From b319bdb12117acf5844fa4ba834bdd1846aba203 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Tue, 28 Mar 2017 19:12:48 +0200 Subject: [PATCH 0001/2296] recognize X-Account-Meta-Quota-Bytes header in accounts.GetHeader --- openstack/objectstorage/v1/accounts/results.go | 15 ++++++++++++++- .../objectstorage/v1/accounts/testing/fixtures.go | 1 + .../v1/accounts/testing/requests_test.go | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/openstack/objectstorage/v1/accounts/results.go b/openstack/objectstorage/v1/accounts/results.go index 9bc8340477..52940b8d7e 100644 --- a/openstack/objectstorage/v1/accounts/results.go +++ b/openstack/objectstorage/v1/accounts/results.go @@ -61,7 +61,9 @@ func (r UpdateResult) Extract() (*UpdateHeader, error) { // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { - BytesUsed int64 `json:"-"` + BytesUsed int64 `json:"-"` + //QuotaBytes will be -1 if no account quota is set. + QuotaBytes int64 `json:"-"` ContainerCount int64 `json:"-"` ContentLength int64 `json:"-"` ObjectCount int64 `json:"-"` @@ -77,6 +79,7 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { var s struct { tmp BytesUsed string `json:"X-Account-Bytes-Used"` + QuotaBytes string `json:"X-Account-Meta-Quota-Bytes"` ContentLength string `json:"Content-Length"` ContainerCount string `json:"X-Account-Container-Count"` ObjectCount string `json:"X-Account-Object-Count"` @@ -99,6 +102,16 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { } } + switch s.QuotaBytes { + case "": + r.QuotaBytes = -1 //OpenStack convention is to represent "no quota" as -1 + default: + r.QuotaBytes, err = strconv.ParseInt(s.QuotaBytes, 10, 64) + if err != nil { + return err + } + } + switch s.ContentLength { case "": r.ContentLength = 0 diff --git a/openstack/objectstorage/v1/accounts/testing/fixtures.go b/openstack/objectstorage/v1/accounts/testing/fixtures.go index fff3071475..cf88dd3734 100644 --- a/openstack/objectstorage/v1/accounts/testing/fixtures.go +++ b/openstack/objectstorage/v1/accounts/testing/fixtures.go @@ -17,6 +17,7 @@ func HandleGetAccountSuccessfully(t *testing.T) { w.Header().Set("X-Account-Container-Count", "2") w.Header().Set("X-Account-Object-Count", "5") + w.Header().Set("X-Account-Meta-Quota-Bytes", "42") w.Header().Set("X-Account-Bytes-Used", "14") w.Header().Set("X-Account-Meta-Subject", "books") w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT") diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go index 97852f1957..4a2c7289aa 100644 --- a/openstack/objectstorage/v1/accounts/testing/requests_test.go +++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go @@ -35,7 +35,7 @@ func TestGetAccount(t *testing.T) { defer th.TeardownHTTP() HandleGetAccountSuccessfully(t) - expectedMetadata := map[string]string{"Subject": "books"} + expectedMetadata := map[string]string{"Subject": "books", "Quota-Bytes": "42"} res := accounts.Get(fake.ServiceClient(), &accounts.GetOpts{}) th.AssertNoErr(t, res.Err) actualMetadata, _ := res.ExtractMetadata() @@ -44,6 +44,7 @@ func TestGetAccount(t *testing.T) { th.AssertNoErr(t, err) expected := &accounts.GetHeader{ + QuotaBytes: 42, ContainerCount: 2, ObjectCount: 5, BytesUsed: 14, From 07765f3ffc009d3a428b14decd611e44b56fd60f Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 3 May 2017 22:30:02 +0800 Subject: [PATCH 0002/2296] get the raw console output from the server instance --- openstack/compute/v2/servers/requests.go | 33 +++++++++++++++ openstack/compute/v2/servers/results.go | 42 +++++++++++++++++++ .../compute/v2/servers/testing/fixtures.go | 30 +++++++++++++ .../v2/servers/testing/requests_test.go | 18 ++++++++ 4 files changed, 123 insertions(+) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 9618637317..72a2ea7edb 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -739,3 +739,36 @@ func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPassw _, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil) return } + +// ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be +// used as ShowConsoleOutput options +type ShowConsoleOutputOptsBuilder interface { + ToServerShowConsoleOutputMap() (map[string]interface{}, error) +} + +// ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder +type ShowConsoleOutputOpts struct { + // The number of lines to fetch from the end of console log. + // All lines will be returned if this is not specified. + Length string `json:"length,omitempty"` +} + +// ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body. +func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-getConsoleOutput") +} + +// ShowConsoleOutput makes a request against the nova API to get console log from the server +func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) { + b, err := opts.ToServerShowConsoleOutputMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + r.Err = err + r.Header = resp.Header + return r +} diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 1ae1e91c78..280f61be78 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -4,9 +4,11 @@ import ( "crypto/rsa" "encoding/base64" "encoding/json" + "errors" "fmt" "net/url" "path" + "strings" "time" "github.com/gophercloud/gophercloud" @@ -72,6 +74,11 @@ type CreateImageResult struct { gophercloud.Result } +// ShowConsoleOutputResult represents the result of console output from a server +type ShowConsoleOutputResult struct { + gophercloud.Result +} + // GetPasswordResult represent the result of a get os-server-password operation. type GetPasswordResult struct { gophercloud.Result @@ -92,6 +99,41 @@ func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, return s.Password, err } +func (r ShowConsoleOutputResult) ExtractConsoleOutput() (string, error) { + if r.Err != nil { + return "", r.Err + } + + var s struct { + Output string `json:"output"` + } + + err := r.ExtractInto(&s) + return s.Output, err +} + +func GetHostKeyFromConsole(consoleOutput string) (string, error) { + start := `-----BEGIN SSH HOST KEY KEYS-----\r` + end := `-----END SSH HOST KEY KEYS-----\r` + searchFlag := false + lines := strings.Split(consoleOutput, "\n") + for _, value := range lines { + if value == start { + searchFlag = true + } + + if value == end { + searchFlag = false + } + + if searchFlag && strings.HasPrefix(value, "ssh-rsa") { + return value, nil + } + } + + return "", errors.New("ssh host key not found") +} + func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) { b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword))) diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index 40d5ed242d..6b71361dde 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -304,6 +304,21 @@ const ServerPasswordBody = ` } ` +const HostKeyConsoleOutput = ` +FAKE CONSOLE OUTPUT\r +-----BEGIN SSH HOST KEY KEYS-----\r +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDciNMyzj0osyPOM+1OyseTWgkzw+M43zp5H2CchG8daRDHel7V3OHETVdI6WofNnSdBJAwIoisRFPxyroNGiVw= root@my-name +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDU854+fNdcKMZTLCUejMOZllQmmphr6V5Aaz1F2+x2jXql5rqKQd5/h6OdFszcp+gdTeVtfgG++/298qodTemVVrvqwjp4eN87iHvhPxH6GDEevAKlEed2ckdAmgvzI9rcOYgR/46G9xIea0IdgNjMvN1baj6WPtv+HfcfH/ZV58G306lSJfbz/GVxNTIxW+Wg7ZQCAe6jWgm4oQ+66sco+7Fub24EPue3kO8jqufqq3mY5+MFlzEHSX5B04ioG5Alw/JuqVx5+7zHt9I2wA3nzsyUdKtCTrw8V4fYEhWDm53WLOpW+8CeYCXuv+yL7EjwLqhIH/TUuzGQiWmFGvyz root@my-name +-----END SSH HOST KEY KEYS-----\r +LAST LINE +` + +const HostKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDU854+fNdcKMZTLCUejMOZllQmmphr6V5Aaz1F2+x2jXql5rqKQd5/h6OdFszcp+gdTeVtfgG++/298qodTemVVrvqwjp4eN87iHvhPxH6GDEevAKlEed2ckdAmgvzI9rcOYgR/46G9xIea0IdgNjMvN1baj6WPtv+HfcfH/ZV58G306lSJfbz/GVxNTIxW+Wg7ZQCAe6jWgm4oQ+66sco+7Fub24EPue3kO8jqufqq3mY5+MFlzEHSX5B04ioG5Alw/JuqVx5+7zHt9I2wA3nzsyUdKtCTrw8V4fYEhWDm53WLOpW+8CeYCXuv+yL7EjwLqhIH/TUuzGQiWmFGvyz root@my-name" + +const ConsoleOutputBody = `{ + "output": "abc" +}` + var ( herpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:10:02Z") herpTimeUpdated, _ = time.Parse(time.RFC3339, "2014-09-25T13:10:10Z") @@ -421,6 +436,8 @@ var ( }, } + ConsoleOutput = "abc" + merpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:41Z") merpTimeUpdated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:49Z") // ServerMerp is a Server struct that should correspond to the second server in ServerListBody. @@ -743,6 +760,19 @@ func HandleRebootSuccessfully(t *testing.T) { }) } +// HandleShowConsoleOutputSuccessfully sets up the test server to respond to a os-getConsoleOutput request with success. +func HandleShowConsoleOutputSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/servers/1234asdf/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-getConsoleOutput": { "length": "50" } }`) + + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + // HandleRebuildSuccessfully sets up the test server to respond to a rebuild request with success. func HandleRebuildSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 05712f7b0f..fe5e06cafe 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -253,6 +253,24 @@ func TestChangeServerAdminPassword(t *testing.T) { th.AssertNoErr(t, res.Err) } +func TestShowConsoleOutput(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleShowConsoleOutputSuccessfully(t, ConsoleOutputBody) + + Actual, err := servers.ShowConsoleOutput(client.ServiceClient(), "1234asdf", &servers.ShowConsoleOutputOpts{ + Length: "50"}).ExtractConsoleOutput() + + th.AssertNoErr(t, err) + th.AssertByteArrayEquals(t, []byte(ConsoleOutput), []byte(Actual)) +} + +func TestHostKeyFromConsoleOutput(t *testing.T) { + hostkey, err := servers.GetHostKeyFromConsole(HostKeyConsoleOutput) + th.AssertNoErr(t, err) + th.AssertByteArrayEquals(t, []byte(HostKey), []byte(hostkey)) +} + func TestGetPassword(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From d4225e32fdadeb579e3c3f247a3f5a0e3e77437c Mon Sep 17 00:00:00 2001 From: zionwu Date: Mon, 24 Jul 2017 16:25:59 +0800 Subject: [PATCH 0003/2296] support suspend/resume for compute instance --- .../v2/extensions/suspendresume/doc.go | 5 +++ .../v2/extensions/suspendresume/requests.go | 19 ++++++++++++ .../extensions/suspendresume/testing/doc.go | 2 ++ .../suspendresume/testing/fixtures.go | 27 ++++++++++++++++ .../suspendresume/testing/requests_test.go | 31 +++++++++++++++++++ 5 files changed, 84 insertions(+) create mode 100644 openstack/compute/v2/extensions/suspendresume/doc.go create mode 100644 openstack/compute/v2/extensions/suspendresume/requests.go create mode 100644 openstack/compute/v2/extensions/suspendresume/testing/doc.go create mode 100644 openstack/compute/v2/extensions/suspendresume/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/suspendresume/testing/requests_test.go diff --git a/openstack/compute/v2/extensions/suspendresume/doc.go b/openstack/compute/v2/extensions/suspendresume/doc.go new file mode 100644 index 0000000000..ad37c49915 --- /dev/null +++ b/openstack/compute/v2/extensions/suspendresume/doc.go @@ -0,0 +1,5 @@ +/* +Package suspendresume provides functionality to suspend and resume servers that have +been provisioned by the OpenStack Compute service. +*/ +package suspendresume diff --git a/openstack/compute/v2/extensions/suspendresume/requests.go b/openstack/compute/v2/extensions/suspendresume/requests.go new file mode 100644 index 0000000000..d5f03fcdf1 --- /dev/null +++ b/openstack/compute/v2/extensions/suspendresume/requests.go @@ -0,0 +1,19 @@ +package suspendresume + +import "github.com/gophercloud/gophercloud" + +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "action") +} + +// Suspend is the operation responsible for suspending a Compute server. +func Suspend(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) + return +} + +// Resume is the operation responsible for resuming a Compute server. +func Resume(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) + return +} diff --git a/openstack/compute/v2/extensions/suspendresume/testing/doc.go b/openstack/compute/v2/extensions/suspendresume/testing/doc.go new file mode 100644 index 0000000000..b81051a39c --- /dev/null +++ b/openstack/compute/v2/extensions/suspendresume/testing/doc.go @@ -0,0 +1,2 @@ +// compute_extensions_suspendresume_v2 +package testing diff --git a/openstack/compute/v2/extensions/suspendresume/testing/fixtures.go b/openstack/compute/v2/extensions/suspendresume/testing/fixtures.go new file mode 100644 index 0000000000..5c6405deeb --- /dev/null +++ b/openstack/compute/v2/extensions/suspendresume/testing/fixtures.go @@ -0,0 +1,27 @@ +package testing + +import ( + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func mockSuspendServerResponse(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, `{"suspend": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} + +func mockResumeServerResponse(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, `{"resume": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go b/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go new file mode 100644 index 0000000000..7c8e4ec6b1 --- /dev/null +++ b/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go @@ -0,0 +1,31 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const serverID = "{serverId}" + +func TestSuspend(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockSuspendServerResponse(t, serverID) + + err := suspendresume.Suspend(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestResume(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockResumeServerResponse(t, serverID) + + err := suspendresume.Resume(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} From d1f42e850e3d8f966dad9b40d802e5d142414c45 Mon Sep 17 00:00:00 2001 From: FengyunPan Date: Fri, 28 Jul 2017 12:48:47 +0800 Subject: [PATCH 0004/2296] Add Nova interface-list support Add support to list interface for a server through a GET on: /v2.1/{tenant_id}/servers/{server_id}/os-interface The same operation with OpenStack CLI is done with: nova interface-list --- .../openstack/compute/v2/servers_test.go | 15 +++++ .../v2/extensions/attachinterfaces/doc.go | 3 + .../extensions/attachinterfaces/requests.go | 13 ++++ .../v2/extensions/attachinterfaces/results.go | 43 +++++++++++++ .../attachinterfaces/testing/doc.go | 2 + .../attachinterfaces/testing/fixtures.go | 61 +++++++++++++++++++ .../attachinterfaces/testing/requests_test.go | 45 ++++++++++++++ .../v2/extensions/attachinterfaces/urls.go | 7 +++ 8 files changed, 189 insertions(+) create mode 100644 openstack/compute/v2/extensions/attachinterfaces/doc.go create mode 100644 openstack/compute/v2/extensions/attachinterfaces/requests.go create mode 100644 openstack/compute/v2/extensions/attachinterfaces/results.go create mode 100644 openstack/compute/v2/extensions/attachinterfaces/testing/doc.go create mode 100644 openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/attachinterfaces/urls.go diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 546abe2efb..46ce0ee23c 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/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/pauseunpause" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" @@ -74,6 +75,20 @@ func TestServersCreateDestroy(t *testing.T) { t.Logf("Addresses on %s: %+v", network, address) } + allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages() + if err != nil { + t.Errorf("Unable to list server Interfaces: %v", err) + } + + allInterfaces, err := attachinterfaces.ExtractInterfaces(allInterfacePages) + if err != nil { + t.Errorf("Unable to extract server Interfaces: %v", err) + } + + for _, Interface := range allInterfaces { + t.Logf("Interfaces: %+v", Interface) + } + allNetworkAddressPages, err := servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages() if err != nil { t.Errorf("Unable to list server addresses: %v", err) diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go new file mode 100644 index 0000000000..2ef30db869 --- /dev/null +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -0,0 +1,3 @@ +// Package attachinterfaces provides the ability to manage network interfaces through +// nova-network +package attachinterfaces diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go new file mode 100644 index 0000000000..76cc879630 --- /dev/null +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -0,0 +1,13 @@ +package attachinterfaces + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List makes a request against the nova API to list the servers interfaces. +func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { + return pagination.NewPager(client, listInterfaceURL(client, serverID), func(r pagination.PageResult) pagination.Page { + return InterfacePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/extensions/attachinterfaces/results.go new file mode 100644 index 0000000000..776ad8305b --- /dev/null +++ b/openstack/compute/v2/extensions/attachinterfaces/results.go @@ -0,0 +1,43 @@ +package attachinterfaces + +import ( + "github.com/gophercloud/gophercloud/pagination" +) + +// FixedIP represents a Fixed IP Address. +type FixedIP struct { + SubnetID string `json:"subnet_id"` + IPAddress string `json:"ip_address"` +} + +// Interface represents a network interface on an instance. +type Interface struct { + PortState string `json:"port_state"` + FixedIPs []FixedIP `json:"fixed_ips"` + PortID string `json:"port_id"` + NetID string `json:"net_id"` + MACAddr string `json:"mac_addr"` +} + +// InterfacePage abstracts the raw results of making a List() request against the API. +// As OpenStack extensions may freely alter the response bodies of structures returned +// to the client, you may only safely access the data provided through the ExtractInterfaces call. +type InterfacePage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if an InterfacePage contains no interfaces. +func (r InterfacePage) IsEmpty() (bool, error) { + interfaces, err := ExtractInterfaces(r) + return len(interfaces) == 0, err +} + +// ExtractInterfaces interprets the results of a single page from a List() call, +// producing a map of interfaces. +func ExtractInterfaces(r pagination.Page) ([]Interface, error) { + var s struct { + Interfaces []Interface `json:"interfaceAttachments"` + } + err := (r.(InterfacePage)).ExtractInto(&s) + return s.Interfaces, err +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/doc.go b/openstack/compute/v2/extensions/attachinterfaces/testing/doc.go new file mode 100644 index 0000000000..d9da11d008 --- /dev/null +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/doc.go @@ -0,0 +1,2 @@ +// compute_extensions_attachinterfaces_v2 +package testing diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go new file mode 100644 index 0000000000..017243f707 --- /dev/null +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go @@ -0,0 +1,61 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListInterfacesExpected represents an expected repsonse from a ListInterfaces request. +var ListInterfacesExpected = []attachinterfaces.Interface{ + { + PortState: "ACTIVE", + FixedIPs: []attachinterfaces.FixedIP{ + { + SubnetID: "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", + IPAddress: "10.0.0.7", + }, + { + SubnetID: "45906d64-a548-4276-h1f8-kcffa80fjbnl", + IPAddress: "10.0.0.8", + }, + }, + PortID: "0dde1598-b374-474e-986f-5b8dd1df1d4e", + NetID: "8a5fe506-7e9f-4091-899b-96336909d93c", + MACAddr: "fa:16:3e:38:2d:80", + }, +} + +// HandleInterfaceListSuccessfully sets up the test server to respond to a ListInterfaces request. +func HandleInterfaceListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface", 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, `{ + "interfaceAttachments": [ + { + "port_state":"ACTIVE", + "fixed_ips": [ + { + "subnet_id": "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", + "ip_address": "10.0.0.7" + }, + { + "subnet_id": "45906d64-a548-4276-h1f8-kcffa80fjbnl", + "ip_address": "10.0.0.8" + } + ], + "port_id": "0dde1598-b374-474e-986f-5b8dd1df1d4e", + "net_id": "8a5fe506-7e9f-4091-899b-96336909d93c", + "mac_addr": "fa:16:3e:38:2d:80" + } + ] + }`) + }) +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go new file mode 100644 index 0000000000..d7eb511daa --- /dev/null +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go @@ -0,0 +1,45 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListInterface(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleInterfaceListSuccessfully(t) + + expected := ListInterfacesExpected + pages := 0 + err := attachinterfaces.List(client.ServiceClient(), "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f").EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := attachinterfaces.ExtractInterfaces(page) + th.AssertNoErr(t, err) + + if len(actual) != 1 { + t.Fatalf("Expected 1 interface, got %d", len(actual)) + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, pages) +} + +func TestListInterfacesAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleInterfaceListSuccessfully(t) + + allPages, err := attachinterfaces.List(client.ServiceClient(), "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f").AllPages() + th.AssertNoErr(t, err) + _, err = attachinterfaces.ExtractInterfaces(allPages) + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/urls.go b/openstack/compute/v2/extensions/attachinterfaces/urls.go new file mode 100644 index 0000000000..7d376f99bb --- /dev/null +++ b/openstack/compute/v2/extensions/attachinterfaces/urls.go @@ -0,0 +1,7 @@ +package attachinterfaces + +import "github.com/gophercloud/gophercloud" + +func listInterfaceURL(client *gophercloud.ServiceClient, serverID string) string { + return client.ServiceURL("servers", serverID, "os-interface") +} From 1935ca0d215d77d3e3919fb3e262e99bd975d2a4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 27 Jul 2017 21:37:24 -0600 Subject: [PATCH 0005/2296] Compute v2: Add Suspend Acceptance Test --- .../openstack/compute/v2/servers_test.go | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 46ce0ee23c..d545a43252 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -12,6 +12,7 @@ import ( "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/pauseunpause" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -440,3 +441,39 @@ func TestServersActionPause(t *testing.T) { t.Fatal(err) } } + +func TestServersActionSuspend(t *testing.T) { + t.Parallel() + + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + server, err := CreateServer(t, client) + if err != nil { + t.Fatal(err) + } + defer DeleteServer(t, client, server) + + t.Logf("Attempting to suspend server %s", server.ID) + err = suspendresume.Suspend(client, server.ID).ExtractErr() + if err != nil { + t.Fatal(err) + } + + err = WaitForComputeStatus(client, server, "SUSPENDED") + if err != nil { + t.Fatal(err) + } + + err = suspendresume.Resume(client, server.ID).ExtractErr() + if err != nil { + t.Fatal(err) + } + + err = WaitForComputeStatus(client, server, "ACTIVE") + if err != nil { + t.Fatal(err) + } +} From 3172ddf2336933bc38b355df89cb015e0954df67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20St=C4=99pniewski?= Date: Sun, 30 Jul 2017 20:05:30 -0700 Subject: [PATCH 0006/2296] Add a ListGroups method to list user groups (#386) --- .../openstack/identity/v3/users_test.go | 33 ++++++ openstack/identity/v3/groups/results.go | 108 ++++++++++++++++++ openstack/identity/v3/users/requests.go | 9 ++ .../identity/v3/users/testing/fixtures.go | 73 ++++++++++++ .../v3/users/testing/requests_test.go | 12 ++ openstack/identity/v3/users/urls.go | 4 + 6 files changed, 239 insertions(+) create mode 100644 openstack/identity/v3/groups/results.go diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index 1526e55228..e87c88e44b 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" ) @@ -119,5 +120,37 @@ func TestUserCRUD(t *testing.T) { tools.PrintResource(t, newUser) tools.PrintResource(t, newUser.Extra) +} + +func TestUsersListGroups(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + allUserPages, err := users.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list users: %v", err) + } + + allUsers, err := users.ExtractUsers(allUserPages) + if err != nil { + t.Fatalf("Unable to extract users: %v", err) + } + + user := allUsers[0] + allGroupPages, err := users.ListGroups(client, user.ID).AllPages() + if err != nil { + t.Fatalf("Unable to list groups: %v", err) + } + + allGroups, err := groups.ExtractGroups(allGroupPages) + if err != nil { + t.Fatalf("Unable to extract groups: %v", err) + } + + for _, group := range allGroups { + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) + } } diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go new file mode 100644 index 0000000000..ad25d6c8c1 --- /dev/null +++ b/openstack/identity/v3/groups/results.go @@ -0,0 +1,108 @@ +package groups + +import ( + "encoding/json" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/internal" + "github.com/gophercloud/gophercloud/pagination" +) + +// Group helps manage related users. +type Group struct { + // Description describes the group purpose. + Description string `json:"description"` + + // DomainID is the domain ID the group belongs to. + DomainID string `json:"domain_id"` + + // ID is the unique ID of the group. + ID string `json:"id"` + + // Extra is a collection of miscellaneous key/values. + Extra map[string]interface{} `json:"-"` + + // Links contains referencing links to the group. + Links map[string]interface{} `json:"links"` + + // Name is the name of the group. + Name string `json:"name"` +} + +func (r *Group) UnmarshalJSON(b []byte) error { + type tmp Group + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Group(s.tmp) + + // Collect other fields and bundle them into Extra + // but only if a field titled "extra" wasn't sent. + if s.Extra != nil { + r.Extra = s.Extra + } else { + var result interface{} + err := json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = internal.RemainingKeys(Group{}, resultMap) + } + } + + return err +} + +type groupResult struct { + gophercloud.Result +} + +// GroupPage is a single page of Group results. +type GroupPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Groups contains any results. +func (r GroupPage) IsEmpty() (bool, error) { + groups, err := ExtractGroups(r) + return len(groups) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r GroupPage) 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 +} + +// ExtractGroups returns a slice of Groups contained in a single page of results. +func ExtractGroups(r pagination.Page) ([]Group, error) { + var s struct { + Groups []Group `json:"groups"` + } + err := (r.(GroupPage)).ExtractInto(&s) + return s.Groups, err +} + +// Extract interprets any group results as a Group. +func (r groupResult) Extract() (*Group, error) { + var s struct { + Group *Group `json:"group"` + } + err := r.ExtractInto(&s) + return s.Group, err +} diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index a73bf8beb3..eea19197c0 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -2,6 +2,7 @@ package users import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/pagination" ) @@ -207,3 +208,11 @@ func Delete(client *gophercloud.ServiceClient, userID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, userID), nil) return } + +// ListGroups enumerates groups user belongs to. +func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pager { + url := listGroupsURL(client, userID) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return groups.GroupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 735bf0cc34..00a0e3493f 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" @@ -162,6 +163,38 @@ const UpdateOutput = ` } ` +// ListGroupsOutput provides a ListGroups result. +const ListGroupsOutput = ` +{ + "groups": [ + { + "description": "Developers cleared for work on all general projects", + "domain_id": "1789d1", + "id": "ea167b", + "links": { + "self": "https://example.com/identity/v3/groups/ea167b" + }, + "building": "Hilltop A", + "name": "Developers" + }, + { + "description": "Developers cleared for work on secret projects", + "domain_id": "1789d1", + "id": "a62db1", + "links": { + "self": "https://example.com/identity/v3/groups/a62db1" + }, + "name": "Secure Developers" + } + ], + "links": { + "self": "http://example.com/identity/v3/users/9fe1d3/groups", + "previous": null, + "next": null + } +} +` + // FirstUser is the first user in the List request. var nilTime time.Time var FirstUser = users.User{ @@ -241,6 +274,32 @@ var SecondUserUpdated = users.User{ // ExpectedUsersSlice is the slice of users expected to be returned from ListOutput. var ExpectedUsersSlice = []users.User{FirstUser, SecondUser} +var FirstGroup = groups.Group{ + Description: "Developers cleared for work on all general projects", + DomainID: "1789d1", + ID: "ea167b", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/groups/ea167b", + }, + Extra: map[string]interface{}{ + "building": "Hilltop A", + }, + Name: "Developers", +} + +var SecondGroup = groups.Group{ + Description: "Developers cleared for work on secret projects", + DomainID: "1789d1", + ID: "a62db1", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/groups/a62db1", + }, + Extra: map[string]interface{}{}, + Name: "Secure Developers", +} + +var ExpectedGroupsSlice = []groups.Group{FirstGroup, SecondGroup} + // HandleListUsersSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a list of two users. func HandleListUsersSuccessfully(t *testing.T) { @@ -318,3 +377,17 @@ func HandleDeleteUserSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// HandleListUserGroupsSuccessfully creates an HTTP handler at /users/{userID}/groups +// on the test handler mux that respons wit a list of two groups +func HandleListUserGroupsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/9fe1d3/groups", 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, ListGroupsOutput) + }) +} diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index 9a5ca669ae..5ba21f9de6 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -3,6 +3,7 @@ package testing import ( "testing" + "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -134,3 +135,14 @@ func TestDeleteUser(t *testing.T) { res := users.Delete(client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } + +func TestListUserGroups(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListUserGroupsSuccessfully(t) + allPages, err := users.ListGroups(client.ServiceClient(), "9fe1d3").AllPages() + th.AssertNoErr(t, err) + actual, err := groups.ExtractGroups(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedGroupsSlice, actual) +} diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index d8b697d126..9ac605ae42 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -21,3 +21,7 @@ func updateURL(client *gophercloud.ServiceClient, userID string) string { func deleteURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID) } + +func listGroupsURL(client *gophercloud.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "groups") +} From 86d57213cee3ca1e5a940987bec16270ccc16af1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 25 Jul 2017 02:58:19 +0000 Subject: [PATCH 0007/2296] OpenStack Client: Handle New Identity Endpoints This commit modifies the openstack client to handle new identity endpoints in the following ways: 1. Identity endpoints published with a valid URL path (http://example.com/identity) are now parsed correctly. If the endpoint has a version suffix (http://example.com/identity/v3), the client will use /identity as the base and /identity/v3 as the endpoint. If the endpoint does not have a version suffix, both the base and the endpoint will be set to /identity and further version discovery will be done. 2. Version discovery can now handle version IDs other than v2.0 and v3. If the Identity Service is publishing an ID of v3.8, Gophercloud will recognize it as a valid result. --- openstack/client.go | 42 ++++++++++++++++++------------- openstack/utils/choose_version.go | 31 +++++++++++------------ 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index 09120e8faf..cc6ec0aa4d 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -4,6 +4,8 @@ import ( "fmt" "net/url" "reflect" + "regexp" + "strings" "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" @@ -12,8 +14,13 @@ import ( ) const ( - v20 = "v2.0" - v30 = "v3.0" + // v2 represents Keystone v2. + // It should never increase beyond 2.0. + v2 = "v2.0" + + // v3 represents Keystone v3. + // The version can be anything from v3 to v3.x. + v3 = "v3" ) // NewClient prepares an unauthenticated ProviderClient instance. @@ -25,24 +32,25 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { if err != nil { return nil, err } - hadPath := u.Path != "" - u.Path, u.RawQuery, u.Fragment = "", "", "" - base := u.String() - endpoint = gophercloud.NormalizeURL(endpoint) - base = gophercloud.NormalizeURL(base) + u.RawQuery, u.Fragment = "", "" - if hadPath { - return &gophercloud.ProviderClient{ - IdentityBase: base, - IdentityEndpoint: endpoint, - }, nil + var base string + versionRe := regexp.MustCompile("v[0-9.]+/?") + if version := versionRe.FindString(u.Path); version != "" { + base = strings.Replace(u.String(), version, "", -1) + } else { + base = u.String() } + endpoint = gophercloud.NormalizeURL(endpoint) + base = gophercloud.NormalizeURL(base) + return &gophercloud.ProviderClient{ IdentityBase: base, - IdentityEndpoint: "", + IdentityEndpoint: endpoint, }, nil + } // AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and @@ -65,8 +73,8 @@ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.Provider // 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: v20, Priority: 20, Suffix: "/v2.0/"}, - {ID: v30, Priority: 30, Suffix: "/v3/"}, + {ID: v2, Priority: 20, Suffix: "/v2.0/"}, + {ID: v3, Priority: 30, Suffix: "/v3/"}, } chosen, endpoint, err := utils.ChooseVersion(client, versions) @@ -75,9 +83,9 @@ func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOp } switch chosen.ID { - case v20: + case v2: return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) - case v30: + case v3: return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) default: // The switch statement must be out of date from the versions list. diff --git a/openstack/utils/choose_version.go b/openstack/utils/choose_version.go index c605d08444..27da19f91a 100644 --- a/openstack/utils/choose_version.go +++ b/openstack/utils/choose_version.go @@ -68,11 +68,6 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* return nil, "", err } - byID := make(map[string]*Version) - for _, version := range recognized { - byID[version.ID] = version - } - var highest *Version var endpoint string @@ -84,20 +79,22 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* } } - if matching, ok := byID[value.ID]; ok { - // Prefer a version that exactly matches the provided endpoint. - if href == identityEndpoint { - if href == "" { - return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, client.IdentityBase) + for _, version := range recognized { + if strings.Contains(value.ID, version.ID) { + // Prefer a version that exactly matches the provided endpoint. + if href == identityEndpoint { + if href == "" { + return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, client.IdentityBase) + } + return version, href, nil } - return matching, href, nil - } - // Otherwise, find the highest-priority version with a whitelisted status. - if goodStatus[strings.ToLower(value.Status)] { - if highest == nil || matching.Priority > highest.Priority { - highest = matching - endpoint = href + // Otherwise, find the highest-priority version with a whitelisted status. + if goodStatus[strings.ToLower(value.Status)] { + if highest == nil || version.Priority > highest.Priority { + highest = version + endpoint = href + } } } } From c0406a133c4a74a838baf0ddff3c2fab21155fba Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 6 Aug 2017 21:32:09 -0600 Subject: [PATCH 0008/2296] Extensions Refactor (#443) * Network v2: Updating networks to support extensions * Network v2: Network unit test cleanup * Network v2: Provider Extension * Network v2: Provider Extension unit tests * Core: Handle nested JSON unmarshaling * Network v2: Refactor External Network extension This commit refactors the external network extension by doing two things: 1. It extends the base Network CreateOpts with an External field rather than maintaining a separate copy of CreateOpts. 2. It provides the ability to extend the results with the `router:external` field. * Core: Unit tests for unmarshaling results --- .../networking/v2/extensions/extensions.go | 12 +- .../v2/extensions/external/requests.go | 48 +++- .../v2/extensions/external/results.go | 72 +----- .../extensions/external/testing/fixtures.go | 59 +++++ .../external/testing/results_test.go | 236 +++++------------- .../v2/extensions/provider/results.go | 88 +------ .../provider/testing/results_test.go | 217 ++++------------ openstack/networking/v2/networks/results.go | 22 +- .../v2/networks/testing/fixtures.go | 141 +++++++++++ .../v2/networks/testing/requests_test.go | 147 +---------- results.go | 47 ++++ testing/results_test.go | 208 +++++++++++++++ 12 files changed, 643 insertions(+), 654 deletions(-) create mode 100644 openstack/networking/v2/extensions/external/testing/fixtures.go create mode 100644 openstack/networking/v2/networks/testing/fixtures.go create mode 100644 testing/results_test.go diff --git a/acceptance/openstack/networking/v2/extensions/extensions.go b/acceptance/openstack/networking/v2/extensions/extensions.go index 154e34eac7..96c6e42cfa 100644 --- a/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/acceptance/openstack/networking/v2/extensions/extensions.go @@ -21,12 +21,16 @@ func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*ne adminStateUp := true isExternal := true - createOpts := external.CreateOpts{ - External: &isExternal, + + networkCreateOpts := networks.CreateOpts{ + Name: networkName, + AdminStateUp: &adminStateUp, } - createOpts.Name = networkName - createOpts.AdminStateUp = &adminStateUp + createOpts := external.CreateOptsExt{ + networkCreateOpts, + &isExternal, + } network, err := networks.Create(client, createOpts).Extract() if err != nil { diff --git a/openstack/networking/v2/extensions/external/requests.go b/openstack/networking/v2/extensions/external/requests.go index 1ee39d2bee..f28e574612 100644 --- a/openstack/networking/v2/extensions/external/requests.go +++ b/openstack/networking/v2/extensions/external/requests.go @@ -1,32 +1,56 @@ package external import ( - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) -// CreateOpts is the structure used when creating new external network +// CreateOptsExt is the structure used when creating new external network // resources. It embeds networks.CreateOpts and so inherits all of its required // and optional fields, with the addition of the External field. -type CreateOpts struct { - networks.CreateOpts +type CreateOptsExt struct { + networks.CreateOptsBuilder External *bool `json:"router:external,omitempty"` } -// ToNetworkCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "network") +// ToNetworkCreateMap adds the router:external options to the base network +// creation options. +func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() + if err != nil { + return nil, err + } + + if opts.External == nil { + return base, nil + } + + networkMap := base["network"].(map[string]interface{}) + networkMap["router:external"] = opts.External + + return base, nil } -// UpdateOpts is the structure used when updating existing external network +// UpdateOptsExt is the structure used when updating existing external network // resources. It embeds networks.UpdateOpts and so inherits all of its required // and optional fields, with the addition of the External field. -type UpdateOpts struct { - networks.UpdateOpts +type UpdateOptsExt struct { + networks.UpdateOptsBuilder External *bool `json:"router:external,omitempty"` } // ToNetworkUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "network") +func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() + if err != nil { + return nil, err + } + + if opts.External == nil { + return base, nil + } + + networkMap := base["network"].(map[string]interface{}) + networkMap["router:external"] = opts.External + + return base, nil } diff --git a/openstack/networking/v2/extensions/external/results.go b/openstack/networking/v2/extensions/external/results.go index 7e10c6d299..7cbbffdcf8 100644 --- a/openstack/networking/v2/extensions/external/results.go +++ b/openstack/networking/v2/extensions/external/results.go @@ -1,76 +1,8 @@ package external -import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/pagination" -) - -// NetworkExternal represents a decorated form of a Network with based on the +// NetworkExternalExt represents a decorated form of a Network with based on the // "external-net" extension. -type NetworkExternal struct { - // UUID for the network - ID string `json:"id"` - - // Human-readable name for the network. Might not be unique. - Name string `json:"name"` - - // The administrative state of network. If false (down), the network does not forward packets. - AdminStateUp bool `json:"admin_state_up"` - - // Indicates whether network is currently operational. Possible values include - // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values. - Status string `json:"status"` - - // Subnets associated with this network. - Subnets []string `json:"subnets"` - - // Owner of network. Only admin users can specify a tenant_id other than its own. - TenantID string `json:"tenant_id"` - - // Specifies whether the network resource can be accessed by any tenant or not. - Shared bool `json:"shared"` - +type NetworkExternalExt struct { // Specifies whether the network is an external network or not. External bool `json:"router:external"` } - -// ExtractGet decorates a GetResult struct returned from a networks.Get() -// function with extended attributes. -func ExtractGet(r networks.GetResult) (*NetworkExternal, error) { - var s struct { - Network *NetworkExternal `json:"network"` - } - err := r.ExtractInto(&s) - return s.Network, err -} - -// ExtractCreate decorates a CreateResult struct returned from a networks.Create() -// function with extended attributes. -func ExtractCreate(r networks.CreateResult) (*NetworkExternal, error) { - var s struct { - Network *NetworkExternal `json:"network"` - } - err := r.ExtractInto(&s) - return s.Network, err -} - -// ExtractUpdate decorates a UpdateResult struct returned from a -// networks.Update() function with extended attributes. -func ExtractUpdate(r networks.UpdateResult) (*NetworkExternal, error) { - var s struct { - Network *NetworkExternal `json:"network"` - } - err := r.ExtractInto(&s) - return s.Network, err -} - -// ExtractList accepts a Page struct, specifically a NetworkPage struct, and -// extracts the elements into a slice of NetworkExternal structs. In other -// words, a generic collection is mapped into a relevant slice. -func ExtractList(r pagination.Page) ([]NetworkExternal, error) { - var s struct { - Networks []NetworkExternal `json:"networks" json:"networks"` - } - err := (r.(networks.NetworkPage)).ExtractInto(&s) - return s.Networks, err -} diff --git a/openstack/networking/v2/extensions/external/testing/fixtures.go b/openstack/networking/v2/extensions/external/testing/fixtures.go new file mode 100644 index 0000000000..8739236d69 --- /dev/null +++ b/openstack/networking/v2/extensions/external/testing/fixtures.go @@ -0,0 +1,59 @@ +package testing + +// These fixtures are here instead of in the underlying networks package +// because all network tests (including extensions) would have to +// implement the NetworkExternalExt extention for create/update tests +// to pass. + +const CreateRequest = ` +{ + "network": { + "name": "private", + "admin_state_up": true, + "router:external": false + } +}` + +const CreateResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": false + } +}` + +const UpdateRequest = ` +{ + "network": { + "name": "new_network_name", + "admin_state_up": false, + "shared": true, + "router:external": false + } +}` + +const UpdateResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": [], + "name": "new_network_name", + "admin_state_up": false, + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "shared": true, + "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", + "provider:segmentation_id": 1234567890, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": false + } +}` diff --git a/openstack/networking/v2/extensions/external/testing/results_test.go b/openstack/networking/v2/extensions/external/testing/results_test.go index 82a420ede5..48448e3dec 100644 --- a/openstack/networking/v2/extensions/external/testing/results_test.go +++ b/openstack/networking/v2/extensions/external/testing/results_test.go @@ -1,262 +1,138 @@ package testing import ( - "errors" "fmt" "net/http" "testing" - "github.com/gophercloud/gophercloud" + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/pagination" + nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" ) func TestList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/v2.0/networks", 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, ` -{ - "networks": [ - { - "admin_state_up": true, - "id": "0f38d5ad-10a6-428f-a5fc-825cfe0f1970", - "name": "net1", - "router:external": false, - "shared": false, - "status": "ACTIVE", - "subnets": [ - "25778974-48a8-46e7-8998-9dc8c70d2f06" - ], - "tenant_id": "b575417a6c444a6eb5cc3a58eb4f714a" - }, - { - "admin_state_up": true, - "id": "8d05a1b1-297a-46ca-8974-17debf51ca3c", - "name": "ext_net", - "router:external": true, - "shared": false, - "status": "ACTIVE", - "subnets": [ - "2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5" - ], - "tenant_id": "5eb8995cf717462c9df8d1edfa498010" - } - ] -} - `) - }) - - count := 0 - - networks.List(fake.ServiceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := external.ExtractList(page) - if err != nil { - t.Errorf("Failed to extract networks: %v", err) - return false, err - } - - expected := []external.NetworkExternal{ - { - Status: "ACTIVE", - Subnets: []string{"25778974-48a8-46e7-8998-9dc8c70d2f06"}, - Name: "net1", - AdminStateUp: true, - TenantID: "b575417a6c444a6eb5cc3a58eb4f714a", - Shared: false, - ID: "0f38d5ad-10a6-428f-a5fc-825cfe0f1970", - External: false, - }, - { - Status: "ACTIVE", - Subnets: []string{"2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5"}, - Name: "ext_net", - AdminStateUp: true, - TenantID: "5eb8995cf717462c9df8d1edfa498010", - Shared: false, - ID: "8d05a1b1-297a-46ca-8974-17debf51ca3c", - External: true, - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil + fmt.Fprintf(w, nettest.ListResponse) }) - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) + type NetworkWithExternalExt struct { + networks.Network + external.NetworkExternalExt } + var actual []NetworkWithExternalExt + + allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + err = networks.ExtractNetworksInto(allPages, &actual) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", actual[0].ID) + th.AssertEquals(t, true, actual[0].External) } func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/v2.0/networks/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, ` -{ - "network": { - "admin_state_up": true, - "id": "8d05a1b1-297a-46ca-8974-17debf51ca3c", - "name": "ext_net", - "router:external": true, - "shared": false, - "status": "ACTIVE", - "subnets": [ - "2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5" - ], - "tenant_id": "5eb8995cf717462c9df8d1edfa498010" - } -} - `) + fmt.Fprintf(w, nettest.GetResponse) }) - res := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - n, err := external.ExtractGet(res) + var s struct { + networks.Network + external.NetworkExternalExt + } + err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) - th.AssertEquals(t, true, n.External) + + th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", s.ID) + th.AssertEquals(t, true, s.External) } func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/networks", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/v2.0/networks", 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, ` -{ - "network": { - "admin_state_up": true, - "name": "ext_net", - "router:external": true - } -} - `) + th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` -{ - "network": { - "admin_state_up": true, - "id": "8d05a1b1-297a-46ca-8974-17debf51ca3c", - "name": "ext_net", - "router:external": true, - "shared": false, - "status": "ACTIVE", - "subnets": [ - "2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5" - ], - "tenant_id": "5eb8995cf717462c9df8d1edfa498010" - } -} - `) + fmt.Fprintf(w, CreateResponse) }) - options := external.CreateOpts{ - CreateOpts: networks.CreateOpts{Name: "ext_net", AdminStateUp: gophercloud.Enabled}, - External: gophercloud.Enabled, + iTrue := true + iFalse := false + networkCreateOpts := networks.CreateOpts{ + Name: "private", + AdminStateUp: &iTrue, + } + + externalCreateOpts := external.CreateOptsExt{ + networkCreateOpts, + &iFalse, } - res := networks.Create(fake.ServiceClient(), options) - n, err := external.ExtractCreate(res) + _, err := networks.Create(fake.ServiceClient(), externalCreateOpts).Extract() + th.AssertNoErr(t, err) th.AssertNoErr(t, err) - th.AssertEquals(t, true, n.External) } func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) { + 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, ` -{ - "network": { - "router:external": true, - "name": "new_name" - } -} - `) + th.TestJSONRequest(t, r, UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "network": { - "admin_state_up": true, - "id": "8d05a1b1-297a-46ca-8974-17debf51ca3c", - "name": "new_name", - "router:external": true, - "shared": false, - "status": "ACTIVE", - "subnets": [ - "2f1fb918-9b0e-4bf9-9a50-6cebbb4db2c5" - ], - "tenant_id": "5eb8995cf717462c9df8d1edfa498010" - } -} - `) + fmt.Fprintf(w, UpdateResponse) }) - options := external.UpdateOpts{ - UpdateOpts: networks.UpdateOpts{Name: "new_name"}, - External: gophercloud.Enabled, + iTrue := true + iFalse := false + networkUpdateOpts := networks.UpdateOpts{ + Name: "new_network_name", + AdminStateUp: &iFalse, + Shared: &iTrue, } - res := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options) - n, err := external.ExtractUpdate(res) - - th.AssertNoErr(t, err) - th.AssertEquals(t, true, n.External) -} -func TestExtractFnsReturnsErrWhenResultContainsErr(t *testing.T) { - gr := networks.GetResult{} - gr.Err = errors.New("") - - if _, err := external.ExtractGet(gr); err == nil { - t.Fatalf("Expected error, got one") - } - - ur := networks.UpdateResult{} - ur.Err = errors.New("") - - if _, err := external.ExtractUpdate(ur); err == nil { - t.Fatalf("Expected error, got one") + externalUpdateOpts := external.UpdateOptsExt{ + networkUpdateOpts, + &iFalse, } - cr := networks.CreateResult{} - cr.Err = errors.New("") - - if _, err := external.ExtractCreate(cr); err == nil { - t.Fatalf("Expected error, got one") - } + _, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", externalUpdateOpts).Extract() + th.AssertNoErr(t, err) } diff --git a/openstack/networking/v2/extensions/provider/results.go b/openstack/networking/v2/extensions/provider/results.go index 55770d8b8f..2f56869481 100644 --- a/openstack/networking/v2/extensions/provider/results.go +++ b/openstack/networking/v2/extensions/provider/results.go @@ -3,35 +3,10 @@ package provider import ( "encoding/json" "strconv" - - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/pagination" ) -// NetworkExtAttrs represents an extended form of a Network with additional fields. -type NetworkExtAttrs struct { - // UUID for the network - ID string `json:"id"` - - // Human-readable name for the network. Might not be unique. - Name string `json:"name"` - - // The administrative state of network. If false (down), the network does not forward packets. - AdminStateUp bool `json:"admin_state_up"` - - // Indicates whether network is currently operational. Possible values include - // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values. - Status string `json:"status"` - - // Subnets associated with this network. - Subnets []string `json:"subnets"` - - // Owner of network. Only admin users can specify a tenant_id other than its own. - TenantID string `json:"tenant_id"` - - // Specifies whether the network resource can be accessed by any tenant or not. - Shared bool `json:"shared"` - +// NetworkProviderExt represents an extended form of a Network with additional fields. +type NetworkProviderExt struct { // Specifies the nature of the physical network mapped to this network // resource. Examples are flat, vlan, or gre. NetworkType string `json:"provider:network_type"` @@ -48,7 +23,7 @@ type NetworkExtAttrs struct { // segment depends on the segmentation model defined by network_type. For // instance, if network_type is vlan, then this is a vlan identifier; // otherwise, if network_type is gre, then this will be a gre key. - SegmentationID string `json:"provider:segmentation_id"` + SegmentationID string `json:"-"` // Segments is an array of Segment which defines multiple physical bindings to logical networks. Segments []Segment `json:"segments"` @@ -61,66 +36,25 @@ type Segment struct { SegmentationID int `json:"provider:segmentation_id"` } -func (n *NetworkExtAttrs) UnmarshalJSON(b []byte) error { - type tmp NetworkExtAttrs - var networkExtAttrs *struct { +func (r *NetworkProviderExt) UnmarshalJSON(b []byte) error { + type tmp NetworkProviderExt + var networkProviderExt struct { tmp SegmentationID interface{} `json:"provider:segmentation_id"` } - if err := json.Unmarshal(b, &networkExtAttrs); err != nil { + if err := json.Unmarshal(b, &networkProviderExt); err != nil { return err } - *n = NetworkExtAttrs(networkExtAttrs.tmp) + *r = NetworkProviderExt(networkProviderExt.tmp) - switch t := networkExtAttrs.SegmentationID.(type) { + switch t := networkProviderExt.SegmentationID.(type) { case float64: - n.SegmentationID = strconv.FormatFloat(t, 'f', -1, 64) + r.SegmentationID = strconv.FormatFloat(t, 'f', -1, 64) case string: - n.SegmentationID = string(t) + r.SegmentationID = string(t) } return nil } - -// ExtractGet decorates a GetResult struct returned from a networks.Get() -// function with extended attributes. -func ExtractGet(r networks.GetResult) (*NetworkExtAttrs, error) { - var s struct { - Network *NetworkExtAttrs `json:"network"` - } - err := r.ExtractInto(&s) - return s.Network, err -} - -// ExtractCreate decorates a CreateResult struct returned from a networks.Create() -// function with extended attributes. -func ExtractCreate(r networks.CreateResult) (*NetworkExtAttrs, error) { - var s struct { - Network *NetworkExtAttrs `json:"network"` - } - err := r.ExtractInto(&s) - return s.Network, err -} - -// ExtractUpdate decorates a UpdateResult struct returned from a -// networks.Update() function with extended attributes. -func ExtractUpdate(r networks.UpdateResult) (*NetworkExtAttrs, error) { - var s struct { - Network *NetworkExtAttrs `json:"network"` - } - err := r.ExtractInto(&s) - return s.Network, err -} - -// ExtractList accepts a Page struct, specifically a NetworkPage struct, and -// extracts the elements into a slice of NetworkExtAttrs structs. In other -// words, a generic collection is mapped into a relevant slice. -func ExtractList(r pagination.Page) ([]NetworkExtAttrs, error) { - var s struct { - Networks []NetworkExtAttrs `json:"networks" json:"networks"` - } - err := (r.(networks.NetworkPage)).ExtractInto(&s) - return s.Networks, err -} diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go index a6e4d24f09..fa8eb8e1f3 100644 --- a/openstack/networking/v2/extensions/provider/testing/results_test.go +++ b/openstack/networking/v2/extensions/provider/testing/results_test.go @@ -9,7 +9,7 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/provider" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/pagination" + nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -24,87 +24,25 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "networks": [ - { - "status": "ACTIVE", - "subnets": [ - "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" - ], - "name": "private-network", - "admin_state_up": true, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "shared": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "provider:segmentation_id": null, - "provider:physical_network": null, - "provider:network_type": "local" - }, - { - "status": "ACTIVE", - "subnets": [ - "08eae331-0402-425a-923c-34f7cfe39c1b" - ], - "name": "private", - "admin_state_up": true, - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "shared": true, - "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "provider:segmentation_id": 1234567890, - "provider:physical_network": null, - "provider:network_type": "local" - } - ] -} - `) + fmt.Fprintf(w, nettest.ListResponse) }) - count := 0 - - networks.List(fake.ServiceClient(), networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := provider.ExtractList(page) - if err != nil { - t.Errorf("Failed to extract networks: %v", err) - return false, err - } - - expected := []provider.NetworkExtAttrs{ - { - Status: "ACTIVE", - Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, - Name: "private-network", - AdminStateUp: true, - TenantID: "4fd44f30292945e481c7b8a0c8908869", - Shared: true, - ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", - NetworkType: "local", - PhysicalNetwork: "", - SegmentationID: "", - }, - { - Status: "ACTIVE", - Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, - Name: "private", - AdminStateUp: true, - TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", - Shared: true, - ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - NetworkType: "local", - PhysicalNetwork: "", - SegmentationID: "1234567890", - }, - } + type NetworkWithExt struct { + networks.Network + provider.NetworkProviderExt + } + var actual []NetworkWithExt - th.CheckDeepEquals(t, expected, actual) + allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() + th.AssertNoErr(t, err) - return true, nil - }) + err = networks.ExtractNetworksInto(allPages, &actual) + th.AssertNoErr(t, err) - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } + th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", actual[0].ID) + th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", actual[1].ID) + th.AssertEquals(t, "local", actual[1].NetworkType) + th.AssertEquals(t, "1234567890", actual[1].SegmentationID) } func TestGet(t *testing.T) { @@ -118,34 +56,21 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "network": { - "status": "ACTIVE", - "subnets": [ - "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" - ], - "name": "private-network", - "provider:physical_network": null, - "admin_state_up": true, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "provider:network_type": "local", - "shared": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "provider:segmentation_id": null - } -} - `) + fmt.Fprintf(w, nettest.GetResponse) }) - res := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - n, err := provider.ExtractGet(res) + var s struct { + networks.Network + provider.NetworkProviderExt + } + err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) - th.AssertEquals(t, "", n.PhysicalNetwork) - th.AssertEquals(t, "local", n.NetworkType) - th.AssertEquals(t, "", n.SegmentationID) + th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", s.ID) + th.AssertEquals(t, "", s.PhysicalNetwork) + th.AssertEquals(t, "local", s.NetworkType) + th.AssertEquals(t, "9876543210", s.SegmentationID) } func TestCreate(t *testing.T) { @@ -157,47 +82,27 @@ func TestCreate(t *testing.T) { 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, ` -{ - "network": { - "name": "sample_network", - "admin_state_up": true - } -} - `) + th.TestJSONRequest(t, r, nettest.CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` -{ - "network": { - "status": "ACTIVE", - "subnets": [ - "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" - ], - "name": "private-network", - "provider:physical_network": null, - "admin_state_up": true, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "provider:network_type": "local", - "shared": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "provider:segmentation_id": null - } -} - `) + fmt.Fprintf(w, nettest.CreateResponse) }) - options := networks.CreateOpts{Name: "sample_network", AdminStateUp: gophercloud.Enabled} - res := networks.Create(fake.ServiceClient(), options) - n, err := provider.ExtractCreate(res) + var s struct { + networks.Network + provider.NetworkProviderExt + } + options := networks.CreateOpts{Name: "private", AdminStateUp: gophercloud.Enabled} + err := networks.Create(fake.ServiceClient(), options).ExtractInto(&s) th.AssertNoErr(t, err) - th.AssertEquals(t, "", n.PhysicalNetwork) - th.AssertEquals(t, "local", n.NetworkType) - th.AssertEquals(t, "", n.SegmentationID) + th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) + th.AssertEquals(t, "", s.PhysicalNetwork) + th.AssertEquals(t, "local", s.NetworkType) + th.AssertEquals(t, "9876543210", s.SegmentationID) } func TestCreateWithMultipleProvider(t *testing.T) { @@ -276,8 +181,7 @@ func TestCreateWithMultipleProvider(t *testing.T) { Segments: segments, } - res := networks.Create(fake.ServiceClient(), providerCreateOpts) - _, err := provider.ExtractCreate(res) + _, err := networks.Create(fake.ServiceClient(), providerCreateOpts).Extract() th.AssertNoErr(t, err) } @@ -290,47 +194,26 @@ func TestUpdate(t *testing.T) { 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, ` -{ - "network": { - "name": "new_network_name", - "admin_state_up": false, - "shared": true - } -} - `) + th.TestJSONRequest(t, r, nettest.UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "network": { - "status": "ACTIVE", - "subnets": [ - "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" - ], - "name": "private-network", - "provider:physical_network": null, - "admin_state_up": true, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "provider:network_type": "local", - "shared": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "provider:segmentation_id": null - } -} - `) + fmt.Fprintf(w, nettest.UpdateResponse) }) + var s struct { + networks.Network + provider.NetworkProviderExt + } + iTrue := true options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: gophercloud.Disabled, Shared: &iTrue} - res := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options) - n, err := provider.ExtractUpdate(res) - + err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).ExtractInto(&s) th.AssertNoErr(t, err) - th.AssertEquals(t, "", n.PhysicalNetwork) - th.AssertEquals(t, "local", n.NetworkType) - th.AssertEquals(t, "", n.SegmentationID) + th.AssertEquals(t, "4e8e5957-649f-477b-9e5b-f1f75b21c03c", s.ID) + th.AssertEquals(t, "", s.PhysicalNetwork) + th.AssertEquals(t, "local", s.NetworkType) + th.AssertEquals(t, "1234567890", s.SegmentationID) } diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index d9289800fd..838afaa976 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -11,11 +11,13 @@ type commonResult struct { // Extract is a function that accepts a result and extracts a network resource. func (r commonResult) Extract() (*Network, error) { - var s struct { - Network *Network `json:"network"` - } + var s Network err := r.ExtractInto(&s) - return s.Network, err + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "network") } // CreateResult represents the result of a create operation. @@ -93,9 +95,11 @@ func (r NetworkPage) IsEmpty() (bool, error) { // and extracts the elements into a slice of Network structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractNetworks(r pagination.Page) ([]Network, error) { - var s struct { - Networks []Network `json:"networks"` - } - err := (r.(NetworkPage)).ExtractInto(&s) - return s.Networks, err + var s []Network + err := ExtractNetworksInto(r, &s) + return s, err +} + +func ExtractNetworksInto(r pagination.Page, v interface{}) error { + return r.(NetworkPage).Result.ExtractIntoSlicePtr(v, "networks") } diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go new file mode 100644 index 0000000000..eec7155b37 --- /dev/null +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -0,0 +1,141 @@ +package testing + +import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" +) + +const ListResponse = ` +{ + "networks": [ + { + "status": "ACTIVE", + "subnets": [ + "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + ], + "name": "public", + "admin_state_up": true, + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "shared": true, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": true + }, + { + "status": "ACTIVE", + "subnets": [ + "08eae331-0402-425a-923c-34f7cfe39c1b" + ], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 1234567890, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": false + } + ] +}` + +const GetResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": [ + "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + ], + "name": "public", + "admin_state_up": true, + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "shared": true, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": true + } +}` + +const CreateRequest = ` +{ + "network": { + "name": "private", + "admin_state_up": true + } +}` + +const CreateResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local" + } +}` + +const CreateOptionalFieldsRequest = ` +{ + "network": { + "name": "public", + "admin_state_up": true, + "shared": true, + "tenant_id": "12345" + } +}` + +const UpdateRequest = ` +{ + "network": { + "name": "new_network_name", + "admin_state_up": false, + "shared": true + } +}` + +const UpdateResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": [], + "name": "new_network_name", + "admin_state_up": false, + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "shared": true, + "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", + "provider:segmentation_id": 1234567890, + "provider:physical_network": null, + "provider:network_type": "local" + } +}` + +var Network1 = networks.Network{ + Status: "ACTIVE", + Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, + Name: "public", + AdminStateUp: true, + TenantID: "4fd44f30292945e481c7b8a0c8908869", + Shared: true, + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", +} + +var Network2 = networks.Network{ + Status: "ACTIVE", + Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, + Name: "private", + AdminStateUp: true, + TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", + Shared: false, + ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", +} + +var ExpectedNetworkSlice = []networks.Network{Network1, Network2} diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 5b9f03d9e4..4ffe864c01 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -22,34 +22,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "networks": [ - { - "status": "ACTIVE", - "subnets": [ - "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" - ], - "name": "private-network", - "admin_state_up": true, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "shared": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" - }, - { - "status": "ACTIVE", - "subnets": [ - "08eae331-0402-425a-923c-34f7cfe39c1b" - ], - "name": "private", - "admin_state_up": true, - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "shared": true, - "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324" - } - ] -} - `) + fmt.Fprintf(w, ListResponse) }) client := fake.ServiceClient() @@ -63,28 +36,7 @@ func TestList(t *testing.T) { return false, err } - expected := []networks.Network{ - { - Status: "ACTIVE", - Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, - Name: "private-network", - AdminStateUp: true, - TenantID: "4fd44f30292945e481c7b8a0c8908869", - Shared: true, - ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", - }, - { - Status: "ACTIVE", - Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, - Name: "private", - AdminStateUp: true, - TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", - Shared: true, - ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - }, - } - - th.CheckDeepEquals(t, expected, actual) + th.CheckDeepEquals(t, ExpectedNetworkSlice, actual) return true, nil }) @@ -105,33 +57,12 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "network": { - "status": "ACTIVE", - "subnets": [ - "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" - ], - "name": "private-network", - "admin_state_up": true, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "shared": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" - } -} - `) + fmt.Fprintf(w, GetResponse) }) n, err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) - - th.AssertEquals(t, n.Status, "ACTIVE") - th.AssertDeepEquals(t, n.Subnets, []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}) - th.AssertEquals(t, n.Name, "private-network") - th.AssertEquals(t, n.AdminStateUp, true) - th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") - th.AssertEquals(t, n.Shared, true) - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.CheckDeepEquals(t, &Network1, n) } func TestCreate(t *testing.T) { @@ -143,45 +74,20 @@ func TestCreate(t *testing.T) { 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, ` -{ - "network": { - "name": "sample_network", - "admin_state_up": true - } -} - `) - + th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` -{ - "network": { - "status": "ACTIVE", - "subnets": [], - "name": "net1", - "admin_state_up": true, - "tenant_id": "9bacb3c5d39d41a79512987f338cf177", - "shared": false, - "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c" - } -} - `) + fmt.Fprintf(w, CreateResponse) }) iTrue := true - options := networks.CreateOpts{Name: "sample_network", AdminStateUp: &iTrue} + options := networks.CreateOpts{Name: "private", AdminStateUp: &iTrue} n, err := networks.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") - th.AssertDeepEquals(t, n.Subnets, []string{}) - th.AssertEquals(t, n.Name, "net1") - th.AssertEquals(t, n.AdminStateUp, true) - th.AssertEquals(t, n.TenantID, "9bacb3c5d39d41a79512987f338cf177") - th.AssertEquals(t, n.Shared, false) - th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c") + th.AssertDeepEquals(t, &Network2, n) } func TestCreateWithOptionalFields(t *testing.T) { @@ -193,23 +99,14 @@ func TestCreateWithOptionalFields(t *testing.T) { 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, ` -{ - "network": { - "name": "sample_network", - "admin_state_up": true, - "shared": true, - "tenant_id": "12345" - } -} - `) + th.TestJSONRequest(t, r, CreateOptionalFieldsRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{}`) }) iTrue := true - options := networks.CreateOpts{Name: "sample_network", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345"} + options := networks.CreateOpts{Name: "public", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345"} _, err := networks.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -223,32 +120,12 @@ func TestUpdate(t *testing.T) { 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, ` -{ - "network": { - "name": "new_network_name", - "admin_state_up": false, - "shared": true - } -} - `) + th.TestJSONRequest(t, r, UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "network": { - "status": "ACTIVE", - "subnets": [], - "name": "new_network_name", - "admin_state_up": false, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "shared": true, - "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c" - } -} - `) + fmt.Fprintf(w, UpdateResponse) }) iTrue, iFalse := true, false diff --git a/results.go b/results.go index 76c16ef8ff..6a5b0baaa6 100644 --- a/results.go +++ b/results.go @@ -78,6 +78,53 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { return err } + toValue := reflect.ValueOf(to) + if toValue.Kind() == reflect.Ptr { + toValue = toValue.Elem() + } + + switch toValue.Kind() { + case reflect.Slice: + typeOfV := toValue.Type().Elem() + if typeOfV.Kind() == reflect.Struct { + if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { + newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) + newType := reflect.New(typeOfV).Elem() + + for _, v := range m[label].([]interface{}) { + b, err := json.Marshal(v) + if err != nil { + return err + } + + for i := 0; i < newType.NumField(); i++ { + s := newType.Field(i).Addr().Interface() + err = json.NewDecoder(bytes.NewReader(b)).Decode(s) + if err != nil { + return err + } + } + newSlice = reflect.Append(newSlice, newType) + } + toValue.Set(newSlice) + } + } + case reflect.Struct: + typeOfV := toValue.Type() + if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { + for i := 0; i < toValue.NumField(); i++ { + toField := toValue.Field(i) + if toField.Kind() == reflect.Struct { + s := toField.Addr().Interface() + err = json.NewDecoder(bytes.NewReader(b)).Decode(s) + if err != nil { + return err + } + } + } + } + } + err = json.Unmarshal(b, &to) return err } diff --git a/testing/results_test.go b/testing/results_test.go new file mode 100644 index 0000000000..ddcb1322a4 --- /dev/null +++ b/testing/results_test.go @@ -0,0 +1,208 @@ +package testing + +import ( + "encoding/json" + "testing" + + "github.com/gophercloud/gophercloud" + th "github.com/gophercloud/gophercloud/testhelper" +) + +var singleResponse = ` +{ + "person": { + "name": "Bill", + "email": "bill@example.com", + "location": "Canada" + } +} +` + +var multiResponse = ` +{ + "people": [ + { + "name": "Bill", + "email": "bill@example.com", + "location": "Canada" + }, + { + "name": "Ted", + "email": "ted@example.com", + "location": "Mexico" + } + ] +} +` + +type TestPerson struct { + Name string `json:"-"` + Email string `json:"email"` +} + +func (r *TestPerson) UnmarshalJSON(b []byte) error { + type tmp TestPerson + var s struct { + tmp + Name string `json:"name"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = TestPerson(s.tmp) + r.Name = s.Name + " unmarshalled" + + return nil +} + +type TestPersonExt struct { + Location string `json:"-"` +} + +func (r *TestPersonExt) UnmarshalJSON(b []byte) error { + type tmp TestPersonExt + var s struct { + tmp + Location string `json:"location"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = TestPersonExt(s.tmp) + r.Location = s.Location + " unmarshalled" + + return nil +} + +type TestPersonWithExtensions struct { + TestPerson + TestPersonExt +} + +type TestPersonWithExtensionsNamed struct { + TestPerson TestPerson + TestPersonExt TestPersonExt +} + +// TestUnmarshalAnonymousStruct tests if UnmarshalJSON is called on each +// of the anonymous structs contained in an overarching struct. +func TestUnmarshalAnonymousStructs(t *testing.T) { + var actual TestPersonWithExtensions + + var dejson interface{} + sejson := []byte(singleResponse) + err := json.Unmarshal(sejson, &dejson) + if err != nil { + t.Fatal(err) + } + + var singleResult = gophercloud.Result{ + Body: dejson, + } + + err = singleResult.ExtractIntoStructPtr(&actual, "person") + th.AssertNoErr(t, err) + + th.AssertEquals(t, "Bill unmarshalled", actual.Name) + th.AssertEquals(t, "Canada unmarshalled", actual.Location) +} + +// TestUnmarshalSliceofAnonymousStructs tests if UnmarshalJSON is called on each +// of the anonymous structs contained in an overarching struct slice. +func TestUnmarshalSliceOfAnonymousStructs(t *testing.T) { + var actual []TestPersonWithExtensions + + var dejson interface{} + sejson := []byte(multiResponse) + err := json.Unmarshal(sejson, &dejson) + if err != nil { + t.Fatal(err) + } + + var multiResult = gophercloud.Result{ + Body: dejson, + } + + err = multiResult.ExtractIntoSlicePtr(&actual, "people") + th.AssertNoErr(t, err) + + th.AssertEquals(t, "Bill unmarshalled", actual[0].Name) + th.AssertEquals(t, "Canada unmarshalled", actual[0].Location) + th.AssertEquals(t, "Ted unmarshalled", actual[1].Name) + th.AssertEquals(t, "Mexico unmarshalled", actual[1].Location) +} + +// TestUnmarshalSliceOfStruct tests if extracting results from a "normal" +// struct still works correctly. +func TestUnmarshalSliceofStruct(t *testing.T) { + var actual []TestPerson + + var dejson interface{} + sejson := []byte(multiResponse) + err := json.Unmarshal(sejson, &dejson) + if err != nil { + t.Fatal(err) + } + + var multiResult = gophercloud.Result{ + Body: dejson, + } + + err = multiResult.ExtractIntoSlicePtr(&actual, "people") + th.AssertNoErr(t, err) + + th.AssertEquals(t, "Bill unmarshalled", actual[0].Name) + th.AssertEquals(t, "Ted unmarshalled", actual[1].Name) +} + +// TestUnmarshalNamedStruct tests if the result is empty. +func TestUnmarshalNamedStructs(t *testing.T) { + var actual TestPersonWithExtensionsNamed + + var dejson interface{} + sejson := []byte(singleResponse) + err := json.Unmarshal(sejson, &dejson) + if err != nil { + t.Fatal(err) + } + + var singleResult = gophercloud.Result{ + Body: dejson, + } + + err = singleResult.ExtractIntoStructPtr(&actual, "person") + th.AssertNoErr(t, err) + + th.AssertEquals(t, "", actual.TestPerson.Name) + th.AssertEquals(t, "", actual.TestPersonExt.Location) +} + +// TestUnmarshalSliceofNamedStructs tests if the result is empty. +func TestUnmarshalSliceOfNamedStructs(t *testing.T) { + var actual []TestPersonWithExtensionsNamed + + var dejson interface{} + sejson := []byte(multiResponse) + err := json.Unmarshal(sejson, &dejson) + if err != nil { + t.Fatal(err) + } + + var multiResult = gophercloud.Result{ + Body: dejson, + } + + err = multiResult.ExtractIntoSlicePtr(&actual, "people") + th.AssertNoErr(t, err) + + th.AssertEquals(t, "", actual[0].TestPerson.Name) + th.AssertEquals(t, "", actual[0].TestPersonExt.Location) + th.AssertEquals(t, "", actual[1].TestPerson.Name) + th.AssertEquals(t, "", actual[1].TestPersonExt.Location) +} From 189036ab74e2301b98a4731494bca8e78039cdf3 Mon Sep 17 00:00:00 2001 From: pospispa Date: Tue, 13 Jun 2017 19:00:42 +0200 Subject: [PATCH 0009/2296] Shared File Systems: Share Export Locations In order to mount a share one needs to know the share export location. That's why the Get Share Export Locations request is added. --- .../sharedfilesystems/v2/shares/requests.go | 6 ++++ .../sharedfilesystems/v2/shares/results.go | 28 +++++++++++++++++++ openstack/sharedfilesystems/v2/shares/urls.go | 4 +++ 3 files changed, 38 insertions(+) diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index cfa8460aeb..d2543a7ec9 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -79,3 +79,9 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } + +// GetExportLocations will get shareID's export locations +func GetExportLocations(client *gophercloud.ServiceClient, id string) (r GetExportLocationsResult) { + _, r.Err = client.Get(getExportLocationsURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 224d1dfd17..7f4f8c27dd 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -110,3 +110,31 @@ type DeleteResult struct { type GetResult struct { commonResult } + +// ExportLocation contains all information associated with a share export location +type ExportLocation struct { + // 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"` + // The share export location UUID. + ID string `json:"id"` + // 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"` +} + +// ExtractExportLocations will get the Export Locations from the commonResult +func (r commonResult) ExtractExportLocations() ([]ExportLocation, error) { + var s struct { + GetExportLocationsRes []ExportLocation `json:"export_locations"` + } + err := r.ExtractInto(&s) + return s.GetExportLocationsRes, err +} + +// GetExportLocationsResult contains the result. +type GetExportLocationsResult struct { + commonResult +} diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index 309f071bae..e0832c2bdd 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -13,3 +13,7 @@ func deleteURL(c *gophercloud.ServiceClient, id string) string { func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id) } + +func getExportLocationsURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "export_locations") +} From 7b621d30d2fbed70cbdd410a5fec50a198eade77 Mon Sep 17 00:00:00 2001 From: pospispa Date: Wed, 14 Jun 2017 13:13:10 +0200 Subject: [PATCH 0010/2296] Added unit tests for GET Export Locations request. --- .../v2/shares/testing/fixtures.go | 22 +++++++++++++++++ .../v2/shares/testing/request_test.go | 24 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index a747f0805e..368a0be259 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -141,3 +141,25 @@ func MockGetResponse(t *testing.T) { fmt.Fprintf(w, getResponse) }) } + +var getExportLocationsResponse = `{ + "export_locations": [ + { + "path": "127.0.0.1:/var/lib/manila/mnt/share-9a922036-ad26-4d27-b955-7a1e285fa74d", + "share_instance_id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", + "is_admin_only": false, + "id": "80ed63fc-83bc-4afc-b881-da4a345ac83d", + "preferred": false + } + ] +}` + +// MockGetExportLocationsResponse creates a mock get export locations response +func MockGetExportLocationsResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/export_locations", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, getExportLocationsResponse) + }) +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index 5b700a6823..e9f37b8857 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -82,3 +82,27 @@ func TestGet(t *testing.T) { }, }) } + +func TestGetExportLocationsSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetExportLocationsResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Get Export Locations is 2.14 + c.Microversion = "2.14" + + s, err := shares.GetExportLocations(c, shareID).ExtractExportLocations() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, s, []shares.ExportLocation{ + { + Path: "127.0.0.1:/var/lib/manila/mnt/share-9a922036-ad26-4d27-b955-7a1e285fa74d", + ShareInstanceID: "011d21e2-fbc3-4e4a-9993-9ea223f73264", + IsAdminOnly: false, + ID: "80ed63fc-83bc-4afc-b881-da4a345ac83d", + Preferred: false, + }, + }) +} From a41308eee6ec9ec6aed5ddedcae43039b05c004f Mon Sep 17 00:00:00 2001 From: pospispa Date: Thu, 10 Aug 2017 17:08:57 +0200 Subject: [PATCH 0011/2296] Fixes for the first round of code reviews. --- openstack/sharedfilesystems/v2/shares/results.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 7f4f8c27dd..0c1330c263 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -117,21 +117,26 @@ type ExportLocation struct { 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. + // 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"` // The share export location UUID. ID string `json:"id"` - // 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 + // 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"` } // ExtractExportLocations will get the Export Locations from the commonResult func (r commonResult) ExtractExportLocations() ([]ExportLocation, error) { var s struct { - GetExportLocationsRes []ExportLocation `json:"export_locations"` + ExportLocations []ExportLocation `json:"export_locations"` } err := r.ExtractInto(&s) - return s.GetExportLocationsRes, err + return s.ExportLocations, err } // GetExportLocationsResult contains the result. From 52f5e13efe14a85e9378a79e3ef5fe453f421bd8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 10 Aug 2017 21:51:46 -0600 Subject: [PATCH 0012/2296] BlockStorage v2: Fix small issues with volumeactions This commit fixes two small issues found in the volumeactions package: 1. ExtendSize does not return a body. Prior to this patch, an EOF error was returned. 2. commonResult was being used only for InitializeConnection which is not really common. --- .../extensions/volumeactions/requests.go | 2 +- .../extensions/volumeactions/results.go | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index 939d397da9..b74b4ff85b 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -209,7 +209,7 @@ func ExtendSize(client *gophercloud.ServiceClient, id string, opts ExtendSizeOpt r.Err = err return } - _, r.Err = client.Post(extendSizeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(extendSizeURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index ae97ac407a..0e0a7c4063 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -42,12 +42,18 @@ type TerminateConnectionResult struct { gophercloud.ErrResult } -type commonResult struct { +// InitializeConnectionResult contains the response body and error from an +// InitializeConnection request. +type InitializeConnectionResult struct { gophercloud.Result } -// Extract will get the Volume object out of the commonResult object. -func (r commonResult) Extract() (map[string]interface{}, error) { +// Extract will get the connection information out of the +// InitializeConnectionResult object. +// +// This will be a generic map[string]interface{} and the results will be +// dependent on the type of connection made. +func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { var s struct { ConnectionInfo map[string]interface{} `json:"connection_info"` } @@ -150,11 +156,6 @@ func (r UploadImageResult) Extract() (VolumeImage, error) { return s.VolumeImage, err } -// InitializeConnectionResult contains the response body and error from a Get request. -type InitializeConnectionResult struct { - commonResult -} - // ExtendSizeResult contains the response body and error from an ExtendSize request. type ExtendSizeResult struct { gophercloud.ErrResult From 4b675b76fa2c80dff3eed20f2e87f25de0af5733 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 10 Aug 2017 20:58:39 -0600 Subject: [PATCH 0013/2296] BlockStorage v2: Updating docs for schedulerstats --- .../extensions/schedulerstats/doc.go | 24 +++++++++++++++++-- .../extensions/schedulerstats/requests.go | 10 ++++---- .../extensions/schedulerstats/results.go | 15 +++++++++--- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/openstack/blockstorage/extensions/schedulerstats/doc.go b/openstack/blockstorage/extensions/schedulerstats/doc.go index 965cc5654f..ea3a4d3ddc 100644 --- a/openstack/blockstorage/extensions/schedulerstats/doc.go +++ b/openstack/blockstorage/extensions/schedulerstats/doc.go @@ -1,3 +1,23 @@ -// Package schedulerstats gives information about block storage pool capacity -// and utilisation +/* +Package schedulerstats returns information about block storage pool capacity +and utilisation. Example: + + listOpts := schedulerstats.ListOpts{ + Detail: true, + } + + allPages, err := schedulerstats.List(client, listOpts).AllPages() + if err != nil { + panic("Unable to query schedulerstats: %s", err) + } + + allStats, err := schedulerstats.ExtractStoragePools(allPages) + if err != nil { + panic("Unable to extract pools: %s", err) + } + + for _, stat := range allStats { + fmt.Printf("%+v\n", stat) + } +*/ package schedulerstats diff --git a/openstack/blockstorage/extensions/schedulerstats/requests.go b/openstack/blockstorage/extensions/schedulerstats/requests.go index 73bd49eb05..7b374dcd86 100644 --- a/openstack/blockstorage/extensions/schedulerstats/requests.go +++ b/openstack/blockstorage/extensions/schedulerstats/requests.go @@ -12,22 +12,22 @@ type ListOptsBuilder interface { } // ListOpts controls the view of data returned (e.g globally or per project) -// via tenant_id and the verbosity via detail +// via tenant_id and the verbosity via detail. type ListOpts struct { - // ID of the tenant to look up storage pools for + // ID of the tenant to look up storage pools for. TenantID string `q:"tenant_id"` - // Whether to list extended details + // Whether to list extended details. Detail bool `q:"detail"` } -// Formats a ListOpts into a query string. +// ToStoragePoolsListQuery formats a ListOpts into a query string. func (opts ListOpts) ToStoragePoolsListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } -// List makes a request against the API to list hypervisors. +// List makes a request against the API to list storage pool information. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := storagePoolsListURL(client) if opts != nil { diff --git a/openstack/blockstorage/extensions/schedulerstats/results.go b/openstack/blockstorage/extensions/schedulerstats/results.go index 68f33264d0..3da8f80c30 100644 --- a/openstack/blockstorage/extensions/schedulerstats/results.go +++ b/openstack/blockstorage/extensions/schedulerstats/results.go @@ -7,16 +7,18 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// Minimum set of driver capabilities only +// Capabilities represents the information of an individual StoragePool. type Capabilities struct { - // Required Fields + // The following fields should be present in all storage drivers. DriverVersion string `json:"driver_version"` FreeCapacityGB float64 `json:"-"` StorageProtocol string `json:"storage_protocol"` TotalCapacityGB float64 `json:"-"` VendorName string `json:"vendor_name"` VolumeBackendName string `json:"volume_backend_name"` - // Optional Fields + + // The following fields are optional and may have empty values depending + // on the storage driver in use. ReservedPercentage int64 `json:"reserved_percentage"` LocationInfo string `json:"location_info"` QoSSupport bool `json:"QoS_support"` @@ -31,6 +33,8 @@ type Capabilities struct { SparseCopyVolume bool `json:"sparse_copy_volume"` } +// StoragePool represents an individual StoragePool retrieved from the +// schedulerstats API. type StoragePool struct { Name string `json:"name"` Capabilities Capabilities `json:"capabilities"` @@ -71,15 +75,20 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { return nil } +// StoragePoolPage is a single page of all List results. type StoragePoolPage struct { pagination.SinglePageBase } +// IsEmpty satisfies the IsEmpty method of the Page interface. It returns true +// if a List contains no results. func (page StoragePoolPage) IsEmpty() (bool, error) { va, err := ExtractStoragePools(page) return len(va) == 0, err } +// ExtractStoragePools takes a List result and extracts the collection of +// StoragePools returned by the API. func ExtractStoragePools(p pagination.Page) ([]StoragePool, error) { var s struct { StoragePools []StoragePool `json:"pools"` From ede198658b8faa8de4bfea5daa949d2b27d13130 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 10 Aug 2017 20:58:54 -0600 Subject: [PATCH 0014/2296] BlockStorage v2: Adding acceptance test for schedulerstats --- .../extensions/schedulerstats_test.go | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 acceptance/openstack/blockstorage/extensions/schedulerstats_test.go diff --git a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go new file mode 100644 index 0000000000..76093f4c12 --- /dev/null +++ b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -0,0 +1,36 @@ +// +build acceptance blockstorage + +package extensions + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" +) + +func TestSchedulerStatsList(t *testing.T) { + blockClient, err := clients.NewBlockStorageV2Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + listOpts := schedulerstats.ListOpts{ + Detail: true, + } + + allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to query schedulerstats: %v", err) + } + + allStats, err := schedulerstats.ExtractStoragePools(allPages) + if err != nil { + t.Fatalf("Unable to extract pools: %v", err) + } + + for _, stat := range allStats { + tools.PrintResource(t, stat) + } +} From 3e1b2d44800f62a277da21a65724c1486fa24660 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 10 Aug 2017 22:18:49 -0600 Subject: [PATCH 0015/2296] BlockStorage v2: Clean-up volumetenants --- .../extensions/volumetenants/doc.go | 26 +++++++++++++++++++ .../extensions/volumetenants/results.go | 9 ++----- .../v2/volumes/testing/requests_test.go | 4 +-- 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 openstack/blockstorage/extensions/volumetenants/doc.go diff --git a/openstack/blockstorage/extensions/volumetenants/doc.go b/openstack/blockstorage/extensions/volumetenants/doc.go new file mode 100644 index 0000000000..2f501649fe --- /dev/null +++ b/openstack/blockstorage/extensions/volumetenants/doc.go @@ -0,0 +1,26 @@ +/* +Package volumetenants provides the ability to extend a volume result with +tenant/project information. Example: + + type VolumeWithTenant struct { + volumes.Volume + volumetenants.VolumeTenantExt + } + + var allVolumes []VolumeWithTenant + + allPages, err := volumes.List(client, nil).AllPages() + if err != nil { + panic("Unable to retrieve volumes: %s", err) + } + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + if err != nil { + panic("Unable to extract volumes: %s", err) + } + + for _, volume := range allVolumes { + fmt.Println(volume.TenantID) + } +*/ +package volumetenants diff --git a/openstack/blockstorage/extensions/volumetenants/results.go b/openstack/blockstorage/extensions/volumetenants/results.go index b7d51c72b7..821e523b7f 100644 --- a/openstack/blockstorage/extensions/volumetenants/results.go +++ b/openstack/blockstorage/extensions/volumetenants/results.go @@ -1,12 +1,7 @@ package volumetenants -// VolumeExt is an extension to the base Volume object -type VolumeExt struct { +// VolumeTenantExt is an extension to the base Volume object +type VolumeTenantExt struct { // TenantID is the id of the project that owns the volume. TenantID string `json:"os-vol-tenant-attr:tenant_id"` } - -// UnmarshalJSON to override default -func (r *VolumeExt) UnmarshalJSON(b []byte) error { - return nil -} diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 0a18544850..cd7dcf5ecb 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -102,7 +102,7 @@ func TestListAllWithExtensions(t *testing.T) { type VolumeWithExt struct { volumes.Volume - volumetenants.VolumeExt + volumetenants.VolumeTenantExt } allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() @@ -244,7 +244,7 @@ func TestGetWithExtensions(t *testing.T) { var s struct { volumes.Volume - volumetenants.VolumeExt + volumetenants.VolumeTenantExt } err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) From 2b90137e07c9b52d4e67e2730325e4449934d683 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 11 Aug 2017 08:15:58 -0600 Subject: [PATCH 0016/2296] Docs: Top level doc updates (#450) --- auth_options.go | 32 +++++++++++++++++++++++++++----- doc.go | 34 +++++++++++++++++++++++++++++----- endpoint_search.go | 2 +- params.go | 26 ++++++++++++++++++++++---- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/auth_options.go b/auth_options.go index 19c08341af..f438ab2f2e 100644 --- a/auth_options.go +++ b/auth_options.go @@ -9,12 +9,32 @@ ProviderClient representing an active session on that provider. Its fields are the union of those recognized by each identity implementation and provider. + +An example of manually providing authentication information: + + opts := gophercloud.AuthOptions{ + IdentityEndpoint: "https://openstack.example.com:5000/v2.0", + Username: "{username}", + Password: "{password}", + TenantID: "{tenant_id}", + } + + provider, err := openstack.AuthenticatedClient(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) */ type AuthOptions struct { // IdentityEndpoint specifies the HTTP endpoint that is required to work with // the Identity API of the appropriate version. While it's ultimately needed by // all of the identity services, it will often be populated by a provider-level // function. + // + // The IdentityEndpoint is typically referred to as the "auth_url" or + // "OS_AUTH_URL" in the information provided by the cloud operator. IdentityEndpoint string `json:"-"` // Username is required if using Identity V2 API. Consult with your provider's @@ -39,7 +59,7 @@ type AuthOptions struct { // If DomainID or DomainName are provided, they will also apply to TenantName. // It is not currently possible to authenticate with Username and a Domain // and scope to a Project in a different Domain by using TenantName. To - // accomplish that, the ProjectID will need to be provided to the TenantID + // accomplish that, the ProjectID will need to be provided as the TenantID // option. TenantID string `json:"tenantId,omitempty"` TenantName string `json:"tenantName,omitempty"` @@ -50,10 +70,12 @@ type AuthOptions struct { // false, it will not cache these settings, but re-authentication will not be // possible. This setting defaults to false. // - // NOTE: The reauth function will try to re-authenticate endlessly if left unchecked. - // The way to limit the number of attempts is to provide a custom HTTP client to the provider client - // and provide a transport that implements the RoundTripper interface and stores the number of failed retries. - // For an example of this, see here: https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311 + // NOTE: The reauth function will try to re-authenticate endlessly if left + // unchecked. The way to limit the number of attempts is to provide a custom + // HTTP client to the provider client and provide a transport that implements + // the RoundTripper interface and stores the number of failed retries. For an + // example of this, see here: + // https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311 AllowReauth bool `json:"-"` // TokenID allows users to authenticate (possibly as another user) with an diff --git a/doc.go b/doc.go index b559516f91..30067aa352 100644 --- a/doc.go +++ b/doc.go @@ -3,11 +3,17 @@ Package gophercloud provides a multi-vendor interface to OpenStack-compatible clouds. The library has a three-level hierarchy: providers, services, and resources. -Provider structs represent the service providers that offer and manage a -collection of services. The IdentityEndpoint is typically refered to as -"auth_url" in information provided by the cloud operator. Additionally, -the cloud may refer to TenantID or TenantName as project_id and project_name. -These are defined like so: +Authenticating with Providers + +Provider structs represent the cloud providers that offer and manage a +collection of services. You will generally want to create one Provider +client per OpenStack cloud. + +Use your OpenStack credentials to create a Provider client. The +IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in +information provided by the cloud operator. Additionally, the cloud may refer to +TenantID or TenantName as project_id and project_name. Credentials are +specified like so: opts := gophercloud.AuthOptions{ IdentityEndpoint: "https://openstack.example.com:5000/v2.0", @@ -18,6 +24,16 @@ These are defined like so: provider, err := openstack.AuthenticatedClient(opts) +You may also use the openstack.AuthOptionsFromEnv() helper function. This +function reads in standard environment variables frequently found in an +OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant" +instead of "project". + + opts, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(opts) + +Service Clients + Service structs are specific to a provider and handle all of the logic and operations for a particular OpenStack service. Examples of services include: Compute, Object Storage, Block Storage. In order to define one, you need to @@ -27,6 +43,8 @@ pass in the parent provider, like so: client := openstack.NewComputeV2(provider, opts) +Resources + Resource structs are the domain models that services make use of in order to work with and represent the state of API resources: @@ -62,6 +80,12 @@ of results: return true, nil }) +If you want to obtain the entire collection of pages without doing any +intermediary processing on each page, you can use the AllPages method: + + allPages, err := servers.List(client, nil).AllPages() + allServers, err := servers.ExtractServers(allPages) + This top-level package contains utility functions and data types that are used throughout the provider and service packages. Of particular note for end users are the AuthOptions and EndpointOpts structs. diff --git a/endpoint_search.go b/endpoint_search.go index 9887947f61..2fbc3c97f1 100644 --- a/endpoint_search.go +++ b/endpoint_search.go @@ -27,7 +27,7 @@ const ( // unambiguously identify one, and only one, endpoint within the catalog. // // Usually, these are passed to service client factory functions in a provider -// package, like "rackspace.NewComputeV2()". +// package, like "openstack.NewComputeV2()". type EndpointOpts struct { // Type [required] is the service type for the client (e.g., "compute", // "object-store"). Generally, this will be supplied by the service client diff --git a/params.go b/params.go index e484fe1c1e..6afc8f8b72 100644 --- a/params.go +++ b/params.go @@ -10,10 +10,28 @@ import ( "time" ) -// BuildRequestBody builds a map[string]interface from the given `struct`. If -// parent is not the empty string, the final map[string]interface returned will -// encapsulate the built one -// +/* +BuildRequestBody builds a map[string]interface from the given `struct`. If +parent is not an empty string, the final map[string]interface returned will +encapsulate the built one. For example: + + disk := 1 + createOpts := flavors.CreateOpts{ + ID: "1", + Name: "m1.tiny", + Disk: &disk, + RAM: 512, + VCPUs: 1, + RxTxFactor: 1.0, + } + + body, err := gophercloud.BuildRequestBody(createOpts, "flavor") + +The above example can be run as-is, however it is recommended to look at how +BuildRequestBody is used within Gophercloud to more fully understand how it +fits within the request process as a whole rather than use it directly as shown +above. +*/ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) { optsValue := reflect.ValueOf(opts) if optsValue.Kind() == reflect.Ptr { From 8a1afadf22396fc5a6f2c94b889163cc4835e2ef Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 11 Aug 2017 09:40:38 -0600 Subject: [PATCH 0017/2296] SharedFileSystems v2: Restrict ExtractExportLocations to only GetExportLocationsResults (#449) * SharedFileSystems v2: Restrict ExtractExportLocations to only GetExportLocationsResults * SharedFileSystems v2: Further changes to ExportLocations results * SharedFileSystems v2: Fix comment --- .../sharedfilesystems/v2/shares/results.go | 21 ++++++++++--------- .../v2/shares/testing/request_test.go | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 0c1330c263..db7827f7ef 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -96,21 +96,27 @@ func (r commonResult) Extract() (*Share, error) { return s.Share, err } -// CreateResult contains the result.. +// CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult } -// DeleteResult contains the delete results +// DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult } -// GetResult contains the get result +// GetResult contains the response body and error from a Get request. type GetResult struct { commonResult } +// GetExportLocationsResult contains the result body and error from an +// GetExportLocations request. +type GetExportLocationsResult struct { + gophercloud.Result +} + // ExportLocation contains all information associated with a share export location type ExportLocation struct { // The export location path that should be used for mount operation. @@ -130,16 +136,11 @@ type ExportLocation struct { Preferred bool `json:"preferred"` } -// ExtractExportLocations will get the Export Locations from the commonResult -func (r commonResult) ExtractExportLocations() ([]ExportLocation, error) { +// Extract will get the Export Locations from the commonResult +func (r GetExportLocationsResult) Extract() ([]ExportLocation, error) { var s struct { ExportLocations []ExportLocation `json:"export_locations"` } err := r.ExtractInto(&s) return s.ExportLocations, err } - -// GetExportLocationsResult contains the result. -type GetExportLocationsResult struct { - commonResult -} diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index e9f37b8857..f2253769fc 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -93,7 +93,7 @@ func TestGetExportLocationsSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for Get Export Locations is 2.14 c.Microversion = "2.14" - s, err := shares.GetExportLocations(c, shareID).ExtractExportLocations() + s, err := shares.GetExportLocations(c, shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, []shares.ExportLocation{ From 5e241cb959fdf31858958422758b5b2d405e2615 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 10 Aug 2017 21:46:05 -0600 Subject: [PATCH 0018/2296] BlockStorage v2: Updating volumeaction docs --- .../extensions/volumeactions/doc.go | 89 ++++++++++++++++++- .../extensions/volumeactions/requests.go | 44 +++++---- .../extensions/volumeactions/results.go | 56 ++++++++---- 3 files changed, 152 insertions(+), 37 deletions(-) diff --git a/openstack/blockstorage/extensions/volumeactions/doc.go b/openstack/blockstorage/extensions/volumeactions/doc.go index 0935fdbd59..11b46c1bbf 100644 --- a/openstack/blockstorage/extensions/volumeactions/doc.go +++ b/openstack/blockstorage/extensions/volumeactions/doc.go @@ -1,5 +1,86 @@ -// Package volumeactions provides information and interaction with volumes in the -// OpenStack Block Storage service. A volume is a detachable block storage -// device, akin to a USB hard drive. It can only be attached to one instance at -// a time. +/* +Package volumeactions provides information and interaction with volumes in the +OpenStack Block Storage service. A volume is a detachable block storage +device, akin to a USB hard drive. + +Example of Attaching a Volume to an Instance + + attachOpts := volumeactions.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: server.ID, + } + + err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr() + if err != nil { + panic("Unable to attach volume: %s", err) + } + + detachOpts := volumeactions.DetachOpts{ + AttachmentID: volume.Attachments[0].AttachmentID, + } + + err = volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr() + if err != nil { + panic("Unable to detach volume: %s", err) + } + + +Example of Creating an Image from a Volume + + uploadImageOpts := volumeactions.UploadImageOpts{ + ImageName: "my_vol", + Force: true, + } + + volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() + if err != nil { + panic("Unable to upload image: %s", err) + } + + fmt.Printf("%+v\n", volumeImage) + +Example of Extending a Volume's Size + + extendOpts := volumeactions.ExtendSizeOpts{ + NewSize: 100, + } + + err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() + if err != nil { + panic("Unable to resize volume: %s", err) + } + +Example of Initializing a Volume Connection + + connectOpts := &volumeactions.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + connectionInfo, err := volumeactions.InitializeConnection(client, volume.ID, connectOpts).Extract() + if err != nil { + panic("Unable to initialize connection: %s", err) + } + + fmt.Println("%+v\n", connectionInfo["data"]) + + terminateOpts := &volumeactions.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + err = volumeactions.TerminateConnection(client, volume.ID, terminateOpts).ExtractErr() + if err != nil { + panic("Unable to terminate connection: %s", err) + } +*/ package volumeactions diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index b74b4ff85b..375e262cdc 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -13,7 +13,7 @@ type AttachOptsBuilder interface { // AttachMode describes the attachment mode for volumes. type AttachMode string -// These constants determine how a volume is attached +// These constants determine how a volume is attached. const ( ReadOnly AttachMode = "ro" ReadWrite AttachMode = "rw" @@ -21,13 +21,16 @@ const ( // AttachOpts contains options for attaching a Volume. type AttachOpts struct { - // The mountpoint of this volume + // The mountpoint of this volume. MountPoint string `json:"mountpoint,omitempty"` - // The nova instance ID, can't set simultaneously with HostName + + // The nova instance ID, can't set simultaneously with HostName. InstanceUUID string `json:"instance_uuid,omitempty"` - // The hostname of baremetal host, can't set simultaneously with InstanceUUID + + // The hostname of baremetal host, can't set simultaneously with InstanceUUID. HostName string `json:"host_name,omitempty"` - // Mount mode of this volume + + // Mount mode of this volume. Mode AttachMode `json:"mode,omitempty"` } @@ -50,7 +53,7 @@ func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder return } -// BeginDetach will mark the volume as detaching +// BeginDetach will mark the volume as detaching. func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} _, r.Err = client.Post(beginDetachingURL(client, id), b, nil, &gophercloud.RequestOpts{ @@ -65,7 +68,9 @@ type DetachOptsBuilder interface { ToVolumeDetachMap() (map[string]interface{}, error) } +// DetachOpts contains options for detaching a Volume. type DetachOpts struct { + // AttachmentID is the ID of the attachment between a volume and instance. AttachmentID string `json:"attachment_id,omitempty"` } @@ -75,7 +80,7 @@ func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "os-detach") } -// Detach will detach a volume based on volume id. +// Detach will detach a volume based on volume ID. func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { b, err := opts.ToVolumeDetachMap() if err != nil { @@ -88,7 +93,7 @@ func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder return } -// Reserve will reserve a volume based on volume id. +// Reserve will reserve a volume based on volume ID. func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { b := map[string]interface{}{"os-reserve": make(map[string]interface{})} _, r.Err = client.Post(reserveURL(client, id), b, nil, &gophercloud.RequestOpts{ @@ -97,7 +102,7 @@ func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { return } -// Unreserve will unreserve a volume based on volume id. +// Unreserve will unreserve a volume based on volume ID. func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) { b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} _, r.Err = client.Post(unreserveURL(client, id), b, nil, &gophercloud.RequestOpts{ @@ -113,6 +118,8 @@ type InitializeConnectionOptsBuilder interface { } // InitializeConnectionOpts hosts options for InitializeConnection. +// The fields are specific to the storage driver in use and the destination +// attachment. type InitializeConnectionOpts struct { IP string `json:"ip,omitempty"` Host string `json:"host,omitempty"` @@ -131,7 +138,7 @@ func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[stri return map[string]interface{}{"os-initialize_connection": b}, err } -// InitializeConnection initializes iscsi connection. +// InitializeConnection initializes an iSCSI connection by volume ID. func InitializeConnection(client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { b, err := opts.ToVolumeInitializeConnectionMap() if err != nil { @@ -169,7 +176,7 @@ func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string return map[string]interface{}{"os-terminate_connection": b}, err } -// TerminateConnection terminates iscsi connection. +// TerminateConnection terminates an iSCSI connection by volume ID. func TerminateConnection(client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { b, err := opts.ToVolumeTerminateConnectionMap() if err != nil { @@ -188,10 +195,10 @@ type ExtendSizeOptsBuilder interface { ToVolumeExtendSizeMap() (map[string]interface{}, error) } -// ExtendSizeOpts contain options for extending the size of an existing Volume. This object is passed -// to the volumes.ExtendSize function. +// ExtendSizeOpts contains options for extending the size of an existing Volume. +// This object is passed to the volumes.ExtendSize function. type ExtendSizeOpts struct { - // NewSize is the new size of the volume, in GB + // NewSize is the new size of the volume, in GB. NewSize int `json:"new_size" required:"true"` } @@ -225,11 +232,14 @@ type UploadImageOptsBuilder interface { type UploadImageOpts struct { // Container format, may be bare, ofv, ova, etc. ContainerFormat string `json:"container_format,omitempty"` + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. DiskFormat string `json:"disk_format,omitempty"` - // The name of image that will be stored in glance + + // The name of image that will be stored in glance. ImageName string `json:"image_name,omitempty"` - // Force image creation, usable if volume attached to instance + + // Force image creation, usable if volume attached to instance. Force bool `json:"force,omitempty"` } @@ -239,7 +249,7 @@ func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, er return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") } -// UploadImage will upload image base on the values in UploadImageOptsBuilder +// UploadImage will upload image based on the values in UploadImageOptsBuilder. func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { b, err := opts.ToVolumeUploadImageMap() if err != nil { diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index 0e0a7c4063..9815f0c26a 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -7,37 +7,41 @@ import ( "github.com/gophercloud/gophercloud" ) -// AttachResult contains the response body and error from a Get request. +// AttachResult contains the response body and error from an Attach request. type AttachResult struct { gophercloud.ErrResult } -// BeginDetachingResult contains the response body and error from a Get request. +// BeginDetachingResult contains the response body and error from a BeginDetach +// request. type BeginDetachingResult struct { gophercloud.ErrResult } -// DetachResult contains the response body and error from a Get request. +// DetachResult contains the response body and error from a Detach request. type DetachResult struct { gophercloud.ErrResult } -// UploadImageResult contains the response body and error from a UploadImage request. +// UploadImageResult contains the response body and error from an UploadImage +// request. type UploadImageResult struct { gophercloud.Result } -// ReserveResult contains the response body and error from a Get request. +// ReserveResult contains the response body and error from a Reserve request. type ReserveResult struct { gophercloud.ErrResult } -// UnreserveResult contains the response body and error from a Get request. +// UnreserveResult contains the response body and error from an Unreserve +// request. type UnreserveResult struct { gophercloud.ErrResult } -// TerminateConnectionResult contains the response body and error from a Get request. +// TerminateConnectionResult contains the response body and error from a +// TerminateConnection request. type TerminateConnectionResult struct { gophercloud.ErrResult } @@ -48,6 +52,11 @@ type InitializeConnectionResult struct { gophercloud.Result } +// ExtendSizeResult contains the response body and error from an ExtendSize request. +type ExtendSizeResult struct { + gophercloud.ErrResult +} + // Extract will get the connection information out of the // InitializeConnectionResult object. // @@ -61,26 +70,36 @@ func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { return s.ConnectionInfo, err } -// ImageVolumeType contains volume type object obtained from UploadImage action. +// ImageVolumeType contains volume type information obtained from UploadImage +// action. type ImageVolumeType struct { // The ID of a volume type. ID string `json:"id"` + // Human-readable display name for the volume type. Name string `json:"name"` + // Human-readable description for the volume type. Description string `json:"display_description"` + // Flag for public access. IsPublic bool `json:"is_public"` + // Extra specifications for volume type. ExtraSpecs map[string]interface{} `json:"extra_specs"` + // ID of quality of service specs. QosSpecsID string `json:"qos_specs_id"` + // Flag for deletion status of volume type. Deleted bool `json:"deleted"` + // The date when volume type was deleted. DeletedAt time.Time `json:"-"` + // The date when volume type was created. CreatedAt time.Time `json:"-"` + // The date when this volume was last updated. UpdatedAt time.Time `json:"-"` } @@ -106,26 +125,35 @@ func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { return err } -// VolumeImage contains information about volume upload to an image service. +// VolumeImage contains information about volume uploaded to an image service. type VolumeImage struct { // The ID of a volume an image is created from. VolumeID string `json:"id"` + // Container format, may be bare, ofv, ova, etc. ContainerFormat string `json:"container_format"` + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. DiskFormat string `json:"disk_format"` + // Human-readable description for the volume. Description string `json:"display_description"` - // The ID of an image being created. + + // The ID of the created image. ImageID string `json:"image_id"` + // Human-readable display name for the image. ImageName string `json:"image_name"` + // Size of the volume in GB. Size int `json:"size"` + // Current status of the volume. Status string `json:"status"` + // The date when this volume was last updated. UpdatedAt time.Time `json:"-"` + // Volume type object of used volume. VolumeType ImageVolumeType `json:"volume_type"` } @@ -147,7 +175,8 @@ func (r *VolumeImage) UnmarshalJSON(b []byte) error { return err } -// Extract will get an object with info about image being uploaded out of the UploadImageResult object. +// Extract will get an object with info about the uploaded image out of the +// UploadImageResult object. func (r UploadImageResult) Extract() (VolumeImage, error) { var s struct { VolumeImage VolumeImage `json:"os-volume_upload_image"` @@ -155,8 +184,3 @@ func (r UploadImageResult) Extract() (VolumeImage, error) { err := r.ExtractInto(&s) return s.VolumeImage, err } - -// ExtendSizeResult contains the response body and error from an ExtendSize request. -type ExtendSizeResult struct { - gophercloud.ErrResult -} From 10fc3877c4ba46f15495013884083ab9801c1ce3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 10 Aug 2017 21:46:20 -0600 Subject: [PATCH 0019/2296] BlockStorage v2: Adding volume extend acceptance test --- .../blockstorage/extensions/extensions.go | 20 ++++++++++++++ .../extensions/volumeactions_test.go | 27 +++++++++++++++++++ .../extensions/volumeactions/requests.go | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index 2785392062..2856b8b655 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -151,3 +151,23 @@ func DeleteVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume t.Logf("Unreserved volume %s", volume.ID) } + +// ExtendVolumeSize will extend the size of a volume. +func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to extend the size of volume %s", volume.ID) + + extendOpts := volumeactions.ExtendSizeOpts{ + NewSize: 2, + } + + err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() + if err != nil { + return err + } + + if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + return err + } + + return nil +} diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index b202852790..0c88516765 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -97,6 +97,33 @@ func TestVolumeActionsReserveUnreserve(t *testing.T) { defer DeleteVolumeReserve(t, client, volume) } +func TestVolumeActionsExtendSize(t *testing.T) { + blockClient, err := clients.NewBlockStorageV2Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + volume, err := blockstorage.CreateVolume(t, blockClient) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + defer blockstorage.DeleteVolume(t, blockClient, volume) + + tools.PrintResource(t, volume) + + err = ExtendVolumeSize(t, blockClient, volume) + if err != nil { + t.Fatalf("Unable to resize volume: %v", err) + } + + newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + if err != nil { + t.Fatal("Unable to get updated volume information: %v", err) + } + + tools.PrintResource(t, newVolume) +} + // 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 375e262cdc..a3916c77c1 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -249,7 +249,7 @@ func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, er return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") } -// UploadImage will upload image based on the values in UploadImageOptsBuilder. +// UploadImage will upload an image based on the values in UploadImageOptsBuilder. func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { b, err := opts.ToVolumeUploadImageMap() if err != nil { From d14433c61d3ef436a4f3e5e983b22202486cc48d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 11 Aug 2017 23:02:13 -0600 Subject: [PATCH 0020/2296] Docs: Updating openstack package --- openstack/auth_env.go | 20 +++++++-- openstack/client.go | 78 +++++++++++++++++++++++++--------- openstack/doc.go | 14 ++++++ openstack/endpoint_location.go | 32 ++++++++------ 4 files changed, 108 insertions(+), 36 deletions(-) create mode 100644 openstack/doc.go diff --git a/openstack/auth_env.go b/openstack/auth_env.go index f6d2eb194b..95286041d6 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -8,10 +8,22 @@ import ( var nilOptions = gophercloud.AuthOptions{} -// AuthOptionsFromEnv fills out an identity.AuthOptions structure with the settings found on the various OpenStack -// OS_* environment variables. The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, -// OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must -// have settings, or an error will result. OS_TENANT_ID and OS_TENANT_NAME are optional. +/* +AuthOptionsFromEnv fills out an identity.AuthOptions structure with the +settings found on the various OpenStack OS_* environment variables. + +The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, +OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. + +Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, +or an error will result. OS_TENANT_ID and OS_TENANT_NAME are optional. + +To use this function, first set the OS_* environment variables (for example, +by sourcing an `openrc` file), then: + + opts, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(opts) +*/ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { authURL := os.Getenv("OS_AUTH_URL") username := os.Getenv("OS_USERNAME") diff --git a/openstack/client.go b/openstack/client.go index 09120e8faf..e1c699fcbf 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -16,10 +16,20 @@ const ( v30 = "v3.0" ) -// 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. +/* +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) { u, err := url.Parse(endpoint) if err != nil { @@ -45,10 +55,26 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { }, nil } -// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and -// returns a Client instance that's ready to operate. -// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses -// the most recent identity service available to proceed. +/* +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(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +*/ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { client, err := NewClient(options.IdentityEndpoint) if err != nil { @@ -62,7 +88,8 @@ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.Provider return client, nil } -// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint. +// 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: v20, Priority: 20, Suffix: "/v2.0/"}, @@ -179,7 +206,8 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au return nil } -// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service. +// NewIdentityV2 creates a ServiceClient that may be used to interact with the +// v2 identity service. func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { endpoint := client.IdentityBase + "v2.0/" clientType := "identity" @@ -199,7 +227,8 @@ func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp }, nil } -// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service. +// NewIdentityV3 creates a ServiceClient that may be used to access the v3 +// identity service. func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { endpoint := client.IdentityBase + "v3/" clientType := "identity" @@ -232,34 +261,40 @@ func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointO return sc, nil } -// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package. +// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 +// object storage package. func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "object-store") } -// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package. +// NewComputeV2 creates a ServiceClient that may be used with the v2 compute +// package. func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "compute") } -// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package. +// NewNetworkV2 creates a ServiceClient that may be used with the v2 network +// package. func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "network") sc.ResourceBase = sc.Endpoint + "v2.0/" return sc, err } -// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service. +// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 +// block storage service. func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "volume") } -// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service. +// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 +// block storage service. func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "volumev2") } -// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service. +// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the +// v2 shared file system service. func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "sharev2") } @@ -270,7 +305,8 @@ func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) ( return initClientOpts(client, eo, "cdn") } -// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service. +// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 +// orchestration service. func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "orchestration") } @@ -280,14 +316,16 @@ func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (* return initClientOpts(client, eo, "database") } -// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS service. +// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS +// service. func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "dns") sc.ResourceBase = sc.Endpoint + "v2/" return sc, err } -// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 image service. +// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 +// image service. func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "image") sc.ResourceBase = sc.Endpoint + "v2/" diff --git a/openstack/doc.go b/openstack/doc.go new file mode 100644 index 0000000000..cedf1f4d3a --- /dev/null +++ b/openstack/doc.go @@ -0,0 +1,14 @@ +/* +Package openstack contains resources for the individual OpenStack projects +supported in Gophercloud. It also includes functions to authenticate to an +OpenStack cloud and for provisioning various service-level clients. + +Example of Creating a Service Client + + ao, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(ao) + client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +*/ +package openstack diff --git a/openstack/endpoint_location.go b/openstack/endpoint_location.go index ea37f5b271..070ea7cbef 100644 --- a/openstack/endpoint_location.go +++ b/openstack/endpoint_location.go @@ -6,12 +6,16 @@ import ( tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" ) -// V2EndpointURL discovers the endpoint URL for a specific service from a ServiceCatalog acquired -// during the v2 identity service. The specified EndpointOpts are used to identify a unique, -// unambiguous endpoint to return. It's an error both when multiple endpoints match the provided -// criteria and when none do. The minimum that can be specified is a Type, but you will also often -// need to specify a Name and/or a Region depending on what's available on your OpenStack -// deployment. +/* +V2EndpointURL discovers the endpoint URL for a specific service from a +ServiceCatalog acquired during the v2 identity service. + +The specified EndpointOpts are used to identify a unique, unambiguous endpoint +to return. It's an error both when multiple endpoints match the provided +criteria and when none do. The minimum that can be specified is a Type, but you +will also often need to specify a Name and/or a Region depending on what's +available on your OpenStack deployment. +*/ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { // Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided. var endpoints = make([]tokens2.Endpoint, 0, 1) @@ -54,12 +58,16 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt return "", err } -// V3EndpointURL discovers the endpoint URL for a specific service from a Catalog acquired -// during the v3 identity service. The specified EndpointOpts are used to identify a unique, -// unambiguous endpoint to return. It's an error both when multiple endpoints match the provided -// criteria and when none do. The minimum that can be specified is a Type, but you will also often -// need to specify a Name and/or a Region depending on what's available on your OpenStack -// deployment. +/* +V3EndpointURL discovers the endpoint URL for a specific service from a Catalog +acquired during the v3 identity service. + +The specified EndpointOpts are used to identify a unique, unambiguous endpoint +to return. It's an error both when multiple endpoints match the provided +criteria and when none do. The minimum that can be specified is a Type, but you +will also often need to specify a Name and/or a Region depending on what's +available on your OpenStack deployment. +*/ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { // Extract Endpoints from the catalog entries that match the requested Type, Interface, // Name if provided, and Region if provided. From 7a7d65d22123fd857e6626841dadc697697dac52 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 14:29:27 -0600 Subject: [PATCH 0021/2296] Common: Clean up common package This commit cleans up the common package by removing unused files as well as adds more detailed documentation. --- openstack/common/README.md | 3 -- openstack/common/extensions/doc.go | 65 ++++++++++++++++++++------ openstack/common/extensions/errors.go | 1 - openstack/common/extensions/results.go | 4 +- 4 files changed, 53 insertions(+), 20 deletions(-) delete mode 100644 openstack/common/README.md delete mode 100755 openstack/common/extensions/errors.go mode change 100755 => 100644 openstack/common/extensions/results.go diff --git a/openstack/common/README.md b/openstack/common/README.md deleted file mode 100644 index 7b55795d08..0000000000 --- a/openstack/common/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Common Resources - -This directory is for resources that are shared by multiple services. diff --git a/openstack/common/extensions/doc.go b/openstack/common/extensions/doc.go index 4a168f4b2c..5510f26539 100644 --- a/openstack/common/extensions/doc.go +++ b/openstack/common/extensions/doc.go @@ -1,15 +1,52 @@ -// Package extensions provides information and interaction with the different extensions available -// for an OpenStack service. -// -// The purpose of OpenStack API extensions is to: -// -// - Introduce new features in the API without requiring a version change. -// - Introduce vendor-specific niche functionality. -// - Act as a proving ground for experimental functionalities that might be included in a future -// version of the API. -// -// Extensions usually have tags that prevent conflicts with other extensions that define attributes -// or resources with the same names, and with core resources and attributes. -// Because an extension might not be supported by all plug-ins, its availability varies with deployments -// and the specific plug-in. +/* +Package extensions provides information and interaction with the different +extensions available for an OpenStack service. + +The purpose of OpenStack API extensions is to: + +- Introduce new features in the API without requiring a version change. +- Introduce vendor-specific niche functionality. +- Act as a proving ground for experimental functionalities that might be +included in a future version of the API. + +Extensions usually have tags that prevent conflicts with other extensions that +define attributes or resources with the same names, and with core resources and +attributes. Because an extension might not be supported by all plug-ins, its +availability varies with deployments and the specific plug-in. + +The results of this package vary depending on the type of Service Client used. +In the following examples, note how the only difference is the creation of the +Service Client. + +Example of Retrieving Compute Extensions + + ao, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(ao) + computeClient, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) + + allPages, err := extensions.List(computeClient).Allpages() + allExtensions, err := extensions.ExtractExtensions(allPages) + + for _, extension := range allExtensions{ + fmt.Println("%+v\n", extension) + } + + +Example of Retrieving Network Extensions + + ao, err := openstack.AuthOptionsFromEnv() + provider, err := openstack.AuthenticatedClient(ao) + networkClient, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) + + allPages, err := extensions.List(networkClient).Allpages() + allExtensions, err := extensions.ExtractExtensions(allPages) + + for _, extension := range allExtensions{ + fmt.Println("%+v\n", extension) + } +*/ package extensions diff --git a/openstack/common/extensions/errors.go b/openstack/common/extensions/errors.go deleted file mode 100755 index aeec0fa756..0000000000 --- a/openstack/common/extensions/errors.go +++ /dev/null @@ -1 +0,0 @@ -package extensions diff --git a/openstack/common/extensions/results.go b/openstack/common/extensions/results.go old mode 100755 new mode 100644 index d5f8650913..8a26edd1c5 --- a/openstack/common/extensions/results.go +++ b/openstack/common/extensions/results.go @@ -41,8 +41,8 @@ func (r ExtensionPage) IsEmpty() (bool, error) { return len(is) == 0, err } -// ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the -// elements into a slice of Extension structs. +// ExtractExtensions accepts a Page struct, specifically an ExtensionPage +// struct, and extracts the elements into a slice of Extension structs. // In other words, a generic collection is mapped into a relevant slice. func ExtractExtensions(r pagination.Page) ([]Extension, error) { var s struct { From 2735053d41e78e71613a4e0a8b29886cefa08b80 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 14:51:49 -0600 Subject: [PATCH 0022/2296] Compute v2: attachinterface docs --- .../v2/extensions/attachinterfaces/doc.go | 23 +++++++++++++++++-- .../extensions/attachinterfaces/requests.go | 2 +- .../v2/extensions/attachinterfaces/results.go | 13 +++++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go index 2ef30db869..fb431e33e9 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -1,3 +1,22 @@ -// Package attachinterfaces provides the ability to manage network interfaces through -// nova-network +/* +Package attachinterfaces provides the ability to retrieve and manage network +interfaces through Nova. + +Example of Listing a Server's Interfaces + + serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" + allPages, err := attachinterfaces.List(computeClient, serverID).AllPages() + if err != nil { + panic("Unable to retrieve interfaces: %s", err) + } + + allInterfaces, err := attachinterfaces.ExtractInterfaces(allPages) + if err != nil { + panic("Unable to extract interfaces: %s", err) + } + + for _, interface := range allInterfaces { + fmt.Println("%+v\n", interface) + } +*/ package attachinterfaces diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go index 76cc879630..faf2747246 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/requests.go +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -5,7 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// List makes a request against the nova API to list the servers interfaces. +// List makes a request against the nova API to list the server's interfaces. func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { return pagination.NewPager(client, listInterfaceURL(client, serverID), func(r pagination.PageResult) pagination.Page { return InterfacePage{pagination.SinglePageBase(r)} diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/extensions/attachinterfaces/results.go index 776ad8305b..e3987eaca8 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/results.go +++ b/openstack/compute/v2/extensions/attachinterfaces/results.go @@ -10,7 +10,7 @@ type FixedIP struct { IPAddress string `json:"ip_address"` } -// Interface represents a network interface on an instance. +// Interface represents a network interface on a server. type Interface struct { PortState string `json:"port_state"` FixedIPs []FixedIP `json:"fixed_ips"` @@ -19,9 +19,12 @@ type Interface struct { MACAddr string `json:"mac_addr"` } -// InterfacePage abstracts the raw results of making a List() request against the API. -// As OpenStack extensions may freely alter the response bodies of structures returned -// to the client, you may only safely access the data provided through the ExtractInterfaces call. +// InterfacePage abstracts the raw results of making a List() request against +// the API. +// +// As OpenStack extensions may freely alter the response bodies of structures +// returned to the client, you may only safely access the data provided through +// the ExtractInterfaces call. type InterfacePage struct { pagination.SinglePageBase } @@ -33,7 +36,7 @@ func (r InterfacePage) IsEmpty() (bool, error) { } // ExtractInterfaces interprets the results of a single page from a List() call, -// producing a map of interfaces. +// producing a slice of Interface structs. func ExtractInterfaces(r pagination.Page) ([]Interface, error) { var s struct { Interfaces []Interface `json:"interfaceAttachments"` From 4f8096ef12fc3dc8db4d67bbbfe4bbc5f98fc606 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 15:41:40 -0600 Subject: [PATCH 0023/2296] Compute v2: bootfromvolume docs --- .../v2/extensions/bootfromvolume/doc.go | 152 ++++++++++++++++++ .../v2/extensions/bootfromvolume/results.go | 2 + 2 files changed, 154 insertions(+) create mode 100644 openstack/compute/v2/extensions/bootfromvolume/doc.go diff --git a/openstack/compute/v2/extensions/bootfromvolume/doc.go b/openstack/compute/v2/extensions/bootfromvolume/doc.go new file mode 100644 index 0000000000..5437092a70 --- /dev/null +++ b/openstack/compute/v2/extensions/bootfromvolume/doc.go @@ -0,0 +1,152 @@ +/* +Package bootfromvolume extends a server create request with the ability to +specify block device options. This can be used to boot a server from a block +storage volume as well as specify multiple ephemeral disks upon creation. + +It is recommended to refer to the Block Device Mapping documentation to see +all possible ways to configure a server's block devices at creation time: + +https://docs.openstack.org/nova/latest/user/block-device-mapping.html + +Note that this package implements `block_device_mapping_v2`. + +Example of Creating a Server From an Image + +This example will boot a server from an image and use a standard ephemeral +disk as the server's root disk. This is virtually no different than creating +a server without using block device mappings. + + blockDevices := []bootfromvolume.BlockDevice{ + bootfromvolume.BlockDevice{ + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: bootfromvolume.DestinationLocal, + SourceType: bootfromvolume.SourceImage, + UUID: "image-uuid", + }, + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + ImageRef: "image-uuid", + } + + createOpts := bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + BlockDevice: blockDevices, + } + + server, err := bootfromvolume.Create(client, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } + +Example of Creating a Server From a New Volume + +This example will create a block storage volume based on the given Image. The +server will use this volume as its root disk. + + blockDevices := []bootfromvolume.BlockDevice{ + bootfromvolume.BlockDevice{ + DeleteOnTermination: true, + DestinationType: bootfromvolume.DestinationVolume, + SourceType: bootfromvolume.SourceImage, + UUID: "image-uuid", + VolumeSize: 2, + }, + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + } + + createOpts := bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + BlockDevice: blockDevices, + } + + server, err := bootfromvolume.Create(client, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } + +Example of Creating a Server From an Existing Volume + +This example will create a server with an existing volume as its root disk. + + blockDevices := []bootfromvolume.BlockDevice{ + bootfromvolume.BlockDevice{ + DeleteOnTermination: true, + DestinationType: bootfromvolume.DestinationVolume, + SourceType: bootfromvolume.SourceVolume, + UUID: "volume-uuid", + }, + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + } + + createOpts := bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + BlockDevice: blockDevices, + } + + server, err := bootfromvolume.Create(client, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } + +Example of Creating a Server with Multiple Ephemeral Disks + +This example will create a server with multiple ephemeral disks. The first +block device will be based off of an existing Image. Each additional +ephemeral disks must have an index of -1. + + blockDevices := []bootfromvolume.BlockDevice{ + bootfromvolume.BlockDevice{ + BootIndex: 0, + DestinationType: bootfromvolume.DestinationLocal, + DeleteOnTermination: true, + SourceType: bootfromvolume.SourceImage, + UUID: "image-uuid", + VolumeSize: 5, + }, + bootfromvolume.BlockDevice{ + BootIndex: -1, + DestinationType: bootfromvolume.DestinationLocal, + DeleteOnTermination: true, + GuestFormat: "ext4", + SourceType: bootfromvolume.SourceBlank, + VolumeSize: 1, + }, + bootfromvolume.BlockDevice{ + BootIndex: -1, + DestinationType: bootfromvolume.DestinationLocal, + DeleteOnTermination: true, + GuestFormat: "ext4", + SourceType: bootfromvolume.SourceBlank, + VolumeSize: 1, + }, + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + ImageRef: "image-uuid", + } + + createOpts := bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + BlockDevice: blockDevices, + } + + server, err := bootfromvolume.Create(client, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } +*/ +package bootfromvolume diff --git a/openstack/compute/v2/extensions/bootfromvolume/results.go b/openstack/compute/v2/extensions/bootfromvolume/results.go index 3211fb1f3d..ba1eafabcd 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/results.go +++ b/openstack/compute/v2/extensions/bootfromvolume/results.go @@ -5,6 +5,8 @@ import ( ) // CreateResult temporarily contains the response from a Create call. +// It embeds the standard servers.CreateResults type and so can be used the +// same way as a standard server request result. type CreateResult struct { os.CreateResult } From c3c5d6e8ea5ca976aeee361b07e126c73fd6c4a8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 15:06:57 -0600 Subject: [PATCH 0024/2296] Compute v2: Clean up availabilityzones --- .../openstack/compute/v2/servers_test.go | 4 ++- .../v2/extensions/availabilityzones/doc.go | 26 +++++++++++++++++++ .../extensions/availabilityzones/results.go | 10 +++---- .../v2/servers/testing/requests_test.go | 4 +-- 4 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 openstack/compute/v2/extensions/availabilityzones/doc.go diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index d545a43252..14516ff137 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -109,7 +109,7 @@ func TestServersCreateDestroy(t *testing.T) { func TestServersCreateDestroyWithExtensions(t *testing.T) { var extendedServer struct { servers.Server - availabilityzones.ServerExt + availabilityzones.ServerAvailabilityZoneExt } client, err := clients.NewComputeV2Client() @@ -128,6 +128,8 @@ func TestServersCreateDestroyWithExtensions(t *testing.T) { t.Errorf("Unable to retrieve server: %v", err) } tools.PrintResource(t, extendedServer) + + t.Logf("Availability Zone: %s\n", extendedServer.AvailabilityZone) } func TestServersWithoutImageRef(t *testing.T) { diff --git a/openstack/compute/v2/extensions/availabilityzones/doc.go b/openstack/compute/v2/extensions/availabilityzones/doc.go new file mode 100644 index 0000000000..80464ba399 --- /dev/null +++ b/openstack/compute/v2/extensions/availabilityzones/doc.go @@ -0,0 +1,26 @@ +/* +Package availabilityzones provides the ability to extend a server result with +availability zone information. Example: + + type ServerWithAZ struct { + servers.Server + availabilityzones.ServerAvailabilityZoneExt + } + + var allServers []ServerWithAZ + + allPages, err := servers.List(client, nil).AllPages() + if err != nil { + panic("Unable to retrieve servers: %s", err) + } + + err = servers.ExtractServersInto(allPages, &allServers) + if err != nil { + panic("Unable to extract servers: %s", err) + } + + for _, server := range allServers { + fmt.Println(server.AvailabilityZone) + } +*/ +package availabilityzones diff --git a/openstack/compute/v2/extensions/availabilityzones/results.go b/openstack/compute/v2/extensions/availabilityzones/results.go index 96a6a50b3d..ae87404137 100644 --- a/openstack/compute/v2/extensions/availabilityzones/results.go +++ b/openstack/compute/v2/extensions/availabilityzones/results.go @@ -1,12 +1,8 @@ package availabilityzones -// ServerExt is an extension to the base Server object -type ServerExt struct { +// ServerAvailabilityZoneExt is an extension to the base Server result which +// includes the Availability Zone information. +type ServerAvailabilityZoneExt struct { // AvailabilityZone is the availabilty zone the server is in. AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` } - -// UnmarshalJSON to override default -func (r *ServerExt) UnmarshalJSON(b []byte) error { - return nil -} diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 05712f7b0f..c810f8f17b 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -64,7 +64,7 @@ func TestListAllServersWithExtensions(t *testing.T) { type ServerWithExt struct { servers.Server - availabilityzones.ServerExt + availabilityzones.ServerAvailabilityZoneExt } allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages() @@ -217,7 +217,7 @@ func TestGetServerWithExtensions(t *testing.T) { var s struct { servers.Server - availabilityzones.ServerExt + availabilityzones.ServerAvailabilityZoneExt } err := servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(&s) From f2a6a02b0cc3b360dbace488c6e715c2152fd14b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 18:49:15 -0600 Subject: [PATCH 0025/2296] Compute v2: defsecrules docs --- .../compute/v2/extensions/defsecrules/doc.go | 54 +++++++++++++++++++ .../v2/extensions/defsecrules/requests.go | 13 +++-- .../v2/extensions/defsecrules/results.go | 6 +-- 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/openstack/compute/v2/extensions/defsecrules/doc.go b/openstack/compute/v2/extensions/defsecrules/doc.go index 2571a1a5a7..da41271a8e 100644 --- a/openstack/compute/v2/extensions/defsecrules/doc.go +++ b/openstack/compute/v2/extensions/defsecrules/doc.go @@ -1 +1,55 @@ +/* +Package defsecrules enables management of default security group rules. + +Default security group rules are rules that are managed in the "default" +security group. + +This is only applicable in environments running nova-network. This package will +not work if the OpenStack environment is running the OpenStack Networking +(Neutron) service. + +Example of Listing Default Security Group Rules + + allPages, err := defsecrules.List(computeClient).AllPages() + if err != nil { + panic("Unable to list default security group rules: %s", err) + } + + allDefaultRules, err := defsecrules.ExtractDefaultRules(allPages) + if err != nil { + panic("Unable to extract default security group rules: %s", err) + } + + for _, df := allDefaultRules { + fmt.Println("%+v\n", df) + } + +Example of Retrieving a Default Security Group Rule + + rule, err := defsecrules.Get(computeClient, "rule-id").Extract() + if err != nil { + panic("Unable to retrieve default security group rule: %s", err + } + +Example of Creating a Default Security Group Rule + + createOpts := defsecrules.CreateOpts{ + IPProtocol: "TCP", + FromPort: 80, + ToPort: 80, + CIDR: "10.10.12.0/24", + } + + rule, err := defsecrules.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create default security group rule: %s", err) + } + +Example of Deleting a Default Security Group Rule + + err := defsecrules.Delete(computeClient, "rule-id").ExtractErr() + if err != nil { + panic("Unable to delete default security group rule: %s", err) + } +*/ package defsecrules diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/extensions/defsecrules/requests.go index 184fdc919c..1888cb94b7 100644 --- a/openstack/compute/v2/extensions/defsecrules/requests.go +++ b/openstack/compute/v2/extensions/defsecrules/requests.go @@ -16,15 +16,20 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // CreateOpts represents the configuration for adding a new default rule. type CreateOpts struct { - // The lower bound of the port range that will be opened.s + // The lower bound of the port range that will be opened. FromPort int `json:"from_port"` + // The upper bound of the port range that will be opened. ToPort int `json:"to_port"` + // The protocol type that will be allowed, e.g. TCP. IPProtocol string `json:"ip_protocol" required:"true"` + // ONLY required if FromGroupID is blank. This represents the IP range that - // will be the source of network traffic to your security group. Use - // 0.0.0.0/0 to allow all IP addresses. + // will be the source of network traffic to your security group. + // + // Use 0.0.0.0/0 to allow all IPv4 addresses. + // Use ::/0 to allow all IPv6 addresses. CIDR string `json:"cidr,omitempty"` } @@ -63,7 +68,7 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// Delete will permanently delete a default rule from the project. +// Delete will permanently delete a rule the project's default security group. func Delete(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { _, r.Err = client.Delete(resourceURL(client, id), nil) return diff --git a/openstack/compute/v2/extensions/defsecrules/results.go b/openstack/compute/v2/extensions/defsecrules/results.go index f990c9991d..c6c484264b 100644 --- a/openstack/compute/v2/extensions/defsecrules/results.go +++ b/openstack/compute/v2/extensions/defsecrules/results.go @@ -8,8 +8,8 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// DefaultRule represents a default rule - which is identical to a -// normal security rule. +// DefaultRule represents a rule belonging to the "default" security group. +// It is identical to an openstack/compute/v2/extensions/secgroups.Rule. type DefaultRule secgroups.Rule func (r *DefaultRule) UnmarshalJSON(b []byte) error { @@ -57,7 +57,7 @@ type GetResult struct { commonResult } -// Extract will extract a DefaultRule struct from most responses. +// Extract will extract a DefaultRule struct from a Create or Get response. func (r commonResult) Extract() (*DefaultRule, error) { var s struct { DefaultRule DefaultRule `json:"security_group_default_rule"` From dbc2b5083d79b5b2ef171bcede6164eba747daa6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 19:49:45 -0600 Subject: [PATCH 0026/2296] Compute v2: floatingips docs --- .../compute/v2/extensions/floatingips/doc.go | 69 ++++++++++++++++++- .../v2/extensions/floatingips/requests.go | 38 +++++----- .../v2/extensions/floatingips/results.go | 36 +++++----- 3 files changed, 104 insertions(+), 39 deletions(-) diff --git a/openstack/compute/v2/extensions/floatingips/doc.go b/openstack/compute/v2/extensions/floatingips/doc.go index 6682fa6290..9c9a8ee24e 100644 --- a/openstack/compute/v2/extensions/floatingips/doc.go +++ b/openstack/compute/v2/extensions/floatingips/doc.go @@ -1,3 +1,68 @@ -// Package floatingips provides the ability to manage floating ips through -// nova-network +/* +Package floatingips provides the ability to manage floating ips through the +Nova API. + +This API has been deprecated and will be removed from a future release of the +Nova API service. + +For environements that support this extension, this package can be used +regardless of if either Neutron or nova-network is used as the cloud's network +service. + +Example to List Floating IPs + + allPages, err := floatingips.List(computeClient).AllPages() + if err != nil { + panic("Unable to retrieve Floating IPs: %s", err) + } + + allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages) + if err != nil { + panic("Unable to extract Floating IPs: %s", err) + } + + for _, fip := range allFloatingIPs { + fmt.Println("%+v\n", fip) + } + +Example to Create a Floating IP + + createOpts := floatingips.CreateOpts{ + Pool: "nova", + } + + fip, err := floatingips.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create Floating IP: %s", err) + } + +Example to Delete a Floating IP + + err := floatingips.Delete(computeClient, "floatingip-id").ExtractErr() + if err != nil { + panic("Unable to delete Floating IP: %s", err) + } + +Example to Associate a Floating IP With a Server + + associateOpts := floatingips.AssociateOpts{ + FloatingIP: "10.10.10.2", + } + + err := floatingips.AssociateInstance(computeClient, "server-id", associateOpts).ExtractErr() + if err != nil { + panic("Unable to associate Floating IP: %s", err) + } + +Example to Disassociate a Floating IP From a Server + + disassociateOpts := floatingips.DisassociateOpts{ + FloatingIP: "10.10.10.2", + } + + err := floatingips.DisassociateInstance(computeClient, "server-id", disassociateOpts).ExtractErr() + if err != nil { + panic("Unable to disassocaite Floating IP: %s", err) + } +*/ package floatingips diff --git a/openstack/compute/v2/extensions/floatingips/requests.go b/openstack/compute/v2/extensions/floatingips/requests.go index b36aeba59c..a922639dec 100644 --- a/openstack/compute/v2/extensions/floatingips/requests.go +++ b/openstack/compute/v2/extensions/floatingips/requests.go @@ -12,15 +12,15 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the -// CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToFloatingIPCreateMap() (map[string]interface{}, error) } -// CreateOpts specifies a Floating IP allocation request +// CreateOpts specifies a Floating IP allocation request. type CreateOpts struct { - // Pool is the pool of floating IPs to allocate one from + // Pool is the pool of Floating IPs to allocate one from. Pool string `json:"pool" required:"true"` } @@ -29,7 +29,7 @@ func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } -// Create requests the creation of a new floating IP +// Create requests the creation of a new Floating IP. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFloatingIPCreateMap() if err != nil { @@ -42,29 +42,30 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Get returns data about a previously created FloatingIP. +// Get returns data about a previously created Floating IP. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } -// Delete requests the deletion of a previous allocated FloatingIP. +// Delete requests the deletion of a previous allocated Floating IP. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } -// AssociateOptsBuilder is the interface types must satfisfy to be used as -// Associate options +// AssociateOptsBuilder allows extensions to add additional parameters to the +// Associate request. type AssociateOptsBuilder interface { ToFloatingIPAssociateMap() (map[string]interface{}, error) } -// AssociateOpts specifies the required information to associate a floating IP with an instance +// AssociateOpts specifies the required information to associate a Floating IP with an instance type AssociateOpts struct { - // FloatingIP is the floating IP to associate with an instance + // FloatingIP is the Floating IP to associate with an instance. FloatingIP string `json:"address" required:"true"` - // FixedIP is an optional fixed IP address of the server + + // FixedIP is an optional fixed IP address of the server. FixedIP string `json:"fixed_address,omitempty"` } @@ -73,7 +74,7 @@ func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, er return gophercloud.BuildRequestBody(opts, "addFloatingIp") } -// AssociateInstance pairs an allocated floating IP with an instance. +// AssociateInstance pairs an allocated Floating IP with a server. func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) { b, err := opts.ToFloatingIPAssociateMap() if err != nil { @@ -84,23 +85,24 @@ func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts return } -// DisassociateOptsBuilder is the interface types must satfisfy to be used as -// Disassociate options +// DisassociateOptsBuilder allows extensions to add additional parameters to +// the Disassociate request. type DisassociateOptsBuilder interface { ToFloatingIPDisassociateMap() (map[string]interface{}, error) } -// DisassociateOpts specifies the required information to disassociate a floating IP with an instance +// DisassociateOpts specifies the required information to disassociate a +// Floating IP with a server. type DisassociateOpts struct { FloatingIP string `json:"address" required:"true"` } -// ToFloatingIPDisassociateMap constructs a request body from AssociateOpts. +// ToFloatingIPDisassociateMap constructs a request body from DisassociateOpts. func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "removeFloatingIp") } -// DisassociateInstance decouples an allocated floating IP from an instance +// DisassociateInstance decouples an allocated Floating IP from an instance func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) { b, err := opts.ToFloatingIPDisassociateMap() if err != nil { diff --git a/openstack/compute/v2/extensions/floatingips/results.go b/openstack/compute/v2/extensions/floatingips/results.go index 2f5b33844e..da4e9da0e6 100644 --- a/openstack/compute/v2/extensions/floatingips/results.go +++ b/openstack/compute/v2/extensions/floatingips/results.go @@ -8,21 +8,21 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// A FloatingIP is an IP that can be associated with an instance +// A FloatingIP is an IP that can be associated with a server. type FloatingIP struct { // ID is a unique ID of the Floating IP ID string `json:"-"` - // FixedIP is the IP of the instance related to the Floating IP + // FixedIP is a specific IP on the server to pair the Floating IP with. FixedIP string `json:"fixed_ip,omitempty"` - // InstanceID is the ID of the instance that is using the Floating IP + // InstanceID is the ID of the server that is using the Floating IP. InstanceID string `json:"instance_id"` - // IP is the actual Floating IP + // IP is the actual Floating IP. IP string `json:"ip"` - // Pool is the pool of floating IPs that this floating IP belongs to + // Pool is the pool of Floating IPs that this Floating IP belongs to. Pool string `json:"pool"` } @@ -49,8 +49,7 @@ func (r *FloatingIP) UnmarshalJSON(b []byte) error { return err } -// FloatingIPPage stores a single, only page of FloatingIPs -// results from a List call. +// FloatingIPPage stores a single page of FloatingIPs from a List call. type FloatingIPPage struct { pagination.SinglePageBase } @@ -61,8 +60,7 @@ func (page FloatingIPPage) IsEmpty() (bool, error) { return len(va) == 0, err } -// ExtractFloatingIPs interprets a page of results as a slice of -// FloatingIPs. +// ExtractFloatingIPs interprets a page of results as a slice of FloatingIPs. func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { var s struct { FloatingIPs []FloatingIP `json:"floating_ips"` @@ -86,32 +84,32 @@ func (r FloatingIPResult) Extract() (*FloatingIP, error) { return s.FloatingIP, err } -// CreateResult is the response from a Create operation. Call its Extract method to interpret it -// as a FloatingIP. +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a FloatingIP. type CreateResult struct { FloatingIPResult } -// GetResult is the response from a Get operation. Call its Extract method to interpret it -// as a FloatingIP. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a FloatingIP. type GetResult struct { FloatingIPResult } -// DeleteResult is the response from a Delete operation. Call its Extract method to determine if -// the call succeeded or failed. +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } -// AssociateResult is the response from a Delete operation. Call its Extract method to determine if -// the call succeeded or failed. +// AssociateResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. type AssociateResult struct { gophercloud.ErrResult } -// DisassociateResult is the response from a Delete operation. Call its Extract method to determine if -// the call succeeded or failed. +// DisassociateResult is the response from a Delete operation. Call its +// ExtractErr method to determine if the call succeeded or failed. type DisassociateResult struct { gophercloud.ErrResult } From 99e47a2be537f77d79f547622d64957249fd9a7b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 20:07:09 -0600 Subject: [PATCH 0027/2296] Compute v2: hypervisors docs --- .../compute/v2/extensions/hypervisors/doc.go | 22 ++++- .../v2/extensions/hypervisors/results.go | 84 +++++++++++++------ 2 files changed, 77 insertions(+), 29 deletions(-) diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index 026f3ddf75..001e0a5dc5 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -1,3 +1,21 @@ -// Package hypervisors gives information and control of the os-hypervisors -// portion of the compute API +/* +Package hypervisors returns details about the hypervisors in the OpenStack +cloud. + +Example of Retrieving Details of All Hypervisors + + allPages, err := hypervisors.List(computeClient).AllPages() + if err != nil { + panic("Unable to retrieve hypervisors: %s", err) + } + + allHypervisors, err := hypervisors.ExtractHypervisors(allPages) + if err != nil { + panic("Unable to extract hypervisors: %s", err) + } + + for _, hypervisor := range allHypervisors { + fmt.Println("%+v\n", hypervisor) + } +*/ package hypervisors diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index 844aa65c50..d4e87de083 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -7,12 +7,14 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// Topology represents a CPU Topology. type Topology struct { Sockets int `json:"sockets"` Cores int `json:"cores"` Threads int `json:"threads"` } +// CPUInfo represents CPU information of the hypervisor. type CPUInfo struct { Vendor string `json:"vendor"` Arch string `json:"arch"` @@ -21,59 +23,82 @@ type CPUInfo struct { Topology Topology `json:"topology"` } +// Service represents a Compute service running on the hypervisor. type Service struct { Host string `json:"host"` ID int `json:"id"` DisabledReason string `json:"disabled_reason"` } +// Hypervisor represents a hypervisor in the OpenStack cloud. type Hypervisor struct { - // A structure that contains cpu information like arch, model, vendor, features and topology + // A structure that contains cpu information like arch, model, vendor, + // features and topology. CPUInfo CPUInfo `json:"-"` - // The current_workload is the number of tasks the hypervisor is responsible for. - // This will be equal or greater than the number of active VMs on the system - // (it can be greater when VMs are being deleted and the hypervisor is still cleaning up). + + // The current_workload is the number of tasks the hypervisor is responsible + // for. This will be equal or greater than the number of active VMs on the + // system (it can be greater when VMs are being deleted and the hypervisor is + // still cleaning up). CurrentWorkload int `json:"current_workload"` - // Status of the hypervisor, either "enabled" or "disabled" + + // Status of the hypervisor, either "enabled" or "disabled". Status string `json:"status"` - // State of the hypervisor, either "up" or "down" + + // State of the hypervisor, either "up" or "down". State string `json:"state"` - // Actual free disk on this hypervisor in GB + + // DiskAvailableLeast is the actual free disk on this hypervisor, + // measured in GB. DiskAvailableLeast int `json:"disk_available_least"` - // The hypervisor's IP address + + // HostIP is the hypervisor's IP address. HostIP string `json:"host_ip"` - // The free disk remaining on this hypervisor in GB + + // FreeDiskGB is the free disk remaining on the hypervisor, measured in GB. FreeDiskGB int `json:"-"` - // The free RAM in this hypervisor in MB + + // FreeRAMMB is the free RAM in the hypervisor, measured in MB. FreeRamMB int `json:"free_ram_mb"` - // The hypervisor host name + + // HypervisorHostname is the hostname of the hypervisor. HypervisorHostname string `json:"hypervisor_hostname"` - // The hypervisor type + + // HypervisorType is the type of hypervisor. HypervisorType string `json:"hypervisor_type"` - // The hypervisor version + + // HypervisorVersion is the version of the hypervisor. HypervisorVersion int `json:"-"` - // Unique ID of the hypervisor + + // ID is the unique ID of the hypervisor. ID int `json:"id"` - // The disk in this hypervisor in GB + + // LocalGB is the disk space in the hypervisor, measured in GB. LocalGB int `json:"-"` - // The disk used in this hypervisor in GB + + // LocalGBUsed is the used disk space of the hypervisor, measured in GB. LocalGBUsed int `json:"local_gb_used"` - // The memory of this hypervisor in MB + + // MemoryMB is the total memory of the hypervisor, measured in MB. MemoryMB int `json:"memory_mb"` - // The memory used in this hypervisor in MB + + // MemoryMBUsed is the used memory of the hypervisor, measured in MB. MemoryMBUsed int `json:"memory_mb_used"` - // The number of running vms on this hypervisor + + // RunningVMs is the The number of running vms on the hypervisor. RunningVMs int `json:"running_vms"` - // The hypervisor service object + + // Service is the service this hypervisor represents. Service Service `json:"service"` - // The number of vcpu in this hypervisor + + // VCPUs is the total number of vcpus on the hypervisor. VCPUs int `json:"vcpus"` - // The number of vcpu used in this hypervisor + + // VCPUsUsed is the number of used vcpus on the hypervisor. VCPUsUsed int `json:"vcpus_used"` } func (r *Hypervisor) UnmarshalJSON(b []byte) error { - type tmp Hypervisor var s struct { tmp @@ -90,9 +115,9 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { *r = Hypervisor(s.tmp) - // Newer versions pass the CPU into around as the correct types, this just needs - // converting and copying into place. Older versions pass CPU info around as a string - // and can simply be unmarshalled by the json parser + // Newer versions return the CPU info as the correct type. + // Older versions return the CPU info as a string and need to be + // unmarshalled by the json parser. var tmpb []byte switch t := s.CPUInfo.(type) { @@ -112,7 +137,8 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { return err } - // These fields may be passed in in scientific notation + // These fields may be returned as a scientific notation, so they need + // converted to int. switch t := s.HypervisorVersion.(type) { case int: r.HypervisorVersion = t @@ -143,15 +169,19 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { return nil } +// HypervisorPage represents a single page of all Hypervisors from a List +// request. type HypervisorPage struct { pagination.SinglePageBase } +// IsEmpty determines whether or not a HypervisorPage is empty. func (page HypervisorPage) IsEmpty() (bool, error) { va, err := ExtractHypervisors(page) return len(va) == 0, err } +// ExtractHypervisors interprets a page of results as a slice of Hypervisors. func ExtractHypervisors(p pagination.Page) ([]Hypervisor, error) { var h struct { Hypervisors []Hypervisor `json:"hypervisors"` From 52bc547ff5b5f7ed4fb36f7b8d0bb6b34ed14f23 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 21:02:05 -0600 Subject: [PATCH 0028/2296] Compute v2: keypairs docs --- .../compute/v2/extensions/keypairs/doc.go | 72 ++++++++++++++++++- .../v2/extensions/keypairs/requests.go | 20 +++--- .../compute/v2/extensions/keypairs/results.go | 39 +++++----- 3 files changed, 103 insertions(+), 28 deletions(-) diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go index 856f41bacc..2314777584 100644 --- a/openstack/compute/v2/extensions/keypairs/doc.go +++ b/openstack/compute/v2/extensions/keypairs/doc.go @@ -1,3 +1,71 @@ -// Package keypairs provides information and interaction with the Keypairs -// extension for the OpenStack Compute service. +/* +Package keypairs provides the ability to manage key pairs as well as create +servers with a specified key pair. + +Example to List Key Pairs + + allPages, err := keypairs.List(computeClient).AllPages() + if err != nil { + panic("Unable to list key pairs: %s", err) + } + + allKeyPairs, err := keypairs.ExtractKeyPairs(allPages) + if err != nil { + panic("Unable to extract key pairs: %s", err) + } + + for _, kp := range allKeyPairs { + fmt.Println("%+v\n", kp) + } + +Example to Create a Key Pair + + createOpts := keypairs.CreateOpts{ + Name: "keypair-name", + } + + keypair, err := keypairs.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create key pair: %s", err) + } + + fmt.Println("%+v", keypair) + +Example to Import a Key Pair + + createOpts := keypairs.CreateOpts{ + Name: "keypair-name", + PublicKey: "public-key", + } + + keypair, err := keypairs.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create key pair: %s", err) + } + +Example to Delete a Key Pair + + err := keypairs.Delete(computeClient, "keypair-name").ExtractErr() + if err != nil { + panic("Unable to delete key pair: %s", err) + } + +Example to Create a Server With a Key Pair + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := keypairs.CreateOpts{ + CreateOptsBuilder: serverCreateOpts, + KeyName: "keypair-name", + } + + server, err := servers.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Could not create server: %s", err) + } +*/ package keypairs diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go index adf1e5596f..4e5e499e3a 100644 --- a/openstack/compute/v2/extensions/keypairs/requests.go +++ b/openstack/compute/v2/extensions/keypairs/requests.go @@ -9,11 +9,12 @@ import ( // CreateOptsExt adds a KeyPair option to the base CreateOpts. type CreateOptsExt struct { servers.CreateOptsBuilder + + // KeyName is the name of the key pair. KeyName string `json:"key_name,omitempty"` } -// ToServerCreateMap adds the key_name and, optionally, key_data options to -// the base server creation options. +// ToServerCreateMap adds the key_name to the base server creation options. func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToServerCreateMap() if err != nil { @@ -37,18 +38,19 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the -// CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToKeyPairCreateMap() (map[string]interface{}, error) } -// CreateOpts specifies keypair creation or import parameters. +// CreateOpts specifies KeyPair creation or import parameters. type CreateOpts struct { // Name is a friendly name to refer to this KeyPair in other services. Name string `json:"name" required:"true"` - // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. If provided, this key - // will be imported and no new key will be created. + + // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. + // If provided, this key will be imported and no new key will be created. PublicKey string `json:"public_key,omitempty"` } @@ -57,8 +59,8 @@ func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "keypair") } -// Create requests the creation of a new keypair on the server, or to import a pre-existing -// keypair. +// Create requests the creation of a new KeyPair on the server, or to import a +// pre-existing keypair. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToKeyPairCreateMap() if err != nil { diff --git a/openstack/compute/v2/extensions/keypairs/results.go b/openstack/compute/v2/extensions/keypairs/results.go index 4c785a24cd..2d71034b10 100644 --- a/openstack/compute/v2/extensions/keypairs/results.go +++ b/openstack/compute/v2/extensions/keypairs/results.go @@ -5,29 +5,33 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// KeyPair is an SSH key known to the OpenStack Cloud that is available to be injected into -// servers. +// KeyPair is an SSH key known to the OpenStack Cloud that is available to be +// injected into servers. type KeyPair struct { - // Name is used to refer to this keypair from other services within this region. + // Name is used to refer to this keypair from other services within this + // region. Name string `json:"name"` - // Fingerprint is a short sequence of bytes that can be used to authenticate or validate a longer - // public key. + // Fingerprint is a short sequence of bytes that can be used to authenticate + // or validate a longer public key. Fingerprint string `json:"fingerprint"` - // PublicKey is the public key from this pair, in OpenSSH format. "ssh-rsa AAAAB3Nz..." + // PublicKey is the public key from this pair, in OpenSSH format. + // "ssh-rsa AAAAB3Nz..." PublicKey string `json:"public_key"` // PrivateKey is the private key from this pair, in PEM format. - // "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." It is only present if this keypair was just - // returned from a Create call + // "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." + // It is only present if this KeyPair was just returned from a Create call. PrivateKey string `json:"private_key"` - // UserID is the user who owns this keypair. + // UserID is the user who owns this KeyPair. UserID string `json:"user_id"` } -// KeyPairPage stores a single, only page of KeyPair results from a List call. +// KeyPairPage stores a single page of all KeyPair results from a List call. +// Use the ExtractKeyPairs function to convert the results to a slice of +// KeyPairs. type KeyPairPage struct { pagination.SinglePageBase } @@ -58,7 +62,8 @@ type keyPairResult struct { gophercloud.Result } -// Extract is a method that attempts to interpret any KeyPair resource response as a KeyPair struct. +// Extract is a method that attempts to interpret any KeyPair resource response +// as a KeyPair struct. func (r keyPairResult) Extract() (*KeyPair, error) { var s struct { KeyPair *KeyPair `json:"keypair"` @@ -67,20 +72,20 @@ func (r keyPairResult) Extract() (*KeyPair, error) { return s.KeyPair, err } -// CreateResult is the response from a Create operation. Call its Extract method to interpret it -// as a KeyPair. +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a KeyPair. type CreateResult struct { keyPairResult } -// GetResult is the response from a Get operation. Call its Extract method to interpret it -// as a KeyPair. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a KeyPair. type GetResult struct { keyPairResult } -// DeleteResult is the response from a Delete operation. Call its Extract method to determine if -// the call succeeded or failed. +// 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 } From 65607610880c4d64add7e9e57cf2675d7eac1f10 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 22:45:03 -0600 Subject: [PATCH 0029/2296] Compute v2: limits docs --- openstack/compute/v2/extensions/limits/doc.go | 17 +++++++++++++++++ .../compute/v2/extensions/limits/requests.go | 2 +- .../compute/v2/extensions/limits/results.go | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 openstack/compute/v2/extensions/limits/doc.go diff --git a/openstack/compute/v2/extensions/limits/doc.go b/openstack/compute/v2/extensions/limits/doc.go new file mode 100644 index 0000000000..cea9688a16 --- /dev/null +++ b/openstack/compute/v2/extensions/limits/doc.go @@ -0,0 +1,17 @@ +/* +Package limits shows rate and limit information for a tenant/project. + +Example to Retrieve Limits for a Tenant + + getOpts := limits.GetOpts{ + TenantID: "tenant-id", + } + + limits, err := limits.Get(computeClient, getOpts).Extract() + if err != nil { + panic("Unable to retrieve limits: %s", err) + } + + fmt.Println("%+v\n", limits) +*/ +package limits diff --git a/openstack/compute/v2/extensions/limits/requests.go b/openstack/compute/v2/extensions/limits/requests.go index 70324b8356..57573d38c1 100644 --- a/openstack/compute/v2/extensions/limits/requests.go +++ b/openstack/compute/v2/extensions/limits/requests.go @@ -12,7 +12,7 @@ type GetOptsBuilder interface { // GetOpts enables retrieving limits by a specific tenant. type GetOpts struct { - // The tenant ID to retrieve limits for + // The tenant ID to retrieve limits for. TenantID string `q:"tenant_id"` } diff --git a/openstack/compute/v2/extensions/limits/results.go b/openstack/compute/v2/extensions/limits/results.go index b58f1ddd09..8d0564bd2f 100644 --- a/openstack/compute/v2/extensions/limits/results.go +++ b/openstack/compute/v2/extensions/limits/results.go @@ -83,7 +83,7 @@ func (r GetResult) Extract() (*Limits, error) { return s.Limits, err } -// GetResult is the response from a Get operation. Call its ExtractAbsolute +// GetResult is the response from a Get operation. Call its Extract // method to interpret it as an Absolute. type GetResult struct { gophercloud.Result From 57b7d75f8c8d52efb342df86d8037a3698847c39 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 22:51:57 -0600 Subject: [PATCH 0030/2296] Compute v2: networks docs --- .../compute/v2/extensions/networks/doc.go | 24 ++++++++++++++++++- .../compute/v2/extensions/networks/results.go | 11 ++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/openstack/compute/v2/extensions/networks/doc.go b/openstack/compute/v2/extensions/networks/doc.go index fafe4a04d7..b73048c2f5 100644 --- a/openstack/compute/v2/extensions/networks/doc.go +++ b/openstack/compute/v2/extensions/networks/doc.go @@ -1,2 +1,24 @@ -// Package network provides the ability to manage nova-networks +/* +Package networks provides the ability to create and manage networks in cloud +environments using nova-network. + +This package can also be used to retrieve network details of Neutron-based +networks. + +Example to List Networks + + allPages, err := networks.List(computeClient).AllPages() + if err != nil { + panic("Unable to retrieve networks: %s", err) + } + + allNetworks, err := networks.ExtractNetworks(allPages) + if err != nil { + panic("Unable to extract networks: %s", err) + } + + for _, network := range allNetworks { + fmt.Println("%+v\n", network) + } +*/ package networks diff --git a/openstack/compute/v2/extensions/networks/results.go b/openstack/compute/v2/extensions/networks/results.go index cbcce31987..c36ce678cc 100644 --- a/openstack/compute/v2/extensions/networks/results.go +++ b/openstack/compute/v2/extensions/networks/results.go @@ -5,7 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// A Network represents a nova-network that an instance communicates on +// A Network represents a network in an OpenStack cloud. type Network struct { // The Bridge that VIFs on this network are connected to Bridge string `json:"bridge"` @@ -92,8 +92,7 @@ type Network struct { VPNPublicPort int `json:"vpn_public_port"` } -// NetworkPage stores a single, only page of Networks -// results from a List call. +// NetworkPage stores a single page of all Network results from a List call. type NetworkPage struct { pagination.SinglePageBase } @@ -104,7 +103,7 @@ func (page NetworkPage) IsEmpty() (bool, error) { return len(va) == 0, err } -// ExtractNetworks interprets a page of results as a slice of Networks +// ExtractNetworks interprets a page of results as a slice of Networks. func ExtractNetworks(r pagination.Page) ([]Network, error) { var s struct { Networks []Network `json:"networks"` @@ -127,8 +126,8 @@ func (r NetworkResult) Extract() (*Network, error) { return s.Network, err } -// GetResult is the response from a Get operation. Call its Extract method to interpret it -// as a Network. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a Network. type GetResult struct { NetworkResult } From aa77a5de5636c38d90a02f9ca04c76060fcad3fe Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 22:54:28 -0600 Subject: [PATCH 0031/2296] Compute v2: pauseunpause docs --- .../compute/v2/extensions/pauseunpause/doc.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/openstack/compute/v2/extensions/pauseunpause/doc.go b/openstack/compute/v2/extensions/pauseunpause/doc.go index 7a45a24c33..5dfe02c09a 100644 --- a/openstack/compute/v2/extensions/pauseunpause/doc.go +++ b/openstack/compute/v2/extensions/pauseunpause/doc.go @@ -1,5 +1,18 @@ /* -Package pauseunpause provides functionality to pause and unpause servers that have -been provisioned by the OpenStack Compute service. +Package pauseunpause provides functionality to pause and unpause servers that +have been provisioned by the OpenStack Compute service. + +Example to Pause and Unpause a Server + + serverID := "32c8baf7-1cdb-4cc2-bc31-c3a55b89f56b" + err := pauseunpause.Pause(computeClient, serverID).ExtractErr() + if err != nil { + panic("Unable to pause server: %s", err) + } + + err = pauseunpause.Unpause(computeClient, serverID).ExtractErr() + if err != nil { + panic("Unable to unpause server: %s", err) + } */ package pauseunpause From 88e9712a999a4b5ca6f591b5d58f3755e7b6cbc7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 23:17:37 -0600 Subject: [PATCH 0032/2296] Compute v2: quotasets docs --- .../compute/v2/extensions/quotasets/doc.go | 28 +++++++- .../v2/extensions/quotasets/requests.go | 62 +++++++++++------- .../v2/extensions/quotasets/results.go | 65 ++++++++++++------- 3 files changed, 107 insertions(+), 48 deletions(-) diff --git a/openstack/compute/v2/extensions/quotasets/doc.go b/openstack/compute/v2/extensions/quotasets/doc.go index 721024ec58..c4a7a6c800 100644 --- a/openstack/compute/v2/extensions/quotasets/doc.go +++ b/openstack/compute/v2/extensions/quotasets/doc.go @@ -1,3 +1,27 @@ -// Package quotasets provides information and interaction with QuotaSet -// extension for the OpenStack Compute service. +/* +Package quotasets enables retrieving and managing Compute quotas. + +Example to Get a Quota Set + + quotaset, err := quotasets.Get(computeClient, "tenant-id").Extract() + if err != nil { + panic("Unable to retrieve quotaset: %s", err) + } + + fmt.Println("%+v\n", quotaset) + +Example to Update a Quota Set + + updateOpts := quotasets.UpdateOpts{ + FixedIPs: gophercloud.IntToPointer(100), + Cores: gophercloud.IntToPointer(64), + } + + quotaset, err := quotasets.Update(computeClient, "tenant-id", updateOpts).Extract() + if err != nil { + panic("Unable to update quotaset: %s", err) + } + + fmt.Println("%+v\n", quotaset) +*/ package quotasets diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go index bb9cb22181..d303535b3b 100644 --- a/openstack/compute/v2/extensions/quotasets/requests.go +++ b/openstack/compute/v2/extensions/quotasets/requests.go @@ -11,7 +11,7 @@ func Get(client *gophercloud.ServiceClient, tenantID string) GetResult { return res } -//Updates the quotas for the given tenantID and returns the new quota-set +// Updates the quotas for the given tenantID and returns the new QuotaSet. func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (res UpdateResult) { reqBody, err := opts.ToComputeQuotaUpdateMap() if err != nil { @@ -23,54 +23,72 @@ func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsB return res } -//Resets the uotas for the given tenant to their default values +// Resets the quotas for the given tenant to their default values. func Delete(client *gophercloud.ServiceClient, tenantID string) (res DeleteResult) { _, res.Err = client.Delete(deleteURL(client, tenantID), nil) 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 +// 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 { - //FixedIps is number of fixed ips alloted this quota_set + // FixedIps is number of fixed ips alloted this quota_set. FixedIps *int `json:"fixed_ips,omitempty"` - // FloatingIps is number of floating ips alloted this quota_set + + // FloatingIps is number of floating ips alloted this quota_set. FloatingIps *int `json:"floating_ips,omitempty"` - // InjectedFileContentBytes is content bytes allowed for each injected file + + // InjectedFileContentBytes is content bytes allowed for each injected file. InjectedFileContentBytes *int `json:"injected_file_content_bytes,omitempty"` - // InjectedFilePathBytes is allowed bytes for each injected file path + + // InjectedFilePathBytes is allowed bytes for each injected file path. InjectedFilePathBytes *int `json:"injected_file_path_bytes,omitempty"` - // InjectedFiles is injected files allowed for each project + + // InjectedFiles is injected files allowed for each project. InjectedFiles *int `json:"injected_files,omitempty"` - // KeyPairs is number of ssh keypairs + + // KeyPairs is number of ssh keypairs. KeyPairs *int `json:"key_pairs,omitempty"` - // MetadataItems is number of metadata items allowed for each instance + + // MetadataItems is number of metadata items allowed for each instance. MetadataItems *int `json:"metadata_items,omitempty"` - // Ram is megabytes allowed for each instance + + // Ram is megabytes allowed for each instance. Ram *int `json:"ram,omitempty"` - // SecurityGroupRules is rules allowed for each security group + + // SecurityGroupRules is rules allowed for each security group. SecurityGroupRules *int `json:"security_group_rules,omitempty"` - // SecurityGroups security groups allowed for each project + + // SecurityGroups security groups allowed for each project. SecurityGroups *int `json:"security_groups,omitempty"` - // Cores is number of instance cores allowed for each project + + // Cores is number of instance cores allowed for each project. Cores *int `json:"cores,omitempty"` - // Instances is number of instances allowed for each project + + // Instances is number of instances allowed for each project. Instances *int `json:"instances,omitempty"` - // Number of ServerGroups allowed for the project + + // Number of ServerGroups allowed for the project. ServerGroups *int `json:"server_groups,omitempty"` - // Max number of Members for each ServerGroup + + // Max number of Members for each ServerGroup. ServerGroupMembers *int `json:"server_group_members,omitempty"` - //Users can force the update even if the quota has already been used and the reserved quota exceeds the new quota. + + // Force will update the quotaset even if the quota has already been used + // and the reserved quota exceeds the new quota. Force bool `json:"force,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) + // Extra specific name to prevent collisions with interfaces for other quotas + // (e.g. neutron) ToComputeQuotaUpdateMap() (map[string]interface{}, error) } +// ToComputeQuotaUpdateMap builds the update options into a serializable +// format. func (opts UpdateOpts) ToComputeQuotaUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "quota_set") } diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go index 44e6b06028..361a2dd1a7 100644 --- a/openstack/compute/v2/extensions/quotasets/results.go +++ b/openstack/compute/v2/extensions/quotasets/results.go @@ -5,41 +5,57 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// QuotaSet is a set of operational limits that allow for control of compute usage. +// QuotaSet is a set of operational limits that allow for control of compute +// usage. type QuotaSet struct { - //ID is tenant associated with this quota_set + // ID is tenant associated with this QuotaSet. ID string `json:"id"` - //FixedIps is number of fixed ips alloted this quota_set + + // FixedIps is number of fixed ips alloted this QuotaSet. FixedIps int `json:"fixed_ips"` - // FloatingIps is number of floating ips alloted this quota_set + + // FloatingIps is number of floating ips alloted this QuotaSet. FloatingIps int `json:"floating_ips"` - // InjectedFileContentBytes is content bytes allowed for each injected file + + // InjectedFileContentBytes is the allowed bytes for each injected file. InjectedFileContentBytes int `json:"injected_file_content_bytes"` - // InjectedFilePathBytes is allowed bytes for each injected file path + + // InjectedFilePathBytes is allowed bytes for each injected file path. InjectedFilePathBytes int `json:"injected_file_path_bytes"` - // InjectedFiles is injected files allowed for each project + + // InjectedFiles is the number of injected files allowed for each project. InjectedFiles int `json:"injected_files"` - // KeyPairs is number of ssh keypairs + + // KeyPairs is number of ssh keypairs. KeyPairs int `json:"key_pairs"` - // MetadataItems is number of metadata items allowed for each instance + + // MetadataItems is number of metadata items allowed for each instance. MetadataItems int `json:"metadata_items"` - // Ram is megabytes allowed for each instance + + // Ram is megabytes allowed for each instance. Ram int `json:"ram"` - // SecurityGroupRules is rules allowed for each security group + + // SecurityGroupRules is number of security group rules allowed for each + // security group. SecurityGroupRules int `json:"security_group_rules"` - // SecurityGroups security groups allowed for each project + + // SecurityGroups is the number of security groups allowed for each project. SecurityGroups int `json:"security_groups"` - // Cores is number of instance cores allowed for each project + + // Cores is number of instance cores allowed for each project. Cores int `json:"cores"` - // Instances is number of instances allowed for each project + + // Instances is number of instances allowed for each project. Instances int `json:"instances"` - // Number of ServerGroups allowed for the project + + // ServerGroups is the number of ServerGroups allowed for the project. ServerGroups int `json:"server_groups"` - // Max number of Members for each ServerGroup + + // ServerGroupMembers is the number of members for each ServerGroup. ServerGroupMembers int `json:"server_group_members"` } -// QuotaSetPage stores a single, only page of QuotaSet results from a List call. +// QuotaSetPage stores a single page of all QuotaSet results from a List call. type QuotaSetPage struct { pagination.SinglePageBase } @@ -63,7 +79,8 @@ type quotaResult struct { gophercloud.Result } -// Extract is a method that attempts to interpret any QuotaSet resource response as a QuotaSet struct. +// 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"` @@ -72,20 +89,20 @@ func (r quotaResult) Extract() (*QuotaSet, error) { return s.QuotaSet, err } -// GetResult is the response from a Get operation. Call its Extract method to interpret it -// as a QuotaSet. +// 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. +// UpdateResult is the response from a Update operation. Call its Extract method +// to interpret it as a QuotaSet. type UpdateResult struct { quotaResult } -// DeleteResult is the response from a Delete operation. Call its Extract method to interpret it -// as a QuotaSet. +// DeleteResult is the response from a Delete operation. Call its Extract method +// to interpret it as a QuotaSet. type DeleteResult struct { quotaResult } From b38d5d9e82cb4d528ef96c0a18df908a66578731 Mon Sep 17 00:00:00 2001 From: Pavel Pospisil Date: Tue, 15 Aug 2017 03:44:37 +0200 Subject: [PATCH 0033/2296] Shared Filesystems: Grant Access (#346) * Shared File Systems: Grant Access By default shares are inaccessible because they do not have set any access permissions. That's why the Grant Access request is added. * Added unit tests for POST Grant Access request. * Fixes for the first round of code reviews. * Fixes for the second round of code reviews. * Fixes for the third round of code reviews. * Fixes for the fourth round of code reviews. * Fixes for the fifth round of code reviews. --- .../sharedfilesystems/v2/shares/requests.go | 42 +++++++++++++++++-- .../sharedfilesystems/v2/shares/results.go | 33 +++++++++++++++ .../v2/shares/testing/fixtures.go | 34 +++++++++++++++ .../v2/shares/testing/request_test.go | 29 +++++++++++++ openstack/sharedfilesystems/v2/shares/urls.go | 4 ++ 5 files changed, 139 insertions(+), 3 deletions(-) diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index d2543a7ec9..6eb31932f9 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -1,8 +1,6 @@ package shares -import ( - "github.com/gophercloud/gophercloud" -) +import "github.com/gophercloud/gophercloud" // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. @@ -85,3 +83,41 @@ func GetExportLocations(client *gophercloud.ServiceClient, id string) (r GetExpo _, r.Err = client.Get(getExportLocationsURL(client, id), &r.Body, nil) return } + +// GrantAccessOptsBuilder allows extensions to add additional parameters to the +// GrantAccess request. +type GrantAccessOptsBuilder interface { + ToGrantAccessMap() (map[string]interface{}, error) +} + +// GrantAccessOpts contains the options for creation of an GrantAccess request. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Share Actions, Grant Access documentation +type GrantAccessOpts struct { + // The access rule type that can be "ip", "cert" or "user". + AccessType string `json:"access_type"` + // The value that defines the access that can be a valid format of IP, cert or user. + AccessTo string `json:"access_to"` + // The access level to the share is either "rw" or "ro". + AccessLevel string `json:"access_level"` +} + +// ToGrantAccessMap assembles a request body based on the contents of a +// GrantAccessOpts. +func (opts GrantAccessOpts) ToGrantAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "allow_access") +} + +// GrantAccess will grant access to a Share based on the values in GrantAccessOpts. To extract +// the GrantAccess object from the response, call the Extract method on the GrantAccessResult. +func GrantAccess(client *gophercloud.ServiceClient, id string, opts GrantAccessOptsBuilder) (r GrantAccessResult) { + b, err := opts.ToGrantAccessMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(grantAccessURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index db7827f7ef..b76f86afb0 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -144,3 +144,36 @@ func (r GetExportLocationsResult) Extract() ([]ExportLocation, error) { err := r.ExtractInto(&s) return s.ExportLocations, err } + +// AccessRight contains all information associated with an OpenStack share +// Grant Access Response +type AccessRight struct { + // The UUID of the share to which you are granted or denied access. + ShareID string `json:"share_id"` + // The access rule type that can be "ip", "cert" or "user". + AccessType string `json:"access_type,omitempty"` + // The value that defines the access that can be a valid format of IP, cert or user. + AccessTo string `json:"access_to,omitempty"` + // The access credential of the entity granted share access. + AccessKey string `json:"access_key,omitempty"` + // The access level to the share is either "rw" or "ro". + AccessLevel string `json:"access_level,omitempty"` + // The state of the access rule + State string `json:"state,omitempty"` + // The access rule ID. + ID string `json:"id"` +} + +// Extract will get the GrantAccess object from the commonResult +func (r GrantAccessResult) Extract() (*AccessRight, error) { + var s struct { + AccessRight *AccessRight `json:"access"` + } + err := r.ExtractInto(&s) + return s.AccessRight, err +} + +// GrantAccessResult contains the result body and error from an GrantAccess request. +type GrantAccessResult struct { + gophercloud.Result +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 368a0be259..8ab2742df9 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -163,3 +163,37 @@ func MockGetExportLocationsResponse(t *testing.T) { fmt.Fprintf(w, getExportLocationsResponse) }) } + +var grantAccessRequest = `{ + "allow_access": { + "access_type": "ip", + "access_to": "0.0.0.0/0", + "access_level": "rw" + } + }` + +var grantAccessResponse = `{ + "access": { + "share_id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", + "access_type": "ip", + "access_to": "0.0.0.0/0", + "access_key": "", + "access_level": "rw", + "state": "new", + "id": "a2f226a5-cee8-430b-8a03-78a59bd84ee8" + } +}` + +// MockGrantAccessResponse creates a mock grant access response +func MockGrantAccessResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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, grantAccessRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, grantAccessResponse) + }) +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index f2253769fc..443dd1fa48 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -106,3 +106,32 @@ func TestGetExportLocationsSuccess(t *testing.T) { }, }) } + +func TestGrantAcessSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGrantAccessResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 + c.Microversion = "2.7" + + var grantAccessReq shares.GrantAccessOpts + grantAccessReq.AccessType = "ip" + grantAccessReq.AccessTo = "0.0.0.0/0" + grantAccessReq.AccessLevel = "rw" + + s, err := shares.GrantAccess(c, shareID, grantAccessReq).Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, s, &shares.AccessRight{ + ShareID: "011d21e2-fbc3-4e4a-9993-9ea223f73264", + AccessType: "ip", + AccessTo: "0.0.0.0/0", + AccessKey: "", + AccessLevel: "rw", + State: "new", + ID: "a2f226a5-cee8-430b-8a03-78a59bd84ee8", + }) +} diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index e0832c2bdd..38e1aa431e 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -17,3 +17,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func getExportLocationsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "export_locations") } + +func grantAccessURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} From 35aa79014336fed42ed343b2aae8594f2cb95d3c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 19:27:29 -0600 Subject: [PATCH 0034/2296] Compute v2: startstop docs --- openstack/compute/v2/extensions/startstop/doc.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openstack/compute/v2/extensions/startstop/doc.go b/openstack/compute/v2/extensions/startstop/doc.go index d2729f8743..824190f118 100644 --- a/openstack/compute/v2/extensions/startstop/doc.go +++ b/openstack/compute/v2/extensions/startstop/doc.go @@ -1,5 +1,19 @@ /* Package startstop provides functionality to start and stop servers that have been provisioned by the OpenStack Compute service. + +Example to Stop and Start a Server + + serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" + + err := startstop.Stop(computeClient, serverID).ExtractErr() + if err != nil { + panic("Unable to stop server: %s", err) + } + + err := startstop.Start(computeClient, serverID).ExtractErr() + if err != nil { + panic("Unable to start server: %s", err) + } */ package startstop From 4b9ea30ba887c6cf493ec39a25d9ded8f63e6e4b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 19:29:30 -0600 Subject: [PATCH 0035/2296] Compute v2: suspendresume docs --- .../compute/v2/extensions/suspendresume/doc.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openstack/compute/v2/extensions/suspendresume/doc.go b/openstack/compute/v2/extensions/suspendresume/doc.go index ad37c49915..b337108b38 100644 --- a/openstack/compute/v2/extensions/suspendresume/doc.go +++ b/openstack/compute/v2/extensions/suspendresume/doc.go @@ -1,5 +1,19 @@ /* Package suspendresume provides functionality to suspend and resume servers that have been provisioned by the OpenStack Compute service. + +Example to Suspend and Resume a Server + + serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" + + err := suspendresume.Suspend(computeClient, serverID).ExtractErr() + if err != nil { + panic("Unable to suspend server: %s", err) + } + + err := suspendresume.Resume(computeClient, serverID).ExtractErr() + if err != nil { + panic("Unable to resume server: %s", err) + } */ package suspendresume From 1296df77de09a57d8bd1fe0a9d920d47c5545fae Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 19:25:09 -0600 Subject: [PATCH 0036/2296] Compute v2: schedulerhints docs --- .../v2/extensions/schedulerhints/doc.go | 77 ++++++++++++++++++- .../v2/extensions/schedulerhints/requests.go | 14 +++- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/openstack/compute/v2/extensions/schedulerhints/doc.go b/openstack/compute/v2/extensions/schedulerhints/doc.go index 0bd45661b5..a26119ef77 100644 --- a/openstack/compute/v2/extensions/schedulerhints/doc.go +++ b/openstack/compute/v2/extensions/schedulerhints/doc.go @@ -1,3 +1,76 @@ -// Package schedulerhints enables instances to provide the OpenStack scheduler -// hints about where they should be placed in the cloud. +/* +Package schedulerhints extends the server create request with the ability to +specify additional parameters which determine where the server will be +created in the OpenStack cloud. + +Example to Add a Server to a Server Group + + schedulerHints := schedulerhints.SchedulerHints{ + Group: "servergroup-uuid", + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := schedulerhints.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + SchedulerHints: schedulerHints, + } + + server, err := servers.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } + +Example to Place Server B on a Different Host than Server A + + schedulerHints := schedulerhints.SchedulerHints{ + DifferentHost: []string{ + "server-a-uuid", + } + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_b", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := schedulerhints.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + SchedulerHints: schedulerHints, + } + + server, err := servers.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } + +Example to Place Server B on the Same Host as Server A + + schedulerHints := schedulerhints.SchedulerHints{ + SameHost: []string{ + "server-a-uuid", + } + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_b", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := schedulerhints.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + SchedulerHints: schedulerHints, + } + + server, err := servers.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } +*/ package schedulerhints diff --git a/openstack/compute/v2/extensions/schedulerhints/requests.go b/openstack/compute/v2/extensions/schedulerhints/requests.go index a34263efcb..3fabeddef3 100644 --- a/openstack/compute/v2/extensions/schedulerhints/requests.go +++ b/openstack/compute/v2/extensions/schedulerhints/requests.go @@ -10,23 +10,29 @@ import ( ) // SchedulerHints represents a set of scheduling hints that are passed to the -// OpenStack scheduler +// OpenStack scheduler. type SchedulerHints struct { // Group specifies a Server Group to place the instance in. Group string + // DifferentHost will place the instance on a compute node that does not // host the given instances. DifferentHost []string + // SameHost will place the instance on a compute node that hosts the given // instances. SameHost []string + // Query is a conditional statement that results in compute nodes able to // host the instance. Query []interface{} + // TargetCell specifies a cell name where the instance will be placed. TargetCell string `json:"target_cell,omitempty"` + // BuildNearHostIP specifies a subnet of compute nodes to host the instance. BuildNearHostIP string + // AdditionalProperies are arbitrary key/values that are not validated by nova. AdditionalProperties map[string]interface{} } @@ -79,8 +85,9 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa sh["same_host"] = opts.SameHost } - /* Query can be something simple like: - [">=", "$free_ram_mb", 1024] + /* + Query can be something simple like: + [">=", "$free_ram_mb", 1024] Or more complex like: ['and', @@ -130,6 +137,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa // CreateOptsExt adds a SchedulerHints option to the base CreateOpts. type CreateOptsExt struct { servers.CreateOptsBuilder + // SchedulerHints provides a set of hints to the scheduler. SchedulerHints CreateOptsBuilder } From 2247dbcc71eeaa0fa6f45f6dec45582c4b5fe704 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 19:42:52 -0600 Subject: [PATCH 0037/2296] Compute v2: tenantnetwork docs --- .../v2/extensions/tenantnetworks/doc.go | 26 ++++++++++++++++++- .../v2/extensions/tenantnetworks/requests.go | 2 +- .../v2/extensions/tenantnetworks/results.go | 15 +++++------ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/openstack/compute/v2/extensions/tenantnetworks/doc.go b/openstack/compute/v2/extensions/tenantnetworks/doc.go index 65c46ff507..34ceb47151 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/doc.go +++ b/openstack/compute/v2/extensions/tenantnetworks/doc.go @@ -1,2 +1,26 @@ -// Package tenantnetworks provides the ability for tenants to see information about the networks they have access to +/* +Package tenantnetworks provides the ability for tenants to see information +about the networks they have access to. + +This is a deprecated API and will be removed from the Nova API service in a +future version. + +This API works in both Neutron and nova-network based OpenStack clouds. + +Example to List Networks Available to a Tenant + + allPages, err := tenantnetworks.List(computeClient).AllPages() + if err != nil { + panic("Unable to retrieve networks: %s", err) + } + + allNetworks, err := tenantnetworks.ExtractNetworks(allPages) + if err != nil { + panic("Unable to extract networks: %s", err) + } + + for _, network := range allNetworks { + fmt.Println("%+v\n", network) + } +*/ package tenantnetworks diff --git a/openstack/compute/v2/extensions/tenantnetworks/requests.go b/openstack/compute/v2/extensions/tenantnetworks/requests.go index 82836d4b8b..00899056fd 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/requests.go +++ b/openstack/compute/v2/extensions/tenantnetworks/requests.go @@ -5,7 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// List returns a Pager that allows you to iterate over a collection of Network. +// List returns a Pager that allows you to iterate over a collection of Networks. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return NetworkPage{pagination.SinglePageBase(r)} diff --git a/openstack/compute/v2/extensions/tenantnetworks/results.go b/openstack/compute/v2/extensions/tenantnetworks/results.go index 88cbc80ec2..bda77d5f50 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/results.go +++ b/openstack/compute/v2/extensions/tenantnetworks/results.go @@ -5,7 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// A Network represents a nova-network that an instance communicates on +// A Network represents a network that a server communicates on. type Network struct { // CIDR is the IPv4 subnet. CIDR string `json:"cidr"` @@ -17,8 +17,7 @@ type Network struct { Name string `json:"label"` } -// NetworkPage stores a single, only page of Networks -// results from a List call. +// NetworkPage stores a single page of all Networks results from a List call. type NetworkPage struct { pagination.SinglePageBase } @@ -29,7 +28,7 @@ func (page NetworkPage) IsEmpty() (bool, error) { return len(va) == 0, err } -// ExtractNetworks interprets a page of results as a slice of Networks +// ExtractNetworks interprets a page of results as a slice of Network. func ExtractNetworks(r pagination.Page) ([]Network, error) { var s struct { Networks []Network `json:"networks"` @@ -42,8 +41,8 @@ type NetworkResult struct { gophercloud.Result } -// Extract is a method that attempts to interpret any Network resource -// response as a Network struct. +// Extract is a method that attempts to interpret any Network resource response +// as a Network struct. func (r NetworkResult) Extract() (*Network, error) { var s struct { Network *Network `json:"network"` @@ -52,8 +51,8 @@ func (r NetworkResult) Extract() (*Network, error) { return s.Network, err } -// GetResult is the response from a Get operation. Call its Extract method to interpret it -// as a Network. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a Network. type GetResult struct { NetworkResult } From ea9f3749f291ae61b3a117dd82728c3438a4e749 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 19:52:15 -0600 Subject: [PATCH 0038/2296] Compute v2: volumeattach docs --- .../compute/v2/extensions/volumeattach/doc.go | 31 +++++++++++++++++-- .../v2/extensions/volumeattach/requests.go | 17 +++++----- .../v2/extensions/volumeattach/results.go | 29 ++++++++--------- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/openstack/compute/v2/extensions/volumeattach/doc.go b/openstack/compute/v2/extensions/volumeattach/doc.go index 22f68d80e5..bc11f8c5ac 100644 --- a/openstack/compute/v2/extensions/volumeattach/doc.go +++ b/openstack/compute/v2/extensions/volumeattach/doc.go @@ -1,3 +1,30 @@ -// Package volumeattach provides the ability to attach and detach volumes -// to instances +/* +Package volumeattach provides the ability to attach and detach volumes +from servers. + +Example to Attach a Volume + + serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d" + volumeID := "87463836-f0e2-4029-abf6-20c8892a3103" + + createOpts := volumeattach.CreateOpts{ + Device: "/dev/vdc", + VolumeID: volumeID, + } + + result, err := volumeattach.Create(computeClient, serverID, createOpts).Extract() + if err != nil { + panic("Unable to attach volume: %s", err) + } + +Example to Detach a Volume + + serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d" + attachmentID := "ed081613-1c9b-4231-aa5e-ebfd4d87f983" + + err := volumeattach.Delete(computeClient, serverID, attachmentID).ExtractErr() + if err != nil { + panic("Unable to detach volume: %s", err) + } +*/ package volumeattach diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go index ee4d62ddb0..6a262c212e 100644 --- a/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/openstack/compute/v2/extensions/volumeattach/requests.go @@ -5,24 +5,26 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// List returns a Pager that allows you to iterate over a collection of VolumeAttachments. +// List returns a Pager that allows you to iterate over a collection of +// VolumeAttachments. func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { return pagination.NewPager(client, listURL(client, serverID), func(r pagination.PageResult) pagination.Page { return VolumeAttachmentPage{pagination.SinglePageBase(r)} }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the -// CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { ToVolumeAttachmentCreateMap() (map[string]interface{}, error) } // CreateOpts specifies volume attachment creation or import parameters. type CreateOpts struct { - // Device is the device that the volume will attach to the instance as. Omit for "auto" + // Device is the device that the volume will attach to the instance as. + // Omit for "auto". Device string `json:"device,omitempty"` - // VolumeID is the ID of the volume to attach to the instance + + // VolumeID is the ID of the volume to attach to the instance. VolumeID string `json:"volumeId" required:"true"` } @@ -31,7 +33,7 @@ func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, er return gophercloud.BuildRequestBody(opts, "volumeAttachment") } -// Create requests the creation of a new volume attachment on the server +// Create requests the creation of a new volume attachment on the server. func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeAttachmentCreateMap() if err != nil { @@ -50,7 +52,8 @@ func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) (r Ge return } -// Delete requests the deletion of a previous stored VolumeAttachment from the server. +// Delete requests the deletion of a previous stored VolumeAttachment from +// the server. func Delete(client *gophercloud.ServiceClient, serverID, attachmentID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, serverID, attachmentID), nil) return diff --git a/openstack/compute/v2/extensions/volumeattach/results.go b/openstack/compute/v2/extensions/volumeattach/results.go index 53faf5d3af..56d5034729 100644 --- a/openstack/compute/v2/extensions/volumeattach/results.go +++ b/openstack/compute/v2/extensions/volumeattach/results.go @@ -5,35 +5,36 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// VolumeAttachment controls the attachment of a volume to an instance. +// VolumeAttachment contains attachment information between a volume +// and server. type VolumeAttachment struct { - // ID is a unique id of the attachment + // ID is a unique id of the attachment. ID string `json:"id"` - // Device is what device the volume is attached as + // Device is what device the volume is attached as. Device string `json:"device"` - // VolumeID is the ID of the attached volume + // VolumeID is the ID of the attached volume. VolumeID string `json:"volumeId"` - // ServerID is the ID of the instance that has the volume attached + // ServerID is the ID of the instance that has the volume attached. ServerID string `json:"serverId"` } -// VolumeAttachmentPage stores a single, only page of VolumeAttachments +// VolumeAttachmentPage stores a single page all of VolumeAttachment // results from a List call. type VolumeAttachmentPage struct { pagination.SinglePageBase } -// IsEmpty determines whether or not a VolumeAttachmentsPage is empty. +// IsEmpty determines whether or not a VolumeAttachmentPage is empty. func (page VolumeAttachmentPage) IsEmpty() (bool, error) { va, err := ExtractVolumeAttachments(page) return len(va) == 0, err } // ExtractVolumeAttachments interprets a page of results as a slice of -// VolumeAttachments. +// VolumeAttachment. func ExtractVolumeAttachments(r pagination.Page) ([]VolumeAttachment, error) { var s struct { VolumeAttachments []VolumeAttachment `json:"volumeAttachments"` @@ -57,20 +58,20 @@ func (r VolumeAttachmentResult) Extract() (*VolumeAttachment, error) { return s.VolumeAttachment, err } -// CreateResult is the response from a Create operation. Call its Extract method to interpret it -// as a VolumeAttachment. +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a VolumeAttachment. type CreateResult struct { VolumeAttachmentResult } -// GetResult is the response from a Get operation. Call its Extract method to interpret it -// as a VolumeAttachment. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a VolumeAttachment. type GetResult struct { VolumeAttachmentResult } -// DeleteResult is the response from a Delete operation. Call its Extract method to determine if -// the call succeeded or failed. +// 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 } From 8945094d663a9a8e7a6716d1a15e04f6568932da Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 20:14:39 -0600 Subject: [PATCH 0039/2296] Compute v2: flavors docs --- openstack/compute/v2/flavors/doc.go | 50 +++++++++-- openstack/compute/v2/flavors/requests.go | 103 ++++++++++++++--------- openstack/compute/v2/flavors/results.go | 42 +++++---- 3 files changed, 135 insertions(+), 60 deletions(-) diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 5822e1bcf6..92b81b3e70 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -1,7 +1,45 @@ -// Package flavors provides information and interaction with the flavor API -// resource in the OpenStack Compute service. -// -// A flavor is an available hardware configuration for a server. Each flavor -// has a unique combination of disk space, memory capacity and priority for CPU -// time. +/* +Package flavors provides information and interaction with the flavor API +in the OpenStack Compute service. + +A flavor is an available hardware configuration for a server. Each flavor +has a unique combination of disk space, memory capacity and priority for CPU +time. + +Example to List Flavors + + listOpts := flavors.ListOpts{ + AccessType: flavors.PublicAccess, + } + + allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages() + if err != nil { + panic("Unable to list flavors: %s", err) + } + + allFlavors, err := flavors.ExtractFlavors(allPages) + if err != nil { + panic("Unable to extract flavors: %s", err) + } + + for _, flavor := range allFlavors { + fmt.Println("%+v\n", flavor) + } + +Example to Create a Flavor + + createOpts := flavors.CreateOpts{ + ID: "1", + Name: "m1.tiny", + Disk: gophercloud.IntToPointer(1), + RAM: 512, + VCPUs: 1, + RxTxFactor: 1.0, + } + + flavor, err := flavors.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create flavor: %s", err) + } +*/ package flavors diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 03d7e8724c..9578e8f87b 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -11,33 +11,45 @@ type ListOptsBuilder interface { ToFlavorListQuery() (string, error) } -// 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: -// -// PublicAccess (the default): Returns public flavors and private flavors associated with that project. -// PrivateAccess (admin only): Returns private flavors, across all projects. -// AllAccess (admin only): Returns public and private flavors across all projects. -// -// The AccessType arguement is optional, and if it is not supplied, OpenStack returns the PublicAccess -// flavors. +/* + 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. +*/ type AccessType string const ( - PublicAccess AccessType = "true" + // PublicAccess returns public flavors and private flavors associated with + // that project. + PublicAccess AccessType = "true" + + // PrivateAccess (admin only) returns private flavors, across all projects. PrivateAccess AccessType = "false" - AllAccess AccessType = "None" + + // AllAccess (admin only) returns public and private flavors across all + // projects. + AllAccess AccessType = "None" ) -// ListOpts helps control 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. +/* + 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. +*/ type ListOpts struct { - // ChangesSince, if provided, instructs List to return only those things which have changed since the timestamp provided. + // ChangesSince, if provided, instructs List to return only those things which + // have changed since the timestamp provided. ChangesSince string `q:"changes-since"` - // MinDisk and MinRAM, if provided, elides flavors which do not meet your criteria. + // MinDisk and MinRAM, if provided, elides flavors which do not meet your + // criteria. MinDisk int `q:"minDisk"` MinRAM int `q:"minRam"` @@ -45,11 +57,12 @@ type ListOpts struct { // Marker instructs List where to start listing from. Marker string `q:"marker"` - // Limit instructs List to refrain from sending excessively large lists of flavors. + // Limit instructs List to refrain from sending excessively large lists of + // flavors. Limit int `q:"limit"` - // AccessType, if provided, instructs List which set of flavors to return. If IsPublic not provided, - // flavors for the current project are returned. + // AccessType, if provided, instructs List which set of flavors to return. + // If IsPublic not provided, flavors for the current project are returned. AccessType AccessType `q:"is_public"` } @@ -60,8 +73,8 @@ func (opts ListOpts) ToFlavorListQuery() (string, error) { } // ListDetail instructs OpenStack to provide a list of flavors. -// You may provide criteria by which List curtails its results for easier processing. -// See ListOpts for more details. +// You may provide criteria by which List curtails its results for easier +// processing. func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { @@ -80,31 +93,42 @@ type CreateOptsBuilder interface { ToFlavorCreateMap() (map[string]interface{}, error) } -// CreateOpts is passed to Create to create a flavor -// Source: -// https://github.com/openstack/nova/blob/stable/newton/nova/api/openstack/compute/schemas/flavor_manage.py#L20 +// CreateOpts specifies parameters used for creating a flavor. type CreateOpts struct { + // Name is the name of the flavor. Name string `json:"name" required:"true"` - // memory size, in MBs - RAM int `json:"ram" required:"true"` + + // RAM is the memory of the flavor, measured in MB. + RAM int `json:"ram" required:"true"` + + // VCPUs is the number of vcpus for the flavor. VCPUs int `json:"vcpus" required:"true"` - // disk size, in GBs - Disk *int `json:"disk" required:"true"` - ID string `json:"id,omitempty"` - // non-zero, positive - Swap *int `json:"swap,omitempty"` + + // Disk the amount of root disk space, measured in GB. + Disk *int `json:"disk" required:"true"` + + // ID is a unique ID for the flavor. + ID string `json:"id,omitempty"` + + // Swap is the amount of swap space for the flavor, measured in MB. + Swap *int `json:"swap,omitempty"` + + // RxTxFactor alters the network bandwidth of a flavor. RxTxFactor float64 `json:"rxtx_factor,omitempty"` - IsPublic *bool `json:"os-flavor-access:is_public,omitempty"` - // ephemeral disk size, in GBs, non-zero, positive + + // IsPublic flags a flavor as being available to all projects or not. + IsPublic *bool `json:"os-flavor-access:is_public,omitempty"` + + // Ephemeral is the amount of ephemeral disk space, measured in GB. Ephemeral *int `json:"OS-FLV-EXT-DATA:ephemeral,omitempty"` } -// ToFlavorCreateMap satisfies the CreateOptsBuilder interface +// ToFlavorCreateMap constructs a request body from CreateOpts. func (opts *CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "flavor") } -// Create a flavor +// Create requests the creation of a new flavor. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFlavorCreateMap() if err != nil { @@ -117,14 +141,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Get instructs OpenStack to provide details on a single flavor, identified by its ID. -// Use ExtractFlavor to convert its result into a Flavor. +// Get retrieves details of a single flavor. Use ExtractFlavor to convert its +// result into a Flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } -// IDFromName is a convienience function that returns a flavor's ID given its name. +// IDFromName is a convienience function that returns a flavor's ID given its +// name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 121abbb8d8..b4f7ffad01 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -16,12 +16,14 @@ type CreateResult struct { commonResult } -// GetResult temporarily holds the response from a Get call. +// GetResult is the response of a Get operations. Call its Extract method to +// interpret it as a Flavor. type GetResult struct { commonResult } -// Extract provides access to the individual Flavor returned by the Get and Create functions. +// Extract provides access to the individual Flavor returned by the Get and +// Create functions. func (r commonResult) Extract() (*Flavor, error) { var s struct { Flavor *Flavor `json:"flavor"` @@ -30,22 +32,30 @@ func (r commonResult) Extract() (*Flavor, error) { return s.Flavor, err } -// Flavor records represent (virtual) hardware configurations for server resources in a region. +// Flavor represent (virtual) hardware configurations for server resources +// in a region. type Flavor struct { - // The Id field contains the flavor's unique identifier. - // For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance. + // ID is the flavor's unique ID. ID string `json:"id"` - // The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively. + + // Disk is the amount of root disk, measured in GB. Disk int `json:"disk"` - RAM int `json:"ram"` - // The Name field provides a human-readable moniker for the flavor. - Name string `json:"name"` + + // RAM is the amount of memory, measured in MB. + RAM int `json:"ram"` + + // Name is the name of the flavor. + Name string `json:"name"` + + // RxTxFactor describes bandwidth alterations of the flavor. RxTxFactor float64 `json:"rxtx_factor"` - // Swap indicates how much space is reserved for swap. - // If not provided, this field will be set to 0. + + // Swap is the amount of swap space, measured in MB. Swap int `json:"swap"` + // VCPUs indicates how many (virtual) CPUs are available for this flavor. VCPUs int `json:"vcpus"` + // IsPublic indicates whether the flavor is public. IsPublic bool `json:"is_public"` } @@ -82,18 +92,19 @@ func (r *Flavor) UnmarshalJSON(b []byte) error { return nil } -// FlavorPage contains a single page of the response from a List call. +// FlavorPage contains a single page of all flavors from a ListDetails call. type FlavorPage struct { pagination.LinkedPageBase } -// IsEmpty determines if a page contains any results. +// IsEmpty determines if a FlavorPage contains any results. func (page FlavorPage) IsEmpty() (bool, error) { flavors, err := ExtractFlavors(page) return len(flavors) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. func (page FlavorPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"flavors_links"` @@ -105,7 +116,8 @@ func (page FlavorPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// ExtractFlavors provides access to the list of flavors in a page acquired from the List operation. +// ExtractFlavors provides access to the list of flavors in a page acquired +// from the ListDetail operation. func ExtractFlavors(r pagination.Page) ([]Flavor, error) { var s struct { Flavors []Flavor `json:"flavors"` From 8e239516a91da9def3c63b9576df9cbd44c08dc3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 20:29:43 -0600 Subject: [PATCH 0040/2296] Compute v2: images docs --- openstack/compute/v2/images/doc.go | 37 +++++++++++++++++++---- openstack/compute/v2/images/requests.go | 31 +++++++++++-------- openstack/compute/v2/images/results.go | 40 ++++++++++++++++--------- 3 files changed, 76 insertions(+), 32 deletions(-) diff --git a/openstack/compute/v2/images/doc.go b/openstack/compute/v2/images/doc.go index 0edaa3f025..3f8d657d57 100644 --- a/openstack/compute/v2/images/doc.go +++ b/openstack/compute/v2/images/doc.go @@ -1,7 +1,32 @@ -// Package images provides information and interaction with the image API -// resource in the OpenStack Compute service. -// -// An image is a collection of files used to create or rebuild a server. -// Operators provide a number of pre-built OS images by default. You may also -// create custom images from cloud servers you have launched. +/* +Package images provides information and interaction with the images through +the OpenStack Compute service. + +This API is deprecated and will be removed from a future version of the Nova +API service. + +An image is a collection of files used to create or rebuild a server. +Operators provide a number of pre-built OS images by default. You may also +create custom images from cloud servers you have launched. + +Example to List Images + + listOpts := images.ListOpts{ + Limit: 2, + } + + allPages, err := images.ListDetail(computeClient, listOpts).AllPages() + if err != nil { + panic("Unable to list images: %s", err) + } + + allImages, err := images.ExtractImages(allPages) + if err != nil { + panic("Unable to extract images: %s", err) + } + + for _, image := range allImages { + fmt.Println("%+v\n", image) + } +*/ package images diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go index df9f1da8f6..558b481b9e 100644 --- a/openstack/compute/v2/images/requests.go +++ b/openstack/compute/v2/images/requests.go @@ -6,26 +6,33 @@ import ( ) // ListOptsBuilder allows extensions to add additional parameters to the -// List request. +// ListDetail request. type ListOptsBuilder interface { ToImageListQuery() (string, error) } -// ListOpts contain options for limiting the number of Images returned from a call to ListDetail. +// ListOpts contain options filtering Images returned from a call to ListDetail. type ListOpts struct { - // When the image last changed status (in date-time format). + // ChangesSince filters Images based on the last changed status (in date-time + // format). ChangesSince string `q:"changes-since"` - // The number of Images to return. + + // Limit limits the number of Images to return. Limit int `q:"limit"` - // UUID of the Image at which to set a marker. + + // Mark is an Image UUID at which to set a marker. Marker string `q:"marker"` - // The name of the Image. + + // Name is the name of the Image. Name string `q:"name"` - // The name of the Server (in URL format). + + // Server is the name of the Server (in URL format). Server string `q:"server"` - // The current status of the Image. + + // Status is the current status of the Image. Status string `q:"status"` - // The value of the type of image (e.g. BASE, SERVER, ALL) + + // Type is the type of image (e.g. BASE, SERVER, ALL). Type string `q:"type"` } @@ -50,8 +57,7 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat }) } -// Get acquires additional detail about a specific image by ID. -// Use ExtractImage() to interpret the result as an openstack Image. +// Get returns data about a specific image by its ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return @@ -63,7 +69,8 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { return } -// IDFromName is a convienience function that returns an image's ID given its name. +// IDFromName is a convienience function that returns an image's ID given its +// name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/compute/v2/images/results.go b/openstack/compute/v2/images/results.go index f9ebc69e98..70d1018c72 100644 --- a/openstack/compute/v2/images/results.go +++ b/openstack/compute/v2/images/results.go @@ -5,12 +5,14 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// GetResult temporarily stores a Get response. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as an Image. type GetResult struct { gophercloud.Result } -// DeleteResult represents the result of an image.Delete operation. +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } @@ -24,44 +26,53 @@ func (r GetResult) Extract() (*Image, error) { return s.Image, err } -// Image is used for JSON (un)marshalling. -// It provides a description of an OS image. +// Image represents an Image returned by the Compute API. type Image struct { - // ID contains the image's unique identifier. + // ID is the unique ID of an image. ID string + // Created is the date when the image was created. Created string - // MinDisk and MinRAM specify the minimum resources a server must provide to be able to install the image. + // MinDisk is the minimum amount of disk a flavor must have to be able + // to create a server based on the image, measured in GB. MinDisk int - MinRAM int + + // MinRAM is the minimum amount of RAM a flavor must have to be able + // to create a server based on the image, measured in MB. + MinRAM int // Name provides a human-readable moniker for the OS image. Name string // The Progress and Status fields indicate image-creation status. - // Any usable image will have 100% progress. Progress int - Status string + // Status is the current status of the image. + Status string + + // Update is the date when the image was updated. Updated string + // Metadata provides free-form key/value pairs that further describe the + // image. Metadata map[string]interface{} } -// ImagePage contains a single page of results from a List operation. -// Use ExtractImages to convert it into a slice of usable structs. +// ImagePage contains a single page of all Images returne from a ListDetail +// operation. Use ExtractImages to convert it into a slice of usable structs. type ImagePage struct { pagination.LinkedPageBase } -// IsEmpty returns true if a page contains no Image results. +// IsEmpty returns true if an ImagePage contains no Image results. func (page ImagePage) IsEmpty() (bool, error) { images, err := ExtractImages(page) return len(images) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. func (page ImagePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"images_links"` @@ -73,7 +84,8 @@ func (page ImagePage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// ExtractImages converts a page of List results into a slice of usable Image structs. +// ExtractImages converts a page of List results into a slice of usable Image +// structs. func ExtractImages(r pagination.Page) ([]Image, error) { var s struct { Images []Image `json:"images"` From 52d2cbf91e112b1465a178fe3997614904cdd945 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 22:05:07 -0600 Subject: [PATCH 0041/2296] Compute v2: servers docs --- openstack/compute/v2/servers/doc.go | 119 +++++++++++- openstack/compute/v2/servers/requests.go | 231 ++++++++++++++--------- openstack/compute/v2/servers/results.go | 146 +++++++++----- openstack/compute/v2/servers/util.go | 5 +- 4 files changed, 356 insertions(+), 145 deletions(-) diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index fe4567120c..087bd860f3 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -1,6 +1,115 @@ -// Package servers provides information and interaction with the server API -// resource in the OpenStack Compute service. -// -// A server is a virtual machine instance in the compute system. In order for -// one to be provisioned, a valid flavor and image are required. +/* +Package servers provides information and interaction with the server API +resource in the OpenStack Compute service. + +A server is a virtual machine instance in the compute system. In order for +one to be provisioned, a valid flavor and image are required. + +Example to List Servers + + listOpts := servers.ListOpts{ + AllTenants: true, + } + + allPages, err := servers.List(computeClient, listOpts).AllPages() + if err != nil { + panic("Unable to list servers: %s", err) + } + + allServers, err := servers.ExtractServers(allPages) + if err != nil { + panic("Unable to extract servers: %s", err) + } + + for _, server := range allServers { + fmt.Println("%+v\n", server) + } + +Example to Create a Server + + createOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + server, err := servers.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } + +Example to Delete a Server + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + err := servers.Delete(computeClient, serverID).ExtractErr() + if err != nil { + panic("Error deleting server: %s", err) + } + +Example to Force Delete a Server + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + err := servers.ForceDelete(computeClient, serverID).ExtractErr() + if err != nil { + panic("Error deleting server: %s", err) + } + +Example to Reboot a Server + + rebootOpts := servers.RebootOpts{ + Type: servers.SoftReboot, + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + err := servers.Reboot(computeClient, serverID, rebootOpts).ExtractErr() + if err != nil { + panic("Unable to reboot server: %s", err) + } + +Example to Rebuild a Server + + rebuildOpts := servers.RebuildOpts{ + Name: "new_name", + ImageID: "image-uuid", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + server, err := servers.Rebuilt(computeClient, serverID, rebuildOpts).Extract() + if err != nil { + panic("Unable to rebuild server: %s", err) + } + +Example to Resize a Server + + resizeOpts := servers.ResizeOpts{ + FlavorRef: "flavor-uuid", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + err := servers.Resize(computeClient, serverID, resizeOpts).ExtractErr() + if err != nil { + panic("Unable to resize server: %s", err) + } + + err = servers.ConfirmResize(computeClient, serverID).ExtractErr() + if err != nil { + panic("Unable to confirm resize: %s", err) + } + +Example to Snapshot a Server + + snapshotOpts := servers.CreateImageOpts{ + Name: "snapshot_name", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + image, err := servers.CreateImage(computeClient, serverID, snapshotOpts).ExtractImageID() + if err != nil { + panic("Unable to snapshot server: %s", err) + } +*/ package servers diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 1a7b3ec77e..c445f0ede0 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -21,13 +21,13 @@ type ListOptsBuilder interface { // the server attributes you want to see returned. Marker and Limit are used // for pagination. type ListOpts struct { - // A time/date stamp for when the server last changed status. + // ChangesSince is a time/date stamp for when the server last changed status. ChangesSince string `q:"changes-since"` - // Name of the image in URL format. + // Image is the name of the image in URL format. Image string `q:"image"` - // Name of the flavor in URL format. + // Flavor is the name of the flavor in URL format. Flavor string `q:"flavor"` // Name of the server as a string; can be queried with regular expressions. @@ -36,22 +36,24 @@ type ListOpts struct { // underlying database server implemented for Compute. Name string `q:"name"` - // Value of the status of the server so that you can filter on "ACTIVE" for example. + // Status is the value of the status of the server so that you can filter on + // "ACTIVE" for example. Status string `q:"status"` - // Name of the host as a string. + // Host is the name of the host as a string. Host string `q:"host"` - // UUID of the server at which you want to set a marker. + // Marker is a UUID of the server at which you want to set a marker. Marker string `q:"marker"` - // Integer value for the limit of values to return. + // Limit is an integer value for the limit of values to return. Limit int `q:"limit"` - // Bool to show all tenants + // AllTenants is a bool to show all tenants. AllTenants bool `q:"all_tenants"` - // List servers for a particular tenant. Setting "AllTenants = true" is required. + // TenantID lists servers for a particular tenant. + // Setting "AllTenants = true" is required. TenantID string `q:"tenant_id"` } @@ -76,15 +78,16 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. -// The CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToServerCreateMap() (map[string]interface{}, error) } -// Network is used within CreateOpts to control a new server's network attachments. +// Network is used within CreateOpts to control a new server's network +// attachments. type Network struct { - // UUID of a nova-network to attach to the newly provisioned server. + // UUID of a network to attach to the newly provisioned server. // Required unless Port is provided. UUID string @@ -92,19 +95,21 @@ type Network struct { // Required unless UUID is provided. Port string - // FixedIP [optional] specifies a fixed IPv4 address to be used on this network. + // FixedIP specifies a fixed IPv4 address to be used on this network. FixedIP string } // Personality is an array of files that are injected into the server at launch. type Personality []*File -// File is used within CreateOpts and RebuildOpts to inject a file into the server at launch. -// File implements the json.Marshaler interface, so when a Create or Rebuild operation is requested, -// json.Marshal will call File's MarshalJSON method. +// File is used within CreateOpts and RebuildOpts to inject a file into the +// server at launch. +// File implements the json.Marshaler interface, so when a Create or Rebuild +// operation is requested, json.Marshal will call File's MarshalJSON method. type File struct { - // Path of the file + // Path of the file. Path string + // Contents of the file. Maximum content size is 255 bytes. Contents []byte } @@ -126,13 +131,13 @@ type CreateOpts struct { // Name is the name to assign to the newly launched server. Name string `json:"name" required:"true"` - // ImageRef [optional; required if ImageName is not provided] is the ID or full - // URL to the image that contains the server's OS and initial state. + // ImageRef [optional; required if ImageName is not provided] is the ID or + // full URL to the image that contains the server's OS and initial state. // Also optional if using the boot-from-volume extension. ImageRef string `json:"imageRef"` - // ImageName [optional; required if ImageRef is not provided] is the name of the - // image that contains the server's OS and initial state. + // ImageName [optional; required if ImageRef is not provided] is the name of + // the image that contains the server's OS and initial state. // Also optional if using the boot-from-volume extension. ImageName string `json:"-"` @@ -144,7 +149,8 @@ type CreateOpts struct { // the flavor that describes the server's specs. FlavorName string `json:"-"` - // SecurityGroups lists the names of the security groups to which this server should belong. + // SecurityGroups lists the names of the security groups to which this server + // should belong. SecurityGroups []string `json:"-"` // UserData contains configuration information or scripts to use upon launch. @@ -155,10 +161,12 @@ type CreateOpts struct { AvailabilityZone string `json:"availability_zone,omitempty"` // Networks dictates how this server will be attached to available networks. - // By default, the server will be attached to all isolated networks for the tenant. + // By default, the server will be attached to all isolated networks for the + // tenant. Networks []Network `json:"-"` - // Metadata contains key-value pairs (up to 255 bytes each) to attach to the server. + // Metadata contains key-value pairs (up to 255 bytes each) to attach to the + // server. Metadata map[string]string `json:"metadata,omitempty"` // Personality includes files to inject into the server at launch. @@ -169,7 +177,7 @@ type CreateOpts struct { ConfigDrive *bool `json:"config_drive,omitempty"` // AdminPass sets the root user password. If not set, a randomly-generated - // password will be created and returned in the rponse. + // password will be created and returned in the response. AdminPass string `json:"adminPass,omitempty"` // AccessIPv4 specifies an IPv4 address for the instance. @@ -183,7 +191,8 @@ type CreateOpts struct { ServiceClient *gophercloud.ServiceClient `json:"-"` } -// ToServerCreateMap assembles a request body based on the contents of a CreateOpts. +// ToServerCreateMap assembles a request body based on the contents of a +// CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { sc := opts.ServiceClient opts.ServiceClient = nil @@ -277,13 +286,14 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Delete requests that a server previously provisioned be removed from your account. +// Delete requests that a server previously provisioned be removed from your +// account. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } -// ForceDelete forces the deletion of a server +// ForceDelete forces the deletion of a server. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) return @@ -297,12 +307,14 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +// UpdateOptsBuilder allows extensions to add additional attributes to the +// Update request. type UpdateOptsBuilder interface { ToServerUpdateMap() (map[string]interface{}, error) } -// UpdateOpts specifies the base attributes that may be updated on an existing server. +// UpdateOpts specifies the base attributes that may be updated on an existing +// server. type UpdateOpts struct { // Name changes the displayed name of the server. // The server host name will *not* change. @@ -334,7 +346,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// ChangeAdminPassword alters the administrator or root password for a specified server. +// ChangeAdminPassword alters the administrator or root password for a specified +// server. func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { b := map[string]interface{}{ "changePassword": map[string]string{ @@ -357,33 +370,38 @@ const ( PowerCycle = HardReboot ) -// RebootOptsBuilder is an interface that options must satisfy in order to be -// used when rebooting a server instance +// RebootOptsBuilder allows extensions to add additional parameters to the +// reboot request. type RebootOptsBuilder interface { ToServerRebootMap() (map[string]interface{}, error) } -// RebootOpts satisfies the RebootOptsBuilder interface +// RebootOpts provides options to the reboot request. type RebootOpts struct { + // Type is the type of reboot to perform on the server. Type RebootMethod `json:"type" required:"true"` } -// ToServerRebootMap allows RebootOpts to satisfiy the RebootOptsBuilder -// interface +// ToServerRebootMap builds a body for the reboot request. func (opts *RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "reboot") } -// Reboot requests that a given server reboot. -// Two methods exist for rebooting a server: -// -// HardReboot (aka PowerCycle) starts the server instance by physically cutting power to the machine, or if a VM, -// terminating it at the hypervisor level. -// It's done. Caput. Full stop. -// Then, after a brief while, power is rtored or the VM instance rtarted. -// -// SoftReboot (aka OSReboot) simply tells the OS to rtart under its own procedur. -// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to rtart the machine. +/* + Reboot requests that a given server reboot. + + Two methods exist for rebooting a server: + + HardReboot (aka PowerCycle) starts the server instance by physically cutting + power to the machine, or if a VM, terminating it at the hypervisor level. + It's done. Caput. Full stop. + Then, after a brief while, power is rtored or the VM instance restarted. + + SoftReboot (aka OSReboot) simply tells the OS to restart under its own + procedure. + E.g., in Linux, asking it to enter runlevel 6, or executing + "sudo shutdown -r now", or by asking Windows to rtart the machine. +*/ func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) { b, err := opts.ToServerRebootMap() if err != nil { @@ -394,31 +412,43 @@ func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder return } -// RebuildOptsBuilder is an interface that allows extensions to override the -// default behaviour of rebuild options +// RebuildOptsBuilder allows extensions to provide additional parameters to the +// rebuild request. type RebuildOptsBuilder interface { ToServerRebuildMap() (map[string]interface{}, error) } // RebuildOpts represents the configuration options used in a server rebuild -// operation +// operation. type RebuildOpts struct { - // The server's admin password + // AdminPass is the server's admin password AdminPass string `json:"adminPass,omitempty"` - // The ID of the image you want your server to be provisioned on - ImageID string `json:"imageRef"` + + // ImageID is the ID of the image you want your server to be provisioned on. + ImageID string `json:"imageRef"` + + // ImageName is readable name of an image. ImageName string `json:"-"` + // Name to set the server to Name string `json:"name,omitempty"` + // AccessIPv4 [optional] provides a new IPv4 address for the instance. AccessIPv4 string `json:"accessIPv4,omitempty"` + // AccessIPv6 [optional] provides a new IPv6 address for the instance. AccessIPv6 string `json:"accessIPv6,omitempty"` - // Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server. + + // Metadata [optional] contains key-value pairs (up to 255 bytes each) + // to attach to the server. Metadata map[string]string `json:"metadata,omitempty"` + // Personality [optional] includes files to inject into the server at launch. // Rebuild will base64-encode file contents for you. - Personality Personality `json:"personality,omitempty"` + Personality Personality `json:"personality,omitempty"` + + // ServiceClient will allow calls to be made to retrieve an image or + // flavor ID by name. ServiceClient *gophercloud.ServiceClient `json:"-"` } @@ -461,31 +491,34 @@ func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuild return } -// ResizeOptsBuilder is an interface that allows extensions to override the default structure of -// a Resize request. +// ResizeOptsBuilder allows extensions to add additional parameters to the +// resize request. type ResizeOptsBuilder interface { ToServerResizeMap() (map[string]interface{}, error) } -// ResizeOpts represents the configuration options used to control a Resize operation. +// ResizeOpts represents the configuration options used to control a Resize +// operation. type ResizeOpts struct { // FlavorRef is the ID of the flavor you wish your server to become. FlavorRef string `json:"flavorRef" required:"true"` } -// ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON request body for the -// Resize request. +// ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON +// request body for the Resize request. func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "resize") } // Resize instructs the provider to change the flavor of the server. +// // Note that this implies rebuilding it. +// // Unfortunately, one cannot pass rebuild parameters to the resize function. // When the resize completes, the server will be in RESIZE_VERIFY state. -// While in this state, you can explore the use of the new server's configuration. -// If you like it, call ConfirmResize() to commit the resize permanently. -// Otherwise, call RevertResize() to restore the old configuration. +// While in this state, you can explore the use of the new server's +// configuration. If you like it, call ConfirmResize() to commit the resize +// permanently. Otherwise, call RevertResize() to restore the old configuration. func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { b, err := opts.ToServerResizeMap() if err != nil { @@ -545,8 +578,8 @@ func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder return } -// ResetMetadataOptsBuilder allows extensions to add additional parameters to the -// Reset request. +// ResetMetadataOptsBuilder allows extensions to add additional parameters to +// the Reset request. type ResetMetadataOptsBuilder interface { ToMetadataResetMap() (map[string]interface{}, error) } @@ -554,20 +587,23 @@ type ResetMetadataOptsBuilder interface { // MetadataOpts is a map that contains key-value pairs. type MetadataOpts map[string]string -// ToMetadataResetMap assembles a body for a Reset request based on the contents of a MetadataOpts. +// ToMetadataResetMap assembles a body for a Reset request based on the contents +// of a MetadataOpts. func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) { return map[string]interface{}{"metadata": opts}, nil } -// ToMetadataUpdateMap assembles a body for an Update request based on the contents of a MetadataOpts. +// ToMetadataUpdateMap assembles a body for an Update request based on the +// contents of a MetadataOpts. func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) { return map[string]interface{}{"metadata": opts}, nil } -// ResetMetadata will create multiple new key-value pairs for the given server ID. -// Note: Using this operation will erase any already-existing metadata and create -// the new metadata provided. To keep any already-existing metadata, use the -// UpdateMetadatas or UpdateMetadata function. +// ResetMetadata will create multiple new key-value pairs for the given server +// ID. +// Note: Using this operation will erase any already-existing metadata and +// create the new metadata provided. To keep any already-existing metadata, +// use the UpdateMetadatas or UpdateMetadata function. func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { b, err := opts.ToMetadataResetMap() if err != nil { @@ -586,15 +622,15 @@ func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult return } -// UpdateMetadataOptsBuilder allows extensions to add additional parameters to the -// Create request. +// UpdateMetadataOptsBuilder allows extensions to add additional parameters to +// the Create request. type UpdateMetadataOptsBuilder interface { ToMetadataUpdateMap() (map[string]interface{}, error) } -// UpdateMetadata updates (or creates) all the metadata specified by opts for the given server ID. -// This operation does not affect already-existing metadata that is not specified -// by opts. +// UpdateMetadata updates (or creates) all the metadata specified by opts for +// the given server ID. This operation does not affect already-existing metadata +// that is not specified by opts. func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToMetadataUpdateMap() if err != nil { @@ -616,7 +652,8 @@ type MetadatumOptsBuilder interface { // MetadatumOpts is a map of length one that contains a key-value pair. type MetadatumOpts map[string]string -// ToMetadatumCreateMap assembles a body for a Create request based on the contents of a MetadataumOpts. +// ToMetadatumCreateMap assembles a body for a Create request based on the +// contents of a MetadataumOpts. func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) { if len(opts) != 1 { err := gophercloud.ErrInvalidInput{} @@ -632,7 +669,8 @@ func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string return metadatum, key, nil } -// CreateMetadatum will create or update the key-value pair with the given key for the given server ID. +// CreateMetadatum will create or update the key-value pair with the given key +// for the given server ID. func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { b, key, err := opts.ToMetadatumCreateMap() if err != nil { @@ -645,53 +683,60 @@ func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts Metadatu return } -// Metadatum requests the key-value pair with the given key for the given server ID. +// Metadatum requests the key-value pair with the given key for the given +// server ID. func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { _, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil) return } -// DeleteMetadatum will delete the key-value pair with the given key for the given server ID. +// DeleteMetadatum will delete the key-value pair with the given key for the +// given server ID. func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { _, r.Err = client.Delete(metadatumURL(client, id, key), nil) return } -// ListAddresses makes a request against the API to list the servers IP addresses. +// ListAddresses makes a request against the API to list the servers IP +// addresses. func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page { return AddressPage{pagination.SinglePageBase(r)} }) } -// ListAddressesByNetwork makes a request against the API to list the servers IP addresses -// for the given network. +// ListAddressesByNetwork makes a request against the API to list the servers IP +// addresses for the given network. func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page { return NetworkAddressPage{pagination.SinglePageBase(r)} }) } -// CreateImageOptsBuilder is the interface types must satisfy in order to be -// used as CreateImage options +// CreateImageOptsBuilder allows extensions to add additional parameters to the +// CreateImage request. type CreateImageOptsBuilder interface { ToServerCreateImageMap() (map[string]interface{}, error) } -// CreateImageOpts satisfies the CreateImageOptsBuilder +// CreateImageOpts provides options to pass to the CreateImage request. type CreateImageOpts struct { - // Name of the image/snapshot + // Name of the image/snapshot. Name string `json:"name" required:"true"` - // Metadata contains key-value pairs (up to 255 bytes each) to attach to the created image. + + // Metadata contains key-value pairs (up to 255 bytes each) to attach to + // the created image. Metadata map[string]string `json:"metadata,omitempty"` } -// ToServerCreateImageMap formats a CreateImageOpts structure into a request body. +// ToServerCreateImageMap formats a CreateImageOpts structure into a request +// body. func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "createImage") } -// CreateImage makes a request against the nova API to schedule an image to be created of the server +// CreateImage makes a request against the nova API to schedule an image to be +// created of the server func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { b, err := opts.ToServerCreateImageMap() if err != nil { @@ -706,7 +751,8 @@ func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageO return } -// IDFromName is a convienience function that returns a server's ID given its name. +// IDFromName is a convienience function that returns a server's ID given its +// name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" @@ -737,7 +783,8 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) } } -// GetPassword makes a request against the nova API to get the encrypted administrative password. +// GetPassword makes a request against the nova API to get the encrypted +// administrative password. func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { _, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil) return diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 1ae1e91c78..7ef80e92e2 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -32,54 +32,64 @@ func ExtractServersInto(r pagination.Page, v interface{}) error { return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers") } -// CreateResult temporarily contains the response from a Create call. +// CreateResult is the response from a Create operation. Call its Extract +// method to interpret it as a Server. type CreateResult struct { serverResult } -// GetResult temporarily contains the response from a Get call. +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as a Server. type GetResult struct { serverResult } -// UpdateResult temporarily contains the response from an Update call. +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Server. type UpdateResult struct { serverResult } -// DeleteResult temporarily contains the response from a Delete call. +// 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 } -// RebuildResult temporarily contains the response from a Rebuild call. +// RebuildResult is the response from a Rebuild operation. Call its Extract +// method to interpret it as a Server. type RebuildResult struct { serverResult } -// ActionResult represents the result of server action operations, like reboot +// ActionResult represents the result of server action operations, like reboot. +// Call its ExtractErr method to determine if the action succeeded or failed. type ActionResult struct { gophercloud.ErrResult } -// RescueResult represents the result of a server rescue operation +// RescueResult is the response from a Rescue operation. Call its ExtractErr +// method to determine if the call succeeded or failed. type RescueResult struct { ActionResult } -// CreateImageResult represents the result of an image creation operation +// CreateImageResult is the response from a CreateImage operation. Call its +// ExtractImageID method to retrieve the ID of the newly created image. type CreateImageResult struct { gophercloud.Result } // GetPasswordResult represent the result of a get os-server-password operation. +// Call its ExtractPassword method to retrieve the password. type GetPasswordResult struct { gophercloud.Result } // ExtractPassword gets the encrypted password. // If privateKey != nil the password is decrypted with the private key. -// If privateKey == nil the encrypted password is returned and can be decrypted with: +// If privateKey == nil the encrypted password is returned and can be decrypted +// with: // echo '' | base64 -D | openssl rsautl -decrypt -inkey func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) { var s struct { @@ -107,7 +117,7 @@ func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (stri return string(password), nil } -// ExtractImageID gets the ID of the newly created server image from the header +// ExtractImageID gets the ID of the newly created server image from the header. func (r CreateImageResult) ExtractImageID() (string, error) { if r.Err != nil { return "", r.Err @@ -133,44 +143,73 @@ func (r RescueResult) Extract() (string, error) { return s.AdminPass, err } -// Server exposes only the standard OpenStack fields corresponding to a given server on the user's account. +// Server represents a server/instance in the OpenStack cloud. type Server struct { - // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant. + // ID uniquely identifies this server amongst all other servers, + // including those not accessible to the current tenant. ID string `json:"id"` + // TenantID identifies the tenant owning this server resource. TenantID string `json:"tenant_id"` + // UserID uniquely identifies the user account owning the tenant. UserID string `json:"user_id"` + // Name contains the human-readable name for the server. Name string `json:"name"` - // Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created. + + // Updated and Created contain ISO-8601 timestamps of when the state of the + // server last changed, and when it was created. Updated time.Time `json:"updated"` Created time.Time `json:"created"` - HostID string `json:"hostid"` - // Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE. + + // HostID is the host where the server is located in the cloud. + HostID string `json:"hostid"` + + // Status contains the current operational status of the server, + // such as IN_PROGRESS or ACTIVE. Status string `json:"status"` + // Progress ranges from 0..100. // A request made against the server completes only once Progress reaches 100. Progress int `json:"progress"` - // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration. + + // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, + // suitable for remote access for administration. AccessIPv4 string `json:"accessIPv4"` AccessIPv6 string `json:"accessIPv6"` - // Image refers to a JSON object, which itself indicates the OS image used to deploy the server. + + // Image refers to a JSON object, which itself indicates the OS image used to + // deploy the server. Image map[string]interface{} `json:"-"` - // Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server. + + // Flavor refers to a JSON object, which itself indicates the hardware + // configuration of the deployed server. Flavor map[string]interface{} `json:"flavor"` - // Addresses includes a list of all IP addresses assigned to the server, keyed by pool. + + // Addresses includes a list of all IP addresses assigned to the server, + // keyed by pool. Addresses map[string]interface{} `json:"addresses"` - // Metadata includes a list of all user-specified key-value pairs attached to the server. + + // Metadata includes a list of all user-specified key-value pairs attached + // to the server. Metadata map[string]string `json:"metadata"` - // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference. + + // Links includes HTTP references to the itself, useful for passing along to + // other APIs that might want a server reference. Links []interface{} `json:"links"` + // KeyName indicates which public key was injected into the server on launch. KeyName string `json:"key_name"` - // AdminPass will generally be empty (""). However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place. + + // AdminPass will generally be empty (""). However, it will contain the + // administrative password chosen when provisioning a new server without a + // set AdminPass setting in the first place. // Note that this is the ONLY time this field will be valid. AdminPass string `json:"adminPass"` - // SecurityGroups includes the security groups that this instance has applied to it + + // SecurityGroups includes the security groups that this instance has applied + // to it. SecurityGroups []map[string]interface{} `json:"security_groups"` } @@ -200,9 +239,10 @@ func (r *Server) UnmarshalJSON(b []byte) error { return err } -// ServerPage 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 ExtractServers call. +// ServerPage 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 ExtractServers call. type ServerPage struct { pagination.LinkedPageBase } @@ -213,7 +253,8 @@ func (r ServerPage) IsEmpty() (bool, error) { return len(s) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. func (r ServerPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"servers_links"` @@ -225,49 +266,59 @@ func (r ServerPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities. +// ExtractServers interprets the results of a single page from a List() call, +// producing a slice of Server entities. func ExtractServers(r pagination.Page) ([]Server, error) { var s []Server err := ExtractServersInto(r, &s) return s, err } -// MetadataResult contains the result of a call for (potentially) multiple key-value pairs. +// MetadataResult 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 MetadataResult struct { gophercloud.Result } -// GetMetadataResult temporarily contains the response from a metadata Get call. +// GetMetadataResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. type GetMetadataResult struct { MetadataResult } -// ResetMetadataResult temporarily contains the response from a metadata Reset call. +// ResetMetadataResult contains the result of a Reset operation. Call its +// Extract method to interpret it as a map[string]interface. type ResetMetadataResult struct { MetadataResult } -// UpdateMetadataResult temporarily contains the response from a metadata Update call. +// UpdateMetadataResult contains the result of an Update operation. Call its +// Extract method to interpret it as a map[string]interface. type UpdateMetadataResult struct { MetadataResult } -// MetadatumResult contains the result of a call for individual a single key-value pair. +// MetadatumResult contains the result of a call for individual a single +// key-value pair. type MetadatumResult struct { gophercloud.Result } -// GetMetadatumResult temporarily contains the response from a metadatum Get call. +// GetMetadatumResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. type GetMetadatumResult struct { MetadatumResult } -// CreateMetadatumResult temporarily contains the response from a metadatum Create call. +// CreateMetadatumResult contains the result of a Create operation. Call its +// Extract method to interpret it as a map[string]interface. type CreateMetadatumResult struct { MetadatumResult } -// DeleteMetadatumResult temporarily contains the response from a metadatum Delete call. +// DeleteMetadatumResult contains the result of a Delete operation. Call its +// ExtractErr method to determine if the call succeeded or failed. type DeleteMetadatumResult struct { gophercloud.ErrResult } @@ -296,9 +347,10 @@ type Address struct { Address string `json:"addr"` } -// AddressPage abstracts the raw results of making a ListAddresses() 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 ExtractAddresses call. +// AddressPage abstracts the raw results of making a ListAddresses() 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 ExtractAddresses call. type AddressPage struct { pagination.SinglePageBase } @@ -309,8 +361,8 @@ func (r AddressPage) IsEmpty() (bool, error) { return len(addresses) == 0, err } -// ExtractAddresses interprets the results of a single page from a ListAddresses() call, -// producing a map of addresses. +// ExtractAddresses interprets the results of a single page from a +// ListAddresses() call, producing a map of addresses. func ExtractAddresses(r pagination.Page) (map[string][]Address, error) { var s struct { Addresses map[string][]Address `json:"addresses"` @@ -319,9 +371,11 @@ func ExtractAddresses(r pagination.Page) (map[string][]Address, error) { return s.Addresses, err } -// NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() 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 ExtractAddresses call. +// NetworkAddressPage abstracts the raw results of making a +// ListAddressesByNetwork() 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 ExtractAddresses call. type NetworkAddressPage struct { pagination.SinglePageBase } @@ -332,8 +386,8 @@ func (r NetworkAddressPage) IsEmpty() (bool, error) { return len(addresses) == 0, err } -// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call, -// producing a slice of addresses. +// ExtractNetworkAddresses interprets the results of a single page from a +// ListAddressesByNetwork() call, producing a slice of addresses. func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) { var s map[string][]Address err := (r.(NetworkAddressPage)).ExtractInto(&s) diff --git a/openstack/compute/v2/servers/util.go b/openstack/compute/v2/servers/util.go index 494a0e4dc4..cadef05450 100644 --- a/openstack/compute/v2/servers/util.go +++ b/openstack/compute/v2/servers/util.go @@ -2,8 +2,9 @@ package servers import "github.com/gophercloud/gophercloud" -// WaitForStatus will continually poll a server until it successfully transitions to a specified -// status. It will do this for at most the number of seconds specified. +// WaitForStatus will continually poll a server until it successfully +// transitions to a specified status. It will do this for at most the number +// of seconds specified. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() From aa6fe65b24f69bba8766aefb440cfd7e9f7c6327 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Aug 2017 22:36:59 -0600 Subject: [PATCH 0042/2296] DNS v2: doc updates --- openstack/dns/v2/recordsets/doc.go | 58 ++++++++++++++++++++++--- openstack/dns/v2/recordsets/requests.go | 25 +++++++---- openstack/dns/v2/recordsets/results.go | 20 ++++++--- openstack/dns/v2/zones/doc.go | 52 +++++++++++++++++++--- openstack/dns/v2/zones/requests.go | 36 ++++++++++----- openstack/dns/v2/zones/results.go | 25 +++++++---- 6 files changed, 170 insertions(+), 46 deletions(-) diff --git a/openstack/dns/v2/recordsets/doc.go b/openstack/dns/v2/recordsets/doc.go index 82a0aed5d7..4954ed5398 100644 --- a/openstack/dns/v2/recordsets/doc.go +++ b/openstack/dns/v2/recordsets/doc.go @@ -1,6 +1,54 @@ -// Package recordsets provides information and interaction with the zone API -// resource for the OpenStack DNS service. -// -// For more information, see: -// http://developer.openstack.org/api-ref/dns/#recordsets +/* +Package recordsets provides information and interaction with the zone API +resource for the OpenStack DNS service. + +Example to List RecordSets by Zone + + listOpts := recordsets.ListOpts{ + Type: "A", + } + + zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" + + allPages, err := recordsets.ListByZone(dnsClient, zoneID, listOpts).AllPages() + if err != nil { + panic("Unable to list recordsets: %s", err) + } + + allRRs, err := recordsets.ExtractRecordSets(allPages() + if err != nil { + panic("Unable to extract recordsets: %s", err) + } + + for _, rr := range allRRs { + fmt.Println("%+v\n", rr) + } + +Example to Create a RecordSet + + createOpts := recordsets.CreateOpts{ + Name: "example.com.", + Type: "A", + TTL: 3600, + Description: "This is a recordset.", + Records: []string{"10.1.0.2"}, + } + + zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" + + rr, err := recordsets.Create(dnsClient, zoneID, createOpts).Extract() + if err != nil { + panic("Unable to create recordset: %s", err) + } + +Example to Delete a RecordSet + + zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" + recordsetID := "d96ed01a-b439-4eb8-9b90-7a9f71017f7b" + + err := recordsets.Delete(dnsClient, zoneID, recordsetID).ExtractErr() + if err != nil { + panic("Unable to delete recordset: %s", err) + } +*/ package recordsets diff --git a/openstack/dns/v2/recordsets/requests.go b/openstack/dns/v2/recordsets/requests.go index eab6bc926b..2d6ecdc3dc 100644 --- a/openstack/dns/v2/recordsets/requests.go +++ b/openstack/dns/v2/recordsets/requests.go @@ -55,18 +55,20 @@ func ListByZone(client *gophercloud.ServiceClient, zoneID string, opts ListOptsB }) } -// Get implements the recordset get request. +// Get implements the recordset Get request. func Get(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) { _, r.Err = client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) return } -// CreateOptsBuilder allows extensions to add additional attributes to the Create request. +// CreateOptsBuilder allows extensions to add additional attributes to the +// Create request. type CreateOptsBuilder interface { ToRecordSetCreateMap() (map[string]interface{}, error) } -// CreateOpts specifies the base attributes that may be used to create a RecordSet. +// CreateOpts specifies the base attributes that may be used to create a +// RecordSet. type CreateOpts struct { // Name is the name of the RecordSet. Name string `json:"name" required:"true"` @@ -107,16 +109,23 @@ func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBui return } -// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +// UpdateOptsBuilder allows extensions to add additional attributes to the +// Update request. type UpdateOptsBuilder interface { ToRecordSetUpdateMap() (map[string]interface{}, error) } -// UpdateOpts specifies the base attributes that may be updated on an existing RecordSet. +// UpdateOpts specifies the base attributes that may be updated on an existing +// RecordSet. type UpdateOpts struct { - Description string `json:"description,omitempty"` - TTL int `json:"ttl,omitempty"` - Records []string `json:"records,omitempty"` + // Description is a description of the RecordSet. + Description string `json:"description,omitempty"` + + // TTL is the time to live of the RecordSet. + TTL int `json:"ttl,omitempty"` + + // Records are the DNS records of the RecordSet. + Records []string `json:"records,omitempty"` } // ToRecordSetUpdateMap formats an UpdateOpts structure into a request body. diff --git a/openstack/dns/v2/recordsets/results.go b/openstack/dns/v2/recordsets/results.go index fc9d9554da..0fdc1fe52e 100644 --- a/openstack/dns/v2/recordsets/results.go +++ b/openstack/dns/v2/recordsets/results.go @@ -12,7 +12,7 @@ type commonResult struct { gophercloud.Result } -// Extract interprets a GetResult, CreateResult or UpdateResult as a concrete RecordSet. +// Extract interprets a GetResult, CreateResult or UpdateResult as a RecordSet. // An error is returned if the original call or the extraction failed. func (r commonResult) Extract() (*RecordSet, error) { var s *RecordSet @@ -20,12 +20,14 @@ func (r commonResult) Extract() (*RecordSet, error) { return s, err } -// CreateResult is the deferred result of a Create call. +// CreateResult is the result of a Create operation. Call its Extract method to +// interpret the result as a RecordSet. type CreateResult struct { commonResult } -// GetResult is the deferred result of a Get call. +// GetResult is the result of a Get operation. Call its Extract method to +// interpret the result as a RecordSet. type GetResult struct { commonResult } @@ -35,12 +37,14 @@ type RecordSetPage struct { pagination.LinkedPageBase } -// UpdateResult is the deferred result of an Update call. +// UpdateResult is result of an Update operation. Call its Extract method to +// interpret the result as a RecordSet. type UpdateResult struct { commonResult } -// DeleteResult is the deferred result of an Delete call. +// DeleteResult is result of a Delete operation. Call its ExtractErr method to +// determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } @@ -51,7 +55,7 @@ func (r RecordSetPage) IsEmpty() (bool, error) { return len(s) == 0, err } -// ExtractRecordSets extracts a slice of RecordSets from a Collection acquired from List. +// ExtractRecordSets extracts a slice of RecordSets from a List result. func ExtractRecordSets(r pagination.Page) ([]RecordSet, error) { var s struct { RecordSets []RecordSet `json:"recordsets"` @@ -60,6 +64,7 @@ func ExtractRecordSets(r pagination.Page) ([]RecordSet, error) { return s.RecordSets, err } +// RecordSet represents a DNS Record Set. type RecordSet struct { // ID is the unique ID of the recordset ID string `json:"id"` @@ -104,7 +109,8 @@ type RecordSet struct { UpdatedAt time.Time `json:"-"` // Links includes HTTP references to the itself, - // useful for passing along to other APIs that might want a recordset reference. + // useful for passing along to other APIs that might want a recordset + // reference. Links []gophercloud.Link `json:"-"` } diff --git a/openstack/dns/v2/zones/doc.go b/openstack/dns/v2/zones/doc.go index 1302cb93d4..7fd241e00b 100644 --- a/openstack/dns/v2/zones/doc.go +++ b/openstack/dns/v2/zones/doc.go @@ -1,6 +1,48 @@ -// Package tokens provides information and interaction with the zone API -// resource for the OpenStack DNS service. -// -// For more information, see: -// http://developer.openstack.org/api-ref/dns/#zone +/* +Package zones provides information and interaction with the zone API +resource for the OpenStack DNS service. + +Example to List Zones + + listOpts := zones.ListOpts{ + Email: "jdoe@example.com", + } + + allPages, err := zones.List(dnsClient, listOpts).AllPages() + if err != nil { + panic("Unable to retrieve zones: %s", err) + } + + allZones, err := zones.ExtractZones(allPages) + if err != nil { + panic("Unable to extract zones: %s", err) + } + + for _, zone := range allZones { + fmt.Println("%+v\n", zone) + } + +Example to Create a Zone + + createOpts := zones.CreateOpts{ + Name: "example.com.", + Email: "jdoe@example.com", + Type: "PRIMARY", + TTL: 7200, + Description: "This is a zone.", + } + + zone, err := zones.Create(dnsClient, createOpts).Extract() + if err != nil { + panic("Unable to create zone: %s", err) + } + +Example to Delete a Zone + + zoneID := "99d10f68-5623-4491-91a0-6daafa32b60e" + err := zones.Delete(dnsClient, zoneID).ExtractErr() + if err != nil { + panic("Unable to delete zone: %s", err) + } +*/ package zones diff --git a/openstack/dns/v2/zones/requests.go b/openstack/dns/v2/zones/requests.go index 4160f80a97..f87deadce7 100644 --- a/openstack/dns/v2/zones/requests.go +++ b/openstack/dns/v2/zones/requests.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add parameters to the List request. type ListOptsBuilder interface { ToZoneListQuery() (string, error) } @@ -31,11 +32,13 @@ type ListOpts struct { Type string `q:"type"` } +// ToZoneListQuery formats a ListOpts into a query string. func (opts ListOpts) ToZoneListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } +// List implements a zone List request. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := baseURL(client) if opts != nil { @@ -50,18 +53,19 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// Get returns additional information about a zone, given its ID. +// Get returns information about a zone, given its ID. func Get(client *gophercloud.ServiceClient, zoneID string) (r GetResult) { _, r.Err = client.Get(zoneURL(client, zoneID), &r.Body, nil) return } -// CreateOptsBuilder allows extensions to add additional attributes to the Update request. +// CreateOptsBuilder allows extensions to add additional attributes to the +// Create request. type CreateOptsBuilder interface { ToZoneCreateMap() (map[string]interface{}, error) } -// CreateOpts specifies the base attributes used to create a zone. +// CreateOpts specifies the attributes used to create a zone. type CreateOpts struct { // Attributes are settings that supply hints and filters for the zone. Attributes map[string]string `json:"attributes,omitempty"` @@ -99,7 +103,7 @@ func (opts CreateOpts) ToZoneCreateMap() (map[string]interface{}, error) { return b, nil } -// Create a zone +// Create implements a zone create request. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToZoneCreateMap() if err != nil { @@ -112,17 +116,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +// UpdateOptsBuilder allows extensions to add additional attributes to the +// Update request. type UpdateOptsBuilder interface { ToZoneUpdateMap() (map[string]interface{}, error) } -// UpdateOpts specifies the base attributes to update a zone. +// UpdateOpts specifies the attributes to update a zone. type UpdateOpts struct { - Email string `json:"email,omitempty"` - TTL int `json:"-"` - Masters []string `json:"masters,omitempty"` - Description string `json:"description,omitempty"` + // Email contact of the zone. + Email string `json:"email,omitempty"` + + // TTL is the time to live of the zone. + TTL int `json:"-"` + + // Masters specifies zone masters if this is a secondary zone. + Masters []string `json:"masters,omitempty"` + + // Description of the zone. + Description string `json:"description,omitempty"` } // ToZoneUpdateMap formats an UpdateOpts structure into a request body. @@ -139,7 +151,7 @@ func (opts UpdateOpts) ToZoneUpdateMap() (map[string]interface{}, error) { return b, nil } -// Update a zone. +// Update implements a zone update request. func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToZoneUpdateMap() if err != nil { @@ -152,7 +164,7 @@ func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBui return } -// Delete a zone. +// Delete implements a zone delete request. func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { _, r.Err = client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ OkCodes: []int{202}, diff --git a/openstack/dns/v2/zones/results.go b/openstack/dns/v2/zones/results.go index de8069567e..a36eca7e20 100644 --- a/openstack/dns/v2/zones/results.go +++ b/openstack/dns/v2/zones/results.go @@ -13,7 +13,7 @@ type commonResult struct { gophercloud.Result } -// Extract interprets a GetResult, CreateResult or UpdateResult as a concrete Zone. +// Extract interprets a GetResult, CreateResult or UpdateResult as a Zone. // An error is returned if the original call or the extraction failed. func (r commonResult) Extract() (*Zone, error) { var s *Zone @@ -21,22 +21,26 @@ func (r commonResult) Extract() (*Zone, error) { return s, err } -// CreateResult is the deferred result of a Create call. +// CreateResult is the result of a Create request. Call its Extract method +// to interpret the result as a Zone. type CreateResult struct { commonResult } -// GetResult is the deferred result of a Get call. +// GetResult is the result of a Get request. Call its Extract method +// to interpret the result as a Zone. type GetResult struct { commonResult } -// UpdateResult is the deferred result of an Update call. +// UpdateResult is the result of an Update request. Call its Extract method +// to interpret the result as a Zone. type UpdateResult struct { commonResult } -// DeleteResult is the deferred result of an Delete call. +// DeleteResult is the result of a Delete request. Call its ExtractErr method +// to determine if the request succeeded or failed. type DeleteResult struct { commonResult } @@ -52,7 +56,7 @@ func (r ZonePage) IsEmpty() (bool, error) { return len(s) == 0, err } -// ExtractZones extracts a slice of Services from a Collection acquired from List. +// ExtractZones extracts a slice of Zones from a List result. func ExtractZones(r pagination.Page) ([]Zone, error) { var s struct { Zones []Zone `json:"zones"` @@ -63,7 +67,8 @@ func ExtractZones(r pagination.Page) ([]Zone, error) { // Zone represents a DNS zone. type Zone struct { - // ID uniquely identifies this zone amongst all other zones, including those not accessible to the current tenant. + // ID uniquely identifies this zone amongst all other zones, including those + // not accessible to the current tenant. ID string `json:"id"` // PoolID is the ID for the pool hosting this zone. @@ -113,10 +118,12 @@ type Zone struct { // UpdatedAt is the date when the last change was made to the zone. UpdatedAt time.Time `json:"-"` - // TransferredAt is the last time an update was retrieved from the master servers. + // TransferredAt is the last time an update was retrieved from the + // master servers. TransferredAt time.Time `json:"-"` - // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference. + // Links includes HTTP references to the itself, useful for passing along + // to other APIs that might want a server reference. Links map[string]interface{} `json:"links"` } From a47992938fceec739195efb989a377ddf18f7cf6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 14:10:06 -0600 Subject: [PATCH 0043/2296] OpenStack: support OS_PROJECT_* variables --- openstack/auth_env.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/openstack/auth_env.go b/openstack/auth_env.go index 95286041d6..b5482ba8c9 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -16,7 +16,12 @@ The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, -or an error will result. OS_TENANT_ID and OS_TENANT_NAME are optional. +or an error will result. OS_TENANT_ID, OS_TENANT_NAME, OS_PROJECT_ID, and +OS_PROJECT_NAME are optional. + +OS_TENANT_ID and OS_TENANT_NAME are mutually exclusive to OS_PROJECT_ID and +OS_PROJECT_NAME. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will +still be referred as "tenant" in Gophercloud. To use this function, first set the OS_* environment variables (for example, by sourcing an `openrc` file), then: @@ -34,6 +39,16 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { domainID := os.Getenv("OS_DOMAIN_ID") domainName := os.Getenv("OS_DOMAIN_NAME") + // If OS_PROJECT_ID is set, overwrite tenantID with the value. + if v := os.Getenv("OS_PROJECT_ID"); v != "" { + tenantID = v + } + + // If OS_PROJECT_NAME is set, overwrite tenantName with the value. + if v := os.Getenv("OS_PROJECT_NAME"); v != "" { + tenantName = v + } + if authURL == "" { err := gophercloud.ErrMissingInput{Argument: "authURL"} return nilOptions, err From 24a39dd9181b5c01a2fef0d155fca494380f8893 Mon Sep 17 00:00:00 2001 From: Joachim Barheine Date: Mon, 26 Jun 2017 16:37:00 +0200 Subject: [PATCH 0044/2296] Fix domain scoping via name * remove ErrScopeDomainName and all its usages (since that error is based on wrong assumptions/specification error) * implement domain scoping via name * remove failure test-case, add positive test-case Closes gophercloud/gophercloud/#252 --- auth_options.go | 7 ++++- errors.go | 7 ----- openstack/identity/v3/tokens/requests.go | 7 ++++- .../v3/tokens/testing/requests_test.go | 31 +++++++++++++++---- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/auth_options.go b/auth_options.go index f438ab2f2e..4211470020 100644 --- a/auth_options.go +++ b/auth_options.go @@ -338,7 +338,12 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { }, }, nil } else if scope.DomainName != "" { - return nil, ErrScopeDomainName{} + // DomainName + return map[string]interface{}{ + "domain": map[string]interface{}{ + "name": &scope.DomainName, + }, + }, nil } return nil, nil diff --git a/errors.go b/errors.go index e0fe7c1e08..88fd2ac676 100644 --- a/errors.go +++ b/errors.go @@ -393,13 +393,6 @@ func (e ErrScopeProjectIDAlone) Error() string { return "ProjectID must be supplied alone in a Scope" } -// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope. -type ErrScopeDomainName struct{ BaseError } - -func (e ErrScopeDomainName) Error() string { - return "DomainName must be supplied with a ProjectName or ProjectID in a Scope" -} - // ErrScopeEmpty indicates that no credentials were provided in a Scope. type ErrScopeEmpty struct{ BaseError } diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index 39c19aee3c..a80fde15c4 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -125,7 +125,12 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { }, }, nil } else if opts.Scope.DomainName != "" { - return nil, gophercloud.ErrScopeDomainName{} + // DomainName + return map[string]interface{}{ + "domain": map[string]interface{}{ + "name": &opts.Scope.DomainName, + }, + }, nil } return nil, nil diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index 41f116218e..3891ae4ab5 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -194,6 +194,31 @@ func TestCreateDomainIDScope(t *testing.T) { `) } +func TestCreateDomainNameScope(t *testing.T) { + options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} + scope := &tokens.Scope{DomainName: "evil-plans"} + authTokenPost(t, options, scope, ` + { + "auth": { + "identity": { + "methods": ["password"], + "password": { + "user": { + "id": "fenris", + "password": "g0t0h311" + } + } + }, + "scope": { + "domain": { + "name": "evil-plans" + } + } + } + } + `) +} + func TestCreateProjectNameAndDomainIDScope(t *testing.T) { options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} scope := &tokens.Scope{ProjectName: "world-domination", DomainID: "1000"} @@ -381,12 +406,6 @@ func TestCreateFailureScopeDomainIDAndDomainName(t *testing.T) { authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainIDOrDomainName{}) } -func TestCreateFailureScopeDomainNameAlone(t *testing.T) { - options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} - scope := &tokens.Scope{DomainName: "notenough"} - authTokenPostErr(t, options, scope, false, gophercloud.ErrScopeDomainName{}) -} - /* func TestCreateFailureEmptyScope(t *testing.T) { options := tokens.AuthOptions{UserID: "myself", Password: "swordfish"} From 9fc2c2b5973941e86ed2df9b85d2107ee8d6d8ad Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 16 Aug 2017 20:01:09 -0600 Subject: [PATCH 0045/2296] Identity v2: roles docs --- .../identity/v2/extensions/admin/roles/doc.go | 56 +++++++++++++++++++ .../v2/extensions/admin/roles/docs.go | 16 ------ .../v2/extensions/admin/roles/results.go | 13 +++-- 3 files changed, 63 insertions(+), 22 deletions(-) create mode 100644 openstack/identity/v2/extensions/admin/roles/doc.go delete mode 100644 openstack/identity/v2/extensions/admin/roles/docs.go diff --git a/openstack/identity/v2/extensions/admin/roles/doc.go b/openstack/identity/v2/extensions/admin/roles/doc.go new file mode 100644 index 0000000000..119bf1395a --- /dev/null +++ b/openstack/identity/v2/extensions/admin/roles/doc.go @@ -0,0 +1,56 @@ +/* +Package roles provides functionality to interact with and control roles on +the API. + +A role represents a personality that a user can assume when performing a +specific set of operations. If a role includes a set of rights and +privileges, a user assuming that role inherits those rights and privileges. + +When a token is generated, the list of roles that user can assume is returned +back to them. Services that are being called by that user determine how they +interpret the set of roles a user has and to which operations or resources +each role grants access. + +It is up to individual services such as Compute or Image to assign meaning +to these roles. As far as the Identity service is concerned, a role is an +arbitrary name assigned by the user. + +Example to List Roles + + allPages, err := roles.List(identityClient).AllPages() + if err != nil { + panic("Unable to list roles: %s", err) + } + + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + panic("Unable to extract roles: %s", err) + } + + for _, role := range allRoles { + fmt.Println("%+v\n", role) + } + +Example to Grant a Role to a User + + tenantID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := roles.AddUser(identityClient, tenantID, userID, roleID).ExtractErr() + if err != nil { + panic("Unable to assign role: %s", err) + } + +Example to Remove a Role from a User + + tenantID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := roles.DeleteUser(identityClient, tenantID, userID, roleID).ExtractErr() + if err != nil { + panic("Unable to remove role: %s", err) + } +*/ +package roles diff --git a/openstack/identity/v2/extensions/admin/roles/docs.go b/openstack/identity/v2/extensions/admin/roles/docs.go deleted file mode 100644 index 8954178716..0000000000 --- a/openstack/identity/v2/extensions/admin/roles/docs.go +++ /dev/null @@ -1,16 +0,0 @@ -// Package roles provides functionality to interact with and control roles on -// the API. -// -// A role represents a personality that a user can assume when performing a -// specific set of operations. If a role includes a set of rights and -// privileges, a user assuming that role inherits those rights and privileges. -// -// When a token is generated, the list of roles that user can assume is returned -// back to them. Services that are being called by that user determine how they -// interpret the set of roles a user has and to which operations or resources -// each role grants access. -// -// It is up to individual services such as Compute or Image to assign meaning -// to these roles. As far as the Identity service is concerned, a role is an -// arbitrary name assigned by the user. -package roles diff --git a/openstack/identity/v2/extensions/admin/roles/results.go b/openstack/identity/v2/extensions/admin/roles/results.go index 28de6bb410..94eccd6fed 100644 --- a/openstack/identity/v2/extensions/admin/roles/results.go +++ b/openstack/identity/v2/extensions/admin/roles/results.go @@ -7,16 +7,16 @@ import ( // Role represents an API role resource. type Role struct { - // The unique ID for the role. + // ID is the unique ID for the role. ID string - // The human-readable name of the role. + // Name is the human-readable name of the role. Name string - // The description of the role. + // Description is the description of the role. Description string - // The associated service for this role. + // ServiceID is the associated service for this role. ServiceID string } @@ -25,7 +25,7 @@ type RolePage struct { pagination.SinglePageBase } -// IsEmpty determines whether or not a page of Tenants contains any results. +// IsEmpty determines whether or not a page of Roles contains any results. func (r RolePage) IsEmpty() (bool, error) { users, err := ExtractRoles(r) return len(users) == 0, err @@ -41,7 +41,8 @@ func ExtractRoles(r pagination.Page) ([]Role, error) { } // UserRoleResult represents the result of either an AddUserRole or -// a DeleteUserRole operation. +// a DeleteUserRole operation. Call its ExtractErr method to determine +// if the request succeeded or failed. type UserRoleResult struct { gophercloud.ErrResult } From 1cc45af35c4176a2fd4827279be0004174bf3758 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 18 Aug 2017 21:19:20 -0600 Subject: [PATCH 0046/2296] Identity v2: tenants docs --- openstack/identity/v2/tenants/doc.go | 70 +++++++++++++++++++++-- openstack/identity/v2/tenants/requests.go | 19 ++++-- openstack/identity/v2/tenants/results.go | 17 ++++-- 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/openstack/identity/v2/tenants/doc.go b/openstack/identity/v2/tenants/doc.go index 0c2d49d567..1758efe194 100644 --- a/openstack/identity/v2/tenants/doc.go +++ b/openstack/identity/v2/tenants/doc.go @@ -1,7 +1,65 @@ -// Package tenants provides information and interaction with the -// tenants API resource for the OpenStack Identity service. -// -// See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 -// and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants -// for more information. +/* +Package tenants provides information and interaction with the +tenants API resource for the OpenStack Identity service. + +See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 +and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants +for more information. + +Example to List Tenants + + listOpts := tenants.ListOpts{ + Limit: 2, + } + + allPages, err := tenants.List(identityClient, listOpts).AllPages() + if err != nil { + panic("Unable to list tenants: %s", err) + } + + allTenants, err := tenants.ExtractTenants(allPages) + if err != nil { + panic("Unable to extract tenants: %s", err) + } + + for _, tenant := range allTenants { + fmt.Println("%+v\n", tenant) + } + +Example to Create a Tenant + + createOpts := tenants.CreateOpts{ + Name: "tenant_name", + Description: "this is a tenant", + Enabled: gophercloud.Enabled, + } + + tenant, err := tenants.Create(identityClient, createOpts).Extract() + if err != nil { + panic("Unable to create tenant: %s", err) + } + +Example to Update a Tenant + + tenantID := "e6db6ed6277c461a853458589063b295" + + updateOpts := tenants.UpdateOpts{ + Description: "this is a new description", + Enabled: gophercloud.Disabled, + } + + tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract() + if err != nil { + panic("Unable to update tenant: %s", err) + } + +Example to Delete a Tenant + + tenantID := "e6db6ed6277c461a853458589063b295" + + err := tenants.Delete(identitYClient, tenantID).ExtractErr() + if err != nil { + panic("Unable to delete tenant: %s", err) + } +*/ package tenants diff --git a/openstack/identity/v2/tenants/requests.go b/openstack/identity/v2/tenants/requests.go index b6550ce60d..60f58c8ce3 100644 --- a/openstack/identity/v2/tenants/requests.go +++ b/openstack/identity/v2/tenants/requests.go @@ -9,6 +9,7 @@ import ( type ListOpts struct { // Marker is the ID of the last Tenant on the previous page. Marker string `q:"marker"` + // Limit specifies the page size. Limit int `q:"limit"` } @@ -32,18 +33,22 @@ func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { type CreateOpts struct { // Name is the name of the tenant. Name string `json:"name" required:"true"` + // Description is the description of the tenant. Description string `json:"description,omitempty"` + // Enabled sets the tenant status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. +// CreateOptsBuilder enables extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToTenantCreateMap() (map[string]interface{}, error) } -// ToTenantCreateMap assembles a request body based on the contents of a CreateOpts. +// ToTenantCreateMap assembles a request body based on the contents of +// a CreateOpts. func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "tenant") } @@ -67,17 +72,21 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToTenantUpdateMap() (map[string]interface{}, error) } -// UpdateOpts specifies the base attributes that may be updated on an existing server. +// UpdateOpts specifies the base attributes that may be updated on an existing +// tenant. type UpdateOpts struct { // Name is the name of the tenant. Name string `json:"name,omitempty"` + // Description is the description of the tenant. Description string `json:"description,omitempty"` + // Enabled sets the tenant status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` } @@ -100,7 +109,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// Delete is the operation responsible for permanently deleting an API tenant. +// Delete is the operation responsible for permanently deleting a tenant. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return diff --git a/openstack/identity/v2/tenants/results.go b/openstack/identity/v2/tenants/results.go index 5a319de5cb..bb6c2c6b08 100644 --- a/openstack/identity/v2/tenants/results.go +++ b/openstack/identity/v2/tenants/results.go @@ -43,7 +43,8 @@ func (r TenantPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// ExtractTenants returns a slice of Tenants contained in a single page of results. +// ExtractTenants returns a slice of Tenants contained in a single page of +// results. func ExtractTenants(r pagination.Page) ([]Tenant, error) { var s struct { Tenants []Tenant `json:"tenants"` @@ -56,7 +57,7 @@ type tenantResult struct { gophercloud.Result } -// Extract interprets any tenantResults as a tenant. +// Extract interprets any tenantResults as a Tenant. func (r tenantResult) Extract() (*Tenant, error) { var s struct { Tenant *Tenant `json:"tenant"` @@ -65,22 +66,26 @@ func (r tenantResult) Extract() (*Tenant, error) { return s.Tenant, err } -// GetResult temporarily contains the response from the Get call. +// GetResult is the response from a Get request. Call its Extract method to +// interpret it as a Tenant. type GetResult struct { tenantResult } -// CreateResult temporarily contains the reponse from the Create call. +// CreateResult is the response from a Create request. Call its Extract method +// to interpret it as a Tenant. type CreateResult struct { tenantResult } -// DeleteResult temporarily contains the response from the Delete call. +// DeleteResult is the response from a Get request. Call its ExtractErr method +// to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } -// UpdateResult temporarily contains the response from the Update call. +// UpdateResult is the response from a Update request. Call its Extract method +// to interpret it as a Tenant. type UpdateResult struct { tenantResult } From 6bfea2bc9e3b14326525464d335aaf37e702484e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 18 Aug 2017 21:34:02 -0600 Subject: [PATCH 0047/2296] Identity v2: tokens docs --- openstack/identity/v2/tokens/doc.go | 49 +++++++++++++++-- openstack/identity/v2/tokens/requests.go | 28 +++++----- openstack/identity/v2/tokens/results.go | 67 +++++++++++++++--------- 3 files changed, 102 insertions(+), 42 deletions(-) diff --git a/openstack/identity/v2/tokens/doc.go b/openstack/identity/v2/tokens/doc.go index 31cacc5e17..de8416f622 100644 --- a/openstack/identity/v2/tokens/doc.go +++ b/openstack/identity/v2/tokens/doc.go @@ -1,5 +1,46 @@ -// Package tokens provides information and interaction with the token API -// resource for the OpenStack Identity service. -// For more information, see: -// http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 +/* +Package tokens provides information and interaction with the token API +resource for the OpenStack Identity service. + +For more information, see: +http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 + +Example to Create an Unscoped Token from a Password + + authOpts := gophercloud.AuthOptions{ + Username: "user", + Password: "pass" + } + + token, err := tokens.Create(identityClient, authOpts).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + +Example to Create a Token from a Tenant ID and Password + + authOpts := gophercloud.AuthOptions{ + Username: "user", + Password: "password", + TenantID: "fc394f2ab2df4114bde39905f800dc57" + } + + token, err := tokens.Create(identityClient, authOpts).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + +Example to Create a Token from a Tenant Name and Password + + authOpts := gophercloud.AuthOptions{ + Username: "user", + Password: "password", + TenantName: "tenantname" + } + + token, err := tokens.Create(identityClient, authOpts).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } +*/ package tokens diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 4983031e7f..ab32368cc6 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -2,17 +2,21 @@ package tokens import "github.com/gophercloud/gophercloud" +// PasswordCredentialsV2 represents the required options to authenticate +// with a username and password. type PasswordCredentialsV2 struct { Username string `json:"username" required:"true"` Password string `json:"password" required:"true"` } +// TokenCredentialsV2 represents the required options to authenticate +// with a token. type TokenCredentialsV2 struct { ID string `json:"id,omitempty" required:"true"` } -// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder -// interface. +// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the +// AuthOptionsBuilder interface. type AuthOptionsV2 struct { PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"` @@ -23,15 +27,16 @@ type AuthOptionsV2 struct { TenantID string `json:"tenantId,omitempty"` TenantName string `json:"tenantName,omitempty"` - // TokenCredentials allows users to authenticate (possibly as another user) with an - // authentication token ID. + // TokenCredentials allows users to authenticate (possibly as another user) + // with an authentication token ID. TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"` } -// AuthOptionsBuilder describes any argument that may be passed to the Create call. +// AuthOptionsBuilder allows extensions to add additional parameters to the +// token create request. type AuthOptionsBuilder interface { - // ToTokenCreateMap assembles the Create request body, returning an error if parameters are - // missing or inconsistent. + // ToTokenCreateMap assembles the Create request body, returning an error + // if parameters are missing or inconsistent. ToTokenV2CreateMap() (map[string]interface{}, error) } @@ -47,8 +52,7 @@ type AuthOptions struct { TokenID string } -// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder -// interface in the v2 tokens package +// ToTokenV2CreateMap builds a token request body from the given AuthOptions. func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { v2Opts := AuthOptionsV2{ TenantID: opts.TenantID, @@ -74,9 +78,9 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { } // Create authenticates to the identity service and attempts to acquire a Token. -// If successful, the CreateResult -// 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. +// 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) { b, err := auth.ToTokenV2CreateMap() if err != nil { diff --git a/openstack/identity/v2/tokens/results.go b/openstack/identity/v2/tokens/results.go index 6b36493706..b11326772b 100644 --- a/openstack/identity/v2/tokens/results.go +++ b/openstack/identity/v2/tokens/results.go @@ -7,20 +7,24 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" ) -// Token provides only the most basic information related to an authentication token. +// Token provides only the most basic information related to an authentication +// token. type Token struct { // ID provides the primary means of identifying a user to the OpenStack API. - // OpenStack defines this field as an opaque value, so do not depend on its content. - // It is safe, however, to compare for equality. + // OpenStack defines this field as an opaque value, so do not depend on its + // content. It is safe, however, to compare for equality. ID string - // ExpiresAt provides a timestamp in ISO 8601 format, indicating when the authentication token becomes invalid. - // After this point in time, future API requests made using this authentication token will respond with errors. - // Either the caller will need to reauthenticate manually, or more preferably, the caller should exploit automatic re-authentication. + // ExpiresAt provides a timestamp in ISO 8601 format, indicating when the + // authentication token becomes invalid. After this point in time, future + // API requests made using this authentication token will respond with + // errors. Either the caller will need to reauthenticate manually, or more + // preferably, the caller should exploit automatic re-authentication. // See the AuthOptions structure for more details. ExpiresAt time.Time - // Tenant provides information about the tenant to which this token grants access. + // Tenant provides information about the tenant to which this token grants + // access. Tenant tenants.Tenant } @@ -38,13 +42,17 @@ type User struct { } // Endpoint represents a single API endpoint offered by a service. -// It provides the public and internal URLs, if supported, along with a region specifier, again if provided. +// It provides the public and internal URLs, if supported, along with a region +// specifier, again if provided. +// // The significance of the Region field will depend upon your provider. // -// In addition, the interface offered by the service will have version information associated with it -// through the VersionId, VersionInfo, and VersionList fields, if provided or supported. +// In addition, the interface offered by the service will have version +// information associated with it through the VersionId, VersionInfo, and +// VersionList fields, if provided or supported. // -// In all cases, fields which aren't supported by the provider and service combined will assume a zero-value (""). +// In all cases, fields which aren't supported by the provider and service +// combined will assume a zero-value (""). type Endpoint struct { TenantID string `json:"tenantId"` PublicURL string `json:"publicURL"` @@ -56,38 +64,44 @@ type Endpoint struct { VersionList string `json:"versionList"` } -// CatalogEntry provides a type-safe interface to an Identity API V2 service catalog listing. -// Each class of service, such as cloud DNS or block storage services, will have a single -// CatalogEntry representing it. +// CatalogEntry provides a type-safe interface to an Identity API V2 service +// catalog listing. +// +// Each class of service, such as cloud DNS or block storage services, will have +// a single CatalogEntry representing it. // -// Note: when looking for the desired service, try, whenever possible, to key off the type field. -// Otherwise, you'll tie the representation of the service to a specific provider. +// Note: when looking for the desired service, try, whenever possible, to key +// off the type field. Otherwise, you'll tie the representation of the service +// to a specific provider. type CatalogEntry struct { // Name will contain the provider-specified name for the service. Name string `json:"name"` - // Type will contain a type string if OpenStack defines a type for the service. - // Otherwise, for provider-specific services, the provider may assign their own type strings. + // Type will contain a type string if OpenStack defines a type for the + // service. Otherwise, for provider-specific services, the provider may assign + // their own type strings. Type string `json:"type"` - // Endpoints will let the caller iterate over all the different endpoints that may exist for - // the service. + // Endpoints will let the caller iterate over all the different endpoints that + // may exist for the service. Endpoints []Endpoint `json:"endpoints"` } -// ServiceCatalog provides a view into the service catalog from a previous, successful authentication. +// ServiceCatalog provides a view into the service catalog from a previous, +// successful authentication. type ServiceCatalog struct { Entries []CatalogEntry } -// CreateResult defers the interpretation of a created token. -// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog. +// CreateResult is the response from a Create request. Use ExtractToken() to +// interpret it as a Token, or ExtractServiceCatalog() to interpret it as a +// service catalog. type CreateResult struct { gophercloud.Result } -// GetResult is the deferred response from a Get call, which is the same with a Created token. -// Use ExtractUser() to interpret it as a User. +// GetResult is the deferred response from a Get call, which is the same with a +// Created token. Use ExtractUser() to interpret it as a User. type GetResult struct { CreateResult } @@ -121,7 +135,8 @@ func (r CreateResult) ExtractToken() (*Token, error) { }, nil } -// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token. +// ExtractServiceCatalog returns the ServiceCatalog that was generated along +// with the user's Token. func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { var s struct { Access struct { From 5dd74db50139d432430bee35b2c0dd62568f994f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 18 Aug 2017 21:49:55 -0600 Subject: [PATCH 0048/2296] Identity v2: users docs --- openstack/identity/v2/users/doc.go | 74 +++++++++++++++++++++++++ openstack/identity/v2/users/requests.go | 27 +++++---- openstack/identity/v2/users/results.go | 34 +++++++----- 3 files changed, 110 insertions(+), 25 deletions(-) diff --git a/openstack/identity/v2/users/doc.go b/openstack/identity/v2/users/doc.go index 82abcb9fcc..bc103c23b1 100644 --- a/openstack/identity/v2/users/doc.go +++ b/openstack/identity/v2/users/doc.go @@ -1 +1,75 @@ +/* +Package users provides information and interaction with the users API +resource for the OpenStack Identity Service. + +Example to List Users + + allPages, err := users.List(identityClient).AllPages() + if err != nil { + panic("Unable to list users: %s", err) + } + + allUsers, err := users.ExtractUsers(allPages) + if err != nil { + panic("Unable to extract users: %s", err) + } + + for _, user := range allUsers { + fmt.Println("%+v\n", user) + } + +Example to Create a User + + createOpts := users.CreateOpts{ + Name: "name", + TenantID: "c39e3de9be2d4c779f1dfd6abacc176d", + Enabled: gophercloud.Enabled, + } + + user, err := users.Create(identityClient, createOpts).Extract() + if err != nil { + panic("Unable to create user: %s", err) + } + +Example to Update a User + + userID := "9fe2ff9ee4384b1894a90878d3e92bab" + + updateOpts := users.UpdateOpts{ + Name: "new_name", + Enabled: gophercloud.Disabled, + } + + user, err := users.Update(identityClient, userID, updateOpts).Extract() + if err != nil { + panic("Unable to update user: %s", err) + } + +Example to Delete a User + + userID := "9fe2ff9ee4384b1894a90878d3e92bab" + err := users.Delete(identityClient, userID).ExtractErr() + if err != nil { + panic("Unable to delete user: %s", err) + } + +Example to List a User's Roles + + tenantID := "1d8b6120dcc640fda4fc9194ffc80273" + userID := "c39e3de9be2d4c779f1dfd6abacc176d" + + allPages, err := users.ListRoles(identityClient, tenantID, userID).AllPages() + if err != nil { + panic("Unable to list user's roles: %s", err) + } + + allRoles, err := users.ExtractRoles(allPages) + if err != nil { + panic("Unable to extract roles: %s", err) + } + + for _, role := range allRoles { + fmt.Println("%+v\n", role) + } +*/ package users diff --git a/openstack/identity/v2/users/requests.go b/openstack/identity/v2/users/requests.go index 37fcd38870..0081e8a9f2 100644 --- a/openstack/identity/v2/users/requests.go +++ b/openstack/identity/v2/users/requests.go @@ -20,23 +20,28 @@ type CommonOpts struct { // omit a username, the latter will be set to the former; and vice versa. Name string `json:"name,omitempty"` Username string `json:"username,omitempty"` - // The ID of the tenant to which you want to assign this user. + + // TenantID is the ID of the tenant to which you want to assign this user. TenantID string `json:"tenantId,omitempty"` - // Indicates whether this user is enabled or not. + + // Enabled indicates whether this user is enabled or not. Enabled *bool `json:"enabled,omitempty"` - // The email address of this user. + + // Email is the email address of this user. Email string `json:"email,omitempty"` } // CreateOpts represents the options needed when creating new users. type CreateOpts CommonOpts -// CreateOptsBuilder describes struct types that can be accepted by the Create call. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToUserCreateMap() (map[string]interface{}, error) } -// ToUserCreateMap assembles a request body based on the contents of a CreateOpts. +// ToUserCreateMap assembles a request body based on the contents of a +// CreateOpts. func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) { if opts.Name == "" && opts.Username == "" { err := gophercloud.ErrMissingInput{} @@ -60,18 +65,20 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Get requests details on a single user, either by ID. +// Get requests details on a single user, either by ID or Name. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(ResourceURL(client, id), &r.Body, nil) return } -// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToUserUpdateMap() (map[string]interface{}, error) } -// UpdateOpts specifies the base attributes that may be updated on an existing server. +// UpdateOpts specifies the base attributes that may be updated on an +// existing server. type UpdateOpts CommonOpts // ToUserUpdateMap formats an UpdateOpts structure into a request body. @@ -79,7 +86,7 @@ func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "user") } -// Update is the operation responsible for updating exist users by their UUID. +// Update is the operation responsible for updating exist users by their ID. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToUserUpdateMap() if err != nil { @@ -92,7 +99,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// Delete is the operation responsible for permanently deleting an API user. +// Delete is the operation responsible for permanently deleting a User. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(ResourceURL(client, id), nil) return diff --git a/openstack/identity/v2/users/results.go b/openstack/identity/v2/users/results.go index c49338357d..9f62eee085 100644 --- a/openstack/identity/v2/users/results.go +++ b/openstack/identity/v2/users/results.go @@ -7,32 +7,32 @@ import ( // User represents a user resource that exists on the API. type User struct { - // The UUID for this user. + // ID is the UUID for this user. ID string - // The human name for this user. + // Name is the human name for this user. Name string - // The username for this user. + // Username is the username for this user. Username string - // Indicates whether the user is enabled (true) or disabled (false). + // Enabled indicates whether the user is enabled (true) or disabled (false). Enabled bool - // The email address for this user. + // Email is the email address for this user. Email string - // The ID of the tenant to which this user belongs. + // TenantID is the ID of the tenant to which this user belongs. TenantID string `json:"tenant_id"` } // Role assigns specific responsibilities to users, allowing them to accomplish // certain API operations whilst scoped to a service. type Role struct { - // UUID of the role + // ID is the UUID of the role. ID string - // Name of the role + // Name is the name of the role. Name string } @@ -46,13 +46,13 @@ type RolePage struct { pagination.SinglePageBase } -// IsEmpty determines whether or not a page of Tenants contains any results. +// IsEmpty determines whether or not a page of Users contains any results. func (r UserPage) IsEmpty() (bool, error) { users, err := ExtractUsers(r) return len(users) == 0, err } -// ExtractUsers returns a slice of Tenants contained in a single page of results. +// ExtractUsers returns a slice of Users contained in a single page of results. func ExtractUsers(r pagination.Page) ([]User, error) { var s struct { Users []User `json:"users"` @@ -61,7 +61,7 @@ func ExtractUsers(r pagination.Page) ([]User, error) { return s.Users, err } -// IsEmpty determines whether or not a page of Tenants contains any results. +// IsEmpty determines whether or not a page of Roles contains any results. func (r RolePage) IsEmpty() (bool, error) { users, err := ExtractRoles(r) return len(users) == 0, err @@ -89,22 +89,26 @@ func (r commonResult) Extract() (*User, error) { return s.User, err } -// CreateResult represents the result of a Create operation +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret the result as a User. type CreateResult struct { commonResult } -// GetResult represents the result of a Get operation +// GetResult represents the result of a Get operation. Call its Extract method +// to interpret the result as a User. type GetResult struct { commonResult } -// UpdateResult represents the result of an Update operation +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret the result as a User. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a Delete operation +// DeleteResult represents the result of a Delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { commonResult } From 3af51e56739d4f75191ab31c5b0068ca046ed89f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 19 Aug 2017 19:43:25 -0600 Subject: [PATCH 0049/2296] Identity v3: Docs (#491) * Identity v3: endpoints docs * Identity v3: trusts docs * Identity v3: groups docs * Identity v3: projects docs * Identity v3: roles docs * Identity v3: services docs * Identity v3: tokens docs * Identity v3: users docs --- openstack/identity/v3/endpoints/doc.go | 73 +++++++++++- openstack/identity/v3/endpoints/requests.go | 67 ++++++++--- openstack/identity/v3/endpoints/results.go | 35 ++++-- .../identity/v3/extensions/trusts/doc.go | 26 ++++ .../identity/v3/extensions/trusts/requests.go | 5 + .../identity/v3/extensions/trusts/results.go | 5 + openstack/identity/v3/groups/doc.go | 3 + openstack/identity/v3/projects/doc.go | 58 +++++++++ openstack/identity/v3/projects/requests.go | 8 +- openstack/identity/v3/projects/results.go | 17 ++- openstack/identity/v3/roles/doc.go | 27 ++++- openstack/identity/v3/roles/requests.go | 29 +++-- openstack/identity/v3/roles/results.go | 14 ++- openstack/identity/v3/services/doc.go | 41 ++++++- openstack/identity/v3/services/requests.go | 8 +- openstack/identity/v3/services/results.go | 36 ++++-- openstack/identity/v3/tokens/doc.go | 112 +++++++++++++++++- openstack/identity/v3/tokens/requests.go | 29 +++-- openstack/identity/v3/tokens/results.go | 54 +++++---- openstack/identity/v3/users/doc.go | 83 +++++++++++++ openstack/identity/v3/users/requests.go | 6 +- openstack/identity/v3/users/results.go | 16 ++- 22 files changed, 636 insertions(+), 116 deletions(-) create mode 100644 openstack/identity/v3/extensions/trusts/doc.go create mode 100644 openstack/identity/v3/groups/doc.go create mode 100644 openstack/identity/v3/projects/doc.go create mode 100644 openstack/identity/v3/users/doc.go diff --git a/openstack/identity/v3/endpoints/doc.go b/openstack/identity/v3/endpoints/doc.go index 85163949a8..42850208f7 100644 --- a/openstack/identity/v3/endpoints/doc.go +++ b/openstack/identity/v3/endpoints/doc.go @@ -1,6 +1,69 @@ -// Package endpoints provides information and interaction with the service -// endpoints API resource in the OpenStack Identity service. -// -// For more information, see: -// http://developer.openstack.org/api-ref-identity-v3.html#endpoints-v3 +/* +Package endpoints provides information and interaction with the service +endpoints API resource in the OpenStack Identity service. + +For more information, see: +http://developer.openstack.org/api-ref-identity-v3.html#endpoints-v3 + +Example to List Endpoints + + serviceID := "e629d6e599d9489fb3ae5d9cc12eaea3" + + listOpts := endpoints.ListOpts{ + ServiceID: serviceID, + } + + allPages, err := endpoints.List(identityClient, listOpts).AllPages() + if err != nil { + panic("Unable to list endpoints: %s", err) + } + + allEndpoints, err := endpoints.ExtractEndpoints(allPages) + if err != nil { + panic("Unable to extract endpoints: %s", err) + } + + for _, endpoint := range allEndpoints { + fmt.Println("%+v\n", endpoint) + } + +Example to Create an Endpoint + + serviceID := "e629d6e599d9489fb3ae5d9cc12eaea3" + + createOpts := endpoints.CreateOpts{ + Availability: gophercloud.AvailabilityPublic, + Name: "neutron", + Region: "RegionOne", + URL: "https://localhost:9696", + ServiceID: serviceID, + } + + endpoint, err := endpoints.Create(identityClient, createOpts).Extract() + if err != nil { + panic("Unable to create endpoint: %s", err) + } + + +Example to Update an Endpoint + + endpointID := "ad59deeec5154d1fa0dcff518596f499" + + updateOpts := endpoints.UpdateOpts{ + Region: "RegionTwo", + } + + endpoint, err := endpoints.Update(identityClient, endpointID, updateOpts).Extract() + if err != nil { + panic("Unable to update endpoint: %s", err) + } + +Example to Delete an Endpoint + + endpointID := "ad59deeec5154d1fa0dcff518596f499" + err := endpoints.Delete(identityClient, endpointID).ExtractErr() + if err != nil { + panic("Unable to delete endpoint: %s", err) + } +*/ package endpoints diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index fc4436587c..645632ddc9 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -9,21 +9,33 @@ type CreateOptsBuilder interface { ToEndpointCreateMap() (map[string]interface{}, error) } -// CreateOpts contains the subset of Endpoint attributes that should be used to create an Endpoint. +// CreateOpts contains the subset of Endpoint attributes that should be used +// to create an Endpoint. type CreateOpts struct { + // Availability is the interface type of the Endpoint (admin, internal, + // or public), referenced by the gophercloud.Availability type. Availability gophercloud.Availability `json:"interface" required:"true"` - Name string `json:"name" required:"true"` - Region string `json:"region,omitempty"` - URL string `json:"url" required:"true"` - ServiceID string `json:"service_id" required:"true"` + + // Name is the name of the Endpoint. + Name string `json:"name" required:"true"` + + // Region is the region the Endpoint is located in. + // This field can be omitted or left as a blank string. + Region string `json:"region,omitempty"` + + // URL is the url of the Endpoint. + URL string `json:"url" required:"true"` + + // ServiceID is the ID of the service the Endpoint refers to. + ServiceID string `json:"service_id" required:"true"` } +// ToEndpointCreateMap builds a request body from the Endpoint Create options. func (opts CreateOpts) ToEndpointCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "endpoint") } // Create inserts a new Endpoint into the service catalog. -// Within EndpointOpts, Region may be omitted by being left as "", but all other fields are required. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToEndpointCreateMap() if err != nil { @@ -34,6 +46,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// ListOptsBuilder allows extensions to add parameters to the List request. type ListOptsBuilder interface { ToEndpointListParams() (string, error) } @@ -41,18 +54,28 @@ type ListOptsBuilder interface { // ListOpts allows finer control over the endpoints returned by a List call. // All fields are optional. type ListOpts struct { + // Availability is the interface type of the Endpoint (admin, internal, + // or public), referenced by the gophercloud.Availability type. Availability gophercloud.Availability `q:"interface"` - ServiceID string `q:"service_id"` - Page int `q:"page"` - PerPage int `q:"per_page"` + + // ServiceID is the ID of the service the Endpoint refers to. + ServiceID string `q:"service_id"` + + // Page is a result page to reference in the results. + Page int `q:"page"` + + // PerPage determines how many results per page are returned. + PerPage int `q:"per_page"` } +// ToEndpointListParams builds a list request from the List options. func (opts ListOpts) ToEndpointListParams() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } -// List enumerates endpoints in a paginated collection, optionally filtered by ListOpts criteria. +// List enumerates endpoints in a paginated collection, optionally filtered +// by ListOpts criteria. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { u := listURL(client) if opts != nil { @@ -67,19 +90,33 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } +// UpdateOptsBuilder allows extensions to add parameters to the Update request. type UpdateOptsBuilder interface { ToEndpointUpdateMap() (map[string]interface{}, error) } -// UpdateOpts contains the subset of Endpoint attributes that should be used to update an Endpoint. +// UpdateOpts contains the subset of Endpoint attributes that should be used to +// update an Endpoint. type UpdateOpts struct { + // Availability is the interface type of the Endpoint (admin, internal, + // or public), referenced by the gophercloud.Availability type. Availability gophercloud.Availability `json:"interface,omitempty"` - Name string `json:"name,omitempty"` - Region string `json:"region,omitempty"` - URL string `json:"url,omitempty"` - ServiceID string `json:"service_id,omitempty"` + + // Name is the name of the Endpoint. + Name string `json:"name,omitempty"` + + // Region is the region the Endpoint is located in. + // This field can be omitted or left as a blank string. + Region string `json:"region,omitempty"` + + // URL is the url of the Endpoint. + URL string `json:"url,omitempty"` + + // ServiceID is the ID of the service the Endpoint refers to. + ServiceID string `json:"service_id,omitempty"` } +// ToEndpointUpdateMap builds an update request body from the Update options. func (opts UpdateOpts) ToEndpointUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "endpoint") } diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index f769881670..6e54f6b413 100644 --- a/openstack/identity/v3/endpoints/results.go +++ b/openstack/identity/v3/endpoints/results.go @@ -9,8 +9,8 @@ type commonResult struct { gophercloud.Result } -// Extract interprets a GetResult, CreateResult or UpdateResult as a concrete Endpoint. -// An error is returned if the original call or the extraction failed. +// Extract interprets a GetResult, CreateResult or UpdateResult as a concrete +// Endpoint. An error is returned if the original call or the extraction failed. func (r commonResult) Extract() (*Endpoint, error) { var s struct { Endpoint *Endpoint `json:"endpoint"` @@ -19,29 +19,44 @@ func (r commonResult) Extract() (*Endpoint, error) { return s.Endpoint, err } -// CreateResult is the deferred result of a Create call. +// CreateResult is the response from a Create operation. Call its Extract +// method to interpret it as an Endpoint. type CreateResult struct { commonResult } -// UpdateResult is the deferred result of an Update call. +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as an Endpoint. type UpdateResult struct { commonResult } -// DeleteResult is the deferred result of an Delete call. +// 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 string `json:"id"` + // 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"` - Name string `json:"name"` - Region string `json:"region"` - ServiceID string `json:"service_id"` - URL string `json:"url"` + + // Name is the name of the Endpoint. + Name string `json:"name"` + + // 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. diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go new file mode 100644 index 0000000000..d0a6b1ac7f --- /dev/null +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -0,0 +1,26 @@ +/* +Package trusts enables management of OpenStack Identity Trusts. + +Example to Create a Token with Username, Password, and Trust ID + + var trustToken struct { + tokens.Token + trusts.TokenExt + } + + authOptions := tokens.AuthOptions{ + UserID: "username", + Password: "password", + } + + createOpts := trusts.AuthOptsExt{ + AuthOptionsBuilder: authOptions, + TrustID: "de0945a", + } + + err := tokens.Create(identityClient, createOpts).ExtractInto(&trustToken) + if err != nil { + panic("Unable to create token: %s", err) + } +*/ +package trusts diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index 999dd73deb..438fba61de 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -2,15 +2,20 @@ package trusts import "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" +// AuthOptsExt extends the base Identity v3 tokens AuthOpts with a TrustID. type AuthOptsExt struct { tokens.AuthOptionsBuilder + + // TrustID is the ID of the trust. TrustID string `json:"id"` } +// ToTokenV3CreateMap builds a create request body from the AuthOpts. func (opts AuthOptsExt) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { return opts.AuthOptionsBuilder.ToTokenV3CreateMap(scope) } +// ToTokenV3ScopeMap builds a scope from AuthOpts. func (opts AuthOptsExt) ToTokenV3ScopeMap() (map[string]interface{}, error) { b, err := opts.AuthOptionsBuilder.ToTokenV3ScopeMap() if err != nil { diff --git a/openstack/identity/v3/extensions/trusts/results.go b/openstack/identity/v3/extensions/trusts/results.go index bdd8e8479e..e6912e612c 100644 --- a/openstack/identity/v3/extensions/trusts/results.go +++ b/openstack/identity/v3/extensions/trusts/results.go @@ -1,13 +1,17 @@ package trusts +// TrusteeUser represents the trusted user ID of a trust. type TrusteeUser struct { ID string `json:"id"` } +// TrustorUser represents the trusting user ID of a trust. type TrustorUser struct { ID string `json:"id"` } +// Trust represents a delegated authorization request between two +// identities. type Trust struct { ID string `json:"id"` Impersonation bool `json:"impersonation"` @@ -17,6 +21,7 @@ type Trust struct { RedelegationCount int `json:"redelegation_count"` } +// TokenExt represents an extension of the base token result. type TokenExt struct { Trust Trust `json:"OS-TRUST:trust"` } diff --git a/openstack/identity/v3/groups/doc.go b/openstack/identity/v3/groups/doc.go new file mode 100644 index 0000000000..dfbf3dec77 --- /dev/null +++ b/openstack/identity/v3/groups/doc.go @@ -0,0 +1,3 @@ +// Package groups retrieves and manages groups in the OpenStack Identity +// Service. +package groups diff --git a/openstack/identity/v3/projects/doc.go b/openstack/identity/v3/projects/doc.go new file mode 100644 index 0000000000..1dee534c2c --- /dev/null +++ b/openstack/identity/v3/projects/doc.go @@ -0,0 +1,58 @@ +/* +Package projects manages and retrieves Projects in the OpenStack Identity +Service. + +Example to List Projects + + listOpts := projects.ListOpts{ + Enabled: gophercloud.Enabled, + } + + allPages, err := projects.List(identityClient, listOpts).AllPages() + if err != nil { + panic("Unable to list projects: %s", err) + } + + allProjects, err := projects.ExtractProjects(allPages) + if err != nil { + panic("Unable to extract projects: %s", err) + } + + for _, project := range allProjects { + fmt.Println("%+v\n", project) + } + +Example to Create a Project + + createOpts := projects.CreateOpts{ + Name: "project_name", + Description: "Project Description" + } + + project, err := projects.Create(identityClient, createOpts).Extract() + if err != nil { + panic("Unable to create project: %s", err) + } + +Example to Update a Project + + projectID := "966b3c7d36a24facaf20b7e458bf2192" + + updateOpts := projects.UpdateOpts{ + Enabled: gophercloud.Disabled, + } + + project, err := projects.Update(identityClient, projectID, updateOpts).Extract() + if err != nil { + panic("Unable to update project: %s", err) + } + +Example to Delete a Project + + projectID := "966b3c7d36a24facaf20b7e458bf2192" + err := projects.Delete(identityClient, projectID).ExtractErr() + if err != nil { + panic("Unable to delete project: %s", err) + } +*/ +package projects diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index af21de39e1..368b7321ba 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -11,7 +11,7 @@ type ListOptsBuilder interface { ToProjectListQuery() (string, error) } -// ListOpts allows you to query the List method. +// ListOpts enables filtering of a list request. type ListOpts struct { // DomainID filters the response by a domain ID. DomainID string `q:"domain_id"` @@ -36,7 +36,7 @@ func (opts ListOpts) ToProjectListQuery() (string, error) { return q.String(), err } -// List enumerats the Projects to which the current token has access. +// List enumerates the Projects to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { @@ -63,7 +63,7 @@ type CreateOptsBuilder interface { ToProjectCreateMap() (map[string]interface{}, error) } -// CreateOpts allows you to modify the details included in the Create request. +// CreateOpts represents parameters used to create a project. type CreateOpts struct { // DomainID is the ID this project will belong under. DomainID string `json:"domain_id,omitempty"` @@ -112,7 +112,7 @@ type UpdateOptsBuilder interface { ToProjectUpdateMap() (map[string]interface{}, error) } -// UpdateOpts allows you to modify the details included in the Update request. +// UpdateOpts represents parameters to update a project. type UpdateOpts struct { // DomainID is the ID this project will belong under. DomainID string `json:"domain_id,omitempty"` diff --git a/openstack/identity/v3/projects/results.go b/openstack/identity/v3/projects/results.go index a441e7f0f1..a13fa7f2ae 100644 --- a/openstack/identity/v3/projects/results.go +++ b/openstack/identity/v3/projects/results.go @@ -9,27 +9,31 @@ type projectResult struct { gophercloud.Result } -// GetResult temporarily contains the response from the Get call. +// GetResult is the result of a Get request. Call its Extract method to +// interpret it as a Project. type GetResult struct { projectResult } -// CreateResult temporarily contains the reponse from the Create call. +// CreateResult is the result of a Create request. Call its Extract method to +// interpret it as a Project. type CreateResult struct { projectResult } -// DeleteResult temporarily contains the response from the Delete call. +// 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 } -// UpdateResult temporarily contains the response from the Update call. +// UpdateResult is the result of an Update request. Call its Extract method to +// interpret it as a Project. type UpdateResult struct { projectResult } -// Project is a base unit of ownership. +// Project represents an OpenStack Identity Project. type Project struct { // IsDomain indicates whether the project is a domain. IsDomain bool `json:"is_domain"` @@ -79,7 +83,8 @@ func (r ProjectPage) NextPageURL() (string, error) { return s.Links.Next, err } -// ExtractProjects returns a slice of Projects contained in a single page of results. +// ExtractProjects returns a slice of Projects contained in a single page of +// results. func ExtractProjects(r pagination.Page) ([]Project, error) { var s struct { Projects []Project `json:"projects"` diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index bdbc674d65..ce62ce4edb 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -1,3 +1,26 @@ -// Package roles provides information and interaction with the roles API -// resource for the OpenStack Identity service. +/* +Package roles provides information and interaction with the roles API +resource for the OpenStack Identity service. + +Example to List Role Assignments + + listOpts := roles.ListAssignmentsOpts{ + UserID: "97061de2ed0647b28a393c36ab584f39", + ScopeProjectID: "9df1a02f5eb2416a9781e8b0c022d3ae", + } + + allPages, err := roles.ListAssignments(identityClient, listOpts).AllPages() + if err != nil { + panic("Unable to list role assignments: %s", err) + } + + allRoles, err := roles.ExtractRoleAssignments(allPages) + if err != nil { + panic("Unable to extract roles: %s", err) + } + + for _, role := range allRoles { + fmt.Println("%+v\n", role) + } +*/ package roles diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index de65c51a78..ea8f207129 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -12,17 +12,28 @@ type ListAssignmentsOptsBuilder interface { } // ListAssignmentsOpts allows you to query the ListAssignments method. -// Specify one of or a combination of GroupId, RoleId, ScopeDomainId, ScopeProjectId, -// and/or UserId to search for roles assigned to corresponding entities. -// Effective lists effective assignments at the user, project, and domain level, -// allowing for the effects of group membership. +// Specify one of or a combination of GroupId, RoleId, ScopeDomainId, +// ScopeProjectId, and/or UserId to search for roles assigned to corresponding +// entities. type ListAssignmentsOpts struct { - GroupID string `q:"group.id"` - RoleID string `q:"role.id"` - ScopeDomainID string `q:"scope.domain.id"` + // GroupID is the group ID to query. + GroupID string `q:"group.id"` + + // RoleID is the specific role to query assignments to. + RoleID string `q:"role.id"` + + // ScopeDomainID filters the results by the given domain ID. + ScopeDomainID string `q:"scope.domain.id"` + + // ScopeProjectID filters the results by the given Project ID. ScopeProjectID string `q:"scope.project.id"` - UserID string `q:"user.id"` - Effective *bool `q:"effective"` + + // UserID filterst he results by the given User ID. + UserID string `q:"user.id"` + + // Effective lists effective assignments at the user, project, and domain + // level, allowing for the effects of group membership. + Effective *bool `q:"effective"` } // ToRolesListAssignmentsQuery formats a ListAssignmentsOpts into a query string. diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index e8a3aa9a90..a7b43f44a4 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -10,27 +10,33 @@ type RoleAssignment struct { Group Group `json:"group,omitempty"` } +// Role represents a Role in an assignment. type Role struct { ID string `json:"id,omitempty"` } +// Scope represents a scope in a Role assignment. type Scope struct { Domain Domain `json:"domain,omitempty"` Project Project `json:"project,omitempty"` } +// Domain represents a domain in a role assignment scope. type Domain struct { ID string `json:"id,omitempty"` } +// Project represents a project in a role assignment scope. type Project struct { ID string `json:"id,omitempty"` } +// User represents a user in a role assignment scope. type User struct { ID string `json:"id,omitempty"` } +// Group represents a group in a role assignment scope. type Group struct { ID string `json:"id,omitempty"` } @@ -40,13 +46,14 @@ type RoleAssignmentPage struct { pagination.LinkedPageBase } -// IsEmpty returns true if the page contains no results. +// IsEmpty returns true if the RoleAssignmentPage contains no results. func (r RoleAssignmentPage) IsEmpty() (bool, error) { roleAssignments, err := ExtractRoleAssignments(r) return len(roleAssignments) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to +// the next page of results. func (r RoleAssignmentPage) NextPageURL() (string, error) { var s struct { Links struct { @@ -57,7 +64,8 @@ func (r RoleAssignmentPage) NextPageURL() (string, error) { return s.Links.Next, err } -// ExtractRoleAssignments extracts a slice of RoleAssignments from a Collection acquired from List. +// ExtractRoleAssignments extracts a slice of RoleAssignments from a Collection +// acquired from List. func ExtractRoleAssignments(r pagination.Page) ([]RoleAssignment, error) { var s struct { RoleAssignments []RoleAssignment `json:"role_assignments"` diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index fa56411856..28d097c4d1 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -1,3 +1,40 @@ -// Package services provides information and interaction with the services API -// resource for the OpenStack Identity service. +/* +Package services provides information and interaction with the services API +resource for the OpenStack Identity service. + +Example to List Services + + listOpts := services.ListOpts{ + ServiceType: "compute", + } + + allPages, err := services.List(identityClient, listOpts).AllPages() + if err != nil { + panic("Unable to list services: %s", err) + } + + allServices, err := services.ExtractServices(allPages) + if err != nil { + panic("Unable to extract services: %s", err) + } + + for _, service := range allServices { + fmt.Printf("%+v\n", service) + } + +Example to Create a Service + + service, err := services.Create(identityClient, "compute").Extract() + if err != nil { + panic("Unable to create service: %s", err) + } + +Example to Delete a Service + + serviceID := "3c7bbe9a6ecb453ca1789586291380ed" + err := services.Delete(identityClient, serviceID).ExtractErr() + if err != nil { + panic("Unable to delete service: %s", err) + } +*/ package services diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index bb7bb04761..a4401c5ad0 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -12,17 +12,20 @@ func Create(client *gophercloud.ServiceClient, serviceType string) (r CreateResu return } +// ListOptsBuilder enables extensions to add additional parameters to the List +// request. type ListOptsBuilder interface { ToServiceListMap() (string, error) } -// ListOpts allows you to query the List method. +// ListOpts provides options for filtering the List results. type ListOpts struct { ServiceType string `q:"type"` PerPage int `q:"perPage"` Page int `q:"page"` } +// ToServiceListMap builds a list query from the list options. func (opts ListOpts) ToServiceListMap() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err @@ -57,7 +60,8 @@ func Update(client *gophercloud.ServiceClient, serviceID string, serviceType str } // Delete removes an existing service. -// It either deletes all associated endpoints, or fails until all endpoints are deleted. +// It either deletes all associated endpoints, or fails until all endpoints +// are deleted. func Delete(client *gophercloud.ServiceClient, serviceID string) (r DeleteResult) { _, r.Err = client.Delete(serviceURL(client, serviceID), nil) return diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index 3942f5e4d1..2c4fa76755 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -9,8 +9,8 @@ type commonResult struct { gophercloud.Result } -// Extract interprets a GetResult, CreateResult or UpdateResult as a concrete Service. -// An error is returned if the original call or the extraction failed. +// Extract interprets a GetResult, CreateResult or UpdateResult as a concrete +// Service. An error is returned if the original call or the extraction failed. func (r commonResult) Extract() (*Service, error) { var s struct { Service *Service `json:"service"` @@ -19,32 +19,43 @@ func (r commonResult) Extract() (*Service, error) { return s.Service, err } -// CreateResult is the deferred result of a Create call. +// CreateResult is the response from a Create request. Call its Extract method +// to interpret it as a Service. type CreateResult struct { commonResult } -// GetResult is the deferred result of a Get call. +// GetResult is the response from a Get request. Call its Extract method +// to interpret it as a Service. type GetResult struct { commonResult } -// UpdateResult is the deferred result of an Update call. +// UpdateResult is the response from an Update request. Call its Extract method +// to interpret it as a Service. type UpdateResult struct { commonResult } -// DeleteResult is the deferred result of an Delete call. +// DeleteResult is the response from a Delete request. Call its ExtractErr +// method to interpret it as a Service. type DeleteResult struct { gophercloud.ErrResult } -// Service is the result of a list or information query. +// Service represents an OpenStack Service. type Service struct { + // Description is a description of the service. Description string `json:"description"` - ID string `json:"id"` - Name string `json:"name"` - Type string `json:"type"` + + // ID is the unique ID of the service. + ID string `json:"id"` + + // Name is the name of the service. + Name string `json:"name"` + + // Type is the type of the service. + Type string `json:"type"` } // ServicePage is a single page of Service results. @@ -52,13 +63,14 @@ type ServicePage struct { pagination.LinkedPageBase } -// IsEmpty returns true if the page contains no results. +// IsEmpty returns true if the ServicePage contains no results. func (p ServicePage) IsEmpty() (bool, error) { services, err := ExtractServices(p) return len(services) == 0, err } -// ExtractServices extracts a slice of Services from a Collection acquired from List. +// ExtractServices extracts a slice of Services from a Collection acquired +// from List. func ExtractServices(r pagination.Page) ([]Service, error) { var s struct { Services []Service `json:"services"` diff --git a/openstack/identity/v3/tokens/doc.go b/openstack/identity/v3/tokens/doc.go index 76ff5f4738..5553b9f96e 100644 --- a/openstack/identity/v3/tokens/doc.go +++ b/openstack/identity/v3/tokens/doc.go @@ -1,6 +1,108 @@ -// Package tokens provides information and interaction with the token API -// resource for the OpenStack Identity service. -// -// For more information, see: -// http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3 +/* +Package tokens provides information and interaction with the token API +resource for the OpenStack Identity service. + +For more information, see: +http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3 + +Example to Create a Token From a Username and Password + + authOptions := tokens.AuthOptions{ + UserID: "username", + Password: "password", + } + + token, err := tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + +Example to Create a Token From a Username, Password, and Domain + + authOptions := tokens.AuthOptions{ + UserID: "username", + Password: "password", + DomainID: "default", + } + + token, err := tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + + authOptions = tokens.AuthOptions{ + UserID: "username", + Password: "password", + DomainName: "default", + } + + token, err = tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + +Example to Create a Token From a Token + + authOptions := tokens.AuthOptions{ + TokenID: "token_id", + } + + token, err := tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + +Example to Create a Token from a Username and Password with Project ID Scope + + scope := tokens.Scope{ + ProjectID: "0fe36e73809d46aeae6705c39077b1b3", + } + + authOptions := tokens.AuthOptions{ + Scope: &scope, + UserID: "username", + Password: "password", + } + + token, err = tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + +Example to Create a Token from a Username and Password with Domain ID Scope + + scope := tokens.Scope{ + DomainID: "default", + } + + authOptions := tokens.AuthOptions{ + Scope: &scope, + UserID: "username", + Password: "password", + } + + token, err = tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + +Example to Create a Token from a Username and Password with Project Name Scope + + scope := tokens.Scope{ + ProjectName: "project_name", + DomainID: "default", + } + + authOptions := tokens.AuthOptions{ + Scope: &scope, + UserID: "username", + Password: "password", + } + + token, err = tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic("Unable to create token: %s", err) + } + +*/ package tokens diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index a80fde15c4..ca35851e4a 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -10,20 +10,22 @@ type Scope struct { DomainName string } -// AuthOptionsBuilder describes any argument that may be passed to the Create call. +// AuthOptionsBuilder provides the ability for extensions to add additional +// parameters to AuthOptions. Extensions must satisfy all required methods. type AuthOptionsBuilder interface { - // ToTokenV3CreateMap assembles the Create request body, returning an error if parameters are - // missing or inconsistent. + // ToTokenV3CreateMap assembles the Create request body, returning an error + // if parameters are missing or inconsistent. ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) ToTokenV3ScopeMap() (map[string]interface{}, error) CanReauth() bool } +// AuthOptions represents options for authenticating a user. type AuthOptions struct { // IdentityEndpoint specifies the HTTP endpoint that is required to work with - // the Identity API of the appropriate version. While it's ultimately needed by - // all of the identity services, it will often be populated by a provider-level - // function. + // the Identity API of the appropriate version. While it's ultimately needed + // by all of the identity services, it will often be populated by a + // provider-level function. IdentityEndpoint string `json:"-"` // Username is required if using Identity V2 API. Consult with your provider's @@ -39,11 +41,11 @@ type AuthOptions struct { DomainID string `json:"-"` DomainName string `json:"name,omitempty"` - // AllowReauth should be set to true if you grant permission for Gophercloud to - // cache your credentials in memory, and to allow Gophercloud to attempt to - // re-authenticate automatically if/when your token expires. If you set it to - // false, it will not cache these settings, but re-authentication will not be - // possible. This setting defaults to false. + // AllowReauth should be set to true if you grant permission for Gophercloud + // to cache your credentials in memory, and to allow Gophercloud to attempt + // to re-authenticate automatically if/when your token expires. If you set + // it to false, it will not cache these settings, but re-authentication will + // not be possible. This setting defaults to false. AllowReauth bool `json:"-"` // TokenID allows users to authenticate (possibly as another user) with an @@ -53,6 +55,7 @@ type AuthOptions struct { Scope Scope `json:"-"` } +// ToTokenV3CreateMap builds a request body from AuthOptions. func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { gophercloudAuthOpts := gophercloud.AuthOptions{ Username: opts.Username, @@ -67,6 +70,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s return gophercloudAuthOpts.ToTokenV3CreateMap(scope) } +// ToTokenV3CreateMap builds a scope request body from AuthOptions. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { if opts.Scope.ProjectName != "" { // ProjectName provided: either DomainID or DomainName must also be supplied. @@ -146,7 +150,8 @@ func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[ } } -// Create authenticates and either generates a new token, or changes the Scope of an existing token. +// Create authenticates and either generates a new token, or changes the Scope +// of an existing token. func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { scope, err := opts.ToTokenV3ScopeMap() if err != nil { diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index 7c306e83fe..6e78d1cbdb 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -17,37 +17,45 @@ type Endpoint struct { URL string `json:"url"` } -// CatalogEntry provides a type-safe interface to an Identity API V3 service catalog listing. -// Each class of service, such as cloud DNS or block storage services, could have multiple -// CatalogEntry representing it (one by interface type, e.g public, admin or internal). +// CatalogEntry provides a type-safe interface to an Identity API V3 service +// catalog listing. Each class of service, such as cloud DNS or block storage +// services, could have multiple CatalogEntry representing it (one by interface +// type, e.g public, admin or internal). // -// Note: when looking for the desired service, try, whenever possible, to key off the type field. -// Otherwise, you'll tie the representation of the service to a specific provider. +// Note: when looking for the desired service, try, whenever possible, to key +// off the type field. Otherwise, you'll tie the representation of the service +// to a specific provider. type CatalogEntry struct { // Service ID ID string `json:"id"` + // Name will contain the provider-specified name for the service. Name string `json:"name"` - // Type will contain a type string if OpenStack defines a type for the service. - // Otherwise, for provider-specific services, the provider may assign their own type strings. + + // Type will contain a type string if OpenStack defines a type for the + // service. Otherwise, for provider-specific services, the provider may + // assign their own type strings. Type string `json:"type"` - // Endpoints will let the caller iterate over all the different endpoints that may exist for - // the service. + + // Endpoints will let the caller iterate over all the different endpoints that + // may exist for the service. Endpoints []Endpoint `json:"endpoints"` } -// ServiceCatalog provides a view into the service catalog from a previous, successful authentication. +// ServiceCatalog provides a view into the service catalog from a previous, +// successful authentication. type ServiceCatalog struct { Entries []CatalogEntry `json:"catalog"` } -// Domain provides information about the domain to which this token grants access. +// Domain provides information about the domain to which this token grants +// access. type Domain struct { ID string `json:"id"` Name string `json:"name"` } -// User represents a user resource that exists on the API. +// User represents a user resource that exists in the Identity Service. type User struct { Domain Domain `json:"domain"` ID string `json:"id"` @@ -67,7 +75,8 @@ type Project struct { Name string `json:"name"` } -// commonResult is the deferred result of a Create or a Get call. +// commonResult is the response from a request. A commonResult has various +// methods which can be used to extract different details about the result. type commonResult struct { gophercloud.Result } @@ -92,7 +101,8 @@ func (r commonResult) ExtractToken() (*Token, error) { return &s, err } -// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token. +// ExtractServiceCatalog returns the ServiceCatalog that was generated along +// with the user's Token. func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) { var s ServiceCatalog err := r.ExtractInto(&s) @@ -126,27 +136,31 @@ func (r commonResult) ExtractProject() (*Project, error) { return s.Project, err } -// CreateResult defers the interpretation of a created token. -// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog. +// CreateResult is the response from a Create request. Use ExtractToken() +// to interpret it as a Token, or ExtractServiceCatalog() to interpret it +// as a service catalog. type CreateResult struct { commonResult } -// GetResult is the deferred response from a Get call. +// GetResult is the response from a Get request. Use ExtractToken() +// to interpret it as a Token, or ExtractServiceCatalog() to interpret it +// as a service catalog. type GetResult struct { commonResult } -// RevokeResult is the deferred response from a Revoke call. +// RevokeResult is response from a Revoke request. type RevokeResult struct { commonResult } -// Token is a string that grants a user access to a controlled set of services in an OpenStack provider. -// Each Token is valid for a set length of time. +// Token is a string that grants a user access to a controlled set of services +// in an OpenStack provider. Each Token is valid for a set length of time. type Token struct { // ID is the issued token. ID string `json:"id"` + // ExpiresAt is the timestamp at which this token will no longer be accepted. ExpiresAt time.Time `json:"expires_at"` } diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go new file mode 100644 index 0000000000..1bbe2e745b --- /dev/null +++ b/openstack/identity/v3/users/doc.go @@ -0,0 +1,83 @@ +/* +Package users manages and retrieves Users in the OpenStack Identity Service. + +Example to List Users + + listOpts := users.ListOpts{ + DomainID: "default", + } + + allPages, err := users.List(identityClient, listOpts).AllPages() + if err != nil { + panic("Unable to list users: %s", err) + } + + allUsers, err := users.ExtractUsers(allPages) + if err != nil { + panic("Unable to extract users: %s", err) + } + + for _, user := range allUsers { + fmt.Printf("%+v\n", user) + } + +Example to Create a User + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + + createOpts := users.CreateOpts{ + Name: "username", + DomainID: "default", + DefaultProjectID: projectID, + Enabled: gophercloud.Enabled, + Password: "supersecret", + Extra: map[string]interface{}{ + "email": "username@example.com", + } + } + + user, err := users.Create(identityClient, createOpts).Extract() + if err != nil { + panic("Unable to create user: %s", err) + } + +Example to Update a User + + userID := "0fe36e73809d46aeae6705c39077b1b3" + + updateOpts := users.UpdateOpts{ + Enabled: gophercloud.Disabled, + } + + user, err := users.Update(identityClient, userID, updateOpts).Extract() + if err != nil { + panic("Unable to update user: %s", err) + } + +Example to Delete a User + + userID := "0fe36e73809d46aeae6705c39077b1b3" + err := users.Delete(identityClient, userID).ExtractErr() + if err != nil { + panic("Unable to delete user: %s", err) + } + +Example to List Groups a User Belongs To + + userID := "0fe36e73809d46aeae6705c39077b1b3" + + allPages, err := users.ListGroups(identityClient, userID).AllPages() + if err != nil { + panic("Unable to retrieve groups: %s", err) + } + + allGroups, err := users.ExtractGroups(allPages) + if err != nil { + panic("Unable to extract groups: %s", err) + } + + for _, group := range allGroups { + fmt.Printf("%+v\n", group) + } +*/ +package users diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index eea19197c0..d0dc26f64d 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -24,7 +24,7 @@ type ListOptsBuilder interface { ToUserListQuery() (string, error) } -// ListOpts allows you to query the List method. +// ListOpts provides options to filter the List results. type ListOpts struct { // DomainID filters the response by a domain ID. DomainID string `q:"domain_id"` @@ -81,7 +81,7 @@ type CreateOptsBuilder interface { ToUserCreateMap() (map[string]interface{}, error) } -// CreateOpts implements CreateOptsBuilder +// CreateOpts provides options used to create a user. type CreateOpts struct { // Name is the name of the new user. Name string `json:"name" required:"true"` @@ -145,7 +145,7 @@ type UpdateOptsBuilder interface { ToUserUpdateMap() (map[string]interface{}, error) } -// UpdateOpts implements UpdateOptsBuilder +// UpdateOpts provides options for updating a user account. type UpdateOpts struct { // Name is the name of the new user. Name string `json:"name,omitempty"` diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 414bbf80df..5716edc304 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// User is a base unit of ownership. +// User represents a User in the OpenStack Identity Service. type User struct { // DefaultProjectID is the ID of the default project of the user. DefaultProjectID string `json:"default_project_id"` @@ -80,22 +80,26 @@ type userResult struct { gophercloud.Result } -// GetResult temporarily contains the response from the Get call. +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a User. type GetResult struct { userResult } -// CreateResult temporarily contains the response from the Create call. +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a User. type CreateResult struct { userResult } -// UpdateResult temporarily contains the response from the Update call. +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a User. type UpdateResult struct { userResult } -// DeleteResult temporarily contains the response from the Delete call. +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to interpret it as a User. type DeleteResult struct { gophercloud.ErrResult } @@ -105,7 +109,7 @@ type UserPage struct { pagination.LinkedPageBase } -// IsEmpty determines whether or not a page of Users contains any results. +// IsEmpty determines whether or not a UserPage contains any results. func (r UserPage) IsEmpty() (bool, error) { users, err := ExtractUsers(r) return len(users) == 0, err From 4554fa696ddae83f69616b88fbd0c994b634aa65 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 19 Aug 2017 19:48:30 -0600 Subject: [PATCH 0050/2296] Docs: Replace Println with Printf --- openstack/blockstorage/extensions/volumeactions/doc.go | 2 +- openstack/compute/v2/extensions/attachinterfaces/doc.go | 2 +- openstack/compute/v2/extensions/defsecrules/doc.go | 4 ++-- openstack/compute/v2/extensions/floatingips/doc.go | 2 +- openstack/compute/v2/extensions/hypervisors/doc.go | 2 +- openstack/compute/v2/extensions/keypairs/doc.go | 4 ++-- openstack/compute/v2/extensions/limits/doc.go | 2 +- openstack/compute/v2/extensions/networks/doc.go | 2 +- openstack/compute/v2/extensions/quotasets/doc.go | 4 ++-- openstack/compute/v2/extensions/tenantnetworks/doc.go | 2 +- openstack/compute/v2/flavors/doc.go | 2 +- openstack/compute/v2/images/doc.go | 2 +- openstack/compute/v2/servers/doc.go | 2 +- openstack/dns/v2/recordsets/doc.go | 2 +- openstack/dns/v2/zones/doc.go | 2 +- openstack/identity/v2/extensions/admin/roles/doc.go | 2 +- openstack/identity/v2/tenants/doc.go | 2 +- openstack/identity/v2/users/doc.go | 4 ++-- openstack/identity/v3/endpoints/doc.go | 2 +- openstack/identity/v3/projects/doc.go | 2 +- openstack/identity/v3/roles/doc.go | 2 +- 21 files changed, 25 insertions(+), 25 deletions(-) diff --git a/openstack/blockstorage/extensions/volumeactions/doc.go b/openstack/blockstorage/extensions/volumeactions/doc.go index 11b46c1bbf..c520817976 100644 --- a/openstack/blockstorage/extensions/volumeactions/doc.go +++ b/openstack/blockstorage/extensions/volumeactions/doc.go @@ -67,7 +67,7 @@ Example of Initializing a Volume Connection panic("Unable to initialize connection: %s", err) } - fmt.Println("%+v\n", connectionInfo["data"]) + fmt.Printf("%+v\n", connectionInfo["data"]) terminateOpts := &volumeactions.InitializeConnectionOpts{ IP: "127.0.0.1", diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go index fb431e33e9..bb8d3bd4ec 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -16,7 +16,7 @@ Example of Listing a Server's Interfaces } for _, interface := range allInterfaces { - fmt.Println("%+v\n", interface) + fmt.Printf("%+v\n", interface) } */ package attachinterfaces diff --git a/openstack/compute/v2/extensions/defsecrules/doc.go b/openstack/compute/v2/extensions/defsecrules/doc.go index da41271a8e..e8b9dc4942 100644 --- a/openstack/compute/v2/extensions/defsecrules/doc.go +++ b/openstack/compute/v2/extensions/defsecrules/doc.go @@ -20,8 +20,8 @@ Example of Listing Default Security Group Rules panic("Unable to extract default security group rules: %s", err) } - for _, df := allDefaultRules { - fmt.Println("%+v\n", df) + for _, df := range allDefaultRules { + fmt.Printf("%+v\n", df) } Example of Retrieving a Default Security Group Rule diff --git a/openstack/compute/v2/extensions/floatingips/doc.go b/openstack/compute/v2/extensions/floatingips/doc.go index 9c9a8ee24e..2e9038623a 100644 --- a/openstack/compute/v2/extensions/floatingips/doc.go +++ b/openstack/compute/v2/extensions/floatingips/doc.go @@ -22,7 +22,7 @@ Example to List Floating IPs } for _, fip := range allFloatingIPs { - fmt.Println("%+v\n", fip) + fmt.Printf("%+v\n", fip) } Example to Create a Floating IP diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index 001e0a5dc5..c5ad87df03 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -15,7 +15,7 @@ Example of Retrieving Details of All Hypervisors } for _, hypervisor := range allHypervisors { - fmt.Println("%+v\n", hypervisor) + fmt.Printf("%+v\n", hypervisor) } */ package hypervisors diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go index 2314777584..bf335def50 100644 --- a/openstack/compute/v2/extensions/keypairs/doc.go +++ b/openstack/compute/v2/extensions/keypairs/doc.go @@ -15,7 +15,7 @@ Example to List Key Pairs } for _, kp := range allKeyPairs { - fmt.Println("%+v\n", kp) + fmt.Printf("%+v\n", kp) } Example to Create a Key Pair @@ -29,7 +29,7 @@ Example to Create a Key Pair panic("Unable to create key pair: %s", err) } - fmt.Println("%+v", keypair) + fmt.Printf("%+v", keypair) Example to Import a Key Pair diff --git a/openstack/compute/v2/extensions/limits/doc.go b/openstack/compute/v2/extensions/limits/doc.go index cea9688a16..a2e1d82e6c 100644 --- a/openstack/compute/v2/extensions/limits/doc.go +++ b/openstack/compute/v2/extensions/limits/doc.go @@ -12,6 +12,6 @@ Example to Retrieve Limits for a Tenant panic("Unable to retrieve limits: %s", err) } - fmt.Println("%+v\n", limits) + fmt.Printf("%+v\n", limits) */ package limits diff --git a/openstack/compute/v2/extensions/networks/doc.go b/openstack/compute/v2/extensions/networks/doc.go index b73048c2f5..6fd25cdd9f 100644 --- a/openstack/compute/v2/extensions/networks/doc.go +++ b/openstack/compute/v2/extensions/networks/doc.go @@ -18,7 +18,7 @@ Example to List Networks } for _, network := range allNetworks { - fmt.Println("%+v\n", network) + fmt.Printf("%+v\n", network) } */ package networks diff --git a/openstack/compute/v2/extensions/quotasets/doc.go b/openstack/compute/v2/extensions/quotasets/doc.go index c4a7a6c800..788662df25 100644 --- a/openstack/compute/v2/extensions/quotasets/doc.go +++ b/openstack/compute/v2/extensions/quotasets/doc.go @@ -8,7 +8,7 @@ Example to Get a Quota Set panic("Unable to retrieve quotaset: %s", err) } - fmt.Println("%+v\n", quotaset) + fmt.Printf("%+v\n", quotaset) Example to Update a Quota Set @@ -22,6 +22,6 @@ Example to Update a Quota Set panic("Unable to update quotaset: %s", err) } - fmt.Println("%+v\n", quotaset) + fmt.Printf("%+v\n", quotaset) */ package quotasets diff --git a/openstack/compute/v2/extensions/tenantnetworks/doc.go b/openstack/compute/v2/extensions/tenantnetworks/doc.go index 34ceb47151..aeb76c17fe 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/doc.go +++ b/openstack/compute/v2/extensions/tenantnetworks/doc.go @@ -20,7 +20,7 @@ Example to List Networks Available to a Tenant } for _, network := range allNetworks { - fmt.Println("%+v\n", network) + fmt.Printf("%+v\n", network) } */ package tenantnetworks diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 92b81b3e70..0511e220ba 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -23,7 +23,7 @@ Example to List Flavors } for _, flavor := range allFlavors { - fmt.Println("%+v\n", flavor) + fmt.Printf("%+v\n", flavor) } Example to Create a Flavor diff --git a/openstack/compute/v2/images/doc.go b/openstack/compute/v2/images/doc.go index 3f8d657d57..e953a622de 100644 --- a/openstack/compute/v2/images/doc.go +++ b/openstack/compute/v2/images/doc.go @@ -26,7 +26,7 @@ Example to List Images } for _, image := range allImages { - fmt.Println("%+v\n", image) + fmt.Printf("%+v\n", image) } */ package images diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index 087bd860f3..845790f453 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -22,7 +22,7 @@ Example to List Servers } for _, server := range allServers { - fmt.Println("%+v\n", server) + fmt.Printf("%+v\n", server) } Example to Create a Server diff --git a/openstack/dns/v2/recordsets/doc.go b/openstack/dns/v2/recordsets/doc.go index 4954ed5398..77f264246a 100644 --- a/openstack/dns/v2/recordsets/doc.go +++ b/openstack/dns/v2/recordsets/doc.go @@ -21,7 +21,7 @@ Example to List RecordSets by Zone } for _, rr := range allRRs { - fmt.Println("%+v\n", rr) + fmt.Printf("%+v\n", rr) } Example to Create a RecordSet diff --git a/openstack/dns/v2/zones/doc.go b/openstack/dns/v2/zones/doc.go index 7fd241e00b..1dbc7afd0f 100644 --- a/openstack/dns/v2/zones/doc.go +++ b/openstack/dns/v2/zones/doc.go @@ -19,7 +19,7 @@ Example to List Zones } for _, zone := range allZones { - fmt.Println("%+v\n", zone) + fmt.Printf("%+v\n", zone) } Example to Create a Zone diff --git a/openstack/identity/v2/extensions/admin/roles/doc.go b/openstack/identity/v2/extensions/admin/roles/doc.go index 119bf1395a..2b7a6d9310 100644 --- a/openstack/identity/v2/extensions/admin/roles/doc.go +++ b/openstack/identity/v2/extensions/admin/roles/doc.go @@ -28,7 +28,7 @@ Example to List Roles } for _, role := range allRoles { - fmt.Println("%+v\n", role) + fmt.Printf("%+v\n", role) } Example to Grant a Role to a User diff --git a/openstack/identity/v2/tenants/doc.go b/openstack/identity/v2/tenants/doc.go index 1758efe194..fe20b7962a 100644 --- a/openstack/identity/v2/tenants/doc.go +++ b/openstack/identity/v2/tenants/doc.go @@ -23,7 +23,7 @@ Example to List Tenants } for _, tenant := range allTenants { - fmt.Println("%+v\n", tenant) + fmt.Printf("%+v\n", tenant) } Example to Create a Tenant diff --git a/openstack/identity/v2/users/doc.go b/openstack/identity/v2/users/doc.go index bc103c23b1..b393b99c5e 100644 --- a/openstack/identity/v2/users/doc.go +++ b/openstack/identity/v2/users/doc.go @@ -15,7 +15,7 @@ Example to List Users } for _, user := range allUsers { - fmt.Println("%+v\n", user) + fmt.Printf("%+v\n", user) } Example to Create a User @@ -69,7 +69,7 @@ Example to List a User's Roles } for _, role := range allRoles { - fmt.Println("%+v\n", role) + fmt.Printf("%+v\n", role) } */ package users diff --git a/openstack/identity/v3/endpoints/doc.go b/openstack/identity/v3/endpoints/doc.go index 42850208f7..23502ab798 100644 --- a/openstack/identity/v3/endpoints/doc.go +++ b/openstack/identity/v3/endpoints/doc.go @@ -24,7 +24,7 @@ Example to List Endpoints } for _, endpoint := range allEndpoints { - fmt.Println("%+v\n", endpoint) + fmt.Printf("%+v\n", endpoint) } Example to Create an Endpoint diff --git a/openstack/identity/v3/projects/doc.go b/openstack/identity/v3/projects/doc.go index 1dee534c2c..35f14a6354 100644 --- a/openstack/identity/v3/projects/doc.go +++ b/openstack/identity/v3/projects/doc.go @@ -19,7 +19,7 @@ Example to List Projects } for _, project := range allProjects { - fmt.Println("%+v\n", project) + fmt.Printf("%+v\n", project) } Example to Create a Project diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index ce62ce4edb..1df59af55a 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -20,7 +20,7 @@ Example to List Role Assignments } for _, role := range allRoles { - fmt.Println("%+v\n", role) + fmt.Printf("%+v\n", role) } */ package roles From dd4772f852da860142e6de0377fc55eafed69e0c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 19 Aug 2017 20:22:37 -0600 Subject: [PATCH 0051/2296] Docs: Fix panic syntax --- .../extensions/schedulerstats/doc.go | 4 ++-- .../extensions/volumeactions/doc.go | 12 +++++------ .../v2/extensions/attachinterfaces/doc.go | 4 ++-- .../v2/extensions/bootfromvolume/doc.go | 8 ++++---- .../compute/v2/extensions/defsecrules/doc.go | 10 +++++----- .../compute/v2/extensions/floatingips/doc.go | 12 +++++------ .../compute/v2/extensions/hypervisors/doc.go | 4 ++-- .../compute/v2/extensions/keypairs/doc.go | 12 +++++------ openstack/compute/v2/extensions/limits/doc.go | 2 +- .../compute/v2/extensions/networks/doc.go | 4 ++-- .../compute/v2/extensions/pauseunpause/doc.go | 4 ++-- .../compute/v2/extensions/quotasets/doc.go | 4 ++-- .../v2/extensions/schedulerhints/doc.go | 6 +++--- .../compute/v2/extensions/startstop/doc.go | 4 ++-- .../v2/extensions/suspendresume/doc.go | 4 ++-- .../v2/extensions/tenantnetworks/doc.go | 4 ++-- .../compute/v2/extensions/volumeattach/doc.go | 4 ++-- openstack/compute/v2/flavors/doc.go | 6 +++--- openstack/compute/v2/images/doc.go | 4 ++-- openstack/compute/v2/servers/doc.go | 20 +++++++++---------- openstack/dns/v2/recordsets/doc.go | 8 ++++---- openstack/dns/v2/zones/doc.go | 8 ++++---- .../identity/v2/extensions/admin/roles/doc.go | 8 ++++---- openstack/identity/v2/tenants/doc.go | 10 +++++----- openstack/identity/v2/tokens/doc.go | 6 +++--- openstack/identity/v2/users/doc.go | 14 ++++++------- openstack/identity/v3/endpoints/doc.go | 10 +++++----- .../identity/v3/extensions/trusts/doc.go | 2 +- openstack/identity/v3/projects/doc.go | 10 +++++----- openstack/identity/v3/roles/doc.go | 4 ++-- openstack/identity/v3/services/doc.go | 8 ++++---- openstack/identity/v3/tokens/doc.go | 14 ++++++------- openstack/identity/v3/users/doc.go | 14 ++++++------- 33 files changed, 124 insertions(+), 124 deletions(-) diff --git a/openstack/blockstorage/extensions/schedulerstats/doc.go b/openstack/blockstorage/extensions/schedulerstats/doc.go index ea3a4d3ddc..b0a2c8ff30 100644 --- a/openstack/blockstorage/extensions/schedulerstats/doc.go +++ b/openstack/blockstorage/extensions/schedulerstats/doc.go @@ -8,12 +8,12 @@ and utilisation. Example: allPages, err := schedulerstats.List(client, listOpts).AllPages() if err != nil { - panic("Unable to query schedulerstats: %s", err) + panic(err) } allStats, err := schedulerstats.ExtractStoragePools(allPages) if err != nil { - panic("Unable to extract pools: %s", err) + panic(err) } for _, stat := range allStats { diff --git a/openstack/blockstorage/extensions/volumeactions/doc.go b/openstack/blockstorage/extensions/volumeactions/doc.go index 11b46c1bbf..46724420fd 100644 --- a/openstack/blockstorage/extensions/volumeactions/doc.go +++ b/openstack/blockstorage/extensions/volumeactions/doc.go @@ -13,7 +13,7 @@ Example of Attaching a Volume to an Instance err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr() if err != nil { - panic("Unable to attach volume: %s", err) + panic(err) } detachOpts := volumeactions.DetachOpts{ @@ -22,7 +22,7 @@ Example of Attaching a Volume to an Instance err = volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr() if err != nil { - panic("Unable to detach volume: %s", err) + panic(err) } @@ -35,7 +35,7 @@ Example of Creating an Image from a Volume volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() if err != nil { - panic("Unable to upload image: %s", err) + panic(err) } fmt.Printf("%+v\n", volumeImage) @@ -48,7 +48,7 @@ Example of Extending a Volume's Size err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() if err != nil { - panic("Unable to resize volume: %s", err) + panic(err) } Example of Initializing a Volume Connection @@ -64,7 +64,7 @@ Example of Initializing a Volume Connection connectionInfo, err := volumeactions.InitializeConnection(client, volume.ID, connectOpts).Extract() if err != nil { - panic("Unable to initialize connection: %s", err) + panic(err) } fmt.Println("%+v\n", connectionInfo["data"]) @@ -80,7 +80,7 @@ Example of Initializing a Volume Connection err = volumeactions.TerminateConnection(client, volume.ID, terminateOpts).ExtractErr() if err != nil { - panic("Unable to terminate connection: %s", err) + panic(err) } */ package volumeactions diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go index fb431e33e9..50420878bd 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -7,12 +7,12 @@ Example of Listing a Server's Interfaces serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" allPages, err := attachinterfaces.List(computeClient, serverID).AllPages() if err != nil { - panic("Unable to retrieve interfaces: %s", err) + panic(err) } allInterfaces, err := attachinterfaces.ExtractInterfaces(allPages) if err != nil { - panic("Unable to extract interfaces: %s", err) + panic(err) } for _, interface := range allInterfaces { diff --git a/openstack/compute/v2/extensions/bootfromvolume/doc.go b/openstack/compute/v2/extensions/bootfromvolume/doc.go index 5437092a70..d291325e0a 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/doc.go +++ b/openstack/compute/v2/extensions/bootfromvolume/doc.go @@ -39,7 +39,7 @@ a server without using block device mappings. server, err := bootfromvolume.Create(client, createOpts).Extract() if err != nil { - panic("Unable to create server: %s", err) + panic(err) } Example of Creating a Server From a New Volume @@ -69,7 +69,7 @@ server will use this volume as its root disk. server, err := bootfromvolume.Create(client, createOpts).Extract() if err != nil { - panic("Unable to create server: %s", err) + panic(err) } Example of Creating a Server From an Existing Volume @@ -97,7 +97,7 @@ This example will create a server with an existing volume as its root disk. server, err := bootfromvolume.Create(client, createOpts).Extract() if err != nil { - panic("Unable to create server: %s", err) + panic(err) } Example of Creating a Server with Multiple Ephemeral Disks @@ -146,7 +146,7 @@ ephemeral disks must have an index of -1. server, err := bootfromvolume.Create(client, createOpts).Extract() if err != nil { - panic("Unable to create server: %s", err) + panic(err) } */ package bootfromvolume diff --git a/openstack/compute/v2/extensions/defsecrules/doc.go b/openstack/compute/v2/extensions/defsecrules/doc.go index da41271a8e..218c059b61 100644 --- a/openstack/compute/v2/extensions/defsecrules/doc.go +++ b/openstack/compute/v2/extensions/defsecrules/doc.go @@ -12,12 +12,12 @@ Example of Listing Default Security Group Rules allPages, err := defsecrules.List(computeClient).AllPages() if err != nil { - panic("Unable to list default security group rules: %s", err) + panic(err) } allDefaultRules, err := defsecrules.ExtractDefaultRules(allPages) if err != nil { - panic("Unable to extract default security group rules: %s", err) + panic(err) } for _, df := allDefaultRules { @@ -28,7 +28,7 @@ Example of Retrieving a Default Security Group Rule rule, err := defsecrules.Get(computeClient, "rule-id").Extract() if err != nil { - panic("Unable to retrieve default security group rule: %s", err + panic(err) } Example of Creating a Default Security Group Rule @@ -42,14 +42,14 @@ Example of Creating a Default Security Group Rule rule, err := defsecrules.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create default security group rule: %s", err) + panic(err) } Example of Deleting a Default Security Group Rule err := defsecrules.Delete(computeClient, "rule-id").ExtractErr() if err != nil { - panic("Unable to delete default security group rule: %s", err) + panic(err) } */ package defsecrules diff --git a/openstack/compute/v2/extensions/floatingips/doc.go b/openstack/compute/v2/extensions/floatingips/doc.go index 9c9a8ee24e..b2e1937ad4 100644 --- a/openstack/compute/v2/extensions/floatingips/doc.go +++ b/openstack/compute/v2/extensions/floatingips/doc.go @@ -13,12 +13,12 @@ Example to List Floating IPs allPages, err := floatingips.List(computeClient).AllPages() if err != nil { - panic("Unable to retrieve Floating IPs: %s", err) + panic(err) } allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages) if err != nil { - panic("Unable to extract Floating IPs: %s", err) + panic(err) } for _, fip := range allFloatingIPs { @@ -33,14 +33,14 @@ Example to Create a Floating IP fip, err := floatingips.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create Floating IP: %s", err) + panic(err) } Example to Delete a Floating IP err := floatingips.Delete(computeClient, "floatingip-id").ExtractErr() if err != nil { - panic("Unable to delete Floating IP: %s", err) + panic(err) } Example to Associate a Floating IP With a Server @@ -51,7 +51,7 @@ Example to Associate a Floating IP With a Server err := floatingips.AssociateInstance(computeClient, "server-id", associateOpts).ExtractErr() if err != nil { - panic("Unable to associate Floating IP: %s", err) + panic(err) } Example to Disassociate a Floating IP From a Server @@ -62,7 +62,7 @@ Example to Disassociate a Floating IP From a Server err := floatingips.DisassociateInstance(computeClient, "server-id", disassociateOpts).ExtractErr() if err != nil { - panic("Unable to disassocaite Floating IP: %s", err) + panic(err) } */ package floatingips diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index 001e0a5dc5..9d45e4d11b 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -6,12 +6,12 @@ Example of Retrieving Details of All Hypervisors allPages, err := hypervisors.List(computeClient).AllPages() if err != nil { - panic("Unable to retrieve hypervisors: %s", err) + panic(err) } allHypervisors, err := hypervisors.ExtractHypervisors(allPages) if err != nil { - panic("Unable to extract hypervisors: %s", err) + panic(err) } for _, hypervisor := range allHypervisors { diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go index 2314777584..138ccf02e9 100644 --- a/openstack/compute/v2/extensions/keypairs/doc.go +++ b/openstack/compute/v2/extensions/keypairs/doc.go @@ -6,12 +6,12 @@ Example to List Key Pairs allPages, err := keypairs.List(computeClient).AllPages() if err != nil { - panic("Unable to list key pairs: %s", err) + panic(err) } allKeyPairs, err := keypairs.ExtractKeyPairs(allPages) if err != nil { - panic("Unable to extract key pairs: %s", err) + panic(err) } for _, kp := range allKeyPairs { @@ -26,7 +26,7 @@ Example to Create a Key Pair keypair, err := keypairs.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create key pair: %s", err) + panic(err) } fmt.Println("%+v", keypair) @@ -40,14 +40,14 @@ Example to Import a Key Pair keypair, err := keypairs.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create key pair: %s", err) + panic(err) } Example to Delete a Key Pair err := keypairs.Delete(computeClient, "keypair-name").ExtractErr() if err != nil { - panic("Unable to delete key pair: %s", err) + panic(err) } Example to Create a Server With a Key Pair @@ -65,7 +65,7 @@ Example to Create a Server With a Key Pair server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { - panic("Could not create server: %s", err) + panic(err) } */ package keypairs diff --git a/openstack/compute/v2/extensions/limits/doc.go b/openstack/compute/v2/extensions/limits/doc.go index cea9688a16..875a988aec 100644 --- a/openstack/compute/v2/extensions/limits/doc.go +++ b/openstack/compute/v2/extensions/limits/doc.go @@ -9,7 +9,7 @@ Example to Retrieve Limits for a Tenant limits, err := limits.Get(computeClient, getOpts).Extract() if err != nil { - panic("Unable to retrieve limits: %s", err) + panic(err) } fmt.Println("%+v\n", limits) diff --git a/openstack/compute/v2/extensions/networks/doc.go b/openstack/compute/v2/extensions/networks/doc.go index b73048c2f5..e4f929e0a7 100644 --- a/openstack/compute/v2/extensions/networks/doc.go +++ b/openstack/compute/v2/extensions/networks/doc.go @@ -9,12 +9,12 @@ Example to List Networks allPages, err := networks.List(computeClient).AllPages() if err != nil { - panic("Unable to retrieve networks: %s", err) + panic(err) } allNetworks, err := networks.ExtractNetworks(allPages) if err != nil { - panic("Unable to extract networks: %s", err) + panic(err) } for _, network := range allNetworks { diff --git a/openstack/compute/v2/extensions/pauseunpause/doc.go b/openstack/compute/v2/extensions/pauseunpause/doc.go index 5dfe02c09a..b260ca0767 100644 --- a/openstack/compute/v2/extensions/pauseunpause/doc.go +++ b/openstack/compute/v2/extensions/pauseunpause/doc.go @@ -7,12 +7,12 @@ Example to Pause and Unpause a Server serverID := "32c8baf7-1cdb-4cc2-bc31-c3a55b89f56b" err := pauseunpause.Pause(computeClient, serverID).ExtractErr() if err != nil { - panic("Unable to pause server: %s", err) + panic(err) } err = pauseunpause.Unpause(computeClient, serverID).ExtractErr() if err != nil { - panic("Unable to unpause server: %s", err) + panic(err) } */ package pauseunpause diff --git a/openstack/compute/v2/extensions/quotasets/doc.go b/openstack/compute/v2/extensions/quotasets/doc.go index c4a7a6c800..815f4dd98b 100644 --- a/openstack/compute/v2/extensions/quotasets/doc.go +++ b/openstack/compute/v2/extensions/quotasets/doc.go @@ -5,7 +5,7 @@ Example to Get a Quota Set quotaset, err := quotasets.Get(computeClient, "tenant-id").Extract() if err != nil { - panic("Unable to retrieve quotaset: %s", err) + panic(err) } fmt.Println("%+v\n", quotaset) @@ -19,7 +19,7 @@ Example to Update a Quota Set quotaset, err := quotasets.Update(computeClient, "tenant-id", updateOpts).Extract() if err != nil { - panic("Unable to update quotaset: %s", err) + panic(err) } fmt.Println("%+v\n", quotaset) diff --git a/openstack/compute/v2/extensions/schedulerhints/doc.go b/openstack/compute/v2/extensions/schedulerhints/doc.go index a26119ef77..2d9d3acdec 100644 --- a/openstack/compute/v2/extensions/schedulerhints/doc.go +++ b/openstack/compute/v2/extensions/schedulerhints/doc.go @@ -22,7 +22,7 @@ Example to Add a Server to a Server Group server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create server: %s", err) + panic(err) } Example to Place Server B on a Different Host than Server A @@ -46,7 +46,7 @@ Example to Place Server B on a Different Host than Server A server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create server: %s", err) + panic(err) } Example to Place Server B on the Same Host as Server A @@ -70,7 +70,7 @@ Example to Place Server B on the Same Host as Server A server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create server: %s", err) + panic(err) } */ package schedulerhints diff --git a/openstack/compute/v2/extensions/startstop/doc.go b/openstack/compute/v2/extensions/startstop/doc.go index 824190f118..ab97edb776 100644 --- a/openstack/compute/v2/extensions/startstop/doc.go +++ b/openstack/compute/v2/extensions/startstop/doc.go @@ -8,12 +8,12 @@ Example to Stop and Start a Server err := startstop.Stop(computeClient, serverID).ExtractErr() if err != nil { - panic("Unable to stop server: %s", err) + panic(err) } err := startstop.Start(computeClient, serverID).ExtractErr() if err != nil { - panic("Unable to start server: %s", err) + panic(err) } */ package startstop diff --git a/openstack/compute/v2/extensions/suspendresume/doc.go b/openstack/compute/v2/extensions/suspendresume/doc.go index b337108b38..9851000e8a 100644 --- a/openstack/compute/v2/extensions/suspendresume/doc.go +++ b/openstack/compute/v2/extensions/suspendresume/doc.go @@ -8,12 +8,12 @@ Example to Suspend and Resume a Server err := suspendresume.Suspend(computeClient, serverID).ExtractErr() if err != nil { - panic("Unable to suspend server: %s", err) + panic(err) } err := suspendresume.Resume(computeClient, serverID).ExtractErr() if err != nil { - panic("Unable to resume server: %s", err) + panic(err) } */ package suspendresume diff --git a/openstack/compute/v2/extensions/tenantnetworks/doc.go b/openstack/compute/v2/extensions/tenantnetworks/doc.go index 34ceb47151..fefee95554 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/doc.go +++ b/openstack/compute/v2/extensions/tenantnetworks/doc.go @@ -11,12 +11,12 @@ Example to List Networks Available to a Tenant allPages, err := tenantnetworks.List(computeClient).AllPages() if err != nil { - panic("Unable to retrieve networks: %s", err) + panic(err) } allNetworks, err := tenantnetworks.ExtractNetworks(allPages) if err != nil { - panic("Unable to extract networks: %s", err) + panic(err) } for _, network := range allNetworks { diff --git a/openstack/compute/v2/extensions/volumeattach/doc.go b/openstack/compute/v2/extensions/volumeattach/doc.go index bc11f8c5ac..484eb20000 100644 --- a/openstack/compute/v2/extensions/volumeattach/doc.go +++ b/openstack/compute/v2/extensions/volumeattach/doc.go @@ -14,7 +14,7 @@ Example to Attach a Volume result, err := volumeattach.Create(computeClient, serverID, createOpts).Extract() if err != nil { - panic("Unable to attach volume: %s", err) + panic(err) } Example to Detach a Volume @@ -24,7 +24,7 @@ Example to Detach a Volume err := volumeattach.Delete(computeClient, serverID, attachmentID).ExtractErr() if err != nil { - panic("Unable to detach volume: %s", err) + panic(err) } */ package volumeattach diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 92b81b3e70..b41bd65c24 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -14,12 +14,12 @@ Example to List Flavors allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages() if err != nil { - panic("Unable to list flavors: %s", err) + panic(err) } allFlavors, err := flavors.ExtractFlavors(allPages) if err != nil { - panic("Unable to extract flavors: %s", err) + panic(err) } for _, flavor := range allFlavors { @@ -39,7 +39,7 @@ Example to Create a Flavor flavor, err := flavors.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create flavor: %s", err) + panic(err) } */ package flavors diff --git a/openstack/compute/v2/images/doc.go b/openstack/compute/v2/images/doc.go index 3f8d657d57..85d16016f7 100644 --- a/openstack/compute/v2/images/doc.go +++ b/openstack/compute/v2/images/doc.go @@ -17,12 +17,12 @@ Example to List Images allPages, err := images.ListDetail(computeClient, listOpts).AllPages() if err != nil { - panic("Unable to list images: %s", err) + panic(err) } allImages, err := images.ExtractImages(allPages) if err != nil { - panic("Unable to extract images: %s", err) + panic(err) } for _, image := range allImages { diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index 087bd860f3..5443e43a8d 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -13,12 +13,12 @@ Example to List Servers allPages, err := servers.List(computeClient, listOpts).AllPages() if err != nil { - panic("Unable to list servers: %s", err) + panic(err) } allServers, err := servers.ExtractServers(allPages) if err != nil { - panic("Unable to extract servers: %s", err) + panic(err) } for _, server := range allServers { @@ -35,7 +35,7 @@ Example to Create a Server server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { - panic("Unable to create server: %s", err) + panic(err) } Example to Delete a Server @@ -43,7 +43,7 @@ Example to Delete a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" err := servers.Delete(computeClient, serverID).ExtractErr() if err != nil { - panic("Error deleting server: %s", err) + panic(err) } Example to Force Delete a Server @@ -51,7 +51,7 @@ Example to Force Delete a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" err := servers.ForceDelete(computeClient, serverID).ExtractErr() if err != nil { - panic("Error deleting server: %s", err) + panic(err) } Example to Reboot a Server @@ -64,7 +64,7 @@ Example to Reboot a Server err := servers.Reboot(computeClient, serverID, rebootOpts).ExtractErr() if err != nil { - panic("Unable to reboot server: %s", err) + panic(err) } Example to Rebuild a Server @@ -78,7 +78,7 @@ Example to Rebuild a Server server, err := servers.Rebuilt(computeClient, serverID, rebuildOpts).Extract() if err != nil { - panic("Unable to rebuild server: %s", err) + panic(err) } Example to Resize a Server @@ -91,12 +91,12 @@ Example to Resize a Server err := servers.Resize(computeClient, serverID, resizeOpts).ExtractErr() if err != nil { - panic("Unable to resize server: %s", err) + panic(err) } err = servers.ConfirmResize(computeClient, serverID).ExtractErr() if err != nil { - panic("Unable to confirm resize: %s", err) + panic(err) } Example to Snapshot a Server @@ -109,7 +109,7 @@ Example to Snapshot a Server image, err := servers.CreateImage(computeClient, serverID, snapshotOpts).ExtractImageID() if err != nil { - panic("Unable to snapshot server: %s", err) + panic(err) } */ package servers diff --git a/openstack/dns/v2/recordsets/doc.go b/openstack/dns/v2/recordsets/doc.go index 4954ed5398..179b504aba 100644 --- a/openstack/dns/v2/recordsets/doc.go +++ b/openstack/dns/v2/recordsets/doc.go @@ -12,12 +12,12 @@ Example to List RecordSets by Zone allPages, err := recordsets.ListByZone(dnsClient, zoneID, listOpts).AllPages() if err != nil { - panic("Unable to list recordsets: %s", err) + panic(err) } allRRs, err := recordsets.ExtractRecordSets(allPages() if err != nil { - panic("Unable to extract recordsets: %s", err) + panic(err) } for _, rr := range allRRs { @@ -38,7 +38,7 @@ Example to Create a RecordSet rr, err := recordsets.Create(dnsClient, zoneID, createOpts).Extract() if err != nil { - panic("Unable to create recordset: %s", err) + panic(err) } Example to Delete a RecordSet @@ -48,7 +48,7 @@ Example to Delete a RecordSet err := recordsets.Delete(dnsClient, zoneID, recordsetID).ExtractErr() if err != nil { - panic("Unable to delete recordset: %s", err) + panic(err) } */ package recordsets diff --git a/openstack/dns/v2/zones/doc.go b/openstack/dns/v2/zones/doc.go index 7fd241e00b..aa9412a2d1 100644 --- a/openstack/dns/v2/zones/doc.go +++ b/openstack/dns/v2/zones/doc.go @@ -10,12 +10,12 @@ Example to List Zones allPages, err := zones.List(dnsClient, listOpts).AllPages() if err != nil { - panic("Unable to retrieve zones: %s", err) + panic(err) } allZones, err := zones.ExtractZones(allPages) if err != nil { - panic("Unable to extract zones: %s", err) + panic(err) } for _, zone := range allZones { @@ -34,7 +34,7 @@ Example to Create a Zone zone, err := zones.Create(dnsClient, createOpts).Extract() if err != nil { - panic("Unable to create zone: %s", err) + panic(err) } Example to Delete a Zone @@ -42,7 +42,7 @@ Example to Delete a Zone zoneID := "99d10f68-5623-4491-91a0-6daafa32b60e" err := zones.Delete(dnsClient, zoneID).ExtractErr() if err != nil { - panic("Unable to delete zone: %s", err) + panic(err) } */ package zones diff --git a/openstack/identity/v2/extensions/admin/roles/doc.go b/openstack/identity/v2/extensions/admin/roles/doc.go index 119bf1395a..fb7265e22a 100644 --- a/openstack/identity/v2/extensions/admin/roles/doc.go +++ b/openstack/identity/v2/extensions/admin/roles/doc.go @@ -19,12 +19,12 @@ Example to List Roles allPages, err := roles.List(identityClient).AllPages() if err != nil { - panic("Unable to list roles: %s", err) + panic(err) } allRoles, err := roles.ExtractRoles(allPages) if err != nil { - panic("Unable to extract roles: %s", err) + panic(err) } for _, role := range allRoles { @@ -39,7 +39,7 @@ Example to Grant a Role to a User err := roles.AddUser(identityClient, tenantID, userID, roleID).ExtractErr() if err != nil { - panic("Unable to assign role: %s", err) + panic(err) } Example to Remove a Role from a User @@ -50,7 +50,7 @@ Example to Remove a Role from a User err := roles.DeleteUser(identityClient, tenantID, userID, roleID).ExtractErr() if err != nil { - panic("Unable to remove role: %s", err) + panic(err) } */ package roles diff --git a/openstack/identity/v2/tenants/doc.go b/openstack/identity/v2/tenants/doc.go index 1758efe194..62fb1c9dbe 100644 --- a/openstack/identity/v2/tenants/doc.go +++ b/openstack/identity/v2/tenants/doc.go @@ -14,12 +14,12 @@ Example to List Tenants allPages, err := tenants.List(identityClient, listOpts).AllPages() if err != nil { - panic("Unable to list tenants: %s", err) + panic(err) } allTenants, err := tenants.ExtractTenants(allPages) if err != nil { - panic("Unable to extract tenants: %s", err) + panic(err) } for _, tenant := range allTenants { @@ -36,7 +36,7 @@ Example to Create a Tenant tenant, err := tenants.Create(identityClient, createOpts).Extract() if err != nil { - panic("Unable to create tenant: %s", err) + panic(err) } Example to Update a Tenant @@ -50,7 +50,7 @@ Example to Update a Tenant tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract() if err != nil { - panic("Unable to update tenant: %s", err) + panic(err) } Example to Delete a Tenant @@ -59,7 +59,7 @@ Example to Delete a Tenant err := tenants.Delete(identitYClient, tenantID).ExtractErr() if err != nil { - panic("Unable to delete tenant: %s", err) + panic(err) } */ package tenants diff --git a/openstack/identity/v2/tokens/doc.go b/openstack/identity/v2/tokens/doc.go index de8416f622..5375eea872 100644 --- a/openstack/identity/v2/tokens/doc.go +++ b/openstack/identity/v2/tokens/doc.go @@ -14,7 +14,7 @@ Example to Create an Unscoped Token from a Password token, err := tokens.Create(identityClient, authOpts).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } Example to Create a Token from a Tenant ID and Password @@ -27,7 +27,7 @@ Example to Create a Token from a Tenant ID and Password token, err := tokens.Create(identityClient, authOpts).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } Example to Create a Token from a Tenant Name and Password @@ -40,7 +40,7 @@ Example to Create a Token from a Tenant Name and Password token, err := tokens.Create(identityClient, authOpts).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } */ package tokens diff --git a/openstack/identity/v2/users/doc.go b/openstack/identity/v2/users/doc.go index bc103c23b1..d3df6eabb3 100644 --- a/openstack/identity/v2/users/doc.go +++ b/openstack/identity/v2/users/doc.go @@ -6,12 +6,12 @@ Example to List Users allPages, err := users.List(identityClient).AllPages() if err != nil { - panic("Unable to list users: %s", err) + panic(err) } allUsers, err := users.ExtractUsers(allPages) if err != nil { - panic("Unable to extract users: %s", err) + panic(err) } for _, user := range allUsers { @@ -28,7 +28,7 @@ Example to Create a User user, err := users.Create(identityClient, createOpts).Extract() if err != nil { - panic("Unable to create user: %s", err) + panic(err) } Example to Update a User @@ -42,7 +42,7 @@ Example to Update a User user, err := users.Update(identityClient, userID, updateOpts).Extract() if err != nil { - panic("Unable to update user: %s", err) + panic(err) } Example to Delete a User @@ -50,7 +50,7 @@ Example to Delete a User userID := "9fe2ff9ee4384b1894a90878d3e92bab" err := users.Delete(identityClient, userID).ExtractErr() if err != nil { - panic("Unable to delete user: %s", err) + panic(err) } Example to List a User's Roles @@ -60,12 +60,12 @@ Example to List a User's Roles allPages, err := users.ListRoles(identityClient, tenantID, userID).AllPages() if err != nil { - panic("Unable to list user's roles: %s", err) + panic(err) } allRoles, err := users.ExtractRoles(allPages) if err != nil { - panic("Unable to extract roles: %s", err) + panic(err) } for _, role := range allRoles { diff --git a/openstack/identity/v3/endpoints/doc.go b/openstack/identity/v3/endpoints/doc.go index 42850208f7..8c18c32300 100644 --- a/openstack/identity/v3/endpoints/doc.go +++ b/openstack/identity/v3/endpoints/doc.go @@ -15,12 +15,12 @@ Example to List Endpoints allPages, err := endpoints.List(identityClient, listOpts).AllPages() if err != nil { - panic("Unable to list endpoints: %s", err) + panic(err) } allEndpoints, err := endpoints.ExtractEndpoints(allPages) if err != nil { - panic("Unable to extract endpoints: %s", err) + panic(err) } for _, endpoint := range allEndpoints { @@ -41,7 +41,7 @@ Example to Create an Endpoint endpoint, err := endpoints.Create(identityClient, createOpts).Extract() if err != nil { - panic("Unable to create endpoint: %s", err) + panic(err) } @@ -55,7 +55,7 @@ Example to Update an Endpoint endpoint, err := endpoints.Update(identityClient, endpointID, updateOpts).Extract() if err != nil { - panic("Unable to update endpoint: %s", err) + panic(err) } Example to Delete an Endpoint @@ -63,7 +63,7 @@ Example to Delete an Endpoint endpointID := "ad59deeec5154d1fa0dcff518596f499" err := endpoints.Delete(identityClient, endpointID).ExtractErr() if err != nil { - panic("Unable to delete endpoint: %s", err) + panic(err) } */ package endpoints diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go index d0a6b1ac7f..8db7724f2b 100644 --- a/openstack/identity/v3/extensions/trusts/doc.go +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -20,7 +20,7 @@ Example to Create a Token with Username, Password, and Trust ID err := tokens.Create(identityClient, createOpts).ExtractInto(&trustToken) if err != nil { - panic("Unable to create token: %s", err) + panic(err) } */ package trusts diff --git a/openstack/identity/v3/projects/doc.go b/openstack/identity/v3/projects/doc.go index 1dee534c2c..084051ae11 100644 --- a/openstack/identity/v3/projects/doc.go +++ b/openstack/identity/v3/projects/doc.go @@ -10,12 +10,12 @@ Example to List Projects allPages, err := projects.List(identityClient, listOpts).AllPages() if err != nil { - panic("Unable to list projects: %s", err) + panic(err) } allProjects, err := projects.ExtractProjects(allPages) if err != nil { - panic("Unable to extract projects: %s", err) + panic(err) } for _, project := range allProjects { @@ -31,7 +31,7 @@ Example to Create a Project project, err := projects.Create(identityClient, createOpts).Extract() if err != nil { - panic("Unable to create project: %s", err) + panic(err) } Example to Update a Project @@ -44,7 +44,7 @@ Example to Update a Project project, err := projects.Update(identityClient, projectID, updateOpts).Extract() if err != nil { - panic("Unable to update project: %s", err) + panic(err) } Example to Delete a Project @@ -52,7 +52,7 @@ Example to Delete a Project projectID := "966b3c7d36a24facaf20b7e458bf2192" err := projects.Delete(identityClient, projectID).ExtractErr() if err != nil { - panic("Unable to delete project: %s", err) + panic(err) } */ package projects diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index ce62ce4edb..ce83cbc36d 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -11,12 +11,12 @@ Example to List Role Assignments allPages, err := roles.ListAssignments(identityClient, listOpts).AllPages() if err != nil { - panic("Unable to list role assignments: %s", err) + panic(err) } allRoles, err := roles.ExtractRoleAssignments(allPages) if err != nil { - panic("Unable to extract roles: %s", err) + panic(err) } for _, role := range allRoles { diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index 28d097c4d1..b43c7bb6a7 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -10,12 +10,12 @@ Example to List Services allPages, err := services.List(identityClient, listOpts).AllPages() if err != nil { - panic("Unable to list services: %s", err) + panic(err) } allServices, err := services.ExtractServices(allPages) if err != nil { - panic("Unable to extract services: %s", err) + panic(err) } for _, service := range allServices { @@ -26,7 +26,7 @@ Example to Create a Service service, err := services.Create(identityClient, "compute").Extract() if err != nil { - panic("Unable to create service: %s", err) + panic(err) } Example to Delete a Service @@ -34,7 +34,7 @@ Example to Delete a Service serviceID := "3c7bbe9a6ecb453ca1789586291380ed" err := services.Delete(identityClient, serviceID).ExtractErr() if err != nil { - panic("Unable to delete service: %s", err) + panic(err) } */ package services diff --git a/openstack/identity/v3/tokens/doc.go b/openstack/identity/v3/tokens/doc.go index 5553b9f96e..966e128f12 100644 --- a/openstack/identity/v3/tokens/doc.go +++ b/openstack/identity/v3/tokens/doc.go @@ -14,7 +14,7 @@ Example to Create a Token From a Username and Password token, err := tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } Example to Create a Token From a Username, Password, and Domain @@ -27,7 +27,7 @@ Example to Create a Token From a Username, Password, and Domain token, err := tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } authOptions = tokens.AuthOptions{ @@ -38,7 +38,7 @@ Example to Create a Token From a Username, Password, and Domain token, err = tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } Example to Create a Token From a Token @@ -49,7 +49,7 @@ Example to Create a Token From a Token token, err := tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } Example to Create a Token from a Username and Password with Project ID Scope @@ -66,7 +66,7 @@ Example to Create a Token from a Username and Password with Project ID Scope token, err = tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } Example to Create a Token from a Username and Password with Domain ID Scope @@ -83,7 +83,7 @@ Example to Create a Token from a Username and Password with Domain ID Scope token, err = tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } Example to Create a Token from a Username and Password with Project Name Scope @@ -101,7 +101,7 @@ Example to Create a Token from a Username and Password with Project Name Scope token, err = tokens.Create(identityClient, authOptions).ExtractToken() if err != nil { - panic("Unable to create token: %s", err) + panic(err) } */ diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index 1bbe2e745b..8a8bf8451b 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -9,12 +9,12 @@ Example to List Users allPages, err := users.List(identityClient, listOpts).AllPages() if err != nil { - panic("Unable to list users: %s", err) + panic(err) } allUsers, err := users.ExtractUsers(allPages) if err != nil { - panic("Unable to extract users: %s", err) + panic(err) } for _, user := range allUsers { @@ -38,7 +38,7 @@ Example to Create a User user, err := users.Create(identityClient, createOpts).Extract() if err != nil { - panic("Unable to create user: %s", err) + panic(err) } Example to Update a User @@ -51,7 +51,7 @@ Example to Update a User user, err := users.Update(identityClient, userID, updateOpts).Extract() if err != nil { - panic("Unable to update user: %s", err) + panic(err) } Example to Delete a User @@ -59,7 +59,7 @@ Example to Delete a User userID := "0fe36e73809d46aeae6705c39077b1b3" err := users.Delete(identityClient, userID).ExtractErr() if err != nil { - panic("Unable to delete user: %s", err) + panic(err) } Example to List Groups a User Belongs To @@ -68,12 +68,12 @@ Example to List Groups a User Belongs To allPages, err := users.ListGroups(identityClient, userID).AllPages() if err != nil { - panic("Unable to retrieve groups: %s", err) + panic(err) } allGroups, err := users.ExtractGroups(allPages) if err != nil { - panic("Unable to extract groups: %s", err) + panic(err) } for _, group := range allGroups { From a4e3eb5c5d234690b69c4c17412a8ca2b9056f05 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 19 Aug 2017 20:59:35 -0600 Subject: [PATCH 0052/2296] ImageService v2: Docs (#494) * Images v2: imagedata docs * Images v2: images docs * Images v2: members docs --- openstack/imageservice/v2/imagedata/doc.go | 33 ++++++++ .../imageservice/v2/imagedata/requests.go | 4 +- .../imageservice/v2/imagedata/results.go | 6 +- openstack/imageservice/v2/images/doc.go | 60 ++++++++++++++ openstack/imageservice/v2/images/requests.go | 82 ++++++++++++------- openstack/imageservice/v2/images/results.go | 57 +++++++------ openstack/imageservice/v2/images/types.go | 20 +++-- openstack/imageservice/v2/members/doc.go | 58 +++++++++++++ openstack/imageservice/v2/members/requests.go | 44 +++++----- openstack/imageservice/v2/members/results.go | 21 +++-- 10 files changed, 291 insertions(+), 94 deletions(-) create mode 100644 openstack/imageservice/v2/imagedata/doc.go create mode 100644 openstack/imageservice/v2/images/doc.go create mode 100644 openstack/imageservice/v2/members/doc.go diff --git a/openstack/imageservice/v2/imagedata/doc.go b/openstack/imageservice/v2/imagedata/doc.go new file mode 100644 index 0000000000..a2f5e58b89 --- /dev/null +++ b/openstack/imageservice/v2/imagedata/doc.go @@ -0,0 +1,33 @@ +/* +Package imagedata enables management of image data. + +Example to Upload 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.Upload(imageClient, imageID, imageData).ExtractErr() + if err != nil { + panic(err) + } + +Example to Download Image Data + + imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" + + image, err := imagedata.Download(imageClient, imageID).Extract() + if err != nil { + panic(err) + } + + imageData, err := ioutil.ReadAll(image) + if err != nil { + panic(err) + } +*/ +package imagedata diff --git a/openstack/imageservice/v2/imagedata/requests.go b/openstack/imageservice/v2/imagedata/requests.go index 7fd6951d3b..4761e488c6 100644 --- a/openstack/imageservice/v2/imagedata/requests.go +++ b/openstack/imageservice/v2/imagedata/requests.go @@ -7,7 +7,7 @@ import ( "github.com/gophercloud/gophercloud" ) -// Upload uploads image file +// Upload uploads an image file. func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { _, r.Err = client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, @@ -16,7 +16,7 @@ func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r Upl return } -// Download retrieves file +// Download retrieves an image. func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { var resp *http.Response resp, r.Err = client.Get(downloadURL(client, id), nil, nil) diff --git a/openstack/imageservice/v2/imagedata/results.go b/openstack/imageservice/v2/imagedata/results.go index 970b226f2a..895d28ba8a 100644 --- a/openstack/imageservice/v2/imagedata/results.go +++ b/openstack/imageservice/v2/imagedata/results.go @@ -7,12 +7,14 @@ import ( "github.com/gophercloud/gophercloud" ) -// UploadResult is the result of an upload image operation +// UploadResult is the result of an upload image operation. Call its ExtractErr +// method to determine if the request succeeded or failed. type UploadResult struct { gophercloud.ErrResult } -// DownloadResult is the result of a download image operation +// DownloadResult is the result of a download image operation. Call its Extract +// method to gain access to the image data. type DownloadResult struct { gophercloud.Result } diff --git a/openstack/imageservice/v2/images/doc.go b/openstack/imageservice/v2/images/doc.go new file mode 100644 index 0000000000..14da9ac90d --- /dev/null +++ b/openstack/imageservice/v2/images/doc.go @@ -0,0 +1,60 @@ +/* +Package images enables management and retrieval of images from the OpenStack +Image Service. + +Example to List Images + + images.ListOpts{ + Owner: "a7509e1ae65945fda83f3e52c6296017", + } + + allPages, err := images.List(imagesClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allImages, err := images.ExtractImages(allPages) + if err != nil { + panic(err) + } + + for _, image := range allImages { + fmt.Printf("%+v\n", image) + } + +Example to Create an Image + + createOpts := images.CreateOpts{ + Name: "image_name", + Visibility: images.ImageVisibilityPrivate, + } + + image, err := images.Create(imageClient, createOpts) + if err != nil { + panic(err) + } + +Example to Update an Image + + imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27" + + updateOpts := images.UpdateOpts{ + images.ReplaceImageName{ + NewName: "new_name", + }, + } + + image, err := images.Update(imageClient, imageID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete an Image + + imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27" + err := images.Delete(imageClient, imageID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package images diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 044b5cb95f..387e791ceb 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -15,7 +15,8 @@ type ListOptsBuilder interface { // the API. Filtering is achieved by passing in struct field values that map to // the server attributes you want to see returned. Marker and Limit are used // for pagination. -//http://developer.openstack.org/api-ref-image-v2.html +// +// http://developer.openstack.org/api-ref-image-v2.html type ListOpts struct { // Integer value for the limit of values to return. Limit int `q:"limit"` @@ -23,16 +24,33 @@ type ListOpts struct { // UUID of the server at which you want to set a marker. Marker string `q:"marker"` - Name string `q:"name"` - Visibility ImageVisibility `q:"visibility"` + // Name filters on the name of the image. + Name string `q:"name"` + + // Visibility filters on the visibility of the image. + Visibility ImageVisibility `q:"visibility"` + + // MemberStatus filters on the member status of the image. MemberStatus ImageMemberStatus `q:"member_status"` - Owner string `q:"owner"` - Status ImageStatus `q:"status"` - SizeMin int64 `q:"size_min"` - SizeMax int64 `q:"size_max"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - Tag string `q:"tag"` + + // Owner filters on the project ID of the image. + Owner string `q:"owner"` + + // Status filters on the status of the image. + Status ImageStatus `q:"status"` + + // SizeMin filters on the size_min image property. + SizeMin int64 `q:"size_min"` + + // SizeMax filters on the size_max image property. + SizeMax int64 `q:"size_max"` + + // SortKey will sort the results based on a specified image property. + SortKey string `q:"sort_key"` + + // SortDir will sort the list results either ascending or decending. + SortDir string `q:"sort_dir"` + Tag string `q:"tag"` } // ToImageListQuery formats a ListOpts into a query string. @@ -41,7 +59,7 @@ func (opts ListOpts) ToImageListQuery() (string, error) { return q.String(), err } -// List implements image list request +// List implements image list request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { @@ -56,14 +74,13 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. -// The CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { // Returns value that can be passed to json.Marshal ToImageCreateMap() (map[string]interface{}, error) } -// CreateOpts implements CreateOptsBuilder +// CreateOpts represents options used to create an image. type CreateOpts struct { // Name is the name of the new image. Name string `json:"name" required:"true"` @@ -118,7 +135,7 @@ func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) { return b, nil } -// Create implements create image request +// Create implements create image request. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToImageCreateMap() if err != nil { @@ -129,19 +146,19 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Delete implements image delete request +// Delete implements image delete request. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } -// Get implements image get request +// Get implements image get request. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } -// Update implements image updated request +// Update implements image updated request. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToImageUpdateMap() if err != nil { @@ -155,9 +172,11 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// UpdateOptsBuilder implements UpdateOptsBuilder +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { - // returns value implementing json.Marshaler which when marshaled matches the patch schema: + // returns value implementing json.Marshaler which when marshaled matches + // the patch schema: // http://specs.openstack.org/openstack/glance-specs/specs/api/v2/http-patch-image-api-v2.html ToImageUpdateMap() ([]interface{}, error) } @@ -165,7 +184,8 @@ type UpdateOptsBuilder interface { // UpdateOpts implements UpdateOpts type UpdateOpts []Patch -// ToImageUpdateMap builder +// ToImageUpdateMap assembles a request body based on the contents of +// UpdateOpts. func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { m := make([]interface{}, len(opts)) for i, patch := range opts { @@ -175,18 +195,18 @@ func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { return m, nil } -// Patch represents a single update to an existing image. Multiple updates to an image can be -// submitted at the same time. +// Patch represents a single update to an existing image. Multiple updates +// to an image can be submitted at the same time. type Patch interface { ToImagePatchMap() map[string]interface{} } -// UpdateVisibility updated visibility +// UpdateVisibility represents an updated visibility property request. type UpdateVisibility struct { Visibility ImageVisibility } -// ToImagePatchMap builder +// ToImagePatchMap assembles a request body based on UpdateVisibility. func (u UpdateVisibility) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", @@ -195,12 +215,12 @@ func (u UpdateVisibility) ToImagePatchMap() map[string]interface{} { } } -// ReplaceImageName implements Patch +// ReplaceImageName represents an updated image_name property request. type ReplaceImageName struct { NewName string } -// ToImagePatchMap builder +// ToImagePatchMap assembles a request body based on ReplaceImageName. func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", @@ -209,12 +229,12 @@ func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} { } } -// ReplaceImageChecksum implements Patch +// ReplaceImageChecksum represents an updated checksum property request. type ReplaceImageChecksum struct { Checksum string } -// ReplaceImageChecksum builder +// ReplaceImageChecksum assembles a request body based on ReplaceImageChecksum. func (rc ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", @@ -223,12 +243,12 @@ func (rc ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { } } -// ReplaceImageTags implements Patch +// ReplaceImageTags represents an updated tags property request. type ReplaceImageTags struct { NewTags []string } -// ToImagePatchMap builder +// ToImagePatchMap assembles a request body based on ReplaceImageTags. func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index 632186b728..cd819ec9c8 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -11,11 +11,9 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// Image model -// Does not include the literal image data; just metadata. -// returned by listing images, and by fetching a specific image. +// Image represents an image found in the OpenStack Image service. type Image struct { - // ID is the image UUID + // ID is the image UUID. ID string `json:"id"` // Name is the human-readable display name for the image. @@ -34,16 +32,19 @@ type Image struct { ContainerFormat string `json:"container_format"` // DiskFormat is the format of the disk. - // If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, and iso. + // If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, + // and iso. DiskFormat string `json:"disk_format"` - // MinDiskGigabytes is the amount of disk space in GB that is required to boot the image. + // MinDiskGigabytes is the amount of disk space in GB that is required to + // boot the image. MinDiskGigabytes int `json:"min_disk"` - // MinRAMMegabytes [optional] is the amount of RAM in MB that is required to boot the image. + // MinRAMMegabytes [optional] is the amount of RAM in MB that is required to + // boot the image. MinRAMMegabytes int `json:"min_ram"` - // Owner is the tenant the image belongs to. + // Owner is the tenant ID the image belongs to. Owner string `json:"owner"` // Protected is whether the image is deletable or not. @@ -52,7 +53,7 @@ type Image struct { // Visibility defines who can see/use the image. Visibility ImageVisibility `json:"visibility"` - // Checksum is the checksum of the data that's associated with the image + // Checksum is the checksum of the data that's associated with the image. Checksum string `json:"checksum"` // SizeBytes is the size of the data that's associated with the image. @@ -60,23 +61,27 @@ type Image struct { // Metadata is a set of metadata associated with the image. // Image metadata allow for meaningfully define the image properties - // and tags. See http://docs.openstack.org/developer/glance/metadefs-concepts.html. + // and tags. + // See http://docs.openstack.org/developer/glance/metadefs-concepts.html. Metadata map[string]string `json:"metadata"` - // Properties is a set of key-value pairs, if any, that are associated with the image. + // Properties is a set of key-value pairs, if any, that are associated with + // the image. Properties map[string]interface{} `json:"-"` // CreatedAt is the date when the image has been created. CreatedAt time.Time `json:"created_at"` - // UpdatedAt is the date when the last change has been made to the image or it's properties. + // UpdatedAt is the date when the last change has been made to the image or + // it's properties. UpdatedAt time.Time `json:"updated_at"` - // File is the trailing path after the glance endpoint that represent the location - // of the image or the path to retrieve it. + // File is the trailing path after the glance endpoint that represent the + // location of the image or the path to retrieve it. File string `json:"file"` - // Schema is the path to the JSON-schema that represent the image or image entity. + // Schema is the path to the JSON-schema that represent the image or image + // entity. Schema string `json:"schema"` // VirtualSize is the virtual size of the image @@ -131,38 +136,43 @@ func (r commonResult) Extract() (*Image, error) { return s, err } -// CreateResult represents the result of a Create operation +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret it as an Image. type CreateResult struct { commonResult } -// UpdateResult represents the result of an Update operation +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret it as an Image. type UpdateResult struct { commonResult } -// GetResult represents the result of a Get operation +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret it as an Image. type GetResult struct { commonResult } -//DeleteResult model +// DeleteResult represents the result of a Delete operation. Call its +// ExtractErr method to interpret it as an Image. type DeleteResult struct { gophercloud.ErrResult } -// ImagePage represents page +// ImagePage represents the results of a List request. type ImagePage struct { pagination.LinkedPageBase } -// IsEmpty returns true if a page contains no Images results. +// IsEmpty returns true if an ImagePage contains no Images results. func (r ImagePage) IsEmpty() (bool, error) { images, err := ExtractImages(r) return len(images) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to +// the next page of results. func (r ImagePage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` @@ -179,7 +189,8 @@ func (r ImagePage) NextPageURL() (string, error) { return nextPageURL(r.URL.String(), s.Next) } -// ExtractImages interprets the results of a single page from a List() call, producing a slice of Image entities. +// ExtractImages interprets the results of a single page from a List() call, +// producing a slice of Image entities. func ExtractImages(r pagination.Page) ([]Image, error) { var s struct { Images []Image `json:"images"` diff --git a/openstack/imageservice/v2/images/types.go b/openstack/imageservice/v2/images/types.go index 086e7e5d57..2e01b38f5c 100644 --- a/openstack/imageservice/v2/images/types.go +++ b/openstack/imageservice/v2/images/types.go @@ -9,7 +9,8 @@ const ( // been reserved for an image in the image registry. ImageStatusQueued ImageStatus = "queued" - // ImageStatusSaving denotes that an image’s raw data is currently being uploaded to Glance + // ImageStatusSaving denotes that an image’s raw data is currently being + // uploaded to Glance ImageStatusSaving ImageStatus = "saving" // ImageStatusActive denotes an image that is fully available in Glance. @@ -23,16 +24,18 @@ const ( // The image information is retained in the image registry. ImageStatusDeleted ImageStatus = "deleted" - // ImageStatusPendingDelete is similar to Delete, but the image is not yet deleted. + // ImageStatusPendingDelete is similar to Delete, but the image is not yet + // deleted. ImageStatusPendingDelete ImageStatus = "pending_delete" - // ImageStatusDeactivated denotes that access to image data is not allowed to any non-admin user. + // ImageStatusDeactivated denotes that access to image data is not allowed to + // any non-admin user. ImageStatusDeactivated ImageStatus = "deactivated" ) // ImageVisibility denotes an image that is fully available in Glance. -// This occurs when the image data is uploaded, or the image size -// is explicitly set to zero on creation. +// This occurs when the image data is uploaded, or the image size is explicitly +// set to zero on creation. // According to design // https://wiki.openstack.org/wiki/Glance-v2-community-image-visibility-design type ImageVisibility string @@ -52,12 +55,13 @@ const ( // ImageVisibilityCommunity images: // - all users can see and boot it - // - users with tenantId in the member-list of the image with member_status == 'accepted' - // have this image in their default image-list + // - users with tenantId in the member-list of the image with + // member_status == 'accepted' have this image in their default image-list. ImageVisibilityCommunity ImageVisibility = "community" ) -// MemberStatus is a status for adding a new member (tenant) to an image member list. +// MemberStatus is a status for adding a new member (tenant) to an image +// member list. type ImageMemberStatus string const ( diff --git a/openstack/imageservice/v2/members/doc.go b/openstack/imageservice/v2/members/doc.go new file mode 100644 index 0000000000..1a7132045f --- /dev/null +++ b/openstack/imageservice/v2/members/doc.go @@ -0,0 +1,58 @@ +/* +Package members enables management and retrieval of image members. + +Members are projects other than the image owner who have access to the image. + +Example to List Members of an Image + + imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" + + allPages, err := members.List(imageID).AllPages() + if err != nil { + panic(err) + } + + allMembers, err := members.ExtractMembers(allPages) + if err != nil { + panic(err) + } + + for _, member := range allMembers { + fmt.Printf("%+v\n", member) + } + +Example to Add a Member to an Image + + imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" + projectID := "fc404778935a4cebaddcb4788fb3ff2c" + + member, err := members.Create(imageClient, imageID, projectID).Extract() + if err != nil { + panic(err) + } + +Example to Update the Status of a Member + + imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" + projectID := "fc404778935a4cebaddcb4788fb3ff2c" + + updateOpts := members.UpdateOpts{ + Status: "accepted", + } + + member, err := members.Update(imageClient, imageID, projectID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Member from an Image + + imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" + projectID := "fc404778935a4cebaddcb4788fb3ff2c" + + err := members.Delete(imageClient, imageID, projectID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package members diff --git a/openstack/imageservice/v2/members/requests.go b/openstack/imageservice/v2/members/requests.go index b16fb82d43..b80e54e83c 100644 --- a/openstack/imageservice/v2/members/requests.go +++ b/openstack/imageservice/v2/members/requests.go @@ -5,16 +5,24 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// Create member for specific image -// -// 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. -// Synchronous Postconditions -// 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 +/* + Create member for specific image + + 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. + + Synchronous Postconditions + + 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 +*/ func Create(client *gophercloud.ServiceClient, id string, member string) (r CreateResult) { b := map[string]interface{}{"member": member} _, r.Err = client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ @@ -23,8 +31,7 @@ func Create(client *gophercloud.ServiceClient, id string, member string) (r Crea return } -// List members returns list of members for specifed image id -// More details: http://developer.openstack.org/api-ref-image-v2.html#listImageMembers-v2 +// List members returns list of members for specifed image id. func List(client *gophercloud.ServiceClient, id string) pagination.Pager { return pagination.NewPager(client, listMembersURL(client, id), func(r pagination.PageResult) pagination.Page { return MemberPage{pagination.SinglePageBase(r)} @@ -32,26 +39,24 @@ func List(client *gophercloud.ServiceClient, id string) pagination.Pager { } // Get image member details. -// More details: http://developer.openstack.org/api-ref-image-v2.html#getImageMember-v2 func Get(client *gophercloud.ServiceClient, imageID string, memberID string) (r DetailsResult) { _, r.Err = client.Get(getMemberURL(client, imageID, memberID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return } -// Delete membership for given image. -// Callee should be image owner -// More details: http://developer.openstack.org/api-ref-image-v2.html#deleteImageMember-v2 +// Delete membership for given image. Callee should be image owner. func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) (r DeleteResult) { _, r.Err = client.Delete(deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204}}) return } -// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +// UpdateOptsBuilder allows extensions to add additional attributes to the +// Update request. type UpdateOptsBuilder interface { ToImageMemberUpdateMap() (map[string]interface{}, error) } -// UpdateOpts implements UpdateOptsBuilder +// UpdateOpts represents options to an Update request. type UpdateOpts struct { Status string } @@ -63,8 +68,7 @@ func (opts UpdateOpts) ToImageMemberUpdateMap() (map[string]interface{}, error) }, nil } -// Update function updates member -// More details: http://developer.openstack.org/api-ref-image-v2.html#updateImageMember-v2 +// Update function updates member. func Update(client *gophercloud.ServiceClient, imageID string, memberID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToImageMemberUpdateMap() if err != nil { diff --git a/openstack/imageservice/v2/members/results.go b/openstack/imageservice/v2/members/results.go index d3cc1ceaf7..ab694bdc0f 100644 --- a/openstack/imageservice/v2/members/results.go +++ b/openstack/imageservice/v2/members/results.go @@ -7,7 +7,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// Member model +// Member represents a member of an Image. type Member struct { CreatedAt time.Time `json:"created_at"` ImageID string `json:"image_id"` @@ -17,7 +17,7 @@ type Member struct { UpdatedAt time.Time `json:"updated_at"` } -// Extract Member model from request if possible +// Extract Member model from a request. func (r commonResult) Extract() (*Member, error) { var s *Member err := r.ExtractInto(&s) @@ -29,7 +29,8 @@ type MemberPage struct { pagination.SinglePageBase } -// ExtractMembers returns a slice of Members contained in a single page of results. +// ExtractMembers returns a slice of Members contained in a single page +// of results. func ExtractMembers(r pagination.Page) ([]Member, error) { var s struct { Members []Member `json:"members"` @@ -38,7 +39,7 @@ func ExtractMembers(r pagination.Page) ([]Member, error) { return s.Members, err } -// IsEmpty determines whether or not a page of Members contains any results. +// IsEmpty determines whether or not a MemberPage contains any results. func (r MemberPage) IsEmpty() (bool, error) { members, err := ExtractMembers(r) return len(members) == 0, err @@ -48,22 +49,26 @@ type commonResult struct { gophercloud.Result } -// CreateResult result model +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret it as a Member. type CreateResult struct { commonResult } -// DetailsResult model +// DetailsResult represents the result of a Get operation. Call its Extract +// method to interpret it as a Member. type DetailsResult struct { commonResult } -// UpdateResult model +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret it as a Member. type UpdateResult struct { commonResult } -// DeleteResult model +// 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 } From c5ac6b22ca259daa4436436e31c33a96422a640c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 20 Aug 2017 22:47:58 -0600 Subject: [PATCH 0053/2296] Networking v2: Docs (#495) * Networking v2: apiversions docs * Networking v2: external docs * Networking v2: firewalls docs * Networking v2: policies docs * Networking v2: routerinsertion docs * Networking v2: rules docs * Networking v2: floatingips docs * Networking v2: routers docs * Networking v2: lbaas members docs * Networking v2: lbaas monitors docs * Networking v2: lbaas pools docs * Networking v2: lbaas vips docs * Networking v2: lbaas_v2 listeners docs * Networking v2: lbaas_v2 loadbalancers docs * Networking v2: lbaas_v2 monitors docs * Networking v2: lbaas_v2 pools docs * Networking v2: provider docs * Networking v2: security groups docs * Networking v2: security rules docs * Networking v2: networks docs * Networking v2: subnets docs * Networking v2: ports docs --- openstack/networking/v2/apiversions/doc.go | 24 ++- .../networking/v2/apiversions/requests.go | 7 +- .../networking/v2/extensions/external/doc.go | 47 +++++- .../v2/extensions/fwaas/firewalls/doc.go | 60 ++++++++ .../v2/extensions/fwaas/firewalls/requests.go | 19 +-- .../v2/extensions/fwaas/firewalls/results.go | 16 +- .../v2/extensions/fwaas/policies/doc.go | 84 +++++++++++ .../v2/extensions/fwaas/policies/requests.go | 32 ++-- .../v2/extensions/fwaas/policies/results.go | 22 ++- .../extensions/fwaas/routerinsertion/doc.go | 68 ++++++++- .../fwaas/routerinsertion/requests.go | 4 +- .../v2/extensions/fwaas/rules/doc.go | 64 ++++++++ .../v2/extensions/fwaas/rules/requests.go | 38 ++--- .../v2/extensions/fwaas/rules/results.go | 18 ++- .../v2/extensions/layer3/floatingips/doc.go | 71 +++++++++ .../extensions/layer3/floatingips/requests.go | 16 +- .../extensions/layer3/floatingips/results.go | 50 ++++--- .../v2/extensions/layer3/routers/doc.go | 108 ++++++++++++++ .../v2/extensions/layer3/routers/requests.go | 32 ++-- .../v2/extensions/layer3/routers/results.go | 41 ++--- .../v2/extensions/lbaas/members/doc.go | 59 ++++++++ .../v2/extensions/lbaas/members/requests.go | 23 ++- .../v2/extensions/lbaas/members/results.go | 33 ++-- .../v2/extensions/lbaas/monitors/doc.go | 63 ++++++++ .../v2/extensions/lbaas/monitors/requests.go | 101 +++++++------ .../v2/extensions/lbaas/monitors/results.go | 49 +++--- .../v2/extensions/lbaas/pools/doc.go | 81 ++++++++++ .../v2/extensions/lbaas/pools/requests.go | 41 ++--- .../v2/extensions/lbaas/pools/results.go | 50 ++++--- .../v2/extensions/lbaas/vips/doc.go | 65 ++++++++ .../v2/extensions/lbaas/vips/requests.go | 73 +++++---- .../v2/extensions/lbaas/vips/results.go | 55 +++---- .../networking/v2/extensions/lbaas_v2/doc.go | 2 - .../v2/extensions/lbaas_v2/listeners/doc.go | 63 ++++++++ .../extensions/lbaas_v2/listeners/requests.go | 54 ++++--- .../extensions/lbaas_v2/listeners/results.go | 45 ++++-- .../extensions/lbaas_v2/loadbalancers/doc.go | 71 +++++++++ .../lbaas_v2/loadbalancers/requests.go | 71 +++++---- .../lbaas_v2/loadbalancers/results.go | 64 +++++--- .../v2/extensions/lbaas_v2/monitors/doc.go | 69 +++++++++ .../extensions/lbaas_v2/monitors/requests.go | 99 +++++++----- .../extensions/lbaas_v2/monitors/results.go | 21 ++- .../v2/extensions/lbaas_v2/pools/doc.go | 124 +++++++++++++++ .../v2/extensions/lbaas_v2/pools/requests.go | 113 +++++++------- .../v2/extensions/lbaas_v2/pools/results.go | 69 ++++++--- .../networking/v2/extensions/provider/doc.go | 92 +++++++++--- .../v2/extensions/provider/results.go | 16 +- .../v2/extensions/security/groups/doc.go | 58 +++++++ .../v2/extensions/security/groups/requests.go | 29 +++- .../v2/extensions/security/groups/results.go | 19 ++- .../v2/extensions/security/rules/doc.go | 50 +++++++ .../v2/extensions/security/rules/requests.go | 69 +++++---- .../v2/extensions/security/rules/results.go | 9 +- openstack/networking/v2/networks/doc.go | 72 ++++++++- openstack/networking/v2/networks/requests.go | 23 ++- openstack/networking/v2/networks/results.go | 22 ++- openstack/networking/v2/ports/doc.go | 79 +++++++++- openstack/networking/v2/ports/requests.go | 19 +-- openstack/networking/v2/ports/results.go | 31 +++- openstack/networking/v2/subnets/doc.go | 141 ++++++++++++++++-- openstack/networking/v2/subnets/requests.go | 83 ++++++++--- openstack/networking/v2/subnets/results.go | 40 +++-- 62 files changed, 2533 insertions(+), 698 deletions(-) mode change 100755 => 100644 openstack/networking/v2/extensions/external/doc.go create mode 100644 openstack/networking/v2/extensions/fwaas/firewalls/doc.go create mode 100644 openstack/networking/v2/extensions/fwaas/policies/doc.go create mode 100644 openstack/networking/v2/extensions/fwaas/rules/doc.go create mode 100644 openstack/networking/v2/extensions/layer3/floatingips/doc.go create mode 100644 openstack/networking/v2/extensions/layer3/routers/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas/members/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas/monitors/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas/pools/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas/vips/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/listeners/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/monitors/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/pools/doc.go mode change 100755 => 100644 openstack/networking/v2/extensions/provider/doc.go create mode 100644 openstack/networking/v2/extensions/security/groups/doc.go create mode 100644 openstack/networking/v2/extensions/security/rules/doc.go diff --git a/openstack/networking/v2/apiversions/doc.go b/openstack/networking/v2/apiversions/doc.go index 0208ee20ec..5461942b92 100644 --- a/openstack/networking/v2/apiversions/doc.go +++ b/openstack/networking/v2/apiversions/doc.go @@ -1,4 +1,22 @@ -// Package apiversions provides information and interaction with the different -// API versions for the OpenStack Neutron service. This functionality is not -// restricted to this particular version. +/* +Package apiversions provides information and interaction with the different +API versions for the OpenStack Neutron service. This functionality is not +restricted to this particular version. + +Example to List API Versions + + allPages, err := apiversions.ListVersions(networkingClient).AllPages() + if err != nil { + panic(err) + } + + allVersions, err := apiversions.ExtractAPIVersions(allPages) + if err != nil { + panic(err) + } + + for _, version := range allVersions { + fmt.Printf("%+v\n", version) + } +*/ package apiversions diff --git a/openstack/networking/v2/apiversions/requests.go b/openstack/networking/v2/apiversions/requests.go index 59ece85090..c5494fb357 100644 --- a/openstack/networking/v2/apiversions/requests.go +++ b/openstack/networking/v2/apiversions/requests.go @@ -5,15 +5,16 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// ListVersions lists all the Neutron API versions available to end-users +// ListVersions lists all the Neutron API versions available to end-users. func ListVersions(c *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(c, apiVersionsURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } -// ListVersionResources lists all of the different API resources for a particular -// API versions. Typical resources for Neutron might be: networks, subnets, etc. +// ListVersionResources lists all of the different API resources for a +// particular API versions. Typical resources for Neutron might be: networks, +// subnets, etc. func ListVersionResources(c *gophercloud.ServiceClient, v string) pagination.Pager { return pagination.NewPager(c, apiInfoURL(c, v), func(r pagination.PageResult) pagination.Page { return APIVersionResourcePage{pagination.SinglePageBase(r)} diff --git a/openstack/networking/v2/extensions/external/doc.go b/openstack/networking/v2/extensions/external/doc.go old mode 100755 new mode 100644 index dad3a844f7..b8261684e7 --- a/openstack/networking/v2/extensions/external/doc.go +++ b/openstack/networking/v2/extensions/external/doc.go @@ -1,3 +1,46 @@ -// Package external provides information and interaction with the external -// extension for the OpenStack Networking service. +/* +Package external provides information and interaction with the external +extension for the OpenStack Networking service. + +Example to List Networks with External Information + + type NetworkWithExternalExt struct { + networks.Network + external.NetworkExternalExt + } + + var allNetworks []NetworkWithExternalExt + + allPages, err := networks.List(networkClient, nil).AllPages() + if err != nil { + panic(err) + } + + err = networks.ExtractNetworksInto(allPages, &allNetworks) + if err != nil { + panic(err) + } + + for _, network := range allNetworks { + fmt.Println("%+v\n", network) + } + +Example to Create a Network with External Information + + iTrue := true + networkCreateOpts := networks.CreateOpts{ + Name: "private", + AdminStateUp: &iTrue, + } + + createOpts := external.CreateOptsExt{ + networkCreateOpts, + &iTrue, + } + + network, err := networks.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } +*/ package external diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/doc.go b/openstack/networking/v2/extensions/fwaas/firewalls/doc.go new file mode 100644 index 0000000000..c01070edc8 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas/firewalls/doc.go @@ -0,0 +1,60 @@ +/* +Package firewalls allows management and retrieval of firewalls from the +OpenStack Networking Service. + +Example to List Firewalls + + listOpts := firewalls.ListOpts{ + TenantID: "tenant-id", + } + + allPages, err := firewalls.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allFirewalls, err := firewalls.ExtractFirewalls(allPages) + if err != nil { + panic(err) + } + + for _, fw := range allFirewalls { + fmt.Printf("%+v\n", fw) + } + +Example to Create a Firewall + + createOpts := firewalls.CreateOpts{ + Name: "firewall_1", + Description: "A firewall", + PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", + AdminStateUp: gophercloud.Enabled, + } + + firewall, err := firewalls.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Firewall + + firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" + + updateOpts := firewalls.UpdateOpts{ + AdminStateUp: gophercloud.Disabled, + } + + firewall, err := firewalls.Update(networkClient, firewallID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Firewall + + firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" + err := firewalls.Delete(networkClient, firewallID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package firewalls diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go index 21ceb4ea18..aa30194668 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go @@ -56,10 +56,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToFirewallCreateMap() (map[string]interface{}, error) } @@ -67,8 +65,9 @@ type CreateOptsBuilder interface { // CreateOpts contains all the values needed to create a new firewall. type CreateOpts struct { PolicyID string `json:"firewall_policy_id" required:"true"` - // Only required if the caller has an admin role and wants to create a firewall - // for another tenant. + // TenantID specifies a tenant to own the firewall. The caller must have + // an admin role in order to set this. Otherwise, this field is left unset + // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` @@ -81,7 +80,7 @@ func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "firewall") } -// Create accepts a CreateOpts struct and uses the values to create a new firewall +// Create accepts a CreateOpts struct and uses the values to create a new firewall. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFirewallCreateMap() if err != nil { @@ -98,10 +97,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToFirewallUpdateMap() (map[string]interface{}, error) } diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/results.go b/openstack/networking/v2/extensions/fwaas/firewalls/results.go index 1403ced2ba..f6786a4433 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/results.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/results.go @@ -61,8 +61,8 @@ func (r FirewallPage) IsEmpty() (bool, error) { return len(is) == 0, err } -// ExtractFirewalls accepts a Page struct, specifically a RouterPage struct, -// and extracts the elements into a slice of Router structs. In other words, +// ExtractFirewalls accepts a Page struct, specifically a FirewallPage struct, +// and extracts the elements into a slice of Firewall structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractFirewalls(r pagination.Page) ([]Firewall, error) { var s []Firewall @@ -70,22 +70,26 @@ func ExtractFirewalls(r pagination.Page) ([]Firewall, error) { return s, err } -// GetResult represents the result of a get operation. +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret it as a Firewall. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret it as a Firewall. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret it as a Firewall. type CreateResult struct { commonResult } diff --git a/openstack/networking/v2/extensions/fwaas/policies/doc.go b/openstack/networking/v2/extensions/fwaas/policies/doc.go new file mode 100644 index 0000000000..ae824491f1 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas/policies/doc.go @@ -0,0 +1,84 @@ +/* +Package policies allows management and retrieval of Firewall Policies in the +OpenStack Networking Service. + +Example to List Policies + + listOpts := policies.ListOpts{ + TenantID: "966b3c7d36a24facaf20b7e458bf2192", + } + + allPages, err := policies.List(networkClient, listOpts).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) + } + +Example to Create a Policy + + createOpts := policies.CreateOpts{ + Name: "policy_1", + Description: "A policy", + Rules: []string{ + "98a58c87-76be-ae7c-a74e-b77fffb88d95", + "7c4f087a-ed46-4ea8-8040-11ca460a61c0", + } + } + + policy, err := policies.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Policy + + policyID := "38aee955-6283-4279-b091-8b9c828000ec" + + updateOpts := policies.UpdateOpts{ + Description: "New Description", + } + + policy, err := policies.Update(networkClient, policyID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Policy + + policyID := "38aee955-6283-4279-b091-8b9c828000ec" + err := policies.Delete(networkClient, policyID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Add a Rule to a Policy + + policyID := "38aee955-6283-4279-b091-8b9c828000ec" + ruleOpts := policies.InsertRuleOpts{ + ID: "98a58c87-76be-ae7c-a74e-b77fffb88d95", + } + + policy, err := policies.AddRule(networkClient, policyID, ruleOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Rule from a Policy + + policyID := "38aee955-6283-4279-b091-8b9c828000ec" + ruleID := "98a58c87-76be-ae7c-a74e-b77fffb88d95", + + policy, err := policies.RemoveRule(networkClient, policyID, ruleID).Extract() + if err != nil { + panic(err) + } +*/ +package policies diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go index 437d1248b7..b1a6a5598b 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas/policies/requests.go @@ -39,8 +39,8 @@ func (opts ListOpts) ToPolicyListQuery() (string, error) { // firewall policies. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. // -// Default policy settings return only those firewall policies that are owned by the -// tenant who submits the request, unless an admin user submits the request. +// Default policy settings return only those firewall policies that are owned by +// the tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { @@ -55,18 +55,17 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToFirewallPolicyCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new firewall policy. type CreateOpts struct { - // Only required if the caller has an admin role and wants to create a firewall policy - // for another tenant. + // TenantID specifies a tenant to own the firewall. The caller must have + // an admin role in order to set this. Otherwise, this field is left unset + // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` @@ -80,7 +79,8 @@ func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, erro return gophercloud.BuildRequestBody(opts, "firewall_policy") } -// Create accepts a CreateOpts struct and uses the values to create a new firewall policy +// Create accepts a CreateOpts struct and uses the values to create a new +// firewall policy. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFirewallPolicyCreateMap() if err != nil { @@ -97,10 +97,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToFirewallPolicyUpdateMap() (map[string]interface{}, error) } @@ -132,16 +130,20 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r return } -// Delete will permanently delete a particular firewall policy based on its unique ID. +// Delete will permanently delete a particular firewall policy based on its +// unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } +// InsertRuleOptsBuilder allows extensions to add additional parameters to the +// InsertRule request. type InsertRuleOptsBuilder interface { ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) } +// InsertRuleOpts contains the values used when updating a policy's rules. type InsertRuleOpts struct { ID string `json:"firewall_rule_id" required:"true"` BeforeRuleID string `json:"insert_before,omitempty"` @@ -152,6 +154,7 @@ func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface return gophercloud.BuildRequestBody(opts, "") } +// AddRule will add a rule to a policy. func AddRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) { b, err := opts.ToFirewallPolicyInsertRuleMap() if err != nil { @@ -164,6 +167,7 @@ func AddRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder return } +// RemoveRule will add a rule to a policy. func RemoveRule(c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { b := map[string]interface{}{"firewall_rule_id": ruleID} _, r.Err = c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ diff --git a/openstack/networking/v2/extensions/fwaas/policies/results.go b/openstack/networking/v2/extensions/fwaas/policies/results.go index 9c5b1861e3..bbe22b1361 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/results.go +++ b/openstack/networking/v2/extensions/fwaas/policies/results.go @@ -55,8 +55,8 @@ func (r PolicyPage) IsEmpty() (bool, error) { return len(is) == 0, err } -// ExtractPolicies accepts a Page struct, specifically a RouterPage struct, -// and extracts the elements into a slice of Router structs. In other words, +// ExtractPolicies accepts a Page struct, specifically a Policy struct, +// and extracts the elements into a slice of Policy structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPolicies(r pagination.Page) ([]Policy, error) { var s struct { @@ -66,32 +66,38 @@ func ExtractPolicies(r pagination.Page) ([]Policy, error) { return s.Policies, err } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Policy. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its +// Extract method to interpret it as a Policy. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the operation succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Policy. type CreateResult struct { commonResult } -// InsertRuleResult represents the result of an InsertRule operation. +// InsertRuleResult represents the result of an InsertRule operation. Call its +// Extract method to interpret it as a Policy. type InsertRuleResult struct { commonResult } -// RemoveRuleResult represents the result of a RemoveRule operation. +// RemoveRuleResult represents the result of a RemoveRule operation. Call its +// Extract method to interpret it as a Policy. type RemoveRuleResult struct { commonResult } diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go index 9b847e2f5e..4f0a779eec 100644 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go +++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go @@ -1,2 +1,68 @@ -// Package routerinsertion implements the fwaasrouterinsertion FWaaS extension. +/* +Package routerinsertion implements the fwaasrouterinsertion Firewall extension. +It is used to manage the router information of a firewall. + +Example to List Firewalls with Router Information + + type FirewallsWithRouters struct { + firewalls.Firewall + routerinsertion.FirewallExt + } + + var allFirewalls []FirewallsWithRouters + + allPages, err := firewalls.List(networkClient, nil).AllPages() + if err != nil { + panic(err) + } + + err = firewalls.ExtractFirewallsInto(allPages, &allFirewalls) + if err != nil { + panic(err) + } + + for _, fw := range allFirewalls { + fmt.Printf("%+v\n", fw) + } + +Example to Create a Firewall with a Router + + firewallCreateOpts := firewalls.CreateOpts{ + Name: "firewall_1", + PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", + } + + createOpts := routerinsertion.CreateOptsExt{ + CreateOptsBuilder: firewallCreateOpts, + RouterIDs: []string{ + "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8", + }, + } + + firewall, err := firewalls.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Firewall with a Router + + firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" + + firewallUpdateOpts := firewalls.UpdateOpts{ + Description: "updated firewall", + PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", + } + + updateOpts := routerinsertion.UpdateOptsExt{ + UpdateOptsBuilder: firewallUpdateOpts, + RouterIDs: []string{ + "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8", + }, + } + + firewall, err := firewalls.Update(networkClient, firewallID, updateOpts).Extract() + if err != nil { + panic(err) + } +*/ package routerinsertion diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go index fce100f87e..b1f6d76e38 100644 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go +++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go @@ -4,7 +4,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" ) -// CreateOptsExt adds a RouterIDs option to the base CreateOpts. +// CreateOptsExt adds the RouterIDs option to the base CreateOpts. type CreateOptsExt struct { firewalls.CreateOptsBuilder RouterIDs []string `json:"router_ids"` @@ -23,7 +23,7 @@ func (opts CreateOptsExt) ToFirewallCreateMap() (map[string]interface{}, error) return base, nil } -// UpdateOptsExt updates a RouterIDs option to the base UpdateOpts. +// UpdateOptsExt adds the RouterIDs option to the base UpdateOpts. type UpdateOptsExt struct { firewalls.UpdateOptsBuilder RouterIDs []string `json:"router_ids"` diff --git a/openstack/networking/v2/extensions/fwaas/rules/doc.go b/openstack/networking/v2/extensions/fwaas/rules/doc.go new file mode 100644 index 0000000000..3351a3e5c9 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas/rules/doc.go @@ -0,0 +1,64 @@ +/* +Package rules enables management and retrieval of Firewall Rules in the +OpenStack Networking Service. + +Example to List Rules + + listOpts := rules.ListOpts{ + Protocol: rules.ProtocolAny, + } + + allPages, err := rules.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allRules, err := rules.ExtractRules(allPages) + if err != nil { + panic(err) + } + + for _, rule := range allRules { + fmt.Printf("%+v\n", rule) + } + +Example to Create a Rule + + createOpts := rules.CreateOpts{ + Action: "allow", + Protocol: rules.ProtocolTCP, + Description: "ssh", + DestinationPort: 22, + DestinationIPAddress: "192.168.1.0/24", + } + + rule, err := rules.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Rule + + ruleID := "f03bd950-6c56-4f5e-a307-45967078f507" + newPort := 80 + newDescription := "http" + + updateOpts := rules.UpdateOpts{ + Description: &newDescription, + port: &newPort, + } + + rule, err := rules.Update(networkClient, ruleID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Rule + + ruleID := "f03bd950-6c56-4f5e-a307-45967078f507" + err := rules.Delete(networkClient, ruleID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package rules diff --git a/openstack/networking/v2/extensions/fwaas/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go index c1784b7325..83bbe99b6d 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas/rules/requests.go @@ -6,21 +6,21 @@ import ( ) type ( - // Protocol represents a valid rule protocol + // Protocol represents a valid rule protocol. Protocol string ) const ( - // ProtocolAny is to allow any protocol + // ProtocolAny is to allow any protocol. ProtocolAny Protocol = "any" - // ProtocolICMP is to allow the ICMP protocol + // ProtocolICMP is to allow the ICMP protocol. ProtocolICMP Protocol = "icmp" - // ProtocolTCP is to allow the TCP protocol + // ProtocolTCP is to allow the TCP protocol. ProtocolTCP Protocol = "tcp" - // ProtocolUDP is to allow the UDP protocol + // ProtocolUDP is to allow the UDP protocol. ProtocolUDP Protocol = "udp" ) @@ -33,8 +33,8 @@ type ListOptsBuilder interface { // 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 Firewall rule attributes you want to see returned. SortKey allows you to -// sort by a particular firewall rule attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. +// sort by a particular firewall rule attribute. SortDir sets the direction, and +// is either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { TenantID string `q:"tenant_id"` Name string `q:"name"` @@ -67,8 +67,8 @@ func (opts ListOpts) ToRuleListQuery() (string, error) { // firewall rules. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. // -// Default policy settings return only those firewall rules that are owned by the -// tenant who submits the request, unless an admin user submits the request. +// Default policy settings return only those firewall rules that are owned by +// the tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) @@ -85,10 +85,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToRuleCreateMap() (map[string]interface{}, error) } @@ -123,7 +121,8 @@ func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { return b, nil } -// Create accepts a CreateOpts struct and uses the values to create a new firewall rule +// Create accepts a CreateOpts struct and uses the values to create a new +// firewall rule. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRuleCreateMap() if err != nil { @@ -140,15 +139,15 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToRuleUpdateMap() (map[string]interface{}, error) } // UpdateOpts contains the values used when updating a firewall rule. +// These fields are all pointers so that unset fields will not cause the +// existing Rule attribute to be removed. type UpdateOpts struct { Protocol *string `json:"protocol,omitempty"` Action *string `json:"action,omitempty"` @@ -181,7 +180,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r return } -// Delete will permanently delete a particular firewall rule based on its unique ID. +// Delete will permanently delete a particular firewall rule based on its +// unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return diff --git a/openstack/networking/v2/extensions/fwaas/rules/results.go b/openstack/networking/v2/extensions/fwaas/rules/results.go index c44e5a9108..1af03e573d 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas/rules/results.go @@ -5,7 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// Rule represents a firewall rule +// Rule represents a firewall rule. type Rule struct { ID string `json:"id"` Name string `json:"name,omitempty"` @@ -50,8 +50,8 @@ func (r RulePage) IsEmpty() (bool, error) { return len(is) == 0, err } -// ExtractRules accepts a Page struct, specifically a RouterPage struct, -// and extracts the elements into a slice of Router structs. In other words, +// ExtractRules accepts a Page struct, specifically a RulePage struct, +// and extracts the elements into a slice of Rule structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractRules(r pagination.Page) ([]Rule, error) { var s struct { @@ -74,22 +74,26 @@ func (r commonResult) Extract() (*Rule, error) { return s.Rule, err } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract method +// to interpret it as a Rule. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Rule. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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 } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Rule. type CreateResult struct { commonResult } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/doc.go b/openstack/networking/v2/extensions/layer3/floatingips/doc.go new file mode 100644 index 0000000000..bf5ec6807c --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/floatingips/doc.go @@ -0,0 +1,71 @@ +/* +package floatingips enables management and retrieval of Floating IPs from the +OpenStack Networking service. + +Example to List Floating IPs + + listOpts := floatingips.ListOpts{ + FloatingNetworkID: "a6917946-38ab-4ffd-a55a-26c0980ce5ee", + } + + allPages, err := floatingips.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allFIPs, err := floatingips.ExtractFloatingIPs(allPages) + if err != nil { + panic(err) + } + + for _, fip := range allFIPs { + fmt.Printf("%+v\n", fip) + } + +Example to Create a Floating IP + + createOpts := floatingips.CreateOpts{ + FloatingNetworkID: "a6917946-38ab-4ffd-a55a-26c0980ce5ee", + } + + fip, err := floatingips.Create(networkingClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Floating IP + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + portID := "76d0a61b-b8e5-490c-9892-4cf674f2bec8" + + updateOpts := floatingips.UpdateOpts{ + PortID: &portID, + } + + fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Disassociate a Floating IP with a Port + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + + updateOpts := floatingips.UpdateOpts{ + PortID: nil, + } + + fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Floating IP + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + err := floatingips.Delete(networkClient, fipID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package floatingips diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 0c628426c0..1c8a8b2f13 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -39,8 +39,8 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { }) } -// CreateOptsBuilder is the interface type must satisfy to be used as Create -// options. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToFloatingIPCreateMap() (map[string]interface{}, error) } @@ -79,10 +79,10 @@ func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { // return 404 error code. // // You must also configure an IP address for the port associated with the PortID -// you have provided - this is what the FixedIP refers to: an IP fixed to a port. -// Because a port might be associated with multiple IP addresses, you can use -// the FixedIP field to associate a particular IP address rather than have the -// API assume for you. If you specify an IP address that is not valid, the +// you have provided - this is what the FixedIP refers to: an IP fixed to a +// port. Because a port might be associated with multiple IP addresses, you can +// use the FixedIP field to associate a particular IP address rather than have +// the API assume for you. If you specify an IP address that is not valid, the // operation will fail and return a 400 error code. If the PortID and FixedIP // are already associated with another resource, the operation will fail and // returns a 409 error code. @@ -102,8 +102,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface type must satisfy to be used as Update -// options. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToFloatingIPUpdateMap() (map[string]interface{}, error) } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index 29d5b5662b..f1af23d4a6 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -12,30 +12,32 @@ import ( // floating IPs can only be defined on networks where the `router:external' // attribute (provided by the external network extension) is set to True. type FloatingIP struct { - // Unique identifier for the floating IP instance. + // ID is the unique identifier for the floating IP instance. ID string `json:"id"` - // UUID of the external network where the floating IP is to be created. + // FloatingNetworkID is the UUID of the external network where the floating + // IP is to be created. FloatingNetworkID string `json:"floating_network_id"` - // Address of the floating IP on the external network. + // FloatingIP is the address of the floating IP on the external network. FloatingIP string `json:"floating_ip_address"` - // UUID of the port on an internal network that is associated with the floating IP. + // PortID is the UUID of the port on an internal network that is associated + // with the floating IP. PortID string `json:"port_id"` - // The specific IP address of the internal port which should be associated - // with the floating IP. + // FixedIP is the specific IP address of the internal port which should be + // associated with the floating IP. FixedIP string `json:"fixed_ip_address"` - // Owner of the floating IP. Only admin users can specify a tenant identifier - // other than its own. + // TenantID is the Owner of the floating IP. Only admin users can specify a + // tenant identifier other than its own. TenantID string `json:"tenant_id"` - // The condition of the API resource. + // Status is the condition of the API resource. Status string `json:"status"` - //The ID of the router used for this Floating-IP + // RouterID is the ID of the router used for this floating IP. RouterID string `json:"router_id"` } @@ -43,7 +45,7 @@ type commonResult struct { gophercloud.Result } -// Extract a result and extracts a FloatingIP resource. +// Extract will extract a FloatingIP resource from a result. func (r commonResult) Extract() (*FloatingIP, error) { var s struct { FloatingIP *FloatingIP `json:"floatingip"` @@ -52,22 +54,26 @@ func (r commonResult) Extract() (*FloatingIP, error) { return s.FloatingIP, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a FloatingIP. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a FloatingIP. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a FloatingIP. type UpdateResult struct { commonResult } -// DeleteResult represents the result of an update operation. +// DeleteResult represents the result of an update operation. Call its +// ExtractErr method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } @@ -78,9 +84,9 @@ type FloatingIPPage struct { pagination.LinkedPageBase } -// NextPageURL is invoked when a paginated collection of floating IPs has reached -// the end of a page and the pager seeks to traverse over a new one. In order -// to do this, it needs to construct the next page's URL. +// NextPageURL is invoked when a paginated collection of floating IPs has +// reached the end of a page and the pager seeks to traverse over a new one. +// In order to do this, it needs to construct the next page's URL. func (r FloatingIPPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"floatingips_links"` @@ -92,15 +98,15 @@ func (r FloatingIPPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// IsEmpty checks whether a NetworkPage struct is empty. +// IsEmpty checks whether a FloatingIPPage struct is empty. func (r FloatingIPPage) IsEmpty() (bool, error) { is, err := ExtractFloatingIPs(r) return len(is) == 0, err } -// ExtractFloatingIPs accepts a Page struct, specifically a FloatingIPPage struct, -// and extracts the elements into a slice of FloatingIP structs. In other words, -// a generic collection is mapped into a relevant slice. +// ExtractFloatingIPs accepts a Page struct, specifically a FloatingIPPage +// struct, and extracts the elements into a slice of FloatingIP structs. In +// other words, a generic collection is mapped into a relevant slice. func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { var s struct { FloatingIPs []FloatingIP `json:"floatingips"` diff --git a/openstack/networking/v2/extensions/layer3/routers/doc.go b/openstack/networking/v2/extensions/layer3/routers/doc.go new file mode 100644 index 0000000000..6ede7f5e17 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/routers/doc.go @@ -0,0 +1,108 @@ +/* +Package routers enables management and retrieval of Routers from the OpenStack +Networking service. + +Example to List Routers + + listOpts := routers.ListOpts{} + allPages, err := routers.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allRouters, err := routers.ExtractRouters(allPages) + if err != nil { + panic(err) + } + + for _, router := range allRoutes { + fmt.Printf("%+v\n", router) + } + +Example to Create a Router + + iTrue := true + gwi := routers.GatewayInfo{ + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + } + + createOpts := routers.CreateOpts{ + Name: "router_1", + AdminStateUp: &iTrue, + GatewayInfo: &gwi, + } + + router, err := routers.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Router + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + routes := []routers.Route{{ + DestinationCIDR: "40.0.1.0/24", + NextHop: "10.1.0.10", + }} + + updateOpts := routers.UpdateOpts{ + Name: "new_name", + Routes: routes, + } + + router, err := routers.Update(networkClient, routerID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Remove all Routes from a Router + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + routes := []routers.Route{} + + updateOpts := routers.UpdateOpts{ + Routes: routes, + } + + router, err := routers.Update(networkClient, routerID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Router + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + err := routers.Delete(networkClient, routerID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Add an Interface to a Router + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + intOpts := routers.AddInterfaceOpts{ + SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1", + } + + interface, err := routers.AddInterface(networkClient, routerID, intOpts).Extract() + if err != nil { + panic(err) + } + +Example to Remove an Interface from a Router + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + intOpts := routers.RemoveInterfaceOpts{ + SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1", + } + + interface, err := routers.RemoveInterface(networkClient, routerID, intOpts).Extract() + if err != nil { + panic(err) + } +*/ +package routers diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index 71b2f627d5..6799d200b7 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -40,10 +40,8 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { }) } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToRouterCreateMap() (map[string]interface{}, error) } @@ -58,6 +56,7 @@ type CreateOpts struct { GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` } +// ToRouterCreateMap builds a create request body from CreateOpts. func (opts CreateOpts) ToRouterCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "router") } @@ -86,6 +85,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToRouterUpdateMap() (map[string]interface{}, error) } @@ -99,6 +100,7 @@ type UpdateOpts struct { Routes []Route `json:"routes"` } +// ToRouterUpdateMap builds an update body based on UpdateOpts. func (opts UpdateOpts) ToRouterUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "router") } @@ -126,21 +128,19 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { return } -// AddInterfaceOptsBuilder is what types must satisfy to be used as AddInterface -// options. +// AddInterfaceOptsBuilder allows extensions to add additional parameters to +// the AddInterface request. type AddInterfaceOptsBuilder interface { ToRouterAddInterfaceMap() (map[string]interface{}, error) } -// AddInterfaceOpts allow you to work with operations that either add -// an internal interface from a router. +// AddInterfaceOpts represents the options for adding an interface to a router. type AddInterfaceOpts struct { SubnetID string `json:"subnet_id,omitempty" xor:"PortID"` PortID string `json:"port_id,omitempty" xor:"SubnetID"` } -// ToRouterAddInterfaceMap allows InterfaceOpts to satisfy the InterfaceOptsBuilder -// interface +// ToRouterAddInterfaceMap builds a request body from AddInterfaceOpts. func (opts AddInterfaceOpts) ToRouterAddInterfaceMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -178,21 +178,21 @@ func AddInterface(c *gophercloud.ServiceClient, id string, opts AddInterfaceOpts return } -// RemoveInterfaceOptsBuilder is what types must satisfy to be used as RemoveInterface -// options. +// RemoveInterfaceOptsBuilder allows extensions to add additional parameters to +// the RemoveInterface request. type RemoveInterfaceOptsBuilder interface { ToRouterRemoveInterfaceMap() (map[string]interface{}, error) } -// RemoveInterfaceOpts allow you to work with operations that either add or remote -// an internal interface from a router. +// RemoveInterfaceOpts represents options for removing an interface from +// a router. type RemoveInterfaceOpts struct { SubnetID string `json:"subnet_id,omitempty" or:"PortID"` PortID string `json:"port_id,omitempty" or:"SubnetID"` } -// ToRouterRemoveInterfaceMap allows RemoveInterfaceOpts to satisfy the RemoveInterfaceOptsBuilder -// interface +// ToRouterRemoveInterfaceMap builds a request body based on +// RemoveInterfaceOpts. func (opts RemoveInterfaceOpts) ToRouterRemoveInterfaceMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index d849d457ad..d6dbb350e6 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -26,28 +26,30 @@ type Route struct { // whenever a router is associated with a subnet, a port for that router // interface is added to the subnet's network. type Router struct { - // Indicates whether or not a router is currently operational. + // Status indicates whether or not a router is currently operational. Status string `json:"status"` - // Information on external gateway for the router. + // GateayInfo provides information on external gateway for the router. GatewayInfo GatewayInfo `json:"external_gateway_info"` - // Administrative state of the router. + // AdminStateUp is the administrative state of the router. AdminStateUp bool `json:"admin_state_up"` - // Whether router is disitrubted or not.. + // Distributed is whether router is disitrubted or not. Distributed bool `json:"distributed"` - // Human readable name for the router. Does not have to be unique. + // Name is the human readable name for the router. It does not have to be + // unique. Name string `json:"name"` - // Unique identifier for the router. + // ID is the unique identifier for the router. ID string `json:"id"` - // Owner of the router. Only admin users can specify a tenant identifier - // other than its own. + // TenantID is the owner of the router. Only admin users can specify a tenant + // identifier other than its own. TenantID string `json:"tenant_id"` + // Routes are a collection of static routes that the router will host. Routes []Route `json:"routes"` } @@ -101,22 +103,26 @@ func (r commonResult) Extract() (*Router, error) { return s.Router, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Router. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Router. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Router. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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 } @@ -125,21 +131,22 @@ type DeleteResult struct { // mentioned above, in order for a router to forward to a subnet, it needs an // interface. type InterfaceInfo struct { - // The ID of the subnet which this interface is associated with. + // SubnetID is the ID of the subnet which this interface is associated with. SubnetID string `json:"subnet_id"` - // The ID of the port that is a part of the subnet. + // PortID is the ID of the port that is a part of the subnet. PortID string `json:"port_id"` - // The UUID of the interface. + // ID is the UUID of the interface. ID string `json:"id"` - // Owner of the interface. + // TenantID is the owner of the interface. TenantID string `json:"tenant_id"` } // InterfaceResult represents the result of interface operations, such as -// AddInterface() and RemoveInterface(). +// AddInterface() and RemoveInterface(). Call its Extract method to interpret +// the result as a InterfaceInfo. type InterfaceResult struct { gophercloud.Result } diff --git a/openstack/networking/v2/extensions/lbaas/members/doc.go b/openstack/networking/v2/extensions/lbaas/members/doc.go new file mode 100644 index 0000000000..bad3324b5e --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas/members/doc.go @@ -0,0 +1,59 @@ +/* +Package members provides information and interaction with Members of the +Load Balancer as a Service extension for the OpenStack Networking service. + +Example to List Members + + listOpts := members.ListOpts{ + ProtocolPort: 80, + } + + allPages, err := members.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allMembers, err := members.ExtractMembers(allPages) + if err != nil { + panic(err) + } + + for _, member := range allMembers { + fmt.Printf("%+v\n", member) + } + +Example to Create a Member + + createOpts := members.CreateOpts{ + Address: "192.168.2.14", + ProtocolPort: 80, + PoolID: "0b266a12-0fdf-4434-bd11-649d84e54bd5" + } + + member, err := members.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Member + + memberID := "46592c54-03f7-40ef-9cdf-b1fcf2775ddf" + + updateOpts := members.UpdateOpts{ + AdminStateUp: gophercloud.Disabled, + } + + member, err := members.Update(networkClient, memberID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Member + + memberID := "46592c54-03f7-40ef-9cdf-b1fcf2775ddf" + err := members.Delete(networkClient, memberID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package members diff --git a/openstack/networking/v2/extensions/lbaas/members/requests.go b/openstack/networking/v2/extensions/lbaas/members/requests.go index 7e7b76885f..1a31288444 100644 --- a/openstack/networking/v2/extensions/lbaas/members/requests.go +++ b/openstack/networking/v2/extensions/lbaas/members/requests.go @@ -26,10 +26,10 @@ type ListOpts struct { } // List returns a Pager which allows you to iterate over a collection of -// pools. It accepts a ListOpts struct, which allows you to filter and sort +// members. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // -// Default policy settings return only those pools that are owned by the +// Default policy settings return only those members that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) @@ -42,23 +42,29 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { }) } +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToLBMemberCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new pool member. type CreateOpts struct { - // The IP address of the member. + // Address is the IP address of the member. Address string `json:"address" required:"true"` - // The port on which the application is hosted. + + // ProtocolPort is the port on which the application is hosted. ProtocolPort int `json:"protocol_port" required:"true"` - // The pool to which this member will belong. + + // PoolID is the pool to which this member will belong. PoolID string `json:"pool_id" required:"true"` - // Only required if the caller has an admin role and wants to create a pool - // for another tenant. + + // TenantID is only required if the caller has an admin role and wants + // to create a pool for another tenant. TenantID string `json:"tenant_id,omitempty"` } +// ToLBMemberCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToLBMemberCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } @@ -81,6 +87,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToLBMemberUpdateMap() (map[string]interface{}, error) } @@ -91,6 +99,7 @@ type UpdateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` } +// ToLBMemberUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToLBMemberUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } diff --git a/openstack/networking/v2/extensions/lbaas/members/results.go b/openstack/networking/v2/extensions/lbaas/members/results.go index 933e1aedbf..804dbe8445 100644 --- a/openstack/networking/v2/extensions/lbaas/members/results.go +++ b/openstack/networking/v2/extensions/lbaas/members/results.go @@ -7,29 +7,30 @@ import ( // Member represents the application running on a backend server. type Member struct { - // The status of the member. Indicates whether the member is operational. + // Status is the status of the member. Indicates whether the member + // is operational. Status string - // Weight of member. + // Weight is the weight of member. Weight int - // The administrative state of the member, which is up (true) or down (false). + // AdminStateUp is the administrative state of the member, which is up + // (true) or down (false). AdminStateUp bool `json:"admin_state_up"` - // Owner of the member. Only an administrative user can specify a tenant ID - // other than its own. + // TenantID is the owner of the member. TenantID string `json:"tenant_id"` - // The pool to which the member belongs. + // PoolID is the pool to which the member belongs. PoolID string `json:"pool_id"` - // The IP address of the member. + // Address is the IP address of the member. Address string - // The port on which the application is hosted. + // ProtocolPort is the port on which the application is hosted. ProtocolPort int `json:"protocol_port"` - // The unique ID for the member. + // ID is the unique ID for the member. ID string } @@ -74,7 +75,7 @@ type commonResult struct { gophercloud.Result } -// Extract is a function that accepts a result and extracts a router. +// Extract is a function that accepts a result and extracts a member. func (r commonResult) Extract() (*Member, error) { var s struct { Member *Member `json:"member"` @@ -83,22 +84,26 @@ func (r commonResult) Extract() (*Member, error) { return s.Member, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Member. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Member. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Member. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the result succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } diff --git a/openstack/networking/v2/extensions/lbaas/monitors/doc.go b/openstack/networking/v2/extensions/lbaas/monitors/doc.go new file mode 100644 index 0000000000..b5c0f29f05 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas/monitors/doc.go @@ -0,0 +1,63 @@ +/* +Package monitors provides information and interaction with the Monitors +of the Load Balancer as a Service extension for the OpenStack Networking +Service. + +Example to List Monitors + + listOpts: monitors.ListOpts{ + Type: "HTTP", + } + + allPages, err := monitors.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allMonitors, err := monitors.ExtractMonitors(allPages) + if err != nil { + panic(err) + } + + for _, monitor := range allMonitors { + fmt.Printf("%+v\n", monitor) + } + +Example to Create a Monitor + + createOpts := monitors.CreateOpts{ + Type: "HTTP", + Delay: 20, + Timeout: 20, + MaxRetries: 5, + URLPath: "/check", + ExpectedCodes: "200-299", + } + + monitor, err := monitors.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Monitor + + monitorID := "681aed03-aadb-43ae-aead-b9016375650a" + + updateOpts := monitors.UpdateOpts{ + Timeout: 30, + } + + monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Member + + monitorID := "681aed03-aadb-43ae-aead-b9016375650a" + err := monitors.Delete(networkClient, monitorID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package monitors diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go index f1b964b65e..9ed0c769c9 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go @@ -31,10 +31,10 @@ type ListOpts struct { } // List returns a Pager which allows you to iterate over a collection of -// routers. It accepts a ListOpts struct, which allows you to filter and sort +// monitors. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // -// Default policy settings return only those routers that are owned by the +// Default policy settings return only those monitors that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) @@ -47,7 +47,7 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { }) } -// MonitorType is the type for all the types of LB monitors +// MonitorType is the type for all the types of LB monitors. type MonitorType string // Constants that represent approved monitoring types. @@ -58,42 +58,52 @@ const ( TypeHTTPS MonitorType = "HTTPS" ) -// CreateOptsBuilder is what types must satisfy to be used as Create -// options. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToLBMonitorCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new health monitor. type CreateOpts struct { - // Required. The type of probe, which is PING, TCP, HTTP, or HTTPS, that is - // sent by the load balancer to verify the member state. + // MonitorType is the type of probe, which is PING, TCP, HTTP, or HTTPS, + // that is sent by the load balancer to verify the member state. Type MonitorType `json:"type" required:"true"` - // Required. The time, in seconds, between sending probes to members. + + // Delay is the time, in seconds, between sending probes to members. Delay int `json:"delay" required:"true"` - // Required. 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 is the 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"` - // Required. Number of permissible ping failures before changing the member's - // status to INACTIVE. Must be a number between 1 and 10. + + // MaxRetries is the number of permissible ping failures before changing the + // member's status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries" required:"true"` - // Required for HTTP(S) types. URI path that will be accessed if monitor type - // is HTTP or HTTPS. + + // URLPath is the URI path that will be accessed if monitor type + // is HTTP or HTTPS. Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` - // Required for HTTP(S) types. The HTTP method used for requests by the - // monitor. If this attribute is not specified, it defaults to "GET". + + // HTTPMethod is the HTTP method used for requests by the monitor. If this + // attribute is not specified, it defaults to "GET". Required for HTTP(S) + // types. HTTPMethod string `json:"http_method,omitempty"` - // Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S) - // monitor. You can either specify a single status like "200", or a range - // like "200-202". + + // ExpectedCodes is the 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. ExpectedCodes string `json:"expected_codes,omitempty"` - // Required for admins. Indicates the owner of the VIP. - TenantID string `json:"tenant_id,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // TenantID is only required if the caller has an admin role and wants + // to create a pool for another tenant. + TenantID string `json:"tenant_id,omitempty"` + + // AdminStateUp denotes whether the monitor is administratively up or down. + AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToLBMonitorCreateMap allows CreateOpts to satisfy the CreateOptsBuilder -// interface +// ToLBMonitorCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToLBMonitorCreateMap() (map[string]interface{}, error) { if opts.Type == TypeHTTP || opts.Type == TypeHTTPS { if opts.URLPath == "" { @@ -146,39 +156,45 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is what types must satisfy to be used as Update -// options. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToLBMonitorUpdateMap() (map[string]interface{}, error) } -// UpdateOpts contains all the values needed to update an existing virtual IP. +// UpdateOpts contains all the values needed to update an existing monitor. // Attributes not listed here but appear in CreateOpts are immutable and cannot // be updated. type UpdateOpts struct { - // The time, in seconds, between sending probes to members. + // Delay is the time, in seconds, between sending probes to members. Delay int `json:"delay,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. + + // Timeout is the 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"` - // Number of permissible ping failures before changing the member's - // status to INACTIVE. Must be a number between 1 and 10. + + // MaxRetries is the number of permissible ping failures before changing the + // member's status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries,omitempty"` - // URI path that will be accessed if monitor type + + // URLPath is the URI path that will be accessed if monitor type // is HTTP or HTTPS. URLPath string `json:"url_path,omitempty"` - // The HTTP method used for requests by the - // monitor. If this attribute is not specified, it defaults to "GET". + + // HTTPMethod is the HTTP method used for requests by the monitor. If this + // attribute is not specified, it defaults to "GET". HTTPMethod string `json:"http_method,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". + + // ExpectedCodes is the expected HTTP codes for a passing HTTP(S) monitor + // You can either specify a single status like "200", or a range like + // "200-202". ExpectedCodes string `json:"expected_codes,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // AdminStateUp denotes whether the monitor is administratively up or down. + AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToLBMonitorUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder -// interface +// ToLBMonitorUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToLBMonitorUpdateMap() (map[string]interface{}, error) { if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout { err := gophercloud.ErrInvalidInput{} @@ -190,7 +206,8 @@ func (opts UpdateOpts) ToLBMonitorUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "health_monitor") } -// Update is an operation which modifies the attributes of the specified monitor. +// Update is an operation which modifies the attributes of the specified +// monitor. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLBMonitorUpdateMap() if err != nil { diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go index 0385942c80..cc99f7cced 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/results.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/results.go @@ -21,46 +21,47 @@ import ( // won't participate in its pool's load balancing. In other words, ALL monitors // must declare the member to be healthy for it to stay ACTIVE. type Monitor struct { - // The unique ID for the VIP. + // ID is the unique ID for the Monitor. ID string - // Monitor name. Does not have to be unique. + // Name is the monitor name. Does not have to be unique. Name string - // Owner of the VIP. Only an administrative user can specify a tenant ID - // other than its own. + // TenantID is the owner of the Monitor. TenantID string `json:"tenant_id"` - // The type of probe sent by the load balancer to verify the member state, - // which is PING, TCP, HTTP, or HTTPS. + // Type is the type of probe sent by the load balancer to verify the member + // state, which is PING, TCP, HTTP, or HTTPS. Type string - // The time, in seconds, between sending probes to members. + // Delay is the time, in seconds, between sending probes to members. Delay int - // 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. + // Timeout is 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. Timeout int - // Number of allowed connection failures before changing the status of the - // member to INACTIVE. A valid value is from 1 to 10. + // MaxRetries is the number of allowed connection failures before changing the + // status of the member to INACTIVE. A valid value is from 1 to 10. MaxRetries int `json:"max_retries"` - // The HTTP method that the monitor uses for requests. + // HTTPMethod is the HTTP method that the monitor uses for requests. HTTPMethod string `json:"http_method"` - // 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 is 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"` - // Expected HTTP codes for a passing HTTP(S) monitor. + // ExpectedCodes is the expected HTTP codes for a passing HTTP(S) monitor. ExpectedCodes string `json:"expected_codes"` - // The administrative state of the health monitor, which is up (true) or down (false). + // AdminStateUp is the administrative state of the health monitor, which is up + // (true) or down (false). AdminStateUp bool `json:"admin_state_up"` - // The status of the health monitor. Indicates whether the health monitor is - // operational. + // Status is the status of the health monitor. Indicates whether the health + // monitor is operational. Status string } @@ -115,22 +116,26 @@ func (r commonResult) Extract() (*Monitor, error) { return s.Monitor, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Monitor. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Monitor. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Monitor. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// DeleteResult represents the result of a delete operation. Call its Extract +// method to determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } diff --git a/openstack/networking/v2/extensions/lbaas/pools/doc.go b/openstack/networking/v2/extensions/lbaas/pools/doc.go new file mode 100644 index 0000000000..25c4204dc9 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas/pools/doc.go @@ -0,0 +1,81 @@ +/* +Package pools provides information and interaction with the Pools of the +Load Balancing as a Service extension for the OpenStack Networking service. + +Example to List Pools + + listOpts := pools.ListOpts{ + SubnetID: "d9bd223b-f1a9-4f98-953b-df977b0f902d", + } + + allPages, err := pools.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allPools, err := pools.ExtractPools(allPages) + if err != nil { + panic(err) + } + + for _, pool := range allPools { + fmt.Printf("%+v\n", pool) + } + +Example to Create a Pool + + createOpts := pools.CreateOpts{ + LBMethod: pools.LBMethodRoundRobin, + Protocol: "HTTP", + Name: "Example pool", + SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", + Provider: "haproxy", + } + + pool, err := pools.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Pool + + poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" + + updateOpts := pools.UpdateOpts{ + LBMethod: pools.LBMethodLeastConnections, + } + + pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Pool + + poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" + err := pools.Delete(networkClient, poolID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Associate a Monitor to a Pool + + poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" + monitorID := "8bbfbe1c-6faa-4d97-abdb-0df6c90df70b" + + pool, err := pools.AssociateMonitor(networkClient, poolID, monitorID).Extract() + if err != nil { + panic(err) + } + +Example to Disassociate a Monitor from a Pool + + poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" + monitorID := "8bbfbe1c-6faa-4d97-abdb-0df6c90df70b" + + pool, err := pools.DisassociateMonitor(networkClient, poolID, monitorID).Extract() + if err != nil { + panic(err) + } +*/ +package pools diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go index 2a75737a8c..b3593548d3 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas/pools/requests.go @@ -43,10 +43,10 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { }) } -// LBMethod is a type used for possible load balancing methods +// LBMethod is a type used for possible load balancing methods. type LBMethod string -// LBProtocol is a type used for possible load balancing protocols +// LBProtocol is a type used for possible load balancing protocols. type LBProtocol string // Supported attributes for create/update operations. @@ -59,8 +59,8 @@ const ( ProtocolHTTPS LBProtocol = "HTTPS" ) -// CreateOptsBuilder is the interface types must satisfy to be used as options -// for the Create function +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToLBPoolCreateMap() (map[string]interface{}, error) } @@ -69,25 +69,29 @@ type CreateOptsBuilder interface { type CreateOpts struct { // Name of the pool. Name string `json:"name" required:"true"` - // The protocol used by the pool members, you can use either + + // Protocol used by the pool members, you can use either // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. Protocol LBProtocol `json:"protocol" required:"true"` - // Only required if the caller has an admin role and wants to create a pool - // for another tenant. + + // TenantID is only required if the caller has an admin role and wants + // to create a pool for another tenant. TenantID string `json:"tenant_id,omitempty"` - // The network on which the members of the pool will be located. Only members - // that are on this network can be added to the pool. + + // SubnetID is the network on which the members of the pool will be located. + // Only members that are on this network can be added to the pool. SubnetID string `json:"subnet_id,omitempty"` - // The algorithm used to distribute load between the members of the pool. The - // current specification supports LBMethodRoundRobin and + + // LBMethod is the algorithm used to distribute load between the members of + // the pool. The current specification supports LBMethodRoundRobin and // LBMethodLeastConnections as valid values for this attribute. LBMethod LBMethod `json:"lb_method" required:"true"` - // The provider of the pool + // Provider of the pool. Provider string `json:"provider,omitempty"` } -// ToLBPoolCreateMap allows CreateOpts to satisfy the CreateOptsBuilder interface +// ToLBPoolCreateMap builds a request body based on CreateOpts. func (opts CreateOpts) ToLBPoolCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } @@ -110,8 +114,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface types must satisfy to be used as options -// for the Update function +// UpdateOptsBuilder allows extensions to add additional parameters ot the +// Update request. type UpdateOptsBuilder interface { ToLBPoolUpdateMap() (map[string]interface{}, error) } @@ -120,13 +124,14 @@ type UpdateOptsBuilder interface { type UpdateOpts struct { // Name of the pool. Name string `json:"name,omitempty"` - // The algorithm used to distribute load between the members of the pool. The - // current specification supports LBMethodRoundRobin and + + // LBMethod is the algorithm used to distribute load between the members of + // the pool. The current specification supports LBMethodRoundRobin and // LBMethodLeastConnections as valid values for this attribute. LBMethod LBMethod `json:"lb_method,omitempty"` } -// ToLBPoolUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder interface +// ToLBPoolUpdateMap builds a request body based on UpdateOpts. func (opts UpdateOpts) ToLBPoolUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go index 2ca1963f27..c2bae82d56 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas/pools/results.go @@ -11,47 +11,48 @@ import ( // method to handle the new requests or connections received on the VIP address. // There is only one pool per virtual IP. type Pool struct { - // The status of the pool. Indicates whether the pool is operational. + // Status of the pool. Indicates whether the pool is operational. Status string - // The load-balancer algorithm, which is round-robin, least-connections, and - // so on. This value, which must be supported, is dependent on the provider. - // Round-robin must be supported. + // LBMethod is the load-balancer algorithm, which is round-robin, + // least-connections, and so on. This value, which must be supported, is + // dependent on the provider. LBMethod string `json:"lb_method"` - // The protocol of the pool, which is TCP, HTTP, or HTTPS. + // Protocol of the pool, which is TCP, HTTP, or HTTPS. Protocol string // Description for the pool. Description string - // The IDs of associated monitors which check the health of the pool members. + // MonitorIDs are the IDs of associated monitors which check the health of + // the pool members. MonitorIDs []string `json:"health_monitors"` - // The network on which the members of the pool will be located. Only members - // that are on this network can be added to the pool. + // SubnetID is the network on which the members of the pool will be located. + // Only members that are on this network can be added to the pool. SubnetID string `json:"subnet_id"` - // Owner of the pool. Only an administrative user can specify a tenant ID - // other than its own. + // TenantID is the owner of the pool. TenantID string `json:"tenant_id"` - // The administrative state of the pool, which is up (true) or down (false). + // AdminStateUp is the administrative state of the pool, which is up + // (true) or down (false). AdminStateUp bool `json:"admin_state_up"` - // Pool name. Does not have to be unique. + // Name of the pool. Name string - // List of member IDs that belong to the pool. + // MemberIDs is the list of member IDs that belong to the pool. MemberIDs []string `json:"members"` - // The unique ID for the pool. + // ID is the unique ID for the pool. ID string - // The ID of the virtual IP associated with this pool + // VIPID is the ID of the virtual IP associated with this pool. VIPID string `json:"vip_id"` - // The provider + // The provider. Provider string } @@ -81,7 +82,7 @@ func (r PoolPage) IsEmpty() (bool, error) { return len(is) == 0, err } -// ExtractPools accepts a Page struct, specifically a RouterPage struct, +// ExtractPools accepts a Page struct, specifically a PoolPage struct, // and extracts the elements into a slice of Router structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPools(r pagination.Page) ([]Pool, error) { @@ -105,27 +106,32 @@ func (r commonResult) Extract() (*Pool, error) { return s.Pool, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Pool. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Pool. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Pool. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to interpret it as a Pool. type DeleteResult struct { gophercloud.ErrResult } -// AssociateResult represents the result of an association operation. +// AssociateResult represents the result of an association operation. Call its Extract +// method to interpret it as a Pool. type AssociateResult struct { commonResult } diff --git a/openstack/networking/v2/extensions/lbaas/vips/doc.go b/openstack/networking/v2/extensions/lbaas/vips/doc.go new file mode 100644 index 0000000000..7fd861044b --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas/vips/doc.go @@ -0,0 +1,65 @@ +/* +Package vips provides information and interaction with the Virtual IPs of the +Load Balancing as a Service extension for the OpenStack Networking service. + +Example to List Virtual IPs + + listOpts := vips.ListOpts{ + SubnetID: "d9bd223b-f1a9-4f98-953b-df977b0f902d", + } + + allPages, err := vips.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allVIPs, err := vips.ExtractVIPs(allPages) + if err != nil { + panic(err) + } + + for _, vip := range allVIPs { + fmt.Printf("%+v\n", vip) + } + +Example to Create a Virtual IP + + createOpts := vips.CreateOpts{ + Protocol: "HTTP", + Name: "NewVip", + AdminStateUp: gophercloud.Enabled, + SubnetID: "8032909d-47a1-4715-90af-5153ffe39861", + PoolID: "61b1f87a-7a21-4ad3-9dda-7f81d249944f", + ProtocolPort: 80, + Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, + } + + vip, err := vips.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Virtual IP + + vipID := "93f1bad4-0423-40a8-afac-3fc541839912" + + i1000 := 1000 + updateOpts := vips.UpdateOpts{ + ConnLimit: &i1000, + Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, + } + + vip, err := vips.Update(networkClient, vipID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Virtual IP + + vipID := "93f1bad4-0423-40a8-afac-3fc541839912" + err := vips.Delete(networkClient, vipID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package vips diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go index f89d769adc..53b81bfdb9 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/requests.go +++ b/openstack/networking/v2/extensions/lbaas/vips/requests.go @@ -29,10 +29,10 @@ type ListOpts struct { } // List returns a Pager which allows you to iterate over a collection of -// routers. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. +// Virtual IPs. It accepts a ListOpts struct, which allows you to filter and +// sort the returned collection for greater efficiency. // -// Default policy settings return only those routers that are owned by the +// Default policy settings return only those virtual IPs that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { q, err := gophercloud.BuildQueryString(&opts) @@ -45,43 +45,54 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { }) } -// CreateOptsBuilder is what types must satisfy to be used as Create -// options. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create Request. type CreateOptsBuilder interface { ToVIPCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new virtual IP. type CreateOpts struct { - // Human-readable name for the VIP. Does not have to be unique. + // Name is the human-readable name for the VIP. Does not have to be unique. Name string `json:"name" required:"true"` - // The network on which to allocate the VIP's address. A tenant can - // only create VIPs on networks authorized by policy (e.g. networks that + + // SubnetID is the network on which to allocate the VIP's address. A tenant + // can only create VIPs on networks authorized by policy (e.g. networks that // belong to them or networks that are shared). SubnetID string `json:"subnet_id" required:"true"` - // The protocol - can either be TCP, HTTP or HTTPS. + + // Protocol - can either be TCP, HTTP or HTTPS. Protocol string `json:"protocol" required:"true"` - // The port on which to listen for client traffic. + + // ProtocolPort is the port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` - // The ID of the pool with which the VIP is associated. + + // PoolID is the ID of the pool with which the VIP is associated. PoolID string `json:"pool_id" required:"true"` - // Required for admins. Indicates the owner of the VIP. + + // TenantID is only required if the caller has an admin role and wants + // to create a pool for another tenant. TenantID string `json:"tenant_id,omitempty"` - // The IP address of the VIP. + + // Address is the IP address of the VIP. Address string `json:"address,omitempty"` - // Human-readable description for the VIP. + + // Description is the human-readable description for the VIP. Description string `json:"description,omitempty"` + + // Persistence is the the of session persistence to use. // Omit this field to prevent session persistence. Persistence *SessionPersistence `json:"session_persistence,omitempty"` - // The maximum number of connections allowed for the VIP. + + // ConnLimit is the maximum number of connections allowed for the VIP. ConnLimit *int `json:"connection_limit,omitempty"` - // The administrative state of the VIP. A valid value is true (UP) - // or false (DOWN). + + // AdminStateUp is the administrative state of the VIP. A valid value is + // true (UP) or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToVIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder -// interface +// ToVIPCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToVIPCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "vip") } @@ -113,8 +124,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is what types must satisfy to be used as Update -// options. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToVIPUpdateMap() (map[string]interface{}, error) } @@ -123,22 +134,28 @@ type UpdateOptsBuilder interface { // Attributes not listed here but appear in CreateOpts are immutable and cannot // be updated. type UpdateOpts struct { - // Human-readable name for the VIP. Does not have to be unique. + // Name is the human-readable name for the VIP. Does not have to be unique. Name *string `json:"name,omitempty"` - // The ID of the pool with which the VIP is associated. + + // PoolID is the ID of the pool with which the VIP is associated. PoolID *string `json:"pool_id,omitempty"` - // Human-readable description for the VIP. + + // Description is the human-readable description for the VIP. Description *string `json:"description,omitempty"` + + // Persistence is the the of session persistence to use. // Omit this field to prevent session persistence. Persistence *SessionPersistence `json:"session_persistence,omitempty"` - // The maximum number of connections allowed for the VIP. + + // ConnLimit is the maximum number of connections allowed for the VIP. ConnLimit *int `json:"connection_limit,omitempty"` - // The administrative state of the VIP. A valid value is true (UP) - // or false (DOWN). + + // AdminStateUp is the administrative state of the VIP. A valid value is + // true (UP) or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToVIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder interface +// ToVIPUpdateMap builds a request body based on UpdateOpts. func (opts UpdateOpts) ToVIPUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "vip") } diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go index 7ac7e79be7..cb0994a7b2 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/results.go +++ b/openstack/networking/v2/extensions/lbaas/vips/results.go @@ -21,10 +21,10 @@ import ( // 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 is the type of persistence mode. Type string `json:"type"` - // Name of cookie if persistence mode is set appropriately + // CookieName is the name of cookie if persistence mode is set appropriately. CookieName string `json:"cookie_name,omitempty"` } @@ -34,54 +34,55 @@ type SessionPersistence struct { // This entity is sometimes known in LB products under the name of a "virtual // server", a "vserver" or a "listener". type VirtualIP struct { - // The unique ID for the VIP. + // ID is the unique ID for the VIP. ID string `json:"id"` - // Owner of the VIP. Only an admin user can specify a tenant ID other than its own. + // TenantID is the owner of the VIP. TenantID string `json:"tenant_id"` - // Human-readable name for the VIP. Does not have to be unique. + // Name is the human-readable name for the VIP. Does not have to be unique. Name string `json:"name"` - // Human-readable description for the VIP. + // Description is the human-readable description for the VIP. Description string `json:"description"` - // The ID of the subnet on which to allocate the VIP address. + // SubnetID is the ID of the subnet on which to allocate the VIP address. SubnetID string `json:"subnet_id"` - // The IP address of the VIP. + // Address is the IP address of the VIP. Address string `json:"address"` - // The protocol of the VIP address. A valid value is TCP, HTTP, or HTTPS. + // Protocol of the VIP address. A valid value is TCP, HTTP, or HTTPS. Protocol string `json:"protocol"` - // The port on which to listen to client traffic that is associated with the - // VIP address. A valid value is from 0 to 65535. + // ProtocolPort is the port on which to listen to client traffic that is + // associated with the VIP address. A valid value is from 0 to 65535. ProtocolPort int `json:"protocol_port"` - // The ID of the pool with which the VIP is associated. + // PoolID is the ID of the pool with which the VIP is associated. PoolID string `json:"pool_id"` - // The ID of the port which belongs to the load balancer + // PortID is the ID of the port which belongs to the load balancer. PortID string `json:"port_id"` - // Indicates whether connections in the same session will be processed by the - // same pool member or not. + // Persistence indicates whether connections in the same session will be + // processed by the same pool member or not. Persistence SessionPersistence `json:"session_persistence"` - // The maximum number of connections allowed for the VIP. Default is -1, - // meaning no limit. + // ConnLimit is the maximum number of connections allowed for the VIP. + // Default is -1, meaning no limit. ConnLimit int `json:"connection_limit"` - // The administrative state of the VIP. A valid value is true (UP) or false (DOWN). + // AdminStateUp is the administrative state of the VIP. A valid value is + // true (UP) or false (DOWN). AdminStateUp bool `json:"admin_state_up"` - // The status of the VIP. Indicates whether the VIP is operational. + // Status is the status of the VIP. Indicates whether the VIP is operational. Status string `json:"status"` } // VIPPage is the page returned by a pager when traversing over a -// collection of routers. +// collection of virtual IPs. type VIPPage struct { pagination.LinkedPageBase } @@ -121,7 +122,7 @@ type commonResult struct { gophercloud.Result } -// Extract is a function that accepts a result and extracts a router. +// Extract is a function that accepts a result and extracts a VirtualIP. func (r commonResult) Extract() (*VirtualIP, error) { var s struct { VirtualIP *VirtualIP `json:"vip" json:"vip"` @@ -130,22 +131,26 @@ func (r commonResult) Extract() (*VirtualIP, error) { return s.VirtualIP, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a VirtualIP type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a VirtualIP type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a VirtualIP type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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/lbaas_v2/doc.go b/openstack/networking/v2/extensions/lbaas_v2/doc.go index 247a75facb..ec7f9d6f04 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/doc.go @@ -1,5 +1,3 @@ // Package lbaas_v2 provides information and interaction with the Load Balancer // as a Service v2 extension for the OpenStack Networking service. -// lbaas v2 api docs: http://developer.openstack.org/api-ref-networking-v2-ext.html#lbaas-v2.0 -// lbaas v2 api schema: https://github.com/openstack/neutron-lbaas/blob/master/neutron_lbaas/extensions/loadbalancerv2.py package lbaas_v2 diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/doc.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/doc.go new file mode 100644 index 0000000000..108cdb03d8 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/doc.go @@ -0,0 +1,63 @@ +/* +Package listeners provides information and interaction with Listeners of the +LBaaS v2 extension for the OpenStack Networking service. + +Example to List Listeners + + listOpts := listeners.ListOpts{ + LoadbalancerID : "ca430f80-1737-4712-8dc6-3f640d55594b", + } + + allPages, err := listeners.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allListeners, err := listeners.ExtractListeners(allPages) + if err != nil { + panic(err) + } + + for _, listener := range allListeners { + fmt.Printf("%+v\n", listener) + } + +Example to Create a Listener + + createOpts := listeners.CreateOpts{ + Protocol: "TCP", + Name: "db", + LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", + AdminStateUp: gophercloud.Enabled, + DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", + ProtocolPort: 3306, + } + + listener, err := listeners.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Listener + + listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" + + i1001 := 1001 + updateOpts := listeners.UpdateOpts{ + ConnLimit: &i1001, + } + + listener, err := listeners.Update(networkClient, listenerID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Listener + + listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := listeners.Delete(networkClient, listenerID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package listeners diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index 4a78447902..625748fd25 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// Type Protocol represents a listener protocol. type Protocol string // Supported attributes for create/update operations. @@ -48,10 +49,10 @@ func (opts ListOpts) ToListenerListQuery() (string, error) { } // List returns a Pager which allows you to iterate over a collection of -// routers. It accepts a ListOpts struct, which allows you to filter and sort +// listeners. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. // -// Default policy settings return only those routers that are owned by the +// Default policy settings return only those listeners that are owned by the // tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) @@ -67,43 +68,51 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToListenerCreateMap() (map[string]interface{}, error) } -// CreateOpts is the common options struct used in this package's Create -// operation. +// CreateOpts represents options for creating a listener. type CreateOpts struct { // The load balancer on which to provision this listener. LoadbalancerID string `json:"loadbalancer_id" required:"true"` + // The protocol - can either be TCP, HTTP or HTTPS. Protocol Protocol `json:"protocol" required:"true"` + // The port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` - // Indicates the owner of the Listener. Required for admins. + + // TenantID is only required if the caller has an admin role and wants + // to create a pool for another tenant. TenantID string `json:"tenant_id,omitempty"` + // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name,omitempty"` + // The ID of the default pool with which the Listener is associated. DefaultPoolID string `json:"default_pool_id,omitempty"` + // Human-readable description for the Listener. Description string `json:"description,omitempty"` + // The maximum number of connections allowed for the Listener. ConnLimit *int `json:"connection_limit,omitempty"` - // A reference to a container of TLS secrets. + + // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` + // A list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs,omitempty"` + // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToListenerCreateMap casts a CreateOpts struct to a map. +// ToListenerCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "listener") } @@ -131,38 +140,41 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToListenerUpdateMap() (map[string]interface{}, error) } -// UpdateOpts is the common options struct used in this package's Update -// operation. +// UpdateOpts represents options for updating a Listener. type UpdateOpts struct { // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name,omitempty"` + // Human-readable description for the Listener. Description string `json:"description,omitempty"` + // The maximum number of connections allowed for the Listener. ConnLimit *int `json:"connection_limit,omitempty"` - // A reference to a container of TLS secrets. + + // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` - // A list of references to TLS secrets. + + // A list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs,omitempty"` + // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToListenerUpdateMap casts a UpdateOpts struct to a map. +// ToListenerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "listener") } -// Update is an operation which modifies the attributes of the specified Listener. +// Update is an operation which modifies the attributes of the specified +// Listener. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToListenerUpdateMap() if err != nil { diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go index aa8ed1bde5..e0c134ed51 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go @@ -16,40 +16,53 @@ type LoadBalancerID struct { type Listener struct { // The unique ID for the Listener. ID string `json:"id"` - // Owner of the Listener. Only an admin user can specify a tenant ID other than its own. + + // Owner of the Listener. TenantID string `json:"tenant_id"` + // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name"` + // Human-readable description for the Listener. Description string `json:"description"` + // The protocol to loadbalance. A valid value is TCP, HTTP, or HTTPS. Protocol string `json:"protocol"` + // The port on which to listen to client traffic that is associated with the // Loadbalancer. A valid value is from 0 to 65535. ProtocolPort int `json:"protocol_port"` + // The UUID of default pool. Must have compatible protocol with listener. DefaultPoolID string `json:"default_pool_id"` + // A list of load balancer IDs. Loadbalancers []LoadBalancerID `json:"loadbalancers"` - // The maximum number of connections allowed for the Loadbalancer. Default is -1, - // meaning no limit. + + // The maximum number of connections allowed for the Loadbalancer. + // Default is -1, meaning no limit. ConnLimit int `json:"connection_limit"` + // The list of references to TLS secrets. SniContainerRefs []string `json:"sni_container_refs"` - // Optional. A reference to a container of TLS secrets. + + // A reference to a Barbican container of TLS secrets. DefaultTlsContainerRef string `json:"default_tls_container_ref"` + // The administrative state of the Listener. A valid value is true (UP) or false (DOWN). - AdminStateUp bool `json:"admin_state_up"` - Pools []pools.Pool `json:"pools"` + AdminStateUp bool `json:"admin_state_up"` + + // Pools are the pools which are part of this listener. + Pools []pools.Pool `json:"pools"` } // ListenerPage is the page returned by a pager when traversing over a -// collection of routers. +// collection of listeners. type ListenerPage struct { pagination.LinkedPageBase } -// NextPageURL is invoked when a paginated collection of routers has reached +// NextPageURL is invoked when a paginated collection of listeners 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 ListenerPage) NextPageURL() (string, error) { @@ -63,7 +76,7 @@ func (r ListenerPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// IsEmpty checks whether a RouterPage struct is empty. +// IsEmpty checks whether a ListenerPage struct is empty. func (r ListenerPage) IsEmpty() (bool, error) { is, err := ExtractListeners(r) return len(is) == 0, err @@ -84,7 +97,7 @@ type commonResult struct { gophercloud.Result } -// Extract is a function that accepts a result and extracts a router. +// Extract is a function that accepts a result and extracts a listener. func (r commonResult) Extract() (*Listener, error) { var s struct { Listener *Listener `json:"listener"` @@ -93,22 +106,26 @@ func (r commonResult) Extract() (*Listener, error) { return s.Listener, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Listener. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Listener. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Listener. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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/lbaas_v2/loadbalancers/doc.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go new file mode 100644 index 0000000000..eea43391a8 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go @@ -0,0 +1,71 @@ +/* +Package loadbalancers provides information and interaction with Load Balancers +of the LBaaS v2 extension for the OpenStack Networking service. + +Example to List Load Balancers + + listOpts := loadbalancers.ListOpts{ + Provider: "haproxy", + } + + allPages, err := loadbalancers.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) + if err != nil { + panic(err) + } + + for _, lb := range allLoadbalancers { + fmt.Printf("%+v\n", lb) + } + +Example to Create a Load Balancer + + createOpts := loadbalancers.CreateOpts{ + Name: "db_lb", + AdminStateUp: gophercloud.Enabled, + VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + VipAddress: "10.30.176.48", + Flavor: "medium", + Provider: "haproxy", + } + + lb, err := loadbalancers.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Load Balancer + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + + i1001 := 1001 + updateOpts := loadbalancers.UpdateOpts{ + Name: "new-name", + } + + lb, err := loadbalancers.Update(networkClient, lbID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Load Balancers + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := loadbalancers.Delete(networkClient, lbID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Get the Status of a Load Balancer + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + status, err := loadbalancers.GetStatuses(networkClient, LBID).Extract() + if err != nil { + panic(err) + } +*/ +package loadbalancers diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index bc4a3c69a1..839776dd28 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -42,11 +42,11 @@ func (opts ListOpts) ToLoadBalancerListQuery() (string, error) { } // List returns a Pager which allows you to iterate over a collection of -// routers. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. +// load balancers. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. // -// Default policy settings return only those routers that are owned by the -// tenant who submits the request, unless an admin user submits the request. +// Default policy settings return only those load balancers that are owned by +// the tenant who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { @@ -61,10 +61,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToLoadBalancerCreateMap() (map[string]interface{}, error) } @@ -72,29 +70,36 @@ type CreateOptsBuilder interface { // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { - // Optional. Human-readable name for the Loadbalancer. Does not have to be unique. + // Human-readable name for the Loadbalancer. Does not have to be unique. Name string `json:"name,omitempty"` - // Optional. Human-readable description for the Loadbalancer. + + // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` - // Required. The network on which to allocate the Loadbalancer's address. A tenant can - // only create Loadbalancers on networks authorized by policy (e.g. networks that - // belong to them or networks that are shared). + + // The network on which to allocate the Loadbalancer's address. A tenant can + // only create Loadbalancers on networks authorized by policy (e.g. networks + // that belong to them or networks that are shared). VipSubnetID string `json:"vip_subnet_id" required:"true"` - // Required for admins. The UUID of the tenant who owns the Loadbalancer. - // Only administrative users can specify a tenant UUID other than their own. + + // The UUID of the tenant who owns the Loadbalancer. Only administrative users + // can specify a tenant UUID other than their own. TenantID string `json:"tenant_id,omitempty"` - // Optional. The IP address of the Loadbalancer. + + // The IP address of the Loadbalancer. VipAddress string `json:"vip_address,omitempty"` - // Optional. The administrative state of the Loadbalancer. A valid value is true (UP) + + // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` - // Optional. The UUID of a flavor. + + // The UUID of a flavor. Flavor string `json:"flavor,omitempty"` - // Optional. The name of the provider. + + // The name of the provider. Provider string `json:"provider,omitempty"` } -// ToLoadBalancerCreateMap casts a CreateOpts struct to a map. +// ToLoadBalancerCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "loadbalancer") } @@ -103,9 +108,6 @@ func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) // configuration defined in the CreateOpts struct. Once the request is // validated and progress has started on the provisioning process, a // CreateResult will be returned. -// -// Users with an admin role can create loadbalancers on behalf of other tenants by -// specifying a TenantID attribute different than their own. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLoadBalancerCreateMap() if err != nil { @@ -122,10 +124,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToLoadBalancerUpdateMap() (map[string]interface{}, error) } @@ -133,21 +133,24 @@ type UpdateOptsBuilder interface { // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { - // Optional. Human-readable name for the Loadbalancer. Does not have to be unique. + // Human-readable name for the Loadbalancer. Does not have to be unique. Name string `json:"name,omitempty"` - // Optional. Human-readable description for the Loadbalancer. + + // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` - // Optional. The administrative state of the Loadbalancer. A valid value is true (UP) + + // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToLoadBalancerUpdateMap casts a UpdateOpts struct to a map. +// ToLoadBalancerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "loadbalancer") } -// Update is an operation which modifies the attributes of the specified LoadBalancer. +// Update is an operation which modifies the attributes of the specified +// LoadBalancer. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToLoadBalancerUpdateMap() if err != nil { @@ -160,12 +163,14 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR return } -// Delete will permanently delete a particular LoadBalancer based on its unique ID. +// Delete will permanently delete a particular LoadBalancer based on its +// unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } +// GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) return diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go index 4423c2460c..9f8f19d7c5 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go @@ -6,50 +6,67 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// LoadBalancer is the primary load balancing configuration object that specifies -// the virtual IP address on which client traffic is received, as well +// LoadBalancer is the primary load balancing configuration object that +// specifies the virtual IP address on which client traffic is received, as well // as other details such as the load balancing method to be use, protocol, etc. type LoadBalancer struct { // Human-readable description for the Loadbalancer. Description string `json:"description"` - // The administrative state of the Loadbalancer. A valid value is true (UP) or false (DOWN). + + // The administrative state of the Loadbalancer. + // A valid value is true (UP) or false (DOWN). AdminStateUp bool `json:"admin_state_up"` - // Owner of the LoadBalancer. Only an admin user can specify a tenant ID other than its own. + + // Owner of the LoadBalancer. TenantID string `json:"tenant_id"` - // The provisioning status of the LoadBalancer. This value is ACTIVE, PENDING_CREATE or ERROR. + + // The provisioning status of the LoadBalancer. + // This value is ACTIVE, PENDING_CREATE or ERROR. ProvisioningStatus string `json:"provisioning_status"` + // The IP address of the Loadbalancer. VipAddress string `json:"vip_address"` + // The UUID of the port associated with the IP address. VipPortID string `json:"vip_port_id"` - // The UUID of the subnet on which to allocate the virtual IP for the Loadbalancer address. + + // The UUID of the subnet on which to allocate the virtual IP for the + // Loadbalancer address. VipSubnetID string `json:"vip_subnet_id"` + // The unique ID for the LoadBalancer. ID string `json:"id"` + // The operating status of the LoadBalancer. This value is ONLINE or OFFLINE. OperatingStatus string `json:"operating_status"` + // Human-readable name for the LoadBalancer. Does not have to be unique. Name string `json:"name"` + // The UUID of a flavor if set. Flavor string `json:"flavor"` + // The name of the provider. - Provider string `json:"provider"` + Provider string `json:"provider"` + + // Listeners are the listeners related to this Loadbalancer. Listeners []listeners.Listener `json:"listeners"` } +// StatusTree represents the status of a loadbalancer. type StatusTree struct { Loadbalancer *LoadBalancer `json:"loadbalancer"` } // LoadBalancerPage is the page returned by a pager when traversing over a -// collection of routers. +// collection of load balancers. type LoadBalancerPage struct { pagination.LinkedPageBase } -// NextPageURL is invoked when a paginated collection of routers 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. +// NextPageURL is invoked when a paginated collection of load balancers 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 LoadBalancerPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"loadbalancers_links"` @@ -67,9 +84,9 @@ func (p LoadBalancerPage) IsEmpty() (bool, error) { return len(is) == 0, err } -// ExtractLoadBalancers accepts a Page struct, specifically a LoadbalancerPage struct, -// and extracts the elements into a slice of LoadBalancer structs. In other words, -// a generic collection is mapped into a relevant slice. +// ExtractLoadBalancers accepts a Page struct, specifically a LoadbalancerPage +// struct, and extracts the elements into a slice of LoadBalancer structs. In +// other words, a generic collection is mapped into a relevant slice. func ExtractLoadBalancers(r pagination.Page) ([]LoadBalancer, error) { var s struct { LoadBalancers []LoadBalancer `json:"loadbalancers"` @@ -82,7 +99,7 @@ type commonResult struct { gophercloud.Result } -// Extract is a function that accepts a result and extracts a router. +// Extract is a function that accepts a result and extracts a loadbalancer. func (r commonResult) Extract() (*LoadBalancer, error) { var s struct { LoadBalancer *LoadBalancer `json:"loadbalancer"` @@ -91,11 +108,14 @@ func (r commonResult) Extract() (*LoadBalancer, error) { return s.LoadBalancer, err } +// GetStatusesResult represents the result of a GetStatuses operation. +// Call its Extract method to interpret it as a StatusTree. type GetStatusesResult struct { gophercloud.Result } -// Extract is a function that accepts a result and extracts a Loadbalancer. +// Extract is a function that accepts a result and extracts the status of +// a Loadbalancer. func (r GetStatusesResult) Extract() (*StatusTree, error) { var s struct { Statuses *StatusTree `json:"statuses"` @@ -104,22 +124,26 @@ func (r GetStatusesResult) Extract() (*StatusTree, error) { return s.Statuses, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a LoadBalancer. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a LoadBalancer. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a LoadBalancer. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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/lbaas_v2/monitors/doc.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/doc.go new file mode 100644 index 0000000000..6ed8c8fb5f --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/doc.go @@ -0,0 +1,69 @@ +/* +Package monitors provides information and interaction with Monitors +of the LBaaS v2 extension for the OpenStack Networking service. + +Example to List Monitors + + listOpts := monitors.ListOpts{ + PoolID: "c79a4468-d788-410c-bf79-9a8ef6354852", + } + + allPages, err := monitors.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allMonitors, err := monitors.ExtractMonitors(allPages) + if err != nil { + panic(err) + } + + for _, monitor := range allMonitors { + fmt.Printf("%+v\n", monitor) + } + +Example to Create a Monitor + + createOpts := monitors.CreateOpts{ + Type: "HTTP", + Name: "db", + PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", + Delay: 20, + Timeout: 10, + MaxRetries: 5, + URLPath: "/check", + ExpectedCodes: "200-299", + } + + monitor, err := monitors.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Monitor + + monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" + + updateOpts := monitors.UpdateOpts{ + Name: "NewHealthmonitorName", + Delay: 3, + Timeout: 20, + MaxRetries: 10, + URLPath: "/another_check", + ExpectedCodes: "301", + } + + monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Monitor + + monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := monitors.Delete(networkClient, monitorID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package monitors diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go index 1e776bfeed..6d9ab8ba79 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go @@ -79,10 +79,8 @@ var ( errDelayMustGETimeout = fmt.Errorf("Delay must be greater than or equal to timeout") ) -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// List request. type CreateOptsBuilder interface { ToMonitorCreateMap() (map[string]interface{}, error) } @@ -90,37 +88,50 @@ type CreateOptsBuilder interface { // CreateOpts is the common options struct used in this package's Create // operation. type CreateOpts struct { - // Required. The Pool to Monitor. + // The Pool to Monitor. PoolID string `json:"pool_id" required:"true"` - // Required. The type of probe, which is PING, TCP, HTTP, or HTTPS, that is + + // The type of probe, which is PING, TCP, HTTP, or HTTPS, that is // sent by the load balancer to verify the member state. Type string `json:"type" required:"true"` - // Required. The time, in seconds, between sending probes to members. + + // The time, in seconds, between sending probes to members. Delay int `json:"delay" required:"true"` - // Required. Maximum number of seconds for a Monitor to wait for a ping reply + + // 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"` - // Required. Number of permissible ping failures before changing the member's + + // Number of permissible ping failures before changing the member's // status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries" required:"true"` - // Required for HTTP(S) types. URI path that will be accessed if Monitor type - // is HTTP or HTTPS. + + // URI path that will be accessed if Monitor type is HTTP or HTTPS. + // Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` - // Required for HTTP(S) types. The HTTP method used for requests by the - // Monitor. If this attribute is not specified, it defaults to "GET". + + // The HTTP method used for requests by the Monitor. If this attribute + // is not specified, it defaults to "GET". Required for HTTP(S) types. HTTPMethod string `json:"http_method,omitempty"` - // Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S) - // Monitor. You can either specify a single status like "200", or a range - // like "200-202". + + // 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. ExpectedCodes string `json:"expected_codes,omitempty"` - // Indicates the owner of the Loadbalancer. Required for admins. + + // The UUID of the tenant who owns the Monitor. Only administrative users + // can specify a tenant UUID other than their own. TenantID string `json:"tenant_id,omitempty"` - // Optional. The Name of the Monitor. - Name string `json:"name,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // The Name of the Monitor. + Name string `json:"name,omitempty"` + + // The administrative state of the Monitor. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToMonitorCreateMap casts a CreateOpts struct to a map. +// ToMonitorCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "healthmonitor") if err != nil { @@ -173,10 +184,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToMonitorUpdateMap() (map[string]interface{}, error) } @@ -184,35 +193,45 @@ type UpdateOptsBuilder interface { // UpdateOpts is the common options struct used in this package's Update // operation. type UpdateOpts struct { - // Required. The time, in seconds, between sending probes to members. + // The time, in seconds, between sending probes to members. Delay int `json:"delay,omitempty"` - // Required. Maximum number of seconds for a Monitor to wait for a ping reply + + // 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"` - // Required. Number of permissible ping failures before changing the member's + + // Number of permissible ping failures before changing the member's // status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries,omitempty"` - // Required for HTTP(S) types. URI path that will be accessed if Monitor type - // is HTTP or HTTPS. + + // URI path that will be accessed if Monitor type is HTTP or HTTPS. + // Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` - // Required for HTTP(S) types. The HTTP method used for requests by the - // Monitor. If this attribute is not specified, it defaults to "GET". + + // The HTTP method used for requests by the Monitor. If this attribute + // is not specified, it defaults to "GET". Required for HTTP(S) types. HTTPMethod string `json:"http_method,omitempty"` - // Required for HTTP(S) types. Expected HTTP codes for a passing HTTP(S) - // Monitor. You can either specify a single status like "200", or a range - // like "200-202". + + // 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. ExpectedCodes string `json:"expected_codes,omitempty"` - // Optional. The Name of the Monitor. - Name string `json:"name,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // The Name of the Monitor. + Name string `json:"name,omitempty"` + + // The administrative state of the Monitor. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToMonitorUpdateMap casts a UpdateOpts struct to a map. +// ToMonitorUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "healthmonitor") } -// Update is an operation which modifies the attributes of the specified Monitor. +// Update is an operation which modifies the attributes of the specified +// Monitor. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToMonitorUpdateMap() if err != nil { diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go index 05dcf477bb..ea832cc5d0 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go @@ -31,8 +31,7 @@ type Monitor struct { // The Name of the Monitor. Name string `json:"name"` - // Only an administrative user can specify a tenant ID - // other than its own. + // TenantID is the owner of the Monitor. TenantID string `json:"tenant_id"` // The type of probe sent by the load balancer to verify the member state, @@ -43,7 +42,8 @@ type Monitor struct { Delay int `json:"delay"` // 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. + // established before it times out. This value must be less than the delay + // value. Timeout int `json:"timeout"` // Number of allowed connection failures before changing the status of the @@ -60,7 +60,8 @@ type Monitor struct { // Expected HTTP codes for a passing HTTP(S) monitor. ExpectedCodes string `json:"expected_codes"` - // The administrative state of the health monitor, which is up (true) or down (false). + // The administrative state of the health monitor, which is up (true) or + // down (false). AdminStateUp bool `json:"admin_state_up"` // The status of the health monitor. Indicates whether the health monitor is @@ -123,22 +124,26 @@ func (r commonResult) Extract() (*Monitor, error) { return s.Monitor, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Monitor. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Monitor. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Monitor. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the result succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go b/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go new file mode 100644 index 0000000000..2d57ed4393 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go @@ -0,0 +1,124 @@ +/* +Package pools provides information and interaction with Pools and +Members of the LBaaS v2 extension for the OpenStack Networking service. + +Example to List Pools + + listOpts := pools.ListOpts{ + LoadbalancerID: "c79a4468-d788-410c-bf79-9a8ef6354852", + } + + allPages, err := pools.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allPools, err := pools.ExtractMonitors(allPages) + if err != nil { + panic(err) + } + + for _, pools := range allPools { + fmt.Printf("%+v\n", pool) + } + +Example to Create a Pool + + createOpts := pools.CreateOpts{ + LBMethod: pools.LBMethodRoundRobin, + Protocol: "HTTP", + Name: "Example pool", + LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", + } + + pool, err := pools.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Pool + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + + updateOpts := pools.UpdateOpts{ + Name: "new-name", + } + + pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Pool + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := pools.Delete(networkClient, poolID).ExtractErr() + if err != nil { + panic(err) + } + +Example to List Pool Members + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + + listOpts := pools.ListMemberOpts{ + ProtocolPort: 80, + } + + allPages, err := pools.ListMembers(networkClient, poolID, listOpts).AllPages() + if err != nil { + panic(err) + } + + allMembers, err := pools.ExtractMembers(allPages) + if err != nil { + panic(err) + } + + for _, member := allMembers { + fmt.Printf("%+v\n", member) + } + +Example to Create a Member + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + + createOpts := pools.CreateMemberOpts{ + Name: "db", + SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", + Address: "10.0.2.11", + ProtocolPort: 80, + Weight: 10, + } + + member, err := pools.CreateMember(networkClient, poolID, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Member + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" + + updateOpts := pools.UpdateMemberOpts{ + Name: "new-name", + Weight: 4, + } + + member, err := pools.UpdateMember(networkClient, poolID, memberID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Member + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" + + err := pools.DeleteMember(networkClient, poolID, memberID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package pools diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index 093df6ad0f..2173ee8171 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -71,10 +71,8 @@ const ( ProtocolHTTPS Protocol = "HTTPS" ) -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToPoolCreateMap() (map[string]interface{}, error) } @@ -86,30 +84,39 @@ type CreateOpts struct { // current specification supports LBMethodRoundRobin, LBMethodLeastConnections // and LBMethodSourceIp as valid values for this attribute. LBMethod LBMethod `json:"lb_algorithm" required:"true"` + // The protocol used by the pool members, you can use either // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. Protocol Protocol `json:"protocol" required:"true"` + // The Loadbalancer on which the members of the pool will be associated with. - // Note: one of LoadbalancerID or ListenerID must be provided. + // Note: one of LoadbalancerID or ListenerID must be provided. LoadbalancerID string `json:"loadbalancer_id,omitempty" xor:"ListenerID"` + // The Listener on which the members of the pool will be associated with. - // Note: one of LoadbalancerID or ListenerID must be provided. + // Note: one of LoadbalancerID or ListenerID must be provided. ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"` - // Only required if the caller has an admin role and wants to create a pool - // for another tenant. + + // The UUID of the tenant who owns the Pool. Only administrative users + // can specify a tenant UUID other than their own. TenantID string `json:"tenant_id,omitempty"` + // Name of the pool. Name string `json:"name,omitempty"` + // Human-readable description for the pool. Description string `json:"description,omitempty"` + + // Persistence is the session persistence of the pool. // Omit this field to prevent session persistence. Persistence *SessionPersistence `json:"session_persistence,omitempty"` + // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToPoolCreateMap casts a CreateOpts struct to a map. +// ToPoolCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } @@ -132,10 +139,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToPoolUpdateMap() (map[string]interface{}, error) } @@ -145,18 +150,21 @@ type UpdateOptsBuilder interface { type UpdateOpts struct { // Name of the pool. Name string `json:"name,omitempty"` + // Human-readable description for the pool. 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. LBMethod LBMethod `json:"lb_algorithm,omitempty"` + // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToPoolUpdateMap casts a UpdateOpts struct to a map. +// ToPoolUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "pool") } @@ -186,11 +194,11 @@ type ListMembersOptsBuilder interface { ToMembersListQuery() (string, error) } -// ListMembersOpts allows the filtering and sorting of paginated collections through -// the API. Filtering is achieved by passing in struct field values that map to -// the Member attributes you want to see returned. SortKey allows you to -// sort by a particular Member attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. +// ListMembersOpts allows the filtering and sorting of paginated collections +// through the API. Filtering is achieved by passing in struct field values +// that map to the Member attributes you want to see returned. SortKey allows +// you to sort by a particular Member attribute. SortDir sets the direction, +// and is either `asc' or `desc'. Marker and Limit are used for pagination. type ListMembersOpts struct { Name string `q:"name"` Weight int `q:"weight"` @@ -212,8 +220,8 @@ func (opts ListMembersOpts) ToMembersListQuery() (string, error) { } // ListMembers returns a Pager which allows you to iterate over a collection of -// members. It accepts a ListMembersOptsBuilder, which allows you to filter and sort -// the returned collection for greater efficiency. +// members. It accepts a ListMembersOptsBuilder, which allows you to filter and +// sort the returned collection for greater efficiency. // // Default policy settings return only those members that are owned by the // tenant who submits the request, unless an admin user submits the request. @@ -231,10 +239,8 @@ func ListMembers(c *gophercloud.ServiceClient, poolID string, opts ListMembersOp }) } -// CreateMemberOptsBuilder is the interface options structs have to satisfy in order -// to be used in the CreateMember operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateMemberOptsBuilder allows extensions to add additional parameters to the +// CreateMember request. type CreateMemberOptsBuilder interface { ToMemberCreateMap() (map[string]interface{}, error) } @@ -242,29 +248,35 @@ type CreateMemberOptsBuilder interface { // CreateMemberOpts is the common options struct used in this package's CreateMember // operation. type CreateMemberOpts struct { - // Required. The IP address of the member to receive traffic from the load balancer. + // The IP address of the member to receive traffic from the load balancer. Address string `json:"address" required:"true"` - // Required. The port on which to listen for client traffic. + + // The port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` - // Optional. Name of the Member. + + // Name of the Member. Name string `json:"name,omitempty"` - // Only required if the caller has an admin role and wants to create a Member - // for another tenant. + + // The UUID of the tenant who owns the Member. Only administrative users + // can specify a tenant UUID other than their own. TenantID string `json:"tenant_id,omitempty"` - // Optional. A positive integer value that indicates the relative portion of - // traffic that this member should receive from the pool. For example, a - // member with a weight of 10 receives five times as much traffic as a member - // with a weight of 2. + + // A positive integer value that indicates the relative portion of traffic + // that this member should receive from the pool. For example, a member with + // a weight of 10 receives five times as much traffic as a member with a + // weight of 2. Weight int `json:"weight,omitempty"` - // Optional. If you omit this parameter, LBaaS uses the vip_subnet_id - // parameter value for the subnet UUID. + + // If you omit this parameter, LBaaS uses the vip_subnet_id parameter value + // for the subnet UUID. SubnetID string `json:"subnet_id,omitempty"` - // Optional. The administrative state of the Pool. A valid value is true (UP) + + // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToMemberCreateMap casts a CreateOpts struct to a map. +// ToMemberCreateMap builds a request body from CreateMemberOpts. func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } @@ -286,10 +298,8 @@ func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r return } -// MemberUpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateMemberOptsBuilder allows extensions to add additional parameters to the +// List request. type UpdateMemberOptsBuilder interface { ToMemberUpdateMap() (map[string]interface{}, error) } @@ -297,19 +307,21 @@ type UpdateMemberOptsBuilder interface { // UpdateMemberOpts is the common options struct used in this package's Update // operation. type UpdateMemberOpts struct { - // Optional. Name of the Member. + // Name of the Member. Name string `json:"name,omitempty"` - // Optional. A positive integer value that indicates the relative portion of - // traffic that this member should receive from the pool. For example, a - // member with a weight of 10 receives five times as much traffic as a member - // with a weight of 2. + + // A positive integer value that indicates the relative portion of traffic + // that this member should receive from the pool. For example, a member with + // a weight of 10 receives five times as much traffic as a member with a + // weight of 2. Weight int `json:"weight,omitempty"` - // Optional. The administrative state of the Pool. A valid value is true (UP) + + // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` } -// ToMemberUpdateMap casts a UpdateOpts struct to a map. +// ToMemberUpdateMap builds a request body from UpdateMemberOpts. func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "member") } @@ -327,7 +339,8 @@ func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, return } -// DisassociateMember will remove and disassociate a Member from a particular Pool. +// DisassociateMember will remove and disassociate a Member from a particular +// Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { _, r.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil) return diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go index 0e0bf366b7..56790fff99 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go @@ -22,17 +22,19 @@ import ( // requests carrying the same cookie value will be handled by the // same Member of the Pool. type SessionPersistence struct { - // The type of persistence mode + // The type of persistence mode. Type string `json:"type"` - // Name of cookie if persistence mode is set appropriately + // Name of cookie if persistence mode is set appropriately. CookieName string `json:"cookie_name,omitempty"` } +// LoadBalancerID represents a load balancer. type LoadBalancerID struct { ID string `json:"id"` } +// ListenerID represents a listener. type ListenerID struct { ID string `json:"id"` } @@ -46,36 +48,50 @@ type Pool struct { // so on. This value, which must be supported, is dependent on the provider. // Round-robin must be supported. LBMethod string `json:"lb_algorithm"` + // The protocol of the Pool, which is TCP, HTTP, or HTTPS. Protocol string `json:"protocol"` + // Description for the Pool. Description string `json:"description"` + // A list of listeners objects IDs. Listeners []ListenerID `json:"listeners"` //[]map[string]interface{} + // A list of member objects IDs. Members []Member `json:"members"` + // The ID of associated health monitor. MonitorID string `json:"healthmonitor_id"` + // The network on which the members of the Pool will be located. Only members // that are on this network can be added to the Pool. SubnetID string `json:"subnet_id"` - // Owner of the Pool. Only an administrative user can specify a tenant ID - // other than its own. + + // Owner of the Pool. TenantID string `json:"tenant_id"` + // The administrative state of the Pool, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` + // Pool name. Does not have to be unique. Name string `json:"name"` + // The unique ID for the Pool. ID string `json:"id"` + // A list of load balancer objects IDs. Loadbalancers []LoadBalancerID `json:"loadbalancers"` + // Indicates whether connections in the same session will be processed by the // same Pool member or not. Persistence SessionPersistence `json:"session_persistence"` - // The provider - Provider string `json:"provider"` - Monitor monitors.Monitor `json:"healthmonitor"` + + // The load balancer provider. + Provider string `json:"provider"` + + // The Monitor associated with this Pool. + Monitor monitors.Monitor `json:"healthmonitor"` } // PoolPage is the page returned by a pager when traversing over a @@ -105,7 +121,7 @@ func (r PoolPage) IsEmpty() (bool, error) { } // ExtractPools accepts a Page struct, specifically a PoolPage struct, -// and extracts the elements into a slice of Router structs. In other words, +// and extracts the elements into a slice of Pool structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPools(r pagination.Page) ([]Pool, error) { var s struct { @@ -119,7 +135,7 @@ type commonResult struct { gophercloud.Result } -// Extract is a function that accepts a result and extracts a router. +// Extract is a function that accepts a result and extracts a pool. func (r commonResult) Extract() (*Pool, error) { var s struct { Pool *Pool `json:"pool"` @@ -128,22 +144,26 @@ func (r commonResult) Extract() (*Pool, error) { return s.Pool, err } -// CreateResult represents the result of a Create operation. +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret the result as a Pool. type CreateResult struct { commonResult } -// GetResult represents the result of a Get operation. +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret the result as a Pool. type GetResult struct { commonResult } -// UpdateResult represents the result of an Update operation. +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret the result as a Pool. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a Delete operation. +// 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 } @@ -152,21 +172,28 @@ type DeleteResult struct { type Member struct { // Name of the Member. Name string `json:"name"` + // Weight of Member. Weight int `json:"weight"` + // The administrative state of the member, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` - // Owner of the Member. Only an administrative user can specify a tenant ID - // other than its own. + + // Owner of the Member. TenantID string `json:"tenant_id"` - // parameter value for the subnet UUID. + + // Parameter value for the subnet UUID. SubnetID string `json:"subnet_id"` + // The Pool to which the Member belongs. PoolID string `json:"pool_id"` + // The IP address of the Member. Address string `json:"address"` + // The port on which the application is hosted. ProtocolPort int `json:"protocol_port"` + // The unique ID for the Member. ID string `json:"id"` } @@ -197,8 +224,8 @@ func (r MemberPage) IsEmpty() (bool, error) { return len(is) == 0, err } -// ExtractMembers accepts a Page struct, specifically a RouterPage struct, -// and extracts the elements into a slice of Router structs. In other words, +// ExtractMembers accepts a Page struct, specifically a MemberPage struct, +// and extracts the elements into a slice of Members structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractMembers(r pagination.Page) ([]Member, error) { var s struct { @@ -212,7 +239,7 @@ type commonMemberResult struct { gophercloud.Result } -// ExtractMember is a function that accepts a result and extracts a router. +// ExtractMember is a function that accepts a result and extracts a member. func (r commonMemberResult) Extract() (*Member, error) { var s struct { Member *Member `json:"member"` @@ -222,21 +249,25 @@ func (r commonMemberResult) Extract() (*Member, error) { } // CreateMemberResult represents the result of a CreateMember operation. +// Call its Extract method to interpret it as a Member. type CreateMemberResult struct { commonMemberResult } // GetMemberResult represents the result of a GetMember operation. +// Call its Extract method to interpret it as a Member. type GetMemberResult struct { commonMemberResult } // UpdateMemberResult represents the result of an UpdateMember operation. +// Call its Extract method to interpret it as a Member. type UpdateMemberResult struct { commonMemberResult } // DeleteMemberResult represents the result of a DeleteMember operation. +// Call its ExtractErr method to determine if the request succeeded or failed. type DeleteMemberResult struct { gophercloud.ErrResult } diff --git a/openstack/networking/v2/extensions/provider/doc.go b/openstack/networking/v2/extensions/provider/doc.go old mode 100755 new mode 100644 index 373da44f84..ddc44175a7 --- a/openstack/networking/v2/extensions/provider/doc.go +++ b/openstack/networking/v2/extensions/provider/doc.go @@ -1,21 +1,73 @@ -// Package provider gives access to the provider Neutron plugin, allowing -// network extended attributes. The provider extended attributes for networks -// enable administrative users to specify how network objects map to the -// underlying networking infrastructure. These extended attributes also appear -// when administrative users query networks. -// -// For more information about extended attributes, see the NetworkExtAttrs -// struct. The actual semantics of these attributes depend on the technology -// back end of the particular plug-in. See the plug-in documentation and the -// OpenStack Cloud Administrator Guide to understand which values should be -// specific for each of these attributes when OpenStack Networking is deployed -// with a particular plug-in. The examples shown in this chapter refer to the -// Open vSwitch plug-in. -// -// The default policy settings enable only users with administrative rights to -// specify these parameters in requests and to see their values in responses. By -// default, the provider network extension attributes are completely hidden from -// regular tenants. As a rule of thumb, if these attributes are not visible in a -// GET /networks/ operation, this implies the user submitting the -// request is not authorized to view or manipulate provider network attributes. +/* +Package provider gives access to the provider Neutron plugin, allowing +network extended attributes. The provider extended attributes for networks +enable administrative users to specify how network objects map to the +underlying networking infrastructure. These extended attributes also appear +when administrative users query networks. + +For more information about extended attributes, see the NetworkExtAttrs +struct. The actual semantics of these attributes depend on the technology +back end of the particular plug-in. See the plug-in documentation and the +OpenStack Cloud Administrator Guide to understand which values should be +specific for each of these attributes when OpenStack Networking is deployed +with a particular plug-in. The examples shown in this chapter refer to the +Open vSwitch plug-in. + +The default policy settings enable only users with administrative rights to +specify these parameters in requests and to see their values in responses. By +default, the provider network extension attributes are completely hidden from +regular tenants. As a rule of thumb, if these attributes are not visible in a +GET /networks/ operation, this implies the user submitting the +request is not authorized to view or manipulate provider network attributes. + +Example to List Networks with Provider Information + + type NetworkWithProvider { + networks.Network + provider.NetworkProviderExt + } + + var allNetworks []NetworkWithProvider + + allPages, err := networks.List(networkClient, nil).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 to Create a Provider Network + + segments := []provider.Segment{ + provider.Segment{ + NetworkType: "vxlan", + PhysicalNetwork: "br-ex", + SegmentationID: 615, + }, + } + + iTrue := true + networkCreateOpts := networks.CreateOpts{ + Name: "provider-network", + AdminStateUp: &iTrue, + Shared: &iTrue, + } + + createOpts : provider.CreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + Segments: segments, + } + + network, err := networks.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } +*/ package provider diff --git a/openstack/networking/v2/extensions/provider/results.go b/openstack/networking/v2/extensions/provider/results.go index 2f56869481..9babd2ab6e 100644 --- a/openstack/networking/v2/extensions/provider/results.go +++ b/openstack/networking/v2/extensions/provider/results.go @@ -5,18 +5,19 @@ import ( "strconv" ) -// NetworkProviderExt represents an extended form of a Network with additional fields. +// NetworkProviderExt represents an extended form of a Network with additional +// fields. type NetworkProviderExt struct { // Specifies the nature of the physical network mapped to this network // resource. Examples are flat, vlan, or gre. NetworkType string `json:"provider:network_type"` // Identifies the physical network on top of which this network object is - // being implemented. The OpenStack Networking API does not expose any facility - // for retrieving the list of available physical networks. As an example, in - // the Open vSwitch plug-in this is a symbolic name which is then mapped to - // specific bridges on each compute host through the Open vSwitch plug-in - // configuration file. + // being implemented. The OpenStack Networking API does not expose any + // facility for retrieving the list of available physical networks. As an + // example, in the Open vSwitch plug-in this is a symbolic name which is + // then mapped to specific bridges on each compute host through the Open + // vSwitch plug-in configuration file. PhysicalNetwork string `json:"provider:physical_network"` // Identifies an isolated segment on the physical network; the nature of the @@ -25,7 +26,8 @@ type NetworkProviderExt struct { // otherwise, if network_type is gre, then this will be a gre key. SegmentationID string `json:"-"` - // Segments is an array of Segment which defines multiple physical bindings to logical networks. + // Segments is an array of Segment which defines multiple physical bindings + // to logical networks. Segments []Segment `json:"segments"` } diff --git a/openstack/networking/v2/extensions/security/groups/doc.go b/openstack/networking/v2/extensions/security/groups/doc.go new file mode 100644 index 0000000000..7d8bbcaacb --- /dev/null +++ b/openstack/networking/v2/extensions/security/groups/doc.go @@ -0,0 +1,58 @@ +/* +Package groups provides information and interaction with Security Groups +for the OpenStack Networking service. + +Example to List Security Groups + + listOpts := groups.ListOpts{ + TenantID: "966b3c7d36a24facaf20b7e458bf2192", + } + + allPages, err := groups.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allGroups, err := groups.ExtractGroups(allPages) + if err != nil { + panic(err) + } + + for _, group := range allGroups { + fmt.Printf("%+v\n", group) + } + +Example to Create a Security Group + + createOpts := groups.CreateOpts{ + Name: "group_name", + Description: "A Security Group", + } + + group, err := groups.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Security Group + + groupID := "37d94f8a-d136-465c-ae46-144f0d8ef141" + + updateOpts := groups.UpdateOpts{ + Name: "new_name", + } + + group, err := groups.Update(networkClient, groupID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Security Group + + groupID := "37d94f8a-d136-465c-ae46-144f0d8ef141" + err := groups.Delete(networkClient, groupID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package groups diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index 7e1f608128..0a7ef79cf6 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -7,7 +7,7 @@ import ( // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to -// the floating IP attributes you want to see returned. SortKey allows you to +// the group attributes you want to see returned. SortKey allows you to // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { @@ -34,20 +34,26 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { }) } +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToSecGroupCreateMap() (map[string]interface{}, error) } // CreateOpts contains all the values needed to create a new security group. type CreateOpts struct { - // Required. Human-readable name for the Security Group. Does not have to be unique. + // Human-readable name for the Security Group. Does not have to be unique. Name string `json:"name" required:"true"` - // Required for admins. Indicates the owner of the Security Group. + + // The UUID of the tenant who owns the Group. Only administrative users + // can specify a tenant UUID other than their own. TenantID string `json:"tenant_id,omitempty"` - // Optional. Describes the security group. + + // Describes the security group. Description string `json:"description,omitempty"` } +// ToSecGroupCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group") } @@ -64,18 +70,23 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul return } +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToSecGroupUpdateMap() (map[string]interface{}, error) } -// UpdateOpts contains all the values needed to update an existing security group. +// UpdateOpts contains all the values needed to update an existing security +// group. type UpdateOpts struct { // Human-readable name for the Security Group. Does not have to be unique. Name string `json:"name,omitempty"` - // Optional. Describes the security group. + + // Describes the security group. Description string `json:"description,omitempty"` } +// ToSecGroupUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group") } @@ -100,13 +111,15 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// Delete will permanently delete a particular security group based on its unique ID. +// Delete will permanently delete a particular security group based on its +// unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } -// IDFromName is a convenience function that returns a security group's ID given its name. +// IDFromName is a convenience function that returns a security group's ID, +// given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go index d737abb061..8a8e0ffcfd 100644 --- a/openstack/networking/v2/extensions/security/groups/results.go +++ b/openstack/networking/v2/extensions/security/groups/results.go @@ -11,8 +11,8 @@ type SecGroup struct { // The UUID for the security group. ID string - // Human-readable name for the security group. Might not be unique. Cannot be - // named "default" as that is automatically created for a tenant. + // Human-readable name for the security group. Might not be unique. + // Cannot be named "default" as that is automatically created for a tenant. Name string // The security group description. @@ -22,8 +22,7 @@ type SecGroup struct { // traffic entering and leaving the group. Rules []rules.SecGroupRule `json:"security_group_rules"` - // Owner of the security group. Only admin users can specify a TenantID - // other than their own. + // Owner of the security group. TenantID string `json:"tenant_id"` } @@ -78,22 +77,26 @@ func (r commonResult) Extract() (*SecGroup, error) { return s.SecGroup, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a SecGroup. type CreateResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a SecGroup. type UpdateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a SecGroup. type GetResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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/security/rules/doc.go b/openstack/networking/v2/extensions/security/rules/doc.go new file mode 100644 index 0000000000..bf66dc8b40 --- /dev/null +++ b/openstack/networking/v2/extensions/security/rules/doc.go @@ -0,0 +1,50 @@ +/* +Package rules provides information and interaction with Security Group Rules +for the OpenStack Networking service. + +Example to List Security Groups Rules + + listOpts := rules.ListOpts{ + Protocol: "tcp", + } + + allPages, err := rules.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allRules, err := rules.ExtractRules(allPages) + if err != nil { + panic(err) + } + + for _, rule := range allRules { + fmt.Printf("%+v\n", rule) + } + +Example to Create a Security Group Rule + + createOpts := rules.CreateOpts{ + Direction: "ingress", + PortRangeMin: 80, + EtherType: rules.EtherType4, + PortRangeMax: 80, + Protocol: "tcp", + RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + } + + rule, err := rules.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Security Group Rule + + ruleID := "37d94f8a-d136-465c-ae46-144f0d8ef141" + err := rules.Delete(networkClient, ruleID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package rules diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 59ba721d6f..197710fc4c 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -7,9 +7,9 @@ import ( // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to -// the security group attributes you want to see returned. SortKey allows you to -// sort by a particular network attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. +// the security group rule attributes you want to see returned. SortKey allows +// you to sort by a particular network attribute. SortDir sets the direction, +// and is either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Direction string `q:"direction"` EtherType string `q:"ethertype"` @@ -74,48 +74,56 @@ const ( ProtocolVRRP RuleProtocol = "vrrp" ) -// CreateOptsBuilder is what types must satisfy to be used as Create -// options. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToSecGroupRuleCreateMap() (map[string]interface{}, error) } -// CreateOpts contains all the values needed to create a new security group rule. +// CreateOpts contains all the values needed to create a new security group +// rule. type CreateOpts struct { - // Required. Must be either "ingress" or "egress": the direction in which the - // security group rule is applied. + // Must be either "ingress" or "egress": the direction in which the security + // group rule is applied. Direction RuleDirection `json:"direction" required:"true"` - // Required. Must be "IPv4" or "IPv6", and addresses represented in CIDR must - // match the ingress or egress rules. + + // Must be "IPv4" or "IPv6", and addresses represented in CIDR must match the + // ingress or egress rules. EtherType RuleEtherType `json:"ethertype" required:"true"` - // Required. The security group ID to associate with this security group rule. + + // The security group ID to associate with this security group rule. SecGroupID string `json:"security_group_id" required:"true"` - // Optional. The maximum port number in the range that is matched by the - // security group rule. The PortRangeMin attribute constrains the PortRangeMax - // attribute. If the protocol is ICMP, this value must be an ICMP type. + + // The maximum port number in the range that is matched by the security group + // rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If + // the protocol is ICMP, this value must be an ICMP type. PortRangeMax int `json:"port_range_max,omitempty"` - // Optional. The minimum port number in the range that is matched by the - // security group rule. If the protocol is TCP or UDP, this value must be - // less than or equal to the value of the PortRangeMax attribute. If the - // protocol is ICMP, this value must be an ICMP type. + + // The minimum port number in the range that is matched by the security group + // rule. If the protocol is TCP or UDP, this value must be less than or equal + // to the value of the PortRangeMax attribute. If the protocol is ICMP, this + // value must be an ICMP type. PortRangeMin int `json:"port_range_min,omitempty"` - // Optional. The protocol that is matched by the security group rule. Valid - // values are "tcp", "udp", "icmp" or an empty string. + + // The protocol that is matched by the security group rule. Valid values are + // "tcp", "udp", "icmp" or an empty string. Protocol RuleProtocol `json:"protocol,omitempty"` - // Optional. The remote group ID to be associated with this security group - // rule. You can specify either RemoteGroupID or RemoteIPPrefix. + + // The remote group ID to be associated with this security group rule. You can + // specify either RemoteGroupID or RemoteIPPrefix. RemoteGroupID string `json:"remote_group_id,omitempty"` - // Optional. The remote IP prefix to be associated with this security group - // rule. You can specify either RemoteGroupID or RemoteIPPrefix. This - // attribute matches the specified IP prefix as the source IP address of the - // IP packet. + + // The remote IP prefix to be associated with this security group rule. You can + // specify either RemoteGroupID or RemoteIPPrefix. This attribute matches the + // specified IP prefix as the source IP address of the IP packet. RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"` - // Required for admins. Indicates the owner of the VIP. + + // The UUID of the tenant who owns the Rule. Only administrative users + // can specify a tenant UUID other than their own. TenantID string `json:"tenant_id,omitempty"` } -// ToSecGroupRuleCreateMap allows CreateOpts to satisfy the CreateOptsBuilder -// interface +// ToSecGroupRuleCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group_rule") } @@ -138,7 +146,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// Delete will permanently delete a particular security group rule based on its unique ID. +// Delete will permanently delete a particular security group rule based on its +// unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index 18476a602c..0d8c43f8ed 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -102,17 +102,20 @@ func (r commonResult) Extract() (*SecGroupRule, error) { return s.SecGroupRule, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a SecGroupRule. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a SecGroupRule. type GetResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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/networks/doc.go b/openstack/networking/v2/networks/doc.go index c87a7ce270..e768b71f82 100644 --- a/openstack/networking/v2/networks/doc.go +++ b/openstack/networking/v2/networks/doc.go @@ -1,9 +1,65 @@ -// Package networks contains functionality for working with Neutron network -// resources. A network is an isolated virtual layer-2 broadcast domain that is -// typically reserved for the tenant who created it (unless you configure the -// network to be shared). Tenants can create multiple networks until the -// thresholds per-tenant quota is reached. -// -// In the v2.0 Networking API, the network is the main entity. Ports and subnets -// are always associated with a network. +/* +Package networks contains functionality for working with Neutron network +resources. A network is an isolated virtual layer-2 broadcast domain that is +typically reserved for the tenant who created it (unless you configure the +network to be shared). Tenants can create multiple networks until the +thresholds per-tenant quota is reached. + +In the v2.0 Networking API, the network is the main entity. Ports and subnets +are always associated with a network. + +Example to List Networks + + listOpts := networks.ListOpts{ + TenantID: "a99e9b4e620e4db09a2dfb6e42a01e66", + } + + allPages, err := networks.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allNetworks, err := networks.ExtractNetworks(allPages) + if err != nil { + panic(err) + } + + for _, network := range allNetworks { + fmt.Printf("%+v", network) + } + +Example to Create a Network + + iTrue := true + createOpts := networks.CreateOpts{ + Name: "network_1", + AdminStateUp: &iTrue, + } + + network, err := networks.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Network + + networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78" + + updateOpts := networks.UpdateOpts{ + Name: "new_name", + } + + network, err := networks.Update(networkClient, networkID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Network + + networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78" + err := networks.Delete(networkClient, networkID).ExtractErr() + if err != nil { + panic(err) + } +*/ package networks diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 876a00bb0b..5b61b24719 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -58,15 +58,13 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToNetworkCreateMap() (map[string]interface{}, error) } -// CreateOpts satisfies the CreateOptsBuilder interface +// CreateOpts represents options used to create a network. type CreateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` Name string `json:"name,omitempty"` @@ -74,7 +72,7 @@ type CreateOpts struct { TenantID string `json:"tenant_id,omitempty"` } -// ToNetworkCreateMap casts a CreateOpts struct to a map. +// ToNetworkCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "network") } @@ -96,22 +94,20 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToNetworkUpdateMap() (map[string]interface{}, error) } -// UpdateOpts satisfies the UpdateOptsBuilder interface +// UpdateOpts represents options used to update a network. type UpdateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` Name string `json:"name,omitempty"` Shared *bool `json:"shared,omitempty"` } -// ToNetworkUpdateMap casts a UpdateOpts struct to a map. +// ToNetworkUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "network") } @@ -136,7 +132,8 @@ func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { return } -// IDFromName is a convenience function that returns a network's ID given its name. +// IDFromName is a convenience function that returns a network's ID, given +// its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index 838afaa976..ffd0259f1d 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -20,22 +20,26 @@ func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "network") } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Network. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Network. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Network. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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 } @@ -48,20 +52,22 @@ type Network struct { // Human-readable name for the network. Might not be unique. Name string `json:"name"` - // The administrative state of network. If false (down), the network does not forward packets. + // The administrative state of network. If false (down), the network does not + // forward packets. AdminStateUp bool `json:"admin_state_up"` // Indicates whether network is currently operational. Possible values include - // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values. + // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional + // values. Status string `json:"status"` // Subnets associated with this network. Subnets []string `json:"subnets"` - // Owner of network. Only admin users can specify a tenant_id other than its own. + // Owner of network. TenantID string `json:"tenant_id"` - // Specifies whether the network resource can be accessed by any tenant or not. + // Specifies whether the network resource can be accessed by any tenant. Shared bool `json:"shared"` } diff --git a/openstack/networking/v2/ports/doc.go b/openstack/networking/v2/ports/doc.go index f16a4bb01b..654a17181c 100644 --- a/openstack/networking/v2/ports/doc.go +++ b/openstack/networking/v2/ports/doc.go @@ -1,8 +1,73 @@ -// Package ports contains functionality for working with Neutron port resources. -// A port represents a virtual switch port on a logical network switch. Virtual -// instances attach their interfaces into ports. The logical port also defines -// the MAC address and the IP address(es) to be assigned to the interfaces -// plugged into them. When IP addresses are associated to a port, this also -// implies the port is associated with a subnet, as the IP address was taken -// from the allocation pool for a specific subnet. +/* +Package ports contains functionality for working with Neutron port resources. + +A port represents a virtual switch port on a logical network switch. Virtual +instances attach their interfaces into ports. The logical port also defines +the MAC address and the IP address(es) to be assigned to the interfaces +plugged into them. When IP addresses are associated to a port, this also +implies the port is associated with a subnet, as the IP address was taken +from the allocation pool for a specific subnet. + +Example to List Ports + + listOpts := ports.ListOpts{ + DeviceID: "b0b89efe-82f8-461d-958b-adbf80f50c7d", + } + + allPages, err := ports.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allPorts, err := ports.ExtractPorts(allPages) + if err != nil { + panic(err) + } + + for _, port := range allPorts { + fmt.Printf("%+v\n", port) + } + +Example to Create a Port + + createOtps := ports.CreateOpts{ + Name: "private-port", + AdminStateUp: &asu, + NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }, + SecurityGroups: []string{"foo"}, + AllowedAddressPairs: []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }, + } + + port, err := ports.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Port + + portID := "c34bae2b-7641-49b6-bf6d-d8e473620ed8" + + updateOpts := ports.UpdateOpts{ + Name: "new_name", + SecurityGroups: []string{}, + } + + port, err := ports.Update(networkClient, portID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Port + + portID := "c34bae2b-7641-49b6-bf6d-d8e473620ed8" + err := ports.Delete(networkClient, portID).ExtractErr() + if err != nil { + panic(err) + } +*/ package ports diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index d353b7ed38..0ca08716d2 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -65,10 +65,8 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToPortCreateMap() (map[string]interface{}, error) } @@ -87,7 +85,7 @@ type CreateOpts struct { AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` } -// ToPortCreateMap casts a CreateOpts struct to a map. +// ToPortCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "port") } @@ -104,10 +102,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul return } -// UpdateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Update operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToPortUpdateMap() (map[string]interface{}, error) } @@ -123,7 +119,7 @@ type UpdateOpts struct { AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"` } -// ToPortUpdateMap casts an UpdateOpts struct to a map. +// ToPortUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "port") } @@ -148,7 +144,8 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { return } -// IDFromName is a convenience function that returns a port's ID given its name. +// IDFromName is a convenience function that returns a port's ID, +// given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index 57a1765c6c..c50da6dfff 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -18,22 +18,26 @@ func (r commonResult) Extract() (*Port, error) { return s.Port, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Port. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Port. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Port. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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 } @@ -55,28 +59,41 @@ type AddressPair struct { type Port struct { // UUID for the port. ID string `json:"id"` + // Network that this port is associated with. NetworkID string `json:"network_id"` + // Human-readable name for the port. Might not be unique. Name string `json:"name"` - // Administrative state of port. If false (down), port does not forward packets. + + // Administrative state of port. If false (down), port does not forward + // packets. AdminStateUp bool `json:"admin_state_up"` + // Indicates whether network is currently operational. Possible values include - // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional values. + // `ACTIVE', `DOWN', `BUILD', or `ERROR'. Plug-ins might define additional + // values. Status string `json:"status"` + // Mac address to use on this port. MACAddress string `json:"mac_address"` + // Specifies IP addresses for the port thus associating the port itself with // the subnets where the IP addresses are picked from FixedIPs []IP `json:"fixed_ips"` - // Owner of network. Only admin users can specify a tenant_id other than its own. + + // Owner of network. TenantID string `json:"tenant_id"` + // Identifies the entity (e.g.: dhcp agent) using this port. DeviceOwner string `json:"device_owner"` + // Specifies the IDs of any security groups associated with a port. SecurityGroups []string `json:"security_groups"` + // Identifies the device (e.g., virtual server) using this port. DeviceID string `json:"device_id"` + // Identifies the list of IP addresses the port will recognize/accept AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"` } diff --git a/openstack/networking/v2/subnets/doc.go b/openstack/networking/v2/subnets/doc.go index 43e8296c7f..d0ed8dff06 100644 --- a/openstack/networking/v2/subnets/doc.go +++ b/openstack/networking/v2/subnets/doc.go @@ -1,10 +1,133 @@ -// Package subnets contains functionality for working with Neutron subnet -// resources. A subnet represents an IP address block that can be used to -// assign IP addresses to virtual instances. Each subnet must have a CIDR and -// must be associated with a network. IPs can either be selected from the whole -// subnet CIDR or from allocation pools specified by the user. -// -// A subnet can also have a gateway, a list of DNS name servers, and host routes. -// This information is pushed to instances whose interfaces are associated with -// the subnet. +/* +Package subnets contains functionality for working with Neutron subnet +resources. A subnet represents an IP address block that can be used to +assign IP addresses to virtual instances. Each subnet must have a CIDR and +must be associated with a network. IPs can either be selected from the whole +subnet CIDR or from allocation pools specified by the user. + +A subnet can also have a gateway, a list of DNS name servers, and host routes. +This information is pushed to instances whose interfaces are associated with +the subnet. + +Example to List Subnets + + listOpts := subnets.ListOpts{ + IPVersion: 4, + } + + allPages, err := subnets.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allSubnets, err := subnets.ExtractSubnets(allPages) + if err != nil { + panic(err) + } + + for _, subnet := range allSubnets { + fmt.Printf("%+v\n", subnet) + } + +Example to Create a Subnet With Specified Gateway + + var gatewayIP = "192.168.199.1" + createOpts := subnets.CreateOpts{ + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + IPVersion: 4, + CIDR: "192.168.199.0/24", + GatewayIP: &gatewayIP, + AllocationPools: []subnets.AllocationPool{ + { + Start: "192.168.199.2", + End: "192.168.199.254", + }, + }, + DNSNameservers: []string{"foo"}, + } + + subnet, err := subnets.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Create a Subnet With No Gateway + + var noGateway = "" + + createOpts := subnets.CreateOpts{ + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", + IPVersion: 4, + CIDR: "192.168.1.0/24", + GatewayIP: &noGateway, + AllocationPools: []subnets.AllocationPool{ + { + Start: "192.168.1.2", + End: "192.168.1.254", + }, + }, + DNSNameservers: []string{}, + } + + subnet, err := subnets.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Create a Subnet With a Default Gateway + + createOpts := subnets.CreateOpts{ + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", + IPVersion: 4, + CIDR: "192.168.1.0/24", + AllocationPools: []subnets.AllocationPool{ + { + Start: "192.168.1.2", + End: "192.168.1.254", + }, + }, + DNSNameservers: []string{}, + } + + subnet, err := subnets.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Subnet + + subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" + + updateOpts := subnets.UpdateOpts{ + Name: "new_name", + DNSNameservers: []string{"8.8.8.8}, + } + + subnet, err := subnets.Update(networkClient, subnetID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Remove a Gateway From a Subnet + + var noGateway = "" + subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" + + updateOpts := subnets.UpdateOpts{ + GatewayIP: &noGateway, + } + + subnet, err := subnets.Update(networkClient, subnetID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Subnet + + subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" + err := subnets.Delete(networkClient, subnetID).ExtractErr() + if err != nil { + panic(err) + } +*/ package subnets diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index ec3769554b..c2e74eff44 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -64,29 +64,50 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. +// CreateOptsBuilder allows extensions to add additional parameters to the +// List request. type CreateOptsBuilder interface { ToSubnetCreateMap() (map[string]interface{}, error) } // CreateOpts represents the attributes used when creating a new subnet. type CreateOpts struct { - NetworkID string `json:"network_id" required:"true"` - CIDR string `json:"cidr" required:"true"` - Name string `json:"name,omitempty"` - TenantID string `json:"tenant_id,omitempty"` - AllocationPools []AllocationPool `json:"allocation_pools,omitempty"` - GatewayIP *string `json:"gateway_ip,omitempty"` - IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` - EnableDHCP *bool `json:"enable_dhcp,omitempty"` - DNSNameservers []string `json:"dns_nameservers,omitempty"` - HostRoutes []HostRoute `json:"host_routes,omitempty"` + // NetworkID is the UUID of the network the subnet will be associated with. + NetworkID string `json:"network_id" required:"true"` + + // CIDR is the address CIDR of the subnet. + CIDR string `json:"cidr" required:"true"` + + // Name is a human-readable name of the subnet. + Name string `json:"name,omitempty"` + + // The UUID of the tenant who owns the Subnet. Only administrative users + // can specify a tenant UUID other than their own. + TenantID string `json:"tenant_id,omitempty"` + + // AllocationPools are IP Address pools that will be available for DHCP. + AllocationPools []AllocationPool `json:"allocation_pools,omitempty"` + + // GatewayIP sets gateway information for the subnet. Setting to nil will + // cause a default gateway to automatically be created. Setting to an empty + // string will cause the subnet to be created with no gateway. Setting to + // an explicit address will set that address as the gateway. + GatewayIP *string `json:"gateway_ip,omitempty"` + + // IPVersion is the IP version for the subnet. + IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` + + // EnableDHCP will either enable to disable the DHCP service. + EnableDHCP *bool `json:"enable_dhcp,omitempty"` + + // DNSNameservers are the nameservers to be set via DHCP. + DNSNameservers []string `json:"dns_nameservers,omitempty"` + + // HostRoutes are any static host routes to be set via DHCP. + HostRoutes []HostRoute `json:"host_routes,omitempty"` } -// ToSubnetCreateMap casts a CreateOpts struct to a map. +// ToSubnetCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "subnet") if err != nil { @@ -101,7 +122,8 @@ func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) { } // Create accepts a CreateOpts struct and creates a new subnet using the values -// provided. You must remember to provide a valid NetworkID, CIDR and IP version. +// provided. You must remember to provide a valid NetworkID, CIDR and IP +// version. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSubnetCreateMap() if err != nil { @@ -120,15 +142,29 @@ type UpdateOptsBuilder interface { // UpdateOpts represents the attributes used when updating an existing subnet. type UpdateOpts struct { - Name string `json:"name,omitempty"` + // Name is a human-readable name of the subnet. + Name string `json:"name,omitempty"` + + // AllocationPools are IP Address pools that will be available for DHCP. AllocationPools []AllocationPool `json:"allocation_pools,omitempty"` - GatewayIP *string `json:"gateway_ip,omitempty"` - DNSNameservers []string `json:"dns_nameservers,omitempty"` - HostRoutes []HostRoute `json:"host_routes,omitempty"` - EnableDHCP *bool `json:"enable_dhcp,omitempty"` + + // GatewayIP sets gateway information for the subnet. Setting to nil will + // cause a default gateway to automatically be created. Setting to an empty + // string will cause the subnet to be created with no gateway. Setting to + // an explicit address will set that address as the gateway. + GatewayIP *string `json:"gateway_ip,omitempty"` + + // DNSNameservers are the nameservers to be set via DHCP. + DNSNameservers []string `json:"dns_nameservers,omitempty"` + + // HostRoutes are any static host routes to be set via DHCP. + HostRoutes []HostRoute `json:"host_routes,omitempty"` + + // EnableDHCP will either enable to disable the DHCP service. + EnableDHCP *bool `json:"enable_dhcp,omitempty"` } -// ToSubnetUpdateMap casts an UpdateOpts struct to a map. +// ToSubnetUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "subnet") if err != nil { @@ -162,7 +198,8 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { return } -// IDFromName is a convenience function that returns a subnet's ID given its name. +// IDFromName is a convenience function that returns a subnet's ID, +// given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index ab5cce124e..ade8abc699 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -18,22 +18,26 @@ func (r commonResult) Extract() (*Subnet, error) { return s.Subnet, err } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Subnet. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Subnet. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Subnet. type UpdateResult struct { commonResult } -// DeleteResult represents the result of a delete operation. +// 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 } @@ -55,27 +59,39 @@ type HostRoute struct { // Subnet represents a subnet. See package documentation for a top-level // description of what this is. type Subnet struct { - // UUID representing the subnet + // UUID representing the subnet. ID string `json:"id"` - // UUID of the parent network + + // UUID of the parent network. NetworkID string `json:"network_id"` + // Human-readable name for the subnet. Might not be unique. Name string `json:"name"` - // IP version, either `4' or `6' + + // IP version, either `4' or `6'. IPVersion int `json:"ip_version"` - // CIDR representing IP range for this subnet, based on IP version + + // CIDR representing IP range for this subnet, based on IP version. CIDR string `json:"cidr"` - // Default gateway used by devices in this subnet + + // Default gateway used by devices in this subnet. GatewayIP string `json:"gateway_ip"` + // DNS name servers used by hosts in this subnet. DNSNameservers []string `json:"dns_nameservers"` - // Sub-ranges of CIDR available for dynamic allocation to ports. See AllocationPool. + + // Sub-ranges of CIDR available for dynamic allocation to ports. + // See AllocationPool. AllocationPools []AllocationPool `json:"allocation_pools"` - // Routes that should be used by devices with IPs from this subnet (not including local subnet route). + + // Routes that should be used by devices with IPs from this subnet + // (not including local subnet route). HostRoutes []HostRoute `json:"host_routes"` + // Specifies whether DHCP is enabled for this subnet or not. EnableDHCP bool `json:"enable_dhcp"` - // Owner of network. Only admin users can specify a tenant_id other than its own. + + // Owner of network. TenantID string `json:"tenant_id"` } From 248fe1299fa9cbf318bab90f3b04b29af5765fdd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 21 Aug 2017 23:04:04 -0600 Subject: [PATCH 0054/2296] ObjectStorage v1: Docs (#496) * ObjectStorage v1: Fix Acceptance Tests * ObjectStorage v1: accounts docs * ObjectStorage v1: containers docs * ObjectStorage v1: objects docs * ObjectStorage v1: swauth docs * Core: extractinto docs --- acceptance/clients/clients.go | 19 ++++ .../objectstorage/v1/accounts_test.go | 11 +- .../openstack/objectstorage/v1/common.go | 28 ----- .../objectstorage/v1/containers_test.go | 19 +++- .../objectstorage/v1/objects_test.go | 75 ++++++++----- openstack/objectstorage/v1/accounts/doc.go | 35 ++++-- .../objectstorage/v1/accounts/requests.go | 2 +- .../objectstorage/v1/accounts/results.go | 12 +- openstack/objectstorage/v1/containers/doc.go | 94 ++++++++++++++-- .../objectstorage/v1/containers/requests.go | 2 +- .../objectstorage/v1/containers/results.go | 39 ++++--- .../v1/containers/testing/requests_test.go | 2 +- openstack/objectstorage/v1/objects/doc.go | 105 +++++++++++++++++- .../objectstorage/v1/objects/requests.go | 38 ++++--- openstack/objectstorage/v1/objects/results.go | 45 ++++---- openstack/objectstorage/v1/swauth/doc.go | 16 +++ openstack/objectstorage/v1/swauth/requests.go | 2 +- openstack/objectstorage/v1/swauth/results.go | 4 +- results.go | 5 +- 19 files changed, 400 insertions(+), 153 deletions(-) delete mode 100644 acceptance/openstack/objectstorage/v1/common.go create mode 100644 openstack/objectstorage/v1/swauth/doc.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index aa57497de0..7bca0a4408 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -322,3 +322,22 @@ func NewNetworkV2Client() (*gophercloud.ServiceClient, error) { Region: os.Getenv("OS_REGION_NAME"), }) } + +// NewObjectStorageV1Client returns a *ServiceClient for making calls to the +// OpenStack Object Storage v1 API. An error will be returned if authentication +// or client creation was not possible. +func NewObjectStorageV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + return openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} diff --git a/acceptance/openstack/objectstorage/v1/accounts_test.go b/acceptance/openstack/objectstorage/v1/accounts_test.go index 5a29235fb6..bb9745f835 100644 --- a/acceptance/openstack/objectstorage/v1/accounts_test.go +++ b/acceptance/openstack/objectstorage/v1/accounts_test.go @@ -6,16 +6,21 @@ import ( "strings" "testing" + "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" th "github.com/gophercloud/gophercloud/testhelper" ) func TestAccounts(t *testing.T) { - // Create a provider client for making the HTTP requests. - // See common.go in this directory for more information. - client := newClient(t) + client, err := clients.NewObjectStorageV1Client() + if err != nil { + t.Fatalf("Unable to create client: %v", err) + } // Update an account's metadata. + metadata := map[string]string{ + "Gophercloud-Test": "accounts", + } updateres := accounts.Update(client, accounts.UpdateOpts{Metadata: metadata}) t.Logf("Update Account Response: %+v\n", updateres) updateHeaders, err := updateres.Extract() diff --git a/acceptance/openstack/objectstorage/v1/common.go b/acceptance/openstack/objectstorage/v1/common.go deleted file mode 100644 index 1114ed57f9..0000000000 --- a/acceptance/openstack/objectstorage/v1/common.go +++ /dev/null @@ -1,28 +0,0 @@ -// +build acceptance - -package v1 - -import ( - "os" - "testing" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - th "github.com/gophercloud/gophercloud/testhelper" -) - -var metadata = map[string]string{"gopher": "cloud"} - -func newClient(t *testing.T) *gophercloud.ServiceClient { - ao, err := openstack.AuthOptionsFromEnv() - th.AssertNoErr(t, err) - - client, err := openstack.AuthenticatedClient(ao) - th.AssertNoErr(t, err) - - c, err := openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), - }) - th.AssertNoErr(t, err) - return c -} diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index 056b2a9980..5673aa10c9 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -6,6 +6,7 @@ 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/pagination" @@ -16,8 +17,10 @@ import ( var numContainers = 2 func TestContainers(t *testing.T) { - // Create a new client to execute the HTTP requests. See common.go for newClient body. - client := newClient(t) + client, err := clients.NewObjectStorageV1Client() + if err != nil { + t.Fatalf("Unable to create client: %v", err) + } // Create a slice of random container names. cNames := make([]string, numContainers) @@ -40,7 +43,7 @@ func TestContainers(t *testing.T) { // List the numContainer names that were just created. To just list those, // the 'prefix' parameter is used. - err := containers.List(client, &containers.ListOpts{Full: true, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { + err = containers.List(client, &containers.ListOpts{Full: true, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { containerList, err := containers.ExtractInfo(page) th.AssertNoErr(t, err) @@ -66,6 +69,10 @@ func TestContainers(t *testing.T) { th.AssertNoErr(t, err) // Update one of the numContainer container metadata. + metadata := map[string]string{ + "Gophercloud-Test": "containers", + } + updateres := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: metadata}) th.AssertNoErr(t, updateres.Err) // After the tests are done, delete the metadata that was set. @@ -89,8 +96,10 @@ func TestContainers(t *testing.T) { } func TestListAllContainers(t *testing.T) { - // Create a new client to execute the HTTP requests. See common.go for newClient body. - client := newClient(t) + client, err := clients.NewObjectStorageV1Client() + if err != nil { + t.Fatalf("Unable to create client: %v", err) + } numContainers := 20 diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index 3a27738230..9ab26c4abe 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -7,10 +7,10 @@ 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" - "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -18,9 +18,10 @@ import ( var numObjects = 2 func TestObjects(t *testing.T) { - // Create a provider client for executing the HTTP request. - // See common.go for more information. - client := newClient(t) + 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) @@ -30,7 +31,7 @@ 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).ExtractHeader() + header, err := containers.Create(client, cName, nil).Extract() th.AssertNoErr(t, err) t.Logf("Create object headers: %+v\n", header) @@ -44,7 +45,10 @@ func TestObjects(t *testing.T) { oContents := make([]*bytes.Buffer, numObjects) for i := 0; i < numObjects; i++ { oContents[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) - res := objects.Create(client, cName, oNames[i], oContents[i], nil) + createOpts := objects.CreateOpts{ + Content: oContents[i], + } + res := objects.Create(client, cName, oNames[i], createOpts) th.AssertNoErr(t, res.Err) } // Delete the objects after testing. @@ -55,46 +59,61 @@ func TestObjects(t *testing.T) { } }() - ons := make([]string, 0, len(oNames)) - err = objects.List(client, cName, &objects.ListOpts{Full: false, Prefix: "test-object-"}).EachPage(func(page pagination.Page) (bool, error) { - names, err := objects.ExtractNames(page) - th.AssertNoErr(t, err) - ons = append(ons, names...) - - return true, nil - }) - th.AssertNoErr(t, err) - th.AssertEquals(t, len(ons), len(oNames)) + // List all created objects + listOpts := objects.ListOpts{ + Full: true, + Prefix: "test-object-", + } - ois := make([]objects.Object, 0, len(oNames)) - err = objects.List(client, cName, &objects.ListOpts{Full: true, Prefix: "test-object-"}).EachPage(func(page pagination.Page) (bool, error) { - info, err := objects.ExtractInfo(page) - th.AssertNoErr(t, err) + allPages, err := objects.List(client, cName, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list objects: %v", err) + } - ois = append(ois, info...) + ons, err := objects.ExtractNames(allPages) + if err != nil { + t.Fatalf("Unable to extract objects: %v", err) + } + th.AssertEquals(t, len(ons), len(oNames)) - return true, nil - }) - th.AssertNoErr(t, err) + ois, err := objects.ExtractInfo(allPages) + if err != nil { + t.Fatalf("Unable to extract object info: %v", err) + } th.AssertEquals(t, len(ois), len(oNames)) // Copy the contents of one object to another. - copyres := objects.Copy(client, cName, oNames[0], &objects.CopyOpts{Destination: cName + "/" + oNames[1]}) + copyOpts := objects.CopyOpts{ + Destination: cName + "/" + oNames[1], + } + copyres := objects.Copy(client, cName, oNames[0], copyOpts) th.AssertNoErr(t, copyres.Err) // Download one of the objects that was created above. - o1Content, err := objects.Download(client, cName, oNames[0], nil).ExtractContent() + downloadres := objects.Download(client, cName, oNames[0], nil) + th.AssertNoErr(t, downloadres.Err) + + o1Content, err := downloadres.ExtractContent() th.AssertNoErr(t, err) // Download the another object that was create above. - o2Content, err := objects.Download(client, cName, oNames[1], nil).ExtractContent() + downloadres = objects.Download(client, cName, oNames[1], nil) + 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, string(o2Content), string(o1Content)) // Update an object's metadata. - updateres := objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: metadata}) + metadata := map[string]string{ + "Gophercloud-Test": "objects", + } + + updateOpts := objects.UpdateOpts{ + Metadata: metadata, + } + updateres := objects.Update(client, cName, oNames[0], updateOpts) th.AssertNoErr(t, updateres.Err) // Delete the object's metadata after testing. diff --git a/openstack/objectstorage/v1/accounts/doc.go b/openstack/objectstorage/v1/accounts/doc.go index f5f894a9e5..0fa1c083a2 100644 --- a/openstack/objectstorage/v1/accounts/doc.go +++ b/openstack/objectstorage/v1/accounts/doc.go @@ -1,8 +1,29 @@ -// Package accounts contains functionality for working with Object Storage -// account resources. An account is the top-level resource the object storage -// hierarchy: containers belong to accounts, objects belong to containers. -// -// Another way of thinking of an account is like a namespace for all your -// resources. It is synonymous with a project or tenant in other OpenStack -// services. +/* +Package accounts contains functionality for working with Object Storage +account resources. An account is the top-level resource the object storage +hierarchy: containers belong to accounts, objects belong to containers. + +Another way of thinking of an account is like a namespace for all your +resources. It is synonymous with a project or tenant in other OpenStack +services. + +Example to Get an Account + + account, err := accounts.Get(objectStorageClient, nil).Extract() + fmt.Printf("%+v\n", account) + +Example to Update an Account + + metadata := map[string]string{ + "some": "metadata", + } + + updateOpts := accounts.UpdateOpts{ + Metadata: metadata, + } + + updateResult, err := accounts.Update(objectStorageClient, updateOpts).Extract() + fmt.Printf("%+v\n", updateResult) + +*/ package accounts diff --git a/openstack/objectstorage/v1/accounts/requests.go b/openstack/objectstorage/v1/accounts/requests.go index b5beef28b8..df21587853 100644 --- a/openstack/objectstorage/v1/accounts/requests.go +++ b/openstack/objectstorage/v1/accounts/requests.go @@ -22,7 +22,7 @@ func (opts GetOpts) ToAccountGetMap() (map[string]string, error) { // Get is a function that retrieves an account's metadata. To extract just the // custom metadata, call the ExtractMetadata method on the GetResult. To extract // all the headers that are returned (including the metadata), call the -// ExtractHeader method on the GetResult. +// Extract method on the GetResult. func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { h := make(map[string]string) if opts != nil { diff --git a/openstack/objectstorage/v1/accounts/results.go b/openstack/objectstorage/v1/accounts/results.go index 9bc8340477..bf5dc846fc 100644 --- a/openstack/objectstorage/v1/accounts/results.go +++ b/openstack/objectstorage/v1/accounts/results.go @@ -14,7 +14,8 @@ type UpdateResult struct { gophercloud.HeaderResult } -// UpdateHeader represents the headers returned in the response from an Update request. +// UpdateHeader represents the headers returned in the response from an Update +// request. type UpdateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` @@ -51,8 +52,8 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { return err } -// Extract will return a struct of headers returned from a call to Get. To obtain -// a map of headers, call the ExtractHeader method on the GetResult. +// Extract will return a struct of headers returned from a call to Get. To +// obtain a map of headers, call the Extract method on the GetResult. func (r UpdateResult) Extract() (*UpdateHeader, error) { var s *UpdateHeader err := r.ExtractInto(&s) @@ -141,15 +142,14 @@ type GetResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Get. To obtain -// a map of headers, call the ExtractHeader method on the GetResult. +// Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { var s *GetHeader err := r.ExtractInto(&s) return s, err } -// ExtractMetadata is a function that takes a GetResult (of type *htts.Response) +// ExtractMetadata is a function that takes a GetResult (of type *http.Response) // and returns the custom metatdata associated with the account. func (r GetResult) ExtractMetadata() (map[string]string, error) { if r.Err != nil { diff --git a/openstack/objectstorage/v1/containers/doc.go b/openstack/objectstorage/v1/containers/doc.go index 5fed5537f1..1ac8504de7 100644 --- a/openstack/objectstorage/v1/containers/doc.go +++ b/openstack/objectstorage/v1/containers/doc.go @@ -1,8 +1,88 @@ -// Package containers contains functionality for working with Object Storage -// container resources. A container serves as a logical namespace for objects -// that are placed inside it - an object with the same name in two different -// containers represents two different objects. -// -// In addition to containing objects, you can also use the container to control -// access to objects by using an access control list (ACL). +/* +Package containers contains functionality for working with Object Storage +container resources. A container serves as a logical namespace for objects +that are placed inside it - an object with the same name in two different +containers represents two different objects. + +In addition to containing objects, you can also use the container to control +access to objects by using an access control list (ACL). + +Example to List Containers + + listOpts := containers.ListOpts{ + Full: true, + } + + allPages, err := containers.List(objectStorageClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allContainers, err := containers.ExtractInfo(allPages) + if err != nil { + panic(err) + } + + for _, container := range allContainers { + fmt.Printf("%+v\n", container) + } + +Example to List Only Container Names + + listOpts := containers.ListOpts{ + Full: false, + } + + allPages, err := containers.List(objectStorageClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allContainers, err := containers.ExtractNames(allPages) + if err != nil { + panic(err) + } + + for _, container := range allContainers { + fmt.Printf("%+v\n", container) + } + +Example to Create a Container + + createOpts := containers.CreateOpts{ + ContentType: "application/json", + Metadata: map[string]string{ + "foo": "bar", + }, + } + + container, err := containers.Create(objectStorageClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Container + + containerName := "my_container" + + updateOpts := containers.UpdateOpts{ + Metadata: map[string]string{ + "bar": "baz", + }, + } + + container, err := containers.Update(objectStorageClient, containerName, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Container + + containerName := "my_container" + + container, err := containers.Delete(objectStorageClient, containerName).Extract() + if err != nil { + panic(err) + } +*/ package containers diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index a66867394c..ecb76075b1 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -138,7 +138,7 @@ type UpdateOpts struct { VersionsLocation string `h:"X-Versions-Location"` } -// ToContainerUpdateMap formats a CreateOpts into a map of headers. +// ToContainerUpdateMap formats a UpdateOpts into a map of headers. func (opts UpdateOpts) ToContainerUpdateMap() (map[string]string, error) { h, err := gophercloud.BuildHeaders(opts) if err != nil { diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index 8c11b8c834..87682c885b 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -47,14 +47,16 @@ func (r ContainerPage) LastMarker() (string, error) { return names[len(names)-1], nil } -// ExtractInfo is a function that takes a ListResult and returns the containers' information. +// ExtractInfo is a function that takes a ListResult and returns the +// containers' information. func ExtractInfo(r pagination.Page) ([]Container, error) { var s []Container err := (r.(ContainerPage)).ExtractInto(&s) return s, err } -// ExtractNames is a function that takes a ListResult and returns the containers' names. +// ExtractNames is a function that takes a ListResult and returns the +// containers' names. func ExtractNames(page pagination.Page) ([]string, error) { casted := page.(ContainerPage) ct := casted.Header.Get("Content-Type") @@ -162,15 +164,14 @@ type GetResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Get. To obtain -// a map of headers, call the ExtractHeader method on the GetResult. +// Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { var s *GetHeader err := r.ExtractInto(&s) return s, err } -// ExtractMetadata is a function that takes a GetResult (of type *stts.Response) +// ExtractMetadata is a function that takes a GetResult (of type *http.Response) // and returns the custom metadata associated with the container. func (r GetResult) ExtractMetadata() (map[string]string, error) { if r.Err != nil { @@ -186,7 +187,8 @@ func (r GetResult) ExtractMetadata() (map[string]string, error) { return metadata, nil } -// CreateHeader represents the headers returned in the response from a Create request. +// CreateHeader represents the headers returned in the response from a Create +// request. type CreateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` @@ -224,21 +226,21 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { } // CreateResult represents the result of a create operation. To extract the -// the headers from the HTTP response, you can invoke the 'ExtractHeader' -// method on the result struct. +// the headers from the HTTP response, call its Extract method. type CreateResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Create. To obtain -// a map of headers, call the ExtractHeader method on the CreateResult. +// Extract will return a struct of headers returned from a call to Create. +// To extract the headers from the HTTP response, call its Extract method. func (r CreateResult) Extract() (*CreateHeader, error) { var s *CreateHeader err := r.ExtractInto(&s) return s, err } -// UpdateHeader represents the headers returned in the response from a Update request. +// UpdateHeader represents the headers returned in the response from a Update +// request. type UpdateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` @@ -276,21 +278,20 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { } // UpdateResult represents the result of an update operation. To extract the -// the headers from the HTTP response, you can invoke the 'ExtractHeader' -// method on the result struct. +// the headers from the HTTP response, call its Extract method. type UpdateResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Update. To obtain -// a map of headers, call the ExtractHeader method on the UpdateResult. +// Extract will return a struct of headers returned from a call to Update. func (r UpdateResult) Extract() (*UpdateHeader, error) { var s *UpdateHeader err := r.ExtractInto(&s) return s, err } -// DeleteHeader represents the headers returned in the response from a Delete request. +// DeleteHeader represents the headers returned in the response from a Delete +// request. type DeleteHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` @@ -328,14 +329,12 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { } // DeleteResult represents the result of a delete operation. To extract the -// the headers from the HTTP response, you can invoke the 'ExtractHeader' -// method on the result struct. +// the headers from the HTTP response, call its Extract method. type DeleteResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Delete. To obtain -// a map of headers, call the ExtractHeader method on the DeleteResult. +// Extract will return a struct of headers returned from a call to Delete. func (r DeleteResult) Extract() (*DeleteHeader, error) { var s *DeleteHeader err := r.ExtractInto(&s) diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index bb0c784008..484ebf48bc 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -109,7 +109,7 @@ func TestDeleteContainer(t *testing.T) { th.CheckNoErr(t, res.Err) } -func TestUpateContainer(t *testing.T) { +func TestUpdateContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleUpdateContainerSuccessfully(t) diff --git a/openstack/objectstorage/v1/objects/doc.go b/openstack/objectstorage/v1/objects/doc.go index 30a9adde1c..1e02430fb4 100644 --- a/openstack/objectstorage/v1/objects/doc.go +++ b/openstack/objectstorage/v1/objects/doc.go @@ -1,5 +1,102 @@ -// Package objects contains functionality for working with Object Storage -// object resources. An object is a resource that represents and contains data -// - such as documents, images, and so on. You can also store custom metadata -// with an object. +/* +Package objects contains functionality for working with Object Storage +object resources. An object is a resource that represents and contains data +- such as documents, images, and so on. You can also store custom metadata +with an object. + +Example to List Objects + + containerName := "my_container" + + listOpts := objects.ListOpts{ + Full: true, + } + + allPages, err := objects.List(objectStorageClient, containerName, listOpts).AllPages() + if err != nil { + panic(err) + } + + allObjects, err := objects.ExtractInfo(allPages) + if err != nil { + panic(err) + } + + for _, object := range allObjects { + fmt.Printf("%+v\n", object) + } + +Example to List Object Names + + containerName := "my_container" + + listOpts := objects.ListOpts{ + Full: false, + } + + allPages, err := objects.List(objectStorageClient, containerName, listOpts).AllPages() + if err != nil { + panic(err) + } + + allObjects, err := objects.ExtractNames(allPages) + if err != nil { + panic(err) + } + + for _, object := range allObjects { + fmt.Printf("%+v\n", object) + } + +Example to Create an Object + + content := "some object content" + objectName := "my_object" + containerName := "my_container" + + createOpts := objects.CreateOpts{ + ContentType: "text/plain" + Content: strings.NewReader(content), + } + + object, err := objects.Create(objectStorageClient, containerName, objectName, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Copy an Object + + objectName := "my_object" + containerName := "my_container" + + copyOpts := objects.CopyOpts{ + Destination: "/newContainer/newObject", + } + + object, err := objects.Copy(objectStorageClient, containerName, objectName, copyOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete an Object + + objectName := "my_object" + containerName := "my_container" + + object, err := objects.Delete(objectStorageClient, containerName, objectName).Extract() + if err != nil { + panic(err) + } + +Example to Download an Object's Data + + objectName := "my_object" + containerName := "my_container" + + object := objects.Download(objectStorageClient, containerName, objectName, nil) + content, err := object.ExtractContent() + if err != nil { + panic(err) + } +*/ package objects diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 0ab5e17111..f67bfd1590 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -24,9 +24,9 @@ type ListOptsBuilder interface { // ListOpts is a structure that holds parameters for listing objects. type ListOpts struct { // Full is a true/false value that represents the amount of object information - // returned. If Full is set to true, then the content-type, number of bytes, hash - // date last modified, and name are returned. If set to false or not set, then - // only the object names are returned. + // returned. If Full is set to true, then the content-type, number of bytes, + // hash date last modified, and name are returned. If set to false or not set, + // then only the object names are returned. Full bool Limit int `q:"limit"` Marker string `q:"marker"` @@ -44,9 +44,10 @@ func (opts ListOpts) ToObjectListParams() (bool, string, error) { return opts.Full, q.String(), err } -// List is a function that retrieves all objects in a container. It also returns the details -// for the container. To extract only the object information or names, pass the ListResult -// response to the ExtractInfo or ExtractNames function, respectively. +// List is a function that retrieves all objects in a container. It also returns +// the details for the container. To extract only the object information or names, +// 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"} @@ -190,8 +191,10 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str return buf, h, q.String(), nil } -// Create is a function that creates a new object or replaces an existing object. If the returned response's ETag -// header fails to match the local checksum, the failed request will automatically be retried up to a maximum of 3 times. +// Create is a function that creates a new object or replaces an existing +// object. If the returned response's ETag header fails to match the local +// 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) h := make(map[string]string) @@ -315,7 +318,8 @@ type GetOptsBuilder interface { ToObjectGetQuery() (string, error) } -// GetOpts is a structure that holds parameters for getting an object's metadata. +// GetOpts is a structure that holds parameters for getting an object's +// metadata. type GetOpts struct { Expires string `q:"expires"` Signature string `q:"signature"` @@ -327,8 +331,9 @@ func (opts GetOpts) ToObjectGetQuery() (string, error) { return q.String(), err } -// Get is a function that retrieves the metadata of an object. To extract just the custom -// metadata, pass the GetResult response to the ExtractMetadata function. +// Get is a function that retrieves the metadata of an object. To extract just +// 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) if opts != nil { @@ -355,8 +360,8 @@ type UpdateOptsBuilder interface { ToObjectUpdateMap() (map[string]string, error) } -// UpdateOpts is a structure that holds parameters for updating, creating, or deleting an -// object's metadata. +// UpdateOpts is a structure that holds parameters for updating, creating, or +// deleting an object's metadata. type UpdateOpts struct { Metadata map[string]string ContentDisposition string `h:"Content-Disposition"` @@ -410,17 +415,20 @@ type HTTPMethod string var ( // GET represents an HTTP "GET" method. GET HTTPMethod = "GET" + // POST represents an HTTP "POST" method. POST HTTPMethod = "POST" ) // 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". + // (REQUIRED) Method is the HTTP method to allow for users of the temp URL. + // Valid values are "GET" and "POST". Method HTTPMethod + // (REQUIRED) TTL is the number of seconds the temp URL should be active. TTL int + // (Optional) Split is the string on which to split the object URL. Since only // the object path is used in the hash, the object URL needs to be parsed. If // empty, the default OpenStack URL split point will be used ("/v1/"). diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 0dcdbe2fbe..f19b8f4aa5 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -76,14 +76,16 @@ func (r ObjectPage) LastMarker() (string, error) { return names[len(names)-1], nil } -// ExtractInfo is a function that takes a page of objects and returns their full information. +// ExtractInfo is a function that takes a page of objects and returns their +// full information. func ExtractInfo(r pagination.Page) ([]Object, error) { var s []Object err := (r.(ObjectPage)).ExtractInto(&s) return s, err } -// ExtractNames is a function that takes a page of objects and returns only their names. +// ExtractNames is a function that takes a page of objects and returns only +// their names. func ExtractNames(r pagination.Page) ([]string, error) { casted := r.(ObjectPage) ct := casted.Header.Get("Content-Type") @@ -118,7 +120,8 @@ func ExtractNames(r pagination.Page) ([]string, error) { } } -// DownloadHeader represents the headers returned in the response from a Download request. +// DownloadHeader represents the headers returned in the response from a +// Download request. type DownloadHeader struct { AcceptRanges string `json:"Accept-Ranges"` ContentDisposition string `json:"Content-Disposition"` @@ -167,14 +170,14 @@ func (r *DownloadHeader) UnmarshalJSON(b []byte) error { return nil } -// DownloadResult is a *http.Response that is returned from a call to the Download function. +// DownloadResult is a *http.Response that is returned from a call to the +// Download function. type DownloadResult struct { gophercloud.HeaderResult Body io.ReadCloser } -// Extract will return a struct of headers returned from a call to Download. To obtain -// a map of headers, call the ExtractHeader method on the DownloadResult. +// Extract will return a struct of headers returned from a call to Download. func (r DownloadResult) Extract() (*DownloadHeader, error) { var s *DownloadHeader err := r.ExtractInto(&s) @@ -247,13 +250,13 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { return nil } -// GetResult is a *http.Response that is returned from a call to the Get function. +// GetResult is a *http.Response that is returned from a call to the Get +// function. type GetResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Get. To obtain -// a map of headers, call the ExtractHeader method on the GetResult. +// Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { var s *GetHeader err := r.ExtractInto(&s) @@ -276,7 +279,8 @@ func (r GetResult) ExtractMetadata() (map[string]string, error) { return metadata, nil } -// CreateHeader represents the headers returned in the response from a Create request. +// CreateHeader represents the headers returned in the response from a +// Create request. type CreateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` @@ -323,8 +327,7 @@ type CreateResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Create. To obtain -// a map of headers, call the ExtractHeader method on the CreateResult. +// Extract will return a struct of headers returned from a call to Create. func (r CreateResult) Extract() (*CreateHeader, error) { //if r.Header.Get("ETag") != fmt.Sprintf("%x", localChecksum) { // return nil, ErrWrongChecksum{} @@ -334,7 +337,8 @@ func (r CreateResult) Extract() (*CreateHeader, error) { return s, err } -// UpdateHeader represents the headers returned in the response from a Update request. +// UpdateHeader represents the headers returned in the response from a +// Update request. type UpdateHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` @@ -376,15 +380,15 @@ type UpdateResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Update. To obtain -// a map of headers, call the ExtractHeader method on the UpdateResult. +// Extract will return a struct of headers returned from a call to Update. func (r UpdateResult) Extract() (*UpdateHeader, error) { var s *UpdateHeader err := r.ExtractInto(&s) return s, err } -// DeleteHeader represents the headers returned in the response from a Delete request. +// DeleteHeader represents the headers returned in the response from a +// Delete request. type DeleteHeader struct { ContentLength int64 `json:"Content-Length"` ContentType string `json:"Content-Type"` @@ -426,15 +430,15 @@ type DeleteResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Delete. To obtain -// a map of headers, call the ExtractHeader method on the DeleteResult. +// Extract will return a struct of headers returned from a call to Delete. func (r DeleteResult) Extract() (*DeleteHeader, error) { var s *DeleteHeader err := r.ExtractInto(&s) return s, err } -// CopyHeader represents the headers returned in the response from a Copy request. +// CopyHeader represents the headers returned in the response from a +// Copy request. type CopyHeader struct { ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` @@ -484,8 +488,7 @@ type CopyResult struct { gophercloud.HeaderResult } -// Extract will return a struct of headers returned from a call to Copy. To obtain -// a map of headers, call the ExtractHeader method on the CopyResult. +// Extract will return a struct of headers returned from a call to Copy. func (r CopyResult) Extract() (*CopyHeader, error) { var s *CopyHeader err := r.ExtractInto(&s) diff --git a/openstack/objectstorage/v1/swauth/doc.go b/openstack/objectstorage/v1/swauth/doc.go new file mode 100644 index 0000000000..989dc4ece2 --- /dev/null +++ b/openstack/objectstorage/v1/swauth/doc.go @@ -0,0 +1,16 @@ +/* +Package swauth implements Swift's built-in authentication. + +Example to Authenticate with swauth + + authOpts := swauth.AuthOpts{ + User: "project:user", + Key: "password", + } + + swiftClient, err := swauth.NewObjectStorageV1(providerClient, authOpts) + if err != nil { + panic(err) + } +*/ +package swauth diff --git a/openstack/objectstorage/v1/swauth/requests.go b/openstack/objectstorage/v1/swauth/requests.go index e8589ae0cb..29bdcbcf76 100644 --- a/openstack/objectstorage/v1/swauth/requests.go +++ b/openstack/objectstorage/v1/swauth/requests.go @@ -3,7 +3,6 @@ package swauth import "github.com/gophercloud/gophercloud" // AuthOptsBuilder describes struct types that can be accepted by the Auth call. -// The AuthOpts struct in this package does. type AuthOptsBuilder interface { ToAuthOptsMap() (map[string]string, error) } @@ -12,6 +11,7 @@ type AuthOptsBuilder interface { type AuthOpts struct { // User is an Swauth-based username in username:tenant format. User string `h:"X-Auth-User" required:"true"` + // Key is a secret/password to authenticate the User with. Key string `h:"X-Auth-Key" required:"true"` } diff --git a/openstack/objectstorage/v1/swauth/results.go b/openstack/objectstorage/v1/swauth/results.go index 294c43c07c..f442f47255 100644 --- a/openstack/objectstorage/v1/swauth/results.go +++ b/openstack/objectstorage/v1/swauth/results.go @@ -4,8 +4,8 @@ import ( "github.com/gophercloud/gophercloud" ) -// GetAuthResult temporarily contains the response from a Swauth -// authentication call. +// GetAuthResult contains the response from the Auth request. Call its Extract +// method to interpret it as an AuthResult. type GetAuthResult struct { gophercloud.HeaderResult } diff --git a/results.go b/results.go index 6a5b0baaa6..e64feee19e 100644 --- a/results.go +++ b/results.go @@ -224,9 +224,8 @@ type HeaderResult struct { Result } -// ExtractHeader will return the http.Header and error from the HeaderResult. -// -// header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader() +// ExtractInto allows users to provide an object into which `Extract` will +// extract the http.Header headers of the result. func (r HeaderResult) ExtractInto(to interface{}) error { if r.Err != nil { return r.Err From 88274b6541e2fa195a17b4bc3a8e66deda301db0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 21 Aug 2017 23:04:48 -0600 Subject: [PATCH 0055/2296] Docs: update testing docs --- .../blockstorage/extensions/volumeactions/testing/doc.go | 2 +- openstack/common/extensions/testing/doc.go | 2 +- .../v2/extensions/attachinterfaces/testing/doc.go | 2 +- .../compute/v2/extensions/bootfromvolume/testing/doc.go | 2 +- .../compute/v2/extensions/defsecrules/testing/doc.go | 2 +- .../compute/v2/extensions/diskconfig/testing/doc.go | 2 +- .../compute/v2/extensions/floatingips/testing/doc.go | 2 +- openstack/compute/v2/extensions/keypairs/testing/doc.go | 2 +- openstack/compute/v2/extensions/networks/testing/doc.go | 2 +- .../compute/v2/extensions/pauseunpause/testing/doc.go | 2 +- openstack/compute/v2/extensions/quotasets/testing/doc.go | 2 +- .../compute/v2/extensions/schedulerhints/testing/doc.go | 2 +- openstack/compute/v2/extensions/secgroups/testing/doc.go | 2 +- .../compute/v2/extensions/servergroups/testing/doc.go | 2 +- openstack/compute/v2/extensions/startstop/testing/doc.go | 2 +- .../compute/v2/extensions/suspendresume/testing/doc.go | 2 +- .../compute/v2/extensions/tenantnetworks/testing/doc.go | 2 +- openstack/compute/v2/extensions/testing/doc.go | 2 +- .../compute/v2/extensions/volumeattach/testing/doc.go | 9 +-------- openstack/compute/v2/flavors/testing/doc.go | 2 +- openstack/compute/v2/images/testing/doc.go | 2 +- openstack/compute/v2/servers/testing/doc.go | 2 +- openstack/dns/v2/recordsets/testing/doc.go | 2 +- openstack/dns/v2/zones/testing/doc.go | 2 +- .../identity/v2/extensions/admin/roles/testing/doc.go | 2 +- openstack/identity/v2/extensions/testing/doc.go | 2 +- openstack/identity/v2/tenants/testing/doc.go | 2 +- openstack/identity/v2/tokens/testing/doc.go | 2 +- openstack/identity/v2/users/testing/doc.go | 2 +- openstack/identity/v3/endpoints/testing/doc.go | 2 +- openstack/identity/v3/extensions/trusts/testing/doc.go | 2 +- openstack/identity/v3/roles/testing/doc.go | 2 +- openstack/identity/v3/services/testing/doc.go | 2 +- openstack/identity/v3/tokens/testing/doc.go | 2 +- openstack/imageservice/v2/imagedata/testing/doc.go | 2 ++ openstack/imageservice/v2/images/testing/doc.go | 2 ++ openstack/imageservice/v2/members/testing/doc.go | 2 ++ openstack/networking/v2/apiversions/testing/doc.go | 2 +- .../networking/v2/extensions/external/testing/doc.go | 2 +- .../v2/extensions/fwaas/firewalls/testing/doc.go | 2 +- .../v2/extensions/fwaas/policies/testing/doc.go | 2 +- .../v2/extensions/fwaas/routerinsertion/testing/doc.go | 2 +- .../networking/v2/extensions/fwaas/rules/testing/doc.go | 2 +- .../v2/extensions/layer3/floatingips/testing/doc.go | 2 +- .../v2/extensions/layer3/routers/testing/doc.go | 2 +- .../v2/extensions/lbaas/members/testing/doc.go | 2 +- .../v2/extensions/lbaas/monitors/testing/doc.go | 2 +- .../networking/v2/extensions/lbaas/pools/testing/doc.go | 2 +- .../networking/v2/extensions/lbaas/vips/testing/doc.go | 2 +- .../v2/extensions/lbaas_v2/listeners/testing/doc.go | 2 +- .../v2/extensions/lbaas_v2/loadbalancers/testing/doc.go | 2 +- .../v2/extensions/lbaas_v2/monitors/testing/doc.go | 2 +- .../v2/extensions/lbaas_v2/pools/testing/doc.go | 2 +- .../networking/v2/extensions/portsbinding/testing/doc.go | 2 +- .../networking/v2/extensions/provider/testing/doc.go | 2 +- .../v2/extensions/security/groups/testing/doc.go | 2 +- .../v2/extensions/security/rules/testing/doc.go | 2 +- openstack/networking/v2/extensions/testing/doc.go | 2 +- openstack/networking/v2/networks/testing/doc.go | 2 +- openstack/networking/v2/ports/testing/doc.go | 2 +- openstack/networking/v2/subnets/testing/doc.go | 2 +- openstack/objectstorage/v1/accounts/testing/doc.go | 2 +- openstack/objectstorage/v1/containers/testing/doc.go | 2 +- openstack/objectstorage/v1/objects/testing/doc.go | 2 +- openstack/objectstorage/v1/swauth/testing/doc.go | 2 +- 65 files changed, 68 insertions(+), 69 deletions(-) create mode 100644 openstack/imageservice/v2/imagedata/testing/doc.go create mode 100644 openstack/imageservice/v2/images/testing/doc.go create mode 100644 openstack/imageservice/v2/members/testing/doc.go diff --git a/openstack/blockstorage/extensions/volumeactions/testing/doc.go b/openstack/blockstorage/extensions/volumeactions/testing/doc.go index e720733136..336406df1d 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/doc.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/doc.go @@ -1,2 +1,2 @@ -// volumeactions +// volumeactions unit tests package testing diff --git a/openstack/common/extensions/testing/doc.go b/openstack/common/extensions/testing/doc.go index 24b079593e..8d076f30a0 100644 --- a/openstack/common/extensions/testing/doc.go +++ b/openstack/common/extensions/testing/doc.go @@ -1,2 +1,2 @@ -// common_extensions +// common extensions unit tests package testing diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/doc.go b/openstack/compute/v2/extensions/attachinterfaces/testing/doc.go index d9da11d008..cfc07ad558 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_attachinterfaces_v2 +// attachinterfaces unit tests package testing diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go b/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go index cb879d9257..cf5048acba 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_bootfromvolume_v2 +// bootfromvolume unit tests package testing diff --git a/openstack/compute/v2/extensions/defsecrules/testing/doc.go b/openstack/compute/v2/extensions/defsecrules/testing/doc.go index 7e51c8f15a..6eeb60f057 100644 --- a/openstack/compute/v2/extensions/defsecrules/testing/doc.go +++ b/openstack/compute/v2/extensions/defsecrules/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_defsecrules_v2 +// defsecrules unit tests package testing diff --git a/openstack/compute/v2/extensions/diskconfig/testing/doc.go b/openstack/compute/v2/extensions/diskconfig/testing/doc.go index 54c863b523..52ab247565 100644 --- a/openstack/compute/v2/extensions/diskconfig/testing/doc.go +++ b/openstack/compute/v2/extensions/diskconfig/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_diskconfig_v2 +// diskconfig unit tests package testing diff --git a/openstack/compute/v2/extensions/floatingips/testing/doc.go b/openstack/compute/v2/extensions/floatingips/testing/doc.go index 961aeee276..82dfbe7fed 100644 --- a/openstack/compute/v2/extensions/floatingips/testing/doc.go +++ b/openstack/compute/v2/extensions/floatingips/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_floatingips_v2 +// floatingips unit tests package testing diff --git a/openstack/compute/v2/extensions/keypairs/testing/doc.go b/openstack/compute/v2/extensions/keypairs/testing/doc.go index 8f8aaca7d8..8d4200983e 100644 --- a/openstack/compute/v2/extensions/keypairs/testing/doc.go +++ b/openstack/compute/v2/extensions/keypairs/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_keypairs_v2 +// keypairs unit tests package testing diff --git a/openstack/compute/v2/extensions/networks/testing/doc.go b/openstack/compute/v2/extensions/networks/testing/doc.go index 76a18cdfa1..fc8511de47 100644 --- a/openstack/compute/v2/extensions/networks/testing/doc.go +++ b/openstack/compute/v2/extensions/networks/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_networks_v2 +// networks unit tests package testing diff --git a/openstack/compute/v2/extensions/pauseunpause/testing/doc.go b/openstack/compute/v2/extensions/pauseunpause/testing/doc.go index 104b80832e..0953867509 100644 --- a/openstack/compute/v2/extensions/pauseunpause/testing/doc.go +++ b/openstack/compute/v2/extensions/pauseunpause/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_pauseunpause_v2 +// pauseunpause unit tests package testing diff --git a/openstack/compute/v2/extensions/quotasets/testing/doc.go b/openstack/compute/v2/extensions/quotasets/testing/doc.go index 19ad75d54b..30d864eb95 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/doc.go +++ b/openstack/compute/v2/extensions/quotasets/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_quotasets_v2 +// quotasets unit tests package testing diff --git a/openstack/compute/v2/extensions/schedulerhints/testing/doc.go b/openstack/compute/v2/extensions/schedulerhints/testing/doc.go index 0640b5da57..1915aef2fe 100644 --- a/openstack/compute/v2/extensions/schedulerhints/testing/doc.go +++ b/openstack/compute/v2/extensions/schedulerhints/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_schedulerhints_v2 +// schedulerhints unit tests package testing diff --git a/openstack/compute/v2/extensions/secgroups/testing/doc.go b/openstack/compute/v2/extensions/secgroups/testing/doc.go index fbf46133e5..c5e60ea094 100644 --- a/openstack/compute/v2/extensions/secgroups/testing/doc.go +++ b/openstack/compute/v2/extensions/secgroups/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_secgroups_v2 +// secgroups unit tests package testing diff --git a/openstack/compute/v2/extensions/servergroups/testing/doc.go b/openstack/compute/v2/extensions/servergroups/testing/doc.go index 65433f7362..644bb49df1 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/doc.go +++ b/openstack/compute/v2/extensions/servergroups/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_servergroups_v2 +// servergroups unit tests package testing diff --git a/openstack/compute/v2/extensions/startstop/testing/doc.go b/openstack/compute/v2/extensions/startstop/testing/doc.go index 6135475739..b6c5b8c140 100644 --- a/openstack/compute/v2/extensions/startstop/testing/doc.go +++ b/openstack/compute/v2/extensions/startstop/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_startstop_v2 +// startstop unit tests package testing diff --git a/openstack/compute/v2/extensions/suspendresume/testing/doc.go b/openstack/compute/v2/extensions/suspendresume/testing/doc.go index b81051a39c..834f25516f 100644 --- a/openstack/compute/v2/extensions/suspendresume/testing/doc.go +++ b/openstack/compute/v2/extensions/suspendresume/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_suspendresume_v2 +// suspendresume unit tests package testing diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/doc.go b/openstack/compute/v2/extensions/tenantnetworks/testing/doc.go index 7ed7ce3f99..4639153ff3 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/testing/doc.go +++ b/openstack/compute/v2/extensions/tenantnetworks/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_tenantnetworks_v2 +// tenantnetworks unit tests package testing diff --git a/openstack/compute/v2/extensions/testing/doc.go b/openstack/compute/v2/extensions/testing/doc.go index 5818711cfb..3c5d459263 100644 --- a/openstack/compute/v2/extensions/testing/doc.go +++ b/openstack/compute/v2/extensions/testing/doc.go @@ -1,2 +1,2 @@ -// compute_extensions_v2 +// extensions unit tests package testing diff --git a/openstack/compute/v2/extensions/volumeattach/testing/doc.go b/openstack/compute/v2/extensions/volumeattach/testing/doc.go index 2cc0ab4af2..11dfc06942 100644 --- a/openstack/compute/v2/extensions/volumeattach/testing/doc.go +++ b/openstack/compute/v2/extensions/volumeattach/testing/doc.go @@ -1,9 +1,2 @@ -// compute_extensions_volumeattach_v2 +// volumeattach unit tests package testing - -/* -Package testing holds fixtures (which imports testing), -so that importing volumeattach package does not inadvertently import testing into production code -More information here: -https://github.com/gophercloud/gophercloud/issues/473 -*/ diff --git a/openstack/compute/v2/flavors/testing/doc.go b/openstack/compute/v2/flavors/testing/doc.go index 0d00761507..c27087b566 100644 --- a/openstack/compute/v2/flavors/testing/doc.go +++ b/openstack/compute/v2/flavors/testing/doc.go @@ -1,2 +1,2 @@ -// compute_flavors_v2 +// flavors unit tests package testing diff --git a/openstack/compute/v2/images/testing/doc.go b/openstack/compute/v2/images/testing/doc.go index 6f59ade68a..db10451530 100644 --- a/openstack/compute/v2/images/testing/doc.go +++ b/openstack/compute/v2/images/testing/doc.go @@ -1,2 +1,2 @@ -// compute_images_v2 +// images unit tests package testing diff --git a/openstack/compute/v2/servers/testing/doc.go b/openstack/compute/v2/servers/testing/doc.go index c7c598298b..b3fee3aacc 100644 --- a/openstack/compute/v2/servers/testing/doc.go +++ b/openstack/compute/v2/servers/testing/doc.go @@ -1,2 +1,2 @@ -// compute_servers_v2 +// servers unit tests package testing diff --git a/openstack/dns/v2/recordsets/testing/doc.go b/openstack/dns/v2/recordsets/testing/doc.go index f1361e129b..f4d91dc23c 100644 --- a/openstack/dns/v2/recordsets/testing/doc.go +++ b/openstack/dns/v2/recordsets/testing/doc.go @@ -1,2 +1,2 @@ -// dns recordsets v2 +// recordsets unit tests package testing diff --git a/openstack/dns/v2/zones/testing/doc.go b/openstack/dns/v2/zones/testing/doc.go index 54a0d217e0..b9b6286d75 100644 --- a/openstack/dns/v2/zones/testing/doc.go +++ b/openstack/dns/v2/zones/testing/doc.go @@ -1,2 +1,2 @@ -// dns_zones_v2 +// zones unit tests package testing diff --git a/openstack/identity/v2/extensions/admin/roles/testing/doc.go b/openstack/identity/v2/extensions/admin/roles/testing/doc.go index 70ba643262..f4c5dab5b7 100644 --- a/openstack/identity/v2/extensions/admin/roles/testing/doc.go +++ b/openstack/identity/v2/extensions/admin/roles/testing/doc.go @@ -1,2 +1,2 @@ -// identity_extensions_admin_roles_v2 +// roles unit tests package testing diff --git a/openstack/identity/v2/extensions/testing/doc.go b/openstack/identity/v2/extensions/testing/doc.go index 6d4b67d181..3c5d459263 100644 --- a/openstack/identity/v2/extensions/testing/doc.go +++ b/openstack/identity/v2/extensions/testing/doc.go @@ -1,2 +1,2 @@ -// identity_extensions_v2 +// extensions unit tests package testing diff --git a/openstack/identity/v2/tenants/testing/doc.go b/openstack/identity/v2/tenants/testing/doc.go index 57aaa1f035..c08b8c37e0 100644 --- a/openstack/identity/v2/tenants/testing/doc.go +++ b/openstack/identity/v2/tenants/testing/doc.go @@ -1,2 +1,2 @@ -// identity_tenants_v2 +// tenants unit tests package testing diff --git a/openstack/identity/v2/tokens/testing/doc.go b/openstack/identity/v2/tokens/testing/doc.go index f9767eb12f..a7955a717e 100644 --- a/openstack/identity/v2/tokens/testing/doc.go +++ b/openstack/identity/v2/tokens/testing/doc.go @@ -1,2 +1,2 @@ -// identity_tokens_v2 +// tokens unit tests package testing diff --git a/openstack/identity/v2/users/testing/doc.go b/openstack/identity/v2/users/testing/doc.go index a007def604..4519dcad7a 100644 --- a/openstack/identity/v2/users/testing/doc.go +++ b/openstack/identity/v2/users/testing/doc.go @@ -1,2 +1,2 @@ -// identity_users_v2 +// users unit tests package testing diff --git a/openstack/identity/v3/endpoints/testing/doc.go b/openstack/identity/v3/endpoints/testing/doc.go index b6e89eff98..1370fdcc77 100644 --- a/openstack/identity/v3/endpoints/testing/doc.go +++ b/openstack/identity/v3/endpoints/testing/doc.go @@ -1,2 +1,2 @@ -// identity_endpoints_v3 +// endpoints unit tests package testing diff --git a/openstack/identity/v3/extensions/trusts/testing/doc.go b/openstack/identity/v3/extensions/trusts/testing/doc.go index e660e2039e..e9614fdcca 100644 --- a/openstack/identity/v3/extensions/trusts/testing/doc.go +++ b/openstack/identity/v3/extensions/trusts/testing/doc.go @@ -1,2 +1,2 @@ -// identity_extensions_trusts_v3 +// trusts unit tests package testing diff --git a/openstack/identity/v3/roles/testing/doc.go b/openstack/identity/v3/roles/testing/doc.go index 37bcb85a59..f4c5dab5b7 100644 --- a/openstack/identity/v3/roles/testing/doc.go +++ b/openstack/identity/v3/roles/testing/doc.go @@ -1,2 +1,2 @@ -// identity_roles_v3 +// roles unit tests package testing diff --git a/openstack/identity/v3/services/testing/doc.go b/openstack/identity/v3/services/testing/doc.go index e4f1167ebc..68b21e970e 100644 --- a/openstack/identity/v3/services/testing/doc.go +++ b/openstack/identity/v3/services/testing/doc.go @@ -1,2 +1,2 @@ -// identity_services_v3 +// services unit tests package testing diff --git a/openstack/identity/v3/tokens/testing/doc.go b/openstack/identity/v3/tokens/testing/doc.go index ad1d35de9b..a7955a717e 100644 --- a/openstack/identity/v3/tokens/testing/doc.go +++ b/openstack/identity/v3/tokens/testing/doc.go @@ -1,2 +1,2 @@ -// identity_tokens_v3 +// tokens unit tests package testing diff --git a/openstack/imageservice/v2/imagedata/testing/doc.go b/openstack/imageservice/v2/imagedata/testing/doc.go new file mode 100644 index 0000000000..5a9db1bef3 --- /dev/null +++ b/openstack/imageservice/v2/imagedata/testing/doc.go @@ -0,0 +1,2 @@ +// imagedata unit tests +package testing diff --git a/openstack/imageservice/v2/images/testing/doc.go b/openstack/imageservice/v2/images/testing/doc.go new file mode 100644 index 0000000000..db10451530 --- /dev/null +++ b/openstack/imageservice/v2/images/testing/doc.go @@ -0,0 +1,2 @@ +// images unit tests +package testing diff --git a/openstack/imageservice/v2/members/testing/doc.go b/openstack/imageservice/v2/members/testing/doc.go new file mode 100644 index 0000000000..1afbc434f6 --- /dev/null +++ b/openstack/imageservice/v2/members/testing/doc.go @@ -0,0 +1,2 @@ +// members unit tests +package testing diff --git a/openstack/networking/v2/apiversions/testing/doc.go b/openstack/networking/v2/apiversions/testing/doc.go index 0accd9911c..cc76de0a62 100644 --- a/openstack/networking/v2/apiversions/testing/doc.go +++ b/openstack/networking/v2/apiversions/testing/doc.go @@ -1,2 +1,2 @@ -// networking_apiversions_v2 +// apiversions unit tests package testing diff --git a/openstack/networking/v2/extensions/external/testing/doc.go b/openstack/networking/v2/extensions/external/testing/doc.go index 8a30f6b9f9..5641e7980a 100644 --- a/openstack/networking/v2/extensions/external/testing/doc.go +++ b/openstack/networking/v2/extensions/external/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_external_v2 +// external unit tests package testing diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go b/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go index 6b46bba2b9..82ebf979ba 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_fwaas_firewalls_v2 +// firewalls unit tests package testing diff --git a/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go b/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go index d2707f0c19..a61f5488e3 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go +++ b/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_fwaas_policies_v2 +// policies unit tests package testing diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go index 36a6c1c434..86e710f6e1 100644 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go +++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_fwaas_extensions_routerinsertion_v2 +// routerinsertion unit tests package testing diff --git a/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go b/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go index 481ae2e5e9..df31e6c5c3 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go +++ b/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_fwaas_rules_v2 +// rules unit tests package testing diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/doc.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/doc.go index aa133389c8..82dfbe7fed 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/doc.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_layer3_floatingips_v2 +// floatingips unit tests package testing diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/doc.go b/openstack/networking/v2/extensions/layer3/routers/testing/doc.go index ef44c2e5c3..4bfd0b5f21 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/doc.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_layer3_routers_v2 +// routers unit tests package testing diff --git a/openstack/networking/v2/extensions/lbaas/members/testing/doc.go b/openstack/networking/v2/extensions/lbaas/members/testing/doc.go index 3878904e82..1afbc434f6 100644 --- a/openstack/networking/v2/extensions/lbaas/members/testing/doc.go +++ b/openstack/networking/v2/extensions/lbaas/members/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_lbaas_members_v2 +// members unit tests package testing diff --git a/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go b/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go index 5ee866bbf2..e2b6f12a92 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_lbaas_monitors_v2 +// monitors unit tests package testing diff --git a/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go b/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go index 415dd2c93c..46e335f3f2 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go +++ b/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_lbaas_pools_v2 +// pools unit tests package testing diff --git a/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go b/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go index 8e91e78bc5..e04046fbef 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go +++ b/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_lbaas_vips_v2 +// vips unit tests package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/doc.go index c74a4de741..f41387e827 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_lbaas_v2_listeners_v2 +// listeners unit tests package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/doc.go index b06352ec4a..b54468c82f 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_lbaas_v2_loadbalancers_v2 +// loadbalancers unit tests package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/doc.go index 443f9ade3d..e2b6f12a92 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_lbaas_v2_monitors_v2 +// monitors unit tests package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/doc.go index 65eb52174c..46e335f3f2 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_lbaas_v2_pools_v2 +// pools unit tests package testing diff --git a/openstack/networking/v2/extensions/portsbinding/testing/doc.go b/openstack/networking/v2/extensions/portsbinding/testing/doc.go index deb52b1380..abdc76d8a2 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/doc.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_portsbinding_v2 +// portsbindings unit tests package testing diff --git a/openstack/networking/v2/extensions/provider/testing/doc.go b/openstack/networking/v2/extensions/provider/testing/doc.go index 370ce194d0..25d453926d 100644 --- a/openstack/networking/v2/extensions/provider/testing/doc.go +++ b/openstack/networking/v2/extensions/provider/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_provider_v2 +// provider unit tests package testing diff --git a/openstack/networking/v2/extensions/security/groups/testing/doc.go b/openstack/networking/v2/extensions/security/groups/testing/doc.go index 69d5db7495..794dee5b11 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/doc.go +++ b/openstack/networking/v2/extensions/security/groups/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_security_groups_v2 +// groups unit tests package testing diff --git a/openstack/networking/v2/extensions/security/rules/testing/doc.go b/openstack/networking/v2/extensions/security/rules/testing/doc.go index a4f7b43c74..df31e6c5c3 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/doc.go +++ b/openstack/networking/v2/extensions/security/rules/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_security_rules_v2 +// rules unit tests package testing diff --git a/openstack/networking/v2/extensions/testing/doc.go b/openstack/networking/v2/extensions/testing/doc.go index 5a104fbc86..3c5d459263 100644 --- a/openstack/networking/v2/extensions/testing/doc.go +++ b/openstack/networking/v2/extensions/testing/doc.go @@ -1,2 +1,2 @@ -// networking_extensions_v2 +// extensions unit tests package testing diff --git a/openstack/networking/v2/networks/testing/doc.go b/openstack/networking/v2/networks/testing/doc.go index 860bd7a972..fc8511de47 100644 --- a/openstack/networking/v2/networks/testing/doc.go +++ b/openstack/networking/v2/networks/testing/doc.go @@ -1,2 +1,2 @@ -// networking_networks_v2 +// networks unit tests package testing diff --git a/openstack/networking/v2/ports/testing/doc.go b/openstack/networking/v2/ports/testing/doc.go index 70a559a1c7..bf82f4eb0d 100644 --- a/openstack/networking/v2/ports/testing/doc.go +++ b/openstack/networking/v2/ports/testing/doc.go @@ -1,2 +1,2 @@ -// networking_ports_v2 +// ports unit tests package testing diff --git a/openstack/networking/v2/subnets/testing/doc.go b/openstack/networking/v2/subnets/testing/doc.go index 43be31a693..e07714bae3 100644 --- a/openstack/networking/v2/subnets/testing/doc.go +++ b/openstack/networking/v2/subnets/testing/doc.go @@ -1,2 +1,2 @@ -// networking_subnets_v2 +// subnets unit tests package testing diff --git a/openstack/objectstorage/v1/accounts/testing/doc.go b/openstack/objectstorage/v1/accounts/testing/doc.go index b8fdf88c37..d6ad0afdd0 100644 --- a/openstack/objectstorage/v1/accounts/testing/doc.go +++ b/openstack/objectstorage/v1/accounts/testing/doc.go @@ -1,2 +1,2 @@ -// objectstorage_accounts_v1 +// accounts unit tests package testing diff --git a/openstack/objectstorage/v1/containers/testing/doc.go b/openstack/objectstorage/v1/containers/testing/doc.go index c27fa49329..a39f42b41f 100644 --- a/openstack/objectstorage/v1/containers/testing/doc.go +++ b/openstack/objectstorage/v1/containers/testing/doc.go @@ -1,2 +1,2 @@ -// objectstorage_containers_v1 +// containers unit tests package testing diff --git a/openstack/objectstorage/v1/objects/testing/doc.go b/openstack/objectstorage/v1/objects/testing/doc.go index f008a801de..9ca1d8a11a 100644 --- a/openstack/objectstorage/v1/objects/testing/doc.go +++ b/openstack/objectstorage/v1/objects/testing/doc.go @@ -1,2 +1,2 @@ -// objectstorage_objects_v1 +// objects unit tests package testing diff --git a/openstack/objectstorage/v1/swauth/testing/doc.go b/openstack/objectstorage/v1/swauth/testing/doc.go index ff3bf3797b..2388e8d405 100644 --- a/openstack/objectstorage/v1/swauth/testing/doc.go +++ b/openstack/objectstorage/v1/swauth/testing/doc.go @@ -1,2 +1,2 @@ -// objectstorage_swauth_v1 +// swauth unit tests package testing From 2f0c4b71a62485898f9f143693cc4ba4527d097d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 23 Aug 2017 18:47:22 -0600 Subject: [PATCH 0056/2296] Compute v2: Use value receiver for ToFlavorCreateMap --- openstack/compute/v2/flavors/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 9578e8f87b..4e9fbcc033 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -124,7 +124,7 @@ type CreateOpts struct { } // ToFlavorCreateMap constructs a request body from CreateOpts. -func (opts *CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "flavor") } From ac9fa73081da3bec4faaaabe9ecc05050e145189 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 23 Aug 2017 19:18:54 -0600 Subject: [PATCH 0057/2296] Compute v2: Adding Migrate Action (#444) * Compute v2: Adding Migrate Action * Compute v2: Use MigrateResult for the Migrate response * Compute v2: Docs for Migrate --- .../openstack/compute/v2/migrate_test.go | 30 +++++++++++++++++++ .../compute/v2/extensions/migrate/doc.go | 13 ++++++++ .../compute/v2/extensions/migrate/requests.go | 11 +++++++ .../compute/v2/extensions/migrate/results.go | 11 +++++++ .../v2/extensions/migrate/testing/doc.go | 2 ++ .../v2/extensions/migrate/testing/fixtures.go | 18 +++++++++++ .../migrate/testing/requests_test.go | 21 +++++++++++++ .../compute/v2/extensions/migrate/urls.go | 9 ++++++ 8 files changed, 115 insertions(+) create mode 100644 acceptance/openstack/compute/v2/migrate_test.go create mode 100644 openstack/compute/v2/extensions/migrate/doc.go create mode 100644 openstack/compute/v2/extensions/migrate/requests.go create mode 100644 openstack/compute/v2/extensions/migrate/results.go create mode 100644 openstack/compute/v2/extensions/migrate/testing/doc.go create mode 100644 openstack/compute/v2/extensions/migrate/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/migrate/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/migrate/urls.go diff --git a/acceptance/openstack/compute/v2/migrate_test.go b/acceptance/openstack/compute/v2/migrate_test.go new file mode 100644 index 0000000000..4d03350100 --- /dev/null +++ b/acceptance/openstack/compute/v2/migrate_test.go @@ -0,0 +1,30 @@ +// +build acceptance compute servers + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate" +) + +func TestMigrate(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + server, err := CreateServer(t, client) + if err != nil { + t.Fatalf("Unable to create server: %v", err) + } + defer DeleteServer(t, client, server) + + t.Logf("Attempting to migrate server %s", server.ID) + + err = migrate.Migrate(client, server.ID).ExtractErr() + if err != nil { + t.Fatalf("Error during migration: %v", err) + } +} diff --git a/openstack/compute/v2/extensions/migrate/doc.go b/openstack/compute/v2/extensions/migrate/doc.go new file mode 100644 index 0000000000..86750d6c6f --- /dev/null +++ b/openstack/compute/v2/extensions/migrate/doc.go @@ -0,0 +1,13 @@ +/* +Package migrate provides functionality to migrate servers that have been +provisioned by the OpenStack Compute service. + +Example to Migrate a Server + + serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" + err := migrate.Migrate(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package migrate diff --git a/openstack/compute/v2/extensions/migrate/requests.go b/openstack/compute/v2/extensions/migrate/requests.go new file mode 100644 index 0000000000..9f263fa3ba --- /dev/null +++ b/openstack/compute/v2/extensions/migrate/requests.go @@ -0,0 +1,11 @@ +package migrate + +import ( + "github.com/gophercloud/gophercloud" +) + +// Migrate will initiate a migration of the instance to another host. +func Migrate(client *gophercloud.ServiceClient, id string) (r MigrateResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) + return +} diff --git a/openstack/compute/v2/extensions/migrate/results.go b/openstack/compute/v2/extensions/migrate/results.go new file mode 100644 index 0000000000..62997b7f5d --- /dev/null +++ b/openstack/compute/v2/extensions/migrate/results.go @@ -0,0 +1,11 @@ +package migrate + +import ( + "github.com/gophercloud/gophercloud" +) + +// MigrateResult is the response from a Migrate operation. Call its ExtractErr +// method to determine if the suceeded or failed. +type MigrateResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/migrate/testing/doc.go b/openstack/compute/v2/extensions/migrate/testing/doc.go new file mode 100644 index 0000000000..6135475739 --- /dev/null +++ b/openstack/compute/v2/extensions/migrate/testing/doc.go @@ -0,0 +1,2 @@ +// compute_extensions_startstop_v2 +package testing diff --git a/openstack/compute/v2/extensions/migrate/testing/fixtures.go b/openstack/compute/v2/extensions/migrate/testing/fixtures.go new file mode 100644 index 0000000000..8a59aa3478 --- /dev/null +++ b/openstack/compute/v2/extensions/migrate/testing/fixtures.go @@ -0,0 +1,18 @@ +package testing + +import ( + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func mockMigrateResponse(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, `{"migrate": null}`) + 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 new file mode 100644 index 0000000000..7d14365d6a --- /dev/null +++ b/openstack/compute/v2/extensions/migrate/testing/requests_test.go @@ -0,0 +1,21 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" + +func TestMigrate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockMigrateResponse(t, serverID) + + err := migrate.Migrate(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/migrate/urls.go b/openstack/compute/v2/extensions/migrate/urls.go new file mode 100644 index 0000000000..b8e2a437d7 --- /dev/null +++ b/openstack/compute/v2/extensions/migrate/urls.go @@ -0,0 +1,9 @@ +package migrate + +import ( + "github.com/gophercloud/gophercloud" +) + +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "action") +} From c1f5d51119fe82d2e99a307a15ef63179440eb5c Mon Sep 17 00:00:00 2001 From: Gavin Williams Date: Fri, 25 Aug 2017 06:39:05 +0100 Subject: [PATCH 0058/2296] Flavors support delete update (#501) * flavors: Add support for deleting flavors * flavors: Add unit test for Delete --- openstack/compute/v2/flavors/requests.go | 6 ++++++ openstack/compute/v2/flavors/results.go | 6 ++++++ .../compute/v2/flavors/testing/requests_test.go | 15 +++++++++++++++ openstack/compute/v2/flavors/urls.go | 4 ++++ 4 files changed, 31 insertions(+) diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 4e9fbcc033..317fd8530c 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -148,6 +148,12 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } +// Delete deletes the specified flavor ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + // IDFromName is a convienience function that returns a flavor's ID given its // name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index b4f7ffad01..fda11d3e06 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -22,6 +22,12 @@ type GetResult struct { commonResult } +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // Extract provides access to the individual Flavor returned by the Get and // Create functions. func (r commonResult) Extract() (*Flavor, error) { diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 470a44454e..e098ed648e 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -197,3 +197,18 @@ func TestCreateFlavor(t *testing.T) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestDeleteFlavor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/flavors/12345678", 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) + }) + + res := flavors.Delete(fake.ServiceClient(), "12345678") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go index 2fc21796f7..518d05b369 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -15,3 +15,7 @@ func listURL(client *gophercloud.ServiceClient) string { func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("flavors") } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id) +} From 63e61e77975bcfcfa04c48cc45599461dc88cdf4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 26 Aug 2017 09:14:51 -0600 Subject: [PATCH 0059/2296] Compute v2: Flavor acceptance test --- acceptance/openstack/compute/v2/compute.go | 35 +++++++++++++++++++ .../openstack/compute/v2/flavors_test.go | 15 ++++++++ 2 files changed, 50 insertions(+) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 6859bb7810..2ff9e88027 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -23,6 +23,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" + "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "golang.org/x/crypto/ssh" @@ -136,6 +137,29 @@ func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.Def return *defaultRule, nil } +// CreateFlavor will create a flavor with a random name. +// 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) + t.Logf("Attempting to create flavor %s", flavorName) + + createOpts := flavors.CreateOpts{ + Name: flavorName, + RAM: 1, + VCPUs: 1, + Disk: gophercloud.IntToPointer(1), + } + + flavor, err := flavors.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created flavor %s", flavor.ID) + + return flavor, nil +} + // CreateFloatingIP will allocate a floating IP. // An error will be returend if one was unable to be allocated. func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) { @@ -531,6 +555,17 @@ func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultR t.Logf("Deleted default rule: %s", defaultRule.ID) } +// DeleteFlavor will delete a flavor. A fatal error will occur if the flavor +// could not be deleted. This works best when using it as a deferred function. +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 %s", flavor.ID) + } + + t.Logf("Deleted flavor: %s", flavor.ID) +} + // DeleteFloatingIP will de-allocate a floating IP. A fatal error will occur if // the floating IP failed to de-allocate. This works best when using it as a // deferred function. diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index 64ffaccf85..004571fc8a 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -72,3 +72,18 @@ func TestFlavorsGet(t *testing.T) { tools.PrintResource(t, flavor) } + +func TestFlavorCreateDelete(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + flavor, err := CreateFlavor(t, client) + if err != nil { + t.Fatalf("Unable to create flavor: %v", err) + } + defer DeleteFlavor(t, client, flavor) + + tools.PrintResource(t, flavor) +} From b9b949b366dcaa40a92059ce29710d8dd0795bc0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 6 Aug 2017 04:54:25 +0000 Subject: [PATCH 0060/2296] OpenStack Client: Add v3 suffix if missing from the identity endpoint This commit will add the v3 suffix to the identity endpoint of Identity ServiceClients if it is missing. This is to work around the Identity service publishing a versionless endpoint and Gophercloud's current EndpointURL parsing not querying the discovery URL for further details about the endpoint. --- openstack/client.go | 7 +++++++ openstack/testing/client_test.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/openstack/client.go b/openstack/client.go index cc6ec0aa4d..db3b636c72 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -220,6 +220,13 @@ func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp } } + // Ensure endpoint still has a suffix of v3. + // This is because EndpointLocator might have found a versionless + // endpoint and requests will fail unless targeted at /v3. + if !strings.HasSuffix(endpoint, "v3/") { + endpoint = endpoint + "v3/" + } + return &gophercloud.ServiceClient{ ProviderClient: client, Endpoint: endpoint, diff --git a/openstack/testing/client_test.go b/openstack/testing/client_test.go index 3fe768fa42..6f94e44e0d 100644 --- a/openstack/testing/client_test.go +++ b/openstack/testing/client_test.go @@ -289,7 +289,7 @@ func TestIdentityAdminV3Client(t *testing.T) { Availability: gophercloud.AvailabilityAdmin, }) th.AssertNoErr(t, err) - th.CheckEquals(t, "http://localhost:35357/", sc.Endpoint) + th.CheckEquals(t, "http://localhost:35357/v3/", sc.Endpoint) } func testAuthenticatedClientFails(t *testing.T, endpoint string) { From 96ac26acb8d1e39587402b27c66834c41d63f3cc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 31 Aug 2017 05:19:00 +0000 Subject: [PATCH 0061/2296] Networking v2: Fix Port and Security Groups This commit fixes the way security groups are handled during port creation and updating. If no SecurityGroups are specified during Create, then `security_groups` is not passed in the request body. The default behavior for this is to have the default security group applied to the port. If a pointer to an empty string slice is passed, then the port is created with no security groups applied. If no SecurityGroups are specified during Update, then no modification is done to the port's security groups. If a pointer to an empty string slice is passed, then all security groups are removed. --- openstack/networking/v2/ports/doc.go | 4 +- openstack/networking/v2/ports/requests.go | 4 +- .../v2/ports/testing/requests_test.go | 289 +++++++++++++++++- 3 files changed, 289 insertions(+), 8 deletions(-) diff --git a/openstack/networking/v2/ports/doc.go b/openstack/networking/v2/ports/doc.go index 654a17181c..cfb1774fb4 100644 --- a/openstack/networking/v2/ports/doc.go +++ b/openstack/networking/v2/ports/doc.go @@ -37,7 +37,7 @@ Example to Create a Port FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, - SecurityGroups: []string{"foo"}, + SecurityGroups: &[]string{"foo"}, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, @@ -54,7 +54,7 @@ Example to Update a Port updateOpts := ports.UpdateOpts{ Name: "new_name", - SecurityGroups: []string{}, + SecurityGroups: &[]string{}, } port, err := ports.Update(networkClient, portID, updateOpts).Extract() diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 0ca08716d2..63e272d94a 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -81,7 +81,7 @@ type CreateOpts struct { DeviceID string `json:"device_id,omitempty"` DeviceOwner string `json:"device_owner,omitempty"` TenantID string `json:"tenant_id,omitempty"` - SecurityGroups []string `json:"security_groups,omitempty"` + SecurityGroups *[]string `json:"security_groups,omitempty"` AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` } @@ -115,7 +115,7 @@ type UpdateOpts struct { FixedIPs interface{} `json:"fixed_ips,omitempty"` DeviceID string `json:"device_id,omitempty"` DeviceOwner string `json:"device_owner,omitempty"` - SecurityGroups []string `json:"security_groups"` + SecurityGroups *[]string `json:"security_groups,omitempty"` AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"` } diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 1da6ad3a3d..85a603218d 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -219,7 +219,7 @@ func TestCreate(t *testing.T) { FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, - SecurityGroups: []string{"foo"}, + SecurityGroups: &[]string{"foo"}, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, @@ -244,6 +244,200 @@ func TestCreate(t *testing.T) { }) } +func TestCreateOmitSecurityGroups(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, ` +{ + "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" + } + ], + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "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" + } + ], + "device_id": "" + } +} + `) + }) + + 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"}, + }, + AllowedAddressPairs: []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }, + } + 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"}, + }) +} + +func TestCreateWithNoSecurityGroup(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, ` +{ + "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": [], + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "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", + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ], + "device_id": "" + } +} + `) + }) + + 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{}, + AllowedAddressPairs: []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }, + } + 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.AllowedAddressPairs, []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }) +} + func TestRequiredCreateOpts(t *testing.T) { res := ports.Create(fake.ServiceClient(), ports.CreateOpts{}) if res.Err == nil { @@ -323,7 +517,94 @@ func TestUpdate(t *testing.T) { FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, - SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, + SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, + AllowedAddressPairs: []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }, + } + + s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "new_port_name") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }) + th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }) + th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) +} + +func TestUpdateOmitSecurityGroups(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, ` +{ + "port": { + "name": "new_port_name", + "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" + } + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "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" + ], + "device_id": "" + } +} + `) + }) + + options := ports.UpdateOpts{ + Name: "new_port_name", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, @@ -409,7 +690,7 @@ func TestRemoveSecurityGroups(t *testing.T) { FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, - SecurityGroups: []string{}, + SecurityGroups: &[]string{}, AllowedAddressPairs: []ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, @@ -489,7 +770,7 @@ func TestRemoveAllowedAddressPairs(t *testing.T) { FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, - SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, + SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, AllowedAddressPairs: []ports.AddressPair{}, } From 3efdd8f96f3fc75ced2399c9ec0b7fd8c0c6da83 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 31 Aug 2017 05:24:52 +0000 Subject: [PATCH 0062/2296] Networking v2: Fix Port and Security Groups acceptance tests --- .../networking/v2/extensions/extensions.go | 2 +- .../openstack/networking/v2/networking.go | 35 ++++++ .../openstack/networking/v2/ports_test.go | 103 +++++++++++++++++- 3 files changed, 137 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/extensions.go b/acceptance/openstack/networking/v2/extensions/extensions.go index 96c6e42cfa..29a31e91a5 100644 --- a/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/acceptance/openstack/networking/v2/extensions/extensions.go @@ -55,7 +55,7 @@ func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient Name: portName, AdminStateUp: &iFalse, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, - SecurityGroups: []string{secGroupID}, + SecurityGroups: &[]string{secGroupID}, } port, err := ports.Create(client, createOpts).Extract() diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index c5e7ca2315..bc463dde93 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -64,6 +64,41 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subn return newPort, nil } +// CreatePortWithNoSecurityGroup will create a port with no security group +// attached. An error will be returned if the port could not be created. +func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { + portName := tools.RandomString("TESTACC-", 8) + iFalse := false + + t.Logf("Attempting to create port: %s", portName) + + createOpts := ports.CreateOpts{ + NetworkID: networkID, + Name: portName, + AdminStateUp: &iFalse, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + SecurityGroups: &[]string{}, + } + + port, err := ports.Create(client, createOpts).Extract() + if err != nil { + return port, err + } + + if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + return port, err + } + + newPort, err := ports.Get(client, port.ID).Extract() + if err != nil { + return newPort, err + } + + t.Logf("Successfully created port: %s", portName) + + return newPort, nil +} + // CreateSubnet will create a subnet on the specified Network ID. An error // will be returned if the subnet could not be created. func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 394e90fca2..844d1319da 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -59,6 +59,10 @@ func TestPortsCRUD(t *testing.T) { } defer DeletePort(t, client, port.ID) + if len(port.SecurityGroups) != 1 { + t.Logf("WARNING: Port did not have a default security group applied") + } + tools.PrintResource(t, port) // Update port @@ -112,7 +116,7 @@ func TestPortsRemoveSecurityGroups(t *testing.T) { // Add the group to the port updateOpts := ports.UpdateOpts{ - SecurityGroups: []string{group.ID}, + SecurityGroups: &[]string{group.ID}, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() if err != nil { @@ -121,7 +125,7 @@ func TestPortsRemoveSecurityGroups(t *testing.T) { // Remove the group updateOpts = ports.UpdateOpts{ - SecurityGroups: []string{}, + SecurityGroups: &[]string{}, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() if err != nil { @@ -135,6 +139,101 @@ func TestPortsRemoveSecurityGroups(t *testing.T) { } } +func TestPortsDontAlterSecurityGroups(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer DeleteSubnet(t, client, subnet.ID) + + // Create a Security Group + group, err := extensions.CreateSecurityGroup(t, client) + if err != nil { + t.Fatalf("Unable to create security group: %v", err) + } + defer extensions.DeleteSecurityGroup(t, client, group.ID) + + // Create port + port, err := CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + defer DeletePort(t, client, port.ID) + + tools.PrintResource(t, port) + + // Add the group to the port + updateOpts := ports.UpdateOpts{ + SecurityGroups: &[]string{group.ID}, + } + newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Could not update port: %v", err) + } + + // Update the port again + updateOpts = ports.UpdateOpts{ + Name: "some_port", + } + newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Could not update port: %v", err) + } + + tools.PrintResource(t, newPort) + + if len(newPort.SecurityGroups) == 0 { + t.Fatalf("Port had security group updated") + } +} + +func TestPortsWithNoSecurityGroup(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer DeleteSubnet(t, client, subnet.ID) + + // Create port + port, err := CreatePortWithNoSecurityGroup(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + defer DeletePort(t, client, port.ID) + + tools.PrintResource(t, port) + + if len(port.SecurityGroups) != 0 { + t.Fatalf("Port was created with security groups") + } +} + func TestPortsRemoveAddressPair(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { From d8b7bfe90431e0eafb697424b3a1fe2de76287d3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 31 Aug 2017 05:41:06 +0000 Subject: [PATCH 0063/2296] Networking v2: Fix Port and Allowed Address Pairs This commit fixes the way Allowed Address Pairs are handled during port updating. If no AllowedAddressPairs are specified during update, then `allowed_address_pairs` is not passed in the request body. Existing address pairs on the port will remain untouched. If a pointer to an empty string slice is passed, then the address pairs are removed from the port. --- openstack/networking/v2/ports/requests.go | 14 +-- .../v2/ports/testing/requests_test.go | 90 ++++++++++++++++++- 2 files changed, 93 insertions(+), 11 deletions(-) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 63e272d94a..fd1e972576 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -110,13 +110,13 @@ type UpdateOptsBuilder interface { // UpdateOpts represents the attributes used when updating an existing port. type UpdateOpts struct { - Name string `json:"name,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"` + Name string `json:"name,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"` } // ToPortUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 85a603218d..c55cbb2342 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -518,7 +518,7 @@ func TestUpdate(t *testing.T) { {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, - AllowedAddressPairs: []ports.AddressPair{ + AllowedAddressPairs: &[]ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } @@ -605,7 +605,7 @@ func TestUpdateOmitSecurityGroups(t *testing.T) { FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, - AllowedAddressPairs: []ports.AddressPair{ + AllowedAddressPairs: &[]ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } @@ -691,7 +691,7 @@ func TestRemoveSecurityGroups(t *testing.T) { {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{}, - AllowedAddressPairs: []ports.AddressPair{ + AllowedAddressPairs: &[]ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } @@ -771,7 +771,7 @@ func TestRemoveAllowedAddressPairs(t *testing.T) { {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, - AllowedAddressPairs: []ports.AddressPair{}, + AllowedAddressPairs: &[]ports.AddressPair{}, } s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() @@ -785,6 +785,88 @@ func TestRemoveAllowedAddressPairs(t *testing.T) { th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) } +func TestDontUpdateAllowedAddressPairs(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, ` +{ + "port": { + "name": "new_port_name", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.3" + } + ], + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "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" + ], + "device_id": "" + } +} + `) + }) + + options := ports.UpdateOpts{ + Name: "new_port_name", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }, + SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, + } + + s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "new_port_name") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }) + th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{ + {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, + }) + th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 45bd7d55ae7ba8c2e8d25910060ff937c35ef3d4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 31 Aug 2017 05:43:21 +0000 Subject: [PATCH 0064/2296] Networking v2: Fix Port and Allowed Address Pairs acceptance test --- .../openstack/networking/v2/ports_test.go | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 844d1319da..eddddf64f2 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -265,7 +265,7 @@ func TestPortsRemoveAddressPair(t *testing.T) { // Add an address pair to the port updateOpts := ports.UpdateOpts{ - AllowedAddressPairs: []ports.AddressPair{ + AllowedAddressPairs: &[]ports.AddressPair{ ports.AddressPair{IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, }, } @@ -276,7 +276,7 @@ func TestPortsRemoveAddressPair(t *testing.T) { // Remove the address pair updateOpts = ports.UpdateOpts{ - AllowedAddressPairs: []ports.AddressPair{}, + AllowedAddressPairs: &[]ports.AddressPair{}, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() if err != nil { @@ -289,3 +289,61 @@ func TestPortsRemoveAddressPair(t *testing.T) { t.Fatalf("Unable to remove the address pair") } } + +func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer DeleteSubnet(t, client, subnet.ID) + + // Create port + port, err := CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + defer DeletePort(t, client, port.ID) + + tools.PrintResource(t, port) + + // Add an address pair to the port + updateOpts := ports.UpdateOpts{ + AllowedAddressPairs: &[]ports.AddressPair{ + ports.AddressPair{IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, + }, + } + newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Could not update port: %v", err) + } + + tools.PrintResource(t, newPort) + + // Remove the address pair + updateOpts = ports.UpdateOpts{ + Name: "some_port", + } + newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Could not update port: %v", err) + } + + tools.PrintResource(t, newPort) + + if len(newPort.AllowedAddressPairs) == 0 { + t.Fatalf("Address Pairs were removed") + } +} From 20607e013ad7d22c1aca1b0cb16982d4a2a60aea Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 31 Aug 2017 06:10:32 +0000 Subject: [PATCH 0065/2296] Networking v2: Fix portsbinding unit tests --- .../networking/v2/extensions/portsbinding/testing/fixtures.go | 3 +-- .../v2/extensions/portsbinding/testing/requests_test.go | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go b/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go index f688c207cd..484382ea8b 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go @@ -168,8 +168,7 @@ func HandleUpdate(t *testing.T) { "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], "binding:host_id": "HOST1", - "binding:vnic_type": "normal", - "allowed_address_pairs": null + "binding:vnic_type": "normal" } } `) diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go index f41f1cc47a..24ee7a38c0 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go @@ -102,7 +102,7 @@ func TestCreate(t *testing.T) { FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, - SecurityGroups: []string{"foo"}, + SecurityGroups: &[]string{"foo"}, }, HostID: "HOST1", VNICType: "normal", @@ -145,7 +145,7 @@ func TestUpdate(t *testing.T) { FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, - SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, + SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, }, HostID: "HOST1", VNICType: "normal", From 653a6a301165949e540fd3530b3ecc1bb1537b89 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 9 Aug 2017 21:30:27 -0600 Subject: [PATCH 0066/2296] Network v2: Enable Ports to use extensions --- openstack/networking/v2/ports/results.go | 22 +- .../networking/v2/ports/testing/fixtures.go | 465 ++++++++++++++++++ .../v2/ports/testing/requests_test.go | 452 +---------------- 3 files changed, 496 insertions(+), 443 deletions(-) create mode 100644 openstack/networking/v2/ports/testing/fixtures.go diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index c50da6dfff..ebef98d5de 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -11,11 +11,13 @@ type commonResult struct { // Extract is a function that accepts a result and extracts a port resource. func (r commonResult) Extract() (*Port, error) { - var s struct { - Port *Port `json:"port"` - } + var s Port err := r.ExtractInto(&s) - return s.Port, err + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "port") } // CreateResult represents the result of a create operation. Call its Extract @@ -128,9 +130,11 @@ func (r PortPage) IsEmpty() (bool, error) { // and extracts the elements into a slice of Port structs. In other words, // a generic collection is mapped into a relevant slice. func ExtractPorts(r pagination.Page) ([]Port, error) { - var s struct { - Ports []Port `json:"ports"` - } - err := (r.(PortPage)).ExtractInto(&s) - return s.Ports, err + var s []Port + err := ExtractPortsInto(r, &s) + return s, err +} + +func ExtractPortsInto(r pagination.Page, v interface{}) error { + return r.(PortPage).Result.ExtractIntoSlicePtr(v, "ports") } diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go new file mode 100644 index 0000000000..168e1da0e9 --- /dev/null +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -0,0 +1,465 @@ +package testing + +const ListResponse = ` +{ + "ports": [ + { + "status": "ACTIVE", + "binding:host_id": "devstack", + "name": "", + "admin_state_up": true, + "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3", + "tenant_id": "", + "device_owner": "network:router_gateway", + "mac_address": "fa:16:3e:58:42:ed", + "binding:vnic_type": "normal", + "fixed_ips": [ + { + "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062", + "ip_address": "172.24.4.2" + } + ], + "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", + "security_groups": [], + "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824" + } + ] +} +` + +const GetResponse = ` +{ + "port": { + "status": "ACTIVE", + "binding:host_id": "devstack", + "name": "", + "allowed_address_pairs": [], + "admin_state_up": true, + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "7e02058126cc4950b75f9970368ba177", + "extra_dhcp_opts": [], + "binding:vif_details": { + "port_filter": true, + "ovs_hybrid_plug": true + }, + "binding:vif_type": "ovs", + "device_owner": "network:router_interface", + "port_security_enabled": false, + "mac_address": "fa:16:3e:23:fd:d7", + "binding:profile": {}, + "binding:vnic_type": "normal", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.1" + } + ], + "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", + "security_groups": [], + "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e" + } +} +` + +const CreateRequest = ` +{ + "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" + } + ] + } +} +` + +const CreateResponse = ` +{ + "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" + } + ], + "device_id": "" + } +} +` + +const CreateOmitSecurityGroupsRequest = ` +{ + "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" + } + ], + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ] + } +} +` + +const CreateWithNoSecurityGroupsRequest = ` +{ + "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": [], + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ] + } +} +` + +const CreateWithNoSecurityGroupsResponse = ` +{ + "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", + "allowed_address_pairs": [ + { + "ip_address": "10.0.0.4", + "mac_address": "fa:16:3e:c9:cb:f0" + } + ], + "device_id": "" + } +} +` + +const CreateOmitSecurityGroupsResponse = ` +{ + "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" + } + ], + "device_id": "" + } +} +` + +const UpdateRequest = ` +{ + "port": { + "name": "new_port_name", + "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" + } + ], + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ] + } +} +` + +const UpdateResponse = ` +{ + "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" + ], + "device_id": "" + } +} +` + +const UpdateOmitSecurityGroupsRequest = ` +{ + "port": { + "name": "new_port_name", + "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" + } + ] + } +} +` + +const UpdateOmitSecurityGroupsResponse = ` +{ + "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" + ], + "device_id": "" + } +} +` + +const RemoveSecurityGroupRequest = ` +{ + "port": { + "name": "new_port_name", + "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" + } + ], + "security_groups": [] + } +} +` + +const RemoveSecurityGroupResponse = ` +{ + "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", + "device_id": "" + } +} +` + +const RemoveAllowedAddressPairsRequest = ` +{ + "port": { + "name": "new_port_name", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.3" + } + ], + "allowed_address_pairs": [], + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ] + } +} +` + +const RemoveAllowedAddressPairsResponse = ` +{ + "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" + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ], + "device_id": "" + } +} +` + +const DontUpdateAllowedAddressPairsRequest = ` +{ + "port": { + "name": "new_port_name", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.3" + } + ], + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ] + } +} +` + +const DontUpdateAllowedAddressPairsResponse = ` +{ + "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" + ], + "device_id": "" + } +} +` diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index c55cbb2342..6e1bc48748 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -22,31 +22,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "ports": [ - { - "status": "ACTIVE", - "binding:host_id": "devstack", - "name": "", - "admin_state_up": true, - "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3", - "tenant_id": "", - "device_owner": "network:router_gateway", - "mac_address": "fa:16:3e:58:42:ed", - "fixed_ips": [ - { - "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062", - "ip_address": "172.24.4.2" - } - ], - "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", - "security_groups": [], - "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824" - } - ] -} - `) + fmt.Fprintf(w, ListResponse) }) count := 0 @@ -101,28 +77,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "port": { - "status": "ACTIVE", - "name": "", - "admin_state_up": true, - "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", - "tenant_id": "7e02058126cc4950b75f9970368ba177", - "device_owner": "network:router_interface", - "mac_address": "fa:16:3e:23:fd:d7", - "fixed_ips": [ - { - "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", - "ip_address": "10.0.0.1" - } - ], - "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", - "security_groups": [], - "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e" - } -} - `) + fmt.Fprintf(w, GetResponse) }) n, err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract() @@ -153,62 +108,12 @@ func TestCreate(t *testing.T) { 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, ` -{ - "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" - } - ] - } -} - `) + th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` -{ - "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" - } - ], - "device_id": "" - } -} - `) + fmt.Fprintf(w, CreateResponse) }) asu := true @@ -253,61 +158,12 @@ func TestCreateOmitSecurityGroups(t *testing.T) { 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, ` -{ - "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" - } - ], - "allowed_address_pairs": [ - { - "ip_address": "10.0.0.4", - "mac_address": "fa:16:3e:c9:cb:f0" - } - ] - } -} - `) + th.TestJSONRequest(t, r, CreateOmitSecurityGroupsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` -{ - "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" - } - ], - "device_id": "" - } -} - `) + fmt.Fprintf(w, CreateOmitSecurityGroupsResponse) }) asu := true @@ -351,59 +207,12 @@ func TestCreateWithNoSecurityGroup(t *testing.T) { 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, ` -{ - "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": [], - "allowed_address_pairs": [ - { - "ip_address": "10.0.0.4", - "mac_address": "fa:16:3e:c9:cb:f0" - } - ] - } -} - `) + th.TestJSONRequest(t, r, CreateWithNoSecurityGroupsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` -{ - "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", - "allowed_address_pairs": [ - { - "ip_address": "10.0.0.4", - "mac_address": "fa:16:3e:c9:cb:f0" - } - ], - "device_id": "" - } -} - `) + fmt.Fprintf(w, CreateWithNoSecurityGroupsResponse) }) asu := true @@ -454,62 +263,12 @@ func TestUpdate(t *testing.T) { 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, ` -{ - "port": { - "name": "new_port_name", - "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" - } - ], - "security_groups": [ - "f0ac4394-7e4a-4409-9701-ba8be283dbc3" - ] - } -} - `) + th.TestJSONRequest(t, r, UpdateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "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" - ], - "device_id": "" - } -} - `) + fmt.Fprintf(w, UpdateResponse) }) options := ports.UpdateOpts{ @@ -545,59 +304,12 @@ func TestUpdateOmitSecurityGroups(t *testing.T) { 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, ` -{ - "port": { - "name": "new_port_name", - "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" - } - ] - } -} - `) + th.TestJSONRequest(t, r, UpdateOmitSecurityGroupsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "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" - ], - "device_id": "" - } -} - `) + fmt.Fprintf(w, UpdateOmitSecurityGroupsResponse) }) options := ports.UpdateOpts{ @@ -632,57 +344,12 @@ func TestRemoveSecurityGroups(t *testing.T) { 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, ` -{ - "port": { - "name": "new_port_name", - "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" - } - ], - "security_groups": [] - } -} - `) + th.TestJSONRequest(t, r, RemoveSecurityGroupRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "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", - "device_id": "" - } -} - `) + fmt.Fprintf(w, RemoveSecurityGroupResponse) }) options := ports.UpdateOpts{ @@ -718,51 +385,12 @@ func TestRemoveAllowedAddressPairs(t *testing.T) { 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, ` -{ - "port": { - "name": "new_port_name", - "fixed_ips": [ - { - "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", - "ip_address": "10.0.0.3" - } - ], - "allowed_address_pairs": [], - "security_groups": [ - "f0ac4394-7e4a-4409-9701-ba8be283dbc3" - ] - } -} - `) + th.TestJSONRequest(t, r, RemoveAllowedAddressPairsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "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" - } - ], - "id": "65c0ee9f-d634-4522-8954-51021b570b0d", - "security_groups": [ - "f0ac4394-7e4a-4409-9701-ba8be283dbc3" - ], - "device_id": "" - } -} - `) + fmt.Fprintf(w, RemoveAllowedAddressPairsResponse) }) options := ports.UpdateOpts{ @@ -794,56 +422,12 @@ func TestDontUpdateAllowedAddressPairs(t *testing.T) { 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, ` -{ - "port": { - "name": "new_port_name", - "fixed_ips": [ - { - "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", - "ip_address": "10.0.0.3" - } - ], - "security_groups": [ - "f0ac4394-7e4a-4409-9701-ba8be283dbc3" - ] - } -} - `) + th.TestJSONRequest(t, r, DontUpdateAllowedAddressPairsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "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" - ], - "device_id": "" - } -} - `) + fmt.Fprintf(w, DontUpdateAllowedAddressPairsResponse) }) options := ports.UpdateOpts{ From 336e76d7d109938eb628fd8a5034b569326904fb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 9 Aug 2017 21:31:09 -0600 Subject: [PATCH 0067/2296] Network v2: Refactor Ports Binding Extension This commit refactors the portsbinding extension in two ways: 1. It modifies the way ports binding information is created and deleted by naming types to conform to standards and removing extraneous functions. 2. It modifies the result to be an extension of the ports.Port result rather than mirror it. --- .../extensions/portsbinding/portsbinding.go | 34 ++-- .../v2/extensions/portsbinding/requests.go | 104 ++++------ .../v2/extensions/portsbinding/results.go | 63 +----- .../portsbinding/testing/fixtures.go | 94 ++------- .../portsbinding/testing/requests_test.go | 189 ++++++++++-------- .../v2/extensions/portsbinding/urls.go | 23 --- 6 files changed, 196 insertions(+), 311 deletions(-) delete mode 100644 openstack/networking/v2/extensions/portsbinding/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index a6d75f3814..5b7dc90787 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -9,30 +9,40 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) +// PortWithBindingExt represents a port with the binding fields +type PortWithBindingExt struct { + ports.Port + portsbinding.PortsBindingExt +} + // CreatePortsbinding will create a port on the specified subnet. An error will be // returned if the port could not be created. -func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string) (*portsbinding.Port, error) { +func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string) (PortWithBindingExt, error) { portName := tools.RandomString("TESTACC-", 8) iFalse := false t.Logf("Attempting to create port: %s", portName) - createOpts := portsbinding.CreateOpts{ - CreateOptsBuilder: ports.CreateOpts{ - NetworkID: networkID, - Name: portName, - AdminStateUp: &iFalse, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, - }, - HostID: hostID, + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + Name: portName, + AdminStateUp: &iFalse, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } - port, err := portsbinding.Create(client, createOpts).Extract() + createOpts := portsbinding.CreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + HostID: hostID, + } + + var s PortWithBindingExt + + err := ports.Create(client, createOpts).ExtractInto(&s) if err != nil { - return port, err + return s, err } t.Logf("Successfully created port: %s", portName) - return port, nil + return s, nil } diff --git a/openstack/networking/v2/extensions/portsbinding/requests.go b/openstack/networking/v2/extensions/portsbinding/requests.go index b46172be50..3c287463aa 100644 --- a/openstack/networking/v2/extensions/portsbinding/requests.go +++ b/openstack/networking/v2/extensions/portsbinding/requests.go @@ -1,113 +1,91 @@ package portsbinding import ( - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) -// Get retrieves a specific port based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) - return -} - -// CreateOpts represents the attributes used when creating a new -// port with extended attributes. -type CreateOpts struct { +// CreateOptsExt adds port binding options to the base ports.CreateOpts. +type CreateOptsExt struct { // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. - ports.CreateOptsBuilder `json:"-"` + ports.CreateOptsBuilder + // The ID of the host where the port is allocated HostID string `json:"binding:host_id,omitempty"` + // The virtual network interface card (vNIC) type that is bound to the - // neutron port + // neutron port. VNICType string `json:"binding:vnic_type,omitempty"` + // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific - // information to the plug-in + // information to the plug-in. Profile map[string]string `json:"binding:profile,omitempty"` } // ToPortCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { - b1, err := opts.CreateOptsBuilder.ToPortCreateMap() +func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } - b2, err := gophercloud.BuildRequestBody(opts, "") - if err != nil { - return nil, err - } - - port := b1["port"].(map[string]interface{}) + port := base["port"].(map[string]interface{}) - for k, v := range b2 { - port[k] = v + if opts.HostID != "" { + port["binding:host_id"] = opts.HostID } - return map[string]interface{}{"port": port}, nil -} + if opts.VNICType != "" { + port["binding:vnic_type"] = opts.VNICType + } -// Create accepts a CreateOpts struct and creates a new port with extended attributes. -// You must remember to provide a NetworkID value. -func Create(c *gophercloud.ServiceClient, opts ports.CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToPortCreateMap() - if err != nil { - r.Err = err - return + if opts.Profile != nil { + port["binding:profile"] = opts.Profile } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) - return + + return base, nil } -// UpdateOpts represents the attributes used when updating an existing port. -type UpdateOpts struct { +// UpdateOptsExt adds port binding options to the base ports.UpdateOpts +type UpdateOptsExt struct { // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Update operation in this package. - ports.UpdateOptsBuilder `json:"-"` - // The ID of the host where the port is allocated + ports.UpdateOptsBuilder + + // The ID of the host where the port is allocated. HostID string `json:"binding:host_id,omitempty"` + // The virtual network interface card (vNIC) type that is bound to the - // neutron port + // neutron port. VNICType string `json:"binding:vnic_type,omitempty"` + // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific - // information to the plug-in + // information to the plug-in. Profile map[string]string `json:"binding:profile,omitempty"` } // ToPortUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) { - b1, err := opts.UpdateOptsBuilder.ToPortUpdateMap() - if err != nil { - return nil, err - } - - b2, err := gophercloud.BuildRequestBody(opts, "") +func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } - port := b1["port"].(map[string]interface{}) + port := base["port"].(map[string]interface{}) - for k, v := range b2 { - port[k] = v + if opts.HostID != "" { + port["binding:host_id"] = opts.HostID } - return map[string]interface{}{"port": port}, nil -} + if opts.VNICType != "" { + port["binding:vnic_type"] = opts.VNICType + } -// Update accepts a UpdateOpts struct and updates an existing port using the -// values provided. -func Update(c *gophercloud.ServiceClient, id string, opts ports.UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToPortUpdateMap() - if err != nil { - r.Err = err - return r + if opts.Profile != nil { + port["binding:profile"] = opts.Profile } - _, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - }) - return + + return base, nil } diff --git a/openstack/networking/v2/extensions/portsbinding/results.go b/openstack/networking/v2/extensions/portsbinding/results.go index 952747358c..c39f7df50f 100644 --- a/openstack/networking/v2/extensions/portsbinding/results.go +++ b/openstack/networking/v2/extensions/portsbinding/results.go @@ -1,73 +1,30 @@ package portsbinding -import ( - "github.com/gophercloud/gophercloud" - - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - "github.com/gophercloud/gophercloud/pagination" -) - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a port resource. -func (r commonResult) Extract() (*Port, error) { - var s struct { - Port *Port `json:"port"` - } - err := r.ExtractInto(&s) - return s.Port, err -} - -// CreateResult represents the result of a create operation. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. -type UpdateResult struct { - commonResult -} - // IP is a sub-struct that represents an individual IP. type IP struct { SubnetID string `json:"subnet_id"` IPAddress string `json:"ip_address"` } -// Port represents a Neutron port. See package documentation for a top-level -// description of what this is. -type Port struct { - ports.Port - // The ID of the host where the port is allocated +// PortsBindingExt represents a decorated form of a Port with the additional +// port binding information. +type PortsBindingExt struct { + // The ID of the host where the port is allocated. HostID string `json:"binding:host_id"` + // A dictionary that enables the application to pass information about // functions that the Networking API provides. VIFDetails map[string]interface{} `json:"binding:vif_details"` + // The VIF type for the port. VIFType string `json:"binding:vif_type"` + // The virtual network interface card (vNIC) type that is bound to the - // neutron port + // neutron port. VNICType string `json:"binding:vnic_type"` + // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific - // information to the plug-in + // information to the plug-in. Profile map[string]string `json:"binding:profile"` } - -// ExtractPorts accepts a Page struct, specifically a PortPage struct, -// and extracts the elements into a slice of Port structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractPorts(r pagination.Page) ([]Port, error) { - var s struct { - Ports []Port `json:"ports"` - } - err := (r.(ports.PortPage)).ExtractInto(&s) - return s.Ports, err -} diff --git a/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go b/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go index 484382ea8b..03fe353910 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/fixtures.go @@ -6,6 +6,7 @@ import ( "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + porttest "github.com/gophercloud/gophercloud/openstack/networking/v2/ports/testing" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -17,32 +18,7 @@ func HandleListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "ports": [ - { - "status": "ACTIVE", - "binding:host_id": "devstack", - "name": "", - "admin_state_up": true, - "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3", - "tenant_id": "", - "device_owner": "network:router_gateway", - "mac_address": "fa:16:3e:58:42:ed", - "fixed_ips": [ - { - "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062", - "ip_address": "172.24.4.2" - } - ], - "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", - "security_groups": [], - "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824", - "binding:vnic_type": "normal" - } - ] -} - `) + fmt.Fprintf(w, porttest.ListResponse) }) } @@ -54,39 +30,7 @@ func HandleGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "port": { - "status": "ACTIVE", - "binding:host_id": "devstack", - "name": "", - "allowed_address_pairs": [], - "admin_state_up": true, - "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", - "tenant_id": "7e02058126cc4950b75f9970368ba177", - "extra_dhcp_opts": [], - "binding:vif_details": { - "port_filter": true, - "ovs_hybrid_plug": true - }, - "binding:vif_type": "ovs", - "device_owner": "network:router_interface", - "port_security_enabled": false, - "mac_address": "fa:16:3e:23:fd:d7", - "binding:profile": {}, - "binding:vnic_type": "normal", - "fixed_ips": [ - { - "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", - "ip_address": "10.0.0.1" - } - ], - "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", - "security_groups": [], - "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e" - } -} - `) + fmt.Fprintf(w, porttest.GetResponse) }) } @@ -102,14 +46,14 @@ func HandleCreate(t *testing.T) { "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"], - "binding:host_id": "HOST1", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.2" + } + ], + "security_groups": ["foo"], + "binding:host_id": "HOST1", "binding:vnic_type": "normal" } } @@ -159,16 +103,16 @@ func HandleUpdate(t *testing.T) { "port": { "name": "new_port_name", "fixed_ips": [ - { - "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", - "ip_address": "10.0.0.3" - } + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.3" + } ], "security_groups": [ - "f0ac4394-7e4a-4409-9701-ba8be283dbc3" - ], - "binding:host_id": "HOST1", - "binding:vnic_type": "normal" + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ], + "binding:host_id": "HOST1", + "binding:vnic_type": "normal" } } `) diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go index 24ee7a38c0..57901a6b0b 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go @@ -6,7 +6,6 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -16,46 +15,46 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) - count := 0 - - ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := portsbinding.ExtractPorts(page) - th.AssertNoErr(t, err) - - expected := []portsbinding.Port{ - { - Port: ports.Port{ - Status: "ACTIVE", - Name: "", - AdminStateUp: true, - NetworkID: "70c1db1f-b701-45bd-96e0-a313ee3430b3", - TenantID: "", - DeviceOwner: "network:router_gateway", - MACAddress: "fa:16:3e:58:42:ed", - FixedIPs: []ports.IP{ - { - SubnetID: "008ba151-0b8c-4a67-98b5-0d2b87666062", - IPAddress: "172.24.4.2", - }, + type PortWithExt struct { + ports.Port + portsbinding.PortsBindingExt + } + var actual []PortWithExt + + expected := []PortWithExt{ + { + Port: ports.Port{ + Status: "ACTIVE", + Name: "", + AdminStateUp: true, + NetworkID: "70c1db1f-b701-45bd-96e0-a313ee3430b3", + TenantID: "", + DeviceOwner: "network:router_gateway", + MACAddress: "fa:16:3e:58:42:ed", + FixedIPs: []ports.IP{ + { + SubnetID: "008ba151-0b8c-4a67-98b5-0d2b87666062", + IPAddress: "172.24.4.2", }, - ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", - SecurityGroups: []string{}, - DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824", }, + ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", + SecurityGroups: []string{}, + DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824", + }, + PortsBindingExt: portsbinding.PortsBindingExt{ VNICType: "normal", HostID: "devstack", }, - } + }, + } - th.CheckDeepEquals(t, expected, actual) + allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages() + th.AssertNoErr(t, err) - return true, nil - }) + err = ports.ExtractPortsInto(allPages, &actual) + th.AssertNoErr(t, err) - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } + th.CheckDeepEquals(t, expected, actual) } func TestGet(t *testing.T) { @@ -64,27 +63,32 @@ func TestGet(t *testing.T) { HandleGet(t) - n, err := portsbinding.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract() + var s struct { + ports.Port + portsbinding.PortsBindingExt + } + + err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) th.AssertNoErr(t, err) - th.AssertEquals(t, n.Status, "ACTIVE") - th.AssertEquals(t, n.Name, "") - th.AssertEquals(t, n.AdminStateUp, true) - th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") - th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177") - th.AssertEquals(t, n.DeviceOwner, "network:router_interface") - th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7") - th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{ + th.AssertEquals(t, s.Status, "ACTIVE") + th.AssertEquals(t, s.Name, "") + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "7e02058126cc4950b75f9970368ba177") + th.AssertEquals(t, s.DeviceOwner, "network:router_interface") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:23:fd:d7") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"}, }) - th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2") - th.AssertDeepEquals(t, n.SecurityGroups, []string{}) - th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e") - - th.AssertEquals(t, n.HostID, "devstack") - th.AssertEquals(t, n.VNICType, "normal") - th.AssertEquals(t, n.VIFType, "ovs") - th.AssertDeepEquals(t, n.VIFDetails, map[string]interface{}{"port_filter": true, "ovs_hybrid_plug": true}) + th.AssertEquals(t, s.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2") + th.AssertDeepEquals(t, s.SecurityGroups, []string{}) + th.AssertEquals(t, s.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e") + + th.AssertEquals(t, s.HostID, "devstack") + th.AssertEquals(t, s.VNICType, "normal") + th.AssertEquals(t, s.VIFType, "ovs") + th.AssertDeepEquals(t, s.VIFDetails, map[string]interface{}{"port_filter": true, "ovs_hybrid_plug": true}) } func TestCreate(t *testing.T) { @@ -93,41 +97,49 @@ func TestCreate(t *testing.T) { HandleCreate(t) + var s struct { + ports.Port + portsbinding.PortsBindingExt + } + asu := true - options := portsbinding.CreateOpts{ - CreateOptsBuilder: 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"}, + portCreateOpts := 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"}, }, - HostID: "HOST1", - VNICType: "normal", + SecurityGroups: &[]string{"foo"}, + } + + createOpts := portsbinding.CreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + HostID: "HOST1", + VNICType: "normal", } - n, err := portsbinding.Create(fake.ServiceClient(), options).Extract() + + err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s) 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{ + th.AssertEquals(t, s.Status, "DOWN") + th.AssertEquals(t, s.Name, "private-port") + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, s.DeviceOwner, "") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, s.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.AssertEquals(t, n.HostID, "HOST1") - th.AssertEquals(t, n.VNICType, "normal") + th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) + th.AssertEquals(t, s.HostID, "HOST1") + th.AssertEquals(t, s.VNICType, "normal") } func TestRequiredCreateOpts(t *testing.T) { - res := portsbinding.Create(fake.ServiceClient(), portsbinding.CreateOpts{CreateOptsBuilder: ports.CreateOpts{}}) + res := ports.Create(fake.ServiceClient(), portsbinding.CreateOptsExt{CreateOptsBuilder: ports.CreateOpts{}}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -139,19 +151,26 @@ func TestUpdate(t *testing.T) { HandleUpdate(t) - options := portsbinding.UpdateOpts{ - UpdateOptsBuilder: ports.UpdateOpts{ - Name: "new_port_name", - FixedIPs: []ports.IP{ - {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, - }, - SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, + var s struct { + ports.Port + portsbinding.PortsBindingExt + } + + portUpdateOpts := ports.UpdateOpts{ + Name: "new_port_name", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, - HostID: "HOST1", - VNICType: "normal", + SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, + } + + updateOpts := portsbinding.UpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + HostID: "HOST1", + VNICType: "normal", } - s, err := portsbinding.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") diff --git a/openstack/networking/v2/extensions/portsbinding/urls.go b/openstack/networking/v2/extensions/portsbinding/urls.go deleted file mode 100644 index a531a7ee73..0000000000 --- a/openstack/networking/v2/extensions/portsbinding/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package portsbinding - -import "github.com/gophercloud/gophercloud" - -func resourceURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("ports", id) -} - -func rootURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("ports") -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return resourceURL(c, id) -} - -func createURL(c *gophercloud.ServiceClient) string { - return rootURL(c) -} - -func updateURL(c *gophercloud.ServiceClient, id string) string { - return resourceURL(c, id) -} From 4e91496eddbb95550b7ba8cc1b14f8131e7a1ee8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Aug 2017 19:25:05 -0600 Subject: [PATCH 0068/2296] Compute v2: Clean up diskconfig package --- .../compute/v2/extensions/diskconfig/doc.go | 47 ++++++++++++++++++- .../v2/extensions/diskconfig/requests.go | 21 +++++---- .../v2/extensions/diskconfig/results.go | 15 +----- .../v2/servers/testing/requests_test.go | 5 ++ 4 files changed, 64 insertions(+), 24 deletions(-) diff --git a/openstack/compute/v2/extensions/diskconfig/doc.go b/openstack/compute/v2/extensions/diskconfig/doc.go index 80785faca9..ed9cc6f733 100644 --- a/openstack/compute/v2/extensions/diskconfig/doc.go +++ b/openstack/compute/v2/extensions/diskconfig/doc.go @@ -1,3 +1,46 @@ -// Package diskconfig provides information and interaction with the Disk -// Config extension that works with the OpenStack Compute service. +/* +Package diskconfig provides information and interaction with the Disk Config +extension that works with the OpenStack Compute service. + +Example of Obtaining the Disk Config of a Server + + type ServerWithDiskConfig { + servers.Server + diskconfig.ServerDiskConfigExt + } + + var allServers []ServerWithDiskConfig + + allPages, err := servers.List(client, nil).AllPages() + if err != nil { + panic("Unable to retrieve servers: %s", err) + } + + err = servers.ExtractServersInto(allPages, &allServers) + if err != nil { + panic("Unable to extract servers: %s", err) + } + + for _, server := range allServers { + fmt.Println(server.DiskConfig) + } + +Example of Creating a Server with Disk Config + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := diskconfig.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + DiskConfig: diskconfig.Manual, + } + + server, err := servers.Create(computeClient, createOpts).Extract() + if err != nil { + panic("Unable to create server: %s", err) + } +*/ package diskconfig diff --git a/openstack/compute/v2/extensions/diskconfig/requests.go b/openstack/compute/v2/extensions/diskconfig/requests.go index 41d04b9baf..cc04aed6f3 100644 --- a/openstack/compute/v2/extensions/diskconfig/requests.go +++ b/openstack/compute/v2/extensions/diskconfig/requests.go @@ -5,20 +5,22 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) -// DiskConfig represents one of the two possible settings for the DiskConfig option when creating, -// rebuilding, or resizing servers: Auto or Manual. +// DiskConfig represents one of the two possible settings for the DiskConfig +// option when creating, rebuilding, or resizing servers: Auto or Manual. type DiskConfig string const ( - // Auto builds a server with a single partition the size of the target flavor disk and - // automatically adjusts the filesystem to fit the entire partition. Auto may only be used with - // images and servers that use a single EXT3 partition. + // Auto builds a server with a single partition the size of the target flavor + // disk and automatically adjusts the filesystem to fit the entire partition. + // Auto may only be used with images and servers that use a single EXT3 + // partition. Auto DiskConfig = "AUTO" - // Manual builds a server using whatever partition scheme and filesystem are present in the source - // image. If the target flavor disk is larger, the remaining space is left unpartitioned. This - // enables images to have non-EXT3 filesystems, multiple partitions, and so on, and enables you - // to manage the disk configuration. It also results in slightly shorter boot times. + // Manual builds a server using whatever partition scheme and filesystem are + // present in the source image. If the target flavor disk is larger, the + // remaining space is left unpartitioned. This enables images to have non-EXT3 + // filesystems, multiple partitions, and so on, and enables you to manage the + // disk configuration. It also results in slightly shorter boot times. Manual DiskConfig = "MANUAL" ) @@ -50,6 +52,7 @@ func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { // RebuildOptsExt adds a DiskConfig option to the base RebuildOpts. type RebuildOptsExt struct { servers.RebuildOptsBuilder + // DiskConfig controls how the rebuilt server's disk is partitioned. DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` } diff --git a/openstack/compute/v2/extensions/diskconfig/results.go b/openstack/compute/v2/extensions/diskconfig/results.go index 3ba66f5196..239b2683d7 100644 --- a/openstack/compute/v2/extensions/diskconfig/results.go +++ b/openstack/compute/v2/extensions/diskconfig/results.go @@ -1,17 +1,6 @@ package diskconfig -import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - -type ServerWithDiskConfig struct { - servers.Server +type ServerDiskConfigExt struct { + // DiskConfig is the disk configuration of the server. DiskConfig DiskConfig `json:"OS-DCF:diskConfig"` } - -func (s ServerWithDiskConfig) ToServerCreateResult() (m map[string]interface{}) { - m["OS-DCF:diskConfig"] = s.DiskConfig - return -} - -type CreateServerResultBuilder interface { - ToServerCreateResult() map[string]interface{} -} diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index c810f8f17b..361db95ef9 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diskconfig" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -65,6 +66,7 @@ func TestListAllServersWithExtensions(t *testing.T) { type ServerWithExt struct { servers.Server availabilityzones.ServerAvailabilityZoneExt + diskconfig.ServerDiskConfigExt } allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages() @@ -75,6 +77,7 @@ func TestListAllServersWithExtensions(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, 3, len(actual)) th.AssertEquals(t, "nova", actual[0].AvailabilityZone) + th.AssertEquals(t, diskconfig.Manual, actual[0].DiskConfig) } func TestCreateServer(t *testing.T) { @@ -218,11 +221,13 @@ func TestGetServerWithExtensions(t *testing.T) { var s struct { servers.Server availabilityzones.ServerAvailabilityZoneExt + diskconfig.ServerDiskConfigExt } err := servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "nova", s.AvailabilityZone) + th.AssertEquals(t, diskconfig.Manual, s.DiskConfig) err = servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(s) if err == nil { From e764e6cf0316a135851b56f8eea946b906988707 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 7 Sep 2017 22:14:34 -0600 Subject: [PATCH 0069/2296] Compute v2: Forgotten Docs (#513) * Compute v2: secgroup docs * Compute v2: servergroups docs --- .../compute/v2/extensions/secgroups/doc.go | 111 ++++++++++++++++++ .../v2/extensions/secgroups/requests.go | 42 ++++--- .../v2/extensions/secgroups/results.go | 26 ++-- .../compute/v2/extensions/servergroups/doc.go | 40 ++++++- .../v2/extensions/servergroups/requests.go | 12 +- .../v2/extensions/servergroups/results.go | 29 +++-- 6 files changed, 219 insertions(+), 41 deletions(-) diff --git a/openstack/compute/v2/extensions/secgroups/doc.go b/openstack/compute/v2/extensions/secgroups/doc.go index 702f32c985..8d3ebf2e5d 100644 --- a/openstack/compute/v2/extensions/secgroups/doc.go +++ b/openstack/compute/v2/extensions/secgroups/doc.go @@ -1 +1,112 @@ +/* +Package secgroups provides the ability to manage security groups through the +Nova API. + +This API has been deprecated and will be removed from a future release of the +Nova API service. + +For environments that support this extension, this package can be used +regardless of if either Neutron or nova-network is used as the cloud's network +service. + +Example to List Security Groups + + allPages, err := secroups.List(computeClient).AllPages() + if err != nil { + panic(err) + } + + allSecurityGroups, err := secgroups.ExtractSecurityGroups(allPages) + if err != nil { + panic(err) + } + + for _, sg := range allSecurityGroups { + fmt.Printf("%+v\n", sg) + } + +Example to List Security Groups by Server + + serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" + + allPages, err := secroups.ListByServer(computeClient, serverID).AllPages() + if err != nil { + panic(err) + } + + allSecurityGroups, err := secgroups.ExtractSecurityGroups(allPages) + if err != nil { + panic(err) + } + + for _, sg := range allSecurityGroups { + fmt.Printf("%+v\n", sg) + } + +Example to Create a Security Group + + createOpts := secgroups.CreateOpts{ + Name: "group_name", + Description: "A Security Group", + } + + sg, err := secgroups.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Create a Security Group Rule + + sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" + + createOpts := secgroups.CreateRuleOpts{ + ParentGroupID: sgID, + FromPort: 22, + ToPort: 22, + IPProtocol: "tcp", + CIDR: "0.0.0.0/0", + } + + rule, err := secgroups.CreateRule(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Add a Security Group to a Server + + serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" + sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" + + err := secgroups.AddServer(computeClient, serverID, sgID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Remove a Security Group from a Server + + serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" + sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" + + err := secgroups.RemoveServer(computeClient, serverID, sgID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Delete a Security Group + + + sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" + err := secgroups.Delete(computeClient, sgID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Delete a Security Group Rule + + ruleID := "6221fe3e-383d-46c9-a3a6-845e66c1e8b4" + err := secgroups.DeleteRule(computeClient, ruleID).ExtractErr() + if err != nil { + panic(err) + } +*/ package secgroups diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go index ec8019f18c..461e8d9aac 100644 --- a/openstack/compute/v2/extensions/secgroups/requests.go +++ b/openstack/compute/v2/extensions/secgroups/requests.go @@ -36,12 +36,13 @@ type GroupOpts struct { // CreateOpts is the struct responsible for creating a security group. type CreateOpts GroupOpts -// CreateOptsBuilder builds the create options into a serializable format. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToSecGroupCreateMap() (map[string]interface{}, error) } -// ToSecGroupCreateMap builds the create options into a serializable format. +// ToSecGroupCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group") } @@ -62,12 +63,13 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create // UpdateOpts is the struct responsible for updating an existing security group. type UpdateOpts GroupOpts -// UpdateOptsBuilder builds the update options into a serializable format. +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToSecGroupUpdateMap() (map[string]interface{}, error) } -// ToSecGroupUpdateMap builds the update options into a serializable format. +// ToSecGroupUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group") } @@ -101,31 +103,41 @@ func Delete(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResu // CreateRuleOpts represents the configuration for adding a new rule to an // existing security group. type CreateRuleOpts struct { - // the ID of the group that this rule will be added to. + // ID is the ID of the group that this rule will be added to. ParentGroupID string `json:"parent_group_id" required:"true"` - // the lower bound of the port range that will be opened. + + // FromPort is the lower bound of the port range that will be opened. + // Use -1 to allow all ICMP traffic. FromPort int `json:"from_port"` - // the upper bound of the port range that will be opened. + + // ToPort is the upper bound of the port range that will be opened. + // Use -1 to allow all ICMP traffic. ToPort int `json:"to_port"` - // the protocol type that will be allowed, e.g. TCP. + + // IPProtocol the protocol type that will be allowed, e.g. TCP. IPProtocol string `json:"ip_protocol" required:"true"` - // ONLY required if FromGroupID is blank. This represents the IP range that - // will be the source of network traffic to your security group. Use - // 0.0.0.0/0 to allow all IP addresses. + + // CIDR is the network CIDR to allow traffic from. + // This is ONLY required if FromGroupID is blank. This represents the IP + // range that will be the source of network traffic to your security group. + // Use 0.0.0.0/0 to allow all IP addresses. CIDR string `json:"cidr,omitempty" or:"FromGroupID"` - // ONLY required if CIDR is blank. This value represents the ID of a group - // that forwards traffic to the parent group. So, instead of accepting + + // FromGroupID represents another security group to allow access. + // This is ONLY required if CIDR is blank. This value represents the ID of a + // group that forwards traffic to the parent group. So, instead of accepting // network traffic from an entire IP range, you can instead refine the // inbound source by an existing security group. FromGroupID string `json:"group_id,omitempty" or:"CIDR"` } -// CreateRuleOptsBuilder builds the create rule options into a serializable format. +// CreateRuleOptsBuilder allows extensions to add additional parameters to the +// CreateRule request. type CreateRuleOptsBuilder interface { ToRuleCreateMap() (map[string]interface{}, error) } -// ToRuleCreateMap builds the create rule options into a serializable format. +// ToRuleCreateMap builds a request body from CreateRuleOpts. func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "security_group_rule") } diff --git a/openstack/compute/v2/extensions/secgroups/results.go b/openstack/compute/v2/extensions/secgroups/results.go index f49338a1da..2ec6628d8c 100644 --- a/openstack/compute/v2/extensions/secgroups/results.go +++ b/openstack/compute/v2/extensions/secgroups/results.go @@ -59,19 +59,19 @@ type Rule struct { // numeric ID. For the sake of consistency, we always cast it to a string. ID string `json:"-"` - // The lower bound of the port range which this security group should open up + // The lower bound of the port range which this security group should open up. FromPort int `json:"from_port"` - // The upper bound of the port range which this security group should open up + // The upper bound of the port range which this security group should open up. ToPort int `json:"to_port"` - // The IP protocol (e.g. TCP) which the security group accepts + // The IP protocol (e.g. TCP) which the security group accepts. IPProtocol string `json:"ip_protocol"` - // The CIDR IP range whose traffic can be received + // The CIDR IP range whose traffic can be received. IPRange IPRange `json:"ip_range"` - // The security group ID to which this rule belongs + // The security group ID to which this rule belongs. ParentGroupID string `json:"parent_group_id"` // Not documented. @@ -126,13 +126,15 @@ type SecurityGroupPage struct { pagination.SinglePageBase } -// IsEmpty determines whether or not a page of Security Groups contains any results. +// IsEmpty determines whether or not a page of Security Groups contains any +// results. func (page SecurityGroupPage) IsEmpty() (bool, error) { users, err := ExtractSecurityGroups(page) return len(users) == 0, err } -// ExtractSecurityGroups returns a slice of SecurityGroups contained in a single page of results. +// ExtractSecurityGroups returns a slice of SecurityGroups contained in a +// single page of results. func ExtractSecurityGroups(r pagination.Page) ([]SecurityGroup, error) { var s struct { SecurityGroups []SecurityGroup `json:"security_groups"` @@ -145,17 +147,20 @@ type commonResult struct { gophercloud.Result } -// CreateResult represents the result of a create operation. +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret the result as a SecurityGroup. type CreateResult struct { commonResult } -// GetResult represents the result of a get operation. +// GetResult represents the result of a get operation. Call its Extract +// method to interpret the result as a SecurityGroup. type GetResult struct { commonResult } -// UpdateResult represents the result of an update operation. +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret the result as a SecurityGroup. type UpdateResult struct { commonResult } @@ -170,6 +175,7 @@ func (r commonResult) Extract() (*SecurityGroup, error) { } // CreateRuleResult represents the result when adding rules to a security group. +// Call its Extract method to interpret the result as a Rule. type CreateRuleResult struct { gophercloud.Result } diff --git a/openstack/compute/v2/extensions/servergroups/doc.go b/openstack/compute/v2/extensions/servergroups/doc.go index 1e5ed568da..814bde37f3 100644 --- a/openstack/compute/v2/extensions/servergroups/doc.go +++ b/openstack/compute/v2/extensions/servergroups/doc.go @@ -1,2 +1,40 @@ -// Package servergroups provides the ability to manage server groups +/* +Package servergroups provides the ability to manage server groups. + +Example to List Server Groups + + allpages, err := servergroups.List(computeClient).AllPages() + if err != nil { + panic(err) + } + + allServerGroups, err := servergroups.ExtractServerGroups(allPages) + if err != nil { + panic(err) + } + + for _, sg := range allServerGroups { + fmt.Printf("%#v\n", sg) + } + +Example to Create a Server Group + + createOpts := servergroups.CreateOpts{ + Name: "my_sg", + Policies: []string{"anti-affinity"}, + } + + sg, err := servergroups.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Server Group + + sgID := "7a6f29ad-e34d-4368-951a-58a08f11cfb7" + err := servergroups.Delete(computeClient, sgID).ExtractErr() + if err != nil { + panic(err) + } +*/ package servergroups diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go index ee98837074..1439a5a34c 100644 --- a/openstack/compute/v2/extensions/servergroups/requests.go +++ b/openstack/compute/v2/extensions/servergroups/requests.go @@ -5,23 +5,25 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// List returns a Pager that allows you to iterate over a collection of ServerGroups. +// List returns a Pager that allows you to iterate over a collection of +// ServerGroups. func List(client *gophercloud.ServiceClient) pagination.Pager { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return ServerGroupPage{pagination.SinglePageBase(r)} }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notably, the -// CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToServerGroupCreateMap() (map[string]interface{}, error) } -// CreateOpts specifies a Server Group allocation request +// CreateOpts specifies Server Group creation parameters. type CreateOpts struct { // Name is the name of the server group Name string `json:"name" required:"true"` + // Policies are the server group policies Policies []string `json:"policies" required:"true"` } @@ -31,7 +33,7 @@ func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) return gophercloud.BuildRequestBody(opts, "server_group") } -// Create requests the creation of a new Server Group +// Create requests the creation of a new Server Group. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToServerGroupCreateMap() if err != nil { diff --git a/openstack/compute/v2/extensions/servergroups/results.go b/openstack/compute/v2/extensions/servergroups/results.go index ab49b35a44..b9aeef9815 100644 --- a/openstack/compute/v2/extensions/servergroups/results.go +++ b/openstack/compute/v2/extensions/servergroups/results.go @@ -5,7 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// A ServerGroup creates a policy for instance placement in the cloud +// A ServerGroup creates a policy for instance placement in the cloud. type ServerGroup struct { // ID is the unique ID of the Server Group. ID string `json:"id"` @@ -14,17 +14,26 @@ type ServerGroup struct { Name string `json:"name"` // Polices are the group policies. + // + // Normally a single policy is applied: + // + // "affinity" will place all servers within the server group on the + // same compute node. + // + // "anti-affinity" will place servers within the server group on different + // compute nodes. Policies []string `json:"policies"` // Members are the members of the server group. Members []string `json:"members"` - // Metadata includes a list of all user-specified key-value pairs attached to the Server Group. + // Metadata includes a list of all user-specified key-value pairs attached + // to the Server Group. Metadata map[string]interface{} } -// ServerGroupPage stores a single, only page of ServerGroups -// results from a List call. +// ServerGroupPage stores a single page of all ServerGroups results from a +// List call. type ServerGroupPage struct { pagination.SinglePageBase } @@ -59,20 +68,20 @@ func (r ServerGroupResult) Extract() (*ServerGroup, error) { return s.ServerGroup, err } -// CreateResult is the response from a Create operation. Call its Extract method to interpret it -// as a ServerGroup. +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a ServerGroup. type CreateResult struct { ServerGroupResult } -// GetResult is the response from a Get operation. Call its Extract method to interpret it -// as a ServerGroup. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a ServerGroup. type GetResult struct { ServerGroupResult } -// DeleteResult is the response from a Delete operation. Call its Extract method to determine if -// the call succeeded or failed. +// 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 } From 0957926088a666da10dd9edc37ca1d57d261214e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 14 Sep 2017 16:30:12 -0600 Subject: [PATCH 0070/2296] Update README.md --- acceptance/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acceptance/README.md b/acceptance/README.md index 2254aa1ecc..94aad75981 100644 --- a/acceptance/README.md +++ b/acceptance/README.md @@ -43,7 +43,8 @@ to set them manually. |`OS_FLAVOR_ID`|The ID of the flavor you want your server to be based on| |`OS_FLAVOR_ID_RESIZE`|The ID of the flavor you want your server to be resized to| |`OS_POOL_NAME`|The Pool from where to obtain Floating IPs| -|`OS_NETWORK_NAME`|The network to launch instances on| +|`OS_NETWORK_NAME`|The internal/private network to launch instances on| +|`OS_EXTGW_ID`|The external/public network| #### Shared file systems |Name|Description| From 9e9ff818ce2cbe3c9c77c13c85eacf6356b7ba2d Mon Sep 17 00:00:00 2001 From: phionah bugosi Date: Fri, 15 Sep 2017 13:58:45 +0300 Subject: [PATCH 0071/2296] SharedFilesystems v2: Change Specs to ExtraSpecs --- openstack/sharedfilesystems/v2/sharetypes/requests.go | 2 +- .../sharedfilesystems/v2/sharetypes/testing/requests_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/sharedfilesystems/v2/sharetypes/requests.go b/openstack/sharedfilesystems/v2/sharetypes/requests.go index adb1216240..c2c2049b9f 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/requests.go +++ b/openstack/sharedfilesystems/v2/sharetypes/requests.go @@ -113,7 +113,7 @@ type SetExtraSpecsOptsBuilder interface { type SetExtraSpecsOpts struct { // A list of all extra specifications to be added to a ShareType - Specs map[string]interface{} `json:"extra_specs"` + ExtraSpecs map[string]interface{} `json:"extra_specs" required:"true"` } // ToShareTypeSetExtraSpecsMap assembles a request body based on the contents of a diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go index 1307391b2e..85a9e5a974 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go @@ -143,7 +143,7 @@ func TestSetExtraSpecs(t *testing.T) { MockSetExtraSpecsResponse(t) options := &sharetypes.SetExtraSpecsOpts{ - Specs: map[string]interface{}{"my_key": "my_value"}, + ExtraSpecs: map[string]interface{}{"my_key": "my_value"}, } es, err := sharetypes.SetExtraSpecs(client.ServiceClient(), "shareTypeID", options).Extract() From cf422d3045729e599adbb068550c1e198b170c3c Mon Sep 17 00:00:00 2001 From: Phionah Bugosi Date: Sat, 23 Sep 2017 20:09:30 +0300 Subject: [PATCH 0072/2296] Compute v2: Add Lock/Unlock Action (#522) * Fixes 518 * Compute v2: Add Lock/Unlock Action * Compute v2: Add Lock/Unlock Action * Compute v2: Add Lock/Unlock Action * Compute v2: Add Lock/Unlock Action * Compute v2: Add Lock/Unlock Action * Compute v2: Add Lock/Unlock Action * Compute v2: Add Lock/Unlock Action * Compute v2: Add Lock/Unlock Action acceptance test * Compute v2: Add Lock/Unlock Action relevant error return type * Remove err redeclaration in acceptance test --- .../openstack/compute/v2/servers_test.go | 37 +++++++++++++++++++ .../compute/v2/extensions/lockunlock/doc.go | 19 ++++++++++ .../v2/extensions/lockunlock/requests.go | 19 ++++++++++ .../v2/extensions/lockunlock/results.go | 15 ++++++++ .../v2/extensions/lockunlock/testing/doc.go | 2 + .../extensions/lockunlock/testing/fixtures.go | 27 ++++++++++++++ .../lockunlock/testing/request_test.go | 31 ++++++++++++++++ 7 files changed, 150 insertions(+) create mode 100644 openstack/compute/v2/extensions/lockunlock/doc.go create mode 100644 openstack/compute/v2/extensions/lockunlock/requests.go create mode 100644 openstack/compute/v2/extensions/lockunlock/results.go create mode 100644 openstack/compute/v2/extensions/lockunlock/testing/doc.go create mode 100644 openstack/compute/v2/extensions/lockunlock/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/lockunlock/testing/request_test.go diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 14516ff137..ada733f52d 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/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/lockunlock" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" @@ -479,3 +480,39 @@ func TestServersActionSuspend(t *testing.T) { t.Fatal(err) } } + +func TestServersActionLock(t *testing.T) { + t.Parallel() + + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + server, err := CreateServer(t, client) + if err != nil { + t.Fatal(err) + } + defer DeleteServer(t, client, server) + + t.Logf("Attempting to Lock server %s", server.ID) + err = lockunlock.Lock(client, server.ID).ExtractErr() + if err != nil { + t.Fatal(err) + } + + err = servers.Delete(client, server.ID).ExtractErr() + if err == nil { + t.Fatalf("Should not have been able to delete the server") + } + + err = lockunlock.Unlock(client, server.ID).ExtractErr() + if err != nil { + t.Fatal(err) + } + + err = WaitForComputeStatus(client, server, "ACTIVE") + if err != nil { + t.Fatal(err) + } +} diff --git a/openstack/compute/v2/extensions/lockunlock/doc.go b/openstack/compute/v2/extensions/lockunlock/doc.go new file mode 100644 index 0000000000..ac51a36f61 --- /dev/null +++ b/openstack/compute/v2/extensions/lockunlock/doc.go @@ -0,0 +1,19 @@ +/* +Package lockunlock provides functionality to lock and unlock servers that +have been provisioned by the OpenStack Compute service. + +Example to Lock and Unlock a Server + + serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" + + err := lockunlock.Lock(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + + err = lockunlock.Unlock(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package lockunlock diff --git a/openstack/compute/v2/extensions/lockunlock/requests.go b/openstack/compute/v2/extensions/lockunlock/requests.go new file mode 100644 index 0000000000..5243d0fba5 --- /dev/null +++ b/openstack/compute/v2/extensions/lockunlock/requests.go @@ -0,0 +1,19 @@ +package lockunlock + +import "github.com/gophercloud/gophercloud" + +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "action") +} + +// Lock is the operation responsible for locking a Compute server. +func Lock(client *gophercloud.ServiceClient, id string) (r LockResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) + return +} + +// Unlock is the operation responsible for unlocking a Compute server. +func Unlock(client *gophercloud.ServiceClient, id string) (r UnlockResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) + return +} diff --git a/openstack/compute/v2/extensions/lockunlock/results.go b/openstack/compute/v2/extensions/lockunlock/results.go new file mode 100644 index 0000000000..1297e502b0 --- /dev/null +++ b/openstack/compute/v2/extensions/lockunlock/results.go @@ -0,0 +1,15 @@ +package lockunlock + +import ( + "github.com/gophercloud/gophercloud" +) + +// LockResult and UnlockResult are the responses from a Lock and Unlock operations respectively. Call its ExtractErr +// method to determine if the suceeded or failed. +type LockResult struct { + gophercloud.ErrResult +} + +type UnlockResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/lockunlock/testing/doc.go b/openstack/compute/v2/extensions/lockunlock/testing/doc.go new file mode 100644 index 0000000000..59cb9be471 --- /dev/null +++ b/openstack/compute/v2/extensions/lockunlock/testing/doc.go @@ -0,0 +1,2 @@ +// unlocklock unit tests +package testing diff --git a/openstack/compute/v2/extensions/lockunlock/testing/fixtures.go b/openstack/compute/v2/extensions/lockunlock/testing/fixtures.go new file mode 100644 index 0000000000..ec79b75321 --- /dev/null +++ b/openstack/compute/v2/extensions/lockunlock/testing/fixtures.go @@ -0,0 +1,27 @@ +package testing + +import ( + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func mockStartServerResponse(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, `{"lock": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} + +func mockStopServerResponse(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, `{"unlock": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/compute/v2/extensions/lockunlock/testing/request_test.go b/openstack/compute/v2/extensions/lockunlock/testing/request_test.go new file mode 100644 index 0000000000..cb2906d27b --- /dev/null +++ b/openstack/compute/v2/extensions/lockunlock/testing/request_test.go @@ -0,0 +1,31 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const serverID = "{serverId}" + +func TestLock(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockStartServerResponse(t, serverID) + + err := lockunlock.Lock(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUnlock(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockStopServerResponse(t, serverID) + + err := lockunlock.Unlock(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} From e05eb28a67270889404c8b1c2a1557e88467d6a6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Sep 2017 17:10:54 +0000 Subject: [PATCH 0073/2296] Compute v2: lockunlock doc fix --- openstack/compute/v2/extensions/lockunlock/results.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openstack/compute/v2/extensions/lockunlock/results.go b/openstack/compute/v2/extensions/lockunlock/results.go index 1297e502b0..282bc8a0eb 100644 --- a/openstack/compute/v2/extensions/lockunlock/results.go +++ b/openstack/compute/v2/extensions/lockunlock/results.go @@ -4,8 +4,9 @@ import ( "github.com/gophercloud/gophercloud" ) -// LockResult and UnlockResult are the responses from a Lock and Unlock operations respectively. Call its ExtractErr -// method to determine if the suceeded or failed. +// LockResult and UnlockResult are the responses from a Lock and Unlock +// operations respectively. Call their ExtractErr methods to determine if the +// requests suceeded or failed. type LockResult struct { gophercloud.ErrResult } From c29de929c17e149ff04e011fd4733c0ee810ad08 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Sep 2017 17:13:49 +0000 Subject: [PATCH 0074/2296] Compute v2: Implement type-specific results for suspend/resume --- .../v2/extensions/suspendresume/requests.go | 4 ++-- .../v2/extensions/suspendresume/results.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 openstack/compute/v2/extensions/suspendresume/results.go diff --git a/openstack/compute/v2/extensions/suspendresume/requests.go b/openstack/compute/v2/extensions/suspendresume/requests.go index d5f03fcdf1..0abbe72121 100644 --- a/openstack/compute/v2/extensions/suspendresume/requests.go +++ b/openstack/compute/v2/extensions/suspendresume/requests.go @@ -7,13 +7,13 @@ func actionURL(client *gophercloud.ServiceClient, id string) string { } // Suspend is the operation responsible for suspending a Compute server. -func Suspend(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func Suspend(client *gophercloud.ServiceClient, id string) (r SuspendResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) return } // Resume is the operation responsible for resuming a Compute server. -func Resume(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func Resume(client *gophercloud.ServiceClient, id string) (r UnsuspendResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/suspendresume/results.go b/openstack/compute/v2/extensions/suspendresume/results.go new file mode 100644 index 0000000000..988d83e4aa --- /dev/null +++ b/openstack/compute/v2/extensions/suspendresume/results.go @@ -0,0 +1,15 @@ +package suspendresume + +import "github.com/gophercloud/gophercloud" + +// SuspendResult is the response from a Suspend operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type SuspendResult struct { + gophercloud.ErrResult +} + +// UnsuspendResult is the response from an Unsuspend operation. Call +// its ExtractErr method to determine if the request succeeded or failed. +type UnsuspendResult struct { + gophercloud.ErrResult +} From e72683aad28ded7e9a110d8ec3b35aa98a761e55 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Sep 2017 17:14:20 +0000 Subject: [PATCH 0075/2296] Compute v2: migrate doc fix --- openstack/compute/v2/extensions/migrate/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/migrate/results.go b/openstack/compute/v2/extensions/migrate/results.go index 62997b7f5d..ebccf56ac3 100644 --- a/openstack/compute/v2/extensions/migrate/results.go +++ b/openstack/compute/v2/extensions/migrate/results.go @@ -5,7 +5,7 @@ import ( ) // MigrateResult is the response from a Migrate operation. Call its ExtractErr -// method to determine if the suceeded or failed. +// method to determine if the request suceeded or failed. type MigrateResult struct { gophercloud.ErrResult } From 59c037c7457f6d09eb03d081a2ca981dbcd4783e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Sep 2017 17:16:46 +0000 Subject: [PATCH 0076/2296] Compute v2: Implement type-specific result for defsecrules --- openstack/compute/v2/extensions/defsecrules/requests.go | 2 +- openstack/compute/v2/extensions/defsecrules/results.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/extensions/defsecrules/requests.go index 1888cb94b7..b63a5d5d63 100644 --- a/openstack/compute/v2/extensions/defsecrules/requests.go +++ b/openstack/compute/v2/extensions/defsecrules/requests.go @@ -69,7 +69,7 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { } // Delete will permanently delete a rule the project's default security group. -func Delete(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(resourceURL(client, id), nil) return } diff --git a/openstack/compute/v2/extensions/defsecrules/results.go b/openstack/compute/v2/extensions/defsecrules/results.go index c6c484264b..98c18fe560 100644 --- a/openstack/compute/v2/extensions/defsecrules/results.go +++ b/openstack/compute/v2/extensions/defsecrules/results.go @@ -65,3 +65,9 @@ func (r commonResult) Extract() (*DefaultRule, error) { err := r.ExtractInto(&s) return &s.DefaultRule, err } + +// 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 +} From 7bbbddcd3a129671a340b87b8f5bb8ab15920a69 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Sep 2017 17:18:55 +0000 Subject: [PATCH 0077/2296] Compute v2: Implement type-specific results for pause/unpause --- .../v2/extensions/pauseunpause/requests.go | 4 ++-- .../compute/v2/extensions/pauseunpause/results.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 openstack/compute/v2/extensions/pauseunpause/results.go diff --git a/openstack/compute/v2/extensions/pauseunpause/requests.go b/openstack/compute/v2/extensions/pauseunpause/requests.go index a9e02d99e6..aeb880343f 100644 --- a/openstack/compute/v2/extensions/pauseunpause/requests.go +++ b/openstack/compute/v2/extensions/pauseunpause/requests.go @@ -7,13 +7,13 @@ func actionURL(client *gophercloud.ServiceClient, id string) string { } // Pause is the operation responsible for pausing a Compute server. -func Pause(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func Pause(client *gophercloud.ServiceClient, id string) (r PauseResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) return } // Unpause is the operation responsible for unpausing a Compute server. -func Unpause(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func Unpause(client *gophercloud.ServiceClient, id string) (r UnpauseResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/pauseunpause/results.go b/openstack/compute/v2/extensions/pauseunpause/results.go new file mode 100644 index 0000000000..3cb91d981a --- /dev/null +++ b/openstack/compute/v2/extensions/pauseunpause/results.go @@ -0,0 +1,15 @@ +package pauseunpause + +import "github.com/gophercloud/gophercloud" + +// PauseResult is the response from a Pause operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type PauseResult struct { + gophercloud.ErrResult +} + +// UnpauseResult is the response from an Unpause operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type UnpauseResult struct { + gophercloud.ErrResult +} From 73f593b4864b865128471f75a7e1bcb55095286e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Sep 2017 17:24:36 +0000 Subject: [PATCH 0078/2296] Compute v2: Implement type-specific results for start/stop --- .../compute/v2/extensions/startstop/requests.go | 4 ++-- .../compute/v2/extensions/startstop/results.go | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 openstack/compute/v2/extensions/startstop/results.go diff --git a/openstack/compute/v2/extensions/startstop/requests.go b/openstack/compute/v2/extensions/startstop/requests.go index 1d8a593b9f..5b4f3f39dd 100644 --- a/openstack/compute/v2/extensions/startstop/requests.go +++ b/openstack/compute/v2/extensions/startstop/requests.go @@ -7,13 +7,13 @@ func actionURL(client *gophercloud.ServiceClient, id string) string { } // Start is the operation responsible for starting a Compute server. -func Start(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func Start(client *gophercloud.ServiceClient, id string) (r StartResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) return } // Stop is the operation responsible for stopping a Compute server. -func Stop(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func Stop(client *gophercloud.ServiceClient, id string) (r StopResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/startstop/results.go b/openstack/compute/v2/extensions/startstop/results.go new file mode 100644 index 0000000000..8349689332 --- /dev/null +++ b/openstack/compute/v2/extensions/startstop/results.go @@ -0,0 +1,15 @@ +package startstop + +import "github.com/gophercloud/gophercloud" + +// StartResult is the response from a Start operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type StartResult struct { + gophercloud.ErrResult +} + +// StopResult is the response from Stop operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type StopResult struct { + gophercloud.ErrResult +} From 6ea105b37675ceb1b1bcd41a45ac91f9389dcd59 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Sep 2017 17:38:05 +0000 Subject: [PATCH 0079/2296] Compute v2: Implement type-specific results for secgroups --- .../v2/extensions/secgroups/requests.go | 8 +++---- .../v2/extensions/secgroups/results.go | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go index 461e8d9aac..bcceaeacdd 100644 --- a/openstack/compute/v2/extensions/secgroups/requests.go +++ b/openstack/compute/v2/extensions/secgroups/requests.go @@ -95,7 +95,7 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { } // Delete will permanently delete a security group from the project. -func Delete(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(resourceURL(client, id), nil) return } @@ -158,7 +158,7 @@ func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) ( } // DeleteRule will permanently delete a rule from a security group. -func DeleteRule(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { +func DeleteRule(client *gophercloud.ServiceClient, id string) (r DeleteRuleResult) { _, r.Err = client.Delete(resourceRuleURL(client, id), nil) return } @@ -171,13 +171,13 @@ func actionMap(prefix, groupName string) map[string]map[string]string { // AddServer will associate a server and a security group, enforcing the // rules of the group on the server. -func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r gophercloud.ErrResult) { +func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r AddServerResult) { _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &r.Body, nil) return } // RemoveServer will disassociate a server from a security group. -func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r gophercloud.ErrResult) { +func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r RemoveServerResult) { _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &r.Body, nil) return } diff --git a/openstack/compute/v2/extensions/secgroups/results.go b/openstack/compute/v2/extensions/secgroups/results.go index 2ec6628d8c..cf08547e90 100644 --- a/openstack/compute/v2/extensions/secgroups/results.go +++ b/openstack/compute/v2/extensions/secgroups/results.go @@ -188,3 +188,27 @@ func (r CreateRuleResult) Extract() (*Rule, error) { err := r.ExtractInto(&s) return s.Rule, err } + +// DeleteResult is the response from delete operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// DeleteRuleResult is the response from a DeleteRule operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteRuleResult struct { + gophercloud.ErrResult +} + +// AddServerResult is the response from an AddServer operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type AddServerResult struct { + gophercloud.ErrResult +} + +// RemoveServerResult is the response from a RemoveServer operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type RemoveServerResult struct { + gophercloud.ErrResult +} From f792ea9dff58a4a18feeb77f4352b8aa5272a76a Mon Sep 17 00:00:00 2001 From: Tommy Hughes Date: Tue, 26 Sep 2017 12:53:44 -0500 Subject: [PATCH 0080/2296] BlockStorage v2: Support Cinder "noauth" auth_strategy (#503) * openstack blockstorage v2 noauth * getURL mods & noauth move * minor fixes & test addition * Deleted extraneous debug file * Updates to block storage v2 noauth support * consolidate UnAuthenticatedClient into NewClient * acceptance tests added for blockstorage/noauth --- acceptance/clients/clients.go | 18 +++ .../blockstorage/noauth/blockstorage.go | 142 ++++++++++++++++++ .../openstack/blockstorage/noauth/pkg.go | 3 + .../blockstorage/noauth/snapshots_test.go | 58 +++++++ .../blockstorage/noauth/volumes_test.go | 52 +++++++ openstack/blockstorage/noauth/doc.go | 17 +++ openstack/blockstorage/noauth/requests.go | 55 +++++++ openstack/blockstorage/noauth/testing/doc.go | 2 + .../blockstorage/noauth/testing/fixtures.go | 19 +++ .../noauth/testing/requests_test.go | 38 +++++ 10 files changed, 404 insertions(+) create mode 100644 acceptance/openstack/blockstorage/noauth/blockstorage.go create mode 100644 acceptance/openstack/blockstorage/noauth/pkg.go create mode 100644 acceptance/openstack/blockstorage/noauth/snapshots_test.go create mode 100644 acceptance/openstack/blockstorage/noauth/volumes_test.go create mode 100644 openstack/blockstorage/noauth/doc.go create mode 100644 openstack/blockstorage/noauth/requests.go create mode 100644 openstack/blockstorage/noauth/testing/doc.go create mode 100644 openstack/blockstorage/noauth/testing/fixtures.go create mode 100644 openstack/blockstorage/noauth/testing/requests_test.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 7bca0a4408..24091295e0 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" ) // AcceptanceTestChoices contains image and flavor selections for use by the acceptance tests. @@ -136,6 +137,23 @@ func NewBlockStorageV2Client() (*gophercloud.ServiceClient, error) { }) } +// NewBlockStorageV2NoAuthClient returns a noauth *ServiceClient for +// making calls to the OpenStack Block Storage v2 API. An error will be +// returned if client creation was not possible. +func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { + client, err := noauth.NewClient(gophercloud.AuthOptions{ + Username: os.Getenv("OS_USERNAME"), + TenantName: os.Getenv("OS_TENANT_NAME"), + }) + if err != nil { + return nil, err + } + + return noauth.NewBlockStorageV2(client, noauth.EndpointOpts{ + CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), + }) +} + // NewSharedFileSystemV2Client returns a *ServiceClient for making calls // to the OpenStack Shared File System v2 API. An error will be returned // if authentication or client creation was not possible. diff --git a/acceptance/openstack/blockstorage/noauth/blockstorage.go b/acceptance/openstack/blockstorage/noauth/blockstorage.go new file mode 100644 index 0000000000..9e8aeab9dc --- /dev/null +++ b/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -0,0 +1,142 @@ +// Package noauth contains common functions for creating block storage based +// resources for use in acceptance tests. See the `*_test.go` files for +// example usages. +package noauth + +import ( + "testing" + + "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" +) + +// CreateVolume will create a volume with a random name and size of 1GB. An +// error will be returned if the volume was unable to be created. +func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { + if testing.Short() { + t.Skip("Skipping test that requires volume creation in short mode.") + } + + volumeName := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create volume: %s", volumeName) + + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + + volume, err := volumes.Create(client, createOpts).Extract() + if err != nil { + return volume, err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return volume, err + } + + return volume, nil +} + +// CreateVolumeFromImage will create a volume from with a random name and size of +// 1GB. An error will be returned if the volume was unable to be created. +func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { + if testing.Short() { + t.Skip("Skipping test that requires volume creation in short mode.") + } + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + volumeName := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create volume: %s", volumeName) + + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + ImageID: choices.ImageID, + } + + volume, err := volumes.Create(client, createOpts).Extract() + if err != nil { + return volume, err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return volume, err + } + + return volume, nil +} + +// DeleteVolume will delete a volume. A fatal error will occur if the volume +// failed to be deleted. This works best when used as a deferred function. +func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { + err := volumes.Delete(client, volume.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) + } + + t.Logf("Deleted volume: %s", volume.ID) +} + +// CreateSnapshot will create a snapshot of the specified volume. +// Snapshot will be assigned a random name and description. +func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { + if testing.Short() { + t.Skip("Skipping test that requires snapshot creation in short mode.") + } + + snapshotName := tools.RandomString("ACPTTEST", 16) + snapshotDescription := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create snapshot: %s", snapshotName) + + createOpts := snapshots.CreateOpts{ + VolumeID: volume.ID, + Name: snapshotName, + Description: snapshotDescription, + } + + snapshot, err := snapshots.Create(client, createOpts).Extract() + if err != nil { + return snapshot, err + } + + err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + if err != nil { + return snapshot, err + } + + return snapshot, nil +} + +// DeleteSnapshot will delete a snapshot. A fatal error will occur if the +// snapshot failed to be deleted. +func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { + err := snapshots.Delete(client, snapshot.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) + } + + // Volumes can't be deleted until their snapshots have been, + // so block up to 120 seconds for the snapshot to delete. + err = gophercloud.WaitFor(120, func() (bool, error) { + _, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + return true, nil + } + + return false, nil + }) + if err != nil { + t.Fatalf("Error waiting for snapshot to delete: %v", err) + } + + t.Logf("Deleted snapshot: %s", snapshot.ID) +} diff --git a/acceptance/openstack/blockstorage/noauth/pkg.go b/acceptance/openstack/blockstorage/noauth/pkg.go new file mode 100644 index 0000000000..5d4f920cbf --- /dev/null +++ b/acceptance/openstack/blockstorage/noauth/pkg.go @@ -0,0 +1,3 @@ +// The noauth package contains acceptance tests for the Openstack Cinder standalone service. + +package noauth diff --git a/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/acceptance/openstack/blockstorage/noauth/snapshots_test.go new file mode 100644 index 0000000000..f287c3e5f2 --- /dev/null +++ b/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -0,0 +1,58 @@ +// +build acceptance blockstorage + +package noauth + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" +) + +func TestSnapshotsList(t *testing.T) { + client, err := clients.NewBlockStorageV2NoAuthClient() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve snapshots: %v", err) + } + + allSnapshots, err := snapshots.ExtractSnapshots(allPages) + if err != nil { + t.Fatalf("Unable to extract snapshots: %v", err) + } + + for _, snapshot := range allSnapshots { + tools.PrintResource(t, snapshot) + } +} + +func TestSnapshotsCreateDelete(t *testing.T) { + client, err := clients.NewBlockStorageV2NoAuthClient() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + volume, err := CreateVolume(t, client) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + defer DeleteVolume(t, client, volume) + + snapshot, err := CreateSnapshot(t, client, volume) + if err != nil { + t.Fatalf("Unable to create snapshot: %v", err) + } + defer DeleteSnapshot(t, client, snapshot) + + newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve snapshot: %v", err) + } + + tools.PrintResource(t, newSnapshot) +} diff --git a/acceptance/openstack/blockstorage/noauth/volumes_test.go b/acceptance/openstack/blockstorage/noauth/volumes_test.go new file mode 100644 index 0000000000..4e10344cf6 --- /dev/null +++ b/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -0,0 +1,52 @@ +// +build acceptance blockstorage + +package noauth + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" +) + +func TestVolumesList(t *testing.T) { + client, err := clients.NewBlockStorageV2NoAuthClient() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve volumes: %v", err) + } + + allVolumes, err := volumes.ExtractVolumes(allPages) + if err != nil { + t.Fatalf("Unable to extract volumes: %v", err) + } + + for _, volume := range allVolumes { + tools.PrintResource(t, volume) + } +} + +func TestVolumesCreateDestroy(t *testing.T) { + client, err := clients.NewBlockStorageV2NoAuthClient() + if err != nil { + t.Fatalf("Unable to create blockstorage client: %v", err) + } + + volume, err := CreateVolume(t, client) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + defer DeleteVolume(t, client, volume) + + newVolume, err := volumes.Get(client, volume.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve volume: %v", err) + } + + tools.PrintResource(t, newVolume) +} diff --git a/openstack/blockstorage/noauth/doc.go b/openstack/blockstorage/noauth/doc.go new file mode 100644 index 0000000000..204c78fee8 --- /dev/null +++ b/openstack/blockstorage/noauth/doc.go @@ -0,0 +1,17 @@ +/* +Package noauth creates a "noauth" *gophercloud.ServiceClient for use in Cinder +environments configured with the noauth authentication middleware. + +Example of Creating a noauth Service Client + + provider, err := noauth.NewClient(gophercloud.AuthOptions{ + Username: os.Getenv("OS_USERNAME"), + TenantName: os.Getenv("OS_TENANT_NAME"), + }) + client, err := noauth.NewBlockStorageV2(provider, noauth.EndpointOpts{ + CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), + }) + + An example of a CinderEndpoint would be: http://example.com:8776/v2, +*/ +package noauth diff --git a/openstack/blockstorage/noauth/requests.go b/openstack/blockstorage/noauth/requests.go new file mode 100644 index 0000000000..81c084c9b8 --- /dev/null +++ b/openstack/blockstorage/noauth/requests.go @@ -0,0 +1,55 @@ +package noauth + +import ( + "fmt" + "strings" + + "github.com/gophercloud/gophercloud" +) + +// EndpointOpts specifies a "noauth" Cinder Endpoint. +type EndpointOpts struct { + // CinderEndpoint [required] is currently only used with "noauth" Cinder. + // A cinder endpoint with "auth_strategy=noauth" is necessary, for example: + // http://example.com:8776/v2. + CinderEndpoint string +} + +// NewClient prepares an unauthenticated ProviderClient instance. +func NewClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { + if options.Username == "" { + options.Username = "admin" + } + if options.TenantName == "" { + options.TenantName = "admin" + } + + client := &gophercloud.ProviderClient{ + TokenID: fmt.Sprintf("%s:%s", options.Username, options.TenantName), + } + + return client, nil +} + +func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc := new(gophercloud.ServiceClient) + if eo.CinderEndpoint == "" { + return nil, fmt.Errorf("CinderEndpoint is required") + } + + token := strings.Split(client.TokenID, ":") + if len(token) != 2 { + return nil, fmt.Errorf("Malformed noauth token") + } + + endpoint := fmt.Sprintf("%s%s", gophercloud.NormalizeURL(eo.CinderEndpoint), token[1]) + sc.Endpoint = gophercloud.NormalizeURL(endpoint) + sc.ProviderClient = client + return sc, nil +} + +// NewBlockStorageV2 creates a ServiceClient that may be used to access a +// "noauth" block storage service. +func NewBlockStorageV2(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo) +} diff --git a/openstack/blockstorage/noauth/testing/doc.go b/openstack/blockstorage/noauth/testing/doc.go new file mode 100644 index 0000000000..425ab60553 --- /dev/null +++ b/openstack/blockstorage/noauth/testing/doc.go @@ -0,0 +1,2 @@ +// noauth unit tests +package testing diff --git a/openstack/blockstorage/noauth/testing/fixtures.go b/openstack/blockstorage/noauth/testing/fixtures.go new file mode 100644 index 0000000000..f78bda3c5f --- /dev/null +++ b/openstack/blockstorage/noauth/testing/fixtures.go @@ -0,0 +1,19 @@ +package testing + +// NoAuthResult is the expected result of the noauth Service Client +type NoAuthResult struct { + TokenID string + Endpoint string +} + +var naTestResult = NoAuthResult{ + TokenID: "user:test", + Endpoint: "http://cinder:8776/v2/test/", +} + +var naResult = NoAuthResult{ + TokenID: "admin:admin", + Endpoint: "http://cinder:8776/v2/admin/", +} + +var errorResult = "CinderEndpoint is required" diff --git a/openstack/blockstorage/noauth/testing/requests_test.go b/openstack/blockstorage/noauth/testing/requests_test.go new file mode 100644 index 0000000000..492b639f33 --- /dev/null +++ b/openstack/blockstorage/noauth/testing/requests_test.go @@ -0,0 +1,38 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNoAuth(t *testing.T) { + ao := gophercloud.AuthOptions{ + Username: "user", + TenantName: "test", + } + provider, err := noauth.NewClient(ao) + th.AssertNoErr(t, err) + noauthClient, err := noauth.NewBlockStorageV2(provider, noauth.EndpointOpts{ + CinderEndpoint: "http://cinder:8776/v2", + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, naTestResult.Endpoint, noauthClient.Endpoint) + th.AssertEquals(t, naTestResult.TokenID, noauthClient.TokenID) + + ao2 := gophercloud.AuthOptions{} + provider2, err := noauth.NewClient(ao2) + th.AssertNoErr(t, err) + noauthClient2, err := noauth.NewBlockStorageV2(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.NewBlockStorageV2(provider2, noauth.EndpointOpts{}) + _ = errTest + th.AssertEquals(t, errorResult, err.Error()) +} From 273b98b2421a89080b7846f82441ff546ccdd4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Knecht?= Date: Wed, 27 Sep 2017 03:55:28 +0200 Subject: [PATCH 0081/2296] Networking v2: Routers: Parse external_fixed_ips (#516) * networking: routers: parse external_fixed_ips Extract `external_fixed_ips` from a router's `external_gateway_info`. `Router.GatewayInfo.ExternalFixedIPs` is a list of `IP`, `SubnetID` pairs, corresponding to each external IP and subnet of the router. * networking: routers: add test for external_fixed_ips * networking: routers: modify other tests Add `external_fixed_ips` to the JSON returned in `TestGet`, `TestCreate` and `TestUpdate`. * networking: routers: rename ExternalFixedIP.IP Call it `ExternalFixedIP.IPAddress` for consistency with the `IP` struct in `ports`. --- .../v2/extensions/layer3/routers/results.go | 10 ++- .../layer3/routers/testing/requests_test.go | 65 +++++++++++++++++-- 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index d6dbb350e6..e7c27dc332 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -8,7 +8,15 @@ import ( // GatewayInfo represents the information of an external gateway for any // particular network router. type GatewayInfo struct { - NetworkID string `json:"network_id"` + NetworkID string `json:"network_id"` + ExternalFixedIPs []ExternalFixedIP `json:"external_fixed_ips,omitempty"` +} + +// ExternalFixedIP is the IP address and subnet ID of the external gateway of a +// router. +type ExternalFixedIP struct { + IPAddress string `json:"ip_address"` + SubnetID string `json:"subnet_id"` } // Route is a possible route in a router. diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index bf7f35e54c..9feceaba0e 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -44,6 +44,21 @@ func TestList(t *testing.T) { "tenant_id": "33a40233088643acb66ff6eb0ebea679", "distributed": false, "id": "a9254bdb-2613-4a13-ac4c-adc581fba50d" + }, + { + "status": "ACTIVE", + "external_gateway_info": { + "network_id": "2b37576e-b050-4891-8b20-e1e37a93942a", + "external_fixed_ips": [ + {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + {"ip_address": "198.51.100.33", "subnet_id": "1d699529-bdfd-43f8-bcaa-bff00c547af2"} + ] + }, + "name": "gateway", + "admin_state_up": true, + "tenant_id": "a3e881e0a6534880c5473d95b9442099", + "distributed": false, + "id": "308a035c-005d-4452-a9fe-6f8f2f0c28d8" } ] } @@ -79,6 +94,21 @@ func TestList(t *testing.T) { ID: "a9254bdb-2613-4a13-ac4c-adc581fba50d", TenantID: "33a40233088643acb66ff6eb0ebea679", }, + { + Status: "ACTIVE", + GatewayInfo: routers.GatewayInfo{ + NetworkID: "2b37576e-b050-4891-8b20-e1e37a93942a", + ExternalFixedIPs: []routers.ExternalFixedIP{ + {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + {IPAddress: "198.51.100.33", SubnetID: "1d699529-bdfd-43f8-bcaa-bff00c547af2"}, + }, + }, + AdminStateUp: true, + Distributed: false, + Name: "gateway", + ID: "308a035c-005d-4452-a9fe-6f8f2f0c28d8", + TenantID: "a3e881e0a6534880c5473d95b9442099", + }, } th.CheckDeepEquals(t, expected, actual) @@ -120,7 +150,10 @@ func TestCreate(t *testing.T) { "router": { "status": "ACTIVE", "external_gateway_info": { - "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b" + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "external_fixed_ips": [ + {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] }, "name": "foo_router", "admin_state_up": false, @@ -143,9 +176,14 @@ func TestCreate(t *testing.T) { r, err := routers.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) + gwi.ExternalFixedIPs = []routers.ExternalFixedIP{{ + IPAddress: "192.0.2.17", + SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def", + }} + th.AssertEquals(t, "foo_router", r.Name) th.AssertEquals(t, false, r.AdminStateUp) - th.AssertDeepEquals(t, routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}, r.GatewayInfo) + th.AssertDeepEquals(t, gwi, r.GatewayInfo) } func TestGet(t *testing.T) { @@ -164,7 +202,10 @@ func TestGet(t *testing.T) { "router": { "status": "ACTIVE", "external_gateway_info": { - "network_id": "85d76829-6415-48ff-9c63-5c5ca8c61ac6" + "network_id": "85d76829-6415-48ff-9c63-5c5ca8c61ac6", + "external_fixed_ips": [ + {"ip_address": "198.51.100.33", "subnet_id": "1d699529-bdfd-43f8-bcaa-bff00c547af2"} + ] }, "routes": [ { @@ -186,7 +227,12 @@ func TestGet(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") - th.AssertDeepEquals(t, n.GatewayInfo, routers.GatewayInfo{NetworkID: "85d76829-6415-48ff-9c63-5c5ca8c61ac6"}) + th.AssertDeepEquals(t, n.GatewayInfo, routers.GatewayInfo{ + NetworkID: "85d76829-6415-48ff-9c63-5c5ca8c61ac6", + ExternalFixedIPs: []routers.ExternalFixedIP{ + {IPAddress: "198.51.100.33", SubnetID: "1d699529-bdfd-43f8-bcaa-bff00c547af2"}, + }, + }) th.AssertEquals(t, n.Name, "router1") th.AssertEquals(t, n.AdminStateUp, true) th.AssertEquals(t, n.TenantID, "d6554fe62e2f41efbb6e026fad5c1542") @@ -228,7 +274,10 @@ func TestUpdate(t *testing.T) { "router": { "status": "ACTIVE", "external_gateway_info": { - "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b" + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "external_fixed_ips": [ + {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] }, "name": "new_name", "admin_state_up": true, @@ -253,8 +302,12 @@ func TestUpdate(t *testing.T) { n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) + gwi.ExternalFixedIPs = []routers.ExternalFixedIP{ + {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + } + th.AssertEquals(t, n.Name, "new_name") - th.AssertDeepEquals(t, n.GatewayInfo, routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"}) + th.AssertDeepEquals(t, n.GatewayInfo, gwi) th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}) } From 849a2e71dd64dbfa2bd4be110ace68881802414b Mon Sep 17 00:00:00 2001 From: davidaah Date: Thu, 28 Sep 2017 21:20:27 -0600 Subject: [PATCH 0082/2296] Identity v3 - Add Get Projects by User (#529) * Add ListUsers function for Gophercloud's OpenStack Identity v3 API - Allows a caller to get the list of projects that a specified user has access to - Effectively wraps OpenStack Identity v3 API function /v3/users/{user_id}/projects - Based on implementation of ListGroups Note: In line with "ListGroups" uses sample response body from https://developer.openstack.org/api-ref/identity/v3/#list-projects-for-user for test * fix doc and add acceptance test * fix test to point to correct fixture * add CC BY 3.0 attribution where applicable * change data to be mocked instead of doc ref * json syntax fix * fix doc typo --- .../openstack/identity/v3/users_test.go | 33 +++++++++ openstack/identity/v3/users/doc.go | 21 +++++- openstack/identity/v3/users/requests.go | 9 +++ .../identity/v3/users/testing/fixtures.go | 72 ++++++++++++++++++- .../v3/users/testing/requests_test.go | 12 ++++ openstack/identity/v3/users/urls.go | 4 ++ 6 files changed, 149 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index e87c88e44b..3126e3473d 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/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" ) @@ -154,3 +155,35 @@ func TestUsersListGroups(t *testing.T) { tools.PrintResource(t, group.Extra) } } + +func TestUsersListProjects(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + allUserPages, err := users.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list users: %v", err) + } + + allUsers, err := users.ExtractUsers(allUserPages) + if err != nil { + t.Fatalf("Unable to extract users: %v", err) + } + + user := allUsers[0] + + allProjectPages, err := users.ListProjects(client, user.ID).AllPages() + if err != nil { + t.Fatalf("Unable to list projects: %v", err) + } + + allProjects, err := projects.ExtractProjects(allProjectPages) + if err != nil { + t.Fatalf("Unable to extract projects: %v", err) + } + + for _, project := range allProjects { + tools.PrintResource(t, project) + } +} diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index 8a8bf8451b..0d18328de7 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -71,7 +71,7 @@ Example to List Groups a User Belongs To panic(err) } - allGroups, err := users.ExtractGroups(allPages) + allGroups, err := groups.ExtractGroups(allPages) if err != nil { panic(err) } @@ -79,5 +79,24 @@ Example to List Groups a User Belongs To for _, group := range allGroups { fmt.Printf("%+v\n", group) } + +Example to List Projects a User Belongs To + + userID := "0fe36e73809d46aeae6705c39077b1b3" + + allPages, err := users.ListProjects(identityClient, userID).AllPages() + if err != nil { + panic(err) + } + + allProjects, err := projects.ExtractProjects(allPages) + if err != nil { + panic(err) + } + + for _, project := range allProjects { + fmt.Printf("%+v\n", project) + } + */ package users diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index d0dc26f64d..2bf4e3927e 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -3,6 +3,7 @@ package users import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/pagination" ) @@ -216,3 +217,11 @@ func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pag return groups.GroupPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// ListProjects enumerates groups user belongs to. +func ListProjects(client *gophercloud.ServiceClient, userID string) pagination.Pager { + url := listProjectsURL(client, userID) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return projects.ProjectPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 00a0e3493f..733d8a97d8 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" @@ -195,6 +196,41 @@ const ListGroupsOutput = ` } ` +// ListProjectsOutput provides a ListProjects result. +const ListProjectsOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://localhost:5000/identity/v3/users/foobar/projects" + }, + "projects": [ + { + "description": "my first project", + "domain_id": "11111", + "enabled": true, + "id": "abcde", + "links": { + "self": "http://localhost:5000/identity/v3/projects/abcde" + }, + "name": "project 1", + "parent_id": "11111" + }, + { + "description": "my second project", + "domain_id": "22222", + "enabled": true, + "id": "bcdef", + "links": { + "self": "http://localhost:5000/identity/v3/projects/bcdef" + }, + "name": "project 2", + "parent_id": "22222" + } + ] +} +` + // FirstUser is the first user in the List request. var nilTime time.Time var FirstUser = users.User{ @@ -300,6 +336,26 @@ var SecondGroup = groups.Group{ var ExpectedGroupsSlice = []groups.Group{FirstGroup, SecondGroup} +var FirstProject = projects.Project{ + Description: "my first project", + DomainID: "11111", + Enabled: true, + ID: "abcde", + Name: "project 1", + ParentID: "11111", +} + +var SecondProject = projects.Project{ + Description: "my second project", + DomainID: "22222", + Enabled: true, + ID: "bcdef", + Name: "project 2", + ParentID: "22222", +} + +var ExpectedProjectsSlice = []projects.Project{FirstProject, SecondProject} + // HandleListUsersSuccessfully creates an HTTP handler at `/users` on the // test handler mux that responds with a list of two users. func HandleListUsersSuccessfully(t *testing.T) { @@ -379,7 +435,7 @@ func HandleDeleteUserSuccessfully(t *testing.T) { } // HandleListUserGroupsSuccessfully creates an HTTP handler at /users/{userID}/groups -// on the test handler mux that respons wit a list of two groups +// on the test handler mux that respons with a list of two groups func HandleListUserGroupsSuccessfully(t *testing.T) { th.Mux.HandleFunc("/users/9fe1d3/groups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -391,3 +447,17 @@ func HandleListUserGroupsSuccessfully(t *testing.T) { fmt.Fprintf(w, ListGroupsOutput) }) } + +// HandleListUserProjectsSuccessfully creates an HTTP handler at /users/{userID}/projects +// on the test handler mux that respons wit a list of two projects +func HandleListUserProjectsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/9fe1d3/projects", 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, ListProjectsOutput) + }) +} diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index 5ba21f9de6..ef52cd64b7 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -146,3 +147,14 @@ func TestListUserGroups(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedGroupsSlice, actual) } + +func TestListUserProjects(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListUserProjectsSuccessfully(t) + allPages, err := users.ListProjects(client.ServiceClient(), "9fe1d3").AllPages() + th.AssertNoErr(t, err) + actual, err := projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedProjectsSlice, actual) +} diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index 9ac605ae42..5f738bcca9 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -25,3 +25,7 @@ func deleteURL(client *gophercloud.ServiceClient, userID string) string { func listGroupsURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "groups") } + +func listProjectsURL(client *gophercloud.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "projects") +} From 24c79fc5519ef9982543a0c8bf0a3586151ae972 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 28 Sep 2017 14:39:07 -0600 Subject: [PATCH 0083/2296] Adding ability to list users for a group --- openstack/identity/v3/users/doc.go | 21 +++++++++++++++++++ openstack/identity/v3/users/requests.go | 15 +++++++++++++ .../identity/v3/users/testing/fixtures.go | 14 +++++++++++++ .../v3/users/testing/requests_test.go | 17 +++++++++++++++ openstack/identity/v3/users/urls.go | 4 ++++ 5 files changed, 71 insertions(+) diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index 0d18328de7..aa7ec196f5 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -98,5 +98,26 @@ Example to List Projects a User Belongs To fmt.Printf("%+v\n", project) } +Example to List Users in a Group + + groupID := "bede500ee1124ae9b0006ff859758b3a" + listOpts := users.ListOpts{ + DomainID: "default", + } + + allPages, err := users.ListInGroup(identityClient, groupID, listOpts).AllPages() + if err != nil { + panic(err) + } + + allUsers, err := users.ExtractUsers(allPages) + if err != nil { + panic(err) + } + + for _, user := range allUsers { + fmt.Printf("%+v\n", user) + } + */ package users diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 2bf4e3927e..77abd59f6c 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -225,3 +225,18 @@ func ListProjects(client *gophercloud.ServiceClient, userID string) pagination.P return projects.ProjectPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// ListInGroup enumerates users that belong to a group. +func ListInGroup(client *gophercloud.ServiceClient, groupID string, opts ListOptsBuilder) pagination.Pager { + url := listInGroupURL(client, groupID) + if opts != nil { + query, err := opts.ToUserListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return UserPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 733d8a97d8..8d8e6df642 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -461,3 +461,17 @@ func HandleListUserProjectsSuccessfully(t *testing.T) { fmt.Fprintf(w, ListProjectsOutput) }) } + +// HandleListInGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users +// on the test handler mux that response with a list of two users +func HandleListInGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/ea167b/users", 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/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index ef52cd64b7..15314ca61c 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -158,3 +158,20 @@ func TestListUserProjects(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedProjectsSlice, actual) } + +func TestListInGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListInGroupSuccessfully(t) + + iTrue := true + listOpts := users.ListOpts{ + Enabled: &iTrue, + } + + allPages, err := users.ListInGroup(client.ServiceClient(), "ea167b", listOpts).AllPages() + th.AssertNoErr(t, err) + actual, err := users.ExtractUsers(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedUsersSlice, actual) +} diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index 5f738bcca9..1db2831b5e 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -29,3 +29,7 @@ func listGroupsURL(client *gophercloud.ServiceClient, userID string) string { func listProjectsURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "projects") } + +func listInGroupURL(client *gophercloud.ServiceClient, groupID string) string { + return client.ServiceURL("groups", groupID, "users") +} From 5617b7de10c580506dbb0952429dc776d559c89b Mon Sep 17 00:00:00 2001 From: David Lyle Date: Fri, 6 Oct 2017 12:45:41 -0600 Subject: [PATCH 0084/2296] Identity v3 Groups Get / List --- .../openstack/identity/v3/groups_test.go | 62 +++++++++ .../openstack/identity/v3/users_test.go | 33 +++++ openstack/identity/v3/groups/doc.go | 25 +++- openstack/identity/v3/groups/requests.go | 48 +++++++ openstack/identity/v3/groups/results.go | 6 + .../identity/v3/groups/testing/fixtures.go | 125 ++++++++++++++++++ .../v3/groups/testing/requests_test.go | 56 ++++++++ openstack/identity/v3/groups/urls.go | 11 ++ 8 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 acceptance/openstack/identity/v3/groups_test.go create mode 100644 openstack/identity/v3/groups/requests.go create mode 100644 openstack/identity/v3/groups/testing/fixtures.go create mode 100644 openstack/identity/v3/groups/testing/requests_test.go create mode 100644 openstack/identity/v3/groups/urls.go diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go new file mode 100644 index 0000000000..950fb1f3ff --- /dev/null +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -0,0 +1,62 @@ +// +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/groups" +) + +func TestGroupsList(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + listOpts := groups.ListOpts{ + DomainID: "default", + } + + allPages, err := groups.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list groups: %v", err) + } + + allGroups, err := groups.ExtractGroups(allPages) + if err != nil { + t.Fatalf("Unable to extract groups: %v", err) + } + + for _, group := range allGroups { + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) + } +} + +func TestGroupsGet(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + allPages, err := groups.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list groups: %v", err) + } + + allGroups, err := groups.ExtractGroups(allPages) + if err != nil { + t.Fatalf("Unable to extract groups: %v", err) + } + + group := allGroups[0] + p, err := groups.Get(client, group.ID).Extract() + if err != nil { + t.Fatalf("Unable to get group: %v", err) + } + + tools.PrintResource(t, p) +} diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index 3126e3473d..3ba1e87cf5 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -187,3 +187,36 @@ func TestUsersListProjects(t *testing.T) { tools.PrintResource(t, project) } } + +func TestUsersListInGroup(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + allGroupPages, err := groups.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list groups: %v", err) + } + + allGroups, err := groups.ExtractGroups(allGroupPages) + if err != nil { + t.Fatalf("Unable to extract groups: %v", err) + } + + group := allGroups[0] + + allUserPages, err := users.ListInGroup(client, group.ID, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list users: %v", err) + } + + allUsers, err := users.ExtractUsers(allUserPages) + if err != nil { + t.Fatalf("Unable to extract users: %v", err) + } + + for _, user := range allUsers { + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) + } +} diff --git a/openstack/identity/v3/groups/doc.go b/openstack/identity/v3/groups/doc.go index dfbf3dec77..3913d75936 100644 --- a/openstack/identity/v3/groups/doc.go +++ b/openstack/identity/v3/groups/doc.go @@ -1,3 +1,24 @@ -// Package groups retrieves and manages groups in the OpenStack Identity -// Service. +/* +Package groups manages and retrieves Groups in the OpenStack Identity Service. + +Example to List Groups + + listOpts := groups.ListOpts{ + DomainID: "default", + } + + allPages, err := groups.List(identityClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allGroups, err := groups.ExtractGroups(allPages) + if err != nil { + panic(err) + } + + for _, group := range allGroups { + fmt.Printf("%+v\n", group) + } +*/ package groups diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go new file mode 100644 index 0000000000..0400244b08 --- /dev/null +++ b/openstack/identity/v3/groups/requests.go @@ -0,0 +1,48 @@ +package groups + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToGroupListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // DomainID filters the response by a domain ID. + DomainID string `q:"domain_id"` + + // Name filters the response by group name. + Name string `q:"name"` +} + +// ToGroupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToGroupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the Groups to which the current token has access. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToGroupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return GroupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details on a single group, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go index ad25d6c8c1..369aea6b4a 100644 --- a/openstack/identity/v3/groups/results.go +++ b/openstack/identity/v3/groups/results.go @@ -63,6 +63,12 @@ type groupResult struct { gophercloud.Result } +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Group. +type GetResult struct { + groupResult +} + // GroupPage is a single page of Group results. type GroupPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/groups/testing/fixtures.go b/openstack/identity/v3/groups/testing/fixtures.go new file mode 100644 index 0000000000..078ea7e809 --- /dev/null +++ b/openstack/identity/v3/groups/testing/fixtures.go @@ -0,0 +1,125 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of Group results. +const ListOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/groups" + }, + "groups": [ + { + "domain_id": "default", + "id": "2844b2a08be147a08ef58317d6471f1f", + "description": "group for internal support users", + "links": { + "self": "http://example.com/identity/v3/groups/2844b2a08be147a08ef58317d6471f1f" + }, + "name": "internal support", + "extra": { + "email": "support@localhost" + } + }, + { + "domain_id": "1789d1", + "id": "9fe1d3", + "description": "group for support users", + "links": { + "self": "https://example.com/identity/v3/groups/9fe1d3" + }, + "name": "support", + "extra": { + "email": "support@example.com" + } + } + ] +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "group": { + "domain_id": "1789d1", + "id": "9fe1d3", + "description": "group for support users", + "links": { + "self": "https://example.com/identity/v3/groups/9fe1d3" + }, + "name": "support", + "extra": { + "email": "support@example.com" + } + } +} +` + +// FirstGroup is the first group in the List request. +var FirstGroup = groups.Group{ + DomainID: "default", + ID: "2844b2a08be147a08ef58317d6471f1f", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/groups/2844b2a08be147a08ef58317d6471f1f", + }, + Name: "internal support", + Description: "group for internal support users", + Extra: map[string]interface{}{ + "email": "support@localhost", + }, +} + +// SecondGroup is the second group in the List request. +var SecondGroup = groups.Group{ + DomainID: "1789d1", + ID: "9fe1d3", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/groups/9fe1d3", + }, + Name: "support", + Description: "group for support users", + Extra: map[string]interface{}{ + "email": "support@example.com", + }, +} + +// ExpectedGroupsSlice is the slice of groups expected to be returned from ListOutput. +var ExpectedGroupsSlice = []groups.Group{FirstGroup, SecondGroup} + +// HandleListGroupsSuccessfully creates an HTTP handler at `/groups` on the +// test handler mux that responds with a list of two groups. +func HandleListGroupsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups", 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) + }) +} + +// HandleGetGroupSuccessfully creates an HTTP handler at `/groups` on the +// test handler mux that responds with a single group. +func HandleGetGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/9fe1d3", 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/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go new file mode 100644 index 0000000000..3898d95bd2 --- /dev/null +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -0,0 +1,56 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListGroups(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListGroupsSuccessfully(t) + + count := 0 + err := groups.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := groups.ExtractGroups(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedGroupsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListGroupsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListGroupsSuccessfully(t) + + allPages, err := groups.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := groups.ExtractGroups(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedGroupsSlice, actual) + th.AssertEquals(t, ExpectedGroupsSlice[0].Extra["email"], "support@localhost") + th.AssertEquals(t, ExpectedGroupsSlice[1].Extra["email"], "support@example.com") +} + +func TestGetGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetGroupSuccessfully(t) + + actual, err := groups.Get(client.ServiceClient(), "9fe1d3").Extract() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondGroup, *actual) + th.AssertEquals(t, SecondGroup.Extra["email"], "support@example.com") +} diff --git a/openstack/identity/v3/groups/urls.go b/openstack/identity/v3/groups/urls.go new file mode 100644 index 0000000000..61fa8a843c --- /dev/null +++ b/openstack/identity/v3/groups/urls.go @@ -0,0 +1,11 @@ +package groups + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("groups") +} + +func getURL(client *gophercloud.ServiceClient, groupID string) string { + return client.ServiceURL("groups", groupID) +} From 5c8090bc6dd57ac779186acd906f5ab534ace10c Mon Sep 17 00:00:00 2001 From: Phionah Bugosi Date: Mon, 9 Oct 2017 19:30:11 +0300 Subject: [PATCH 0085/2296] Add go vet to Travis (#536) * Add go vet to Travis * Add go vet to Travis * Add go vet to Travis * Add go vet * Fix go vet errors * Add go vet * Fix go vet errors * Fix go vet errors * Fix go vet errors * Add go vet and Fix go vet errors --- .travis.yml | 2 ++ .../openstack/networking/v2/extensions/extensions.go | 4 ++-- openstack/identity/v3/users/requests.go | 4 ++-- .../v2/extensions/external/testing/results_test.go | 8 ++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5d1486901d..59c4194952 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,8 @@ go: env: global: - secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ=" +before_script: +- go vet ./... script: - ./script/coverage - ./script/format diff --git a/acceptance/openstack/networking/v2/extensions/extensions.go b/acceptance/openstack/networking/v2/extensions/extensions.go index 29a31e91a5..06068322e1 100644 --- a/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/acceptance/openstack/networking/v2/extensions/extensions.go @@ -28,8 +28,8 @@ func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*ne } createOpts := external.CreateOptsExt{ - networkCreateOpts, - &isExternal, + CreateOptsBuilder: networkCreateOpts, + External: &isExternal, } network, err := networks.Create(client, createOpts).Extract() diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 77abd59f6c..779d116fcc 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -214,7 +214,7 @@ func Delete(client *gophercloud.ServiceClient, userID string) (r DeleteResult) { func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := listGroupsURL(client, userID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return groups.GroupPage{pagination.LinkedPageBase{PageResult: r}} + return groups.GroupPage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}} }) } @@ -222,7 +222,7 @@ func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pag func ListProjects(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := listProjectsURL(client, userID) return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return projects.ProjectPage{pagination.LinkedPageBase{PageResult: r}} + return projects.ProjectPage{LinkedPageBase: pagination.LinkedPageBase{PageResult: r}} }) } diff --git a/openstack/networking/v2/extensions/external/testing/results_test.go b/openstack/networking/v2/extensions/external/testing/results_test.go index 48448e3dec..64a508824d 100644 --- a/openstack/networking/v2/extensions/external/testing/results_test.go +++ b/openstack/networking/v2/extensions/external/testing/results_test.go @@ -93,8 +93,8 @@ func TestCreate(t *testing.T) { } externalCreateOpts := external.CreateOptsExt{ - networkCreateOpts, - &iFalse, + CreateOptsBuilder: &networkCreateOpts, + External: &iFalse, } _, err := networks.Create(fake.ServiceClient(), externalCreateOpts).Extract() @@ -129,8 +129,8 @@ func TestUpdate(t *testing.T) { } externalUpdateOpts := external.UpdateOptsExt{ - networkUpdateOpts, - &iFalse, + UpdateOptsBuilder: &networkUpdateOpts, + External: &iFalse, } _, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", externalUpdateOpts).Extract() From d30d33cb49cd15bb5ebac99ddbbc24f80dafad98 Mon Sep 17 00:00:00 2001 From: Suramya Shah Date: Fri, 6 Oct 2017 20:47:45 +0530 Subject: [PATCH 0086/2296] Compute v2: Add Reset State Action --- .../compute/v2/extensions/resetstate/doc.go | 13 +++++++++++ .../v2/extensions/resetstate/requests.go | 23 +++++++++++++++++++ .../v2/extensions/resetstate/results.go | 11 +++++++++ .../v2/extensions/resetstate/testing/doc.go | 1 + .../extensions/resetstate/testing/fixtures.go | 19 +++++++++++++++ .../resetstate/testing/requests_test.go | 21 +++++++++++++++++ .../compute/v2/extensions/resetstate/urls.go | 9 ++++++++ 7 files changed, 97 insertions(+) create mode 100644 openstack/compute/v2/extensions/resetstate/doc.go create mode 100644 openstack/compute/v2/extensions/resetstate/requests.go create mode 100644 openstack/compute/v2/extensions/resetstate/results.go create mode 100644 openstack/compute/v2/extensions/resetstate/testing/doc.go create mode 100644 openstack/compute/v2/extensions/resetstate/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/resetstate/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/resetstate/urls.go diff --git a/openstack/compute/v2/extensions/resetstate/doc.go b/openstack/compute/v2/extensions/resetstate/doc.go new file mode 100644 index 0000000000..00f004b022 --- /dev/null +++ b/openstack/compute/v2/extensions/resetstate/doc.go @@ -0,0 +1,13 @@ +/* +Package resetstate provides functionality to reset the state of a server that has +been provisioned by the OpenStack Compute service. + +Example to Reset a Server + + serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" + err := resetstate.ResetState(client, id, resetstate.StateActive).ExtractErr() + if err != nil { + panic(err) + } +*/ +package resetstate diff --git a/openstack/compute/v2/extensions/resetstate/requests.go b/openstack/compute/v2/extensions/resetstate/requests.go new file mode 100644 index 0000000000..628e44aa47 --- /dev/null +++ b/openstack/compute/v2/extensions/resetstate/requests.go @@ -0,0 +1,23 @@ +package resetstate + +import ( + "github.com/gophercloud/gophercloud" +) + +// ServerState refers to the states usable in ResetState Action +type ServerState string + +const ( + // StateActive returns the state of the server as active + StateActive ServerState = "active" + + // StateError returns the state of the server as error + StateError ServerState = "error" +) + +// ResetState will reset the state of a server +func ResetState(client *gophercloud.ServiceClient, id string, state ServerState) (r ResetResult) { + stateMap := map[string]interface{}{"state": state} + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) + return +} diff --git a/openstack/compute/v2/extensions/resetstate/results.go b/openstack/compute/v2/extensions/resetstate/results.go new file mode 100644 index 0000000000..ddeb3519a1 --- /dev/null +++ b/openstack/compute/v2/extensions/resetstate/results.go @@ -0,0 +1,11 @@ +package resetstate + +import ( + "github.com/gophercloud/gophercloud" +) + +// ResetResult is the response of a ResetState operation. Call its ExtractErr +// method to determine if the request suceeded or failed. +type ResetResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/resetstate/testing/doc.go b/openstack/compute/v2/extensions/resetstate/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/compute/v2/extensions/resetstate/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/compute/v2/extensions/resetstate/testing/fixtures.go b/openstack/compute/v2/extensions/resetstate/testing/fixtures.go new file mode 100644 index 0000000000..857a8b2127 --- /dev/null +++ b/openstack/compute/v2/extensions/resetstate/testing/fixtures.go @@ -0,0 +1,19 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func mockResetStateResponse(t *testing.T, id string, state 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, fmt.Sprintf(`{"os-resetState": {"state": "%s"}}`, state)) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/compute/v2/extensions/resetstate/testing/requests_test.go b/openstack/compute/v2/extensions/resetstate/testing/requests_test.go new file mode 100644 index 0000000000..491a7ee1fb --- /dev/null +++ b/openstack/compute/v2/extensions/resetstate/testing/requests_test.go @@ -0,0 +1,21 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/resetstate" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" + +func TestResetState(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockResetStateResponse(t, serverID, "active") + + err := resetstate.ResetState(client.ServiceClient(), serverID, "active").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/resetstate/urls.go b/openstack/compute/v2/extensions/resetstate/urls.go new file mode 100644 index 0000000000..c6da6d9304 --- /dev/null +++ b/openstack/compute/v2/extensions/resetstate/urls.go @@ -0,0 +1,9 @@ +package resetstate + +import ( + "github.com/gophercloud/gophercloud" +) + +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "action") +} From 6e282e125d8fa56d872d35b451f3b85e17547c64 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Mon, 9 Oct 2017 09:26:23 -0600 Subject: [PATCH 0087/2296] Identity v3: Add Group create Changing group acceptance tests to a CRUD based test now that that create exists. This also reduces the risks of errant test failures as group existence is required in an openstack v3 deployment. --- .../openstack/identity/v3/groups_test.go | 42 +++++++-------- acceptance/openstack/identity/v3/identity.go | 30 ++++++++++- openstack/identity/v3/groups/doc.go | 15 ++++++ openstack/identity/v3/groups/requests.go | 52 +++++++++++++++++++ openstack/identity/v3/groups/results.go | 6 +++ .../identity/v3/groups/testing/fixtures.go | 25 +++++++++ .../v3/groups/testing/requests_test.go | 19 +++++++ openstack/identity/v3/groups/urls.go | 4 ++ 8 files changed, 171 insertions(+), 22 deletions(-) diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go index 950fb1f3ff..9b66aac302 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -10,39 +10,34 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" ) -func TestGroupsList(t *testing.T) { +func TestGroupCRUD(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { t.Fatalf("Unable to obtain an identity client: %v", err) } - listOpts := groups.ListOpts{ + createOpts := groups.CreateOpts{ + Name: "testgroup", DomainID: "default", + Extra: map[string]interface{}{ + "email": "testgroup@example.com", + }, } - allPages, err := groups.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list groups: %v", err) - } - - allGroups, err := groups.ExtractGroups(allPages) + // Create Group in the default domain + group, err := CreateGroup(t, client, &createOpts) if err != nil { - t.Fatalf("Unable to extract groups: %v", err) + t.Fatalf("Unable to create group: %v", err) } + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) - for _, group := range allGroups { - tools.PrintResource(t, group) - tools.PrintResource(t, group.Extra) - } -} - -func TestGroupsGet(t *testing.T) { - client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) + listOpts := groups.ListOpts{ + DomainID: "default", } - allPages, err := groups.List(client, nil).AllPages() + // List all Groups in default domain + allPages, err := groups.List(client, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list groups: %v", err) } @@ -52,7 +47,12 @@ func TestGroupsGet(t *testing.T) { t.Fatalf("Unable to extract groups: %v", err) } - group := allGroups[0] + for _, g := range allGroups { + tools.PrintResource(t, g) + tools.PrintResource(t, g.Extra) + } + + // Get the recently created group by ID p, err := groups.Get(client, group.ID).Extract() if err != nil { t.Fatalf("Unable to get group: %v", err) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 4f2f6219d9..bda7af3cd4 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" ) @@ -36,7 +37,7 @@ func CreateProject(t *testing.T, client *gophercloud.ServiceClient, c *projects. return project, nil } -// CreateUser will create a project with a random name. +// CreateUser will create a user with a random name. // It takes an optional createOpts parameter since creating a user // has so many options. An error will be returned if the user was // unable to be created. @@ -63,6 +64,33 @@ func CreateUser(t *testing.T, client *gophercloud.ServiceClient, c *users.Create return user, nil } +// CreateGroup will create a group with a random name. +// It takes an optional createOpts parameter since creating a group +// has so many options. An error will be returned if the group was +// unable to be created. +func CreateGroup(t *testing.T, client *gophercloud.ServiceClient, c *groups.CreateOpts) (*groups.Group, error) { + name := tools.RandomString("ACPTTEST", 8) + t.Logf("Attempting to create group: %s", name) + + var createOpts groups.CreateOpts + if c != nil { + createOpts = *c + } else { + createOpts = groups.CreateOpts{} + } + + createOpts.Name = name + + group, err := groups.Create(client, createOpts).Extract() + if err != nil { + return group, err + } + + t.Logf("Successfully created group %s with ID %s", name, group.ID) + + return group, nil +} + // DeleteProject will delete a project by ID. A fatal error will occur if // the project ID failed to be deleted. This works best when using it as // a deferred function. diff --git a/openstack/identity/v3/groups/doc.go b/openstack/identity/v3/groups/doc.go index 3913d75936..ead2552701 100644 --- a/openstack/identity/v3/groups/doc.go +++ b/openstack/identity/v3/groups/doc.go @@ -20,5 +20,20 @@ Example to List Groups for _, group := range allGroups { fmt.Printf("%+v\n", group) } + +Example to Create a Group + + createOpts := groups.CreateOpts{ + Name: "groupname", + DomainID: "default", + Extra: map[string]interface{}{ + "email": "groupname@example.com", + } + } + + group, err := groups.Create(identityClient, createOpts).Extract() + if err != nil { + panic(err) + } */ package groups diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index 0400244b08..8bb7474af6 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -46,3 +46,55 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) 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 group. + Name string `json:"name" required:"true"` + + // Description is a description of the group. + Description string `json:"description,omitempty"` + + // DomainID is the ID of the domain the group belongs to. + DomainID string `json:"domain_id,omitempty"` + + // Extra is free-form extra key/value pairs to describe the group. + Extra map[string]interface{} `json:"-"` +} + +// ToGroupCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToGroupCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "group") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["group"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Create creates a new Group. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToGroupCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go index 369aea6b4a..14469256b0 100644 --- a/openstack/identity/v3/groups/results.go +++ b/openstack/identity/v3/groups/results.go @@ -69,6 +69,12 @@ type GetResult struct { groupResult } +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Group. +type CreateResult struct { + groupResult +} + // GroupPage is a single page of Group results. type GroupPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/groups/testing/fixtures.go b/openstack/identity/v3/groups/testing/fixtures.go index 078ea7e809..8acc7b0ef9 100644 --- a/openstack/identity/v3/groups/testing/fixtures.go +++ b/openstack/identity/v3/groups/testing/fixtures.go @@ -65,6 +65,18 @@ const GetOutput = ` } ` +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "group": { + "domain_id": "1789d1", + "name": "support", + "description": "group for support users", + "email": "support@example.com" + } +} +` + // FirstGroup is the first group in the List request. var FirstGroup = groups.Group{ DomainID: "default", @@ -123,3 +135,16 @@ func HandleGetGroupSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleCreateGroupSuccessfully creates an HTTP handler at `/groups` on the +// test handler mux that tests group creation. +func HandleCreateGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups", 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, GetOutput) + }) +} diff --git a/openstack/identity/v3/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go index 3898d95bd2..a34a316dd2 100644 --- a/openstack/identity/v3/groups/testing/requests_test.go +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -54,3 +54,22 @@ func TestGetGroup(t *testing.T) { th.CheckDeepEquals(t, SecondGroup, *actual) th.AssertEquals(t, SecondGroup.Extra["email"], "support@example.com") } + +func TestCreateGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateGroupSuccessfully(t) + + createOpts := groups.CreateOpts{ + Name: "support", + DomainID: "1789d1", + Description: "group for support users", + Extra: map[string]interface{}{ + "email": "support@example.com", + }, + } + + actual, err := groups.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondGroup, *actual) +} diff --git a/openstack/identity/v3/groups/urls.go b/openstack/identity/v3/groups/urls.go index 61fa8a843c..d37ca6ed42 100644 --- a/openstack/identity/v3/groups/urls.go +++ b/openstack/identity/v3/groups/urls.go @@ -9,3 +9,7 @@ func listURL(client *gophercloud.ServiceClient) string { func getURL(client *gophercloud.ServiceClient, groupID string) string { return client.ServiceURL("groups", groupID) } + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("groups") +} From 3e45e3e09877fb2d6b6794a6d1ac55e7ba1f5b64 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Tue, 10 Oct 2017 10:47:20 -0600 Subject: [PATCH 0088/2296] Identity v3: group delete --- acceptance/openstack/identity/v3/groups_test.go | 2 ++ acceptance/openstack/identity/v3/identity.go | 12 ++++++++++++ openstack/identity/v3/groups/doc.go | 8 ++++++++ openstack/identity/v3/groups/requests.go | 6 ++++++ openstack/identity/v3/groups/results.go | 6 ++++++ openstack/identity/v3/groups/testing/fixtures.go | 11 +++++++++++ .../identity/v3/groups/testing/requests_test.go | 9 +++++++++ openstack/identity/v3/groups/urls.go | 4 ++++ 8 files changed, 58 insertions(+) diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go index 9b66aac302..4729a60b58 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -29,6 +29,8 @@ func TestGroupCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create group: %v", err) } + defer DeleteGroup(t, client, group.ID) + tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index bda7af3cd4..bd861cd8dd 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -114,3 +114,15 @@ func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) t.Logf("Deleted user: %s", userID) } + +// DeleteGroup will delete a group by ID. A fatal error will occur if +// the group failed to be deleted. This works best when using it as +// a deferred function. +func DeleteGroup(t *testing.T, client *gophercloud.ServiceClient, groupID string) { + err := groups.Delete(client, groupID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete group %s: %v", groupID, err) + } + + t.Logf("Deleted group: %s", groupID) +} diff --git a/openstack/identity/v3/groups/doc.go b/openstack/identity/v3/groups/doc.go index ead2552701..e08394c9b0 100644 --- a/openstack/identity/v3/groups/doc.go +++ b/openstack/identity/v3/groups/doc.go @@ -35,5 +35,13 @@ Example to Create a Group if err != nil { panic(err) } + +Example to Delete a Group + + groupID := "0fe36e73809d46aeae6705c39077b1b3" + err := groups.Delete(identityClient, groupID).ExtractErr() + if err != nil { + panic(err) + } */ package groups diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index 8bb7474af6..8dd755b9c8 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -98,3 +98,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// Delete deletes a group. +func Delete(client *gophercloud.ServiceClient, groupID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, groupID), nil) + return +} diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go index 14469256b0..579e6db611 100644 --- a/openstack/identity/v3/groups/results.go +++ b/openstack/identity/v3/groups/results.go @@ -75,6 +75,12 @@ type CreateResult struct { groupResult } +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // GroupPage is a single page of Group results. type GroupPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/groups/testing/fixtures.go b/openstack/identity/v3/groups/testing/fixtures.go index 8acc7b0ef9..5fcb4ae2d7 100644 --- a/openstack/identity/v3/groups/testing/fixtures.go +++ b/openstack/identity/v3/groups/testing/fixtures.go @@ -148,3 +148,14 @@ func HandleCreateGroupSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleDeleteGroupSuccessfully creates an HTTP handler at `/groups` on the +// test handler mux that tests group deletion. +func HandleDeleteGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/9fe1d3", 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/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go index a34a316dd2..140b579798 100644 --- a/openstack/identity/v3/groups/testing/requests_test.go +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -73,3 +73,12 @@ func TestCreateGroup(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondGroup, *actual) } + +func TestDeleteGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteGroupSuccessfully(t) + + res := groups.Delete(client.ServiceClient(), "9fe1d3") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/groups/urls.go b/openstack/identity/v3/groups/urls.go index d37ca6ed42..5ad30d1f12 100644 --- a/openstack/identity/v3/groups/urls.go +++ b/openstack/identity/v3/groups/urls.go @@ -13,3 +13,7 @@ func getURL(client *gophercloud.ServiceClient, groupID string) string { func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("groups") } + +func deleteURL(client *gophercloud.ServiceClient, groupID string) string { + return client.ServiceURL("groups", groupID) +} From 6507d1f1b23138e185db7879e9e46069ab5cc6a6 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Wed, 11 Oct 2017 08:25:41 -0600 Subject: [PATCH 0089/2296] Identity v3: group update --- .../openstack/identity/v3/groups_test.go | 15 +++++ openstack/identity/v3/groups/doc.go | 13 +++++ openstack/identity/v3/groups/requests.go | 52 ++++++++++++++++++ openstack/identity/v3/groups/results.go | 6 ++ .../identity/v3/groups/testing/fixtures.go | 55 +++++++++++++++++++ .../v3/groups/testing/requests_test.go | 17 ++++++ openstack/identity/v3/groups/urls.go | 4 ++ 7 files changed, 162 insertions(+) diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go index 4729a60b58..3e832c2022 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -34,6 +34,21 @@ func TestGroupCRUD(t *testing.T) { tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) + updateOpts := groups.UpdateOpts{ + Description: "Test Users", + Extra: map[string]interface{}{ + "email": "thetestgroup@example.com", + }, + } + + newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update group: %v", err) + } + + tools.PrintResource(t, newGroup) + tools.PrintResource(t, newGroup.Extra) + listOpts := groups.ListOpts{ DomainID: "default", } diff --git a/openstack/identity/v3/groups/doc.go b/openstack/identity/v3/groups/doc.go index e08394c9b0..696e2a5d8e 100644 --- a/openstack/identity/v3/groups/doc.go +++ b/openstack/identity/v3/groups/doc.go @@ -36,6 +36,19 @@ Example to Create a Group panic(err) } +Example to Update a Group + + groupID := "0fe36e73809d46aeae6705c39077b1b3" + + updateOpts := groups.UpdateOpts{ + Description: "Updated Description for group", + } + + group, err := groups.Update(identityClient, groupID, updateOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a Group groupID := "0fe36e73809d46aeae6705c39077b1b3" diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index 8dd755b9c8..b6e74dcf97 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -99,6 +99,58 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create 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 a group. +type UpdateOpts struct { + // Name is the name of the new group. + Name string `json:"name,omitempty"` + + // Description is a description of the group. + Description string `json:"description,omitempty"` + + // DomainID is the ID of the domain the group belongs to. + DomainID string `json:"domain_id,omitempty"` + + // Extra is free-form extra key/value pairs to describe the group. + Extra map[string]interface{} `json:"-"` +} + +// ToGroupUpdateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToGroupUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "group") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["group"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Update updates an existing Group. +func Update(client *gophercloud.ServiceClient, groupID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToGroupUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(updateURL(client, groupID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // Delete deletes a group. func Delete(client *gophercloud.ServiceClient, groupID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, groupID), nil) diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go index 579e6db611..ba7d018d17 100644 --- a/openstack/identity/v3/groups/results.go +++ b/openstack/identity/v3/groups/results.go @@ -75,6 +75,12 @@ type CreateResult struct { groupResult } +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Group. +type UpdateResult struct { + groupResult +} + // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { diff --git a/openstack/identity/v3/groups/testing/fixtures.go b/openstack/identity/v3/groups/testing/fixtures.go index 5fcb4ae2d7..58f3503785 100644 --- a/openstack/identity/v3/groups/testing/fixtures.go +++ b/openstack/identity/v3/groups/testing/fixtures.go @@ -77,6 +77,34 @@ const CreateRequest = ` } ` +// UpdateRequest provides the input to as Update request. +const UpdateRequest = ` +{ + "group": { + "description": "L2 Support Team", + "email": "supportteam@example.com" + } +} +` + +// UpdateOutput provides an update result. +const UpdateOutput = ` +{ + "group": { + "domain_id": "1789d1", + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/groups/9fe1d3" + }, + "name": "support", + "description": "L2 Support Team", + "extra": { + "email": "supportteam@example.com" + } + } +} +` + // FirstGroup is the first group in the List request. var FirstGroup = groups.Group{ DomainID: "default", @@ -105,6 +133,20 @@ var SecondGroup = groups.Group{ }, } +// SecondGroupUpdated is how SecondGroup should look after an Update. +var SecondGroupUpdated = groups.Group{ + DomainID: "1789d1", + ID: "9fe1d3", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/groups/9fe1d3", + }, + Name: "support", + Description: "L2 Support Team", + Extra: map[string]interface{}{ + "email": "supportteam@example.com", + }, +} + // ExpectedGroupsSlice is the slice of groups expected to be returned from ListOutput. var ExpectedGroupsSlice = []groups.Group{FirstGroup, SecondGroup} @@ -149,6 +191,19 @@ func HandleCreateGroupSuccessfully(t *testing.T) { }) } +// HandleUpdateGroupSuccessfully creates an HTTP handler at `/groups` on the +// test handler mux that tests group update. +func HandleUpdateGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/9fe1d3", 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) + }) +} + // HandleDeleteGroupSuccessfully creates an HTTP handler at `/groups` on the // test handler mux that tests group deletion. func HandleDeleteGroupSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go index 140b579798..e35c97214b 100644 --- a/openstack/identity/v3/groups/testing/requests_test.go +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -74,6 +74,23 @@ func TestCreateGroup(t *testing.T) { th.CheckDeepEquals(t, SecondGroup, *actual) } +func TestUpdateGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateGroupSuccessfully(t) + + updateOpts := groups.UpdateOpts{ + Description: "L2 Support Team", + Extra: map[string]interface{}{ + "email": "supportteam@example.com", + }, + } + + actual, err := groups.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondGroupUpdated, *actual) +} + func TestDeleteGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/groups/urls.go b/openstack/identity/v3/groups/urls.go index 5ad30d1f12..e7d1e53b27 100644 --- a/openstack/identity/v3/groups/urls.go +++ b/openstack/identity/v3/groups/urls.go @@ -14,6 +14,10 @@ func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("groups") } +func updateURL(client *gophercloud.ServiceClient, groupID string) string { + return client.ServiceURL("groups", groupID) +} + func deleteURL(client *gophercloud.ServiceClient, groupID string) string { return client.ServiceURL("groups", groupID) } From 11a2e42218dbd3393e4b926cc7d7041177b73531 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 12 Oct 2017 02:15:29 +0000 Subject: [PATCH 0090/2296] Removing obsolete acceptance build scripts --- .../keystonev2-lbaasv1.sh | 194 ---------------- .../keystonev3-lbaasv2.sh | 208 ------------------ 2 files changed, 402 deletions(-) delete mode 100644 script/acceptancetest_environments/keystonev2-lbaasv1.sh delete mode 100644 script/acceptancetest_environments/keystonev3-lbaasv2.sh diff --git a/script/acceptancetest_environments/keystonev2-lbaasv1.sh b/script/acceptancetest_environments/keystonev2-lbaasv1.sh deleted file mode 100644 index c74db62477..0000000000 --- a/script/acceptancetest_environments/keystonev2-lbaasv1.sh +++ /dev/null @@ -1,194 +0,0 @@ -#!/bin/bash -# -# This script is useful for creating a devstack environment to run gophercloud -# acceptance tests on. -# -# This can be considered a "legacy" devstack environment since it uses -# Keystone v2 and LBaaS v1. -# -# To run, simply execute this script within a virtual machine. -# -# The following OpenStack versions are installed: -# * OpenStack Mitaka -# * Keystone v2 -# * Glance v1 and v2 -# * Nova v2 and v2.1 -# * Cinder v1 and v2 -# * Trove v1 -# * Swift v1 -# * Neutron v2 -# * Neutron LBaaS v1.0 -# * Neutron FWaaS v2.0 -# * Manila v2 -# -# Go 1.6 is also installed. - -set -e - -cd -sudo apt-get update -sudo apt-get install -y git make mercurial - -sudo wget -O /usr/local/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme -sudo chmod +x /usr/local/bin/gimme -gimme 1.6 >> .bashrc - -mkdir ~/go -eval "$(/usr/local/bin/gimme 1.6)" -echo 'export GOPATH=$HOME/go' >> .bashrc -export GOPATH=$HOME/go -source .bashrc - -go get golang.org/x/crypto/ssh -go get github.com/gophercloud/gophercloud - -git clone https://git.openstack.org/openstack-dev/devstack -b stable/mitaka -cd devstack -cat >local.conf <> openrc -echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc -echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc -echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc -echo export OS_POOL_NAME="public" >> openrc -echo export OS_FLAVOR_ID=99 >> openrc -echo export OS_FLAVOR_ID_RESIZE=98 >> openrc - -# Manila share-network needs to be created -_IDTOVALUE="-F id -f value" -_NEUTRON_NET_ID=$(neutron net-list --name private $_IDTOVALUE) -_NEUTRON_IPV4_SUB=$(neutron subnet-list \ - --ip_version 4 \ - --network_id "$_NEUTRON_NET_ID" \ - $_IDTOVALUE) - -manila share-network-create \ - --neutron-net-id "$_NEUTRON_NET_ID" \ - --neutron-subnet-id "$_NEUTRON_IPV4_SUB" \ - --name "acc_share_nw" - -_SHARE_NETWORK=$(manila share-network-list \ - --neutron-net-id "$_NEUTRON_NET_ID" \ - --neutron-subnet-id "$_NEUTRON_IPV4_SUB" \ - --name "acc_share_nw" \ - | awk 'FNR == 4 {print $2}') - -echo export OS_SHARE_NETWORK_ID="$_SHARE_NETWORK" >> openrc -source openrc demo diff --git a/script/acceptancetest_environments/keystonev3-lbaasv2.sh b/script/acceptancetest_environments/keystonev3-lbaasv2.sh deleted file mode 100644 index 5cc9212ddc..0000000000 --- a/script/acceptancetest_environments/keystonev3-lbaasv2.sh +++ /dev/null @@ -1,208 +0,0 @@ -#!/bin/bash -# -# This script is useful for creating a devstack environment to run gophercloud -# acceptance tests on. -# -# To run, simply execute this script within a virtual machine. -# -# The following OpenStack versions are installed: -# * OpenStack Mitaka -# * Keystone v3 -# * Glance v1 and v2 -# * Nova v2 and v2.1 -# * Cinder v1 and v2 -# * Trove v1 -# * Swift v1 -# * Neutron v2 -# * Neutron LBaaS v2.0 -# * Neutron FWaaS v2.0 -# -# Go 1.6 is also installed. - -set -e - -cd -sudo apt-get update -sudo apt-get install -y git make mercurial - -sudo wget -O /usr/local/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme -sudo chmod +x /usr/local/bin/gimme -gimme 1.6 >> .bashrc - -mkdir ~/go -eval "$(/usr/local/bin/gimme 1.6)" -echo 'export GOPATH=$HOME/go' >> .bashrc -export GOPATH=$HOME/go - -export PATH=$PATH:$HOME/terraform:$HOME/go/bin -echo 'export PATH=$PATH:$HOME/terraform:$HOME/go/bin' >> .bashrc -source .bashrc - -go get golang.org/x/crypto/ssh -go get github.com/gophercloud/gophercloud - -git clone https://git.openstack.org/openstack-dev/devstack -b stable/mitaka -cd devstack -cat >local.conf <> openrc <> openrc -echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc -echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc -echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc -echo export OS_POOL_NAME="public" >> openrc -echo export OS_FLAVOR_ID=99 >> openrc -echo export OS_FLAVOR_ID_RESIZE=98 >> openrc -source openrc demo From f8b1f3b9098ad1058557e4b94b71fd8afc435886 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 12 Oct 2017 02:15:53 +0000 Subject: [PATCH 0091/2296] Updating acceptance README with details on building environments --- acceptance/README.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/acceptance/README.md b/acceptance/README.md index 94aad75981..891b245696 100644 --- a/acceptance/README.md +++ b/acceptance/README.md @@ -10,7 +10,26 @@ to a remote API. > be certain cases where this does not happen; always double-check to make sure > you have no stragglers left behind. -### Step 1. Set environment variables +### Step 1. Creating a Testing Environment + +Running tests on an existing OpenStack cloud can be risky. Malformed tests, +especially ones which require Admin privileges, can cause damage to the +environment. Additionally, you may incur bandwidth and service charges for +the resources used, as mentioned in the note above. + +Therefore, it is usually best to first practice running acceptance tests in +an isolated test environment. Two options to easily create a testing +environment are [DevStack](https://docs.openstack.org/devstack/latest/) +and [PackStack](https://www.rdoproject.org/install/packstack/). + +The following blog posts detail how to create reusable PackStack environments. +These posts were written with Gophercloud in mind: + +* http://terrarum.net/blog/building-openstack-environments.html +* http://terrarum.net/blog/building-openstack-environments-2.html +* http://terrarum.net/blog/building-openstack-environments-3.html + +### Step 2. Set environment variables A lot of tests rely on environment variables for configuration - so you will need to set them before running the suite. If you're testing against pure OpenStack APIs, @@ -51,7 +70,7 @@ to set them manually. |---|---| |`OS_SHARE_NETWORK_ID`| The share network ID to use when creating shares| -### 2. Run the test suite +### 3. Run the test suite From the root directory, run: @@ -79,7 +98,7 @@ $ gophercloudtest TestFlavors compute/v2 $ gophercloudtest Test compute/v2 ``` -### 3. Notes +### 4. Notes #### Compute Tests From f73efccdf790dd9accfa6ccb1f92cb8ebe3fea42 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 12 Oct 2017 23:00:08 -0600 Subject: [PATCH 0092/2296] Identity v3 domain list / get --- .../openstack/identity/v3/domains_test.go | 62 ++++++++++ openstack/identity/v3/domains/doc.go | 25 ++++ openstack/identity/v3/domains/requests.go | 48 ++++++++ openstack/identity/v3/domains/results.go | 79 +++++++++++++ .../identity/v3/domains/testing/fixtures.go | 107 ++++++++++++++++++ .../v3/domains/testing/requests_test.go | 52 +++++++++ openstack/identity/v3/domains/urls.go | 11 ++ 7 files changed, 384 insertions(+) create mode 100644 acceptance/openstack/identity/v3/domains_test.go create mode 100644 openstack/identity/v3/domains/doc.go create mode 100644 openstack/identity/v3/domains/requests.go create mode 100644 openstack/identity/v3/domains/results.go create mode 100644 openstack/identity/v3/domains/testing/fixtures.go create mode 100644 openstack/identity/v3/domains/testing/requests_test.go create mode 100644 openstack/identity/v3/domains/urls.go diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go new file mode 100644 index 0000000000..ed831dcd18 --- /dev/null +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -0,0 +1,62 @@ +// +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/domains" +) + +func TestDomainsList(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + var iTrue bool = true + listOpts := domains.ListOpts{ + Enabled: &iTrue, + } + + allPages, err := domains.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list domains: %v", err) + } + + allDomains, err := domains.ExtractDomains(allPages) + if err != nil { + t.Fatalf("Unable to extract domains: %v", err) + } + + for _, domain := range allDomains { + tools.PrintResource(t, domain) + } +} + +func TestDomainsGet(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + allPages, err := domains.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list domains: %v", err) + } + + allDomains, err := domains.ExtractDomains(allPages) + if err != nil { + t.Fatalf("Unable to extract domains: %v", err) + } + + domain := allDomains[0] + p, err := domains.Get(client, domain.ID).Extract() + if err != nil { + t.Fatalf("Unable to get domain: %v", err) + } + + tools.PrintResource(t, p) +} diff --git a/openstack/identity/v3/domains/doc.go b/openstack/identity/v3/domains/doc.go new file mode 100644 index 0000000000..6e0bae66a8 --- /dev/null +++ b/openstack/identity/v3/domains/doc.go @@ -0,0 +1,25 @@ +/* +Package domains manages and retrieves Domains in the OpenStack Identity Service. + +Example to List Domains + + var iTrue bool = true + listOpts := domains.ListOpts{ + Enabled: &iTrue, + } + + allPages, err := domains.List(identityClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allDomains, err := domains.ExtractDomains(allPages) + if err != nil { + panic(err) + } + + for _, domain := range allDomains { + fmt.Printf("%+v\n", domain) + } +*/ +package domains diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go new file mode 100644 index 0000000000..d04e354c0f --- /dev/null +++ b/openstack/identity/v3/domains/requests.go @@ -0,0 +1,48 @@ +package domains + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToDomainListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // Enabled filters the response by enabled domains. + Enabled *bool `q:"enabled"` + + // Name filters the response by domain name. + Name string `q:"name"` +} + +// ToDomainListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToDomainListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the domains to which the current token has access. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToDomainListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + 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) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/identity/v3/domains/results.go b/openstack/identity/v3/domains/results.go new file mode 100644 index 0000000000..8a477f8917 --- /dev/null +++ b/openstack/identity/v3/domains/results.go @@ -0,0 +1,79 @@ +package domains + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// A Domain is a collection of projects, users, and roles. +type Domain struct { + // Description is the description of the Domain. + Description string `json:"description"` + + // Enabled is whether or not the domain is enabled. + Enabled bool `json:"enabled"` + + // ID is the unique ID of the domain. + ID string `json:"id"` + + // Links contains referencing links to the domain. + Links map[string]interface{} `json:"links"` + + // Name is the name of the domain. + Name string `json:"name"` +} + +type domainResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Domain. +type GetResult struct { + domainResult +} + +// DomainPage is a single page of Domain results. +type DomainPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Domains contains any results. +func (r DomainPage) IsEmpty() (bool, error) { + domains, err := ExtractDomains(r) + return len(domains) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r DomainPage) 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 +} + +// ExtractDomains returns a slice of Domains contained in a single page of +// results. +func ExtractDomains(r pagination.Page) ([]Domain, error) { + var s struct { + Domains []Domain `json:"domains"` + } + err := (r.(DomainPage)).ExtractInto(&s) + return s.Domains, err +} + +// Extract interprets any domainResults as a Domain. +func (r domainResult) Extract() (*Domain, error) { + var s struct { + Domain *Domain `json:"domain"` + } + err := r.ExtractInto(&s) + return s.Domain, err +} diff --git a/openstack/identity/v3/domains/testing/fixtures.go b/openstack/identity/v3/domains/testing/fixtures.go new file mode 100644 index 0000000000..10e5bd30d4 --- /dev/null +++ b/openstack/identity/v3/domains/testing/fixtures.go @@ -0,0 +1,107 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of Domain results. +const ListOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/domains" + }, + "domains": [ + { + "enabled": true, + "id": "2844b2a08be147a08ef58317d6471f1f", + "links": { + "self": "http://example.com/identity/v3/domains/2844b2a08be147a08ef58317d6471f1f" + }, + "name": "domain one", + "description": "some description" + }, + { + "enabled": true, + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/domains/9fe1d3" + }, + "name": "domain two" + } + ] +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "domain": { + "enabled": true, + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/domains/9fe1d3" + }, + "name": "domain two" + } +} +` + +// FirstDomain is the first domain in the List request. +var FirstDomain = domains.Domain{ + Enabled: true, + ID: "2844b2a08be147a08ef58317d6471f1f", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/domains/2844b2a08be147a08ef58317d6471f1f", + }, + Name: "domain one", + Description: "some description", +} + +// SecondDomain is the second domain in the List request. +var SecondDomain = domains.Domain{ + Enabled: true, + ID: "9fe1d3", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/domains/9fe1d3", + }, + Name: "domain two", +} + +// ExpectedDomainsSlice is the slice of domains expected to be returned from ListOutput. +var ExpectedDomainsSlice = []domains.Domain{FirstDomain, SecondDomain} + +// 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) { + th.Mux.HandleFunc("/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, ListOutput) + }) +} + +// HandleGetDomainSuccessfully creates an HTTP handler at `/domains` on the +// test handler mux that responds with a single domain. +func HandleGetDomainSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/domains/9fe1d3", 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/domains/testing/requests_test.go b/openstack/identity/v3/domains/testing/requests_test.go new file mode 100644 index 0000000000..fd71596093 --- /dev/null +++ b/openstack/identity/v3/domains/testing/requests_test.go @@ -0,0 +1,52 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListDomains(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListDomainsSuccessfully(t) + + count := 0 + err := domains.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := domains.ExtractDomains(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedDomainsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListDomainsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListDomainsSuccessfully(t) + + allPages, err := domains.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := domains.ExtractDomains(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedDomainsSlice, actual) +} + +func TestGetDomain(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetDomainSuccessfully(t) + + actual, err := domains.Get(client.ServiceClient(), "9fe1d3").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondDomain, *actual) +} diff --git a/openstack/identity/v3/domains/urls.go b/openstack/identity/v3/domains/urls.go new file mode 100644 index 0000000000..95d70f88b8 --- /dev/null +++ b/openstack/identity/v3/domains/urls.go @@ -0,0 +1,11 @@ +package domains + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("domains") +} + +func getURL(client *gophercloud.ServiceClient, domainID string) string { + return client.ServiceURL("domains", domainID) +} From d4f5b72bc0a420c55b683592239d40a53388ff55 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Fri, 13 Oct 2017 10:19:38 -0600 Subject: [PATCH 0093/2296] Identity v3: domain create --- .../openstack/identity/v3/domains_test.go | 20 +++++++++++ acceptance/openstack/identity/v3/identity.go | 28 +++++++++++++++ openstack/identity/v3/domains/doc.go | 12 +++++++ openstack/identity/v3/domains/requests.go | 36 +++++++++++++++++++ openstack/identity/v3/domains/results.go | 6 ++++ .../identity/v3/domains/testing/fixtures.go | 22 ++++++++++++ .../v3/domains/testing/requests_test.go | 14 ++++++++ openstack/identity/v3/domains/urls.go | 4 +++ 8 files changed, 142 insertions(+) diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go index ed831dcd18..075b2e3c14 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -60,3 +60,23 @@ func TestDomainsGet(t *testing.T) { tools.PrintResource(t, p) } + +func TestDomainCRUD(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + var iFalse bool = false + createOpts := domains.CreateOpts{ + Description: "Testing Domain", + Enabled: &iFalse, + } + + domain, err := CreateDomain(t, client, &createOpts) + if err != nil { + t.Fatalf("Unable to create domain: %v", err) + } + + tools.PrintResource(t, domain) +} diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index bd861cd8dd..9ae1d482e2 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" @@ -91,6 +92,33 @@ func CreateGroup(t *testing.T, client *gophercloud.ServiceClient, c *groups.Crea return group, nil } +// CreateDomain will create a domain with a random name. +// It takes an optional createOpts parameter since creating a domain +// has many options. An error will be returned if the domain was +// unable to be created. +func CreateDomain(t *testing.T, client *gophercloud.ServiceClient, c *domains.CreateOpts) (*domains.Domain, error) { + name := tools.RandomString("ACPTTEST", 8) + t.Logf("Attempting to create domain: %s", name) + + var createOpts domains.CreateOpts + if c != nil { + createOpts = *c + } else { + createOpts = domains.CreateOpts{} + } + + createOpts.Name = name + + domain, err := domains.Create(client, createOpts).Extract() + if err != nil { + return domain, err + } + + t.Logf("Successfully created domain %s with ID %s", name, domain.ID) + + return domain, nil +} + // DeleteProject will delete a project by ID. A fatal error will occur if // the project ID failed to be deleted. This works best when using it as // a deferred function. diff --git a/openstack/identity/v3/domains/doc.go b/openstack/identity/v3/domains/doc.go index 6e0bae66a8..e40c725ef1 100644 --- a/openstack/identity/v3/domains/doc.go +++ b/openstack/identity/v3/domains/doc.go @@ -21,5 +21,17 @@ Example to List Domains for _, domain := range allDomains { fmt.Printf("%+v\n", domain) } + +Example to Create a Domain + + createOpts := domains.CreateOpts{ + Name: "domain name", + Description: "Test domain", + } + + domain, err := domains.Create(identityClient, createOpts).Extract() + if err != nil { + panic(err) + } */ package domains diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index d04e354c0f..6b91763f8b 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -46,3 +46,39 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToDomainCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a domain. +type CreateOpts struct { + // Name is the name of the new domain. + Name string `json:"name" required:"true"` + + // Description is a description of the domain. + Description string `json:"description,omitempty"` + + // Enabled sets the domain status to enabled or disabled. + Enabled *bool `json:"enabled,omitempty"` +} + +// ToDomainCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToDomainCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "domain") +} + +// Create creates a new Domain. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToDomainCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/identity/v3/domains/results.go b/openstack/identity/v3/domains/results.go index 8a477f8917..82e765e0fd 100644 --- a/openstack/identity/v3/domains/results.go +++ b/openstack/identity/v3/domains/results.go @@ -33,6 +33,12 @@ type GetResult struct { domainResult } +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Domain. +type CreateResult struct { + domainResult +} + // DomainPage is a single page of Domain results. type DomainPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/domains/testing/fixtures.go b/openstack/identity/v3/domains/testing/fixtures.go index 10e5bd30d4..df75fd464b 100644 --- a/openstack/identity/v3/domains/testing/fixtures.go +++ b/openstack/identity/v3/domains/testing/fixtures.go @@ -54,6 +54,15 @@ const GetOutput = ` } ` +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "domain": { + "name": "domain two" + } +} +` + // FirstDomain is the first domain in the List request. var FirstDomain = domains.Domain{ Enabled: true, @@ -105,3 +114,16 @@ func HandleGetDomainSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleCreateDomainSuccessfully creates an HTTP handler at `/domains` on the +// test handler mux that tests domain creation. +func HandleCreateDomainSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/domains", 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, GetOutput) + }) +} diff --git a/openstack/identity/v3/domains/testing/requests_test.go b/openstack/identity/v3/domains/testing/requests_test.go index fd71596093..0e64fe6a01 100644 --- a/openstack/identity/v3/domains/testing/requests_test.go +++ b/openstack/identity/v3/domains/testing/requests_test.go @@ -50,3 +50,17 @@ func TestGetDomain(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomain, *actual) } + +func TestCreateDomain(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateDomainSuccessfully(t) + + createOpts := domains.CreateOpts{ + Name: "domain two", + } + + actual, err := domains.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondDomain, *actual) +} diff --git a/openstack/identity/v3/domains/urls.go b/openstack/identity/v3/domains/urls.go index 95d70f88b8..986d570971 100644 --- a/openstack/identity/v3/domains/urls.go +++ b/openstack/identity/v3/domains/urls.go @@ -9,3 +9,7 @@ func listURL(client *gophercloud.ServiceClient) string { func getURL(client *gophercloud.ServiceClient, domainID string) string { return client.ServiceURL("domains", domainID) } + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("domains") +} From 63d51cb644075fbfe9dab5e49dced7762520485b Mon Sep 17 00:00:00 2001 From: David Lyle Date: Fri, 13 Oct 2017 15:28:10 -0600 Subject: [PATCH 0094/2296] Identity v3 domain delete --- acceptance/openstack/identity/v3/domains_test.go | 1 + acceptance/openstack/identity/v3/identity.go | 12 ++++++++++++ openstack/identity/v3/domains/doc.go | 7 +++++++ openstack/identity/v3/domains/requests.go | 6 ++++++ openstack/identity/v3/domains/results.go | 6 ++++++ openstack/identity/v3/domains/testing/fixtures.go | 11 +++++++++++ .../identity/v3/domains/testing/requests_test.go | 9 +++++++++ openstack/identity/v3/domains/urls.go | 4 ++++ 8 files changed, 56 insertions(+) diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go index 075b2e3c14..c86796846d 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -77,6 +77,7 @@ func TestDomainCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create domain: %v", err) } + defer DeleteDomain(t, client, domain.ID) tools.PrintResource(t, domain) } diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 9ae1d482e2..82c15f95c5 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -154,3 +154,15 @@ func DeleteGroup(t *testing.T, client *gophercloud.ServiceClient, groupID string t.Logf("Deleted group: %s", groupID) } + +// DeleteDomain will delete a domain by ID. A fatal error will occur if +// the project ID failed to be deleted. This works best when using it as +// a deferred function. +func DeleteDomain(t *testing.T, client *gophercloud.ServiceClient, domainID string) { + err := domains.Delete(client, domainID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete domain %s: %v", domainID, err) + } + + t.Logf("Deleted domain: %s", domainID) +} diff --git a/openstack/identity/v3/domains/doc.go b/openstack/identity/v3/domains/doc.go index e40c725ef1..d78261b5de 100644 --- a/openstack/identity/v3/domains/doc.go +++ b/openstack/identity/v3/domains/doc.go @@ -33,5 +33,12 @@ Example to Create a Domain if err != nil { panic(err) } + +Example to Delete a Domain + + domainID := "0fe36e73809d46aeae6705c39077b1b3" + err := domains.Delete(identityClient, domainID).ExtractErr() + if err != nil { + panic(err) */ package domains diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index 6b91763f8b..f9427ce3fa 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -82,3 +82,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// Delete deletes a domain. +func Delete(client *gophercloud.ServiceClient, domainID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, domainID), nil) + return +} diff --git a/openstack/identity/v3/domains/results.go b/openstack/identity/v3/domains/results.go index 82e765e0fd..e29c9c2840 100644 --- a/openstack/identity/v3/domains/results.go +++ b/openstack/identity/v3/domains/results.go @@ -39,6 +39,12 @@ type CreateResult struct { domainResult } +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // DomainPage is a single page of Domain results. type DomainPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/domains/testing/fixtures.go b/openstack/identity/v3/domains/testing/fixtures.go index df75fd464b..9acd5a1913 100644 --- a/openstack/identity/v3/domains/testing/fixtures.go +++ b/openstack/identity/v3/domains/testing/fixtures.go @@ -127,3 +127,14 @@ func HandleCreateDomainSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleDeleteDomainSuccessfully creates an HTTP handler at `/domains` on the +// test handler mux that tests domain deletion. +func HandleDeleteDomainSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/domains/9fe1d3", 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/domains/testing/requests_test.go b/openstack/identity/v3/domains/testing/requests_test.go index 0e64fe6a01..68c512efc2 100644 --- a/openstack/identity/v3/domains/testing/requests_test.go +++ b/openstack/identity/v3/domains/testing/requests_test.go @@ -64,3 +64,12 @@ func TestCreateDomain(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomain, *actual) } + +func TestDeleteDomain(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteDomainSuccessfully(t) + + res := domains.Delete(client.ServiceClient(), "9fe1d3") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/domains/urls.go b/openstack/identity/v3/domains/urls.go index 986d570971..45296193a7 100644 --- a/openstack/identity/v3/domains/urls.go +++ b/openstack/identity/v3/domains/urls.go @@ -13,3 +13,7 @@ func getURL(client *gophercloud.ServiceClient, domainID string) string { func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("domains") } + +func deleteURL(client *gophercloud.ServiceClient, domainID string) string { + return client.ServiceURL("domains", domainID) +} From c66acc5998b1b3abd0ce32b6dc0bde3a256695df Mon Sep 17 00:00:00 2001 From: David Lyle Date: Fri, 13 Oct 2017 16:16:23 -0600 Subject: [PATCH 0095/2296] Identity v3: domain update --- .../openstack/identity/v3/domains_test.go | 19 ++++++-- openstack/identity/v3/domains/doc.go | 15 ++++++ openstack/identity/v3/domains/requests.go | 36 ++++++++++++++ openstack/identity/v3/domains/results.go | 6 +++ .../identity/v3/domains/testing/fixtures.go | 48 +++++++++++++++++++ .../v3/domains/testing/requests_test.go | 14 ++++++ openstack/identity/v3/domains/urls.go | 4 ++ 7 files changed, 139 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go index c86796846d..b340bed4bd 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -61,16 +61,16 @@ func TestDomainsGet(t *testing.T) { tools.PrintResource(t, p) } -func TestDomainCRUD(t *testing.T) { +func TestDomainsCRUD(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { t.Fatalf("Unable to obtain an identity client: %v", err) } - var iFalse bool = false + var iTrue bool = true createOpts := domains.CreateOpts{ Description: "Testing Domain", - Enabled: &iFalse, + Enabled: &iTrue, } domain, err := CreateDomain(t, client, &createOpts) @@ -80,4 +80,17 @@ func TestDomainCRUD(t *testing.T) { defer DeleteDomain(t, client, domain.ID) tools.PrintResource(t, domain) + + var iFalse bool = false + updateOpts := domains.UpdateOpts{ + Description: "Staging Test Domain", + Enabled: &iFalse, + } + + newDomain, err := domains.Update(client, domain.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update domain: %v", err) + } + + tools.PrintResource(t, newDomain) } diff --git a/openstack/identity/v3/domains/doc.go b/openstack/identity/v3/domains/doc.go index d78261b5de..720db782f7 100644 --- a/openstack/identity/v3/domains/doc.go +++ b/openstack/identity/v3/domains/doc.go @@ -34,11 +34,26 @@ Example to Create a Domain panic(err) } +Example to Update a Domain + + domainID := "0fe36e73809d46aeae6705c39077b1b3" + + var iFalse bool = false + updateOpts := domains.UpdateOpts{ + Enabled: &iFalse, + } + + domain, err := domains.Update(identityClient, domainID, updateOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a Domain domainID := "0fe36e73809d46aeae6705c39077b1b3" err := domains.Delete(identityClient, domainID).ExtractErr() if err != nil { panic(err) + } */ package domains diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index f9427ce3fa..14fbd27eb3 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -88,3 +88,39 @@ func Delete(client *gophercloud.ServiceClient, domainID string) (r DeleteResult) _, r.Err = client.Delete(deleteURL(client, domainID), nil) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToDomainUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents parameters to update a domain. +type UpdateOpts struct { + // Name is the name of the domain. + Name string `json:"name,omitempty"` + + // Description is the description of the domain. + Description string `json:"description,omitempty"` + + // Enabled sets the domain status to enabled or disabled. + Enabled *bool `json:"enabled,omitempty"` +} + +// ToUpdateCreateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToDomainUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "domain") +} + +// Update modifies the attributes of a domain. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToDomainUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/identity/v3/domains/results.go b/openstack/identity/v3/domains/results.go index e29c9c2840..5b8e2662b2 100644 --- a/openstack/identity/v3/domains/results.go +++ b/openstack/identity/v3/domains/results.go @@ -45,6 +45,12 @@ type DeleteResult struct { gophercloud.ErrResult } +// UpdateResult is the result of an Update request. Call its Extract method to +// interpret it as a Domain. +type UpdateResult struct { + domainResult +} + // DomainPage is a single page of Domain results. type DomainPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/domains/testing/fixtures.go b/openstack/identity/v3/domains/testing/fixtures.go index 9acd5a1913..87ac561b5a 100644 --- a/openstack/identity/v3/domains/testing/fixtures.go +++ b/openstack/identity/v3/domains/testing/fixtures.go @@ -63,6 +63,30 @@ const CreateRequest = ` } ` +// UpdateRequest provides the input to as Update request. +const UpdateRequest = ` +{ + "domain": { + "description": "Staging Domain" + } +} +` + +// UpdateOutput provides an update result. +const UpdateOutput = ` +{ + "domain": { + "enabled": true, + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/domains/9fe1d3" + }, + "name": "domain two", + "description": "Staging Domain" + } +} +` + // FirstDomain is the first domain in the List request. var FirstDomain = domains.Domain{ Enabled: true, @@ -84,6 +108,17 @@ var SecondDomain = domains.Domain{ Name: "domain two", } +// SecondDomainUpdated is how SecondDomain should look after an Update. +var SecondDomainUpdated = domains.Domain{ + Enabled: true, + ID: "9fe1d3", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/domains/9fe1d3", + }, + Name: "domain two", + Description: "Staging Domain", +} + // ExpectedDomainsSlice is the slice of domains expected to be returned from ListOutput. var ExpectedDomainsSlice = []domains.Domain{FirstDomain, SecondDomain} @@ -138,3 +173,16 @@ func HandleDeleteDomainSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// HandleUpdateDomainSuccessfully creates an HTTP handler at `/domains` on the +// test handler mux that tests domain update. +func HandleUpdateDomainSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/domains/9fe1d3", 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/domains/testing/requests_test.go b/openstack/identity/v3/domains/testing/requests_test.go index 68c512efc2..73ac97aa73 100644 --- a/openstack/identity/v3/domains/testing/requests_test.go +++ b/openstack/identity/v3/domains/testing/requests_test.go @@ -73,3 +73,17 @@ func TestDeleteDomain(t *testing.T) { res := domains.Delete(client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } + +func TestUpdateDomain(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateDomainSuccessfully(t) + + updateOpts := domains.UpdateOpts{ + Description: "Staging Domain", + } + + actual, err := domains.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondDomainUpdated, *actual) +} diff --git a/openstack/identity/v3/domains/urls.go b/openstack/identity/v3/domains/urls.go index 45296193a7..b0c21b80be 100644 --- a/openstack/identity/v3/domains/urls.go +++ b/openstack/identity/v3/domains/urls.go @@ -17,3 +17,7 @@ func createURL(client *gophercloud.ServiceClient) string { func deleteURL(client *gophercloud.ServiceClient, domainID string) string { return client.ServiceURL("domains", domainID) } + +func updateURL(client *gophercloud.ServiceClient, domainID string) string { + return client.ServiceURL("domains", domainID) +} From 0f540b4d0e72ccb33ad9e5200c67bf425954ee5a Mon Sep 17 00:00:00 2001 From: David Lyle Date: Tue, 10 Oct 2017 21:48:41 -0600 Subject: [PATCH 0096/2296] fix user delete result comment --- openstack/identity/v3/users/results.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 5716edc304..c474e882b9 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -98,8 +98,8 @@ type UpdateResult struct { userResult } -// DeleteResult is the response from a Delete operation. Call its ExtractErr -// method to interpret it as a User. +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } From fa2dadce783eac95ca589fa43142f71b6543246f Mon Sep 17 00:00:00 2001 From: Phionah Bugosi Date: Sun, 15 Oct 2017 00:49:09 +0300 Subject: [PATCH 0097/2296] Compute v2: Add Evacuate Action (#532) * Compute v2: Add Evacuate Action * Compute V2: Add evacuate Action * Add evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action * Compute v2: Add Evacuate Action --- .../compute/v2/extensions/evacuate/doc.go | 13 +++ .../v2/extensions/evacuate/requests.go | 41 +++++++++ .../compute/v2/extensions/evacuate/results.go | 23 +++++ .../v2/extensions/evacuate/testing/doc.go | 2 + .../extensions/evacuate/testing/fixtures.go | 83 +++++++++++++++++++ .../evacuate/testing/requests_test.go | 60 ++++++++++++++ .../compute/v2/extensions/evacuate/urls.go | 9 ++ 7 files changed, 231 insertions(+) create mode 100644 openstack/compute/v2/extensions/evacuate/doc.go create mode 100644 openstack/compute/v2/extensions/evacuate/requests.go create mode 100644 openstack/compute/v2/extensions/evacuate/results.go create mode 100644 openstack/compute/v2/extensions/evacuate/testing/doc.go create mode 100644 openstack/compute/v2/extensions/evacuate/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/evacuate/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/evacuate/urls.go diff --git a/openstack/compute/v2/extensions/evacuate/doc.go b/openstack/compute/v2/extensions/evacuate/doc.go new file mode 100644 index 0000000000..faafe7c313 --- /dev/null +++ b/openstack/compute/v2/extensions/evacuate/doc.go @@ -0,0 +1,13 @@ +/* +Package evacuate provides functionality to evacuates servers that have been +provisioned by the OpenStack Compute service from a failed host to a new host. + +Example to Evacuate a Server from a Host + + serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" + err := evacuate.Evacuate(computeClient, serverID, evacuate.EvacuateOpts{}).ExtractErr() + if err != nil { + panic(err) + } +*/ +package evacuate diff --git a/openstack/compute/v2/extensions/evacuate/requests.go b/openstack/compute/v2/extensions/evacuate/requests.go new file mode 100644 index 0000000000..3ea7af4642 --- /dev/null +++ b/openstack/compute/v2/extensions/evacuate/requests.go @@ -0,0 +1,41 @@ +package evacuate + +import ( + "github.com/gophercloud/gophercloud" +) + +// EvacuateOptsBuilder allows extensions to add additional parameters to the +// the Evacuate request. +type EvacuateOptsBuilder interface { + ToEvacuateMap() (map[string]interface{}, error) +} + +// EvacuateOpts specifies Evacuate action parameters. +type EvacuateOpts struct { + // The name of the host to which the server is evacuated + Host string `json:"host,omitempty"` + + // Indicates whether server is on shared storage + OnSharedStorage bool `json:"onSharedStorage"` + + // An administrative password to access the evacuated server + AdminPass string `json:"adminPass,omitempty"` +} + +// ToServerGroupCreateMap constructs a request body from CreateOpts. +func (opts EvacuateOpts) ToEvacuateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "evacuate") +} + +// Evacuate will Evacuate a failed instance to another host. +func Evacuate(client *gophercloud.ServiceClient, id string, opts EvacuateOptsBuilder) (r EvacuateResult) { + b, err := opts.ToEvacuateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/evacuate/results.go b/openstack/compute/v2/extensions/evacuate/results.go new file mode 100644 index 0000000000..8342cb43d0 --- /dev/null +++ b/openstack/compute/v2/extensions/evacuate/results.go @@ -0,0 +1,23 @@ +package evacuate + +import ( + "github.com/gophercloud/gophercloud" +) + +// EvacuateResult is the response from an Evacuate operation. +//Call its ExtractAdminPass method to retrieve the admin password of the instance. +//The admin password will be an empty string if the cloud is not configured to inject admin passwords.. +type EvacuateResult struct { + gophercloud.Result +} + +func (r EvacuateResult) ExtractAdminPass() (string, error) { + var s struct { + AdminPass string `json:"adminPass"` + } + err := r.ExtractInto(&s) + if err != nil && err.Error() == "EOF" { + return "", nil + } + return s.AdminPass, err +} diff --git a/openstack/compute/v2/extensions/evacuate/testing/doc.go b/openstack/compute/v2/extensions/evacuate/testing/doc.go new file mode 100644 index 0000000000..613ac1d4b6 --- /dev/null +++ b/openstack/compute/v2/extensions/evacuate/testing/doc.go @@ -0,0 +1,2 @@ +// compute_extensions_evacuate_v2 +package testing diff --git a/openstack/compute/v2/extensions/evacuate/testing/fixtures.go b/openstack/compute/v2/extensions/evacuate/testing/fixtures.go new file mode 100644 index 0000000000..e078d1019d --- /dev/null +++ b/openstack/compute/v2/extensions/evacuate/testing/fixtures.go @@ -0,0 +1,83 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func mockEvacuateResponse(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, ` + { + "evacuate": { + "adminPass": "MySecretPass", + "host": "derp", + "onSharedStorage": false + } + + } + `) + w.WriteHeader(http.StatusOK) + }) +} + +func mockEvacuateResponseWithHost(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, ` + { + "evacuate": { + "host": "derp", + "onSharedStorage": false + } + + } + `) + w.WriteHeader(http.StatusOK) + }) +} + +func mockEvacuateResponseWithNoOpts(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, ` + { + "evacuate": { + "onSharedStorage": false + } + + } + `) + w.WriteHeader(http.StatusOK) + }) +} + +const EvacuateResponse = ` +{ + "adminPass": "MySecretPass" +} +` + +func mockEvacuateAdminpassResponse(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, ` + { + "evacuate": { + "onSharedStorage": false + } + } + `) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, EvacuateResponse) + }) +} diff --git a/openstack/compute/v2/extensions/evacuate/testing/requests_test.go b/openstack/compute/v2/extensions/evacuate/testing/requests_test.go new file mode 100644 index 0000000000..aec03114b8 --- /dev/null +++ b/openstack/compute/v2/extensions/evacuate/testing/requests_test.go @@ -0,0 +1,60 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/evacuate" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestEvacuate(t *testing.T) { + const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" + th.SetupHTTP() + defer th.TeardownHTTP() + + mockEvacuateResponse(t, serverID) + + _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{ + Host: "derp", + AdminPass: "MySecretPass", + OnSharedStorage: false, + }).ExtractAdminPass() + th.AssertNoErr(t, err) +} + +func TestEvacuateWithHost(t *testing.T) { + const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" + th.SetupHTTP() + defer th.TeardownHTTP() + + mockEvacuateResponseWithHost(t, serverID) + + _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{ + Host: "derp", + }).ExtractAdminPass() + th.AssertNoErr(t, err) +} + +func TestEvacuateWithNoOpts(t *testing.T) { + const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" + th.SetupHTTP() + defer th.TeardownHTTP() + + mockEvacuateResponseWithNoOpts(t, serverID) + + _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() + th.AssertNoErr(t, err) +} + +func TestEvacuateAdminpassResponse(t *testing.T) { + const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" + th.SetupHTTP() + defer th.TeardownHTTP() + + mockEvacuateAdminpassResponse(t, serverID) + + actual, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() + th.CheckEquals(t, "MySecretPass", actual) + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/evacuate/urls.go b/openstack/compute/v2/extensions/evacuate/urls.go new file mode 100644 index 0000000000..a8896aea0b --- /dev/null +++ b/openstack/compute/v2/extensions/evacuate/urls.go @@ -0,0 +1,9 @@ +package evacuate + +import ( + "github.com/gophercloud/gophercloud" +) + +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "action") +} From 7945c5c32a86fe0c977bb7428ad8f243c3945ad5 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Wed, 11 Oct 2017 07:57:19 -0600 Subject: [PATCH 0098/2296] Identity v3 role list / get --- .../openstack/identity/v3/roles_test.go | 61 ++++++++++ openstack/identity/v3/roles/doc.go | 20 ++++ openstack/identity/v3/roles/requests.go | 43 +++++++ openstack/identity/v3/roles/results.go | 87 +++++++++++++-- .../identity/v3/roles/testing/fixtures.go | 105 ++++++++++++++++++ .../v3/roles/testing/requests_test.go | 61 ++++++++-- openstack/identity/v3/roles/urls.go | 8 ++ 7 files changed, 369 insertions(+), 16 deletions(-) create mode 100644 acceptance/openstack/identity/v3/roles_test.go create mode 100644 openstack/identity/v3/roles/testing/fixtures.go diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go new file mode 100644 index 0000000000..58b6a155d9 --- /dev/null +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -0,0 +1,61 @@ +// +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/roles" +) + +func TestRolesList(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + listOpts := roles.ListOpts{ + DomainID: "default", + } + + allPages, err := roles.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list roles: %v", err) + } + + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + t.Fatalf("Unable to extract roles: %v", err) + } + + for _, role := range allRoles { + tools.PrintResource(t, role) + } +} + +func TestRolesGet(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + allPages, err := roles.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list roles: %v", err) + } + + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + t.Fatalf("Unable to extract roles: %v", err) + } + + role := allRoles[0] + p, err := roles.Get(client, role.ID).Extract() + if err != nil { + t.Fatalf("Unable to get role: %v", err) + } + + tools.PrintResource(t, p) +} diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 59377a4eb1..0e908a80be 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -2,6 +2,26 @@ Package roles provides information and interaction with the roles API resource for the OpenStack Identity service. +Example to List Roles + + listOpts := roles.ListOpts{ + DomainID: "default", + } + + allPages, err := roles.List(identityClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + panic(err) + } + + for _, role := range allRoles { + fmt.Printf("%+v\n", role) + } + Example to List Role Assignments listOpts := roles.ListAssignmentsOpts{ diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index ea8f207129..c1a6020ceb 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -5,6 +5,49 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToRoleListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // DomainID filters the response by a domain ID. + DomainID string `q:"domain_id"` + + // Name filters the response by role name. + Name string `q:"name"` +} + +// ToRoleListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToRoleListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the roles to which the current token has access. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToRoleListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return RolePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details on a single role, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + // ListAssignmentsOptsBuilder allows extensions to add additional parameters to // the ListAssignments request. type ListAssignmentsOptsBuilder interface { diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index a7b43f44a4..5b89f78fc2 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -1,17 +1,90 @@ package roles -import "github.com/gophercloud/gophercloud/pagination" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Role grants permissions to a user. +type Role struct { + // DomainID is the domain ID the role belongs to. + DomainID string `json:"domain_id"` + + // ID is the unique ID of the role. + ID string `json:"id"` + + // Links contains referencing links to the role. + Links map[string]interface{} `json:"links"` + + // Name is the role name + Name string `json:"name"` +} + +type roleResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Role. +type GetResult struct { + roleResult +} + +// RolePage is a single page of Role results. +type RolePage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Roles contains any results. +func (r RolePage) IsEmpty() (bool, error) { + roles, err := ExtractRoles(r) + return len(roles) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r RolePage) 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 +} + +// ExtractProjects returns a slice of Roles contained in a single page of +// results. +func ExtractRoles(r pagination.Page) ([]Role, error) { + var s struct { + Roles []Role `json:"roles"` + } + err := (r.(RolePage)).ExtractInto(&s) + return s.Roles, err +} + +// Extract interprets any roleResults as a Role. +func (r roleResult) Extract() (*Role, error) { + var s struct { + Role *Role `json:"role"` + } + err := r.ExtractInto(&s) + return s.Role, err +} // RoleAssignment is the result of a role assignments query. type RoleAssignment struct { - Role Role `json:"role,omitempty"` - Scope Scope `json:"scope,omitempty"` - User User `json:"user,omitempty"` - Group Group `json:"group,omitempty"` + Role AssignedRole `json:"role,omitempty"` + Scope Scope `json:"scope,omitempty"` + User User `json:"user,omitempty"` + Group Group `json:"group,omitempty"` } -// Role represents a Role in an assignment. -type Role struct { +// AssignedRole represents a Role in an assignment. +type AssignedRole struct { ID string `json:"id,omitempty"` } diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go new file mode 100644 index 0000000000..86681c86ee --- /dev/null +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -0,0 +1,105 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of Role results. +const ListOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/roles" + }, + "roles": [ + { + "domain_id": "default", + "id": "2844b2a08be147a08ef58317d6471f1f", + "links": { + "self": "http://example.com/identity/v3/roles/2844b2a08be147a08ef58317d6471f1f" + }, + "name": "admin-read-only" + }, + { + "domain_id": "1789d1", + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/roles/9fe1d3" + }, + "name": "support" + } + ] +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "role": { + "domain_id": "1789d1", + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/roles/9fe1d3" + }, + "name": "support" + } +} +` + +// FirstRole is the first role in the List request. +var FirstRole = roles.Role{ + DomainID: "default", + ID: "2844b2a08be147a08ef58317d6471f1f", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/2844b2a08be147a08ef58317d6471f1f", + }, + Name: "admin-read-only", +} + +// SecondRole is the second role in the List request. +var SecondRole = roles.Role{ + DomainID: "1789d1", + ID: "9fe1d3", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/roles/9fe1d3", + }, + Name: "support", +} + +// ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. +var ExpectedRolesSlice = []roles.Role{FirstRole, SecondRole} + +// HandleListRolesSuccessfully creates an HTTP handler at `/roles` on the +// test handler mux that responds with a list of two roles. +func HandleListRolesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/roles", 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) + }) +} + +// HandleGetRoleSuccessfully creates an HTTP handler at `/roles` on the +// test handler mux that responds with a single role. +func HandleGetRoleSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/roles/9fe1d3", 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/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index dd9b704d8d..2aeba337e9 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -8,17 +8,60 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) -func TestListSinglePage(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() +func TestListRoles(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRolesSuccessfully(t) - testhelper.Mux.HandleFunc("/role_assignments", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + count := 0 + err := roles.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := roles.ExtractRoles(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedRolesSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListRolesAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRolesSuccessfully(t) + + allPages, err := roles.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRolesSlice, actual) +} + +func TestGetRole(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetRoleSuccessfully(t) + + actual, err := roles.Get(client.ServiceClient(), "9fe1d3").Extract() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondRole, *actual) +} + +func TestListAssignmentsSinglePage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/role_assignments", 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, ` @@ -77,13 +120,13 @@ func TestListSinglePage(t *testing.T) { expected := []roles.RoleAssignment{ { - Role: roles.Role{ID: "123456"}, + Role: roles.AssignedRole{ID: "123456"}, Scope: roles.Scope{Domain: roles.Domain{ID: "161718"}}, User: roles.User{ID: "313233"}, Group: roles.Group{}, }, { - Role: roles.Role{ID: "123456"}, + Role: roles.AssignedRole{ID: "123456"}, Scope: roles.Scope{Project: roles.Project{ID: "456789"}}, User: roles.User{ID: "313233"}, Group: roles.Group{}, diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index 8d87b6e7d4..ab32d850e1 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -2,6 +2,14 @@ package roles import "github.com/gophercloud/gophercloud" +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("roles") +} + +func getURL(client *gophercloud.ServiceClient, roleID string) string { + return client.ServiceURL("roles", roleID) +} + func listAssignmentsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("role_assignments") } From 443743e88335413103dcf1997e46d401b264fbcd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 16 Oct 2017 19:47:15 -0600 Subject: [PATCH 0099/2296] Acc tests db (#563) DB v1: Refactor Acceptance Tests --- acceptance/README.md | 7 + acceptance/clients/clients.go | 45 +++++- acceptance/openstack/db/v1/common.go | 70 --------- acceptance/openstack/db/v1/database_test.go | 45 ------ acceptance/openstack/db/v1/databases_test.go | 55 +++++++ acceptance/openstack/db/v1/db.go | 145 +++++++++++++++++++ acceptance/openstack/db/v1/flavor_test.go | 31 ---- acceptance/openstack/db/v1/flavors_test.go | 58 ++++++++ acceptance/openstack/db/v1/instance_test.go | 138 ------------------ acceptance/openstack/db/v1/instances_test.go | 71 +++++++++ acceptance/openstack/db/v1/user_test.go | 70 --------- acceptance/openstack/db/v1/users_test.go | 54 +++++++ 12 files changed, 427 insertions(+), 362 deletions(-) delete mode 100644 acceptance/openstack/db/v1/common.go delete mode 100644 acceptance/openstack/db/v1/database_test.go create mode 100644 acceptance/openstack/db/v1/databases_test.go create mode 100644 acceptance/openstack/db/v1/db.go delete mode 100644 acceptance/openstack/db/v1/flavor_test.go create mode 100644 acceptance/openstack/db/v1/flavors_test.go delete mode 100644 acceptance/openstack/db/v1/instance_test.go create mode 100644 acceptance/openstack/db/v1/instances_test.go delete mode 100644 acceptance/openstack/db/v1/user_test.go create mode 100644 acceptance/openstack/db/v1/users_test.go diff --git a/acceptance/README.md b/acceptance/README.md index 891b245696..ab35695748 100644 --- a/acceptance/README.md +++ b/acceptance/README.md @@ -65,6 +65,13 @@ to set them manually. |`OS_NETWORK_NAME`|The internal/private network to launch instances on| |`OS_EXTGW_ID`|The external/public network| +#### Database + +|Name|Description| +|---|---| +|`OS_DB_DATASTORE_TYPE`|The Datastore type to use. Example: `mariadb`| +|`OS_DB_DATASTORE_VERSION`|The Datastore version to use. Example: `mariadb-10`| + #### Shared file systems |Name|Description| |---|---| diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 24091295e0..03209e0f21 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -36,6 +36,12 @@ type AcceptanceTestChoices struct { // ShareNetworkID is the Manila Share network ID ShareNetworkID string + + // DBDatastoreType is the datastore type for DB tests. + DBDatastoreType string + + // DBDatastoreTypeID is the datastore type version for DB tests. + DBDatastoreVersion string } // AcceptanceTestChoicesFromEnv populates a ComputeChoices struct from environment variables. @@ -48,6 +54,8 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { floatingIPPoolName := os.Getenv("OS_POOL_NAME") externalNetworkID := os.Getenv("OS_EXTGW_ID") shareNetworkID := os.Getenv("OS_SHARE_NETWORK_ID") + dbDatastoreType := os.Getenv("OS_DB_DATASTORE_TYPE") + dbDatastoreVersion := os.Getenv("OS_DB_DATASTORE_VERSION") missing := make([]string, 0, 3) if imageID == "" { @@ -96,6 +104,8 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { NetworkName: networkName, ExternalNetworkID: externalNetworkID, ShareNetworkID: shareNetworkID, + DBDatastoreType: dbDatastoreType, + DBDatastoreVersion: dbDatastoreVersion, }, nil } @@ -154,10 +164,10 @@ func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { }) } -// NewSharedFileSystemV2Client returns a *ServiceClient for making calls -// to the OpenStack Shared File System v2 API. An error will be returned +// NewComputeV2Client returns a *ServiceClient for making calls +// to the OpenStack Compute v2 API. An error will be returned // if authentication or client creation was not possible. -func NewSharedFileSystemV2Client() (*gophercloud.ServiceClient, error) { +func NewComputeV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err @@ -168,15 +178,15 @@ func NewSharedFileSystemV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - return openstack.NewSharedFileSystemV2(client, gophercloud.EndpointOpts{ + return openstack.NewComputeV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } -// NewComputeV2Client returns a *ServiceClient for making calls -// to the OpenStack Compute v2 API. An error will be returned +// NewDBV1Client returns a *ServiceClient for making calls +// to the OpenStack Database v1 API. An error will be returned // if authentication or client creation was not possible. -func NewComputeV2Client() (*gophercloud.ServiceClient, error) { +func NewDBV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err @@ -187,7 +197,7 @@ func NewComputeV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - return openstack.NewComputeV2(client, gophercloud.EndpointOpts{ + return openstack.NewDBV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } @@ -359,3 +369,22 @@ func NewObjectStorageV1Client() (*gophercloud.ServiceClient, error) { Region: os.Getenv("OS_REGION_NAME"), }) } + +// NewSharedFileSystemV2Client returns a *ServiceClient for making calls +// to the OpenStack Shared File System v2 API. An error will be returned +// if authentication or client creation was not possible. +func NewSharedFileSystemV2Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + return openstack.NewSharedFileSystemV2(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} diff --git a/acceptance/openstack/db/v1/common.go b/acceptance/openstack/db/v1/common.go deleted file mode 100644 index bbe7ebd6fd..0000000000 --- a/acceptance/openstack/db/v1/common.go +++ /dev/null @@ -1,70 +0,0 @@ -// +build acceptance db - -package v1 - -import ( - "os" - "testing" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/db/v1/instances" - th "github.com/gophercloud/gophercloud/testhelper" -) - -func newClient(t *testing.T) *gophercloud.ServiceClient { - ao, err := openstack.AuthOptionsFromEnv() - th.AssertNoErr(t, err) - - client, err := openstack.AuthenticatedClient(ao) - th.AssertNoErr(t, err) - - c, err := openstack.NewDBV1(client, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), - }) - th.AssertNoErr(t, err) - - return c -} - -type context struct { - test *testing.T - client *gophercloud.ServiceClient - instanceID string - DBIDs []string - users []string -} - -func newContext(t *testing.T) context { - return context{ - test: t, - client: newClient(t), - } -} - -func (c context) Logf(msg string, args ...interface{}) { - if len(args) > 0 { - c.test.Logf(msg, args...) - } else { - c.test.Log(msg) - } -} - -func (c context) AssertNoErr(err error) { - th.AssertNoErr(c.test, err) -} - -func (c context) WaitUntilActive(id string) { - err := gophercloud.WaitFor(60, func() (bool, error) { - inst, err := instances.Get(c.client, id).Extract() - if err != nil { - return false, err - } - if inst.Status == "ACTIVE" { - return true, nil - } - return false, nil - }) - - c.AssertNoErr(err) -} diff --git a/acceptance/openstack/db/v1/database_test.go b/acceptance/openstack/db/v1/database_test.go deleted file mode 100644 index c52357a6be..0000000000 --- a/acceptance/openstack/db/v1/database_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// +build acceptance db - -package v1 - -import ( - db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" - "github.com/gophercloud/gophercloud/pagination" -) - -func (c context) createDBs() { - opts := db.BatchCreateOpts{ - db.CreateOpts{Name: "db1"}, - db.CreateOpts{Name: "db2"}, - db.CreateOpts{Name: "db3"}, - } - - err := db.Create(c.client, c.instanceID, opts).ExtractErr() - c.AssertNoErr(err) - c.Logf("Created three databases on instance %s: db1, db2, db3", c.instanceID) -} - -func (c context) listDBs() { - c.Logf("Listing databases on instance %s", c.instanceID) - - err := db.List(c.client, c.instanceID).EachPage(func(page pagination.Page) (bool, error) { - dbList, err := db.ExtractDBs(page) - c.AssertNoErr(err) - - for _, db := range dbList { - c.Logf("DB: %#v", db) - } - - return true, nil - }) - - c.AssertNoErr(err) -} - -func (c context) deleteDBs() { - for _, id := range []string{"db1", "db2", "db3"} { - err := db.Delete(c.client, c.instanceID, id).ExtractErr() - c.AssertNoErr(err) - c.Logf("Deleted DB %s", id) - } -} diff --git a/acceptance/openstack/db/v1/databases_test.go b/acceptance/openstack/db/v1/databases_test.go new file mode 100644 index 0000000000..dcbf72f040 --- /dev/null +++ b/acceptance/openstack/db/v1/databases_test.go @@ -0,0 +1,55 @@ +// +build acceptance db + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/db/v1/databases" +) + +// Because it takes so long to create an instance, +// all tests will be housed in a single function. +func TestDatabases(t *testing.T) { + if testing.Short() { + t.Skip("Skipping in short mode") + } + + client, err := clients.NewDBV1Client() + if err != nil { + t.Fatalf("Unable to create a DB client: %v", err) + } + + // Create and Get an instance. + instance, err := CreateInstance(t, client) + if err != nil { + t.Fatalf("Unable to create instance: %v", err) + } + defer DeleteInstance(t, client, instance.ID) + + // Create a database. + err = CreateDatabase(t, client, instance.ID) + if err != nil { + t.Fatalf("Unable to create database: %v", err) + } + + // List all databases. + allPages, err := databases.List(client, instance.ID).AllPages() + if err != nil { + t.Fatalf("Unable to list databases: %v", err) + } + + allDatabases, err := databases.ExtractDBs(allPages) + if err != nil { + t.Fatalf("Unable to extract databases: %v", err) + } + + for _, db := range allDatabases { + tools.PrintResource(t, db) + } + + defer DeleteDatabase(t, client, instance.ID, allDatabases[0].Name) + +} diff --git a/acceptance/openstack/db/v1/db.go b/acceptance/openstack/db/v1/db.go new file mode 100644 index 0000000000..f5e637f3d9 --- /dev/null +++ b/acceptance/openstack/db/v1/db.go @@ -0,0 +1,145 @@ +// Package v2 contains common functions for creating db resources for use +// in acceptance tests. See the `*_test.go` files for example usages. +package v1 + +import ( + "fmt" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/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" +) + +// CreateDatabase will create a database with a randomly generated name. +// An error will be returned if the database was unable to be created. +func CreateDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID string) error { + name := tools.RandomString("ACPTTEST", 8) + t.Logf("Attempting to create database: %s", name) + + createOpts := databases.BatchCreateOpts{ + databases.CreateOpts{ + Name: name, + }, + } + + return databases.Create(client, instanceID, createOpts).ExtractErr() +} + +// CreateInstance will create an instance with a randomly generated name. +// The flavor of the instance will be the value of the OS_FLAVOR_ID +// environment variable. The Datastore will be pulled from the +// OS_DATASTORE_TYPE_ID environment variable. +// An error will be returned if the instance was unable to be created. +func CreateInstance(t *testing.T, client *gophercloud.ServiceClient) (*instances.Instance, error) { + if testing.Short() { + t.Skip("Skipping test that requires instance creation in short mode.") + } + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + return nil, err + } + + name := tools.RandomString("ACPTTEST", 8) + t.Logf("Attempting to create instance: %s", name) + + createOpts := instances.CreateOpts{ + FlavorRef: choices.FlavorID, + Size: 1, + Name: name, + Datastore: &instances.DatastoreOpts{ + Type: choices.DBDatastoreType, + Version: choices.DBDatastoreVersion, + }, + } + + instance, err := instances.Create(client, createOpts).Extract() + if err != nil { + return instance, err + } + + if err := WaitForInstanceStatus(client, instance, "ACTIVE"); err != nil { + return instance, err + } + + return instances.Get(client, instance.ID).Extract() +} + +// CreateUser will create a user with a randomly generated name. +// An error will be returned if the user was unable to be created. +func CreateUser(t *testing.T, client *gophercloud.ServiceClient, instanceID string) error { + name := tools.RandomString("ACPTTEST", 8) + password := tools.RandomString("", 8) + t.Logf("Attempting to create user: %s", name) + + createOpts := users.BatchCreateOpts{ + users.CreateOpts{ + Name: name, + Password: password, + }, + } + + return users.Create(client, instanceID, createOpts).ExtractErr() +} + +// DeleteDatabase deletes a database. A fatal error will occur if the database +// failed to delete. This works best when used as a deferred function. +func DeleteDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID, name string) { + t.Logf("Attempting to delete database: %s", name) + err := databases.Delete(client, instanceID, name).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete database %s: %s", name, err) + } + + t.Logf("Deleted database: %s", name) +} + +// DeleteInstance deletes an instance. A fatal error will occur if the instance +// failed to delete. This works best when used as a deferred function. +func DeleteInstance(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete instance: %s", id) + err := instances.Delete(client, id).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete instance %s: %s", id, err) + } + + t.Logf("Deleted instance: %s", id) +} + +// DeleteUser deletes a user. A fatal error will occur if the user +// failed to delete. This works best when used as a deferred function. +func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, instanceID, name string) { + t.Logf("Attempting to delete user: %s", name) + err := users.Delete(client, instanceID, name).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete users %s: %s", name, err) + } + + t.Logf("Deleted users: %s", name) +} + +// WaitForInstanceState will poll an instance's status until it either matches +// the specified status or the status becomes ERROR. +func WaitForInstanceStatus( + client *gophercloud.ServiceClient, instance *instances.Instance, status string) error { + return tools.WaitFor(func() (bool, error) { + latest, err := instances.Get(client, instance.ID).Extract() + if err != nil { + return false, err + } + + if latest.Status == status { + return true, nil + } + + if latest.Status == "ERROR" { + return false, fmt.Errorf("Instance in ERROR state") + } + + return false, nil + }) +} diff --git a/acceptance/openstack/db/v1/flavor_test.go b/acceptance/openstack/db/v1/flavor_test.go deleted file mode 100644 index 6440cc9278..0000000000 --- a/acceptance/openstack/db/v1/flavor_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build acceptance db - -package v1 - -import ( - "github.com/gophercloud/gophercloud/openstack/db/v1/flavors" - "github.com/gophercloud/gophercloud/pagination" -) - -func (c context) listFlavors() { - c.Logf("Listing flavors") - - err := flavors.List(c.client).EachPage(func(page pagination.Page) (bool, error) { - flavorList, err := flavors.ExtractFlavors(page) - c.AssertNoErr(err) - - for _, f := range flavorList { - c.Logf("Flavor: ID [%s] Name [%s] RAM [%d]", f.ID, f.Name, f.RAM) - } - - return true, nil - }) - - c.AssertNoErr(err) -} - -func (c context) getFlavor() { - flavor, err := flavors.Get(c.client, "1").Extract() - c.Logf("Getting flavor %s", flavor.ID) - c.AssertNoErr(err) -} diff --git a/acceptance/openstack/db/v1/flavors_test.go b/acceptance/openstack/db/v1/flavors_test.go new file mode 100644 index 0000000000..73171b9424 --- /dev/null +++ b/acceptance/openstack/db/v1/flavors_test.go @@ -0,0 +1,58 @@ +// +build acceptance db + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/db/v1/flavors" +) + +func TestFlavorsList(t *testing.T) { + client, err := clients.NewDBV1Client() + if err != nil { + t.Fatalf("Unable to create a DB client: %v", err) + } + + allPages, err := flavors.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve 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 TestFlavorsGet(t *testing.T) { + client, err := clients.NewDBV1Client() + if err != nil { + t.Fatalf("Unable to create a DB client: %v", err) + } + + allPages, err := flavors.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve flavors: %v", err) + } + + allFlavors, err := flavors.ExtractFlavors(allPages) + if err != nil { + t.Fatalf("Unable to extract flavors: %v", err) + } + + if len(allFlavors) > 0 { + flavor, err := flavors.Get(client, allFlavors[0].StrID).Extract() + if err != nil { + t.Fatalf("Unable to get flavor: %v", err) + } + + tools.PrintResource(t, flavor) + } +} diff --git a/acceptance/openstack/db/v1/instance_test.go b/acceptance/openstack/db/v1/instance_test.go deleted file mode 100644 index 75668a2297..0000000000 --- a/acceptance/openstack/db/v1/instance_test.go +++ /dev/null @@ -1,138 +0,0 @@ -// +build acceptance db - -package v1 - -import ( - "os" - "testing" - - "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/db/v1/instances" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" -) - -const envDSType = "DATASTORE_TYPE_ID" - -func TestRunner(t *testing.T) { - c := newContext(t) - - // FLAVOR tests - c.listFlavors() - c.getFlavor() - - // INSTANCE tests - c.createInstance() - c.listInstances() - c.getInstance() - c.isRootEnabled() - c.enableRootUser() - c.isRootEnabled() - c.restartInstance() - //c.resizeInstance() - //c.resizeVol() - - // DATABASE tests - c.createDBs() - c.listDBs() - - // USER tests - c.createUsers() - c.listUsers() - - // TEARDOWN - c.deleteUsers() - c.deleteDBs() - c.deleteInstance() -} - -func (c context) createInstance() { - if os.Getenv(envDSType) == "" { - c.test.Fatalf("%s must be set as an environment var", envDSType) - } - - opts := instances.CreateOpts{ - FlavorRef: "2", - Size: 5, - Name: tools.RandomString("gopher_db", 5), - Datastore: &instances.DatastoreOpts{Type: os.Getenv(envDSType)}, - } - - instance, err := instances.Create(c.client, opts).Extract() - th.AssertNoErr(c.test, err) - - c.Logf("Restarting %s. Waiting...", instance.ID) - c.WaitUntilActive(instance.ID) - c.Logf("Created Instance %s", instance.ID) - - c.instanceID = instance.ID -} - -func (c context) listInstances() { - c.Logf("Listing instances") - - err := instances.List(c.client).EachPage(func(page pagination.Page) (bool, error) { - instanceList, err := instances.ExtractInstances(page) - c.AssertNoErr(err) - - for _, i := range instanceList { - c.Logf("Instance: ID [%s] Name [%s] Status [%s] VolSize [%d] Datastore Type [%s]", - i.ID, i.Name, i.Status, i.Volume.Size, i.Datastore.Type) - } - - return true, nil - }) - - c.AssertNoErr(err) -} - -func (c context) getInstance() { - instance, err := instances.Get(c.client, c.instanceID).Extract() - c.AssertNoErr(err) - c.Logf("Getting instance: %s", instance.ID) -} - -func (c context) deleteInstance() { - err := instances.Delete(c.client, c.instanceID).ExtractErr() - c.AssertNoErr(err) - c.Logf("Deleted instance %s", c.instanceID) -} - -func (c context) enableRootUser() { - _, err := instances.EnableRootUser(c.client, c.instanceID).Extract() - c.AssertNoErr(err) - c.Logf("Enabled root user on %s", c.instanceID) -} - -func (c context) isRootEnabled() { - enabled, err := instances.IsRootEnabled(c.client, c.instanceID) - c.AssertNoErr(err) - c.Logf("Is root enabled? %d", enabled) -} - -func (c context) restartInstance() { - id := c.instanceID - err := instances.Restart(c.client, id).ExtractErr() - c.AssertNoErr(err) - c.Logf("Restarting %s. Waiting...", id) - c.WaitUntilActive(id) - c.Logf("Restarted %s", id) -} - -func (c context) resizeInstance() { - id := c.instanceID - err := instances.Resize(c.client, id, "3").ExtractErr() - c.AssertNoErr(err) - c.Logf("Resizing %s. Waiting...", id) - c.WaitUntilActive(id) - c.Logf("Resized %s with flavorRef %s", id, "2") -} - -func (c context) resizeVol() { - id := c.instanceID - err := instances.ResizeVolume(c.client, id, 4).ExtractErr() - c.AssertNoErr(err) - c.Logf("Resizing volume of %s. Waiting...", id) - c.WaitUntilActive(id) - c.Logf("Resized the volume of %s to %d GB", id, 2) -} diff --git a/acceptance/openstack/db/v1/instances_test.go b/acceptance/openstack/db/v1/instances_test.go new file mode 100644 index 0000000000..a98047f3e0 --- /dev/null +++ b/acceptance/openstack/db/v1/instances_test.go @@ -0,0 +1,71 @@ +// +build acceptance db + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/db/v1/instances" +) + +// Because it takes so long to create an instance, +// all tests will be housed in a single function. +func TestInstances(t *testing.T) { + if testing.Short() { + t.Skip("Skipping in short mode") + } + + client, err := clients.NewDBV1Client() + if err != nil { + t.Fatalf("Unable to create a DB client: %v", err) + } + + // Create and Get an instance. + instance, err := CreateInstance(t, client) + if err != nil { + t.Fatalf("Unable to create instance: %v", err) + } + defer DeleteInstance(t, client, instance.ID) + tools.PrintResource(t, &instance) + + // List all instances. + allPages, err := instances.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to list instances: %v", err) + } + + allInstances, err := instances.ExtractInstances(allPages) + if err != nil { + t.Fatalf("Unable to extract instances: %v", err) + } + + for _, instance := range allInstances { + tools.PrintResource(t, instance) + } + + // Enable root user. + _, err = instances.EnableRootUser(client, instance.ID).Extract() + if err != nil { + t.Fatalf("Unable to enable root user: %v", err) + } + + enabled, err := instances.IsRootEnabled(client, instance.ID).Extract() + if err != nil { + t.Fatalf("Unable to check if root user is enabled: %v", err) + } + + t.Logf("Root user is enabled: %t", enabled) + + // Restart + err = instances.Restart(client, instance.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to restart instance: %v", err) + } + + err = WaitForInstanceStatus(client, instance, "ACTIVE") + if err != nil { + t.Fatalf("Unable to restart instance: %v", err) + } +} diff --git a/acceptance/openstack/db/v1/user_test.go b/acceptance/openstack/db/v1/user_test.go deleted file mode 100644 index 0f5fcc24b1..0000000000 --- a/acceptance/openstack/db/v1/user_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// +build acceptance db - -package v1 - -import ( - "github.com/gophercloud/gophercloud/acceptance/tools" - db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" - u "github.com/gophercloud/gophercloud/openstack/db/v1/users" - "github.com/gophercloud/gophercloud/pagination" -) - -func (c context) createUsers() { - users := []string{ - tools.RandomString("user_", 5), - tools.RandomString("user_", 5), - tools.RandomString("user_", 5), - } - - db1 := db.CreateOpts{Name: "db1"} - db2 := db.CreateOpts{Name: "db2"} - db3 := db.CreateOpts{Name: "db3"} - - opts := u.BatchCreateOpts{ - u.CreateOpts{ - Name: users[0], - Password: tools.RandomString("", 5), - Databases: db.BatchCreateOpts{db1, db2, db3}, - }, - u.CreateOpts{ - Name: users[1], - Password: tools.RandomString("", 5), - Databases: db.BatchCreateOpts{db1, db2}, - }, - u.CreateOpts{ - Name: users[2], - Password: tools.RandomString("", 5), - Databases: db.BatchCreateOpts{db3}, - }, - } - - err := u.Create(c.client, c.instanceID, opts).ExtractErr() - c.AssertNoErr(err) - c.Logf("Created three users on instance %s: %s, %s, %s", c.instanceID, users[0], users[1], users[2]) - c.users = users -} - -func (c context) listUsers() { - c.Logf("Listing databases on instance %s", c.instanceID) - - err := db.List(c.client, c.instanceID).EachPage(func(page pagination.Page) (bool, error) { - dbList, err := db.ExtractDBs(page) - c.AssertNoErr(err) - - for _, db := range dbList { - c.Logf("DB: %#v", db) - } - - return true, nil - }) - - c.AssertNoErr(err) -} - -func (c context) deleteUsers() { - for _, id := range c.DBIDs { - err := db.Delete(c.client, c.instanceID, id).ExtractErr() - c.AssertNoErr(err) - c.Logf("Deleted DB %s", id) - } -} diff --git a/acceptance/openstack/db/v1/users_test.go b/acceptance/openstack/db/v1/users_test.go new file mode 100644 index 0000000000..4335eabb2f --- /dev/null +++ b/acceptance/openstack/db/v1/users_test.go @@ -0,0 +1,54 @@ +// +build acceptance db + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/db/v1/users" +) + +// Because it takes so long to create an instance, +// all tests will be housed in a single function. +func TestUsers(t *testing.T) { + if testing.Short() { + t.Skip("Skipping in short mode") + } + + client, err := clients.NewDBV1Client() + if err != nil { + t.Fatalf("Unable to create a DB client: %v", err) + } + + // Create and Get an instance. + instance, err := CreateInstance(t, client) + if err != nil { + t.Fatalf("Unable to create instance: %v", err) + } + defer DeleteInstance(t, client, instance.ID) + + // Create a user. + err = CreateUser(t, client, instance.ID) + if err != nil { + t.Fatalf("Unable to create user: %v", err) + } + + // List all users. + allPages, err := users.List(client, instance.ID).AllPages() + if err != nil { + t.Fatalf("Unable to list users: %v", err) + } + + allUsers, err := users.ExtractUsers(allPages) + if err != nil { + t.Fatalf("Unable to extract users: %v", err) + } + + for _, user := range allUsers { + tools.PrintResource(t, user) + } + + defer DeleteUser(t, client, instance.ID, allUsers[0].Name) +} From 2bb7bc2bf489d5a786e31e042b17ef9e9d82e203 Mon Sep 17 00:00:00 2001 From: Maciej Zimnoch Date: Mon, 23 Oct 2017 10:50:00 +0200 Subject: [PATCH 0100/2296] Ignore Gogland IDE internal directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ead84456eb..df9048a010 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ **/*.swp +.idea From 84381a9b121069d9651f7d1f15be0b10fe92df87 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Tue, 24 Oct 2017 08:34:18 -0600 Subject: [PATCH 0101/2296] Identity v3: role create --- acceptance/openstack/identity/v3/identity.go | 28 +++++++++++ .../openstack/identity/v3/roles_test.go | 24 +++++++++ openstack/identity/v3/roles/doc.go | 16 ++++++ openstack/identity/v3/roles/requests.go | 49 +++++++++++++++++++ openstack/identity/v3/roles/results.go | 42 ++++++++++++++++ .../identity/v3/roles/testing/fixtures.go | 40 +++++++++++++-- .../v3/roles/testing/requests_test.go | 19 +++++++ openstack/identity/v3/roles/urls.go | 4 ++ 8 files changed, 219 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 82c15f95c5..2cff94caec 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" ) @@ -119,6 +120,33 @@ func CreateDomain(t *testing.T, client *gophercloud.ServiceClient, c *domains.Cr return domain, nil } +// CreateRole will create a role with a random name. +// It takes an optional createOpts parameter since creating a role +// 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) + t.Logf("Attempting to create role: %s", name) + + var createOpts roles.CreateOpts + if c != nil { + createOpts = *c + } else { + createOpts = roles.CreateOpts{} + } + + createOpts.Name = name + + role, err := roles.Create(client, createOpts).Extract() + if err != nil { + return role, err + } + + t.Logf("Successfully created role %s with ID %s", name, role.ID) + + return role, nil +} + // DeleteProject will delete a project by ID. A fatal error will occur if // the project ID failed to be deleted. This works best when using it as // a deferred function. diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 58b6a155d9..599eb8def1 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -59,3 +59,27 @@ func TestRolesGet(t *testing.T) { tools.PrintResource(t, p) } + +func TestRoleCRUD(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + createOpts := roles.CreateOpts{ + Name: "testrole", + DomainID: "default", + Extra: map[string]interface{}{ + "description": "test role description", + }, + } + + // Create Role in the default domain + role, err := CreateRole(t, client, &createOpts) + if err != nil { + t.Fatalf("Unable to create role: %v", err) + } + + tools.PrintResource(t, role) + tools.PrintResource(t, role.Extra) +} diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 0e908a80be..4e1b2c263a 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -22,6 +22,22 @@ Example to List Roles fmt.Printf("%+v\n", role) } +Example to Create a Role + + createOpts := roles.CreateOpts{ + Name: "read-only-admin", + DomainID: "default", + Extra: map[string]interface{}{ + "description": "this role grants read-only privilege cross tenant", + } + } + + role, err := roles.Create(identityClient, createOpts).Extract() + if err != nil { + panic(err) + } + + Example to List Role Assignments listOpts := roles.ListAssignmentsOpts{ diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index c1a6020ceb..97ca88a7dc 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -48,6 +48,55 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToRoleCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a role. +type CreateOpts struct { + // Name is the name of the new role. + Name string `json:"name" required:"true"` + + // DomainID is the ID of the domain the role belongs to. + DomainID string `json:"domain_id,omitempty"` + + // Extra is free-form extra key/value pairs to describe the role. + Extra map[string]interface{} `json:"-"` +} + +// ToRoleCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToRoleCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "role") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["role"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Create creates a new Role. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToRoleCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + // ListAssignmentsOptsBuilder allows extensions to add additional parameters to // the ListAssignments request. type ListAssignmentsOptsBuilder interface { diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 5b89f78fc2..a6ed861a0b 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -1,7 +1,10 @@ package roles import ( + "encoding/json" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -18,6 +21,39 @@ type Role struct { // Name is the role name Name string `json:"name"` + + // Extra is a collection of miscellaneous key/values. + Extra map[string]interface{} `json:"-"` +} + +func (r *Role) UnmarshalJSON(b []byte) error { + type tmp Role + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Role(s.tmp) + + // Collect other fields and bundle them into Extra + // but only if a field titled "extra" wasn't sent. + if s.Extra != nil { + r.Extra = s.Extra + } else { + var result interface{} + err := json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = internal.RemainingKeys(Role{}, resultMap) + } + } + + return err } type roleResult struct { @@ -30,6 +66,12 @@ type GetResult struct { roleResult } +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Role +type CreateResult struct { + roleResult +} + // RolePage is a single page of Role results. type RolePage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index 86681c86ee..bc6f171a78 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -33,7 +33,10 @@ const ListOutput = ` "links": { "self": "https://example.com/identity/v3/roles/9fe1d3" }, - "name": "support" + "name": "support", + "extra": { + "description": "read-only support role" + } } ] } @@ -48,7 +51,21 @@ const GetOutput = ` "links": { "self": "https://example.com/identity/v3/roles/9fe1d3" }, - "name": "support" + "name": "support", + "extra": { + "description": "read-only support role" + } + } +} +` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "role": { + "domain_id": "1789d1", + "name": "support", + "description": "read-only support role" } } ` @@ -60,7 +77,8 @@ var FirstRole = roles.Role{ Links: map[string]interface{}{ "self": "http://example.com/identity/v3/roles/2844b2a08be147a08ef58317d6471f1f", }, - Name: "admin-read-only", + Name: "admin-read-only", + Extra: map[string]interface{}{}, } // SecondRole is the second role in the List request. @@ -71,6 +89,9 @@ var SecondRole = roles.Role{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, Name: "support", + Extra: map[string]interface{}{ + "description": "read-only support role", + }, } // ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. @@ -103,3 +124,16 @@ func HandleGetRoleSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleCreateRoleSuccessfully creates an HTTP handler at `/roles` on the +// test handler mux that tests role creation. +func HandleCreateRoleSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/roles", 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, GetOutput) + }) +} diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 2aeba337e9..9a54c98ecb 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -42,6 +42,7 @@ func TestListRolesAllPages(t *testing.T) { actual, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRolesSlice, actual) + th.AssertEquals(t, ExpectedRolesSlice[1].Extra["description"], "read-only support role") } func TestGetRole(t *testing.T) { @@ -55,6 +56,24 @@ func TestGetRole(t *testing.T) { th.CheckDeepEquals(t, SecondRole, *actual) } +func TestCreateRole(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateRoleSuccessfully(t) + + createOpts := roles.CreateOpts{ + Name: "support", + DomainID: "1789d1", + Extra: map[string]interface{}{ + "description": "read-only support role", + }, + } + + actual, err := roles.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondRole, *actual) +} + func TestListAssignmentsSinglePage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index ab32d850e1..cd1cad9994 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -10,6 +10,10 @@ func getURL(client *gophercloud.ServiceClient, roleID string) string { return client.ServiceURL("roles", roleID) } +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("roles") +} + func listAssignmentsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("role_assignments") } From 56952a0c3b18b984fd5f20cc282e62b2e4d772a3 Mon Sep 17 00:00:00 2001 From: Christian Eichelmann Date: Thu, 26 Oct 2017 10:14:53 +0200 Subject: [PATCH 0102/2296] allow to configure the enable_snat parameter --- .../networking/v2/extensions/layer3/routers/results.go | 1 + .../extensions/layer3/routers/testing/requests_test.go | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index e7c27dc332..e19c8e74c4 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -9,6 +9,7 @@ import ( // particular network router. type GatewayInfo struct { NetworkID string `json:"network_id"` + EnableSNAT *bool `json:"enable_snat,omitempty"` ExternalFixedIPs []ExternalFixedIP `json:"external_fixed_ips,omitempty"` } diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 9feceaba0e..5010de6050 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -136,6 +136,7 @@ func TestCreate(t *testing.T) { "name": "foo_router", "admin_state_up": false, "external_gateway_info":{ + "enable_snat": false, "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b" } } @@ -151,6 +152,7 @@ func TestCreate(t *testing.T) { "status": "ACTIVE", "external_gateway_info": { "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "enable_snat": false, "external_fixed_ips": [ {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} ] @@ -166,8 +168,11 @@ func TestCreate(t *testing.T) { }) asu := false - gwi := routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"} - + enableSNAT := false + gwi := routers.GatewayInfo{ + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + EnableSNAT: &enableSNAT, + } options := routers.CreateOpts{ Name: "foo_router", AdminStateUp: &asu, From 92a1eb67835bbbf121a38a5e595fae06496a9131 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 27 Oct 2017 02:25:58 +0000 Subject: [PATCH 0103/2296] Networking v2: Add SNAT to external router acceptance test --- .../openstack/networking/v2/extensions/layer3/layer3.go | 4 +++- .../openstack/networking/v2/extensions/layer3/routers_test.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 7bc0676d00..3d017be889 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -46,8 +46,10 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou t.Logf("Attempting to create external router: %s", routerName) adminStateUp := true + enableSNAT := false gatewayInfo := routers.GatewayInfo{ - NetworkID: choices.ExternalNetworkID, + NetworkID: choices.ExternalNetworkID, + EnableSNAT: &enableSNAT, } createOpts := routers.CreateOpts{ diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 06194af223..4be922e9f5 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -34,7 +34,7 @@ func TestLayer3RouterList(t *testing.T) { } } -func TestLayer3RouterCreateDelete(t *testing.T) { +func TestLayer3ExternalRouterCreateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) From bdaf7ea4ee70c28c933b288e0554af15e08be648 Mon Sep 17 00:00:00 2001 From: Dongwoo Son Date: Fri, 27 Oct 2017 18:46:33 -0700 Subject: [PATCH 0104/2296] Identity v3: Add assigning a role to a user on a project (#570) * Identity v3: Add assigning a role to a user on a project * Refactor using 'xor' tag for assigning/unassigning a role * Address namings and toRoleAssignURL function * Separate out UnassignOpts for Unassing from AssignOpts * Update comment and doc --- acceptance/openstack/identity/v3/identity.go | 42 ++- .../openstack/identity/v3/roles_test.go | 244 +++++++++++++++++- openstack/identity/v3/roles/doc.go | 30 +++ openstack/identity/v3/roles/requests.go | 112 ++++++++ openstack/identity/v3/roles/results.go | 12 + .../identity/v3/roles/testing/fixtures.go | 60 ++++- .../v3/roles/testing/requests_test.go | 60 +++++ openstack/identity/v3/roles/urls.go | 14 +- 8 files changed, 557 insertions(+), 17 deletions(-) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 2cff94caec..f68bb4715c 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -165,10 +165,10 @@ func DeleteProject(t *testing.T, client *gophercloud.ServiceClient, projectID st func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) { err := users.Delete(client, userID).ExtractErr() if err != nil { - t.Fatalf("Unable to delete user %s: %v", userID, err) + t.Fatalf("Unable to delete user with ID %s: %v", userID, err) } - t.Logf("Deleted user: %s", userID) + t.Logf("Deleted user with ID: %s", userID) } // DeleteGroup will delete a group by ID. A fatal error will occur if @@ -194,3 +194,41 @@ func DeleteDomain(t *testing.T, client *gophercloud.ServiceClient, domainID stri t.Logf("Deleted domain: %s", domainID) } + +// UnassignRole will delete a role assigned to a user/group on a project/domain +// A fatal error will occur if it fails to delete the assignment. +// This works best when using it as a deferred function. +func UnassignRole(t *testing.T, client *gophercloud.ServiceClient, roleID string, opts *roles.UnassignOpts) { + err := roles.Unassign(client, roleID, *opts).ExtractErr() + if err != nil { + t.Fatalf("Unable to unassign a role %v on context %+v: %v", roleID, *opts, err) + } + t.Logf("Unassigned the role %v on context %+v", roleID, *opts) +} + +// FindRole finds all roles that the current authenticated client has access +// to and returns the first one found. An error will be returned if the lookup +// was unsuccessful. +func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, error) { + t.Log("Attempting to find a role") + var role *roles.Role + + allPages, err := roles.List(client, nil).AllPages() + if err != nil { + return nil, err + } + + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + return nil, err + } + + for _, r := range allRoles { + role = &r + break + } + + t.Logf("Successfully found a role %s with ID %s", role.Name, role.ID) + + return role, nil +} diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 599eb8def1..def152026b 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -5,8 +5,10 @@ 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/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" ) @@ -41,17 +43,11 @@ func TestRolesGet(t *testing.T) { t.Fatalf("Unable to obtain an identity client: %v", err) } - allPages, err := roles.List(client, nil).AllPages() + role, err := FindRole(t, client) if err != nil { - t.Fatalf("Unable to list roles: %v", err) + t.Fatalf("Unable to find a role: %v", err) } - allRoles, err := roles.ExtractRoles(allPages) - if err != nil { - t.Fatalf("Unable to extract roles: %v", err) - } - - role := allRoles[0] p, err := roles.Get(client, role.ID).Extract() if err != nil { t.Fatalf("Unable to get role: %v", err) @@ -83,3 +79,235 @@ func TestRoleCRUD(t *testing.T) { tools.PrintResource(t, role) tools.PrintResource(t, role.Extra) } + +func TestRoleAssignToUserOnProject(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an indentity client: %v", err) + } + + project, err := CreateProject(t, client, nil) + if err != nil { + t.Fatal("Unable to create a project") + } + defer DeleteProject(t, client, project.ID) + + role, err := FindRole(t, client) + if err != nil { + t.Fatalf("Unable to get a role: %v", err) + } + + user, err := CreateUser(t, client, nil) + if err != nil { + t.Fatalf("Unable to create user: %v", 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) + err = roles.Assign(client, role.ID, roles.AssignOpts{ + UserID: user.ID, + ProjectID: project.ID, + }).ExtractErr() + if err != nil { + t.Fatalf("Unable to assign a role to a user on a project: %v", 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, + }) + + allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{ + RoleID: role.ID, + ScopeProjectID: project.ID, + UserID: user.ID, + }).AllPages() + if err != nil { + t.Fatalf("Unable to list role assignments: %v", err) + } + + allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) + if err != nil { + t.Fatalf("Unable to extract role assignments: %v", err) + } + + t.Logf("Role assignments of user %s on project %s:", user.Name, project.Name) + for _, roleAssignment := range allRoleAssignments { + tools.PrintResource(t, roleAssignment) + } +} + +func TestRoleAssignToUserOnDomain(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an indentity client: %v", err) + } + + domain, err := CreateDomain(t, client, &domains.CreateOpts{ + Enabled: gophercloud.Disabled, + }) + if err != nil { + t.Fatal("Unable to create a domain") + } + defer DeleteDomain(t, client, domain.ID) + + role, err := FindRole(t, client) + if err != nil { + t.Fatalf("Unable to get a role: %v", err) + } + + user, err := CreateUser(t, client, nil) + if err != nil { + t.Fatalf("Unable to create user: %v", 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) + err = roles.Assign(client, role.ID, roles.AssignOpts{ + UserID: user.ID, + DomainID: domain.ID, + }).ExtractErr() + if err != nil { + t.Fatalf("Unable to assign a role to a user on a domain: %v", err) + } + t.Logf("Successfully assigned a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name) + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ + UserID: user.ID, + DomainID: domain.ID, + }) + + allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{ + RoleID: role.ID, + ScopeDomainID: domain.ID, + UserID: user.ID, + }).AllPages() + if err != nil { + t.Fatalf("Unable to list role assignments: %v", err) + } + + allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) + if err != nil { + t.Fatalf("Unable to extract role assignments: %v", err) + } + + t.Logf("Role assignments of user %s on domain %s:", user.Name, domain.Name) + for _, roleAssignment := range allRoleAssignments { + tools.PrintResource(t, roleAssignment) + } +} + +func TestRoleAssignToGroupOnDomain(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an indentity client: %v", err) + } + + domain, err := CreateDomain(t, client, &domains.CreateOpts{ + Enabled: gophercloud.Disabled, + }) + if err != nil { + t.Fatal("Unable to create a domain") + } + defer DeleteDomain(t, client, domain.ID) + + role, err := FindRole(t, client) + if err != nil { + t.Fatalf("Unable to get a role: %v", err) + } + + group, err := CreateGroup(t, client, nil) + if err != nil { + t.Fatalf("Unable to create group: %v", 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) + err = roles.Assign(client, role.ID, roles.AssignOpts{ + GroupID: group.ID, + DomainID: domain.ID, + }).ExtractErr() + if err != nil { + t.Fatalf("Unable to assign a role to a group on a domain: %v", err) + } + t.Logf("Successfully assigned a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name) + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ + GroupID: group.ID, + DomainID: domain.ID, + }) + + allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{ + RoleID: role.ID, + ScopeDomainID: domain.ID, + GroupID: group.ID, + }).AllPages() + if err != nil { + t.Fatalf("Unable to list role assignments: %v", err) + } + + allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) + if err != nil { + t.Fatalf("Unable to extract role assignments: %v", err) + } + + t.Logf("Role assignments of group %s on domain %s:", group.Name, domain.Name) + for _, roleAssignment := range allRoleAssignments { + tools.PrintResource(t, roleAssignment) + } +} + +func TestRoleAssignToGroupOnProject(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an indentity client: %v", err) + } + + project, err := CreateProject(t, client, nil) + if err != nil { + t.Fatal("Unable to create a project") + } + defer DeleteProject(t, client, project.ID) + + role, err := FindRole(t, client) + if err != nil { + t.Fatalf("Unable to get a role: %v", err) + } + + group, err := CreateGroup(t, client, nil) + if err != nil { + t.Fatalf("Unable to create group: %v", 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) + err = roles.Assign(client, role.ID, roles.AssignOpts{ + GroupID: group.ID, + ProjectID: project.ID, + }).ExtractErr() + if err != nil { + t.Fatalf("Unable to assign a role to a group on a project: %v", err) + } + t.Logf("Successfully assigned a role %s to a group %s on a project %s", role.Name, group.Name, project.Name) + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ + GroupID: group.ID, + ProjectID: project.ID, + }) + + allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{ + RoleID: role.ID, + ScopeProjectID: project.ID, + GroupID: group.ID, + }).AllPages() + if err != nil { + t.Fatalf("Unable to list role assignments: %v", err) + } + + allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) + if err != nil { + t.Fatalf("Unable to extract role assignments: %v", err) + } + + t.Logf("Role assignments of group %s on project %s:", group.Name, project.Name) + for _, roleAssignment := range allRoleAssignments { + tools.PrintResource(t, roleAssignment) + } +} diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 4e1b2c263a..736bf1bf21 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -58,5 +58,35 @@ Example to List Role Assignments for _, role := range allRoles { fmt.Printf("%+v\n", role) } + +Example to Assign a Role to a User in a Project + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := roles.Assign(identityClient, roleID, roles.AssignOpts{ + UserID: userID, + ProjectID: projectID, + }).ExtractErr() + + if err != nil { + panic(err) + } + +Example to Unassign a Role From a User in a Project + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + roleID := "9fe2ff9ee4384b1894a90878d3e92bab" + + err := roles.Unassign(identityClient, roleID, roles.UnassignOpts{ + UserID: userID, + ProjectID: projectID, + }).ExtractErr() + + if err != nil { + panic(err) + } */ package roles diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 97ca88a7dc..21e4a0e568 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -148,3 +148,115 @@ func ListAssignments(client *gophercloud.ServiceClient, opts ListAssignmentsOpts return RoleAssignmentPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// AssignOpts provides options to assign a role +type AssignOpts struct { + // UserID is the ID of a user to assign a 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 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 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 role on + // Note: exactly one of ProjectID or DomainID must be provided + DomainID string `xor:"ProjectID"` +} + +// UnassignOpts provides options to unassign a role +type UnassignOpts struct { + // UserID is the ID of a user to unassign a role + // Note: exactly one of UserID or GroupID must be provided + UserID string `xor:"GroupID"` + + // GroupID is the ID of a group to unassign a role + // Note: exactly one of UserID or GroupID must be provided + GroupID string `xor:"UserID"` + + // ProjectID is the ID of a project to unassign a role on + // Note: exactly one of ProjectID or DomainID must be provided + ProjectID string `xor:"DomainID"` + + // DomainID is the ID of a domain to unassign a role on + // Note: exactly one of ProjectID or DomainID must be provided + DomainID string `xor:"ProjectID"` +} + +// Assign is the operation responsible for assigning a 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" + } + + _, r.Err = client.Put(assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} + +// Unassign is the operation responsible for unassigning a role +// from 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" + } + + _, r.Err = client.Delete(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index a6ed861a0b..0c48f7fc2b 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -188,3 +188,15 @@ func ExtractRoleAssignments(r pagination.Page) ([]RoleAssignment, error) { err := (r.(RoleAssignmentPage)).ExtractInto(&s) return s.RoleAssignments, err } + +// AssignmentResult represents the result of an assign operation. +// Call ExtractErr method to determine if the request succeeded or failed. +type AssignmentResult 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/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index bc6f171a78..678b560099 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -7,7 +7,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + fake "github.com/gophercloud/gophercloud/testhelper/client" ) // ListOutput provides a single page of Role results. @@ -103,7 +103,7 @@ func HandleListRolesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/roles", 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) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -117,7 +117,7 @@ func HandleGetRoleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/roles/9fe1d3", 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) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -130,10 +130,62 @@ func HandleGetRoleSuccessfully(t *testing.T) { func HandleCreateRoleSuccessfully(t *testing.T) { th.Mux.HandleFunc("/roles", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, GetOutput) }) } + +func HandleAssignSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles/{role_id}", 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("/projects/{project_id}/groups/{group_id}/roles/{role_id}", 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("/domains/{domain_id}/users/{user_id}/roles/{role_id}", 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("/domains/{domain_id}/groups/{group_id}/roles/{role_id}", 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) + }) +} + +func HandleUnassignSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles/{role_id}", 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("/projects/{project_id}/groups/{group_id}/roles/{role_id}", 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("/domains/{domain_id}/users/{user_id}/roles/{role_id}", 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("/domains/{domain_id}/groups/{group_id}/roles/{role_id}", 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/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 9a54c98ecb..a78b12341d 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -165,3 +165,63 @@ func TestListAssignmentsSinglePage(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestAssign(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAssignSuccessfully(t) + + err := roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ + UserID: "{user_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ + UserID: "{user_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ + GroupID: "{group_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ + GroupID: "{group_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUnassign(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUnassignSuccessfully(t) + + err := roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ + UserID: "{user_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ + UserID: "{user_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ + GroupID: "{group_id}", + ProjectID: "{project_id}", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ + GroupID: "{group_id}", + DomainID: "{domain_id}", + }).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index cd1cad9994..3ec5c30062 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -2,18 +2,26 @@ package roles import "github.com/gophercloud/gophercloud" +const ( + rolePath = "roles" +) + func listURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("roles") + return client.ServiceURL(rolePath) } func getURL(client *gophercloud.ServiceClient, roleID string) string { - return client.ServiceURL("roles", roleID) + return client.ServiceURL(rolePath, roleID) } func createURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("roles") + return client.ServiceURL(rolePath) } func listAssignmentsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("role_assignments") } + +func assignURL(client *gophercloud.ServiceClient, targetType, targetID, actorType, actorID, roleID string) string { + return client.ServiceURL(targetType, targetID, actorType, actorID, rolePath, roleID) +} From 7a316cf164112656ce035e9dbc9596697370ba28 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 26 Oct 2017 22:10:20 -0600 Subject: [PATCH 0105/2296] Identity v3 delete role --- acceptance/openstack/identity/v3/identity.go | 12 ++++++++++++ acceptance/openstack/identity/v3/roles_test.go | 1 + openstack/identity/v3/roles/doc.go | 7 +++++++ openstack/identity/v3/roles/requests.go | 6 ++++++ openstack/identity/v3/roles/results.go | 6 ++++++ openstack/identity/v3/roles/testing/fixtures.go | 11 +++++++++++ openstack/identity/v3/roles/testing/requests_test.go | 9 +++++++++ openstack/identity/v3/roles/urls.go | 4 ++++ 8 files changed, 56 insertions(+) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index f68bb4715c..2577297811 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -195,6 +195,18 @@ func DeleteDomain(t *testing.T, client *gophercloud.ServiceClient, domainID stri t.Logf("Deleted domain: %s", domainID) } +// DeleteRole will delete a role by ID. A fatal error will occur if +// the role failed to be deleted. This works best when using it as +// a deferred function. +func DeleteRole(t *testing.T, client *gophercloud.ServiceClient, roleID string) { + err := roles.Delete(client, roleID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete role %s: %v", roleID, err) + } + + t.Logf("Deleted role: %s", roleID) +} + // UnassignRole will delete a role assigned to a user/group on a project/domain // A fatal error will occur if it fails to delete the assignment. // This works best when using it as a deferred function. diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index def152026b..5e613cae05 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -75,6 +75,7 @@ func TestRoleCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create role: %v", err) } + defer DeleteRole(t, client, role.ID) tools.PrintResource(t, role) tools.PrintResource(t, role.Extra) diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 736bf1bf21..66fd652cd7 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -37,6 +37,13 @@ Example to Create a Role panic(err) } +Example to Delete a Role + + roleID := "0fe36e73809d46aeae6705c39077b1b3" + err := roles.Delete(identityClient, roleID).ExtractErr() + if err != nil { + panic(err) + } Example to List Role Assignments diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 21e4a0e568..5bbff77f46 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -97,6 +97,12 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// Delete deletes a role. +func Delete(client *gophercloud.ServiceClient, roleID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, roleID), nil) + return +} + // ListAssignmentsOptsBuilder allows extensions to add additional parameters to // the ListAssignments request. type ListAssignmentsOptsBuilder interface { diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 0c48f7fc2b..878230c667 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -72,6 +72,12 @@ type CreateResult struct { roleResult } +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // RolePage is a single page of Role results. type RolePage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index 678b560099..bb171767dd 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -138,6 +138,17 @@ func HandleCreateRoleSuccessfully(t *testing.T) { }) } +// HandleDeleteRoleSuccessfully creates an HTTP handler at `/roles` on the +// test handler mux that tests role deletion. +func HandleDeleteRoleSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/roles/9fe1d3", 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 HandleAssignSuccessfully(t *testing.T) { th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles/{role_id}", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index a78b12341d..635cee662e 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -74,6 +74,15 @@ func TestCreateRole(t *testing.T) { th.CheckDeepEquals(t, SecondRole, *actual) } +func TestDeleteRole(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteRoleSuccessfully(t) + + res := roles.Delete(client.ServiceClient(), "9fe1d3") + th.AssertNoErr(t, res.Err) +} + func TestListAssignmentsSinglePage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index 3ec5c30062..f35dddd1d1 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -18,6 +18,10 @@ func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(rolePath) } +func deleteURL(client *gophercloud.ServiceClient, roleID string) string { + return client.ServiceURL(rolePath, roleID) +} + func listAssignmentsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("role_assignments") } From d2cb7f160ec16dada83bfac3b235c3ac112f0ac8 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 26 Oct 2017 22:42:04 -0600 Subject: [PATCH 0106/2296] Identity v3 role update --- .../openstack/identity/v3/roles_test.go | 14 +++++ openstack/identity/v3/roles/doc.go | 13 +++++ openstack/identity/v3/roles/requests.go | 46 ++++++++++++++++ openstack/identity/v3/roles/results.go | 6 +++ .../identity/v3/roles/testing/fixtures.go | 52 +++++++++++++++++++ .../v3/roles/testing/requests_test.go | 16 ++++++ openstack/identity/v3/roles/urls.go | 4 ++ 7 files changed, 151 insertions(+) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 5e613cae05..be8e73f4e3 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -79,6 +79,20 @@ func TestRoleCRUD(t *testing.T) { tools.PrintResource(t, role) tools.PrintResource(t, role.Extra) + + updateOpts := roles.UpdateOpts{ + Extra: map[string]interface{}{ + "description": "updated test role description", + }, + } + + newRole, err := roles.Update(client, role.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update role: %v", err) + } + + tools.PrintResource(t, newRole) + tools.PrintResource(t, newRole.Extra) } func TestRoleAssignToUserOnProject(t *testing.T) { diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 66fd652cd7..2886a872d8 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -37,6 +37,19 @@ Example to Create a Role panic(err) } +Example to Update a Role + + roleID := "0fe36e73809d46aeae6705c39077b1b3" + + updateOpts := roles.UpdateOpts{ + Name: "read only admin", + } + + role, err := roles.Update(identityClient, roleID, updateOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a Role roleID := "0fe36e73809d46aeae6705c39077b1b3" diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 5bbff77f46..7908baa0e4 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -97,6 +97,52 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToRoleUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts provides options for updating a role. +type UpdateOpts struct { + // Name is the name of the new role. + Name string `json:"name,omitempty"` + + // Extra is free-form extra key/value pairs to describe the role. + Extra map[string]interface{} `json:"-"` +} + +// ToRoleUpdateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToRoleUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "role") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["role"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Update updates an existing Role. +func Update(client *gophercloud.ServiceClient, roleID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToRoleUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(updateURL(client, roleID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // Delete deletes a role. func Delete(client *gophercloud.ServiceClient, roleID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, roleID), nil) diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 878230c667..af8fd9e6a7 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -72,6 +72,12 @@ type CreateResult struct { roleResult } +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Role. +type UpdateResult struct { + roleResult +} + // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index bb171767dd..483788c8d7 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -70,6 +70,32 @@ const CreateRequest = ` } ` +// UpdateRequest provides the input to as Update request. +const UpdateRequest = ` +{ + "role": { + "description": "admin read-only support role" + } +} +` + +// UpdateOutput provides an update result. +const UpdateOutput = ` +{ + "role": { + "domain_id": "1789d1", + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/roles/9fe1d3" + }, + "name": "support", + "extra": { + "description": "admin read-only support role" + } + } +} +` + // FirstRole is the first role in the List request. var FirstRole = roles.Role{ DomainID: "default", @@ -94,6 +120,19 @@ var SecondRole = roles.Role{ }, } +// SecondRoleUpdated is how SecondRole should look after an Update. +var SecondRoleUpdated = roles.Role{ + DomainID: "1789d1", + ID: "9fe1d3", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/roles/9fe1d3", + }, + Name: "support", + Extra: map[string]interface{}{ + "description": "admin read-only support role", + }, +} + // ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. var ExpectedRolesSlice = []roles.Role{FirstRole, SecondRole} @@ -138,6 +177,19 @@ func HandleCreateRoleSuccessfully(t *testing.T) { }) } +// HandleUpdateRoleSuccessfully creates an HTTP handler at `/roles` on the +// test handler mux that tests role update. +func HandleUpdateRoleSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/roles/9fe1d3", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, UpdateRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateOutput) + }) +} + // HandleDeleteRoleSuccessfully creates an HTTP handler at `/roles` on the // test handler mux that tests role deletion. func HandleDeleteRoleSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 635cee662e..2ca4ee4d93 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -74,6 +74,22 @@ func TestCreateRole(t *testing.T) { th.CheckDeepEquals(t, SecondRole, *actual) } +func TestUpdateRole(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateRoleSuccessfully(t) + + updateOpts := roles.UpdateOpts{ + Extra: map[string]interface{}{ + "description": "admin read-only support role", + }, + } + + actual, err := roles.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondRoleUpdated, *actual) +} + func TestDeleteRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index f35dddd1d1..38d592dca6 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -18,6 +18,10 @@ func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(rolePath) } +func updateURL(client *gophercloud.ServiceClient, roleID string) string { + return client.ServiceURL(rolePath, roleID) +} + func deleteURL(client *gophercloud.ServiceClient, roleID string) string { return client.ServiceURL(rolePath, roleID) } From 03c443226b3a445bf9f7ca8b9a1ac9414e767eba Mon Sep 17 00:00:00 2001 From: Iaroslav Akimov Date: Sat, 28 Oct 2017 17:48:22 +0300 Subject: [PATCH 0107/2296] support for sort new syntax (#580) * support for sort new syntax * add comments --- openstack/imageservice/v2/images/requests.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 387e791ceb..081262f1ff 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -45,6 +45,12 @@ type ListOpts struct { // SizeMax filters on the size_max image property. SizeMax int64 `q:"size_max"` + // Sort sorts the results using the new style of sorting. See the OpenStack + // Image API reference for the exact syntax. + // + // Sort cannot be used with the classic sort options (sort_key and sort_dir). + Sort string `q:"sort"` + // SortKey will sort the results based on a specified image property. SortKey string `q:"sort_key"` From 32005faf458e58c2420f32e38a99399ae9ab55f2 Mon Sep 17 00:00:00 2001 From: Eugene Taranov Date: Sat, 28 Oct 2017 18:17:15 +0300 Subject: [PATCH 0108/2296] added attach/detach functions to configurations (#547) * added attach/detach functions to configurations * fixed formatting * fixed var names * fixed test * fixed tests * updated tests * renamed functions; added ConfigurationResult --- openstack/db/v1/instances/requests.go | 14 ++++++++++ openstack/db/v1/instances/results.go | 5 ++++ openstack/db/v1/instances/testing/fixtures.go | 27 +++++++++++++------ .../db/v1/instances/testing/requests_test.go | 18 +++++++++++++ 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go index f8afb7364d..bbd9093182 100644 --- a/openstack/db/v1/instances/requests.go +++ b/openstack/db/v1/instances/requests.go @@ -203,3 +203,17 @@ func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) (r Act _, r.Err = client.Post(actionURL(client, id), &b, nil, nil) return } + +// AttachConfigurationGroup will attach configuration group to the instance +func AttachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string, configID string) (r ConfigurationResult) { + b := map[string]interface{}{"instance": map[string]interface{}{"configuration": configID}} + _, r.Err = client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + return +} + +// DetachConfigurationGroup will dettach configuration group from the instance +func DetachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string) (r ConfigurationResult) { + b := map[string]interface{}{"instance": map[string]interface{}{}} + _, r.Err = client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + return +} diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index 6bfde15030..1a570899e4 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -106,6 +106,11 @@ type DeleteResult struct { gophercloud.ErrResult } +// ConfigurationResult represents the result of a AttachConfigurationGroup/DetachConfigurationGroup operation. +type ConfigurationResult struct { + gophercloud.ErrResult +} + // Extract will extract an Instance from various result structs. func (r commonResult) Extract() (*Instance, error) { var s struct { diff --git a/openstack/db/v1/instances/testing/fixtures.go b/openstack/db/v1/instances/testing/fixtures.go index 9347ee15be..93643c4fbd 100644 --- a/openstack/db/v1/instances/testing/fixtures.go +++ b/openstack/db/v1/instances/testing/fixtures.go @@ -87,17 +87,20 @@ var createReq = ` ` var ( - instanceID = "{instanceID}" - rootURL = "/instances" - resURL = rootURL + "/" + instanceID - uRootURL = resURL + "/root" - aURL = resURL + "/action" + instanceID = "{instanceID}" + configGroupID = "00000000-0000-0000-0000-000000000000" + rootURL = "/instances" + resURL = rootURL + "/" + instanceID + uRootURL = resURL + "/root" + aURL = resURL + "/action" ) var ( - restartReq = `{"restart": {}}` - resizeReq = `{"resize": {"flavorRef": "2"}}` - resizeVolReq = `{"resize": {"volume": {"size": 4}}}` + restartReq = `{"restart": {}}` + resizeReq = `{"resize": {"flavorRef": "2"}}` + resizeVolReq = `{"resize": {"volume": {"size": 4}}}` + attachConfigurationGroupReq = `{"instance": {"configuration": "00000000-0000-0000-0000-000000000000"}}` + detachConfigurationGroupReq = `{"instance": {}}` ) var ( @@ -167,3 +170,11 @@ func HandleResize(t *testing.T) { func HandleResizeVol(t *testing.T) { fixture.SetupHandler(t, aURL, "POST", resizeVolReq, "", 202) } + +func HandleAttachConfigurationGroup(t *testing.T) { + fixture.SetupHandler(t, resURL, "PUT", attachConfigurationGroupReq, "", 202) +} + +func HandleDetachConfigurationGroup(t *testing.T) { + fixture.SetupHandler(t, resURL, "PUT", detachConfigurationGroupReq, "", 202) +} diff --git a/openstack/db/v1/instances/testing/requests_test.go b/openstack/db/v1/instances/testing/requests_test.go index e3c81e34c7..666cdd2e8d 100644 --- a/openstack/db/v1/instances/testing/requests_test.go +++ b/openstack/db/v1/instances/testing/requests_test.go @@ -132,3 +132,21 @@ func TestResizeVolume(t *testing.T) { res := instances.ResizeVolume(fake.ServiceClient(), instanceID, 4) th.AssertNoErr(t, res.Err) } + +func TestAttachConfigurationGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAttachConfigurationGroup(t) + + res := instances.AttachConfigurationGroup(fake.ServiceClient(), instanceID, configGroupID) + th.AssertNoErr(t, res.Err) +} + +func TestDetachConfigurationGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDetachConfigurationGroup(t) + + res := instances.DetachConfigurationGroup(fake.ServiceClient(), instanceID) + th.AssertNoErr(t, res.Err) +} From 9f080ebe7c3ae4b36ab2d1d591cae9c4337955cf Mon Sep 17 00:00:00 2001 From: David Lyle Date: Sat, 28 Oct 2017 23:09:02 -0600 Subject: [PATCH 0109/2296] Updating old role assignment test --- .../identity/v3/roles/testing/fixtures.go | 79 +++++++++++++++++ .../v3/roles/testing/requests_test.go | 88 ++----------------- 2 files changed, 85 insertions(+), 82 deletions(-) diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index 483788c8d7..fa73b11ee0 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -96,6 +96,51 @@ const UpdateOutput = ` } ` +const ListAssignmentOutput = ` +{ + "role_assignments": [ + { + "links": { + "assignment": "http://identity:35357/v3/domains/161718/users/313233/roles/123456" + }, + "role": { + "id": "123456" + }, + "scope": { + "domain": { + "id": "161718" + } + }, + "user": { + "id": "313233" + } + }, + { + "links": { + "assignment": "http://identity:35357/v3/projects/456789/groups/101112/roles/123456", + "membership": "http://identity:35357/v3/groups/101112/users/313233" + }, + "role": { + "id": "123456" + }, + "scope": { + "project": { + "id": "456789" + } + }, + "user": { + "id": "313233" + } + } + ], + "links": { + "self": "http://identity:35357/v3/role_assignments?effective", + "previous": null, + "next": null + } +} +` + // FirstRole is the first role in the List request. var FirstRole = roles.Role{ DomainID: "default", @@ -252,3 +297,37 @@ func HandleUnassignSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// FirstRoleAssignment is the first role assignment in the List request. +var FirstRoleAssignment = roles.RoleAssignment{ + Role: roles.AssignedRole{ID: "123456"}, + Scope: roles.Scope{Domain: roles.Domain{ID: "161718"}}, + User: roles.User{ID: "313233"}, + Group: roles.Group{}, +} + +// SecondRoleAssignemnt is the second role assignemnt in the List request. +var SecondRoleAssignment = roles.RoleAssignment{ + Role: roles.AssignedRole{ID: "123456"}, + Scope: roles.Scope{Project: roles.Project{ID: "456789"}}, + User: roles.User{ID: "313233"}, + Group: roles.Group{}, +} + +// ExpectedRoleAssignmentsSlice is the slice of role assignments expected to be +// returned from ListAssignmentOutput. +var ExpectedRoleAssignmentsSlice = []roles.RoleAssignment{FirstRoleAssignment, SecondRoleAssignment} + +// HandleListRoleAssignmentsSuccessfully creates an HTTP handler at `/role_assignments` on the +// test handler mux that responds with a list of two role assignments. +func HandleListRoleAssignmentsSuccessfully(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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListAssignmentOutput) + }) +} diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 2ca4ee4d93..c683197bfa 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -1,9 +1,6 @@ package testing import ( - "fmt" - "net/http" - "reflect" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" @@ -102,93 +99,20 @@ func TestDeleteRole(t *testing.T) { func TestListAssignmentsSinglePage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - - th.Mux.HandleFunc("/role_assignments", 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, ` - { - "role_assignments": [ - { - "links": { - "assignment": "http://identity:35357/v3/domains/161718/users/313233/roles/123456" - }, - "role": { - "id": "123456" - }, - "scope": { - "domain": { - "id": "161718" - } - }, - "user": { - "id": "313233" - } - }, - { - "links": { - "assignment": "http://identity:35357/v3/projects/456789/groups/101112/roles/123456", - "membership": "http://identity:35357/v3/groups/101112/users/313233" - }, - "role": { - "id": "123456" - }, - "scope": { - "project": { - "id": "456789" - } - }, - "user": { - "id": "313233" - } - } - ], - "links": { - "self": "http://identity:35357/v3/role_assignments?effective", - "previous": null, - "next": null - } - } - `) - }) + HandleListRoleAssignmentsSuccessfully(t) count := 0 err := roles.ListAssignments(client.ServiceClient(), roles.ListAssignmentsOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoleAssignments(page) - if err != nil { - return false, err - } - - expected := []roles.RoleAssignment{ - { - Role: roles.AssignedRole{ID: "123456"}, - Scope: roles.Scope{Domain: roles.Domain{ID: "161718"}}, - User: roles.User{ID: "313233"}, - Group: roles.Group{}, - }, - { - Role: roles.AssignedRole{ID: "123456"}, - Scope: roles.Scope{Project: roles.Project{ID: "456789"}}, - User: roles.User{ID: "313233"}, - Group: roles.Group{}, - }, - } - - if !reflect.DeepEqual(expected, actual) { - t.Errorf("Expected %#v, got %#v", expected, actual) - } + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedRoleAssignmentsSlice, actual) return true, nil }) - if err != nil { - t.Errorf("Unexpected error while paging: %v", err) - } - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) } func TestAssign(t *testing.T) { From fae8b0246b2756965d394b872cf75e6bed788fd8 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Tue, 31 Oct 2017 20:58:02 -0600 Subject: [PATCH 0110/2296] Identity v3: Region list / get --- .../openstack/identity/v3/regions_test.go | 61 +++++++++ openstack/identity/v3/regions/doc.go | 24 ++++ openstack/identity/v3/regions/requests.go | 45 +++++++ openstack/identity/v3/regions/results.go | 111 +++++++++++++++++ .../identity/v3/regions/testing/fixtures.go | 116 ++++++++++++++++++ .../v3/regions/testing/requests_test.go | 54 ++++++++ openstack/identity/v3/regions/urls.go | 11 ++ 7 files changed, 422 insertions(+) create mode 100644 acceptance/openstack/identity/v3/regions_test.go create mode 100644 openstack/identity/v3/regions/doc.go create mode 100644 openstack/identity/v3/regions/requests.go create mode 100644 openstack/identity/v3/regions/results.go create mode 100644 openstack/identity/v3/regions/testing/fixtures.go create mode 100644 openstack/identity/v3/regions/testing/requests_test.go create mode 100644 openstack/identity/v3/regions/urls.go diff --git a/acceptance/openstack/identity/v3/regions_test.go b/acceptance/openstack/identity/v3/regions_test.go new file mode 100644 index 0000000000..202a9793e8 --- /dev/null +++ b/acceptance/openstack/identity/v3/regions_test.go @@ -0,0 +1,61 @@ +// +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/regions" +) + +func TestRegionsList(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + listOpts := regions.ListOpts{ + ParentRegionID: "RegionOne", + } + + allPages, err := regions.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list regions: %v", err) + } + + allRegions, err := regions.ExtractRegions(allPages) + if err != nil { + t.Fatalf("Unable to extract regions: %v", err) + } + + for _, region := range allRegions { + tools.PrintResource(t, region) + } +} + +func TestRegionsGet(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + allPages, err := regions.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list regions: %v", err) + } + + allRegions, err := regions.ExtractRegions(allPages) + if err != nil { + t.Fatalf("Unable to extract regions: %v", err) + } + + region := allRegions[0] + p, err := regions.Get(client, region.ID).Extract() + if err != nil { + t.Fatalf("Unable to get region: %v", err) + } + + tools.PrintResource(t, p) +} diff --git a/openstack/identity/v3/regions/doc.go b/openstack/identity/v3/regions/doc.go new file mode 100644 index 0000000000..3e3bcf7ccc --- /dev/null +++ b/openstack/identity/v3/regions/doc.go @@ -0,0 +1,24 @@ +/* +Package regions manages and retrieves Regions in the OpenStack Identity Service. + +Example to List Regions + + listOpts := regions.ListOpts{ + ParentRegionID: "RegionOne", + } + + allPages, err := regions.List(identityClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allRegions, err := regions.ExtractRegions(allPages) + if err != nil { + panic(err) + } + + for _, region := range allRegions { + fmt.Printf("%+v\n", region) + } +*/ +package regions diff --git a/openstack/identity/v3/regions/requests.go b/openstack/identity/v3/regions/requests.go new file mode 100644 index 0000000000..e1f8795264 --- /dev/null +++ b/openstack/identity/v3/regions/requests.go @@ -0,0 +1,45 @@ +package regions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToRegionListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // ParentRegionID filters the response by a parent region ID. + ParentRegionID string `q:"parent_region_id"` +} + +// ToRegionListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToRegionListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the Regions to which the current token has access. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToRegionListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return RegionPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details on a single region, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go new file mode 100644 index 0000000000..31c1da0392 --- /dev/null +++ b/openstack/identity/v3/regions/results.go @@ -0,0 +1,111 @@ +package regions + +import ( + "encoding/json" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/internal" + "github.com/gophercloud/gophercloud/pagination" +) + +// Region helps manage related users. +type Region struct { + // Description describes the region purpose. + Description string `json:"description"` + + // ID is the unique ID of the region. + ID string `json:"id"` + + // Extra is a collection of miscellaneous key/values. + Extra map[string]interface{} `json:"-"` + + // Links contains referencing links to the region. + Links map[string]interface{} `json:"links"` + + // Name is the ID of the parent region. + ParentRegionID string `json:"parent_region_id"` +} + +func (r *Region) UnmarshalJSON(b []byte) error { + type tmp Region + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Region(s.tmp) + + // Collect other fields and bundle them into Extra + // but only if a field titled "extra" wasn't sent. + if s.Extra != nil { + r.Extra = s.Extra + } else { + var result interface{} + err := json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = internal.RemainingKeys(Region{}, resultMap) + } + } + + return err +} + +type regionResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Region. +type GetResult struct { + regionResult +} + +// RegionPage is a single page of Region results. +type RegionPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Regions contains any results. +func (r RegionPage) IsEmpty() (bool, error) { + regions, err := ExtractRegions(r) + return len(regions) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r RegionPage) 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 +} + +// ExtractRegions returns a slice of Regions contained in a single page of results. +func ExtractRegions(r pagination.Page) ([]Region, error) { + var s struct { + Regions []Region `json:"regions"` + } + err := (r.(RegionPage)).ExtractInto(&s) + return s.Regions, err +} + +// Extract interprets any region results as a Region. +func (r regionResult) Extract() (*Region, error) { + var s struct { + Region *Region `json:"region"` + } + err := r.ExtractInto(&s) + return s.Region, err +} diff --git a/openstack/identity/v3/regions/testing/fixtures.go b/openstack/identity/v3/regions/testing/fixtures.go new file mode 100644 index 0000000000..2615d6845a --- /dev/null +++ b/openstack/identity/v3/regions/testing/fixtures.go @@ -0,0 +1,116 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of Region results. +const ListOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/regions" + }, + "regions": [ + { + "id": "RegionOne-East", + "description": "East sub-region of RegionOne", + "links": { + "self": "http://example.com/identity/v3/regions/RegionOne-East" + }, + "parent_region_id": "RegionOne" + }, + { + "id": "RegionOne-West", + "description": "West sub-region of RegionOne", + "links": { + "self": "https://example.com/identity/v3/regions/RegionOne-West" + }, + "extra": { + "email": "westsupport@example.com" + }, + "parent_region_id": "RegionOne" + } + ] +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "region": { + "id": "RegionOne-West", + "description": "West sub-region of RegionOne", + "links": { + "self": "https://example.com/identity/v3/regions/RegionOne-West" + }, + "name": "support", + "extra": { + "email": "westsupport@example.com" + }, + "parent_region_id": "RegionOne" + } +} +` + +// FirstRegion is the first region in the List request. +var FirstRegion = regions.Region{ + ID: "RegionOne-East", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/regions/RegionOne-East", + }, + Description: "East sub-region of RegionOne", + Extra: map[string]interface{}{}, + ParentRegionID: "RegionOne", +} + +// SecondRegion is the second region in the List request. +var SecondRegion = regions.Region{ + ID: "RegionOne-West", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/regions/RegionOne-West", + }, + Description: "West sub-region of RegionOne", + Extra: map[string]interface{}{ + "email": "westsupport@example.com", + }, + ParentRegionID: "RegionOne", +} + +// ExpectedRegionsSlice is the slice of regions expected to be returned from ListOutput. +var ExpectedRegionsSlice = []regions.Region{FirstRegion, SecondRegion} + +// HandleListRegionsSuccessfully creates an HTTP handler at `/regions` on the +// test handler mux that responds with a list of two regions. +func HandleListRegionsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/regions", 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) + }) +} + +// HandleGetRegionSuccessfully creates an HTTP handler at `/regions` on the +// test handler mux that responds with a single region. +func HandleGetRegionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/regions/RegionOne-West", 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/regions/testing/requests_test.go b/openstack/identity/v3/regions/testing/requests_test.go new file mode 100644 index 0000000000..7aa15d74e1 --- /dev/null +++ b/openstack/identity/v3/regions/testing/requests_test.go @@ -0,0 +1,54 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListRegions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRegionsSuccessfully(t) + + count := 0 + err := regions.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := regions.ExtractRegions(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedRegionsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListRegionsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRegionsSuccessfully(t) + + allPages, err := regions.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := regions.ExtractRegions(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRegionsSlice, actual) + th.AssertEquals(t, ExpectedRegionsSlice[1].Extra["email"], "westsupport@example.com") +} + +func TestGetRegion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetRegionSuccessfully(t) + + actual, err := regions.Get(client.ServiceClient(), "RegionOne-West").Extract() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondRegion, *actual) +} diff --git a/openstack/identity/v3/regions/urls.go b/openstack/identity/v3/regions/urls.go new file mode 100644 index 0000000000..8043bf53fe --- /dev/null +++ b/openstack/identity/v3/regions/urls.go @@ -0,0 +1,11 @@ +package regions + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("regions") +} + +func getURL(client *gophercloud.ServiceClient, regionID string) string { + return client.ServiceURL("regions", regionID) +} From 754e57e7867bfa48062ff2a237caad538085700a Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 2 Nov 2017 23:02:05 -0500 Subject: [PATCH 0111/2296] add NewLoadBalancerV2 service client (#591) * add NewLoadBalancerV2 service client * add go file to internal/testing pkg to appease go1.9 build * fixes to appease upstream changes to 'go vet' --- internal/testing/pkg.go | 1 + openstack/client.go | 8 ++++++++ openstack/compute/v2/flavors/requests.go | 1 - pagination/pager.go | 1 - 4 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 internal/testing/pkg.go diff --git a/internal/testing/pkg.go b/internal/testing/pkg.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/internal/testing/pkg.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/client.go b/openstack/client.go index 77214caca3..adcf90bcef 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -346,3 +346,11 @@ func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi sc.ResourceBase = sc.Endpoint + "v2/" return sc, err } + +// NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2 +// load balancer service. +func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "load-balancer") + sc.ResourceBase = sc.Endpoint + "v2.0/" + return sc, err +} diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 317fd8530c..0fcaff9662 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -43,7 +43,6 @@ const ( the Marker for the current call. */ type ListOpts struct { - // ChangesSince, if provided, instructs List to return only those things which // have changed since the timestamp provided. ChangesSince string `q:"changes-since"` diff --git a/pagination/pager.go b/pagination/pager.go index 6f1609ef2e..7c65926b72 100644 --- a/pagination/pager.go +++ b/pagination/pager.go @@ -22,7 +22,6 @@ var ( // Depending on the pagination strategy of a particular resource, there may be an additional subinterface that the result type // will need to implement. type Page interface { - // NextPageURL generates the URL for the page of data that follows this collection. // Return "" if no such page exists. NextPageURL() (string, error) From f363989fd857de12a423eb7fd441bf7a41c55d9f Mon Sep 17 00:00:00 2001 From: liusheng Date: Fri, 3 Nov 2017 11:32:41 +0800 Subject: [PATCH 0112/2296] Request integrating a new CI system to Gophercloud I'd like to request to integrate a new CI system which built based on zuul[1] and nodepool[2] tools to Gophercloud repo. It is for running Gophercloud tests(both unit tests and acceptance tests) based on a devstack[3] environment. For now, my colleagues and I have basicially finish the CI system building, and have tested with Gophercloud, it can work OK, please see[4]. We will long-term maintain the CI system and would like to propose to intergrate with Gophercloud official repo. FYI, the zuul jobs definition can be found in [5], the zuul jobs status web page is [6], the test jobs log server is [7]. [1] https://docs.openstack.org/infra/zuul/ [2] https://docs.openstack.org/infra/nodepool/ [3] https://docs.openstack.org/devstack/latest/ [4] https://github.com/theopenlab/gophercloud/pull/5 [5] https://github.com/theopenlab/openlab-zuul-jobs [6] http://80.158.20.68/ [7] http://80.158.20.68/logs/ For #592 --- .zuul.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .zuul.yaml diff --git a/.zuul.yaml b/.zuul.yaml new file mode 100644 index 0000000000..c259d03e18 --- /dev/null +++ b/.zuul.yaml @@ -0,0 +1,12 @@ +- project: + name: gophercloud/gophercloud + check: + jobs: + - gophercloud-unittest + - gophercloud-acceptance-test + recheck-mitaka: + jobs: + - gophercloud-acceptance-test-mitaka + recheck-pike: + jobs: + - gophercloud-acceptance-test-pike From c4570693b3d174324e8ab4f03bd16899018275f6 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Wed, 1 Nov 2017 14:21:31 -0600 Subject: [PATCH 0113/2296] Identity v3 Region create --- acceptance/openstack/identity/v3/identity.go | 28 ++++++++++ .../openstack/identity/v3/regions_test.go | 24 +++++++++ openstack/identity/v3/regions/doc.go | 15 ++++++ openstack/identity/v3/regions/requests.go | 52 +++++++++++++++++++ openstack/identity/v3/regions/results.go | 6 +++ .../identity/v3/regions/testing/fixtures.go | 25 +++++++++ .../v3/regions/testing/requests_test.go | 19 +++++++ openstack/identity/v3/regions/urls.go | 4 ++ 8 files changed, 173 insertions(+) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 2577297811..20a776bce6 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" ) @@ -147,6 +148,33 @@ func CreateRole(t *testing.T, client *gophercloud.ServiceClient, c *roles.Create return role, nil } +// CreateRegion will create a region with a random name. +// It takes an optional createOpts parameter since creating a region +// has so many options. An error will be returned if the region was +// unable to be created. +func CreateRegion(t *testing.T, client *gophercloud.ServiceClient, c *regions.CreateOpts) (*regions.Region, error) { + id := tools.RandomString("ACPTTEST", 8) + t.Logf("Attempting to create region: %s", id) + + var createOpts regions.CreateOpts + if c != nil { + createOpts = *c + } else { + createOpts = regions.CreateOpts{} + } + + createOpts.ID = id + + region, err := regions.Create(client, createOpts).Extract() + if err != nil { + return region, err + } + + t.Logf("Successfully created region %s", id) + + return region, nil +} + // DeleteProject will delete a project by ID. A fatal error will occur if // the project ID failed to be deleted. This works best when using it as // a deferred function. diff --git a/acceptance/openstack/identity/v3/regions_test.go b/acceptance/openstack/identity/v3/regions_test.go index 202a9793e8..aeb0b7e501 100644 --- a/acceptance/openstack/identity/v3/regions_test.go +++ b/acceptance/openstack/identity/v3/regions_test.go @@ -59,3 +59,27 @@ func TestRegionsGet(t *testing.T) { tools.PrintResource(t, p) } + +func TestRegionsCRUD(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + createOpts := regions.CreateOpts{ + ID: "testregion", + Description: "Region for testing", + Extra: map[string]interface{}{ + "email": "testregion@example.com", + }, + } + + // Create region in the default domain + region, err := CreateRegion(t, client, &createOpts) + if err != nil { + t.Fatalf("Unable to create region: %v", err) + } + + tools.PrintResource(t, region) + tools.PrintResource(t, region.Extra) +} diff --git a/openstack/identity/v3/regions/doc.go b/openstack/identity/v3/regions/doc.go index 3e3bcf7ccc..8e07805a95 100644 --- a/openstack/identity/v3/regions/doc.go +++ b/openstack/identity/v3/regions/doc.go @@ -20,5 +20,20 @@ Example to List Regions for _, region := range allRegions { fmt.Printf("%+v\n", region) } + +Example to Create a Region + + createOpts := regions.CreateOpts{ + ID: "TestRegion", + Description: "Region for testing" + Extra: map[string]interface{}{ + "email": "testregionsupport@example.com", + } + } + + region, err := regions.Create(identityClient, createOpts).Extract() + if err != nil { + panic(err) + } */ package regions diff --git a/openstack/identity/v3/regions/requests.go b/openstack/identity/v3/regions/requests.go index e1f8795264..5b41922f50 100644 --- a/openstack/identity/v3/regions/requests.go +++ b/openstack/identity/v3/regions/requests.go @@ -43,3 +43,55 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToRegionCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a region. +type CreateOpts struct { + // ID is the ID of the new region. + ID string `json:"id,omitempty"` + + // Description is a description of the region. + Description string `json:"description,omitempty"` + + // ParentRegionID is the ID of the parent the region to add this region under. + ParentRegionID string `json:"parent_region_id,omitempty"` + + // Extra is free-form extra key/value pairs to describe the region. + Extra map[string]interface{} `json:"-"` +} + +// ToRegionCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToRegionCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "region") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["region"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Create creates a new Region. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToRegionCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go index 31c1da0392..d7b12d4280 100644 --- a/openstack/identity/v3/regions/results.go +++ b/openstack/identity/v3/regions/results.go @@ -66,6 +66,12 @@ type GetResult struct { regionResult } +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Region. +type CreateResult struct { + regionResult +} + // RegionPage is a single page of Region results. type RegionPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/regions/testing/fixtures.go b/openstack/identity/v3/regions/testing/fixtures.go index 2615d6845a..2b3f120102 100644 --- a/openstack/identity/v3/regions/testing/fixtures.go +++ b/openstack/identity/v3/regions/testing/fixtures.go @@ -60,6 +60,18 @@ const GetOutput = ` } ` +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "region": { + "id": "RegionOne-West", + "description": "West sub-region of RegionOne", + "email": "westsupport@example.com", + "parent_region_id": "RegionOne" + } +} +` + // FirstRegion is the first region in the List request. var FirstRegion = regions.Region{ ID: "RegionOne-East", @@ -114,3 +126,16 @@ func HandleGetRegionSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleCreateRegionSuccessfully creates an HTTP handler at `/regions` on the +// test handler mux that tests region creation. +func HandleCreateRegionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/regions", 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, GetOutput) + }) +} diff --git a/openstack/identity/v3/regions/testing/requests_test.go b/openstack/identity/v3/regions/testing/requests_test.go index 7aa15d74e1..0515f1e52f 100644 --- a/openstack/identity/v3/regions/testing/requests_test.go +++ b/openstack/identity/v3/regions/testing/requests_test.go @@ -52,3 +52,22 @@ func TestGetRegion(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegion, *actual) } + +func TestCreateRegion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateRegionSuccessfully(t) + + createOpts := regions.CreateOpts{ + ID: "RegionOne-West", + Description: "West sub-region of RegionOne", + Extra: map[string]interface{}{ + "email": "westsupport@example.com", + }, + ParentRegionID: "RegionOne", + } + + actual, err := regions.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondRegion, *actual) +} diff --git a/openstack/identity/v3/regions/urls.go b/openstack/identity/v3/regions/urls.go index 8043bf53fe..9f3e3bcdf8 100644 --- a/openstack/identity/v3/regions/urls.go +++ b/openstack/identity/v3/regions/urls.go @@ -9,3 +9,7 @@ func listURL(client *gophercloud.ServiceClient) string { func getURL(client *gophercloud.ServiceClient, regionID string) string { return client.ServiceURL("regions", regionID) } + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("regions") +} From 8676c7b69c3132e40f54c34de217460aa08aef92 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Wed, 1 Nov 2017 15:04:35 -0600 Subject: [PATCH 0114/2296] Identity v3 region delete --- acceptance/openstack/identity/v3/identity.go | 12 ++++++++++++ acceptance/openstack/identity/v3/regions_test.go | 1 + openstack/identity/v3/regions/doc.go | 6 ++++++ openstack/identity/v3/regions/requests.go | 6 ++++++ openstack/identity/v3/regions/results.go | 6 ++++++ openstack/identity/v3/regions/testing/fixtures.go | 11 +++++++++++ .../identity/v3/regions/testing/requests_test.go | 9 +++++++++ openstack/identity/v3/regions/urls.go | 4 ++++ 8 files changed, 55 insertions(+) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 20a776bce6..1156a58cc9 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -235,6 +235,18 @@ func DeleteRole(t *testing.T, client *gophercloud.ServiceClient, roleID string) t.Logf("Deleted role: %s", roleID) } +// DeleteRegion will delete a reg by ID. A fatal error will occur if +// the role failed to be deleted. This works best when using it as +// a deferred function. +func DeleteRegion(t *testing.T, client *gophercloud.ServiceClient, regionID string) { + err := regions.Delete(client, regionID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete region %s: %v", regionID, err) + } + + t.Logf("Deleted region: %s", regionID) +} + // UnassignRole will delete a role assigned to a user/group on a project/domain // A fatal error will occur if it fails to delete the assignment. // This works best when using it as a deferred function. diff --git a/acceptance/openstack/identity/v3/regions_test.go b/acceptance/openstack/identity/v3/regions_test.go index aeb0b7e501..b0c22705f0 100644 --- a/acceptance/openstack/identity/v3/regions_test.go +++ b/acceptance/openstack/identity/v3/regions_test.go @@ -79,6 +79,7 @@ func TestRegionsCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create region: %v", err) } + defer DeleteRegion(t, client, region.ID) tools.PrintResource(t, region) tools.PrintResource(t, region.Extra) diff --git a/openstack/identity/v3/regions/doc.go b/openstack/identity/v3/regions/doc.go index 8e07805a95..394856556c 100644 --- a/openstack/identity/v3/regions/doc.go +++ b/openstack/identity/v3/regions/doc.go @@ -34,6 +34,12 @@ Example to Create a Region region, err := regions.Create(identityClient, createOpts).Extract() if err != nil { panic(err) + +Example to Delete a Region + + regionID := "TestRegion" + err := regions.Delete(identityClient, regionID).ExtractErr() + if err != nil { } */ package regions diff --git a/openstack/identity/v3/regions/requests.go b/openstack/identity/v3/regions/requests.go index 5b41922f50..7e05e327cc 100644 --- a/openstack/identity/v3/regions/requests.go +++ b/openstack/identity/v3/regions/requests.go @@ -95,3 +95,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// Delete deletes a region. +func Delete(client *gophercloud.ServiceClient, regionID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, regionID), nil) + return +} diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go index d7b12d4280..5966217a2a 100644 --- a/openstack/identity/v3/regions/results.go +++ b/openstack/identity/v3/regions/results.go @@ -72,6 +72,12 @@ type CreateResult struct { regionResult } +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // RegionPage is a single page of Region results. type RegionPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/regions/testing/fixtures.go b/openstack/identity/v3/regions/testing/fixtures.go index 2b3f120102..2472c71fdd 100644 --- a/openstack/identity/v3/regions/testing/fixtures.go +++ b/openstack/identity/v3/regions/testing/fixtures.go @@ -139,3 +139,14 @@ func HandleCreateRegionSuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleDeleteRegionSuccessfully creates an HTTP handler at `/regions` on the +// test handler mux that tests region deletion. +func HandleDeleteRegionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/regions/RegionOne-West", 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/regions/testing/requests_test.go b/openstack/identity/v3/regions/testing/requests_test.go index 0515f1e52f..3b31169ac9 100644 --- a/openstack/identity/v3/regions/testing/requests_test.go +++ b/openstack/identity/v3/regions/testing/requests_test.go @@ -71,3 +71,12 @@ func TestCreateRegion(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegion, *actual) } + +func TestDeleteRegion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteRegionSuccessfully(t) + + res := regions.Delete(client.ServiceClient(), "RegionOne-West") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/regions/urls.go b/openstack/identity/v3/regions/urls.go index 9f3e3bcdf8..6015f06a6a 100644 --- a/openstack/identity/v3/regions/urls.go +++ b/openstack/identity/v3/regions/urls.go @@ -13,3 +13,7 @@ func getURL(client *gophercloud.ServiceClient, regionID string) string { func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("regions") } + +func deleteURL(client *gophercloud.ServiceClient, regionID string) string { + return client.ServiceURL("regions", regionID) +} From 9c8172293509b8bae285f37772cbdb6898d356ed Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 4 Nov 2017 12:28:05 -0600 Subject: [PATCH 0115/2296] Compute v2: flavor access list (#507) * Compute v2: Flavor Access List * Compute v2: Flavor Access List unit test * Compute v2: Flavor Access List acceptance test * Compute v2: Rename Access to Accesses * Compute v2: Using pagination for ListAccesses --- acceptance/openstack/compute/v2/compute.go | 25 ++++++++++++ .../openstack/compute/v2/flavors_test.go | 27 +++++++++++++ openstack/compute/v2/flavors/doc.go | 18 +++++++++ openstack/compute/v2/flavors/requests.go | 9 +++++ openstack/compute/v2/flavors/results.go | 31 +++++++++++++++ .../v2/flavors/testing/requests_test.go | 38 +++++++++++++++++++ openstack/compute/v2/flavors/urls.go | 4 ++ 7 files changed, 152 insertions(+) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 2ff9e88027..5ac111ee28 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -268,6 +268,31 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, return newServer, nil } +// CreatePrivateFlavor will create a private flavor with a random name. +// An error will be returned if the flavor could not be created. +func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) { + flavorName := tools.RandomString("flavor_", 5) + t.Logf("Attempting to create flavor %s", flavorName) + + isPublic := false + createOpts := flavors.CreateOpts{ + Name: flavorName, + RAM: 1, + VCPUs: 1, + Disk: gophercloud.IntToPointer(1), + IsPublic: &isPublic, + } + + flavor, err := flavors.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created flavor %s", flavor.ID) + + return flavor, nil +} + // CreateSecurityGroup will create a security group with a random name. // An error will be returned if one was failed to be created. func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (secgroups.SecurityGroup, error) { diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index 004571fc8a..0fd0158d9e 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -87,3 +87,30 @@ func TestFlavorCreateDelete(t *testing.T) { tools.PrintResource(t, flavor) } + +func TestFlavorAccessesList(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + flavor, err := CreatePrivateFlavor(t, client) + if err != nil { + t.Fatalf("Unable to create flavor: %v", err) + } + defer DeleteFlavor(t, client, flavor) + + allPages, err := flavors.ListAccesses(client, flavor.ID).AllPages() + if err != nil { + t.Fatalf("Unable to list flavor accesses: %v", err) + } + + allAccesses, err := flavors.ExtractAccesses(allPages) + if err != nil { + t.Fatalf("Unable to extract accesses: %v", err) + } + + for _, access := range allAccesses { + tools.PrintResource(t, access) + } +} diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 2661cfac20..a7bc15c3e5 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -41,5 +41,23 @@ Example to Create a Flavor if err != nil { panic(err) } + +Example to List Flavor Access + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages() + if err != nil { + panic(err) + } + + allAccesses, err := flavors.ExtractAccesses(allPages) + if err != nil { + panic(err) + } + + for _, access := range allAccesses { + fmt.Printf("%+v", access) + } */ package flavors diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 0fcaff9662..6eb3678b2b 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -153,6 +153,15 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { return } +// ListAccesses retrieves the tenants which have access to a flavor. +func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager { + url := accessURL(client, id) + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessPage{pagination.SinglePageBase(r)} + }) +} + // IDFromName is a convienience function that returns a flavor's ID given its // name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index fda11d3e06..47d58ccfb3 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -12,6 +12,8 @@ type commonResult struct { gophercloud.Result } +// CreateResult is the response of a Get operations. Call its Extract method to +// interpret it as a Flavor. type CreateResult struct { commonResult } @@ -131,3 +133,32 @@ func ExtractFlavors(r pagination.Page) ([]Flavor, error) { err := (r.(FlavorPage)).ExtractInto(&s) return s.Flavors, err } + +// AccessPage contains a single page of all FlavorAccess entries for a flavor. +type AccessPage struct { + pagination.SinglePageBase +} + +// IsEmpty indicates whether an AccessPage is empty. +func (page AccessPage) IsEmpty() (bool, error) { + v, err := ExtractAccesses(page) + return len(v) == 0, err +} + +// ExtractAccesses interprets a page of results as a slice of FlavorAccess. +func ExtractAccesses(r pagination.Page) ([]FlavorAccess, error) { + var s struct { + FlavorAccesses []FlavorAccess `json:"flavor_access"` + } + err := (r.(AccessPage)).ExtractInto(&s) + return s.FlavorAccesses, err +} + +// FlavorAccess represents an ACL of tenant access to a specific Flavor. +type FlavorAccess struct { + // FlavorID is the unique ID of the flavor. + FlavorID string `json:"flavor_id"` + + // TenantID is the unique ID of the tenant. + TenantID string `json:"tenant_id"` +} diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index e098ed648e..9b49924237 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -212,3 +212,41 @@ func TestDeleteFlavor(t *testing.T) { res := flavors.Delete(fake.ServiceClient(), "12345678") th.AssertNoErr(t, res.Err) } + +func TestFlavorAccessesList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/flavors/12345678/os-flavor-access", 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, ` + { + "flavor_access": [ + { + "flavor_id": "12345678", + "tenant_id": "2f954bcf047c4ee9b09a37d49ae6db54" + } + ] + } + `) + }) + + expected := []flavors.FlavorAccess{ + flavors.FlavorAccess{ + FlavorID: "12345678", + TenantID: "2f954bcf047c4ee9b09a37d49ae6db54", + }, + } + + allPages, err := flavors.ListAccesses(fake.ServiceClient(), "12345678").AllPages() + th.AssertNoErr(t, err) + + actual, err := flavors.ExtractAccesses(allPages) + th.AssertNoErr(t, err) + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go index 518d05b369..04d33bf127 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -19,3 +19,7 @@ func createURL(client *gophercloud.ServiceClient) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id) } + +func accessURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id, "os-flavor-access") +} From 6e5b7d64ea59ae3b8fdf0456e73e6c7638200e31 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Mon, 6 Nov 2017 22:57:50 -0700 Subject: [PATCH 0116/2296] Identity v3 Region update (#594) * Identity v3 region update * Identity v3: Region update back out extra support --- .../openstack/identity/v3/regions_test.go | 21 +++++ openstack/identity/v3/regions/doc.go | 18 +++++ openstack/identity/v3/regions/requests.go | 61 +++++++++++++++ openstack/identity/v3/regions/results.go | 8 +- .../identity/v3/regions/testing/fixtures.go | 76 +++++++++++++++++++ .../v3/regions/testing/requests_test.go | 23 ++++++ openstack/identity/v3/regions/urls.go | 4 + 7 files changed, 210 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/identity/v3/regions_test.go b/acceptance/openstack/identity/v3/regions_test.go index b0c22705f0..f98c232314 100644 --- a/acceptance/openstack/identity/v3/regions_test.go +++ b/acceptance/openstack/identity/v3/regions_test.go @@ -83,4 +83,25 @@ func TestRegionsCRUD(t *testing.T) { tools.PrintResource(t, region) tools.PrintResource(t, region.Extra) + + updateOpts := regions.UpdateOpts{ + Description: "Region A for testing", + /* + // Due to a bug in Keystone, the Extra column of the Region table + // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 + // The following lines should be uncommented once the fix is merged. + + Extra: map[string]interface{}{ + "email": "testregionA@example.com", + }, + */ + } + + newRegion, err := regions.Update(client, region.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update region: %v", err) + } + + tools.PrintResource(t, newRegion) + tools.PrintResource(t, newRegion.Extra) } diff --git a/openstack/identity/v3/regions/doc.go b/openstack/identity/v3/regions/doc.go index 394856556c..a37b05a544 100644 --- a/openstack/identity/v3/regions/doc.go +++ b/openstack/identity/v3/regions/doc.go @@ -34,12 +34,30 @@ Example to Create a Region region, err := regions.Create(identityClient, createOpts).Extract() if err != nil { panic(err) + } + +Example to Update a Region + + regionID := "TestRegion" + + // There is currently a bug in Keystone where updating the optional Extras + // attributes set in regions.Create is not supported, see: + // https://bugs.launchpad.net/keystone/+bug/1729933 + updateOpts := regions.UpdateOpts{ + Description: "Updated Description for region", + } + + region, err := regions.Update(identityClient, regionID, updateOpts).Extract() + if err != nil { + panic(err) + } Example to Delete a Region regionID := "TestRegion" err := regions.Delete(identityClient, regionID).ExtractErr() if err != nil { + panic(err) } */ package regions diff --git a/openstack/identity/v3/regions/requests.go b/openstack/identity/v3/regions/requests.go index 7e05e327cc..aa588fdff4 100644 --- a/openstack/identity/v3/regions/requests.go +++ b/openstack/identity/v3/regions/requests.go @@ -96,6 +96,67 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToRegionUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts provides options for updating a region. +type UpdateOpts struct { + // Description is a description of the region. + Description string `json:"description,omitempty"` + + // ParentRegionID is the ID of the parent region. + ParentRegionID string `json:"parent_region_id,omitempty"` + + /* + // Due to a bug in Keystone, the Extra column of the Region table + // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 + // The following lines should be uncommented once the fix is merged. + + // Extra is free-form extra key/value pairs to describe the region. + Extra map[string]interface{} `json:"-"` + */ +} + +// ToRegionUpdateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToRegionUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "region") + if err != nil { + return nil, err + } + + /* + // Due to a bug in Keystone, the Extra column of the Region table + // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 + // The following lines should be uncommented once the fix is merged. + + if opts.Extra != nil { + if v, ok := b["region"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + */ + + return b, nil +} + +// Update updates an existing Region. +func Update(client *gophercloud.ServiceClient, regionID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToRegionUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(updateURL(client, regionID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // Delete deletes a region. func Delete(client *gophercloud.ServiceClient, regionID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, regionID), nil) diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go index 5966217a2a..a60cb34883 100644 --- a/openstack/identity/v3/regions/results.go +++ b/openstack/identity/v3/regions/results.go @@ -22,7 +22,7 @@ type Region struct { // Links contains referencing links to the region. Links map[string]interface{} `json:"links"` - // Name is the ID of the parent region. + // ParentRegionID is the ID of the parent region. ParentRegionID string `json:"parent_region_id"` } @@ -72,6 +72,12 @@ type CreateResult struct { regionResult } +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Region. +type UpdateResult struct { + regionResult +} + // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { diff --git a/openstack/identity/v3/regions/testing/fixtures.go b/openstack/identity/v3/regions/testing/fixtures.go index 2472c71fdd..dee57c52af 100644 --- a/openstack/identity/v3/regions/testing/fixtures.go +++ b/openstack/identity/v3/regions/testing/fixtures.go @@ -72,6 +72,48 @@ const CreateRequest = ` } ` +/* + // Due to a bug in Keystone, the Extra column of the Region table + // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 + // The following line should be added to region in UpdateRequest once the + // fix is merged. + + "email": "1stwestsupport@example.com" +*/ +// UpdateRequest provides the input to as Update request. +const UpdateRequest = ` +{ + "region": { + "description": "First West sub-region of RegionOne" + } +} +` + +/* + // Due to a bug in Keystone, the Extra column of the Region table + // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 + // This following line should replace the email in UpdateOutput.extra once + // the fix is merged. + + "email": "1stwestsupport@example.com" +*/ +// UpdateOutput provides an update result. +const UpdateOutput = ` +{ + "region": { + "id": "RegionOne-West", + "links": { + "self": "https://example.com/identity/v3/regions/RegionOne-West" + }, + "description": "First West sub-region of RegionOne", + "extra": { + "email": "westsupport@example.com" + }, + "parent_region_id": "RegionOne" + } +} +` + // FirstRegion is the first region in the List request. var FirstRegion = regions.Region{ ID: "RegionOne-East", @@ -96,6 +138,27 @@ var SecondRegion = regions.Region{ ParentRegionID: "RegionOne", } +/* + // Due to a bug in Keystone, the Extra column of the Region table + // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 + // This should replace the email in SecondRegionUpdated.Extra once the fix + // is merged. + + "email": "1stwestsupport@example.com" +*/ +// SecondRegionUpdated is the second region in the List request. +var SecondRegionUpdated = regions.Region{ + ID: "RegionOne-West", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/regions/RegionOne-West", + }, + Description: "First West sub-region of RegionOne", + Extra: map[string]interface{}{ + "email": "westsupport@example.com", + }, + ParentRegionID: "RegionOne", +} + // ExpectedRegionsSlice is the slice of regions expected to be returned from ListOutput. var ExpectedRegionsSlice = []regions.Region{FirstRegion, SecondRegion} @@ -140,6 +203,19 @@ func HandleCreateRegionSuccessfully(t *testing.T) { }) } +// HandleUpdateRegionSuccessfully creates an HTTP handler at `/regions` on the +// test handler mux that tests region update. +func HandleUpdateRegionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/regions/RegionOne-West", 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) + }) +} + // HandleDeleteRegionSuccessfully creates an HTTP handler at `/regions` on the // test handler mux that tests region deletion. func HandleDeleteRegionSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/regions/testing/requests_test.go b/openstack/identity/v3/regions/testing/requests_test.go index 3b31169ac9..7f57557048 100644 --- a/openstack/identity/v3/regions/testing/requests_test.go +++ b/openstack/identity/v3/regions/testing/requests_test.go @@ -72,6 +72,29 @@ func TestCreateRegion(t *testing.T) { th.CheckDeepEquals(t, SecondRegion, *actual) } +func TestUpdateRegion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateRegionSuccessfully(t) + + updateOpts := regions.UpdateOpts{ + Description: "First West sub-region of RegionOne", + /* + // Due to a bug in Keystone, the Extra column of the Region table + // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 + // The following lines should be uncommented once the fix is merged. + + Extra: map[string]interface{}{ + "email": "1stwestsupport@example.com", + }, + */ + } + + actual, err := regions.Update(client.ServiceClient(), "RegionOne-West", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondRegionUpdated, *actual) +} + func TestDeleteRegion(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/regions/urls.go b/openstack/identity/v3/regions/urls.go index 6015f06a6a..150ecc8358 100644 --- a/openstack/identity/v3/regions/urls.go +++ b/openstack/identity/v3/regions/urls.go @@ -14,6 +14,10 @@ func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("regions") } +func updateURL(client *gophercloud.ServiceClient, regionID string) string { + return client.ServiceURL("regions", regionID) +} + func deleteURL(client *gophercloud.ServiceClient, regionID string) string { return client.ServiceURL("regions", regionID) } From 32beead056feb2df22742b9d29cd64e99e67513b Mon Sep 17 00:00:00 2001 From: jianqli Date: Fri, 10 Nov 2017 15:33:18 +0800 Subject: [PATCH 0117/2296] fix token issue which results in endless loop on reauth --- provider_client.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/provider_client.go b/provider_client.go index f88682381d..01b3010739 100644 --- a/provider_client.go +++ b/provider_client.go @@ -145,10 +145,6 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } req.Header.Set("Accept", applicationJSON) - for k, v := range client.AuthenticatedHeaders() { - req.Header.Add(k, v) - } - // Set the User-Agent header req.Header.Set("User-Agent", client.UserAgent.Join()) @@ -162,6 +158,11 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } } + // get latest token from client + for k, v := range client.AuthenticatedHeaders() { + req.Header.Set(k, v) + } + // Set connection parameter to close the connection immediately when we've got the response req.Close = true From a043441f16dfaf905739e313528ba613ea5387fe Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 2 Nov 2017 13:46:27 +0300 Subject: [PATCH 0118/2296] fixed bug with endless loop when using delimiter on folded directory --- openstack/objectstorage/v1/objects/results.go | 4 +++- .../v1/objects/testing/fixtures.go | 19 ++++++++++++++++++- .../v1/objects/testing/requests_test.go | 4 ++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index f19b8f4aa5..b600169b9e 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -98,7 +98,9 @@ func ExtractNames(r pagination.Page) ([]string, error) { names := make([]string, 0, len(parsed)) for _, object := range parsed { - names = append(names, object.Name) + if object.Name != "" { + names = append(names, object.Name) + } } return names, nil diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 08faab89a6..6426e26ac9 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -43,6 +43,13 @@ var ExpectedListInfo = []objects.Object{ Name: "hello", ContentType: "application/octet-stream", }, + { + Hash: "d41d8cd98f00b204e9800998ecf8427e", + LastModified: time.Date(2016, time.August, 17, 22, 11, 58, 602650000, time.UTC), + Bytes: 0, + Name: "directory", + ContentType: "application/directory", + }, } // ExpectedListNames is the result expected from a call to `List` when just @@ -76,9 +83,19 @@ func HandleListObjectsInfoSuccessfully(t *testing.T) { "bytes": 14, "name": "hello", "content_type": "application/octet-stream" + }, + { + "hash": "d41d8cd98f00b204e9800998ecf8427e", + "last_modified": "2016-08-17T22:11:58.602650", + "bytes": 0, + "name": "directory", + "content_type": "application\/directory" + }, + { + "subdir": "directory\/" } ]`) - case "hello": + case "directory": fmt.Fprintf(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 4f2663212a..66b631fdf0 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -59,7 +59,7 @@ func TestListObjectInfo(t *testing.T) { HandleListObjectsInfoSuccessfully(t) count := 0 - options := &objects.ListOpts{Full: true} + options := &objects.ListOpts{Full: true, Prefix: "", Delimiter: "/"} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractInfo(page) @@ -79,7 +79,7 @@ func TestListObjectNames(t *testing.T) { HandleListObjectNamesSuccessfully(t) count := 0 - options := &objects.ListOpts{Full: false} + options := &objects.ListOpts{Full: false, Prefix: "", Delimiter: "/"} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) From 755794a71939d9d60d230926dea83476e2318512 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 4 Nov 2017 04:31:43 +0000 Subject: [PATCH 0119/2296] ObjectStorage v1: Subdir and Marker detection This commit adds the Subdir field to objects.Object. A subdir denotes a pseudo-directory within a container when the List options request enumeration of such directories. Marker detection has also been updated to provide correct paging when subdirs exist. --- .../objectstorage/v1/objects_test.go | 119 ++++++++++++++++++ openstack/objectstorage/v1/objects/results.go | 63 +++++++++- .../v1/objects/testing/fixtures.go | 47 ++++--- .../v1/objects/testing/requests_test.go | 43 ++++++- 4 files changed, 251 insertions(+), 21 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index 9ab26c4abe..1335d0cbc1 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -136,3 +136,122 @@ func TestObjects(t *testing.T) { } } } + +func TestObjectsListSubdir(t *testing.T) { + client, err := clients.NewObjectStorageV1Client() + if err != nil { + t.Fatalf("Unable to create client: %v", err) + } + + // Create a random subdirectory name. + cSubdir1 := tools.RandomString("test-subdir-", 8) + cSubdir2 := tools.RandomString("test-subdir-", 8) + + // Make a slice of length numObjects to hold the random object names. + oNames1 := make([]string, numObjects) + for i := 0; i < len(oNames1); i++ { + oNames1[i] = cSubdir1 + "/" + tools.RandomString("test-object-", 8) + } + + oNames2 := make([]string, numObjects) + for i := 0; i < len(oNames2); i++ { + oNames2[i] = cSubdir2 + "/" + tools.RandomString("test-object-", 8) + } + + // Create a container to hold the test objects. + cName := tools.RandomString("test-container-", 8) + _, err = containers.Create(client, cName, nil).Extract() + th.AssertNoErr(t, err) + + // Defer deletion of the container until after testing. + defer func() { + t.Logf("Deleting container %s", cName) + res := containers.Delete(client, cName) + th.AssertNoErr(t, res.Err) + }() + + // Create a slice of buffers to hold the test object content. + oContents1 := make([]*bytes.Buffer, numObjects) + for i := 0; i < numObjects; i++ { + oContents1[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + createOpts := objects.CreateOpts{ + Content: oContents1[i], + } + res := objects.Create(client, cName, oNames1[i], createOpts) + th.AssertNoErr(t, res.Err) + } + // Delete the objects after testing. + defer func() { + for i := 0; i < numObjects; i++ { + t.Logf("Deleting object %s", oNames1[i]) + res := objects.Delete(client, cName, oNames1[i], nil) + th.AssertNoErr(t, res.Err) + } + }() + + oContents2 := make([]*bytes.Buffer, numObjects) + for i := 0; i < numObjects; i++ { + oContents2[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + createOpts := objects.CreateOpts{ + Content: oContents2[i], + } + res := objects.Create(client, cName, oNames2[i], createOpts) + th.AssertNoErr(t, res.Err) + } + // Delete the objects after testing. + defer func() { + for i := 0; i < numObjects; i++ { + t.Logf("Deleting object %s", oNames2[i]) + res := objects.Delete(client, cName, oNames2[i], nil) + th.AssertNoErr(t, res.Err) + } + }() + + listOpts := objects.ListOpts{ + Full: true, + Delimiter: "/", + } + + allPages, err := objects.List(client, cName, listOpts).AllPages() + if err != nil { + t.Fatal(err) + } + + allObjects, err := objects.ExtractNames(allPages) + if err != nil { + t.Fatal(err) + } + + t.Logf("%#v\n", allObjects) + expected := []string{cSubdir1, cSubdir2} + for _, e := range expected { + var valid bool + for _, a := range allObjects { + if e+"/" == a { + valid = true + } + } + if !valid { + t.Fatalf("could not find %s in results", e) + } + } + + listOpts = objects.ListOpts{ + Full: true, + Delimiter: "/", + Prefix: cSubdir2, + } + + allPages, err = objects.List(client, cName, listOpts).AllPages() + if err != nil { + t.Fatal(err) + } + + allObjects, err = objects.ExtractNames(allPages) + if err != nil { + t.Fatal(err) + } + + th.AssertEquals(t, allObjects[0], cSubdir2+"/") + t.Logf("%#v\n", allObjects) +} diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index b600169b9e..af06c2f836 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/ioutil" + "net/url" "strconv" "strings" "time" @@ -30,6 +31,9 @@ type Object struct { // Name is the unique name for the object. Name string `json:"name"` + + // Subdir denotes if the result contains a subdir. + Subdir string `json:"subdir"` } func (r *Object) UnmarshalJSON(b []byte) error { @@ -66,14 +70,63 @@ func (r ObjectPage) IsEmpty() (bool, error) { // LastMarker returns the last object name in a ListResult. func (r ObjectPage) LastMarker() (string, error) { - names, err := ExtractNames(r) + return ExtractLastMarker(r) +} + +// ExtractLastMarker is a function that takes a page of objects and returns the +// marker for the page. This can either be a subdir or the last object's name. +func ExtractLastMarker(r pagination.Page) (string, error) { + casted := r.(ObjectPage) + + // If a delimiter was requested, check if a subdir exists. + queryParams, err := url.ParseQuery(casted.URL.RawQuery) if err != nil { return "", err } - if len(names) == 0 { + + var delimeter bool + if v, ok := queryParams["delimiter"]; ok && len(v) > 0 { + delimeter = true + } + + ct := casted.Header.Get("Content-Type") + switch { + case strings.HasPrefix(ct, "application/json"): + parsed, err := ExtractInfo(r) + if err != nil { + return "", err + } + + var lastObject Object + if len(parsed) > 0 { + lastObject = parsed[len(parsed)-1] + } + + if !delimeter { + return lastObject.Name, nil + } + + if lastObject.Name != "" { + return lastObject.Name, nil + } + + return lastObject.Subdir, nil + case strings.HasPrefix(ct, "text/plain"): + names := make([]string, 0, 50) + + body := string(r.(ObjectPage).Body.([]uint8)) + for _, name := range strings.Split(body, "\n") { + if len(name) > 0 { + names = append(names, name) + } + } + + return names[len(names)-1], err + case strings.HasPrefix(ct, "text/html"): return "", nil + default: + return "", fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct) } - return names[len(names)-1], nil } // ExtractInfo is a function that takes a page of objects and returns their @@ -98,7 +151,9 @@ func ExtractNames(r pagination.Page) ([]string, error) { names := make([]string, 0, len(parsed)) for _, object := range parsed { - if object.Name != "" { + if object.Subdir != "" { + names = append(names, object.Subdir) + } else { names = append(names, object.Name) } } diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 6426e26ac9..a6b7e571e1 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -43,12 +43,13 @@ var ExpectedListInfo = []objects.Object{ Name: "hello", ContentType: "application/octet-stream", }, +} + +// ExpectedListSubdir is the result expected from a call to `List` when full +// info is requested. +var ExpectedListSubdir = []objects.Object{ { - Hash: "d41d8cd98f00b204e9800998ecf8427e", - LastModified: time.Date(2016, time.August, 17, 22, 11, 58, 602650000, time.UTC), - Bytes: 0, - Name: "directory", - ContentType: "application/directory", + Subdir: "directory/", }, } @@ -83,19 +84,35 @@ func HandleListObjectsInfoSuccessfully(t *testing.T) { "bytes": 14, "name": "hello", "content_type": "application/octet-stream" - }, - { - "hash": "d41d8cd98f00b204e9800998ecf8427e", - "last_modified": "2016-08-17T22:11:58.602650", - "bytes": 0, - "name": "directory", - "content_type": "application\/directory" - }, + } + ]`) + case "hello": + fmt.Fprintf(w, `[]`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +// HandleListSubdirSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that +// responds with a `List` response when full info is requested. +func HandleListSubdirSuccessfully(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", "application/json") + + w.Header().Set("Content-Type", "application/json") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, `[ { - "subdir": "directory\/" + "subdir": "directory/" } ]`) - case "directory": + case "directory/": fmt.Fprintf(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 66b631fdf0..6825d34426 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -59,7 +59,7 @@ func TestListObjectInfo(t *testing.T) { HandleListObjectsInfoSuccessfully(t) count := 0 - options := &objects.ListOpts{Full: true, Prefix: "", Delimiter: "/"} + options := &objects.ListOpts{Full: true} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractInfo(page) @@ -73,13 +73,34 @@ func TestListObjectInfo(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListObjectSubdir(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSubdirSuccessfully(t) + + count := 0 + options := &objects.ListOpts{Full: true, Prefix: "", Delimiter: "/"} + err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := objects.ExtractInfo(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedListSubdir, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + func TestListObjectNames(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListObjectNamesSuccessfully(t) + // Check without delimiter. count := 0 - options := &objects.ListOpts{Full: false, Prefix: "", Delimiter: "/"} + 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) @@ -94,6 +115,24 @@ func TestListObjectNames(t *testing.T) { }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) + + // Check with delimiter. + count = 0 + options = &objects.ListOpts{Full: false, Delimiter: "/"} + 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, ExpectedListNames, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) } func TestCreateObject(t *testing.T) { From d63799f5d704803813b82b31f4d02e31ce903fea Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 11 Nov 2017 19:18:09 +0000 Subject: [PATCH 0120/2296] Compute v2: Server Fault field --- openstack/compute/v2/servers/results.go | 10 ++ .../compute/v2/servers/testing/fixtures.go | 104 ++++++++++++++++++ .../v2/servers/testing/requests_test.go | 16 +++ 3 files changed, 130 insertions(+) diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 7ef80e92e2..c6c1ff43f7 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -211,6 +211,16 @@ type Server struct { // SecurityGroups includes the security groups that this instance has applied // to it. SecurityGroups []map[string]interface{} `json:"security_groups"` + + // Fault contains failure information about a server. + Fault Fault `json:"fault"` +} + +type Fault struct { + Code int `json:"code"` + Created time.Time `json:"created"` + Details string `json:"details"` + Message string `json:"message"` } func (r *Server) UnmarshalJSON(b []byte) error { diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index f77dfb44ee..baec93efbe 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -298,6 +298,89 @@ const SingleServerBody = ` } ` +// FaultyServerBody is the body of a Get request on an existing server +// which has a fault/error. +const FaultyServerBody = ` +{ + "server": { + "status": "ACTIVE", + "updated": "2014-09-25T13:04:49Z", + "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", + "OS-EXT-SRV-ATTR:host": "devstack", + "addresses": { + "private": [ + { + "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", + "version": 4, + "addr": "10.0.0.31", + "OS-EXT-IPS:type": "fixed" + } + ] + }, + "links": [ + { + "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", + "rel": "self" + }, + { + "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", + "rel": "bookmark" + } + ], + "key_name": null, + "image": { + "id": "f90f6034-2570-4974-8351-6b49732ef2eb", + "links": [ + { + "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", + "rel": "bookmark" + } + ] + }, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", + "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", + "rel": "bookmark" + } + ] + }, + "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba", + "security_groups": [ + { + "name": "default" + } + ], + "OS-SRV-USG:terminated_at": null, + "OS-EXT-AZ:availability_zone": "nova", + "user_id": "9349aff8be7545ac9d2f1d00999a23cd", + "name": "derp", + "created": "2014-09-25T13:04:41Z", + "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", + "OS-DCF:diskConfig": "MANUAL", + "os-extended-volumes:volumes_attached": [], + "accessIPv4": "", + "accessIPv6": "", + "progress": 0, + "OS-EXT-STS:power_state": 1, + "config_drive": "", + "metadata": {}, + "fault": { + "message": "Conflict updating instance c2ce4dea-b73f-4d01-8633-2c6032869281. Expected: {'task_state': [u'spawning']}. Actual: {'task_state': None}", + "code": 500, + "created": "2017-11-11T07:58:39Z", + "details": "Stock details for test" + } + } +} +` + const ServerPasswordBody = ` { "password": "xlozO3wLCBRWAa2yDjCCVx8vwNPypxnypmRYDa/zErlQ+EzPe1S/Gz6nfmC52mOlOSCRuUOmG7kqqgejPof6M7bOezS387zjq4LSvvwp28zUknzy4YzfFGhnHAdai3TxUJ26pfQCYrq8UTzmKF2Bq8ioSEtVVzM0A96pDh8W2i7BOz6MdoiVyiev/I1K2LsuipfxSJR7Wdke4zNXJjHHP2RfYsVbZ/k9ANu+Nz4iIH8/7Cacud/pphH7EjrY6a4RZNrjQskrhKYed0YERpotyjYk1eDtRe72GrSiXteqCM4biaQ5w3ruS+AcX//PXk3uJ5kC7d67fPXaVz4WaQRYMg==" @@ -470,6 +553,15 @@ var ( }, }, } + + faultTimeCreated, _ = time.Parse(time.RFC3339, "2017-11-11T07:58:39Z") + DerpFault = servers.Fault{ + Code: 500, + Created: faultTimeCreated, + Details: "Stock details for test", + Message: "Conflict updating instance c2ce4dea-b73f-4d01-8633-2c6032869281. " + + "Expected: {'task_state': [u'spawning']}. Actual: {'task_state': None}", + } ) type CreateOptsWithCustomField struct { @@ -707,6 +799,18 @@ func HandleServerGetSuccessfully(t *testing.T) { }) } +// HandleServerGetFaultSuccessfully sets up the test server to respond to a server Get +// request which contains a fault. +func HandleServerGetFaultSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/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, FaultyServerBody) + }) +} + // HandleServerUpdateSuccessfully sets up the test server to respond to a server Update request. func HandleServerUpdateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 361db95ef9..c832129910 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -213,6 +213,22 @@ func TestGetServer(t *testing.T) { th.CheckDeepEquals(t, ServerDerp, *actual) } +func TestGetFaultyServer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleServerGetFaultSuccessfully(t) + + client := client.ServiceClient() + actual, err := servers.Get(client, "1234asdf").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + FaultyServer := ServerDerp + FaultyServer.Fault = DerpFault + th.CheckDeepEquals(t, FaultyServer, *actual) +} + func TestGetServerWithExtensions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From a72b601c0f4945af5572f2597b620c722639a06e Mon Sep 17 00:00:00 2001 From: Wang Bo Date: Tue, 14 Nov 2017 22:30:08 -0600 Subject: [PATCH 0121/2296] Improve function description about microversion (#585) explicitly state that microversion is necessary parameter --- openstack/sharedfilesystems/v2/shares/requests.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 6eb31932f9..27bc43b563 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -78,7 +78,8 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// GetExportLocations will get shareID's export locations +// GetExportLocations will get shareID's export locations. +// Client must have Microversion set; minimum supported microversion for GetExportLocations is 2.14. func GetExportLocations(client *gophercloud.ServiceClient, id string) (r GetExportLocationsResult) { _, r.Err = client.Get(getExportLocationsURL(client, id), &r.Body, nil) return @@ -110,6 +111,7 @@ func (opts GrantAccessOpts) ToGrantAccessMap() (map[string]interface{}, error) { // GrantAccess will grant access to a Share based on the values in GrantAccessOpts. To extract // the GrantAccess object from the response, call the Extract method on the GrantAccessResult. +// Client must have Microversion set; minimum supported microversion for GrantAccess is 2.7. func GrantAccess(client *gophercloud.ServiceClient, id string, opts GrantAccessOptsBuilder) (r GrantAccessResult) { b, err := opts.ToGrantAccessMap() if err != nil { From ff03cc04453fa68d7a21e6663b823eb54f2b7b32 Mon Sep 17 00:00:00 2001 From: Nate Johnston Date: Thu, 16 Nov 2017 22:02:16 -0500 Subject: [PATCH 0122/2296] Compute v2 - Add Get Detailed Quota for Tenant (#549) (#564) * Compute v2 - Add Get Detailed Quota for Tenant (#549) * Add GetQuotaDetailed function for the os-quota-sets extension in Gophercloud's OpenStack Compute v2 API - Allows a caller to get the list of quota types with in_use, reserved, and limit values for each - Effectively wraps OpenStack Compute v2 API function /v2/os-quota-sets/{tenant_id}/detail - Based on implementation of GetQuota Note: In line with "GetQuota" uses sample response body from https://developer.openstack.org/api-ref/compute/#show-the-detail-of-quota for test Code citation: https://github.com/openstack/nova/blob/stable/pike/nova/api/openstack/compute/quota_sets.py#L119 For #549 * fixed PR comments * fixed details url * fixed compiler/test errors --- .../compute/v2/extensions/quotasets/doc.go | 9 ++ .../v2/extensions/quotasets/requests.go | 7 ++ .../v2/extensions/quotasets/results.go | 83 ++++++++++++++++ .../extensions/quotasets/testing/fixtures.go | 96 +++++++++++++++++++ .../quotasets/testing/requests_test.go | 9 ++ .../compute/v2/extensions/quotasets/urls.go | 4 + 6 files changed, 208 insertions(+) diff --git a/openstack/compute/v2/extensions/quotasets/doc.go b/openstack/compute/v2/extensions/quotasets/doc.go index 4fe89fb3d7..04d9887a14 100644 --- a/openstack/compute/v2/extensions/quotasets/doc.go +++ b/openstack/compute/v2/extensions/quotasets/doc.go @@ -10,6 +10,15 @@ Example to Get a Quota Set fmt.Printf("%+v\n", quotaset) +Example to Get a Detailed Quota Set + + quotaset, err := quotasets.GetDetail(computeClient, "tenant-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + Example to Update a Quota Set updateOpts := quotasets.UpdateOpts{ diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go index d303535b3b..3105171835 100644 --- a/openstack/compute/v2/extensions/quotasets/requests.go +++ b/openstack/compute/v2/extensions/quotasets/requests.go @@ -11,6 +11,13 @@ func Get(client *gophercloud.ServiceClient, tenantID string) GetResult { return res } +// GetDetail returns detailed public data about a previously created QuotaSet. +func GetDetail(client *gophercloud.ServiceClient, tenantID string) GetDetailResult { + var res GetDetailResult + _, res.Err = client.Get(getDetailURL(client, tenantID), &res.Body, nil) + return res +} + // Updates the quotas for the given tenantID and returns the new QuotaSet. func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (res UpdateResult) { reqBody, err := opts.ToComputeQuotaUpdateMap() diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go index 361a2dd1a7..080169d581 100644 --- a/openstack/compute/v2/extensions/quotasets/results.go +++ b/openstack/compute/v2/extensions/quotasets/results.go @@ -55,6 +55,69 @@ type QuotaSet struct { ServerGroupMembers int `json:"server_group_members"` } +// QuotaDetailSet represents details of both operational limits of compute +// resources and the current usage of those resources. +type QuotaDetailSet struct { + // ID is the tenant ID associated with this QuotaDetailSet. + ID string `json:"id"` + + // FixedIps is number of fixed ips alloted this QuotaDetailSet. + FixedIps QuotaDetail `json:"fixed_ips"` + + // FloatingIps is number of floating ips alloted this QuotaDetailSet. + FloatingIps QuotaDetail `json:"floating_ips"` + + // InjectedFileContentBytes is the allowed bytes for each injected file. + InjectedFileContentBytes QuotaDetail `json:"injected_file_content_bytes"` + + // InjectedFilePathBytes is allowed bytes for each injected file path. + InjectedFilePathBytes QuotaDetail `json:"injected_file_path_bytes"` + + // InjectedFiles is the number of injected files allowed for each project. + InjectedFiles QuotaDetail `json:"injected_files"` + + // KeyPairs is number of ssh keypairs. + KeyPairs QuotaDetail `json:"key_pairs"` + + // MetadataItems is number of metadata items allowed for each instance. + MetadataItems QuotaDetail `json:"metadata_items"` + + // RAM is megabytes allowed for each instance. + RAM QuotaDetail `json:"ram"` + + // SecurityGroupRules is number of security group rules allowed for each + // security group. + SecurityGroupRules QuotaDetail `json:"security_group_rules"` + + // SecurityGroups is the number of security groups allowed for each project. + SecurityGroups QuotaDetail `json:"security_groups"` + + // Cores is number of instance cores allowed for each project. + Cores QuotaDetail `json:"cores"` + + // Instances is number of instances allowed for each project. + Instances QuotaDetail `json:"instances"` + + // ServerGroups is the number of ServerGroups allowed for the project. + ServerGroups QuotaDetail `json:"server_groups"` + + // ServerGroupMembers is the number of members for each ServerGroup. + ServerGroupMembers QuotaDetail `json:"server_group_members"` +} + +// QuotaDetail is a set of details about a single operational limit that allows +// for control of compute 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 @@ -106,3 +169,23 @@ type UpdateResult struct { type DeleteResult struct { quotaResult } + +type quotaDetailResult struct { + gophercloud.Result +} + +// GetDetailResult is the response from a Get operation. Call its Extract method to interpret it +// as a QuotaSet. +type GetDetailResult struct { + quotaDetailResult +} + +// ExtractDetails is a method that attempts to interpret any QuotaDetailSet +// resource response as an array of QuotaDetailSet structs. +func (r quotaDetailResult) Extract() (QuotaDetailSet, error) { + var s struct { + QuotaData QuotaDetailSet `json:"quota_set"` + } + err := r.ExtractInto(&s) + return s.QuotaData, err +} diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go index 004d7e8ae7..3fdf2b72fc 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go +++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go @@ -30,6 +30,75 @@ const GetOutput = ` } } ` + +// GetDetailsOutput is a sample response to a Get call with the detailed option. +const GetDetailsOutput = ` +{ + "quota_set" : { + "id": "555544443333222211110000ffffeeee", + "instances" : { + "in_use": 0, + "limit": 25, + "reserved": 0 + }, + "security_groups" : { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "security_group_rules" : { + "in_use": 0, + "limit": 20, + "reserved": 0 + }, + "cores" : { + "in_use": 0, + "limit": 200, + "reserved": 0 + }, + "injected_file_content_bytes" : { + "in_use": 0, + "limit": 10240, + "reserved": 0 + }, + "injected_files" : { + "in_use": 0, + "limit": 5, + "reserved": 0 + }, + "metadata_items" : { + "in_use": 0, + "limit": 128, + "reserved": 0 + }, + "ram" : { + "in_use": 0, + "limit": 200000, + "reserved": 0 + }, + "key_pairs" : { + "in_use": 0, + "limit": 10, + "reserved": 0 + }, + "injected_file_path_bytes" : { + "in_use": 0, + "limit": 255, + "reserved": 0 + }, + "server_groups" : { + "in_use": 0, + "limit": 2, + "reserved": 0 + }, + "server_group_members" : { + "in_use": 0, + "limit": 3, + "reserved": 0 + } + } +} +` const FirstTenantID = "555544443333222211110000ffffeeee" // FirstQuotaSet is the first result in ListOutput. @@ -50,6 +119,23 @@ var FirstQuotaSet = quotasets.QuotaSet{ ServerGroupMembers: 3, } +// FirstQuotaDetailsSet is the first result in ListOutput. +var FirstQuotaDetailsSet = quotasets.QuotaDetailSet{ + ID: FirstTenantID, + InjectedFileContentBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10240}, + InjectedFilePathBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 255}, + InjectedFiles: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 5}, + KeyPairs: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10}, + MetadataItems: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 128}, + RAM: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 200000}, + SecurityGroupRules: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 20}, + SecurityGroups: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10}, + Cores: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 200}, + Instances: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 25}, + ServerGroups: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 2}, + ServerGroupMembers: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 3}, +} + //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":200000,"security_group_rules":20,"security_groups":10,"server_groups":2,"server_group_members":3}}` @@ -85,6 +171,16 @@ func HandleGetSuccessfully(t *testing.T) { }) } +// HandleGetDetailSuccessfully configures the test server to respond to a Get Details request for sample tenant +func HandleGetDetailSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID+"/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") + fmt.Fprintf(w, GetDetailsOutput) + }) +} + // HandlePutSuccessfully configures the test server to respond to a Put request for sample tenant func HandlePutSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go index dd45630b2b..ccac51b364 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go +++ b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go @@ -19,6 +19,15 @@ func TestGet(t *testing.T) { th.CheckDeepEquals(t, &FirstQuotaSet, actual) } +func TestGetDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetDetailSuccessfully(t) + actual, err := quotasets.GetDetail(client.ServiceClient(), FirstTenantID).Extract() + th.CheckDeepEquals(t, FirstQuotaDetailsSet, actual) + th.AssertNoErr(t, err) +} + func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/compute/v2/extensions/quotasets/urls.go b/openstack/compute/v2/extensions/quotasets/urls.go index 64190e980b..37e50215b5 100644 --- a/openstack/compute/v2/extensions/quotasets/urls.go +++ b/openstack/compute/v2/extensions/quotasets/urls.go @@ -12,6 +12,10 @@ 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, "detail") +} + func updateURL(c *gophercloud.ServiceClient, tenantID string) string { return getURL(c, tenantID) } From 2fa126160676d15da90ff098a795eb9bd395eeb1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 17 Nov 2017 03:08:04 +0000 Subject: [PATCH 0123/2296] Compute v2: Rename IP and RAM quota set fields --- acceptance/openstack/compute/v2/compute.go | 6 +++--- .../openstack/compute/v2/quotaset_test.go | 12 +++++------ .../v2/extensions/quotasets/requests.go | 12 +++++------ .../v2/extensions/quotasets/results.go | 20 +++++++++---------- .../extensions/quotasets/testing/fixtures.go | 12 +++++------ 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 5ac111ee28..38c355bc93 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -810,13 +810,13 @@ func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Ser //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 + dest.FixedIPs = &src.FixedIPs + dest.FloatingIPs = &src.FloatingIPs dest.InjectedFileContentBytes = &src.InjectedFileContentBytes dest.InjectedFilePathBytes = &src.InjectedFilePathBytes dest.InjectedFiles = &src.InjectedFiles dest.KeyPairs = &src.KeyPairs - dest.Ram = &src.Ram + dest.RAM = &src.RAM dest.SecurityGroupRules = &src.SecurityGroupRules dest.SecurityGroups = &src.SecurityGroups dest.Cores = &src.Cores diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go index 126bc239db..28f2be1a88 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -79,14 +79,14 @@ func getTenantIDByName(t *testing.T, client *gophercloud.ServiceClient, name str //What will be sent as desired Quotas to the Server var UpdatQuotaOpts = quotasets.UpdateOpts{ - FixedIps: gophercloud.IntToPointer(10), - FloatingIps: gophercloud.IntToPointer(10), + FixedIPs: gophercloud.IntToPointer(10), + FloatingIPs: gophercloud.IntToPointer(10), InjectedFileContentBytes: gophercloud.IntToPointer(10240), InjectedFilePathBytes: gophercloud.IntToPointer(255), InjectedFiles: gophercloud.IntToPointer(5), KeyPairs: gophercloud.IntToPointer(10), MetadataItems: gophercloud.IntToPointer(128), - Ram: gophercloud.IntToPointer(20000), + RAM: gophercloud.IntToPointer(20000), SecurityGroupRules: gophercloud.IntToPointer(20), SecurityGroups: gophercloud.IntToPointer(10), Cores: gophercloud.IntToPointer(10), @@ -97,14 +97,14 @@ var UpdatQuotaOpts = quotasets.UpdateOpts{ //What the Server hopefully returns as the new Quotas var UpdatedQuotas = quotasets.QuotaSet{ - FixedIps: 10, - FloatingIps: 10, + FixedIPs: 10, + FloatingIPs: 10, InjectedFileContentBytes: 10240, InjectedFilePathBytes: 255, InjectedFiles: 5, KeyPairs: 10, MetadataItems: 128, - Ram: 20000, + RAM: 20000, SecurityGroupRules: 20, SecurityGroups: 10, Cores: 10, diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go index 3105171835..34d935893e 100644 --- a/openstack/compute/v2/extensions/quotasets/requests.go +++ b/openstack/compute/v2/extensions/quotasets/requests.go @@ -40,11 +40,11 @@ func Delete(client *gophercloud.ServiceClient, tenantID string) (res DeleteResul // 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 { - // FixedIps is number of fixed ips alloted this quota_set. - FixedIps *int `json:"fixed_ips,omitempty"` + // FixedIPs is number of fixed ips alloted this quota_set. + FixedIPs *int `json:"fixed_ips,omitempty"` - // FloatingIps is number of floating ips alloted this quota_set. - FloatingIps *int `json:"floating_ips,omitempty"` + // FloatingIPs is number of floating ips alloted this quota_set. + FloatingIPs *int `json:"floating_ips,omitempty"` // InjectedFileContentBytes is content bytes allowed for each injected file. InjectedFileContentBytes *int `json:"injected_file_content_bytes,omitempty"` @@ -61,8 +61,8 @@ type UpdateOpts struct { // MetadataItems is number of metadata items allowed for each instance. MetadataItems *int `json:"metadata_items,omitempty"` - // Ram is megabytes allowed for each instance. - Ram *int `json:"ram,omitempty"` + // RAM is megabytes allowed for each instance. + RAM *int `json:"ram,omitempty"` // SecurityGroupRules is rules allowed for each security group. SecurityGroupRules *int `json:"security_group_rules,omitempty"` diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go index 080169d581..af53dbc4fd 100644 --- a/openstack/compute/v2/extensions/quotasets/results.go +++ b/openstack/compute/v2/extensions/quotasets/results.go @@ -11,11 +11,11 @@ type QuotaSet struct { // ID is tenant associated with this QuotaSet. ID string `json:"id"` - // FixedIps is number of fixed ips alloted this QuotaSet. - FixedIps int `json:"fixed_ips"` + // FixedIPs is number of fixed ips alloted this QuotaSet. + FixedIPs int `json:"fixed_ips"` - // FloatingIps is number of floating ips alloted this QuotaSet. - FloatingIps int `json:"floating_ips"` + // FloatingIPs is number of floating ips alloted this QuotaSet. + FloatingIPs int `json:"floating_ips"` // InjectedFileContentBytes is the allowed bytes for each injected file. InjectedFileContentBytes int `json:"injected_file_content_bytes"` @@ -32,8 +32,8 @@ type QuotaSet struct { // MetadataItems is number of metadata items allowed for each instance. MetadataItems int `json:"metadata_items"` - // Ram is megabytes allowed for each instance. - Ram int `json:"ram"` + // RAM is megabytes allowed for each instance. + RAM int `json:"ram"` // SecurityGroupRules is number of security group rules allowed for each // security group. @@ -61,11 +61,11 @@ type QuotaDetailSet struct { // ID is the tenant ID associated with this QuotaDetailSet. ID string `json:"id"` - // FixedIps is number of fixed ips alloted this QuotaDetailSet. - FixedIps QuotaDetail `json:"fixed_ips"` + // FixedIPs is number of fixed ips alloted this QuotaDetailSet. + FixedIPs QuotaDetail `json:"fixed_ips"` - // FloatingIps is number of floating ips alloted this QuotaDetailSet. - FloatingIps QuotaDetail `json:"floating_ips"` + // FloatingIPs is number of floating ips alloted this QuotaDetailSet. + FloatingIPs QuotaDetail `json:"floating_ips"` // InjectedFileContentBytes is the allowed bytes for each injected file. InjectedFileContentBytes QuotaDetail `json:"injected_file_content_bytes"` diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go index 3fdf2b72fc..53516413db 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go +++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go @@ -103,14 +103,14 @@ const FirstTenantID = "555544443333222211110000ffffeeee" // FirstQuotaSet is the first result in ListOutput. var FirstQuotaSet = quotasets.QuotaSet{ - FixedIps: 0, - FloatingIps: 0, + FixedIPs: 0, + FloatingIPs: 0, InjectedFileContentBytes: 10240, InjectedFilePathBytes: 255, InjectedFiles: 5, KeyPairs: 10, MetadataItems: 128, - Ram: 200000, + RAM: 200000, SecurityGroupRules: 20, SecurityGroups: 10, Cores: 200, @@ -144,14 +144,14 @@ const PartialUpdateBody = `{"quota_set":{"cores":200, "force":true}}` //Result of Quota-update var UpdatedQuotaSet = quotasets.UpdateOpts{ - FixedIps: gophercloud.IntToPointer(0), - FloatingIps: gophercloud.IntToPointer(0), + FixedIPs: gophercloud.IntToPointer(0), + FloatingIPs: gophercloud.IntToPointer(0), InjectedFileContentBytes: gophercloud.IntToPointer(10240), InjectedFilePathBytes: gophercloud.IntToPointer(255), InjectedFiles: gophercloud.IntToPointer(5), KeyPairs: gophercloud.IntToPointer(10), MetadataItems: gophercloud.IntToPointer(128), - Ram: gophercloud.IntToPointer(200000), + RAM: gophercloud.IntToPointer(200000), SecurityGroupRules: gophercloud.IntToPointer(20), SecurityGroups: gophercloud.IntToPointer(10), Cores: gophercloud.IntToPointer(200), From cace757875bf9ca750282dc496d33a22b24f253a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 17 Nov 2017 03:12:31 +0000 Subject: [PATCH 0124/2296] Compute v2: quotaset godoc updates --- .../compute/v2/extensions/quotasets/results.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go index af53dbc4fd..e38868a637 100644 --- a/openstack/compute/v2/extensions/quotasets/results.go +++ b/openstack/compute/v2/extensions/quotasets/results.go @@ -108,13 +108,16 @@ type QuotaDetailSet struct { // QuotaDetail is a set of details about a single operational limit that allows // for control of compute usage. type QuotaDetail struct { - // InUse is the current number of provisioned/allocated resources of the given type. + // 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 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 is the maximum number of a given resource that can be + // allocated/provisioned. This is what "quota" usually refers to. Limit int `json:"limit"` } @@ -174,14 +177,14 @@ type quotaDetailResult struct { gophercloud.Result } -// GetDetailResult is the response from a Get operation. Call its Extract method to interpret it -// as a QuotaSet. +// GetDetailResult is the response from a Get operation. Call its Extract +// method to interpret it as a QuotaSet. type GetDetailResult struct { quotaDetailResult } -// ExtractDetails is a method that attempts to interpret any QuotaDetailSet -// resource response as an array of QuotaDetailSet structs. +// Extract is a method that attempts to interpret any QuotaDetailSet +// resource response as a set of QuotaDetailSet structs. func (r quotaDetailResult) Extract() (QuotaDetailSet, error) { var s struct { QuotaData QuotaDetailSet `json:"quota_set"` From b47265a60452b8c6b31422c18482bcddc9ffc89e Mon Sep 17 00:00:00 2001 From: David Lyle Date: Wed, 15 Nov 2017 16:27:32 -0700 Subject: [PATCH 0125/2296] Identity v3 fix/update Service define and create --- acceptance/openstack/identity/v3/identity.go | 42 +++- .../openstack/identity/v3/service_test.go | 24 ++ openstack/identity/v3/services/doc.go | 10 +- openstack/identity/v3/services/requests.go | 48 +++- openstack/identity/v3/services/results.go | 73 ++++++- .../identity/v3/services/testing/fixtures.go | 206 ++++++++++++++++++ .../v3/services/testing/requests_test.go | 153 +++---------- openstack/identity/v3/services/urls.go | 4 + 8 files changed, 417 insertions(+), 143 deletions(-) create mode 100644 openstack/identity/v3/services/testing/fixtures.go diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 1156a58cc9..ae7560dd58 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" + "github.com/gophercloud/gophercloud/openstack/identity/v3/services" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" ) @@ -175,6 +176,33 @@ func CreateRegion(t *testing.T, client *gophercloud.ServiceClient, c *regions.Cr return region, nil } +// CreateService will create a service with a random name. +// It takes an optional createOpts parameter since creating a service +// has so many options. An error will be returned if the service was +// unable to be created. +func CreateService(t *testing.T, client *gophercloud.ServiceClient, c *services.CreateOpts) (*services.Service, error) { + name := tools.RandomString("ACPTTEST", 8) + t.Logf("Attempting to create service: %s", name) + + var createOpts services.CreateOpts + if c != nil { + createOpts = *c + } else { + createOpts = services.CreateOpts{} + } + + createOpts.Extra["name"] = name + + service, err := services.Create(client, createOpts).Extract() + if err != nil { + return service, err + } + + t.Logf("Successfully created service %s", service.ID) + + return service, nil +} + // DeleteProject will delete a project by ID. A fatal error will occur if // the project ID failed to be deleted. This works best when using it as // a deferred function. @@ -236,7 +264,7 @@ func DeleteRole(t *testing.T, client *gophercloud.ServiceClient, roleID string) } // DeleteRegion will delete a reg by ID. A fatal error will occur if -// the role failed to be deleted. This works best when using it as +// the region failed to be deleted. This works best when using it as // a deferred function. func DeleteRegion(t *testing.T, client *gophercloud.ServiceClient, regionID string) { err := regions.Delete(client, regionID).ExtractErr() @@ -247,6 +275,18 @@ func DeleteRegion(t *testing.T, client *gophercloud.ServiceClient, regionID stri t.Logf("Deleted region: %s", regionID) } +// DeleteService will delete a reg by ID. A fatal error will occur if +// the service failed to be deleted. This works best when using it as +// a deferred function. +func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID string) { + err := services.Delete(client, serviceID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete service %s: %v", serviceID, err) + } + + t.Logf("Deleted service: %s", serviceID) +} + // UnassignRole will delete a role assigned to a user/group on a project/domain // A fatal error will occur if it fails to delete the assignment. // This works best when using it as a deferred function. diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go index 7a0c71f4fa..997fb189f6 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/acceptance/openstack/identity/v3/service_test.go @@ -31,3 +31,27 @@ func TestServicesList(t *testing.T) { } } + +func TestServicesCRUD(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + createOpts := services.CreateOpts{ + Type: "testing", + Extra: map[string]interface{}{ + "email": "testservice@example.com", + }, + } + + // Create service in the default domain + service, err := CreateService(t, client, &createOpts) + if err != nil { + t.Fatalf("Unable to create service: %v", err) + } + defer DeleteService(t, client, service.ID) + + tools.PrintResource(t, service) + tools.PrintResource(t, service.Extra) +} diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index b43c7bb6a7..b5e4d2cc79 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -24,7 +24,15 @@ Example to List Services Example to Create a Service - service, err := services.Create(identityClient, "compute").Extract() + createOpts := services.CreateOpts{ + Type: "compute", + Extra: map[string]interface{}{ + "name": "compute-service", + "description": "Compute Service", + }, + } + + service, err := services.Create(identityClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index a4401c5ad0..2fb91956d8 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -5,10 +5,52 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToServiceCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a service. +type CreateOpts struct { + // Type is the type of the service. + Type string `json:"type"` + + // Enabled is whether or not the service is enabled. + Enabled *bool `json:"enabled,omitempty"` + + // Extra is free-form extra key/value pairs to describe the service. + Extra map[string]interface{} `json:"-"` +} + +// ToServiceCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "service") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["service"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + // Create adds a new service of the requested type to the catalog. -func Create(client *gophercloud.ServiceClient, serviceType string) (r CreateResult) { - b := map[string]string{"type": serviceType} - _, r.Err = client.Post(listURL(client), b, &r.Body, nil) +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToServiceCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) return } diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index 2c4fa76755..f3484d7a96 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -1,17 +1,20 @@ package services import ( + "encoding/json" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) -type commonResult struct { +type serviceResult struct { gophercloud.Result } // Extract interprets a GetResult, CreateResult or UpdateResult as a concrete // Service. An error is returned if the original call or the extraction failed. -func (r commonResult) Extract() (*Service, error) { +func (r serviceResult) Extract() (*Service, error) { var s struct { Service *Service `json:"service"` } @@ -22,19 +25,19 @@ func (r commonResult) Extract() (*Service, error) { // CreateResult is the response from a Create request. Call its Extract method // to interpret it as a Service. type CreateResult struct { - commonResult + serviceResult } // GetResult is the response from a Get request. Call its Extract method // to interpret it as a Service. type GetResult struct { - commonResult + serviceResult } // UpdateResult is the response from an Update request. Call its Extract method // to interpret it as a Service. type UpdateResult struct { - commonResult + serviceResult } // DeleteResult is the response from a Delete request. Call its ExtractErr @@ -45,17 +48,50 @@ type DeleteResult struct { // Service represents an OpenStack Service. type Service struct { - // Description is a description of the service. - Description string `json:"description"` - // ID is the unique ID of the service. ID string `json:"id"` - // Name is the name of the service. - Name string `json:"name"` - // Type is the type of the service. Type string `json:"type"` + + // Enabled is whether or not the service is enabled. + Enabled bool `json:"enabled"` + + // Links contains referencing links to the service. + Links map[string]interface{} `json:"links"` + + // Extra is a collection of miscellaneous key/values. + Extra map[string]interface{} `json:"-"` +} + +func (r *Service) UnmarshalJSON(b []byte) error { + type tmp Service + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Service(s.tmp) + + // Collect other fields and bundle them into Extra + // but only if a field titled "extra" wasn't sent. + if s.Extra != nil { + r.Extra = s.Extra + } else { + var result interface{} + err := json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = internal.RemainingKeys(Service{}, resultMap) + } + } + + return err } // ServicePage is a single page of Service results. @@ -69,6 +105,21 @@ func (p ServicePage) IsEmpty() (bool, error) { return len(services) == 0, err } +// NextPageURL extracts the "next" link from the links section of the result. +func (r ServicePage) 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 +} + // ExtractServices extracts a slice of Services from a Collection acquired // from List. func ExtractServices(r pagination.Page) ([]Service, error) { diff --git a/openstack/identity/v3/services/testing/fixtures.go b/openstack/identity/v3/services/testing/fixtures.go new file mode 100644 index 0000000000..82e05fa2f9 --- /dev/null +++ b/openstack/identity/v3/services/testing/fixtures.go @@ -0,0 +1,206 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of Service results. +const ListOutput = ` +{ + "links": { + "next": null, + "previous": null + }, + "services": [ + { + "id": "1234", + "links": { + "self": "https://example.com/identity/v3/services/1234" + }, + "type": "identity", + "enabled": false, + "extra": { + "name": "service-one", + "description": "Service One" + } + }, + { + "id": "9876", + "links": { + "self": "https://example.com/identity/v3/services/9876" + }, + "type": "compute", + "enabled": false, + "extra": { + "name": "service-two", + "description": "Service Two", + "email": "service@example.com" + } + } + ] +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "service": { + "id": "9876", + "links": { + "self": "https://example.com/identity/v3/services/9876" + }, + "type": "compute", + "enabled": false, + "extra": { + "name": "service-two", + "description": "Service Two", + "email": "service@example.com" + } + } +} +` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "service": { + "description": "Service Two", + "email": "service@example.com", + "name": "service-two", + "type": "compute" + } +} +` + +// UpdateRequest provides the input to as Update request. +const UpdateRequest = ` +{ + "type": "compute2" +} +` + +// UpdateOutput provides an update result. +const UpdateOutput = ` +{ + "service": { + "id": "9876", + "links": { + "self": "https://example.com/identity/v3/services/9876" + }, + "type": "compute2", + "enabled": false, + "extra": { + "name": "service-two", + "description": "Service Two", + "email": "service@example.com" + } + } +} +` + +// FirstService is the first service in the List request. +var FirstService = services.Service{ + ID: "1234", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/services/1234", + }, + Type: "identity", + Enabled: false, + Extra: map[string]interface{}{ + "name": "service-one", + "description": "Service One", + }, +} + +// SecondService is the second service in the List request. +var SecondService = services.Service{ + ID: "9876", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/services/9876", + }, + Type: "compute", + Enabled: false, + Extra: map[string]interface{}{ + "name": "service-two", + "description": "Service Two", + "email": "service@example.com", + }, +} + +// SecondServiceUpdated is the SecondService should look after an Update. +var SecondServiceUpdated = services.Service{ + ID: "9876", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/services/9876", + }, + Type: "compute2", + Enabled: false, + Extra: map[string]interface{}{ + "name": "service-two", + "description": "Service Two", + "email": "service@example.com", + }, +} + +// ExpectedServicesSlice is the slice of services to be returned from ListOutput. +var ExpectedServicesSlice = []services.Service{FirstService, SecondService} + +// HandleListServicesSuccessfully creates an HTTP handler at `/services` on the +// test handler mux that responds with a list of two services. +func HandleListServicesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/services", 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) + }) +} + +// HandleGetServiceSuccessfully creates an HTTP handler at `/services` on the +// test handler mux that responds with a single service. +func HandleGetServiceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/services/9876", 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) + }) +} + +// HandleCreateServiceSuccessfully creates an HTTP handler at `/services` on the +// test handler mux that tests service creation. +func HandleCreateServiceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/services", 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, GetOutput) + }) +} + +// HandleUpdateServiceSuccessfully creates an HTTP handler at `/services` on the +// test handler mux that tests service update. +func HandleUpdateServiceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/services/9876", 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/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index 0a065a2afc..15e08af8b2 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -1,7 +1,6 @@ package testing import ( - "fmt" "net/http" "testing" @@ -14,162 +13,62 @@ import ( func TestCreateSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - - th.Mux.HandleFunc("/services", 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, `{ "type": "compute" }`) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ - "service": { - "description": "Here's your service", - "id": "1234", - "name": "InscrutableOpenStackProjectName", - "type": "compute" - } - }`) - }) - - expected := &services.Service{ - Description: "Here's your service", - ID: "1234", - Name: "InscrutableOpenStackProjectName", - Type: "compute", + HandleCreateServiceSuccessfully(t) + + createOpts := services.CreateOpts{ + Type: "compute", + Extra: map[string]interface{}{ + "name": "service-two", + "description": "Service Two", + "email": "service@example.com", + }, } - actual, err := services.Create(client.ServiceClient(), "compute").Extract() - if err != nil { - t.Fatalf("Unexpected error from Create: %v", err) - } - th.AssertDeepEquals(t, expected, actual) + actual, err := services.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondService, *actual) } func TestListSinglePage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - - 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, ` - { - "links": { - "next": null, - "previous": null - }, - "services": [ - { - "description": "Service One", - "id": "1234", - "name": "service-one", - "type": "identity" - }, - { - "description": "Service Two", - "id": "9876", - "name": "service-two", - "type": "compute" - } - ] - } - `) - }) + HandleListServicesSuccessfully(t) count := 0 err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ + actual, err := services.ExtractServices(page) - if err != nil { - return false, err - } - - expected := []services.Service{ - { - Description: "Service One", - ID: "1234", - Name: "service-one", - Type: "identity", - }, - { - Description: "Service Two", - ID: "9876", - Name: "service-two", - Type: "compute", - }, - } - th.AssertDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedServicesSlice, actual) + return true, nil }) th.AssertNoErr(t, err) - th.AssertEquals(t, 1, count) + th.CheckEquals(t, count, 1) } func TestGetSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() + HandleGetServiceSuccessfully(t) - th.Mux.HandleFunc("/services/12345", 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, ` - { - "service": { - "description": "Service One", - "id": "12345", - "name": "service-one", - "type": "identity" - } - } - `) - }) + actual, err := services.Get(client.ServiceClient(), "9876").Extract() - actual, err := services.Get(client.ServiceClient(), "12345").Extract() th.AssertNoErr(t, err) - - expected := &services.Service{ - ID: "12345", - Description: "Service One", - Name: "service-one", - Type: "identity", - } - - th.AssertDeepEquals(t, expected, actual) + th.CheckDeepEquals(t, SecondService, *actual) + th.AssertEquals(t, SecondService.Extra["email"], "service@example.com") } func TestUpdateSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() + HandleUpdateServiceSuccessfully(t) - th.Mux.HandleFunc("/services/12345", 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, `{ "type": "lasermagic" }`) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` - { - "service": { - "id": "12345", - "type": "lasermagic" - } - } - `) - }) - - expected := &services.Service{ - ID: "12345", - Type: "lasermagic", - } - - actual, err := services.Update(client.ServiceClient(), "12345", "lasermagic").Extract() + actual, err := services.Update(client.ServiceClient(), "9876", "compute2").Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, actual) + th.CheckDeepEquals(t, SecondServiceUpdated, *actual) } func TestDeleteSuccessful(t *testing.T) { diff --git a/openstack/identity/v3/services/urls.go b/openstack/identity/v3/services/urls.go index c5ae268379..b462632f03 100644 --- a/openstack/identity/v3/services/urls.go +++ b/openstack/identity/v3/services/urls.go @@ -6,6 +6,10 @@ func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("services") } +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("services") +} + func serviceURL(client *gophercloud.ServiceClient, serviceID string) string { return client.ServiceURL("services", serviceID) } From 1597235e7a527e67c57f0df2f447698ece912aa7 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 16 Nov 2017 11:42:38 -0700 Subject: [PATCH 0126/2296] Identity v3 update Service update --- .../openstack/identity/v3/service_test.go | 16 ++++++ openstack/identity/v3/services/doc.go | 18 +++++++ openstack/identity/v3/services/requests.go | 50 +++++++++++++++++-- .../identity/v3/services/testing/fixtures.go | 9 ++-- .../v3/services/testing/requests_test.go | 9 +++- openstack/identity/v3/services/urls.go | 4 ++ 6 files changed, 98 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go index 997fb189f6..2868a2cae9 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/acceptance/openstack/identity/v3/service_test.go @@ -54,4 +54,20 @@ func TestServicesCRUD(t *testing.T) { tools.PrintResource(t, service) tools.PrintResource(t, service.Extra) + + updateOpts := services.UpdateOpts{ + Type: "testing2", + Extra: map[string]interface{}{ + "description": "Test Users", + "email": "thetestservice@example.com", + }, + } + + newService, err := services.Update(client, service.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update service: %v", err) + } + + tools.PrintResource(t, newService) + tools.PrintResource(t, newService.Extra) } diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index b5e4d2cc79..81702359ac 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -37,6 +37,23 @@ Example to Create a Service panic(err) } +Example to Update a Service + + serviceID := "3c7bbe9a6ecb453ca1789586291380ed" + + var iFalse bool = false + updateOpts := services.UpdateOpts{ + Enabled: &iFalse, + Extra: map[string]interface{}{ + "description": "Disabled Compute Service" + }, + } + + service, err := services.Update(identityClient, serviceID, updateOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a Service serviceID := "3c7bbe9a6ecb453ca1789586291380ed" @@ -44,5 +61,6 @@ Example to Delete a Service if err != nil { panic(err) } + */ package services diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index 2fb91956d8..523568f248 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -94,10 +94,52 @@ func Get(client *gophercloud.ServiceClient, serviceID string) (r GetResult) { return } -// Update changes the service type of an existing service. -func Update(client *gophercloud.ServiceClient, serviceID string, serviceType string) (r UpdateResult) { - b := map[string]string{"type": serviceType} - _, r.Err = client.Patch(serviceURL(client, serviceID), &b, &r.Body, nil) +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToServiceUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts provides options for updating a service. +type UpdateOpts struct { + // Type is the type of the service. + Type string `json:"type"` + + // Enabled is whether or not the service is enabled. + Enabled *bool `json:"enabled,omitempty"` + + // Extra is free-form extra key/value pairs to describe the service. + Extra map[string]interface{} `json:"-"` +} + +// ToServiceUpdateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "service") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["service"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Update updates an existing Service. +func Update(client *gophercloud.ServiceClient, serviceID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToServiceUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(updateURL(client, serviceID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) return } diff --git a/openstack/identity/v3/services/testing/fixtures.go b/openstack/identity/v3/services/testing/fixtures.go index 82e05fa2f9..fd1d632425 100644 --- a/openstack/identity/v3/services/testing/fixtures.go +++ b/openstack/identity/v3/services/testing/fixtures.go @@ -81,7 +81,10 @@ const CreateRequest = ` // UpdateRequest provides the input to as Update request. const UpdateRequest = ` { - "type": "compute2" + "service": { + "type": "compute2", + "description": "Service Two Updated" + } } ` @@ -97,7 +100,7 @@ const UpdateOutput = ` "enabled": false, "extra": { "name": "service-two", - "description": "Service Two", + "description": "Service Two Updated", "email": "service@example.com" } } @@ -143,7 +146,7 @@ var SecondServiceUpdated = services.Service{ Enabled: false, Extra: map[string]interface{}{ "name": "service-two", - "description": "Service Two", + "description": "Service Two Updated", "email": "service@example.com", }, } diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index 15e08af8b2..6882cf9366 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -66,9 +66,16 @@ func TestUpdateSuccessful(t *testing.T) { defer th.TeardownHTTP() HandleUpdateServiceSuccessfully(t) - actual, err := services.Update(client.ServiceClient(), "9876", "compute2").Extract() + updateOpts := services.UpdateOpts{ + Type: "compute2", + Extra: map[string]interface{}{ + "description": "Service Two Updated", + }, + } + actual, err := services.Update(client.ServiceClient(), "9876", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondServiceUpdated, *actual) + th.AssertEquals(t, SecondServiceUpdated.Extra["description"], "Service Two Updated") } func TestDeleteSuccessful(t *testing.T) { diff --git a/openstack/identity/v3/services/urls.go b/openstack/identity/v3/services/urls.go index b462632f03..caa625a208 100644 --- a/openstack/identity/v3/services/urls.go +++ b/openstack/identity/v3/services/urls.go @@ -13,3 +13,7 @@ func createURL(client *gophercloud.ServiceClient) string { func serviceURL(client *gophercloud.ServiceClient, serviceID string) string { return client.ServiceURL("services", serviceID) } + +func updateURL(client *gophercloud.ServiceClient, serviceID string) string { + return client.ServiceURL("services", serviceID) +} From 2779d780cc38d8be870182c56b07ffe327821feb Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 16 Nov 2017 13:04:55 -0700 Subject: [PATCH 0127/2296] Identity v3: update Service List --- acceptance/openstack/identity/v3/service_test.go | 6 +++++- openstack/identity/v3/services/requests.go | 14 ++++++++------ .../v3/services/testing/requests_test.go | 16 +++++++++++++++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go index 2868a2cae9..ffd7e4d1fb 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/acceptance/openstack/identity/v3/service_test.go @@ -16,7 +16,11 @@ func TestServicesList(t *testing.T) { t.Fatalf("Unable to obtain an identity client: %v") } - allPages, err := services.List(client, nil).AllPages() + listOpts := services.ListOpts{ + ServiceType: "identity", + } + + allPages, err := services.List(client, listOpts).AllPages() if err != nil { t.Fatalf("Unable to list services: %v", err) } diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index 523568f248..1a8b35232a 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -62,9 +62,11 @@ type ListOptsBuilder interface { // ListOpts provides options for filtering the List results. type ListOpts struct { + // ServiceType filter the response by a type of service. ServiceType string `q:"type"` - PerPage int `q:"perPage"` - Page int `q:"page"` + + // Name filters the response by a service name. + Name string `q:"name"` } // ToServiceListMap builds a list query from the list options. @@ -75,15 +77,15 @@ func (opts ListOpts) ToServiceListMap() (string, error) { // List enumerates the services available to a specific user. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - u := listURL(client) + url := listURL(client) if opts != nil { - q, err := opts.ToServiceListMap() + query, err := opts.ToServiceListMap() if err != nil { return pagination.Pager{Err: err} } - u += q + url += query } - return pagination.NewPager(client, u, func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ServicePage{pagination.LinkedPageBase{PageResult: r}} }) } diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index 6882cf9366..2a8a89dd24 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -29,7 +29,7 @@ func TestCreateSuccessful(t *testing.T) { th.CheckDeepEquals(t, SecondService, *actual) } -func TestListSinglePage(t *testing.T) { +func TestListServices(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListServicesSuccessfully(t) @@ -49,6 +49,20 @@ func TestListSinglePage(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListServicesAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListServicesSuccessfully(t) + + allPages, err := services.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := services.ExtractServices(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedServicesSlice, actual) + th.AssertEquals(t, ExpectedServicesSlice[0].Extra["name"], "service-one") + th.AssertEquals(t, ExpectedServicesSlice[1].Extra["email"], "service@example.com") +} + func TestGetSuccessful(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 639d71fdea8341ba30c93a62ae8db02a27442967 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 17 Nov 2017 04:08:56 +0000 Subject: [PATCH 0128/2296] Networking v2: Port Security Extension This commit adds the ability to retrieve the port security status of networks and ports. --- .../openstack/networking/v2/networks_test.go | 10 +++- .../v2/extensions/portsecurity/doc.go | 48 +++++++++++++++ .../v2/extensions/portsecurity/results.go | 7 +++ .../v2/networks/testing/fixtures.go | 9 ++- .../v2/networks/testing/requests_test.go | 60 +++++++++++++++++++ .../networking/v2/ports/testing/fixtures.go | 3 +- .../v2/ports/testing/requests_test.go | 57 ++++++++++++++++++ 7 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 openstack/networking/v2/extensions/portsecurity/doc.go create mode 100644 openstack/networking/v2/extensions/portsecurity/results.go diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index 66f42f85a3..c100bd4160 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) @@ -16,12 +17,19 @@ func TestNetworksList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } + type networkWithExt struct { + networks.Network + portsecurity.PortSecurityExt + } + + var allNetworks []networkWithExt + allPages, err := networks.List(client, nil).AllPages() if err != nil { t.Fatalf("Unable to list networks: %v", err) } - allNetworks, err := networks.ExtractNetworks(allPages) + err = networks.ExtractNetworksInto(allPages, &allNetworks) if err != nil { t.Fatalf("Unable to extract networks: %v", err) } diff --git a/openstack/networking/v2/extensions/portsecurity/doc.go b/openstack/networking/v2/extensions/portsecurity/doc.go new file mode 100644 index 0000000000..9de4fcf750 --- /dev/null +++ b/openstack/networking/v2/extensions/portsecurity/doc.go @@ -0,0 +1,48 @@ +/* +Package portsecurity provides information and interaction with the port +security extension for the OpenStack Networking service. + +Example to List Networks with Port Security Information + + type NetworkWithPortSecurityExt struct { + networks.Network + portsecurity.PortSecurityExt + } + + var allNetworks []NetworkWithPortSecurityExt + + listOpts := networks.ListOpts{ + Name: "network_1", + } + + 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.Println("%+v\n", network) + } + +Example to Get a Port with Port Security Information + + var portWithExtensions struct { + ports.Port + portsecurity.PortSecurityExt + } + + portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + + err := ports.Get(networkingClient, portID).ExtractInto(&portWithExtensions) + if err != nil { + panic(err) + } + + fmt.Println("%+v\n", portWithExtensions) +*/ +package portsecurity diff --git a/openstack/networking/v2/extensions/portsecurity/results.go b/openstack/networking/v2/extensions/portsecurity/results.go new file mode 100644 index 0000000000..7b3482a405 --- /dev/null +++ b/openstack/networking/v2/extensions/portsecurity/results.go @@ -0,0 +1,7 @@ +package portsecurity + +type PortSecurityExt struct { + // PortSecurityEnabled specifies whether port security is enabled or + // disabled. + PortSecurityEnabled bool `json:"port_security_enabled"` +} diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index eec7155b37..8ba32a9c20 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -20,7 +20,8 @@ const ListResponse = ` "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local", - "router:external": true + "router:external": true, + "port_security_enabled": true }, { "status": "ACTIVE", @@ -35,7 +36,8 @@ const ListResponse = ` "provider:segmentation_id": 1234567890, "provider:physical_network": null, "provider:network_type": "local", - "router:external": false + "router:external": false, + "port_security_enabled": false } ] }` @@ -55,7 +57,8 @@ const GetResponse = ` "provider:segmentation_id": 9876543210, "provider:physical_network": null, "provider:network_type": "local", - "router:external": true + "router:external": true, + "port_security_enabled": true } }` diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 4ffe864c01..cbe349351d 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -6,6 +6,7 @@ import ( "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -46,6 +47,39 @@ func TestList(t *testing.T) { } } +func TestListWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks", 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, ListResponse) + }) + + client := fake.ServiceClient() + + type networkWithExt struct { + networks.Network + portsecurity.PortSecurityExt + } + + var allNetworks []networkWithExt + + allPages, err := networks.List(client, networks.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + err = networks.ExtractNetworksInto(allPages, &allNetworks) + th.AssertNoErr(t, err) + + th.AssertEquals(t, allNetworks[0].Status, "ACTIVE") + th.AssertEquals(t, allNetworks[0].PortSecurityEnabled, true) +} + func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -65,6 +99,32 @@ func TestGet(t *testing.T) { th.CheckDeepEquals(t, &Network1, n) } +func TestGetWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/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, GetResponse) + }) + + var networkWithExtensions struct { + networks.Network + portsecurity.PortSecurityExt + } + + err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&networkWithExtensions) + th.AssertNoErr(t, err) + + th.AssertEquals(t, networkWithExtensions.Status, "ACTIVE") + th.AssertEquals(t, networkWithExtensions.PortSecurityEnabled, true) +} + func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index 168e1da0e9..dea77fe011 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -21,7 +21,8 @@ const ListResponse = ` ], "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", "security_groups": [], - "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824" + "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824", + "port_security_enabled": false } ] } diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 6e1bc48748..aa6c74d64a 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -6,6 +6,7 @@ import ( "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -66,6 +67,36 @@ func TestList(t *testing.T) { } } +func TestListWithExtensions(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, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ListResponse) + }) + + type portWithExt struct { + ports.Port + portsecurity.PortSecurityExt + } + + var allPorts []portWithExt + + allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + err = ports.ExtractPortsInto(allPages, &allPorts) + + th.AssertEquals(t, allPorts[0].Status, "ACTIVE") + th.AssertEquals(t, allPorts[0].PortSecurityEnabled, false) +} + func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -99,6 +130,32 @@ func TestGet(t *testing.T) { th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e") } +func TestGetWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", 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, GetResponse) + }) + + var portWithExtensions struct { + ports.Port + portsecurity.PortSecurityExt + } + + err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&portWithExtensions) + th.AssertNoErr(t, err) + + th.AssertEquals(t, portWithExtensions.Status, "ACTIVE") + th.AssertEquals(t, portWithExtensions.PortSecurityEnabled, false) +} + func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 94e1cbcf835e33225231603b4f513761b0f049c0 Mon Sep 17 00:00:00 2001 From: j-griffith Date: Sat, 15 Jul 2017 11:07:30 -0600 Subject: [PATCH 0129/2296] Add Cinder V3 and MicroVersion Support This change pulls the existing V2 implementations into V3. Note that the base V3 version (3.0) is a direct map back to V2. As a result to use the base V3 endpoint for Cinder we can don't need to change anything other than the URL. Also clean up some simple lint errors in requests.go One thing that the base V3 version does buy us here though is Microversions (for better or worse). So, this change also adds in the ability to specify MicroVersion in the additional-header of the ServiceClient. This will be important going forward and so we can now add things like the new standalone Attachment API. Volume V3 just points to V2 unless a microversioned call is issued: https://github.com/openstack/cinder/blob/master/cinder/api/v3/volumes.py#L25 https://github.com/openstack/cinder/blob/master/cinder/api/v3/volumes.py#L50 An example of a micro versioned call (not implemented in gophercloud): https://github.com/openstack/cinder/blob/master/cinder/api/v3/volumes.py#L212 Even the base V3 view builder just utilizes V2 (only adding additional info for specific versions): https://github.com/openstack/cinder/blob/master/cinder/api/v3/views/volumes.py#L19 https://github.com/openstack/cinder/blob/master/cinder/api/v3/views/snapshots.py#L19 --- acceptance/clients/clients.go | 38 ++- openstack/blockstorage/noauth/doc.go | 2 +- openstack/blockstorage/noauth/requests.go | 6 +- .../noauth/testing/requests_test.go | 6 +- openstack/blockstorage/v3/snapshots/doc.go | 5 + .../blockstorage/v3/snapshots/requests.go | 158 +++++++++++ .../blockstorage/v3/snapshots/results.go | 121 +++++++++ .../blockstorage/v3/snapshots/testing/doc.go | 2 + .../v3/snapshots/testing/fixtures.go | 134 +++++++++ .../v3/snapshots/testing/requests_test.go | 116 ++++++++ openstack/blockstorage/v3/snapshots/urls.go | 27 ++ openstack/blockstorage/v3/snapshots/util.go | 22 ++ openstack/blockstorage/v3/volumes/doc.go | 5 + openstack/blockstorage/v3/volumes/requests.go | 182 +++++++++++++ openstack/blockstorage/v3/volumes/results.go | 159 +++++++++++ .../blockstorage/v3/volumes/testing/doc.go | 2 + .../v3/volumes/testing/fixtures.go | 203 ++++++++++++++ .../v3/volumes/testing/requests_test.go | 257 ++++++++++++++++++ openstack/blockstorage/v3/volumes/urls.go | 23 ++ openstack/blockstorage/v3/volumes/util.go | 22 ++ openstack/client.go | 8 +- service_client.go | 2 + 22 files changed, 1490 insertions(+), 10 deletions(-) create mode 100644 openstack/blockstorage/v3/snapshots/doc.go create mode 100644 openstack/blockstorage/v3/snapshots/requests.go create mode 100644 openstack/blockstorage/v3/snapshots/results.go create mode 100644 openstack/blockstorage/v3/snapshots/testing/doc.go create mode 100644 openstack/blockstorage/v3/snapshots/testing/fixtures.go create mode 100644 openstack/blockstorage/v3/snapshots/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/snapshots/urls.go create mode 100644 openstack/blockstorage/v3/snapshots/util.go create mode 100644 openstack/blockstorage/v3/volumes/doc.go create mode 100644 openstack/blockstorage/v3/volumes/requests.go create mode 100644 openstack/blockstorage/v3/volumes/results.go create mode 100644 openstack/blockstorage/v3/volumes/testing/doc.go create mode 100644 openstack/blockstorage/v3/volumes/testing/fixtures.go create mode 100644 openstack/blockstorage/v3/volumes/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/volumes/urls.go create mode 100644 openstack/blockstorage/v3/volumes/util.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 03209e0f21..fcf22a7e8d 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -147,6 +147,25 @@ func NewBlockStorageV2Client() (*gophercloud.ServiceClient, error) { }) } +// NewBlockStorageV3Client returns a *ServiceClient for making calls +// to the OpenStack Block Storage v3 API. An error will be returned +// if authentication or client creation was not possible. +func NewBlockStorageV3Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + return openstack.NewBlockStorageV3(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} + // NewBlockStorageV2NoAuthClient returns a noauth *ServiceClient for // making calls to the OpenStack Block Storage v2 API. An error will be // returned if client creation was not possible. @@ -159,7 +178,24 @@ func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { return nil, err } - return noauth.NewBlockStorageV2(client, noauth.EndpointOpts{ + return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{ + CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), + }) +} + +// NewBlockStorageV3NoAuthClient returns a noauth *ServiceClient for +// making calls to the OpenStack Block Storage v2 API. An error will be +// returned if client creation was not possible. +func NewBlockStorageV3NoAuthClient() (*gophercloud.ServiceClient, error) { + client, err := noauth.NewClient(gophercloud.AuthOptions{ + Username: os.Getenv("OS_USERNAME"), + TenantName: os.Getenv("OS_TENANT_NAME"), + }) + if err != nil { + return nil, err + } + + return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) } diff --git a/openstack/blockstorage/noauth/doc.go b/openstack/blockstorage/noauth/doc.go index 204c78fee8..25a7f84582 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.NewBlockStorageV2(provider, noauth.EndpointOpts{ + client, err := noauth.NewBlockStorageNoAuth(provider, noauth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) diff --git a/openstack/blockstorage/noauth/requests.go b/openstack/blockstorage/noauth/requests.go index 81c084c9b8..21cc8f09df 100644 --- a/openstack/blockstorage/noauth/requests.go +++ b/openstack/blockstorage/noauth/requests.go @@ -48,8 +48,8 @@ func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophe return sc, nil } -// NewBlockStorageV2 creates a ServiceClient that may be used to access a -// "noauth" block storage service. -func NewBlockStorageV2(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { +// 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) } diff --git a/openstack/blockstorage/noauth/testing/requests_test.go b/openstack/blockstorage/noauth/testing/requests_test.go index 492b639f33..14080259aa 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.NewBlockStorageV2(provider, noauth.EndpointOpts{ + noauthClient, err := noauth.NewBlockStorageNoAuth(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.NewBlockStorageV2(provider2, noauth.EndpointOpts{ + noauthClient2, err := noauth.NewBlockStorageNoAuth(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.NewBlockStorageV2(provider2, noauth.EndpointOpts{}) + errTest, err := noauth.NewBlockStorageNoAuth(provider2, noauth.EndpointOpts{}) _ = errTest th.AssertEquals(t, errorResult, err.Error()) } diff --git a/openstack/blockstorage/v3/snapshots/doc.go b/openstack/blockstorage/v3/snapshots/doc.go new file mode 100644 index 0000000000..198f83077c --- /dev/null +++ b/openstack/blockstorage/v3/snapshots/doc.go @@ -0,0 +1,5 @@ +// Package snapshots provides information and interaction with snapshots in the +// OpenStack Block Storage service. A snapshot is a point in time copy of the +// data contained in an external storage volume, and can be controlled +// programmatically. +package snapshots diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go new file mode 100644 index 0000000000..1f8f81b89a --- /dev/null +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -0,0 +1,158 @@ +package snapshots + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToSnapshotCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Snapshot. This object is passed to +// the snapshots.Create function. For more information about these parameters, +// see the Snapshot object. +type CreateOpts struct { + VolumeID string `json:"volume_id" required:"true"` + Force bool `json:"force,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToSnapshotCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "snapshot") +} + +// Create will create a new Snapshot based on the values in CreateOpts. To +// extract the Snapshot object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToSnapshotCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} + +// Delete will delete the existing Snapshot with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// Get retrieves the Snapshot with the provided ID. To extract the Snapshot +// object from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToSnapshotListQuery() (string, error) +} + +// ListOpts hold options for listing Snapshots. It is passed to the +// snapshots.List function. +type ListOpts struct { + Name string `q:"name"` + Status string `q:"status"` + VolumeID string `q:"volume_id"` +} + +// ToSnapshotListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToSnapshotListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Snapshots optionally limited by the conditions provided in +// ListOpts. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToSnapshotListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return SnapshotPage{pagination.SinglePageBase(r)} + }) +} + +// UpdateMetadataOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateMetadataOptsBuilder interface { + ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) +} + +// UpdateMetadataOpts contain options for updating an existing Snapshot. This +// object is passed to the snapshots.Update function. For more information +// about the parameters, see the Snapshot object. +type UpdateMetadataOpts struct { + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of +// an UpdateMetadataOpts. +func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// UpdateMetadata will update the Snapshot with provided information. To +// extract the updated Snapshot from the response, call the ExtractMetadata +// method on the UpdateMetadataResult. +func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { + b, err := opts.ToSnapshotUpdateMetadataMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// IDFromName is a convienience function that returns a snapshot's ID given its name. +func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { + count := 0 + id := "" + pages, err := List(client, nil).AllPages() + if err != nil { + return "", err + } + + all, err := ExtractSnapshots(pages) + if err != nil { + return "", err + } + + for _, s := range all { + if s.Name == name { + count++ + id = s.ID + } + } + + switch count { + case 0: + return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} + case 1: + return id, nil + default: + return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} + } +} diff --git a/openstack/blockstorage/v3/snapshots/results.go b/openstack/blockstorage/v3/snapshots/results.go new file mode 100644 index 0000000000..5f03ff925a --- /dev/null +++ b/openstack/blockstorage/v3/snapshots/results.go @@ -0,0 +1,121 @@ +package snapshots + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Snapshot contains all the information associated with a Cinder Snapshot. +type Snapshot struct { + // Unique identifier. + ID string `json:"id"` + + // Date created. + CreatedAt time.Time `json:"-"` + + // Date updated. + UpdatedAt time.Time `json:"-"` + + // Display name. + Name string `json:"name"` + + // Display description. + Description string `json:"description"` + + // ID of the Volume from which this Snapshot was created. + VolumeID string `json:"volume_id"` + + // Currect status of the Snapshot. + Status string `json:"status"` + + // Size of the Snapshot, in GB. + Size int `json:"size"` + + // User-defined key-value pairs. + Metadata map[string]string `json:"metadata"` +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// SnapshotPage is a pagination.Pager that is returned from a call to the List function. +type SnapshotPage struct { + pagination.SinglePageBase +} + +// UnmarshalJSON converts our JSON API response into our snapshot struct +func (r *Snapshot) UnmarshalJSON(b []byte) error { + type tmp Snapshot + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Snapshot(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +// IsEmpty returns true if a SnapshotPage contains no Snapshots. +func (r SnapshotPage) IsEmpty() (bool, error) { + volumes, err := ExtractSnapshots(r) + return len(volumes) == 0, err +} + +// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. +func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { + var s struct { + Snapshots []Snapshot `json:"snapshots"` + } + err := (r.(SnapshotPage)).ExtractInto(&s) + return s.Snapshots, err +} + +// UpdateMetadataResult contains the response body and error from an UpdateMetadata request. +type UpdateMetadataResult struct { + commonResult +} + +// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. +func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { + if r.Err != nil { + return nil, r.Err + } + m := r.Body.(map[string]interface{})["metadata"] + return m.(map[string]interface{}), nil +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Snapshot object out of the commonResult object. +func (r commonResult) Extract() (*Snapshot, error) { + var s struct { + Snapshot *Snapshot `json:"snapshot"` + } + err := r.ExtractInto(&s) + return s.Snapshot, err +} diff --git a/openstack/blockstorage/v3/snapshots/testing/doc.go b/openstack/blockstorage/v3/snapshots/testing/doc.go new file mode 100644 index 0000000000..3de6274b32 --- /dev/null +++ b/openstack/blockstorage/v3/snapshots/testing/doc.go @@ -0,0 +1,2 @@ +// snapshots_v3 +package testing diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures.go b/openstack/blockstorage/v3/snapshots/testing/fixtures.go new file mode 100644 index 0000000000..9638fa5007 --- /dev/null +++ b/openstack/blockstorage/v3/snapshots/testing/fixtures.go @@ -0,0 +1,134 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots", 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, ` + { + "snapshots": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "snapshot-001", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "description": "Daily Backup", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "snapshot-002", + "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", + "description": "Weekly Backup", + "status": "available", + "size": 25, + "created_at": "2017-05-30T03:35:03.000000" + } + ] + } + `) + }) +} + +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") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "snapshot": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "snapshot-001", + "description": "Daily backup", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} + `) + }) +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots", 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, ` +{ + "snapshot": { + "volume_id": "1234", + "name": "snapshot-001" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, ` +{ + "snapshot": { + "volume_id": "1234", + "name": "snapshot-001", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "description": "Daily backup", + "volume_id": "1234", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} + `) + }) +} + +func MockUpdateMetadataResponse(t *testing.T) { + th.Mux.HandleFunc("/snapshots/123/metadata", 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.TestJSONRequest(t, r, ` + { + "metadata": { + "key": "v1" + } + } + `) + + fmt.Fprintf(w, ` + { + "metadata": { + "key": "v1" + } + } + `) + }) +} + +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") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/blockstorage/v3/snapshots/testing/requests_test.go b/openstack/blockstorage/v3/snapshots/testing/requests_test.go new file mode 100644 index 0000000000..51231e3017 --- /dev/null +++ b/openstack/blockstorage/v3/snapshots/testing/requests_test.go @@ -0,0 +1,116 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + count := 0 + + snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := snapshots.ExtractSnapshots(page) + if err != nil { + t.Errorf("Failed to extract snapshots: %v", err) + return false, err + } + + expected := []snapshots.Snapshot{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "snapshot-001", + VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + Status: "available", + Size: 30, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Daily Backup", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "snapshot-002", + VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", + Status: "available", + Size: 25, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Weekly Backup", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "snapshot-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} + n, err := snapshots.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.Name, "snapshot-001") + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestUpdateMetadata(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateMetadataResponse(t) + + expected := map[string]interface{}{"key": "v1"} + + options := &snapshots.UpdateMetadataOpts{ + Metadata: map[string]interface{}{ + "key": "v1", + }, + } + + actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, actual, expected) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := snapshots.Delete(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 new file mode 100644 index 0000000000..7780437493 --- /dev/null +++ b/openstack/blockstorage/v3/snapshots/urls.go @@ -0,0 +1,27 @@ +package snapshots + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("snapshots") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return deleteURL(c, id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return createURL(c) +} + +func metadataURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id, "metadata") +} + +func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { + return metadataURL(c, id) +} diff --git a/openstack/blockstorage/v3/snapshots/util.go b/openstack/blockstorage/v3/snapshots/util.go new file mode 100644 index 0000000000..40fbb827b8 --- /dev/null +++ b/openstack/blockstorage/v3/snapshots/util.go @@ -0,0 +1,22 @@ +package snapshots + +import ( + "github.com/gophercloud/gophercloud" +) + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/blockstorage/v3/volumes/doc.go b/openstack/blockstorage/v3/volumes/doc.go new file mode 100644 index 0000000000..307b8b12d2 --- /dev/null +++ b/openstack/blockstorage/v3/volumes/doc.go @@ -0,0 +1,5 @@ +// Package volumes provides information and interaction with volumes in the +// OpenStack Block Storage service. A volume is a detachable block storage +// device, akin to a USB hard drive. It can only be attached to one instance at +// a time. +package volumes diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go new file mode 100644 index 0000000000..18c9cb272e --- /dev/null +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -0,0 +1,182 @@ +package volumes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToVolumeCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Volume. This object is passed to +// the volumes.Create function. For more information about these parameters, +// see the Volume object. +type CreateOpts struct { + // The size of the volume, in GB + Size int `json:"size" required:"true"` + // The availability zone + AvailabilityZone string `json:"availability_zone,omitempty"` + // ConsistencyGroupID is the ID of a consistency group + ConsistencyGroupID string `json:"consistencygroup_id,omitempty"` + // The volume description + Description string `json:"description,omitempty"` + // One or more metadata key and value pairs to associate with the volume + Metadata map[string]string `json:"metadata,omitempty"` + // The volume name + Name string `json:"name,omitempty"` + // the ID of the existing volume snapshot + SnapshotID string `json:"snapshot_id,omitempty"` + // SourceReplica is a UUID of an existing volume to replicate with + SourceReplica string `json:"source_replica,omitempty"` + // the ID of the existing volume + SourceVolID string `json:"source_volid,omitempty"` + // The ID of the image from which you want to create the volume. + // Required to create a bootable volume. + ImageID string `json:"imageRef,omitempty"` + // The associated volume type + VolumeType string `json:"volume_type,omitempty"` +} + +// ToVolumeCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "volume") +} + +// Create will create a new Volume based on the values in CreateOpts. To extract +// the Volume object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToVolumeCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} + +// Delete will delete the existing Volume with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// Get retrieves the Volume with the provided ID. To extract the Volume object +// from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToVolumeListQuery() (string, error) +} + +// ListOpts holds options for listing Volumes. It is passed to the volumes.List +// function. +type ListOpts struct { + // admin-only option. Set it to true to see all tenant volumes. + AllTenants bool `q:"all_tenants"` + // List only volumes that contain Metadata. + Metadata map[string]string `q:"metadata"` + // List only volumes that have Name as the display name. + Name string `q:"name"` + // List only volumes that have a status of Status. + Status string `q:"status"` +} + +// ToVolumeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToVolumeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Volumes optionally limited by the conditions provided in ListOpts. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToVolumeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return VolumePage{pagination.SinglePageBase(r)} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToVolumeUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Volume. This object is passed +// to the volumes.Update function. For more information about the parameters, see +// the Volume object. +type UpdateOpts struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToVolumeUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "volume") +} + +// Update will update the Volume with provided information. To extract the updated +// Volume from the response, call the Extract method on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToVolumeUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// IDFromName is a convienience function that returns a server's ID given its name. +func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { + count := 0 + id := "" + pages, err := List(client, nil).AllPages() + if err != nil { + return "", err + } + + all, err := ExtractVolumes(pages) + if err != nil { + return "", err + } + + for _, s := range all { + if s.Name == name { + count++ + id = s.ID + } + } + + switch count { + case 0: + return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} + case 1: + return id, nil + default: + return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} + } +} diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go new file mode 100644 index 0000000000..5ebe36a338 --- /dev/null +++ b/openstack/blockstorage/v3/volumes/results.go @@ -0,0 +1,159 @@ +package volumes + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Attachment represents a Volume Attachment record +type Attachment struct { + AttachedAt time.Time `json:"-"` + AttachmentID string `json:"attachment_id"` + Device string `json:"device"` + HostName string `json:"host_name"` + ID string `json:"id"` + ServerID string `json:"server_id"` + VolumeID string `json:"volume_id"` +} + +// UnmarshalJSON is our unmarshalling helper +func (r *Attachment) UnmarshalJSON(b []byte) error { + type tmp Attachment + var s struct { + tmp + AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Attachment(s.tmp) + + r.AttachedAt = time.Time(s.AttachedAt) + + return err +} + +// Volume contains all the information associated with an OpenStack Volume. +type Volume struct { + // Unique identifier for the volume. + ID string `json:"id"` + // Current status of the volume. + Status string `json:"status"` + // Size of the volume in GB. + Size int `json:"size"` + // AvailabilityZone is which availability zone the volume is in. + AvailabilityZone string `json:"availability_zone"` + // The date when this volume was created. + CreatedAt time.Time `json:"-"` + // The date when this volume was last updated + UpdatedAt time.Time `json:"-"` + // Instances onto which the volume is attached. + Attachments []Attachment `json:"attachments"` + // Human-readable display name for the volume. + Name string `json:"name"` + // Human-readable description for the volume. + Description string `json:"description"` + // The type of volume to create, either SATA or SSD. + VolumeType string `json:"volume_type"` + // The ID of the snapshot from which the volume was created + SnapshotID string `json:"snapshot_id"` + // The ID of another block storage volume from which the current volume was created + SourceVolID string `json:"source_volid"` + // Arbitrary key-value pairs defined by the user. + Metadata map[string]string `json:"metadata"` + // UserID is the id of the user who created the volume. + UserID string `json:"user_id"` + // Indicates whether this is a bootable volume. + Bootable string `json:"bootable"` + // Encrypted denotes if the volume is encrypted. + Encrypted bool `json:"encrypted"` + // ReplicationStatus is the status of replication. + ReplicationStatus string `json:"replication_status"` + // ConsistencyGroupID is the consistency group ID. + ConsistencyGroupID string `json:"consistencygroup_id"` + // Multiattach denotes if the volume is multi-attach capable. + Multiattach bool `json:"multiattach"` +} + +// UnmarshalJSON another unmarshalling function +func (r *Volume) UnmarshalJSON(b []byte) error { + type tmp Volume + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Volume(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +// VolumePage is a pagination.pager that is returned from a call to the List function. +type VolumePage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a ListResult contains no Volumes. +func (r VolumePage) IsEmpty() (bool, error) { + volumes, err := ExtractVolumes(r) + return len(volumes) == 0, err +} + +// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. +func ExtractVolumes(r pagination.Page) ([]Volume, error) { + var s []Volume + err := ExtractVolumesInto(r, &s) + return s, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Volume object out of the commonResult object. +func (r commonResult) Extract() (*Volume, error) { + var s Volume + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a volume struct +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "volume") +} + +// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes +func ExtractVolumesInto(r pagination.Page, v interface{}) error { + return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/volumes/testing/doc.go b/openstack/blockstorage/v3/volumes/testing/doc.go new file mode 100644 index 0000000000..a2b24b7c19 --- /dev/null +++ b/openstack/blockstorage/v3/volumes/testing/doc.go @@ -0,0 +1,2 @@ +// volumes_v3 +package testing diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures.go b/openstack/blockstorage/v3/volumes/testing/fixtures.go new file mode 100644 index 0000000000..44d2ca383c --- /dev/null +++ b/openstack/blockstorage/v3/volumes/testing/fixtures.go @@ -0,0 +1,203 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/detail", 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, ` + { + "volumes": [ + { + "volume_type": "lvmdriver-1", + "created_at": "2015-09-17T03:35:03.000000", + "bootable": "false", + "name": "vol-001", + "os-vol-mig-status-attr:name_id": null, + "consistencygroup_id": null, + "source_volid": null, + "os-volume-replication:driver_data": null, + "multiattach": false, + "snapshot_id": null, + "replication_status": "disabled", + "os-volume-replication:extended_status": null, + "encrypted": false, + "os-vol-host-attr:host": null, + "availability_zone": "nova", + "attachments": [{ + "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", + "attached_at": "2016-08-06T14:48:20.000000", + "host_name": "foobar", + "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + "device": "/dev/vdc", + "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" + }], + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "size": 75, + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", + "os-vol-mig-status-attr:migstat": null, + "metadata": {"foo": "bar"}, + "status": "available", + "description": null + }, + { + "volume_type": "lvmdriver-1", + "created_at": "2015-09-17T03:32:29.000000", + "bootable": "false", + "name": "vol-002", + "os-vol-mig-status-attr:name_id": null, + "consistencygroup_id": null, + "source_volid": null, + "os-volume-replication:driver_data": null, + "multiattach": false, + "snapshot_id": null, + "replication_status": "disabled", + "os-volume-replication:extended_status": null, + "encrypted": false, + "os-vol-host-attr:host": null, + "availability_zone": "nova", + "attachments": [], + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "size": 75, + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", + "os-vol-mig-status-attr:migstat": null, + "metadata": {}, + "status": "available", + "description": null + } + ] +} + `) + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/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, ` +{ + "volume": { + "volume_type": "lvmdriver-1", + "created_at": "2015-09-17T03:32:29.000000", + "bootable": "false", + "name": "vol-001", + "os-vol-mig-status-attr:name_id": null, + "consistencygroup_id": null, + "source_volid": null, + "os-volume-replication:driver_data": null, + "multiattach": false, + "snapshot_id": null, + "replication_status": "disabled", + "os-volume-replication:extended_status": null, + "encrypted": false, + "os-vol-host-attr:host": null, + "availability_zone": "nova", + "attachments": [{ + "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "attachment_id": "05551600-a936-4d4a-ba42-79a037c1-c91a", + "attached_at": "2016-08-06T14:48:20.000000", + "host_name": "foobar", + "volume_id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + "device": "/dev/vdc", + "id": "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75" + }], + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "size": 75, + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "os-vol-tenant-attr:tenant_id": "304dc00909ac4d0da6c62d816bcb3459", + "os-vol-mig-status-attr:migstat": null, + "metadata": {}, + "status": "available", + "description": null + } +} + `) + }) +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes", 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, ` +{ + "volume": { + "name": "vol-001", + "size": 75 + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, ` +{ + "volume": { + "size": 75, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "metadata": {}, + "created_at": "2015-09-17T03:32:29.044216", + "encrypted": false, + "bootable": "false", + "availability_zone": "nova", + "attachments": [], + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "status": "creating", + "description": null, + "volume_type": "lvmdriver-1", + "name": "vol-001", + "replication_status": "disabled", + "consistencygroup_id": null, + "source_volid": null, + "snapshot_id": null, + "multiattach": false + } +} + `) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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 MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/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) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "volume": { + "name": "vol-002" + } +} + `) + }) +} diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go new file mode 100644 index 0000000000..5a1e46b653 --- /dev/null +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -0,0 +1,257 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + count := 0 + + volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := volumes.ExtractVolumes(page) + if err != nil { + t.Errorf("Failed to extract volumes: %v", err) + return false, err + } + + expected := []volumes.Volume{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "vol-001", + Attachments: []volumes.Attachment{{ + ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", + AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", + AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), + HostName: "foobar", + VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + Device: "/dev/vdc", + ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + }}, + AvailabilityZone: "nova", + Bootable: "false", + ConsistencyGroupID: "", + CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), + Description: "", + Encrypted: false, + Metadata: map[string]string{"foo": "bar"}, + Multiattach: false, + //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + //ReplicationDriverData: "", + //ReplicationExtendedStatus: "", + ReplicationStatus: "disabled", + Size: 75, + SnapshotID: "", + SourceVolID: "", + Status: "available", + UserID: "ff1ce52c03ab433aaba9108c2e3ef541", + VolumeType: "lvmdriver-1", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "vol-002", + Attachments: []volumes.Attachment{}, + AvailabilityZone: "nova", + Bootable: "false", + ConsistencyGroupID: "", + CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), + Description: "", + Encrypted: false, + Metadata: map[string]string{}, + Multiattach: false, + //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + //ReplicationDriverData: "", + //ReplicationExtendedStatus: "", + ReplicationStatus: "disabled", + Size: 75, + SnapshotID: "", + SourceVolID: "", + Status: "available", + UserID: "ff1ce52c03ab433aaba9108c2e3ef541", + VolumeType: "lvmdriver-1", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestListAllWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + type VolumeWithExt struct { + volumes.Volume + volumetenants.VolumeTenantExt + } + + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + var actual []VolumeWithExt + err = volumes.ExtractVolumesInto(allPages, &actual) + th.AssertNoErr(t, err) + th.AssertEquals(t, 2, len(actual)) + th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", actual[0].TenantID) +} + +func TestListAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := volumes.ExtractVolumes(allPages) + th.AssertNoErr(t, err) + + expected := []volumes.Volume{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "vol-001", + Attachments: []volumes.Attachment{{ + ServerID: "83ec2e3b-4321-422b-8706-a84185f52a0a", + AttachmentID: "05551600-a936-4d4a-ba42-79a037c1-c91a", + AttachedAt: time.Date(2016, 8, 6, 14, 48, 20, 0, time.UTC), + HostName: "foobar", + VolumeID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + Device: "/dev/vdc", + ID: "d6cacb1a-8b59-4c88-ad90-d70ebb82bb75", + }}, + AvailabilityZone: "nova", + Bootable: "false", + ConsistencyGroupID: "", + CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), + Description: "", + Encrypted: false, + Metadata: map[string]string{"foo": "bar"}, + Multiattach: false, + //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + //ReplicationDriverData: "", + //ReplicationExtendedStatus: "", + ReplicationStatus: "disabled", + Size: 75, + SnapshotID: "", + SourceVolID: "", + Status: "available", + UserID: "ff1ce52c03ab433aaba9108c2e3ef541", + VolumeType: "lvmdriver-1", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "vol-002", + Attachments: []volumes.Attachment{}, + AvailabilityZone: "nova", + Bootable: "false", + ConsistencyGroupID: "", + CreatedAt: time.Date(2015, 9, 17, 3, 32, 29, 0, time.UTC), + Description: "", + Encrypted: false, + Metadata: map[string]string{}, + Multiattach: false, + //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + //ReplicationDriverData: "", + //ReplicationExtendedStatus: "", + ReplicationStatus: "disabled", + Size: 75, + SnapshotID: "", + SourceVolID: "", + Status: "available", + UserID: "ff1ce52c03ab433aaba9108c2e3ef541", + VolumeType: "lvmdriver-1", + }, + } + + th.CheckDeepEquals(t, expected, actual) + +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "vol-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} + n, err := volumes.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Size, 75) + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + options := volumes.UpdateOpts{Name: "vol-002"} + v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + th.CheckEquals(t, "vol-002", v.Name) +} + +func TestGetWithExtensions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + var s struct { + volumes.Volume + volumetenants.VolumeTenantExt + } + err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + th.AssertNoErr(t, err) + th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) + + err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) + if err == nil { + t.Errorf("Expected error when providing non-pointer struct") + } +} diff --git a/openstack/blockstorage/v3/volumes/urls.go b/openstack/blockstorage/v3/volumes/urls.go new file mode 100644 index 0000000000..170724905a --- /dev/null +++ b/openstack/blockstorage/v3/volumes/urls.go @@ -0,0 +1,23 @@ +package volumes + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("volumes") +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("volumes", "detail") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("volumes", id) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return deleteURL(c, id) +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return deleteURL(c, id) +} diff --git a/openstack/blockstorage/v3/volumes/util.go b/openstack/blockstorage/v3/volumes/util.go new file mode 100644 index 0000000000..e86c1b4b4e --- /dev/null +++ b/openstack/blockstorage/v3/volumes/util.go @@ -0,0 +1,22 @@ +package volumes + +import ( + "github.com/gophercloud/gophercloud" +) + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/client.go b/openstack/client.go index adcf90bcef..c796795b8e 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -308,8 +308,12 @@ func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi return initClientOpts(client, eo, "volumev2") } -// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the -// v2 shared file system service. +// NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service. +func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "volumev3") +} + +// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service. func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "sharev2") } diff --git a/service_client.go b/service_client.go index 1160fefa7c..d1a48fea35 100644 --- a/service_client.go +++ b/service_client.go @@ -114,6 +114,8 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion case "sharev2": opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion + case "volume": + opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion } if client.Type != "" { From 91a6562b1b456b1fad7686c162fd53567c2f3eea Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 24 Nov 2017 05:05:57 +0000 Subject: [PATCH 0130/2296] Compute v2: Fix Flavor IsPublic field --- acceptance/openstack/compute/v2/compute.go | 10 ++++++---- openstack/compute/v2/flavors/results.go | 2 +- openstack/compute/v2/flavors/testing/requests_test.go | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 38c355bc93..fad4673b42 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -143,11 +143,13 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Fla flavorName := tools.RandomString("flavor_", 5) t.Logf("Attempting to create flavor %s", flavorName) + isPublic := true createOpts := flavors.CreateOpts{ - Name: flavorName, - RAM: 1, - VCPUs: 1, - Disk: gophercloud.IntToPointer(1), + Name: flavorName, + RAM: 1, + VCPUs: 1, + Disk: gophercloud.IntToPointer(1), + IsPublic: &isPublic, } flavor, err := flavors.Create(client, createOpts).Extract() diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 47d58ccfb3..fb5c335b8e 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -65,7 +65,7 @@ type Flavor struct { VCPUs int `json:"vcpus"` // IsPublic indicates whether the flavor is public. - IsPublic bool `json:"is_public"` + IsPublic bool `json:"os-flavor-access:is_public"` } 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 9b49924237..9dfdbeaf5b 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -37,7 +37,7 @@ func TestListFlavors(t *testing.T) { "disk": 1, "ram": 512, "swap":"", - "is_public": true + "os-flavor-access:is_public": true }, { "id": "2", @@ -46,7 +46,7 @@ func TestListFlavors(t *testing.T) { "disk": 20, "ram": 2048, "swap": 1000, - "is_public": true + "os-flavor-access:is_public": true }, { "id": "3", @@ -55,7 +55,7 @@ func TestListFlavors(t *testing.T) { "disk": 40, "ram": 4096, "swap": 1000, - "is_public": false + "os-flavor-access:is_public": false } ], "flavors_links": [ From c943ac2cfb890e9c7fa51ec0ce55ca4426de19d0 Mon Sep 17 00:00:00 2001 From: zengchen Date: Sun, 26 Nov 2017 00:17:58 +0800 Subject: [PATCH 0131/2296] fix BuildQueryString does not build map type data structure (#619) * fix BuildQueryString does not build map type data structure * simplify the use case * only support map[string]string --- params.go | 11 ++++++++++- testing/params_test.go | 33 ++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/params.go b/params.go index 6afc8f8b72..687af3dc0c 100644 --- a/params.go +++ b/params.go @@ -347,12 +347,21 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { params.Add(tags[0], v.Index(i).String()) } } + case reflect.Map: + if v.Type().Key().Kind() == reflect.String && v.Type().Elem().Kind() == reflect.String { + var s []string + for _, k := range v.MapKeys() { + value := v.MapIndex(k).String() + s = append(s, fmt.Sprintf("'%s':'%s'", k.String(), value)) + } + params.Add(tags[0], fmt.Sprintf("{%s}", strings.Join(s, ", "))) + } } } else { // Otherwise, the field is not set. if len(tags) == 2 && tags[1] == "required" { // And the field is required. Return an error. - return nil, fmt.Errorf("Required query parameter [%s] not set.", f.Name) + return &url.URL{}, fmt.Errorf("Required query parameter [%s] not set.", f.Name) } } } diff --git a/testing/params_test.go b/testing/params_test.go index 8757079c2a..acf392f2ab 100644 --- a/testing/params_test.go +++ b/testing/params_test.go @@ -37,13 +37,14 @@ func TestBuildQueryString(t *testing.T) { type testVar string iFalse := false opts := struct { - J int `q:"j"` - R string `q:"r,required"` - C bool `q:"c"` - S []string `q:"s"` - TS []testVar `q:"ts"` - TI []int `q:"ti"` - F *bool `q:"f"` + J int `q:"j"` + R string `q:"r,required"` + C bool `q:"c"` + S []string `q:"s"` + TS []testVar `q:"ts"` + TI []int `q:"ti"` + F *bool `q:"f"` + M map[string]string `q:"m"` }{ J: 2, R: "red", @@ -52,8 +53,9 @@ func TestBuildQueryString(t *testing.T) { TS: []testVar{"a", "b"}, TI: []int{1, 2}, F: &iFalse, + M: map[string]string{"k1": "success1"}, } - expected := &url.URL{RawQuery: "c=true&f=false&j=2&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"} + expected := &url.URL{RawQuery: "c=true&f=false&j=2&m=%7B%27k1%27%3A%27success1%27%7D&r=red&s=one&s=two&s=three&ti=1&ti=2&ts=a&ts=b"} actual, err := gophercloud.BuildQueryString(&opts) if err != nil { t.Errorf("Error building query string: %v", err) @@ -61,13 +63,14 @@ func TestBuildQueryString(t *testing.T) { th.CheckDeepEquals(t, expected, actual) opts = struct { - J int `q:"j"` - R string `q:"r,required"` - C bool `q:"c"` - S []string `q:"s"` - TS []testVar `q:"ts"` - TI []int `q:"ti"` - F *bool `q:"f"` + J int `q:"j"` + R string `q:"r,required"` + C bool `q:"c"` + S []string `q:"s"` + TS []testVar `q:"ts"` + TI []int `q:"ti"` + F *bool `q:"f"` + M map[string]string `q:"m"` }{ J: 2, C: true, From fdb0ced38cdd245c01f6630bf72bcf086d096ed8 Mon Sep 17 00:00:00 2001 From: Obed N Munoz Date: Mon, 27 Nov 2017 14:50:00 -0600 Subject: [PATCH 0132/2296] Fix wrong status on Resize function comment This commit changes the VERIFY status for the resize operation. The correct VERIFY_RESIZE status was gotten from: https://developer.openstack.org/api-guide/compute/server_concepts.html Signed-off-by: Obed N Munoz --- openstack/compute/v2/servers/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index c445f0ede0..626eb63e91 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -515,7 +515,7 @@ func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { // Note that this implies rebuilding it. // // Unfortunately, one cannot pass rebuild parameters to the resize function. -// When the resize completes, the server will be in RESIZE_VERIFY state. +// When the resize completes, the server will be in VERIFY_RESIZE state. // While in this state, you can explore the use of the new server's // configuration. If you like it, call ConfirmResize() to commit the resize // permanently. Otherwise, call RevertResize() to restore the old configuration. From 2c05d0e441e97f693f8380f159aed4c56ddef188 Mon Sep 17 00:00:00 2001 From: Hu Sheng Date: Tue, 28 Nov 2017 12:26:39 +0800 Subject: [PATCH 0133/2296] Add 'tenant' support in volume&snapshot API (#628) * Add 'tenant' support in volume&snapshot API `tenant` is required in a multi-tenant environment. Add this support in V3 APIs. * aDD 'tenant' support in volume&snapshot API `tenant` is required in a multi-tenant environment. Add this support in V3 APIs. --- openstack/blockstorage/v2/snapshots/requests.go | 5 +++++ openstack/blockstorage/v2/volumes/requests.go | 3 +++ openstack/blockstorage/v3/snapshots/requests.go | 5 +++++ openstack/blockstorage/v3/volumes/requests.go | 3 +++ 4 files changed, 16 insertions(+) diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go index 1f8f81b89a..a32ca2f4cb 100644 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ b/openstack/blockstorage/v2/snapshots/requests.go @@ -68,6 +68,11 @@ type ListOpts struct { Name string `q:"name"` Status string `q:"status"` VolumeID string `q:"volume_id"` + // Admin-only option. Set it to true to see all tenant snapshots. + AllTenants bool `q:"all_tenants"` + // List only snapshots that belongs to one particular tenant. + // Setting "AllTenants = true" is required. + TenantID string `q:"project_id"` } // ToSnapshotListQuery formats a ListOpts into a query string. diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index 18c9cb272e..2f9cfd14f7 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -91,6 +91,9 @@ type ListOpts struct { Name string `q:"name"` // List only volumes that have a status of Status. Status string `q:"status"` + // List only volumes that belongs to one particular tenant. + // Setting "AllTenants = true" is required. + TenantID string `q:"project_id"` } // ToVolumeListQuery formats a ListOpts into a query string. diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 1f8f81b89a..a32ca2f4cb 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -68,6 +68,11 @@ type ListOpts struct { Name string `q:"name"` Status string `q:"status"` VolumeID string `q:"volume_id"` + // Admin-only option. Set it to true to see all tenant snapshots. + AllTenants bool `q:"all_tenants"` + // List only snapshots that belongs to one particular tenant. + // Setting "AllTenants = true" is required. + TenantID string `q:"project_id"` } // ToSnapshotListQuery formats a ListOpts into a query string. diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 18c9cb272e..2f9cfd14f7 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -91,6 +91,9 @@ type ListOpts struct { Name string `q:"name"` // List only volumes that have a status of Status. Status string `q:"status"` + // List only volumes that belongs to one particular tenant. + // Setting "AllTenants = true" is required. + TenantID string `q:"project_id"` } // ToVolumeListQuery formats a ListOpts into a query string. From c47bb004850a7824e656e9027635d7f8d7dd06ec Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 28 Nov 2017 04:33:26 +0000 Subject: [PATCH 0134/2296] BlockStorage v2/v3: Reorder snapshot/volume ListOpts and update godoc --- .../blockstorage/v2/snapshots/requests.go | 19 +++++++++++------ openstack/blockstorage/v2/volumes/requests.go | 16 ++++++++------ .../blockstorage/v3/snapshots/requests.go | 21 ++++++++++++------- openstack/blockstorage/v3/volumes/requests.go | 16 ++++++++------ 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go index a32ca2f4cb..dc707fd93d 100644 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ b/openstack/blockstorage/v2/snapshots/requests.go @@ -65,14 +65,21 @@ type ListOptsBuilder interface { // ListOpts hold options for listing Snapshots. It is passed to the // snapshots.List function. type ListOpts struct { - Name string `q:"name"` - Status string `q:"status"` - VolumeID string `q:"volume_id"` - // Admin-only option. Set it to true to see all tenant snapshots. + // AllTenants will retrieve snapshots of all tenants/projects. AllTenants bool `q:"all_tenants"` - // List only snapshots that belongs to one particular tenant. - // Setting "AllTenants = true" is required. + + // Name will filter by the specified snapshot name. + Name string `q:"name"` + + // Status will filter by the specified status. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required to use this. TenantID string `q:"project_id"` + + // VolumeID will filter by a specified volume ID. + VolumeID string `q:"volume_id"` } // ToSnapshotListQuery formats a ListOpts into a query string. diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index 2f9cfd14f7..2ec10ad55e 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -83,16 +83,20 @@ type ListOptsBuilder interface { // ListOpts holds options for listing Volumes. It is passed to the volumes.List // function. type ListOpts struct { - // admin-only option. Set it to true to see all tenant volumes. + // AllTenants will retrieve volumes of all tenants/projects. AllTenants bool `q:"all_tenants"` - // List only volumes that contain Metadata. + + // Metadata will filter results based on specified metadata. Metadata map[string]string `q:"metadata"` - // List only volumes that have Name as the display name. + + // Name will filter by the specified volume name. Name string `q:"name"` - // List only volumes that have a status of Status. + + // Status will filter by the specified status. Status string `q:"status"` - // List only volumes that belongs to one particular tenant. - // Setting "AllTenants = true" is required. + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required for this. TenantID string `q:"project_id"` } diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index a32ca2f4cb..65679bf2f5 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -62,17 +62,22 @@ type ListOptsBuilder interface { ToSnapshotListQuery() (string, error) } -// ListOpts hold options for listing Snapshots. It is passed to the -// snapshots.List function. type ListOpts struct { - Name string `q:"name"` - Status string `q:"status"` - VolumeID string `q:"volume_id"` - // Admin-only option. Set it to true to see all tenant snapshots. + // AllTenants will retrieve snapshots of all tenants/projects. AllTenants bool `q:"all_tenants"` - // List only snapshots that belongs to one particular tenant. - // Setting "AllTenants = true" is required. + + // Name will filter by the specified snapshot name. + Name string `q:"name"` + + // Status will filter by the specified status. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required to use this. TenantID string `q:"project_id"` + + // VolumeID will filter by a specified volume ID. + VolumeID string `q:"volume_id"` } // ToSnapshotListQuery formats a ListOpts into a query string. diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 2f9cfd14f7..2ec10ad55e 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -83,16 +83,20 @@ type ListOptsBuilder interface { // ListOpts holds options for listing Volumes. It is passed to the volumes.List // function. type ListOpts struct { - // admin-only option. Set it to true to see all tenant volumes. + // AllTenants will retrieve volumes of all tenants/projects. AllTenants bool `q:"all_tenants"` - // List only volumes that contain Metadata. + + // Metadata will filter results based on specified metadata. Metadata map[string]string `q:"metadata"` - // List only volumes that have Name as the display name. + + // Name will filter by the specified volume name. Name string `q:"name"` - // List only volumes that have a status of Status. + + // Status will filter by the specified status. Status string `q:"status"` - // List only volumes that belongs to one particular tenant. - // Setting "AllTenants = true" is required. + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required for this. TenantID string `q:"project_id"` } From e6a5f874eedda2f955ec567fb64359e397fa1375 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 28 Nov 2017 04:39:06 +0000 Subject: [PATCH 0135/2296] ObjectStorage v1: Rename ExtractLastMarker to extractLastMarker --- openstack/objectstorage/v1/objects/results.go | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index af06c2f836..a47b39343c 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -70,63 +70,7 @@ func (r ObjectPage) IsEmpty() (bool, error) { // LastMarker returns the last object name in a ListResult. func (r ObjectPage) LastMarker() (string, error) { - return ExtractLastMarker(r) -} - -// ExtractLastMarker is a function that takes a page of objects and returns the -// marker for the page. This can either be a subdir or the last object's name. -func ExtractLastMarker(r pagination.Page) (string, error) { - casted := r.(ObjectPage) - - // If a delimiter was requested, check if a subdir exists. - queryParams, err := url.ParseQuery(casted.URL.RawQuery) - if err != nil { - return "", err - } - - var delimeter bool - if v, ok := queryParams["delimiter"]; ok && len(v) > 0 { - delimeter = true - } - - ct := casted.Header.Get("Content-Type") - switch { - case strings.HasPrefix(ct, "application/json"): - parsed, err := ExtractInfo(r) - if err != nil { - return "", err - } - - var lastObject Object - if len(parsed) > 0 { - lastObject = parsed[len(parsed)-1] - } - - if !delimeter { - return lastObject.Name, nil - } - - if lastObject.Name != "" { - return lastObject.Name, nil - } - - return lastObject.Subdir, nil - case strings.HasPrefix(ct, "text/plain"): - names := make([]string, 0, 50) - - body := string(r.(ObjectPage).Body.([]uint8)) - for _, name := range strings.Split(body, "\n") { - if len(name) > 0 { - names = append(names, name) - } - } - - return names[len(names)-1], err - case strings.HasPrefix(ct, "text/html"): - return "", nil - default: - return "", fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct) - } + return extractLastMarker(r) } // ExtractInfo is a function that takes a page of objects and returns their @@ -551,3 +495,59 @@ func (r CopyResult) Extract() (*CopyHeader, error) { err := r.ExtractInto(&s) return s, err } + +// extractLastMarker is a function that takes a page of objects and returns the +// marker for the page. This can either be a subdir or the last object's name. +func extractLastMarker(r pagination.Page) (string, error) { + casted := r.(ObjectPage) + + // If a delimiter was requested, check if a subdir exists. + queryParams, err := url.ParseQuery(casted.URL.RawQuery) + if err != nil { + return "", err + } + + var delimeter bool + if v, ok := queryParams["delimiter"]; ok && len(v) > 0 { + delimeter = true + } + + ct := casted.Header.Get("Content-Type") + switch { + case strings.HasPrefix(ct, "application/json"): + parsed, err := ExtractInfo(r) + if err != nil { + return "", err + } + + var lastObject Object + if len(parsed) > 0 { + lastObject = parsed[len(parsed)-1] + } + + if !delimeter { + return lastObject.Name, nil + } + + if lastObject.Name != "" { + return lastObject.Name, nil + } + + return lastObject.Subdir, nil + case strings.HasPrefix(ct, "text/plain"): + names := make([]string, 0, 50) + + body := string(r.(ObjectPage).Body.([]uint8)) + for _, name := range strings.Split(body, "\n") { + if len(name) > 0 { + names = append(names, name) + } + } + + return names[len(names)-1], err + case strings.HasPrefix(ct, "text/html"): + return "", nil + default: + return "", fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct) + } +} From e193578cf0081abbeeca2b4a415afb9a6c2c9d04 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 30 Nov 2017 11:42:10 -0600 Subject: [PATCH 0136/2296] add UseTokenLock method in ProviderClient to allow safe concurrent access (#636) * define a new 'mut' field in ProviderClient to allow safe concurrent access to it * add Token and SetToken methods on ProviderClient * directly set TokenID in reauth func; add comments about new methods * change method name: UseSafeReauth->UseTokenLock; call UseTokenLock in gophercloud.NewClient --- openstack/client.go | 9 ++-- provider_client.go | 52 ++++++++++++++++---- testing/provider_client_test.go | 84 +++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 12 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index c796795b8e..8f762e9c52 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -56,11 +56,12 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { endpoint = gophercloud.NormalizeURL(endpoint) base = gophercloud.NormalizeURL(base) - return &gophercloud.ProviderClient{ - IdentityBase: base, - IdentityEndpoint: endpoint, - }, nil + p := new(gophercloud.ProviderClient) + p.IdentityBase = base + p.IdentityEndpoint = endpoint + p.UseTokenLock() + return p, nil } /* diff --git a/provider_client.go b/provider_client.go index 01b3010739..f22fff1ec3 100644 --- a/provider_client.go +++ b/provider_client.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" "strings" + "sync" ) // DefaultUserAgent is the default User-Agent string set in the request header. @@ -51,6 +52,8 @@ type ProviderClient struct { IdentityEndpoint string // TokenID is the ID of the most recently issued valid token. + // NOTE: Aside from within a custom ReauthFunc, this field shouldn't be set by an application. + // To safely read or write this value, call `Token` or `SetToken`, respectively TokenID string // EndpointLocator describes how this provider discovers the endpoints for @@ -68,16 +71,43 @@ type ProviderClient struct { // authentication functions for different Identity service versions. ReauthFunc func() error - Debug bool + mut *sync.RWMutex } // AuthenticatedHeaders returns a map of HTTP headers that are common for all // authenticated service requests. func (client *ProviderClient) AuthenticatedHeaders() map[string]string { - if client.TokenID == "" { + t := client.Token() + if t == "" { return map[string]string{} } - return map[string]string{"X-Auth-Token": client.TokenID} + return map[string]string{"X-Auth-Token": t} +} + +// UseTokenLock creates a mutex that is used to allow safe concurrent access to the auth token. +// If the application's ProviderClient is not used concurrently, this doesn't need to be called. +func (client *ProviderClient) UseTokenLock() { + client.mut = new(sync.RWMutex) +} + +// Token safely reads the value of the auth token from the ProviderClient. Applications should +// call this method to access the token instead of the TokenID field +func (client *ProviderClient) Token() string { + if client.mut != nil { + client.mut.RLock() + defer client.mut.RUnlock() + } + return client.TokenID +} + +// SetToken safely sets the value of the auth token in the ProviderClient. Applications may +// use this method in a custom ReauthFunc +func (client *ProviderClient) SetToken(t string) { + if client.mut != nil { + client.mut.Lock() + defer client.mut.Unlock() + } + client.TokenID = t } // RequestOpts customizes the behavior of the provider.Request() method. @@ -166,6 +196,8 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) // Set connection parameter to close the connection immediately when we've got the response req.Close = true + prereqtok := req.Header.Get("X-Auth-Token") + // Issue the request. resp, err := client.HTTPClient.Do(req) if err != nil { @@ -189,9 +221,6 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) if !ok { body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() - //pc := make([]uintptr, 1) - //runtime.Callers(2, pc) - //f := runtime.FuncForPC(pc[0]) respErr := ErrUnexpectedResponseCode{ URL: url, Method: method, @@ -199,7 +228,6 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) Actual: resp.StatusCode, Body: body, } - //respErr.Function = "gophercloud.ProviderClient.Request" errType := options.ErrorContext switch resp.StatusCode { @@ -210,7 +238,15 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } case http.StatusUnauthorized: if client.ReauthFunc != nil { - err = client.ReauthFunc() + if client.mut != nil { + client.mut.Lock() + if curtok := client.TokenID; curtok == prereqtok { + err = client.ReauthFunc() + } + client.mut.Unlock() + } else { + err = client.ReauthFunc() + } if err != nil { e := &ErrUnableToReauthenticate{} e.ErrOriginal = respErr diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 7c0e84eae6..effdc1a8c2 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -1,10 +1,16 @@ package testing import ( + "fmt" + "io/ioutil" + "net/http" + "sync" "testing" + "time" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) func TestAuthenticatedHeaders(t *testing.T) { @@ -34,3 +40,81 @@ func TestUserAgent(t *testing.T) { actual = p.UserAgent.Join() th.CheckEquals(t, expected, actual) } + +func TestConcurrentReauth(t *testing.T) { + var info = struct { + numreauths int + mut *sync.RWMutex + }{ + 0, + new(sync.RWMutex), + } + + numconc := 20 + + prereauthTok := client.TokenID + postreauthTok := "12345678" + + p := new(gophercloud.ProviderClient) + p.UseTokenLock() + p.SetToken(prereauthTok) + p.ReauthFunc = func() error { + time.Sleep(1 * time.Second) + info.mut.Lock() + info.numreauths++ + info.mut.Unlock() + p.TokenID = postreauthTok + return nil + } + + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("X-Auth-Token") != postreauthTok { + w.WriteHeader(http.StatusUnauthorized) + return + } + info.mut.RLock() + hasReauthed := info.numreauths != 0 + info.mut.RUnlock() + + if hasReauthed { + th.CheckEquals(t, p.Token(), postreauthTok) + } + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, `{}`) + }) + + wg := new(sync.WaitGroup) + reqopts := new(gophercloud.RequestOpts) + + for i := 0; i < numconc; i++ { + wg.Add(1) + go func() { + defer wg.Done() + resp, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) + th.CheckNoErr(t, err) + if resp == nil { + t.Errorf("got a nil response") + return + } + if resp.Body == nil { + t.Errorf("response body was nil") + return + } + defer resp.Body.Close() + actual, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("error reading response body: %s", err) + return + } + th.CheckByteArrayEquals(t, []byte(`{}`), actual) + }() + } + + wg.Wait() + + th.AssertEquals(t, 1, info.numreauths) +} From e30da2315e8652dd1a63f76c1763aef64deec97e Mon Sep 17 00:00:00 2001 From: Hu Sheng Date: Fri, 1 Dec 2017 11:10:45 +0800 Subject: [PATCH 0137/2296] Feature/support force delete (#635) * Supprt extend volume in v2/v3 APIs Add extending volume support in v2/v3 APIs * Support force_delete in volume V2&V3 APIs Add force_delete action support in volume v2&v3 APIs * Supprt extend volume in v2/v3 APIs Add extending volume support in v2/v3 APIs * Support force_delete in volume V2&V3 APIs Add force_delete action support in volume v2&v3 APIs --- .../blockstorage/extensions/volumeactions/requests.go | 6 ++++++ .../blockstorage/extensions/volumeactions/results.go | 5 +++++ .../extensions/volumeactions/testing/fixtures.go | 9 +++++++++ .../extensions/volumeactions/testing/requests_test.go | 10 ++++++++++ .../blockstorage/extensions/volumeactions/urls.go | 4 ++++ 5 files changed, 34 insertions(+) diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index a3916c77c1..a62d8c8173 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -261,3 +261,9 @@ func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageO }) return } + +// ForceDelete will delete the volume regardless of state. +func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + _, r.Err = client.Post(forceDeleteURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + return +} diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index 9815f0c26a..5cadd360f2 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -184,3 +184,8 @@ func (r UploadImageResult) Extract() (VolumeImage, error) { err := r.ExtractInto(&s) return s.VolumeImage, err } + +// ForceDeleteResult contains the response body and error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go index d51c2b6ad4..4a0c21701e 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go @@ -276,3 +276,12 @@ func MockExtendSizeResponse(t *testing.T) { fmt.Fprintf(w, `{}`) }) } + +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/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.TestBody(t, r, `{"os-force_delete":""}`) + 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 667a3edb81..38fae47889 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go @@ -154,3 +154,13 @@ func TestExtendSize(t *testing.T) { err := volumeactions.ExtendSize(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } + +func TestForceDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockForceDeleteResponse(t) + + res := volumeactions.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/extensions/volumeactions/urls.go b/openstack/blockstorage/extensions/volumeactions/urls.go index 5efd2b25c0..ff35b1aed6 100644 --- a/openstack/blockstorage/extensions/volumeactions/urls.go +++ b/openstack/blockstorage/extensions/volumeactions/urls.go @@ -37,3 +37,7 @@ func teminateConnectionURL(c *gophercloud.ServiceClient, id string) string { func extendSizeURL(c *gophercloud.ServiceClient, id string) string { return attachURL(c, id) } + +func forceDeleteURL(c *gophercloud.ServiceClient, id string) string { + return attachURL(c, id) +} From 1e86e54d3a31e9121073586c8772aa40501d86a3 Mon Sep 17 00:00:00 2001 From: TommyLike Date: Fri, 1 Dec 2017 11:23:23 +0800 Subject: [PATCH 0138/2296] Refactor blockstorage actionURL Replace volumeactions' xxxxURL with actionURL. --- .../extensions/volumeactions/requests.go | 20 +++++----- .../extensions/volumeactions/urls.go | 38 +------------------ 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index a62d8c8173..d18bff555b 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -47,7 +47,7 @@ func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder r.Err = err return } - _, r.Err = client.Post(attachURL(client, id), b, nil, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return @@ -56,7 +56,7 @@ func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder // BeginDetach will mark the volume as detaching. func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} - _, r.Err = client.Post(beginDetachingURL(client, id), b, nil, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return @@ -87,7 +87,7 @@ func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder r.Err = err return } - _, r.Err = client.Post(detachURL(client, id), b, nil, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return @@ -96,7 +96,7 @@ func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder // Reserve will reserve a volume based on volume ID. func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { b := map[string]interface{}{"os-reserve": make(map[string]interface{})} - _, r.Err = client.Post(reserveURL(client, id), b, nil, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return @@ -105,7 +105,7 @@ func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { // Unreserve will unreserve a volume based on volume ID. func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) { b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} - _, r.Err = client.Post(unreserveURL(client, id), b, nil, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return @@ -145,7 +145,7 @@ func InitializeConnection(client *gophercloud.ServiceClient, id string, opts Ini r.Err = err return } - _, r.Err = client.Post(initializeConnectionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) return @@ -183,7 +183,7 @@ func TerminateConnection(client *gophercloud.ServiceClient, id string, opts Term r.Err = err return } - _, r.Err = client.Post(teminateConnectionURL(client, id), b, nil, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return @@ -216,7 +216,7 @@ func ExtendSize(client *gophercloud.ServiceClient, id string, opts ExtendSizeOpt r.Err = err return } - _, r.Err = client.Post(extendSizeURL(client, id), b, nil, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return @@ -256,7 +256,7 @@ func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageO r.Err = err return } - _, r.Err = client.Post(uploadURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) return @@ -264,6 +264,6 @@ func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageO // ForceDelete will delete the volume regardless of state. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - _, r.Err = client.Post(forceDeleteURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) return } diff --git a/openstack/blockstorage/extensions/volumeactions/urls.go b/openstack/blockstorage/extensions/volumeactions/urls.go index ff35b1aed6..20486ed719 100644 --- a/openstack/blockstorage/extensions/volumeactions/urls.go +++ b/openstack/blockstorage/extensions/volumeactions/urls.go @@ -2,42 +2,6 @@ package volumeactions import "github.com/gophercloud/gophercloud" -func attachURL(c *gophercloud.ServiceClient, id string) string { +func actionURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("volumes", id, "action") } - -func beginDetachingURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} - -func detachURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} - -func uploadURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} - -func reserveURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} - -func unreserveURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} - -func initializeConnectionURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} - -func teminateConnectionURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} - -func extendSizeURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} - -func forceDeleteURL(c *gophercloud.ServiceClient, id string) string { - return attachURL(c, id) -} From 7dc13e0d6bc39fbb158c9dd60b311547846a5866 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 1 Dec 2017 03:26:19 +0000 Subject: [PATCH 0139/2296] AccTests: BlockStorage v2 ForceDelete --- .../openstack/blockstorage/v2/volumes_test.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go index 9003ca7111..630b21e492 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" ) @@ -50,3 +51,27 @@ func TestVolumesCreateDestroy(t *testing.T) { tools.PrintResource(t, newVolume) } + +func TestVolumesCreateForceDestroy(t *testing.T) { + client, err := clients.NewBlockStorageV2Client() + if err != nil { + t.Fatalf("Unable to create blockstorage client: %v", err) + } + + volume, err := CreateVolume(t, client) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + + newVolume, err := volumes.Get(client, volume.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve volume: %v", err) + } + + tools.PrintResource(t, newVolume) + + err = volumeactions.ForceDelete(client, newVolume.ID).ExtractErr() + if err != nil { + t.Errorf("Unable to force delete volume: %v", err) + } +} From 4d0f825353f8630091416f74e29d660cc19a3cbc Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 3 Dec 2017 02:08:05 +0300 Subject: [PATCH 0140/2296] Add support to get interface of a server (#642) * Add support to get interface of a server through a GET on: /v2.1/{tenant_id}/servers/{server_id}/os-interface/{port_id} * Rename interfaceID to portID in Get request of the attachinterfaces extension. * Update the Get response code in Nova attachinterfaces extensions. Nova API only returns 200 status for the GET os-interfaces request. --- .../v2/extensions/attachinterfaces/doc.go | 9 ++++ .../extensions/attachinterfaces/requests.go | 8 ++++ .../v2/extensions/attachinterfaces/results.go | 20 ++++++++ .../attachinterfaces/testing/fixtures.go | 47 +++++++++++++++++++ .../attachinterfaces/testing/requests_test.go | 15 ++++++ .../v2/extensions/attachinterfaces/urls.go | 4 ++ 6 files changed, 103 insertions(+) diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go index a996013711..f43a2e2136 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -18,5 +18,14 @@ Example of Listing a Server's Interfaces for _, interface := range allInterfaces { fmt.Printf("%+v\n", interface) } + +Example to Get a Server's Interface + + portID = "0dde1598-b374-474e-986f-5b8dd1df1d4e" + serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" + interface, err := attachinterfaces.Get(computeClient, serverID, portID).Extract() + if err != nil { + panic(err) + } */ package attachinterfaces diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go index faf2747246..34a5248c09 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/requests.go +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -11,3 +11,11 @@ func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { return InterfacePage{pagination.SinglePageBase(r)} }) } + +// Get requests details on a single interface attachment by the server and port IDs. +func Get(client *gophercloud.ServiceClient, serverID, portID string) (r GetResult) { + _, r.Err = client.Get(getInterfaceURL(client, serverID, portID), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/extensions/attachinterfaces/results.go index e3987eaca8..ef045bf09b 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/results.go +++ b/openstack/compute/v2/extensions/attachinterfaces/results.go @@ -1,9 +1,29 @@ package attachinterfaces import ( + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) +type attachInterfaceResult struct { + gophercloud.Result +} + +// Extract interprets any attachInterfaceResult as an Interface, if possible. +func (r attachInterfaceResult) Extract() (*Interface, error) { + var s struct { + Interface *Interface `json:"interfaceAttachment"` + } + err := r.ExtractInto(&s) + return s.Interface, err +} + +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as a Interface. +type GetResult struct { + attachInterfaceResult +} + // FixedIP represents a Fixed IP Address. type FixedIP struct { SubnetID string `json:"subnet_id"` diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go index 017243f707..a7ef1d94dd 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go @@ -30,6 +30,24 @@ var ListInterfacesExpected = []attachinterfaces.Interface{ }, } +// GetInterfaceExpected represents an expected repsonse from a GetInterface request. +var GetInterfaceExpected = attachinterfaces.Interface{ + PortState: "ACTIVE", + FixedIPs: []attachinterfaces.FixedIP{ + { + SubnetID: "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", + IPAddress: "10.0.0.7", + }, + { + SubnetID: "45906d64-a548-4276-h1f8-kcffa80fjbnl", + IPAddress: "10.0.0.8", + }, + }, + PortID: "0dde1598-b374-474e-986f-5b8dd1df1d4e", + NetID: "8a5fe506-7e9f-4091-899b-96336909d93c", + MACAddr: "fa:16:3e:38:2d:80", +} + // HandleInterfaceListSuccessfully sets up the test server to respond to a ListInterfaces request. func HandleInterfaceListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface", func(w http.ResponseWriter, r *http.Request) { @@ -59,3 +77,32 @@ func HandleInterfaceListSuccessfully(t *testing.T) { }`) }) } + +// HandleInterfaceGetSuccessfully sets up the test server to respond to a GetInterface request. +func HandleInterfaceGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface/0dde1598-b374-474e-986f-5b8dd1df1d4e", 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, `{ + "interfaceAttachment": + { + "port_state":"ACTIVE", + "fixed_ips": [ + { + "subnet_id": "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", + "ip_address": "10.0.0.7" + }, + { + "subnet_id": "45906d64-a548-4276-h1f8-kcffa80fjbnl", + "ip_address": "10.0.0.8" + } + ], + "port_id": "0dde1598-b374-474e-986f-5b8dd1df1d4e", + "net_id": "8a5fe506-7e9f-4091-899b-96336909d93c", + "mac_addr": "fa:16:3e:38:2d:80" + } + }`) + }) +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go index d7eb511daa..628374d933 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go @@ -43,3 +43,18 @@ func TestListInterfacesAllPages(t *testing.T) { _, err = attachinterfaces.ExtractInterfaces(allPages) th.AssertNoErr(t, err) } + +func TestGetInterface(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleInterfaceGetSuccessfully(t) + + expected := GetInterfaceExpected + + serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" + interfaceID := "0dde1598-b374-474e-986f-5b8dd1df1d4e" + + actual, err := attachinterfaces.Get(client.ServiceClient(), serverID, interfaceID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/urls.go b/openstack/compute/v2/extensions/attachinterfaces/urls.go index 7d376f99bb..7154eceb3c 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/urls.go +++ b/openstack/compute/v2/extensions/attachinterfaces/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func listInterfaceURL(client *gophercloud.ServiceClient, serverID string) string { return client.ServiceURL("servers", serverID, "os-interface") } + +func getInterfaceURL(client *gophercloud.ServiceClient, serverID, portID string) string { + return client.ServiceURL("servers", serverID, "os-interface", portID) +} From 7883fd95c9c94c0ce123f546f05d38660e97704a Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 2 Dec 2017 19:28:57 -0600 Subject: [PATCH 0141/2296] fix reauth deadlock by not calling Token() during reauth (#646) * don't call Token() during reauth; call AuthenticatedHeaders in unit test * guard reauth check with mutex --- provider_client.go | 26 ++++++++++++++++++++++++-- testing/provider_client_test.go | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/provider_client.go b/provider_client.go index f22fff1ec3..72daeb0a3e 100644 --- a/provider_client.go +++ b/provider_client.go @@ -72,14 +72,29 @@ type ProviderClient struct { ReauthFunc func() error mut *sync.RWMutex + + reauthmut *reauthlock +} + +type reauthlock struct { + sync.RWMutex + reauthing bool } // AuthenticatedHeaders returns a map of HTTP headers that are common for all // authenticated service requests. -func (client *ProviderClient) AuthenticatedHeaders() map[string]string { +func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { + if client.reauthmut != nil { + client.reauthmut.RLock() + if client.reauthmut.reauthing { + client.reauthmut.RUnlock() + return + } + client.reauthmut.RUnlock() + } t := client.Token() if t == "" { - return map[string]string{} + return } return map[string]string{"X-Auth-Token": t} } @@ -88,6 +103,7 @@ func (client *ProviderClient) AuthenticatedHeaders() map[string]string { // If the application's ProviderClient is not used concurrently, this doesn't need to be called. func (client *ProviderClient) UseTokenLock() { client.mut = new(sync.RWMutex) + client.reauthmut = new(reauthlock) } // Token safely reads the value of the auth token from the ProviderClient. Applications should @@ -240,9 +256,15 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) if client.ReauthFunc != nil { if client.mut != nil { client.mut.Lock() + client.reauthmut.Lock() + client.reauthmut.reauthing = true + client.reauthmut.Unlock() if curtok := client.TokenID; curtok == prereqtok { err = client.ReauthFunc() } + client.reauthmut.Lock() + client.reauthmut.reauthing = false + client.reauthmut.Unlock() client.mut.Unlock() } else { err = client.ReauthFunc() diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index effdc1a8c2..514147e727 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -60,6 +60,7 @@ func TestConcurrentReauth(t *testing.T) { p.SetToken(prereauthTok) p.ReauthFunc = func() error { time.Sleep(1 * time.Second) + p.AuthenticatedHeaders() info.mut.Lock() info.numreauths++ info.mut.Unlock() From d6484abc23c1f576c931bfca0bec8e318d4969ba Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 3 Dec 2017 20:34:57 +0300 Subject: [PATCH 0142/2296] Add Nova interface-attach support (#643) * Add Nova interface-attach support Add support to attach interface to a server through a POST on: /v2.1/{tenant_id}/servers/{server_id}/os-interface The same operation with the OpenStack CLI is done with: nova interface-attach * Nova attachinterfaces extension - fix some comments --- .../v2/extensions/attachinterfaces/doc.go | 12 +++++ .../extensions/attachinterfaces/requests.go | 44 +++++++++++++++++++ .../v2/extensions/attachinterfaces/results.go | 8 +++- .../attachinterfaces/testing/fixtures.go | 44 +++++++++++++++++++ .../attachinterfaces/testing/requests_test.go | 17 +++++++ .../v2/extensions/attachinterfaces/urls.go | 4 ++ 6 files changed, 128 insertions(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go index f43a2e2136..10e8fe3ea4 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -27,5 +27,17 @@ Example to Get a Server's Interface if err != nil { panic(err) } + +Example to Create a new Interface attachment on the Server + + networkID := "8a5fe506-7e9f-4091-899b-96336909d93c" + serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" + attachOpts := attachinterfaces.CreateOpts{ + NetworkID: networkID, + } + interface, err := attachinterfaces.Create(computeClient, serverID, attachOpts).Extract() + if err != nil { + panic(err) + } */ package attachinterfaces diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go index 34a5248c09..f99bfea6c6 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/requests.go +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -19,3 +19,47 @@ func Get(client *gophercloud.ServiceClient, serverID, portID string) (r GetResul }) return } + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToAttachInterfacesCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters of a new interface attachment. +type CreateOpts struct { + + // PortID is the ID of the port for which you want to create an interface. + // The NetworkID and PortID parameters are mutually exclusive. + // If you do not specify the PortID parameter, the OpenStack Networking API + // v2.0 allocates a port and creates an interface for it on the network. + PortID string `json:"port_id,omitempty"` + + // NetworkID is the ID of the network for which you want to create an interface. + // The NetworkID and PortID parameters are mutually exclusive. + // If you do not specify the NetworkID parameter, the OpenStack Networking + // API v2.0 uses the network information cache that is associated with the instance. + NetworkID string `json:"net_id,omitempty"` + + // Slice of FixedIPs. If you request a specific FixedIP address without a + // NetworkID, the request returns a Bad Request (400) response code. + FixedIPs []FixedIP `json:"fixed_ips,omitempty"` +} + +// ToAttachInterfacesCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToAttachInterfacesCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "interfaceAttachment") +} + +// Create requests the creation of a new interface attachment on the server. +func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToAttachInterfacesCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createInterfaceURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/extensions/attachinterfaces/results.go index ef045bf09b..e9b0756256 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/results.go +++ b/openstack/compute/v2/extensions/attachinterfaces/results.go @@ -19,11 +19,17 @@ func (r attachInterfaceResult) Extract() (*Interface, error) { } // GetResult is the response from a Get operation. Call its Extract -// method to interpret it as a Interface. +// method to interpret it as an Interface. type GetResult struct { attachInterfaceResult } +// CreateResult is the response from a Create operation. Call its Extract +// method to interpret it as an Interface. +type CreateResult struct { + attachInterfaceResult +} + // FixedIP represents a Fixed IP Address. type FixedIP struct { SubnetID string `json:"subnet_id"` diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go index a7ef1d94dd..e2b0710346 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go @@ -48,6 +48,20 @@ var GetInterfaceExpected = attachinterfaces.Interface{ MACAddr: "fa:16:3e:38:2d:80", } +// CreateInterfacesExpected represents an expected repsonse from a CreateInterface request. +var CreateInterfacesExpected = attachinterfaces.Interface{ + PortState: "ACTIVE", + FixedIPs: []attachinterfaces.FixedIP{ + { + SubnetID: "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", + IPAddress: "10.0.0.7", + }, + }, + PortID: "0dde1598-b374-474e-986f-5b8dd1df1d4e", + NetID: "8a5fe506-7e9f-4091-899b-96336909d93c", + MACAddr: "fa:16:3e:38:2d:80", +} + // HandleInterfaceListSuccessfully sets up the test server to respond to a ListInterfaces request. func HandleInterfaceListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface", func(w http.ResponseWriter, r *http.Request) { @@ -106,3 +120,33 @@ func HandleInterfaceGetSuccessfully(t *testing.T) { }`) }) } + +// HandleInterfaceCreateSuccessfully sets up the test server to respond to a CreateInterface request. +func HandleInterfaceCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface", 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, `{ + "interfaceAttachment": { + "net_id": "8a5fe506-7e9f-4091-899b-96336909d93c" + } + }`) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, `{ + "interfaceAttachment": + { + "port_state":"ACTIVE", + "fixed_ips": [ + { + "subnet_id": "d7906db4-a566-4546-b1f4-5c7fa70f0bf3", + "ip_address": "10.0.0.7" + } + ], + "port_id": "0dde1598-b374-474e-986f-5b8dd1df1d4e", + "net_id": "8a5fe506-7e9f-4091-899b-96336909d93c", + "mac_addr": "fa:16:3e:38:2d:80" + } + }`) + }) +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go index 628374d933..078de75e81 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go @@ -58,3 +58,20 @@ func TestGetInterface(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, actual) } + +func TestCreateInterface(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleInterfaceCreateSuccessfully(t) + + expected := CreateInterfacesExpected + + serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" + networkID := "8a5fe506-7e9f-4091-899b-96336909d93c" + + actual, err := attachinterfaces.Create(client.ServiceClient(), serverID, attachinterfaces.CreateOpts{ + NetworkID: networkID, + }).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/urls.go b/openstack/compute/v2/extensions/attachinterfaces/urls.go index 7154eceb3c..5312990e0e 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/urls.go +++ b/openstack/compute/v2/extensions/attachinterfaces/urls.go @@ -9,3 +9,7 @@ func listInterfaceURL(client *gophercloud.ServiceClient, serverID string) string func getInterfaceURL(client *gophercloud.ServiceClient, serverID, portID string) string { return client.ServiceURL("servers", serverID, "os-interface", portID) } + +func createInterfaceURL(client *gophercloud.ServiceClient, serverID string) string { + return client.ServiceURL("servers", serverID, "os-interface") +} From 8113f0cb50d49f4527e0becd66a12741cf2dace9 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 5 Dec 2017 05:27:41 +0300 Subject: [PATCH 0143/2296] Add Nova interface-detach support (#644) * Add Nova interface-detach support Add support to detach interface from a server through a DELETE on: /v2.1/{tenant_id}/servers/{server_id}/os-interface/{port_id} The same operation with the OpenStack CLI is done with: nova interface-detach * Update Nova's attachinterfaces extenstion docs. Rename interfaceID to portID in the Delete request. * Update Nova's attachinterfaces extenstion tests. Rename interfaceID to portID in the Delete request test. --- .../compute/v2/extensions/attachinterfaces/doc.go | 9 +++++++++ .../v2/extensions/attachinterfaces/requests.go | 7 +++++++ .../v2/extensions/attachinterfaces/results.go | 6 ++++++ .../extensions/attachinterfaces/testing/fixtures.go | 10 ++++++++++ .../attachinterfaces/testing/requests_test.go | 12 ++++++++++++ .../compute/v2/extensions/attachinterfaces/urls.go | 3 +++ 6 files changed, 47 insertions(+) diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go index 10e8fe3ea4..3653122bf3 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -39,5 +39,14 @@ Example to Create a new Interface attachment on the Server if err != nil { panic(err) } + +Example to Delete an Interface attachment from the Server + + portID = "0dde1598-b374-474e-986f-5b8dd1df1d4e" + serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" + err := attachinterfaces.Delete(computeClient, serverID, portID).ExtractErr() + if err != nil { + panic(err) + } */ package attachinterfaces diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go index f99bfea6c6..18dade837c 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/requests.go +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -63,3 +63,10 @@ func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsB }) return } + +// Delete makes a request against the nova API to detach a single interface from the server. +// It needs server and port IDs to make a such request. +func Delete(client *gophercloud.ServiceClient, serverID, portID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteInterfaceURL(client, serverID, portID), nil) + return +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/extensions/attachinterfaces/results.go index e9b0756256..a16fa14f75 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/results.go +++ b/openstack/compute/v2/extensions/attachinterfaces/results.go @@ -30,6 +30,12 @@ type CreateResult struct { attachInterfaceResult } +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // FixedIP represents a Fixed IP Address. type FixedIP struct { SubnetID string `json:"subnet_id"` diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go index e2b0710346..e701a93fc6 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures.go @@ -150,3 +150,13 @@ func HandleInterfaceCreateSuccessfully(t *testing.T) { }`) }) } + +// HandleInterfaceDeleteSuccessfully sets up the test server to respond to a DeleteInterface request. +func HandleInterfaceDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/b07e7a3b-d951-4efc-a4f9-ac9f001afb7f/os-interface/0dde1598-b374-474e-986f-5b8dd1df1d4e", 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/compute/v2/extensions/attachinterfaces/testing/requests_test.go b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go index 078de75e81..1ffbd61e75 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go @@ -75,3 +75,15 @@ func TestCreateInterface(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, actual) } + +func TestDeleteInterface(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleInterfaceDeleteSuccessfully(t) + + serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" + portID := "0dde1598-b374-474e-986f-5b8dd1df1d4e" + + err := attachinterfaces.Delete(client.ServiceClient(), serverID, portID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/attachinterfaces/urls.go b/openstack/compute/v2/extensions/attachinterfaces/urls.go index 5312990e0e..50292e8b5a 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/urls.go +++ b/openstack/compute/v2/extensions/attachinterfaces/urls.go @@ -13,3 +13,6 @@ func getInterfaceURL(client *gophercloud.ServiceClient, serverID, portID string) func createInterfaceURL(client *gophercloud.ServiceClient, serverID string) string { return client.ServiceURL("servers", serverID, "os-interface") } +func deleteInterfaceURL(client *gophercloud.ServiceClient, serverID, portID string) string { + return client.ServiceURL("servers", serverID, "os-interface", portID) +} From 669959f80fec1527fb7b2f5077564ebd83b6e421 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 5 Dec 2017 02:29:05 +0000 Subject: [PATCH 0144/2296] Compute v2: attachinterfaces acceptance test --- .../compute/v2/attachinterfaces_test.go | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 acceptance/openstack/compute/v2/attachinterfaces_test.go diff --git a/acceptance/openstack/compute/v2/attachinterfaces_test.go b/acceptance/openstack/compute/v2/attachinterfaces_test.go new file mode 100644 index 0000000000..766a3aec8e --- /dev/null +++ b/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -0,0 +1,60 @@ +// +build acceptance compute servers + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" +) + +func TestAttachDetachInterface(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + server, err := CreateServer(t, client) + if err != nil { + t.Fatalf("Unable to create server: %v", err) + } + + defer DeleteServer(t, client, server) + + newServer, err := servers.Get(client, server.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve server: %v", err) + } + tools.PrintResource(t, newServer) + + intOpts := attachinterfaces.CreateOpts{} + + iface, err := attachinterfaces.Create(client, server.ID, intOpts).Extract() + if err != nil { + t.Fatal(err) + } + + tools.PrintResource(t, iface) + + allPages, err := attachinterfaces.List(client, server.ID).AllPages() + if err != nil { + t.Fatal(err) + } + + allIfaces, err := attachinterfaces.ExtractInterfaces(allPages) + if err != nil { + t.Fatal(err) + } + + for _, i := range allIfaces { + tools.PrintResource(t, i) + } + + err = attachinterfaces.Delete(client, server.ID, iface.PortID).ExtractErr() + if err != nil { + t.Fatal(err) + } +} From 578e2aab869bdbc1e4f29cb2de4d28a914d0a172 Mon Sep 17 00:00:00 2001 From: Eugene Taranov Date: Tue, 5 Dec 2017 06:46:53 +0300 Subject: [PATCH 0145/2296] Configuration group time parsing error (#648) * fix * fix * fix tests * fix tests --- openstack/db/v1/configurations/results.go | 20 +++++++++++++++++++ .../db/v1/configurations/testing/fixtures.go | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/openstack/db/v1/configurations/results.go b/openstack/db/v1/configurations/results.go index c52a65417b..3516330249 100644 --- a/openstack/db/v1/configurations/results.go +++ b/openstack/db/v1/configurations/results.go @@ -1,6 +1,7 @@ package configurations import ( + "encoding/json" "time" "github.com/gophercloud/gophercloud" @@ -20,6 +21,25 @@ type Config struct { Values map[string]interface{} } +func (r *Config) UnmarshalJSON(b []byte) error { + type tmp Config + var s struct { + tmp + Created gophercloud.JSONRFC3339NoZ `json:"created"` + Updated gophercloud.JSONRFC3339NoZ `json:"updated"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Config(s.tmp) + + r.Created = time.Time(s.Created) + r.Updated = time.Time(s.Updated) + + return nil +} + // ConfigPage contains a page of Config resources in a paginated collection. type ConfigPage struct { pagination.SinglePageBase diff --git a/openstack/db/v1/configurations/testing/fixtures.go b/openstack/db/v1/configurations/testing/fixtures.go index 3a9b562c4f..3d515ea2b1 100644 --- a/openstack/db/v1/configurations/testing/fixtures.go +++ b/openstack/db/v1/configurations/testing/fixtures.go @@ -4,12 +4,13 @@ import ( "fmt" "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" ) var ( - timestamp = "2015-11-12T14:22:42Z" - timeVal, _ = time.Parse(time.RFC3339, timestamp) + timestamp = "2015-11-12T14:22:42" + timeVal, _ = time.Parse(gophercloud.RFC3339NoZ, timestamp) ) var singleConfigJSON = ` From ed468967ccbba2c5e91e16ee56023977c2a587bb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 5 Dec 2017 03:48:08 +0000 Subject: [PATCH 0146/2296] DBv1: configurations acceptance test --- .../openstack/db/v1/configurations_test.go | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 acceptance/openstack/db/v1/configurations_test.go diff --git a/acceptance/openstack/db/v1/configurations_test.go b/acceptance/openstack/db/v1/configurations_test.go new file mode 100644 index 0000000000..87e883f458 --- /dev/null +++ b/acceptance/openstack/db/v1/configurations_test.go @@ -0,0 +1,50 @@ +// +build acceptance db + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" +) + +func TestConfigurationsCRUD(t *testing.T) { + client, err := clients.NewDBV1Client() + if err != nil { + t.Fatalf("Unable to create a DB client: %v", err) + } + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatalf("Unable to get environment settings") + } + + createOpts := &configurations.CreateOpts{ + Name: "test", + Description: "description", + } + + datastore := configurations.DatastoreOpts{ + Type: choices.DBDatastoreType, + Version: choices.DBDatastoreVersion, + } + createOpts.Datastore = &datastore + + values := make(map[string]interface{}) + values["collation_server"] = "latin1_swedish_ci" + createOpts.Values = values + + cgroup, err := configurations.Create(client, createOpts).Extract() + if err != nil { + t.Fatalf("Unable to create configuration: %v", err) + } + + err = configurations.Delete(client, cgroup.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete configuration: %v", err) + } + + tools.PrintResource(t, cgroup) +} From 157d75114064373b2a19fe8b176af0cf7c6acbdb Mon Sep 17 00:00:00 2001 From: Fredrik Date: Thu, 7 Dec 2017 20:50:28 +0100 Subject: [PATCH 0147/2296] Add support for ipv6_address_mode and ipv6_ra_mode in subnets (#660) * Add support for openstack subnet ipv6_ra_mode/ipv6_address_mode. * Add tests for ipv6_address_mode and ipv6_ra_mode Tests for ipv6_address_mode and ipv6_ra_mode when creating subnets. Implemented as a seperate test-case. * Change field name from IPv6RaMode to IPv6RAMode --- openstack/networking/v2/subnets/requests.go | 33 +++++++++------ openstack/networking/v2/subnets/results.go | 7 ++++ .../networking/v2/subnets/testing/fixtures.go | 30 ++++++++++++++ .../v2/subnets/testing/requests_test.go | 41 +++++++++++++++++++ 4 files changed, 99 insertions(+), 12 deletions(-) diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index c2e74eff44..403f692234 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -17,18 +17,20 @@ type ListOptsBuilder interface { // by a particular subnet attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - Name string `q:"name"` - EnableDHCP *bool `q:"enable_dhcp"` - NetworkID string `q:"network_id"` - TenantID string `q:"tenant_id"` - IPVersion int `q:"ip_version"` - GatewayIP string `q:"gateway_ip"` - CIDR string `q:"cidr"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` + Name string `q:"name"` + EnableDHCP *bool `q:"enable_dhcp"` + NetworkID string `q:"network_id"` + TenantID string `q:"tenant_id"` + IPVersion int `q:"ip_version"` + GatewayIP string `q:"gateway_ip"` + CIDR string `q:"cidr"` + IPv6AddressMode string `q:"ipv6_address_mode"` + IPv6RAMode string `q:"ipv6_ra_mode"` + ID string `q:"id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` } // ToSubnetListQuery formats a ListOpts into a query string. @@ -105,6 +107,13 @@ type CreateOpts struct { // HostRoutes are any static host routes to be set via DHCP. HostRoutes []HostRoute `json:"host_routes,omitempty"` + + // The IPv6 address modes specifies mechanisms for assigning IPv6 IP addresses. + IPv6AddressMode string `json:"ipv6_address_mode,omitempty"` + + // The IPv6 router advertisement specifies whether the networking service + // should transmit ICMPv6 packets. + IPv6RAMode string `json:"ipv6_ra_mode,omitempty"` } // ToSubnetCreateMap builds a request body from CreateOpts. diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index ade8abc699..743610f01e 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -93,6 +93,13 @@ type Subnet struct { // Owner of network. TenantID string `json:"tenant_id"` + + // The IPv6 address modes specifies mechanisms for assigning IPv6 IP addresses. + IPv6AddressMode string `json:"ipv6_address_mode"` + + // The IPv6 router advertisement specifies whether the networking service + // should transmit ICMPv6 packets. + IPv6RAMode string `json:"ipv6_ra_mode"` } // SubnetPage is the page returned by a pager when traversing over a collection diff --git a/openstack/networking/v2/subnets/testing/fixtures.go b/openstack/networking/v2/subnets/testing/fixtures.go index f9104c5aa3..0dac09260f 100644 --- a/openstack/networking/v2/subnets/testing/fixtures.go +++ b/openstack/networking/v2/subnets/testing/fixtures.go @@ -263,6 +263,36 @@ const SubnetCreateWithDefaultGatewayResponse = ` } } ` +const SubnetCreateWithIPv6RaAddressModeRequest = ` +{ + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ip_version": 6, + "gateway_ip": "2001:db8:0:a::1", + "cidr": "2001:db8:0:a:0:0:0:0/64", + "ipv6_address_mode": "slaac", + "ipv6_ra_mode": "slaac" + } +} +` +const SubnetCreateWithIPv6RaAddressModeResponse = ` +{ + "subnet": { + "name": "", + "enable_dhcp": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": [], + "host_routes": [], + "ip_version": 6, + "gateway_ip": "2001:db8:0:a::1", + "cidr": "2001:db8:0:a:0:0:0:0/64", + "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126", + "ipv6_address_mode": "slaac", + "ipv6_ra_mode": "slaac" + } +} +` const SubnetUpdateRequest = ` { diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index a563f70cdf..2dd4e029df 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -241,6 +241,47 @@ func TestCreateDefaultGateway(t *testing.T) { th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0c") } +func TestCreateIPv6RaAddressMode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/subnets", 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, SubnetCreateWithIPv6RaAddressModeRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, SubnetCreateWithIPv6RaAddressModeResponse) + }) + + var gatewayIP = "2001:db8:0:a::1" + opts := subnets.CreateOpts{ + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + IPVersion: 6, + CIDR: "2001:db8:0:a:0:0:0:0/64", + GatewayIP: &gatewayIP, + IPv6AddressMode: "slaac", + IPv6RAMode: "slaac", + } + s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "") + th.AssertEquals(t, s.EnableDHCP, true) + th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") + th.AssertEquals(t, s.IPVersion, 6) + th.AssertEquals(t, s.GatewayIP, "2001:db8:0:a::1") + th.AssertEquals(t, s.CIDR, "2001:db8:0:a:0:0:0:0/64") + th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126") + th.AssertEquals(t, s.IPv6AddressMode, "slaac") + th.AssertEquals(t, s.IPv6RAMode, "slaac") +} + func TestRequiredCreateOpts(t *testing.T) { res := subnets.Create(fake.ServiceClient(), subnets.CreateOpts{}) if res.Err == nil { From b63d2fd36f5dae9723e73696248abca95d6b8dc6 Mon Sep 17 00:00:00 2001 From: Fredrik Date: Thu, 7 Dec 2017 22:13:15 +0100 Subject: [PATCH 0148/2296] availability_zone_hints for network(s) (#662) * availability_zone_hints for network(s) * Add missing commit for field AvailabilityZoneHints --- openstack/networking/v2/networks/requests.go | 9 +++++---- openstack/networking/v2/networks/results.go | 4 ++++ openstack/networking/v2/networks/testing/fixtures.go | 3 ++- .../networking/v2/networks/testing/requests_test.go | 8 +++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 5b61b24719..040f32183b 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -66,10 +66,11 @@ type CreateOptsBuilder interface { // CreateOpts represents options used to create a network. type CreateOpts struct { - AdminStateUp *bool `json:"admin_state_up,omitempty"` - Name string `json:"name,omitempty"` - Shared *bool `json:"shared,omitempty"` - TenantID string `json:"tenant_id,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Name string `json:"name,omitempty"` + Shared *bool `json:"shared,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + AvailabilityZoneHints []string `json:"availability_zone_hints,omitempty"` } // ToNetworkCreateMap builds a request body from CreateOpts. diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index ffd0259f1d..c73f9e1a63 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -69,6 +69,10 @@ type Network struct { // Specifies whether the network resource can be accessed by any tenant. Shared bool `json:"shared"` + + // Availability zone hints groups network nodes that run services like DHCP, L3, FW, and others. + // Used to make network resources highly available. + AvailabilityZoneHints []string `json:"availability_zone_hints"` } // NetworkPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index 8ba32a9c20..3edbe8f37a 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -92,7 +92,8 @@ const CreateOptionalFieldsRequest = ` "name": "public", "admin_state_up": true, "shared": true, - "tenant_id": "12345" + "tenant_id": "12345", + "availability_zone_hints": ["zone1", "zone2"] } }` diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index cbe349351d..0e028ead4e 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -166,7 +166,13 @@ func TestCreateWithOptionalFields(t *testing.T) { }) iTrue := true - options := networks.CreateOpts{Name: "public", AdminStateUp: &iTrue, Shared: &iTrue, TenantID: "12345"} + options := networks.CreateOpts{ + Name: "public", + AdminStateUp: &iTrue, + Shared: &iTrue, + TenantID: "12345", + AvailabilityZoneHints: []string{"zone1", "zone2"}, + } _, err := networks.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } From 25e18920a97c09d00e9c70260f025c6d561aefbb Mon Sep 17 00:00:00 2001 From: Yuki KIRII Date: Fri, 8 Dec 2017 11:53:39 +0900 Subject: [PATCH 0149/2296] Compute v2: Add the extended status information API (#659) * Implement the extended status information API * Add tests for extended status API * Add UNUSED state into the list of Power State constants * Rename PowerState receiver --- .../openstack/compute/v2/servers_test.go | 5 +++ .../v2/extensions/extendedstatus/doc.go | 28 +++++++++++++ .../v2/extensions/extendedstatus/results.go | 41 +++++++++++++++++++ .../v2/servers/testing/requests_test.go | 9 ++++ 4 files changed, 83 insertions(+) create mode 100644 openstack/compute/v2/extensions/extendedstatus/doc.go create mode 100644 openstack/compute/v2/extensions/extendedstatus/results.go diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index ada733f52d..db7422f9b0 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/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/extendedstatus" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" @@ -111,6 +112,7 @@ func TestServersCreateDestroyWithExtensions(t *testing.T) { var extendedServer struct { servers.Server availabilityzones.ServerAvailabilityZoneExt + extendedstatus.ServerExtendedStatusExt } client, err := clients.NewComputeV2Client() @@ -131,6 +133,9 @@ func TestServersCreateDestroyWithExtensions(t *testing.T) { tools.PrintResource(t, extendedServer) t.Logf("Availability Zone: %s\n", extendedServer.AvailabilityZone) + t.Logf("Power State: %s\n", extendedServer.PowerState) + t.Logf("Task State: %s\n", extendedServer.TaskState) + t.Logf("VM State: %s\n", extendedServer.VmState) } func TestServersWithoutImageRef(t *testing.T) { diff --git a/openstack/compute/v2/extensions/extendedstatus/doc.go b/openstack/compute/v2/extensions/extendedstatus/doc.go new file mode 100644 index 0000000000..33b1e35cd6 --- /dev/null +++ b/openstack/compute/v2/extensions/extendedstatus/doc.go @@ -0,0 +1,28 @@ +/* +Package extendedstatus provides the ability to extend a server result with +the extended status information. Example: + + type ServerWithExt struct { + servers.Server + extendedstatus.ServerExtendedStatusExt + } + + var allServers []ServerWithExt + + allPages, err := servers.List(client, nil).AllPages() + if err != nil { + panic("Unable to retrieve servers: %s", err) + } + + err = servers.ExtractServersInto(allPages, &allServers) + if err != nil { + panic("Unable to extract servers: %s", err) + } + + for _, server := range allServers { + fmt.Println(server.TaskState) + fmt.Println(server.VmState) + fmt.Println(server.PowerState) + } +*/ +package extendedstatus diff --git a/openstack/compute/v2/extensions/extendedstatus/results.go b/openstack/compute/v2/extensions/extendedstatus/results.go new file mode 100644 index 0000000000..acfbd3fb24 --- /dev/null +++ b/openstack/compute/v2/extensions/extendedstatus/results.go @@ -0,0 +1,41 @@ +package extendedstatus + +type PowerState int + +type ServerExtendedStatusExt struct { + TaskState string `json:"OS-EXT-STS:task_state"` + VmState string `json:"OS-EXT-STS:vm_state"` + PowerState PowerState `json:"OS-EXT-STS:power_state"` +} + +const ( + NOSTATE = iota + RUNNING + _UNUSED1 + PAUSED + SHUTDOWN + _UNUSED2 + CRASHED + SUSPENDED +) + +func (r PowerState) String() string { + switch r { + case NOSTATE: + return "NOSTATE" + case RUNNING: + return "RUNNING" + case PAUSED: + return "PAUSED" + case SHUTDOWN: + return "SHUTDOWN" + case CRASHED: + return "CRASHED" + case SUSPENDED: + return "SUSPENDED" + case _UNUSED1, _UNUSED2: + return "_UNUSED" + default: + return "N/A" + } +} diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index c832129910..e039f29aef 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diskconfig" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedstatus" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -66,6 +67,7 @@ func TestListAllServersWithExtensions(t *testing.T) { type ServerWithExt struct { servers.Server availabilityzones.ServerAvailabilityZoneExt + extendedstatus.ServerExtendedStatusExt diskconfig.ServerDiskConfigExt } @@ -77,6 +79,9 @@ func TestListAllServersWithExtensions(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, 3, len(actual)) th.AssertEquals(t, "nova", actual[0].AvailabilityZone) + th.AssertEquals(t, "RUNNING", actual[0].PowerState.String()) + th.AssertEquals(t, "", actual[0].TaskState) + th.AssertEquals(t, "active", actual[0].VmState) th.AssertEquals(t, diskconfig.Manual, actual[0].DiskConfig) } @@ -237,12 +242,16 @@ func TestGetServerWithExtensions(t *testing.T) { var s struct { servers.Server availabilityzones.ServerAvailabilityZoneExt + extendedstatus.ServerExtendedStatusExt diskconfig.ServerDiskConfigExt } err := servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "nova", s.AvailabilityZone) + th.AssertEquals(t, "RUNNING", s.PowerState.String()) + th.AssertEquals(t, "", s.TaskState) + th.AssertEquals(t, "active", s.VmState) th.AssertEquals(t, diskconfig.Manual, s.DiskConfig) err = servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(s) From a7ec61ea727cf2a5dc15266986b5f4f795cfeb44 Mon Sep 17 00:00:00 2001 From: liusheng Date: Fri, 8 Dec 2017 17:14:50 +0800 Subject: [PATCH 0150/2296] Fix the undefined function error of TestNetworksProviderCRUD test When try to run the TestNetworksProviderCRUD test, the test will extract info of a new created network and then print it, it extracts the network info by "provider.ExtractGet" function, which is undefined. This change use the "Extract" method of returned network object to instead. For #663 --- acceptance/openstack/networking/v2/extensions/provider_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/provider_test.go b/acceptance/openstack/networking/v2/extensions/provider_test.go index b0d5846ddc..44726ce27e 100644 --- a/acceptance/openstack/networking/v2/extensions/provider_test.go +++ b/acceptance/openstack/networking/v2/extensions/provider_test.go @@ -8,7 +8,6 @@ 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/networking/v2/extensions/provider" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) @@ -26,7 +25,7 @@ func TestNetworksProviderCRUD(t *testing.T) { defer networking.DeleteNetwork(t, client, network.ID) getResult := networks.Get(client, network.ID) - newNetwork, err := provider.ExtractGet(getResult) + newNetwork, err := getResult.Extract() if err != nil { t.Fatalf("Unable to extract network: %v", err) } From 747776a73eca8c49d63e58e42d3d57da506ad7a9 Mon Sep 17 00:00:00 2001 From: liusheng Date: Fri, 8 Dec 2017 17:46:46 +0800 Subject: [PATCH 0151/2296] Fix the undefined function error of TestPortsbindingCRUD test The is a "portsbinding.Update" function will be called when run the test TestPortsbindingCRUD, but the function is undefined, this change use the ports.UpdateOpts to instead. For #665 --- .../networking/v2/extensions/portsbinding/portsbinding_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index 803f62a3dd..6ce205925a 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -8,7 +8,6 @@ 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/networking/v2/extensions/portsbinding" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) @@ -49,7 +48,7 @@ func TestPortsbindingCRUD(t *testing.T) { updateOpts := ports.UpdateOpts{ Name: newPortName, } - newPort, err := portsbinding.Update(client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(client, port.ID, updateOpts).Extract() if err != nil { t.Fatalf("Could not update port: %v", err) } From 48a403996f37c7fec3b402cad371b448b1b60172 Mon Sep 17 00:00:00 2001 From: Fredrik Steen Date: Fri, 8 Dec 2017 15:03:34 +0100 Subject: [PATCH 0152/2296] Support for setting availability_zone_hints to a router --- .../v2/extensions/layer3/routers/requests.go | 11 ++++++----- .../v2/extensions/layer3/routers/results.go | 4 ++++ .../layer3/routers/testing/requests_test.go | 18 ++++++++++++------ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index 6799d200b7..fa346c8555 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -49,11 +49,12 @@ type CreateOptsBuilder interface { // CreateOpts contains all the values needed to create a new router. There are // no required values. type CreateOpts struct { - Name string `json:"name,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - Distributed *bool `json:"distributed,omitempty"` - TenantID string `json:"tenant_id,omitempty"` - GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` + Name string `json:"name,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Distributed *bool `json:"distributed,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` + AvailabilityZoneHints []string `json:"availability_zone_hints,omitempty"` } // ToRouterCreateMap builds a create request body from CreateOpts. diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index e19c8e74c4..da1b9e4bdf 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -60,6 +60,10 @@ type Router struct { // Routes are a collection of static routes that the router will host. Routes []Route `json:"routes"` + + // Availability zone hints groups network nodes that run services like DHCP, L3, FW, and others. + // Used to make network resources highly available. + AvailabilityZoneHints []string `json:"availability_zone_hints"` } // RouterPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 5010de6050..2f7b93d4b9 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -138,7 +138,8 @@ func TestCreate(t *testing.T) { "external_gateway_info":{ "enable_snat": false, "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b" - } + }, + "availability_zone_hints": ["zone1", "zone2"] } } `) @@ -160,7 +161,8 @@ func TestCreate(t *testing.T) { "name": "foo_router", "admin_state_up": false, "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", - "distributed": false, + "distributed": false, + "availability_zone_hints": ["zone1", "zone2"], "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e" } } @@ -174,9 +176,10 @@ func TestCreate(t *testing.T) { EnableSNAT: &enableSNAT, } options := routers.CreateOpts{ - Name: "foo_router", - AdminStateUp: &asu, - GatewayInfo: &gwi, + Name: "foo_router", + AdminStateUp: &asu, + GatewayInfo: &gwi, + AvailabilityZoneHints: []string{"zone1", "zone2"}, } r, err := routers.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) @@ -189,6 +192,7 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, "foo_router", r.Name) th.AssertEquals(t, false, r.AdminStateUp) th.AssertDeepEquals(t, gwi, r.GatewayInfo) + th.AssertDeepEquals(t, []string{"zone1", "zone2"}, r.AvailabilityZoneHints) } func TestGet(t *testing.T) { @@ -221,7 +225,8 @@ func TestGet(t *testing.T) { "name": "router1", "admin_state_up": true, "tenant_id": "d6554fe62e2f41efbb6e026fad5c1542", - "distributed": false, + "distributed": false, + "availability_zone_hints": ["zone1", "zone2"], "id": "a07eea83-7710-4860-931b-5fe220fae533" } } @@ -243,6 +248,7 @@ func TestGet(t *testing.T) { th.AssertEquals(t, n.TenantID, "d6554fe62e2f41efbb6e026fad5c1542") th.AssertEquals(t, n.ID, "a07eea83-7710-4860-931b-5fe220fae533") th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}) + th.AssertDeepEquals(t, n.AvailabilityZoneHints, []string{"zone1", "zone2"}) } func TestUpdate(t *testing.T) { From 3f38a1ee55876c384179b5fdb582c565e76c959e Mon Sep 17 00:00:00 2001 From: Hu Sheng Date: Thu, 14 Dec 2017 04:21:16 +0800 Subject: [PATCH 0153/2296] Add List/Get support for volume type in volume V3 (#657) * Add basic CRUD support in volume v3 types Add Create/Delete/List/Update/Get support for volume type in volume V3. * Add list/show support in V3 volume type Add list/show support in V3 volume type --- openstack/blockstorage/v3/volumetypes/doc.go | 30 +++++++ .../blockstorage/v3/volumetypes/requests.go | 56 ++++++++++++ .../blockstorage/v3/volumetypes/results.go | 79 ++++++++++++++++ .../v3/volumetypes/testing/doc.go | 2 + .../v3/volumetypes/testing/fixtures.go | 89 +++++++++++++++++++ .../v3/volumetypes/testing/requests_test.go | 65 ++++++++++++++ openstack/blockstorage/v3/volumetypes/urls.go | 11 +++ 7 files changed, 332 insertions(+) create mode 100644 openstack/blockstorage/v3/volumetypes/doc.go create mode 100644 openstack/blockstorage/v3/volumetypes/requests.go create mode 100644 openstack/blockstorage/v3/volumetypes/results.go create mode 100644 openstack/blockstorage/v3/volumetypes/testing/doc.go create mode 100644 openstack/blockstorage/v3/volumetypes/testing/fixtures.go create mode 100644 openstack/blockstorage/v3/volumetypes/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/volumetypes/urls.go diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go new file mode 100644 index 0000000000..f1b6fc7803 --- /dev/null +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -0,0 +1,30 @@ +/* +Package volumetypes provides information and interaction with volume types in the +OpenStack Block Storage service. A volume type is a collection of specs used to +define the volume capabilities. + +Example to list Volume Types + + allPages, err := volumetypes.List(client, volumetypes.ListOpts{}).AllPages() + if err != nil{ + panic(err) + } + volumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) + if err != nil{ + panic(err) + } + for _,vt := range volumeTypes{ + fmt.Println(vt) + } + +Example to show a Volume Type + + typeID := "0fe36e73809d46aeae6705c39077b1b3" + volumeType, err := volumetypes.Get(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 new file mode 100644 index 0000000000..468fd864dc --- /dev/null +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -0,0 +1,56 @@ +package volumetypes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Get retrieves the Volume Type with the provided ID. To extract the Volume Type object +// from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToVolumeTypeListQuery() (string, error) +} + +// ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List +// function. +type ListOpts struct { + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + // Requests a page size of items. + Limit int `q:"limit"` + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToVolumeTypeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToVolumeTypeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Volume types. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + + if opts != nil { + query, err := opts.ToVolumeTypeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return VolumeTypePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go new file mode 100644 index 0000000000..eb2a8e6939 --- /dev/null +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -0,0 +1,79 @@ +package volumetypes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Volume Type contains all the information associated with an OpenStack Volume Type. +type VolumeType struct { + // Unique identifier for the volume type. + ID string `json:"id"` + // Human-readable display name for the volume type. + Name string `json:"name"` + // Human-readable description for the volume type. + Description string `json:"description"` + // Arbitrary key-value pairs defined by the user. + ExtraSpecs map[string]string `json:"extra_specs"` + // Whether the volume type is publicly visible. + IsPublic bool `json:"is_public"` + // Qos Spec ID + QosSpecID string `json:"qos_specs_id"` + // Volume Type access public attribute + PublicAccess bool `json:"os-volume-type-access:is_public"` +} + +// VolumeTypePage is a pagination.pager that is returned from a call to the List function. +type VolumeTypePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Volume Types. +func (r VolumeTypePage) IsEmpty() (bool, error) { + volumetypes, err := ExtractVolumeTypes(r) + return len(volumetypes) == 0, err +} + +func (page VolumeTypePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"volume_type_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractVolumeTypes extracts and returns Volumes. It is used while iterating over a volumetypes.List call. +func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { + var s []VolumeType + err := ExtractVolumeTypesInto(r, &s) + return s, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Volume Type object out of the commonResult object. +func (r commonResult) Extract() (*VolumeType, error) { + var s VolumeType + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a volume type struct +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "volume_type") +} + +// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volume types +func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { + return r.(VolumeTypePage).Result.ExtractIntoSlicePtr(v, "volume_types") +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/doc.go b/openstack/blockstorage/v3/volumetypes/testing/doc.go new file mode 100644 index 0000000000..3fd720a674 --- /dev/null +++ b/openstack/blockstorage/v3/volumetypes/testing/doc.go @@ -0,0 +1,2 @@ +// volume_types +package testing diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go new file mode 100644 index 0000000000..8fa59ad338 --- /dev/null +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go @@ -0,0 +1,89 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/types", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` +{ + "volume_types": [ + { + "name": "SSD", + "qos_specs_id": null, + "os-volume-type-access:is_public": true, + "extra_specs": { + "volume_backend_name": "lvmdriver-1" + }, + "is_public": true, + "id": "6685584b-1eac-4da6-b5c3-555430cf68ff", + "description": null + }, + { + "name": "SATA", + "qos_specs_id": null, + "os-volume-type-access:is_public": true, + "extra_specs": { + "volume_backend_name": "lvmdriver-1" + }, + "is_public": true, + "id": "8eb69a46-df97-4e41-9586-9a40a7533803", + "description": null + } + ], + "volume_type_links": [ + { + "href": "%s/types?marker=1", + "rel": "next" + } + ] +} + `, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"volume_types": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/types/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, ` +{ + "volume_type": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "vol-type-001", + "os-volume-type-access:is_public": true, + "qos_specs_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "description": "volume type 001", + "is_public": true, + "extra_specs": { + "capabilities": "gpu" + } + } +} +`) + }) +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go new file mode 100644 index 0000000000..e205d90255 --- /dev/null +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -0,0 +1,65 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + pages := 0 + err := volumetypes.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + pages++ + actual, err := volumetypes.ExtractVolumeTypes(page) + if err != nil { + return false, err + } + expected := []volumetypes.VolumeType{ + { + ID: "6685584b-1eac-4da6-b5c3-555430cf68ff", + Name: "SSD", + ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, + IsPublic: true, + Description: "", + QosSpecID: "", + PublicAccess: true, + }, { + ID: "8eb69a46-df97-4e41-9586-9a40a7533803", + Name: "SATA", + ExtraSpecs: map[string]string{"volume_backend_name": "lvmdriver-1"}, + IsPublic: true, + Description: "", + QosSpecID: "", + PublicAccess: true, + }, + } + th.CheckDeepEquals(t, expected, actual) + return true, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, pages, 1) + +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "vol-type-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertEquals(t, v.ExtraSpecs["capabilities"], "gpu") + th.AssertEquals(t, v.QosSpecID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertEquals(t, v.PublicAccess, true) +} diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go new file mode 100644 index 0000000000..70ee229ea8 --- /dev/null +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -0,0 +1,11 @@ +package volumetypes + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("types") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("types", id) +} From be1b616c90aa127694398cf9988a5ff29f1a19cd Mon Sep 17 00:00:00 2001 From: liusheng Date: Thu, 14 Dec 2017 09:53:40 +0800 Subject: [PATCH 0154/2296] Fix a small syntax error of TestShareTypeExtraSpecs test This test try to init a sharetypes.SetExtraSpecsOpts object with a wrong field name, this change fix it. For #670 --- acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index f2f821951d..9efe0359d1 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -70,7 +70,7 @@ func TestShareTypeExtraSpecs(t *testing.T) { } options := sharetypes.SetExtraSpecsOpts{ - Specs: map[string]interface{}{"my_new_key": "my_value"}, + ExtraSpecs: map[string]interface{}{"my_new_key": "my_value"}, } _, err = sharetypes.SetExtraSpecs(client, shareType.ID, options).Extract() From f85e7c0fa2e34edb0cb701cee6f530e0b2039ec2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 Dec 2017 18:54:39 -0700 Subject: [PATCH 0155/2296] Docs: Updating Contributing and Style Guides (#669) * updating contributing and style guide * more contributing updates --- .github/CONTRIBUTING.md | 141 ++++++++++++++++++++++------------------ STYLEGUIDE.md | 5 ++ 2 files changed, 82 insertions(+), 64 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a9a0c8a765..3092511a3b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,14 +1,59 @@ # Contributing to Gophercloud +- [3 ways to get involved](#3-ways-to-get-involved) - [Getting started](#getting-started) - [Tests](#tests) - [Style guide](#basic-style-guide) -- [3 ways to get involved](#5-ways-to-get-involved) -## Setting up your git workspace +## 3 ways to get involved + +There are three main ways you can get involved in our open-source project, and +each is described briefly below. + +### 1. Fixing bugs + +If you want to start fixing open bugs, we'd really appreciate that! Bug fixing +is central to any project. The best way to get started is by heading to our +[bug tracker](https://github.com/gophercloud/gophercloud/issues) and finding open +bugs that you think nobody is working on. It might be useful to comment on the +thread to see the current state of the issue and if anybody has made any +breakthroughs on it so far. + +### 2. Improving documentation + +Gophercloud's documentation is automatically generated from the source code +and can be read online at [godoc.org](https://godoc.org/github.com/gophercloud/gophercloud). + +If you feel that a certain section could be improved - whether it's to clarify +ambiguity, correct a technical mistake, or to fix a grammatical error - please +feel entitled to do so! We welcome doc pull requests with the same childlike +enthusiasm as any other contribution! + +### 3. Working on a new feature + +If you've found something we've left out, definitely feel free to start work on +introducing that feature. It's always useful to open an issue or submit a pull +request early on to indicate your intent to a core contributor - this enables +quick/early feedback and can help steer you in the right direction by avoiding +known issues. It might also help you avoid losing time implementing something +that might not ever work. One tip is to prefix your Pull Request issue title +with [wip] - then people know it's a work in progress. + +We ask that you do not submit a feature that you have not spent time researching +and testing first-hand in an actual OpenStack environment. While we appreciate +the contribution, submitting code which you are unfamiliar with is a risk to the +users who will ultimately use it. See our [acceptance tests readme](/acceptance) +for information about how you can create a local development environment to +better understand the feature you're working on. + +Please do not hesitate to ask questions or request clarification. Your +contribution is very much appreciated and we are happy to work with you to get +it merged. + +## Getting Started As a contributor you will need to setup your workspace in a slightly different -way than just downloading it. Here are the basic installation instructions: +way than just downloading it. Here are the basic instructions: 1. Configure your `$GOPATH` and run `go get` as described in the main [README](/README.md#how-to-install) but add `-tags "fixtures acceptance"` to @@ -46,9 +91,19 @@ need to checkout a new feature branch: git checkout -b my-new-feature ``` -Another thing to bear in mind is that you will need to add a few extra -environment variables for acceptance tests - this is documented in our -[acceptance tests readme](/acceptance). +6. Use a standard text editor or IDE of your choice to make your changes to the code or documentation. Once finished, commit them. + + ```bash + git status + git add path/to/changed/file.go + git commit + ``` + +7. Submit your branch as a [Pull Request](https://help.github.com/articles/creating-a-pull-request/). When submitting a Pull Request, please follow our [Style Guide](https://github.com/gophercloud/gophercloud/blob/master/STYLEGUIDE.md). + +> Further information about using Git can be found [here](https://git-scm.com/book/en/v2). + +Happy Hacking! ## Tests @@ -101,7 +156,7 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) func TestGet(t *testing.T) { @@ -159,77 +214,35 @@ service charges from your provider. Although most tests handle their own teardown procedures, it is always worth manually checking that resources are deleted after the test suite finishes. +We provide detailed information about how to set up local acceptance test +environments in our [acceptance tests readme](/acceptance). + ### Running tests To run all tests: -```bash -go test -tags fixtures ./... -``` + ```bash + go test -tags fixtures ./... + ``` To run all tests with verbose output: -```bash -go test -v -tags fixtures ./... -``` + ```bash + go test -v -tags fixtures ./... + ``` To run tests that match certain [build tags](): -```bash -go test -tags "fixtures foo bar" ./... -``` + ```bash + go test -tags "fixtures foo bar" ./... + ``` To run tests for a particular sub-package: -```bash -cd ./path/to/package && go test -tags fixtures . -``` + ```bash + cd ./path/to/package && go test -tags fixtures ./... + ``` ## Style guide See [here](/STYLEGUIDE.md) - -## 3 ways to get involved - -There are five main ways you can get involved in our open-source project, and -each is described briefly below. Once you've made up your mind and decided on -your fix, you will need to follow the same basic steps that all submissions are -required to adhere to: - -1. [fork](https://help.github.com/articles/fork-a-repo/) the `gophercloud/gophercloud` repository -2. checkout a [new branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches) -3. submit your branch as a [pull request](https://help.github.com/articles/creating-a-pull-request/) - -### 1. Fixing bugs - -If you want to start fixing open bugs, we'd really appreciate that! Bug fixing -is central to any project. The best way to get started is by heading to our -[bug tracker](https://github.com/gophercloud/gophercloud/issues) and finding open -bugs that you think nobody is working on. It might be useful to comment on the -thread to see the current state of the issue and if anybody has made any -breakthroughs on it so far. - -### 2. Improving documentation -The best source of documentation is on [godoc.org](http://godoc.org). It is -automatically generated from the source code. - -If you feel that a certain section could be improved - whether it's to clarify -ambiguity, correct a technical mistake, or to fix a grammatical error - please -feel entitled to do so! We welcome doc pull requests with the same childlike -enthusiasm as any other contribution! - -### 3. Working on a new feature - -If you've found something we've left out, definitely feel free to start work on -introducing that feature. It's always useful to open an issue or submit a pull -request early on to indicate your intent to a core contributor - this enables -quick/early feedback and can help steer you in the right direction by avoiding -known issues. It might also help you avoid losing time implementing something -that might not ever work. One tip is to prefix your Pull Request issue title -with [wip] - then people know it's a work in progress. - -You must ensure that all of your work is well tested - both in terms of unit -and acceptance tests. Untested code will not be merged because it introduces -too much of a risk to end-users. - -Happy hacking! diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index e7531a83d9..22a2900941 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -1,6 +1,8 @@ ## On Pull Requests +- Please make sure to read our [contributing guide](/.github/CONTRIBUTING.md). + - Before you start a PR there needs to be a Github issue and a discussion about it on that issue with a core contributor, even if it's just a 'SGTM'. @@ -34,6 +36,9 @@ append. It makes it difficult for the reviewer to see what's changed from one review to the next. +- See [#583](https://github.com/gophercloud/gophercloud/issues/583) as an example of a + well-formatted issue which contains all relevant information we need to review and approve. + ## On Code - In re design: follow as closely as is reasonable the code already in the library. From bcab0f792e9f832f64a80fca6cff3be2d5a59c34 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 15 Dec 2017 23:37:44 +0000 Subject: [PATCH 0156/2296] Update README with Thank Yous --- README.md | 16 ++++++++++++++++ assets/openlab.png | Bin 0 -> 22080 bytes assets/vexxhost.png | Bin 0 -> 16202 bytes 3 files changed, 16 insertions(+) create mode 100644 assets/openlab.png create mode 100644 assets/vexxhost.png diff --git a/README.md b/README.md index 60ca479de8..bb218c3fe9 100644 --- a/README.md +++ b/README.md @@ -141,3 +141,19 @@ 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](/issues). + +## Thank You + +We'd like to extend special thanks and appreciation to the following: + +### OpenLab + + + +OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases. + +### VEXXHOST + + + +VEXXHOST is providing their services to assist with the development and testing of Gophercloud. diff --git a/assets/openlab.png b/assets/openlab.png new file mode 100644 index 0000000000000000000000000000000000000000..63077ed27561020a78ca1cbe48ae6cd5874d7d61 GIT binary patch literal 22080 zcmd3O1y>wR6Ye4j?(XjH8bV-kcL)+>2@>21?(QC3gIf~Z-7UDg1$XC8zP$JThP&r1 zXNT?SuIjF?dg|$#4O3Q>LO~=%1c5*(GSXmG5C|4{Lm|Px1^)8@lNS#Hk%MHwAJpA4 zj#hLNiD%O29()qrT`v-qAmGWwiGe@Nss?7WX)}0Czf*p4;FKvxc{6=vw8{!R($W?tyByG;Q?Q$#l&hcyFNQQVs{I_uFti!>B+pWbt z3YR>webvU>=*QQ3O=fdGjC%=9NYCmgPY+w)(Em?f^n%G@0q_*^#KB^luLfnH9Ol>Y zp~lx3XANQtNHD*o!~V%W@`IV`AZA^g7PSj3`}CBC zmOx`_)s_6(#^Z+xht~n$A(Yw$@-@pgiVymCLtZafcI=)@DH@9!r(-c7Mk|(jg?zq8 z2&DHo0!6F!uMs`Be8Kj}*&-bYthmDss5o>~okpRq_nSY`IyI>NY88ra)%?Gfa;egT zejDxXwbA7Es#0&U=u&usSp9os>#x}0T2k{VDF43luuUjw0)n$GaX`~hN4-VG{Ul0x z@;aoxtiC3NuPKh@=oo)rlMtXY_}8Y2K`eInS`xK0@&v{kA^Fn(zTM%7i+AvD#GzL5 zl^n?}$b?(R_lo71|w79YYlt20Pg=prIZEHs;U~^wbv7ZZeZy9iDk2#LCh&H zwy2N)bQ$8JRmD6Cc+H>bpvCRn5R#ed#E#a#Rzk8__WLKO%2legOe)%oei_Km9TtZ^ zs9`HN!okSszVtN$0q5U@2pdt-d1eIwpaWqCdj~;z8hpe z3DLt8aw0*<+c&x(R#4&#$FgkX#%Vn<>{`EDv*HU^$z_j|p;6-%ntbtJX$$?1GV1Kk zQ`}g6bzIaLSY2u~LDh&k4sov#Si{b>edoOJFlhOL9`if63PfKCy{8b2Ul{&yeL@%6 z2AEhd-S5qlFsRil#m`n~Xc{@8?O;1(56BvZ{wKNWAhoOq(dz+7G-(h!{F{8-5L>^d!nMXf$3gT@DT`C?2n=IV7QZTjc0xkO)x z|1RAXhXeok18g(%BT;LiT^>afd$aZzn57f8XplVD1|A8mDkbkz_pGo#V`9TDls!h| zcSpH)1NAwK8_{PxNUtjV?@^q+rlR_}N zT=IT2u$kqKs5OawGdBjXTFUP6pF{9{hMLBAcdWej(4zHMf1XeT2wsfJA{mav+2c~N z#NmevKd~_Po;Cgz)QPlNTPSESBv?z}o>`)ob@mW`S28W|kL^{dQI9y88E2eBPz{yo zh@H|G1YBII7i~P~evp$~`_QKg2$POG7pR*3Nxlr3>7rn06KKQc`}qLETlPO+rAp0k z`3FNExoVQwh;m1``&|X6zf5`F1BDwX8frgL(Sd(3DAD^DO-b!{4LA|`(!dJj^=Y7w zMyL20hQG3b!9Cz1DNkjKQC&@lV{QW|T5rd};kQhpBC8XW)BEC|#IRTb$KrPyj1gq& z{)dTWdYa$=+4K$p76VA6ScIVb+FF7WPZqA@LgN|605&^GN2?Ku)RzV%N2Z=_1)7hs zD+?V0JpdPa4W&LgSetwQZ3_P#kvPt$qXeAcF2=43F#;%lXv3Y-dbI7YCkgBWJ(7C? z<03jAs+&}43St;R=00Z&bhq$^xjpSPe_aEH%87h0iD<^txJV;x4x6t(2)&CJv!fPN zRQ2_-12chsqv`M%=0wAmtQawe5yV%}&H!dFC_eRhkx`_V_@C;DT#e<@=j~@;s4hzr z#A|ds8-usi#_he^GSjlQWOh&uFMHw@$_!M}Ma=WI_Qiiq7#P-IyOOf^wF}D>p`@-6v2s<_kK#FAd>D`+?SS@U(?I&$rpPUzGSk-&{_~6U_c6FPI=X zJv?y(FWVfn4@2Jpjch}uZN6wrTFx03Z*`Lnmtttd$Lxgi2?nHG&wLf~$&)g?@W;Qf zKu_B!U4Yzv1xCVMoW(YD`=gCpbiCTBxV$I5WO9r8)#Y@(+%(rxp_9w~U56L}?rg}X zul@-WnEzARRDBnVp_}d9~9y)uVL+t*OZQd=`ea0S*18SD5a>Q3!Ofq=S&)Dj12PKMwD;zK<4Tbygw-wk! z@}G{_b@H^T!E^6bMA^w?yVs4RK%eERNc8Ai#~2ft<78@9K!R`4bU_oJIsf^1dR@*G zX@XT?5!Loh5KZNwf_d@OQdwJFs)?@emAPfBNQjdRgyws&KXU}db$<2_VX%*A`ua9(pTv$(-VCU;XjnO_}mV&Q(N!KGM836imRJ@*@nb+P~%Fq~Hom z+=vQ6>Js~;O^<}cUL6$}bg0qJ-++DFsPeP&q4$H5b}=FniTE65V9?E3Uw2f)c+|gp zuOO6hn`=(mV_}p-zP^6epwN{c<_vKj8tA!xgJk_m`+r57V!RH+_Bq|)Wu8`Gv9@Y=D7I*K z2))dm2szS1ZLC%f3i6BlV}-~9G;!Fb`!D=0R)z|JNM2UEtwAW-lBjb(m52}V1yud- z+UH6^#h_U_DDb5FEyy0ZqvK^RXoR}pD4UG%&I3&ggSo8Y@f|Mc>bW(f>?TYc=6V00 zmh_Az3I%u9-6$Xwm=HoJ?EzWL-fWA1VV?GI@8vz^5IZ4fGL)|gsOcu{4cic?%P^at zJAN<`_o}KDnz{Vpy1V(9l_}?6cTfpS77F6C(1Ig9t(`p+j0s3uAK1!-v8{bW*M zR+nS@E*y4op!o(@aRZBf3rlpU4x0JrvqKrlSUpze)Nmi88g58EX{FB-)_;>PqnZG0 zhOZ58a`N0$b1-)bo#<48vu${ob8HoI4HcjmRtdY6>J?GmuS`|2hie?rm?SYAZ&c{v zrv0x2`a#t=X#(#ZI}+U8DD}JoVD!#*YaVu*#Z=6J0nIuQzZbYyUYJ4b=b^+L)xEmz zk{8y0BiAyYhhe|g^^+LovM`G}rn5XV@5m(j%rTlETK~bqt^H{P>`jX2ZclHQVg_a> zo~qxR9fM~7oKrj_aIf}ksi#`iWl_6DyW2pNh?^sTg&0E_u6pTwBW>WAlJ!f+ zysJy}@~ib-y<^*dyO>(miL$r-`J6^H5eh2Gdck``neJj;0V&yYR)LsNb&h(v7Xpa@Y&;M4%ZL=0tio-d;#*QTMH=1TF1AKxB8P?HNkBoK1WiS z0M`(J6A10$a)-S)I{i$IH1`PKDg(pZd5(PlZ}aKuVYq_uYT!F2=Y3#vENK(nP;P$` zR zp8rG3vi@KiRnUmIt-17^$jf?ouC_6qZ?NQEv!ljq#}NP_YSef2X5OzOB54>I6=1(t zzJ31+p@^PWV0vc;xmb{RWv$#4|DR;o^*z+8Pt9bGhG7y>he&A21AW`I@ppmqR3a4Y z_iyX3Zv5ukBc_(%-Wnrn{BlJ1rD3$s4q&)Bhl8u`@$b9V_ocbuK4AYB)hTIcvsf*g4hCM|@~s?fYLbaFcPyO_i2&(?9E&f5ep} zoNKb_iu-4T+|-nD(5*X0aD`^1A31TPjkSUJj$LO`)8l~v#AbNhCd9q?Fagy%#&?$! zZf)2aV`o|Y{O8R7gqJr~0?g3e3gIi(2<>P52o>BR#<0YxKX5DBo2|r1r}P<0MZq_b zNm=g2NUani5#1i7!+P=mAp;a+1ppa5XhY%Nd?7Y&yc!Wt(>}a2DN^phX5ZND#r)$U z->Jc$&HP)Z_d5tFf;Qm44DL9rv5avSN<~?>7uJ9_KD4Ix^LtsmND`?{&9Zak=JGXlj=;;@(y- zi7V_k!uEi^voGbc|AY*m!m;<Js;@q`^T^;c&{Aq3|`So6G*d+$k@?&QW4g}-{Z0U}|G?Dt{ z0`RmvRRgaKV}&zHHutyZ&g*^Hawh|8Rs^C@z9_AOCkZxxy!zj;pYeS~AcZC-Y1U?< zx?4(t@>G4{Le+hh6g_j@{6PT&WE|1!Itj~`=a6&IU92^=2p%2PlEBs%*77$D^U3d& zn8UC|_mO4zRUZcVgw^7x=S4ZvYRM4f{ug6=3nMQty7i!Xyo*=v2)nCZdmRq?8}Af) zWO-Yd_~+OiXoY+F1pkE#9t zbVRWijVw+z5Pw2)=W07;hW6DjM?rU*joCRcFv0usCgTre8pA+Jym!{D`|cDLcKi+6 z-U&kEy+>i16;nB-&UohZ!UYlk+Z-UXY0-v}{hNrJ_LcDbZe3c(&@nKSJhm9jNN6^}gOu#2>h+BLi_6RE?TDZElgQiRO$Pk|^En znS^yGo4wRCgu7u1dlsgbOe?riOdLKlHJj6YcirYjbyrill?$P?d@a{!jDb_tq#yu{dJb?AD8-}PK8v#0<2KYf9i@jg z$`q#Qm-C=gE2&rM{%(RNM5}qtO0blJmiSf6gwkibD=1?bOC3er^jne_(?aDuwk9yV zh3;qU2x>5cK8UXN%C$KIn_AUr(f;Q-;%Xibk3PoZs~J{g6&nd*dMU%INA%1cpEXp97QkcV%VA5+{V!#PU;oBGc; z3%0?!h+poonGpr6!wN(c4RpkVUw1ynV=-LNL|PI>)9z6=d~)aLKgYnJQ}qtGTG&1 zd|G3jebA*99%B)RG$WDE+h>H~r@_J~`D@k9+PV_TVe#7@jTT$sEzTlD#=rPZKn_Lz z>)uJjk8Pee-_J$%$i=M}LYY+Csy<%qE@ZwR*WejS^?BT&No80%gx;jy-Zlkb!`HXb z2%s}tpBBugbRJUF4RcZlSi?mCj-0GEJw~1K<-ADdTe!Zon6Q338d6?xh5HcprP7<} z;&cv9OB~-319{ZWB$~>h_V-%2P&D!(Ksij0w9@PqnFrhk)}Z=(_~SVeOVYGZ7NU&} zVyEEvt;%KRgZ zmP}*01ng~~?6TjsDC;K>tu&R{U}Cwx?HdrwLb8Ga5nDVse9mnefsvB7N)f#yj^0?l z0jGF8Gmdw-aF1+cany*?!7ci`_Gou$BGNo(iln8@7-C;$mV8^T8K&t{9qnvJLjXd6 z=a-pKq38^Hs$7ltx+e(od3xUh))yue9aNcN6njWOxp6bR6ZhK%>jumAfri3bbE?aJ zR#NIX45e65QJ_f<0%<}3x7krMbN6B!T5=5<>e(FJ!)c93rf7=s_-Wq~#?!Od!nQEvIOIypgO&O)yPB`K+u&qzG%ihK9O_4ZXXSE-OU3G6xa9Gx0r#+4f zKrJo9-2BwUhyBm--ojMcd6s13x3O6j5a^n+hDVWvnu0hhNEPLpEfbJFVLi~gFV&S- zgAY}??DtK7#+|M?En~*TCg9*i*!Radq{&p%h7sG;5OvC0VrN5RI*-q)EGVo; zE!BpVGq+Pd#h{mFHdX$9sWan@XRglnDi&zj;~@WFYsN!YMFLfvoaG+66H7*woO|P1 z={72r(emPLoDn$H-FRL~3i|Z&=*&NN@0imrd{=;bY&@#?TeWDmU;5Xgh%v`eOZXH#$~sb@V;FJed7ib4cP0`D_1~eL{z%T@^$3@@an!5IubI>mmDY zrSf!|aPXA@3BfTQ0$c4E=;J?fDb^OpC4U{6lMc{)FXP-unddt|;Ig69KqmcRjQ7Ra zC;$6{DkslV)M~tbc?EgHYDTOG?Ma>0Fzalk+(%tre{lGP!glH;J!ip{{6#!Ncq+IN z_WQH-_!kjQ-;W_>(T4Gx%k!!?pTgF+-ogtS(LuQ-xX7Erltr$0s(-l4JPdyF{$^px zJ}l|o-H|H^&qyp(8-X@dXP3OEk<`UnX;seN0dh17R^h$iInM&rU*{uK-=B|}e)$jiZM=)& zF=S}1&}V(V%B3-}_q^svlF`zGANjj}XeV-K-(t9PG_&~a7S~p-`7?UL%_UM;{dMl6 z_*zUun*4Ll(RYc;p?s&gQ&BYVSYSdcw)jd|q6P%Ji269i>_`go;@HYm9=?UxV^Cwh zTXCC`U8TdzeSd%Bpr^s$aABAE_yphKpP@^BzP)Dk=N$!ei5oA#xyyR|IJCXe`(2~l z0BJ6M^ZcicVCPgH^?Mfg`6+-AEa5l6j9qnBQgms?7N~{ujLQP5%ttDj5Z>tq&O88C zlZYa#3N9RRCeSY#=9cjvSS>W)dMGk;NHK3yhlk`)c^9@%reE`Y&15J1_98i+(4X~& zo#p*+u4jSRgo$7~y)9(*{7BHz4c}KenNwn`9ows>7s;vv+p9`2yL~QES3fp-OOWC) zB(rIT9trs7+a5I{dP~g}-&Z7fT9>(dg(E)fDKJD#VecCWC{k3;)VKKxQ`RKdtO_66ieUgiAYZgtkN>?P!flg`+hw+C!! zrEx7Yn~CERdp7tSy`?ubx`~RF3@rB|X-oJ)YLV*+W2ZpamsNc4;Tqj3RaM2_o}>1I zssy{cQUBS7TAcjCMX;R4@3s9M%hau%0V4&CVmdWMj*7e-uDGxLYx~EFYO2?soF%oC z*(g8QvmvIOL6}mj?asjti3u+m!+G$FwPDglfyvV6C(o;H8GAj=fU`c*rU<`S zzHmU65PF|)2%{+dwiTu!0p5X*2#L|1Ta0le2j}7g5IAxSw47n(WA3QfnnA4Rs8!MA znr{XudA^3%(bTRPPTp%J1YHG3jfYdrfF{3g@>3V8EI|#vO4afI6J|LSUk~^ z?k?XvilZ(05(0~R1^r1%#MlqwZyYF&Wf+ND-erymIK9tKs|LG+h|?!@L7}H4z;*Pt zkqxoH{i#i=F3o3-q77YeN*)l)6y0`AZLBPYHH|7+mcq+eh-3kp=5zy^dv&4pp~LqaD$=V{>PZARn9yV{SP)76Qr{s=Pq;{8aO<#p@=W_^~>MEG+ICn@RS zk_-50L5->l_XW-w5FGz-FpFa*gwwtJp}7PfdB@=f5ct05FtuEJ3B_@5IeA<+4;*`Y z9ld!X<7ttoTf5Rq^w|I&2VG5&&Ce9^{#hCV@743!F4ri|zO)9Q@#4BioDq-Juh zO&Kekq22*SC#=tX&qC}eshkppom+#lQF?KOwMDB()D>IX^e5j1tJDpgv2TAUytOBY z?h?r@uT6Hku&xS&{Q}id>J0`<7E+wcEmiLXDO)bp8quh#uMtT;6aftzxyVUzU2zzznfp)y6 ztv&XQd%qL91*K58#y*`IqfBO?Tl#VXj5GrPk;P&MWxm(Tx$r9p6X9ZMmPi&{ENOZT zDq*mvK1Kax`63LE-Sv9Di@Jz&cJ%kLfjEx9kRruxrlUjM;;x({_Lww|QD4MScA=+Q zk4$%a@Np#mZ!N$}Wddiayaf$)Npg~h7U2``2}pi;v(;PmZ32T#sXEorv#n4Ba-FI3 zrzRU+d>jVA2px3&OXv`0$Ynw)9*@XazAC4~Hlq?Oz}}D>5Pwy~x}rH}K9Ao!PN^|OWqo1q1iaD#k~Kl$Je|p|9eQd6#1PmzudAnT!Jz@uzm82m~Dg2{w-5JaDren2?hR;tCsm^1~;^BSfpti%DCW+sF^j{ z_j;5nwdzAJ{6pc0MbVGM3;;D-u}|1f$v`kwF1T}f*9bdGu_?o6nWvX(bF~WbE2S|^ zdk*ZgeLm_6v>**Oc=yWsmu>T3#c zKQNgjf-=8;jwOkK@yN3y1TWG4(m|9)IV?nn&qG%eq%&iLds1n!z}`UteVyjhFCoQeq2A11>?34ONV z$xu2jt;vk30)gmi@@Od|T$;unfWwE*pvFZW>}=Kum4@1F8#mW(&jfvWRp$jnl-_hS z0fSnVHL`a94xlbS*P2?BKmCG zFb*LHgHz%L>dJxs^Q+OyRO&i`uGiP5;HA9$=X%r&tX2p?v(JyoM8Pn_j5BMI zCY+({gR<~5mZ7FPh|+g7>y(a%WJa_Bq|5_?@5gqu|bs;B9Feb3lZVI$&WK}l};!|nU{ zJ%rC}l5Ors^e1DePIbk!Gk0$Vm(&?S?o|o`_bq?_z3~o=O|X!xQ6c*`PE}J%xHlk> z_28|Kvm`@Jq6N36mWs(ZNesBVY-&vTCy-S>LJQzzk_(gr(zKee#*YXTHIOBI zBcLo(74BVO>VwYhi5(UfL~O3j<0n!NxlK&dqsa?pK$GGo41%!68F#9bh*rmlY!XIL zP(3xC&YEIMI13zDThRvL5wJibfVYusi=9c_354mAP=2E_9)IG^Y{slkH5)ku1 zG_GlgrFh_s%tFCOosRHL0z#H+mpL|YYmzDl0c>w&e#56;L*zsdr_7@Aa%NcKk+K(Wu}D=|s5(s% zWA8TPN4ENWKhNEL$=rc1fSP>;vYtVd3n|218e&w zZ7Rc9GpC_ZJIHaDl*M{T(EwQz+?|^`U_?+VZVp!t8bQ5(QYWzd#6gS*tS)sC%NNeL zF2%~XJ|{xfq9Y;v@_jHph~zFyQe+04M6C^&=nA4vVv+5j(6;xtBnDX~@6*P-FR_^>~*sG9bQFW4?bwm;sDU6IEyBA97P% zG1XP840>6vUxGJNT~LW#?yz~>zb!h>U(>CQ@gBc(wIO?69T0hFjrO|oPylH`n?oC; z8*uI{H7XU&E4>$#?g>}e)xf^p0+6ZNLuD7fu5671&aK?k+jP)Yq6NO!K<*O-|0F35`_vCXr=?5R&@-ZKe|l`60Zs7g{i;C@wRk*` zNw+MHl+K&wIHioT{W327>OPFQRd#XUzoiK%t470O zV1WE%ub}M^A4t|rkA3OwArA68I;9WLfuYh#mT|t5ydyZYXhj`S_m?JH@6ReYne(xR zo6yY{mEIo`2p3`8{Eh**b{((Z{%JU2L-3k>rG)&NvIuAkf0i||Z|qU}W8q-rU)UG0 zB+0SMT(NItzH%m03+yEZ7%o_D{G$`mNy(6>$kiF9rIK!wO z{MeTA2X?xofOVlUF-(sZ<7LhexOgZ)^o*>l_IBz7$AXEvFbx5FM(%nj-xUk#yvPNm zcY?LqU!8C}{Bw;&E?;1LY&`YPkmalpD*M9Ur5#NK$f*9`rg*QA#gs z*}^z+sb|1EsiIoo=g983`sM%De6>|j54BY^yT|4)d|4L6b#8zO4M{Wd%ud~P-SPEe-!^J49`I%HEsXR`!EDX zl-b(}r;}~*Pssj7%x6=FM;kTn#zhkOIbIMebYTutY!e=)`w+pEDNRmXA z=pkhtkZ)3B!6DiAV4DS!>k zy{_!}9#Rv)+__l=U&_ZxoQ&ZG1)<*rRna=4F$0F&Ln4mY`*N0hH&|cDD4bwV?U#WD z5jf$eoR(luJ4yl=hOkTm${$C@)}*5EPWUmcde`i9A3cwjr~7sEHWN{dI63zW;Ps^#S;%Eh6$%?4c;ewu5}7azNX8xLiM zltH<_r%WIY@`^O@d5gs$J40g%xg15yF+kx+1%&8_kmFG@eI|t8bYi{Cj!9s`0bM%0 zqWYTrmdbYO4m+d5MIUh!Dd~Dx_LIb3GZRk4a+cxk*v?FOX9OE>j}zp@|HJ#HxMg%u zf=ZkBW~E_^JkO~nf(P!yhu~U z$lybbh^rw&hoFCCW4>GJTi+L8oVIdvgedu00DzzJ;K^)4Hmncs0hd;L_byP9wYQiO zk7PeXr4hG*nH6KQ_W<>PfCB$P)f6*mzl(cU>Iym7$SE2-#<3vMde%=tz72tG1Tp5i z@&4jr`H8{M5o4aYxc9E60@^C;V|Kc25RR)TGgF_|r|k4`NSC60G&WzfG+jR%TSr&liLwjRmZR=H0Rxs?H$LZ5vlPYD=@pw{aPfUJ7{U0O1tbMCmO?@3v{1c?-+d^E(gqkarg!fso5S z^LPl~!3_AAl4fd#1|{uGM%_xy*yEV_K{_B#svq-KPS1(SH_%Y&DX zrAQj(gi2(Iv2gO80HcrsnHT3|Ciz0Ba##&Bom;+~T7{UM!rKq$@lmniU6oY7Z5=~H zIlWC2g6U5#_IQuRu{0^Y^k5Ci5Udz_>;?_x- za^GGi<1nU^8=YrQAJzKp_&KnmKLXiy0}xkj;I-h@nOqx;mFkLA^bRn>Z`KaKd+3QJUsV)#w7tsi`5W9LJ&43(F34`-!kZTBrS12@&0xr%N_5 zMx&^F?U7E7c3eiVvlJVIUe|Rx8bvcbSmLs%VO!ov&g8<#_z3W{%8a}8yrl>{TL?as zd6CM;IDOF|%dTG?XzvQ#;K16ujn}~W= zXwdhMd~h5{aTzL50_eIALCB@1uQV}2SA97~CJ{N#R)HzJ`SO3Oqig?uRV%EHjbn!1 zw-Y>cHHo8SUF@-N#_IAUw@9 zeOJd!6+aJ8K{>>sxT^5eDWEjj7*1?{A~GIB_H?c{es3(*%3Cl2OxYwK z28MpdQ22LnWBxrQ{m+|-jc{yx&T(0C3VaXDb0YB4VcQxE-SUhwn>1zZ{(R+BHp8d)0((HlF*)h~$fkE6{PQ(sDS7ZMdKF+vRnoit}qu^D^+L z=eD<-OWWVEMG}lLe7&fT9gu_vDsm=!2kNXO>~H!e`KgiLv9|B7tW>n`Lae6b zlI2{g(m-wE!Yt-h$i_GFZb<54$tFfw;+Tw%l*i)|_1dU+JY!+|Fu zhk7tcaM%|}2ad^IzJfSfIhm^BqzxhSSL94>?40S*%N@--nJ?u46zevi2%x;@{)t= ztnkCIlkS-^u0388u*8@rRrM+7m-oul8P^ZT)DlKNGxx{@jhCbfsr0<+_e&nB$deax z#!((D!vp7qm)xJi%S9&$rn)T?kz?nc7_S)5QH8N)xpO zFubCPCif=pVHpu6_*bviUd-5n&7)#97?=QYKv$Qs@^LeKf;jM?YC}rgaDAk&-3ra+ zl0Xqr9zB8OaH*mM2gW6Ermb}x)i`e=jEg9D5-dyI`StR>pfA1B(-LM$r6hsw+ngVV z$^@co03@U)6ZYEIAa4MmN-ZLfERYs{fiX8HrL92VsiO%Etvv=XoM9>eTxE=^S^j(r z+ni?lpYqcx z(d)yENBuPnuGr5Y^fy7{>|bH9#1b{ect!&Ox8O*sQ2)v(XX~6kL9_`oU?igBW)7U0 zlYAx>w(%Dn=cWJ(tmb1DugO2IjOh?BUQXzbO5;;w zQ~}a=)3|}9jO+VmPrP~@wCE8MU6wgLt!iY{W%C1_l$5Wsd{K_iP1fff)&BP$0?n#;SQJK6C(G$-X_f6aPZ=SJnPET2ui~uFWoG*HNUOGFZ6x`>Rr4*5y7Yn+p$?r z@hGus>|AY4MAzvV!;|K*v%jy>Y4hX_H~AnJZL7-5D_nSkrVQ79JMry}u}O7S(trby z)R47YLr3yU5CdV^6k##+V;j#m@dS##j;x^A@16vsL$_l*KeONc}8PVa+N7(Xu*)GE^iMQQ4u)v*_@&+jJDYr zS$T|X6-all;{x@FawJ#RnG}t&kyomPnd~k^oAE}Q1L5Pgbn64$UysE_q>!g>bb=BP zU|2j}`j-@owgAfdG`5NHS@u)jM?3Sg$u7RVLAg_ITblD zbe~gQ$T1>g>0oMmxYE6MCI19crWug3!zGdckn=Ifzf8sD3iMX)iSR7b7A_v6;D~=r zg25dcBRuNJ{r1!^%Yz~5e3wTx(HoacOg(W0_OA)5ERX8Pz6JhAccWrH7Yh}Kpjp5- zX9fliC5I@IeKxx#6ZH|W2oIIdVTRHU`6_-P2Bg;r>3BPyf+*{_LUdqkicA+RTG~#u zfF}b@9#o^dpp(MEL-?7x1J79l=#07K3VdOwBkKirCRI>q<;PQ@3M@7RC61-)eh4;K ze~fc%MropH!X7R}BT3&EOo0pKZg${vOtK+(XRKh z@qRlhen(kN?*61Y3Oo#@V>=w2A>E14E5maWM65;sIr?=ThFx|4EO>I@OJ6qFPss79 zpCp0KdAG8PffmQmX(p$*rTz3pFpysUi3qx-Oh}2QDF7lHI*3g!U3_(HY^|>-OY{*& zO2lr_rRIhR_EJb)>@F27?6$uNxquj;?tGL|_QUM!Df!D3AZ#=k@#odpGSVf{=1otv ze#+d;U?3~$nU=`|ef2>jX=01l1;`&IG3P7lZDJdsu1H^^57?-*z(y4={Fdgty5{QB zYsWu{-leg>CuDZ=RBFN*nYQ(wRMb$QPk0C({Rizh0*Jg?izRDXv*1W=Q4Z2hXj&GV zz#z&VGUTSZ=$v5N00?a{4D;R3PHT0UdiwA&;@*rM}Dx$?4*Aq<+J?7Mra zJFLcMIg2l;m@1{^qU|iT^h<X7ute@h}B9W6DSuZeA-bJ!b2Ol1ZgTHvE(nV zKmEcOS1vgmInNHZi!0MO9TC1&o3kU&_3&AHMxCytA>TaDkpYr{x4_eZK`%=Y!}jPc z8wPP@a13!#`66A%>@hTloeAo-0s4}6J_+wGvXf%p?|A~XM})(O{T2idOy}6>+#`5^ z+P*na=enaNyS*UaVGsDp!h53DIQ?=GKX1&A4|d~UmU_QMtn;PrJkHQz2xBRT_@tP$ znXz=sla)i|7~y>(_1{ajT~93)30t>S7v(;ltH7K~j-rfnfVRX_*?RW6Wz+LII_UA# zS)xo#Nnb(h4#Nhex}SU><#Jty0jJAbj0wL+o+HKrmuev&;RGsX2vB;T-)fP02v*?m ze}r(F5>8Un?;aZ;7xcw{i3r`LmJa?i6gJf12)#w0e|P!BWrKg7eLtj+$}6GIQ8%qo z(DHiS_?8^evOV3cFMu|zeUfM}-DZ9`PawSPA+R-kpy7dhww{AwQnEq3bF*&K_R7U9 z*I$&@zro7&X$ZdbC3K&18(mf;U@I;a=V1HC+*3x}ee7MG6?Yi6A62;RY$kwE=0d3@ z1IiWbyD)vbX8ApaGZnS%M_cZW!fDv85gNkJ_05TNpoh8GM4CNzd(B#_jq71?akhv8 z3AD_C4?bKgWj-0T8+H`&d}}MppG2I67!e1+215?{}3%BHq`aExvg-GBhl_R;F zyY*II7xms8Zdt0iwi#XWeFY4rp6Eetl4iw?a&m!LoeT{51f-q?0odH4>VuX)rZosZ zPpks$o~)lv;3H?qn2gm-o+m==wM#KViRG{)_fI%#|$Tyzt-n6iw;I zSctkN);Bj3Yq18G7mg=9Z11&Ju+xVb?f+hC{rw|1*Yc#dk9S`*uc!XR8rNs)8-yMa z-i~v6m`$v(axV#xZY&hViuNdEixKA8aylO-?046EIFlM+*hj>bgQn!gVZdrwp1+Ce znbE?iIE{x}tmTUWne3UGGL-7Xf$5BckQ9`&5b03v+?)Ni1LH`PmP`}R0donNEg; z6FJiB%zWrdpf{<_i|_Un8IqVtR#=hC8Ng)l1t^D|qj#gSi8Y6dlU|(xzGbPP;_~Cx zRt%XO1f>GeoWi(RStH{QjfAGuf%`oqULOG@TnQ9%NuIG^a-+@w?t<~rHMjmDG$Lm* zc5%lFt1ChXq(B8**!8>Cir(1H38k|JgRk~%YCyp+mrLu7l+n^YJilM*wz~f4roZk2 z>$iH4Y^ePR9d5EDC0YsJtbEC`*n18Z<^Td7P*|iR|59WQ+l!jfHx)_(tMh1Sz=$Tc z+ehL{iT2^m&|i9$K>=le9n4+A?Uhk7)Fc;=awJn_&ljkC(Up1DevAEyzSxfL=~PO_ zLvskhTtTyUHz_0ss}0+cSa$m?$zuD`V5YMX7MNG&kCX}p1JMRjHKzswj;>TkG~!W< z_L~xThbYa^z8a2`1;dL9pBTi=q*J8V0u{v!Ro+$6sJgbvP%^2P!joDuHF(09`I6`6 zF8d{+P13TieZW`Baw+uu(>0bSVDYdLX;*n3w}hA&Az-U|phuTX$=ODl&uI~4@y&Zl zhU>tU#Pr%W-eigQ{%O3M`pyWSqF7AkS$UR4L)Klo+#M((`(4ZOr;jL_0Qk9uPy|No zvE>yzP)+5X%Ie6E{vw>)s9r$45^*+)+Lo=PY!(8X=x)tMDKuUI<$rTjL){Adli5*!egLp3yWJBnYgj&HoY6G17@(dB z&^xOBCbwR&=O|k16JjAotIJk}oU%+o%Y&+0820mNS>x7Bis0wGu)ehB+Y0gLKAlYW zo769?gn4cfY0`1oDTxrEq;cc_v~u0?P{;rOb4HF-7fDvR>=tF6kzL5jDxu7r5|?@Q z%FdoyT|^{%WuJ&5BP2PTki9aF?C)FG@Ar6o|Np-KynDZ1@AZ1WKhO7gKJ6LgX?U(4 ziz)|P3Q7Nm?(?R6EdeKe15p<;E&cN&eq!9Ns%M@7FJ9HEoOX(zb7q`&%emu%GOGH6 zA>1eqc>Gv$cy*iTj?et*J#l^}WP0jhQzEmQkx6m(2bBHlaerpN(A~Xk*%*fb85-Zm z`t9(>^^O&b(A^2+&x9n*TWAI_nz()ED)63zuCLt}zr4RB&l9uyB}q{=E8lDDbUHU1 zR3+uT<&cF@c1o|d!D;e!rAv5U(w|mylF;VU`GuB*6X|YNFoe=HCLOFp^s$7Q&vb*R z0;u={6Iw4VV4G^F74$y!xgs%E2*rSXa?delCo}k!#e6HGH#n9(m3CE9kjrQD9n2ri zWy1TjdA<(t3f_Yo%r4=VTsFR@FSEYw7UH|4|139%hpl_Dz#S>6|ib$)nWJY?HH(w^GD3M)0!Nn3caYQRB!Z)XZI&h zon(ApXKLeR^3LT zXPb#Ty-(OEjzuifAPiJ>8F)Aiefjxc7JuQU1IIvs_g-$Umes+2P(oABG8!lJ(rc*D z%gfo4w&V#dETX_vetn?9Ks-@hJEA44Lh$=gBH~!*a;w_Vt?THeZFg#SCe+)!cCwyE zyZ;%m|LDw%Z%?jms7z;RVYK+fHo^oSyHJ4fu)8Q1vTtZxlpLD-ZD6Rs^3ITv!Mnx# zfrsv>5Sea^Z0`h8P2|c&3lF+FR?g+*ZwLKl(DCm98XKryD2*ReLj>3279E&=9HPzg|XhYv=ou85;=MQ z=klU?p$ZJ4mmPk}BW@!5w~mGK@<(gkkipJQjTt+H`O5mV-5hRzSfgD{@`6ugP#>=L z(OHFcadGi4gwbb+XB?Q9D*cEg;JAW5QE+SvC;PFZa$0jPMDfD9(-s2%P1qWrbq0rg6 z*QY|}(3(G;CWD%0+mERsP}F{58IEV=L*_s>9{shwFg3l1BRVQtjC1PAV%Rj7t23UucLS#e!Hws|ZhZJ! zO`&jU!E>iyPovU(YNPfd!u-K&)|Pa){bbEL^gkjkCR7!4#zFW|YklkZ?qL^fUZTM7-6zbfyK8aPM7K@h0YUi?XLyOV<(Y2f_fwji%p5Hk>CAo#JvuZK72hx z_!pPZ%4X$T)M(olaqso~QD_d@X!kL9?7b>C)Hh7pEOKr_JgI(&bbCuR- zLXioL_u_Mv+B=?iM|Kh2?=s#_Tju&I<1v5#*&*yp4!z)6>rEoejL6nBfVW;qr#OK) zZS}0WonVs-oGQC|fC1JNHB_pTtRw+!)b9Mg{&qE>h1TjHH=6URqDO0?+pH_If|K*K zngoEll9&Uo_w&mpY+$_NeWiGyep?+@94S@6Z$G|qJYMb|-;{0ofR*G$?JlAN`u=fD zFKfHVTG>RJX(BoFU<^qrF)8pwH+}sh(KTB-E_>Ugo-QBPdOEPZ;8Gl{>Y=zmM;VfUNo3%3B%4~<@i--+!_ zwH|ne_+pPcO7@!`_gRS!GA`Tp#4={qf+fb*zQEgqYspc&WDQ3&fZYykw3fP9^TaM} zF#TY?^_RZ%5Xwc}2(1f3*vFkKfid3>OhqJn=R9@;U`2_nU~<#t!TlQyf{P%3K63u- zw_&)qVa@kc)N;UQzWT?hz8|OV@7ggk4~qo`Ym|5l_$*i7I_FR_(OO{}c2ewyBltlS zyZ8RVg*dnkX=n(+Z^Ij#h|O<^Zs#T|WoRezNXC@D-f4n2=KCp<1~%S^B(HX3B3<^@ zQp*rOj`=acs~qO#URBTIM$?F-ZcHAwYNAMbum%)fiv^iDCf7m-L8ZiegQm=eIYyWh z*Z(Odo$q6)*j))+m}w9)81mSm;B?}Rx(v+}E!Nv^MluSz2Pr)NR;Y_P2E8b{ecO2d zM=?-nsGk33G*U&`Q&PEE>1NHReMz@Xkvx~_ob=@l zIP>VrbK@YyUPWpHc=CjBW(o@)4dwJ(#SAGX*tt2(l;@%0-J*a{{N-70Q2Q2+RZoS0^N`Il% zpIgC(5!Da9Msa&&rM>%FjPbuuTFu8If+JGuf!EtIm*xTw(B>z%lO381`Z@5fZM$Kp zN57@arcjBK6ewEL-*3z8^7ta_(<<2PY!wd(l>XJ`c~TF+bvHRgFY6=&JOB*eR&Pui z(Lr-5!E}i?_g60iXPy#1ZSfja&=V+f*Cg%oK|@0AAurkc;=%i#obOswQyxYDI=qSH zhlEnU*hG>Ywg4_D3_3~AVUN&DoZ*L1BqFJ-)5gSTai%qa7iTCtJN{agp~iQ!5O=w; z^bSmqo}<)lQP9NY7{}o~4M^u>YXJAWW+O>3ic^V6fj(S)0aiY`)4QP4k#mD~jhJM` z0eQi-uZsAouMtcdhC{c?$%ZO`kO1K2qRl%lxE@l-7b+JwU8xuBtQ_4LzjSvUS-06T zNaw4I;Ag9Dph{f3Lm~%PJkMLLEU2-f$mfMR3(MSs7<33Px=CR&Ztpi*wxxY#sOW+U z-*r_K6B^U$d#G53C>%_2E6& zoGyRxpE2HPeJs4>2=0px)AeLkP++8Z4JBNg`X`H|C9HAI&=&LM>k9wa_s1SzAWeB6 z(cA(_DdNnvP_lkQJS_Rvp$#zeNK~b4lkN zfStslr*m>gKNzFlghL%myR~;Ek3^}G`%$4Dd)LOhf^-4mCD)y*F`I zM2Q9dTt{P!DT>cDWhg^=Qls9^h^CTI2S9JpJ9|MN9C@Wg{*Cd{52fK*vi=syC>7Sj zA**q>pp=u!YQ}Yu_ZRXIZ*i zcW=3Dm@LYL0F=P;@&qL!6Ndb*wOnl`u(XyHC0RcmvL-jzpenm$usj$g7lIYvLrYD9EfWmZF;#qnbe z{*`TS9Roq^Nm|FCBc zo1g#N&FVm{jOx&i8hVZ}m+_BcLmEs@e=an^FWvaq9_dzB*?FvC^|!s17z zDUsBWFO7zZgt^glB0)geE78W-rBv;qEwGf2nAfZyIr!u-NAOyWa>!Wdw7C)=$Vw{! zLDJR=w={s$%V=qDanhpai!<2^diPQf#<$gPCKM!HQ6Fz(dn<0Ok)P9FBjVF{kfr%Sq#b4%k~dpGE|BShAH8I=2Lia5L#AjRYUeWGTf3iZE)$ zu6moK9IU$cf87>Zo6;2Yj`rB0|1sE5zw%YPlM-rN;mZ;+{nKPF*jD>?)4I(&kNa*M zeT@mnvh6}M^XVahEL8_ni#zPq3{b&g6C5cHG+ zE^v%h245A+pf^_&zQL)#6fPb|A4^{|I($Y{x*N|bv*|9YDwt!4)}^?W)8@8eptX8u zb3Q`5?-|rGJlnGASf!kDLT5?&q#O+Tb64S3b5M6ZQBz{hQKFAgkJqJ?P5;{2gXGd@ z4|8@Fl`BR#ggiGqWhcF6v->4e8PwgK3w%z1x`X0!Szl8A_=GeM3!#7-GvETc=6@Ep zT-ul76M~c_@>MNWW93`79B~6DbST=CE~2p}wL7F)v0Dz=!0)?b-7t33rFC;z9>e8& z;p|(bm0lhe6@D=ggbE6X7ZCQG^5mOTAS77vjX|#ui=*WSYN2IHD#9!S=lZ>4pW8>ZYqRj&Q!6Fxt;A`Uy#{{^ zX#~VuJo)qO-6HozmM9>~bo#{sTD0JM|56k#U;H{8Wa(2?kw6A%HS~T-H&&O}?|bB$ zHeq=^$3e8seKSW3n{pQDo%+u5W~IqB^KI8F>1~~uZ?lu$s%Fpv#wZcwWB{8CahYe9 z&&^_U{t_$CTb*&${=FO0poKs8;R3=_-!1kgxsR&y`b}~ZRB8EARr7@Lcb-{&I?RxT z(6JgWP~O>#f8`*?Q|QFXKJBfqdPKEBODBD+fK^oT7$}4p4)3t`EpS`y#rgOAo y7e|(91U@+(LGS-?Dmv`k2#;tmGyex^Zk?Gka$M^=WnJr`H`N=ON`(rh{{I2d3xiGo literal 0 HcmV?d00001 diff --git a/assets/vexxhost.png b/assets/vexxhost.png new file mode 100644 index 0000000000000000000000000000000000000000..846db18f09a0062d08e33aab2943e3d2f2b324b0 GIT binary patch literal 16202 zcmb_@Wm}eAwCxQ@mvom%2uMqpw19MXgEZ3JAT1&#r648U-7VcM9Rkvwd-3l556=0* z^(4=Vx#k>mjIqKM<)zS3h*2O2LVqVMt_(r2;2{)-gaG~;Dz8>T5GC|Z{H>~c+P@_? zT@?!g#G~aQNOacg6P$$7;(}GNSBa|pM=8p6JYcY9#PASBG80WCPC5I2%wW$1nnb`zLV;$}R|dt-2;V_4e$KJJ zqX75#g07N28`ALMt)_*U%@3hp{+p||r78XTLGJHz8UfVpbtlC$qsMfqzbI!Pxw&s# z!1%7qlpsZf0?idsQ4Fr;{;vC~z`Sczm05NeuwEzOu>S1g-LQG%XID1H2Y4{K>m5Z%F+vj}SaT{n zOPHjGYfa_F^bvSHy0Ap5|HAi}<@0mVKB-7pf8~!HS~1-$Iqu2&O>aCAL>Q&0T1qKJ z`RO?3PZhv^Nz+60Ml3&N1~KE-td!-;hfVq|Mtdh}T{V@dj_$ zD5x29)=;^fzHNceDBIvd$)kP+9|w^VS0vc5?{?>rSSuoE_;^wSdVIi^^oYXvBfs3r zFFOjs?KL(<$jojjhD@4XkIl!`9D_Aw6GDNaKEXS@DQxPD{pN3XPFmhg-n5FY?2RX z^(fP#8SSM{QxZ~w3?clsfP9-ftQQiwFsV1fZf|-`DcA&<&`9dmD-(E#Gs4B}*^li5 z(Y_oq1WHvRf?qfjf$`7y@=-PUOf_&I{6^!X(3ZNbh}f933UBv*p0gfBs*|I9yBarX zt>XB>N=!T#Hlgb*`z822XHKh~RnH1i9C8avDyf{AJ)37}w5mAk{0gYe2a^d*4qSYw z{QB>XG)(a;Tn4#xQNSrVfrD#o^t!lT(nfh{z&m>^oL> z_n#@aX=sLb?-B&Jgo~J~*Sxh?FAqd4D((sqE=LO=RyUgLbZ&0E@X$FqmU_>~SJw>T0CJ?O1Z;F7zN^06c?N2r-XhS4ZyYyWT)-8c82b(t_&}J8nmtZ5NhpdeEE2^w~p2vaf3u?=x!z( z_3K5T7S`5nycvUycU%BE6?Rh`G1uo-Y3`y6=;;7PwwfNV%`wYk)VW;3yf85~vak-? zRV(~%_u4?W!4dT@5w>`5Pqtd6Bm}Wwy8X0}K4L$M{$ro``72tek>WyaLwPG^wsJ*V z#7s&uIa7dK?B>Hw4Xll=VdO!{)(Qta<^u}{ZZgr=2nu`JH5+HH@r|2Jyrx+AqsY?e z|Bal7BWPt}>-}iUIUVG0>xH@gUL1E(H;wfq?+^wGxD0gITEjb!MDSi9NoW>8_v{p- z8(!(JBc{N_qLE&5o&dq$`&=3WYmm>d~; zjuJvB{E>BV=)6>8q=G3C(MC+$Gc>LRejPcEevFY7Z|0nSTW49_KYnPR_2-xTHW}Zf z^@_2-hdiA8IFZ?_Df9=QZK*eY_^C0b*&YURH#6d%+C1$si3?iCixlV76ELLML%yb+~f~zVp`4Vl*}PXRgutR=VL_h10gDkPxE5 zeNx0xQC9WACP&-VuSA~NSqwGQy1iD?q-gbRnj7D6AV+-L+qUiR ztwE^SIWRAAGI-pqvq7s%3FgI|cuw_NTk(f1jugjWv3;2BYqI0)T0E;cWUBAueqc=^ z-ZGN4+7@nEE@dG@hMgBapQ06l^BN0p>>f{|b?R>Hzu#Po-P~j12`r*{p87~VcAO^t zKxm#O++Z;5h(}}cQad|;Xu)H0$VwrzT5w2GbpMre)-<3OBr8xyuYBp{bKFSe2o=`P%I)piS?|u&Z_*VNiz#22P4T? z`LX3(6;-lm%`lOycp|pXVeaZXEYuy&8XRK{YMsf=x;=&XLe}-S3^z;C9YMJFw{VdE z=iZfD2IKv!E@Y0~`5!-mwBoFgz*wk4*U%uSZFm}CdRo1XeP;8S?x!)EVHH(ZbAhPh4?)evX``*b@D|14 z?WsVH7mbpz0FZ9#m#V0#9Mg*|ne&svOBltM~d!kN82~L zt_%ytB4X7h^uVAFJ6LQHmJUWSr1FeJ`>EeEHl!@>T3t@k;`ToLk z^Lxt7^UUI|w$yi)9$ffu5#%npomo47Ob@$X=g|3HREH+`=UyWNTp0sv0r@Xx3AYHT zIsWaj?V@=ZII`Y8GJ|-2=k0jfdMhpeu6tnAq3XjN8d_&t`!N0sR8#@?JJrL;IB5>7 z%;aGFXcEGkdxv?7gy?<+8hV-bCW>aJvWgmXn~*~U3;ed8&uHEryfC%ck%^pFE#~rD zvx^;AktB23iRsU2zV3ob8N8!v2|*$zihd_|G*-9jVPUDVc3CUuI-g{vpZzI3K3vx$ z`mq}KDJFX2Hn1bxcxA^B^H4_Lvo&#`=7?#~m-%(oNd{4DfZuCtv{IV_CCzQEZ=?o# z@7fxkBUhX=)u(bFvDjF~T20>^etCk(ayfmGBTM`>Zzn>}Cv&_xUMi~=Xu{mL{zHd& z?)0PmLXB8G6@@|hBXL$rb)(>+aWa2CUEWXQp+r4HEquKN zztFAdy&yBKBBa?+=kRBcAr1ce%qkqZ)jea)esQ-2UGGS91_WMk0p%?>&gHG#e>6VNho(N~L8XkHNvUj3KgA(E+@>Ex{x?Yj~p(= z8(Xx7Hh;3)NwYB2$}=_dA3?3~vF-B>(}YUbOSt!;pV6MOC;vMMosTOVZTZC0?o$Ie z6MM5AUusUDnu|6;fL zY!ZgSnm=i8Cmi_tGb0M~LL(Iu>Yga7{;3=5x{-m2ju=~wStDii)1dV zLCvmWIib$R`Gz0EEz*7F(3YkxCQ&|FXBi`Ai}MJC>~$GlAwsbMI)(Sbo)gs8qlfs1 z7NKR}q1E>m{cOjl^WrS@TYf&xbp@W1y#!aH%Kcg4CX6*`RKtF{uM9cZrr z;3#Qeb#FborMVUQ+?XfKKwC@sJhr!U)=0R0UZ!ZqD>*e~wU({3S{uVauCM)P9ua&? zO?o_$>zk^OAR^3gcDx4NsLm+F0tu@>6H_X|bbegDHhw)x ziU=NK1DyQw9~tQ4-UtEV>`xJc$`*1->_Bkrm|x8yU+WZ5oABU+{$G3!)7$QVuwymX zwEME05~<;EYo`bj+vkU}+RE9@C~Uf;E{J&9wRzcR3G zyydGvCA>#e#We!8>yIOJJoMx{YMgb3TrdY#3Mn3E%imo5rd?i$QTX~jC65{C!rZ^$F# z3s_ftwF6%J^m^(TOS-UaFyMFE)0xZ`{}g4sQLDsX`ADbnjMFVRov3=7=4s zExZ&C=foL+t9e_X++G@r+46S`4=S_UvwQiH1`fLN7V1H#Kq{wkGfpp!<(9;_(_b%_ zqls+C9LI_;Zd4typX$DvTfj!1lX1S|Cx@Ye-$r7EdaksocbqW2x2%5jy2*l7^sb8{ zhMF=rF88~wNv}?Ep_No}J*7NzbZL&tGVe}xdbN7Ey2wgu$%jdWJ%QmEjATwv`rtsf zy+av%!do6V1rBG0^^x=Hj_(mK1myaQH~rp}teihsO~dN`-46v>EI%3Z38se_X{oo8 z*Gf1XuHxL4dy^4{wih7_XUi88OTT;HS(kDemqGQm^dNooa4%zb+y0Nwa|>pD80Zrd z6RY2iyXx85D9`pGm9h=$cF9SviT|=s@sY3sYFhNLXl7w}-)92dQ8#xNYbIM$1(2>c zvJ;t9sMu8k^5tm8HyKRnHKkFmy_pzbVgsuE1B!_-nen~rBCn3HQ5BUb3d^-*n&KEh zIS*9*AzJfAA~Y>TIo4<>syDwE#+nLqO8`M0U!YF=>;9!Ou^jdjqJrynxZ7WTin4KD z6(eht=RNjRMM&$rpy zu!XJ9oH?XKq!A<$XbI{#+TM5dCgR8JY`yckL4KuxFC^dD(fgM;_TTj+^3p(?#oId7 zfCj%0H6n*P5>YNgq-R1JRxv1u9Y1;S5<(uoe0*LgWx`>?y3Te~iIbI{3V)+oA+ADq z+F_t7?iT;iL4X}+X3exNGcAad8lJrYY1x;BY>{bnL?_Dg!aStKeD_|p$WQ-h+)2x* z0um`uHNFYlMDoCkKya}#nlGhpXGOap6$;4C8*U%&As*OzA5!)O%=!z1UR z0U*5mmDSu!kKbSTG0Ca89`xdK6W^0`{29g36*8z^Fod4g!@^M5>bwc7*IK1; z&IvKo(yb>#3jf-8R-4lY3*MMv5ESC4$8#dj{UG346wT3c#&Az2Y=jN}i!^fFy$E!8 zHjiE9ecPYuHSZo+@d`7{C}lrAA36wQ;9(U=qNo3{*a*S8F8HeYgA9 zEc|?kbcK0b58ZX&uV3?ezY__PtLaZ4**3ZR6hO%BBy=F`8G&QLl4UW2aaD7AJ=S%QR|Qosh9uc4v$SC!gZcsctklyO+k>)=N45L)U1D zTot{2#M7+KT)m+5pC*-xS$Q5{)&z#}Ck|#5sip0Rfp#}sqLBO7Dcha5&uFs=$@%fm zuc{yw)_pA*c4m@z5e{{r4}*x4D}pe$#Mji>%IN8)n-P zKx;WNjwZQ>BDq(pSt2u$$L<;W4R9}$7Q)%9_y{7q(f_fPB>KKz{iw=>@R%B`2k+gI z^m836<%&fq)LL&*D1w-ccIj|yW5-_!&JTk2dA&(l^uq#1){PcFy)CQ3rrzo2xbxV8 z-L|e&V+w`Op?($S6=6|QisxCMlHHpV42$^qtw!{jt4n9TG1>!UXiL*m_OM*g8WR=M zp3zAc;n*`ytwCRAeky;yVsB>`ItJ%Q??&;>!|Fygw@v%`^I}k*a^EyU`h7sw~R)GTu6*xZ$f{&aBKv8&+_|M&Kl~zopv-y zDeb}v&Y;^Xp~WruW}{_s@;nz5s+~XO=<<4XQJW++M*V589I9$Ncisvcw*N+-b=zRM znmT%W{!5KhpIcBfkdBZ2!LN{6Qp19ys`+_Xn|R9bb7E7%@o;vJx%&5)~i6xw6^5Dj=AiOOt77$@WwYPjf{-dZ{8iC=a|O> z;YmJncNQd-N6o+jlhDpNrAHSn6Jss>+nDsOxYps19} z6u!Dh#i@p)%!z`0CL6!*^jjK3fBfMs%T42eU(>LvxTuD&O%2;kDDh7yCr@wV={4z9 znLtT+`CAn!p8klE25~#P5-X{=S-_q96(XQ0c~N^jv{53yGAiQ!z=j_`-Q=CD!s{56 z7H-47w!Z$M!164tsbh~DOS9F7r6-m!xaWNDyHwHz&;2EyxOlhaGw3$E!jUP|%aB5^ z8{42RdA0Ji+`Y&|urF=I@VyEmT8X*Y*`e9!74m zB|`*Akvy(9OGS~HrI-y>)_M^ds;-*JsU{~QV&#{820Wg-Y1795sU{1!VbMMc;J*3g zk<@{=B_B{aTSo>+HlEnVdLEPAjh5%ETRjrK%p!aH25_^N3IeJ%;a5mzHIN?H7lR`{y*%vFz~}VPbr@qHZv9-x>eb zK{+yL#)3X4rv(l*V(u<2f9LGm{0KrABLPO`5j3RT+v)S)Zm{3SX!KqFW<_gxY(NAj zvm79*zEgC*4}Xc0%N^hWLl9Y0{4MoFxf zGgwdm?c>Vauz-6Ey`b@aQ$OgAhM-X?>cx#(#Y+)(()YERLNb|uMt<~#z6x;(K-hz$5V#6OyGvZ4#&@e{{a9$NsBLu&Icc+0~0 z4Y@{HeZO-MO7f3p!a}{SA+fIB$@!l8%ITTfgzjXrueos+RBC1J)N0Rc%=LLpQ&duS~5>K;jl=()OGm38I>i$&u7dCUMUosM8^Nql@q$!CIV@w%&Rd3 z3*xd_&f~yoWJ2;iMxh(lQ}uhV@)&Xo+FprDT5FfAL8rBS2`0^Y7%i<3w`8UmMs7H^ z2|t?cm67dZ){U@7qn+4gVu7n|Hg4|DpJQT2Q`_mhv0+jYCMSy$?l*U4<$v;6kHgrn;g{Cq9de4}+z1j}Dy)E@OUJ$r+|>tq|k5%?-n$Exlc9#zU>g zo2Oz!(Hh-J_nzbw4Zo9UF=l7 zP!wjOtVw|7RhfE(hDBUkT>SW{g5X<>y?tmhoQ~{QZDQI=>@3^j_6agS2N$%m^klDW z1CEryFLlQbepDZ2)^Ua2cx~O%(+f$mO z-pd&`K_;i;!bhsiZh@RWVcu_VTHSas;|i6}ce+CJ@U$FxWD)~nh>&&ryrKNAs9BO( zqg;ArRzFGVHx}7WWA(_aO$Zu8I6Uzqlkf(b$kzbK@Nc2qv+aQ$0@k*UZ|a2Z)NvdO zkEt?=S9#fti#)&Cg#50FC*+NX?7U=l`||d;_U7vNTcw(@t;3I<9!*(!i1f3@L3fQvNSCn6@xfhH$D49Tcj@XdqE$yHfWmdqRI zibXBvwSCngx%pICM{t|zv%C*k>H?f?`>q?KCXP!?QY7kri|OwXy5Nh zAW3d^$76pvU%GyJP57pa0!VF*U3qP%i6Slk&Uv?wcV>CyQfdi7-!`$=RDO&M=E?>d zliwaqj`w`Y!am;h^FCRaC5@&^M9tKjzn5gc{LO$VTSX!i{q2y`iK$iq52eW9<77&I ze$;T+q``vC$XzvRVNxWAd!jG=#Jq;lW~lwryHC({sxNG}!vX5v5=4x4Iwn0~fhXnj zB`)pg01+C3>`7Wwe)d-7vB%8bBGN;p$Lg8yq1V|NFYSn45gSfUB#5->=CE?3Tm9iz z4kXr$gDC7OJ6W%Cr8MLS-)Qz?OVe(>){6wI@Ou239A3D0qdwC4;qh#D z8d=4c2q8{lPG^Fdah&Ji)4p>zhpQ_7f23(Y)`6@WC*ax*8uLq}p2D+d^xD%9!J>)<@HUcuP*l_vM z*yPU?Eln|%M_M{u6gG29zq2VN8kR$h%RilTd?QptgB=tDGpmy!vT41H9v;SHMg94I zRi-Rw^hjS5bGLyM8x|jQA>@=G6Y{Tk=FYw4Rus$EN~JMg*a|Lv7;}JI}KpuZ|%~ z*?wc~vMu7~;?HlXWyx!rM!gq7O`jUJv)s&TE>-)XXgGgo=1D-)pV(h!`p-OF7QQ>d z@72m=$bkFVr29fAiWA)q8=!4-9=_#1$Vwg<3c|>f2E7@mn5{Wvowi#l{xkAOf5n?S zs8ghra_rOGxQ&2EqvfeV;abC$tkGt8&%^U9ew$g;)Y%TNZjmPN4PO!xkV`wV=2WV_ z1;(B9FUa^Sq*eP}cD_WJ6ebGFw4+@;N|o#KUI}rR(Fvbn)jj9KSv|ZTbw@i&h$PBg zemCu|Z^j72?Cc=%hDS?<LqseJHi{I-61`-(VLtrly|SV}{+ zY;tiN8;I_JxNebsDzF&>enyHP#yJQ|zq=0YTK>sz`BVB@?m-{s-K&Dxsy(O&gT(Oc z(sMacFpP6PhuCRXm`MOTsbrbqF5E0lq-`dp&H~MqYdV?Z1KPCxavmE_d~fYHZqUAB z@avF7n-No9mU#YV>2#JaPEu=ba0|=}BT+}Nk*bk%ik=?oMy|HHT2XR9vVcbOEV$49 z&J|AvPO^r7Fo%72>~it^Nl7lkTs`oMlgc%fHPvd9?CT`J6Hiv}#eXAP*O^uDRTKJd z98l1X-DJR1kdppGU>-Cyee7S=fq~C-A z2dNh8VyX0?g$3#LyYO-3=A!Q!Kb#?JmKh6TfB9%}`i=WnETG%N>g$Po$sTZ?dY;GG zcaVQd%JWvVzXn?*mT}|N4Fz!SXl3eBGo4?)GHVzq0L$suEQRH znFS(b?_olTbL|Qh4>Qj3p<2-Rn=3f~$arevslsT$GZigW=m;+D7}{GnFzSCW#|K6W zMplzK#cMZsAa$f8Rvp|#Q3;9#zURjTIIQ5Y;)g{N#!~~ZsHa!;W#Ny3nEg}{*4F#` z^B}UjQ`KK9FpnwmiW7MZEe~0!v{A%G@==2el01%+&)w_l*J77@CRP+sx-Id;cE()_KF925=wR`?fQ~R)EJgy==O}qOe~1N?ou~n*#O2#Z$ahz*`k_8f@O`bg zlyNfU0A>DZV3Zg`0i^KYNvv9=G`f(2}cSXcDQyqF1?GfJvkt3R%9t0gZICX!SQ=zOV zd+MZ*7AR!P*W1{Ds{>t)K^=n5ZX4(V#qm&d7ZCU$wxeW&aqFzB=V5sj~E-| zcrgAZ`~8T){iX0?%Wa7tzuA2f8Qq9M$Hl!=TLqIjlkShW#U!9(N)*B!-I>w*FMflG z_ajHS#dOD~e8GTls!=56ZH%2E;1$r9tQkfAHFdGKf+;40FBVulUl28QUN0a>>G0xiree+HG14ulHMUx0yUI^b z*nqP{g9MP4CKCs6gn~PWPv_+RaRycAg0usOVP)@=6ihd9w%qb&wbN~t!{zWg?YE-%+;AVX}o^gZwSGVm5TTiq6njp&TatPQJxQng+J zNBr993BTFThJMN?e`jFf2ipICG<=&lKre>|T zI;}+RnB|VhgxtoI7z~&~Xp9Q}m?^ z-Fx)K5^|deG3&L4Jh3aFpLe!>BqE0oqdd}o&ULU@It=6B=-Snnd1G`u|K8KU0+b~{ zJQ@%GWRs+L3E-__vU14dU3R8O~_6nbDZJW?t3JR z)jugGpzey}<4eoPkMSj@eTn#snrL+PokA{=$qL0Uqs4zIEY{i>4l+0QfmlhS;U}$^nZmeke zgO7I5RD5jDxUp)3sG+)QdDWJatDtuF?;gIwj2$Sb@E=29)s1DN$S!cM1ju1s4<=?! z79SV>0LR&fQ5Om!M}*kGXoLD#5WhcgEdF!(ZOh2XFqaqjDRa7!3v^6+$Z|HYY=PEL z_~LI!CgLOXgYwd?MgQNoqfB0IXHYAJkEY{*$r>EqM-KvJoi^1T2($ry{>?}D-+^VH=t1i*o!*vg z&^w}Se9@I@WRjDT-@SdxR`lrh#7(CS*t{zEklbR(3;HyZFMG_GxMt4N(!WMoWFKX8 zxzwNKS>MIKl6~|#tsWzYEI(~yJ3n30(_<}qtVPiCld&RJx0^Tm@@f_| zgk$B7vo2*j=t082gjyHnF17>wmK=1~;I zPIC;L0u^oTH9wbHNP?FiG&_jC0+&UAPiqgLD5#$AWGwAX|3C*H?bagZXVLn|BK5&2 zt6wr-aJ_eos!ncYK1XBpV!+2J&>eqsaK4yZ}R6>C!Uq)aX$7W*1h^mXnT`y{2}7^~=fu^Bl;g zSEpz2L8WR0B{}{8A6laAHgwUwH0lk=-YxyG+j{mh*TOLI5heX!&b`%D8$idZM-D7! zy58iNLAU4aqZF;1awtQob~AZk%SzOE#Uw4cCo%T zUPW7yrK~@!4tG)y*;pfv`omJBu1>Eh(&3o2Z&n zrm24^)Pm9Q$iVqPu1#E+B&ta1Zalo&TD_NBu;GQ+i|z=2Tg3v^vhs?KIxILRxS-dK zpU?EGo7@H-P=8hElO5mU4P#Lp+XEN*y0>X+V1 zl0IM6Q4}uR`DDWI;IF;M<)FJmV=TYe0|v&Yij
&S)0o|8t(t8TX#&@3hIKz`}q z6!170p0ClEtG|5?s(%MjyyD16(y9AOGv(byud)l+$ya`Yy8fWD~qPgR(bIWM=E zy%FlwWcEG=I9ccw`#(sQiJnBF7|rO65ff=3@a!KZgTN~iCRFdgX2^h~mh_6X_jC>c zT11ABUwEfI$6xCk)$ZD6p)m1p;hTH4zl2{74GppSZipa-+}N`z7pQ91_(t}q^PBXy z;(kv178=cex##^*fSPo?agBOD^E7R{W%Q1!>w(XG<N6ad|S*Cqez z!g`!?E&CTX8hdD@P>0{xDbL9g`+WVil;7JOi0KGZd|r2Of%CgSp zoc{(Xo~z`BFKf6K{<{>ENjKKc19f>x`jI1y|MSH3%%dR2W~yUG8TkAm0**bbyEQye zrDg8#A~MJkY9HRq$+yoJdquU7_gCXOP#-Z{z^Vd^=>;io$$w2L`9KAa^P1cNE|1iB zE*L7&x(lZ1jf0b82cX+r)N`9~&Ina}4Fa#8{Ubx{Ib&cVjo)G(Pf1y>zPCu`_2n$; z>Ms15M8JbSgT<56tOt8d*q}tLVjCmA>($ST^xA|BBUZ|C#O4O?%d_FB=}R^#N~LkT zE^C%|YPd<3Dtg)MR!|wQ04h5kDpqWJ{1sE3=FzbQ$u=ytpcwD$oH6j}K>m0z3vtYG zz(aS({^a5+a@^S96~Mqy5^fdSQ$S-b!3NS_-eZe#)WCFGHEx;Xwr_OkTMvHtk01Zs zpMV6^|GPiVM52w$g|6`qq}f%VckWKFW&Gv}&XEoV33xqn_#bbqlw_~r8mXUv$3@_& zyWPn0%ip-HNGa*heIIItgp12Wt4Xc^8D@Kx=#%S)9J4^7wI zSHPoD7N7k=-Y9$}p=&;1<RNNQyK+r#S9 z6}Bc6RPNs{tt^kq{+t}Bg}^WY9nIAHao&)Of(gs`^27Uraz4C%ZZ*^OM4TRIH&qPl z0`c?7+fMCwlW{t%38?kqFH?06*9~KT;?1<4%r%L{%fGOkx5}|2*{nN8_%2Tgk}~bv z`{Vu68$ZD_a#M21K7L%LAhAFdBIf`0L9k^#;n{76Osk4sZeX|D3^vtv{>buB<%;PM z0>=FEObhF)634}Wq+mJtNVdlh+gVi(Tw%xu_Oel9u>Q8IT79Y&WrmtBP5a8L;shZaNJA+1HNO3~%3C?H$m%V&NE%qyVFo){(o z*mz@;=->)JJjz2Nt$X3gm#onosVy&`8 zz{q2GZ%dim=B8{%wkq$u!KZcIz#X;nGm>|Bh@Henj{GY9SyShxzzGygXZj;H38_(= z*fr1gQ`ZJ=R3N0z!N{o{bxHTb`0l$a++4Nne}WX+FYZ>8Gk4uX;?^!OZNVvAXzMpRhmwYe8DMzjbr{=(eIw^4csr)_UzGZ zy;dIr>)M00Ce$s!%-*cm?u_2xsOV2P3WfyP+88F@^NNK{3e`SnZ0Q$U=( zf3|(xb8cm_UzSOUouI4W3V1@ju!qUHtpypIZ~%_7<=-EO%3Kd&$V`1B7oP5^A9RJ= zH<&_M5Ps!ez#`S%u{~m$KKCfUC2&?^8J@_De^iatrq{KtYruj5kNVP_5sO)ECxQj5F4^^6N!cyLMlY|7uCB;%6or)RZq0H4#&V^DFbY7?N}I`p;Dmq3(xB_42lh zPfSQe2>nF7K%J}aLiB^4oEa@Ui{yZvN1BamOb4bbd|}LMESVf^*%@0U`}E(ov}b~8 zq-YD@>dZ!{n!-0)P&sgCltsuv!$QQab%0w}(%uRLi#v<=4At!v5Yylg+x7O3ovC7+ zCjr16z+J+(u|bi}Ex=?ImV&C`fbjqu010D0V1aHx%_>M3sWmPricYs6JnVOOa}Z1c!gq zeoyyQApa-JW}yxpFv}Zv#x(D4YlnA(L-+YDh0R9~yIw96ZmZRKJV&BX45&ffBm z>#i=C(U)^`nkEMuY?c!hDN3bFif?>=J+ZK=<6|3pKqO19HI05}1M(N*Bed_@1jRxEbIi^NvSAN;CwS@R&*M_c2F1Mfj;?A=V}|r)ZA`aDX1PStCi9yW^&HBQCz_Q5 zLf>Y#g&@Kk&bvkq;c<$@aevZd_*5OZ@Sns?$v5`#y0t(4&*H=RK{4s{3W$loPf~t! z*wXY@>1}Y5f#*srf~#ce3uX+V_|$)XKY|!zsvSd{1FSc}J-VYiSp_C5%h=tVs?CyL z)eyLLvgX2~AeV1uGTFeDQ=rKUfg704*WB>Gn0Nyl>UNtij2RyP)W_0Tb7uqR*`0-4 zup+&!tERv43JBzU4wi{q&c)VsRq7 zP1&8BtU#Jy+DQ+FDS!;!lN}1K)vghBK4J9^>j%CJJDT4|obO|h3b2poWM1~Gex_Gd z%!g7UBoaH01_PQlxXb+m5qFBJz1BZ-xF3n zbKsW12!%m^0QXM%5PyG%4Chdhw{8*-5{1Dif(hjhAxa^ga8lMCP1tDPr!N^3dm=)4 z#%6?~Dv$qt>HQ%j@(Y%aCu!*gcpF^dO4En&{{qjTb)wYu2kpOCvd`ubu2}T~ei;jO znK>_m`yk+!n`9{w#0Vo6^z`fmYt$OSA71Divov^1+9P)c4f@|P_5QFqI|QC?QR(pi eKNra#FukKT*xg)2V!(wE=$(YTc!{WC;Qs)1#!`v^ literal 0 HcmV?d00001 From 7cbf466123cad1ab726daf35b4f43b75d41cff75 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 16 Dec 2017 00:52:57 +0000 Subject: [PATCH 0157/2296] BlockStorage v3: volumetype get/list acc test --- .../blockstorage/v3/volumetypes_test.go | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 acceptance/openstack/blockstorage/v3/volumetypes_test.go diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go new file mode 100644 index 0000000000..d8f1f71951 --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -0,0 +1,46 @@ +// +build acceptance blockstorage + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" +) + +func TestVolumeTypesList(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + listOpts := volumetypes.ListOpts{ + Sort: "name:asc", + Limit: 1, + } + + allPages, err := volumetypes.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve volumetypes: %v", err) + } + + allVolumes, err := volumetypes.ExtractVolumeTypes(allPages) + if err != nil { + t.Fatalf("Unable to extract volumetypes: %v", err) + } + + for _, volume := range allVolumes { + tools.PrintResource(t, volume) + } + + if len(allVolumes) > 0 { + vt, err := volumetypes.Get(client, allVolumes[0].ID).Extract() + if err != nil { + t.Fatalf("Error retrieving volume type: %v", err) + } + + tools.PrintResource(t, vt) + } +} From aed60e9f1b654776148a7ca862c4d84ae57488fb Mon Sep 17 00:00:00 2001 From: TommyLike Date: Tue, 19 Dec 2017 18:02:04 +0800 Subject: [PATCH 0158/2296] Add basic CRUD acceptance in volume V3 --- .../openstack/blockstorage/v3/blockstorage.go | 51 ++++++++++++++++ acceptance/openstack/blockstorage/v3/pkg.go | 3 + .../openstack/blockstorage/v3/volumes_test.go | 61 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 acceptance/openstack/blockstorage/v3/blockstorage.go create mode 100644 acceptance/openstack/blockstorage/v3/pkg.go create mode 100644 acceptance/openstack/blockstorage/v3/volumes_test.go diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go new file mode 100644 index 0000000000..89311cca0b --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -0,0 +1,51 @@ +// Package v3 contains common functions for creating block storage based +// resources for use in acceptance tests. See the `*_test.go` files for +// example usages. +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" +) + +// CreateVolume will create a volume with a random name and size of 1GB. An +// error will be returned if the volume was unable to be created. +func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { + if testing.Short() { + t.Skip("Skipping test that requires volume creation in short mode.") + } + + volumeName := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create volume: %s", volumeName) + + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + + volume, err := volumes.Create(client, createOpts).Extract() + if err != nil { + return volume, err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return volume, err + } + + return volume, nil +} + +// DeleteVolume will delete a volume. A fatal error will occur if the volume +// failed to be deleted. This works best when used as a deferred function. +func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { + err := volumes.Delete(client, volume.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) + } + + t.Logf("Deleted volume: %s", volume.ID) +} diff --git a/acceptance/openstack/blockstorage/v3/pkg.go b/acceptance/openstack/blockstorage/v3/pkg.go new file mode 100644 index 0000000000..b86245c710 --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/pkg.go @@ -0,0 +1,3 @@ +// The v3 package contains acceptance tests for the OpenStack Cinder V3 service. + +package v3 diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go new file mode 100644 index 0000000000..fa99f8d2d4 --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -0,0 +1,61 @@ +// +build acceptance blockstorage + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" +) + +func TestVolumesList(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve volumes: %v", err) + } + + allVolumes, err := volumes.ExtractVolumes(allPages) + if err != nil { + t.Fatalf("Unable to extract volumes: %v", err) + } + + for _, volume := range allVolumes { + tools.PrintResource(t, volume) + } + + if len(allVolumes) > 0 { + vt, err := volumes.Get(client, allVolumes[0].ID).Extract() + if err != nil { + t.Fatalf("Error retrieving volume: %v", err) + } + + tools.PrintResource(t, vt) + } +} + +func TestVolumesCreateDelete(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + if err != nil { + t.Fatalf("Unable to create blockstorage client: %v", err) + } + + volume, err := CreateVolume(t, client) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + defer DeleteVolume(t, client, volume) + + newVolume, err := volumes.Get(client, volume.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve volume: %v", err) + } + + tools.PrintResource(t, newVolume) +} From 22c7abce5bb32e9ef9fd51d94b894b741c07f64e Mon Sep 17 00:00:00 2001 From: Hu Sheng Date: Wed, 20 Dec 2017 02:05:57 +0800 Subject: [PATCH 0159/2296] Add CREATE support in V3 volume types (#654) * Add basic CRUD support in volume v3 types Add Create/Delete/List/Update/Get support for volume type in volume V3. * Support volume type create in V3 Add create API in V3's volume type resource --- .../blockstorage/v3/volumetypes_test.go | 21 ++++++++++ openstack/blockstorage/v3/volumetypes/doc.go | 12 ++++++ .../blockstorage/v3/volumetypes/requests.go | 41 +++++++++++++++++++ .../blockstorage/v3/volumetypes/results.go | 5 +++ .../v3/volumetypes/testing/fixtures.go | 40 ++++++++++++++++++ .../v3/volumetypes/testing/requests_test.go | 18 ++++++++ openstack/blockstorage/v3/volumetypes/urls.go | 4 ++ 7 files changed, 141 insertions(+) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index d8f1f71951..b69e431721 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -44,3 +44,24 @@ func TestVolumeTypesList(t *testing.T) { tools.PrintResource(t, vt) } } + +func TestVolumeTypesCreate(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + createOpts := volumetypes.CreateOpts{ + Name: "create_from_gophercloud", + PublicAccess: true, + ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, + Description: "create_from_gophercloud", + } + + vt, err := volumetypes.Create(client, createOpts).Extract() + if err != nil { + t.Fatalf("Unable to create volumetype: %v", err) + } + + tools.PrintResource(t, vt) +} diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index f1b6fc7803..2425ee3a78 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -25,6 +25,18 @@ Example to show a Volume Type panic(err) } fmt.Println(volumeType) + +Example to create a Volume Type + + volumeType, err := volumetypes.Create(client, volumetypes.CreateOpts{ + Name:"volume_type_001", + IsPublic:true, + Description:"description_001", + }).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 468fd864dc..fc3b6ea98e 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -5,6 +5,47 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToVolumeTypeCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Volume Type. This object is passed to +// the volumetypes.Create function. For more information about these parameters, +// see the Volume Type object. +type CreateOpts struct { + // The name of the volume type + Name string `json:"name" required:"true"` + // The volume type description + Description string `json:"description,omitempty"` + // the ID of the existing volume snapshot + PublicAccess bool `json:"os-volume-type-access:is_public"` + // Extra spec key-value pairs defined by the user. + ExtraSpecs map[string]string `json:"extra_specs"` +} + +// ToVolumeTypeCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "volume_type") +} + +// Create will create a new Volume Type based on the values in CreateOpts. To extract +// the Volume Type object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToVolumeTypeCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // Get retrieves the Volume Type with the provided ID. To extract the Volume Type object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go index eb2a8e6939..16228495f9 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -77,3 +77,8 @@ func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { type GetResult struct { commonResult } + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go index 8fa59ad338..88eede1658 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go @@ -87,3 +87,43 @@ func MockGetResponse(t *testing.T) { `) }) } + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/types", 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, ` +{ + "volume_type": { + "name": "test_type", + "os-volume-type-access:is_public": true, + "description": "test_type_desc", + "extra_specs": { + "capabilities": "gpu" + } + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "volume_type": { + "name": "test_type", + "extra_specs": {}, + "is_public": true, + "os-volume-type-access:is_public": true, + "id": "6d0ff92a-0007-4780-9ece-acfe5876966a", + "description": "test_type_desc", + "extra_specs": { + "capabilities": "gpu" + } + } +} + `) + }) +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index e205d90255..f40bef8deb 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -63,3 +63,21 @@ func TestGet(t *testing.T) { th.AssertEquals(t, v.QosSpecID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, v.PublicAccess, true) } + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := &volumetypes.CreateOpts{Name: "test_type", PublicAccess: true, Description: "test_type_desc", ExtraSpecs: map[string]string{"capabilities": "gpu"}} + n, err := volumetypes.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Name, "test_type") + th.AssertEquals(t, n.Description, "test_type_desc") + th.AssertEquals(t, n.IsPublic, true) + th.AssertEquals(t, n.PublicAccess, true) + th.AssertEquals(t, n.ID, "6d0ff92a-0007-4780-9ece-acfe5876966a") + th.AssertEquals(t, n.ExtraSpecs["capabilities"], "gpu") +} diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go index 70ee229ea8..0e7eb41e63 100644 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -9,3 +9,7 @@ func listURL(c *gophercloud.ServiceClient) string { func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id) } + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("types") +} From 69194d93d337f6dd5e1f4bf359d29bd7a7f95afa Mon Sep 17 00:00:00 2001 From: TommyLike Date: Wed, 20 Dec 2017 08:40:34 +0800 Subject: [PATCH 0160/2296] Add basic CRUD acceptance testcases in snapshot V3 --- .../openstack/blockstorage/v3/blockstorage.go | 56 ++++++++++++++++ .../blockstorage/v3/snapshots_test.go | 66 +++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 acceptance/openstack/blockstorage/v3/snapshots_test.go diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index 89311cca0b..4fedc8b816 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" ) @@ -49,3 +50,58 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum t.Logf("Deleted volume: %s", volume.ID) } + +// CreateSnapshot will create a snapshot of the specified volume. +// Snapshot will be assigned a random name and description. +func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { + if testing.Short() { + t.Skip("Skipping test that requires snapshot creation in short mode.") + } + + snapshotName := tools.RandomString("ACPTTEST", 16) + snapshotDescription := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create snapshot: %s", snapshotName) + + createOpts := snapshots.CreateOpts{ + VolumeID: volume.ID, + Name: snapshotName, + Description: snapshotDescription, + } + + snapshot, err := snapshots.Create(client, createOpts).Extract() + if err != nil { + return snapshot, err + } + + err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + if err != nil { + return snapshot, err + } + + return snapshot, nil +} + +// DeleteSnapshot will delete a snapshot. A fatal error will occur if the +// snapshot failed to be deleted. +func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { + err := snapshots.Delete(client, snapshot.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) + } + + // Volumes can't be deleted until their snapshots have been, + // so block up to 120 seconds for the snapshot to delete. + err = gophercloud.WaitFor(120, func() (bool, error) { + _, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + return true, nil + } + + return false, nil + }) + if err != nil { + t.Fatalf("Error waiting for snapshot to delete: %v", err) + } + + t.Logf("Deleted snapshot: %s", snapshot.ID) +} diff --git a/acceptance/openstack/blockstorage/v3/snapshots_test.go b/acceptance/openstack/blockstorage/v3/snapshots_test.go new file mode 100644 index 0000000000..335e7f3b9a --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -0,0 +1,66 @@ +// +build acceptance blockstorage + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" +) + +func TestSnapshotsList(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve snapshots: %v", err) + } + + allSnapshots, err := snapshots.ExtractSnapshots(allPages) + if err != nil { + t.Fatalf("Unable to extract snapshots: %v", err) + } + + for _, snapshot := range allSnapshots { + tools.PrintResource(t, snapshot) + } + if len(allSnapshots) > 0 { + vt, err := snapshots.Get(client, allSnapshots[0].ID).Extract() + if err != nil { + t.Fatalf("Error retrieving snapshot: %v", err) + } + + tools.PrintResource(t, vt) + } +} + +func TestSnapshotsCreateDelete(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + volume, err := CreateVolume(t, client) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + defer DeleteVolume(t, client, volume) + + snapshot, err := CreateSnapshot(t, client, volume) + if err != nil { + t.Fatalf("Unable to create snapshot: %v", err) + } + defer DeleteSnapshot(t, client, snapshot) + + newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve snapshot: %v", err) + } + + tools.PrintResource(t, newSnapshot) +} From 1db0312eec7a54e708c34dd314dcb37fe61bc067 Mon Sep 17 00:00:00 2001 From: TommyLike Date: Wed, 20 Dec 2017 08:43:57 +0800 Subject: [PATCH 0161/2296] TrivialFix incorrect variable name --- acceptance/openstack/blockstorage/v3/volumetypes_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index b69e431721..99e57c78c3 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -26,16 +26,16 @@ func TestVolumeTypesList(t *testing.T) { t.Fatalf("Unable to retrieve volumetypes: %v", err) } - allVolumes, err := volumetypes.ExtractVolumeTypes(allPages) + allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) if err != nil { t.Fatalf("Unable to extract volumetypes: %v", err) } - for _, volume := range allVolumes { - tools.PrintResource(t, volume) + for _, vt := range allVolumeTypes { + tools.PrintResource(t, vt) } - if len(allVolumes) > 0 { + if len(allVolumeTypes) > 0 { vt, err := volumetypes.Get(client, allVolumes[0].ID).Extract() if err != nil { t.Fatalf("Error retrieving volume type: %v", err) From a5c718687f9222e37c96d8c0210bebd242f926b5 Mon Sep 17 00:00:00 2001 From: TommyLike Date: Tue, 19 Dec 2017 17:29:12 +0800 Subject: [PATCH 0162/2296] Support pagination in volume resources --- .../openstack/blockstorage/v3/volumes_test.go | 34 +++++++++++++------ openstack/blockstorage/v3/volumes/requests.go | 15 +++++++- openstack/blockstorage/v3/volumes/results.go | 13 ++++++- .../v3/volumes/testing/fixtures.go | 20 +++++++++-- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index fa99f8d2d4..2e349111d4 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/pagination" ) func TestVolumesList(t *testing.T) { @@ -16,27 +17,38 @@ func TestVolumesList(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() + volume1, err := CreateVolume(t, client) if err != nil { - t.Fatalf("Unable to retrieve volumes: %v", err) + t.Fatalf("Unable to create volume: %v", err) } + defer DeleteVolume(t, client, volume1) - allVolumes, err := volumes.ExtractVolumes(allPages) + volume2, err := CreateVolume(t, client) if err != nil { - t.Fatalf("Unable to extract volumes: %v", err) + t.Fatalf("Unable to create volume: %v", err) } + defer DeleteVolume(t, client, volume2) - for _, volume := range allVolumes { - tools.PrintResource(t, volume) - } + pages := 0 + err = volumes.List(client, volumes.ListOpts{Limit: 1}).EachPage(func(page pagination.Page) (bool, error) { + pages++ - if len(allVolumes) > 0 { - vt, err := volumes.Get(client, allVolumes[0].ID).Extract() + actual, err := volumes.ExtractVolumes(page) if err != nil { - t.Fatalf("Error retrieving volume: %v", err) + t.Fatalf("Unable to extract volumes: %v", err) } - tools.PrintResource(t, vt) + if len(actual) != 1 { + t.Fatalf("Expected 1 volume, got %d", len(actual)) + } + + tools.PrintResource(t, actual[0]) + + return true, nil + }) + + if pages != 2 { + t.Fatalf("Expected 2 pages, saw %d", pages) } } diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 2ec10ad55e..43727409dd 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -98,6 +98,19 @@ type ListOpts struct { // TenantID will filter by a specific tenant/project ID. // Setting AllTenants is required for this. TenantID string `q:"project_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` } // ToVolumeListQuery formats a ListOpts into a query string. @@ -118,7 +131,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumePage{pagination.SinglePageBase(r)} + return VolumePage{pagination.LinkedPageBase{PageResult: r}} }) } diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index 5ebe36a338..87f71262c1 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -101,7 +101,7 @@ func (r *Volume) UnmarshalJSON(b []byte) error { // VolumePage is a pagination.pager that is returned from a call to the List function. type VolumePage struct { - pagination.SinglePageBase + pagination.LinkedPageBase } // IsEmpty returns true if a ListResult contains no Volumes. @@ -110,6 +110,17 @@ func (r VolumePage) IsEmpty() (bool, error) { return len(volumes) == 0, err } +func (page VolumePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"volumes_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. func ExtractVolumes(r pagination.Page) ([]Volume, error) { var s []Volume diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures.go b/openstack/blockstorage/v3/volumes/testing/fixtures.go index 44d2ca383c..74efb1a256 100644 --- a/openstack/blockstorage/v3/volumes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumes/testing/fixtures.go @@ -17,7 +17,11 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` { "volumes": [ { @@ -80,9 +84,19 @@ func MockListResponse(t *testing.T) { "status": "available", "description": null } - ] + ], + "volumes_links": [ + { + "href": "%s/volumes/detail?marker=1", + "rel": "next" + }] } - `) + `, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"volumes": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } }) } From 2997913a2a5a166d6c814d2670ee91980970a4b5 Mon Sep 17 00:00:00 2001 From: TommyLike Date: Tue, 19 Dec 2017 11:36:38 +0800 Subject: [PATCH 0163/2296] Add pagination support in snapshots --- .../blockstorage/v3/snapshots_test.go | 49 +++++++++++++++---- .../blockstorage/v3/snapshots/requests.go | 15 +++++- .../blockstorage/v3/snapshots/results.go | 13 ++++- .../v3/snapshots/testing/fixtures.go | 20 ++++++-- 4 files changed, 82 insertions(+), 15 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/snapshots_test.go b/acceptance/openstack/blockstorage/v3/snapshots_test.go index 335e7f3b9a..f8fdc1f0f6 100644 --- a/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/pagination" ) func TestSnapshotsList(t *testing.T) { @@ -16,26 +17,54 @@ func TestSnapshotsList(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + volume1, err := CreateVolume(t, client) if err != nil { - t.Fatalf("Unable to retrieve snapshots: %v", err) + t.Fatalf("Unable to create volume: %v", err) } - allSnapshots, err := snapshots.ExtractSnapshots(allPages) + defer DeleteVolume(t, client, volume1) + + snapshot1, err := CreateSnapshot(t, client, volume1) if err != nil { - t.Fatalf("Unable to extract snapshots: %v", err) + t.Fatalf("Unable to create snapshot: %v", err) } - for _, snapshot := range allSnapshots { - tools.PrintResource(t, snapshot) + defer DeleteSnapshot(t, client, snapshot1) + + volume2, err := CreateVolume(t, client) + if err != nil { + t.Fatalf("Unable to create volume: %v", err) + } + + defer DeleteVolume(t, client, volume2) + + snapshot2, err := CreateSnapshot(t, client, volume2) + if err != nil { + t.Fatalf("Unable to create snapshot: %v", err) } - if len(allSnapshots) > 0 { - vt, err := snapshots.Get(client, allSnapshots[0].ID).Extract() + + defer DeleteSnapshot(t, client, snapshot2) + + pages := 0 + err = snapshots.List(client, snapshots.ListOpts{Limit: 1}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := snapshots.ExtractSnapshots(page) if err != nil { - t.Fatalf("Error retrieving snapshot: %v", err) + t.Fatalf("Unable to extract snapshots: %v", err) } - tools.PrintResource(t, vt) + if len(actual) != 1 { + t.Fatalf("Expected 1 snapshot, got %d", len(actual)) + } + + tools.PrintResource(t, actual[0]) + + return true, nil + }) + + if pages != 2 { + t.Fatalf("Expected 2 pages, saw %d", pages) } } diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 65679bf2f5..7df688ede8 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -78,6 +78,19 @@ type ListOpts struct { // VolumeID will filter by a specified volume ID. VolumeID string `q:"volume_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` } // ToSnapshotListQuery formats a ListOpts into a query string. @@ -98,7 +111,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return SnapshotPage{pagination.SinglePageBase(r)} + return SnapshotPage{pagination.LinkedPageBase{PageResult: r}} }) } diff --git a/openstack/blockstorage/v3/snapshots/results.go b/openstack/blockstorage/v3/snapshots/results.go index 5f03ff925a..1ffae8f61f 100644 --- a/openstack/blockstorage/v3/snapshots/results.go +++ b/openstack/blockstorage/v3/snapshots/results.go @@ -55,7 +55,7 @@ type DeleteResult struct { // SnapshotPage is a pagination.Pager that is returned from a call to the List function. type SnapshotPage struct { - pagination.SinglePageBase + pagination.LinkedPageBase } // UnmarshalJSON converts our JSON API response into our snapshot struct @@ -84,6 +84,17 @@ func (r SnapshotPage) IsEmpty() (bool, error) { return len(volumes) == 0, err } +func (page SnapshotPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"snapshots_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + // ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { var s struct { diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures.go b/openstack/blockstorage/v3/snapshots/testing/fixtures.go index 9638fa5007..8255207cc2 100644 --- a/openstack/blockstorage/v3/snapshots/testing/fixtures.go +++ b/openstack/blockstorage/v3/snapshots/testing/fixtures.go @@ -17,7 +17,11 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` { "snapshots": [ { @@ -38,9 +42,19 @@ func MockListResponse(t *testing.T) { "size": 25, "created_at": "2017-05-30T03:35:03.000000" } - ] + ], + "snapshots_links": [ + { + "href": "%s/snapshots?marker=1", + "rel": "next" + }] } - `) + `, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"snapshots": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } }) } From a879b375ebbc99b71ae3233a60ef355e188d3761 Mon Sep 17 00:00:00 2001 From: TommyLike Date: Wed, 20 Dec 2017 11:43:50 +0800 Subject: [PATCH 0164/2296] Fix incorrect variable name --- acceptance/openstack/blockstorage/v3/volumetypes_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 99e57c78c3..8960cce47d 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -36,7 +36,7 @@ func TestVolumeTypesList(t *testing.T) { } if len(allVolumeTypes) > 0 { - vt, err := volumetypes.Get(client, allVolumes[0].ID).Extract() + vt, err := volumetypes.Get(client, allVolumeTypes[0].ID).Extract() if err != nil { t.Fatalf("Error retrieving volume type: %v", err) } From cf81d92c5aa5aa965c729e10fec2b65d13194320 Mon Sep 17 00:00:00 2001 From: Hu Sheng Date: Thu, 21 Dec 2017 11:49:05 +0800 Subject: [PATCH 0165/2296] Add DELETE support in V3 volume types (#655) * Add basic CRUD support in volume v3 types Add Create/Delete/List/Update/Get support for volume type in volume V3. * Support volume type delete in V3 Support volume type delete in V3 --- .../openstack/blockstorage/v3/volumetypes_test.go | 4 +++- openstack/blockstorage/v3/volumetypes/doc.go | 8 ++++++++ openstack/blockstorage/v3/volumetypes/requests.go | 6 ++++++ openstack/blockstorage/v3/volumetypes/results.go | 5 +++++ .../blockstorage/v3/volumetypes/testing/fixtures.go | 8 ++++++++ .../v3/volumetypes/testing/requests_test.go | 10 ++++++++++ openstack/blockstorage/v3/volumetypes/urls.go | 4 ++++ 7 files changed, 44 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 8960cce47d..e9145ebe1f 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -45,7 +45,7 @@ func TestVolumeTypesList(t *testing.T) { } } -func TestVolumeTypesCreate(t *testing.T) { +func TestVolumeTypesCreateDestroy(t *testing.T) { client, err := clients.NewBlockStorageV3Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) @@ -64,4 +64,6 @@ func TestVolumeTypesCreate(t *testing.T) { } tools.PrintResource(t, vt) + + defer volumetypes.Delete(client, vt.ID) } diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index 2425ee3a78..da906207b9 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -37,6 +37,14 @@ Example to create a Volume Type panic(err) } fmt.Println(volumeType) + +Example to delete a Volume TYpe + + typeID := "0fe36e73809d46aeae6705c39077b1b3" + err := volumetypes.Delete(client, typeID).ExtractErr() + if err != nil{ + panic(err) + } */ package volumetypes diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index fc3b6ea98e..fb48c8130b 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -46,6 +46,12 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// Delete will delete the existing Volume Type with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + // Get retrieves the Volume Type with the provided ID. To extract the Volume Type object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go index 16228495f9..b254e3fbdd 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -82,3 +82,8 @@ type GetResult struct { type CreateResult struct { commonResult } + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go index 88eede1658..c705483c97 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go @@ -127,3 +127,11 @@ func MockCreateResponse(t *testing.T) { `) }) } + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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) + }) +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index f40bef8deb..2753f8798c 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -81,3 +81,13 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, n.ID, "6d0ff92a-0007-4780-9ece-acfe5876966a") th.AssertEquals(t, n.ExtraSpecs["capabilities"], "gpu") } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go index 0e7eb41e63..664b40e471 100644 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -13,3 +13,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types") } + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("types", id) +} From 7b1b87753c31d4900587840774a019bbfa770698 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 21 Dec 2017 15:14:02 -0700 Subject: [PATCH 0166/2296] Compute v2: Flavor Access Add (#687) * Compute v2: Flavor Access Add * updating acceptance test to use a real project --- .../openstack/compute/v2/flavors_test.go | 39 +++++++++++++++ openstack/compute/v2/flavors/doc.go | 13 +++++ openstack/compute/v2/flavors/requests.go | 30 ++++++++++++ openstack/compute/v2/flavors/results.go | 20 ++++++++ .../v2/flavors/testing/requests_test.go | 49 +++++++++++++++++++ openstack/compute/v2/flavors/urls.go | 4 ++ 6 files changed, 155 insertions(+) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index 0fd0158d9e..ce5a5a0105 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -8,6 +8,8 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + + identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" ) func TestFlavorsList(t *testing.T) { @@ -114,3 +116,40 @@ func TestFlavorAccessesList(t *testing.T) { tools.PrintResource(t, access) } } + +func TestFlavorAccessCRUD(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + identityClient, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatal("Unable to create identity client: %v", err) + } + + project, err := identity.CreateProject(t, identityClient, nil) + if err != nil { + t.Fatal("Unable to create project: %v", err) + } + defer identity.DeleteProject(t, identityClient, project.ID) + + flavor, err := CreatePrivateFlavor(t, client) + if err != nil { + t.Fatalf("Unable to create flavor: %v", err) + } + defer DeleteFlavor(t, client, flavor) + + addAccessOpts := flavors.AddAccessOpts{ + Tenant: project.ID, + } + + accessList, err := flavors.AddAccess(client, flavor.ID, addAccessOpts).Extract() + if err != nil { + t.Fatalf("Unable to add access to flavor: %v", err) + } + + for _, access := range accessList { + tools.PrintResource(t, access) + } +} diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index a7bc15c3e5..79ef8c0ff7 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -59,5 +59,18 @@ Example to List Flavor Access for _, access := range allAccesses { fmt.Printf("%+v", access) } + +Example to Grant Access to a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + accessOpts := flavors.AddAccessOpts{ + Tenant: "15153a0979884b59b0592248ef947921", + } + + accessList, err := flavors.AddAccess(computeClient, flavor.ID, accessOpts).Extract() + if err != nil { + panic(err) + } */ package flavors diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 6eb3678b2b..5b0bd8ccfa 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -162,6 +162,36 @@ func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager }) } +// AddAccessOptsBuilder allows extensions to add additional parameters to the +// AddAccess requests. +type AddAccessOptsBuilder interface { + ToAddAccessMap() (map[string]interface{}, error) +} + +// AddAccessOpts represents options for adding access to a flavor. +type AddAccessOpts struct { + // Tenant is the project/tenant ID to grant access. + Tenant string `json:"tenant"` +} + +// ToAddAccessMap constructs a request body from AddAccessOpts. +func (opts AddAccessOpts) ToAddAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "addTenantAccess") +} + +// AddAccess grants a tenant/project access to a flavor. +func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { + b, err := opts.ToAddAccessMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // IDFromName is a convienience function that returns a flavor's ID given its // name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index fb5c335b8e..66d40361a0 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -154,6 +154,26 @@ func ExtractAccesses(r pagination.Page) ([]FlavorAccess, error) { return s.FlavorAccesses, err } +type accessResult struct { + gophercloud.Result +} + +// AddAccessResult is the response of an AddAccess operations. Call its +// Extract method to interpret it as a slice of FlavorAccess. +type AddAccessResult struct { + accessResult +} + +// Extract provides access to the result of an access create or delete. +// The result will be all accesses that the flavor has. +func (r accessResult) Extract() ([]FlavorAccess, error) { + var s struct { + FlavorAccesses []FlavorAccess `json:"flavor_access"` + } + err := r.ExtractInto(&s) + return s.FlavorAccesses, err +} + // FlavorAccess represents an ACL of tenant access to a specific Flavor. type FlavorAccess struct { // FlavorID is the unique ID of the flavor. diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 9dfdbeaf5b..f08a078d93 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -250,3 +250,52 @@ func TestFlavorAccessesList(t *testing.T) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestFlavorAccessAdd(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/flavors/12345678/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, "accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "addTenantAccess": { + "tenant": "2f954bcf047c4ee9b09a37d49ae6db54" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` + { + "flavor_access": [ + { + "flavor_id": "12345678", + "tenant_id": "2f954bcf047c4ee9b09a37d49ae6db54" + } + ] + } + `) + }) + + expected := []flavors.FlavorAccess{ + flavors.FlavorAccess{ + FlavorID: "12345678", + TenantID: "2f954bcf047c4ee9b09a37d49ae6db54", + }, + } + + addAccessOpts := flavors.AddAccessOpts{ + Tenant: "2f954bcf047c4ee9b09a37d49ae6db54", + } + + actual, err := flavors.AddAccess(fake.ServiceClient(), "12345678", addAccessOpts).Extract() + th.AssertNoErr(t, err) + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go index 04d33bf127..8f875e489a 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -23,3 +23,7 @@ func deleteURL(client *gophercloud.ServiceClient, id string) string { func accessURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id, "os-flavor-access") } + +func accessActionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id, "action") +} From c2cafb46bb409768f420742757949fd05fb1d704 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 21 Dec 2017 16:20:40 -0700 Subject: [PATCH 0167/2296] Flavor Extra Specs: List / Get (#686) --- .../openstack/compute/v2/flavors_test.go | 20 ++++++ openstack/compute/v2/flavors/doc.go | 10 +++ openstack/compute/v2/flavors/requests.go | 11 ++++ openstack/compute/v2/flavors/results.go | 41 ++++++++++++ .../compute/v2/flavors/testing/fixtures.go | 62 +++++++++++++++++++ .../v2/flavors/testing/requests_test.go | 22 +++++++ openstack/compute/v2/flavors/urls.go | 8 +++ 7 files changed, 174 insertions(+) create mode 100644 openstack/compute/v2/flavors/testing/fixtures.go diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index ce5a5a0105..138dab0a8a 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -153,3 +153,23 @@ func TestFlavorAccessCRUD(t *testing.T) { tools.PrintResource(t, access) } } + +func TestFlavorExtraSpecs(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + flavor, err := CreatePrivateFlavor(t, client) + if err != nil { + t.Fatalf("Unable to create flavor: %v", err) + } + defer DeleteFlavor(t, client, flavor) + + allExtraSpecs, err := flavors.ListExtraSpecs(client, flavor.ID).Extract() + if err != nil { + t.Fatalf("Unable to get flavor extra_specs: %v", err) + } + + tools.PrintResource(t, allExtraSpecs) +} diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 79ef8c0ff7..a8d80377a1 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -72,5 +72,15 @@ Example to Grant Access to a Flavor if err != nil { panic(err) } + +Example to Get Extra Specs for a Flavor + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + extraSpecs, err := flavors.ListExtraSpecs(computeClient, flavorID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", extraSpecs) */ package flavors diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 5b0bd8ccfa..fb668cd910 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -230,3 +230,14 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) return "", err } } + +// ExtraSpecs requests all the extra-specs for the given flavor ID. +func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { + _, r.Err = client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) + return +} + +func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { + _, r.Err = client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) + return +} diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 66d40361a0..dbfa96bf85 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -182,3 +182,44 @@ type FlavorAccess struct { // TenantID is the unique ID of the tenant. TenantID string `json:"tenant_id"` } + +// Extract interprets any extraSpecsResult as ExtraSpecs, if possible. +func (r extraSpecsResult) Extract() (map[string]string, error) { + var s struct { + ExtraSpecs map[string]string `json:"extra_specs"` + } + err := r.ExtractInto(&s) + return s.ExtraSpecs, err +} + +// extraSpecsResult 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 extraSpecsResult struct { + gophercloud.Result +} + +// ListExtraSpecsResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. +type ListExtraSpecsResult struct { + extraSpecsResult +} + +// extraSpecResult contains the result of a call for individual a single +// key-value pair. +type extraSpecResult struct { + gophercloud.Result +} + +// GetExtraSpecResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. +type GetExtraSpecResult struct { + extraSpecResult +} + +// Extract interprets any extraSpecResult as an ExtraSpec, if possible. +func (r extraSpecResult) Extract() (map[string]string, error) { + var s map[string]string + err := r.ExtractInto(&s) + return s, err +} diff --git a/openstack/compute/v2/flavors/testing/fixtures.go b/openstack/compute/v2/flavors/testing/fixtures.go new file mode 100644 index 0000000000..057857a80a --- /dev/null +++ b/openstack/compute/v2/flavors/testing/fixtures.go @@ -0,0 +1,62 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ExtraSpecsGetBody provides a GET result of the extra_specs for a flavor +const ExtraSpecsGetBody = ` +{ + "extra_specs" : { + "hw:cpu_policy": "CPU-POLICY", + "hw:cpu_thread_policy": "CPU-THREAD-POLICY" + } +} +` + +// ExtraSpecGetBody provides a GET result of a particular extra_spec for a flavor +const GetExtraSpecBody = ` +{ + "hw:cpu_policy": "CPU-POLICY" +} +` + +// ExtraSpecs is the expected extra_specs returned from GET on a flavor's extra_specs +var ExtraSpecs = map[string]string{ + "hw:cpu_policy": "CPU-POLICY", + "hw:cpu_thread_policy": "CPU-THREAD-POLICY", +} + +// ExtraSpec is the expected extra_spec returned from GET on a flavor's extra_specs +var ExtraSpec = map[string]string{ + "hw:cpu_policy": "CPU-POLICY", +} + +func HandleExtraSpecsListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/flavors/1/os-extra_specs", 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().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExtraSpecsGetBody) + }) +} + +func HandleExtraSpecGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/flavors/1/os-extra_specs/hw:cpu_policy", 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().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetExtraSpecBody) + }) +} diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index f08a078d93..37be42ed5d 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -299,3 +299,25 @@ func TestFlavorAccessAdd(t *testing.T) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestFlavorExtraSpecsList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecsListSuccessfully(t) + + expected := ExtraSpecs + actual, err := flavors.ListExtraSpecs(fake.ServiceClient(), "1").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestFlavorExtraSpecGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecGetSuccessfully(t) + + expected := ExtraSpec + actual, err := flavors.GetExtraSpec(fake.ServiceClient(), "1", "hw:cpu_policy").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go index 8f875e489a..0756c08785 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -27,3 +27,11 @@ func accessURL(client *gophercloud.ServiceClient, id string) string { func accessActionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id, "action") } + +func extraSpecsListURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id, "os-extra_specs") +} + +func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("flavors", id, "os-extra_specs", key) +} From be3fd7845c1928cbc5bbe289f2e39f5dec2e7278 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Thu, 21 Dec 2017 13:12:16 -0700 Subject: [PATCH 0168/2296] Flavor Extra Specs Create --- .../openstack/compute/v2/flavors_test.go | 21 +++++++- openstack/compute/v2/flavors/doc.go | 17 +++++++ openstack/compute/v2/flavors/requests.go | 50 +++++++++++++++---- openstack/compute/v2/flavors/results.go | 6 +++ .../compute/v2/flavors/testing/fixtures.go | 18 +++++++ .../v2/flavors/testing/requests_test.go | 15 ++++++ openstack/compute/v2/flavors/urls.go | 4 ++ 7 files changed, 118 insertions(+), 13 deletions(-) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index 138dab0a8a..bb6c5c41d9 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -154,7 +154,7 @@ func TestFlavorAccessCRUD(t *testing.T) { } } -func TestFlavorExtraSpecs(t *testing.T) { +func TestFlavorExtraSpecsCRUD(t *testing.T) { client, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) @@ -166,10 +166,27 @@ func TestFlavorExtraSpecs(t *testing.T) { } defer DeleteFlavor(t, client, flavor) + createOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_policy": "CPU-POLICY", + "hw:cpu_thread_policy": "CPU-THREAD-POLICY", + } + createdExtraSpecs, err := flavors.CreateExtraSpecs(client, flavor.ID, createOpts).Extract() + if err != nil { + t.Fatalf("Unable to create flavor extra_specs: %v", err) + } + tools.PrintResource(t, createdExtraSpecs) + allExtraSpecs, err := flavors.ListExtraSpecs(client, flavor.ID).Extract() if err != nil { t.Fatalf("Unable to get flavor extra_specs: %v", err) } - tools.PrintResource(t, allExtraSpecs) + + for key, _ := range allExtraSpecs { + spec, err := flavors.GetExtraSpec(client, flavor.ID, key).Extract() + if err != nil { + t.Fatalf("Unable to get flavor extra spec: %v", err) + } + tools.PrintResource(t, spec) + } } diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index a8d80377a1..867d53a819 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -73,7 +73,23 @@ Example to Grant Access to a Flavor panic(err) } +Example to Create Extra Specs for a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + createOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_policy": "CPU-POLICY", + "hw:cpu_thread_policy": "CPU-THREAD-POLICY", + } + createdExtraSpecs, err := flavors.CreateExtraSpecs(computeClient, flavorID, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", createdExtraSpecs) + Example to Get Extra Specs for a Flavor + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" extraSpecs, err := flavors.ListExtraSpecs(computeClient, flavorID).Extract() @@ -82,5 +98,6 @@ Example to Get Extra Specs for a Flavor } fmt.Printf("%+v", extraSpecs) + */ package flavors diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index fb668cd910..965d271d1d 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -192,6 +192,45 @@ func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsB return } +// ExtraSpecs requests all the extra-specs for the given flavor ID. +func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { + _, r.Err = client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) + return +} + +func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { + _, r.Err = client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) + return +} + +// CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the +// CreateExtraSpecs requests. +type CreateExtraSpecsOptsBuilder interface { + ToExtraSpecsCreateMap() (map[string]interface{}, error) +} + +// ExtraSpecsOpts is a map that contains key-value pairs. +type ExtraSpecsOpts map[string]string + +// ToExtraSpecsCreateMap assembles a body for a Create request based on the +// contents of a ExtraSpecsOpts +func (opts ExtraSpecsOpts) ToExtraSpecsCreateMap() (map[string]interface{}, error) { + return map[string]interface{}{"extra_specs": opts}, nil +} + +// CreateExtraSpecs will create or update the extra-specs key-value pairs for the specified Flavor +func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { + b, err := opts.ToExtraSpecsCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // IDFromName is a convienience function that returns a flavor's ID given its // name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { @@ -230,14 +269,3 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) return "", err } } - -// ExtraSpecs requests all the extra-specs for the given flavor ID. -func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { - _, r.Err = client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) - return -} - -func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { - _, r.Err = client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) - return -} diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index dbfa96bf85..4451be38c9 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -205,6 +205,12 @@ type ListExtraSpecsResult struct { extraSpecsResult } +// CreateExtraSpecResult contains the result of a Create operation. Call its +// Extract method to interpret it as a map[string]interface. +type CreateExtraSpecsResult struct { + extraSpecsResult +} + // extraSpecResult contains the result of a call for individual a single // key-value pair. type extraSpecResult struct { diff --git a/openstack/compute/v2/flavors/testing/fixtures.go b/openstack/compute/v2/flavors/testing/fixtures.go index 057857a80a..536a7c11d4 100644 --- a/openstack/compute/v2/flavors/testing/fixtures.go +++ b/openstack/compute/v2/flavors/testing/fixtures.go @@ -60,3 +60,21 @@ func HandleExtraSpecGetSuccessfully(t *testing.T) { fmt.Fprintf(w, GetExtraSpecBody) }) } + +func HandleExtraSpecsCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/flavors/1/os-extra_specs", 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.TestJSONRequest(t, r, `{ + "extra_specs": { + "hw:cpu_policy": "CPU-POLICY", + "hw:cpu_thread_policy": "CPU-THREAD-POLICY" + } + }`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExtraSpecsGetBody) + }) +} diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 37be42ed5d..ef40e5973f 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -321,3 +321,18 @@ func TestFlavorExtraSpecGet(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } + +func TestFlavorExtraSpecsCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecsCreateSuccessfully(t) + + createOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_policy": "CPU-POLICY", + "hw:cpu_thread_policy": "CPU-THREAD-POLICY", + } + expected := ExtraSpecs + actual, err := flavors.CreateExtraSpecs(fake.ServiceClient(), "1", createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go index 0756c08785..b74f81625d 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -35,3 +35,7 @@ func extraSpecsListURL(client *gophercloud.ServiceClient, id string) string { func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string { return client.ServiceURL("flavors", id, "os-extra_specs", key) } + +func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id, "os-extra_specs") +} From 614da04dc6c502d81c906a55ddaf1a89425e2d86 Mon Sep 17 00:00:00 2001 From: Hu Sheng Date: Fri, 29 Dec 2017 03:46:32 +0800 Subject: [PATCH 0169/2296] Add UPDATE support in V3 volume types (#656) * Add basic CRUD support in volume v3 types Add Create/Delete/List/Update/Get support for volume type in volume V3. * Add Update support in V3 volume type Add Update support in V3 volume type --- .../blockstorage/v3/volumetypes_test.go | 13 ++++++- openstack/blockstorage/v3/volumetypes/doc.go | 19 ++++++++-- .../blockstorage/v3/volumetypes/requests.go | 35 +++++++++++++++++++ .../blockstorage/v3/volumetypes/results.go | 5 +++ .../v3/volumetypes/testing/fixtures.go | 17 +++++++++ .../v3/volumetypes/testing/requests_test.go | 14 ++++++++ openstack/blockstorage/v3/volumetypes/urls.go | 4 +++ 7 files changed, 103 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index e9145ebe1f..3a001fccb2 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumeTypesList(t *testing.T) { @@ -45,7 +46,7 @@ func TestVolumeTypesList(t *testing.T) { } } -func TestVolumeTypesCreateDestroy(t *testing.T) { +func TestVolumeTypesCRUD(t *testing.T) { client, err := clients.NewBlockStorageV3Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) @@ -66,4 +67,14 @@ func TestVolumeTypesCreateDestroy(t *testing.T) { tools.PrintResource(t, vt) defer volumetypes.Delete(client, vt.ID) + + var isPublic = false + + newVT, err := volumetypes.Update(client, vt.ID, volumetypes.UpdateOpts{ + Name: "updated_volume_type", + IsPublic: &isPublic, + }).Extract() + th.AssertEquals(t, "updated_volume_type", newVT.Name) + + th.AssertEquals(t, false, newVT.IsPublic) } diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index da906207b9..8b2769cb7c 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -19,7 +19,7 @@ Example to list Volume Types Example to show a Volume Type - typeID := "0fe36e73809d46aeae6705c39077b1b3" + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" volumeType, err := volumetypes.Get(client, typeID).Extract() if err != nil{ panic(err) @@ -38,13 +38,26 @@ Example to create a Volume Type } fmt.Println(volumeType) -Example to delete a Volume TYpe +Example to delete a Volume Type - typeID := "0fe36e73809d46aeae6705c39077b1b3" + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" err := volumetypes.Delete(client, typeID).ExtractErr() if err != nil{ panic(err) } + +Example to update a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + volumetype, err = volumetypes.Update(client, typeID, volumetypes.UpdateOpts{ + Name: "volume_type_002", + Description:"description_002", + IsPublic:false, + }).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 fb48c8130b..9f312d09f5 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -101,3 +101,38 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return VolumeTypePage{pagination.LinkedPageBase{PageResult: r}} }) } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToVolumeTypeUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Volume Type. This object is passed +// to the volumetypes.Update function. For more information about the parameters, see +// the Volume Type object. +type UpdateOpts struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + IsPublic *bool `json:"is_public,omitempty"` +} + +// ToVolumeUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "volume_type") +} + +// Update will update the Volume Type with provided information. To extract the updated +// Volume Type from the response, call the Extract method on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToVolumeTypeUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go index b254e3fbdd..2e3169070f 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -87,3 +87,8 @@ type CreateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go index c705483c97..826facc5cc 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go @@ -135,3 +135,20 @@ func MockDeleteResponse(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/types/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) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "volume_type": { + "name": "vol-type-002", + "description": "volume type 0001", + "is_public": true, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" + } +}`) + }) +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 2753f8798c..9ade590832 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -91,3 +91,17 @@ func TestDelete(t *testing.T) { res := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + var isPublic = true + options := volumetypes.UpdateOpts{Name: "vol-type-002", IsPublic: &isPublic} + v, err := volumetypes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + th.CheckEquals(t, "vol-type-002", v.Name) + th.CheckEquals(t, true, v.IsPublic) +} diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go index 664b40e471..f794af86d1 100644 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -17,3 +17,7 @@ func createURL(c *gophercloud.ServiceClient) string { func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id) } + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("types", id) +} From d13755e6a6d98a1b4347ecfb86b89640e839ecf4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 28 Dec 2017 19:56:50 +0000 Subject: [PATCH 0170/2296] BlockStorage v3: Rename VolumeType PublicAccess to IsPublic This commit renames the VolumeType CreateOpts PublicAccess field to IsPublic. It also changes the field type to *bool to match the API's default behavior of setting the volume type to Public if the field is omitted. --- .../blockstorage/v3/volumetypes_test.go | 13 ++++++++----- .../blockstorage/v3/volumetypes/requests.go | 2 +- .../v3/volumetypes/testing/fixtures.go | 16 ++++++++-------- .../v3/volumetypes/testing/requests_test.go | 17 ++++++++++++++--- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 3a001fccb2..8e14c3afb8 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -53,10 +53,9 @@ func TestVolumeTypesCRUD(t *testing.T) { } createOpts := volumetypes.CreateOpts{ - Name: "create_from_gophercloud", - PublicAccess: true, - ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, - Description: "create_from_gophercloud", + Name: "create_from_gophercloud", + ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, + Description: "create_from_gophercloud", } vt, err := volumetypes.Create(client, createOpts).Extract() @@ -64,6 +63,8 @@ func TestVolumeTypesCRUD(t *testing.T) { t.Fatalf("Unable to create volumetype: %v", err) } + th.AssertEquals(t, true, vt.IsPublic) + tools.PrintResource(t, vt) defer volumetypes.Delete(client, vt.ID) @@ -74,7 +75,9 @@ func TestVolumeTypesCRUD(t *testing.T) { Name: "updated_volume_type", IsPublic: &isPublic, }).Extract() - th.AssertEquals(t, "updated_volume_type", newVT.Name) + th.AssertEquals(t, "updated_volume_type", newVT.Name) th.AssertEquals(t, false, newVT.IsPublic) + + tools.PrintResource(t, newVT) } diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 9f312d09f5..82a79bcf6a 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -20,7 +20,7 @@ type CreateOpts struct { // The volume type description Description string `json:"description,omitempty"` // the ID of the existing volume snapshot - PublicAccess bool `json:"os-volume-type-access:is_public"` + IsPublic *bool `json:"os-volume-type-access:is_public,omitempty"` // Extra spec key-value pairs defined by the user. ExtraSpecs map[string]string `json:"extra_specs"` } diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go index 826facc5cc..979aee174c 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go @@ -27,9 +27,9 @@ func MockListResponse(t *testing.T) { { "name": "SSD", "qos_specs_id": null, - "os-volume-type-access:is_public": true, + "os-volume-type-access:is_public": true, "extra_specs": { - "volume_backend_name": "lvmdriver-1" + "volume_backend_name": "lvmdriver-1" }, "is_public": true, "id": "6685584b-1eac-4da6-b5c3-555430cf68ff", @@ -38,7 +38,7 @@ func MockListResponse(t *testing.T) { { "name": "SATA", "qos_specs_id": null, - "os-volume-type-access:is_public": true, + "os-volume-type-access:is_public": true, "extra_specs": { "volume_backend_name": "lvmdriver-1" }, @@ -75,8 +75,8 @@ func MockGetResponse(t *testing.T) { "volume_type": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "name": "vol-type-001", - "os-volume-type-access:is_public": true, - "qos_specs_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "os-volume-type-access:is_public": true, + "qos_specs_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "description": "volume type 001", "is_public": true, "extra_specs": { @@ -100,7 +100,7 @@ func MockCreateResponse(t *testing.T) { "name": "test_type", "os-volume-type-access:is_public": true, "description": "test_type_desc", - "extra_specs": { + "extra_specs": { "capabilities": "gpu" } } @@ -119,7 +119,7 @@ func MockCreateResponse(t *testing.T) { "os-volume-type-access:is_public": true, "id": "6d0ff92a-0007-4780-9ece-acfe5876966a", "description": "test_type_desc", - "extra_specs": { + "extra_specs": { "capabilities": "gpu" } } @@ -147,7 +147,7 @@ func MockUpdateResponse(t *testing.T) { "name": "vol-type-002", "description": "volume type 0001", "is_public": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" } }`) }) diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 9ade590832..00c3e6ea9e 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -45,7 +45,6 @@ func TestListAll(t *testing.T) { }) th.AssertNoErr(t, err) th.AssertEquals(t, pages, 1) - } func TestGet(t *testing.T) { @@ -70,7 +69,15 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) - options := &volumetypes.CreateOpts{Name: "test_type", PublicAccess: true, Description: "test_type_desc", ExtraSpecs: map[string]string{"capabilities": "gpu"}} + var isPublic = true + + options := &volumetypes.CreateOpts{ + Name: "test_type", + IsPublic: &isPublic, + Description: "test_type_desc", + ExtraSpecs: map[string]string{"capabilities": "gpu"}, + } + n, err := volumetypes.Create(client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) @@ -99,7 +106,11 @@ func TestUpdate(t *testing.T) { MockUpdateResponse(t) var isPublic = true - options := volumetypes.UpdateOpts{Name: "vol-type-002", IsPublic: &isPublic} + options := volumetypes.UpdateOpts{ + Name: "vol-type-002", + IsPublic: &isPublic, + } + v, err := volumetypes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-type-002", v.Name) From 4b7db606e70b1060befcc5e7e06ef519158c0d2e Mon Sep 17 00:00:00 2001 From: jrperritt Date: Wed, 20 Dec 2017 14:10:24 -0600 Subject: [PATCH 0171/2296] only try to reauth once --- openstack/client.go | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index 8f762e9c52..5a52e57914 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -159,9 +159,21 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc } if options.AllowReauth { + // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but + // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, + // this should retry authentication only once + tac := *client + tac.ReauthFunc = nil + tac.TokenID = "" + tao := options + tao.AllowReauth = false client.ReauthFunc = func() error { - client.TokenID = "" - return v2auth(client, endpoint, options, eo) + err := v2auth(&tac, endpoint, tao, eo) + if err != nil { + return err + } + client.TokenID = tac.TokenID + return nil } } client.TokenID = token.ID @@ -203,9 +215,32 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au client.TokenID = token.ID if opts.CanReauth() { + // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but + // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, + // this should retry authentication only once + tac := *client + tac.ReauthFunc = nil + tac.TokenID = "" + var tao tokens3.AuthOptionsBuilder + switch ot := opts.(type) { + case *gophercloud.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + case *tokens3.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + default: + tao = opts + } client.ReauthFunc = func() error { - client.TokenID = "" - return v3auth(client, endpoint, opts, eo) + err := v3auth(&tac, endpoint, tao, eo) + if err != nil { + return err + } + client.TokenID = tac.TokenID + return nil } } client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { From 5dd941e0d62f89762b1ff513f518bcd02284fd35 Mon Sep 17 00:00:00 2001 From: Thiago da Silva Date: Thu, 28 Dec 2017 20:25:22 -0500 Subject: [PATCH 0172/2296] Add X-Storage-Policy to GetHeader struct The X-Storage-Policy header is missing from the container's GetHeader struct. For #696 Signed-off-by: Thiago da Silva --- .../objectstorage/v1/containers/results.go | 1 + .../v1/containers/testing/fixtures.go | 1 + .../v1/containers/testing/requests_test.go | 17 +++++++++-------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index 87682c885b..e8c78880a4 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -101,6 +101,7 @@ type GetHeader struct { TransID string `json:"X-Trans-Id"` VersionsLocation string `json:"X-Versions-Location"` Write []string `json:"-"` + StoragePolicy string `json:"X-Storage-Policy"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index b68230a8a4..5915323ad4 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -149,6 +149,7 @@ func HandleGetContainerSuccessfully(t *testing.T) { w.Header().Set("X-Container-Write", "test2,user4") w.Header().Set("X-Timestamp", "1471298837.95721") w.Header().Set("X-Trans-Id", "tx554ed59667a64c61866f1-0057b4ba37") + w.Header().Set("X-Storage-Policy", "test_policy") 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 484ebf48bc..b2c50141d4 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -129,14 +129,15 @@ func TestGetContainer(t *testing.T) { th.CheckNoErr(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, loc), //Wed, 17 Aug 2016 19:25:43 GMT - ObjectCount: 4, - Read: []string{"test"}, - TransID: "tx554ed59667a64c61866f1-0057b4ba37", - Write: []string{"test2", "user4"}, + AcceptRanges: "bytes", + BytesUsed: 100, + ContentType: "application/json; charset=utf-8", + Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, loc), //Wed, 17 Aug 2016 19:25:43 GMT + ObjectCount: 4, + Read: []string{"test"}, + TransID: "tx554ed59667a64c61866f1-0057b4ba37", + Write: []string{"test2", "user4"}, + StoragePolicy: "test_policy", } actual, err := res.Extract() th.CheckNoErr(t, err) From af117d579f204e53a57440196dee123cca34518f Mon Sep 17 00:00:00 2001 From: Ildar Svetlov Date: Sun, 31 Dec 2017 02:46:58 +0400 Subject: [PATCH 0173/2296] Add os-aggregates support (#691) * Add os-aggregates support * Fix hosts tag * Add unit and acceptance test * Fix path in the import * Add metadata field --- .../openstack/compute/v2/aggregates_test.go | 32 ++++++++ .../compute/v2/extensions/aggregates/doc.go | 21 ++++++ .../v2/extensions/aggregates/requests.go | 13 ++++ .../v2/extensions/aggregates/results.go | 42 +++++++++++ .../extensions/aggregates/testing/fixtures.go | 74 +++++++++++++++++++ .../aggregates/testing/requests_test.go | 40 ++++++++++ .../compute/v2/extensions/aggregates/urls.go | 7 ++ 7 files changed, 229 insertions(+) create mode 100644 acceptance/openstack/compute/v2/aggregates_test.go create mode 100644 openstack/compute/v2/extensions/aggregates/doc.go create mode 100644 openstack/compute/v2/extensions/aggregates/requests.go create mode 100644 openstack/compute/v2/extensions/aggregates/results.go create mode 100644 openstack/compute/v2/extensions/aggregates/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/aggregates/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/aggregates/urls.go diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go new file mode 100644 index 0000000000..d0771155ce --- /dev/null +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -0,0 +1,32 @@ +// +build acceptance compute aggregates + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" +) + +func TestAggregatesList(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + allPages, err := aggregates.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to list aggregates: %v", err) + } + + allAggregates, err := aggregates.ExtractAggregates(allPages) + if err != nil { + t.Fatalf("Unable to extract aggregates") + } + + for _, h := range allAggregates { + tools.PrintResource(t, h) + } +} diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go new file mode 100644 index 0000000000..43699cb916 --- /dev/null +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -0,0 +1,21 @@ +/* +Package aggregates returns information about the host aggregates in the +OpenStack cloud. + +Example of Retrieving list of all aggregates + + allPages, err := aggregates.List(computeClient).AllPages() + if err != nil { + panic(err) + } + + allAggregates, err := aggregates.ExtractAggregates(allPages) + if err != nil { + panic(err) + } + + for _, aggregate := range allAggregates { + fmt.Printf("%+v\n", aggregate) + } +*/ +package aggregates diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go new file mode 100644 index 0000000000..5f31136c52 --- /dev/null +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -0,0 +1,13 @@ +package aggregates + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List makes a request against the API to list aggregates. +func List(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, aggregatesListURL(client), func(r pagination.PageResult) pagination.Page { + return AggregatesPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go new file mode 100644 index 0000000000..19fc4443d6 --- /dev/null +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -0,0 +1,42 @@ +package aggregates + +import "github.com/gophercloud/gophercloud/pagination" + +// Aggregate represents a host aggregate in the OpenStack cloud. +type Aggregate struct { + // The availability zone of the host aggregate. + AvailabilityZone string `json:"availability_zone"` + + // A list of host ids in this aggregate. + Hosts []string `json:"hosts"` + + // The ID of the host aggregate. + ID int `json:"id"` + + // Metadata key and value pairs associate with the aggregate. + Metadata map[string]string `json:"metadata"` + + // Name of the aggregate. + Name string `json:"name"` +} + +// AggregatesPage represents a single page of all Aggregates from a List +// request. +type AggregatesPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a page of Aggregates contains any results. +func (page AggregatesPage) IsEmpty() (bool, error) { + aggregates, err := ExtractAggregates(page) + return len(aggregates) == 0, err +} + +// ExtractAggregates interprets a page of results as a slice of Aggregates. +func ExtractAggregates(p pagination.Page) ([]Aggregate, error) { + var a struct { + Aggregates []Aggregate `json:"aggregates"` + } + err := (p.(AggregatesPage)).ExtractInto(&a) + return a.Aggregates, err +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go new file mode 100644 index 0000000000..758da2fb00 --- /dev/null +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -0,0 +1,74 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// AggregateListBody is sample response to the List call +const AggregateListBody = ` +{ + "aggregates": [ + { + "name": "test-aggregate1", + "availability_zone": null, + "deleted": false, + "created_at": "2017-12-22T10:12:06.000000", + "updated_at": null, + "hosts": [], + "deleted_at": null, + "id": 1, + "metadata": {} + }, + { + "name": "test-aggregate2", + "availability_zone": "test-az", + "deleted": false, + "created_at": "2017-12-22T10:16:07.000000", + "updated_at": null, + "hosts": [ + "cmp0" + ], + "deleted_at": null, + "id": 4, + "metadata": { + "availability_zone": "test-az" + } + } + ] +} +` + +// First aggregate from the AggregateListBody +var FirstFakeAggregate = aggregates.Aggregate{ + AvailabilityZone: "", + Hosts: []string{}, + ID: 1, + Metadata: map[string]string{}, + Name: "test-aggregate1", +} + +// Second aggregate from the AggregateListBody +var SecondFakeAggregate = aggregates.Aggregate{ + AvailabilityZone: "test-az", + Hosts: []string{"cmp0"}, + ID: 4, + Metadata: map[string]string{"availability_zone": "test-az"}, + Name: "test-aggregate2", +} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-aggregates", 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, AggregateListBody) + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go new file mode 100644 index 0000000000..903b675c9f --- /dev/null +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -0,0 +1,40 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" + "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAggregates(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleListSuccessfully(t) + + pages := 0 + err := aggregates.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := aggregates.ExtractAggregates(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 aggregates, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, FirstFakeAggregate, actual[0]) + testhelper.CheckDeepEquals(t, SecondFakeAggregate, actual[1]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go new file mode 100644 index 0000000000..88b15009fa --- /dev/null +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -0,0 +1,7 @@ +package aggregates + +import "github.com/gophercloud/gophercloud" + +func aggregatesListURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-aggregates") +} From 6d9bf8c02219fd13d1c5b4079b74390cd718eef6 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 1 Jan 2018 00:25:02 +0300 Subject: [PATCH 0174/2296] Add Neutron subnetpool list support. (#694) * Add Neutron subnetpool list support. Add support to list Neutron subnetpools through a GET request on /v2.0/subnetpools The same command with OpenStack CLI is done with: openstack subnet pool list or neutron subnetpool-list * Add Neutron subnetpools list acceptance test * Update subnetpools acceptance test build options Add extension name to the subnetpools acceptance test. * Add custom UnmarshalJSON to subnetpools Neutron API can return string type in following subnetpool's fields: - default_prefixlen - min_prefixlen - max_prefixlen But it should be integer based on the API reference. We need to be sure that Gophercloud can work with both types in those fields. * Update subnetpool test fixtures Some fields in the SubnetPoolsListResult should be a string: - default_prefixlen - max_prefixlen - min_prefixlen * Refactor subnetpools test fixture Use spaces for indentation inside the SubnetPoolsListResult instead of hard tabs. --- .../subnetpools/subnetpools_test.go | 32 ++++ .../v2/extensions/subnetpools/doc.go | 24 +++ .../v2/extensions/subnetpools/requests.go | 68 ++++++++ .../v2/extensions/subnetpools/results.go | 162 ++++++++++++++++++ .../v2/extensions/subnetpools/testing/doc.go | 2 + .../subnetpools/testing/fixtures.go | 145 ++++++++++++++++ .../subnetpools/testing/requests_test.go | 52 ++++++ .../v2/extensions/subnetpools/urls.go | 13 ++ 8 files changed, 498 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go create mode 100644 openstack/networking/v2/extensions/subnetpools/doc.go create mode 100644 openstack/networking/v2/extensions/subnetpools/requests.go create mode 100644 openstack/networking/v2/extensions/subnetpools/results.go create mode 100644 openstack/networking/v2/extensions/subnetpools/testing/doc.go create mode 100644 openstack/networking/v2/extensions/subnetpools/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/subnetpools/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/subnetpools/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go new file mode 100644 index 0000000000..9884a70a9c --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking subnetpools + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" +) + +func TestSubnetPoolsList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := subnetpools.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list subnetpools: %v", err) + } + + allSubnetPools, err := subnetpools.ExtractSubnetPools(allPages) + if err != nil { + t.Fatalf("Unable to extract subnetpools: %v", err) + } + + for _, subnetpool := range allSubnetPools { + tools.PrintResource(t, subnetpool) + } +} diff --git a/openstack/networking/v2/extensions/subnetpools/doc.go b/openstack/networking/v2/extensions/subnetpools/doc.go new file mode 100644 index 0000000000..82d09a320d --- /dev/null +++ b/openstack/networking/v2/extensions/subnetpools/doc.go @@ -0,0 +1,24 @@ +/* +Package subnetpools provides the ability to retrieve and manage subnetpools through the Neutron API. + +Example of Listing Subnetpools. + + listOpts := subnets.ListOpts{ + IPVersion: 6, + } + + allPages, err := subnetpools.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allSubnetpools, err := subnetpools.ExtractSubnetPools(allPages) + if err != nil { + panic(err) + } + + for _, subnetpools := range allSubnetpools { + fmt.Printf("%+v\n", subnetpools) + } +*/ +package subnetpools diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go new file mode 100644 index 0000000000..506a867d5c --- /dev/null +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -0,0 +1,68 @@ +package subnetpools + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToSubnetPoolListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the Neutron API. Filtering is achieved by passing in struct field values +// that map to the subnetpool attributes you want to see returned. +// SortKey allows you to sort by a particular subnetpool attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for the pagination. +type ListOpts struct { + ID string `q:"id"` + Name string `q:"name"` + DefaultQuota int `q:"default_quota"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + CreatedAt string `q:"created_at"` + UpdatedAt string `q:"updated_at"` + Prefixes []string `q:"prefixes"` + DefaultPrefixLen int `q:"default_prefixlen"` + MinPrefixLen int `q:"min_prefixlen"` + MaxPrefixLen int `q:"max_prefixlen"` + AddressScopeID string `q:"address_scope_id"` + IPversion int `q:"ip_version"` + Shared bool `q:"shared"` + Description string `q:"description"` + IsDefault bool `q:"is_default"` + RevisionNumber int `q:"revision_number"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToSubnetPoolListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToSubnetPoolListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// subnetpools. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only the subnetpools owned by the project +// of the user submitting the request, unless the user has the administrative role. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToSubnetPoolListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return SubnetPoolPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go new file mode 100644 index 0000000000..b4eb8fb2db --- /dev/null +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -0,0 +1,162 @@ +package subnetpools + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// SubnetPool represents a Neutron subnetpool. +// A subnetpool is a pool of addresses from which subnets can be allocated. +type SubnetPool struct { + // ID is the id of the subnetpool. + ID string `json:"id"` + + // Name is the human-readable name of the subnetpool. + Name string `json:"name"` + + // DefaultQuota is the per-project quota on the prefix space + // that can be allocated from the subnetpool for project subnets. + DefaultQuota int `json:"default_quota"` + + // TenantID is the id of the Identity project. + TenantID string `json:"tenant_id"` + + // ProjectID is the id of the Identity project. + ProjectID string `json:"project_id"` + + // CreatedAt is the time at which subnetpool has been created. + CreatedAt string `json:"created_at"` + + // UpdatedAt is the time at which subnetpool has been created. + UpdatedAt string `json:"updated_at"` + + // Prefixes is the list of subnet prefixes to assign to the subnetpool. + // Neutron API merges adjacent prefixes and treats them as a single prefix. + // Each subnet prefix must be unique among all subnet prefixes in all subnetpools + // that are associated with the address scope. + Prefixes []string `json:"prefixes"` + + // DefaultPrefixLen is yhe size of the prefix to allocate when the cidr + // or prefixlen attributes are omitted when you create the subnet. + // Defaults to the MinPrefixLen. + DefaultPrefixLen int `json:"-"` + + // MinPrefixLen is the smallest prefix that can be allocated from a subnetpool. + // For IPv4 subnetpools, default is 8. + // For IPv6 subnetpools, default is 64. + MinPrefixLen int `json:"-"` + + // MaxPrefixLen is the maximum prefix size that can be allocated from the subnetpool. + // For IPv4 subnetpools, default is 32. + // For IPv6 subnetpools, default is 128. + MaxPrefixLen int `json:"-"` + + // AddressScopeID is the Neutron address scope to assign to the subnetpool. + AddressScopeID string `json:"address_scope_id"` + + // IPversion is the IP protocol version. + // Valid value is 4 or 6. Default is 4. + IPversion int `json:"ip_version"` + + // Shared indicates whether this network is shared across all projects. + Shared bool `json:"shared"` + + // Description is thehuman-readable description for the resource. + Description string `json:"description"` + + // IsDefault indicates if the subnetpool is default pool or not. + IsDefault bool `json:"is_default"` + + // RevisionNumber is the revision number of the subnetpool. + RevisionNumber int `json:"revision_number"` +} + +func (r *SubnetPool) UnmarshalJSON(b []byte) error { + type tmp SubnetPool + var s struct { + tmp + DefaultPrefixLen interface{} `json:"default_prefixlen"` + MinPrefixLen interface{} `json:"min_prefixlen"` + MaxPrefixLen interface{} `json:"max_prefixlen"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = SubnetPool(s.tmp) + + switch t := s.DefaultPrefixLen.(type) { + case string: + if r.DefaultPrefixLen, err = strconv.Atoi(t); err != nil { + return err + } + case float64: + r.DefaultPrefixLen = int(t) + default: + return fmt.Errorf("DefaultPrefixLen has unexpected type: %T", t) + } + + switch t := s.MinPrefixLen.(type) { + case string: + if r.MinPrefixLen, err = strconv.Atoi(t); err != nil { + return err + } + case float64: + r.MinPrefixLen = int(t) + default: + return fmt.Errorf("MinPrefixLen has unexpected type: %T", t) + } + + switch t := s.MaxPrefixLen.(type) { + case string: + if r.MaxPrefixLen, err = strconv.Atoi(t); err != nil { + return err + } + case float64: + r.MaxPrefixLen = int(t) + default: + return fmt.Errorf("MaxPrefixLen has unexpected type: %T", t) + } + + return nil +} + +// SubnetPoolPage stores a single page of SubnetPools from a List() API call. +type SubnetPoolPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of subnetpools 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 SubnetPoolPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"subnetpools_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty determines whether or not a SubnetPoolPage is empty. +func (r SubnetPoolPage) IsEmpty() (bool, error) { + subnetpools, err := ExtractSubnetPools(r) + return len(subnetpools) == 0, err +} + +// ExtractSubnetPools interprets the results of a single page from a List() API call, +// producing a slice of SubnetPools structs. +func ExtractSubnetPools(r pagination.Page) ([]SubnetPool, error) { + var s struct { + SubnetPools []SubnetPool `json:"subnetpools"` + } + err := (r.(SubnetPoolPage)).ExtractInto(&s) + return s.SubnetPools, err +} diff --git a/openstack/networking/v2/extensions/subnetpools/testing/doc.go b/openstack/networking/v2/extensions/subnetpools/testing/doc.go new file mode 100644 index 0000000000..7787611308 --- /dev/null +++ b/openstack/networking/v2/extensions/subnetpools/testing/doc.go @@ -0,0 +1,2 @@ +// subnetpools unit tests +package testing diff --git a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go new file mode 100644 index 0000000000..d22074c148 --- /dev/null +++ b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go @@ -0,0 +1,145 @@ +package testing + +import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" +) + +const SubnetPoolsListResult = ` +{ + "subnetpools": [ + { + "address_scope_id": null, + "created_at": "2017-12-28T07:21:41Z", + "default_prefixlen": "8", + "default_quota": null, + "description": "IPv4", + "id": "d43a57fe-3390-4608-b437-b1307b0adb40", + "ip_version": 4, + "is_default": false, + "max_prefixlen": "32", + "min_prefixlen": "8", + "name": "MyPoolIpv4", + "prefixes": [ + "10.10.10.0/24", + "10.11.11.0/24" + ], + "project_id": "1e2b9857295a4a3e841809ef492812c5", + "revision_number": 1, + "shared": false, + "tenant_id": "1e2b9857295a4a3e841809ef492812c5", + "updated_at": "2017-12-28T07:21:41Z" + }, + { + "address_scope_id": "0bc38e22-be49-4e67-969e-fec3f36508bd", + "created_at": "2017-12-28T07:21:34Z", + "default_prefixlen": "64", + "default_quota": null, + "description": "IPv6", + "id": "832cb7f3-59fe-40cf-8f64-8350ffc03272", + "ip_version": 6, + "is_default": true, + "max_prefixlen": "128", + "min_prefixlen": "64", + "name": "MyPoolIpv6", + "prefixes": [ + "fdf7:b13d:dead:beef::/64", + "fd65:86cc:a334:39b7::/64" + ], + "project_id": "1e2b9857295a4a3e841809ef492812c5", + "revision_number": 1, + "shared": false, + "tenant_id": "1e2b9857295a4a3e841809ef492812c5", + "updated_at": "2017-12-28T07:21:34Z" + }, + { + "address_scope_id": null, + "created_at": "2017-12-28T07:21:27Z", + "default_prefixlen": "64", + "default_quota": 4, + "description": "PublicPool", + "id": "2fe18ae6-58c2-4a85-8bfb-566d6426749b", + "ip_version": 6, + "is_default": false, + "max_prefixlen": "128", + "min_prefixlen": "64", + "name": "PublicIPv6", + "prefixes": [ + "2001:db8::a3/64" + ], + "project_id": "ceb366d50ad54fe39717df3af60f9945", + "revision_number": 1, + "shared": true, + "tenant_id": "ceb366d50ad54fe39717df3af60f9945", + "updated_at": "2017-12-28T07:21:27Z" + } + ] +} +` + +var SubnetPool1 = subnetpools.SubnetPool{ + AddressScopeID: "", + CreatedAt: "2017-12-28T07:21:41Z", + DefaultPrefixLen: 8, + DefaultQuota: 0, + Description: "IPv4", + ID: "d43a57fe-3390-4608-b437-b1307b0adb40", + IPversion: 4, + IsDefault: false, + MaxPrefixLen: 32, + MinPrefixLen: 8, + Name: "MyPoolIpv4", + Prefixes: []string{ + "10.10.10.0/24", + "10.11.11.0/24", + }, + ProjectID: "1e2b9857295a4a3e841809ef492812c5", + TenantID: "1e2b9857295a4a3e841809ef492812c5", + RevisionNumber: 1, + Shared: false, + UpdatedAt: "2017-12-28T07:21:41Z", +} + +var SubnetPool2 = subnetpools.SubnetPool{ + AddressScopeID: "0bc38e22-be49-4e67-969e-fec3f36508bd", + CreatedAt: "2017-12-28T07:21:34Z", + DefaultPrefixLen: 64, + DefaultQuota: 0, + Description: "IPv6", + ID: "832cb7f3-59fe-40cf-8f64-8350ffc03272", + IPversion: 6, + IsDefault: true, + MaxPrefixLen: 128, + MinPrefixLen: 64, + Name: "MyPoolIpv6", + Prefixes: []string{ + "fdf7:b13d:dead:beef::/64", + "fd65:86cc:a334:39b7::/64", + }, + ProjectID: "1e2b9857295a4a3e841809ef492812c5", + TenantID: "1e2b9857295a4a3e841809ef492812c5", + RevisionNumber: 1, + Shared: false, + UpdatedAt: "2017-12-28T07:21:34Z", +} + +var SubnetPool3 = subnetpools.SubnetPool{ + AddressScopeID: "", + CreatedAt: "2017-12-28T07:21:27Z", + DefaultPrefixLen: 64, + DefaultQuota: 4, + Description: "PublicPool", + ID: "2fe18ae6-58c2-4a85-8bfb-566d6426749b", + IPversion: 6, + IsDefault: false, + MaxPrefixLen: 128, + MinPrefixLen: 64, + Name: "PublicIPv6", + Prefixes: []string{ + "2001:db8::a3/64", + }, + ProjectID: "ceb366d50ad54fe39717df3af60f9945", + TenantID: "ceb366d50ad54fe39717df3af60f9945", + RevisionNumber: 1, + Shared: true, + UpdatedAt: "2017-12-28T07:21:27Z", +} diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go new file mode 100644 index 0000000000..9624bf4420 --- /dev/null +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -0,0 +1,52 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" + "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/subnetpools", 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, SubnetPoolsListResult) + }) + + count := 0 + + subnetpools.List(fake.ServiceClient(), subnetpools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := subnetpools.ExtractSubnetPools(page) + if err != nil { + t.Errorf("Failed to extract subnetpools: %v", err) + return false, nil + } + + expected := []subnetpools.SubnetPool{ + SubnetPool1, + SubnetPool2, + SubnetPool3, + } + + 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/subnetpools/urls.go b/openstack/networking/v2/extensions/subnetpools/urls.go new file mode 100644 index 0000000000..c8fc5cb13e --- /dev/null +++ b/openstack/networking/v2/extensions/subnetpools/urls.go @@ -0,0 +1,13 @@ +package subnetpools + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "subnetpools" + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From fe438f6d3ecf9253ea3b0db1dcf4448ca8795742 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 2 Jan 2018 18:31:32 +0300 Subject: [PATCH 0175/2296] Add Neutron subnetpool get support Add support to show a Neutron subnetpool through a GET request on /v2.0/subnetpools/{subnetpool_id}. The same command with OpenStack CLI is done with: openstack subnet pool show or neutron subnetpool-show --- .../v2/extensions/subnetpools/doc.go | 8 ++++ .../v2/extensions/subnetpools/requests.go | 6 +++ .../v2/extensions/subnetpools/results.go | 19 ++++++++++ .../subnetpools/testing/fixtures.go | 26 +++++++++++++ .../subnetpools/testing/requests_test.go | 38 +++++++++++++++++++ .../v2/extensions/subnetpools/urls.go | 8 ++++ 6 files changed, 105 insertions(+) diff --git a/openstack/networking/v2/extensions/subnetpools/doc.go b/openstack/networking/v2/extensions/subnetpools/doc.go index 82d09a320d..9d2f46ac9c 100644 --- a/openstack/networking/v2/extensions/subnetpools/doc.go +++ b/openstack/networking/v2/extensions/subnetpools/doc.go @@ -20,5 +20,13 @@ Example of Listing Subnetpools. for _, subnetpools := range allSubnetpools { fmt.Printf("%+v\n", subnetpools) } + +Example to Get a Subnetpool + + subnetPoolID = "23d5d3f7-9dfa-4f73-b72b-8b0b0063ec55" + subnetPool, err := subnetpools.Get(networkClient, subnetPoolID).Extract() + if err != nil { + panic(err) + } */ package subnetpools diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index 506a867d5c..c7471d5927 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -66,3 +66,9 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { return SubnetPoolPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Get retrieves a specific subnetpool based on its ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index b4eb8fb2db..bef3d6ea08 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -9,6 +9,25 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a subnetpool resource. +func (r commonResult) Extract() (*SubnetPool, error) { + var s struct { + SubnetPool *SubnetPool `json:"subnetpool"` + } + err := r.ExtractInto(&s) + return s.SubnetPool, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a SubnetPool. +type GetResult struct { + commonResult +} + // SubnetPool represents a Neutron subnetpool. // A subnetpool is a pool of addresses from which subnets can be allocated. type SubnetPool struct { diff --git a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go index d22074c148..f736493df0 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go @@ -143,3 +143,29 @@ var SubnetPool3 = subnetpools.SubnetPool{ Shared: true, UpdatedAt: "2017-12-28T07:21:27Z", } + +const SubnetPoolGetResult = ` +{ + "subnetpool": { + "min_prefixlen": "64", + "address_scope_id": null, + "default_prefixlen": "64", + "id": "0a738452-8057-4ad3-89c2-92f6a74afa76", + "max_prefixlen": "128", + "name": "my-ipv6-pool", + "default_quota": 2, + "is_default": true, + "project_id": "1e2b9857295a4a3e841809ef492812c5", + "tenant_id": "1e2b9857295a4a3e841809ef492812c5", + "created_at": "2018-01-01T00:00:01", + "prefixes": [ + "2001:db8::a3/64" + ], + "updated_at": "2018-01-01T00:10:10", + "ip_version": 6, + "shared": false, + "description": "ipv6 prefixes", + "revision_number": 2 + } +} +` diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index 9624bf4420..92487c7e60 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -50,3 +50,41 @@ func TestList(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/subnetpools/0a738452-8057-4ad3-89c2-92f6a74afa76", 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, SubnetPoolGetResult) + }) + + s, err := subnetpools.Get(fake.ServiceClient(), "0a738452-8057-4ad3-89c2-92f6a74afa76").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.ID, "0a738452-8057-4ad3-89c2-92f6a74afa76") + th.AssertEquals(t, s.Name, "my-ipv6-pool") + th.AssertEquals(t, s.DefaultQuota, 2) + th.AssertEquals(t, s.TenantID, "1e2b9857295a4a3e841809ef492812c5") + th.AssertEquals(t, s.ProjectID, "1e2b9857295a4a3e841809ef492812c5") + th.AssertEquals(t, s.CreatedAt, "2018-01-01T00:00:01") + th.AssertEquals(t, s.UpdatedAt, "2018-01-01T00:10:10") + th.AssertDeepEquals(t, s.Prefixes, []string{ + "2001:db8::a3/64", + }) + th.AssertEquals(t, s.DefaultPrefixLen, 64) + th.AssertEquals(t, s.MinPrefixLen, 64) + th.AssertEquals(t, s.MaxPrefixLen, 128) + th.AssertEquals(t, s.AddressScopeID, "") + th.AssertEquals(t, s.IPversion, 6) + th.AssertEquals(t, s.Shared, false) + th.AssertEquals(t, s.Description, "ipv6 prefixes") + th.AssertEquals(t, s.IsDefault, true) + th.AssertEquals(t, s.RevisionNumber, 2) +} diff --git a/openstack/networking/v2/extensions/subnetpools/urls.go b/openstack/networking/v2/extensions/subnetpools/urls.go index c8fc5cb13e..cdd345a45c 100644 --- a/openstack/networking/v2/extensions/subnetpools/urls.go +++ b/openstack/networking/v2/extensions/subnetpools/urls.go @@ -4,6 +4,10 @@ import "github.com/gophercloud/gophercloud" const resourcePath = "subnetpools" +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} + func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } @@ -11,3 +15,7 @@ func rootURL(c *gophercloud.ServiceClient) string { func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From 45bd3e7f16f5c2edafb0791be1423aa1ea001c5a Mon Sep 17 00:00:00 2001 From: liusheng Date: Wed, 3 Jan 2018 15:40:52 +0800 Subject: [PATCH 0176/2296] Expose the real error message of BadRequest error This change expose the error message of BadRequest error from openstack API, this is helpful to find out the reason of request failure. --- errors.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/errors.go b/errors.go index 88fd2ac676..5356541e7b 100644 --- a/errors.go +++ b/errors.go @@ -103,7 +103,11 @@ type ErrDefault503 struct { } func (e ErrDefault400) Error() string { - return "Invalid request due to incorrect syntax or missing required parameters." + e.DefaultErrString = fmt.Sprintf( + "Bad request with: [%s %s], error message: %s", + e.Method, e.URL, e.Body, + ) + return e.choseErrString() } func (e ErrDefault401) Error() string { return "Authentication failed" From f05feab319e784a98d4ae5e7292011536c7cc3d4 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 4 Jan 2018 06:28:44 +0300 Subject: [PATCH 0177/2296] Add Neutron subnetpool creation support (#702) * Add Neutron subnetpool creation support Add support to create a Neutron subnetpool through a POST request on /v2.0/subnetpools. The same command with OpenStack CLI is done with: openstack subnet pool create or neutron subnetpool-create * Add acceptance test for the subnetpool creation Add the TestCreateSubnetPool acceptance test and a generic CreateSubnetPool function inside the acceptance tests for sharing with other components. * Rename TestCreateSubnetPool to TestSubnetPoolsCRUD * Fix Subnetpools CreateOpts comments Fix DefaultPrefixLen comment and remove empty line at the beginning of a struct. --- .../v2/extensions/subnetpools/subnetpools.go | 32 ++++++++ .../subnetpools/subnetpools_test.go | 15 ++++ .../v2/extensions/subnetpools/doc.go | 17 +++++ .../v2/extensions/subnetpools/requests.go | 73 +++++++++++++++++++ .../v2/extensions/subnetpools/results.go | 6 ++ .../subnetpools/testing/fixtures.go | 43 +++++++++++ .../subnetpools/testing/requests_test.go | 41 +++++++++++ .../v2/extensions/subnetpools/urls.go | 4 + 8 files changed, 231 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go new file mode 100644 index 0000000000..028bb3d2d9 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go @@ -0,0 +1,32 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" +) + +// CreateSubnetPool will create a subnetpool. An error will be returned if the +// subnetpool could not be created. +func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetpools.SubnetPool, error) { + subnetPoolName := tools.RandomString("TESTACC-", 8) + subnetPoolPrefixes := []string{ + "10.0.0.0/8", + } + createOpts := subnetpools.CreateOpts{ + Name: subnetPoolName, + Prefixes: subnetPoolPrefixes, + } + + t.Logf("Attempting to create a subnetpool: %s", subnetPoolName) + + subnetPool, err := subnetpools.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created the subnetpool.") + return subnetPool, nil +} diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index 9884a70a9c..d419ee9bea 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -10,6 +10,21 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" ) +func TestSubnetPoolsCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create a subnetpool + subnetPool, err := CreateSubnetPool(t, client) + if err != nil { + t.Fatalf("Unable to create a subnetpool: %v", err) + } + + tools.PrintResource(t, subnetPool) +} + func TestSubnetPoolsList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { diff --git a/openstack/networking/v2/extensions/subnetpools/doc.go b/openstack/networking/v2/extensions/subnetpools/doc.go index 9d2f46ac9c..bef874546f 100644 --- a/openstack/networking/v2/extensions/subnetpools/doc.go +++ b/openstack/networking/v2/extensions/subnetpools/doc.go @@ -28,5 +28,22 @@ Example to Get a Subnetpool if err != nil { panic(err) } + +Example to Create a new Subnetpool. + + subnetPoolName := "private_pool" + subnetPoolPrefixes := []string{ + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + } + subnetPoolOpts := subnetpools.CreateOpts{ + Name: subnetPoolName, + Prefixes: subnetPoolPrefixes, + } + subnetPool, err := subnetpools.Create(networkClient, subnetPoolOpts).Extract() + if err != nil { + panic(err) + } */ package subnetpools diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index c7471d5927..4828e05aa1 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -72,3 +72,76 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } + +// CreateOptsBuilder allows to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToSubnetPoolCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters of a new subnetpool. +type CreateOpts struct { + // Name is the human-readable name of the subnetpool. + Name string `json:"name"` + + // DefaultQuota is the per-project quota on the prefix space + // that can be allocated from the subnetpool for project subnets. + DefaultQuota int `json:"default_quota,omitempty"` + + // TenantID is the id of the Identity project. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the id of the Identity project. + ProjectID string `json:"project_id,omitempty"` + + // Prefixes is the list of subnet prefixes to assign to the subnetpool. + // Neutron API merges adjacent prefixes and treats them as a single prefix. + // Each subnet prefix must be unique among all subnet prefixes in all subnetpools + // that are associated with the address scope. + Prefixes []string `json:"prefixes"` + + // DefaultPrefixLen is the size of the prefix to allocate when the cidr + // or prefixlen attributes are omitted when you create the subnet. + // Defaults to the MinPrefixLen. + DefaultPrefixLen int `json:"default_prefixlen,omitempty"` + + // MinPrefixLen is the smallest prefix that can be allocated from a subnetpool. + // For IPv4 subnetpools, default is 8. + // For IPv6 subnetpools, default is 64. + MinPrefixLen int `json:"min_prefixlen,omitempty"` + + // MaxPrefixLen is the maximum prefix size that can be allocated from the subnetpool. + // For IPv4 subnetpools, default is 32. + // For IPv6 subnetpools, default is 128. + MaxPrefixLen int `json:"max_prefixlen,omitempty"` + + // AddressScopeID is the Neutron address scope to assign to the subnetpool. + AddressScopeID string `json:"address_scope_id,omitempty"` + + // Shared indicates whether this network is shared across all projects. + Shared bool `json:"shared,omitempty"` + + // Description is the human-readable description for the resource. + Description string `json:"description,omitempty"` + + // IsDefault indicates if the subnetpool is default pool or not. + IsDefault bool `json:"is_default,omitempty"` +} + +// ToSubnetPoolCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToSubnetPoolCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "subnetpool") +} + +// Create requests the creation of a new subnetpool on the server. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToSubnetPoolCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index bef3d6ea08..ff3dc38009 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -28,6 +28,12 @@ type GetResult struct { commonResult } +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a SubnetPool. +type CreateResult struct { + commonResult +} + // SubnetPool represents a Neutron subnetpool. // A subnetpool is a pool of addresses from which subnets can be allocated. type SubnetPool struct { diff --git a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go index f736493df0..fd279f1d38 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go @@ -169,3 +169,46 @@ const SubnetPoolGetResult = ` } } ` + +const SubnetPoolCreateRequest = ` +{ + "subnetpool": { + "name": "my_ipv4_pool", + "prefixes": [ + "10.10.0.0/16", + "10.11.11.0/24" + ], + "address_scope_id": "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3", + "min_prefixlen": 25, + "max_prefixlen": 30, + "description": "ipv4 prefixes" + } +} +` + +const SubnetPoolCreateResult = ` +{ + "subnetpool": { + "address_scope_id": "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3", + "created_at": "2018-01-01T00:00:15Z", + "default_prefixlen": "25", + "default_quota": null, + "description": "ipv4 prefixes", + "id": "55b5999c-c2fe-42cd-bce0-961a551b80f5", + "ip_version": 4, + "is_default": false, + "max_prefixlen": "30", + "min_prefixlen": "25", + "name": "my_ipv4_pool", + "prefixes": [ + "10.10.0.0/16", + "10.11.11.0/24" + ], + "project_id": "1e2b9857295a4a3e841809ef492812c5", + "revision_number": 1, + "shared": false, + "tenant_id": "1e2b9857295a4a3e841809ef492812c5", + "updated_at": "2018-01-01T00:00:15Z" + } +} +` diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index 92487c7e60..b80d7ef032 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -88,3 +88,44 @@ func TestGet(t *testing.T) { th.AssertEquals(t, s.IsDefault, true) th.AssertEquals(t, s.RevisionNumber, 2) } +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/subnetpools", 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, SubnetPoolCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, SubnetPoolCreateResult) + }) + + opts := subnetpools.CreateOpts{ + Name: "my_ipv4_pool", + Prefixes: []string{ + "10.10.0.0/16", + "10.11.11.0/24", + }, + MinPrefixLen: 25, + MaxPrefixLen: 30, + AddressScopeID: "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3", + Description: "ipv4 prefixes", + } + s, err := subnetpools.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "my_ipv4_pool") + th.AssertDeepEquals(t, s.Prefixes, []string{ + "10.10.0.0/16", + "10.11.11.0/24", + }) + th.AssertEquals(t, s.MinPrefixLen, 25) + th.AssertEquals(t, s.MaxPrefixLen, 30) + th.AssertEquals(t, s.AddressScopeID, "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3") + th.AssertEquals(t, s.Description, "ipv4 prefixes") +} diff --git a/openstack/networking/v2/extensions/subnetpools/urls.go b/openstack/networking/v2/extensions/subnetpools/urls.go index cdd345a45c..5ba98ae359 100644 --- a/openstack/networking/v2/extensions/subnetpools/urls.go +++ b/openstack/networking/v2/extensions/subnetpools/urls.go @@ -19,3 +19,7 @@ func listURL(c *gophercloud.ServiceClient) string { func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From 800a4c0d57fbe8403b0bb6f13a8340c8fc990ad5 Mon Sep 17 00:00:00 2001 From: David Lyle Date: Wed, 3 Jan 2018 21:57:46 -0700 Subject: [PATCH 0178/2296] Flavor Extra Spec Delete --- acceptance/openstack/compute/v2/flavors_test.go | 6 ++++++ openstack/compute/v2/flavors/doc.go | 7 +++++++ openstack/compute/v2/flavors/requests.go | 9 +++++++++ openstack/compute/v2/flavors/results.go | 6 ++++++ openstack/compute/v2/flavors/testing/fixtures.go | 9 +++++++++ openstack/compute/v2/flavors/testing/requests_test.go | 9 +++++++++ openstack/compute/v2/flavors/urls.go | 4 ++++ 7 files changed, 50 insertions(+) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index bb6c5c41d9..817fcc5c58 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -176,6 +176,11 @@ func TestFlavorExtraSpecsCRUD(t *testing.T) { } tools.PrintResource(t, createdExtraSpecs) + err = flavors.DeleteExtraSpec(client, flavor.ID, "hw:cpu_policy").ExtractErr() + if err != nil { + t.Fatalf("Unable to delete ExtraSpec: %v\n", err) + } + allExtraSpecs, err := flavors.ListExtraSpecs(client, flavor.ID).Extract() if err != nil { t.Fatalf("Unable to get flavor extra_specs: %v", err) @@ -189,4 +194,5 @@ func TestFlavorExtraSpecsCRUD(t *testing.T) { } tools.PrintResource(t, spec) } + } diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 867d53a819..31e8e2733f 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -99,5 +99,12 @@ Example to Get Extra Specs for a Flavor fmt.Printf("%+v", extraSpecs) +Example to Delete an Extra Spec for a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + err := flavors.DeleteExtraSpec(computeClient, flavorID, "hw:cpu_thread_policy").ExtractErr() + if err != nil { + panic(err) + } */ package flavors diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 965d271d1d..6edae4c9ce 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -231,6 +231,15 @@ func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts C return } +// DeleteExtraSpec will delete the key-value pair with the given key for the given +// flavor ID. +func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { + _, r.Err = client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // IDFromName is a convienience function that returns a flavor's ID given its // name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 4451be38c9..e0ab64bf37 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -223,6 +223,12 @@ type GetExtraSpecResult struct { extraSpecResult } +// DeleteExtraSpecResult contains the result of a Delete operation. Call its +// ExtractErr method to determine if the call succeeded or failed. +type DeleteExtraSpecResult struct { + gophercloud.ErrResult +} + // Extract interprets any extraSpecResult as an ExtraSpec, if possible. func (r extraSpecResult) Extract() (map[string]string, error) { var s map[string]string diff --git a/openstack/compute/v2/flavors/testing/fixtures.go b/openstack/compute/v2/flavors/testing/fixtures.go index 536a7c11d4..0c5486b152 100644 --- a/openstack/compute/v2/flavors/testing/fixtures.go +++ b/openstack/compute/v2/flavors/testing/fixtures.go @@ -78,3 +78,12 @@ func HandleExtraSpecsCreateSuccessfully(t *testing.T) { fmt.Fprintf(w, ExtraSpecsGetBody) }) } + +func HandleExtraSpecDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/flavors/1/os-extra_specs/hw:cpu_policy", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.WriteHeader(http.StatusOK) + }) +} diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index ef40e5973f..f34cb3c1a3 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -336,3 +336,12 @@ func TestFlavorExtraSpecsCreate(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } + +func TestFlavorExtraSpecDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecDeleteSuccessfully(t) + + res := flavors.DeleteExtraSpec(fake.ServiceClient(), "1", "hw:cpu_policy") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go index b74f81625d..c9d43c5b46 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -39,3 +39,7 @@ func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id, "os-extra_specs") } + +func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("flavors", id, "os-extra_specs", key) +} From b2bf8a613b41baa2a8b56a6a8483321a84520dfa Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy Date: Mon, 8 Jan 2018 07:05:15 +0300 Subject: [PATCH 0179/2296] Compute availability zones info support (#704) * Availability zones info support (#320) Add ability to get list and detailed AZs info * Style fixes * Change test fixture to proper * Delete useless method of `ServerAvailabilityZoneExt` * Code improvements * Change `AvailabilityZoneInfo` type from `struct` to `slice` * Add custom `UnmarshalJSON` to `AvailabilityZoneInfo` * Get rid of 'OS' prefixes in variable's names * Update doc.go * Code refactoring * Rename `ServiceOfState` to `ServiceState` * Change type `Services` from `struct` to `map[string]ServiceState` * Delete useless type `AvailabilityZoneInfo` * Update doc.go * Acceptance tests for #704 * Change struct tag `updated_at` to `-` --- .../compute/v2/availabilityzones_test.go | 53 +++++ .../v2/extensions/availabilityzones/doc.go | 43 +++- .../extensions/availabilityzones/requests.go | 20 ++ .../extensions/availabilityzones/results.go | 66 +++++- .../availabilityzones/testing/doc.go | 2 + .../availabilityzones/testing/fixtures.go | 197 ++++++++++++++++++ .../testing/requests_test.go | 41 ++++ .../v2/extensions/availabilityzones/urls.go | 11 + 8 files changed, 425 insertions(+), 8 deletions(-) create mode 100644 acceptance/openstack/compute/v2/availabilityzones_test.go create mode 100644 openstack/compute/v2/extensions/availabilityzones/requests.go create mode 100644 openstack/compute/v2/extensions/availabilityzones/testing/doc.go create mode 100644 openstack/compute/v2/extensions/availabilityzones/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/availabilityzones/urls.go diff --git a/acceptance/openstack/compute/v2/availabilityzones_test.go b/acceptance/openstack/compute/v2/availabilityzones_test.go new file mode 100644 index 0000000000..3e82128422 --- /dev/null +++ b/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -0,0 +1,53 @@ +// +build acceptance compute availabilityzones + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" +) + +func TestAvailabilityZonesList(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + allPages, err := availabilityzones.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to list availability zones info: %v", err) + } + + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + t.Fatalf("Unable to extract availability zones info: %v", err) + } + + for _, zoneInfo := range availabilityZoneInfo { + tools.PrintResource(t, zoneInfo) + } +} + +func TestAvailabilityZonesListDetail(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + allPages, err := availabilityzones.ListDetail(client).AllPages() + if err != nil { + t.Fatalf("Unable to list availability zones detailed info: %v", err) + } + + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + t.Fatalf("Unable to extract availability zones detailed info: %v", err) + } + + for _, zoneInfo := range availabilityZoneInfo { + tools.PrintResource(t, zoneInfo) + } +} diff --git a/openstack/compute/v2/extensions/availabilityzones/doc.go b/openstack/compute/v2/extensions/availabilityzones/doc.go index 80464ba399..c5429add0a 100644 --- a/openstack/compute/v2/extensions/availabilityzones/doc.go +++ b/openstack/compute/v2/extensions/availabilityzones/doc.go @@ -1,26 +1,57 @@ /* -Package availabilityzones provides the ability to extend a server result with -availability zone information. Example: +Package availabilityzones provides the ability to get lists and detailed +availability zone information and to extend a server result with +availability zone information. + +Example of Extend server result with Availability Zone Information: type ServerWithAZ struct { servers.Server availabilityzones.ServerAvailabilityZoneExt } - var allServers []ServerWithAZ - allPages, err := servers.List(client, nil).AllPages() if err != nil { panic("Unable to retrieve servers: %s", err) } - err = servers.ExtractServersInto(allPages, &allServers) if err != nil { panic("Unable to extract servers: %s", err) } - for _, server := range allServers { fmt.Println(server.AvailabilityZone) } + +Example of Get Availability Zone Information + + allPages, err := availabilityzones.List(computeClient).AllPages() + if err != nil { + panic(err) + } + + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + panic(err) + } + + for _, zoneInfo := range availabilityZoneInfo { + fmt.Printf("%+v\n", zoneInfo) + } + +Example of Get Detailed Availability Zone Information + + allPages, err := availabilityzones.ListDetail(computeClient).AllPages() + if err != nil { + panic(err) + } + + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + panic(err) + } + + for _, zoneInfo := range availabilityZoneInfo { + fmt.Printf("%+v\n", zoneInfo) + } */ package availabilityzones diff --git a/openstack/compute/v2/extensions/availabilityzones/requests.go b/openstack/compute/v2/extensions/availabilityzones/requests.go new file mode 100644 index 0000000000..f9a2e86e03 --- /dev/null +++ b/openstack/compute/v2/extensions/availabilityzones/requests.go @@ -0,0 +1,20 @@ +package availabilityzones + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List will return the existing availability zones. +func List(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { + return AvailabilityZonePage{pagination.SinglePageBase(r)} + }) +} + +// ListDetail will return the existing availability zones with detailed information. +func ListDetail(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, listDetailURL(client), func(r pagination.PageResult) pagination.Page { + return AvailabilityZonePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/compute/v2/extensions/availabilityzones/results.go b/openstack/compute/v2/extensions/availabilityzones/results.go index ae87404137..82ee42e480 100644 --- a/openstack/compute/v2/extensions/availabilityzones/results.go +++ b/openstack/compute/v2/extensions/availabilityzones/results.go @@ -1,8 +1,70 @@ package availabilityzones -// ServerAvailabilityZoneExt is an extension to the base Server result which -// includes the Availability Zone information. +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ServerExt is an extension to the base Server object type ServerAvailabilityZoneExt struct { // AvailabilityZone is the availabilty zone the server is in. AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` } + +type ServiceState struct { + Active bool `json:"active"` + Available bool `json:"available"` + UpdatedAt time.Time `json:"-"` +} + +// UnmarshalJSON to override default +func (r *ServiceState) UnmarshalJSON(b []byte) error { + type tmp ServiceState + var s struct { + tmp + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ServiceState(s.tmp) + + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} + +type Services map[string]ServiceState + +type Hosts map[string]Services + +// The current state of the availability zone +type ZoneState struct { + // Returns true if the availability zone is available + Available bool `json:"available"` +} + +// AvailabilityZone contains all the information associated with an OpenStack +// AvailabilityZone. +type AvailabilityZone struct { + Hosts Hosts `json:"hosts"` + // The availability zone name + ZoneName string `json:"zoneName"` + ZoneState ZoneState `json:"zoneState"` +} + +type AvailabilityZonePage struct { + pagination.SinglePageBase +} + +func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) { + var s struct { + AvailabilityZoneInfo []AvailabilityZone `json:"availabilityZoneInfo"` + } + err := (r.(AvailabilityZonePage)).ExtractInto(&s) + return s.AvailabilityZoneInfo, err +} diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/doc.go b/openstack/compute/v2/extensions/availabilityzones/testing/doc.go new file mode 100644 index 0000000000..a4408d7a0d --- /dev/null +++ b/openstack/compute/v2/extensions/availabilityzones/testing/doc.go @@ -0,0 +1,2 @@ +// availabilityzones unittests +package testing diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/fixtures.go b/openstack/compute/v2/extensions/availabilityzones/testing/fixtures.go new file mode 100644 index 0000000000..9cc6d46379 --- /dev/null +++ b/openstack/compute/v2/extensions/availabilityzones/testing/fixtures.go @@ -0,0 +1,197 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + az "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const GetOutput = ` +{ + "availabilityZoneInfo": [ + { + "hosts": null, + "zoneName": "nova", + "zoneState": { + "available": true + } + } + ] +} +` + +const GetDetailOutput = ` +{ + "availabilityZoneInfo": [ + { + "hosts": { + "localhost": { + "nova-cert": { + "active": true, + "available": false, + "updated_at": "2017-10-14T17:03:39.000000" + }, + "nova-conductor": { + "active": true, + "available": false, + "updated_at": "2017-10-14T17:04:09.000000" + }, + "nova-consoleauth": { + "active": true, + "available": false, + "updated_at": "2017-10-14T17:04:18.000000" + }, + "nova-scheduler": { + "active": true, + "available": false, + "updated_at": "2017-10-14T17:04:30.000000" + } + }, + "openstack-acc-tests.novalocal": { + "nova-cert": { + "active": true, + "available": true, + "updated_at": "2018-01-04T04:11:19.000000" + }, + "nova-conductor": { + "active": true, + "available": true, + "updated_at": "2018-01-04T04:11:22.000000" + }, + "nova-consoleauth": { + "active": true, + "available": true, + "updated_at": "2018-01-04T04:11:20.000000" + }, + "nova-scheduler": { + "active": true, + "available": true, + "updated_at": "2018-01-04T04:11:23.000000" + } + } + }, + "zoneName": "internal", + "zoneState": { + "available": true + } + }, + { + "hosts": { + "openstack-acc-tests.novalocal": { + "nova-compute": { + "active": true, + "available": true, + "updated_at": "2018-01-04T04:11:23.000000" + } + } + }, + "zoneName": "nova", + "zoneState": { + "available": true + } + } + ] +}` + +var AZResult = []az.AvailabilityZone{ + { + Hosts: nil, + ZoneName: "nova", + ZoneState: az.ZoneState{Available: true}, + }, +} + +var AZDetailResult = []az.AvailabilityZone{ + { + Hosts: az.Hosts{ + "localhost": az.Services{ + "nova-cert": az.ServiceState{ + Active: true, + Available: false, + UpdatedAt: time.Date(2017, 10, 14, 17, 3, 39, 0, time.UTC), + }, + "nova-conductor": az.ServiceState{ + Active: true, + Available: false, + UpdatedAt: time.Date(2017, 10, 14, 17, 4, 9, 0, time.UTC), + }, + "nova-consoleauth": az.ServiceState{ + Active: true, + Available: false, + UpdatedAt: time.Date(2017, 10, 14, 17, 4, 18, 0, time.UTC), + }, + "nova-scheduler": az.ServiceState{ + Active: true, + Available: false, + UpdatedAt: time.Date(2017, 10, 14, 17, 4, 30, 0, time.UTC), + }, + }, + "openstack-acc-tests.novalocal": az.Services{ + "nova-cert": az.ServiceState{ + Active: true, + Available: true, + UpdatedAt: time.Date(2018, 1, 4, 4, 11, 19, 0, time.UTC), + }, + "nova-conductor": az.ServiceState{ + Active: true, + Available: true, + UpdatedAt: time.Date(2018, 1, 4, 4, 11, 22, 0, time.UTC), + }, + "nova-consoleauth": az.ServiceState{ + Active: true, + Available: true, + UpdatedAt: time.Date(2018, 1, 4, 4, 11, 20, 0, time.UTC), + }, + "nova-scheduler": az.ServiceState{ + Active: true, + Available: true, + UpdatedAt: time.Date(2018, 1, 4, 4, 11, 23, 0, time.UTC), + }, + }, + }, + ZoneName: "internal", + ZoneState: az.ZoneState{Available: true}, + }, + { + Hosts: az.Hosts{ + "openstack-acc-tests.novalocal": az.Services{ + "nova-compute": az.ServiceState{ + Active: true, + Available: true, + UpdatedAt: time.Date(2018, 1, 4, 4, 11, 23, 0, time.UTC), + }, + }, + }, + ZoneName: "nova", + ZoneState: az.ZoneState{Available: true}, + }, +} + +// HandleGetSuccessfully configures the test server to respond to a Get request +// for availability zone information. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-availability-zone", 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, GetOutput) + }) +} + +// HandleGetDetailSuccessfully configures the test server to respond to a Get request +// for detailed availability zone information. +func HandleGetDetailSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-availability-zone/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") + fmt.Fprintf(w, GetDetailOutput) + }) +} diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go b/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go new file mode 100644 index 0000000000..8996d366d0 --- /dev/null +++ b/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go @@ -0,0 +1,41 @@ +package testing + +import ( + "testing" + + az "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// Verifies that availability zones can be listed correctly +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetSuccessfully(t) + + allPages, err := az.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + + actual, err := az.ExtractAvailabilityZones(allPages) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, AZResult, actual) +} + +// Verifies that detailed availability zones can be listed correctly +func TestListDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetDetailSuccessfully(t) + + allPages, err := az.ListDetail(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + + actual, err := az.ExtractAvailabilityZones(allPages) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, AZDetailResult, actual) +} diff --git a/openstack/compute/v2/extensions/availabilityzones/urls.go b/openstack/compute/v2/extensions/availabilityzones/urls.go new file mode 100644 index 0000000000..9d99ec74b7 --- /dev/null +++ b/openstack/compute/v2/extensions/availabilityzones/urls.go @@ -0,0 +1,11 @@ +package availabilityzones + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-availability-zone") +} + +func listDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-availability-zone", "detail") +} From 28db6d7217b6079cf27b97a8484c64a00e48452e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 8 Jan 2018 04:10:10 +0000 Subject: [PATCH 0180/2296] Compute v2: Update docs for Availability Zones --- .../compute/v2/extensions/availabilityzones/doc.go | 4 ++++ .../compute/v2/extensions/availabilityzones/results.go | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/openstack/compute/v2/extensions/availabilityzones/doc.go b/openstack/compute/v2/extensions/availabilityzones/doc.go index c5429add0a..29b554d213 100644 --- a/openstack/compute/v2/extensions/availabilityzones/doc.go +++ b/openstack/compute/v2/extensions/availabilityzones/doc.go @@ -9,15 +9,19 @@ Example of Extend server result with Availability Zone Information: servers.Server availabilityzones.ServerAvailabilityZoneExt } + var allServers []ServerWithAZ + allPages, err := servers.List(client, nil).AllPages() if err != nil { panic("Unable to retrieve servers: %s", err) } + err = servers.ExtractServersInto(allPages, &allServers) if err != nil { panic("Unable to extract servers: %s", err) } + for _, server := range allServers { fmt.Println(server.AvailabilityZone) } diff --git a/openstack/compute/v2/extensions/availabilityzones/results.go b/openstack/compute/v2/extensions/availabilityzones/results.go index 82ee42e480..d48a0ea858 100644 --- a/openstack/compute/v2/extensions/availabilityzones/results.go +++ b/openstack/compute/v2/extensions/availabilityzones/results.go @@ -8,12 +8,13 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// ServerExt is an extension to the base Server object +// ServerAvailabilityZoneExt is an extension to the base Server object. type ServerAvailabilityZoneExt struct { // AvailabilityZone is the availabilty zone the server is in. AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` } +// ServiceState represents the state of a service in an AvailabilityZone. type ServiceState struct { Active bool `json:"active"` Available bool `json:"available"` @@ -38,11 +39,14 @@ func (r *ServiceState) UnmarshalJSON(b []byte) error { return nil } +// Services is a map of services contained in an AvailabilityZone. type Services map[string]ServiceState +// Hosts is map of hosts/nodes contained in an AvailabilityZone. +// Each host can have multiple services. type Hosts map[string]Services -// The current state of the availability zone +// ZoneState represents the current state of the availability zone. type ZoneState struct { // Returns true if the availability zone is available Available bool `json:"available"` @@ -61,6 +65,8 @@ type AvailabilityZonePage struct { pagination.SinglePageBase } +// ExtractAvailabilityZones returns a slice of AvailabilityZones contained in a +// single page of results. func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) { var s struct { AvailabilityZoneInfo []AvailabilityZone `json:"availabilityZoneInfo"` From 4f997f57855b1849b8a1508b605111e3aef39d74 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 11 Jan 2018 06:42:52 +0300 Subject: [PATCH 0181/2296] Fix Orchestration TestGetRRFileContents (#709) * Fix "Content-Type" in orchestration env tests We need to use "application/json" instead of "application/jason" * Fix Orchestration's TestGetRRFileContents Fix environments unit test "TestGetRRFileContents" by updating the "expectedParsed" to be a valid representation of a Heat's resource_registry definition. Also update environment's fileMaps to sucessfully apply the "fixFileRefs" method. * Fix Orchestration TestGetRRFileContents comments --- .../v1/stacks/environment_test.go | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/openstack/orchestration/v1/stacks/environment_test.go b/openstack/orchestration/v1/stacks/environment_test.go index a7e3aaee19..6fcc230d43 100644 --- a/openstack/orchestration/v1/stacks/environment_test.go +++ b/openstack/orchestration/v1/stacks/environment_test.go @@ -138,7 +138,7 @@ service_db: // handler for my_env.yaml th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "application/jason") + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, environmentContent) }) @@ -150,7 +150,7 @@ service_db: // handler for my_db.yaml th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "application/jason") + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, dbContent) }) @@ -170,13 +170,20 @@ service_db: th.AssertEquals(t, expectedEnvFilesContent, env.Files[fakeEnvURL]) th.AssertEquals(t, expectedDBFilesContent, env.Files[fakeDBURL]) + // Update env's fileMaps to replace relative filenames by absolute URLs. + env.fileMaps = map[string]string{ + "my_env.yaml": fakeEnvURL, + "my_db.yaml": fakeDBURL, + } env.fixFileRefs() + expectedParsed := map[string]interface{}{ - "resource_registry": "2015-04-30", - "My::WP::Server": fakeEnvURL, - "resources": map[string]interface{}{ - "my_db_server": map[string]interface{}{ - "OS::DBInstance": fakeDBURL, + "resource_registry": map[string]interface{}{ + "My::WP::Server": fakeEnvURL, + "resources": map[string]interface{}{ + "my_db_server": map[string]interface{}{ + "OS::DBInstance": fakeDBURL, + }, }, }, } From 993bca9e3fac8efeaa6cfe74b0b7b00468d8fcf5 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 11 Jan 2018 06:43:56 +0300 Subject: [PATCH 0182/2296] Fix Orchestration TestTemplateParsing (#710) * Fix "Content-Type" in orchestration template tests We need to use "application/json" instead of "application/jason" * Fix flavor field in orchestration fixtures Set "default" field to be a string in "ValidJSONTemplate", "ValidYAMLTemplate", "InvalidTemplateNoVersion", "InvalidEnvironment", "ValidJSONTemplateParsed" orchestration templates test fixtures. That also fixes "TestTemplateParsing" orchestration template unit test. --- openstack/orchestration/v1/stacks/fixtures.go | 10 +++++----- openstack/orchestration/v1/stacks/template_test.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openstack/orchestration/v1/stacks/fixtures.go b/openstack/orchestration/v1/stacks/fixtures.go index d6fd0750f3..58987d4bfd 100644 --- a/openstack/orchestration/v1/stacks/fixtures.go +++ b/openstack/orchestration/v1/stacks/fixtures.go @@ -6,7 +6,7 @@ const ValidJSONTemplate = ` "heat_template_version": "2014-10-16", "parameters": { "flavor": { - "default": 4353, + "default": "debian2G", "description": "Flavor for the server to be created", "hidden": true, "type": "string" @@ -32,7 +32,7 @@ parameters: flavor: type: string description: Flavor for the server to be created - default: 4353 + default: debian2G hidden: true resources: test_server: @@ -49,7 +49,7 @@ parameters: flavor: type: string description: Flavor for the server to be created - default: 4353 + default: debian2G hidden: true resources: test_server: @@ -128,7 +128,7 @@ parameters: flavor: type: string description: Flavor for the server to be created - default: 4353 + default: debian2G hidden: true resources: test_server: @@ -180,7 +180,7 @@ var ValidJSONTemplateParsed = map[string]interface{}{ "heat_template_version": "2014-10-16", "parameters": map[string]interface{}{ "flavor": map[string]interface{}{ - "default": 4353, + "default": "debian2G", "description": "Flavor for the server to be created", "hidden": true, "type": "string", diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go index cbe99ed9cf..1ad9fc9c71 100644 --- a/openstack/orchestration/v1/stacks/template_test.go +++ b/openstack/orchestration/v1/stacks/template_test.go @@ -99,7 +99,7 @@ resources: - {uuid: 11111111-1111-1111-1111-111111111111}` th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "application/jason") + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, myNovaContent) }) From 35ab3f13f69349f99ba8b9c9c36a7031ae2963dd Mon Sep 17 00:00:00 2001 From: David Lyle Date: Tue, 9 Jan 2018 23:24:51 -0700 Subject: [PATCH 0183/2296] Flavor Extra Spec Update --- .../openstack/compute/v2/flavors_test.go | 9 +++++ openstack/compute/v2/flavors/doc.go | 14 +++++++ openstack/compute/v2/flavors/requests.go | 37 +++++++++++++++++++ openstack/compute/v2/flavors/results.go | 6 +++ .../compute/v2/flavors/testing/fixtures.go | 29 ++++++++++++++- .../v2/flavors/testing/requests_test.go | 14 +++++++ openstack/compute/v2/flavors/urls.go | 4 ++ 7 files changed, 112 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index 817fcc5c58..b20a5555a0 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -181,6 +181,15 @@ func TestFlavorExtraSpecsCRUD(t *testing.T) { t.Fatalf("Unable to delete ExtraSpec: %v\n", err) } + updateOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_thread_policy": "CPU-THREAD-POLICY-BETTER", + } + updatedExtraSpec, err := flavors.UpdateExtraSpec(client, flavor.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update flavor extra_specs: %v", err) + } + tools.PrintResource(t, updatedExtraSpec) + allExtraSpecs, err := flavors.ListExtraSpecs(client, flavor.ID).Extract() if err != nil { t.Fatalf("Unable to get flavor extra_specs: %v", err) diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 31e8e2733f..0edc478098 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -99,6 +99,20 @@ Example to Get Extra Specs for a Flavor fmt.Printf("%+v", extraSpecs) +Example to Update Extra Specs for a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + updateOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_thread_policy": "CPU-THREAD-POLICY-UPDATED", + } + updatedExtraSpec, err := flavors.UpdateExtraSpec(computeClient, flavorID, updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", updatedExtraSpec) + Example to Delete an Extra Spec for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 6edae4c9ce..8144dde846 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -231,6 +231,43 @@ func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts C return } +// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateExtraSpecOptsBuilder interface { + ToExtraSpecUpdateMap() (map[string]string, string, error) +} + +// ToExtraSpecUpdateMap assembles a body for an Update request based on the +// contents of a ExtraSpecOpts. +func (opts ExtraSpecsOpts) ToExtraSpecUpdateMap() (map[string]string, string, error) { + if len(opts) != 1 { + err := gophercloud.ErrInvalidInput{} + err.Argument = "flavors.ExtraSpecOpts" + err.Info = "Must have 1 and only one key-value pair" + return nil, "", err + } + + var key string + for k := range opts { + key = k + } + + return opts, key, nil +} + +// UpdateExtraSpec will updates the value of the specified flavor's extra spec for the key in opts. +func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { + b, key, err := opts.ToExtraSpecUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // DeleteExtraSpec will delete the key-value pair with the given key for the given // flavor ID. func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index e0ab64bf37..49e3bed936 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -223,6 +223,12 @@ type GetExtraSpecResult struct { extraSpecResult } +// UpdateExtraSpecResult contains the result of an Update operation. Call its +// Extract method to interpret it as a map[string]interface. +type UpdateExtraSpecResult struct { + extraSpecResult +} + // DeleteExtraSpecResult contains the result of a Delete operation. Call its // ExtractErr method to determine if the call succeeded or failed. type DeleteExtraSpecResult struct { diff --git a/openstack/compute/v2/flavors/testing/fixtures.go b/openstack/compute/v2/flavors/testing/fixtures.go index 0c5486b152..445f769b2b 100644 --- a/openstack/compute/v2/flavors/testing/fixtures.go +++ b/openstack/compute/v2/flavors/testing/fixtures.go @@ -19,13 +19,20 @@ const ExtraSpecsGetBody = ` } ` -// ExtraSpecGetBody provides a GET result of a particular extra_spec for a flavor +// GetExtraSpecBody provides a GET result of a particular extra_spec for a flavor const GetExtraSpecBody = ` { "hw:cpu_policy": "CPU-POLICY" } ` +// UpdatedExtraSpecBody provides an PUT result of a particular updated extra_spec for a flavor +const UpdatedExtraSpecBody = ` +{ + "hw:cpu_policy": "CPU-POLICY-2" +} +` + // ExtraSpecs is the expected extra_specs returned from GET on a flavor's extra_specs var ExtraSpecs = map[string]string{ "hw:cpu_policy": "CPU-POLICY", @@ -37,6 +44,11 @@ var ExtraSpec = map[string]string{ "hw:cpu_policy": "CPU-POLICY", } +// UpdatedExtraSpec is the expected extra_spec returned from PUT on a flavor's extra_specs +var UpdatedExtraSpec = map[string]string{ + "hw:cpu_policy": "CPU-POLICY-2", +} + func HandleExtraSpecsListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors/1/os-extra_specs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -79,6 +91,21 @@ func HandleExtraSpecsCreateSuccessfully(t *testing.T) { }) } +func HandleExtraSpecUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/flavors/1/os-extra_specs/hw:cpu_policy", 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, `{ + "hw:cpu_policy": "CPU-POLICY-2" + }`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdatedExtraSpecBody) + }) +} + func HandleExtraSpecDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/flavors/1/os-extra_specs/hw:cpu_policy", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index f34cb3c1a3..7ad667c482 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -337,6 +337,20 @@ func TestFlavorExtraSpecsCreate(t *testing.T) { th.CheckDeepEquals(t, expected, actual) } +func TestFlavorExtraSpecUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecUpdateSuccessfully(t) + + updateOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_policy": "CPU-POLICY-2", + } + expected := UpdatedExtraSpec + actual, err := flavors.UpdateExtraSpec(fake.ServiceClient(), "1", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + func TestFlavorExtraSpecDelete(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 c9d43c5b46..8620dd78ad 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -40,6 +40,10 @@ func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id, "os-extra_specs") } +func extraSpecUpdateURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("flavors", id, "os-extra_specs", key) +} + func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string { return client.ServiceURL("flavors", id, "os-extra_specs", key) } From 57f1c660ea6fe700cde9d9c964c820ec985c731e Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 12 Jan 2018 06:17:04 +0300 Subject: [PATCH 0184/2296] Add Neutron subnetpool update support (#706) * Add Neutron subnetpool update support Add support to update a Neutron subnetpool through a PUT request on /v2.0/subnetpools. The same command with OpenStack CLI is done with: openstack subnet pool set or neutron subnetpool-update * Add a documentation about subnetpool updating * Add subnetpool update to acceptance tests * Cleanup Neutron subnetpools docs Remove some garbage dots from titles because we don't really need them in the documentation. * Update Neutron subnetpools update fileds types We need to use a "*string" to "AddressScopeID" and "Description" fileds, "*bool" to a IsDefault field and "*int" to a DefaultQuota field. That is because we wan't to have a possibility to update those fields to empty or false or zero values. * Fix TestSubnetPoolsCRUD update test Variable newSubnetPool is new so it should be initialized. --- .../subnetpools/subnetpools_test.go | 12 ++++ .../v2/extensions/subnetpools/doc.go | 19 ++++- .../v2/extensions/subnetpools/requests.go | 71 +++++++++++++++++++ .../v2/extensions/subnetpools/results.go | 6 ++ .../subnetpools/testing/fixtures.go | 43 +++++++++++ .../subnetpools/testing/requests_test.go | 46 ++++++++++++ .../v2/extensions/subnetpools/urls.go | 4 ++ 7 files changed, 199 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index d419ee9bea..8dc50b6a39 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -23,6 +23,18 @@ func TestSubnetPoolsCRUD(t *testing.T) { } tools.PrintResource(t, subnetPool) + + newName := tools.RandomString("TESTACC-", 8) + updateOpts := &subnetpools.UpdateOpts{ + Name: newName, + } + + newSubnetPool, err := subnetpools.Update(client, subnetPool.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update the subnetpool: %v", err) + } + + tools.PrintResource(t, newSubnetPool) } func TestSubnetPoolsList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/subnetpools/doc.go b/openstack/networking/v2/extensions/subnetpools/doc.go index bef874546f..3e8bae90df 100644 --- a/openstack/networking/v2/extensions/subnetpools/doc.go +++ b/openstack/networking/v2/extensions/subnetpools/doc.go @@ -1,7 +1,7 @@ /* Package subnetpools provides the ability to retrieve and manage subnetpools through the Neutron API. -Example of Listing Subnetpools. +Example of Listing Subnetpools listOpts := subnets.ListOpts{ IPVersion: 6, @@ -29,7 +29,7 @@ Example to Get a Subnetpool panic(err) } -Example to Create a new Subnetpool. +Example to Create a new Subnetpool subnetPoolName := "private_pool" subnetPoolPrefixes := []string{ @@ -45,5 +45,20 @@ Example to Create a new Subnetpool. if err != nil { panic(err) } + +Example to Update a Subnetpool + + subnetPoolID := "099546ca-788d-41e5-a76d-17d8cd282d3e" + updateOpts := networks.UpdateOpts{ + Prefixes: []string{ + "fdf7:b13d:dead:beef::/64", + }, + MaxPrefixLen: 72, + } + + subnetPool, err := subnetpools.Update(networkClient, subnetPoolID, updateOpts).Extract() + if err != nil { + panic(err) + } */ package subnetpools diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index 4828e05aa1..9bbd3957e9 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -145,3 +145,74 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToSubnetPoolUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update a network. +type UpdateOpts struct { + // Name is the human-readable name of the subnetpool. + Name string `json:"name,omitempty"` + + // DefaultQuota is the per-project quota on the prefix space + // that can be allocated from the subnetpool for project subnets. + DefaultQuota *int `json:"default_quota,omitempty"` + + // TenantID is the id of the Identity project. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the id of the Identity project. + ProjectID string `json:"project_id,omitempty"` + + // Prefixes is the list of subnet prefixes to assign to the subnetpool. + // Neutron API merges adjacent prefixes and treats them as a single prefix. + // Each subnet prefix must be unique among all subnet prefixes in all subnetpools + // that are associated with the address scope. + Prefixes []string `json:"prefixes,omitempty"` + + // DefaultPrefixLen is yhe size of the prefix to allocate when the cidr + // or prefixlen attributes are omitted when you create the subnet. + // Defaults to the MinPrefixLen. + DefaultPrefixLen int `json:"default_prefixlen,omitempty"` + + // MinPrefixLen is the smallest prefix that can be allocated from a subnetpool. + // For IPv4 subnetpools, default is 8. + // For IPv6 subnetpools, default is 64. + MinPrefixLen int `json:"min_prefixlen,omitempty"` + + // MaxPrefixLen is the maximum prefix size that can be allocated from the subnetpool. + // For IPv4 subnetpools, default is 32. + // For IPv6 subnetpools, default is 128. + MaxPrefixLen int `json:"max_prefixlen,omitempty"` + + // AddressScopeID is the Neutron address scope to assign to the subnetpool. + AddressScopeID *string `json:"address_scope_id,omitempty"` + + // Description is thehuman-readable description for the resource. + Description *string `json:"description,omitempty"` + + // IsDefault indicates if the subnetpool is default pool or not. + IsDefault *bool `json:"is_default,omitempty"` +} + +// ToSubnetPoolUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToSubnetPoolUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "subnetpool") +} + +// Update accepts a UpdateOpts struct and updates an existing subnetpool using the +// values provided. +func Update(c *gophercloud.ServiceClient, subnetPoolID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToSubnetPoolUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(updateURL(c, subnetPoolID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index ff3dc38009..0d60ff3e20 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -34,6 +34,12 @@ type CreateResult struct { commonResult } +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a SubnetPool. +type UpdateResult struct { + commonResult +} + // SubnetPool represents a Neutron subnetpool. // A subnetpool is a pool of addresses from which subnets can be allocated. type SubnetPool struct { diff --git a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go index fd279f1d38..f70b41773e 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go @@ -212,3 +212,46 @@ const SubnetPoolCreateResult = ` } } ` + +const SubnetPoolUpdateRequest = ` +{ + "subnetpool": { + "name": "new_subnetpool_name", + "prefixes": [ + "10.11.12.0/24", + "10.24.0.0/16" + ], + "max_prefixlen": 16, + "address_scope_id": "", + "default_quota": 0, + "description": "" + } +} +` + +const SubnetPoolUpdateResponse = ` +{ + "subnetpool": { + "address_scope_id": null, + "created_at": "2018-01-03T07:21:34Z", + "default_prefixlen": 8, + "default_quota": null, + "description": null, + "id": "099546ca-788d-41e5-a76d-17d8cd282d3e", + "ip_version": 4, + "is_default": true, + "max_prefixlen": 16, + "min_prefixlen": 8, + "name": "new_subnetpool_name", + "prefixes": [ + "10.8.0.0/16", + "10.11.12.0/24", + "10.24.0.0/16" + ], + "revision_number": 2, + "shared": false, + "tenant_id": "1e2b9857295a4a3e841809ef492812c5", + "updated_at": "2018-01-05T09:56:56Z" + } +} +` diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index b80d7ef032..ec02227a79 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -129,3 +129,49 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, s.AddressScopeID, "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3") th.AssertEquals(t, s.Description, "ipv4 prefixes") } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/subnetpools/099546ca-788d-41e5-a76d-17d8cd282d3e", 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, SubnetPoolUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, SubnetPoolUpdateResponse) + }) + + nullString := "" + nullInt := 0 + updateOpts := subnetpools.UpdateOpts{ + Name: "new_subnetpool_name", + Prefixes: []string{ + "10.11.12.0/24", + "10.24.0.0/16", + }, + MaxPrefixLen: 16, + AddressScopeID: &nullString, + DefaultQuota: &nullInt, + Description: &nullString, + } + n, err := subnetpools.Update(fake.ServiceClient(), "099546ca-788d-41e5-a76d-17d8cd282d3e", updateOpts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Name, "new_subnetpool_name") + th.AssertDeepEquals(t, n.Prefixes, []string{ + "10.8.0.0/16", + "10.11.12.0/24", + "10.24.0.0/16", + }) + th.AssertEquals(t, n.MaxPrefixLen, 16) + th.AssertEquals(t, n.ID, "099546ca-788d-41e5-a76d-17d8cd282d3e") + th.AssertEquals(t, n.AddressScopeID, "") + th.AssertEquals(t, n.DefaultQuota, 0) + th.AssertEquals(t, n.Description, "") +} diff --git a/openstack/networking/v2/extensions/subnetpools/urls.go b/openstack/networking/v2/extensions/subnetpools/urls.go index 5ba98ae359..2b02dd6a93 100644 --- a/openstack/networking/v2/extensions/subnetpools/urls.go +++ b/openstack/networking/v2/extensions/subnetpools/urls.go @@ -23,3 +23,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From df0b021b1d39cc2f8f12a333615200a3234e9f41 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 13 Jan 2018 23:50:34 +0300 Subject: [PATCH 0185/2296] Add get request in subnetpools acceptance tests --- .../v2/extensions/subnetpools/subnetpools_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index 8dc50b6a39..eeb3e19afb 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -29,11 +29,16 @@ func TestSubnetPoolsCRUD(t *testing.T) { Name: newName, } - newSubnetPool, err := subnetpools.Update(client, subnetPool.ID, updateOpts).Extract() + _, err = subnetpools.Update(client, subnetPool.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update the subnetpool: %v", err) } + newSubnetPool, err := subnetpools.Get(client, subnetPool.ID).Extract() + if err != nil { + t.Fatalf("Unable to get subnetpool: %v", err) + } + tools.PrintResource(t, newSubnetPool) } From 9c7c97cb37e9f6724d09dad32e681d97dca37d6d Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 14 Jan 2018 00:08:39 +0300 Subject: [PATCH 0186/2296] Implement Neutron subnetpools delete support Add a Delete request, unit test and acceptance tests with acceptance reusable "DeleteSubnetPool" function. --- .../v2/extensions/subnetpools/subnetpools.go | 13 +++++++++++++ .../v2/extensions/subnetpools/subnetpools_test.go | 1 + .../networking/v2/extensions/subnetpools/doc.go | 8 ++++++++ .../v2/extensions/subnetpools/requests.go | 6 ++++++ .../v2/extensions/subnetpools/results.go | 6 ++++++ .../subnetpools/testing/requests_test.go | 14 ++++++++++++++ .../networking/v2/extensions/subnetpools/urls.go | 4 ++++ 7 files changed, 52 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go index 028bb3d2d9..fdf6318d52 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go @@ -30,3 +30,16 @@ func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetp t.Logf("Successfully created the subnetpool.") return subnetPool, nil } + +// DeleteSubnetPool will delete a subnetpool with a specified ID. +// A fatal error will occur if the delete was not successful. +func DeleteSubnetPool(t *testing.T, client *gophercloud.ServiceClient, subnetPoolID string) { + t.Logf("Attempting to delete the subnetpool: %s", subnetPoolID) + + err := subnetpools.Delete(client, subnetPoolID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete subnetpool %s: %v", subnetPoolID, err) + } + + t.Logf("Deleted subnetpool: %s", subnetPoolID) +} diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index 8dc50b6a39..6d51f19091 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -21,6 +21,7 @@ func TestSubnetPoolsCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create a subnetpool: %v", err) } + defer DeleteSubnetPool(t, client, subnetPool.ID) tools.PrintResource(t, subnetPool) diff --git a/openstack/networking/v2/extensions/subnetpools/doc.go b/openstack/networking/v2/extensions/subnetpools/doc.go index 3e8bae90df..2a9fe63dd4 100644 --- a/openstack/networking/v2/extensions/subnetpools/doc.go +++ b/openstack/networking/v2/extensions/subnetpools/doc.go @@ -60,5 +60,13 @@ Example to Update a Subnetpool if err != nil { panic(err) } + +Example to Delete a Subnetpool + + subnetPoolID := "23d5d3f7-9dfa-4f73-b72b-8b0b0063ec55" + err := subnetpools.Delete(networkClient, subnetPoolID).ExtractErr() + if err != nil { + panic(err) + } */ package subnetpools diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index 9bbd3957e9..61f660d5ab 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -216,3 +216,9 @@ func Update(c *gophercloud.ServiceClient, subnetPoolID string, opts UpdateOptsBu }) return } + +// Delete accepts a unique ID and deletes the subnetpool associated with it. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(deleteURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index 0d60ff3e20..a490f05401 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -40,6 +40,12 @@ 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 +} + // SubnetPool represents a Neutron subnetpool. // A subnetpool is a pool of addresses from which subnets can be allocated. type SubnetPool struct { diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index ec02227a79..d4f62319ad 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -175,3 +175,17 @@ func TestUpdate(t *testing.T) { th.AssertEquals(t, n.DefaultQuota, 0) th.AssertEquals(t, n.Description, "") } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/subnetpools/099546ca-788d-41e5-a76d-17d8cd282d3e", 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) + }) + + res := subnetpools.Delete(fake.ServiceClient(), "099546ca-788d-41e5-a76d-17d8cd282d3e") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/subnetpools/urls.go b/openstack/networking/v2/extensions/subnetpools/urls.go index 2b02dd6a93..a05062c96d 100644 --- a/openstack/networking/v2/extensions/subnetpools/urls.go +++ b/openstack/networking/v2/extensions/subnetpools/urls.go @@ -27,3 +27,7 @@ func createURL(c *gophercloud.ServiceClient) string { func updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From 7e80bca56edbda494d00ab082547c16086269bac Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy Date: Sun, 14 Jan 2018 05:48:30 +0300 Subject: [PATCH 0187/2296] Compute v2: Show Hypervisor Statistics (#720) * Hypervisors statistics support * Add ability to get hypervisors stats * Add unit tests * Update doc.go * Add acceptance tests * Style fixes * Rename few vars --- .../openstack/compute/v2/hypervisors_test.go | 14 +++++ .../compute/v2/extensions/hypervisors/doc.go | 14 ++++- .../v2/extensions/hypervisors/requests.go | 8 +++ .../v2/extensions/hypervisors/results.go | 54 +++++++++++++++++++ .../hypervisors/testing/fixtures.go | 43 +++++++++++++++ .../hypervisors/testing/requests_test.go | 12 +++++ .../compute/v2/extensions/hypervisors/urls.go | 4 ++ 7 files changed, 147 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/compute/v2/hypervisors_test.go b/acceptance/openstack/compute/v2/hypervisors_test.go index 627dc76345..c9e9071358 100644 --- a/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/acceptance/openstack/compute/v2/hypervisors_test.go @@ -30,3 +30,17 @@ func TestHypervisorsList(t *testing.T) { tools.PrintResource(t, h) } } + +func TestHypervisorsStatistics(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + hypervisorsStats, err := hypervisors.GetStatistics(client).Extract() + if err != nil { + t.Fatalf("Unable to get hypervisors statistics: %v", err) + } + + tools.PrintResource(t, hypervisorsStats) +} diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index cf603a9f32..05c8c5df5b 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -1,6 +1,6 @@ /* -Package hypervisors returns details about the hypervisors in the OpenStack -cloud. +Package hypervisors returns details about the hypervisors and shows +summary statistics for all hypervisors over all compute nodes in the OpenStack cloud. Example of Retrieving Details of All Hypervisors @@ -17,5 +17,15 @@ Example of Retrieving Details of All Hypervisors for _, hypervisor := range allHypervisors { fmt.Printf("%+v\n", hypervisor) } + +Example of Show Hypervisor Statistics + + hypervisorsStatistics, err := hypervisors.GetStatistics(computeClient).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", hypervisorsStatistics) + */ package hypervisors diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/extensions/hypervisors/requests.go index 57cc19a71f..e8151c06d6 100644 --- a/openstack/compute/v2/extensions/hypervisors/requests.go +++ b/openstack/compute/v2/extensions/hypervisors/requests.go @@ -11,3 +11,11 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { return HypervisorPage{pagination.SinglePageBase(r)} }) } + +// Statistics makes a request against the API to get hypervisors statistics. +func GetStatistics(client *gophercloud.ServiceClient) (r StatisticsResult) { + _, r.Err = client.Get(hypervisorsStatisticsURL(client), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index d4e87de083..c8b1cbaa91 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -189,3 +190,56 @@ func ExtractHypervisors(p pagination.Page) ([]Hypervisor, error) { err := (p.(HypervisorPage)).ExtractInto(&h) return h.Hypervisors, err } + +// Statistics represents a summary statistics for all enabled +// hypervisors over all compute nodes in the OpenStack cloud. +type Statistics struct { + // The number of hypervisors. + Count int `json:"count"` + + // The current_workload is the number of tasks the hypervisor is responsible for + CurrentWorkload int `json:"current_workload"` + + // The actual free disk on this hypervisor(in GB). + DiskAvailableLeast int `json:"disk_available_least"` + + // The free disk remaining on this hypervisor(in GB). + FreeDiskGB int `json:"free_disk_gb"` + + // The free RAM in this hypervisor(in MB). + FreeRamMB int `json:"free_ram_mb"` + + // The disk in this hypervisor(in GB). + LocalGB int `json:"local_gb"` + + // The disk used in this hypervisor(in GB). + LocalGBUsed int `json:"local_gb_used"` + + // The memory of this hypervisor(in MB). + MemoryMB int `json:"memory_mb"` + + // The memory used in this hypervisor(in MB). + MemoryMBUsed int `json:"memory_mb_used"` + + // The total number of running vms on all hypervisors. + RunningVMs int `json:"running_vms"` + + // The number of vcpu in this hypervisor. + VCPUs int `json:"vcpus"` + + // The number of vcpu used in this hypervisor. + VCPUsUsed int `json:"vcpus_used"` +} + +type StatisticsResult struct { + gophercloud.Result +} + +// Extract interprets any StatisticsResult as a Statistics, if possible. +func (r StatisticsResult) Extract() (*Statistics, error) { + var s struct { + Stats Statistics `json:"hypervisor_statistics"` + } + err := r.ExtractInto(&s) + return &s.Stats, err +} diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go index 45a32de18d..faeea676b7 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go @@ -83,6 +83,25 @@ const HypervisorListBody = ` ] }` +const HypervisorsStatisticsBody = ` +{ + "hypervisor_statistics": { + "count": 1, + "current_workload": 0, + "disk_available_least": 0, + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "vcpus": 2, + "vcpus_used": 0 + } +} +` + var ( HypervisorFake = hypervisors.Hypervisor{ CPUInfo: hypervisors.CPUInfo{ @@ -123,8 +142,32 @@ var ( VCPUs: 1, VCPUsUsed: 0, } + HypervisorsStatisticsExpected = hypervisors.Statistics{ + Count: 1, + CurrentWorkload: 0, + DiskAvailableLeast: 0, + FreeDiskGB: 1028, + FreeRamMB: 7680, + LocalGB: 1028, + LocalGBUsed: 0, + MemoryMB: 8192, + MemoryMBUsed: 512, + RunningVMs: 0, + VCPUs: 2, + VCPUsUsed: 0, + } ) +func HandleHypervisorsStatisticsSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/os-hypervisors/statistics", 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") + fmt.Fprintf(w, HypervisorsStatisticsBody) + }) +} + func HandleHypervisorListSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go index 1da3b1de50..0c200ab6cd 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go @@ -51,3 +51,15 @@ func TestListAllHypervisors(t *testing.T) { testhelper.CheckDeepEquals(t, HypervisorFake, actual[0]) testhelper.CheckDeepEquals(t, HypervisorFake, actual[1]) } + +func TestHypervisorsStatistics(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleHypervisorsStatisticsSuccessfully(t) + + expected := HypervisorsStatisticsExpected + + actual, err := hypervisors.GetStatistics(client.ServiceClient()).Extract() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/hypervisors/urls.go b/openstack/compute/v2/extensions/hypervisors/urls.go index 5e6f679e96..5c51db96e3 100644 --- a/openstack/compute/v2/extensions/hypervisors/urls.go +++ b/openstack/compute/v2/extensions/hypervisors/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func hypervisorsListDetailURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-hypervisors", "detail") } + +func hypervisorsStatisticsURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-hypervisors", "statistics") +} From 050e933d542f7a12e43034f0f456f770d4c475b3 Mon Sep 17 00:00:00 2001 From: Ildar Svetlov Date: Tue, 16 Jan 2018 07:21:11 +0400 Subject: [PATCH 0188/2296] Compute service list support (#716) * Add Compute service list support * Remove ForcedDown field, add UpdatedAt field --- .../openstack/compute/v2/services_test.go | 32 +++++ .../compute/v2/extensions/services/doc.go | 22 ++++ .../v2/extensions/services/requests.go | 13 ++ .../compute/v2/extensions/services/results.go | 73 +++++++++++ .../extensions/services/testing/fixtures.go | 123 ++++++++++++++++++ .../services/testing/requests_test.go | 42 ++++++ .../compute/v2/extensions/services/urls.go | 7 + 7 files changed, 312 insertions(+) create mode 100644 acceptance/openstack/compute/v2/services_test.go create mode 100644 openstack/compute/v2/extensions/services/doc.go create mode 100644 openstack/compute/v2/extensions/services/requests.go create mode 100644 openstack/compute/v2/extensions/services/results.go create mode 100644 openstack/compute/v2/extensions/services/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/services/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/services/urls.go diff --git a/acceptance/openstack/compute/v2/services_test.go b/acceptance/openstack/compute/v2/services_test.go new file mode 100644 index 0000000000..b949b70fa2 --- /dev/null +++ b/acceptance/openstack/compute/v2/services_test.go @@ -0,0 +1,32 @@ +// +build acceptance compute services + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" +) + +func TestServicesList(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + allPages, err := services.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to list services: %v", err) + } + + allServices, err := services.ExtractServices(allPages) + if err != nil { + t.Fatalf("Unable to extract services") + } + + for _, service := range allServices { + tools.PrintResource(t, service) + } +} diff --git a/openstack/compute/v2/extensions/services/doc.go b/openstack/compute/v2/extensions/services/doc.go new file mode 100644 index 0000000000..2d38c42a95 --- /dev/null +++ b/openstack/compute/v2/extensions/services/doc.go @@ -0,0 +1,22 @@ +/* +Package services returns information about the compute services in the OpenStack +cloud. + +Example of Retrieving list of all services + + allPages, err := services.List(computeClient).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/compute/v2/extensions/services/requests.go b/openstack/compute/v2/extensions/services/requests.go new file mode 100644 index 0000000000..d2e31f82d3 --- /dev/null +++ b/openstack/compute/v2/extensions/services/requests.go @@ -0,0 +1,13 @@ +package services + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List makes a request against the API to list services. +func List(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { + return ServicePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/compute/v2/extensions/services/results.go b/openstack/compute/v2/extensions/services/results.go new file mode 100644 index 0000000000..1ffc99cf9d --- /dev/null +++ b/openstack/compute/v2/extensions/services/results.go @@ -0,0 +1,73 @@ +package services + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Service represents a Compute service in the OpenStack cloud. +type Service struct { + // The binary name of the service. + Binary string `json:"binary"` + + // The reason for disabling a service. + DisabledReason string `json:"disabled_reason"` + + // 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 enabled or disabled. + Status string `json:"status"` + + // The date and time when the resource was 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/compute/v2/extensions/services/testing/fixtures.go b/openstack/compute/v2/extensions/services/testing/fixtures.go new file mode 100644 index 0000000000..79e704a7a7 --- /dev/null +++ b/openstack/compute/v2/extensions/services/testing/fixtures.go @@ -0,0 +1,123 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ServiceListBody is sample response to the List call +const ServiceListBody = ` +{ + "services": [ + { + "id": 1, + "binary": "nova-scheduler", + "disabled_reason": "test1", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:02.000000", + "forced_down": false, + "zone": "internal" + }, + { + "id": 2, + "binary": "nova-compute", + "disabled_reason": "test2", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:05.000000", + "forced_down": false, + "zone": "nova" + }, + { + "id": 3, + "binary": "nova-scheduler", + "disabled_reason": null, + "host": "host2", + "state": "down", + "status": "enabled", + "updated_at": "2012-09-19T06:55:34.000000", + "forced_down": false, + "zone": "internal" + }, + { + "id": 4, + "binary": "nova-compute", + "disabled_reason": "test4", + "host": "host2", + "state": "down", + "status": "disabled", + "updated_at": "2012-09-18T08:03:38.000000", + "forced_down": false, + "zone": "nova" + } + ] +} +` + +// First service from the ServiceListBody +var FirstFakeService = services.Service{ + Binary: "nova-scheduler", + DisabledReason: "test1", + Host: "host1", + ID: 1, + State: "up", + Status: "disabled", + UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), + Zone: "internal", +} + +// Second service from the ServiceListBody +var SecondFakeService = services.Service{ + Binary: "nova-compute", + DisabledReason: "test2", + Host: "host1", + ID: 2, + State: "up", + Status: "disabled", + UpdatedAt: time.Date(2012, 10, 29, 13, 42, 5, 0, time.UTC), + Zone: "nova", +} + +// Third service from the ServiceListBody +var ThirdFakeService = services.Service{ + Binary: "nova-scheduler", + DisabledReason: "", + Host: "host2", + ID: 3, + State: "down", + Status: "enabled", + UpdatedAt: time.Date(2012, 9, 19, 6, 55, 34, 0, time.UTC), + Zone: "internal", +} + +// Fourth service from the ServiceListBody +var FourthFakeService = services.Service{ + Binary: "nova-compute", + DisabledReason: "test4", + Host: "host2", + ID: 4, + State: "down", + Status: "disabled", + UpdatedAt: time.Date(2012, 9, 18, 8, 3, 38, 0, time.UTC), + Zone: "nova", +} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-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/compute/v2/extensions/services/testing/requests_test.go b/openstack/compute/v2/extensions/services/testing/requests_test.go new file mode 100644 index 0000000000..7f998814be --- /dev/null +++ b/openstack/compute/v2/extensions/services/testing/requests_test.go @@ -0,0 +1,42 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/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()).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := services.ExtractServices(page) + if err != nil { + return false, err + } + + if len(actual) != 4 { + t.Fatalf("Expected 4 services, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) + testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) + testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) + testhelper.CheckDeepEquals(t, FourthFakeService, actual[3]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} diff --git a/openstack/compute/v2/extensions/services/urls.go b/openstack/compute/v2/extensions/services/urls.go new file mode 100644 index 0000000000..61d794007e --- /dev/null +++ b/openstack/compute/v2/extensions/services/urls.go @@ -0,0 +1,7 @@ +package services + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-services") +} From 936e48acce6798b2efd69db8ac3c8ffe7f67784f Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy Date: Wed, 17 Jan 2018 05:52:53 +0300 Subject: [PATCH 0189/2296] Compute v2: Get Hypervisor Details (#722) * Add support to get specific hypervisor by ID * Add unit tests * Update doc.go * Add acceptance tests * Fix conversion `int` -> `string` --- .../openstack/compute/v2/hypervisors_test.go | 39 +++++++++++++ .../compute/v2/extensions/hypervisors/doc.go | 14 ++++- .../v2/extensions/hypervisors/requests.go | 11 ++++ .../v2/extensions/hypervisors/results.go | 13 +++++ .../hypervisors/testing/fixtures.go | 56 +++++++++++++++++++ .../hypervisors/testing/requests_test.go | 12 ++++ .../compute/v2/extensions/hypervisors/urls.go | 4 ++ 7 files changed, 147 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/compute/v2/hypervisors_test.go b/acceptance/openstack/compute/v2/hypervisors_test.go index c9e9071358..567cfaac30 100644 --- a/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/acceptance/openstack/compute/v2/hypervisors_test.go @@ -3,8 +3,10 @@ package v2 import ( + "fmt" "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" @@ -31,6 +33,25 @@ func TestHypervisorsList(t *testing.T) { } } +func TestHypervisorsGet(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + hypervisorID, err := getHypervisorID(t, client) + if err != nil { + t.Fatal(err) + } + + hypervisor, err := hypervisors.Get(client, hypervisorID).Extract() + if err != nil { + t.Fatalf("Unable to get hypervisor: %v", err) + } + + tools.PrintResource(t, hypervisor) +} + func TestHypervisorsStatistics(t *testing.T) { client, err := clients.NewComputeV2Client() if err != nil { @@ -44,3 +65,21 @@ func TestHypervisorsStatistics(t *testing.T) { tools.PrintResource(t, hypervisorsStats) } + +func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (int, error) { + allPages, err := hypervisors.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to list hypervisors: %v", err) + } + + allHypervisors, err := hypervisors.ExtractHypervisors(allPages) + if err != nil { + t.Fatalf("Unable to extract hypervisors") + } + + for _, h := range allHypervisors { + return h.ID, nil + } + + return 0, fmt.Errorf("Unable to get hypervisor ID") +} diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index 05c8c5df5b..9d72f8fe61 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -1,6 +1,16 @@ /* -Package hypervisors returns details about the hypervisors and shows -summary statistics for all hypervisors over all compute nodes in the OpenStack cloud. +Package hypervisors returns details about list of hypervisors, shows details for a hypervisor +and shows summary statistics for all hypervisors over all compute nodes in the OpenStack cloud. + +Example of Show Hypervisor Details + + hypervisorID := 42 + hypervisor, err := hypervisors.Get(computeClient, 42).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", hypervisor) Example of Retrieving Details of All Hypervisors diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/extensions/hypervisors/requests.go index e8151c06d6..f5efff85c7 100644 --- a/openstack/compute/v2/extensions/hypervisors/requests.go +++ b/openstack/compute/v2/extensions/hypervisors/requests.go @@ -1,6 +1,8 @@ package hypervisors import ( + "strconv" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -19,3 +21,12 @@ func GetStatistics(client *gophercloud.ServiceClient) (r StatisticsResult) { }) return } + +// Get makes a request against the API to get details for specific hypervisor. +func Get(client *gophercloud.ServiceClient, hypervisorID int) (r HypervisorResult) { + v := strconv.Itoa(hypervisorID) + _, r.Err = client.Get(hypervisorsGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index c8b1cbaa91..a35a0341ce 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -191,6 +191,19 @@ func ExtractHypervisors(p pagination.Page) ([]Hypervisor, error) { return h.Hypervisors, err } +type HypervisorResult struct { + gophercloud.Result +} + +// Extract interprets any HypervisorResult as a Hypervisor, if possible. +func (r HypervisorResult) Extract() (*Hypervisor, error) { + var s struct { + Hypervisor Hypervisor `json:"hypervisor"` + } + err := r.ExtractInto(&s) + return &s.Hypervisor, err +} + // Statistics represents a summary statistics for all enabled // hypervisors over all compute nodes in the OpenStack cloud. type Statistics struct { diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go index faeea676b7..d261fa658f 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go @@ -3,6 +3,7 @@ package testing import ( "fmt" "net/http" + "strconv" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" @@ -102,6 +103,50 @@ const HypervisorsStatisticsBody = ` } ` +const HypervisorGetBody = ` +{ + "hypervisor":{ + "cpu_info":{ + "arch":"x86_64", + "model":"Nehalem", + "vendor":"Intel", + "features":[ + "pge", + "clflush" + ], + "topology":{ + "cores":1, + "threads":1, + "sockets":4 + } + }, + "current_workload":0, + "status":"enabled", + "state":"up", + "disk_available_least":0, + "host_ip":"1.1.1.1", + "free_disk_gb":1028, + "free_ram_mb":7680, + "hypervisor_hostname":"fake-mini", + "hypervisor_type":"fake", + "hypervisor_version":2002000, + "id":1, + "local_gb":1028, + "local_gb_used":0, + "memory_mb":8192, + "memory_mb_used":512, + "running_vms":0, + "service":{ + "host":"e6a37ee802d74863ab8b91ade8f12a67", + "id":2, + "disabled_reason":null + }, + "vcpus":1, + "vcpus_used":0 + } +} +` + var ( HypervisorFake = hypervisors.Hypervisor{ CPUInfo: hypervisors.CPUInfo{ @@ -177,3 +222,14 @@ func HandleHypervisorListSuccessfully(t *testing.T) { fmt.Fprintf(w, HypervisorListBody) }) } + +func HandleHypervisorGetSuccessfully(t *testing.T) { + v := strconv.Itoa(HypervisorFake.ID) + testhelper.Mux.HandleFunc("/os-hypervisors/"+v, 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") + fmt.Fprintf(w, HypervisorGetBody) + }) +} diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go index 0c200ab6cd..b89f6e76c8 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go @@ -63,3 +63,15 @@ func TestHypervisorsStatistics(t *testing.T) { testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } + +func TestGetHypervisor(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleHypervisorGetSuccessfully(t) + + expected := HypervisorFake + + actual, err := hypervisors.Get(client.ServiceClient(), expected.ID).Extract() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/hypervisors/urls.go b/openstack/compute/v2/extensions/hypervisors/urls.go index 5c51db96e3..19107fe53d 100644 --- a/openstack/compute/v2/extensions/hypervisors/urls.go +++ b/openstack/compute/v2/extensions/hypervisors/urls.go @@ -9,3 +9,7 @@ func hypervisorsListDetailURL(c *gophercloud.ServiceClient) string { func hypervisorsStatisticsURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-hypervisors", "statistics") } + +func hypervisorsGetURL(c *gophercloud.ServiceClient, hypervisorID string) string { + return c.ServiceURL("os-hypervisors", hypervisorID) +} From 1a43566306cb8cebad8cae85c67b15b3c254f316 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 18 Jan 2018 04:45:06 +0000 Subject: [PATCH 0190/2296] Prevent Recursive BuildRequestBody This commit prevents Gophercloud from performing a recursive BuildRequestBody when the field's JSON tag is "-". --- params.go | 7 ++++++- testing/params_test.go | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/params.go b/params.go index 687af3dc0c..28ad906856 100644 --- a/params.go +++ b/params.go @@ -115,10 +115,15 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, } } + jsonTag := f.Tag.Get("json") + if jsonTag == "-" { + continue + } + if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) { if zero { //fmt.Printf("value before change: %+v\n", optsValue.Field(i)) - if jsonTag := f.Tag.Get("json"); jsonTag != "" { + if jsonTag != "" { jsonTagPieces := strings.Split(jsonTag, ",") if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" { if v.CanSet() { diff --git a/testing/params_test.go b/testing/params_test.go index acf392f2ab..18d6704d95 100644 --- a/testing/params_test.go +++ b/testing/params_test.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "testing" + "time" "github.com/gophercloud/gophercloud" th "github.com/gophercloud/gophercloud/testhelper" @@ -254,4 +255,22 @@ func TestBuildRequestBody(t *testing.T) { _, err := gophercloud.BuildRequestBody(failCase.opts, "auth") th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err)) } + + createdAt := time.Date(2018, 1, 4, 10, 00, 12, 0, time.UTC) + var complexFields = struct { + Username string `json:"username" required:"true"` + CreatedAt *time.Time `json:"-"` + }{ + Username: "jdoe", + CreatedAt: &createdAt, + } + + expectedComplexFields := map[string]interface{}{ + "username": "jdoe", + } + + actual, err := gophercloud.BuildRequestBody(complexFields, "") + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expectedComplexFields, actual) + } From 8a6dfa8264e8b64523272c7a205e5f08bb6c118f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 18 Jan 2018 20:43:55 -0700 Subject: [PATCH 0191/2296] Compute v2: Flavor Access Remove (#688) * Compute v2: Flavor Access Remove * Correcting method names --- .../openstack/compute/v2/flavors_test.go | 13 +++++++ openstack/compute/v2/flavors/doc.go | 13 +++++++ openstack/compute/v2/flavors/requests.go | 38 +++++++++++++++++-- openstack/compute/v2/flavors/results.go | 8 +++- .../v2/flavors/testing/requests_test.go | 38 +++++++++++++++++++ 5 files changed, 105 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index b20a5555a0..b7768b380a 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -152,6 +152,19 @@ func TestFlavorAccessCRUD(t *testing.T) { for _, access := range accessList { tools.PrintResource(t, access) } + + removeAccessOpts := flavors.RemoveAccessOpts{ + Tenant: project.ID, + } + + accessList, err = flavors.RemoveAccess(client, flavor.ID, removeAccessOpts).Extract() + if err != nil { + t.Fatalf("Unable to remove access to flavor: %v", err) + } + + for _, access := range accessList { + tools.PrintResource(t, access) + } } func TestFlavorExtraSpecsCRUD(t *testing.T) { diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 0edc478098..34d8764fad 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -73,6 +73,19 @@ Example to Grant Access to a Flavor panic(err) } +Example to Remove/Revoke Access to a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + accessOpts := flavors.RemoveAccessOpts{ + Tenant: "15153a0979884b59b0592248ef947921", + } + + accessList, err := flavors.RemoveAccess(computeClient, flavor.ID, accessOpts).Extract() + if err != nil { + panic(err) + } + Example to Create Extra Specs for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 8144dde846..e7041df059 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -165,7 +165,7 @@ func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager // AddAccessOptsBuilder allows extensions to add additional parameters to the // AddAccess requests. type AddAccessOptsBuilder interface { - ToAddAccessMap() (map[string]interface{}, error) + ToFlavorAddAccessMap() (map[string]interface{}, error) } // AddAccessOpts represents options for adding access to a flavor. @@ -174,14 +174,44 @@ type AddAccessOpts struct { Tenant string `json:"tenant"` } -// ToAddAccessMap constructs a request body from AddAccessOpts. -func (opts AddAccessOpts) ToAddAccessMap() (map[string]interface{}, error) { +// ToFlavorAddAccessMap constructs a request body from AddAccessOpts. +func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "addTenantAccess") } // AddAccess grants a tenant/project access to a flavor. func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { - b, err := opts.ToAddAccessMap() + b, err := opts.ToFlavorAddAccessMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// RemoveAccessOptsBuilder allows extensions to add additional parameters to the +// RemoveAccess requests. +type RemoveAccessOptsBuilder interface { + ToFlavorRemoveAccessMap() (map[string]interface{}, error) +} + +// RemoveAccessOpts represents options for removing access to a flavor. +type RemoveAccessOpts struct { + // Tenant is the project/tenant ID to grant access. + Tenant string `json:"tenant"` +} + +// ToFlavorRemoveAccessMap constructs a request body from RemoveAccessOpts. +func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "removeTenantAccess") +} + +// RemoveAccess removes/revokes a tenant/project access to a flavor. +func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { + b, err := opts.ToFlavorRemoveAccessMap() if err != nil { r.Err = err return diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 49e3bed936..5fe4e6cfef 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -158,12 +158,18 @@ type accessResult struct { gophercloud.Result } -// AddAccessResult is the response of an AddAccess operations. Call its +// AddAccessResult is the response of an AddAccess operation. Call its // Extract method to interpret it as a slice of FlavorAccess. type AddAccessResult struct { accessResult } +// RemoveAccessResult is the response of a RemoveAccess operation. Call its +// Extract method to interpret it as a slice of FlavorAccess. +type RemoveAccessResult struct { + accessResult +} + // Extract provides access to the result of an access create or delete. // The result will be all accesses that the flavor has. func (r accessResult) Extract() ([]FlavorAccess, error) { diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 7ad667c482..990d75d495 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -300,6 +300,44 @@ func TestFlavorAccessAdd(t *testing.T) { } } +func TestFlavorAccessRemove(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/flavors/12345678/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, "accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "removeTenantAccess": { + "tenant": "2f954bcf047c4ee9b09a37d49ae6db54" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` + { + "flavor_access": [] + } + `) + }) + + expected := []flavors.FlavorAccess{} + removeAccessOpts := flavors.RemoveAccessOpts{ + Tenant: "2f954bcf047c4ee9b09a37d49ae6db54", + } + + actual, err := flavors.RemoveAccess(fake.ServiceClient(), "12345678", removeAccessOpts).Extract() + th.AssertNoErr(t, err) + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} + func TestFlavorExtraSpecsList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 8ca2d18cc1409027815e2a5085152d8a224ffa00 Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Fri, 19 Jan 2018 06:47:02 +0300 Subject: [PATCH 0192/2296] Compute v2: Get Hypervisor Uptime (#724) * Add support to get hypervisor uptime specified by ID * Add unit test * Add acceptance test --- .../openstack/compute/v2/hypervisors_test.go | 21 +++++++++++- .../compute/v2/extensions/hypervisors/doc.go | 10 ++++++ .../v2/extensions/hypervisors/requests.go | 9 ++++++ .../v2/extensions/hypervisors/results.go | 32 +++++++++++++++++++ .../hypervisors/testing/fixtures.go | 30 +++++++++++++++++ .../hypervisors/testing/requests_test.go | 12 +++++++ .../compute/v2/extensions/hypervisors/urls.go | 4 +++ 7 files changed, 117 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/hypervisors_test.go b/acceptance/openstack/compute/v2/hypervisors_test.go index 567cfaac30..15aff3de74 100644 --- a/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/acceptance/openstack/compute/v2/hypervisors_test.go @@ -52,7 +52,7 @@ func TestHypervisorsGet(t *testing.T) { tools.PrintResource(t, hypervisor) } -func TestHypervisorsStatistics(t *testing.T) { +func TestHypervisorsGetStatistics(t *testing.T) { client, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) @@ -66,6 +66,25 @@ func TestHypervisorsStatistics(t *testing.T) { tools.PrintResource(t, hypervisorsStats) } +func TestHypervisorsGetUptime(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + hypervisorID, err := getHypervisorID(t, client) + if err != nil { + t.Fatal(err) + } + + hypervisor, err := hypervisors.GetUptime(client, hypervisorID).Extract() + if err != nil { + t.Fatalf("Unable to hypervisor uptime: %v", err) + } + + tools.PrintResource(t, hypervisor) +} + func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (int, error) { allPages, err := hypervisors.List(client).AllPages() if err != nil { diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index 9d72f8fe61..b8eb699edb 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -37,5 +37,15 @@ Example of Show Hypervisor Statistics fmt.Printf("%+v\n", hypervisorsStatistics) +Example of Show Hypervisor Uptime + + hypervisorID := 42 + hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", hypervisorUptime) + */ package hypervisors diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/extensions/hypervisors/requests.go index f5efff85c7..b6f1c541cb 100644 --- a/openstack/compute/v2/extensions/hypervisors/requests.go +++ b/openstack/compute/v2/extensions/hypervisors/requests.go @@ -30,3 +30,12 @@ func Get(client *gophercloud.ServiceClient, hypervisorID int) (r HypervisorResul }) return } + +// GetUptime makes a request against the API to get uptime for specific hypervisor. +func GetUptime(client *gophercloud.ServiceClient, hypervisorID int) (r UptimeResult) { + v := strconv.Itoa(hypervisorID) + _, r.Err = client.Get(hypervisorsUptimeURL(client, v), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index a35a0341ce..7f3fafe1aa 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -256,3 +256,35 @@ func (r StatisticsResult) Extract() (*Statistics, error) { err := r.ExtractInto(&s) return &s.Stats, err } + +// Uptime represents uptime and additional info for a specific hypervisor. +type Uptime struct { + // The hypervisor host name provided by the Nova virt driver. + // For the Ironic driver, it is the Ironic node uuid. + HypervisorHostname string `json:"hypervisor_hostname"` + + // The id of the hypervisor. + ID int `json:"id"` + + // The state of the hypervisor. One of up or down. + State string `json:"state"` + + // The status of the hypervisor. One of enabled or disabled. + Status string `json:"status"` + + // The total uptime of the hypervisor and information about average load. + Uptime string `json:"uptime"` +} + +type UptimeResult struct { + gophercloud.Result +} + +// Extract interprets any UptimeResult as a Uptime, if possible. +func (r UptimeResult) Extract() (*Uptime, error) { + var s struct { + Uptime Uptime `json:"hypervisor"` + } + err := r.ExtractInto(&s) + return &s.Uptime, err +} diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go index d261fa658f..1dc05fb9b0 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go @@ -147,6 +147,18 @@ const HypervisorGetBody = ` } ` +const HypervisorUptimeBody = ` +{ + "hypervisor": { + "hypervisor_hostname": "fake-mini", + "id": 1, + "state": "up", + "status": "enabled", + "uptime": " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14" + } +} +` + var ( HypervisorFake = hypervisors.Hypervisor{ CPUInfo: hypervisors.CPUInfo{ @@ -201,6 +213,13 @@ var ( VCPUs: 2, VCPUsUsed: 0, } + HypervisorUptimeExpected = hypervisors.Uptime{ + HypervisorHostname: "fake-mini", + ID: 1, + State: "up", + Status: "enabled", + Uptime: " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14", + } ) func HandleHypervisorsStatisticsSuccessfully(t *testing.T) { @@ -233,3 +252,14 @@ func HandleHypervisorGetSuccessfully(t *testing.T) { fmt.Fprintf(w, HypervisorGetBody) }) } + +func HandleHypervisorUptimeSuccessfully(t *testing.T) { + v := strconv.Itoa(HypervisorFake.ID) + testhelper.Mux.HandleFunc("/os-hypervisors/"+v+"/uptime", 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") + fmt.Fprintf(w, HypervisorUptimeBody) + }) +} diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go index b89f6e76c8..95f9636c0c 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go @@ -75,3 +75,15 @@ func TestGetHypervisor(t *testing.T) { testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } + +func TestHypervisorsUptime(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleHypervisorUptimeSuccessfully(t) + + expected := HypervisorUptimeExpected + + actual, err := hypervisors.GetUptime(client.ServiceClient(), HypervisorFake.ID).Extract() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/hypervisors/urls.go b/openstack/compute/v2/extensions/hypervisors/urls.go index 19107fe53d..4c18ed43c4 100644 --- a/openstack/compute/v2/extensions/hypervisors/urls.go +++ b/openstack/compute/v2/extensions/hypervisors/urls.go @@ -13,3 +13,7 @@ func hypervisorsStatisticsURL(c *gophercloud.ServiceClient) string { func hypervisorsGetURL(c *gophercloud.ServiceClient, hypervisorID string) string { return c.ServiceURL("os-hypervisors", hypervisorID) } + +func hypervisorsUptimeURL(c *gophercloud.ServiceClient, hypervisorID string) string { + return c.ServiceURL("os-hypervisors", hypervisorID, "uptime") +} From a13ccee8008ac7ac09a11b361812fa2163cabb82 Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Sun, 21 Jan 2018 05:18:45 +0300 Subject: [PATCH 0193/2296] Compute v2: Add Live Migration Action (#728) * Add support for server live-migration * Add unit test * Fix 'Host' type `string` -> `*string` to be able to set it to nil * Add acceptance test * Add ability to turn-on/off acceptance test for server live-migration * Get rid of 'strconv', add description when skip migrate test --- acceptance/clients/clients.go | 9 +++++ .../openstack/compute/v2/migrate_test.go | 37 ++++++++++++++++++ .../compute/v2/extensions/migrate/doc.go | 19 ++++++++- .../compute/v2/extensions/migrate/requests.go | 39 +++++++++++++++++++ .../v2/extensions/migrate/testing/fixtures.go | 15 +++++++ .../migrate/testing/requests_test.go | 20 ++++++++++ 6 files changed, 138 insertions(+), 1 deletion(-) diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index fcf22a7e8d..d5c9cccdf1 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -42,6 +42,9 @@ type AcceptanceTestChoices struct { // DBDatastoreTypeID is the datastore type version for DB tests. DBDatastoreVersion string + + // LiveMigrate indicates ability to run multi-node migration tests + LiveMigrate bool } // AcceptanceTestChoicesFromEnv populates a ComputeChoices struct from environment variables. @@ -57,6 +60,11 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { dbDatastoreType := os.Getenv("OS_DB_DATASTORE_TYPE") dbDatastoreVersion := os.Getenv("OS_DB_DATASTORE_VERSION") + var liveMigrate bool + if v := os.Getenv("OS_LIVE_MIGRATE"); v != "" { + liveMigrate = true + } + missing := make([]string, 0, 3) if imageID == "" { missing = append(missing, "OS_IMAGE_ID") @@ -106,6 +114,7 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { ShareNetworkID: shareNetworkID, DBDatastoreType: dbDatastoreType, DBDatastoreVersion: dbDatastoreVersion, + LiveMigrate: liveMigrate, }, nil } diff --git a/acceptance/openstack/compute/v2/migrate_test.go b/acceptance/openstack/compute/v2/migrate_test.go index 4d03350100..954716bda0 100644 --- a/acceptance/openstack/compute/v2/migrate_test.go +++ b/acceptance/openstack/compute/v2/migrate_test.go @@ -28,3 +28,40 @@ func TestMigrate(t *testing.T) { t.Fatalf("Error during migration: %v", err) } } + +func TestLiveMigrate(t *testing.T) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + if !choices.LiveMigrate { + t.Skip("Testing of live migration is disabled") + } + + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + server, err := CreateServer(t, client) + if err != nil { + t.Fatalf("Unable to create server: %v", err) + } + defer DeleteServer(t, client, server) + + t.Logf("Attempting to migrate server %s", server.ID) + + blockMigration := false + diskOverCommit := false + + liveMigrateOpts := migrate.LiveMigrateOpts{ + BlockMigration: &blockMigration, + DiskOverCommit: &diskOverCommit, + } + + err = migrate.LiveMigrate(client, server.ID, liveMigrateOpts).ExtractErr() + if err != nil { + t.Fatalf("Error during live migration: %v", err) + } +} diff --git a/openstack/compute/v2/extensions/migrate/doc.go b/openstack/compute/v2/extensions/migrate/doc.go index 86750d6c6f..cf3067716d 100644 --- a/openstack/compute/v2/extensions/migrate/doc.go +++ b/openstack/compute/v2/extensions/migrate/doc.go @@ -2,12 +2,29 @@ Package migrate provides functionality to migrate servers that have been provisioned by the OpenStack Compute service. -Example to Migrate a Server +Example of Migrate Server (migrate Action) serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" err := migrate.Migrate(computeClient, serverID).ExtractErr() if err != nil { panic(err) } + +Example of Live-Migrate Server (os-migrateLive Action) + + serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" + host := "01c0cadef72d47e28a672a76060d492c" + blockMigration := false + + migrationOpts := migrate.LiveMigrateOpts{ + Host: &host, + BlockMigration: &blockMigration, + } + + err := migrate.LiveMigrate(computeClient, serverID, migrationOpts).ExtractErr() + if err != nil { + panic(err) + } + */ package migrate diff --git a/openstack/compute/v2/extensions/migrate/requests.go b/openstack/compute/v2/extensions/migrate/requests.go index 9f263fa3ba..90ae62e381 100644 --- a/openstack/compute/v2/extensions/migrate/requests.go +++ b/openstack/compute/v2/extensions/migrate/requests.go @@ -9,3 +9,42 @@ func Migrate(client *gophercloud.ServiceClient, id string) (r MigrateResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) return } + +// LiveMigrateOptsBuilder allows extensions to add additional parameters to the +// LiveMigrate request. +type LiveMigrateOptsBuilder interface { + ToLiveMigrateMap() (map[string]interface{}, error) +} + +// LiveMigrateOpts specifies parameters of live migrate action. +type LiveMigrateOpts struct { + // The host to which to migrate the server. + // If this parameter is None, the scheduler chooses a host. + Host *string `json:"host"` + + // Set to True to migrate local disks by using block migration. + // If the source or destination host uses shared storage and you set + // this value to True, the live migration fails. + BlockMigration *bool `json:"block_migration,omitempty"` + + // Set to True to enable over commit when the destination host is checked + // for available disk space. Set to False to disable over commit. This setting + // affects only the libvirt virt driver. + DiskOverCommit *bool `json:"disk_over_commit,omitempty"` +} + +// ToLiveMigrateMap constructs a request body from LiveMigrateOpts. +func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-migrateLive") +} + +// LiveMigrate will initiate a live-migration (without rebooting) of the instance to another host. +func LiveMigrate(client *gophercloud.ServiceClient, id string, opts LiveMigrateOptsBuilder) (r MigrateResult) { + b, err := opts.ToLiveMigrateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + return +} diff --git a/openstack/compute/v2/extensions/migrate/testing/fixtures.go b/openstack/compute/v2/extensions/migrate/testing/fixtures.go index 8a59aa3478..1d2f5902c2 100644 --- a/openstack/compute/v2/extensions/migrate/testing/fixtures.go +++ b/openstack/compute/v2/extensions/migrate/testing/fixtures.go @@ -16,3 +16,18 @@ func mockMigrateResponse(t *testing.T, id string) { w.WriteHeader(http.StatusAccepted) }) } + +func mockLiveMigrateResponse(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": false, + "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 7d14365d6a..b6906b7839 100644 --- a/openstack/compute/v2/extensions/migrate/testing/requests_test.go +++ b/openstack/compute/v2/extensions/migrate/testing/requests_test.go @@ -19,3 +19,23 @@ func TestMigrate(t *testing.T) { err := migrate.Migrate(client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } + +func TestLiveMigrate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockLiveMigrateResponse(t, serverID) + + host := "01c0cadef72d47e28a672a76060d492c" + blockMigration := false + diskOverCommit := true + + migrationOpts := migrate.LiveMigrateOpts{ + Host: &host, + BlockMigration: &blockMigration, + DiskOverCommit: &diskOverCommit, + } + + err := migrate.LiveMigrate(client.ServiceClient(), serverID, migrationOpts).ExtractErr() + th.AssertNoErr(t, err) +} From 4a3f5ae58624b68283375060dad06a214b05a32b Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Tue, 23 Jan 2018 13:50:12 -0500 Subject: [PATCH 0194/2296] add simple tenant usage (#624) * add simple tenant usage * tenantusage time fields changed to time.Time * removed multi, kept single * lint * fixed naming * address comments in simpletenantusage * renamed simpletenantusage to usage * added acceptance test * updated usage/doc.go to reflect package name * renamed usage to tenantUsage to prevent shadowing * renamed symbols in extension/usage --- acceptance/openstack/compute/v2/usage_test.go | 34 +++++ openstack/compute/v2/extensions/usage/doc.go | 27 ++++ .../compute/v2/extensions/usage/requests.go | 54 +++++++ .../compute/v2/extensions/usage/results.go | 137 ++++++++++++++++++ .../v2/extensions/usage/testing/doc.go | 2 + .../v2/extensions/usage/testing/fixtures.go | 137 ++++++++++++++++++ .../extensions/usage/testing/requests_test.go | 21 +++ openstack/compute/v2/extensions/usage/urls.go | 13 ++ 8 files changed, 425 insertions(+) create mode 100644 acceptance/openstack/compute/v2/usage_test.go create mode 100644 openstack/compute/v2/extensions/usage/doc.go create mode 100644 openstack/compute/v2/extensions/usage/requests.go create mode 100644 openstack/compute/v2/extensions/usage/results.go create mode 100644 openstack/compute/v2/extensions/usage/testing/doc.go create mode 100644 openstack/compute/v2/extensions/usage/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/usage/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/usage/urls.go diff --git a/acceptance/openstack/compute/v2/usage_test.go b/acceptance/openstack/compute/v2/usage_test.go new file mode 100644 index 0000000000..1537fda0cb --- /dev/null +++ b/acceptance/openstack/compute/v2/usage_test.go @@ -0,0 +1,34 @@ +// +build acceptance compute usage + +package v2 + +import ( + "strings" + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" +) + +func TestUsageSingleTenant(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + endpointParts := strings.Split(client.Endpoint, "/") + tenantID := endpointParts[4] + + page, err := usage.SingleTenant(client, tenantID, nil).AllPages() + if err != nil { + t.Fatal(err) + } + + tenantUsage, err := usage.ExtractSingleTenant(page) + if err != nil { + t.Fatal(err) + } + + tools.PrintResource(t, tenantUsage) +} diff --git a/openstack/compute/v2/extensions/usage/doc.go b/openstack/compute/v2/extensions/usage/doc.go new file mode 100644 index 0000000000..32e8643e4d --- /dev/null +++ b/openstack/compute/v2/extensions/usage/doc.go @@ -0,0 +1,27 @@ +/* +Package usage provides information and interaction with the +SimpleTenantUsage extension for the OpenStack Compute service. + +Example to Retrieve Usage for a Single Tenant: + start := time.Date(2017, 01, 21, 10, 4, 20, 0, time.UTC) + end := time.Date(2017, 01, 21, 10, 4, 20, 0, time.UTC) + + singleTenantOpts := usage.SingleTenantOpts{ + Start: &start, + End: &end, + } + + page, err := usage.SingleTenant(computeClient, tenantID, singleTenantOpts).AllPages() + if err != nil { + panic(err) + } + + tenantUsage, err := usage.ExtractSingleTenant(page) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", tenantUsage) + +*/ +package usage diff --git a/openstack/compute/v2/extensions/usage/requests.go b/openstack/compute/v2/extensions/usage/requests.go new file mode 100644 index 0000000000..ee66e2a212 --- /dev/null +++ b/openstack/compute/v2/extensions/usage/requests.go @@ -0,0 +1,54 @@ +package usage + +import ( + "net/url" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// SingleTenant returns usage data about a single tenant. +func SingleTenant(client *gophercloud.ServiceClient, tenantID string, opts SingleTenantOptsBuilder) pagination.Pager { + url := getTenantURL(client, tenantID) + if opts != nil { + query, err := opts.ToUsageSingleTenantQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return SingleTenantPage{pagination.SinglePageBase(r)} + }) +} + +// SingleTenantOpts are options for fetching usage of a single tenant. +type SingleTenantOpts struct { + // The ending time to calculate usage statistics on compute and storage resources. + End *time.Time `q:"end"` + + // The beginning time to calculate usage statistics on compute and storage resources. + Start *time.Time `q:"start"` +} + +// SingleTenantOptsBuilder allows extensions to add additional parameters to the +// SingleTenant request. +type SingleTenantOptsBuilder interface { + ToUsageSingleTenantQuery() (string, error) +} + +// ToUsageSingleTenantQuery formats a SingleTenantOpts into a query string. +func (opts SingleTenantOpts) ToUsageSingleTenantQuery() (string, error) { + params := make(url.Values) + if opts.Start != nil { + params.Add("start", opts.Start.Format(gophercloud.RFC3339MilliNoZ)) + } + + if opts.End != nil { + params.Add("end", opts.End.Format(gophercloud.RFC3339MilliNoZ)) + } + + q := &url.URL{RawQuery: params.Encode()} + return q.String(), nil +} diff --git a/openstack/compute/v2/extensions/usage/results.go b/openstack/compute/v2/extensions/usage/results.go new file mode 100644 index 0000000000..39661ba463 --- /dev/null +++ b/openstack/compute/v2/extensions/usage/results.go @@ -0,0 +1,137 @@ +package usage + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// TenantUsage is a set of usage information about a tenant over the sampling window +type TenantUsage struct { + // ServerUsages is an array of ServerUsage maps + ServerUsages []ServerUsage `json:"server_usages"` + + // Start is the beginning time to calculate usage statistics on compute and storage resources + Start time.Time `json:"-"` + + // Stop is the ending time to calculate usage statistics on compute and storage resources + Stop time.Time `json:"-"` + + // TenantID is the ID of the tenant whose usage is being reported on + TenantID string `json:"tenant_id"` + + // TotalHours is the total duration that servers exist (in hours) + TotalHours float64 `json:"total_hours"` + + // TotalLocalGBUsage multiplies the server disk size (in GiB) by hours the server exists, and then adding that all together for each server + TotalLocalGBUsage float64 `json:"total_local_gb_usage"` + + // TotalMemoryMBUsage multiplies the server memory size (in MB) by hours the server exists, and then adding that all together for each server + TotalMemoryMBUsage float64 `json:"total_memory_mb_usage"` + + // TotalVCPUsUsage multiplies the number of virtual CPUs of the server by hours the server exists, and then adding that all together for each server + TotalVCPUsUsage float64 `json:"total_vcpus_usage"` +} + +// UnmarshalJSON sets *u to a copy of data. +func (u *TenantUsage) UnmarshalJSON(b []byte) error { + type tmp TenantUsage + var s struct { + tmp + Start gophercloud.JSONRFC3339MilliNoZ `json:"start"` + Stop gophercloud.JSONRFC3339MilliNoZ `json:"stop"` + } + + if err := json.Unmarshal(b, &s); err != nil { + return err + } + *u = TenantUsage(s.tmp) + + u.Start = time.Time(s.Start) + u.Stop = time.Time(s.Stop) + + return nil +} + +// ServerUsage is a detailed set of information about a specific instance inside a tenant +type ServerUsage struct { + // EndedAt is the date and time when the server was deleted + EndedAt time.Time `json:"-"` + + // Flavor is the display name of a flavor + Flavor string `json:"flavor"` + + // Hours is the duration that the server exists in hours + Hours float64 `json:"hours"` + + // InstanceID is the UUID of the instance + InstanceID string `json:"instance_id"` + + // LocalGB is the sum of the root disk size of the server and the ephemeral disk size of it (in GiB) + LocalGB int `json:"local_gb"` + + // MemoryMB is the memory size of the server (in MB) + MemoryMB int `json:"memory_mb"` + + // Name is the name assigned to the server when it was created + Name string `json:"name"` + + // StartedAt is the date and time when the server was started + StartedAt time.Time `json:"-"` + + // State is the VM power state + State string `json:"state"` + + // TenantID is the UUID of the tenant in a multi-tenancy cloud + TenantID string `json:"tenant_id"` + + // Uptime is the uptime of the server in seconds + Uptime int `json:"uptime"` + + // VCPUs is the number of virtual CPUs that the server uses + VCPUs int `json:"vcpus"` +} + +// UnmarshalJSON sets *u to a copy of data. +func (u *ServerUsage) UnmarshalJSON(b []byte) error { + type tmp ServerUsage + var s struct { + tmp + EndedAt gophercloud.JSONRFC3339MilliNoZ `json:"ended_at"` + StartedAt gophercloud.JSONRFC3339MilliNoZ `json:"started_at"` + } + + if err := json.Unmarshal(b, &s); err != nil { + return err + } + *u = ServerUsage(s.tmp) + + u.EndedAt = time.Time(s.EndedAt) + u.StartedAt = time.Time(s.StartedAt) + + return nil +} + +// SingleTenantPage stores a single, only page of TenantUsage results from a +// SingleTenant call. +type SingleTenantPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a SingleTenantPage is empty. +func (page SingleTenantPage) IsEmpty() (bool, error) { + ks, err := ExtractSingleTenant(page) + return ks == nil, err +} + +// ExtractSingleTenant interprets a SingleTenantPage as a TenantUsage result. +func ExtractSingleTenant(page pagination.Page) (*TenantUsage, error) { + var s struct { + TenantUsage *TenantUsage `json:"tenant_usage"` + TenantUsageLinks []gophercloud.Link `json:"tenant_usage_links"` + } + err := (page.(SingleTenantPage)).ExtractInto(&s) + return s.TenantUsage, err +} diff --git a/openstack/compute/v2/extensions/usage/testing/doc.go b/openstack/compute/v2/extensions/usage/testing/doc.go new file mode 100644 index 0000000000..a3521795bb --- /dev/null +++ b/openstack/compute/v2/extensions/usage/testing/doc.go @@ -0,0 +1,2 @@ +// simple tenant usage unit tests +package testing diff --git a/openstack/compute/v2/extensions/usage/testing/fixtures.go b/openstack/compute/v2/extensions/usage/testing/fixtures.go new file mode 100644 index 0000000000..b7c1ae55f5 --- /dev/null +++ b/openstack/compute/v2/extensions/usage/testing/fixtures.go @@ -0,0 +1,137 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const FirstTenantID = "aabbccddeeff112233445566" + +// GetSingleTenant holds the fixtures for the content of the request for a +// single tenant. +const GetSingleTenant = `{ + "tenant_usage": { + "server_usages": [ + { + "ended_at": null, + "flavor": "m1.tiny", + "hours": 0.021675453333333334, + "instance_id": "a70096fd-8196-406b-86c4-045840f53ad7", + "local_gb": 1, + "memory_mb": 512, + "name": "jttest", + "started_at": "2017-11-30T03:23:43.000000", + "state": "active", + "tenant_id": "aabbccddeeff112233445566", + "uptime": 78, + "vcpus": 1 + }, + { + "ended_at": "2017-11-21T04:10:11.000000", + "flavor": "m1.acctest", + "hours": 0.33444444444444443, + "instance_id": "c04e38f2-dcee-4ca8-9466-7708d0a9b6dd", + "local_gb": 15, + "memory_mb": 512, + "name": "basic", + "started_at": "2017-11-21T03:50:07.000000", + "state": "terminated", + "tenant_id": "aabbccddeeff112233445566", + "uptime": 1204, + "vcpus": 1 + }, + { + "ended_at": "2017-11-30T03:21:21.000000", + "flavor": "m1.acctest", + "hours": 0.004166666666666667, + "instance_id": "ceb654fa-e0e8-44fb-8942-e4d0bfad3941", + "local_gb": 15, + "memory_mb": 512, + "name": "ACPTTESTJSxbPQAC34lTnBE1", + "started_at": "2017-11-30T03:21:06.000000", + "state": "terminated", + "tenant_id": "aabbccddeeff112233445566", + "uptime": 15, + "vcpus": 1 + } + ], + "start": "2017-11-02T03:25:01.000000", + "stop": "2017-11-30T03:25:01.000000", + "tenant_id": "aabbccddeeff112233445566", + "total_hours": 1.25834212, + "total_local_gb_usage": 18.571675453333334, + "total_memory_mb_usage": 644.27116544, + "total_vcpus_usage": 1.25834212 + } +}` + +// HandleGetSingleTenantSuccessfully configures the test server to respond to a +// Get request for a single tenant +func HandleGetSingleTenantSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-simple-tenant-usage/"+FirstTenantID, 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.Fprint(w, GetSingleTenant) + }) +} + +// SingleTenantUsageResults is the code fixture for GetSingleTenant. +var SingleTenantUsageResults = usage.TenantUsage{ + ServerUsages: []usage.ServerUsage{ + { + Flavor: "m1.tiny", + Hours: 0.021675453333333334, + InstanceID: "a70096fd-8196-406b-86c4-045840f53ad7", + LocalGB: 1, + MemoryMB: 512, + Name: "jttest", + StartedAt: time.Date(2017, 11, 30, 3, 23, 43, 0, time.UTC), + State: "active", + TenantID: "aabbccddeeff112233445566", + Uptime: 78, + VCPUs: 1, + }, + { + Flavor: "m1.acctest", + Hours: 0.33444444444444443, + InstanceID: "c04e38f2-dcee-4ca8-9466-7708d0a9b6dd", + LocalGB: 15, + MemoryMB: 512, + Name: "basic", + StartedAt: time.Date(2017, 11, 21, 3, 50, 7, 0, time.UTC), + EndedAt: time.Date(2017, 11, 21, 4, 10, 11, 0, time.UTC), + State: "terminated", + TenantID: "aabbccddeeff112233445566", + Uptime: 1204, + VCPUs: 1, + }, + { + Flavor: "m1.acctest", + Hours: 0.004166666666666667, + InstanceID: "ceb654fa-e0e8-44fb-8942-e4d0bfad3941", + LocalGB: 15, + MemoryMB: 512, + Name: "ACPTTESTJSxbPQAC34lTnBE1", + StartedAt: time.Date(2017, 11, 30, 3, 21, 6, 0, time.UTC), + EndedAt: time.Date(2017, 11, 30, 3, 21, 21, 0, time.UTC), + State: "terminated", + TenantID: "aabbccddeeff112233445566", + Uptime: 15, + VCPUs: 1, + }, + }, + Start: time.Date(2017, 11, 2, 3, 25, 1, 0, time.UTC), + Stop: time.Date(2017, 11, 30, 3, 25, 1, 0, time.UTC), + TenantID: "aabbccddeeff112233445566", + TotalHours: 1.25834212, + TotalLocalGBUsage: 18.571675453333334, + TotalMemoryMBUsage: 644.27116544, + TotalVCPUsUsage: 1.25834212, +} diff --git a/openstack/compute/v2/extensions/usage/testing/requests_test.go b/openstack/compute/v2/extensions/usage/testing/requests_test.go new file mode 100644 index 0000000000..1b43f12aec --- /dev/null +++ b/openstack/compute/v2/extensions/usage/testing/requests_test.go @@ -0,0 +1,21 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGetTenant(t *testing.T) { + var getOpts usage.SingleTenantOpts + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSingleTenantSuccessfully(t) + page, err := usage.SingleTenant(client.ServiceClient(), FirstTenantID, getOpts).AllPages() + th.AssertNoErr(t, err) + actual, err := usage.ExtractSingleTenant(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &SingleTenantUsageResults, actual) +} diff --git a/openstack/compute/v2/extensions/usage/urls.go b/openstack/compute/v2/extensions/usage/urls.go new file mode 100644 index 0000000000..f172b62211 --- /dev/null +++ b/openstack/compute/v2/extensions/usage/urls.go @@ -0,0 +1,13 @@ +package usage + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "os-simple-tenant-usage" + +func getURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(resourcePath) +} + +func getTenantURL(client *gophercloud.ServiceClient, tenantID string) string { + return client.ServiceURL(resourcePath, tenantID) +} From 0b8b348f5ad19aa4513ad9f8ad24f766a6623ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Knecht?= Date: Wed, 24 Jan 2018 15:39:50 +0100 Subject: [PATCH 0195/2296] compute: flavors: add Ephemeral attribute Nova returns the amount of ephemeral storage (in GB) associated with a flavor as `OS-FLV-EXT-DATA:ephemeral`. --- openstack/compute/v2/flavors/results.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 5fe4e6cfef..525cddaea2 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -66,6 +66,9 @@ type Flavor struct { // IsPublic indicates whether the flavor is public. IsPublic bool `json:"os-flavor-access:is_public"` + + // Ephemeral is the amount of ephemeral disk space, measured in GB. + Ephemeral int `json:"OS-FLV-EXT-DATA:ephemeral"` } func (r *Flavor) UnmarshalJSON(b []byte) error { From a3e3dd5688a0be7260fa9887c91f66b5543ef203 Mon Sep 17 00:00:00 2001 From: Ildar Svetlov Date: Mon, 29 Jan 2018 01:40:20 +0400 Subject: [PATCH 0196/2296] Blockstorage service list support (#733) * Add Blockstorage service list support * Add optional fields into List query --- .../blockstorage/extensions/services_test.go | 32 ++++++ .../blockstorage/extensions/services/doc.go | 22 +++++ .../extensions/services/requests.go | 42 ++++++++ .../extensions/services/results.go | 84 ++++++++++++++++ .../extensions/services/testing/fixtures.go | 97 +++++++++++++++++++ .../services/testing/requests_test.go | 41 ++++++++ .../blockstorage/extensions/services/urls.go | 7 ++ 7 files changed, 325 insertions(+) create mode 100644 acceptance/openstack/blockstorage/extensions/services_test.go create mode 100644 openstack/blockstorage/extensions/services/doc.go create mode 100644 openstack/blockstorage/extensions/services/requests.go create mode 100644 openstack/blockstorage/extensions/services/results.go create mode 100644 openstack/blockstorage/extensions/services/testing/fixtures.go create mode 100644 openstack/blockstorage/extensions/services/testing/requests_test.go create mode 100644 openstack/blockstorage/extensions/services/urls.go diff --git a/acceptance/openstack/blockstorage/extensions/services_test.go b/acceptance/openstack/blockstorage/extensions/services_test.go new file mode 100644 index 0000000000..b65f586ec8 --- /dev/null +++ b/acceptance/openstack/blockstorage/extensions/services_test.go @@ -0,0 +1,32 @@ +// +build acceptance blockstorage + +package extensions + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" +) + +func TestServicesList(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := services.List(blockClient, services.ListOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to list services: %v", err) + } + + allServices, err := services.ExtractServices(allPages) + if err != nil { + t.Fatalf("Unable to extract services") + } + + for _, service := range allServices { + tools.PrintResource(t, service) + } +} diff --git a/openstack/blockstorage/extensions/services/doc.go b/openstack/blockstorage/extensions/services/doc.go new file mode 100644 index 0000000000..b3fba4cd62 --- /dev/null +++ b/openstack/blockstorage/extensions/services/doc.go @@ -0,0 +1,22 @@ +/* +Package services returns information about the blockstorage services in the +OpenStack cloud. + +Example of Retrieving list of all services + + allPages, err := services.List(blockstorageClient, 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/blockstorage/extensions/services/requests.go b/openstack/blockstorage/extensions/services/requests.go new file mode 100644 index 0000000000..0edcfc9d7e --- /dev/null +++ b/openstack/blockstorage/extensions/services/requests.go @@ -0,0 +1,42 @@ +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 { + // Filter the service list result by binary name of the service. + Binary string `q:"binary"` + + // Filter the service list result by host name of the service. + Host string `q:"host"` +} + +// 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/blockstorage/extensions/services/results.go b/openstack/blockstorage/extensions/services/results.go new file mode 100644 index 0000000000..49ad48ef61 --- /dev/null +++ b/openstack/blockstorage/extensions/services/results.go @@ -0,0 +1,84 @@ +package services + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Service represents a Blockstorage service in the OpenStack cloud. +type Service struct { + // The binary name of the service. + Binary string `json:"binary"` + + // The reason for disabling a service. + DisabledReason string `json:"disabled_reason"` + + // The name of the host. + Host string `json:"host"` + + // 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"` + + // The following fields are optional + + // The host is frozen or not. Only in cinder-volume service. + Frozen bool `json:"frozen"` + + // The cluster name. Only in cinder-volume service. + Cluster string `json:"cluster"` + + // The volume service replication status. Only in cinder-volume service. + ReplicationStatus string `json:"replication_status"` + + // The ID of active storage backend. Only in cinder-volume service. + ActiveBackendID string `json:"active_backend_id"` +} + +// 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/blockstorage/extensions/services/testing/fixtures.go b/openstack/blockstorage/extensions/services/testing/fixtures.go new file mode 100644 index 0000000000..9d14723c12 --- /dev/null +++ b/openstack/blockstorage/extensions/services/testing/fixtures.go @@ -0,0 +1,97 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/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": "cinder-scheduler", + "zone": "nova", + "state": "up", + "updated_at": "2017-06-29T05:50:35.000000", + "host": "devstack", + "disabled_reason": null + }, + { + "status": "enabled", + "binary": "cinder-backup", + "zone": "nova", + "state": "up", + "updated_at": "2017-06-29T05:50:42.000000", + "host": "devstack", + "disabled_reason": null + }, + { + "status": "enabled", + "binary": "cinder-volume", + "zone": "nova", + "frozen": false, + "state": "up", + "updated_at": "2017-06-29T05:50:39.000000", + "cluster": null, + "host": "devstack@lvmdriver-1", + "replication_status": "disabled", + "active_backend_id": null, + "disabled_reason": null + }] +} +` + +// First service from the ServiceListBody +var FirstFakeService = services.Service{ + Binary: "cinder-scheduler", + DisabledReason: "", + Host: "devstack", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 35, 0, time.UTC), + Zone: "nova", +} + +// Second service from the ServiceListBody +var SecondFakeService = services.Service{ + Binary: "cinder-backup", + DisabledReason: "", + Host: "devstack", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 42, 0, time.UTC), + Zone: "nova", +} + +// Third service from the ServiceListBody +var ThirdFakeService = services.Service{ + ActiveBackendID: "", + Binary: "cinder-volume", + Cluster: "", + DisabledReason: "", + Frozen: false, + Host: "devstack@lvmdriver-1", + ReplicationStatus: "disabled", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 39, 0, time.UTC), + Zone: "nova", +} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-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/blockstorage/extensions/services/testing/requests_test.go b/openstack/blockstorage/extensions/services/testing/requests_test.go new file mode 100644 index 0000000000..4178c23699 --- /dev/null +++ b/openstack/blockstorage/extensions/services/testing/requests_test.go @@ -0,0 +1,41 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/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) != 3 { + t.Fatalf("Expected 3 services, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) + testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) + testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} diff --git a/openstack/blockstorage/extensions/services/urls.go b/openstack/blockstorage/extensions/services/urls.go new file mode 100644 index 0000000000..61d794007e --- /dev/null +++ b/openstack/blockstorage/extensions/services/urls.go @@ -0,0 +1,7 @@ +package services + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-services") +} From 1db95d798aa72ec12a6e60e40749cea56073d2fb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 28 Jan 2018 21:48:51 +0000 Subject: [PATCH 0197/2296] Compute v2: Add unit tests for Ephemeral field --- .../compute/v2/flavors/testing/requests_test.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 990d75d495..fba0d4776b 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -37,7 +37,8 @@ func TestListFlavors(t *testing.T) { "disk": 1, "ram": 512, "swap":"", - "os-flavor-access:is_public": true + "os-flavor-access:is_public": true, + "OS-FLV-EXT-DATA:ephemeral": 10 }, { "id": "2", @@ -46,7 +47,8 @@ func TestListFlavors(t *testing.T) { "disk": 20, "ram": 2048, "swap": 1000, - "os-flavor-access:is_public": true + "os-flavor-access:is_public": true, + "OS-FLV-EXT-DATA:ephemeral": 0 }, { "id": "3", @@ -55,7 +57,8 @@ func TestListFlavors(t *testing.T) { "disk": 40, "ram": 4096, "swap": 1000, - "os-flavor-access:is_public": false + "os-flavor-access:is_public": false, + "OS-FLV-EXT-DATA:ephemeral": 0 } ], "flavors_links": [ @@ -84,9 +87,9 @@ func TestListFlavors(t *testing.T) { } expected := []flavors.Flavor{ - {ID: "1", Name: "m1.tiny", VCPUs: 1, Disk: 1, RAM: 512, Swap: 0, IsPublic: true}, - {ID: "2", Name: "m1.small", VCPUs: 1, Disk: 20, RAM: 2048, Swap: 1000, IsPublic: true}, - {ID: "3", Name: "m1.medium", VCPUs: 2, Disk: 40, RAM: 4096, Swap: 1000, IsPublic: false}, + {ID: "1", Name: "m1.tiny", VCPUs: 1, Disk: 1, RAM: 512, Swap: 0, IsPublic: true, Ephemeral: 10}, + {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}, } if !reflect.DeepEqual(expected, actual) { From f0a5d284c0791cb8e0aa93882560076f9371ea9e Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Sun, 28 Jan 2018 21:59:35 +0100 Subject: [PATCH 0198/2296] add ProviderClient.Reauthenticate() function If a user wants to do their own HTTP requests and reauthenticate in case of 401 responses, they can already use ProviderClient.ReauthFunc(), but that function is not thread-safe. This commit provides a safer alternative by pulling the relevant piece of code out of ProviderClient.Request(). --- provider_client.go | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/provider_client.go b/provider_client.go index 72daeb0a3e..3b8e9cb8d4 100644 --- a/provider_client.go +++ b/provider_client.go @@ -126,6 +126,36 @@ func (client *ProviderClient) SetToken(t string) { client.TokenID = t } +//Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is +//called because of a 401 response, the caller may pass the previous token. In +//this case, the reauthentication can be skipped if another thread has already +//reauthenticated in the meantime. If no previous token is known, an empty +//string should be passed instead to force unconditional reauthentication. +func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { + if client.ReauthFunc == nil { + return nil + } + + if client.mut == nil { + return client.ReauthFunc() + } + client.mut.Lock() + defer client.mut.Unlock() + + client.reauthmut.Lock() + client.reauthmut.reauthing = true + client.reauthmut.Unlock() + + if previousToken == "" || client.TokenID == previousToken { + err = client.ReauthFunc() + } + + client.reauthmut.Lock() + client.reauthmut.reauthing = false + client.reauthmut.Unlock() + return +} + // RequestOpts customizes the behavior of the provider.Request() method. type RequestOpts struct { // JSONBody, if provided, will be encoded as JSON and used as the body of the HTTP request. The @@ -254,21 +284,7 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } case http.StatusUnauthorized: if client.ReauthFunc != nil { - if client.mut != nil { - client.mut.Lock() - client.reauthmut.Lock() - client.reauthmut.reauthing = true - client.reauthmut.Unlock() - if curtok := client.TokenID; curtok == prereqtok { - err = client.ReauthFunc() - } - client.reauthmut.Lock() - client.reauthmut.reauthing = false - client.reauthmut.Unlock() - client.mut.Unlock() - } else { - err = client.ReauthFunc() - } + err = client.Reauthenticate(prereqtok) if err != nil { e := &ErrUnableToReauthenticate{} e.ErrOriginal = respErr From a924af7658c466bf641b255397766fd4f4889189 Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Mon, 29 Jan 2018 22:18:43 +0300 Subject: [PATCH 0199/2296] Compute v2: Create aggregate (#739) * Add create host aggregate support * Update doc.go * Add `required:true` to struct field --- .../compute/v2/extensions/aggregates/doc.go | 16 +++++- .../v2/extensions/aggregates/requests.go | 28 ++++++++++ .../v2/extensions/aggregates/results.go | 51 ++++++++++++++++++- .../extensions/aggregates/testing/fixtures.go | 39 ++++++++++++++ .../aggregates/testing/requests_test.go | 30 ++++++++--- .../compute/v2/extensions/aggregates/urls.go | 4 ++ 6 files changed, 160 insertions(+), 8 deletions(-) diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index 43699cb916..506fa81fef 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -1,7 +1,21 @@ /* -Package aggregates returns information about the host aggregates in the +Package aggregates manages information about the host aggregates in the OpenStack cloud. +Example of Create an aggregate + + opts := aggregates.CreateOpts{ + Name: "name", + AvailabilityZone: "london", + } + + aggregate, err := aggregates.Create(computeClient, opts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", aggregate) + Example of Retrieving list of all aggregates allPages, err := aggregates.List(computeClient).AllPages() diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index 5f31136c52..af5c5de09a 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -11,3 +11,31 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { return AggregatesPage{pagination.SinglePageBase(r)} }) } + +type CreateOpts struct { + // The name of the host aggregate. + Name string `json:"name" required:"true"` + + // The availability zone of the host aggregate. + // You should use a custom availability zone rather than + // the default returned by the os-availability-zone API. + // The availability zone must not include ‘:’ in its name. + AvailabilityZone string `json:"availability_zone,omitempty"` +} + +func (opts CreateOpts) ToAggregatesCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "aggregate") +} + +// Create makes a request against the API to create an aggregate. +func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := opts.ToAggregatesCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(aggregatesCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go index 19fc4443d6..f11463855b 100644 --- a/openstack/compute/v2/extensions/aggregates/results.go +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -1,6 +1,12 @@ package aggregates -import "github.com/gophercloud/gophercloud/pagination" +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) // Aggregate represents a host aggregate in the OpenStack cloud. type Aggregate struct { @@ -18,6 +24,33 @@ type Aggregate struct { // Name of the aggregate. Name string `json:"name"` + + // The date and time when the resource was created. + CreatedAt time.Time `json:"-"` + + // The date and time when the resource was updated, + // if the resource has not been updated, this field will show as null. + UpdatedAt time.Time `json:"-"` +} + +// UnmarshalJSON to override default +func (r *Aggregate) UnmarshalJSON(b []byte) error { + type tmp Aggregate + 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 = Aggregate(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil } // AggregatesPage represents a single page of all Aggregates from a List @@ -40,3 +73,19 @@ func ExtractAggregates(p pagination.Page) ([]Aggregate, error) { err := (p.(AggregatesPage)).ExtractInto(&a) return a.Aggregates, err } + +type aggregatesResult struct { + gophercloud.Result +} + +type CreateResult struct { + aggregatesResult +} + +func (r CreateResult) Extract() (*Aggregate, error) { + var s struct { + Aggregate *Aggregate `json:"aggregate"` + } + err := r.ExtractInto(&s) + return s.Aggregate, err +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go index 758da2fb00..b4c53202cd 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" th "github.com/gophercloud/gophercloud/testhelper" @@ -44,6 +45,20 @@ const AggregateListBody = ` } ` +const AggregateCreateBody = ` +{ + "aggregate": { + "availability_zone": "london", + "created_at": "2016-12-27T22:51:32.000000", + "deleted": false, + "deleted_at": null, + "id": 32, + "name": "name", + "updated_at": null + } +} +` + // First aggregate from the AggregateListBody var FirstFakeAggregate = aggregates.Aggregate{ AvailabilityZone: "", @@ -51,6 +66,8 @@ var FirstFakeAggregate = aggregates.Aggregate{ ID: 1, Metadata: map[string]string{}, Name: "test-aggregate1", + CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), + UpdatedAt: time.Time{}, } // Second aggregate from the AggregateListBody @@ -60,6 +77,18 @@ var SecondFakeAggregate = aggregates.Aggregate{ ID: 4, Metadata: map[string]string{"availability_zone": "test-az"}, Name: "test-aggregate2", + CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), + UpdatedAt: time.Time{}, +} + +var CreatedAggregate = aggregates.Aggregate{ + AvailabilityZone: "london", + Hosts: nil, + ID: 32, + Metadata: nil, + Name: "name", + CreatedAt: time.Date(2016, 12, 27, 22, 51, 32, 0, time.UTC), + UpdatedAt: time.Time{}, } // HandleListSuccessfully configures the test server to respond to a List request. @@ -72,3 +101,13 @@ func HandleListSuccessfully(t *testing.T) { fmt.Fprintf(w, AggregateListBody) }) } + +func HandleCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-aggregates", 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") + fmt.Fprintf(w, AggregateCreateBody) + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index 903b675c9f..7a59b0da0f 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -5,13 +5,13 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestListAggregates(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleListSuccessfully(t) pages := 0 @@ -26,15 +26,33 @@ func TestListAggregates(t *testing.T) { if len(actual) != 2 { t.Fatalf("Expected 2 aggregates, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, FirstFakeAggregate, actual[0]) - testhelper.CheckDeepEquals(t, SecondFakeAggregate, actual[1]) + th.CheckDeepEquals(t, FirstFakeAggregate, actual[0]) + th.CheckDeepEquals(t, SecondFakeAggregate, actual[1]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) } } + +func TestCreateAggregates(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t) + + expected := CreatedAggregate + + opts := aggregates.CreateOpts{ + Name: "name", + AvailabilityZone: "london", + } + + actual, err := aggregates.Create(client.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go index 88b15009fa..80a37b8169 100644 --- a/openstack/compute/v2/extensions/aggregates/urls.go +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func aggregatesListURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-aggregates") } + +func aggregatesCreateURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-aggregates") +} From 8128df2865bb9d40a9d09f901483b6cd0436e53c Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Tue, 30 Jan 2018 22:37:20 +0300 Subject: [PATCH 0200/2296] Compute v2: Delete aggregate (#740) * Add delete aggregate support * Change expected status code * Style fix --- openstack/compute/v2/extensions/aggregates/doc.go | 9 ++++++++- .../compute/v2/extensions/aggregates/requests.go | 11 +++++++++++ .../compute/v2/extensions/aggregates/results.go | 4 ++++ .../v2/extensions/aggregates/testing/fixtures.go | 13 +++++++++++++ .../extensions/aggregates/testing/requests_test.go | 9 +++++++++ openstack/compute/v2/extensions/aggregates/urls.go | 4 ++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index 506fa81fef..f5c1f640d3 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -13,9 +13,16 @@ Example of Create an aggregate if err != nil { panic(err) } - fmt.Printf("%+v\n", aggregate) +Example of Delete an aggregate + + aggregateID := 32 + err := aggregates.Delete(computeClient, aggregateID).ExtractErr() + if err != nil { + panic(err) + } + Example of Retrieving list of all aggregates allPages, err := aggregates.List(computeClient).AllPages() diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index af5c5de09a..22de32b4b2 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -1,6 +1,8 @@ package aggregates import ( + "strconv" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -39,3 +41,12 @@ func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) }) return } + +// Delete makes a request against the API to delete an aggregate. +func Delete(client *gophercloud.ServiceClient, aggregateID int) (r DeleteResult) { + v := strconv.Itoa(aggregateID) + _, r.Err = client.Delete(aggregatesDeleteURL(client, v), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go index f11463855b..fa62b75140 100644 --- a/openstack/compute/v2/extensions/aggregates/results.go +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -89,3 +89,7 @@ func (r CreateResult) Extract() (*Aggregate, error) { err := r.ExtractInto(&s) return s.Aggregate, err } + +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go index b4c53202cd..c6214516e4 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -3,6 +3,7 @@ package testing import ( "fmt" "net/http" + "strconv" "testing" "time" @@ -91,6 +92,8 @@ var CreatedAggregate = aggregates.Aggregate{ UpdatedAt: time.Time{}, } +var AggregateIDtoDelete = 1 + // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-aggregates", func(w http.ResponseWriter, r *http.Request) { @@ -111,3 +114,13 @@ func HandleCreateSuccessfully(t *testing.T) { fmt.Fprintf(w, AggregateCreateBody) }) } + +func HandleDeleteSuccessfully(t *testing.T) { + v := strconv.Itoa(AggregateIDtoDelete) + th.Mux.HandleFunc("/os-aggregates/"+v, 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) + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index 7a59b0da0f..123fcfcb42 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -56,3 +56,12 @@ func TestCreateAggregates(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } + +func TestDeleteAggregates(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + err := aggregates.Delete(client.ServiceClient(), AggregateIDtoDelete).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go index 80a37b8169..9a991e951d 100644 --- a/openstack/compute/v2/extensions/aggregates/urls.go +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -9,3 +9,7 @@ func aggregatesListURL(c *gophercloud.ServiceClient) string { func aggregatesCreateURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-aggregates") } + +func aggregatesDeleteURL(c *gophercloud.ServiceClient, aggregateID string) string { + return c.ServiceURL("os-aggregates", aggregateID) +} From b2667db96072181f10f4ef7af905a34c078e91f6 Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Wed, 31 Jan 2018 11:36:09 +0300 Subject: [PATCH 0201/2296] Compute v2: Get aggregate (#746) * Add get aggregates support * Update doc.go --- .../compute/v2/extensions/aggregates/doc.go | 13 ++- .../v2/extensions/aggregates/requests.go | 9 ++ .../v2/extensions/aggregates/results.go | 14 ++- .../extensions/aggregates/testing/fixtures.go | 96 +++++++++++++------ .../aggregates/testing/requests_test.go | 13 +++ .../compute/v2/extensions/aggregates/urls.go | 4 + 6 files changed, 113 insertions(+), 36 deletions(-) diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index f5c1f640d3..140ac1acbe 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -2,7 +2,7 @@ Package aggregates manages information about the host aggregates in the OpenStack cloud. -Example of Create an aggregate +Example of Create Aggregate opts := aggregates.CreateOpts{ Name: "name", @@ -15,7 +15,16 @@ Example of Create an aggregate } fmt.Printf("%+v\n", aggregate) -Example of Delete an aggregate +Example of Show Aggregate Details + + aggregateID := 42 + aggregate, err := aggregates.Get(computeClient, aggregateID).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", aggregate) + +Example of Delete Aggregate aggregateID := 32 err := aggregates.Delete(computeClient, aggregateID).ExtractErr() diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index 22de32b4b2..d08cbdb939 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -50,3 +50,12 @@ func Delete(client *gophercloud.ServiceClient, aggregateID int) (r DeleteResult) }) return } + +// Get makes a request against the API to get details for an specific aggregate. +func Get(client *gophercloud.ServiceClient, aggregateID int) (r GetResult) { + v := strconv.Itoa(aggregateID) + _, r.Err = client.Get(aggregatesGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go index fa62b75140..bbd60440d9 100644 --- a/openstack/compute/v2/extensions/aggregates/results.go +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -78,11 +78,7 @@ type aggregatesResult struct { gophercloud.Result } -type CreateResult struct { - aggregatesResult -} - -func (r CreateResult) Extract() (*Aggregate, error) { +func (r aggregatesResult) Extract() (*Aggregate, error) { var s struct { Aggregate *Aggregate `json:"aggregate"` } @@ -90,6 +86,14 @@ func (r CreateResult) Extract() (*Aggregate, error) { return s.Aggregate, err } +type CreateResult struct { + aggregatesResult +} + +type GetResult struct { + aggregatesResult +} + type DeleteResult struct { gophercloud.ErrResult } diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go index c6214516e4..818ca2ba66 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -60,39 +60,66 @@ const AggregateCreateBody = ` } ` -// First aggregate from the AggregateListBody -var FirstFakeAggregate = aggregates.Aggregate{ - AvailabilityZone: "", - Hosts: []string{}, - ID: 1, - Metadata: map[string]string{}, - Name: "test-aggregate1", - CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), - UpdatedAt: time.Time{}, +const AggregateGetBody = ` +{ + "aggregate": { + "name": "test-aggregate2", + "availability_zone": "test-az", + "deleted": false, + "created_at": "2017-12-22T10:16:07.000000", + "updated_at": null, + "hosts": [ + "cmp0" + ], + "deleted_at": null, + "id": 4, + "metadata": { + "availability_zone": "test-az" + } + } } +` -// Second aggregate from the AggregateListBody -var SecondFakeAggregate = aggregates.Aggregate{ - AvailabilityZone: "test-az", - Hosts: []string{"cmp0"}, - ID: 4, - Metadata: map[string]string{"availability_zone": "test-az"}, - Name: "test-aggregate2", - CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), - UpdatedAt: time.Time{}, -} +var ( + // First aggregate from the AggregateListBody + FirstFakeAggregate = aggregates.Aggregate{ + AvailabilityZone: "", + Hosts: []string{}, + ID: 1, + Metadata: map[string]string{}, + Name: "test-aggregate1", + CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), + UpdatedAt: time.Time{}, + } -var CreatedAggregate = aggregates.Aggregate{ - AvailabilityZone: "london", - Hosts: nil, - ID: 32, - Metadata: nil, - Name: "name", - CreatedAt: time.Date(2016, 12, 27, 22, 51, 32, 0, time.UTC), - UpdatedAt: time.Time{}, -} + // Second aggregate from the AggregateListBody + SecondFakeAggregate = aggregates.Aggregate{ + AvailabilityZone: "test-az", + Hosts: []string{"cmp0"}, + ID: 4, + Metadata: map[string]string{"availability_zone": "test-az"}, + Name: "test-aggregate2", + CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), + UpdatedAt: time.Time{}, + } -var AggregateIDtoDelete = 1 + // Aggregate from the AggregateCreateBody + CreatedAggregate = aggregates.Aggregate{ + AvailabilityZone: "london", + Hosts: nil, + ID: 32, + Metadata: nil, + Name: "name", + CreatedAt: time.Date(2016, 12, 27, 22, 51, 32, 0, time.UTC), + UpdatedAt: time.Time{}, + } + + // Aggregate ID to delete + AggregateIDtoDelete = 1 + + // Aggregate ID to get, from the AggregateGetBody + AggregateIDtoGet = SecondFakeAggregate.ID +) // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { @@ -124,3 +151,14 @@ func HandleDeleteSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) }) } + +func HandleGetSuccessfully(t *testing.T) { + v := strconv.Itoa(AggregateIDtoGet) + th.Mux.HandleFunc("/os-aggregates/"+v, 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, AggregateGetBody) + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index 123fcfcb42..b838a1907c 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -65,3 +65,16 @@ func TestDeleteAggregates(t *testing.T) { err := aggregates.Delete(client.ServiceClient(), AggregateIDtoDelete).ExtractErr() th.AssertNoErr(t, err) } + +func TestGetAggregates(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + expected := SecondFakeAggregate + + actual, err := aggregates.Get(client.ServiceClient(), AggregateIDtoGet).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go index 9a991e951d..0b681f11f7 100644 --- a/openstack/compute/v2/extensions/aggregates/urls.go +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -13,3 +13,7 @@ func aggregatesCreateURL(c *gophercloud.ServiceClient) string { func aggregatesDeleteURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID) } + +func aggregatesGetURL(c *gophercloud.ServiceClient, aggregateID string) string { + return c.ServiceURL("os-aggregates", aggregateID) +} From 6e3bdd38004d86c8dd0e4acab69e91123751a86e Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy Date: Wed, 31 Jan 2018 16:27:27 +0300 Subject: [PATCH 0202/2296] Add missing fields `Deleted` and `DeletedAt` --- openstack/compute/v2/extensions/aggregates/results.go | 10 ++++++++++ .../v2/extensions/aggregates/testing/fixtures.go | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go index bbd60440d9..3b69b2b4d4 100644 --- a/openstack/compute/v2/extensions/aggregates/results.go +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -31,6 +31,14 @@ type Aggregate struct { // The date and time when the resource was updated, // if the resource has not been updated, this field will show as null. UpdatedAt time.Time `json:"-"` + + // The date and time when the resource was deleted, + // if the resource has not been deleted yet, this field will be null. + DeletedAt time.Time `json:"-"` + + // A boolean indicates whether this aggregate is deleted or not, + // if it has not been deleted, false will appear. + Deleted bool `json:"deleted"` } // UnmarshalJSON to override default @@ -40,6 +48,7 @@ func (r *Aggregate) UnmarshalJSON(b []byte) error { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -49,6 +58,7 @@ func (r *Aggregate) UnmarshalJSON(b []byte) error { r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) return nil } diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go index 818ca2ba66..9760710b1d 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -90,6 +90,8 @@ var ( Name: "test-aggregate1", CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), UpdatedAt: time.Time{}, + DeletedAt: time.Time{}, + Deleted: false, } // Second aggregate from the AggregateListBody @@ -101,6 +103,8 @@ var ( Name: "test-aggregate2", CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), UpdatedAt: time.Time{}, + DeletedAt: time.Time{}, + Deleted: false, } // Aggregate from the AggregateCreateBody @@ -112,6 +116,8 @@ var ( Name: "name", CreatedAt: time.Date(2016, 12, 27, 22, 51, 32, 0, time.UTC), UpdatedAt: time.Time{}, + DeletedAt: time.Time{}, + Deleted: false, } // Aggregate ID to delete From 1861349f37e119f911d170838a9d766b16e274e5 Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Sat, 3 Feb 2018 00:34:13 +0300 Subject: [PATCH 0203/2296] Acceptance test for create, get and delete aggregate (#745) * Add acceptance test for create and delete aggregate #739, #740 * Add reusable functions for Create/Delete aggregates * Fix acceptance test --- .../openstack/compute/v2/aggregates_test.go | 20 ++++++++++++ acceptance/openstack/compute/v2/compute.go | 32 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index d0771155ce..cad2379902 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -30,3 +30,23 @@ func TestAggregatesList(t *testing.T) { tools.PrintResource(t, h) } } + +func TestAggregatesCreateGetDelete(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + createdAggregate, err := CreateAggregate(t, client) + if err != nil { + t.Fatalf("Unable to create an aggregate: %v", err) + } + defer DeleteAggregate(t, client, createdAggregate) + + aggregate, err := aggregates.Get(client, createdAggregate.ID).Extract() + if err != nil { + t.Fatalf("Unable to get an aggregate: %v", err) + } + + tools.PrintResource(t, aggregate) +} diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index fad4673b42..5cb86185d3 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -26,6 +26,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "golang.org/x/crypto/ssh" ) @@ -570,6 +571,37 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blo return volumeAttachment, nil } +// CreateAggregate will create an aggregate with random name and available zone. +// An error will be returned if the aggregate could not be created. +func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregates.Aggregate, error) { + aggregateName := tools.RandomString("aggregate_", 5) + availableZone := tools.RandomString("zone_", 5) + t.Logf("Attempting to create aggregate %s", aggregateName) + + createOpts := aggregates.CreateOpts{Name: aggregateName, AvailabilityZone: availableZone} + + aggregate, err := aggregates.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created aggregate %d", aggregate.ID) + + return aggregate, nil +} + +// DeleteAggregate will delete a given host aggregate. A fatal error will occur if +// the aggregate deleting is failed. This works best when using it as a +// deferred function. +func DeleteAggregate(t *testing.T, client *gophercloud.ServiceClient, aggregate *aggregates.Aggregate) { + err := aggregates.Delete(client, aggregate.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete aggregate %d", aggregate.ID) + } + + t.Logf("Deleted aggregate: %d", aggregate.ID) +} + // DeleteDefaultRule deletes a default security group rule. // A fatal error will occur if the rule failed to delete. This works best when // using it as a deferred function. From 3b177406222d4b76d93e90d6ab412e7b7dc160f4 Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Sat, 3 Feb 2018 02:12:48 +0300 Subject: [PATCH 0204/2296] Compute v2: Update aggregate (#748) * Add aggregate update support * Change field to optional * Add acceptance test for Update --- .../openstack/compute/v2/aggregates_test.go | 42 ++++++++++++++++- .../compute/v2/extensions/aggregates/doc.go | 14 ++++++ .../v2/extensions/aggregates/requests.go | 29 ++++++++++++ .../v2/extensions/aggregates/results.go | 4 ++ .../extensions/aggregates/testing/fixtures.go | 45 +++++++++++++++++++ .../aggregates/testing/requests_test.go | 18 ++++++++ .../compute/v2/extensions/aggregates/urls.go | 4 ++ 7 files changed, 155 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index cad2379902..8a85e401ad 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -31,7 +31,22 @@ func TestAggregatesList(t *testing.T) { } } -func TestAggregatesCreateGetDelete(t *testing.T) { +func TestAggregatesCreateDelete(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + createdAggregate, err := CreateAggregate(t, client) + if err != nil { + t.Fatalf("Unable to create an aggregate: %v", err) + } + defer DeleteAggregate(t, client, createdAggregate) + + tools.PrintResource(t, createdAggregate) +} + +func TestAggregatesGet(t *testing.T) { client, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) @@ -50,3 +65,28 @@ func TestAggregatesCreateGetDelete(t *testing.T) { tools.PrintResource(t, aggregate) } + +func TestAggregatesUpdate(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + createdAggregate, err := CreateAggregate(t, client) + if err != nil { + t.Fatalf("Unable to create an aggregate: %v", err) + } + defer DeleteAggregate(t, client, createdAggregate) + + updateOpts := aggregates.UpdateOpts{ + Name: "new_aggregate_name", + AvailabilityZone: "new_azone", + } + + updatedAggregate, err := aggregates.Update(client, createdAggregate.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update an aggregate: %v", err) + } + + tools.PrintResource(t, updatedAggregate) +} diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index 140ac1acbe..3e353952ba 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -32,6 +32,20 @@ Example of Delete Aggregate panic(err) } +Example of Update Aggregate + + aggregateID := 42 + opts := aggregates.UpdateOpts{ + Name: "new_name", + AvailabilityZone: "nova2", + } + + aggregate, err := aggregates.Update(computeClient, aggregateID, opts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", aggregate) + Example of Retrieving list of all aggregates allPages, err := aggregates.List(computeClient).AllPages() diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index d08cbdb939..a26f0ca0f4 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -59,3 +59,32 @@ func Get(client *gophercloud.ServiceClient, aggregateID int) (r GetResult) { }) return } + +type UpdateOpts struct { + // The name of the host aggregate. + Name string `json:"name,omitempty"` + + // The availability zone of the host aggregate. + // You should use a custom availability zone rather than + // the default returned by the os-availability-zone API. + // The availability zone must not include ‘:’ in its name. + AvailabilityZone string `json:"availability_zone,omitempty"` +} + +func (opts UpdateOpts) ToAggregatesUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "aggregate") +} + +func Update(client *gophercloud.ServiceClient, aggregateID int, opts UpdateOpts) (r UpdateResult) { + v := strconv.Itoa(aggregateID) + + b, err := opts.ToAggregatesUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(aggregatesUpdateURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go index 3b69b2b4d4..b6e613e35e 100644 --- a/openstack/compute/v2/extensions/aggregates/results.go +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -107,3 +107,7 @@ type GetResult struct { type DeleteResult struct { gophercloud.ErrResult } + +type UpdateResult struct { + aggregatesResult +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go index 9760710b1d..e11e2b537d 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -80,6 +80,24 @@ const AggregateGetBody = ` } ` +const AggregateUpdateBody = ` +{ + "aggregate": { + "name": "test-aggregate2", + "availability_zone": "nova2", + "deleted": false, + "created_at": "2017-12-22T10:12:06.000000", + "updated_at": "2017-12-23T10:18:00.000000", + "hosts": [], + "deleted_at": null, + "id": 1, + "metadata": { + "availability_zone": "nova2" + } + } +} +` + var ( // First aggregate from the AggregateListBody FirstFakeAggregate = aggregates.Aggregate{ @@ -125,6 +143,22 @@ var ( // Aggregate ID to get, from the AggregateGetBody AggregateIDtoGet = SecondFakeAggregate.ID + + // Aggregate ID to update + AggregateIDtoUpdate = FirstFakeAggregate.ID + + // Updated aggregate + UpdatedAggregate = aggregates.Aggregate{ + AvailabilityZone: "nova2", + Hosts: []string{}, + ID: 1, + Metadata: map[string]string{"availability_zone": "nova2"}, + Name: "test-aggregate2", + CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 23, 10, 18, 0, 0, time.UTC), + DeletedAt: time.Time{}, + Deleted: false, + } ) // HandleListSuccessfully configures the test server to respond to a List request. @@ -168,3 +202,14 @@ func HandleGetSuccessfully(t *testing.T) { fmt.Fprintf(w, AggregateGetBody) }) } + +func HandleUpdateSuccessfully(t *testing.T) { + v := strconv.Itoa(AggregateIDtoUpdate) + th.Mux.HandleFunc("/os-aggregates/"+v, 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") + fmt.Fprintf(w, AggregateUpdateBody) + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index b838a1907c..e19e66e348 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -78,3 +78,21 @@ func TestGetAggregates(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } + +func TestUpdateAggregate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateSuccessfully(t) + + expected := UpdatedAggregate + + opts := aggregates.UpdateOpts{ + Name: "test-aggregates2", + AvailabilityZone: "nova2", + } + + actual, err := aggregates.Update(client.ServiceClient(), expected.ID, opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go index 0b681f11f7..6e45bd13d7 100644 --- a/openstack/compute/v2/extensions/aggregates/urls.go +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -17,3 +17,7 @@ func aggregatesDeleteURL(c *gophercloud.ServiceClient, aggregateID string) strin func aggregatesGetURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID) } + +func aggregatesUpdateURL(c *gophercloud.ServiceClient, aggregateID string) string { + return c.ServiceURL("os-aggregates", aggregateID) +} From fe85f9519740a5b5c09ecb0909609b58e59e5dec Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Sun, 4 Feb 2018 21:36:31 +0300 Subject: [PATCH 0205/2296] Compute v2: Add host to aggregate (#751) * Add host to aggregate support * Add unit test * Update doc.go * Add acceptance test * Fix acceptance test --- .../openstack/compute/v2/aggregates_test.go | 50 +++++++++++++++++++ .../compute/v2/extensions/aggregates/doc.go | 14 ++++++ .../v2/extensions/aggregates/requests.go | 27 +++++++++- .../v2/extensions/aggregates/results.go | 4 ++ .../extensions/aggregates/testing/fixtures.go | 44 ++++++++++++++++ .../aggregates/testing/requests_test.go | 17 +++++++ .../compute/v2/extensions/aggregates/urls.go | 4 ++ 7 files changed, 159 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index 8a85e401ad..5bb002d520 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -3,11 +3,14 @@ package v2 import ( + "fmt" "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" ) func TestAggregatesList(t *testing.T) { @@ -90,3 +93,50 @@ func TestAggregatesUpdate(t *testing.T) { tools.PrintResource(t, updatedAggregate) } + +func TestAggregatesAddHost(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + hostToAdd, err := getHypervisor(t, client) + if err != nil { + t.Fatal(err) + } + + createdAggregate, err := CreateAggregate(t, client) + if err != nil { + t.Fatalf("Unable to create an aggregate: %v", err) + } + defer DeleteAggregate(t, client, createdAggregate) + + addHostOpts := aggregates.AddHostOpts{ + Host: hostToAdd.HypervisorHostname, + } + + aggregateWithNewHost, err := aggregates.AddHost(client, createdAggregate.ID, addHostOpts).Extract() + if err != nil { + t.Fatalf("Unable to add host to aggregate: %v", err) + } + + tools.PrintResource(t, aggregateWithNewHost) +} + +func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (*hypervisors.Hypervisor, error) { + allPages, err := hypervisors.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to list hypervisors: %v", err) + } + + allHypervisors, err := hypervisors.ExtractHypervisors(allPages) + if err != nil { + t.Fatal("Unable to extract hypervisors") + } + + for _, h := range allHypervisors { + return &h, nil + } + + return nil, fmt.Errorf("Unable to get hypervisor") +} diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index 3e353952ba..d7fc339bf7 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -61,5 +61,19 @@ Example of Retrieving list of all aggregates for _, aggregate := range allAggregates { fmt.Printf("%+v\n", aggregate) } + +Example of Add Host + + aggregateID := 22 + opts := aggregates.AddHostOpts{ + Host: "newhost-cmp1" + } + + aggregate, err := aggregates.AddHost(computeClient, aggregateID, opts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", aggregate) + */ package aggregates diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index a26f0ca0f4..2bf2ec92b6 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -51,7 +51,7 @@ func Delete(client *gophercloud.ServiceClient, aggregateID int) (r DeleteResult) return } -// Get makes a request against the API to get details for an specific aggregate. +// Get makes a request against the API to get details for a specific aggregate. func Get(client *gophercloud.ServiceClient, aggregateID int) (r GetResult) { v := strconv.Itoa(aggregateID) _, r.Err = client.Get(aggregatesGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ @@ -75,6 +75,7 @@ func (opts UpdateOpts) ToAggregatesUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "aggregate") } +// Update makes a request against the API to update a specific aggregate. func Update(client *gophercloud.ServiceClient, aggregateID int, opts UpdateOpts) (r UpdateResult) { v := strconv.Itoa(aggregateID) @@ -88,3 +89,27 @@ func Update(client *gophercloud.ServiceClient, aggregateID int, opts UpdateOpts) }) return } + +type AddHostOpts struct { + // The name of the host. + Host string `json:"host" required:"true"` +} + +func (opts AddHostOpts) ToAggregatesAddHostMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "add_host") +} + +// AddHost makes a request against the API to add host to a specific aggregate. +func AddHost(client *gophercloud.ServiceClient, aggregateID int, opts AddHostOpts) (r ActionResult) { + v := strconv.Itoa(aggregateID) + + b, err := opts.ToAggregatesAddHostMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(aggregatesAddHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go index b6e613e35e..2ab0cf22f0 100644 --- a/openstack/compute/v2/extensions/aggregates/results.go +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -111,3 +111,7 @@ type DeleteResult struct { type UpdateResult struct { aggregatesResult } + +type ActionResult struct { + aggregatesResult +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go index e11e2b537d..f0fe61bf09 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -98,6 +98,27 @@ const AggregateUpdateBody = ` } ` +const AggregateAddHostBody = ` +{ + "aggregate": { + "name": "test-aggregate2", + "availability_zone": "test-az", + "deleted": false, + "created_at": "2017-12-22T10:16:07.000000", + "updated_at": null, + "hosts": [ + "cmp0", + "cmp1" + ], + "deleted_at": null, + "id": 4, + "metadata": { + "availability_zone": "test-az" + } + } +} +` + var ( // First aggregate from the AggregateListBody FirstFakeAggregate = aggregates.Aggregate{ @@ -159,6 +180,18 @@ var ( DeletedAt: time.Time{}, Deleted: false, } + + AggregateWithAddedHost = aggregates.Aggregate{ + AvailabilityZone: "test-az", + Hosts: []string{"cmp0", "cmp1"}, + ID: 4, + Metadata: map[string]string{"availability_zone": "test-az"}, + Name: "test-aggregate2", + CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), + UpdatedAt: time.Time{}, + DeletedAt: time.Time{}, + Deleted: false, + } ) // HandleListSuccessfully configures the test server to respond to a List request. @@ -213,3 +246,14 @@ func HandleUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, AggregateUpdateBody) }) } + +func HandleAddHostSuccessfully(t *testing.T) { + v := strconv.Itoa(AggregateWithAddedHost.ID) + th.Mux.HandleFunc("/os-aggregates/"+v+"/action", 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") + fmt.Fprintf(w, AggregateAddHostBody) + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index e19e66e348..b154957d50 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -96,3 +96,20 @@ func TestUpdateAggregate(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } + +func TestAddHostAggregate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAddHostSuccessfully(t) + + expected := AggregateWithAddedHost + + opts := aggregates.AddHostOpts{ + Host: "cmp1", + } + + actual, err := aggregates.AddHost(client.ServiceClient(), expected.ID, opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go index 6e45bd13d7..ed10604bd7 100644 --- a/openstack/compute/v2/extensions/aggregates/urls.go +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -21,3 +21,7 @@ func aggregatesGetURL(c *gophercloud.ServiceClient, aggregateID string) string { func aggregatesUpdateURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID) } + +func aggregatesAddHostURL(c *gophercloud.ServiceClient, aggregateID string) string { + return c.ServiceURL("os-aggregates", aggregateID, "action") +} From debc1adf8e41fb5c5b7e2021a1be0b4d0c78318a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 4 Feb 2018 20:34:22 +0000 Subject: [PATCH 0206/2296] Networking v2: Create Floating IP with Subnet This commit enables the creation of floating IPs by specifying a subnet ID. This is useful when a floating IP pool has multiple subnets. --- .../v2/extensions/layer3/floatingips_test.go | 47 +++++++++++++++++ .../extensions/layer3/floatingips/requests.go | 1 + .../floatingips/testing/requests_test.go | 52 +++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index 351020410e..b38d7283ca 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -3,6 +3,7 @@ package layer3 import ( + "os" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -10,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" ) func TestLayer3FloatingIPsList(t *testing.T) { @@ -98,3 +100,48 @@ func TestLayer3FloatingIPsCreateDelete(t *testing.T) { t.Fatalf("Unable to disassociate floating IP: %v", err) } } + +func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { + username := os.Getenv("OS_USERNAME") + if username != "admin" { + t.Skip("must be admin to run this test") + } + + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatalf("Unable to get choices: %v", err) + } + + listOpts := subnets.ListOpts{ + NetworkID: choices.ExternalNetworkID, + } + + subnetPages, err := subnets.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list subnets: %v", err) + } + + allSubnets, err := subnets.ExtractSubnets(subnetPages) + if err != nil { + t.Fatalf("Unable to extract subnets: %v", err) + } + + createOpts := floatingips.CreateOpts{ + FloatingNetworkID: choices.ExternalNetworkID, + SubnetID: allSubnets[0].ID, + } + + fip, err := floatingips.Create(client, createOpts).Extract() + if err != nil { + t.Fatalf("Unable to create floating IP: %v") + } + + tools.PrintResource(t, fip) + + DeleteFloatingIP(t, client, fip.ID) +} diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 1c8a8b2f13..0a6eb62cb6 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -53,6 +53,7 @@ type CreateOpts struct { FloatingIP string `json:"floating_ip_address,omitempty"` PortID string `json:"port_id,omitempty"` FixedIP string `json:"fixed_ip_address,omitempty"` + SubnetID string `json:"subnet_id,omitempty"` TenantID string `json:"tenant_id,omitempty"` } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index c665a2ef18..89c028e8a7 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -224,6 +224,58 @@ func TestCreateEmptyPort(t *testing.T) { th.AssertEquals(t, "10.0.0.3", ip.FixedIP) } +func TestCreateWithSubnetID(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/floatingips", 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, ` +{ + "floatingip": { + "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", + "subnet_id": "37adf01c-24db-467a-b845-7ab1e8216c01" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "floatingip": { + "router_id": null, + "tenant_id": "4969c491a3c74ee4af974e6d800c62de", + "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", + "fixed_ip_address": null, + "floating_ip_address": "172.24.4.3", + "port_id": null, + "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + } +} + `) + }) + + options := floatingips.CreateOpts{ + FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57", + SubnetID: "37adf01c-24db-467a-b845-7ab1e8216c01", + } + + ip, err := floatingips.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) + th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) + th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) + th.AssertEquals(t, "172.24.4.3", ip.FloatingIP) + th.AssertEquals(t, "", ip.PortID) + th.AssertEquals(t, "", ip.FixedIP) +} + func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 104e2578924bb3b211150c19414d0144b82165bb Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Tue, 6 Feb 2018 19:55:54 +0300 Subject: [PATCH 0207/2296] Compute v2: Remove host (#754) * Add `remove-host` support * Add unit test * Update doc.go * Add acceptance test --- .../openstack/compute/v2/aggregates_test.go | 13 +++++- .../compute/v2/extensions/aggregates/doc.go | 13 ++++++ .../v2/extensions/aggregates/requests.go | 24 +++++++++++ .../extensions/aggregates/testing/fixtures.go | 41 +++++++++++++++++++ .../aggregates/testing/requests_test.go | 17 ++++++++ .../compute/v2/extensions/aggregates/urls.go | 4 ++ 6 files changed, 111 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index 5bb002d520..24ba7a8ff2 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -94,7 +94,7 @@ func TestAggregatesUpdate(t *testing.T) { tools.PrintResource(t, updatedAggregate) } -func TestAggregatesAddHost(t *testing.T) { +func TestAggregatesAddRemoveHost(t *testing.T) { client, err := clients.NewComputeV2Client() if err != nil { t.Fatalf("Unable to create a compute client: %v", err) @@ -121,6 +121,17 @@ func TestAggregatesAddHost(t *testing.T) { } tools.PrintResource(t, aggregateWithNewHost) + + removeHostOpts := aggregates.RemoveHostOpts{ + Host: hostToAdd.HypervisorHostname, + } + + aggregateWithRemovedHost, err := aggregates.RemoveHost(client, createdAggregate.ID, removeHostOpts).Extract() + if err != nil { + t.Fatalf("Unable to remove host from aggregate: %v", err) + } + + tools.PrintResource(t, aggregateWithRemovedHost) } func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (*hypervisors.Hypervisor, error) { diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index d7fc339bf7..8f15289997 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -75,5 +75,18 @@ Example of Add Host } fmt.Printf("%+v\n", aggregate) +Example of Remove Host + + aggregateID := 22 + opts := aggregates.RemoveHostOpts{ + Host: "newhost-cmp1" + } + + aggregate, err := aggregates.RemoveHost(computeClient, aggregateID, opts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", aggregate) + */ package aggregates diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index 2bf2ec92b6..2f433f645f 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -113,3 +113,27 @@ func AddHost(client *gophercloud.ServiceClient, aggregateID int, opts AddHostOpt }) return } + +type RemoveHostOpts struct { + // The name of the host. + Host string `json:"host" required:"true"` +} + +func (opts RemoveHostOpts) ToAggregatesRemoveHostMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "remove_host") +} + +// RemoveHost makes a request against the API to remove host from a specific aggregate. +func RemoveHost(client *gophercloud.ServiceClient, aggregateID int, opts RemoveHostOpts) (r ActionResult) { + v := strconv.Itoa(aggregateID) + + b, err := opts.ToAggregatesRemoveHostMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(aggregatesRemoveHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go index f0fe61bf09..5d1d802548 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -119,6 +119,24 @@ const AggregateAddHostBody = ` } ` +const AggregateRemoveHostBody = ` +{ + "aggregate": { + "name": "test-aggregate2", + "availability_zone": "nova2", + "deleted": false, + "created_at": "2017-12-22T10:12:06.000000", + "updated_at": "2017-12-23T10:18:00.000000", + "hosts": [], + "deleted_at": null, + "id": 1, + "metadata": { + "availability_zone": "nova2" + } + } +} +` + var ( // First aggregate from the AggregateListBody FirstFakeAggregate = aggregates.Aggregate{ @@ -192,6 +210,18 @@ var ( DeletedAt: time.Time{}, Deleted: false, } + + AggregateWithRemovedHost = aggregates.Aggregate{ + AvailabilityZone: "nova2", + Hosts: []string{}, + ID: 1, + Metadata: map[string]string{"availability_zone": "nova2"}, + Name: "test-aggregate2", + CreatedAt: time.Date(2017, 12, 22, 10, 12, 6, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 23, 10, 18, 0, 0, time.UTC), + DeletedAt: time.Time{}, + Deleted: false, + } ) // HandleListSuccessfully configures the test server to respond to a List request. @@ -257,3 +287,14 @@ func HandleAddHostSuccessfully(t *testing.T) { fmt.Fprintf(w, AggregateAddHostBody) }) } + +func HandleRemoveHostSuccessfully(t *testing.T) { + v := strconv.Itoa(AggregateWithRemovedHost.ID) + th.Mux.HandleFunc("/os-aggregates/"+v+"/action", 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") + fmt.Fprintf(w, AggregateRemoveHostBody) + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index b154957d50..ddf7eb8625 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -113,3 +113,20 @@ func TestAddHostAggregate(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } + +func TestRemoveHostAggregate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAddHostSuccessfully(t) + + expected := AggregateWithAddedHost + + opts := aggregates.RemoveHostOpts{ + Host: "cmp1", + } + + actual, err := aggregates.RemoveHost(client.ServiceClient(), expected.ID, opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go index ed10604bd7..fb1090019a 100644 --- a/openstack/compute/v2/extensions/aggregates/urls.go +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -25,3 +25,7 @@ func aggregatesUpdateURL(c *gophercloud.ServiceClient, aggregateID string) strin func aggregatesAddHostURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID, "action") } + +func aggregatesRemoveHostURL(c *gophercloud.ServiceClient, aggregateID string) string { + return c.ServiceURL("os-aggregates", aggregateID, "action") +} From 6da026c32e2d622cc242d32984259c77237aefe1 Mon Sep 17 00:00:00 2001 From: Daniil Rutskiy <10889589+dstdfx@users.noreply.github.com> Date: Sat, 10 Feb 2018 05:43:43 +0300 Subject: [PATCH 0208/2296] Compute v2: Create Or Update Aggregate Metadata (#756) * Add support create/update metadata * Add unit test * Update doc.go * Add acceptance test * Fix doc.go * Change `Metadata` field type to map[string]interface{} * Add delete key to acceptance test --- .../openstack/compute/v2/aggregates_test.go | 35 +++++++++++++++ .../compute/v2/extensions/aggregates/doc.go | 17 ++++++- .../v2/extensions/aggregates/requests.go | 23 ++++++++++ .../extensions/aggregates/testing/fixtures.go | 44 +++++++++++++++++++ .../aggregates/testing/requests_test.go | 21 ++++++++- .../compute/v2/extensions/aggregates/urls.go | 4 ++ 6 files changed, 140 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index 24ba7a8ff2..7209831c3d 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -134,6 +134,41 @@ func TestAggregatesAddRemoveHost(t *testing.T) { tools.PrintResource(t, aggregateWithRemovedHost) } +func TestAggregatesSetRemoveMetadata(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + createdAggregate, err := CreateAggregate(t, client) + if err != nil { + t.Fatalf("Unable to create an aggregate: %v", err) + } + defer DeleteAggregate(t, client, createdAggregate) + + opts := aggregates.SetMetadataOpts{ + Metadata: map[string]interface{}{"key": "value"}, + } + + aggregateWithMetadata, err := aggregates.SetMetadata(client, createdAggregate.ID, opts).Extract() + if err != nil { + t.Fatalf("Unable to set metadata to aggregate: %v", err) + } + + tools.PrintResource(t, aggregateWithMetadata) + + optsToRemove := aggregates.SetMetadataOpts{ + Metadata: map[string]interface{}{"key": nil}, + } + + aggregateWithRemovedKey, err := aggregates.SetMetadata(client, createdAggregate.ID, optsToRemove).Extract() + if err != nil { + t.Fatalf("Unable to set metadata to aggregate: %v", err) + } + + tools.PrintResource(t, aggregateWithRemovedKey) +} + func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (*hypervisors.Hypervisor, error) { allPages, err := hypervisors.List(client).AllPages() if err != nil { diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index 8f15289997..97f1b033da 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -66,7 +66,7 @@ Example of Add Host aggregateID := 22 opts := aggregates.AddHostOpts{ - Host: "newhost-cmp1" + Host: "newhost-cmp1", } aggregate, err := aggregates.AddHost(computeClient, aggregateID, opts).Extract() @@ -79,7 +79,7 @@ Example of Remove Host aggregateID := 22 opts := aggregates.RemoveHostOpts{ - Host: "newhost-cmp1" + Host: "newhost-cmp1", } aggregate, err := aggregates.RemoveHost(computeClient, aggregateID, opts).Extract() @@ -88,5 +88,18 @@ Example of Remove Host } fmt.Printf("%+v\n", aggregate) +Example of Create or Update Metadata + + aggregateID := 22 + opts := aggregates.SetMetadata{ + Metadata: map[string]string{"key": "value"}, + } + + aggregate, err := aggregates.SetMetadata(computeClient, aggregateID, opts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", aggregate) + */ package aggregates diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index 2f433f645f..c37531c56a 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -137,3 +137,26 @@ func RemoveHost(client *gophercloud.ServiceClient, aggregateID int, opts RemoveH }) return } + +type SetMetadataOpts struct { + Metadata map[string]interface{} `json:"metadata" required:"true"` +} + +func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "set_metadata") +} + +// SetMetadata makes a request against the API to set metadata to a specific aggregate. +func SetMetadata(client *gophercloud.ServiceClient, aggregateID int, opts SetMetadataOpts) (r ActionResult) { + v := strconv.Itoa(aggregateID) + + b, err := opts.ToSetMetadataMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(aggregatesSetMetadataURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go index 5d1d802548..9ae71d2230 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures.go @@ -137,6 +137,27 @@ const AggregateRemoveHostBody = ` } ` +const AggregateSetMetadataBody = ` +{ + "aggregate": { + "name": "test-aggregate2", + "availability_zone": "test-az", + "deleted": false, + "created_at": "2017-12-22T10:16:07.000000", + "updated_at": "2017-12-23T10:18:00.000000", + "hosts": [ + "cmp0" + ], + "deleted_at": null, + "id": 4, + "metadata": { + "availability_zone": "test-az", + "key": "value" + } + } +} +` + var ( // First aggregate from the AggregateListBody FirstFakeAggregate = aggregates.Aggregate{ @@ -222,6 +243,18 @@ var ( DeletedAt: time.Time{}, Deleted: false, } + + AggregateWithUpdatedMetadata = aggregates.Aggregate{ + AvailabilityZone: "test-az", + Hosts: []string{"cmp0"}, + ID: 4, + Metadata: map[string]string{"availability_zone": "test-az", "key": "value"}, + Name: "test-aggregate2", + CreatedAt: time.Date(2017, 12, 22, 10, 16, 7, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 23, 10, 18, 0, 0, time.UTC), + DeletedAt: time.Time{}, + Deleted: false, + } ) // HandleListSuccessfully configures the test server to respond to a List request. @@ -298,3 +331,14 @@ func HandleRemoveHostSuccessfully(t *testing.T) { fmt.Fprintf(w, AggregateRemoveHostBody) }) } + +func HandleSetMetadataSuccessfully(t *testing.T) { + v := strconv.Itoa(AggregateWithUpdatedMetadata.ID) + th.Mux.HandleFunc("/os-aggregates/"+v+"/action", 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") + fmt.Fprintf(w, AggregateSetMetadataBody) + }) +} diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index ddf7eb8625..bfd18614cc 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -117,9 +117,9 @@ func TestAddHostAggregate(t *testing.T) { func TestRemoveHostAggregate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - HandleAddHostSuccessfully(t) + HandleRemoveHostSuccessfully(t) - expected := AggregateWithAddedHost + expected := AggregateWithRemovedHost opts := aggregates.RemoveHostOpts{ Host: "cmp1", @@ -130,3 +130,20 @@ func TestRemoveHostAggregate(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } + +func TestSetMetadataAggregate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSetMetadataSuccessfully(t) + + expected := AggregateWithUpdatedMetadata + + opts := aggregates.SetMetadataOpts{ + Metadata: map[string]interface{}{"key": "value"}, + } + + actual, err := aggregates.SetMetadata(client.ServiceClient(), expected.ID, opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go index fb1090019a..bb30c7fc90 100644 --- a/openstack/compute/v2/extensions/aggregates/urls.go +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -29,3 +29,7 @@ func aggregatesAddHostURL(c *gophercloud.ServiceClient, aggregateID string) stri func aggregatesRemoveHostURL(c *gophercloud.ServiceClient, aggregateID string) string { return c.ServiceURL("os-aggregates", aggregateID, "action") } + +func aggregatesSetMetadataURL(c *gophercloud.ServiceClient, aggregateID string) string { + return c.ServiceURL("os-aggregates", aggregateID, "action") +} From 197afc8d119485289f060e34e53a15ca97bf2743 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 14 Feb 2018 03:10:44 +0000 Subject: [PATCH 0209/2296] Compute v2: Fix flavor extra spec builder names --- openstack/compute/v2/flavors/requests.go | 30 +++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index e7041df059..4b406df957 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -236,21 +236,22 @@ func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string // CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the // CreateExtraSpecs requests. type CreateExtraSpecsOptsBuilder interface { - ToExtraSpecsCreateMap() (map[string]interface{}, error) + ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) } // ExtraSpecsOpts is a map that contains key-value pairs. type ExtraSpecsOpts map[string]string -// ToExtraSpecsCreateMap assembles a body for a Create request based on the -// contents of a ExtraSpecsOpts -func (opts ExtraSpecsOpts) ToExtraSpecsCreateMap() (map[string]interface{}, error) { +// ToFlavorExtraSpecsCreateMap assembles a body for a Create request based on +// the contents of ExtraSpecsOpts. +func (opts ExtraSpecsOpts) ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) { return map[string]interface{}{"extra_specs": opts}, nil } -// CreateExtraSpecs will create or update the extra-specs key-value pairs for the specified Flavor +// CreateExtraSpecs will create or update the extra-specs key-value pairs for +// the specified Flavor. func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { - b, err := opts.ToExtraSpecsCreateMap() + b, err := opts.ToFlavorExtraSpecsCreateMap() if err != nil { r.Err = err return @@ -261,15 +262,15 @@ func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts C return } -// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to the -// Update request. +// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to +// the Update request. type UpdateExtraSpecOptsBuilder interface { - ToExtraSpecUpdateMap() (map[string]string, string, error) + ToFlavorExtraSpecUpdateMap() (map[string]string, string, error) } -// ToExtraSpecUpdateMap assembles a body for an Update request based on the -// contents of a ExtraSpecOpts. -func (opts ExtraSpecsOpts) ToExtraSpecUpdateMap() (map[string]string, string, error) { +// ToFlavorExtraSpecUpdateMap assembles a body for an Update request based on +// the contents of a ExtraSpecOpts. +func (opts ExtraSpecsOpts) ToFlavorExtraSpecUpdateMap() (map[string]string, string, error) { if len(opts) != 1 { err := gophercloud.ErrInvalidInput{} err.Argument = "flavors.ExtraSpecOpts" @@ -285,9 +286,10 @@ func (opts ExtraSpecsOpts) ToExtraSpecUpdateMap() (map[string]string, string, er return opts, key, nil } -// UpdateExtraSpec will updates the value of the specified flavor's extra spec for the key in opts. +// UpdateExtraSpec will updates the value of the specified flavor's extra spec +// for the key in opts. func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { - b, key, err := opts.ToExtraSpecUpdateMap() + b, key, err := opts.ToFlavorExtraSpecUpdateMap() if err != nil { r.Err = err return From bd6e51250a76aa01959ffc4f6fbbddcfb48a9afb Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 15 Feb 2018 05:45:49 +0300 Subject: [PATCH 0210/2296] Add SubnetPoolID into the Subnet struct and GET requests (#764) * Add SubnetPoolID into the Subnet struct Add ability to populate subnet's subnetpool_id field from GET requests. Also add this field to GET and LIST unit tests. * Add SubnetPoolID as a query parameter to Subnets Add SubnetPoolID to the subnet List request. --- openstack/networking/v2/subnets/requests.go | 1 + openstack/networking/v2/subnets/results.go | 3 ++ .../networking/v2/subnets/testing/fixtures.go | 42 ++++++++++++++++++- .../v2/subnets/testing/requests_test.go | 2 + 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 403f692234..bef04f077f 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -27,6 +27,7 @@ type ListOpts struct { IPv6AddressMode string `q:"ipv6_address_mode"` IPv6RAMode string `q:"ipv6_ra_mode"` ID string `q:"id"` + SubnetPoolID string `q:"subnetpool_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index 743610f01e..8cc4dfac46 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -100,6 +100,9 @@ type Subnet struct { // The IPv6 router advertisement specifies whether the networking service // should transmit ICMPv6 packets. IPv6RAMode string `json:"ipv6_ra_mode"` + + // SubnetPoolID is the id of the subnet pool associated with the subnet. + SubnetPoolID string `json:"subnetpool_id"` } // SubnetPage is the page returned by a pager when traversing over a collection diff --git a/openstack/networking/v2/subnets/testing/fixtures.go b/openstack/networking/v2/subnets/testing/fixtures.go index 0dac09260f..89afa625f3 100644 --- a/openstack/networking/v2/subnets/testing/fixtures.go +++ b/openstack/networking/v2/subnets/testing/fixtures.go @@ -60,6 +60,25 @@ const SubnetListResult = ` "gateway_ip": null, "cidr": "192.168.1.0/24", "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" + }, + { + "name": "my_subnet_with_subnetpool", + "enable_dhcp": false, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.11.12.2", + "end": "10.11.12.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": null, + "cidr": "10.11.12.0/24", + "id": "38186a51-f373-4bbc-838b-6eaa1aa13eac", + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } ] } @@ -122,6 +141,26 @@ var Subnet3 = subnets.Subnet{ ID: "54d6f61d-db07-451c-9ab3-b9609b6b6f0c", } +var Subnet4 = subnets.Subnet{ + Name: "my_subnet_with_subnetpool", + EnableDHCP: false, + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", + TenantID: "4fd44f30292945e481c7b8a0c8908869", + DNSNameservers: []string{}, + AllocationPools: []subnets.AllocationPool{ + { + Start: "10.11.12.2", + End: "10.11.12.254", + }, + }, + HostRoutes: []subnets.HostRoute{}, + IPVersion: 4, + GatewayIP: "", + CIDR: "10.11.12.0/24", + ID: "38186a51-f373-4bbc-838b-6eaa1aa13eac", + SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", +} + const SubnetGetResult = ` { "subnet": { @@ -140,7 +179,8 @@ const SubnetGetResult = ` "ip_version": 4, "gateway_ip": "192.0.0.1", "cidr": "192.0.0.0/8", - "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b", + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } } ` diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index 2dd4e029df..dbe86a6d0e 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -39,6 +39,7 @@ func TestList(t *testing.T) { Subnet1, Subnet2, Subnet3, + Subnet4, } th.CheckDeepEquals(t, expected, actual) @@ -84,6 +85,7 @@ func TestGet(t *testing.T) { th.AssertEquals(t, s.GatewayIP, "192.0.0.1") th.AssertEquals(t, s.CIDR, "192.0.0.0/8") th.AssertEquals(t, s.ID, "54d6f61d-db07-451c-9ab3-b9609b6b6f0b") + th.AssertEquals(t, s.SubnetPoolID, "b80340c7-9960-4f67-a99c-02501656284b") } func TestCreate(t *testing.T) { From 3efdea1595d59f73812eb09a6d77b5d991ceb4b0 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Wed, 14 Feb 2018 22:17:44 +0300 Subject: [PATCH 0211/2296] Add the SubnetPoolID to the Subnet Create request Add the "subnetpool_id" field to the subnet creation request with an acceptance test. Also update the unit test. --- .../openstack/networking/v2/networking.go | 26 ++++++++++++++ .../openstack/networking/v2/subnets_test.go | 35 +++++++++++++++++++ openstack/networking/v2/subnets/requests.go | 3 ++ .../networking/v2/subnets/testing/fixtures.go | 6 ++-- .../v2/subnets/testing/requests_test.go | 2 ++ 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index bc463dde93..d19fb06262 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -188,6 +188,32 @@ func CreateSubnetWithNoGateway(t *testing.T, client *gophercloud.ServiceClient, return subnet, nil } +// CreateSubnetWithSubnetPool will create a subnet associated with the provided subnetpool on the specified Network ID. +// An error will be returned if the subnet or the subnetpool could not be created. +func CreateSubnetWithSubnetPool(t *testing.T, client *gophercloud.ServiceClient, networkID string, subnetPoolID string) (*subnets.Subnet, error) { + subnetName := tools.RandomString("TESTACC-", 8) + subnetOctet := tools.RandomInt(1, 250) + subnetCIDR := fmt.Sprintf("10.%d.0.0/24", subnetOctet) + createOpts := subnets.CreateOpts{ + NetworkID: networkID, + CIDR: subnetCIDR, + IPVersion: 4, + Name: subnetName, + EnableDHCP: gophercloud.Disabled, + SubnetPoolID: subnetPoolID, + } + + 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.") + return subnet, nil +} + // DeleteNetwork will delete a network with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index fd50a1f84b..ee665286ed 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -8,6 +8,7 @@ import ( "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/openstack/networking/v2/subnets" ) @@ -156,3 +157,37 @@ func TestSubnetsNoGateway(t *testing.T) { t.Fatalf("Gateway was not updated correctly") } } + +func TestSubnetsWithSubnetPool(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + // Create SubnetPool + subnetPool, err := subnetpools.CreateSubnetPool(t, client) + if err != nil { + t.Fatalf("Unable to create subnet pool: %v", err) + } + defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) + + // Create Subnet + subnet, err := CreateSubnetWithSubnetPool(t, client, network.ID, subnetPool.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer DeleteSubnet(t, client, subnet.ID) + + tools.PrintResource(t, subnet) + + if subnet.GatewayIP == "" { + t.Fatalf("A subnet pool was not associated.") + } +} diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index bef04f077f..32f5c78c67 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -115,6 +115,9 @@ type CreateOpts struct { // The IPv6 router advertisement specifies whether the networking service // should transmit ICMPv6 packets. IPv6RAMode string `json:"ipv6_ra_mode,omitempty"` + + // SubnetPoolID is the id of the subnet pool that subnet should be associated to. + SubnetPoolID string `json:"subnetpool_id,omitempty"` } // ToSubnetCreateMap builds a request body from CreateOpts. diff --git a/openstack/networking/v2/subnets/testing/fixtures.go b/openstack/networking/v2/subnets/testing/fixtures.go index 89afa625f3..619ea3e55e 100644 --- a/openstack/networking/v2/subnets/testing/fixtures.go +++ b/openstack/networking/v2/subnets/testing/fixtures.go @@ -199,7 +199,8 @@ const SubnetCreateRequest = ` "end": "192.168.199.254" } ], - "host_routes": [{"destination":"","nexthop": "bar"}] + "host_routes": [{"destination":"","nexthop": "bar"}], + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } } ` @@ -222,7 +223,8 @@ const SubnetCreateResult = ` "ip_version": 4, "gateway_ip": "192.168.199.1", "cidr": "192.168.199.0/24", - "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126" + "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126", + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" } } ` diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index dbe86a6d0e..208fc608f9 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -121,6 +121,7 @@ func TestCreate(t *testing.T) { HostRoutes: []subnets.HostRoute{ {NextHop: "bar"}, }, + SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", } s, err := subnets.Create(fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) @@ -141,6 +142,7 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, s.GatewayIP, "192.168.199.1") th.AssertEquals(t, s.CIDR, "192.168.199.0/24") th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126") + th.AssertEquals(t, s.SubnetPoolID, "b80340c7-9960-4f67-a99c-02501656284b") } func TestCreateNoGateway(t *testing.T) { From b922c6e3f157de4ad6e715842d5afd4c4256950d Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Sun, 18 Feb 2018 03:52:47 +0100 Subject: [PATCH 0212/2296] Vpnaas: Create VPN service (#760) * Added create method for vpn services * Added urls to urls file * Added results file for create method * Created unit test for vpn service creation * Added missing extract method to results file * Made parameter in create API compliant * Made flavorId parameter optional, fixed unit tests * Removed space in struct definition * removed comma in struct * formatting * added documentation file doc.go * added descriptive comments to fields * renamed ToVPNServiceCreateMap to ToServiceCreateMap * removed faulty description of subnet ID * removed field project_id * Added acceptance test for vpn service creation * Removed unnecessary print statement * Formatting * formatting doc.go file * Changed example in doc.go to verified working example. Also edited some formatting on inline comments * Fixed inline formatting for results.go * Added comparison to created object at the end * Changed individual attribute comparisons in unit test to struct comparison. Also changed AdminStateUp type from *bool to bool --- .../v2/extensions/vpnaas/service_test.go | 31 ++++++++ .../networking/v2/extensions/vpnaas/vpnaas.go | 32 ++++++++ .../v2/extensions/vpnaas/services/doc.go | 21 +++++ .../v2/extensions/vpnaas/services/requests.go | 52 +++++++++++++ .../v2/extensions/vpnaas/services/results.go | 62 +++++++++++++++ .../vpnaas/services/testing/requests_test.go | 77 +++++++++++++++++++ .../v2/extensions/vpnaas/services/urls.go | 16 ++++ 7 files changed, 291 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go create mode 100644 acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go create mode 100644 openstack/networking/v2/extensions/vpnaas/services/doc.go create mode 100644 openstack/networking/v2/extensions/vpnaas/services/requests.go create mode 100644 openstack/networking/v2/extensions/vpnaas/services/results.go create mode 100644 openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/services/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go new file mode 100644 index 0000000000..b002f1b5ae --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -0,0 +1,31 @@ +// +build acceptance networking fwaas + +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" +) + +func TestServiceCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + router, err := layer3.CreateExternalRouter(t, client) + if err != nil { + t.Fatalf("Unable to create router: %v", err) + } + defer layer3.DeleteRouter(t, client, router.ID) + + service, err := CreateService(t, client, router.ID) + if err != nil { + t.Fatalf("Unable to create service: %v", err) + } + + tools.PrintResource(t, service) +} diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go new file mode 100644 index 0000000000..c50c3b796d --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -0,0 +1,32 @@ +package vpnaas + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" +) + +// CreateService will create a Service with a random name and a specified router ID +// An error will be returned if the service could not be created. +func CreateService(t *testing.T, client *gophercloud.ServiceClient, routerID string) (*services.Service, error) { + serviceName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create service %s", serviceName) + + iTrue := true + createOpts := services.CreateOpts{ + Name: serviceName, + AdminStateUp: &iTrue, + RouterID: routerID, + } + service, err := services.Create(client, createOpts).Extract() + if err != nil { + return service, err + } + + t.Logf("Successfully created service %s", serviceName) + + return service, nil +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/doc.go b/openstack/networking/v2/extensions/vpnaas/services/doc.go new file mode 100644 index 0000000000..7cddfcaf64 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/services/doc.go @@ -0,0 +1,21 @@ +/* +Package services allows management and retrieval of VPN services in the +OpenStack Networking Service. + + +Example to Create a Service + + createOpts := services.CreateOpts{ + Name: "vpnservice1", + Description: "A service", + RouterID: "2512e759-e8d7-4eea-a0af-4a85927a2e59", + AdminStateUp: gophercloud.Enabled, + } + + service, err := services.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +*/ +package services diff --git a/openstack/networking/v2/extensions/vpnaas/services/requests.go b/openstack/networking/v2/extensions/vpnaas/services/requests.go new file mode 100644 index 0000000000..7feeb7baec --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/services/requests.go @@ -0,0 +1,52 @@ +package services + +import "github.com/gophercloud/gophercloud" + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToServiceCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new VPN service +type CreateOpts struct { + // TenantID specifies a tenant to own the VPN service. The caller must have + // an admin role in order to set this. Otherwise, this field is left unset + // and the caller will be the owner. + TenantID string `json:"tenant_id,omitempty"` + + // SubnetID is the ID of the subnet. + SubnetID string `json:"subnet_id,omitempty"` + + // RouterID is the ID of the router. + RouterID string `json:"router_id" required:"true"` + + // Description is the human readable description of the service. + Description string `json:"description,omitempty"` + + // AdminStateUp is the administrative state of the resource, which is up (true) or down (false). + AdminStateUp *bool `json:"admin_state_up"` + + // Name is the human readable name of the service. + Name string `json:"name,omitempty"` + + // The ID of the flavor. + FlavorID string `json:"flavor_id,omitempty"` +} + +// ToServiceCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "vpnservice") +} + +// Create accepts a CreateOpts struct and uses the values to create a new +// VPN service. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToServiceCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/results.go b/openstack/networking/v2/extensions/vpnaas/services/results.go new file mode 100644 index 0000000000..73aad3fc17 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/services/results.go @@ -0,0 +1,62 @@ +package services + +import ( + "github.com/gophercloud/gophercloud" +) + +// Service is a VPN Service +type Service struct { + // TenantID is the ID of the project. + TenantID string `json:"tenant_id"` + + // SubnetID is the ID of the subnet. + SubnetID string `json:"subnet_id"` + + // RouterID is the ID of the router. + RouterID string `json:"router_id"` + + // Description is a human-readable description for the resource. + // Default is an empty string + Description string `json:"description"` + + // AdminStateUp is the administrative state of the resource, which is up (true) or down (false). + AdminStateUp bool `json:"admin_state_up"` + + // Name is the human readable name of the service. + Name string `json:"name"` + + // Status indicates whether IPsec VPN service is currently operational. + // Values are ACTIVE, DOWN, BUILD, ERROR, PENDING_CREATE, PENDING_UPDATE, or PENDING_DELETE. + Status string `json:"status"` + + // ID is the unique ID of the VPN service. + ID string `json:"id"` + + // ExternalV6IP is the read-only external (public) IPv6 address that is used for the VPN service. + ExternalV6IP string `json:"external_v6_ip"` + + // ExternalV4IP is the read-only external (public) IPv4 address that is used for the VPN service. + ExternalV4IP string `json:"external_v4_ip"` + + // FlavorID is the ID of the flavor. + FlavorID string `json:"flavor_id"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a VPN service. +func (r commonResult) Extract() (*Service, error) { + var s struct { + Service *Service `json:"vpnservice"` + } + err := r.ExtractInto(&s) + return s.Service, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Service. +type CreateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go new file mode 100644 index 0000000000..e7548ed722 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go @@ -0,0 +1,77 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud" + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/vpnservices", 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, ` +{ + "vpnservice": { + "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + "name": "vpn", + "admin_state_up": true, + "description": "OpenStack VPN service", + "tenant_id": "10039663455a446d8ba2cbb058b0f578" + } +} `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "vpnservice": { + "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + "status": "PENDING_CREATE", + "name": "vpn", + "external_v6_ip": "2001:db8::1", + "admin_state_up": true, + "subnet_id": null, + "tenant_id": "10039663455a446d8ba2cbb058b0f578", + "external_v4_ip": "172.32.1.11", + "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "description": "OpenStack VPN service" + } +} + `) + }) + + options := services.CreateOpts{ + TenantID: "10039663455a446d8ba2cbb058b0f578", + Name: "vpn", + Description: "OpenStack VPN service", + AdminStateUp: gophercloud.Enabled, + RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + } + actual, err := services.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + expected := services.Service{ + RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + Status: "PENDING_CREATE", + Name: "vpn", + ExternalV6IP: "2001:db8::1", + AdminStateUp: true, + SubnetID: "", + TenantID: "10039663455a446d8ba2cbb058b0f578", + ExternalV4IP: "172.32.1.11", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Description: "OpenStack VPN service", + } + th.AssertDeepEquals(t, expected, *actual) +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/urls.go b/openstack/networking/v2/extensions/vpnaas/services/urls.go new file mode 100644 index 0000000000..fe8b343fe3 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/services/urls.go @@ -0,0 +1,16 @@ +package services + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "vpn" + resourcePath = "vpnservices" +) + +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 1858d1f75923be847d3a98198dcc4756dc177beb Mon Sep 17 00:00:00 2001 From: PriyankaJ77 <35596310+PriyankaJ77@users.noreply.github.com> Date: Tue, 20 Feb 2018 05:56:48 +0530 Subject: [PATCH 0213/2296] Network v2: RBAC-Create (#755) * [wip] Network v2: RBAC-Create * Added Acceptance Tests for RBAC. * Addressed some nits. * Addressed Review Comments * Resolved build failure * Addressed doc nits, RBAC to RBACPolicy naming convention, and restricting the acceptance test to only be run by admin user. --- .../v2/extensions/rbacpolicies/pkg.go | 1 + .../extensions/rbacpolicies/rbacpolicies.go | 29 ++++++++++ .../rbacpolicies/rbacpolicies_test.go | 56 +++++++++++++++++++ .../v2/extensions/rbacpolicies/doc.go | 31 ++++++++++ .../v2/extensions/rbacpolicies/requests.go | 51 +++++++++++++++++ .../v2/extensions/rbacpolicies/results.go | 53 ++++++++++++++++++ .../v2/extensions/rbacpolicies/testing/doc.go | 2 + .../rbacpolicies/testing/fixtures.go | 40 +++++++++++++ .../rbacpolicies/testing/requests_test.go | 39 +++++++++++++ .../v2/extensions/rbacpolicies/urls.go | 11 ++++ 10 files changed, 313 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/rbacpolicies/pkg.go create mode 100644 acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go create mode 100644 acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go create mode 100644 openstack/networking/v2/extensions/rbacpolicies/doc.go create mode 100644 openstack/networking/v2/extensions/rbacpolicies/requests.go create mode 100644 openstack/networking/v2/extensions/rbacpolicies/results.go create mode 100644 openstack/networking/v2/extensions/rbacpolicies/testing/doc.go create mode 100644 openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/rbacpolicies/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/pkg.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/pkg.go new file mode 100644 index 0000000000..f682aeab06 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/pkg.go @@ -0,0 +1 @@ +package rbacpolicies diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go new file mode 100644 index 0000000000..27d3df7f8b --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go @@ -0,0 +1,29 @@ +package rbacpolicies + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" +) + +// CreateRBACPolicy will create a rbac-policy. An error will be returned if the +// rbac-policy could not be created. +func CreateRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, tenantID, networkID string) (*rbacpolicies.RBACPolicy, error) { + createOpts := rbacpolicies.CreateOpts{ + Action: rbacpolicies.ActionAccessShared, + ObjectType: "network", + TargetTenant: tenantID, + ObjectID: networkID, + } + + t.Logf("Trying to create rbac_policy") + + rbacPolicy, err := rbacpolicies.Create(client, createOpts).Extract() + if err != nil { + return rbacPolicy, err + } + + t.Logf("Successfully created rbac_policy") + return rbacPolicy, nil +} diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go new file mode 100644 index 0000000000..9c0b4bd515 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -0,0 +1,56 @@ +// +build acceptance + +package rbacpolicies + +import ( + "os" + "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" +) + +func TestRBACPolicyCreate(t *testing.T) { + username := os.Getenv("OS_USERNAME") + if username != "admin" { + t.Skip("must be admin to run this test") + } + + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create a network + network, err := networking.CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer networking.DeleteNetwork(t, client, network.ID) + + tools.PrintResource(t, network) + + identityClient, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v") + } + + // Create a project/tenant + project, err := projects.CreateProject(t, identityClient, nil) + if err != nil { + t.Fatalf("Unable to create project: %v", err) + } + defer projects.DeleteProject(t, identityClient, project.ID) + + tools.PrintResource(t, project) + + // Create a rbac-policy + rbacPolicy, err := CreateRBACPolicy(t, client, project.ID, network.ID) + if err != nil { + t.Fatalf("Unable to create rbac-policy: %v", err) + } + + tools.PrintResource(t, rbacPolicy) +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/doc.go b/openstack/networking/v2/extensions/rbacpolicies/doc.go new file mode 100644 index 0000000000..4a3aec1a59 --- /dev/null +++ b/openstack/networking/v2/extensions/rbacpolicies/doc.go @@ -0,0 +1,31 @@ +/* +Package rbacpolicies contains functionality for working with Neutron RBAC Policies. +Role-Based Access Control (RBAC) policy framework enables both operators +and users to grant access to resources for specific projects. + +Sharing an object with a specific project is accomplished by creating a +policy entry that permits the target project the access_as_shared action +on that object. + +To make a network available as an external network for specific projects +rather than all projects, use the access_as_external action. +If a network is marked as external during creation, it now implicitly creates +a wildcard RBAC policy granting everyone access to preserve previous behavior +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) + } + +*/ +package rbacpolicies diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go new file mode 100644 index 0000000000..286c02a94e --- /dev/null +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -0,0 +1,51 @@ +package rbacpolicies + +import ( + "github.com/gophercloud/gophercloud" +) + +// PolicyAction maps to Action for the RBAC policy. +// Which allows access_as_external or access_as_shared. +type PolicyAction string + +const ( + // ActionAccessExternal returns Action for the RBAC policy as access_as_external. + ActionAccessExternal PolicyAction = "access_as_external" + + // ActionAccessShared returns Action for the RBAC policy as access_as_shared. + ActionAccessShared PolicyAction = "access_as_shared" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToRBACPolicyCreateMap() (map[string]interface{}, error) +} + +// CreateOpts represents options used to create a rbac-policy. +type CreateOpts struct { + Action PolicyAction `json:"action" required:"true"` + ObjectType string `json:"object_type" required:"true"` + TargetTenant string `json:"target_tenant" required:"true"` + ObjectID string `json:"object_id" required:"true"` +} + +// ToRBACPolicyCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToRBACPolicyCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "rbac_policy") +} + +// Create accepts a CreateOpts struct and creates a new rbac-policy using the values +// provided. +// +// The tenant ID that is contained in the URI is the tenant that creates the +// rbac-policy. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToRBACPolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go new file mode 100644 index 0000000000..c2d120efe3 --- /dev/null +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -0,0 +1,53 @@ +package rbacpolicies + +import ( + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts RBAC Policy resource. +func (r commonResult) Extract() (*RBACPolicy, error) { + var s RBACPolicy + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "rbac_policy") +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a RBAC Policy. +type CreateResult struct { + commonResult +} + +// RBACPolicy represents a RBAC policy. +type RBACPolicy struct { + // UUID of the RBAC policy. + ID string `json:"id"` + + // Action for the RBAC policy which is access_as_external or access_as_shared. + Action PolicyAction `json:"action"` + + // ObjectID is the ID of the object_type resource. + // An object_type of network returns a network ID and + // object_type of qos-policy returns a QoS ID. + ObjectID string `json:"object_id"` + + // ObjectType is the type of the object that the RBAC policy affects. + // Types include qos-policy or network. + ObjectType string `json:"object_type"` + + // TenantID is the ID of the project that owns the resource. + TenantID string `json:"tenant_id"` + + // TargetTenant is the ID of the tenant to which the RBAC policy will be enforced. + TargetTenant string `json:"target_tenant"` + + // ProjectID is the ID of the project. + ProjectID string `json:"project_id"` +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/doc.go b/openstack/networking/v2/extensions/rbacpolicies/testing/doc.go new file mode 100644 index 0000000000..e95610ae4f --- /dev/null +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing includes rbac unit tests +package testing diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go new file mode 100644 index 0000000000..cd26e0139b --- /dev/null +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go @@ -0,0 +1,40 @@ +package testing + +import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" +) + +// CreateRequest is the structure of request body to create rbac-policy. +const CreateRequest = ` +{ + "rbac_policy": { + "action": "access_as_shared", + "object_type": "network", + "target_tenant": "6e547a3bcfe44702889fdeff3c3520c3", + "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc" + } +}` + +// CreateResponse is the structure of response body of rbac-policy create. +const CreateResponse = ` +{ + "rbac_policy": { + "target_tenant": "6e547a3bcfe44702889fdeff3c3520c3", + "tenant_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", + "object_type": "network", + "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc", + "action": "access_as_shared", + "project_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", + "id": "2cf7523a-93b5-4e69-9360-6c6bf986bb7c" + } +}` + +var rbacPolicy1 = rbacpolicies.RBACPolicy{ + ID: "2cf7523a-93b5-4e69-9360-6c6bf986bb7c", + Action: rbacpolicies.ActionAccessShared, + ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc", + ObjectType: "network", + TenantID: "3de27ce0a2a54cc6ae06dc62dd0ec832", + TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", + ProjectID: "3de27ce0a2a54cc6ae06dc62dd0ec832", +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go new file mode 100644 index 0000000000..50d1ac4063 --- /dev/null +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -0,0 +1,39 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/rbac-policies", 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) + }) + + options := rbacpolicies.CreateOpts{ + Action: rbacpolicies.ActionAccessShared, + ObjectType: "network", + TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", + ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc", + } + rbacResult, err := rbacpolicies.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &rbacPolicy1, rbacResult) +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/urls.go b/openstack/networking/v2/extensions/rbacpolicies/urls.go new file mode 100644 index 0000000000..542b38b58c --- /dev/null +++ b/openstack/networking/v2/extensions/rbacpolicies/urls.go @@ -0,0 +1,11 @@ +package rbacpolicies + +import "github.com/gophercloud/gophercloud" + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("rbac-policies") +} + +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From bfeb72b9527bf840b4bffd2f7e082d6ab974e7d3 Mon Sep 17 00:00:00 2001 From: Manjunath Patil Date: Wed, 21 Feb 2018 08:06:37 +0530 Subject: [PATCH 0214/2296] Network v2: RBAC-GET-BY-ID (#770) * [wip] Network v2: RBAC-Create * Added Acceptance Tests for RBAC. * Addressed some nits. * Addressed Review Comments * Resolved build failure * Addressed doc nits, RBAC to RBACPolicy naming convention, and restricting the acceptance test to only be run by admin user. * Network v2: RBAC-GET-BY-ID --- .../rbacpolicies/rbacpolicies_test.go | 10 ++++++++++ .../v2/extensions/rbacpolicies/requests.go | 6 ++++++ .../v2/extensions/rbacpolicies/results.go | 6 ++++++ .../rbacpolicies/testing/fixtures.go | 14 ++++++++++++++ .../rbacpolicies/testing/requests_test.go | 19 +++++++++++++++++++ .../v2/extensions/rbacpolicies/urls.go | 8 ++++++++ 6 files changed, 63 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index 9c0b4bd515..de944c0c08 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -10,6 +10,7 @@ import ( 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/openstack/networking/v2/extensions/rbacpolicies" ) func TestRBACPolicyCreate(t *testing.T) { @@ -53,4 +54,13 @@ func TestRBACPolicyCreate(t *testing.T) { } tools.PrintResource(t, rbacPolicy) + + // Get the rbac-policy by ID + t.Logf("Get rbac_policy by ID") + newrbacPolicy, err := rbacpolicies.Get(client, rbacPolicy.ID).Extract() + if err != nil { + t.Fatalf("Unable to retrieve rbac policy: %v", err) + } + + tools.PrintResource(t, newrbacPolicy) } diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index 286c02a94e..7332fce9ad 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -4,6 +4,12 @@ import ( "github.com/gophercloud/gophercloud" ) +// Get retrieves a specific rbac policy based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + return +} + // PolicyAction maps to Action for the RBAC policy. // Which allows access_as_external or access_as_shared. type PolicyAction string diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go index c2d120efe3..0e85a66798 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/results.go +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -25,6 +25,12 @@ type CreateResult struct { commonResult } +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a RBAC Policy. +type GetResult struct { + commonResult +} + // RBACPolicy represents a RBAC policy. type RBACPolicy struct { // UUID of the RBAC policy. diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go index cd26e0139b..4328baac40 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go @@ -29,6 +29,20 @@ const CreateResponse = ` } }` +// GetResponse is the structure of the response body of rbac-policy get operation. +const GetResponse = ` +{ + "rbac_policy": { + "target_tenant": "6e547a3bcfe44702889fdeff3c3520c3", + "tenant_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", + "object_type": "network", + "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc", + "action": "access_as_shared", + "project_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", + "id": "2cf7523a-93b5-4e69-9360-6c6bf986bb7c" + } +}` + var rbacPolicy1 = rbacpolicies.RBACPolicy{ ID: "2cf7523a-93b5-4e69-9360-6c6bf986bb7c", Action: rbacpolicies.ActionAccessShared, diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go index 50d1ac4063..ccd433098b 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -37,3 +37,22 @@ func TestCreate(t *testing.T) { th.AssertDeepEquals(t, &rbacPolicy1, rbacResult) } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/rbac-policies/2cf7523a-93b5-4e69-9360-6c6bf986bb7c", 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, GetResponse) + }) + + n, err := rbacpolicies.Get(fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &rbacPolicy1, n) +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/urls.go b/openstack/networking/v2/extensions/rbacpolicies/urls.go index 542b38b58c..068daa1998 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/urls.go +++ b/openstack/networking/v2/extensions/rbacpolicies/urls.go @@ -2,6 +2,10 @@ package rbacpolicies import "github.com/gophercloud/gophercloud" +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("rbac-policies", id) +} + func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("rbac-policies") } @@ -9,3 +13,7 @@ func rootURL(c *gophercloud.ServiceClient) string { func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From d693a2e15df35aba7480c4c719d63066bc59adee Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 21 Feb 2018 04:03:51 +0100 Subject: [PATCH 0215/2296] Vpnaas: Delete service (#769) * Added delete request * Added delete result * added unit and acceptance tests for delete function * added delete Service to acceptancetest * Added documentation in doc.go file --- .../v2/extensions/vpnaas/service_test.go | 1 + .../networking/v2/extensions/vpnaas/vpnaas.go | 14 ++++++++++++++ .../v2/extensions/vpnaas/services/doc.go | 8 ++++++++ .../v2/extensions/vpnaas/services/requests.go | 7 +++++++ .../v2/extensions/vpnaas/services/results.go | 6 ++++++ .../vpnaas/services/testing/requests_test.go | 14 ++++++++++++++ 6 files changed, 50 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index b002f1b5ae..658f0840d5 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -26,6 +26,7 @@ func TestServiceCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create service: %v", err) } + defer DeleteService(t, client, service.ID) tools.PrintResource(t, service) } diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index c50c3b796d..5a5ec1de3f 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -30,3 +30,17 @@ func CreateService(t *testing.T, client *gophercloud.ServiceClient, routerID str return service, nil } + +// DeleteService will delete a service 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 DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID string) { + t.Logf("Attempting to delete service: %s", serviceID) + + err := services.Delete(client, serviceID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete service %s: %v", serviceID, err) + } + + t.Logf("Service deleted: %s", serviceID) +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/doc.go b/openstack/networking/v2/extensions/vpnaas/services/doc.go index 7cddfcaf64..2060bcb086 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/services/doc.go @@ -17,5 +17,13 @@ Example to Create a Service panic(err) } +Example to Delete a Service + + serviceID := "38aee955-6283-4279-b091-8b9c828000ec" + err := policies.Delete(networkClient, serviceID).ExtractErr() + if err != nil { + panic(err) + } + */ package services diff --git a/openstack/networking/v2/extensions/vpnaas/services/requests.go b/openstack/networking/v2/extensions/vpnaas/services/requests.go index 7feeb7baec..7073508f9f 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/services/requests.go @@ -50,3 +50,10 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } + +// Delete will permanently delete a particular VPN service based on its +// unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/results.go b/openstack/networking/v2/extensions/vpnaas/services/results.go index 73aad3fc17..77bc02b76c 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/results.go +++ b/openstack/networking/v2/extensions/vpnaas/services/results.go @@ -60,3 +60,9 @@ func (r commonResult) Extract() (*Service, error) { type CreateResult struct { commonResult } + +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the operation succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go index e7548ed722..450a56ad26 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go @@ -75,3 +75,17 @@ func TestCreate(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/vpnservices/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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) + }) + + res := services.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + th.AssertNoErr(t, res.Err) +} From d4d605f09f27442acc49fb097f5432501c82ef98 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 21 Feb 2018 04:30:35 +0000 Subject: [PATCH 0216/2296] Identity v3: Add support for RegionID in token catalog endpoints --- openstack/endpoint_location.go | 2 +- openstack/identity/v3/tokens/results.go | 1 + .../identity/v3/tokens/testing/fixtures.go | 6 +++ openstack/testing/endpoint_location_test.go | 43 +++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/openstack/endpoint_location.go b/openstack/endpoint_location.go index 070ea7cbef..12c8aebcf7 100644 --- a/openstack/endpoint_location.go +++ b/openstack/endpoint_location.go @@ -84,7 +84,7 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpt return "", err } if (opts.Availability == gophercloud.Availability(endpoint.Interface)) && - (opts.Region == "" || endpoint.Region == opts.Region) { + (opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) { endpoints = append(endpoints, endpoint) } } diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index 6e78d1cbdb..ebdca58f65 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -13,6 +13,7 @@ import ( type Endpoint struct { ID string `json:"id"` Region string `json:"region"` + RegionID string `json:"region_id"` Interface string `json:"interface"` URL string `json:"url"` } diff --git a/openstack/identity/v3/tokens/testing/fixtures.go b/openstack/identity/v3/tokens/testing/fixtures.go index a475acb1b7..e6f44178a4 100644 --- a/openstack/identity/v3/tokens/testing/fixtures.go +++ b/openstack/identity/v3/tokens/testing/fixtures.go @@ -125,18 +125,21 @@ var catalogEntry1 = tokens.CatalogEntry{ tokens.Endpoint{ ID: "3eac9e7588eb4eb2a4650cf5e079505f", Region: "RegionOne", + RegionID: "RegionOne", Interface: "admin", URL: "http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", }, tokens.Endpoint{ ID: "6b33fabc69c34ea782a3f6282582b59f", Region: "RegionOne", + RegionID: "RegionOne", Interface: "internal", URL: "http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", }, tokens.Endpoint{ ID: "dae63c71bee24070a71f5425e7a916b5", Region: "RegionOne", + RegionID: "RegionOne", Interface: "public", URL: "http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", }, @@ -150,18 +153,21 @@ var catalogEntry2 = tokens.CatalogEntry{ tokens.Endpoint{ ID: "0539aeff80954a0bb756cec496768d3d", Region: "RegionOne", + RegionID: "RegionOne", Interface: "admin", URL: "http://127.0.0.1:35357/v3", }, tokens.Endpoint{ ID: "15bdf2d0853e4c939993d29548b1b56f", Region: "RegionOne", + RegionID: "RegionOne", Interface: "public", URL: "http://127.0.0.1:5000/v3", }, tokens.Endpoint{ ID: "3b4423c54ba343c58226bc424cb11c4b", Region: "RegionOne", + RegionID: "RegionOne", Interface: "internal", URL: "http://127.0.0.1:5000/v3", }, diff --git a/openstack/testing/endpoint_location_test.go b/openstack/testing/endpoint_location_test.go index ea7bdd2bf0..5ac773eee6 100644 --- a/openstack/testing/endpoint_location_test.go +++ b/openstack/testing/endpoint_location_test.go @@ -178,6 +178,30 @@ var catalog3 = tokens3.ServiceCatalog{ }, }, }, + tokens3.CatalogEntry{ + Type: "someother", + Name: "someother", + Endpoints: []tokens3.Endpoint{ + tokens3.Endpoint{ + ID: "1", + Region: "someother", + Interface: "public", + URL: "https://public.correct.com/", + }, + tokens3.Endpoint{ + ID: "2", + RegionID: "someother", + Interface: "admin", + URL: "https://admin.correct.com/", + }, + tokens3.Endpoint{ + ID: "3", + RegionID: "someother", + Interface: "internal", + URL: "https://internal.correct.com/", + }, + }, + }, }, } @@ -229,3 +253,22 @@ func TestV3EndpointBadAvailability(t *testing.T) { }) th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error()) } + +func TestV3EndpointWithRegionID(t *testing.T) { + expectedURLs := map[gophercloud.Availability]string{ + gophercloud.AvailabilityPublic: "https://public.correct.com/", + gophercloud.AvailabilityAdmin: "https://admin.correct.com/", + gophercloud.AvailabilityInternal: "https://internal.correct.com/", + } + + for availability, expected := range expectedURLs { + actual, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ + Type: "someother", + Name: "someother", + Region: "someother", + Availability: availability, + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, expected, actual) + } +} From 94986f2ad4599240265d6d225acd894cb1b41c5d Mon Sep 17 00:00:00 2001 From: PriyankaJ77 <35596310+PriyankaJ77@users.noreply.github.com> Date: Thu, 22 Feb 2018 04:35:07 +0530 Subject: [PATCH 0217/2296] Network v2: RBAC-Delete (#781) --- .../v2/extensions/rbacpolicies/rbacpolicies.go | 14 ++++++++++++++ .../extensions/rbacpolicies/rbacpolicies_test.go | 1 + .../networking/v2/extensions/rbacpolicies/doc.go | 8 ++++++++ .../v2/extensions/rbacpolicies/requests.go | 6 ++++++ .../v2/extensions/rbacpolicies/results.go | 6 ++++++ .../rbacpolicies/testing/requests_test.go | 14 ++++++++++++++ .../networking/v2/extensions/rbacpolicies/urls.go | 4 ++++ 7 files changed, 53 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go index 27d3df7f8b..46903dff78 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go @@ -27,3 +27,17 @@ func CreateRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, tenantID, t.Logf("Successfully created rbac_policy") return rbacPolicy, nil } + +// DeleteRBACPolicy will delete a rbac-policy 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 DeleteRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, rbacPolicyID string) { + t.Logf("Trying to delete rbac_policy: %s", rbacPolicyID) + + err := rbacpolicies.Delete(client, rbacPolicyID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete rbac_policy %s: %v", rbacPolicyID, err) + } + + t.Logf("Deleted rbac_policy: %s", rbacPolicyID) +} diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index de944c0c08..d898c7db40 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -52,6 +52,7 @@ func TestRBACPolicyCreate(t *testing.T) { if err != nil { t.Fatalf("Unable to create rbac-policy: %v", err) } + defer DeleteRBACPolicy(t, client, rbacPolicy.ID) tools.PrintResource(t, rbacPolicy) diff --git a/openstack/networking/v2/extensions/rbacpolicies/doc.go b/openstack/networking/v2/extensions/rbacpolicies/doc.go index 4a3aec1a59..9c61d00027 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/doc.go +++ b/openstack/networking/v2/extensions/rbacpolicies/doc.go @@ -27,5 +27,13 @@ Example to Create a RBAC Policy panic(err) } +Example to Delete a RBAC Policy + + rbacPolicyID := "94fe107f-da78-4d92-a9d7-5611b06dad8d" + err := rbacpolicies.Delete(rbacClient, rbacPolicyID).ExtractErr() + if err != nil { + panic(err) + } + */ package rbacpolicies diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index 7332fce9ad..9b0658a9a1 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -55,3 +55,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(createURL(c), b, &r.Body, nil) return } + +// Delete accepts a unique ID and deletes the rbac-policy associated with it. +func Delete(c *gophercloud.ServiceClient, rbacPolicyID string) (r DeleteResult) { + _, r.Err = c.Delete(deleteURL(c, rbacPolicyID), nil) + return +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go index 0e85a66798..22d91edaac 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/results.go +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -31,6 +31,12 @@ type GetResult 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 +} + // RBACPolicy represents a RBAC policy. type RBACPolicy struct { // UUID of the RBAC policy. diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go index ccd433098b..2e50c1feec 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -56,3 +56,17 @@ func TestGet(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, &rbacPolicy1, n) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/rbac-policies/71d55b18-d2f8-4c76-a5e6-e0a3dd114361", 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) + }) + + res := rbacpolicies.Delete(fake.ServiceClient(), "71d55b18-d2f8-4c76-a5e6-e0a3dd114361").ExtractErr() + th.AssertNoErr(t, res) +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/urls.go b/openstack/networking/v2/extensions/rbacpolicies/urls.go index 068daa1998..9954f65629 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/urls.go +++ b/openstack/networking/v2/extensions/rbacpolicies/urls.go @@ -17,3 +17,7 @@ func createURL(c *gophercloud.ServiceClient) string { func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From 6c823a6861b8e5dcf93a9a5a6937d592370aacb6 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Thu, 22 Feb 2018 03:46:25 +0100 Subject: [PATCH 0218/2296] Vpnaas: Create IPSec Policy (#768) * Added requests.go file and necessary methods for ipsecpolicy creation * created results file * Updated results description * Created urls.go file * Put correct root and resource urls * Added unit test for ipsecpolicy creation. Also renamed LifetimeName to LifetimeValue * Formatted inline comments, put LifetimeCreateOpts and Lifetime into their own structs * Added acceptance test, formatted struct fields * Added doc.go file * Added comparison between expected and actual struct to tests * Removed Lifetime prefix before Units and Value fields. Updated unit test to compare struct instead of fields * added types to pass into createopts * Added missing 'id' field to results struct, fixed typo in comment to TransformProtocol * fixed typo --- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 24 ++++ .../networking/v2/extensions/vpnaas/vpnaas.go | 22 ++++ .../v2/extensions/vpnaas/ipsecpolicies/doc.go | 16 +++ .../vpnaas/ipsecpolicies/requests.go | 110 ++++++++++++++++++ .../vpnaas/ipsecpolicies/results.go | 67 +++++++++++ .../ipsecpolicies/testing/requests_test.go | 97 +++++++++++++++ .../extensions/vpnaas/ipsecpolicies/urls.go | 16 +++ 7 files changed, 352 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ipsecpolicies/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go new file mode 100644 index 0000000000..a3e4d7c600 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -0,0 +1,24 @@ +// +build acceptance networking vpnaas + +package vpnaas + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" +) + +func TestPolicyCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + policy, err := CreateIPSecPolicy(t, client) + if err != nil { + t.Fatalf("Unable to create policy: %v", err) + } + + tools.PrintResource(t, policy) +} diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index 5a5ec1de3f..71b0b98763 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" ) @@ -44,3 +45,24 @@ func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID st t.Logf("Service deleted: %s", serviceID) } + +// CreateIPSecPolicy will create an IPSec Policy with a random name and given +// rule. An error will be returned if the rule could not be created. +func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecpolicies.Policy, error) { + policyName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create policy %s", policyName) + + createOpts := ipsecpolicies.CreateOpts{ + Name: policyName, + } + + policy, err := ipsecpolicies.Create(client, createOpts).Extract() + if err != nil { + return policy, err + } + + t.Logf("Successfully created IPSec policy %s", policyName) + + return policy, nil +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go new file mode 100644 index 0000000000..64ff17eec4 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -0,0 +1,16 @@ +/* +Package ipsecpolicies allows management and retrieval of IPSec Policies in the +OpenStack Networking Service. + +Example to Create a Policy + + createOpts := ipsecpolicies.CreateOpts{ + Name: "IPSecPolicy_1", + } + + policy, err := policies.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } +*/ +package ipsecpolicies diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go new file mode 100644 index 0000000000..eea9333005 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -0,0 +1,110 @@ +package ipsecpolicies + +import "github.com/gophercloud/gophercloud" + +type TransformProtocol string +type AuthAlgorithm string +type EncapsulationMode string +type EncryptionAlgorithm string +type PFS string +type Unit string + +const ( + TransformProtocolESP TransformProtocol = "esp" + TransformProtocolAH TransformProtocol = "ah" + TransformProtocolAHESP TransformProtocol = "ah-esp" + AuthAlgorithmSHA1 AuthAlgorithm = "sha1" + AuthAlgorithmSHA256 AuthAlgorithm = "sha256" + AuthAlgorithmSHA384 AuthAlgorithm = "sha384" + AuthAlgorithmSHA512 AuthAlgorithm = "sha512" + EncryptionAlgorithm3DES EncryptionAlgorithm = "3des" + EncryptionAlgorithmAES128 EncryptionAlgorithm = "aes-128" + EncryptionAlgorithmAES256 EncryptionAlgorithm = "aes-256" + EncryptionAlgorithmAES192 EncryptionAlgorithm = "aes-192" + EncapsulationModeTunnel EncapsulationMode = "tunnel" + EncapsulationModeTransport EncapsulationMode = "transport" + UnitSeconds Unit = "seconds" + UnitKilobytes Unit = "kilobytes" + PFSGroup2 PFS = "group2" + PFSGroup5 PFS = "group5" + PFSGroup14 PFS = "group14" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPolicyCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new IPSec policy +type CreateOpts struct { + // TenantID specifies a tenant to own the IPSec policy. The caller must have + // an admin role in order to set this. Otherwise, this field is left unset + // and the caller will be the owner. + TenantID string `json:"tenant_id,omitempty"` + + // Description is the human readable description of the policy. + Description string `json:"description,omitempty"` + + // Name is the human readable name of the policy. + // Does not have to be unique. + Name string `json:"name,omitempty"` + + // AuthAlgorithm is the authentication hash algorithm. + // Valid values are sha1, sha256, sha384, sha512. + // The default is sha1. + AuthAlgorithm AuthAlgorithm `json:"auth_algorithm,omitempty"` + + // EncapsulationMode is the encapsulation mode. + // A valid value is tunnel or transport. + // Default is tunnel. + EncapsulationMode EncapsulationMode `json:"encapsulation_mode,omitempty"` + + // EncryptionAlgorithm is the encryption algorithm. + // A valid value is 3des, aes-128, aes-192, aes-256, and so on. + // Default is aes-128. + EncryptionAlgorithm EncryptionAlgorithm `json:"encryption_algorithm,omitempty"` + + // PFS is the Perfect forward secrecy mode. + // A valid value is Group2, Group5, Group14, and so on. + // Default is Group5. + PFS PFS `json:"pfs,omitempty"` + + // TransformProtocol is the transform protocol. + // A valid value is ESP, AH, or AH- ESP. + // Default is ESP. + TransformProtocol TransformProtocol `json:"transform_protocol,omitempty"` + + //Lifetime is the lifetime of the security association + Lifetime *LifetimeCreateOpts `json:"lifetime,omitempty"` +} + +// The lifetime consists of a unit and integer value +// You can omit either the unit or value portion of the lifetime +type LifetimeCreateOpts struct { + // Units is the units for the lifetime of the security association + // Default unit is seconds + Units Unit `json:"units,omitempty"` + + // The lifetime value. + // Must be a positive integer. + // Default value is 3600. + Value int `json:"value,omitempty"` +} + +// ToPolicyCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "ipsecpolicy") +} + +// Create accepts a CreateOpts struct and uses the values to create a new +// IPSec policy +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToPolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go new file mode 100644 index 0000000000..278c0b1560 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go @@ -0,0 +1,67 @@ +package ipsecpolicies + +import ( + "github.com/gophercloud/gophercloud" +) + +// Policy is an IPSec Policy +type Policy struct { + // TenantID is the ID of the project + TenantID string `json:"tenant_id"` + + // Description is the human readable description of the policy + Description string `json:"description"` + + // Name is the human readable name of the policy + Name string `json:"name"` + + // AuthAlgorithm is the authentication hash algorithm + AuthAlgorithm string `json:"auth_algorithm"` + + // EncapsulationMode is the encapsulation mode + EncapsulationMode string `json:"encapsulation_mode"` + + // EncryptionAlgorithm is the encryption algorithm + EncryptionAlgorithm string `json:"encryption_algorithm"` + + // PFS is the Perfect forward secrecy (PFS) mode + PFS string `json:"pfs"` + + // TransformProtocol is the transform protocol + TransformProtocol string `json:"transform_protocol"` + + // Lifetime is the lifetime of the security association + Lifetime Lifetime `json:"lifetime"` + + // ID is the ID of the policy + ID string `json:"id"` +} + +type Lifetime struct { + // Units is the unit for the lifetime + // Default is seconds + Units string `json:"units"` + + // Value is the lifetime + // Default is 3600 + Value int `json:"value"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts an IPSec Policy. +func (r commonResult) Extract() (*Policy, error) { + var s struct { + Policy *Policy `json:"ipsecpolicy"` + } + err := r.ExtractInto(&s) + return s.Policy, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Policy. +type CreateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go new file mode 100644 index 0000000000..715e1d8f9d --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -0,0 +1,97 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies", 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, ` +{ + "ipsecpolicy": { + "name": "ipsecpolicy1", + "transform_protocol": "esp", + "auth_algorithm": "sha1", + "encapsulation_mode": "tunnel", + "encryption_algorithm": "aes-128", + "pfs": "group5", + "lifetime": { + "units": "seconds", + "value": 7200 + }, + "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b" +} +} `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "ipsecpolicy": { + "name": "ipsecpolicy1", + "transform_protocol": "esp", + "auth_algorithm": "sha1", + "encapsulation_mode": "tunnel", + "encryption_algorithm": "aes-128", + "pfs": "group5", + "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "lifetime": { + "units": "seconds", + "value": 7200 + }, + "id": "5291b189-fd84-46e5-84bd-78f40c05d69c", + "description": "" + } +} + `) + }) + + lifetime := ipsecpolicies.LifetimeCreateOpts{ + Units: ipsecpolicies.UnitSeconds, + Value: 7200, + } + options := ipsecpolicies.CreateOpts{ + TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", + Name: "ipsecpolicy1", + TransformProtocol: ipsecpolicies.TransformProtocolESP, + AuthAlgorithm: ipsecpolicies.AuthAlgorithmSHA1, + EncapsulationMode: ipsecpolicies.EncapsulationModeTunnel, + EncryptionAlgorithm: ipsecpolicies.EncryptionAlgorithmAES128, + PFS: ipsecpolicies.PFSGroup5, + Lifetime: &lifetime, + Description: "", + } + actual, err := ipsecpolicies.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + expectedLifetime := ipsecpolicies.Lifetime{ + Units: "seconds", + Value: 7200, + } + expected := ipsecpolicies.Policy{ + TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", + Name: "ipsecpolicy1", + TransformProtocol: "esp", + AuthAlgorithm: "sha1", + EncapsulationMode: "tunnel", + EncryptionAlgorithm: "aes-128", + PFS: "group5", + Description: "", + Lifetime: expectedLifetime, + ID: "5291b189-fd84-46e5-84bd-78f40c05d69c", + } + th.AssertDeepEquals(t, expected, *actual) +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/urls.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/urls.go new file mode 100644 index 0000000000..8781cc4499 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/urls.go @@ -0,0 +1,16 @@ +package ipsecpolicies + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "vpn" + resourcePath = "ipsecpolicies" +) + +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 c06af9d18942a83da79436ee0b415a496458347b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 22 Feb 2018 04:48:55 +0000 Subject: [PATCH 0219/2296] Networking v2: Port Security Create --- .../openstack/networking/v2/networking.go | 66 +++++++++++++++++++ .../openstack/networking/v2/networks_test.go | 26 ++++++++ .../openstack/networking/v2/ports_test.go | 41 ++++++++++++ .../v2/extensions/portsecurity/requests.go | 55 ++++++++++++++++ .../v2/networks/testing/fixtures.go | 26 ++++++++ .../v2/networks/testing/requests_test.go | 36 ++++++++++ .../networking/v2/ports/testing/fixtures.go | 56 ++++++++++++++++ .../v2/ports/testing/requests_test.go | 48 ++++++++++++++ 8 files changed, 354 insertions(+) create mode 100644 openstack/networking/v2/extensions/portsecurity/requests.go diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index d19fb06262..e889267674 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" @@ -31,6 +32,32 @@ func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.N return network, nil } +// CreateNetworkWithoutPortSecurity will create a network without port security. +// An error will be returned if the network could not be created. +func CreateNetworkWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) { + networkName := tools.RandomString("TESTACC-", 8) + networkCreateOpts := networks.CreateOpts{ + Name: networkName, + AdminStateUp: gophercloud.Enabled, + } + + iFalse := false + createOpts := portsecurity.NetworkCreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + PortSecurityEnabled: &iFalse, + } + + t.Logf("Attempting to create network: %s", networkName) + + network, err := networks.Create(client, createOpts).Extract() + if err != nil { + return network, err + } + + t.Logf("Successfully created network.") + return network, nil +} + // CreatePort will create a port on the specified subnet. An error will be // returned if the port could not be created. func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { @@ -99,6 +126,45 @@ func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClie return newPort, nil } +// CreatePortWithoutPortSecurity will create a port without port security on the +// specified subnet. An error will be returned if the port could not be created. +func CreatePortWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { + portName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create port: %s", portName) + + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + Name: portName, + AdminStateUp: gophercloud.Enabled, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + } + + iFalse := false + createOpts := portsecurity.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + PortSecurityEnabled: &iFalse, + } + + port, err := ports.Create(client, createOpts).Extract() + if err != nil { + return port, err + } + + if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + return port, err + } + + newPort, err := ports.Get(client, port.ID).Extract() + if err != nil { + return newPort, err + } + + t.Logf("Successfully created port: %s", portName) + + return newPort, nil +} + // CreateSubnet will create a subnet on the specified Network ID. An error // will be returned if the subnet could not be created. func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index c100bd4160..2ff00172bc 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -71,3 +71,29 @@ func TestNetworksCRUD(t *testing.T) { tools.PrintResource(t, newNetwork) } + +func TestNetworksPortSecurityCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create a network without port security + network, err := CreateNetworkWithoutPortSecurity(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + var networkWithExtensions struct { + networks.Network + portsecurity.PortSecurityExt + } + + err = networks.Get(client, network.ID).ExtractInto(&networkWithExtensions) + if err != nil { + t.Fatalf("Unable to retrieve network: %v", err) + } + + tools.PrintResource(t, networkWithExtensions) +} diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index eddddf64f2..89ac3b5382 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -8,6 +8,7 @@ import ( "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/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) @@ -347,3 +348,43 @@ func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { t.Fatalf("Address Pairs were removed") } } + +func TestPortsPortSecurityCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer DeleteSubnet(t, client, subnet.ID) + + // Create port + port, err := CreatePortWithoutPortSecurity(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + defer DeletePort(t, client, port.ID) + + var portWithExt struct { + ports.Port + portsecurity.PortSecurityExt + } + + err = ports.Get(client, port.ID).ExtractInto(&portWithExt) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + + tools.PrintResource(t, portWithExt) +} diff --git a/openstack/networking/v2/extensions/portsecurity/requests.go b/openstack/networking/v2/extensions/portsecurity/requests.go new file mode 100644 index 0000000000..781353ee37 --- /dev/null +++ b/openstack/networking/v2/extensions/portsecurity/requests.go @@ -0,0 +1,55 @@ +package portsecurity + +import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" +) + +// PortCreateOptsExt adds port security options to the base ports.CreateOpts. +type PortCreateOptsExt struct { + ports.CreateOptsBuilder + + // PortSecurityEnabled toggles port security on a port. + PortSecurityEnabled *bool `json:"port_security_enabled,omitempty"` +} + +// ToPortCreateMap casts a CreateOpts struct to a map. +func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToPortCreateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]interface{}) + + if opts.PortSecurityEnabled != nil { + port["port_security_enabled"] = &opts.PortSecurityEnabled + } + + return base, nil +} + +// NetworkCreateOptsExt adds port security options to the base +// networks.CreateOpts. +type NetworkCreateOptsExt struct { + networks.CreateOptsBuilder + + // PortSecurityEnabled toggles port security on a port. + PortSecurityEnabled *bool `json:"port_security_enabled,omitempty"` +} + +// ToNetworkCreateMap casts a CreateOpts struct to a map. +func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() + if err != nil { + return nil, err + } + + network := base["network"].(map[string]interface{}) + + if opts.PortSecurityEnabled != nil { + network["port_security_enabled"] = &opts.PortSecurityEnabled + } + + return base, nil +} diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index 3edbe8f37a..e4f6b6bd02 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -86,6 +86,32 @@ const CreateResponse = ` } }` +const CreatePortSecurityRequest = ` +{ + "network": { + "name": "private", + "admin_state_up": true, + "port_security_enabled": false + } +}` + +const CreatePortSecurityResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local", + "port_security_enabled": false + } +}` + const CreateOptionalFieldsRequest = ` { "network": { diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 0e028ead4e..1bfafaae8d 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -218,3 +218,39 @@ func TestDelete(t *testing.T) { res := networks.Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c") th.AssertNoErr(t, res.Err) } + +func TestCreatePortSecurity(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks", 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, CreatePortSecurityRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, CreatePortSecurityResponse) + }) + + var networkWithExtensions struct { + networks.Network + portsecurity.PortSecurityExt + } + + iTrue := true + iFalse := false + networkCreateOpts := networks.CreateOpts{Name: "private", AdminStateUp: &iTrue} + createOpts := portsecurity.NetworkCreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + PortSecurityEnabled: &iFalse, + } + + err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&networkWithExtensions) + th.AssertNoErr(t, err) + + th.AssertEquals(t, networkWithExtensions.Status, "ACTIVE") + th.AssertEquals(t, networkWithExtensions.PortSecurityEnabled, false) +} diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index dea77fe011..d8e57cbbae 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -220,6 +220,62 @@ const CreateOmitSecurityGroupsResponse = ` } ` +const CreatePortSecurityRequest = ` +{ + "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" + } + ], + "port_security_enabled": false + } +} +` + +const CreatePortSecurityResponse = ` +{ + "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" + } + ], + "device_id": "", + "port_security_enabled": false + } +} +` + const UpdateRequest = ` { "port": { diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index aa6c74d64a..87a18864d4 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -311,6 +311,54 @@ func TestRequiredCreateOpts(t *testing.T) { } } +func TestCreatePortSecurity(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, CreatePortSecurityRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, CreatePortSecurityResponse) + }) + + var portWithExt struct { + ports.Port + portsecurity.PortSecurityExt + } + + asu := true + iFalse := false + portCreateOpts := 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"}, + }, + } + createOpts := portsecurity.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + PortSecurityEnabled: &iFalse, + } + + err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&portWithExt) + th.AssertNoErr(t, err) + + th.AssertEquals(t, portWithExt.Status, "DOWN") + th.AssertEquals(t, portWithExt.PortSecurityEnabled, false) +} + func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 8f1afb16493e82ad2cabafc23843cb128e64ee97 Mon Sep 17 00:00:00 2001 From: Manjunath Patil Date: Thu, 22 Feb 2018 22:05:32 +0530 Subject: [PATCH 0220/2296] Network v2: RBAC-LIST (#758) * [wip] Network v2: RBAC-Create * Added Acceptance Tests for RBAC. * Addressed some nits. * Addressed Review Comments * Resolved build failure * Addressed doc nits, RBAC to RBACPolicy naming convention, and restricting the acceptance test to only be run by admin user. * Network v2: RBAC-GET-ALL List all the RBAC policies. * Doc Comment addressed --- .../rbacpolicies/rbacpolicies_test.go | 27 +++++++ .../v2/extensions/rbacpolicies/doc.go | 39 ++++++++-- .../v2/extensions/rbacpolicies/requests.go | 48 +++++++++++++ .../v2/extensions/rbacpolicies/results.go | 27 +++++++ .../rbacpolicies/testing/fixtures.go | 36 ++++++++++ .../rbacpolicies/testing/requests_test.go | 72 +++++++++++++++++++ .../v2/extensions/rbacpolicies/urls.go | 4 ++ 7 files changed, 248 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index d898c7db40..6b3a72f141 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -65,3 +65,30 @@ func TestRBACPolicyCreate(t *testing.T) { tools.PrintResource(t, newrbacPolicy) } + +func TestRBACPolicyList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + type rbacPolicy struct { + rbacpolicies.RBACPolicy + } + + var allRBACPolicies []rbacPolicy + + allPages, err := rbacpolicies.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list rbac policies: %v", err) + } + + err = rbacpolicies.ExtractRBACPolicesInto(allPages, &allRBACPolicies) + if err != nil { + t.Fatalf("Unable to extract rbac policies: %v", err) + } + + for _, rbacpolicy := range allRBACPolicies { + tools.PrintResource(t, rbacpolicy) + } +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/doc.go b/openstack/networking/v2/extensions/rbacpolicies/doc.go index 9c61d00027..4eabbec7aa 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/doc.go +++ b/openstack/networking/v2/extensions/rbacpolicies/doc.go @@ -27,13 +27,42 @@ Example to Create a RBAC Policy panic(err) } +Example to List RBAC Policies + + listOpts := rbacpolicies.ListOpts{ + TenantID: "a99e9b4e620e4db09a2dfb6e42a01e66", + } + + allPages, err := rbacpolicies.List(rbacClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allRBACPolicies, err := rbacpolicies.ExtractRBACPolicies(allPages) + if err != nil { + panic(err) + } + + for _, rbacpolicy := range allRBACPolicies { + fmt.Printf("%+v", rbacpolicy) + } + Example to Delete a RBAC Policy - rbacPolicyID := "94fe107f-da78-4d92-a9d7-5611b06dad8d" - err := rbacpolicies.Delete(rbacClient, rbacPolicyID).ExtractErr() - if err != nil { - panic(err) - } + rbacPolicyID := "94fe107f-da78-4d92-a9d7-5611b06dad8d" + err := rbacpolicies.Delete(rbacClient, rbacPolicyID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Get RBAC Policy by ID + + rbacPolicyID := "94fe107f-da78-4d92-a9d7-5611b06dad8d" + rbacpolicy, err := rbacpolicies.Get(rbacClient, rbacPolicyID).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v", rbacpolicy) */ package rbacpolicies diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index 9b0658a9a1..b3c89f80d1 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -2,8 +2,56 @@ package rbacpolicies import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToRBACPolicyListQuery() (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 rbac attributes you want to see returned. SortKey allows you to sort +// by a particular rbac attribute. SortDir sets the direction, and is either +// `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + TargetTenant string `q:"target_tenant"` + ObjectType string `q:"object_type"` + ObjectID string `q:"object_id"` + Action PolicyAction `q:"action"` + ProjectID string `q:"project_id"` + Marker string `q:"marker"` + Limit int `q:"limit"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToRBACPolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToRBACPolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// rbac policies. 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 := listURL(c) + if opts != nil { + query, err := opts.ToRBACPolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return RBACPolicyPage{pagination.LinkedPageBase{PageResult: r}} + + }) +} + // Get retrieves a specific rbac policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go index 22d91edaac..8baa5a8feb 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/results.go +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -2,6 +2,7 @@ package rbacpolicies import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { @@ -63,3 +64,29 @@ type RBACPolicy struct { // ProjectID is the ID of the project. ProjectID string `json:"project_id"` } + +// RBACPolicyPage is the page returned by a pager when traversing over a +// collection of rbac policies. +type RBACPolicyPage struct { + pagination.LinkedPageBase +} + +// IsEmpty checks whether a RBACPolicyPage struct is empty. +func (r RBACPolicyPage) IsEmpty() (bool, error) { + is, err := ExtractRBACPolicies(r) + return len(is) == 0, err +} + +// ExtractRBACPolicies accepts a Page struct, specifically a RBAC Policy struct, +// and extracts the elements into a slice of RBAC Policy structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractRBACPolicies(r pagination.Page) ([]RBACPolicy, error) { + var s []RBACPolicy + err := ExtractRBACPolicesInto(r, &s) + return s, err +} + +// ExtractRBACPolicesInto extracts the elements into a slice of RBAC Policy structs. +func ExtractRBACPolicesInto(r pagination.Page, v interface{}) error { + return r.(RBACPolicyPage).Result.ExtractIntoSlicePtr(v, "rbac_policies") +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go index 4328baac40..0b3772ddd4 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go @@ -4,6 +4,30 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" ) +const ListResponse = ` +{ + "rbac_policies": [ + { + "target_tenant": "6e547a3bcfe44702889fdeff3c3520c3", + "tenant_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", + "object_type": "network", + "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc", + "action": "access_as_shared", + "project_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", + "id": "2cf7523a-93b5-4e69-9360-6c6bf986bb7c" + }, + { + "target_tenant": "1a547a3bcfe44702889fdeff3c3520c3", + "tenant_id": "1ae27ce0a2a54cc6ae06dc62dd0ec832", + "object_type": "network", + "object_id": "120d22bf-bd17-4238-9758-25f72610ecdc", + "action": "access_as_shared", + "project_id": "1ae27ce0a2a54cc6ae06dc62dd0ec832", + "id":"1ab7523a-93b5-4e69-9360-6c6bf986bb7c" + } + ] +}` + // CreateRequest is the structure of request body to create rbac-policy. const CreateRequest = ` { @@ -52,3 +76,15 @@ var rbacPolicy1 = rbacpolicies.RBACPolicy{ TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", ProjectID: "3de27ce0a2a54cc6ae06dc62dd0ec832", } + +var rbacPolicy2 = rbacpolicies.RBACPolicy{ + ID: "1ab7523a-93b5-4e69-9360-6c6bf986bb7c", + Action: rbacpolicies.ActionAccessShared, + ObjectID: "120d22bf-bd17-4238-9758-25f72610ecdc", + ObjectType: "network", + TenantID: "1ae27ce0a2a54cc6ae06dc62dd0ec832", + TargetTenant: "1a547a3bcfe44702889fdeff3c3520c3", + ProjectID: "1ae27ce0a2a54cc6ae06dc62dd0ec832", +} + +var ExpectedRBACPoliciesSlice = []rbacpolicies.RBACPolicy{rbacPolicy1, rbacPolicy2} diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go index 2e50c1feec..98654616cc 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -7,6 +7,7 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -57,6 +58,77 @@ func TestGet(t *testing.T) { th.CheckDeepEquals(t, &rbacPolicy1, n) } +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/rbac-policies", 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, ListResponse) + }) + + client := fake.ServiceClient() + count := 0 + + rbacpolicies.List(client, rbacpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := rbacpolicies.ExtractRBACPolicies(page) + if err != nil { + t.Errorf("Failed to extract rbac policies: %v", err) + return false, err + } + + th.CheckDeepEquals(t, ExpectedRBACPoliciesSlice, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestListWithAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/rbac-policies", 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, ListResponse) + }) + + client := fake.ServiceClient() + + type newRBACPolicy struct { + rbacpolicies.RBACPolicy + } + + var allRBACpolicies []newRBACPolicy + + allPages, err := rbacpolicies.List(client, rbacpolicies.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + err = rbacpolicies.ExtractRBACPolicesInto(allPages, &allRBACpolicies) + th.AssertNoErr(t, err) + + th.AssertEquals(t, allRBACpolicies[0].ObjectType, "network") + th.AssertEquals(t, allRBACpolicies[0].Action, rbacpolicies.ActionAccessShared) + + th.AssertEquals(t, allRBACpolicies[1].ProjectID, "1ae27ce0a2a54cc6ae06dc62dd0ec832") + th.AssertEquals(t, allRBACpolicies[1].TargetTenant, "1a547a3bcfe44702889fdeff3c3520c3") + +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/networking/v2/extensions/rbacpolicies/urls.go b/openstack/networking/v2/extensions/rbacpolicies/urls.go index 9954f65629..258c207304 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/urls.go +++ b/openstack/networking/v2/extensions/rbacpolicies/urls.go @@ -14,6 +14,10 @@ func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } From b57885588035b78f5f12a11ed087709518f65e0f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 23 Feb 2018 03:15:24 +0000 Subject: [PATCH 0221/2296] Networking v2: Fix subnetpools The following fixes have been applied: * Removal of Prefixes from ListOpts * Changing ListOpts Shared to *bool * Changing ListOpts IsDefault to *bool * Changing the result CreatedAt to time.Time * Changing the result UpdatedAt to time.Time --- .../v2/extensions/subnetpools/requests.go | 39 +++++++++---------- .../v2/extensions/subnetpools/results.go | 5 ++- .../subnetpools/testing/fixtures.go | 18 +++++---- .../subnetpools/testing/requests_test.go | 5 ++- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index 61f660d5ab..e7ee96aef6 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -18,27 +18,24 @@ type ListOptsBuilder interface { // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for the pagination. type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` - DefaultQuota int `q:"default_quota"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - CreatedAt string `q:"created_at"` - UpdatedAt string `q:"updated_at"` - Prefixes []string `q:"prefixes"` - DefaultPrefixLen int `q:"default_prefixlen"` - MinPrefixLen int `q:"min_prefixlen"` - MaxPrefixLen int `q:"max_prefixlen"` - AddressScopeID string `q:"address_scope_id"` - IPversion int `q:"ip_version"` - Shared bool `q:"shared"` - Description string `q:"description"` - IsDefault bool `q:"is_default"` - RevisionNumber int `q:"revision_number"` - 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"` + DefaultQuota int `q:"default_quota"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + DefaultPrefixLen int `q:"default_prefixlen"` + MinPrefixLen int `q:"min_prefixlen"` + MaxPrefixLen int `q:"max_prefixlen"` + AddressScopeID string `q:"address_scope_id"` + IPVersion int `q:"ip_version"` + Shared *bool `q:"shared"` + Description string `q:"description"` + IsDefault *bool `q:"is_default"` + RevisionNumber int `q:"revision_number"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` } // ToSubnetPoolListQuery formats a ListOpts into a query string. diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index a490f05401..e761eac44d 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "strconv" + "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" @@ -66,10 +67,10 @@ type SubnetPool struct { ProjectID string `json:"project_id"` // CreatedAt is the time at which subnetpool has been created. - CreatedAt string `json:"created_at"` + CreatedAt time.Time `json:"created_at"` // UpdatedAt is the time at which subnetpool has been created. - UpdatedAt string `json:"updated_at"` + UpdatedAt time.Time `json:"updated_at"` // Prefixes is the list of subnet prefixes to assign to the subnetpool. // Neutron API merges adjacent prefixes and treats them as a single prefix. diff --git a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go index f70b41773e..2742028905 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go @@ -1,6 +1,8 @@ package testing import ( + "time" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" ) @@ -78,7 +80,7 @@ const SubnetPoolsListResult = ` var SubnetPool1 = subnetpools.SubnetPool{ AddressScopeID: "", - CreatedAt: "2017-12-28T07:21:41Z", + CreatedAt: time.Date(2017, 12, 28, 7, 21, 41, 0, time.UTC), DefaultPrefixLen: 8, DefaultQuota: 0, Description: "IPv4", @@ -96,12 +98,12 @@ var SubnetPool1 = subnetpools.SubnetPool{ TenantID: "1e2b9857295a4a3e841809ef492812c5", RevisionNumber: 1, Shared: false, - UpdatedAt: "2017-12-28T07:21:41Z", + UpdatedAt: time.Date(2017, 12, 28, 7, 21, 41, 0, time.UTC), } var SubnetPool2 = subnetpools.SubnetPool{ AddressScopeID: "0bc38e22-be49-4e67-969e-fec3f36508bd", - CreatedAt: "2017-12-28T07:21:34Z", + CreatedAt: time.Date(2017, 12, 28, 7, 21, 34, 0, time.UTC), DefaultPrefixLen: 64, DefaultQuota: 0, Description: "IPv6", @@ -119,12 +121,12 @@ var SubnetPool2 = subnetpools.SubnetPool{ TenantID: "1e2b9857295a4a3e841809ef492812c5", RevisionNumber: 1, Shared: false, - UpdatedAt: "2017-12-28T07:21:34Z", + UpdatedAt: time.Date(2017, 12, 28, 7, 21, 34, 0, time.UTC), } var SubnetPool3 = subnetpools.SubnetPool{ AddressScopeID: "", - CreatedAt: "2017-12-28T07:21:27Z", + CreatedAt: time.Date(2017, 12, 28, 7, 21, 27, 0, time.UTC), DefaultPrefixLen: 64, DefaultQuota: 4, Description: "PublicPool", @@ -141,7 +143,7 @@ var SubnetPool3 = subnetpools.SubnetPool{ TenantID: "ceb366d50ad54fe39717df3af60f9945", RevisionNumber: 1, Shared: true, - UpdatedAt: "2017-12-28T07:21:27Z", + UpdatedAt: time.Date(2017, 12, 28, 7, 21, 27, 0, time.UTC), } const SubnetPoolGetResult = ` @@ -157,11 +159,11 @@ const SubnetPoolGetResult = ` "is_default": true, "project_id": "1e2b9857295a4a3e841809ef492812c5", "tenant_id": "1e2b9857295a4a3e841809ef492812c5", - "created_at": "2018-01-01T00:00:01", + "created_at": "2018-01-01T00:00:01Z", "prefixes": [ "2001:db8::a3/64" ], - "updated_at": "2018-01-01T00:10:10", + "updated_at": "2018-01-01T00:10:10Z", "ip_version": 6, "shared": false, "description": "ipv6 prefixes", diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index d4f62319ad..3d138d37b3 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" @@ -73,8 +74,8 @@ func TestGet(t *testing.T) { th.AssertEquals(t, s.DefaultQuota, 2) th.AssertEquals(t, s.TenantID, "1e2b9857295a4a3e841809ef492812c5") th.AssertEquals(t, s.ProjectID, "1e2b9857295a4a3e841809ef492812c5") - th.AssertEquals(t, s.CreatedAt, "2018-01-01T00:00:01") - th.AssertEquals(t, s.UpdatedAt, "2018-01-01T00:10:10") + th.AssertEquals(t, s.CreatedAt, time.Date(2018, 1, 1, 0, 0, 1, 0, time.UTC)) + th.AssertEquals(t, s.UpdatedAt, time.Date(2018, 1, 1, 0, 10, 10, 0, time.UTC)) th.AssertDeepEquals(t, s.Prefixes, []string{ "2001:db8::a3/64", }) From 44ab6a048b83d9042103347ba28a1513274f72f7 Mon Sep 17 00:00:00 2001 From: PriyankaJ77 <35596310+PriyankaJ77@users.noreply.github.com> Date: Sat, 24 Feb 2018 09:11:01 +0530 Subject: [PATCH 0222/2296] Network v2: RBAC-Update (#782) --- .../rbacpolicies/rbacpolicies_test.go | 21 ++++++++++++- .../v2/extensions/rbacpolicies/doc.go | 11 +++++++ .../v2/extensions/rbacpolicies/requests.go | 30 +++++++++++++++++++ .../v2/extensions/rbacpolicies/results.go | 6 ++++ .../rbacpolicies/testing/fixtures.go | 22 ++++++++++++++ .../rbacpolicies/testing/requests_test.go | 25 ++++++++++++++++ .../v2/extensions/rbacpolicies/urls.go | 4 +++ 7 files changed, 118 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index 6b3a72f141..db838d2816 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -13,7 +13,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" ) -func TestRBACPolicyCreate(t *testing.T) { +func TestRBACPolicyCRUD(t *testing.T) { username := os.Getenv("OS_USERNAME") if username != "admin" { t.Skip("must be admin to run this test") @@ -56,6 +56,25 @@ func TestRBACPolicyCreate(t *testing.T) { tools.PrintResource(t, rbacPolicy) + // Create another project/tenant for rbac-update + project2, err := projects.CreateProject(t, identityClient, nil) + if err != nil { + t.Fatalf("Unable to create project2: %v", err) + } + defer projects.DeleteProject(t, identityClient, project2.ID) + + tools.PrintResource(t, project2) + + // Update a rbac-policy + updateOpts := rbacpolicies.UpdateOpts{ + TargetTenant: project2.ID, + } + + _, err = rbacpolicies.Update(client, rbacPolicy.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update rbac-policy: %v", err) + } + // Get the rbac-policy by ID t.Logf("Get rbac_policy by ID") newrbacPolicy, err := rbacpolicies.Get(client, rbacPolicy.ID).Extract() diff --git a/openstack/networking/v2/extensions/rbacpolicies/doc.go b/openstack/networking/v2/extensions/rbacpolicies/doc.go index 4eabbec7aa..f0ddbc0f67 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/doc.go +++ b/openstack/networking/v2/extensions/rbacpolicies/doc.go @@ -64,5 +64,16 @@ Example to Get RBAC Policy by ID } fmt.Printf("%+v", rbacpolicy) +Example to Update a RBAC Policy + + rbacPolicyID := "570b0306-afb5-4d3b-ab47-458fdc16baaa" + updateOpts := rbacpolicies.UpdateOpts{ + TargetTenant: "9d766060b6354c9e8e2da44cab0e8f38", + } + rbacPolicy, err := rbacpolicies.Update(rbacClient, rbacPolicyID, updateOpts).Extract() + if err != nil { + panic(err) + } + */ package rbacpolicies diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index b3c89f80d1..8ce85a54c8 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -109,3 +109,33 @@ func Delete(c *gophercloud.ServiceClient, rbacPolicyID string) (r DeleteResult) _, r.Err = c.Delete(deleteURL(c, rbacPolicyID), nil) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToRBACPolicyUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update a rbac-policy. +type UpdateOpts struct { + TargetTenant string `json:"target_tenant" required:"true"` +} + +// ToRBACPolicyUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToRBACPolicyUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "rbac_policy") +} + +// Update accepts a UpdateOpts struct and updates an existing rbac-policy using the +// values provided. +func Update(c *gophercloud.ServiceClient, rbacPolicyID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToRBACPolicyUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(updateURL(c, rbacPolicyID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, + }) + return +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go index 8baa5a8feb..a62facc0dc 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/results.go +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -38,6 +38,12 @@ type DeleteResult struct { gophercloud.ErrResult } +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a RBAC Policy. +type UpdateResult struct { + commonResult +} + // RBACPolicy represents a RBAC policy. type RBACPolicy struct { // UUID of the RBAC policy. diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go index 0b3772ddd4..f63fa2b89f 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures.go @@ -67,6 +67,28 @@ const GetResponse = ` } }` +// UpdateRequest is the structure of request body to update rbac-policy. +const UpdateRequest = ` +{ + "rbac_policy": { + "target_tenant": "9d766060b6354c9e8e2da44cab0e8f38" + } +}` + +// UpdateResponse is the structure of response body of rbac-policy update. +const UpdateResponse = ` +{ + "rbac_policy": { + "target_tenant": "9d766060b6354c9e8e2da44cab0e8f38", + "tenant_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", + "object_type": "network", + "object_id": "240d22bf-bd17-4238-9758-25f72610ecdc", + "action": "access_as_shared", + "project_id": "3de27ce0a2a54cc6ae06dc62dd0ec832", + "id": "2cf7523a-93b5-4e69-9360-6c6bf986bb7c" + } +}` + var rbacPolicy1 = rbacpolicies.RBACPolicy{ ID: "2cf7523a-93b5-4e69-9360-6c6bf986bb7c", Action: rbacpolicies.ActionAccessShared, diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go index 98654616cc..8aad843459 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -142,3 +142,28 @@ func TestDelete(t *testing.T) { res := rbacpolicies.Delete(fake.ServiceClient(), "71d55b18-d2f8-4c76-a5e6-e0a3dd114361").ExtractErr() th.AssertNoErr(t, res) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/rbac-policies/2cf7523a-93b5-4e69-9360-6c6bf986bb7c", 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, UpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateResponse) + }) + + options := rbacpolicies.UpdateOpts{TargetTenant: "9d766060b6354c9e8e2da44cab0e8f38"} + rbacResult, err := rbacpolicies.Update(fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, rbacResult.TargetTenant, "9d766060b6354c9e8e2da44cab0e8f38") + th.AssertEquals(t, rbacResult.ID, "2cf7523a-93b5-4e69-9360-6c6bf986bb7c") +} diff --git a/openstack/networking/v2/extensions/rbacpolicies/urls.go b/openstack/networking/v2/extensions/rbacpolicies/urls.go index 258c207304..8beeed9a5f 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/urls.go +++ b/openstack/networking/v2/extensions/rbacpolicies/urls.go @@ -25,3 +25,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From 47e78c6bba46f5484da66c8e9ba0ecb94093641c Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Sat, 24 Feb 2018 05:26:33 +0100 Subject: [PATCH 0223/2296] Vpnaas: Delete IPSec policy (#775) * Added delete unit test * Added delete function and result * Added comment to delete result * Added documentation and acceptance tests --- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 1 + .../networking/v2/extensions/vpnaas/vpnaas.go | 14 ++++++++++++++ .../v2/extensions/vpnaas/ipsecpolicies/doc.go | 7 +++++++ .../v2/extensions/vpnaas/ipsecpolicies/requests.go | 7 +++++++ .../v2/extensions/vpnaas/ipsecpolicies/results.go | 6 ++++++ .../vpnaas/ipsecpolicies/testing/requests_test.go | 14 ++++++++++++++ 6 files changed, 49 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index a3e4d7c600..e46e7150bc 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -19,6 +19,7 @@ func TestPolicyCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create policy: %v", err) } + defer DeleteIPSecPolicy(t, client, policy.ID) tools.PrintResource(t, policy) } diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index 71b0b98763..b899c624f4 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -66,3 +66,17 @@ func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecp return policy, nil } + +// DeleteIPSecPolicy will delete an IPSec policy 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 DeleteIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { + t.Logf("Attempting to delete policy: %s", policyID) + + err := ipsecpolicies.Delete(client, policyID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete policy %s: %v", policyID, err) + } + + t.Logf("Deleted policy: %s", policyID) +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go index 64ff17eec4..4da16abeba 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -12,5 +12,12 @@ Example to Create a Policy if err != nil { panic(err) } + +Example to Delete a Policy + + err := ipsecpolicies.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() + if err != nil { + panic(err) + } */ package ipsecpolicies diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go index eea9333005..d9c38c2dec 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -108,3 +108,10 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } + +// Delete will permanently delete a particular IPSec policy based on its +// unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go index 278c0b1560..e011f4670b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go @@ -65,3 +65,9 @@ func (r commonResult) Extract() (*Policy, error) { type CreateResult struct { commonResult } + +// CreateResult represents the result of a delete operation. Call its ExtractErr method +// to determine if the operation succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go index 715e1d8f9d..42907e6220 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -95,3 +95,17 @@ func TestCreate(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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) + }) + + res := ipsecpolicies.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + th.AssertNoErr(t, res.Err) +} From cb23b19a1ee57a4bca31c797a0af490260a4a870 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 24 Feb 2018 05:26:49 +0000 Subject: [PATCH 0224/2296] Networking v2: Support both TenantID and ProjectID where applicable --- .../v2/extensions/fwaas/firewalls/requests.go | 2 ++ .../v2/extensions/fwaas/firewalls/results.go | 1 + .../v2/extensions/fwaas/policies/requests.go | 2 ++ .../v2/extensions/fwaas/policies/results.go | 1 + .../v2/extensions/fwaas/rules/requests.go | 2 ++ .../v2/extensions/fwaas/rules/results.go | 1 + .../extensions/layer3/floatingips/requests.go | 2 ++ .../extensions/layer3/floatingips/results.go | 7 ++++-- .../v2/extensions/layer3/routers/requests.go | 2 ++ .../v2/extensions/layer3/routers/results.go | 7 ++++-- .../extensions/lbaas_v2/listeners/requests.go | 7 +++++- .../lbaas_v2/loadbalancers/requests.go | 9 ++++++-- .../extensions/lbaas_v2/monitors/requests.go | 9 ++++++-- .../v2/extensions/lbaas_v2/pools/requests.go | 17 ++++++++++---- .../v2/extensions/rbacpolicies/requests.go | 1 + .../v2/extensions/security/groups/requests.go | 23 +++++++++++-------- .../v2/extensions/security/groups/results.go | 5 +++- .../v2/extensions/security/rules/requests.go | 7 +++--- .../v2/extensions/security/rules/results.go | 5 +++- openstack/networking/v2/networks/requests.go | 2 ++ openstack/networking/v2/networks/results.go | 5 +++- openstack/networking/v2/ports/requests.go | 2 ++ openstack/networking/v2/ports/results.go | 5 +++- openstack/networking/v2/subnets/requests.go | 9 ++++++-- openstack/networking/v2/subnets/results.go | 5 +++- 25 files changed, 106 insertions(+), 32 deletions(-) diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go index aa30194668..cbd6c1fb0d 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go @@ -18,6 +18,7 @@ type ListOptsBuilder interface { // `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"` AdminStateUp bool `q:"admin_state_up"` @@ -69,6 +70,7 @@ type CreateOpts struct { // an admin role in order to set this. Otherwise, this field is left unset // and the caller will be the owner. TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/results.go b/openstack/networking/v2/extensions/fwaas/firewalls/results.go index f6786a4433..9543f0fae4 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/results.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/results.go @@ -14,6 +14,7 @@ type Firewall struct { Status string `json:"status"` PolicyID string `json:"firewall_policy_id"` TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` } type commonResult struct { diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go index b1a6a5598b..40ab7a8c46 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas/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"` @@ -67,6 +68,7 @@ type CreateOpts struct { // an admin role in order to set this. Otherwise, this field is left unset // and the caller will be the owner. 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/policies/results.go b/openstack/networking/v2/extensions/fwaas/policies/results.go index bbe22b1361..495cef2c0e 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/results.go +++ b/openstack/networking/v2/extensions/fwaas/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/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go index 83bbe99b6d..17979b637b 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas/rules/requests.go @@ -37,6 +37,7 @@ type ListOptsBuilder interface { // 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"` Protocol string `q:"protocol"` @@ -96,6 +97,7 @@ type CreateOpts struct { Protocol Protocol `json:"protocol" required:"true"` Action string `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/rules/results.go b/openstack/networking/v2/extensions/fwaas/rules/results.go index 1af03e573d..82bf4a36a8 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas/rules/results.go @@ -22,6 +22,7 @@ type Rule struct { PolicyID string `json:"firewall_policy_id"` Position int `json:"position"` TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` } // RulePage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 0a6eb62cb6..d82a1bc8e2 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -17,6 +17,7 @@ type ListOpts struct { FixedIP string `q:"fixed_ip_address"` FloatingIP string `q:"floating_ip_address"` TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` @@ -55,6 +56,7 @@ type CreateOpts struct { FixedIP string `json:"fixed_ip_address,omitempty"` SubnetID string `json:"subnet_id,omitempty"` TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` } // ToFloatingIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index f1af23d4a6..8b1a517645 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -30,10 +30,13 @@ type FloatingIP struct { // associated with the floating IP. FixedIP string `json:"fixed_ip_address"` - // TenantID is the Owner of the floating IP. Only admin users can specify a - // tenant identifier other than its own. + // TenantID is the project owner of the floating IP. Only admin users can + // specify a project identifier other than its own. TenantID string `json:"tenant_id"` + // ProjectID is the project owner of the floating IP. + ProjectID string `json:"project_id"` + // Status is the condition of the API resource. Status string `json:"status"` diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index fa346c8555..8b2bde530e 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -17,6 +17,7 @@ type ListOpts struct { Distributed *bool `q:"distributed"` Status string `q:"status"` TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` @@ -53,6 +54,7 @@ type CreateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` Distributed *bool `json:"distributed,omitempty"` TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` AvailabilityZoneHints []string `json:"availability_zone_hints,omitempty"` } diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index da1b9e4bdf..dffdce8f48 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -54,10 +54,13 @@ type Router struct { // ID is the unique identifier for the router. ID string `json:"id"` - // TenantID is the owner of the router. Only admin users can specify a tenant - // identifier other than its own. + // TenantID is the project owner of the router. Only admin users can + // specify a project identifier other than its own. TenantID string `json:"tenant_id"` + // ProjectID is the project owner of the router. + ProjectID string `json:"project_id"` + // Routes are a collection of static routes that the router will host. Routes []Route `json:"routes"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index 625748fd25..dd190f606f 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -31,6 +31,7 @@ type ListOpts struct { Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` LoadbalancerID string `q:"loadbalancer_id"` DefaultPoolID string `q:"default_pool_id"` Protocol string `q:"protocol"` @@ -86,9 +87,13 @@ type CreateOpts struct { ProtocolPort int `json:"protocol_port" required:"true"` // TenantID is only required if the caller has an admin role and wants - // to create a pool for another tenant. + // to create a pool for another project. TenantID string `json:"tenant_id,omitempty"` + // ProjectID is only required if the caller has an admin role and wants + // to create a pool for another project. + ProjectID string `json:"project_id,omitempty"` + // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name,omitempty"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index 839776dd28..49ec9ecac3 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -20,6 +20,7 @@ type ListOpts struct { Description string `q:"description"` AdminStateUp *bool `q:"admin_state_up"` TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` ProvisioningStatus string `q:"provisioning_status"` VipAddress string `q:"vip_address"` VipPortID string `q:"vip_port_id"` @@ -81,10 +82,14 @@ type CreateOpts struct { // that belong to them or networks that are shared). VipSubnetID string `json:"vip_subnet_id" required:"true"` - // The UUID of the tenant who owns the Loadbalancer. Only administrative users - // can specify a tenant UUID other than their own. + // TenantID is the UUID of the project who owns the Loadbalancer. + // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` + // ProjectID is the UUID of the project who owns the Loadbalancer. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + // The IP address of the Loadbalancer. VipAddress string `json:"vip_address,omitempty"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go index 6d9ab8ba79..c173e1c64e 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go @@ -22,6 +22,7 @@ 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"` @@ -119,10 +120,14 @@ type CreateOpts struct { // types. ExpectedCodes string `json:"expected_codes,omitempty"` - // The UUID of the tenant who owns the Monitor. Only administrative users - // can specify a tenant UUID other than their own. + // TenantID is the UUID of the project who owns the Monitor. + // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` + // ProjectID is the UUID of the project who owns the Monitor. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + // The Name of the Monitor. Name string `json:"name,omitempty"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index 2173ee8171..11564be83f 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -20,6 +20,7 @@ type ListOpts struct { LBMethod string `q:"lb_algorithm"` Protocol string `q:"protocol"` TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` AdminStateUp *bool `q:"admin_state_up"` Name string `q:"name"` ID string `q:"id"` @@ -97,10 +98,14 @@ type CreateOpts struct { // Note: one of LoadbalancerID or ListenerID must be provided. ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"` - // The UUID of the tenant who owns the Pool. Only administrative users - // can specify a tenant UUID other than their own. + // TenantID is the UUID of the project who owns the Pool. + // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` + // ProjectID is the UUID of the project who owns the Pool. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + // Name of the pool. Name string `json:"name,omitempty"` @@ -257,10 +262,14 @@ type CreateMemberOpts struct { // Name of the Member. Name string `json:"name,omitempty"` - // The UUID of the tenant who owns the Member. Only administrative users - // can specify a tenant UUID other than their own. + // TenantID is the UUID of the project who owns the Member. + // Only administrative users can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` + // ProjectID is the UUID of the project who owns the Member. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + // A positive integer value that indicates the relative portion of traffic // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index 8ce85a54c8..532a2f23d2 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -21,6 +21,7 @@ type ListOpts struct { ObjectType string `q:"object_type"` ObjectID string `q:"object_id"` Action PolicyAction `q:"action"` + TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Marker string `q:"marker"` Limit int `q:"limit"` diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index 0a7ef79cf6..ebacc6ee34 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -11,13 +11,14 @@ import ( // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` - TenantID string `q:"tenant_id"` - 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"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` } // List returns a Pager which allows you to iterate over a collection of @@ -45,10 +46,14 @@ type CreateOpts struct { // Human-readable name for the Security Group. Does not have to be unique. Name string `json:"name" required:"true"` - // The UUID of the tenant who owns the Group. Only administrative users - // can specify a tenant UUID other than their own. + // TenantID is the UUID of the project who owns the Group. + // Only administrative users can specify a tenant UUID other than their own. TenantID string `json:"tenant_id,omitempty"` + // ProjectID is the UUID of the project who owns the Group. + // Only administrative users can specify a tenant UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + // Describes the security group. Description string `json:"description,omitempty"` } diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go index 8a8e0ffcfd..66915e6e55 100644 --- a/openstack/networking/v2/extensions/security/groups/results.go +++ b/openstack/networking/v2/extensions/security/groups/results.go @@ -22,8 +22,11 @@ type SecGroup struct { // traffic entering and leaving the group. Rules []rules.SecGroupRule `json:"security_group_rules"` - // Owner of the security group. + // TenantID is the project owner of the security group. TenantID string `json:"tenant_id"` + + // ProjectID is the project owner of the security group. + ProjectID string `json:"project_id"` } // SecGroupPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 197710fc4c..96cce2817d 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -21,6 +21,7 @@ type ListOpts struct { RemoteIPPrefix string `q:"remote_ip_prefix"` SecGroupID string `q:"security_group_id"` TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` @@ -118,9 +119,9 @@ type CreateOpts struct { // specified IP prefix as the source IP address of the IP packet. RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"` - // The UUID of the tenant who owns the Rule. Only administrative users - // can specify a tenant UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` + // TenantID is the UUID of the project who owns the Rule. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` } // ToSecGroupRuleCreateMap builds a request body from CreateOpts. diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index 0d8c43f8ed..377e753140 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -48,8 +48,11 @@ type SecGroupRule struct { // matches the specified IP prefix as the source IP address of the IP packet. RemoteIPPrefix string `json:"remote_ip_prefix"` - // The owner of this security group rule. + // TenantID is the project owner of this security group rule. TenantID string `json:"tenant_id"` + + // ProjectID is the project owner of this security group rule. + ProjectID string `json:"project_id"` } // SecGroupRulePage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 040f32183b..bc4460a065 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -21,6 +21,7 @@ type ListOpts struct { Name string `q:"name"` 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"` @@ -70,6 +71,7 @@ type CreateOpts struct { Name string `json:"name,omitempty"` Shared *bool `json:"shared,omitempty"` TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` AvailabilityZoneHints []string `json:"availability_zone_hints,omitempty"` } diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index c73f9e1a63..62f4b3c3a5 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -64,9 +64,12 @@ type Network struct { // Subnets associated with this network. Subnets []string `json:"subnets"` - // Owner of network. + // TenantID is the project owner of the network. TenantID string `json:"tenant_id"` + // ProjectID is the project owner of the network. + ProjectID string `json:"project_id"` + // Specifies whether the network resource can be accessed by any tenant. Shared bool `json:"shared"` diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index fd1e972576..90416faa19 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -22,6 +22,7 @@ type ListOpts struct { 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"` @@ -81,6 +82,7 @@ type CreateOpts struct { 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"` } diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index ebef98d5de..66937fd989 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -84,9 +84,12 @@ type Port struct { // the subnets where the IP addresses are picked from FixedIPs []IP `json:"fixed_ips"` - // Owner of network. + // TenantID is the project owner of the port. TenantID string `json:"tenant_id"` + // ProjectID is the project owner of the port. + ProjectID string `json:"project_id"` + // Identifies the entity (e.g.: dhcp agent) using this port. DeviceOwner string `json:"device_owner"` diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 32f5c78c67..597a4e77f3 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -21,6 +21,7 @@ type ListOpts struct { EnableDHCP *bool `q:"enable_dhcp"` NetworkID string `q:"network_id"` TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` IPVersion int `q:"ip_version"` GatewayIP string `q:"gateway_ip"` CIDR string `q:"cidr"` @@ -84,10 +85,14 @@ type CreateOpts struct { // Name is a human-readable name of the subnet. Name string `json:"name,omitempty"` - // The UUID of the tenant who owns the Subnet. Only administrative users - // can specify a tenant UUID other than their own. + // The UUID of the project who owns the Subnet. Only administrative users + // can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` + // The UUID of the project who owns the Subnet. Only administrative users + // can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + // AllocationPools are IP Address pools that will be available for DHCP. AllocationPools []AllocationPool `json:"allocation_pools,omitempty"` diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index 8cc4dfac46..493e5c042e 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -91,9 +91,12 @@ type Subnet struct { // Specifies whether DHCP is enabled for this subnet or not. EnableDHCP bool `json:"enable_dhcp"` - // Owner of network. + // TenantID is the project owner of the subnet. TenantID string `json:"tenant_id"` + // ProjectID is the project owner of the subnet. + ProjectID string `json:"project_id"` + // The IPv6 address modes specifies mechanisms for assigning IPv6 IP addresses. IPv6AddressMode string `json:"ipv6_address_mode"` From fbf9a3c4ef4f5ea3ab16fbe095e532934f8b00e1 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Tue, 27 Feb 2018 05:29:00 +0100 Subject: [PATCH 0225/2296] Vpnaas: Show IPSec Policy Details (#773) * Added get function for IPSec Policies * Added GetResult * added unit test for Get function * Added acceptance test * fixed C&P error * added projectID --- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 9 ++- .../vpnaas/ipsecpolicies/requests.go | 6 ++ .../vpnaas/ipsecpolicies/results.go | 9 +++ .../ipsecpolicies/testing/requests_test.go | 57 +++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index e46e7150bc..e4ee3d4063 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" ) func TestPolicyCRUD(t *testing.T) { @@ -17,9 +18,15 @@ func TestPolicyCRUD(t *testing.T) { policy, err := CreateIPSecPolicy(t, client) if err != nil { - t.Fatalf("Unable to create policy: %v", err) + t.Fatalf("Unable to create IPSec policy: %v", err) } defer DeleteIPSecPolicy(t, client, policy.ID) tools.PrintResource(t, policy) + + newPolicy, err := ipsecpolicies.Get(client, policy.ID).Extract() + if err != nil { + t.Fatalf("Unable to get IPSec policy: %v", err) + } + tools.PrintResource(t, newPolicy) } diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go index d9c38c2dec..0fc1813afd 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -115,3 +115,9 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } + +// Get retrieves a particular IPSec policy based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go index e011f4670b..dffab6444a 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go @@ -9,6 +9,9 @@ type Policy struct { // TenantID is the ID of the project TenantID string `json:"tenant_id"` + // ProjectID is the ID of the project + ProjectID string `json:"project_id"` + // Description is the human readable description of the policy Description string `json:"description"` @@ -71,3 +74,9 @@ type CreateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Policy. +type GetResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go index 42907e6220..03bec8d070 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -49,6 +49,7 @@ func TestCreate(t *testing.T) { "encryption_algorithm": "aes-128", "pfs": "group5", "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", "lifetime": { "units": "seconds", "value": 7200 @@ -92,6 +93,62 @@ func TestCreate(t *testing.T) { Description: "", Lifetime: expectedLifetime, ID: "5291b189-fd84-46e5-84bd-78f40c05d69c", + ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", + } + th.AssertDeepEquals(t, expected, *actual) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` +{ + "ipsecpolicy": { + "name": "ipsecpolicy1", + "transform_protocol": "esp", + "auth_algorithm": "sha1", + "encapsulation_mode": "tunnel", + "encryption_algorithm": "aes-128", + "pfs": "group5", + "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "lifetime": { + "units": "seconds", + "value": 7200 + }, + "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "description": "" + } +} + `) + }) + + actual, err := ipsecpolicies.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + th.AssertNoErr(t, err) + expectedLifetime := ipsecpolicies.Lifetime{ + Units: "seconds", + Value: 7200, + } + expected := ipsecpolicies.Policy{ + Name: "ipsecpolicy1", + TransformProtocol: "esp", + Description: "", + AuthAlgorithm: "sha1", + EncapsulationMode: "tunnel", + EncryptionAlgorithm: "aes-128", + PFS: "group5", + Lifetime: expectedLifetime, + TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", } th.AssertDeepEquals(t, expected, *actual) } From eedbafadaa1aecde95e4c14c3bebb401275b4d5d Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Tue, 27 Feb 2018 05:32:27 +0100 Subject: [PATCH 0226/2296] Vpnaas: Create IKE policy (#785) * Added result struct and file structure for IKE policies. Also added unit test for ike policy creation * added create function and unit test for ikepolicies * Added acceptance test for IKE policy creation * Added documentation * Fixed typo * formatting * renamed Acceptance tests, renamed variables, removed invalid fields * Added projectID field and removed line --- .../v2/extensions/vpnaas/ikepolicy_test.go | 24 ++++ .../v2/extensions/vpnaas/ipsecpolicy_test.go | 2 +- .../networking/v2/extensions/vpnaas/vpnaas.go | 24 ++++ .../v2/extensions/vpnaas/ikepolicies/doc.go | 21 ++++ .../extensions/vpnaas/ikepolicies/requests.go | 107 ++++++++++++++++++ .../extensions/vpnaas/ikepolicies/results.go | 65 +++++++++++ .../ikepolicies/testing/requests_test.go | 85 ++++++++++++++ .../v2/extensions/vpnaas/ikepolicies/urls.go | 16 +++ 8 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/ikepolicies/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go new file mode 100644 index 0000000000..365745a9d7 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -0,0 +1,24 @@ +// +build acceptance networking vpnaas + +package vpnaas + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" +) + +func TestIKEPolicyCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + policy, err := CreateIKEPolicy(t, client) + if err != nil { + t.Fatalf("Unable to create IKE policy: %v", err) + } + + tools.PrintResource(t, policy) +} diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index e4ee3d4063..2d7b4dac30 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" ) -func TestPolicyCRUD(t *testing.T) { +func TestIPSecPolicyCRUD(t *testing.T) { 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/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index b899c624f4..385597ed42 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" ) @@ -67,6 +68,29 @@ func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecp return policy, nil } +// CreateIKEPolicy will create an IKE Policy with a random name and given +// rule. An error will be returned if the policy could not be created. +func CreateIKEPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ikepolicies.Policy, error) { + policyName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create policy %s", policyName) + + createOpts := ikepolicies.CreateOpts{ + Name: policyName, + EncryptionAlgorithm: ikepolicies.EncryptionAlgorithm3DES, + PFS: ikepolicies.PFSGroup5, + } + + policy, err := ikepolicies.Create(client, createOpts).Extract() + if err != nil { + return policy, err + } + + t.Logf("Successfully created IKE policy %s", policyName) + + return policy, nil +} + // DeleteIPSecPolicy will delete an IPSec policy with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go new file mode 100644 index 0000000000..6b65ff9985 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -0,0 +1,21 @@ +/* +Package ikepolicies allows management and retrieval of IKE policies in the +OpenStack Networking Service. + + +Example to Create an IKE policy + + createOpts := ikepolicies.CreateOpts{ + Name: "ikepolicy1", + Description: "Description of ikepolicy1", + EncryptionAlgorithm: ikepolicies.EncryptionAlgorithm3DES, + PFS: ikepolicies.PFSGroup5, + } + + policy, err := ikepolicies.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +*/ +package ikepolicies diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go new file mode 100644 index 0000000000..ed42ddf70e --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -0,0 +1,107 @@ +package ikepolicies + +import "github.com/gophercloud/gophercloud" + +type AuthAlgorithm string +type EncryptionAlgorithm string +type PFS string +type Unit string +type IKEVersion string +type Phase1NegotiationMode string + +const ( + AuthAlgorithmSHA1 AuthAlgorithm = "sha1" + AuthAlgorithmSHA256 AuthAlgorithm = "sha256" + AuthAlgorithmSHA384 AuthAlgorithm = "sha384" + AuthAlgorithmSHA512 AuthAlgorithm = "sha512" + EncryptionAlgorithm3DES EncryptionAlgorithm = "3des" + EncryptionAlgorithmAES128 EncryptionAlgorithm = "aes-128" + EncryptionAlgorithmAES256 EncryptionAlgorithm = "aes-256" + EncryptionAlgorithmAES192 EncryptionAlgorithm = "aes-192" + UnitSeconds Unit = "seconds" + UnitKilobytes Unit = "kilobytes" + PFSGroup2 PFS = "group2" + PFSGroup5 PFS = "group5" + PFSGroup14 PFS = "group14" + IKEVersionv1 IKEVersion = "v1" + IKEVersionv2 IKEVersion = "v2" + Phase1NegotiationModeMain Phase1NegotiationMode = "main" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPolicyCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new IKE policy +type CreateOpts struct { + // TenantID specifies a tenant to own the IKE policy. The caller must have + // an admin role in order to set this. Otherwise, this field is left unset + // and the caller will be the owner. + TenantID string `json:"tenant_id,omitempty"` + + // Description is the human readable description of the policy. + Description string `json:"description,omitempty"` + + // Name is the human readable name of the policy. + // Does not have to be unique. + Name string `json:"name,omitempty"` + + // AuthAlgorithm is the authentication hash algorithm. + // Valid values are sha1, sha256, sha384, sha512. + // The default is sha1. + AuthAlgorithm AuthAlgorithm `json:"auth_algorithm,omitempty"` + + // EncryptionAlgorithm is the encryption algorithm. + // A valid value is 3des, aes-128, aes-192, aes-256, and so on. + // Default is aes-128. + EncryptionAlgorithm EncryptionAlgorithm `json:"encryption_algorithm,omitempty"` + + // PFS is the Perfect forward secrecy mode. + // A valid value is Group2, Group5, Group14, and so on. + // Default is Group5. + PFS PFS `json:"pfs,omitempty"` + + // The IKE mode. + // A valid value is main, which is the default. + Phase1NegotiationMode Phase1NegotiationMode `json:"phase1_negotiation_mode,omitempty"` + + // The IKE version. + // A valid value is v1 or v2. + // Default is v1. + IKEVersion IKEVersion `json:"ike_version,omitempty"` + + //Lifetime is the lifetime of the security association + Lifetime *LifetimeCreateOpts `json:"lifetime,omitempty"` +} + +// The lifetime consists of a unit and integer value +// You can omit either the unit or value portion of the lifetime +type LifetimeCreateOpts struct { + // Units is the units for the lifetime of the security association + // Default unit is seconds + Units Unit `json:"units,omitempty"` + + // The lifetime value. + // Must be a positive integer. + // Default value is 3600. + Value int `json:"value,omitempty"` +} + +// ToPolicyCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "ikepolicy") +} + +// Create accepts a CreateOpts struct and uses the values to create a new +// IKE policy +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToPolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go new file mode 100644 index 0000000000..01ea95e429 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go @@ -0,0 +1,65 @@ +package ikepolicies + +import "github.com/gophercloud/gophercloud" + +// Policy is an IKE Policy +type Policy struct { + // TenantID is the ID of the project + TenantID string `json:"tenant_id"` + + // ProjectID is the ID of the project + ProjectID string `json:"project_id"` + + // Description is the human readable description of the policy + Description string `json:"description"` + + // Name is the human readable name of the policy + Name string `json:"name"` + + // AuthAlgorithm is the authentication hash algorithm + AuthAlgorithm string `json:"auth_algorithm"` + + // EncryptionAlgorithm is the encryption algorithm + EncryptionAlgorithm string `json:"encryption_algorithm"` + + // PFS is the Perfect forward secrecy (PFS) mode + PFS string `json:"pfs"` + + // Lifetime is the lifetime of the security association + Lifetime Lifetime `json:"lifetime"` + + // ID is the ID of the policy + ID string `json:"id"` + + // Phase1NegotiationMode is the IKE mode + Phase1NegotiationMode string `json:"phase1_negotiation_mode"` + + // IKEVersion is the IKE version. + IKEVersion string `json:"ike_version"` +} + +type commonResult struct { + gophercloud.Result +} +type Lifetime struct { + // Units is the unit for the lifetime + // Default is seconds + Units string `json:"units"` + + // Value is the lifetime + // Default is 3600 + Value int `json:"value"` +} + +// Extract is a function that accepts a result and extracts an IKE Policy. +func (r commonResult) Extract() (*Policy, error) { + var s struct { + Policy *Policy `json:"ikepolicy"` + } + err := r.ExtractInto(&s) + return s.Policy, err +} + +type CreateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go new file mode 100644 index 0000000000..f8485e4256 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -0,0 +1,85 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ikepolicies", 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, ` +{ + "ikepolicy":{ + "name": "policy", + "description": "IKE policy", + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "ike_version": "v2" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "ikepolicy":{ + "name": "policy", + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", + "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + "description": "IKE policy", + "auth_algorithm": "sha1", + "encryption_algorithm": "aes-128", + "pfs": "Group5", + "lifetime": { + "value": 3600, + "units": "seconds" + }, + "phase1_negotiation_mode": "main", + "ike_version": "v2" + } +} + `) + }) + + options := ikepolicies.CreateOpts{ + TenantID: "9145d91459d248b1b02fdaca97c6a75d", + Name: "policy", + Description: "IKE policy", + IKEVersion: ikepolicies.IKEVersionv2, + } + + actual, err := ikepolicies.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + expectedLifetime := ikepolicies.Lifetime{ + Units: "seconds", + Value: 3600, + } + expected := ikepolicies.Policy{ + AuthAlgorithm: "sha1", + IKEVersion: "v2", + TenantID: "9145d91459d248b1b02fdaca97c6a75d", + Phase1NegotiationMode: "main", + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + Lifetime: expectedLifetime, + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", + } + th.AssertDeepEquals(t, expected, *actual) +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/urls.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/urls.go new file mode 100644 index 0000000000..a364a881e6 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/urls.go @@ -0,0 +1,16 @@ +package ikepolicies + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "vpn" + resourcePath = "ikepolicies" +) + +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 7cab6edf6ad120e9a3a8b84724e1fdb0a8158bdb Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 28 Feb 2018 04:08:15 +0100 Subject: [PATCH 0227/2296] Vpnaas: Show IKE policy details (#786) * Added unit test for ike policy get operation * Added get request * fixed unit test * Added acceptance test * added project id to test * added documentation --- .../v2/extensions/vpnaas/ikepolicy_test.go | 9 ++- .../v2/extensions/vpnaas/ikepolicies/doc.go | 7 +++ .../extensions/vpnaas/ikepolicies/requests.go | 6 ++ .../extensions/vpnaas/ikepolicies/results.go | 8 +++ .../ikepolicies/testing/requests_test.go | 55 +++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index 365745a9d7..c721007bea 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" ) func TestIKEPolicyCRUD(t *testing.T) { @@ -19,6 +20,12 @@ func TestIKEPolicyCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create IKE policy: %v", err) } - tools.PrintResource(t, policy) + + newPolicy, err := ikepolicies.Get(client, policy.ID).Extract() + if err != nil { + t.Fatalf("Unable to get IKE policy: %v", err) + } + tools.PrintResource(t, newPolicy) + } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index 6b65ff9985..9d4803de67 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -17,5 +17,12 @@ Example to Create an IKE policy panic(err) } +Example to Show the details of a specific IKE policy by ID + + policy, err := ikepolicies.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + if err != nil { + panic(err) + } + */ package ikepolicies diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go index ed42ddf70e..be7fbe2f5f 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -105,3 +105,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } + +// Get retrieves a particular IKE policy based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go index 01ea95e429..411366efd9 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go @@ -60,6 +60,14 @@ func (r commonResult) Extract() (*Policy, error) { return s.Policy, err } +// CreateResult represents the result of a Create operation. Call its Extract method to +// interpret it as a Policy. type CreateResult struct { commonResult } + +// GetResult represents the result of a Get operation. Call its Extract method to +// interpret it as a Policy. +type GetResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index f8485e4256..5f864e94a8 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -83,3 +83,58 @@ func TestCreate(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ikepolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` +{ + "ikepolicy":{ + "name": "policy", + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", + "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "description": "IKE policy", + "auth_algorithm": "sha1", + "encryption_algorithm": "aes-128", + "pfs": "Group5", + "lifetime": { + "value": 3600, + "units": "seconds" + }, + "phase1_negotiation_mode": "main", + "ike_version": "v2" + } +} + `) + }) + + actual, err := ikepolicies.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + th.AssertNoErr(t, err) + expectedLifetime := ikepolicies.Lifetime{ + Units: "seconds", + Value: 3600, + } + expected := ikepolicies.Policy{ + AuthAlgorithm: "sha1", + IKEVersion: "v2", + TenantID: "9145d91459d248b1b02fdaca97c6a75d", + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", + Phase1NegotiationMode: "main", + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Lifetime: expectedLifetime, + } + th.AssertDeepEquals(t, expected, *actual) +} From 3ffde9a2861d6120fa02fcfc6fe70dc24aa10742 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 28 Feb 2018 04:25:30 +0100 Subject: [PATCH 0228/2296] Vpnaas: List IPSec Policies (#772) * Added unit test for list function * Added required parts to results.go for listing function * Added list request * Added acceptance test for list policy * removed id field from Listopts, removed Lifetime field from ListOpts, added projectID to unit test * Added documentation --- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 21 ++++++ .../v2/extensions/vpnaas/ipsecpolicies/doc.go | 13 ++++ .../vpnaas/ipsecpolicies/requests.go | 49 +++++++++++- .../vpnaas/ipsecpolicies/results.go | 38 ++++++++++ .../ipsecpolicies/testing/requests_test.go | 75 +++++++++++++++++++ 5 files changed, 195 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index 2d7b4dac30..4a9905bb93 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -10,6 +10,27 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" ) +func TestPolicyList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := ipsecpolicies.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list IPSec policies: %v", err) + } + + allPolicies, err := ipsecpolicies.ExtractPolicies(allPages) + if err != nil { + t.Fatalf("Unable to extract policies: %v", err) + } + + for _, policy := range allPolicies { + tools.PrintResource(t, policy) + } +} + func TestIPSecPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go index 4da16abeba..26ce7b5980 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -19,5 +19,18 @@ Example to Delete a Policy if err != nil { panic(err) } + +Example to List IPSec policies + + allPages, err := ipsecpolicies.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list IPSec policies: %v", err) + } + + allPolicies, err := ipsecpolicies.ExtractPolicies(allPages) + if err != nil { + t.Fatalf("Unable to extract policies: %v", err) + } + */ package ipsecpolicies diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go index 0fc1813afd..d7c2ddc45b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -1,6 +1,9 @@ package ipsecpolicies -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) type TransformProtocol string type AuthAlgorithm string @@ -121,3 +124,47 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToPolicyListQuery() (string, error) +} + +// ListOpts allows the filtering of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the IPSec policy attributes you want to see returned. +type ListOpts struct { + TenantID string `q:"tenant_id"` + Name string `q:"name"` + Description string `q:"description"` + ProjectID string `q:"project_id"` + AuthAlgorithm string `q:"auth_algorithm"` + EncapsulationMode string `q:"encapsulation_mode"` + EncryptionAlgorithm string `q:"encryption_algorithm"` + PFS string `q:"pfs"` + TransformProtocol string `q:"transform_protocol"` +} + +// ToPolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// IPSec policies. It accepts a ListOpts struct, which allows you to filter +// the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToPolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return PolicyPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go index dffab6444a..780ecbab8d 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go @@ -2,6 +2,7 @@ package ipsecpolicies import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // Policy is an IPSec Policy @@ -80,3 +81,40 @@ type DeleteResult struct { type GetResult struct { commonResult } + +// PolicyPage is the page returned by a pager when traversing over a +// collection of Policies. +type PolicyPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of IPSec policies 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 PolicyPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"ipsecpolicies_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a PolicyPage struct is empty. +func (r PolicyPage) IsEmpty() (bool, error) { + is, err := ExtractPolicies(r) + return len(is) == 0, err +} + +// ExtractPolicies accepts a Page struct, specifically a Policy struct, +// and extracts the elements into a slice of Policy structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractPolicies(r pagination.Page) ([]Policy, error) { + var s struct { + Policies []Policy `json:"ipsecpolicies"` + } + err := (r.(PolicyPage)).ExtractInto(&s) + return s.Policies, err +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go index 03bec8d070..d6148b59d6 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -7,6 +7,7 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -166,3 +167,77 @@ func TestDelete(t *testing.T) { res := ipsecpolicies.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies", 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, ` + { + "ipsecpolicies": [ + { + "name": "ipsecpolicy1", + "transform_protocol": "esp", + "auth_algorithm": "sha1", + "encapsulation_mode": "tunnel", + "encryption_algorithm": "aes-128", + "pfs": "group5", + "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "lifetime": { + "units": "seconds", + "value": 7200 + }, + "id": "5291b189-fd84-46e5-84bd-78f40c05d69c", + "description": "" + } + ] +} + `) + }) + + count := 0 + + ipsecpolicies.List(fake.ServiceClient(), ipsecpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ipsecpolicies.ExtractPolicies(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + + expected := []ipsecpolicies.Policy{ + { + Name: "ipsecpolicy1", + TransformProtocol: "esp", + TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", + ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", + AuthAlgorithm: "sha1", + EncapsulationMode: "tunnel", + EncryptionAlgorithm: "aes-128", + PFS: "group5", + Lifetime: ipsecpolicies.Lifetime{ + Value: 7200, + Units: "seconds", + }, + Description: "", + ID: "5291b189-fd84-46e5-84bd-78f40c05d69c", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} From 95b324abc9c478b454cc572eec6c2b4852716764 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 28 Feb 2018 03:27:28 +0000 Subject: [PATCH 0229/2296] Networking v2: VPNaaS Doc fixes --- .../networking/v2/extensions/vpnaas/ikepolicies/doc.go | 6 +++--- .../networking/v2/extensions/vpnaas/ipsecpolicies/doc.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index 9d4803de67..c1affed9ac 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -6,8 +6,8 @@ OpenStack Networking Service. Example to Create an IKE policy createOpts := ikepolicies.CreateOpts{ - Name: "ikepolicy1", - Description: "Description of ikepolicy1", + Name: "ikepolicy1", + Description: "Description of ikepolicy1", EncryptionAlgorithm: ikepolicies.EncryptionAlgorithm3DES, PFS: ikepolicies.PFSGroup5, } @@ -20,7 +20,7 @@ Example to Create an IKE policy Example to Show the details of a specific IKE policy by ID policy, err := ikepolicies.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() - if err != nil { + if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go index 26ce7b5980..d2d227f466 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -24,12 +24,12 @@ Example to List IPSec policies allPages, err := ipsecpolicies.List(client, nil).AllPages() if err != nil { - t.Fatalf("Unable to list IPSec policies: %v", err) + panic(err) } allPolicies, err := ipsecpolicies.ExtractPolicies(allPages) if err != nil { - t.Fatalf("Unable to extract policies: %v", err) + panic(err) } */ From a3a5eb426a650a1184b3964e2c5c6c78a03021e7 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 28 Feb 2018 17:36:07 +0100 Subject: [PATCH 0230/2296] Vpnaas: Service Get/List (#771) * unit test for List and Get * request functions for list and get added to requests.go * edited comment for accuracy * Added relevant parts for service listing to results.go * Made test compare structs instead of values * Added acceptance test * Added documentation in doc.go file * Added missing parameters * Added project_id as a field to result struct * removed id field from ListOpts --- .../v2/extensions/vpnaas/service_test.go | 28 +++++ .../v2/extensions/vpnaas/services/doc.go | 19 +++ .../v2/extensions/vpnaas/services/requests.go | 57 ++++++++- .../v2/extensions/vpnaas/services/results.go | 47 ++++++++ .../vpnaas/services/testing/requests_test.go | 111 +++++++++++++++++- 5 files changed, 260 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index 658f0840d5..f88aa7611d 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -8,8 +8,30 @@ import ( "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/openstack/networking/v2/extensions/vpnaas/services" ) +func TestServiceList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := services.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list services: %v", err) + } + + allServices, err := services.ExtractServices(allPages) + if err != nil { + t.Fatalf("Unable to extract services: %v", err) + } + + for _, service := range allServices { + tools.PrintResource(t, service) + } +} + func TestServiceCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { @@ -28,5 +50,11 @@ func TestServiceCRUD(t *testing.T) { } defer DeleteService(t, client, service.ID) + newService, err := services.Get(client, service.ID).Extract() + if err != nil { + t.Fatalf("Unable to get service: %v", err) + } + tools.PrintResource(t, service) + tools.PrintResource(t, newService) } diff --git a/openstack/networking/v2/extensions/vpnaas/services/doc.go b/openstack/networking/v2/extensions/vpnaas/services/doc.go index 2060bcb086..bf4302a5aa 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/services/doc.go @@ -2,6 +2,25 @@ Package services allows management and retrieval of VPN services in the OpenStack Networking Service. +Example to List Services + + listOpts := services.ListOpts{ + TenantID: "966b3c7d36a24facaf20b7e458bf2192", + } + + allPages, err := services.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allPolicies, err := services.ExtractServices(allPages) + if err != nil { + panic(err) + } + + for _, service := range allServices { + fmt.Printf("%+v\n", service) + } Example to Create a Service diff --git a/openstack/networking/v2/extensions/vpnaas/services/requests.go b/openstack/networking/v2/extensions/vpnaas/services/requests.go index 7073508f9f..6cfd656a25 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/services/requests.go @@ -1,6 +1,9 @@ package services -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. @@ -57,3 +60,55 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToServiceListQuery() (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 VPN service attributes you want to see returned. +type ListOpts struct { + TenantID string `q:"tenant_id"` + Name string `q:"name"` + Description string `q:"description"` + AdminStateUp *bool `q:"admin_state_up"` + Status string `q:"status"` + SubnetID string `q:"subnet_id"` + RouterID string `q:"router_id"` + ProjectID string `q:"project_id"` + ExternalV6IP string `q:"external_v6_ip"` + ExternalV4IP string `q:"external_v4_ip"` + FlavorID string `q:"flavor_id"` +} + +// ToServiceListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToServiceListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// VPN services. 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.ToServiceListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return ServicePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves a particular VPN service based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/results.go b/openstack/networking/v2/extensions/vpnaas/services/results.go index 77bc02b76c..df30884e21 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/results.go +++ b/openstack/networking/v2/extensions/vpnaas/services/results.go @@ -2,6 +2,7 @@ package services import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // Service is a VPN Service @@ -9,6 +10,9 @@ type Service struct { // TenantID is the ID of the project. TenantID string `json:"tenant_id"` + // ProjectID is the ID of the project. + ProjectID string `json:"project_id"` + // SubnetID is the ID of the subnet. SubnetID string `json:"subnet_id"` @@ -46,6 +50,49 @@ type commonResult struct { gophercloud.Result } +// ServicePage is the page returned by a pager when traversing over a +// collection of VPN services. +type ServicePage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of VPN services 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 ServicePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"vpnservices_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a ServicePage struct is empty. +func (r ServicePage) IsEmpty() (bool, error) { + is, err := ExtractServices(r) + return len(is) == 0, err +} + +// ExtractServices accepts a Page struct, specifically a Service struct, +// and extracts the elements into a slice of Service structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractServices(r pagination.Page) ([]Service, error) { + var s struct { + Services []Service `json:"vpnservices"` + } + err := (r.(ServicePage)).ExtractInto(&s) + return s.Services, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Service. +type GetResult struct { + commonResult +} + // Extract is a function that accepts a result and extracts a VPN service. func (r commonResult) Extract() (*Service, error) { var s struct { diff --git a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go index 450a56ad26..0bfa1ded67 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -46,7 +47,8 @@ func TestCreate(t *testing.T) { "tenant_id": "10039663455a446d8ba2cbb058b0f578", "external_v4_ip": "172.32.1.11", "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", - "description": "OpenStack VPN service" + "description": "OpenStack VPN service", + "project_id": "10039663455a446d8ba2cbb058b0f578" } } `) @@ -69,6 +71,7 @@ func TestCreate(t *testing.T) { AdminStateUp: true, SubnetID: "", TenantID: "10039663455a446d8ba2cbb058b0f578", + ProjectID: "10039663455a446d8ba2cbb058b0f578", ExternalV4IP: "172.32.1.11", ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", Description: "OpenStack VPN service", @@ -76,6 +79,112 @@ func TestCreate(t *testing.T) { th.AssertDeepEquals(t, expected, *actual) } +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/vpnservices", 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, ` +{ + "vpnservices":[ + { + "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + "status": "PENDING_CREATE", + "name": "vpnservice1", + "admin_state_up": true, + "subnet_id": null, + "project_id": "10039663455a446d8ba2cbb058b0f578", + "tenant_id": "10039663455a446d8ba2cbb058b0f578", + "description": "Test VPN service" + } + ] +} + `) + }) + + count := 0 + + services.List(fake.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := services.ExtractServices(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + + expected := []services.Service{ + { + Status: "PENDING_CREATE", + Name: "vpnservice1", + AdminStateUp: true, + TenantID: "10039663455a446d8ba2cbb058b0f578", + ProjectID: "10039663455a446d8ba2cbb058b0f578", + Description: "Test VPN service", + SubnetID: "", + RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/vpnservices/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` +{ + "vpnservice": { + "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + "status": "PENDING_CREATE", + "name": "vpnservice1", + "admin_state_up": true, + "subnet_id": null, + "project_id": "10039663455a446d8ba2cbb058b0f578", + "tenant_id": "10039663455a446d8ba2cbb058b0f578", + "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "description": "VPN test service" + } +} + `) + }) + + actual, err := services.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + th.AssertNoErr(t, err) + expected := services.Service{ + Status: "PENDING_CREATE", + Name: "vpnservice1", + Description: "VPN test service", + AdminStateUp: true, + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + TenantID: "10039663455a446d8ba2cbb058b0f578", + ProjectID: "10039663455a446d8ba2cbb058b0f578", + RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + SubnetID: "", + } + th.AssertDeepEquals(t, expected, *actual) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 43de125425b9d90b4d5eb67fa9b568493d61416a Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 28 Feb 2018 17:43:44 +0100 Subject: [PATCH 0231/2296] Vpnaas: Delete IKE policy (#792) * Added ike policy delete operation * Added documentation --- .../v2/extensions/vpnaas/ikepolicy_test.go | 2 ++ .../networking/v2/extensions/vpnaas/vpnaas.go | 22 +++++++++++++++---- .../v2/extensions/vpnaas/ikepolicies/doc.go | 7 ++++++ .../extensions/vpnaas/ikepolicies/requests.go | 7 ++++++ .../extensions/vpnaas/ikepolicies/results.go | 6 +++++ .../ikepolicies/testing/requests_test.go | 14 ++++++++++++ 6 files changed, 54 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index c721007bea..c411ef68ec 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -20,6 +20,8 @@ func TestIKEPolicyCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create IKE policy: %v", err) } + defer DeleteIKEPolicy(t, client, policy.ID) + tools.PrintResource(t, policy) newPolicy, err := ikepolicies.Get(client, policy.ID).Extract() diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index 385597ed42..f5f9a110d3 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -73,7 +73,7 @@ func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecp func CreateIKEPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ikepolicies.Policy, error) { policyName := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create policy %s", policyName) + t.Logf("Attempting to create IKE policy %s", policyName) createOpts := ikepolicies.CreateOpts{ Name: policyName, @@ -95,12 +95,26 @@ func CreateIKEPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ikepolic // occur if the delete was not successful. This works best when used as a // deferred function. func DeleteIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { - t.Logf("Attempting to delete policy: %s", policyID) + t.Logf("Attempting to delete IPSec policy: %s", policyID) err := ipsecpolicies.Delete(client, policyID).ExtractErr() if err != nil { - t.Fatalf("Unable to delete policy %s: %v", policyID, err) + t.Fatalf("Unable to delete IPSec policy %s: %v", policyID, err) + } + + t.Logf("Deleted IPSec policy: %s", policyID) +} + +// DeleteIKEPolicy will delete an IKE policy 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 DeleteIKEPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { + t.Logf("Attempting to delete policy: %s", policyID) + + err := ikepolicies.Delete(client, policyID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete IKE policy %s: %v", policyID, err) } - t.Logf("Deleted policy: %s", policyID) + t.Logf("Deleted IKE policy: %s", policyID) } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index c1affed9ac..94fdbdd485 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -24,5 +24,12 @@ 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() + if err != nil { + panic(err) + } + */ package ikepolicies diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go index be7fbe2f5f..89c73c7e4e 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -111,3 +111,10 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } + +// Delete will permanently delete a particular IKE policy based on its +// unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go index 411366efd9..eb5bd8857b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go @@ -71,3 +71,9 @@ type CreateResult struct { type GetResult struct { commonResult } + +// DeleteResult represents the results of a Delete operation. Call its ExtractErr method +// to determine whether the operation succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index 5f864e94a8..f5f1f9dc9b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -138,3 +138,17 @@ func TestGet(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ikepolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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) + }) + + res := ikepolicies.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + th.AssertNoErr(t, res.Err) +} From afbf0422412f5dc726fa12be280fa0c3cb31fcbd Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 28 Feb 2018 17:50:54 +0100 Subject: [PATCH 0232/2296] Vpnaas: Create Endpoint Group (#794) * Added request, result, unit test and acceptance test for endpoint group creation * Added documentation * switched acronyms to uppercase, changed ToGroupCreateMap to ToEndpointGroupCreateMap --- .../v2/extensions/vpnaas/group_test.go | 24 ++++++ .../networking/v2/extensions/vpnaas/vpnaas.go | 26 +++++++ .../extensions/vpnaas/endpointgroups/doc.go | 19 +++++ .../vpnaas/endpointgroups/requests.go | 58 ++++++++++++++ .../vpnaas/endpointgroups/results.go | 48 ++++++++++++ .../endpointgroups/testing/requests_test.go | 78 +++++++++++++++++++ .../extensions/vpnaas/endpointgroups/urls.go | 16 ++++ 7 files changed, 269 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go create mode 100644 openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go create mode 100644 openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go create mode 100644 openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/endpointgroups/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go new file mode 100644 index 0000000000..7305fcf8c6 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -0,0 +1,24 @@ +// +build acceptance networking vpnaas + +package vpnaas + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" +) + +func TestGroupCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + group, err := CreateEndpointGroup(t, client) + if err != nil { + t.Fatalf("Unable to create Endpoint group: %v", err) + } + + tools.PrintResource(t, group) +} diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index f5f9a110d3..a770895457 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" @@ -118,3 +119,28 @@ func DeleteIKEPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID s t.Logf("Deleted IKE policy: %s", policyID) } + +// CreateEndpointGroup will create an endpoint group with a random name. +// An error will be returned if the group could not be created. +func CreateEndpointGroup(t *testing.T, client *gophercloud.ServiceClient) (*endpointgroups.EndpointGroup, error) { + groupName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create group %s", groupName) + + createOpts := endpointgroups.CreateOpts{ + Name: groupName, + Type: endpointgroups.TypeCIDR, + Endpoints: []string{ + "10.2.0.0/24", + "10.3.0.0/24", + }, + } + group, err := endpointgroups.Create(client, createOpts).Extract() + if err != nil { + return group, err + } + + t.Logf("Successfully created group %s", groupName) + + return group, nil +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go new file mode 100644 index 0000000000..3915124555 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go @@ -0,0 +1,19 @@ +/* +Package endpointgroups allows management of endpoint groups in the Openstack Network Service + +Example to create an Endpoint Group + + createOpts := endpointgroups.CreateOpts{ + Name: groupName, + Type: endpointgroups.TypeCidr, + Endpoints: []string{ + "10.2.0.0/24", + "10.3.0.0/24", + }, + } + group, err := endpointgroups.Create(client, createOpts).Extract() + if err != nil { + return group, err + } +*/ +package endpointgroups diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go new file mode 100644 index 0000000000..47b9855894 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -0,0 +1,58 @@ +package endpointgroups + +import "github.com/gophercloud/gophercloud" + +type EndpointType string + +const ( + TypeSubnet EndpointType = "subnet" + TypeCIDR EndpointType = "cidr" + TypeVLAN EndpointType = "vlan" + TypeNetwork EndpointType = "network" + TypeRouter EndpointType = "router" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToEndpointGroupCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new endpoint group +type CreateOpts struct { + // TenantID specifies a tenant to own the endpoint group. The caller must have + // an admin role in order to set this. Otherwise, this field is left unset + // and the caller will be the owner. + TenantID string `json:"tenant_id,omitempty"` + + // Description is the human readable description of the endpoint group. + Description string `json:"description,omitempty"` + + // Name is the human readable name of the endpoint group. + Name string `json:"name,omitempty"` + + // The type of the endpoints in the group. + // A valid value is subnet, cidr, network, router, or vlan. + Type EndpointType `json:"type,omitempty"` + + // List of endpoints of the same type, for the endpoint group. + // The values will depend on the type. + Endpoints []string `json:"endpoints"` +} + +// ToEndpointGroupCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToEndpointGroupCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "endpoint_group") +} + +// Create accepts a CreateOpts struct and uses the values to create a new +// endpoint group. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToEndpointGroupCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go new file mode 100644 index 0000000000..c4a6ade334 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go @@ -0,0 +1,48 @@ +package endpointgroups + +import ( + "github.com/gophercloud/gophercloud" +) + +// EndpointGroup is an endpoint group. +type EndpointGroup struct { + // TenantID specifies a tenant to own the endpoint group. + TenantID string `json:"tenant_id"` + + // TenantID specifies a tenant to own the endpoint group. + ProjectID string `json:"project_id"` + + // Description is the human readable description of the endpoint group. + Description string `json:"description"` + + // Name is the human readable name of the endpoint group. + Name string `json:"name"` + + // Type is the type of the endpoints in the group. + Type string `json:"type"` + + // Endpoints is a list of endpoints. + Endpoints []string `json:"endpoints"` + + // ID is the id of the endpoint group + ID string `json:"id"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts an endpoint group. +func (r commonResult) Extract() (*EndpointGroup, error) { + var s struct { + Service *EndpointGroup `json:"endpoint_group"` + } + err := r.ExtractInto(&s) + return s.Service, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as an endpoint group. +type CreateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go new file mode 100644 index 0000000000..f4fe3eb634 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -0,0 +1,78 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups", 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, ` +{ + "endpoint_group": { + "endpoints": [ + "10.2.0.0/24", + "10.3.0.0/24" + ], + "type": "cidr", + "name": "peers" + } +} `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "endpoint_group": { + "description": "", + "tenant_id": "4ad57e7ce0b24fca8f12b9834d91079d", + "project_id": "4ad57e7ce0b24fca8f12b9834d91079d", + "endpoints": [ + "10.2.0.0/24", + "10.3.0.0/24" + ], + "type": "cidr", + "id": "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", + "name": "peers" + } +} + `) + }) + + options := endpointgroups.CreateOpts{ + Name: "peers", + Type: endpointgroups.TypeCIDR, + Endpoints: []string{ + "10.2.0.0/24", + "10.3.0.0/24", + }, + } + actual, err := endpointgroups.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + expected := endpointgroups.EndpointGroup{ + Name: "peers", + TenantID: "4ad57e7ce0b24fca8f12b9834d91079d", + ProjectID: "4ad57e7ce0b24fca8f12b9834d91079d", + ID: "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", + Description: "", + Endpoints: []string{ + "10.2.0.0/24", + "10.3.0.0/24", + }, + Type: "cidr", + } + th.AssertDeepEquals(t, expected, *actual) +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/urls.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/urls.go new file mode 100644 index 0000000000..9e83563ce1 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/urls.go @@ -0,0 +1,16 @@ +package endpointgroups + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "vpn" + resourcePath = "endpoint-groups" +) + +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 32083cdce4c64eff52ec57f82b2d3b522d1ba48a Mon Sep 17 00:00:00 2001 From: Colin Nolan Date: Fri, 2 Mar 2018 19:53:23 +0000 Subject: [PATCH 0233/2296] Corrects example to create a server with a key pair (#799) --- openstack/compute/v2/extensions/keypairs/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go index dc7b65fda1..24c4607722 100644 --- a/openstack/compute/v2/extensions/keypairs/doc.go +++ b/openstack/compute/v2/extensions/keypairs/doc.go @@ -58,7 +58,7 @@ Example to Create a Server With a Key Pair FlavorRef: "flavor-uuid", } - createOpts := keypairs.CreateOpts{ + createOpts := keypairs.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, KeyName: "keypair-name", } From f7e32fe391ee0d82498d64d244443936604f0e5d Mon Sep 17 00:00:00 2001 From: Fabian Ruff Date: Mon, 5 Mar 2018 17:52:58 +0100 Subject: [PATCH 0234/2296] Add support for custom 403 error handling (#714) * Add support for custom 403 error handling * Include full response body in error string --- errors.go | 18 ++++++++++++++++++ provider_client.go | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/errors.go b/errors.go index 88fd2ac676..2466932efe 100644 --- a/errors.go +++ b/errors.go @@ -72,6 +72,11 @@ type ErrDefault401 struct { ErrUnexpectedResponseCode } +// ErrDefault403 is the default error type returned on a 403 HTTP response code. +type ErrDefault403 struct { + ErrUnexpectedResponseCode +} + // ErrDefault404 is the default error type returned on a 404 HTTP response code. type ErrDefault404 struct { ErrUnexpectedResponseCode @@ -108,6 +113,13 @@ func (e ErrDefault400) Error() string { func (e ErrDefault401) Error() string { return "Authentication failed" } +func (e ErrDefault403) Error() string { + e.DefaultErrString = fmt.Sprintf( + "Request forbidden: [%s %s], error message: %s", + e.Method, e.URL, e.Body, + ) + return e.choseErrString() +} func (e ErrDefault404) Error() string { return "Resource not found" } @@ -141,6 +153,12 @@ type Err401er interface { Error401(ErrUnexpectedResponseCode) error } +// Err403er is the interface resource error types implement to override the error message +// from a 403 error. +type Err403er interface { + Error403(ErrUnexpectedResponseCode) error +} + // Err404er is the interface resource error types implement to override the error message // from a 404 error. type Err404er interface { diff --git a/provider_client.go b/provider_client.go index 72daeb0a3e..e93c236e17 100644 --- a/provider_client.go +++ b/provider_client.go @@ -298,6 +298,11 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) if error401er, ok := errType.(Err401er); ok { err = error401er.Error401(respErr) } + case http.StatusForbidden: + err = ErrDefault403{respErr} + if error403er, ok := errType.(Err403er); ok { + err = error403er.Error403(respErr) + } case http.StatusNotFound: err = ErrDefault404{respErr} if error404er, ok := errType.(Err404er); ok { From aa1f387e28bd82a1d4294b3ea48d7bf274437820 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Tue, 6 Mar 2018 02:27:45 +0100 Subject: [PATCH 0235/2296] Vpnaas: List IKE policies (#797) * Added list function for IKE policies(with unit and acceptance tests * Added documentation for IKE policy list function --- .../v2/extensions/vpnaas/ikepolicy_test.go | 21 ++++++ .../v2/extensions/vpnaas/ipsecpolicy_test.go | 2 +- .../v2/extensions/vpnaas/ikepolicies/doc.go | 14 ++++ .../extensions/vpnaas/ikepolicies/requests.go | 50 ++++++++++++- .../extensions/vpnaas/ikepolicies/results.go | 42 ++++++++++- .../ikepolicies/testing/requests_test.go | 75 +++++++++++++++++++ 6 files changed, 201 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index c411ef68ec..891eea81f5 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -10,6 +10,27 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" ) +func TestIKEPolicyList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := ikepolicies.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list IKE policies: %v", err) + } + + allPolicies, err := ikepolicies.ExtractPolicies(allPages) + if err != nil { + t.Fatalf("Unable to extract IKE policies: %v", err) + } + + for _, policy := range allPolicies { + tools.PrintResource(t, policy) + } +} + func TestIKEPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index 4a9905bb93..87f3aa490f 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" ) -func TestPolicyList(t *testing.T) { +func TestIPSecPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index 94fdbdd485..23630a17f4 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -24,6 +24,7 @@ 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() @@ -31,5 +32,18 @@ Example to Delete a Policy panic(err) } + +Example to List IKE policies + + allPages, err := ikepolicies.List(client, nil).AllPages() + if err != nil { + panic(err) + } + + allPolicies, err := ikepolicies.ExtractPolicies(allPages) + if err != nil { + panic(err) + } + */ package ikepolicies diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go index 89c73c7e4e..f6dfcdd46f 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -1,6 +1,9 @@ package ikepolicies -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) type AuthAlgorithm string type EncryptionAlgorithm string @@ -118,3 +121,48 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToPolicyListQuery() (string, error) +} + +// ListOpts allows the filtering of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the IKE policy attributes you want to see returned. +type ListOpts struct { + TenantID string `q:"tenant_id"` + Name string `q:"name"` + Description string `q:"description"` + ProjectID string `q:"project_id"` + AuthAlgorithm string `q:"auth_algorithm"` + EncapsulationMode string `q:"encapsulation_mode"` + EncryptionAlgorithm string `q:"encryption_algorithm"` + PFS string `q:"pfs"` + Phase1NegotiationMode string `q:"phase_1_negotiation_mode"` + IKEVersion string `q:"ike_version"` +} + +// ToPolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// IKE policies. It accepts a ListOpts struct, which allows you to filter +// the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToPolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return PolicyPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go index eb5bd8857b..d298bc53a5 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go @@ -1,6 +1,9 @@ package ikepolicies -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) // Policy is an IKE Policy type Policy struct { @@ -60,6 +63,43 @@ func (r commonResult) Extract() (*Policy, error) { return s.Policy, err } +// PolicyPage is the page returned by a pager when traversing over a +// collection of Policies. +type PolicyPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of IKE policies 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 PolicyPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"ikepolicies_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a PolicyPage struct is empty. +func (r PolicyPage) IsEmpty() (bool, error) { + is, err := ExtractPolicies(r) + return len(is) == 0, err +} + +// ExtractPolicies accepts a Page struct, specifically a Policy struct, +// and extracts the elements into a slice of Policy structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractPolicies(r pagination.Page) ([]Policy, error) { + var s struct { + Policies []Policy `json:"ikepolicies"` + } + err := (r.(PolicyPage)).ExtractInto(&s) + return s.Policies, err +} + // CreateResult represents the result of a Create operation. Call its Extract method to // interpret it as a Policy. type CreateResult struct { diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index f5f1f9dc9b..8f32432f94 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -7,6 +7,7 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -152,3 +153,77 @@ func TestDelete(t *testing.T) { res := ikepolicies.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ikepolicies", 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, ` + { + "ikepolicies": [ + { + "name": "policy", + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "project_id": "9145d91459d248b1b02fdaca97c6a75d", + "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "description": "IKE policy", + "auth_algorithm": "sha1", + "encryption_algorithm": "aes-128", + "pfs": "Group5", + "lifetime": { + "value": 3600, + "units": "seconds" + }, + "phase1_negotiation_mode": "main", + "ike_version": "v2" + } + ] +} + `) + }) + + count := 0 + + ikepolicies.List(fake.ServiceClient(), ikepolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ikepolicies.ExtractPolicies(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + expectedLifetime := ikepolicies.Lifetime{ + Units: "seconds", + Value: 3600, + } + expected := []ikepolicies.Policy{ + { + AuthAlgorithm: "sha1", + IKEVersion: "v2", + TenantID: "9145d91459d248b1b02fdaca97c6a75d", + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", + Phase1NegotiationMode: "main", + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Lifetime: expectedLifetime, + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} From 8eb2fcc5bd524d966c59efb5db1cbbdc8c6a82fd Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Tue, 6 Mar 2018 02:30:03 +0100 Subject: [PATCH 0236/2296] Vpnaas: Get Endpoint Group (#798) * Added endpointgroup get operation * Fixed wrong constant in doc --- .../v2/extensions/vpnaas/group_test.go | 9 +++- .../extensions/vpnaas/endpointgroups/doc.go | 9 +++- .../vpnaas/endpointgroups/requests.go | 6 +++ .../vpnaas/endpointgroups/results.go | 6 +++ .../endpointgroups/testing/requests_test.go | 46 +++++++++++++++++++ 5 files changed, 74 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index 7305fcf8c6..ce80011be1 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" ) func TestGroupCRUD(t *testing.T) { @@ -19,6 +20,12 @@ func TestGroupCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create Endpoint group: %v", err) } - tools.PrintResource(t, group) + + newGroup, err := endpointgroups.Get(client, group.ID).Extract() + if err != nil { + t.Fatalf("Unable to retrieve Endpoint group: %v", err) + } + tools.PrintResource(t, newGroup) + } diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go index 3915124555..bdd986bdc9 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go @@ -5,7 +5,7 @@ Example to create an Endpoint Group createOpts := endpointgroups.CreateOpts{ Name: groupName, - Type: endpointgroups.TypeCidr, + Type: endpointgroups.TypeCIDR, Endpoints: []string{ "10.2.0.0/24", "10.3.0.0/24", @@ -15,5 +15,12 @@ Example to create an Endpoint Group if err != nil { return group, err } + +Example to retrieve an Endpoint Group + + group, err := endpointgroups.Get(client, "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a").Extract() + if err != nil { + panic(err) + } */ package endpointgroups diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go index 47b9855894..5279f26aca 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -56,3 +56,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } + +// Get retrieves a particular endpoint group based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go index c4a6ade334..6804d53757 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go @@ -46,3 +46,9 @@ func (r commonResult) Extract() (*EndpointGroup, error) { type CreateResult struct { commonResult } + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as an EndpointGroup. +type GetResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go index f4fe3eb634..ba8d33c458 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -76,3 +76,49 @@ func TestCreate(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` +{ + "endpoint_group": { + "description": "", + "tenant_id": "4ad57e7ce0b24fca8f12b9834d91079d", + "project_id": "4ad57e7ce0b24fca8f12b9834d91079d", + "endpoints": [ + "10.2.0.0/24", + "10.3.0.0/24" + ], + "type": "cidr", + "id": "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", + "name": "peers" + } +} + `) + }) + + actual, err := endpointgroups.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + th.AssertNoErr(t, err) + expected := endpointgroups.EndpointGroup{ + Name: "peers", + TenantID: "4ad57e7ce0b24fca8f12b9834d91079d", + ProjectID: "4ad57e7ce0b24fca8f12b9834d91079d", + ID: "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", + Description: "", + Endpoints: []string{ + "10.2.0.0/24", + "10.3.0.0/24", + }, + Type: "cidr", + } + th.AssertDeepEquals(t, expected, *actual) +} From 9b604ddaf1a607170b72aa50c34e1977edffbfea Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Tue, 6 Mar 2018 02:36:37 +0100 Subject: [PATCH 0237/2296] Vpnaas: Update IPSec Policy (#774) * Added update methods to requests.go * Added updateresult to results.go * Added unit test for update function * Added acceptance test * fixed typo * Changed name and description to pointers in updateOpts * Adjusted the pointers for updateOpts * fixed indentation and log message --- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 10 +++ .../vpnaas/ipsecpolicies/requests.go | 41 ++++++++++ .../vpnaas/ipsecpolicies/results.go | 6 ++ .../ipsecpolicies/testing/requests_test.go | 78 +++++++++++++++++++ 4 files changed, 135 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index 87f3aa490f..2fdee7dd92 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -42,7 +42,17 @@ func TestIPSecPolicyCRUD(t *testing.T) { t.Fatalf("Unable to create IPSec policy: %v", err) } defer DeleteIPSecPolicy(t, client, policy.ID) + tools.PrintResource(t, policy) + + updatedDescription := "Updated policy description" + updateOpts := ipsecpolicies.UpdateOpts{ + Description: &updatedDescription, + } + policy, err = ipsecpolicies.Update(client, policy.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update IPSec policy: %v", err) + } tools.PrintResource(t, policy) newPolicy, err := ipsecpolicies.Get(client, policy.ID).Extract() diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go index d7c2ddc45b..9496365ca4 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -168,3 +168,44 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { return PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToPolicyUpdateMap() (map[string]interface{}, error) +} + +type LifetimeUpdateOpts struct { + Units Unit `json:"units,omitempty"` + Value int `json:"value,omitempty"` +} + +// UpdateOpts contains the values used when updating an IPSec policy +type UpdateOpts struct { + Description *string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` + AuthAlgorithm AuthAlgorithm `json:"auth_algorithm,omitempty"` + EncapsulationMode EncapsulationMode `json:"encapsulation_mode,omitempty"` + EncryptionAlgorithm EncryptionAlgorithm `json:"encryption_algorithm,omitempty"` + PFS PFS `json:"pfs,omitempty"` + TransformProtocol TransformProtocol `json:"transform_protocol,omitempty"` + Lifetime *LifetimeUpdateOpts `json:"lifetime,omitempty"` +} + +// ToPolicyUpdateMap casts an UpdateOpts struct to a map. +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "ipsecpolicy") +} + +// Update allows IPSec policies to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToPolicyUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go index 780ecbab8d..eda4a1bd23 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go @@ -118,3 +118,9 @@ func ExtractPolicies(r pagination.Page) ([]Policy, error) { err := (r.(PolicyPage)).ExtractInto(&s) return s.Policies, err } + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Policy. +type UpdateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go index d6148b59d6..702bd38355 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -241,3 +241,81 @@ func TestList(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsecpolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` + { + "ipsecpolicy":{ + "name": "updatedname", + "description": "updated policy", + "lifetime": { + "value": 7000 + } + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + + { + "ipsecpolicy": { + "name": "updatedname", + "transform_protocol": "esp", + "auth_algorithm": "sha1", + "encapsulation_mode": "tunnel", + "encryption_algorithm": "aes-128", + "pfs": "group5", + "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "lifetime": { + "units": "seconds", + "value": 7000 + }, + "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "description": "updated policy" + } +} +`) + }) + updatedName := "updatedname" + updatedDescription := "updated policy" + options := ipsecpolicies.UpdateOpts{ + Name: &updatedName, + Description: &updatedDescription, + Lifetime: &ipsecpolicies.LifetimeUpdateOpts{ + Value: 7000, + }, + } + + actual, err := ipsecpolicies.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + th.AssertNoErr(t, err) + expectedLifetime := ipsecpolicies.Lifetime{ + Units: "seconds", + Value: 7000, + } + expected := ipsecpolicies.Policy{ + TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", + ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", + Name: "updatedname", + TransformProtocol: "esp", + AuthAlgorithm: "sha1", + EncapsulationMode: "tunnel", + EncryptionAlgorithm: "aes-128", + PFS: "group5", + Description: "updated policy", + Lifetime: expectedLifetime, + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + } + th.AssertDeepEquals(t, expected, *actual) +} From d7d64b9b261b431428e50b460c2b52dea74c999f Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 6 Mar 2018 05:57:07 +0300 Subject: [PATCH 0238/2296] Add networking port extra DHCP opts Update call (#804) * Add networking port extra DHCP opts GET support Add a new networking v2 extension to provide extra DHCP configuration support for ports. Add basic structures with a unit test. * Fix networking port extra DHCP structs names Use "ExtraDHCPOptsExt" and "ExtraDHCPOpts" for basic structs names of the extra dhcp options extenstion. Upgrade unit-test and fix spacing in the unit test fixture. * Move network port extra DHCP tests Move the extra DHCP options GET unit test to the standard networking port package. * Add networking port extra DHCP opts Create call Add a Create request to the networking v2 extra DHCP options extension. Add unit and acceptance tests. * Move network port extra DHCP creation tests Move the extra DHCP options Create call unit and acceptance tests to the standard networking port package. * Fix inline comments for the extra DHCP creation Rename comment for the CreateOptsExt.ExtraDHCPOpts field. * Add networking port extra DHCP opts Update call Add an Update request to the networking v2 extra DHCP options extension. Add unit and acceptance tests. --- .../openstack/networking/v2/networking.go | 50 ++++++ .../openstack/networking/v2/ports_test.go | 54 ++++++ .../v2/extensions/extradhcpopts/doc.go | 75 ++++++++ .../v2/extensions/extradhcpopts/requests.go | 75 ++++++++ .../v2/extensions/extradhcpopts/results.go | 32 ++++ .../networking/v2/ports/testing/fixtures.go | 139 +++++++++++++++ .../v2/ports/testing/requests_test.go | 168 ++++++++++++++++++ 7 files changed, 593 insertions(+) create mode 100644 openstack/networking/v2/extensions/extradhcpopts/doc.go create mode 100644 openstack/networking/v2/extensions/extradhcpopts/requests.go create mode 100644 openstack/networking/v2/extensions/extradhcpopts/results.go diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index e889267674..e8bd67c046 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" @@ -336,3 +337,52 @@ func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string, secs return false, nil }) } + +// PortWithDHCPOpts represents a port with extra DHCP options configuration. +type PortWithDHCPOpts struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt +} + +// CreatePortWithDHCPOpts will create a port with DHCP options on the specified subnet. +// An error will be returned if the port could not be created. +func CreatePortWithDHCPOpts(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*PortWithDHCPOpts, error) { + portName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create port: %s", portName) + + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + Name: portName, + AdminStateUp: gophercloud.Enabled, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + } + createOpts := extradhcpopts.CreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + { + OptName: "test_option_1", + OptValue: "test_value_1", + }, + }, + } + port := &PortWithDHCPOpts{} + + err := ports.Create(client, createOpts).ExtractInto(port) + if err != nil { + return nil, err + } + + if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + return nil, err + } + + err = ports.Get(client, port.ID).ExtractInto(port) + if err != nil { + return port, err + } + + t.Logf("Successfully created port: %s", portName) + + return port, nil +} diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 89ac3b5382..6466aadfb0 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -8,6 +8,7 @@ import ( "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/openstack/networking/v2/extensions/extradhcpopts" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) @@ -388,3 +389,56 @@ func TestPortsPortSecurityCRUD(t *testing.T) { tools.PrintResource(t, portWithExt) } + +func TestPortsWithDHCPOptsCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create a Network + network, err := CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create a network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + // Create a Subnet + subnet, err := CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create a subnet: %v", err) + } + defer DeleteSubnet(t, client, subnet.ID) + + // Create a port with extra DHCP options. + port, err := CreatePortWithDHCPOpts(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create a port: %v", err) + } + defer DeletePort(t, client, port.ID) + + tools.PrintResource(t, port) + + // Update the port with extra DHCP options. + newPortName := tools.RandomString("TESTACC-", 8) + portUpdateOpts := ports.UpdateOpts{ + Name: newPortName, + } + updateOpts := extradhcpopts.UpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + { + OptName: "test_option_2", + OptValue: "test_value_2", + }, + }, + } + + newPort := &PortWithDHCPOpts{} + err = ports.Update(client, port.ID, updateOpts).ExtractInto(newPort) + if err != nil { + t.Fatalf("Could not update port: %v", err) + } + + tools.PrintResource(t, newPort) +} diff --git a/openstack/networking/v2/extensions/extradhcpopts/doc.go b/openstack/networking/v2/extensions/extradhcpopts/doc.go new file mode 100644 index 0000000000..d36b9025c1 --- /dev/null +++ b/openstack/networking/v2/extensions/extradhcpopts/doc.go @@ -0,0 +1,75 @@ +/* +Package extradhcpopts allow to work with extra DHCP functionality of Neutron ports. + +Example to Get a Port with DHCP opts + + portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + var s struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt + } + + err := ports.Get(networkClient, portID).ExtractInto(&s) + if err != nil { + panic(err) + } + +Example to Create a Port with DHCP opts + + adminStateUp := true + portCreateOpts := ports.CreateOpts{ + Name: "dhcp-conf-port", + AdminStateUp: &adminStateUp, + NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }, + } + createOpts := extradhcpopts.CreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + { + OptName: "optionA", + OptValue: "valueA", + }, + }, + } + var s struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt + } + + err := ports.Create(networkClient, createOpts).ExtractInto(&s) + if err != nil { + panic(err) + } + +Example to Update a Port with DHCP opts + + portUpdateOpts := ports.UpdateOpts{ + Name: "updated-dhcp-conf-port", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }, + } + updateOpts := extradhcpopts.UpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + { + OptName: "optionB", + OptValue: "valueB", + }, + }, + } + var s struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt + } + portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + + err := ports.Update(networkClient, portID, updateOpts).ExtractInto(&s) + if err != nil { + panic(err) + } +*/ +package extradhcpopts diff --git a/openstack/networking/v2/extensions/extradhcpopts/requests.go b/openstack/networking/v2/extensions/extradhcpopts/requests.go new file mode 100644 index 0000000000..ead0950312 --- /dev/null +++ b/openstack/networking/v2/extensions/extradhcpopts/requests.go @@ -0,0 +1,75 @@ +package extradhcpopts + +import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" +) + +// CreateOptsExt adds port DHCP options to the base ports.CreateOpts. +type CreateOptsExt struct { + // CreateOptsBuilder is the interface options structs have to satisfy in order + // to be used in the main Create operation in this package. + ports.CreateOptsBuilder + + // ExtraDHCPOpts field is a set of DHCP options for a single port. + ExtraDHCPOpts []ExtraDHCPOpts `json:"extra_dhcp_opts,omitempty"` +} + +// ToPortCreateMap casts a CreateOptsExt struct to a map. +func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToPortCreateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]interface{}) + + // Convert opts.DHCPOpts to a slice of maps. + if opts.ExtraDHCPOpts != nil { + extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) + for i, opt := range opts.ExtraDHCPOpts { + extraDHCPOptMap, err := opt.ToMap() + if err != nil { + return nil, err + } + extraDHCPOpts[i] = extraDHCPOptMap + } + port["extra_dhcp_opts"] = extraDHCPOpts + } + + return base, nil +} + +// UpdateOptsExt adds port DHCP options to the base ports.UpdateOpts +type UpdateOptsExt struct { + // UpdateOptsBuilder is the interface options structs have to satisfy in order + // to be used in the main Update operation in this package. + ports.UpdateOptsBuilder + + // ExtraDHCPOpts field is a set of DHCP options for a single port. + ExtraDHCPOpts []ExtraDHCPOpts `json:"extra_dhcp_opts,omitempty"` +} + +// ToPortUpdateMap casts an UpdateOpts struct to a map. +func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]interface{}) + + // Convert opts.ExtraDHCPOpts to a slice of maps. + if opts.ExtraDHCPOpts != nil { + extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) + for i, opt := range opts.ExtraDHCPOpts { + extraDHCPOptMap, err := opt.ToMap() + if err != nil { + return nil, err + } + extraDHCPOpts[i] = extraDHCPOptMap + } + port["extra_dhcp_opts"] = extraDHCPOpts + } + + return base, nil +} diff --git a/openstack/networking/v2/extensions/extradhcpopts/results.go b/openstack/networking/v2/extensions/extradhcpopts/results.go new file mode 100644 index 0000000000..0a79ebd81c --- /dev/null +++ b/openstack/networking/v2/extensions/extradhcpopts/results.go @@ -0,0 +1,32 @@ +package extradhcpopts + +import "github.com/gophercloud/gophercloud" + +// ExtraDHCPOptsExt is a struct that contains different DHCP options for a single port. +type ExtraDHCPOptsExt struct { + ExtraDHCPOpts []ExtraDHCPOpts `json:"extra_dhcp_opts"` +} + +// ExtraDHCPOpts represents a single set of extra DHCP options for a single port. +type ExtraDHCPOpts struct { + // Name is the name of a single DHCP option. + OptName string `json:"opt_name"` + + // Value is the value of a single DHCP option. + OptValue string `json:"opt_value"` + + // IPVersion is the IP protocol version of a single DHCP option. + // Valid value is 4 or 6. Default is 4. + IPVersion int `json:"ip_version,omitempty"` +} + +// ToMap is a helper function to convert an individual DHCPOpts structure +// into a sub-map. +func (opts ExtraDHCPOpts) ToMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return b, nil +} diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index d8e57cbbae..96d70a706d 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -520,3 +520,142 @@ const DontUpdateAllowedAddressPairsResponse = ` } } ` + +// GetWithDHCPOptsResponse represents a raw port response with extra DHCP options. +const GetWithDHCPOptsResponse = ` +{ + "port": { + "status": "ACTIVE", + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "extra_dhcp_opts": [ + { + "opt_name": "option1", + "opt_value": "value1", + "ip_version": 4 + }, + { + "opt_name": "option2", + "opt_value": "value2", + "ip_version": 4 + } + ], + "admin_state_up": true, + "name": "port-with-extra-dhcp-opts", + "device_owner": "", + "mac_address": "fa:16:3e:c9:cb:f0", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.4" + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "device_id": "" + } +} +` + +// CreateWithDHCPOptsRequest represents a raw port creation request with extra DHCP options. +const CreateWithDHCPOptsRequest = ` +{ + "port": { + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "name": "port-with-extra-dhcp-opts", + "admin_state_up": true, + "fixed_ips": [ + { + "ip_address": "10.0.0.2", + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2" + } + ], + "extra_dhcp_opts": [ + { + "opt_name": "option1", + "opt_value": "value1" + } + ] + } +} +` + +// CreateWithDHCPOptsResponse represents a raw port creation response with extra DHCP options. +const CreateWithDHCPOptsResponse = ` +{ + "port": { + "status": "DOWN", + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "extra_dhcp_opts": [ + { + "opt_name": "option1", + "opt_value": "value1", + "ip_version": 4 + } + ], + "admin_state_up": true, + "name": "port-with-extra-dhcp-opts", + "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", + "device_id": "" + } +} +` + +// UpdateWithDHCPOptsRequest represents a raw port update request with extra DHCP options. +const UpdateWithDHCPOptsRequest = ` +{ + "port": { + "name": "updated-port-with-dhcp-opts", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.3" + } + ], + "extra_dhcp_opts": [ + { + "opt_name": "option2", + "opt_value": "value2" + } + ] + } +} +` + +// UpdateWithDHCPOptsResponse represents a raw port update response with extra DHCP options. +const UpdateWithDHCPOptsResponse = ` +{ + "port": { + "status": "DOWN", + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "extra_dhcp_opts": [ + { + "opt_name": "option2", + "opt_value": "value2", + "ip_version": 4 + } + ], + "admin_state_up": true, + "name": "updated-port-with-dhcp-opts", + "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" + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "device_id": "" + } +} +` diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 87a18864d4..a58badb08e 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -6,6 +6,7 @@ import ( "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "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" "github.com/gophercloud/gophercloud/pagination" @@ -569,3 +570,170 @@ func TestDelete(t *testing.T) { res := ports.Delete(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertNoErr(t, res.Err) } + +func TestGetWithDHCPOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", 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, GetWithDHCPOptsResponse) + }) + + var s struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt + } + + err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, "ACTIVE") + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + {OptName: "option1", OptValue: "value1", IPVersion: 4}, + {OptName: "option2", OptValue: "value2", IPVersion: 4}, + }, + }) + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.Name, "port-with-extra-dhcp-opts") + th.AssertEquals(t, s.DeviceOwner, "") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.4"}, + }) + th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, s.DeviceID, "") +} + +func TestCreateWithDHCPOpts(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, CreateWithDHCPOptsRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, CreateWithDHCPOptsResponse) + }) + + adminStateUp := true + portCreateOpts := ports.CreateOpts{ + Name: "port-with-extra-dhcp-opts", + AdminStateUp: &adminStateUp, + NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }, + } + + createOpts := extradhcpopts.CreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + { + OptName: "option1", + OptValue: "value1", + }, + }, + } + + var s struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt + } + + err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, "DOWN") + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + {OptName: "option1", OptValue: "value1", IPVersion: 4}, + }, + }) + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.Name, "port-with-extra-dhcp-opts") + th.AssertEquals(t, s.DeviceOwner, "") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }) + th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, s.DeviceID, "") +} + +func TestUpdateWithDHCPOpts(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, UpdateWithDHCPOptsRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateWithDHCPOptsResponse) + }) + + portUpdateOpts := ports.UpdateOpts{ + Name: "updated-port-with-dhcp-opts", + FixedIPs: []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }, + } + + updateOpts := extradhcpopts.UpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + { + OptName: "option2", + OptValue: "value2", + }, + }, + } + + var s struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt + } + + err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, "DOWN") + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + {OptName: "option2", OptValue: "value2", IPVersion: 4}, + }, + }) + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.Name, "updated-port-with-dhcp-opts") + th.AssertEquals(t, s.DeviceOwner, "") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }) + th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, s.DeviceID, "") +} From 282b17ffd963a1a5e81c3b2c9f52622ad33612a8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 6 Mar 2018 02:49:42 +0000 Subject: [PATCH 0239/2296] Rename ExtraDHCPOpts to ExtraDHCPOpt. Doc cleanup --- .../openstack/networking/v2/networking.go | 14 +++++----- .../openstack/networking/v2/ports_test.go | 8 +++--- .../v2/extensions/extradhcpopts/doc.go | 10 +++---- .../v2/extensions/extradhcpopts/requests.go | 10 +++---- .../v2/extensions/extradhcpopts/results.go | 13 +++++----- .../networking/v2/ports/testing/fixtures.go | 25 +++++++++++------- .../v2/ports/testing/requests_test.go | 26 +++++++++---------- 7 files changed, 56 insertions(+), 50 deletions(-) diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index e8bd67c046..58a59f48ae 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -338,15 +338,15 @@ func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string, secs }) } -// PortWithDHCPOpts represents a port with extra DHCP options configuration. -type PortWithDHCPOpts struct { +// PortWithExtraDHCPOpts represents a port with extra DHCP options configuration. +type PortWithExtraDHCPOpts struct { ports.Port extradhcpopts.ExtraDHCPOptsExt } -// CreatePortWithDHCPOpts will create a port with DHCP options on the specified subnet. -// An error will be returned if the port could not be created. -func CreatePortWithDHCPOpts(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*PortWithDHCPOpts, error) { +// CreatePortWithExtraDHCPOpts will create a port with DHCP options on the +// specified subnet. An error will be returned if the port could not be created. +func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*PortWithExtraDHCPOpts, error) { portName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create port: %s", portName) @@ -359,14 +359,14 @@ func CreatePortWithDHCPOpts(t *testing.T, client *gophercloud.ServiceClient, net } createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ { OptName: "test_option_1", OptValue: "test_value_1", }, }, } - port := &PortWithDHCPOpts{} + port := &PortWithExtraDHCPOpts{} err := ports.Create(client, createOpts).ExtractInto(port) if err != nil { diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 6466aadfb0..d47bc3a196 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -390,7 +390,7 @@ func TestPortsPortSecurityCRUD(t *testing.T) { tools.PrintResource(t, portWithExt) } -func TestPortsWithDHCPOptsCRUD(t *testing.T) { +func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -411,7 +411,7 @@ func TestPortsWithDHCPOptsCRUD(t *testing.T) { defer DeleteSubnet(t, client, subnet.ID) // Create a port with extra DHCP options. - port, err := CreatePortWithDHCPOpts(t, client, network.ID, subnet.ID) + port, err := CreatePortWithExtraDHCPOpts(t, client, network.ID, subnet.ID) if err != nil { t.Fatalf("Unable to create a port: %v", err) } @@ -426,7 +426,7 @@ func TestPortsWithDHCPOptsCRUD(t *testing.T) { } updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ { OptName: "test_option_2", OptValue: "test_value_2", @@ -434,7 +434,7 @@ func TestPortsWithDHCPOptsCRUD(t *testing.T) { }, } - newPort := &PortWithDHCPOpts{} + newPort := &PortWithExtraDHCPOpts{} err = ports.Update(client, port.ID, updateOpts).ExtractInto(newPort) if err != nil { t.Fatalf("Could not update port: %v", err) diff --git a/openstack/networking/v2/extensions/extradhcpopts/doc.go b/openstack/networking/v2/extensions/extradhcpopts/doc.go index d36b9025c1..487fdd6cbe 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/doc.go +++ b/openstack/networking/v2/extensions/extradhcpopts/doc.go @@ -1,7 +1,7 @@ /* Package extradhcpopts allow to work with extra DHCP functionality of Neutron ports. -Example to Get a Port with DHCP opts +Example to Get a Port with Extra DHCP Options portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" var s struct { @@ -14,7 +14,7 @@ Example to Get a Port with DHCP opts panic(err) } -Example to Create a Port with DHCP opts +Example to Create a Port with Extra DHCP Options adminStateUp := true portCreateOpts := ports.CreateOpts{ @@ -27,7 +27,7 @@ Example to Create a Port with DHCP opts } createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ { OptName: "optionA", OptValue: "valueA", @@ -44,7 +44,7 @@ Example to Create a Port with DHCP opts panic(err) } -Example to Update a Port with DHCP opts +Example to Update a Port with Extra DHCP Options portUpdateOpts := ports.UpdateOpts{ Name: "updated-dhcp-conf-port", @@ -54,7 +54,7 @@ Example to Update a Port with DHCP opts } updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ { OptName: "optionB", OptValue: "valueB", diff --git a/openstack/networking/v2/extensions/extradhcpopts/requests.go b/openstack/networking/v2/extensions/extradhcpopts/requests.go index ead0950312..9893f84d84 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/requests.go +++ b/openstack/networking/v2/extensions/extradhcpopts/requests.go @@ -4,14 +4,14 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) -// CreateOptsExt adds port DHCP options to the base ports.CreateOpts. +// CreateOptsExt adds extra DHCP options to the base ports.CreateOpts. type CreateOptsExt struct { // CreateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Create operation in this package. ports.CreateOptsBuilder // ExtraDHCPOpts field is a set of DHCP options for a single port. - ExtraDHCPOpts []ExtraDHCPOpts `json:"extra_dhcp_opts,omitempty"` + ExtraDHCPOpts []ExtraDHCPOpt `json:"extra_dhcp_opts,omitempty"` } // ToPortCreateMap casts a CreateOptsExt struct to a map. @@ -23,7 +23,7 @@ func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { port := base["port"].(map[string]interface{}) - // Convert opts.DHCPOpts to a slice of maps. + // Convert opts.ExtraDHCPOpts to a slice of maps. if opts.ExtraDHCPOpts != nil { extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) for i, opt := range opts.ExtraDHCPOpts { @@ -39,14 +39,14 @@ func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { return base, nil } -// UpdateOptsExt adds port DHCP options to the base ports.UpdateOpts +// UpdateOptsExt adds extra DHCP options to the base ports.UpdateOpts. type UpdateOptsExt struct { // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the main Update operation in this package. ports.UpdateOptsBuilder // ExtraDHCPOpts field is a set of DHCP options for a single port. - ExtraDHCPOpts []ExtraDHCPOpts `json:"extra_dhcp_opts,omitempty"` + ExtraDHCPOpts []ExtraDHCPOpt `json:"extra_dhcp_opts,omitempty"` } // ToPortUpdateMap casts an UpdateOpts struct to a map. diff --git a/openstack/networking/v2/extensions/extradhcpopts/results.go b/openstack/networking/v2/extensions/extradhcpopts/results.go index 0a79ebd81c..042d425a58 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/results.go +++ b/openstack/networking/v2/extensions/extradhcpopts/results.go @@ -2,13 +2,14 @@ package extradhcpopts import "github.com/gophercloud/gophercloud" -// ExtraDHCPOptsExt is a struct that contains different DHCP options for a single port. +// ExtraDHCPOptsExt is a struct that contains different DHCP options for a +// single port. type ExtraDHCPOptsExt struct { - ExtraDHCPOpts []ExtraDHCPOpts `json:"extra_dhcp_opts"` + ExtraDHCPOpts []ExtraDHCPOpt `json:"extra_dhcp_opts"` } -// ExtraDHCPOpts represents a single set of extra DHCP options for a single port. -type ExtraDHCPOpts struct { +// ExtraDHCPOpt represents a single set of extra DHCP options for a single port. +type ExtraDHCPOpt struct { // Name is the name of a single DHCP option. OptName string `json:"opt_name"` @@ -20,9 +21,9 @@ type ExtraDHCPOpts struct { IPVersion int `json:"ip_version,omitempty"` } -// ToMap is a helper function to convert an individual DHCPOpts structure +// ToMap is a helper function to convert an individual ExtraDHCPOpt structure // into a sub-map. -func (opts ExtraDHCPOpts) ToMap() (map[string]interface{}, error) { +func (opts ExtraDHCPOpt) ToMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index 96d70a706d..870572cb64 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -521,8 +521,9 @@ const DontUpdateAllowedAddressPairsResponse = ` } ` -// GetWithDHCPOptsResponse represents a raw port response with extra DHCP options. -const GetWithDHCPOptsResponse = ` +// GetWithExtraDHCPOptsResponse represents a raw port response with extra +// DHCP options. +const GetWithExtraDHCPOptsResponse = ` { "port": { "status": "ACTIVE", @@ -556,8 +557,9 @@ const GetWithDHCPOptsResponse = ` } ` -// CreateWithDHCPOptsRequest represents a raw port creation request with extra DHCP options. -const CreateWithDHCPOptsRequest = ` +// CreateWithExtraDHCPOptsRequest represents a raw port creation request +// with extra DHCP options. +const CreateWithExtraDHCPOptsRequest = ` { "port": { "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", @@ -579,8 +581,9 @@ const CreateWithDHCPOptsRequest = ` } ` -// CreateWithDHCPOptsResponse represents a raw port creation response with extra DHCP options. -const CreateWithDHCPOptsResponse = ` +// CreateWithExtraDHCPOptsResponse represents a raw port creation response +// with extra DHCP options. +const CreateWithExtraDHCPOptsResponse = ` { "port": { "status": "DOWN", @@ -609,8 +612,9 @@ const CreateWithDHCPOptsResponse = ` } ` -// UpdateWithDHCPOptsRequest represents a raw port update request with extra DHCP options. -const UpdateWithDHCPOptsRequest = ` +// UpdateWithExtraDHCPOptsRequest represents a raw port update request with +// extra DHCP options. +const UpdateWithExtraDHCPOptsRequest = ` { "port": { "name": "updated-port-with-dhcp-opts", @@ -630,8 +634,9 @@ const UpdateWithDHCPOptsRequest = ` } ` -// UpdateWithDHCPOptsResponse represents a raw port update response with extra DHCP options. -const UpdateWithDHCPOptsResponse = ` +// UpdateWithExtraDHCPOptsResponse represents a raw port update response with +// extra DHCP options. +const UpdateWithExtraDHCPOptsResponse = ` { "port": { "status": "DOWN", diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index a58badb08e..7df21c7a35 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -571,7 +571,7 @@ func TestDelete(t *testing.T) { th.AssertNoErr(t, res.Err) } -func TestGetWithDHCPOpts(t *testing.T) { +func TestGetWithExtraDHCPOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -582,7 +582,7 @@ func TestGetWithDHCPOpts(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetWithDHCPOptsResponse) + fmt.Fprintf(w, GetWithExtraDHCPOptsResponse) }) var s struct { @@ -597,7 +597,7 @@ func TestGetWithDHCPOpts(t *testing.T) { th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ {OptName: "option1", OptValue: "value1", IPVersion: 4}, {OptName: "option2", OptValue: "value2", IPVersion: 4}, }, @@ -613,7 +613,7 @@ func TestGetWithDHCPOpts(t *testing.T) { th.AssertEquals(t, s.DeviceID, "") } -func TestCreateWithDHCPOpts(t *testing.T) { +func TestCreateWithExtraDHCPOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -622,12 +622,12 @@ func TestCreateWithDHCPOpts(t *testing.T) { 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, CreateWithDHCPOptsRequest) + th.TestJSONRequest(t, r, CreateWithExtraDHCPOptsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateWithDHCPOptsResponse) + fmt.Fprintf(w, CreateWithExtraDHCPOptsResponse) }) adminStateUp := true @@ -642,7 +642,7 @@ func TestCreateWithDHCPOpts(t *testing.T) { createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ { OptName: "option1", OptValue: "value1", @@ -662,7 +662,7 @@ func TestCreateWithDHCPOpts(t *testing.T) { th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ {OptName: "option1", OptValue: "value1", IPVersion: 4}, }, }) @@ -677,7 +677,7 @@ func TestCreateWithDHCPOpts(t *testing.T) { th.AssertEquals(t, s.DeviceID, "") } -func TestUpdateWithDHCPOpts(t *testing.T) { +func TestUpdateWithExtraDHCPOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -686,12 +686,12 @@ func TestUpdateWithDHCPOpts(t *testing.T) { 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, UpdateWithDHCPOptsRequest) + th.TestJSONRequest(t, r, UpdateWithExtraDHCPOptsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateWithDHCPOptsResponse) + fmt.Fprintf(w, UpdateWithExtraDHCPOptsResponse) }) portUpdateOpts := ports.UpdateOpts{ @@ -703,7 +703,7 @@ func TestUpdateWithDHCPOpts(t *testing.T) { updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ { OptName: "option2", OptValue: "value2", @@ -723,7 +723,7 @@ func TestUpdateWithDHCPOpts(t *testing.T) { th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpts{ + ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ {OptName: "option2", OptValue: "value2", IPVersion: 4}, }, }) From fe864ba585e153c296567b9ab4bbb1345d05900a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 5 Mar 2018 21:08:46 -0700 Subject: [PATCH 0240/2296] Image Service v2: Add Missing Fields to ListOpts (#780) * Add date filtering fields to list operation * Changing Tag to Tags * adding container_format and disk_format fields to list operation * fix FilterNE/FilterEQ typo * Add comment about filter behavior when no filter is applied * Add ID to ListOpts * Change status to a string * Add descriptive comments about the in operator * Re-add comment that was accidentaly removed * This commit makes the following changes: * Changes the ListOpts.Status type back to images.ImageStatus * Renames the ListOpts.CreatedAt field to CreatedAtQuery * Renames the ListOpts.UpdatedAt field to UpdatedAtQuery --- .../openstack/imageservice/v2/images_test.go | 89 +++++++++++++++++++ .../openstack/imageservice/v2/imageservice.go | 1 + openstack/imageservice/v2/images/requests.go | 55 +++++++++++- .../v2/images/testing/fixtures.go | 41 +++++++++ .../v2/images/testing/requests_test.go | 88 ++++++++++++++++++ openstack/imageservice/v2/images/types.go | 25 ++++++ 6 files changed, 298 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/acceptance/openstack/imageservice/v2/images_test.go index c2a8987319..04926109f6 100644 --- a/acceptance/openstack/imageservice/v2/images_test.go +++ b/acceptance/openstack/imageservice/v2/images_test.go @@ -4,6 +4,7 @@ package v2 import ( "testing" + "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" @@ -78,3 +79,91 @@ func TestImagesCreateDestroyEmptyImage(t *testing.T) { tools.PrintResource(t, image) } + +func TestImagesListByDate(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + if err != nil { + t.Fatalf("Unable to create an image service client: %v", err) + } + + date := time.Date(2014, 1, 1, 1, 1, 1, 0, time.UTC) + listOpts := images.ListOpts{ + Limit: 1, + CreatedAt: &images.ImageDateQuery{ + Date: date, + Filter: images.FilterGTE, + }, + } + + allPages, err := images.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve all images: %v", err) + } + + allImages, err := images.ExtractImages(allPages) + if err != nil { + t.Fatalf("Unable to extract images: %v", err) + } + + for _, image := range allImages { + tools.PrintResource(t, image) + tools.PrintResource(t, image.Properties) + } + + date = time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) + listOpts = images.ListOpts{ + Limit: 1, + CreatedAt: &images.ImageDateQuery{ + Date: date, + Filter: images.FilterGTE, + }, + } + + allPages, err = images.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve all images: %v", err) + } + + allImages, err = images.ExtractImages(allPages) + if err != nil { + t.Fatalf("Unable to extract images: %v", err) + } + + if len(allImages) > 0 { + t.Fatalf("Expected 0 images, got %d", len(allImages)) + } +} + +func TestImagesFilter(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + if err != nil { + t.Fatalf("Unable to create an image service client: %v", err) + } + + image, err := CreateEmptyImage(t, client) + if err != nil { + t.Fatalf("Unable to create empty image: %v", err) + } + + defer DeleteImage(t, client, image) + + listOpts := images.ListOpts{ + Tags: []string{"foo", "bar"}, + ContainerFormat: "bare", + DiskFormat: "qcow2", + } + + allPages, err := images.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve all images: %v", err) + } + + allImages, err := images.ExtractImages(allPages) + if err != nil { + t.Fatalf("Unable to extract images: %v", err) + } + + if len(allImages) == 0 { + t.Fatalf("Query resulted in no results") + } +} diff --git a/acceptance/openstack/imageservice/v2/imageservice.go b/acceptance/openstack/imageservice/v2/imageservice.go index 8aaeeb74b8..18af05ab3b 100644 --- a/acceptance/openstack/imageservice/v2/imageservice.go +++ b/acceptance/openstack/imageservice/v2/imageservice.go @@ -31,6 +31,7 @@ func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images. Properties: map[string]string{ "architecture": "x86_64", }, + Tags: []string{"foo", "bar", "baz"}, } image, err := images.Create(client, createOpts).Extract() diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 081262f1ff..88cd4d265e 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -1,6 +1,10 @@ package images import ( + "fmt" + "net/url" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -18,6 +22,11 @@ type ListOptsBuilder interface { // // http://developer.openstack.org/api-ref-image-v2.html type ListOpts struct { + // ID is the ID of the image. + // Multiple IDs can be specified by constructing a string + // such as "in:uuid1,uuid2,uuid3". + ID string `q:"id"` + // Integer value for the limit of values to return. Limit int `q:"limit"` @@ -25,6 +34,8 @@ type ListOpts struct { Marker string `q:"marker"` // Name filters on the name of the image. + // Multiple names can be specified by constructing a string + // such as "in:name1,name2,name3". Name string `q:"name"` // Visibility filters on the visibility of the image. @@ -37,6 +48,8 @@ type ListOpts struct { Owner string `q:"owner"` // Status filters on the status of the image. + // Multiple statuses can be specified by constructing a string + // such as "in:saving,queued". Status ImageStatus `q:"status"` // SizeMin filters on the size_min image property. @@ -56,12 +69,52 @@ type ListOpts struct { // SortDir will sort the list results either ascending or decending. SortDir string `q:"sort_dir"` - Tag string `q:"tag"` + + // Tags filters on specific image tags. + Tags []string `q:"tag"` + + // CreatedAtQuery filters images based on their creation date. + CreatedAtQuery *ImageDateQuery + + // UpdatedAtQuery filters images based on their updated date. + UpdatedAtQuery *ImageDateQuery + + // ContainerFormat filters images based on the container_format. + // Multiple container formats can be specified by constructing a + // string such as "in:bare,ami". + ContainerFormat string `q:"container_format"` + + // DiskFormat filters images based on the disk_format. + // Multiple disk formats can be specified by constructing a string + // such as "in:qcow2,iso". + DiskFormat string `q:"disk_format"` } // ToImageListQuery formats a ListOpts into a query string. func (opts ListOpts) ToImageListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) + params := q.Query() + + if opts.CreatedAtQuery != nil { + createdAt := opts.CreatedAtQuery.Date.Format(time.RFC3339) + if v := opts.CreatedAtQuery.Filter; v != "" { + createdAt = fmt.Sprintf("%s:%s", v, createdAt) + } + + params.Add("created_at", createdAt) + } + + if opts.UpdatedAtQuery != nil { + updatedAt := opts.UpdatedAtQuery.Date.Format(time.RFC3339) + if v := opts.UpdatedAtQuery.Filter; v != "" { + updatedAt = fmt.Sprintf("%s:%s", v, updatedAt) + } + + params.Add("updated_at", updatedAt) + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), err } diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index 33177c23f4..29757d203b 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -345,3 +345,44 @@ func HandleImageUpdateSuccessfully(t *testing.T) { }`) }) } + +// HandleImageListByTagsSuccessfully tests a list operation with tags. +func HandleImageListByTagsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + w.Header().Add("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, `{ + "images": [ + { + "status": "active", + "name": "cirros-0.3.2-x86_64-disk", + "tags": ["foo", "bar"], + "container_format": "bare", + "created_at": "2014-05-05T17:15:10Z", + "disk_format": "qcow2", + "updated_at": "2014-05-05T17:15:11Z", + "visibility": "public", + "self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", + "min_disk": 0, + "protected": false, + "id": "1bea47ed-f6a9-463b-b423-14b9cca9ad27", + "file": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27/file", + "checksum": "64d7c1cd2b6f60c92c14662941cb7913", + "owner": "5ef70662f8b34079a6eddb8da9d75fe8", + "size": 13167616, + "min_ram": 0, + "schema": "/v2/schemas/image", + "virtual_size": null, + "hw_disk_bus": "scsi", + "hw_disk_bus_model": "virtio-scsi", + "hw_scsi_model": "virtio-scsi" + } + ] + }`) + }) +} diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index d1f0966a42..487247b110 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -2,6 +2,7 @@ package testing import ( "testing" + "time" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/pagination" @@ -291,3 +292,90 @@ func TestUpdateImage(t *testing.T) { th.AssertDeepEquals(t, &expectedImage, actualImage) } + +func TestImageDateQuery(t *testing.T) { + date := time.Date(2014, 1, 1, 1, 1, 1, 0, time.UTC) + + listOpts := images.ListOpts{ + CreatedAtQuery: &images.ImageDateQuery{ + Date: date, + Filter: images.FilterGTE, + }, + UpdatedAtQuery: &images.ImageDateQuery{ + Date: date, + }, + } + + expectedQueryString := "?created_at=gte%3A2014-01-01T01%3A01%3A01Z&updated_at=2014-01-01T01%3A01%3A01Z" + actualQueryString, err := listOpts.ToImageListQuery() + th.AssertNoErr(t, err) + th.AssertEquals(t, expectedQueryString, actualQueryString) +} + +func TestImageListByTags(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleImageListByTagsSuccessfully(t) + + listOpts := images.ListOpts{ + Tags: []string{"foo", "bar"}, + } + + expectedQueryString := "?tag=foo&tag=bar" + actualQueryString, err := listOpts.ToImageListQuery() + th.AssertNoErr(t, err) + th.AssertEquals(t, expectedQueryString, actualQueryString) + + pages, err := images.List(fakeclient.ServiceClient(), listOpts).AllPages() + th.AssertNoErr(t, err) + allImages, err := images.ExtractImages(pages) + th.AssertNoErr(t, err) + + checksum := "64d7c1cd2b6f60c92c14662941cb7913" + sizeBytes := int64(13167616) + containerFormat := "bare" + diskFormat := "qcow2" + minDiskGigabytes := 0 + minRAMMegabytes := 0 + owner := "5ef70662f8b34079a6eddb8da9d75fe8" + file := allImages[0].File + createdDate := allImages[0].CreatedAt + lastUpdate := allImages[0].UpdatedAt + schema := "/v2/schemas/image" + tags := []string{"foo", "bar"} + + expectedImage := images.Image{ + ID: "1bea47ed-f6a9-463b-b423-14b9cca9ad27", + Name: "cirros-0.3.2-x86_64-disk", + Tags: tags, + + Status: images.ImageStatusActive, + + ContainerFormat: containerFormat, + DiskFormat: diskFormat, + + MinDiskGigabytes: minDiskGigabytes, + MinRAMMegabytes: minRAMMegabytes, + + Owner: owner, + + Protected: false, + Visibility: images.ImageVisibilityPublic, + + Checksum: checksum, + SizeBytes: sizeBytes, + File: file, + CreatedAt: createdDate, + UpdatedAt: lastUpdate, + Schema: schema, + VirtualSize: 0, + Properties: map[string]interface{}{ + "hw_disk_bus": "scsi", + "hw_disk_bus_model": "virtio-scsi", + "hw_scsi_model": "virtio-scsi", + }, + } + + th.AssertDeepEquals(t, expectedImage, allImages[0]) +} diff --git a/openstack/imageservice/v2/images/types.go b/openstack/imageservice/v2/images/types.go index 2e01b38f5c..d2f9cbd3bf 100644 --- a/openstack/imageservice/v2/images/types.go +++ b/openstack/imageservice/v2/images/types.go @@ -1,5 +1,9 @@ package images +import ( + "time" +) + // ImageStatus image statuses // http://docs.openstack.org/developer/glance/statuses.html type ImageStatus string @@ -77,3 +81,24 @@ const ( // ImageMemberStatusAll ImageMemberStatusAll ImageMemberStatus = "all" ) + +// ImageDateFilter represents a valid filter to use for filtering +// images by their date during a List. +type ImageDateFilter string + +const ( + FilterGT ImageDateFilter = "gt" + FilterGTE ImageDateFilter = "gte" + FilterLT ImageDateFilter = "lt" + FilterLTE ImageDateFilter = "lte" + FilterNEQ ImageDateFilter = "neq" + FilterEQ ImageDateFilter = "eq" +) + +// ImageDateQuery represents a date field to be used for listing images. +// If no filter is specified, the query will act as though FilterEQ was +// set. +type ImageDateQuery struct { + Date time.Time + Filter ImageDateFilter +} From 55324a00c82e9e71110e0ef947a4654fc9f849c3 Mon Sep 17 00:00:00 2001 From: sreinkemeier Date: Tue, 6 Mar 2018 14:15:10 +0100 Subject: [PATCH 0241/2296] Added delete endpoint group operation --- .../networking/v2/extensions/vpnaas/group_test.go | 1 + .../networking/v2/extensions/vpnaas/vpnaas.go | 14 ++++++++++++++ .../v2/extensions/vpnaas/endpointgroups/doc.go | 8 ++++++++ .../extensions/vpnaas/endpointgroups/requests.go | 7 +++++++ .../v2/extensions/vpnaas/endpointgroups/results.go | 6 ++++++ .../vpnaas/endpointgroups/testing/requests_test.go | 14 ++++++++++++++ 6 files changed, 50 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index ce80011be1..ce02a97493 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -20,6 +20,7 @@ func TestGroupCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create Endpoint group: %v", err) } + defer DeleteEndpointGroup(t, client, group.ID) tools.PrintResource(t, group) newGroup, err := endpointgroups.Get(client, group.ID).Extract() diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index a770895457..0c6616538b 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -144,3 +144,17 @@ func CreateEndpointGroup(t *testing.T, client *gophercloud.ServiceClient) (*endp return group, nil } + +// DeleteEndpointGroup will delete an Endpoint group 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 DeleteEndpointGroup(t *testing.T, client *gophercloud.ServiceClient, epGroupID string) { + t.Logf("Attempting to delete endpoint group: %s", epGroupID) + + err := endpointgroups.Delete(client, epGroupID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete endpoint group %s: %v", epGroupID, err) + } + + t.Logf("Deleted endpoint group: %s", epGroupID) +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go index bdd986bdc9..250dcd2a17 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go @@ -22,5 +22,13 @@ Example to retrieve an Endpoint Group if err != nil { panic(err) } + +Example to Delete an Endpoint Group + + err := endpointgroups.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() + if err != nil { + panic(err) + } + */ package endpointgroups diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go index 5279f26aca..d75c252aee 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -62,3 +62,10 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } + +// Delete will permanently delete a particular endpoint group based on its +// unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go index 6804d53757..fa687f40c8 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go @@ -52,3 +52,9 @@ type CreateResult struct { type GetResult struct { commonResult } + +// DeleteResult represents the results of a Delete operation. Call its ExtractErr method +// to determine whether the operation succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go index ba8d33c458..d21c7ca33d 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -122,3 +122,17 @@ func TestGet(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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) + }) + + res := endpointgroups.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + th.AssertNoErr(t, res.Err) +} From c2736130e84eb237aaae7a461b8c0f8fe8a19051 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 7 Mar 2018 03:39:17 +0100 Subject: [PATCH 0242/2296] Vpnaas: Update IKE policy (#793) * Added unit test for ike policy update * Added update request * Added acceptance test for update operation, added documentation * Changed Name and Description to pointers in UpdateOpts so updating to empty string is possible * Added phase1negotiationmode and ikeversion as updateable fields --- .../v2/extensions/vpnaas/ikepolicy_test.go | 15 ++++ .../v2/extensions/vpnaas/ikepolicies/doc.go | 13 ++++ .../extensions/vpnaas/ikepolicies/requests.go | 41 ++++++++++ .../extensions/vpnaas/ikepolicies/results.go | 6 ++ .../ikepolicies/testing/requests_test.go | 77 ++++++++++++++++++- 5 files changed, 151 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index 891eea81f5..2efa1e1b65 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -51,4 +51,19 @@ func TestIKEPolicyCRUD(t *testing.T) { } tools.PrintResource(t, newPolicy) + updatedName := "updatedname" + updatedDescription := "updated policy" + updateOpts := ikepolicies.UpdateOpts{ + Name: &updatedName, + Description: &updatedDescription, + Lifetime: &ikepolicies.LifetimeUpdateOpts{ + Value: 7000, + }, + } + updatedPolicy, err := ikepolicies.Update(client, policy.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update IKE policy: %v", err) + } + tools.PrintResource(t, updatedPolicy) + } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index 23630a17f4..649027103d 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -30,6 +30,19 @@ Example to Delete a Policy err := ikepolicies.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() if err != nil { panic(err) + +Example to Update an IKE policy + + updateOpts := ikepolicies.UpdateOpts{ + Name: "updatedname", + Description: "updated policy", + Lifetime: &ikepolicies.LifetimeUpdateOpts{ + Value: 7000, + }, + } + updatedPolicy, err := ikepolicies.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update IKE policy: %v", err) } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go index f6dfcdd46f..6b084ff624 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -166,3 +166,44 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { return PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToPolicyUpdateMap() (map[string]interface{}, error) +} + +type LifetimeUpdateOpts struct { + Units Unit `json:"units,omitempty"` + Value int `json:"value,omitempty"` +} + +// UpdateOpts contains the values used when updating an IKE policy +type UpdateOpts struct { + Description *string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` + AuthAlgorithm AuthAlgorithm `json:"auth_algorithm,omitempty"` + EncryptionAlgorithm EncryptionAlgorithm `json:"encryption_algorithm,omitempty"` + PFS PFS `json:"pfs,omitempty"` + Lifetime *LifetimeUpdateOpts `json:"lifetime,omitempty"` + Phase1NegotiationMode Phase1NegotiationMode `json:"phase_1_negotiation_mode,omitempty"` + IKEVersion IKEVersion `json:"ike_version,omitempty"` +} + +// ToPolicyUpdateMap casts an UpdateOpts struct to a map. +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "ikepolicy") +} + +// Update allows IKE policies to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToPolicyUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go index d298bc53a5..b825f5754f 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go @@ -117,3 +117,9 @@ type GetResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// UpdateResult represents the result of an update operation. Call its Extract method +// to interpret it as a Policy. +type UpdateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index 8f32432f94..9c3b08f120 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -161,7 +161,6 @@ func TestList(t *testing.T) { th.Mux.HandleFunc("/v2.0/vpn/ikepolicies", 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) @@ -227,3 +226,79 @@ func TestList(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ikepolicies/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` + { + "ikepolicy":{ + "name": "updatedname", + "description": "updated policy", + "lifetime": { + "value": 7000 + } + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "ikepolicy": { + "name": "updatedname", + "transform_protocol": "esp", + "auth_algorithm": "sha1", + "encapsulation_mode": "tunnel", + "encryption_algorithm": "aes-128", + "pfs": "group5", + "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "project_id": "b4eedccc6fb74fa8a7ad6b08382b852b", + "lifetime": { + "units": "seconds", + "value": 7000 + }, + "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "description": "updated policy" + } +} +`) + }) + + updatedName := "updatedname" + updatedDescription := "updated policy" + options := ikepolicies.UpdateOpts{ + Name: &updatedName, + Description: &updatedDescription, + Lifetime: &ikepolicies.LifetimeUpdateOpts{ + Value: 7000, + }, + } + + actual, err := ikepolicies.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + th.AssertNoErr(t, err) + expectedLifetime := ikepolicies.Lifetime{ + Units: "seconds", + Value: 7000, + } + expected := ikepolicies.Policy{ + TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", + ProjectID: "b4eedccc6fb74fa8a7ad6b08382b852b", + Name: "updatedname", + AuthAlgorithm: "sha1", + EncryptionAlgorithm: "aes-128", + PFS: "group5", + Description: "updated policy", + Lifetime: expectedLifetime, + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + } + th.AssertDeepEquals(t, expected, *actual) +} From 24d38e255f73b6eac52312031a9450f57e0c6b60 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Thu, 8 Mar 2018 03:20:18 +0100 Subject: [PATCH 0243/2296] Vpnaas: Create IPSec site connection (#810) * Added file structure, methods and structs for IPSec site connection creation * Added unit test * Added acceptance test * Got acceptance test to work by adding link between subnet and router * Added documentation * removed print statement * renamed AuthenticationMode to AuthMode to match json string, deleted '(Deprecated)' in comment * Removed AuthMode and RouteMode from request * fixed typos --- .../extensions/vpnaas/siteconnection_test.go | 82 +++++++++++ .../networking/v2/extensions/vpnaas/vpnaas.go | 60 +++++++- .../extensions/vpnaas/siteconnections/doc.go | 27 ++++ .../vpnaas/siteconnections/requests.go | 129 ++++++++++++++++++ .../vpnaas/siteconnections/results.go | 107 +++++++++++++++ .../siteconnections/testing/requests_test.go | 124 +++++++++++++++++ .../extensions/vpnaas/siteconnections/urls.go | 16 +++ 7 files changed, 544 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go create mode 100644 openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go create mode 100644 openstack/networking/v2/extensions/vpnaas/siteconnections/results.go create mode 100644 openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/vpnaas/siteconnections/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go new file mode 100644 index 0000000000..264cb54db9 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -0,0 +1,82 @@ +// +build acceptance networking vpnaas + +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/openstack/networking/v2/extensions/layer3/routers" + + "github.com/gophercloud/gophercloud/acceptance/tools" +) + +func TestConnectionCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := networks.CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + + // Create Subnet + subnet, err := networks.CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + + router, err := layer3.CreateExternalRouter(t, client) + if err != nil { + t.Fatalf("Unable to create router: %v", err) + } + + // Link router and subnet + aiOpts := routers.AddInterfaceOpts{ + SubnetID: subnet.ID, + } + + _, err = routers.AddInterface(client, router.ID, aiOpts).Extract() + if err != nil { + t.Fatalf("Failed to add interface to router: %v", err) + } + + // Create all needed resources for the connection + service, err := CreateService(t, client, router.ID) + if err != nil { + t.Fatalf("Unable to create service: %v", err) + } + + ikepolicy, err := CreateIKEPolicy(t, client) + if err != nil { + t.Fatalf("Unable to create IKE policy: %v", err) + } + + ipsecpolicy, err := CreateIPSecPolicy(t, client) + if err != nil { + t.Fatalf("Unable to create IPSec Policy: %v", err) + } + + peerEPGroup, err := CreateEndpointGroup(t, client) + if err != nil { + t.Fatalf("Unable to create Endpoint Group with CIDR endpoints: %v", err) + } + + localEPGroup, err := CreateEndpointGroupWithSubnet(t, client, subnet.ID) + if err != nil { + t.Fatalf("Unable to create Endpoint Group with subnet endpoints: %v", err) + } + + conn, err := CreateSiteConnection(t, client, ikepolicy.ID, ipsecpolicy.ID, service.ID, peerEPGroup.ID, localEPGroup.ID) + if err != nil { + t.Fatalf("Unable to create IPSec Site Connection: %v", err) + } + + tools.PrintResource(t, conn) + +} diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index 0c6616538b..e38625a6b3 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" ) // CreateService will create a Service with a random name and a specified router ID @@ -53,7 +54,7 @@ func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID st func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecpolicies.Policy, error) { policyName := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create policy %s", policyName) + t.Logf("Attempting to create IPSec policy %s", policyName) createOpts := ipsecpolicies.CreateOpts{ Name: policyName, @@ -157,4 +158,61 @@ func DeleteEndpointGroup(t *testing.T, client *gophercloud.ServiceClient, epGrou } t.Logf("Deleted endpoint group: %s", epGroupID) + +} + +// CreateEndpointGroupWithSubnet will create an endpoint group with a random name. +// An error will be returned if the group could not be created. +func CreateEndpointGroupWithSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*endpointgroups.EndpointGroup, error) { + groupName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create group %s", groupName) + + createOpts := endpointgroups.CreateOpts{ + Name: groupName, + Type: endpointgroups.TypeSubnet, + Endpoints: []string{ + subnetID, + }, + } + group, err := endpointgroups.Create(client, createOpts).Extract() + if err != nil { + return group, err + } + + t.Logf("Successfully created group %s", groupName) + + return group, nil +} + +// CreateSiteConnection will create an IPSec site connection with a random name and specified +// IKE policy, IPSec policy, service, peer EP group and local EP Group. +// An error will be returned if the connection could not be created. +func CreateSiteConnection(t *testing.T, client *gophercloud.ServiceClient, ikepolicyID string, ipsecpolicyID string, serviceID string, peerEPGroupID string, localEPGroupID string) (*siteconnections.Connection, error) { + connectionName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create IPSec site connection %s", connectionName) + + createOpts := siteconnections.CreateOpts{ + Name: connectionName, + PSK: "secret", + Initiator: siteconnections.InitiatorBiDirectional, + AdminStateUp: gophercloud.Enabled, + IPSecPolicyID: ipsecpolicyID, + PeerEPGroupID: peerEPGroupID, + IKEPolicyID: ikepolicyID, + VPNServiceID: serviceID, + LocalEPGroupID: localEPGroupID, + PeerAddress: "172.24.4.233", + PeerID: "172.24.4.233", + MTU: 1500, + } + connection, err := siteconnections.Create(client, createOpts).Extract() + if err != nil { + return connection, err + } + + t.Logf("Successfully created IPSec Site Connection %s", connectionName) + + return connection, nil } diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go new file mode 100644 index 0000000000..b90cca8e57 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go @@ -0,0 +1,27 @@ +/* +Package siteconnections allows management and retrieval of IPSec site connections in the +OpenStack Networking Service. + + +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) + } +*/ +package siteconnections diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go new file mode 100644 index 0000000000..3b2e66c3fa --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -0,0 +1,129 @@ +package siteconnections + +import ( + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToConnectionCreateMap() (map[string]interface{}, error) +} +type Action string +type Initiator string + +const ( + ActionHold Action = "hold" + ActionClear Action = "clear" + ActionRestart Action = "restart" + ActionDisabled Action = "disabled" + ActionRestartByPeer Action = "restart-by-peer" + InitiatorBiDirectional Initiator = "bi-directional" + InitiatorResponseOnly Initiator = "response-only" +) + +// DPDCreateOpts contains all the values needed to create a valid configuration for Dead Peer detection protocols +type DPDCreateOpts struct { + // The dead peer detection (DPD) action. + // A valid value is clear, hold, restart, disabled, or restart-by-peer. + // Default value is hold. + Action Action `json:"action,omitempty"` + + // The dead peer detection (DPD) timeout in seconds. + // A valid value is a positive integer that is greater than the DPD interval value. + // Default is 120. + Timeout int `json:"timeout,omitempty"` + + // The dead peer detection (DPD) interval, in seconds. + // A valid value is a positive integer. + // Default is 30. + Interval int `json:"interval,omitempty"` +} + +// CreateOpts contains all the values needed to create a new IPSec site connection +type CreateOpts struct { + // The ID of the IKE policy + IKEPolicyID string `json:"ikepolicy_id"` + + // The ID of the VPN Service + VPNServiceID string `json:"vpnservice_id"` + + // The ID for the endpoint group that contains private subnets for the local side of the connection. + // You must specify this parameter with the peer_ep_group_id parameter unless + // in backward- compatible mode where peer_cidrs is provided with a subnet_id for the VPN service. + LocalEPGroupID string `json:"local_ep_group_id,omitempty"` + + // The ID of the IPsec policy. + IPSecPolicyID string `json:"ipsecpolicy_id"` + + // The peer router identity for authentication. + // A valid value is an IPv4 address, IPv6 address, e-mail address, key ID, or FQDN. + // Typically, this value matches the peer_address value. + PeerID string `json:"peer_id"` + + // The ID of the project + TenantID string `json:"tenant_id,omitempty"` + + // The ID for the endpoint group that contains private CIDRs in the form < net_address > / < prefix > + // for the peer side of the connection. + // You must specify this parameter with the local_ep_group_id parameter unless in backward-compatible mode + // where peer_cidrs is provided with a subnet_id for the VPN service. + PeerEPGroupID string `json:"peer_ep_group_id,omitempty"` + + // An ID to be used instead of the external IP address for a virtual router used in traffic between instances on different networks in east-west traffic. + // Most often, local ID would be domain name, email address, etc. + // If this is not configured then the external IP address will be used as the ID. + LocalID string `json:"local_id,omitempty"` + + // The human readable name of the connection. + // Does not have to be unique. + // Default is an empty string + Name string `json:"name,omitempty"` + + // The human readable description of the connection. + // Does not have to be unique. + // Default is an empty string + Description string `json:"description,omitempty"` + + // The peer gateway public IPv4 or IPv6 address or FQDN. + PeerAddress string `json:"peer_address"` + + // The pre-shared key. + // A valid value is any string. + PSK string `json:"psk"` + + // Indicates whether this VPN can only respond to connections or both respond to and initiate connections. + // A valid value is response-only or bi-directional. Default is bi-directional. + Initiator Initiator `json:"initiator,omitempty"` + + // Unique list of valid peer private CIDRs in the form < net_address > / < prefix > . + PeerCIDRs []string `json:"peer_cidrs,omitempty"` + + // The administrative state of the resource, which is up (true) or down (false). + // Default is false + AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // A dictionary with dead peer detection (DPD) protocol controls. + DPD *DPDCreateOpts `json:"dpd,omitempty"` + + // The maximum transmission unit (MTU) value to address fragmentation. + // Minimum value is 68 for IPv4, and 1280 for IPv6. + MTU int `json:"mtu,omitempty"` +} + +// ToServiceCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToConnectionCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "ipsec_site_connection") +} + +// Create accepts a CreateOpts struct and uses the values to create a new +// IPSec site connection. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToConnectionCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go new file mode 100644 index 0000000000..420ab8ca2f --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go @@ -0,0 +1,107 @@ +package siteconnections + +import ( + "github.com/gophercloud/gophercloud" +) + +type DPD struct { + // Action is the dead peer detection (DPD) action. + Action string `json:"action"` + + // Timeout is the dead peer detection (DPD) timeout in seconds. + Timeout int `json:"timeout"` + + // Interval is the dead peer detection (DPD) interval in seconds. + Interval int `json:"interval"` +} + +// Connection is an IPSec site connection +type Connection struct { + // IKEPolicyID is the ID of the IKE policy. + IKEPolicyID string `json:"ikepolicy_id"` + + // VPNServiceID is the ID of the VPN service. + VPNServiceID string `json:"vpnservice_id"` + + // LocalEPGroupID is the ID for the endpoint group that contains private subnets for the local side of the connection. + LocalEPGroupID string `json:"local_ep_group_id"` + + // IPSecPolicyID is the ID of the IPSec policy + IPSecPolicyID string `json:"ipsecpolicy_id"` + + // PeerID is the peer router identity for authentication. + PeerID string `json:"peer_id"` + + // TenantID is the ID of the project. + TenantID string `json:"tenant_id"` + + // ProjectID is the ID of the project. + ProjectID string `json:"project_id"` + + // PeerEPGroupID is the ID for the endpoint group that contains private CIDRs in the form < net_address > / < prefix > + // for the peer side of the connection. + PeerEPGroupID string `json:"peer_ep_group_id"` + + // LocalID is an ID to be used instead of the external IP address for a virtual router used in traffic + // between instances on different networks in east-west traffic. + LocalID string `json:"local_id"` + + // Name is the human readable name of the connection. + Name string `json:"name"` + + // Description is the human readable description of the connection. + Description string `json:"description"` + + // PeerAddress is the peer gateway public IPv4 or IPv6 address or FQDN. + PeerAddress string `json:"peer_address"` + + // RouteMode is the route mode. + RouteMode string `json:"route_mode"` + + // PSK is the pre-shared key. + PSK string `json:"psk"` + + // Initiator indicates whether this VPN can only respond to connections or both respond to and initiate connections. + Initiator string `json:"initiator"` + + // PeerCIDRs is a unique list of valid peer private CIDRs in the form < net_address > / < prefix > . + PeerCIDRs []string `json:"peer_cidrs"` + + // AdminStateUp is the administrative state of the connection. + AdminStateUp bool `json:"admin_state_up"` + + // DPD is the dead peer detection (DPD) protocol controls. + DPD DPD `json:"dpd"` + + // AuthMode is the authentication mode. + AuthMode string `json:"auth_mode"` + + // MTU is the maximum transmission unit (MTU) value to address fragmentation. + MTU int `json:"mtu"` + + // Status indicates whether the IPsec connection is currently operational. + // Values are ACTIVE, DOWN, BUILD, ERROR, PENDING_CREATE, PENDING_UPDATE, or PENDING_DELETE. + Status string `json:"status"` + + // ID is the id of the connection + ID string `json:"id"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts an IPSec site connection. +func (r commonResult) Extract() (*Connection, error) { + var s struct { + Connection *Connection `json:"ipsec_site_connection"` + } + err := r.ExtractInto(&s) + return s.Connection, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Connection. +type CreateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go new file mode 100644 index 0000000000..f99290526a --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -0,0 +1,124 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud" + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections", 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, ` +{ + + "ipsec_site_connection": { + "psk": "secret", + "initiator": "bi-directional", + "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + "admin_state_up": true, + "mtu": 1500, + "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", + "peer_address": "172.24.4.233", + "peer_id": "172.24.4.233", + "name": "vpnconnection1" + +} +} `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "ipsec_site_connection": { + "status": "PENDING_CREATE", + "psk": "secret", + "initiator": "bi-directional", + "name": "vpnconnection1", + "admin_state_up": true, + "project_id": "10039663455a446d8ba2cbb058b0f578", + "tenant_id": "10039663455a446d8ba2cbb058b0f578", + "auth_mode": "psk", + "peer_cidrs": [], + "mtu": 1500, + "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "dpd": { + "action": "hold", + "interval": 30, + "timeout": 120 + }, + "route_mode": "static", + "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", + "peer_address": "172.24.4.233", + "peer_id": "172.24.4.233", + "id": "851f280f-5639-4ea3-81aa-e298525ab74b", + "description": "" + } +} + `) + }) + + options := siteconnections.CreateOpts{ + Name: "vpnconnection1", + AdminStateUp: gophercloud.Enabled, + PSK: "secret", + Initiator: siteconnections.InitiatorBiDirectional, + IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + MTU: 1500, + PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", + PeerAddress: "172.24.4.233", + PeerID: "172.24.4.233", + } + actual, err := siteconnections.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + expectedDPD := siteconnections.DPD{ + Action: "hold", + Interval: 30, + Timeout: 120, + } + expected := siteconnections.Connection{ + TenantID: "10039663455a446d8ba2cbb058b0f578", + Name: "vpnconnection1", + AdminStateUp: true, + PSK: "secret", + Initiator: "bi-directional", + IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + MTU: 1500, + PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", + PeerAddress: "172.24.4.233", + PeerID: "172.24.4.233", + Status: "PENDING_CREATE", + ProjectID: "10039663455a446d8ba2cbb058b0f578", + AuthMode: "psk", + PeerCIDRs: []string{}, + DPD: expectedDPD, + RouteMode: "static", + ID: "851f280f-5639-4ea3-81aa-e298525ab74b", + Description: "", + } + th.AssertDeepEquals(t, expected, *actual) +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/urls.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/urls.go new file mode 100644 index 0000000000..5c8ee9a364 --- /dev/null +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/urls.go @@ -0,0 +1,16 @@ +package siteconnections + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "vpn" + resourcePath = "ipsec-site-connections" +) + +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 dd34b9036dd655fd414389304277d49b347e1cbd Mon Sep 17 00:00:00 2001 From: sreinkemeier Date: Wed, 7 Mar 2018 17:07:12 +0100 Subject: [PATCH 0244/2296] Added delete request, result, unit test and acceptance test --- .../v2/extensions/vpnaas/siteconnection_test.go | 15 +++++++++++++++ .../networking/v2/extensions/vpnaas/vpnaas.go | 15 +++++++++++++++ .../extensions/vpnaas/siteconnections/requests.go | 7 +++++++ .../extensions/vpnaas/siteconnections/results.go | 6 ++++++ .../siteconnections/testing/requests_test.go | 14 ++++++++++++++ 5 files changed, 57 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index 264cb54db9..a631522aac 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -24,17 +24,20 @@ func TestConnectionCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create network: %v", err) } + defer networks.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networks.CreateSubnet(t, client, network.ID) if err != nil { t.Fatalf("Unable to create subnet: %v", err) } + defer networks.DeleteSubnet(t, client, subnet.ID) router, err := layer3.CreateExternalRouter(t, client) if err != nil { t.Fatalf("Unable to create router: %v", err) } + defer layer3.DeleteRouter(t, client, router.ID) // Link router and subnet aiOpts := routers.AddInterfaceOpts{ @@ -45,37 +48,49 @@ func TestConnectionCRUD(t *testing.T) { if err != nil { t.Fatalf("Failed to add interface to router: %v", err) } + defer func() { + riOpts := routers.RemoveInterfaceOpts{ + SubnetID: subnet.ID, + } + routers.RemoveInterface(client, router.ID, riOpts) + }() // Create all needed resources for the connection service, err := CreateService(t, client, router.ID) if err != nil { t.Fatalf("Unable to create service: %v", err) } + defer DeleteService(t, client, service.ID) ikepolicy, err := CreateIKEPolicy(t, client) if err != nil { t.Fatalf("Unable to create IKE policy: %v", err) } + defer DeleteIKEPolicy(t, client, ikepolicy.ID) ipsecpolicy, err := CreateIPSecPolicy(t, client) if err != nil { t.Fatalf("Unable to create IPSec Policy: %v", err) } + defer DeleteIPSecPolicy(t, client, ipsecpolicy.ID) peerEPGroup, err := CreateEndpointGroup(t, client) if err != nil { t.Fatalf("Unable to create Endpoint Group with CIDR endpoints: %v", err) } + defer DeleteEndpointGroup(t, client, peerEPGroup.ID) localEPGroup, err := CreateEndpointGroupWithSubnet(t, client, subnet.ID) if err != nil { t.Fatalf("Unable to create Endpoint Group with subnet endpoints: %v", err) } + defer DeleteEndpointGroup(t, client, localEPGroup.ID) conn, err := CreateSiteConnection(t, client, ikepolicy.ID, ipsecpolicy.ID, service.ID, peerEPGroup.ID, localEPGroup.ID) if err != nil { t.Fatalf("Unable to create IPSec Site Connection: %v", err) } + defer DeleteSiteConnection(t, client, conn.ID) tools.PrintResource(t, conn) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index e38625a6b3..f42aa50450 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -216,3 +216,18 @@ func CreateSiteConnection(t *testing.T, client *gophercloud.ServiceClient, ikepo return connection, nil } + +// DeleteSiteConnection will delete an IPSec site connection 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 DeleteSiteConnection(t *testing.T, client *gophercloud.ServiceClient, siteConnectionID string) { + t.Logf("Attempting to delete site connection: %s", siteConnectionID) + + err := siteconnections.Delete(client, siteConnectionID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete site connection %s: %v", siteConnectionID, err) + } + + t.Logf("Deleted site connection: %s", siteConnectionID) + +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index 3b2e66c3fa..280f53ee8f 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -127,3 +127,10 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } + +// Delete will permanently delete a particular IPSec site connection based on its +// unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go index 420ab8ca2f..1a7e0dd2fe 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go @@ -105,3 +105,9 @@ func (r commonResult) Extract() (*Connection, error) { type CreateResult struct { commonResult } + +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the operation succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go index f99290526a..ab68720904 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -122,3 +122,17 @@ func TestCreate(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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) + }) + + res := siteconnections.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + th.AssertNoErr(t, res.Err) +} From d2fe5bf4e65410df3bcca179f80aa84a3fc7fb98 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Fri, 9 Mar 2018 04:24:54 +0100 Subject: [PATCH 0245/2296] Vpnaas: List Endpoint groups (#813) * Added List function for Endpoint groups * Added documentation * Removed Endpoints parameter from list function * Added projectID to ListOpts --- .../v2/extensions/vpnaas/group_test.go | 21 ++++++ .../extensions/vpnaas/endpointgroups/doc.go | 11 ++++ .../vpnaas/endpointgroups/requests.go | 45 ++++++++++++- .../vpnaas/endpointgroups/results.go | 38 +++++++++++ .../endpointgroups/testing/requests_test.go | 64 +++++++++++++++++++ 5 files changed, 178 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index ce02a97493..f793b3428c 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -10,6 +10,27 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" ) +func TestGroupList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := endpointgroups.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list endpoint groups: %v", err) + } + + allGroups, err := endpointgroups.ExtractEndpointGroups(allPages) + if err != nil { + t.Fatalf("Unable to extract endpoint groups: %v", err) + } + + for _, group := range allGroups { + tools.PrintResource(t, group) + } +} + func TestGroupCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go index 250dcd2a17..f8dff3b66c 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go @@ -30,5 +30,16 @@ Example to Delete an Endpoint Group panic(err) } +Example to List Endpoint groups + + allPages, err := endpointgroups.List(client, nil).AllPages() + if err != nil { + panic(err) + } + + allGroups, err := endpointgroups.ExtractEndpointGroups(allPages) + if err != nil { + panic(err) + } */ package endpointgroups diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go index d75c252aee..cd9ca9ce8c 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -1,6 +1,9 @@ package endpointgroups -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) type EndpointType string @@ -63,6 +66,46 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToEndpointGroupListQuery() (string, error) +} + +// ListOpts allows the filtering of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the Endpoint group attributes you want to see returned. +type ListOpts struct { + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Description string `q:"description"` + Name string `q:"name"` + Type string `q:"type"` +} + +// ToEndpointGroupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToEndpointGroupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// Endpoint groups. It accepts a ListOpts struct, which allows you to filter +// the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToEndpointGroupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return EndpointGroupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + // Delete will permanently delete a particular endpoint group based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go index fa687f40c8..c3d7dfcf5d 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go @@ -2,6 +2,7 @@ package endpointgroups import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // EndpointGroup is an endpoint group. @@ -41,6 +42,43 @@ func (r commonResult) Extract() (*EndpointGroup, error) { return s.Service, err } +// EndpointGroupPage is the page returned by a pager when traversing over a +// collection of Policies. +type EndpointGroupPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of Endpoint groups 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 EndpointGroupPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"endpoint_groups_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether an EndpointGroupPage struct is empty. +func (r EndpointGroupPage) IsEmpty() (bool, error) { + is, err := ExtractEndpointGroups(r) + return len(is) == 0, err +} + +// ExtractEndpointGroups accepts a Page struct, specifically an EndpointGroupPage struct, +// and extracts the elements into a slice of Endpoint group structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractEndpointGroups(r pagination.Page) ([]EndpointGroup, error) { + var s struct { + EndpointGroups []EndpointGroup `json:"endpoint_groups"` + } + err := (r.(EndpointGroupPage)).ExtractInto(&s) + return s.EndpointGroups, err +} + // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as an endpoint group. type CreateResult struct { diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go index d21c7ca33d..1a3814d47b 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -7,6 +7,7 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -123,6 +124,69 @@ func TestGet(t *testing.T) { th.AssertDeepEquals(t, expected, *actual) } +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups", 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, ` + { + "endpoint_groups": [ + { + "description": "", + "tenant_id": "4ad57e7ce0b24fca8f12b9834d91079d", + "project_id": "4ad57e7ce0b24fca8f12b9834d91079d", + "endpoints": [ + "10.2.0.0/24", + "10.3.0.0/24" + ], + "type": "cidr", + "id": "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", + "name": "peers" + } + ] +} + `) + }) + + count := 0 + + endpointgroups.List(fake.ServiceClient(), endpointgroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := endpointgroups.ExtractEndpointGroups(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + expected := []endpointgroups.EndpointGroup{ + { + Name: "peers", + TenantID: "4ad57e7ce0b24fca8f12b9834d91079d", + ProjectID: "4ad57e7ce0b24fca8f12b9834d91079d", + ID: "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", + Description: "", + Endpoints: []string{ + "10.2.0.0/24", + "10.3.0.0/24", + }, + Type: "cidr", + }, + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 4a1a047deff21d19687a0e1a21786a93f4d25707 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 9 Mar 2018 04:09:15 +0000 Subject: [PATCH 0246/2296] Compute v2: Fix EOF errors in compute secgroups --- acceptance/openstack/compute/v2/compute.go | 10 +++++++- .../openstack/compute/v2/secgroup_test.go | 24 ++++++++++++------- .../v2/extensions/secgroups/requests.go | 4 ++-- .../extensions/secgroups/testing/fixtures.go | 2 -- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 5cb86185d3..77b3d7d2f8 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -681,7 +681,15 @@ func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *serve t.Fatalf("Unable to delete server %s: %s", server.ID, err) } - t.Logf("Deleted server: %s", server.ID) + if err := WaitForComputeStatus(client, server, "DELETED"); err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + t.Logf("Deleted server: %s", server.ID) + return + } + t.Fatalf("Error deleting server %s: %s", server.ID, err) + } + + t.Fatalf("Could not delete server: %s", server.ID) } // DeleteServerGroup will delete a server group. A fatal error will occur if diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/acceptance/openstack/compute/v2/secgroup_test.go index c0d023037d..d77c4ace86 100644 --- a/acceptance/openstack/compute/v2/secgroup_test.go +++ b/acceptance/openstack/compute/v2/secgroup_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" ) func TestSecGroupsList(t *testing.T) { @@ -105,12 +106,6 @@ func TestSecGroupsAddGroupToServer(t *testing.T) { t.Fatalf("Unable to create a compute client: %v", err) } - server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } - defer DeleteServer(t, client, server) - securityGroup, err := CreateSecurityGroup(t, client) if err != nil { t.Fatalf("Unable to create security group: %v", err) @@ -123,15 +118,28 @@ func TestSecGroupsAddGroupToServer(t *testing.T) { } defer DeleteSecurityGroupRule(t, client, rule) + server, err := CreateServer(t, client) + if err != nil { + t.Fatalf("Unable to create server: %v", err) + } + defer DeleteServer(t, client, server) + t.Logf("Adding group %s to server %s", securityGroup.ID, server.ID) err = secgroups.AddServer(client, server.ID, securityGroup.Name).ExtractErr() - if err != nil && err.Error() != "EOF" { + if err != nil { t.Fatalf("Unable to add group %s to server %s: %s", securityGroup.ID, server.ID, err) } + server, err = servers.Get(client, server.ID).Extract() + if err != nil { + t.Fatalf("Unable to get server %s: %s", server.ID, err) + } + + tools.PrintResource(t, server) + t.Logf("Removing group %s from server %s", securityGroup.ID, server.ID) err = secgroups.RemoveServer(client, server.ID, securityGroup.Name).ExtractErr() - if err != nil && err.Error() != "EOF" { + if err != nil { t.Fatalf("Unable to remove group %s from server %s: %s", securityGroup.ID, server.ID, err) } } diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go index bcceaeacdd..8b93f08b07 100644 --- a/openstack/compute/v2/extensions/secgroups/requests.go +++ b/openstack/compute/v2/extensions/secgroups/requests.go @@ -172,12 +172,12 @@ func actionMap(prefix, groupName string) map[string]map[string]string { // AddServer will associate a server and a security group, enforcing the // rules of the group on the server. func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r AddServerResult) { - _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &r.Body, nil) + _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), nil, nil) return } // RemoveServer will disassociate a server from a security group. func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r RemoveServerResult) { - _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &r.Body, nil) + _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), nil, nil) return } diff --git a/openstack/compute/v2/extensions/secgroups/testing/fixtures.go b/openstack/compute/v2/extensions/secgroups/testing/fixtures.go index 536e7f8ea1..27bd56f364 100644 --- a/openstack/compute/v2/extensions/secgroups/testing/fixtures.go +++ b/openstack/compute/v2/extensions/secgroups/testing/fixtures.go @@ -303,7 +303,6 @@ func mockAddServerToGroupResponse(t *testing.T, serverID string) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) }) } @@ -323,6 +322,5 @@ func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) }) } From fd83de6f9a5581d9bfb2fbc5a96287f8cec6ad71 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 12 Mar 2018 03:02:53 +0000 Subject: [PATCH 0247/2296] Networking v2: Fix Extra DHCP Options Updates This commit fixes updating of extra DHCP options on a Networking port. The fix entails creating two new structs: one for create and one for update. The update struct is able to take a string pointer as a dhcp value so a nil/null string can be provided. This nil/null value will cause a dhcp option to be remove if requested. --- .../openstack/networking/v2/networking.go | 3 +- .../openstack/networking/v2/ports_test.go | 12 +++++- .../v2/extensions/extradhcpopts/doc.go | 29 +++++++------ .../v2/extensions/extradhcpopts/requests.go | 39 +++++++++++++++--- .../v2/extensions/extradhcpopts/results.go | 19 ++------- .../networking/v2/ports/testing/fixtures.go | 4 ++ .../v2/ports/testing/requests_test.go | 41 ++++++++++--------- 7 files changed, 91 insertions(+), 56 deletions(-) diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index 58a59f48ae..b5dcb6d2c7 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -357,9 +357,10 @@ func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient AdminStateUp: gophercloud.Enabled, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } + createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ + ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{ { OptName: "test_option_1", OptValue: "test_value_1", diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index d47bc3a196..96faa09a2c 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -424,12 +424,20 @@ func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { portUpdateOpts := ports.UpdateOpts{ Name: newPortName, } + + existingOpt := port.ExtraDHCPOpts[0] + newOptValue := "test_value_2" + updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ + ExtraDHCPOpts: []extradhcpopts.UpdateExtraDHCPOpt{ + { + OptName: existingOpt.OptName, + OptValue: nil, + }, { OptName: "test_option_2", - OptValue: "test_value_2", + OptValue: &newOptValue, }, }, } diff --git a/openstack/networking/v2/extensions/extradhcpopts/doc.go b/openstack/networking/v2/extensions/extradhcpopts/doc.go index 487fdd6cbe..ec5d6181d6 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/doc.go +++ b/openstack/networking/v2/extensions/extradhcpopts/doc.go @@ -16,6 +16,11 @@ Example to Get a Port with Extra DHCP Options Example to Create a Port with Extra DHCP Options + var s struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt + } + adminStateUp := true portCreateOpts := ports.CreateOpts{ Name: "dhcp-conf-port", @@ -25,19 +30,16 @@ Example to Create a Port with Extra DHCP Options {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, }, } + createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ + ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{ { OptName: "optionA", OptValue: "valueA", }, }, } - var s struct { - ports.Port - extradhcpopts.ExtraDHCPOptsExt - } err := ports.Create(networkClient, createOpts).ExtractInto(&s) if err != nil { @@ -46,27 +48,30 @@ Example to Create a Port with Extra DHCP Options Example to Update a Port with Extra DHCP Options + var s struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt + } + portUpdateOpts := ports.UpdateOpts{ Name: "updated-dhcp-conf-port", FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, } + + value := "valueB" updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ + ExtraDHCPOpts: []extradhcpopts.UpdateExtraDHCPOpt{ { OptName: "optionB", - OptValue: "valueB", + OptValue: &value, }, }, } - var s struct { - ports.Port - extradhcpopts.ExtraDHCPOptsExt - } - portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" err := ports.Update(networkClient, portID, updateOpts).ExtractInto(&s) if err != nil { panic(err) diff --git a/openstack/networking/v2/extensions/extradhcpopts/requests.go b/openstack/networking/v2/extensions/extradhcpopts/requests.go index 9893f84d84..f3eb9bc450 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/requests.go +++ b/openstack/networking/v2/extensions/extradhcpopts/requests.go @@ -1,6 +1,7 @@ package extradhcpopts import ( + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) @@ -11,7 +12,20 @@ type CreateOptsExt struct { ports.CreateOptsBuilder // ExtraDHCPOpts field is a set of DHCP options for a single port. - ExtraDHCPOpts []ExtraDHCPOpt `json:"extra_dhcp_opts,omitempty"` + ExtraDHCPOpts []CreateExtraDHCPOpt `json:"extra_dhcp_opts,omitempty"` +} + +// CreateExtraDHCPOpt represents the options required to create an extra DHCP +// option on a port. +type CreateExtraDHCPOpt struct { + // OptName is the name of a DHCP option. + OptName string `json:"opt_name" required:"true"` + + // OptValue is the value of the DHCP option. + OptValue string `json:"opt_value" required:"true"` + + // IPVersion is the IP protocol version of a DHCP option. + IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` } // ToPortCreateMap casts a CreateOptsExt struct to a map. @@ -27,11 +41,11 @@ func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { if opts.ExtraDHCPOpts != nil { extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) for i, opt := range opts.ExtraDHCPOpts { - extraDHCPOptMap, err := opt.ToMap() + b, err := gophercloud.BuildRequestBody(opt, "") if err != nil { return nil, err } - extraDHCPOpts[i] = extraDHCPOptMap + extraDHCPOpts[i] = b } port["extra_dhcp_opts"] = extraDHCPOpts } @@ -46,7 +60,20 @@ type UpdateOptsExt struct { ports.UpdateOptsBuilder // ExtraDHCPOpts field is a set of DHCP options for a single port. - ExtraDHCPOpts []ExtraDHCPOpt `json:"extra_dhcp_opts,omitempty"` + ExtraDHCPOpts []UpdateExtraDHCPOpt `json:"extra_dhcp_opts,omitempty"` +} + +// UpdateExtraDHCPOpt represents the options required to update an extra DHCP +// option on a port. +type UpdateExtraDHCPOpt struct { + // OptName is the name of a DHCP option. + OptName string `json:"opt_name" required:"true"` + + // OptValue is the value of the DHCP option. + OptValue *string `json:"opt_value"` + + // IPVersion is the IP protocol version of a DHCP option. + IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` } // ToPortUpdateMap casts an UpdateOpts struct to a map. @@ -62,11 +89,11 @@ func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { if opts.ExtraDHCPOpts != nil { extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) for i, opt := range opts.ExtraDHCPOpts { - extraDHCPOptMap, err := opt.ToMap() + b, err := gophercloud.BuildRequestBody(opt, "") if err != nil { return nil, err } - extraDHCPOpts[i] = extraDHCPOptMap + extraDHCPOpts[i] = b } port["extra_dhcp_opts"] = extraDHCPOpts } diff --git a/openstack/networking/v2/extensions/extradhcpopts/results.go b/openstack/networking/v2/extensions/extradhcpopts/results.go index 042d425a58..8e3132ea4a 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/results.go +++ b/openstack/networking/v2/extensions/extradhcpopts/results.go @@ -1,7 +1,5 @@ package extradhcpopts -import "github.com/gophercloud/gophercloud" - // ExtraDHCPOptsExt is a struct that contains different DHCP options for a // single port. type ExtraDHCPOptsExt struct { @@ -10,24 +8,13 @@ type ExtraDHCPOptsExt struct { // ExtraDHCPOpt represents a single set of extra DHCP options for a single port. type ExtraDHCPOpt struct { - // Name is the name of a single DHCP option. + // OptName is the name of a single DHCP option. OptName string `json:"opt_name"` - // Value is the value of a single DHCP option. + // OptValue is the value of a single DHCP option. OptValue string `json:"opt_value"` // IPVersion is the IP protocol version of a single DHCP option. // Valid value is 4 or 6. Default is 4. - IPVersion int `json:"ip_version,omitempty"` -} - -// ToMap is a helper function to convert an individual ExtraDHCPOpt structure -// into a sub-map. -func (opts ExtraDHCPOpt) ToMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "") - if err != nil { - return nil, err - } - - return b, nil + IPVersion int `json:"ip_version"` } diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index 870572cb64..94b5a64a21 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -625,6 +625,10 @@ const UpdateWithExtraDHCPOptsRequest = ` } ], "extra_dhcp_opts": [ + { + "opt_name": "option1", + "opt_value": null + }, { "opt_name": "option2", "opt_value": "value2" diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 7df21c7a35..7b04bb1e5e 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -596,12 +596,6 @@ func TestGetWithExtraDHCPOpts(t *testing.T) { th.AssertEquals(t, s.Status, "ACTIVE") th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") - th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ - {OptName: "option1", OptValue: "value1", IPVersion: 4}, - {OptName: "option2", OptValue: "value2", IPVersion: 4}, - }, - }) th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.Name, "port-with-extra-dhcp-opts") th.AssertEquals(t, s.DeviceOwner, "") @@ -611,6 +605,13 @@ func TestGetWithExtraDHCPOpts(t *testing.T) { }) th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, s.DeviceID, "") + + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option1") + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value1") + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4) + th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].OptName, "option2") + th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].OptValue, "value2") + th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].IPVersion, 4) } func TestCreateWithExtraDHCPOpts(t *testing.T) { @@ -642,7 +643,7 @@ func TestCreateWithExtraDHCPOpts(t *testing.T) { createOpts := extradhcpopts.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ + ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{ { OptName: "option1", OptValue: "value1", @@ -661,11 +662,6 @@ func TestCreateWithExtraDHCPOpts(t *testing.T) { th.AssertEquals(t, s.Status, "DOWN") th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") - th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ - {OptName: "option1", OptValue: "value1", IPVersion: 4}, - }, - }) th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.Name, "port-with-extra-dhcp-opts") th.AssertEquals(t, s.DeviceOwner, "") @@ -675,6 +671,10 @@ func TestCreateWithExtraDHCPOpts(t *testing.T) { }) th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, s.DeviceID, "") + + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option1") + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value1") + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4) } func TestUpdateWithExtraDHCPOpts(t *testing.T) { @@ -701,12 +701,16 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { }, } + edoValue2 := "value2" updateOpts := extradhcpopts.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ + ExtraDHCPOpts: []extradhcpopts.UpdateExtraDHCPOpt{ + { + OptName: "option1", + }, { OptName: "option2", - OptValue: "value2", + OptValue: &edoValue2, }, }, } @@ -722,11 +726,6 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { th.AssertEquals(t, s.Status, "DOWN") th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") - th.AssertDeepEquals(t, s.ExtraDHCPOptsExt, extradhcpopts.ExtraDHCPOptsExt{ - ExtraDHCPOpts: []extradhcpopts.ExtraDHCPOpt{ - {OptName: "option2", OptValue: "value2", IPVersion: 4}, - }, - }) th.AssertEquals(t, s.AdminStateUp, true) th.AssertEquals(t, s.Name, "updated-port-with-dhcp-opts") th.AssertEquals(t, s.DeviceOwner, "") @@ -736,4 +735,8 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { }) th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, s.DeviceID, "") + + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option2") + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value2") + th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4) } From 81bac724d837c187b5f9d364fc70fa92f20f2803 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 12 Mar 2018 03:35:00 +0000 Subject: [PATCH 0248/2296] Acc: Add Debug Logging This commit adds the ability to view the OpenStack API JSON requests and responses during acceptance test execution by setting the OS_DEBUG environment variable to 1/true/yes. --- acceptance/clients/clients.go | 49 ++++++++++ acceptance/clients/http.go | 170 ++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 acceptance/clients/http.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index d5c9cccdf1..3a7717d6a9 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -5,6 +5,7 @@ package clients import ( "fmt" + "net/http" "os" "strings" @@ -132,6 +133,8 @@ func NewBlockStorageV1Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -151,6 +154,8 @@ func NewBlockStorageV2Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewBlockStorageV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -170,6 +175,8 @@ func NewBlockStorageV3Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewBlockStorageV3(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -187,6 +194,8 @@ func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) @@ -204,6 +213,8 @@ func NewBlockStorageV3NoAuthClient() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) @@ -223,6 +234,8 @@ func NewComputeV2Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewComputeV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -242,6 +255,8 @@ func NewDBV1Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewDBV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -261,6 +276,8 @@ func NewDNSV2Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewDNSV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -280,6 +297,8 @@ func NewIdentityV2Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewIdentityV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -299,6 +318,8 @@ func NewIdentityV2AdminClient() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewIdentityV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), Availability: gophercloud.AvailabilityAdmin, @@ -319,6 +340,8 @@ func NewIdentityV2UnauthenticatedClient() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewIdentityV2(client, gophercloud.EndpointOpts{}) } @@ -336,6 +359,8 @@ func NewIdentityV3Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewIdentityV3(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -355,6 +380,8 @@ func NewIdentityV3UnauthenticatedClient() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewIdentityV3(client, gophercloud.EndpointOpts{}) } @@ -372,6 +399,8 @@ func NewImageServiceV2Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewImageServiceV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -391,6 +420,8 @@ func NewNetworkV2Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -410,6 +441,8 @@ func NewObjectStorageV1Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewObjectStorageV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -429,7 +462,23 @@ func NewSharedFileSystemV2Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewSharedFileSystemV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } + +// configureDebug will configure the provider client to print the API +// requests and responses if OS_DEBUG is enabled. +func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderClient { + if os.Getenv("OS_DEBUG") != "" { + client.HTTPClient = http.Client{ + Transport: &LogRoundTripper{ + Rt: &http.Transport{}, + }, + } + } + + return client +} diff --git a/acceptance/clients/http.go b/acceptance/clients/http.go new file mode 100644 index 0000000000..3f42231e32 --- /dev/null +++ b/acceptance/clients/http.go @@ -0,0 +1,170 @@ +package clients + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "sort" + "strings" +) + +// List of headers that need to be redacted +var REDACT_HEADERS = []string{"x-auth-token", "x-auth-key", "x-service-token", + "x-storage-token", "x-account-meta-temp-url-key", "x-account-meta-temp-url-key-2", + "x-container-meta-temp-url-key", "x-container-meta-temp-url-key-2", "set-cookie", + "x-subject-token"} + +// LogRoundTripper satisfies the http.RoundTripper interface and is used to +// customize the default http client RoundTripper to allow logging. +type LogRoundTripper struct { + Rt http.RoundTripper +} + +// RoundTrip performs a round-trip HTTP request and logs relevant information +// about it. +func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { + defer func() { + if request.Body != nil { + request.Body.Close() + } + }() + + var err error + + log.Printf("[DEBUG] OpenStack Request URL: %s %s", request.Method, request.URL) + log.Printf("[DEBUG] OpenStack request Headers:\n%s", formatHeaders(request.Header)) + + if request.Body != nil { + request.Body, err = lrt.logRequest(request.Body, request.Header.Get("Content-Type")) + if err != nil { + return nil, err + } + } + + response, err := lrt.Rt.RoundTrip(request) + if response == nil { + return nil, err + } + + log.Printf("[DEBUG] OpenStack Response Code: %d", response.StatusCode) + log.Printf("[DEBUG] OpenStack Response Headers:\n%s", formatHeaders(response.Header)) + + response.Body, err = lrt.logResponse(response.Body, response.Header.Get("Content-Type")) + + return response, err +} + +// logRequest will log the HTTP Request details. +// If the body is JSON, it will attempt to be pretty-formatted. +func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, contentType string) (io.ReadCloser, error) { + defer original.Close() + + var bs bytes.Buffer + _, err := io.Copy(&bs, original) + if err != nil { + return nil, err + } + + // Handle request contentType + if strings.HasPrefix(contentType, "application/json") { + debugInfo := lrt.formatJSON(bs.Bytes()) + log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo) + } else { + log.Printf("[DEBUG] OpenStack Request Body: %s", bs.String()) + } + + return ioutil.NopCloser(strings.NewReader(bs.String())), nil +} + +// logResponse will log the HTTP Response details. +// If the body is JSON, it will attempt to be pretty-formatted. +func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, contentType string) (io.ReadCloser, error) { + if strings.HasPrefix(contentType, "application/json") { + var bs bytes.Buffer + defer original.Close() + _, err := io.Copy(&bs, original) + if err != nil { + return nil, err + } + debugInfo := lrt.formatJSON(bs.Bytes()) + if debugInfo != "" { + log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo) + } + return ioutil.NopCloser(strings.NewReader(bs.String())), nil + } + + log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON") + return original, nil +} + +// formatJSON will try to pretty-format a JSON body. +// It will also mask known fields which contain sensitive information. +func (lrt *LogRoundTripper) formatJSON(raw []byte) string { + var data map[string]interface{} + + err := json.Unmarshal(raw, &data) + if err != nil { + log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err) + return string(raw) + } + + // Mask known password fields + if v, ok := data["auth"].(map[string]interface{}); ok { + if v, ok := v["identity"].(map[string]interface{}); ok { + if v, ok := v["password"].(map[string]interface{}); ok { + if v, ok := v["user"].(map[string]interface{}); ok { + v["password"] = "***" + } + } + } + } + + // Ignore the catalog + if v, ok := data["token"].(map[string]interface{}); ok { + if _, ok := v["catalog"]; ok { + return "" + } + } + + pretty, err := json.MarshalIndent(data, "", " ") + if err != nil { + log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err) + return string(raw) + } + + return string(pretty) +} + +// redactHeaders processes a headers object, returning a redacted list +func redactHeaders(headers http.Header) (processedHeaders []string) { + for name, header := range headers { + var sensitive bool + + for _, redact_header := range REDACT_HEADERS { + if strings.ToLower(name) == strings.ToLower(redact_header) { + sensitive = true + } + } + + for _, v := range header { + if sensitive { + processedHeaders = append(processedHeaders, fmt.Sprintf("%v: %v", name, "***")) + } else { + processedHeaders = append(processedHeaders, fmt.Sprintf("%v: %v", name, v)) + } + } + } + return +} + +// formatHeaders processes a headers object plus a deliminator, returning a string +func formatHeaders(headers http.Header) string { + redactedHeaders := redactHeaders(headers) + sort.Strings(redactedHeaders) + + return strings.Join(redactedHeaders, "\n") +} From bc0882a09253c768051d9201257640794415c5a9 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 14 Mar 2018 01:23:02 +0100 Subject: [PATCH 0249/2296] Vpnaas: Show IPSec site connection details (#821) * Added unit and acceptance tests * Added request and result for Get operation * Added documentation for Get operation * Added various missing bits of documentation --- .../extensions/vpnaas/siteconnection_test.go | 7 ++ .../v2/extensions/vpnaas/ikepolicies/doc.go | 2 +- .../v2/extensions/vpnaas/ipsecpolicies/doc.go | 18 +++++ .../v2/extensions/vpnaas/services/doc.go | 7 ++ .../extensions/vpnaas/siteconnections/doc.go | 15 ++++ .../vpnaas/siteconnections/requests.go | 6 ++ .../vpnaas/siteconnections/results.go | 6 ++ .../siteconnections/testing/requests_test.go | 77 +++++++++++++++++++ 8 files changed, 137 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index a631522aac..c063dc24f5 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" ) func TestConnectionCRUD(t *testing.T) { @@ -92,6 +93,12 @@ func TestConnectionCRUD(t *testing.T) { } defer DeleteSiteConnection(t, client, conn.ID) + newConnection, err := siteconnections.Get(client, conn.ID).Extract() + if err != nil { + t.Fatalf("Unable to get connection: %v", err) + } + tools.PrintResource(t, conn) + tools.PrintResource(t, newConnection) } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index 649027103d..2285aabd3b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -42,7 +42,7 @@ Example to Update an IKE policy } updatedPolicy, err := ikepolicies.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { - t.Fatalf("Unable to update IKE policy: %v", err) + panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go index d2d227f466..13a1636807 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -20,6 +20,24 @@ Example to Delete a Policy panic(err) } +Example to Show the details of a specific IPSec policy by ID + + policy, err := ipsecpolicies.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + if err != nil { + panic(err) + } + +Example to Update an IPSec policy + + updateOpts := ipsecpolicies.UpdateOpts{ + Name: "updatedname", + Description: "updated policy", + } + updatedPolicy, err := ipsecpolicies.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() + if err != nil { + panic(err) + } + Example to List IPSec policies allPages, err := ipsecpolicies.List(client, nil).AllPages() diff --git a/openstack/networking/v2/extensions/vpnaas/services/doc.go b/openstack/networking/v2/extensions/vpnaas/services/doc.go index bf4302a5aa..cb1ef8cabf 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/services/doc.go @@ -44,5 +44,12 @@ Example to Delete a Service panic(err) } +Example to Show the details of a specific Service by ID + + service, err := services.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + 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 b90cca8e57..9311ec61a2 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go @@ -23,5 +23,20 @@ createOpts := siteconnections.CreateOpts{ if err != nil { panic(err) } + +Example to Show the details of a specific IPSec site connection by ID + + conn, err := siteconnections.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + if err != nil { + panic(err) + } + +Example to Delete a site connection + + connID := "38aee955-6283-4279-b091-8b9c828000ec" + err := siteconnections.Delete(networkClient, serviceID).ExtractErr() + if err != nil { + panic(err) + } */ package siteconnections diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index 280f53ee8f..6d79dc1395 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -134,3 +134,9 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } + +// Get retrieves a particular IPSec site connection based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go index 1a7e0dd2fe..351aa0d29a 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go @@ -111,3 +111,9 @@ type CreateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Connection. +type GetResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go index ab68720904..c552091cce 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -136,3 +136,80 @@ func TestDelete(t *testing.T) { res := siteconnections.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` +{ + "ipsec_site_connection": { + "status": "PENDING_CREATE", + "psk": "secret", + "initiator": "bi-directional", + "name": "vpnconnection1", + "admin_state_up": true, + "project_id": "10039663455a446d8ba2cbb058b0f578", + "tenant_id": "10039663455a446d8ba2cbb058b0f578", + "auth_mode": "psk", + "peer_cidrs": [], + "mtu": 1500, + "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "dpd": { + "action": "hold", + "interval": 30, + "timeout": 120 + }, + "route_mode": "static", + "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", + "peer_address": "172.24.4.233", + "peer_id": "172.24.4.233", + "id": "851f280f-5639-4ea3-81aa-e298525ab74b", + "description": "" + } +} + `) + }) + + actual, err := siteconnections.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + th.AssertNoErr(t, err) + expectedDPD := siteconnections.DPD{ + Action: "hold", + Interval: 30, + Timeout: 120, + } + expected := siteconnections.Connection{ + TenantID: "10039663455a446d8ba2cbb058b0f578", + Name: "vpnconnection1", + AdminStateUp: true, + PSK: "secret", + Initiator: "bi-directional", + IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + MTU: 1500, + PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", + PeerAddress: "172.24.4.233", + PeerID: "172.24.4.233", + Status: "PENDING_CREATE", + ProjectID: "10039663455a446d8ba2cbb058b0f578", + AuthMode: "psk", + PeerCIDRs: []string{}, + DPD: expectedDPD, + RouteMode: "static", + ID: "851f280f-5639-4ea3-81aa-e298525ab74b", + Description: "", + } + th.AssertDeepEquals(t, expected, *actual) +} From ab6985d3fcf97fff7583a8813f7d5d9c72b02219 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 14 Mar 2018 16:07:16 -0600 Subject: [PATCH 0250/2296] Docs: Update CONTRIBUTING Guide (#826) * Docs: Update CONTRIBUTING Guide This commit updates the CONTRIBUTING guide so it suggests creating an issue before submitting a PR. * Amending CONTRIBUTING --- .github/CONTRIBUTING.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3092511a3b..d6c894637e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -31,13 +31,15 @@ enthusiasm as any other contribution! ### 3. Working on a new feature -If you've found something we've left out, definitely feel free to start work on -introducing that feature. It's always useful to open an issue or submit a pull -request early on to indicate your intent to a core contributor - this enables -quick/early feedback and can help steer you in the right direction by avoiding -known issues. It might also help you avoid losing time implementing something -that might not ever work. One tip is to prefix your Pull Request issue title -with [wip] - then people know it's a work in progress. +If you've found something we've left out, we'd love for you to add it! Please +first open an issue to indicate your interest to a core contributor - this +enables quick/early feedback and can help steer you in the right direction by +avoiding known issues. It might also help you avoid losing time implementing +something that might not ever work or is outside the scope of the project. + +While you're implementing the feature, one tip is to prefix your Pull Request +title with `[wip]` - then people know it's a work in progress. Once the PR is +ready for review, you can remove the `[wip]` tag and request a review. We ask that you do not submit a feature that you have not spent time researching and testing first-hand in an actual OpenStack environment. While we appreciate From de4b788e8f384b917d879d1d788765f1c752d3e9 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Mar 2018 17:49:47 -0400 Subject: [PATCH 0251/2296] add cascade option to loadbalancers.Delete --- acceptance/clients/clients.go | 19 +++ .../extensions/lbaas_v2/loadbalancers_test.go | 149 ++++++++++++++++++ .../lbaas_v2/loadbalancers/requests.go | 18 ++- .../lbaas_v2/loadbalancers/results.go | 4 +- .../loadbalancers/testing/requests_test.go | 17 ++ 5 files changed, 204 insertions(+), 3 deletions(-) diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 3a7717d6a9..6c548bfb59 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -482,3 +482,22 @@ func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderCli return client } + +// NewLoadBalancerV2Client returns a *ServiceClient for making calls to the +// OpenStack Octavia v2 API. An error will be returned if authentication +// or client creation was not possible. +func NewLoadBalancerV2Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + return openstack.NewLoadBalancerV2(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} 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 650eb2cc49..26064f0c28 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -176,3 +176,152 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newMonitor) } + +func TestOctaviaLoadbalancersCRUD(t *testing.T) { + netClient, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + lbClient, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + network, err := networking.CreateNetwork(t, netClient) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer networking.DeleteNetwork(t, netClient, network.ID) + + subnet, err := networking.CreateSubnet(t, netClient, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer networking.DeleteSubnet(t, netClient, subnet.ID) + + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) + if err != nil { + t.Fatalf("Unable to create loadbalancer: %v", err) + } + defer func() { + t.Logf("Running cascading delete on Octavia LB...") + err := loadbalancers.CascadingDelete(lbClient, lb.ID).ExtractErr() + if err != nil { + t.Fatalf("Error running cascading delete: %v", err) + } + }() + + newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() + if err != nil { + t.Fatalf("Unable to get loadbalancer: %v", err) + } + + tools.PrintResource(t, newLB) + + // Because of the time it takes to create a loadbalancer, + // this test will include some other resources. + + // Listener + listener, err := CreateListener(t, lbClient, lb) + if err != nil { + t.Fatalf("Unable to create listener: %v", err) + } + + updateListenerOpts := listeners.UpdateOpts{ + Description: "Some listener description", + } + _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + if err != nil { + t.Fatalf("Unable to update listener") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newListener, err := listeners.Get(lbClient, listener.ID).Extract() + if err != nil { + t.Fatalf("Unable to get listener") + } + + tools.PrintResource(t, newListener) + + // Pool + pool, err := CreatePool(t, lbClient, lb) + if err != nil { + t.Fatalf("Unable to create pool: %v", err) + } + + updatePoolOpts := pools.UpdateOpts{ + Description: "Some pool description", + } + _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() + if err != nil { + t.Fatalf("Unable to update pool") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newPool, err := pools.Get(lbClient, pool.ID).Extract() + if err != nil { + t.Fatalf("Unable to get pool") + } + + tools.PrintResource(t, newPool) + + // Member + member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) + if err != nil { + t.Fatalf("Unable to create member: %v", err) + } + + newWeight := tools.RandomInt(11, 100) + updateMemberOpts := pools.UpdateMemberOpts{ + Weight: newWeight, + } + _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() + if err != nil { + t.Fatalf("Unable to update pool") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() + if err != nil { + t.Fatalf("Unable to get member") + } + + tools.PrintResource(t, newMember) + + // Monitor + monitor, err := CreateMonitor(t, lbClient, lb, newPool) + if err != nil { + t.Fatalf("Unable to create monitor: %v", err) + } + + newDelay := tools.RandomInt(20, 30) + updateMonitorOpts := monitors.UpdateOpts{ + Delay: newDelay, + } + _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() + if err != nil { + t.Fatalf("Unable to update monitor") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() + if err != nil { + t.Fatalf("Unable to get monitor") + } + + tools.PrintResource(t, newMonitor) + +} diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index 49ec9ecac3..1ed23c3c82 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -1,6 +1,8 @@ package loadbalancers import ( + "fmt" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -36,7 +38,7 @@ type ListOpts struct { SortDir string `q:"sort_dir"` } -// ToLoadbalancerListQuery formats a ListOpts into a query string. +// ToLoadBalancerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToLoadBalancerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err @@ -175,6 +177,20 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { return } +// CascadingDelete is like `Delete`, but will also delete any of the load balancer's +// children (listener, monitor, etc). +// NOTE: This function will only work with Octavia load balancers; Neutron does not +// support this. +func CascadingDelete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + if c.Type != "load-balancer" { + r.Err = fmt.Errorf("error prior to running cascade delete: only Octavia LBs supported") + return + } + u := fmt.Sprintf("%s?cascade=true", resourceURL(c, id)) + _, r.Err = c.Delete(u, nil) + return +} + // GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go index 9f8f19d7c5..42fff57131 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go @@ -79,8 +79,8 @@ func (r LoadBalancerPage) NextPageURL() (string, error) { } // IsEmpty checks whether a LoadBalancerPage struct is empty. -func (p LoadBalancerPage) IsEmpty() (bool, error) { - is, err := ExtractLoadBalancers(p) +func (r LoadBalancerPage) IsEmpty() (bool, error) { + is, err := ExtractLoadBalancers(r) return len(is) == 0, err } diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go index 270bdf5a66..e370c669bf 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go @@ -142,3 +142,20 @@ func TestUpdateLoadbalancer(t *testing.T) { th.CheckDeepEquals(t, LoadbalancerUpdated, *actual) } + +func TestCascadingDeleteLoadbalancer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerDeletionSuccessfully(t) + + sc := fake.ServiceClient() + sc.Type = "network" + err := loadbalancers.CascadingDelete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() + if err == nil { + t.Fatalf("expected error running CascadingDelete with Neutron service client but didn't get one") + } + + sc.Type = "load-balancer" + err = loadbalancers.CascadingDelete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() + th.AssertNoErr(t, err) +} From 08084679db8635a7b53f445cdfd1414024b4938d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 14 Mar 2018 20:44:07 -0600 Subject: [PATCH 0252/2296] Networking v2: Port Security Update (#808) * Networking v2: Port Security Update * Networking v2: Port Security Create and Update Docs --- .../openstack/networking/v2/networks_test.go | 14 +++ .../openstack/networking/v2/ports_test.go | 14 +++ .../v2/extensions/portsecurity/doc.go | 103 +++++++++++++++++- .../v2/extensions/portsecurity/requests.go | 49 +++++++++ .../v2/networks/testing/fixtures.go | 24 ++++ .../v2/networks/testing/requests_test.go | 39 +++++++ .../networking/v2/ports/testing/fixtures.go | 40 +++++++ .../v2/ports/testing/requests_test.go | 37 +++++++ 8 files changed, 317 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index 2ff00172bc..ab4c2b1ce9 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -96,4 +96,18 @@ func TestNetworksPortSecurityCRUD(t *testing.T) { } tools.PrintResource(t, networkWithExtensions) + + iTrue := true + networkUpdateOpts := networks.UpdateOpts{} + updateOpts := portsecurity.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + PortSecurityEnabled: &iTrue, + } + + err = networks.Update(client, network.ID, updateOpts).ExtractInto(&networkWithExtensions) + if err != nil { + t.Fatalf("Unable to update network: %v", err) + } + + tools.PrintResource(t, networkWithExtensions) } diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index d47bc3a196..1d4a9d757b 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -388,6 +388,20 @@ func TestPortsPortSecurityCRUD(t *testing.T) { } tools.PrintResource(t, portWithExt) + + iTrue := true + portUpdateOpts := ports.UpdateOpts{} + updateOpts := portsecurity.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + PortSecurityEnabled: &iTrue, + } + + err = ports.Update(client, port.ID, updateOpts).ExtractInto(&portWithExt) + if err != nil { + t.Fatalf("Unable to update port: %v", err) + } + + tools.PrintResource(t, portWithExt) } func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { diff --git a/openstack/networking/v2/extensions/portsecurity/doc.go b/openstack/networking/v2/extensions/portsecurity/doc.go index 9de4fcf750..2b9a391681 100644 --- a/openstack/networking/v2/extensions/portsecurity/doc.go +++ b/openstack/networking/v2/extensions/portsecurity/doc.go @@ -29,20 +29,117 @@ Example to List Networks with Port Security Information fmt.Println("%+v\n", network) } +Example to Create a Network without Port Security + + var networkWithPortSecurityExt struct { + networks.Network + portsecurity.PortSecurityExt + } + + networkCreateOpts := networks.CreateOpts{ + Name: "private", + } + + iFalse := false + createOpts := portsecurity.NetworkCreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + PortSecurityEnabled: &iFalse, + } + + err := networks.Create(networkClient, createOpts).ExtractInto(&networkWithPortSecurityExt) + if err != nil { + panic(err) + } + + fmt.Println("%+v\n", networkWithPortSecurityExt) + +Example to Disable Port Security on an Existing Network + + var networkWithPortSecurityExt struct { + networks.Network + portsecurity.PortSecurityExt + } + + iFalse := false + networkID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + networkUpdateOpts := networks.UpdateOpts{} + updateOpts := portsecurity.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + PortSecurityEnabled: &iFalse, + } + + err := networks.Update(networkClient, networkID, updateOpts).ExtractInto(&networkWithPortSecurityExt) + if err != nil { + panic(err) + } + + fmt.Println("%+v\n", networkWithPortSecurityExt) + Example to Get a Port with Port Security Information - var portWithExtensions struct { + var portWithPortSecurityExtensions struct { ports.Port portsecurity.PortSecurityExt } portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" - err := ports.Get(networkingClient, portID).ExtractInto(&portWithExtensions) + err := ports.Get(networkingClient, portID).ExtractInto(&portWithPortSecurityExtensions) + if err != nil { + panic(err) + } + + fmt.Println("%+v\n", portWithPortSecurityExtensions) + +Example to Create a Port Without Port Security + + var portWithPortSecurityExtensions struct { + ports.Port + portsecurity.PortSecurityExt + } + + iFalse := false + networkID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + subnetID := "a87cc70a-3e15-4acf-8205-9b711a3531b7" + + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + } + + createOpts := portsecurity.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + PortSecurityEnabled: &iFalse, + } + + err := ports.Create(networkingClient, createOpts).ExtractInto(&portWithPortSecurityExtensions) + if err != nil { + panic(err) + } + + fmt.Println("%+v\n", portWithPortSecurityExtensions) + +Example to Disable Port Security on an Existing Port + + var portWithPortSecurityExtensions struct { + ports.Port + portsecurity.PortSecurityExt + } + + iFalse := false + portID := "65c0ee9f-d634-4522-8954-51021b570b0d" + + portUpdateOpts := ports.UpdateOpts{} + updateOpts := portsecurity.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + PortSecurityEnabled: &iFalse, + } + + err := ports.Update(networkingClient, portID, updateOpts).ExtractInto(&portWithPortSecurityExtensions) if err != nil { panic(err) } - fmt.Println("%+v\n", portWithExtensions) + fmt.Println("%+v\n", portWithPortSecurityExtensions) */ package portsecurity diff --git a/openstack/networking/v2/extensions/portsecurity/requests.go b/openstack/networking/v2/extensions/portsecurity/requests.go index 781353ee37..c80f47cf61 100644 --- a/openstack/networking/v2/extensions/portsecurity/requests.go +++ b/openstack/networking/v2/extensions/portsecurity/requests.go @@ -29,6 +29,30 @@ func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) return base, nil } +// PortUpdateOptsExt adds port security options to the base ports.UpdateOpts. +type PortUpdateOptsExt struct { + ports.UpdateOptsBuilder + + // PortSecurityEnabled toggles port security on a port. + PortSecurityEnabled *bool `json:"port_security_enabled,omitempty"` +} + +// ToPortUpdateMap casts a UpdateOpts struct to a map. +func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]interface{}) + + if opts.PortSecurityEnabled != nil { + port["port_security_enabled"] = &opts.PortSecurityEnabled + } + + return base, nil +} + // NetworkCreateOptsExt adds port security options to the base // networks.CreateOpts. type NetworkCreateOptsExt struct { @@ -53,3 +77,28 @@ func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, e return base, nil } + +// NetworkUpdateOptsExt adds port security options to the base +// networks.UpdateOpts. +type NetworkUpdateOptsExt struct { + networks.UpdateOptsBuilder + + // PortSecurityEnabled toggles port security on a port. + PortSecurityEnabled *bool `json:"port_security_enabled,omitempty"` +} + +// ToNetworkUpdateMap casts a UpdateOpts struct to a map. +func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() + if err != nil { + return nil, err + } + + network := base["network"].(map[string]interface{}) + + if opts.PortSecurityEnabled != nil { + network["port_security_enabled"] = &opts.PortSecurityEnabled + } + + return base, nil +} diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index e4f6b6bd02..9632d448a5 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -148,6 +148,30 @@ const UpdateResponse = ` } }` +const UpdatePortSecurityRequest = ` +{ + "network": { + "port_security_enabled": false + } +}` + +const UpdatePortSecurityResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local", + "port_security_enabled": false + } +}` + var Network1 = networks.Network{ Status: "ACTIVE", Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 1bfafaae8d..231d7f087c 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -254,3 +254,42 @@ func TestCreatePortSecurity(t *testing.T) { th.AssertEquals(t, networkWithExtensions.Status, "ACTIVE") th.AssertEquals(t, networkWithExtensions.PortSecurityEnabled, false) } + +func TestUpdatePortSecurity(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.TestJSONRequest(t, r, UpdatePortSecurityRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdatePortSecurityResponse) + }) + + var networkWithExtensions struct { + networks.Network + portsecurity.PortSecurityExt + } + + iFalse := false + networkUpdateOpts := networks.UpdateOpts{} + updateOpts := portsecurity.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + PortSecurityEnabled: &iFalse, + } + + err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", updateOpts).ExtractInto(&networkWithExtensions) + th.AssertNoErr(t, err) + + th.AssertEquals(t, networkWithExtensions.Name, "private") + th.AssertEquals(t, networkWithExtensions.AdminStateUp, true) + th.AssertEquals(t, networkWithExtensions.Shared, false) + th.AssertEquals(t, networkWithExtensions.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c") + th.AssertEquals(t, networkWithExtensions.PortSecurityEnabled, false) +} diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index 870572cb64..28c37f7237 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -381,6 +381,46 @@ const UpdateOmitSecurityGroupsResponse = ` } ` +const UpdatePortSecurityRequest = ` +{ + "port": { + "port_security_enabled": false + } +} +` + +const UpdatePortSecurityResponse = ` +{ + "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" + } + ], + "device_id": "", + "port_security_enabled": false + } +} +` + const RemoveSecurityGroupRequest = ` { "port": { diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 7df21c7a35..7abeb5f965 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -441,6 +441,43 @@ func TestUpdateOmitSecurityGroups(t *testing.T) { th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) } +func TestUpdatePortSecurity(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, UpdatePortSecurityRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdatePortSecurityResponse) + }) + + var portWithExt struct { + ports.Port + portsecurity.PortSecurityExt + } + + iFalse := false + portUpdateOpts := ports.UpdateOpts{} + updateOpts := portsecurity.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + PortSecurityEnabled: &iFalse, + } + + err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithExt) + th.AssertNoErr(t, err) + + th.AssertEquals(t, portWithExt.Status, "DOWN") + th.AssertEquals(t, portWithExt.Name, "private-port") + th.AssertEquals(t, portWithExt.PortSecurityEnabled, false) +} + func TestRemoveSecurityGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 3aef8e417612ddbdd8f7a76004af4307296cd1b6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 20 Mar 2018 18:01:40 -0600 Subject: [PATCH 0253/2296] Acc Compute v2: Updating the Compute v2 Acceptance Tests (#820) * Acc: Adding condition to require admin user * Acc Compute v2: Updating aggregates tests * Acc Compute v2: Updating attachinterfaces tests * Acc Compute v2: Updating availabilityzone tests * Acc Compute v2: Removing short test check in compute.go * Acc Compute v2: Updating bootfromvolume tests * Acc Compute v2: Updating defsecrules tests * Acc Compute v2: Updating extensions tests * Acc Compute v2: Updating flavors tests * Acc Compute v2: Updating floatingips tests * Acc Compute v2: Updating hypervisors tests * Acc Compute v2: Updating images tests * Acc Compute v2: Updating keypairs tests * Acc Compute v2: Updating limits tests * Acc Compute v2: Updating migration tests * Acc Compute v2: Updating network tests * Acc Compute v2: Updating quotaset tests * Acc Compute v2: Simplifying assertions * Acc Compute v2: Updating servers tests * Acc Compute v2: Updating services tests * Acc Compute v2: Updating tenantnetworks tests * Acc Compute v2: Updating usage tests * Acc Compute v2: Updating volumeattach tests * Acc Compute v2: Adding RequireLong convenience function * Acc Compute v2: Cleaning up convenience functions * Acc Compute v2: Suppressing error message verbosity * Acc Compute v2: Updating secgroups tests --- acceptance/clients/clients.go | 9 - acceptance/clients/conditions.go | 44 +++ .../openstack/blockstorage/v2/blockstorage.go | 15 +- .../openstack/compute/v2/aggregates_test.go | 152 +++----- .../compute/v2/attachinterfaces_test.go | 59 ++- .../compute/v2/availabilityzones_test.go | 41 +- .../compute/v2/bootfromvolume_test.go | 169 ++++---- acceptance/openstack/compute/v2/compute.go | 278 +++++++++----- .../openstack/compute/v2/defsecrules_test.go | 42 +- .../openstack/compute/v2/extension_test.go | 32 +- .../openstack/compute/v2/flavors_test.go | 188 ++++----- .../openstack/compute/v2/floatingip_test.go | 105 ++--- .../openstack/compute/v2/hypervisors_test.go | 73 ++-- .../openstack/compute/v2/images_test.go | 37 +- .../openstack/compute/v2/keypairs_test.go | 83 ++-- .../openstack/compute/v2/limits_test.go | 30 +- .../openstack/compute/v2/migrate_test.go | 39 +- .../openstack/compute/v2/network_test.go | 41 +- .../openstack/compute/v2/quotaset_test.go | 99 ++--- .../openstack/compute/v2/secgroup_test.go | 143 ++++--- .../openstack/compute/v2/servergroup_test.go | 80 ++-- .../openstack/compute/v2/servers_test.go | 363 +++++++----------- .../openstack/compute/v2/services_test.go | 22 +- .../compute/v2/tenantnetworks_test.go | 39 +- acceptance/openstack/compute/v2/usage_test.go | 31 +- .../openstack/compute/v2/volumeattach_test.go | 64 +-- 26 files changed, 1064 insertions(+), 1214 deletions(-) create mode 100644 acceptance/clients/conditions.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 6c548bfb59..eee80005ef 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -43,9 +43,6 @@ type AcceptanceTestChoices struct { // DBDatastoreTypeID is the datastore type version for DB tests. DBDatastoreVersion string - - // LiveMigrate indicates ability to run multi-node migration tests - LiveMigrate bool } // AcceptanceTestChoicesFromEnv populates a ComputeChoices struct from environment variables. @@ -61,11 +58,6 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { dbDatastoreType := os.Getenv("OS_DB_DATASTORE_TYPE") dbDatastoreVersion := os.Getenv("OS_DB_DATASTORE_VERSION") - var liveMigrate bool - if v := os.Getenv("OS_LIVE_MIGRATE"); v != "" { - liveMigrate = true - } - missing := make([]string, 0, 3) if imageID == "" { missing = append(missing, "OS_IMAGE_ID") @@ -115,7 +107,6 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { ShareNetworkID: shareNetworkID, DBDatastoreType: dbDatastoreType, DBDatastoreVersion: dbDatastoreVersion, - LiveMigrate: liveMigrate, }, nil } diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go new file mode 100644 index 0000000000..9c62c29c11 --- /dev/null +++ b/acceptance/clients/conditions.go @@ -0,0 +1,44 @@ +package clients + +import ( + "os" + "testing" +) + +// RequireAdmin will restrict a test to only be run by admin users. +func RequireAdmin(t *testing.T) { + if os.Getenv("OS_USERNAME") != "admin" { + t.Skip("must be admin to run this test") + } +} + +// RequireGuestAgent will restrict a test to only be run in +// environments that support the QEMU guest agent. +func RequireGuestAgent(t *testing.T) { + if os.Getenv("OS_GUEST_AGENT") == "" { + t.Skip("this test requires support for qemu guest agent and to set OS_GUEST_AGENT to 1") + } +} + +// RequireLiveMigration will restrict a test to only be run in +// environments that support live migration. +func RequireLiveMigration(t *testing.T) { + if os.Getenv("OS_LIVE_MIGRATE") == "" { + t.Skip("this test requires support for live migration and to set OS_LIVE_MIGRATE to 1") + } +} + +// RequireLong will ensure long-running tests can run. +func RequireLong(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode") + } +} + +// RequireNovaNetwork will restrict a test to only be run in +// environments that support nova-network. +func RequireNovaNetwork(t *testing.T) { + if os.Getenv("OS_NOVANET") == "" { + t.Skip("this test requires nova-network and to set OS_NOVANET to 1") + } +} diff --git a/acceptance/openstack/blockstorage/v2/blockstorage.go b/acceptance/openstack/blockstorage/v2/blockstorage.go index 51c8e59cad..7b4682bbf1 100644 --- a/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateVolume will create a volume with a random name and size of 1GB. An @@ -44,10 +45,6 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol // CreateVolumeFromImage will create a volume from with a random name and size of // 1GB. An error will be returned if the volume was unable to be created. func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { - if testing.Short() { - t.Skip("Skipping test that requires volume creation in short mode.") - } - choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) @@ -72,7 +69,15 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo return volume, err } - return volume, nil + newVolume, err := volumes.Get(client, volume.ID).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, newVolume.Name, volumeName) + th.AssertEquals(t, newVolume.Size, 1) + + return newVolume, nil } // DeleteVolume will delete a volume. A fatal error will occur if the volume diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index 7209831c3d..352adb38e3 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -11,174 +11,132 @@ import ( "github.com/gophercloud/gophercloud/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" ) func TestAggregatesList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := aggregates.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list aggregates: %v", err) - } + th.AssertNoErr(t, err) allAggregates, err := aggregates.ExtractAggregates(allPages) - if err != nil { - t.Fatalf("Unable to extract aggregates") - } + th.AssertNoErr(t, err) - for _, h := range allAggregates { - tools.PrintResource(t, h) + for _, v := range allAggregates { + tools.PrintResource(t, v) } } -func TestAggregatesCreateDelete(t *testing.T) { - client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } +func TestAggregatesCRUD(t *testing.T) { + clients.RequireAdmin(t) - createdAggregate, err := CreateAggregate(t, client) - if err != nil { - t.Fatalf("Unable to create an aggregate: %v", err) - } - defer DeleteAggregate(t, client, createdAggregate) - - tools.PrintResource(t, createdAggregate) -} - -func TestAggregatesGet(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) - createdAggregate, err := CreateAggregate(t, client) - if err != nil { - t.Fatalf("Unable to create an aggregate: %v", err) - } - defer DeleteAggregate(t, client, createdAggregate) + aggregate, err := CreateAggregate(t, client) + th.AssertNoErr(t, err) - aggregate, err := aggregates.Get(client, createdAggregate.ID).Extract() - if err != nil { - t.Fatalf("Unable to get an aggregate: %v", err) - } + defer DeleteAggregate(t, client, aggregate) tools.PrintResource(t, aggregate) -} - -func TestAggregatesUpdate(t *testing.T) { - client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } - - createdAggregate, err := CreateAggregate(t, client) - if err != nil { - t.Fatalf("Unable to create an aggregate: %v", err) - } - defer DeleteAggregate(t, client, createdAggregate) updateOpts := aggregates.UpdateOpts{ Name: "new_aggregate_name", AvailabilityZone: "new_azone", } - updatedAggregate, err := aggregates.Update(client, createdAggregate.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update an aggregate: %v", err) - } + updatedAggregate, err := aggregates.Update(client, aggregate.ID, updateOpts).Extract() + th.AssertNoErr(t, err) - tools.PrintResource(t, updatedAggregate) + tools.PrintResource(t, aggregate) + + th.AssertEquals(t, updatedAggregate.Name, "new_aggregate_name") + th.AssertEquals(t, updatedAggregate.AvailabilityZone, "new_azone") } func TestAggregatesAddRemoveHost(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) hostToAdd, err := getHypervisor(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) - createdAggregate, err := CreateAggregate(t, client) - if err != nil { - t.Fatalf("Unable to create an aggregate: %v", err) - } - defer DeleteAggregate(t, client, createdAggregate) + aggregate, err := CreateAggregate(t, client) + th.AssertNoErr(t, err) + defer DeleteAggregate(t, client, aggregate) addHostOpts := aggregates.AddHostOpts{ Host: hostToAdd.HypervisorHostname, } - aggregateWithNewHost, err := aggregates.AddHost(client, createdAggregate.ID, addHostOpts).Extract() - if err != nil { - t.Fatalf("Unable to add host to aggregate: %v", err) - } + aggregateWithNewHost, err := aggregates.AddHost(client, aggregate.ID, addHostOpts).Extract() + th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithNewHost) + th.AssertEquals(t, aggregateWithNewHost.Hosts[0], hostToAdd.HypervisorHostname) + removeHostOpts := aggregates.RemoveHostOpts{ Host: hostToAdd.HypervisorHostname, } - aggregateWithRemovedHost, err := aggregates.RemoveHost(client, createdAggregate.ID, removeHostOpts).Extract() - if err != nil { - t.Fatalf("Unable to remove host from aggregate: %v", err) - } + aggregateWithRemovedHost, err := aggregates.RemoveHost(client, aggregate.ID, removeHostOpts).Extract() + th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithRemovedHost) + + th.AssertEquals(t, len(aggregateWithRemovedHost.Hosts), 0) } func TestAggregatesSetRemoveMetadata(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) - createdAggregate, err := CreateAggregate(t, client) - if err != nil { - t.Fatalf("Unable to create an aggregate: %v", err) - } - defer DeleteAggregate(t, client, createdAggregate) + aggregate, err := CreateAggregate(t, client) + th.AssertNoErr(t, err) + defer DeleteAggregate(t, client, aggregate) opts := aggregates.SetMetadataOpts{ Metadata: map[string]interface{}{"key": "value"}, } - aggregateWithMetadata, err := aggregates.SetMetadata(client, createdAggregate.ID, opts).Extract() - if err != nil { - t.Fatalf("Unable to set metadata to aggregate: %v", err) - } + aggregateWithMetadata, err := aggregates.SetMetadata(client, aggregate.ID, opts).Extract() + th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithMetadata) + if _, ok := aggregateWithMetadata.Metadata["key"]; !ok { + t.Fatalf("aggregate %s did not contain metadata", aggregateWithMetadata.Name) + } + optsToRemove := aggregates.SetMetadataOpts{ Metadata: map[string]interface{}{"key": nil}, } - aggregateWithRemovedKey, err := aggregates.SetMetadata(client, createdAggregate.ID, optsToRemove).Extract() - if err != nil { - t.Fatalf("Unable to set metadata to aggregate: %v", err) - } + aggregateWithRemovedKey, err := aggregates.SetMetadata(client, aggregate.ID, optsToRemove).Extract() + th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithRemovedKey) + + if _, ok := aggregateWithRemovedKey.Metadata["key"]; ok { + t.Fatalf("aggregate %s still contains metadata", aggregateWithRemovedKey.Name) + } } func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (*hypervisors.Hypervisor, error) { allPages, err := hypervisors.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list hypervisors: %v", err) - } + th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) - if err != nil { - t.Fatal("Unable to extract hypervisors") - } + th.AssertNoErr(t, err) for _, h := range allHypervisors { return &h, nil diff --git a/acceptance/openstack/compute/v2/attachinterfaces_test.go b/acceptance/openstack/compute/v2/attachinterfaces_test.go index 766a3aec8e..b8f9680285 100644 --- a/acceptance/openstack/compute/v2/attachinterfaces_test.go +++ b/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -7,54 +7,45 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestAttachDetachInterface(t *testing.T) { + clients.RequireLong(t) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } - + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - newServer, err := servers.Get(client, server.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve server: %v", err) - } - tools.PrintResource(t, newServer) - - intOpts := attachinterfaces.CreateOpts{} - - iface, err := attachinterfaces.Create(client, server.ID, intOpts).Extract() - if err != nil { - t.Fatal(err) - } + iface, err := AttachInterface(t, client, server.ID) + th.AssertNoErr(t, err) + defer DetachInterface(t, client, server.ID, iface.PortID) tools.PrintResource(t, iface) - allPages, err := attachinterfaces.List(client, server.ID).AllPages() - if err != nil { - t.Fatal(err) - } + server, err = servers.Get(client, server.ID).Extract() + th.AssertNoErr(t, err) - allIfaces, err := attachinterfaces.ExtractInterfaces(allPages) - if err != nil { - t.Fatal(err) - } + var found bool + for _, networkAddresses := range server.Addresses[choices.NetworkName].([]interface{}) { + address := networkAddresses.(map[string]interface{}) + if address["OS-EXT-IPS:type"] == "fixed" { + fixedIP := address["addr"].(string) - for _, i := range allIfaces { - tools.PrintResource(t, i) + for _, v := range iface.FixedIPs { + if fixedIP == v.IPAddress { + found = true + } + } + } } - err = attachinterfaces.Delete(client, server.ID, iface.PortID).ExtractErr() - if err != nil { - t.Fatal(err) - } + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/compute/v2/availabilityzones_test.go b/acceptance/openstack/compute/v2/availabilityzones_test.go index 3e82128422..4d030c2968 100644 --- a/acceptance/openstack/compute/v2/availabilityzones_test.go +++ b/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -8,46 +8,51 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestAvailabilityZonesList(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := availabilityzones.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list availability zones info: %v", err) - } + th.AssertNoErr(t, err) availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) - if err != nil { - t.Fatalf("Unable to extract availability zones info: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, zoneInfo := range availabilityZoneInfo { tools.PrintResource(t, zoneInfo) + + if zoneInfo.ZoneName == "nova" { + found = true + } } + + th.AssertEquals(t, found, true) } func TestAvailabilityZonesListDetail(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := availabilityzones.ListDetail(client).AllPages() - if err != nil { - t.Fatalf("Unable to list availability zones detailed info: %v", err) - } + th.AssertNoErr(t, err) availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) - if err != nil { - t.Fatalf("Unable to extract availability zones detailed info: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, zoneInfo := range availabilityZoneInfo { tools.PrintResource(t, zoneInfo) + + if zoneInfo.ZoneName == "nova" { + found = true + } } + + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go index 2ba8888bf2..50a2396f94 100644 --- a/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -9,22 +9,18 @@ import ( blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" "github.com/gophercloud/gophercloud/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" ) func TestBootFromImage(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ @@ -37,28 +33,22 @@ func TestBootFromImage(t *testing.T) { } server, err := CreateBootableVolumeServer(t, client, blockDevices) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) tools.PrintResource(t, server) + + th.AssertEquals(t, server.Image["id"], choices.ImageID) } func TestBootFromNewVolume(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ @@ -71,33 +61,40 @@ func TestBootFromNewVolume(t *testing.T) { } server, err := CreateBootableVolumeServer(t, client, blockDevices) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) + attachPages, err := volumeattach.List(client, server.ID).AllPages() + th.AssertNoErr(t, err) + + attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) + th.AssertNoErr(t, err) + tools.PrintResource(t, server) + tools.PrintResource(t, attachments) + + if server.Image != nil { + t.Fatalf("server image should be nil") + } + + th.AssertEquals(t, len(attachments), 1) + + // TODO: volumes_attached extension } func TestBootFromExistingVolume(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) computeClient, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) blockStorageClient, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a block storage client: %v", err) - } + th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolumeFromImage(t, blockStorageClient) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) + + tools.PrintResource(t, volume) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ @@ -109,28 +106,35 @@ func TestBootFromExistingVolume(t *testing.T) { } server, err := CreateBootableVolumeServer(t, computeClient, blockDevices) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, computeClient, server) + attachPages, err := volumeattach.List(computeClient, server.ID).AllPages() + th.AssertNoErr(t, err) + + attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) + th.AssertNoErr(t, err) + tools.PrintResource(t, server) + tools.PrintResource(t, attachments) + + if server.Image != nil { + t.Fatalf("server image should be nil") + } + + th.AssertEquals(t, len(attachments), 1) + th.AssertEquals(t, attachments[0].VolumeID, volume.ID) + // TODO: volumes_attached extension } func TestBootFromMultiEphemeralServer(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ @@ -160,28 +164,20 @@ func TestBootFromMultiEphemeralServer(t *testing.T) { } server, err := CreateMultiEphemeralServer(t, client, blockDevices) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) tools.PrintResource(t, server) } func TestAttachNewVolume(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ @@ -201,38 +197,38 @@ func TestAttachNewVolume(t *testing.T) { } server, err := CreateBootableVolumeServer(t, client, blockDevices) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) + attachPages, err := volumeattach.List(client, server.ID).AllPages() + th.AssertNoErr(t, err) + + attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) + th.AssertNoErr(t, err) + tools.PrintResource(t, server) + tools.PrintResource(t, attachments) + + th.AssertEquals(t, server.Image["id"], choices.ImageID) + th.AssertEquals(t, len(attachments), 1) + + // TODO: volumes_attached extension } func TestAttachExistingVolume(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) computeClient, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) blockStorageClient, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a block storage client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockStorageClient) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ @@ -252,10 +248,21 @@ func TestAttachExistingVolume(t *testing.T) { } server, err := CreateBootableVolumeServer(t, computeClient, blockDevices) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, computeClient, server) + attachPages, err := volumeattach.List(computeClient, server.ID).AllPages() + th.AssertNoErr(t, err) + + attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) + th.AssertNoErr(t, err) + tools.PrintResource(t, server) + tools.PrintResource(t, attachments) + + th.AssertEquals(t, server.Image["id"], choices.ImageID) + th.AssertEquals(t, len(attachments), 1) + th.AssertEquals(t, attachments[0].VolumeID, volume.ID) + + // TODO: volumes_attached extension } diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 77b3d7d2f8..cdcbf0bb9e 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -11,7 +11,8 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" @@ -25,6 +26,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "golang.org/x/crypto/ssh" @@ -64,14 +66,69 @@ func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceCli return nil } +// AttachInterface will create and attach an interface on a given server. +// An error will returned if the interface could not be created. +func AttachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*attachinterfaces.Interface, error) { + t.Logf("Attempting to attach interface to server %s", serverID) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) + if err != nil { + return nil, err + } + + createOpts := attachinterfaces.CreateOpts{ + NetworkID: networkID, + } + + iface, err := attachinterfaces.Create(client, serverID, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created interface %s on server %s", iface.PortID, serverID) + + return iface, nil +} + +// CreateAggregate will create an aggregate with random name and available zone. +// An error will be returned if the aggregate could not be created. +func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregates.Aggregate, error) { + aggregateName := tools.RandomString("aggregate_", 5) + availabilityZone := tools.RandomString("zone_", 5) + t.Logf("Attempting to create aggregate %s", aggregateName) + + createOpts := aggregates.CreateOpts{ + Name: aggregateName, + AvailabilityZone: availabilityZone, + } + + aggregate, err := aggregates.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created aggregate %d", aggregate.ID) + + aggregate, err = aggregates.Get(client, aggregate.ID).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, aggregate.Name, aggregateName) + th.AssertEquals(t, aggregate.AvailabilityZone, availabilityZone) + + return aggregate, nil +} + // CreateBootableVolumeServer works like CreateServer but is configured with // one or more block devices defined by passing in []bootfromvolume.BlockDevice. // An error will be returned if a server was unable to be created. func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } - var server *servers.Server choices, err := clients.AcceptanceTestChoicesFromEnv() @@ -113,6 +170,12 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, } newServer, err := servers.Get(client, server.ID).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, newServer.Name, name) + th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) return newServer, nil } @@ -160,6 +223,12 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Fla t.Logf("Successfully created flavor %s", flavor.ID) + th.AssertEquals(t, flavor.Name, flavorName) + th.AssertEquals(t, flavor.RAM, 1) + th.AssertEquals(t, flavor.Disk, 1) + th.AssertEquals(t, flavor.VCPUs, 1) + th.AssertEquals(t, flavor.IsPublic, true) + return flavor, nil } @@ -216,6 +285,9 @@ func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.K } t.Logf("Created keypair: %s", keyPairName) + + th.AssertEquals(t, keyPair.Name, keyPairName) + return keyPair, nil } @@ -225,10 +297,6 @@ func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.K // are actually local ephemeral disks. // An error will be returned if a server was unable to be created. func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } - var server *servers.Server choices, err := clients.AcceptanceTestChoicesFromEnv() @@ -268,6 +336,10 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, newServer, err := servers.Get(client, server.ID).Extract() + th.AssertEquals(t, newServer.Name, name) + th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) + th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + return newServer, nil } @@ -293,45 +365,63 @@ func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flav t.Logf("Successfully created flavor %s", flavor.ID) + th.AssertEquals(t, flavor.Name, flavorName) + th.AssertEquals(t, flavor.RAM, 1) + th.AssertEquals(t, flavor.Disk, 1) + th.AssertEquals(t, flavor.VCPUs, 1) + th.AssertEquals(t, flavor.IsPublic, false) + return flavor, nil } // CreateSecurityGroup will create a security group with a random name. // An error will be returned if one was failed to be created. -func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (secgroups.SecurityGroup, error) { +func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*secgroups.SecurityGroup, error) { + name := tools.RandomString("secgroup_", 5) + createOpts := secgroups.CreateOpts{ - Name: tools.RandomString("secgroup_", 5), + Name: name, Description: "something", } securityGroup, err := secgroups.Create(client, createOpts).Extract() if err != nil { - return *securityGroup, err + return nil, err } t.Logf("Created security group: %s", securityGroup.ID) - return *securityGroup, nil + + th.AssertEquals(t, securityGroup.Name, name) + + return securityGroup, nil } // CreateSecurityGroupRule will create a security group rule with a random name // and a random TCP port range between port 80 and 99. An error will be // returned if the rule failed to be created. -func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (secgroups.Rule, error) { +func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (*secgroups.Rule, error) { + fromPort := tools.RandomInt(80, 89) + toPort := tools.RandomInt(90, 99) createOpts := secgroups.CreateRuleOpts{ ParentGroupID: securityGroupID, - FromPort: tools.RandomInt(80, 89), - ToPort: tools.RandomInt(90, 99), + FromPort: fromPort, + ToPort: toPort, IPProtocol: "TCP", CIDR: "0.0.0.0/0", } rule, err := secgroups.CreateRule(client, createOpts).Extract() if err != nil { - return *rule, err + return nil, err } t.Logf("Created security group rule: %s", rule.ID) - return *rule, nil + + th.AssertEquals(t, rule.FromPort, fromPort) + th.AssertEquals(t, rule.ToPort, toPort) + th.AssertEquals(t, rule.ParentGroupID, securityGroupID) + + return rule, nil } // CreateServer creates a basic instance with a randomly generated name. @@ -340,12 +430,6 @@ func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, se // The instance will be launched on the network specified in OS_NETWORK_NAME. // An error will be returned if the instance was unable to be created. func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } - - var server *servers.Server - choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) @@ -353,7 +437,7 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { - return server, err + return nil, err } name := tools.RandomString("ACPTTEST", 16) @@ -361,7 +445,7 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser pwd := tools.MakeNewPassword("") - server, err = servers.Create(client, servers.CreateOpts{ + server, err := servers.Create(client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, @@ -384,10 +468,19 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { - return server, err + return nil, err } - return server, nil + newServer, err := servers.Get(client, server.ID).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, newServer.Name, name) + th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) + th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + + return newServer, nil } // CreateServerWithoutImageRef creates a basic instance with a randomly generated name. @@ -396,12 +489,6 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser // The instance will be launched on the network specified in OS_NETWORK_NAME. // An error will be returned if the instance was unable to be created. func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } - - var server *servers.Server - choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) @@ -409,7 +496,7 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { - return server, err + return nil, err } name := tools.RandomString("ACPTTEST", 16) @@ -417,7 +504,7 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient pwd := tools.MakeNewPassword("") - server, err = servers.Create(client, servers.CreateOpts{ + server, err := servers.Create(client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, AdminPass: pwd, @@ -432,11 +519,11 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient }, }).Extract() if err != nil { - return server, err + return nil, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { - return server, err + return nil, err } return server, nil @@ -445,27 +532,29 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient // CreateServerGroup will create a server with a random name. An error will be // returned if the server group failed to be created. func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) { + name := tools.RandomString("ACPTTEST", 16) + + t.Logf("Attempting to create server group %s", name) + sg, err := servergroups.Create(client, &servergroups.CreateOpts{ - Name: "test", + Name: name, Policies: []string{policy}, }).Extract() if err != nil { - return sg, err + return nil, err } + t.Logf("Successfully created server group %s", name) + + th.AssertEquals(t, sg.Name, name) + return sg, nil } // CreateServerInServerGroup works like CreateServer but places the instance in // a specified Server Group. func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) (*servers.Server, error) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } - - var server *servers.Server - choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) @@ -473,7 +562,7 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { - return server, err + return nil, err } name := tools.RandomString("ACPTTEST", 16) @@ -497,23 +586,30 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, Group: serverGroup.ID, }, } - server, err = servers.Create(client, schedulerHintsOpts).Extract() + server, err := servers.Create(client, schedulerHintsOpts).Extract() if err != nil { - return server, err + return nil, err } - return server, nil + if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { + return nil, err + } + + newServer, err := servers.Get(client, server.ID).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, newServer.Name, name) + th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) + th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + + return newServer, nil } // CreateServerWithPublicKey works the same as CreateServer, but additionally // configures the server with a specified Key Pair name. func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, keyPairName string) (*servers.Server, error) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } - - var server *servers.Server - choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { t.Fatal(err) @@ -521,7 +617,7 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) if err != nil { - return server, err + return nil, err } name := tools.RandomString("ACPTTEST", 16) @@ -536,19 +632,28 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, }, } - server, err = servers.Create(client, keypairs.CreateOptsExt{ + server, err := servers.Create(client, keypairs.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, KeyName: keyPairName, }).Extract() if err != nil { - return server, err + return nil, err } if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { - return server, err + return nil, err } - return server, nil + newServer, err := servers.Get(client, server.ID).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, newServer.Name, name) + th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) + th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + + return newServer, nil } // CreateVolumeAttachment will attach a volume to a server. An error will be @@ -571,25 +676,6 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blo return volumeAttachment, nil } -// CreateAggregate will create an aggregate with random name and available zone. -// An error will be returned if the aggregate could not be created. -func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregates.Aggregate, error) { - aggregateName := tools.RandomString("aggregate_", 5) - availableZone := tools.RandomString("zone_", 5) - t.Logf("Attempting to create aggregate %s", aggregateName) - - createOpts := aggregates.CreateOpts{Name: aggregateName, AvailabilityZone: availableZone} - - aggregate, err := aggregates.Create(client, createOpts).Extract() - if err != nil { - return nil, err - } - - t.Logf("Successfully created aggregate %d", aggregate.ID) - - return aggregate, nil -} - // DeleteAggregate will delete a given host aggregate. A fatal error will occur if // the aggregate deleting is failed. This works best when using it as a // deferred function. @@ -651,25 +737,25 @@ func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *key // DeleteSecurityGroup will delete a security group. A fatal error will occur // if the group failed to be deleted. This works best as a deferred function. -func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroup secgroups.SecurityGroup) { - err := secgroups.Delete(client, securityGroup.ID).ExtractErr() +func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) { + err := secgroups.Delete(client, securityGroupID).ExtractErr() if err != nil { - t.Fatalf("Unable to delete security group %s: %s", securityGroup.ID, err) + t.Fatalf("Unable to delete security group %s: %s", securityGroupID, err) } - t.Logf("Deleted security group: %s", securityGroup.ID) + t.Logf("Deleted security group: %s", securityGroupID) } // DeleteSecurityGroupRule will delete a security group rule. A fatal error // will occur if the rule failed to be deleted. This works best when used // as a deferred function. -func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, rule secgroups.Rule) { - err := secgroups.DeleteRule(client, rule.ID).ExtractErr() +func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { + err := secgroups.DeleteRule(client, ruleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete rule: %v", err) } - t.Logf("Deleted security group rule: %s", rule.ID) + t.Logf("Deleted security group rule: %s", ruleID) } // DeleteServer deletes an instance via its UUID. @@ -720,6 +806,20 @@ func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blo t.Logf("Deleted volume: %s", volumeAttachment.VolumeID) } +// DetachInterface will detach an interface from a server. A fatal +// error will occur if the interface could not be detached. This works best +// when used as a deferred function. +func DetachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID, portID string) { + t.Logf("Attempting to detach interface %s from server %s", portID, serverID) + + err := attachinterfaces.Delete(client, serverID, portID).ExtractErr() + if err != nil { + t.Fatalf("Unable to detach interface %s from server %s", portID, serverID) + } + + t.Logf("Detached interface %s from server %s", portID, serverID) +} + // DisassociateFloatingIP will disassociate a floating IP from an instance. A // fatal error will occur if the floating IP failed to disassociate. This works // best when using it as a deferred function. @@ -802,6 +902,10 @@ func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey } t.Logf("Created keypair: %s", keyPairName) + + th.AssertEquals(t, keyPair.Name, keyPairName) + th.AssertEquals(t, keyPair.PublicKey, publicKey) + return keyPair, nil } diff --git a/acceptance/openstack/compute/v2/defsecrules_test.go b/acceptance/openstack/compute/v2/defsecrules_test.go index 16c43f4c75..e97a378718 100644 --- a/acceptance/openstack/compute/v2/defsecrules_test.go +++ b/acceptance/openstack/compute/v2/defsecrules_test.go @@ -8,23 +8,21 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestDefSecRulesList(t *testing.T) { + clients.RequireAdmin(t) + clients.RequireNovaNetwork(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := dsr.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list default rules: %v", err) - } + th.AssertNoErr(t, err) allDefaultRules, err := dsr.ExtractDefaultRules(allPages) - if err != nil { - t.Fatalf("Unable to extract default rules: %v", err) - } + th.AssertNoErr(t, err) for _, defaultRule := range allDefaultRules { tools.PrintResource(t, defaultRule) @@ -32,36 +30,32 @@ func TestDefSecRulesList(t *testing.T) { } func TestDefSecRulesCreate(t *testing.T) { + clients.RequireAdmin(t) + clients.RequireNovaNetwork(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) defaultRule, err := CreateDefaultRule(t, client) - if err != nil { - t.Fatalf("Unable to create default rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteDefaultRule(t, client, defaultRule) tools.PrintResource(t, defaultRule) } func TestDefSecRulesGet(t *testing.T) { + clients.RequireAdmin(t) + clients.RequireNovaNetwork(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) defaultRule, err := CreateDefaultRule(t, client) - if err != nil { - t.Fatalf("Unable to create default rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteDefaultRule(t, client, defaultRule) newDefaultRule, err := dsr.Get(client, defaultRule.ID).Extract() - if err != nil { - t.Fatalf("Unable to get default rule %s: %v", defaultRule.ID, err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newDefaultRule) } diff --git a/acceptance/openstack/compute/v2/extension_test.go b/acceptance/openstack/compute/v2/extension_test.go index 5b2cf4a42d..f76cc52e06 100644 --- a/acceptance/openstack/compute/v2/extension_test.go +++ b/acceptance/openstack/compute/v2/extension_test.go @@ -8,39 +8,39 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/common/extensions" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestExtensionsList(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := extensions.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list extensions: %v", err) - } + th.AssertNoErr(t, err) allExtensions, err := extensions.ExtractExtensions(allPages) - if err != nil { - t.Fatalf("Unable to extract extensions: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, extension := range allExtensions { tools.PrintResource(t, extension) + + if extension.Name == "SchedulerHints" { + found = true + } } + + th.AssertEquals(t, found, true) } -func TestExtensionGet(t *testing.T) { +func TestExtensionsGet(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) extension, err := extensions.Get(client, "os-admin-actions").Extract() - if err != nil { - t.Fatalf("Unable to get extension os-admin-actions: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, extension) + + th.AssertEquals(t, extension.Name, "AdminActions") } diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index b7768b380a..d4aa341a74 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -8,136 +8,122 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + th "github.com/gophercloud/gophercloud/testhelper" identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" ) func TestFlavorsList(t *testing.T) { - t.Logf("** Default flavors (same as Project flavors): **") - t.Logf("") client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) allPages, err := flavors.ListDetail(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve flavors: %v", err) - } + th.AssertNoErr(t, err) allFlavors, err := flavors.ExtractFlavors(allPages) - if err != nil { - t.Fatalf("Unable to extract flavor results: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, flavor := range allFlavors { tools.PrintResource(t, flavor) + + if flavor.ID == choices.FlavorID { + found = true + } } - flavorAccessTypes := [3]flavors.AccessType{flavors.PublicAccess, flavors.PrivateAccess, flavors.AllAccess} - for _, flavorAccessType := range flavorAccessTypes { - t.Logf("** %s flavors: **", flavorAccessType) - t.Logf("") + th.AssertEquals(t, found, true) +} + +func TestFlavorsAccessTypeList(t *testing.T) { + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + flavorAccessTypes := map[string]flavors.AccessType{ + "public": flavors.PublicAccess, + "private": flavors.PrivateAccess, + "all": flavors.AllAccess, + } + + for flavorTypeName, flavorAccessType := range flavorAccessTypes { + t.Logf("** %s flavors: **", flavorTypeName) allPages, err := flavors.ListDetail(client, flavors.ListOpts{AccessType: flavorAccessType}).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve flavors: %v", err) - } + th.AssertNoErr(t, err) allFlavors, err := flavors.ExtractFlavors(allPages) - if err != nil { - t.Fatalf("Unable to extract flavor results: %v", err) - } + th.AssertNoErr(t, err) for _, flavor := range allFlavors { tools.PrintResource(t, flavor) - t.Logf("") } } - } func TestFlavorsGet(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) flavor, err := flavors.Get(client, choices.FlavorID).Extract() - if err != nil { - t.Fatalf("Unable to get flavor information: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, flavor) + + th.AssertEquals(t, flavor.ID, choices.FlavorID) } -func TestFlavorCreateDelete(t *testing.T) { +func TestFlavorsCreateDelete(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) flavor, err := CreateFlavor(t, client) - if err != nil { - t.Fatalf("Unable to create flavor: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) tools.PrintResource(t, flavor) } -func TestFlavorAccessesList(t *testing.T) { +func TestFlavorsAccessesList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) flavor, err := CreatePrivateFlavor(t, client) - if err != nil { - t.Fatalf("Unable to create flavor: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) allPages, err := flavors.ListAccesses(client, flavor.ID).AllPages() - if err != nil { - t.Fatalf("Unable to list flavor accesses: %v", err) - } + th.AssertNoErr(t, err) allAccesses, err := flavors.ExtractAccesses(allPages) - if err != nil { - t.Fatalf("Unable to extract accesses: %v", err) - } + th.AssertNoErr(t, err) - for _, access := range allAccesses { - tools.PrintResource(t, access) - } + th.AssertEquals(t, len(allAccesses), 0) } -func TestFlavorAccessCRUD(t *testing.T) { +func TestFlavorsAccessCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) identityClient, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatal("Unable to create identity client: %v", err) - } + th.AssertNoErr(t, err) project, err := identity.CreateProject(t, identityClient, nil) - if err != nil { - t.Fatal("Unable to create project: %v", err) - } + th.AssertNoErr(t, err) defer identity.DeleteProject(t, identityClient, project.ID) flavor, err := CreatePrivateFlavor(t, client) - if err != nil { - t.Fatalf("Unable to create flavor: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) addAccessOpts := flavors.AddAccessOpts{ @@ -145,9 +131,11 @@ func TestFlavorAccessCRUD(t *testing.T) { } accessList, err := flavors.AddAccess(client, flavor.ID, addAccessOpts).Extract() - if err != nil { - t.Fatalf("Unable to add access to flavor: %v", err) - } + th.AssertNoErr(t, err) + + th.AssertEquals(t, len(accessList), 1) + th.AssertEquals(t, accessList[0].TenantID, project.ID) + th.AssertEquals(t, accessList[0].FlavorID, flavor.ID) for _, access := range accessList { tools.PrintResource(t, access) @@ -158,25 +146,19 @@ func TestFlavorAccessCRUD(t *testing.T) { } accessList, err = flavors.RemoveAccess(client, flavor.ID, removeAccessOpts).Extract() - if err != nil { - t.Fatalf("Unable to remove access to flavor: %v", err) - } + th.AssertNoErr(t, err) - for _, access := range accessList { - tools.PrintResource(t, access) - } + th.AssertEquals(t, len(accessList), 0) } -func TestFlavorExtraSpecsCRUD(t *testing.T) { +func TestFlavorsExtraSpecsCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) flavor, err := CreatePrivateFlavor(t, client) - if err != nil { - t.Fatalf("Unable to create flavor: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) createOpts := flavors.ExtraSpecsOpts{ @@ -184,37 +166,37 @@ func TestFlavorExtraSpecsCRUD(t *testing.T) { "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } createdExtraSpecs, err := flavors.CreateExtraSpecs(client, flavor.ID, createOpts).Extract() - if err != nil { - t.Fatalf("Unable to create flavor extra_specs: %v", err) - } + th.AssertNoErr(t, err) + tools.PrintResource(t, createdExtraSpecs) + th.AssertEquals(t, len(createdExtraSpecs), 2) + th.AssertEquals(t, createdExtraSpecs["hw:cpu_policy"], "CPU-POLICY") + th.AssertEquals(t, createdExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY") + err = flavors.DeleteExtraSpec(client, flavor.ID, "hw:cpu_policy").ExtractErr() - if err != nil { - t.Fatalf("Unable to delete ExtraSpec: %v\n", err) - } + th.AssertNoErr(t, err) updateOpts := flavors.ExtraSpecsOpts{ "hw:cpu_thread_policy": "CPU-THREAD-POLICY-BETTER", } updatedExtraSpec, err := flavors.UpdateExtraSpec(client, flavor.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update flavor extra_specs: %v", err) - } + th.AssertNoErr(t, err) + tools.PrintResource(t, updatedExtraSpec) allExtraSpecs, err := flavors.ListExtraSpecs(client, flavor.ID).Extract() - if err != nil { - t.Fatalf("Unable to get flavor extra_specs: %v", err) - } + th.AssertNoErr(t, err) + tools.PrintResource(t, allExtraSpecs) - for key, _ := range allExtraSpecs { - spec, err := flavors.GetExtraSpec(client, flavor.ID, key).Extract() - if err != nil { - t.Fatalf("Unable to get flavor extra spec: %v", err) - } - tools.PrintResource(t, spec) - } + th.AssertEquals(t, len(allExtraSpecs), 1) + th.AssertEquals(t, allExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY-BETTER") + + spec, err := flavors.GetExtraSpec(client, flavor.ID, "hw:cpu_thread_policy").Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, spec) + th.AssertEquals(t, spec["hw:cpu_thread_policy"], "CPU-THREAD-POLICY-BETTER") } diff --git a/acceptance/openstack/compute/v2/floatingip_test.go b/acceptance/openstack/compute/v2/floatingip_test.go index 26b7bfe16a..8130873676 100644 --- a/acceptance/openstack/compute/v2/floatingip_test.go +++ b/acceptance/openstack/compute/v2/floatingip_test.go @@ -9,114 +9,90 @@ import ( "github.com/gophercloud/gophercloud/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" ) -func TestFloatingIPsList(t *testing.T) { +func TestFloatingIPsCreateDelete(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) + + floatingIP, err := CreateFloatingIP(t, client) + th.AssertNoErr(t, err) + defer DeleteFloatingIP(t, client, floatingIP) + + tools.PrintResource(t, floatingIP) allPages, err := floatingips.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve floating IPs: %v", err) - } + th.AssertNoErr(t, err) allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages) - if err != nil { - t.Fatalf("Unable to extract floating IPs: %v", err) - } + th.AssertNoErr(t, err) - for _, floatingIP := range allFloatingIPs { + var found bool + for _, fip := range allFloatingIPs { tools.PrintResource(t, floatingIP) - } -} -func TestFloatingIPsCreate(t *testing.T) { - client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) + if fip.ID == floatingIP.ID { + found = true + } } - floatingIP, err := CreateFloatingIP(t, client) - if err != nil { - t.Fatalf("Unable to create floating IP: %v", err) - } - defer DeleteFloatingIP(t, client, floatingIP) + th.AssertEquals(t, found, true) - tools.PrintResource(t, floatingIP) + fip, err := floatingips.Get(client, floatingIP.ID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, floatingIP.ID, fip.ID) } func TestFloatingIPsAssociate(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) floatingIP, err := CreateFloatingIP(t, client) - if err != nil { - t.Fatalf("Unable to create floating IP: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, floatingIP) tools.PrintResource(t, floatingIP) err = AssociateFloatingIP(t, client, floatingIP, server) - if err != nil { - t.Fatalf("Unable to associate floating IP %s with server %s: %v", floatingIP.IP, server.ID, err) - } + th.AssertNoErr(t, err) defer DisassociateFloatingIP(t, client, floatingIP, server) newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract() - if err != nil { - t.Fatalf("Unable to get floating IP %s: %v", floatingIP.ID, err) - } + th.AssertNoErr(t, err) t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP) tools.PrintResource(t, newFloatingIP) + + th.AssertEquals(t, newFloatingIP.InstanceID, server.ID) } func TestFloatingIPsFixedIPAssociate(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) newServer, err := servers.Get(client, server.ID).Extract() - if err != nil { - t.Fatalf("Unable to get server %s: %v", server.ID, err) - } + th.AssertNoErr(t, err) floatingIP, err := CreateFloatingIP(t, client) - if err != nil { - t.Fatalf("Unable to create floating IP: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, floatingIP) tools.PrintResource(t, floatingIP) @@ -132,17 +108,16 @@ func TestFloatingIPsFixedIPAssociate(t *testing.T) { } err = AssociateFloatingIPWithFixedIP(t, client, floatingIP, newServer, fixedIP) - if err != nil { - t.Fatalf("Unable to associate floating IP %s with server %s: %v", floatingIP.IP, newServer.ID, err) - } + th.AssertNoErr(t, err) defer DisassociateFloatingIP(t, client, floatingIP, newServer) newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract() - if err != nil { - t.Fatalf("Unable to get floating IP %s: %v", floatingIP.ID, err) - } + th.AssertNoErr(t, err) t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP) tools.PrintResource(t, newFloatingIP) + + th.AssertEquals(t, newFloatingIP.InstanceID, server.ID) + th.AssertEquals(t, newFloatingIP.FixedIP, fixedIP) } diff --git a/acceptance/openstack/compute/v2/hypervisors_test.go b/acceptance/openstack/compute/v2/hypervisors_test.go index 15aff3de74..29d49a277e 100644 --- a/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/acceptance/openstack/compute/v2/hypervisors_test.go @@ -10,23 +10,20 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestHypervisorsList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := hypervisors.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list hypervisors: %v", err) - } + th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) - if err != nil { - t.Fatalf("Unable to extract hypervisors") - } + th.AssertNoErr(t, err) for _, h := range allHypervisors { tools.PrintResource(t, h) @@ -34,70 +31,64 @@ func TestHypervisorsList(t *testing.T) { } func TestHypervisorsGet(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) hypervisorID, err := getHypervisorID(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) hypervisor, err := hypervisors.Get(client, hypervisorID).Extract() - if err != nil { - t.Fatalf("Unable to get hypervisor: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, hypervisor) + + th.AssertEquals(t, hypervisorID, hypervisor.ID) } func TestHypervisorsGetStatistics(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) hypervisorsStats, err := hypervisors.GetStatistics(client).Extract() - if err != nil { - t.Fatalf("Unable to get hypervisors statistics: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, hypervisorsStats) + + if hypervisorsStats.Count == 0 { + t.Fatalf("Unable to get hypervisor stats") + } } func TestHypervisorsGetUptime(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) hypervisorID, err := getHypervisorID(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) hypervisor, err := hypervisors.GetUptime(client, hypervisorID).Extract() - if err != nil { - t.Fatalf("Unable to hypervisor uptime: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, hypervisor) + + th.AssertEquals(t, hypervisorID, hypervisor.ID) } func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (int, error) { allPages, err := hypervisors.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list hypervisors: %v", err) - } + th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) - if err != nil { - t.Fatalf("Unable to extract hypervisors") - } + th.AssertNoErr(t, err) - for _, h := range allHypervisors { - return h.ID, nil + if len(allHypervisors) > 0 { + return allHypervisors[0].ID, nil } return 0, fmt.Errorf("Unable to get hypervisor ID") diff --git a/acceptance/openstack/compute/v2/images_test.go b/acceptance/openstack/compute/v2/images_test.go index a34ce3ea62..d7fe19b35b 100644 --- a/acceptance/openstack/compute/v2/images_test.go +++ b/acceptance/openstack/compute/v2/images_test.go @@ -8,44 +8,45 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestImagesList(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute: client: %v", err) - } + th.AssertNoErr(t, err) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) allPages, err := images.ListDetail(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve images: %v", err) - } + th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) - if err != nil { - t.Fatalf("Unable to extract image results: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, image := range allImages { tools.PrintResource(t, image) + + if image.ID == choices.ImageID { + found = true + } } + + th.AssertEquals(t, found, true) } func TestImagesGet(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute: client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) image, err := images.Get(client, choices.ImageID).Extract() - if err != nil { - t.Fatalf("Unable to get image information: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, image) + + th.AssertEquals(t, choices.ImageID, image.ID) } diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go index c4b91ec854..a3a17d19e6 100644 --- a/acceptance/openstack/compute/v2/keypairs_test.go +++ b/acceptance/openstack/compute/v2/keypairs_test.go @@ -9,99 +9,72 @@ import ( "github.com/gophercloud/gophercloud/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" ) const keyName = "gophercloud_test_key_pair" -func TestKeypairsList(t *testing.T) { +func TestKeypairsCreateDelete(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) + + keyPair, err := CreateKeyPair(t, client) + th.AssertNoErr(t, err) + defer DeleteKeyPair(t, client, keyPair) + + tools.PrintResource(t, keyPair) allPages, err := keypairs.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve keypairs: %s", err) - } + th.AssertNoErr(t, err) allKeys, err := keypairs.ExtractKeyPairs(allPages) - if err != nil { - t.Fatalf("Unable to extract keypairs results: %s", err) - } - - for _, keypair := range allKeys { - tools.PrintResource(t, keypair) - } -} + th.AssertNoErr(t, err) -func TestKeypairsCreate(t *testing.T) { - client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + var found bool + for _, kp := range allKeys { + tools.PrintResource(t, kp) - keyPair, err := CreateKeyPair(t, client) - if err != nil { - t.Fatalf("Unable to create key pair: %v", err) + if kp.Name == keyPair.Name { + found = true + } } - defer DeleteKeyPair(t, client, keyPair) - tools.PrintResource(t, keyPair) + th.AssertEquals(t, found, true) } func TestKeypairsImportPublicKey(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) publicKey, err := createKey() - if err != nil { - t.Fatalf("Unable to create public key: %s", err) - } + th.AssertNoErr(t, err) keyPair, err := ImportPublicKey(t, client, publicKey) - if err != nil { - t.Fatalf("Unable to create keypair: %s", err) - } + th.AssertNoErr(t, err) defer DeleteKeyPair(t, client, keyPair) tools.PrintResource(t, keyPair) } func TestKeypairsServerCreateWithKey(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) publicKey, err := createKey() - if err != nil { - t.Fatalf("Unable to create public key: %s", err) - } + th.AssertNoErr(t, err) keyPair, err := ImportPublicKey(t, client, publicKey) - if err != nil { - t.Fatalf("Unable to create keypair: %s", err) - } + th.AssertNoErr(t, err) defer DeleteKeyPair(t, client, keyPair) server, err := CreateServerWithPublicKey(t, client, keyPair.Name) - if err != nil { - t.Fatalf("Unable to create server: %s", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) server, err = servers.Get(client, server.ID).Extract() - if err != nil { - t.Fatalf("Unable to retrieve server: %s", err) - } + th.AssertNoErr(t, err) - if server.KeyName != keyPair.Name { - t.Fatalf("key name of server %s is %s, not %s", server.ID, server.KeyName, keyPair.Name) - } + th.AssertEquals(t, server.KeyName, keyPair.Name) } diff --git a/acceptance/openstack/compute/v2/limits_test.go b/acceptance/openstack/compute/v2/limits_test.go index 2bf5ce6b85..8133999c6c 100644 --- a/acceptance/openstack/compute/v2/limits_test.go +++ b/acceptance/openstack/compute/v2/limits_test.go @@ -7,29 +7,28 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/limits" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestLimits(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) limits, err := limits.Get(client, nil).Extract() - if err != nil { - t.Fatalf("Unable to get limits: %v", err) - } + th.AssertNoErr(t, err) - t.Logf("Limits for scoped user:") - t.Logf("%#v", limits) + tools.PrintResource(t, limits) + + th.AssertEquals(t, limits.Absolute.MaxPersonalitySize, 10240) } func TestLimitsForTenant(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) // I think this is the easiest way to get the tenant ID while being // agnostic to Identity v2 and v3. @@ -43,10 +42,9 @@ func TestLimitsForTenant(t *testing.T) { } limits, err := limits.Get(client, getOpts).Extract() - if err != nil { - t.Fatalf("Unable to get absolute limits: %v", err) - } + th.AssertNoErr(t, err) + + tools.PrintResource(t, limits) - t.Logf("Limits for tenant %s:", tenantID) - t.Logf("%#v", limits) + th.AssertEquals(t, limits.Absolute.MaxPersonalitySize, 10240) } diff --git a/acceptance/openstack/compute/v2/migrate_test.go b/acceptance/openstack/compute/v2/migrate_test.go index 954716bda0..3f61188ae4 100644 --- a/acceptance/openstack/compute/v2/migrate_test.go +++ b/acceptance/openstack/compute/v2/migrate_test.go @@ -7,47 +7,36 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestMigrate(t *testing.T) { + clients.RequireLong(t) + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to migrate server %s", server.ID) err = migrate.Migrate(client, server.ID).ExtractErr() - if err != nil { - t.Fatalf("Error during migration: %v", err) - } + th.AssertNoErr(t, err) } func TestLiveMigrate(t *testing.T) { - choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } - - if !choices.LiveMigrate { - t.Skip("Testing of live migration is disabled") - } + clients.RequireLong(t) + clients.RequireAdmin(t) + clients.RequireLiveMigration(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to migrate server %s", server.ID) @@ -61,7 +50,5 @@ func TestLiveMigrate(t *testing.T) { } err = migrate.LiveMigrate(client, server.ID, liveMigrateOpts).ExtractErr() - if err != nil { - t.Fatalf("Error during live migration: %v", err) - } + th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/compute/v2/network_test.go b/acceptance/openstack/compute/v2/network_test.go index 745151829d..25fbe4144c 100644 --- a/acceptance/openstack/compute/v2/network_test.go +++ b/acceptance/openstack/compute/v2/network_test.go @@ -8,49 +8,48 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestNetworksList(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) allPages, err := networks.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } + th.AssertNoErr(t, err) allNetworks, err := networks.ExtractNetworks(allPages) - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, network := range allNetworks { tools.PrintResource(t, network) + + if network.Label == choices.NetworkName { + found = true + } } + + th.AssertEquals(t, found, true) } func TestNetworksGet(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) network, err := networks.Get(client, networkID).Extract() - if err != nil { - t.Fatalf("Unable to get network %s: %v", networkID, err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, network) + + th.AssertEquals(t, network.Label, choices.NetworkName) } diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go index 28f2be1a88..62b2042b8c 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -17,38 +17,28 @@ import ( func TestQuotasetGet(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) identityClient, err := clients.NewIdentityV2Client() - if err != nil { - t.Fatalf("Unable to get a new identity client: %v", err) - } + th.AssertNoErr(t, err) tenantID, err := getTenantID(t, identityClient) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) quotaSet, err := quotasets.Get(client, tenantID).Extract() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) + + th.AssertEquals(t, quotaSet.FixedIPs, -1) } func getTenantID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { allPages, err := tenants.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to get list of tenants: %v", err) - } + th.AssertNoErr(t, err) allTenants, err := tenants.ExtractTenants(allPages) - if err != nil { - t.Fatalf("Unable to extract tenants: %v", err) - } + th.AssertNoErr(t, err) for _, tenant := range allTenants { return tenant.ID, nil @@ -59,14 +49,10 @@ func getTenantID(t *testing.T, client *gophercloud.ServiceClient) (string, error func getTenantIDByName(t *testing.T, client *gophercloud.ServiceClient, name string) (string, error) { allPages, err := tenants.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to get list of tenants: %v", err) - } + th.AssertNoErr(t, err) allTenants, err := tenants.ExtractTenants(allPages) - if err != nil { - t.Fatalf("Unable to extract tenants: %v", err) - } + th.AssertNoErr(t, err) for _, tenant := range allTenants { if tenant.Name == name { @@ -77,8 +63,8 @@ func getTenantIDByName(t *testing.T, client *gophercloud.ServiceClient, name str return "", fmt.Errorf("Unable to get tenant ID") } -//What will be sent as desired Quotas to the Server -var UpdatQuotaOpts = quotasets.UpdateOpts{ +// What will be sent as desired Quotas to the Server +var UpdateQuotaOpts = quotasets.UpdateOpts{ FixedIPs: gophercloud.IntToPointer(10), FloatingIPs: gophercloud.IntToPointer(10), InjectedFileContentBytes: gophercloud.IntToPointer(10240), @@ -95,7 +81,7 @@ var UpdatQuotaOpts = quotasets.UpdateOpts{ ServerGroupMembers: gophercloud.IntToPointer(3), } -//What the Server hopefully returns as the new Quotas +// What the Server hopefully returns as the new Quotas var UpdatedQuotas = quotasets.QuotaSet{ FixedIPs: 10, FloatingIPs: 10, @@ -114,71 +100,44 @@ var UpdatedQuotas = quotasets.QuotaSet{ } func TestQuotasetUpdateDelete(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) idclient, err := clients.NewIdentityV2Client() - if err != nil { - t.Fatalf("Could not create IdentityClient to look up tenant id!") - } + th.AssertNoErr(t, err) tenantid, err := getTenantIDByName(t, idclient, os.Getenv("OS_TENANT_NAME")) - if err != nil { - t.Fatalf("Id for Tenant named '%' not found. Please set OS_TENANT_NAME appropriately", os.Getenv("OS_TENANT_NAME")) - } + th.AssertNoErr(t, err) - //save original quotas + // save original quotas orig, err := quotasets.Get(client, tenantid).Extract() th.AssertNoErr(t, err) - //Test Update - res, err := quotasets.Update(client, tenantid, UpdatQuotaOpts).Extract() + // Test Update + res, err := quotasets.Update(client, tenantid, UpdateQuotaOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, UpdatedQuotas, *res) - //Test Delete + // Test Delete _, err = quotasets.Delete(client, tenantid).Extract() th.AssertNoErr(t, err) - //We dont know the default quotas, so just check if the quotas are not the same as before + + // 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() - if newres == res { - t.Fatalf("Quotas after delete equal quotas before delete!") + th.AssertNoErr(t, err) + if newres.RAM == res.RAM { + t.Fatalf("Failed to update quotas") } restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) - //restore original quotas + // restore original quotas res, err = quotasets.Update(client, tenantid, restore).Extract() th.AssertNoErr(t, err) orig.ID = "" - th.AssertEquals(t, *orig, *res) - -} - -// Makes sure that the FillUpdateOptsFromQuotaSet() helper function works properly -func TestFillFromQuotaSetHelperFunction(t *testing.T) { - op := "asets.UpdateOpts{} - expected := ` - { - "fixed_ips": 10, - "floating_ips": 10, - "injected_file_content_bytes": 10240, - "injected_file_path_bytes": 255, - "injected_files": 5, - "key_pairs": 10, - "metadata_items": 128, - "ram": 20000, - "security_group_rules": 20, - "security_groups": 10, - "cores": 10, - "instances": 4, - "server_groups": 2, - "server_group_members": 3 - }` - FillUpdateOptsFromQuotaSet(UpdatedQuotas, op) - th.AssertJSONEquals(t, expected, op) + th.AssertDeepEquals(t, orig, res) } diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/acceptance/openstack/compute/v2/secgroup_test.go index d77c4ace86..0d69a18fd0 100644 --- a/acceptance/openstack/compute/v2/secgroup_test.go +++ b/acceptance/openstack/compute/v2/secgroup_test.go @@ -9,137 +9,136 @@ import ( "github.com/gophercloud/gophercloud/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" ) func TestSecGroupsList(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := secgroups.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve security groups: %v", err) - } + th.AssertNoErr(t, err) allSecGroups, err := secgroups.ExtractSecurityGroups(allPages) - if err != nil { - t.Fatalf("Unable to extract security groups: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, secgroup := range allSecGroups { tools.PrintResource(t, secgroup) - } -} -func TestSecGroupsCreate(t *testing.T) { - client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) + if secgroup.Name == "default" { + found = true + } } - securityGroup, err := CreateSecurityGroup(t, client) - if err != nil { - t.Fatalf("Unable to create security group: %v", err) - } - defer DeleteSecurityGroup(t, client, securityGroup) + th.AssertEquals(t, found, true) } -func TestSecGroupsUpdate(t *testing.T) { +func TestSecGroupsCRUD(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) securityGroup, err := CreateSecurityGroup(t, client) - if err != nil { - t.Fatalf("Unable to create security group: %v", err) - } - defer DeleteSecurityGroup(t, client, securityGroup) + th.AssertNoErr(t, err) + defer DeleteSecurityGroup(t, client, securityGroup.ID) + + tools.PrintResource(t, securityGroup) + newName := tools.RandomString("secgroup_", 4) updateOpts := secgroups.UpdateOpts{ - Name: tools.RandomString("secgroup_", 4), + Name: newName, Description: tools.RandomString("dec_", 10), } updatedSecurityGroup, err := secgroups.Update(client, securityGroup.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update security group: %v", err) - } + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedSecurityGroup) t.Logf("Updated %s's name to %s", updatedSecurityGroup.ID, updatedSecurityGroup.Name) + + th.AssertEquals(t, updatedSecurityGroup.Name, newName) } func TestSecGroupsRuleCreate(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) securityGroup, err := CreateSecurityGroup(t, client) - if err != nil { - t.Fatalf("Unable to create security group: %v", err) - } - defer DeleteSecurityGroup(t, client, securityGroup) + th.AssertNoErr(t, err) + defer DeleteSecurityGroup(t, client, securityGroup.ID) + + tools.PrintResource(t, securityGroup) rule, err := CreateSecurityGroupRule(t, client, securityGroup.ID) - if err != nil { - t.Fatalf("Unable to create rule: %v", err) - } - defer DeleteSecurityGroupRule(t, client, rule) + th.AssertNoErr(t, err) + defer DeleteSecurityGroupRule(t, client, rule.ID) + + tools.PrintResource(t, rule) newSecurityGroup, err := secgroups.Get(client, securityGroup.ID).Extract() - if err != nil { - t.Fatalf("Unable to obtain security group: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newSecurityGroup) + th.AssertEquals(t, len(newSecurityGroup.Rules), 1) } func TestSecGroupsAddGroupToServer(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) + + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) securityGroup, err := CreateSecurityGroup(t, client) - if err != nil { - t.Fatalf("Unable to create security group: %v", err) - } - defer DeleteSecurityGroup(t, client, securityGroup) + th.AssertNoErr(t, err) + defer DeleteSecurityGroup(t, client, securityGroup.ID) rule, err := CreateSecurityGroupRule(t, client, securityGroup.ID) - if err != nil { - t.Fatalf("Unable to create rule: %v", err) - } - defer DeleteSecurityGroupRule(t, client, rule) + th.AssertNoErr(t, err) + defer DeleteSecurityGroupRule(t, client, rule.ID) - server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + server, err = CreateServer(t, client) + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Adding group %s to server %s", securityGroup.ID, server.ID) err = secgroups.AddServer(client, server.ID, securityGroup.Name).ExtractErr() - if err != nil { - t.Fatalf("Unable to add group %s to server %s: %s", securityGroup.ID, server.ID, err) - } + th.AssertNoErr(t, err) server, err = servers.Get(client, server.ID).Extract() - if err != nil { - t.Fatalf("Unable to get server %s: %s", server.ID, err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, server) + var found bool + for _, sg := range server.SecurityGroups { + if sg["name"] == securityGroup.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + t.Logf("Removing group %s from server %s", securityGroup.ID, server.ID) err = secgroups.RemoveServer(client, server.ID, securityGroup.Name).ExtractErr() - if err != nil { - t.Fatalf("Unable to remove group %s from server %s: %s", securityGroup.ID, server.ID, err) + th.AssertNoErr(t, err) + + server, err = servers.Get(client, server.ID).Extract() + th.AssertNoErr(t, err) + + found = false + + tools.PrintResource(t, server) + + for _, sg := range server.SecurityGroups { + if sg["name"] == securityGroup.Name { + found = true + } } + + th.AssertEquals(t, found, false) } diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/acceptance/openstack/compute/v2/servergroup_test.go index 547b82fd5a..8b7af0f3c1 100644 --- a/acceptance/openstack/compute/v2/servergroup_test.go +++ b/acceptance/openstack/compute/v2/servergroup_test.go @@ -9,85 +9,63 @@ import ( "github.com/gophercloud/gophercloud/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" ) -func TestServergroupsList(t *testing.T) { +func TestServergroupsCreateDelete(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) + + serverGroup, err := CreateServerGroup(t, client, "anti-affinity") + th.AssertNoErr(t, err) + defer DeleteServerGroup(t, client, serverGroup) + + serverGroup, err = servergroups.Get(client, serverGroup.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, serverGroup) allPages, err := servergroups.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list server groups: %v", err) - } + th.AssertNoErr(t, err) allServerGroups, err := servergroups.ExtractServerGroups(allPages) - if err != nil { - t.Fatalf("Unable to extract server groups: %v", err) - } + th.AssertNoErr(t, err) - for _, serverGroup := range allServerGroups { + var found bool + for _, sg := range allServerGroups { tools.PrintResource(t, serverGroup) - } -} - -func TestServergroupsCreate(t *testing.T) { - client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } - serverGroup, err := CreateServerGroup(t, client, "anti-affinity") - if err != nil { - t.Fatalf("Unable to create server group: %v", err) - } - defer DeleteServerGroup(t, client, serverGroup) - - serverGroup, err = servergroups.Get(client, serverGroup.ID).Extract() - if err != nil { - t.Fatalf("Unable to get server group: %v", err) + if sg.ID == serverGroup.ID { + found = true + } } - tools.PrintResource(t, serverGroup) + th.AssertEquals(t, found, true) } func TestServergroupsAffinityPolicy(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) serverGroup, err := CreateServerGroup(t, client, "affinity") - if err != nil { - t.Fatalf("Unable to create server group: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServerGroup(t, client, serverGroup) firstServer, err := CreateServerInServerGroup(t, client, serverGroup) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } - if err = WaitForComputeStatus(client, firstServer, "ACTIVE"); err != nil { - t.Fatalf("Unable to wait for server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, firstServer) firstServer, err = servers.Get(client, firstServer.ID).Extract() + th.AssertNoErr(t, err) secondServer, err := CreateServerInServerGroup(t, client, serverGroup) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } - - if err = WaitForComputeStatus(client, secondServer, "ACTIVE"); err != nil { - t.Fatalf("Unable to wait for server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, secondServer) secondServer, err = servers.Get(client, secondServer.ID).Extract() + th.AssertNoErr(t, err) - if firstServer.HostID != secondServer.HostID { - t.Fatalf("%s and %s were not scheduled on the same host.", firstServer.ID, secondServer.ID) - } + th.AssertEquals(t, firstServer.HostID, secondServer.HostID) } diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index db7422f9b0..917795d326 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -19,88 +19,61 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestServersList(t *testing.T) { +func TestServersCreateDestroy(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) allPages, err := servers.List(client, servers.ListOpts{}).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve servers: %v", err) - } + th.AssertNoErr(t, err) allServers, err := servers.ExtractServers(allPages) - if err != nil { - t.Fatalf("Unable to extract servers: %v", err) - } + th.AssertNoErr(t, err) - for _, server := range allServers { + var found bool + for _, s := range allServers { tools.PrintResource(t, server) - } -} - -func TestServersCreateDestroy(t *testing.T) { - client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } - choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } - - server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) + if s.ID == server.ID { + found = true + } } - defer DeleteServer(t, client, server) - - newServer, err := servers.Get(client, server.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve server: %v", err) - } - tools.PrintResource(t, newServer) + th.AssertEquals(t, found, true) allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages() - if err != nil { - t.Errorf("Unable to list server addresses: %v", err) - } + th.AssertNoErr(t, err) allAddresses, err := servers.ExtractAddresses(allAddressPages) - if err != nil { - t.Errorf("Unable to extract server addresses: %v", err) - } + th.AssertNoErr(t, err) for network, address := range allAddresses { t.Logf("Addresses on %s: %+v", network, address) } allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages() - if err != nil { - t.Errorf("Unable to list server Interfaces: %v", err) - } + th.AssertNoErr(t, err) allInterfaces, err := attachinterfaces.ExtractInterfaces(allInterfacePages) - if err != nil { - t.Errorf("Unable to extract server Interfaces: %v", err) - } + th.AssertNoErr(t, err) - for _, Interface := range allInterfaces { - t.Logf("Interfaces: %+v", Interface) + for _, iface := range allInterfaces { + t.Logf("Interfaces: %+v", iface) } allNetworkAddressPages, err := servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages() - if err != nil { - t.Errorf("Unable to list server addresses: %v", err) - } + th.AssertNoErr(t, err) allNetworkAddresses, err := servers.ExtractNetworkAddresses(allNetworkAddressPages) - if err != nil { - t.Errorf("Unable to extract server addresses: %v", err) - } + th.AssertNoErr(t, err) t.Logf("Addresses on %s:", choices.NetworkName) for _, address := range allNetworkAddresses { @@ -108,7 +81,9 @@ func TestServersCreateDestroy(t *testing.T) { } } -func TestServersCreateDestroyWithExtensions(t *testing.T) { +func TestServersWithExtensionsCreateDestroy(t *testing.T) { + clients.RequireLong(t) + var extendedServer struct { servers.Server availabilityzones.ServerAvailabilityZoneExt @@ -116,33 +91,25 @@ func TestServersCreateDestroyWithExtensions(t *testing.T) { } client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) err = servers.Get(client, server.ID).ExtractInto(&extendedServer) - if err != nil { - t.Errorf("Unable to retrieve server: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, extendedServer) - t.Logf("Availability Zone: %s\n", extendedServer.AvailabilityZone) - t.Logf("Power State: %s\n", extendedServer.PowerState) - t.Logf("Task State: %s\n", extendedServer.TaskState) - t.Logf("VM State: %s\n", extendedServer.VmState) + th.AssertEquals(t, extendedServer.AvailabilityZone, "nova") + th.AssertEquals(t, int(extendedServer.PowerState), extendedstatus.RUNNING) + th.AssertEquals(t, extendedServer.TaskState, "") + th.AssertEquals(t, extendedServer.VmState, "active") } func TestServersWithoutImageRef(t *testing.T) { client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServerWithoutImageRef(t, client) if err != nil { @@ -155,15 +122,13 @@ func TestServersWithoutImageRef(t *testing.T) { } func TestServersUpdate(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) alternateName := tools.RandomString("ACPTTEST", 16) @@ -178,13 +143,9 @@ func TestServersUpdate(t *testing.T) { } updated, err := servers.Update(client, server.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to rename server: %v", err) - } + th.AssertNoErr(t, err) - if updated.ID != server.ID { - t.Errorf("Updated server ID [%s] didn't match original server ID [%s]!", updated.ID, server.ID) - } + th.AssertEquals(t, updated.ID, server.ID) err = tools.WaitFor(func() (bool, error) { latest, err := servers.Get(client, updated.ID).Extract() @@ -197,81 +158,99 @@ func TestServersUpdate(t *testing.T) { } func TestServersMetadata(t *testing.T) { - t.Parallel() + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) + tools.PrintResource(t, server) + metadata, err := servers.UpdateMetadata(client, server.ID, servers.MetadataOpts{ "foo": "bar", "this": "that", }).Extract() - if err != nil { - t.Fatalf("Unable to update metadata: %v", err) - } + th.AssertNoErr(t, err) t.Logf("UpdateMetadata result: %+v\n", metadata) + server, err = servers.Get(client, server.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, server) + + expectedMetadata := map[string]string{ + "abc": "def", + "foo": "bar", + "this": "that", + } + th.AssertDeepEquals(t, expectedMetadata, server.Metadata) + err = servers.DeleteMetadatum(client, server.ID, "foo").ExtractErr() - if err != nil { - t.Fatalf("Unable to delete metadatum: %v", err) + th.AssertNoErr(t, err) + + server, err = servers.Get(client, server.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, server) + + expectedMetadata = map[string]string{ + "abc": "def", + "this": "that", } + th.AssertDeepEquals(t, expectedMetadata, server.Metadata) metadata, err = servers.CreateMetadatum(client, server.ID, servers.MetadatumOpts{ "foo": "baz", }).Extract() - if err != nil { - t.Fatalf("Unable to create metadatum: %v", err) - } + th.AssertNoErr(t, err) t.Logf("CreateMetadatum result: %+v\n", metadata) - metadata, err = servers.Metadatum(client, server.ID, "foo").Extract() - if err != nil { - t.Fatalf("Unable to get metadatum: %v", err) + server, err = servers.Get(client, server.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, server) + + expectedMetadata = map[string]string{ + "abc": "def", + "this": "that", + "foo": "baz", } + th.AssertDeepEquals(t, expectedMetadata, server.Metadata) + + metadata, err = servers.Metadatum(client, server.ID, "foo").Extract() + th.AssertNoErr(t, err) t.Logf("Metadatum result: %+v\n", metadata) th.AssertEquals(t, "baz", metadata["foo"]) metadata, err = servers.Metadata(client, server.ID).Extract() - if err != nil { - t.Fatalf("Unable to get metadata: %v", err) - } + th.AssertNoErr(t, err) t.Logf("Metadata result: %+v\n", metadata) + th.AssertDeepEquals(t, expectedMetadata, metadata) + metadata, err = servers.ResetMetadata(client, server.ID, servers.MetadataOpts{}).Extract() - if err != nil { - t.Fatalf("Unable to reset metadata: %v", err) - } + th.AssertNoErr(t, err) t.Logf("ResetMetadata result: %+v\n", metadata) th.AssertDeepEquals(t, map[string]string{}, metadata) } func TestServersActionChangeAdminPassword(t *testing.T) { - t.Parallel() + clients.RequireLong(t) + clients.RequireGuestAgent(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) randomPassword := tools.MakeNewPassword(server.AdminPass) res := servers.ChangeAdminPassword(client, server.ID, randomPassword) - if res.Err != nil { - t.Fatal(res.Err) - } + th.AssertNoErr(t, res.Err) if err = WaitForComputeStatus(client, server, "PASSWORD"); err != nil { t.Fatal(err) @@ -283,17 +262,13 @@ func TestServersActionChangeAdminPassword(t *testing.T) { } func TestServersActionReboot(t *testing.T) { - t.Parallel() + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) rebootOpts := &servers.RebootOpts{ @@ -302,9 +277,7 @@ func TestServersActionReboot(t *testing.T) { t.Logf("Attempting reboot of server %s", server.ID) res := servers.Reboot(client, server.ID, rebootOpts) - if res.Err != nil { - t.Fatalf("Unable to reboot server: %v", res.Err) - } + th.AssertNoErr(t, res.Err) if err = WaitForComputeStatus(client, server, "REBOOT"); err != nil { t.Fatal(err) @@ -316,22 +289,16 @@ func TestServersActionReboot(t *testing.T) { } func TestServersActionRebuild(t *testing.T) { - t.Parallel() + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to rebuild server %s", server.ID) @@ -343,13 +310,9 @@ func TestServersActionRebuild(t *testing.T) { } rebuilt, err := servers.Rebuild(client, server.ID, rebuildOpts).Extract() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) - if rebuilt.ID != server.ID { - t.Errorf("Expected rebuilt server ID of [%s]; got [%s]", server.ID, rebuilt.ID) - } + th.AssertEquals(t, rebuilt.ID, server.ID) if err = WaitForComputeStatus(client, rebuilt, "REBUILD"); err != nil { t.Fatal(err) @@ -361,17 +324,16 @@ func TestServersActionRebuild(t *testing.T) { } func TestServersActionResizeConfirm(t *testing.T) { - t.Parallel() + clients.RequireLong(t) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to resize server %s", server.ID) @@ -385,20 +347,24 @@ func TestServersActionResizeConfirm(t *testing.T) { if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } + + server, err = servers.Get(client, server.ID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, server.Flavor["id"], choices.FlavorIDResize) } func TestServersActionResizeRevert(t *testing.T) { - t.Parallel() + clients.RequireLong(t) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to resize server %s", server.ID) @@ -412,112 +378,81 @@ func TestServersActionResizeRevert(t *testing.T) { if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } + + server, err = servers.Get(client, server.ID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, server.Flavor["id"], choices.FlavorID) } func TestServersActionPause(t *testing.T) { - t.Parallel() + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to pause server %s", server.ID) err = pauseunpause.Pause(client, server.ID).ExtractErr() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "PAUSED") - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) err = pauseunpause.Unpause(client, server.ID).ExtractErr() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) } func TestServersActionSuspend(t *testing.T) { - t.Parallel() + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to suspend server %s", server.ID) err = suspendresume.Suspend(client, server.ID).ExtractErr() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "SUSPENDED") - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) err = suspendresume.Resume(client, server.ID).ExtractErr() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) } func TestServersActionLock(t *testing.T) { - t.Parallel() + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) t.Logf("Attempting to Lock server %s", server.ID) err = lockunlock.Lock(client, server.ID).ExtractErr() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) err = servers.Delete(client, server.ID).ExtractErr() - if err == nil { - t.Fatalf("Should not have been able to delete the server") - } + th.AssertNoErr(t, err) err = lockunlock.Unlock(client, server.ID).ExtractErr() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/compute/v2/services_test.go b/acceptance/openstack/compute/v2/services_test.go index b949b70fa2..5c6484e0e6 100644 --- a/acceptance/openstack/compute/v2/services_test.go +++ b/acceptance/openstack/compute/v2/services_test.go @@ -8,25 +8,29 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestServicesList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := services.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list services: %v", err) - } + th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) - if err != nil { - t.Fatalf("Unable to extract services") - } + th.AssertNoErr(t, err) + var found bool for _, service := range allServices { tools.PrintResource(t, service) + + if service.Binary == "nova-scheduler" { + found = true + } } + + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/compute/v2/tenantnetworks_test.go b/acceptance/openstack/compute/v2/tenantnetworks_test.go index 9b6b527022..a53c64d353 100644 --- a/acceptance/openstack/compute/v2/tenantnetworks_test.go +++ b/acceptance/openstack/compute/v2/tenantnetworks_test.go @@ -8,49 +8,46 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestTenantNetworksList(t *testing.T) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := tenantnetworks.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } + th.AssertNoErr(t, err) allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, network := range allTenantNetworks { tools.PrintResource(t, network) + + if network.Name == choices.NetworkName { + found = true + } } + + th.AssertEquals(t, found, true) } func TestTenantNetworksGet(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) network, err := tenantnetworks.Get(client, networkID).Extract() - if err != nil { - t.Fatalf("Unable to get network %s: %v", networkID, err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, network) } diff --git a/acceptance/openstack/compute/v2/usage_test.go b/acceptance/openstack/compute/v2/usage_test.go index 1537fda0cb..0511f8937c 100644 --- a/acceptance/openstack/compute/v2/usage_test.go +++ b/acceptance/openstack/compute/v2/usage_test.go @@ -5,30 +5,43 @@ package v2 import ( "strings" "testing" + "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestUsageSingleTenant(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) + + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + DeleteServer(t, client, server) endpointParts := strings.Split(client.Endpoint, "/") tenantID := endpointParts[4] - page, err := usage.SingleTenant(client, tenantID, nil).AllPages() - if err != nil { - t.Fatal(err) + end := time.Now() + start := end.AddDate(0, -1, 0) + opts := usage.SingleTenantOpts{ + Start: &start, + End: &end, } + page, err := usage.SingleTenant(client, tenantID, opts).AllPages() + th.AssertNoErr(t, err) + tenantUsage, err := usage.ExtractSingleTenant(page) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, tenantUsage) + + if tenantUsage.TotalHours == 0 { + t.Fatalf("TotalHours should not be 0") + } } diff --git a/acceptance/openstack/compute/v2/volumeattach_test.go b/acceptance/openstack/compute/v2/volumeattach_test.go index 78d85a9bfc..022df830ca 100644 --- a/acceptance/openstack/compute/v2/volumeattach_test.go +++ b/acceptance/openstack/compute/v2/volumeattach_test.go @@ -5,74 +5,34 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud" "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/openstack/blockstorage/v1/volumes" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumeAttachAttachment(t *testing.T) { - if testing.Short() { - t.Skip("Skipping test that requires server creation in short mode.") - } + clients.RequireLong(t) client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) - blockClient, err := clients.NewBlockStorageV1Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - volume, err := createVolume(t, blockClient) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - - if err = volumes.WaitForStatus(blockClient, volume.ID, "available", 60); err != nil { - t.Fatalf("Unable to wait for volume: %v", err) - } - defer deleteVolume(t, blockClient, volume) + volume, err := bs.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer bs.DeleteVolume(t, blockClient, volume) volumeAttachment, err := CreateVolumeAttachment(t, client, blockClient, server, volume) - if err != nil { - t.Fatalf("Unable to attach volume: %v", err) - } + th.AssertNoErr(t, err) defer DeleteVolumeAttachment(t, client, blockClient, server, volumeAttachment) tools.PrintResource(t, volumeAttachment) -} - -func createVolume(t *testing.T, blockClient *gophercloud.ServiceClient) (*volumes.Volume, error) { - volumeName := tools.RandomString("ACPTTEST", 16) - createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, - } - - volume, err := volumes.Create(blockClient, createOpts).Extract() - if err != nil { - return volume, err - } - - t.Logf("Created volume: %s", volume.ID) - return volume, nil -} - -func deleteVolume(t *testing.T, blockClient *gophercloud.ServiceClient, volume *volumes.Volume) { - err := volumes.Delete(blockClient, volume.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete volume: %v", err) - } - - t.Logf("Deleted volume: %s", volume.ID) + th.AssertEquals(t, volumeAttachment.ServerID, server.ID) } From c7ca48da8eafab13e1835090a1147e64cfc10174 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Sun, 25 Mar 2018 23:12:57 +0200 Subject: [PATCH 0254/2296] Vpnaas: List IPSec site connections (#842) * Added list function for site connections * Added acceptance test and documentation --- .../extensions/vpnaas/siteconnection_test.go | 21 ++++ .../extensions/vpnaas/siteconnections/doc.go | 15 ++- .../vpnaas/siteconnections/requests.go | 54 ++++++++++- .../vpnaas/siteconnections/results.go | 38 ++++++++ .../siteconnections/testing/requests_test.go | 97 +++++++++++++++++++ 5 files changed, 223 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index c063dc24f5..5bf7560747 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -14,6 +14,27 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" ) +func TestConnectionList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := siteconnections.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list IPSec site connections: %v", err) + } + + allConnections, err := siteconnections.ExtractConnections(allPages) + if err != nil { + t.Fatalf("Unable to extract IPSec site connections: %v", err) + } + + for _, connection := range allConnections { + tools.PrintResource(t, connection) + } +} + func TestConnectionCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go index 9311ec61a2..76d71553a2 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go @@ -34,9 +34,22 @@ Example to Show the details of a specific IPSec site connection by ID Example to Delete a site connection connID := "38aee955-6283-4279-b091-8b9c828000ec" - err := siteconnections.Delete(networkClient, serviceID).ExtractErr() + err := siteconnections.Delete(networkClient, connID).ExtractErr() if err != nil { panic(err) } + +Example to List site connections + + allPages, err := siteconnections.List(client, nil).AllPages() + if err != nil { + panic(err) + } + + allConnections, err := siteconnections.ExtractConnections(allPages) + if err != nil { + panic(err) + } + */ package siteconnections diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index 6d79dc1395..70fd9d1152 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -2,6 +2,7 @@ package siteconnections import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -111,7 +112,7 @@ type CreateOpts struct { MTU int `json:"mtu,omitempty"` } -// ToServiceCreateMap casts a CreateOpts struct to a map. +// ToConnectionCreateMap casts a CreateOpts struct to a map. func (opts CreateOpts) ToConnectionCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "ipsec_site_connection") } @@ -140,3 +141,54 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToConnectionListQuery() (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 IPSec site connection attributes you want to see returned. +type ListOpts struct { + IKEPolicyID string `q:"ikepolicy_id"` + VPNServiceID string `q:"vpnservice_id"` + LocalEPGroupID string `q:"local_ep_group_id"` + IPSecPolicyID string `q:"ipsecpolicy_id"` + PeerID string `q:"peer_id"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + PeerEPGroupID string `q:"peer_ep_group_id"` + LocalID string `q:"local_id"` + Name string `q:"name"` + Description string `q:"description"` + PeerAddress string `q:"peer_address"` + PSK string `q:"psk"` + Initiator Initiator `q:"initiator"` + AdminStateUp *bool `q:"admin_state_up"` + MTU int `q:"mtu"` +} + +// ToConnectionListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToConnectionListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// IPSec site connections. 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.ToConnectionListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return ConnectionPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go index 351aa0d29a..c8e21d5f71 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go @@ -2,6 +2,7 @@ package siteconnections import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type DPD struct { @@ -91,6 +92,43 @@ type commonResult struct { gophercloud.Result } +// ConnectionPage is the page returned by a pager when traversing over a +// collection of IPSec site connections. +type ConnectionPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of IPSec site connections 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 ConnectionPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"ipsec_site_connections_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a ConnectionPage struct is empty. +func (r ConnectionPage) IsEmpty() (bool, error) { + is, err := ExtractConnections(r) + return len(is) == 0, err +} + +// ExtractConnections accepts a Page struct, specifically a Connection struct, +// and extracts the elements into a slice of Connection structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractConnections(r pagination.Page) ([]Connection, error) { + var s struct { + Connections []Connection `json:"ipsec_site_connections"` + } + err := (r.(ConnectionPage)).ExtractInto(&s) + return s.Connections, err +} + // Extract is a function that accepts a result and extracts an IPSec site connection. func (r commonResult) Extract() (*Connection, error) { var s struct { diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go index c552091cce..710f713664 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -213,3 +214,99 @@ func TestGet(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections", 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, ` +{ + "ipsec_site_connections":[ + { + "status": "PENDING_CREATE", + "psk": "secret", + "initiator": "bi-directional", + "name": "vpnconnection1", + "admin_state_up": true, + "project_id": "10039663455a446d8ba2cbb058b0f578", + "tenant_id": "10039663455a446d8ba2cbb058b0f578", + "auth_mode": "psk", + "peer_cidrs": [], + "mtu": 1500, + "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "dpd": { + "action": "hold", + "interval": 30, + "timeout": 120 + }, + "route_mode": "static", + "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", + "peer_address": "172.24.4.233", + "peer_id": "172.24.4.233", + "id": "851f280f-5639-4ea3-81aa-e298525ab74b", + "description": "" + }] +} + `) + }) + + count := 0 + + siteconnections.List(fake.ServiceClient(), siteconnections.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := siteconnections.ExtractConnections(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + + expectedDPD := siteconnections.DPD{ + Action: "hold", + Interval: 30, + Timeout: 120, + } + expected := []siteconnections.Connection{ + { + TenantID: "10039663455a446d8ba2cbb058b0f578", + Name: "vpnconnection1", + AdminStateUp: true, + PSK: "secret", + Initiator: "bi-directional", + IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + MTU: 1500, + PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", + PeerAddress: "172.24.4.233", + PeerID: "172.24.4.233", + Status: "PENDING_CREATE", + ProjectID: "10039663455a446d8ba2cbb058b0f578", + AuthMode: "psk", + PeerCIDRs: []string{}, + DPD: expectedDPD, + RouteMode: "static", + ID: "851f280f-5639-4ea3-81aa-e298525ab74b", + Description: "", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} From 2b0354debf8c26defaa09cad57d2e148b7dcaf3a Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Tue, 27 Mar 2018 03:58:43 +0200 Subject: [PATCH 0255/2296] Vpnaas: Update endpoint groups (#841) * Added endpoint group update function * doc fixes(changed strings to pointers) --- .../v2/extensions/vpnaas/group_test.go | 12 ++++ .../extensions/vpnaas/endpointgroups/doc.go | 13 ++++ .../vpnaas/endpointgroups/requests.go | 30 +++++++++ .../vpnaas/endpointgroups/results.go | 6 ++ .../endpointgroups/testing/requests_test.go | 63 +++++++++++++++++++ 5 files changed, 124 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index f793b3428c..853065d219 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -50,4 +50,16 @@ func TestGroupCRUD(t *testing.T) { } tools.PrintResource(t, newGroup) + updatedName := "updatedname" + updatedDescription := "updated description" + updateOpts := endpointgroups.UpdateOpts{ + Name: &updatedName, + Description: &updatedDescription, + } + updatedGroup, err := endpointgroups.Update(client, group.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update endpoint group: %v", err) + } + tools.PrintResource(t, updatedGroup) + } diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go index f8dff3b66c..5f49bd1da4 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go @@ -41,5 +41,18 @@ Example to List Endpoint groups if err != nil { panic(err) } + +Example to Update an endpoint group + + name := "updatedname" + description := "updated description" + updateOpts := endpointgroups.UpdateOpts{ + Name: &name, + Description: &description, + } + updatedPolicy, err := endpointgroups.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() + if err != nil { + panic(err) + } */ package endpointgroups diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go index cd9ca9ce8c..c12d0a8004 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -112,3 +112,33 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToEndpointGroupUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contains the values used when updating an endpoint group. +type UpdateOpts struct { + Description *string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` +} + +// ToEndpointGroupUpdateMap casts an UpdateOpts struct to a map. +func (opts UpdateOpts) ToEndpointGroupUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "endpoint_group") +} + +// Update allows endpoint groups to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToEndpointGroupUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go index c3d7dfcf5d..822b70002c 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go @@ -96,3 +96,9 @@ type GetResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// UpdateResult represents the result of an update operation. Call its Extract method +// to interpret it as an EndpointGroup. +type UpdateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go index 1a3814d47b..7feac37f78 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -200,3 +200,66 @@ func TestDelete(t *testing.T) { res := endpointgroups.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/endpoint-groups/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` +{ + "endpoint_group": { + "description": "updated description", + "name": "updatedname" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "endpoint_group": { + "description": "updated description", + "tenant_id": "4ad57e7ce0b24fca8f12b9834d91079d", + "project_id": "4ad57e7ce0b24fca8f12b9834d91079d", + "endpoints": [ + "10.2.0.0/24", + "10.3.0.0/24" + ], + "type": "cidr", + "id": "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", + "name": "updatedname" + } +} +`) + }) + + updatedName := "updatedname" + updatedDescription := "updated description" + options := endpointgroups.UpdateOpts{ + Name: &updatedName, + Description: &updatedDescription, + } + + actual, err := endpointgroups.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + th.AssertNoErr(t, err) + expected := endpointgroups.EndpointGroup{ + Name: "updatedname", + TenantID: "4ad57e7ce0b24fca8f12b9834d91079d", + ProjectID: "4ad57e7ce0b24fca8f12b9834d91079d", + ID: "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a", + Description: "updated description", + Endpoints: []string{ + "10.2.0.0/24", + "10.3.0.0/24", + }, + Type: "cidr", + } + th.AssertDeepEquals(t, expected, *actual) +} From 3b8ddacec5a2308dc30446f82c6bab8337555b67 Mon Sep 17 00:00:00 2001 From: sreinkemeier Date: Tue, 20 Feb 2018 14:03:27 +0100 Subject: [PATCH 0256/2296] added update request --- .../networking/v2/extensions/vpnaas/vpnaas.go | 25 +++++++ .../v2/extensions/vpnaas/services/doc.go | 15 +++- .../v2/extensions/vpnaas/services/requests.go | 36 ++++++++++ .../v2/extensions/vpnaas/services/results.go | 6 ++ .../vpnaas/services/testing/requests_test.go | 69 ++++++++++++++++++- .../vpnaas/siteconnections/requests.go | 1 + 6 files changed, 150 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index f42aa50450..2194a4c70e 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -146,6 +146,31 @@ func CreateEndpointGroup(t *testing.T, client *gophercloud.ServiceClient) (*endp return group, nil } +// CreateEndpointGroupWithCIDR will create an endpoint group with a random name and a specified CIDR. +// An error will be returned if the group could not be created. +func CreateEndpointGroupWithCIDR(t *testing.T, client *gophercloud.ServiceClient, cidr string) (*endpointgroups.EndpointGroup, error) { + groupName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create group %s", groupName) + + createOpts := endpointgroups.CreateOpts{ + Name: groupName, + Type: endpointgroups.TypeCIDR, + Endpoints: []string{ + cidr, + }, + } + group, err := endpointgroups.Create(client, createOpts).Extract() + if err != nil { + return group, err + } + + t.Logf("Successfully created group %s", groupName) + t.Logf("%v", group) + + return group, nil +} + // DeleteEndpointGroup will delete an Endpoint group with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. diff --git a/openstack/networking/v2/extensions/vpnaas/services/doc.go b/openstack/networking/v2/extensions/vpnaas/services/doc.go index cb1ef8cabf..6bd3236c84 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/services/doc.go @@ -36,10 +36,23 @@ Example to Create a Service panic(err) } +Example to Update a Service + + serviceID := "38aee955-6283-4279-b091-8b9c828000ec" + + updateOpts := services.UpdateOpts{ + Description: "New Description", + } + + service, err := services.Update(networkClient, serviceID, updateOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a Service serviceID := "38aee955-6283-4279-b091-8b9c828000ec" - err := policies.Delete(networkClient, serviceID).ExtractErr() + err := services.Delete(networkClient, serviceID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/services/requests.go b/openstack/networking/v2/extensions/vpnaas/services/requests.go index 6cfd656a25..8d642197e0 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/services/requests.go @@ -61,6 +61,42 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { return } +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToServiceUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contains the values used when updating a VPN service +type UpdateOpts struct { + // Name is the human readable name of the service. + Name *string `json:"name,omitempty"` + + // Description is the human readable description of the service. + Description *string `json:"description,omitempty"` + + // AdminStateUp is the administrative state of the resource, which is up (true) or down (false). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToServiceUpdateMap casts aa UodateOpts struct to a map. +func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "vpnservice") +} + +// Update allows VPN services to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToServiceUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { diff --git a/openstack/networking/v2/extensions/vpnaas/services/results.go b/openstack/networking/v2/extensions/vpnaas/services/results.go index df30884e21..5e555699fc 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/results.go +++ b/openstack/networking/v2/extensions/vpnaas/services/results.go @@ -113,3 +113,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 service. +type UpdateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go index 0bfa1ded67..ca7adf327c 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go @@ -194,7 +194,74 @@ func TestDelete(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) - res := services.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/vpnservices/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` +{ + "vpnservice":{ + "name": "updatedname", + "description": "updated service", + "admin_state_up": false + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "vpnservice": { + "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + "status": "PENDING_CREATE", + "name": "updatedname", + "admin_state_up": false, + "subnet_id": null, + "tenant_id": "10039663455a446d8ba2cbb058b0f578", + "project_id": "10039663455a446d8ba2cbb058b0f578", + "id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "description": "updated service", + "external_v4_ip": "172.32.1.11", + "external_v6_ip": "2001:db8::1" + } +} + `) + }) + updatedName := "updatedname" + updatedServiceDescription := "updated service" + options := services.UpdateOpts{ + Name: &updatedName, + Description: &updatedServiceDescription, + AdminStateUp: gophercloud.Disabled, + } + + actual, err := services.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + th.AssertNoErr(t, err) + expected := services.Service{ + RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", + Status: "PENDING_CREATE", + Name: "updatedname", + ExternalV6IP: "2001:db8::1", + AdminStateUp: false, + SubnetID: "", + TenantID: "10039663455a446d8ba2cbb058b0f578", + ProjectID: "10039663455a446d8ba2cbb058b0f578", + ExternalV4IP: "172.32.1.11", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Description: "updated service", + } + th.AssertDeepEquals(t, expected, *actual) + +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index 70fd9d1152..9906e67b2a 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -126,6 +126,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul return } _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return } From 6fb25bc8ee20976989ee1db3b9d7a7b08a1e1a49 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Tue, 27 Mar 2018 21:38:32 +0200 Subject: [PATCH 0257/2296] Vpnaas: Update Site connection (#847) * Added update function and unit test * Changed strings to pointers in documentation --- .../v2/extensions/vpnaas/ikepolicies/doc.go | 6 +- .../v2/extensions/vpnaas/ipsecpolicies/doc.go | 6 +- .../extensions/vpnaas/siteconnections/doc.go | 13 +++ .../vpnaas/siteconnections/requests.go | 48 +++++++++ .../vpnaas/siteconnections/results.go | 6 ++ .../siteconnections/testing/requests_test.go | 101 ++++++++++++++++++ 6 files changed, 176 insertions(+), 4 deletions(-) diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index 2285aabd3b..ee44279afa 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -33,9 +33,11 @@ Example to Delete a Policy Example to Update an IKE policy + name := "updatedname" + description := "updated policy" updateOpts := ikepolicies.UpdateOpts{ - Name: "updatedname", - Description: "updated policy", + Name: &name, + Description: &description, Lifetime: &ikepolicies.LifetimeUpdateOpts{ Value: 7000, }, diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go index 13a1636807..91d5451a6e 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -29,9 +29,11 @@ Example to Show the details of a specific IPSec policy by ID Example to Update an IPSec policy + name := "updatedname" + description := "updated policy" updateOpts := ipsecpolicies.UpdateOpts{ - Name: "updatedname", - Description: "updated policy", + Name: &name, + Description: &description, } updatedPolicy, err := ipsecpolicies.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go index 76d71553a2..66befd3ba2 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go @@ -51,5 +51,18 @@ Example to List site connections panic(err) } +Example to Update an IPSec site connection + + description := "updated connection" + name := "updatedname" + updateOpts := siteconnections.UpdateOpts{ + Name: &name, + Description: &description, + } + updatedConnection, err := siteconnections.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() + if err != nil { + panic(err) + } + */ package siteconnections diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index 70fd9d1152..bc2a911f22 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -192,3 +192,51 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { return ConnectionPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToConnectionUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contains the values used when updating the DPD of an IPSec site connection +type DPDUpdateOpts struct { + Action Action `json:"action,omitempty"` + Timeout int `json:"timeout,omitempty"` + Interval int `json:"interval,omitempty"` +} + +// UpdateOpts contains the values used when updating an IPSec site connection +type UpdateOpts struct { + Description *string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` + LocalID string `json:"local_id,omitempty"` + PeerAddress string `json:"peer_address,omitempty"` + PeerID string `json:"peer_id,omitempty"` + PeerCIDRs []string `json:"peer_cidrs,omitempty"` + LocalEPGroupID string `json:"local_ep_group_id,omitempty"` + PeerEPGroupID string `json:"peer_ep_group_id,omitempty"` + MTU int `json:"mtu,omitempty"` + Initiator Initiator `json:"initiator,omitempty"` + PSK string `json:"psk,omitempty"` + DPD *DPDUpdateOpts `json:"dpd,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToConnectionUpdateMap casts an UpdateOpts struct to a map. +func (opts UpdateOpts) ToConnectionUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "ipsec_site_connection") +} + +// Update allows IPSec site connections to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToConnectionUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go index c8e21d5f71..3c09e4d074 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go @@ -155,3 +155,9 @@ type DeleteResult struct { type GetResult struct { commonResult } + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a connection +type UpdateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go index 710f713664..3db27364df 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -310,3 +310,104 @@ func TestList(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/vpn/ipsec-site-connections/5c561d9d-eaea-45f6-ae3e-08d1a7080828", 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, ` + { + "ipsec_site_connection": { + "psk": "updatedsecret", + "initiator": "response-only", + "name": "updatedconnection", + "description": "updateddescription" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + + { + "ipsec_site_connection": { + "status": "ACTIVE", + "psk": "updatedsecret", + "initiator": "response-only", + "name": "updatedconnection", + "admin_state_up": true, + "project_id": "10039663455a446d8ba2cbb058b0f578", + "tenant_id": "10039663455a446d8ba2cbb058b0f578", + "auth_mode": "psk", + "peer_cidrs": [], + "mtu": 1500, + "peer_ep_group_id": "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + "ikepolicy_id": "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + "vpnservice_id": "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + "dpd": { + "action": "hold", + "interval": 30, + "timeout": 120 + }, + "route_mode": "static", + "ipsecpolicy_id": "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + "local_ep_group_id": "3e1815dd-e212-43d0-8f13-b494fa553e68", + "peer_address": "172.24.4.233", + "peer_id": "172.24.4.233", + "id": "851f280f-5639-4ea3-81aa-e298525ab74b", + "description": "updateddescription" + } +} +} +`) + }) + updatedName := "updatedconnection" + updatedDescription := "updateddescription" + options := siteconnections.UpdateOpts{ + Name: &updatedName, + Description: &updatedDescription, + Initiator: siteconnections.InitiatorResponseOnly, + PSK: "updatedsecret", + } + + actual, err := siteconnections.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + th.AssertNoErr(t, err) + + expectedDPD := siteconnections.DPD{ + Action: "hold", + Interval: 30, + Timeout: 120, + } + + expected := siteconnections.Connection{ + TenantID: "10039663455a446d8ba2cbb058b0f578", + Name: "updatedconnection", + AdminStateUp: true, + PSK: "updatedsecret", + Initiator: "response-only", + IPSecPolicyID: "e6e23d0c-9519-4d52-8ea4-5b1f96d857b1", + MTU: 1500, + PeerEPGroupID: "9ad5a7e0-6dac-41b4-b20d-a7b8645fddf1", + IKEPolicyID: "9b00d6b0-6c93-4ca5-9747-b8ade7bb514f", + VPNServiceID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + LocalEPGroupID: "3e1815dd-e212-43d0-8f13-b494fa553e68", + PeerAddress: "172.24.4.233", + PeerID: "172.24.4.233", + Status: "ACTIVE", + ProjectID: "10039663455a446d8ba2cbb058b0f578", + AuthMode: "psk", + PeerCIDRs: []string{}, + DPD: expectedDPD, + RouteMode: "static", + ID: "851f280f-5639-4ea3-81aa-e298525ab74b", + Description: "updateddescription", + } + th.AssertDeepEquals(t, expected, *actual) +} From 781450b3c4fcb4f5182bcc5133adb4b2e4a09d1d Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Fri, 30 Mar 2018 09:58:14 -0700 Subject: [PATCH 0258/2296] Clustering policytype list (#860) * Clustering PolicyType list implementation * ran gofmt * fix policytype list example * add policytype list acceptance test * remove admin required * Fix review comments * add acceptance test for microversion 1.5 * Fix wrong method name --- acceptance/clients/clients.go | 19 +++ acceptance/openstack/clustering/v1/pkg.go | 2 + .../clustering/v1/policytypes_test.go | 43 ++++++ openstack/client.go | 6 + openstack/clustering/v1/policytypes/doc.go | 21 +++ .../clustering/v1/policytypes/requests.go | 14 ++ .../clustering/v1/policytypes/results.go | 38 +++++ .../clustering/v1/policytypes/testing/doc.go | 2 + .../v1/policytypes/testing/fixtures.go | 139 ++++++++++++++++++ .../v1/policytypes/testing/requests_test.go | 37 +++++ openstack/clustering/v1/policytypes/urls.go | 12 ++ 11 files changed, 333 insertions(+) create mode 100644 acceptance/openstack/clustering/v1/pkg.go create mode 100644 acceptance/openstack/clustering/v1/policytypes_test.go create mode 100644 openstack/clustering/v1/policytypes/doc.go create mode 100644 openstack/clustering/v1/policytypes/requests.go create mode 100644 openstack/clustering/v1/policytypes/results.go create mode 100644 openstack/clustering/v1/policytypes/testing/doc.go create mode 100644 openstack/clustering/v1/policytypes/testing/fixtures.go create mode 100644 openstack/clustering/v1/policytypes/testing/requests_test.go create mode 100644 openstack/clustering/v1/policytypes/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index eee80005ef..4c916309a0 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -492,3 +492,22 @@ func NewLoadBalancerV2Client() (*gophercloud.ServiceClient, error) { Region: os.Getenv("OS_REGION_NAME"), }) } + +// NewClusteringV1Client returns a *ServiceClient for making calls to the +// OpenStack Clustering v1 API. An error will be returned if authentication +// or client creation was not possible. +func NewClusteringV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + return openstack.NewClusteringV1(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} diff --git a/acceptance/openstack/clustering/v1/pkg.go b/acceptance/openstack/clustering/v1/pkg.go new file mode 100644 index 0000000000..e2200bc5ba --- /dev/null +++ b/acceptance/openstack/clustering/v1/pkg.go @@ -0,0 +1,2 @@ +// Package v1 package contains acceptance tests for the Openstack Clustering V1 service. +package v1 diff --git a/acceptance/openstack/clustering/v1/policytypes_test.go b/acceptance/openstack/clustering/v1/policytypes_test.go new file mode 100644 index 0000000000..a3bb9c7c77 --- /dev/null +++ b/acceptance/openstack/clustering/v1/policytypes_test.go @@ -0,0 +1,43 @@ +// +build acceptance clustering policytypes + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestPolicyTypeList(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + allPages, err := policytypes.List(client).AllPages() + th.AssertNoErr(t, err) + + allPolicyTypes, err := policytypes.ExtractPolicyTypes(allPages) + th.AssertNoErr(t, err) + + for _, v := range allPolicyTypes { + tools.PrintResource(t, v) + } +} + +func TestPolicyTypeList_v_1_5(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.5" + allPages, err := policytypes.List(client).AllPages() + th.AssertNoErr(t, err) + + allPolicyTypes, err := policytypes.ExtractPolicyTypes(allPages) + th.AssertNoErr(t, err) + + for _, v := range allPolicyTypes { + tools.PrintResource(t, v) + } +} diff --git a/openstack/client.go b/openstack/client.go index 5a52e57914..85705d2126 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -394,3 +394,9 @@ func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi sc.ResourceBase = sc.Endpoint + "v2.0/" return sc, err } + +// NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering +// package. +func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "clustering") +} diff --git a/openstack/clustering/v1/policytypes/doc.go b/openstack/clustering/v1/policytypes/doc.go new file mode 100644 index 0000000000..d89641c014 --- /dev/null +++ b/openstack/clustering/v1/policytypes/doc.go @@ -0,0 +1,21 @@ +/* +Package policytypes lists all policy types and shows details for a policy type from the OpenStack +Clustering Service. + +Example to list policy types + + allPages, err := policytypes.List(clusteringClient).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) + } +*/ +package policytypes diff --git a/openstack/clustering/v1/policytypes/requests.go b/openstack/clustering/v1/policytypes/requests.go new file mode 100644 index 0000000000..87ef56a049 --- /dev/null +++ b/openstack/clustering/v1/policytypes/requests.go @@ -0,0 +1,14 @@ +package policytypes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List makes a request against the API to list policy types. +func List(client *gophercloud.ServiceClient) pagination.Pager { + url := policyTypeListURL(client) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return PolicyTypePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/clustering/v1/policytypes/results.go b/openstack/clustering/v1/policytypes/results.go new file mode 100644 index 0000000000..2be8b3df09 --- /dev/null +++ b/openstack/clustering/v1/policytypes/results.go @@ -0,0 +1,38 @@ +package policytypes + +import ( + "github.com/gophercloud/gophercloud/pagination" +) + +// PolicyType represents a clustering policy type in the Openstack cloud +type PolicyType struct { + Name string `json:"name"` + Version string `json:"version"` + SupportStatus map[string][]SupportStatusType `json:"support_status"` +} + +// SupportStatusType represents the support status information for a clustering policy type +type SupportStatusType struct { + Status string `json:"status"` + Since string `json:"since"` +} + +// ExtractPolicyTypes interprets a page of results as a slice of PolicyTypes. +func ExtractPolicyTypes(r pagination.Page) ([]PolicyType, error) { + var s struct { + PolicyTypes []PolicyType `json:"policy_types"` + } + err := (r.(PolicyTypePage)).ExtractInto(&s) + return s.PolicyTypes, err +} + +// PolicyTypePage contains a single page of all policy types from a List call. +type PolicyTypePage struct { + pagination.SinglePageBase +} + +// IsEmpty determines if a PolicyType contains any results. +func (page PolicyTypePage) IsEmpty() (bool, error) { + policyTypes, err := ExtractPolicyTypes(page) + return len(policyTypes) == 0, err +} diff --git a/openstack/clustering/v1/policytypes/testing/doc.go b/openstack/clustering/v1/policytypes/testing/doc.go new file mode 100644 index 0000000000..5bd30a4704 --- /dev/null +++ b/openstack/clustering/v1/policytypes/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_policytypes_v1 +package testing diff --git a/openstack/clustering/v1/policytypes/testing/fixtures.go b/openstack/clustering/v1/policytypes/testing/fixtures.go new file mode 100644 index 0000000000..4854d70321 --- /dev/null +++ b/openstack/clustering/v1/policytypes/testing/fixtures.go @@ -0,0 +1,139 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const PolicyTypeBody = ` +{ + "policy_types": [ + { + "name": "senlin.policy.affinity", + "version": "1.0", + "support_status": { + "1.0": [ + { + "status": "SUPPORTED", + "since": "2016.10" + } + ] + } + }, + { + "name": "senlin.policy.health", + "version": "1.0", + "support_status": { + "1.0": [ + { + "status": "EXPERIMENTAL", + "since": "2016.10" + } + ] + } + }, + { + "name": "senlin.policy.scaling", + "version": "1.0", + "support_status": { + "1.0": [ + { + "status": "SUPPORTED", + "since": "2016.04" + } + ] + } + }, + { + "name": "senlin.policy.region_placement", + "version": "1.0", + "support_status": { + "1.0": [ + { + "status": "EXPERIMENTAL", + "since": "2016.04" + }, + { + "status": "SUPPORTED", + "since": "2016.10" + } + ] + } + } + ] +} +` + +var ( + ExpectedPolicyTypes = []policytypes.PolicyType{ + { + Name: "senlin.policy.affinity", + Version: "1.0", + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": { + { + Status: "SUPPORTED", + Since: "2016.10", + }, + }, + }, + }, + { + Name: "senlin.policy.health", + Version: "1.0", + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": { + { + Status: "EXPERIMENTAL", + Since: "2016.10", + }, + }, + }, + }, + { + Name: "senlin.policy.scaling", + Version: "1.0", + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": { + { + Status: "SUPPORTED", + Since: "2016.04", + }, + }, + }, + }, + { + Name: "senlin.policy.region_placement", + Version: "1.0", + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": { + { + Status: "EXPERIMENTAL", + Since: "2016.04", + }, + { + Status: "SUPPORTED", + Since: "2016.10", + }, + }, + }, + }, + } +) + +func HandlePolicyTypeList(t *testing.T) { + th.Mux.HandleFunc("/v1/policy-types", 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, PolicyTypeBody) + }) +} diff --git a/openstack/clustering/v1/policytypes/testing/requests_test.go b/openstack/clustering/v1/policytypes/testing/requests_test.go new file mode 100644 index 0000000000..2714c9876f --- /dev/null +++ b/openstack/clustering/v1/policytypes/testing/requests_test.go @@ -0,0 +1,37 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListPolicyTypes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandlePolicyTypeList(t) + + count := 0 + err := policytypes.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := policytypes.ExtractPolicyTypes(page) + if err != nil { + t.Errorf("Failed to extract policy types: %v", err) + return false, err + } + th.AssertDeepEquals(t, ExpectedPolicyTypes, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/clustering/v1/policytypes/urls.go b/openstack/clustering/v1/policytypes/urls.go new file mode 100644 index 0000000000..3cc0fec8fb --- /dev/null +++ b/openstack/clustering/v1/policytypes/urls.go @@ -0,0 +1,12 @@ +package policytypes + +import "github.com/gophercloud/gophercloud" + +const ( + apiVersion = "v1" + apiName = "policy-types" +) + +func policyTypeListURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} From 91d0b48a086c7d21bb0235009c0c877c5523b262 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 1 Apr 2018 16:31:25 -0600 Subject: [PATCH 0259/2296] Docs: Contributor Tutorial (#845) * Move assets to docs directory * Move FAQ to docs * Move styleguide to docs * Move migration guide to docs * Adding contributor tutorial --- .github/CONTRIBUTING.md | 4 +- README.md | 6 +- FAQ.md => docs/FAQ.md | 0 MIGRATING.md => docs/MIGRATING.md | 0 STYLEGUIDE.md => docs/STYLEGUIDE.md | 0 {assets => docs/assets}/openlab.png | Bin {assets => docs/assets}/vexxhost.png | Bin docs/contributor-tutorial/README.md | 12 ++ .../step-01-introduction.md | 16 ++ docs/contributor-tutorial/step-02-issues.md | 124 ++++++++++++ .../step-03-code-hunting.md | 104 ++++++++++ .../step-04-acceptance-testing.md | 27 +++ .../step-05-pull-requests.md | 183 ++++++++++++++++++ .../step-06-code-review.md | 93 +++++++++ .../step-07-congratulations.md | 9 + 15 files changed, 573 insertions(+), 5 deletions(-) rename FAQ.md => docs/FAQ.md (100%) rename MIGRATING.md => docs/MIGRATING.md (100%) rename STYLEGUIDE.md => docs/STYLEGUIDE.md (100%) rename {assets => docs/assets}/openlab.png (100%) rename {assets => docs/assets}/vexxhost.png (100%) create mode 100644 docs/contributor-tutorial/README.md create mode 100644 docs/contributor-tutorial/step-01-introduction.md create mode 100644 docs/contributor-tutorial/step-02-issues.md create mode 100644 docs/contributor-tutorial/step-03-code-hunting.md create mode 100644 docs/contributor-tutorial/step-04-acceptance-testing.md create mode 100644 docs/contributor-tutorial/step-05-pull-requests.md create mode 100644 docs/contributor-tutorial/step-06-code-review.md create mode 100644 docs/contributor-tutorial/step-07-congratulations.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d6c894637e..0d511dbf9f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -101,7 +101,7 @@ need to checkout a new feature branch: git commit ``` -7. Submit your branch as a [Pull Request](https://help.github.com/articles/creating-a-pull-request/). When submitting a Pull Request, please follow our [Style Guide](https://github.com/gophercloud/gophercloud/blob/master/STYLEGUIDE.md). +7. Submit your branch as a [Pull Request](https://help.github.com/articles/creating-a-pull-request/). When submitting a Pull Request, please follow our [Style Guide](https://github.com/gophercloud/gophercloud/blob/master/docs/STYLEGUIDE.md). > Further information about using Git can be found [here](https://git-scm.com/book/en/v2). @@ -247,4 +247,4 @@ To run tests for a particular sub-package: ## Style guide -See [here](/STYLEGUIDE.md) +See [here](/docs/STYLEGUIDE.md) diff --git a/README.md b/README.md index bb218c3fe9..8c5bfce796 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ new resource in the `server` variable (a ## Advanced Usage -Have a look at the [FAQ](./FAQ.md) for some tips on customizing the way Gophercloud works. +Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gophercloud works. ## Backwards-Compatibility Guarantees @@ -148,12 +148,12 @@ We'd like to extend special thanks and appreciation to the following: ### OpenLab - + OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases. ### VEXXHOST - + VEXXHOST is providing their services to assist with the development and testing of Gophercloud. diff --git a/FAQ.md b/docs/FAQ.md similarity index 100% rename from FAQ.md rename to docs/FAQ.md diff --git a/MIGRATING.md b/docs/MIGRATING.md similarity index 100% rename from MIGRATING.md rename to docs/MIGRATING.md diff --git a/STYLEGUIDE.md b/docs/STYLEGUIDE.md similarity index 100% rename from STYLEGUIDE.md rename to docs/STYLEGUIDE.md diff --git a/assets/openlab.png b/docs/assets/openlab.png similarity index 100% rename from assets/openlab.png rename to docs/assets/openlab.png diff --git a/assets/vexxhost.png b/docs/assets/vexxhost.png similarity index 100% rename from assets/vexxhost.png rename to docs/assets/vexxhost.png diff --git a/docs/contributor-tutorial/README.md b/docs/contributor-tutorial/README.md new file mode 100644 index 0000000000..14950b2bd6 --- /dev/null +++ b/docs/contributor-tutorial/README.md @@ -0,0 +1,12 @@ +Contributor Tutorial +==================== + +This tutorial is to help new contributors become familiar with the processes +used by the Gophercloud team when adding a new feature or fixing a bug. + +While we have a defined process for working on Gophercloud, we're very mindful +that everyone is new to this in the beginning. Please reach out for help or ask +for clarification if needed. No question is ever "dumb" or not worth our time +answering. + +To begin, go to [Step 1](step-01-introduction.md). diff --git a/docs/contributor-tutorial/step-01-introduction.md b/docs/contributor-tutorial/step-01-introduction.md new file mode 100644 index 0000000000..d806143d77 --- /dev/null +++ b/docs/contributor-tutorial/step-01-introduction.md @@ -0,0 +1,16 @@ +Step 1: Read Our Guides +======================== + +There are two introductory guides you should read before proceeding: + +* [CONTRIBUTING](/.github/CONTRIBUTING.md): The Contributing guide is a detailed + document which describes the different ways you can contribute to Gophercloud + and how to get started. This tutorial you're reading is very similar to that + guide, but presented in a different way. We still recommend you read it over. + +* [STYLE](/docs/STYLEGUIDE.md): The Style Guide documents coding conventions used + in the Gophercloud project. + +--- + +When you've finished reading those guides, proceed to [Step 2](step-02-issues.md). diff --git a/docs/contributor-tutorial/step-02-issues.md b/docs/contributor-tutorial/step-02-issues.md new file mode 100644 index 0000000000..a3ae2a237b --- /dev/null +++ b/docs/contributor-tutorial/step-02-issues.md @@ -0,0 +1,124 @@ +Step 2: Create an Issue +======================== + +Every patch / Pull Request requires a corresponding issue. If you're fixing +a bug for an existing issue, then there's no need to create a new issue. + +However, if no prior issue exists, you must create an issue. + +Reporting a Bug +--------------- + +When reporting a bug, please try to provide as much information as you +can. + +The following issues are good examples for reporting a bug: + +* https://github.com/gophercloud/gophercloud/issues/108 +* https://github.com/gophercloud/gophercloud/issues/212 +* https://github.com/gophercloud/gophercloud/issues/424 +* https://github.com/gophercloud/gophercloud/issues/588 +* https://github.com/gophercloud/gophercloud/issues/629 +* https://github.com/gophercloud/gophercloud/issues/647 + +Feature Request +--------------- + +If you've noticed that a feature is missing from Gophercloud, you'll also +need to create an issue before doing any work. This is start a discussion about +whether or not the feature should be included in Gophercloud. We don't want to +want to see you put in hours of work only to learn that the feature is out of +scope of the project. + +Feature requests can come in different forms: + +### Adding a Feature to Gophercloud Core + +The "core" of Gophercloud is the code which supports API requests and +responses: pagination, error handling, building request bodies, and parsing +response bodies are all examples of core code. + +Modifications to core will usually have the most amount of discussion than +other requests since a change to core will affect _all_ of Gophercloud. + +The following issues are examples of core change discussions: + +* https://github.com/gophercloud/gophercloud/issues/310 +* https://github.com/gophercloud/gophercloud/issues/613 +* https://github.com/gophercloud/gophercloud/issues/729 +* https://github.com/gophercloud/gophercloud/issues/713 + +### Adding a Missing Field + +If you've found a missing field in an existing struct, submit an issue to +request having it added. These kinds of issues are pretty easy to report +and resolve. + +You should also provide a link to the actual service's Python code which +defines the missing field. + +The following issues are examples of missing fields: + +* https://github.com/gophercloud/gophercloud/issues/620 +* https://github.com/gophercloud/gophercloud/issues/621 +* https://github.com/gophercloud/gophercloud/issues/658 + +There's one situation which can make adding fields more difficult: if the field +is part of an API extension rather than the base API itself. An example of this +can be seen in [this](https://github.com/gophercloud/gophercloud/issues/749) +issue. + +Here, a user reported fields missing in the `Get` function of +`networking/v2/networks`. The fields reported missing weren't missing at all, +they're just part of various Networking extensions located in +`networking/v2/extensions`. + +### Adding a Missing API Call + +If you've found a missing API action, create an issue with details of +the action. For example: + +* https://github.com/gophercloud/gophercloud/issues/715 +* https://github.com/gophercloud/gophercloud/issues/719 + +You'll want to make sure the API call is part of the upstream OpenStack project +and not an extension created by a third-party or vendor. Gophercloud only +supports the OpenStack projects proper. + +### Adding a Missing API Suite + +Adding support to a missing suite of API calls will require more than one Pull +Request. However, you can use a single issue for all PRs. + +Examples of issues which track the addition of a missing API suite are: + +* https://github.com/gophercloud/gophercloud/issues/539 +* https://github.com/gophercloud/gophercloud/issues/555 +* https://github.com/gophercloud/gophercloud/issues/571 +* https://github.com/gophercloud/gophercloud/issues/583 +* https://github.com/gophercloud/gophercloud/issues/605 + +Note how the issue breaks down the implementation by request types (Create, +Update, Delete, Get, List). + +Also note how these issues provide links to the service's Python code. These +links are not required for _issues_, but it's usually a good idea to provide +them, anyway. These links _are required_ for PRs and that will be covered in +detail in a later step of this tutorial. + +### Adding a Missing OpenStack Project + +These kinds of feature additions are large undertakings. Adding support for +an entire OpenStack project is something the Gophercloud team very much +appreciates, but you should be prepared for several weeks of work and +interaction with the Gophercloud team. + +An example of how to create an issue for an entire project can be seen +here: + +* https://github.com/gophercloud/gophercloud/issues/723 + +--- + +With all of the above in mind, proceed to [Step 3](step-03-code-hunting.md) to +learn about Code Hunting. diff --git a/docs/contributor-tutorial/step-03-code-hunting.md b/docs/contributor-tutorial/step-03-code-hunting.md new file mode 100644 index 0000000000..f773eec040 --- /dev/null +++ b/docs/contributor-tutorial/step-03-code-hunting.md @@ -0,0 +1,104 @@ +Step 3: Code Hunting +==================== + +If you plan to submit a feature or bug fix to Gophercloud, you must be +able to prove your code correctly works with the OpenStack service in +question. + +Let's use the following issue as an example: +[https://github.com/gophercloud/gophercloud/issues/621](https://github.com/gophercloud/gophercloud/issues/621). +In this issue, there's a request being made to add support for +`availability_zone_hints` to the `networking/v2/networks` package. +Meaning, we want to change: + +```go +type Network struct { + ID string `json:"id"` + Name string `json:"name"` + AdminStateUp bool `json:"admin_state_up"` + Status string `json:"status"` + Subnets []string `json:"subnets"` + TenantID string `json:"tenant_id"` + Shared bool `json:"shared"` +} +``` + +to look like + +```go +type Network struct { + ID string `json:"id"` + Name string `json:"name"` + AdminStateUp bool `json:"admin_state_up"` + Status string `json:"status"` + Subnets []string `json:"subnets"` + TenantID string `json:"tenant_id"` + Shared bool `json:"shared"` + + AvailabilityZoneHints []string `json:"availability_zone_hints"` +} +``` + +We need to be sure that `availability_zone_hints` is a field which really does +exist in the OpenStack Neutron project and it's not a field which was added as +a customization to a single OpenStack cloud. + +In addition, we need to ensure that `availability_zone_hints` is really a +`[]string` and not a different kind of type. + +One way of verifying this is through the [OpenStack API reference +documentation](https://developer.openstack.org/api-ref/network/v2/). +However, the API docs might either be incorrect or they might not provide all of +the details we need to know in order to ensure this field is added correctly. + +> Note: when we say the API docs might be incorrect, we are _not_ implying +> that the API docs aren't useful or that the contributors who work on the API +> docs are wrong. OpenStack moves fast. Typos happen. Forgetting to update +> documentation happens. + +Since the OpenStack service itself correctly accepts and processes the fields, +the best source of information on how the field works is in the service code +itself. + +Continuing on with using #621 as an example, we can find the definition of +`availability_zone_hints` in the following piece of code: + +https://github.com/openstack/neutron/blob/8e9959725eda4063a318b4ba6af1e3494cad9e35/neutron/objects/network.py#L191 + +The above code confirms that `availability_zone_hints` is indeed part of the +`Network` object and that its type is a list of strings (`[]string`). + +This example is a best-case situation: the code is relatively easy to find +and it's simple to understand. However, there will be times when proving the +implementation in the service code is difficult. Make no mistake, this is _not_ +fun work. This can sometimes be more difficult than writing the actual patch +for Gophercloud. However, this is an essential step to ensuring the feature +or bug fix is correctly added to Gophercloud. + +Examples of good code hunting can be seen here: + +* https://github.com/gophercloud/gophercloud/issues/539 +* https://github.com/gophercloud/gophercloud/issues/555 +* https://github.com/gophercloud/gophercloud/issues/571 +* https://github.com/gophercloud/gophercloud/issues/583 +* https://github.com/gophercloud/gophercloud/issues/605 + +Code Hunting Tips +----------------- + +OpenStack projects differ from one to another. Code is organized in different +ways. However, the following tips should be useful across all projects. + +* The logic which implements Create and Delete actions is usually either located + in the "model" or "controller" portion of the code. + +* Use Github's search box to search for the exact field you're working on. + Review all results to gain a good understanding of everywhere the field is + used. + +* When adding a field, look for an object model or a schema of some sort. + +--- + +Proceed to [Step 4](step-04-acceptance-testing.md) to learn about Acceptance +Testing. diff --git a/docs/contributor-tutorial/step-04-acceptance-testing.md b/docs/contributor-tutorial/step-04-acceptance-testing.md new file mode 100644 index 0000000000..fe82717439 --- /dev/null +++ b/docs/contributor-tutorial/step-04-acceptance-testing.md @@ -0,0 +1,27 @@ +Step 4: Acceptance Testing +========================== + +If we haven't started working on the feature or bug fix, why are we talking +about Acceptance Testing now? + +Before you implement a feature or bug fix, you _must_ be able to test your code +in a working OpenStack environment. Please do not submit code which you have +only tested with offline unit tests. + +Blindly submitting code is dangerous to the Gophercloud project. Developers +from all over the world use Gophercloud in many different projects. If you +submit code which is untested, it can cause these projects to break or become +unstable. + +And, to be frank, submitting untested code will inevitably cause someone else +to have to spend time fixing it. + +If you don't have an OpenStack environment to test with, we have lots of +documentation [here](/acceptance) to help you build your own small OpenStack +environment for testing. + +--- + +Once you've confirmed you are able to test your code, proceed to +[Step 5](step-05-pull-requests.md) to (finally!) start working on a Pull +Request. diff --git a/docs/contributor-tutorial/step-05-pull-requests.md b/docs/contributor-tutorial/step-05-pull-requests.md new file mode 100644 index 0000000000..9bf1e0a4d8 --- /dev/null +++ b/docs/contributor-tutorial/step-05-pull-requests.md @@ -0,0 +1,183 @@ +Step 5: Writing the Code +======================== + +At this point, you should have: + +- [x] Identified a feature or bug fix +- [x] Opened an Issue about it +- [x] Located the project's service code which validates the feature or fix +- [x] Have an OpenStack environment available to test with + +Now it's time to write the actual code! We recommend reading over the +[CONTRIBUTING](/.github/CONTRIBUTING.md) guide again as a refresh. Notably +the [Getting Started](/.github/CONTRIBUTING.md#getting-started) section will +help you set up a `git` repository correctly. + +We encourage you to browse the existing Gophercloud code to find examples +of similar implementations. It would be a _very_ rare occurrence for you +to be implementing something that hasn't already been done. + +Use the existing packages as templates and mirror the style, naming, and +logic. + +Types of Pull Requests +---------------------- + +The amount of changes you plan to make will determine how much code you should +submit as Pull Requests. + +### A Single Bug Fix + +If you're implementing a single bug fix, then creating one `git` branch and +submitting one Pull Request is fine. + +### Adding a Single Field + +If you're adding a single field, then a single Pull Request is also fine. See +[#662](https://github.com/gophercloud/gophercloud/pull/662) as an example of +this. + +If you plan to add more than one missing field, you will need to open a Pull +Request for _each_ field. + +### Adding a Single API Call + +Single API calls can also be submitted as a single Pull Request. See +[#722](https://github.com/gophercloud/gophercloud/pull/722) as an example of +this. + +### Adding a Suite of API Calls + +If you're adding support for a "suite" of API calls (meaning: Create, Update, +Delete, Get), then you will need to create one Pull Request for _each_ call. + +The following Pull Requests are good examples of how to do this: + +* https://github.com/gophercloud/gophercloud/pull/584 +* https://github.com/gophercloud/gophercloud/pull/586 +* https://github.com/gophercloud/gophercloud/pull/587 +* https://github.com/gophercloud/gophercloud/pull/594 + +### Adding an Entire OpenStack Project + +To add an entire OpenStack project, you must break each set of API calls into +individual Pull Requests. Implementing an entire project can be thought of as +implementing multiple API suites. + +An example of this can be seen from the Pull Requests referenced in +[#723](https://github.com/gophercloud/gophercloud/issues/723). + +What to Include in a Pull Request +--------------------------------- + +Each Pull Request should contain the following: + +1. The actual Go code to implement the feature or bug fix +2. Unit tests +3. Acceptance tests +4. Documentation + +Whether you want to bundle all of the above into a single commit or multiple +commits is up to you. Use your preferred style. + +### Unit Tests + +Unit tests should provide basic validation that your code works as intended. + +Please do not use JSON fixtures from the API reference documentation. Please +generate your own fixtures using the OpenStack environment you're +[testing](step-04-acceptance-testing.md) with. + +### Acceptance Tests + +Since unit tests are not run against an actual OpenStack environment, +acceptance tests can arguably be more important. The acceptance tests that you +include in your Pull Request should confirm that your implemented code works +as intended with an actual OpenStack environment. + +### Documentation + +All documentation in Gophercloud is done through in-line `godoc`. Please make +sure to document all fields, functions, and methods appropriately. In addition, +each package has a `doc.go` file which should be created or amended with +details of your Pull Request, where appropriate. + +Dealing with Related Pull Requests +---------------------------------- + +If you plan to open more than one Pull Request, it's only natural that code +from one Pull Request will be dependent on code from the prior Pull Request. + +There are two methods of handling this: + +### Create Independent Pull Requests + +With this method, each Pull Request has all of the code to fully implement +the code in question. Each Pull Request can be merged in any order because +it's self contained. + +Use the following `git` workflow to implement this method: + +```shell +$ git checkout master +$ git pull +$ git checkout -b identityv3-regions-create +$ (write your code) +$ git add . +$ git commit -m "Implementing Regions Create" + +$ git checkout master +$ git checkout -b identityv3-regions-update +$ (write your code) +$ git add . +$ git commit -m "Implementing Regions Update" +``` + +Advantages of this Method: + +* Pull Requests can be merged in any order +* Additional commits to one Pull Request are independent of other Pull Requests + +Disadvantages of this Method: + +* There will be _a lot_ of duplicate code in each Pull Request +* You will have to rebase all other Pull Requests and resolve a good amount of + merge conflicts. + +### Create a Chain of Pull Requests + +With this method, each Pull Request is based off of a previous Pull Request. +Pull Requests will have to be merged in a specific order since there is a +defined relationship. + +Use the following `git` workflow to implement this method: + +```shell +$ git checkout master +$ git pull +$ git checkout -b identityv3-regions-create +$ (write your code) +$ git add . +$ git commit -m "Implementing Regions Create" + +$ git checkout -b identityv3-regions-update +$ (write your code) +$ git add . +$ git commit -m "Implementing Regions Update" +``` + +Advantages of this Method: + +* Each Pull Request becomes smaller since you are building off of the last + +Disadvantages of this Method: + +* If a Pull Request requires changes, you will have to rebase _all_ child + Pull Requests based off of the parent. + +The choice of method is up to you. + +--- + +Once you have your code written, submit a Pull Request to Gophercloud and +proceed to [Step 6](step-06-code-review.md). diff --git a/docs/contributor-tutorial/step-06-code-review.md b/docs/contributor-tutorial/step-06-code-review.md new file mode 100644 index 0000000000..ac3b68808b --- /dev/null +++ b/docs/contributor-tutorial/step-06-code-review.md @@ -0,0 +1,93 @@ +Step 6: Code Review +=================== + +Once you've submitted a Pull Request, three things will happen automatically: + +1. Travis-CI will run a set of simple tests: + + a. Unit Tests + + b. Code Formatting checks + + c. `go vet` checks + +2. Coveralls will run a coverage test. +3. [OpenLab](https://openlabtesting.org/) will run acceptance tests. + +Depending on the results of the above, you might need to make additional +changes to your code. + +While you're working on the finishing touches to your code, it is helpful +to add a `[wip]` tag to the title of your Pull Request. + +You are most welcomed to take as much time as you need to work on your Pull +Request. As well, take advantage of the automatic testing that is done to +each commit. + +### Travis-CI + +If Travis reports code formatting issues, please make sure to run `gofmt` on all +of your code. Travis will also report errors with unit tests, so you should +ensure those are fixed, too. + +### Coveralls + +If Coveralls reports a decrease in test coverage, check and make sure you have +provided unit tests. A decrease in test coverage is _sometimes_ unavoidable and +ignorable. + +### OpenLab + +OpenLab does not yet run a full suite of acceptance tests, so it's possible +that the acceptance tests you've included were not run. When this happens, +a core member for Gophercloud will run the tests manually. + +There are times when a core reviewer does not have access to the resources +required to run the acceptance tests. When this happens, it is essential +that you've run them yourself (See [Step 4](step-04.md)). + +Request a Code Review +--------------------- + +When you feel your Pull Request is ready for review, please leave a comment +requesting a code review. If you don't explicitly ask for a code review, a +core member might not know the Pull Request is ready for review. + +Additionally, if there are parts of your implementation that you are unsure +about, please ask for help. We're more than happy to provide advice. + +During the code review process, a core member will review the code you've +submitted and either request changes or request additional information. +Generally these requests fall under the following categories: + +1. Code which needs to be reformatted (See our [Style Guide](/docs/STYLEGUIDE.md) + for conventions used. + +2. Requests for additional information about the validity of something. This + might happen because the included supporting service code URLs don't have + enough information. + +3. Missing unit tests or acceptance tests. + +Submitting Changes +------------------ + +If a code review requires changes to be submitted, please do not squash your +commits. Please only add new commits to the Pull Request. This is to help the +code reviewer see only the changes that were made. + +It's Never Personal +------------------- + +Code review is a healthy exercise where a new set of eyes can sometimes spot +items forgotten by the author. + +Please don't take change requests personally. Our intention is to ensure the +code is correct before merging. + +--- + +Once the code has been reviewed and approved, a core member will merge your +Pull Request. + +Please proceed to [Step 7](step-07-congratulations.md). diff --git a/docs/contributor-tutorial/step-07-congratulations.md b/docs/contributor-tutorial/step-07-congratulations.md new file mode 100644 index 0000000000..e14b794143 --- /dev/null +++ b/docs/contributor-tutorial/step-07-congratulations.md @@ -0,0 +1,9 @@ +Step 7: Congratulations! +======================== + +At this point your code is merged and you've either fixed a bug or added a new +feature to Gophercloud! + +We completely understand that this has been a long process. We appreciate your +patience as well as the time you have taken for working on this. You've made +Gophercloud a better project with your work. From d38bdaf7706c833e9f32c6b1cac3de2191266ff0 Mon Sep 17 00:00:00 2001 From: "zhang.zujian" Date: Mon, 2 Apr 2018 14:17:39 +0800 Subject: [PATCH 0260/2296] fix Fatalf(): format %v reads arg #1, but call has only 0 args --- acceptance/openstack/identity/v3/endpoint_test.go | 8 ++++---- acceptance/openstack/identity/v3/projects_test.go | 8 ++++---- acceptance/openstack/identity/v3/service_test.go | 2 +- acceptance/openstack/identity/v3/token_test.go | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/acceptance/openstack/identity/v3/endpoint_test.go b/acceptance/openstack/identity/v3/endpoint_test.go index a589970606..68f5a351d4 100644 --- a/acceptance/openstack/identity/v3/endpoint_test.go +++ b/acceptance/openstack/identity/v3/endpoint_test.go @@ -15,7 +15,7 @@ import ( func TestEndpointsList(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } allPages, err := endpoints.List(client, nil).AllPages() @@ -36,7 +36,7 @@ func TestEndpointsList(t *testing.T) { func TestEndpointsNavigateCatalog(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } // Discover the service we're interested in. @@ -51,7 +51,7 @@ func TestEndpointsNavigateCatalog(t *testing.T) { allServices, err := services.ExtractServices(allPages) if err != nil { - t.Fatalf("Unable to extract service: %v") + t.Fatalf("Unable to extract service: %v", err) } if len(allServices) != 1 { @@ -74,7 +74,7 @@ func TestEndpointsNavigateCatalog(t *testing.T) { allEndpoints, err := endpoints.ExtractEndpoints(allPages) if err != nil { - t.Fatalf("Unable to extract endpoint: %v") + t.Fatalf("Unable to extract endpoint: %v", err) } if len(allEndpoints) != 1 { diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index 08a5cfdad4..326b4ad034 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -64,7 +64,7 @@ func TestProjectsGet(t *testing.T) { func TestProjectsCRUD(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } project, err := CreateProject(t, client, nil) @@ -91,7 +91,7 @@ func TestProjectsCRUD(t *testing.T) { func TestProjectsDomain(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } var iTrue = true @@ -126,14 +126,14 @@ func TestProjectsDomain(t *testing.T) { _, err = projects.Update(client, projectDomain.ID, updateOpts).Extract() if err != nil { - t.Fatalf("Unable to disable domain: %v") + t.Fatalf("Unable to disable domain: %v", err) } } func TestProjectsNested(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } projectMain, err := CreateProject(t, client, nil) diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go index ffd7e4d1fb..ed9d3855df 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/acceptance/openstack/identity/v3/service_test.go @@ -13,7 +13,7 @@ import ( func TestServicesList(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } listOpts := services.ListOpts{ diff --git a/acceptance/openstack/identity/v3/token_test.go b/acceptance/openstack/identity/v3/token_test.go index 0f471f776b..b426116f4d 100644 --- a/acceptance/openstack/identity/v3/token_test.go +++ b/acceptance/openstack/identity/v3/token_test.go @@ -14,7 +14,7 @@ import ( func TestGetToken(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") + t.Fatalf("Unable to obtain an identity client: %v", err) } ao, err := openstack.AuthOptionsFromEnv() From fd734d98d0a24dc7e7e14861563eef154dec560b Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Mon, 2 Apr 2018 15:38:50 +0800 Subject: [PATCH 0261/2296] Support acceptance test against OpenStack Queens release in OpenLab Input comment "recheck stable/queens" in pull request comments, that will trigger acceptance tests against OpenStack Queens release. OpenLab will report test result in followint comments automatically. And add support for Newton and Ocata release - "recheck stable/newton" -> test against OpenStack Newton release - "recheck stable/ocata" -> test against OpenStack Ocata release Related-Bug: theopenlab/openlab#38 --- .zuul.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.zuul.yaml b/.zuul.yaml index c259d03e18..436fbb6e94 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -7,6 +7,15 @@ recheck-mitaka: jobs: - gophercloud-acceptance-test-mitaka + recheck-newton: + jobs: + - gophercloud-acceptance-test-newton + recheck-ocata: + jobs: + - gophercloud-acceptance-test-ocata recheck-pike: jobs: - gophercloud-acceptance-test-pike + recheck-queens: + jobs: + - gophercloud-acceptance-test-queens From d2426f82c33d910620b0a6a34ecc5fa57a693f6c Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Mon, 2 Apr 2018 11:49:58 -0700 Subject: [PATCH 0262/2296] Clustering PolicyType Get implementation (#861) * Add Clustering PolicyType Get implementation * Changed PolicyTypeDetail Schema to map[string]interface{} * Updated test fixtures to use real response from API --- .../clustering/v1/policytypes_test.go | 21 ++++ openstack/clustering/v1/policytypes/doc.go | 9 ++ .../clustering/v1/policytypes/requests.go | 7 ++ .../clustering/v1/policytypes/results.go | 25 +++++ .../v1/policytypes/testing/fixtures.go | 104 ++++++++++++++++-- .../v1/policytypes/testing/requests_test.go | 12 ++ openstack/clustering/v1/policytypes/urls.go | 4 + 7 files changed, 175 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/clustering/v1/policytypes_test.go b/acceptance/openstack/clustering/v1/policytypes_test.go index a3bb9c7c77..fdb42a3153 100644 --- a/acceptance/openstack/clustering/v1/policytypes_test.go +++ b/acceptance/openstack/clustering/v1/policytypes_test.go @@ -41,3 +41,24 @@ func TestPolicyTypeList_v_1_5(t *testing.T) { tools.PrintResource(t, v) } } + +func TestPolicyTypeGet(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + policyType, err := policytypes.Get(client, "senlin.policy.batch-1.0").Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, policyType) +} + +func TestPolicyTypeGet_v_1_5(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.5" + policyType, err := policytypes.Get(client, "senlin.policy.batch-1.0").Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, policyType) +} diff --git a/openstack/clustering/v1/policytypes/doc.go b/openstack/clustering/v1/policytypes/doc.go index d89641c014..1886939f39 100644 --- a/openstack/clustering/v1/policytypes/doc.go +++ b/openstack/clustering/v1/policytypes/doc.go @@ -17,5 +17,14 @@ Example to list policy types for _, policyType := range allPolicyTypes { fmt.Printf("%+v\n", policyType) } + +Example of get policy type details + + policyTypeName := "senlin.policy.affinity-1.0" + policyTypeDetail, err := policyTypes.Get(clusteringClient, policyTypeName).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", policyTypeDetail) */ package policytypes diff --git a/openstack/clustering/v1/policytypes/requests.go b/openstack/clustering/v1/policytypes/requests.go index 87ef56a049..bb8a48d40f 100644 --- a/openstack/clustering/v1/policytypes/requests.go +++ b/openstack/clustering/v1/policytypes/requests.go @@ -12,3 +12,10 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { return PolicyTypePage{pagination.SinglePageBase(r)} }) } + +// Get makes a request against the API to get details for a policy type +func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { + _, r.Err = client.Get(policyTypeGetURL(client, policyTypeName), &r.Body, + &gophercloud.RequestOpts{OkCodes: []int{200}}) + return +} diff --git a/openstack/clustering/v1/policytypes/results.go b/openstack/clustering/v1/policytypes/results.go index 2be8b3df09..4d5d8e1c3f 100644 --- a/openstack/clustering/v1/policytypes/results.go +++ b/openstack/clustering/v1/policytypes/results.go @@ -1,6 +1,7 @@ package policytypes import ( + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -36,3 +37,27 @@ func (page PolicyTypePage) IsEmpty() (bool, error) { policyTypes, err := ExtractPolicyTypes(page) return len(policyTypes) == 0, err } + +// PolicyTypeDetail represents the detailed policy type information for a clustering policy type +type PolicyTypeDetail struct { + Name string `json:"name"` + Schema map[string]interface{} `json:"schema"` + SupportStatus map[string][]SupportStatusType `json:"support_status,omitempty"` +} + +// Extract provides access to the individual policy type returned by Get and extracts PolicyTypeDetail +func (r policyTypeResult) Extract() (*PolicyTypeDetail, error) { + var s struct { + PolicyType *PolicyTypeDetail `json:"policy_type"` + } + err := r.ExtractInto(&s) + return s.PolicyType, err +} + +type policyTypeResult struct { + gophercloud.Result +} + +type GetResult struct { + policyTypeResult +} diff --git a/openstack/clustering/v1/policytypes/testing/fixtures.go b/openstack/clustering/v1/policytypes/testing/fixtures.go index 4854d70321..3db56cdadc 100644 --- a/openstack/clustering/v1/policytypes/testing/fixtures.go +++ b/openstack/clustering/v1/policytypes/testing/fixtures.go @@ -10,6 +10,8 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +const FakePolicyTypetoGet = "fake-policytype" + const PolicyTypeBody = ` { "policy_types": [ @@ -69,6 +71,45 @@ const PolicyTypeBody = ` } ` +const PolicyTypeDetailBody = ` +{ + "policy_type": { + "name": "senlin.policy.batch-1.0", + "schema": { + "max_batch_size": { + "default": -1, + "description": "Maximum number of nodes that will be updated in parallel.", + "required": false, + "type": "Integer", + "updatable": false + }, + "min_in_service": { + "default": 1, + "description": "Minimum number of nodes in service when performing updates.", + "required": false, + "type": "Integer", + "updatable": false + }, + "pause_time": { + "default": 60, + "description": "Interval in seconds between update batches if any.", + "required": false, + "type": "Integer", + "updatable": false + } + }, + "support_status": { + "1.0": [ + { + "status": "EXPERIMENTAL", + "since": "2017.02" + } + ] + } + } +} +` + var ( ExpectedPolicyTypes = []policytypes.PolicyType{ { @@ -124,16 +165,65 @@ var ( }, }, } + + ExpectedPolicyTypeDetail = &policytypes.PolicyTypeDetail{ + Name: "senlin.policy.batch-1.0", + Schema: map[string]interface{}{ + "max_batch_size": map[string]interface{}{ + "default": float64(-1), + "description": "Maximum number of nodes that will be updated in parallel.", + "required": false, + "type": "Integer", + "updatable": false, + }, + "min_in_service": map[string]interface{}{ + "default": float64(1), + "description": "Minimum number of nodes in service when performing updates.", + "required": false, + "type": "Integer", + "updatable": false, + }, + "pause_time": map[string]interface{}{ + "default": float64(60), + "description": "Interval in seconds between update batches if any.", + "required": false, + "type": "Integer", + "updatable": false, + }, + }, + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": []policytypes.SupportStatusType{ + { + Status: "EXPERIMENTAL", + Since: "2017.02", + }, + }, + }, + } ) func HandlePolicyTypeList(t *testing.T) { - th.Mux.HandleFunc("/v1/policy-types", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.Mux.HandleFunc("/v1/policy-types", + 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, PolicyTypeBody) + }) +} + +func HandlePolicyTypeGet(t *testing.T) { + th.Mux.HandleFunc("/v1/policy-types/"+FakePolicyTypetoGet, + 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) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, PolicyTypeBody) - }) + fmt.Fprintf(w, PolicyTypeDetailBody) + }) } diff --git a/openstack/clustering/v1/policytypes/testing/requests_test.go b/openstack/clustering/v1/policytypes/testing/requests_test.go index 2714c9876f..87e42fab1a 100644 --- a/openstack/clustering/v1/policytypes/testing/requests_test.go +++ b/openstack/clustering/v1/policytypes/testing/requests_test.go @@ -35,3 +35,15 @@ func TestListPolicyTypes(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestGetPolicyType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandlePolicyTypeGet(t) + + actual, err := policytypes.Get(fake.ServiceClient(), FakePolicyTypetoGet).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, ExpectedPolicyTypeDetail, actual) +} diff --git a/openstack/clustering/v1/policytypes/urls.go b/openstack/clustering/v1/policytypes/urls.go index 3cc0fec8fb..b291a95c70 100644 --- a/openstack/clustering/v1/policytypes/urls.go +++ b/openstack/clustering/v1/policytypes/urls.go @@ -10,3 +10,7 @@ const ( func policyTypeListURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } + +func policyTypeGetURL(client *gophercloud.ServiceClient, policyTypeName string) string { + return client.ServiceURL(apiVersion, apiName, policyTypeName) +} From 3ff109d28762ddc1560994a051fab6b1146c6dbf Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Apr 2018 13:14:32 -0600 Subject: [PATCH 0263/2296] Octavia (#854) * LBaaS v2: Initial commit of dedicated lbaas packages * LBaaS v2: Customizing the loadbalancer packages * Networking v2: Removing old Octavia acceptance test * LBaaS v2: gofmt fixes * LBaaS v2: fix acceptance test client * LBaaS v2: Support Cascading delete by way of a DeleteOpts parameter --- acceptance/clients/clients.go | 30 +- .../loadbalancer/v2/listeners_test.go | 32 ++ .../openstack/loadbalancer/v2/loadbalancer.go | 309 ++++++++++++++ .../loadbalancer/v2/loadbalancers_test.go | 326 +++++++++++++++ .../loadbalancer/v2/monitors_test.go | 32 ++ acceptance/openstack/loadbalancer/v2/pkg.go | 1 + .../openstack/loadbalancer/v2/pools_test.go | 32 ++ .../extensions/lbaas_v2/loadbalancers_test.go | 149 ------- openstack/loadbalancer/v2/doc.go | 3 + openstack/loadbalancer/v2/listeners/doc.go | 63 +++ .../loadbalancer/v2/listeners/requests.go | 199 +++++++++ .../loadbalancer/v2/listeners/results.go | 131 ++++++ .../loadbalancer/v2/listeners/testing/doc.go | 2 + .../v2/listeners/testing/fixtures.go | 213 ++++++++++ .../v2/listeners/testing/requests_test.go | 137 +++++++ openstack/loadbalancer/v2/listeners/urls.go | 16 + .../loadbalancer/v2/loadbalancers/doc.go | 76 ++++ .../loadbalancer/v2/loadbalancers/requests.go | 210 ++++++++++ .../loadbalancer/v2/loadbalancers/results.go | 149 +++++++ .../v2/loadbalancers/testing/doc.go | 2 + .../v2/loadbalancers/testing/fixtures.go | 284 +++++++++++++ .../v2/loadbalancers/testing/requests_test.go | 162 ++++++++ .../loadbalancer/v2/loadbalancers/urls.go | 21 + openstack/loadbalancer/v2/monitors/doc.go | 69 ++++ .../loadbalancer/v2/monitors/requests.go | 257 ++++++++++++ openstack/loadbalancer/v2/monitors/results.go | 149 +++++++ .../loadbalancer/v2/monitors/testing/doc.go | 2 + .../v2/monitors/testing/fixtures.go | 215 ++++++++++ .../v2/monitors/testing/requests_test.go | 154 +++++++ openstack/loadbalancer/v2/monitors/urls.go | 16 + openstack/loadbalancer/v2/pools/doc.go | 124 ++++++ openstack/loadbalancer/v2/pools/requests.go | 356 ++++++++++++++++ openstack/loadbalancer/v2/pools/results.go | 273 ++++++++++++ .../loadbalancer/v2/pools/testing/doc.go | 2 + .../loadbalancer/v2/pools/testing/fixtures.go | 388 ++++++++++++++++++ .../v2/pools/testing/requests_test.go | 262 ++++++++++++ openstack/loadbalancer/v2/pools/urls.go | 25 ++ .../loadbalancer/v2/testhelper/client.go | 14 + 38 files changed, 4722 insertions(+), 163 deletions(-) create mode 100644 acceptance/openstack/loadbalancer/v2/listeners_test.go create mode 100644 acceptance/openstack/loadbalancer/v2/loadbalancer.go create mode 100644 acceptance/openstack/loadbalancer/v2/loadbalancers_test.go create mode 100644 acceptance/openstack/loadbalancer/v2/monitors_test.go create mode 100644 acceptance/openstack/loadbalancer/v2/pkg.go create mode 100644 acceptance/openstack/loadbalancer/v2/pools_test.go create mode 100644 openstack/loadbalancer/v2/doc.go create mode 100644 openstack/loadbalancer/v2/listeners/doc.go create mode 100644 openstack/loadbalancer/v2/listeners/requests.go create mode 100644 openstack/loadbalancer/v2/listeners/results.go create mode 100644 openstack/loadbalancer/v2/listeners/testing/doc.go create mode 100644 openstack/loadbalancer/v2/listeners/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/listeners/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/listeners/urls.go create mode 100644 openstack/loadbalancer/v2/loadbalancers/doc.go create mode 100644 openstack/loadbalancer/v2/loadbalancers/requests.go create mode 100644 openstack/loadbalancer/v2/loadbalancers/results.go create mode 100644 openstack/loadbalancer/v2/loadbalancers/testing/doc.go create mode 100644 openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/loadbalancers/urls.go create mode 100644 openstack/loadbalancer/v2/monitors/doc.go create mode 100644 openstack/loadbalancer/v2/monitors/requests.go create mode 100644 openstack/loadbalancer/v2/monitors/results.go create mode 100644 openstack/loadbalancer/v2/monitors/testing/doc.go create mode 100644 openstack/loadbalancer/v2/monitors/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/monitors/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/monitors/urls.go create mode 100644 openstack/loadbalancer/v2/pools/doc.go create mode 100644 openstack/loadbalancer/v2/pools/requests.go create mode 100644 openstack/loadbalancer/v2/pools/results.go create mode 100644 openstack/loadbalancer/v2/pools/testing/doc.go create mode 100644 openstack/loadbalancer/v2/pools/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/pools/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/pools/urls.go create mode 100644 openstack/loadbalancer/v2/testhelper/client.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 4c916309a0..93e852418a 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -460,20 +460,6 @@ func NewSharedFileSystemV2Client() (*gophercloud.ServiceClient, error) { }) } -// configureDebug will configure the provider client to print the API -// requests and responses if OS_DEBUG is enabled. -func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderClient { - if os.Getenv("OS_DEBUG") != "" { - client.HTTPClient = http.Client{ - Transport: &LogRoundTripper{ - Rt: &http.Transport{}, - }, - } - } - - return client -} - // NewLoadBalancerV2Client returns a *ServiceClient for making calls to the // OpenStack Octavia v2 API. An error will be returned if authentication // or client creation was not possible. @@ -488,6 +474,8 @@ func NewLoadBalancerV2Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewLoadBalancerV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -511,3 +499,17 @@ func NewClusteringV1Client() (*gophercloud.ServiceClient, error) { Region: os.Getenv("OS_REGION_NAME"), }) } + +// configureDebug will configure the provider client to print the API +// requests and responses if OS_DEBUG is enabled. +func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderClient { + if os.Getenv("OS_DEBUG") != "" { + client.HTTPClient = http.Client{ + Transport: &LogRoundTripper{ + Rt: &http.Transport{}, + }, + } + } + + return client +} diff --git a/acceptance/openstack/loadbalancer/v2/listeners_test.go b/acceptance/openstack/loadbalancer/v2/listeners_test.go new file mode 100644 index 0000000000..f643676544 --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/listeners_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking loadbalancer listeners + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" +) + +func TestListenersList(t *testing.T) { + client, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := listeners.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list listeners: %v", err) + } + + allListeners, err := listeners.ExtractListeners(allPages) + if err != nil { + t.Fatalf("Unable to extract listeners: %v", err) + } + + for _, listener := range allListeners { + tools.PrintResource(t, listener) + } +} diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go new file mode 100644 index 0000000000..0a811d69c3 --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -0,0 +1,309 @@ +package v2 + +import ( + "fmt" + "strings" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" +) + +const loadbalancerActiveTimeoutSeconds = 300 +const loadbalancerDeleteTimeoutSeconds = 300 + +// CreateListener will create a listener for a given load balancer on a random +// port with a random name. An error will be returned if the listener could not +// be created. +func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) { + listenerName := tools.RandomString("TESTACCT-", 8) + listenerPort := tools.RandomInt(1, 100) + + t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) + + createOpts := listeners.CreateOpts{ + Name: listenerName, + LoadbalancerID: lb.ID, + Protocol: "TCP", + ProtocolPort: listenerPort, + } + + listener, err := listeners.Create(client, createOpts).Extract() + if err != nil { + return listener, err + } + + t.Logf("Successfully created listener %s", listenerName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active") + } + + return listener, nil +} + +// 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) (*loadbalancers.LoadBalancer, error) { + lbName := tools.RandomString("TESTACCT-", 8) + + t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) + + createOpts := loadbalancers.CreateOpts{ + Name: lbName, + VipSubnetID: subnetID, + AdminStateUp: gophercloud.Enabled, + } + + lb, err := loadbalancers.Create(client, createOpts).Extract() + if err != nil { + return lb, err + } + + t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID) + t.Logf("Waiting for loadbalancer %s to become active", lbName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return lb, err + } + + t.Logf("LoadBalancer %s is active", lbName) + + return lb, nil +} + +// CreateMember will create a member with a random name, port, address, and +// weight. An error will be returned if the member could not be created. +func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) { + memberName := tools.RandomString("TESTACCT-", 8) + memberPort := tools.RandomInt(100, 1000) + memberWeight := tools.RandomInt(1, 10) + + cidrParts := strings.Split(subnetCIDR, "/") + subnetParts := strings.Split(cidrParts[0], ".") + memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100)) + + t.Logf("Attempting to create member %s", memberName) + + createOpts := pools.CreateMemberOpts{ + Name: memberName, + ProtocolPort: memberPort, + Weight: memberWeight, + Address: memberAddress, + SubnetID: subnetID, + } + + t.Logf("Member create opts: %#v", createOpts) + + member, err := pools.CreateMember(client, pool.ID, createOpts).Extract() + if err != nil { + return member, err + } + + t.Logf("Successfully created member %s", memberName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return member, fmt.Errorf("Timed out waiting for loadbalancer to become active") + } + + return member, nil +} + +// CreateMonitor will create a monitor with a random name for a specific pool. +// An error will be returned if the monitor could not be created. +func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) { + monitorName := tools.RandomString("TESTACCT-", 8) + + t.Logf("Attempting to create monitor %s", monitorName) + + createOpts := monitors.CreateOpts{ + PoolID: pool.ID, + Name: monitorName, + Delay: 10, + Timeout: 5, + MaxRetries: 5, + Type: "PING", + } + + monitor, err := monitors.Create(client, createOpts).Extract() + if err != nil { + return monitor, err + } + + t.Logf("Successfully created monitor: %s", monitorName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active") + } + + return monitor, nil +} + +// CreatePool will create a pool with a random name with a specified listener +// and loadbalancer. An error will be returned if the pool could not be +// created. +func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) { + poolName := tools.RandomString("TESTACCT-", 8) + + t.Logf("Attempting to create pool %s", poolName) + + createOpts := pools.CreateOpts{ + Name: poolName, + Protocol: pools.ProtocolTCP, + LoadbalancerID: lb.ID, + LBMethod: pools.LBMethodLeastConnections, + } + + pool, err := pools.Create(client, createOpts).Extract() + if err != nil { + return pool, err + } + + t.Logf("Successfully created pool %s", poolName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active") + } + + return pool, nil +} + +// DeleteListener will delete a specified listener. A fatal error will occur if +// the listener could not be deleted. This works best when used as a deferred +// function. +func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) { + t.Logf("Attempting to delete listener %s", listenerID) + + if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete listener: %v", err) + } + + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + t.Logf("Successfully deleted listener %s", listenerID) +} + +// DeleteMember will delete a specified member. A fatal error will occur if the +// member could not be deleted. This works best when used as a deferred +// function. +func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) { + t.Logf("Attempting to delete member %s", memberID) + + if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete member: %s", memberID) + } + + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + t.Logf("Successfully deleted member %s", memberID) +} + +// DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will +// occur if the loadbalancer could not be deleted. This works best when used +// as a deferred function. +func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) { + t.Logf("Attempting to delete loadbalancer %s", lbID) + + deleteOpts := loadbalancers.DeleteOpts{ + Cascade: false, + } + + if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil { + t.Fatalf("Unable to delete loadbalancer: %v", err) + } + + t.Logf("Waiting for loadbalancer %s to delete", lbID) + + if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Loadbalancer did not delete in time.") + } + + t.Logf("Successfully deleted loadbalancer %s", lbID) +} + +// CascadeDeleteLoadBalancer will perform a cascading delete on a loadbalancer. +// A fatal error will occur if the loadbalancer could not be deleted. This works +// best when used as a deferred function. +func CascadeDeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) { + t.Logf("Attempting to cascade delete loadbalancer %s", lbID) + + deleteOpts := loadbalancers.DeleteOpts{ + Cascade: true, + } + + if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil { + t.Fatalf("Unable to cascade delete loadbalancer: %v", err) + } + + t.Logf("Waiting for loadbalancer %s to cascade delete", lbID) + + if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Loadbalancer did not delete in time.") + } + + t.Logf("Successfully deleted loadbalancer %s", lbID) +} + +// DeleteMonitor will delete a specified monitor. A fatal error will occur if +// the monitor could not be deleted. This works best when used as a deferred +// function. +func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) { + t.Logf("Attempting to delete monitor %s", monitorID) + + if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete monitor: %v", err) + } + + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + t.Logf("Successfully deleted monitor %s", monitorID) +} + +// DeletePool will delete a specified pool. A fatal error will occur if the +// pool could not be deleted. This works best when used as a deferred function. +func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) { + t.Logf("Attempting to delete pool %s", poolID) + + if err := pools.Delete(client, poolID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete pool: %v", err) + } + + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + t.Logf("Successfully deleted pool %s", poolID) +} + +// WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. +func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := loadbalancers.Get(client, lbID).Extract() + if err != nil { + if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { + if httpStatus.Actual == 404 { + if status == "DELETED" { + return true, nil + } + } + } + return false, err + } + + if current.ProvisioningStatus == status { + return true, nil + } + + return false, nil + }) +} diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go new file mode 100644 index 0000000000..b18b975c35 --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -0,0 +1,326 @@ +// +build acceptance networking loadbalancer loadbalancers + +package 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/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" +) + +func TestLoadbalancersList(t *testing.T) { + client, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := loadbalancers.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list loadbalancers: %v", err) + } + + allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) + if err != nil { + t.Fatalf("Unable to extract loadbalancers: %v", err) + } + + for _, lb := range allLoadbalancers { + tools.PrintResource(t, lb) + } +} + +func TestLoadbalancersCRUD(t *testing.T) { + netClient, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a networking client: %v", err) + } + + lbClient, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + network, err := networking.CreateNetwork(t, netClient) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer networking.DeleteNetwork(t, netClient, network.ID) + + subnet, err := networking.CreateSubnet(t, netClient, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer networking.DeleteSubnet(t, netClient, subnet.ID) + + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) + if err != nil { + t.Fatalf("Unable to create loadbalancer: %v", err) + } + defer DeleteLoadBalancer(t, lbClient, lb.ID) + + newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() + if err != nil { + t.Fatalf("Unable to get loadbalancer: %v", err) + } + + tools.PrintResource(t, newLB) + + // Because of the time it takes to create a loadbalancer, + // this test will include some other resources. + + // Listener + listener, err := CreateListener(t, lbClient, lb) + if err != nil { + t.Fatalf("Unable to create listener: %v", err) + } + defer DeleteListener(t, lbClient, lb.ID, listener.ID) + + updateListenerOpts := listeners.UpdateOpts{ + Description: "Some listener description", + } + _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + if err != nil { + t.Fatalf("Unable to update listener") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newListener, err := listeners.Get(lbClient, listener.ID).Extract() + if err != nil { + t.Fatalf("Unable to get listener") + } + + tools.PrintResource(t, newListener) + + // Pool + pool, err := CreatePool(t, lbClient, lb) + if err != nil { + t.Fatalf("Unable to create pool: %v", err) + } + defer DeletePool(t, lbClient, lb.ID, pool.ID) + + updatePoolOpts := pools.UpdateOpts{ + Description: "Some pool description", + } + _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() + if err != nil { + t.Fatalf("Unable to update pool") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newPool, err := pools.Get(lbClient, pool.ID).Extract() + if err != nil { + t.Fatalf("Unable to get pool") + } + + tools.PrintResource(t, newPool) + + // Member + member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) + if err != nil { + t.Fatalf("Unable to create member: %v", err) + } + defer DeleteMember(t, lbClient, lb.ID, pool.ID, member.ID) + + newWeight := tools.RandomInt(11, 100) + updateMemberOpts := pools.UpdateMemberOpts{ + Weight: newWeight, + } + _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() + if err != nil { + t.Fatalf("Unable to update pool") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() + if err != nil { + t.Fatalf("Unable to get member") + } + + tools.PrintResource(t, newMember) + + // Monitor + monitor, err := CreateMonitor(t, lbClient, lb, newPool) + if err != nil { + t.Fatalf("Unable to create monitor: %v", err) + } + defer DeleteMonitor(t, lbClient, lb.ID, monitor.ID) + + newDelay := tools.RandomInt(20, 30) + updateMonitorOpts := monitors.UpdateOpts{ + Delay: newDelay, + } + _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() + if err != nil { + t.Fatalf("Unable to update monitor") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() + if err != nil { + t.Fatalf("Unable to get monitor") + } + + tools.PrintResource(t, newMonitor) + +} + +func TestLoadbalancersCascadeCRUD(t *testing.T) { + netClient, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a networking client: %v", err) + } + + lbClient, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + network, err := networking.CreateNetwork(t, netClient) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer networking.DeleteNetwork(t, netClient, network.ID) + + subnet, err := networking.CreateSubnet(t, netClient, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer networking.DeleteSubnet(t, netClient, subnet.ID) + + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) + if err != nil { + t.Fatalf("Unable to create loadbalancer: %v", err) + } + defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) + + newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() + if err != nil { + t.Fatalf("Unable to get loadbalancer: %v", err) + } + + tools.PrintResource(t, newLB) + + // Because of the time it takes to create a loadbalancer, + // this test will include some other resources. + + // Listener + listener, err := CreateListener(t, lbClient, lb) + if err != nil { + t.Fatalf("Unable to create listener: %v", err) + } + + updateListenerOpts := listeners.UpdateOpts{ + Description: "Some listener description", + } + _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + if err != nil { + t.Fatalf("Unable to update listener") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newListener, err := listeners.Get(lbClient, listener.ID).Extract() + if err != nil { + t.Fatalf("Unable to get listener") + } + + tools.PrintResource(t, newListener) + + // Pool + pool, err := CreatePool(t, lbClient, lb) + if err != nil { + t.Fatalf("Unable to create pool: %v", err) + } + + updatePoolOpts := pools.UpdateOpts{ + Description: "Some pool description", + } + _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() + if err != nil { + t.Fatalf("Unable to update pool") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newPool, err := pools.Get(lbClient, pool.ID).Extract() + if err != nil { + t.Fatalf("Unable to get pool") + } + + tools.PrintResource(t, newPool) + + // Member + member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) + if err != nil { + t.Fatalf("Unable to create member: %v", err) + } + + newWeight := tools.RandomInt(11, 100) + updateMemberOpts := pools.UpdateMemberOpts{ + Weight: newWeight, + } + _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() + if err != nil { + t.Fatalf("Unable to update pool") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() + if err != nil { + t.Fatalf("Unable to get member") + } + + tools.PrintResource(t, newMember) + + // Monitor + monitor, err := CreateMonitor(t, lbClient, lb, newPool) + if err != nil { + t.Fatalf("Unable to create monitor: %v", err) + } + + newDelay := tools.RandomInt(20, 30) + updateMonitorOpts := monitors.UpdateOpts{ + Delay: newDelay, + } + _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() + if err != nil { + t.Fatalf("Unable to update monitor") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() + if err != nil { + t.Fatalf("Unable to get monitor") + } + + tools.PrintResource(t, newMonitor) + +} diff --git a/acceptance/openstack/loadbalancer/v2/monitors_test.go b/acceptance/openstack/loadbalancer/v2/monitors_test.go new file mode 100644 index 0000000000..93688fdb2e --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/monitors_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking loadbalancer monitors + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" +) + +func TestMonitorsList(t *testing.T) { + client, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := monitors.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list monitors: %v", err) + } + + allMonitors, err := monitors.ExtractMonitors(allPages) + if err != nil { + t.Fatalf("Unable to extract monitors: %v", err) + } + + for _, monitor := range allMonitors { + tools.PrintResource(t, monitor) + } +} diff --git a/acceptance/openstack/loadbalancer/v2/pkg.go b/acceptance/openstack/loadbalancer/v2/pkg.go new file mode 100644 index 0000000000..5ec3cc8e83 --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/pkg.go @@ -0,0 +1 @@ +package v2 diff --git a/acceptance/openstack/loadbalancer/v2/pools_test.go b/acceptance/openstack/loadbalancer/v2/pools_test.go new file mode 100644 index 0000000000..f7ec2a4ac4 --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/pools_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking loadbalancer pools + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" +) + +func TestPoolsList(t *testing.T) { + client, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := pools.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list pools: %v", err) + } + + allPools, err := pools.ExtractPools(allPages) + if err != nil { + t.Fatalf("Unable to extract pools: %v", err) + } + + for _, pool := range allPools { + tools.PrintResource(t, pool) + } +} 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 26064f0c28..650eb2cc49 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -176,152 +176,3 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newMonitor) } - -func TestOctaviaLoadbalancersCRUD(t *testing.T) { - netClient, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - lbClient, err := clients.NewLoadBalancerV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - network, err := networking.CreateNetwork(t, netClient) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } - defer networking.DeleteNetwork(t, netClient, network.ID) - - subnet, err := networking.CreateSubnet(t, netClient, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } - defer networking.DeleteSubnet(t, netClient, subnet.ID) - - lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) - if err != nil { - t.Fatalf("Unable to create loadbalancer: %v", err) - } - defer func() { - t.Logf("Running cascading delete on Octavia LB...") - err := loadbalancers.CascadingDelete(lbClient, lb.ID).ExtractErr() - if err != nil { - t.Fatalf("Error running cascading delete: %v", err) - } - }() - - newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() - if err != nil { - t.Fatalf("Unable to get loadbalancer: %v", err) - } - - tools.PrintResource(t, newLB) - - // Because of the time it takes to create a loadbalancer, - // this test will include some other resources. - - // Listener - listener, err := CreateListener(t, lbClient, lb) - if err != nil { - t.Fatalf("Unable to create listener: %v", err) - } - - updateListenerOpts := listeners.UpdateOpts{ - Description: "Some listener description", - } - _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() - if err != nil { - t.Fatalf("Unable to update listener") - } - - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newListener, err := listeners.Get(lbClient, listener.ID).Extract() - if err != nil { - t.Fatalf("Unable to get listener") - } - - tools.PrintResource(t, newListener) - - // Pool - pool, err := CreatePool(t, lbClient, lb) - if err != nil { - t.Fatalf("Unable to create pool: %v", err) - } - - updatePoolOpts := pools.UpdateOpts{ - Description: "Some pool description", - } - _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() - if err != nil { - t.Fatalf("Unable to update pool") - } - - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newPool, err := pools.Get(lbClient, pool.ID).Extract() - if err != nil { - t.Fatalf("Unable to get pool") - } - - tools.PrintResource(t, newPool) - - // Member - member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) - if err != nil { - t.Fatalf("Unable to create member: %v", err) - } - - newWeight := tools.RandomInt(11, 100) - updateMemberOpts := pools.UpdateMemberOpts{ - Weight: newWeight, - } - _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() - if err != nil { - t.Fatalf("Unable to update pool") - } - - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() - if err != nil { - t.Fatalf("Unable to get member") - } - - tools.PrintResource(t, newMember) - - // Monitor - monitor, err := CreateMonitor(t, lbClient, lb, newPool) - if err != nil { - t.Fatalf("Unable to create monitor: %v", err) - } - - newDelay := tools.RandomInt(20, 30) - updateMonitorOpts := monitors.UpdateOpts{ - Delay: newDelay, - } - _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() - if err != nil { - t.Fatalf("Unable to update monitor") - } - - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() - if err != nil { - t.Fatalf("Unable to get monitor") - } - - tools.PrintResource(t, newMonitor) - -} diff --git a/openstack/loadbalancer/v2/doc.go b/openstack/loadbalancer/v2/doc.go new file mode 100644 index 0000000000..ec7f9d6f04 --- /dev/null +++ b/openstack/loadbalancer/v2/doc.go @@ -0,0 +1,3 @@ +// Package lbaas_v2 provides information and interaction with the Load Balancer +// as a Service v2 extension for the OpenStack Networking service. +package lbaas_v2 diff --git a/openstack/loadbalancer/v2/listeners/doc.go b/openstack/loadbalancer/v2/listeners/doc.go new file mode 100644 index 0000000000..108cdb03d8 --- /dev/null +++ b/openstack/loadbalancer/v2/listeners/doc.go @@ -0,0 +1,63 @@ +/* +Package listeners provides information and interaction with Listeners of the +LBaaS v2 extension for the OpenStack Networking service. + +Example to List Listeners + + listOpts := listeners.ListOpts{ + LoadbalancerID : "ca430f80-1737-4712-8dc6-3f640d55594b", + } + + allPages, err := listeners.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allListeners, err := listeners.ExtractListeners(allPages) + if err != nil { + panic(err) + } + + for _, listener := range allListeners { + fmt.Printf("%+v\n", listener) + } + +Example to Create a Listener + + createOpts := listeners.CreateOpts{ + Protocol: "TCP", + Name: "db", + LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", + AdminStateUp: gophercloud.Enabled, + DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", + ProtocolPort: 3306, + } + + listener, err := listeners.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Listener + + listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" + + i1001 := 1001 + updateOpts := listeners.UpdateOpts{ + ConnLimit: &i1001, + } + + listener, err := listeners.Update(networkClient, listenerID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Listener + + listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := listeners.Delete(networkClient, listenerID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package listeners diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go new file mode 100644 index 0000000000..dd190f606f --- /dev/null +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -0,0 +1,199 @@ +package listeners + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Type Protocol represents a listener protocol. +type Protocol string + +// Supported attributes for create/update operations. +const ( + ProtocolTCP Protocol = "TCP" + ProtocolHTTP Protocol = "HTTP" + ProtocolHTTPS Protocol = "HTTPS" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToListenerListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the floating IP attributes you want to see returned. SortKey allows you to +// sort by a particular listener 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"` + AdminStateUp *bool `q:"admin_state_up"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + LoadbalancerID string `q:"loadbalancer_id"` + DefaultPoolID string `q:"default_pool_id"` + Protocol string `q:"protocol"` + ProtocolPort int `q:"protocol_port"` + ConnectionLimit int `q:"connection_limit"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToListenerListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToListenerListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// listeners. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those listeners that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToListenerListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return ListenerPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToListenerCreateMap() (map[string]interface{}, error) +} + +// CreateOpts represents options for creating a listener. +type CreateOpts struct { + // The load balancer on which to provision this listener. + LoadbalancerID string `json:"loadbalancer_id" required:"true"` + + // The protocol - can either be TCP, HTTP or HTTPS. + Protocol Protocol `json:"protocol" required:"true"` + + // The port on which to listen for client traffic. + ProtocolPort int `json:"protocol_port" required:"true"` + + // TenantID is only required if the caller has an admin role and wants + // to create a pool for another project. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is only required if the caller has an admin role and wants + // to create a pool for another project. + ProjectID string `json:"project_id,omitempty"` + + // Human-readable name for the Listener. Does not have to be unique. + Name string `json:"name,omitempty"` + + // The ID of the default pool with which the Listener is associated. + DefaultPoolID string `json:"default_pool_id,omitempty"` + + // Human-readable description for the Listener. + Description string `json:"description,omitempty"` + + // The maximum number of connections allowed for the Listener. + ConnLimit *int `json:"connection_limit,omitempty"` + + // A reference to a Barbican container of TLS secrets. + DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` + + // A list of references to TLS secrets. + SniContainerRefs []string `json:"sni_container_refs,omitempty"` + + // The administrative state of the Listener. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToListenerCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "listener") +} + +// Create is an operation which provisions a new Listeners based on the +// configuration defined in the CreateOpts struct. Once the request is +// validated and progress has started on the provisioning process, a +// CreateResult will be returned. +// +// Users with an admin role can create Listeners on behalf of other tenants by +// specifying a TenantID attribute different than their own. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToListenerCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} + +// Get retrieves a particular Listeners based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToListenerUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options for updating a Listener. +type UpdateOpts struct { + // Human-readable name for the Listener. Does not have to be unique. + Name string `json:"name,omitempty"` + + // Human-readable description for the Listener. + Description string `json:"description,omitempty"` + + // The maximum number of connections allowed for the Listener. + ConnLimit *int `json:"connection_limit,omitempty"` + + // A reference to a Barbican container of TLS secrets. + DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` + + // A list of references to TLS secrets. + SniContainerRefs []string `json:"sni_container_refs,omitempty"` + + // The administrative state of the Listener. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToListenerUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "listener") +} + +// Update is an operation which modifies the attributes of the specified +// Listener. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + b, err := opts.ToListenerUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + return +} + +// Delete will permanently delete a particular Listeners based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go new file mode 100644 index 0000000000..728d04266d --- /dev/null +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -0,0 +1,131 @@ +package listeners + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" + "github.com/gophercloud/gophercloud/pagination" +) + +type LoadBalancerID struct { + ID string `json:"id"` +} + +// Listener is the primary load balancing configuration object that specifies +// the loadbalancer and port on which client traffic is received, as well +// as other details such as the load balancing method to be use, protocol, etc. +type Listener struct { + // The unique ID for the Listener. + ID string `json:"id"` + + // Owner of the Listener. + TenantID string `json:"tenant_id"` + + // Human-readable name for the Listener. Does not have to be unique. + Name string `json:"name"` + + // Human-readable description for the Listener. + Description string `json:"description"` + + // The protocol to loadbalance. A valid value is TCP, HTTP, or HTTPS. + Protocol string `json:"protocol"` + + // The port on which to listen to client traffic that is associated with the + // Loadbalancer. A valid value is from 0 to 65535. + ProtocolPort int `json:"protocol_port"` + + // The UUID of default pool. Must have compatible protocol with listener. + DefaultPoolID string `json:"default_pool_id"` + + // A list of load balancer IDs. + Loadbalancers []LoadBalancerID `json:"loadbalancers"` + + // The maximum number of connections allowed for the Loadbalancer. + // Default is -1, meaning no limit. + ConnLimit int `json:"connection_limit"` + + // The list of references to TLS secrets. + SniContainerRefs []string `json:"sni_container_refs"` + + // A reference to a Barbican container of TLS secrets. + DefaultTlsContainerRef string `json:"default_tls_container_ref"` + + // The administrative state of the Listener. A valid value is true (UP) or false (DOWN). + AdminStateUp bool `json:"admin_state_up"` + + // Pools are the pools which are part of this listener. + Pools []pools.Pool `json:"pools"` +} + +// ListenerPage is the page returned by a pager when traversing over a +// collection of listeners. +type ListenerPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of listeners 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 ListenerPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"listeners_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a ListenerPage struct is empty. +func (r ListenerPage) IsEmpty() (bool, error) { + is, err := ExtractListeners(r) + return len(is) == 0, err +} + +// ExtractListeners accepts a Page struct, specifically a ListenerPage struct, +// and extracts the elements into a slice of Listener structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractListeners(r pagination.Page) ([]Listener, error) { + var s struct { + Listeners []Listener `json:"listeners"` + } + err := (r.(ListenerPage)).ExtractInto(&s) + return s.Listeners, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a listener. +func (r commonResult) Extract() (*Listener, error) { + var s struct { + Listener *Listener `json:"listener"` + } + err := r.ExtractInto(&s) + return s.Listener, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Listener. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Listener. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Listener. +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/listeners/testing/doc.go b/openstack/loadbalancer/v2/listeners/testing/doc.go new file mode 100644 index 0000000000..f41387e827 --- /dev/null +++ b/openstack/loadbalancer/v2/listeners/testing/doc.go @@ -0,0 +1,2 @@ +// listeners unit tests +package testing diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go new file mode 100644 index 0000000000..a3df254b43 --- /dev/null +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -0,0 +1,213 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListenersListBody contains the canned body of a listeners list response. +const ListenersListBody = ` +{ + "listeners":[ + { + "id": "db902c0c-d5ff-4753-b465-668ad9656918", + "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", + "name": "web", + "description": "listener config for the web tier", + "loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}], + "protocol": "HTTP", + "protocol_port": 80, + "default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d", + "admin_state_up": true, + "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", + "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] + }, + { + "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", + "name": "db", + "description": "listener config for the db tier", + "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], + "protocol": "TCP", + "protocol_port": 3306, + "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", + "connection_limit": 2000, + "admin_state_up": true, + "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", + "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] + } + ] +} +` + +// SingleServerBody is the canned body of a Get request on an existing listener. +const SingleListenerBody = ` +{ + "listener": { + "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", + "name": "db", + "description": "listener config for the db tier", + "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], + "protocol": "TCP", + "protocol_port": 3306, + "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", + "connection_limit": 2000, + "admin_state_up": true, + "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", + "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] + } +} +` + +// PostUpdateListenerBody is the canned response body of a Update request on an existing listener. +const PostUpdateListenerBody = ` +{ + "listener": { + "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", + "name": "NewListenerName", + "description": "listener config for the db tier", + "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], + "protocol": "TCP", + "protocol_port": 3306, + "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", + "connection_limit": 1000, + "admin_state_up": true, + "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", + "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] + } +} +` + +var ( + ListenerWeb = listeners.Listener{ + ID: "db902c0c-d5ff-4753-b465-668ad9656918", + TenantID: "310df60f-2a10-4ee5-9554-98393092194c", + Name: "web", + Description: "listener config for the web tier", + Loadbalancers: []listeners.LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}}, + Protocol: "HTTP", + ProtocolPort: 80, + DefaultPoolID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + AdminStateUp: true, + DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", + SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, + } + ListenerDb = listeners.Listener{ + ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + TenantID: "310df60f-2a10-4ee5-9554-98393092194c", + Name: "db", + Description: "listener config for the db tier", + Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, + Protocol: "TCP", + ProtocolPort: 3306, + DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", + ConnLimit: 2000, + AdminStateUp: true, + DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", + SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, + } + ListenerUpdated = listeners.Listener{ + ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + TenantID: "310df60f-2a10-4ee5-9554-98393092194c", + Name: "NewListenerName", + Description: "listener config for the db tier", + Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, + Protocol: "TCP", + ProtocolPort: 3306, + DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", + ConnLimit: 1000, + AdminStateUp: true, + DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", + SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, + } +) + +// HandleListenerListSuccessfully sets up the test server to respond to a listener List request. +func HandleListenerListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/listeners", 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, ListenersListBody) + case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": + fmt.Fprintf(w, `{ "listeners": [] }`) + default: + t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleListenerCreationSuccessfully sets up the test server to respond to a listener creation request +// with a given response. +func HandleListenerCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/listeners", 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, `{ + "listener": { + "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab", + "protocol": "TCP", + "name": "db", + "admin_state_up": true, + "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", + "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", + "protocol_port": 3306 + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// HandleListenerGetSuccessfully sets up the test server to respond to a listener Get request. +func HandleListenerGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", 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, SingleListenerBody) + }) +} + +// HandleListenerDeletionSuccessfully sets up the test server to respond to a listener deletion request. +func HandleListenerDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", 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) + }) +} + +// HandleListenerUpdateSuccessfully sets up the test server to respond to a listener Update request. +func HandleListenerUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", 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, `{ + "listener": { + "name": "NewListenerName", + "connection_limit": 1001 + } + }`) + + fmt.Fprintf(w, PostUpdateListenerBody) + }) +} diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go new file mode 100644 index 0000000000..4f0a0db0f3 --- /dev/null +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -0,0 +1,137 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" + fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListListeners(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListenerListSuccessfully(t) + + pages := 0 + err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := listeners.ExtractListeners(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 listeners, got %d", len(actual)) + } + th.CheckDeepEquals(t, ListenerWeb, actual[0]) + th.CheckDeepEquals(t, ListenerDb, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllListeners(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListenerListSuccessfully(t) + + allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := listeners.ExtractListeners(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ListenerWeb, actual[0]) + th.CheckDeepEquals(t, ListenerDb, actual[1]) +} + +func TestCreateListener(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListenerCreationSuccessfully(t, SingleListenerBody) + + actual, err := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{ + Protocol: "TCP", + Name: "db", + LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", + AdminStateUp: gophercloud.Enabled, + DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", + DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", + ProtocolPort: 3306, + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ListenerDb, *actual) +} + +func TestRequiredCreateOpts(t *testing.T) { + res := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar", ProtocolPort: 80}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestGetListener(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListenerGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := listeners.Get(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, ListenerDb, *actual) +} + +func TestDeleteListener(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListenerDeletionSuccessfully(t) + + res := listeners.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateListener(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListenerUpdateSuccessfully(t) + + client := fake.ServiceClient() + i1001 := 1001 + actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ + Name: "NewListenerName", + ConnLimit: &i1001, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, ListenerUpdated, *actual) +} diff --git a/openstack/loadbalancer/v2/listeners/urls.go b/openstack/loadbalancer/v2/listeners/urls.go new file mode 100644 index 0000000000..02fb1eb39e --- /dev/null +++ b/openstack/loadbalancer/v2/listeners/urls.go @@ -0,0 +1,16 @@ +package listeners + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "listeners" +) + +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/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go new file mode 100644 index 0000000000..b0a20b8fab --- /dev/null +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -0,0 +1,76 @@ +/* +Package loadbalancers provides information and interaction with Load Balancers +of the LBaaS v2 extension for the OpenStack Networking service. + +Example to List Load Balancers + + listOpts := loadbalancers.ListOpts{ + Provider: "haproxy", + } + + allPages, err := loadbalancers.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) + if err != nil { + panic(err) + } + + for _, lb := range allLoadbalancers { + fmt.Printf("%+v\n", lb) + } + +Example to Create a Load Balancer + + createOpts := loadbalancers.CreateOpts{ + Name: "db_lb", + AdminStateUp: gophercloud.Enabled, + VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + VipAddress: "10.30.176.48", + Flavor: "medium", + Provider: "haproxy", + } + + lb, err := loadbalancers.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Load Balancer + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + + i1001 := 1001 + updateOpts := loadbalancers.UpdateOpts{ + Name: "new-name", + } + + lb, err := loadbalancers.Update(networkClient, lbID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Load Balancers + + deleteOpts := loadbalancers.DeleteOpts{ + Cascade: true, + } + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + + err := loadbalancers.Delete(networkClient, lbID, deleteOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Get the Status of a Load Balancer + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + status, err := loadbalancers.GetStatuses(networkClient, LBID).Extract() + if err != nil { + panic(err) + } +*/ +package loadbalancers diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go new file mode 100644 index 0000000000..9d82f9efa0 --- /dev/null +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -0,0 +1,210 @@ +package loadbalancers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToLoadBalancerListQuery() (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 Loadbalancer attributes you want to see returned. SortKey allows you to +// sort by a particular attribute. SortDir sets the direction, and is +// either `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + Description string `q:"description"` + AdminStateUp *bool `q:"admin_state_up"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + ProvisioningStatus string `q:"provisioning_status"` + VipAddress string `q:"vip_address"` + VipPortID string `q:"vip_port_id"` + VipSubnetID string `q:"vip_subnet_id"` + ID string `q:"id"` + OperatingStatus string `q:"operating_status"` + Name string `q:"name"` + Flavor string `q:"flavor"` + Provider string `q:"provider"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToLoadBalancerListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToLoadBalancerListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// load balancers. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. +// +// Default policy settings return only those load balancers that are owned by +// the tenant who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToLoadBalancerListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return LoadBalancerPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToLoadBalancerCreateMap() (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,omitempty"` + + // Human-readable description for the Loadbalancer. + Description string `json:"description,omitempty"` + + // The network on which to allocate the Loadbalancer's address. A tenant can + // only create Loadbalancers on networks authorized by policy (e.g. networks + // that belong to them or networks that are shared). + VipSubnetID string `json:"vip_subnet_id" required:"true"` + + // TenantID is the UUID of the project who owns the Loadbalancer. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the UUID of the project who owns the Loadbalancer. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + + // The IP address of the Loadbalancer. + VipAddress string `json:"vip_address,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // The UUID of a flavor. + Flavor string `json:"flavor,omitempty"` + + // The name of the provider. + Provider string `json:"provider,omitempty"` +} + +// ToLoadBalancerCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "loadbalancer") +} + +// Create is an operation which provisions a new loadbalancer based on the +// configuration defined in the CreateOpts struct. Once the request is +// validated and progress has started on the provisioning process, a +// CreateResult will be returned. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToLoadBalancerCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} + +// Get retrieves a particular Loadbalancer based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToLoadBalancerUpdateMap() (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"` + + // Human-readable description for the Loadbalancer. + Description string `json:"description,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToLoadBalancerUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "loadbalancer") +} + +// Update is an operation which modifies the attributes of the specified +// LoadBalancer. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + b, err := opts.ToLoadBalancerUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + return +} + +// DeleteOptsBuilder allows extensions to add additional parameters to the +// Delete request. +type DeleteOptsBuilder interface { + ToLoadBalancerDeleteQuery() (string, error) +} + +// DeleteOpts is the common options struct used in this package's Delete +// operation. +type DeleteOpts struct { + // Cascade will delete all children of the load balancer (listners, monitors, etc). + Cascade bool `q:"cascade"` +} + +// ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. +func (opts DeleteOpts) ToLoadBalancerDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Delete will permanently delete a particular LoadBalancer based on its +// unique ID. +func Delete(c *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { + url := resourceURL(c, id) + if opts != nil { + query, err := opts.ToLoadBalancerDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + _, r.Err = c.Delete(url, nil) + return +} + +// GetStatuses will return the status of a particular LoadBalancer. +func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { + _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go new file mode 100644 index 0000000000..80a9ff0557 --- /dev/null +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -0,0 +1,149 @@ +package loadbalancers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/pagination" +) + +// LoadBalancer is the primary load balancing configuration object that +// specifies the virtual IP address on which client traffic is received, as well +// as other details such as the load balancing method to be use, protocol, etc. +type LoadBalancer struct { + // Human-readable description for the Loadbalancer. + Description string `json:"description"` + + // The administrative state of the Loadbalancer. + // A valid value is true (UP) or false (DOWN). + AdminStateUp bool `json:"admin_state_up"` + + // Owner of the LoadBalancer. + TenantID string `json:"tenant_id"` + + // The provisioning status of the LoadBalancer. + // This value is ACTIVE, PENDING_CREATE or ERROR. + ProvisioningStatus string `json:"provisioning_status"` + + // The IP address of the Loadbalancer. + VipAddress string `json:"vip_address"` + + // The UUID of the port associated with the IP address. + VipPortID string `json:"vip_port_id"` + + // The UUID of the subnet on which to allocate the virtual IP for the + // Loadbalancer address. + VipSubnetID string `json:"vip_subnet_id"` + + // The unique ID for the LoadBalancer. + ID string `json:"id"` + + // The operating status of the LoadBalancer. This value is ONLINE or OFFLINE. + OperatingStatus string `json:"operating_status"` + + // Human-readable name for the LoadBalancer. Does not have to be unique. + Name string `json:"name"` + + // The UUID of a flavor if set. + Flavor string `json:"flavor"` + + // The name of the provider. + Provider string `json:"provider"` + + // Listeners are the listeners related to this Loadbalancer. + Listeners []listeners.Listener `json:"listeners"` +} + +// StatusTree represents the status of a loadbalancer. +type StatusTree struct { + Loadbalancer *LoadBalancer `json:"loadbalancer"` +} + +// LoadBalancerPage is the page returned by a pager when traversing over a +// collection of load balancers. +type LoadBalancerPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of load balancers 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 LoadBalancerPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"loadbalancers_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a LoadBalancerPage struct is empty. +func (r LoadBalancerPage) IsEmpty() (bool, error) { + is, err := ExtractLoadBalancers(r) + return len(is) == 0, err +} + +// ExtractLoadBalancers accepts a Page struct, specifically a LoadbalancerPage +// struct, and extracts the elements into a slice of LoadBalancer structs. In +// other words, a generic collection is mapped into a relevant slice. +func ExtractLoadBalancers(r pagination.Page) ([]LoadBalancer, error) { + var s struct { + LoadBalancers []LoadBalancer `json:"loadbalancers"` + } + err := (r.(LoadBalancerPage)).ExtractInto(&s) + return s.LoadBalancers, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a loadbalancer. +func (r commonResult) Extract() (*LoadBalancer, error) { + var s struct { + LoadBalancer *LoadBalancer `json:"loadbalancer"` + } + err := r.ExtractInto(&s) + return s.LoadBalancer, err +} + +// GetStatusesResult represents the result of a GetStatuses operation. +// Call its Extract method to interpret it as a StatusTree. +type GetStatusesResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts the status of +// a Loadbalancer. +func (r GetStatusesResult) Extract() (*StatusTree, error) { + var s struct { + Statuses *StatusTree `json:"statuses"` + } + err := r.ExtractInto(&s) + return s.Statuses, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a LoadBalancer. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a LoadBalancer. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a LoadBalancer. +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/loadbalancers/testing/doc.go b/openstack/loadbalancer/v2/loadbalancers/testing/doc.go new file mode 100644 index 0000000000..b54468c82f --- /dev/null +++ b/openstack/loadbalancer/v2/loadbalancers/testing/doc.go @@ -0,0 +1,2 @@ +// loadbalancers unit tests +package testing diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go new file mode 100644 index 0000000000..759d2d9ed6 --- /dev/null +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -0,0 +1,284 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" +) + +// LoadbalancersListBody contains the canned body of a loadbalancer list response. +const LoadbalancersListBody = ` +{ + "loadbalancers":[ + { + "id": "c331058c-6a40-4144-948e-b9fb1df9db4b", + "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", + "name": "web_lb", + "description": "lb config for the web tier", + "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154", + "vip_address": "10.30.176.47", + "vip_port_id": "2a22e552-a347-44fd-b530-1f2b1b2a6735", + "flavor": "small", + "provider": "haproxy", + "admin_state_up": true, + "provisioning_status": "ACTIVE", + "operating_status": "ONLINE" + }, + { + "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", + "name": "db_lb", + "description": "lb config for the db tier", + "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + "vip_address": "10.30.176.48", + "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", + "flavor": "medium", + "provider": "haproxy", + "admin_state_up": true, + "provisioning_status": "PENDING_CREATE", + "operating_status": "OFFLINE" + } + ] +} +` + +// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer. +const SingleLoadbalancerBody = ` +{ + "loadbalancer": { + "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", + "name": "db_lb", + "description": "lb config for the db tier", + "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + "vip_address": "10.30.176.48", + "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", + "flavor": "medium", + "provider": "haproxy", + "admin_state_up": true, + "provisioning_status": "PENDING_CREATE", + "operating_status": "OFFLINE" + } +} +` + +// PostUpdateLoadbalancerBody is the canned response body of a Update request on an existing loadbalancer. +const PostUpdateLoadbalancerBody = ` +{ + "loadbalancer": { + "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", + "name": "NewLoadbalancerName", + "description": "lb config for the db tier", + "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + "vip_address": "10.30.176.48", + "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", + "flavor": "medium", + "provider": "haproxy", + "admin_state_up": true, + "provisioning_status": "PENDING_CREATE", + "operating_status": "OFFLINE" + } +} +` + +// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer. +const LoadbalancerStatuesesTree = ` +{ + "statuses" : { + "loadbalancer": { + "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + "name": "db_lb", + "provisioning_status": "PENDING_UPDATE", + "operating_status": "ACTIVE", + "listeners": [{ + "id": "db902c0c-d5ff-4753-b465-668ad9656918", + "name": "db", + "pools": [{ + "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", + "name": "db", + "healthmonitor": { + "id": "67306cda-815d-4354-9fe4-59e09da9c3c5", + "type":"PING" + }, + "members":[{ + "id": "2a280670-c202-4b0b-a562-34077415aabf", + "name": "db", + "address": "10.0.2.11", + "protocol_port": 80 + }] + }] + }] + } + } +} +` + +var ( + LoadbalancerWeb = loadbalancers.LoadBalancer{ + ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", + TenantID: "54030507-44f7-473c-9342-b4d14a95f692", + Name: "web_lb", + Description: "lb config for the web tier", + VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154", + VipAddress: "10.30.176.47", + VipPortID: "2a22e552-a347-44fd-b530-1f2b1b2a6735", + Flavor: "small", + Provider: "haproxy", + AdminStateUp: true, + ProvisioningStatus: "ACTIVE", + OperatingStatus: "ONLINE", + } + LoadbalancerDb = loadbalancers.LoadBalancer{ + ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + TenantID: "54030507-44f7-473c-9342-b4d14a95f692", + Name: "db_lb", + Description: "lb config for the db tier", + VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + VipAddress: "10.30.176.48", + VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", + Flavor: "medium", + Provider: "haproxy", + AdminStateUp: true, + ProvisioningStatus: "PENDING_CREATE", + OperatingStatus: "OFFLINE", + } + LoadbalancerUpdated = loadbalancers.LoadBalancer{ + ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + TenantID: "54030507-44f7-473c-9342-b4d14a95f692", + Name: "NewLoadbalancerName", + Description: "lb config for the db tier", + VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + VipAddress: "10.30.176.48", + VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", + Flavor: "medium", + Provider: "haproxy", + AdminStateUp: true, + ProvisioningStatus: "PENDING_CREATE", + OperatingStatus: "OFFLINE", + } + LoadbalancerStatusesTree = loadbalancers.LoadBalancer{ + ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + Name: "db_lb", + ProvisioningStatus: "PENDING_UPDATE", + OperatingStatus: "ACTIVE", + Listeners: []listeners.Listener{{ + ID: "db902c0c-d5ff-4753-b465-668ad9656918", + Name: "db", + Pools: []pools.Pool{{ + ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + Name: "db", + Monitor: monitors.Monitor{ + ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", + Type: "PING", + }, + Members: []pools.Member{{ + ID: "2a280670-c202-4b0b-a562-34077415aabf", + Name: "db", + Address: "10.0.2.11", + ProtocolPort: 80, + }}, + }}, + }}, + } +) + +// HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request. +func HandleLoadbalancerListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", 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, LoadbalancersListBody) + case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": + fmt.Fprintf(w, `{ "loadbalancers": [] }`) + default: + t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request +// with a given response. +func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", 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, `{ + "loadbalancer": { + "name": "db_lb", + "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + "vip_address": "10.30.176.48", + "flavor": "medium", + "provider": "haproxy", + "admin_state_up": true + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// HandleLoadbalancerGetSuccessfully sets up the test server to respond to a loadbalancer Get request. +func HandleLoadbalancerGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", 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, SingleLoadbalancerBody) + }) +} + +// HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request. +func HandleLoadbalancerGetStatusesTree(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/statuses", 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, LoadbalancerStatuesesTree) + }) +} + +// HandleLoadbalancerDeletionSuccessfully sets up the test server to respond to a loadbalancer deletion request. +func HandleLoadbalancerDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", 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) + }) +} + +// HandleLoadbalancerUpdateSuccessfully sets up the test server to respond to a loadbalancer Update request. +func HandleLoadbalancerUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", 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, `{ + "loadbalancer": { + "name": "NewLoadbalancerName" + } + }`) + + fmt.Fprintf(w, PostUpdateLoadbalancerBody) + }) +} diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go new file mode 100644 index 0000000000..51374e0460 --- /dev/null +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -0,0 +1,162 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" + fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListLoadbalancers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerListSuccessfully(t) + + pages := 0 + err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := loadbalancers.ExtractLoadBalancers(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 loadbalancers, got %d", len(actual)) + } + th.CheckDeepEquals(t, LoadbalancerWeb, actual[0]) + th.CheckDeepEquals(t, LoadbalancerDb, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllLoadbalancers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerListSuccessfully(t) + + allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := loadbalancers.ExtractLoadBalancers(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, LoadbalancerWeb, actual[0]) + th.CheckDeepEquals(t, LoadbalancerDb, actual[1]) +} + +func TestCreateLoadbalancer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody) + + actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{ + Name: "db_lb", + AdminStateUp: gophercloud.Enabled, + VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + VipAddress: "10.30.176.48", + Flavor: "medium", + Provider: "haproxy", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, LoadbalancerDb, *actual) +} + +func TestRequiredCreateOpts(t *testing.T) { + res := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestGetLoadbalancer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := loadbalancers.Get(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, LoadbalancerDb, *actual) +} + +func TestGetLoadbalancerStatusesTree(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerGetStatusesTree(t) + + client := fake.ServiceClient() + actual, err := loadbalancers.GetStatuses(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, LoadbalancerStatusesTree, *(actual.Loadbalancer)) +} + +func TestDeleteLoadbalancer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerDeletionSuccessfully(t) + + res := loadbalancers.Delete(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", nil) + th.AssertNoErr(t, res.Err) +} + +func TestUpdateLoadbalancer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerUpdateSuccessfully(t) + + client := fake.ServiceClient() + actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ + Name: "NewLoadbalancerName", + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, LoadbalancerUpdated, *actual) +} + +func TestCascadingDeleteLoadbalancer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerDeletionSuccessfully(t) + + sc := fake.ServiceClient() + deleteOpts := loadbalancers.DeleteOpts{ + Cascade: true, + } + + query, err := deleteOpts.ToLoadBalancerDeleteQuery() + th.AssertNoErr(t, err) + th.AssertEquals(t, query, "?cascade=true") + + err = loadbalancers.Delete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", deleteOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/loadbalancer/v2/loadbalancers/urls.go b/openstack/loadbalancer/v2/loadbalancers/urls.go new file mode 100644 index 0000000000..73cf5dc126 --- /dev/null +++ b/openstack/loadbalancer/v2/loadbalancers/urls.go @@ -0,0 +1,21 @@ +package loadbalancers + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "loadbalancers" + statusPath = "statuses" +) + +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) +} + +func statusRootURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id, statusPath) +} diff --git a/openstack/loadbalancer/v2/monitors/doc.go b/openstack/loadbalancer/v2/monitors/doc.go new file mode 100644 index 0000000000..6ed8c8fb5f --- /dev/null +++ b/openstack/loadbalancer/v2/monitors/doc.go @@ -0,0 +1,69 @@ +/* +Package monitors provides information and interaction with Monitors +of the LBaaS v2 extension for the OpenStack Networking service. + +Example to List Monitors + + listOpts := monitors.ListOpts{ + PoolID: "c79a4468-d788-410c-bf79-9a8ef6354852", + } + + allPages, err := monitors.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allMonitors, err := monitors.ExtractMonitors(allPages) + if err != nil { + panic(err) + } + + for _, monitor := range allMonitors { + fmt.Printf("%+v\n", monitor) + } + +Example to Create a Monitor + + createOpts := monitors.CreateOpts{ + Type: "HTTP", + Name: "db", + PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", + Delay: 20, + Timeout: 10, + MaxRetries: 5, + URLPath: "/check", + ExpectedCodes: "200-299", + } + + monitor, err := monitors.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Monitor + + monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" + + updateOpts := monitors.UpdateOpts{ + Name: "NewHealthmonitorName", + Delay: 3, + Timeout: 20, + MaxRetries: 10, + URLPath: "/another_check", + ExpectedCodes: "301", + } + + monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Monitor + + monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := monitors.Delete(networkClient, monitorID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package monitors diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go new file mode 100644 index 0000000000..c173e1c64e --- /dev/null +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -0,0 +1,257 @@ +package monitors + +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 { + ToMonitorListQuery() (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 Monitor attributes you want to see returned. SortKey allows you to +// 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"` + 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"` +} + +// ToMonitorListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToMonitorListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), nil +} + +// List returns a Pager which allows you to iterate over a collection of +// health monitors. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those health monitors that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToMonitorListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return MonitorPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Constants that represent approved monitoring types. +const ( + TypePING = "PING" + TypeTCP = "TCP" + TypeHTTP = "HTTP" + TypeHTTPS = "HTTPS" +) + +var ( + errDelayMustGETimeout = fmt.Errorf("Delay must be greater than or equal to timeout") +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// List request. +type CreateOptsBuilder interface { + ToMonitorCreateMap() (map[string]interface{}, error) +} + +// CreateOpts is the common options struct used in this package's Create +// operation. +type CreateOpts struct { + // The Pool to Monitor. + PoolID string `json:"pool_id" required:"true"` + + // The type of probe, which is PING, TCP, HTTP, or HTTPS, that is + // sent by the load balancer to verify the member state. + Type string `json:"type" required:"true"` + + // The time, in seconds, between sending probes to members. + Delay int `json:"delay" required:"true"` + + // 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"` + + // Number of permissible ping failures before changing the member's + // status to INACTIVE. Must be a number between 1 and 10. + MaxRetries int `json:"max_retries" required:"true"` + + // URI path that will be accessed if Monitor type is HTTP or HTTPS. + // Required for HTTP(S) types. + URLPath string `json:"url_path,omitempty"` + + // The HTTP method used for requests by the Monitor. If this attribute + // is not specified, it defaults to "GET". Required for HTTP(S) types. + HTTPMethod string `json:"http_method,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. + ExpectedCodes string `json:"expected_codes,omitempty"` + + // TenantID is the UUID of the project who owns the Monitor. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the UUID of the project who owns the Monitor. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + + // The Name of the Monitor. + Name string `json:"name,omitempty"` + + // The administrative state of the Monitor. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToMonitorCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "healthmonitor") + if err != nil { + return nil, err + } + + switch opts.Type { + case TypeHTTP, TypeHTTPS: + switch opts.URLPath { + case "": + return nil, fmt.Errorf("URLPath must be provided for HTTP and HTTPS") + } + switch opts.ExpectedCodes { + case "": + return nil, fmt.Errorf("ExpectedCodes must be provided for HTTP and HTTPS") + } + } + + return b, nil +} + +/* + 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: + + 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: + + 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() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} + +// Get retrieves a particular Health Monitor based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToMonitorUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts is the common options struct used in this package's Update +// operation. +type UpdateOpts struct { + // The time, in seconds, between sending probes to members. + Delay int `json:"delay,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. + Timeout int `json:"timeout,omitempty"` + + // Number of permissible ping failures before changing the member's + // status to INACTIVE. Must be a number between 1 and 10. + MaxRetries int `json:"max_retries,omitempty"` + + // URI path that will be accessed if Monitor type is HTTP or HTTPS. + // Required for HTTP(S) types. + URLPath string `json:"url_path,omitempty"` + + // The HTTP method used for requests by the Monitor. If this attribute + // is not specified, it defaults to "GET". Required for HTTP(S) types. + HTTPMethod string `json:"http_method,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. + ExpectedCodes string `json:"expected_codes,omitempty"` + + // The Name of the Monitor. + Name string `json:"name,omitempty"` + + // The administrative state of the Monitor. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToMonitorUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "healthmonitor") +} + +// Update is an operation which modifies the attributes of the specified +// Monitor. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToMonitorUpdateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + return +} + +// Delete will permanently delete a particular Monitor based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go new file mode 100644 index 0000000000..ea832cc5d0 --- /dev/null +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -0,0 +1,149 @@ +package monitors + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type PoolID struct { + ID string `json:"id"` +} + +// Monitor represents a load balancer health monitor. A health monitor is used +// to determine whether or not back-end members of the VIP's pool are usable +// for processing a request. A pool can have several health monitors associated +// with it. There are different types of health monitors supported: +// +// PING: used to ping the members using ICMP. +// TCP: used to connect to the members using TCP. +// HTTP: used to send an HTTP request to the member. +// HTTPS: used to send a secure HTTP request to the member. +// +// When a pool has several monitors associated with it, each member of the pool +// is monitored by all these monitors. If any monitor declares the member as +// unhealthy, then the member status is changed to INACTIVE and the member +// won't participate in its pool's load balancing. In other words, ALL monitors +// must declare the member to be healthy for it to stay ACTIVE. +type Monitor struct { + // The unique ID for the Monitor. + ID string `json:"id"` + + // The Name of the Monitor. + Name string `json:"name"` + + // TenantID is the owner of the Monitor. + TenantID string `json:"tenant_id"` + + // The type of probe sent by the load balancer to verify the member state, + // which is PING, TCP, HTTP, or HTTPS. + Type string `json:"type"` + + // The time, in seconds, between sending probes to members. + Delay int `json:"delay"` + + // 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. + Timeout int `json:"timeout"` + + // Number of allowed connection failures before changing the status of the + // member to INACTIVE. A valid value is from 1 to 10. + MaxRetries int `json:"max_retries"` + + // The HTTP method that the monitor uses for requests. + HTTPMethod string `json:"http_method"` + + // 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" ` + + // Expected HTTP codes for a passing HTTP(S) monitor. + ExpectedCodes string `json:"expected_codes"` + + // The administrative state of the health monitor, which is up (true) or + // down (false). + AdminStateUp bool `json:"admin_state_up"` + + // The status of the health monitor. Indicates whether the health monitor is + // operational. + Status string `json:"status"` + + // List of pools that are associated with the health monitor. + Pools []PoolID `json:"pools"` +} + +// MonitorPage is the page returned by a pager when traversing over a +// collection of health monitors. +type MonitorPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of monitors 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 MonitorPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"healthmonitors_links"` + } + + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a MonitorPage struct is empty. +func (r MonitorPage) IsEmpty() (bool, error) { + is, err := ExtractMonitors(r) + return len(is) == 0, err +} + +// ExtractMonitors accepts a Page struct, specifically a MonitorPage struct, +// and extracts the elements into a slice of Monitor structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractMonitors(r pagination.Page) ([]Monitor, error) { + var s struct { + Monitors []Monitor `json:"healthmonitors"` + } + err := (r.(MonitorPage)).ExtractInto(&s) + return s.Monitors, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a monitor. +func (r commonResult) Extract() (*Monitor, error) { + var s struct { + Monitor *Monitor `json:"healthmonitor"` + } + err := r.ExtractInto(&s) + return s.Monitor, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Monitor. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Monitor. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Monitor. +type UpdateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the result succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/loadbalancer/v2/monitors/testing/doc.go b/openstack/loadbalancer/v2/monitors/testing/doc.go new file mode 100644 index 0000000000..e2b6f12a92 --- /dev/null +++ b/openstack/loadbalancer/v2/monitors/testing/doc.go @@ -0,0 +1,2 @@ +// monitors unit tests +package testing diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures.go b/openstack/loadbalancer/v2/monitors/testing/fixtures.go new file mode 100644 index 0000000000..262ebbe8f1 --- /dev/null +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures.go @@ -0,0 +1,215 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// HealthmonitorsListBody contains the canned body of a healthmonitor list response. +const HealthmonitorsListBody = ` +{ + "healthmonitors":[ + { + "admin_state_up":true, + "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "delay":10, + "name":"web", + "max_retries":1, + "timeout":1, + "type":"PING", + "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}], + "id":"466c8345-28d8-4f84-a246-e04380b0461d" + }, + { + "admin_state_up":true, + "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "delay":5, + "name":"db", + "expected_codes":"200", + "max_retries":2, + "http_method":"GET", + "timeout":2, + "url_path":"/", + "type":"HTTP", + "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], + "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" + } + ] +} +` + +// SingleHealthmonitorBody is the canned body of a Get request on an existing healthmonitor. +const SingleHealthmonitorBody = ` +{ + "healthmonitor": { + "admin_state_up":true, + "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "delay":5, + "name":"db", + "expected_codes":"200", + "max_retries":2, + "http_method":"GET", + "timeout":2, + "url_path":"/", + "type":"HTTP", + "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], + "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" + } +} +` + +// PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor. +const PostUpdateHealthmonitorBody = ` +{ + "healthmonitor": { + "admin_state_up":true, + "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "delay":3, + "name":"NewHealthmonitorName", + "expected_codes":"301", + "max_retries":10, + "http_method":"GET", + "timeout":20, + "url_path":"/another_check", + "type":"HTTP", + "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], + "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" + } +} +` + +var ( + HealthmonitorWeb = monitors.Monitor{ + AdminStateUp: true, + Name: "web", + TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + Delay: 10, + MaxRetries: 1, + Timeout: 1, + Type: "PING", + ID: "466c8345-28d8-4f84-a246-e04380b0461d", + Pools: []monitors.PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}}, + } + HealthmonitorDb = monitors.Monitor{ + AdminStateUp: true, + Name: "db", + TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + Delay: 5, + ExpectedCodes: "200", + MaxRetries: 2, + Timeout: 2, + URLPath: "/", + Type: "HTTP", + HTTPMethod: "GET", + ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", + Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, + } + HealthmonitorUpdated = monitors.Monitor{ + AdminStateUp: true, + Name: "NewHealthmonitorName", + TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + Delay: 3, + ExpectedCodes: "301", + MaxRetries: 10, + Timeout: 20, + URLPath: "/another_check", + Type: "HTTP", + HTTPMethod: "GET", + ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", + Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, + } +) + +// HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request. +func HandleHealthmonitorListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", 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, HealthmonitorsListBody) + case "556c8345-28d8-4f84-a246-e04380b0461d": + fmt.Fprintf(w, `{ "healthmonitors": [] }`) + default: + t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleHealthmonitorCreationSuccessfully sets up the test server to respond to a healthmonitor creation request +// with a given response. +func HandleHealthmonitorCreationSuccessfully(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":"HTTP", + "pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", + "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", + "delay":20, + "name":"db", + "timeout":10, + "max_retries":5, + "url_path":"/check", + "expected_codes":"200-299" + } + }`) + + 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) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + fmt.Fprintf(w, SingleHealthmonitorBody) + }) +} + +// HandleHealthmonitorDeletionSuccessfully sets up the test server to respond to a healthmonitor deletion request. +func HandleHealthmonitorDeletionSuccessfully(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, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request. +func HandleHealthmonitorUpdateSuccessfully(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": { + "name": "NewHealthmonitorName", + "delay": 3, + "timeout": 20, + "max_retries": 10, + "url_path": "/another_check", + "expected_codes": "301" + } + }`) + + fmt.Fprintf(w, PostUpdateHealthmonitorBody) + }) +} diff --git a/openstack/loadbalancer/v2/monitors/testing/requests_test.go b/openstack/loadbalancer/v2/monitors/testing/requests_test.go new file mode 100644 index 0000000000..80cba9ca70 --- /dev/null +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -0,0 +1,154 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" + fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListHealthmonitors(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHealthmonitorListSuccessfully(t) + + pages := 0 + err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := monitors.ExtractMonitors(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 healthmonitors, got %d", len(actual)) + } + th.CheckDeepEquals(t, HealthmonitorWeb, actual[0]) + th.CheckDeepEquals(t, HealthmonitorDb, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllHealthmonitors(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHealthmonitorListSuccessfully(t) + + allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := monitors.ExtractMonitors(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, HealthmonitorWeb, actual[0]) + th.CheckDeepEquals(t, HealthmonitorDb, actual[1]) +} + +func TestCreateHealthmonitor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody) + + actual, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + Type: "HTTP", + Name: "db", + PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", + TenantID: "453105b9-1754-413f-aab1-55f1af620750", + Delay: 20, + Timeout: 10, + MaxRetries: 5, + URLPath: "/check", + ExpectedCodes: "200-299", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, HealthmonitorDb, *actual) +} + +func TestRequiredCreateOpts(t *testing.T) { + res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestGetHealthmonitor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHealthmonitorGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := monitors.Get(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, HealthmonitorDb, *actual) +} + +func TestDeleteHealthmonitor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHealthmonitorDeletionSuccessfully(t) + + res := monitors.Delete(fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateHealthmonitor(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleHealthmonitorUpdateSuccessfully(t) + + client := fake.ServiceClient() + actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ + Name: "NewHealthmonitorName", + Delay: 3, + Timeout: 20, + MaxRetries: 10, + URLPath: "/another_check", + ExpectedCodes: "301", + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, HealthmonitorUpdated, *actual) +} + +func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { + _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + Type: "HTTP", + PoolID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d", + Delay: 1, + Timeout: 10, + MaxRetries: 5, + URLPath: "/check", + ExpectedCodes: "200-299", + }).Extract() + + if err == nil { + t.Fatalf("Expected error, got none") + } + + _, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ + Delay: 1, + Timeout: 10, + }).Extract() + + if err == nil { + t.Fatalf("Expected error, got none") + } +} diff --git a/openstack/loadbalancer/v2/monitors/urls.go b/openstack/loadbalancer/v2/monitors/urls.go new file mode 100644 index 0000000000..a222e52a93 --- /dev/null +++ b/openstack/loadbalancer/v2/monitors/urls.go @@ -0,0 +1,16 @@ +package monitors + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "healthmonitors" +) + +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/pools/doc.go b/openstack/loadbalancer/v2/pools/doc.go new file mode 100644 index 0000000000..2d57ed4393 --- /dev/null +++ b/openstack/loadbalancer/v2/pools/doc.go @@ -0,0 +1,124 @@ +/* +Package pools provides information and interaction with Pools and +Members of the LBaaS v2 extension for the OpenStack Networking service. + +Example to List Pools + + listOpts := pools.ListOpts{ + LoadbalancerID: "c79a4468-d788-410c-bf79-9a8ef6354852", + } + + allPages, err := pools.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allPools, err := pools.ExtractMonitors(allPages) + if err != nil { + panic(err) + } + + for _, pools := range allPools { + fmt.Printf("%+v\n", pool) + } + +Example to Create a Pool + + createOpts := pools.CreateOpts{ + LBMethod: pools.LBMethodRoundRobin, + Protocol: "HTTP", + Name: "Example pool", + LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", + } + + pool, err := pools.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Pool + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + + updateOpts := pools.UpdateOpts{ + Name: "new-name", + } + + pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Pool + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := pools.Delete(networkClient, poolID).ExtractErr() + if err != nil { + panic(err) + } + +Example to List Pool Members + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + + listOpts := pools.ListMemberOpts{ + ProtocolPort: 80, + } + + allPages, err := pools.ListMembers(networkClient, poolID, listOpts).AllPages() + if err != nil { + panic(err) + } + + allMembers, err := pools.ExtractMembers(allPages) + if err != nil { + panic(err) + } + + for _, member := allMembers { + fmt.Printf("%+v\n", member) + } + +Example to Create a Member + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + + createOpts := pools.CreateMemberOpts{ + Name: "db", + SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", + Address: "10.0.2.11", + ProtocolPort: 80, + Weight: 10, + } + + member, err := pools.CreateMember(networkClient, poolID, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a Member + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" + + updateOpts := pools.UpdateMemberOpts{ + Name: "new-name", + Weight: 4, + } + + member, err := pools.UpdateMember(networkClient, poolID, memberID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Member + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" + + err := pools.DeleteMember(networkClient, poolID, memberID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package pools diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go new file mode 100644 index 0000000000..11564be83f --- /dev/null +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -0,0 +1,356 @@ +package pools + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToPoolListQuery() (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 Pool attributes you want to see returned. SortKey allows you to +// sort by a particular Pool attribute. SortDir sets the direction, and is +// either `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + LBMethod string `q:"lb_algorithm"` + Protocol string `q:"protocol"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + AdminStateUp *bool `q:"admin_state_up"` + Name string `q:"name"` + ID string `q:"id"` + LoadbalancerID string `q:"loadbalancer_id"` + ListenerID string `q:"listener_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToPoolListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPoolListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// pools. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those pools that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToPoolListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return PoolPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +type LBMethod string +type Protocol string + +// Supported attributes for create/update operations. +const ( + LBMethodRoundRobin LBMethod = "ROUND_ROBIN" + LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS" + LBMethodSourceIp LBMethod = "SOURCE_IP" + + ProtocolTCP Protocol = "TCP" + ProtocolHTTP Protocol = "HTTP" + ProtocolHTTPS Protocol = "HTTPS" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPoolCreateMap() (map[string]interface{}, error) +} + +// CreateOpts is the common options struct used in this package's Create +// 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. + LBMethod LBMethod `json:"lb_algorithm" required:"true"` + + // The protocol used by the pool members, you can use either + // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. + Protocol Protocol `json:"protocol" required:"true"` + + // The Loadbalancer on which the members of the pool will be associated with. + // Note: one of LoadbalancerID or ListenerID must be provided. + LoadbalancerID string `json:"loadbalancer_id,omitempty" xor:"ListenerID"` + + // The Listener on which the members of the pool will be associated with. + // Note: one of LoadbalancerID or ListenerID must be provided. + ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"` + + // TenantID is the UUID of the project who owns the Pool. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the UUID of the project who owns the Pool. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + + // Name of the pool. + Name string `json:"name,omitempty"` + + // Human-readable description for the pool. + Description string `json:"description,omitempty"` + + // Persistence is the session persistence of the pool. + // Omit this field to prevent session persistence. + Persistence *SessionPersistence `json:"session_persistence,omitempty"` + + // The administrative state of the Pool. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToPoolCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "pool") +} + +// Create accepts a CreateOpts struct and uses the values to create a new +// load balancer pool. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToPoolCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} + +// Get retrieves a particular pool based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToPoolUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts is the common options struct used in this package's Update +// operation. +type UpdateOpts struct { + // Name of the pool. + Name string `json:"name,omitempty"` + + // Human-readable description for the pool. + 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. + LBMethod LBMethod `json:"lb_algorithm,omitempty"` + + // The administrative state of the Pool. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToPoolUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "pool") +} + +// Update allows pools to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToPoolUpdateMap() + 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 pool based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} + +// ListMemberOptsBuilder allows extensions to add additional parameters to the +// ListMembers request. +type ListMembersOptsBuilder interface { + ToMembersListQuery() (string, error) +} + +// ListMembersOpts allows the filtering and sorting of paginated collections +// through the API. Filtering is achieved by passing in struct field values +// that map to the Member attributes you want to see returned. SortKey allows +// you to sort by a particular Member attribute. SortDir sets the direction, +// and is either `asc' or `desc'. Marker and Limit are used for pagination. +type ListMembersOpts struct { + Name string `q:"name"` + Weight int `q:"weight"` + AdminStateUp *bool `q:"admin_state_up"` + TenantID string `q:"tenant_id"` + Address string `q:"address"` + ProtocolPort int `q:"protocol_port"` + ID string `q:"id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToMemberListQuery formats a ListOpts into a query string. +func (opts ListMembersOpts) ToMembersListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListMembers returns a Pager which allows you to iterate over a collection of +// members. It accepts a ListMembersOptsBuilder, which allows you to filter and +// sort the returned collection for greater efficiency. +// +// Default policy settings return only those members that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func ListMembers(c *gophercloud.ServiceClient, poolID string, opts ListMembersOptsBuilder) pagination.Pager { + url := memberRootURL(c, poolID) + if opts != nil { + query, err := opts.ToMembersListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return MemberPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateMemberOptsBuilder allows extensions to add additional parameters to the +// CreateMember request. +type CreateMemberOptsBuilder interface { + ToMemberCreateMap() (map[string]interface{}, error) +} + +// CreateMemberOpts is the common options struct used in this package's CreateMember +// operation. +type CreateMemberOpts struct { + // The IP address of the member to receive traffic from the load balancer. + Address string `json:"address" required:"true"` + + // The port on which to listen for client traffic. + ProtocolPort int `json:"protocol_port" required:"true"` + + // Name of the Member. + Name string `json:"name,omitempty"` + + // TenantID is the UUID of the project who owns the Member. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the UUID of the project who owns the Member. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + + // A positive integer value that indicates the relative portion of traffic + // that this member should receive from the pool. For example, a member with + // a weight of 10 receives five times as much traffic as a member with a + // weight of 2. + Weight int `json:"weight,omitempty"` + + // If you omit this parameter, LBaaS uses the vip_subnet_id parameter value + // for the subnet UUID. + SubnetID string `json:"subnet_id,omitempty"` + + // The administrative state of the Pool. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToMemberCreateMap builds a request body from CreateMemberOpts. +func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "member") +} + +// CreateMember will create and associate a Member with a particular Pool. +func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMemberOpts) (r CreateMemberResult) { + b, err := opts.ToMemberCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + return +} + +// GetMember retrieves a particular Pool Member based on its unique ID. +func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { + _, r.Err = c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) + return +} + +// UpdateMemberOptsBuilder allows extensions to add additional parameters to the +// List request. +type UpdateMemberOptsBuilder interface { + ToMemberUpdateMap() (map[string]interface{}, error) +} + +// UpdateMemberOpts is the common options struct used in this package's Update +// operation. +type UpdateMemberOpts struct { + // Name of the Member. + Name string `json:"name,omitempty"` + + // A positive integer value that indicates the relative portion of traffic + // that this member should receive from the pool. For example, a member with + // a weight of 10 receives five times as much traffic as a member with a + // weight of 2. + Weight int `json:"weight,omitempty"` + + // The administrative state of the Pool. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToMemberUpdateMap builds a request body from UpdateMemberOpts. +func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "member") +} + +// Update allows Member to be updated. +func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) { + b, err := opts.ToMemberUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + return +} + +// DisassociateMember will remove and disassociate a Member from a particular +// Pool. +func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { + _, r.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil) + return +} diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go new file mode 100644 index 0000000000..81d3ebf7d6 --- /dev/null +++ b/openstack/loadbalancer/v2/pools/results.go @@ -0,0 +1,273 @@ +package pools + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/pagination" +) + +// SessionPersistence represents the session persistence feature of the load +// balancing service. It attempts to force connections or requests in the same +// session to be processed by the same member as long as it is ative. Three +// 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. +// 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. +// 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. +type SessionPersistence struct { + // The type of persistence mode. + Type string `json:"type"` + + // Name of cookie if persistence mode is set appropriately. + CookieName string `json:"cookie_name,omitempty"` +} + +// LoadBalancerID represents a load balancer. +type LoadBalancerID struct { + ID string `json:"id"` +} + +// ListenerID represents a listener. +type ListenerID struct { + ID string `json:"id"` +} + +// Pool represents a logical set of devices, such as web servers, that you +// group together to receive and process traffic. The load balancing function +// chooses a Member of the Pool according to the configured load balancing +// method to handle the new requests or connections received on the VIP address. +type Pool struct { + // The load-balancer algorithm, which is round-robin, least-connections, and + // so on. This value, which must be supported, is dependent on the provider. + // Round-robin must be supported. + LBMethod string `json:"lb_algorithm"` + + // The protocol of the Pool, which is TCP, HTTP, or HTTPS. + Protocol string `json:"protocol"` + + // Description for the Pool. + Description string `json:"description"` + + // A list of listeners objects IDs. + Listeners []ListenerID `json:"listeners"` //[]map[string]interface{} + + // A list of member objects IDs. + Members []Member `json:"members"` + + // The ID of associated health monitor. + MonitorID string `json:"healthmonitor_id"` + + // The network on which the members of the Pool will be located. Only members + // that are on this network can be added to the Pool. + SubnetID string `json:"subnet_id"` + + // Owner of the Pool. + TenantID string `json:"tenant_id"` + + // The administrative state of the Pool, which is up (true) or down (false). + AdminStateUp bool `json:"admin_state_up"` + + // Pool name. Does not have to be unique. + Name string `json:"name"` + + // The unique ID for the Pool. + ID string `json:"id"` + + // A list of load balancer objects IDs. + Loadbalancers []LoadBalancerID `json:"loadbalancers"` + + // Indicates whether connections in the same session will be processed by the + // same Pool member or not. + Persistence SessionPersistence `json:"session_persistence"` + + // The load balancer provider. + Provider string `json:"provider"` + + // The Monitor associated with this Pool. + Monitor monitors.Monitor `json:"healthmonitor"` +} + +// PoolPage is the page returned by a pager when traversing over a +// collection of pools. +type PoolPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of pools 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 PoolPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"pools_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a PoolPage struct is empty. +func (r PoolPage) IsEmpty() (bool, error) { + is, err := ExtractPools(r) + return len(is) == 0, err +} + +// ExtractPools accepts a Page struct, specifically a PoolPage struct, +// and extracts the elements into a slice of Pool structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractPools(r pagination.Page) ([]Pool, error) { + var s struct { + Pools []Pool `json:"pools"` + } + err := (r.(PoolPage)).ExtractInto(&s) + return s.Pools, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a pool. +func (r commonResult) Extract() (*Pool, error) { + var s struct { + Pool *Pool `json:"pool"` + } + err := r.ExtractInto(&s) + return s.Pool, err +} + +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret the result as a Pool. +type CreateResult struct { + commonResult +} + +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret the result as a Pool. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret the result as a Pool. +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 +} + +// Member represents the application running on a backend server. +type Member struct { + // Name of the Member. + Name string `json:"name"` + + // Weight of Member. + Weight int `json:"weight"` + + // The administrative state of the member, which is up (true) or down (false). + AdminStateUp bool `json:"admin_state_up"` + + // Owner of the Member. + TenantID string `json:"tenant_id"` + + // Parameter value for the subnet UUID. + SubnetID string `json:"subnet_id"` + + // The Pool to which the Member belongs. + PoolID string `json:"pool_id"` + + // The IP address of the Member. + Address string `json:"address"` + + // The port on which the application is hosted. + ProtocolPort int `json:"protocol_port"` + + // The unique ID for the Member. + ID string `json:"id"` +} + +// MemberPage is the page returned by a pager when traversing over a +// collection of Members in a Pool. +type MemberPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of members 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 MemberPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"members_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a MemberPage struct is empty. +func (r MemberPage) IsEmpty() (bool, error) { + is, err := ExtractMembers(r) + return len(is) == 0, err +} + +// ExtractMembers accepts a Page struct, specifically a MemberPage struct, +// and extracts the elements into a slice of Members structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractMembers(r pagination.Page) ([]Member, error) { + var s struct { + Members []Member `json:"members"` + } + err := (r.(MemberPage)).ExtractInto(&s) + return s.Members, err +} + +type commonMemberResult struct { + gophercloud.Result +} + +// ExtractMember is a function that accepts a result and extracts a member. +func (r commonMemberResult) Extract() (*Member, error) { + var s struct { + Member *Member `json:"member"` + } + err := r.ExtractInto(&s) + return s.Member, err +} + +// CreateMemberResult represents the result of a CreateMember operation. +// Call its Extract method to interpret it as a Member. +type CreateMemberResult struct { + commonMemberResult +} + +// GetMemberResult represents the result of a GetMember operation. +// Call its Extract method to interpret it as a Member. +type GetMemberResult struct { + commonMemberResult +} + +// UpdateMemberResult represents the result of an UpdateMember operation. +// Call its Extract method to interpret it as a Member. +type UpdateMemberResult struct { + commonMemberResult +} + +// DeleteMemberResult represents the result of a DeleteMember operation. +// Call its ExtractErr method to determine if the request succeeded or failed. +type DeleteMemberResult struct { + gophercloud.ErrResult +} diff --git a/openstack/loadbalancer/v2/pools/testing/doc.go b/openstack/loadbalancer/v2/pools/testing/doc.go new file mode 100644 index 0000000000..46e335f3f2 --- /dev/null +++ b/openstack/loadbalancer/v2/pools/testing/doc.go @@ -0,0 +1,2 @@ +// pools unit tests +package testing diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures.go b/openstack/loadbalancer/v2/pools/testing/fixtures.go new file mode 100644 index 0000000000..fe0a85123b --- /dev/null +++ b/openstack/loadbalancer/v2/pools/testing/fixtures.go @@ -0,0 +1,388 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// PoolsListBody contains the canned body of a pool list response. +const PoolsListBody = ` +{ + "pools":[ + { + "lb_algorithm":"ROUND_ROBIN", + "protocol":"HTTP", + "description":"", + "healthmonitor_id": "466c8345-28d8-4f84-a246-e04380b0461d", + "members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}], + "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], + "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], + "id":"72741b06-df4d-4715-b142-276b6bce75ab", + "name":"web", + "admin_state_up":true, + "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "provider": "haproxy" + }, + { + "lb_algorithm":"LEAST_CONNECTION", + "protocol":"HTTP", + "description":"", + "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", + "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], + "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], + "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], + "id":"c3741b06-df4d-4715-b142-276b6bce75ab", + "name":"db", + "admin_state_up":true, + "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "provider": "haproxy" + } + ] +} +` + +// SinglePoolBody is the canned body of a Get request on an existing pool. +const SinglePoolBody = ` +{ + "pool": { + "lb_algorithm":"LEAST_CONNECTION", + "protocol":"HTTP", + "description":"", + "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", + "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], + "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], + "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], + "id":"c3741b06-df4d-4715-b142-276b6bce75ab", + "name":"db", + "admin_state_up":true, + "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "provider": "haproxy" + } +} +` + +// PostUpdatePoolBody is the canned response body of a Update request on an existing pool. +const PostUpdatePoolBody = ` +{ + "pool": { + "lb_algorithm":"LEAST_CONNECTION", + "protocol":"HTTP", + "description":"", + "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", + "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], + "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], + "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], + "id":"c3741b06-df4d-4715-b142-276b6bce75ab", + "name":"db", + "admin_state_up":true, + "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "provider": "haproxy" + } +} +` + +var ( + PoolWeb = pools.Pool{ + LBMethod: "ROUND_ROBIN", + Protocol: "HTTP", + Description: "", + MonitorID: "466c8345-28d8-4f84-a246-e04380b0461d", + TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + AdminStateUp: true, + Name: "web", + Members: []pools.Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}}, + ID: "72741b06-df4d-4715-b142-276b6bce75ab", + Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, + Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, + Provider: "haproxy", + } + PoolDb = pools.Pool{ + LBMethod: "LEAST_CONNECTION", + Protocol: "HTTP", + Description: "", + MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", + TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + AdminStateUp: true, + Name: "db", + Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, + ID: "c3741b06-df4d-4715-b142-276b6bce75ab", + Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, + Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, + Provider: "haproxy", + } + PoolUpdated = pools.Pool{ + LBMethod: "LEAST_CONNECTION", + Protocol: "HTTP", + Description: "", + MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", + TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + AdminStateUp: true, + Name: "db", + Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, + ID: "c3741b06-df4d-4715-b142-276b6bce75ab", + Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, + Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, + Provider: "haproxy", + } +) + +// HandlePoolListSuccessfully sets up the test server to respond to a pool List request. +func HandlePoolListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools", 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, PoolsListBody) + case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": + fmt.Fprintf(w, `{ "pools": [] }`) + default: + t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandlePoolCreationSuccessfully sets up the test server to respond to a pool creation request +// with a given response. +func HandlePoolCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/pools", 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, `{ + "pool": { + "lb_algorithm": "ROUND_ROBIN", + "protocol": "HTTP", + "name": "Example pool", + "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// HandlePoolGetSuccessfully sets up the test server to respond to a pool Get request. +func HandlePoolGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", 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, SinglePoolBody) + }) +} + +// HandlePoolDeletionSuccessfully sets up the test server to respond to a pool deletion request. +func HandlePoolDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", 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) + }) +} + +// HandlePoolUpdateSuccessfully sets up the test server to respond to a pool Update request. +func HandlePoolUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", 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, `{ + "pool": { + "name": "NewPoolName", + "lb_algorithm": "LEAST_CONNECTIONS" + } + }`) + + fmt.Fprintf(w, PostUpdatePoolBody) + }) +} + +// MembersListBody contains the canned body of a member list response. +const MembersListBody = ` +{ + "members":[ + { + "id": "2a280670-c202-4b0b-a562-34077415aabf", + "address": "10.0.2.10", + "weight": 5, + "name": "web", + "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", + "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "admin_state_up":true, + "protocol_port": 80 + }, + { + "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", + "address": "10.0.2.11", + "weight": 10, + "name": "db", + "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", + "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "admin_state_up":false, + "protocol_port": 80 + } + ] +} +` + +// SingleMemberBody is the canned body of a Get request on an existing member. +const SingleMemberBody = ` +{ + "member": { + "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", + "address": "10.0.2.11", + "weight": 10, + "name": "db", + "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", + "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "admin_state_up":false, + "protocol_port": 80 + } +} +` + +// PostUpdateMemberBody is the canned response body of a Update request on an existing member. +const PostUpdateMemberBody = ` +{ + "member": { + "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", + "address": "10.0.2.11", + "weight": 10, + "name": "db", + "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", + "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "admin_state_up":false, + "protocol_port": 80 + } +} +` + +var ( + MemberWeb = pools.Member{ + SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", + TenantID: "2ffc6e22aae24e4795f87155d24c896f", + AdminStateUp: true, + Name: "web", + ID: "2a280670-c202-4b0b-a562-34077415aabf", + Address: "10.0.2.10", + Weight: 5, + ProtocolPort: 80, + } + MemberDb = pools.Member{ + SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", + TenantID: "2ffc6e22aae24e4795f87155d24c896f", + AdminStateUp: false, + Name: "db", + ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + Address: "10.0.2.11", + Weight: 10, + ProtocolPort: 80, + } + MemberUpdated = pools.Member{ + SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", + TenantID: "2ffc6e22aae24e4795f87155d24c896f", + AdminStateUp: false, + Name: "db", + ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + Address: "10.0.2.11", + Weight: 10, + ProtocolPort: 80, + } +) + +// HandleMemberListSuccessfully sets up the test server to respond to a member List request. +func HandleMemberListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", 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, MembersListBody) + case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": + fmt.Fprintf(w, `{ "members": [] }`) + default: + t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleMemberCreationSuccessfully sets up the test server to respond to a member creation request +// with a given response. +func HandleMemberCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", 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, `{ + "member": { + "address": "10.0.2.11", + "weight": 10, + "name": "db", + "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", + "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "protocol_port": 80 + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// HandleMemberGetSuccessfully sets up the test server to respond to a member Get request. +func HandleMemberGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", 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, SingleMemberBody) + }) +} + +// HandleMemberDeletionSuccessfully sets up the test server to respond to a member deletion request. +func HandleMemberDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", 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) + }) +} + +// HandleMemberUpdateSuccessfully sets up the test server to respond to a member Update request. +func HandleMemberUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", 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, `{ + "member": { + "name": "newMemberName", + "weight": 4 + } + }`) + + fmt.Fprintf(w, PostUpdateMemberBody) + }) +} diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go new file mode 100644 index 0000000000..9eaec03e01 --- /dev/null +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -0,0 +1,262 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" + fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListPools(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePoolListSuccessfully(t) + + pages := 0 + err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := pools.ExtractPools(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 pools, got %d", len(actual)) + } + th.CheckDeepEquals(t, PoolWeb, actual[0]) + th.CheckDeepEquals(t, PoolDb, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllPools(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePoolListSuccessfully(t) + + allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := pools.ExtractPools(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, PoolWeb, actual[0]) + th.CheckDeepEquals(t, PoolDb, actual[1]) +} + +func TestCreatePool(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePoolCreationSuccessfully(t, SinglePoolBody) + + actual, err := pools.Create(fake.ServiceClient(), pools.CreateOpts{ + LBMethod: pools.LBMethodRoundRobin, + Protocol: "HTTP", + Name: "Example pool", + TenantID: "2ffc6e22aae24e4795f87155d24c896f", + LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, PoolDb, *actual) +} + +func TestGetPool(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePoolGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := pools.Get(client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, PoolDb, *actual) +} + +func TestDeletePool(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePoolDeletionSuccessfully(t) + + res := pools.Delete(fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab") + th.AssertNoErr(t, res.Err) +} + +func TestUpdatePool(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePoolUpdateSuccessfully(t) + + client := fake.ServiceClient() + actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ + Name: "NewPoolName", + LBMethod: pools.LBMethodLeastConnections, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, PoolUpdated, *actual) +} + +func TestRequiredPoolCreateOpts(t *testing.T) { + res := pools.Create(fake.ServiceClient(), pools.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + LBMethod: pools.LBMethod("invalid"), + Protocol: pools.ProtocolHTTPS, + LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + + res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + LBMethod: pools.LBMethodRoundRobin, + Protocol: pools.Protocol("invalid"), + LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + + res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + LBMethod: pools.LBMethodRoundRobin, + Protocol: pools.ProtocolHTTPS, + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } +} + +func TestListMembers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleMemberListSuccessfully(t) + + pages := 0 + err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := pools.ExtractMembers(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 members, got %d", len(actual)) + } + th.CheckDeepEquals(t, MemberWeb, actual[0]) + th.CheckDeepEquals(t, MemberDb, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllMembers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleMemberListSuccessfully(t) + + allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := pools.ExtractMembers(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, MemberWeb, actual[0]) + th.CheckDeepEquals(t, MemberDb, actual[1]) +} + +func TestCreateMember(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleMemberCreationSuccessfully(t, SingleMemberBody) + + actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ + Name: "db", + SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", + TenantID: "2ffc6e22aae24e4795f87155d24c896f", + Address: "10.0.2.11", + ProtocolPort: 80, + Weight: 10, + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, MemberDb, *actual) +} + +func TestRequiredMemberCreateOpts(t *testing.T) { + res := pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80}) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80}) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"}) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } +} + +func TestGetMember(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleMemberGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := pools.GetMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, MemberDb, *actual) +} + +func TestDeleteMember(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleMemberDeletionSuccessfully(t) + + res := pools.DeleteMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateMember(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleMemberUpdateSuccessfully(t) + + client := fake.ServiceClient() + actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ + Name: "newMemberName", + Weight: 4, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, MemberUpdated, *actual) +} diff --git a/openstack/loadbalancer/v2/pools/urls.go b/openstack/loadbalancer/v2/pools/urls.go new file mode 100644 index 0000000000..bceca67707 --- /dev/null +++ b/openstack/loadbalancer/v2/pools/urls.go @@ -0,0 +1,25 @@ +package pools + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "pools" + memberPath = "members" +) + +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) +} + +func memberRootURL(c *gophercloud.ServiceClient, poolId string) string { + return c.ServiceURL(rootPath, resourcePath, poolId, memberPath) +} + +func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memeberID string) string { + return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memeberID) +} diff --git a/openstack/loadbalancer/v2/testhelper/client.go b/openstack/loadbalancer/v2/testhelper/client.go new file mode 100644 index 0000000000..7e1d917280 --- /dev/null +++ b/openstack/loadbalancer/v2/testhelper/client.go @@ -0,0 +1,14 @@ +package common + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const TokenID = client.TokenID + +func ServiceClient() *gophercloud.ServiceClient { + sc := client.ServiceClient() + sc.ResourceBase = sc.Endpoint + "v2.0/" + return sc +} From 6190a9d8261cd133ffde3c6abca0a09a5ec50191 Mon Sep 17 00:00:00 2001 From: zhangzujian Date: Wed, 4 Apr 2018 01:45:11 +0800 Subject: [PATCH 0264/2296] Identity V3: Change user password (#863) * Identity V3: change password for user * add testing * Identity V3 users: some renaming * fix JSON tags * fix doc and testing * fix doc * add acceptance testing for users.ChangePassword() --- .../openstack/identity/v3/users_test.go | 40 +++++++++++++++++++ openstack/identity/v3/users/doc.go | 16 ++++++++ openstack/identity/v3/users/requests.go | 39 ++++++++++++++++++ openstack/identity/v3/users/results.go | 6 +++ .../identity/v3/users/testing/fixtures.go | 24 ++++++++++- .../v3/users/testing/requests_test.go | 14 +++++++ openstack/identity/v3/users/urls.go | 4 ++ 7 files changed, 142 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index 3ba1e87cf5..7abeb9a887 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -123,6 +123,46 @@ func TestUserCRUD(t *testing.T) { tools.PrintResource(t, newUser.Extra) } +func TestUserChangePassword(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + createOpts := users.CreateOpts{ + Password: "secretsecret", + DomainID: "default", + Options: map[users.Option]interface{}{ + users.IgnorePasswordExpiry: true, + users.MultiFactorAuthRules: []interface{}{ + []string{"password", "totp"}, + []string{"password", "custom-auth-method"}, + }, + }, + Extra: map[string]interface{}{ + "email": "jsmith@example.com", + }, + } + + user, err := CreateUser(t, client, &createOpts) + if err != nil { + t.Fatalf("Unable to create user: %v", err) + } + defer DeleteUser(t, client, user.ID) + + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) + + changePasswordOpts := users.ChangePasswordOpts{ + OriginalPassword: "secretsecret", + Password: "new_secretsecret", + } + err = users.ChangePassword(client, user.ID, changePasswordOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to change password for user: %v", err) + } +} + func TestUsersListGroups(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index aa7ec196f5..282d0f9ca2 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -54,6 +54,22 @@ Example to Update a User panic(err) } +Example to Change Password of a User + + userID := "0fe36e73809d46aeae6705c39077b1b3" + originalPassword := "secretsecret" + password := "new_secretsecret" + + changePasswordOpts := users.ChangePasswordOpts{ + OriginalPassword: originalPassword, + Password: password, + } + + err := users.ChangePassword(identityClient, userID, changePasswordOpts).ExtractErr() + if err != nil { + panic(err) + } + Example to Delete a User userID := "0fe36e73809d46aeae6705c39077b1b3" diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 779d116fcc..b8eb7b0628 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -204,6 +204,45 @@ func Update(client *gophercloud.ServiceClient, userID string, opts UpdateOptsBui return } +// ChangePasswordOptsBuilder allows extensions to add additional parameters to +// the ChangePassword request. +type ChangePasswordOptsBuilder interface { + ToUserChangePasswordMap() (map[string]interface{}, error) +} + +// ChangePasswordOpts provides options for changing password for a user. +type ChangePasswordOpts struct { + // OriginalPassword is the original password of the user. + OriginalPassword string `json:"original_password"` + + // Password is the new password of the user. + Password string `json:"password"` +} + +// ToUserChangePasswordMap formats a ChangePasswordOpts into a ChangePassword request. +func (opts ChangePasswordOpts) ToUserChangePasswordMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "user") + if err != nil { + return nil, err + } + + return b, nil +} + +// ChangePassword changes password for a user. +func ChangePassword(client *gophercloud.ServiceClient, userID string, opts ChangePasswordOptsBuilder) (r ChangePasswordResult) { + b, err := opts.ToUserChangePasswordMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(changePasswordURL(client, userID), &b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} + // Delete deletes a user. func Delete(client *gophercloud.ServiceClient, userID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, userID), nil) diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index c474e882b9..12ab05eaba 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -98,6 +98,12 @@ type UpdateResult struct { userResult } +// ChangePasswordResult is the response from a ChangePassword operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type ChangePasswordResult struct { + gophercloud.ErrResult +} + // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 8d8e6df642..820da2ee5e 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -129,7 +129,7 @@ const CreateNoOptionsRequest = ` } ` -// UpdateRequest provides the input to as Update request. +// UpdateRequest provides the input to an Update request. const UpdateRequest = ` { "user": { @@ -164,6 +164,16 @@ const UpdateOutput = ` } ` +// ChangePasswordRequest provides the input to a ChangePassword request. +const ChangePasswordRequest = ` +{ + "user": { + "password": "new_secretsecret", + "original_password": "secretsecret" + } +} +` + // ListGroupsOutput provides a ListGroups result. const ListGroupsOutput = ` { @@ -423,6 +433,18 @@ func HandleUpdateUserSuccessfully(t *testing.T) { }) } +// HandleChangeUserPasswordSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that tests change user password. +func HandleChangeUserPasswordSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/9fe1d3/password", 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, ChangePasswordRequest) + + w.WriteHeader(http.StatusNoContent) + }) +} + // HandleDeleteUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests user deletion. func HandleDeleteUserSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index 15314ca61c..884c31d239 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -128,6 +128,20 @@ func TestUpdateUser(t *testing.T) { th.CheckDeepEquals(t, SecondUserUpdated, *actual) } +func TestChangeUserPassword(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleChangeUserPasswordSuccessfully(t) + + changePasswordOpts := users.ChangePasswordOpts{ + OriginalPassword: "secretsecret", + Password: "new_secretsecret", + } + + res := users.ChangePassword(client.ServiceClient(), "9fe1d3", changePasswordOpts) + th.AssertNoErr(t, res.Err) +} + func TestDeleteUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index 1db2831b5e..3d2bde85a7 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -18,6 +18,10 @@ func updateURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID) } +func changePasswordURL(client *gophercloud.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "password") +} + func deleteURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID) } From 5cc045ce480e5a23394538be9ce931343248607b Mon Sep 17 00:00:00 2001 From: zhangzujian Date: Wed, 4 Apr 2018 10:28:58 +0800 Subject: [PATCH 0265/2296] Identity V3: add user to group (#867) * add: Identity V3: add user to group * add acceptance testing for users.AddToGroup() * fix acceptance testing --- .../openstack/identity/v3/users_test.go | 54 +++++++++++++++++++ openstack/identity/v3/users/doc.go | 10 ++++ openstack/identity/v3/users/requests.go | 9 ++++ openstack/identity/v3/users/results.go | 6 +++ .../identity/v3/users/testing/fixtures.go | 11 ++++ .../v3/users/testing/requests_test.go | 8 +++ openstack/identity/v3/users/urls.go | 4 ++ 7 files changed, 102 insertions(+) diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index 7abeb9a887..e21bfaf03e 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -196,6 +196,60 @@ func TestUsersListGroups(t *testing.T) { } } +func TestUserAddToGroup(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + createOpts := users.CreateOpts{ + Password: "foobar", + DomainID: "default", + Options: map[users.Option]interface{}{ + users.IgnorePasswordExpiry: true, + users.MultiFactorAuthRules: []interface{}{ + []string{"password", "totp"}, + []string{"password", "custom-auth-method"}, + }, + }, + Extra: map[string]interface{}{ + "email": "jsmith@example.com", + }, + } + + user, err := CreateUser(t, client, &createOpts) + if err != nil { + t.Fatalf("Unable to create user: %v", err) + } + defer DeleteUser(t, client, user.ID) + + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) + + createGroupOpts := groups.CreateOpts{ + Name: "testgroup", + DomainID: "default", + Extra: map[string]interface{}{ + "email": "testgroup@example.com", + }, + } + + // Create Group in the default domain + group, err := CreateGroup(t, client, &createGroupOpts) + if err != nil { + t.Fatalf("Unable to create group: %v", err) + } + defer DeleteGroup(t, client, group.ID) + + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) + + err = users.AddToGroup(client, group.ID, user.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to add user to group: %v", err) + } +} + func TestUsersListProjects(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index 282d0f9ca2..ca9e4b4285 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -96,6 +96,16 @@ Example to List Groups a User Belongs To fmt.Printf("%+v\n", group) } +Example to Add a User to a Group + + groupID := "bede500ee1124ae9b0006ff859758b3a" + userID := "0fe36e73809d46aeae6705c39077b1b3" + err := users.AddToGroup(identityClient, groupID, userID).ExtractErr() + + if err != nil { + panic(err) + } + Example to List Projects a User Belongs To userID := "0fe36e73809d46aeae6705c39077b1b3" diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index b8eb7b0628..ec6e90669c 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -257,6 +257,15 @@ func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pag }) } +// AddToGroup adds a user to a group. +func AddToGroup(client *gophercloud.ServiceClient, groupID, userID string) (r AddToGroupResult) { + url := addToGroupURL(client, groupID, userID) + _, r.Err = client.Put(url, nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} + // ListProjects enumerates groups user belongs to. func ListProjects(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := listProjectsURL(client, userID) diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 12ab05eaba..d8a55612e4 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -110,6 +110,12 @@ type DeleteResult struct { gophercloud.ErrResult } +// AddToGroupResult is the response from a AddToGroup operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type AddToGroupResult struct { + gophercloud.ErrResult +} + // UserPage is a single page of User results. type UserPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 820da2ee5e..0f97c14854 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -470,6 +470,17 @@ func HandleListUserGroupsSuccessfully(t *testing.T) { }) } +// HandleAddToGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users/{userID} +// on the test handler mux that tests adding user to group. +func HandleAddToGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", 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) + }) +} + // HandleListUserProjectsSuccessfully creates an HTTP handler at /users/{userID}/projects // on the test handler mux that respons wit a list of two projects func HandleListUserProjectsSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index 884c31d239..b372d98913 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -162,6 +162,14 @@ func TestListUserGroups(t *testing.T) { th.CheckDeepEquals(t, ExpectedGroupsSlice, actual) } +func TestAddToGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAddToGroupSuccessfully(t) + res := users.AddToGroup(client.ServiceClient(), "ea167b", "9fe1d3") + th.AssertNoErr(t, res.Err) +} + func TestListUserProjects(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index 3d2bde85a7..900a435c62 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -30,6 +30,10 @@ func listGroupsURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "groups") } +func addToGroupURL(client *gophercloud.ServiceClient, groupID, userID string) string { + return client.ServiceURL("groups", groupID, "users", userID) +} + func listProjectsURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "projects") } From bc178df6d64d999e4a5520b601bc3e26bb38f446 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Wed, 4 Apr 2018 16:27:41 +1200 Subject: [PATCH 0266/2296] LBaaS v2 l7 policy support [Part 1]: create l7policy (#834) * LBaaS v2 l7 policy support [Part 1]: create l7policy For #832 L7policy functionality in Octavia is backward compatible with Neutron-LBaaS. Octavia L7 policy create API implementation: https://github.com/openstack/octavia/blob/master/octavia/api/v2/controllers/l7policy.py#L140 Neutron-LBaaS L7 policy create API implementation: https://github.com/openstack/neutron-lbaas/blob/8e8a6c47c7d38fa7b61850ff9ea2cf130718ded3/neutron_lbaas/services/loadbalancer/plugin.py#L913 Octavia L7 policy create API doc: https://developer.openstack.org/api-ref/load-balancer/v2/index.html#create-an-l7-policy * Use original type for the response fields * Change to use lbClient for octavia acceptance test --- .../openstack/loadbalancer/v2/loadbalancer.go | 30 ++++++ .../loadbalancer/v2/loadbalancers_test.go | 6 ++ openstack/loadbalancer/v2/l7policies/doc.go | 16 ++++ .../loadbalancer/v2/l7policies/requests.go | 84 +++++++++++++++++ .../loadbalancer/v2/l7policies/results.go | 93 +++++++++++++++++++ .../loadbalancer/v2/l7policies/testing/doc.go | 2 + .../v2/l7policies/testing/fixtures.go | 67 +++++++++++++ .../v2/l7policies/testing/requests_test.go | 42 +++++++++ openstack/loadbalancer/v2/l7policies/urls.go | 12 +++ 9 files changed, 352 insertions(+) create mode 100644 openstack/loadbalancer/v2/l7policies/doc.go create mode 100644 openstack/loadbalancer/v2/l7policies/requests.go create mode 100644 openstack/loadbalancer/v2/l7policies/results.go create mode 100644 openstack/loadbalancer/v2/l7policies/testing/doc.go create mode 100644 openstack/loadbalancer/v2/l7policies/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/l7policies/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/l7policies/urls.go diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 0a811d69c3..ba1b64fb7e 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" @@ -172,6 +173,35 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance return pool, nil } +// CreateL7Policy will create a l7 policy with a random name with a specified listener +// and loadbalancer. An error will be returned if the l7 policy could not be +// created. +func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) { + policyName := tools.RandomString("TESTACCT-", 8) + + t.Logf("Attempting to create l7 policy %s", policyName) + + createOpts := l7policies.CreateOpts{ + Name: policyName, + ListenerID: listener.ID, + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + } + + policy, err := l7policies.Create(client, createOpts).Extract() + if err != nil { + return policy, err + } + + t.Logf("Successfully created l7 policy %s", policyName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active") + } + + return policy, nil +} + // DeleteListener will delete a specified listener. A fatal error will occur if // the listener could not be deleted. This works best when used as a deferred // function. diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index b18b975c35..866f9b852a 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -100,6 +100,12 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newListener) + // L7 policy + _, err = CreateL7Policy(t, lbClient, listener, lb) + if err != nil { + t.Fatalf("Unable to create l7 policy: %v", err) + } + // Pool pool, err := CreatePool(t, lbClient, lb) if err != nil { diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go new file mode 100644 index 0000000000..7d9418f355 --- /dev/null +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -0,0 +1,16 @@ +/* +Package l7policies provides information and interaction with L7Policies and +Rules of the LBaaS v2 extension for the OpenStack Networking service. +Example to Create a L7Policy + createOpts := l7policies.CreateOpts{ + Name: "redirect-example.com", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + } + l7policy, err := l7policies.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } +*/ +package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go new file mode 100644 index 0000000000..1d59b4a601 --- /dev/null +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -0,0 +1,84 @@ +package l7policies + +import ( + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToL7PolicyCreateMap() (map[string]interface{}, error) +} + +type Action string +type RuleType string +type CompareType string + +const ( + ActionRedirectToPool Action = "REDIRECT_TO_POOL" + ActionRedirectToURL Action = "REDIRECT_TO_URL" + ActionReject Action = "REJECT" + + TypeCookie RuleType = "COOKIE" + TypeFileType RuleType = "FILE_TYPE" + TypeHeader RuleType = "HEADER" + TypeHostName RuleType = "HOST_NAME" + TypePath RuleType = "PATH" + + CompareTypeContains CompareType = "CONTAINS" + CompareTypeEndWith CompareType = "ENDS_WITH" + CompareTypeEqual CompareType = "EQUAL_TO" + CompareTypeRegex CompareType = "REGEX" + CompareTypeStartWith CompareType = "STARTS_WITH" +) + +// CreateOpts is the common options struct used in this package's Create +// operation. +type CreateOpts struct { + // Name of the L7 policy. + Name string `json:"name,omitempty"` + + // The ID of the listener. + ListenerID string `json:"listener_id" required:"true"` + + // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. + Action Action `json:"action" required:"true"` + + // The position of this policy on the listener. + Position int32 `json:"position,omitempty"` + + // A human-readable description for the resource. + Description string `json:"description,omitempty"` + + // TenantID is the UUID of the project who owns the L7 policy in neutron-lbaas. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the UUID of the project who owns the L7 policy in octavia. + // 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 the pool with this ID. + // Only valid if action is REDIRECT_TO_POOL. + RedirectPoolID string `json:"redirect_pool_id,omitempty"` + + // Requests matching this policy will be redirected to this URL. + // Only valid if action is REDIRECT_TO_URL. + RedirectURL string `json:"redirect_url,omitempty"` +} + +// ToL7PolicyCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "l7policy") +} + +// Create accepts a CreateOpts struct and uses the values to create a new l7policy. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToL7PolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go new file mode 100644 index 0000000000..827fd59f69 --- /dev/null +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -0,0 +1,93 @@ +package l7policies + +import ( + "github.com/gophercloud/gophercloud" +) + +// L7Policy is a collection of L7 rules associated with a Listener, and which +// may also have an association to a back-end pool. +type L7Policy struct { + // The unique ID for the L7 policy. + ID string `json:"id"` + + // Name of the L7 policy. + Name string `json:"name"` + + // The ID of the listener. + ListenerID string `json:"listener_id"` + + // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. + Action string `json:"action"` + + // The position of this policy on the listener. + Position int32 `json:"position"` + + // A human-readable description for the resource. + Description string `json:"description"` + + // TenantID is the UUID of the project who owns the L7 policy in neutron-lbaas. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id"` + + // 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"` + + // Requests matching this policy will be redirected to this URL. + // Only valid if action is REDIRECT_TO_URL. + RedirectURL string `json:"redirect_url"` + + // The administrative state of the L7 policy, which is up (true) or down (false). + AdminStateUp bool `json:"admin_state_up"` + + // Rules are List of associated L7 rule IDs. + Rules []Rule `json:"rules"` +} + +// Rule represents layer 7 load balancing rule. +type Rule struct { + // The unique ID for the L7 rule. + ID string `json:"id"` + + // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. + RuleType string `json:"type"` + + // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. + CompareType string `json:"compare_type"` + + // The value to use for the comparison. For example, the file type to compare. + Value string `json:"value"` + + // TenantID is the UUID of the project who owns the rule in neutron-lbaas. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id"` + + // The key to use for the comparison. For example, the name of the cookie to evaluate. + Key string `json:"key"` + + // When true the logic of the rule is inverted. For example, with invert true, + // equal to would become not equal to. Default is false. + Invert bool `json:"invert"` + + // The administrative state of the L7 rule, which is up (true) or down (false). + AdminStateUp bool `json:"admin_state_up"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a l7policy. +func (r commonResult) Extract() (*L7Policy, error) { + var s struct { + L7Policy *L7Policy `json:"l7policy"` + } + err := r.ExtractInto(&s) + return s.L7Policy, err +} + +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret the result as a L7Policy. +type CreateResult struct { + commonResult +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/doc.go b/openstack/loadbalancer/v2/l7policies/testing/doc.go new file mode 100644 index 0000000000..f8068dfb6b --- /dev/null +++ b/openstack/loadbalancer/v2/l7policies/testing/doc.go @@ -0,0 +1,2 @@ +// l7policies unit tests +package testing diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go new file mode 100644 index 0000000000..f64dde2e66 --- /dev/null +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -0,0 +1,67 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// SingleL7PolicyBody is the canned body of a Get request on an existing l7policy. +const SingleL7PolicyBody = ` +{ + "l7policy": { + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "description": "", + "admin_state_up": true, + "redirect_pool_id": null, + "redirect_url": "http://www.example.com", + "action": "REDIRECT_TO_URL", + "position": 1, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", + "name": "redirect-example.com", + "rules": [] + } +} +` + +var ( + L7PolicyToURL = l7policies.L7Policy{ + ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", + Name: "redirect-example.com", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: "REDIRECT_TO_URL", + Position: 1, + Description: "", + TenantID: "e3cd678b11784734bc366148aa37580e", + RedirectPoolID: "", + RedirectURL: "http://www.example.com", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + } +) + +// HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request +// with a given response. +func HandleL7PolicyCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies", 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, `{ + "l7policy": { + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "redirect_url": "http://www.example.com", + "name": "redirect-example.com", + "action": "REDIRECT_TO_URL" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go new file mode 100644 index 0000000000..fe0278c197 --- /dev/null +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -0,0 +1,42 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreateL7Policy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyCreationSuccessfully(t, SingleL7PolicyBody) + + actual, err := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ + Name: "redirect-example.com", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + }).Extract() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, L7PolicyToURL, *actual) +} + +func TestRequiredL7PolicyCreateOpts(t *testing.T) { + // no param specified. + res := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + + // Action is invalid. + res = l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: l7policies.Action("invalid"), + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } +} diff --git a/openstack/loadbalancer/v2/l7policies/urls.go b/openstack/loadbalancer/v2/l7policies/urls.go new file mode 100644 index 0000000000..fd898e9b06 --- /dev/null +++ b/openstack/loadbalancer/v2/l7policies/urls.go @@ -0,0 +1,12 @@ +package l7policies + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "l7policies" +) + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, resourcePath) +} From 30ecef204a3177eb9d3746bb23d9ef52ca91e96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Wed, 4 Apr 2018 12:35:17 +0800 Subject: [PATCH 0267/2296] Identity V3: remove user from group (#882) * Identity V3 users: add function RemoveFromGroup() * fix acceptance testing --- .../openstack/identity/v3/users_test.go | 61 ++++++++++++++++++- openstack/identity/v3/users/doc.go | 10 +++ openstack/identity/v3/users/requests.go | 9 +++ openstack/identity/v3/users/results.go | 6 ++ .../identity/v3/users/testing/fixtures.go | 11 ++++ .../v3/users/testing/requests_test.go | 8 +++ openstack/identity/v3/users/urls.go | 4 ++ 7 files changed, 108 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index e21bfaf03e..1eb15456d6 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -196,7 +196,7 @@ func TestUsersListGroups(t *testing.T) { } } -func TestUserAddToGroup(t *testing.T) { +func TestUsersAddToGroup(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { t.Fatalf("Unable to obtain an identity client: %v", err) @@ -250,6 +250,65 @@ func TestUserAddToGroup(t *testing.T) { } } +func TestUsersRemoveFromGroup(t *testing.T) { + client, err := clients.NewIdentityV3Client() + if err != nil { + t.Fatalf("Unable to obtain an identity client: %v", err) + } + + createOpts := users.CreateOpts{ + Password: "foobar", + DomainID: "default", + Options: map[users.Option]interface{}{ + users.IgnorePasswordExpiry: true, + users.MultiFactorAuthRules: []interface{}{ + []string{"password", "totp"}, + []string{"password", "custom-auth-method"}, + }, + }, + Extra: map[string]interface{}{ + "email": "jsmith@example.com", + }, + } + + user, err := CreateUser(t, client, &createOpts) + if err != nil { + t.Fatalf("Unable to create user: %v", err) + } + defer DeleteUser(t, client, user.ID) + + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) + + createGroupOpts := groups.CreateOpts{ + Name: "testgroup", + DomainID: "default", + Extra: map[string]interface{}{ + "email": "testgroup@example.com", + }, + } + + // Create Group in the default domain + group, err := CreateGroup(t, client, &createGroupOpts) + if err != nil { + t.Fatalf("Unable to create group: %v", err) + } + defer DeleteGroup(t, client, group.ID) + + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) + + err = users.AddToGroup(client, group.ID, user.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to add user to group: %v", err) + } + + err = users.RemoveFromGroup(client, group.ID, user.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to remove user from group: %v", err) + } +} + func TestUsersListProjects(t *testing.T) { client, err := clients.NewIdentityV3Client() if err != nil { diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index ca9e4b4285..c51a3fb607 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -106,6 +106,16 @@ Example to Add a User to a Group panic(err) } +Example to Remove a User from a Group + + groupID := "bede500ee1124ae9b0006ff859758b3a" + userID := "0fe36e73809d46aeae6705c39077b1b3" + err := users.RemoveFromGroup(identityClient, groupID, userID).ExtractErr() + + if err != nil { + panic(err) + } + Example to List Projects a User Belongs To userID := "0fe36e73809d46aeae6705c39077b1b3" diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index ec6e90669c..e1be94e7d9 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -266,6 +266,15 @@ func AddToGroup(client *gophercloud.ServiceClient, groupID, userID string) (r Ad return } +// RemoveFromGroup removes a user from a group. +func RemoveFromGroup(client *gophercloud.ServiceClient, groupID, userID string) (r RemoveFromGroupResult) { + url := removeFromGroupURL(client, groupID, userID) + _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} + // ListProjects enumerates groups user belongs to. func ListProjects(client *gophercloud.ServiceClient, userID string) pagination.Pager { url := listProjectsURL(client, userID) diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index d8a55612e4..00a1a00062 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -116,6 +116,12 @@ type AddToGroupResult struct { gophercloud.ErrResult } +// RemoveFromGroupResult is the response from a RemoveFromGroup operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type RemoveFromGroupResult struct { + gophercloud.ErrResult +} + // UserPage is a single page of User results. type UserPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 0f97c14854..73e6acb767 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -481,6 +481,17 @@ func HandleAddToGroupSuccessfully(t *testing.T) { }) } +// HandleRemoveFromGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users/{userID} +// on the test handler mux that tests removing user from group. +func HandleRemoveFromGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", 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) + }) +} + // HandleListUserProjectsSuccessfully creates an HTTP handler at /users/{userID}/projects // on the test handler mux that respons wit a list of two projects func HandleListUserProjectsSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index b372d98913..8cd4c47ac5 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -170,6 +170,14 @@ func TestAddToGroup(t *testing.T) { th.AssertNoErr(t, res.Err) } +func TestRemoveFromGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRemoveFromGroupSuccessfully(t) + res := users.RemoveFromGroup(client.ServiceClient(), "ea167b", "9fe1d3") + th.AssertNoErr(t, res.Err) +} + func TestListUserProjects(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index 900a435c62..35468ad28e 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -34,6 +34,10 @@ func addToGroupURL(client *gophercloud.ServiceClient, groupID, userID string) st return client.ServiceURL("groups", groupID, "users", userID) } +func removeFromGroupURL(client *gophercloud.ServiceClient, groupID, userID string) string { + return client.ServiceURL("groups", groupID, "users", userID) +} + func listProjectsURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "projects") } From 041bfb8d7fd5d76f57e2af41ba9c292f7b09e9e8 Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Wed, 4 Apr 2018 08:34:37 -0700 Subject: [PATCH 0268/2296] Clustering Policy List implementation (#870) * Add Clustering Policy List Implementation * add metadata * Fix review comments * Changed to non-pointer time.Time for CreatedAt and UpdatedAt fields * Changed to pointer bool for GlobalProject * Fixed Domain and Project field names * Removed Metadata field * change UserUUID to User * remove debug error messages --- .../openstack/clustering/v1/policies_test.go | 36 +++++ openstack/clustering/v1/policies/doc.go | 26 +++ openstack/clustering/v1/policies/requests.go | 56 +++++++ openstack/clustering/v1/policies/results.go | 74 +++++++++ .../clustering/v1/policies/testing/doc.go | 2 + .../v1/policies/testing/fixtures.go | 152 ++++++++++++++++++ .../v1/policies/testing/requests_test.go | 41 +++++ openstack/clustering/v1/policies/urls.go | 12 ++ 8 files changed, 399 insertions(+) create mode 100644 acceptance/openstack/clustering/v1/policies_test.go create mode 100644 openstack/clustering/v1/policies/doc.go create mode 100644 openstack/clustering/v1/policies/requests.go create mode 100644 openstack/clustering/v1/policies/results.go create mode 100644 openstack/clustering/v1/policies/testing/doc.go create mode 100644 openstack/clustering/v1/policies/testing/fixtures.go create mode 100644 openstack/clustering/v1/policies/testing/requests_test.go create mode 100644 openstack/clustering/v1/policies/urls.go diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go new file mode 100644 index 0000000000..b3343c3ac3 --- /dev/null +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -0,0 +1,36 @@ +// +build acceptance clustering policies + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestPolicyList(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + allPages, err := policies.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err := policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + + for _, v := range allPolicies { + tools.PrintResource(t, v) + + if v.CreatedAt.IsZero() { + t.Fatalf("CreatedAt value should not be zero") + } + t.Log("Created at: " + v.CreatedAt.String()) + + if !v.UpdatedAt.IsZero() { + t.Log("Updated at: " + v.UpdatedAt.String()) + } + } +} diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go new file mode 100644 index 0000000000..053c380551 --- /dev/null +++ b/openstack/clustering/v1/policies/doc.go @@ -0,0 +1,26 @@ +/* +Package policies provides information and interaction with the policies through +the OpenStack Clustering service. + +Example to List Policies + + listOpts := policies.ListOpts{ + Limit: 2, + } + + allPages, err := policies.List(clusteringClient, listOpts).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) + } + +*/ +package policies diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go new file mode 100644 index 0000000000..4f50a0ce3c --- /dev/null +++ b/openstack/clustering/v1/policies/requests.go @@ -0,0 +1,56 @@ +package policies + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder Builder. +type ListOptsBuilder interface { + ToPolicyListQuery() (string, error) +} + +// ListOpts params +type ListOpts struct { + // Limit limits the number of Policies to return. + Limit int `q:"limit"` + + // Marker and Limit control paging. Marker instructs List where to start listing from. + Marker string `q:"marker"` + + // Sorts the response by one or more attribute and optional sort direction combinations. + Sort string `q:"sort"` + + // GlobalProject indicates whether to include resources for all projects or resources for the current project + GlobalProject *bool `q:"global_project"` + + // Name to filter the response by the specified name property of the object + Name string `q:"name"` + + // Filter the response by the specified type property of the object + Type string `q:"type"` +} + +// ToPolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to retrieve a list of policies. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := policyListURL(client) + if opts != nil { + query, err := opts.ToPolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + p := PolicyPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go new file mode 100644 index 0000000000..044e58d99e --- /dev/null +++ b/openstack/clustering/v1/policies/results.go @@ -0,0 +1,74 @@ +package policies + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Policy represents a clustering policy in the Openstack cloud +type Policy struct { + CreatedAt time.Time `json:"-"` + Data map[string]interface{} `json:"data"` + Domain string `json:"domain"` + ID string `json:"id"` + Name string `json:"name"` + Project string `json:"project"` + Spec map[string]interface{} `json:"spec"` + Type string `json:"type"` + UpdatedAt time.Time `json:"-"` + User string `json:"user"` +} + +// ExtractPolicies interprets a page of results as a slice of Policy. +func ExtractPolicies(r pagination.Page) ([]Policy, error) { + var s struct { + Policies []Policy `json:"policies"` + } + err := (r.(PolicyPage)).ExtractInto(&s) + return s.Policies, err +} + +// PolicyPage contains a list page of all policies from a List call. +type PolicyPage struct { + pagination.MarkerPageBase +} + +// IsEmpty determines if a PolicyPage contains any results. +func (page PolicyPage) IsEmpty() (bool, error) { + policies, err := ExtractPolicies(page) + return len(policies) == 0, err +} + +// LastMarker returns the last policy ID in a ListResult. +func (r PolicyPage) LastMarker() (string, error) { + policies, err := ExtractPolicies(r) + if err != nil { + return "", err + } + if len(policies) == 0 { + return "", nil + } + return policies[len(policies)-1].ID, nil +} + +func (r *Policy) UnmarshalJSON(b []byte) error { + type tmp Policy + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at,omitempty"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at,omitempty"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Policy(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} diff --git a/openstack/clustering/v1/policies/testing/doc.go b/openstack/clustering/v1/policies/testing/doc.go new file mode 100644 index 0000000000..61bc1c3b6d --- /dev/null +++ b/openstack/clustering/v1/policies/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_policies_v1 +package testing diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go new file mode 100644 index 0000000000..30db27b88d --- /dev/null +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -0,0 +1,152 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const PolicyListBody1 = ` +{ + "policies": [ + { + "created_at": "2018-04-02T21:43:30.000000", + "data": {}, + "domain": null, + "id": "PolicyListBodyID1", + "name": "delpol", + "project": "018cd0909fb44cd5bc9b7a3cd664920e", + "spec": { + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": { + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": true, + "grace_period": 60, + "reduce_desired_capacity": false + }, + "type": "senlin.policy.deletion", + "version": 1 + }, + "type": "senlin.policy.deletion-1.0", + "updated_at": null, + "user": "fe43e41739154b72818565e0d2580819" + } + ] +} +` + +const PolicyListBody2 = ` +{ + "policies": [ + { + "created_at": "2018-04-02T22:29:36.000000", + "data": {}, + "domain": null, + "id": "PolicyListBodyID2", + "name": "delpol2", + "project": "018cd0909fb44cd5bc9b7a3cd664920e", + "spec": { + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": { + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": true, + "grace_period": 60, + "reduce_desired_capacity": false + }, + "type": "senlin.policy.deletion", + "version": 1 + }, + "type": "senlin.policy.deletion-1.0", + "updated_at": null, + "user": "fe43e41739154b72818565e0d2580819" + } + ] +} +` + +var ( + ExpectedPolicyCreatedAt1, _ = time.Parse(time.RFC3339, "2018-04-02T21:43:30.000000Z") + ExpectedPolicyCreatedAt2, _ = time.Parse(time.RFC3339, "2018-04-02T22:29:36.000000Z") + ZeroTime, _ = time.Parse(time.RFC3339, "1-01-01T00:00:00.000000Z") + + ExpectedPolicies = [][]policies.Policy{ + { + { + CreatedAt: ExpectedPolicyCreatedAt1, + Data: map[string]interface{}{}, + Domain: "", + ID: "PolicyListBodyID1", + Name: "delpol", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: map[string]interface{}{ + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": map[string]interface{}{ + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": true, + "grace_period": float64(60), + "reduce_desired_capacity": false, + }, + "type": "senlin.policy.deletion", + "version": float64(1), + }, + Type: "senlin.policy.deletion-1.0", + User: "fe43e41739154b72818565e0d2580819", + UpdatedAt: ZeroTime, + }, + }, + { + { + CreatedAt: ExpectedPolicyCreatedAt2, + Data: map[string]interface{}{}, + Domain: "", + ID: "PolicyListBodyID2", + Name: "delpol2", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: map[string]interface{}{ + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": map[string]interface{}{ + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": true, + "grace_period": float64(60), + "reduce_desired_capacity": false, + }, + "type": "senlin.policy.deletion", + "version": float64(1), + }, + Type: "senlin.policy.deletion-1.0", + User: "fe43e41739154b72818565e0d2580819", + UpdatedAt: ZeroTime, + }, + }, + } +) + +func HandlePolicyList(t *testing.T) { + th.Mux.HandleFunc("/v1/policies", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, PolicyListBody1) + case "PolicyListBodyID1": + fmt.Fprintf(w, PolicyListBody2) + case "PolicyListBodyID2": + fmt.Fprintf(w, `{"policies":[]}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go new file mode 100644 index 0000000000..0f110a52f0 --- /dev/null +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -0,0 +1,41 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListPolicies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandlePolicyList(t) + + listOpts := policies.ListOpts{ + Limit: 1, + } + + count := 0 + err := policies.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { + actual, err := policies.ExtractPolicies(page) + if err != nil { + t.Errorf("Failed to extract policies: %v", err) + return false, err + } + + th.AssertDeepEquals(t, ExpectedPolicies[count], actual) + count++ + + return true, nil + }) + + th.AssertNoErr(t, err) + + if count != 2 { + t.Errorf("Expected 2 pages, got %d", count) + } +} diff --git a/openstack/clustering/v1/policies/urls.go b/openstack/clustering/v1/policies/urls.go new file mode 100644 index 0000000000..9e2349ea8b --- /dev/null +++ b/openstack/clustering/v1/policies/urls.go @@ -0,0 +1,12 @@ +package policies + +import "github.com/gophercloud/gophercloud" + +const ( + apiVersion = "v1" + apiName = "policies" +) + +func policyListURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} From 407d3c61d7efbe73af3fc82489b8be46f09b8a7e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 4 Apr 2018 20:10:29 +0000 Subject: [PATCH 0269/2296] Acc tests: Fix TestSecGroupsAddGroupToServer --- acceptance/openstack/compute/v2/secgroup_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/acceptance/openstack/compute/v2/secgroup_test.go index 0d69a18fd0..6f3028432a 100644 --- a/acceptance/openstack/compute/v2/secgroup_test.go +++ b/acceptance/openstack/compute/v2/secgroup_test.go @@ -101,10 +101,6 @@ func TestSecGroupsAddGroupToServer(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecurityGroupRule(t, client, rule.ID) - server, err = CreateServer(t, client) - th.AssertNoErr(t, err) - defer DeleteServer(t, client, server) - t.Logf("Adding group %s to server %s", securityGroup.ID, server.ID) err = secgroups.AddServer(client, server.ID, securityGroup.Name).ExtractErr() th.AssertNoErr(t, err) From 54a8a6b7fcccae909f78aee14f7ecddf70f775e7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 4 Apr 2018 18:07:26 -0600 Subject: [PATCH 0270/2296] Fix go tip gofmt errors (#892) * Fix go tip gofmt errors * Modify format script to ignore certain files * Update Travis to test with Go 1.10 * Fix yaml parsing in .travis.yml --- .travis.yml | 4 +- .../extensions/quotasets/testing/fixtures.go | 2 +- .../ikepolicies/testing/requests_test.go | 38 +++++++++---------- script/format | 26 +++++++++++-- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 59c4194952..02728f4968 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,8 @@ install: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/goimports go: -- 1.8 -- tip +- "1.10" +- "tip" env: global: - secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ=" diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go index 53516413db..2915d31037 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go +++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go @@ -121,7 +121,7 @@ var FirstQuotaSet = quotasets.QuotaSet{ // FirstQuotaDetailsSet is the first result in ListOutput. var FirstQuotaDetailsSet = quotasets.QuotaDetailSet{ - ID: FirstTenantID, + ID: FirstTenantID, InjectedFileContentBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10240}, InjectedFilePathBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 255}, InjectedFiles: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 5}, diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index 9c3b08f120..b3ec548da7 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -74,13 +74,13 @@ func TestCreate(t *testing.T) { IKEVersion: "v2", TenantID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", - Lifetime: expectedLifetime, - ProjectID: "9145d91459d248b1b02fdaca97c6a75d", + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + Lifetime: expectedLifetime, + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", } th.AssertDeepEquals(t, expected, *actual) } @@ -130,12 +130,12 @@ func TestGet(t *testing.T) { TenantID: "9145d91459d248b1b02fdaca97c6a75d", ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", - Lifetime: expectedLifetime, + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Lifetime: expectedLifetime, } th.AssertDeepEquals(t, expected, *actual) } @@ -208,12 +208,12 @@ func TestList(t *testing.T) { TenantID: "9145d91459d248b1b02fdaca97c6a75d", ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", - Lifetime: expectedLifetime, + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Lifetime: expectedLifetime, }, } diff --git a/script/format b/script/format index 8ed602fde0..05645a8252 100755 --- a/script/format +++ b/script/format @@ -16,8 +16,26 @@ find_files() { \) -name '*.go' } -diff=$(find_files | xargs ${goimports} -d -e 2>&1) -if [[ -n "${diff}" ]]; then - echo "${diff}" - exit 1 +ignore_files=( + "./openstack/compute/v2/extensions/quotasets/testing/fixtures.go" + "./openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go" +) + +bad_files=$(find_files | xargs ${goimports} -l) + +final_files=() +for bad_file in $bad_files; do + found= + for ignore_file in "${ignore_files[@]}"; do + [[ "${bad_file}" == "${ignore_file}" ]] && { found=1; break; } + done + [[ -n $found ]] || final_files+=("$bad_file") +done + +if [[ "${#final_files[@]}" -gt 0 ]]; then + diff=$(echo "${final_files[@]}" | xargs ${goimports} -d -e 2>&1) + if [[ -n "${diff}" ]]; then + echo "${diff}" + exit 1 + fi fi From 4b59796743b1afab8785ff215be229b3a81c5f9e Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Wed, 4 Apr 2018 19:14:51 +1200 Subject: [PATCH 0271/2296] LBaaS v2 l7 policy support - part 2: list l7policy For #832 Neutron-LBaaS l7 policy list API implementation: https://github.com/openstack/neutron-lbaas/blob/8e8a6c47c7d38fa7b61850ff9ea2cf130718ded3/neutron_lbaas/services/loadbalancer/plugin.py#L967 Octavia l7 policy list API implementation: https://github.com/openstack/octavia/blob/54a4cf00cf304b108013ca4487c138c13f30983d/octavia/api/v2/controllers/l7policy.py#L61 --- .../loadbalancer/v2/l7policies_test.go | 32 +++++++++ openstack/loadbalancer/v2/l7policies/doc.go | 16 +++++ .../loadbalancer/v2/l7policies/requests.go | 49 ++++++++++++++ .../loadbalancer/v2/l7policies/results.go | 38 +++++++++++ .../v2/l7policies/testing/fixtures.go | 66 +++++++++++++++++++ .../v2/l7policies/testing/requests_test.go | 44 +++++++++++++ 6 files changed, 245 insertions(+) create mode 100644 acceptance/openstack/loadbalancer/v2/l7policies_test.go diff --git a/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/acceptance/openstack/loadbalancer/v2/l7policies_test.go new file mode 100644 index 0000000000..848d4e85bc --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking loadbalancer l7policies + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" +) + +func TestL7PoliciesList(t *testing.T) { + client, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := l7policies.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list l7policies: %v", err) + } + + allL7Policies, err := l7policies.ExtractL7Policies(allPages) + if err != nil { + t.Fatalf("Unable to extract l7policies: %v", err) + } + + for _, policy := range allL7Policies { + tools.PrintResource(t, policy) + } +} diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 7d9418f355..98e074b876 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -12,5 +12,21 @@ Example to Create a L7Policy if err != nil { panic(err) } + +Example to List L7Policies + listOpts := l7policies.ListOpts{ + ListenerID: "c79a4468-d788-410c-bf79-9a8ef6354852", + } + allPages, err := l7policies.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + allL7Policies, err := l7policies.ExtractL7Policies(allPages) + if err != nil { + panic(err) + } + for _, l7policy := range allL7Policies { + fmt.Printf("%+v\n", l7policy) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 1d59b4a601..48a197b146 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -2,6 +2,7 @@ package l7policies import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -82,3 +83,51 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToL7PolicyListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. +type ListOpts struct { + Name string `q:"name"` + ListenerID string `q:"listener_id"` + Action string `q:"action"` + TenantID string `q:"tenant_id"` + RedirectPoolID string `q:"redirect_pool_id"` + RedirectURL string `q:"redirect_url"` + ID string `q:"id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToL7PolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToL7PolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// l7policies. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those l7policies that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToL7PolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return L7PolicyPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index 827fd59f69..a9f3f1405e 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -2,6 +2,7 @@ package l7policies import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // L7Policy is a collection of L7 rules associated with a Listener, and which @@ -91,3 +92,40 @@ func (r commonResult) Extract() (*L7Policy, error) { type CreateResult struct { commonResult } + +// L7PolicyPage is the page returned by a pager when traversing over a +// collection of l7policies. +type L7PolicyPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of l7policies 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 L7PolicyPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"l7policies_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a L7PolicyPage struct is empty. +func (r L7PolicyPage) IsEmpty() (bool, error) { + is, err := ExtractL7Policies(r) + return len(is) == 0, err +} + +// ExtractL7Policies accepts a Page struct, specifically a L7PolicyPage struct, +// and extracts the elements into a slice of L7Policy structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractL7Policies(r pagination.Page) ([]L7Policy, error) { + var s struct { + L7Policies []L7Policy `json:"l7policies"` + } + err := (r.(L7PolicyPage)).ExtractInto(&s) + return s.L7Policies, err +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index f64dde2e66..35cd2129cb 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -43,6 +43,19 @@ var ( AdminStateUp: true, Rules: []l7policies.Rule{}, } + L7PolicyToPool = l7policies.L7Policy{ + ID: "964f4ba4-f6cd-405c-bebd-639460af7231", + Name: "redirect-pool", + ListenerID: "be3138a3-5cf7-4513-a4c2-bb137e668bab", + Action: "REDIRECT_TO_POOL", + Position: 1, + Description: "", + TenantID: "c1f7910086964990847dc6c8b128f63c", + RedirectPoolID: "bac433c6-5bea-4311-80da-bd1cd90fbd25", + RedirectURL: "", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + } ) // HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request @@ -65,3 +78,56 @@ func HandleL7PolicyCreationSuccessfully(t *testing.T, response string) { fmt.Fprintf(w, response) }) } + +// L7PoliciesListBody contains the canned body of a l7policy list response. +const L7PoliciesListBody = ` +{ + "l7policies": [ + { + "redirect_pool_id": null, + "description": "", + "admin_state_up": true, + "rules": [], + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "redirect_url": "http://www.example.com", + "action": "REDIRECT_TO_URL", + "position": 1, + "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", + "name": "redirect-example.com" + }, + { + "redirect_pool_id": "bac433c6-5bea-4311-80da-bd1cd90fbd25", + "description": "", + "admin_state_up": true, + "rules": [], + "tenant_id": "c1f7910086964990847dc6c8b128f63c", + "listener_id": "be3138a3-5cf7-4513-a4c2-bb137e668bab", + "action": "REDIRECT_TO_POOL", + "position": 1, + "id": "964f4ba4-f6cd-405c-bebd-639460af7231", + "name": "redirect-pool" + } + ] +} +` + +// HandleL7PolicyListSuccessfully sets up the test server to respond to a l7policy List request. +func HandleL7PolicyListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies", 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, L7PoliciesListBody) + case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": + fmt.Fprintf(w, `{ "l7policies": [] }`) + default: + t.Fatalf("/v2.0/lbaas/l7policies invoked with unexpected marker=[%s]", marker) + } + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index fe0278c197..4dc64a638c 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -40,3 +41,46 @@ func TestRequiredL7PolicyCreateOpts(t *testing.T) { t.Fatalf("Expected error, but got none") } } + +func TestListL7Policies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyListSuccessfully(t) + + pages := 0 + err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := l7policies.ExtractL7Policies(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 l7policies, got %d", len(actual)) + } + th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) + th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllL7Policies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyListSuccessfully(t) + + allPages, err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := l7policies.ExtractL7Policies(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) + th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) +} From 66b52f4d8afd5e54a01a58311dbe37273ab7dd74 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Thu, 5 Apr 2018 17:55:14 +1200 Subject: [PATCH 0272/2296] LBaaS v2 l7 policy support - part 3: get l7policy For #832 Neutron-LBaaS l7 policy get API implementation: https://github.com/openstack/neutron-lbaas/blob/8e8a6c47c7d38fa7b61850ff9ea2cf130718ded3/neutron_lbaas/services/loadbalancer/plugin.py#L971 Octavia l7 policy get API implementation: https://github.com/openstack/octavia/blob/54a4cf00cf304b108013ca4487c138c13f30983d/octavia/api/v2/controllers/l7policy.py#L47 --- .../loadbalancer/v2/loadbalancers_test.go | 10 +++++++++- openstack/loadbalancer/v2/l7policies/doc.go | 14 ++++++++++++-- openstack/loadbalancer/v2/l7policies/requests.go | 6 ++++++ openstack/loadbalancer/v2/l7policies/results.go | 6 ++++++ .../loadbalancer/v2/l7policies/testing/fixtures.go | 11 +++++++++++ .../v2/l7policies/testing/requests_test.go | 14 ++++++++++++++ openstack/loadbalancer/v2/l7policies/urls.go | 4 ++++ 7 files changed, 62 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 866f9b852a..ee6885bb86 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/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" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" @@ -101,11 +102,18 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newListener) // L7 policy - _, err = CreateL7Policy(t, lbClient, listener, lb) + policy, err := CreateL7Policy(t, lbClient, listener, lb) if err != nil { t.Fatalf("Unable to create l7 policy: %v", err) } + newPolicy, err := l7policies.Get(lbClient, policy.ID).Extract() + if err != nil { + t.Fatalf("Unable to get l7 policy: %v", err) + } + + tools.PrintResource(t, newPolicy) + // Pool pool, err := CreatePool(t, lbClient, lb) if err != nil { diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 98e074b876..c1fe28eccf 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -1,23 +1,26 @@ /* Package l7policies provides information and interaction with L7Policies and Rules of the LBaaS v2 extension for the OpenStack Networking service. + Example to Create a L7Policy + createOpts := l7policies.CreateOpts{ Name: "redirect-example.com", ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: l7policies.ActionRedirectToURL, RedirectURL: "http://www.example.com", } - l7policy, err := l7policies.Create(networkClient, createOpts).Extract() + l7policy, err := l7policies.Create(lbClient, createOpts).Extract() if err != nil { panic(err) } Example to List L7Policies + listOpts := l7policies.ListOpts{ ListenerID: "c79a4468-d788-410c-bf79-9a8ef6354852", } - allPages, err := l7policies.List(networkClient, listOpts).AllPages() + allPages, err := l7policies.List(lbClient, listOpts).AllPages() if err != nil { panic(err) } @@ -28,5 +31,12 @@ Example to List L7Policies for _, l7policy := range allL7Policies { fmt.Printf("%+v\n", l7policy) } + +Example to Get a L7Policy + + l7policy, err := l7policies.Get(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d").Extract() + if err != nil { + panic(err) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 48a197b146..95853aac10 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -131,3 +131,9 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { return L7PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Get retrieves a particular l7policy based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index a9f3f1405e..566185e17c 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -129,3 +129,9 @@ func ExtractL7Policies(r pagination.Page) ([]L7Policy, error) { err := (r.(L7PolicyPage)).ExtractInto(&s) return s.L7Policies, err } + +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret the result as a L7Policy. +type GetResult struct { + commonResult +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index 35cd2129cb..8b5a2e022f 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -131,3 +131,14 @@ func HandleL7PolicyListSuccessfully(t *testing.T) { } }) } + +// HandleL7PolicyGetSuccessfully sets up the test server to respond to a l7policy Get request. +func HandleL7PolicyGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, SingleL7PolicyBody) + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index 4dc64a638c..b13a3fb138 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -84,3 +84,17 @@ func TestListAllL7Policies(t *testing.T) { th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) } + +func TestGetL7Policy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := l7policies.Get(client, "8a1412f0-4c32-4257-8b07-af4770b604fd").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, L7PolicyToURL, *actual) +} diff --git a/openstack/loadbalancer/v2/l7policies/urls.go b/openstack/loadbalancer/v2/l7policies/urls.go index fd898e9b06..7a87a187f9 100644 --- a/openstack/loadbalancer/v2/l7policies/urls.go +++ b/openstack/loadbalancer/v2/l7policies/urls.go @@ -10,3 +10,7 @@ const ( 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 04ada7564b59ad6efffa284c370c805c531fcb5d Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Fri, 6 Apr 2018 08:15:17 -0500 Subject: [PATCH 0273/2296] add MoreHeaders field to ServiceClient --- service_client.go | 17 +++++++++++++++++ testing/service_client_test.go | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/service_client.go b/service_client.go index d1a48fea35..145d932a6b 100644 --- a/service_client.go +++ b/service_client.go @@ -28,6 +28,10 @@ type ServiceClient struct { // The microversion of the service to use. Set this to use a particular microversion. Microversion string + + // MoreHeaders allows users (or Gophercloud) to set service-wide headers on requests. Put another way, + // values set in this field will be set on all the HTTP requests the service client sends. + MoreHeaders map[string]string } // ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /. @@ -122,3 +126,16 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion } } + +// Request carries out the HTTP operation for the service client +func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { + if len(client.MoreHeaders) > 0 { + if options == nil { + options = new(RequestOpts) + } + for k, v := range client.MoreHeaders { + options.MoreHeaders[k] = v + } + } + return client.ProviderClient.Request(method, url, options) +} diff --git a/testing/service_client_test.go b/testing/service_client_test.go index 904b303ee9..034fdc1d93 100644 --- a/testing/service_client_test.go +++ b/testing/service_client_test.go @@ -1,6 +1,8 @@ package testing import ( + "fmt" + "net/http" "testing" "github.com/gophercloud/gophercloud" @@ -13,3 +15,20 @@ func TestServiceURL(t *testing.T) { actual := c.ServiceURL("more", "parts", "here") th.CheckEquals(t, expected, actual) } + +func TestMoreHeaders(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + c := new(gophercloud.ServiceClient) + c.MoreHeaders = map[string]string{ + "custom": "header", + } + c.ProviderClient = new(gophercloud.ProviderClient) + resp, err := c.Get(fmt.Sprintf("%s/route", th.Endpoint()), nil, nil) + th.AssertNoErr(t, err) + th.AssertEquals(t, resp.Request.Header.Get("custom"), "header") +} From 4f9015244d4a6d3267a602a2786d758b1bed156d Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Fri, 6 Apr 2018 09:24:46 -0500 Subject: [PATCH 0274/2296] simulate previous token existing in reauth test --- testing/provider_client_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 514147e727..15385beb0b 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -90,6 +90,9 @@ func TestConcurrentReauth(t *testing.T) { wg := new(sync.WaitGroup) reqopts := new(gophercloud.RequestOpts) + reqopts.MoreHeaders = map[string]string{ + "X-Auth-Token": prereauthTok, + } for i := 0; i < numconc; i++ { wg.Add(1) From b4ed2b8f377486b0339992fdf8f6ee22278adae4 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Sat, 7 Apr 2018 07:31:49 +1200 Subject: [PATCH 0275/2296] LBaaS v2 l7 policy support - part 4: delete l7policy (#839) * LBaaS v2 l7 policy support - part 3: get l7policy For #832 Neutron-LBaaS l7 policy get API implementation: https://github.com/openstack/neutron-lbaas/blob/8e8a6c47c7d38fa7b61850ff9ea2cf130718ded3/neutron_lbaas/services/loadbalancer/plugin.py#L971 Octavia l7 policy get API implementation: https://github.com/openstack/octavia/blob/54a4cf00cf304b108013ca4487c138c13f30983d/octavia/api/v2/controllers/l7policy.py#L47 * LBaaS v2 l7 policy support - part 4: delete l7policy For #832 Neutron-LBaaS l7 policy delete API implementation: https://github.com/openstack/neutron-lbaas/blob/8e8a6c47c7d38fa7b61850ff9ea2cf130718ded3/neutron_lbaas/services/loadbalancer/plugin.py#L954 Octavia l7 policy delete API implementation: https://github.com/openstack/octavia/blob/54a4cf00cf304b108013ca4487c138c13f30983d/octavia/api/v2/controllers/l7policy.py#L253 * Add missing parameter --- .../openstack/loadbalancer/v2/loadbalancer.go | 17 +++++++++++++++++ .../loadbalancer/v2/loadbalancers_test.go | 1 + openstack/loadbalancer/v2/l7policies/doc.go | 8 ++++++++ .../loadbalancer/v2/l7policies/requests.go | 6 ++++++ openstack/loadbalancer/v2/l7policies/results.go | 6 ++++++ .../v2/l7policies/testing/fixtures.go | 10 ++++++++++ .../v2/l7policies/testing/requests_test.go | 9 +++++++++ 7 files changed, 57 insertions(+) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index ba1b64fb7e..10bbcc28c1 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -202,6 +202,23 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l return policy, nil } +// DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if +// the l7 policy could not be deleted. This works best when used as a deferred +// function. +func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) { + t.Logf("Attempting to delete l7 policy %s", policyID) + + if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete l7 policy: %v", err) + } + + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + t.Logf("Successfully deleted l7 policy %s", policyID) +} + // DeleteListener will delete a specified listener. A fatal error will occur if // the listener could not be deleted. This works best when used as a deferred // function. diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index ee6885bb86..02d4136a5d 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -106,6 +106,7 @@ func TestLoadbalancersCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create l7 policy: %v", err) } + defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) newPolicy, err := l7policies.Get(lbClient, policy.ID).Extract() if err != nil { diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index c1fe28eccf..9c9a40471b 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -38,5 +38,13 @@ Example to Get a L7Policy if err != nil { panic(err) } + +Example to Delete a L7Policy + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := l7policies.Delete(lbClient, l7policyID).ExtractErr() + if err != nil { + panic(err) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 95853aac10..c565da5f67 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -137,3 +137,9 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } + +// Delete will permanently delete a particular l7policy based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index 566185e17c..8ab9493baf 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -135,3 +135,9 @@ func ExtractL7Policies(r pagination.Page) ([]L7Policy, error) { type GetResult 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/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index 8b5a2e022f..3c9bf6cc74 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -142,3 +142,13 @@ func HandleL7PolicyGetSuccessfully(t *testing.T) { fmt.Fprintf(w, SingleL7PolicyBody) }) } + +// HandleL7PolicyDeletionSuccessfully sets up the test server to respond to a l7policy deletion request. +func HandleL7PolicyDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index b13a3fb138..e350bc8541 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -98,3 +98,12 @@ func TestGetL7Policy(t *testing.T) { th.CheckDeepEquals(t, L7PolicyToURL, *actual) } + +func TestDeleteL7Policy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyDeletionSuccessfully(t) + + res := l7policies.Delete(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") + th.AssertNoErr(t, res.Err) +} From 0ff48d79bb747dcbb1c9899592b31ab82892079d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 6 Apr 2018 21:14:32 -0600 Subject: [PATCH 0276/2296] Add Scope to AuthOptions (#896) * Add Scope to AuthOptions This commit adds a Scope field to the base AuthOptions struct. This allows an explicit scope to be passed to AuthOptions which will enable detailed scoping configurations. * Use new() --- auth_options.go | 75 +++++++++++++----------- openstack/identity/v3/tokens/requests.go | 71 +++------------------- 2 files changed, 49 insertions(+), 97 deletions(-) diff --git a/auth_options.go b/auth_options.go index 4211470020..5e693585c2 100644 --- a/auth_options.go +++ b/auth_options.go @@ -81,6 +81,17 @@ type AuthOptions struct { // TokenID allows users to authenticate (possibly as another user) with an // authentication token ID. TokenID string `json:"-"` + + // Scope determines the scoping of the authentication request. + Scope *AuthScope `json:"-"` +} + +// AuthScope allows a created token to be limited to a specific domain or project. +type AuthScope struct { + ProjectID string + ProjectName string + DomainID string + DomainName string } // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder @@ -263,85 +274,83 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { - - var scope struct { - ProjectID string - ProjectName string - DomainID string - DomainName string - } - - if opts.TenantID != "" { - scope.ProjectID = opts.TenantID - } else { - if opts.TenantName != "" { - scope.ProjectName = opts.TenantName - scope.DomainID = opts.DomainID - scope.DomainName = opts.DomainName + // For backwards compatibility. + // If AuthOptions.Scope was not set, try to determine it. + // This works well for common scenarios. + if opts.Scope == nil { + opts.Scope = new(AuthScope) + if opts.TenantID != "" { + opts.Scope.ProjectID = opts.TenantID + } else { + if opts.TenantName != "" { + opts.Scope.ProjectName = opts.TenantName + opts.Scope.DomainID = opts.DomainID + opts.Scope.DomainName = opts.DomainName + } } } - if scope.ProjectName != "" { + if opts.Scope.ProjectName != "" { // ProjectName provided: either DomainID or DomainName must also be supplied. // ProjectID may not be supplied. - if scope.DomainID == "" && scope.DomainName == "" { + if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { return nil, ErrScopeDomainIDOrDomainName{} } - if scope.ProjectID != "" { + if opts.Scope.ProjectID != "" { return nil, ErrScopeProjectIDOrProjectName{} } - if scope.DomainID != "" { + if opts.Scope.DomainID != "" { // ProjectName + DomainID return map[string]interface{}{ "project": map[string]interface{}{ - "name": &scope.ProjectName, - "domain": map[string]interface{}{"id": &scope.DomainID}, + "name": &opts.Scope.ProjectName, + "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, }, }, nil } - if scope.DomainName != "" { + if opts.Scope.DomainName != "" { // ProjectName + DomainName return map[string]interface{}{ "project": map[string]interface{}{ - "name": &scope.ProjectName, - "domain": map[string]interface{}{"name": &scope.DomainName}, + "name": &opts.Scope.ProjectName, + "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, }, }, nil } - } else if scope.ProjectID != "" { + } else if opts.Scope.ProjectID != "" { // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. - if scope.DomainID != "" { + if opts.Scope.DomainID != "" { return nil, ErrScopeProjectIDAlone{} } - if scope.DomainName != "" { + if opts.Scope.DomainName != "" { return nil, ErrScopeProjectIDAlone{} } // ProjectID return map[string]interface{}{ "project": map[string]interface{}{ - "id": &scope.ProjectID, + "id": &opts.Scope.ProjectID, }, }, nil - } else if scope.DomainID != "" { + } else if opts.Scope.DomainID != "" { // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. - if scope.DomainName != "" { + if opts.Scope.DomainName != "" { return nil, ErrScopeDomainIDOrDomainName{} } // DomainID return map[string]interface{}{ "domain": map[string]interface{}{ - "id": &scope.DomainID, + "id": &opts.Scope.DomainID, }, }, nil - } else if scope.DomainName != "" { + } else if opts.Scope.DomainName != "" { // DomainName return map[string]interface{}{ "domain": map[string]interface{}{ - "name": &scope.DomainName, + "name": &opts.Scope.DomainName, }, }, nil } diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index ca35851e4a..0323e063df 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -72,72 +72,15 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // ToTokenV3CreateMap builds a scope request body from AuthOptions. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { - if opts.Scope.ProjectName != "" { - // ProjectName provided: either DomainID or DomainName must also be supplied. - // ProjectID may not be supplied. - if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { - return nil, gophercloud.ErrScopeDomainIDOrDomainName{} - } - if opts.Scope.ProjectID != "" { - return nil, gophercloud.ErrScopeProjectIDOrProjectName{} - } - - if opts.Scope.DomainID != "" { - // ProjectName + DomainID - return map[string]interface{}{ - "project": map[string]interface{}{ - "name": &opts.Scope.ProjectName, - "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, - }, - }, nil - } - - if opts.Scope.DomainName != "" { - // ProjectName + DomainName - return map[string]interface{}{ - "project": map[string]interface{}{ - "name": &opts.Scope.ProjectName, - "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, - }, - }, nil - } - } else if opts.Scope.ProjectID != "" { - // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. - if opts.Scope.DomainID != "" { - return nil, gophercloud.ErrScopeProjectIDAlone{} - } - if opts.Scope.DomainName != "" { - return nil, gophercloud.ErrScopeProjectIDAlone{} - } - - // ProjectID - return map[string]interface{}{ - "project": map[string]interface{}{ - "id": &opts.Scope.ProjectID, - }, - }, nil - } else if opts.Scope.DomainID != "" { - // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. - if opts.Scope.DomainName != "" { - return nil, gophercloud.ErrScopeDomainIDOrDomainName{} - } - - // DomainID - return map[string]interface{}{ - "domain": map[string]interface{}{ - "id": &opts.Scope.DomainID, - }, - }, nil - } else if opts.Scope.DomainName != "" { - // DomainName - return map[string]interface{}{ - "domain": map[string]interface{}{ - "name": &opts.Scope.DomainName, - }, - }, nil + scope := gophercloud.AuthScope(opts.Scope) + + gophercloudAuthOpts := gophercloud.AuthOptions{ + Scope: &scope, + DomainID: opts.DomainID, + DomainName: opts.DomainName, } - return nil, nil + return gophercloudAuthOpts.ToTokenV3ScopeMap() } func (opts *AuthOptions) CanReauth() bool { From 94190ef1db5858af6bfd9c302c2e295227269cc6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 7 Apr 2018 04:45:00 +0000 Subject: [PATCH 0277/2296] Acc Tests: Update Identity v2 Tests --- acceptance/clients/conditions.go | 8 +++ .../openstack/identity/v2/extension_test.go | 33 +++++----- acceptance/openstack/identity/v2/identity.go | 8 ++- acceptance/openstack/identity/v2/role_test.go | 61 +++++++++---------- .../openstack/identity/v2/tenant_test.go | 44 ++++++------- .../openstack/identity/v2/token_test.go | 39 +++++------- acceptance/openstack/identity/v2/user_test.go | 42 ++++++------- 7 files changed, 122 insertions(+), 113 deletions(-) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 9c62c29c11..a858468669 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -20,6 +20,14 @@ func RequireGuestAgent(t *testing.T) { } } +// RequireIdentityV2 will restrict a test to only be run in +// environments that support the Identity V2 API. +func RequireIdentityV2(t *testing.T) { + if os.Getenv("OS_IDENTITY_API_VERSION") != "2.0" { + t.Skip("this test requires support for the identity v2 API") + } +} + // RequireLiveMigration will restrict a test to only be run in // environments that support live migration. func RequireLiveMigration(t *testing.T) { diff --git a/acceptance/openstack/identity/v2/extension_test.go b/acceptance/openstack/identity/v2/extension_test.go index c6a2bdef41..593d75ca45 100644 --- a/acceptance/openstack/identity/v2/extension_test.go +++ b/acceptance/openstack/identity/v2/extension_test.go @@ -8,39 +8,42 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestExtensionsList(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2Client() - if err != nil { - t.Fatalf("Unable to create an identity client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := extensions.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list extensions: %v", err) - } + th.AssertNoErr(t, err) allExtensions, err := extensions.ExtractExtensions(allPages) - if err != nil { - t.Fatalf("Unable to extract extensions: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, extension := range allExtensions { tools.PrintResource(t, extension) + if extension.Name == "OS-KSCRUD" { + found = true + } } + + th.AssertEquals(t, found, true) } func TestExtensionsGet(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2Client() - if err != nil { - t.Fatalf("Unable to create an identity client: %v", err) - } + th.AssertNoErr(t, err) extension, err := extensions.Get(client, "OS-KSCRUD").Extract() - if err != nil { - t.Fatalf("Unable to get extension OS-KSCRUD: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, extension) } diff --git a/acceptance/openstack/identity/v2/identity.go b/acceptance/openstack/identity/v2/identity.go index 6d0d0f2090..b8e9ee2207 100644 --- a/acceptance/openstack/identity/v2/identity.go +++ b/acceptance/openstack/identity/v2/identity.go @@ -10,6 +10,7 @@ import ( "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" + th "github.com/gophercloud/gophercloud/testhelper" ) // AddUserRole will grant a role to a user in a tenant. An error will be @@ -46,12 +47,13 @@ func CreateTenant(t *testing.T, client *gophercloud.ServiceClient, c *tenants.Cr tenant, err := tenants.Create(client, createOpts).Extract() if err != nil { - t.Logf("Foo") return tenant, err } t.Logf("Successfully created project %s with ID %s", name, tenant.ID) + th.AssertEquals(t, name, tenant.Name) + return tenant, nil } @@ -74,6 +76,8 @@ func CreateUser(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants return user, err } + th.AssertEquals(t, userName, user.Name) + return user, nil } @@ -182,5 +186,7 @@ func UpdateUser(t *testing.T, client *gophercloud.ServiceClient, user *users.Use return newUser, err } + th.AssertEquals(t, userName, newUser.Name) + return newUser, nil } diff --git a/acceptance/openstack/identity/v2/role_test.go b/acceptance/openstack/identity/v2/role_test.go index 83fbd318fa..bc9d26ec34 100644 --- a/acceptance/openstack/identity/v2/role_test.go +++ b/acceptance/openstack/identity/v2/role_test.go @@ -9,69 +9,68 @@ import ( "github.com/gophercloud/gophercloud/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" ) func TestRolesAddToUser(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2AdminClient() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) tenant, err := FindTenant(t, client) - if err != nil { - t.Fatalf("Unable to get a tenant: %v", err) - } + th.AssertNoErr(t, err) role, err := FindRole(t, client) - if err != nil { - t.Fatalf("Unable to get a role: %v", err) - } + th.AssertNoErr(t, err) user, err := CreateUser(t, client, tenant) - if err != nil { - t.Fatalf("Unable to create a user: %v", err) - } + th.AssertNoErr(t, err) defer DeleteUser(t, client, user) err = AddUserRole(t, client, tenant, user, role) - if err != nil { - t.Fatalf("Unable to add role to user: %v", err) - } + th.AssertNoErr(t, err) defer DeleteUserRole(t, client, tenant, user, role) allPages, err := users.ListRoles(client, tenant.ID, user.ID).AllPages() - if err != nil { - t.Fatalf("Unable to obtain roles for user: %v", err) - } + th.AssertNoErr(t, err) allRoles, err := users.ExtractRoles(allPages) - if err != nil { - t.Fatalf("Unable to extract roles: %v", err) - } + th.AssertNoErr(t, err) t.Logf("Roles of user %s:", user.Name) - for _, role := range allRoles { + var found bool + for _, r := range allRoles { tools.PrintResource(t, role) + if r.Name == role.Name { + found = true + } } + + th.AssertEquals(t, found, true) } func TestRolesList(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2AdminClient() - if err != nil { - t.Fatalf("Unable to create an identity client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := roles.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list all roles: %v", err) - } + th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) - if err != nil { - t.Fatalf("Unable to extract roles: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, r := range allRoles { tools.PrintResource(t, r) + if r.Name == "admin" { + found = true + } } + + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/identity/v2/tenant_test.go b/acceptance/openstack/identity/v2/tenant_test.go index 049ec910a1..13a1c08c9e 100644 --- a/acceptance/openstack/identity/v2/tenant_test.go +++ b/acceptance/openstack/identity/v2/tenant_test.go @@ -8,45 +8,47 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestTenantsList(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") - } + th.AssertNoErr(t, err) allPages, err := tenants.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list tenants: %v", err) - } + th.AssertNoErr(t, err) allTenants, err := tenants.ExtractTenants(allPages) - if err != nil { - t.Fatalf("Unable to extract tenants: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, tenant := range allTenants { tools.PrintResource(t, tenant) + + if tenant.Name == "admin" { + found = true + } } + + th.AssertEquals(t, found, true) } func TestTenantsCRUD(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2AdminClient() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") - } + th.AssertNoErr(t, err) tenant, err := CreateTenant(t, client, nil) - if err != nil { - t.Fatalf("Unable to create tenant: %v", err) - } + th.AssertNoErr(t, err) defer DeleteTenant(t, client, tenant.ID) tenant, err = tenants.Get(client, tenant.ID).Extract() - if err != nil { - t.Fatalf("Unable to get tenant: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, tenant) @@ -55,9 +57,9 @@ func TestTenantsCRUD(t *testing.T) { } newTenant, err := tenants.Update(client, tenant.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update tenant: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newTenant) + + th.AssertEquals(t, newTenant.Description, "some tenant") } diff --git a/acceptance/openstack/identity/v2/token_test.go b/acceptance/openstack/identity/v2/token_test.go index 82a317a157..30ebcc2bf0 100644 --- a/acceptance/openstack/identity/v2/token_test.go +++ b/acceptance/openstack/identity/v2/token_test.go @@ -9,31 +9,27 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestTokenAuthenticate(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2UnauthenticatedClient() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) authOptions, err := openstack.AuthOptionsFromEnv() - if err != nil { - t.Fatalf("Unable to obtain authentication options: %v", err) - } + th.AssertNoErr(t, err) result := tokens.Create(client, authOptions) token, err := result.ExtractToken() - if err != nil { - t.Fatalf("Unable to extract token: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, token) catalog, err := result.ExtractServiceCatalog() - if err != nil { - t.Fatalf("Unable to extract service catalog: %v", err) - } + th.AssertNoErr(t, err) for _, entry := range catalog.Entries { tools.PrintResource(t, entry) @@ -41,29 +37,24 @@ func TestTokenAuthenticate(t *testing.T) { } func TestTokenValidate(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) authOptions, err := openstack.AuthOptionsFromEnv() - if err != nil { - t.Fatalf("Unable to obtain authentication options: %v", err) - } + th.AssertNoErr(t, err) result := tokens.Create(client, authOptions) token, err := result.ExtractToken() - if err != nil { - t.Fatalf("Unable to extract token: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, token) getResult := tokens.Get(client, token.ID) user, err := getResult.ExtractUser() - if err != nil { - t.Fatalf("Unable to extract user: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, user) } diff --git a/acceptance/openstack/identity/v2/user_test.go b/acceptance/openstack/identity/v2/user_test.go index faa5bba2f8..caaaaf936a 100644 --- a/acceptance/openstack/identity/v2/user_test.go +++ b/acceptance/openstack/identity/v2/user_test.go @@ -8,52 +8,52 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v2/users" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestUsersList(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2AdminClient() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := users.List(client).AllPages() - if err != nil { - t.Fatalf("Unable to list users: %v", err) - } + th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) - if err != nil { - t.Fatalf("Unable to extract users: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, user := range allUsers { tools.PrintResource(t, user) + + if user.Name == "admin" { + found = true + } } + + th.AssertEquals(t, found, true) } func TestUsersCreateUpdateDelete(t *testing.T) { + clients.RequireIdentityV2(t) + clients.RequireAdmin(t) + client, err := clients.NewIdentityV2AdminClient() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) tenant, err := FindTenant(t, client) - if err != nil { - t.Fatalf("Unable to get a tenant: %v", err) - } + th.AssertNoErr(t, err) user, err := CreateUser(t, client, tenant) - if err != nil { - t.Fatalf("Unable to create a user: %v", err) - } + th.AssertNoErr(t, err) defer DeleteUser(t, client, user) tools.PrintResource(t, user) newUser, err := UpdateUser(t, client, user) - if err != nil { - t.Fatalf("Unable to update user: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newUser) } From 1eccea2fadf173e8022ad57ec0344a8aef0fe139 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 7 Apr 2018 05:31:40 +0000 Subject: [PATCH 0278/2296] Acc Tests: Update Identity v3 Tests --- .../openstack/identity/v3/domains_test.go | 65 ++-- .../openstack/identity/v3/endpoint_test.go | 54 ++- .../openstack/identity/v3/groups_test.go | 27 +- acceptance/openstack/identity/v3/identity.go | 15 + .../openstack/identity/v3/projects_test.go | 88 +++-- .../openstack/identity/v3/regions_test.go | 49 ++- .../openstack/identity/v3/roles_test.go | 269 +++++++-------- .../openstack/identity/v3/service_test.go | 37 +- .../openstack/identity/v3/token_test.go | 33 +- .../openstack/identity/v3/users_test.go | 315 ++++++------------ 10 files changed, 413 insertions(+), 539 deletions(-) diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go index b340bed4bd..7d146464c5 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -8,13 +8,14 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestDomainsList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) var iTrue bool = true listOpts := domains.ListOpts{ @@ -22,50 +23,42 @@ func TestDomainsList(t *testing.T) { } allPages, err := domains.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list domains: %v", err) - } + th.AssertNoErr(t, err) allDomains, err := domains.ExtractDomains(allPages) - if err != nil { - t.Fatalf("Unable to extract domains: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, domain := range allDomains { tools.PrintResource(t, domain) + + if domain.Name == "Default" { + found = true + } } + + th.AssertEquals(t, found, true) } func TestDomainsGet(t *testing.T) { - client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + clients.RequireAdmin(t) - allPages, err := domains.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list domains: %v", err) - } - - allDomains, err := domains.ExtractDomains(allPages) - if err != nil { - t.Fatalf("Unable to extract domains: %v", err) - } + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) - domain := allDomains[0] - p, err := domains.Get(client, domain.ID).Extract() - if err != nil { - t.Fatalf("Unable to get domain: %v", err) - } + p, err := domains.Get(client, "default").Extract() + th.AssertNoErr(t, err) tools.PrintResource(t, p) + + th.AssertEquals(t, p.Name, "Default") } func TestDomainsCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) var iTrue bool = true createOpts := domains.CreateOpts{ @@ -74,13 +67,13 @@ func TestDomainsCRUD(t *testing.T) { } domain, err := CreateDomain(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create domain: %v", err) - } + th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) tools.PrintResource(t, domain) + th.AssertEquals(t, domain.Description, "Testing Domain") + var iFalse bool = false updateOpts := domains.UpdateOpts{ Description: "Staging Test Domain", @@ -88,9 +81,9 @@ func TestDomainsCRUD(t *testing.T) { } newDomain, err := domains.Update(client, domain.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update domain: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newDomain) + + th.AssertEquals(t, newDomain.Description, "Staging Test Domain") } diff --git a/acceptance/openstack/identity/v3/endpoint_test.go b/acceptance/openstack/identity/v3/endpoint_test.go index 68f5a351d4..4bc606b34c 100644 --- a/acceptance/openstack/identity/v3/endpoint_test.go +++ b/acceptance/openstack/identity/v3/endpoint_test.go @@ -3,6 +3,7 @@ package v3 import ( + "strings" "testing" "github.com/gophercloud/gophercloud" @@ -10,34 +11,38 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestEndpointsList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := endpoints.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list endpoints: %v", err) - } + th.AssertNoErr(t, err) allEndpoints, err := endpoints.ExtractEndpoints(allPages) - if err != nil { - t.Fatalf("Unable to extract endpoints: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, endpoint := range allEndpoints { tools.PrintResource(t, endpoint) + + if strings.Contains(endpoint.URL, "/v3") { + found = true + } } + + th.AssertEquals(t, found, true) } func TestEndpointsNavigateCatalog(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) // Discover the service we're interested in. serviceListOpts := services.ListOpts{ @@ -45,18 +50,12 @@ func TestEndpointsNavigateCatalog(t *testing.T) { } allPages, err := services.List(client, serviceListOpts).AllPages() - if err != nil { - t.Fatalf("Unable to lookup compute service: %v", err) - } + th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) - if err != nil { - t.Fatalf("Unable to extract service: %v", err) - } + th.AssertNoErr(t, err) - if len(allServices) != 1 { - t.Fatalf("Expected one service, got %d", len(allServices)) - } + th.AssertEquals(t, len(allServices), 1) computeService := allServices[0] tools.PrintResource(t, computeService) @@ -68,19 +67,12 @@ func TestEndpointsNavigateCatalog(t *testing.T) { } allPages, err = endpoints.List(client, endpointListOpts).AllPages() - if err != nil { - t.Fatalf("Unable to lookup compute endpoint: %v", err) - } + th.AssertNoErr(t, err) allEndpoints, err := endpoints.ExtractEndpoints(allPages) - if err != nil { - t.Fatalf("Unable to extract endpoint: %v", err) - } + th.AssertNoErr(t, err) - if len(allEndpoints) != 1 { - t.Fatalf("Expected one endpoint, got %d", len(allEndpoints)) - } + th.AssertEquals(t, len(allServices), 1) tools.PrintResource(t, allEndpoints[0]) - } diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go index 3e832c2022..3b411bd032 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -8,13 +8,14 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestGroupCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) createOpts := groups.CreateOpts{ Name: "testgroup", @@ -26,9 +27,7 @@ func TestGroupCRUD(t *testing.T) { // Create Group in the default domain group, err := CreateGroup(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create group: %v", err) - } + th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) tools.PrintResource(t, group) @@ -42,9 +41,7 @@ func TestGroupCRUD(t *testing.T) { } newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update group: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) tools.PrintResource(t, newGroup.Extra) @@ -55,14 +52,10 @@ func TestGroupCRUD(t *testing.T) { // List all Groups in default domain allPages, err := groups.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list groups: %v", err) - } + th.AssertNoErr(t, err) allGroups, err := groups.ExtractGroups(allPages) - if err != nil { - t.Fatalf("Unable to extract groups: %v", err) - } + th.AssertNoErr(t, err) for _, g := range allGroups { tools.PrintResource(t, g) @@ -71,9 +64,7 @@ func TestGroupCRUD(t *testing.T) { // Get the recently created group by ID p, err := groups.Get(client, group.ID).Extract() - if err != nil { - t.Fatalf("Unable to get group: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, p) } diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index ae7560dd58..88dee1ec93 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -12,6 +12,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateProject will create a project with a random name. @@ -38,6 +39,8 @@ func CreateProject(t *testing.T, client *gophercloud.ServiceClient, c *projects. t.Logf("Successfully created project %s with ID %s", name, project.ID) + th.AssertEquals(t, project.Name, name) + return project, nil } @@ -65,6 +68,8 @@ func CreateUser(t *testing.T, client *gophercloud.ServiceClient, c *users.Create t.Logf("Successfully created user %s with ID %s", name, user.ID) + th.AssertEquals(t, user.Name, name) + return user, nil } @@ -92,6 +97,8 @@ func CreateGroup(t *testing.T, client *gophercloud.ServiceClient, c *groups.Crea t.Logf("Successfully created group %s with ID %s", name, group.ID) + th.AssertEquals(t, group.Name, name) + return group, nil } @@ -119,6 +126,8 @@ func CreateDomain(t *testing.T, client *gophercloud.ServiceClient, c *domains.Cr t.Logf("Successfully created domain %s with ID %s", name, domain.ID) + th.AssertEquals(t, domain.Name, name) + return domain, nil } @@ -146,6 +155,8 @@ func CreateRole(t *testing.T, client *gophercloud.ServiceClient, c *roles.Create t.Logf("Successfully created role %s with ID %s", name, role.ID) + th.AssertEquals(t, role.Name, name) + return role, nil } @@ -173,6 +184,8 @@ func CreateRegion(t *testing.T, client *gophercloud.ServiceClient, c *regions.Cr t.Logf("Successfully created region %s", id) + th.AssertEquals(t, region.ID, id) + return region, nil } @@ -200,6 +213,8 @@ func CreateService(t *testing.T, client *gophercloud.ServiceClient, c *services. t.Logf("Successfully created service %s", service.ID) + th.AssertEquals(t, service.Extra["name"], name) + return service, nil } diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index 326b4ad034..e6d63c82a5 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -8,13 +8,14 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestProjectsList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) var iTrue bool = true listOpts := projects.ListOpts{ @@ -22,35 +23,34 @@ func TestProjectsList(t *testing.T) { } allPages, err := projects.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list projects: %v", err) - } + th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) - if err != nil { - t.Fatalf("Unable to extract projects: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, project := range allProjects { tools.PrintResource(t, project) + + if project.Name == "admin" { + found = true + } } + + th.AssertEquals(t, found, true) } func TestProjectsGet(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := projects.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list projects: %v", err) - } + th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) - if err != nil { - t.Fatalf("Unable to extract projects: %v", err) - } + th.AssertNoErr(t, err) project := allProjects[0] p, err := projects.Get(client, project.ID).Extract() @@ -59,18 +59,18 @@ func TestProjectsGet(t *testing.T) { } tools.PrintResource(t, p) + + th.AssertEquals(t, project.Name, p.Name) } func TestProjectsCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) - if err != nil { - t.Fatalf("Unable to create project: %v", err) - } + th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) tools.PrintResource(t, project) @@ -81,18 +81,16 @@ func TestProjectsCRUD(t *testing.T) { } updatedProject, err := projects.Update(client, project.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update project: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) } func TestProjectsDomain(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) var iTrue = true createOpts := projects.CreateOpts{ @@ -100,9 +98,7 @@ func TestProjectsDomain(t *testing.T) { } projectDomain, err := CreateProject(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create project: %v", err) - } + th.AssertNoErr(t, err) defer DeleteProject(t, client, projectDomain.ID) tools.PrintResource(t, projectDomain) @@ -112,34 +108,30 @@ func TestProjectsDomain(t *testing.T) { } project, err := CreateProject(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create project: %v", err) - } + th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) tools.PrintResource(t, project) + th.AssertEquals(t, project.DomainID, projectDomain.ID) + var iFalse = false updateOpts := projects.UpdateOpts{ Enabled: &iFalse, } _, err = projects.Update(client, projectDomain.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to disable domain: %v", err) - } + th.AssertNoErr(t, err) } func TestProjectsNested(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) projectMain, err := CreateProject(t, client, nil) - if err != nil { - t.Fatalf("Unable to create project: %v", err) - } + th.AssertNoErr(t, err) defer DeleteProject(t, client, projectMain.ID) tools.PrintResource(t, projectMain) @@ -149,10 +141,10 @@ func TestProjectsNested(t *testing.T) { } project, err := CreateProject(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create project: %v", err) - } + th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) tools.PrintResource(t, project) + + th.AssertEquals(t, project.ParentID, projectMain.ID) } diff --git a/acceptance/openstack/identity/v3/regions_test.go b/acceptance/openstack/identity/v3/regions_test.go index f98c232314..f44a65be8b 100644 --- a/acceptance/openstack/identity/v3/regions_test.go +++ b/acceptance/openstack/identity/v3/regions_test.go @@ -8,27 +8,24 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestRegionsList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) listOpts := regions.ListOpts{ ParentRegionID: "RegionOne", } allPages, err := regions.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list regions: %v", err) - } + th.AssertNoErr(t, err) allRegions, err := regions.ExtractRegions(allPages) - if err != nil { - t.Fatalf("Unable to extract regions: %v", err) - } + th.AssertNoErr(t, err) for _, region := range allRegions { tools.PrintResource(t, region) @@ -36,35 +33,31 @@ func TestRegionsList(t *testing.T) { } func TestRegionsGet(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := regions.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list regions: %v", err) - } + th.AssertNoErr(t, err) allRegions, err := regions.ExtractRegions(allPages) - if err != nil { - t.Fatalf("Unable to extract regions: %v", err) - } + th.AssertNoErr(t, err) region := allRegions[0] p, err := regions.Get(client, region.ID).Extract() - if err != nil { - t.Fatalf("Unable to get region: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, p) + + th.AssertEquals(t, region.ID, p.ID) } func TestRegionsCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) createOpts := regions.CreateOpts{ ID: "testregion", @@ -76,9 +69,7 @@ func TestRegionsCRUD(t *testing.T) { // Create region in the default domain region, err := CreateRegion(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create region: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRegion(t, client, region.ID) tools.PrintResource(t, region) @@ -98,9 +89,7 @@ func TestRegionsCRUD(t *testing.T) { } newRegion, err := regions.Update(client, region.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update region: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newRegion) tools.PrintResource(t, newRegion.Extra) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index be8e73f4e3..0955e3415f 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -10,27 +10,24 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestRolesList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) listOpts := roles.ListOpts{ DomainID: "default", } allPages, err := roles.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list roles: %v", err) - } + th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) - if err != nil { - t.Fatalf("Unable to extract roles: %v", err) - } + th.AssertNoErr(t, err) for _, role := range allRoles { tools.PrintResource(t, role) @@ -38,29 +35,25 @@ func TestRolesList(t *testing.T) { } func TestRolesGet(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) role, err := FindRole(t, client) - if err != nil { - t.Fatalf("Unable to find a role: %v", err) - } + th.AssertNoErr(t, err) p, err := roles.Get(client, role.ID).Extract() - if err != nil { - t.Fatalf("Unable to get role: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, p) } -func TestRoleCRUD(t *testing.T) { +func TestRolesCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ Name: "testrole", @@ -72,9 +65,7 @@ func TestRoleCRUD(t *testing.T) { // Create Role in the default domain role, err := CreateRole(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create role: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRole(t, client, role.ID) tools.PrintResource(t, role) @@ -87,242 +78,256 @@ func TestRoleCRUD(t *testing.T) { } newRole, err := roles.Update(client, role.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update role: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newRole) tools.PrintResource(t, newRole.Extra) + + th.AssertEquals(t, newRole.Extra["description"], "updated test role description") } -func TestRoleAssignToUserOnProject(t *testing.T) { +func TestRolesAssignToUserOnProject(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an indentity client: %v", err) - } + th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) - if err != nil { - t.Fatal("Unable to create a project") - } + th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) role, err := FindRole(t, client) - if err != nil { - t.Fatalf("Unable to get a role: %v", err) - } + th.AssertNoErr(t, err) user, err := CreateUser(t, client, nil) - if err != nil { - t.Fatalf("Unable to create user: %v", err) - } + 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) - err = roles.Assign(client, role.ID, roles.AssignOpts{ + 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, - }).ExtractErr() - if err != nil { - t.Fatalf("Unable to assign a role to a user on a project: %v", err) } - t.Logf("Successfully assigned a role %s to a user %s on a project %s", role.Name, user.Name, project.Name) + 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, }) - allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{ + lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeProjectID: project.ID, UserID: user.ID, - }).AllPages() - if err != nil { - t.Fatalf("Unable to list role assignments: %v", err) } + allPages, err := roles.ListAssignments(client, lao).AllPages() + th.AssertNoErr(t, err) + allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) - if err != nil { - t.Fatalf("Unable to extract role assignments: %v", err) - } + th.AssertNoErr(t, err) t.Logf("Role assignments of user %s on project %s:", user.Name, project.Name) + var found bool for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) + + if roleAssignment.Role.ID == role.ID { + found = true + } } + + th.AssertEquals(t, found, true) } -func TestRoleAssignToUserOnDomain(t *testing.T) { +func TestRolesAssignToUserOnDomain(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an indentity client: %v", err) - } + th.AssertNoErr(t, err) domain, err := CreateDomain(t, client, &domains.CreateOpts{ Enabled: gophercloud.Disabled, }) - if err != nil { - t.Fatal("Unable to create a domain") - } + th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) role, err := FindRole(t, client) - if err != nil { - t.Fatalf("Unable to get a role: %v", err) - } + th.AssertNoErr(t, err) user, err := CreateUser(t, client, nil) - if err != nil { - t.Fatalf("Unable to create user: %v", err) - } + 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) - err = roles.Assign(client, role.ID, roles.AssignOpts{ + t.Logf("Attempting to assign a role %s to a user %s on a domain %s", + role.Name, user.Name, domain.Name) + + assignOpts := roles.AssignOpts{ UserID: user.ID, DomainID: domain.ID, - }).ExtractErr() - if err != nil { - t.Fatalf("Unable to assign a role to a user on a domain: %v", err) } - t.Logf("Successfully assigned a role %s to a user %s on a domain %s", role.Name, user.Name, domain.Name) + + 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 domain %s", + role.Name, user.Name, domain.Name) + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ UserID: user.ID, DomainID: domain.ID, }) - allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{ + lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeDomainID: domain.ID, UserID: user.ID, - }).AllPages() - if err != nil { - t.Fatalf("Unable to list role assignments: %v", err) } + allPages, err := roles.ListAssignments(client, lao).AllPages() + th.AssertNoErr(t, err) + allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) - if err != nil { - t.Fatalf("Unable to extract role assignments: %v", err) - } + th.AssertNoErr(t, err) t.Logf("Role assignments of user %s on domain %s:", user.Name, domain.Name) + var found bool for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) + + if roleAssignment.Role.ID == role.ID { + found = true + } } + + th.AssertEquals(t, found, true) } -func TestRoleAssignToGroupOnDomain(t *testing.T) { +func TestRolesAssignToGroupOnDomain(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an indentity client: %v", err) - } + th.AssertNoErr(t, err) domain, err := CreateDomain(t, client, &domains.CreateOpts{ Enabled: gophercloud.Disabled, }) - if err != nil { - t.Fatal("Unable to create a domain") - } + th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) role, err := FindRole(t, client) - if err != nil { - t.Fatalf("Unable to get a role: %v", err) - } + th.AssertNoErr(t, err) group, err := CreateGroup(t, client, nil) - if err != nil { - t.Fatalf("Unable to create group: %v", err) - } + 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) - err = roles.Assign(client, role.ID, roles.AssignOpts{ + t.Logf("Attempting to assign a role %s to a group %s on a domain %s", + role.Name, group.Name, domain.Name) + + assignOpts := roles.AssignOpts{ GroupID: group.ID, DomainID: domain.ID, - }).ExtractErr() - if err != nil { - t.Fatalf("Unable to assign a role to a group on a domain: %v", err) } - t.Logf("Successfully assigned a role %s to a group %s on a domain %s", role.Name, group.Name, domain.Name) + + err = roles.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) + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ GroupID: group.ID, DomainID: domain.ID, }) - allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{ + lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeDomainID: domain.ID, GroupID: group.ID, - }).AllPages() - if err != nil { - t.Fatalf("Unable to list role assignments: %v", err) } + allPages, err := roles.ListAssignments(client, lao).AllPages() + th.AssertNoErr(t, err) + allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) - if err != nil { - t.Fatalf("Unable to extract role assignments: %v", err) - } + th.AssertNoErr(t, err) t.Logf("Role assignments of group %s on domain %s:", group.Name, domain.Name) + var found bool for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) + + if roleAssignment.Role.ID == role.ID { + found = true + } } + + th.AssertEquals(t, found, true) } -func TestRoleAssignToGroupOnProject(t *testing.T) { +func TestRolesAssignToGroupOnProject(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an indentity client: %v", err) - } + th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) - if err != nil { - t.Fatal("Unable to create a project") - } + th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) role, err := FindRole(t, client) - if err != nil { - t.Fatalf("Unable to get a role: %v", err) - } + th.AssertNoErr(t, err) group, err := CreateGroup(t, client, nil) - if err != nil { - t.Fatalf("Unable to create group: %v", err) - } + 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) - err = roles.Assign(client, role.ID, roles.AssignOpts{ + t.Logf("Attempting to assign a role %s to a group %s on a project %s", + role.Name, group.Name, project.Name) + + assignOpts := roles.AssignOpts{ GroupID: group.ID, ProjectID: project.ID, - }).ExtractErr() - if err != nil { - t.Fatalf("Unable to assign a role to a group on a project: %v", err) } - t.Logf("Successfully assigned a role %s to a group %s on a project %s", role.Name, group.Name, project.Name) + err = roles.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) + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ GroupID: group.ID, ProjectID: project.ID, }) - allPages, err := roles.ListAssignments(client, roles.ListAssignmentsOpts{ + lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeProjectID: project.ID, GroupID: group.ID, - }).AllPages() - if err != nil { - t.Fatalf("Unable to list role assignments: %v", err) } + allPages, err := roles.ListAssignments(client, lao).AllPages() + th.AssertNoErr(t, err) + allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) - if err != nil { - t.Fatalf("Unable to extract role assignments: %v", err) - } + th.AssertNoErr(t, err) t.Logf("Role assignments of group %s on project %s:", group.Name, project.Name) + var found bool for _, roleAssignment := range allRoleAssignments { tools.PrintResource(t, roleAssignment) + + if roleAssignment.Role.ID == role.ID { + found = true + } } + + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/identity/v3/service_test.go b/acceptance/openstack/identity/v3/service_test.go index ed9d3855df..7e072ce3a4 100644 --- a/acceptance/openstack/identity/v3/service_test.go +++ b/acceptance/openstack/identity/v3/service_test.go @@ -8,39 +8,42 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestServicesList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) listOpts := services.ListOpts{ ServiceType: "identity", } allPages, err := services.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list services: %v", err) - } + th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) - if err != nil { - t.Fatalf("Unable to extract services: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, service := range allServices { tools.PrintResource(t, service) + + if service.Type == "identity" { + found = true + } } + th.AssertEquals(t, found, true) } func TestServicesCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) createOpts := services.CreateOpts{ Type: "testing", @@ -51,9 +54,7 @@ func TestServicesCRUD(t *testing.T) { // Create service in the default domain service, err := CreateService(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create service: %v", err) - } + th.AssertNoErr(t, err) defer DeleteService(t, client, service.ID) tools.PrintResource(t, service) @@ -68,10 +69,10 @@ func TestServicesCRUD(t *testing.T) { } newService, err := services.Update(client, service.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update service: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newService) tools.PrintResource(t, newService.Extra) + + th.AssertEquals(t, newService.Extra["description"], "Test Users") } diff --git a/acceptance/openstack/identity/v3/token_test.go b/acceptance/openstack/identity/v3/token_test.go index b426116f4d..ff6e91d49f 100644 --- a/acceptance/openstack/identity/v3/token_test.go +++ b/acceptance/openstack/identity/v3/token_test.go @@ -9,18 +9,17 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestGetToken(t *testing.T) { +func TestTokensGet(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) ao, err := openstack.AuthOptionsFromEnv() - if err != nil { - t.Fatalf("Unable to obtain environment auth options: %v", err) - } + th.AssertNoErr(t, err) authOptions := tokens.AuthOptions{ Username: ao.Username, @@ -29,32 +28,22 @@ func TestGetToken(t *testing.T) { } token, err := tokens.Create(client, &authOptions).Extract() - if err != nil { - t.Fatalf("Unable to get token: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, token) catalog, err := tokens.Get(client, token.ID).ExtractServiceCatalog() - if err != nil { - t.Fatalf("Unable to get catalog from token: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, catalog) user, err := tokens.Get(client, token.ID).ExtractUser() - if err != nil { - t.Fatalf("Unable to get user from token: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, user) roles, err := tokens.Get(client, token.ID).ExtractRoles() - if err != nil { - t.Fatalf("Unable to get roles from token: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, roles) project, err := tokens.Get(client, token.ID).ExtractProject() - if err != nil { - t.Fatalf("Unable to get project from token: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, project) } diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index 1eb15456d6..d51eba18d2 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -10,13 +10,14 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/users" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestUsersList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) var iTrue bool = true listOpts := users.ListOpts{ @@ -24,56 +25,53 @@ func TestUsersList(t *testing.T) { } allPages, err := users.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list users: %v", err) - } + th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) - if err != nil { - t.Fatalf("Unable to extract users: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, user := range allUsers { tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) + + if user.Name == "admin" { + found = true + } } + + th.AssertEquals(t, found, true) } func TestUsersGet(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := users.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list users: %v", err) - } + th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) - if err != nil { - t.Fatalf("Unable to extract users: %v", err) - } + th.AssertNoErr(t, err) user := allUsers[0] p, err := users.Get(client, user.ID).Extract() - if err != nil { - t.Fatalf("Unable to get user: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, p) + + th.AssertEquals(t, user.Name, p.Name) } func TestUserCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) project, err := CreateProject(t, client, nil) - if err != nil { - t.Fatalf("Unable to create project: %v", err) - } + th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) tools.PrintResource(t, project) @@ -95,9 +93,7 @@ func TestUserCRUD(t *testing.T) { } user, err := CreateUser(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create user: %v", err) - } + th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) tools.PrintResource(t, user) @@ -115,39 +111,27 @@ func TestUserCRUD(t *testing.T) { } newUser, err := users.Update(client, user.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update user: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newUser) tools.PrintResource(t, newUser.Extra) + + th.AssertEquals(t, newUser.Extra["disabled_reason"], "DDOS") } func TestUserChangePassword(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) createOpts := users.CreateOpts{ Password: "secretsecret", DomainID: "default", - Options: map[users.Option]interface{}{ - users.IgnorePasswordExpiry: true, - users.MultiFactorAuthRules: []interface{}{ - []string{"password", "totp"}, - []string{"password", "custom-auth-method"}, - }, - }, - Extra: map[string]interface{}{ - "email": "jsmith@example.com", - }, } user, err := CreateUser(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create user: %v", err) - } + th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) tools.PrintResource(t, user) @@ -158,69 +142,22 @@ func TestUserChangePassword(t *testing.T) { Password: "new_secretsecret", } err = users.ChangePassword(client, user.ID, changePasswordOpts).ExtractErr() - if err != nil { - t.Fatalf("Unable to change password for user: %v", err) - } + th.AssertNoErr(t, err) } -func TestUsersListGroups(t *testing.T) { - client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } - allUserPages, err := users.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list users: %v", err) - } +func TestUsersGroups(t *testing.T) { + clients.RequireAdmin(t) - allUsers, err := users.ExtractUsers(allUserPages) - if err != nil { - t.Fatalf("Unable to extract users: %v", err) - } - - user := allUsers[0] - - allGroupPages, err := users.ListGroups(client, user.ID).AllPages() - if err != nil { - t.Fatalf("Unable to list groups: %v", err) - } - - allGroups, err := groups.ExtractGroups(allGroupPages) - if err != nil { - t.Fatalf("Unable to extract groups: %v", err) - } - - for _, group := range allGroups { - tools.PrintResource(t, group) - tools.PrintResource(t, group.Extra) - } -} - -func TestUsersAddToGroup(t *testing.T) { client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) createOpts := users.CreateOpts{ Password: "foobar", DomainID: "default", - Options: map[users.Option]interface{}{ - users.IgnorePasswordExpiry: true, - users.MultiFactorAuthRules: []interface{}{ - []string{"password", "totp"}, - []string{"password", "custom-auth-method"}, - }, - }, - Extra: map[string]interface{}{ - "email": "jsmith@example.com", - }, } user, err := CreateUser(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create user: %v", err) - } + th.AssertNoErr(t, err) defer DeleteUser(t, client, user.ID) tools.PrintResource(t, user) @@ -229,147 +166,117 @@ func TestUsersAddToGroup(t *testing.T) { createGroupOpts := groups.CreateOpts{ Name: "testgroup", DomainID: "default", - Extra: map[string]interface{}{ - "email": "testgroup@example.com", - }, } // Create Group in the default domain group, err := CreateGroup(t, client, &createGroupOpts) - if err != nil { - t.Fatalf("Unable to create group: %v", err) - } + th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) err = users.AddToGroup(client, group.ID, user.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to add user to group: %v", err) - } -} + th.AssertNoErr(t, err) -func TestUsersRemoveFromGroup(t *testing.T) { - client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + allGroupPages, err := users.ListGroups(client, user.ID).AllPages() + th.AssertNoErr(t, err) - createOpts := users.CreateOpts{ - Password: "foobar", - DomainID: "default", - Options: map[users.Option]interface{}{ - users.IgnorePasswordExpiry: true, - users.MultiFactorAuthRules: []interface{}{ - []string{"password", "totp"}, - []string{"password", "custom-auth-method"}, - }, - }, - Extra: map[string]interface{}{ - "email": "jsmith@example.com", - }, - } + allGroups, err := groups.ExtractGroups(allGroupPages) + th.AssertNoErr(t, err) - user, err := CreateUser(t, client, &createOpts) - if err != nil { - t.Fatalf("Unable to create user: %v", err) + var found bool + for _, g := range allGroups { + tools.PrintResource(t, g) + tools.PrintResource(t, g.Extra) + + if g.ID == group.ID { + found = true + } } - defer DeleteUser(t, client, user.ID) - tools.PrintResource(t, user) - tools.PrintResource(t, user.Extra) + th.AssertEquals(t, found, true) - createGroupOpts := groups.CreateOpts{ - Name: "testgroup", - DomainID: "default", - Extra: map[string]interface{}{ - "email": "testgroup@example.com", - }, - } + found = false + allUserPages, err := users.ListInGroup(client, group.ID, nil).AllPages() + th.AssertNoErr(t, err) - // Create Group in the default domain - group, err := CreateGroup(t, client, &createGroupOpts) - if err != nil { - t.Fatalf("Unable to create group: %v", err) - } - defer DeleteGroup(t, client, group.ID) + allUsers, err := users.ExtractUsers(allUserPages) + th.AssertNoErr(t, err) - tools.PrintResource(t, group) - tools.PrintResource(t, group.Extra) + for _, u := range allUsers { + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) - err = users.AddToGroup(client, group.ID, user.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to add user to group: %v", err) + if u.ID == user.ID { + found = true + } } + th.AssertEquals(t, found, true) + err = users.RemoveFromGroup(client, group.ID, user.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to remove user from group: %v", err) + th.AssertNoErr(t, err) + + allGroupPages, err = users.ListGroups(client, user.ID).AllPages() + th.AssertNoErr(t, err) + + allGroups, err = groups.ExtractGroups(allGroupPages) + th.AssertNoErr(t, err) + + found = false + for _, g := range allGroups { + tools.PrintResource(t, g) + tools.PrintResource(t, g.Extra) + + if g.ID == group.ID { + found = true + } } + + th.AssertEquals(t, found, false) + + found = false + allUserPages, err = users.ListInGroup(client, group.ID, nil).AllPages() + th.AssertNoErr(t, err) + + allUsers, err = users.ExtractUsers(allUserPages) + th.AssertNoErr(t, err) + + for _, u := range allUsers { + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) + + if u.ID == user.ID { + found = true + } + } + + th.AssertEquals(t, found, false) + } func TestUsersListProjects(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } + th.AssertNoErr(t, err) + allUserPages, err := users.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list users: %v", err) - } + th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allUserPages) - if err != nil { - t.Fatalf("Unable to extract users: %v", err) - } + th.AssertNoErr(t, err) user := allUsers[0] allProjectPages, err := users.ListProjects(client, user.ID).AllPages() - if err != nil { - t.Fatalf("Unable to list projects: %v", err) - } + th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allProjectPages) - if err != nil { - t.Fatalf("Unable to extract projects: %v", err) - } + th.AssertNoErr(t, err) for _, project := range allProjects { tools.PrintResource(t, project) } } - -func TestUsersListInGroup(t *testing.T) { - client, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v", err) - } - allGroupPages, err := groups.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list groups: %v", err) - } - - allGroups, err := groups.ExtractGroups(allGroupPages) - if err != nil { - t.Fatalf("Unable to extract groups: %v", err) - } - - group := allGroups[0] - - allUserPages, err := users.ListInGroup(client, group.ID, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list users: %v", err) - } - - allUsers, err := users.ExtractUsers(allUserPages) - if err != nil { - t.Fatalf("Unable to extract users: %v", err) - } - - for _, user := range allUsers { - tools.PrintResource(t, user) - tools.PrintResource(t, user.Extra) - } -} From 8873eb140010928dfbfb1e4a7f9cadab94de3ce2 Mon Sep 17 00:00:00 2001 From: Jude C Date: Sat, 7 Apr 2018 14:13:36 -0700 Subject: [PATCH 0279/2296] Messaging Queue Create (#846) * Add NewMessagingV2 client * Add create function for queues * Add unit tests for create queues * Remove queueName from function signature & fix createOpts metadata * Fix unit tests to use Extra and proper function signature * Fix documentation * Update urls to use contants * Fix Extra comments * Fix formatting * Fix doc formatting * Remove Client-ID from create function * Add clientID parameter to NewMessagingV2 --- openstack/client.go | 8 ++ openstack/messaging/v2/queues/doc.go | 24 ++++++ openstack/messaging/v2/queues/requests.go | 80 +++++++++++++++++++ openstack/messaging/v2/queues/results.go | 10 +++ openstack/messaging/v2/queues/testing/doc.go | 2 + .../messaging/v2/queues/testing/fixtures.go | 37 +++++++++ .../v2/queues/testing/requests_test.go | 29 +++++++ openstack/messaging/v2/queues/urls.go | 10 +++ 8 files changed, 200 insertions(+) create mode 100644 openstack/messaging/v2/queues/doc.go create mode 100644 openstack/messaging/v2/queues/requests.go create mode 100644 openstack/messaging/v2/queues/results.go create mode 100644 openstack/messaging/v2/queues/testing/doc.go create mode 100644 openstack/messaging/v2/queues/testing/fixtures.go create mode 100644 openstack/messaging/v2/queues/testing/requests_test.go create mode 100644 openstack/messaging/v2/queues/urls.go diff --git a/openstack/client.go b/openstack/client.go index 85705d2126..b9e187ddaa 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -400,3 +400,11 @@ func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "clustering") } + +// NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging +// service. +func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "messaging") + sc.MoreHeaders = map[string]string{"Client-ID": clientID} + return sc, err +} diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go new file mode 100644 index 0000000000..7f89a01688 --- /dev/null +++ b/openstack/messaging/v2/queues/doc.go @@ -0,0 +1,24 @@ +/* +Package queues provides information and interaction with the queues through +the OpenStack Messaging (Zaqar) service. + +Lists all queues and creates, shows information for updates, deletes, and actions on a queue. + +Example to Create a Queue + + createOpts := queues.CreateOpts{ + QueueName: "My_Queue", + MaxMessagesPostSize: 262143, + DefaultMessageTTL: 3700, + DefaultMessageDelay: 25, + DeadLetterQueueMessageTTL: 3500, + MaxClaimCount: 10, + Extra: map[string]interface{}{"description": "Test queue."}, + } + + err := queues.Create(client, createOpts).ExtractErr() + if err != nil { + panic(err) + } +*/ +package queues diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go new file mode 100644 index 0000000000..02551907f0 --- /dev/null +++ b/openstack/messaging/v2/queues/requests.go @@ -0,0 +1,80 @@ +package queues + +import ( + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToQueueCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies the queue creation parameters. +type CreateOpts struct { + // The name of the queue to create. + QueueName string `json:"queue_name" required:"true"` + + // The target incoming messages will be moved to when a message can’t + // processed successfully after meet the max claim count is met. + DeadLetterQueue string `json:"_dead_letter_queue,omitempty"` + + // The new TTL setting for messages when moved to dead letter queue. + DeadLetterQueueMessagesTTL int `json:"_dead_letter_queue_messages_ttl,omitempty"` + + // The delay of messages defined for a queue. When the messages send to + // the queue, it will be delayed for some times and means it can not be + // claimed until the delay expired. + DefaultMessageDelay int `json:"_default_message_delay,omitempty"` + + // The default TTL of messages defined for a queue, which will effect for + // any messages posted to the queue. + DefaultMessageTTL int `json:"_default_message_ttl" required:"true"` + + // The flavor name which can tell Zaqar which storage pool will be used + // to create the queue. + Flavor string `json:"_flavor,omitempty"` + + // The max number the message can be claimed. + MaxClaimCount int `json:"_max_claim_count,omitempty"` + + // The max post size of messages defined for a queue, which will effect + // for any messages posted to the queue. + MaxMessagesPostSize int `json:"_max_messages_post_size,omitempty"` + + // Extra is free-form extra key/value pairs to describe the queue. + Extra map[string]interface{} `json:"-"` +} + +// ToQueueCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToQueueCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + for key, value := range opts.Extra { + b[key] = value + } + + } + return b, nil +} + +// Create requests the creation of a new queue. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToQueueCreateMap() + if err != nil { + r.Err = err + return + } + + queueName := b["queue_name"].(string) + delete(b, "queue_name") + + _, r.Err = client.Put(createURL(client, queueName), b, r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201, 204}, + }) + return +} diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go new file mode 100644 index 0000000000..3273e82d96 --- /dev/null +++ b/openstack/messaging/v2/queues/results.go @@ -0,0 +1,10 @@ +package queues + +import ( + "github.com/gophercloud/gophercloud" +) + +// CreateResult is the response of a Create operation. +type CreateResult struct { + gophercloud.ErrResult +} diff --git a/openstack/messaging/v2/queues/testing/doc.go b/openstack/messaging/v2/queues/testing/doc.go new file mode 100644 index 0000000000..0937008836 --- /dev/null +++ b/openstack/messaging/v2/queues/testing/doc.go @@ -0,0 +1,2 @@ +// queues unit tests +package testing diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go new file mode 100644 index 0000000000..b118048f93 --- /dev/null +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -0,0 +1,37 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +// QueueName is the name of the queue +var QueueName = "FakeTestQueue" + +// CreateQueueRequest is a sample request to create a queue. +const CreateQueueRequest = ` +{ + "_max_messages_post_size": 262144, + "_default_message_ttl": 3600, + "_default_message_delay": 30, + "_dead_letter_queue": "dead_letter", + "_dead_letter_queue_messages_ttl": 3600, + "_max_claim_count": 10, + "description": "Queue for unit testing." +}` + +// HandleCreateSuccessfully configures the test server to respond to a Create request. +func HandleCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), + 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, CreateQueueRequest) + + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go new file mode 100644 index 0000000000..37c1f899ab --- /dev/null +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -0,0 +1,29 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t) + + createOpts := queues.CreateOpts{ + QueueName: QueueName, + MaxMessagesPostSize: 262144, + DefaultMessageTTL: 3600, + DefaultMessageDelay: 30, + DeadLetterQueue: "dead_letter", + DeadLetterQueueMessagesTTL: 3600, + MaxClaimCount: 10, + Extra: map[string]interface{}{"description": "Queue for unit testing."}, + } + + err := queues.Create(fake.ServiceClient(), createOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go new file mode 100644 index 0000000000..b29fb7a9d7 --- /dev/null +++ b/openstack/messaging/v2/queues/urls.go @@ -0,0 +1,10 @@ +package queues + +import "github.com/gophercloud/gophercloud" + +const ApiVersion = "v2" +const ApiName = "queues" + +func createURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName) +} From 4e791fb6e668d1a194eeb627af5e56d46431b9b4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 8 Apr 2018 02:13:57 +0000 Subject: [PATCH 0280/2296] Test Acc: Updating DNS Tests --- acceptance/clients/conditions.go | 8 ++ acceptance/openstack/dns/v2/dns.go | 15 +++- .../openstack/dns/v2/recordsets_test.go | 83 +++++++------------ acceptance/openstack/dns/v2/zones_test.go | 54 ++++++------ 4 files changed, 77 insertions(+), 83 deletions(-) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 9c62c29c11..1f7043c9ec 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -12,6 +12,14 @@ func RequireAdmin(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/dns.go b/acceptance/openstack/dns/v2/dns.go index 7a0893ff5c..18cd157fc1 100644 --- a/acceptance/openstack/dns/v2/dns.go +++ b/acceptance/openstack/dns/v2/dns.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateRecordSet will create a RecordSet with a random name. An error will @@ -38,6 +39,8 @@ func CreateRecordSet(t *testing.T, client *gophercloud.ServiceClient, zone *zone t.Logf("Created record set: %s", newRS.Name) + th.AssertEquals(t, newRS.Name, zone.Name) + return rs, nil } @@ -70,6 +73,10 @@ func CreateZone(t *testing.T, client *gophercloud.ServiceClient) (*zones.Zone, e } t.Logf("Created Zone: %s", zoneName) + + th.AssertEquals(t, newZone.Name, zoneName) + th.AssertEquals(t, newZone.TTL, 7200) + return newZone, nil } @@ -102,6 +109,10 @@ func CreateSecondaryZone(t *testing.T, client *gophercloud.ServiceClient) (*zone } t.Logf("Created Zone: %s", zoneName) + + th.AssertEquals(t, newZone.Name, zoneName) + th.AssertEquals(t, newZone.Masters[0], "10.0.0.1") + return newZone, nil } @@ -132,7 +143,7 @@ func DeleteZone(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zon // WaitForRecordSetStatus will poll a record set's status until it either matches // the specified status or the status becomes ERROR. func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.RecordSet, status string) error { - return gophercloud.WaitFor(60, func() (bool, error) { + return gophercloud.WaitFor(600, func() (bool, error) { current, err := recordsets.Get(client, rs.ZoneID, rs.ID).Extract() if err != nil { return false, err @@ -149,7 +160,7 @@ func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.Re // WaitForZoneStatus will poll a zone's status until it either matches // the specified status or the status becomes ERROR. func WaitForZoneStatus(client *gophercloud.ServiceClient, zone *zones.Zone, status string) error { - return gophercloud.WaitFor(60, func() (bool, error) { + return gophercloud.WaitFor(600, func() (bool, error) { current, err := zones.Get(client, zone.ID).Extract() if err != nil { return false, err diff --git a/acceptance/openstack/dns/v2/recordsets_test.go b/acceptance/openstack/dns/v2/recordsets_test.go index 17c40bb0ce..d2d862ba55 100644 --- a/acceptance/openstack/dns/v2/recordsets_test.go +++ b/acceptance/openstack/dns/v2/recordsets_test.go @@ -8,85 +8,66 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestRecordSetsListByZone(t *testing.T) { + clients.RequireDNS(t) + client, err := clients.NewDNSV2Client() - if err != nil { - t.Fatalf("Unable to create a DNS client: %v", err) - } + th.AssertNoErr(t, err) zone, err := CreateZone(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteZone(t, client, zone) - var allRecordSets []recordsets.RecordSet allPages, err := recordsets.ListByZone(client, zone.ID, nil).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve recordsets: %v", err) - } + th.AssertNoErr(t, err) - allRecordSets, err = recordsets.ExtractRecordSets(allPages) - if err != nil { - t.Fatalf("Unable to extract recordsets: %v", err) - } + allRecordSets, err := recordsets.ExtractRecordSets(allPages) + th.AssertNoErr(t, err) + var found bool for _, recordset := range allRecordSets { tools.PrintResource(t, &recordset) - } -} -func TestRecordSetsListByZoneLimited(t *testing.T) { - client, err := clients.NewDNSV2Client() - if err != nil { - t.Fatalf("Unable to create a DNS client: %v", err) + if recordset.ZoneID == zone.ID { + found = true + } } - zone, err := CreateZone(t, client) - if err != nil { - t.Fatal(err) - } - defer DeleteZone(t, client, zone) + th.AssertEquals(t, found, true) - var allRecordSets []recordsets.RecordSet listOpts := recordsets.ListOpts{ Limit: 1, } - allPages, err := recordsets.ListByZone(client, zone.ID, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve recordsets: %v", err) - } - allRecordSets, err = recordsets.ExtractRecordSets(allPages) - if err != nil { - t.Fatalf("Unable to extract recordsets: %v", err) - } - - for _, recordset := range allRecordSets { - tools.PrintResource(t, &recordset) - } + err = recordsets.ListByZone(client, zone.ID, listOpts).EachPage( + func(page pagination.Page) (bool, error) { + rr, err := recordsets.ExtractRecordSets(page) + th.AssertNoErr(t, err) + th.AssertEquals(t, len(rr), 1) + return true, nil + }, + ) + th.AssertNoErr(t, err) } -func TestRecordSetCRUD(t *testing.T) { +func TestRecordSetsCRUD(t *testing.T) { + clients.RequireDNS(t) + client, err := clients.NewDNSV2Client() - if err != nil { - t.Fatalf("Unable to create a DNS client: %v", err) - } + th.AssertNoErr(t, err) zone, err := CreateZone(t, client) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteZone(t, client, zone) tools.PrintResource(t, &zone) rs, err := CreateRecordSet(t, client, zone) - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) defer DeleteRecordSet(t, client, rs) tools.PrintResource(t, &rs) @@ -97,9 +78,9 @@ func TestRecordSetCRUD(t *testing.T) { } newRS, err := recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, &newRS) + + th.AssertEquals(t, newRS.Description, "New description") } diff --git a/acceptance/openstack/dns/v2/zones_test.go b/acceptance/openstack/dns/v2/zones_test.go index 8e71687898..263e9fea0a 100644 --- a/acceptance/openstack/dns/v2/zones_test.go +++ b/acceptance/openstack/dns/v2/zones_test.go @@ -8,43 +8,37 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestZonesList(t *testing.T) { +func TestZonesCRUD(t *testing.T) { + clients.RequireDNS(t) + client, err := clients.NewDNSV2Client() - if err != nil { - t.Fatalf("Unable to create a DNS client: %v", err) - } + th.AssertNoErr(t, err) - var allZones []zones.Zone - allPages, err := zones.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve zones: %v", err) - } + zone, err := CreateZone(t, client) + th.AssertNoErr(t, err) + defer DeleteZone(t, client, zone) - allZones, err = zones.ExtractZones(allPages) - if err != nil { - t.Fatalf("Unable to extract zones: %v", err) - } + tools.PrintResource(t, &zone) - for _, zone := range allZones { - tools.PrintResource(t, &zone) - } -} + allPages, err := zones.List(client, nil).AllPages() + th.AssertNoErr(t, err) -func TestZonesCRUD(t *testing.T) { - client, err := clients.NewDNSV2Client() - if err != nil { - t.Fatalf("Unable to create a DNS client: %v", err) - } + allZones, err := zones.ExtractZones(allPages) + th.AssertNoErr(t, err) - zone, err := CreateZone(t, client) - if err != nil { - t.Fatal(err) + var found bool + for _, z := range allZones { + tools.PrintResource(t, &z) + + if zone.Name == z.Name { + found = true + } } - defer DeleteZone(t, client, zone) - tools.PrintResource(t, &zone) + th.AssertEquals(t, found, true) updateOpts := zones.UpdateOpts{ Description: "New description", @@ -52,9 +46,9 @@ func TestZonesCRUD(t *testing.T) { } newZone, err := zones.Update(client, zone.ID, updateOpts).Extract() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, &newZone) + + th.AssertEquals(t, newZone.Description, "New description") } From 71a0b3389c74fd3bb489511e4ab3a33fa7b11bd1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 8 Apr 2018 03:10:40 +0000 Subject: [PATCH 0281/2296] Image Service v2: Fix unmarshaling of empty images --- openstack/imageservice/v2/images/results.go | 2 +- openstack/imageservice/v2/images/testing/fixtures.go | 2 ++ .../imageservice/v2/images/testing/requests_test.go | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index cd819ec9c8..e256068844 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -102,7 +102,7 @@ func (r *Image) UnmarshalJSON(b []byte) error { switch t := s.SizeBytes.(type) { case nil: - return nil + r.SizeBytes = 0 case float32: r.SizeBytes = int64(t) case float64: diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index 29757d203b..ddfe0438ed 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -212,6 +212,7 @@ func HandleImageCreationSuccessfullyNulls(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestJSONRequest(t, r, `{ "id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", + "architecture": "x86_64", "name": "Ubuntu 12.10", "tags": [ "ubuntu", @@ -222,6 +223,7 @@ func HandleImageCreationSuccessfullyNulls(t *testing.T) { w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ + "architecture": "x86_64", "status": "queued", "name": "Ubuntu 12.10", "protected": false, diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index 487247b110..ac71db86d6 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -132,6 +132,9 @@ func TestCreateImageNulls(t *testing.T) { ID: id, Name: name, Tags: []string{"ubuntu", "quantal"}, + Properties: map[string]string{ + "architecture": "x86_64", + }, }).Extract() th.AssertNoErr(t, err) @@ -145,6 +148,10 @@ func TestCreateImageNulls(t *testing.T) { createdDate := actualImage.CreatedAt lastUpdate := actualImage.UpdatedAt schema := "/v2/schemas/image" + properties := map[string]interface{}{ + "architecture": "x86_64", + } + sizeBytes := int64(0) expectedImage := images.Image{ ID: "e7db3b45-8db7-47ad-8109-3fb55c2c24fd", @@ -166,6 +173,8 @@ func TestCreateImageNulls(t *testing.T) { CreatedAt: createdDate, UpdatedAt: lastUpdate, Schema: schema, + Properties: properties, + SizeBytes: sizeBytes, } th.AssertDeepEquals(t, &expectedImage, actualImage) From 38714be079fc3bd9b3b3cab12c2d1f074a09c1e3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 8 Apr 2018 03:10:59 +0000 Subject: [PATCH 0282/2296] Test Acc: Updating Image Service Tests --- .../openstack/imageservice/v2/images_test.go | 93 +++++++------------ .../openstack/imageservice/v2/imageservice.go | 13 ++- 2 files changed, 43 insertions(+), 63 deletions(-) diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/acceptance/openstack/imageservice/v2/images_test.go index 04926109f6..9c6cf32a06 100644 --- a/acceptance/openstack/imageservice/v2/images_test.go +++ b/acceptance/openstack/imageservice/v2/images_test.go @@ -10,13 +10,12 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestImagesListEachPage(t *testing.T) { client, err := clients.NewImageServiceV2Client() - if err != nil { - t.Fatalf("Unable to create an image service client: %v", err) - } + th.AssertNoErr(t, err) listOpts := images.ListOpts{ Limit: 1, @@ -40,69 +39,54 @@ func TestImagesListEachPage(t *testing.T) { func TestImagesListAllPages(t *testing.T) { client, err := clients.NewImageServiceV2Client() - if err != nil { - t.Fatalf("Unable to create an image service client: %v", err) - } + th.AssertNoErr(t, err) - listOpts := images.ListOpts{ - Limit: 1, - } + image, err := CreateEmptyImage(t, client) + th.AssertNoErr(t, err) + defer DeleteImage(t, client, image) + + listOpts := images.ListOpts{} allPages, err := images.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve all images: %v", err) - } + th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) - if err != nil { - t.Fatalf("Unable to extract images: %v", err) - } - - for _, image := range allImages { - tools.PrintResource(t, image) - tools.PrintResource(t, image.Properties) - } -} + th.AssertNoErr(t, err) -func TestImagesCreateDestroyEmptyImage(t *testing.T) { - client, err := clients.NewImageServiceV2Client() - if err != nil { - t.Fatalf("Unable to create an image service client: %v", err) - } + var found bool + for _, i := range allImages { + tools.PrintResource(t, i) + tools.PrintResource(t, i.Properties) - image, err := CreateEmptyImage(t, client) - if err != nil { - t.Fatalf("Unable to create empty image: %v", err) + if i.Name == image.Name { + found = true + } } - defer DeleteImage(t, client, image) - - tools.PrintResource(t, image) + th.AssertEquals(t, found, true) } func TestImagesListByDate(t *testing.T) { client, err := clients.NewImageServiceV2Client() - if err != nil { - t.Fatalf("Unable to create an image service client: %v", err) - } + th.AssertNoErr(t, err) date := time.Date(2014, 1, 1, 1, 1, 1, 0, time.UTC) listOpts := images.ListOpts{ Limit: 1, - CreatedAt: &images.ImageDateQuery{ + CreatedAtQuery: &images.ImageDateQuery{ Date: date, Filter: images.FilterGTE, }, } allPages, err := images.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve all images: %v", err) - } + th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) - if err != nil { - t.Fatalf("Unable to extract images: %v", err) + th.AssertNoErr(t, err) + + if len(allImages) == 0 { + t.Fatalf("Query resulted in no results") } for _, image := range allImages { @@ -113,21 +97,17 @@ func TestImagesListByDate(t *testing.T) { date = time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) listOpts = images.ListOpts{ Limit: 1, - CreatedAt: &images.ImageDateQuery{ + CreatedAtQuery: &images.ImageDateQuery{ Date: date, Filter: images.FilterGTE, }, } allPages, err = images.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve all images: %v", err) - } + th.AssertNoErr(t, err) allImages, err = images.ExtractImages(allPages) - if err != nil { - t.Fatalf("Unable to extract images: %v", err) - } + th.AssertNoErr(t, err) if len(allImages) > 0 { t.Fatalf("Expected 0 images, got %d", len(allImages)) @@ -136,15 +116,10 @@ func TestImagesListByDate(t *testing.T) { func TestImagesFilter(t *testing.T) { client, err := clients.NewImageServiceV2Client() - if err != nil { - t.Fatalf("Unable to create an image service client: %v", err) - } + th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) - if err != nil { - t.Fatalf("Unable to create empty image: %v", err) - } - + th.AssertNoErr(t, err) defer DeleteImage(t, client, image) listOpts := images.ListOpts{ @@ -154,14 +129,10 @@ func TestImagesFilter(t *testing.T) { } allPages, err := images.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve all images: %v", err) - } + th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) - if err != nil { - t.Fatalf("Unable to extract images: %v", err) - } + th.AssertNoErr(t, err) if len(allImages) == 0 { t.Fatalf("Query resulted in no results") diff --git a/acceptance/openstack/imageservice/v2/imageservice.go b/acceptance/openstack/imageservice/v2/imageservice.go index 18af05ab3b..54035b0fcc 100644 --- a/acceptance/openstack/imageservice/v2/imageservice.go +++ b/acceptance/openstack/imageservice/v2/imageservice.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateEmptyImage will create an image, but with no actual image data. @@ -39,8 +40,16 @@ func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images. return image, err } - t.Logf("Created image %s: %#v", name, image) - return image, nil + newImage, err := images.Get(client, image.ID).Extract() + if err != nil { + return image, err + } + + t.Logf("Created image %s: %#v", name, newImage) + + th.CheckEquals(t, newImage.Name, name) + th.CheckEquals(t, newImage.Properties["architecture"], "x86_64") + return newImage, nil } // DeleteImage deletes an image. From e09b6c86177fba6c539e66dc198752d26c759c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Tue, 10 Apr 2018 09:22:52 +0800 Subject: [PATCH 0283/2296] Identity V3: list role assignments for user on project (#885) * Identity V3 roles: add function ListAssignmentsForUserOnProject() * fix error * fix JSON output * create function ListAssignmentsOnResource() * fix unit testing * fix unit testing * fix acceptance testing --- .../openstack/identity/v3/roles_test.go | 238 ++++++++++++++++++ openstack/identity/v3/roles/doc.go | 23 ++ openstack/identity/v3/roles/requests.go | 56 +++++ .../identity/v3/roles/testing/fixtures.go | 56 +++++ .../v3/roles/testing/requests_test.go | 70 ++++++ openstack/identity/v3/roles/urls.go | 4 + 6 files changed, 447 insertions(+) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 0955e3415f..f442439086 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -86,6 +86,244 @@ func TestRolesCRUD(t *testing.T) { th.AssertEquals(t, newRole.Extra["description"], "updated test role description") } +func TestRoleListAssignmentForUserOnProject(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) + + role, err := FindRole(t, client) + th.AssertNoErr(t, err) + + 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, + }) + + listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ + UserID: user.ID, + ProjectID: project.ID, + } + allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() + th.AssertNoErr(t, err) + + allRoles, err := roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + + t.Logf("Role assignments of user %s on project %s:", user.Name, project.Name) + var found bool + for _, _role := range allRoles { + tools.PrintResource(t, _role) + + if _role.ID == role.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} + +func TestRoleListAssignmentForUserOnDomain(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) + + role, err := FindRole(t, client) + th.AssertNoErr(t, err) + + 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 := roles.AssignOpts{ + UserID: user.ID, + DomainID: domain.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 domain %s", + role.Name, user.Name, domain.Name) + + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ + UserID: user.ID, + DomainID: domain.ID, + }) + + listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ + UserID: user.ID, + DomainID: domain.ID, + } + allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() + th.AssertNoErr(t, err) + + allRoles, err := roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + + t.Logf("Role assignments of user %s on domain %s:", user.Name, domain.Name) + var found bool + for _, _role := range allRoles { + tools.PrintResource(t, _role) + + if _role.ID == role.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} + +func TestRoleListAssignmentForGroupOnProject(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) + + role, err := FindRole(t, client) + th.AssertNoErr(t, err) + + group, err := CreateGroup(t, client, nil) + 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 := roles.AssignOpts{ + GroupID: group.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 group %s on a project %s", + role.Name, group.Name, project.Name) + + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ + GroupID: group.ID, + ProjectID: project.ID, + }) + + listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ + GroupID: group.ID, + ProjectID: project.ID, + } + allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() + th.AssertNoErr(t, err) + + allRoles, err := roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + + t.Logf("Role assignments of group %s on project %s:", group.Name, project.Name) + var found bool + for _, _role := range allRoles { + tools.PrintResource(t, _role) + + if _role.ID == role.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} + +func TestRoleListAssignmentForGroupOnDomain(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) + + role, err := FindRole(t, client) + th.AssertNoErr(t, err) + + group, err := CreateGroup(t, client, nil) + 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 := roles.AssignOpts{ + GroupID: group.ID, + DomainID: domain.ID, + } + + err = roles.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) + + defer UnassignRole(t, client, role.ID, &roles.UnassignOpts{ + GroupID: group.ID, + DomainID: domain.ID, + }) + + listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ + GroupID: group.ID, + DomainID: domain.ID, + } + allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() + th.AssertNoErr(t, err) + + allRoles, err := roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + + t.Logf("Role assignments of group %s on domain %s:", group.Name, domain.Name) + var found bool + for _, _role := range allRoles { + tools.PrintResource(t, _role) + + if _role.ID == role.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} + func TestRolesAssignToUserOnProject(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 2886a872d8..f0e4d045e9 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -79,6 +79,29 @@ Example to List Role Assignments fmt.Printf("%+v\n", role) } +Example to List Role Assignments for a User on a Project + + projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" + userID := "9df1a02f5eb2416a9781e8b0c022d3ae" + listAssignmentsOnResourceOpts := roles.ListAssignmentsOnResourceOpts{ + UserID: userID, + ProjectID: projectID, + } + + allPages, err := roles.ListAssignmentsOnResource(identityClient, listAssignmentsOnResourceOpts).AllPages() + if err != nil { + panic(err) + } + + allRoles, err := roles.ExtractRoles(allPages) + if err != nil { + panic(err) + } + + for _, role := range allRoles { + fmt.Printf("%+v\n", role) + } + Example to Assign a Role to a User in a Project projectID := "a99e9b4e620e4db09a2dfb6e42a01e66" diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 7908baa0e4..86c68c66ab 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -201,6 +201,26 @@ func ListAssignments(client *gophercloud.ServiceClient, opts ListAssignmentsOpts }) } +// ListAssignmentsOnResourceOpts provides options to list role assignments +// for a user/group on a project/domain +type ListAssignmentsOnResourceOpts struct { + // UserID is the ID of a user to assign a 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 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 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 role on + // Note: exactly one of ProjectID or DomainID must be provided + DomainID string `xor:"ProjectID"` +} + // AssignOpts provides options to assign a role type AssignOpts struct { // UserID is the ID of a user to assign a role @@ -239,6 +259,42 @@ type UnassignOpts struct { DomainID string `xor:"ProjectID"` } +// ListAssignmentsOnResource is the operation responsible for listing role +// assignments for a user/group on a project/domain. +func ListAssignmentsOnResource(client *gophercloud.ServiceClient, opts ListAssignmentsOnResourceOpts) pagination.Pager { + // Check xor conditions + _, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return pagination.Pager{Err: err} + } + + // 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" + } + + url := listAssignmentsOnResourceURL(client, targetType, targetID, actorType, actorID) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return RolePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + // Assign is the operation responsible for assigning a role // to a user/group on a project/domain. func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) (r AssignmentResult) { diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index fa73b11ee0..fc56220bc8 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -141,6 +141,29 @@ const ListAssignmentOutput = ` } ` +// ListAssignmentsOnResourceOutput provides a result of ListAssignmentsOnResource request. +const ListAssignmentsOnResourceOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/projects/9e5a15/users/b964a9/roles" + }, + "roles": [ + { + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/roles/9fe1d3" + }, + "name": "support", + "extra": { + "description": "read-only support role" + } + } + ] +} +` + // FirstRole is the first role in the List request. var FirstRole = roles.Role{ DomainID: "default", @@ -331,3 +354,36 @@ func HandleListRoleAssignmentsSuccessfully(t *testing.T) { fmt.Fprintf(w, ListAssignmentOutput) }) } + +// RoleOnResource is the role in the ListAssignmentsOnResource request. +var RoleOnResource = roles.Role{ + ID: "9fe1d3", + Links: map[string]interface{}{ + "self": "https://example.com/identity/v3/roles/9fe1d3", + }, + Name: "support", + Extra: map[string]interface{}{ + "description": "read-only support role", + }, +} + +// ExpectedRolesOnResourceSlice is the slice of roles expected to be returned +// from ListAssignmentsOnResourceOutput. +var ExpectedRolesOnResourceSlice = []roles.Role{RoleOnResource} + +func HandleListAssignmentsOnResourceSuccessfully(t *testing.T) { + fn := 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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListAssignmentsOnResourceOutput) + } + + th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles", fn) + th.Mux.HandleFunc("/projects/{project_id}/groups/{group_id}/roles", fn) + th.Mux.HandleFunc("/domains/{domain_id}/users/{user_id}/roles", fn) + th.Mux.HandleFunc("/domains/{domain_id}/groups/{group_id}/roles", fn) +} diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index c683197bfa..ea8b19a7bb 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -115,6 +115,76 @@ func TestListAssignmentsSinglePage(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListAssignmentsOnResource(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAssignmentsOnResourceSuccessfully(t) + + count := 0 + err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ + UserID: "{user_id}", + ProjectID: "{project_id}", + }).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := roles.ExtractRoles(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRolesOnResourceSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) + + count = 0 + err = roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ + UserID: "{user_id}", + DomainID: "{domain_id}", + }).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := roles.ExtractRoles(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRolesOnResourceSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) + + count = 0 + err = roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ + GroupID: "{group_id}", + ProjectID: "{project_id}", + }).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := roles.ExtractRoles(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRolesOnResourceSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) + + count = 0 + err = roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ + GroupID: "{group_id}", + DomainID: "{domain_id}", + }).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := roles.ExtractRoles(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRolesOnResourceSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + func TestAssign(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index 38d592dca6..2b82011424 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -30,6 +30,10 @@ func listAssignmentsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("role_assignments") } +func listAssignmentsOnResourceURL(client *gophercloud.ServiceClient, targetType, targetID, actorType, actorID string) string { + return client.ServiceURL(targetType, targetID, actorType, actorID, rolePath) +} + func assignURL(client *gophercloud.ServiceClient, targetType, targetID, actorType, actorID, roleID string) string { return client.ServiceURL(targetType, targetID, actorType, actorID, rolePath, roleID) } From ff8db8da249eb162535238257b472e32570aee27 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Tue, 10 Apr 2018 13:28:21 +1200 Subject: [PATCH 0284/2296] LBaaS v2 l7 policy support - part 5: update l7policy (#905) * LBaaS v2 l7 policy support - part 5: update l7policy For #832 Neutron-LBaaS l7 policy update API implementation: https://github.com/openstack/neutron-lbaas/blob/ac720b2a49720fb99e4189f93d5a83cfb295ccb3/neutron_lbaas/services/loadbalancer/plugin.py#L931 Octavia l7 policy update API implementation: https://github.com/openstack/octavia/blob/06bf5c58d5845f684fcaf933605ed112586eefc3/octavia/api/v2/controllers/l7policy.py#L204 * Allow empty string for name and description fields --- .../loadbalancer/v2/loadbalancers_test.go | 13 +++++ openstack/loadbalancer/v2/l7policies/doc.go | 11 ++++ .../loadbalancer/v2/l7policies/requests.go | 44 ++++++++++++++++ .../loadbalancer/v2/l7policies/results.go | 6 +++ .../v2/l7policies/testing/fixtures.go | 51 +++++++++++++++++++ .../v2/l7policies/testing/requests_test.go | 20 ++++++++ 6 files changed, 145 insertions(+) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 02d4136a5d..080f215ef0 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -108,6 +108,19 @@ func TestLoadbalancersCRUD(t *testing.T) { } defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) + newDescription := "New l7 policy description" + updateL7policyOpts := l7policies.UpdateOpts{ + Description: &newDescription, + } + _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() + if err != nil { + t.Fatalf("Unable to update l7 policy") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + newPolicy, err := l7policies.Get(lbClient, policy.ID).Extract() if err != nil { t.Fatalf("Unable to get l7 policy: %v", err) diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 9c9a40471b..7165dd522a 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -46,5 +46,16 @@ Example to Delete a L7Policy if err != nil { panic(err) } + +Example to Update a L7Policy + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + updateOpts := l7policies.UpdateOpts{ + Name: "new-name", + } + l7policy, err := l7policies.Update(lbClient, l7policyID, updateOpts).Extract() + if err != nil { + panic(err) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index c565da5f67..b64c14b863 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -143,3 +143,47 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToL7PolicyUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts is the common options struct used in this package's Update +// operation. +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. + Action Action `json:"action,omitempty"` + + // The position of this policy on the listener. + Position int32 `json:"position,omitempty"` + + // A human-readable description for the resource, empty string is allowed. + Description *string `json:"description,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"` + + // Requests matching this policy will be redirected to this URL. + // Only valid if action is REDIRECT_TO_URL. + RedirectURL string `json:"redirect_url,omitempty"` +} + +// ToL7PolicyUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "l7policy") +} + +// Update allows l7policy to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, _ := opts.ToL7PolicyUpdateMap() + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index 8ab9493baf..f4907a3f59 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -141,3 +141,9 @@ type GetResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret the result as a L7Policy. +type UpdateResult struct { + commonResult +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index 3c9bf6cc74..193e728097 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -56,6 +56,19 @@ var ( AdminStateUp: true, Rules: []l7policies.Rule{}, } + L7PolicyUpdated = l7policies.L7Policy{ + ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", + Name: "NewL7PolicyName", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: "REDIRECT_TO_URL", + Position: 1, + Description: "Redirect requests to example.com", + TenantID: "e3cd678b11784734bc366148aa37580e", + RedirectPoolID: "", + RedirectURL: "http://www.new-example.com", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + } ) // HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request @@ -112,6 +125,25 @@ const L7PoliciesListBody = ` } ` +// PostUpdateL7PolicyBody is the canned response body of a Update request on an existing l7policy. +const PostUpdateL7PolicyBody = ` +{ + "l7policy": { + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "description": "Redirect requests to example.com", + "admin_state_up": true, + "redirect_pool_id": null, + "redirect_url": "http://www.new-example.com", + "action": "REDIRECT_TO_URL", + "position": 1, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", + "name": "NewL7PolicyName", + "rules": [] + } +} +` + // HandleL7PolicyListSuccessfully sets up the test server to respond to a l7policy List request. func HandleL7PolicyListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies", func(w http.ResponseWriter, r *http.Request) { @@ -152,3 +184,22 @@ func HandleL7PolicyDeletionSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// HandleL7PolicyUpdateSuccessfully sets up the test server to respond to a l7policy Update request. +func HandleL7PolicyUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, `{ + "l7policy": { + "name": "NewL7PolicyName", + "action": "REDIRECT_TO_URL", + "redirect_url": "http://www.new-example.com" + } + }`) + + fmt.Fprintf(w, PostUpdateL7PolicyBody) + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index e350bc8541..f9c47d665c 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -107,3 +107,23 @@ func TestDeleteL7Policy(t *testing.T) { res := l7policies.Delete(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") th.AssertNoErr(t, res.Err) } + +func TestUpdateL7Policy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyUpdateSuccessfully(t) + + client := fake.ServiceClient() + newName := "NewL7PolicyName" + actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", + l7policies.UpdateOpts{ + Name: &newName, + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.new-example.com", + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, L7PolicyUpdated, *actual) +} From 72c94d28d050ef138da464bbf94f73e057bd1d50 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 9 Apr 2018 19:29:45 -0600 Subject: [PATCH 0285/2296] LBaaS v2: Fix doc example for updating l7policies --- openstack/loadbalancer/v2/l7policies/doc.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 7165dd522a..95ac5e17f0 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -50,8 +50,9 @@ Example to Delete a L7Policy Example to Update a L7Policy l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + name := "new-name" updateOpts := l7policies.UpdateOpts{ - Name: "new-name", + Name: &name, } l7policy, err := l7policies.Update(lbClient, l7policyID, updateOpts).Extract() if err != nil { From aaee2285afde23ac71e3b8ac0d20c19d0056e88d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Apr 2018 02:17:54 +0000 Subject: [PATCH 0286/2296] Acc Tests: Fix Server Delete There might be occasions when the API will return an actual DELETED status. This is a short window of time, but it could happen. --- acceptance/openstack/compute/v2/compute.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index cdcbf0bb9e..570378ddfd 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -775,7 +775,9 @@ func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *serve t.Fatalf("Error deleting server %s: %s", server.ID, err) } - t.Fatalf("Could not delete server: %s", server.ID) + // If we reach this point, the API returned an actual DELETED status + // which is a very short window of time, but happens occasionally. + t.Logf("Deleted server: %s", server.ID) } // DeleteServerGroup will delete a server group. A fatal error will occur if From 760543a1f14f91a19a9ae133321e4f6857de509c Mon Sep 17 00:00:00 2001 From: Jude C Date: Mon, 9 Apr 2018 20:15:27 -0700 Subject: [PATCH 0287/2296] Messaging Queue List (#848) * Add create function for queues * Add list function for queues * Add unit tests for list queues * Fix documentation * Update commonURL to use constants * Fix QueueDetails to properly handle misc key/values and update unit tests * Remove Client-ID from list function * Implement NextPageURL for list queues --- openstack/messaging/v2/queues/doc.go | 20 ++++ openstack/messaging/v2/queues/requests.go | 43 +++++++ openstack/messaging/v2/queues/results.go | 111 ++++++++++++++++++ .../messaging/v2/queues/testing/fixtures.go | 108 +++++++++++++++++ .../v2/queues/testing/requests_test.go | 25 ++++ openstack/messaging/v2/queues/urls.go | 27 ++++- 6 files changed, 333 insertions(+), 1 deletion(-) diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index 7f89a01688..2bf91491bf 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -4,6 +4,26 @@ the OpenStack Messaging (Zaqar) service. Lists all queues and creates, shows information for updates, deletes, and actions on a queue. +Example to List Queues + + listOpts := queues.ListOpts{ + Limit: 10, + } + + pager := queues.List(client, listOpts) + 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) + } + + return true, nil + }) + Example to Create a Queue createOpts := queues.CreateOpts{ diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 02551907f0..05fe91a779 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -2,8 +2,51 @@ package queues import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToQueueListQuery() (string, error) +} + +// ListOpts params to be used with List +type ListOpts struct { + // Limit instructs List to refrain from sending excessively large lists of queues + Limit int `q:"limit,omitempty"` + + // Marker and Limit control paging. Marker instructs List where to start listing from. + Marker string `q:"marker,omitempty"` + + // Specifies if showing the detailed information when querying queues + Detailed bool `q:"detailed,omitempty"` +} + +// ToQueueListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToQueueListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of queues. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToQueueListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + pager := pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return QueuePage{pagination.LinkedPageBase{PageResult: r}} + + }) + return pager +} + // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 3273e82d96..55927eb31a 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -1,10 +1,121 @@ package queues import ( + "encoding/json" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/internal" + "github.com/gophercloud/gophercloud/pagination" ) +// QueuePage contains a single page of all queues from a List operation. +type QueuePage struct { + pagination.LinkedPageBase +} + // CreateResult is the response of a Create operation. type CreateResult struct { gophercloud.ErrResult } + +// Queue represents a messaging queue. +type Queue struct { + Href string `json:"href"` + Methods []string `json:"methods"` + Name string `json:"name"` + Paths []string `json:"paths"` + ResourceTypes []string `json:"resource_types"` + Metadata QueueDetails `json:"metadata"` +} + +// QueueDetails represents the metadata of a queue. +type QueueDetails struct { + // The queue the message will be moved to when the message can’t + // be processed successfully after the max claim count is met. + DeadLetterQueue string `json:"_dead_letter_queue"` + + // The TTL setting for messages when moved to dead letter queue. + DeadLetterQueueMessageTTL int `json:"_dead_letter_queue_messages_ttl"` + + // The delay of messages defined for the queue. + DefaultMessageDelay int `json:"_default_message_delay"` + + // The default TTL of messages defined for the queue. + DefaultMessageTTL int `json:"_default_message_ttl"` + + // Extra is a collection of miscellaneous key/values. + Extra map[string]interface{} `json:"-"` + + // The max number the message can be claimed from the queue. + MaxClaimCount int `json:"_max_claim_count"` + + // The max post size of messages defined for the queue. + MaxMessagesPostSize int `json:"_max_messages_post_size"` + + // The flavor defined for the queue. + Flavor string `json:"flavor"` +} + +// ExtractQueues interprets the results of a single page from a +// List() call, producing a map of queues. +func ExtractQueues(r pagination.Page) ([]Queue, error) { + var s struct { + Queues []Queue `json:"queues"` + } + err := (r.(QueuePage)).ExtractInto(&s) + return s.Queues, err +} + +// IsEmpty determines if a QueuesPage contains any results. +func (r QueuePage) IsEmpty() (bool, error) { + s, err := ExtractQueues(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r QueuePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + + next, err := gophercloud.ExtractNextURL(s.Links) + if err != nil { + return "", err + } + return nextPageURL(r.URL.String(), next) +} + +func (r *QueueDetails) UnmarshalJSON(b []byte) error { + type tmp QueueDetails + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = QueueDetails(s.tmp) + + // Collect other fields and bundle them into Extra + // but only if a field titled "extra" wasn't sent. + if s.Extra != nil { + r.Extra = s.Extra + } else { + var result interface{} + err := json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = internal.RemainingKeys(QueueDetails{}, resultMap) + } + } + + return err +} diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index b118048f93..58cbe60a93 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -5,6 +5,7 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -24,6 +25,113 @@ const CreateQueueRequest = ` "description": "Queue for unit testing." }` +// ListQueuesResponse1 is a sample response to a List queues. +const ListQueuesResponse1 = ` +{ + "queues":[ + { + "href":"/v2/queues/london", + "name":"london", + "metadata":{ + "_dead_letter_queue":"fake_queue", + "_dead_letter_queue_messages_ttl":3500, + "_default_message_delay":25, + "_default_message_ttl":3700, + "_max_claim_count":10, + "_max_messages_post_size":262143, + "description":"Test queue." + } + } + ], + "links":[ + { + "href":"/v2/queues?marker=london", + "rel":"next" + } + ] +}` + +// ListQueuesResponse2 is a sample response to a List queues. +const ListQueuesResponse2 = ` +{ + "queues":[ + { + "href":"/v2/queues/beijing", + "name":"beijing", + "metadata":{ + "_dead_letter_queue":"fake_queue", + "_dead_letter_queue_messages_ttl":3500, + "_default_message_delay":25, + "_default_message_ttl":3700, + "_max_claim_count":10, + "_max_messages_post_size":262143, + "description":"Test queue." + } + } + ], + "links":[ + { + "href":"/v2/queues?marker=beijing", + "rel":"next" + } + ] +}` + +// FirstQueue is the first result in a List. +var FirstQueue = queues.Queue{ + Href: "/v2/queues/london", + Name: "london", + Metadata: queues.QueueDetails{ + DeadLetterQueue: "fake_queue", + DeadLetterQueueMessageTTL: 3500, + DefaultMessageDelay: 25, + DefaultMessageTTL: 3700, + MaxClaimCount: 10, + MaxMessagesPostSize: 262143, + Extra: map[string]interface{}{"description": "Test queue."}, + }, +} + +// SecondQueue is the second result in a List. +var SecondQueue = queues.Queue{ + Href: "/v2/queues/beijing", + Name: "beijing", + Metadata: queues.QueueDetails{ + DeadLetterQueue: "fake_queue", + DeadLetterQueueMessageTTL: 3500, + DefaultMessageDelay: 25, + DefaultMessageTTL: 3700, + MaxClaimCount: 10, + MaxMessagesPostSize: 262143, + Extra: map[string]interface{}{"description": "Test queue."}, + }, +} + +// ExpectedQueueSlice is the expected result in a List. +var ExpectedQueueSlice = [][]queues.Queue{{FirstQueue}, {SecondQueue}} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2/queues", + 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") + next := r.RequestURI + + switch next { + case "/v2/queues?limit=1": + fmt.Fprintf(w, ListQueuesResponse1) + case "/v2/queues?marker=london": + fmt.Fprint(w, ListQueuesResponse2) + case "/v2/queues?marker=beijing": + fmt.Fprint(w, `{ "queues": [] }`) + } + + }) +} + // HandleCreateSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index 37c1f899ab..e85a6462e5 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -4,10 +4,35 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + listOpts := queues.ListOpts{ + Limit: 1, + } + + count := 0 + err := queues.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { + actual, err := queues.ExtractQueues(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedQueueSlice[count], actual) + count++ + + return true, nil + }) + th.AssertNoErr(t, err) + + th.CheckEquals(t, 2, count) +} + func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index b29fb7a9d7..8be0d8a7a1 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -1,10 +1,35 @@ package queues -import "github.com/gophercloud/gophercloud" +import ( + "net/url" + + "github.com/gophercloud/gophercloud" +) const ApiVersion = "v2" const ApiName = "queues" +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(ApiVersion, ApiName) +} + func createURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName) } + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} + +// builds next page full url based on current url +func nextPageURL(currentURL string, next string) (string, error) { + base, err := url.Parse(currentURL) + if err != nil { + return "", err + } + rel, err := url.Parse(next) + if err != nil { + return "", err + } + return base.ResolveReference(rel).String(), nil +} From 8bbbdbd42a0315234ae3cc2e3136a5c50794edad Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Tue, 10 Apr 2018 18:52:49 -0700 Subject: [PATCH 0288/2296] Clustering Policy Create Implementation (#895) * Clustering Policy Create Implementation * Fix review comments * Created Spec type * Changed Policy unmarshal to handle both time with and without Z * Added custom unmarshal to handle Version as either string or float * Change UpdatedAt value in fixtures to test different code path * fix variable name * Fix review comments - Removed custom Version type and unmarshal function - Added Version assignment to policy unmarshal with custom logic * added custom unmarshal for spec --- .../openstack/clustering/v1/policies_test.go | 34 ++++++ openstack/clustering/v1/policies/doc.go | 26 +++++ openstack/clustering/v1/policies/requests.go | 29 +++++ openstack/clustering/v1/policies/results.go | 81 ++++++++++++- .../v1/policies/testing/fixtures.go | 108 +++++++++++++++--- .../v1/policies/testing/requests_test.go | 19 +++ openstack/clustering/v1/policies/urls.go | 4 + 7 files changed, 278 insertions(+), 23 deletions(-) diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go index b3343c3ac3..6fd9cffa35 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -34,3 +34,37 @@ func TestPolicyList(t *testing.T) { } } } + +func TestPolicyCreate(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + opts := policies.CreateOpts{ + Name: "new_policy2", + Spec: policies.Spec{ + Description: "new policy description", + Properties: map[string]interface{}{ + "destroy_after_deletion": true, + "grace_period": 60, + "reduce_desired_capacity": false, + "criteria": "OLDEST_FIRST", + }, + Type: "senlin.policy.deletion", + Version: "1.0", + }, + } + + createdPolicy, err := policies.Create(client, opts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, createdPolicy) + + if createdPolicy.CreatedAt.IsZero() { + t.Fatalf("CreatedAt value should not be zero") + } + t.Log("Created at: " + createdPolicy.CreatedAt.String()) + + if !createdPolicy.UpdatedAt.IsZero() { + t.Log("Updated at: " + createdPolicy.UpdatedAt.String()) + } +} diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index 053c380551..90830c8dcf 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -22,5 +22,31 @@ Example to List Policies fmt.Printf("%+v\n", policy) } + +Example to Create a policy + + opts := policies.CreateOpts{ + Name: "new_policy", + Spec: policies.Spec{ + Description: "new policy description", + Properties: map[string]interface{}{ + "hooks": map[string]interface{}{ + "type": "zaqar", + "params": map[string]interface{}{ + "queue": "my_zaqar_queue", + }, + "timeout": 10, + }, + }, + Type: "senlin.policy.deletion", + Version: "1.1", + }, + } + + createdPolicy, err := policies.Create(client, opts).Extract() + if err != nil { + panic(err) + } + */ package policies diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index 4f50a0ce3c..cd6f6c9759 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -54,3 +54,32 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return p }) } + +// CreateOpts params +type CreateOpts struct { + Name string `json:"name"` + Spec Spec `json:"spec"` +} + +// ToPolicyCreateMap formats a CreateOpts into a body map. +func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return map[string]interface{}{"policy": b}, nil +} + +// Create makes a request against the API to create a policy +func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := opts.ToPolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index 044e58d99e..2e82deb53c 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -2,6 +2,8 @@ package policies import ( "encoding/json" + "fmt" + "strconv" "time" "github.com/gophercloud/gophercloud" @@ -16,12 +18,19 @@ type Policy struct { ID string `json:"id"` Name string `json:"name"` Project string `json:"project"` - Spec map[string]interface{} `json:"spec"` + Spec Spec `json:"spec"` Type string `json:"type"` UpdatedAt time.Time `json:"-"` User string `json:"user"` } +type Spec struct { + Description string `json:"description"` + Properties map[string]interface{} `json:"properties"` + Type string `json:"type"` + Version string `json:"version"` +} + // ExtractPolicies interprets a page of results as a slice of Policy. func ExtractPolicies(r pagination.Page) ([]Policy, error) { var s struct { @@ -54,12 +63,14 @@ func (r PolicyPage) LastMarker() (string, error) { return policies[len(policies)-1].ID, nil } +const RFC3339WithZ = "2006-01-02T15:04:05Z" + func (r *Policy) UnmarshalJSON(b []byte) error { type tmp Policy var s struct { tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at,omitempty"` - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` } err := json.Unmarshal(b, &s) if err != nil { @@ -67,8 +78,68 @@ func (r *Policy) UnmarshalJSON(b []byte) error { } *r = Policy(s.tmp) - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) + if s.CreatedAt != "" { + r.CreatedAt, err = time.Parse(gophercloud.RFC3339MilliNoZ, s.CreatedAt) + if err != nil { + r.CreatedAt, err = time.Parse(RFC3339WithZ, s.CreatedAt) + if err != nil { + return err + } + } + } + + if s.UpdatedAt != "" { + r.UpdatedAt, err = time.Parse(gophercloud.RFC3339MilliNoZ, s.UpdatedAt) + if err != nil { + r.UpdatedAt, err = time.Parse(RFC3339WithZ, s.UpdatedAt) + if err != nil { + return err + } + } + } + + return nil +} + +func (r *Spec) UnmarshalJSON(b []byte) error { + type tmp Spec + var s struct { + tmp + Version interface{} `json:"version"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Spec(s.tmp) + + switch t := s.Version.(type) { + case float64: + if t == 1 { + r.Version = fmt.Sprintf("%.1f", t) + } else { + r.Version = strconv.FormatFloat(t, 'f', -1, 64) + } + case string: + r.Version = t + } return nil } + +type policyResult struct { + gophercloud.Result +} + +func (r policyResult) Extract() (*Policy, error) { + var s struct { + Policy *Policy `json:"policy"` + } + err := r.ExtractInto(&s) + + return s.Policy, err +} + +type CreateResult struct { + policyResult +} diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go index 30db27b88d..6f2bad66f9 100644 --- a/openstack/clustering/v1/policies/testing/fixtures.go +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -33,7 +33,7 @@ const PolicyListBody1 = ` "version": 1 }, "type": "senlin.policy.deletion-1.0", - "updated_at": null, + "updated_at": "2018-04-02T00:19:12Z", "user": "fe43e41739154b72818565e0d2580819" } ] @@ -59,20 +59,53 @@ const PolicyListBody2 = ` "reduce_desired_capacity": false }, "type": "senlin.policy.deletion", - "version": 1 + "version": "1.0" }, "type": "senlin.policy.deletion-1.0", - "updated_at": null, + "updated_at": "2018-04-02T23:15:11.000000", "user": "fe43e41739154b72818565e0d2580819" } ] } ` +const PolicyCreateBody = ` +{ + "policy": { + "created_at": "2018-04-04T00:18:36Z", + "data": {}, + "domain": null, + "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + "name": "delpol4", + "project": "018cd0909fb44cd5bc9b7a3cd664920e", + "spec": { + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": { + "hooks": { + "params": { + "queue": "zaqar_queue_name" + }, + "timeout": 180, + "type": "zaqar" + } + }, + "type": "senlin.policy.deletion", + "version": 1.1 + }, + "type": "senlin.policy.deletion-1.1", + "updated_at": null, + "user": "fe43e41739154b72818565e0d2580819" + } +} +` + var ( - ExpectedPolicyCreatedAt1, _ = time.Parse(time.RFC3339, "2018-04-02T21:43:30.000000Z") - ExpectedPolicyCreatedAt2, _ = time.Parse(time.RFC3339, "2018-04-02T22:29:36.000000Z") - ZeroTime, _ = time.Parse(time.RFC3339, "1-01-01T00:00:00.000000Z") + ExpectedPolicyCreatedAt1, _ = time.Parse(time.RFC3339, "2018-04-02T21:43:30.000000Z") + ExpectedPolicyUpdatedAt1, _ = time.Parse(time.RFC3339, "2018-04-02T00:19:12.000000Z") + ExpectedPolicyCreatedAt2, _ = time.Parse(time.RFC3339, "2018-04-02T22:29:36.000000Z") + ExpectedPolicyUpdatedAt2, _ = time.Parse(time.RFC3339, "2018-04-02T23:15:11.000000Z") + ExpectedCreatePolicyCreatedAt, _ = time.Parse(time.RFC3339, "2018-04-04T00:18:36.000000Z") + ZeroTime, _ = time.Parse(time.RFC3339, "1-01-01T00:00:00.000000Z") ExpectedPolicies = [][]policies.Policy{ { @@ -84,20 +117,20 @@ var ( Name: "delpol", Project: "018cd0909fb44cd5bc9b7a3cd664920e", - Spec: map[string]interface{}{ - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": map[string]interface{}{ + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ "criteria": "OLDEST_FIRST", "destroy_after_deletion": true, "grace_period": float64(60), "reduce_desired_capacity": false, }, - "type": "senlin.policy.deletion", - "version": float64(1), + Type: "senlin.policy.deletion", + Version: "1.0", }, Type: "senlin.policy.deletion-1.0", User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: ZeroTime, + UpdatedAt: ExpectedPolicyUpdatedAt1, }, }, { @@ -109,23 +142,50 @@ var ( Name: "delpol2", Project: "018cd0909fb44cd5bc9b7a3cd664920e", - Spec: map[string]interface{}{ - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": map[string]interface{}{ + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ "criteria": "OLDEST_FIRST", "destroy_after_deletion": true, "grace_period": float64(60), "reduce_desired_capacity": false, }, - "type": "senlin.policy.deletion", - "version": float64(1), + Type: "senlin.policy.deletion", + Version: "1.0", }, Type: "senlin.policy.deletion-1.0", User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: ZeroTime, + UpdatedAt: ExpectedPolicyUpdatedAt2, }, }, } + + ExpectedCreatePolicy = policies.Policy{ + CreatedAt: ExpectedCreatePolicyCreatedAt, + Data: map[string]interface{}{}, + Domain: "", + ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + Name: "delpol4", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "hooks": map[string]interface{}{ + "params": map[string]interface{}{ + "queue": "zaqar_queue_name", + }, + "timeout": float64(180), + "type": "zaqar", + }, + }, + Type: "senlin.policy.deletion", + Version: "1.1", + }, + Type: "senlin.policy.deletion-1.1", + User: "fe43e41739154b72818565e0d2580819", + UpdatedAt: ZeroTime, + } ) func HandlePolicyList(t *testing.T) { @@ -150,3 +210,15 @@ func HandlePolicyList(t *testing.T) { } }) } + +func HandlePolicyCreate(t *testing.T) { + th.Mux.HandleFunc("/v1/policies", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, PolicyCreateBody) + }) +} diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go index 0f110a52f0..17fef6ce76 100644 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -39,3 +39,22 @@ func TestListPolicies(t *testing.T) { t.Errorf("Expected 2 pages, got %d", count) } } + +func TestCreatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandlePolicyCreate(t) + + expected := ExpectedCreatePolicy + + opts := policies.CreateOpts{ + Name: ExpectedCreatePolicy.Name, + Spec: ExpectedCreatePolicy.Spec, + } + + actual, err := policies.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} diff --git a/openstack/clustering/v1/policies/urls.go b/openstack/clustering/v1/policies/urls.go index 9e2349ea8b..e0d0c8de7f 100644 --- a/openstack/clustering/v1/policies/urls.go +++ b/openstack/clustering/v1/policies/urls.go @@ -10,3 +10,7 @@ const ( func policyListURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } + +func policyCreateURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} From 92596e817fe740fd67363d63ca4e1e9993c71dcd Mon Sep 17 00:00:00 2001 From: Jude C Date: Tue, 10 Apr 2018 18:56:20 -0700 Subject: [PATCH 0289/2296] Messaging Queue Update (#849) * Add create function for queues * Fix documentation * Add list function for queues * Add unit tests for list queues * Fix QueueDetails to properly handle misc key/values and update unit tests * Add update function for queues * Add unit tests for update queue * Fix formatting * Update updateURL to use constants * Fix UpdateQueueBody to use UpdateOp instead of a string * Remove Client-ID from update function * Fix unit tests to use proper path in update * Remove Client-ID referneces in docs & fix unit tests * Fix Update to use BatchUpdate --- openstack/messaging/v2/queues/doc.go | 23 +++++++- openstack/messaging/v2/queues/requests.go | 52 +++++++++++++++++++ openstack/messaging/v2/queues/results.go | 17 ++++++ .../messaging/v2/queues/testing/fixtures.go | 30 ++++++++++- .../v2/queues/testing/requests_test.go | 21 ++++++++ openstack/messaging/v2/queues/urls.go | 4 ++ 6 files changed, 145 insertions(+), 2 deletions(-) diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index 2bf91491bf..0b214d46cb 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -11,7 +11,8 @@ Example to List Queues } pager := queues.List(client, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + + err = pager.EachPage(func(page pagination.Page) (bool, error) { queues, err := queues.ExtractQueues(page) if err != nil { panic(err) @@ -40,5 +41,25 @@ Example to Create a Queue if err != nil { panic(err) } + +Example to Update a Queue + + updateOpts := queues.BatchUpdateOpts{ + queues.UpdateOpts{ + Op: "replace", + Path: "/metadata/_max_claim_count", + Value: 15, + }, + queues.UpdateOpts{ + Op: "replace", + Path: "/metadata/description", + Value: "Updated description test queue.", + }, + } + + updateResult, err := queues.Update(client, queueName, updateOpts).Extract() + if err != nil { + panic(err) + } */ package queues diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 05fe91a779..5ed51d0061 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -121,3 +121,55 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// update request. +type UpdateOptsBuilder interface { + ToQueueUpdateMap() ([]map[string]interface{}, error) +} + +// UpdateOpts is an array of UpdateQueueBody. +type BatchUpdateOpts []UpdateOpts + +// UpdateOpts is the struct responsible for updating a property of a queue. +type UpdateOpts struct { + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value interface{} `json:"value" required:"true"` +} + +type UpdateOp string + +const ( + ReplaceOp UpdateOp = "replace" + AddOp UpdateOp = "add" + RemoveOp UpdateOp = "remove" +) + +// ToQueueUpdateMap constructs a request body from UpdateOpts. +func (opts BatchUpdateOpts) ToQueueUpdateMap() ([]map[string]interface{}, error) { + queuesUpdates := make([]map[string]interface{}, len(opts)) + for i, queue := range opts { + queueMap, err := queue.ToMap() + if err != nil { + return nil, err + } + queuesUpdates[i] = queueMap + } + return queuesUpdates, nil +} + +// ToMap constructs a request body from UpdateOpts. +func (opts UpdateOpts) ToMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update Updates the specified queue. +func Update(client *gophercloud.ServiceClient, queueName string, opts UpdateOptsBuilder) (r UpdateResult) { + _, r.Err = client.Patch(updateURL(client, queueName), opts, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 204}, + MoreHeaders: map[string]string{ + "Content-Type": "application/openstack-messaging-v2.0-json-patch"}, + }) + return +} diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 55927eb31a..f31608e6b5 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -8,6 +8,11 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + // QueuePage contains a single page of all queues from a List operation. type QueuePage struct { pagination.LinkedPageBase @@ -18,6 +23,11 @@ type CreateResult struct { gophercloud.ErrResult } +// UpdateResult is the response of a Update operation. +type UpdateResult struct { + commonResult +} + // Queue represents a messaging queue. type Queue struct { Href string `json:"href"` @@ -56,6 +66,13 @@ type QueueDetails struct { Flavor string `json:"flavor"` } +// Extract interprets any commonResult as a Queue. +func (r commonResult) Extract() (QueueDetails, error) { + var s QueueDetails + err := r.ExtractInto(&s) + return s, err +} + // ExtractQueues interprets the results of a single page from a // List() call, producing a map of queues. func ExtractQueues(r pagination.Page) ([]Queue, error) { diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index 58cbe60a93..1010fa2f07 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -77,6 +77,22 @@ const ListQueuesResponse2 = ` ] }` +// UpdateQueueRequest is a sample request to update a queue. +const UpdateQueueRequest = ` +[ + { + "op": "replace", + "path": "/metadata/description", + "value": "Update queue description" + } +]` + +// UpdateQueueResponse is a sample response to a update queue. +const UpdateQueueResponse = ` +{ + "description": "Update queue description" +}` + // FirstQueue is the first result in a List. var FirstQueue = queues.Queue{ Href: "/v2/queues/london", @@ -128,7 +144,6 @@ func HandleListSuccessfully(t *testing.T) { case "/v2/queues?marker=beijing": fmt.Fprint(w, `{ "queues": [] }`) } - }) } @@ -143,3 +158,16 @@ func HandleCreateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// HandleUpdateSuccessfully configures the test server to respond to an Update request. +func HandleUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, UpdateQueueRequest) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, UpdateQueueResponse) + }) +} diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index e85a6462e5..408ad9a4f3 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -52,3 +52,24 @@ func TestCreate(t *testing.T) { err := queues.Create(fake.ServiceClient(), createOpts).ExtractErr() th.AssertNoErr(t, err) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateSuccessfully(t) + + updateOpts := queues.BatchUpdateOpts{ + queues.UpdateOpts{ + Op: queues.ReplaceOp, + Path: "/metadata/description", + Value: "Update queue description", + }, + } + updatedQueueResult := queues.QueueDetails{ + Extra: map[string]interface{}{"description": "Update queue description"}, + } + + actual, err := queues.Update(fake.ServiceClient(), QueueName, updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, updatedQueueResult, actual) +} diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index 8be0d8a7a1..38238133f1 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -33,3 +33,7 @@ func nextPageURL(currentURL string, next string) (string, error) { } return base.ResolveReference(rel).String(), nil } + +func updateURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName) +} From dadd6cfbab32d7b31a94108835e04517925ff369 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Mon, 26 Mar 2018 22:52:38 -0700 Subject: [PATCH 0290/2296] Add get function for queues --- openstack/messaging/v2/queues/doc.go | 7 +++++ openstack/messaging/v2/queues/requests.go | 8 ++++++ openstack/messaging/v2/queues/results.go | 5 ++++ .../messaging/v2/queues/testing/fixtures.go | 27 +++++++++++++++++++ .../v2/queues/testing/requests_test.go | 10 +++++++ openstack/messaging/v2/queues/urls.go | 4 +++ 6 files changed, 61 insertions(+) diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index 0b214d46cb..8c83b13bed 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -61,5 +61,12 @@ Example to Update a Queue if err != nil { panic(err) } + +Example to Get a Queue + + queue, err := queues.Get(client, queueName).Extract() + if err != nil { + panic(err) + } */ package queues diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 5ed51d0061..fd98530d1b 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -173,3 +173,11 @@ func Update(client *gophercloud.ServiceClient, queueName string, opts UpdateOpts }) return } + +// Get requests details on a single queue, by name. +func Get(client *gophercloud.ServiceClient, queueName string) (r GetResult) { + _, r.Err = client.Get(getURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index f31608e6b5..84ac48acc2 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -28,6 +28,11 @@ type UpdateResult struct { commonResult } +// GetResult is the response of a Get operation. +type GetResult struct { + commonResult +} + // Queue represents a messaging queue. type Queue struct { Href string `json:"href"` diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index 1010fa2f07..bbea7b90fc 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -93,6 +93,14 @@ const UpdateQueueResponse = ` "description": "Update queue description" }` +// GetQueueResponse is a sample response to a get queue. +const GetQueueResponse = ` +{ + "_max_messages_post_size": 262144, + "_default_message_ttl": 3600, + "description": "Queue used for unit testing." +}` + // FirstQueue is the first result in a List. var FirstQueue = queues.Queue{ Href: "/v2/queues/london", @@ -126,6 +134,13 @@ var SecondQueue = queues.Queue{ // ExpectedQueueSlice is the expected result in a List. var ExpectedQueueSlice = [][]queues.Queue{{FirstQueue}, {SecondQueue}} +// QueueDetails is the expected result in a Get. +var QueueDetails = queues.QueueDetails{ + DefaultMessageTTL: 3600, + MaxMessagesPostSize: 262144, + Extra: map[string]interface{}{"description": "Queue used for unit testing."}, +} + // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2/queues", @@ -171,3 +186,15 @@ func HandleUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, UpdateQueueResponse) }) } + +// HandleGetSuccessfully configures the test server to respond to a Get request. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), + 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, GetQueueResponse) + }) +} diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index 408ad9a4f3..3a7a6f92ab 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -73,3 +73,13 @@ func TestUpdate(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, updatedQueueResult, actual) } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := queues.Get(fake.ServiceClient(), QueueName).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, QueueDetails, actual) +} diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index 38238133f1..76ce7efbce 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -37,3 +37,7 @@ func nextPageURL(currentURL string, next string) (string, error) { func updateURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName) } + +func getURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName) +} From 3e7c3510d8a3ff8c7f14fadb30bfd361b2d78013 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Mon, 26 Mar 2018 22:52:38 -0700 Subject: [PATCH 0291/2296] Add delete function for queues --- openstack/messaging/v2/queues/doc.go | 7 +++++++ openstack/messaging/v2/queues/requests.go | 8 ++++++++ openstack/messaging/v2/queues/results.go | 6 ++++++ openstack/messaging/v2/queues/testing/fixtures.go | 10 ++++++++++ openstack/messaging/v2/queues/testing/requests_test.go | 9 +++++++++ openstack/messaging/v2/queues/urls.go | 4 ++++ 6 files changed, 44 insertions(+) diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index 8c83b13bed..c24e8d94d9 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -68,5 +68,12 @@ Example to Get a Queue if err != nil { panic(err) } + +Example to Delete a Queue + + err := queues.Delete(client, queueName).ExtractErr() + if err != nil { + panic(err) + } */ package queues diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index fd98530d1b..327f6f887e 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -181,3 +181,11 @@ func Get(client *gophercloud.ServiceClient, queueName string) (r GetResult) { }) return } + +// Delete deletes the specified queue. +func Delete(client *gophercloud.ServiceClient, queueName string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, queueName), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 84ac48acc2..6800ec7bce 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -33,6 +33,12 @@ type GetResult struct { commonResult } +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // Queue represents a messaging queue. type Queue struct { Href string `json:"href"` diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index bbea7b90fc..eb05dae826 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -198,3 +198,13 @@ func HandleGetSuccessfully(t *testing.T) { fmt.Fprintf(w, GetQueueResponse) }) } + +// HandleDeleteSuccessfully configures the test server to respond to a Delete request. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s", QueueName), + 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/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index 3a7a6f92ab..5b8252e43f 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -83,3 +83,12 @@ func TestGet(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, QueueDetails, actual) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + err := queues.Delete(fake.ServiceClient(), QueueName).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index 76ce7efbce..efea4528b2 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -41,3 +41,7 @@ func updateURL(client *gophercloud.ServiceClient, queueName string) string { func getURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName) } + +func deleteURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName) +} From 7314aaab5fec29673689a4df85654d6b7b68c88f Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Tue, 10 Apr 2018 15:58:29 -0700 Subject: [PATCH 0292/2296] Add acceptance tests for queues --- acceptance/clients/clients.go | 21 +++++ .../openstack/messaging/v2/messaging.go | 54 ++++++++++++ .../openstack/messaging/v2/queue_test.go | 86 +++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 acceptance/openstack/messaging/v2/messaging.go create mode 100644 acceptance/openstack/messaging/v2/queue_test.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 93e852418a..666564ad5f 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -500,6 +500,27 @@ func NewClusteringV1Client() (*gophercloud.ServiceClient, error) { }) } +// NewMessagingV2Client returns a *ServiceClient for making calls +// to the OpenStack Messaging (Zaqar) v2 API. An error will be returned +// if authentication or client creation was not possible. +func NewMessagingV2Client(clientID string) (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewMessagingV2(client, clientID, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} + // configureDebug will configure the provider client to print the API // requests and responses if OS_DEBUG is enabled. func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderClient { diff --git a/acceptance/openstack/messaging/v2/messaging.go b/acceptance/openstack/messaging/v2/messaging.go new file mode 100644 index 0000000000..d3b2480aab --- /dev/null +++ b/acceptance/openstack/messaging/v2/messaging.go @@ -0,0 +1,54 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" +) + +func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error) { + queueName := tools.RandomString("ACPTTEST", 5) + + t.Logf("Attempting to create Queue: %s", queueName) + + createOpts := queues.CreateOpts{ + QueueName: queueName, + MaxMessagesPostSize: 262143, + DefaultMessageTTL: 3700, + DefaultMessageDelay: 25, + DeadLetterQueueMessagesTTL: 3500, + MaxClaimCount: 10, + Extra: map[string]interface{}{"description": "Test Queue for Gophercloud acceptance tests."}, + } + + createErr := queues.Create(client, createOpts).ExtractErr() + if createErr != nil { + t.Fatalf("Unable to create Queue: %v", createErr) + } + + GetQueue(t, client, queueName) + + t.Logf("Created Queue: %s", queueName) + return queueName, nil +} + +func DeleteQueue(t *testing.T, client *gophercloud.ServiceClient, queueName string) { + t.Logf("Attempting to delete Queue: %s", queueName) + err := queues.Delete(client, queueName).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete Queue %s: %v", queueName, err) + } + + t.Logf("Deleted Queue: %s", queueName) +} + +func GetQueue(t *testing.T, client *gophercloud.ServiceClient, queueName string) (queues.QueueDetails, error) { + t.Logf("Attempting to get Queue: %s", queueName) + queue, err := queues.Get(client, queueName).Extract() + if err != nil { + t.Fatalf("Unable to get Queue %s: %v", queueName, err) + } + return queue, nil +} diff --git a/acceptance/openstack/messaging/v2/queue_test.go b/acceptance/openstack/messaging/v2/queue_test.go new file mode 100644 index 0000000000..5ab131847b --- /dev/null +++ b/acceptance/openstack/messaging/v2/queue_test.go @@ -0,0 +1,86 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" + "github.com/gophercloud/gophercloud/pagination" +) + +func TestCRUDQueues(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-71861300734d" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + createdQueue, err := queues.Get(client, createdQueueName).Extract() + + tools.PrintResource(t, createdQueue) + tools.PrintResource(t, createdQueue.Extra) + + updateOpts := queues.BatchUpdateOpts{ + queues.UpdateOpts{ + Op: "replace", + Path: "/metadata/_max_claim_count", + Value: 15, + }, + queues.UpdateOpts{ + Op: "replace", + Path: "/metadata/description", + Value: "Updated description for queues acceptance test.", + }, + } + + t.Logf("Attempting to update Queue: %s", createdQueueName) + updateResult, updateErr := queues.Update(client, createdQueueName, updateOpts).Extract() + if updateErr != nil { + t.Fatalf("Unable to update Queue %s: %v", createdQueueName, updateErr) + } + + updatedQueue, err := GetQueue(t, client, createdQueueName) + + tools.PrintResource(t, updateResult) + tools.PrintResource(t, updatedQueue) + tools.PrintResource(t, updatedQueue.Extra) +} + +func TestListQueues(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-71861300734d" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + firstQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, firstQueueName) + + secondQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, secondQueueName) + + listOpts := queues.ListOpts{ + Limit: 10, + Detailed: true, + } + + pager := queues.List(client, listOpts) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + allQueues, err := queues.ExtractQueues(page) + if err != nil { + t.Fatalf("Unable to extract Queues: %v", err) + } + + for _, queue := range allQueues { + tools.PrintResource(t, queue) + } + + return true, nil + }) +} From 76e673f99f92ac10f73aeaa5dc0401b2d742780d Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Wed, 11 Apr 2018 18:44:46 -0700 Subject: [PATCH 0293/2296] Clustering Policy Delete Implementation (#917) * initial commit * fix X-OpenStack-Request-ID header * fix review comments --- .../openstack/clustering/v1/policies_test.go | 4 +++- openstack/clustering/v1/policies/requests.go | 12 ++++++++++++ openstack/clustering/v1/policies/results.go | 17 ++++++++++++++++- .../clustering/v1/policies/testing/fixtures.go | 15 +++++++++++++++ .../v1/policies/testing/requests_test.go | 12 ++++++++++++ openstack/clustering/v1/policies/urls.go | 4 ++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go index 6fd9cffa35..8fff7a5f54 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -35,7 +35,7 @@ func TestPolicyList(t *testing.T) { } } -func TestPolicyCreate(t *testing.T) { +func TestPolicyCreateAndDelete(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) @@ -57,6 +57,8 @@ func TestPolicyCreate(t *testing.T) { createdPolicy, err := policies.Create(client, opts).Extract() th.AssertNoErr(t, err) + defer policies.Delete(client, createdPolicy.ID) + tools.PrintResource(t, createdPolicy) if createdPolicy.CreatedAt.IsZero() { diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index cd6f6c9759..6a6f1657b7 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -1,6 +1,8 @@ package policies import ( + "net/http" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -83,3 +85,13 @@ func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) }) return } + +// Create makes a request against the API to delete a policy +func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { + var result *http.Response + result, r.Err = client.Delete(policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + r.Header = result.Header + return +} diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index 2e82deb53c..a0a0356fa9 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -131,7 +131,7 @@ type policyResult struct { gophercloud.Result } -func (r policyResult) Extract() (*Policy, error) { +func (r CreateResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"policy"` } @@ -143,3 +143,18 @@ func (r policyResult) Extract() (*Policy, error) { type CreateResult struct { policyResult } + +type DeleteResult struct { + gophercloud.HeaderResult +} + +// DeleteResult contains the delete information from a delete policy request +type DeleteHeader struct { + RequestID string `json:"X-OpenStack-Request-ID"` +} + +func (r DeleteResult) Extract() (*DeleteHeader, error) { + var s *DeleteHeader + err := r.HeaderResult.ExtractInto(&s) + return s, err +} diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go index 6f2bad66f9..7f2aaa92bb 100644 --- a/openstack/clustering/v1/policies/testing/fixtures.go +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -99,6 +99,8 @@ const PolicyCreateBody = ` } ` +const PolicyDeleteRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" + var ( ExpectedPolicyCreatedAt1, _ = time.Parse(time.RFC3339, "2018-04-02T21:43:30.000000Z") ExpectedPolicyUpdatedAt1, _ = time.Parse(time.RFC3339, "2018-04-02T00:19:12.000000Z") @@ -107,6 +109,9 @@ var ( ExpectedCreatePolicyCreatedAt, _ = time.Parse(time.RFC3339, "2018-04-04T00:18:36.000000Z") ZeroTime, _ = time.Parse(time.RFC3339, "1-01-01T00:00:00.000000Z") + // Policy ID to delete + PolicyIDtoDelete = "1" + ExpectedPolicies = [][]policies.Policy{ { { @@ -222,3 +227,13 @@ func HandlePolicyCreate(t *testing.T) { fmt.Fprintf(w, PolicyCreateBody) }) } + +func HandlePolicyDelete(t *testing.T) { + th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoDelete, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("X-OpenStack-Request-ID", PolicyDeleteRequestID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go index 17fef6ce76..f077a5459a 100644 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -58,3 +58,15 @@ func TestCreatePolicy(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } + +func TestDeletePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandlePolicyDelete(t) + + actual, err := policies.Delete(fake.ServiceClient(), PolicyIDtoDelete).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, PolicyDeleteRequestID, actual.RequestID) +} diff --git a/openstack/clustering/v1/policies/urls.go b/openstack/clustering/v1/policies/urls.go index e0d0c8de7f..d705e8a3fc 100644 --- a/openstack/clustering/v1/policies/urls.go +++ b/openstack/clustering/v1/policies/urls.go @@ -14,3 +14,7 @@ func policyListURL(client *gophercloud.ServiceClient) string { func policyCreateURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } + +func policyDeleteURL(client *gophercloud.ServiceClient, policyID string) string { + return client.ServiceURL(apiVersion, apiName, policyID) +} From 4d8fee31efb25411f1588e71c8347570a3df4ad2 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Wed, 11 Apr 2018 18:56:12 -0700 Subject: [PATCH 0294/2296] Senlin: Webhook Trigger (#824) * Senlin: Webhook Trigger (#823) Add webhooks trigger for senlin clustering * Fixed example code in doc Improved/cleanup comments to make it more readable Refactored Webhook struct for Extract() to be cleaner and more readable * Fixed trigger opts to properly construct the query strings for "V" and "params" Added test cases for nil, empty, and params to webhook trigger Added test case for testing query string construction * Added acceptance test, but commented out because in order to invoke webhook trigger, requires cluster, profiles, and receiver to be created. * Changed doc.go to use V param * Merged from upstream with go fmt fix * Fixed formatting for doc as well for testing for triggerOpts --- acceptance/clients/clients.go | 6 +- .../clustering/v1/webhooktrigger_test.go | 32 +++++ openstack/clustering/v1/webhooks/doc.go | 15 ++ openstack/clustering/v1/webhooks/requests.go | 53 +++++++ openstack/clustering/v1/webhooks/results.go | 22 +++ .../clustering/v1/webhooks/testing/doc.go | 2 + .../v1/webhooks/testing/requests_test.go | 136 ++++++++++++++++++ openstack/clustering/v1/webhooks/urls.go | 7 + 8 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 acceptance/openstack/clustering/v1/webhooktrigger_test.go create mode 100644 openstack/clustering/v1/webhooks/doc.go create mode 100644 openstack/clustering/v1/webhooks/requests.go create mode 100644 openstack/clustering/v1/webhooks/results.go create mode 100644 openstack/clustering/v1/webhooks/testing/doc.go create mode 100644 openstack/clustering/v1/webhooks/testing/requests_test.go create mode 100644 openstack/clustering/v1/webhooks/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 666564ad5f..d664f90109 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -481,9 +481,9 @@ func NewLoadBalancerV2Client() (*gophercloud.ServiceClient, error) { }) } -// NewClusteringV1Client returns a *ServiceClient for making calls to the -// OpenStack Clustering v1 API. An error will be returned if authentication -// or client creation was not possible. +// NewClusteringV1Client returns a *ServiceClient for making calls +// to the OpenStack Clustering v1 API. An error will be returned +// if authentication or client creation was not possible. func NewClusteringV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { diff --git a/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/acceptance/openstack/clustering/v1/webhooktrigger_test.go new file mode 100644 index 0000000000..4acbabe5f4 --- /dev/null +++ b/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -0,0 +1,32 @@ +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestClusteringWebhookTrigger(t *testing.T) { + + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + // TODO: need to have cluster receiver created + receiverUUID := "f93f83f6-762b-41b6-b757-80507834d394" + actionID, err := webhooks.Trigger(client, receiverUUID, nil).Extract() + if err != nil { + // TODO: Uncomment next line once using real receiver + //t.Fatalf("Unable to extract webhooks trigger: %v", err) + t.Logf("TODO: Need to implement webhook trigger once PR receiver") + } else { + t.Logf("Webhook trigger action id %s", actionID) + } + + // TODO: Need to compare to make sure action ID exists + th.AssertEquals(t, true, true) +} diff --git a/openstack/clustering/v1/webhooks/doc.go b/openstack/clustering/v1/webhooks/doc.go new file mode 100644 index 0000000000..c76dc11ffb --- /dev/null +++ b/openstack/clustering/v1/webhooks/doc.go @@ -0,0 +1,15 @@ +/* +Package webhooks provides the ability to trigger an action represented by a webhook from the OpenStack Clustering +Service. + +Example to Trigger webhook action + + result, err := webhooks.Trigger(serviceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{V: "1"}).Extract() + if err != nil { + panic(err) + } + + fmt.Println("result", result) + +*/ +package webhooks diff --git a/openstack/clustering/v1/webhooks/requests.go b/openstack/clustering/v1/webhooks/requests.go new file mode 100644 index 0000000000..0b38861b98 --- /dev/null +++ b/openstack/clustering/v1/webhooks/requests.go @@ -0,0 +1,53 @@ +package webhooks + +import ( + "net/url" + + "github.com/gophercloud/gophercloud" + "golang.org/x/crypto/openpgp/errors" +) + +// TriggerOpts represents options used for triggering an action +type TriggerOpts struct { + V string `q:"V,required"` + Params map[string]string +} + +// TriggerOptsBuilder Query string builder interface for webhooks +type TriggerOptsBuilder interface { + ToWebhookTriggerQuery() (string, error) +} + +// Query string builder for webhooks +func (opts TriggerOpts) ToWebhookTriggerQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + params := q.Query() + + for k, v := range opts.Params { + params.Add(k, v) + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), err +} + +// Trigger an action represented by a webhook. +func Trigger(client *gophercloud.ServiceClient, id string, opts TriggerOptsBuilder) (r TriggerResult) { + url := triggerURL(client, id) + if opts != nil { + query, err := opts.ToWebhookTriggerQuery() + if err != nil { + r.Err = err + return + } + url += query + } else { + r.Err = errors.InvalidArgumentError("Must contain V for TriggerOpt") + return + } + + _, r.Err = client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + return +} diff --git a/openstack/clustering/v1/webhooks/results.go b/openstack/clustering/v1/webhooks/results.go new file mode 100644 index 0000000000..ccb06086a2 --- /dev/null +++ b/openstack/clustering/v1/webhooks/results.go @@ -0,0 +1,22 @@ +package webhooks + +import ( + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +type TriggerResult struct { + commonResult +} + +// Extract retrieves the response action +func (r commonResult) Extract() (string, error) { + var s struct { + Action string `json:"action"` + } + err := r.ExtractInto(&s) + return s.Action, err +} diff --git a/openstack/clustering/v1/webhooks/testing/doc.go b/openstack/clustering/v1/webhooks/testing/doc.go new file mode 100644 index 0000000000..9a759ee29a --- /dev/null +++ b/openstack/clustering/v1/webhooks/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_webhooks_v1 +package testing diff --git a/openstack/clustering/v1/webhooks/testing/requests_test.go b/openstack/clustering/v1/webhooks/testing/requests_test.go new file mode 100644 index 0000000000..9d0da6f335 --- /dev/null +++ b/openstack/clustering/v1/webhooks/testing/requests_test.go @@ -0,0 +1,136 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "encoding/json" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestWebhookTrigger(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" + }`) + }) + + triggerOpts := webhooks.TriggerOpts{ + V: "1", + Params: map[string]string{ + "foo": "bar", + "bar": "baz", + }, + } + result, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, result, "290c44fa-c60f-4d75-a0eb-87433ba982a3") +} + +// Test webhook with params that generates query strings +func TestWebhookParams(t *testing.T) { + triggerOpts := webhooks.TriggerOpts{ + V: "1", + Params: map[string]string{ + "foo": "bar", + "bar": "baz", + }, + } + expected := "?V=1&bar=baz&foo=bar" + actual, err := triggerOpts.ToWebhookTriggerQuery() + th.AssertNoErr(t, err) + th.AssertEquals(t, actual, expected) +} + +// Nagative test case for returning invalid type (integer) for action id +func TestWebhooksInvalidAction(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": 123 + }`) + }) + + triggerOpts := webhooks.TriggerOpts{ + V: "1", + Params: map[string]string{ + "foo": "bar", + "bar": "baz", + }, + } + _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() + isValid := err.(*json.UnmarshalTypeError) == nil + th.AssertEquals(t, false, isValid) +} + +// Negative test case for passing empty TriggerOpt +func TestWebhookTriggerInvalidEmptyOpt(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" + }`) + }) + + _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{}).Extract() + if err == nil { + t.Errorf("Expected error without V param") + } +} + +// Negative test case for passing in nil for TriggerOpt +func TestWebhookTriggerInvalidNilOpt(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" + }`) + }) + + _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", nil).Extract() + + if err == nil { + t.Errorf("Expected error with nil param") + } +} diff --git a/openstack/clustering/v1/webhooks/urls.go b/openstack/clustering/v1/webhooks/urls.go new file mode 100644 index 0000000000..563cf81122 --- /dev/null +++ b/openstack/clustering/v1/webhooks/urls.go @@ -0,0 +1,7 @@ +package webhooks + +import "github.com/gophercloud/gophercloud" + +func triggerURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("v1", "webhooks", id, "trigger") +} From 3c0e6bd2622193314406205c231ca623528832c9 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Thu, 12 Apr 2018 17:13:20 +1200 Subject: [PATCH 0295/2296] Octavia l7 rule support - part 1: create (#924) * Octavia l7 rule support - part 1: create For #832 Octavia l7 rule create API implementation: https://github.com/openstack/octavia/blob/06bf5c58d5845f684fcaf933605ed112586eefc3/octavia/api/v2/controllers/l7rule.py#L146 * Use project_id instead of tenant_id --- .../openstack/loadbalancer/v2/loadbalancer.go | 24 ++++++++ .../loadbalancer/v2/loadbalancers_test.go | 6 ++ openstack/loadbalancer/v2/l7policies/doc.go | 13 ++++ .../loadbalancer/v2/l7policies/requests.go | 46 ++++++++++++-- .../loadbalancer/v2/l7policies/results.go | 27 +++++++-- .../v2/l7policies/testing/fixtures.go | 60 ++++++++++++++++--- .../v2/l7policies/testing/requests_test.go | 47 +++++++++++++++ openstack/loadbalancer/v2/l7policies/urls.go | 5 ++ 8 files changed, 212 insertions(+), 16 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 10bbcc28c1..d9c8ce86c0 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -202,6 +202,30 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l return policy, nil } +// CreateL7Rule creates a l7 rule for specified l7 policy. +func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer) (*l7policies.Rule, error) { + t.Logf("Attempting to create l7 rule for policy %s", policyID) + + createOpts := l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeStartWith, + Value: "/api", + } + + rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract() + if err != nil { + return rule, err + } + + t.Logf("Successfully created l7 rule for policy %s", policyID) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active") + } + + return rule, nil +} + // DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if // the l7 policy could not be deleted. This works best when used as a deferred // function. diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 080f215ef0..0149b19ea4 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -128,6 +128,12 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newPolicy) + // L7 rule + _, err = CreateL7Rule(t, lbClient, newPolicy.ID, lb) + if err != nil { + t.Fatalf("Unable to create l7 rule: %v", err) + } + // Pool pool, err := CreatePool(t, lbClient, lb) if err != nil { diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 95ac5e17f0..86135e3d37 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -58,5 +58,18 @@ Example to Update a L7Policy if err != nil { panic(err) } + +Example to Create a Rule + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + createOpts := l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images*", + } + rule, err := l7policies.CreateRule(lbClient, l7policyID, createOpts).Extract() + if err != nil { + panic(err) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index b64c14b863..16a655947b 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -51,10 +51,6 @@ type CreateOpts struct { // A human-readable description for the resource. Description string `json:"description,omitempty"` - // TenantID is the UUID of the project who owns the L7 policy in neutron-lbaas. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - // ProjectID is the UUID of the project who owns the L7 policy in octavia. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` @@ -96,7 +92,7 @@ type ListOpts struct { Name string `q:"name"` ListenerID string `q:"listener_id"` Action string `q:"action"` - TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` RedirectPoolID string `q:"redirect_pool_id"` RedirectURL string `q:"redirect_url"` ID string `q:"id"` @@ -187,3 +183,43 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r }) return } + +// CreateRuleOpts is the common options struct used in this package's CreateRule +// operation. +type CreateRuleOpts struct { + // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. + RuleType RuleType `json:"type" required:"true"` + + // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. + CompareType CompareType `json:"compare_type" required:"true"` + + // The value to use for the comparison. For example, the file type to compare. + Value string `json:"value" required:"true"` + + // ProjectID is the UUID of the project who owns the rule in octavia. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + + // The key to use for the comparison. For example, the name of the cookie to evaluate. + Key string `json:"key,omitempty"` + + // When true the logic of the rule is inverted. For example, with invert true, + // equal to would become not equal to. Default is false. + Invert bool `json:"invert,omitempty"` +} + +// ToRuleCreateMap builds a request body from CreateRuleOpts. +func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "rule") +} + +// CreateRule will create and associate a Rule with a particular L7Policy. +func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) { + b, err := opts.ToRuleCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + return +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index f4907a3f59..78a8e83db0 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -26,9 +26,9 @@ type L7Policy struct { // A human-readable description for the resource. Description string `json:"description"` - // TenantID is the UUID of the project who owns the L7 policy in neutron-lbaas. + // ProjectID is the UUID of the project who owns the L7 policy in octavia. // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` // Requests matching this policy will be redirected to the pool with this ID. // Only valid if action is REDIRECT_TO_POOL. @@ -59,9 +59,9 @@ type Rule struct { // The value to use for the comparison. For example, the file type to compare. Value string `json:"value"` - // TenantID is the UUID of the project who owns the rule in neutron-lbaas. + // ProjectID is the UUID of the project who owns the rule in octavia. // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` // The key to use for the comparison. For example, the name of the cookie to evaluate. Key string `json:"key"` @@ -147,3 +147,22 @@ type DeleteResult struct { type UpdateResult struct { commonResult } + +type commonRuleResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a rule. +func (r commonRuleResult) Extract() (*Rule, error) { + var s struct { + Rule *Rule `json:"rule"` + } + err := r.ExtractInto(&s) + return s.Rule, err +} + +// CreateRuleResult represents the result of a CreateRule operation. +// Call its Extract method to interpret it as a Rule. +type CreateRuleResult struct { + commonRuleResult +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index 193e728097..a4d2e13144 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -21,7 +21,7 @@ const SingleL7PolicyBody = ` "redirect_url": "http://www.example.com", "action": "REDIRECT_TO_URL", "position": 1, - "tenant_id": "e3cd678b11784734bc366148aa37580e", + "project_id": "e3cd678b11784734bc366148aa37580e", "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", "name": "redirect-example.com", "rules": [] @@ -37,7 +37,7 @@ var ( Action: "REDIRECT_TO_URL", Position: 1, Description: "", - TenantID: "e3cd678b11784734bc366148aa37580e", + ProjectID: "e3cd678b11784734bc366148aa37580e", RedirectPoolID: "", RedirectURL: "http://www.example.com", AdminStateUp: true, @@ -50,7 +50,7 @@ var ( Action: "REDIRECT_TO_POOL", Position: 1, Description: "", - TenantID: "c1f7910086964990847dc6c8b128f63c", + ProjectID: "c1f7910086964990847dc6c8b128f63c", RedirectPoolID: "bac433c6-5bea-4311-80da-bd1cd90fbd25", RedirectURL: "", AdminStateUp: true, @@ -63,12 +63,22 @@ var ( Action: "REDIRECT_TO_URL", Position: 1, Description: "Redirect requests to example.com", - TenantID: "e3cd678b11784734bc366148aa37580e", + ProjectID: "e3cd678b11784734bc366148aa37580e", RedirectPoolID: "", RedirectURL: "http://www.new-example.com", AdminStateUp: true, Rules: []l7policies.Rule{}, } + RulePath = l7policies.Rule{ + ID: "16621dbb-a736-4888-a57a-3ecd53df784c", + RuleType: "PATH", + CompareType: "REGEX", + Value: "/images*", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Key: "", + Invert: false, + AdminStateUp: true, + } ) // HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request @@ -101,7 +111,7 @@ const L7PoliciesListBody = ` "description": "", "admin_state_up": true, "rules": [], - "tenant_id": "e3cd678b11784734bc366148aa37580e", + "project_id": "e3cd678b11784734bc366148aa37580e", "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", "redirect_url": "http://www.example.com", "action": "REDIRECT_TO_URL", @@ -114,7 +124,7 @@ const L7PoliciesListBody = ` "description": "", "admin_state_up": true, "rules": [], - "tenant_id": "c1f7910086964990847dc6c8b128f63c", + "project_id": "c1f7910086964990847dc6c8b128f63c", "listener_id": "be3138a3-5cf7-4513-a4c2-bb137e668bab", "action": "REDIRECT_TO_POOL", "position": 1, @@ -136,7 +146,7 @@ const PostUpdateL7PolicyBody = ` "redirect_url": "http://www.new-example.com", "action": "REDIRECT_TO_URL", "position": 1, - "tenant_id": "e3cd678b11784734bc366148aa37580e", + "project_id": "e3cd678b11784734bc366148aa37580e", "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", "name": "NewL7PolicyName", "rules": [] @@ -203,3 +213,39 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, PostUpdateL7PolicyBody) }) } + +// SingleRuleBody is the canned body of a Get request on an existing rule. +const SingleRuleBody = ` +{ + "rule": { + "compare_type": "REGEX", + "invert": false, + "admin_state_up": true, + "value": "/images*", + "key": null, + "project_id": "e3cd678b11784734bc366148aa37580e", + "type": "PATH", + "id": "16621dbb-a736-4888-a57a-3ecd53df784c" + } +} +` + +// HandleRuleCreationSuccessfully sets up the test server to respond to a rule creation request +// with a given response. +func HandleRuleCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", 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, `{ + "rule": { + "compare_type": "REGEX", + "type": "PATH", + "value": "/images*" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index f9c47d665c..9fac83960b 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -127,3 +127,50 @@ func TestUpdateL7Policy(t *testing.T) { th.CheckDeepEquals(t, L7PolicyUpdated, *actual) } + +func TestCreateRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleCreationSuccessfully(t, SingleRuleBody) + + actual, err := l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images*", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, RulePath, *actual) +} + +func TestRequiredRuleCreateOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + res := l7policies.CreateRule(fake.ServiceClient(), "", l7policies.CreateRuleOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + RuleType: l7policies.RuleType("invalid"), + CompareType: l7policies.CompareTypeRegex, + Value: "/images*", + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareType("invalid"), + Value: "/images*", + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } +} diff --git a/openstack/loadbalancer/v2/l7policies/urls.go b/openstack/loadbalancer/v2/l7policies/urls.go index 7a87a187f9..44ebdd444f 100644 --- a/openstack/loadbalancer/v2/l7policies/urls.go +++ b/openstack/loadbalancer/v2/l7policies/urls.go @@ -5,6 +5,7 @@ import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "l7policies" + rulePath = "rules" ) func rootURL(c *gophercloud.ServiceClient) string { @@ -14,3 +15,7 @@ func rootURL(c *gophercloud.ServiceClient) string { func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } + +func ruleRootURL(c *gophercloud.ServiceClient, policyID string) string { + return c.ServiceURL(rootPath, resourcePath, policyID, rulePath) +} From 0beb722f2dfa54267db1a44ebce1844ef797049f Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Tue, 10 Apr 2018 18:22:31 -0700 Subject: [PATCH 0296/2296] Add stats function to queues --- .../openstack/messaging/v2/queue_test.go | 19 ++++++++++++ openstack/messaging/v2/queues/doc.go | 7 +++++ openstack/messaging/v2/queues/requests.go | 7 +++++ openstack/messaging/v2/queues/results.go | 26 +++++++++++++++++ .../messaging/v2/queues/testing/fixtures.go | 29 +++++++++++++++++++ .../v2/queues/testing/requests_test.go | 10 +++++++ openstack/messaging/v2/queues/urls.go | 4 +++ 7 files changed, 102 insertions(+) diff --git a/acceptance/openstack/messaging/v2/queue_test.go b/acceptance/openstack/messaging/v2/queue_test.go index 5ab131847b..38d80dd203 100644 --- a/acceptance/openstack/messaging/v2/queue_test.go +++ b/acceptance/openstack/messaging/v2/queue_test.go @@ -84,3 +84,22 @@ func TestListQueues(t *testing.T) { return true, nil }) } + +func TestStatQueue(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-71861300734c" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + queueStats, err := queues.GetStats(client, createdQueueName).Extract() + if err != nil { + t.Fatalf("Unable to stat queue: %v", err) + } + + tools.PrintResource(t, queueStats) +} diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index c24e8d94d9..4f1fea9a43 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -75,5 +75,12 @@ Example to Delete a Queue if err != nil { panic(err) } + +Example to Get Message Stats of a Queue + + queueStats, err := queues.GetStats(client, queueName).Extract() + if err != nil { + panic(err) + } */ package queues diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 327f6f887e..5acf60d772 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -189,3 +189,10 @@ func Delete(client *gophercloud.ServiceClient, queueName string) (r DeleteResult }) return } + +// GetStats returns statistics for the specified queue. +func GetStats(client *gophercloud.ServiceClient, queueName string) (r StatResult) { + _, r.Err = client.Get(statURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}}) + return +} diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 6800ec7bce..cb2ba5060d 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -33,6 +33,11 @@ type GetResult struct { commonResult } +// StatResult contains the result of a Share operation. +type StatResult struct { + gophercloud.Result +} + // DeleteResult is the result from a Delete operation. Call its ExtractErr // method to determine if the call succeeded or failed. type DeleteResult struct { @@ -77,6 +82,18 @@ type QueueDetails struct { Flavor string `json:"flavor"` } +// Stats represents a stats response. +type Stats struct { + // Number of Claimed messages for a queue + Claimed int `json:"claimed"` + + // Total Messages for a queue + Total int `json:"total"` + + // Number of free messages + Free int `json:"free"` +} + // Extract interprets any commonResult as a Queue. func (r commonResult) Extract() (QueueDetails, error) { var s QueueDetails @@ -84,6 +101,15 @@ func (r commonResult) Extract() (QueueDetails, error) { return s, err } +// Extract interprets any StatResult as a Stats. +func (r StatResult) Extract() (Stats, error) { + var s struct { + Stats Stats `json:"messages"` + } + err := r.ExtractInto(&s) + return s.Stats, err +} + // ExtractQueues interprets the results of a single page from a // List() call, producing a map of queues. func ExtractQueues(r pagination.Page) ([]Queue, error) { diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index eb05dae826..060db72421 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -101,6 +101,16 @@ const GetQueueResponse = ` "description": "Queue used for unit testing." }` +// GetStatsResponse is a sample response to a stats request. +const GetStatsResponse = ` +{ + "messages":{ + "claimed": 10, + "total": 20, + "free": 10 + } +}` + // FirstQueue is the first result in a List. var FirstQueue = queues.Queue{ Href: "/v2/queues/london", @@ -141,6 +151,13 @@ var QueueDetails = queues.QueueDetails{ Extra: map[string]interface{}{"description": "Queue used for unit testing."}, } +// ExpectedStats is the expected result in a GetStats. +var ExpectedStats = queues.Stats{ + Claimed: 10, + Total: 20, + Free: 10, +} + // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2/queues", @@ -208,3 +225,15 @@ func HandleDeleteSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// HandleGetSuccessfully configures the test server to respond to a Get request. +func HandleGetStatsSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/stats", QueueName), + 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, GetStatsResponse) + }) +} diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index 5b8252e43f..c02cdcef1e 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -92,3 +92,13 @@ func TestDelete(t *testing.T) { err := queues.Delete(fake.ServiceClient(), QueueName).ExtractErr() th.AssertNoErr(t, err) } + +func TestGetStat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetStatsSuccessfully(t) + + actual, err := queues.GetStats(fake.ServiceClient(), QueueName).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedStats, actual) +} diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index efea4528b2..1c541e47ad 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -45,3 +45,7 @@ func getURL(client *gophercloud.ServiceClient, queueName string) string { func deleteURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName) } + +func statURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "stats") +} From 822d8c2adf78ea80c16a734155713d6cb9b0d75a Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Fri, 13 Apr 2018 13:45:33 +1200 Subject: [PATCH 0297/2296] Octavia l7 rule support - part 2: list For #832 Octavia l7 rule list API implementation: - https://github.com/openstack/octavia/blob/69a45c254be854dbdbff946dbe2564711cdecec3/octavia/api/v2/controllers/l7rule.py#L59 - https://github.com/openstack/octavia/blob/69a45c254be854dbdbff946dbe2564711cdecec3/octavia/api/v2/types/l7rule.py#L26 --- .../loadbalancer/v2/l7policies_test.go | 2 +- .../loadbalancer/v2/loadbalancers_test.go | 12 ++++ openstack/loadbalancer/v2/l7policies/doc.go | 18 ++++++ .../loadbalancer/v2/l7policies/requests.go | 44 ++++++++++++++ .../loadbalancer/v2/l7policies/results.go | 37 ++++++++++++ .../v2/l7policies/testing/fixtures.go | 58 +++++++++++++++++++ .../v2/l7policies/testing/requests_test.go | 44 ++++++++++++++ 7 files changed, 214 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/acceptance/openstack/loadbalancer/v2/l7policies_test.go index 848d4e85bc..d6f0925870 100644 --- a/acceptance/openstack/loadbalancer/v2/l7policies_test.go +++ b/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -13,7 +13,7 @@ import ( func TestL7PoliciesList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() if err != nil { - t.Fatalf("Unable to create a network client: %v", err) + t.Fatalf("Unable to create a loadbalancer client: %v", err) } allPages, err := l7policies.List(client, nil).AllPages() diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 0149b19ea4..33a2e5fda6 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -134,6 +134,18 @@ func TestLoadbalancersCRUD(t *testing.T) { t.Fatalf("Unable to create l7 rule: %v", err) } + allPages, err = l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to get l7 rules: %v", err) + } + allRules, err = l7policies.ExtractRules(allPages) + if err != nil { + t.Fatalf("Unable to extract l7 rules: %v", err) + } + for _, rule := range allRules { + tools.PrintResource(t, rule) + } + // Pool pool, err := CreatePool(t, lbClient, lb) if err != nil { diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 86135e3d37..045fba6280 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -71,5 +71,23 @@ Example to Create a Rule if err != nil { panic(err) } + +Example to List L7 Rules + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + listOpts := l7policies.ListRulesOpts{ + RuleType: l7policies.TypePath, + } + allPages, err := l7policies.ListRules(lbClient, l7policyID, listOpts).AllPages() + if err != nil { + panic(err) + } + allRules, err := l7policies.ExtractRules(allPages) + if err != nil { + panic(err) + } + for _, rule := allRules { + fmt.Printf("%+v\n", rule) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 16a655947b..46bd553bbf 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -223,3 +223,47 @@ func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOp _, r.Err = c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) return } + +// ListRulesOptsBuilder allows extensions to add additional parameters to the +// ListRules request. +type ListRulesOptsBuilder interface { + ToRulesListQuery() (string, error) +} + +// ListRulesOpts allows the filtering and sorting of paginated collections +// through the API. +type ListRulesOpts struct { + RuleType RuleType `q:"type"` + ProjectID string `q:"project_id"` + ID string `q:"id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToRulesListQuery formats a ListOpts into a query string. +func (opts ListRulesOpts) ToRulesListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListRules returns a Pager which allows you to iterate over a collection of +// rules. It accepts a ListRulesOptsBuilder, which allows you to filter and +// sort the returned collection for greater efficiency. +// +// Default policy settings return only those rules that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOptsBuilder) pagination.Pager { + url := ruleRootURL(c, policyID) + if opts != nil { + query, err := opts.ToRulesListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return RulePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index 78a8e83db0..14fd6b1112 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -166,3 +166,40 @@ func (r commonRuleResult) Extract() (*Rule, error) { type CreateRuleResult struct { commonRuleResult } + +// RulePage is the page returned by a pager when traversing over a +// collection of Rules in a L7Policy. +type RulePage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of rules 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 RulePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"rules_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a RulePage struct is empty. +func (r RulePage) IsEmpty() (bool, error) { + is, err := ExtractRules(r) + return len(is) == 0, err +} + +// ExtractRules accepts a Page struct, specifically a RulePage struct, +// and extracts the elements into a slice of Rules structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractRules(r pagination.Page) ([]Rule, error) { + var s struct { + Rules []Rule `json:"rules"` + } + err := (r.(RulePage)).ExtractInto(&s) + return s.Rules, err +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index a4d2e13144..446101cc88 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -79,6 +79,16 @@ var ( Invert: false, AdminStateUp: true, } + RuleHostName = l7policies.Rule{ + ID: "d24521a0-df84-4468-861a-a531af116d1e", + RuleType: "HOST_NAME", + CompareType: "EQUAL_TO", + Value: "www.example.com", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Key: "", + Invert: false, + AdminStateUp: true, + } ) // HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request @@ -249,3 +259,51 @@ func HandleRuleCreationSuccessfully(t *testing.T, response string) { fmt.Fprintf(w, response) }) } + +// RulesListBody contains the canned body of a rule list response. +const RulesListBody = ` +{ + "rules":[ + { + "compare_type": "REGEX", + "invert": false, + "admin_state_up": true, + "value": "/images*", + "key": null, + "project_id": "e3cd678b11784734bc366148aa37580e", + "type": "PATH", + "id": "16621dbb-a736-4888-a57a-3ecd53df784c" + }, + { + "compare_type": "EQUAL_TO", + "invert": false, + "admin_state_up": true, + "value": "www.example.com", + "key": null, + "project_id": "e3cd678b11784734bc366148aa37580e", + "type": "HOST_NAME", + "id": "d24521a0-df84-4468-861a-a531af116d1e" + } + ] +} +` + +// HandleRuleListSuccessfully sets up the test server to respond to a rule List request. +func HandleRuleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", 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, RulesListBody) + case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": + fmt.Fprintf(w, `{ "rules": [] }`) + default: + t.Fatalf("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules invoked with unexpected marker=[%s]", marker) + } + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index 9fac83960b..2c2f5fbacc 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -174,3 +174,47 @@ func TestRequiredRuleCreateOpts(t *testing.T) { t.Fatalf("Expected error, but got none") } } + +func TestListRules(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleListSuccessfully(t) + + pages := 0 + err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := l7policies.ExtractRules(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 rules, got %d", len(actual)) + } + th.CheckDeepEquals(t, RulePath, actual[0]) + th.CheckDeepEquals(t, RuleHostName, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllRules(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleListSuccessfully(t) + + allPages, err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).AllPages() + th.AssertNoErr(t, err) + + actual, err := l7policies.ExtractRules(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, RulePath, actual[0]) + th.CheckDeepEquals(t, RuleHostName, actual[1]) +} From f049cc899d336fd557fa75d5f86b3ef0dd5f9924 Mon Sep 17 00:00:00 2001 From: wayne Date: Fri, 13 Apr 2018 11:05:35 -0500 Subject: [PATCH 0298/2296] Blockstorage v3 quota-set support - part 1, Get (#877) * Copy compute v2 quotaset to storage v3 quotaset. Monkey see, monkey do. * Modify tests and fixtures to use block storage values. * Update QuotaSet and UpdateQuotaSet types for block storage. * Fix test fixture values. * Handle apparent bug in PartialPut test case. * Group stimuli and their responses together in fixtures. * Improve test fixture parmaeterization. This allows us to more easily see problems with test cases by specifying the expected inputs and outputs in the same place in code rather than the implicit assumption that the fixture has the output expected for the test case. Another advantage is that this brings us that much closer to the ideal of test fixtures consisting purely of a list of data structures which can then be iterated over in a single test case function that glues all the pieces together to validate that the tested code, given some input data, produces the expected output data. * Implement data-driven testing approach. * Remove redundant test. * Absorb TestGetDetail into DDT test cases. * Absorb TestUpdate into DDT test cases. * Absorb TestPartialUpdate into DDT test cases. * Remove unnecessary field initialization. * Get rid of defunct fixture function. * Absorb TestDelete into DDT test cases. * Not expecting a json response in Error test case. * Fix blockstorage quotaset api docs. * Fix blockstorage QuotaDetail struct. * s/tenantID/projectID/ * Fix blockstorage quotaset detailed url. * Accept HTTP 200 for blockstorage quotaset DELETE * OS block storage quotasets don't support groups. * Get rid of unused resourceURL. * Add blockstorage quotaset GetDefaults function. * Add quotaset acceptance tests. * s/block storage/compute/ in comments. * Fix blockstorage quotaset test json strings. * Fix blockstorage quotaset Delete tests. * Handle URI query params in blockstorage quotaset tests. * Remove blockstorage quotaset delete code. * Remove blockstorage quotaset update code. * Remove blockstorage quotaset details code. * Remove blockstorage quotaset defaults code. * Rename block storage directory. * Address first round of PR comments. --- .../blockstorage/v3/quotaset_test.go | 34 +++++++++ .../blockstorage/extensions/quotasets/doc.go | 14 ++++ .../extensions/quotasets/requests.go | 11 +++ .../extensions/quotasets/results.go | 74 +++++++++++++++++++ .../extensions/quotasets/testing/doc.go | 2 + .../extensions/quotasets/testing/fixtures.go | 59 +++++++++++++++ .../quotasets/testing/requests_test.go | 33 +++++++++ .../blockstorage/extensions/quotasets/urls.go | 9 +++ 8 files changed, 236 insertions(+) create mode 100644 acceptance/openstack/blockstorage/v3/quotaset_test.go create mode 100644 openstack/blockstorage/extensions/quotasets/doc.go create mode 100644 openstack/blockstorage/extensions/quotasets/requests.go create mode 100644 openstack/blockstorage/extensions/quotasets/results.go create mode 100644 openstack/blockstorage/extensions/quotasets/testing/doc.go create mode 100644 openstack/blockstorage/extensions/quotasets/testing/fixtures.go create mode 100644 openstack/blockstorage/extensions/quotasets/testing/requests_test.go create mode 100644 openstack/blockstorage/extensions/quotasets/urls.go diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go new file mode 100644 index 0000000000..3f60fc3353 --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -0,0 +1,34 @@ +// +build acceptance quotasets + +package v3 + +import ( + "os" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestQuotasetGet(t *testing.T) { + client, projectID := getClientAndProject(t) + + quotaSet, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaSet) +} + +// getClientAndProject reduces boilerplate by returning a new blockstorage v3 +// ServiceClient and a project ID obtained from the OS_PROJECT_NAME envvar. +func getClientAndProject(t *testing.T) (*gophercloud.ServiceClient, string) { + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + projectID := os.Getenv("OS_PROJECT_NAME") + th.AssertNoErr(t, err) + return client, projectID +} diff --git a/openstack/blockstorage/extensions/quotasets/doc.go b/openstack/blockstorage/extensions/quotasets/doc.go new file mode 100644 index 0000000000..e9a95cc15e --- /dev/null +++ b/openstack/blockstorage/extensions/quotasets/doc.go @@ -0,0 +1,14 @@ +/* +Package quotasets enables retrieving and managing Block Storage quotas. + +Example to Get a Quota Set + + quotaset, err := quotasets.Get(blockStorageClient, "project-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +*/ +package quotasets diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go new file mode 100644 index 0000000000..737ea28887 --- /dev/null +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -0,0 +1,11 @@ +package quotasets + +import ( + "github.com/gophercloud/gophercloud" +) + +// Get returns public data about a previously created QuotaSet. +func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { + _, r.Err = client.Get(getURL(client, projectID), &r.Body, nil) + return +} diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/extensions/quotasets/results.go new file mode 100644 index 0000000000..b36de95050 --- /dev/null +++ b/openstack/blockstorage/extensions/quotasets/results.go @@ -0,0 +1,74 @@ +package quotasets + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// QuotaSet is a set of operational limits that allow for control of block +// storage usage. +type QuotaSet struct { + // ID is project associated with this QuotaSet. + ID string `json:"id"` + + // Volumes is the number of volumes that are allowed for each project. + Volumes int `json:"volumes"` + + // Snapshots is the number of snapshots that are allowed for each project. + Snapshots int `json:"snapshots"` + + // Gigabytes is the size (GB) of volumes and snapshots that are allowed for + // each project. + Gigabytes int `json:"gigabytes"` + + // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are + // allowed for each project and the specifed volume type. + PerVolumeGigabytes int `json:"per_volume_gigabytes"` + + // Backups is the number of backups that are allowed for each project. + Backups int `json:"backups"` + + // BackupGigabytes is the size (GB) of backups that are allowed for each + // project. + BackupGigabytes int `json:"backup_gigabytes"` +} + +// 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 (r QuotaSetPage) IsEmpty() (bool, error) { + ks, err := ExtractQuotaSets(r) + 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 +} + +// 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 +} diff --git a/openstack/blockstorage/extensions/quotasets/testing/doc.go b/openstack/blockstorage/extensions/quotasets/testing/doc.go new file mode 100644 index 0000000000..30d864eb95 --- /dev/null +++ b/openstack/blockstorage/extensions/quotasets/testing/doc.go @@ -0,0 +1,2 @@ +// quotasets unit tests +package testing diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go new file mode 100644 index 0000000000..7a2726eac7 --- /dev/null +++ b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go @@ -0,0 +1,59 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const FirstTenantID = "555544443333222211110000ffffeeee" + +var successTestCases = []struct { + name string + httpMethod string + jsonBody string + uriPath string + uriQueryParams map[string]string + expectedQuotaSet quotasets.QuotaSet +}{ + { + name: "simple GET request", + jsonBody: ` +{ + "quota_set" : { + "volumes" : 8, + "snapshots" : 9, + "gigabytes" : 10, + "per_volume_gigabytes" : 11, + "backups" : 12, + "backup_gigabytes" : 13 + } +}`, + expectedQuotaSet: quotasets.QuotaSet{ + Volumes: 8, + Snapshots: 9, + Gigabytes: 10, + PerVolumeGigabytes: 11, + Backups: 12, + BackupGigabytes: 13, + }, + uriPath: "/os-quota-sets/" + FirstTenantID, + httpMethod: "GET", + }, +} + +// HandleSuccessfulRequest configures the test server to respond to an HTTP request. +func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string) { + + th.Mux.HandleFunc(uriPath, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, httpMethod) + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + + fmt.Fprintf(w, jsonOutput) + }) +} diff --git a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go new file mode 100644 index 0000000000..6dcd1aa941 --- /dev/null +++ b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go @@ -0,0 +1,33 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +var emptyQuotaSet = quotasets.QuotaSet{} + +func testSuccessTestCase(t *testing.T, httpMethod, uriPath, jsonBody string, expectedQuotaSet quotasets.QuotaSet) error { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSuccessfulRequest(t, httpMethod, uriPath, jsonBody) + + actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() + if err != nil { + return err + } + th.CheckDeepEquals(t, &expectedQuotaSet, actual) + return nil +} + +func TestSuccessTestCases(t *testing.T) { + for _, tt := range successTestCases { + err := testSuccessTestCase(t, tt.httpMethod, tt.uriPath, tt.jsonBody, tt.expectedQuotaSet) + if err != nil { + t.Fatalf("Test case '%s' failed with error:\n%s", tt.name, err) + } + } +} diff --git a/openstack/blockstorage/extensions/quotasets/urls.go b/openstack/blockstorage/extensions/quotasets/urls.go new file mode 100644 index 0000000000..68897bf092 --- /dev/null +++ b/openstack/blockstorage/extensions/quotasets/urls.go @@ -0,0 +1,9 @@ +package quotasets + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "os-quota-sets" + +func getURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID) +} From 2aaf13981a1dd8dfe85a22a4aea957e90ff8bddd Mon Sep 17 00:00:00 2001 From: wayne Date: Fri, 13 Apr 2018 12:27:05 -0500 Subject: [PATCH 0299/2296] Blockstorage v3 quota-set support - part 2, Get Defaults (#878) * Revert "Remove blockstorage quotaset defaults code." This reverts commit 92ea29d4f7b295050500a81a5ebfc1a706b87a25. * Functions in requests.go should return named result. * Fix comment docstring. --- acceptance/openstack/blockstorage/v3/quotaset_test.go | 9 +++++++++ openstack/blockstorage/extensions/quotasets/requests.go | 6 ++++++ openstack/blockstorage/extensions/quotasets/urls.go | 4 ++++ 3 files changed, 19 insertions(+) diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index 3f60fc3353..5bcbbf21b5 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -22,6 +22,15 @@ func TestQuotasetGet(t *testing.T) { tools.PrintResource(t, quotaSet) } +func TestQuotasetGetDefaults(t *testing.T) { + client, projectID := getClientAndProject(t) + + quotaSet, err := quotasets.GetDefaults(client, projectID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaSet) +} + // getClientAndProject reduces boilerplate by returning a new blockstorage v3 // ServiceClient and a project ID obtained from the OS_PROJECT_NAME envvar. func getClientAndProject(t *testing.T) (*gophercloud.ServiceClient, string) { diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go index 737ea28887..0328791583 100644 --- a/openstack/blockstorage/extensions/quotasets/requests.go +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -9,3 +9,9 @@ func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { _, r.Err = client.Get(getURL(client, projectID), &r.Body, nil) return } + +// GetDefaults returns public data about the project's default block storage quotas. +func GetDefaults(client *gophercloud.ServiceClient, projectID string) (r GetResult) { + _, r.Err = client.Get(getDefaultsURL(client, projectID), &r.Body, nil) + return +} diff --git a/openstack/blockstorage/extensions/quotasets/urls.go b/openstack/blockstorage/extensions/quotasets/urls.go index 68897bf092..737cab03ca 100644 --- a/openstack/blockstorage/extensions/quotasets/urls.go +++ b/openstack/blockstorage/extensions/quotasets/urls.go @@ -7,3 +7,7 @@ const resourcePath = "os-quota-sets" func getURL(c *gophercloud.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID) } + +func getDefaultsURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID, "defaults") +} From a1471c71904c908a8ddd80debefceae7a1504d69 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Mon, 16 Apr 2018 10:18:44 +1200 Subject: [PATCH 0300/2296] Octavia l7 rule support - part 3: get For #832 Octavia l7 rule Get API implementation: - https://github.com/openstack/octavia/blob/69a45c254be854dbdbff946dbe2564711cdecec3/octavia/api/v2/controllers/l7rule.py#L45 - https://github.com/openstack/octavia/blob/69a45c254be854dbdbff946dbe2564711cdecec3/octavia/api/v2/types/l7rule.py#L26 --- .../loadbalancer/v2/loadbalancers_test.go | 17 ++++++++++++++--- openstack/loadbalancer/v2/l7policies/doc.go | 7 +++++++ .../loadbalancer/v2/l7policies/requests.go | 6 ++++++ openstack/loadbalancer/v2/l7policies/results.go | 6 ++++++ .../v2/l7policies/testing/fixtures.go | 11 +++++++++++ .../v2/l7policies/testing/requests_test.go | 14 ++++++++++++++ openstack/loadbalancer/v2/l7policies/urls.go | 4 ++++ 7 files changed, 62 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 33a2e5fda6..14a1b5c323 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -129,16 +129,16 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newPolicy) // L7 rule - _, err = CreateL7Rule(t, lbClient, newPolicy.ID, lb) + rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) if err != nil { t.Fatalf("Unable to create l7 rule: %v", err) } - allPages, err = l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() + allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() if err != nil { t.Fatalf("Unable to get l7 rules: %v", err) } - allRules, err = l7policies.ExtractRules(allPages) + allRules, err := l7policies.ExtractRules(allPages) if err != nil { t.Fatalf("Unable to extract l7 rules: %v", err) } @@ -146,6 +146,17 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, rule) } + newRule, err := l7policies.GetRule(lbClient, newPolicy.ID, rule.ID).Extract() + if err != nil { + t.Fatalf("Unable to get l7 rule: %v", err) + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + tools.PrintResource(t, newRule) + // Pool pool, err := CreatePool(t, lbClient, lb) if err != nil { diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 045fba6280..4125b81787 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -89,5 +89,12 @@ Example to List L7 Rules for _, rule := allRules { fmt.Printf("%+v\n", rule) } + +Example to Get a l7 rule + + l7rule, err := l7policies.GetRule(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d", "53ad8ab8-40fa-11e8-a508-00224d6b7bc1").Extract() + if err != nil { + panic(err) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 46bd553bbf..8ee4ef3112 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -267,3 +267,9 @@ func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOpts return RulePage{pagination.LinkedPageBase{PageResult: r}} }) } + +// GetRule retrieves a particular L7Policy Rule based on its unique ID. +func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { + _, r.Err = c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) + return +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index 14fd6b1112..20aff9f67d 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -203,3 +203,9 @@ func ExtractRules(r pagination.Page) ([]Rule, error) { err := (r.(RulePage)).ExtractInto(&s) return s.Rules, err } + +// GetRuleResult represents the result of a GetRule operation. +// Call its Extract method to interpret it as a Rule. +type GetRuleResult struct { + commonRuleResult +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index 446101cc88..9bf7e18c75 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -307,3 +307,14 @@ func HandleRuleListSuccessfully(t *testing.T) { } }) } + +// HandleRuleGetSuccessfully sets up the test server to respond to a rule Get request. +func HandleRuleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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, SingleRuleBody) + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index 2c2f5fbacc..7091820a97 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -218,3 +218,17 @@ func TestListAllRules(t *testing.T) { th.CheckDeepEquals(t, RulePath, actual[0]) th.CheckDeepEquals(t, RuleHostName, actual[1]) } + +func TestGetRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := l7policies.GetRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, RulePath, *actual) +} diff --git a/openstack/loadbalancer/v2/l7policies/urls.go b/openstack/loadbalancer/v2/l7policies/urls.go index 44ebdd444f..ecb607a8e8 100644 --- a/openstack/loadbalancer/v2/l7policies/urls.go +++ b/openstack/loadbalancer/v2/l7policies/urls.go @@ -19,3 +19,7 @@ func resourceURL(c *gophercloud.ServiceClient, id string) string { func ruleRootURL(c *gophercloud.ServiceClient, policyID string) string { return c.ServiceURL(rootPath, resourcePath, policyID, rulePath) } + +func ruleResourceURL(c *gophercloud.ServiceClient, policyID string, ruleID string) string { + return c.ServiceURL(rootPath, resourcePath, policyID, rulePath, ruleID) +} From 7522d2d908cf0d8f1a7df78b377e2078fa3bac9f Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Mon, 16 Apr 2018 15:19:29 +1200 Subject: [PATCH 0301/2296] Octavia l7 rule support - part 4: delete For #832 Octavia l7 rule Delete API implementation: - https://github.com/openstack/octavia/blob/69a45c254be854dbdbff946dbe2564711cdecec3/octavia/api/v2/controllers/l7rule.py#L228 --- .../openstack/loadbalancer/v2/loadbalancer.go | 17 +++++++++++++++++ .../loadbalancer/v2/loadbalancers_test.go | 1 + openstack/loadbalancer/v2/l7policies/doc.go | 9 +++++++++ .../loadbalancer/v2/l7policies/requests.go | 6 ++++++ openstack/loadbalancer/v2/l7policies/results.go | 6 ++++++ .../v2/l7policies/testing/fixtures.go | 10 ++++++++++ .../v2/l7policies/testing/requests_test.go | 9 +++++++++ 7 files changed, 58 insertions(+) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index d9c8ce86c0..c15dd068e4 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -243,6 +243,23 @@ func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, polic t.Logf("Successfully deleted l7 policy %s", policyID) } +// DeleteL7Rule will delete a specified l7 rule. A fatal error will occur if +// the l7 rule could not be deleted. This works best when used as a deferred +// function. +func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) { + t.Logf("Attempting to delete l7 rule %s", ruleID) + + if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete l7 rule: %v", err) + } + + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + t.Logf("Successfully deleted l7 rule %s", ruleID) +} + // DeleteListener will delete a specified listener. A fatal error will occur if // the listener could not be deleted. This works best when used as a deferred // function. diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 14a1b5c323..e2ab0c3ae2 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -133,6 +133,7 @@ func TestLoadbalancersCRUD(t *testing.T) { if err != nil { t.Fatalf("Unable to create l7 rule: %v", err) } + defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() if err != nil { diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 4125b81787..f32c6b6232 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -96,5 +96,14 @@ Example to Get a l7 rule if err != nil { panic(err) } + +Example to Delete a l7 rule + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" + err := l7policies.DeleteRule(lbClient, l7policyID, ruleID).ExtractErr() + if err != nil { + panic(err) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 8ee4ef3112..82ee041956 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -273,3 +273,9 @@ func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r Ge _, r.Err = c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) return } + +// DeleteRule will remove a Rule from a particular L7Policy. +func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { + _, r.Err = c.Delete(ruleResourceURL(c, policyID, ruleID), nil) + return +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index 20aff9f67d..f629896008 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -209,3 +209,9 @@ func ExtractRules(r pagination.Page) ([]Rule, error) { type GetRuleResult struct { commonRuleResult } + +// DeleteRuleResult represents the result of a DeleteRule operation. +// Call its ExtractErr method to determine if the request succeeded or failed. +type DeleteRuleResult struct { + gophercloud.ErrResult +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index 9bf7e18c75..81700350b0 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -318,3 +318,13 @@ func HandleRuleGetSuccessfully(t *testing.T) { fmt.Fprintf(w, SingleRuleBody) }) } + +// HandleRuleDeletionSuccessfully sets up the test server to respond to a rule deletion request. +func HandleRuleDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index 7091820a97..17ea33cbd8 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -232,3 +232,12 @@ func TestGetRule(t *testing.T) { th.CheckDeepEquals(t, RulePath, *actual) } + +func TestDeleteRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleDeletionSuccessfully(t) + + res := l7policies.DeleteRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") + th.AssertNoErr(t, res.Err) +} From 55fd009dc7d741503af377f09818462d7d4959ff Mon Sep 17 00:00:00 2001 From: wayne Date: Mon, 16 Apr 2018 09:53:03 -0500 Subject: [PATCH 0302/2296] Blockstorage v3 quota-set support - part 3, Get Details (#879) * Revert "Remove blockstorage quotaset details code." This reverts commit 01df962e4b6721c8fb157ab3affb71270d2517b3. * Fix formatting problem. * s/Detail/Usage * get usage url differently. * address nitpicks --- .../blockstorage/v3/quotaset_test.go | 9 +++ .../blockstorage/extensions/quotasets/doc.go | 8 ++ .../extensions/quotasets/requests.go | 9 +++ .../extensions/quotasets/results.go | 79 +++++++++++++++++++ .../extensions/quotasets/testing/fixtures.go | 72 +++++++++++++++-- .../quotasets/testing/requests_test.go | 23 ++++-- 6 files changed, 186 insertions(+), 14 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index 5bcbbf21b5..84b93858bf 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -31,6 +31,15 @@ func TestQuotasetGetDefaults(t *testing.T) { tools.PrintResource(t, quotaSet) } +func TestQuotasetGetUsage(t *testing.T) { + client, projectID := getClientAndProject(t) + + quotaSetUsage, err := quotasets.GetUsage(client, projectID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotaSetUsage) +} + // getClientAndProject reduces boilerplate by returning a new blockstorage v3 // ServiceClient and a project ID obtained from the OS_PROJECT_NAME envvar. func getClientAndProject(t *testing.T) (*gophercloud.ServiceClient, string) { diff --git a/openstack/blockstorage/extensions/quotasets/doc.go b/openstack/blockstorage/extensions/quotasets/doc.go index e9a95cc15e..cd58644410 100644 --- a/openstack/blockstorage/extensions/quotasets/doc.go +++ b/openstack/blockstorage/extensions/quotasets/doc.go @@ -10,5 +10,13 @@ Example to Get a Quota Set fmt.Printf("%+v\n", quotaset) +Example to Get Quota Set Usage + + quotaset, err := quotasets.GetUsage(blockStorageClient, "project-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) */ package quotasets diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go index 0328791583..5943dd6d76 100644 --- a/openstack/blockstorage/extensions/quotasets/requests.go +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -1,6 +1,8 @@ package quotasets import ( + "fmt" + "github.com/gophercloud/gophercloud" ) @@ -15,3 +17,10 @@ func GetDefaults(client *gophercloud.ServiceClient, projectID string) (r GetResu _, r.Err = client.Get(getDefaultsURL(client, projectID), &r.Body, nil) return } + +// GetUsage returns detailed public data about a previously created QuotaSet. +func GetUsage(client *gophercloud.ServiceClient, projectID string) (r GetUsageResult) { + u := fmt.Sprintf("%s?usage=true", getURL(client, projectID)) + _, r.Err = client.Get(u, &r.Body, nil) + return +} diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/extensions/quotasets/results.go index b36de95050..fec3b10474 100644 --- a/openstack/blockstorage/extensions/quotasets/results.go +++ b/openstack/blockstorage/extensions/quotasets/results.go @@ -33,6 +33,65 @@ type QuotaSet struct { BackupGigabytes int `json:"backup_gigabytes"` } +// QuotaUsageSet represents details of both operational limits of block +// storage resources and the current usage of those resources. +type QuotaUsageSet struct { + // ID is the project ID associated with this QuotaUsageSet. + ID string `json:"id"` + + // Volumes is the volume usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Volumes QuotaUsage `json:"volumes"` + + // Snapshots is the snapshot usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Snapshots QuotaUsage `json:"snapshots"` + + // Gigabytes is the size (GB) usage information of volumes and snapshots + // for this project, including in_use, limit, reserved and allocated + // attributes. Note: allocated attribute is available only when nested + // quota is enabled. + Gigabytes QuotaUsage `json:"gigabytes"` + + // PerVolumeGigabytes is the size (GB) usage information for each volume, + // including in_use, limit, reserved and allocated attributes. Note: + // allocated attribute is available only when nested quota is enabled and + // only limit is meaningful here. + PerVolumeGigabytes QuotaUsage `json:"per_volume_gigabytes"` + + // Backups is the backup usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Backups QuotaUsage `json:"backups"` + + // BackupGigabytes is the size (GB) usage information of backup for this + // project, including in_use, limit, reserved and allocated attributes. + // Note: allocated attribute is available only when nested quota is + // enabled. + BackupGigabytes QuotaUsage `json:"backup_gigabytes"` +} + +// QuotaUsage is a set of details about a single operational limit that allows +// for control of block storage usage. +type QuotaUsage struct { + // InUse is the current number of provisioned resources of the given type. + InUse int `json:"in_use"` + + // Allocated is the current number of resources of a given type allocated + // for use. It is only available when nested quota is enabled. + Allocated int `json:"allocated"` + + // 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 @@ -72,3 +131,23 @@ func (r quotaResult) Extract() (*QuotaSet, error) { type GetResult struct { quotaResult } + +type quotaUsageResult struct { + gophercloud.Result +} + +// GetUsageResult is the response from a Get operation. Call its Extract +// method to interpret it as a QuotaSet. +type GetUsageResult struct { + quotaUsageResult +} + +// Extract is a method that attempts to interpret any QuotaUsageSet resource +// response as a set of QuotaUsageSet structs. +func (r quotaUsageResult) Extract() (QuotaUsageSet, error) { + var s struct { + QuotaUsageSet QuotaUsageSet `json:"quota_set"` + } + err := r.ExtractInto(&s) + return s.QuotaUsageSet, err +} diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go index 7a2726eac7..aa31b5a0ef 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go +++ b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go @@ -13,12 +13,13 @@ import ( const FirstTenantID = "555544443333222211110000ffffeeee" var successTestCases = []struct { - name string - httpMethod string - jsonBody string - uriPath string - uriQueryParams map[string]string - expectedQuotaSet quotasets.QuotaSet + name string + httpMethod string + jsonBody string + uriPath string + uriQueryParams map[string]string + expectedQuotaSet quotasets.QuotaSet + expectedQuotaUsageSet quotasets.QuotaUsageSet }{ { name: "simple GET request", @@ -44,16 +45,73 @@ var successTestCases = []struct { uriPath: "/os-quota-sets/" + FirstTenantID, httpMethod: "GET", }, + + { + name: "GET details request", + jsonBody: ` +{ + "quota_set" : { + "id": "555544443333222211110000ffffeeee", + "volumes" : { + "in_use": 15, + "limit": 16, + "reserved": 17 + }, + "snapshots" : { + "in_use": 18, + "limit": 19, + "reserved": 20 + }, + "gigabytes" : { + "in_use": 21, + "limit": 22, + "reserved": 23 + }, + "per_volume_gigabytes" : { + "in_use": 24, + "limit": 25, + "reserved": 26 + }, + "backups" : { + "in_use": 27, + "limit": 28, + "reserved": 29 + }, + "backup_gigabytes" : { + "in_use": 30, + "limit": 31, + "reserved": 32 + } + } + } +}`, + expectedQuotaUsageSet: quotasets.QuotaUsageSet{ + ID: FirstTenantID, + Volumes: quotasets.QuotaUsage{InUse: 15, Limit: 16, Reserved: 17}, + Snapshots: quotasets.QuotaUsage{InUse: 18, Limit: 19, Reserved: 20}, + Gigabytes: quotasets.QuotaUsage{InUse: 21, Limit: 22, Reserved: 23}, + PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, + Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, + BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, + }, + uriPath: "/os-quota-sets/" + FirstTenantID, + uriQueryParams: map[string]string{"usage": "true"}, + httpMethod: "GET", + }, } // HandleSuccessfulRequest configures the test server to respond to an HTTP request. -func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string) { +func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { th.Mux.HandleFunc(uriPath, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, httpMethod) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") + if uriQueryParams != nil { + th.TestFormValues(t, r, uriQueryParams) + } + fmt.Fprintf(w, jsonOutput) }) } diff --git a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go index 6dcd1aa941..4379d50d63 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go +++ b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go @@ -9,23 +9,32 @@ import ( ) var emptyQuotaSet = quotasets.QuotaSet{} +var emptyQuotaUsageSet = quotasets.QuotaUsageSet{} -func testSuccessTestCase(t *testing.T, httpMethod, uriPath, jsonBody string, expectedQuotaSet quotasets.QuotaSet) error { +func testSuccessTestCase(t *testing.T, httpMethod, uriPath, jsonBody string, uriQueryParams map[string]string, expectedQuotaSet quotasets.QuotaSet, expectedQuotaUsageSet quotasets.QuotaUsageSet) error { th.SetupHTTP() defer th.TeardownHTTP() - HandleSuccessfulRequest(t, httpMethod, uriPath, jsonBody) + HandleSuccessfulRequest(t, httpMethod, uriPath, jsonBody, uriQueryParams) - actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() - if err != nil { - return err + if expectedQuotaSet != emptyQuotaSet { + actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() + if err != nil { + return err + } + th.CheckDeepEquals(t, &expectedQuotaSet, actual) + } else if expectedQuotaUsageSet != emptyQuotaUsageSet { + actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID).Extract() + if err != nil { + return err + } + th.CheckDeepEquals(t, expectedQuotaUsageSet, actual) } - th.CheckDeepEquals(t, &expectedQuotaSet, actual) return nil } func TestSuccessTestCases(t *testing.T) { for _, tt := range successTestCases { - err := testSuccessTestCase(t, tt.httpMethod, tt.uriPath, tt.jsonBody, tt.expectedQuotaSet) + err := testSuccessTestCase(t, tt.httpMethod, tt.uriPath, tt.jsonBody, tt.uriQueryParams, tt.expectedQuotaSet, tt.expectedQuotaUsageSet) if err != nil { t.Fatalf("Test case '%s' failed with error:\n%s", tt.name, err) } From afd5af3ab91b2f3b2a3664a0e1c1bfe831f3d2a4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 16 Apr 2018 16:30:07 -0600 Subject: [PATCH 0303/2296] Compute v2: Style fixes for quotasets (#935) --- .../v2/extensions/quotasets/requests.go | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go index 34d935893e..d5daa7e3fb 100644 --- a/openstack/compute/v2/extensions/quotasets/requests.go +++ b/openstack/compute/v2/extensions/quotasets/requests.go @@ -5,34 +5,32 @@ import ( ) // Get returns public data about a previously created QuotaSet. -func Get(client *gophercloud.ServiceClient, tenantID string) GetResult { - var res GetResult - _, res.Err = client.Get(getURL(client, tenantID), &res.Body, nil) - return res +func Get(client *gophercloud.ServiceClient, tenantID string) (r GetResult) { + _, r.Err = client.Get(getURL(client, tenantID), &r.Body, nil) + return } // GetDetail returns detailed public data about a previously created QuotaSet. -func GetDetail(client *gophercloud.ServiceClient, tenantID string) GetDetailResult { - var res GetDetailResult - _, res.Err = client.Get(getDetailURL(client, tenantID), &res.Body, nil) - return res +func GetDetail(client *gophercloud.ServiceClient, tenantID string) (r GetDetailResult) { + _, r.Err = client.Get(getDetailURL(client, tenantID), &r.Body, nil) + return } // Updates the quotas for the given tenantID and returns the new QuotaSet. -func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (res UpdateResult) { +func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (r UpdateResult) { reqBody, err := opts.ToComputeQuotaUpdateMap() if err != nil { - res.Err = err + r.Err = err return } - _, res.Err = client.Put(updateURL(client, tenantID), reqBody, &res.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - return res + _, r.Err = client.Put(updateURL(client, tenantID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + return } // Resets the quotas for the given tenant to their default values. -func Delete(client *gophercloud.ServiceClient, tenantID string) (res DeleteResult) { - _, res.Err = client.Delete(deleteURL(client, tenantID), nil) +func Delete(client *gophercloud.ServiceClient, tenantID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, tenantID), nil) return } From 3b807d710e297a9c9029e67ca2e18e6806ead347 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Tue, 17 Apr 2018 13:32:33 +1200 Subject: [PATCH 0304/2296] Remove 'listener_id' from Pool ListOpts For #937 Octavia does not support to filter pools by listener_id. --- openstack/loadbalancer/v2/pools/requests.go | 1 - 1 file changed, 1 deletion(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 11564be83f..b58bf9c359 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -25,7 +25,6 @@ type ListOpts struct { Name string `q:"name"` ID string `q:"id"` LoadbalancerID string `q:"loadbalancer_id"` - ListenerID string `q:"listener_id"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` From aa92b798f1eb2bf76af5a3aaca3df26e591068ef Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Tue, 17 Apr 2018 16:49:20 +1200 Subject: [PATCH 0305/2296] Octavia l7 rule support - part 5(final): update (#936) * Octavia l7 rule support - part 5(final): update For #832 Octavia l7 rule PUT API implementation: - https://github.com/openstack/octavia/blob/69a45c254be854dbdbff946dbe2564711cdecec3/octavia/api/v2/controllers/l7rule.py#L189 * Catch error when constructing update request body --- .../loadbalancer/v2/loadbalancers_test.go | 14 +++++- openstack/loadbalancer/v2/l7policies/doc.go | 14 ++++++ .../loadbalancer/v2/l7policies/requests.go | 49 ++++++++++++++++++- .../loadbalancer/v2/l7policies/results.go | 6 +++ .../v2/l7policies/testing/fixtures.go | 45 +++++++++++++++++ .../v2/l7policies/testing/requests_test.go | 49 +++++++++++++++++++ 6 files changed, 174 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index e2ab0c3ae2..6fd0f5f0a5 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -147,15 +147,25 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, rule) } - newRule, err := l7policies.GetRule(lbClient, newPolicy.ID, rule.ID).Extract() + updateL7ruleOpts := l7policies.UpdateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images/special*", + } + _, err = l7policies.UpdateRule(lbClient, policy.ID, rule.ID, updateL7ruleOpts).Extract() if err != nil { - t.Fatalf("Unable to get l7 rule: %v", err) + t.Fatalf("Unable to update l7 rule: %v", err) } if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } + newRule, err := l7policies.GetRule(lbClient, newPolicy.ID, rule.ID).Extract() + if err != nil { + t.Fatalf("Unable to get l7 rule: %v", err) + } + tools.PrintResource(t, newRule) // Pool diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index f32c6b6232..813579905c 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -105,5 +105,19 @@ Example to Delete a l7 rule if err != nil { panic(err) } + +Example to Update a Rule + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" + updateOpts := l7policies.UpdateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images/special*", + } + rule, err := l7policies.UpdateRule(lbClient, l7policyID, ruleID, updateOpts).Extract() + if err != nil { + panic(err) + } */ package l7policies diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 82ee041956..1272bc1b21 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -177,7 +177,11 @@ func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { // Update allows l7policy to be updated. func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, _ := opts.ToL7PolicyUpdateMap() + b, err := opts.ToL7PolicyUpdateMap() + if err != nil { + r.Err = err + return + } _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) @@ -279,3 +283,46 @@ func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r _, r.Err = c.Delete(ruleResourceURL(c, policyID, ruleID), nil) return } + +// UpdateRuleOptsBuilder allows to add additional parameters to the PUT request. +type UpdateRuleOptsBuilder interface { + ToRuleUpdateMap() (map[string]interface{}, error) +} + +// UpdateRuleOpts is the common options struct used in this package's Update +// operation. +type UpdateRuleOpts struct { + // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. + RuleType RuleType `json:"type,omitempty"` + + // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. + CompareType CompareType `json:"compare_type,omitempty"` + + // The value to use for the comparison. For example, the file type to compare. + Value string `json:"value,omitempty"` + + // The key to use for the comparison. For example, the name of the cookie to evaluate. + Key string `json:"key,omitempty"` + + // When true the logic of the rule is inverted. For example, with invert true, + // equal to would become not equal to. Default is false. + Invert bool `json:"invert,omitempty"` +} + +// ToRuleUpdateMap builds a request body from UpdateRuleOpts. +func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "rule") +} + +// UpdateRule allows Rule to be updated. +func UpdateRule(c *gophercloud.ServiceClient, policyID string, ruleID string, opts UpdateRuleOptsBuilder) (r UpdateRuleResult) { + b, err := opts.ToRuleUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + return +} diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index f629896008..43c8b74d8a 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -215,3 +215,9 @@ type GetRuleResult struct { type DeleteRuleResult struct { gophercloud.ErrResult } + +// UpdateRuleResult represents the result of an UpdateRule operation. +// Call its Extract method to interpret it as a Rule. +type UpdateRuleResult struct { + commonRuleResult +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index 81700350b0..e14cf898d1 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -89,6 +89,16 @@ var ( Invert: false, AdminStateUp: true, } + RuleUpdated = l7policies.Rule{ + ID: "16621dbb-a736-4888-a57a-3ecd53df784c", + RuleType: "PATH", + CompareType: "REGEX", + Value: "/images/special*", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Key: "", + Invert: false, + AdminStateUp: true, + } ) // HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request @@ -328,3 +338,38 @@ func HandleRuleDeletionSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// PostUpdateRuleBody is the canned response body of a Update request on an existing rule. +const PostUpdateRuleBody = ` +{ + "rule": { + "compare_type": "REGEX", + "invert": false, + "admin_state_up": true, + "value": "/images/special*", + "key": null, + "project_id": "e3cd678b11784734bc366148aa37580e", + "type": "PATH", + "id": "16621dbb-a736-4888-a57a-3ecd53df784c" + } +} +` + +// HandleRuleUpdateSuccessfully sets up the test server to respond to a rule Update request. +func HandleRuleUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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, `{ + "rule": { + "compare_type": "REGEX", + "type": "PATH", + "value": "/images/special*" + } + }`) + + fmt.Fprintf(w, PostUpdateRuleBody) + }) +} diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index 17ea33cbd8..dc84754505 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -128,6 +128,18 @@ func TestUpdateL7Policy(t *testing.T) { th.CheckDeepEquals(t, L7PolicyUpdated, *actual) } +func TestUpdateL7PolicyWithInvalidOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + res := l7policies.Update(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ + Action: l7policies.Action("invalid"), + }) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + func TestCreateRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -241,3 +253,40 @@ func TestDeleteRule(t *testing.T) { res := l7policies.DeleteRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") th.AssertNoErr(t, res.Err) } + +func TestUpdateRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleUpdateSuccessfully(t) + + client := fake.ServiceClient() + actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images/special*", + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, RuleUpdated, *actual) +} + +func TestUpdateRuleWithInvalidOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + res := l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ + RuleType: l7policies.RuleType("invalid"), + }) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + + res = l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ + CompareType: l7policies.CompareType("invalid"), + }) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} From 020d890c61002325cdb4c2ab15db01b62bf3e00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Wed, 18 Apr 2018 01:58:24 +0800 Subject: [PATCH 0306/2296] Identity V3: Check Whether User Belongs to Group (#887) --- acceptance/openstack/identity/v3/users_test.go | 8 ++++++++ openstack/identity/v3/users/doc.go | 13 +++++++++++++ openstack/identity/v3/users/requests.go | 18 ++++++++++++++++++ openstack/identity/v3/users/results.go | 12 ++++++++++++ .../identity/v3/users/testing/fixtures.go | 11 +++++++++++ .../identity/v3/users/testing/requests_test.go | 9 +++++++++ openstack/identity/v3/users/urls.go | 4 ++++ 7 files changed, 75 insertions(+) diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index d51eba18d2..dd2e519df2 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -215,6 +215,14 @@ func TestUsersGroups(t *testing.T) { th.AssertEquals(t, found, true) + ok, err := users.IsMemberOfGroup(client, group.ID, user.ID).Extract() + if err != nil { + t.Fatalf("Unable to check whether user belongs to group: %v", err) + } + if !ok { + t.Fatalf("User %s is expected to be a member of group %s", user.ID, group.ID) + } + err = users.RemoveFromGroup(client, group.ID, user.ID).ExtractErr() th.AssertNoErr(t, err) diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index c51a3fb607..994ce71bcb 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -106,6 +106,19 @@ Example to Add a User to a Group panic(err) } +Example to Check Whether a User Belongs to a Group + + groupID := "bede500ee1124ae9b0006ff859758b3a" + userID := "0fe36e73809d46aeae6705c39077b1b3" + ok, err := users.IsMemberOfGroup(identityClient, groupID, userID).Extract() + if err != nil { + panic(err) + } + + if ok { + fmt.Printf("user %s is a member of group %s\n", userID, groupID) + } + Example to Remove a User from a Group groupID := "bede500ee1124ae9b0006ff859758b3a" diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index e1be94e7d9..223ad87c12 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -1,6 +1,8 @@ package users import ( + "net/http" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" @@ -266,6 +268,22 @@ func AddToGroup(client *gophercloud.ServiceClient, groupID, userID string) (r Ad return } +// IsMemberOfGroup checks whether a user belongs to a group. +func IsMemberOfGroup(client *gophercloud.ServiceClient, groupID, userID string) (r IsMemberOfGroupResult) { + url := isMemberOfGroupURL(client, groupID, userID) + var response *http.Response + response, r.Err = client.Request("HEAD", url, &gophercloud.RequestOpts{ + OkCodes: []int{204, 404}, + }) + if r.Err == nil && response != nil { + if (*response).StatusCode == 204 { + r.isMember = true + } + } + + return +} + // RemoveFromGroup removes a user from a group. func RemoveFromGroup(client *gophercloud.ServiceClient, groupID, userID string) (r RemoveFromGroupResult) { url := removeFromGroupURL(client, groupID, userID) diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 00a1a00062..e158ac723b 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -116,6 +116,13 @@ type AddToGroupResult struct { gophercloud.ErrResult } +// IsMemberOfGroupResult is the response from a IsMemberOfGroup operation. Call its +// Extract method to determine if the request succeeded or failed. +type IsMemberOfGroupResult struct { + isMember bool + gophercloud.Result +} + // RemoveFromGroupResult is the response from a RemoveFromGroup operation. Call its // ExtractErr method to determine if the request succeeded or failed. type RemoveFromGroupResult struct { @@ -165,3 +172,8 @@ func (r userResult) Extract() (*User, error) { err := r.ExtractInto(&s) return s.User, err } + +// Extract extracts IsMemberOfGroupResult as bool and error values +func (r IsMemberOfGroupResult) Extract() (bool, error) { + return r.isMember, r.Err +} diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index 73e6acb767..d68e1ac2a4 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -481,6 +481,17 @@ func HandleAddToGroupSuccessfully(t *testing.T) { }) } +// HandleIsMemberOfGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users/{userID} +// on the test handler mux that tests checking whether user belongs to group. +func HandleIsMemberOfGroupSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/groups/ea167b/users/9fe1d3", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + // HandleRemoveFromGroupSuccessfully creates an HTTP handler at /groups/{groupID}/users/{userID} // on the test handler mux that tests removing user from group. func HandleRemoveFromGroupSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index 8cd4c47ac5..c9c362451a 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -170,6 +170,15 @@ func TestAddToGroup(t *testing.T) { th.AssertNoErr(t, res.Err) } +func TestIsMemberOfGroup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleIsMemberOfGroupSuccessfully(t) + ok, err := users.IsMemberOfGroup(client.ServiceClient(), "ea167b", "9fe1d3").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, ok) +} + func TestRemoveFromGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index 35468ad28e..3caa8bbb6c 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -34,6 +34,10 @@ func addToGroupURL(client *gophercloud.ServiceClient, groupID, userID string) st return client.ServiceURL("groups", groupID, "users", userID) } +func isMemberOfGroupURL(client *gophercloud.ServiceClient, groupID, userID string) string { + return client.ServiceURL("groups", groupID, "users", userID) +} + func removeFromGroupURL(client *gophercloud.ServiceClient, groupID, userID string) string { return client.ServiceURL("groups", groupID, "users", userID) } From 2ee41042b9e440a5c6b64b11d8edfed5f5db3f47 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Wed, 11 Apr 2018 10:25:21 -0700 Subject: [PATCH 0307/2296] Add share function for queues --- .../openstack/messaging/v2/messaging.go | 13 +++++ .../openstack/messaging/v2/queue_test.go | 23 ++++++++ openstack/messaging/v2/queues/doc.go | 12 ++++ openstack/messaging/v2/queues/requests.go | 52 +++++++++++++++++ openstack/messaging/v2/queues/results.go | 21 +++++++ .../messaging/v2/queues/testing/fixtures.go | 58 +++++++++++++++++++ .../v2/queues/testing/requests_test.go | 16 +++++ openstack/messaging/v2/queues/urls.go | 4 ++ 8 files changed, 199 insertions(+) diff --git a/acceptance/openstack/messaging/v2/messaging.go b/acceptance/openstack/messaging/v2/messaging.go index d3b2480aab..6e65f04a5c 100644 --- a/acceptance/openstack/messaging/v2/messaging.go +++ b/acceptance/openstack/messaging/v2/messaging.go @@ -52,3 +52,16 @@ func GetQueue(t *testing.T, client *gophercloud.ServiceClient, queueName string) } return queue, nil } + +func CreateShare(t *testing.T, client *gophercloud.ServiceClient, queueName string, clientID string) (queues.QueueShare, error) { + t.Logf("Attempting to create share for queue: %s", queueName) + + shareOpts := queues.ShareOpts{ + Paths: []queues.SharePath{queues.PathMessages}, + Methods: []queues.ShareMethod{queues.MethodPost}, + } + + share, err := queues.Share(client, queueName, shareOpts).Extract() + + return share, err +} diff --git a/acceptance/openstack/messaging/v2/queue_test.go b/acceptance/openstack/messaging/v2/queue_test.go index 38d80dd203..6526cd7aa1 100644 --- a/acceptance/openstack/messaging/v2/queue_test.go +++ b/acceptance/openstack/messaging/v2/queue_test.go @@ -103,3 +103,26 @@ func TestStatQueue(t *testing.T) { tools.PrintResource(t, queueStats) } + +func TestShare(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-71861300734c" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + queueName, err := CreateQueue(t, client) + if err != nil { + t.Logf("Unable to create queue for share.") + } + defer DeleteQueue(t, client, queueName) + + t.Logf("Attempting to create share for queue: %s", queueName) + share, shareErr := CreateShare(t, client, queueName, clientID) + if shareErr != nil { + t.Fatalf("Unable to create share: %v", shareErr) + } + + tools.PrintResource(t, share) +} diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index 4f1fea9a43..23958358d7 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -82,5 +82,17 @@ Example to Get Message Stats of a Queue if err != nil { panic(err) } + +Example to Share a queue + + shareOpts := queues.ShareOpts{ + Paths: []queues.SharePath{queues.ShareMessages}, + Methods: []queues.ShareMethod{queues.MethodGet}, + } + + queueShare, err := queues.Share(client, queueName, shareOpts).Extract() + if err != nil { + panic(err) + } */ package queues diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 5acf60d772..c3526eee7b 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -196,3 +196,55 @@ func GetStats(client *gophercloud.ServiceClient, queueName string) (r StatResult OkCodes: []int{200}}) return } + +type SharePath string + +const ( + PathMessages SharePath = "messages" + PathClaims SharePath = "claims" + PathSubscriptions SharePath = "subscriptions" +) + +type ShareMethod string + +const ( + MethodGet ShareMethod = "GET" + MethodPatch ShareMethod = "PATCH" + MethodPost ShareMethod = "POST" + MethodPut ShareMethod = "PUT" +) + +// ShareOpts specifies share creation parameters. +type ShareOpts struct { + Paths []SharePath `json:"paths,omitempty"` + Methods []ShareMethod `json:"methods,omitempty"` + Expires string `json:"expires,omitempty"` +} + +// ShareOptsBuilder allows extensions to add additional attributes to the +// Share request. +type ShareOptsBuilder interface { + ToQueuesShareMap() (map[string]interface{}, error) +} + +// ToShareQueueMap formats a ShareOpts structure into a request body. +func (opts ShareOpts) ToQueuesShareMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +// Share creates a pre-signed URL for a given queue. +func Share(client *gophercloud.ServiceClient, queueName string, opts ShareOptsBuilder) (r ShareResult) { + b, err := opts.ToQueuesShareMap() + if err != nil { + r.Err = err + return r + } + _, r.Err = client.Post(shareURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index cb2ba5060d..88de920d56 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -44,6 +44,11 @@ type DeleteResult struct { gophercloud.ErrResult } +// ShareResult contains the result of a Share operation. +type ShareResult struct { + gophercloud.Result +} + // Queue represents a messaging queue. type Queue struct { Href string `json:"href"` @@ -94,6 +99,15 @@ type Stats struct { Free int `json:"free"` } +// QueueShare represents a share response. +type QueueShare struct { + Project string `json:"project"` + Paths []string `json:"paths"` + Expires string `json:"expires"` + Methods []string `json:"methods"` + Signature string `json:"signature"` +} + // Extract interprets any commonResult as a Queue. func (r commonResult) Extract() (QueueDetails, error) { var s QueueDetails @@ -110,6 +124,13 @@ func (r StatResult) Extract() (Stats, error) { return s.Stats, err } +// Extract interprets any ShareResult as a QueueShare. +func (r ShareResult) Extract() (QueueShare, error) { + var s QueueShare + err := r.ExtractInto(&s) + return s, err +} + // ExtractQueues interprets the results of a single page from a // List() call, producing a map of queues. func ExtractQueues(r pagination.Page) ([]Queue, error) { diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index 060db72421..aa223a445b 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -25,6 +25,14 @@ const CreateQueueRequest = ` "description": "Queue for unit testing." }` +// CreateShareRequest is a sample request to a share. +const CreateShareRequest = ` +{ + "paths": ["messages", "claims", "subscriptions"], + "methods": ["GET", "POST", "PUT", "PATCH"], + "expires": "2016-09-01T00:00:00" +}` + // ListQueuesResponse1 is a sample response to a List queues. const ListQueuesResponse1 = ` { @@ -111,6 +119,25 @@ const GetStatsResponse = ` } }` +// CreateShareResponse is a sample response to a share request. +const CreateShareResponse = ` +{ + "project": "2887aabf368046a3bb0070f1c0413470", + "paths": [ + "/v2/queues/test/messages", + "/v2/queues/test/claims", + "/v2/queues/test/subscriptions" + ], + "expires": "2016-09-01T00:00:00", + "methods": [ + "GET", + "PATCH", + "POST", + "PUT" + ], + "signature": "6a63d63242ebd18c3518871dda6fdcb6273db2672c599bf985469241e9a1c799" +}` + // FirstQueue is the first result in a List. var FirstQueue = queues.Queue{ Href: "/v2/queues/london", @@ -158,6 +185,24 @@ var ExpectedStats = queues.Stats{ Free: 10, } +// ExpectedShare is the expected result in Share. +var ExpectedShare = queues.QueueShare{ + Project: "2887aabf368046a3bb0070f1c0413470", + Paths: []string{ + "/v2/queues/test/messages", + "/v2/queues/test/claims", + "/v2/queues/test/subscriptions", + }, + Expires: "2016-09-01T00:00:00", + Methods: []string{ + "GET", + "PATCH", + "POST", + "PUT", + }, + Signature: "6a63d63242ebd18c3518871dda6fdcb6273db2672c599bf985469241e9a1c799", +} + // HandleListSuccessfully configures the test server to respond to a List request. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2/queues", @@ -237,3 +282,16 @@ func HandleGetStatsSuccessfully(t *testing.T) { fmt.Fprintf(w, GetStatsResponse) }) } + +// HandleShareSuccessfully configures the test server to respond to a Share request. +func HandleShareSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/share", QueueName), + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, CreateShareRequest) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, CreateShareResponse) + }) +} diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index c02cdcef1e..2dc4e34e94 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -102,3 +102,19 @@ func TestGetStat(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedStats, actual) } + +func TestShare(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleShareSuccessfully(t) + + shareOpts := queues.ShareOpts{ + Paths: []queues.SharePath{queues.PathMessages, queues.PathClaims, queues.PathSubscriptions}, + Methods: []queues.ShareMethod{queues.MethodGet, queues.MethodPost, queues.MethodPut, queues.MethodPatch}, + Expires: "2016-09-01T00:00:00", + } + + actual, err := queues.Share(fake.ServiceClient(), QueueName, shareOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedShare, actual) +} diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index 1c541e47ad..23324135a8 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -49,3 +49,7 @@ func deleteURL(client *gophercloud.ServiceClient, queueName string) string { func statURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName, "stats") } + +func shareURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "share") +} From b06207adc597d227dd874274305c96e49c3015e3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 17 Apr 2018 14:25:06 -0600 Subject: [PATCH 0308/2296] Networking v2: CIDR is not required for subnets (#940) --- openstack/networking/v2/subnets/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 597a4e77f3..f3c1164944 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -80,7 +80,7 @@ type CreateOpts struct { NetworkID string `json:"network_id" required:"true"` // CIDR is the address CIDR of the subnet. - CIDR string `json:"cidr" required:"true"` + CIDR string `json:"cidr"` // Name is a human-readable name of the subnet. Name string `json:"name,omitempty"` From 6a08a65ee4d462b2dba502d08aa06e38f0bf98a3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 17 Apr 2018 15:07:25 -0600 Subject: [PATCH 0309/2296] Fix required tag for queries and headers (#913) This commit fixes how the required tag is set for query and header parameters. Instead of using x:"name,required" as before, it now uses x:"name" required:"true" --- .../objectstorage/v1/swauth/testing/requests_test.go | 8 ++++++++ params.go | 12 +++++------- testing/params_test.go | 6 +++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/openstack/objectstorage/v1/swauth/testing/requests_test.go b/openstack/objectstorage/v1/swauth/testing/requests_test.go index 57b503463f..46571f6117 100644 --- a/openstack/objectstorage/v1/swauth/testing/requests_test.go +++ b/openstack/objectstorage/v1/swauth/testing/requests_test.go @@ -25,3 +25,11 @@ func TestAuth(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, swiftClient.TokenID, AuthResult.Token) } + +func TestBadAuth(t *testing.T) { + authOpts := swauth.AuthOpts{} + _, err := authOpts.ToAuthOptsMap() + if err == nil { + t.Fatalf("Expected an error due to missing auth options") + } +} diff --git a/params.go b/params.go index 28ad906856..19b8cf7bf8 100644 --- a/params.go +++ b/params.go @@ -363,9 +363,8 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { } } } else { - // Otherwise, the field is not set. - if len(tags) == 2 && tags[1] == "required" { - // And the field is required. Return an error. + // if the field has a 'required' tag, it can't have a zero-value + if requiredTag := f.Tag.Get("required"); requiredTag == "true" { return &url.URL{}, fmt.Errorf("Required query parameter [%s] not set.", f.Name) } } @@ -439,10 +438,9 @@ func BuildHeaders(opts interface{}) (map[string]string, error) { optsMap[tags[0]] = strconv.FormatBool(v.Bool()) } } else { - // Otherwise, the field is not set. - if len(tags) == 2 && tags[1] == "required" { - // And the field is required. Return an error. - return optsMap, fmt.Errorf("Required header not set.") + // if the field has a 'required' tag, it can't have a zero-value + if requiredTag := f.Tag.Get("required"); requiredTag == "true" { + return optsMap, fmt.Errorf("Required header [%s] not set.", f.Name) } } } diff --git a/testing/params_test.go b/testing/params_test.go index 18d6704d95..6025366495 100644 --- a/testing/params_test.go +++ b/testing/params_test.go @@ -39,7 +39,7 @@ func TestBuildQueryString(t *testing.T) { iFalse := false opts := struct { J int `q:"j"` - R string `q:"r,required"` + R string `q:"r" required:"true"` C bool `q:"c"` S []string `q:"s"` TS []testVar `q:"ts"` @@ -65,7 +65,7 @@ func TestBuildQueryString(t *testing.T) { opts = struct { J int `q:"j"` - R string `q:"r,required"` + R string `q:"r" required:"true"` C bool `q:"c"` S []string `q:"s"` TS []testVar `q:"ts"` @@ -91,7 +91,7 @@ func TestBuildQueryString(t *testing.T) { func TestBuildHeaders(t *testing.T) { testStruct := struct { Accept string `h:"Accept"` - Num int `h:"Number,required"` + Num int `h:"Number" required:"true"` Style bool `h:"Style"` }{ Accept: "application/json", From e02a6935a9a628ff8694e6072b9d0aac22623abd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 17 Apr 2018 15:29:20 -0600 Subject: [PATCH 0310/2296] Fix query tag format (#942) --- openstack/clustering/v1/webhooks/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/clustering/v1/webhooks/requests.go b/openstack/clustering/v1/webhooks/requests.go index 0b38861b98..7ceff87225 100644 --- a/openstack/clustering/v1/webhooks/requests.go +++ b/openstack/clustering/v1/webhooks/requests.go @@ -9,7 +9,7 @@ import ( // TriggerOpts represents options used for triggering an action type TriggerOpts struct { - V string `q:"V,required"` + V string `q:"V" required:"true"` Params map[string]string } From 29842f990b3db904777ca78e38128196234e680d Mon Sep 17 00:00:00 2001 From: Kevin Zhao Date: Tue, 13 Feb 2018 09:47:36 -0600 Subject: [PATCH 0311/2296] Add support for Zun experimental API "Capsule"Get (#725) * Add support for Zun experimental API "Capsule" get Also add the test case and acceptance test. Signed-off-by: Kevin Zhao * Add SubnetID to Addresses Signed-off-by: Kevin Zhao --- acceptance/clients/clients.go | 19 ++++ .../container/experimental/capsules_test.go | 28 +++++ openstack/client.go | 5 + .../container/experimental/capsules/doc.go | 4 + .../experimental/capsules/requests.go | 13 +++ .../experimental/capsules/results.go | 107 ++++++++++++++++++ .../experimental/capsules/testing/doc.go | 1 + .../experimental/capsules/testing/fixtures.go | 68 +++++++++++ .../capsules/testing/requests_test.go | 91 +++++++++++++++ .../container/experimental/capsules/urls.go | 7 ++ results.go | 21 ++++ 11 files changed, 364 insertions(+) create mode 100644 acceptance/openstack/container/experimental/capsules_test.go create mode 100644 openstack/container/experimental/capsules/doc.go create mode 100644 openstack/container/experimental/capsules/requests.go create mode 100644 openstack/container/experimental/capsules/results.go create mode 100644 openstack/container/experimental/capsules/testing/doc.go create mode 100644 openstack/container/experimental/capsules/testing/fixtures.go create mode 100644 openstack/container/experimental/capsules/testing/requests_test.go create mode 100644 openstack/container/experimental/capsules/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index d664f90109..29adb0a1da 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -521,6 +521,25 @@ func NewMessagingV2Client(clientID string) (*gophercloud.ServiceClient, error) { }) } +// NewContainerExperimentalClient returns a *ServiceClient for making calls +// to the OpenStack Container Experimental API. An error will be returned +// if authentication or client creation was not possible. +func NewContainerExperimentalClient() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + return openstack.NewContainerExperimental(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} + // configureDebug will configure the provider client to print the API // requests and responses if OS_DEBUG is enabled. func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderClient { diff --git a/acceptance/openstack/container/experimental/capsules_test.go b/acceptance/openstack/container/experimental/capsules_test.go new file mode 100644 index 0000000000..5c037aa201 --- /dev/null +++ b/acceptance/openstack/container/experimental/capsules_test.go @@ -0,0 +1,28 @@ +package experimental + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/container/experimental/capsules" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCapsuleGet(t *testing.T) { + client, err := clients.NewContainerExperimentalClient() + if err != nil { + t.Fatalf("Unable to create an container experimental client: %v", err) + } + th.AssertNoErr(t, err) + capsuleUUID := "e6c913bb-b4e4-409d-8b71-3e029f196458" + if capsuleUUID == "" { + t.Fatalf("In order to retrieve a capsule, the CapsuleUUID must be set") + } + capsule, err := capsules.Get(client, capsuleUUID).Extract() + // Get a capsule + + th.AssertNoErr(t, err) + th.AssertEquals(t, capsule.Status, "Running") + th.AssertEquals(t, capsule.MetaName, "template") + th.AssertEquals(t, capsule.CPU, float64(2.0)) +} diff --git a/openstack/client.go b/openstack/client.go index b9e187ddaa..2847d8da65 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -408,3 +408,8 @@ func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo goph sc.MoreHeaders = map[string]string{"Client-ID": clientID} return sc, err } + +// NewContainerExperimental creates a ServiceClient that may be used with experimental container package +func NewContainerExperimental(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "container-experimental") +} diff --git a/openstack/container/experimental/capsules/doc.go b/openstack/container/experimental/capsules/doc.go new file mode 100644 index 0000000000..5237759eb2 --- /dev/null +++ b/openstack/container/experimental/capsules/doc.go @@ -0,0 +1,4 @@ +// Package capsules contains functionality for working with Zun capsule +// resources. A capsule is a container group, as the co-located and +// co-scheduled unit, is the same like pod in Kubernetes. +package capsules diff --git a/openstack/container/experimental/capsules/requests.go b/openstack/container/experimental/capsules/requests.go new file mode 100644 index 0000000000..25b1ffa875 --- /dev/null +++ b/openstack/container/experimental/capsules/requests.go @@ -0,0 +1,13 @@ +package capsules + +import ( + "github.com/gophercloud/gophercloud" +) + +// Get requests details on a single capsule, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + }) + return +} diff --git a/openstack/container/experimental/capsules/results.go b/openstack/container/experimental/capsules/results.go new file mode 100644 index 0000000000..26b56e45e1 --- /dev/null +++ b/openstack/container/experimental/capsules/results.go @@ -0,0 +1,107 @@ +package capsules + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a capsule resource. +func (r commonResult) Extract() (*Capsule, error) { + var s *Capsule + err := r.ExtractInto(&s) + return s, err +} + +// GetResult represents the result of a get operation. +type GetResult struct { + commonResult +} + +// Represents a Container Orchestration Engine Bay, i.e. a cluster +type Capsule struct { + // UUID for the capsule + UUID string `json:"uuid"` + + // ID for the capsule + ID int `json:"id"` + + // User ID for the capsule + UserID string `json:"user_id"` + + // Project ID for the capsule + ProjectID string `json:"project_id"` + + // cpu for the capsule + CPU float64 `json:"cpu"` + + // Memory for the capsule + Memory string `json:"memory"` + + // The name of the capsule + MetaName string `json:"meta_name"` + + // Indicates whether capsule is currently operational. Possible values include: + // Running, + Status string `json:"status"` + + // The created time of the capsule. + CreatedAt time.Time `json:"-"` + + // The updated time of the capsule. + UpdatedAt time.Time `json:"-"` + + // Links includes HTTP references to the itself, useful for passing along to + // other APIs that might want a server reference. + Links []interface{} `json:"links"` + + // The capsule version + CapsuleVersion string `json:"capsule_version"` + + // The capsule restart policy + RestartPolicy string `json:"restart_policy"` + + // The capsule metadata labels + MetaLabels map[string]string `json:"meta_labels"` + + // The list of containers uuids inside capsule. + ContainersUUIDs []string `json:"containers_uuids"` + + // The capsule IP addresses + Addresses map[string][]Address `json:"addresses"` + + // The capsule volume attached information + VolumesInfo map[string][]string `json:"volumes_info"` +} + +type Address struct { + PreserveOnDelete bool `json:"preserve_on_delete"` + Addr string `json:"addr"` + Port string `json:"port"` + Version float64 `json:"version"` + SubnetID string `json:"subnet_id"` +} + +func (r *Capsule) UnmarshalJSON(b []byte) error { + type tmp Capsule + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoT `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339ZNoT `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Capsule(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} diff --git a/openstack/container/experimental/capsules/testing/doc.go b/openstack/container/experimental/capsules/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/container/experimental/capsules/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/container/experimental/capsules/testing/fixtures.go b/openstack/container/experimental/capsules/testing/fixtures.go new file mode 100644 index 0000000000..017d12bc7e --- /dev/null +++ b/openstack/container/experimental/capsules/testing/fixtures.go @@ -0,0 +1,68 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fakeclient "github.com/gophercloud/gophercloud/testhelper/client" +) + +type imageEntry struct { + ID string + JSON string +} + +// HandleImageGetSuccessfully test setup +func HandleCapsuleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, `{ + "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", + "status": "Running", + "id": 1, + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "cpu": 1, + "memory": "1024M", + "meta_name": "test", + "meta_labels": {"web": "app"}, + "created_at": "2018-01-12 09:37:25+00:00", + "updated_at": "2018-01-12 09:37:25+01:00", + "links": [ + { + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self" + }, + { + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark" + } + ], + "capsule_version": "beta", + "restart_policy": "always", + "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "d1469e8d-bcbc-43fc-b163-8b9b6a740930"], + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "volumes_info": { + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ + "4b725a92-2197-497b-b6b1-fb8caa4cb99b" + ] + } + }`) + }) +} diff --git a/openstack/container/experimental/capsules/testing/requests_test.go b/openstack/container/experimental/capsules/testing/requests_test.go new file mode 100644 index 0000000000..8f7b64b39c --- /dev/null +++ b/openstack/container/experimental/capsules/testing/requests_test.go @@ -0,0 +1,91 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/container/experimental/capsules" + th "github.com/gophercloud/gophercloud/testhelper" + fakeclient "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGetCapsule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCapsuleGetSuccessfully(t) + + actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), "cc654059-1a77-47a3-bfcf-715bde5aad9e").Extract() + + th.AssertNoErr(t, err) + + uuid := "cc654059-1a77-47a3-bfcf-715bde5aad9e" + status := "Running" + id := 1 + userID := "d33b18c384574fd2a3299447aac285f0" + projectID := "6b8ffef2a0ac42ee87887b9cc98bdf68" + cpu := float64(1) + memory := "1024M" + metaName := "test" + + createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") + updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+01:00") + links := []interface{}{ + map[string]interface{}{ + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark", + }, + } + capsuleVersion := "beta" + restartPolicy := "always" + metaLabels := map[string]string{ + "web": "app", + } + containersUUIDs := []string{ + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", + "d1469e8d-bcbc-43fc-b163-8b9b6a740930", + } + addresses := map[string][]capsules.Address{ + "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ + { + PreserveOnDelete: false, + Addr: "172.24.4.11", + Port: "8439060f-381a-4386-a518-33d5a4058636", + Version: float64(4), + SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", + }, + }, + } + volumesInfo := map[string][]string{ + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{ + "4b725a92-2197-497b-b6b1-fb8caa4cb99b", + }, + } + + expectedCapsule := capsules.Capsule{ + UUID: uuid, + ID: id, + UserID: userID, + ProjectID: projectID, + CPU: cpu, + Status: status, + Memory: memory, + MetaName: metaName, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + Links: links, + CapsuleVersion: capsuleVersion, + RestartPolicy: restartPolicy, + MetaLabels: metaLabels, + ContainersUUIDs: containersUUIDs, + Addresses: addresses, + VolumesInfo: volumesInfo, + } + + th.AssertDeepEquals(t, &expectedCapsule, actualCapsule) +} diff --git a/openstack/container/experimental/capsules/urls.go b/openstack/container/experimental/capsules/urls.go new file mode 100644 index 0000000000..c3baf01a84 --- /dev/null +++ b/openstack/container/experimental/capsules/urls.go @@ -0,0 +1,7 @@ +package capsules + +import "github.com/gophercloud/gophercloud" + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("capsules", id) +} diff --git a/results.go b/results.go index e64feee19e..fdd4830ec1 100644 --- a/results.go +++ b/results.go @@ -345,6 +345,27 @@ func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error { return nil } +// RFC3339ZNoT is the time format used in Zun (Containers Service). +const RFC3339ZNoT = "2006-01-02 15:04:05-07:00" + +type JSONRFC3339ZNoT time.Time + +func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339ZNoT, s) + if err != nil { + return err + } + *jt = JSONRFC3339ZNoT(t) + return nil +} + /* Link is an internal type to be used in packages of collection resources that are paginated in a certain way. From 10a32ceb59cc4f9a28693ec7fadc1e440b590ffe Mon Sep 17 00:00:00 2001 From: Kevin Zhao Date: Tue, 6 Mar 2018 16:04:23 +0800 Subject: [PATCH 0312/2296] Move capsule from experimental to v1 Refer to: https://review.openstack.org/543878 Signed-off-by: Kevin Zhao --- acceptance/clients/clients.go | 8 ++++---- .../container/{experimental => v1}/capsules_test.go | 8 ++++---- openstack/client.go | 6 +++--- openstack/container/{experimental => v1}/capsules/doc.go | 0 .../container/{experimental => v1}/capsules/requests.go | 0 .../container/{experimental => v1}/capsules/results.go | 0 .../{experimental => v1}/capsules/testing/doc.go | 0 .../{experimental => v1}/capsules/testing/fixtures.go | 0 .../capsules/testing/requests_test.go | 2 +- openstack/container/{experimental => v1}/capsules/urls.go | 0 10 files changed, 12 insertions(+), 12 deletions(-) rename acceptance/openstack/container/{experimental => v1}/capsules_test.go (72%) rename openstack/container/{experimental => v1}/capsules/doc.go (100%) rename openstack/container/{experimental => v1}/capsules/requests.go (100%) rename openstack/container/{experimental => v1}/capsules/results.go (100%) rename openstack/container/{experimental => v1}/capsules/testing/doc.go (100%) rename openstack/container/{experimental => v1}/capsules/testing/fixtures.go (100%) rename openstack/container/{experimental => v1}/capsules/testing/requests_test.go (96%) rename openstack/container/{experimental => v1}/capsules/urls.go (100%) diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 29adb0a1da..c07fecf22a 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -521,10 +521,10 @@ func NewMessagingV2Client(clientID string) (*gophercloud.ServiceClient, error) { }) } -// NewContainerExperimentalClient returns a *ServiceClient for making calls -// to the OpenStack Container Experimental API. An error will be returned +// NewContainerV1Client returns a *ServiceClient for making calls +// to the OpenStack Container V1 API. An error will be returned // if authentication or client creation was not possible. -func NewContainerExperimentalClient() (*gophercloud.ServiceClient, error) { +func NewContainerV1Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err @@ -535,7 +535,7 @@ func NewContainerExperimentalClient() (*gophercloud.ServiceClient, error) { return nil, err } - return openstack.NewContainerExperimental(client, gophercloud.EndpointOpts{ + return openstack.NewContainerV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } diff --git a/acceptance/openstack/container/experimental/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go similarity index 72% rename from acceptance/openstack/container/experimental/capsules_test.go rename to acceptance/openstack/container/v1/capsules_test.go index 5c037aa201..41dad46339 100644 --- a/acceptance/openstack/container/experimental/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -1,17 +1,17 @@ -package experimental +package v1 import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/container/experimental/capsules" + "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCapsuleGet(t *testing.T) { - client, err := clients.NewContainerExperimentalClient() + client, err := clients.NewContainerV1Client() if err != nil { - t.Fatalf("Unable to create an container experimental client: %v", err) + t.Fatalf("Unable to create an container v1 client: %v", err) } th.AssertNoErr(t, err) capsuleUUID := "e6c913bb-b4e4-409d-8b71-3e029f196458" diff --git a/openstack/client.go b/openstack/client.go index 2847d8da65..0178d358eb 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -409,7 +409,7 @@ func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo goph return sc, err } -// NewContainerExperimental creates a ServiceClient that may be used with experimental container package -func NewContainerExperimental(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "container-experimental") +// NewContainerV1 creates a ServiceClient that may be used with v1 container package +func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "container") } diff --git a/openstack/container/experimental/capsules/doc.go b/openstack/container/v1/capsules/doc.go similarity index 100% rename from openstack/container/experimental/capsules/doc.go rename to openstack/container/v1/capsules/doc.go diff --git a/openstack/container/experimental/capsules/requests.go b/openstack/container/v1/capsules/requests.go similarity index 100% rename from openstack/container/experimental/capsules/requests.go rename to openstack/container/v1/capsules/requests.go diff --git a/openstack/container/experimental/capsules/results.go b/openstack/container/v1/capsules/results.go similarity index 100% rename from openstack/container/experimental/capsules/results.go rename to openstack/container/v1/capsules/results.go diff --git a/openstack/container/experimental/capsules/testing/doc.go b/openstack/container/v1/capsules/testing/doc.go similarity index 100% rename from openstack/container/experimental/capsules/testing/doc.go rename to openstack/container/v1/capsules/testing/doc.go diff --git a/openstack/container/experimental/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go similarity index 100% rename from openstack/container/experimental/capsules/testing/fixtures.go rename to openstack/container/v1/capsules/testing/fixtures.go diff --git a/openstack/container/experimental/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go similarity index 96% rename from openstack/container/experimental/capsules/testing/requests_test.go rename to openstack/container/v1/capsules/testing/requests_test.go index 8f7b64b39c..016ad0d277 100644 --- a/openstack/container/experimental/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -5,7 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/container/experimental/capsules" + "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) diff --git a/openstack/container/experimental/capsules/urls.go b/openstack/container/v1/capsules/urls.go similarity index 100% rename from openstack/container/experimental/capsules/urls.go rename to openstack/container/v1/capsules/urls.go From a82a5437ede55f7618af8ca3b8900533ce557d6e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 18 Apr 2018 03:19:38 +0000 Subject: [PATCH 0313/2296] Object Storage v1: Support string results for X-Static-Large-Object --- openstack/objectstorage/v1/objects/results.go | 40 ++++++++++++++----- .../v1/objects/testing/fixtures.go | 2 + .../v1/objects/testing/requests_test.go | 11 +++-- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index a47b39343c..49a0abcdb6 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -134,7 +134,7 @@ type DownloadHeader struct { ETag string `json:"Etag"` LastModified time.Time `json:"-"` ObjectManifest string `json:"X-Object-Manifest"` - StaticLargeObject bool `json:"X-Static-Large-Object"` + StaticLargeObject bool `json:"-"` TransID string `json:"X-Trans-Id"` } @@ -142,10 +142,11 @@ func (r *DownloadHeader) UnmarshalJSON(b []byte) error { type tmp DownloadHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` - DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` - LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` + ContentLength string `json:"Content-Length"` + Date gophercloud.JSONRFC1123 `json:"Date"` + DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` + LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` + StaticLargeObject interface{} `json:"X-Static-Large-Object"` } err := json.Unmarshal(b, &s) if err != nil { @@ -164,6 +165,15 @@ func (r *DownloadHeader) UnmarshalJSON(b []byte) error { } } + switch t := s.StaticLargeObject.(type) { + case string: + if t == "True" || t == "true" { + r.StaticLargeObject = true + } + case bool: + r.StaticLargeObject = t + } + r.Date = time.Time(s.Date) r.DeleteAt = time.Time(s.DeleteAt) r.LastModified = time.Time(s.LastModified) @@ -214,7 +224,7 @@ type GetHeader struct { ETag string `json:"Etag"` LastModified time.Time `json:"-"` ObjectManifest string `json:"X-Object-Manifest"` - StaticLargeObject bool `json:"X-Static-Large-Object"` + StaticLargeObject bool `json:"-"` TransID string `json:"X-Trans-Id"` } @@ -222,10 +232,11 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` - DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` - LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` + ContentLength string `json:"Content-Length"` + Date gophercloud.JSONRFC1123 `json:"Date"` + DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` + LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` + StaticLargeObject interface{} `json:"X-Static-Large-Object"` } err := json.Unmarshal(b, &s) if err != nil { @@ -244,6 +255,15 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { } } + switch t := s.StaticLargeObject.(type) { + case string: + if t == "True" || t == "true" { + r.StaticLargeObject = true + } + case bool: + r.StaticLargeObject = t + } + r.Date = time.Time(s.Date) r.DeleteAt = time.Time(s.DeleteAt) r.LastModified = time.Time(s.LastModified) diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index a6b7e571e1..0ede1d6201 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -21,6 +21,7 @@ func HandleDownloadObjectSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Date", "Wed, 10 Nov 2009 23:00:00 GMT") + w.Header().Set("X-Static-Large-Object", "True") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Successful download with Gophercloud") }) @@ -243,6 +244,7 @@ func HandleGetObjectSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.Header().Add("X-Object-Meta-Gophercloud-Test", "objects") + w.Header().Add("X-Static-Large-Object", "true") w.WriteHeader(http.StatusNoContent) }) } diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 6825d34426..c12ae48bf0 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -44,9 +44,10 @@ func TestDownloadExtraction(t *testing.T) { th.CheckEquals(t, "Successful download with Gophercloud", string(bytes)) expected := &objects.DownloadHeader{ - ContentLength: 36, - ContentType: "text/plain; charset=utf-8", - Date: time.Date(2009, time.November, 10, 23, 0, 0, 0, loc), + ContentLength: 36, + ContentType: "text/plain; charset=utf-8", + Date: time.Date(2009, time.November, 10, 23, 0, 0, 0, loc), + StaticLargeObject: true, } actual, err := response.Extract() th.AssertNoErr(t, err) @@ -232,4 +233,8 @@ func TestGetObject(t *testing.T) { actual, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) + + actualHeaders, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", nil).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, actualHeaders.StaticLargeObject, true) } From 88240b85162e0b58faa5ec9a50f4d33b0d5d1649 Mon Sep 17 00:00:00 2001 From: wayne Date: Tue, 17 Apr 2018 23:40:12 -0500 Subject: [PATCH 0314/2296] Blockstorage v3 quota-set support - part 4, Update (#880) --- .../blockstorage/v3/quotaset_test.go | 54 ++++++++ .../blockstorage/extensions/quotasets/doc.go | 13 ++ .../extensions/quotasets/requests.go | 58 +++++++++ .../extensions/quotasets/results.go | 6 + .../extensions/quotasets/testing/fixtures.go | 117 ++++++++++++------ .../quotasets/testing/requests_test.go | 79 ++++++++---- .../blockstorage/extensions/quotasets/urls.go | 4 + 7 files changed, 265 insertions(+), 66 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index 84b93858bf..17d3eb1733 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -40,6 +40,51 @@ func TestQuotasetGetUsage(t *testing.T) { tools.PrintResource(t, quotaSetUsage) } +var UpdateQuotaOpts = quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(100), + Snapshots: gophercloud.IntToPointer(200), + Gigabytes: gophercloud.IntToPointer(300), + PerVolumeGigabytes: gophercloud.IntToPointer(50), + Backups: gophercloud.IntToPointer(2), + BackupGigabytes: gophercloud.IntToPointer(300), +} + +var UpdatedQuotas = quotasets.QuotaSet{ + Volumes: 100, + Snapshots: 200, + Gigabytes: 300, + PerVolumeGigabytes: 50, + Backups: 2, + BackupGigabytes: 300, +} + +func TestQuotasetUpdate(t *testing.T) { + client, projectID := getClientAndProject(t) + + // save original quotas + orig, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + + defer func() { + restore := quotasets.UpdateOpts{} + FillUpdateOptsFromQuotaSet(*orig, &restore) + + _, err = quotasets.Update(client, projectID, restore).Extract() + th.AssertNoErr(t, err) + }() + + // test Update + resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, UpdatedQuotas, *resultQuotas) + + // We dont know the default quotas, so just check if the quotas are not the + // same as before + newQuotas, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) +} + // getClientAndProject reduces boilerplate by returning a new blockstorage v3 // ServiceClient and a project ID obtained from the OS_PROJECT_NAME envvar. func getClientAndProject(t *testing.T) (*gophercloud.ServiceClient, string) { @@ -50,3 +95,12 @@ func getClientAndProject(t *testing.T) (*gophercloud.ServiceClient, string) { th.AssertNoErr(t, err) return client, projectID } + +func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) { + dest.Volumes = &src.Volumes + dest.Snapshots = &src.Snapshots + dest.Gigabytes = &src.Gigabytes + dest.PerVolumeGigabytes = &src.PerVolumeGigabytes + dest.Backups = &src.Backups + dest.BackupGigabytes = &src.BackupGigabytes +} diff --git a/openstack/blockstorage/extensions/quotasets/doc.go b/openstack/blockstorage/extensions/quotasets/doc.go index cd58644410..9b48292806 100644 --- a/openstack/blockstorage/extensions/quotasets/doc.go +++ b/openstack/blockstorage/extensions/quotasets/doc.go @@ -17,6 +17,19 @@ Example to Get Quota Set Usage panic(err) } + fmt.Printf("%+v\n", quotaset) + +Example to Update a Quota Set + + updateOpts := quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(100), + } + + quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", quotaset) */ package quotasets diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go index 5943dd6d76..29f0d3aba8 100644 --- a/openstack/blockstorage/extensions/quotasets/requests.go +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -24,3 +24,61 @@ func GetUsage(client *gophercloud.ServiceClient, projectID string) (r GetUsageRe _, r.Err = client.Get(u, &r.Body, nil) return } + +// Updates the quotas for the given projectID and returns the new QuotaSet. +func Update(client *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { + reqBody, err := opts.ToBlockStorageQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Put(updateURL(client, projectID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + return r +} + +// 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 { + // Volumes is the number of volumes that are allowed for each project. + Volumes *int `json:"volumes,omitempty"` + + // Snapshots is the number of snapshots that are allowed for each project. + Snapshots *int `json:"snapshots,omitempty"` + + // Gigabytes is the size (GB) of volumes and snapshots that are allowed for + // each project. + Gigabytes *int `json:"gigabytes,omitempty"` + + // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are + // allowed for each project and the specifed volume type. + PerVolumeGigabytes *int `json:"per_volume_gigabytes,omitempty"` + + // Backups is the number of backups that are allowed for each project. + Backups *int `json:"backups,omitempty"` + + // BackupGigabytes is the size (GB) of backups that are allowed for each + // project. + BackupGigabytes *int `json:"backup_gigabytes,omitempty"` + + // Groups is the number of groups that are allowed for each project. + Groups *int `json:"groups,omitempty"` + + // Force will update the quotaset even if the quota has already been used + // and the reserved quota exceeds the new quota. + Force bool `json:"force,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) + ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) +} + +// ToBlockStorageQuotaUpdateMap builds the update options into a serializable +// format. +func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "quota_set") +} diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/extensions/quotasets/results.go index fec3b10474..0e22ce17aa 100644 --- a/openstack/blockstorage/extensions/quotasets/results.go +++ b/openstack/blockstorage/extensions/quotasets/results.go @@ -132,6 +132,12 @@ 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 +} + type quotaUsageResult struct { gophercloud.Result } diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go index aa31b5a0ef..24ad444081 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go +++ b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go @@ -5,6 +5,7 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" @@ -12,18 +13,7 @@ import ( const FirstTenantID = "555544443333222211110000ffffeeee" -var successTestCases = []struct { - name string - httpMethod string - jsonBody string - uriPath string - uriQueryParams map[string]string - expectedQuotaSet quotasets.QuotaSet - expectedQuotaUsageSet quotasets.QuotaUsageSet -}{ - { - name: "simple GET request", - jsonBody: ` +var getExpectedJSONBody = ` { "quota_set" : { "volumes" : 8, @@ -33,22 +23,18 @@ var successTestCases = []struct { "backups" : 12, "backup_gigabytes" : 13 } -}`, - expectedQuotaSet: quotasets.QuotaSet{ - Volumes: 8, - Snapshots: 9, - Gigabytes: 10, - PerVolumeGigabytes: 11, - Backups: 12, - BackupGigabytes: 13, - }, - uriPath: "/os-quota-sets/" + FirstTenantID, - httpMethod: "GET", - }, +}` + +var getExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 8, + Snapshots: 9, + Gigabytes: 10, + PerVolumeGigabytes: 11, + Backups: 12, + BackupGigabytes: 13, +} - { - name: "GET details request", - jsonBody: ` +var getUsageExpectedJSONBody = ` { "quota_set" : { "id": "555544443333222211110000ffffeeee", @@ -84,22 +70,71 @@ var successTestCases = []struct { } } } -}`, - expectedQuotaUsageSet: quotasets.QuotaUsageSet{ - ID: FirstTenantID, - Volumes: quotasets.QuotaUsage{InUse: 15, Limit: 16, Reserved: 17}, - Snapshots: quotasets.QuotaUsage{InUse: 18, Limit: 19, Reserved: 20}, - Gigabytes: quotasets.QuotaUsage{InUse: 21, Limit: 22, Reserved: 23}, - PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, - Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, - BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, - }, - uriPath: "/os-quota-sets/" + FirstTenantID, - uriQueryParams: map[string]string{"usage": "true"}, - httpMethod: "GET", - }, +}` + +var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ + ID: FirstTenantID, + Volumes: quotasets.QuotaUsage{InUse: 15, Limit: 16, Reserved: 17}, + Snapshots: quotasets.QuotaUsage{InUse: 18, Limit: 19, Reserved: 20}, + Gigabytes: quotasets.QuotaUsage{InUse: 21, Limit: 22, Reserved: 23}, + PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, + Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, + BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, +} + +var fullUpdateExpectedJSONBody = ` +{ + "quota_set": { + "volumes": 8, + "snapshots": 9, + "gigabytes": 10, + "per_volume_gigabytes": 11, + "backups": 12, + "backup_gigabytes": 13 + } +}` + +var fullUpdateOpts = quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(8), + Snapshots: gophercloud.IntToPointer(9), + Gigabytes: gophercloud.IntToPointer(10), + PerVolumeGigabytes: gophercloud.IntToPointer(11), + Backups: gophercloud.IntToPointer(12), + BackupGigabytes: gophercloud.IntToPointer(13), +} + +var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 8, + Snapshots: 9, + Gigabytes: 10, + PerVolumeGigabytes: 11, + Backups: 12, + BackupGigabytes: 13, } +var partialUpdateExpectedJSONBody = ` +{ + "quota_set": { + "volumes": 200, + "snapshots": 0, + "gigabytes": 0, + "per_volume_gigabytes": 0, + "backups": 0, + "backup_gigabytes": 0 + } +}` + +var partialUpdateOpts = quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(200), + Snapshots: gophercloud.IntToPointer(0), + Gigabytes: gophercloud.IntToPointer(0), + PerVolumeGigabytes: gophercloud.IntToPointer(0), + Backups: gophercloud.IntToPointer(0), + BackupGigabytes: gophercloud.IntToPointer(0), +} + +var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{Volumes: 200} + // HandleSuccessfulRequest configures the test server to respond to an HTTP request. func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { diff --git a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go index 4379d50d63..0b01321f8e 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go +++ b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "errors" "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" @@ -8,35 +9,63 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) -var emptyQuotaSet = quotasets.QuotaSet{} -var emptyQuotaUsageSet = quotasets.QuotaUsageSet{} +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &getExpectedQuotaSet, actual) +} -func testSuccessTestCase(t *testing.T, httpMethod, uriPath, jsonBody string, uriQueryParams map[string]string, expectedQuotaSet quotasets.QuotaSet, expectedQuotaUsageSet quotasets.QuotaUsageSet) error { +func TestGetUsage(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - HandleSuccessfulRequest(t, httpMethod, uriPath, jsonBody, uriQueryParams) - - if expectedQuotaSet != emptyQuotaSet { - actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() - if err != nil { - return err - } - th.CheckDeepEquals(t, &expectedQuotaSet, actual) - } else if expectedQuotaUsageSet != emptyQuotaUsageSet { - actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID).Extract() - if err != nil { - return err - } - th.CheckDeepEquals(t, expectedQuotaUsageSet, actual) - } - return nil + + uriQueryParms := map[string]string{"usage": "true"} + HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getUsageExpectedJSONBody, uriQueryParms) + actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, getUsageExpectedQuotaSet, actual) +} + +func TestFullUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, fullUpdateExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, fullUpdateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &fullUpdateExpectedQuotaSet, actual) } -func TestSuccessTestCases(t *testing.T) { - for _, tt := range successTestCases { - err := testSuccessTestCase(t, tt.httpMethod, tt.uriPath, tt.jsonBody, tt.uriQueryParams, tt.expectedQuotaSet, tt.expectedQuotaUsageSet) - if err != nil { - t.Fatalf("Test case '%s' failed with error:\n%s", tt.name, err) - } +func TestPartialUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, partialUpdateExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, partialUpdateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &partiualUpdateExpectedQuotaSet, actual) +} + +type ErrorUpdateOpts quotasets.UpdateOpts + +func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { + return nil, errors.New("This is an error") +} + +func TestErrorInToBlockStorageQuotaUpdateMap(t *testing.T) { + opts := &ErrorUpdateOpts{} + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, "", nil) + _, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() + if err == nil { + t.Fatal("Error handling failed") } } diff --git a/openstack/blockstorage/extensions/quotasets/urls.go b/openstack/blockstorage/extensions/quotasets/urls.go index 737cab03ca..a5c11442d2 100644 --- a/openstack/blockstorage/extensions/quotasets/urls.go +++ b/openstack/blockstorage/extensions/quotasets/urls.go @@ -11,3 +11,7 @@ func getURL(c *gophercloud.ServiceClient, projectID string) string { func getDefaultsURL(c *gophercloud.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID, "defaults") } + +func updateURL(c *gophercloud.ServiceClient, projectID string) string { + return getURL(c, projectID) +} From 482bcee3e503006779c2c99437c0613881160de1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 17 Apr 2018 22:52:34 -0600 Subject: [PATCH 0315/2296] Networking v2: omitempty when CIDR not specified (#943) --- .../openstack/networking/v2/networking.go | 24 ++++++++++ .../openstack/networking/v2/subnets_test.go | 34 +++++++++++++ openstack/networking/v2/subnets/requests.go | 2 +- .../networking/v2/subnets/testing/fixtures.go | 12 +++++ .../v2/subnets/testing/requests_test.go | 48 +++++++++++++++++++ 5 files changed, 119 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index b5dcb6d2c7..2abf4f9180 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -281,6 +281,30 @@ func CreateSubnetWithSubnetPool(t *testing.T, client *gophercloud.ServiceClient, return subnet, nil } +// CreateSubnetWithSubnetPoolNoCIDR will create a subnet associated with the +// provided subnetpool on the specified Network ID. +// An error will be returned if the subnet or the subnetpool could not be created. +func CreateSubnetWithSubnetPoolNoCIDR(t *testing.T, client *gophercloud.ServiceClient, networkID string, subnetPoolID string) (*subnets.Subnet, error) { + subnetName := tools.RandomString("TESTACC-", 8) + createOpts := subnets.CreateOpts{ + NetworkID: networkID, + IPVersion: 4, + Name: subnetName, + EnableDHCP: gophercloud.Disabled, + SubnetPoolID: subnetPoolID, + } + + 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.") + return subnet, nil +} + // DeleteNetwork will delete a network with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index ee665286ed..6eb46074e5 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -191,3 +191,37 @@ func TestSubnetsWithSubnetPool(t *testing.T) { t.Fatalf("A subnet pool was not associated.") } } + +func TestSubnetsWithSubnetPoolNoCIDR(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + // Create SubnetPool + subnetPool, err := subnetpools.CreateSubnetPool(t, client) + if err != nil { + t.Fatalf("Unable to create subnet pool: %v", err) + } + defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) + + // Create Subnet + subnet, err := CreateSubnetWithSubnetPoolNoCIDR(t, client, network.ID, subnetPool.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer DeleteSubnet(t, client, subnet.ID) + + tools.PrintResource(t, subnet) + + if subnet.GatewayIP == "" { + t.Fatalf("A subnet pool was not associated.") + } +} diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index f3c1164944..b77aedee17 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -80,7 +80,7 @@ type CreateOpts struct { NetworkID string `json:"network_id" required:"true"` // CIDR is the address CIDR of the subnet. - CIDR string `json:"cidr"` + CIDR string `json:"cidr,omitempty"` // Name is a human-readable name of the subnet. Name string `json:"name,omitempty"` diff --git a/openstack/networking/v2/subnets/testing/fixtures.go b/openstack/networking/v2/subnets/testing/fixtures.go index 619ea3e55e..9f9af0e658 100644 --- a/openstack/networking/v2/subnets/testing/fixtures.go +++ b/openstack/networking/v2/subnets/testing/fixtures.go @@ -336,6 +336,18 @@ const SubnetCreateWithIPv6RaAddressModeResponse = ` } ` +const SubnetCreateRequestWithNoCIDR = ` +{ + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ip_version": 4, + "dns_nameservers": ["foo"], + "host_routes": [{"destination":"","nexthop": "bar"}], + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" + } +} +` + const SubnetUpdateRequest = ` { "subnet": { diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index 208fc608f9..85fe14fbcc 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -286,6 +286,54 @@ func TestCreateIPv6RaAddressMode(t *testing.T) { th.AssertEquals(t, s.IPv6RAMode, "slaac") } +func TestCreateWithNoCIDR(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/subnets", 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, SubnetCreateRequestWithNoCIDR) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, SubnetCreateResult) + }) + + opts := subnets.CreateOpts{ + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + IPVersion: 4, + DNSNameservers: []string{"foo"}, + HostRoutes: []subnets.HostRoute{ + {NextHop: "bar"}, + }, + SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", + } + s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "") + 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.AllocationPools, []subnets.AllocationPool{ + { + Start: "192.168.199.2", + End: "192.168.199.254", + }, + }) + th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{}) + th.AssertEquals(t, s.IPVersion, 4) + th.AssertEquals(t, s.GatewayIP, "192.168.199.1") + th.AssertEquals(t, s.CIDR, "192.168.199.0/24") + th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126") + th.AssertEquals(t, s.SubnetPoolID, "b80340c7-9960-4f67-a99c-02501656284b") +} + func TestRequiredCreateOpts(t *testing.T) { res := subnets.Create(fake.ServiceClient(), subnets.CreateOpts{}) if res.Err == nil { From 22663d5eb866ce59e5332465f40668ae135bf1a1 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Wed, 11 Apr 2018 10:56:09 -0700 Subject: [PATCH 0316/2296] Add purge function to queues --- .../openstack/messaging/v2/queue_test.go | 24 ++++++++++ openstack/messaging/v2/queues/doc.go | 13 +++++ openstack/messaging/v2/queues/requests.go | 48 +++++++++++++++++-- openstack/messaging/v2/queues/results.go | 5 ++ .../messaging/v2/queues/testing/fixtures.go | 18 +++++++ .../v2/queues/testing/requests_test.go | 13 +++++ openstack/messaging/v2/queues/urls.go | 4 ++ 7 files changed, 122 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/messaging/v2/queue_test.go b/acceptance/openstack/messaging/v2/queue_test.go index 6526cd7aa1..1234bd2fcf 100644 --- a/acceptance/openstack/messaging/v2/queue_test.go +++ b/acceptance/openstack/messaging/v2/queue_test.go @@ -126,3 +126,27 @@ func TestShare(t *testing.T) { tools.PrintResource(t, share) } + +func TestPurge(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-71861300734c" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + queueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, queueName) + + purgeOpts := queues.PurgeOpts{ + ResourceTypes: []queues.PurgeResource{ + queues.ResourceMessages, + }, + } + + t.Logf("Attempting to purge queue: %s", queueName) + purgeErr := queues.Purge(client, queueName, purgeOpts).ExtractErr() + if purgeErr != nil { + t.Fatalf("Unable to purge queue %s: %v", queueName, purgeErr) + } +} diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index 23958358d7..ca97c52a8a 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -94,5 +94,18 @@ Example to Share a queue if err != nil { panic(err) } + +Example to Purge a queue + + purgeOpts := queues.PurgeOpts{ + ResourceTypes: []queues.PurgeResource{ + queues.ResourceMessages, + }, + } + + err := queues.Purge(client, queueName, purgeOpts).ExtractErr() + if err != nil { + panic(err) + } */ package queues diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index c3526eee7b..213c601ea9 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -224,11 +224,11 @@ type ShareOpts struct { // ShareOptsBuilder allows extensions to add additional attributes to the // Share request. type ShareOptsBuilder interface { - ToQueuesShareMap() (map[string]interface{}, error) + ToQueueShareMap() (map[string]interface{}, error) } // ToShareQueueMap formats a ShareOpts structure into a request body. -func (opts ShareOpts) ToQueuesShareMap() (map[string]interface{}, error) { +func (opts ShareOpts) ToQueueShareMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -238,7 +238,7 @@ func (opts ShareOpts) ToQueuesShareMap() (map[string]interface{}, error) { // Share creates a pre-signed URL for a given queue. func Share(client *gophercloud.ServiceClient, queueName string, opts ShareOptsBuilder) (r ShareResult) { - b, err := opts.ToQueuesShareMap() + b, err := opts.ToQueueShareMap() if err != nil { r.Err = err return r @@ -248,3 +248,45 @@ func Share(client *gophercloud.ServiceClient, queueName string, opts ShareOptsBu }) return } + +type PurgeResource string + +const ( + ResourceMessages PurgeResource = "messages" + ResourceSubscriptions PurgeResource = "subscriptions" +) + +// PurgeOpts specifies the purge parameters. +type PurgeOpts struct { + ResourceTypes []PurgeResource `json:"resource_types" required:"true"` +} + +// PurgeOptsBuilder allows extensions to add additional attributes to the +// Purge request. +type PurgeOptsBuilder interface { + ToQueuePurgeMap() (map[string]interface{}, error) +} + +// ToPurgeQueueMap formats a PurgeOpts structure into a request body +func (opts PurgeOpts) ToQueuePurgeMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return b, nil +} + +// Purge purges particular resource of the queue. +func Purge(client *gophercloud.ServiceClient, queueName string, opts PurgeOptsBuilder) (r PurgeResult) { + b, err := opts.ToQueuePurgeMap() + if err != nil { + r.Err = err + return r + } + + _, r.Err = client.Post(purgeURL(client, queueName), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 88de920d56..61005da778 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -49,6 +49,11 @@ type ShareResult struct { gophercloud.Result } +// PurgeResult is the response of a Purge operation. +type PurgeResult struct { + gophercloud.ErrResult +} + // Queue represents a messaging queue. type Queue struct { Href string `json:"href"` diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index aa223a445b..50e7bd4c93 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -33,6 +33,12 @@ const CreateShareRequest = ` "expires": "2016-09-01T00:00:00" }` +// CreatePurgeRequest is a sample request to a purge. +const CreatePurgeRequest = ` +{ + "resource_types": ["messages", "subscriptions"] +}` + // ListQueuesResponse1 is a sample response to a List queues. const ListQueuesResponse1 = ` { @@ -295,3 +301,15 @@ func HandleShareSuccessfully(t *testing.T) { fmt.Fprintf(w, CreateShareResponse) }) } + +// HandlePurgeSuccessfully configures the test server to respond to a Purge request. +func HandlePurgeSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/purge", QueueName), + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, CreatePurgeRequest) + + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index 2dc4e34e94..c4502cdac8 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -118,3 +118,16 @@ func TestShare(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedShare, actual) } + +func TestPurge(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePurgeSuccessfully(t) + + purgeOpts := queues.PurgeOpts{ + ResourceTypes: []queues.PurgeResource{queues.ResourceMessages, queues.ResourceSubscriptions}, + } + + err := queues.Purge(fake.ServiceClient(), QueueName, purgeOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index 23324135a8..e58bdf765d 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -53,3 +53,7 @@ func statURL(client *gophercloud.ServiceClient, queueName string) string { func shareURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName, "share") } + +func purgeURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "purge") +} From e1143ff0345146b5067d4fa397de1a76f853bf57 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 19 Apr 2018 03:56:04 +0000 Subject: [PATCH 0317/2296] Ensure IdentityV3 clients have the right endpoint This commit ensures IdentityV3 clients have the right endpoint upon creation. This handles both versionless endpoints and v2.0 endpoints being returned when doing an endpoint search. --- openstack/client.go | 27 ++++----- openstack/utils/base_endpoint.go | 29 +++++++++ openstack/utils/testing/base_endpoint_test.go | 60 +++++++++++++++++++ 3 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 openstack/utils/base_endpoint.go create mode 100644 openstack/utils/testing/base_endpoint_test.go diff --git a/openstack/client.go b/openstack/client.go index b9e187ddaa..1f9b019987 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -2,10 +2,7 @@ package openstack import ( "fmt" - "net/url" "reflect" - "regexp" - "strings" "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" @@ -38,21 +35,11 @@ A basic example of using this would be: client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) */ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { - u, err := url.Parse(endpoint) + base, err := utils.BaseEndpoint(endpoint) if err != nil { return nil, err } - u.RawQuery, u.Fragment = "", "" - - var base string - versionRe := regexp.MustCompile("v[0-9.]+/?") - if version := versionRe.FindString(u.Path); version != "" { - base = strings.Replace(u.String(), version, "", -1) - } else { - base = u.String() - } - endpoint = gophercloud.NormalizeURL(endpoint) base = gophercloud.NormalizeURL(base) @@ -287,11 +274,17 @@ func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp // Ensure endpoint still has a suffix of v3. // This is because EndpointLocator might have found a versionless - // endpoint and requests will fail unless targeted at /v3. - if !strings.HasSuffix(endpoint, "v3/") { - endpoint = endpoint + "v3/" + // endpoint or the published endpoint is still /v2.0. In both + // cases, we need to fix the endpoint to point to /v3. + base, err := utils.BaseEndpoint(endpoint) + if err != nil { + return nil, err } + base = gophercloud.NormalizeURL(base) + + endpoint = base + "v3/" + return &gophercloud.ServiceClient{ ProviderClient: client, Endpoint: endpoint, diff --git a/openstack/utils/base_endpoint.go b/openstack/utils/base_endpoint.go new file mode 100644 index 0000000000..d6f9e34eaa --- /dev/null +++ b/openstack/utils/base_endpoint.go @@ -0,0 +1,29 @@ +package utils + +import ( + "net/url" + "regexp" + "strings" +) + +// BaseEndpoint will return a URL without the /vX.Y +// portion of the URL. +func BaseEndpoint(endpoint string) (string, error) { + var base string + + u, err := url.Parse(endpoint) + if err != nil { + return base, err + } + + u.RawQuery, u.Fragment = "", "" + + versionRe := regexp.MustCompile("v[0-9.]+/?") + if version := versionRe.FindString(u.Path); version != "" { + base = strings.Replace(u.String(), version, "", -1) + } else { + base = u.String() + } + + return base, nil +} diff --git a/openstack/utils/testing/base_endpoint_test.go b/openstack/utils/testing/base_endpoint_test.go new file mode 100644 index 0000000000..5a8152db2c --- /dev/null +++ b/openstack/utils/testing/base_endpoint_test.go @@ -0,0 +1,60 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/utils" + th "github.com/gophercloud/gophercloud/testhelper" +) + +type endpointTestCases struct { + Endpoint string + BaseEndpoint string +} + +func TestBaseEndpoint(t *testing.T) { + tests := []endpointTestCases{ + { + Endpoint: "http://example.com:5000/v3", + BaseEndpoint: "http://example.com:5000/", + }, + { + Endpoint: "http://example.com:5000/v3.6", + BaseEndpoint: "http://example.com:5000/", + }, + { + Endpoint: "http://example.com:5000/v2.0", + BaseEndpoint: "http://example.com:5000/", + }, + { + Endpoint: "http://example.com:5000/", + BaseEndpoint: "http://example.com:5000/", + }, + { + Endpoint: "http://example.com:5000", + BaseEndpoint: "http://example.com:5000", + }, + { + Endpoint: "http://example.com/identity/v3", + BaseEndpoint: "http://example.com/identity/", + }, + { + Endpoint: "http://example.com/identity/v3.6", + BaseEndpoint: "http://example.com/identity/", + }, + { + Endpoint: "http://example.com/identity/v2.0", + BaseEndpoint: "http://example.com/identity/", + }, + { + Endpoint: "http://example.com/identity/", + BaseEndpoint: "http://example.com/identity/", + }, + } + + for _, test := range tests { + actual, err := utils.BaseEndpoint(test.Endpoint) + th.AssertNoErr(t, err) + th.AssertEquals(t, test.BaseEndpoint, actual) + } +} From 2080d8f7dd5b9a08914714f53e24116f39f41573 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 19 Apr 2018 08:55:52 -0700 Subject: [PATCH 0318/2296] Senlin: Actions List (#856) * senlin-actions-list * Fixed formatting * Fixed to use gophercloud.JSONRFC3339Milli Fixed to use "r" for receiver name * Fixed by making struct names to be consistent with json tags by removing "UUID" from the struct name * Bring back customized JSONRFC3339Milli because Senlin can have empty field for time which causes gophercloud.JSONRFC3339Milli to fail * Created new JSONRFC3339MilliAllowEmpty which uses gophercloud.JSONRFC3339Milli * Added test case for empty and null for time field * Explicitly parse the time format * Use time.Time from float for StartTime and EndTime * Revert "Use time.Time from float for StartTime and EndTime" This reverts commit af59f65d964c57cc0727b9e166d596fb6a4a3939. * Explicitly use time.Time{} when json is null --- .../openstack/clustering/v1/actions_test.go | 33 ++++ openstack/clustering/v1/actions/doc.go | 23 +++ openstack/clustering/v1/actions/requests.go | 44 ++++++ openstack/clustering/v1/actions/results.go | 111 ++++++++++++++ .../clustering/v1/actions/testing/doc.go | 2 + .../v1/actions/testing/requests_test.go | 145 ++++++++++++++++++ openstack/clustering/v1/actions/urls.go | 14 ++ 7 files changed, 372 insertions(+) create mode 100644 acceptance/openstack/clustering/v1/actions_test.go create mode 100644 openstack/clustering/v1/actions/doc.go create mode 100644 openstack/clustering/v1/actions/requests.go create mode 100644 openstack/clustering/v1/actions/results.go create mode 100644 openstack/clustering/v1/actions/testing/doc.go create mode 100644 openstack/clustering/v1/actions/testing/requests_test.go create mode 100644 openstack/clustering/v1/actions/urls.go diff --git a/acceptance/openstack/clustering/v1/actions_test.go b/acceptance/openstack/clustering/v1/actions_test.go new file mode 100644 index 0000000000..9f73916b4b --- /dev/null +++ b/acceptance/openstack/clustering/v1/actions_test.go @@ -0,0 +1,33 @@ +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" + "github.com/gophercloud/gophercloud/pagination" +) + +func TestActionsList(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create a clustering client: %v", err) + } + + opts := actions.ListOpts{ + Limit: 200, + } + + err = actions.List(client, opts).EachPage(func(page pagination.Page) (bool, error) { + actionInfos, err := actions.ExtractActions(page) + if err != nil { + return false, err + } + + for _, actionInfo := range actionInfos { + tools.PrintResource(t, actionInfo) + } + return true, nil + }) +} diff --git a/openstack/clustering/v1/actions/doc.go b/openstack/clustering/v1/actions/doc.go new file mode 100644 index 0000000000..1942f1d07a --- /dev/null +++ b/openstack/clustering/v1/actions/doc.go @@ -0,0 +1,23 @@ +/* +Package actions provides listing and retrieving of senlin actions for the OpenStack Clustering Service. + +Example to list actions + + opts := actions.ListOpts{ + Limit: 5, + } + + err = actions.List(serviceClient, opts).EachPage(func(page pagination.Page) (bool, error) { + actionInfos, err := actions.ExtractActions(page) + if err != nil { + return false, err + } + + for _, actionInfo := range actionInfos { + fmt.Println("%+v\n", actionInfo) + } + return true, nil + }) + +*/ +package actions diff --git a/openstack/clustering/v1/actions/requests.go b/openstack/clustering/v1/actions/requests.go new file mode 100644 index 0000000000..6bd545473d --- /dev/null +++ b/openstack/clustering/v1/actions/requests.go @@ -0,0 +1,44 @@ +package actions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOpts params +type ListOpts struct { + Limit int `q:"limit"` + Marker string `q:"marker"` + Sort string `q:"sort"` + GlobalProject string `q:"global_project"` + Name string `q:"name"` + Target string `q:"target"` + Action string `q:"action"` + Status string `q:"status"` +} + +// ListOptsBuilder builds query string for the ListOpts +type ListOptsBuilder interface { + ToActionListQuery() (string, error) +} + +// ToClusterListQuery builds a query string from ListOpts +func (opts ListOpts) ToActionListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of actions +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToActionListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ActionPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/clustering/v1/actions/results.go b/openstack/clustering/v1/actions/results.go new file mode 100644 index 0000000000..c6709c536d --- /dev/null +++ b/openstack/clustering/v1/actions/results.go @@ -0,0 +1,111 @@ +package actions + +import ( + "encoding/json" + "fmt" + "reflect" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + +// ActionPage contains a single page of all actions from a ListDetails call. +type ActionPage struct { + pagination.LinkedPageBase +} + +// Action represents a Detailed Action +type Action struct { + Action string `json:"action"` + Cause string `json:"cause"` + CreatedAt time.Time `json:"-"` + Data map[string]interface{} `json:"data"` + DependedBy []string `json:"depended_by"` + DependsOn []string `json:"depends_on"` + StartTime float64 `json:"start_time"` + EndTime float64 `json:"end_time"` + ID string `json:"id"` + Inputs map[string]interface{} `json:"inputs"` + Interval int `json:"interval"` + Name string `json:"name"` + Outputs map[string]interface{} `json:"outputs"` + Owner string `json:"owner"` + Project string `json:"project"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + Target string `json:"target"` + Timeout int `json:"timeout"` + UpdatedAt time.Time `json:"-"` + User string `json:"user"` +} + +// ExtractActions provides access to the list of actions in a page acquired from the List operation. +func ExtractActions(r pagination.Page) ([]Action, error) { + var s struct { + Actions []Action `json:"actions"` + } + err := (r.(ActionPage)).ExtractInto(&s) + return s.Actions, err +} + +// IsEmpty determines if a ActionPage contains any results. +func (r ActionPage) IsEmpty() (bool, error) { + actions, err := ExtractActions(r) + return len(actions) == 0, err +} + +func (r *Action) UnmarshalJSON(b []byte) error { + type tmp Action + var s struct { + tmp + CreatedAt interface{} `json:"created_at"` + UpdatedAt interface{} `json:"updated_at"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Action(s.tmp) + + switch t := s.CreatedAt.(type) { + case string: + if t != "" { + r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.CreatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) + } + + switch t := s.UpdatedAt.(type) { + case string: + if t != "" { + r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.UpdatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) + } + + return nil +} diff --git a/openstack/clustering/v1/actions/testing/doc.go b/openstack/clustering/v1/actions/testing/doc.go new file mode 100644 index 0000000000..ffecf4d863 --- /dev/null +++ b/openstack/clustering/v1/actions/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_actions_v1 +package testing diff --git a/openstack/clustering/v1/actions/testing/requests_test.go b/openstack/clustering/v1/actions/testing/requests_test.go new file mode 100644 index 0000000000..31de3cb6ec --- /dev/null +++ b/openstack/clustering/v1/actions/testing/requests_test.go @@ -0,0 +1,145 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListActions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/actions", 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, ` + { + "actions": [ + { + "action": "NODE_DELETE", + "cause": "RPC Request", + "created_at": "2015-11-04T05:21:41Z", + "data": {}, + "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "end_time": 1425550000.0, + "id": "edce3528-864f-41fb-8759-f4707925cc09", + "inputs": {}, + "interval": -1, + "name": "node_delete_f0de9b9c", + "outputs": {}, + "owner": null, + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "start_time": 1425550000.0, + "status": "SUCCEEDED", + "status_reason": "Action completed successfully.", + "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + "timeout": 3600, + "updated_at": "2016-11-04T05:21:41Z", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + }, + { + "action": "NODE_DELETE", + "cause": "RPC Request", + "created_at": null, + "data": {}, + "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "end_time": 1425550000.0, + "id": "edce3528-864f-41fb-8759-f4707925cc09", + "inputs": {}, + "interval": -1, + "name": "node_delete_f0de9b9c", + "outputs": {}, + "owner": null, + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "start_time": 1425550000.0, + "status": "SUCCEEDED", + "status_reason": "Action completed successfully.", + "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + "timeout": 3600, + "updated_at": "", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + } + ] + }`) + }) + + pageCount := 0 + actions.List(fake.ServiceClient(), actions.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pageCount++ + actual, err := actions.ExtractActions(page) + if err != nil { + t.Errorf("Failed to extract actions: %v", err) + return false, err + } + + createdAt, _ := time.Parse(time.RFC3339, "2015-11-04T05:21:41Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-11-04T05:21:41Z") + expected := []actions.Action{ + { + Action: "NODE_DELETE", + Cause: "RPC Request", + CreatedAt: createdAt, + Data: map[string]interface{}{}, + DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + EndTime: 1425550000.0, + ID: "edce3528-864f-41fb-8759-f4707925cc09", + Inputs: make(map[string]interface{}), + Interval: -1, + Name: "node_delete_f0de9b9c", + Outputs: make(map[string]interface{}), + Owner: "", + Project: "f1fe61dcda2f4618a14c10dc7abc214d", + StartTime: 1425550000.0, + Status: "SUCCEEDED", + StatusReason: "Action completed successfully.", + Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + Timeout: 3600, + UpdatedAt: updatedAt, + User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", + }, + { + Action: "NODE_DELETE", + Cause: "RPC Request", + CreatedAt: time.Time{}, + Data: map[string]interface{}{}, + DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + EndTime: 1425550000.0, + ID: "edce3528-864f-41fb-8759-f4707925cc09", + Inputs: make(map[string]interface{}), + Interval: -1, + Name: "node_delete_f0de9b9c", + Outputs: make(map[string]interface{}), + Owner: "", + Project: "f1fe61dcda2f4618a14c10dc7abc214d", + StartTime: 1425550000.0, + Status: "SUCCEEDED", + StatusReason: "Action completed successfully.", + Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + Timeout: 3600, + UpdatedAt: time.Time{}, + User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", + }, + } + + th.AssertDeepEquals(t, expected, actual) + + return true, nil + }) + if pageCount != 1 { + t.Errorf("Expected 1 page, got %d", pageCount) + } +} diff --git a/openstack/clustering/v1/actions/urls.go b/openstack/clustering/v1/actions/urls.go new file mode 100644 index 0000000000..19f7f5c86b --- /dev/null +++ b/openstack/clustering/v1/actions/urls.go @@ -0,0 +1,14 @@ +package actions + +import "github.com/gophercloud/gophercloud" + +var apiVersion = "v1" +var apiName = "actions" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 0071e101c211f51ab4eaf7e615d752293ce03402 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 20 Apr 2018 01:34:07 +0000 Subject: [PATCH 0319/2296] Clustering v1: Fixing acceptance tests --- acceptance/openstack/clustering/v1/actions_test.go | 2 ++ acceptance/openstack/clustering/v1/webhooktrigger_test.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/acceptance/openstack/clustering/v1/actions_test.go b/acceptance/openstack/clustering/v1/actions_test.go index 9f73916b4b..0136c2b443 100644 --- a/acceptance/openstack/clustering/v1/actions_test.go +++ b/acceptance/openstack/clustering/v1/actions_test.go @@ -1,3 +1,5 @@ +// +build acceptance clustering actions + package v1 import ( diff --git a/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/acceptance/openstack/clustering/v1/webhooktrigger_test.go index 4acbabe5f4..16c34cbea5 100644 --- a/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -1,3 +1,5 @@ +// +build acceptance clustering webhooks + package v1 import ( From 469982ac90de7c8779b95b824c46be7564de9eec Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Fri, 6 Apr 2018 16:55:35 -0700 Subject: [PATCH 0320/2296] Add Create for messages --- .../openstack/messaging/v2/message_test.go | 23 +++++++ .../openstack/messaging/v2/messaging.go | 20 ++++++ .../openstack/messaging/v2/queue_test.go | 2 + openstack/messaging/v2/messages/doc.go | 33 +++++++++ openstack/messaging/v2/messages/requests.go | 58 ++++++++++++++++ openstack/messaging/v2/messages/results.go | 25 +++++++ .../messaging/v2/messages/testing/doc.go | 2 + .../messaging/v2/messages/testing/fixtures.go | 67 +++++++++++++++++++ .../v2/messages/testing/requests_test.go | 37 ++++++++++ openstack/messaging/v2/messages/urls.go | 10 +++ openstack/messaging/v2/queues/requests.go | 2 +- 11 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/messaging/v2/message_test.go create mode 100644 openstack/messaging/v2/messages/doc.go create mode 100644 openstack/messaging/v2/messages/requests.go create mode 100644 openstack/messaging/v2/messages/results.go create mode 100644 openstack/messaging/v2/messages/testing/doc.go create mode 100644 openstack/messaging/v2/messages/testing/fixtures.go create mode 100644 openstack/messaging/v2/messages/testing/requests_test.go create mode 100644 openstack/messaging/v2/messages/urls.go diff --git a/acceptance/openstack/messaging/v2/message_test.go b/acceptance/openstack/messaging/v2/message_test.go new file mode 100644 index 0000000000..0d3f09b3a7 --- /dev/null +++ b/acceptance/openstack/messaging/v2/message_test.go @@ -0,0 +1,23 @@ +// +build acceptance messaging messages + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" +) + +func TestCreateMessages(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-71861300734c" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + CreateMessage(t, client, createdQueueName) +} diff --git a/acceptance/openstack/messaging/v2/messaging.go b/acceptance/openstack/messaging/v2/messaging.go index 6e65f04a5c..1c7b8cc6a9 100644 --- a/acceptance/openstack/messaging/v2/messaging.go +++ b/acceptance/openstack/messaging/v2/messaging.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" ) @@ -65,3 +66,22 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient, queueName stri return share, err } + +func CreateMessage(t *testing.T, client *gophercloud.ServiceClient, queueName string) (messages.ResourceList, error) { + t.Logf("Attempting to add message to Queue: %s", queueName) + createOpts := messages.BatchCreateOpts{ + messages.CreateOpts{ + TTL: 300, + Body: map[string]interface{}{"Key": tools.RandomString("ACPTTEST", 8)}, + }, + } + + resource, err := messages.Create(client, queueName, createOpts).Extract() + if err != nil { + t.Fatalf("Unable to add message to queue %s: %v", queueName, err) + } else { + t.Logf("Successfully added message to queue: %s", queueName) + } + + return resource, err +} diff --git a/acceptance/openstack/messaging/v2/queue_test.go b/acceptance/openstack/messaging/v2/queue_test.go index 1234bd2fcf..f03b26f6b5 100644 --- a/acceptance/openstack/messaging/v2/queue_test.go +++ b/acceptance/openstack/messaging/v2/queue_test.go @@ -1,3 +1,5 @@ +// +build acceptance messaging queues + package v2 import ( diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go new file mode 100644 index 0000000000..4412fc2602 --- /dev/null +++ b/openstack/messaging/v2/messages/doc.go @@ -0,0 +1,33 @@ +/* +Package messages provides information and interaction with the messages through +the OpenStack Messaging(Zaqar) service. + +Example to Create Messages + createOpts := messages.CreateOpts{ + Messages: []messages.Messages{ + { + TTL: 300, + Delay: 20, + Body: map[string]interface{}{ + "event": "BackupStarted", + "backup_id": "c378813c-3f0b-11e2-ad92-7823d2b0f3ce", + }, + }, + { + Body: map[string]interface{}{ + "event": "BackupProgress", + "current_bytes": "0", + "total_bytes": "99614720", + }, + }, + }, + } + + queueName = "my_queue" + + resources, err := messages.Create(client, queueName, createOpts).Extract() + if err != nil { + panic(err) + } +*/ +package messages diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go new file mode 100644 index 0000000000..7b925aae34 --- /dev/null +++ b/openstack/messaging/v2/messages/requests.go @@ -0,0 +1,58 @@ +package messages + +import ( + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder Builder. +type CreateOptsBuilder interface { + ToMessageCreateMap() (map[string]interface{}, error) +} + +// BatchCreateOpts is an array of CreateOpts. +type BatchCreateOpts []CreateOpts + +// CreateOpts params to be used with Create. +type CreateOpts struct { + // TTL specifies how long the server waits before marking the message + // as expired and removing it from the queue. + TTL int `json:"ttl,omitempty"` + + // Delay specifies how long the message can be claimed. + Delay int `json:"delay,omitempty"` + + // Body specifies an arbitrary document that constitutes the body of the message being sent. + Body map[string]interface{} `json:"body" required:"true"` +} + +// ToMessageCreateMap constructs a request body from BatchCreateOpts. +func (opts BatchCreateOpts) ToMessageCreateMap() (map[string]interface{}, error) { + messages := make([]map[string]interface{}, len(opts)) + for i, message := range opts { + messageMap, err := message.ToMap() + if err != nil { + return nil, err + } + messages[i] = messageMap + } + return map[string]interface{}{"messages": messages}, nil +} + +// ToMap constructs a request body from UpdateOpts. +func (opts CreateOpts) ToMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create creates a message on a specific queue based of off queue name. +func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToMessageCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(createURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go new file mode 100644 index 0000000000..62b8a60a1b --- /dev/null +++ b/openstack/messaging/v2/messages/results.go @@ -0,0 +1,25 @@ +package messages + +import "github.com/gophercloud/gophercloud" + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Create operations. +type CreateResult struct { + gophercloud.Result +} + +// ResourceList represents the result of creating a message. +type ResourceList struct { + Resources []string `json:"resources"` +} + +// Extract interprets any CreateResult as a ResourceList. +func (r CreateResult) Extract() (ResourceList, error) { + var s ResourceList + err := r.ExtractInto(&s) + return s, err +} diff --git a/openstack/messaging/v2/messages/testing/doc.go b/openstack/messaging/v2/messages/testing/doc.go new file mode 100644 index 0000000000..05931e0235 --- /dev/null +++ b/openstack/messaging/v2/messages/testing/doc.go @@ -0,0 +1,2 @@ +// messages unit tests +package testing diff --git a/openstack/messaging/v2/messages/testing/fixtures.go b/openstack/messaging/v2/messages/testing/fixtures.go new file mode 100644 index 0000000000..537869f4f5 --- /dev/null +++ b/openstack/messaging/v2/messages/testing/fixtures.go @@ -0,0 +1,67 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +// QueueName is the name of the queue +var QueueName = "FakeTestQueue" + +// CreateMessageResponse is a sample response to a Create message. +const CreateMessageResponse = ` +{ + "resources": [ + "/v2/queues/demoqueue/messages/51db6f78c508f17ddc924357", + "/v2/queues/demoqueue/messages/51db6f78c508f17ddc924358" + ] +}` + +// CreateMessageRequest is a sample request to create a message. +const CreateMessageRequest = ` +{ + "messages": [ + { + "body": { + "backup_id": "c378813c-3f0b-11e2-ad92-7823d2b0f3ce", + "event": "BackupStarted" + }, + "delay": 20, + "ttl": 300 + }, + { + "body": { + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720" + } + } + ] +}` + +// ExpectedResources is the expected result in Create +var ExpectedResources = messages.ResourceList{ + Resources: []string{ + "/v2/queues/demoqueue/messages/51db6f78c508f17ddc924357", + "/v2/queues/demoqueue/messages/51db6f78c508f17ddc924358", + }, +} + +// HandleCreateSuccessfully configures the test server to respond to a Create request. +func HandleCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, CreateMessageRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateMessageResponse) + }) +} diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go new file mode 100644 index 0000000000..557dabd849 --- /dev/null +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -0,0 +1,37 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t) + + createOpts := messages.BatchCreateOpts{ + messages.CreateOpts{ + TTL: 300, + Delay: 20, + Body: map[string]interface{}{ + "event": "BackupStarted", + "backup_id": "c378813c-3f0b-11e2-ad92-7823d2b0f3ce", + }, + }, + messages.CreateOpts{ + Body: map[string]interface{}{ + "event": "BackupProgress", + "current_bytes": "0", + "total_bytes": "99614720", + }, + }, + } + + actual, err := messages.Create(fake.ServiceClient(), QueueName, createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedResources, actual) +} diff --git a/openstack/messaging/v2/messages/urls.go b/openstack/messaging/v2/messages/urls.go new file mode 100644 index 0000000000..12154bc998 --- /dev/null +++ b/openstack/messaging/v2/messages/urls.go @@ -0,0 +1,10 @@ +package messages + +import "github.com/gophercloud/gophercloud" + +const ApiVersion = "v2" +const ApiName = "queues" + +func createURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") +} diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 213c601ea9..6ef53947d7 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -128,7 +128,7 @@ type UpdateOptsBuilder interface { ToQueueUpdateMap() ([]map[string]interface{}, error) } -// UpdateOpts is an array of UpdateQueueBody. +// BatchUpdateOpts is an array of UpdateOpts. type BatchUpdateOpts []UpdateOpts // UpdateOpts is the struct responsible for updating a property of a queue. From e25ff659a6d3bba96aff7c840f89270f1ae7df93 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Tue, 20 Mar 2018 18:43:35 -0700 Subject: [PATCH 0321/2296] senlin-actions-get --- openstack/clustering/v1/actions/doc.go | 8 ++ openstack/clustering/v1/actions/requests.go | 7 ++ openstack/clustering/v1/actions/results.go | 8 ++ .../v1/actions/testing/requests_test.go | 73 +++++++++++++++++++ openstack/clustering/v1/actions/urls.go | 8 ++ 5 files changed, 104 insertions(+) diff --git a/openstack/clustering/v1/actions/doc.go b/openstack/clustering/v1/actions/doc.go index 1942f1d07a..d7b9ec8596 100644 --- a/openstack/clustering/v1/actions/doc.go +++ b/openstack/clustering/v1/actions/doc.go @@ -19,5 +19,13 @@ Example to list actions return true, nil }) +Example to get an action + + action, err := actions.Get(serviceClient, "edce3528-864f-41fb-8759-f4707925cc09").Extract() + if err != nil { + panic(err) + } + fmt.Printf("Action %+v: ", action) + */ package actions diff --git a/openstack/clustering/v1/actions/requests.go b/openstack/clustering/v1/actions/requests.go index 6bd545473d..27ffd18e96 100644 --- a/openstack/clustering/v1/actions/requests.go +++ b/openstack/clustering/v1/actions/requests.go @@ -42,3 +42,10 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return ActionPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Get retrieves details of a single action. Use Extract to convert its +// result into an action id. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + return +} diff --git a/openstack/clustering/v1/actions/results.go b/openstack/clustering/v1/actions/results.go index c6709c536d..5f29338142 100644 --- a/openstack/clustering/v1/actions/results.go +++ b/openstack/clustering/v1/actions/results.go @@ -50,6 +50,14 @@ type Action struct { User string `json:"user"` } +func (r commonResult) Extract() (*Action, error) { + var s struct { + Action *Action `json:"action"` + } + err := r.ExtractInto(&s) + return s.Action, err +} + // ExtractActions provides access to the list of actions in a page acquired from the List operation. func ExtractActions(r pagination.Page) ([]Action, error) { var s struct { diff --git a/openstack/clustering/v1/actions/testing/requests_test.go b/openstack/clustering/v1/actions/testing/requests_test.go index 31de3cb6ec..1c74ad9be6 100644 --- a/openstack/clustering/v1/actions/testing/requests_test.go +++ b/openstack/clustering/v1/actions/testing/requests_test.go @@ -143,3 +143,76 @@ func TestListActions(t *testing.T) { t.Errorf("Expected 1 page, got %d", pageCount) } } + +func TestGetAction(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/actions/edce3528-864f-41fb-8759-f4707925cc09", 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, ` + { + "action": { + "action": "NODE_DELETE", + "cause": "RPC Request", + "created_at": "2015-11-04T05:21:41Z", + "data": {}, + "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "end_time": 1425550000.0, + "id": "edce3528-864f-41fb-8759-f4707925cc09", + "inputs": {}, + "interval": -1, + "name": "node_delete_f0de9b9c", + "outputs": {}, + "owner": null, + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "start_time": 1425550000.0, + "status": "SUCCEEDED", + "status_reason": "Action completed successfully.", + "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + "timeout": 3600, + "updated_at": "2016-11-04T05:21:41Z", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + } + }`) + }) + + createdAt, _ := time.Parse(time.RFC3339, "2015-11-04T05:21:41Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-11-04T05:21:41Z") + expected := actions.Action{ + Action: "NODE_DELETE", + Cause: "RPC Request", + CreatedAt: createdAt, + Data: map[string]interface{}{}, + DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + EndTime: 1425550000.0, + ID: "edce3528-864f-41fb-8759-f4707925cc09", + Inputs: make(map[string]interface{}), + Interval: -1, + Name: "node_delete_f0de9b9c", + Outputs: make(map[string]interface{}), + Owner: "", + Project: "f1fe61dcda2f4618a14c10dc7abc214d", + StartTime: 1425550000.0, + Status: "SUCCEEDED", + StatusReason: "Action completed successfully.", + Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + Timeout: 3600, + UpdatedAt: updatedAt, + User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", + } + + actual, err := actions.Get(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() + if err != nil { + t.Errorf("Failed retrieving action. %v", err) + } else { + th.AssertDeepEquals(t, expected, *actual) + } +} diff --git a/openstack/clustering/v1/actions/urls.go b/openstack/clustering/v1/actions/urls.go index 19f7f5c86b..3288cc805e 100644 --- a/openstack/clustering/v1/actions/urls.go +++ b/openstack/clustering/v1/actions/urls.go @@ -12,3 +12,11 @@ func commonURL(client *gophercloud.ServiceClient) string { func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func idURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 57c1f9d8bea9f84a6145d15ca2758559bcfbbd97 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 21 Apr 2018 01:43:31 +0000 Subject: [PATCH 0322/2296] Acc Tests: don't print non-json --- acceptance/clients/http.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/acceptance/clients/http.go b/acceptance/clients/http.go index 3f42231e32..ed09a1ac52 100644 --- a/acceptance/clients/http.go +++ b/acceptance/clients/http.go @@ -73,8 +73,6 @@ func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, contentType strin if strings.HasPrefix(contentType, "application/json") { debugInfo := lrt.formatJSON(bs.Bytes()) log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo) - } else { - log.Printf("[DEBUG] OpenStack Request Body: %s", bs.String()) } return ioutil.NopCloser(strings.NewReader(bs.String())), nil From 10244f9e8a6751d9c0ac1656edb8d56aaaf97c32 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 21 Apr 2018 02:00:55 +0000 Subject: [PATCH 0323/2296] Object Storage v1: Allow ETag to be specified and omitted --- .../objectstorage/v1/objects/requests.go | 10 +++++++ .../v1/objects/testing/requests_test.go | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index f67bfd1590..b1d153749c 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -145,6 +145,7 @@ type CreateOptsBuilder interface { type CreateOpts struct { Content io.Reader Metadata map[string]string + NoETag bool CacheControl string `h:"Cache-Control"` ContentDisposition string `h:"Content-Disposition"` ContentEncoding string `h:"Content-Encoding"` @@ -179,6 +180,15 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str h["X-Object-Meta-"+k] = v } + if opts.NoETag { + delete(h, "etag") + return opts.Content, h, q.String(), nil + } + + if h["ETag"] != "" { + return opts.Content, h, q.String(), nil + } + hash := md5.New() buf := bytes.NewBuffer([]byte{}) _, err = io.Copy(io.MultiWriter(hash, buf), opts.Content) diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index c12ae48bf0..23d6e5731a 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -2,6 +2,8 @@ package testing import ( "bytes" + "crypto/md5" + "fmt" "io" "strings" "testing" @@ -238,3 +240,29 @@ func TestGetObject(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, actualHeaders.StaticLargeObject, true) } + +func TestETag(t *testing.T) { + content := "some example object" + createOpts := objects.CreateOpts{ + Content: strings.NewReader(content), + NoETag: true, + } + + _, headers, _, err := createOpts.ToObjectCreateParams() + th.AssertNoErr(t, err) + _, ok := headers["ETag"] + th.AssertEquals(t, ok, false) + + hash := md5.New() + io.WriteString(hash, content) + localChecksum := fmt.Sprintf("%x", hash.Sum(nil)) + + createOpts = objects.CreateOpts{ + Content: strings.NewReader(content), + ETag: localChecksum, + } + + _, headers, _, err = createOpts.ToObjectCreateParams() + th.AssertNoErr(t, err) + th.AssertEquals(t, headers["ETag"], localChecksum) +} From b45de5eb2decf9d33197dd9c2a458e446208ccb0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 24 Apr 2018 03:01:32 +0000 Subject: [PATCH 0324/2296] Object Storage v1: Doc update --- openstack/objectstorage/v1/containers/doc.go | 4 ++++ openstack/objectstorage/v1/objects/doc.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/openstack/objectstorage/v1/containers/doc.go b/openstack/objectstorage/v1/containers/doc.go index 1ac8504de7..9e5f664198 100644 --- a/openstack/objectstorage/v1/containers/doc.go +++ b/openstack/objectstorage/v1/containers/doc.go @@ -7,6 +7,10 @@ containers represents two different objects. In addition to containing objects, you can also use the container to control access to objects by using an access control list (ACL). +Note: When referencing the Object Storage API docs, some of the API actions +are listed under "accounts" rather than "containers". This was an intentional +design in Gophercloud to make some container actions feel more natural. + Example to List Containers listOpts := containers.ListOpts{ diff --git a/openstack/objectstorage/v1/objects/doc.go b/openstack/objectstorage/v1/objects/doc.go index 1e02430fb4..e9b4b8a9f3 100644 --- a/openstack/objectstorage/v1/objects/doc.go +++ b/openstack/objectstorage/v1/objects/doc.go @@ -4,6 +4,10 @@ object resources. An object is a resource that represents and contains data - such as documents, images, and so on. You can also store custom metadata with an object. +Note: When referencing the Object Storage API docs, some of the API actions +are listed under "containers" rather than "objects". This was an intentional +design in Gophercloud to make some object actions feel more natural. + Example to List Objects containerName := "my_container" From 539361393b51e89f34936eadfc6b0121cbe67877 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Fri, 6 Apr 2018 17:15:05 -0700 Subject: [PATCH 0325/2296] Add list to messages --- .../openstack/messaging/v2/message_test.go | 39 +++++++ openstack/messaging/v2/messages/doc.go | 24 ++++ openstack/messaging/v2/messages/requests.go | 47 ++++++++ openstack/messaging/v2/messages/results.go | 53 ++++++++- .../messaging/v2/messages/testing/fixtures.go | 103 ++++++++++++++++++ .../v2/messages/testing/requests_test.go | 25 +++++ openstack/messaging/v2/messages/urls.go | 23 +++- 7 files changed, 312 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/messaging/v2/message_test.go b/acceptance/openstack/messaging/v2/message_test.go index 0d3f09b3a7..c5d1b8aac5 100644 --- a/acceptance/openstack/messaging/v2/message_test.go +++ b/acceptance/openstack/messaging/v2/message_test.go @@ -6,8 +6,47 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" + "github.com/gophercloud/gophercloud/pagination" ) +func TestListMessages(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-718613007343" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + for i := 0; i < 3; i++ { + CreateMessage(t, client, createdQueueName) + } + + // Use a different client/clientID in order to see messages on the Queue + clientID = "3381af92-2b9e-11e3-b191-71861300734d" + client, err = clients.NewMessagingV2Client(clientID) + + listOpts := messages.ListOpts{} + + pager := messages.List(client, createdQueueName, listOpts) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + allMessages, err := messages.ExtractMessages(page) + if err != nil { + t.Fatalf("Unable to extract messages: %v", err) + } + + for _, message := range allMessages { + tools.PrintResource(t, message) + } + + return true, nil + }) +} + func TestCreateMessages(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-71861300734c" diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go index 4412fc2602..f623c45e71 100644 --- a/openstack/messaging/v2/messages/doc.go +++ b/openstack/messaging/v2/messages/doc.go @@ -2,7 +2,31 @@ Package messages provides information and interaction with the messages through the OpenStack Messaging(Zaqar) service. +Example to List Messages + + listOpts := messages.ListOpts{ + Limit: 10, + } + + queueName := "my_queue" + + pager := messages.List(client, queueName, listOpts) + + err = pager.EachPage(func(page pagination.Page) (bool, error) { + allMessages, err := queues.ExtractQueues(page) + if err != nil { + panic(err) + } + + for _, message := range allMessages { + fmt.Printf("%+v\n", message) + } + + return true, nil + }) + Example to Create Messages + createOpts := messages.CreateOpts{ Messages: []messages.Messages{ { diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index 7b925aae34..bccd84c84d 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -2,8 +2,55 @@ package messages import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToMessageListQuery() (string, error) +} + +// ListOpts params to be used with List. +type ListOpts struct { + // Limit instructs List to refrain from sending excessively large lists of queues + Limit int `q:"limit,omitempty"` + + // Marker and Limit control paging. Marker instructs List where to start listing from. + Marker string `q:"marker,omitempty"` + + // Indicate if the messages can be echoed back to the client that posted them. + Echo bool `q:"echo,omitempty"` + + // Indicate if the messages list should include the claimed messages. + IncludeClaimed bool `q:"include_claimed,omitempty"` + + //Indicate if the messages list should include the delayed messages. + IncludeDelayed bool `q:"include_delayed,omitempty"` +} + +func (opts ListOpts) ToMessageListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListMessages lists messages on a specific queue based off queue name. +func List(client *gophercloud.ServiceClient, queueName string, opts ListOptsBuilder) pagination.Pager { + url := listURL(client, queueName) + if opts != nil { + query, err := opts.ToMessageListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + pager := pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return MessagePage{pagination.LinkedPageBase{PageResult: r}} + }) + return pager +} + // CreateOptsBuilder Builder. type CreateOptsBuilder interface { ToMessageCreateMap() (map[string]interface{}, error) diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go index 62b8a60a1b..abc20993f2 100644 --- a/openstack/messaging/v2/messages/results.go +++ b/openstack/messaging/v2/messages/results.go @@ -1,6 +1,9 @@ package messages -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) // commonResult is the response of a base result. type commonResult struct { @@ -12,6 +15,21 @@ type CreateResult struct { gophercloud.Result } +// MessagePage contains a single page of all clusters from a ListDetails call. +type MessagePage struct { + pagination.LinkedPageBase +} + +// Message represents a message on a queue. +type Message struct { + Body map[string]interface{} `json:"body"` + Age int `json:"age"` + Href string `json:"href"` + ID string `json:"id"` + TTL int `json:"ttl"` + Checksum string `json:"checksum"` +} + // ResourceList represents the result of creating a message. type ResourceList struct { Resources []string `json:"resources"` @@ -23,3 +41,36 @@ func (r CreateResult) Extract() (ResourceList, error) { err := r.ExtractInto(&s) return s, err } + +// ExtractMessage extracts message into a list of Message. +func ExtractMessages(r pagination.Page) ([]Message, error) { + var s struct { + Messages []Message `json:"messages"` + } + err := (r.(MessagePage)).ExtractInto(&s) + return s.Messages, err +} + +// IsEmpty determines if a MessagePage contains any results. +func (r MessagePage) IsEmpty() (bool, error) { + s, err := ExtractMessages(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r MessagePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + + next, err := gophercloud.ExtractNextURL(s.Links) + if err != nil { + return "", err + } + return nextPageURL(r.URL.String(), next) +} diff --git a/openstack/messaging/v2/messages/testing/fixtures.go b/openstack/messaging/v2/messages/testing/fixtures.go index 537869f4f5..d8cd010ae1 100644 --- a/openstack/messaging/v2/messages/testing/fixtures.go +++ b/openstack/messaging/v2/messages/testing/fixtures.go @@ -44,6 +44,57 @@ const CreateMessageRequest = ` ] }` +// ListMessagesResponse is a sample response to list messages. +const ListMessagesResponse1 = ` +{ + "messages": [ + { + "body": { + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720" + }, + "age": 482, + "href": "/v2/queues/FakeTestQueue/messages/578edfe6508f153f256f717b", + "id": "578edfe6508f153f256f717b", + "ttl": 3600, + "checksum": "MD5:abf7213555626e29c3cb3e5dc58b3515" + } + ], + "links": [ + { + "href": "/v2/queues/FakeTestQueue/messages?marker=1", + "rel": "next" + } + ] +}` + +// ListMessagesResponse is a sample response to list messages. +const ListMessagesResponse2 = ` +{ + "messages": [ + { + "body": { + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720" + }, + "age": 456, + "href": "/v2/queues/FakeTestQueue/messages/578ee000508f153f256f717d", + "id": "578ee000508f153f256f717d", + "ttl": 3600, + "checksum": "MD5:abf7213555626e29c3cb3e5dc58b3515" + } + ], + "links": [ + { + "href": "/v2/queues/FakeTestQueue/messages?marker=2", + "rel": "next" + } + ] + +}` + // ExpectedResources is the expected result in Create var ExpectedResources = messages.ResourceList{ Resources: []string{ @@ -52,6 +103,37 @@ var ExpectedResources = messages.ResourceList{ }, } +// FirstMessage is the first result in a List. +var FirstMessage = messages.Message{ + Body: map[string]interface{}{ + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720", + }, + Age: 482, + Href: fmt.Sprintf("/v2/queues/%s/messages/578edfe6508f153f256f717b", QueueName), + ID: "578edfe6508f153f256f717b", + TTL: 3600, + Checksum: "MD5:abf7213555626e29c3cb3e5dc58b3515", +} + +// SecondMessage is the second result in a List. +var SecondMessage = messages.Message{ + Body: map[string]interface{}{ + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720", + }, + Age: 456, + Href: fmt.Sprintf("/v2/queues/%s/messages/578ee000508f153f256f717d", QueueName), + ID: "578ee000508f153f256f717d", + TTL: 3600, + Checksum: "MD5:abf7213555626e29c3cb3e5dc58b3515", +} + +// ExpectedMessagesSlice is the expected result in a List. +var ExpectedMessagesSlice = [][]messages.Message{{FirstMessage}, {SecondMessage}} + // HandleCreateSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), @@ -65,3 +147,24 @@ func HandleCreateSuccessfully(t *testing.T) { fmt.Fprintf(w, CreateMessageResponse) }) } + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), + 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") + next := r.RequestURI + + switch next { + case fmt.Sprintf("/v2/queues/%s/messages?limit=1", QueueName): + fmt.Fprintf(w, ListMessagesResponse1) + case fmt.Sprintf("/v2/queues/%s/messages?marker=1", QueueName): + fmt.Fprint(w, ListMessagesResponse2) + case fmt.Sprintf("/v2/queues/%s/messages?marker=2", QueueName): + fmt.Fprint(w, `{ "messages": [] }`) + } + }) +} diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go index 557dabd849..9dc9633313 100644 --- a/openstack/messaging/v2/messages/testing/requests_test.go +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -4,10 +4,35 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + listOpts := messages.ListOpts{ + Limit: 1, + } + + count := 0 + err := messages.List(fake.ServiceClient(), QueueName, listOpts).EachPage(func(page pagination.Page) (bool, error) { + actual, err := messages.ExtractMessages(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedMessagesSlice[count], actual) + count++ + + return true, nil + }) + th.AssertNoErr(t, err) + + th.CheckEquals(t, 2, count) +} + func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/messaging/v2/messages/urls.go b/openstack/messaging/v2/messages/urls.go index 12154bc998..30059855e2 100644 --- a/openstack/messaging/v2/messages/urls.go +++ b/openstack/messaging/v2/messages/urls.go @@ -1,6 +1,10 @@ package messages -import "github.com/gophercloud/gophercloud" +import ( + "net/url" + + "github.com/gophercloud/gophercloud" +) const ApiVersion = "v2" const ApiName = "queues" @@ -8,3 +12,20 @@ const ApiName = "queues" func createURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") } + +func listURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") +} + +// Builds next page full url based on current url. +func nextPageURL(currentURL string, next string) (string, error) { + base, err := url.Parse(currentURL) + if err != nil { + return "", err + } + rel, err := url.Parse(next) + if err != nil { + return "", err + } + return base.ResolveReference(rel).String(), nil +} From adf8152c7d56bc3ccbc881fb9eda170f97b479f9 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Fri, 27 Apr 2018 11:55:09 +1200 Subject: [PATCH 0326/2296] Use 'project_id' for octavia resources For #963 According to Octavia API document https://developer.openstack.org/api-ref/load-balancer/v2/index.html 'tenant_id' is no longer supported. --- .../loadbalancer/v2/l7policies/requests.go | 4 +-- .../loadbalancer/v2/listeners/requests.go | 11 ++----- .../loadbalancer/v2/listeners/results.go | 2 +- .../v2/listeners/testing/fixtures.go | 14 ++++---- .../v2/listeners/testing/requests_test.go | 6 ++-- .../loadbalancer/v2/loadbalancers/requests.go | 9 ++---- .../loadbalancer/v2/loadbalancers/results.go | 2 +- .../v2/loadbalancers/testing/fixtures.go | 14 ++++---- openstack/loadbalancer/v2/monitors/results.go | 4 +-- .../v2/monitors/testing/fixtures.go | 16 +++++----- .../v2/monitors/testing/requests_test.go | 2 +- openstack/loadbalancer/v2/pools/requests.go | 15 ++------- openstack/loadbalancer/v2/pools/results.go | 4 +-- .../loadbalancer/v2/pools/testing/fixtures.go | 32 +++++++++---------- .../v2/pools/testing/requests_test.go | 4 +-- 15 files changed, 60 insertions(+), 79 deletions(-) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 1272bc1b21..c8887ae3b6 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -113,7 +113,7 @@ func (opts ListOpts) ToL7PolicyListQuery() (string, error) { // the returned collection for greater efficiency. // // Default policy settings return only those l7policies that are owned by the -// tenant who submits the request, unless an admin user submits the request. +// project who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { @@ -257,7 +257,7 @@ func (opts ListRulesOpts) ToRulesListQuery() (string, error) { // sort the returned collection for greater efficiency. // // Default policy settings return only those rules that are owned by the -// tenant who submits the request, unless an admin user submits the request. +// project who submits the request, unless an admin user submits the request. func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOptsBuilder) pagination.Pager { url := ruleRootURL(c, policyID) if opts != nil { diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index dd190f606f..5662ef8a11 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -30,7 +30,6 @@ type ListOpts struct { ID string `q:"id"` Name string `q:"name"` AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` LoadbalancerID string `q:"loadbalancer_id"` DefaultPoolID string `q:"default_pool_id"` @@ -54,7 +53,7 @@ func (opts ListOpts) ToListenerListQuery() (string, error) { // the returned collection for greater efficiency. // // Default policy settings return only those listeners that are owned by the -// tenant who submits the request, unless an admin user submits the request. +// project who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { @@ -86,10 +85,6 @@ type CreateOpts struct { // The port on which to listen for client traffic. ProtocolPort int `json:"protocol_port" required:"true"` - // TenantID is only required if the caller has an admin role and wants - // to create a pool for another project. - TenantID string `json:"tenant_id,omitempty"` - // ProjectID is only required if the caller has an admin role and wants // to create a pool for another project. ProjectID string `json:"project_id,omitempty"` @@ -127,8 +122,8 @@ func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { // validated and progress has started on the provisioning process, a // CreateResult will be returned. // -// Users with an admin role can create Listeners on behalf of other tenants by -// specifying a TenantID attribute different than their own. +// Users with an admin role can create Listeners on behalf of other projects by +// specifying a ProjectID attribute different than their own. func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToListenerCreateMap() if err != nil { diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 728d04266d..1051c69b9b 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -18,7 +18,7 @@ type Listener struct { ID string `json:"id"` // Owner of the Listener. - TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` // Human-readable name for the Listener. Does not have to be unique. Name string `json:"name"` diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index a3df254b43..07faa8d49a 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -16,7 +16,7 @@ const ListenersListBody = ` "listeners":[ { "id": "db902c0c-d5ff-4753-b465-668ad9656918", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", + "project_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "web", "description": "listener config for the web tier", "loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}], @@ -29,7 +29,7 @@ const ListenersListBody = ` }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", + "project_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "db", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], @@ -50,7 +50,7 @@ const SingleListenerBody = ` { "listener": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", + "project_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "db", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], @@ -70,7 +70,7 @@ const PostUpdateListenerBody = ` { "listener": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", + "project_id": "310df60f-2a10-4ee5-9554-98393092194c", "name": "NewListenerName", "description": "listener config for the db tier", "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], @@ -88,7 +88,7 @@ const PostUpdateListenerBody = ` var ( ListenerWeb = listeners.Listener{ ID: "db902c0c-d5ff-4753-b465-668ad9656918", - TenantID: "310df60f-2a10-4ee5-9554-98393092194c", + ProjectID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "web", Description: "listener config for the web tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}}, @@ -101,7 +101,7 @@ var ( } ListenerDb = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "310df60f-2a10-4ee5-9554-98393092194c", + ProjectID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "db", Description: "listener config for the db tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, @@ -115,7 +115,7 @@ var ( } ListenerUpdated = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "310df60f-2a10-4ee5-9554-98393092194c", + ProjectID: "310df60f-2a10-4ee5-9554-98393092194c", Name: "NewListenerName", Description: "listener config for the db tier", Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 4f0a0db0f3..4123b305ce 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -81,15 +81,15 @@ func TestRequiredCreateOpts(t *testing.T) { if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar"}) + res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar"}) + res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar", Protocol: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar", ProtocolPort: 80}) + res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar", Protocol: "bar", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, got none") } diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 9d82f9efa0..0a62f59bc5 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -19,7 +19,6 @@ type ListOptsBuilder interface { type ListOpts struct { Description string `q:"description"` AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` ProvisioningStatus string `q:"provisioning_status"` VipAddress string `q:"vip_address"` @@ -47,7 +46,7 @@ func (opts ListOpts) ToLoadBalancerListQuery() (string, error) { // and sort the returned collection for greater efficiency. // // Default policy settings return only those load balancers that are owned by -// the tenant who submits the request, unless an admin user submits the request. +// the project who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { @@ -77,15 +76,11 @@ type CreateOpts struct { // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` - // The network on which to allocate the Loadbalancer's address. A tenant can + // The network on which to allocate the Loadbalancer's address. A project can // only create Loadbalancers on networks authorized by policy (e.g. networks // that belong to them or networks that are shared). VipSubnetID string `json:"vip_subnet_id" required:"true"` - // TenantID is the UUID of the project who owns the Loadbalancer. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - // ProjectID is the UUID of the project who owns the Loadbalancer. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 80a9ff0557..62d1b668b9 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -18,7 +18,7 @@ type LoadBalancer struct { AdminStateUp bool `json:"admin_state_up"` // Owner of the LoadBalancer. - TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` // The provisioning status of the LoadBalancer. // This value is ACTIVE, PENDING_CREATE or ERROR. diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index 759d2d9ed6..f2d1f65ca9 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -20,7 +20,7 @@ const LoadbalancersListBody = ` "loadbalancers":[ { "id": "c331058c-6a40-4144-948e-b9fb1df9db4b", - "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", + "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "web_lb", "description": "lb config for the web tier", "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154", @@ -34,7 +34,7 @@ const LoadbalancersListBody = ` }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", + "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "db_lb", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -55,7 +55,7 @@ const SingleLoadbalancerBody = ` { "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", + "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "db_lb", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -75,7 +75,7 @@ const PostUpdateLoadbalancerBody = ` { "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", + "project_id": "54030507-44f7-473c-9342-b4d14a95f692", "name": "NewLoadbalancerName", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -125,7 +125,7 @@ const LoadbalancerStatuesesTree = ` var ( LoadbalancerWeb = loadbalancers.LoadBalancer{ ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", - TenantID: "54030507-44f7-473c-9342-b4d14a95f692", + ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "web_lb", Description: "lb config for the web tier", VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154", @@ -139,7 +139,7 @@ var ( } LoadbalancerDb = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "54030507-44f7-473c-9342-b4d14a95f692", + ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "db_lb", Description: "lb config for the db tier", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -153,7 +153,7 @@ var ( } LoadbalancerUpdated = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "54030507-44f7-473c-9342-b4d14a95f692", + ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", Name: "NewLoadbalancerName", Description: "lb config for the db tier", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index ea832cc5d0..f66579e979 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -31,8 +31,8 @@ type Monitor struct { // The Name of the Monitor. Name string `json:"name"` - // TenantID is the owner of the Monitor. - TenantID string `json:"tenant_id"` + // The owner of the Monitor. + ProjectID string `json:"project_id"` // The type of probe sent by the load balancer to verify the member state, // which is PING, TCP, HTTP, or HTTPS. diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures.go b/openstack/loadbalancer/v2/monitors/testing/fixtures.go index 262ebbe8f1..23c097b909 100644 --- a/openstack/loadbalancer/v2/monitors/testing/fixtures.go +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures.go @@ -16,7 +16,7 @@ const HealthmonitorsListBody = ` "healthmonitors":[ { "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":10, "name":"web", "max_retries":1, @@ -27,7 +27,7 @@ const HealthmonitorsListBody = ` }, { "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, "name":"db", "expected_codes":"200", @@ -48,7 +48,7 @@ const SingleHealthmonitorBody = ` { "healthmonitor": { "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":5, "name":"db", "expected_codes":"200", @@ -68,7 +68,7 @@ const PostUpdateHealthmonitorBody = ` { "healthmonitor": { "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "delay":3, "name":"NewHealthmonitorName", "expected_codes":"301", @@ -87,7 +87,7 @@ var ( HealthmonitorWeb = monitors.Monitor{ AdminStateUp: true, Name: "web", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 10, MaxRetries: 1, Timeout: 1, @@ -98,7 +98,7 @@ var ( HealthmonitorDb = monitors.Monitor{ AdminStateUp: true, Name: "db", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 5, ExpectedCodes: "200", MaxRetries: 2, @@ -112,7 +112,7 @@ var ( HealthmonitorUpdated = monitors.Monitor{ AdminStateUp: true, Name: "NewHealthmonitorName", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", Delay: 3, ExpectedCodes: "301", MaxRetries: 10, @@ -155,7 +155,7 @@ func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) { "healthmonitor": { "type":"HTTP", "pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", - "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", + "project_id":"453105b9-1754-413f-aab1-55f1af620750", "delay":20, "name":"db", "timeout":10, diff --git a/openstack/loadbalancer/v2/monitors/testing/requests_test.go b/openstack/loadbalancer/v2/monitors/testing/requests_test.go index 80cba9ca70..d850fe199b 100644 --- a/openstack/loadbalancer/v2/monitors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -61,7 +61,7 @@ func TestCreateHealthmonitor(t *testing.T) { Type: "HTTP", Name: "db", PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", - TenantID: "453105b9-1754-413f-aab1-55f1af620750", + ProjectID: "453105b9-1754-413f-aab1-55f1af620750", Delay: 20, Timeout: 10, MaxRetries: 5, diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index b58bf9c359..538b2f1b2a 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -19,7 +19,6 @@ type ListOptsBuilder interface { type ListOpts struct { LBMethod string `q:"lb_algorithm"` Protocol string `q:"protocol"` - TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` AdminStateUp *bool `q:"admin_state_up"` Name string `q:"name"` @@ -42,7 +41,7 @@ func (opts ListOpts) ToPoolListQuery() (string, error) { // the returned collection for greater efficiency. // // Default policy settings return only those pools that are owned by the -// tenant who submits the request, unless an admin user submits the request. +// project who submits the request, unless an admin user submits the request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := rootURL(c) if opts != nil { @@ -97,10 +96,6 @@ type CreateOpts struct { // Note: one of LoadbalancerID or ListenerID must be provided. ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"` - // TenantID is the UUID of the project who owns the Pool. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - // ProjectID is the UUID of the project who owns the Pool. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` @@ -207,7 +202,7 @@ type ListMembersOpts struct { Name string `q:"name"` Weight int `q:"weight"` AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` Address string `q:"address"` ProtocolPort int `q:"protocol_port"` ID string `q:"id"` @@ -228,7 +223,7 @@ func (opts ListMembersOpts) ToMembersListQuery() (string, error) { // sort the returned collection for greater efficiency. // // Default policy settings return only those members that are owned by the -// tenant who submits the request, unless an admin user submits the request. +// project who submits the request, unless an admin user submits the request. func ListMembers(c *gophercloud.ServiceClient, poolID string, opts ListMembersOptsBuilder) pagination.Pager { url := memberRootURL(c, poolID) if opts != nil { @@ -261,10 +256,6 @@ type CreateMemberOpts struct { // Name of the Member. Name string `json:"name,omitempty"` - // TenantID is the UUID of the project who owns the Member. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - // ProjectID is the UUID of the project who owns the Member. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index 81d3ebf7d6..40ba5a528f 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -69,7 +69,7 @@ type Pool struct { SubnetID string `json:"subnet_id"` // Owner of the Pool. - TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` // The administrative state of the Pool, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` @@ -180,7 +180,7 @@ type Member struct { AdminStateUp bool `json:"admin_state_up"` // Owner of the Member. - TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` // Parameter value for the subnet UUID. SubnetID string `json:"subnet_id"` diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures.go b/openstack/loadbalancer/v2/pools/testing/fixtures.go index fe0a85123b..9d929525d6 100644 --- a/openstack/loadbalancer/v2/pools/testing/fixtures.go +++ b/openstack/loadbalancer/v2/pools/testing/fixtures.go @@ -25,7 +25,7 @@ const PoolsListBody = ` "id":"72741b06-df4d-4715-b142-276b6bce75ab", "name":"web", "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" }, { @@ -39,7 +39,7 @@ const PoolsListBody = ` "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } ] @@ -60,7 +60,7 @@ const SinglePoolBody = ` "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } } @@ -80,7 +80,7 @@ const PostUpdatePoolBody = ` "id":"c3741b06-df4d-4715-b142-276b6bce75ab", "name":"db", "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", + "project_id":"83657cfcdfe44cd5920adaf26c48ceea", "provider": "haproxy" } } @@ -92,7 +92,7 @@ var ( Protocol: "HTTP", Description: "", MonitorID: "466c8345-28d8-4f84-a246-e04380b0461d", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "web", Members: []pools.Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}}, @@ -106,7 +106,7 @@ var ( Protocol: "HTTP", Description: "", MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "db", Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, @@ -120,7 +120,7 @@ var ( Protocol: "HTTP", Description: "", MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", AdminStateUp: true, Name: "db", Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, @@ -162,7 +162,7 @@ func HandlePoolCreationSuccessfully(t *testing.T, response string) { "lb_algorithm": "ROUND_ROBIN", "protocol": "HTTP", "name": "Example pool", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "project_id": "2ffc6e22aae24e4795f87155d24c896f", "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab" } }`) @@ -222,7 +222,7 @@ const MembersListBody = ` "weight": 5, "name": "web", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":true, "protocol_port": 80 }, @@ -232,7 +232,7 @@ const MembersListBody = ` "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } @@ -249,7 +249,7 @@ const SingleMemberBody = ` "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } @@ -265,7 +265,7 @@ const PostUpdateMemberBody = ` "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, "protocol_port": 80 } @@ -275,7 +275,7 @@ const PostUpdateMemberBody = ` var ( MemberWeb = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", + ProjectID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: true, Name: "web", ID: "2a280670-c202-4b0b-a562-34077415aabf", @@ -285,7 +285,7 @@ var ( } MemberDb = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", + ProjectID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: false, Name: "db", ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", @@ -295,7 +295,7 @@ var ( } MemberUpdated = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", + ProjectID: "2ffc6e22aae24e4795f87155d24c896f", AdminStateUp: false, Name: "db", ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", @@ -337,7 +337,7 @@ func HandleMemberCreationSuccessfully(t *testing.T, response string) { "weight": 10, "name": "db", "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", + "project_id": "2ffc6e22aae24e4795f87155d24c896f", "protocol_port": 80 } }`) diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go index 9eaec03e01..33c5c73642 100644 --- a/openstack/loadbalancer/v2/pools/testing/requests_test.go +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -61,7 +61,7 @@ func TestCreatePool(t *testing.T) { LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", + ProjectID: "2ffc6e22aae24e4795f87155d24c896f", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", }).Extract() th.AssertNoErr(t, err) @@ -192,7 +192,7 @@ func TestCreateMember(t *testing.T) { actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", + ProjectID: "2ffc6e22aae24e4795f87155d24c896f", Address: "10.0.2.11", ProtocolPort: 80, Weight: 10, From a3ac2539cedccb3a3161b20ab7a4cc889b21a2f9 Mon Sep 17 00:00:00 2001 From: Kevin Zhao Date: Fri, 27 Apr 2018 11:51:43 +1000 Subject: [PATCH 0327/2296] Zun Capsule Creation Method (#954) * Add capsule creation method Also add unit test and accept test Depends-On: https://github.com/gophercloud/gophercloud/pull/944 Change-Id: I29f9c56b0aa71ef4d20917cbc216b909a253f473 Signed-off-by: Kevin Zhao * Remove TE and Validate method Change-Id: I06f3295970f5945998ca75a2b5dc40b427eef8be Signed-off-by: Kevin Zhao * Remove the unused error type Change-Id: Ib6f016107d3c5c367ceeb968c699c981c3238643 Signed-off-by: Kevin Zhao * Remove the invalid change in orchestration Change-Id: Ib7a0e4da4c69f416e425229910b48d128facc010 Signed-off-by: Kevin Zhao --- .../openstack/container/v1/capsules_test.go | 56 ++++++ openstack/container/v1/capsules/errors.go | 15 ++ openstack/container/v1/capsules/fixtures.go | 166 ++++++++++++++++++ openstack/container/v1/capsules/requests.go | 43 +++++ openstack/container/v1/capsules/results.go | 6 + openstack/container/v1/capsules/template.go | 27 +++ .../container/v1/capsules/template_test.go | 28 +++ .../container/v1/capsules/testing/fixtures.go | 17 +- .../v1/capsules/testing/requests_test.go | 54 ++++++ openstack/container/v1/capsules/urls.go | 4 + 10 files changed, 411 insertions(+), 5 deletions(-) create mode 100644 openstack/container/v1/capsules/errors.go create mode 100644 openstack/container/v1/capsules/fixtures.go create mode 100644 openstack/container/v1/capsules/template.go create mode 100644 openstack/container/v1/capsules/template_test.go diff --git a/acceptance/openstack/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index 41dad46339..3e93edce29 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -26,3 +26,59 @@ func TestCapsuleGet(t *testing.T) { th.AssertEquals(t, capsule.MetaName, "template") th.AssertEquals(t, capsule.CPU, float64(2.0)) } + +func TestCapsuleCreate(t *testing.T) { + client, err := clients.NewContainerV1Client() + if err != nil { + t.Fatalf("Unable to create an container v1 client: %v", err) + } + th.AssertNoErr(t, err) + template := new(capsules.Template) + template.Bin = []byte(`{ + "capsuleVersion": "beta", + "kind": "capsule", + "metadata": { + "labels": { + "app": "web", + "app1": "web1" + }, + "name": "template" + }, + "restartPolicy": "Always", + "spec": { + "containers": [ + { + "command": [ + "/bin/bash" + ], + "env": { + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin" + }, + "image": "ubuntu", + "imagePullPolicy": "ifnotpresent", + "ports": [ + { + "containerPort": 80, + "hostPort": 80, + "name": "nginx-port", + "protocol": "TCP" + } + ], + "resources": { + "requests": { + "cpu": 1, + "memory": 1024 + } + }, + "workDir": "/root" + } + ] + } + }`) + createOpts := capsules.CreateOpts{ + TemplateOpts: template, + } + err = capsules.Create(client, createOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/container/v1/capsules/errors.go b/openstack/container/v1/capsules/errors.go new file mode 100644 index 0000000000..6542e66ec8 --- /dev/null +++ b/openstack/container/v1/capsules/errors.go @@ -0,0 +1,15 @@ +package capsules + +import ( + "fmt" + + "github.com/gophercloud/gophercloud" +) + +type ErrInvalidDataFormat struct { + gophercloud.BaseError +} + +func (e ErrInvalidDataFormat) Error() string { + return fmt.Sprintf("Data in neither json nor yaml format.") +} diff --git a/openstack/container/v1/capsules/fixtures.go b/openstack/container/v1/capsules/fixtures.go new file mode 100644 index 0000000000..e547263b94 --- /dev/null +++ b/openstack/container/v1/capsules/fixtures.go @@ -0,0 +1,166 @@ +package capsules + +// ValidJSONTemplate is a valid OpenStack Capsule template in JSON format +const ValidJSONTemplate = ` +{ + "capsuleVersion": "beta", + "kind": "capsule", + "metadata": { + "labels": { + "app": "web", + "app1": "web1" + }, + "name": "template" + }, + "restartPolicy": "Always", + "spec": { + "containers": [ + { + "command": [ + "/bin/bash" + ], + "env": { + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin" + }, + "image": "ubuntu", + "imagePullPolicy": "ifnotpresent", + "ports": [ + { + "containerPort": 80, + "hostPort": 80, + "name": "nginx-port", + "protocol": "TCP" + } + ], + "resources": { + "requests": { + "cpu": 1, + "memory": 1024 + } + }, + "workDir": "/root" + } + ] + } +} +` + +// ValidYAMLTemplate is a valid OpenStack Capsule template in YAML format +const ValidYAMLTemplate = ` +capsuleVersion: beta +kind: capsule +metadata: + name: template + labels: + app: web + app1: web1 +restartPolicy: Always +spec: + containers: + - image: ubuntu + command: + - "/bin/bash" + imagePullPolicy: ifnotpresent + workDir: /root + ports: + - name: nginx-port + containerPort: 80 + hostPort: 80 + protocol: TCP + resources: + requests: + cpu: 1 + memory: 1024 + env: + ENV1: /usr/local/bin + ENV2: /usr/bin +` + +// ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate +var ValidJSONTemplateParsed = map[string]interface{}{ + "capsuleVersion": "beta", + "kind": "capsule", + "restartPolicy": "Always", + "metadata": map[string]interface{}{ + "name": "template", + "labels": map[string]string{ + "app": "web", + "app1": "web1", + }, + }, + "spec": map[string]interface{}{ + "containers": []map[string]interface{}{ + map[string]interface{}{ + "image": "ubuntu", + "command": []interface{}{ + "/bin/bash", + }, + "imagePullPolicy": "ifnotpresent", + "workDir": "/root", + "ports": []interface{}{ + map[string]interface{}{ + "name": "nginx-port", + "containerPort": float64(80), + "hostPort": float64(80), + "protocol": "TCP", + }, + }, + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": float64(1), + "memory": float64(1024), + }, + }, + "env": map[string]interface{}{ + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin", + }, + }, + }, + }, +} + +// ValidYAMLTemplateParsed is the expected parsed version of ValidYAMLTemplate +var ValidYAMLTemplateParsed = map[string]interface{}{ + "capsuleVersion": "beta", + "kind": "capsule", + "restartPolicy": "Always", + "metadata": map[string]interface{}{ + "name": "template", + "labels": map[string]string{ + "app": "web", + "app1": "web1", + }, + }, + "spec": map[interface{}]interface{}{ + "containers": []map[interface{}]interface{}{ + map[interface{}]interface{}{ + "image": "ubuntu", + "command": []interface{}{ + "/bin/bash", + }, + "imagePullPolicy": "ifnotpresent", + "workDir": "/root", + "ports": []interface{}{ + map[interface{}]interface{}{ + "name": "nginx-port", + "containerPort": 80, + "hostPort": 80, + "protocol": "TCP", + }, + }, + "resources": map[interface{}]interface{}{ + "requests": map[interface{}]interface{}{ + "cpu": 1, + "memory": 1024, + }, + }, + "env": map[interface{}]interface{}{ + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin", + }, + }, + }, + }, +} diff --git a/openstack/container/v1/capsules/requests.go b/openstack/container/v1/capsules/requests.go index 25b1ffa875..c73721c3d2 100644 --- a/openstack/container/v1/capsules/requests.go +++ b/openstack/container/v1/capsules/requests.go @@ -4,6 +4,14 @@ import ( "github.com/gophercloud/gophercloud" ) +// CreateOptsBuilder is the interface options structs have to satisfy in order +// to be used in the main Create operation in this package. Since many +// extensions decorate or modify the common logic, it is useful for them to +// satisfy a basic interface in order for them to be used. +type CreateOptsBuilder interface { + ToCapsuleCreateMap() (map[string]interface{}, error) +} + // Get requests details on a single capsule, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ @@ -11,3 +19,38 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { }) return } + +// CreateOpts is the common options struct used in this package's Create +// operation. +type CreateOpts struct { + // 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 `json:"-" required:"true"` +} + +// ToCapsuleCreateMap assembles a request body based on the contents of +// a CreateOpts. +func (opts CreateOpts) ToCapsuleCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if err := opts.TemplateOpts.Parse(); err != nil { + return nil, err + } + b["template"] = string(opts.TemplateOpts.Bin) + + return b, nil +} + +// Create implements create capsule request. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToCapsuleCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + return +} diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index 26b56e45e1..cfc19663c1 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -23,6 +23,12 @@ type GetResult struct { commonResult } +// CreateResult is the response from a Create operation. Call its Extract +// method to interpret it as a Server. +type CreateResult struct { + gophercloud.ErrResult +} + // Represents a Container Orchestration Engine Bay, i.e. a cluster type Capsule struct { // UUID for the capsule diff --git a/openstack/container/v1/capsules/template.go b/openstack/container/v1/capsules/template.go new file mode 100644 index 0000000000..ad72ca9ff2 --- /dev/null +++ b/openstack/container/v1/capsules/template.go @@ -0,0 +1,27 @@ +package capsules + +import ( + "encoding/json" + + "gopkg.in/yaml.v2" +) + +// Template is a structure that represents OpenStack Zun Capsule templates +type Template struct { + // Bin stores the contents of the template or environment. + Bin []byte + // Parsed contains a parsed version of Bin. Since there are 2 different + // fields referring to the same value, you must be careful when accessing + // this filed. + Parsed map[string]interface{} +} + +// Parse will parse the contents and then validate. The contents MUST be either JSON or YAML. +func (t *Template) Parse() error { + if jerr := json.Unmarshal(t.Bin, &t.Parsed); jerr != nil { + if yerr := yaml.Unmarshal(t.Bin, &t.Parsed); yerr != nil { + return ErrInvalidDataFormat{} + } + } + return nil +} diff --git a/openstack/container/v1/capsules/template_test.go b/openstack/container/v1/capsules/template_test.go new file mode 100644 index 0000000000..289834b659 --- /dev/null +++ b/openstack/container/v1/capsules/template_test.go @@ -0,0 +1,28 @@ +package capsules + +import ( + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestTemplateParsing(t *testing.T) { + templateJSON := new(Template) + templateJSON.Bin = []byte(ValidJSONTemplate) + err := templateJSON.Parse() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateJSON.Parsed) + + templateYAML := new(Template) + templateYAML.Bin = []byte(ValidYAMLTemplate) + err = templateYAML.Parse() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ValidYAMLTemplateParsed, templateYAML.Parsed) + + templateInvalid := new(Template) + templateInvalid.Bin = []byte("Keep Austin Weird") + err = templateInvalid.Parse() + if err == nil { + t.Error("Template parsing did not catch invalid template") + } +} diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 017d12bc7e..d3d1d7fe0c 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -9,11 +9,6 @@ import ( fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) -type imageEntry struct { - ID string - JSON string -} - // HandleImageGetSuccessfully test setup func HandleCapsuleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { @@ -66,3 +61,15 @@ func HandleCapsuleGetSuccessfully(t *testing.T) { }`) }) } + +// HandleCapsuleCreateSuccessfully creates an HTTP handler at `/capsules` on the test handler mux +// that responds with a `Create` response. +func HandleCapsuleCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/capsules", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + w.WriteHeader(http.StatusAccepted) + fmt.Fprintf(w, `{}`) + }) +} diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 016ad0d277..f7e6c37227 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -89,3 +89,57 @@ func TestGetCapsule(t *testing.T) { th.AssertDeepEquals(t, &expectedCapsule, actualCapsule) } + +func TestCreateCapsule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCapsuleCreateSuccessfully(t) + template := new(capsules.Template) + template.Bin = []byte(`{ + "capsuleVersion": "beta", + "kind": "capsule", + "metadata": { + "labels": { + "app": "web", + "app1": "web1" + }, + "name": "template" + }, + "restartPolicy": "Always", + "spec": { + "containers": [ + { + "command": [ + "/bin/bash" + ], + "env": { + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin" + }, + "image": "ubuntu", + "imagePullPolicy": "ifnotpresent", + "ports": [ + { + "containerPort": 80, + "hostPort": 80, + "name": "nginx-port", + "protocol": "TCP" + } + ], + "resources": { + "requests": { + "cpu": 1, + "memory": 1024 + } + }, + "workDir": "/root" + } + ] + } + }`) + createOpts := capsules.CreateOpts{ + TemplateOpts: template, + } + err := capsules.Create(fakeclient.ServiceClient(), createOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/container/v1/capsules/urls.go b/openstack/container/v1/capsules/urls.go index c3baf01a84..b276c4509f 100644 --- a/openstack/container/v1/capsules/urls.go +++ b/openstack/container/v1/capsules/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("capsules", id) } + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("capsules") +} From 5e5c39dab860799a1b6efaa8aa5d89157550a932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Sat, 28 Apr 2018 09:40:05 +0800 Subject: [PATCH 0328/2296] Service Client: add HEAD method (#876) * service client: add HEAD method * use the new HEAD() function in all HEAD calls * fix error --- openstack/identity/v3/tokens/requests.go | 2 +- openstack/identity/v3/users/requests.go | 2 +- openstack/objectstorage/v1/accounts/requests.go | 2 +- openstack/objectstorage/v1/containers/requests.go | 2 +- openstack/objectstorage/v1/objects/requests.go | 2 +- service_client.go | 9 +++++++++ 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index 0323e063df..6e99a793c5 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -133,7 +133,7 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { // Validate determines if a specified token is valid or not. func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { - resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{ + resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(c, token), OkCodes: []int{200, 204, 404}, }) diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 223ad87c12..e5d396b656 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -272,7 +272,7 @@ func AddToGroup(client *gophercloud.ServiceClient, groupID, userID string) (r Ad func IsMemberOfGroup(client *gophercloud.ServiceClient, groupID, userID string) (r IsMemberOfGroupResult) { url := isMemberOfGroupURL(client, groupID, userID) var response *http.Response - response, r.Err = client.Request("HEAD", url, &gophercloud.RequestOpts{ + response, r.Err = client.Head(url, &gophercloud.RequestOpts{ OkCodes: []int{204, 404}, }) if r.Err == nil && response != nil { diff --git a/openstack/objectstorage/v1/accounts/requests.go b/openstack/objectstorage/v1/accounts/requests.go index df21587853..452a331c74 100644 --- a/openstack/objectstorage/v1/accounts/requests.go +++ b/openstack/objectstorage/v1/accounts/requests.go @@ -35,7 +35,7 @@ func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { h[k] = v } } - resp, err := c.Request("HEAD", getURL(c), &gophercloud.RequestOpts{ + resp, err := c.Head(getURL(c), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{204}, }) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index ecb76075b1..8a59fd1bc3 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -180,7 +180,7 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB // the custom metadata, pass the GetResult response to the ExtractMetadata // function. func Get(c *gophercloud.ServiceClient, containerName string) (r GetResult) { - resp, err := c.Request("HEAD", getURL(c, containerName), &gophercloud.RequestOpts{ + resp, err := c.Head(getURL(c, containerName), &gophercloud.RequestOpts{ OkCodes: []int{200, 204}, }) if resp != nil { diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index f67bfd1590..6f78d53692 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -344,7 +344,7 @@ func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts Ge } url += query } - resp, err := c.Request("HEAD", url, &gophercloud.RequestOpts{ + resp, err := c.Head(url, &gophercloud.RequestOpts{ OkCodes: []int{200, 204}, }) if resp != nil { diff --git a/service_client.go b/service_client.go index 145d932a6b..2734510e1b 100644 --- a/service_client.go +++ b/service_client.go @@ -112,6 +112,15 @@ func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Respon return client.Request("DELETE", url, opts) } +// Head calls `Request` with the "HEAD" HTTP verb. +func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = new(RequestOpts) + } + client.initReqOpts(url, nil, nil, opts) + return client.Request("HEAD", url, opts) +} + func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { switch client.Type { case "compute": From 92eaa62dc6087df950b9ae0ca4fd35edb9435588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Sat, 28 Apr 2018 11:16:32 +0800 Subject: [PATCH 0329/2296] improve error handling in AuthOptionsFromEnv() (#965) * improve error handling in AuthOptionsFromEnv() * modify error message and follow the multi-line code style --- errors.go | 32 +++++++++++++++++++++++++++++++- openstack/auth_env.go | 12 +++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/errors.go b/errors.go index 2466932efe..5088eda0d9 100644 --- a/errors.go +++ b/errors.go @@ -1,6 +1,9 @@ package gophercloud -import "fmt" +import ( + "fmt" + "strings" +) // BaseError is an error type that all other error types embed. type BaseError struct { @@ -43,6 +46,33 @@ func (e ErrInvalidInput) Error() string { return e.choseErrString() } +// ErrMissingEnvironmentVariable is the error when environment variable is required +// in a particular situation but not provided by the user +type ErrMissingEnvironmentVariable struct { + BaseError + EnvironmentVariable string +} + +func (e ErrMissingEnvironmentVariable) Error() string { + e.DefaultErrString = fmt.Sprintf("Missing environment variable [%s]", e.EnvironmentVariable) + return e.choseErrString() +} + +// ErrMissingAnyoneOfEnvironmentVariables is the error when anyone of the environment variables +// is required in a particular situation but not provided by the user +type ErrMissingAnyoneOfEnvironmentVariables struct { + BaseError + EnvironmentVariables []string +} + +func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string { + e.DefaultErrString = fmt.Sprintf( + "Missing one of the following environment variables [%s]", + strings.Join(e.EnvironmentVariables, ", "), + ) + return e.choseErrString() +} + // ErrUnexpectedResponseCode is returned by the Request method when a response code other than // those listed in OkCodes is encountered. type ErrUnexpectedResponseCode struct { diff --git a/openstack/auth_env.go b/openstack/auth_env.go index b5482ba8c9..994b5550c9 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -50,17 +50,23 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { } if authURL == "" { - err := gophercloud.ErrMissingInput{Argument: "authURL"} + err := gophercloud.ErrMissingEnvironmentVariable{ + EnvironmentVariable: "OS_AUTH_URL", + } return nilOptions, err } if username == "" && userID == "" { - err := gophercloud.ErrMissingInput{Argument: "username"} + err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ + EnvironmentVariables: []string{"OS_USERNAME", "OS_USERID"}, + } return nilOptions, err } if password == "" { - err := gophercloud.ErrMissingInput{Argument: "password"} + err := gophercloud.ErrMissingEnvironmentVariable{ + EnvironmentVariable: "OS_PASSWORD", + } return nilOptions, err } From 01a8bffb447a6c924f92126c98c2f6dbe6387750 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Mon, 23 Apr 2018 16:28:35 -0700 Subject: [PATCH 0330/2296] Add delete for messages --- .../openstack/messaging/v2/message_test.go | 99 +++++++++++++++++++ .../openstack/messaging/v2/messaging.go | 25 ++++- .../openstack/messaging/v2/queue_test.go | 2 +- openstack/messaging/v2/messages/doc.go | 26 +++++ openstack/messaging/v2/messages/requests.go | 71 +++++++++++++ openstack/messaging/v2/messages/results.go | 30 ++++++ .../messaging/v2/messages/testing/fixtures.go | 58 +++++++++++ .../v2/messages/testing/requests_test.go | 27 +++++ openstack/messaging/v2/messages/urls.go | 4 + 9 files changed, 340 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/messaging/v2/message_test.go b/acceptance/openstack/messaging/v2/message_test.go index c5d1b8aac5..162b76edd6 100644 --- a/acceptance/openstack/messaging/v2/message_test.go +++ b/acceptance/openstack/messaging/v2/message_test.go @@ -60,3 +60,102 @@ func TestCreateMessages(t *testing.T) { CreateMessage(t, client, createdQueueName) } + +func TestDeleteMessagesIDs(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-718613007343" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + CreateMessage(t, client, createdQueueName) + CreateMessage(t, client, createdQueueName) + + // Use a different client/clientID in order to see messages on the Queue + clientID = "3381af92-2b9e-11e3-b191-71861300734d" + + client, err = clients.NewMessagingV2Client(clientID) + + listOpts := messages.ListOpts{} + + var messageIDs []string + + pager := messages.List(client, createdQueueName, listOpts) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + allMessages, err := messages.ExtractMessages(page) + if err != nil { + t.Fatalf("Unable to extract messages: %v", err) + } + + for _, message := range allMessages { + messageIDs = append(messageIDs, message.ID) + tools.PrintResource(t, message) + } + + return true, nil + }) + + deleteOpts := messages.DeleteMessagesOpts{ + IDs: messageIDs, + } + + t.Logf("Attempting to delete messages: %v", messageIDs) + deleteErr := messages.DeleteMessages(client, createdQueueName, deleteOpts).ExtractErr() + if deleteErr != nil { + t.Fatalf("Unable to delete messages: %v", deleteErr) + } + + t.Logf("Attempting to list messages.") + messageList, err := ListMessages(t, client, createdQueueName) + + if len(messageList) > 0 { + t.Fatalf("Did not delete all specified messages in the queue.") + } +} + +func TestDeleteMessagesPop(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-718613007343" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + for i := 0; i < 5; i++ { + CreateMessage(t, client, createdQueueName) + } + + // Use a different client/clientID in order to see messages on the Queue + clientID = "3381af92-2b9e-11e3-b191-71861300734d" + + client, err = clients.NewMessagingV2Client(clientID) + + messageList, err := ListMessages(t, client, createdQueueName) + + messagesNumber := len(messageList) + popNumber := 3 + + PopOpts := messages.PopMessagesOpts{ + Pop: popNumber, + } + + t.Logf("Attempting to Pop last %v messages.", popNumber) + popMessages, deleteErr := messages.PopMessages(client, createdQueueName, PopOpts).Extract() + if deleteErr != nil { + t.Fatalf("Unable to Pop messages: %v", deleteErr) + } + + tools.PrintResource(t, popMessages) + + messageList, err = ListMessages(t, client, createdQueueName) + if len(messageList) != messagesNumber-popNumber { + t.Fatalf("Unable to Pop specified number of messages.") + } +} diff --git a/acceptance/openstack/messaging/v2/messaging.go b/acceptance/openstack/messaging/v2/messaging.go index 1c7b8cc6a9..5b98cafdba 100644 --- a/acceptance/openstack/messaging/v2/messaging.go +++ b/acceptance/openstack/messaging/v2/messaging.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" + "github.com/gophercloud/gophercloud/pagination" ) func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error) { @@ -54,7 +55,7 @@ func GetQueue(t *testing.T, client *gophercloud.ServiceClient, queueName string) return queue, nil } -func CreateShare(t *testing.T, client *gophercloud.ServiceClient, queueName string, clientID string) (queues.QueueShare, error) { +func CreateShare(t *testing.T, client *gophercloud.ServiceClient, queueName string) (queues.QueueShare, error) { t.Logf("Attempting to create share for queue: %s", queueName) shareOpts := queues.ShareOpts{ @@ -85,3 +86,25 @@ func CreateMessage(t *testing.T, client *gophercloud.ServiceClient, queueName st return resource, err } + +func ListMessages(t *testing.T, client *gophercloud.ServiceClient, queueName string) ([]messages.Message, error) { + listOpts := messages.ListOpts{} + var allMessages []messages.Message + var listErr error + + t.Logf("Attempting to list messages on queue: %s", queueName) + pager := messages.List(client, queueName, listOpts) + err := pager.EachPage(func(page pagination.Page) (bool, error) { + allMessages, listErr = messages.ExtractMessages(page) + if listErr != nil { + t.Fatalf("Unable to extract messages: %v", listErr) + } + + for _, message := range allMessages { + tools.PrintResource(t, message) + } + + return true, nil + }) + return allMessages, err +} diff --git a/acceptance/openstack/messaging/v2/queue_test.go b/acceptance/openstack/messaging/v2/queue_test.go index f03b26f6b5..166f46e83d 100644 --- a/acceptance/openstack/messaging/v2/queue_test.go +++ b/acceptance/openstack/messaging/v2/queue_test.go @@ -121,7 +121,7 @@ func TestShare(t *testing.T) { defer DeleteQueue(t, client, queueName) t.Logf("Attempting to create share for queue: %s", queueName) - share, shareErr := CreateShare(t, client, queueName, clientID) + share, shareErr := CreateShare(t, client, queueName) if shareErr != nil { t.Fatalf("Unable to create share: %v", shareErr) } diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go index f623c45e71..c16b2f4c5a 100644 --- a/openstack/messaging/v2/messages/doc.go +++ b/openstack/messaging/v2/messages/doc.go @@ -53,5 +53,31 @@ Example to Create Messages if err != nil { panic(err) } + +Example to Delete a set of Messages + + queueName := "my_queue" + + deleteMessagesOpts := messages.DeleteMessagesOpts{ + IDs: []string{"9988776655"}, + } + + err := messages.DeleteMessages(client, queueName, deleteMessagesOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Pop a set of Messages + + queueName := "my_queue" + + deleteMessagesOpts := messages.DeleteMessageOpts{ + Pop: 5, + } + + resources, err := messages.PopMessages(client, queueName, deleteMessagesOpts).Extract() + if err != nil { + panic(err) + } */ package messages diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index bccd84c84d..aadc845d7b 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -103,3 +103,74 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts }) return } + +// DeleteMessagesOptsBuilder allows extensions to add additional parameters to the +// DeleteMessages request. +type DeleteMessagesOptsBuilder interface { + ToMessagesDeleteQuery() (string, error) +} + +// DeleteMessagesOpts params to be used with DeleteMessages. +type DeleteMessagesOpts struct { + IDs []string `q:"ids,omitempty"` +} + +// ToMessagesDeleteQuery formats a DeleteMessagesOpts structure into a query string. +func (opts DeleteMessagesOpts) ToMessagesDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// DeleteMessages deletes multiple messages based off of ID. +func DeleteMessages(client *gophercloud.ServiceClient, queueName string, opts DeleteMessagesOptsBuilder) (r DeleteResult) { + url := deleteURL(client, queueName) + if opts != nil { + query, err := opts.ToMessagesDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + OkCodes: []int{200, 204}, + }) + return +} + +// PopMessagesOptsBuilder allows extensions to add additional parameters to the +// DeleteMessages request. +type PopMessagesOptsBuilder interface { + ToMessagesPopQuery() (string, error) +} + +// PopMessagesOpts params to be used with PopMessages. +type PopMessagesOpts struct { + Pop int `q:"pop,omitempty"` +} + +// ToMessagesPopQuery formats a PopMessagesOpts structure into a query string. +func (opts PopMessagesOpts) ToMessagesPopQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// PopMessages deletes and returns multiple messages based off of number of messages. +func PopMessages(client *gophercloud.ServiceClient, queueName string, opts PopMessagesOptsBuilder) (r PopResult) { + url := deleteURL(client, queueName) + if opts != nil { + query, err := opts.ToMessagesPopQuery() + if err != nil { + r.Err = err + return + } + url += query + } + result, err := client.Delete(url, &gophercloud.RequestOpts{ + OkCodes: []int{200, 204}, + }) + r.Body = result.Body + r.Header = result.Header + r.Err = err + return +} diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go index abc20993f2..9ce5103d63 100644 --- a/openstack/messaging/v2/messages/results.go +++ b/openstack/messaging/v2/messages/results.go @@ -20,6 +20,17 @@ type MessagePage struct { pagination.LinkedPageBase } +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// CreateResult is the response of a Create operations. +type PopResult struct { + gophercloud.Result +} + // Message represents a message on a queue. type Message struct { Body map[string]interface{} `json:"body"` @@ -30,6 +41,16 @@ type Message struct { Checksum string `json:"checksum"` } +// PopMessage represents a message returned from PopMessages. +type PopMessage struct { + Body map[string]interface{} `json:"body"` + Age int `json:"age"` + ID string `json:"id"` + TTL int `json:"ttl"` + ClaimCount int `json:"claim_count"` + ClaimID string `json:"claim_id"` +} + // ResourceList represents the result of creating a message. type ResourceList struct { Resources []string `json:"resources"` @@ -42,6 +63,15 @@ func (r CreateResult) Extract() (ResourceList, error) { return s, err } +// Extract interprets any PopResult as a list of PopMessage. +func (r PopResult) Extract() ([]PopMessage, error) { + var s struct { + PopMessages []PopMessage `json:"messages"` + } + err := r.ExtractInto(&s) + return s.PopMessages, err +} + // ExtractMessage extracts message into a list of Message. func ExtractMessages(r pagination.Page) ([]Message, error) { var s struct { diff --git a/openstack/messaging/v2/messages/testing/fixtures.go b/openstack/messaging/v2/messages/testing/fixtures.go index d8cd010ae1..d85a7d922d 100644 --- a/openstack/messaging/v2/messages/testing/fixtures.go +++ b/openstack/messaging/v2/messages/testing/fixtures.go @@ -95,6 +95,25 @@ const ListMessagesResponse2 = ` }` +// PopMessageResponse is a sample reponse to pop messages +const PopMessageResponse = ` +{ + "messages": [ + { + "body": { + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720" + }, + "age": 20, + "ttl": 120, + "claim_count": 55, + "claim_id": "123456", + "id": "5ae7972599352b436763aee7" + } + ] +}` + // ExpectedResources is the expected result in Create var ExpectedResources = messages.ResourceList{ Resources: []string{ @@ -134,6 +153,20 @@ var SecondMessage = messages.Message{ // ExpectedMessagesSlice is the expected result in a List. var ExpectedMessagesSlice = [][]messages.Message{{FirstMessage}, {SecondMessage}} +// ExpectedPopMessage is the expected result of a Pop. +var ExpectedPopMessage = []messages.PopMessage{{ + Body: map[string]interface{}{ + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720", + }, + Age: 20, + TTL: 120, + ClaimID: "123456", + ClaimCount: 55, + ID: "5ae7972599352b436763aee7", +}} + // HandleCreateSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), @@ -168,3 +201,28 @@ func HandleListSuccessfully(t *testing.T) { } }) } + +// HandleDeleteMessagesSuccessfully configures the test server to respond to a Delete request. +func HandleDeleteMessagesSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), + 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) + }) +} + +// HandlePopSuccessfully configures the test server to respond to a Pop request. +func HandlePopSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), + 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.StatusOK) + fmt.Fprintf(w, PopMessageResponse) + }) +} diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go index 9dc9633313..300ef23be5 100644 --- a/openstack/messaging/v2/messages/testing/requests_test.go +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -60,3 +60,30 @@ func TestCreate(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedResources, actual) } + +func TestDeleteMessages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteMessagesSuccessfully(t) + + deleteMessagesOpts := messages.DeleteMessagesOpts{ + IDs: []string{"9988776655"}, + } + + err := messages.DeleteMessages(fake.ServiceClient(), QueueName, deleteMessagesOpts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestPopMessages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePopSuccessfully(t) + + popMessagesOpts := messages.PopMessagesOpts{ + Pop: 1, + } + + actual, err := messages.PopMessages(fake.ServiceClient(), QueueName, popMessagesOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedPopMessage, actual) +} diff --git a/openstack/messaging/v2/messages/urls.go b/openstack/messaging/v2/messages/urls.go index 30059855e2..3d48c54cf1 100644 --- a/openstack/messaging/v2/messages/urls.go +++ b/openstack/messaging/v2/messages/urls.go @@ -29,3 +29,7 @@ func nextPageURL(currentURL string, next string) (string, error) { } return base.ResolveReference(rel).String(), nil } + +func deleteURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") +} From 242a24b4f7bc39e508f1b1c7376874d636705a63 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Mon, 30 Apr 2018 20:05:36 -0700 Subject: [PATCH 0331/2296] Clustering Policy Validate Implementation (#951) * senlin-policies-validate * Removed returning empty policy object when error --- .../openstack/clustering/v1/policies_test.go | 33 ++++-- openstack/clustering/v1/policies/doc.go | 24 ++++ openstack/clustering/v1/policies/requests.go | 28 +++++ openstack/clustering/v1/policies/results.go | 6 +- .../v1/policies/testing/fixtures.go | 111 ++++++++++++++++++ .../v1/policies/testing/requests_test.go | 31 +++++ openstack/clustering/v1/policies/urls.go | 4 + 7 files changed, 229 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go index 8fff7a5f54..9d3de972d7 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -35,12 +35,14 @@ func TestPolicyList(t *testing.T) { } } -func TestPolicyCreateAndDelete(t *testing.T) { +func TestPolicyCreateUpdateValidateDelete(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) + testName := tools.RandomString("TESTACC-", 8) + client.Microversion = "1.5" - opts := policies.CreateOpts{ - Name: "new_policy2", + createOpts := policies.CreateOpts{ + Name: testName, Spec: policies.Spec{ Description: "new policy description", Properties: map[string]interface{}{ @@ -54,7 +56,7 @@ func TestPolicyCreateAndDelete(t *testing.T) { }, } - createdPolicy, err := policies.Create(client, opts).Extract() + createdPolicy, err := policies.Create(client, createOpts).Extract() th.AssertNoErr(t, err) defer policies.Delete(client, createdPolicy.ID) @@ -62,11 +64,28 @@ func TestPolicyCreateAndDelete(t *testing.T) { tools.PrintResource(t, createdPolicy) if createdPolicy.CreatedAt.IsZero() { - t.Fatalf("CreatedAt value should not be zero") + t.Fatalf("CreatePolicy's CreatedAt value should not be zero") } - t.Log("Created at: " + createdPolicy.CreatedAt.String()) + t.Log("CreatePolicy created at: " + createdPolicy.CreatedAt.String()) if !createdPolicy.UpdatedAt.IsZero() { - t.Log("Updated at: " + createdPolicy.UpdatedAt.String()) + t.Log("CreatePolicy updated at: " + createdPolicy.UpdatedAt.String()) + } + + validateOpts := policies.ValidateOpts{ + Spec: createOpts.Spec, + } + + validatePolicy, err := policies.Validate(client, validateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, validatePolicy) + + if validatePolicy.Name != "validated_policy" { + t.Fatalf("ValidatePolicy's Name value should be 'validated_policy'") + } + + if validatePolicy.Spec.Version != createOpts.Spec.Version { + t.Fatalf("ValidatePolicy's Version value should be ", createOpts.Spec.Version) } } diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index 90830c8dcf..b458f679ab 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -48,5 +48,29 @@ Example to Create a policy panic(err) } +Example to Validate a policy + + opts := policies.ValidateOpts{ + Spec: policies.Spec{ + Description: "new policy description", + Properties: map[string]interface{}{ + "hooks": map[string]interface{}{ + "type": "zaqar", + "params": map[string]interface{}{ + "queue": "my_zaqar_queue", + }, + "timeout": 10, + }, + }, + Type: "senlin.policy.deletion", + Version: "1.1", + }, + } + + validatePolicy, err := policies.Validate(client, opts).Extract() + if err != nil { + panic(err) + } + */ package policies diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index 6a6f1657b7..6061ab7514 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -95,3 +95,31 @@ func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) r.Header = result.Header return } + +// ValidateOpts params +type ValidateOpts struct { + Spec Spec `json:"spec"` +} + +// ToValidatePolicyMap formats a CreateOpts into a body map. +func (opts ValidateOpts) ToValidatePolicyMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "policy") +} + +// Validate policy. +func Validate(client *gophercloud.ServiceClient, opts ValidateOpts) (r ValidateResult) { + b, err := opts.ToValidatePolicyMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, + }) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index a0a0356fa9..eb69720f92 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -131,7 +131,7 @@ type policyResult struct { gophercloud.Result } -func (r CreateResult) Extract() (*Policy, error) { +func (r policyResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"policy"` } @@ -148,6 +148,10 @@ type DeleteResult struct { gophercloud.HeaderResult } +type ValidateResult struct { + policyResult +} + // DeleteResult contains the delete information from a delete policy request type DeleteHeader struct { RequestID string `json:"X-OpenStack-Request-ID"` diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go index 7f2aaa92bb..3d8f095f59 100644 --- a/openstack/clustering/v1/policies/testing/fixtures.go +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -99,6 +99,66 @@ const PolicyCreateBody = ` } ` +const PolicyValidateBody = ` +{ + "policy": { + "created_at": "2018-04-02T21:43:30.000000", + "data": {}, + "domain": null, + "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + "name": "delpol4", + "project": "018cd0909fb44cd5bc9b7a3cd664920e", + "spec": { + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": { + "hooks": { + "params": { + "queue": "zaqar_queue_name" + }, + "timeout": 180, + "type": "zaqar" + } + }, + "type": "senlin.policy.deletion", + "version": 1.1 + }, + "type": "senlin.policy.deletion-1.1", + "updated_at": null, + "user": "fe43e41739154b72818565e0d2580819" + } +} +` + +const PolicyBadValidateBody = ` +{ + "policy": { + "created_at": "invalid", + "data": {}, + "domain": null, + "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + "name": "delpol4", + "project": "018cd0909fb44cd5bc9b7a3cd664920e", + "spec": { + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": { + "hooks": { + "params": { + "queue": "zaqar_queue_name" + }, + "timeout": 180, + "type": "zaqar" + } + }, + "type": "senlin.policy.deletion", + "version": 1.1 + }, + "type": "invalid", + "updated_at": null, + "user": "fe43e41739154b72818565e0d2580819" + } +} +` + const PolicyDeleteRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" var ( @@ -191,6 +251,33 @@ var ( User: "fe43e41739154b72818565e0d2580819", UpdatedAt: ZeroTime, } + + ExpectedValidatePolicy = policies.Policy{ + CreatedAt: ExpectedPolicyCreatedAt1, + Data: map[string]interface{}{}, + Domain: "", + ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + Name: "delpol4", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "hooks": map[string]interface{}{ + "params": map[string]interface{}{ + "queue": "zaqar_queue_name", + }, + "timeout": float64(180), + "type": "zaqar", + }, + }, + Type: "senlin.policy.deletion", + Version: "1.1", + }, + Type: "senlin.policy.deletion-1.1", + User: "fe43e41739154b72818565e0d2580819", + UpdatedAt: ZeroTime, + } ) func HandlePolicyList(t *testing.T) { @@ -237,3 +324,27 @@ func HandlePolicyDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandlePolicyValidate(t *testing.T) { + th.Mux.HandleFunc("/v1/policies/validate", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, PolicyValidateBody) + }) +} + +func HandleBadPolicyValidate(t *testing.T) { + th.Mux.HandleFunc("/v1/policies/validate", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, PolicyBadValidateBody) + }) +} diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go index f077a5459a..6f954be40c 100644 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -70,3 +70,34 @@ func TestDeletePolicy(t *testing.T) { th.AssertEquals(t, PolicyDeleteRequestID, actual.RequestID) } + +func TestValidatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandlePolicyValidate(t) + + expected := ExpectedValidatePolicy + + opts := policies.ValidateOpts{ + Spec: ExpectedValidatePolicy.Spec, + } + + actual, err := policies.Validate(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, &expected, actual) +} + +func TestBadValidatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleBadPolicyValidate(t) + + opts := policies.ValidateOpts{ + Spec: ExpectedValidatePolicy.Spec, + } + + _, err := policies.Validate(fake.ServiceClient(), opts).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/policies/urls.go b/openstack/clustering/v1/policies/urls.go index d705e8a3fc..468c003174 100644 --- a/openstack/clustering/v1/policies/urls.go +++ b/openstack/clustering/v1/policies/urls.go @@ -18,3 +18,7 @@ func policyCreateURL(client *gophercloud.ServiceClient) string { func policyDeleteURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(apiVersion, apiName, policyID) } + +func validateURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName, "validate") +} From 91daeec5a7f53ee0c37677f719957e24c2813d5b Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Tue, 1 May 2018 11:08:23 -0700 Subject: [PATCH 0332/2296] Returns headers for senlin's policy create --- acceptance/openstack/clustering/v1/policies_test.go | 10 ++++++++-- openstack/clustering/v1/policies/requests.go | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go index 9d3de972d7..f87fb62ae0 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -52,11 +52,17 @@ func TestPolicyCreateUpdateValidateDelete(t *testing.T) { "criteria": "OLDEST_FIRST", }, Type: "senlin.policy.deletion", - Version: "1.0", + Version: "1.1", }, } - createdPolicy, err := policies.Create(client, createOpts).Extract() + createResult := policies.Create(client, createOpts) + th.AssertNoErr(t, createResult.Err) + + requestID := createResult.Header.Get("X-Openstack-Request-ID") + th.AssertEquals(t, true, requestID != "") + + createdPolicy, err := createResult.Extract() th.AssertNoErr(t, err) defer policies.Delete(client, createdPolicy.ID) diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index 6061ab7514..bf685f7de0 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -80,9 +80,13 @@ func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) r.Err = err return } - _, r.Err = client.Post(policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + var result *http.Response + result, r.Err = client.Post(policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + if r.Err == nil { + r.Header = result.Header + } return } From b35b0633c08973f1b749f54ac6c26250aa9bbe02 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Wed, 2 May 2018 18:43:59 -0700 Subject: [PATCH 0333/2296] Senlin: Profiles Create (#871) * senlin-profiles-create * Added build tags * Added detailed fields for Spec: Type, Version, Properties. * Added invalid time unit test cases * Added test case for retrieving header --- .../clustering/v1/autoscaling_test.go | 70 ++++++ openstack/clustering/v1/profiles/doc.go | 32 +++ openstack/clustering/v1/profiles/requests.go | 42 ++++ openstack/clustering/v1/profiles/results.go | 95 ++++++++ .../clustering/v1/profiles/testing/doc.go | 2 + .../v1/profiles/testing/requests_test.go | 217 ++++++++++++++++++ openstack/clustering/v1/profiles/urls.go | 14 ++ 7 files changed, 472 insertions(+) create mode 100644 acceptance/openstack/clustering/v1/autoscaling_test.go create mode 100644 openstack/clustering/v1/profiles/doc.go create mode 100644 openstack/clustering/v1/profiles/requests.go create mode 100644 openstack/clustering/v1/profiles/results.go create mode 100644 openstack/clustering/v1/profiles/testing/doc.go create mode 100644 openstack/clustering/v1/profiles/testing/requests_test.go create mode 100644 openstack/clustering/v1/profiles/urls.go diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go new file mode 100644 index 0000000000..4ff8b63606 --- /dev/null +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -0,0 +1,70 @@ +// +build acceptance clustering autoscaling profiles + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" + + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +var testName string + +func TestAutoScaling(t *testing.T) { + testName = tools.RandomString("TESTACC-", 8) + profileCreate(t) +} + +func profileCreate(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + networks := []map[string]interface{}{ + {"network": "sandbox-internal-net"}, + } + + props := map[string]interface{}{ + "name": "centos-server", + "flavor": "t2.micro", + "image": "centos7.3-latest", + "networks": networks, + "security_groups": "", + } + + profileName := testName + optsProfile := &profiles.CreateOpts{ + Metadata: map[string]interface{}{ + "foo": "bar", + "test": "123", + }, + Name: profileName, + Spec: profiles.Spec{ + Type: "os.nova.server", + Version: "1.0", + Properties: props, + }, + } + + createResult := profiles.Create(client, optsProfile) + th.AssertNoErr(t, createResult.Err) + + requestID := createResult.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, true, requestID != "") + + profile, err := createResult.Extract() + if err != nil { + t.Fatalf("Unable to create profile %s: %v", profileName, err) + } else { + t.Logf("Profile created %v", profile) + } + + th.AssertEquals(t, profileName, profile.Name) + th.AssertEquals(t, "os.nova.server", profile.Spec.Type) + th.AssertEquals(t, "1.0", profile.Spec.Version) +} diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go new file mode 100644 index 0000000000..f4c96cd659 --- /dev/null +++ b/openstack/clustering/v1/profiles/doc.go @@ -0,0 +1,32 @@ +/* +Example to Create a profile + + networks := []map[string]interface{} { + {"network": "test-network"}, + } + + props := map[string]interface{}{ + "name": "test_gophercloud_profile", + "flavor": "t2.micro", + "image": "centos7.3-latest", + "networks": networks, + "security_groups": "", + } + + createOpts := profiles.CreateOpts { + Name: "test_profile", + Spec: profiles.Spec{ + Type: "os.nova.server", + Version: "1.0", + Properties: props, + }, + } + + profile, err := profiles.Create(serviceClient, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println("Profile", profile) +*/ +package profiles diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go new file mode 100644 index 0000000000..2c3475bf27 --- /dev/null +++ b/openstack/clustering/v1/profiles/requests.go @@ -0,0 +1,42 @@ +package profiles + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder for options used for creating a profile. +type CreateOptsBuilder interface { + ToProfileCreateMap() (map[string]interface{}, error) +} + +// CreateOpts represents options used for creating a profile +type CreateOpts struct { + Name string `json:"name" required:"true"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + Spec Spec `json:"spec" required:"true"` +} + +// ToProfileCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToProfileCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "profile") +} + +// Create requests the creation of a new profile on the server. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToProfileCreateMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, + }) + + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go new file mode 100644 index 0000000000..c72a2e891f --- /dev/null +++ b/openstack/clustering/v1/profiles/results.go @@ -0,0 +1,95 @@ +package profiles + +import ( + "encoding/json" + "fmt" + "time" + + "reflect" + + "github.com/gophercloud/gophercloud" +) + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Create operation. +type CreateResult struct { + commonResult +} + +// Extract provides access to Profile returned by the Get and Create functions. +func (r commonResult) Extract() (*Profile, error) { + var s struct { + Profile *Profile `json:"profile"` + } + err := r.ExtractInto(&s) + return s.Profile, err +} + +type Spec struct { + Type string `json:"type"` + Version string `json:"version"` + Properties map[string]interface{} `json:"properties"` +} + +// Profile represent a detailed profile +type Profile struct { + CreatedAt time.Time `json:"-"` + Domain string `json:"domain"` + ID string `json:"id"` + Metadata map[string]interface{} `json:"metadata"` + Name string `json:"name"` + Project string `json:"project"` + Spec Spec `json:"spec"` + Type string `json:"type"` + UpdatedAt time.Time `json:"-"` + User string `json:"user"` +} + +func (r *Profile) UnmarshalJSON(b []byte) error { + type tmp Profile + var s struct { + tmp + CreatedAt interface{} `json:"created_at"` + UpdatedAt interface{} `json:"updated_at"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Profile(s.tmp) + + switch t := s.CreatedAt.(type) { + case string: + if t != "" { + r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.CreatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) + } + + switch t := s.UpdatedAt.(type) { + case string: + if t != "" { + r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.UpdatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) + } + + return nil +} diff --git a/openstack/clustering/v1/profiles/testing/doc.go b/openstack/clustering/v1/profiles/testing/doc.go new file mode 100644 index 0000000000..2a99a8a64b --- /dev/null +++ b/openstack/clustering/v1/profiles/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_profiles_v1 +package testing diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go new file mode 100644 index 0000000000..9a9c2939cb --- /dev/null +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -0,0 +1,217 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateProfile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "profile": { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "test-profile", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "centos7.3-latest", + "name": "centos_server", + "networks": [ + { + "network": "private-network" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "2016-01-03T17:22:23Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + networks := []map[string]interface{}{ + {"network": "private-network"}, + } + + props := map[string]interface{}{ + "name": "test_gopher_cloud_profile", + "flavor": "t2.small", + "image": "centos7.3-latest", + "networks": networks, + "security_groups": "", + } + + optsProfile := &profiles.CreateOpts{ + Name: "TestProfile", + Spec: profiles.Spec{ + Type: "os.nova.server", + Version: "1.0", + Properties: props, + }, + } + + profile, err := profiles.Create(fake.ServiceClient(), optsProfile).Extract() + if err != nil { + t.Errorf("Failed to extract profile: %v", err) + } + + createdAt, _ := time.Parse(time.RFC3339, "2016-01-03T16:22:23Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-01-03T17:22:23Z") + + expected := profiles.Profile{ + CreatedAt: createdAt, + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "test-profile", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.small", + "image": "centos7.3-latest", + "name": "centos_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private-network"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: updatedAt, + User: "5e5bf8027826429c96af157f68dc9072", + } + + th.AssertDeepEquals(t, expected, *profile) +} + +func TestCreateProfileInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "profile": { + "created_at": 123456789.0, + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "test-profile", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "centos7.3-latest", + "name": "centos_server", + "networks": [ + { + "network": "private-network" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": 123456789.0, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + optsProfile := &profiles.CreateOpts{ + Name: "TestProfile", + Spec: profiles.Spec{ + Type: "os.nova.server", + Version: "1.0", + Properties: map[string]interface{}{}, + }, + } + + _, err := profiles.Create(fake.ServiceClient(), optsProfile).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestCreateProfileInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "profile": { + "created_at": "invalid_time", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "test-profile", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "centos7.3-latest", + "name": "centos_server", + "networks": [ + { + "network": "private-network" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "invalid_time", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + optsProfile := &profiles.CreateOpts{ + Name: "TestProfile", + Spec: profiles.Spec{ + Type: "os.nova.server", + Version: "1.0", + Properties: map[string]interface{}{}, + }, + } + + _, err := profiles.Create(fake.ServiceClient(), optsProfile).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/profiles/urls.go b/openstack/clustering/v1/profiles/urls.go new file mode 100644 index 0000000000..123be5eda4 --- /dev/null +++ b/openstack/clustering/v1/profiles/urls.go @@ -0,0 +1,14 @@ +package profiles + +import "github.com/gophercloud/gophercloud" + +var apiVersion = "v1" +var apiName = "profiles" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} + +func createURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 4504252941557ab436f50e44a0d394f147a461f2 Mon Sep 17 00:00:00 2001 From: Kevin Zhao Date: Thu, 3 May 2018 13:05:05 +1000 Subject: [PATCH 0334/2296] Add container dict, host, status_reason field to capsule (#969) * Add container dict, host, status_reason field to capsule Signed-off-by: Kevin Zhao * Fix gofmt error Change-Id: I6f7c6b49a7b02256fc27f44b6f829a3b6f437b1a Signed-off-by: Kevin Zhao --- openstack/container/v1/capsules/results.go | 145 +++++++++++++++++- .../container/v1/capsules/testing/fixtures.go | 63 +++++++- .../v1/capsules/testing/requests_test.go | 94 +++++++++++- 3 files changed, 293 insertions(+), 9 deletions(-) diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index cfc19663c1..f494d825cd 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -52,10 +52,12 @@ type Capsule struct { // The name of the capsule MetaName string `json:"meta_name"` - // Indicates whether capsule is currently operational. Possible values include: - // Running, + // Indicates whether capsule is currently operational. Status string `json:"status"` + // Indicates whether capsule is currently operational. + StatusReason string `json:"status_reason"` + // The created time of the capsule. CreatedAt time.Time `json:"-"` @@ -83,6 +85,126 @@ type Capsule struct { // The capsule volume attached information VolumesInfo map[string][]string `json:"volumes_info"` + + // The container object inside capsule + Containers []Container `json:"containers"` + + // The capsule host + Host string `json:"host"` +} + +type Container struct { + // The Container IP addresses + Addresses map[string][]Address `json:"addresses"` + + // UUID for the container + UUID string `json:"uuid"` + + // ID for the container + ID int `json:"id"` + + // User ID for the container + UserID string `json:"user_id"` + + // Project ID for the container + ProjectID string `json:"project_id"` + + // cpu for the container + CPU float64 `json:"cpu"` + + // Memory for the container + Memory string `json:"memory"` + + // Image for the container + Image string `json:"image"` + + // The container container + Labels map[string]string `json:"labels"` + + // The created time of the container + CreatedAt time.Time `json:"-"` + + // The updated time of the container + UpdatedAt time.Time `json:"-"` + + // Name for the container + Name string `json:"name"` + + // Links includes HTTP references to the itself, useful for passing along to + // other APIs that might want a server reference. + Links []interface{} `json:"links"` + + // Container ID for the container + ContainerID string `json:"container_id"` + + // Websocket url for the container + WebsocketUrl string `json:"websocket_url"` + + // Websocket token for the container + WebsocketToken string `json:"websocket_token"` + + // auto remove flag token for the container + AutoRemove bool `json:"auto_remove"` + + // Host for the container + Host string `json:"host"` + + // Work directory for the container + WorkDir string `json:"workdir"` + + // Disk for the container + Disk int `json:"disk"` + + // Image pull policy for the container + ImagePullPolicy string `json:"image_pull_policy"` + + // Task state for the container + TaskState string `json:"task_state"` + + // Host name for the container + HostName string `json:"hostname"` + + // Environment for the container + Environment map[string]string `json:"environment"` + + // Status for the container + Status string `json:"status"` + + // Auto Heal flag for the container + AutoHeal bool `json:"auto_heal"` + + // Status details for the container + StatusDetail string `json:"status_detail"` + + // Status reason for the container + StatusReason string `json:"status_reason"` + + // Image driver for the container + ImageDriver string `json:"image_driver"` + + // Command for the container + Command string `json:"command"` + + // Capsule ID for the container + CapsuleID int `json:"capsule_id"` + + // Image for the container + Runtime string `json:"runtime"` + + // Interactive flag for the container + Interactive bool `json:"interactive"` + + // Restart Policy for the container + RestartPolicy map[string]string `json:"restart_policy"` + + // Ports information for the container + Ports []int `json:"ports"` + + // Meta for the container + Meta map[string]string `json:"meta"` + + // Security groups for the container + SecurityGroups []string `json:"security_groups"` } type Address struct { @@ -111,3 +233,22 @@ func (r *Capsule) UnmarshalJSON(b []byte) error { return nil } + +func (r *Container) UnmarshalJSON(b []byte) error { + type tmp Container + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoT `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339ZNoT `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Container(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index d3d1d7fe0c..06e55d9e5b 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -28,7 +28,7 @@ func HandleCapsuleGetSuccessfully(t *testing.T) { "meta_name": "test", "meta_labels": {"web": "app"}, "created_at": "2018-01-12 09:37:25+00:00", - "updated_at": "2018-01-12 09:37:25+01:00", + "updated_at": "2018-01-12 09:37:26+00:00", "links": [ { "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", @@ -41,7 +41,7 @@ func HandleCapsuleGetSuccessfully(t *testing.T) { ], "capsule_version": "beta", "restart_policy": "always", - "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "d1469e8d-bcbc-43fc-b163-8b9b6a740930"], + "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b"], "addresses": { "b1295212-64e1-471d-aa01-25ff46f9818d": [ { @@ -55,9 +55,64 @@ func HandleCapsuleGetSuccessfully(t *testing.T) { }, "volumes_info": { "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ - "4b725a92-2197-497b-b6b1-fb8caa4cb99b" + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" ] - } + }, + "host": "test-host", + "status_reason": "No reason", + "containers": [ + { + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "image": "test", + "labels": {"foo": "bar"}, + "created_at": "2018-01-12 09:37:25+00:00", + "updated_at": "2018-01-12 09:37:26+00:00", + "workdir": "/root", + "disk": 0, + "id": 1, + "security_groups": ["default"], + "image_pull_policy": "ifnotpresent", + "task_state": "Creating", + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "uuid": "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", + "hostname": "test-hostname", + "environment": {"USER1": "test"}, + "memory": "1024M", + "status": "Running", + "auto_remove": false, + "container_id": "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", + "websocket_url": "ws://10.10.10.10/", + "auto_heal": false, + "host": "test-host", + "image_driver": "docker", + "status_detail": "Just created", + "status_reason": "No reason", + "websocket_token": "2ba16a5a-552f-422f-b511-bd786102691f", + "name": "test-demo-omicron-13", + "restart_policy": { + "MaximumRetryCount": "0", + "Name": "always" + }, + "ports": [80], + "meta": {"key1": "value1"}, + "command": "testcmd", + "capsule_id": 1, + "runtime": "runc", + "cpu": 1, + "interactive": true + } + ] }`) }) } diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index f7e6c37227..d92bb7b9c6 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -30,7 +30,7 @@ func TestGetCapsule(t *testing.T) { metaName := "test" createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") - updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+01:00") + updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:26+00:00") links := []interface{}{ map[string]interface{}{ "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", @@ -48,7 +48,6 @@ func TestGetCapsule(t *testing.T) { } containersUUIDs := []string{ "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", - "d1469e8d-bcbc-43fc-b163-8b9b6a740930", } addresses := map[string][]capsules.Address{ "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ @@ -63,9 +62,95 @@ func TestGetCapsule(t *testing.T) { } volumesInfo := map[string][]string{ "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{ - "4b725a92-2197-497b-b6b1-fb8caa4cb99b", + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", }, } + host := "test-host" + statusReason := "No reason" + + capsuleID := 1 + containerID := 1 + containerName := "test-demo-omicron-13" + containerUUID := "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" + containerImage := "test" + labels := map[string]string{ + "foo": "bar", + } + meta := map[string]string{ + "key1": "value1", + } + workDir := "/root" + disk := 0 + + containerIDBackend := "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb" + command := "testcmd" + ports := []int{ + 80, + } + securityGroups := []string{ + "default", + } + imagePullPolicy := "ifnotpresent" + runTime := "runc" + taskState := "Creating" + hostName := "test-hostname" + environment := map[string]string{ + "USER1": "test", + } + websocketToken := "2ba16a5a-552f-422f-b511-bd786102691f" + websocketUrl := "ws://10.10.10.10/" + containerStatusReason := "No reason" + statusDetail := "Just created" + imageDriver := "docker" + interactive := true + autoRemove := false + autoHeal := false + containerRestartPolicy := map[string]string{ + "MaximumRetryCount": "0", + "Name": "always", + } + + container1 := capsules.Container{ + Addresses: addresses, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + UUID: containerUUID, + ID: containerID, + UserID: userID, + ProjectID: projectID, + CPU: cpu, + Status: status, + Memory: memory, + Host: host, + ContainerID: containerIDBackend, + CapsuleID: capsuleID, + Name: containerName, + Image: containerImage, + Labels: labels, + Meta: meta, + WorkDir: workDir, + Disk: disk, + Command: command, + Ports: ports, + SecurityGroups: securityGroups, + ImagePullPolicy: imagePullPolicy, + Runtime: runTime, + TaskState: taskState, + HostName: hostName, + Environment: environment, + WebsocketToken: websocketToken, + WebsocketUrl: websocketUrl, + StatusReason: containerStatusReason, + StatusDetail: statusDetail, + ImageDriver: imageDriver, + AutoHeal: autoHeal, + AutoRemove: autoRemove, + Interactive: interactive, + RestartPolicy: containerRestartPolicy, + } + containers := []capsules.Container{ + container1, + } expectedCapsule := capsules.Capsule{ UUID: uuid, @@ -85,6 +170,9 @@ func TestGetCapsule(t *testing.T) { ContainersUUIDs: containersUUIDs, Addresses: addresses, VolumesInfo: volumesInfo, + StatusReason: statusReason, + Host: host, + Containers: containers, } th.AssertDeepEquals(t, &expectedCapsule, actualCapsule) From 770560368d2c193695dafd0dca8fc7b4d68903b1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 3 May 2018 03:08:31 +0000 Subject: [PATCH 0335/2296] Container v1: Move testing files to testing directory --- openstack/container/v1/capsules/fixtures.go | 166 ------------------ .../container/v1/capsules/testing/fixtures.go | 165 +++++++++++++++++ .../capsules/{ => testing}/template_test.go | 9 +- 3 files changed, 170 insertions(+), 170 deletions(-) delete mode 100644 openstack/container/v1/capsules/fixtures.go rename openstack/container/v1/capsules/{ => testing}/template_test.go (74%) diff --git a/openstack/container/v1/capsules/fixtures.go b/openstack/container/v1/capsules/fixtures.go deleted file mode 100644 index e547263b94..0000000000 --- a/openstack/container/v1/capsules/fixtures.go +++ /dev/null @@ -1,166 +0,0 @@ -package capsules - -// ValidJSONTemplate is a valid OpenStack Capsule template in JSON format -const ValidJSONTemplate = ` -{ - "capsuleVersion": "beta", - "kind": "capsule", - "metadata": { - "labels": { - "app": "web", - "app1": "web1" - }, - "name": "template" - }, - "restartPolicy": "Always", - "spec": { - "containers": [ - { - "command": [ - "/bin/bash" - ], - "env": { - "ENV1": "/usr/local/bin", - "ENV2": "/usr/bin" - }, - "image": "ubuntu", - "imagePullPolicy": "ifnotpresent", - "ports": [ - { - "containerPort": 80, - "hostPort": 80, - "name": "nginx-port", - "protocol": "TCP" - } - ], - "resources": { - "requests": { - "cpu": 1, - "memory": 1024 - } - }, - "workDir": "/root" - } - ] - } -} -` - -// ValidYAMLTemplate is a valid OpenStack Capsule template in YAML format -const ValidYAMLTemplate = ` -capsuleVersion: beta -kind: capsule -metadata: - name: template - labels: - app: web - app1: web1 -restartPolicy: Always -spec: - containers: - - image: ubuntu - command: - - "/bin/bash" - imagePullPolicy: ifnotpresent - workDir: /root - ports: - - name: nginx-port - containerPort: 80 - hostPort: 80 - protocol: TCP - resources: - requests: - cpu: 1 - memory: 1024 - env: - ENV1: /usr/local/bin - ENV2: /usr/bin -` - -// ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate -var ValidJSONTemplateParsed = map[string]interface{}{ - "capsuleVersion": "beta", - "kind": "capsule", - "restartPolicy": "Always", - "metadata": map[string]interface{}{ - "name": "template", - "labels": map[string]string{ - "app": "web", - "app1": "web1", - }, - }, - "spec": map[string]interface{}{ - "containers": []map[string]interface{}{ - map[string]interface{}{ - "image": "ubuntu", - "command": []interface{}{ - "/bin/bash", - }, - "imagePullPolicy": "ifnotpresent", - "workDir": "/root", - "ports": []interface{}{ - map[string]interface{}{ - "name": "nginx-port", - "containerPort": float64(80), - "hostPort": float64(80), - "protocol": "TCP", - }, - }, - "resources": map[string]interface{}{ - "requests": map[string]interface{}{ - "cpu": float64(1), - "memory": float64(1024), - }, - }, - "env": map[string]interface{}{ - "ENV1": "/usr/local/bin", - "ENV2": "/usr/bin", - }, - }, - }, - }, -} - -// ValidYAMLTemplateParsed is the expected parsed version of ValidYAMLTemplate -var ValidYAMLTemplateParsed = map[string]interface{}{ - "capsuleVersion": "beta", - "kind": "capsule", - "restartPolicy": "Always", - "metadata": map[string]interface{}{ - "name": "template", - "labels": map[string]string{ - "app": "web", - "app1": "web1", - }, - }, - "spec": map[interface{}]interface{}{ - "containers": []map[interface{}]interface{}{ - map[interface{}]interface{}{ - "image": "ubuntu", - "command": []interface{}{ - "/bin/bash", - }, - "imagePullPolicy": "ifnotpresent", - "workDir": "/root", - "ports": []interface{}{ - map[interface{}]interface{}{ - "name": "nginx-port", - "containerPort": 80, - "hostPort": 80, - "protocol": "TCP", - }, - }, - "resources": map[interface{}]interface{}{ - "requests": map[interface{}]interface{}{ - "cpu": 1, - "memory": 1024, - }, - }, - "env": map[interface{}]interface{}{ - "ENV1": "/usr/local/bin", - "ENV2": "/usr/bin", - }, - }, - }, - }, -} diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 06e55d9e5b..3c619acc0e 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -9,6 +9,171 @@ import ( fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) +// ValidJSONTemplate is a valid OpenStack Capsule template in JSON format +const ValidJSONTemplate = ` +{ + "capsuleVersion": "beta", + "kind": "capsule", + "metadata": { + "labels": { + "app": "web", + "app1": "web1" + }, + "name": "template" + }, + "restartPolicy": "Always", + "spec": { + "containers": [ + { + "command": [ + "/bin/bash" + ], + "env": { + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin" + }, + "image": "ubuntu", + "imagePullPolicy": "ifnotpresent", + "ports": [ + { + "containerPort": 80, + "hostPort": 80, + "name": "nginx-port", + "protocol": "TCP" + } + ], + "resources": { + "requests": { + "cpu": 1, + "memory": 1024 + } + }, + "workDir": "/root" + } + ] + } +} +` + +// ValidYAMLTemplate is a valid OpenStack Capsule template in YAML format +const ValidYAMLTemplate = ` +capsuleVersion: beta +kind: capsule +metadata: + name: template + labels: + app: web + app1: web1 +restartPolicy: Always +spec: + containers: + - image: ubuntu + command: + - "/bin/bash" + imagePullPolicy: ifnotpresent + workDir: /root + ports: + - name: nginx-port + containerPort: 80 + hostPort: 80 + protocol: TCP + resources: + requests: + cpu: 1 + memory: 1024 + env: + ENV1: /usr/local/bin + ENV2: /usr/bin +` + +// ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate +var ValidJSONTemplateParsed = map[string]interface{}{ + "capsuleVersion": "beta", + "kind": "capsule", + "restartPolicy": "Always", + "metadata": map[string]interface{}{ + "name": "template", + "labels": map[string]string{ + "app": "web", + "app1": "web1", + }, + }, + "spec": map[string]interface{}{ + "containers": []map[string]interface{}{ + map[string]interface{}{ + "image": "ubuntu", + "command": []interface{}{ + "/bin/bash", + }, + "imagePullPolicy": "ifnotpresent", + "workDir": "/root", + "ports": []interface{}{ + map[string]interface{}{ + "name": "nginx-port", + "containerPort": float64(80), + "hostPort": float64(80), + "protocol": "TCP", + }, + }, + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": float64(1), + "memory": float64(1024), + }, + }, + "env": map[string]interface{}{ + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin", + }, + }, + }, + }, +} + +// ValidYAMLTemplateParsed is the expected parsed version of ValidYAMLTemplate +var ValidYAMLTemplateParsed = map[string]interface{}{ + "capsuleVersion": "beta", + "kind": "capsule", + "restartPolicy": "Always", + "metadata": map[string]interface{}{ + "name": "template", + "labels": map[string]string{ + "app": "web", + "app1": "web1", + }, + }, + "spec": map[interface{}]interface{}{ + "containers": []map[interface{}]interface{}{ + map[interface{}]interface{}{ + "image": "ubuntu", + "command": []interface{}{ + "/bin/bash", + }, + "imagePullPolicy": "ifnotpresent", + "workDir": "/root", + "ports": []interface{}{ + map[interface{}]interface{}{ + "name": "nginx-port", + "containerPort": 80, + "hostPort": 80, + "protocol": "TCP", + }, + }, + "resources": map[interface{}]interface{}{ + "requests": map[interface{}]interface{}{ + "cpu": 1, + "memory": 1024, + }, + }, + "env": map[interface{}]interface{}{ + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin", + }, + }, + }, + }, +} + // HandleImageGetSuccessfully test setup func HandleCapsuleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/container/v1/capsules/template_test.go b/openstack/container/v1/capsules/testing/template_test.go similarity index 74% rename from openstack/container/v1/capsules/template_test.go rename to openstack/container/v1/capsules/testing/template_test.go index 289834b659..62d0de8f5d 100644 --- a/openstack/container/v1/capsules/template_test.go +++ b/openstack/container/v1/capsules/testing/template_test.go @@ -1,25 +1,26 @@ -package capsules +package testing import ( "testing" + "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" th "github.com/gophercloud/gophercloud/testhelper" ) func TestTemplateParsing(t *testing.T) { - templateJSON := new(Template) + templateJSON := new(capsules.Template) templateJSON.Bin = []byte(ValidJSONTemplate) err := templateJSON.Parse() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateJSON.Parsed) - templateYAML := new(Template) + templateYAML := new(capsules.Template) templateYAML.Bin = []byte(ValidYAMLTemplate) err = templateYAML.Parse() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ValidYAMLTemplateParsed, templateYAML.Parsed) - templateInvalid := new(Template) + templateInvalid := new(capsules.Template) templateInvalid.Bin = []byte("Keep Austin Weird") err = templateInvalid.Parse() if err == nil { From 0f499158033fcbeecf78acc1d7272db5acf18b95 Mon Sep 17 00:00:00 2001 From: "zhang.zujian" Date: Thu, 3 May 2018 17:22:44 +0800 Subject: [PATCH 0336/2296] fix fields of struct endpoints.ListOpts --- openstack/identity/v3/endpoints/requests.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 645632ddc9..0764a118e2 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -61,11 +61,8 @@ type ListOpts struct { // ServiceID is the ID of the service the Endpoint refers to. ServiceID string `q:"service_id"` - // Page is a result page to reference in the results. - Page int `q:"page"` - - // PerPage determines how many results per page are returned. - PerPage int `q:"per_page"` + // RegionID is the ID of the region the Endpoint refers to. + RegionID int `q:"region_id"` } // ToEndpointListParams builds a list request from the List options. From 131d678566f9b605f0c3ca8f43004288084495c5 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 3 May 2018 19:34:27 -0700 Subject: [PATCH 0337/2296] Clustering Policy Update Implementation (#952) * senlin-policies-update * Removed erroneous policy field --- .../openstack/clustering/v1/policies_test.go | 18 +++ openstack/clustering/v1/policies/doc.go | 11 ++ openstack/clustering/v1/policies/requests.go | 42 ++++++- openstack/clustering/v1/policies/results.go | 5 + .../v1/policies/testing/fixtures.go | 114 ++++++++++++++++++ .../v1/policies/testing/requests_test.go | 32 +++++ openstack/clustering/v1/policies/urls.go | 4 + 7 files changed, 224 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go index f87fb62ae0..e8cc0b3aa4 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -78,6 +78,24 @@ func TestPolicyCreateUpdateValidateDelete(t *testing.T) { t.Log("CreatePolicy updated at: " + createdPolicy.UpdatedAt.String()) } + updateOpts := policies.UpdateOpts{ + Name: testName + "-UPDATE", + } + + updatePolicy, err := policies.Update(client, createdPolicy.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatePolicy) + + if updatePolicy.CreatedAt.IsZero() { + t.Fatalf("UpdatePolicy's CreatedAt value should not be zero") + } + t.Log("UpdatePolicy created at: " + updatePolicy.CreatedAt.String()) + + if !updatePolicy.UpdatedAt.IsZero() { + t.Log("UpdatePolicy updated at: " + updatePolicy.UpdatedAt.String()) + } + validateOpts := policies.ValidateOpts{ Spec: createOpts.Spec, } diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index b458f679ab..6200a6fb21 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -48,6 +48,17 @@ Example to Create a policy panic(err) } +Example to Update a policy + + opts := policies.UpdateOpts{ + Name: "update_policy", + } + + updatePolicy, err := policies.Update(client, opts).Extract() + if err != nil { + panic(err) + } + Example to Validate a policy opts := policies.ValidateOpts{ diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index bf685f7de0..5401351f24 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -90,13 +90,51 @@ func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) return } -// Create makes a request against the API to delete a policy +// Delete makes a request against the API to delete a policy func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) - r.Header = result.Header + if r.Err == nil { + r.Header = result.Header + } + return +} + +// UpdateOptsBuilder builder +type UpdateOptsBuilder interface { + ToPolicyUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts params +type UpdateOpts struct { + Name string `json:"name,omitempty"` +} + +// ToPolicyUpdateMap formats a UpdateOpts into a body map. +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "policy") + if err != nil { + return nil, err + } + return b, nil +} + +// Update implements profile updated request. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToPolicyUpdateMap() + if err != nil { + r.Err = err + return r + } + var result *http.Response + result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + if r.Err == nil { + r.Header = result.Header + } return } diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index eb69720f92..d4e83cd9f4 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -148,6 +148,11 @@ type DeleteResult struct { gophercloud.HeaderResult } +// UpdateResult is the response of a Update operations. +type UpdateResult struct { + policyResult +} + type ValidateResult struct { policyResult } diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go index 3d8f095f59..1cad57d39d 100644 --- a/openstack/clustering/v1/policies/testing/fixtures.go +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -99,6 +99,66 @@ const PolicyCreateBody = ` } ` +const PolicyUpdateBody = ` +{ + "policy": { + "created_at": "2018-04-02T21:43:30.000000", + "data": {}, + "domain": null, + "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + "name": "delpol4", + "project": "018cd0909fb44cd5bc9b7a3cd664920e", + "spec": { + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": { + "hooks": { + "params": { + "queue": "zaqar_queue_name" + }, + "timeout": 180, + "type": "zaqar" + } + }, + "type": "senlin.policy.deletion", + "version": 1.1 + }, + "type": "senlin.policy.deletion-1.1", + "updated_at": null, + "user": "fe43e41739154b72818565e0d2580819" + } +} +` + +const PolicyBadUpdateBody = ` +{ + "policy": { + "created_at": "invalid", + "data": {}, + "domain": null, + "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + "name": "delpol4", + "project": "018cd0909fb44cd5bc9b7a3cd664920e", + "spec": { + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": { + "hooks": { + "params": { + "queue": "zaqar_queue_name" + }, + "timeout": 180, + "type": "zaqar" + } + }, + "type": "senlin.policy.deletion", + "version": 1.1 + }, + "type": "invalid", + "updated_at": null, + "user": "fe43e41739154b72818565e0d2580819" + } +} +` + const PolicyValidateBody = ` { "policy": { @@ -172,6 +232,9 @@ var ( // Policy ID to delete PolicyIDtoDelete = "1" + // Policy ID to update + PolicyIDtoUpdate = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" + ExpectedPolicies = [][]policies.Policy{ { { @@ -252,6 +315,33 @@ var ( UpdatedAt: ZeroTime, } + ExpectedUpdatePolicy = policies.Policy{ + CreatedAt: ExpectedPolicyCreatedAt1, + Data: map[string]interface{}{}, + Domain: "", + ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + Name: "delpol4", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "hooks": map[string]interface{}{ + "params": map[string]interface{}{ + "queue": "zaqar_queue_name", + }, + "timeout": float64(180), + "type": "zaqar", + }, + }, + Type: "senlin.policy.deletion", + Version: "1.1", + }, + Type: "senlin.policy.deletion-1.1", + User: "fe43e41739154b72818565e0d2580819", + UpdatedAt: ZeroTime, + } + ExpectedValidatePolicy = policies.Policy{ CreatedAt: ExpectedPolicyCreatedAt1, Data: map[string]interface{}{}, @@ -325,6 +415,30 @@ func HandlePolicyDelete(t *testing.T) { }) } +func HandlePolicyUpdate(t *testing.T) { + th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoUpdate, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, PolicyUpdateBody) + }) +} + +func HandleBadPolicyUpdate(t *testing.T) { + th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoUpdate, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, PolicyBadUpdateBody) + }) +} + func HandlePolicyValidate(t *testing.T) { th.Mux.HandleFunc("/v1/policies/validate", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go index 6f954be40c..7a6fe726fe 100644 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -71,6 +71,38 @@ func TestDeletePolicy(t *testing.T) { th.AssertEquals(t, PolicyDeleteRequestID, actual.RequestID) } +func TestUpdatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandlePolicyUpdate(t) + + expected := ExpectedUpdatePolicy + + opts := policies.UpdateOpts{ + Name: ExpectedUpdatePolicy.Name, + } + + actual, err := policies.Update(fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} + +func TestBadUpdatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleBadPolicyUpdate(t) + + opts := policies.UpdateOpts{ + Name: ExpectedUpdatePolicy.Name, + } + + _, err := policies.Update(fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() + th.AssertEquals(t, false, err == nil) +} + func TestValidatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/clustering/v1/policies/urls.go b/openstack/clustering/v1/policies/urls.go index 468c003174..b8a1ee1c56 100644 --- a/openstack/clustering/v1/policies/urls.go +++ b/openstack/clustering/v1/policies/urls.go @@ -19,6 +19,10 @@ func policyDeleteURL(client *gophercloud.ServiceClient, policyID string) string return client.ServiceURL(apiVersion, apiName, policyID) } +func updateURL(client *gophercloud.ServiceClient, policyID string) string { + return client.ServiceURL(apiVersion, apiName, policyID) +} + func validateURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName, "validate") } From 259c75ed5617169499d70b34f04c9acc61fce794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Fri, 4 May 2018 11:12:01 +0800 Subject: [PATCH 0338/2296] Identity V3: List Policies (#973) * Identity V3: list policies * add acceptance test --- .../openstack/identity/v3/policies_test.go | 29 +++++ openstack/identity/v3/policies/doc.go | 25 ++++ openstack/identity/v3/policies/requests.go | 41 +++++++ openstack/identity/v3/policies/results.go | 93 +++++++++++++++ openstack/identity/v3/policies/testing/doc.go | 2 + .../identity/v3/policies/testing/fixtures.go | 111 ++++++++++++++++++ .../v3/policies/testing/requests_test.go | 57 +++++++++ openstack/identity/v3/policies/urls.go | 9 ++ 8 files changed, 367 insertions(+) create mode 100644 acceptance/openstack/identity/v3/policies_test.go create mode 100644 openstack/identity/v3/policies/doc.go create mode 100644 openstack/identity/v3/policies/requests.go create mode 100644 openstack/identity/v3/policies/results.go create mode 100644 openstack/identity/v3/policies/testing/doc.go create mode 100644 openstack/identity/v3/policies/testing/fixtures.go create mode 100644 openstack/identity/v3/policies/testing/requests_test.go create mode 100644 openstack/identity/v3/policies/urls.go diff --git a/acceptance/openstack/identity/v3/policies_test.go b/acceptance/openstack/identity/v3/policies_test.go new file mode 100644 index 0000000000..1f0b642eea --- /dev/null +++ b/acceptance/openstack/identity/v3/policies_test.go @@ -0,0 +1,29 @@ +// +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/policies" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestPoliciesList(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + allPages, err := policies.List(client, policies.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err := policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + + for _, policy := range allPolicies { + tools.PrintResource(t, policy) + } +} diff --git a/openstack/identity/v3/policies/doc.go b/openstack/identity/v3/policies/doc.go new file mode 100644 index 0000000000..16ea3e0ff6 --- /dev/null +++ b/openstack/identity/v3/policies/doc.go @@ -0,0 +1,25 @@ +/* +Package policies provides information and interaction with the policies API +resource for the OpenStack Identity service. + +Example to List Policies + + listOpts := policies.ListOpts{ + Type: "application/json", + } + + allPages, err := policies.List(identityClient, listOpts).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) + } +*/ +package policies diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go new file mode 100644 index 0000000000..4b49d3ea6f --- /dev/null +++ b/openstack/identity/v3/policies/requests.go @@ -0,0 +1,41 @@ +package policies + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToPolicyListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // Type filters the response by MIME media type + // of the serialized policy blob. + Type string `q:"type"` +} + +// ToPolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the roles to which the current token has access. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToPolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return PolicyPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go new file mode 100644 index 0000000000..6528b12247 --- /dev/null +++ b/openstack/identity/v3/policies/results.go @@ -0,0 +1,93 @@ +package policies + +import ( + "encoding/json" + + "github.com/gophercloud/gophercloud/internal" + "github.com/gophercloud/gophercloud/pagination" +) + +// Policy is an arbitrarily serialized policy engine rule +// set to be consumed by a remote service. +type Policy struct { + // ID is the unique ID of the policy. + ID string `json:"id"` + + // Blob is the policy rule as a serialized blob. + Blob string `json:"blob"` + + // Type is the MIME media type of the serialized policy blob. + Type string `json:"type"` + + // Links contains referencing links to the policy. + Links map[string]interface{} `json:"links"` + + // Extra is a collection of miscellaneous key/values. + Extra map[string]interface{} `json:"-"` +} + +func (r *Policy) UnmarshalJSON(b []byte) error { + type tmp Policy + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Policy(s.tmp) + + // Collect other fields and bundle them into Extra + // but only if a field titled "extra" wasn't sent. + if s.Extra != nil { + r.Extra = s.Extra + } else { + var result interface{} + err := json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = internal.RemainingKeys(Policy{}, resultMap) + } + } + + return err +} + +// PolicyPage is a single page of Policy results. +type PolicyPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Policies contains any results. +func (r PolicyPage) IsEmpty() (bool, error) { + policies, err := ExtractPolicies(r) + return len(policies) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r PolicyPage) 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 +} + +// ExtractPolicies returns a slice of Policies +// contained in a single page of results. +func ExtractPolicies(r pagination.Page) ([]Policy, error) { + var s struct { + Policies []Policy `json:"policies"` + } + err := (r.(PolicyPage)).ExtractInto(&s) + return s.Policies, err +} diff --git a/openstack/identity/v3/policies/testing/doc.go b/openstack/identity/v3/policies/testing/doc.go new file mode 100644 index 0000000000..80554d4418 --- /dev/null +++ b/openstack/identity/v3/policies/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing contains policies unit tests +package testing diff --git a/openstack/identity/v3/policies/testing/fixtures.go b/openstack/identity/v3/policies/testing/fixtures.go new file mode 100644 index 0000000000..292905c037 --- /dev/null +++ b/openstack/identity/v3/policies/testing/fixtures.go @@ -0,0 +1,111 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of Policy results. +const ListOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/policies" + }, + "policies": [ + { + "type": "text/plain", + "id": "2844b2a08be147a08ef58317d6471f1f", + "links": { + "self": "http://example.com/identity/v3/policies/2844b2a08be147a08ef58317d6471f1f" + }, + "blob": "'foo_user': 'role:compute-user'" + }, + { + "type": "application/json", + "id": "b49884da9d31494ea02aff38d4b4e701", + "links": { + "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701" + }, + "blob": "{'bar_user': 'role:network-user'}", + "description": "policy for bar_user" + } + ] +} +` + +// ListWithFilterOutput provides a single page of filtered Policy results. +const ListWithFilterOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/policies" + }, + "policies": [ + { + "type": "application/json", + "id": "b49884da9d31494ea02aff38d4b4e701", + "links": { + "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701" + }, + "blob": "{'bar_user': 'role:network-user'}", + "description": "policy for bar_user" + } + ] +} +` + +// FirstPolicy is the first policy in the List request. +var FirstPolicy = policies.Policy{ + ID: "2844b2a08be147a08ef58317d6471f1f", + Blob: "'foo_user': 'role:compute-user'", + Type: "text/plain", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/policies/2844b2a08be147a08ef58317d6471f1f", + }, + Extra: map[string]interface{}{}, +} + +// SecondPolicy is the second policy in the List request. +var SecondPolicy = policies.Policy{ + ID: "b49884da9d31494ea02aff38d4b4e701", + Blob: "{'bar_user': 'role:network-user'}", + Type: "application/json", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701", + }, + Extra: map[string]interface{}{ + "description": "policy for bar_user", + }, +} + +// ExpectedPoliciesSlice is the slice of policies expected to be returned from ListOutput. +var ExpectedPoliciesSlice = []policies.Policy{FirstPolicy, SecondPolicy} + +// HandleListPoliciesSuccessfully creates an HTTP handler at `/policies` on the +// test handler mux that responds with a list of two policies. +func HandleListPoliciesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/policies", 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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + switch r.URL.Query().Get("type") { + case "": + fmt.Fprintf(w, ListOutput) + case "application/json": + fmt.Fprintf(w, ListWithFilterOutput) + default: + w.WriteHeader(http.StatusBadRequest) + } + }) +} diff --git a/openstack/identity/v3/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go new file mode 100644 index 0000000000..e67dd6c130 --- /dev/null +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -0,0 +1,57 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListPolicies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListPoliciesSuccessfully(t) + + count := 0 + err := policies.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := policies.ExtractPolicies(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedPoliciesSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListPoliciesAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListPoliciesSuccessfully(t) + + allPages, err := policies.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedPoliciesSlice, actual) +} + +func TestListPoliciesWithFilter(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListPoliciesSuccessfully(t) + + listOpts := policies.ListOpts{ + Type: "application/json", + } + allPages, err := policies.List(client.ServiceClient(), listOpts).AllPages() + th.AssertNoErr(t, err) + actual, err := policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, []policies.Policy{SecondPolicy}, actual) +} diff --git a/openstack/identity/v3/policies/urls.go b/openstack/identity/v3/policies/urls.go new file mode 100644 index 0000000000..93a183bed4 --- /dev/null +++ b/openstack/identity/v3/policies/urls.go @@ -0,0 +1,9 @@ +package policies + +import "github.com/gophercloud/gophercloud" + +const policyPath = "policies" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(policyPath) +} From e48b7a73528715f062252440713953c2b1db6a9b Mon Sep 17 00:00:00 2001 From: Yongfeng Du Date: Thu, 3 May 2018 17:07:02 +0800 Subject: [PATCH 0339/2296] Move CI job definition to gophercloud repo Add the jobs definition to .zuul.yaml, and the ansible playbooks. --- .zuul.yaml | 55 +++++++++++++++++ .../gophercloud-acceptance-test/run.yaml | 60 +++++++++++++++++++ .zuul/playbooks/gophercloud-unittest/run.yaml | 21 +++++++ 3 files changed, 136 insertions(+) create mode 100644 .zuul/playbooks/gophercloud-acceptance-test/run.yaml create mode 100644 .zuul/playbooks/gophercloud-unittest/run.yaml diff --git a/.zuul.yaml b/.zuul.yaml index 436fbb6e94..3d4798fe6f 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,3 +1,58 @@ +- 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 + parent: golang-test + description: | + Run gophercloud acceptance test on master branch + run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml + +- job: + name: gophercloud-acceptance-test-queens + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on queens branch + vars: + os_branch: 'stable/queens' + +- job: + name: gophercloud-acceptance-test-pike + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on pike branch + vars: + os_branch: 'stable/pike' +- job: + name: gophercloud-acceptance-test-ocata + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on ocata branch + vars: + os_branch: 'stable/ocata' + +- job: + name: gophercloud-acceptance-test-newton + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on newton branch + vars: + os_branch: 'stable/newton' + +- job: + name: gophercloud-acceptance-test-mitaka + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on mitaka branch + vars: + os_branch: 'stable/mitaka' + nodeset: ubuntu-trusty + - project: name: gophercloud/gophercloud check: diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml new file mode 100644 index 0000000000..f4e20f641e --- /dev/null +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -0,0 +1,60 @@ +- hosts: all + become: yes + roles: + - clone-devstack-gate-to-workspace + - role: create-devstack-local-conf + enable_services: + - 'manila' + - 'designate' + - 'zun' + - install-devstack + tasks: + - name: Run acceptance tests with gophercloud + shell: + cmd: | + set -e + set -o pipefail + set -x + + # Prep the testing environment by creating the required testing resources and environment variables + pushd /opt/stack/new/devstack + 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 + _NETWORK_ID=$(openstack network show private -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 ' ') + echo export OS_IMAGE_NAME="$_IMAGE_NAME" >> openrc + echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc + echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc + echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc + 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_SHARE_NETWORK_ID=foobar >> openrc + echo export OS_DOMAIN_ID=default >> openrc + source openrc admin admin + popd + + go get ./... || true + # Temporally enable all tests with openlab repo and only enable part tests with official repo + if [[ '{{ zuul.project.src_dir }}' =~ "theopenlab" ]];then + go test -v -tags "fixtures acceptance" ./acceptance/openstack/... 2>&1 | tee $TEST_RESULTS_TXT + else + { + # Only enable these successful test cases + go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v3/ + go test -v -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ + go test -v -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ + go test -v -tags "fixtures acceptance" -run "SecGroup|Flavor" ./acceptance/openstack/compute/v2/ + # To enable more after the fix of https://github.com/gophercloud/gophercloud/issues/608 + # go test -v -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ + # go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v2/ + # go test -v -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ + } 2>&1 | tee $TEST_RESULTS_TXT + fi + executable: /bin/bash + chdir: '{{ zuul.project.src_dir }}' + environment: '{{ golang_env }}' diff --git a/.zuul/playbooks/gophercloud-unittest/run.yaml b/.zuul/playbooks/gophercloud-unittest/run.yaml new file mode 100644 index 0000000000..cd85cac1ce --- /dev/null +++ b/.zuul/playbooks/gophercloud-unittest/run.yaml @@ -0,0 +1,21 @@ +- hosts: all + tasks: + - name: Run unit tests with gophercloud + shell: + cmd: | + set -e + set -o pipefail + set -x + + if [[ ! -d $GOPATH/src/github.com/gophercloud/gophercloud/ && -d $GOPATH/src/github.com/theopenlab/gophercloud ]]; then + echo "Warning: this is a temporary workaround because this job is not triggered from official git repo." + mkdir -p $GOPATH/src/github.com/gophercloud/ + cp -r $GOPATH/src/github.com/theopenlab/gophercloud $GOPATH/src/github.com/gophercloud/ + cd $GOPATH/src/github.com/gophercloud/gophercloud + fi + + go get ./... || true + ./script/unittest -v 2>&1 | tee $TEST_RESULTS_TXT + executable: /bin/bash + chdir: '{{ zuul.project.src_dir }}' + environment: '{{ golang_env }}' From 357775a64739267580eadbaf8617b5c9183c9346 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Thu, 3 May 2018 15:07:13 -0700 Subject: [PATCH 0340/2296] Add GetMessages for messages --- .../openstack/messaging/v2/message_test.go | 48 +++++++++++++++++++ openstack/messaging/v2/messages/doc.go | 21 ++++++-- openstack/messaging/v2/messages/requests.go | 34 +++++++++++++ openstack/messaging/v2/messages/results.go | 14 ++++++ .../messaging/v2/messages/testing/fixtures.go | 46 ++++++++++++++++++ .../v2/messages/testing/requests_test.go | 14 ++++++ openstack/messaging/v2/messages/urls.go | 4 ++ 7 files changed, 177 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/messaging/v2/message_test.go b/acceptance/openstack/messaging/v2/message_test.go index 162b76edd6..6b956eba6c 100644 --- a/acceptance/openstack/messaging/v2/message_test.go +++ b/acceptance/openstack/messaging/v2/message_test.go @@ -61,6 +61,54 @@ func TestCreateMessages(t *testing.T) { CreateMessage(t, client, createdQueueName) } +func TestGetMessages(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-718613007343" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + CreateMessage(t, client, createdQueueName) + CreateMessage(t, client, createdQueueName) + + // Use a different client/clientID in order to see messages on the Queue + clientID = "3381af92-2b9e-11e3-b191-71861300734d" + client, err = clients.NewMessagingV2Client(clientID) + + listOpts := messages.ListOpts{} + + var messageIDs []string + + pager := messages.List(client, createdQueueName, listOpts) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + allMessages, err := messages.ExtractMessages(page) + if err != nil { + t.Fatalf("Unable to extract messages: %v", err) + } + + for _, message := range allMessages { + messageIDs = append(messageIDs, message.ID) + } + + return true, nil + }) + + getMessageOpts := messages.GetMessagesOpts{ + IDs: messageIDs, + } + t.Logf("Attempting to get messages from queue %s with ids: %v", createdQueueName, messageIDs) + messagesList, err := messages.GetMessages(client, createdQueueName, getMessageOpts).Extract() + if err != nil { + t.Fatalf("Unable to get messages from queue: %s", createdQueueName) + } + + tools.PrintResource(t, messagesList) +} + func TestDeleteMessagesIDs(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-718613007343" diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go index c16b2f4c5a..fc4f48c7d2 100644 --- a/openstack/messaging/v2/messages/doc.go +++ b/openstack/messaging/v2/messages/doc.go @@ -27,6 +27,8 @@ Example to List Messages Example to Create Messages + queueName = "my_queue" + createOpts := messages.CreateOpts{ Messages: []messages.Messages{ { @@ -47,13 +49,24 @@ Example to Create Messages }, } - queueName = "my_queue" - resources, err := messages.Create(client, queueName, createOpts).Extract() if err != nil { panic(err) } +Example to Get a set of Messages + + queueName := "my_queue" + + getMessageOpts := messages.GetMessagesOpts{ + IDs: "123456", + } + + messagesList, err := messages.GetMessages(client, createdQueueName, getMessageOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a set of Messages queueName := "my_queue" @@ -71,11 +84,11 @@ Example to Pop a set of Messages queueName := "my_queue" - deleteMessagesOpts := messages.DeleteMessageOpts{ + popMessagesOpts := messages.PopMessagesOpts{ Pop: 5, } - resources, err := messages.PopMessages(client, queueName, deleteMessagesOpts).Extract() + resources, err := messages.PopMessages(client, queueName, popMessagesOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index aadc845d7b..0d0093e00a 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -174,3 +174,37 @@ func PopMessages(client *gophercloud.ServiceClient, queueName string, opts PopMe r.Err = err return } + +// GetMessagesOptsBuilder allows extensions to add additional parameters to the +// GetMessages request. +type GetMessagesOptsBuilder interface { + ToGetMessagesListQuery() (string, error) +} + +// GetMessagesOpts params to be used with GetMessages. +type GetMessagesOpts struct { + IDs []string `q:"ids,omitempty"` +} + +// ToGetMessagesListQuery formats a GetMessagesOpts structure into a query string. +func (opts GetMessagesOpts) ToGetMessagesListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// GetMessages requests details on a multiple messages, by IDs. +func GetMessages(client *gophercloud.ServiceClient, queueName string, opts GetMessagesOptsBuilder) (r GetMessagesResult) { + url := getURL(client, queueName) + if opts != nil { + query, err := opts.ToGetMessagesListQuery() + if err != nil { + r.Err = err + return + } + url += query + } + _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go index 9ce5103d63..c8ff42c15b 100644 --- a/openstack/messaging/v2/messages/results.go +++ b/openstack/messaging/v2/messages/results.go @@ -31,6 +31,11 @@ type PopResult struct { gophercloud.Result } +// GetMessagesResult is the response of a GetMessages operations. +type GetMessagesResult struct { + commonResult +} + // Message represents a message on a queue. type Message struct { Body map[string]interface{} `json:"body"` @@ -72,6 +77,15 @@ func (r PopResult) Extract() ([]PopMessage, error) { return s.PopMessages, err } +// Extract interprets any GetMessagesResult as a list of Message. +func (r GetMessagesResult) Extract() ([]Message, error) { + var s struct { + Messages []Message `json:"messages"` + } + err := r.ExtractInto(&s) + return s.Messages, err +} + // ExtractMessage extracts message into a list of Message. func ExtractMessages(r pagination.Page) ([]Message, error) { var s struct { diff --git a/openstack/messaging/v2/messages/testing/fixtures.go b/openstack/messaging/v2/messages/testing/fixtures.go index d85a7d922d..b4c5df6a14 100644 --- a/openstack/messaging/v2/messages/testing/fixtures.go +++ b/openstack/messaging/v2/messages/testing/fixtures.go @@ -95,6 +95,24 @@ const ListMessagesResponse2 = ` }` +// GetMessagesResponse is a sample response to GetMessages. +const GetMessagesResponse = ` +{ + "messages": [ + { + "body": { + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720" + }, + "age": 443, + "href": "/v2/queues/beijing/messages/578f0055508f153f256f717f", + "id": "578f0055508f153f256f717f", + "ttl": 3600 + } + ] +}` + // PopMessageResponse is a sample reponse to pop messages const PopMessageResponse = ` { @@ -153,6 +171,22 @@ var SecondMessage = messages.Message{ // ExpectedMessagesSlice is the expected result in a List. var ExpectedMessagesSlice = [][]messages.Message{{FirstMessage}, {SecondMessage}} +// ExpectedMessagesSet is the expected result in GetMessages +var ExpectedMessagesSet = []messages.Message{ + { + Body: map[string]interface{}{ + "total_bytes": "99614720", + "current_bytes": "0", + "event": "BackupProgress", + }, + Age: 443, + Href: "/v2/queues/beijing/messages/578f0055508f153f256f717f", + ID: "578f0055508f153f256f717f", + TTL: 3600, + Checksum: "", + }, +} + // ExpectedPopMessage is the expected result of a Pop. var ExpectedPopMessage = []messages.PopMessage{{ Body: map[string]interface{}{ @@ -202,6 +236,18 @@ func HandleListSuccessfully(t *testing.T) { }) } +// HandleGetMessagesSuccessfully configures the test server to respond to a GetMessages request. +func HandleGetMessagesSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), + 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, GetMessagesResponse) + }) +} + // HandleDeleteMessagesSuccessfully configures the test server to respond to a Delete request. func HandleDeleteMessagesSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go index 300ef23be5..9162e0c4cb 100644 --- a/openstack/messaging/v2/messages/testing/requests_test.go +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -61,6 +61,20 @@ func TestCreate(t *testing.T) { th.CheckDeepEquals(t, ExpectedResources, actual) } +func TestGetMessages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetMessagesSuccessfully(t) + + getMessagesOpts := messages.GetMessagesOpts{ + IDs: []string{"9988776655"}, + } + + actual, err := messages.GetMessages(fake.ServiceClient(), QueueName, getMessagesOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedMessagesSet, actual) +} + func TestDeleteMessages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/messaging/v2/messages/urls.go b/openstack/messaging/v2/messages/urls.go index 3d48c54cf1..49a8e25468 100644 --- a/openstack/messaging/v2/messages/urls.go +++ b/openstack/messaging/v2/messages/urls.go @@ -17,6 +17,10 @@ func listURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") } +func getURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") +} + // Builds next page full url based on current url. func nextPageURL(currentURL string, next string) (string, error) { base, err := url.Parse(currentURL) From 5278af2b10833dd0fc31f3ef347bdb7d7090d346 Mon Sep 17 00:00:00 2001 From: "zhang.zujian" Date: Fri, 4 May 2018 15:07:15 +0800 Subject: [PATCH 0341/2296] ignore .vscode --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index df9048a010..dd91ed2055 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ **/*.swp .idea +.vscode From 8f7b2e3cb14f7e5851f894df7fa20a55beee4acd Mon Sep 17 00:00:00 2001 From: Kevin Zhao Date: Sat, 5 May 2018 00:47:06 +1000 Subject: [PATCH 0342/2296] Add Zun Capsule List Operation (#955) * Add Capsule List Operation and test Change-Id: Ia8d871e49cd04ce3b272d61ae6e38abd1796f12d Signed-off-by: Kevin Zhao * Fix typo error Change-Id: I426e2324a63dfe3269016f5749d88e8658fc8f14 Signed-off-by: Kevin Zhao * add pagination import Change-Id: I71bf2a36d9d5b3acb35f8e596bd48e68eabff2ab Signed-off-by: Kevin Zhao * Modify the acceptance test Change-Id: Ie4b92f8ff17bad9d2283d815ab0dbfff0c2fda0e Signed-off-by: Kevin Zhao * remove unused fmt Change-Id: I6c959f908ffd29355a7da66eef86d0b564062137 Signed-off-by: Kevin Zhao --- .../openstack/container/v1/capsules_test.go | 37 ++++--- openstack/container/v1/capsules/requests.go | 40 ++++++++ openstack/container/v1/capsules/results.go | 36 +++++++ .../container/v1/capsules/testing/fixtures.go | 64 +++++++++++- .../v1/capsules/testing/requests_test.go | 99 +++++++++++++++++++ openstack/container/v1/capsules/urls.go | 6 ++ 6 files changed, 261 insertions(+), 21 deletions(-) diff --git a/acceptance/openstack/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index 3e93edce29..63c1078c6b 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -5,29 +5,11 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) -func TestCapsuleGet(t *testing.T) { - client, err := clients.NewContainerV1Client() - if err != nil { - t.Fatalf("Unable to create an container v1 client: %v", err) - } - th.AssertNoErr(t, err) - capsuleUUID := "e6c913bb-b4e4-409d-8b71-3e029f196458" - if capsuleUUID == "" { - t.Fatalf("In order to retrieve a capsule, the CapsuleUUID must be set") - } - capsule, err := capsules.Get(client, capsuleUUID).Extract() - // Get a capsule - - th.AssertNoErr(t, err) - th.AssertEquals(t, capsule.Status, "Running") - th.AssertEquals(t, capsule.MetaName, "template") - th.AssertEquals(t, capsule.CPU, float64(2.0)) -} - -func TestCapsuleCreate(t *testing.T) { +func TestCapsule(t *testing.T) { client, err := clients.NewContainerV1Client() if err != nil { t.Fatalf("Unable to create an container v1 client: %v", err) @@ -81,4 +63,19 @@ func TestCapsuleCreate(t *testing.T) { } err = capsules.Create(client, createOpts).ExtractErr() th.AssertNoErr(t, err) + pager := capsules.List(client, nil) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + CapsuleList, err := capsules.ExtractCapsules(page) + th.AssertNoErr(t, err) + + for _, m := range CapsuleList { + capsuleUUID := m.UUID + capsule, err := capsules.Get(client, capsuleUUID).Extract() + + th.AssertNoErr(t, err) + th.AssertEquals(t, capsule.MetaName, "template") + } + return true, nil + }) + th.AssertNoErr(t, err) } diff --git a/openstack/container/v1/capsules/requests.go b/openstack/container/v1/capsules/requests.go index c73721c3d2..40d0771765 100644 --- a/openstack/container/v1/capsules/requests.go +++ b/openstack/container/v1/capsules/requests.go @@ -2,6 +2,7 @@ package capsules import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder is the interface options structs have to satisfy in order @@ -12,6 +13,12 @@ type CreateOptsBuilder interface { ToCapsuleCreateMap() (map[string]interface{}, error) } +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToCapsuleListQuery() (string, error) +} + // Get requests details on a single capsule, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ @@ -54,3 +61,36 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) return } + +// 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 capsule attributes you want to see returned. Marker and Limit are used +// for pagination. +type ListOpts struct { + Marker string `q:"marker"` + Limit int `q:"limit"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + AllProjects bool `q:"all_projects"` +} + +// ToCapsuleListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToCapsuleListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list servers accessible to you. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToCapsuleListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return CapsulePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index f494d825cd..58f87b617e 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { @@ -29,6 +30,10 @@ type CreateResult struct { gophercloud.ErrResult } +type CapsulePage struct { + pagination.LinkedPageBase +} + // Represents a Container Orchestration Engine Bay, i.e. a cluster type Capsule struct { // UUID for the capsule @@ -215,6 +220,37 @@ type Address struct { SubnetID string `json:"subnet_id"` } +// NextPageURL is invoked when a paginated collection of capsules 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 CapsulePage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Next, nil +} + +// IsEmpty checks whether a CapsulePage struct is empty. +func (r CapsulePage) IsEmpty() (bool, error) { + is, err := ExtractCapsules(r) + return len(is) == 0, err +} + +// ExtractCapsules accepts a Page struct, specifically a CapsulePage struct, +// and extracts the elements into a slice of Capsule structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractCapsules(r pagination.Page) ([]Capsule, error) { + var s struct { + Capsules []Capsule `json:"capsules"` + } + err := (r.(CapsulePage)).ExtractInto(&s) + return s.Capsules, err +} + func (r *Capsule) UnmarshalJSON(b []byte) error { type tmp Capsule var s struct { diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 3c619acc0e..3f3b1c7e97 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -174,7 +174,7 @@ var ValidYAMLTemplateParsed = map[string]interface{}{ }, } -// HandleImageGetSuccessfully test setup +// HandleCapsuleGetSuccessfully test setup func HandleCapsuleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -282,6 +282,56 @@ func HandleCapsuleGetSuccessfully(t *testing.T) { }) } +const CapsuleListBody = ` +{ + "capsules": [ + { + "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", + "status": "Running", + "id": 1, + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "cpu": 1, + "memory": "1024M", + "meta_name": "test", + "meta_labels": {"web": "app"}, + "created_at": "2018-01-12 09:37:25+00:00", + "updated_at": "2018-01-12 09:37:25+01:00", + "links": [ + { + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self" + }, + { + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark" + } + ], + "capsule_version": "beta", + "restart_policy": "always", + "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "d1469e8d-bcbc-43fc-b163-8b9b6a740930"], + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "volumes_info": { + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ + "4b725a92-2197-497b-b6b1-fb8caa4cb99b" + ] + }, + "host": "test-host", + "status_reason": "No reason" + } + ] +}` + // HandleCapsuleCreateSuccessfully creates an HTTP handler at `/capsules` on the test handler mux // that responds with a `Create` response. func HandleCapsuleCreateSuccessfully(t *testing.T) { @@ -293,3 +343,15 @@ func HandleCapsuleCreateSuccessfully(t *testing.T) { fmt.Fprintf(w, `{}`) }) } + +// HandleCapsuleListSuccessfully test setup +func HandleCapsuleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/capsules/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, CapsuleListBody) + }) +} diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index d92bb7b9c6..8f77a55ae1 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -231,3 +232,101 @@ func TestCreateCapsule(t *testing.T) { err := capsules.Create(fakeclient.ServiceClient(), createOpts).ExtractErr() th.AssertNoErr(t, err) } + +func TestListCapsule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCapsuleListSuccessfully(t) + + count := 0 + results := capsules.List(fakeclient.ServiceClient(), nil) + err := results.EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := capsules.ExtractCapsules(page) + if err != nil { + t.Errorf("Failed to extract capsules: %v", err) + return false, err + } + uuid := "cc654059-1a77-47a3-bfcf-715bde5aad9e" + status := "Running" + id := 1 + userID := "d33b18c384574fd2a3299447aac285f0" + projectID := "6b8ffef2a0ac42ee87887b9cc98bdf68" + cpu := float64(1) + memory := "1024M" + metaName := "test" + + createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") + updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+01:00") + links := []interface{}{ + map[string]interface{}{ + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark", + }, + } + capsuleVersion := "beta" + restartPolicy := "always" + metaLabels := map[string]string{ + "web": "app", + } + containersUUIDs := []string{ + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", + "d1469e8d-bcbc-43fc-b163-8b9b6a740930", + } + addresses := map[string][]capsules.Address{ + "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ + { + PreserveOnDelete: false, + Addr: "172.24.4.11", + Port: "8439060f-381a-4386-a518-33d5a4058636", + Version: float64(4), + SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", + }, + }, + } + volumesInfo := map[string][]string{ + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{ + "4b725a92-2197-497b-b6b1-fb8caa4cb99b", + }, + } + host := "test-host" + statusReason := "No reason" + + expected := []capsules.Capsule{ + { + UUID: uuid, + ID: id, + UserID: userID, + ProjectID: projectID, + CPU: cpu, + Status: status, + Memory: memory, + MetaName: metaName, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + Links: links, + CapsuleVersion: capsuleVersion, + RestartPolicy: restartPolicy, + MetaLabels: metaLabels, + ContainersUUIDs: containersUUIDs, + Addresses: addresses, + VolumesInfo: volumesInfo, + StatusReason: statusReason, + Host: host, + }, + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/container/v1/capsules/urls.go b/openstack/container/v1/capsules/urls.go index b276c4509f..fd682efbc4 100644 --- a/openstack/container/v1/capsules/urls.go +++ b/openstack/container/v1/capsules/urls.go @@ -9,3 +9,9 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("capsules") } + +// `listURL` is a pure function. `listURL(c)` is a URL for which a GET +// request will respond with a list of capsules in the service `c`. +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("capsules") +} From 6553692288d9df078a7f85338eeac1becacdead2 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Fri, 4 May 2018 11:41:39 -0700 Subject: [PATCH 0343/2296] Add get to messaging --- .../openstack/messaging/v2/message_test.go | 45 +++++++++++++++++++ openstack/messaging/v2/messages/doc.go | 10 +++++ openstack/messaging/v2/messages/requests.go | 8 ++++ openstack/messaging/v2/messages/results.go | 14 +++++- .../messaging/v2/messages/testing/fixtures.go | 30 +++++++++++++ .../v2/messages/testing/requests_test.go | 10 +++++ openstack/messaging/v2/messages/urls.go | 4 ++ 7 files changed, 120 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/messaging/v2/message_test.go b/acceptance/openstack/messaging/v2/message_test.go index 6b956eba6c..14984a568f 100644 --- a/acceptance/openstack/messaging/v2/message_test.go +++ b/acceptance/openstack/messaging/v2/message_test.go @@ -109,6 +109,51 @@ func TestGetMessages(t *testing.T) { tools.PrintResource(t, messagesList) } +func TestGetMessage(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-718613007343" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + CreateMessage(t, client, createdQueueName) + + // Use a different client/clientID in order to see messages on the Queue + clientID = "3381af92-2b9e-11e3-b191-71861300734d" + client, err = clients.NewMessagingV2Client(clientID) + + listOpts := messages.ListOpts{} + + var messageIDs []string + + pager := messages.List(client, createdQueueName, listOpts) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + allMessages, err := messages.ExtractMessages(page) + if err != nil { + t.Fatalf("Unable to extract messages: %v", err) + } + + for _, message := range allMessages { + messageIDs = append(messageIDs, message.ID) + } + + return true, nil + }) + + for _, messageID := range messageIDs { + t.Logf("Attempting to get message from queue %s: %s", createdQueueName, messageID) + message, getErr := messages.Get(client, createdQueueName, messageID).Extract() + if getErr != nil { + t.Fatalf("Unable to get message from queue %s: %s", createdQueueName, messageID) + } + tools.PrintResource(t, message) + } +} + func TestDeleteMessagesIDs(t *testing.T) { clientID := "3381af92-2b9e-11e3-b191-718613007343" diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go index fc4f48c7d2..73dbed83b3 100644 --- a/openstack/messaging/v2/messages/doc.go +++ b/openstack/messaging/v2/messages/doc.go @@ -67,6 +67,16 @@ Example to Get a set of Messages panic(err) } +Example to get a singular Message + + queueName := "my_queue" + messageID := "123456" + + message, err := messages.Get(client, queueName, messageID).Extract() + if err != nil { + panic(err) + } + Example to Delete a set of Messages queueName := "my_queue" diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index 0d0093e00a..1c940a51a3 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -208,3 +208,11 @@ func GetMessages(client *gophercloud.ServiceClient, queueName string, opts GetMe }) return } + +// Get requests details on a single message, by ID. +func Get(client *gophercloud.ServiceClient, queueName string, messageID string) (r GetResult) { + _, r.Err = client.Get(messageURL(client, queueName, messageID), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go index c8ff42c15b..1c361e3f6d 100644 --- a/openstack/messaging/v2/messages/results.go +++ b/openstack/messaging/v2/messages/results.go @@ -33,7 +33,12 @@ type PopResult struct { // GetMessagesResult is the response of a GetMessages operations. type GetMessagesResult struct { - commonResult + gophercloud.Result +} + +// GetResult is the response of a Get operations. +type GetResult struct { + gophercloud.Result } // Message represents a message on a queue. @@ -86,6 +91,13 @@ func (r GetMessagesResult) Extract() ([]Message, error) { return s.Messages, err } +// Extract interprets any GetResult as a Message. +func (r GetResult) Extract() (Message, error) { + var s Message + err := r.ExtractInto(&s) + return s, err +} + // ExtractMessage extracts message into a list of Message. func ExtractMessages(r pagination.Page) ([]Message, error) { var s struct { diff --git a/openstack/messaging/v2/messages/testing/fixtures.go b/openstack/messaging/v2/messages/testing/fixtures.go index b4c5df6a14..65ff32efad 100644 --- a/openstack/messaging/v2/messages/testing/fixtures.go +++ b/openstack/messaging/v2/messages/testing/fixtures.go @@ -13,6 +13,9 @@ import ( // QueueName is the name of the queue var QueueName = "FakeTestQueue" +// MessageID is the id of the message +var MessageID = "9988776655" + // CreateMessageResponse is a sample response to a Create message. const CreateMessageResponse = ` { @@ -113,6 +116,21 @@ const GetMessagesResponse = ` ] }` +// GetMessageResponse is a sample response to Get. +const GetMessageResponse = ` +{ + "body": { + "current_bytes": "0", + "event": "BackupProgress", + "total_bytes": "99614720" + }, + "age": 482, + "href": "/v2/queues/FakeTestQueue/messages/578edfe6508f153f256f717b", + "id": "578edfe6508f153f256f717b", + "ttl": 3600, + "checksum": "MD5:abf7213555626e29c3cb3e5dc58b3515" +}` + // PopMessageResponse is a sample reponse to pop messages const PopMessageResponse = ` { @@ -248,6 +266,18 @@ func HandleGetMessagesSuccessfully(t *testing.T) { }) } +// HandleGetSuccessfully configures the test server to respond to a Get request. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages/%s", QueueName, MessageID), + 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, GetMessageResponse) + }) +} + // HandleDeleteMessagesSuccessfully configures the test server to respond to a Delete request. func HandleDeleteMessagesSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages", QueueName), diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go index 9162e0c4cb..f369f1eea9 100644 --- a/openstack/messaging/v2/messages/testing/requests_test.go +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -75,6 +75,16 @@ func TestGetMessages(t *testing.T) { th.CheckDeepEquals(t, ExpectedMessagesSet, actual) } +func TestGetMessage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := messages.Get(fake.ServiceClient(), QueueName, MessageID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, FirstMessage, actual) +} + func TestDeleteMessages(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/messaging/v2/messages/urls.go b/openstack/messaging/v2/messages/urls.go index 49a8e25468..9a526a5dd8 100644 --- a/openstack/messaging/v2/messages/urls.go +++ b/openstack/messaging/v2/messages/urls.go @@ -37,3 +37,7 @@ func nextPageURL(currentURL string, next string) (string, error) { func deleteURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") } + +func messageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "messages", messageID) +} From 4bc4da2d066ba5e71254c7150dab3d5463364bc2 Mon Sep 17 00:00:00 2001 From: Gavin Williams Date: Fri, 4 May 2018 23:10:49 +0100 Subject: [PATCH 0344/2296] =?UTF-8?q?Update=20subnets=20package=20to=20sup?= =?UTF-8?q?port=20removing=20all=20Host=20Routes=20from=20a=20sub=E2=80=A6?= =?UTF-8?q?=20(#987)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update subnets package to support removing all Host Routes from a subnet. Add test coverage. * Fix indentation * Update zero value handling, restore 'omitempty' * Fix failing tests due to pointer handling --- openstack/networking/v2/subnets/requests.go | 2 +- .../networking/v2/subnets/testing/fixtures.go | 73 +++++++++++++++++++ .../v2/subnets/testing/requests_test.go | 67 ++++++++++++++++- 3 files changed, 140 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index b77aedee17..f4a52a457b 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -176,7 +176,7 @@ type UpdateOpts struct { DNSNameservers []string `json:"dns_nameservers,omitempty"` // HostRoutes are any static host routes to be set via DHCP. - HostRoutes []HostRoute `json:"host_routes,omitempty"` + HostRoutes *[]HostRoute `json:"host_routes,omitempty"` // EnableDHCP will either enable to disable the DHCP service. EnableDHCP *bool `json:"enable_dhcp,omitempty"` diff --git a/openstack/networking/v2/subnets/testing/fixtures.go b/openstack/networking/v2/subnets/testing/fixtures.go index 9f9af0e658..8cac911eb3 100644 --- a/openstack/networking/v2/subnets/testing/fixtures.go +++ b/openstack/networking/v2/subnets/testing/fixtures.go @@ -445,6 +445,79 @@ const SubnetUpdateRemoveGatewayResponse = ` } ` +const SubnetUpdateHostRoutesRequest = ` +{ + "subnet": { + "name": "my_new_subnet", + "host_routes": [ + { + "destination": "192.168.1.1/24", + "nexthop": "bar" + } + ] + } +} +` + +const SubnetUpdateHostRoutesResponse = ` +{ + "subnet": { + "name": "my_new_subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "ip_version": 4, + "gateway_ip": "10.0.0.1", + "host_routes": [ + { + "destination": "192.168.1.1/24", + "nexthop": "bar" + } + ], + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + } +} +` + +const SubnetUpdateRemoveHostRoutesRequest = ` +{ + "subnet": { + "host_routes": [] + } +} +` + +const SubnetUpdateRemoveHostRoutesResponse = ` +{ + "subnet": { + "name": "my_new_subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": null, + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + } +} +` + const SubnetUpdateAllocationPoolRequest = ` { "subnet": { diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index 85fe14fbcc..bef6794b26 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -371,7 +371,7 @@ func TestUpdate(t *testing.T) { opts := subnets.UpdateOpts{ Name: "my_new_subnet", DNSNameservers: []string{"foo"}, - HostRoutes: []subnets.HostRoute{ + HostRoutes: &[]subnets.HostRoute{ {NextHop: "bar"}, }, } @@ -442,6 +442,71 @@ func TestUpdateRemoveGateway(t *testing.T) { th.AssertEquals(t, s.GatewayIP, "") } +func TestUpdateHostRoutes(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.TestJSONRequest(t, r, SubnetUpdateHostRoutesRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, SubnetUpdateHostRoutesResponse) + }) + + HostRoutes := []subnets.HostRoute{ + { + DestinationCIDR: "192.168.1.1/24", + NextHop: "bar", + }, + } + + opts := subnets.UpdateOpts{ + Name: "my_new_subnet", + HostRoutes: &HostRoutes, + } + s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "my_new_subnet") + th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b") + th.AssertDeepEquals(t, s.HostRoutes, HostRoutes) +} + +func TestUpdateRemoveHostRoutes(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.TestJSONRequest(t, r, SubnetUpdateRemoveHostRoutesRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, SubnetUpdateRemoveHostRoutesResponse) + }) + + noHostRoutes := []subnets.HostRoute{} + opts := subnets.UpdateOpts{ + HostRoutes: &noHostRoutes, + } + s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "my_new_subnet") + th.AssertEquals(t, s.ID, "08eae331-0402-425a-923c-34f7cfe39c1b") + th.AssertDeepEquals(t, s.HostRoutes, noHostRoutes) +} + func TestUpdateAllocationPool(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 913c7d4d6ffc5a8647b665be4097e97a6bae24cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Mon, 7 May 2018 10:37:56 +0800 Subject: [PATCH 0345/2296] Identity V3: Add Filters into Struct users.ListOpts (#980) * add Filters into struct users.ListOpts * some fixes * fix comment of field *Filters* * add filters check * move error type *InvalidListFilter* to the new file *errors.go* * simplify unit test *TestListUsersFiltersCheck* --- .../openstack/identity/v3/users_test.go | 44 +++++++++++++++++++ openstack/identity/v3/users/errors.go | 17 +++++++ openstack/identity/v3/users/requests.go | 21 +++++++++ .../v3/users/testing/requests_test.go | 32 ++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 openstack/identity/v3/users/errors.go diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index dd2e519df2..5bf2fcb5fb 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -41,6 +41,50 @@ func TestUsersList(t *testing.T) { } th.AssertEquals(t, found, true) + + listOpts.Filters = map[string]string{ + "name__contains": "dmi", + } + + allPages, err = users.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allUsers, err = users.ExtractUsers(allPages) + th.AssertNoErr(t, err) + + found = false + for _, user := range allUsers { + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) + + if user.Name == "admin" { + found = true + } + } + + th.AssertEquals(t, found, true) + + listOpts.Filters = map[string]string{ + "name__contains": "foo", + } + + allPages, err = users.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allUsers, err = users.ExtractUsers(allPages) + th.AssertNoErr(t, err) + + found = false + for _, user := range allUsers { + tools.PrintResource(t, user) + tools.PrintResource(t, user.Extra) + + if user.Name == "admin" { + found = true + } + } + + th.AssertEquals(t, found, false) } func TestUsersGet(t *testing.T) { diff --git a/openstack/identity/v3/users/errors.go b/openstack/identity/v3/users/errors.go new file mode 100644 index 0000000000..0f0b798754 --- /dev/null +++ b/openstack/identity/v3/users/errors.go @@ -0,0 +1,17 @@ +package users + +import "fmt" + +// InvalidListFilter is returned by the ToUserListQuery method when validation of +// a filter does not pass +type InvalidListFilter struct { + FilterName string +} + +func (e InvalidListFilter) Error() string { + s := fmt.Sprintf( + "Invalid filter name [%s]: it must be in format of NAME__COMPARATOR", + e.FilterName, + ) + return s +} diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index e5d396b656..ed3d1a026e 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -2,6 +2,8 @@ package users import ( "net/http" + "net/url" + "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" @@ -49,11 +51,30 @@ type ListOpts struct { // UniqueID filters the response by unique ID. UniqueID string `q:"unique_id"` + + // Filters filters the response by custom filters such as + // 'name__contains=foo' + Filters map[string]string `q:"-"` } // ToUserListQuery formats a ListOpts into a query string. func (opts ListOpts) ToUserListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + for k, v := range opts.Filters { + i := strings.Index(k, "__") + if i > 0 && i < len(k)-2 { + params.Add(k, v) + } else { + return "", InvalidListFilter{FilterName: k} + } + } + + q = &url.URL{RawQuery: params.Encode()} return q.String(), err } diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index c9c362451a..3eb1b46f5b 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -45,6 +45,38 @@ func TestListUsersAllPages(t *testing.T) { th.AssertEquals(t, ExpectedUsersSlice[1].Extra["email"], "jsmith@example.com") } +func TestListUsersFiltersCheck(t *testing.T) { + type test struct { + filterName string + wantErr bool + } + tests := []test{ + {"foo__contains", false}, + {"foo", true}, + {"foo_contains", true}, + {"foo__", true}, + {"__foo", true}, + } + + var listOpts users.ListOpts + for _, _test := range tests { + listOpts.Filters = map[string]string{_test.filterName: "bar"} + _, err := listOpts.ToUserListQuery() + + if !_test.wantErr { + th.AssertNoErr(t, err) + } else { + switch _t := err.(type) { + case nil: + t.Fatal("error expected but got a nil") + case users.InvalidListFilter: + default: + t.Fatalf("unexpected error type: [%T]", _t) + } + } + } +} + func TestGetUser(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From bcabb09813142f6673e6fd106cde1fe4dedf464c Mon Sep 17 00:00:00 2001 From: "zhang.zujian" Date: Mon, 7 May 2018 11:33:47 +0800 Subject: [PATCH 0346/2296] add field *Filters* into struct projects.ListOpts --- .../openstack/identity/v3/projects_test.go | 42 +++++++++++++++++++ openstack/identity/v3/projects/errors.go | 17 ++++++++ openstack/identity/v3/projects/requests.go | 22 ++++++++++ .../v3/projects/testing/requests_test.go | 32 ++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 openstack/identity/v3/projects/errors.go diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index e6d63c82a5..7256b80d81 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -38,6 +38,48 @@ func TestProjectsList(t *testing.T) { } th.AssertEquals(t, found, true) + + listOpts.Filters = map[string]string{ + "name__contains": "dmi", + } + + allPages, err = projects.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allProjects, err = projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + + found = false + for _, project := range allProjects { + tools.PrintResource(t, project) + + if project.Name == "admin" { + found = true + } + } + + th.AssertEquals(t, found, true) + + listOpts.Filters = map[string]string{ + "name__contains": "foo", + } + + allPages, err = projects.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allProjects, err = projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + + found = false + for _, project := range allProjects { + tools.PrintResource(t, project) + + if project.Name == "admin" { + found = true + } + } + + th.AssertEquals(t, found, false) } func TestProjectsGet(t *testing.T) { diff --git a/openstack/identity/v3/projects/errors.go b/openstack/identity/v3/projects/errors.go new file mode 100644 index 0000000000..7be97d8594 --- /dev/null +++ b/openstack/identity/v3/projects/errors.go @@ -0,0 +1,17 @@ +package projects + +import "fmt" + +// InvalidListFilter is returned by the ToUserListQuery method when validation of +// a filter does not pass +type InvalidListFilter struct { + FilterName string +} + +func (e InvalidListFilter) Error() string { + s := fmt.Sprintf( + "Invalid filter name [%s]: it must be in format of NAME__COMPARATOR", + e.FilterName, + ) + return s +} diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 368b7321ba..30f3d1f9ea 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -1,6 +1,9 @@ package projects import ( + "net/url" + "strings" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -28,11 +31,30 @@ type ListOpts struct { // ParentID filters the response by projects of a given parent project. ParentID string `q:"parent_id"` + + // Filters filters the response by custom filters such as + // 'name__contains=foo' + Filters map[string]string `q:"-"` } // ToProjectListQuery formats a ListOpts into a query string. func (opts ListOpts) ToProjectListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + for k, v := range opts.Filters { + i := strings.Index(k, "__") + if i > 0 && i < len(k)-2 { + params.Add(k, v) + } else { + return "", InvalidListFilter{FilterName: k} + } + } + + q = &url.URL{RawQuery: params.Encode()} return q.String(), err } diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 4b8af26a9c..9a6664889f 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -29,6 +29,38 @@ func TestListProjects(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListGroupsFiltersCheck(t *testing.T) { + type test struct { + filterName string + wantErr bool + } + tests := []test{ + {"foo__contains", false}, + {"foo", true}, + {"foo_contains", true}, + {"foo__", true}, + {"__foo", true}, + } + + var listOpts projects.ListOpts + for _, _test := range tests { + listOpts.Filters = map[string]string{_test.filterName: "bar"} + _, err := listOpts.ToProjectListQuery() + + if !_test.wantErr { + th.AssertNoErr(t, err) + } else { + switch _t := err.(type) { + case nil: + t.Fatal("error expected but got a nil") + case projects.InvalidListFilter: + default: + t.Fatalf("unexpected error type: [%T]", _t) + } + } + } +} + func TestGetProject(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 74d3703c79897f21f2c4c958ca0fa240a1bbe646 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Mon, 7 May 2018 19:43:51 -0700 Subject: [PATCH 0347/2296] Senlin: Clusters Create (#872) * senlin-clusters-create * Added Location header handling * Removed ExtractActionFromLocation() from results.go Added metadata unit/accep test Removed redundant DesiredCapacity json flag Changed MinSize from int to *int to accomodate value of 0 --- .../clustering/v1/autoscaling_test.go | 65 ++- openstack/clustering/v1/clusters/doc.go | 19 + openstack/clustering/v1/clusters/requests.go | 48 ++ openstack/clustering/v1/clusters/results.go | 117 +++++ .../clustering/v1/clusters/testing/doc.go | 2 + .../v1/clusters/testing/requests_test.go | 415 ++++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 14 + 7 files changed, 679 insertions(+), 1 deletion(-) create mode 100644 openstack/clustering/v1/clusters/doc.go create mode 100644 openstack/clustering/v1/clusters/requests.go create mode 100644 openstack/clustering/v1/clusters/results.go create mode 100644 openstack/clustering/v1/clusters/testing/doc.go create mode 100644 openstack/clustering/v1/clusters/testing/requests_test.go create mode 100644 openstack/clustering/v1/clusters/urls.go diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index 4ff8b63606..f7ab5d88df 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -1,11 +1,13 @@ -// +build acceptance clustering autoscaling profiles +// +build acceptance clustering autoscaling clusters profiles package v1 import ( + "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" "github.com/gophercloud/gophercloud/acceptance/tools" @@ -17,6 +19,7 @@ var testName string func TestAutoScaling(t *testing.T) { testName = tools.RandomString("TESTACC-", 8) profileCreate(t) + clusterCreate(t) } func profileCreate(t *testing.T) { @@ -68,3 +71,63 @@ func profileCreate(t *testing.T) { th.AssertEquals(t, "os.nova.server", profile.Spec.Type) th.AssertEquals(t, "1.0", profile.Spec.Version) } + +func clusterCreate(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + clusterName := testName + optsCluster := clusters.CreateOpts{ + Name: clusterName, + DesiredCapacity: 3, + ProfileID: testName, + MinSize: new(int), + MaxSize: 20, + Timeout: 3600, + Metadata: map[string]interface{}{ + "foo": "bar", + "test": map[string]interface{}{ + "nil_interface": interface{}(nil), + "float_value": float64(123.3), + "string_value": "test_string", + "bool_value": false, + }, + }, + Config: map[string]interface{}{}, + } + + createResult := clusters.Create(client, optsCluster) + th.AssertNoErr(t, createResult.Err) + + requestID := createResult.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, true, requestID != "") + + location := createResult.Header.Get("Location") + th.AssertEquals(t, true, location != "") + + actionID := "" + locationFields := strings.Split(location, "actions/") + if len(locationFields) >= 2 { + actionID = locationFields[1] + } + th.AssertEquals(t, true, actionID != "") + t.Logf("Cluster create action id: %s", actionID) + + cluster, err := createResult.Extract() + if err != nil { + t.Fatalf("Unable to create cluster %s: %v", clusterName, err) + } else { + t.Logf("Cluster created %+v", cluster) + } + + th.AssertEquals(t, optsCluster.Name, cluster.Name) + th.AssertEquals(t, optsCluster.DesiredCapacity, cluster.DesiredCapacity) + th.AssertEquals(t, optsCluster.ProfileID, cluster.ProfileName) + th.AssertEquals(t, *optsCluster.MinSize, cluster.MinSize) + th.AssertEquals(t, optsCluster.MaxSize, cluster.MaxSize) + th.AssertEquals(t, optsCluster.Timeout, cluster.Timeout) + th.CheckDeepEquals(t, optsCluster.Metadata, cluster.Metadata) + th.CheckDeepEquals(t, optsCluster.Config, cluster.Config) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go new file mode 100644 index 0000000000..bd19dd4ccd --- /dev/null +++ b/openstack/clustering/v1/clusters/doc.go @@ -0,0 +1,19 @@ +/* +Package clusters provides information and interaction with the clusters through +the OpenStack Clustering service. + +Example to Create a cluster + + createOpts := clusters.CreateOpts{ + Name: "test-cluster", + DesiredCapacity: 1, + ProfileUUID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", + } + + cluster, err := clusters.Create(serviceClient, createOpts).Extract() + if err != nil { + panic(err) + } + +*/ +package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go new file mode 100644 index 0000000000..15cc34c479 --- /dev/null +++ b/openstack/clustering/v1/clusters/requests.go @@ -0,0 +1,48 @@ +package clusters + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder Builder. +type CreateOptsBuilder interface { + ToClusterCreateMap() (map[string]interface{}, error) +} + +// CreateOpts params +type CreateOpts struct { + Name string `json:"name" required:"true"` + DesiredCapacity int `json:"desired_capacity"` + ProfileID string `json:"profile_id" required:"true"` + MinSize *int `json:"min_size,omitempty"` + Timeout int `json:"timeout,omitempty"` + MaxSize int `json:"max_size,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + Config map[string]interface{} `json:"config,omitempty"` +} + +// ToClusterCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "cluster") +} + +// Create requests the creation of a new cluster. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToClusterCreateMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go new file mode 100644 index 0000000000..5ebf32024a --- /dev/null +++ b/openstack/clustering/v1/clusters/results.go @@ -0,0 +1,117 @@ +package clusters + +import ( + "encoding/json" + "fmt" + "reflect" + "time" + + "github.com/gophercloud/gophercloud" +) + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Create operations. +type CreateResult struct { + commonResult +} + +type Cluster struct { + Config map[string]interface{} `json:"config"` + CreatedAt time.Time `json:"-"` + Data map[string]interface{} `json:"data"` + Dependents map[string]interface{} `json:"dependents"` + DesiredCapacity int `json:"desired_capacity"` + Domain string `json:"domain"` + ID string `json:"id"` + InitAt time.Time `json:"-"` + MaxSize int `json:"max_size"` + Metadata map[string]interface{} `json:"metadata"` + MinSize int `json:"min_size"` + Name string `json:"name"` + Nodes []string `json:"nodes"` + Policies []string `json:"policies"` + ProfileID string `json:"profile_id"` + ProfileName string `json:"profile_name"` + Project string `json:"project"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + Timeout int `json:"timeout"` + UpdatedAt time.Time `json:"-"` + User string `json:"user"` +} + +func (r commonResult) Extract() (*Cluster, error) { + var s struct { + Cluster *Cluster `json:"cluster"` + } + err := r.ExtractInto(&s) + if err != nil { + return s.Cluster, err + } + + return s.Cluster, nil +} + +func (r *Cluster) UnmarshalJSON(b []byte) error { + type tmp Cluster + var s struct { + tmp + CreatedAt interface{} `json:"created_at"` + InitAt interface{} `json:"init_at"` + UpdatedAt interface{} `json:"updated_at"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Cluster(s.tmp) + + switch t := s.CreatedAt.(type) { + case string: + if t != "" { + r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.CreatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) + } + + switch t := s.InitAt.(type) { + case string: + if t != "" { + r.InitAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.InitAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.InitAt)) + } + + switch t := s.UpdatedAt.(type) { + case string: + if t != "" { + r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.UpdatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) + } + + return nil +} diff --git a/openstack/clustering/v1/clusters/testing/doc.go b/openstack/clustering/v1/clusters/testing/doc.go new file mode 100644 index 0000000000..0221577893 --- /dev/null +++ b/openstack/clustering/v1/clusters/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_clusters_v1 +package testing diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go new file mode 100644 index 0000000000..2ae6f38086 --- /dev/null +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -0,0 +1,415 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "strings" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": "2015-02-10T14:26:14Z", + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "2015-02-10T15:26:14Z", + "max_size": 20, + "metadata": {}, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "2015-02-10T16:26:14Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + minSize := 1 + opts := clusters.CreateOpts{ + Name: "cluster1", + DesiredCapacity: 3, + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + MinSize: &minSize, + MaxSize: 20, + Timeout: 3600, + Metadata: map[string]interface{}{}, + Config: map[string]interface{}{}, + } + + createdAt, _ := time.Parse(time.RFC3339, "2015-02-10T14:26:14Z") + initAt, _ := time.Parse(time.RFC3339, "2015-02-10T15:26:14Z") + updatedAt, _ := time.Parse(time.RFC3339, "2015-02-10T16:26:14Z") + + createResult := clusters.Create(fake.ServiceClient(), opts) + if createResult.Err != nil { + t.Error("Error creating cluster. error=", createResult.Err) + } + + location := createResult.Header.Get("Location") + th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d", location) + + actionID := "" + locationFields := strings.Split(location, "actions/") + if len(locationFields) >= 2 { + actionID = locationFields[1] + } + th.AssertEquals(t, "625628cd-f877-44be-bde0-fec79f84e13d", actionID) + + actual, err := createResult.Extract() + if err != nil { + t.Error("Error creating cluster. error=", err) + } else { + expected := clusters.Cluster{ + Config: map[string]interface{}{}, + CreatedAt: createdAt, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 3, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: initAt, + MaxSize: 20, + Metadata: map[string]interface{}{}, + MinSize: 1, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: updatedAt, + User: "5e5bf8027826429c96af157f68dc9072", + } + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestCreateClusterEmptyTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": null, + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": null, + "max_size": 20, + "metadata": {}, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + minSize := 1 + opts := clusters.CreateOpts{ + Name: "cluster1", + DesiredCapacity: 3, + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + MinSize: &minSize, + MaxSize: 20, + Timeout: 3600, + Metadata: map[string]interface{}{}, + Config: map[string]interface{}{}, + } + + actual, err := clusters.Create(fake.ServiceClient(), opts).Extract() + if err != nil { + t.Error("Error creating cluster. error=", err) + } else { + expected := clusters.Cluster{ + Config: map[string]interface{}{}, + CreatedAt: time.Time{}, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 3, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: time.Time{}, + MaxSize: 20, + Metadata: map[string]interface{}{}, + MinSize: 1, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: time.Time{}, + User: "5e5bf8027826429c96af157f68dc9072", + } + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestCreateClusterInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": 123456789.0, + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": 123456789.0, + "max_size": 20, + "metadata": {}, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": 123456789.0, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + minSize := 1 + opts := clusters.CreateOpts{ + Name: "cluster1", + DesiredCapacity: 3, + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + MinSize: &minSize, + MaxSize: 20, + Timeout: 3600, + Metadata: map[string]interface{}{}, + Config: map[string]interface{}{}, + } + + _, err := clusters.Create(fake.ServiceClient(), opts).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestCreateClusterInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": "invalid", + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "invalid", + "max_size": 20, + "metadata": {}, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "invalid", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + minSize := 1 + opts := clusters.CreateOpts{ + Name: "cluster1", + DesiredCapacity: 3, + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + MinSize: &minSize, + MaxSize: 20, + Timeout: 3600, + Metadata: map[string]interface{}{}, + Config: map[string]interface{}{}, + } + + _, err := clusters.Create(fake.ServiceClient(), opts).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestCreateClusterMetadata(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": "invalid", + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "invalid", + "max_size": 20, + "metadata": { + "test": { + "nil_interface": null, + "bool_value": false, + "string_value": "test_string", + "float_value": 123.3 + }, + "foo": "bar" + }, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "invalid", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + minSize := 1 + opts := clusters.CreateOpts{ + Name: "cluster1", + DesiredCapacity: 3, + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + MinSize: &minSize, + MaxSize: 20, + Timeout: 3600, + Metadata: map[string]interface{}{ + "foo": "bar", + "test": map[string]interface{}{ + "nil_interface": interface{}(nil), + "float_value": float64(123.3), + "string_value": "test_string", + "bool_value": false, + }, + }, + Config: map[string]interface{}{}, + } + + _, err := clusters.Create(fake.ServiceClient(), opts).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go new file mode 100644 index 0000000000..508b76ad5e --- /dev/null +++ b/openstack/clustering/v1/clusters/urls.go @@ -0,0 +1,14 @@ +package clusters + +import "github.com/gophercloud/gophercloud" + +var apiVersion = "v1" +var apiName = "clusters" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} + +func createURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 92be9d473e0133865c99b0de4042dba1a0ee97f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Tue, 8 May 2018 11:48:50 +0800 Subject: [PATCH 0348/2296] Add Field *Filters* into Struct groups.ListOpts (#989) * add field *Filters* to struct groups.ListOpts * fix acceptance test * fix acceptance test --- .../openstack/identity/v3/groups_test.go | 58 ++++++++++++++++++- openstack/identity/v3/groups/errors.go | 17 ++++++ openstack/identity/v3/groups/requests.go | 22 +++++++ .../v3/groups/testing/requests_test.go | 32 ++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 openstack/identity/v3/groups/errors.go diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go index 3b411bd032..18c5ae0578 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -34,7 +34,7 @@ func TestGroupCRUD(t *testing.T) { tools.PrintResource(t, group.Extra) updateOpts := groups.UpdateOpts{ - Description: "Test Users", + Description: "Test Groups", Extra: map[string]interface{}{ "email": "thetestgroup@example.com", }, @@ -62,6 +62,62 @@ func TestGroupCRUD(t *testing.T) { tools.PrintResource(t, g.Extra) } + var found bool + for _, group := range allGroups { + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) + + if group.Name == newGroup.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + + listOpts.Filters = map[string]string{ + "name__contains": "TEST", + } + + allPages, err = groups.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allGroups, err = groups.ExtractGroups(allPages) + th.AssertNoErr(t, err) + + found = false + for _, group := range allGroups { + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) + + if group.Name == newGroup.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + + listOpts.Filters = map[string]string{ + "name__contains": "foo", + } + + allPages, err = groups.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allGroups, err = groups.ExtractGroups(allPages) + th.AssertNoErr(t, err) + + found = false + for _, group := range allGroups { + tools.PrintResource(t, group) + tools.PrintResource(t, group.Extra) + + if group.Name == newGroup.Name { + found = true + } + } + + th.AssertEquals(t, found, false) + // Get the recently created group by ID p, err := groups.Get(client, group.ID).Extract() th.AssertNoErr(t, err) diff --git a/openstack/identity/v3/groups/errors.go b/openstack/identity/v3/groups/errors.go new file mode 100644 index 0000000000..98e6fe4b0e --- /dev/null +++ b/openstack/identity/v3/groups/errors.go @@ -0,0 +1,17 @@ +package groups + +import "fmt" + +// InvalidListFilter is returned by the ToUserListQuery method when validation of +// a filter does not pass +type InvalidListFilter struct { + FilterName string +} + +func (e InvalidListFilter) Error() string { + s := fmt.Sprintf( + "Invalid filter name [%s]: it must be in format of NAME__COMPARATOR", + e.FilterName, + ) + return s +} diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index b6e74dcf97..cc991204cf 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -1,6 +1,9 @@ package groups import ( + "net/url" + "strings" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -18,11 +21,30 @@ type ListOpts struct { // Name filters the response by group name. Name string `q:"name"` + + // Filters filters the response by custom filters such as + // 'name__contains=foo' + Filters map[string]string `q:"-"` } // ToGroupListQuery formats a ListOpts into a query string. func (opts ListOpts) ToGroupListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + for k, v := range opts.Filters { + i := strings.Index(k, "__") + if i > 0 && i < len(k)-2 { + params.Add(k, v) + } else { + return "", InvalidListFilter{FilterName: k} + } + } + + q = &url.URL{RawQuery: params.Encode()} return q.String(), err } diff --git a/openstack/identity/v3/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go index e35c97214b..b62779a9ad 100644 --- a/openstack/identity/v3/groups/testing/requests_test.go +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -43,6 +43,38 @@ func TestListGroupsAllPages(t *testing.T) { th.AssertEquals(t, ExpectedGroupsSlice[1].Extra["email"], "support@example.com") } +func TestListGroupsFiltersCheck(t *testing.T) { + type test struct { + filterName string + wantErr bool + } + tests := []test{ + {"foo__contains", false}, + {"foo", true}, + {"foo_contains", true}, + {"foo__", true}, + {"__foo", true}, + } + + var listOpts groups.ListOpts + for _, _test := range tests { + listOpts.Filters = map[string]string{_test.filterName: "bar"} + _, err := listOpts.ToGroupListQuery() + + if !_test.wantErr { + th.AssertNoErr(t, err) + } else { + switch _t := err.(type) { + case nil: + t.Fatal("error expected but got a nil") + case groups.InvalidListFilter: + default: + t.Fatalf("unexpected error type: [%T]", _t) + } + } + } +} + func TestGetGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 62533d72ab71d24befbe451ac3a20ec1b4801249 Mon Sep 17 00:00:00 2001 From: Yongfeng Du Date: Wed, 9 May 2018 10:58:15 +0800 Subject: [PATCH 0349/2296] Run container tests in gophercloud The previous PR overwritten the container tests job theopenlab/openlab-zuul-jobs#190, add it to avoid regression. --- .zuul/playbooks/gophercloud-acceptance-test/run.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index f4e20f641e..fedb69d10b 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -49,6 +49,7 @@ go test -v -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ go test -v -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ go test -v -tags "fixtures acceptance" -run "SecGroup|Flavor" ./acceptance/openstack/compute/v2/ + go test -v -tags "fixtures acceptance" ./acceptance/openstack/container/v1/ # To enable more after the fix of https://github.com/gophercloud/gophercloud/issues/608 # go test -v -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ # go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v2/ From 4cb9db631b8011926caa55a06237ae458dca5517 Mon Sep 17 00:00:00 2001 From: Kevin Zhao Date: Wed, 9 May 2018 13:07:37 +1000 Subject: [PATCH 0350/2296] Add capsule delete operation (#956) * Add capsule delete operation Change-Id: Id46a2d90e21b51f20e2f229394c8bf2666d794b8 Signed-off-by: Kevin Zhao * Add capsule delete acceptance test Change-Id: I744e82ccd5e5ede54fa644d742430dbc43431b4d Signed-off-by: Kevin Zhao --- acceptance/openstack/container/v1/capsules_test.go | 4 ++++ openstack/container/v1/capsules/requests.go | 6 ++++++ openstack/container/v1/capsules/results.go | 5 +++++ openstack/container/v1/capsules/testing/fixtures.go | 8 ++++++++ .../container/v1/capsules/testing/requests_test.go | 10 ++++++++++ openstack/container/v1/capsules/urls.go | 4 ++++ 6 files changed, 37 insertions(+) diff --git a/acceptance/openstack/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index 63c1078c6b..2c0fcaa7f2 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -74,6 +74,10 @@ func TestCapsule(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, capsule.MetaName, "template") + + err = capsules.Delete(client, capsuleUUID).ExtractErr() + th.AssertNoErr(t, err) + } return true, nil }) diff --git a/openstack/container/v1/capsules/requests.go b/openstack/container/v1/capsules/requests.go index 40d0771765..0c606fa3b5 100644 --- a/openstack/container/v1/capsules/requests.go +++ b/openstack/container/v1/capsules/requests.go @@ -94,3 +94,9 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return CapsulePage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Delete implements Capsule delete request. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index 58f87b617e..589398ac41 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -30,6 +30,11 @@ type CreateResult struct { gophercloud.ErrResult } +// DeleteResult represents the result of a delete operation. +type DeleteResult struct { + gophercloud.ErrResult +} + type CapsulePage struct { pagination.LinkedPageBase } diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 3f3b1c7e97..d3c553f3fc 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -355,3 +355,11 @@ func HandleCapsuleListSuccessfully(t *testing.T) { fmt.Fprintf(w, CapsuleListBody) }) } + +func HandleCapsuleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/capsules/963a239d-3946-452b-be5a-055eab65a421", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 8f77a55ae1..41490b23a8 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -330,3 +330,13 @@ func TestListCapsule(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCapsuleDeleteSuccessfully(t) + + res := capsules.Delete(fakeclient.ServiceClient(), "963a239d-3946-452b-be5a-055eab65a421") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/container/v1/capsules/urls.go b/openstack/container/v1/capsules/urls.go index fd682efbc4..575fb2a712 100644 --- a/openstack/container/v1/capsules/urls.go +++ b/openstack/container/v1/capsules/urls.go @@ -15,3 +15,7 @@ func createURL(client *gophercloud.ServiceClient) string { func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("capsules") } + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("capsules", id) +} From 1da27352fc3d026d0e424c123aabcacc1b2f5921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Va=C5=A1ek?= Date: Wed, 9 May 2018 05:36:53 +0200 Subject: [PATCH 0351/2296] openstack shares: add ListAccessRights (#986) * openstack shares: add ListAccessRights * sharedfilesystems: fixed comment for ListAccessRights * shares: ListAccessRightsResult should return []AccessRight * acceptance tests: added shares/TestListAccessRights --- .../openstack/sharedfilesystems/v2/shares.go | 26 ++++++++++++++ .../sharedfilesystems/v2/shares_test.go | 34 +++++++++++++++++++ .../sharedfilesystems/v2/shares/requests.go | 11 ++++++ .../sharedfilesystems/v2/shares/results.go | 14 ++++++++ .../v2/shares/testing/fixtures.go | 32 +++++++++++++++++ .../v2/shares/testing/request_test.go | 26 ++++++++++++++ openstack/sharedfilesystems/v2/shares/urls.go | 4 +++ 7 files changed, 147 insertions(+) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index 82f2f8d1ff..5a3cc333b7 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -43,6 +43,22 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share return share, nil } +// GrantAccess will grant access to an existing share. A fatal error will occur if +// this operation fails. +func GrantAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) (*shares.AccessRight, error) { + return shares.GrantAccess(client, share.ID, shares.GrantAccessOpts{ + AccessType: "ip", + AccessTo: "0.0.0.0/32", + AccessLevel: "r", + }).Extract() +} + +// GetAccessRightsSlice will retrieve all access rules assigned to a share. +// A fatal error will occur if this operation fails. +func GetAccessRightsSlice(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) ([]shares.AccessRight, error) { + return shares.ListAccessRights(client, share.ID).Extract() +} + // DeleteShare will delete a share. A fatal error will occur if the share // failed to be deleted. This works best when used as a deferred function. func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) { @@ -64,6 +80,16 @@ func PrintShare(t *testing.T, share *shares.Share) { t.Logf("Share %s", string(asJSON)) } +// PrintAccessRight prints contents of an access rule +func PrintAccessRight(t *testing.T, accessRight *shares.AccessRight) { + asJSON, err := json.MarshalIndent(accessRight, "", " ") + if err != nil { + t.Logf("Cannot print access rule") + } + + t.Logf("Access rule %s", string(asJSON)) +} + func waitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := shares.Get(c, id).Extract() diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index abb9b3aefa..9de10a5252 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -26,3 +26,37 @@ func TestShareCreate(t *testing.T) { } PrintShare(t, created) } + +func TestListAccessRights(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a sharedfs client: %v", err) + } + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + _, err = GrantAccess(t, client, share) + if err != nil { + t.Fatalf("Unable to grant access: %v", err) + } + + rs, err := GetAccessRightsSlice(t, client, share) + if err != nil { + t.Fatalf("Unable to retrieve list of access rules for share %s: %v", share.ID, err) + } + + if len(rs) != 1 { + t.Fatalf("Unexpected number of access rules for share %s: got %d, expected 1", share.ID, len(rs)) + } + + t.Logf("Share %s has %d access rule(s):", share.ID, len(rs)) + + for _, r := range rs { + PrintAccessRight(t, &r) + } +} diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 27bc43b563..3a4bda096d 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -123,3 +123,14 @@ func GrantAccess(client *gophercloud.ServiceClient, id string, opts GrantAccessO }) return } + +// ListAccessRights lists all access rules assigned to a Share based on its id. To extract +// the AccessRight slice from the response, call the Extract method on the ListAccessRightsResult. +// Client must have Microversion set; minimum supported microversion for ListAccessRights is 2.7. +func ListAccessRights(client *gophercloud.ServiceClient, id string) (r ListAccessRightsResult) { + requestBody := map[string]interface{}{"access_list": nil} + _, r.Err = client.Post(listAccessRightsURL(client, id), requestBody, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index b76f86afb0..1df7af21ab 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -177,3 +177,17 @@ func (r GrantAccessResult) Extract() (*AccessRight, error) { type GrantAccessResult struct { gophercloud.Result } + +// Extract will get a slice of AccessRight objects from the commonResult +func (r ListAccessRightsResult) Extract() ([]AccessRight, error) { + var s struct { + AccessRights []AccessRight `json:"access_list"` + } + err := r.ExtractInto(&s) + return s.AccessRights, err +} + +// ListAccessRightsResult contains the result body and error from a ListAccessRights request. +type ListAccessRightsResult struct { + gophercloud.Result +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 8ab2742df9..dc725b9e07 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -197,3 +197,35 @@ func MockGrantAccessResponse(t *testing.T) { fmt.Fprintf(w, grantAccessResponse) }) } + +var listAccessRightsRequest = `{ + "access_list": null + }` + +var listAccessRightsResponse = `{ + "access_list": [ + { + "share_id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", + "access_type": "ip", + "access_to": "0.0.0.0/0", + "access_key": "", + "access_level": "rw", + "state": "new", + "id": "a2f226a5-cee8-430b-8a03-78a59bd84ee8" + } + ] + }` + +// MockListAccessRightsResponse creates a mock list access response +func MockListAccessRightsResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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, listAccessRightsRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, listAccessRightsResponse) + }) +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index 443dd1fa48..e31cb1a6d3 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -135,3 +135,29 @@ func TestGrantAcessSuccess(t *testing.T) { ID: "a2f226a5-cee8-430b-8a03-78a59bd84ee8", }) } + +func TestListAccessRightsSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListAccessRightsResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 + c.Microversion = "2.7" + + s, err := shares.ListAccessRights(c, shareID).Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, s, []shares.AccessRight{ + { + ShareID: "011d21e2-fbc3-4e4a-9993-9ea223f73264", + AccessType: "ip", + AccessTo: "0.0.0.0/0", + AccessKey: "", + AccessLevel: "rw", + State: "new", + ID: "a2f226a5-cee8-430b-8a03-78a59bd84ee8", + }, + }) +} diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index 38e1aa431e..5fa5b9c69c 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -21,3 +21,7 @@ func getExportLocationsURL(c *gophercloud.ServiceClient, id string) string { func grantAccessURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } + +func listAccessRightsURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} From 6a29763cc0f7fbe1b6a84ac622d79c51ea141d14 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Tue, 8 May 2018 11:01:26 -0700 Subject: [PATCH 0352/2296] Add delete to messages --- .../openstack/messaging/v2/message_test.go | 47 +++++++++++++++++++ openstack/messaging/v2/messages/doc.go | 15 ++++++ openstack/messaging/v2/messages/requests.go | 35 ++++++++++++++ .../messaging/v2/messages/testing/fixtures.go | 12 +++++ .../v2/messages/testing/requests_test.go | 13 +++++ openstack/messaging/v2/messages/urls.go | 4 ++ 6 files changed, 126 insertions(+) diff --git a/acceptance/openstack/messaging/v2/message_test.go b/acceptance/openstack/messaging/v2/message_test.go index 14984a568f..f3c558116e 100644 --- a/acceptance/openstack/messaging/v2/message_test.go +++ b/acceptance/openstack/messaging/v2/message_test.go @@ -252,3 +252,50 @@ func TestDeleteMessagesPop(t *testing.T) { t.Fatalf("Unable to Pop specified number of messages.") } } + +func TestDeleteMessage(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-718613007343" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + CreateMessage(t, client, createdQueueName) + + // Use a different client/clientID in order to see messages on the Queue + clientID = "3381af92-2b9e-11e3-b191-71861300734d" + client, err = clients.NewMessagingV2Client(clientID) + + listOpts := messages.ListOpts{} + + var messageIDs []string + + pager := messages.List(client, createdQueueName, listOpts) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + allMessages, err := messages.ExtractMessages(page) + if err != nil { + t.Fatalf("Unable to extract messages: %v", err) + } + + for _, message := range allMessages { + messageIDs = append(messageIDs, message.ID) + } + + return true, nil + }) + + for _, messageID := range messageIDs { + t.Logf("Attempting to delete message from queue %s: %s", createdQueueName, messageID) + deleteOpts := messages.DeleteOpts{} + deleteErr := messages.Delete(client, createdQueueName, messageID, deleteOpts).ExtractErr() + if deleteErr != nil { + t.Fatalf("Unable to delete message from queue %s: %s", createdQueueName, messageID) + } else { + t.Logf("Successfully deleted message: %s", messageID) + } + } +} diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go index 73dbed83b3..e5f5bce60f 100644 --- a/openstack/messaging/v2/messages/doc.go +++ b/openstack/messaging/v2/messages/doc.go @@ -102,5 +102,20 @@ Example to Pop a set of Messages if err != nil { panic(err) } + +Example to Delete a singular Message + + clientID := "3381af92-2b9e-11e3-b191-71861300734d" + queueName := "my_queue" + messageID := "123456" + + deleteOpts := messages.DeleteOpts{ + ClaimID: "12345", + } + + err := messages.Delete(client), queueName, messageID, deleteOpts).ExtractErr() + if err != nil { + panic(err) + } */ package messages diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index 1c940a51a3..6aac4ac35c 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -216,3 +216,38 @@ func Get(client *gophercloud.ServiceClient, queueName string, messageID string) }) return } + +// DeleteOptsBuilder allows extensions to add additional parameters to the +// delete request. +type DeleteOptsBuilder interface { + ToMessageDeleteQuery() (string, error) +} + +// DeleteOpts params to be used with Delete. +type DeleteOpts struct { + // ClaimID instructs Delete to delete a message that is associated with a claim ID + ClaimID string `q:"claim_id,omitempty"` +} + +// ToMessageDeleteQuery formats a DeleteOpts structure into a query string. +func (opts DeleteOpts) ToMessageDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Delete deletes a specific message from the queue. +func Delete(client *gophercloud.ServiceClient, queueName string, messageID string, opts DeleteOptsBuilder) (r DeleteResult) { + url := DeleteMessageURL(client, queueName, messageID) + if opts != nil { + query, err := opts.ToMessageDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/messaging/v2/messages/testing/fixtures.go b/openstack/messaging/v2/messages/testing/fixtures.go index 65ff32efad..000f887ffa 100644 --- a/openstack/messaging/v2/messages/testing/fixtures.go +++ b/openstack/messaging/v2/messages/testing/fixtures.go @@ -302,3 +302,15 @@ func HandlePopSuccessfully(t *testing.T) { fmt.Fprintf(w, PopMessageResponse) }) } + +// HandleGetSuccessfully configures the test server to respond to a Get request. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/messages/%s", QueueName, MessageID), + 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) + }) +} diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go index f369f1eea9..eb839262b9 100644 --- a/openstack/messaging/v2/messages/testing/requests_test.go +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -111,3 +111,16 @@ func TestPopMessages(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedPopMessage, actual) } + +func TestDeleteMessage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + deleteOpts := messages.DeleteOpts{ + ClaimID: "12345", + } + + err := messages.Delete(fake.ServiceClient(), QueueName, MessageID, deleteOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/messaging/v2/messages/urls.go b/openstack/messaging/v2/messages/urls.go index 9a526a5dd8..0816ee14df 100644 --- a/openstack/messaging/v2/messages/urls.go +++ b/openstack/messaging/v2/messages/urls.go @@ -38,6 +38,10 @@ func deleteURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") } +func DeleteMessageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { + return client.ServiceURL(ApiVersion, ApiName, queueName, "messages", messageID) +} + func messageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { return client.ServiceURL(ApiVersion, ApiName, queueName, "messages", messageID) } From 62d7b0348926742e3765d02e6aa563b6775da3a5 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 30 Mar 2018 11:50:46 -0700 Subject: [PATCH 0353/2296] senlin-profile-get --- .../clustering/v1/autoscaling_test.go | 18 +- openstack/clustering/v1/profiles/doc.go | 10 ++ openstack/clustering/v1/profiles/requests.go | 12 ++ openstack/clustering/v1/profiles/results.go | 8 +- .../v1/profiles/testing/requests_test.go | 169 ++++++++++++++++++ openstack/clustering/v1/profiles/urls.go | 8 + 6 files changed, 221 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index f7ab5d88df..22cfce0ea1 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -7,10 +7,9 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" - - "github.com/gophercloud/gophercloud/acceptance/tools" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -19,6 +18,7 @@ var testName string func TestAutoScaling(t *testing.T) { testName = tools.RandomString("TESTACC-", 8) profileCreate(t) + profileGet(t) clusterCreate(t) } @@ -131,3 +131,17 @@ func clusterCreate(t *testing.T) { th.CheckDeepEquals(t, optsCluster.Metadata, cluster.Metadata) th.CheckDeepEquals(t, optsCluster.Config, cluster.Config) } + +func profileGet(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + profileName := testName + profile, err := profiles.Get(client, profileName).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, profileName, profile.Name) + + tools.PrintResource(t, profile) +} diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index f4c96cd659..2778eae140 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -28,5 +28,15 @@ Example to Create a profile } fmt.Println("Profile", profile) + +Example to Get profile + + profile, err := profiles.Get(serviceClient, "profile-name").Extract() + if err != nil { + panic(err) + } + + fmt.Print("profile", profile) + */ package profiles diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index 2c3475bf27..b29a2ce122 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -40,3 +40,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } return } + +// Get retrieves detail of a single profile. Use Extract to convert its +// result into a Profile. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + var result *http.Response + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index c72a2e891f..1661271724 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -3,9 +3,8 @@ package profiles import ( "encoding/json" "fmt" - "time" - "reflect" + "time" "github.com/gophercloud/gophercloud" ) @@ -20,6 +19,11 @@ type CreateResult struct { commonResult } +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + // Extract provides access to Profile returned by the Get and Create functions. func (r commonResult) Extract() (*Profile, error) { var s struct { diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go index 9a9c2939cb..cc6b7b644e 100644 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -215,3 +215,172 @@ func TestCreateProfileInvalidTimeString(t *testing.T) { _, err := profiles.Create(fake.ServiceClient(), optsProfile).Extract() th.AssertEquals(t, false, err == nil) } + +func TestGetProfile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` + { + "profile": { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + actual, err := profiles.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() + if err != nil { + t.Errorf("Failed to get profile. %v", err) + } else { + createdAt, _ := time.Parse(time.RFC3339, "2016-01-03T16:22:23Z") + updatedAt := time.Time{} + expected := profiles.Profile{ + CreatedAt: createdAt, + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": float64(1), + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: updatedAt, + User: "5e5bf8027826429c96af157f68dc9072", + } + + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestGetProfileInvalidCreatedAtTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` + { + "profile": { + "created_at": 1234567890.0, + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + _, err := profiles.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestGetProfileInvalidUpdatedAtTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` + { + "profile": { + "created_at": null, + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": 1234567890.0, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + _, err := profiles.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/profiles/urls.go b/openstack/clustering/v1/profiles/urls.go index 123be5eda4..d7cdaa3612 100644 --- a/openstack/clustering/v1/profiles/urls.go +++ b/openstack/clustering/v1/profiles/urls.go @@ -12,3 +12,11 @@ func commonURL(client *gophercloud.ServiceClient) string { func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func idURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 3110a504c2eecd9ae59536caeca4b183346d94c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Fri, 11 May 2018 09:42:32 +0800 Subject: [PATCH 0354/2296] Identity V3: Create Policy (#981) * Identity V3: create policy * fix acceptance test * change type of CreateOpts.Blob from string to []byte; add length check of CreateOpts.Type. * change package import into a single line * fix acceptance test --- .../openstack/identity/v3/policies_test.go | 25 ++++++++ openstack/identity/v3/policies/doc.go | 15 +++++ openstack/identity/v3/policies/errors.go | 15 +++++ openstack/identity/v3/policies/requests.go | 62 ++++++++++++++++++- openstack/identity/v3/policies/results.go | 20 ++++++ .../identity/v3/policies/testing/fixtures.go | 39 ++++++++++++ .../v3/policies/testing/requests_test.go | 62 +++++++++++++++++++ openstack/identity/v3/policies/urls.go | 4 ++ 8 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 openstack/identity/v3/policies/errors.go diff --git a/acceptance/openstack/identity/v3/policies_test.go b/acceptance/openstack/identity/v3/policies_test.go index 1f0b642eea..e78f6c5697 100644 --- a/acceptance/openstack/identity/v3/policies_test.go +++ b/acceptance/openstack/identity/v3/policies_test.go @@ -27,3 +27,28 @@ func TestPoliciesList(t *testing.T) { tools.PrintResource(t, policy) } } + +func TestPoliciesCreate(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + createOpts := policies.CreateOpts{ + Type: "application/json", + Blob: []byte("{'foobar_user': 'role:compute-user'}"), + Extra: map[string]interface{}{ + "description": "policy for foobar_user", + }, + } + + policy, err := policies.Create(client, &createOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, policy) + tools.PrintResource(t, policy.Extra) + + th.AssertEquals(t, policy.Type, createOpts.Type) + th.AssertEquals(t, policy.Blob, string(createOpts.Blob)) + th.AssertEquals(t, policy.Extra["description"], createOpts.Extra["description"]) +} diff --git a/openstack/identity/v3/policies/doc.go b/openstack/identity/v3/policies/doc.go index 16ea3e0ff6..76ff76ab90 100644 --- a/openstack/identity/v3/policies/doc.go +++ b/openstack/identity/v3/policies/doc.go @@ -21,5 +21,20 @@ Example to List Policies for _, policy := range allPolicies { fmt.Printf("%+v\n", policy) } + +Example to Create a Policy + + createOpts := policies.CreateOpts{ + Type: "application/json", + Blob: []byte("{'foobar_user': 'role:compute-user'}"), + Extra: map[string]interface{}{ + "description": "policy for foobar_user", + }, + } + + policy, err := policies.Create(identityClient, createOpts).Extract() + if err != nil { + panic(err) + } */ package policies diff --git a/openstack/identity/v3/policies/errors.go b/openstack/identity/v3/policies/errors.go new file mode 100644 index 0000000000..43c477b8e4 --- /dev/null +++ b/openstack/identity/v3/policies/errors.go @@ -0,0 +1,15 @@ +package policies + +import "fmt" + +// StringFieldLengthExceedsLimit xxx +type StringFieldLengthExceedsLimit struct { + Field string + Limit int +} + +func (e StringFieldLengthExceedsLimit) Error() string { + return fmt.Sprintf("String length of field [%s] exceeds limit (%d)", + e.Field, e.Limit, + ) +} diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index 4b49d3ea6f..7449131f23 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -5,6 +5,8 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +const createOptsTypeMaxLength = 255 + // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { @@ -24,7 +26,7 @@ func (opts ListOpts) ToPolicyListQuery() (string, error) { return q.String(), err } -// List enumerates the roles to which the current token has access. +// List enumerates the policies to which the current token has access. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { @@ -39,3 +41,61 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return PolicyPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToPolicyCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a policy. +type CreateOpts struct { + // Type is the MIME media type of the serialized policy blob. + Type string `json:"type" required:"true"` + + // Blob is the policy rule as a serialized blob. + Blob []byte `json:"-" required:"true"` + + // Extra is free-form extra key/value pairs to describe the policy. + Extra map[string]interface{} `json:"-"` +} + +// ToPolicyCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { + if len(opts.Type) > createOptsTypeMaxLength { + return nil, StringFieldLengthExceedsLimit{ + Field: "type", + Limit: createOptsTypeMaxLength, + } + } + + b, err := gophercloud.BuildRequestBody(opts, "policy") + if err != nil { + return nil, err + } + + if v, ok := b["policy"].(map[string]interface{}); ok { + v["blob"] = string(opts.Blob) + + if opts.Extra != nil { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Create creates a new Policy. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToPolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go index 6528b12247..84ef1fc7c2 100644 --- a/openstack/identity/v3/policies/results.go +++ b/openstack/identity/v3/policies/results.go @@ -3,6 +3,7 @@ package policies import ( "encoding/json" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -56,6 +57,16 @@ func (r *Policy) UnmarshalJSON(b []byte) error { return err } +type policyResult struct { + gophercloud.Result +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Policy +type CreateResult struct { + policyResult +} + // PolicyPage is a single page of Policy results. type PolicyPage struct { pagination.LinkedPageBase @@ -91,3 +102,12 @@ func ExtractPolicies(r pagination.Page) ([]Policy, error) { err := (r.(PolicyPage)).ExtractInto(&s) return s.Policies, err } + +// Extract interprets any policyResults as a Policy. +func (r policyResult) Extract() (*Policy, error) { + var s struct { + Policy *Policy `json:"policy"` + } + err := r.ExtractInto(&s) + return s.Policy, err +} diff --git a/openstack/identity/v3/policies/testing/fixtures.go b/openstack/identity/v3/policies/testing/fixtures.go index 292905c037..5038c5a0f4 100644 --- a/openstack/identity/v3/policies/testing/fixtures.go +++ b/openstack/identity/v3/policies/testing/fixtures.go @@ -62,6 +62,32 @@ const ListWithFilterOutput = ` } ` +// GetOutput provides a Get result. +const GetOutput = ` +{ + "policy": { + "type": "application/json", + "id": "b49884da9d31494ea02aff38d4b4e701", + "links": { + "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701" + }, + "blob": "{'bar_user': 'role:network-user'}", + "description": "policy for bar_user" + } +} +` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "policy": { + "blob": "{'bar_user': 'role:network-user'}", + "description": "policy for bar_user", + "type": "application/json" + } +} +` + // FirstPolicy is the first policy in the List request. var FirstPolicy = policies.Policy{ ID: "2844b2a08be147a08ef58317d6471f1f", @@ -109,3 +135,16 @@ func HandleListPoliciesSuccessfully(t *testing.T) { } }) } + +// HandleCreatePolicySuccessfully creates an HTTP handler at `/policies` on the +// test handler mux that tests policy creation. +func HandleCreatePolicySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/policies", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, CreateRequest) + + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, GetOutput) + }) +} diff --git a/openstack/identity/v3/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go index e67dd6c130..45359f7588 100644 --- a/openstack/identity/v3/policies/testing/requests_test.go +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "fmt" "testing" "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" @@ -55,3 +56,64 @@ func TestListPoliciesWithFilter(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, []policies.Policy{SecondPolicy}, actual) } + +func TestCreatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreatePolicySuccessfully(t) + + createOpts := policies.CreateOpts{ + Type: "application/json", + Blob: []byte("{'bar_user': 'role:network-user'}"), + Extra: map[string]interface{}{ + "description": "policy for bar_user", + }, + } + + actual, err := policies.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondPolicy, *actual) +} + +func TestCreatePolicyTypeLengthCheck(t *testing.T) { + // strGenerator generates a string of fixed length filled with '0' + strGenerator := func(length int) string { + return fmt.Sprintf(fmt.Sprintf("%%0%dd", length), 0) + } + + type test struct { + length int + wantErr bool + } + + tests := []test{ + {100, false}, + {255, false}, + {256, true}, + {300, true}, + } + + createOpts := policies.CreateOpts{ + Blob: []byte("{'bar_user': 'role:network-user'}"), + } + + for _, _test := range tests { + createOpts.Type = strGenerator(_test.length) + if len(createOpts.Type) != _test.length { + t.Fatal("function strGenerator does not work properly") + } + + _, err := createOpts.ToPolicyCreateMap() + if !_test.wantErr { + th.AssertNoErr(t, err) + } else { + switch _t := err.(type) { + case nil: + t.Fatal("error expected but got a nil") + case policies.StringFieldLengthExceedsLimit: + default: + t.Fatalf("unexpected error type: [%T]", _t) + } + } + } +} diff --git a/openstack/identity/v3/policies/urls.go b/openstack/identity/v3/policies/urls.go index 93a183bed4..6ba76fbffc 100644 --- a/openstack/identity/v3/policies/urls.go +++ b/openstack/identity/v3/policies/urls.go @@ -7,3 +7,7 @@ const policyPath = "policies" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(policyPath) } + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(policyPath) +} From b75b88f65d8f40089b540410728544e997cd410e Mon Sep 17 00:00:00 2001 From: "zhang.zujian" Date: Thu, 10 May 2018 18:29:24 +0800 Subject: [PATCH 0355/2296] Identity V3: delete policy --- .../openstack/identity/v3/policies_test.go | 24 +++++++++++++++++++ openstack/identity/v3/policies/doc.go | 8 +++++++ openstack/identity/v3/policies/requests.go | 6 +++++ openstack/identity/v3/policies/results.go | 6 +++++ .../identity/v3/policies/testing/fixtures.go | 11 +++++++++ .../v3/policies/testing/requests_test.go | 9 +++++++ openstack/identity/v3/policies/urls.go | 4 ++++ 7 files changed, 68 insertions(+) diff --git a/acceptance/openstack/identity/v3/policies_test.go b/acceptance/openstack/identity/v3/policies_test.go index e78f6c5697..0d7f06e157 100644 --- a/acceptance/openstack/identity/v3/policies_test.go +++ b/acceptance/openstack/identity/v3/policies_test.go @@ -52,3 +52,27 @@ func TestPoliciesCreate(t *testing.T) { th.AssertEquals(t, policy.Blob, string(createOpts.Blob)) th.AssertEquals(t, policy.Extra["description"], createOpts.Extra["description"]) } + +func TestPoliciesDelete(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + createOpts := policies.CreateOpts{ + Type: "application/json", + Blob: []byte("{'foobar_user': 'role:compute-user'}"), + Extra: map[string]interface{}{ + "description": "policy for foobar_user", + }, + } + + policy, err := policies.Create(client, &createOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, policy) + tools.PrintResource(t, policy.Extra) + + err = policies.Delete(client, policy.ID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/identity/v3/policies/doc.go b/openstack/identity/v3/policies/doc.go index 76ff76ab90..67b83cb6dd 100644 --- a/openstack/identity/v3/policies/doc.go +++ b/openstack/identity/v3/policies/doc.go @@ -36,5 +36,13 @@ Example to Create a Policy if err != nil { panic(err) } + +Example to Delete a Policy + + policyID := "0fe36e73809d46aeae6705c39077b1b3" + err := policies.Delete(identityClient, policyID).ExtractErr() + if err != nil { + panic(err) + } */ package policies diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index 7449131f23..28ed65ffa2 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -99,3 +99,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// Delete deletes a policy. +func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, policyID), nil) + return +} diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go index 84ef1fc7c2..71ca714f4f 100644 --- a/openstack/identity/v3/policies/results.go +++ b/openstack/identity/v3/policies/results.go @@ -67,6 +67,12 @@ type CreateResult struct { policyResult } +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // PolicyPage is a single page of Policy results. type PolicyPage struct { pagination.LinkedPageBase diff --git a/openstack/identity/v3/policies/testing/fixtures.go b/openstack/identity/v3/policies/testing/fixtures.go index 5038c5a0f4..993122721f 100644 --- a/openstack/identity/v3/policies/testing/fixtures.go +++ b/openstack/identity/v3/policies/testing/fixtures.go @@ -148,3 +148,14 @@ func HandleCreatePolicySuccessfully(t *testing.T) { fmt.Fprintf(w, GetOutput) }) } + +// HandleDeletePolicySuccessfully creates an HTTP handler at `/policies` on the +// test handler mux that tests policy deletion. +func HandleDeletePolicySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/policies/9fe1d3", 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/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go index 45359f7588..e486903015 100644 --- a/openstack/identity/v3/policies/testing/requests_test.go +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -117,3 +117,12 @@ func TestCreatePolicyTypeLengthCheck(t *testing.T) { } } } + +func TestDeletePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeletePolicySuccessfully(t) + + res := policies.Delete(client.ServiceClient(), "9fe1d3") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/policies/urls.go b/openstack/identity/v3/policies/urls.go index 6ba76fbffc..5de6453d6c 100644 --- a/openstack/identity/v3/policies/urls.go +++ b/openstack/identity/v3/policies/urls.go @@ -11,3 +11,7 @@ func listURL(client *gophercloud.ServiceClient) string { func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(policyPath) } + +func deleteURL(client *gophercloud.ServiceClient, policyID string) string { + return client.ServiceURL(policyPath, policyID) +} From 31405f70e8aafdca5f9b40d0c85281c0fdf9544f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Fri, 11 May 2018 09:57:25 +0800 Subject: [PATCH 0356/2296] Add Field *Filters* into Struct roles.ListOpts (#990) * add field *Filters* into struct roles.ListOpts * fix acceptance test * fix acceptance test * fix acceptance test --- .../openstack/identity/v3/roles_test.go | 66 ++++++++++++++++++- openstack/identity/v3/roles/errors.go | 17 +++++ openstack/identity/v3/roles/requests.go | 22 +++++++ .../v3/roles/testing/requests_test.go | 32 +++++++++ 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 openstack/identity/v3/roles/errors.go diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index f442439086..52867f66f6 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -56,8 +56,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", }, @@ -71,6 +70,69 @@ func TestRolesCRUD(t *testing.T) { tools.PrintResource(t, role) tools.PrintResource(t, role.Extra) + var listOpts roles.ListOpts + allPages, err := roles.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allRoles, err := roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, r := range allRoles { + tools.PrintResource(t, r) + tools.PrintResource(t, r.Extra) + + if r.Name == role.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + + listOpts.Filters = map[string]string{ + "name__contains": "TEST", + } + + allPages, err = roles.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allRoles, err = roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + + found = false + for _, r := range allRoles { + tools.PrintResource(t, r) + tools.PrintResource(t, r.Extra) + + if r.Name == role.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + + listOpts.Filters = map[string]string{ + "name__contains": "foo", + } + + allPages, err = roles.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allRoles, err = roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + + found = false + for _, r := range allRoles { + tools.PrintResource(t, r) + tools.PrintResource(t, r.Extra) + + if r.Name == role.Name { + found = true + } + } + + th.AssertEquals(t, found, false) + updateOpts := roles.UpdateOpts{ Extra: map[string]interface{}{ "description": "updated test role description", diff --git a/openstack/identity/v3/roles/errors.go b/openstack/identity/v3/roles/errors.go new file mode 100644 index 0000000000..b60d7d18b6 --- /dev/null +++ b/openstack/identity/v3/roles/errors.go @@ -0,0 +1,17 @@ +package roles + +import "fmt" + +// InvalidListFilter is returned by the ToUserListQuery method when validation of +// a filter does not pass +type InvalidListFilter struct { + FilterName string +} + +func (e InvalidListFilter) Error() string { + s := fmt.Sprintf( + "Invalid filter name [%s]: it must be in format of NAME__COMPARATOR", + e.FilterName, + ) + return s +} diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 86c68c66ab..573db9ea20 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -1,6 +1,9 @@ package roles import ( + "net/url" + "strings" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -18,11 +21,30 @@ type ListOpts struct { // Name filters the response by role name. Name string `q:"name"` + + // Filters filters the response by custom filters such as + // 'name__contains=foo' + Filters map[string]string `q:"-"` } // ToRoleListQuery formats a ListOpts into a query string. func (opts ListOpts) ToRoleListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + for k, v := range opts.Filters { + i := strings.Index(k, "__") + if i > 0 && i < len(k)-2 { + params.Add(k, v) + } else { + return "", InvalidListFilter{FilterName: k} + } + } + + q = &url.URL{RawQuery: params.Encode()} return q.String(), err } diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index ea8b19a7bb..8372c4ff88 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -42,6 +42,38 @@ func TestListRolesAllPages(t *testing.T) { th.AssertEquals(t, ExpectedRolesSlice[1].Extra["description"], "read-only support role") } +func TestListUsersFiltersCheck(t *testing.T) { + type test struct { + filterName string + wantErr bool + } + tests := []test{ + {"foo__contains", false}, + {"foo", true}, + {"foo_contains", true}, + {"foo__", true}, + {"__foo", true}, + } + + var listOpts roles.ListOpts + for _, _test := range tests { + listOpts.Filters = map[string]string{_test.filterName: "bar"} + _, err := listOpts.ToRoleListQuery() + + if !_test.wantErr { + th.AssertNoErr(t, err) + } else { + switch _t := err.(type) { + case nil: + t.Fatal("error expected but got a nil") + case roles.InvalidListFilter: + default: + t.Fatalf("unexpected error type: [%T]", _t) + } + } + } +} + func TestGetRole(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 89767d0eff7dd27c031cd5a2b57fdbaceef7ee1c Mon Sep 17 00:00:00 2001 From: "zhang.zujian" Date: Thu, 10 May 2018 18:43:32 +0800 Subject: [PATCH 0357/2296] Identity V3: get policy --- .../openstack/identity/v3/policies_test.go | 27 +++++++++++++++++++ openstack/identity/v3/policies/doc.go | 10 +++++++ openstack/identity/v3/policies/requests.go | 6 +++++ openstack/identity/v3/policies/results.go | 6 +++++ .../identity/v3/policies/testing/fixtures.go | 16 +++++++++++ .../v3/policies/testing/requests_test.go | 12 +++++++++ openstack/identity/v3/policies/urls.go | 4 +++ 7 files changed, 81 insertions(+) diff --git a/acceptance/openstack/identity/v3/policies_test.go b/acceptance/openstack/identity/v3/policies_test.go index 0d7f06e157..4a51da651b 100644 --- a/acceptance/openstack/identity/v3/policies_test.go +++ b/acceptance/openstack/identity/v3/policies_test.go @@ -53,6 +53,33 @@ func TestPoliciesCreate(t *testing.T) { th.AssertEquals(t, policy.Extra["description"], createOpts.Extra["description"]) } +func TestPoliciesGet(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + createOpts := policies.CreateOpts{ + Type: "application/json", + Blob: []byte("{'foobar_user': 'role:compute-user'}"), + Extra: map[string]interface{}{ + "description": "policy for foobar_user", + }, + } + + policy, err := policies.Create(client, &createOpts).Extract() + th.AssertNoErr(t, err) + + defer policies.Delete(client, policy.ID) + + tools.PrintResource(t, policy) + tools.PrintResource(t, policy.Extra) + + gotPolicy, err := policies.Get(client, policy.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, policy, gotPolicy) +} + func TestPoliciesDelete(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/identity/v3/policies/doc.go b/openstack/identity/v3/policies/doc.go index 67b83cb6dd..c2b7b82eca 100644 --- a/openstack/identity/v3/policies/doc.go +++ b/openstack/identity/v3/policies/doc.go @@ -37,6 +37,16 @@ Example to Create a Policy panic(err) } +Example to Get a Policy + + policyID := "0fe36e73809d46aeae6705c39077b1b3" + policy, err := policies.Get(identityClient, policyID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", policy) + Example to Delete a Policy policyID := "0fe36e73809d46aeae6705c39077b1b3" diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index 28ed65ffa2..c379c0570a 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -100,6 +100,12 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// Get retrieves details on a single policy, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + // Delete deletes a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, policyID), nil) diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go index 71ca714f4f..8d5ea32cb4 100644 --- a/openstack/identity/v3/policies/results.go +++ b/openstack/identity/v3/policies/results.go @@ -67,6 +67,12 @@ type CreateResult struct { policyResult } +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Policy. +type GetResult struct { + policyResult +} + // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { diff --git a/openstack/identity/v3/policies/testing/fixtures.go b/openstack/identity/v3/policies/testing/fixtures.go index 993122721f..086b44a4f1 100644 --- a/openstack/identity/v3/policies/testing/fixtures.go +++ b/openstack/identity/v3/policies/testing/fixtures.go @@ -149,6 +149,22 @@ func HandleCreatePolicySuccessfully(t *testing.T) { }) } +// HandleGetPolicySuccessfully creates an HTTP handler at `/policies` on the +// test handler mux that responds with a single policy. +func HandleGetPolicySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/policies/b49884da9d31494ea02aff38d4b4e701", + 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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetOutput) + }, + ) +} + // HandleDeletePolicySuccessfully creates an HTTP handler at `/policies` on the // test handler mux that tests policy deletion. func HandleDeletePolicySuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go index e486903015..5f75111f8d 100644 --- a/openstack/identity/v3/policies/testing/requests_test.go +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -118,6 +118,18 @@ func TestCreatePolicyTypeLengthCheck(t *testing.T) { } } +func TestGetPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetPolicySuccessfully(t) + + id := "b49884da9d31494ea02aff38d4b4e701" + actual, err := policies.Get(client.ServiceClient(), id).Extract() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondPolicy, *actual) +} + func TestDeletePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/policies/urls.go b/openstack/identity/v3/policies/urls.go index 5de6453d6c..6650c46fea 100644 --- a/openstack/identity/v3/policies/urls.go +++ b/openstack/identity/v3/policies/urls.go @@ -12,6 +12,10 @@ func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(policyPath) } +func getURL(client *gophercloud.ServiceClient, policyID string) string { + return client.ServiceURL(policyPath, policyID) +} + func deleteURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(policyPath, policyID) } From 13eda87f0940a5a4627d1ffd7b16efb4394107f3 Mon Sep 17 00:00:00 2001 From: "zhang.zujian" Date: Fri, 11 May 2018 13:20:08 +0800 Subject: [PATCH 0358/2296] change id to policyID --- openstack/identity/v3/policies/requests.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index c379c0570a..846f7c513c 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -101,8 +101,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get retrieves details on a single policy, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) +func Get(client *gophercloud.ServiceClient, policyID string) (r GetResult) { + _, r.Err = client.Get(getURL(client, policyID), &r.Body, nil) return } From 86621fc7f55fde56303d29e49517a160f132714a Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Fri, 11 May 2018 18:38:14 -0700 Subject: [PATCH 0359/2296] Senlin: Profiles List (#888) * senlin-profiles-list * Change Spec version type from string to interface{} because senlin is returning int for profiles. But policies spec's version is string. Need to file a bug report. Changed from ListDatail() to List() * Added handling of float,int,string for Spec Version Fixed import formatting * Changed to bool ptr for GlobalProject Implement UnmarshalJSON for Profile's Spec --- .../clustering/v1/autoscaling_test.go | 64 +++- .../v1/clusters/testing/requests_test.go | 3 +- openstack/clustering/v1/profiles/doc.go | 15 + openstack/clustering/v1/profiles/requests.go | 38 +++ openstack/clustering/v1/profiles/results.go | 52 +++ .../v1/profiles/testing/requests_test.go | 305 ++++++++++++++++++ openstack/clustering/v1/profiles/urls.go | 4 + 7 files changed, 465 insertions(+), 16 deletions(-) diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index 22cfce0ea1..b2621b1a8d 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -19,6 +20,7 @@ func TestAutoScaling(t *testing.T) { testName = tools.RandomString("TESTACC-", 8) profileCreate(t) profileGet(t) + profileList(t) clusterCreate(t) } @@ -72,6 +74,54 @@ func profileCreate(t *testing.T) { th.AssertEquals(t, "1.0", profile.Spec.Version) } +func profileGet(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + profileName := testName + profile, err := profiles.Get(client, profileName).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, profileName, profile.Name) + + tools.PrintResource(t, profile) +} + +func profileList(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + testProfileFound := false + profiles.List(client, profiles.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + allProfiles, err := profiles.ExtractProfiles(page) + if err != nil { + t.Fatalf("Error extracting page of profiles: %v", err) + } + + for _, profile := range allProfiles { + tools.PrintResource(t, profile) + if profile.Name == testName { + testProfileFound = true + break + } + } + + empty, err := page.IsEmpty() + + th.AssertNoErr(t, err) + + // Expect the page IS NOT empty + th.AssertEquals(t, false, empty) + + return true, nil + }) + + th.AssertEquals(t, true, testProfileFound) +} + func clusterCreate(t *testing.T) { client, err := clients.NewClusteringV1Client() if err != nil { @@ -131,17 +181,3 @@ func clusterCreate(t *testing.T) { th.CheckDeepEquals(t, optsCluster.Metadata, cluster.Metadata) th.CheckDeepEquals(t, optsCluster.Config, cluster.Config) } - -func profileGet(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - profileName := testName - profile, err := profiles.Get(client, profileName).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, profileName, profile.Name) - - tools.PrintResource(t, profile) -} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 2ae6f38086..517fb7203e 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -3,11 +3,10 @@ package testing import ( "fmt" "net/http" + "strings" "testing" "time" - "strings" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index 2778eae140..d72df047c5 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -38,5 +38,20 @@ Example to Get profile fmt.Print("profile", profile) + +Example to List profiles + + profiles.List(serviceClient, profiles.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + allProfiles, err := profiles.ExtractProfiles(page) + if err != nil { + panic(err) + } + + for _, profile := range allProfiles { + fmt.Printf("%+v\n", profile) + } + return true, nil + }) + */ package profiles diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index b29a2ce122..aa6b41a6bd 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder for options used for creating a profile. @@ -52,3 +53,40 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { } return } + +// ListOptsBuilder Builder. +type ListOptsBuilder interface { + ToProfileListQuery() (string, error) +} + +// ListOpts params +type ListOpts struct { + GlobalProject *bool `q:"global_project"` + Limit int `q:"limit"` + Marker string `q:"marker"` + Name string `q:"name"` + Sort string `q:"sort"` + Type string `q:"type"` +} + +// ToProfileListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToProfileListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of profiles. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToProfileListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ProfilePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index 1661271724..1875964524 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -4,9 +4,12 @@ import ( "encoding/json" "fmt" "reflect" + "strconv" + "strings" "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // commonResult is the response of a base result. @@ -33,6 +36,15 @@ func (r commonResult) Extract() (*Profile, error) { return s.Profile, err } +// ExtractProfiles provides access to the list of profiles in a page from the List operation. +func ExtractProfiles(r pagination.Page) ([]Profile, error) { + var s struct { + Profiles []Profile `json:"profiles"` + } + err := (r.(ProfilePage)).ExtractInto(&s) + return s.Profiles, err +} + type Spec struct { Type string `json:"type"` Version string `json:"version"` @@ -53,6 +65,46 @@ type Profile struct { User string `json:"user"` } +type ProfilePage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a ProfilePage contains any results. +func (page ProfilePage) IsEmpty() (bool, error) { + profiles, err := ExtractProfiles(page) + return len(profiles) == 0, err +} + +func (r *Spec) UnmarshalJSON(b []byte) error { + type tmp Spec + var s struct { + tmp + Version interface{} `json:"version"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Spec(s.tmp) + + switch s.Version.(type) { + case nil: + r.Version = "" + case float32, float64, int, int32, int64: + r.Version = strconv.FormatFloat(s.Version.(float64), 'f', -1, 64) + if !strings.Contains(r.Version, ".") { + r.Version = strconv.FormatFloat(s.Version.(float64), 'f', 1, 64) + } + case string: + r.Version = s.Version.(string) + default: + return fmt.Errorf("Invalid type for Spec Version. type=%v", reflect.TypeOf(s.Version)) + } + + return nil +} + func (r *Profile) UnmarshalJSON(b []byte) error { type tmp Profile var s struct { diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go index cc6b7b644e..a12552aaa4 100644 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -384,3 +385,307 @@ func TestGetProfileInvalidUpdatedAtTime(t *testing.T) { _, err := profiles.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() th.AssertEquals(t, false, err == nil) } +func TestListProfiles(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles", 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, ` + { + "profiles": [ + { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": 1.0 + }, + "type": "os.nova.server-1.0", + "updated_at": "2016-01-03T17:22:23Z", + "user": "5e5bf8027826429c96af157f68dc9072" + }, + { + "created_at": null, + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": 1.0 + }, + "type": "os.nova.server-1.0", + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + }, + { + "created_at": "", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "", + "user": "5e5bf8027826429c96af157f68dc9072" + } + ] + }`) + }) + + count := 0 + profiles.List(fake.ServiceClient(), profiles.ListOpts{GlobalProject: new(bool)}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := profiles.ExtractProfiles(page) + if err != nil { + t.Errorf("Failed to extract profiles: %v", err) + return false, err + } + + createdAt, _ := time.Parse(time.RFC3339, "2016-01-03T16:22:23Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-01-03T17:22:23Z") + + expected := []profiles.Profile{ + { + CreatedAt: createdAt, + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: updatedAt, + User: "5e5bf8027826429c96af157f68dc9072", + }, + { + CreatedAt: time.Time{}, + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: time.Time{}, + User: "5e5bf8027826429c96af157f68dc9072", + }, + { + CreatedAt: time.Time{}, + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: time.Time{}, + User: "5e5bf8027826429c96af157f68dc9072", + }, + } + + th.AssertDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page of profiles, got %d pages instead", count) + } +} + +func TestListProfilesInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles", 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, ` + { + "profiles": [ + { + "created_at": 123456789.0, + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": 1.0 + }, + "type": "os.nova.server-1.0", + "updated_at": 123456789.0, + "user": "5e5bf8027826429c96af157f68dc9072" + } + ] + }`) + }) + + count := 0 + err := profiles.List(fake.ServiceClient(), profiles.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + return true, nil + }) + + th.AssertEquals(t, false, err == nil) + if count != 0 { + t.Errorf("Expected 0 page of profiles, got %d pages instead", count) + } +} + +func TestListProfilesInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles", 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, ` + { + "profiles": [ + { + "created_at": "invalid", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": 1.0 + }, + "type": "os.nova.server-1.0", + "updated_at": "invalid", + "user": "5e5bf8027826429c96af157f68dc9072" + } + ] + }`) + }) + + count := 0 + err := profiles.List(fake.ServiceClient(), profiles.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + return true, nil + }) + + th.AssertEquals(t, false, err == nil) + if count != 0 { + t.Errorf("Expected 0 page of profiles, got %d pages instead", count) + } +} diff --git a/openstack/clustering/v1/profiles/urls.go b/openstack/clustering/v1/profiles/urls.go index d7cdaa3612..02ff7c7242 100644 --- a/openstack/clustering/v1/profiles/urls.go +++ b/openstack/clustering/v1/profiles/urls.go @@ -20,3 +20,7 @@ func idURL(client *gophercloud.ServiceClient, id string) string { func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From b1702f265543229735d35367a59d31da79aa8c88 Mon Sep 17 00:00:00 2001 From: "zhang.zujian" Date: Thu, 10 May 2018 19:15:47 +0800 Subject: [PATCH 0360/2296] Identity V3: policy update --- .../openstack/identity/v3/policies_test.go | 56 +++++++--------- openstack/identity/v3/policies/doc.go | 19 ++++++ openstack/identity/v3/policies/requests.go | 66 ++++++++++++++++++- openstack/identity/v3/policies/results.go | 6 ++ .../identity/v3/policies/testing/fixtures.go | 52 +++++++++++++++ .../v3/policies/testing/requests_test.go | 57 ++++++++++++++++ openstack/identity/v3/policies/urls.go | 4 ++ 7 files changed, 225 insertions(+), 35 deletions(-) diff --git a/acceptance/openstack/identity/v3/policies_test.go b/acceptance/openstack/identity/v3/policies_test.go index 4a51da651b..61ccb2a3f1 100644 --- a/acceptance/openstack/identity/v3/policies_test.go +++ b/acceptance/openstack/identity/v3/policies_test.go @@ -28,7 +28,7 @@ func TestPoliciesList(t *testing.T) { } } -func TestPoliciesCreate(t *testing.T) { +func TestPoliciesCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewIdentityV3Client() @@ -51,54 +51,46 @@ func TestPoliciesCreate(t *testing.T) { th.AssertEquals(t, policy.Type, createOpts.Type) th.AssertEquals(t, policy.Blob, string(createOpts.Blob)) th.AssertEquals(t, policy.Extra["description"], createOpts.Extra["description"]) -} - -func TestPoliciesGet(t *testing.T) { - clients.RequireAdmin(t) - client, err := clients.NewIdentityV3Client() + allPages, err := policies.List(client, nil).AllPages() th.AssertNoErr(t, err) - createOpts := policies.CreateOpts{ - Type: "application/json", - Blob: []byte("{'foobar_user': 'role:compute-user'}"), - Extra: map[string]interface{}{ - "description": "policy for foobar_user", - }, - } - - policy, err := policies.Create(client, &createOpts).Extract() + allPolicies, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) - defer policies.Delete(client, policy.ID) + var found bool + for _, p := range allPolicies { + tools.PrintResource(t, p) + tools.PrintResource(t, p.Extra) - tools.PrintResource(t, policy) - tools.PrintResource(t, policy.Extra) + if p.ID == policy.ID { + found = true + } + } + + th.AssertEquals(t, true, found) gotPolicy, err := policies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, policy, gotPolicy) -} - -func TestPoliciesDelete(t *testing.T) { - clients.RequireAdmin(t) - - client, err := clients.NewIdentityV3Client() - th.AssertNoErr(t, err) - createOpts := policies.CreateOpts{ - Type: "application/json", - Blob: []byte("{'foobar_user': 'role:compute-user'}"), + updateOpts := policies.UpdateOpts{ + Type: "text/plain", + Blob: []byte("'foobar_user': 'role:compute-user'"), Extra: map[string]interface{}{ - "description": "policy for foobar_user", + "description": "updated policy for foobar_user", }, } - policy, err := policies.Create(client, &createOpts).Extract() + updatedPolicy, err := policies.Update(client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) - tools.PrintResource(t, policy) - tools.PrintResource(t, policy.Extra) + tools.PrintResource(t, updatedPolicy) + tools.PrintResource(t, updatedPolicy.Extra) + + th.AssertEquals(t, updatedPolicy.Type, updateOpts.Type) + th.AssertEquals(t, updatedPolicy.Blob, string(updateOpts.Blob)) + th.AssertEquals(t, updatedPolicy.Extra["description"], updateOpts.Extra["description"]) err = policies.Delete(client, policy.ID).ExtractErr() th.AssertNoErr(t, err) diff --git a/openstack/identity/v3/policies/doc.go b/openstack/identity/v3/policies/doc.go index c2b7b82eca..0bdc7a6851 100644 --- a/openstack/identity/v3/policies/doc.go +++ b/openstack/identity/v3/policies/doc.go @@ -47,6 +47,25 @@ Example to Get a Policy fmt.Printf("%+v\n", policy) +Example to Update a Policy + + policyID := "0fe36e73809d46aeae6705c39077b1b3" + + updateOpts := policies.UpdateOpts{ + Type: "application/json", + Blob: []byte("{'foobar_user': 'role:compute-user'}"), + Extra: map[string]interface{}{ + "description": "policy for foobar_user", + }, + } + + policy, err := policies.Update(identityClient, policyID, updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", policy) + Example to Delete a Policy policyID := "0fe36e73809d46aeae6705c39077b1b3" diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index 846f7c513c..cc727dab33 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -5,7 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -const createOptsTypeMaxLength = 255 +const policyTypeMaxLength = 255 // ListOptsBuilder allows extensions to add additional parameters to // the List request @@ -62,10 +62,10 @@ type CreateOpts struct { // ToPolicyCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { - if len(opts.Type) > createOptsTypeMaxLength { + if len(opts.Type) > policyTypeMaxLength { return nil, StringFieldLengthExceedsLimit{ Field: "type", - Limit: createOptsTypeMaxLength, + Limit: policyTypeMaxLength, } } @@ -106,6 +106,66 @@ func Get(client *gophercloud.ServiceClient, policyID string) (r GetResult) { return } +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToPolicyUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts provides options for updating a policy. +type UpdateOpts struct { + // Type is the MIME media type of the serialized policy blob. + Type string `json:"type,omitempty"` + + // Blob is the policy rule as a serialized blob. + Blob []byte `json:"-"` + + // Extra is free-form extra key/value pairs to describe the policy. + Extra map[string]interface{} `json:"-"` +} + +// ToPolicyUpdateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { + if len(opts.Type) > policyTypeMaxLength { + return nil, StringFieldLengthExceedsLimit{ + Field: "type", + Limit: policyTypeMaxLength, + } + } + + b, err := gophercloud.BuildRequestBody(opts, "policy") + if err != nil { + return nil, err + } + + if v, ok := b["policy"].(map[string]interface{}); ok { + if len(opts.Blob) != 0 { + v["blob"] = string(opts.Blob) + } + + if opts.Extra != nil { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// Update updates an existing Role. +func Update(client *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToPolicyUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(updateURL(client, policyID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // Delete deletes a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, policyID), nil) diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go index 8d5ea32cb4..131a9f8103 100644 --- a/openstack/identity/v3/policies/results.go +++ b/openstack/identity/v3/policies/results.go @@ -73,6 +73,12 @@ type GetResult struct { policyResult } +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Policy. +type UpdateResult struct { + policyResult +} + // DeleteResult is the response from a Delete operation. Call its ExtractErr to // determine if the request succeeded or failed. type DeleteResult struct { diff --git a/openstack/identity/v3/policies/testing/fixtures.go b/openstack/identity/v3/policies/testing/fixtures.go index 086b44a4f1..24bca7e175 100644 --- a/openstack/identity/v3/policies/testing/fixtures.go +++ b/openstack/identity/v3/policies/testing/fixtures.go @@ -88,6 +88,30 @@ const CreateRequest = ` } ` +// UpdateRequest provides the input to as Update request. +const UpdateRequest = ` +{ + "policy": { + "description": "updated policy for bar_user" + } +} +` + +// UpdateOutput provides an update result. +const UpdateOutput = ` +{ + "policy": { + "type": "application/json", + "id": "b49884da9d31494ea02aff38d4b4e701", + "links": { + "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701" + }, + "blob": "{'bar_user': 'role:network-user'}", + "description": "updated policy for bar_user" + } +} +` + // FirstPolicy is the first policy in the List request. var FirstPolicy = policies.Policy{ ID: "2844b2a08be147a08ef58317d6471f1f", @@ -112,6 +136,19 @@ var SecondPolicy = policies.Policy{ }, } +// SecondPolicyUpdated is the policy in the Update request. +var SecondPolicyUpdated = policies.Policy{ + ID: "b49884da9d31494ea02aff38d4b4e701", + Blob: "{'bar_user': 'role:network-user'}", + Type: "application/json", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701", + }, + Extra: map[string]interface{}{ + "description": "updated policy for bar_user", + }, +} + // ExpectedPoliciesSlice is the slice of policies expected to be returned from ListOutput. var ExpectedPoliciesSlice = []policies.Policy{FirstPolicy, SecondPolicy} @@ -165,6 +202,21 @@ func HandleGetPolicySuccessfully(t *testing.T) { ) } +// HandleUpdatePolicySuccessfully creates an HTTP handler at `/policies` on the +// test handler mux that tests role update. +func HandleUpdatePolicySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/policies/b49884da9d31494ea02aff38d4b4e701", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, UpdateRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateOutput) + }, + ) +} + // HandleDeletePolicySuccessfully creates an HTTP handler at `/policies` on the // test handler mux that tests policy deletion. func HandleDeletePolicySuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go index 5f75111f8d..8c60ba60e5 100644 --- a/openstack/identity/v3/policies/testing/requests_test.go +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -130,6 +130,63 @@ func TestGetPolicy(t *testing.T) { th.CheckDeepEquals(t, SecondPolicy, *actual) } +func TestUpdatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdatePolicySuccessfully(t) + + updateOpts := policies.UpdateOpts{ + Extra: map[string]interface{}{ + "description": "updated policy for bar_user", + }, + } + + id := "b49884da9d31494ea02aff38d4b4e701" + actual, err := policies.Update(client.ServiceClient(), id, updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondPolicyUpdated, *actual) +} + +func TestUpdatePolicyTypeLengthCheck(t *testing.T) { + // strGenerator generates a string of fixed length filled with '0' + strGenerator := func(length int) string { + return fmt.Sprintf(fmt.Sprintf("%%0%dd", length), 0) + } + + type test struct { + length int + wantErr bool + } + + tests := []test{ + {100, false}, + {255, false}, + {256, true}, + {300, true}, + } + + var updateOpts policies.UpdateOpts + for _, _test := range tests { + updateOpts.Type = strGenerator(_test.length) + if len(updateOpts.Type) != _test.length { + t.Fatal("function strGenerator does not work properly") + } + + _, err := updateOpts.ToPolicyUpdateMap() + if !_test.wantErr { + th.AssertNoErr(t, err) + } else { + switch _t := err.(type) { + case nil: + t.Fatal("error expected but got a nil") + case policies.StringFieldLengthExceedsLimit: + default: + t.Fatalf("unexpected error type: [%T]", _t) + } + } + } +} + func TestDeletePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/policies/urls.go b/openstack/identity/v3/policies/urls.go index 6650c46fea..df0d183b43 100644 --- a/openstack/identity/v3/policies/urls.go +++ b/openstack/identity/v3/policies/urls.go @@ -16,6 +16,10 @@ func getURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(policyPath, policyID) } +func updateURL(client *gophercloud.ServiceClient, policyID string) string { + return client.ServiceURL(policyPath, policyID) +} + func deleteURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(policyPath, policyID) } From 0d2e011178b8d494fa82ba7423e4e784690f2af4 Mon Sep 17 00:00:00 2001 From: Yongfeng Du Date: Wed, 9 May 2018 13:20:42 +0800 Subject: [PATCH 0361/2296] Acceptance test job definition cleanup Remove the unused theopenlab/gophercloud branches. Put env variables setup for accp test into script/stackenv. Move the test case enable/disable to script/acceptancetest. --- .../gophercloud-acceptance-test/run.yaml | 42 +------------------ .zuul/playbooks/gophercloud-unittest/run.yaml | 8 ---- script/acceptancetest | 16 ++++++- script/stackenv | 25 +++++++++++ 4 files changed, 42 insertions(+), 49 deletions(-) create mode 100644 script/stackenv diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index fedb69d10b..2578351942 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -15,47 +15,9 @@ set -e set -o pipefail set -x - - # Prep the testing environment by creating the required testing resources and environment variables - pushd /opt/stack/new/devstack - 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 - _NETWORK_ID=$(openstack network show private -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 ' ') - echo export OS_IMAGE_NAME="$_IMAGE_NAME" >> openrc - echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc - echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc - echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc - 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_SHARE_NETWORK_ID=foobar >> openrc - echo export OS_DOMAIN_ID=default >> openrc - source openrc admin admin - popd - + go get ./... || true - # Temporally enable all tests with openlab repo and only enable part tests with official repo - if [[ '{{ zuul.project.src_dir }}' =~ "theopenlab" ]];then - go test -v -tags "fixtures acceptance" ./acceptance/openstack/... 2>&1 | tee $TEST_RESULTS_TXT - else - { - # Only enable these successful test cases - go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v3/ - go test -v -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ - go test -v -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ - go test -v -tags "fixtures acceptance" -run "SecGroup|Flavor" ./acceptance/openstack/compute/v2/ - go test -v -tags "fixtures acceptance" ./acceptance/openstack/container/v1/ - # To enable more after the fix of https://github.com/gophercloud/gophercloud/issues/608 - # go test -v -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ - # go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v2/ - # go test -v -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ - } 2>&1 | tee $TEST_RESULTS_TXT - fi + ./script/acceptancetest -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash chdir: '{{ zuul.project.src_dir }}' environment: '{{ golang_env }}' diff --git a/.zuul/playbooks/gophercloud-unittest/run.yaml b/.zuul/playbooks/gophercloud-unittest/run.yaml index cd85cac1ce..1af36aca13 100644 --- a/.zuul/playbooks/gophercloud-unittest/run.yaml +++ b/.zuul/playbooks/gophercloud-unittest/run.yaml @@ -6,14 +6,6 @@ set -e set -o pipefail set -x - - if [[ ! -d $GOPATH/src/github.com/gophercloud/gophercloud/ && -d $GOPATH/src/github.com/theopenlab/gophercloud ]]; then - echo "Warning: this is a temporary workaround because this job is not triggered from official git repo." - mkdir -p $GOPATH/src/github.com/gophercloud/ - cp -r $GOPATH/src/github.com/theopenlab/gophercloud $GOPATH/src/github.com/gophercloud/ - cd $GOPATH/src/github.com/gophercloud/gophercloud - fi - go get ./... || true ./script/unittest -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash diff --git a/script/acceptancetest b/script/acceptancetest index 9debd48b62..f45165debf 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -1,5 +1,19 @@ #!/bin/bash # + +source `dirname $0`/stackenv + # Run the acceptance tests. -exec go test -p=1 github.com/gophercloud/gophercloud/acceptance/... $@ +# exec go test -p=1 github.com/gophercloud/gophercloud/acceptance/... $@ + +# Only enable these successful test cases +go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v3/ +go test -v -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ +go test -v -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ +go test -v -tags "fixtures acceptance" -run "SecGroup|Flavor" ./acceptance/openstack/compute/v2/ +go test -v -tags "fixtures acceptance" ./acceptance/openstack/container/v1/ +# To enable more after the fix of https://github.com/gophercloud/gophercloud/issues/818 +# go test -v -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ +# go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v2/ +# go test -v -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ diff --git a/script/stackenv b/script/stackenv new file mode 100644 index 0000000000..6a742a32f1 --- /dev/null +++ b/script/stackenv @@ -0,0 +1,25 @@ +# Prep the testing environment by creating the required testing resources and +# environment variables. This env is for theopenlab CI jobs, you might need +# to modify this according to your setup + +pushd /opt/stack/new/devstack +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 +_NETWORK_ID=$(openstack network show private -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 ' ') +echo export OS_IMAGE_NAME="$_IMAGE_NAME" >> openrc +echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc +echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc +echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc +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_SHARE_NETWORK_ID=foobar >> openrc +echo export OS_DOMAIN_ID=default >> openrc +source openrc admin admin +popd + From 2c22679668bc52d348e59fb957b1b5c4c950e9d7 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0362/2296] senlin-clusters-get --- .../clustering/v1/autoscaling_test.go | 15 + openstack/clustering/v1/clusters/doc.go | 9 + openstack/clustering/v1/clusters/requests.go | 11 + openstack/clustering/v1/clusters/results.go | 5 + .../v1/clusters/testing/requests_test.go | 261 ++++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 8 + 6 files changed, 309 insertions(+) diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index b2621b1a8d..b1d3355d04 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -22,6 +22,7 @@ func TestAutoScaling(t *testing.T) { profileGet(t) profileList(t) clusterCreate(t) + clusterGet(t) } func profileCreate(t *testing.T) { @@ -181,3 +182,17 @@ func clusterCreate(t *testing.T) { th.CheckDeepEquals(t, optsCluster.Metadata, cluster.Metadata) th.CheckDeepEquals(t, optsCluster.Config, cluster.Config) } + +func clusterGet(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + clusterName := testName + cluster, err := clusters.Get(client, clusterName).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, clusterName, cluster.Name) + + tools.PrintResource(t, cluster) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index bd19dd4ccd..00e214573a 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -15,5 +15,14 @@ Example to Create a cluster panic(err) } +Example to Get Clusters + + clusterName := "cluster123" + cluster, err := clusters.Get(serviceClient, clusterName).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", cluster) + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 15cc34c479..f95bea8c2c 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -46,3 +46,14 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } + +// Get retrieves details of a single cluster. Use Extract to convert its +// result into a Cluster. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + var result *http.Response + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 5ebf32024a..b5a563c5e6 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -19,6 +19,11 @@ type CreateResult struct { commonResult } +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 517fb7203e..05159a2c3d 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -412,3 +412,264 @@ func TestCreateClusterMetadata(t *testing.T) { _, err := clusters.Create(fake.ServiceClient(), opts).Extract() th.AssertEquals(t, false, err == nil) } + +func TestGetCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ` + { + "cluster": { + "config": {}, + "created_at": "2015-02-10T14:26:14Z", + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "2015-02-10T15:26:14Z", + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "2015-02-10T16:26:14Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + createdAt, _ := time.Parse(time.RFC3339, "2015-02-10T14:26:14Z") + initAt, _ := time.Parse(time.RFC3339, "2015-02-10T15:26:14Z") + updatedAt, _ := time.Parse(time.RFC3339, "2015-02-10T16:26:14Z") + expected := clusters.Cluster{ + Config: map[string]interface{}{}, + CreatedAt: createdAt, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 4, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: initAt, + MaxSize: -1, + Metadata: map[string]interface{}{}, + MinSize: 0, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: updatedAt, + User: "5e5bf8027826429c96af157f68dc9072", + } + + actual, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + if err != nil { + t.Errorf("Failed Get cluster. %v", err) + } else { + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestGetClusterEmptyTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ` + { + "cluster": { + "config": {}, + "created_at": null, + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": null, + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + expected := clusters.Cluster{ + Config: map[string]interface{}{}, + CreatedAt: time.Time{}, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 4, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: time.Time{}, + MaxSize: -1, + Metadata: map[string]interface{}{}, + MinSize: 0, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: time.Time{}, + User: "5e5bf8027826429c96af157f68dc9072", + } + + actual, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + if err != nil { + t.Errorf("Failed Get cluster. %v", err) + } else { + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestGetClusterInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ` + { + "cluster": { + "config": {}, + "created_at": 123456789.0, + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": 123456789.0, + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": 123456789.0, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + _, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestGetClusterInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ` + { + "cluster": { + "config": {}, + "created_at": "invalid", + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "invalid", + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "invalid", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + _, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 508b76ad5e..756eb8897c 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -9,6 +9,14 @@ func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } +func idURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id) +} + func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func getURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 42d8196dfea62fa06e527ca3b1fbb461cac0b280 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Tue, 29 May 2018 18:51:22 -0700 Subject: [PATCH 0363/2296] Senlin: Cluster List (#889) * senlin-clusters-list * Changed GlobalProject from string to *bool --- .../clustering/v1/autoscaling_test.go | 33 +++ openstack/clustering/v1/clusters/doc.go | 20 ++ openstack/clustering/v1/clusters/requests.go | 38 ++++ openstack/clustering/v1/clusters/results.go | 20 ++ .../v1/clusters/testing/requests_test.go | 214 ++++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 + 6 files changed, 329 insertions(+) diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index b1d3355d04..568c414aa6 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -23,6 +23,7 @@ func TestAutoScaling(t *testing.T) { profileList(t) clusterCreate(t) clusterGet(t) + clusterList(t) } func profileCreate(t *testing.T) { @@ -196,3 +197,35 @@ func clusterGet(t *testing.T) { tools.PrintResource(t, cluster) } + +func clusterList(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + testClusterFound := false + clusters.List(client, clusters.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + allClusters, err := clusters.ExtractClusters(page) + if err != nil { + t.Fatalf("Error extracting page of clusters: %v", err) + } + + for _, cluster := range allClusters { + if cluster.Name == testName { + testClusterFound = true + } + } + + empty, err := page.IsEmpty() + + th.AssertNoErr(t, err) + + // Expect the page IS NOT empty + th.AssertEquals(t, false, empty) + + return true, nil + }) + + th.AssertEquals(t, true, testClusterFound) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 00e214573a..3008e07815 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -24,5 +24,25 @@ Example to Get Clusters } fmt.Printf("%+v\n", cluster) +Example to List Clusters + + listOpts := clusters.ListOpts{ + Name: "testcluster", + } + + allPages, err := clusters.ListDetail(serviceClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allClusters, err := clusters.ExtractClusters(allPages) + if err != nil { + panic(err) + } + + for _, cluster := range allClusters { + fmt.Printf("%+v\n", cluster) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index f95bea8c2c..cb9abc2165 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder Builder. @@ -57,3 +58,40 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { } return } + +// ListOptsBuilder Builder. +type ListOptsBuilder interface { + ToClusterListQuery() (string, error) +} + +// ListOpts params +type ListOpts struct { + Limit int `q:"limit"` + Marker string `q:"marker"` + Sort string `q:"sort"` + GlobalProject *bool `q:"global_project"` + Name string `q:"name,omitempty"` + Status string `q:"status,omitempty"` +} + +// ToClusterListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToClusterListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of clusters. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToClusterListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ClusterPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index b5a563c5e6..345e45c013 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // commonResult is the response of a base result. @@ -61,6 +62,25 @@ func (r commonResult) Extract() (*Cluster, error) { return s.Cluster, nil } +// ClusterPage contains a single page of all clusters from a List call. +type ClusterPage struct { + pagination.LinkedPageBase +} + +func (page ClusterPage) IsEmpty() (bool, error) { + clusters, err := ExtractClusters(page) + return len(clusters) == 0, err +} + +// ExtractCluster provides access to the list of clusters in a page acquired from the List operation. +func ExtractClusters(r pagination.Page) ([]Cluster, error) { + var s struct { + Clusters []Cluster `json:"clusters"` + } + err := (r.(ClusterPage)).ExtractInto(&s) + return s.Clusters, err +} + func (r *Cluster) UnmarshalJSON(b []byte) error { type tmp Cluster var s struct { diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 05159a2c3d..4d6e9b1147 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -7,7 +7,9 @@ import ( "testing" "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -673,3 +675,215 @@ func TestGetClusterInvalidTimeString(t *testing.T) { _, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertEquals(t, false, err == nil) } + +func TestListClusters(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters", 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, ` + { + "clusters": [ + { + "config": {}, + "created_at": "2015-02-10T14:26:14Z", + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "2015-02-10T15:26:14Z", + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "2015-02-10T16:26:14Z", + "user": "5e5bf8027826429c96af157f68dc9072" + }, + { + "config": {}, + "created_at": "", + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "", + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "", + "user": "5e5bf8027826429c96af157f68dc9072" + }, + { + "config": {}, + "created_at": null, + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": null, + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } + ] + }`) + }) + + count := 0 + + clusters.List(fake.ServiceClient(), clusters.ListOpts{GlobalProject: new(bool)}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := clusters.ExtractClusters(page) + if err != nil { + t.Errorf("Failed to extract clusters: %v", err) + return false, err + } + + createdAt, _ := time.Parse(gophercloud.RFC3339Milli, "2015-02-10T14:26:14Z") + initAt, _ := time.Parse(gophercloud.RFC3339Milli, "2015-02-10T15:26:14Z") + updatedAt, _ := time.Parse(gophercloud.RFC3339Milli, "2015-02-10T16:26:14Z") + expected := []clusters.Cluster{ + { + Config: map[string]interface{}{}, + CreatedAt: createdAt, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 4, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: initAt, + MaxSize: -1, + Metadata: map[string]interface{}{}, + MinSize: 0, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: updatedAt, + User: "5e5bf8027826429c96af157f68dc9072", + }, + { + Config: map[string]interface{}{}, + CreatedAt: time.Time{}, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 4, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: time.Time{}, + MaxSize: -1, + Metadata: map[string]interface{}{}, + MinSize: 0, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: time.Time{}, + User: "5e5bf8027826429c96af157f68dc9072", + }, + { + Config: map[string]interface{}{}, + CreatedAt: time.Time{}, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 4, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: time.Time{}, + MaxSize: -1, + Metadata: map[string]interface{}{}, + MinSize: 0, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: time.Time{}, + User: "5e5bf8027826429c96af157f68dc9072", + }, + } + + th.AssertDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 756eb8897c..d1a3d9211a 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -20,3 +20,7 @@ func createURL(client *gophercloud.ServiceClient) string { func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 52e379551cf2b52873a826d95e59ea25996074f2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 30 May 2018 03:07:46 +0000 Subject: [PATCH 0364/2296] Networking v2: Add ability to list external networks This commit adds the ability to list external networks. It does this by way of creating an extention to the networks package's ListOpts struct. --- .../openstack/networking/v2/networks_test.go | 66 +++++++++++++++++++ .../networking/v2/extensions/external/doc.go | 9 ++- .../v2/extensions/external/requests.go | 28 ++++++++ .../extensions/external/testing/fixtures.go | 2 + .../external/testing/requests_test.go | 26 ++++++++ 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 openstack/networking/v2/extensions/external/testing/requests_test.go diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index ab4c2b1ce9..d7300a35d5 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -7,8 +7,10 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/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" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestNetworksList(t *testing.T) { @@ -39,6 +41,70 @@ func TestNetworksList(t *testing.T) { } } +func TestNetworksExternalList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatalf("Unable to fetch environment information: %s", err) + } + + type networkWithExt struct { + networks.Network + external.NetworkExternalExt + } + + var allNetworks []networkWithExt + + iTrue := true + networkListOpts := networks.ListOpts{ + ID: choices.ExternalNetworkID, + } + listOpts := external.ListOptsExt{ + ListOptsBuilder: networkListOpts, + External: &iTrue, + } + + allPages, err := networks.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list networks: %v", err) + } + + err = networks.ExtractNetworksInto(allPages, &allNetworks) + if err != nil { + t.Fatalf("Unable to extract networks: %v", err) + } + + var found bool + for _, network := range allNetworks { + if network.External == true && network.ID == choices.ExternalNetworkID { + found = true + } + } + + th.AssertEquals(t, found, true) + + iFalse := false + networkListOpts = networks.ListOpts{ + ID: choices.ExternalNetworkID, + } + listOpts = external.ListOptsExt{ + ListOptsBuilder: networkListOpts, + External: &iFalse, + } + + allPages, err = networks.List(client, listOpts).AllPages() + if err != nil { + t.Fatalf("Unable to list networks: %v", err) + } + + v, err := networks.ExtractNetworks(allPages) + th.AssertEquals(t, len(v), 0) +} + func TestNetworksCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { diff --git a/openstack/networking/v2/extensions/external/doc.go b/openstack/networking/v2/extensions/external/doc.go index b8261684e7..eda010cb0c 100644 --- a/openstack/networking/v2/extensions/external/doc.go +++ b/openstack/networking/v2/extensions/external/doc.go @@ -4,6 +4,13 @@ extension for the OpenStack Networking service. Example to List Networks with External Information + iTrue := true + networkListOpts := networks.ListOpts{} + listOpts := external.ListOptsExt{ + ListOptsBuilder: networkListOpts, + External: &iTrue, + } + type NetworkWithExternalExt struct { networks.Network external.NetworkExternalExt @@ -11,7 +18,7 @@ Example to List Networks with External Information var allNetworks []NetworkWithExternalExt - allPages, err := networks.List(networkClient, nil).AllPages() + allPages, err := networks.List(networkClient, listOpts).AllPages() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/external/requests.go b/openstack/networking/v2/extensions/external/requests.go index f28e574612..ced5efed8d 100644 --- a/openstack/networking/v2/extensions/external/requests.go +++ b/openstack/networking/v2/extensions/external/requests.go @@ -1,9 +1,37 @@ package external import ( + "net/url" + "strconv" + + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) +// ListOptsExt adds the external network options to the base ListOpts. +type ListOptsExt struct { + networks.ListOptsBuilder + External *bool `q:"router:external"` +} + +// ToNetworkListQuery adds the router:external option to the base network +// list options. +func (opts ListOptsExt) ToNetworkListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts.ListOptsBuilder) + if err != nil { + return "", err + } + + params := q.Query() + if opts.External != nil { + v := strconv.FormatBool(*opts.External) + params.Add("router:external", v) + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), err +} + // CreateOptsExt is the structure used when creating new external network // resources. It embeds networks.CreateOpts and so inherits all of its required // and optional fields, with the addition of the External field. diff --git a/openstack/networking/v2/extensions/external/testing/fixtures.go b/openstack/networking/v2/extensions/external/testing/fixtures.go index 8739236d69..f23bbea4a1 100644 --- a/openstack/networking/v2/extensions/external/testing/fixtures.go +++ b/openstack/networking/v2/extensions/external/testing/fixtures.go @@ -57,3 +57,5 @@ const UpdateResponse = ` "router:external": false } }` + +const ExpectedListOpts = "?id=d32019d3-bc6e-4319-9c1d-6722fc136a22&router%3Aexternal=true" diff --git a/openstack/networking/v2/extensions/external/testing/requests_test.go b/openstack/networking/v2/extensions/external/testing/requests_test.go new file mode 100644 index 0000000000..29236b8928 --- /dev/null +++ b/openstack/networking/v2/extensions/external/testing/requests_test.go @@ -0,0 +1,26 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListExternal(t *testing.T) { + var iTrue bool = true + + networkListOpts := networks.ListOpts{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + } + + listOpts := external.ListOptsExt{ + ListOptsBuilder: networkListOpts, + External: &iTrue, + } + + actual, err := listOpts.ToNetworkListQuery() + th.AssertNoErr(t, err) + th.AssertEquals(t, ExpectedListOpts, actual) +} From f0eff89eec238ed526948329b8697549674a30a4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 30 May 2018 20:03:03 -0600 Subject: [PATCH 0365/2296] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 0d511dbf9f..af0bdb979a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,10 +1,16 @@ # Contributing to Gophercloud +- [New Contributor Tutorial](#new-contributor-tutorial) - [3 ways to get involved](#3-ways-to-get-involved) - [Getting started](#getting-started) - [Tests](#tests) - [Style guide](#basic-style-guide) +## New Contributor Tutorial + +For new contributors, we've put together a detailed tutorial +[here](https://github.com/gophercloud/gophercloud/tree/master/docs/contributor-tutorial)! + ## 3 ways to get involved There are three main ways you can get involved in our open-source project, and From 20c603fea7ffd0db9fe1b92835981d360fdb3dac Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 30 May 2018 20:05:35 -0600 Subject: [PATCH 0366/2296] Update ISSUE_TEMPLATE --- .github/ISSUE_TEMPLATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 1451b81b4b..2c3be773ad 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -1 +1 @@ -Before starting a PR, please read the [style guide](https://github.com/gophercloud/gophercloud/blob/master/STYLEGUIDE.md). +Before starting a PR, please read our [contributor tutorial](https://github.com/gophercloud/gophercloud/tree/master/docs/contributor-tutorial). From 7112fcd50da4ea27e8d4d499b30f04eea143bec2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 30 May 2018 20:06:30 -0600 Subject: [PATCH 0367/2296] Update PULL_REQUEST_TEMPLATE --- .github/PULL_REQUEST_TEMPLATE | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index 43aafa02f8..ea3abc8f88 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,3 +1,6 @@ +Prior to starting a PR, please make sure you have read our +[contributor tutorial](https://github.com/gophercloud/gophercloud/tree/master/docs/contributor-tutorial). + Prior to a PR being reviewed, there needs to be a Github issue that the PR addresses. Replace the brackets and text below with that issue number. From 0adf31394443e81bd23060125c6b25aaa260ff75 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Thu, 31 May 2018 15:19:10 +0800 Subject: [PATCH 0368/2296] Enable OpenLab periodic check Periodic check will be triggered at UTC-0 0:00 and 12:00 of everyday automatically, unit and acceptance tests will be executed, results will be shown in OpenLab CI dashboard http://status.openlabtesting.org/t/openlab/builds.html --- .zuul.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.zuul.yaml b/.zuul.yaml index 3d4798fe6f..8db63e0428 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -74,3 +74,7 @@ recheck-queens: jobs: - gophercloud-acceptance-test-queens + periodic: + jobs: + - gophercloud-unittest + - gophercloud-acceptance-test From 2dfb2c92bd25349e469a652950d749d1b3cc63e8 Mon Sep 17 00:00:00 2001 From: Peter Salin Date: Thu, 5 Jan 2017 07:08:23 +0200 Subject: [PATCH 0369/2296] db/instances: add support for fault information in response body --- openstack/db/v1/instances/results.go | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index 1a570899e4..0c4065f658 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -26,6 +26,17 @@ type Flavor struct { Links []gophercloud.Link } +// Fault describes the fault reason in more detail when a database instance has errored +type Fault struct { + // Indicates the time when the fault occured + Created time.Time `json:"-"` + // A message describing the fault reason + Message string + // More details about the fault, for example a stack trace. Only filled + // in for admin users. + Details string +} + // Instance represents a remote MySQL instance. type Instance struct { // Indicates the datetime that the instance was created @@ -61,6 +72,9 @@ type Instance struct { // The build status of the instance. Status string + // Fault information (only available when the instance has errored) + Fault Fault + // Information about the attached volume of the instance. Volume Volume @@ -87,6 +101,28 @@ func (r *Instance) UnmarshalJSON(b []byte) error { return nil } +func (s *Fault) UnmarshalJSON(b []byte) error { + type tmp Fault + var p *struct { + tmp + Created string `json:"created"` + } + err := json.Unmarshal(b, &p) + if err != nil { + return err + } + *s = Fault(p.tmp) + + if p.Created != "" { + s.Created, err = time.Parse(gophercloud.RFC3339NoZ, p.Created) + if err != nil { + return err + } + } + + return err +} + type commonResult struct { gophercloud.Result } From 8a5c31e7d9bfad5b1a92b82f595a2600469edae4 Mon Sep 17 00:00:00 2001 From: Peter Salin Date: Fri, 13 Jan 2017 14:16:39 +0200 Subject: [PATCH 0370/2296] fixup! db/instances: add support for fault information in response body --- openstack/db/v1/instances/results.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index 0c4065f658..f1ca56e68a 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -101,26 +101,21 @@ func (r *Instance) UnmarshalJSON(b []byte) error { return nil } -func (s *Fault) UnmarshalJSON(b []byte) error { +func (r *Fault) UnmarshalJSON(b []byte) error { type tmp Fault - var p *struct { + var s struct { tmp - Created string `json:"created"` + Created gophercloud.JSONRFC3339NoZ `json:"created"` } - err := json.Unmarshal(b, &p) + err := json.Unmarshal(b, &s) if err != nil { return err } - *s = Fault(p.tmp) + *r = Fault(s.tmp) - if p.Created != "" { - s.Created, err = time.Parse(gophercloud.RFC3339NoZ, p.Created) - if err != nil { - return err - } - } + r.Created = time.Time(s.Created) - return err + return nil } type commonResult struct { From 02ff85428a1c9f80f3b0b8b4d7d5df172282a4bb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 4 Jun 2018 04:37:37 +0000 Subject: [PATCH 0371/2296] DB v1: amend support for fault information in response body --- openstack/db/v1/instances/results.go | 38 ++++---- openstack/db/v1/instances/testing/fixtures.go | 86 +++++++++++++++++-- .../db/v1/instances/testing/requests_test.go | 30 +++++++ 3 files changed, 131 insertions(+), 23 deletions(-) diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index f1ca56e68a..e47a740ce9 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -30,13 +30,32 @@ type Flavor struct { type Fault struct { // Indicates the time when the fault occured Created time.Time `json:"-"` + // A message describing the fault reason Message string + // More details about the fault, for example a stack trace. Only filled // in for admin users. Details string } +func (r *Fault) UnmarshalJSON(b []byte) error { + type tmp Fault + var s struct { + tmp + Created gophercloud.JSONRFC3339NoZ `json:"created"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Fault(s.tmp) + + r.Created = time.Time(s.Created) + + return nil +} + // Instance represents a remote MySQL instance. type Instance struct { // Indicates the datetime that the instance was created @@ -73,7 +92,7 @@ type Instance struct { Status string // Fault information (only available when the instance has errored) - Fault Fault + Fault *Fault // Information about the attached volume of the instance. Volume Volume @@ -101,23 +120,6 @@ func (r *Instance) UnmarshalJSON(b []byte) error { return nil } -func (r *Fault) UnmarshalJSON(b []byte) error { - type tmp Fault - var s struct { - tmp - Created gophercloud.JSONRFC3339NoZ `json:"created"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Fault(s.tmp) - - r.Created = time.Time(s.Created) - - return nil -} - type commonResult struct { gophercloud.Result } diff --git a/openstack/db/v1/instances/testing/fixtures.go b/openstack/db/v1/instances/testing/fixtures.go index 93643c4fbd..3555a008ae 100644 --- a/openstack/db/v1/instances/testing/fixtures.go +++ b/openstack/db/v1/instances/testing/fixtures.go @@ -86,6 +86,48 @@ var createReq = ` } ` +var instanceWithFault = ` +{ + "created": "` + timestamp + `", + "datastore": { + "type": "mysql", + "version": "5.6" + }, + "flavor": { + "id": "1", + "links": [ + { + "href": "https://openstack.example.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://openstack.example.com/v1.0/1234/flavors/1", + "rel": "bookmark" + } + ] + }, + "links": [ + { + "href": "https://openstack.example.com/v1.0/1234/instances/1", + "rel": "self" + } + ], + "hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.openstack.example.com", + "id": "{instanceID}", + "name": "json_rack_instance", + "status": "BUILD", + "updated": "` + timestamp + `", + "volume": { + "size": 2 + }, + "fault": { + "message": "some error message", + "created": "` + timestamp + `", + "details": "some details about the error" + } +} +` + var ( instanceID = "{instanceID}" configGroupID = "00000000-0000-0000-0000-000000000000" @@ -104,11 +146,12 @@ var ( ) var ( - createResp = fmt.Sprintf(`{"instance": %s}`, instance) - listInstancesResp = fmt.Sprintf(`{"instances":[%s]}`, instance) - getInstanceResp = createResp - enableUserResp = `{"user":{"name":"root","password":"secretsecret"}}` - isUserEnabledResp = `{"rootEnabled":true}` + createResp = fmt.Sprintf(`{"instance": %s}`, instance) + createWithFaultResp = fmt.Sprintf(`{"instance": %s}`, instanceWithFault) + listInstancesResp = fmt.Sprintf(`{"instances":[%s]}`, instance) + getInstanceResp = createResp + enableUserResp = `{"user":{"name":"root","password":"secretsecret"}}` + isUserEnabledResp = `{"rootEnabled":true}` ) var expectedInstance = instances.Instance{ @@ -135,10 +178,43 @@ var expectedInstance = instances.Instance{ }, } +var expectedInstanceWithFault = instances.Instance{ + Created: timeVal, + Updated: timeVal, + Flavor: instances.Flavor{ + ID: "1", + Links: []gophercloud.Link{ + {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"}, + {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "bookmark"}, + }, + }, + Hostname: "e09ad9a3f73309469cf1f43d11e79549caf9acf2.openstack.example.com", + ID: instanceID, + Links: []gophercloud.Link{ + {Href: "https://openstack.example.com/v1.0/1234/instances/1", Rel: "self"}, + }, + Name: "json_rack_instance", + Status: "BUILD", + Volume: instances.Volume{Size: 2}, + Datastore: datastores.DatastorePartial{ + Type: "mysql", + Version: "5.6", + }, + Fault: &instances.Fault{ + Created: timeVal, + Message: "some error message", + Details: "some details about the error", + }, +} + func HandleCreate(t *testing.T) { fixture.SetupHandler(t, rootURL, "POST", createReq, createResp, 200) } +func HandleCreateWithFault(t *testing.T) { + fixture.SetupHandler(t, rootURL, "POST", createReq, createWithFaultResp, 200) +} + func HandleList(t *testing.T) { fixture.SetupHandler(t, rootURL, "GET", "", listInstancesResp, 200) } diff --git a/openstack/db/v1/instances/testing/requests_test.go b/openstack/db/v1/instances/testing/requests_test.go index 666cdd2e8d..6e6c4f127e 100644 --- a/openstack/db/v1/instances/testing/requests_test.go +++ b/openstack/db/v1/instances/testing/requests_test.go @@ -41,6 +41,36 @@ func TestCreate(t *testing.T) { th.AssertDeepEquals(t, &expectedInstance, instance) } +func TestCreateWithFault(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateWithFault(t) + + opts := instances.CreateOpts{ + Name: "json_rack_instance", + FlavorRef: "1", + Databases: db.BatchCreateOpts{ + {CharSet: "utf8", Collate: "utf8_general_ci", Name: "sampledb"}, + {Name: "nextround"}, + }, + Users: users.BatchCreateOpts{ + { + Name: "demouser", + Password: "demopassword", + Databases: db.BatchCreateOpts{ + {Name: "sampledb"}, + }, + }, + }, + Size: 2, + } + + instance, err := instances.Create(fake.ServiceClient(), opts).Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, &expectedInstanceWithFault, instance) +} + func TestInstanceList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 19e41f5930c19adb0bcfde9834e489990c355804 Mon Sep 17 00:00:00 2001 From: Yongfeng Du Date: Mon, 28 May 2018 22:20:57 +0800 Subject: [PATCH 0372/2296] Fix the CI report issue CI report SUCCESS when some test cases fails. Closed theopenlab/openlab-zuul-jobs#219 --- script/acceptancetest | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/acceptancetest b/script/acceptancetest index f45165debf..d97084fe21 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -1,5 +1,8 @@ #!/bin/bash # +set -e +set -o pipefail +set -x source `dirname $0`/stackenv From 3f88405b0261560629b73638c2730901d1abf04c Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 7 Jun 2018 14:57:49 -0700 Subject: [PATCH 0373/2296] Senlin: Clusters Update (#915) * senlin-cluster-update * Change Timeout to *int to allow change to value of 0 --- .../clustering/v1/autoscaling_test.go | 94 ++++++ openstack/clustering/v1/clusters/doc.go | 14 + openstack/clustering/v1/clusters/requests.go | 45 +++ openstack/clustering/v1/clusters/results.go | 5 + .../v1/clusters/testing/requests_test.go | 295 ++++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 + 6 files changed, 457 insertions(+) diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index 568c414aa6..573dabcc6c 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -3,11 +3,14 @@ package v1 import ( + "fmt" "strings" "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/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/profiles" "github.com/gophercloud/gophercloud/pagination" @@ -24,6 +27,7 @@ func TestAutoScaling(t *testing.T) { clusterCreate(t) clusterGet(t) clusterList(t) + clusterUpdate(t) } func profileCreate(t *testing.T) { @@ -229,3 +233,93 @@ func clusterList(t *testing.T) { th.AssertEquals(t, true, testClusterFound) } + +func clusterUpdate(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + clusterName := testName + newClusterName := clusterName + "-TEST-UPDATE_CLUSTER" + + cluster, err := clusters.Get(client, clusterName).Extract() + if err != nil { + t.Fatalf("Unable to get cluster %s: %v", clusterName, err) + } + th.AssertEquals(t, clusterName, cluster.Name) + clusterID := cluster.ID + + // Update to new cluster name + updateOpts := clusters.UpdateOpts{ + Name: newClusterName, + } + + updateResult := clusters.Update(client, clusterID, updateOpts) + location := updateResult.Header.Get("Location") + th.AssertEquals(t, true, location != "") + + actionID := "" + locationFields := strings.Split(location, "actions/") + if len(locationFields) >= 2 { + actionID = locationFields[1] + } + + err = WaitForClusterToUpdate(client, actionID, 15) + if err != nil { + t.Fatalf("Error waiting for cluster to update: %v", err) + } + + cluster, err = clusters.Get(client, clusterID).Extract() + if err != nil { + t.Fatalf("Unable to get cluster: %v", err) + } + th.AssertEquals(t, newClusterName, cluster.Name) + + // Revert back to original cluster name + updateOpts = clusters.UpdateOpts{ + Name: clusterName, + } + + updateResult = clusters.Update(client, clusterID, updateOpts) + location = updateResult.Header.Get("Location") + th.AssertEquals(t, true, location != "") + + actionID = "" + locationFields = strings.Split(location, "actions/") + if len(locationFields) >= 2 { + actionID = locationFields[1] + } + + err = WaitForClusterToUpdate(client, actionID, 15) + if err != nil { + t.Fatalf("Error waiting for cluster to update: %v", err) + } + + cluster, err = clusters.Get(client, clusterID).Extract() + if err != nil { + t.Fatalf("Unable to get cluster: %v", err) + } + th.AssertEquals(t, clusterName, cluster.Name) +} + +func WaitForClusterToUpdate(client *gophercloud.ServiceClient, actionID string, sleepTimeSecs int) error { + return gophercloud.WaitFor(sleepTimeSecs, func() (bool, error) { + if actionID == "" { + return false, fmt.Errorf("Invalid action id. id=%s", actionID) + } + + action, err := actions.Get(client, actionID).Extract() + if err != nil { + return false, err + } + switch action.Status { + case "SUCCEEDED": + return true, nil + case "READY", "RUNNING": + return false, nil + default: + return false, fmt.Errorf("Error WaitFor ActionID=%s. Received status=%v", actionID, action.Status) + } + }) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 3008e07815..9dada539bb 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -44,5 +44,19 @@ Example to List Clusters fmt.Printf("%+v\n", cluster) } +Example to Update a cluster + + updateOpts := clusters.UpdateOpts{ + Name: "testcluster", + ProfileID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", + } + + clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" + cluster, err := clusters.Update(serviceClient, clusterName, clusters.UpdateOpts{Name: newClusterName}).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", cluster) + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index cb9abc2165..80bb842100 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -95,3 +95,48 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return ClusterPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// UpdateOpts implements UpdateOpts +type UpdateOpts struct { + Config string `json:"config,omitempty"` + Name string `json:"name,omitempty"` + ProfileID string `json:"profile_id,omitempty"` + Timeout *int `json:"timeout,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + ProfileOnly *bool `json:"profile_only,omitempty"` +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToClusterUpdateMap() (map[string]interface{}, error) +} + +// ToClusterUpdateMap assembles a request body based on the contents of +// UpdateOpts. +func (opts UpdateOpts) ToClusterUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "cluster") + if err != nil { + return nil, err + } + return b, nil +} + +// Update implements profile updated request. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToClusterUpdateMap() + if err != nil { + r.Err = err + return r + } + + var result *http.Response + result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 345e45c013..731da771e3 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -25,6 +25,11 @@ type GetResult struct { commonResult } +// UpdateResult is the response of a Update operations. +type UpdateResult struct { + commonResult +} + type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 4d6e9b1147..4dccc4777f 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -887,3 +887,298 @@ func TestListClusters(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestUpdateCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + id := "7d85f602-a948-4a30-afd4-e84f47471c15" + clusterName := "cluster1" + profileID := "edc63d0a-2ca4-48fa-9854-27926da76a4a" + profileName := "profile1" + + th.Mux.HandleFunc("/v1/clusters/"+id, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": "2015-02-10T14:26:14Z", + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "%s", + "init_at": "2015-02-10T15:26:14Z", + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "%s", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "%s", + "profile_name": "%s", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "2015-02-10T16:26:14Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`, id, clusterName, profileID, profileName) + }) + + createdAt, _ := time.Parse(time.RFC3339, "2015-02-10T14:26:14Z") + initAt, _ := time.Parse(time.RFC3339, "2015-02-10T15:26:14Z") + updatedAt, _ := time.Parse(time.RFC3339, "2015-02-10T16:26:14Z") + expected := clusters.Cluster{ + Config: map[string]interface{}{}, + CreatedAt: createdAt, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 4, + Domain: "", + ID: id, + InitAt: initAt, + MaxSize: -1, + Metadata: map[string]interface{}{}, + MinSize: 0, + Name: clusterName, + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: profileID, + ProfileName: profileName, + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: updatedAt, + User: "5e5bf8027826429c96af157f68dc9072", + } + + updateOpts := clusters.UpdateOpts{ + Name: clusterName, + ProfileID: profileID, + } + + actual, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, *actual) +} + +func TestUpdateClusterEmptyTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + id := "7d85f602-a948-4a30-afd4-e84f47471c15" + clusterName := "cluster1" + profileID := "edc63d0a-2ca4-48fa-9854-27926da76a4a" + profileName := "profile1" + + th.Mux.HandleFunc("/v1/clusters/"+id, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": null, + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "%s", + "init_at": null, + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "%s", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "%s", + "profile_name": "%s", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`, id, clusterName, profileID, profileName) + }) + + expected := clusters.Cluster{ + Config: map[string]interface{}{}, + CreatedAt: time.Time{}, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 4, + Domain: "", + ID: id, + InitAt: time.Time{}, + MaxSize: -1, + Metadata: map[string]interface{}{}, + MinSize: 0, + Name: clusterName, + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: profileID, + ProfileName: profileName, + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: time.Time{}, + User: "5e5bf8027826429c96af157f68dc9072", + } + + updateOpts := clusters.UpdateOpts{ + Name: clusterName, + ProfileID: profileID, + } + + actual, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, *actual) +} + +func TestUpdateClusterInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + id := "7d85f602-a948-4a30-afd4-e84f47471c15" + clusterName := "cluster1" + profileID := "edc63d0a-2ca4-48fa-9854-27926da76a4a" + profileName := "profile1" + + th.Mux.HandleFunc("/v1/clusters/"+id, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": 123456789.0, + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "%s", + "init_at": 123456789.0, + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "%s", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "%s", + "profile_name": "%s", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": 123456789.0, + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`, id, clusterName, profileID, profileName) + }) + + updateOpts := clusters.UpdateOpts{ + Name: clusterName, + ProfileID: profileID, + } + + _, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestUpdateClusterInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + id := "7d85f602-a948-4a30-afd4-e84f47471c15" + clusterName := "cluster1" + profileID := "edc63d0a-2ca4-48fa-9854-27926da76a4a" + profileName := "profile1" + + th.Mux.HandleFunc("/v1/clusters/"+id, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "cluster": { + "config": {}, + "created_at": "invalid", + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "%s", + "init_at": "invalid", + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "%s", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "%s", + "profile_name": "%s", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "invalid", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`, id, clusterName, profileID, profileName) + }) + + updateOpts := clusters.UpdateOpts{ + Name: clusterName, + ProfileID: profileID, + } + + _, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index d1a3d9211a..cc8970f311 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -24,3 +24,7 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 6be80906801ec5c26c13ac0215cd3dae88c423b3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 8 Jun 2018 02:00:39 +0000 Subject: [PATCH 0374/2296] Object Storage v1: Close Body on Container Create --- openstack/objectstorage/v1/containers/requests.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 8a59fd1bc3..3321dd545b 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -107,6 +107,7 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB }) if resp != nil { r.Header = resp.Header + resp.Body.Close() } r.Err = err return From 2f1e0f1ac09004e45b4a35ec7792458cd388acc1 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0375/2296] senlin-clusters-delete --- .../openstack/clustering/v1/autoscaling_test.go | 14 ++++++++++++++ openstack/clustering/v1/clusters/doc.go | 8 ++++++++ openstack/clustering/v1/clusters/requests.go | 10 ++++++++++ openstack/clustering/v1/clusters/results.go | 6 ++++++ .../v1/clusters/testing/requests_test.go | 16 ++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 ++++ 6 files changed, 58 insertions(+) diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index 573dabcc6c..014eb3971e 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -25,6 +25,7 @@ func TestAutoScaling(t *testing.T) { profileGet(t) profileList(t) clusterCreate(t) + defer clustersDelete(t) clusterGet(t) clusterList(t) clusterUpdate(t) @@ -323,3 +324,16 @@ func WaitForClusterToUpdate(client *gophercloud.ServiceClient, actionID string, } }) } + +func clustersDelete(t *testing.T) { + + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + clusterName := testName + err = clusters.Delete(client, clusterName).ExtractErr() + th.AssertNoErr(t, err) + t.Logf("Cluster deleted: %s", clusterName) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 9dada539bb..bef956fee6 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -58,5 +58,13 @@ Example to Update a cluster } fmt.Printf("%+v\n", cluster) +Example to Delete a cluster + + clusterID := "dc6d336e3fc4c0a951b5698cd1236ee" + err := clusters.Delete(serviceClient, clusterID).ExtractErr() + if err != nil { + panic(err) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 80bb842100..44a234158a 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -140,3 +140,13 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } return } + +// Delete deletes the specified cluster ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + var result *http.Response + result, r.Err = client.Delete(deleteURL(client, id), nil) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 731da771e3..d5f46bac5e 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -145,3 +145,9 @@ func (r *Cluster) UnmarshalJSON(b []byte) error { return nil } + +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 4dccc4777f..4d12c1cda1 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -1182,3 +1182,19 @@ func TestUpdateClusterInvalidTimeString(t *testing.T) { _, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() th.AssertEquals(t, false, err == nil) } + +func TestDeleteCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/6dc6d336e3fc4c0a951b5698cd1236ee", 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 := clusters.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index cc8970f311..272e687030 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -28,3 +28,7 @@ func listURL(client *gophercloud.ServiceClient) string { func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 001e70231e5e348e64ebb21a34a7125642b3876f Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 30 Mar 2018 11:50:46 -0700 Subject: [PATCH 0376/2296] senlin-profile-update --- .../clustering/v1/autoscaling_test.go | 25 +++ openstack/clustering/v1/profiles/doc.go | 9 + openstack/clustering/v1/profiles/requests.go | 39 ++++ openstack/clustering/v1/profiles/results.go | 5 + .../v1/profiles/testing/requests_test.go | 176 ++++++++++++++++++ openstack/clustering/v1/profiles/urls.go | 4 + 6 files changed, 258 insertions(+) diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index 014eb3971e..34c7f164dc 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -24,6 +24,7 @@ func TestAutoScaling(t *testing.T) { profileCreate(t) profileGet(t) profileList(t) + profileUpdate(t) clusterCreate(t) defer clustersDelete(t) clusterGet(t) @@ -129,6 +130,30 @@ func profileList(t *testing.T) { th.AssertEquals(t, true, testProfileFound) } +func profileUpdate(t *testing.T) { + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + profileName := testName + newProfileName := profileName + "-TEST-PROFILE_UPDATE" + + // Use new name + profile, err := profiles.Update(client, profileName, profiles.UpdateOpts{Name: newProfileName}).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newProfileName, profile.Name) + + tools.PrintResource(t, profile) + + // Revert back to original name + profile, err = profiles.Update(client, newProfileName, profiles.UpdateOpts{Name: profileName}).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, profileName, profile.Name) + + tools.PrintResource(t, profile) +} + func clusterCreate(t *testing.T) { client, err := clients.NewClusteringV1Client() if err != nil { diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index d72df047c5..21de74631e 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -53,5 +53,14 @@ Example to List profiles return true, nil }) +Example to Update profile + + profile, err := profiles.Update(serviceClient, profileName, profiles.UpdateOpts{Name: newProfileName}).Extract() + if err != nil { + panic(err) + } + + fmt.Print("profile", profile) + */ package profiles diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index aa6b41a6bd..fd3f2a0b9b 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -90,3 +90,42 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return ProfilePage{pagination.LinkedPageBase{PageResult: r}} }) } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToProfileUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts implements Profile's UpdateOpts +type UpdateOpts struct { + Metadata map[string]interface{} `json:"metadata,omitempty"` + Name string `json:"name,omitempty"` +} + +// ToProfileUpdateMap assembles a request body based on the contents of +// UpdateOpts. +func (opts UpdateOpts) ToProfileUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "profile") + if err != nil { + return nil, err + } + return b, nil +} + +// Update implements profile update request. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToProfileUpdateMap() + if err != nil { + r.Err = err + return r + } + var result *http.Response + result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index 1875964524..f9c0ff1c96 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -27,6 +27,11 @@ type GetResult struct { commonResult } +// UpdateResult is the response of a Update operations. +type UpdateResult struct { + commonResult +} + // Extract provides access to Profile returned by the Get and Create functions. func (r commonResult) Extract() (*Profile, error) { var s struct { diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go index a12552aaa4..63152b6919 100644 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -689,3 +689,179 @@ func TestListProfilesInvalidTimeString(t *testing.T) { t.Errorf("Expected 0 page of profiles, got %d pages instead", count) } } + +func TestUpdateProfile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "profile": { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": { + "foo": "bar" + }, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "2016-01-03T17:22:23Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } + }`) + }) + + actual, err := profiles.Update(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", profiles.UpdateOpts{Name: "pserver"}).Extract() + if err != nil { + t.Errorf("Failed to get profile. %v", err) + } else { + createdAt, _ := time.Parse(time.RFC3339, "2016-01-03T16:22:23Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-01-03T17:22:23Z") + + expected := profiles.Profile{ + CreatedAt: createdAt, + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{"foo": "bar"}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": float64(1), + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: updatedAt, + User: "5e5bf8027826429c96af157f68dc9072", + } + + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestUpdateProfilesInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles", 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, ` + { + "profiles": [ + { + "created_at": 123456789.0, + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": 1.0 + }, + "type": "os.nova.server-1.0", + "updated_at": 123456789.0, + "user": "5e5bf8027826429c96af157f68dc9072" + } + ] + }`) + }) + + _, err := profiles.Update(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", profiles.UpdateOpts{Name: "pserver"}).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestUpdateProfilesInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles", 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, ` + { + "profiles": [ + { + "created_at": "invalid", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": 1.0 + }, + "type": "os.nova.server-1.0", + "updated_at": "invalid", + "user": "5e5bf8027826429c96af157f68dc9072" + } + ] + }`) + }) + + _, err := profiles.Update(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", profiles.UpdateOpts{Name: "pserver"}).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/profiles/urls.go b/openstack/clustering/v1/profiles/urls.go index 02ff7c7242..88bd152901 100644 --- a/openstack/clustering/v1/profiles/urls.go +++ b/openstack/clustering/v1/profiles/urls.go @@ -24,3 +24,7 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From d20555b88b1b2090c6cbd734f38c65bdec652a48 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 30 Mar 2018 11:50:46 -0700 Subject: [PATCH 0377/2296] senlin-profile-delete Added wait for cluster to complete cluster delete before moving on to profile delete for acceptance test --- .../clustering/v1/autoscaling_test.go | 59 ++++++++++++++++++- openstack/clustering/v1/profiles/doc.go | 8 +++ openstack/clustering/v1/profiles/requests.go | 10 ++++ openstack/clustering/v1/profiles/results.go | 6 ++ .../v1/profiles/testing/requests_test.go | 16 +++++ openstack/clustering/v1/profiles/urls.go | 4 ++ 6 files changed, 101 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go index 34c7f164dc..2a2a7c837a 100644 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ b/acceptance/openstack/clustering/v1/autoscaling_test.go @@ -22,6 +22,7 @@ var testName string func TestAutoScaling(t *testing.T) { testName = tools.RandomString("TESTACC-", 8) profileCreate(t) + defer profileDelete(t) profileGet(t) profileList(t) profileUpdate(t) @@ -96,6 +97,18 @@ func profileGet(t *testing.T) { tools.PrintResource(t, profile) } +func profileDelete(t *testing.T) { + + client, err := clients.NewClusteringV1Client() + if err != nil { + t.Fatalf("Unable to create clustering client: %v", err) + } + + profileName := testName + err = profiles.Delete(client, profileName).ExtractErr() + th.AssertNoErr(t, err) +} + func profileList(t *testing.T) { client, err := clients.NewClusteringV1Client() if err != nil { @@ -358,7 +371,49 @@ func clustersDelete(t *testing.T) { } clusterName := testName - err = clusters.Delete(client, clusterName).ExtractErr() + deleteResult := clusters.Delete(client, clusterName) + err = deleteResult.ExtractErr() th.AssertNoErr(t, err) - t.Logf("Cluster deleted: %s", clusterName) + + location := deleteResult.Header.Get("Location") + th.AssertEquals(t, true, location != "") + + actionID := "" + locationFields := strings.Split(location, "actions/") + if len(locationFields) >= 2 { + actionID = locationFields[1] + } + + err = WaitForClusterToDelete(client, actionID, 15) + if err != nil { + t.Fatalf("Error waiting for cluster to delete: %v", err) + } + + _, err = clusters.Get(client, clusterName).Extract() + if err == nil { + t.Fatalf("Unable to delete cluster [%s]: %v", clusterName, err) + } + t.Log("Cluster deleted:", clusterName) + +} + +func WaitForClusterToDelete(client *gophercloud.ServiceClient, actionID string, sleepTimeSecs int) error { + return gophercloud.WaitFor(sleepTimeSecs, func() (bool, error) { + if actionID == "" { + return false, fmt.Errorf("Invalid action id. id=%s", actionID) + } + + action, err := actions.Get(client, actionID).Extract() + if err != nil { + return false, err + } + switch action.Status { + case "SUCCEEDED": + return true, nil + case "READY", "RUNNING", "DELETING", "WAITING": + return false, nil + default: + return false, fmt.Errorf("Error WaitFor ActionID=%s. Received status=%v", actionID, action.Status) + } + }) } diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index 21de74631e..9bb70601df 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -62,5 +62,13 @@ Example to Update profile fmt.Print("profile", profile) +Example to Delete profile + + profileID := "6dc6d336e3fc4c0a951b5698cd1236ee" + err := profiles.Delete(serviceClient, profileID).ExtractErr() + if err != nil { + panic(err) + } + */ package profiles diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index fd3f2a0b9b..968a05d13d 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -129,3 +129,13 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } return } + +// Delete deletes the specified profile via profile id. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + var result *http.Response + result, r.Err = client.Delete(deleteURL(client, id), nil) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index f9c0ff1c96..1a50ca36ce 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -154,3 +154,9 @@ func (r *Profile) UnmarshalJSON(b []byte) error { return nil } + +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go index 63152b6919..7abb37318c 100644 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -865,3 +865,19 @@ func TestUpdateProfilesInvalidTimeString(t *testing.T) { _, err := profiles.Update(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", profiles.UpdateOpts{Name: "pserver"}).Extract() th.AssertEquals(t, false, err == nil) } + +func TestDeleteProfile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/profiles/6dc6d336e3fc4c0a951b5698cd1236ee", 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) + }) + + deleteResult := profiles.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") + th.AssertNoErr(t, deleteResult.ExtractErr()) +} diff --git a/openstack/clustering/v1/profiles/urls.go b/openstack/clustering/v1/profiles/urls.go index 88bd152901..060d91ddc2 100644 --- a/openstack/clustering/v1/profiles/urls.go +++ b/openstack/clustering/v1/profiles/urls.go @@ -28,3 +28,7 @@ func listURL(client *gophercloud.ServiceClient) string { func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 131c0d5a03da6b130e0265e11cd7cb04e80636d1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 4 Jun 2018 02:52:21 +0000 Subject: [PATCH 0378/2296] Acc Tests: Parse the key returned by Nova --- acceptance/openstack/compute/v2/keypairs_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go index a3a17d19e6..eeb9e75083 100644 --- a/acceptance/openstack/compute/v2/keypairs_test.go +++ b/acceptance/openstack/compute/v2/keypairs_test.go @@ -5,6 +5,8 @@ package v2 import ( "testing" + "golang.org/x/crypto/ssh" + "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" @@ -22,6 +24,12 @@ func TestKeypairsCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteKeyPair(t, client, keyPair) + // There was a series of OpenStack releases, between Liberty and Ocata, + // where the returned SSH key was not parsable by Go. + // This checks if the issue is happening again. + _, err = ssh.ParsePrivateKey([]byte(keyPair.PrivateKey)) + th.AssertNoErr(t, err) + tools.PrintResource(t, keyPair) allPages, err := keypairs.List(client).AllPages() From dece64a100bc529375dcfaba482d52439058eb34 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 Jun 2018 01:40:47 +0000 Subject: [PATCH 0379/2296] Acc Tests: enable keypair testing --- script/acceptancetest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/acceptancetest b/script/acceptancetest index f45165debf..d4fbe77d3f 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -11,7 +11,7 @@ source `dirname $0`/stackenv go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v3/ go test -v -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ go test -v -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ -go test -v -tags "fixtures acceptance" -run "SecGroup|Flavor" ./acceptance/openstack/compute/v2/ +go test -v -tags "fixtures acceptance" -run "SecGroup|Flavor|Keypair" ./acceptance/openstack/compute/v2/ go test -v -tags "fixtures acceptance" ./acceptance/openstack/container/v1/ # To enable more after the fix of https://github.com/gophercloud/gophercloud/issues/818 # go test -v -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ From af3294b2dff581b5928f753f42dd961ab8d7592f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 Jun 2018 04:44:31 +0000 Subject: [PATCH 0380/2296] Changing int64 to *int64 --- .../objectstorage/v1/accounts/results.go | 10 +++---- .../v1/accounts/testing/fixtures.go | 17 +++++++++++ .../v1/accounts/testing/requests_test.go | 28 ++++++++++++++++++- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/openstack/objectstorage/v1/accounts/results.go b/openstack/objectstorage/v1/accounts/results.go index 52940b8d7e..fe9d4ab8b6 100644 --- a/openstack/objectstorage/v1/accounts/results.go +++ b/openstack/objectstorage/v1/accounts/results.go @@ -61,9 +61,8 @@ func (r UpdateResult) Extract() (*UpdateHeader, error) { // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { - BytesUsed int64 `json:"-"` - //QuotaBytes will be -1 if no account quota is set. - QuotaBytes int64 `json:"-"` + BytesUsed int64 `json:"-"` + QuotaBytes *int64 `json:"-"` ContainerCount int64 `json:"-"` ContentLength int64 `json:"-"` ObjectCount int64 `json:"-"` @@ -104,12 +103,13 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { switch s.QuotaBytes { case "": - r.QuotaBytes = -1 //OpenStack convention is to represent "no quota" as -1 + r.QuotaBytes = nil default: - r.QuotaBytes, err = strconv.ParseInt(s.QuotaBytes, 10, 64) + v, err := strconv.ParseInt(s.QuotaBytes, 10, 64) if err != nil { return err } + r.QuotaBytes = &v } switch s.ContentLength { diff --git a/openstack/objectstorage/v1/accounts/testing/fixtures.go b/openstack/objectstorage/v1/accounts/testing/fixtures.go index cf88dd3734..e1d2182699 100644 --- a/openstack/objectstorage/v1/accounts/testing/fixtures.go +++ b/openstack/objectstorage/v1/accounts/testing/fixtures.go @@ -26,6 +26,23 @@ func HandleGetAccountSuccessfully(t *testing.T) { }) } +// HandleGetAccountNoQuotaSuccessfully creates an HTTP handler at `/` on the +// test handler mux that responds with a `Get` response. +func HandleGetAccountNoQuotaSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Set("X-Account-Container-Count", "2") + w.Header().Set("X-Account-Object-Count", "5") + w.Header().Set("X-Account-Bytes-Used", "14") + w.Header().Set("X-Account-Meta-Subject", "books") + w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT") + + w.WriteHeader(http.StatusNoContent) + }) +} + // HandleUpdateAccountSuccessfully creates an HTTP handler at `/` on the test handler mux that // responds with a `Update` response. func HandleUpdateAccountSuccessfully(t *testing.T) { diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go index 4a2c7289aa..c396227e4d 100644 --- a/openstack/objectstorage/v1/accounts/testing/requests_test.go +++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go @@ -43,8 +43,34 @@ func TestGetAccount(t *testing.T) { _, err := res.Extract() th.AssertNoErr(t, err) + var quotaBytes int64 = 42 expected := &accounts.GetHeader{ - QuotaBytes: 42, + QuotaBytes: "aBytes, + ContainerCount: 2, + ObjectCount: 5, + BytesUsed: 14, + Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, loc), // Fri, 17 Jan 2014 16:09:56 GMT + } + actual, err := res.Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestGetAccountNoQuota(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetAccountNoQuotaSuccessfully(t) + + expectedMetadata := map[string]string{"Subject": "books"} + res := accounts.Get(fake.ServiceClient(), &accounts.GetOpts{}) + th.AssertNoErr(t, res.Err) + actualMetadata, _ := res.ExtractMetadata() + th.CheckDeepEquals(t, expectedMetadata, actualMetadata) + _, err := res.Extract() + th.AssertNoErr(t, err) + + expected := &accounts.GetHeader{ + QuotaBytes: nil, ContainerCount: 2, ObjectCount: 5, BytesUsed: 14, From cf98df97d965cfd583d14ae353aa5ee0901e03c2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 Jun 2018 05:14:34 +0000 Subject: [PATCH 0381/2296] Compute v2: cleaning up console output implementation --- .../openstack/compute/v2/servers_test.go | 24 ++++++++++ openstack/compute/v2/servers/requests.go | 8 ++-- openstack/compute/v2/servers/results.go | 47 ++++--------------- .../compute/v2/servers/testing/fixtures.go | 13 +---- .../v2/servers/testing/requests_test.go | 14 ++---- 5 files changed, 43 insertions(+), 63 deletions(-) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index f43c94da1d..8ada3c092f 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -388,3 +388,27 @@ func TestServersActionResizeRevert(t *testing.T) { t.Fatal(err) } } + +func TestServersConsoleOutput(t *testing.T) { + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + server, err := CreateServer(t, client) + if err != nil { + t.Fatalf("Unable to create server: %v", err) + } + + defer DeleteServer(t, client, server) + + outputOpts := &servers.ShowConsoleOutputOpts{ + Length: 4, + } + output, err := servers.ShowConsoleOutput(client, server.ID, outputOpts).Extract() + if err != nil { + t.Fatal(err) + } + + tools.PrintResource(t, output) +} diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 72a2ea7edb..08f20d97ce 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -750,7 +750,7 @@ type ShowConsoleOutputOptsBuilder interface { type ShowConsoleOutputOpts struct { // The number of lines to fetch from the end of console log. // All lines will be returned if this is not specified. - Length string `json:"length,omitempty"` + Length int `json:"length,omitempty"` } // ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body. @@ -765,10 +765,8 @@ func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowCo r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - r.Err = err - r.Header = resp.Header - return r + return } diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 280f61be78..b6227869c9 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -4,11 +4,9 @@ import ( "crypto/rsa" "encoding/base64" "encoding/json" - "errors" "fmt" "net/url" "path" - "strings" "time" "github.com/gophercloud/gophercloud" @@ -79,6 +77,16 @@ type ShowConsoleOutputResult struct { gophercloud.Result } +// Extract will return the console output from a ShowConsoleOutput request. +func (r ShowConsoleOutputResult) Extract() (string, error) { + var s struct { + Output string `json:"output"` + } + + err := r.ExtractInto(&s) + return s.Output, err +} + // GetPasswordResult represent the result of a get os-server-password operation. type GetPasswordResult struct { gophercloud.Result @@ -99,41 +107,6 @@ func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, return s.Password, err } -func (r ShowConsoleOutputResult) ExtractConsoleOutput() (string, error) { - if r.Err != nil { - return "", r.Err - } - - var s struct { - Output string `json:"output"` - } - - err := r.ExtractInto(&s) - return s.Output, err -} - -func GetHostKeyFromConsole(consoleOutput string) (string, error) { - start := `-----BEGIN SSH HOST KEY KEYS-----\r` - end := `-----END SSH HOST KEY KEYS-----\r` - searchFlag := false - lines := strings.Split(consoleOutput, "\n") - for _, value := range lines { - if value == start { - searchFlag = true - } - - if value == end { - searchFlag = false - } - - if searchFlag && strings.HasPrefix(value, "ssh-rsa") { - return value, nil - } - } - - return "", errors.New("ssh host key not found") -} - func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) { b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword))) diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index 6b71361dde..681637d6ac 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -304,17 +304,6 @@ const ServerPasswordBody = ` } ` -const HostKeyConsoleOutput = ` -FAKE CONSOLE OUTPUT\r ------BEGIN SSH HOST KEY KEYS-----\r -ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDciNMyzj0osyPOM+1OyseTWgkzw+M43zp5H2CchG8daRDHel7V3OHETVdI6WofNnSdBJAwIoisRFPxyroNGiVw= root@my-name -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDU854+fNdcKMZTLCUejMOZllQmmphr6V5Aaz1F2+x2jXql5rqKQd5/h6OdFszcp+gdTeVtfgG++/298qodTemVVrvqwjp4eN87iHvhPxH6GDEevAKlEed2ckdAmgvzI9rcOYgR/46G9xIea0IdgNjMvN1baj6WPtv+HfcfH/ZV58G306lSJfbz/GVxNTIxW+Wg7ZQCAe6jWgm4oQ+66sco+7Fub24EPue3kO8jqufqq3mY5+MFlzEHSX5B04ioG5Alw/JuqVx5+7zHt9I2wA3nzsyUdKtCTrw8V4fYEhWDm53WLOpW+8CeYCXuv+yL7EjwLqhIH/TUuzGQiWmFGvyz root@my-name ------END SSH HOST KEY KEYS-----\r -LAST LINE -` - -const HostKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDU854+fNdcKMZTLCUejMOZllQmmphr6V5Aaz1F2+x2jXql5rqKQd5/h6OdFszcp+gdTeVtfgG++/298qodTemVVrvqwjp4eN87iHvhPxH6GDEevAKlEed2ckdAmgvzI9rcOYgR/46G9xIea0IdgNjMvN1baj6WPtv+HfcfH/ZV58G306lSJfbz/GVxNTIxW+Wg7ZQCAe6jWgm4oQ+66sco+7Fub24EPue3kO8jqufqq3mY5+MFlzEHSX5B04ioG5Alw/JuqVx5+7zHt9I2wA3nzsyUdKtCTrw8V4fYEhWDm53WLOpW+8CeYCXuv+yL7EjwLqhIH/TUuzGQiWmFGvyz root@my-name" - const ConsoleOutputBody = `{ "output": "abc" }` @@ -765,7 +754,7 @@ func HandleShowConsoleOutputSuccessfully(t *testing.T, response string) { th.Mux.HandleFunc("/servers/1234asdf/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-getConsoleOutput": { "length": "50" } }`) + th.TestJSONRequest(t, r, `{ "os-getConsoleOutput": { "length": 50 } }`) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index fe5e06cafe..c5fa2a4b73 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -258,17 +258,13 @@ func TestShowConsoleOutput(t *testing.T) { defer th.TeardownHTTP() HandleShowConsoleOutputSuccessfully(t, ConsoleOutputBody) - Actual, err := servers.ShowConsoleOutput(client.ServiceClient(), "1234asdf", &servers.ShowConsoleOutputOpts{ - Length: "50"}).ExtractConsoleOutput() - - th.AssertNoErr(t, err) - th.AssertByteArrayEquals(t, []byte(ConsoleOutput), []byte(Actual)) -} + outputOpts := &servers.ShowConsoleOutputOpts{ + Length: 50, + } + actual, err := servers.ShowConsoleOutput(client.ServiceClient(), "1234asdf", outputOpts).Extract() -func TestHostKeyFromConsoleOutput(t *testing.T) { - hostkey, err := servers.GetHostKeyFromConsole(HostKeyConsoleOutput) th.AssertNoErr(t, err) - th.AssertByteArrayEquals(t, []byte(HostKey), []byte(hostkey)) + th.AssertByteArrayEquals(t, []byte(ConsoleOutput), []byte(actual)) } func TestGetPassword(t *testing.T) { From b939b9e14e6851c8bcf52f951f106ea72b867087 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 Jun 2018 05:25:57 +0000 Subject: [PATCH 0382/2296] Messaging v2: URL const nit --- openstack/messaging/v2/messages/urls.go | 36 ++++++++++--------- openstack/messaging/v2/queues/urls.go | 48 +++++++++++++------------ 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/openstack/messaging/v2/messages/urls.go b/openstack/messaging/v2/messages/urls.go index 0816ee14df..a00dd5620d 100644 --- a/openstack/messaging/v2/messages/urls.go +++ b/openstack/messaging/v2/messages/urls.go @@ -6,19 +6,33 @@ import ( "github.com/gophercloud/gophercloud" ) -const ApiVersion = "v2" -const ApiName = "queues" +const ( + apiVersion = "v2" + apiName = "queues" +) func createURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") + return client.ServiceURL(apiVersion, apiName, queueName, "messages") } func listURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") + return client.ServiceURL(apiVersion, apiName, queueName, "messages") } func getURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") + return client.ServiceURL(apiVersion, apiName, queueName, "messages") +} + +func deleteURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(apiVersion, apiName, queueName, "messages") +} + +func DeleteMessageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { + return client.ServiceURL(apiVersion, apiName, queueName, "messages", messageID) +} + +func messageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { + return client.ServiceURL(apiVersion, apiName, queueName, "messages", messageID) } // Builds next page full url based on current url. @@ -33,15 +47,3 @@ func nextPageURL(currentURL string, next string) (string, error) { } return base.ResolveReference(rel).String(), nil } - -func deleteURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "messages") -} - -func DeleteMessageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "messages", messageID) -} - -func messageURL(client *gophercloud.ServiceClient, queueName string, messageID string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "messages", messageID) -} diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index e58bdf765d..6b5a0e325a 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -6,54 +6,56 @@ import ( "github.com/gophercloud/gophercloud" ) -const ApiVersion = "v2" -const ApiName = "queues" +const ( + apiVersion = "v2" + apiName = "queues" +) func commonURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(ApiVersion, ApiName) + return client.ServiceURL(apiVersion, apiName) } func createURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName) + return client.ServiceURL(apiVersion, apiName, queueName) } func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } -// builds next page full url based on current url -func nextPageURL(currentURL string, next string) (string, error) { - base, err := url.Parse(currentURL) - if err != nil { - return "", err - } - rel, err := url.Parse(next) - if err != nil { - return "", err - } - return base.ResolveReference(rel).String(), nil -} - func updateURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName) + return client.ServiceURL(apiVersion, apiName, queueName) } func getURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName) + return client.ServiceURL(apiVersion, apiName, queueName) } func deleteURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName) + return client.ServiceURL(apiVersion, apiName, queueName) } func statURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "stats") + return client.ServiceURL(apiVersion, apiName, queueName, "stats") } func shareURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "share") + return client.ServiceURL(apiVersion, apiName, queueName, "share") } func purgeURL(client *gophercloud.ServiceClient, queueName string) string { - return client.ServiceURL(ApiVersion, ApiName, queueName, "purge") + return client.ServiceURL(apiVersion, apiName, queueName, "purge") +} + +// builds next page full url based on current url +func nextPageURL(currentURL string, next string) (string, error) { + base, err := url.Parse(currentURL) + if err != nil { + return "", err + } + rel, err := url.Parse(next) + if err != nil { + return "", err + } + return base.ResolveReference(rel).String(), nil } From 6ae2902c7b0fb8d5068ec8e305e2ff15bf794e79 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 14 Jun 2018 03:02:53 +0000 Subject: [PATCH 0383/2296] Container v1: Support new time format --- openstack/container/v1/capsules/results.go | 62 +- .../container/v1/capsules/testing/fixtures.go | 599 ++++++++++++------ .../v1/capsules/testing/requests_test.go | 303 ++------- results.go | 21 + 4 files changed, 522 insertions(+), 463 deletions(-) diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index 589398ac41..a3661b23b5 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -258,38 +258,80 @@ func ExtractCapsules(r pagination.Page) ([]Capsule, error) { func (r *Capsule) UnmarshalJSON(b []byte) error { type tmp Capsule - var s struct { + + // Support for "older" zun time formats. + var s1 struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoT `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoT `json:"updated_at"` } - err := json.Unmarshal(b, &s) + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = Capsule(s1.tmp) + + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + return nil + } + + // Support for "new" zun time formats. + var s2 struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` + } + + err = json.Unmarshal(b, &s2) if err != nil { return err } - *r = Capsule(s.tmp) - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) + *r = Capsule(s2.tmp) + + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) return nil } func (r *Container) UnmarshalJSON(b []byte) error { type tmp Container - var s struct { + + // Support for "older" zun time formats. + var s1 struct { tmp CreatedAt gophercloud.JSONRFC3339ZNoT `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoT `json:"updated_at"` } - err := json.Unmarshal(b, &s) + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = Container(s1.tmp) + + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + return nil + } + + // Support for "new" zun time formats. + var s2 struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` + } + + err = json.Unmarshal(b, &s2) if err != nil { return err } - *r = Container(s.tmp) - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) + *r = Container(s2.tmp) + + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) return nil } diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index d3c553f3fc..e9cc10ff9c 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -5,6 +5,7 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" th "github.com/gophercloud/gophercloud/testhelper" fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -12,46 +13,46 @@ import ( // ValidJSONTemplate is a valid OpenStack Capsule template in JSON format const ValidJSONTemplate = ` { - "capsuleVersion": "beta", - "kind": "capsule", - "metadata": { - "labels": { - "app": "web", - "app1": "web1" - }, - "name": "template" + "capsuleVersion": "beta", + "kind": "capsule", + "metadata": { + "labels": { + "app": "web", + "app1": "web1" }, - "restartPolicy": "Always", - "spec": { - "containers": [ - { - "command": [ - "/bin/bash" - ], - "env": { - "ENV1": "/usr/local/bin", - "ENV2": "/usr/bin" - }, - "image": "ubuntu", - "imagePullPolicy": "ifnotpresent", - "ports": [ - { - "containerPort": 80, - "hostPort": 80, - "name": "nginx-port", - "protocol": "TCP" - } - ], - "resources": { - "requests": { - "cpu": 1, - "memory": 1024 - } - }, - "workDir": "/root" - } - ] - } + "name": "template" + }, + "restartPolicy": "Always", + "spec": { + "containers": [ + { + "command": [ + "/bin/bash" + ], + "env": { + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin" + }, + "image": "ubuntu", + "imagePullPolicy": "ifnotpresent", + "ports": [ + { + "containerPort": 80, + "hostPort": 80, + "name": "nginx-port", + "protocol": "TCP" + } + ], + "resources": { + "requests": { + "cpu": 1, + "memory": 1024 + } + }, + "workDir": "/root" + } + ] + } } ` @@ -174,163 +175,387 @@ var ValidYAMLTemplateParsed = map[string]interface{}{ }, } -// HandleCapsuleGetSuccessfully test setup -func HandleCapsuleGetSuccessfully(t *testing.T) { +const CapsuleGetBody_OldTime = ` +{ + "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", + "status": "Running", + "id": 1, + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "cpu": 1, + "memory": "1024M", + "meta_name": "test", + "meta_labels": {"web": "app"}, + "created_at": "2018-01-12 09:37:25+00:00", + "updated_at": "2018-01-12 09:37:26+00:00", + "links": [ + { + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self" + }, + { + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark" + } + ], + "capsule_version": "beta", + "restart_policy": "always", + "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b"], + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "volumes_info": { + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" + ] + }, + "host": "test-host", + "status_reason": "No reason", + "containers": [ + { + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "image": "test", + "labels": {"foo": "bar"}, + "created_at": "2018-01-12 09:37:25+00:00", + "updated_at": "2018-01-12 09:37:26+00:00", + "workdir": "/root", + "disk": 0, + "id": 1, + "security_groups": ["default"], + "image_pull_policy": "ifnotpresent", + "task_state": "Creating", + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "uuid": "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", + "hostname": "test-hostname", + "environment": {"USER1": "test"}, + "memory": "1024M", + "status": "Running", + "auto_remove": false, + "container_id": "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", + "websocket_url": "ws://10.10.10.10/", + "auto_heal": false, + "host": "test-host", + "image_driver": "docker", + "status_detail": "Just created", + "status_reason": "No reason", + "websocket_token": "2ba16a5a-552f-422f-b511-bd786102691f", + "name": "test-demo-omicron-13", + "restart_policy": { + "MaximumRetryCount": "0", + "Name": "always" + }, + "ports": [80], + "meta": {"key1": "value1"}, + "command": "testcmd", + "capsule_id": 1, + "runtime": "runc", + "cpu": 1, + "interactive": true + } + ] +}` + +const CapsuleGetBody_NewTime = ` +{ + "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", + "status": "Running", + "id": 1, + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "cpu": 1, + "memory": "1024M", + "meta_name": "test", + "meta_labels": {"web": "app"}, + "created_at": "2018-01-12 09:37:25", + "updated_at": "2018-01-12 09:37:26", + "links": [ + { + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self" + }, + { + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark" + } + ], + "capsule_version": "beta", + "restart_policy": "always", + "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b"], + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "volumes_info": { + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" + ] + }, + "host": "test-host", + "status_reason": "No reason", + "containers": [ + { + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "image": "test", + "labels": {"foo": "bar"}, + "created_at": "2018-01-12 09:37:25", + "updated_at": "2018-01-12 09:37:26", + "workdir": "/root", + "disk": 0, + "id": 1, + "security_groups": ["default"], + "image_pull_policy": "ifnotpresent", + "task_state": "Creating", + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "uuid": "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", + "hostname": "test-hostname", + "environment": {"USER1": "test"}, + "memory": "1024M", + "status": "Running", + "auto_remove": false, + "container_id": "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", + "websocket_url": "ws://10.10.10.10/", + "auto_heal": false, + "host": "test-host", + "image_driver": "docker", + "status_detail": "Just created", + "status_reason": "No reason", + "websocket_token": "2ba16a5a-552f-422f-b511-bd786102691f", + "name": "test-demo-omicron-13", + "restart_policy": { + "MaximumRetryCount": "0", + "Name": "always" + }, + "ports": [80], + "meta": {"key1": "value1"}, + "command": "testcmd", + "capsule_id": 1, + "runtime": "runc", + "cpu": 1, + "interactive": true + } + ] +}` + +const CapsuleListBody = ` +{ + "capsules": [ + { + "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", + "status": "Running", + "id": 1, + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "cpu": 1, + "memory": "1024M", + "meta_name": "test", + "meta_labels": {"web": "app"}, + "created_at": "2018-01-12 09:37:25+00:00", + "updated_at": "2018-01-12 09:37:25+01:00", + "links": [ + { + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self" + }, + { + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark" + } + ], + "capsule_version": "beta", + "restart_policy": "always", + "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "d1469e8d-bcbc-43fc-b163-8b9b6a740930"], + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "volumes_info": { + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" + ] + }, + "host": "test-host", + "status_reason": "No reason" + } + ] +}` + +var ExpectedContainer1 = capsules.Container{ + CapsuleID: 1, + ID: 1, + Name: "test-demo-omicron-13", + UUID: "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", + UserID: "d33b18c384574fd2a3299447aac285f0", + ProjectID: "6b8ffef2a0ac42ee87887b9cc98bdf68", + CPU: float64(1), + Memory: "1024M", + Host: "test-host", + Status: "Running", + Image: "test", + Labels: map[string]string{ + "foo": "bar", + }, + Meta: map[string]string{ + "key1": "value1", + }, + WorkDir: "/root", + Disk: 0, + ContainerID: "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", + Command: "testcmd", + Ports: []int{ + 80, + }, + SecurityGroups: []string{ + "default", + }, + ImagePullPolicy: "ifnotpresent", + Runtime: "runc", + TaskState: "Creating", + HostName: "test-hostname", + Environment: map[string]string{ + "USER1": "test", + }, + WebsocketToken: "2ba16a5a-552f-422f-b511-bd786102691f", + WebsocketUrl: "ws://10.10.10.10/", + StatusReason: "No reason", + StatusDetail: "Just created", + ImageDriver: "docker", + Interactive: true, + AutoRemove: false, + AutoHeal: false, + RestartPolicy: map[string]string{ + "MaximumRetryCount": "0", + "Name": "always", + }, + Addresses: map[string][]capsules.Address{ + "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ + { + PreserveOnDelete: false, + Addr: "172.24.4.11", + Port: "8439060f-381a-4386-a518-33d5a4058636", + Version: float64(4), + SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", + }, + }, + }, +} + +var ExpectedCapsule = capsules.Capsule{ + UUID: "cc654059-1a77-47a3-bfcf-715bde5aad9e", + Status: "Running", + ID: 1, + UserID: "d33b18c384574fd2a3299447aac285f0", + ProjectID: "6b8ffef2a0ac42ee87887b9cc98bdf68", + CPU: float64(1), + Memory: "1024M", + MetaName: "test", + Links: []interface{}{ + map[string]interface{}{ + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark", + }, + }, + CapsuleVersion: "beta", + RestartPolicy: "always", + MetaLabels: map[string]string{ + "web": "app", + }, + ContainersUUIDs: []string{ + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", + }, + Addresses: map[string][]capsules.Address{ + "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ + { + PreserveOnDelete: false, + Addr: "172.24.4.11", + Port: "8439060f-381a-4386-a518-33d5a4058636", + Version: float64(4), + SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", + }, + }, + }, + VolumesInfo: map[string][]string{ + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{ + "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", + }, + }, + Host: "test-host", + StatusReason: "No reason", + Containers: []capsules.Container{ + ExpectedContainer1, + }, +} + +// HandleCapsuleGetOldTimeSuccessfully test setup +func HandleCapsuleGetOldTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ - "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", - "status": "Running", - "id": 1, - "user_id": "d33b18c384574fd2a3299447aac285f0", - "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", - "cpu": 1, - "memory": "1024M", - "meta_name": "test", - "meta_labels": {"web": "app"}, - "created_at": "2018-01-12 09:37:25+00:00", - "updated_at": "2018-01-12 09:37:26+00:00", - "links": [ - { - "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", - "rel": "self" - }, - { - "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", - "rel": "bookmark" - } - ], - "capsule_version": "beta", - "restart_policy": "always", - "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b"], - "addresses": { - "b1295212-64e1-471d-aa01-25ff46f9818d": [ - { - "version": 4, - "preserve_on_delete": false, - "addr": "172.24.4.11", - "port": "8439060f-381a-4386-a518-33d5a4058636", - "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" - } - ] - }, - "volumes_info": { - "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ - "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" - ] - }, - "host": "test-host", - "status_reason": "No reason", - "containers": [ - { - "addresses": { - "b1295212-64e1-471d-aa01-25ff46f9818d": [ - { - "version": 4, - "preserve_on_delete": false, - "addr": "172.24.4.11", - "port": "8439060f-381a-4386-a518-33d5a4058636", - "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" - } - ] - }, - "image": "test", - "labels": {"foo": "bar"}, - "created_at": "2018-01-12 09:37:25+00:00", - "updated_at": "2018-01-12 09:37:26+00:00", - "workdir": "/root", - "disk": 0, - "id": 1, - "security_groups": ["default"], - "image_pull_policy": "ifnotpresent", - "task_state": "Creating", - "user_id": "d33b18c384574fd2a3299447aac285f0", - "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", - "uuid": "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", - "hostname": "test-hostname", - "environment": {"USER1": "test"}, - "memory": "1024M", - "status": "Running", - "auto_remove": false, - "container_id": "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", - "websocket_url": "ws://10.10.10.10/", - "auto_heal": false, - "host": "test-host", - "image_driver": "docker", - "status_detail": "Just created", - "status_reason": "No reason", - "websocket_token": "2ba16a5a-552f-422f-b511-bd786102691f", - "name": "test-demo-omicron-13", - "restart_policy": { - "MaximumRetryCount": "0", - "Name": "always" - }, - "ports": [80], - "meta": {"key1": "value1"}, - "command": "testcmd", - "capsule_id": 1, - "runtime": "runc", - "cpu": 1, - "interactive": true - } - ] - }`) + fmt.Fprintf(w, CapsuleGetBody_OldTime) }) } -const CapsuleListBody = ` -{ - "capsules": [ - { - "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", - "status": "Running", - "id": 1, - "user_id": "d33b18c384574fd2a3299447aac285f0", - "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", - "cpu": 1, - "memory": "1024M", - "meta_name": "test", - "meta_labels": {"web": "app"}, - "created_at": "2018-01-12 09:37:25+00:00", - "updated_at": "2018-01-12 09:37:25+01:00", - "links": [ - { - "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", - "rel": "self" - }, - { - "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", - "rel": "bookmark" - } - ], - "capsule_version": "beta", - "restart_policy": "always", - "containers_uuids": ["1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", "d1469e8d-bcbc-43fc-b163-8b9b6a740930"], - "addresses": { - "b1295212-64e1-471d-aa01-25ff46f9818d": [ - { - "version": 4, - "preserve_on_delete": false, - "addr": "172.24.4.11", - "port": "8439060f-381a-4386-a518-33d5a4058636", - "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" - } - ] - }, - "volumes_info": { - "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": [ - "4b725a92-2197-497b-b6b1-fb8caa4cb99b" - ] - }, - "host": "test-host", - "status_reason": "No reason" - } - ] -}` +// HandleCapsuleGetNewTimeSuccessfully test setup +func HandleCapsuleGetNewTimeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, CapsuleGetBody_NewTime) + }) +} // HandleCapsuleCreateSuccessfully creates an HTTP handler at `/capsules` on the test handler mux // that responds with a `Create` response. diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 41490b23a8..5991e1ea64 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -11,221 +11,54 @@ import ( fakeclient "github.com/gophercloud/gophercloud/testhelper/client" ) -func TestGetCapsule(t *testing.T) { +func TestGetCapsule_OldTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - HandleCapsuleGetSuccessfully(t) + HandleCapsuleGetOldTimeSuccessfully(t) - actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), "cc654059-1a77-47a3-bfcf-715bde5aad9e").Extract() + createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") + updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:26+00:00") + + ExpectedCapsule.CreatedAt = createdAt + ExpectedCapsule.UpdatedAt = updatedAt + ExpectedCapsule.Containers[0].CreatedAt = createdAt + ExpectedCapsule.Containers[0].UpdatedAt = updatedAt + actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() th.AssertNoErr(t, err) - uuid := "cc654059-1a77-47a3-bfcf-715bde5aad9e" - status := "Running" - id := 1 - userID := "d33b18c384574fd2a3299447aac285f0" - projectID := "6b8ffef2a0ac42ee87887b9cc98bdf68" - cpu := float64(1) - memory := "1024M" - metaName := "test" + th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) +} - createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") - updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:26+00:00") - links := []interface{}{ - map[string]interface{}{ - "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", - "rel": "self", - }, - map[string]interface{}{ - "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", - "rel": "bookmark", - }, - } - capsuleVersion := "beta" - restartPolicy := "always" - metaLabels := map[string]string{ - "web": "app", - } - containersUUIDs := []string{ - "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", - } - addresses := map[string][]capsules.Address{ - "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ - { - PreserveOnDelete: false, - Addr: "172.24.4.11", - Port: "8439060f-381a-4386-a518-33d5a4058636", - Version: float64(4), - SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", - }, - }, - } - volumesInfo := map[string][]string{ - "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{ - "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", - }, - } - host := "test-host" - statusReason := "No reason" +func TestGetCapsule_NewTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() - capsuleID := 1 - containerID := 1 - containerName := "test-demo-omicron-13" - containerUUID := "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b" - containerImage := "test" - labels := map[string]string{ - "foo": "bar", - } - meta := map[string]string{ - "key1": "value1", - } - workDir := "/root" - disk := 0 + HandleCapsuleGetNewTimeSuccessfully(t) - containerIDBackend := "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb" - command := "testcmd" - ports := []int{ - 80, - } - securityGroups := []string{ - "default", - } - imagePullPolicy := "ifnotpresent" - runTime := "runc" - taskState := "Creating" - hostName := "test-hostname" - environment := map[string]string{ - "USER1": "test", - } - websocketToken := "2ba16a5a-552f-422f-b511-bd786102691f" - websocketUrl := "ws://10.10.10.10/" - containerStatusReason := "No reason" - statusDetail := "Just created" - imageDriver := "docker" - interactive := true - autoRemove := false - autoHeal := false - containerRestartPolicy := map[string]string{ - "MaximumRetryCount": "0", - "Name": "always", - } + createdAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:25") + updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:26") - container1 := capsules.Container{ - Addresses: addresses, - CreatedAt: createdAt, - UpdatedAt: updatedAt, - UUID: containerUUID, - ID: containerID, - UserID: userID, - ProjectID: projectID, - CPU: cpu, - Status: status, - Memory: memory, - Host: host, - ContainerID: containerIDBackend, - CapsuleID: capsuleID, - Name: containerName, - Image: containerImage, - Labels: labels, - Meta: meta, - WorkDir: workDir, - Disk: disk, - Command: command, - Ports: ports, - SecurityGroups: securityGroups, - ImagePullPolicy: imagePullPolicy, - Runtime: runTime, - TaskState: taskState, - HostName: hostName, - Environment: environment, - WebsocketToken: websocketToken, - WebsocketUrl: websocketUrl, - StatusReason: containerStatusReason, - StatusDetail: statusDetail, - ImageDriver: imageDriver, - AutoHeal: autoHeal, - AutoRemove: autoRemove, - Interactive: interactive, - RestartPolicy: containerRestartPolicy, - } - containers := []capsules.Container{ - container1, - } + ExpectedCapsule.CreatedAt = createdAt + ExpectedCapsule.UpdatedAt = updatedAt + ExpectedCapsule.Containers[0].CreatedAt = createdAt + ExpectedCapsule.Containers[0].UpdatedAt = updatedAt - expectedCapsule := capsules.Capsule{ - UUID: uuid, - ID: id, - UserID: userID, - ProjectID: projectID, - CPU: cpu, - Status: status, - Memory: memory, - MetaName: metaName, - CreatedAt: createdAt, - UpdatedAt: updatedAt, - Links: links, - CapsuleVersion: capsuleVersion, - RestartPolicy: restartPolicy, - MetaLabels: metaLabels, - ContainersUUIDs: containersUUIDs, - Addresses: addresses, - VolumesInfo: volumesInfo, - StatusReason: statusReason, - Host: host, - Containers: containers, - } + actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() + th.AssertNoErr(t, err) - th.AssertDeepEquals(t, &expectedCapsule, actualCapsule) + th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) } func TestCreateCapsule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCapsuleCreateSuccessfully(t) + template := new(capsules.Template) - template.Bin = []byte(`{ - "capsuleVersion": "beta", - "kind": "capsule", - "metadata": { - "labels": { - "app": "web", - "app1": "web1" - }, - "name": "template" - }, - "restartPolicy": "Always", - "spec": { - "containers": [ - { - "command": [ - "/bin/bash" - ], - "env": { - "ENV1": "/usr/local/bin", - "ENV2": "/usr/bin" - }, - "image": "ubuntu", - "imagePullPolicy": "ifnotpresent", - "ports": [ - { - "containerPort": 80, - "hostPort": 80, - "name": "nginx-port", - "protocol": "TCP" - } - ], - "resources": { - "requests": { - "cpu": 1, - "memory": 1024 - } - }, - "workDir": "/root" - } - ] - } - }`) + template.Bin = []byte(ValidJSONTemplate) + createOpts := capsules.CreateOpts{ TemplateOpts: template, } @@ -239,6 +72,15 @@ func TestListCapsule(t *testing.T) { HandleCapsuleListSuccessfully(t) + createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") + updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+01:00") + + ExpectedCapsule.CreatedAt = createdAt + ExpectedCapsule.UpdatedAt = updatedAt + ExpectedCapsule.Containers = nil + + expected := []capsules.Capsule{ExpectedCapsule} + count := 0 results := capsules.List(fakeclient.ServiceClient(), nil) err := results.EachPage(func(page pagination.Page) (bool, error) { @@ -248,78 +90,7 @@ func TestListCapsule(t *testing.T) { t.Errorf("Failed to extract capsules: %v", err) return false, err } - uuid := "cc654059-1a77-47a3-bfcf-715bde5aad9e" - status := "Running" - id := 1 - userID := "d33b18c384574fd2a3299447aac285f0" - projectID := "6b8ffef2a0ac42ee87887b9cc98bdf68" - cpu := float64(1) - memory := "1024M" - metaName := "test" - createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") - updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+01:00") - links := []interface{}{ - map[string]interface{}{ - "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", - "rel": "self", - }, - map[string]interface{}{ - "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", - "rel": "bookmark", - }, - } - capsuleVersion := "beta" - restartPolicy := "always" - metaLabels := map[string]string{ - "web": "app", - } - containersUUIDs := []string{ - "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", - "d1469e8d-bcbc-43fc-b163-8b9b6a740930", - } - addresses := map[string][]capsules.Address{ - "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ - { - PreserveOnDelete: false, - Addr: "172.24.4.11", - Port: "8439060f-381a-4386-a518-33d5a4058636", - Version: float64(4), - SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", - }, - }, - } - volumesInfo := map[string][]string{ - "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{ - "4b725a92-2197-497b-b6b1-fb8caa4cb99b", - }, - } - host := "test-host" - statusReason := "No reason" - - expected := []capsules.Capsule{ - { - UUID: uuid, - ID: id, - UserID: userID, - ProjectID: projectID, - CPU: cpu, - Status: status, - Memory: memory, - MetaName: metaName, - CreatedAt: createdAt, - UpdatedAt: updatedAt, - Links: links, - CapsuleVersion: capsuleVersion, - RestartPolicy: restartPolicy, - MetaLabels: metaLabels, - ContainersUUIDs: containersUUIDs, - Addresses: addresses, - VolumesInfo: volumesInfo, - StatusReason: statusReason, - Host: host, - }, - } th.CheckDeepEquals(t, expected, actual) return true, nil diff --git a/results.go b/results.go index fdd4830ec1..c5f245c152 100644 --- a/results.go +++ b/results.go @@ -366,6 +366,27 @@ func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error { return nil } +// RFC3339ZNoTNoZ is another time format used in Zun (Containers Service). +const RFC3339ZNoTNoZ = "2006-01-02 15:04:05" + +type JSONRFC3339ZNoTNoZ time.Time + +func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339ZNoTNoZ, s) + if err != nil { + return err + } + *jt = JSONRFC3339ZNoTNoZ(t) + return nil +} + /* Link is an internal type to be used in packages of collection resources that are paginated in a certain way. From 47c3c30bf85ec19977636ed6715f47ec0b4f5977 Mon Sep 17 00:00:00 2001 From: liusheng Date: Thu, 14 Jun 2018 20:58:19 +0800 Subject: [PATCH 0384/2296] Expose OS_BRANCH var based on refactors on openlab-zuul-jobs (#1043) * Expose OS_BRANCH var based on refactors on openlab-zuul-jobs * add become yes for ut job to avoid permission error --- .zuul.yaml | 16 +++++++++++----- .../gophercloud-acceptance-test/run.yaml | 6 ++++-- .zuul/playbooks/gophercloud-unittest/run.yaml | 5 ++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 3d4798fe6f..f7ee17223f 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -19,7 +19,8 @@ description: | Run gophercloud acceptance test on queens branch vars: - os_branch: 'stable/queens' + global_env: + OS_BRANCH: stable/queens - job: name: gophercloud-acceptance-test-pike @@ -27,14 +28,17 @@ description: | Run gophercloud acceptance test on pike branch vars: - os_branch: 'stable/pike' + global_env: + OS_BRANCH: stable/pike + - job: name: gophercloud-acceptance-test-ocata parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on ocata branch vars: - os_branch: 'stable/ocata' + global_env: + OS_BRANCH: stable/ocata - job: name: gophercloud-acceptance-test-newton @@ -42,7 +46,8 @@ description: | Run gophercloud acceptance test on newton branch vars: - os_branch: 'stable/newton' + global_env: + OS_BRANCH: stable/newton - job: name: gophercloud-acceptance-test-mitaka @@ -50,7 +55,8 @@ description: | Run gophercloud acceptance test on mitaka branch vars: - os_branch: 'stable/mitaka' + global_env: + OS_BRANCH: stable/mitaka nodeset: ubuntu-trusty - project: diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index 2578351942..8509b7d348 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -1,6 +1,7 @@ - hosts: all become: yes roles: + - config-golang - clone-devstack-gate-to-workspace - role: create-devstack-local-conf enable_services: @@ -15,9 +16,10 @@ set -e set -o pipefail set -x - + echo $(export |grep OS_BRANCH) + go get ./... || true ./script/acceptancetest -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash chdir: '{{ zuul.project.src_dir }}' - environment: '{{ golang_env }}' + environment: '{{ global_env }}' diff --git a/.zuul/playbooks/gophercloud-unittest/run.yaml b/.zuul/playbooks/gophercloud-unittest/run.yaml index 1af36aca13..feefa54f2e 100644 --- a/.zuul/playbooks/gophercloud-unittest/run.yaml +++ b/.zuul/playbooks/gophercloud-unittest/run.yaml @@ -1,4 +1,7 @@ - hosts: all + become: yes + roles: + - config-golang tasks: - name: Run unit tests with gophercloud shell: @@ -10,4 +13,4 @@ ./script/unittest -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash chdir: '{{ zuul.project.src_dir }}' - environment: '{{ golang_env }}' + environment: '{{ global_env }}' From 6cd928389e7eefe1715e188b97fdd07b09a55e62 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 14 Jun 2018 22:31:43 -0400 Subject: [PATCH 0385/2296] [WIP] qos: list rule types (#1030) * qos: list rule types * fix typo in imports for requests test * fix style * fix style in extensions/qos/doc.go * implement fixes requested in code review (move to ruletypes package, put the list into a struct, use pagination, add docs, and update tests to work with the new code) * fix style in requests_test, and move acceptance test to a ruletypes package * gofmt * fix return type syntax for formatter --- .../qos/ruletypes/ruletypes_test.go | 31 ++++++++++++++ .../v2/extensions/qos/ruletypes/doc.go | 19 +++++++++ .../v2/extensions/qos/ruletypes/requests.go | 13 ++++++ .../v2/extensions/qos/ruletypes/results.go | 26 ++++++++++++ .../extensions/qos/ruletypes/testing/doc.go | 2 + .../qos/ruletypes/testing/fixtures.go | 19 +++++++++ .../qos/ruletypes/testing/requests_test.go | 41 +++++++++++++++++++ .../v2/extensions/qos/ruletypes/urls.go | 7 ++++ 8 files changed, 158 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go create mode 100644 openstack/networking/v2/extensions/qos/ruletypes/doc.go create mode 100644 openstack/networking/v2/extensions/qos/ruletypes/requests.go create mode 100644 openstack/networking/v2/extensions/qos/ruletypes/results.go create mode 100644 openstack/networking/v2/extensions/qos/ruletypes/testing/doc.go create mode 100644 openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/qos/ruletypes/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go b/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go new file mode 100644 index 0000000000..e14844b56a --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -0,0 +1,31 @@ +package ruletypes + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/ruletypes" +) + +func TestListRuleTypes(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + return + } + + page, err := ruletypes.ListRuleTypes(client).AllPages() + if err != nil { + t.Fatalf("Failed to list rule types pages: %v", err) + return + } + + ruleTypes, err := ruletypes.ExtractRuleTypes(page) + if err != nil { + t.Fatalf("Failed to list rule types: %v", err) + return + } + + tools.PrintResource(t, ruleTypes) +} diff --git a/openstack/networking/v2/extensions/qos/ruletypes/doc.go b/openstack/networking/v2/extensions/qos/ruletypes/doc.go new file mode 100644 index 0000000000..5cfc6884c1 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/ruletypes/doc.go @@ -0,0 +1,19 @@ +/* +Package ruletypes contains functionality for working with Neutron 'quality of service' rule-type resources. + +Example: You can list rule-types in the following way: + + page, err := ruletypes.ListRuleTypes(client).AllPages() + if err != nil { + return + } + + rules, err := ruletypes.ExtractRuleTypes(page) + if err != nil { + return + } + + fmt.Printf("%v <- Rule Types\n", rules) + +*/ +package ruletypes diff --git a/openstack/networking/v2/extensions/qos/ruletypes/requests.go b/openstack/networking/v2/extensions/qos/ruletypes/requests.go new file mode 100644 index 0000000000..e193cea5a2 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/ruletypes/requests.go @@ -0,0 +1,13 @@ +package ruletypes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListRuleTypes returns the list of rule types from the server +func ListRuleTypes(c *gophercloud.ServiceClient) (result pagination.Pager) { + return pagination.NewPager(c, listRuleTypesURL(c), func(r pagination.PageResult) pagination.Page { + return ListRuleTypesPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/networking/v2/extensions/qos/ruletypes/results.go b/openstack/networking/v2/extensions/qos/ruletypes/results.go new file mode 100644 index 0000000000..f62818819e --- /dev/null +++ b/openstack/networking/v2/extensions/qos/ruletypes/results.go @@ -0,0 +1,26 @@ +package ruletypes + +import "github.com/gophercloud/gophercloud/pagination" + +// The result of listing the qos rule types +type RuleType struct { + Type string `json:"type"` +} + +type ListRuleTypesPage struct { + pagination.SinglePageBase +} + +func (r ListRuleTypesPage) IsEmpty() (bool, error) { + v, err := ExtractRuleTypes(r) + return len(v) == 0, err +} + +func ExtractRuleTypes(r pagination.Page) ([]RuleType, error) { + var s struct { + RuleTypes []RuleType `json:"rule_types"` + } + + err := (r.(ListRuleTypesPage)).ExtractInto(&s) + return s.RuleTypes, err +} diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/doc.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/doc.go new file mode 100644 index 0000000000..1e9c71e8fb --- /dev/null +++ b/openstack/networking/v2/extensions/qos/ruletypes/testing/doc.go @@ -0,0 +1,2 @@ +// qos unit tests +package testing diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go new file mode 100644 index 0000000000..63556695d6 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go @@ -0,0 +1,19 @@ +package testing + +const ( + ListRuleTypesResponse = ` +{ + "rule_types": [ + { + "type": "bandwidth_limit" + }, + { + "type": "dscp_marking" + }, + { + "type": "minimum_bandwidth" + } + ] +} +` +) diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go new file mode 100644 index 0000000000..79559efe1a --- /dev/null +++ b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go @@ -0,0 +1,41 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/ruletypes" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListRuleTypes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/", 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, ListRuleTypesResponse) + }) + + page, err := ruletypes.ListRuleTypes(fake.ServiceClient()).AllPages() + if err != nil { + t.Errorf("Failed to list rule types pages: %v", err) + return + } + + rules, err := ruletypes.ExtractRuleTypes(page) + if err != nil { + t.Errorf("Failed to list rule types: %v", err) + return + } + + expected := []ruletypes.RuleType{{Type: "bandwidth_limit"}, {Type: "dscp_marking"}, {Type: "minimum_bandwidth"}} + th.AssertDeepEquals(t, expected, rules) +} diff --git a/openstack/networking/v2/extensions/qos/ruletypes/urls.go b/openstack/networking/v2/extensions/qos/ruletypes/urls.go new file mode 100644 index 0000000000..15795100c2 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/ruletypes/urls.go @@ -0,0 +1,7 @@ +package ruletypes + +import "github.com/gophercloud/gophercloud" + +func listRuleTypesURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("qos", "rule-types") +} From 74bf3b4933236c9794ac682e2bdd55e0276fbfb5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 Jun 2018 19:42:01 +0000 Subject: [PATCH 0386/2296] Acc Tests: Enable all compute tests --- acceptance/clients/conditions.go | 8 +++ .../openstack/compute/v2/keypairs_test.go | 16 +++++- .../openstack/compute/v2/migrate_test.go | 2 + .../openstack/compute/v2/quotaset_test.go | 4 ++ acceptance/openstack/compute/v2/usage_test.go | 2 + script/acceptancetest | 54 ++++++++++++++----- 6 files changed, 71 insertions(+), 15 deletions(-) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 7bf3f26265..9cbbc0744f 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -58,3 +58,11 @@ func RequireNovaNetwork(t *testing.T) { t.Skip("this test requires nova-network and to set OS_NOVANET to 1") } } + +// 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 { + t.Skipf("this is not supported in %s", release) + } +} diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go index eeb9e75083..4bb0ed97d7 100644 --- a/acceptance/openstack/compute/v2/keypairs_test.go +++ b/acceptance/openstack/compute/v2/keypairs_test.go @@ -16,7 +16,10 @@ import ( const keyName = "gophercloud_test_key_pair" -func TestKeypairsCreateDelete(t *testing.T) { +func TestKeypairsParse(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) @@ -31,6 +34,17 @@ func TestKeypairsCreateDelete(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, keyPair) +} + +func TestKeypairsCreateDelete(t *testing.T) { + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + keyPair, err := CreateKeyPair(t, client) + th.AssertNoErr(t, err) + defer DeleteKeyPair(t, client, keyPair) + + tools.PrintResource(t, keyPair) allPages, err := keypairs.List(client).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/compute/v2/migrate_test.go b/acceptance/openstack/compute/v2/migrate_test.go index 3f61188ae4..0661d12dce 100644 --- a/acceptance/openstack/compute/v2/migrate_test.go +++ b/acceptance/openstack/compute/v2/migrate_test.go @@ -11,6 +11,8 @@ 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 62b2042b8c..867d3fb55c 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -16,6 +16,8 @@ import ( ) func TestQuotasetGet(t *testing.T) { + clients.SkipRelease(t, "master") + client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) @@ -100,6 +102,8 @@ var UpdatedQuotas = quotasets.QuotaSet{ } func TestQuotasetUpdateDelete(t *testing.T) { + clients.SkipRelease(t, "master") + 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 0511f8937c..ab6c71db8c 100644 --- a/acceptance/openstack/compute/v2/usage_test.go +++ b/acceptance/openstack/compute/v2/usage_test.go @@ -14,6 +14,8 @@ import ( ) func TestUsageSingleTenant(t *testing.T) { + t.Skip("This is not passing in OpenLab. Works locally") + clients.RequireLong(t) client, err := clients.NewComputeV2Client() diff --git a/script/acceptancetest b/script/acceptancetest index 32b4805de0..4c56719bfc 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -1,22 +1,48 @@ #!/bin/bash # -set -e -set -o pipefail set -x source `dirname $0`/stackenv +timeout="60m" +failed= + # Run the acceptance tests. +# Check the error code after each suite, but do not exit early if a suite failed. + +# Identity v3 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/identity/v3/ +if [[ $? != 0 ]]; then + failed=1 +fi + +# Networking v2 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ +if [[ $? != 0 ]]; then + failed=1 +fi + +# Block Storage v2 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ +if [[ $? != 0 ]]; then + failed=1 +fi + +# Compute v2 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ +if [[ $? != 0 ]]; then + failed=1 +fi + +# Container v1 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/container/v1/ +if [[ $? != 0 ]]; then + failed=1 +fi + +# If any of the test suites failed, exit 1 +if [[ -n $failed ]]; then + exit 1 +fi -# exec go test -p=1 github.com/gophercloud/gophercloud/acceptance/... $@ - -# Only enable these successful test cases -go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v3/ -go test -v -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ -go test -v -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ -go test -v -tags "fixtures acceptance" -run "SecGroup|Flavor|Keypair" ./acceptance/openstack/compute/v2/ -go test -v -tags "fixtures acceptance" ./acceptance/openstack/container/v1/ -# To enable more after the fix of https://github.com/gophercloud/gophercloud/issues/818 -# go test -v -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ -# go test -v -tags "fixtures acceptance" ./acceptance/openstack/identity/v2/ -# go test -v -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ +exit 0 From 4f00addd8d67f157d7531375e295f44d766fc03e Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Wed, 13 Jun 2018 16:31:01 -0700 Subject: [PATCH 0387/2296] Add Create for Claims --- .../openstack/messaging/v2/claims_test.go | 37 +++++++++++ .../openstack/messaging/v2/messaging.go | 14 ++++ openstack/messaging/v2/claims/doc.go | 21 ++++++ openstack/messaging/v2/claims/requests.go | 66 +++++++++++++++++++ openstack/messaging/v2/claims/results.go | 23 +++++++ openstack/messaging/v2/claims/testing/doc.go | 2 + .../messaging/v2/claims/testing/fixtures.go | 63 ++++++++++++++++++ .../v2/claims/testing/requests_test.go | 25 +++++++ openstack/messaging/v2/claims/urls.go | 12 ++++ 9 files changed, 263 insertions(+) create mode 100644 acceptance/openstack/messaging/v2/claims_test.go create mode 100644 openstack/messaging/v2/claims/doc.go create mode 100644 openstack/messaging/v2/claims/requests.go create mode 100644 openstack/messaging/v2/claims/results.go create mode 100644 openstack/messaging/v2/claims/testing/doc.go create mode 100644 openstack/messaging/v2/claims/testing/fixtures.go create mode 100644 openstack/messaging/v2/claims/testing/requests_test.go create mode 100644 openstack/messaging/v2/claims/urls.go diff --git a/acceptance/openstack/messaging/v2/claims_test.go b/acceptance/openstack/messaging/v2/claims_test.go new file mode 100644 index 0000000000..357c21737f --- /dev/null +++ b/acceptance/openstack/messaging/v2/claims_test.go @@ -0,0 +1,37 @@ +// +build acceptance messaging claims + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" +) + +func TestCRUDClaim(t *testing.T) { + clientID := "3381af92-2b9e-11e3-b191-71861300734c" + + client, err := clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + + createdQueueName, err := CreateQueue(t, client) + defer DeleteQueue(t, client, createdQueueName) + + clientID = "3381af92-2b9e-11e3-b191-71861300734d" + + client, err = clients.NewMessagingV2Client(clientID) + if err != nil { + t.Fatalf("Unable to create a messaging service client: %v", err) + } + for i := 0; i < 3; i++ { + CreateMessage(t, client, createdQueueName) + } + + clientID = "3381af92-2b9e-11e3-b191-7186130073dd" + claimedMessages, err := CreateClaim(t, client, createdQueueName) + + tools.PrintResource(t, claimedMessages) +} diff --git a/acceptance/openstack/messaging/v2/messaging.go b/acceptance/openstack/messaging/v2/messaging.go index 5b98cafdba..187bbafcbf 100644 --- a/acceptance/openstack/messaging/v2/messaging.go +++ b/acceptance/openstack/messaging/v2/messaging.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/pagination" @@ -108,3 +109,16 @@ func ListMessages(t *testing.T, client *gophercloud.ServiceClient, queueName str }) return allMessages, err } + +func CreateClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string) ([]claims.Messages, error) { + createOpts := claims.CreateOpts{} + + t.Logf("Attempting to create claim on queue: %s", queueName) + claimedMessages, err := claims.Create(client, queueName, createOpts).Extract() + tools.PrintResource(t, claimedMessages) + if err != nil { + t.Fatalf("Unable to create claim: %v", err) + } + + return claimedMessages, err +} diff --git a/openstack/messaging/v2/claims/doc.go b/openstack/messaging/v2/claims/doc.go new file mode 100644 index 0000000000..48cf777d95 --- /dev/null +++ b/openstack/messaging/v2/claims/doc.go @@ -0,0 +1,21 @@ +/* +Package claims provides information and interaction with the Zaqar API +claims resource for the OpenStack Messaging service. + +Example to Create a Claim on a specified Zaqar queue + + createOpts := claims.CreateOpts{ + TTL: 60, + Grace: 120, + Limit: 20, + } + + queueName := "my_queue" + + messages, err := claims.Create(messagingClient, queueName, createOpts).Extract() + if err != nil { + panic(err) + } + +*/ +package claims diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go new file mode 100644 index 0000000000..cca2ca9b18 --- /dev/null +++ b/openstack/messaging/v2/claims/requests.go @@ -0,0 +1,66 @@ +package claims + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder Builder. +type CreateOptsBuilder interface { + ToClaimCreateRequest() (map[string]interface{}, string, error) +} + +// CreateOpts params to be used with Create. +type CreateOpts struct { + // Sets the TTL for the claim. When the claim expires un-deleted messages will be able to be claimed again. + TTL int `json:"ttl,omitempty"` + + // Sets the Grace period for the claimed messages. The server extends the lifetime of claimed messages + // to be at least as long as the lifetime of the claim itself, plus the specified grace period. + Grace int `json:"grace,omitempty"` + + // Set the limit of messages returned by create. + Limit int `q:"limit" json:"-"` +} + +// ToClaimCreateRequest assembles a body and URL for a Create request based on +// the contents of a CreateOpts. +func (opts CreateOpts) ToClaimCreateRequest() (map[string]interface{}, string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return nil, q.String(), err + } + + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return b, "", err + } + return b, q.String(), err +} + +// Create creates a Claim that claims messages on a specified queue. +func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOptsBuilder) (r CreateResult) { + b, q, err := opts.ToClaimCreateRequest() + if err != nil { + r.Err = err + return + } + + url := createURL(client, queueName) + if q != "" { + url += q + } + + var resp *http.Response + resp, r.Err = client.Post(url, b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201, 204}, + }) + // If the Claim has no content return an empty CreateResult + if resp.StatusCode == 204 { + r.Body = CreateResult{} + } else { + r.Body = resp.Body + } + return +} diff --git a/openstack/messaging/v2/claims/results.go b/openstack/messaging/v2/claims/results.go new file mode 100644 index 0000000000..eae4dc1b00 --- /dev/null +++ b/openstack/messaging/v2/claims/results.go @@ -0,0 +1,23 @@ +package claims + +import "github.com/gophercloud/gophercloud" + +func (r CreateResult) Extract() ([]Messages, error) { + var s struct { + Messages []Messages `json:"messages"` + } + err := r.ExtractInto(&s) + return s.Messages, err +} + +// CreateResult is the response of a Create operations. +type CreateResult struct { + gophercloud.Result +} + +type Messages struct { + Age float32 `json:"age"` + Href string `json:"href"` + TTL int `json:"ttl"` + Body map[string]interface{} `json:"body"` +} diff --git a/openstack/messaging/v2/claims/testing/doc.go b/openstack/messaging/v2/claims/testing/doc.go new file mode 100644 index 0000000000..787309df54 --- /dev/null +++ b/openstack/messaging/v2/claims/testing/doc.go @@ -0,0 +1,2 @@ +// Claims unit tests +package testing diff --git a/openstack/messaging/v2/claims/testing/fixtures.go b/openstack/messaging/v2/claims/testing/fixtures.go new file mode 100644 index 0000000000..26dbd8e68b --- /dev/null +++ b/openstack/messaging/v2/claims/testing/fixtures.go @@ -0,0 +1,63 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +// QueueName is the name of the queue +var QueueName = "FakeTestQueue" + +var ClaimID = "51db7067821e727dc24df754" + +var ClientID = "1234567890" + +// CreateClaimResponse is a sample response to a create claim +const CreateClaimResponse = ` +{ + "messages": [ + { + "body": {"event": "BackupStarted"}, + "href": "/v2/queues/FakeTestQueue/messages/51db6f78c508f17ddc924357?claim_id=51db7067821e727dc24df754", + "age": 57, + "ttl": 300 + } + ] +}` + +// CreateClaimRequest is a sample request to create a claim. +const CreateClaimRequest = ` +{ + "ttl": 3600, + "grace": 3600 +} +` + +// CreatedClaim is the result of a create request. +var CreatedClaim = []claims.Messages{ + { + Age: 57, + Href: fmt.Sprintf("/v2/queues/%s/messages/51db6f78c508f17ddc924357?claim_id=%s", QueueName, ClaimID), + TTL: 300, + Body: map[string]interface{}{"event": "BackupStarted"}, + }, +} + +// HandleCreateSuccessfully configures the test server to respond to a Create request. +func HandleCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims", QueueName), + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, CreateClaimRequest) + + w.WriteHeader(http.StatusCreated) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, CreateClaimResponse) + }) +} diff --git a/openstack/messaging/v2/claims/testing/requests_test.go b/openstack/messaging/v2/claims/testing/requests_test.go new file mode 100644 index 0000000000..75a6a88106 --- /dev/null +++ b/openstack/messaging/v2/claims/testing/requests_test.go @@ -0,0 +1,25 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t) + + createOpts := claims.CreateOpts{ + TTL: 3600, + Grace: 3600, + Limit: 10, + } + + actual, err := claims.Create(fake.ServiceClient(), QueueName, createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, CreatedClaim, actual) +} diff --git a/openstack/messaging/v2/claims/urls.go b/openstack/messaging/v2/claims/urls.go new file mode 100644 index 0000000000..8177ea05d7 --- /dev/null +++ b/openstack/messaging/v2/claims/urls.go @@ -0,0 +1,12 @@ +package claims + +import "github.com/gophercloud/gophercloud" + +const ( + apiVersion = "v2" + apiName = "queues" +) + +func createURL(client *gophercloud.ServiceClient, queueName string) string { + return client.ServiceURL(apiVersion, apiName, queueName, "claims") +} From d65f319521d9992aee31cd796a12acfe8a60292b Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 16 Jun 2018 13:52:35 +0800 Subject: [PATCH 0388/2296] Fix the issue of unmarshal string into Go struct field StoragePool.capabilities The MaxOverSubscriptionRatio type should be string because max_over_subscription_ratio returned by cinder is a string, not float. --- 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 3da8f80c30..9f5361195e 100644 --- a/openstack/blockstorage/extensions/schedulerstats/results.go +++ b/openstack/blockstorage/extensions/schedulerstats/results.go @@ -23,7 +23,7 @@ type Capabilities struct { LocationInfo string `json:"location_info"` QoSSupport bool `json:"QoS_support"` ProvisionedCapacityGB float64 `json:"provisioned_capacity_gb"` - MaxOverSubscriptionRatio float64 `json:"max_over_subscription_ratio"` + MaxOverSubscriptionRatio string `json:"max_over_subscription_ratio"` ThinProvisioningSupport bool `json:"thin_provisioning_support"` ThickProvisioningSupport bool `json:"thick_provisioning_support"` TotalVolumes int64 `json:"total_volumes"` From dadbe83d506262f131eb45871a61ac8ff7288cef Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 16 Jun 2018 20:31:05 +0000 Subject: [PATCH 0389/2296] Acc Tests: Enabling Block Storage v3 tests --- script/acceptancetest | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/acceptancetest b/script/acceptancetest index 4c56719bfc..c26ed456b6 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -28,6 +28,12 @@ if [[ $? != 0 ]]; then failed=1 fi +# Block Storage v3 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v3/ +if [[ $? != 0 ]]; then + failed=1 +fi + # Compute v2 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ if [[ $? != 0 ]]; then From a7932fd1aefb857419e6fb08801b08702aeaa0c5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 16 Jun 2018 21:24:34 +0000 Subject: [PATCH 0390/2296] Acc Tests: Updating Block Storage v3 tests --- .../openstack/blockstorage/v3/blockstorage.go | 118 ++++++++++++------ .../blockstorage/v3/snapshots_test.go | 83 ++++-------- .../openstack/blockstorage/v3/volumes_test.go | 65 ++++------ .../blockstorage/v3/volumetypes_test.go | 75 ++++------- 4 files changed, 148 insertions(+), 193 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index 4fedc8b816..aee4fcea01 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -10,15 +10,45 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" + th "github.com/gophercloud/gophercloud/testhelper" ) +// CreateSnapshot will create a snapshot of the specified volume. +// Snapshot will be assigned a random name and description. +func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { + snapshotName := tools.RandomString("ACPTTEST", 16) + snapshotDescription := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create snapshot: %s", snapshotName) + + createOpts := snapshots.CreateOpts{ + VolumeID: volume.ID, + Name: snapshotName, + Description: snapshotDescription, + } + + snapshot, err := snapshots.Create(client, createOpts).Extract() + if err != nil { + return snapshot, err + } + + err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + if err != nil { + return snapshot, err + } + + th.AssertEquals(t, snapshot.Name, snapshotName) + th.AssertEquals(t, snapshot.VolumeID, volume.ID) + tools.PrintResource(t, snapshot) + + t.Logf("Successfully created snapshot: %s", snapshot.ID) + + return snapshot, nil +} + // CreateVolume will create a volume with a random name and size of 1GB. An // error will be returned if the volume was unable to be created. func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { - if testing.Short() { - t.Skip("Skipping test that requires volume creation in short mode.") - } - volumeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume: %s", volumeName) @@ -37,48 +67,37 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } - return volume, nil -} + th.AssertEquals(t, volume.Name, volumeName) + tools.PrintResource(t, volume) -// DeleteVolume will delete a volume. A fatal error will occur if the volume -// failed to be deleted. This works best when used as a deferred function. -func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { - err := volumes.Delete(client, volume.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) - } + t.Logf("Successfully created volume: %s", volume.ID) - t.Logf("Deleted volume: %s", volume.ID) + return volume, nil } -// CreateSnapshot will create a snapshot of the specified volume. -// Snapshot will be assigned a random name and description. -func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { - if testing.Short() { - t.Skip("Skipping test that requires snapshot creation in short mode.") - } - - snapshotName := tools.RandomString("ACPTTEST", 16) - snapshotDescription := tools.RandomString("ACPTTEST", 16) - t.Logf("Attempting to create snapshot: %s", snapshotName) - - createOpts := snapshots.CreateOpts{ - VolumeID: volume.ID, - Name: snapshotName, - Description: snapshotDescription, +// CreateVolumeType will create a volume type with a random name. An +// error will be returned if the volume was unable to be created. +func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) { + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create volume type: %s", name) + + createOpts := volumetypes.CreateOpts{ + Name: name, + ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, + Description: "create_from_gophercloud", } - snapshot, err := snapshots.Create(client, createOpts).Extract() + vt, err := volumetypes.Create(client, createOpts).Extract() if err != nil { - return snapshot, err + return nil, err } - err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) - if err != nil { - return snapshot, err - } + th.AssertEquals(t, vt.IsPublic, true) + tools.PrintResource(t, vt) - return snapshot, nil + t.Logf("Successfully created volume type: %s", vt.ID) + + return vt, nil } // DeleteSnapshot will delete a snapshot. A fatal error will occur if the @@ -105,3 +124,30 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s t.Logf("Deleted snapshot: %s", snapshot.ID) } + +// DeleteVolume will delete a volume. A fatal error will occur if the volume +// failed to be deleted. This works best when used as a deferred function. +func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { + t.Logf("Attempting to delete volume: %s", volume.ID) + + err := volumes.Delete(client, volume.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) + } + + t.Logf("Successfully deleted volume: %s", volume.ID) +} + +// DeleteVolumeType will delete a volume type. A fatal error will occur if the +// volume type failed to be deleted. This works best when used as a deferred +// function. +func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, vt *volumetypes.VolumeType) { + t.Logf("Attempting to delete volume type: %s", vt.ID) + + err := volumetypes.Delete(client, vt.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", vt.ID, err) + } + + t.Logf("Successfully deleted volume type: %s", vt.ID) +} diff --git a/acceptance/openstack/blockstorage/v3/snapshots_test.go b/acceptance/openstack/blockstorage/v3/snapshots_test.go index f8fdc1f0f6..60bfb70597 100644 --- a/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -6,90 +6,53 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestSnapshotsList(t *testing.T) { +func TestSnapshots(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewBlockStorageV3Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) volume1, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - + th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume1) snapshot1, err := CreateSnapshot(t, client, volume1) - if err != nil { - t.Fatalf("Unable to create snapshot: %v", err) - } - + th.AssertNoErr(t, err) defer DeleteSnapshot(t, client, snapshot1) volume2, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - + th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume2) snapshot2, err := CreateSnapshot(t, client, volume2) - if err != nil { - t.Fatalf("Unable to create snapshot: %v", err) - } - + th.AssertNoErr(t, err) defer DeleteSnapshot(t, client, snapshot2) - pages := 0 - err = snapshots.List(client, snapshots.ListOpts{Limit: 1}).EachPage(func(page pagination.Page) (bool, error) { - pages++ + listOpts := snapshots.ListOpts{ + Limit: 1, + } + err = snapshots.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { actual, err := snapshots.ExtractSnapshots(page) - if err != nil { - t.Fatalf("Unable to extract snapshots: %v", err) - } - - if len(actual) != 1 { - t.Fatalf("Expected 1 snapshot, got %d", len(actual)) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + + var found bool + for _, v := range actual { + if v.ID == snapshot1.ID || v.ID == snapshot2.ID { + found = true + } } - tools.PrintResource(t, actual[0]) + th.AssertEquals(t, found, true) return true, nil }) - if pages != 2 { - t.Fatalf("Expected 2 pages, saw %d", pages) - } -} - -func TestSnapshotsCreateDelete(t *testing.T) { - client, err := clients.NewBlockStorageV3Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } - - volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - defer DeleteVolume(t, client, volume) - - snapshot, err := CreateSnapshot(t, client, volume) - if err != nil { - t.Fatalf("Unable to create snapshot: %v", err) - } - defer DeleteSnapshot(t, client, snapshot) - - newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve snapshot: %v", err) - } - - tools.PrintResource(t, newSnapshot) + th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index 2e349111d4..32ad756c0a 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -6,68 +6,45 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestVolumesList(t *testing.T) { +func TestVolumes(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewBlockStorageV3Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) volume1, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume1) volume2, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume2) - pages := 0 - err = volumes.List(client, volumes.ListOpts{Limit: 1}).EachPage(func(page pagination.Page) (bool, error) { - pages++ + listOpts := volumes.ListOpts{ + Limit: 1, + } + err = volumes.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { actual, err := volumes.ExtractVolumes(page) - if err != nil { - t.Fatalf("Unable to extract volumes: %v", err) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + + var found bool + for _, v := range actual { + if v.ID == volume1.ID || v.ID == volume2.ID { + found = true + } } - if len(actual) != 1 { - t.Fatalf("Expected 1 volume, got %d", len(actual)) - } - - tools.PrintResource(t, actual[0]) + th.AssertEquals(t, found, true) return true, nil }) - if pages != 2 { - t.Fatalf("Expected 2 pages, saw %d", pages) - } -} - -func TestVolumesCreateDelete(t *testing.T) { - client, err := clients.NewBlockStorageV3Client() - if err != nil { - t.Fatalf("Unable to create blockstorage client: %v", err) - } - - volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - defer DeleteVolume(t, client, volume) - - newVolume, err := volumes.Get(client, volume.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve volume: %v", err) - } - - tools.PrintResource(t, newVolume) + th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 8e14c3afb8..635d054731 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -11,72 +11,41 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestVolumeTypesList(t *testing.T) { +func TestVolumeTypes(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewBlockStorageV3Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) - listOpts := volumetypes.ListOpts{ - Sort: "name:asc", - Limit: 1, - } + vt, err := CreateVolumeType(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) - allPages, err := volumetypes.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve volumetypes: %v", err) - } + allPages, err := volumetypes.List(client, nil).AllPages() + th.AssertNoErr(t, err) allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) - if err != nil { - t.Fatalf("Unable to extract volumetypes: %v", err) - } + th.AssertNoErr(t, err) - for _, vt := range allVolumeTypes { - tools.PrintResource(t, vt) - } - - if len(allVolumeTypes) > 0 { - vt, err := volumetypes.Get(client, allVolumeTypes[0].ID).Extract() - if err != nil { - t.Fatalf("Error retrieving volume type: %v", err) + var found bool + for _, v := range allVolumeTypes { + tools.PrintResource(t, v) + if v.ID == vt.ID { + found = true } - - tools.PrintResource(t, vt) - } -} - -func TestVolumeTypesCRUD(t *testing.T) { - client, err := clients.NewBlockStorageV3Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } - - createOpts := volumetypes.CreateOpts{ - Name: "create_from_gophercloud", - ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, - Description: "create_from_gophercloud", - } - - vt, err := volumetypes.Create(client, createOpts).Extract() - if err != nil { - t.Fatalf("Unable to create volumetype: %v", err) } - th.AssertEquals(t, true, vt.IsPublic) - - tools.PrintResource(t, vt) - - defer volumetypes.Delete(client, vt.ID) + th.AssertEquals(t, found, true) var isPublic = false - - newVT, err := volumetypes.Update(client, vt.ID, volumetypes.UpdateOpts{ - Name: "updated_volume_type", + updateOpts := volumetypes.UpdateOpts{ + Name: vt.Name + "-UPDATED", IsPublic: &isPublic, - }).Extract() + } - th.AssertEquals(t, "updated_volume_type", newVT.Name) + newVT, err := volumetypes.Update(client, vt.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, vt.Name+"-UPDATED", newVT.Name) th.AssertEquals(t, false, newVT.IsPublic) tools.PrintResource(t, newVT) From f97a16e3fefc1a349f2c9563512aa5cb0bb7bd41 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 16 Jun 2018 21:24:40 +0000 Subject: [PATCH 0391/2296] Acc Tests: Updating Block Storage v2 tests --- .../openstack/blockstorage/v2/blockstorage.go | 79 ++++++++++--------- .../blockstorage/v2/snapshots_test.go | 54 +++++-------- .../openstack/blockstorage/v2/volumes_test.go | 68 ++++++---------- 3 files changed, 90 insertions(+), 111 deletions(-) diff --git a/acceptance/openstack/blockstorage/v2/blockstorage.go b/acceptance/openstack/blockstorage/v2/blockstorage.go index 7b4682bbf1..5b6ea75e06 100644 --- a/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -14,13 +14,37 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) +// CreateSnapshot will create a snapshot of the specified volume. +// Snapshot will be assigned a random name and description. +func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { + snapshotName := tools.RandomString("ACPTTEST", 16) + snapshotDescription := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create snapshot: %s", snapshotName) + + createOpts := snapshots.CreateOpts{ + VolumeID: volume.ID, + Name: snapshotName, + Description: snapshotDescription, + } + + snapshot, err := snapshots.Create(client, createOpts).Extract() + if err != nil { + return snapshot, err + } + + err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + if err != nil { + return snapshot, err + } + + t.Logf("Successfully created snapshot: %s", snapshot.ID) + + return snapshot, nil +} + // CreateVolume will create a volume with a random name and size of 1GB. An // error will be returned if the volume was unable to be created. func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { - if testing.Short() { - t.Skip("Skipping test that requires volume creation in short mode.") - } - volumeName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create volume: %s", volumeName) @@ -39,6 +63,13 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } + th.AssertEquals(t, volume.Name, volumeName) + th.AssertEquals(t, volume.Size, 1) + + tools.PrintResource(t, volume) + + t.Logf("Successfully created volume: %s", volume.ID) + return volume, nil } @@ -77,53 +108,29 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo th.AssertEquals(t, newVolume.Name, volumeName) th.AssertEquals(t, newVolume.Size, 1) + t.Logf("Successfully created volume from image: %s", newVolume.ID) + return newVolume, nil } // DeleteVolume will delete a volume. A fatal error will occur if the volume // failed to be deleted. This works best when used as a deferred function. func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { + t.Logf("Attempting to delete volume: %s", volume.ID) + err := volumes.Delete(client, volume.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } - t.Logf("Deleted volume: %s", volume.ID) -} - -// CreateSnapshot will create a snapshot of the specified volume. -// Snapshot will be assigned a random name and description. -func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { - if testing.Short() { - t.Skip("Skipping test that requires snapshot creation in short mode.") - } - - snapshotName := tools.RandomString("ACPTTEST", 16) - snapshotDescription := tools.RandomString("ACPTTEST", 16) - t.Logf("Attempting to create snapshot: %s", snapshotName) - - createOpts := snapshots.CreateOpts{ - VolumeID: volume.ID, - Name: snapshotName, - Description: snapshotDescription, - } - - snapshot, err := snapshots.Create(client, createOpts).Extract() - if err != nil { - return snapshot, err - } - - err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) - if err != nil { - return snapshot, err - } - - return snapshot, nil + t.Logf("Successfully deleted volume: %s", volume.ID) } // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { + t.Logf("Attempting to delete snapshot: %s", snapshot.ID) + err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) @@ -143,5 +150,5 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s t.Fatalf("Error waiting for snapshot to delete: %v", err) } - t.Logf("Deleted snapshot: %s", snapshot.ID) + t.Logf("Successfully deleted snapshot: %s", snapshot.ID) } diff --git a/acceptance/openstack/blockstorage/v2/snapshots_test.go b/acceptance/openstack/blockstorage/v2/snapshots_test.go index 7c1a4e5a56..a24f4dceff 100644 --- a/acceptance/openstack/blockstorage/v2/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v2/snapshots_test.go @@ -8,51 +8,39 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestSnapshotsList(t *testing.T) { - client, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } - - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve snapshots: %v", err) - } +func TestSnapshots(t *testing.T) { + clients.RequireLong(t) - allSnapshots, err := snapshots.ExtractSnapshots(allPages) - if err != nil { - t.Fatalf("Unable to extract snapshots: %v", err) - } - - for _, snapshot := range allSnapshots { - tools.PrintResource(t, snapshot) - } -} - -func TestSnapshotsCreateDelete(t *testing.T) { client, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume) snapshot, err := CreateSnapshot(t, client, volume) - if err != nil { - t.Fatalf("Unable to create snapshot: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSnapshot(t, client, snapshot) newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve snapshot: %v", err) + th.AssertNoErr(t, err) + + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allSnapshots, err := snapshots.ExtractSnapshots(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allSnapshots { + tools.PrintResource(t, snapshot) + if v.ID == newSnapshot.ID { + found = true + } } - tools.PrintResource(t, newSnapshot) + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go index 630b21e492..03f659f5e1 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -9,69 +9,53 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestVolumesList(t *testing.T) { - client, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } - - allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() - if err != nil { - t.Fatalf("Unable to retrieve volumes: %v", err) - } - - allVolumes, err := volumes.ExtractVolumes(allPages) - if err != nil { - t.Fatalf("Unable to extract volumes: %v", err) - } - - for _, volume := range allVolumes { - tools.PrintResource(t, volume) - } -} - func TestVolumesCreateDestroy(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create blockstorage client: %v", err) - } + th.AssertNoErr(t, err) volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume) newVolume, err := volumes.Get(client, volume.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve volume: %v", err) + th.AssertNoErr(t, err) + + allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allVolumes, err := volumes.ExtractVolumes(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allVolumes { + tools.PrintResource(t, volume) + if v.ID == newVolume.ID { + found = true + } } - tools.PrintResource(t, newVolume) + th.AssertEquals(t, found, true) } func TestVolumesCreateForceDestroy(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create blockstorage client: %v", err) - } + th.AssertNoErr(t, err) volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) newVolume, err := volumes.Get(client, volume.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve volume: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newVolume) err = volumeactions.ForceDelete(client, newVolume.ID).ExtractErr() - if err != nil { - t.Errorf("Unable to force delete volume: %v", err) - } + th.AssertNoErr(t, err) } From 5b89ea36590f7ffd4aefb8947fa26dd473a18586 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Mon, 18 Jun 2018 12:07:33 -0700 Subject: [PATCH 0392/2296] Add get to claims --- .../openstack/messaging/v2/claims_test.go | 10 +++++ .../openstack/messaging/v2/messaging.go | 38 ++++++++++++++++ openstack/messaging/v2/claims/doc.go | 9 ++++ openstack/messaging/v2/claims/requests.go | 8 ++++ openstack/messaging/v2/claims/results.go | 18 ++++++++ .../messaging/v2/claims/testing/fixtures.go | 45 ++++++++++++++++++- .../v2/claims/testing/requests_test.go | 10 +++++ openstack/messaging/v2/claims/urls.go | 4 ++ 8 files changed, 140 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/messaging/v2/claims_test.go b/acceptance/openstack/messaging/v2/claims_test.go index 357c21737f..9fcc7a6f55 100644 --- a/acceptance/openstack/messaging/v2/claims_test.go +++ b/acceptance/openstack/messaging/v2/claims_test.go @@ -32,6 +32,16 @@ func TestCRUDClaim(t *testing.T) { clientID = "3381af92-2b9e-11e3-b191-7186130073dd" claimedMessages, err := CreateClaim(t, client, createdQueueName) + claimIDs, _ := ExtractIDs(claimedMessages) tools.PrintResource(t, claimedMessages) + + for _, claimID := range claimIDs { + updatedClaim, getErr := GetClaim(t, client, createdQueueName, claimID) + if getErr != nil { + t.Fatalf("Unable to retrieve claim %s: %v", claimID, getErr) + } + + tools.PrintResource(t, updatedClaim) + } } diff --git a/acceptance/openstack/messaging/v2/messaging.go b/acceptance/openstack/messaging/v2/messaging.go index 187bbafcbf..8f8d972fdc 100644 --- a/acceptance/openstack/messaging/v2/messaging.go +++ b/acceptance/openstack/messaging/v2/messaging.go @@ -1,6 +1,7 @@ package v2 import ( + "strings" "testing" "github.com/gophercloud/gophercloud" @@ -122,3 +123,40 @@ func CreateClaim(t *testing.T, client *gophercloud.ServiceClient, queueName stri return claimedMessages, err } + +func GetClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string, claimID string) (*claims.Claim, error) { + t.Logf("Attempting to get claim: %s", claimID) + claim, err := claims.Get(client, queueName, claimID).Extract() + if err != nil { + t.Fatalf("Unable to get claim: %s", claimID) + } + + return claim, err +} + +func ExtractIDs(claim []claims.Messages) ([]string, []string) { + var claimIDs []string + var messageID []string + + for _, msg := range claim { + parts := strings.Split(msg.Href, "?claim_id=") + if len(parts) == 2 { + pieces := strings.Split(parts[0], "/") + if len(pieces) > 0 { + messageID = append(messageID, pieces[len(pieces)-1]) + } + claimIDs = append(claimIDs, parts[1]) + } + } + encountered := map[string]bool{} + for v := range claimIDs { + encountered[claimIDs[v]] = true + } + + var uniqueClaimIDs []string + + for key := range encountered { + uniqueClaimIDs = append(uniqueClaimIDs, key) + } + return uniqueClaimIDs, messageID +} diff --git a/openstack/messaging/v2/claims/doc.go b/openstack/messaging/v2/claims/doc.go index 48cf777d95..26579a11ad 100644 --- a/openstack/messaging/v2/claims/doc.go +++ b/openstack/messaging/v2/claims/doc.go @@ -17,5 +17,14 @@ Example to Create a Claim on a specified Zaqar queue panic(err) } +Example to get a claim for a specified Zaqar queue + + queueName := "my_queue" + + claim, err := claims.Get(messagingClient, queueName, claimID).Extract() + if err != nil { + panic(err) + } + */ package claims diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index cca2ca9b18..4d457557a7 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -64,3 +64,11 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts } return } + +// Get queries the specified claim for the specified queue. +func Get(client *gophercloud.ServiceClient, queueName string, claimID string) (r GetResult) { + _, r.Err = client.Get(getURL(client, queueName, claimID), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/messaging/v2/claims/results.go b/openstack/messaging/v2/claims/results.go index eae4dc1b00..dda91b07ef 100644 --- a/openstack/messaging/v2/claims/results.go +++ b/openstack/messaging/v2/claims/results.go @@ -10,14 +10,32 @@ func (r CreateResult) Extract() ([]Messages, error) { return s.Messages, err } +func (r GetResult) Extract() (*Claim, error) { + var s *Claim + err := r.ExtractInto(&s) + return s, err +} + // CreateResult is the response of a Create operations. type CreateResult struct { gophercloud.Result } +// GetResult is the response of a Get operations. +type GetResult struct { + gophercloud.Result +} + type Messages struct { Age float32 `json:"age"` Href string `json:"href"` TTL int `json:"ttl"` Body map[string]interface{} `json:"body"` } + +type Claim struct { + Age float32 `json:"age"` + Href string `json:"href"` + Messages []Messages `json:"messages"` + TTL int `json:"ttl"` +} diff --git a/openstack/messaging/v2/claims/testing/fixtures.go b/openstack/messaging/v2/claims/testing/fixtures.go index 26dbd8e68b..bcb6bcd7cb 100644 --- a/openstack/messaging/v2/claims/testing/fixtures.go +++ b/openstack/messaging/v2/claims/testing/fixtures.go @@ -15,8 +15,6 @@ var QueueName = "FakeTestQueue" var ClaimID = "51db7067821e727dc24df754" -var ClientID = "1234567890" - // CreateClaimResponse is a sample response to a create claim const CreateClaimResponse = ` { @@ -30,6 +28,22 @@ const CreateClaimResponse = ` ] }` +// GetClaimResponse is a sample response to a get claim +const GetClaimResponse = ` +{ + "age": 50, + "href": "/v2/queues/demoqueue/claims/51db7067821e727dc24df754", + "messages": [ + { + "body": {"event": "BackupStarted"}, + "href": "/v2/queues/FakeTestQueue/messages/51db6f78c508f17ddc924357?claim_id=51db7067821e727dc24df754", + "age": 57, + "ttl": 300 + } + ], + "ttl": 50 +}` + // CreateClaimRequest is a sample request to create a claim. const CreateClaimRequest = ` { @@ -48,6 +62,21 @@ var CreatedClaim = []claims.Messages{ }, } +// FirstClaim is the result of a get claim. +var FirstClaim = claims.Claim{ + Age: 50, + Href: "/v2/queues/demoqueue/claims/51db7067821e727dc24df754", + Messages: []claims.Messages{ + { + Age: 57, + Href: fmt.Sprintf("/v2/queues/%s/messages/51db6f78c508f17ddc924357?claim_id=%s", QueueName, ClaimID), + TTL: 300, + Body: map[string]interface{}{"event": "BackupStarted"}, + }, + }, + TTL: 50, +} + // HandleCreateSuccessfully configures the test server to respond to a Create request. func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims", QueueName), @@ -61,3 +90,15 @@ func HandleCreateSuccessfully(t *testing.T) { fmt.Fprintf(w, CreateClaimResponse) }) } + +// HandleGetSuccessfully configures the test server to respond to a Get request. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims/%s", QueueName, ClaimID), + 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, GetClaimResponse) + }) +} diff --git a/openstack/messaging/v2/claims/testing/requests_test.go b/openstack/messaging/v2/claims/testing/requests_test.go index 75a6a88106..141e7755fe 100644 --- a/openstack/messaging/v2/claims/testing/requests_test.go +++ b/openstack/messaging/v2/claims/testing/requests_test.go @@ -23,3 +23,13 @@ func TestCreate(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, CreatedClaim, actual) } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := claims.Get(fake.ServiceClient(), QueueName, ClaimID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &FirstClaim, actual) +} diff --git a/openstack/messaging/v2/claims/urls.go b/openstack/messaging/v2/claims/urls.go index 8177ea05d7..a11394ed94 100644 --- a/openstack/messaging/v2/claims/urls.go +++ b/openstack/messaging/v2/claims/urls.go @@ -10,3 +10,7 @@ const ( func createURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "claims") } + +func getURL(client *gophercloud.ServiceClient, queueName string, claimId string) string { + return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimId) +} From ea493ecfefc0ff430e0cd195b0263dd93babc56d Mon Sep 17 00:00:00 2001 From: Artem Tsebrovskiy Date: Tue, 19 Jun 2018 11:21:32 +0300 Subject: [PATCH 0393/2296] fix downloading with range OkCode --- openstack/objectstorage/v1/objects/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index b13cbd1c86..d1f854cf7b 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -125,7 +125,7 @@ func Download(c *gophercloud.ServiceClient, containerName, objectName string, op resp, err := c.Get(url, nil, &gophercloud.RequestOpts{ MoreHeaders: h, - OkCodes: []int{200, 304}, + OkCodes: []int{200, 206, 304}, }) if resp != nil { r.Header = resp.Header From 1b5321768bbd295b3207b7af0a59f2d2638807a8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 15 Jun 2018 04:29:18 +0000 Subject: [PATCH 0394/2296] Clustering v1: actions updates --- openstack/clustering/v1/actions/requests.go | 2 +- openstack/clustering/v1/actions/results.go | 37 +--- .../clustering/v1/actions/testing/fixtures.go | 166 +++++++++++++++ .../v1/actions/testing/requests_test.go | 194 +----------------- 4 files changed, 188 insertions(+), 211 deletions(-) create mode 100644 openstack/clustering/v1/actions/testing/fixtures.go diff --git a/openstack/clustering/v1/actions/requests.go b/openstack/clustering/v1/actions/requests.go index 27ffd18e96..ac27421c67 100644 --- a/openstack/clustering/v1/actions/requests.go +++ b/openstack/clustering/v1/actions/requests.go @@ -10,7 +10,7 @@ type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` Sort string `q:"sort"` - GlobalProject string `q:"global_project"` + GlobalProject *bool `q:"global_project"` Name string `q:"name"` Target string `q:"target"` Action string `q:"action"` diff --git a/openstack/clustering/v1/actions/results.go b/openstack/clustering/v1/actions/results.go index 5f29338142..a06422a2c2 100644 --- a/openstack/clustering/v1/actions/results.go +++ b/openstack/clustering/v1/actions/results.go @@ -2,8 +2,6 @@ package actions import ( "encoding/json" - "fmt" - "reflect" "time" "github.com/gophercloud/gophercloud" @@ -77,42 +75,29 @@ func (r *Action) UnmarshalJSON(b []byte) error { type tmp Action var s struct { tmp - CreatedAt interface{} `json:"created_at"` - UpdatedAt interface{} `json:"updated_at"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { return err } + *r = Action(s.tmp) - switch t := s.CreatedAt.(type) { - case string: - if t != "" { - r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.CreatedAt != "" { + r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) + if err != nil { + return err } - case nil: - r.CreatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) } - switch t := s.UpdatedAt.(type) { - case string: - if t != "" { - r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.UpdatedAt != "" { + r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) + if err != nil { + return err } - case nil: - r.UpdatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) } return nil diff --git a/openstack/clustering/v1/actions/testing/fixtures.go b/openstack/clustering/v1/actions/testing/fixtures.go new file mode 100644 index 0000000000..4709b7c516 --- /dev/null +++ b/openstack/clustering/v1/actions/testing/fixtures.go @@ -0,0 +1,166 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ListResponse = ` +{ + "actions": [ + { + "action": "NODE_DELETE", + "cause": "RPC Request", + "created_at": "2015-11-04T05:21:41Z", + "data": {}, + "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "end_time": 1425550000.0, + "id": "edce3528-864f-41fb-8759-f4707925cc09", + "inputs": {}, + "interval": -1, + "name": "node_delete_f0de9b9c", + "outputs": {}, + "owner": null, + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "start_time": 1425550000.0, + "status": "SUCCEEDED", + "status_reason": "Action completed successfully.", + "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + "timeout": 3600, + "updated_at": "2016-11-04T05:21:41Z", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + }, + { + "action": "NODE_DELETE", + "cause": "RPC Request", + "created_at": null, + "data": {}, + "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "end_time": 1425550000.0, + "id": "edce3528-864f-41fb-8759-f4707925cc09", + "inputs": {}, + "interval": -1, + "name": "node_delete_f0de9b9c", + "outputs": {}, + "owner": null, + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "start_time": 1425550000.0, + "status": "SUCCEEDED", + "status_reason": "Action completed successfully.", + "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + "timeout": 3600, + "updated_at": "", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + } + ] +} +` + +const GetResponse = ` +{ + "action": { + "action": "NODE_DELETE", + "cause": "RPC Request", + "created_at": "2015-11-04T05:21:41Z", + "data": {}, + "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], + "end_time": 1425550000.0, + "id": "edce3528-864f-41fb-8759-f4707925cc09", + "inputs": {}, + "interval": -1, + "name": "node_delete_f0de9b9c", + "outputs": {}, + "owner": null, + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "start_time": 1425550000.0, + "status": "SUCCEEDED", + "status_reason": "Action completed successfully.", + "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + "timeout": 3600, + "updated_at": "2016-11-04T05:21:41Z", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + } +} +` + +var ExpectedAction1 = actions.Action{ + Action: "NODE_DELETE", + Cause: "RPC Request", + CreatedAt: time.Date(2015, 11, 4, 5, 21, 41, 0, time.UTC), + Data: map[string]interface{}{}, + DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + EndTime: 1425550000.0, + ID: "edce3528-864f-41fb-8759-f4707925cc09", + Inputs: make(map[string]interface{}), + Interval: -1, + Name: "node_delete_f0de9b9c", + Outputs: make(map[string]interface{}), + Owner: "", + Project: "f1fe61dcda2f4618a14c10dc7abc214d", + StartTime: 1425550000.0, + Status: "SUCCEEDED", + StatusReason: "Action completed successfully.", + Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + Timeout: 3600, + UpdatedAt: time.Date(2016, 11, 4, 5, 21, 41, 0, time.UTC), + User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", +} + +var ExpectedAction2 = actions.Action{ + Action: "NODE_DELETE", + Cause: "RPC Request", + CreatedAt: time.Time{}, + Data: map[string]interface{}{}, + DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, + EndTime: 1425550000.0, + ID: "edce3528-864f-41fb-8759-f4707925cc09", + Inputs: make(map[string]interface{}), + Interval: -1, + Name: "node_delete_f0de9b9c", + Outputs: make(map[string]interface{}), + Owner: "", + Project: "f1fe61dcda2f4618a14c10dc7abc214d", + StartTime: 1425550000.0, + Status: "SUCCEEDED", + StatusReason: "Action completed successfully.", + Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", + Timeout: 3600, + UpdatedAt: time.Time{}, + User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", +} + +var ExpectedActions = []actions.Action{ExpectedAction1, ExpectedAction2} + +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/actions", 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, ListResponse) + }) +} + +func HandleGetSuccessfully(t *testing.T, id string) { + th.Mux.HandleFunc("/v1/actions/"+id, 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, GetResponse) + }) +} diff --git a/openstack/clustering/v1/actions/testing/requests_test.go b/openstack/clustering/v1/actions/testing/requests_test.go index 1c74ad9be6..6a4e5f1592 100644 --- a/openstack/clustering/v1/actions/testing/requests_test.go +++ b/openstack/clustering/v1/actions/testing/requests_test.go @@ -1,10 +1,7 @@ package testing import ( - "fmt" - "net/http" "testing" - "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" "github.com/gophercloud/gophercloud/pagination" @@ -16,129 +13,20 @@ func TestListActions(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/actions", 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, ` - { - "actions": [ - { - "action": "NODE_DELETE", - "cause": "RPC Request", - "created_at": "2015-11-04T05:21:41Z", - "data": {}, - "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "end_time": 1425550000.0, - "id": "edce3528-864f-41fb-8759-f4707925cc09", - "inputs": {}, - "interval": -1, - "name": "node_delete_f0de9b9c", - "outputs": {}, - "owner": null, - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "start_time": 1425550000.0, - "status": "SUCCEEDED", - "status_reason": "Action completed successfully.", - "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - "timeout": 3600, - "updated_at": "2016-11-04T05:21:41Z", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - }, - { - "action": "NODE_DELETE", - "cause": "RPC Request", - "created_at": null, - "data": {}, - "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "end_time": 1425550000.0, - "id": "edce3528-864f-41fb-8759-f4707925cc09", - "inputs": {}, - "interval": -1, - "name": "node_delete_f0de9b9c", - "outputs": {}, - "owner": null, - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "start_time": 1425550000.0, - "status": "SUCCEEDED", - "status_reason": "Action completed successfully.", - "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - "timeout": 3600, - "updated_at": "", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - } - ] - }`) - }) + HandleListSuccessfully(t) pageCount := 0 - actions.List(fake.ServiceClient(), actions.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := actions.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pageCount++ actual, err := actions.ExtractActions(page) - if err != nil { - t.Errorf("Failed to extract actions: %v", err) - return false, err - } - - createdAt, _ := time.Parse(time.RFC3339, "2015-11-04T05:21:41Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-11-04T05:21:41Z") - expected := []actions.Action{ - { - Action: "NODE_DELETE", - Cause: "RPC Request", - CreatedAt: createdAt, - Data: map[string]interface{}{}, - DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - EndTime: 1425550000.0, - ID: "edce3528-864f-41fb-8759-f4707925cc09", - Inputs: make(map[string]interface{}), - Interval: -1, - Name: "node_delete_f0de9b9c", - Outputs: make(map[string]interface{}), - Owner: "", - Project: "f1fe61dcda2f4618a14c10dc7abc214d", - StartTime: 1425550000.0, - Status: "SUCCEEDED", - StatusReason: "Action completed successfully.", - Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - Timeout: 3600, - UpdatedAt: updatedAt, - User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", - }, - { - Action: "NODE_DELETE", - Cause: "RPC Request", - CreatedAt: time.Time{}, - Data: map[string]interface{}{}, - DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - EndTime: 1425550000.0, - ID: "edce3528-864f-41fb-8759-f4707925cc09", - Inputs: make(map[string]interface{}), - Interval: -1, - Name: "node_delete_f0de9b9c", - Outputs: make(map[string]interface{}), - Owner: "", - Project: "f1fe61dcda2f4618a14c10dc7abc214d", - StartTime: 1425550000.0, - Status: "SUCCEEDED", - StatusReason: "Action completed successfully.", - Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - Timeout: 3600, - UpdatedAt: time.Time{}, - User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", - }, - } + th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, actual) + th.AssertDeepEquals(t, ExpectedActions, actual) return true, nil }) + th.AssertNoErr(t, err) + if pageCount != 1 { t.Errorf("Expected 1 page, got %d", pageCount) } @@ -148,71 +36,9 @@ func TestGetAction(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/actions/edce3528-864f-41fb-8759-f4707925cc09", 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) + HandleGetSuccessfully(t, ExpectedAction1.ID) - fmt.Fprintf(w, ` - { - "action": { - "action": "NODE_DELETE", - "cause": "RPC Request", - "created_at": "2015-11-04T05:21:41Z", - "data": {}, - "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "end_time": 1425550000.0, - "id": "edce3528-864f-41fb-8759-f4707925cc09", - "inputs": {}, - "interval": -1, - "name": "node_delete_f0de9b9c", - "outputs": {}, - "owner": null, - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "start_time": 1425550000.0, - "status": "SUCCEEDED", - "status_reason": "Action completed successfully.", - "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - "timeout": 3600, - "updated_at": "2016-11-04T05:21:41Z", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - } - }`) - }) - - createdAt, _ := time.Parse(time.RFC3339, "2015-11-04T05:21:41Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-11-04T05:21:41Z") - expected := actions.Action{ - Action: "NODE_DELETE", - Cause: "RPC Request", - CreatedAt: createdAt, - Data: map[string]interface{}{}, - DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - EndTime: 1425550000.0, - ID: "edce3528-864f-41fb-8759-f4707925cc09", - Inputs: make(map[string]interface{}), - Interval: -1, - Name: "node_delete_f0de9b9c", - Outputs: make(map[string]interface{}), - Owner: "", - Project: "f1fe61dcda2f4618a14c10dc7abc214d", - StartTime: 1425550000.0, - Status: "SUCCEEDED", - StatusReason: "Action completed successfully.", - Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - Timeout: 3600, - UpdatedAt: updatedAt, - User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", - } - - actual, err := actions.Get(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() - if err != nil { - t.Errorf("Failed retrieving action. %v", err) - } else { - th.AssertDeepEquals(t, expected, *actual) - } + actual, err := actions.Get(fake.ServiceClient(), ExpectedAction1.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAction1, *actual) } From 8459401f91d3a2f947e71a16c52e2cd4a1e11dad Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 15 Jun 2018 05:57:34 +0000 Subject: [PATCH 0395/2296] Clustering v1: clusters updates --- openstack/clustering/v1/clusters/results.go | 60 +- .../v1/clusters/testing/fixtures.go | 444 +++++++ .../v1/clusters/testing/requests_test.go | 1086 +---------------- 3 files changed, 496 insertions(+), 1094 deletions(-) create mode 100644 openstack/clustering/v1/clusters/testing/fixtures.go diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index d5f46bac5e..df71ecb420 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -2,8 +2,6 @@ package clusters import ( "encoding/json" - "fmt" - "reflect" "time" "github.com/gophercloud/gophercloud" @@ -59,12 +57,9 @@ func (r commonResult) Extract() (*Cluster, error) { var s struct { Cluster *Cluster `json:"cluster"` } - err := r.ExtractInto(&s) - if err != nil { - return s.Cluster, err - } - return s.Cluster, nil + err := r.ExtractInto(&s) + return s.Cluster, err } // ClusterPage contains a single page of all clusters from a List call. @@ -90,9 +85,9 @@ func (r *Cluster) UnmarshalJSON(b []byte) error { type tmp Cluster var s struct { tmp - CreatedAt interface{} `json:"created_at"` - InitAt interface{} `json:"init_at"` - UpdatedAt interface{} `json:"updated_at"` + CreatedAt string `json:"created_at"` + InitAt string `json:"init_at"` + UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) @@ -101,46 +96,25 @@ func (r *Cluster) UnmarshalJSON(b []byte) error { } *r = Cluster(s.tmp) - switch t := s.CreatedAt.(type) { - case string: - if t != "" { - r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.CreatedAt != "" { + r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, s.CreatedAt) + if err != nil { + return err } - case nil: - r.CreatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) } - switch t := s.InitAt.(type) { - case string: - if t != "" { - r.InitAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.InitAt != "" { + r.InitAt, err = time.Parse(gophercloud.RFC3339Milli, s.InitAt) + if err != nil { + return err } - case nil: - r.InitAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.InitAt)) } - switch t := s.UpdatedAt.(type) { - case string: - if t != "" { - r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.UpdatedAt != "" { + r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, s.UpdatedAt) + if err != nil { + return err } - case nil: - r.UpdatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) } return nil diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go new file mode 100644 index 0000000000..aa8ce49065 --- /dev/null +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -0,0 +1,444 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ClusterResponse = ` +{ + "cluster": { + "config": {}, + "created_at": "2015-02-10T14:26:14Z", + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "2015-02-10T15:26:14Z", + "max_size": 20, + "metadata": {}, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "2015-02-10T16:26:14Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +var ExpectedCluster = clusters.Cluster{ + Config: map[string]interface{}{}, + CreatedAt: time.Date(2015, 2, 10, 14, 26, 14, 0, time.UTC), + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 3, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: time.Date(2015, 2, 10, 15, 26, 14, 0, time.UTC), + MaxSize: 20, + Metadata: map[string]interface{}{}, + MinSize: 1, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: time.Date(2015, 2, 10, 16, 26, 14, 0, time.UTC), + User: "5e5bf8027826429c96af157f68dc9072", +} + +const ClusterResponse_EmptyTime = ` +{ + "cluster": { + "config": {}, + "created_at": null, + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": null, + "max_size": 20, + "metadata": {}, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +var ExpectedCluster_EmptyTime = clusters.Cluster{ + Config: map[string]interface{}{}, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 3, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + MaxSize: 20, + Metadata: map[string]interface{}{}, + MinSize: 1, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + User: "5e5bf8027826429c96af157f68dc9072", +} + +const ClusterResponse_Metadata = ` +{ + "cluster": { + "config": {}, + "created_at": "2015-02-10T14:26:14Z", + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "2015-02-10T15:26:14Z", + "max_size": 20, + "metadata": { + "test": { + "nil_interface": null, + "bool_value": false, + "string_value": "test_string", + "float_value": 123.3 + }, + "foo": "bar" + }, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "2015-02-10T16:26:14Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +var ExpectedCluster_Metadata = clusters.Cluster{ + Config: map[string]interface{}{}, + CreatedAt: time.Date(2015, 2, 10, 14, 26, 14, 0, time.UTC), + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + DesiredCapacity: 3, + Domain: "", + ID: "7d85f602-a948-4a30-afd4-e84f47471c15", + InitAt: time.Date(2015, 2, 10, 15, 26, 14, 0, time.UTC), + MaxSize: 20, + MinSize: 1, + Metadata: map[string]interface{}{ + "foo": "bar", + "test": map[string]interface{}{ + "nil_interface": interface{}(nil), + "float_value": float64(123.3), + "string_value": "test_string", + "bool_value": false, + }, + }, + Name: "cluster1", + Nodes: []string{ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac", + }, + Policies: []string{}, + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", + ProfileName: "mystack", + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Status: "ACTIVE", + StatusReason: "Cluster scale-in succeeded", + Timeout: 3600, + UpdatedAt: time.Date(2015, 2, 10, 16, 26, 14, 0, time.UTC), + User: "5e5bf8027826429c96af157f68dc9072", +} + +const ListResponse = ` +{ + "clusters": [ + { + "config": {}, + "created_at": "2015-02-10T14:26:14Z", + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "2015-02-10T15:26:14Z", + "max_size": 20, + "min_size": 1, + "metadata": {}, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "2015-02-10T16:26:14Z", + "user": "5e5bf8027826429c96af157f68dc9072" + }, + { + "config": {}, + "created_at": null, + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": null, + "max_size": 20, + "metadata": {}, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } + ] +}` + +var ExpectedClusters = []clusters.Cluster{ExpectedCluster, ExpectedCluster_EmptyTime} + +const UpdateResponse = ` +{ + "cluster": { + "config": {}, + "created_at": "2015-02-10T14:26:14Z", + "data": {}, + "dependents": {}, + "desired_capacity": 4, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": "2015-02-10T15:26:14Z", + "max_size": -1, + "metadata": {}, + "min_size": 0, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "profile1", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": "2015-02-10T16:26:14Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +const UpdateResponse_EmptyTime = ` +{ + "cluster": { + "config": {}, + "created_at": null, + "data": {}, + "dependents": {}, + "desired_capacity": 3, + "domain": null, + "id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "init_at": null, + "max_size": 20, + "metadata": {}, + "min_size": 1, + "name": "cluster1", + "nodes": [ + "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", + "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", + "da1e9c87-e584-4626-a120-022da5062dac" + ], + "policies": [], + "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", + "profile_name": "mystack", + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "status": "ACTIVE", + "status_reason": "Cluster scale-in succeeded", + "timeout": 3600, + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +func HandleCreateClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ClusterResponse) + }) +} + +func HandleCreateClusterEmptyTimeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ClusterResponse_EmptyTime) + }) +} + +func HandleCreateClusterMetadataSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ClusterResponse_Metadata) + }) +} + +func HandleGetClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ClusterResponse) + }) +} + +func HandleGetClusterEmptyTimeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ClusterResponse_EmptyTime) + }) +} + +func HandleListClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters", 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, ListResponse) + }) +} + +func HandleUpdateClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+ExpectedCluster.ID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ClusterResponse) + }) +} + +func HandleUpdateClusterEmptyTimeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+ExpectedCluster_EmptyTime.ID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, UpdateResponse_EmptyTime) + }) +} + +func HandleDeleteClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/6dc6d336e3fc4c0a951b5698cd1236ee", 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) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 4d12c1cda1..24600c06ad 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -1,13 +1,9 @@ package testing import ( - "fmt" - "net/http" "strings" "testing" - "time" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -18,47 +14,7 @@ func TestCreateCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") - w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": "2015-02-10T14:26:14Z", - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "2015-02-10T15:26:14Z", - "max_size": 20, - "metadata": {}, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "2015-02-10T16:26:14Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) + HandleCreateClusterSuccessfully(t) minSize := 1 opts := clusters.CreateOpts{ @@ -72,104 +28,26 @@ func TestCreateCluster(t *testing.T) { Config: map[string]interface{}{}, } - createdAt, _ := time.Parse(time.RFC3339, "2015-02-10T14:26:14Z") - initAt, _ := time.Parse(time.RFC3339, "2015-02-10T15:26:14Z") - updatedAt, _ := time.Parse(time.RFC3339, "2015-02-10T16:26:14Z") - - createResult := clusters.Create(fake.ServiceClient(), opts) - if createResult.Err != nil { - t.Error("Error creating cluster. error=", createResult.Err) - } + res := clusters.Create(fake.ServiceClient(), opts) + th.AssertNoErr(t, res.Err) - location := createResult.Header.Get("Location") + location := res.Header.Get("Location") th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d", location) - actionID := "" locationFields := strings.Split(location, "actions/") - if len(locationFields) >= 2 { - actionID = locationFields[1] - } + actionID := locationFields[1] th.AssertEquals(t, "625628cd-f877-44be-bde0-fec79f84e13d", actionID) - actual, err := createResult.Extract() - if err != nil { - t.Error("Error creating cluster. error=", err) - } else { - expected := clusters.Cluster{ - Config: map[string]interface{}{}, - CreatedAt: createdAt, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 3, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: initAt, - MaxSize: 20, - Metadata: map[string]interface{}{}, - MinSize: 1, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: updatedAt, - User: "5e5bf8027826429c96af157f68dc9072", - } - th.AssertDeepEquals(t, expected, *actual) - } + actual, err := res.Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCluster, *actual) } func TestCreateClusterEmptyTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": null, - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": null, - "max_size": 20, - "metadata": {}, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) + HandleCreateClusterEmptyTimeSuccessfully(t) minSize := 1 opts := clusters.CreateOpts{ @@ -184,212 +62,15 @@ func TestCreateClusterEmptyTime(t *testing.T) { } actual, err := clusters.Create(fake.ServiceClient(), opts).Extract() - if err != nil { - t.Error("Error creating cluster. error=", err) - } else { - expected := clusters.Cluster{ - Config: map[string]interface{}{}, - CreatedAt: time.Time{}, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 3, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: time.Time{}, - MaxSize: 20, - Metadata: map[string]interface{}{}, - MinSize: 1, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: time.Time{}, - User: "5e5bf8027826429c96af157f68dc9072", - } - th.AssertDeepEquals(t, expected, *actual) - } -} - -func TestCreateClusterInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": 123456789.0, - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": 123456789.0, - "max_size": 20, - "metadata": {}, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": 123456789.0, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - minSize := 1 - opts := clusters.CreateOpts{ - Name: "cluster1", - DesiredCapacity: 3, - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - MinSize: &minSize, - MaxSize: 20, - Timeout: 3600, - Metadata: map[string]interface{}{}, - Config: map[string]interface{}{}, - } - - _, err := clusters.Create(fake.ServiceClient(), opts).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestCreateClusterInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": "invalid", - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "invalid", - "max_size": 20, - "metadata": {}, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "invalid", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - minSize := 1 - opts := clusters.CreateOpts{ - Name: "cluster1", - DesiredCapacity: 3, - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - MinSize: &minSize, - MaxSize: 20, - Timeout: 3600, - Metadata: map[string]interface{}{}, - Config: map[string]interface{}{}, - } - - _, err := clusters.Create(fake.ServiceClient(), opts).Extract() - th.AssertEquals(t, false, err == nil) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } func TestCreateClusterMetadata(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": "invalid", - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "invalid", - "max_size": 20, - "metadata": { - "test": { - "nil_interface": null, - "bool_value": false, - "string_value": "test_string", - "float_value": 123.3 - }, - "foo": "bar" - }, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "invalid", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) + HandleCreateClusterMetadataSuccessfully(t) minSize := 1 opts := clusters.CreateOpts{ @@ -411,474 +92,46 @@ func TestCreateClusterMetadata(t *testing.T) { Config: map[string]interface{}{}, } - _, err := clusters.Create(fake.ServiceClient(), opts).Extract() - th.AssertEquals(t, false, err == nil) + actual, err := clusters.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCluster_Metadata, *actual) } func TestGetCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ` - { - "cluster": { - "config": {}, - "created_at": "2015-02-10T14:26:14Z", - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "2015-02-10T15:26:14Z", - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "2015-02-10T16:26:14Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - createdAt, _ := time.Parse(time.RFC3339, "2015-02-10T14:26:14Z") - initAt, _ := time.Parse(time.RFC3339, "2015-02-10T15:26:14Z") - updatedAt, _ := time.Parse(time.RFC3339, "2015-02-10T16:26:14Z") - expected := clusters.Cluster{ - Config: map[string]interface{}{}, - CreatedAt: createdAt, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 4, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: initAt, - MaxSize: -1, - Metadata: map[string]interface{}{}, - MinSize: 0, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: updatedAt, - User: "5e5bf8027826429c96af157f68dc9072", - } + HandleGetClusterSuccessfully(t) actual, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() - if err != nil { - t.Errorf("Failed Get cluster. %v", err) - } else { - th.AssertDeepEquals(t, expected, *actual) - } + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCluster, *actual) } func TestGetClusterEmptyTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ` - { - "cluster": { - "config": {}, - "created_at": null, - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": null, - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - expected := clusters.Cluster{ - Config: map[string]interface{}{}, - CreatedAt: time.Time{}, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 4, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: time.Time{}, - MaxSize: -1, - Metadata: map[string]interface{}{}, - MinSize: 0, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: time.Time{}, - User: "5e5bf8027826429c96af157f68dc9072", - } + HandleGetClusterEmptyTimeSuccessfully(t) actual, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() - if err != nil { - t.Errorf("Failed Get cluster. %v", err) - } else { - th.AssertDeepEquals(t, expected, *actual) - } -} - -func TestGetClusterInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ` - { - "cluster": { - "config": {}, - "created_at": 123456789.0, - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": 123456789.0, - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": 123456789.0, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - _, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestGetClusterInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ` - { - "cluster": { - "config": {}, - "created_at": "invalid", - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "invalid", - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "invalid", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - _, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() - th.AssertEquals(t, false, err == nil) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } func TestListClusters(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters", 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, ` - { - "clusters": [ - { - "config": {}, - "created_at": "2015-02-10T14:26:14Z", - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "2015-02-10T15:26:14Z", - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "2015-02-10T16:26:14Z", - "user": "5e5bf8027826429c96af157f68dc9072" - }, - { - "config": {}, - "created_at": "", - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "", - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "", - "user": "5e5bf8027826429c96af157f68dc9072" - }, - { - "config": {}, - "created_at": null, - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": null, - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } - ] - }`) - }) + HandleListClusterSuccessfully(t) count := 0 clusters.List(fake.ServiceClient(), clusters.ListOpts{GlobalProject: new(bool)}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := clusters.ExtractClusters(page) - if err != nil { - t.Errorf("Failed to extract clusters: %v", err) - return false, err - } - - createdAt, _ := time.Parse(gophercloud.RFC3339Milli, "2015-02-10T14:26:14Z") - initAt, _ := time.Parse(gophercloud.RFC3339Milli, "2015-02-10T15:26:14Z") - updatedAt, _ := time.Parse(gophercloud.RFC3339Milli, "2015-02-10T16:26:14Z") - expected := []clusters.Cluster{ - { - Config: map[string]interface{}{}, - CreatedAt: createdAt, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 4, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: initAt, - MaxSize: -1, - Metadata: map[string]interface{}{}, - MinSize: 0, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: updatedAt, - User: "5e5bf8027826429c96af157f68dc9072", - }, - { - Config: map[string]interface{}{}, - CreatedAt: time.Time{}, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 4, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: time.Time{}, - MaxSize: -1, - Metadata: map[string]interface{}{}, - MinSize: 0, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: time.Time{}, - User: "5e5bf8027826429c96af157f68dc9072", - }, - { - Config: map[string]interface{}{}, - CreatedAt: time.Time{}, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 4, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: time.Time{}, - MaxSize: -1, - Metadata: map[string]interface{}{}, - MinSize: 0, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: time.Time{}, - User: "5e5bf8027826429c96af157f68dc9072", - }, - } - - th.AssertDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedClusters, actual) return true, nil }) @@ -892,308 +145,39 @@ func TestUpdateCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - id := "7d85f602-a948-4a30-afd4-e84f47471c15" - clusterName := "cluster1" - profileID := "edc63d0a-2ca4-48fa-9854-27926da76a4a" - profileName := "profile1" - - th.Mux.HandleFunc("/v1/clusters/"+id, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": "2015-02-10T14:26:14Z", - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "%s", - "init_at": "2015-02-10T15:26:14Z", - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "%s", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "%s", - "profile_name": "%s", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "2015-02-10T16:26:14Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`, id, clusterName, profileID, profileName) - }) - - createdAt, _ := time.Parse(time.RFC3339, "2015-02-10T14:26:14Z") - initAt, _ := time.Parse(time.RFC3339, "2015-02-10T15:26:14Z") - updatedAt, _ := time.Parse(time.RFC3339, "2015-02-10T16:26:14Z") - expected := clusters.Cluster{ - Config: map[string]interface{}{}, - CreatedAt: createdAt, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 4, - Domain: "", - ID: id, - InitAt: initAt, - MaxSize: -1, - Metadata: map[string]interface{}{}, - MinSize: 0, - Name: clusterName, - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: profileID, - ProfileName: profileName, - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: updatedAt, - User: "5e5bf8027826429c96af157f68dc9072", - } + HandleUpdateClusterSuccessfully(t) updateOpts := clusters.UpdateOpts{ - Name: clusterName, - ProfileID: profileID, + Name: "cluster1", + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", } - actual, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() + actual, err := clusters.Update(fake.ServiceClient(), ExpectedCluster.ID, updateOpts).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, *actual) + th.AssertDeepEquals(t, ExpectedCluster, *actual) } func TestUpdateClusterEmptyTime(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - id := "7d85f602-a948-4a30-afd4-e84f47471c15" - clusterName := "cluster1" - profileID := "edc63d0a-2ca4-48fa-9854-27926da76a4a" - profileName := "profile1" - - th.Mux.HandleFunc("/v1/clusters/"+id, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": null, - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "%s", - "init_at": null, - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "%s", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "%s", - "profile_name": "%s", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`, id, clusterName, profileID, profileName) - }) - - expected := clusters.Cluster{ - Config: map[string]interface{}{}, - CreatedAt: time.Time{}, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 4, - Domain: "", - ID: id, - InitAt: time.Time{}, - MaxSize: -1, - Metadata: map[string]interface{}{}, - MinSize: 0, - Name: clusterName, - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: profileID, - ProfileName: profileName, - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: time.Time{}, - User: "5e5bf8027826429c96af157f68dc9072", - } + HandleUpdateClusterEmptyTimeSuccessfully(t) updateOpts := clusters.UpdateOpts{ - Name: clusterName, - ProfileID: profileID, + Name: "cluster1", + ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", } - actual, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() + actual, err := clusters.Update(fake.ServiceClient(), ExpectedCluster_EmptyTime.ID, updateOpts).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, *actual) -} - -func TestUpdateClusterInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - id := "7d85f602-a948-4a30-afd4-e84f47471c15" - clusterName := "cluster1" - profileID := "edc63d0a-2ca4-48fa-9854-27926da76a4a" - profileName := "profile1" - - th.Mux.HandleFunc("/v1/clusters/"+id, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": 123456789.0, - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "%s", - "init_at": 123456789.0, - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "%s", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "%s", - "profile_name": "%s", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": 123456789.0, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`, id, clusterName, profileID, profileName) - }) - - updateOpts := clusters.UpdateOpts{ - Name: clusterName, - ProfileID: profileID, - } - - _, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestUpdateClusterInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - id := "7d85f602-a948-4a30-afd4-e84f47471c15" - clusterName := "cluster1" - profileID := "edc63d0a-2ca4-48fa-9854-27926da76a4a" - profileName := "profile1" - - th.Mux.HandleFunc("/v1/clusters/"+id, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "cluster": { - "config": {}, - "created_at": "invalid", - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "%s", - "init_at": "invalid", - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "%s", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "%s", - "profile_name": "%s", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "invalid", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`, id, clusterName, profileID, profileName) - }) - - updateOpts := clusters.UpdateOpts{ - Name: clusterName, - ProfileID: profileID, - } - - _, err := clusters.Update(fake.ServiceClient(), id, updateOpts).Extract() - th.AssertEquals(t, false, err == nil) + th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } func TestDeleteCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/6dc6d336e3fc4c0a951b5698cd1236ee", 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) - }) + HandleDeleteClusterSuccessfully(t) err := clusters.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").ExtractErr() th.AssertNoErr(t, err) From 4955f735c513ad8660cf8256863dd0174e15de4e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 16 Jun 2018 02:14:24 +0000 Subject: [PATCH 0396/2296] Clustering v1: policies updates --- openstack/clustering/v1/policies/requests.go | 13 +- openstack/clustering/v1/policies/results.go | 6 +- .../v1/policies/testing/fixtures.go | 260 ++++++++---------- 3 files changed, 132 insertions(+), 147 deletions(-) diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index 5401351f24..16e319d515 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -17,19 +17,22 @@ type ListOpts struct { // Limit limits the number of Policies to return. Limit int `q:"limit"` - // Marker and Limit control paging. Marker instructs List where to start listing from. + // Marker and Limit control paging. Marker instructs List where to start + // listing from. Marker string `q:"marker"` - // Sorts the response by one or more attribute and optional sort direction combinations. + // Sorts the response by one or more attribute and optional sort direction + // combinations. Sort string `q:"sort"` - // GlobalProject indicates whether to include resources for all projects or resources for the current project + // GlobalProject indicates whether to include resources for all projects or + // resources for the current project. GlobalProject *bool `q:"global_project"` - // Name to filter the response by the specified name property of the object + // Name to filter the response by the specified name property of the object. Name string `q:"name"` - // Filter the response by the specified type property of the object + // Filter the response by the specified type property of the object. Type string `q:"type"` } diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index d4e83cd9f4..4b9fd6fd39 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -63,8 +63,6 @@ func (r PolicyPage) LastMarker() (string, error) { return policies[len(policies)-1].ID, nil } -const RFC3339WithZ = "2006-01-02T15:04:05Z" - func (r *Policy) UnmarshalJSON(b []byte) error { type tmp Policy var s struct { @@ -81,7 +79,7 @@ func (r *Policy) UnmarshalJSON(b []byte) error { if s.CreatedAt != "" { r.CreatedAt, err = time.Parse(gophercloud.RFC3339MilliNoZ, s.CreatedAt) if err != nil { - r.CreatedAt, err = time.Parse(RFC3339WithZ, s.CreatedAt) + r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) if err != nil { return err } @@ -91,7 +89,7 @@ func (r *Policy) UnmarshalJSON(b []byte) error { if s.UpdatedAt != "" { r.UpdatedAt, err = time.Parse(gophercloud.RFC3339MilliNoZ, s.UpdatedAt) if err != nil { - r.UpdatedAt, err = time.Parse(RFC3339WithZ, s.UpdatedAt) + r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) if err != nil { return err } diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go index 1cad57d39d..f514214ee3 100644 --- a/openstack/clustering/v1/policies/testing/fixtures.go +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -220,155 +220,139 @@ const PolicyBadValidateBody = ` ` const PolicyDeleteRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" - -var ( - ExpectedPolicyCreatedAt1, _ = time.Parse(time.RFC3339, "2018-04-02T21:43:30.000000Z") - ExpectedPolicyUpdatedAt1, _ = time.Parse(time.RFC3339, "2018-04-02T00:19:12.000000Z") - ExpectedPolicyCreatedAt2, _ = time.Parse(time.RFC3339, "2018-04-02T22:29:36.000000Z") - ExpectedPolicyUpdatedAt2, _ = time.Parse(time.RFC3339, "2018-04-02T23:15:11.000000Z") - ExpectedCreatePolicyCreatedAt, _ = time.Parse(time.RFC3339, "2018-04-04T00:18:36.000000Z") - ZeroTime, _ = time.Parse(time.RFC3339, "1-01-01T00:00:00.000000Z") - - // Policy ID to delete - PolicyIDtoDelete = "1" - - // Policy ID to update - PolicyIDtoUpdate = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" - - ExpectedPolicies = [][]policies.Policy{ - { - { - CreatedAt: ExpectedPolicyCreatedAt1, - Data: map[string]interface{}{}, - Domain: "", - ID: "PolicyListBodyID1", - Name: "delpol", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "criteria": "OLDEST_FIRST", - "destroy_after_deletion": true, - "grace_period": float64(60), - "reduce_desired_capacity": false, - }, - Type: "senlin.policy.deletion", - Version: "1.0", - }, - Type: "senlin.policy.deletion-1.0", - User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: ExpectedPolicyUpdatedAt1, - }, +const PolicyIDtoUpdate = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" +const PolicyIDtoDelete = "1" + +var ExpectedPolicy1 = policies.Policy{ + CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), + Data: map[string]interface{}{}, + Domain: "", + ID: "PolicyListBodyID1", + Name: "delpol", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": true, + "grace_period": float64(60), + "reduce_desired_capacity": false, }, - { - { - CreatedAt: ExpectedPolicyCreatedAt2, - Data: map[string]interface{}{}, - Domain: "", - ID: "PolicyListBodyID2", - Name: "delpol2", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "criteria": "OLDEST_FIRST", - "destroy_after_deletion": true, - "grace_period": float64(60), - "reduce_desired_capacity": false, - }, - Type: "senlin.policy.deletion", - Version: "1.0", - }, - Type: "senlin.policy.deletion-1.0", - User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: ExpectedPolicyUpdatedAt2, - }, + Type: "senlin.policy.deletion", + Version: "1.0", + }, + Type: "senlin.policy.deletion-1.0", + User: "fe43e41739154b72818565e0d2580819", + UpdatedAt: time.Date(2018, 4, 2, 0, 19, 12, 0, time.UTC), +} + +var ExpectedPolicy2 = policies.Policy{ + CreatedAt: time.Date(2018, 4, 2, 22, 29, 36, 0, time.UTC), + Data: map[string]interface{}{}, + Domain: "", + ID: "PolicyListBodyID2", + Name: "delpol2", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": true, + "grace_period": float64(60), + "reduce_desired_capacity": false, }, - } - - ExpectedCreatePolicy = policies.Policy{ - CreatedAt: ExpectedCreatePolicyCreatedAt, - Data: map[string]interface{}{}, - Domain: "", - ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - Name: "delpol4", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "hooks": map[string]interface{}{ - "params": map[string]interface{}{ - "queue": "zaqar_queue_name", - }, - "timeout": float64(180), - "type": "zaqar", + Type: "senlin.policy.deletion", + Version: "1.0", + }, + Type: "senlin.policy.deletion-1.0", + User: "fe43e41739154b72818565e0d2580819", + UpdatedAt: time.Date(2018, 4, 2, 23, 15, 11, 0, time.UTC), +} + +var ExpectedPolicies = [][]policies.Policy{ + []policies.Policy{ExpectedPolicy1}, + []policies.Policy{ExpectedPolicy2}, +} + +var ExpectedCreatePolicy = policies.Policy{ + CreatedAt: time.Date(2018, 4, 4, 0, 18, 36, 0, time.UTC), + Data: map[string]interface{}{}, + Domain: "", + ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + Name: "delpol4", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "hooks": map[string]interface{}{ + "params": map[string]interface{}{ + "queue": "zaqar_queue_name", }, + "timeout": float64(180), + "type": "zaqar", }, - Type: "senlin.policy.deletion", - Version: "1.1", }, - Type: "senlin.policy.deletion-1.1", - User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: ZeroTime, - } - - ExpectedUpdatePolicy = policies.Policy{ - CreatedAt: ExpectedPolicyCreatedAt1, - Data: map[string]interface{}{}, - Domain: "", - ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - Name: "delpol4", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "hooks": map[string]interface{}{ - "params": map[string]interface{}{ - "queue": "zaqar_queue_name", - }, - "timeout": float64(180), - "type": "zaqar", + Type: "senlin.policy.deletion", + Version: "1.1", + }, + Type: "senlin.policy.deletion-1.1", + User: "fe43e41739154b72818565e0d2580819", +} + +var ExpectedUpdatePolicy = policies.Policy{ + CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), + Data: map[string]interface{}{}, + Domain: "", + ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + Name: "delpol4", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "hooks": map[string]interface{}{ + "params": map[string]interface{}{ + "queue": "zaqar_queue_name", }, + "timeout": float64(180), + "type": "zaqar", }, - Type: "senlin.policy.deletion", - Version: "1.1", }, - Type: "senlin.policy.deletion-1.1", - User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: ZeroTime, - } - - ExpectedValidatePolicy = policies.Policy{ - CreatedAt: ExpectedPolicyCreatedAt1, - Data: map[string]interface{}{}, - Domain: "", - ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - Name: "delpol4", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "hooks": map[string]interface{}{ - "params": map[string]interface{}{ - "queue": "zaqar_queue_name", - }, - "timeout": float64(180), - "type": "zaqar", + Type: "senlin.policy.deletion", + Version: "1.1", + }, + Type: "senlin.policy.deletion-1.1", + User: "fe43e41739154b72818565e0d2580819", +} + +var ExpectedValidatePolicy = policies.Policy{ + CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), + Data: map[string]interface{}{}, + Domain: "", + ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + Name: "delpol4", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "hooks": map[string]interface{}{ + "params": map[string]interface{}{ + "queue": "zaqar_queue_name", }, + "timeout": float64(180), + "type": "zaqar", }, - Type: "senlin.policy.deletion", - Version: "1.1", }, - Type: "senlin.policy.deletion-1.1", - User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: ZeroTime, - } -) + Type: "senlin.policy.deletion", + Version: "1.1", + }, + Type: "senlin.policy.deletion-1.1", + User: "fe43e41739154b72818565e0d2580819", +} func HandlePolicyList(t *testing.T) { th.Mux.HandleFunc("/v1/policies", func(w http.ResponseWriter, r *http.Request) { From 5a01756d457e1805b376411914a0c47b4ca61975 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 16 Jun 2018 02:19:15 +0000 Subject: [PATCH 0397/2296] Clustering v1: policytypes updates --- .../clustering/v1/policytypes/requests.go | 2 +- .../clustering/v1/policytypes/results.go | 11 +- .../v1/policytypes/testing/fixtures.go | 166 +++++++++--------- 3 files changed, 94 insertions(+), 85 deletions(-) diff --git a/openstack/clustering/v1/policytypes/requests.go b/openstack/clustering/v1/policytypes/requests.go index bb8a48d40f..05ccb538fb 100644 --- a/openstack/clustering/v1/policytypes/requests.go +++ b/openstack/clustering/v1/policytypes/requests.go @@ -13,7 +13,7 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { }) } -// Get makes a request against the API to get details for a policy type +// Get makes a request against the API to get details for a policy type. func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { _, r.Err = client.Get(policyTypeGetURL(client, policyTypeName), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) diff --git a/openstack/clustering/v1/policytypes/results.go b/openstack/clustering/v1/policytypes/results.go index 4d5d8e1c3f..0de84db4ab 100644 --- a/openstack/clustering/v1/policytypes/results.go +++ b/openstack/clustering/v1/policytypes/results.go @@ -5,14 +5,15 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// PolicyType represents a clustering policy type in the Openstack cloud +// PolicyType represents a clustering policy type in the Openstack cloud. type PolicyType struct { Name string `json:"name"` Version string `json:"version"` SupportStatus map[string][]SupportStatusType `json:"support_status"` } -// SupportStatusType represents the support status information for a clustering policy type +// SupportStatusType represents the support status information for a +// clustering policy type. type SupportStatusType struct { Status string `json:"status"` Since string `json:"since"` @@ -38,14 +39,16 @@ func (page PolicyTypePage) IsEmpty() (bool, error) { return len(policyTypes) == 0, err } -// PolicyTypeDetail represents the detailed policy type information for a clustering policy type +// PolicyTypeDetail represents the detailed policy type information for a +// clustering policy type. type PolicyTypeDetail struct { Name string `json:"name"` Schema map[string]interface{} `json:"schema"` SupportStatus map[string][]SupportStatusType `json:"support_status,omitempty"` } -// Extract provides access to the individual policy type returned by Get and extracts PolicyTypeDetail +// Extract provides access to the individual policy type returned by Get +// and extracts PolicyTypeDetail. func (r policyTypeResult) Extract() (*PolicyTypeDetail, error) { var s struct { PolicyType *PolicyTypeDetail `json:"policy_type"` diff --git a/openstack/clustering/v1/policytypes/testing/fixtures.go b/openstack/clustering/v1/policytypes/testing/fixtures.go index 3db56cdadc..2732dd9ebf 100644 --- a/openstack/clustering/v1/policytypes/testing/fixtures.go +++ b/openstack/clustering/v1/policytypes/testing/fixtures.go @@ -110,97 +110,103 @@ const PolicyTypeDetailBody = ` } ` -var ( - ExpectedPolicyTypes = []policytypes.PolicyType{ - { - Name: "senlin.policy.affinity", - Version: "1.0", - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "SUPPORTED", - Since: "2016.10", - }, - }, +var ExpectedPolicyType1 = policytypes.PolicyType{ + Name: "senlin.policy.affinity", + Version: "1.0", + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": { + { + Status: "SUPPORTED", + Since: "2016.10", }, }, - { - Name: "senlin.policy.health", - Version: "1.0", - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "EXPERIMENTAL", - Since: "2016.10", - }, - }, - }, - }, - { - Name: "senlin.policy.scaling", - Version: "1.0", - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "SUPPORTED", - Since: "2016.04", - }, - }, + }, +} + +var ExpectedPolicyType2 = policytypes.PolicyType{ + Name: "senlin.policy.health", + Version: "1.0", + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": { + { + Status: "EXPERIMENTAL", + Since: "2016.10", }, }, - { - Name: "senlin.policy.region_placement", - Version: "1.0", - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "EXPERIMENTAL", - Since: "2016.04", - }, - { - Status: "SUPPORTED", - Since: "2016.10", - }, - }, + }, +} + +var ExpectedPolicyType3 = policytypes.PolicyType{ + Name: "senlin.policy.scaling", + Version: "1.0", + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": { + { + Status: "SUPPORTED", + Since: "2016.04", }, }, - } - - ExpectedPolicyTypeDetail = &policytypes.PolicyTypeDetail{ - Name: "senlin.policy.batch-1.0", - Schema: map[string]interface{}{ - "max_batch_size": map[string]interface{}{ - "default": float64(-1), - "description": "Maximum number of nodes that will be updated in parallel.", - "required": false, - "type": "Integer", - "updatable": false, - }, - "min_in_service": map[string]interface{}{ - "default": float64(1), - "description": "Minimum number of nodes in service when performing updates.", - "required": false, - "type": "Integer", - "updatable": false, + }, +} + +var ExpectedPolicyType4 = policytypes.PolicyType{ + Name: "senlin.policy.region_placement", + Version: "1.0", + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": { + { + Status: "EXPERIMENTAL", + Since: "2016.04", }, - "pause_time": map[string]interface{}{ - "default": float64(60), - "description": "Interval in seconds between update batches if any.", - "required": false, - "type": "Integer", - "updatable": false, + { + Status: "SUPPORTED", + Since: "2016.10", }, }, - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": []policytypes.SupportStatusType{ - { - Status: "EXPERIMENTAL", - Since: "2017.02", - }, + }, +} + +var ExpectedPolicyTypes = []policytypes.PolicyType{ + ExpectedPolicyType1, + ExpectedPolicyType2, + ExpectedPolicyType3, + ExpectedPolicyType4, +} + +var ExpectedPolicyTypeDetail = &policytypes.PolicyTypeDetail{ + Name: "senlin.policy.batch-1.0", + Schema: map[string]interface{}{ + "max_batch_size": map[string]interface{}{ + "default": float64(-1), + "description": "Maximum number of nodes that will be updated in parallel.", + "required": false, + "type": "Integer", + "updatable": false, + }, + "min_in_service": map[string]interface{}{ + "default": float64(1), + "description": "Minimum number of nodes in service when performing updates.", + "required": false, + "type": "Integer", + "updatable": false, + }, + "pause_time": map[string]interface{}{ + "default": float64(60), + "description": "Interval in seconds between update batches if any.", + "required": false, + "type": "Integer", + "updatable": false, + }, + }, + SupportStatus: map[string][]policytypes.SupportStatusType{ + "1.0": []policytypes.SupportStatusType{ + { + Status: "EXPERIMENTAL", + Since: "2017.02", }, }, - } -) + }, +} func HandlePolicyTypeList(t *testing.T) { th.Mux.HandleFunc("/v1/policy-types", From 92111cf9a0e9c45e1d4932d1534226d364f24c79 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 16 Jun 2018 02:52:44 +0000 Subject: [PATCH 0398/2296] Clustering v1: profiles updates --- openstack/clustering/v1/profiles/results.go | 53 +- .../v1/profiles/testing/fixtures.go | 395 +++++++++ .../v1/profiles/testing/requests_test.go | 824 +----------------- 3 files changed, 437 insertions(+), 835 deletions(-) create mode 100644 openstack/clustering/v1/profiles/testing/fixtures.go diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index 1a50ca36ce..558d9182f9 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -3,9 +3,7 @@ package profiles import ( "encoding/json" "fmt" - "reflect" "strconv" - "strings" "time" "github.com/gophercloud/gophercloud" @@ -93,18 +91,15 @@ func (r *Spec) UnmarshalJSON(b []byte) error { } *r = Spec(s.tmp) - switch s.Version.(type) { - case nil: - r.Version = "" - case float32, float64, int, int32, int64: - r.Version = strconv.FormatFloat(s.Version.(float64), 'f', -1, 64) - if !strings.Contains(r.Version, ".") { - r.Version = strconv.FormatFloat(s.Version.(float64), 'f', 1, 64) + switch t := s.Version.(type) { + case float64: + if t == 1 { + r.Version = fmt.Sprintf("%.1f", t) + } else { + r.Version = strconv.FormatFloat(t, 'f', -1, 64) } case string: - r.Version = s.Version.(string) - default: - return fmt.Errorf("Invalid type for Spec Version. type=%v", reflect.TypeOf(s.Version)) + r.Version = t } return nil @@ -114,8 +109,8 @@ func (r *Profile) UnmarshalJSON(b []byte) error { type tmp Profile var s struct { tmp - CreatedAt interface{} `json:"created_at"` - UpdatedAt interface{} `json:"updated_at"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) @@ -124,32 +119,18 @@ func (r *Profile) UnmarshalJSON(b []byte) error { } *r = Profile(s.tmp) - switch t := s.CreatedAt.(type) { - case string: - if t != "" { - r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.CreatedAt != "" { + r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) + if err != nil { + return err } - case nil: - r.CreatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) } - switch t := s.UpdatedAt.(type) { - case string: - if t != "" { - r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.UpdatedAt != "" { + r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) + if err != nil { + return err } - case nil: - r.UpdatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) } return nil diff --git a/openstack/clustering/v1/profiles/testing/fixtures.go b/openstack/clustering/v1/profiles/testing/fixtures.go new file mode 100644 index 0000000000..f989fc3ac5 --- /dev/null +++ b/openstack/clustering/v1/profiles/testing/fixtures.go @@ -0,0 +1,395 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const CreateResponse = ` +{ + "profile": { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "test-profile", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "centos7.3-latest", + "name": "centos_server", + "networks": [ + { + "network": "private-network" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "2016-01-03T17:22:23Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +var ExpectedCreate = profiles.Profile{ + CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "test-profile", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.small", + "image": "centos7.3-latest", + "name": "centos_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private-network"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), + User: "5e5bf8027826429c96af157f68dc9072", +} + +const GetResponse = ` +{ + "profile": { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +var ExpectedGet = profiles.Profile{ + CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": float64(1), + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + User: "5e5bf8027826429c96af157f68dc9072", +} + +const ListResponse = ` +{ + "profiles": [ + { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": 1.0 + }, + "type": "os.nova.server-1.0", + "updated_at": "2016-01-03T17:22:23Z", + "user": "5e5bf8027826429c96af157f68dc9072" + }, + { + "created_at": null, + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": 1.0 + }, + "type": "os.nova.server-1.0", + "updated_at": null, + "user": "5e5bf8027826429c96af157f68dc9072" + }, + { + "created_at": "", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "", + "user": "5e5bf8027826429c96af157f68dc9072" + } + ] +}` + +var ExpectedListProfile1 = profiles.Profile{ + CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), + User: "5e5bf8027826429c96af157f68dc9072", +} + +var ExpectedListProfile2 = profiles.Profile{ + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + User: "5e5bf8027826429c96af157f68dc9072", +} + +var ExpectedListProfile3 = profiles.Profile{ + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.small", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + User: "5e5bf8027826429c96af157f68dc9072", +} + +var ExpectedList = []profiles.Profile{ + ExpectedListProfile1, + ExpectedListProfile2, + ExpectedListProfile3, +} + +const UpdateResponse = ` +{ + "profile": { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": { + "foo": "bar" + }, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": 1, + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "2016-01-03T17:22:23Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +var ExpectedUpdate = profiles.Profile{ + CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{"foo": "bar"}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": float64(1), + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), + User: "5e5bf8027826429c96af157f68dc9072", +} + +func HandleCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, CreateResponse) + }) +} + +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, GetResponse) + }) +} + +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/profiles", 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, ListResponse) + }) +} + +func HandleUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, UpdateResponse) + }) +} + +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/profiles/6dc6d336e3fc4c0a951b5698cd1236ee", 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) + }) +} diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go index 7abb37318c..1793c4bf1f 100644 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -1,10 +1,7 @@ package testing import ( - "fmt" - "net/http" "testing" - "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" "github.com/gophercloud/gophercloud/pagination" @@ -16,42 +13,7 @@ func TestCreateProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "profile": { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "test-profile", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "centos7.3-latest", - "name": "centos_server", - "networks": [ - { - "network": "private-network" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "2016-01-03T17:22:23Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) + HandleCreateSuccessfully(t) networks := []map[string]interface{}{ {"network": "private-network"}, @@ -65,7 +27,7 @@ func TestCreateProfile(t *testing.T) { "security_groups": "", } - optsProfile := &profiles.CreateOpts{ + createOpts := &profiles.CreateOpts{ Name: "TestProfile", Spec: profiles.Spec{ Type: "os.nova.server", @@ -74,809 +36,73 @@ func TestCreateProfile(t *testing.T) { }, } - profile, err := profiles.Create(fake.ServiceClient(), optsProfile).Extract() + profile, err := profiles.Create(fake.ServiceClient(), createOpts).Extract() if err != nil { t.Errorf("Failed to extract profile: %v", err) } - createdAt, _ := time.Parse(time.RFC3339, "2016-01-03T16:22:23Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-01-03T17:22:23Z") - - expected := profiles.Profile{ - CreatedAt: createdAt, - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "test-profile", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.small", - "image": "centos7.3-latest", - "name": "centos_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private-network"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: updatedAt, - User: "5e5bf8027826429c96af157f68dc9072", - } - - th.AssertDeepEquals(t, expected, *profile) -} - -func TestCreateProfileInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "profile": { - "created_at": 123456789.0, - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "test-profile", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "centos7.3-latest", - "name": "centos_server", - "networks": [ - { - "network": "private-network" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": 123456789.0, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - optsProfile := &profiles.CreateOpts{ - Name: "TestProfile", - Spec: profiles.Spec{ - Type: "os.nova.server", - Version: "1.0", - Properties: map[string]interface{}{}, - }, - } - - _, err := profiles.Create(fake.ServiceClient(), optsProfile).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestCreateProfileInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "profile": { - "created_at": "invalid_time", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "test-profile", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "centos7.3-latest", - "name": "centos_server", - "networks": [ - { - "network": "private-network" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "invalid_time", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - optsProfile := &profiles.CreateOpts{ - Name: "TestProfile", - Spec: profiles.Spec{ - Type: "os.nova.server", - Version: "1.0", - Properties: map[string]interface{}{}, - }, - } - - _, err := profiles.Create(fake.ServiceClient(), optsProfile).Extract() - th.AssertEquals(t, false, err == nil) + th.AssertDeepEquals(t, ExpectedCreate, *profile) } func TestGetProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + HandleGetSuccessfully(t) - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "profile": { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - actual, err := profiles.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() - if err != nil { - t.Errorf("Failed to get profile. %v", err) - } else { - createdAt, _ := time.Parse(time.RFC3339, "2016-01-03T16:22:23Z") - updatedAt := time.Time{} - expected := profiles.Profile{ - CreatedAt: createdAt, - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": float64(1), - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: updatedAt, - User: "5e5bf8027826429c96af157f68dc9072", - } - - th.AssertDeepEquals(t, expected, *actual) - } + actual, err := profiles.Get(fake.ServiceClient(), ExpectedGet.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedGet, *actual) } -func TestGetProfileInvalidCreatedAtTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` - { - "profile": { - "created_at": 1234567890.0, - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - _, err := profiles.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestGetProfileInvalidUpdatedAtTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` - { - "profile": { - "created_at": null, - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": 1234567890.0, - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - _, err := profiles.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() - th.AssertEquals(t, false, err == nil) -} func TestListProfiles(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + HandleListSuccessfully(t) - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "profiles": [ - { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": 1.0 - }, - "type": "os.nova.server-1.0", - "updated_at": "2016-01-03T17:22:23Z", - "user": "5e5bf8027826429c96af157f68dc9072" - }, - { - "created_at": null, - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": 1.0 - }, - "type": "os.nova.server-1.0", - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - }, - { - "created_at": "", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "", - "user": "5e5bf8027826429c96af157f68dc9072" - } - ] - }`) - }) + var iFalse bool + listOpts := profiles.ListOpts{ + GlobalProject: &iFalse, + } count := 0 - profiles.List(fake.ServiceClient(), profiles.ListOpts{GlobalProject: new(bool)}).EachPage(func(page pagination.Page) (bool, error) { + err := profiles.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := profiles.ExtractProfiles(page) - if err != nil { - t.Errorf("Failed to extract profiles: %v", err) - return false, err - } - - createdAt, _ := time.Parse(time.RFC3339, "2016-01-03T16:22:23Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-01-03T17:22:23Z") - - expected := []profiles.Profile{ - { - CreatedAt: createdAt, - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: updatedAt, - User: "5e5bf8027826429c96af157f68dc9072", - }, - { - CreatedAt: time.Time{}, - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: time.Time{}, - User: "5e5bf8027826429c96af157f68dc9072", - }, - { - CreatedAt: time.Time{}, - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: time.Time{}, - User: "5e5bf8027826429c96af157f68dc9072", - }, - } - - th.AssertDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedList, actual) return true, nil }) + th.AssertNoErr(t, err) + if count != 1 { t.Errorf("Expected 1 page of profiles, got %d pages instead", count) } } -func TestListProfilesInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/profiles", 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, ` - { - "profiles": [ - { - "created_at": 123456789.0, - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": 1.0 - }, - "type": "os.nova.server-1.0", - "updated_at": 123456789.0, - "user": "5e5bf8027826429c96af157f68dc9072" - } - ] - }`) - }) - - count := 0 - err := profiles.List(fake.ServiceClient(), profiles.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - return true, nil - }) - - th.AssertEquals(t, false, err == nil) - if count != 0 { - t.Errorf("Expected 0 page of profiles, got %d pages instead", count) - } -} - -func TestListProfilesInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/profiles", 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, ` - { - "profiles": [ - { - "created_at": "invalid", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": 1.0 - }, - "type": "os.nova.server-1.0", - "updated_at": "invalid", - "user": "5e5bf8027826429c96af157f68dc9072" - } - ] - }`) - }) - - count := 0 - err := profiles.List(fake.ServiceClient(), profiles.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - return true, nil - }) - - th.AssertEquals(t, false, err == nil) - if count != 0 { - t.Errorf("Expected 0 page of profiles, got %d pages instead", count) - } -} - func TestUpdateProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "profile": { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": { - "foo": "bar" - }, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "2016-01-03T17:22:23Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } - }`) - }) - - actual, err := profiles.Update(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", profiles.UpdateOpts{Name: "pserver"}).Extract() - if err != nil { - t.Errorf("Failed to get profile. %v", err) - } else { - createdAt, _ := time.Parse(time.RFC3339, "2016-01-03T16:22:23Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-01-03T17:22:23Z") - - expected := profiles.Profile{ - CreatedAt: createdAt, - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{"foo": "bar"}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": float64(1), - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: updatedAt, - User: "5e5bf8027826429c96af157f68dc9072", - } + HandleUpdateSuccessfully(t) - th.AssertDeepEquals(t, expected, *actual) + updateOpts := profiles.UpdateOpts{ + Name: "pserver", } -} - -func TestUpdateProfilesInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/profiles", 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, ` - { - "profiles": [ - { - "created_at": 123456789.0, - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": 1.0 - }, - "type": "os.nova.server-1.0", - "updated_at": 123456789.0, - "user": "5e5bf8027826429c96af157f68dc9072" - } - ] - }`) - }) - - _, err := profiles.Update(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", profiles.UpdateOpts{Name: "pserver"}).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestUpdateProfilesInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/profiles", 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, ` - { - "profiles": [ - { - "created_at": "invalid", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": 1.0 - }, - "type": "os.nova.server-1.0", - "updated_at": "invalid", - "user": "5e5bf8027826429c96af157f68dc9072" - } - ] - }`) - }) - - _, err := profiles.Update(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", profiles.UpdateOpts{Name: "pserver"}).Extract() - th.AssertEquals(t, false, err == nil) + actual, err := profiles.Update(fake.ServiceClient(), ExpectedUpdate.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedUpdate, *actual) } func TestDeleteProfile(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/profiles/6dc6d336e3fc4c0a951b5698cd1236ee", 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) - }) + HandleDeleteSuccessfully(t) deleteResult := profiles.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) From be1983353b7265cbac54bdeb8240c07625955662 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 16 Jun 2018 06:09:58 +0000 Subject: [PATCH 0399/2296] Clustering v1: acceptance test updates --- .../openstack/clustering/v1/actions_test.go | 26 +- .../clustering/v1/autoscaling_test.go | 419 ------------------ .../openstack/clustering/v1/clustering.go | 263 +++++++++++ .../openstack/clustering/v1/clusters_test.go | 61 +++ .../openstack/clustering/v1/policies_test.go | 93 +--- .../openstack/clustering/v1/profiles_test.go | 49 ++ 6 files changed, 405 insertions(+), 506 deletions(-) delete mode 100644 acceptance/openstack/clustering/v1/autoscaling_test.go create mode 100644 acceptance/openstack/clustering/v1/clustering.go create mode 100644 acceptance/openstack/clustering/v1/clusters_test.go create mode 100644 acceptance/openstack/clustering/v1/profiles_test.go diff --git a/acceptance/openstack/clustering/v1/actions_test.go b/acceptance/openstack/clustering/v1/actions_test.go index 0136c2b443..eca8fc62c6 100644 --- a/acceptance/openstack/clustering/v1/actions_test.go +++ b/acceptance/openstack/clustering/v1/actions_test.go @@ -8,28 +8,24 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" - "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestActionsList(t *testing.T) { client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create a clustering client: %v", err) - } + th.AssertNoErr(t, err) opts := actions.ListOpts{ Limit: 200, } - err = actions.List(client, opts).EachPage(func(page pagination.Page) (bool, error) { - actionInfos, err := actions.ExtractActions(page) - if err != nil { - return false, err - } - - for _, actionInfo := range actionInfos { - tools.PrintResource(t, actionInfo) - } - return true, nil - }) + allPages, err := actions.List(client, opts).AllPages() + th.AssertNoErr(t, err) + + allActions, err := actions.ExtractActions(allPages) + th.AssertNoErr(t, err) + + for _, action := range allActions { + tools.PrintResource(t, action) + } } diff --git a/acceptance/openstack/clustering/v1/autoscaling_test.go b/acceptance/openstack/clustering/v1/autoscaling_test.go deleted file mode 100644 index 2a2a7c837a..0000000000 --- a/acceptance/openstack/clustering/v1/autoscaling_test.go +++ /dev/null @@ -1,419 +0,0 @@ -// +build acceptance clustering autoscaling clusters profiles - -package v1 - -import ( - "fmt" - "strings" - "testing" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/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/profiles" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" -) - -var testName string - -func TestAutoScaling(t *testing.T) { - testName = tools.RandomString("TESTACC-", 8) - profileCreate(t) - defer profileDelete(t) - profileGet(t) - profileList(t) - profileUpdate(t) - clusterCreate(t) - defer clustersDelete(t) - clusterGet(t) - clusterList(t) - clusterUpdate(t) -} - -func profileCreate(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - networks := []map[string]interface{}{ - {"network": "sandbox-internal-net"}, - } - - props := map[string]interface{}{ - "name": "centos-server", - "flavor": "t2.micro", - "image": "centos7.3-latest", - "networks": networks, - "security_groups": "", - } - - profileName := testName - optsProfile := &profiles.CreateOpts{ - Metadata: map[string]interface{}{ - "foo": "bar", - "test": "123", - }, - Name: profileName, - Spec: profiles.Spec{ - Type: "os.nova.server", - Version: "1.0", - Properties: props, - }, - } - - createResult := profiles.Create(client, optsProfile) - th.AssertNoErr(t, createResult.Err) - - requestID := createResult.Header.Get("X-OpenStack-Request-Id") - th.AssertEquals(t, true, requestID != "") - - profile, err := createResult.Extract() - if err != nil { - t.Fatalf("Unable to create profile %s: %v", profileName, err) - } else { - t.Logf("Profile created %v", profile) - } - - th.AssertEquals(t, profileName, profile.Name) - th.AssertEquals(t, "os.nova.server", profile.Spec.Type) - th.AssertEquals(t, "1.0", profile.Spec.Version) -} - -func profileGet(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - profileName := testName - profile, err := profiles.Get(client, profileName).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, profileName, profile.Name) - - tools.PrintResource(t, profile) -} - -func profileDelete(t *testing.T) { - - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - profileName := testName - err = profiles.Delete(client, profileName).ExtractErr() - th.AssertNoErr(t, err) -} - -func profileList(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - testProfileFound := false - profiles.List(client, profiles.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - allProfiles, err := profiles.ExtractProfiles(page) - if err != nil { - t.Fatalf("Error extracting page of profiles: %v", err) - } - - for _, profile := range allProfiles { - tools.PrintResource(t, profile) - if profile.Name == testName { - testProfileFound = true - break - } - } - - empty, err := page.IsEmpty() - - th.AssertNoErr(t, err) - - // Expect the page IS NOT empty - th.AssertEquals(t, false, empty) - - return true, nil - }) - - th.AssertEquals(t, true, testProfileFound) -} - -func profileUpdate(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - profileName := testName - newProfileName := profileName + "-TEST-PROFILE_UPDATE" - - // Use new name - profile, err := profiles.Update(client, profileName, profiles.UpdateOpts{Name: newProfileName}).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, newProfileName, profile.Name) - - tools.PrintResource(t, profile) - - // Revert back to original name - profile, err = profiles.Update(client, newProfileName, profiles.UpdateOpts{Name: profileName}).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, profileName, profile.Name) - - tools.PrintResource(t, profile) -} - -func clusterCreate(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - clusterName := testName - optsCluster := clusters.CreateOpts{ - Name: clusterName, - DesiredCapacity: 3, - ProfileID: testName, - MinSize: new(int), - MaxSize: 20, - Timeout: 3600, - Metadata: map[string]interface{}{ - "foo": "bar", - "test": map[string]interface{}{ - "nil_interface": interface{}(nil), - "float_value": float64(123.3), - "string_value": "test_string", - "bool_value": false, - }, - }, - Config: map[string]interface{}{}, - } - - createResult := clusters.Create(client, optsCluster) - th.AssertNoErr(t, createResult.Err) - - requestID := createResult.Header.Get("X-OpenStack-Request-Id") - th.AssertEquals(t, true, requestID != "") - - location := createResult.Header.Get("Location") - th.AssertEquals(t, true, location != "") - - actionID := "" - locationFields := strings.Split(location, "actions/") - if len(locationFields) >= 2 { - actionID = locationFields[1] - } - th.AssertEquals(t, true, actionID != "") - t.Logf("Cluster create action id: %s", actionID) - - cluster, err := createResult.Extract() - if err != nil { - t.Fatalf("Unable to create cluster %s: %v", clusterName, err) - } else { - t.Logf("Cluster created %+v", cluster) - } - - th.AssertEquals(t, optsCluster.Name, cluster.Name) - th.AssertEquals(t, optsCluster.DesiredCapacity, cluster.DesiredCapacity) - th.AssertEquals(t, optsCluster.ProfileID, cluster.ProfileName) - th.AssertEquals(t, *optsCluster.MinSize, cluster.MinSize) - th.AssertEquals(t, optsCluster.MaxSize, cluster.MaxSize) - th.AssertEquals(t, optsCluster.Timeout, cluster.Timeout) - th.CheckDeepEquals(t, optsCluster.Metadata, cluster.Metadata) - th.CheckDeepEquals(t, optsCluster.Config, cluster.Config) -} - -func clusterGet(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - clusterName := testName - cluster, err := clusters.Get(client, clusterName).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, clusterName, cluster.Name) - - tools.PrintResource(t, cluster) -} - -func clusterList(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - testClusterFound := false - clusters.List(client, clusters.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - allClusters, err := clusters.ExtractClusters(page) - if err != nil { - t.Fatalf("Error extracting page of clusters: %v", err) - } - - for _, cluster := range allClusters { - if cluster.Name == testName { - testClusterFound = true - } - } - - empty, err := page.IsEmpty() - - th.AssertNoErr(t, err) - - // Expect the page IS NOT empty - th.AssertEquals(t, false, empty) - - return true, nil - }) - - th.AssertEquals(t, true, testClusterFound) -} - -func clusterUpdate(t *testing.T) { - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - clusterName := testName - newClusterName := clusterName + "-TEST-UPDATE_CLUSTER" - - cluster, err := clusters.Get(client, clusterName).Extract() - if err != nil { - t.Fatalf("Unable to get cluster %s: %v", clusterName, err) - } - th.AssertEquals(t, clusterName, cluster.Name) - clusterID := cluster.ID - - // Update to new cluster name - updateOpts := clusters.UpdateOpts{ - Name: newClusterName, - } - - updateResult := clusters.Update(client, clusterID, updateOpts) - location := updateResult.Header.Get("Location") - th.AssertEquals(t, true, location != "") - - actionID := "" - locationFields := strings.Split(location, "actions/") - if len(locationFields) >= 2 { - actionID = locationFields[1] - } - - err = WaitForClusterToUpdate(client, actionID, 15) - if err != nil { - t.Fatalf("Error waiting for cluster to update: %v", err) - } - - cluster, err = clusters.Get(client, clusterID).Extract() - if err != nil { - t.Fatalf("Unable to get cluster: %v", err) - } - th.AssertEquals(t, newClusterName, cluster.Name) - - // Revert back to original cluster name - updateOpts = clusters.UpdateOpts{ - Name: clusterName, - } - - updateResult = clusters.Update(client, clusterID, updateOpts) - location = updateResult.Header.Get("Location") - th.AssertEquals(t, true, location != "") - - actionID = "" - locationFields = strings.Split(location, "actions/") - if len(locationFields) >= 2 { - actionID = locationFields[1] - } - - err = WaitForClusterToUpdate(client, actionID, 15) - if err != nil { - t.Fatalf("Error waiting for cluster to update: %v", err) - } - - cluster, err = clusters.Get(client, clusterID).Extract() - if err != nil { - t.Fatalf("Unable to get cluster: %v", err) - } - th.AssertEquals(t, clusterName, cluster.Name) -} - -func WaitForClusterToUpdate(client *gophercloud.ServiceClient, actionID string, sleepTimeSecs int) error { - return gophercloud.WaitFor(sleepTimeSecs, func() (bool, error) { - if actionID == "" { - return false, fmt.Errorf("Invalid action id. id=%s", actionID) - } - - action, err := actions.Get(client, actionID).Extract() - if err != nil { - return false, err - } - switch action.Status { - case "SUCCEEDED": - return true, nil - case "READY", "RUNNING": - return false, nil - default: - return false, fmt.Errorf("Error WaitFor ActionID=%s. Received status=%v", actionID, action.Status) - } - }) -} - -func clustersDelete(t *testing.T) { - - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - clusterName := testName - deleteResult := clusters.Delete(client, clusterName) - err = deleteResult.ExtractErr() - th.AssertNoErr(t, err) - - location := deleteResult.Header.Get("Location") - th.AssertEquals(t, true, location != "") - - actionID := "" - locationFields := strings.Split(location, "actions/") - if len(locationFields) >= 2 { - actionID = locationFields[1] - } - - err = WaitForClusterToDelete(client, actionID, 15) - if err != nil { - t.Fatalf("Error waiting for cluster to delete: %v", err) - } - - _, err = clusters.Get(client, clusterName).Extract() - if err == nil { - t.Fatalf("Unable to delete cluster [%s]: %v", clusterName, err) - } - t.Log("Cluster deleted:", clusterName) - -} - -func WaitForClusterToDelete(client *gophercloud.ServiceClient, actionID string, sleepTimeSecs int) error { - return gophercloud.WaitFor(sleepTimeSecs, func() (bool, error) { - if actionID == "" { - return false, fmt.Errorf("Invalid action id. id=%s", actionID) - } - - action, err := actions.Get(client, actionID).Extract() - if err != nil { - return false, err - } - switch action.Status { - case "SUCCEEDED": - return true, nil - case "READY", "RUNNING", "DELETING", "WAITING": - return false, nil - default: - return false, fmt.Errorf("Error WaitFor ActionID=%s. Received status=%v", actionID, action.Status) - } - }) -} diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go new file mode 100644 index 0000000000..8607ae3a61 --- /dev/null +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -0,0 +1,263 @@ +package v1 + +import ( + "fmt" + "net/http" + "strings" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/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/policies" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" + th "github.com/gophercloud/gophercloud/testhelper" +) + +var TestPolicySpec = policies.Spec{ + Description: "new policy description", + Properties: map[string]interface{}{ + "destroy_after_deletion": true, + "grace_period": 60, + "reduce_desired_capacity": false, + "criteria": "OLDEST_FIRST", + }, + Type: "senlin.policy.deletion", + Version: "1.1", +} + +// CreateCluster creates a random cluster. An error will be returned if +// the cluster could not be created. +func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, profileID string) (*clusters.Cluster, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create cluster: %s", name) + + createOpts := clusters.CreateOpts{ + Name: name, + DesiredCapacity: 1, + ProfileID: profileID, + MinSize: new(int), + MaxSize: 20, + Timeout: 3600, + Metadata: map[string]interface{}{ + "foo": "bar", + "test": map[string]interface{}{ + "nil_interface": interface{}(nil), + "float_value": float64(123.3), + "string_value": "test_string", + "bool_value": false, + }, + }, + Config: map[string]interface{}{}, + } + + res := clusters.Create(client, createOpts) + if res.Err != nil { + return nil, res.Err + } + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, true, requestID != "") + t.Logf("Cluster %s request ID: %s", name, requestID) + + actionID, err := GetActionID(res.Header) + th.AssertNoErr(t, err) + th.AssertEquals(t, true, actionID != "") + t.Logf("Cluster %s action ID: %s", name, actionID) + + err = WaitForAction(client, actionID, 600) + th.AssertNoErr(t, err) + + cluster, err := res.Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created cluster: %s", cluster.ID) + + tools.PrintResource(t, cluster) + tools.PrintResource(t, cluster.CreatedAt) + + th.AssertEquals(t, name, cluster.Name) + th.AssertEquals(t, profileID, cluster.ProfileID) + + return cluster, nil +} + +// CreatePolicy creates a random policy. An error will be returned if the +// policy could not be created. +func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies.Policy, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create policy: %s", name) + + createOpts := policies.CreateOpts{ + Name: name, + Spec: TestPolicySpec, + } + + res := policies.Create(client, createOpts) + if res.Err != nil { + return nil, res.Err + } + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, true, requestID != "") + + t.Logf("Policy %s request ID: %s", name, requestID) + + policy, err := res.Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created policy: %s", policy.ID) + + tools.PrintResource(t, policy) + tools.PrintResource(t, policy.CreatedAt) + + th.AssertEquals(t, name, policy.Name) + + return policy, nil +} + +// CreateProfile will create a random profile. An error will be returned if the +// profile could not be created. +func CreateProfile(t *testing.T, client *gophercloud.ServiceClient) (*profiles.Profile, error) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create profile: %s", name) + + networks := []map[string]interface{}{ + {"network": choices.NetworkName}, + } + + props := map[string]interface{}{ + "name": name, + "flavor": choices.FlavorID, + "image": choices.ImageID, + "networks": networks, + "security_groups": "", + } + + createOpts := profiles.CreateOpts{ + Name: name, + Spec: profiles.Spec{ + Type: "os.nova.server", + Version: "1.0", + Properties: props, + }, + } + + res := profiles.Create(client, createOpts) + if res.Err != nil { + return nil, res.Err + } + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, true, requestID != "") + + t.Logf("Profile %s request ID: %s", name, requestID) + + profile, err := res.Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created profile: %s", profile.ID) + + tools.PrintResource(t, profile) + tools.PrintResource(t, profile.CreatedAt) + + th.AssertEquals(t, name, profile.Name) + th.AssertEquals(t, profile.Spec.Type, "os.nova.server") + th.AssertEquals(t, profile.Spec.Version, "1.0") + + return profile, nil +} + +// DeleteCluster will delete a given policy. A fatal error will occur if the +// cluster could not be deleted. This works best as a deferred function. +func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete cluster: %s", id) + + res := clusters.Delete(client, id) + if res.Err != nil { + t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) + } + + actionID, err := GetActionID(res.Header) + if err != nil { + t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) + } + + err = WaitForAction(client, actionID, 600) + if err != nil { + t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) + } + + t.Logf("Successfully deleted cluster: %s", id) + + return +} + +// DeletePolicy will delete a given policy. A fatal error will occur if the +// policy could not be deleted. This works best as a deferred function. +func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete policy: %s", id) + + _, err := policies.Delete(client, id).Extract() + if err != nil { + t.Fatalf("Error deleting policy %s: %s:", id, err) + } + + t.Logf("Successfully deleted policy: %s", id) + + return +} + +// DeleteProfile will delete a given profile. A fatal error will occur if the +// profile could not be deleted. This works best as a deferred function. +func DeleteProfile(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete profile: %s", id) + + err := profiles.Delete(client, id).ExtractErr() + if err != nil { + t.Fatalf("Error deleting profile %s: %s:", id, err) + } + + t.Logf("Successfully deleted profile: %s", id) + + return +} + +// GetActionID parses an HTTP header and returns the action ID. +func GetActionID(headers http.Header) (string, error) { + location := headers.Get("Location") + v := strings.Split(location, "actions/") + if len(v) < 2 { + return "", fmt.Errorf("unable to determine action ID") + } + + actionID := v[1] + + return actionID, nil +} + +func WaitForAction(client *gophercloud.ServiceClient, actionID string, sleepTimeSecs int) error { + return gophercloud.WaitFor(sleepTimeSecs, func() (bool, error) { + action, err := actions.Get(client, actionID).Extract() + if err != nil { + return false, err + } + + if action.Status == "SUCCEEDED" { + return true, nil + } + + return false, nil + }) +} diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go new file mode 100644 index 0000000000..550174e694 --- /dev/null +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -0,0 +1,61 @@ +// +build acceptance clustering policies + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestClustersCRUD(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + // Test clusters list + allPages, err := clusters.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allClusters, err := clusters.ExtractClusters(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allClusters { + if v.ID == cluster.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + // Test cluster update + updateOpts := clusters.UpdateOpts{ + Name: cluster.Name + "-UPDATED", + } + + res := clusters.Update(client, cluster.ID, updateOpts) + th.AssertNoErr(t, res.Err) + + actionID, err := GetActionID(res.Header) + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID, 600) + th.AssertNoErr(t, err) + + newCluster, err := clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newCluster.Name, cluster.Name+"-UPDATED") + + tools.PrintResource(t, newCluster) +} diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go index e8cc0b3aa4..8cd6833309 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -11,93 +11,47 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestPolicyList(t *testing.T) { +func TestPoliciesCRUD(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) + client.Microversion = "1.5" + + policy, err := CreatePolicy(t, client) + th.AssertNoErr(t, err) + defer DeletePolicy(t, client, policy.ID) + // Test listing policies allPages, err := policies.List(client, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) + var found bool for _, v := range allPolicies { - tools.PrintResource(t, v) - - if v.CreatedAt.IsZero() { - t.Fatalf("CreatedAt value should not be zero") - } - t.Log("Created at: " + v.CreatedAt.String()) - - if !v.UpdatedAt.IsZero() { - t.Log("Updated at: " + v.UpdatedAt.String()) + if v.ID == policy.ID { + found = true } } -} -func TestPolicyCreateUpdateValidateDelete(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - testName := tools.RandomString("TESTACC-", 8) - client.Microversion = "1.5" - - createOpts := policies.CreateOpts{ - Name: testName, - Spec: policies.Spec{ - Description: "new policy description", - Properties: map[string]interface{}{ - "destroy_after_deletion": true, - "grace_period": 60, - "reduce_desired_capacity": false, - "criteria": "OLDEST_FIRST", - }, - Type: "senlin.policy.deletion", - Version: "1.1", - }, - } - - createResult := policies.Create(client, createOpts) - th.AssertNoErr(t, createResult.Err) - - requestID := createResult.Header.Get("X-Openstack-Request-ID") - th.AssertEquals(t, true, requestID != "") - - createdPolicy, err := createResult.Extract() - th.AssertNoErr(t, err) - - defer policies.Delete(client, createdPolicy.ID) - - tools.PrintResource(t, createdPolicy) - - if createdPolicy.CreatedAt.IsZero() { - t.Fatalf("CreatePolicy's CreatedAt value should not be zero") - } - t.Log("CreatePolicy created at: " + createdPolicy.CreatedAt.String()) - - if !createdPolicy.UpdatedAt.IsZero() { - t.Log("CreatePolicy updated at: " + createdPolicy.UpdatedAt.String()) - } + th.AssertEquals(t, found, true) + // Test updating policy updateOpts := policies.UpdateOpts{ - Name: testName + "-UPDATE", + Name: policy.Name + "-UPDATE", } - updatePolicy, err := policies.Update(client, createdPolicy.ID, updateOpts).Extract() + t.Logf("Attempting to update policy: %s", policy.ID) + updatePolicy, err := policies.Update(client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatePolicy) + tools.PrintResource(t, updatePolicy.UpdatedAt) - if updatePolicy.CreatedAt.IsZero() { - t.Fatalf("UpdatePolicy's CreatedAt value should not be zero") - } - t.Log("UpdatePolicy created at: " + updatePolicy.CreatedAt.String()) - - if !updatePolicy.UpdatedAt.IsZero() { - t.Log("UpdatePolicy updated at: " + updatePolicy.UpdatedAt.String()) - } - + // Test validating policy + t.Logf("Attempting to validate policy: %s", policy.ID) validateOpts := policies.ValidateOpts{ - Spec: createOpts.Spec, + Spec: TestPolicySpec, } validatePolicy, err := policies.Validate(client, validateOpts).Extract() @@ -105,11 +59,6 @@ func TestPolicyCreateUpdateValidateDelete(t *testing.T) { tools.PrintResource(t, validatePolicy) - if validatePolicy.Name != "validated_policy" { - t.Fatalf("ValidatePolicy's Name value should be 'validated_policy'") - } - - if validatePolicy.Spec.Version != createOpts.Spec.Version { - t.Fatalf("ValidatePolicy's Version value should be ", createOpts.Spec.Version) - } + th.AssertEquals(t, validatePolicy.Name, "validated_policy") + th.AssertEquals(t, validatePolicy.Spec.Version, TestPolicySpec.Version) } diff --git a/acceptance/openstack/clustering/v1/profiles_test.go b/acceptance/openstack/clustering/v1/profiles_test.go new file mode 100644 index 0000000000..f426f83232 --- /dev/null +++ b/acceptance/openstack/clustering/v1/profiles_test.go @@ -0,0 +1,49 @@ +// +build acceptance clustering policies + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestProfilesCRUD(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + // Test listing profiles + allPages, err := profiles.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allProfiles, err := profiles.ExtractProfiles(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allProfiles { + if v.ID == profile.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + // Test updating profile + updateOpts := profiles.UpdateOpts{ + Name: profile.Name + "-UPDATED", + } + + newProfile, err := profiles.Update(client, profile.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newProfile.Name, profile.Name+"-UPDATED") + + tools.PrintResource(t, newProfile) + tools.PrintResource(t, newProfile.UpdatedAt) +} From b89a2ae6c617e5d407def56539b5a379fe51bd3c Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 09:09:51 -0700 Subject: [PATCH 0400/2296] senlin-nodes-create --- openstack/clustering/v1/nodes/doc.go | 22 + openstack/clustering/v1/nodes/requests.go | 44 ++ openstack/clustering/v1/nodes/results.go | 113 +++++ openstack/clustering/v1/nodes/testing/doc.go | 2 + .../v1/nodes/testing/requests_test.go | 433 ++++++++++++++++++ openstack/clustering/v1/nodes/urls.go | 14 + 6 files changed, 628 insertions(+) create mode 100644 openstack/clustering/v1/nodes/doc.go create mode 100644 openstack/clustering/v1/nodes/requests.go create mode 100644 openstack/clustering/v1/nodes/results.go create mode 100644 openstack/clustering/v1/nodes/testing/doc.go create mode 100644 openstack/clustering/v1/nodes/testing/requests_test.go create mode 100644 openstack/clustering/v1/nodes/urls.go diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go new file mode 100644 index 0000000000..bbca89fb47 --- /dev/null +++ b/openstack/clustering/v1/nodes/doc.go @@ -0,0 +1,22 @@ +/* +Package nodes provides information and interaction with the nodes through +the OpenStack Clustering service. + +Example to Create Nodes + + opts := nodes.CreateOpts{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + Metadata: map[string]interface{}{}, + Name: "node-e395be1e-002", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + Role: "", + } + + node, err := nodes.Create(serviceClient, opts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("node", node) + +*/ +package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go new file mode 100644 index 0000000000..be22926722 --- /dev/null +++ b/openstack/clustering/v1/nodes/requests.go @@ -0,0 +1,44 @@ +package nodes + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder Builder. +type CreateOptsBuilder interface { + ToNodeCreateMap() (map[string]interface{}, error) +} + +// CreateOpts params +type CreateOpts struct { + Role string `json:"role,omitempty"` + ProfileID string `json:"profile_id" required:"true"` + ClusterID string `json:"cluster_id,omitempty"` + Name string `json:"name" required:"true"` + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +// ToNodeCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "node") +} + +// Create requests the creation of a new node. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToNodeCreateMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go new file mode 100644 index 0000000000..c3ecdb21b0 --- /dev/null +++ b/openstack/clustering/v1/nodes/results.go @@ -0,0 +1,113 @@ +package nodes + +import ( + "encoding/json" + "fmt" + "reflect" + "time" + + "github.com/gophercloud/gophercloud" +) + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Create operations. +type CreateResult struct { + commonResult +} + +// Extract provides access to the individual node returned by Get and Create +func (r commonResult) Extract() (*Node, error) { + var s struct { + Node *Node `json:"node"` + } + err := r.ExtractInto(&s) + + return s.Node, err +} + +// Node represents a node structure +type Node struct { + ClusterID string `json:"cluster_id"` + CreatedAt time.Time `json:"-"` + Data map[string]interface{} `json:"data"` + Dependents map[string]interface{} `json:"dependents"` + Domain string `json:"domain"` + ID string `json:"id"` + Index int `json:"index"` + InitAt time.Time `json:"-"` + Metadata map[string]interface{} `json:"metadata"` + Name string `json:"name"` + PhysicalID string `json:"physical_id"` + ProfileID string `json:"profile_id"` + ProfileName string `json:"profile_name"` + ProjectID string `json:"project_id"` + Role string `json:"role"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + UpdatedAt time.Time `json:"-"` + User string `json:"user"` +} + +func (r *Node) UnmarshalJSON(b []byte) error { + type tmp Node + var s struct { + tmp + CreatedAt interface{} `json:"created_at"` + InitAt interface{} `json:"init_at"` + UpdatedAt interface{} `json:"updated_at"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Node(s.tmp) + + switch t := s.CreatedAt.(type) { + case string: + if t != "" { + r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.CreatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) + } + + switch t := s.InitAt.(type) { + case string: + if t != "" { + r.InitAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.InitAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.InitAt)) + } + + switch t := s.UpdatedAt.(type) { + case string: + if t != "" { + r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.UpdatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) + } + + return nil +} diff --git a/openstack/clustering/v1/nodes/testing/doc.go b/openstack/clustering/v1/nodes/testing/doc.go new file mode 100644 index 0000000000..cb76666d2d --- /dev/null +++ b/openstack/clustering/v1/nodes/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_nodes_v1 +package testing diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go new file mode 100644 index 0000000000..2636683019 --- /dev/null +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -0,0 +1,433 @@ +package testing + +import ( + "fmt" + "net/http" + "strings" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-3791a089-9d46-4671-a3f9-55e95e55d2b4") + w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da") + + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "2016-05-13T07:02:20Z", + "data": { + "internal_ports": [ + { + "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", + "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], + "fixed_ips": [ + { + "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", + "ip_address": "10.63.177.162" + } + ], + "id": "43aa53d7-a70b-4f40-812f-4feecb687018", + "remove": true + } + ], + "placement": { + "zone": "nova" + } + }, + "dependents": {}, + "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "2016-05-13T08:02:04Z", + "metadata": { + "test": { + "nil_interface": null, + "bool_value": false, + "string_value": "test_string", + "float_value": 123.3 + }, + "foo": "bar" + }, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "2016-05-13T09:02:04Z", + "user": "ab79b9647d074e46ac223a8fa297b846" + } + }`) + }) + + opts := nodes.CreateOpts{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + Metadata: map[string]interface{}{ + "foo": "bar", + "test": map[string]interface{}{ + "nil_interface": interface{}(nil), + "float_value": float64(123.3), + "string_value": "test_string", + "bool_value": false, + }, + }, + Name: "node-e395be1e-002", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + Role: "", + } + + createResult := nodes.Create(fake.ServiceClient(), opts) + if createResult.Err != nil { + t.Error("Error creating node. error=", createResult.Err) + } + + requestID := createResult.Header.Get("X-Openstack-Request-Id") + th.AssertEquals(t, "req-3791a089-9d46-4671-a3f9-55e95e55d2b4", requestID) + + location := createResult.Header.Get("Location") + th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da", location) + + actionID := "" + locationFields := strings.Split(location, "actions/") + if len(locationFields) >= 2 { + actionID = locationFields[1] + } + th.AssertEquals(t, "ffd94dd8-6266-4887-9a8c-5b78b72136da", actionID) + + actual, err := createResult.Extract() + if err != nil { + t.Error("Error creating nodes. error=", err) + } else { + createdAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") + initAt, _ := time.Parse(time.RFC3339, "2016-05-13T08:02:04Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-05-13T09:02:04Z") + + expected := nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: createdAt, + Data: map[string]interface{}{ + "internal_ports": []map[string]interface{}{ + { + "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", + "security_group_ids": []interface{}{ + "8db277ab-1d98-4148-ba72-724721789427", + }, + "fixed_ips": []interface{}{ + map[string]interface{}{ + "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", + "ip_address": "10.63.177.162", + }, + }, + "id": "43aa53d7-a70b-4f40-812f-4feecb687018", + "remove": true, + }, + }, + "placement": map[string]interface{}{ + "zone": "nova", + }, + }, + Dependents: map[string]interface{}{}, + Domain: "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: initAt, + Metadata: map[string]interface{}{ + "foo": "bar", + "test": map[string]interface{}{ + "nil_interface": interface{}(nil), + "float_value": float64(123.3), + "string_value": "test_string", + "bool_value": false, + }, + }, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: updatedAt, + User: "ab79b9647d074e46ac223a8fa297b846", + } + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestCreateNodeEmptyTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": null, + "data": { + "internal_ports": [ + { + "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", + "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], + "fixed_ips": [ + { + "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", + "ip_address": "10.63.177.162" + } + ], + "floating": { + "id": "e906af80-ce13-4ec3-9fba-fa20581c2695", + "floating_network_id": "c87774b5-95a4-4efb-8e6b-883e2212d67b", + "floating_ip_address": "10.0.0.20", + "remove": false + }, + "id": "43aa53d7-a70b-4f40-812f-4feecb687018", + "remove": true + } + ], + "placement": { + "zone": "nova" + } + }, + "dependents": {}, + "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": null, + "metadata": {}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": null, + "user": "ab79b9647d074e46ac223a8fa297b846" + } + }`) + }) + + opts := nodes.CreateOpts{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + Metadata: map[string]interface{}{}, + Name: "node-e395be1e-002", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + Role: "", + } + + actual, err := nodes.Create(fake.ServiceClient(), opts).Extract() + if err != nil { + t.Error("Error creating nodes. error=", err) + } else { + expected := nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: time.Time{}, + Data: map[string]interface{}{ + "internal_ports": []map[string]interface{}{ + { + "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", + "security_group_ids": []interface{}{ + "8db277ab-1d98-4148-ba72-724721789427", + }, + "fixed_ips": []interface{}{ + map[string]interface{}{ + "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", + "ip_address": "10.63.177.162", + }, + }, + "floating": map[string]interface{}{ + "floating_network_id": "c87774b5-95a4-4efb-8e6b-883e2212d67b", + "floating_ip_address": "10.0.0.20", + "remove": false, + "id": "e906af80-ce13-4ec3-9fba-fa20581c2695", + }, + "id": "43aa53d7-a70b-4f40-812f-4feecb687018", + "remove": true, + }, + }, + "placement": map[string]interface{}{ + "zone": "nova", + }, + }, + + Dependents: map[string]interface{}{}, + Domain: "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: time.Time{}, + Metadata: map[string]interface{}{}, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: time.Time{}, + User: "ab79b9647d074e46ac223a8fa297b846", + } + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestCreateNodeInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": 123456789.0, + "data": { + "internal_ports": [ + { + "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", + "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], + "fixed_ips": [ + { + "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", + "ip_address": "10.63.177.162" + } + ], + "id": "43aa53d7-a70b-4f40-812f-4feecb687018", + "remove": true + } + ], + "placement": { + "zone": "nova" + } + }, + "dependents": {}, + "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": 123456789.0, + "metadata": {}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": 123456789.0, + "user": "ab79b9647d074e46ac223a8fa297b846" + } + }`) + }) + + opts := nodes.CreateOpts{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + Metadata: map[string]interface{}{}, + Name: "node-e395be1e-002", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + Role: "", + } + + _, err := nodes.Create(fake.ServiceClient(), opts).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestCreateNodeInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "invalid", + "data": { + "internal_ports": [ + { + "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", + "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], + "fixed_ips": [ + { + "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", + "ip_address": "10.63.177.162" + } + ], + "id": "43aa53d7-a70b-4f40-812f-4feecb687018", + "remove": true + } + ], + "placement": { + "zone": "nova" + } + }, + "dependents": {}, + "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "invalid", + "metadata": {}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "invalid", + "user": "ab79b9647d074e46ac223a8fa297b846" + } + }`) + }) + + opts := nodes.CreateOpts{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + Metadata: map[string]interface{}{}, + Name: "node-e395be1e-002", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + Role: "", + } + + _, err := nodes.Create(fake.ServiceClient(), opts).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go new file mode 100644 index 0000000000..8cae88f4c9 --- /dev/null +++ b/openstack/clustering/v1/nodes/urls.go @@ -0,0 +1,14 @@ +package nodes + +import "github.com/gophercloud/gophercloud" + +var apiVersion = "v1" +var apiName = "nodes" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} + +func createURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 560c0599349af936587d1886d0583356e9b50757 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 17 Jun 2018 01:54:09 +0000 Subject: [PATCH 0401/2296] Clustering v1: nodes create updates --- .../openstack/clustering/v1/clustering.go | 57 +++ .../openstack/clustering/v1/nodes_test.go | 26 ++ openstack/clustering/v1/nodes/results.go | 53 +-- .../clustering/v1/nodes/testing/fixtures.go | 126 ++++++ .../v1/nodes/testing/requests_test.go | 403 +----------------- 5 files changed, 234 insertions(+), 431 deletions(-) create mode 100644 acceptance/openstack/clustering/v1/nodes_test.go create mode 100644 openstack/clustering/v1/nodes/testing/fixtures.go diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go index 8607ae3a61..e99718d73a 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" th "github.com/gophercloud/gophercloud/testhelper" @@ -86,6 +87,62 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, profileID st return cluster, nil } +// CreateNode creates a random node. An error will be returned if +// the node could not be created. +func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, profileID string) (*nodes.Node, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create node: %s", name) + + createOpts := nodes.CreateOpts{ + ClusterID: clusterID, + Metadata: map[string]interface{}{ + "foo": "bar", + "test": map[string]interface{}{ + "nil_interface": interface{}(nil), + "float_value": float64(123.3), + "string_value": "test_string", + "bool_value": false, + }, + }, + Name: name, + ProfileID: profileID, + Role: "", + } + + res := nodes.Create(client, createOpts) + if res.Err != nil { + return nil, res.Err + } + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, true, requestID != "") + t.Logf("Node %s request ID: %s", name, requestID) + + actionID, err := GetActionID(res.Header) + th.AssertNoErr(t, err) + th.AssertEquals(t, true, actionID != "") + t.Logf("Node %s action ID: %s", name, actionID) + + err = WaitForAction(client, actionID, 600) + th.AssertNoErr(t, err) + + node, err := res.Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created node: %s", node.ID) + + tools.PrintResource(t, node) + tools.PrintResource(t, node.CreatedAt) + + th.AssertEquals(t, profileID, node.ProfileID) + th.AssertEquals(t, clusterID, node.ClusterID) + th.AssertDeepEquals(t, createOpts.Metadata, node.Metadata) + + return node, nil +} + // CreatePolicy creates a random policy. An error will be returned if the // policy could not be created. func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies.Policy, error) { diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go new file mode 100644 index 0000000000..397739faf0 --- /dev/null +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -0,0 +1,26 @@ +// +build acceptance clustering policies + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNodesCRUD(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + _, err = CreateNode(t, client, cluster.ID, profile.ID) + th.AssertNoErr(t, err) +} diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index c3ecdb21b0..79dc378867 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -2,8 +2,6 @@ package nodes import ( "encoding/json" - "fmt" - "reflect" "time" "github.com/gophercloud/gophercloud" @@ -56,9 +54,9 @@ func (r *Node) UnmarshalJSON(b []byte) error { type tmp Node var s struct { tmp - CreatedAt interface{} `json:"created_at"` - InitAt interface{} `json:"init_at"` - UpdatedAt interface{} `json:"updated_at"` + CreatedAt string `json:"created_at"` + InitAt string `json:"init_at"` + UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) @@ -67,46 +65,25 @@ func (r *Node) UnmarshalJSON(b []byte) error { } *r = Node(s.tmp) - switch t := s.CreatedAt.(type) { - case string: - if t != "" { - r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.CreatedAt != "" { + r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) + if err != nil { + return err } - case nil: - r.CreatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) } - switch t := s.InitAt.(type) { - case string: - if t != "" { - r.InitAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.InitAt != "" { + r.InitAt, err = time.Parse(time.RFC3339, s.InitAt) + if err != nil { + return err } - case nil: - r.InitAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.InitAt)) } - switch t := s.UpdatedAt.(type) { - case string: - if t != "" { - r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.UpdatedAt != "" { + r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) + if err != nil { + return err } - case nil: - r.UpdatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) } return nil diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go new file mode 100644 index 0000000000..990b217829 --- /dev/null +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -0,0 +1,126 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const CreateResponse = `{ + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "2016-05-13T07:02:20Z", + "data": { + "internal_ports": [ + { + "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", + "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], + "fixed_ips": [ + { + "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", + "ip_address": "10.63.177.162" + } + ], + "id": "43aa53d7-a70b-4f40-812f-4feecb687018", + "remove": true + } + ], + "placement": { + "zone": "nova" + } + }, + "dependents": {}, + "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "2016-05-13T08:02:04Z", + "metadata": { + "test": { + "nil_interface": null, + "bool_value": false, + "string_value": "test_string", + "float_value": 123.3 + }, + "foo": "bar" + }, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": null, + "user": "ab79b9647d074e46ac223a8fa297b846" + } +}` + +var ExpectedCreate = nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), + Data: map[string]interface{}{ + "internal_ports": []map[string]interface{}{ + { + "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", + "security_group_ids": []interface{}{ + "8db277ab-1d98-4148-ba72-724721789427", + }, + "fixed_ips": []interface{}{ + map[string]interface{}{ + "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", + "ip_address": "10.63.177.162", + }, + }, + "id": "43aa53d7-a70b-4f40-812f-4feecb687018", + "remove": true, + }, + }, + "placement": map[string]interface{}{ + "zone": "nova", + }, + }, + Dependents: map[string]interface{}{}, + Domain: "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), + Metadata: map[string]interface{}{ + "foo": "bar", + "test": map[string]interface{}{ + "nil_interface": interface{}(nil), + "float_value": float64(123.3), + "string_value": "test_string", + "bool_value": false, + }, + }, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + User: "ab79b9647d074e46ac223a8fa297b846", +} + +func HandleCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-3791a089-9d46-4671-a3f9-55e95e55d2b4") + w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da") + + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, CreateResponse) + }) +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 2636683019..6ce6cbd4aa 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,11 +1,8 @@ package testing import ( - "fmt" - "net/http" "strings" "testing" - "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" @@ -16,69 +13,9 @@ func TestCreateNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + HandleCreateSuccessfully(t) - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-3791a089-9d46-4671-a3f9-55e95e55d2b4") - w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da") - - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "2016-05-13T07:02:20Z", - "data": { - "internal_ports": [ - { - "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", - "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], - "fixed_ips": [ - { - "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", - "ip_address": "10.63.177.162" - } - ], - "id": "43aa53d7-a70b-4f40-812f-4feecb687018", - "remove": true - } - ], - "placement": { - "zone": "nova" - } - }, - "dependents": {}, - "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "2016-05-13T08:02:04Z", - "metadata": { - "test": { - "nil_interface": null, - "bool_value": false, - "string_value": "test_string", - "float_value": 123.3 - }, - "foo": "bar" - }, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "2016-05-13T09:02:04Z", - "user": "ab79b9647d074e46ac223a8fa297b846" - } - }`) - }) - - opts := nodes.CreateOpts{ + createOpts := nodes.CreateOpts{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", Metadata: map[string]interface{}{ "foo": "bar", @@ -94,340 +31,20 @@ func TestCreateNode(t *testing.T) { Role: "", } - createResult := nodes.Create(fake.ServiceClient(), opts) - if createResult.Err != nil { - t.Error("Error creating node. error=", createResult.Err) - } + res := nodes.Create(fake.ServiceClient(), createOpts) + th.AssertNoErr(t, res.Err) - requestID := createResult.Header.Get("X-Openstack-Request-Id") + requestID := res.Header.Get("X-Openstack-Request-Id") th.AssertEquals(t, "req-3791a089-9d46-4671-a3f9-55e95e55d2b4", requestID) - location := createResult.Header.Get("Location") + location := res.Header.Get("Location") th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da", location) - actionID := "" locationFields := strings.Split(location, "actions/") - if len(locationFields) >= 2 { - actionID = locationFields[1] - } + actionID := locationFields[1] th.AssertEquals(t, "ffd94dd8-6266-4887-9a8c-5b78b72136da", actionID) - actual, err := createResult.Extract() - if err != nil { - t.Error("Error creating nodes. error=", err) - } else { - createdAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") - initAt, _ := time.Parse(time.RFC3339, "2016-05-13T08:02:04Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-05-13T09:02:04Z") - - expected := nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: createdAt, - Data: map[string]interface{}{ - "internal_ports": []map[string]interface{}{ - { - "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", - "security_group_ids": []interface{}{ - "8db277ab-1d98-4148-ba72-724721789427", - }, - "fixed_ips": []interface{}{ - map[string]interface{}{ - "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", - "ip_address": "10.63.177.162", - }, - }, - "id": "43aa53d7-a70b-4f40-812f-4feecb687018", - "remove": true, - }, - }, - "placement": map[string]interface{}{ - "zone": "nova", - }, - }, - Dependents: map[string]interface{}{}, - Domain: "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: initAt, - Metadata: map[string]interface{}{ - "foo": "bar", - "test": map[string]interface{}{ - "nil_interface": interface{}(nil), - "float_value": float64(123.3), - "string_value": "test_string", - "bool_value": false, - }, - }, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: updatedAt, - User: "ab79b9647d074e46ac223a8fa297b846", - } - th.AssertDeepEquals(t, expected, *actual) - } -} - -func TestCreateNodeEmptyTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": null, - "data": { - "internal_ports": [ - { - "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", - "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], - "fixed_ips": [ - { - "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", - "ip_address": "10.63.177.162" - } - ], - "floating": { - "id": "e906af80-ce13-4ec3-9fba-fa20581c2695", - "floating_network_id": "c87774b5-95a4-4efb-8e6b-883e2212d67b", - "floating_ip_address": "10.0.0.20", - "remove": false - }, - "id": "43aa53d7-a70b-4f40-812f-4feecb687018", - "remove": true - } - ], - "placement": { - "zone": "nova" - } - }, - "dependents": {}, - "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": null, - "metadata": {}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": null, - "user": "ab79b9647d074e46ac223a8fa297b846" - } - }`) - }) - - opts := nodes.CreateOpts{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - Metadata: map[string]interface{}{}, - Name: "node-e395be1e-002", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - Role: "", - } - - actual, err := nodes.Create(fake.ServiceClient(), opts).Extract() - if err != nil { - t.Error("Error creating nodes. error=", err) - } else { - expected := nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: time.Time{}, - Data: map[string]interface{}{ - "internal_ports": []map[string]interface{}{ - { - "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", - "security_group_ids": []interface{}{ - "8db277ab-1d98-4148-ba72-724721789427", - }, - "fixed_ips": []interface{}{ - map[string]interface{}{ - "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", - "ip_address": "10.63.177.162", - }, - }, - "floating": map[string]interface{}{ - "floating_network_id": "c87774b5-95a4-4efb-8e6b-883e2212d67b", - "floating_ip_address": "10.0.0.20", - "remove": false, - "id": "e906af80-ce13-4ec3-9fba-fa20581c2695", - }, - "id": "43aa53d7-a70b-4f40-812f-4feecb687018", - "remove": true, - }, - }, - "placement": map[string]interface{}{ - "zone": "nova", - }, - }, - - Dependents: map[string]interface{}{}, - Domain: "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: time.Time{}, - Metadata: map[string]interface{}{}, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: time.Time{}, - User: "ab79b9647d074e46ac223a8fa297b846", - } - th.AssertDeepEquals(t, expected, *actual) - } -} - -func TestCreateNodeInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": 123456789.0, - "data": { - "internal_ports": [ - { - "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", - "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], - "fixed_ips": [ - { - "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", - "ip_address": "10.63.177.162" - } - ], - "id": "43aa53d7-a70b-4f40-812f-4feecb687018", - "remove": true - } - ], - "placement": { - "zone": "nova" - } - }, - "dependents": {}, - "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": 123456789.0, - "metadata": {}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": 123456789.0, - "user": "ab79b9647d074e46ac223a8fa297b846" - } - }`) - }) - - opts := nodes.CreateOpts{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - Metadata: map[string]interface{}{}, - Name: "node-e395be1e-002", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - Role: "", - } - - _, err := nodes.Create(fake.ServiceClient(), opts).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestCreateNodeInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "invalid", - "data": { - "internal_ports": [ - { - "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", - "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], - "fixed_ips": [ - { - "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", - "ip_address": "10.63.177.162" - } - ], - "id": "43aa53d7-a70b-4f40-812f-4feecb687018", - "remove": true - } - ], - "placement": { - "zone": "nova" - } - }, - "dependents": {}, - "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "invalid", - "metadata": {}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "invalid", - "user": "ab79b9647d074e46ac223a8fa297b846" - } - }`) - }) - - opts := nodes.CreateOpts{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - Metadata: map[string]interface{}{}, - Name: "node-e395be1e-002", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - Role: "", - } - - _, err := nodes.Create(fake.ServiceClient(), opts).Extract() - th.AssertEquals(t, false, err == nil) + actual, err := res.Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCreate, *actual) } From e9070fcac01646bc96ca4e2d1b52cb6d294de525 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 09:09:51 -0700 Subject: [PATCH 0402/2296] senlin-nodes-list --- openstack/clustering/v1/nodes/doc.go | 20 ++ openstack/clustering/v1/nodes/requests.go | 39 ++++ openstack/clustering/v1/nodes/results.go | 26 +++ .../v1/nodes/testing/requests_test.go | 179 ++++++++++++++++++ openstack/clustering/v1/nodes/urls.go | 4 + 5 files changed, 268 insertions(+) diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index bbca89fb47..da2c52a027 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -18,5 +18,25 @@ Example to Create Nodes } fmt.Printf("node", node) +Example to List Nodes + + listOpts := nodes.ListOpts{ + Name: "testnode", + } + + allNodes, err := nodes.List(serviceClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allNodes, err := nodes.ExtractNodes(allPages) + if err != nil { + panic(err) + } + + for _, node := range allNodes { + fmt.Printf("%+v\n", node) + } + */ package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index be22926722..aeaa7306e8 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder Builder. @@ -42,3 +43,41 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } return } + +// ListOptsBuilder Builder. +type ListOptsBuilder interface { + ToNodeListQuery() (string, error) +} + +// ListOpts params +type ListOpts struct { + Limit int `q:"limit"` + Marker string `q:"marker"` + Sort string `q:"sort"` + GlobalProject string `q:"global_project"` + ClusterID string `q:"cluster_id"` + Name string `q:"name"` + Status string `q:"status"` +} + +// ToNodeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToNodeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDetail instructs OpenStack to provide a list of nodes. +func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToNodeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return NodePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index 79dc378867..a63b9d143b 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // commonResult is the response of a base result. @@ -27,6 +28,25 @@ func (r commonResult) Extract() (*Node, error) { return s.Node, err } +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + +// ExtractNodes provides access to the list of nodes in a page acquired from the ListDetail operation. +func ExtractNodes(r pagination.Page) ([]Node, error) { + var s struct { + Nodes []Node `json:"nodes"` + } + err := (r.(NodePage)).ExtractInto(&s) + return s.Nodes, err +} + +// NodePage contains a single page of all nodes from a ListDetails call. +type NodePage struct { + pagination.LinkedPageBase +} + // Node represents a node structure type Node struct { ClusterID string `json:"cluster_id"` @@ -50,6 +70,12 @@ type Node struct { User string `json:"user"` } +// IsEmpty determines if a NodePage contains any results. +func (page NodePage) IsEmpty() (bool, error) { + nodes, err := ExtractNodes(page) + return len(nodes) == 0, err +} + func (r *Node) UnmarshalJSON(b []byte) error { type tmp Node var s struct { diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 6ce6cbd4aa..ec9905914a 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,10 +1,14 @@ package testing import ( + "fmt" + "net/http" "strings" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -48,3 +52,178 @@ func TestCreateNode(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreate, *actual) } + +func TestListNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes", 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, ` + { + "nodes": [ + { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "2016-05-13T07:02:20Z", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "2016-05-13T08:02:04Z", + "metadata": {}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "2016-05-13T09:02:04Z", + "user": "ab79b9647d074e46ac223a8fa297b846" } + ] + }`) + }) + + count := 0 + nodes.ListDetail(fake.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + actual, err := nodes.ExtractNodes(page) + if err != nil { + t.Errorf("Failed to extract nodes: %v", err) + return false, err + } + + createdAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") + initAt, _ := time.Parse(time.RFC3339, "2016-05-13T08:02:04Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-05-13T09:02:04Z") + expected := []nodes.Node{ + { + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: createdAt, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + Domain: "", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: initAt, + Metadata: map[string]interface{}{}, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: updatedAt, + User: "ab79b9647d074e46ac223a8fa297b846", + }, + } + + th.AssertDeepEquals(t, expected, actual) + count++ + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestListNodesInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes", 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, ` + { + "nodes": [ + { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": 123456789.0, + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": 123456789.0, + "metadata": {}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": 123456789.0, + "user": "ab79b9647d074e46ac223a8fa297b846" } + ] + }`) + }) + + err := nodes.ListDetail(fake.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + _, err := nodes.ExtractNodes(page) + th.AssertEquals(t, false, err == nil) + return false, err + }) + th.AssertEquals(t, false, err == nil) +} + +func TestListNodesInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes", 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, ` + { + "nodes": [ + { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "invalid", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "invalid", + "metadata": {}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "invalid", + "user": "ab79b9647d074e46ac223a8fa297b846" } + ] + }`) + }) + + err := nodes.ListDetail(fake.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + _, err := nodes.ExtractNodes(page) + th.AssertEquals(t, false, err == nil) + return false, err + }) + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go index 8cae88f4c9..971b119fec 100644 --- a/openstack/clustering/v1/nodes/urls.go +++ b/openstack/clustering/v1/nodes/urls.go @@ -12,3 +12,7 @@ func commonURL(client *gophercloud.ServiceClient) string { func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From a00ed0f83f12c173e3c8709b54a6e7a27441dda9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 17 Jun 2018 14:27:19 +0000 Subject: [PATCH 0403/2296] Clustering v1: nodes list updates --- .../openstack/clustering/v1/nodes_test.go | 19 +- openstack/clustering/v1/nodes/requests.go | 6 +- openstack/clustering/v1/nodes/results.go | 5 +- .../clustering/v1/nodes/testing/fixtures.go | 62 +++++++ .../v1/nodes/testing/requests_test.go | 171 +----------------- 5 files changed, 92 insertions(+), 171 deletions(-) diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index 397739faf0..ad7c1fb604 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -21,6 +22,22 @@ func TestNodesCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) - _, err = CreateNode(t, client, cluster.ID, profile.ID) + node, err := CreateNode(t, client, cluster.ID, profile.ID) th.AssertNoErr(t, err) + + // Test nodes list + allPages, err := nodes.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allNodes, err := nodes.ExtractNodes(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allNodes { + if v.ID == node.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index aeaa7306e8..277369dd59 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -54,7 +54,7 @@ type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` Sort string `q:"sort"` - GlobalProject string `q:"global_project"` + GlobalProject *bool `q:"global_project"` ClusterID string `q:"cluster_id"` Name string `q:"name"` Status string `q:"status"` @@ -66,8 +66,8 @@ func (opts ListOpts) ToNodeListQuery() (string, error) { return q.String(), err } -// ListDetail instructs OpenStack to provide a list of nodes. -func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { +// List instructs OpenStack to provide a list of nodes. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { query, err := opts.ToNodeListQuery() diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index a63b9d143b..2bacb220c9 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -33,7 +33,8 @@ type GetResult struct { commonResult } -// ExtractNodes provides access to the list of nodes in a page acquired from the ListDetail operation. +// ExtractNodes provides access to the list of nodes in a page acquired from +// the List operation. func ExtractNodes(r pagination.Page) ([]Node, error) { var s struct { Nodes []Node `json:"nodes"` @@ -42,7 +43,7 @@ func ExtractNodes(r pagination.Page) ([]Node, error) { return s.Nodes, err } -// NodePage contains a single page of all nodes from a ListDetails call. +// NodePage contains a single page of all nodes from a List call. type NodePage struct { pagination.LinkedPageBase } diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go index 990b217829..588526a2d5 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures.go +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -110,6 +110,56 @@ var ExpectedCreate = nodes.Node{ User: "ab79b9647d074e46ac223a8fa297b846", } +const ListResponse = ` +{ + "nodes": [ + { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "2016-05-13T07:02:20Z", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "2016-05-13T08:02:04Z", + "metadata": {}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "2016-05-13T09:02:04Z", + "user": "ab79b9647d074e46ac223a8fa297b846" } + ] +}` + +var ExpectedList1 = nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + Domain: "", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), + Metadata: map[string]interface{}{}, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: time.Date(2016, 5, 13, 9, 2, 4, 0, time.UTC), + User: "ab79b9647d074e46ac223a8fa297b846", +} + +var ExpectedList = []nodes.Node{ExpectedList1} + func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -124,3 +174,15 @@ func HandleCreateSuccessfully(t *testing.T) { fmt.Fprint(w, CreateResponse) }) } + +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/nodes", 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, ListResponse) + }) +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index ec9905914a..e3f733e32a 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,11 +1,8 @@ package testing import ( - "fmt" - "net/http" "strings" "testing" - "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/pagination" @@ -57,173 +54,17 @@ func TestListNodes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/nodes", 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, ` - { - "nodes": [ - { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "2016-05-13T07:02:20Z", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "2016-05-13T08:02:04Z", - "metadata": {}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "2016-05-13T09:02:04Z", - "user": "ab79b9647d074e46ac223a8fa297b846" } - ] - }`) - }) + HandleListSuccessfully(t) count := 0 - nodes.ListDetail(fake.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := nodes.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { actual, err := nodes.ExtractNodes(page) - if err != nil { - t.Errorf("Failed to extract nodes: %v", err) - return false, err - } - - createdAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") - initAt, _ := time.Parse(time.RFC3339, "2016-05-13T08:02:04Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-05-13T09:02:04Z") - expected := []nodes.Node{ - { - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: createdAt, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - Domain: "", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: initAt, - Metadata: map[string]interface{}{}, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: updatedAt, - User: "ab79b9647d074e46ac223a8fa297b846", - }, - } - - th.AssertDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedList, actual) count++ return true, nil }) - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestListNodesInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes", 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, ` - { - "nodes": [ - { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": 123456789.0, - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": 123456789.0, - "metadata": {}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": 123456789.0, - "user": "ab79b9647d074e46ac223a8fa297b846" } - ] - }`) - }) - - err := nodes.ListDetail(fake.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - _, err := nodes.ExtractNodes(page) - th.AssertEquals(t, false, err == nil) - return false, err - }) - th.AssertEquals(t, false, err == nil) -} - -func TestListNodesInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes", 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, ` - { - "nodes": [ - { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "invalid", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "invalid", - "metadata": {}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "invalid", - "user": "ab79b9647d074e46ac223a8fa297b846" } - ] - }`) - }) - - err := nodes.ListDetail(fake.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - _, err := nodes.ExtractNodes(page) - th.AssertEquals(t, false, err == nil) - return false, err - }) - th.AssertEquals(t, false, err == nil) + th.AssertNoErr(t, err) + th.AssertEquals(t, count, 1) } From e8a02b5bbc1fdefc00fd6d5e9573f47eab14a97c Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 09:09:51 -0700 Subject: [PATCH 0404/2296] senlin-nodes-delete --- openstack/clustering/v1/nodes/doc.go | 10 +++++++++- openstack/clustering/v1/nodes/requests.go | 11 +++++++++++ openstack/clustering/v1/nodes/results.go | 6 ++++++ .../v1/nodes/testing/requests_test.go | 17 +++++++++++++++++ openstack/clustering/v1/nodes/urls.go | 8 ++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index da2c52a027..ac9049f1cf 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -24,7 +24,7 @@ Example to List Nodes Name: "testnode", } - allNodes, err := nodes.List(serviceClient, listOpts).AllPages() + allPages, err := nodes.List(serviceClient, listOpts).AllPages() if err != nil { panic(err) } @@ -38,5 +38,13 @@ Example to List Nodes fmt.Printf("%+v\n", node) } + +Example to Delete a Node + + nodeID := "6dc6d336e3fc4c0a951b5698cd1236ee" + err := nodes.Delete(serviceClient, nodeID).ExtractErr() + if err != nil { + panic(err) + } */ package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index 277369dd59..5d3fbd64b6 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -41,6 +41,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create if r.Err == nil { r.Header = result.Header } + return } @@ -81,3 +82,13 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return NodePage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Delete deletes the specified node ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + var result *http.Response + result, r.Err = client.Delete(deleteURL(client, id), nil) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index 2bacb220c9..12c4848512 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -115,3 +115,9 @@ func (r *Node) UnmarshalJSON(b []byte) error { return nil } + +// DeleteResult is the result from a Delete operation. Call ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index e3f733e32a..3c9bf53e25 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "net/http" "strings" "testing" @@ -68,3 +69,19 @@ func TestListNodes(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, count, 1) } + +func TestDeleteNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes/6dc6d336e3fc4c0a951b5698cd1236ee", 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) + }) + + deleteResult := nodes.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") + th.AssertNoErr(t, deleteResult.ExtractErr()) +} diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go index 971b119fec..d8df0690e8 100644 --- a/openstack/clustering/v1/nodes/urls.go +++ b/openstack/clustering/v1/nodes/urls.go @@ -16,3 +16,11 @@ func createURL(client *gophercloud.ServiceClient) string { func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func idURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id) +} + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 66a3dc7b499d3beab92a436107686f3ea497e02e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 17 Jun 2018 15:32:24 +0000 Subject: [PATCH 0405/2296] Clustering v1: nodes delete updates --- acceptance/openstack/clustering/v1/clustering.go | 15 +++++++++++++++ acceptance/openstack/clustering/v1/nodes_test.go | 1 + openstack/clustering/v1/nodes/testing/fixtures.go | 10 ++++++++++ .../clustering/v1/nodes/testing/requests_test.go | 9 +-------- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go index e99718d73a..76a4e1a394 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -261,6 +261,21 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { return } +// DeleteNode will delete a given node. A fatal error will occur if the +// node could not be deleted. This works best as a deferred function. +func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete node: %s", id) + + err := nodes.Delete(client, id).ExtractErr() + if err != nil { + t.Fatalf("Error deleting node %s: %s:", id, err) + } + + t.Logf("Successfully deleted node: %s", id) + + return +} + // DeletePolicy will delete a given policy. A fatal error will occur if the // policy could not be deleted. This works best as a deferred function. func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, id string) { diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index ad7c1fb604..d6dbfb6378 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -24,6 +24,7 @@ func TestNodesCRUD(t *testing.T) { node, err := CreateNode(t, client, cluster.ID, profile.ID) th.AssertNoErr(t, err) + defer DeleteNode(t, client, node.ID) // Test nodes list allPages, err := nodes.List(client, nil).AllPages() diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go index 588526a2d5..17a8e6864f 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures.go +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -186,3 +186,13 @@ func HandleListSuccessfully(t *testing.T) { fmt.Fprint(w, ListResponse) }) } + +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/nodes/6dc6d336e3fc4c0a951b5698cd1236ee", 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) + }) +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 3c9bf53e25..de589db49a 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,7 +1,6 @@ package testing import ( - "net/http" "strings" "testing" @@ -74,13 +73,7 @@ func TestDeleteNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/nodes/6dc6d336e3fc4c0a951b5698cd1236ee", 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) - }) + HandleDeleteSuccessfully(t) deleteResult := nodes.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) From dbcabc34dd2c109d1f7bd752177509054a641a84 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 09:09:51 -0700 Subject: [PATCH 0406/2296] senlin-nodes-get --- openstack/clustering/v1/nodes/doc.go | 10 ++ openstack/clustering/v1/nodes/requests.go | 10 ++ openstack/clustering/v1/nodes/results.go | 26 +-- .../v1/nodes/testing/requests_test.go | 155 ++++++++++++++++++ openstack/clustering/v1/nodes/urls.go | 4 + 5 files changed, 194 insertions(+), 11 deletions(-) diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index ac9049f1cf..9f1928991d 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -46,5 +46,15 @@ Example to Delete a Node if err != nil { panic(err) } + +Example to Get Node + + nodeID := "node123" + node, err := nodes.Get(serviceClient, nodeID).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", node) + */ package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index 5d3fbd64b6..3192cd9ed3 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -92,3 +92,13 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { } return } + +// Get makes a request against senlin to get a details of a node type +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + var result *http.Response + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index 12c4848512..8fbcbb7894 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -19,20 +19,30 @@ type CreateResult struct { } // Extract provides access to the individual node returned by Get and Create +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + +// DeleteResult is the result from a Delete operation. Call ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// Extract provides access to the individual node returned by Get and extracts Node func (r commonResult) Extract() (*Node, error) { var s struct { Node *Node `json:"node"` } err := r.ExtractInto(&s) + if err != nil { + return &Node{}, err + } return s.Node, err } -// GetResult is the response of a Get operations. -type GetResult struct { - commonResult -} - // ExtractNodes provides access to the list of nodes in a page acquired from // the List operation. func ExtractNodes(r pagination.Page) ([]Node, error) { @@ -115,9 +125,3 @@ func (r *Node) UnmarshalJSON(b []byte) error { return nil } - -// DeleteResult is the result from a Delete operation. Call ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index de589db49a..53d3e54d3b 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,8 +1,11 @@ package testing import ( + "fmt" + "net/http" "strings" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/pagination" @@ -78,3 +81,155 @@ func TestDeleteNode(t *testing.T) { deleteResult := nodes.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } + +func TestGetNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "2016-05-13T07:02:20Z", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "2016-05-13T07:02:04Z", + "metadata": {"foo": "bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "2016-05-13T07:02:20Z", + "user": "ab79b9647d074e46ac223a8fa297b846" + } + }`) + }) + + initAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:04Z") + createdAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") + expected := nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: createdAt, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + Domain: "", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: initAt, + Metadata: map[string]interface{}{"foo": "bar"}, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: updatedAt, + User: "ab79b9647d074e46ac223a8fa297b846", + } + + actual, err := nodes.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() + if err != nil { + t.Errorf("Failed Get nodes. %v", err) + } else { + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestGetNodeInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": 123456789.0, + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": 123456789.0, + "metadata": {"foo": "bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": 123456789.0, + "user": "ab79b9647d074e46ac223a8fa297b846" + } + }`) + }) + + _, err := nodes.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestGetNodeInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "invalid", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "invalid", + "metadata": {"foo": "bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "invalid", + "user": "ab79b9647d074e46ac223a8fa297b846" + } + }`) + }) + + _, err := nodes.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go index d8df0690e8..a44d5019f7 100644 --- a/openstack/clustering/v1/nodes/urls.go +++ b/openstack/clustering/v1/nodes/urls.go @@ -24,3 +24,7 @@ func idURL(client *gophercloud.ServiceClient, id string) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func getURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 7fefc19e15581902b53946945d19e9ff45bb8efe Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 17 Jun 2018 16:55:47 +0000 Subject: [PATCH 0407/2296] Clustering v1: nodes get updates --- .../openstack/clustering/v1/clustering.go | 45 +++++- .../openstack/clustering/v1/clusters_test.go | 2 +- .../clustering/v1/nodes/testing/fixtures.go | 59 +++++++ .../v1/nodes/testing/requests_test.go | 150 +----------------- 4 files changed, 103 insertions(+), 153 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go index 76a4e1a394..6b84cb76e1 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -68,7 +68,7 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, profileID st th.AssertEquals(t, true, actionID != "") t.Logf("Cluster %s action ID: %s", name, actionID) - err = WaitForAction(client, actionID, 600) + err = WaitForAction(client, actionID) th.AssertNoErr(t, err) cluster, err := res.Extract() @@ -123,7 +123,7 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, prof th.AssertEquals(t, true, actionID != "") t.Logf("Node %s action ID: %s", name, actionID) - err = WaitForAction(client, actionID, 600) + err = WaitForAction(client, actionID) th.AssertNoErr(t, err) node, err := res.Extract() @@ -131,6 +131,9 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, prof return nil, err } + err = WaitForNodeStatus(client, node.ID, "ACTIVE") + th.AssertNoErr(t, err) + t.Logf("Successfully created node: %s", node.ID) tools.PrintResource(t, node) @@ -251,7 +254,7 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) } - err = WaitForAction(client, actionID, 600) + err = WaitForAction(client, actionID) if err != nil { t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) } @@ -271,6 +274,11 @@ func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Fatalf("Error deleting node %s: %s:", id, err) } + err = WaitForNodeStatus(client, id, "DELETED") + if err != nil { + t.Fatalf("Error deleting node %s: %s", id, err) + } + t.Logf("Successfully deleted node: %s", id) return @@ -319,8 +327,8 @@ func GetActionID(headers http.Header) (string, error) { return actionID, nil } -func WaitForAction(client *gophercloud.ServiceClient, actionID string, sleepTimeSecs int) error { - return gophercloud.WaitFor(sleepTimeSecs, func() (bool, error) { +func WaitForAction(client *gophercloud.ServiceClient, actionID string) error { + return tools.WaitFor(func() (bool, error) { action, err := actions.Get(client, actionID).Extract() if err != nil { return false, err @@ -330,6 +338,33 @@ func WaitForAction(client *gophercloud.ServiceClient, actionID string, sleepTime return true, nil } + if action.Status == "FAILED" { + return false, fmt.Errorf("Action %s in FAILED state", actionID) + } + + return false, nil + }) +} + +func WaitForNodeStatus(client *gophercloud.ServiceClient, id string, status string) error { + return tools.WaitFor(func() (bool, error) { + latest, err := nodes.Get(client, id).Extract() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETED" { + return true, nil + } + + return false, err + } + + if latest.Status == status { + return true, nil + } + + if latest.Status == "ERROR" { + return false, fmt.Errorf("Node %s in ERROR state", id) + } + return false, nil }) } diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 550174e694..058ad5f424 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -50,7 +50,7 @@ func TestClustersCRUD(t *testing.T) { actionID, err := GetActionID(res.Header) th.AssertNoErr(t, err) - err = WaitForAction(client, actionID, 600) + err = WaitForAction(client, actionID) th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, cluster.ID).Extract() diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go index 17a8e6864f..10ac626759 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures.go +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -160,6 +160,53 @@ var ExpectedList1 = nodes.Node{ var ExpectedList = []nodes.Node{ExpectedList1} +const GetResponse = ` +{ + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "2016-05-13T07:02:20Z", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "2016-05-13T07:02:04Z", + "metadata": {"foo": "bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "2016-05-13T07:02:20Z", + "user": "ab79b9647d074e46ac223a8fa297b846" + } +}` + +var ExpectedGet = nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + Domain: "", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: time.Date(2016, 5, 13, 7, 2, 4, 0, time.UTC), + Metadata: map[string]interface{}{"foo": "bar"}, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), + User: "ab79b9647d074e46ac223a8fa297b846", +} + func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -196,3 +243,15 @@ func HandleDeleteSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, GetResponse) + }) +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 53d3e54d3b..73a314cc78 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,11 +1,8 @@ package testing import ( - "fmt" - "net/http" "strings" "testing" - "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/pagination" @@ -86,150 +83,9 @@ func TestGetNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "2016-05-13T07:02:20Z", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "2016-05-13T07:02:04Z", - "metadata": {"foo": "bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "2016-05-13T07:02:20Z", - "user": "ab79b9647d074e46ac223a8fa297b846" - } - }`) - }) - - initAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:04Z") - createdAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") - expected := nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: createdAt, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - Domain: "", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: initAt, - Metadata: map[string]interface{}{"foo": "bar"}, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: updatedAt, - User: "ab79b9647d074e46ac223a8fa297b846", - } + HandleGetSuccessfully(t) actual, err := nodes.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() - if err != nil { - t.Errorf("Failed Get nodes. %v", err) - } else { - th.AssertDeepEquals(t, expected, *actual) - } -} - -func TestGetNodeInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": 123456789.0, - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": 123456789.0, - "metadata": {"foo": "bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": 123456789.0, - "user": "ab79b9647d074e46ac223a8fa297b846" - } - }`) - }) - - _, err := nodes.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestGetNodeInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "invalid", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "invalid", - "metadata": {"foo": "bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "invalid", - "user": "ab79b9647d074e46ac223a8fa297b846" - } - }`) - }) - - _, err := nodes.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() - th.AssertEquals(t, false, err == nil) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedGet, *actual) } From 81358b8155cad4f33ead1a6d2fa03ba1d691ec19 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 09:09:51 -0700 Subject: [PATCH 0408/2296] senlin-nodes-update --- openstack/clustering/v1/nodes/doc.go | 14 ++ openstack/clustering/v1/nodes/requests.go | 38 +++ openstack/clustering/v1/nodes/results.go | 9 +- .../v1/nodes/testing/requests_test.go | 226 ++++++++++++++++++ openstack/clustering/v1/nodes/urls.go | 4 + 5 files changed, 287 insertions(+), 4 deletions(-) diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index 9f1928991d..9db40e40d4 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -38,6 +38,19 @@ Example to List Nodes fmt.Printf("%+v\n", node) } +Example to Update Nodes + + opts := nodes.UpdateOpts{ + Name: "new-node-name", + } + + nodeID := "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1" + node, err := nodes.Update(serviceClient, nodeID, opts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", node) Example to Delete a Node @@ -54,6 +67,7 @@ Example to Get Node if err != nil { panic(err) } + fmt.Printf("%+v\n", node) */ diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index 3192cd9ed3..5786de0247 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -45,6 +45,44 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// UpdateOpts params +type UpdateOpts struct { + Node map[string]interface{} `json:"-"` + Name string `json:"name,omitempty"` + ProfileID string `json:"profile_id,omitempty"` + Role string `json:"role,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +// UpdateOptsBuilder params +type UpdateOptsBuilder interface { + ToNodeUpdateMap() (map[string]interface{}, error) +} + +// ToClusterUpdateMap constructs a request body from CreateOpts. +func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "node") +} + +// Update requests the update of a node. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToNodeUpdateMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} + // ListOptsBuilder Builder. type ListOptsBuilder interface { ToNodeListQuery() (string, error) diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index 8fbcbb7894..b62d24a5e3 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -30,16 +30,17 @@ type DeleteResult struct { gophercloud.ErrResult } +// UpdateResult is the response of a Update operations. +type UpdateResult struct { + commonResult +} + // Extract provides access to the individual node returned by Get and extracts Node func (r commonResult) Extract() (*Node, error) { var s struct { Node *Node `json:"node"` } err := r.ExtractInto(&s) - if err != nil { - return &Node{}, err - } - return s.Node, err } diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 73a314cc78..0eb330e845 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,8 +1,11 @@ package testing import ( + "fmt" + "net/http" "strings" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/pagination" @@ -89,3 +92,226 @@ func TestGetNode(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedGet, *actual) } + +func TestUpdateNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "2016-05-13T07:02:20Z", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "2016-05-13T08:02:04Z", + "metadata": {"foo":"bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "2016-05-13T09:02:04Z", + "user": "ab79b9647d074e46ac223a8fa297b846" } + } + }`) + }) + + createdAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") + initAt, _ := time.Parse(time.RFC3339, "2016-05-13T08:02:04Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-05-13T09:02:04Z") + expected := nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: createdAt, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + Domain: "", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: initAt, + Metadata: map[string]interface{}{"foo": "bar"}, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: updatedAt, + User: "ab79b9647d074e46ac223a8fa297b846", + } + + nodeOpts := nodes.UpdateOpts{ + Name: "node-e395be1e-002", + } + actual, err := nodes.Update(fake.ServiceClient(), "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", nodeOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, *actual) +} + +func TestUpdateNodeEmptyTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": null, + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": null, + "metadata": {"foo":"bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": null, + "user": "ab79b9647d074e46ac223a8fa297b846" } + } + }`) + }) + + expected := nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: time.Time{}, + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + Domain: "", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: time.Time{}, + Metadata: map[string]interface{}{"foo": "bar"}, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: time.Time{}, + User: "ab79b9647d074e46ac223a8fa297b846", + } + + nodeOpts := nodes.UpdateOpts{ + Name: "node-e395be1e-002", + } + actual, err := nodes.Update(fake.ServiceClient(), "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", nodeOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, *actual) +} + +func TestUpdateNodeInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": 123456789.0, + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": 123456789.0, + "metadata": {"foo":"bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": 123456789.0, + "user": "ab79b9647d074e46ac223a8fa297b846" } + } + }`) + }) + nodeOpts := nodes.UpdateOpts{ + Name: "node-e395be1e-002", + } + _, err := nodes.Update(fake.ServiceClient(), "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", nodeOpts).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestUpdateNodeInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "invalid", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "invalid", + "metadata": {"foo":"bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "invalid", + "user": "ab79b9647d074e46ac223a8fa297b846" } + } + }`) + }) + nodeOpts := nodes.UpdateOpts{ + Name: "node-e395be1e-002", + } + _, err := nodes.Update(fake.ServiceClient(), "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", nodeOpts).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go index a44d5019f7..5479145d0b 100644 --- a/openstack/clustering/v1/nodes/urls.go +++ b/openstack/clustering/v1/nodes/urls.go @@ -28,3 +28,7 @@ func deleteURL(client *gophercloud.ServiceClient, id string) string { func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From d7e00f419dbef7af94fd38f234e76f6f9253cb93 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jun 2018 00:47:28 +0000 Subject: [PATCH 0409/2296] Clustering v1: nodes update and acceptance test updates --- acceptance/clients/clients.go | 2 + .../openstack/clustering/v1/clustering.go | 3 + .../openstack/clustering/v1/nodes_test.go | 25 ++ openstack/clustering/v1/nodes/requests.go | 1 - .../clustering/v1/nodes/testing/fixtures.go | 59 +++++ .../v1/nodes/testing/requests_test.go | 218 +----------------- 6 files changed, 92 insertions(+), 216 deletions(-) diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index c07fecf22a..155a3c6744 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -495,6 +495,8 @@ func NewClusteringV1Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewClusteringV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go index 6b84cb76e1..2acc652869 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -136,6 +136,9 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, prof t.Logf("Successfully created node: %s", node.ID) + node, err = nodes.Get(client, node.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, node) tools.PrintResource(t, node.CreatedAt) diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index d6dbfb6378..0890d45ff1 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -41,4 +42,28 @@ func TestNodesCRUD(t *testing.T) { } th.AssertEquals(t, found, true) + + // Test nodes update + t.Logf("Attempting to update node %s", node.ID) + + updateOpts := nodes.UpdateOpts{ + Metadata: map[string]interface{}{ + "bar": "baz", + }, + } + + res := nodes.Update(client, node.ID, updateOpts) + th.AssertNoErr(t, res.Err) + + actionID, err := GetActionID(res.Header) + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + node, err = nodes.Get(client, node.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, node) + tools.PrintResource(t, node.Metadata) } diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index 5786de0247..c0e1f84601 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -47,7 +47,6 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create // UpdateOpts params type UpdateOpts struct { - Node map[string]interface{} `json:"-"` Name string `json:"name,omitempty"` ProfileID string `json:"profile_id,omitempty"` Role string `json:"role,omitempty"` diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go index 10ac626759..25dd92dc55 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures.go +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -207,6 +207,53 @@ var ExpectedGet = nodes.Node{ User: "ab79b9647d074e46ac223a8fa297b846", } +const UpdateResponse = ` +{ + "node": { + "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + "created_at": "2016-05-13T07:02:20Z", + "data": {}, + "dependents": {}, + "domain": null, + "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + "index": 2, + "init_at": "2016-05-13T08:02:04Z", + "metadata": {"foo":"bar"}, + "name": "node-e395be1e-002", + "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", + "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + "profile_name": "pcirros", + "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "role": "", + "status": "ACTIVE", + "status_reason": "Creation succeeded", + "updated_at": "2016-05-13T09:02:04Z", + "user": "ab79b9647d074e46ac223a8fa297b846" + } +}` + +var ExpectedUpdate = nodes.Node{ + ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", + CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), + Data: map[string]interface{}{}, + Dependents: map[string]interface{}{}, + Domain: "", + ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", + Index: 2, + InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), + Metadata: map[string]interface{}{"foo": "bar"}, + Name: "node-e395be1e-002", + PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", + ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", + ProfileName: "pcirros", + ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Role: "", + Status: "ACTIVE", + StatusReason: "Creation succeeded", + UpdatedAt: time.Date(2016, 5, 13, 9, 2, 4, 0, time.UTC), + User: "ab79b9647d074e46ac223a8fa297b846", +} + func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -255,3 +302,15 @@ func HandleGetSuccessfully(t *testing.T) { fmt.Fprint(w, GetResponse) }) } + +func HandleUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, UpdateResponse) + }) +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 0eb330e845..1a8e72adc1 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,11 +1,8 @@ package testing import ( - "fmt" - "net/http" "strings" "testing" - "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/pagination" @@ -97,221 +94,12 @@ func TestUpdateNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "2016-05-13T07:02:20Z", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "2016-05-13T08:02:04Z", - "metadata": {"foo":"bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "2016-05-13T09:02:04Z", - "user": "ab79b9647d074e46ac223a8fa297b846" } - } - }`) - }) - - createdAt, _ := time.Parse(time.RFC3339, "2016-05-13T07:02:20Z") - initAt, _ := time.Parse(time.RFC3339, "2016-05-13T08:02:04Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-05-13T09:02:04Z") - expected := nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: createdAt, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - Domain: "", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: initAt, - Metadata: map[string]interface{}{"foo": "bar"}, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: updatedAt, - User: "ab79b9647d074e46ac223a8fa297b846", - } + HandleUpdateSuccessfully(t) nodeOpts := nodes.UpdateOpts{ Name: "node-e395be1e-002", } - actual, err := nodes.Update(fake.ServiceClient(), "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", nodeOpts).Extract() + actual, err := nodes.Update(fake.ServiceClient(), ExpectedUpdate.ID, nodeOpts).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, *actual) -} - -func TestUpdateNodeEmptyTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": null, - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": null, - "metadata": {"foo":"bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": null, - "user": "ab79b9647d074e46ac223a8fa297b846" } - } - }`) - }) - - expected := nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: time.Time{}, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - Domain: "", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: time.Time{}, - Metadata: map[string]interface{}{"foo": "bar"}, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: time.Time{}, - User: "ab79b9647d074e46ac223a8fa297b846", - } - - nodeOpts := nodes.UpdateOpts{ - Name: "node-e395be1e-002", - } - actual, err := nodes.Update(fake.ServiceClient(), "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", nodeOpts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, *actual) -} - -func TestUpdateNodeInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": 123456789.0, - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": 123456789.0, - "metadata": {"foo":"bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": 123456789.0, - "user": "ab79b9647d074e46ac223a8fa297b846" } - } - }`) - }) - nodeOpts := nodes.UpdateOpts{ - Name: "node-e395be1e-002", - } - _, err := nodes.Update(fake.ServiceClient(), "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", nodeOpts).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestUpdateNodeInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "invalid", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "invalid", - "metadata": {"foo":"bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "invalid", - "user": "ab79b9647d074e46ac223a8fa297b846" } - } - }`) - }) - nodeOpts := nodes.UpdateOpts{ - Name: "node-e395be1e-002", - } - _, err := nodes.Update(fake.ServiceClient(), "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", nodeOpts).Extract() - th.AssertEquals(t, false, err == nil) + th.AssertDeepEquals(t, ExpectedUpdate, *actual) } From 2c6e37ead978000bf661505ac2843420bb55ca48 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0410/2296] senlin-clusters-resize --- openstack/clustering/v1/clusters/doc.go | 24 +++ openstack/clustering/v1/clusters/requests.go | 53 +++++++ openstack/clustering/v1/clusters/results.go | 19 +++ .../v1/clusters/testing/requests_test.go | 139 ++++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 + 5 files changed, 239 insertions(+) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index bef956fee6..9fc2d05ebe 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -66,5 +66,29 @@ Example to Delete a cluster panic(err) } +Example to Resize a cluster + + adjustmentType := "CHANGE_IN_CAPACITY" + number := 1 + maxSize := 5 + minSize := 1 + minStep := 1 + strict := true + + resizeOpts := clusters.ResizeOpts{ + AdjustmentType: adjustmentType, + Number: number, + MaxSize: &maxSize, + MinSize: &minSize, + MinStep: &minStep, + Strict: &strict, + } + + actionID, err := clusters.Resize(client, clusterName, resizeOpts).Extract() + if err != nil { + t.Fatalf("Unable to resize cluster: %v", err) + } + fmt.Println("Resize actionID", actionID) + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 44a234158a..43651ae406 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -1,7 +1,9 @@ package clusters import ( + "fmt" "net/http" + "reflect" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" @@ -150,3 +152,54 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { } return } + +// ResizeOpts params +type ResizeOpts struct { + AdjustmentType string `json:"adjustment_type,omitempty"` + Number interface{} `json:"number,omitempty"` + MinSize *int `json:"min_size,omitempty"` + MaxSize *int `json:"max_size,omitempty"` + MinStep *int `json:"min_step,omitempty"` + Strict *bool `json:"strict,omitempty"` +} + +// ToClusterResizeMap constructs a request body from ResizeOpts. +func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "resize") +} + +func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOpts) (r ResizeResult) { + if opts.AdjustmentType != "" && opts.Number == nil { + r.Err = fmt.Errorf("Number field MUST NOT be empty when AdjustmentType field used") + return + } + + switch opts.Number.(type) { + case int, int32, int64, *int, *int32, *int64: + // Valid type. Always allow + case float32, float64, *float32, *float64: + if opts.AdjustmentType != "CHANGE_IN_PERCENTAGE" { + r.Err = fmt.Errorf("Only AdjustmentType=CHANGE_IN_PERCENTAGE allows float value for Number field"+ + "AdjustmentType=%s", opts.AdjustmentType) + return + } + default: + r.Err = fmt.Errorf("Number field must be either int or float %s", reflect.TypeOf(opts.Number)) + return + } + + b, err := opts.ToClusterResizeMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index df71ecb420..e7be979913 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -23,6 +23,11 @@ type GetResult struct { commonResult } +// ResizeResult is the response of a Get operations. +type ResizeResult struct { + commonResult +} + // UpdateResult is the response of a Update operations. type UpdateResult struct { commonResult @@ -62,6 +67,20 @@ func (r commonResult) Extract() (*Cluster, error) { return s.Cluster, err } +type Action struct { + Action string `json:"action"` +} + +func (r ResizeResult) Extract() (string, error) { + var s Action + err := r.ExtractInto(&s) + if err != nil { + return s.Action, err + } + + return s.Action, nil +} + // ClusterPage contains a single page of all clusters from a List call. type ClusterPage struct { pagination.LinkedPageBase diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 24600c06ad..80e3d05661 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -182,3 +182,142 @@ func TestDeleteCluster(t *testing.T) { err := clusters.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").ExtractErr() th.AssertNoErr(t, err) } + +func TestResizeCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + maxSize := 5 + minSize := 1 + number := -2 + strict := true + opts := clusters.ResizeOpts{ + AdjustmentType: "CHANGE_IN_CAPACITY", + MaxSize: &maxSize, + MinSize: &minSize, + Number: &number, + Strict: &strict, + } + + actionID, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") +} + +// Test case for Number field having a float value +func TestResizeClusterNumberFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + maxSize := 5 + minSize := 1 + number := 100.0 + strict := true + opts := clusters.ResizeOpts{ + AdjustmentType: "CHANGE_IN_PERCENTAGE", + MaxSize: &maxSize, + MinSize: &minSize, + Number: &number, + Strict: &strict, + } + + result, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") +} + +// Negative test case for missing Number field which is required when AdjustmentType is specified +func TestResizeClusterInvalidParamsMissingNumber(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + maxSize := 5 + minSize := 1 + strict := true + opts := clusters.ResizeOpts{ + AdjustmentType: "CHANGE_IN_CAPACITY", + MaxSize: &maxSize, + MinSize: &minSize, + //Number: MISSING, + Strict: &strict, + } + + _, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + isValid := err == nil + th.AssertEquals(t, false, isValid) +} + +// Negative test case for missing Number field which is required when AdjustmentType is specified +func TestResizeClusterInvalidParamsNumberFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + maxSize := 5 + minSize := 1 + number := 100.0 + strict := true + opts := clusters.ResizeOpts{ + AdjustmentType: "CHANGE_IN_CAPACITY", + MaxSize: &maxSize, + MinSize: &minSize, + Number: &number, + Strict: &strict, + } + + _, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + isValid := err == nil + th.AssertEquals(t, false, isValid) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 272e687030..70d2930c7c 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -5,6 +5,10 @@ import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "clusters" +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "actions") +} + func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } From a33346966872d1d9e67970f898d2db387c4c6ef3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jun 2018 02:42:20 +0000 Subject: [PATCH 0411/2296] Clustering v1: clusters resize updates --- .../openstack/clustering/v1/clusters_test.go | 32 ++++++ openstack/clustering/v1/clusters/doc.go | 5 +- openstack/clustering/v1/clusters/requests.go | 45 +++++---- .../v1/clusters/testing/fixtures.go | 19 ++++ .../v1/clusters/testing/requests_test.go | 97 +++++-------------- 5 files changed, 102 insertions(+), 96 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 058ad5f424..3ce326dc8e 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -59,3 +59,35 @@ func TestClustersCRUD(t *testing.T) { tools.PrintResource(t, newCluster) } + +func TestClustersResize(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + iTrue := true + resizeOpts := clusters.ResizeOpts{ + AdjustmentType: clusters.ChangeInCapacityAdjustment, + Number: 1, + Strict: &iTrue, + } + + actionID, err := clusters.Resize(client, cluster.ID, resizeOpts).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + newCluster, err := clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newCluster.DesiredCapacity, 2) + + tools.PrintResource(t, newCluster) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 9fc2d05ebe..5d60aa3bf1 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -52,7 +52,7 @@ Example to Update a cluster } clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" - cluster, err := clusters.Update(serviceClient, clusterName, clusters.UpdateOpts{Name: newClusterName}).Extract() + cluster, err := clusters.Update(serviceClient, clusterName, opts).Extract() if err != nil { panic(err) } @@ -68,7 +68,6 @@ Example to Delete a cluster Example to Resize a cluster - adjustmentType := "CHANGE_IN_CAPACITY" number := 1 maxSize := 5 minSize := 1 @@ -76,7 +75,7 @@ Example to Resize a cluster strict := true resizeOpts := clusters.ResizeOpts{ - AdjustmentType: adjustmentType, + AdjustmentType: clusters.ChangeInCapacityAdjustment, Number: number, MaxSize: &maxSize, MinSize: &minSize, diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 43651ae406..3b71fc7ff5 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -3,12 +3,19 @@ package clusters import ( "fmt" "net/http" - "reflect" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) +type AdjustmentType string + +const ( + ExactCapacityAdjustment AdjustmentType = "EXACT_CAPACITY" + ChangeInCapacityAdjustment AdjustmentType = "CHANGE_IN_CAPACITY" + ChangeInPercentageAdjustment AdjustmentType = "CHANGE_IN_PERCENTAGE" +) + // CreateOptsBuilder Builder. type CreateOptsBuilder interface { ToClusterCreateMap() (map[string]interface{}, error) @@ -155,39 +162,35 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { // ResizeOpts params type ResizeOpts struct { - AdjustmentType string `json:"adjustment_type,omitempty"` - Number interface{} `json:"number,omitempty"` - MinSize *int `json:"min_size,omitempty"` - MaxSize *int `json:"max_size,omitempty"` - MinStep *int `json:"min_step,omitempty"` - Strict *bool `json:"strict,omitempty"` + AdjustmentType AdjustmentType `json:"adjustment_type,omitempty"` + Number interface{} `json:"number,omitempty"` + MinSize *int `json:"min_size,omitempty"` + MaxSize *int `json:"max_size,omitempty"` + MinStep *int `json:"min_step,omitempty"` + Strict *bool `json:"strict,omitempty"` } // ToClusterResizeMap constructs a request body from ResizeOpts. func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "resize") -} - -func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOpts) (r ResizeResult) { if opts.AdjustmentType != "" && opts.Number == nil { - r.Err = fmt.Errorf("Number field MUST NOT be empty when AdjustmentType field used") - return + return nil, fmt.Errorf("Number field MUST NOT be empty when AdjustmentType field used") } switch opts.Number.(type) { - case int, int32, int64, *int, *int32, *int64: + case nil, int, int32, int64: // Valid type. Always allow - case float32, float64, *float32, *float64: - if opts.AdjustmentType != "CHANGE_IN_PERCENTAGE" { - r.Err = fmt.Errorf("Only AdjustmentType=CHANGE_IN_PERCENTAGE allows float value for Number field"+ - "AdjustmentType=%s", opts.AdjustmentType) - return + case float32, float64: + if opts.AdjustmentType != ChangeInPercentageAdjustment { + return nil, fmt.Errorf("Only ChangeInPercentageAdjustment allows float value for Number field") } default: - r.Err = fmt.Errorf("Number field must be either int or float %s", reflect.TypeOf(opts.Number)) - return + return nil, fmt.Errorf("Number field must be either int, float, or omitted") } + return gophercloud.BuildRequestBody(opts, "resize") +} + +func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOpts) (r ResizeResult) { b, err := opts.ToClusterResizeMap() if err != nil { r.Err = err diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index aa8ce49065..b90cff607a 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -335,6 +335,13 @@ const UpdateResponse_EmptyTime = ` } }` +const ResizeResult = ` +{ + "action": "2a0ff107-e789-4660-a122-3816c43af703" +}` + +const ExpectedResizeActionID = "2a0ff107-e789-4660-a122-3816c43af703" + func HandleCreateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -442,3 +449,15 @@ func HandleDeleteClusterSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleResizeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ResizeResult) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 80e3d05661..82f4469a03 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -187,18 +187,7 @@ func TestResizeCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) + HandleResizeSuccessfully(t) maxSize := 5 minSize := 1 @@ -208,33 +197,17 @@ func TestResizeCluster(t *testing.T) { AdjustmentType: "CHANGE_IN_CAPACITY", MaxSize: &maxSize, MinSize: &minSize, - Number: &number, + Number: number, Strict: &strict, } actionID, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") + th.AssertEquals(t, ExpectedResizeActionID, actionID) } // Test case for Number field having a float value func TestResizeClusterNumberFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) - maxSize := 5 minSize := 1 number := 100.0 @@ -243,33 +216,31 @@ func TestResizeClusterNumberFloat(t *testing.T) { AdjustmentType: "CHANGE_IN_PERCENTAGE", MaxSize: &maxSize, MinSize: &minSize, - Number: &number, + Number: number, Strict: &strict, } - result, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + _, err := opts.ToClusterResizeMap() th.AssertNoErr(t, err) - th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") } -// Negative test case for missing Number field which is required when AdjustmentType is specified -func TestResizeClusterInvalidParamsMissingNumber(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) +// Test case for missing Number field. +func TestResizeClusterMissingNumber(t *testing.T) { + maxSize := 5 + minSize := 1 + strict := true + opts := clusters.ResizeOpts{ + MaxSize: &maxSize, + MinSize: &minSize, + Strict: &strict, + } - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) + _, err := opts.ToClusterResizeMap() + th.AssertNoErr(t, err) +} +// Test case for missing Number field which is required when AdjustmentType is specified +func TestResizeClusterInvalidParamsMissingNumber(t *testing.T) { maxSize := 5 minSize := 1 strict := true @@ -277,34 +248,16 @@ func TestResizeClusterInvalidParamsMissingNumber(t *testing.T) { AdjustmentType: "CHANGE_IN_CAPACITY", MaxSize: &maxSize, MinSize: &minSize, - //Number: MISSING, - Strict: &strict, + Strict: &strict, } - _, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + _, err := opts.ToClusterResizeMap() isValid := err == nil th.AssertEquals(t, false, isValid) } -// Negative test case for missing Number field which is required when AdjustmentType is specified +// Test case for float Number field which is only valid for CHANGE_IN_PERCENTAGE. func TestResizeClusterInvalidParamsNumberFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) - maxSize := 5 minSize := 1 number := 100.0 @@ -313,11 +266,11 @@ func TestResizeClusterInvalidParamsNumberFloat(t *testing.T) { AdjustmentType: "CHANGE_IN_CAPACITY", MaxSize: &maxSize, MinSize: &minSize, - Number: &number, + Number: number, Strict: &strict, } - _, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + _, err := opts.ToClusterResizeMap() isValid := err == nil th.AssertEquals(t, false, isValid) } From c7095bf22b35e2fbad7a90236bd2c7549eb3ab51 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0412/2296] senlin-clusters-scalein --- openstack/clustering/v1/clusters/doc.go | 12 ++++++++ openstack/clustering/v1/clusters/requests.go | 30 +++++++++++++++++++ openstack/clustering/v1/clusters/results.go | 17 +++++++++++ .../v1/clusters/testing/requests_test.go | 26 ++++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 5d60aa3bf1..b0d923bf59 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -89,5 +89,17 @@ Example to Resize a cluster } fmt.Println("Resize actionID", actionID) +Example to ScaleIn a cluster + + count := 2 + scaleInOpts := clusters.ScaleInOpts{ + Count: &count, + } + clusterID: "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + + action, err := clusters.ScaleIn(computeClient, clusterID, scaleInOpts).Extract() + if err != nil { + panic(err) + } */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 3b71fc7ff5..6e2f1b2a1d 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -204,5 +204,35 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOpts) (r Re if r.Err == nil { r.Header = result.Header } + + return +} + +// ScaleInOpts params +type ScaleInOpts struct { + Count *int `json:"count,omitempty"` +} + +// ToClusterScaleInMap constructs a request body from ScaleInOpts. +func (opts ScaleInOpts) ToClusterScaleInMap(scaleAction string) (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, scaleAction) +} + +// ScaleIn CLI +func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r ScaleInResult) { + b, err := opts.ToClusterScaleInMap("scale_in") + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + return } diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index e7be979913..067610b935 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -33,6 +33,11 @@ type UpdateResult struct { commonResult } +// ScaleInResult is the response of a ScaleIn operations. +type ScaleInResult struct { + commonResult +} + type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` @@ -81,6 +86,18 @@ func (r ResizeResult) Extract() (string, error) { return s.Action, nil } +func (r ScaleInResult) Extract() (string, error) { + var s struct { + Action string `json:"action"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + + return s.Action, nil +} + // ClusterPage contains a single page of all clusters from a List call. type ClusterPage struct { pagination.LinkedPageBase diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 82f4469a03..d2226d3898 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -274,3 +274,29 @@ func TestResizeClusterInvalidParamsNumberFloat(t *testing.T) { isValid := err == nil th.AssertEquals(t, false, isValid) } + +func TestClusterScaleIn(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + count := 5 + scaleOpts := clusters.ScaleInOpts{ + Count: &count, + } + result, err := clusters.ScaleIn(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") +} From 37794317e4b849a20891d8acd3f82191b157652d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jun 2018 05:08:02 +0000 Subject: [PATCH 0413/2296] Clustering v1: clusters scalein updates --- .../openstack/clustering/v1/clusters_test.go | 31 +++++++++++++++++++ openstack/clustering/v1/clusters/requests.go | 1 - .../v1/clusters/testing/fixtures.go | 18 +++++++++-- .../v1/clusters/testing/requests_test.go | 19 +++--------- 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 3ce326dc8e..d0756f92d6 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -91,3 +91,34 @@ func TestClustersResize(t *testing.T) { tools.PrintResource(t, newCluster) } + +func TestClustersScale(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + // reduce cluster size to 0 + count := 1 + scaleInOpts := clusters.ScaleInOpts{ + Count: &count, + } + + actionID, err := clusters.ScaleIn(client, cluster.ID, scaleInOpts).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + newCluster, err := clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newCluster.DesiredCapacity, 0) + + tools.PrintResource(t, newCluster) +} diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 6e2f1b2a1d..27f7ac5fc0 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -218,7 +218,6 @@ func (opts ScaleInOpts) ToClusterScaleInMap(scaleAction string) (map[string]inte return gophercloud.BuildRequestBody(opts, scaleAction) } -// ScaleIn CLI func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r ScaleInResult) { b, err := opts.ToClusterScaleInMap("scale_in") if err != nil { diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index b90cff607a..8f83e5218f 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -335,12 +335,12 @@ const UpdateResponse_EmptyTime = ` } }` -const ResizeResult = ` +const ActionResult = ` { "action": "2a0ff107-e789-4660-a122-3816c43af703" }` -const ExpectedResizeActionID = "2a0ff107-e789-4660-a122-3816c43af703" +const ExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" func HandleCreateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { @@ -458,6 +458,18 @@ func HandleResizeSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ResizeResult) + fmt.Fprint(w, ActionResult) + }) +} + +func HandleScaleInSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ActionResult) }) } diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index d2226d3898..e007327cc1 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -203,7 +203,7 @@ func TestResizeCluster(t *testing.T) { actionID, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedResizeActionID, actionID) + th.AssertEquals(t, ExpectedActionID, actionID) } // Test case for Number field having a float value @@ -279,24 +279,13 @@ func TestClusterScaleIn(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) + HandleScaleInSuccessfully(t) count := 5 scaleOpts := clusters.ScaleInOpts{ Count: &count, } - result, err := clusters.ScaleIn(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOpts).Extract() + actionID, err := clusters.ScaleIn(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") + th.AssertEquals(t, ExpectedActionID, actionID) } From eff9965b027f6a4dc59052817db02a9bff0858e6 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Mon, 11 Jun 2018 16:11:47 -0700 Subject: [PATCH 0414/2296] senlin-clusterpolicies-list --- .../clustering/v1/clusterpolicies/doc.go | 23 +++++ .../clustering/v1/clusterpolicies/requests.go | 41 +++++++++ .../clustering/v1/clusterpolicies/results.go | 56 ++++++++++++ .../v1/clusterpolicies/testing/doc.go | 2 + .../clusterpolicies/testing/requests_test.go | 86 +++++++++++++++++++ .../clustering/v1/clusterpolicies/urls.go | 10 +++ 6 files changed, 218 insertions(+) create mode 100644 openstack/clustering/v1/clusterpolicies/doc.go create mode 100644 openstack/clustering/v1/clusterpolicies/requests.go create mode 100644 openstack/clustering/v1/clusterpolicies/results.go create mode 100644 openstack/clustering/v1/clusterpolicies/testing/doc.go create mode 100644 openstack/clustering/v1/clusterpolicies/testing/requests_test.go create mode 100644 openstack/clustering/v1/clusterpolicies/urls.go diff --git a/openstack/clustering/v1/clusterpolicies/doc.go b/openstack/clustering/v1/clusterpolicies/doc.go new file mode 100644 index 0000000000..8d9e3e1c15 --- /dev/null +++ b/openstack/clustering/v1/clusterpolicies/doc.go @@ -0,0 +1,23 @@ +/* +Package clusterpolicies enables Lists all cluster policies and shows information for a cluster policy from the OpenStack +Clustering Service. + +Example to list cluster policies for a Senlin deployment + + clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" + allPages, err := clusterpolicies.List(serviceClient, clusterID, clusterpolicies.ListOpts{}).AllPages() + if err != nil { + panic(err) + } + + allClusterPolicies, err := clusterpolicies.ExtractClusterPolicies(allPages) + if err != nil { + panic(err) + } + + for _, clusterPolicy := range allClusterPolicies { + fmt.Printf("%+v\n", clusterPolicy) + } + +*/ +package clusterpolicies diff --git a/openstack/clustering/v1/clusterpolicies/requests.go b/openstack/clustering/v1/clusterpolicies/requests.go new file mode 100644 index 0000000000..51874d1447 --- /dev/null +++ b/openstack/clustering/v1/clusterpolicies/requests.go @@ -0,0 +1,41 @@ +package clusterpolicies + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder Builder. +type ListOptsBuilder interface { + ToClusterPolicyListQuery() (string, error) +} + +// ListOpts params +type ListOpts struct { + Enabled *bool `q:"enabled"` + Name string `q:"policy_name"` + Type string `q:"policy_type"` + Sort string `q:"sort"` +} + +// ToPolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToClusterPolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of policies. +func List(client *gophercloud.ServiceClient, clusterID string, opts ListOptsBuilder) pagination.Pager { + url := listURL(client, clusterID) + if opts != nil { + query, err := opts.ToClusterPolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ClusterPolicyPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/clustering/v1/clusterpolicies/results.go b/openstack/clustering/v1/clusterpolicies/results.go new file mode 100644 index 0000000000..3e70b6adb6 --- /dev/null +++ b/openstack/clustering/v1/clusterpolicies/results.go @@ -0,0 +1,56 @@ +package clusterpolicies + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + +// Extract provides access to the individual Policy returned by the Get and +// Create functions. +func (r commonResult) Extract() (*ClusterPolicy, error) { + var s struct { + ClusterPolicy *ClusterPolicy `json:"cluster_policy"` + } + err := r.ExtractInto(&s) + return s.ClusterPolicy, err +} + +type ClusterPolicy struct { + ClusterUUID string `json:"cluster_id"` + ClusterName string `json:"cluster_name"` + Enabled bool `json:"enabled"` + ID string `json:"id"` + PolicyID string `json:"policy_id"` + PolicyName string `json:"policy_name"` + PolicyType string `json:"policy_type"` +} + +// ExtractClusterPolicies provides access to the list of profiles in a page acquired from the ListDetail operation. +func ExtractClusterPolicies(r pagination.Page) ([]ClusterPolicy, error) { + var s struct { + ClusterPolicies []ClusterPolicy `json:"cluster_policies"` + } + err := (r.(ClusterPolicyPage)).ExtractInto(&s) + return s.ClusterPolicies, err +} + +// ClusterPolicyPage contains a single page of all policies from a ListDetails call. +type ClusterPolicyPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines if ClusterPolicyPage contains any results. +func (page ClusterPolicyPage) IsEmpty() (bool, error) { + clusterPolicies, err := ExtractClusterPolicies(page) + return len(clusterPolicies) == 0, err +} diff --git a/openstack/clustering/v1/clusterpolicies/testing/doc.go b/openstack/clustering/v1/clusterpolicies/testing/doc.go new file mode 100644 index 0000000000..c05640742a --- /dev/null +++ b/openstack/clustering/v1/clusterpolicies/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_cluster_policies_v1 +package testing diff --git a/openstack/clustering/v1/clusterpolicies/testing/requests_test.go b/openstack/clustering/v1/clusterpolicies/testing/requests_test.go new file mode 100644 index 0000000000..be16191c3b --- /dev/null +++ b/openstack/clustering/v1/clusterpolicies/testing/requests_test.go @@ -0,0 +1,86 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusterpolicies" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListClusterPolicies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/", 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, ` + { + "cluster_policies": [ + { + "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "cluster_name": "cluster4", + "enabled": true, + "id": "06be3a1f-b238-4a96-a737-ceec5714087e", + "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", + "policy_name": "dp01", + "policy_type": "senlin.policy.deletion-1.0" + } + ] + }`) + }) + + pageCount := 0 + clusterpolicies.List(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", clusterpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pageCount++ + actual, err := clusterpolicies.ExtractClusterPolicies(page) + if err != nil { + t.Errorf("Failed to extract cluster policies: %v", err) + return false, err + } + + expected := []clusterpolicies.ClusterPolicy{ + { + ClusterUUID: "7d85f602-a948-4a30-afd4-e84f47471c15", + ClusterName: "cluster4", + Enabled: true, + ID: "06be3a1f-b238-4a96-a737-ceec5714087e", + PolicyID: "714fe676-a08f-4196-b7af-61d52eeded15", + PolicyName: "dp01", + PolicyType: "senlin.policy.deletion-1.0", + }, + } + + th.AssertDeepEquals(t, expected, actual) + + return true, nil + }) + + if pageCount != 1 { + t.Errorf("Expected 1 page, got %d", pageCount) + } +} + +func TestNonJSONCannotBeExtractedIntoClusterPolicies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + clusterpolicies.List(fake.ServiceClient(), "", clusterpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + if _, err := clusterpolicies.ExtractClusterPolicies(page); err == nil { + t.Fatalf("Expected error, got nil") + } + return true, nil + }) +} diff --git a/openstack/clustering/v1/clusterpolicies/urls.go b/openstack/clustering/v1/clusterpolicies/urls.go new file mode 100644 index 0000000000..9d5d9954d5 --- /dev/null +++ b/openstack/clustering/v1/clusterpolicies/urls.go @@ -0,0 +1,10 @@ +package clusterpolicies + +import "github.com/gophercloud/gophercloud" + +var apiVersion = "v1" +var apiName = "clusters" + +func listURL(client *gophercloud.ServiceClient, clusterID string) string { + return client.ServiceURL(apiVersion, apiName, clusterID, "policies") +} From 0a753b1a32bcca37380e326c58222cc317afd4c4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jun 2018 05:45:47 +0000 Subject: [PATCH 0415/2296] Clustering v1: clusters policy list updates --- .../openstack/clustering/v1/clusters_test.go | 23 +++++ .../clustering/v1/clusterpolicies/doc.go | 23 ----- .../clustering/v1/clusterpolicies/requests.go | 41 --------- .../clustering/v1/clusterpolicies/results.go | 56 ------------ .../v1/clusterpolicies/testing/doc.go | 2 - .../clusterpolicies/testing/requests_test.go | 86 ------------------- .../clustering/v1/clusterpolicies/urls.go | 10 --- openstack/clustering/v1/clusters/doc.go | 20 ++++- openstack/clustering/v1/clusters/requests.go | 35 ++++++++ openstack/clustering/v1/clusters/results.go | 46 ++++++++-- .../v1/clusters/testing/fixtures.go | 38 ++++++++ .../v1/clusters/testing/requests_test.go | 20 +++++ openstack/clustering/v1/clusters/urls.go | 4 + 13 files changed, 177 insertions(+), 227 deletions(-) delete mode 100644 openstack/clustering/v1/clusterpolicies/doc.go delete mode 100644 openstack/clustering/v1/clusterpolicies/requests.go delete mode 100644 openstack/clustering/v1/clusterpolicies/results.go delete mode 100644 openstack/clustering/v1/clusterpolicies/testing/doc.go delete mode 100644 openstack/clustering/v1/clusterpolicies/testing/requests_test.go delete mode 100644 openstack/clustering/v1/clusterpolicies/urls.go diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index d0756f92d6..0354f98663 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -122,3 +122,26 @@ func TestClustersScale(t *testing.T) { tools.PrintResource(t, newCluster) } + +func TestClustersPolicies(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + allPages, err := clusters.ListPolicies(client, cluster.ID, nil).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err := clusters.ExtractClusterPolicies(allPages) + th.AssertNoErr(t, err) + + for _, v := range allPolicies { + tools.PrintResource(t, v) + } +} diff --git a/openstack/clustering/v1/clusterpolicies/doc.go b/openstack/clustering/v1/clusterpolicies/doc.go deleted file mode 100644 index 8d9e3e1c15..0000000000 --- a/openstack/clustering/v1/clusterpolicies/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Package clusterpolicies enables Lists all cluster policies and shows information for a cluster policy from the OpenStack -Clustering Service. - -Example to list cluster policies for a Senlin deployment - - clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" - allPages, err := clusterpolicies.List(serviceClient, clusterID, clusterpolicies.ListOpts{}).AllPages() - if err != nil { - panic(err) - } - - allClusterPolicies, err := clusterpolicies.ExtractClusterPolicies(allPages) - if err != nil { - panic(err) - } - - for _, clusterPolicy := range allClusterPolicies { - fmt.Printf("%+v\n", clusterPolicy) - } - -*/ -package clusterpolicies diff --git a/openstack/clustering/v1/clusterpolicies/requests.go b/openstack/clustering/v1/clusterpolicies/requests.go deleted file mode 100644 index 51874d1447..0000000000 --- a/openstack/clustering/v1/clusterpolicies/requests.go +++ /dev/null @@ -1,41 +0,0 @@ -package clusterpolicies - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// ListOptsBuilder Builder. -type ListOptsBuilder interface { - ToClusterPolicyListQuery() (string, error) -} - -// ListOpts params -type ListOpts struct { - Enabled *bool `q:"enabled"` - Name string `q:"policy_name"` - Type string `q:"policy_type"` - Sort string `q:"sort"` -} - -// ToPolicyListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToClusterPolicyListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to provide a list of policies. -func List(client *gophercloud.ServiceClient, clusterID string, opts ListOptsBuilder) pagination.Pager { - url := listURL(client, clusterID) - if opts != nil { - query, err := opts.ToClusterPolicyListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ClusterPolicyPage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/clustering/v1/clusterpolicies/results.go b/openstack/clustering/v1/clusterpolicies/results.go deleted file mode 100644 index 3e70b6adb6..0000000000 --- a/openstack/clustering/v1/clusterpolicies/results.go +++ /dev/null @@ -1,56 +0,0 @@ -package clusterpolicies - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// GetResult is the response of a Get operations. -type GetResult struct { - commonResult -} - -// Extract provides access to the individual Policy returned by the Get and -// Create functions. -func (r commonResult) Extract() (*ClusterPolicy, error) { - var s struct { - ClusterPolicy *ClusterPolicy `json:"cluster_policy"` - } - err := r.ExtractInto(&s) - return s.ClusterPolicy, err -} - -type ClusterPolicy struct { - ClusterUUID string `json:"cluster_id"` - ClusterName string `json:"cluster_name"` - Enabled bool `json:"enabled"` - ID string `json:"id"` - PolicyID string `json:"policy_id"` - PolicyName string `json:"policy_name"` - PolicyType string `json:"policy_type"` -} - -// ExtractClusterPolicies provides access to the list of profiles in a page acquired from the ListDetail operation. -func ExtractClusterPolicies(r pagination.Page) ([]ClusterPolicy, error) { - var s struct { - ClusterPolicies []ClusterPolicy `json:"cluster_policies"` - } - err := (r.(ClusterPolicyPage)).ExtractInto(&s) - return s.ClusterPolicies, err -} - -// ClusterPolicyPage contains a single page of all policies from a ListDetails call. -type ClusterPolicyPage struct { - pagination.SinglePageBase -} - -// IsEmpty determines if ClusterPolicyPage contains any results. -func (page ClusterPolicyPage) IsEmpty() (bool, error) { - clusterPolicies, err := ExtractClusterPolicies(page) - return len(clusterPolicies) == 0, err -} diff --git a/openstack/clustering/v1/clusterpolicies/testing/doc.go b/openstack/clustering/v1/clusterpolicies/testing/doc.go deleted file mode 100644 index c05640742a..0000000000 --- a/openstack/clustering/v1/clusterpolicies/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_cluster_policies_v1 -package testing diff --git a/openstack/clustering/v1/clusterpolicies/testing/requests_test.go b/openstack/clustering/v1/clusterpolicies/testing/requests_test.go deleted file mode 100644 index be16191c3b..0000000000 --- a/openstack/clustering/v1/clusterpolicies/testing/requests_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusterpolicies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestListClusterPolicies(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/", 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, ` - { - "cluster_policies": [ - { - "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "cluster_name": "cluster4", - "enabled": true, - "id": "06be3a1f-b238-4a96-a737-ceec5714087e", - "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", - "policy_name": "dp01", - "policy_type": "senlin.policy.deletion-1.0" - } - ] - }`) - }) - - pageCount := 0 - clusterpolicies.List(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", clusterpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - pageCount++ - actual, err := clusterpolicies.ExtractClusterPolicies(page) - if err != nil { - t.Errorf("Failed to extract cluster policies: %v", err) - return false, err - } - - expected := []clusterpolicies.ClusterPolicy{ - { - ClusterUUID: "7d85f602-a948-4a30-afd4-e84f47471c15", - ClusterName: "cluster4", - Enabled: true, - ID: "06be3a1f-b238-4a96-a737-ceec5714087e", - PolicyID: "714fe676-a08f-4196-b7af-61d52eeded15", - PolicyName: "dp01", - PolicyType: "senlin.policy.deletion-1.0", - }, - } - - th.AssertDeepEquals(t, expected, actual) - - return true, nil - }) - - if pageCount != 1 { - t.Errorf("Expected 1 page, got %d", pageCount) - } -} - -func TestNonJSONCannotBeExtractedIntoClusterPolicies(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }) - - clusterpolicies.List(fake.ServiceClient(), "", clusterpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - if _, err := clusterpolicies.ExtractClusterPolicies(page); err == nil { - t.Fatalf("Expected error, got nil") - } - return true, nil - }) -} diff --git a/openstack/clustering/v1/clusterpolicies/urls.go b/openstack/clustering/v1/clusterpolicies/urls.go deleted file mode 100644 index 9d5d9954d5..0000000000 --- a/openstack/clustering/v1/clusterpolicies/urls.go +++ /dev/null @@ -1,10 +0,0 @@ -package clusterpolicies - -import "github.com/gophercloud/gophercloud" - -var apiVersion = "v1" -var apiName = "clusters" - -func listURL(client *gophercloud.ServiceClient, clusterID string) string { - return client.ServiceURL(apiVersion, apiName, clusterID, "policies") -} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index b0d923bf59..26497b7e7e 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -7,7 +7,7 @@ Example to Create a cluster createOpts := clusters.CreateOpts{ Name: "test-cluster", DesiredCapacity: 1, - ProfileUUID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", + ProfileID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", } cluster, err := clusters.Create(serviceClient, createOpts).Extract() @@ -101,5 +101,23 @@ Example to ScaleIn a cluster if err != nil { panic(err) } + +Example to list Policies for a Cluster + + clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" + allPages, err := clusters.ListPolicies(serviceClient, clusterID, nil).AllPages() + if err != nil { + panic(err) + } + + allClusterPolicies, err := clusters.ExtractClusterPolicies(allPages) + if err != nil { + panic(err) + } + + for _, clusterPolicy := range allClusterPolicies { + fmt.Printf("%+v\n", clusterPolicy) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 27f7ac5fc0..0ab15d2ba0 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -235,3 +235,38 @@ func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r return } + +// ListPoliciesOptsBuilder Builder. +type ListPoliciesOptsBuilder interface { + ToClusterListPoliciesQuery() (string, error) +} + +// ListPoliciesOpts params +type ListPoliciesOpts struct { + Enabled *bool `q:"enabled"` + Name string `q:"policy_name"` + Type string `q:"policy_type"` + Sort string `q:"sort"` +} + +// ToClusterPoliciesListQuery formats a ListOpts into a query string. +func (opts ListPoliciesOpts) ToClusterPoliciesListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListPolicies instructs OpenStack to provide a list of policies for a cluster. +func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts ListPoliciesOptsBuilder) pagination.Pager { + url := listPoliciesURL(client, clusterID) + if opts != nil { + query, err := opts.ToClusterListPoliciesQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ClusterPolicyPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 067610b935..fd0924b565 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -23,13 +23,19 @@ type GetResult struct { commonResult } -// ResizeResult is the response of a Get operations. -type ResizeResult struct { +// UpdateResult is the response of a Update operations. +type UpdateResult struct { commonResult } -// UpdateResult is the response of a Update operations. -type UpdateResult struct { +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// ResizeResult is the response of a Get operations. +type ResizeResult struct { commonResult } @@ -38,6 +44,11 @@ type ScaleInResult struct { commonResult } +// ClusterPolicyPage contains a single page of all policies from a ListDetails call. +type ClusterPolicyPage struct { + pagination.SinglePageBase +} + type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` @@ -156,8 +167,27 @@ func (r *Cluster) UnmarshalJSON(b []byte) error { return nil } -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult +type ClusterPolicy struct { + ClusterID string `json:"cluster_id"` + ClusterName string `json:"cluster_name"` + Enabled bool `json:"enabled"` + ID string `json:"id"` + PolicyID string `json:"policy_id"` + PolicyName string `json:"policy_name"` + PolicyType string `json:"policy_type"` +} + +// ExtractClusterPolicies provides access to the list of profiles in a page acquired from the ListDetail operation. +func ExtractClusterPolicies(r pagination.Page) ([]ClusterPolicy, error) { + var s struct { + ClusterPolicies []ClusterPolicy `json:"cluster_policies"` + } + err := (r.(ClusterPolicyPage)).ExtractInto(&s) + return s.ClusterPolicies, err +} + +// IsEmpty determines if ClusterPolicyPage contains any results. +func (page ClusterPolicyPage) IsEmpty() (bool, error) { + clusterPolicies, err := ExtractClusterPolicies(page) + return len(clusterPolicies) == 0, err } diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 8f83e5218f..461833e46a 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -342,6 +342,32 @@ const ActionResult = ` const ExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" +const ListPoliciesResult = `{ + "cluster_policies": [ + { + "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "cluster_name": "cluster4", + "enabled": true, + "id": "06be3a1f-b238-4a96-a737-ceec5714087e", + "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", + "policy_name": "dp01", + "policy_type": "senlin.policy.deletion-1.0" + } + ] +}` + +var ExpectedClusterPolicy = clusters.ClusterPolicy{ + ClusterID: "7d85f602-a948-4a30-afd4-e84f47471c15", + ClusterName: "cluster4", + Enabled: true, + ID: "06be3a1f-b238-4a96-a737-ceec5714087e", + PolicyID: "714fe676-a08f-4196-b7af-61d52eeded15", + PolicyName: "dp01", + PolicyType: "senlin.policy.deletion-1.0", +} + +var ExpectedListPolicies = []clusters.ClusterPolicy{ExpectedClusterPolicy} + func HandleCreateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -473,3 +499,15 @@ func HandleScaleInSuccessfully(t *testing.T) { fmt.Fprint(w, ActionResult) }) } + +func HandleListPoliciesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies", 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, ListPoliciesResult) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index e007327cc1..0775057294 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -289,3 +289,23 @@ func TestClusterScaleIn(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } + +func TestListClusterPolicies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleListPoliciesSuccessfully(t) + + pageCount := 0 + err := clusters.ListPolicies(fake.ServiceClient(), ExpectedClusterPolicy.ClusterID, nil).EachPage(func(page pagination.Page) (bool, error) { + pageCount++ + actual, err := clusters.ExtractClusterPolicies(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedListPolicies, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + th.AssertEquals(t, pageCount, 1) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 70d2930c7c..9f66ac63e5 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -36,3 +36,7 @@ func updateURL(client *gophercloud.ServiceClient, id string) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func listPoliciesURL(client *gophercloud.ServiceClient, clusterID string) string { + return client.ServiceURL(apiVersion, apiName, clusterID, "policies") +} From e0c2ca972feb712afe62fa7ef516b676400b9d4c Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Mon, 11 Jun 2018 16:11:47 -0700 Subject: [PATCH 0416/2296] senlin-clusterpolicies-get --- openstack/clustering/v1/clusters/doc.go | 7 +++ openstack/clustering/v1/clusters/requests.go | 7 +++ openstack/clustering/v1/clusters/results.go | 18 ++++++++ .../v1/clusters/testing/requests_test.go | 44 +++++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 ++ 5 files changed, 80 insertions(+) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 26497b7e7e..15a785a8a5 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -119,5 +119,12 @@ Example to list Policies for a Cluster fmt.Printf("%+v\n", clusterPolicy) } +Example to get cluster-policies + + clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" + profileID := "714fe676-a08f-4196-b7af-61d52eeded15" + clusterPolicy, err := clusterpolicies.Get(serviceCLient, clusterID, profileID).Extract() + fmt.Println("ClusterPolicy=", clusterPolicy) + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 0ab15d2ba0..4f35e011ec 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -270,3 +270,10 @@ func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts List return ClusterPolicyPage{pagination.SinglePageBase(r)} }) } + +// Get retrieves details of a single cluster-policy. Use Extract to convert its +// result into a Node. +func Get(client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetResult) { + _, r.Err = client.Get(getURL(client, clusterID, policyID), &r.Body, nil) + return +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index fd0924b565..5c4163413f 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -191,3 +191,21 @@ func (page ClusterPolicyPage) IsEmpty() (bool, error) { clusterPolicies, err := ExtractClusterPolicies(page) return len(clusterPolicies) == 0, err } + +// GetPolicyResult is the response of a Get operations. +type GetPolicyResult struct { + commonResult +} + +// Extract provides access to the individual Policy returned by the Get and +// Create functions. +func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { + var s struct { + ClusterPolicy *ClusterPolicy `json:"cluster_policy"` + } + err := r.ExtractInto(&s) + if err != nil { + return nil, err + } + return s.ClusterPolicy, err +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 0775057294..e5ef90a802 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -309,3 +309,47 @@ func TestListClusterPolicies(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, pageCount, 1) } + +func TestGetClusterPolicies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies/714fe676-a08f-4196-b7af-61d52eeded15", 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, ` + { + "cluster_policy": + { + "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "cluster_name": "cluster4", + "enabled": true, + "id": "06be3a1f-b238-4a96-a737-ceec5714087e", + "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", + "policy_name": "dp01", + "policy_type": "senlin.policy.deletion-1.0" + } + }`) + }) + + expected := clusterpolicies.ClusterPolicy{ + ClusterUUID: "7d85f602-a948-4a30-afd4-e84f47471c15", + ClusterName: "cluster4", + Enabled: true, + ID: "06be3a1f-b238-4a96-a737-ceec5714087e", + PolicyID: "714fe676-a08f-4196-b7af-61d52eeded15", + PolicyName: "dp01", + PolicyType: "senlin.policy.deletion-1.0", + } + + actual, err := clusterpolicies.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", "714fe676-a08f-4196-b7af-61d52eeded15").Extract() + if err != nil { + t.Errorf("Failed Get cluster policies. %v", err) + } else { + th.AssertDeepEquals(t, expected, *actual) + } +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 9f66ac63e5..a67f05bce4 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -40,3 +40,7 @@ func deleteURL(client *gophercloud.ServiceClient, id string) string { func listPoliciesURL(client *gophercloud.ServiceClient, clusterID string) string { return client.ServiceURL(apiVersion, apiName, clusterID, "policies") } + +func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID string) string { + return client.ServiceURL(apiVersion, apiName, clusterID, "policies", policyID) +} From 3a4397f365e50ee9fbe64ef4881b65e51cd377df Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jun 2018 06:13:57 +0000 Subject: [PATCH 0417/2296] Clustering v1: clusters policy get updates --- openstack/clustering/v1/clusters/doc.go | 8 +++- openstack/clustering/v1/clusters/requests.go | 7 ++-- openstack/clustering/v1/clusters/results.go | 13 +++--- .../v1/clusters/testing/fixtures.go | 25 +++++++++++ .../v1/clusters/testing/requests_test.go | 41 ++----------------- 5 files changed, 43 insertions(+), 51 deletions(-) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 15a785a8a5..1182635796 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -119,12 +119,16 @@ Example to list Policies for a Cluster fmt.Printf("%+v\n", clusterPolicy) } -Example to get cluster-policies +Example to Get a Cluster Policy clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" profileID := "714fe676-a08f-4196-b7af-61d52eeded15" clusterPolicy, err := clusterpolicies.Get(serviceCLient, clusterID, profileID).Extract() - fmt.Println("ClusterPolicy=", clusterPolicy) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", clusterPolicy) */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 4f35e011ec..8d210928c5 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -271,9 +271,8 @@ func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts List }) } -// Get retrieves details of a single cluster-policy. Use Extract to convert its -// result into a Node. -func Get(client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetResult) { - _, r.Err = client.Get(getURL(client, clusterID, policyID), &r.Body, nil) +// GetPolicy retrieves details of a cluster policy. +func GetPolicy(client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetPolicyResult) { + _, r.Err = client.Get(getPolicyURL(client, clusterID, policyID), &r.Body, nil) return } diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 5c4163413f..8f1f964ed0 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -49,6 +49,11 @@ type ClusterPolicyPage struct { pagination.SinglePageBase } +// GetPolicyResult is the response of a Get operations. +type GetPolicyResult struct { + commonResult +} + type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` @@ -192,11 +197,6 @@ func (page ClusterPolicyPage) IsEmpty() (bool, error) { return len(clusterPolicies) == 0, err } -// GetPolicyResult is the response of a Get operations. -type GetPolicyResult struct { - commonResult -} - // Extract provides access to the individual Policy returned by the Get and // Create functions. func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { @@ -204,8 +204,5 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { ClusterPolicy *ClusterPolicy `json:"cluster_policy"` } err := r.ExtractInto(&s) - if err != nil { - return nil, err - } return s.ClusterPolicy, err } diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 461833e46a..1c135e9c2e 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -368,6 +368,19 @@ var ExpectedClusterPolicy = clusters.ClusterPolicy{ var ExpectedListPolicies = []clusters.ClusterPolicy{ExpectedClusterPolicy} +const GetPolicyResponse = ` +{ + "cluster_policy": { + "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", + "cluster_name": "cluster4", + "enabled": true, + "id": "06be3a1f-b238-4a96-a737-ceec5714087e", + "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", + "policy_name": "dp01", + "policy_type": "senlin.policy.deletion-1.0" + } +}` + func HandleCreateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -511,3 +524,15 @@ func HandleListPoliciesSuccessfully(t *testing.T) { fmt.Fprint(w, ListPoliciesResult) }) } + +func HandleGetPolicySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies/714fe676-a08f-4196-b7af-61d52eeded15", 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, GetPolicyResponse) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index e5ef90a802..85836caaa3 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -314,42 +314,9 @@ func TestGetClusterPolicies(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies/714fe676-a08f-4196-b7af-61d52eeded15", 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, ` - { - "cluster_policy": - { - "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "cluster_name": "cluster4", - "enabled": true, - "id": "06be3a1f-b238-4a96-a737-ceec5714087e", - "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", - "policy_name": "dp01", - "policy_type": "senlin.policy.deletion-1.0" - } - }`) - }) - - expected := clusterpolicies.ClusterPolicy{ - ClusterUUID: "7d85f602-a948-4a30-afd4-e84f47471c15", - ClusterName: "cluster4", - Enabled: true, - ID: "06be3a1f-b238-4a96-a737-ceec5714087e", - PolicyID: "714fe676-a08f-4196-b7af-61d52eeded15", - PolicyName: "dp01", - PolicyType: "senlin.policy.deletion-1.0", - } + HandleGetPolicySuccessfully(t) - actual, err := clusterpolicies.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", "714fe676-a08f-4196-b7af-61d52eeded15").Extract() - if err != nil { - t.Errorf("Failed Get cluster policies. %v", err) - } else { - th.AssertDeepEquals(t, expected, *actual) - } + actual, err := clusters.GetPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", "714fe676-a08f-4196-b7af-61d52eeded15").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedClusterPolicy, *actual) } From dad8760a075c009c7c5972cf9a10a356bc462aec Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 8 Jun 2018 11:27:32 -0700 Subject: [PATCH 0418/2296] senlin-clusters-recover --- openstack/clustering/v1/clusters/doc.go | 18 +++++++++ openstack/clustering/v1/clusters/requests.go | 37 ++++++++++++++++++- openstack/clustering/v1/clusters/results.go | 20 ++++++++++ .../v1/clusters/testing/requests_test.go | 27 ++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 ++ 5 files changed, 104 insertions(+), 2 deletions(-) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 1182635796..a0724a5257 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -66,6 +66,7 @@ Example to Delete a cluster panic(err) } +<<<<<<< HEAD Example to Resize a cluster number := 1 @@ -130,5 +131,22 @@ Example to Get a Cluster Policy fmt.Printf("%+v\n", clusterPolicy) +Example to Recover a cluster + + check := true + checkCapacity := true + recoverOpts := clusters.RecoverOpts{ + Operation: "rebuild", + Check: &check, + CheckCapacity: &checkCapacity, + } + + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + actionID, err := clusters.Recover(computeClient, clusterID, recoverOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("action=", actionID) + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 8d210928c5..6b3c02f5ce 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -131,7 +131,7 @@ func (opts UpdateOpts) ToClusterUpdateMap() (map[string]interface{}, error) { return b, nil } -// Update implements profile updated request. +// Update implements cluster updated request. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToClusterUpdateMap() if err != nil { @@ -224,7 +224,6 @@ func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r r.Err = err return } - var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, @@ -236,6 +235,40 @@ func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r return } +// Cluster Recover +type RecoverOpts struct { + Operation string `json:"operation,omitempty"` + Check *bool `json:"check,omitempty"` + CheckCapacity *bool `json:"check_capacity,omitempty"` +} + +func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "recover") + if err != nil { + return nil, err + } + return b, nil +} + +// Recover implements cluster recover request. +func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r RecoverResult) { + b, err := opts.ToClusterRecoverMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(recoverURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} + // ListPoliciesOptsBuilder Builder. type ListPoliciesOptsBuilder interface { ToClusterListPoliciesQuery() (string, error) diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 8f1f964ed0..5584c2dcd0 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -23,6 +23,11 @@ type GetResult struct { commonResult } +// PostResult is the response of a Post operations. +type PostResult struct { + commonResult +} + // UpdateResult is the response of a Update operations. type UpdateResult struct { commonResult @@ -133,6 +138,21 @@ func ExtractClusters(r pagination.Page) ([]Cluster, error) { return s.Clusters, err } +// PostResult is the response of a Post operations. +type RecoverResult struct { + PostResult +} + +// Extract retrieves the response action +func (r RecoverResult) Extract() (string, error) { + var s *Action + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Action, err +} + func (r *Cluster) UnmarshalJSON(b []byte) error { type tmp Cluster var s struct { diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 85836caaa3..78eac5cd07 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -320,3 +320,30 @@ func TestGetClusterPolicies(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedClusterPolicy, *actual) } + +func TestClusterRecover(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + recoverOpts := clusters.RecoverOpts{ + Operation: "rebuild", + Check: new(bool), + CheckCapacity: new(bool), + } + actionID, err := clusters.Recover(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "2a0ff107-e789-4660-a122-3816c43af703", actionID) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index a67f05bce4..22a15dd496 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,3 +44,7 @@ func listPoliciesURL(client *gophercloud.ServiceClient, clusterID string) string func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID string) string { return client.ServiceURL(apiVersion, apiName, clusterID, "policies", policyID) } + +func recoverURL(client *gophercloud.ServiceClient, id string) string { + return actionURL(client, id) +} From 09fe5185e19e513ae8efc910fa806a4d341607d1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jun 2018 16:08:06 +0000 Subject: [PATCH 0419/2296] Clustering v1: clusters recover updates --- .../openstack/clustering/v1/clusters_test.go | 28 ++++++++++++++++++ openstack/clustering/v1/clusters/doc.go | 2 +- openstack/clustering/v1/clusters/requests.go | 26 +++++++++++------ openstack/clustering/v1/clusters/results.go | 29 ++++--------------- .../v1/clusters/testing/fixtures.go | 12 ++++++++ .../v1/clusters/testing/requests_test.go | 17 ++--------- 6 files changed, 66 insertions(+), 48 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 0354f98663..309a53f299 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -145,3 +145,31 @@ func TestClustersPolicies(t *testing.T) { tools.PrintResource(t, v) } } + +func TestClustersRecovery(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + recoverOpts := clusters.RecoverOpts{ + Operation: clusters.RebuildRecovery, + } + + actionID, err := clusters.Recover(client, cluster.ID, recoverOpts).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + newCluster, err := clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newCluster) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index a0724a5257..60be2034ea 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -136,7 +136,7 @@ Example to Recover a cluster check := true checkCapacity := true recoverOpts := clusters.RecoverOpts{ - Operation: "rebuild", + Operation: clusters.RebuildRecovery, Check: &check, CheckCapacity: &checkCapacity, } diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 6b3c02f5ce..ed596a02f1 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -16,6 +16,14 @@ const ( ChangeInPercentageAdjustment AdjustmentType = "CHANGE_IN_PERCENTAGE" ) +type RecoveryAction string + +const ( + RebootRecovery RecoveryAction = "REBOOT" + RebuildRecovery RecoveryAction = "REBUILD" + RecreateRecovery RecoveryAction = "RECREATE" +) + // CreateOptsBuilder Builder. type CreateOptsBuilder interface { ToClusterCreateMap() (map[string]interface{}, error) @@ -190,7 +198,7 @@ func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "resize") } -func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOpts) (r ResizeResult) { +func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOpts) (r ActionResult) { b, err := opts.ToClusterResizeMap() if err != nil { r.Err = err @@ -214,12 +222,12 @@ type ScaleInOpts struct { } // ToClusterScaleInMap constructs a request body from ScaleInOpts. -func (opts ScaleInOpts) ToClusterScaleInMap(scaleAction string) (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, scaleAction) +func (opts ScaleInOpts) ToClusterScaleInMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "scale_in") } -func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r ScaleInResult) { - b, err := opts.ToClusterScaleInMap("scale_in") +func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r ActionResult) { + b, err := opts.ToClusterScaleInMap() if err != nil { r.Err = err return @@ -237,9 +245,9 @@ func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r // Cluster Recover type RecoverOpts struct { - Operation string `json:"operation,omitempty"` - Check *bool `json:"check,omitempty"` - CheckCapacity *bool `json:"check_capacity,omitempty"` + Operation RecoveryAction `json:"operation,omitempty"` + Check *bool `json:"check,omitempty"` + CheckCapacity *bool `json:"check_capacity,omitempty"` } func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { @@ -251,7 +259,7 @@ func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { } // Recover implements cluster recover request. -func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r RecoverResult) { +func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r ActionResult) { b, err := opts.ToClusterRecoverMap() if err != nil { r.Err = err diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 5584c2dcd0..c6b91163ed 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -39,16 +39,6 @@ type DeleteResult struct { gophercloud.ErrResult } -// ResizeResult is the response of a Get operations. -type ResizeResult struct { - commonResult -} - -// ScaleInResult is the response of a ScaleIn operations. -type ScaleInResult struct { - commonResult -} - // ClusterPolicyPage contains a single page of all policies from a ListDetails call. type ClusterPolicyPage struct { pagination.SinglePageBase @@ -97,26 +87,17 @@ type Action struct { Action string `json:"action"` } -func (r ResizeResult) Extract() (string, error) { - var s Action - err := r.ExtractInto(&s) - if err != nil { - return s.Action, err - } - - return s.Action, nil +// ActionResult is the response of Senlin actions. +type ActionResult struct { + commonResult } -func (r ScaleInResult) Extract() (string, error) { +func (r ActionResult) Extract() (string, error) { var s struct { Action string `json:"action"` } err := r.ExtractInto(&s) - if err != nil { - return "", err - } - - return s.Action, nil + return s.Action, err } // ClusterPage contains a single page of all clusters from a List call. diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 1c135e9c2e..36d7586068 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -536,3 +536,15 @@ func HandleGetPolicySuccessfully(t *testing.T) { fmt.Fprintf(w, GetPolicyResponse) }) } + +func HandleRecoverSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ActionResult) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 78eac5cd07..8cbd8ac951 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -325,25 +325,14 @@ func TestClusterRecover(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) + HandleRecoverSuccessfully(t) recoverOpts := clusters.RecoverOpts{ - Operation: "rebuild", + Operation: clusters.RebuildRecovery, Check: new(bool), CheckCapacity: new(bool), } actionID, err := clusters.Recover(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, "2a0ff107-e789-4660-a122-3816c43af703", actionID) + th.AssertEquals(t, ExpectedActionID, actionID) } From b4aed007a62118fb07450afdf1af186897dfaeb9 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0420/2296] senlin-clusters-attachpolicy --- openstack/clustering/v1/clusters/doc.go | 15 +++++++++- openstack/clustering/v1/clusters/requests.go | 29 +++++++++++++++++++ openstack/clustering/v1/clusters/results.go | 17 +++++++++++ .../v1/clusters/testing/requests_test.go | 27 +++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 12 +++++--- 5 files changed, 95 insertions(+), 5 deletions(-) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 60be2034ea..35be1a5dd2 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -66,7 +66,6 @@ Example to Delete a cluster panic(err) } -<<<<<<< HEAD Example to Resize a cluster number := 1 @@ -148,5 +147,19 @@ Example to Recover a cluster } fmt.Println("action=", actionID) +Example to attach a policy to a cluster + + enabled := true + attachPolicyOpts := clusters.AttachPolicyOpts{ + PolicyID: "policy-123", + Enabled: &enabled, + } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + actionID, err := clusters.AttachPolicy(serviceClient, clusterID, attachPolicyOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("Attach Policy actionID", actionID) + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index ed596a02f1..15451645f8 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -243,6 +243,35 @@ func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r return } +// PolicyOpts params +type AttachPolicyOpts struct { + PolicyID string `json:"policy_id" required:"true"` + Enabled *bool `json:"enabled,omitempty"` +} + +// ToClusterPolicyMap constructs a request body from PolicyOpts +func (opts AttachPolicyOpts) ToClusterAttachPolicyMap(policyAction string) (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, policyAction) +} + +// Attach Policy +func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolicyOpts) (r AttachPolicyResult) { + b, err := opts.ToClusterAttachPolicyMap("policy_attach") + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(policyURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} + // Cluster Recover type RecoverOpts struct { Operation RecoveryAction `json:"operation,omitempty"` diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index c6b91163ed..27fcea777f 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -49,6 +49,11 @@ type GetPolicyResult struct { commonResult } +// AttachPolicy is the response of a Create operations. +type AttachPolicyResult struct { + commonResult +} + type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` @@ -100,6 +105,18 @@ func (r ActionResult) Extract() (string, error) { return s.Action, err } +func (r AttachPolicyResult) Extract() (string, error) { + var s struct { + Action string `json:"action"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + + return s.Action, nil +} + // ClusterPage contains a single page of all clusters from a List call. type ClusterPage struct { pagination.LinkedPageBase diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 8cbd8ac951..bf90505cc3 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -336,3 +336,30 @@ func TestClusterRecover(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } + +func TestAttachPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + enabled := true + opts := clusters.AttachPolicyOpts{ + PolicyID: "policy1", + Enabled: &enabled, + } + actionID, err := clusters.AttachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 22a15dd496..2a76878ff7 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -5,10 +5,6 @@ import "github.com/gophercloud/gophercloud" var apiVersion = "v1" var apiName = "clusters" -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "actions") -} - func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } @@ -46,5 +42,13 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID } func recoverURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "actions") +} + +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "actions") +} + +func policyURL(client *gophercloud.ServiceClient, id string) string { return actionURL(client, id) } From b0d9044199fe1912a3d823ce1e478920c095e7ac Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jun 2018 19:08:47 +0000 Subject: [PATCH 0421/2296] Clustering v1: clusters attach policy updates --- .../openstack/clustering/v1/clusters_test.go | 23 +++++++++++++++++++ openstack/clustering/v1/clusters/requests.go | 12 +++++----- openstack/clustering/v1/clusters/results.go | 17 -------------- .../v1/clusters/testing/fixtures.go | 12 ++++++++++ .../v1/clusters/testing/requests_test.go | 15 ++---------- openstack/clustering/v1/clusters/urls.go | 8 ------- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 309a53f299..ab2085694f 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -126,6 +126,7 @@ func TestClustersScale(t *testing.T) { func TestClustersPolicies(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) + client.Microversion = "1.5" profile, err := CreateProfile(t, client) th.AssertNoErr(t, err) @@ -135,15 +136,37 @@ func TestClustersPolicies(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) + policy, err := CreatePolicy(t, client) + th.AssertNoErr(t, err) + defer DeletePolicy(t, client, policy.ID) + + iTrue := true + attachPolicyOpts := clusters.AttachPolicyOpts{ + PolicyID: policy.ID, + Enabled: &iTrue, + } + + actionID, err := clusters.AttachPolicy(client, cluster.ID, attachPolicyOpts).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + allPages, err := clusters.ListPolicies(client, cluster.ID, nil).AllPages() th.AssertNoErr(t, err) allPolicies, err := clusters.ExtractClusterPolicies(allPages) th.AssertNoErr(t, err) + var found bool for _, v := range allPolicies { tools.PrintResource(t, v) + if v.PolicyID == policy.ID { + found = true + } } + + th.AssertEquals(t, found, true) } func TestClustersRecovery(t *testing.T) { diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 15451645f8..25f4d50f27 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -250,19 +250,19 @@ type AttachPolicyOpts struct { } // ToClusterPolicyMap constructs a request body from PolicyOpts -func (opts AttachPolicyOpts) ToClusterAttachPolicyMap(policyAction string) (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, policyAction) +func (opts AttachPolicyOpts) ToClusterAttachPolicyMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "policy_attach") } // Attach Policy -func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolicyOpts) (r AttachPolicyResult) { - b, err := opts.ToClusterAttachPolicyMap("policy_attach") +func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolicyOpts) (r ActionResult) { + b, err := opts.ToClusterAttachPolicyMap() if err != nil { r.Err = err return } var result *http.Response - result, r.Err = client.Post(policyURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { @@ -296,7 +296,7 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r } var result *http.Response - result, r.Err = client.Post(recoverURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 27fcea777f..c6b91163ed 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -49,11 +49,6 @@ type GetPolicyResult struct { commonResult } -// AttachPolicy is the response of a Create operations. -type AttachPolicyResult struct { - commonResult -} - type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` @@ -105,18 +100,6 @@ func (r ActionResult) Extract() (string, error) { return s.Action, err } -func (r AttachPolicyResult) Extract() (string, error) { - var s struct { - Action string `json:"action"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - - return s.Action, nil -} - // ClusterPage contains a single page of all clusters from a List call. type ClusterPage struct { pagination.LinkedPageBase diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 36d7586068..2bec25372a 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -548,3 +548,15 @@ func HandleRecoverSuccessfully(t *testing.T) { fmt.Fprint(w, ActionResult) }) } + +func HandleAttachPolicySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ActionResult) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index bf90505cc3..bc248aa962 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -341,18 +341,7 @@ func TestAttachPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) + HandleAttachPolicySuccessfully(t) enabled := true opts := clusters.AttachPolicyOpts{ @@ -361,5 +350,5 @@ func TestAttachPolicy(t *testing.T) { } actionID, err := clusters.AttachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") + th.AssertEquals(t, ExpectedActionID, actionID) } diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 2a76878ff7..337eb66747 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -41,14 +41,6 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID return client.ServiceURL(apiVersion, apiName, clusterID, "policies", policyID) } -func recoverURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "actions") -} - func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "actions") } - -func policyURL(client *gophercloud.ServiceClient, id string) string { - return actionURL(client, id) -} From 58d4bd3bb814a162ff64be28067b41bbdff8e71b Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0422/2296] senlin-clusters-detachpolicy --- openstack/clustering/v1/clusters/doc.go | 12 +++++++++ openstack/clustering/v1/clusters/requests.go | 27 +++++++++++++++++++ openstack/clustering/v1/clusters/results.go | 14 ++++++++++ .../v1/clusters/testing/requests_test.go | 25 +++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 +++ 5 files changed, 82 insertions(+) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 35be1a5dd2..45210d13ba 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -161,5 +161,17 @@ Example to attach a policy to a cluster } fmt.Println("Attach Policy actionID", actionID) +Example to detach a policy to cluster + + detachpolicyOpts := clusters.DetachPolicyOpts{ + PolicyID: "policy-123", + } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + actionID, err := clusters.DetachPolicy(serviceClient, clusterID, detachpolicyOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("DetachPolicy actionID", actionID) + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 25f4d50f27..ae8b3f037e 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -306,6 +306,33 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r return } +// DetachPolicyOpts params +type DetachPolicyOpts struct { + PolicyID string `json:"policy_id" required:"true"` +} + +func (opts DetachPolicyOpts) ToClusterDetachPolicyMap(policyAction string) (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, policyAction) +} + +func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolicyOpts) (r DetachPolicyResult) { + b, err := opts.ToClusterDetachPolicyMap("policy_detach") + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(policyURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} + // ListPoliciesOptsBuilder Builder. type ListPoliciesOptsBuilder interface { ToClusterListPoliciesQuery() (string, error) diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index c6b91163ed..558f2c7412 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -207,3 +207,17 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } + +type DetachPolicyResult struct { + PostResult +} + +// Extract retrieves the response action +func (r DetachPolicyResult) Extract() (string, error) { + var s *Action + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Action, err +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index bc248aa962..02a83bf777 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -352,3 +352,28 @@ func TestAttachPolicy(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } + +func TestAttachPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + opts := clusters.DetachPolicyOpts{ + PolicyID: "policy1", + } + result, err := clusters.DetachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 337eb66747..e998e21a22 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,3 +44,7 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "actions") } + +func policyURL(client *gophercloud.ServiceClient, id string) string { + return actionURL(client, id) +} From 2469fafc3068dc4dfd9b371d9e1d10974ce7ec50 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jun 2018 19:21:02 +0000 Subject: [PATCH 0423/2296] Clustering v1: clusters detach policy updates --- .../openstack/clustering/v1/clusters_test.go | 30 +++++++++++++++++++ openstack/clustering/v1/clusters/requests.go | 10 +++---- openstack/clustering/v1/clusters/results.go | 29 ------------------ .../v1/clusters/testing/fixtures.go | 12 ++++++++ .../v1/clusters/testing/requests_test.go | 19 +++--------- openstack/clustering/v1/clusters/urls.go | 4 --- 6 files changed, 51 insertions(+), 53 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index ab2085694f..eaa8737c91 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -152,6 +152,8 @@ func TestClustersPolicies(t *testing.T) { err = WaitForAction(client, actionID) th.AssertNoErr(t, err) + // List all policies in the cluster to see if the policy was + // successfully attached. allPages, err := clusters.ListPolicies(client, cluster.ID, nil).AllPages() th.AssertNoErr(t, err) @@ -167,6 +169,34 @@ func TestClustersPolicies(t *testing.T) { } th.AssertEquals(t, found, true) + + detachPolicyOpts := clusters.DetachPolicyOpts{ + PolicyID: policy.ID, + } + + actionID, err = clusters.DetachPolicy(client, cluster.ID, detachPolicyOpts).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + // List all policies in the cluster to see if the policy was + // successfully detached. + allPages, err = clusters.ListPolicies(client, cluster.ID, nil).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err = clusters.ExtractClusterPolicies(allPages) + th.AssertNoErr(t, err) + + found = false + for _, v := range allPolicies { + tools.PrintResource(t, v) + if v.PolicyID == policy.ID { + found = true + } + } + + th.AssertEquals(t, found, false) } func TestClustersRecovery(t *testing.T) { diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index ae8b3f037e..432eabcee2 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -311,19 +311,19 @@ type DetachPolicyOpts struct { PolicyID string `json:"policy_id" required:"true"` } -func (opts DetachPolicyOpts) ToClusterDetachPolicyMap(policyAction string) (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, policyAction) +func (opts DetachPolicyOpts) ToClusterDetachPolicyMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "policy_detach") } -func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolicyOpts) (r DetachPolicyResult) { - b, err := opts.ToClusterDetachPolicyMap("policy_detach") +func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolicyOpts) (r ActionResult) { + b, err := opts.ToClusterDetachPolicyMap() if err != nil { r.Err = err return } var result *http.Response - result, r.Err = client.Post(policyURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 558f2c7412..55d90a0585 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -119,21 +119,6 @@ func ExtractClusters(r pagination.Page) ([]Cluster, error) { return s.Clusters, err } -// PostResult is the response of a Post operations. -type RecoverResult struct { - PostResult -} - -// Extract retrieves the response action -func (r RecoverResult) Extract() (string, error) { - var s *Action - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return s.Action, err -} - func (r *Cluster) UnmarshalJSON(b []byte) error { type tmp Cluster var s struct { @@ -207,17 +192,3 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } - -type DetachPolicyResult struct { - PostResult -} - -// Extract retrieves the response action -func (r DetachPolicyResult) Extract() (string, error) { - var s *Action - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return s.Action, err -} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 2bec25372a..68fee7113c 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -560,3 +560,15 @@ func HandleAttachPolicySuccessfully(t *testing.T) { fmt.Fprint(w, ActionResult) }) } + +func HandleDetachPolicySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ActionResult) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 02a83bf777..07d741b795 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -353,27 +353,16 @@ func TestAttachPolicy(t *testing.T) { th.AssertEquals(t, ExpectedActionID, actionID) } -func TestAttachPolicy(t *testing.T) { +func TestDetachPolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) + HandleDetachPolicySuccessfully(t) opts := clusters.DetachPolicyOpts{ PolicyID: "policy1", } - result, err := clusters.DetachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + actionID, err := clusters.DetachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") + th.AssertEquals(t, ExpectedActionID, actionID) } diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index e998e21a22..337eb66747 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,7 +44,3 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "actions") } - -func policyURL(client *gophercloud.ServiceClient, id string) string { - return actionURL(client, id) -} From 4fdf55678efd6f4875b282b82bb523289b62f4ce Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0424/2296] senlin-clusters-updatepolicy --- openstack/clustering/v1/clusters/doc.go | 19 +++++++++++++ openstack/clustering/v1/clusters/requests.go | 26 ++++++++++++++++++ openstack/clustering/v1/clusters/results.go | 13 +++++++++ .../v1/clusters/testing/requests_test.go | 27 +++++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 +++ 5 files changed, 89 insertions(+) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 45210d13ba..0c0e18c013 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -161,6 +161,20 @@ Example to attach a policy to a cluster } fmt.Println("Attach Policy actionID", actionID) +Example to update a policy to a cluster + + enabled := true + updatePolicyOpts := clusters.UpdatePolicyOpts{ + PolicyID: "policy-123", + Enabled: &enabled, + } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + actionID, err := clusters.AttachPolicy(serviceClient, clusterID, attachPolicyOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("Attach Policy actionID", actionID) + Example to detach a policy to cluster detachpolicyOpts := clusters.DetachPolicyOpts{ @@ -172,6 +186,11 @@ Example to detach a policy to cluster panic(err) } fmt.Println("DetachPolicy actionID", actionID) + actionID, err := clusters.UpdatePolicy(serviceClient, clusterID, updatePolicyOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("Update Policy actionID", actionID) */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 432eabcee2..fe61ec1c1c 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -272,6 +272,32 @@ func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolic return } +type UpdatePolicyOpts struct { + PolicyID string `json:"policy_id" required:"true"` + Enabled *bool `json:"enabled,omitempty" required:"true"` +} + +func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap(policyAction string) (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, policyAction) +} + +func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolicyOpts) (r UpdatePolicyResult) { + b, err := opts.ToClusterUpdatePolicyMap("policy_update") + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(policyURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} + // Cluster Recover type RecoverOpts struct { Operation RecoveryAction `json:"operation,omitempty"` diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 55d90a0585..3174b1019a 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -192,3 +192,16 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } + +type UpdatePolicyResult struct { + PostResult +} + +func (r UpdatePolicyResult) Extract() (string, error) { + var s *Action + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Action, err +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 07d741b795..dba4d78dca 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -366,3 +366,30 @@ func TestDetachPolicy(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } + +func TestUpdatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + enabled := true + opts := clusters.UpdatePolicyOpts{ + PolicyID: "policy1", + Enabled: &enabled, + } + actionID, err := clusters.UpdatePolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 337eb66747..e998e21a22 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,3 +44,7 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "actions") } + +func policyURL(client *gophercloud.ServiceClient, id string) string { + return actionURL(client, id) +} From 16302040c48f6d6c296187dc0e3be4910a7971b2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 01:52:45 +0000 Subject: [PATCH 0425/2296] Clustering v1: clusters update policy updates --- .../openstack/clustering/v1/clusters_test.go | 18 ++++++++++++++++++ openstack/clustering/v1/clusters/requests.go | 10 +++++----- openstack/clustering/v1/clusters/results.go | 13 ------------- .../clustering/v1/clusters/testing/fixtures.go | 12 ++++++++++++ .../v1/clusters/testing/requests_test.go | 15 ++------------- openstack/clustering/v1/clusters/urls.go | 4 ---- 6 files changed, 37 insertions(+), 35 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index eaa8737c91..65b3151eb8 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -170,6 +170,24 @@ func TestClustersPolicies(t *testing.T) { th.AssertEquals(t, found, true) + // Set the policy to disabled + iFalse := false + updatePolicyOpts := clusters.UpdatePolicyOpts{ + PolicyID: policy.ID, + Enabled: &iFalse, + } + + actionID, err = clusters.UpdatePolicy(client, cluster.ID, updatePolicyOpts).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + clusterPolicy, err := clusters.GetPolicy(client, cluster.ID, policy.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, clusterPolicy.Enabled, false) + + // Detach the policy detachPolicyOpts := clusters.DetachPolicyOpts{ PolicyID: policy.ID, } diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index fe61ec1c1c..1d7a2cfc10 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -277,18 +277,18 @@ type UpdatePolicyOpts struct { Enabled *bool `json:"enabled,omitempty" required:"true"` } -func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap(policyAction string) (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, policyAction) +func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "policy_update") } -func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolicyOpts) (r UpdatePolicyResult) { - b, err := opts.ToClusterUpdatePolicyMap("policy_update") +func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolicyOpts) (r ActionResult) { + b, err := opts.ToClusterUpdatePolicyMap() if err != nil { r.Err = err return } var result *http.Response - result, r.Err = client.Post(policyURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 3174b1019a..55d90a0585 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -192,16 +192,3 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } - -type UpdatePolicyResult struct { - PostResult -} - -func (r UpdatePolicyResult) Extract() (string, error) { - var s *Action - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return s.Action, err -} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 68fee7113c..e6dd24ccc9 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -572,3 +572,15 @@ func HandleDetachPolicySuccessfully(t *testing.T) { fmt.Fprint(w, ActionResult) }) } + +func HandleUpdatePolicySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ActionResult) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index dba4d78dca..e11a1200cb 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -371,18 +371,7 @@ func TestUpdatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) + HandleUpdatePolicySuccessfully(t) enabled := true opts := clusters.UpdatePolicyOpts{ @@ -391,5 +380,5 @@ func TestUpdatePolicy(t *testing.T) { } actionID, err := clusters.UpdatePolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") + th.AssertEquals(t, ExpectedActionID, actionID) } diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index e998e21a22..337eb66747 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,7 +44,3 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "actions") } - -func policyURL(client *gophercloud.ServiceClient, id string) string { - return actionURL(client, id) -} From 937eb70bfabdd35556627f09d3a488eabfc4a8bf Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0426/2296] senlin-clusters-scaleout --- openstack/clustering/v1/clusters/doc.go | 12 +++++++++ openstack/clustering/v1/clusters/requests.go | 25 +++++++++++++++++++ openstack/clustering/v1/clusters/results.go | 13 ++++++++++ .../v1/clusters/testing/requests_test.go | 25 +++++++++++++++++++ openstack/clustering/v1/clusters/urls.go | 4 +++ 5 files changed, 79 insertions(+) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 0c0e18c013..db9291bde5 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -192,5 +192,17 @@ Example to detach a policy to cluster } fmt.Println("Update Policy actionID", actionID) +Example to ScaleOut a cluster + + scaleOutOpts := clusters.ScaleOutOpts{ + Count: 2, + } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + + actionID, err := clusters.ScaleOut(computeClient, clusterID, scaleOutOpts).Extract() + if err != nil { + panic(err) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 1d7a2cfc10..640aa4f32e 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -298,6 +298,31 @@ func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolic return } +type ScaleOutOpts struct { + Count int `json:"count,omitempty"` +} + +func (opts ScaleOutOpts) ToClusterScaleOutMap(scaleOutAction string) (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, scaleOutAction) +} + +func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOpts) (r ScaleOutResult) { + b, err := opts.ToClusterScaleOutMap("scale_out") + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(scaleURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} + // Cluster Recover type RecoverOpts struct { Operation RecoveryAction `json:"operation,omitempty"` diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 55d90a0585..00ce9c27b2 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -192,3 +192,16 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } + +type ScaleOutResult struct { + PostResult +} + +func (r ScaleOutResult) Extract() (string, error) { + var s *Action + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Action, err +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index e11a1200cb..f0dd32c448 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -382,3 +382,28 @@ func TestUpdatePolicy(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } + +func TestClusterScaleOut(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + scaleOutOpts := clusters.ScaleOutOpts{ + Count: 5, + } + result, err := clusters.ScaleOut(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOutOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 337eb66747..414381fe69 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,3 +44,7 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "actions") } + +func scaleURL(client *gophercloud.ServiceClient, id string) string { + return actionURL(client, id) +} From 4be309b7060912e0e33399577c52095ad0dd5715 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 02:13:58 +0000 Subject: [PATCH 0427/2296] Clustering v1: clusters scale out updates --- .../openstack/clustering/v1/clusters_test.go | 20 +++++++++++-- openstack/clustering/v1/clusters/requests.go | 10 +++---- openstack/clustering/v1/clusters/results.go | 13 --------- .../v1/clusters/testing/fixtures.go | 28 +++++++++++++------ .../v1/clusters/testing/requests_test.go | 17 ++--------- openstack/clustering/v1/clusters/urls.go | 4 --- 6 files changed, 45 insertions(+), 47 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 65b3151eb8..e038283eb0 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -104,19 +104,33 @@ func TestClustersScale(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) + // increase cluster size to 2 + scaleOutOpts := clusters.ScaleOutOpts{ + Count: 1, + } + actionID, err := clusters.ScaleOut(client, cluster.ID, scaleOutOpts).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + newCluster, err := clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newCluster.DesiredCapacity, 2) + // reduce cluster size to 0 - count := 1 + count := 2 scaleInOpts := clusters.ScaleInOpts{ Count: &count, } - actionID, err := clusters.ScaleIn(client, cluster.ID, scaleInOpts).Extract() + actionID, err = clusters.ScaleIn(client, cluster.ID, scaleInOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - newCluster, err := clusters.Get(client, cluster.ID).Extract() + newCluster, err = clusters.Get(client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.DesiredCapacity, 0) diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 640aa4f32e..9766e268e6 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -302,18 +302,18 @@ type ScaleOutOpts struct { Count int `json:"count,omitempty"` } -func (opts ScaleOutOpts) ToClusterScaleOutMap(scaleOutAction string) (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, scaleOutAction) +func (opts ScaleOutOpts) ToClusterScaleOutMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "scale_out") } -func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOpts) (r ScaleOutResult) { - b, err := opts.ToClusterScaleOutMap("scale_out") +func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOpts) (r ActionResult) { + b, err := opts.ToClusterScaleOutMap() if err != nil { r.Err = err return } var result *http.Response - result, r.Err = client.Post(scaleURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 00ce9c27b2..55d90a0585 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -192,16 +192,3 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } - -type ScaleOutResult struct { - PostResult -} - -func (r ScaleOutResult) Extract() (string, error) { - var s *Action - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return s.Action, err -} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index e6dd24ccc9..42e2e7686a 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -391,7 +391,7 @@ func HandleCreateClusterSuccessfully(t *testing.T) { w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ClusterResponse) + fmt.Fprint(w, ClusterResponse) }) } @@ -403,7 +403,7 @@ func HandleCreateClusterEmptyTimeSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ClusterResponse_EmptyTime) + fmt.Fprint(w, ClusterResponse_EmptyTime) }) } @@ -415,7 +415,7 @@ func HandleCreateClusterMetadataSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ClusterResponse_Metadata) + fmt.Fprint(w, ClusterResponse_Metadata) }) } @@ -427,7 +427,7 @@ func HandleGetClusterSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ClusterResponse) + fmt.Fprint(w, ClusterResponse) }) } @@ -439,7 +439,7 @@ func HandleGetClusterEmptyTimeSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ClusterResponse_EmptyTime) + fmt.Fprint(w, ClusterResponse_EmptyTime) }) } @@ -451,7 +451,7 @@ func HandleListClusterSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) } @@ -513,6 +513,18 @@ func HandleScaleInSuccessfully(t *testing.T) { }) } +func HandleScaleOutSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ActionResult) + }) +} + func HandleListPoliciesSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -533,7 +545,7 @@ func HandleGetPolicySuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetPolicyResponse) + fmt.Fprint(w, GetPolicyResponse) }) } @@ -581,6 +593,6 @@ func HandleUpdatePolicySuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ActionResult) + fmt.Fprint(w, ActionResult) }) } diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index f0dd32c448..f70c3b47e4 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -387,23 +387,12 @@ func TestClusterScaleOut(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) + HandleScaleOutSuccessfully(t) scaleOutOpts := clusters.ScaleOutOpts{ Count: 5, } - result, err := clusters.ScaleOut(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOutOpts).Extract() + actionID, err := clusters.ScaleOut(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOutOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") + th.AssertEquals(t, ExpectedActionID, actionID) } diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 414381fe69..337eb66747 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,7 +44,3 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID func actionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id, "actions") } - -func scaleURL(client *gophercloud.ServiceClient, id string) string { - return actionURL(client, id) -} From f779379d3f39a38426171ab3476f4b635a4835d6 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 8 Jun 2018 11:27:32 -0700 Subject: [PATCH 0428/2296] senlin-clusters-check --- openstack/clustering/v1/clusters/doc.go | 8 +++++ openstack/clustering/v1/clusters/requests.go | 31 ++++++++++++++++++- openstack/clustering/v1/clusters/results.go | 14 +++++++++ .../v1/clusters/testing/requests_test.go | 23 ++++++++++++++ openstack/clustering/v1/clusters/urls.go | 8 +++-- 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index db9291bde5..72a8afa2c1 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -204,5 +204,13 @@ Example to ScaleOut a cluster panic(err) } +Example to Check a cluster + + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + action, err := clusters.Check(computeClient, clusterID, clusters.CheckOpts{}).Extract() + if err != nil { + panic(err) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 9766e268e6..5e61d62746 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -338,6 +338,18 @@ func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { return b, nil } +// Cluster Check +type CheckOpts struct { +} + +func (opts CheckOpts) ToClusterCheckMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "check") + if err != nil { + return nil, err + } + return b, nil +} + // Recover implements cluster recover request. func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r ActionResult) { b, err := opts.ToClusterRecoverMap() @@ -345,7 +357,6 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r r.Err = err return } - var result *http.Response result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, @@ -357,6 +368,24 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r return } +func Check(client *gophercloud.ServiceClient, id string, opts CheckOpts) (r CheckResult) { + b, err := opts.ToClusterCheckMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(checkURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} + // DetachPolicyOpts params type DetachPolicyOpts struct { PolicyID string `json:"policy_id" required:"true"` diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 55d90a0585..96e62db859 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -192,3 +192,17 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } + +type CheckResult struct { + PostResult +} + +// Extract retrieves the response action +func (r CheckResult) Extract() (string, error) { + var s *Action + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Action, err +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index f70c3b47e4..940231b7d2 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -396,3 +396,26 @@ func TestClusterScaleOut(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } + +func TestClusterCheck(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "action": "2a0ff107-e789-4660-a122-3816c43af703" + }`) + }) + + recoverOpts := clusters.CheckOpts{} + result, err := clusters.Check(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 337eb66747..44d033b642 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -13,6 +13,10 @@ func idURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiVersion, apiName, id) } +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "actions") +} + func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } @@ -41,6 +45,6 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID return client.ServiceURL(apiVersion, apiName, clusterID, "policies", policyID) } -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "actions") +func checkURL(client *gophercloud.ServiceClient, id string) string { + return actionURL(client, id) } From 44787e7e780604dc568c1d7e2e98789bdc8a403c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 02:29:39 +0000 Subject: [PATCH 0429/2296] Clustering v1: clusters check updates --- .../openstack/clustering/v1/clusters_test.go | 7 +++++ openstack/clustering/v1/clusters/requests.go | 28 ++++--------------- openstack/clustering/v1/clusters/results.go | 14 ---------- .../v1/clusters/testing/fixtures.go | 28 +++++++++++++------ .../v1/clusters/testing/requests_test.go | 18 ++---------- openstack/clustering/v1/clusters/urls.go | 4 --- 6 files changed, 35 insertions(+), 64 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index e038283eb0..ba759b9bd6 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -58,6 +58,13 @@ func TestClustersCRUD(t *testing.T) { th.AssertEquals(t, newCluster.Name, cluster.Name+"-UPDATED") tools.PrintResource(t, newCluster) + + // Test cluster health + actionID, err = clusters.Check(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) } func TestClustersResize(t *testing.T) { diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 5e61d62746..f549b567fe 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -331,23 +331,7 @@ type RecoverOpts struct { } func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "recover") - if err != nil { - return nil, err - } - return b, nil -} - -// Cluster Check -type CheckOpts struct { -} - -func (opts CheckOpts) ToClusterCheckMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "check") - if err != nil { - return nil, err - } - return b, nil + return gophercloud.BuildRequestBody(opts, "recover") } // Recover implements cluster recover request. @@ -368,15 +352,13 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r return } -func Check(client *gophercloud.ServiceClient, id string, opts CheckOpts) (r CheckResult) { - b, err := opts.ToClusterCheckMap() - if err != nil { - r.Err = err - return +func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { + b := map[string]interface{}{ + "check": map[string]interface{}{}, } var result *http.Response - result, r.Err = client.Post(checkURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) if r.Err == nil { diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 96e62db859..55d90a0585 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -192,17 +192,3 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } - -type CheckResult struct { - PostResult -} - -// Extract retrieves the response action -func (r CheckResult) Extract() (string, error) { - var s *Action - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return s.Action, err -} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 42e2e7686a..f5361d276d 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -335,7 +335,7 @@ const UpdateResponse_EmptyTime = ` } }` -const ActionResult = ` +const ActionResponse = ` { "action": "2a0ff107-e789-4660-a122-3816c43af703" }` @@ -497,7 +497,7 @@ func HandleResizeSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ActionResult) + fmt.Fprint(w, ActionResponse) }) } @@ -509,7 +509,7 @@ func HandleScaleInSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ActionResult) + fmt.Fprint(w, ActionResponse) }) } @@ -521,7 +521,7 @@ func HandleScaleOutSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ActionResult) + fmt.Fprint(w, ActionResponse) }) } @@ -557,7 +557,7 @@ func HandleRecoverSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ActionResult) + fmt.Fprint(w, ActionResponse) }) } @@ -569,7 +569,7 @@ func HandleAttachPolicySuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ActionResult) + fmt.Fprint(w, ActionResponse) }) } @@ -581,7 +581,7 @@ func HandleDetachPolicySuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ActionResult) + fmt.Fprint(w, ActionResponse) }) } @@ -593,6 +593,18 @@ func HandleUpdatePolicySuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ActionResult) + fmt.Fprint(w, ActionResponse) + }) +} + +func HandleCheckSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ActionResponse) }) } diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 940231b7d2..88abda5626 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -401,21 +401,9 @@ func TestClusterCheck(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + HandleCheckSuccessfully(t) - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "2a0ff107-e789-4660-a122-3816c43af703" - }`) - }) - - recoverOpts := clusters.CheckOpts{} - result, err := clusters.Check(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() + actionID, err := clusters.Check(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") + th.AssertEquals(t, ExpectedActionID, actionID) } diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 44d033b642..a4e0db0c1f 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,7 +44,3 @@ func listPoliciesURL(client *gophercloud.ServiceClient, clusterID string) string func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID string) string { return client.ServiceURL(apiVersion, apiName, clusterID, "policies", policyID) } - -func checkURL(client *gophercloud.ServiceClient, id string) string { - return actionURL(client, id) -} From 0835d74f22e2220b956ba79f3a78d714f068b482 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 03:01:20 +0000 Subject: [PATCH 0430/2296] Clustering v1: clusters updates --- openstack/clustering/v1/clusters/doc.go | 98 ++++---- openstack/clustering/v1/clusters/requests.go | 233 ++++++++++++------- openstack/clustering/v1/clusters/results.go | 201 ++++++++-------- 3 files changed, 300 insertions(+), 232 deletions(-) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 72a8afa2c1..90f384c0b5 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -2,7 +2,7 @@ Package clusters provides information and interaction with the clusters through the OpenStack Clustering service. -Example to Create a cluster +Example to Create a Cluster createOpts := clusters.CreateOpts{ Name: "test-cluster", @@ -15,7 +15,7 @@ Example to Create a cluster panic(err) } -Example to Get Clusters +Example to Get a Cluster clusterName := "cluster123" cluster, err := clusters.Get(serviceClient, clusterName).Extract() @@ -44,7 +44,7 @@ Example to List Clusters fmt.Printf("%+v\n", cluster) } -Example to Update a cluster +Example to Update a Cluster updateOpts := clusters.UpdateOpts{ Name: "testcluster", @@ -58,7 +58,7 @@ Example to Update a cluster } fmt.Printf("%+v\n", cluster) -Example to Delete a cluster +Example to Delete a Cluster clusterID := "dc6d336e3fc4c0a951b5698cd1236ee" err := clusters.Delete(serviceClient, clusterID).ExtractErr() @@ -66,7 +66,7 @@ Example to Delete a cluster panic(err) } -Example to Resize a cluster +Example to Resize a Cluster number := 1 maxSize := 5 @@ -89,7 +89,7 @@ Example to Resize a cluster } fmt.Println("Resize actionID", actionID) -Example to ScaleIn a cluster +Example to ScaleIn a Cluster count := 2 scaleInOpts := clusters.ScaleInOpts{ @@ -102,7 +102,19 @@ Example to ScaleIn a cluster panic(err) } -Example to list Policies for a Cluster +Example to ScaleOut a cluster + + scaleOutOpts := clusters.ScaleOutOpts{ + Count: 2, + } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + + actionID, err := clusters.ScaleOut(computeClient, clusterID, scaleOutOpts).Extract() + if err != nil { + panic(err) + } + +Example to List Policies for a Cluster clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" allPages, err := clusters.ListPolicies(serviceClient, clusterID, nil).AllPages() @@ -130,87 +142,75 @@ Example to Get a Cluster Policy fmt.Printf("%+v\n", clusterPolicy) -Example to Recover a cluster - - check := true - checkCapacity := true - recoverOpts := clusters.RecoverOpts{ - Operation: clusters.RebuildRecovery, - Check: &check, - CheckCapacity: &checkCapacity, - } - - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.Recover(computeClient, clusterID, recoverOpts).Extract() - if err != nil { - panic(err) - } - fmt.Println("action=", actionID) - -Example to attach a policy to a cluster +Example to Attach a Policy to a Cluster enabled := true attachPolicyOpts := clusters.AttachPolicyOpts{ PolicyID: "policy-123", Enabled: &enabled, } - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.AttachPolicy(serviceClient, clusterID, attachPolicyOpts).Extract() - if err != nil { - panic(err) - } - fmt.Println("Attach Policy actionID", actionID) -Example to update a policy to a cluster - - enabled := true - updatePolicyOpts := clusters.UpdatePolicyOpts{ - PolicyID: "policy-123", - Enabled: &enabled, - } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.AttachPolicy(serviceClient, clusterID, attachPolicyOpts).Extract() if err != nil { panic(err) } + fmt.Println("Attach Policy actionID", actionID) -Example to detach a policy to cluster +Example to Detach a Policy to Cluster detachpolicyOpts := clusters.DetachPolicyOpts{ PolicyID: "policy-123", } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.DetachPolicy(serviceClient, clusterID, detachpolicyOpts).Extract() if err != nil { panic(err) } - fmt.Println("DetachPolicy actionID", actionID) + + fmt.Println("Update Policy actionID", actionID) + +Example to Update a Policy to a Cluster + + enabled := true + updatePolicyOpts := clusters.UpdatePolicyOpts{ + PolicyID: "policy-123", + Enabled: &enabled, + } + + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" actionID, err := clusters.UpdatePolicy(serviceClient, clusterID, updatePolicyOpts).Extract() if err != nil { panic(err) } - fmt.Println("Update Policy actionID", actionID) -Example to ScaleOut a cluster + fmt.Println("Attach Policy actionID", actionID) - scaleOutOpts := clusters.ScaleOutOpts{ - Count: 2, +Example to Recover a Cluster + + check := true + checkCapacity := true + recoverOpts := clusters.RecoverOpts{ + Operation: clusters.RebuildRecovery, + Check: &check, + CheckCapacity: &checkCapacity, } - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.ScaleOut(computeClient, clusterID, scaleOutOpts).Extract() + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + actionID, err := clusters.Recover(computeClient, clusterID, recoverOpts).Extract() if err != nil { panic(err) } + fmt.Println("action=", actionID) -Example to Check a cluster +Example to Check a Cluster clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - action, err := clusters.Check(computeClient, clusterID, clusters.CheckOpts{}).Extract() + action, err := clusters.Check(computeClient, clusterID).Extract() if err != nil { panic(err) } - */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index f549b567fe..fccf5c621d 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// AdjustmentType represents valid values for resizing a cluster. type AdjustmentType string const ( @@ -16,6 +17,7 @@ const ( ChangeInPercentageAdjustment AdjustmentType = "CHANGE_IN_PERCENTAGE" ) +// RecoveryAction represents valid values for recovering a cluster. type RecoveryAction string const ( @@ -24,12 +26,13 @@ const ( RecreateRecovery RecoveryAction = "RECREATE" ) -// CreateOptsBuilder Builder. +// CreateOptsBuilder allows extensions to add additional parameters +// to the Create request. type CreateOptsBuilder interface { ToClusterCreateMap() (map[string]interface{}, error) } -// CreateOpts params +// CreateOpts represents options used to create a cluster. type CreateOpts struct { Name string `json:"name" required:"true"` DesiredCapacity int `json:"desired_capacity"` @@ -65,23 +68,27 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Get retrieves details of a single cluster. Use Extract to convert its -// result into a Cluster. +// Get retrieves details of a single cluster. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + if r.Err == nil { r.Header = result.Header } + return } -// ListOptsBuilder Builder. +// ListOptsBuilder allows extensions to add additional parameters to +// the List request. type ListOptsBuilder interface { ToClusterListQuery() (string, error) } -// ListOpts params +// ListOpts represents options to list clusters. type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` @@ -113,7 +120,13 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// UpdateOpts implements UpdateOpts +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToClusterUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options to update a cluster. type UpdateOpts struct { Config string `json:"config,omitempty"` Name string `json:"name,omitempty"` @@ -123,12 +136,6 @@ type UpdateOpts struct { ProfileOnly *bool `json:"profile_only,omitempty"` } -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToClusterUpdateMap() (map[string]interface{}, error) -} - // ToClusterUpdateMap assembles a request body based on the contents of // UpdateOpts. func (opts UpdateOpts) ToClusterUpdateMap() (map[string]interface{}, error) { @@ -139,7 +146,7 @@ func (opts UpdateOpts) ToClusterUpdateMap() (map[string]interface{}, error) { return b, nil } -// Update implements cluster updated request. +// Update will update an existing cluster. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToClusterUpdateMap() if err != nil { @@ -168,7 +175,13 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { return } -// ResizeOpts params +// ResizeOptsBuilder allows extensions to add additional parameters to the +// resize request. +type ResizeOptsBuilder interface { + ToClusterResizeMap() (map[string]interface{}, error) +} + +// ResizeOpts represents options for resizing a cluster. type ResizeOpts struct { AdjustmentType AdjustmentType `json:"adjustment_type,omitempty"` Number interface{} `json:"number,omitempty"` @@ -198,7 +211,7 @@ func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "resize") } -func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOpts) (r ActionResult) { +func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { b, err := opts.ToClusterResizeMap() if err != nil { r.Err = err @@ -216,7 +229,13 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOpts) (r Ac return } -// ScaleInOpts params +// ScaleInOptsBuilder allows extensions to add additional parameters to the +// ScaleIn request. +type ScaleInOptsBuilder interface { + ToClusterScaleInMap() (map[string]interface{}, error) +} + +// ScaleInOpts represents options used to scale-in a cluster. type ScaleInOpts struct { Count *int `json:"count,omitempty"` } @@ -226,7 +245,8 @@ func (opts ScaleInOpts) ToClusterScaleInMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "scale_in") } -func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r ActionResult) { +// ScaleIn will reduce the capacity of a cluster. +func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOptsBuilder) (r ActionResult) { b, err := opts.ToClusterScaleInMap() if err != nil { r.Err = err @@ -243,20 +263,25 @@ func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOpts) (r return } -// PolicyOpts params -type AttachPolicyOpts struct { - PolicyID string `json:"policy_id" required:"true"` - Enabled *bool `json:"enabled,omitempty"` +// ScaleOutOptsBuilder allows extensions to add additional parameters to the +// ScaleOut request. +type ScaleOutOptsBuilder interface { + ToClusterScaleOutMap() (map[string]interface{}, error) } -// ToClusterPolicyMap constructs a request body from PolicyOpts -func (opts AttachPolicyOpts) ToClusterAttachPolicyMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "policy_attach") +// ScaleOutOpts represents options used to scale-out a cluster. +type ScaleOutOpts struct { + Count int `json:"count,omitempty"` } -// Attach Policy -func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolicyOpts) (r ActionResult) { - b, err := opts.ToClusterAttachPolicyMap() +// ToClusterScaleOutMap constructs a request body from ScaleOutOpts. +func (opts ScaleOutOpts) ToClusterScaleOutMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "scale_out") +} + +// ScaleOut will increase the capacity of a cluster. +func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOptsBuilder) (r ActionResult) { + b, err := opts.ToClusterScaleOutMap() if err != nil { r.Err = err return @@ -272,17 +297,26 @@ func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolic return } -type UpdatePolicyOpts struct { +// AttachPolicyOptsBuilder allows extensions to add additional parameters to the +// AttachPolicy request. +type AttachPolicyOptsBuilder interface { + ToClusterAttachPolicyMap() (map[string]interface{}, error) +} + +// PolicyOpts params +type AttachPolicyOpts struct { PolicyID string `json:"policy_id" required:"true"` - Enabled *bool `json:"enabled,omitempty" required:"true"` + Enabled *bool `json:"enabled,omitempty"` } -func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "policy_update") +// ToClusterAttachPolicyMap constructs a request body from AttachPolicyOpts. +func (opts AttachPolicyOpts) ToClusterAttachPolicyMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "policy_attach") } -func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolicyOpts) (r ActionResult) { - b, err := opts.ToClusterUpdatePolicyMap() +// Attach Policy will attach a policy to a cluster. +func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolicyOptsBuilder) (r ActionResult) { + b, err := opts.ToClusterAttachPolicyMap() if err != nil { r.Err = err return @@ -298,45 +332,26 @@ func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolic return } -type ScaleOutOpts struct { - Count int `json:"count,omitempty"` +// UpdatePolicyOptsBuilder allows extensions to add additional parameters to the +// UpdatePolicy request. +type UpdatePolicyOptsBuilder interface { + ToClusterUpdatePolicyMap() (map[string]interface{}, error) } -func (opts ScaleOutOpts) ToClusterScaleOutMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "scale_out") -} - -func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOpts) (r ActionResult) { - b, err := opts.ToClusterScaleOutMap() - if err != nil { - r.Err = err - return - } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - if r.Err == nil { - r.Header = result.Header - } - - return -} - -// Cluster Recover -type RecoverOpts struct { - Operation RecoveryAction `json:"operation,omitempty"` - Check *bool `json:"check,omitempty"` - CheckCapacity *bool `json:"check_capacity,omitempty"` +// UpdatePolicyOpts represents options used to update a cluster policy. +type UpdatePolicyOpts struct { + PolicyID string `json:"policy_id" required:"true"` + Enabled *bool `json:"enabled,omitempty" required:"true"` } -func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "recover") +// ToClusterUpdatePolicyMap constructs a request body from UpdatePolicyOpts. +func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "policy_update") } -// Recover implements cluster recover request. -func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r ActionResult) { - b, err := opts.ToClusterRecoverMap() +// UpdatePolicy will update a cluster's policy. +func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolicyOptsBuilder) (r ActionResult) { + b, err := opts.ToClusterUpdatePolicyMap() if err != nil { r.Err = err return @@ -352,32 +367,24 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r return } -func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { - b := map[string]interface{}{ - "check": map[string]interface{}{}, - } - - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - if r.Err == nil { - r.Header = result.Header - } - - return +// DetachPolicyOptsBuilder allows extensions to add additional parameters to the +// DetachPolicy request. +type DetachPolicyOptsBuilder interface { + ToClusterDetachPolicyMap() (map[string]interface{}, error) } -// DetachPolicyOpts params +// DetachPolicyOpts represents options used to detach a policy from a cluster. type DetachPolicyOpts struct { PolicyID string `json:"policy_id" required:"true"` } +// ToClusterDetachPolicyMap constructs a request body from DetachPolicyOpts. func (opts DetachPolicyOpts) ToClusterDetachPolicyMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy_detach") } -func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolicyOpts) (r ActionResult) { +// DetachPolicy will detach a policy from a cluster. +func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolicyOptsBuilder) (r ActionResult) { b, err := opts.ToClusterDetachPolicyMap() if err != nil { r.Err = err @@ -395,12 +402,13 @@ func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolic return } -// ListPoliciesOptsBuilder Builder. +// ListPolicyOptsBuilder allows extensions to add additional parameters to the +// ListPolicies request. type ListPoliciesOptsBuilder interface { ToClusterListPoliciesQuery() (string, error) } -// ListPoliciesOpts params +// ListPoliciesOpts represents options to list a cluster's policies. type ListPoliciesOpts struct { Enabled *bool `q:"enabled"` Name string `q:"policy_name"` @@ -435,3 +443,56 @@ func GetPolicy(client *gophercloud.ServiceClient, clusterID string, policyID str _, r.Err = client.Get(getPolicyURL(client, clusterID, policyID), &r.Body, nil) return } + +// RecoverOptsBuilder allows extensions to add additional parameters to the +// Recover request. +type RecoverOptsBuilder interface { + ToClusterRecoverMap() (map[string]interface{}, error) +} + +// RecoverOpts represents options used to recover a cluster. +type RecoverOpts struct { + Operation RecoveryAction `json:"operation,omitempty"` + Check *bool `json:"check,omitempty"` + CheckCapacity *bool `json:"check_capacity,omitempty"` +} + +// ToClusterRecovermap constructs a request body from RecoverOpts. +func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "recover") +} + +// Recover implements cluster recover request. +func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOptsBuilder) (r ActionResult) { + b, err := opts.ToClusterRecoverMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} + +// Check will perform a health check on a cluster. +func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { + b := map[string]interface{}{ + "check": map[string]interface{}{}, + } + + var result *http.Response + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 55d90a0585..b9d7aa2be8 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -8,47 +8,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// CreateResult is the response of a Create operations. -type CreateResult struct { - commonResult -} - -// GetResult is the response of a Get operations. -type GetResult struct { - commonResult -} - -// PostResult is the response of a Post operations. -type PostResult struct { - commonResult -} - -// UpdateResult is the response of a Update operations. -type UpdateResult struct { - commonResult -} - -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// ClusterPolicyPage contains a single page of all policies from a ListDetails call. -type ClusterPolicyPage struct { - pagination.SinglePageBase -} - -// GetPolicyResult is the response of a Get operations. -type GetPolicyResult struct { - commonResult -} - +// Cluster represents an OpenStack Clustering cluster. type Cluster struct { Config map[string]interface{} `json:"config"` CreatedAt time.Time `json:"-"` @@ -74,51 +34,6 @@ type Cluster struct { User string `json:"user"` } -func (r commonResult) Extract() (*Cluster, error) { - var s struct { - Cluster *Cluster `json:"cluster"` - } - - err := r.ExtractInto(&s) - return s.Cluster, err -} - -type Action struct { - Action string `json:"action"` -} - -// ActionResult is the response of Senlin actions. -type ActionResult struct { - commonResult -} - -func (r ActionResult) Extract() (string, error) { - var s struct { - Action string `json:"action"` - } - err := r.ExtractInto(&s) - return s.Action, err -} - -// ClusterPage contains a single page of all clusters from a List call. -type ClusterPage struct { - pagination.LinkedPageBase -} - -func (page ClusterPage) IsEmpty() (bool, error) { - clusters, err := ExtractClusters(page) - return len(clusters) == 0, err -} - -// ExtractCluster provides access to the list of clusters in a page acquired from the List operation. -func ExtractClusters(r pagination.Page) ([]Cluster, error) { - var s struct { - Clusters []Cluster `json:"clusters"` - } - err := (r.(ClusterPage)).ExtractInto(&s) - return s.Clusters, err -} - func (r *Cluster) UnmarshalJSON(b []byte) error { type tmp Cluster var s struct { @@ -158,6 +73,7 @@ func (r *Cluster) UnmarshalJSON(b []byte) error { return nil } +// ClusterPolicy represents and OpenStack Clustering cluster policy. type ClusterPolicy struct { ClusterID string `json:"cluster_id"` ClusterName string `json:"cluster_name"` @@ -168,23 +84,51 @@ type ClusterPolicy struct { PolicyType string `json:"policy_type"` } -// ExtractClusterPolicies provides access to the list of profiles in a page acquired from the ListDetail operation. -func ExtractClusterPolicies(r pagination.Page) ([]ClusterPolicy, error) { +// Action represents an OpenStack Clustering action. +type Action struct { + Action string `json:"action"` +} + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult-based result as a Cluster. +func (r commonResult) Extract() (*Cluster, error) { var s struct { - ClusterPolicies []ClusterPolicy `json:"cluster_policies"` + Cluster *Cluster `json:"cluster"` } - err := (r.(ClusterPolicyPage)).ExtractInto(&s) - return s.ClusterPolicies, err + + err := r.ExtractInto(&s) + return s.Cluster, err } -// IsEmpty determines if ClusterPolicyPage contains any results. -func (page ClusterPolicyPage) IsEmpty() (bool, error) { - clusterPolicies, err := ExtractClusterPolicies(page) - return len(clusterPolicies) == 0, err +// CreateResult is the response of a Create operations. Call its Extract method +// to interpret it as a Cluster. +type CreateResult struct { + commonResult +} + +// GetResult is the response of a Get operations. Call its Extract method to +// interpret it as a Cluster. +type GetResult struct { + commonResult } -// Extract provides access to the individual Policy returned by the Get and -// Create functions. +// UpdateResult is the response of a Update operations. Call its Extract method +// to interpret it as a Cluster. +type UpdateResult struct { + commonResult +} + +// GetPolicyResult is the response of a Get operations. Call its Extract method +// to interpret it as a ClusterPolicy. +type GetPolicyResult struct { + gophercloud.Result +} + +// Extract interprets a GetPolicyResult as a ClusterPolicy. func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { var s struct { ClusterPolicy *ClusterPolicy `json:"cluster_policy"` @@ -192,3 +136,66 @@ func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { err := r.ExtractInto(&s) return s.ClusterPolicy, err } + +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// ClusterPage contains a single page of all clusters from a List call. +type ClusterPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Clusters contains any results. +func (page ClusterPage) IsEmpty() (bool, error) { + clusters, err := ExtractClusters(page) + return len(clusters) == 0, err +} + +// ClusterPolicyPage contains a single page of all policies from a List call +type ClusterPolicyPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a page of ClusterPolicies contains any +// results. +func (page ClusterPolicyPage) IsEmpty() (bool, error) { + clusterPolicies, err := ExtractClusterPolicies(page) + return len(clusterPolicies) == 0, err +} + +// ActionResult is the response of Senlin actions. Call its Extract method to +// obtain the Action ID of the action. +type ActionResult struct { + gophercloud.Result +} + +// Extract interprets any Action result as an Action. +func (r ActionResult) Extract() (string, error) { + var s struct { + Action string `json:"action"` + } + err := r.ExtractInto(&s) + return s.Action, err +} + +// ExtractClusters returns a slice of Clusters from the List operation. +func ExtractClusters(r pagination.Page) ([]Cluster, error) { + var s struct { + Clusters []Cluster `json:"clusters"` + } + err := (r.(ClusterPage)).ExtractInto(&s) + return s.Clusters, err +} + +// ExtractClusterPolicies returns a slice of ClusterPolicies from the +// ListClusterPolicies operation. +func ExtractClusterPolicies(r pagination.Page) ([]ClusterPolicy, error) { + var s struct { + ClusterPolicies []ClusterPolicy `json:"cluster_policies"` + } + err := (r.(ClusterPolicyPage)).ExtractInto(&s) + return s.ClusterPolicies, err +} From 6be68fdba81e9783bbee58c7f896b6be6904e05c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 03:05:44 +0000 Subject: [PATCH 0431/2296] Clustering v1: action updates --- openstack/clustering/v1/actions/doc.go | 12 ++-- openstack/clustering/v1/actions/requests.go | 20 +++--- openstack/clustering/v1/actions/results.go | 80 +++++++++++---------- 3 files changed, 58 insertions(+), 54 deletions(-) diff --git a/openstack/clustering/v1/actions/doc.go b/openstack/clustering/v1/actions/doc.go index d7b9ec8596..2312a512f3 100644 --- a/openstack/clustering/v1/actions/doc.go +++ b/openstack/clustering/v1/actions/doc.go @@ -1,7 +1,8 @@ /* -Package actions provides listing and retrieving of senlin actions for the OpenStack Clustering Service. +Package actions provides listing and retrieving of senlin actions for the +OpenStack Clustering Service. -Example to list actions +Example to List Actions opts := actions.ListOpts{ Limit: 5, @@ -19,13 +20,14 @@ Example to list actions return true, nil }) -Example to get an action +Example to Get an Action - action, err := actions.Get(serviceClient, "edce3528-864f-41fb-8759-f4707925cc09").Extract() + actionID := "edce3528-864f-41fb-8759-f4707925cc09" + action, err := actions.Get(serviceClient, actionID).Extract() if err != nil { panic(err) } - fmt.Printf("Action %+v: ", action) + fmt.Printf("Action %+v: ", action) */ package actions diff --git a/openstack/clustering/v1/actions/requests.go b/openstack/clustering/v1/actions/requests.go index ac27421c67..f1a4f43f3e 100644 --- a/openstack/clustering/v1/actions/requests.go +++ b/openstack/clustering/v1/actions/requests.go @@ -5,7 +5,13 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// ListOpts params +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToActionListQuery() (string, error) +} + +// ListOpts represents options used to list actions. type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` @@ -17,18 +23,13 @@ type ListOpts struct { Status string `q:"status"` } -// ListOptsBuilder builds query string for the ListOpts -type ListOptsBuilder interface { - ToActionListQuery() (string, error) -} - -// ToClusterListQuery builds a query string from ListOpts +// ToClusterListQuery builds a query string from ListOpts. func (opts ListOpts) ToActionListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } -// List instructs OpenStack to provide a list of actions +// List instructs OpenStack to provide a list of actions. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { @@ -43,8 +44,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// Get retrieves details of a single action. Use Extract to convert its -// result into an action id. +// Get retrieves details of a single action. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) return diff --git a/openstack/clustering/v1/actions/results.go b/openstack/clustering/v1/actions/results.go index a06422a2c2..ca03a4b71c 100644 --- a/openstack/clustering/v1/actions/results.go +++ b/openstack/clustering/v1/actions/results.go @@ -8,22 +8,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// GetResult is the response of a Get operations. -type GetResult struct { - commonResult -} - -// ActionPage contains a single page of all actions from a ListDetails call. -type ActionPage struct { - pagination.LinkedPageBase -} - -// Action represents a Detailed Action +// Action represents a detailed Action. type Action struct { Action string `json:"action"` Cause string `json:"cause"` @@ -48,29 +33,6 @@ type Action struct { User string `json:"user"` } -func (r commonResult) Extract() (*Action, error) { - var s struct { - Action *Action `json:"action"` - } - err := r.ExtractInto(&s) - return s.Action, err -} - -// ExtractActions provides access to the list of actions in a page acquired from the List operation. -func ExtractActions(r pagination.Page) ([]Action, error) { - var s struct { - Actions []Action `json:"actions"` - } - err := (r.(ActionPage)).ExtractInto(&s) - return s.Actions, err -} - -// IsEmpty determines if a ActionPage contains any results. -func (r ActionPage) IsEmpty() (bool, error) { - actions, err := ExtractActions(r) - return len(actions) == 0, err -} - func (r *Action) UnmarshalJSON(b []byte) error { type tmp Action var s struct { @@ -102,3 +64,43 @@ func (r *Action) UnmarshalJSON(b []byte) error { return nil } + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult-based result as an Action. +func (r commonResult) Extract() (*Action, error) { + var s struct { + Action *Action `json:"action"` + } + err := r.ExtractInto(&s) + return s.Action, err +} + +// GetResult is the response of a Get operations. Call its Extract method to +// interpret it as an Action. +type GetResult struct { + commonResult +} + +// ActionPage contains a single page of all actions from a List call. +type ActionPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a ActionPage contains any results. +func (r ActionPage) IsEmpty() (bool, error) { + actions, err := ExtractActions(r) + return len(actions) == 0, err +} + +// ExtractActions returns a slice of Actions from the List operation. +func ExtractActions(r pagination.Page) ([]Action, error) { + var s struct { + Actions []Action `json:"actions"` + } + err := (r.(ActionPage)).ExtractInto(&s) + return s.Actions, err +} From 8b6782f54c894ef957b68af299af8c2cb58bbcd9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 03:11:50 +0000 Subject: [PATCH 0432/2296] Clustering v1: node updates --- openstack/clustering/v1/nodes/doc.go | 8 +- openstack/clustering/v1/nodes/requests.go | 31 +++--- openstack/clustering/v1/nodes/results.go | 117 +++++++++++----------- 3 files changed, 81 insertions(+), 75 deletions(-) diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index 9db40e40d4..3257b8d12d 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -2,7 +2,7 @@ Package nodes provides information and interaction with the nodes through the OpenStack Clustering service. -Example to Create Nodes +Example to Create a Node opts := nodes.CreateOpts{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", @@ -16,6 +16,7 @@ Example to Create Nodes if err != nil { panic(err) } + fmt.Printf("node", node) Example to List Nodes @@ -38,7 +39,7 @@ Example to List Nodes fmt.Printf("%+v\n", node) } -Example to Update Nodes +Example to Update a Node opts := nodes.UpdateOpts{ Name: "new-node-name", @@ -60,7 +61,7 @@ Example to Delete a Node panic(err) } -Example to Get Node +Example to Get a Node nodeID := "node123" node, err := nodes.Get(serviceClient, nodeID).Extract() @@ -69,6 +70,5 @@ Example to Get Node } fmt.Printf("%+v\n", node) - */ package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index c0e1f84601..98bbd6d730 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -7,12 +7,13 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// CreateOptsBuilder Builder. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToNodeCreateMap() (map[string]interface{}, error) } -// CreateOpts params +// CreateOpts represents options used to create a Node. type CreateOpts struct { Role string `json:"role,omitempty"` ProfileID string `json:"profile_id" required:"true"` @@ -45,7 +46,13 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// UpdateOpts params +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToNodeUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update a Node. type UpdateOpts struct { Name string `json:"name,omitempty"` ProfileID string `json:"profile_id,omitempty"` @@ -53,12 +60,7 @@ type UpdateOpts struct { Metadata map[string]interface{} `json:"metadata,omitempty"` } -// UpdateOptsBuilder params -type UpdateOptsBuilder interface { - ToNodeUpdateMap() (map[string]interface{}, error) -} - -// ToClusterUpdateMap constructs a request body from CreateOpts. +// ToClusterUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "node") } @@ -82,12 +84,13 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// ListOptsBuilder Builder. +// ListOptsBuilder allows extensions to add additional parmeters to the +// List request. type ListOptsBuilder interface { ToNodeListQuery() (string, error) } -// ListOpts params +// ListOpts represents options used to list nodes. type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` @@ -120,7 +123,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// Delete deletes the specified node ID. +// Delete deletes the specified node. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(deleteURL(client, id), nil) @@ -133,7 +136,9 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { // Get makes a request against senlin to get a details of a node type func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) if r.Err == nil { r.Header = result.Header } diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index b62d24a5e3..e1deefd868 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -8,58 +8,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// CreateResult is the response of a Create operations. -type CreateResult struct { - commonResult -} - -// Extract provides access to the individual node returned by Get and Create -// GetResult is the response of a Get operations. -type GetResult struct { - commonResult -} - -// DeleteResult is the result from a Delete operation. Call ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// UpdateResult is the response of a Update operations. -type UpdateResult struct { - commonResult -} - -// Extract provides access to the individual node returned by Get and extracts Node -func (r commonResult) Extract() (*Node, error) { - var s struct { - Node *Node `json:"node"` - } - err := r.ExtractInto(&s) - return s.Node, err -} - -// ExtractNodes provides access to the list of nodes in a page acquired from -// the List operation. -func ExtractNodes(r pagination.Page) ([]Node, error) { - var s struct { - Nodes []Node `json:"nodes"` - } - err := (r.(NodePage)).ExtractInto(&s) - return s.Nodes, err -} - -// NodePage contains a single page of all nodes from a List call. -type NodePage struct { - pagination.LinkedPageBase -} - -// Node represents a node structure +// Node represents an OpenStack clustering node. type Node struct { ClusterID string `json:"cluster_id"` CreatedAt time.Time `json:"-"` @@ -82,12 +31,6 @@ type Node struct { User string `json:"user"` } -// IsEmpty determines if a NodePage contains any results. -func (page NodePage) IsEmpty() (bool, error) { - nodes, err := ExtractNodes(page) - return len(nodes) == 0, err -} - func (r *Node) UnmarshalJSON(b []byte) error { type tmp Node var s struct { @@ -126,3 +69,61 @@ func (r *Node) UnmarshalJSON(b []byte) error { return nil } + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult-based result as a Node. +func (r commonResult) Extract() (*Node, error) { + var s struct { + Node *Node `json:"node"` + } + err := r.ExtractInto(&s) + return s.Node, err +} + +// CreateResult is the result of a Create operation. Call its Extract +// method to intepret it as a Node. +type CreateResult struct { + commonResult +} + +// GetResult is the result of a Get operation. Call its Extract method to +// interpret it as a Node. +type GetResult struct { + commonResult +} + +// DeleteResult is the result from a Delete operation. Call ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// UpdateResult is the result of an Update operation. Call its Extract method +// to interpet it as a Node. +type UpdateResult struct { + commonResult +} + +// NodePage contains a single page of all nodes from a List call. +type NodePage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a NodePage contains any results. +func (page NodePage) IsEmpty() (bool, error) { + nodes, err := ExtractNodes(page) + return len(nodes) == 0, err +} + +// ExtractNodes returns a slice of Nodes from the List operation. +func ExtractNodes(r pagination.Page) ([]Node, error) { + var s struct { + Nodes []Node `json:"nodes"` + } + err := (r.(NodePage)).ExtractInto(&s) + return s.Nodes, err +} From cfd40eca4f6901161b93d87dfe94ad1e65308ad0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 03:22:17 +0000 Subject: [PATCH 0433/2296] Clustering v1: policies updates --- openstack/clustering/v1/policies/doc.go | 7 +- openstack/clustering/v1/policies/requests.go | 56 ++++++---- openstack/clustering/v1/policies/results.go | 102 ++++++++++--------- 3 files changed, 93 insertions(+), 72 deletions(-) diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index 6200a6fb21..ba5ae7b0d4 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -23,7 +23,7 @@ Example to List Policies } -Example to Create a policy +Example to Create a Policy opts := policies.CreateOpts{ Name: "new_policy", @@ -48,7 +48,7 @@ Example to Create a policy panic(err) } -Example to Update a policy +Example to Update a Policy opts := policies.UpdateOpts{ Name: "update_policy", @@ -59,7 +59,7 @@ Example to Update a policy panic(err) } -Example to Validate a policy +Example to Validate a Policy opts := policies.ValidateOpts{ Spec: policies.Spec{ @@ -82,6 +82,5 @@ Example to Validate a policy if err != nil { panic(err) } - */ package policies diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index 16e319d515..4224fe8ace 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -7,12 +7,13 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// ListOptsBuilder Builder. +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. type ListOptsBuilder interface { ToPolicyListQuery() (string, error) } -// ListOpts params +// ListOpts represents options used to list policies. type ListOpts struct { // Limit limits the number of Policies to return. Limit int `q:"limit"` @@ -60,13 +61,19 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// CreateOpts params +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPolicyCreateMap() (map[string]interface{}, error) +} + +// CreateOpts represents options used to create a policy. type CreateOpts struct { Name string `json:"name"` Spec Spec `json:"spec"` } -// ToPolicyCreateMap formats a CreateOpts into a body map. +// ToPolicyCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { @@ -77,7 +84,7 @@ func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { } // Create makes a request against the API to create a policy -func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err @@ -93,7 +100,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) return } -// Delete makes a request against the API to delete a policy +// Delete makes a request against the API to delete a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { var result *http.Response result, r.Err = client.Delete(policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ @@ -105,32 +112,30 @@ func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) return } -// UpdateOptsBuilder builder +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { ToPolicyUpdateMap() (map[string]interface{}, error) } -// UpdateOpts params +// UpdateOpts represents options to update a policy. type UpdateOpts struct { Name string `json:"name,omitempty"` } -// ToPolicyUpdateMap formats a UpdateOpts into a body map. +// ToPolicyUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "policy") - if err != nil { - return nil, err - } - return b, nil + return gophercloud.BuildRequestBody(opts, "policy") } -// Update implements profile updated request. +// Update updates a specified policy. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err - return r + return } + var result *http.Response result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, @@ -138,22 +143,29 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder if r.Err == nil { r.Header = result.Header } + return } -// ValidateOpts params +// ValidateOptsBuilder allows extensions to add additional parameters to the +// Validate request. +type ValidateOptsBuilder interface { + ToPolicyValidateMap() (map[string]interface{}, error) +} + +// ValidateOpts represents options used to validate a policy. type ValidateOpts struct { Spec Spec `json:"spec"` } -// ToValidatePolicyMap formats a CreateOpts into a body map. -func (opts ValidateOpts) ToValidatePolicyMap() (map[string]interface{}, error) { +// ToPolicyValidateMap formats a CreateOpts into a body map. +func (opts ValidateOpts) ToPolicyValidateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "policy") } -// Validate policy. -func Validate(client *gophercloud.ServiceClient, opts ValidateOpts) (r ValidateResult) { - b, err := opts.ToValidatePolicyMap() +// Validate policy will validate a specified policy. +func Validate(client *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) { + b, err := opts.ToPolicyValidateMap() if err != nil { r.Err = err return diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index 4b9fd6fd39..67d7002311 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// Policy represents a clustering policy in the Openstack cloud +// Policy represents a clustering policy in the Openstack cloud. type Policy struct { CreatedAt time.Time `json:"-"` Data map[string]interface{} `json:"data"` @@ -24,45 +24,6 @@ type Policy struct { User string `json:"user"` } -type Spec struct { - Description string `json:"description"` - Properties map[string]interface{} `json:"properties"` - Type string `json:"type"` - Version string `json:"version"` -} - -// ExtractPolicies interprets a page of results as a slice of Policy. -func ExtractPolicies(r pagination.Page) ([]Policy, error) { - var s struct { - Policies []Policy `json:"policies"` - } - err := (r.(PolicyPage)).ExtractInto(&s) - return s.Policies, err -} - -// PolicyPage contains a list page of all policies from a List call. -type PolicyPage struct { - pagination.MarkerPageBase -} - -// IsEmpty determines if a PolicyPage contains any results. -func (page PolicyPage) IsEmpty() (bool, error) { - policies, err := ExtractPolicies(page) - return len(policies) == 0, err -} - -// LastMarker returns the last policy ID in a ListResult. -func (r PolicyPage) LastMarker() (string, error) { - policies, err := ExtractPolicies(r) - if err != nil { - return "", err - } - if len(policies) == 0 { - return "", nil - } - return policies[len(policies)-1].ID, nil -} - func (r *Policy) UnmarshalJSON(b []byte) error { type tmp Policy var s struct { @@ -99,6 +60,14 @@ func (r *Policy) UnmarshalJSON(b []byte) error { return nil } +// Spec represents an OpenStack clustering policy spec. +type Spec struct { + Description string `json:"description"` + Properties map[string]interface{} `json:"properties"` + Type string `json:"type"` + Version string `json:"version"` +} + func (r *Spec) UnmarshalJSON(b []byte) error { type tmp Spec var s struct { @@ -125,10 +94,12 @@ func (r *Spec) UnmarshalJSON(b []byte) error { return nil } +// policyResult is the resposne of a base Policy result. type policyResult struct { gophercloud.Result } +// Extract interpets any policyResult-base result as a Policy. func (r policyResult) Extract() (*Policy, error) { var s struct { Policy *Policy `json:"policy"` @@ -138,24 +109,31 @@ func (r policyResult) Extract() (*Policy, error) { return s.Policy, err } +// CreateResult is the result of an Update operation. Call its Extract +// method to interpret it as a Policy. type CreateResult struct { policyResult } -type DeleteResult struct { - gophercloud.HeaderResult -} - -// UpdateResult is the response of a Update operations. +// UpdateResult is the result of an Update operation. Call its Extract +// method to interpret it as a Policy. type UpdateResult struct { policyResult } +// ValidateResult is the result of a Validate operation. Call its Extract +// method to interpret it as a Policy. type ValidateResult struct { policyResult } -// DeleteResult contains the delete information from a delete policy request +// DeleteResult is the result of a Delete operation. Call its Extract +// method to interpret it as a DeleteHeader. +type DeleteResult struct { + gophercloud.HeaderResult +} + +// DeleteHeader contains the delete information from a delete policy request. type DeleteHeader struct { RequestID string `json:"X-OpenStack-Request-ID"` } @@ -165,3 +143,35 @@ func (r DeleteResult) Extract() (*DeleteHeader, error) { err := r.HeaderResult.ExtractInto(&s) return s, err } + +// PolicyPage contains a list page of all policies from a List call. +type PolicyPage struct { + pagination.MarkerPageBase +} + +// IsEmpty determines if a PolicyPage contains any results. +func (page PolicyPage) IsEmpty() (bool, error) { + policies, err := ExtractPolicies(page) + return len(policies) == 0, err +} + +// LastMarker returns the last policy ID in a ListResult. +func (r PolicyPage) LastMarker() (string, error) { + policies, err := ExtractPolicies(r) + if err != nil { + return "", err + } + if len(policies) == 0 { + return "", nil + } + return policies[len(policies)-1].ID, nil +} + +// ExtractPolicies returns a slice of Policies from the List operation. +func ExtractPolicies(r pagination.Page) ([]Policy, error) { + var s struct { + Policies []Policy `json:"policies"` + } + err := (r.(PolicyPage)).ExtractInto(&s) + return s.Policies, err +} From 9b4b018254e9897ec68dcb48ebd0248933b08d7c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 03:29:11 +0000 Subject: [PATCH 0434/2296] Clustering v1: policytypes updates --- openstack/clustering/v1/policytypes/doc.go | 9 ++-- .../clustering/v1/policytypes/requests.go | 9 +++- .../clustering/v1/policytypes/results.go | 54 ++++++++++--------- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/openstack/clustering/v1/policytypes/doc.go b/openstack/clustering/v1/policytypes/doc.go index 1886939f39..2b1b6d6860 100644 --- a/openstack/clustering/v1/policytypes/doc.go +++ b/openstack/clustering/v1/policytypes/doc.go @@ -1,8 +1,8 @@ /* -Package policytypes lists all policy types and shows details for a policy type from the OpenStack -Clustering Service. +Package policytypes lists all policy types and shows details for a policy type +from the OpenStack Clustering Service. -Example to list policy types +Example to List Policy Types allPages, err := policytypes.List(clusteringClient).AllPages() if err != nil { @@ -18,13 +18,14 @@ Example to list policy types fmt.Printf("%+v\n", policyType) } -Example of get policy type details +Example to Get a Policy Type policyTypeName := "senlin.policy.affinity-1.0" policyTypeDetail, err := policyTypes.Get(clusteringClient, policyTypeName).Extract() if err != nil { panic(err) } + fmt.Printf("%+v\n", policyTypeDetail) */ package policytypes diff --git a/openstack/clustering/v1/policytypes/requests.go b/openstack/clustering/v1/policytypes/requests.go index 05ccb538fb..ca9a5a3be1 100644 --- a/openstack/clustering/v1/policytypes/requests.go +++ b/openstack/clustering/v1/policytypes/requests.go @@ -8,6 +8,7 @@ import ( // List makes a request against the API to list policy types. func List(client *gophercloud.ServiceClient) pagination.Pager { url := policyTypeListURL(client) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return PolicyTypePage{pagination.SinglePageBase(r)} }) @@ -15,7 +16,11 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // Get makes a request against the API to get details for a policy type. func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { - _, r.Err = client.Get(policyTypeGetURL(client, policyTypeName), &r.Body, - &gophercloud.RequestOpts{OkCodes: []int{200}}) + url := policyTypeGetURL(client, policyTypeName) + + _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return } diff --git a/openstack/clustering/v1/policytypes/results.go b/openstack/clustering/v1/policytypes/results.go index 0de84db4ab..98c1e53cb7 100644 --- a/openstack/clustering/v1/policytypes/results.go +++ b/openstack/clustering/v1/policytypes/results.go @@ -19,26 +19,6 @@ type SupportStatusType struct { Since string `json:"since"` } -// ExtractPolicyTypes interprets a page of results as a slice of PolicyTypes. -func ExtractPolicyTypes(r pagination.Page) ([]PolicyType, error) { - var s struct { - PolicyTypes []PolicyType `json:"policy_types"` - } - err := (r.(PolicyTypePage)).ExtractInto(&s) - return s.PolicyTypes, err -} - -// PolicyTypePage contains a single page of all policy types from a List call. -type PolicyTypePage struct { - pagination.SinglePageBase -} - -// IsEmpty determines if a PolicyType contains any results. -func (page PolicyTypePage) IsEmpty() (bool, error) { - policyTypes, err := ExtractPolicyTypes(page) - return len(policyTypes) == 0, err -} - // PolicyTypeDetail represents the detailed policy type information for a // clustering policy type. type PolicyTypeDetail struct { @@ -47,8 +27,12 @@ type PolicyTypeDetail struct { SupportStatus map[string][]SupportStatusType `json:"support_status,omitempty"` } -// Extract provides access to the individual policy type returned by Get -// and extracts PolicyTypeDetail. +// policyTypeResult is the base result of a Policy Type operation. +type policyTypeResult struct { + gophercloud.Result +} + +// Extract interprets any policyTypeResult result as a PolicyTypeDetail. func (r policyTypeResult) Extract() (*PolicyTypeDetail, error) { var s struct { PolicyType *PolicyTypeDetail `json:"policy_type"` @@ -57,10 +41,28 @@ func (r policyTypeResult) Extract() (*PolicyTypeDetail, error) { return s.PolicyType, err } -type policyTypeResult struct { - gophercloud.Result -} - +// GetResult is the result of a Get operation. Call its Extract method to +// interpret it as a PolicyTypeDetail. type GetResult struct { policyTypeResult } + +// PolicyTypePage contains a single page of all policy types from a List call. +type PolicyTypePage struct { + pagination.SinglePageBase +} + +// IsEmpty determines if a PolicyType contains any results. +func (page PolicyTypePage) IsEmpty() (bool, error) { + policyTypes, err := ExtractPolicyTypes(page) + return len(policyTypes) == 0, err +} + +// ExtractPolicyTypes returns a slice of PolicyTypes from a List operation. +func ExtractPolicyTypes(r pagination.Page) ([]PolicyType, error) { + var s struct { + PolicyTypes []PolicyType `json:"policy_types"` + } + err := (r.(PolicyTypePage)).ExtractInto(&s) + return s.PolicyTypes, err +} From f211d817bf689b346061d9fea7cfd4c93fd3176e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 03:35:38 +0000 Subject: [PATCH 0435/2296] Clustering v1: profiles updates --- openstack/clustering/v1/profiles/doc.go | 31 ++-- openstack/clustering/v1/profiles/requests.go | 30 ++-- openstack/clustering/v1/profiles/results.go | 155 ++++++++++--------- 3 files changed, 115 insertions(+), 101 deletions(-) diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index 9bb70601df..13676df8c1 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -1,5 +1,8 @@ /* -Example to Create a profile +Package profiles provides information and interaction with profiles through +the OpenStack Clustering service. + +Example to Create a Profile networks := []map[string]interface{} { {"network": "test-network"}, @@ -29,7 +32,7 @@ Example to Create a profile fmt.Println("Profile", profile) -Example to Get profile +Example to Get a Profile profile, err := profiles.Get(serviceClient, "profile-name").Extract() if err != nil { @@ -39,9 +42,13 @@ Example to Get profile fmt.Print("profile", profile) -Example to List profiles +Example to List Profiles + + listOpts := profiles.ListOpts{ + Limit: 2, + } - profiles.List(serviceClient, profiles.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + profiles.List(serviceClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { allProfiles, err := profiles.ExtractProfiles(page) if err != nil { panic(err) @@ -53,16 +60,20 @@ Example to List profiles return true, nil }) -Example to Update profile +Example to Update a Profile + + updateOpts := profiles.UpdateOpts{ + Name: "new-name", + } - profile, err := profiles.Update(serviceClient, profileName, profiles.UpdateOpts{Name: newProfileName}).Extract() - if err != nil { + profile, err := profiles.Update(serviceClient, profileName, updateOpts).Extract() + if err != nil { panic(err) - } + } - fmt.Print("profile", profile) + fmt.Print("profile", profile) -Example to Delete profile +Example to Delete a Profile profileID := "6dc6d336e3fc4c0a951b5698cd1236ee" err := profiles.Delete(serviceClient, profileID).ExtractErr() diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index 968a05d13d..5fb10f8159 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -7,12 +7,13 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// CreateOptsBuilder for options used for creating a profile. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToProfileCreateMap() (map[string]interface{}, error) } -// CreateOpts represents options used for creating a profile +// CreateOpts represents options used for creating a profile. type CreateOpts struct { Name string `json:"name" required:"true"` Metadata map[string]interface{} `json:"metadata,omitempty"` @@ -42,11 +43,12 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Get retrieves detail of a single profile. Use Extract to convert its -// result into a Profile. +// Get retrieves detail of a single profile. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) if r.Err == nil { r.Header = result.Header @@ -54,12 +56,13 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// ListOptsBuilder Builder. +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. type ListOptsBuilder interface { ToProfileListQuery() (string, error) } -// ListOpts params +// ListOpts represents options used to list profiles. type ListOpts struct { GlobalProject *bool `q:"global_project"` Limit int `q:"limit"` @@ -97,23 +100,18 @@ type UpdateOptsBuilder interface { ToProfileUpdateMap() (map[string]interface{}, error) } -// UpdateOpts implements Profile's UpdateOpts +// UpdateOpts represents options used to update a profile. type UpdateOpts struct { Metadata map[string]interface{} `json:"metadata,omitempty"` Name string `json:"name,omitempty"` } -// ToProfileUpdateMap assembles a request body based on the contents of -// UpdateOpts. +// ToProfileUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToProfileUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "profile") - if err != nil { - return nil, err - } - return b, nil + return gophercloud.BuildRequestBody(opts, "profile") } -// Update implements profile update request. +// Update updates a profile. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToProfileUpdateMap() if err != nil { diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index 558d9182f9..5b457d563c 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -10,51 +10,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// CreateResult is the response of a Create operation. -type CreateResult struct { - commonResult -} - -// GetResult is the response of a Get operations. -type GetResult struct { - commonResult -} - -// UpdateResult is the response of a Update operations. -type UpdateResult struct { - commonResult -} - -// Extract provides access to Profile returned by the Get and Create functions. -func (r commonResult) Extract() (*Profile, error) { - var s struct { - Profile *Profile `json:"profile"` - } - err := r.ExtractInto(&s) - return s.Profile, err -} - -// ExtractProfiles provides access to the list of profiles in a page from the List operation. -func ExtractProfiles(r pagination.Page) ([]Profile, error) { - var s struct { - Profiles []Profile `json:"profiles"` - } - err := (r.(ProfilePage)).ExtractInto(&s) - return s.Profiles, err -} - -type Spec struct { - Type string `json:"type"` - Version string `json:"version"` - Properties map[string]interface{} `json:"properties"` -} - -// Profile represent a detailed profile +// Profile represent a detailed profile. type Profile struct { CreatedAt time.Time `json:"-"` Domain string `json:"domain"` @@ -68,14 +24,42 @@ type Profile struct { User string `json:"user"` } -type ProfilePage struct { - pagination.LinkedPageBase +func (r *Profile) UnmarshalJSON(b []byte) error { + type tmp Profile + var s struct { + tmp + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Profile(s.tmp) + + if s.CreatedAt != "" { + r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) + if err != nil { + return err + } + } + + if s.UpdatedAt != "" { + r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) + if err != nil { + return err + } + } + + return nil } -// IsEmpty determines if a ProfilePage contains any results. -func (page ProfilePage) IsEmpty() (bool, error) { - profiles, err := ExtractProfiles(page) - return len(profiles) == 0, err +// Spec represents a profile spec. +type Spec struct { + Type string `json:"type"` + Version string `json:"version"` + Properties map[string]interface{} `json:"properties"` } func (r *Spec) UnmarshalJSON(b []byte) error { @@ -105,35 +89,36 @@ func (r *Spec) UnmarshalJSON(b []byte) error { return nil } -func (r *Profile) UnmarshalJSON(b []byte) error { - type tmp Profile - var s struct { - tmp - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - } +// commonResult is the base result of a Profile operation. +type commonResult struct { + gophercloud.Result +} - err := json.Unmarshal(b, &s) - if err != nil { - return err +// Extract provides access to Profile returned by the Get and Create functions. +func (r commonResult) Extract() (*Profile, error) { + var s struct { + Profile *Profile `json:"profile"` } - *r = Profile(s.tmp) + err := r.ExtractInto(&s) + return s.Profile, err +} - if s.CreatedAt != "" { - r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) - if err != nil { - return err - } - } +// CreateResult is the result of a Create operation. Call its Extract +// method to interpret it as a Profile. +type CreateResult struct { + commonResult +} - if s.UpdatedAt != "" { - r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) - if err != nil { - return err - } - } +// GetResult is the result of a Get operations. Call its Extract +// method to interpret it as a Profile. +type GetResult struct { + commonResult +} - return nil +// UpdateResult is the result of a Update operations. Call its Extract +// method to interpret it as a Profile. +type UpdateResult struct { + commonResult } // DeleteResult is the result from a Delete operation. Call its ExtractErr @@ -141,3 +126,23 @@ func (r *Profile) UnmarshalJSON(b []byte) error { type DeleteResult struct { gophercloud.ErrResult } + +// ProfilePage contains a single page of all profiles from a List operation. +type ProfilePage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a ProfilePage contains any results. +func (page ProfilePage) IsEmpty() (bool, error) { + profiles, err := ExtractProfiles(page) + return len(profiles) == 0, err +} + +// ExtractProfiles returns a slice of Profiles from the List operation. +func ExtractProfiles(r pagination.Page) ([]Profile, error) { + var s struct { + Profiles []Profile `json:"profiles"` + } + err := (r.(ProfilePage)).ExtractInto(&s) + return s.Profiles, err +} From 4dc1235315f895f39b64d4ddcf605595070a3cb5 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Thu, 22 Mar 2018 15:14:51 -0700 Subject: [PATCH 0436/2296] senlin-receivers-create --- openstack/clustering/v1/receivers/doc.go | 21 ++ openstack/clustering/v1/receivers/requests.go | 38 ++++ openstack/clustering/v1/receivers/results.go | 89 ++++++++ .../clustering/v1/receivers/testing/doc.go | 2 + .../v1/receivers/testing/requests_test.go | 198 ++++++++++++++++++ openstack/clustering/v1/receivers/urls.go | 14 ++ 6 files changed, 362 insertions(+) create mode 100644 openstack/clustering/v1/receivers/doc.go create mode 100644 openstack/clustering/v1/receivers/requests.go create mode 100644 openstack/clustering/v1/receivers/results.go create mode 100644 openstack/clustering/v1/receivers/testing/doc.go create mode 100644 openstack/clustering/v1/receivers/testing/requests_test.go create mode 100644 openstack/clustering/v1/receivers/urls.go diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go new file mode 100644 index 0000000000..5477cff6c3 --- /dev/null +++ b/openstack/clustering/v1/receivers/doc.go @@ -0,0 +1,21 @@ +/* +Package receivers provides information and interaction with the receivers through +the OpenStack Clustering service. + +Example to Create a Receiver + + createOpts := receivers.CreateOpts{ + Action: "CLUSTER_DEL_NODES", + ClusterID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2dc", + Name: "test_receiver", + Type: "webhook", + } + + receiver, err := receivers.Create(serviceClient, createOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("receiver", receiver) + +*/ +package receivers diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go new file mode 100644 index 0000000000..9f08f1a993 --- /dev/null +++ b/openstack/clustering/v1/receivers/requests.go @@ -0,0 +1,38 @@ +package receivers + +import ( + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder Builder. +type CreateOptsBuilder interface { + ToReceiverCreateMap() (map[string]interface{}, error) +} + +type CreateOpts struct { + Name string `json:"name" required:"true"` + ClusterID string `json:"cluster_id,omitempty"` + Type string `json:"type" required:"true"` + Action string `json:"action,omitempty"` + Actor map[string]interface{} `json:"actor,omitempty"` + Params map[string]interface{} `json:"params,omitempty"` +} + +// ToReceiverCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToReceiverCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "receiver") +} + +// Create requests the creation of a new receiver. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToReceiverCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + return +} diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go new file mode 100644 index 0000000000..306eb0b425 --- /dev/null +++ b/openstack/clustering/v1/receivers/results.go @@ -0,0 +1,89 @@ +package receivers + +import ( + "encoding/json" + "fmt" + "reflect" + "time" + + "github.com/gophercloud/gophercloud" +) + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response for a create operation. +type CreateResult struct { + commonResult +} + +// Extract provides access to the individual node returned by Get and extracts Node +func (r commonResult) Extract() (*Receiver, error) { + var s struct { + Receiver *Receiver `json:"receiver"` + } + err := r.ExtractInto(&s) + return s.Receiver, err +} + +type Receiver struct { + Action string `json:"action"` + Actor map[string]interface{} `json:"actor"` + Channel map[string]interface{} `json:"channel"` + Cluster string `json:"cluster_id"` + CreatedAt time.Time `json:"-"` + Domain string `json:"domain"` + ID string `json:"id"` + Name string `json:"name"` + Params map[string]interface{} `json:"params"` + Project string `json:"project"` + Type string `json:"type"` + UpdatedAt time.Time `json:"-"` + User string `json:"user"` +} + +func (r *Receiver) UnmarshalJSON(b []byte) error { + type tmp Receiver + var s struct { + tmp + CreatedAt interface{} `json:"created_at"` + UpdatedAt interface{} `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Receiver(s.tmp) + + switch t := s.CreatedAt.(type) { + case string: + if t != "" { + r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.CreatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) + } + + switch t := s.UpdatedAt.(type) { + case string: + if t != "" { + r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) + if err != nil { + return err + } + } + case nil: + r.UpdatedAt = time.Time{} + default: + return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) + } + + return nil +} diff --git a/openstack/clustering/v1/receivers/testing/doc.go b/openstack/clustering/v1/receivers/testing/doc.go new file mode 100644 index 0000000000..c692a35a01 --- /dev/null +++ b/openstack/clustering/v1/receivers/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_receivers_v1 +package testing diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go new file mode 100644 index 0000000000..63a164a6c3 --- /dev/null +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -0,0 +1,198 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateReceiver(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "2015-11-04T05:21:41Z", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": "2016-11-04T05:21:41Z", + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + + opts := receivers.CreateOpts{ + Name: "cluster_inflate", + ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + Type: "webhook", + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{}, + Params: map[string]interface{}{}, + } + + actual, err := receivers.Create(fake.ServiceClient(), opts).Extract() + if err != nil { + t.Error("Error creating receiver. err=", err) + } else { + createdAt, _ := time.Parse(time.RFC3339, "2015-11-04T05:21:41Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-11-04T05:21:41Z") + expected := receivers.Receiver{ + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{ + "trust_id": []string{ + "6dc6d336e3fc4c0a951b5698cd1236d9", + }, + }, + Channel: map[string]interface{}{ + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", + }, + Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + CreatedAt: createdAt, + Domain: "Default", + ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + Name: "cluster_inflate", + Params: map[string]interface{}{ + "count": "1", + }, + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Type: "webhook", + UpdatedAt: updatedAt, + User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", + } + + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestCreateReceiverInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": 123456789.0, + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": 123456789.0, + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + + opts := receivers.CreateOpts{ + Name: "cluster_inflate", + ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + Type: "webhook", + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{}, + Params: map[string]interface{}{}, + } + + _, err := receivers.Create(fake.ServiceClient(), opts).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestCreateReceiverInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "invalid", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": "invalid", + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + + opts := receivers.CreateOpts{ + Name: "cluster_inflate", + ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + Type: "webhook", + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{}, + Params: map[string]interface{}{}, + } + + _, err := receivers.Create(fake.ServiceClient(), opts).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/receivers/urls.go b/openstack/clustering/v1/receivers/urls.go new file mode 100644 index 0000000000..06324eb39b --- /dev/null +++ b/openstack/clustering/v1/receivers/urls.go @@ -0,0 +1,14 @@ +package receivers + +import "github.com/gophercloud/gophercloud" + +var apiVersion = "v1" +var apiName = "receivers" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} + +func createURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 7132e429bda73fa801b7e4e534f97494918b1a4e Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Thu, 22 Mar 2018 15:14:51 -0700 Subject: [PATCH 0437/2296] senlin-receivers-get --- openstack/clustering/v1/receivers/doc.go | 8 + openstack/clustering/v1/receivers/requests.go | 12 ++ openstack/clustering/v1/receivers/results.go | 5 + .../v1/receivers/testing/requests_test.go | 159 ++++++++++++++++++ openstack/clustering/v1/receivers/urls.go | 8 + 5 files changed, 192 insertions(+) diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index 5477cff6c3..42d8cf5cfe 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -17,5 +17,13 @@ Example to Create a Receiver } fmt.Println("receiver", receiver) +Example to Get a Receiver + + receiver, err := receivers.Get(serviceClient, "receiver-name").Extract() + if err != nil { + panic(err) + } + + fmt.Print("receiver", receiver) */ package receivers diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index 9f08f1a993..cdb33a9d79 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -1,6 +1,8 @@ package receivers import ( + "net/http" + "github.com/gophercloud/gophercloud" ) @@ -36,3 +38,13 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// Get retrieves details of a single receiver. Use Extract to convert its result into a Receiver. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + var result *http.Response + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index 306eb0b425..579e5002d4 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -19,6 +19,11 @@ type CreateResult struct { commonResult } +// GetResult is the response for a get operation. +type GetResult struct { + commonResult +} + // Extract provides access to the individual node returned by Get and extracts Node func (r commonResult) Extract() (*Receiver, error) { var s struct { diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go index 63a164a6c3..29e22eef47 100644 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -196,3 +196,162 @@ func TestCreateReceiverInvalidTimeString(t *testing.T) { _, err := receivers.Create(fake.ServiceClient(), opts).Extract() th.AssertEquals(t, false, err == nil) } + +func TestGetReceivers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "2015-11-04T05:21:41Z", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": "2016-11-04T05:21:41Z", + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + + createdAt, _ := time.Parse(time.RFC3339, "2015-11-04T05:21:41Z") + updatedAt, _ := time.Parse(time.RFC3339, "2016-11-04T05:21:41Z") + expected := receivers.Receiver{ + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{ + "trust_id": []string{ + "6dc6d336e3fc4c0a951b5698cd1236d9", + }, + }, + Channel: map[string]interface{}{ + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", + }, + Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + CreatedAt: createdAt, + Domain: "Default", + ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + Name: "cluster_inflate", + Params: map[string]interface{}{ + "count": "1", + }, + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Type: "webhook", + UpdatedAt: updatedAt, + User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", + } + + actual, err := receivers.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() + if err != nil { + t.Errorf("Failed Get receiver. %v", err) + } else { + th.AssertDeepEquals(t, expected, *actual) + } +} + +func TestGetReceiverInvalidCreatedAtTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": 123456789.0, + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": 123456789.0, + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + + _, err := receivers.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestGetReceiverInvalidUpdatedAtTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "invalid", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": "invalid", + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + + _, err := receivers.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/receivers/urls.go b/openstack/clustering/v1/receivers/urls.go index 06324eb39b..286f799c0e 100644 --- a/openstack/clustering/v1/receivers/urls.go +++ b/openstack/clustering/v1/receivers/urls.go @@ -12,3 +12,11 @@ func commonURL(client *gophercloud.ServiceClient) string { func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func idURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From ad0f38238faf342c1220d3ff97139043f3f7f590 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Thu, 22 Mar 2018 15:14:51 -0700 Subject: [PATCH 0438/2296] senlin-receivers-update --- openstack/clustering/v1/receivers/doc.go | 9 + openstack/clustering/v1/receivers/requests.go | 34 ++++ openstack/clustering/v1/receivers/results.go | 6 + .../v1/receivers/testing/requests_test.go | 167 ++++++++++++++++++ openstack/clustering/v1/receivers/urls.go | 4 + 5 files changed, 220 insertions(+) diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index 42d8cf5cfe..e6d4c519b4 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -24,6 +24,15 @@ Example to Get a Receiver panic(err) } + fmt.Print("receiver", receiver) + +Example to Update Receiver + + receiver, err := receivers.Update(serviceClient, receiverName, receivers.UpdateOpts{Name: newReceiverName}).Extract() + if err != nil { + panic(err) + } + fmt.Print("receiver", receiver) */ package receivers diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index cdb33a9d79..930f2bd3b1 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -39,6 +39,40 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// UpdateOpts params +type UpdateOpts struct { + Name string `json:"name,omitempty"` + Action string `json:"action,omitempty"` + Params map[string]interface{} `json:"params,omitempty"` +} + +type UpdateOptsBuilder interface { + ToReceiverUpdateMap() (map[string]interface{}, error) +} + +func (opts UpdateOpts) ToReceiverUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "receiver") +} + +// Update requests the update of a receiver. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToReceiverUpdateMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, + }) + + if r.Err == nil { + r.Header = result.Header + } + return +} + // Get retrieves details of a single receiver. Use Extract to convert its result into a Receiver. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { var result *http.Response diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index 579e5002d4..6208fbe7e0 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -24,6 +24,11 @@ type GetResult struct { commonResult } +// UpdateResult is the response of a Update operations. +type UpdateResult struct { + commonResult +} + // Extract provides access to the individual node returned by Get and extracts Node func (r commonResult) Extract() (*Receiver, error) { var s struct { @@ -33,6 +38,7 @@ func (r commonResult) Extract() (*Receiver, error) { return s.Receiver, err } +// Receiver represent a detailed receiver type Receiver struct { Action string `json:"action"` Actor map[string]interface{} `json:"actor"` diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go index 29e22eef47..dd0cf36111 100644 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -355,3 +355,170 @@ func TestGetReceiverInvalidUpdatedAtTime(t *testing.T) { _, err := receivers.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() th.AssertEquals(t, false, err == nil) } + +func TestUpdateReceiver(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "2015-06-27T05:09:43Z", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": null, + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + + createdAt, _ := time.Parse(time.RFC3339, "2015-06-27T05:09:43Z") + updatedAt := time.Time{} + expected := receivers.Receiver{ + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{ + "trust_id": []string{ + "6dc6d336e3fc4c0a951b5698cd1236d9", + }, + }, + Channel: map[string]interface{}{ + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", + }, + Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + CreatedAt: createdAt, + Domain: "Default", + ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + Name: "cluster_inflate", + Params: map[string]interface{}{ + "count": "1", + }, + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Type: "webhook", + UpdatedAt: updatedAt, + User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", + } + + opts := receivers.UpdateOpts{ + Name: "cluster_inflate", + Action: "CLUSTER_SCALE_OUT", + Params: map[string]interface{}{ + "count": "2", + }, + } + actual, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, *actual) +} + +func TestUpdateReceiversInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": 123456789.0, + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": 123456789.0, + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + opts := receivers.UpdateOpts{ + Name: "cluster_inflate", + } + _, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() + th.AssertEquals(t, false, err == nil) +} + +func TestUpdateReceiverInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": null, + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": "invalid", + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + }`) + }) + opts := receivers.UpdateOpts{ + Name: "cluster_inflate", + } + _, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() + th.AssertEquals(t, false, err == nil) +} diff --git a/openstack/clustering/v1/receivers/urls.go b/openstack/clustering/v1/receivers/urls.go index 286f799c0e..bfd1c2543a 100644 --- a/openstack/clustering/v1/receivers/urls.go +++ b/openstack/clustering/v1/receivers/urls.go @@ -20,3 +20,7 @@ func idURL(client *gophercloud.ServiceClient, id string) string { func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 5e3230617213e6068de10431211608b246df68f1 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Thu, 22 Mar 2018 15:14:51 -0700 Subject: [PATCH 0439/2296] senlin-receivers-list --- openstack/clustering/v1/receivers/doc.go | 14 ++ openstack/clustering/v1/receivers/requests.go | 41 ++++ openstack/clustering/v1/receivers/results.go | 21 ++ .../v1/receivers/testing/requests_test.go | 198 ++++++++++++++++++ openstack/clustering/v1/receivers/urls.go | 4 + 5 files changed, 278 insertions(+) diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index e6d4c519b4..c76714b12d 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -34,5 +34,19 @@ Example to Update Receiver } fmt.Print("receiver", receiver) + +Example to List Receivers + + receivers.List(serviceClient, receivers.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + allReceivers, err := receivers.ExtractReceivers(page) + if err != nil { + panic(err) + } + + for _, receiver := range allReceivers { + fmt.Printf("%+v\n", receiver) + } + return true, nil + }) */ package receivers diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index 930f2bd3b1..d0d6c74108 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder Builder. @@ -82,3 +83,43 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { } return } + +// ListOpts params +type ListOpts struct { + Limit int `q:"limit"` + Marker string `q:"marker"` + Sort string `q:"sort"` + GlobalProject string `q:"global_project"` + Name string `q:"name"` + Type string `q:"type"` + ClusterID string `q:"cluster_id"` + Action string `q:"action"` + User string `q:"user"` +} + +// ListOptsBuilder Builder. +type ListOptsBuilder interface { + ToReceiverListQuery() (string, error) +} + +// ToReceiverListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToReceiverListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of cluster. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToReceiverListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ReceiverPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index 6208fbe7e0..9f1dd4e952 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // commonResult is the response of a base result. @@ -98,3 +99,23 @@ func (r *Receiver) UnmarshalJSON(b []byte) error { return nil } + +// ExtractReceivers provides access to the list of nodes in a page acquired from the ListDetail operation. +func ExtractReceivers(r pagination.Page) ([]Receiver, error) { + var s struct { + Receivers []Receiver `json:"receivers"` + } + err := (r.(ReceiverPage)).ExtractInto(&s) + return s.Receivers, err +} + +// ReceiverPage contains a single page of all nodes from a ListDetails call. +type ReceiverPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a ProfilePage contains any results. +func (page ReceiverPage) IsEmpty() (bool, error) { + receivers, err := ExtractReceivers(page) + return len(receivers) == 0, err +} diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go index dd0cf36111..85cff38d31 100644 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -522,3 +523,200 @@ func TestUpdateReceiverInvalidTimeString(t *testing.T) { _, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() th.AssertEquals(t, false, err == nil) } + +func TestListReceivers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestFormValues(t, r, map[string]string{"limit": "2", "sort": "name:asc,status:desc"}) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "receivers": [ + { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "2015-06-27T05:09:43Z", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": null, + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + ] + }`) + }) + + opts := receivers.ListOpts{ + Limit: 2, + Sort: "name:asc,status:desc", + } + count := 0 + receivers.List(fake.ServiceClient(), opts).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := receivers.ExtractReceivers(page) + if err != nil { + t.Errorf("Failed to extract receivers: %v", err) + return false, err + } + + createdAt, _ := time.Parse(time.RFC3339, "2015-06-27T05:09:43Z") + updatedAt := time.Time{} + expected := []receivers.Receiver{ + { + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{ + "trust_id": []string{ + "6dc6d336e3fc4c0a951b5698cd1236d9", + }, + }, + Channel: map[string]interface{}{ + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", + }, + Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + CreatedAt: createdAt, + Domain: "Default", + ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + Name: "cluster_inflate", + Params: map[string]interface{}{ + "count": "1", + }, + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Type: "webhook", + UpdatedAt: updatedAt, + User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", + }, + } + + th.AssertDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestListReceiversInvalidTimeFloat(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers", 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, ` + { + "receivers": [ + { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": 123456789.0, + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": 123456789.0, + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + ] + }`) + }) + + count := 0 + err := receivers.List(fake.ServiceClient(), receivers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + return true, nil + }) + + th.AssertEquals(t, false, err == nil) + if count != 0 { + t.Errorf("Expected 0 page of receivers, got %d pages instead", count) + } +} + +func TestListReceiversInvalidTimeString(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers", 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, ` + { + "receivers": [ + { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "invalid", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": "invalid", + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + ] + }`) + }) + + count := 0 + err := receivers.List(fake.ServiceClient(), receivers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + return true, nil + }) + + th.AssertEquals(t, false, err == nil) + if count != 0 { + t.Errorf("Expected 0 page of receivers, got %d pages instead", count) + } +} diff --git a/openstack/clustering/v1/receivers/urls.go b/openstack/clustering/v1/receivers/urls.go index bfd1c2543a..2cc6f2df6e 100644 --- a/openstack/clustering/v1/receivers/urls.go +++ b/openstack/clustering/v1/receivers/urls.go @@ -24,3 +24,7 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From dfb352e34a1fd5b595ac7bd918f65c3edcf0bedc Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Thu, 22 Mar 2018 15:14:51 -0700 Subject: [PATCH 0440/2296] senlin-receivers-delete --- openstack/clustering/v1/receivers/doc.go | 10 ++++++++++ openstack/clustering/v1/receivers/requests.go | 6 ++++++ openstack/clustering/v1/receivers/results.go | 6 ++++++ .../v1/receivers/testing/requests_test.go | 16 ++++++++++++++++ openstack/clustering/v1/receivers/urls.go | 4 ++++ 5 files changed, 42 insertions(+) diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index c76714b12d..47316363c1 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -26,6 +26,16 @@ Example to Get a Receiver fmt.Print("receiver", receiver) +Example to Delete receiver + + receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" + err := receivers.Delete(serviceClient, receiverID).ExtractErr() + if err != nil { + panic(err) + } + + fmt.Print("receiver", receiver) + Example to Update Receiver receiver, err := receivers.Update(serviceClient, receiverName, receivers.UpdateOpts{Name: newReceiverName}).Extract() diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index d0d6c74108..1cbff92e62 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -123,3 +123,9 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return ReceiverPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Delete deletes the specified receiver ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index 9f1dd4e952..fa2607f786 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -119,3 +119,9 @@ func (page ReceiverPage) IsEmpty() (bool, error) { receivers, err := ExtractReceivers(page) return len(receivers) == 0, err } + +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go index 85cff38d31..626c3932a6 100644 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -720,3 +720,19 @@ func TestListReceiversInvalidTimeString(t *testing.T) { t.Errorf("Expected 0 page of receivers, got %d pages instead", count) } } + +func TestDeleteReceiver(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", 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) + }) + + deleteResult := receivers.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") + th.AssertNoErr(t, deleteResult.ExtractErr()) +} diff --git a/openstack/clustering/v1/receivers/urls.go b/openstack/clustering/v1/receivers/urls.go index 2cc6f2df6e..be97ded533 100644 --- a/openstack/clustering/v1/receivers/urls.go +++ b/openstack/clustering/v1/receivers/urls.go @@ -28,3 +28,7 @@ func updateURL(client *gophercloud.ServiceClient, id string) string { func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From f193a16f1a5e613631347875e124e07645c4ed45 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Jun 2018 21:26:08 +0000 Subject: [PATCH 0441/2296] Clustering v1: receivers updates --- .../openstack/clustering/v1/clustering.go | 70 +- .../openstack/clustering/v1/receivers_test.go | 59 ++ openstack/clustering/v1/receivers/doc.go | 24 +- openstack/clustering/v1/receivers/requests.go | 42 +- openstack/clustering/v1/receivers/results.go | 119 ++-- .../v1/receivers/testing/fixtures.go | 229 ++++++ .../v1/receivers/testing/requests_test.go | 674 +----------------- 7 files changed, 466 insertions(+), 751 deletions(-) create mode 100644 acceptance/openstack/clustering/v1/receivers_test.go create mode 100644 openstack/clustering/v1/receivers/testing/fixtures.go diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go index 2acc652869..35da0a6607 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -14,6 +14,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -69,7 +70,9 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, profileID st t.Logf("Cluster %s action ID: %s", name, actionID) err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) + if err != nil { + return nil, err + } cluster, err := res.Extract() if err != nil { @@ -124,7 +127,9 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, prof t.Logf("Node %s action ID: %s", name, actionID) err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) + if err != nil { + return nil, err + } node, err := res.Extract() if err != nil { @@ -132,12 +137,16 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, prof } err = WaitForNodeStatus(client, node.ID, "ACTIVE") - th.AssertNoErr(t, err) + if err != nil { + return nil, err + } t.Logf("Successfully created node: %s", node.ID) node, err = nodes.Get(client, node.ID).Extract() - th.AssertNoErr(t, err) + if err != nil { + return nil, err + } tools.PrintResource(t, node) tools.PrintResource(t, node.CreatedAt) @@ -189,7 +198,9 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies.Po // profile could not be created. func CreateProfile(t *testing.T, client *gophercloud.ServiceClient) (*profiles.Profile, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) + if err != nil { + return nil, err + } name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create profile: %s", name) @@ -242,6 +253,40 @@ func CreateProfile(t *testing.T, client *gophercloud.ServiceClient) (*profiles.P return profile, nil } +// CreateReceiver will create a random profile. An error will be returned if the +// profile could not be created. +func CreateReceiver(t *testing.T, client *gophercloud.ServiceClient, clusterID string) (*receivers.Receiver, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create receiver: %s", name) + + createOpts := receivers.CreateOpts{ + Name: name, + ClusterID: clusterID, + Type: receivers.WebhookReceiver, + Action: "CLUSTER_SCALE_OUT", + } + + res := receivers.Create(client, createOpts) + if res.Err != nil { + return nil, res.Err + } + + receiver, err := res.Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created receiver: %s", receiver.ID) + + tools.PrintResource(t, receiver) + tools.PrintResource(t, receiver.CreatedAt) + + th.AssertEquals(t, name, receiver.Name) + th.AssertEquals(t, createOpts.Action, receiver.Action) + + return receiver, nil +} + // DeleteCluster will delete a given policy. A fatal error will occur if the // cluster could not be deleted. This works best as a deferred function. func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { @@ -317,6 +362,21 @@ func DeleteProfile(t *testing.T, client *gophercloud.ServiceClient, id string) { return } +// DeleteReceiver will delete a given receiver. A fatal error will occur if the +// receiver could not be deleted. This works best as a deferred function. +func DeleteReceiver(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete Receiver: %s", id) + + res := receivers.Delete(client, id) + if res.Err != nil { + t.Fatalf("Error deleting receiver %s: %s:", id, res.Err) + } + + t.Logf("Successfully deleted receiver: %s", id) + + return +} + // GetActionID parses an HTTP header and returns the action ID. func GetActionID(headers http.Header) (string, error) { location := headers.Get("Location") diff --git a/acceptance/openstack/clustering/v1/receivers_test.go b/acceptance/openstack/clustering/v1/receivers_test.go new file mode 100644 index 0000000000..ff0ca8eea2 --- /dev/null +++ b/acceptance/openstack/clustering/v1/receivers_test.go @@ -0,0 +1,59 @@ +// +build acceptance clustering policies + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestReceiversCRUD(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + receiver, err := CreateReceiver(t, client, cluster.ID) + th.AssertNoErr(t, err) + defer DeleteReceiver(t, client, receiver.ID) + + // Test listing receivers + allPages, err := receivers.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allReceivers, err := receivers.ExtractReceivers(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allReceivers { + if v.ID == receiver.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + // Test updating receivers + newName := receiver.Name + "-UPDATED" + updateOpts := receivers.UpdateOpts{ + Name: newName, + } + + receiver, err = receivers.Update(client, receiver.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, receiver) + tools.PrintResource(t, receiver.UpdatedAt) + + th.AssertEquals(t, receiver.Name, newName) +} diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index 47316363c1..0217136a62 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -8,14 +8,15 @@ Example to Create a Receiver Action: "CLUSTER_DEL_NODES", ClusterID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2dc", Name: "test_receiver", - Type: "webhook", + Type: receivers.WebhookReceiver, } receiver, err := receivers.Create(serviceClient, createOpts).Extract() if err != nil { panic(err) } - fmt.Println("receiver", receiver) + + fmt.Printf("%v\n", receiver) Example to Get a Receiver @@ -24,7 +25,7 @@ Example to Get a Receiver panic(err) } - fmt.Print("receiver", receiver) + fmt.Printf("%v\n", receiver) Example to Delete receiver @@ -34,20 +35,29 @@ Example to Delete receiver panic(err) } - fmt.Print("receiver", receiver) + fmt.Printf("%v\n", receiver) Example to Update Receiver - receiver, err := receivers.Update(serviceClient, receiverName, receivers.UpdateOpts{Name: newReceiverName}).Extract() + updateOpts := receivers.UpdateOpts{ + Name: "new-name", + } + + receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" + receiver, err := receivers.Update(serviceClient, receiverID, updateOpts).Extract() if err != nil { panic(err) } - fmt.Print("receiver", receiver) + fmt.Printf("%v\n", receiver) Example to List Receivers - receivers.List(serviceClient, receivers.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + listOpts := receivers.ListOpts{ + Limit: 2, + } + + receivers.List(serviceClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { allReceivers, err := receivers.ExtractReceivers(page) if err != nil { panic(err) diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index 1cbff92e62..1c528c732d 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -7,15 +7,25 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// CreateOptsBuilder Builder. +// ReceiverType represents a valid type of receiver +type ReceiverType string + +const ( + WebhookReceiver ReceiverType = "webhook" + MessageReceiver ReceiverType = "message" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToReceiverCreateMap() (map[string]interface{}, error) } +// CreatOpts represents options used to create a receiver. type CreateOpts struct { Name string `json:"name" required:"true"` ClusterID string `json:"cluster_id,omitempty"` - Type string `json:"type" required:"true"` + Type ReceiverType `json:"type" required:"true"` Action string `json:"action,omitempty"` Actor map[string]interface{} `json:"actor,omitempty"` Params map[string]interface{} `json:"params,omitempty"` @@ -40,17 +50,20 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// UpdateOpts params +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToReceiverUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update a receiver. type UpdateOpts struct { Name string `json:"name,omitempty"` Action string `json:"action,omitempty"` Params map[string]interface{} `json:"params,omitempty"` } -type UpdateOptsBuilder interface { - ToReceiverUpdateMap() (map[string]interface{}, error) -} - +// ToReceiverUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToReceiverUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "receiver") } @@ -84,12 +97,18 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// ListOpts params +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToReceiverListQuery() (string, error) +} + +// ListOpts represents options used to list recievers. type ListOpts struct { Limit int `q:"limit"` Marker string `q:"marker"` Sort string `q:"sort"` - GlobalProject string `q:"global_project"` + GlobalProject *bool `q:"global_project"` Name string `q:"name"` Type string `q:"type"` ClusterID string `q:"cluster_id"` @@ -97,11 +116,6 @@ type ListOpts struct { User string `q:"user"` } -// ListOptsBuilder Builder. -type ListOptsBuilder interface { - ToReceiverListQuery() (string, error) -} - // ToReceiverListQuery formats a ListOpts into a query string. func (opts ListOpts) ToReceiverListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index fa2607f786..fabb008c9f 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -2,43 +2,12 @@ package receivers import ( "encoding/json" - "fmt" - "reflect" "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// CreateResult is the response for a create operation. -type CreateResult struct { - commonResult -} - -// GetResult is the response for a get operation. -type GetResult struct { - commonResult -} - -// UpdateResult is the response of a Update operations. -type UpdateResult struct { - commonResult -} - -// Extract provides access to the individual node returned by Get and extracts Node -func (r commonResult) Extract() (*Receiver, error) { - var s struct { - Receiver *Receiver `json:"receiver"` - } - err := r.ExtractInto(&s) - return s.Receiver, err -} - // Receiver represent a detailed receiver type Receiver struct { Action string `json:"action"` @@ -60,8 +29,8 @@ func (r *Receiver) UnmarshalJSON(b []byte) error { type tmp Receiver var s struct { tmp - CreatedAt interface{} `json:"created_at"` - UpdatedAt interface{} `json:"updated_at"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -69,59 +38,77 @@ func (r *Receiver) UnmarshalJSON(b []byte) error { } *r = Receiver(s.tmp) - switch t := s.CreatedAt.(type) { - case string: - if t != "" { - r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.CreatedAt != "" { + r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) + if err != nil { + return err } - case nil: - r.CreatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.CreatedAt)) } - switch t := s.UpdatedAt.(type) { - case string: - if t != "" { - r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, t) - if err != nil { - return err - } + if s.UpdatedAt != "" { + r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) + if err != nil { + return err } - case nil: - r.UpdatedAt = time.Time{} - default: - return fmt.Errorf("Invalid type for time. type=%v", reflect.TypeOf(s.UpdatedAt)) } return nil } -// ExtractReceivers provides access to the list of nodes in a page acquired from the ListDetail operation. -func ExtractReceivers(r pagination.Page) ([]Receiver, error) { +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult-based result as a Receiver. +func (r commonResult) Extract() (*Receiver, error) { var s struct { - Receivers []Receiver `json:"receivers"` + Receiver *Receiver `json:"receiver"` } - err := (r.(ReceiverPage)).ExtractInto(&s) - return s.Receivers, err + err := r.ExtractInto(&s) + return s.Receiver, err +} + +// CreateResult is the result of a Create operation. Call its Extract method +// to interpret it as a Receiver. +type CreateResult struct { + commonResult +} + +// GetResult is the result for of a Get operation. Call its Extract method +// to interpret it as a Receiver. +type GetResult struct { + commonResult +} + +// UpdateResult is the result of a Update operation. Call its Extract method +// to interpret it as a Receiver. +type UpdateResult struct { + commonResult } -// ReceiverPage contains a single page of all nodes from a ListDetails call. +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// ReceiverPage contains a single page of all nodes from a List operation. type ReceiverPage struct { pagination.LinkedPageBase } -// IsEmpty determines if a ProfilePage contains any results. +// IsEmpty determines if a ReceiverPage contains any results. func (page ReceiverPage) IsEmpty() (bool, error) { receivers, err := ExtractReceivers(page) return len(receivers) == 0, err } -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult +// ExtractReceivers returns a slice of Receivers from the List operation. +func ExtractReceivers(r pagination.Page) ([]Receiver, error) { + var s struct { + Receivers []Receiver `json:"receivers"` + } + err := (r.(ReceiverPage)).ExtractInto(&s) + return s.Receivers, err } diff --git a/openstack/clustering/v1/receivers/testing/fixtures.go b/openstack/clustering/v1/receivers/testing/fixtures.go new file mode 100644 index 0000000000..08712c8b95 --- /dev/null +++ b/openstack/clustering/v1/receivers/testing/fixtures.go @@ -0,0 +1,229 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const CreateResponse = ` +{ + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "2015-11-04T05:21:41Z", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": "2016-11-04T05:21:41Z", + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } +}` + +var ExpectedReceiver = receivers.Receiver{ + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{ + "trust_id": []string{ + "6dc6d336e3fc4c0a951b5698cd1236d9", + }, + }, + Channel: map[string]interface{}{ + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", + }, + Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + CreatedAt: time.Date(2015, 11, 4, 5, 21, 41, 0, time.UTC), + Domain: "Default", + ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + Name: "cluster_inflate", + Params: map[string]interface{}{ + "count": "1", + }, + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Type: "webhook", + UpdatedAt: time.Date(2016, 11, 4, 5, 21, 41, 0, time.UTC), + User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", +} + +const GetResponse = ` +{ + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "2015-11-04T05:21:41Z", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": "2016-11-04T05:21:41Z", + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } +}` + +const UpdateResponse = ` +{ + "receiver": { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "2015-06-27T05:09:43Z", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": null, + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } +}` + +var ExpectedUpdateReceiver = receivers.Receiver{ + Action: "CLUSTER_SCALE_OUT", + Actor: map[string]interface{}{ + "trust_id": []string{ + "6dc6d336e3fc4c0a951b5698cd1236d9", + }, + }, + Channel: map[string]interface{}{ + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", + }, + Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + CreatedAt: time.Date(2015, 6, 27, 5, 9, 43, 0, time.UTC), + Domain: "Default", + ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + Name: "cluster_inflate", + Params: map[string]interface{}{ + "count": "1", + }, + Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", + Type: "webhook", + User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", +} + +const ListResponse = ` +{ + "receivers": [ + { + "action": "CLUSTER_SCALE_OUT", + "actor": { + "trust_id": [ + "6dc6d336e3fc4c0a951b5698cd1236d9" + ] + }, + "channel": { + "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" + }, + "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", + "created_at": "2015-06-27T05:09:43Z", + "domain": "Default", + "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", + "name": "cluster_inflate", + "params": { + "count": "1" + }, + "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", + "type": "webhook", + "updated_at": null, + "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" + } + ] +}` + +var ExpectedReceiversList = []receivers.Receiver{ExpectedUpdateReceiver} + +func HandleCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, CreateResponse) + }) +} + +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/receivers/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, GetResponse) + }) +} + +func HandleUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, UpdateResponse) + }) +} + +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestFormValues(t, r, map[string]string{"limit": "2", "sort": "name:asc,status:desc"}) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ListResponse) + }) +} + +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", 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) + }) +} diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go index 626c3932a6..02f8f0692d 100644 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -1,10 +1,7 @@ package testing import ( - "fmt" - "net/http" "testing" - "time" "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" "github.com/gophercloud/gophercloud/pagination" @@ -16,411 +13,38 @@ func TestCreateReceiver(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "2015-11-04T05:21:41Z", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": "2016-11-04T05:21:41Z", - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) + HandleCreateSuccessfully(t) opts := receivers.CreateOpts{ Name: "cluster_inflate", ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - Type: "webhook", + Type: receivers.WebhookReceiver, Action: "CLUSTER_SCALE_OUT", Actor: map[string]interface{}{}, Params: map[string]interface{}{}, } actual, err := receivers.Create(fake.ServiceClient(), opts).Extract() - if err != nil { - t.Error("Error creating receiver. err=", err) - } else { - createdAt, _ := time.Parse(time.RFC3339, "2015-11-04T05:21:41Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-11-04T05:21:41Z") - expected := receivers.Receiver{ - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{ - "trust_id": []string{ - "6dc6d336e3fc4c0a951b5698cd1236d9", - }, - }, - Channel: map[string]interface{}{ - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", - }, - Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - CreatedAt: createdAt, - Domain: "Default", - ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - Name: "cluster_inflate", - Params: map[string]interface{}{ - "count": "1", - }, - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Type: "webhook", - UpdatedAt: updatedAt, - User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", - } - - th.AssertDeepEquals(t, expected, *actual) - } -} - -func TestCreateReceiverInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": 123456789.0, - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": 123456789.0, - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) - - opts := receivers.CreateOpts{ - Name: "cluster_inflate", - ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - Type: "webhook", - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{}, - Params: map[string]interface{}{}, - } - - _, err := receivers.Create(fake.ServiceClient(), opts).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestCreateReceiverInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "invalid", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": "invalid", - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) - - opts := receivers.CreateOpts{ - Name: "cluster_inflate", - ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - Type: "webhook", - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{}, - Params: map[string]interface{}{}, - } - - _, err := receivers.Create(fake.ServiceClient(), opts).Extract() - th.AssertEquals(t, false, err == nil) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedReceiver, *actual) } func TestGetReceivers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/receivers/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "2015-11-04T05:21:41Z", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": "2016-11-04T05:21:41Z", - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) - - createdAt, _ := time.Parse(time.RFC3339, "2015-11-04T05:21:41Z") - updatedAt, _ := time.Parse(time.RFC3339, "2016-11-04T05:21:41Z") - expected := receivers.Receiver{ - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{ - "trust_id": []string{ - "6dc6d336e3fc4c0a951b5698cd1236d9", - }, - }, - Channel: map[string]interface{}{ - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", - }, - Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - CreatedAt: createdAt, - Domain: "Default", - ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - Name: "cluster_inflate", - Params: map[string]interface{}{ - "count": "1", - }, - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Type: "webhook", - UpdatedAt: updatedAt, - User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", - } + HandleGetSuccessfully(t) actual, err := receivers.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() - if err != nil { - t.Errorf("Failed Get receiver. %v", err) - } else { - th.AssertDeepEquals(t, expected, *actual) - } -} - -func TestGetReceiverInvalidCreatedAtTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/receivers/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": 123456789.0, - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": 123456789.0, - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) - - _, err := receivers.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestGetReceiverInvalidUpdatedAtTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/receivers/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "invalid", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": "invalid", - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) - - _, err := receivers.Get(fake.ServiceClient(), "9e1c6f42-acf5-4688-be2c-8ce954ef0f23").Extract() - th.AssertEquals(t, false, err == nil) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedReceiver, *actual) } func TestUpdateReceiver(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "2015-06-27T05:09:43Z", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": null, - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) - - createdAt, _ := time.Parse(time.RFC3339, "2015-06-27T05:09:43Z") - updatedAt := time.Time{} - expected := receivers.Receiver{ - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{ - "trust_id": []string{ - "6dc6d336e3fc4c0a951b5698cd1236d9", - }, - }, - Channel: map[string]interface{}{ - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", - }, - Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - CreatedAt: createdAt, - Domain: "Default", - ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - Name: "cluster_inflate", - Params: map[string]interface{}{ - "count": "1", - }, - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Type: "webhook", - UpdatedAt: updatedAt, - User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", - } + HandleUpdateSuccessfully(t) opts := receivers.UpdateOpts{ Name: "cluster_inflate", @@ -431,307 +55,39 @@ func TestUpdateReceiver(t *testing.T) { } actual, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, *actual) -} - -func TestUpdateReceiversInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": 123456789.0, - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": 123456789.0, - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) - opts := receivers.UpdateOpts{ - Name: "cluster_inflate", - } - _, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestUpdateReceiverInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": null, - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": "invalid", - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - }`) - }) - opts := receivers.UpdateOpts{ - Name: "cluster_inflate", - } - _, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() - th.AssertEquals(t, false, err == nil) + th.AssertDeepEquals(t, ExpectedUpdateReceiver, *actual) } func TestListReceivers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestFormValues(t, r, map[string]string{"limit": "2", "sort": "name:asc,status:desc"}) - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "receivers": [ - { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "2015-06-27T05:09:43Z", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": null, - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - ] - }`) - }) + HandleListSuccessfully(t) opts := receivers.ListOpts{ Limit: 2, Sort: "name:asc,status:desc", } + count := 0 receivers.List(fake.ServiceClient(), opts).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := receivers.ExtractReceivers(page) - if err != nil { - t.Errorf("Failed to extract receivers: %v", err) - return false, err - } + th.AssertNoErr(t, err) - createdAt, _ := time.Parse(time.RFC3339, "2015-06-27T05:09:43Z") - updatedAt := time.Time{} - expected := []receivers.Receiver{ - { - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{ - "trust_id": []string{ - "6dc6d336e3fc4c0a951b5698cd1236d9", - }, - }, - Channel: map[string]interface{}{ - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", - }, - Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - CreatedAt: createdAt, - Domain: "Default", - ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - Name: "cluster_inflate", - Params: map[string]interface{}{ - "count": "1", - }, - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Type: "webhook", - UpdatedAt: updatedAt, - User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", - }, - } + th.AssertDeepEquals(t, ExpectedReceiversList, actual) - th.AssertDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestListReceiversInvalidTimeFloat(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/receivers", 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, ` - { - "receivers": [ - { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": 123456789.0, - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": 123456789.0, - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - ] - }`) - }) - - count := 0 - err := receivers.List(fake.ServiceClient(), receivers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - return true, nil - }) - - th.AssertEquals(t, false, err == nil) - if count != 0 { - t.Errorf("Expected 0 page of receivers, got %d pages instead", count) - } -} - -func TestListReceiversInvalidTimeString(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v1/receivers", 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, ` - { - "receivers": [ - { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "invalid", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": "invalid", - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - ] - }`) - }) - - count := 0 - err := receivers.List(fake.ServiceClient(), receivers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ return true, nil }) - th.AssertEquals(t, false, err == nil) - if count != 0 { - t.Errorf("Expected 0 page of receivers, got %d pages instead", count) - } + th.AssertEquals(t, count, 1) } func TestDeleteReceiver(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", 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) - }) + HandleDeleteSuccessfully(t) deleteResult := receivers.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) From b9da6487abe3540a9f09a4bc544a2358be4673bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=A5=96=E5=BB=BA?= Date: Thu, 21 Jun 2018 06:30:22 +0800 Subject: [PATCH 0442/2296] Identity V3: add field Filters to policies.ListOpts (#1002) * fix comment of struct StringFieldLengthExceedsLimit * add field Filter to struct policies.ListOpts * fix acceptance testing --- .../openstack/identity/v3/policies_test.go | 48 ++++++++++++++++++- openstack/identity/v3/policies/errors.go | 18 ++++++- openstack/identity/v3/policies/requests.go | 22 +++++++++ .../v3/policies/testing/requests_test.go | 32 +++++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/identity/v3/policies_test.go b/acceptance/openstack/identity/v3/policies_test.go index 61ccb2a3f1..3fb22bb221 100644 --- a/acceptance/openstack/identity/v3/policies_test.go +++ b/acceptance/openstack/identity/v3/policies_test.go @@ -52,7 +52,9 @@ func TestPoliciesCRUD(t *testing.T) { th.AssertEquals(t, policy.Blob, string(createOpts.Blob)) th.AssertEquals(t, policy.Extra["description"], createOpts.Extra["description"]) - allPages, err := policies.List(client, nil).AllPages() + var listOpts policies.ListOpts + + allPages, err := policies.List(client, listOpts).AllPages() th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) @@ -70,6 +72,50 @@ func TestPoliciesCRUD(t *testing.T) { th.AssertEquals(t, true, found) + listOpts.Filters = map[string]string{ + "type__contains": "json", + } + + allPages, err = policies.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err = policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + + found = false + for _, p := range allPolicies { + tools.PrintResource(t, p) + tools.PrintResource(t, p.Extra) + + if p.ID == policy.ID { + found = true + } + } + + th.AssertEquals(t, true, found) + + listOpts.Filters = map[string]string{ + "type__contains": "foobar", + } + + allPages, err = policies.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err = policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + + found = false + for _, p := range allPolicies { + tools.PrintResource(t, p) + tools.PrintResource(t, p.Extra) + + if p.ID == policy.ID { + found = true + } + } + + th.AssertEquals(t, false, found) + gotPolicy, err := policies.Get(client, policy.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, policy, gotPolicy) diff --git a/openstack/identity/v3/policies/errors.go b/openstack/identity/v3/policies/errors.go index 43c477b8e4..27fb4b1cb0 100644 --- a/openstack/identity/v3/policies/errors.go +++ b/openstack/identity/v3/policies/errors.go @@ -2,7 +2,23 @@ package policies import "fmt" -// StringFieldLengthExceedsLimit xxx +// InvalidListFilter is returned by the ToPolicyListQuery method when +// validation of a filter does not pass +type InvalidListFilter struct { + FilterName string +} + +func (e InvalidListFilter) Error() string { + s := fmt.Sprintf( + "Invalid filter name [%s]: it must be in format of TYPE__COMPARATOR", + e.FilterName, + ) + return s +} + +// StringFieldLengthExceedsLimit is returned by the +// ToPolicyCreateMap/ToPolicyUpdateMap methods when validation of +// a type does not pass type StringFieldLengthExceedsLimit struct { Field string Limit int diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index cc727dab33..c9640b2278 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -1,6 +1,9 @@ package policies import ( + "net/url" + "strings" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -18,11 +21,30 @@ type ListOpts struct { // Type filters the response by MIME media type // of the serialized policy blob. Type string `q:"type"` + + // Filters filters the response by custom filters such as + // 'type__contains=foo' + Filters map[string]string `q:"-"` } // ToPolicyListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPolicyListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + for k, v := range opts.Filters { + i := strings.Index(k, "__") + if i > 0 && i < len(k)-2 { + params.Add(k, v) + } else { + return "", InvalidListFilter{FilterName: k} + } + } + + q = &url.URL{RawQuery: params.Encode()} return q.String(), err } diff --git a/openstack/identity/v3/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go index 8c60ba60e5..6348fd57f1 100644 --- a/openstack/identity/v3/policies/testing/requests_test.go +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -57,6 +57,38 @@ func TestListPoliciesWithFilter(t *testing.T) { th.CheckDeepEquals(t, []policies.Policy{SecondPolicy}, actual) } +func TestListPoliciesFiltersCheck(t *testing.T) { + type test struct { + filterName string + wantErr bool + } + tests := []test{ + {"foo__contains", false}, + {"foo", true}, + {"foo_contains", true}, + {"foo__", true}, + {"__foo", true}, + } + + var listOpts policies.ListOpts + for _, _test := range tests { + listOpts.Filters = map[string]string{_test.filterName: "bar"} + _, err := listOpts.ToPolicyListQuery() + + if !_test.wantErr { + th.AssertNoErr(t, err) + } else { + switch _t := err.(type) { + case nil: + t.Fatal("error expected but got a nil") + case policies.InvalidListFilter: + default: + t.Fatalf("unexpected error type: [%T]", _t) + } + } + } +} + func TestCreatePolicy(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 57beb4b85593615e5a3998e89f4b87a8351fb428 Mon Sep 17 00:00:00 2001 From: Kevin Zhao Date: Wed, 20 Jun 2018 16:20:52 +0800 Subject: [PATCH 0443/2296] Add StartAt to Container field Change-Id: Iac046b36cc30153c546de7caa6c1dec3680cd628 Signed-off-by: Kevin Zhao --- openstack/container/v1/capsules/results.go | 7 +++++++ openstack/container/v1/capsules/testing/fixtures.go | 2 ++ openstack/container/v1/capsules/testing/requests_test.go | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index a3661b23b5..38b3ff4f63 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -137,6 +137,9 @@ type Container struct { // The updated time of the container UpdatedAt time.Time `json:"-"` + // The started time of the container + StartedAt time.Time `json:"-"` + // Name for the container Name string `json:"name"` @@ -304,6 +307,7 @@ func (r *Container) UnmarshalJSON(b []byte) error { tmp CreatedAt gophercloud.JSONRFC3339ZNoT `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoT `json:"updated_at"` + StartedAt gophercloud.JSONRFC3339ZNoT `json:"started_at"` } err := json.Unmarshal(b, &s1) @@ -312,6 +316,7 @@ func (r *Container) UnmarshalJSON(b []byte) error { r.CreatedAt = time.Time(s1.CreatedAt) r.UpdatedAt = time.Time(s1.UpdatedAt) + r.StartedAt = time.Time(s1.StartedAt) return nil } @@ -321,6 +326,7 @@ func (r *Container) UnmarshalJSON(b []byte) error { tmp CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` + StartedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"started_at"` } err = json.Unmarshal(b, &s2) @@ -332,6 +338,7 @@ func (r *Container) UnmarshalJSON(b []byte) error { r.CreatedAt = time.Time(s2.CreatedAt) r.UpdatedAt = time.Time(s2.UpdatedAt) + r.StartedAt = time.Time(s2.StartedAt) return nil } diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index e9cc10ff9c..7d8b2eac85 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -236,6 +236,7 @@ const CapsuleGetBody_OldTime = ` "labels": {"foo": "bar"}, "created_at": "2018-01-12 09:37:25+00:00", "updated_at": "2018-01-12 09:37:26+00:00", + "started_at": "2018-01-12 09:37:26+00:00", "workdir": "/root", "disk": 0, "id": 1, @@ -335,6 +336,7 @@ const CapsuleGetBody_NewTime = ` "labels": {"foo": "bar"}, "created_at": "2018-01-12 09:37:25", "updated_at": "2018-01-12 09:37:26", + "started_at": "2018-01-12 09:37:26", "workdir": "/root", "disk": 0, "id": 1, diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 5991e1ea64..805d296bc9 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -19,11 +19,13 @@ func TestGetCapsule_OldTime(t *testing.T) { createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:26+00:00") + startedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:26+00:00") ExpectedCapsule.CreatedAt = createdAt ExpectedCapsule.UpdatedAt = updatedAt ExpectedCapsule.Containers[0].CreatedAt = createdAt ExpectedCapsule.Containers[0].UpdatedAt = updatedAt + ExpectedCapsule.Containers[0].StartedAt = startedAt actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() th.AssertNoErr(t, err) @@ -39,11 +41,13 @@ func TestGetCapsule_NewTime(t *testing.T) { createdAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:25") updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:26") + startedAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:26") ExpectedCapsule.CreatedAt = createdAt ExpectedCapsule.UpdatedAt = updatedAt ExpectedCapsule.Containers[0].CreatedAt = createdAt ExpectedCapsule.Containers[0].UpdatedAt = updatedAt + ExpectedCapsule.Containers[0].StartedAt = startedAt actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() th.AssertNoErr(t, err) From b0946a7e5fd0677315c2463031909b77cad2ca73 Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Tue, 19 Jun 2018 16:01:50 -0700 Subject: [PATCH 0444/2296] Add update to claims --- .../openstack/messaging/v2/claims_test.go | 15 ++++++++ openstack/messaging/v2/claims/doc.go | 14 +++++++ openstack/messaging/v2/claims/requests.go | 38 +++++++++++++++++++ openstack/messaging/v2/claims/results.go | 5 +++ .../messaging/v2/claims/testing/fixtures.go | 22 ++++++++++- .../v2/claims/testing/requests_test.go | 14 +++++++ openstack/messaging/v2/claims/urls.go | 8 +++- 7 files changed, 112 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/messaging/v2/claims_test.go b/acceptance/openstack/messaging/v2/claims_test.go index 9fcc7a6f55..d9c3418c99 100644 --- a/acceptance/openstack/messaging/v2/claims_test.go +++ b/acceptance/openstack/messaging/v2/claims_test.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" ) func TestCRUDClaim(t *testing.T) { @@ -36,7 +37,21 @@ func TestCRUDClaim(t *testing.T) { tools.PrintResource(t, claimedMessages) + updateOpts := claims.UpdateOpts{ + TTL: 600, + Grace: 500, + } + for _, claimID := range claimIDs { + t.Logf("Attempting to update claim: %s", claimID) + updateErr := claims.Update(client, createdQueueName, claimID, updateOpts).ExtractErr() + + if updateErr != nil { + t.Fatalf("Unable to update claim %s: %v", claimID, err) + } else { + t.Logf("Successfully updated claim: %s", claimID) + } + updatedClaim, getErr := GetClaim(t, client, createdQueueName, claimID) if getErr != nil { t.Fatalf("Unable to retrieve claim %s: %v", claimID, getErr) diff --git a/openstack/messaging/v2/claims/doc.go b/openstack/messaging/v2/claims/doc.go index 26579a11ad..5e1b166e81 100644 --- a/openstack/messaging/v2/claims/doc.go +++ b/openstack/messaging/v2/claims/doc.go @@ -20,11 +20,25 @@ Example to Create a Claim on a specified Zaqar queue Example to get a claim for a specified Zaqar queue queueName := "my_queue" + claimID := "123456789012345678" claim, err := claims.Get(messagingClient, queueName, claimID).Extract() if err != nil { panic(err) } +Example to update a claim for a specified Zaqar queue + + updateOpts := claims.UpdateOpts{ + TTL: 600 + Grace: 1200 + } + + queueName := "my_queue" + + err := claims.Update(messagingClient, queueName, claimID, updateOpts).ExtractErr() + if err != nil { + panic(err) + } */ package claims diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index 4d457557a7..8a319bfcd9 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -72,3 +72,41 @@ func Get(client *gophercloud.ServiceClient, queueName string, claimID string) (r }) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToClaimUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts implements UpdateOpts. +type UpdateOpts struct { + // Update the TTL for the specified Claim. + TTL int `json:"ttl,omitempty"` + + // Update the grace period for Messages in a specified Claim. + Grace int `json:"grace,omitempty"` +} + +// ToClaimUpdateMap assembles a request body based on the contents of +// UpdateOpts. +func (opts UpdateOpts) ToClaimUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +// Update will update the options for a specified claim. +func Update(client *gophercloud.ServiceClient, queueName string, claimID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToClaimUpdateMap() + if err != nil { + r.Err = err + return r + } + _, r.Err = client.Patch(updateURL(client, queueName, claimID), &b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/messaging/v2/claims/results.go b/openstack/messaging/v2/claims/results.go index dda91b07ef..d3abe106ab 100644 --- a/openstack/messaging/v2/claims/results.go +++ b/openstack/messaging/v2/claims/results.go @@ -26,6 +26,11 @@ type GetResult struct { gophercloud.Result } +// UpdateResult is the response of a Update operations. +type UpdateResult struct { + gophercloud.ErrResult +} + type Messages struct { Age float32 `json:"age"` Href string `json:"href"` diff --git a/openstack/messaging/v2/claims/testing/fixtures.go b/openstack/messaging/v2/claims/testing/fixtures.go index bcb6bcd7cb..f81bee02be 100644 --- a/openstack/messaging/v2/claims/testing/fixtures.go +++ b/openstack/messaging/v2/claims/testing/fixtures.go @@ -49,8 +49,14 @@ const CreateClaimRequest = ` { "ttl": 3600, "grace": 3600 -} -` +}` + +// UpdateClaimRequest is a sample request to update a claim. +const UpdateClaimRequest = ` +{ + "ttl": 1200, + "grace": 1600 +}` // CreatedClaim is the result of a create request. var CreatedClaim = []claims.Messages{ @@ -102,3 +108,15 @@ func HandleGetSuccessfully(t *testing.T) { fmt.Fprintf(w, GetClaimResponse) }) } + +// HandleUpdateSuccessfully configures the test server to respond to a Update request. +func HandleUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims/%s", QueueName, ClaimID), + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, UpdateClaimRequest) + + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/messaging/v2/claims/testing/requests_test.go b/openstack/messaging/v2/claims/testing/requests_test.go index 141e7755fe..a4cb4cacbe 100644 --- a/openstack/messaging/v2/claims/testing/requests_test.go +++ b/openstack/messaging/v2/claims/testing/requests_test.go @@ -33,3 +33,17 @@ func TestGet(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstClaim, actual) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateSuccessfully(t) + + updateOpts := claims.UpdateOpts{ + Grace: 1600, + TTL: 1200, + } + + err := claims.Update(fake.ServiceClient(), QueueName, ClaimID, updateOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/messaging/v2/claims/urls.go b/openstack/messaging/v2/claims/urls.go index a11394ed94..b0990a1934 100644 --- a/openstack/messaging/v2/claims/urls.go +++ b/openstack/messaging/v2/claims/urls.go @@ -11,6 +11,10 @@ func createURL(client *gophercloud.ServiceClient, queueName string) string { return client.ServiceURL(apiVersion, apiName, queueName, "claims") } -func getURL(client *gophercloud.ServiceClient, queueName string, claimId string) string { - return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimId) +func getURL(client *gophercloud.ServiceClient, queueName string, claimID string) string { + return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimID) +} + +func updateURL(client *gophercloud.ServiceClient, queueName string, claimID string) string { + return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimID) } From 39b2664997891a42f1266326609fbbbbdcfe18b2 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Thu, 21 Jun 2018 10:35:14 +0200 Subject: [PATCH 0445/2296] Do not fetch twice the first page --- pagination/pager.go | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/pagination/pager.go b/pagination/pager.go index 7c65926b72..42c0b2dbe5 100644 --- a/pagination/pager.go +++ b/pagination/pager.go @@ -41,6 +41,8 @@ type Pager struct { createPage func(r PageResult) Page + firstPage Page + Err error // Headers supplies additional HTTP headers to populate on each paged request. @@ -89,9 +91,18 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { } currentURL := p.initialURL for { - currentPage, err := p.fetchNextPage(currentURL) - if err != nil { - return err + var currentPage Page + + // if first page has already been fetched, no need to fetch it again + if p.firstPage != nil { + currentPage = p.firstPage + p.firstPage = nil + } else { + var err error + currentPage, err = p.fetchNextPage(currentURL) + if err != nil { + return err + } } empty, err := currentPage.IsEmpty() @@ -128,23 +139,26 @@ func (p Pager) AllPages() (Page, error) { // body will contain the final concatenated Page body. var body reflect.Value - // Grab a test page to ascertain the page body type. - testPage, err := p.fetchNextPage(p.initialURL) + // Grab a first page to ascertain the page body type. + firstPage, err := p.fetchNextPage(p.initialURL) if err != nil { return nil, err } // Store the page type so we can use reflection to create a new mega-page of // that type. - pageType := reflect.TypeOf(testPage) + pageType := reflect.TypeOf(firstPage) - // if it's a single page, just return the testPage (first page) + // if it's a single page, just return the firstPage (first page) if _, found := pageType.FieldByName("SinglePageBase"); found { - return testPage, nil + return firstPage, nil } + // store the first page to avoid getting it twice + p.firstPage = firstPage + // Switch on the page body type. Recognized types are `map[string]interface{}`, // `[]byte`, and `[]interface{}`. - switch pb := testPage.GetBody().(type) { + switch pb := firstPage.GetBody().(type) { case map[string]interface{}: // key is the map key for the page body if the body type is `map[string]interface{}`. var key string From 1ad7a96d69a2e8105920431ee428843b6d1ee4c3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 22 Jun 2018 21:31:57 -0600 Subject: [PATCH 0446/2296] Key Manager v1 Support (#1059) * Key Manager v1: Adding clients * Key Manager v1: Secrets * Key Manager v1: Secrets Metadata * Key Manager v1: Containers * Key Manager v1: Adding GetPayloadOpts to Secrets * Key Manager v1: Orders * Key Manager v1: Container Consumers * Key Manager v1: ACLs --- acceptance/clients/clients.go | 21 + .../openstack/keymanager/v1/acls_test.go | 103 +++ .../keymanager/v1/containers_test.go | 188 +++++ .../openstack/keymanager/v1/keymanager.go | 713 ++++++++++++++++++ .../openstack/keymanager/v1/orders_test.go | 84 +++ .../openstack/keymanager/v1/secrets_test.go | 242 ++++++ openstack/client.go | 8 + openstack/keymanager/v1/acls/doc.go | 54 ++ openstack/keymanager/v1/acls/requests.go | 112 +++ openstack/keymanager/v1/acls/results.go | 85 +++ .../keymanager/v1/acls/testing/fixtures.go | 168 +++++ .../v1/acls/testing/requests_test.go | 115 +++ openstack/keymanager/v1/acls/urls.go | 11 + openstack/keymanager/v1/containers/doc.go | 86 +++ .../keymanager/v1/containers/requests.go | 212 ++++++ openstack/keymanager/v1/containers/results.go | 232 ++++++ .../v1/containers/testing/fixtures.go | 321 ++++++++ .../v1/containers/testing/requests_test.go | 144 ++++ openstack/keymanager/v1/containers/urls.go | 31 + openstack/keymanager/v1/orders/doc.go | 45 ++ openstack/keymanager/v1/orders/requests.go | 129 ++++ openstack/keymanager/v1/orders/results.go | 170 +++++ .../keymanager/v1/orders/testing/fixtures.go | 186 +++++ .../v1/orders/testing/requests_test.go | 81 ++ openstack/keymanager/v1/orders/urls.go | 19 + openstack/keymanager/v1/secrets/doc.go | 142 ++++ openstack/keymanager/v1/secrets/requests.go | 412 ++++++++++ openstack/keymanager/v1/secrets/results.go | 227 ++++++ .../keymanager/v1/secrets/testing/fixtures.go | 355 +++++++++ .../v1/secrets/testing/requests_test.go | 179 +++++ openstack/keymanager/v1/secrets/urls.go | 35 + 31 files changed, 4910 insertions(+) create mode 100644 acceptance/openstack/keymanager/v1/acls_test.go create mode 100644 acceptance/openstack/keymanager/v1/containers_test.go create mode 100644 acceptance/openstack/keymanager/v1/keymanager.go create mode 100644 acceptance/openstack/keymanager/v1/orders_test.go create mode 100644 acceptance/openstack/keymanager/v1/secrets_test.go create mode 100644 openstack/keymanager/v1/acls/doc.go create mode 100644 openstack/keymanager/v1/acls/requests.go create mode 100644 openstack/keymanager/v1/acls/results.go create mode 100644 openstack/keymanager/v1/acls/testing/fixtures.go create mode 100644 openstack/keymanager/v1/acls/testing/requests_test.go create mode 100644 openstack/keymanager/v1/acls/urls.go create mode 100644 openstack/keymanager/v1/containers/doc.go create mode 100644 openstack/keymanager/v1/containers/requests.go create mode 100644 openstack/keymanager/v1/containers/results.go create mode 100644 openstack/keymanager/v1/containers/testing/fixtures.go create mode 100644 openstack/keymanager/v1/containers/testing/requests_test.go create mode 100644 openstack/keymanager/v1/containers/urls.go create mode 100644 openstack/keymanager/v1/orders/doc.go create mode 100644 openstack/keymanager/v1/orders/requests.go create mode 100644 openstack/keymanager/v1/orders/results.go create mode 100644 openstack/keymanager/v1/orders/testing/fixtures.go create mode 100644 openstack/keymanager/v1/orders/testing/requests_test.go create mode 100644 openstack/keymanager/v1/orders/urls.go create mode 100644 openstack/keymanager/v1/secrets/doc.go create mode 100644 openstack/keymanager/v1/secrets/requests.go create mode 100644 openstack/keymanager/v1/secrets/results.go create mode 100644 openstack/keymanager/v1/secrets/testing/fixtures.go create mode 100644 openstack/keymanager/v1/secrets/testing/requests_test.go create mode 100644 openstack/keymanager/v1/secrets/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 155a3c6744..c04de120d4 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -542,6 +542,27 @@ func NewContainerV1Client() (*gophercloud.ServiceClient, error) { }) } +// NewKeyManagerV1Client returns a *ServiceClient for making calls +// to the OpenStack Key Manager (Barbican) v1 API. An error will be +// returned if authentication or client creation was not possible. +func NewKeyManagerV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewKeyManagerV1(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} + // configureDebug will configure the provider client to print the API // requests and responses if OS_DEBUG is enabled. func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderClient { diff --git a/acceptance/openstack/keymanager/v1/acls_test.go b/acceptance/openstack/keymanager/v1/acls_test.go new file mode 100644 index 0000000000..54a123a2b2 --- /dev/null +++ b/acceptance/openstack/keymanager/v1/acls_test.go @@ -0,0 +1,103 @@ +// +build acceptance keymanager acls + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestACLCRUD(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + payload := tools.RandomString("SUPERSECRET-", 8) + secret, err := CreateSecretWithPayload(t, client, payload) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + user := tools.RandomString("", 32) + users := []string{user} + iFalse := false + setOpts := acls.SetOpts{ + Type: "read", + Users: &users, + ProjectAccess: &iFalse, + } + + aclRef, err := acls.SetSecretACL(client, secretID, setOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, aclRef) + defer func() { + err := acls.DeleteSecretACL(client, secretID).ExtractErr() + th.AssertNoErr(t, err) + acl, err := acls.GetSecretACL(client, secretID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, acl) + }() + + acl, err := acls.GetSecretACL(client, secretID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, acl) + tools.PrintResource(t, (*acl)["read"].Created) + th.AssertEquals(t, len((*acl)["read"].Users), 1) + th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) + + newUsers := []string{} + updateOpts := acls.SetOpts{ + Type: "read", + Users: &newUsers, + } + + aclRef, err = acls.UpdateSecretACL(client, secretID, updateOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, aclRef) + + acl, err = acls.GetSecretACL(client, secretID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, acl) + tools.PrintResource(t, (*acl)["read"].Created) + th.AssertEquals(t, len((*acl)["read"].Users), 0) + th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) + + container, err := CreateGenericContainer(t, client, secret) + th.AssertNoErr(t, err) + containerID, err := ParseID(container.ContainerRef) + th.AssertNoErr(t, err) + defer DeleteContainer(t, client, containerID) + + aclRef, err = acls.SetContainerACL(client, containerID, setOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, aclRef) + defer func() { + err := acls.DeleteContainerACL(client, containerID).ExtractErr() + th.AssertNoErr(t, err) + acl, err := acls.GetContainerACL(client, containerID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, acl) + }() + + acl, err = acls.GetContainerACL(client, containerID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, acl) + tools.PrintResource(t, (*acl)["read"].Created) + th.AssertEquals(t, len((*acl)["read"].Users), 1) + th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) + + aclRef, err = acls.UpdateContainerACL(client, containerID, updateOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, aclRef) + + acl, err = acls.GetContainerACL(client, containerID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, acl) + tools.PrintResource(t, (*acl)["read"].Created) + th.AssertEquals(t, len((*acl)["read"].Users), 0) + th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) +} diff --git a/acceptance/openstack/keymanager/v1/containers_test.go b/acceptance/openstack/keymanager/v1/containers_test.go new file mode 100644 index 0000000000..2b5f4d01fe --- /dev/null +++ b/acceptance/openstack/keymanager/v1/containers_test.go @@ -0,0 +1,188 @@ +// +build acceptance keymanager containers + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestGenericContainersCRUD(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + payload := tools.RandomString("SUPERSECRET-", 8) + secret, err := CreateSecretWithPayload(t, client, payload) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + container, err := CreateGenericContainer(t, client, secret) + th.AssertNoErr(t, err) + containerID, err := ParseID(container.ContainerRef) + th.AssertNoErr(t, err) + defer DeleteContainer(t, client, containerID) + + allPages, err := containers.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allContainers, err := containers.ExtractContainers(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allContainers { + if v.ContainerRef == container.ContainerRef { + found = true + } + } + + th.AssertEquals(t, found, true) +} + +func TestCertificateContainer(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + pass := tools.RandomString("", 16) + priv, cert, err := CreateCertificate(t, pass) + th.AssertNoErr(t, err) + + private, err := CreatePrivateSecret(t, client, priv) + th.AssertNoErr(t, err) + secretID, err := ParseID(private.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + t.Logf("Private Payload: %s", string(payload)) + + certificate, err := CreateCertificateSecret(t, client, cert) + th.AssertNoErr(t, err) + secretID, err = ParseID(certificate.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err = secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + t.Logf("Certificate Payload: %s", string(payload)) + + passphrase, err := CreatePassphraseSecret(t, client, pass) + th.AssertNoErr(t, err) + secretID, err = ParseID(passphrase.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err = secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + t.Logf("Passphrase Payload: %s", string(payload)) + + container, err := CreateCertificateContainer(t, client, passphrase, private, certificate) + th.AssertNoErr(t, err) + containerID, err := ParseID(container.ContainerRef) + defer DeleteContainer(t, client, containerID) +} + +func TestRSAContainer(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + pass := tools.RandomString("", 16) + priv, pub, err := CreateRSAKeyPair(t, pass) + th.AssertNoErr(t, err) + + private, err := CreatePrivateSecret(t, client, priv) + th.AssertNoErr(t, err) + secretID, err := ParseID(private.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + t.Logf("Private Payload: %s", string(payload)) + + public, err := CreatePublicSecret(t, client, pub) + th.AssertNoErr(t, err) + secretID, err = ParseID(public.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err = secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + t.Logf("Public Payload: %s", string(payload)) + + passphrase, err := CreatePassphraseSecret(t, client, pass) + th.AssertNoErr(t, err) + secretID, err = ParseID(passphrase.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err = secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + t.Logf("Passphrase Payload: %s", string(payload)) + + container, err := CreateRSAContainer(t, client, passphrase, private, public) + th.AssertNoErr(t, err) + containerID, err := ParseID(container.ContainerRef) + defer DeleteContainer(t, client, containerID) +} + +func TestContainerConsumersCRUD(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + payload := tools.RandomString("SUPERSECRET-", 8) + secret, err := CreateSecretWithPayload(t, client, payload) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + container, err := CreateGenericContainer(t, client, secret) + th.AssertNoErr(t, err) + containerID, err := ParseID(container.ContainerRef) + th.AssertNoErr(t, err) + defer DeleteContainer(t, client, containerID) + + consumerName := tools.RandomString("CONSUMER-", 8) + consumerCreateOpts := containers.CreateConsumerOpts{ + Name: consumerName, + URL: "http://example.com", + } + + container, err = containers.CreateConsumer(client, containerID, consumerCreateOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, container.Consumers) + th.AssertEquals(t, len(container.Consumers), 1) + defer func() { + deleteOpts := containers.DeleteConsumerOpts{ + Name: consumerName, + URL: "http://example.com", + } + + container, err := containers.DeleteConsumer(client, containerID, deleteOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, len(container.Consumers), 0) + }() + + allPages, err := containers.ListConsumers(client, containerID, nil).AllPages() + th.AssertNoErr(t, err) + + allConsumers, err := containers.ExtractConsumers(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allConsumers { + if v.Name == consumerName { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/acceptance/openstack/keymanager/v1/keymanager.go b/acceptance/openstack/keymanager/v1/keymanager.go new file mode 100644 index 0000000000..99a85f9687 --- /dev/null +++ b/acceptance/openstack/keymanager/v1/keymanager.go @@ -0,0 +1,713 @@ +package v1 + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/base64" + "encoding/pem" + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/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" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// CreateAsymmetric Order will create a random asymmetric order. +// An error will be returned if the order could not be created. +func CreateAsymmetricOrder(t *testing.T, client *gophercloud.ServiceClient) (*orders.Order, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create order %s", name) + + expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) + createOpts := orders.CreateOpts{ + Type: orders.AsymmetricOrder, + Meta: orders.MetaOpts{ + Name: name, + Algorithm: "rsa", + BitLength: 2048, + Mode: "cbc", + Expiration: &expiration, + }, + } + + order, err := orders.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + orderID, err := ParseID(order.OrderRef) + if err != nil { + return nil, err + } + + err = WaitForOrder(client, orderID) + th.AssertNoErr(t, err) + + order, err = orders.Get(client, orderID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, order) + tools.PrintResource(t, order.Meta.Expiration) + + th.AssertEquals(t, order.Meta.Name, name) + th.AssertEquals(t, order.Type, "asymmetric") + + return order, nil +} + +// CreateCertificateContainer will create a random certificate container. +// An error will be returned if the container could not be created. +func CreateCertificateContainer(t *testing.T, client *gophercloud.ServiceClient, passphrase, private, certificate *secrets.Secret) (*containers.Container, error) { + containerName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create container %s", containerName) + + createOpts := containers.CreateOpts{ + Type: containers.CertificateContainer, + Name: containerName, + SecretRefs: []containers.SecretRef{ + { + Name: "certificate", + SecretRef: certificate.SecretRef, + }, + { + Name: "private_key", + SecretRef: private.SecretRef, + }, + { + Name: "private_key_passphrase", + SecretRef: passphrase.SecretRef, + }, + }, + } + + container, err := containers.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created container: %s", container.ContainerRef) + + containerID, err := ParseID(container.ContainerRef) + if err != nil { + return nil, err + } + + container, err = containers.Get(client, containerID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, container) + + th.AssertEquals(t, container.Name, containerName) + th.AssertEquals(t, container.Type, "certificate") + + return container, nil +} + +// CreateKeyOrder will create a random key order. +// An error will be returned if the order could not be created. +func CreateKeyOrder(t *testing.T, client *gophercloud.ServiceClient) (*orders.Order, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create order %s", name) + + expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) + createOpts := orders.CreateOpts{ + Type: orders.KeyOrder, + Meta: orders.MetaOpts{ + Name: name, + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + Expiration: &expiration, + }, + } + + order, err := orders.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + orderID, err := ParseID(order.OrderRef) + if err != nil { + return nil, err + } + + order, err = orders.Get(client, orderID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, order) + tools.PrintResource(t, order.Meta.Expiration) + + th.AssertEquals(t, order.Meta.Name, name) + th.AssertEquals(t, order.Type, "key") + + return order, nil +} + +// CreateRSAContainer will create a random RSA container. +// An error will be returned if the container could not be created. +func CreateRSAContainer(t *testing.T, client *gophercloud.ServiceClient, passphrase, private, public *secrets.Secret) (*containers.Container, error) { + containerName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create container %s", containerName) + + createOpts := containers.CreateOpts{ + Type: containers.RSAContainer, + Name: containerName, + SecretRefs: []containers.SecretRef{ + { + Name: "public_key", + SecretRef: public.SecretRef, + }, + { + Name: "private_key", + SecretRef: private.SecretRef, + }, + { + Name: "private_key_passphrase", + SecretRef: passphrase.SecretRef, + }, + }, + } + + container, err := containers.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created container: %s", container.ContainerRef) + + containerID, err := ParseID(container.ContainerRef) + if err != nil { + return nil, err + } + + container, err = containers.Get(client, containerID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, container) + + th.AssertEquals(t, container.Name, containerName) + th.AssertEquals(t, container.Type, "rsa") + + return container, nil +} + +// CreateCertificateSecret will create a random certificate secret. An error +// will be returned if the secret could not be created. +func CreateCertificateSecret(t *testing.T, client *gophercloud.ServiceClient, cert []byte) (*secrets.Secret, error) { + b64Cert := base64.StdEncoding.EncodeToString(cert) + + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create public key %s", name) + + createOpts := secrets.CreateOpts{ + Name: name, + SecretType: secrets.CertificateSecret, + Payload: b64Cert, + PayloadContentType: "application/octet-stream", + PayloadContentEncoding: "base64", + Algorithm: "rsa", + } + + secret, err := secrets.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created secret: %s", secret.SecretRef) + + secretID, err := ParseID(secret.SecretRef) + if err != nil { + return nil, err + } + + secret, err = secrets.Get(client, secretID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, secret) + + th.AssertEquals(t, secret.Name, name) + th.AssertEquals(t, secret.Algorithm, "rsa") + + return secret, nil +} + +// CreateEmptySecret will create a random secret with no payload. An error will +// be returned if the secret could not be created. +func CreateEmptySecret(t *testing.T, client *gophercloud.ServiceClient) (*secrets.Secret, error) { + secretName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create secret %s", secretName) + + createOpts := secrets.CreateOpts{ + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + Name: secretName, + SecretType: secrets.OpaqueSecret, + } + + secret, err := secrets.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created secret: %s", secret.SecretRef) + + secretID, err := ParseID(secret.SecretRef) + if err != nil { + return nil, err + } + + secret, err = secrets.Get(client, secretID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, secret) + + th.AssertEquals(t, secret.Name, secretName) + th.AssertEquals(t, secret.Algorithm, "aes") + + return secret, nil +} + +// CreateGenericContainer will create a random generic container with a +// specified secret. An error will be returned if the container could not +// be created. +func CreateGenericContainer(t *testing.T, client *gophercloud.ServiceClient, secret *secrets.Secret) (*containers.Container, error) { + containerName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create container %s", containerName) + + createOpts := containers.CreateOpts{ + Type: containers.GenericContainer, + Name: containerName, + SecretRefs: []containers.SecretRef{ + { + Name: secret.Name, + SecretRef: secret.SecretRef, + }, + }, + } + + container, err := containers.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created container: %s", container.ContainerRef) + + containerID, err := ParseID(container.ContainerRef) + if err != nil { + return nil, err + } + + container, err = containers.Get(client, containerID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, container) + + th.AssertEquals(t, container.Name, containerName) + th.AssertEquals(t, container.Type, "generic") + + return container, nil +} + +// CreatePassphraseSecret will create a random passphrase secret. +// An error will be returned if the secret could not be created. +func CreatePassphraseSecret(t *testing.T, client *gophercloud.ServiceClient, passphrase string) (*secrets.Secret, error) { + secretName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create secret %s", secretName) + + createOpts := secrets.CreateOpts{ + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + Name: secretName, + Payload: passphrase, + PayloadContentType: "text/plain", + SecretType: secrets.PassphraseSecret, + } + + secret, err := secrets.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created secret: %s", secret.SecretRef) + + secretID, err := ParseID(secret.SecretRef) + if err != nil { + return nil, err + } + + secret, err = secrets.Get(client, secretID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, secret) + + th.AssertEquals(t, secret.Name, secretName) + th.AssertEquals(t, secret.Algorithm, "aes") + + return secret, nil +} + +// CreatePublicSecret will create a random public secret. An error +// will be returned if the secret could not be created. +func CreatePublicSecret(t *testing.T, client *gophercloud.ServiceClient, pub []byte) (*secrets.Secret, error) { + b64Cert := base64.StdEncoding.EncodeToString(pub) + + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create public key %s", name) + + createOpts := secrets.CreateOpts{ + Name: name, + SecretType: secrets.PublicSecret, + Payload: b64Cert, + PayloadContentType: "application/octet-stream", + PayloadContentEncoding: "base64", + Algorithm: "rsa", + } + + secret, err := secrets.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created secret: %s", secret.SecretRef) + + secretID, err := ParseID(secret.SecretRef) + if err != nil { + return nil, err + } + + secret, err = secrets.Get(client, secretID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, secret) + + th.AssertEquals(t, secret.Name, name) + th.AssertEquals(t, secret.Algorithm, "rsa") + + return secret, nil +} + +// CreatePrivateSecret will create a random private secret. An error +// will be returned if the secret could not be created. +func CreatePrivateSecret(t *testing.T, client *gophercloud.ServiceClient, priv []byte) (*secrets.Secret, error) { + b64Cert := base64.StdEncoding.EncodeToString(priv) + + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create public key %s", name) + + createOpts := secrets.CreateOpts{ + Name: name, + SecretType: secrets.PrivateSecret, + Payload: b64Cert, + PayloadContentType: "application/octet-stream", + PayloadContentEncoding: "base64", + Algorithm: "rsa", + } + + secret, err := secrets.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created secret: %s", secret.SecretRef) + + secretID, err := ParseID(secret.SecretRef) + if err != nil { + return nil, err + } + + secret, err = secrets.Get(client, secretID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, secret) + + th.AssertEquals(t, secret.Name, name) + th.AssertEquals(t, secret.Algorithm, "rsa") + + return secret, nil +} + +// CreateSecretWithPayload will create a random secret with a given payload. +// An error will be returned if the secret could not be created. +func CreateSecretWithPayload(t *testing.T, client *gophercloud.ServiceClient, payload string) (*secrets.Secret, error) { + secretName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create secret %s", secretName) + + createOpts := secrets.CreateOpts{ + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + Name: secretName, + Payload: payload, + PayloadContentType: "text/plain", + SecretType: secrets.OpaqueSecret, + } + + secret, err := secrets.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created secret: %s", secret.SecretRef) + + secretID, err := ParseID(secret.SecretRef) + if err != nil { + return nil, err + } + + secret, err = secrets.Get(client, secretID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, secret) + + th.AssertEquals(t, secret.Name, secretName) + th.AssertEquals(t, secret.Algorithm, "aes") + + return secret, nil +} + +// CreateSymmetricSecret will create a random symmetric secret. An error +// will be returned if the secret could not be created. +func CreateSymmetricSecret(t *testing.T, client *gophercloud.ServiceClient) (*secrets.Secret, error) { + name := tools.RandomString("TESTACC-", 8) + key := tools.RandomString("", 256) + b64Key := base64.StdEncoding.EncodeToString([]byte(key)) + + t.Logf("Attempting to create symmetric key %s", name) + + createOpts := secrets.CreateOpts{ + Name: name, + SecretType: secrets.SymmetricSecret, + Payload: b64Key, + PayloadContentType: "application/octet-stream", + PayloadContentEncoding: "base64", + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + } + + secret, err := secrets.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created secret: %s", secret.SecretRef) + + secretID, err := ParseID(secret.SecretRef) + if err != nil { + return nil, err + } + + secret, err = secrets.Get(client, secretID).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, secret) + + th.AssertEquals(t, secret.Name, name) + th.AssertEquals(t, secret.Algorithm, "aes") + + return secret, nil +} + +// DeleteContainer will delete a container. A fatal error will occur if the +// container could not be deleted. This works best when used as a deferred +// function. +func DeleteContainer(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete container %s", id) + + err := containers.Delete(client, id).ExtractErr() + if err != nil { + t.Fatalf("Could not delete container: %s", err) + } + + t.Logf("Successfully deleted container %s", id) +} + +// DeleteOrder will delete an order. A fatal error will occur if the +// order could not be deleted. This works best when used as a deferred +// function. +func DeleteOrder(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete order %s", id) + + err := orders.Delete(client, id).ExtractErr() + if err != nil { + t.Fatalf("Could not delete order: %s", err) + } + + t.Logf("Successfully deleted order %s", id) +} + +// DeleteSecret will delete a secret. A fatal error will occur if the secret +// could not be deleted. This works best when used as a deferred function. +func DeleteSecret(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete secret %s", id) + + err := secrets.Delete(client, id).ExtractErr() + if err != nil { + t.Fatalf("Could not delete secret: %s", err) + } + + t.Logf("Successfully deleted secret %s", id) +} + +func ParseID(ref string) (string, error) { + parts := strings.Split(ref, "/") + if len(parts) < 2 { + return "", fmt.Errorf("Could not parse %s", ref) + } + + return parts[len(parts)-1], nil +} + +// CreateCertificate will create a random certificate. A fatal error will +// be returned if creation failed. +// https://golang.org/src/crypto/tls/generate_cert.go +func CreateCertificate(t *testing.T, passphrase string) ([]byte, []byte, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + + block := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + } + + if passphrase != "" { + block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(passphrase), x509.PEMCipherAES256) + if err != nil { + return nil, nil, err + } + } + + keyPem := pem.EncodeToMemory(block) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return nil, nil, err + } + + tpl := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Some Org"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(5, 0, 0), + BasicConstraintsValid: true, + } + + cert, err := x509.CreateCertificate(rand.Reader, &tpl, &tpl, &key.PublicKey, key) + if err != nil { + return nil, nil, err + } + + certPem := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: cert, + }) + + return keyPem, certPem, nil +} + +// CreateRSAKeyPair will create a random RSA key pair. An error will be +// returned if the pair could not be created. +func CreateRSAKeyPair(t *testing.T, passphrase string) ([]byte, []byte, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + + block := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + } + + if passphrase != "" { + block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(passphrase), x509.PEMCipherAES256) + if err != nil { + return nil, nil, err + } + } + + keyPem := pem.EncodeToMemory(block) + + asn1Bytes, err := asn1.Marshal(key.PublicKey) + if err != nil { + return nil, nil, err + } + + block = &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: asn1Bytes, + } + + pubPem := pem.EncodeToMemory(block) + + return keyPem, pubPem, nil +} + +func WaitForOrder(client *gophercloud.ServiceClient, orderID string) error { + return tools.WaitFor(func() (bool, error) { + order, err := orders.Get(client, orderID).Extract() + if err != nil { + return false, err + } + + if order.SecretRef != "" { + return true, nil + } + + if order.ContainerRef != "" { + return true, nil + } + + if order.Status == "ERROR" { + return false, fmt.Errorf("Order %s in ERROR state", orderID) + } + + return false, nil + }) +} diff --git a/acceptance/openstack/keymanager/v1/orders_test.go b/acceptance/openstack/keymanager/v1/orders_test.go new file mode 100644 index 0000000000..de44debe91 --- /dev/null +++ b/acceptance/openstack/keymanager/v1/orders_test.go @@ -0,0 +1,84 @@ +// +build acceptance keymanager orders + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/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" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestOrdersCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + order, err := CreateKeyOrder(t, client) + th.AssertNoErr(t, err) + orderID, err := ParseID(order.OrderRef) + th.AssertNoErr(t, err) + defer DeleteOrder(t, client, orderID) + + secretID, err := ParseID(order.SecretRef) + th.AssertNoErr(t, err) + + payloadOpts := secrets.GetPayloadOpts{ + PayloadContentType: "application/octet-stream", + } + payload, err := secrets.GetPayload(client, secretID, payloadOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, payload) + + allPages, err := orders.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allOrders, err := orders.ExtractOrders(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allOrders { + if v.OrderRef == order.OrderRef { + found = true + } + } + + th.AssertEquals(t, found, true) +} + +func TestOrdersAsymmetric(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + order, err := CreateAsymmetricOrder(t, client) + th.AssertNoErr(t, err) + orderID, err := ParseID(order.OrderRef) + th.AssertNoErr(t, err) + defer DeleteOrder(t, client, orderID) + + containerID, err := ParseID(order.ContainerRef) + th.AssertNoErr(t, err) + + container, err := containers.Get(client, containerID).Extract() + th.AssertNoErr(t, err) + + for _, v := range container.SecretRefs { + secretID, err := ParseID(v.SecretRef) + th.AssertNoErr(t, err) + + payloadOpts := secrets.GetPayloadOpts{ + PayloadContentType: "application/octet-stream", + } + + payload, err := secrets.GetPayload(client, secretID, payloadOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, string(payload)) + } +} diff --git a/acceptance/openstack/keymanager/v1/secrets_test.go b/acceptance/openstack/keymanager/v1/secrets_test.go new file mode 100644 index 0000000000..dd00e569ad --- /dev/null +++ b/acceptance/openstack/keymanager/v1/secrets_test.go @@ -0,0 +1,242 @@ +// +build acceptance keymanager secrets + +package v1 + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestSecretsCRUD(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + payload := tools.RandomString("SUPERSECRET-", 8) + secret, err := CreateSecretWithPayload(t, client, payload) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + // Test payload retrieval + actual, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, payload, string(actual)) + + // Test listing secrets + createdQuery := &secrets.DateQuery{ + Date: time.Date(2049, 6, 7, 1, 2, 3, 0, time.UTC), + Filter: secrets.DateFilterLT, + } + + listOpts := secrets.ListOpts{ + CreatedQuery: createdQuery, + } + + allPages, err := secrets.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allSecrets, err := secrets.ExtractSecrets(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allSecrets { + if v.SecretRef == secret.SecretRef { + found = true + } + } + + th.AssertEquals(t, found, true) +} + +func TestSecretsDelayedPayload(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + secret, err := CreateEmptySecret(t, client) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload := tools.RandomString("SUPERSECRET-", 8) + updateOpts := secrets.UpdateOpts{ + ContentType: "text/plain", + Payload: payload, + } + + err = secrets.Update(client, secretID, updateOpts).ExtractErr() + th.AssertNoErr(t, err) + + // Test payload retrieval + actual, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, payload, string(actual)) +} + +func TestSecretsMetadataCRUD(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + payload := tools.RandomString("SUPERSECRET-", 8) + secret, err := CreateSecretWithPayload(t, client, payload) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + // Create some metadata + createOpts := secrets.MetadataOpts{ + "foo": "bar", + "something": "something else", + } + + ref, err := secrets.CreateMetadata(client, secretID, createOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, ref["metadata_ref"], secret.SecretRef+"/metadata") + + // Get the metadata + metadata, err := secrets.GetMetadata(client, secretID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, metadata) + th.AssertEquals(t, metadata["foo"], "bar") + th.AssertEquals(t, metadata["something"], "something else") + + // Add a single metadatum + metadatumOpts := secrets.MetadatumOpts{ + Key: "bar", + Value: "baz", + } + + err = secrets.CreateMetadatum(client, secretID, metadatumOpts).ExtractErr() + th.AssertNoErr(t, err) + + metadata, err = secrets.GetMetadata(client, secretID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, metadata) + th.AssertEquals(t, len(metadata), 3) + th.AssertEquals(t, metadata["foo"], "bar") + th.AssertEquals(t, metadata["something"], "something else") + th.AssertEquals(t, metadata["bar"], "baz") + + // Update a metadatum + metadatumOpts.Key = "foo" + metadatumOpts.Value = "foo" + + metadatum, err := secrets.UpdateMetadatum(client, secretID, metadatumOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, metadatum) + th.AssertDeepEquals(t, metadatum.Key, "foo") + th.AssertDeepEquals(t, metadatum.Value, "foo") + + metadata, err = secrets.GetMetadata(client, secretID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, metadata) + th.AssertEquals(t, len(metadata), 3) + th.AssertEquals(t, metadata["foo"], "foo") + th.AssertEquals(t, metadata["something"], "something else") + th.AssertEquals(t, metadata["bar"], "baz") + + // Delete a metadatum + err = secrets.DeleteMetadatum(client, secretID, "foo").ExtractErr() + th.AssertNoErr(t, err) + + metadata, err = secrets.GetMetadata(client, secretID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, metadata) + th.AssertEquals(t, len(metadata), 2) + th.AssertEquals(t, metadata["something"], "something else") + th.AssertEquals(t, metadata["bar"], "baz") +} + +func TestSymmetricSecret(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + secret, err := CreateSymmetricSecret(t, client) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, string(payload)) +} + +func TestCertificateSecret(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + pass := tools.RandomString("", 16) + cert, _, err := CreateCertificate(t, pass) + th.AssertNoErr(t, err) + + secret, err := CreateCertificateSecret(t, client, cert) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, string(payload)) +} + +func TestPrivateSecret(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + pass := tools.RandomString("", 16) + priv, _, err := CreateCertificate(t, pass) + th.AssertNoErr(t, err) + + secret, err := CreatePrivateSecret(t, client, priv) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, string(payload)) +} + +func TestPublicSecret(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + _, pub, err := CreateRSAKeyPair(t, "") + th.AssertNoErr(t, err) + + secret, err := CreatePublicSecret(t, client, pub) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, string(payload)) +} + +func TestPassphraseSecret(t *testing.T) { + client, err := clients.NewKeyManagerV1Client() + th.AssertNoErr(t, err) + + pass := tools.RandomString("", 16) + secret, err := CreatePassphraseSecret(t, client, pass) + th.AssertNoErr(t, err) + secretID, err := ParseID(secret.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID) + + payload, err := secrets.GetPayload(client, secretID, nil).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, string(payload)) +} diff --git a/openstack/client.go b/openstack/client.go index e554b7bc37..4c8c83ae8f 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -406,3 +406,11 @@ func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo goph func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "container") } + +// NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key +// manager service. +func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(client, eo, "key-manager") + sc.ResourceBase = sc.Endpoint + "v1/" + return sc, err +} diff --git a/openstack/keymanager/v1/acls/doc.go b/openstack/keymanager/v1/acls/doc.go new file mode 100644 index 0000000000..d61940cb08 --- /dev/null +++ b/openstack/keymanager/v1/acls/doc.go @@ -0,0 +1,54 @@ +/* +Package acls manages acls in the OpenStack Key Manager Service. + +All functions have a Secret and Container equivalent. + +Example to Get a Secret's ACL + + acl, err := acls.GetSecretACL(client, secretID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", acl) + +Example to Set a Secret's ACL + + users := []string{"uuid", "uuid"} + iFalse := false + setOpts := acls.SetOpts{ + Type: "read", + users: &users, + ProjectAccess: &iFalse, + } + + aclRef, err := acls.SetSecretACL(client, secretID, setOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", aclRef) + +Example to Update a Secret's ACL + + users := []string{} + setOpts := acls.SetOpts{ + Type: "read", + users: &users, + } + + aclRef, err := acls.UpdateSecretACL(client, secretID, setOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", aclRef) + +Example to Delete a Secret's ACL + + err := acls.DeleteSecretACL(client, secretID).ExtractErr() + if err != nil { + panci(err) + } +*/ +package acls diff --git a/openstack/keymanager/v1/acls/requests.go b/openstack/keymanager/v1/acls/requests.go new file mode 100644 index 0000000000..b83c56075d --- /dev/null +++ b/openstack/keymanager/v1/acls/requests.go @@ -0,0 +1,112 @@ +package acls + +import ( + "github.com/gophercloud/gophercloud" +) + +// GetContainerACL retrieves the ACL of a container. +func GetContainerACL(client *gophercloud.ServiceClient, containerID string) (r ACLResult) { + _, r.Err = client.Get(containerURL(client, containerID), &r.Body, nil) + return +} + +// GetSecretACL retrieves the ACL of a secret. +func GetSecretACL(client *gophercloud.ServiceClient, secretID string) (r ACLResult) { + _, r.Err = client.Get(secretURL(client, secretID), &r.Body, nil) + return +} + +// SetOptsBuilder allows extensions to add additional parameters to the +// Set request. +type SetOptsBuilder interface { + ToACLSetMap() (map[string]interface{}, error) +} + +// SetOpts represents options to set an ACL on a resource. +type SetOpts struct { + // Type is the type of ACL to set. ie: read. + Type string `json:"-" required:"true"` + + // Users are the list of Keystone user UUIDs. + Users *[]string `json:"users,omitempty"` + + // ProjectAccess toggles if all users in a project can access the resource. + ProjectAccess *bool `json:"project-access,omitempty"` +} + +// ToACLSetMap formats a SetOpts into a set request. +func (opts SetOpts) ToACLSetMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, opts.Type) +} + +// SetContainerACL will set an ACL on a container. +func SetContainerACL(client *gophercloud.ServiceClient, containerID string, opts SetOptsBuilder) (r ACLRefResult) { + b, err := opts.ToACLSetMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Put(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// SetSecretACL will set an ACL on a secret. +func SetSecretACL(client *gophercloud.ServiceClient, secretID string, opts SetOptsBuilder) (r ACLRefResult) { + b, err := opts.ToACLSetMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Put(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// UpdateContainerACL will update an ACL on a container. +func UpdateContainerACL(client *gophercloud.ServiceClient, containerID string, opts SetOptsBuilder) (r ACLRefResult) { + b, err := opts.ToACLSetMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Patch(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// UpdateSecretACL will update an ACL on a secret. +func UpdateSecretACL(client *gophercloud.ServiceClient, secretID string, opts SetOptsBuilder) (r ACLRefResult) { + b, err := opts.ToACLSetMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Patch(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// DeleteContainerACL will delete an ACL from a conatiner. +func DeleteContainerACL(client *gophercloud.ServiceClient, containerID string) (r DeleteResult) { + _, r.Err = client.Delete(containerURL(client, containerID), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// DeleteSecretACL will delete an ACL from a secret. +func DeleteSecretACL(client *gophercloud.ServiceClient, secretID string) (r DeleteResult) { + _, r.Err = client.Delete(secretURL(client, secretID), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/keymanager/v1/acls/results.go b/openstack/keymanager/v1/acls/results.go new file mode 100644 index 0000000000..959742da95 --- /dev/null +++ b/openstack/keymanager/v1/acls/results.go @@ -0,0 +1,85 @@ +package acls + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" +) + +// ACL represents an ACL on a resource. +type ACL map[string]ACLDetails + +// ACLDetails represents the details of an ACL. +type ACLDetails struct { + // Created is when the ACL was created. + Created time.Time `json:"-"` + + // ProjectAccess denotes project-level access of the resource. + ProjectAccess bool `json:"project-access"` + + // Updated is when the ACL was updated + Updated time.Time `json:"-"` + + // Users are the UserIDs who have access to the resource. + Users []string `json:"users"` +} + +func (r *ACLDetails) UnmarshalJSON(b []byte) error { + type tmp ACLDetails + var s struct { + tmp + Created gophercloud.JSONRFC3339NoZ `json:"created"` + Updated gophercloud.JSONRFC3339NoZ `json:"updated"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ACLDetails(s.tmp) + + r.Created = time.Time(s.Created) + r.Updated = time.Time(s.Updated) + + return nil +} + +// ACLRef represents an ACL reference. +type ACLRef string + +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult as an ACL. +func (r commonResult) Extract() (*ACL, error) { + var s *ACL + err := r.ExtractInto(&s) + return s, err +} + +// ACLResult is the response from a Get operation. Call its Extract method +// to interpret it as an ACL. +type ACLResult struct { + commonResult +} + +// ACLRefResult is the response from a Set or Update operation. Call its +// Extract method to interpret it as an ACLRef. +type ACLRefResult struct { + gophercloud.Result +} + +func (r ACLRefResult) Extract() (*ACLRef, error) { + var s struct { + ACLRef ACLRef `json:"acl_ref"` + } + err := r.ExtractInto(&s) + return &s.ACLRef, err +} + +// 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/keymanager/v1/acls/testing/fixtures.go b/openstack/keymanager/v1/acls/testing/fixtures.go new file mode 100644 index 0000000000..7ae5b5971c --- /dev/null +++ b/openstack/keymanager/v1/acls/testing/fixtures.go @@ -0,0 +1,168 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const GetResponse = ` +{ + "read": { + "created": "2018-06-22T17:54:24", + "project-access": false, + "updated": "2018-06-22T17:54:24", + "users": [ + "GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW" + ] + } +}` + +const SetRequest = ` +{ + "read": { + "project-access": false, + "users": [ + "GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW" + ] + } +}` + +const SecretSetResponse = ` +{ + "acl_ref": "http://barbican:9311/v1/secrets/4befede0-fbde-4480-982c-b160c1014a47/acl" +}` + +const ContainerSetResponse = ` +{ + "acl_ref": "http://barbican:9311/v1/containers/4befede0-fbde-4480-982c-b160c1014a47/acl" +}` + +var ExpectedACL = acls.ACL{ + "read": acls.ACLDetails{ + Created: time.Date(2018, 6, 22, 17, 54, 24, 0, time.UTC), + ProjectAccess: false, + Updated: time.Date(2018, 6, 22, 17, 54, 24, 0, time.UTC), + Users: []string{ + "GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW", + }, + }, +} + +var ExpectedSecretACLRef = acls.ACLRef("http://barbican:9311/v1/secrets/4befede0-fbde-4480-982c-b160c1014a47/acl") + +var ExpectedContainerACLRef = acls.ACLRef("http://barbican:9311/v1/containers/4befede0-fbde-4480-982c-b160c1014a47/acl") + +const UpdateRequest = ` +{ + "read": { + "users": [] + } +}` + +// HandleGetSecretACLSuccessfully creates an HTTP handler at `/secrets/uuid/acl` +// on the test handler mux that responds with an acl. +func HandleGetSecretACLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", 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, GetResponse) + }) +} + +// HandleGetContainerACLSuccessfully creates an HTTP handler at `/secrets/uuid/acl` +// on the test handler mux that responds with an acl. +func HandleGetContainerACLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", 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, GetResponse) + }) +} + +// HandleSetSecretACLSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret creation. +func HandleSetSecretACLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", 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, SetRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, SecretSetResponse) + }) +} + +// HandleSetContainerACLSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret creation. +func HandleSetContainerACLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", 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, SetRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ContainerSetResponse) + }) +} + +// HandleUpdateSecretACLSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret creation. +func HandleUpdateSecretACLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", 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, SecretSetResponse) + }) +} + +// HandleUpdateContainerACLSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret creation. +func HandleUpdateContainerACLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", 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, ContainerSetResponse) + }) +} + +// HandleDeleteSecretACLSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret deletion. +func HandleDeleteSecretACLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", 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) + }) +} + +// HandleDeleteContainerACLSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret deletion. +func HandleDeleteContainerACLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/acl", 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) + }) +} diff --git a/openstack/keymanager/v1/acls/testing/requests_test.go b/openstack/keymanager/v1/acls/testing/requests_test.go new file mode 100644 index 0000000000..d2b31250f1 --- /dev/null +++ b/openstack/keymanager/v1/acls/testing/requests_test.go @@ -0,0 +1,115 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGetSecretACL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSecretACLSuccessfully(t) + + actual, err := acls.GetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedACL, *actual) +} + +func TestGetContainerACL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetContainerACLSuccessfully(t) + + actual, err := acls.GetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedACL, *actual) +} + +func TestSetSecretACL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSetSecretACLSuccessfully(t) + + users := []string{"GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW"} + iFalse := false + setOpts := acls.SetOpts{ + Type: "read", + Users: &users, + ProjectAccess: &iFalse, + } + + actual, err := acls.SetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedSecretACLRef, *actual) +} + +func TestSetContainerACL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSetContainerACLSuccessfully(t) + + users := []string{"GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW"} + iFalse := false + setOpts := acls.SetOpts{ + Type: "read", + Users: &users, + ProjectAccess: &iFalse, + } + + actual, err := acls.SetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedContainerACLRef, *actual) +} + +func TestDeleteSecretACL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSecretACLSuccessfully(t) + + res := acls.DeleteSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") + th.AssertNoErr(t, res.Err) +} + +func TestDeleteContainerACL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteContainerACLSuccessfully(t) + + res := acls.DeleteContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateSecretACL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateSecretACLSuccessfully(t) + + newUsers := []string{} + updateOpts := acls.SetOpts{ + Type: "read", + Users: &newUsers, + } + + actual, err := acls.UpdateSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedSecretACLRef, *actual) +} + +func TestUpdateContainerACL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateContainerACLSuccessfully(t) + + newUsers := []string{} + updateOpts := acls.SetOpts{ + Type: "read", + Users: &newUsers, + } + + actual, err := acls.UpdateContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedContainerACLRef, *actual) +} diff --git a/openstack/keymanager/v1/acls/urls.go b/openstack/keymanager/v1/acls/urls.go new file mode 100644 index 0000000000..5cb4439a8c --- /dev/null +++ b/openstack/keymanager/v1/acls/urls.go @@ -0,0 +1,11 @@ +package acls + +import "github.com/gophercloud/gophercloud" + +func containerURL(client *gophercloud.ServiceClient, containerID string) string { + return client.ServiceURL("containers", containerID, "acl") +} + +func secretURL(client *gophercloud.ServiceClient, secretID string) string { + return client.ServiceURL("secrets", secretID, "acl") +} diff --git a/openstack/keymanager/v1/containers/doc.go b/openstack/keymanager/v1/containers/doc.go new file mode 100644 index 0000000000..cb11920f29 --- /dev/null +++ b/openstack/keymanager/v1/containers/doc.go @@ -0,0 +1,86 @@ +/* +Package containers manages and retrieves containers in the OpenStack Key Manager +Service. + +Example to List Containers + + allPages, err := containers.List(client, nil).AllPages() + if err != nil { + panic(err) + } + + allContainers, err := containers.ExtractContainers(allPages) + if err != nil { + panic(err) + } + + for _, v := range allContainers { + fmt.Printf("%v\n", v) + } + +Example to Create a Container + + createOpts := containers.CreateOpts{ + Type: containers.GenericContainer, + Name: "mycontainer", + SecretRefs: []containers.SecretRef{ + { + Name: secret.Name, + SecretRef: secret.SecretRef, + }, + }, + } + + container, err := containers.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", container) + +Example to Delete a Container + + err := containers.Delete(client, containerID).ExtractErr() + if err != nil { + panic(err) + } + +Example to List Consumers of a Container + + allPages, err := containers.ListConsumers(client, containerID, nil).AllPages() + if err != nil { + panic(err) + } + + allConsumers, err := containers.ExtractConsumers(allPages) + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", allConsumers) + +Example to Create a Consumer of a Container + + createOpts := containers.CreateConsumerOpts{ + Name: "jdoe", + URL: "http://example.com", + } + + container, err := containers.CreateConsumer(client, containerID, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Consumer of a Container + + deleteOpts := containers.DeleteConsumerOpts{ + Name: "jdoe", + URL: "http://example.com", + } + + container, err := containers.DeleteConsumer(client, containerID, deleteOpts).Extract() + if err != nil { + panic(err) + } +*/ +package containers diff --git a/openstack/keymanager/v1/containers/requests.go b/openstack/keymanager/v1/containers/requests.go new file mode 100644 index 0000000000..190438ed28 --- /dev/null +++ b/openstack/keymanager/v1/containers/requests.go @@ -0,0 +1,212 @@ +package containers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ContainerType represents the valid types of containers. +type ContainerType string + +const ( + GenericContainer ContainerType = "generic" + RSAContainer ContainerType = "rsa" + CertificateContainer ContainerType = "certificate" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToContainerListQuery() (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"` + + // Name is the name of the container + Name string `q:"name"` + + // Offset is the index within the list to retrieve. + Offset int `q:"offset"` +} + +// ToContainerListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToContainerListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List retrieves a list of containers. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToContainerListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ContainerPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details of a container. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToContainerCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a container. +type CreateOpts struct { + // Type represents the type of container. + Type ContainerType `json:"type" required:"true"` + + // Name is the name of the container. + Name string `json:"name"` + + // SecretRefs is a list of secret refs for the container. + SecretRefs []SecretRef `json:"secret_refs"` +} + +// ToContainerCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToContainerCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create creates a new container. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToContainerCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// Delete deletes a container. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// ListConsumersOptsBuilder allows extensions to add additional parameters to +// the ListConsumers request +type ListConsumersOptsBuilder interface { + ToContainerListConsumersQuery() (string, error) +} + +// ListConsumersOpts provides options to filter the List results. +type ListConsumersOpts struct { + // Limit is the amount of consumers to retrieve. + Limit int `q:"limit"` + + // Offset is the index within the list to retrieve. + Offset int `q:"offset"` +} + +// ToContainerListConsumersQuery formats a ListConsumersOpts into a query +// string. +func (opts ListOpts) ToContainerListConsumersQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListConsumers retrieves a list of consumers from a container. +func ListConsumers(client *gophercloud.ServiceClient, containerID string, opts ListConsumersOptsBuilder) pagination.Pager { + url := listConsumersURL(client, containerID) + if opts != nil { + query, err := opts.ToContainerListConsumersQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ConsumerPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateConsumerOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateConsumerOptsBuilder interface { + ToContainerConsumerCreateMap() (map[string]interface{}, error) +} + +// CreateConsumerOpts provides options used to create a container. +type CreateConsumerOpts struct { + // Name is the name of the consumer. + Name string `json:"name"` + + // URL is the URL to the consumer resource. + URL string `json:"URL"` +} + +// ToContainerConsumerCreateMap formats a CreateConsumerOpts into a create +// request. +func (opts CreateConsumerOpts) ToContainerConsumerCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// CreateConsumer creates a new consumer. +func CreateConsumer(client *gophercloud.ServiceClient, containerID string, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { + b, err := opts.ToContainerConsumerCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createConsumerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// DeleteConsumerOptsBuilder allows extensions to add additional parameters to +// the Delete request. +type DeleteConsumerOptsBuilder interface { + ToContainerConsumerDeleteMap() (map[string]interface{}, error) +} + +// DeleteConsumerOpts represents options used for deleting a consumer. +type DeleteConsumerOpts struct { + // Name is the name of the consumer. + Name string `json:"name"` + + // URL is the URL to the consumer resource. + URL string `json:"URL"` +} + +// ToContainerConsumerDeleteMap formats a DeleteConsumerOpts into a create +// request. +func (opts DeleteConsumerOpts) ToContainerConsumerDeleteMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// DeleteConsumer deletes a consumer. +func DeleteConsumer(client *gophercloud.ServiceClient, containerID string, opts DeleteConsumerOptsBuilder) (r DeleteConsumerResult) { + url := deleteConsumerURL(client, containerID) + + b, err := opts.ToContainerConsumerDeleteMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Request("DELETE", url, &gophercloud.RequestOpts{ + JSONBody: b, + JSONResponse: &r.Body, + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/keymanager/v1/containers/results.go b/openstack/keymanager/v1/containers/results.go new file mode 100644 index 0000000000..c94c9ac671 --- /dev/null +++ b/openstack/keymanager/v1/containers/results.go @@ -0,0 +1,232 @@ +package containers + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Container represents a container in the key manager service. +type Container struct { + // Consumers are the consumers of the container. + Consumers []ConsumerRef `json:"consumers"` + + // ContainerRef is the URL to the container + ContainerRef string `json:"container_ref"` + + // Created is the date the container was created. + Created time.Time `json:"-"` + + // CreatorID is the creator of the container. + CreatorID string `json:"creator_id"` + + // Name is the name of the container. + Name string `json:"name"` + + // SecretRefs are the secret references of the container. + SecretRefs []SecretRef `json:"secret_refs"` + + // Status is the status of the container. + Status string `json:"status"` + + // Type is the type of container. + Type string `json:"type"` + + // Updated is the date the container was updated. + Updated time.Time `json:"-"` +} + +func (r *Container) UnmarshalJSON(b []byte) error { + type tmp Container + var s struct { + tmp + Created gophercloud.JSONRFC3339NoZ `json:"created"` + Updated gophercloud.JSONRFC3339NoZ `json:"updated"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Container(s.tmp) + + r.Created = time.Time(s.Created) + r.Updated = time.Time(s.Updated) + + return nil +} + +// ConsumerRef represents a consumer reference in a container. +type ConsumerRef struct { + // Name is the name of the consumer. + Name string `json:"name"` + + // URL is the URL to the consumer resource. + URL string `json:"url"` +} + +// SecretRef is a reference to a secret. +type SecretRef struct { + SecretRef string `json:"secret_ref"` + Name string `json:"name"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult as a Container. +func (r commonResult) Extract() (*Container, error) { + var s *Container + err := r.ExtractInto(&s) + return s, err +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a container. +type GetResult struct { + commonResult +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a container. +type CreateResult 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 +} + +// ContainerPage is a single page of container results. +type ContainerPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Container contains any results. +func (r ContainerPage) IsEmpty() (bool, error) { + containers, err := ExtractContainers(r) + return len(containers) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r ContainerPage) 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 +} + +// ExtractContainers returns a slice of Containers contained in a single page of +// results. +func ExtractContainers(r pagination.Page) ([]Container, error) { + var s struct { + Containers []Container `json:"containers"` + } + err := (r.(ContainerPage)).ExtractInto(&s) + return s.Containers, err +} + +// Consumer represents a consumer in a container. +type Consumer struct { + // Created is the date the container was created. + Created time.Time `json:"-"` + + // Name is the name of the container. + Name string `json:"name"` + + // Status is the status of the container. + Status string `json:"status"` + + // Updated is the date the container was updated. + Updated time.Time `json:"-"` + + // URL is the url to the consumer. + URL string `json:"url"` +} + +func (r *Consumer) UnmarshalJSON(b []byte) error { + type tmp Consumer + var s struct { + tmp + Created gophercloud.JSONRFC3339NoZ `json:"created"` + Updated gophercloud.JSONRFC3339NoZ `json:"updated"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Consumer(s.tmp) + + r.Created = time.Time(s.Created) + r.Updated = time.Time(s.Updated) + + return nil +} + +type consumerResult struct { + gophercloud.Result +} + +// Extract interprets any consumerResult as a Consumer. +func (r consumerResult) Extract() (*Consumer, error) { + var s *Consumer + err := r.ExtractInto(&s) + return s, err +} + +// CreateConsumerResult is the response from a CreateConsumer operation. +// Call its Extract method to interpret it as a container. +type CreateConsumerResult struct { + // This is not a typo. + commonResult +} + +// DeleteConsumerResult is the response from a DeleteConsumer operation. +// Call its Extract to interpret it as a container. +type DeleteConsumerResult struct { + // This is not a typo. + commonResult +} + +// ConsumerPage is a single page of consumer results. +type ConsumerPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of consumers contains any results. +func (r ConsumerPage) IsEmpty() (bool, error) { + consumers, err := ExtractConsumers(r) + return len(consumers) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r ConsumerPage) 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 +} + +// ExtractConsumers returns a slice of Consumers contained in a single page of +// results. +func ExtractConsumers(r pagination.Page) ([]Consumer, error) { + var s struct { + Consumers []Consumer `json:"consumers"` + } + err := (r.(ConsumerPage)).ExtractInto(&s) + return s.Consumers, err +} diff --git a/openstack/keymanager/v1/containers/testing/fixtures.go b/openstack/keymanager/v1/containers/testing/fixtures.go new file mode 100644 index 0000000000..3c73e38b1c --- /dev/null +++ b/openstack/keymanager/v1/containers/testing/fixtures.go @@ -0,0 +1,321 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListResponse provides a single page of container results. +const ListResponse = ` +{ + "containers": [ + { + "consumers": [], + "container_ref": "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", + "created": "2018-06-21T21:28:37", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "name": "mycontainer", + "secret_refs": [ + { + "name": "mysecret", + "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" + } + ], + "status": "ACTIVE", + "type": "generic", + "updated": "2018-06-21T21:28:37" + }, + { + "consumers": [], + "container_ref": "http://barbican:9311/v1/containers/47b20e73-335b-4867-82dc-3796524d5e20", + "created": "2018-06-21T21:30:09", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "name": "anothercontainer", + "secret_refs": [ + { + "name": "another", + "secret_ref": "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac" + } + ], + "status": "ACTIVE", + "type": "generic", + "updated": "2018-06-21T21:30:09" + } + ], + "total": 2 +}` + +// GetResponse provides a Get result. +const GetResponse = ` +{ + "consumers": [], + "container_ref": "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", + "created": "2018-06-21T21:28:37", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "name": "mycontainer", + "secret_refs": [ + { + "name": "mysecret", + "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" + } + ], + "status": "ACTIVE", + "type": "generic", + "updated": "2018-06-21T21:28:37" +}` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "name": "mycontainer", + "secret_refs": [ + { + "name": "mysecret", + "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" + } + ], + "type": "generic" +}` + +// CreateResponse is the response of a Create request. +const CreateResponse = ` +{ + "consumers": [], + "container_ref": "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", + "created": "2018-06-21T21:28:37", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "name": "mycontainer", + "secret_refs": [ + { + "name": "mysecret", + "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" + } + ], + "status": "ACTIVE", + "type": "generic", + "updated": "2018-06-21T21:28:37" +}` + +// FirstContainer is the first resource in the List request. +var FirstContainer = containers.Container{ + Consumers: []containers.ConsumerRef{}, + ContainerRef: "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", + Created: time.Date(2018, 6, 21, 21, 28, 37, 0, time.UTC), + CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", + Name: "mycontainer", + SecretRefs: []containers.SecretRef{ + { + Name: "mysecret", + SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", + }, + }, + Status: "ACTIVE", + Type: "generic", + Updated: time.Date(2018, 6, 21, 21, 28, 37, 0, time.UTC), +} + +// SecondContainer is the second resource in the List request. +var SecondContainer = containers.Container{ + Consumers: []containers.ConsumerRef{}, + ContainerRef: "http://barbican:9311/v1/containers/47b20e73-335b-4867-82dc-3796524d5e20", + Created: time.Date(2018, 6, 21, 21, 30, 9, 0, time.UTC), + CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", + Name: "anothercontainer", + SecretRefs: []containers.SecretRef{ + { + Name: "another", + SecretRef: "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac", + }, + }, + Status: "ACTIVE", + Type: "generic", + Updated: time.Date(2018, 6, 21, 21, 30, 9, 0, time.UTC), +} + +// ExpectedContainersSlice is the slice of containers expected to be returned from ListResponse. +var ExpectedContainersSlice = []containers.Container{FirstContainer, SecondContainer} + +const ListConsumersResponse = ` +{ + "consumers": [ + { + "URL": "http://example.com", + "created": "2018-06-22T16:26:25", + "name": "CONSUMER-LZILN1zq", + "status": "ACTIVE", + "updated": "2018-06-22T16:26:25" + } + ], + "total": 1 +}` + +// ExpectedConsumer is the expected result of a consumer retrieval. +var ExpectedConsumer = containers.Consumer{ + URL: "http://example.com", + Created: time.Date(2018, 6, 22, 16, 26, 25, 0, time.UTC), + Name: "CONSUMER-LZILN1zq", + Status: "ACTIVE", + Updated: time.Date(2018, 6, 22, 16, 26, 25, 0, time.UTC), +} + +// ExpectedConsumersSlice is an expected slice of consumers. +var ExpectedConsumersSlice = []containers.Consumer{ExpectedConsumer} + +const CreateConsumerRequest = ` +{ + "URL": "http://example.com", + "name": "CONSUMER-LZILN1zq" +}` + +const CreateConsumerResponse = ` +{ + "consumers": [ + { + "URL": "http://example.com", + "name": "CONSUMER-LZILN1zq" + } + ], + "container_ref": "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", + "created": "2018-06-21T21:28:37", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "name": "mycontainer", + "secret_refs": [ + { + "name": "mysecret", + "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" + } + ], + "status": "ACTIVE", + "type": "generic", + "updated": "2018-06-21T21:28:37" +}` + +// ExpectedCreatedConsumer is the expected result of adding a consumer. +var ExpectedCreatedConsumer = containers.Container{ + Consumers: []containers.ConsumerRef{ + { + Name: "CONSUMER-LZILN1zq", + URL: "http://example.com", + }, + }, + ContainerRef: "http://barbican:9311/v1/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", + Created: time.Date(2018, 6, 21, 21, 28, 37, 0, time.UTC), + CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", + Name: "mycontainer", + SecretRefs: []containers.SecretRef{ + { + Name: "mysecret", + SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", + }, + }, + Status: "ACTIVE", + Type: "generic", + Updated: time.Date(2018, 6, 21, 21, 28, 37, 0, time.UTC), +} + +const DeleteConsumerRequest = ` +{ + "URL": "http://example.com", + "name": "CONSUMER-LZILN1zq" +}` + +// HandleListContainersSuccessfully creates an HTTP handler at `/containers` on the +// test handler mux that responds with a list of two containers. +func HandleListContainersSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers", 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, ListResponse) + }) +} + +// HandleGetContainerSuccessfully creates an HTTP handler at `/containers` on the +// test handler mux that responds with a single resource. +func HandleGetContainerSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", 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, GetResponse) + }) +} + +// HandleCreateContainerSuccessfully creates an HTTP handler at `/containers` on the +// test handler mux that tests resource creation. +func HandleCreateContainerSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers", 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, GetResponse) + }) +} + +// HandleDeleteContainerSuccessfully creates an HTTP handler at `/containers` on the +// test handler mux that tests resource deletion. +func HandleDeleteContainerSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0", 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) + }) +} + +// HandleListConsumersSuccessfully creates an HTTP handler at +// `/containers/uuid/consumers` on the test handler mux that responds with +// a list of consumers. +func HandleListConsumersSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0/consumers", 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, ListConsumersResponse) + }) +} + +// HandleCreateConsumerSuccessfully creates an HTTP handler at +// `/containers/uuid/consumers` on the test handler mux that tests resource +// creation. +func HandleCreateConsumerSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0/consumers", 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, CreateConsumerRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, CreateConsumerResponse) + }) +} + +// HandleDeleteConsumerSuccessfully creates an HTTP handler at +// `/containers/uuid/consumers` on the test handler mux that tests resource +// deletion. +func HandleDeleteConsumerSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/containers/dfdb88f3-4ddb-4525-9da6-066453caa9b0/consumers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, CreateConsumerRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetResponse) + }) +} diff --git a/openstack/keymanager/v1/containers/testing/requests_test.go b/openstack/keymanager/v1/containers/testing/requests_test.go new file mode 100644 index 0000000000..94a657d732 --- /dev/null +++ b/openstack/keymanager/v1/containers/testing/requests_test.go @@ -0,0 +1,144 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListContainers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListContainersSuccessfully(t) + + count := 0 + err := containers.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := containers.ExtractContainers(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedContainersSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListContainersAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListContainersSuccessfully(t) + + allPages, err := containers.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := containers.ExtractContainers(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedContainersSlice, actual) +} + +func TestGetContainer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetContainerSuccessfully(t) + + actual, err := containers.Get(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, FirstContainer, *actual) +} + +func TestCreateContainer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateContainerSuccessfully(t) + + createOpts := containers.CreateOpts{ + Type: containers.GenericContainer, + Name: "mycontainer", + SecretRefs: []containers.SecretRef{ + { + Name: "mysecret", + SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", + }, + }, + } + + actual, err := containers.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, FirstContainer, *actual) +} + +func TestDeleteContainer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteContainerSuccessfully(t) + + res := containers.Delete(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0") + th.AssertNoErr(t, res.Err) +} + +func TestListConsumers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListConsumersSuccessfully(t) + + count := 0 + err := containers.ListConsumers(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := containers.ExtractConsumers(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedConsumersSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListConsumersAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListConsumersSuccessfully(t) + + allPages, err := containers.ListConsumers(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", nil).AllPages() + th.AssertNoErr(t, err) + actual, err := containers.ExtractConsumers(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedConsumersSlice, actual) +} + +func TestCreateConsumer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateConsumerSuccessfully(t) + + createOpts := containers.CreateConsumerOpts{ + Name: "CONSUMER-LZILN1zq", + URL: "http://example.com", + } + + actual, err := containers.CreateConsumer(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedCreatedConsumer, *actual) +} + +func TestDeleteConsumer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteConsumerSuccessfully(t) + + deleteOpts := containers.DeleteConsumerOpts{ + Name: "CONSUMER-LZILN1zq", + URL: "http://example.com", + } + + actual, err := containers.DeleteConsumer(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", deleteOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, FirstContainer, *actual) +} diff --git a/openstack/keymanager/v1/containers/urls.go b/openstack/keymanager/v1/containers/urls.go new file mode 100644 index 0000000000..236ebc8133 --- /dev/null +++ b/openstack/keymanager/v1/containers/urls.go @@ -0,0 +1,31 @@ +package containers + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("containers") +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("containers", id) +} + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("containers") +} + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("containers", id) +} + +func listConsumersURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("containers", id, "consumers") +} + +func createConsumerURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("containers", id, "consumers") +} + +func deleteConsumerURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("containers", id, "consumers") +} diff --git a/openstack/keymanager/v1/orders/doc.go b/openstack/keymanager/v1/orders/doc.go new file mode 100644 index 0000000000..fcbeccf4e5 --- /dev/null +++ b/openstack/keymanager/v1/orders/doc.go @@ -0,0 +1,45 @@ +/* +Package orders manages and retrieves orders in the OpenStack Key Manager +Service. + +Example to List Orders + + allPages, err := orders.List(client, nil).AllPages() + if err != nil { + panic(err) + } + + allOrders, err := orders.ExtractOrders(allPages) + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", allOrders) + +Example to Create a Order + + createOpts := orders.CreateOpts{ + Type: orders.KeyOrder, + Meta: orders.MetaOpts{ + Name: "order-name", + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + }, + } + + order, err := orders.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", order) + +Example to Delete a Order + + err := orders.Delete(client, orderID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package orders diff --git a/openstack/keymanager/v1/orders/requests.go b/openstack/keymanager/v1/orders/requests.go new file mode 100644 index 0000000000..7a55f9ddb8 --- /dev/null +++ b/openstack/keymanager/v1/orders/requests.go @@ -0,0 +1,129 @@ +package orders + +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// OrderType represents the valid types of orders. +type OrderType string + +const ( + KeyOrder OrderType = "key" + AsymmetricOrder OrderType = "asymmetric" +) + +// 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 OrderPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details of a orders. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToOrderCreateMap() (map[string]interface{}, error) +} + +// MetaOpts represents options used for creating an order. +type MetaOpts struct { + // Algorithm is the algorithm of the secret. + Algorithm string `json:"algorithm"` + + // BitLength is the bit length of the secret. + BitLength int `json:"bit_length"` + + // Expiration is the expiration date of the order. + Expiration *time.Time `json:"-"` + + // Mode is the mode of the secret. + Mode string `json:"mode"` + + // Name is the name of the secret. + Name string `json:"name,omitempty"` + + // PayloadContentType is the content type of the secret payload. + PayloadContentType string `json:"payload_content_type,omitempty"` +} + +// CreateOpts provides options used to create a orders. +type CreateOpts struct { + // Type is the type of order to create. + Type OrderType `json:"type"` + + // Meta contains secrets data to create a secret. + Meta MetaOpts `json:"meta"` +} + +// ToOrderCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToOrderCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if opts.Meta.Expiration != nil { + meta := b["meta"].(map[string]interface{}) + meta["expiration"] = opts.Meta.Expiration.Format(gophercloud.RFC3339NoZ) + b["meta"] = meta + } + + return b, nil +} + +// Create creates a new orders. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToOrderCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} + +// Delete deletes a orders. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/keymanager/v1/orders/results.go b/openstack/keymanager/v1/orders/results.go new file mode 100644 index 0000000000..35e89b50ba --- /dev/null +++ b/openstack/keymanager/v1/orders/results.go @@ -0,0 +1,170 @@ +package orders + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Order represents an order in the key manager service. +type Order struct { + // ContainerRef is the container URL. + ContainerRef string `json:"container_ref"` + + // Created is when the order was created. + Created time.Time `json:"-"` + + // CreatorID is the creator of the order. + CreatorID string `json:"creator_id"` + + // ErrorReason is the reason of the error. + ErrorReason string `json:"error_reason"` + + // ErrorStatusCode is the error status code. + ErrorStatusCode string `json:"error_status_code"` + + // OrderRef is the order URL. + OrderRef string `json:"order_ref"` + + // Meta is secret data about the order. + Meta Meta `json:"meta"` + + // SecretRef is the secret URL. + SecretRef string `json:"secret_ref"` + + // Status is the status of the order. + Status string `json:"status"` + + // SubStatus is the status of the order. + SubStatus string `json:"sub_status"` + + // SubStatusMessage is the message of the sub status. + SubStatusMessage string `json:"sub_status_message"` + + // Type is the order type. + Type string `json:"type"` + + // Updated is when the order was updated. + Updated time.Time `json:"-"` +} + +func (r *Order) UnmarshalJSON(b []byte) error { + type tmp Order + var s struct { + tmp + Created gophercloud.JSONRFC3339NoZ `json:"created"` + Updated gophercloud.JSONRFC3339NoZ `json:"updated"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Order(s.tmp) + + r.Created = time.Time(s.Created) + r.Updated = time.Time(s.Updated) + + return nil +} + +type Meta struct { + // Algorithm is the algorithm of the secret. + Algorithm string `json:"algorithm"` + + // BitLength is the bit length of the secret. + BitLength int `json:"bit_length"` + + // Expiration is the expiration date of the order. + Expiration time.Time `json:"-"` + + // Mode is the mode of the secret. + Mode string `json:"mode"` + + // Name is the name of the secret. + Name string `json:"name"` + + // PayloadContentType is the content type of the secret payload. + PayloadContentType string `json:"payload_content_type"` +} + +func (r *Meta) UnmarshalJSON(b []byte) error { + type tmp Meta + var s struct { + tmp + Expiration gophercloud.JSONRFC3339NoZ `json:"expiration"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Meta(s.tmp) + + r.Expiration = time.Time(s.Expiration) + + return nil +} + +type commonResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a orders. +type GetResult struct { + commonResult +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a orders. +type CreateResult 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 +} + +// OrderPage is a single page of orders results. +type OrderPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of ordersS contains any results. +func (r OrderPage) IsEmpty() (bool, error) { + orders, err := ExtractOrders(r) + return len(orders) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r OrderPage) 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 +} + +// ExtractOrders returns a slice of Orders contained in a single page of +// results. +func ExtractOrders(r pagination.Page) ([]Order, error) { + var s struct { + Orders []Order `json:"orders"` + } + err := (r.(OrderPage)).ExtractInto(&s) + return s.Orders, err +} + +// Extract interprets any commonResult as a Order. +func (r commonResult) Extract() (*Order, error) { + var s *Order + err := r.ExtractInto(&s) + return s, err +} diff --git a/openstack/keymanager/v1/orders/testing/fixtures.go b/openstack/keymanager/v1/orders/testing/fixtures.go new file mode 100644 index 0000000000..bdab5427b0 --- /dev/null +++ b/openstack/keymanager/v1/orders/testing/fixtures.go @@ -0,0 +1,186 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListResponse provides a single page of RESOURCE results. +const ListResponse = ` +{ + "orders": [ + { + "created": "2018-06-22T05:05:43", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "meta": { + "algorithm": "aes", + "bit_length": 256, + "expiration": null, + "mode": "cbc", + "name": null, + "payload_content_type": "application/octet-stream" + }, + "order_ref": "http://barbican:9311/v1/orders/46f73695-82bb-447a-bf96-6635f0fb0ce7", + "secret_ref": "http://barbican:9311/v1/secrets/22dfef44-1046-4549-a86d-95af462e8fa0", + "status": "ACTIVE", + "sub_status": "Unknown", + "sub_status_message": "Unknown", + "type": "key", + "updated": "2018-06-22T05:05:43" + }, + { + "created": "2018-06-22T05:08:15", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "meta": { + "algorithm": "aes", + "bit_length": 256, + "expiration": null, + "mode": "cbc", + "name": null, + "payload_content_type": "application/octet-stream" + }, + "order_ref": "http://barbican:9311/v1/orders/07fba88b-3dcf-44e3-a4a3-0bad7f56f01c", + "secret_ref": "http://barbican:9311/v1/secrets/a31ad551-1aa5-4ba0-810e-0865163e0fa9", + "status": "ACTIVE", + "sub_status": "Unknown", + "sub_status_message": "Unknown", + "type": "key", + "updated": "2018-06-22T05:08:15" + } + ], + "total": 2 +}` + +// GetResponse provides a Get result. +const GetResponse = ` +{ + "created": "2018-06-22T05:08:15", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "meta": { + "algorithm": "aes", + "bit_length": 256, + "expiration": null, + "mode": "cbc", + "name": null, + "payload_content_type": "application/octet-stream" + }, + "order_ref": "http://barbican:9311/v1/orders/07fba88b-3dcf-44e3-a4a3-0bad7f56f01c", + "secret_ref": "http://barbican:9311/v1/secrets/a31ad551-1aa5-4ba0-810e-0865163e0fa9", + "status": "ACTIVE", + "sub_status": "Unknown", + "sub_status_message": "Unknown", + "type": "key", + "updated": "2018-06-22T05:08:15" +} +` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "meta": { + "algorithm": "aes", + "bit_length": 256, + "mode": "cbc", + "payload_content_type": "application/octet-stream" + }, + "type": "key" +}` + +// FirstOrder is the first resource in the List request. +var FirstOrder = orders.Order{ + Created: time.Date(2018, 6, 22, 5, 5, 43, 0, time.UTC), + CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", + Meta: orders.Meta{ + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + PayloadContentType: "application/octet-stream", + }, + OrderRef: "http://barbican:9311/v1/orders/46f73695-82bb-447a-bf96-6635f0fb0ce7", + SecretRef: "http://barbican:9311/v1/secrets/22dfef44-1046-4549-a86d-95af462e8fa0", + Status: "ACTIVE", + SubStatus: "Unknown", + SubStatusMessage: "Unknown", + Type: "key", + Updated: time.Date(2018, 6, 22, 5, 5, 43, 0, time.UTC), +} + +// SecondOrder is the second resource in the List request. +var SecondOrder = orders.Order{ + Created: time.Date(2018, 6, 22, 5, 8, 15, 0, time.UTC), + CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", + Meta: orders.Meta{ + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + PayloadContentType: "application/octet-stream", + }, + OrderRef: "http://barbican:9311/v1/orders/07fba88b-3dcf-44e3-a4a3-0bad7f56f01c", + SecretRef: "http://barbican:9311/v1/secrets/a31ad551-1aa5-4ba0-810e-0865163e0fa9", + Status: "ACTIVE", + SubStatus: "Unknown", + SubStatusMessage: "Unknown", + Type: "key", + Updated: time.Date(2018, 6, 22, 5, 8, 15, 0, time.UTC), +} + +// ExpectedOrdersSlice is the slice of orders expected to be returned from ListResponse. +var ExpectedOrdersSlice = []orders.Order{FirstOrder, SecondOrder} + +// HandleListOrdersSuccessfully creates an HTTP handler at `/orders` on the +// test handler mux that responds with a list of two orders. +func HandleListOrdersSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/orders", 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, ListResponse) + }) +} + +// HandleGetOrderSuccessfully creates an HTTP handler at `/orders` on the +// test handler mux that responds with a single resource. +func HandleGetOrderSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/orders/46f73695-82bb-447a-bf96-6635f0fb0ce7", 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, GetResponse) + }) +} + +// HandleCreateOrderSuccessfully creates an HTTP handler at `/orders` on the +// test handler mux that tests resource creation. +func HandleCreateOrderSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/orders", 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.StatusAccepted) + fmt.Fprintf(w, GetResponse) + }) +} + +// HandleDeleteOrderSuccessfully creates an HTTP handler at `/orders` on the +// test handler mux that tests resource deletion. +func HandleDeleteOrderSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/orders/46f73695-82bb-447a-bf96-6635f0fb0ce7", 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/keymanager/v1/orders/testing/requests_test.go b/openstack/keymanager/v1/orders/testing/requests_test.go new file mode 100644 index 0000000000..a6249630e8 --- /dev/null +++ b/openstack/keymanager/v1/orders/testing/requests_test.go @@ -0,0 +1,81 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListOrders(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListOrdersSuccessfully(t) + + count := 0 + err := orders.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := orders.ExtractOrders(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedOrdersSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListOrdersAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListOrdersSuccessfully(t) + + allPages, err := orders.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := orders.ExtractOrders(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedOrdersSlice, actual) +} + +func TestGetOrder(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetOrderSuccessfully(t) + + actual, err := orders.Get(client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondOrder, *actual) +} + +func TestCreateOrder(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateOrderSuccessfully(t) + + createOpts := orders.CreateOpts{ + Type: orders.KeyOrder, + Meta: orders.MetaOpts{ + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + PayloadContentType: "application/octet-stream", + }, + } + + actual, err := orders.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondOrder, *actual) +} + +func TestDeleteOrder(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteOrderSuccessfully(t) + + res := orders.Delete(client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/keymanager/v1/orders/urls.go b/openstack/keymanager/v1/orders/urls.go new file mode 100644 index 0000000000..36890e533a --- /dev/null +++ b/openstack/keymanager/v1/orders/urls.go @@ -0,0 +1,19 @@ +package orders + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("orders") +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("orders", id) +} + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("orders") +} + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("orders", id) +} diff --git a/openstack/keymanager/v1/secrets/doc.go b/openstack/keymanager/v1/secrets/doc.go new file mode 100644 index 0000000000..4167e425d2 --- /dev/null +++ b/openstack/keymanager/v1/secrets/doc.go @@ -0,0 +1,142 @@ +/* +Package secrets manages and retrieves secrets in the OpenStack Key Manager +Service. + +Example to List Secrets + + createdQuery := &secrets.DateQuery{ + Date: time.Date(2049, 6, 7, 1, 2, 3, 0, time.UTC), + Filter: secrets.DateFilterLT, + } + + listOpts := secrets.ListOpts{ + CreatedQuery: createdQuery, + } + + allPages, err := secrets.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allSecrets, err := secrets.ExtractSecrets(allPages) + if err != nil { + panic(err) + } + + for _, v := range allSecrets { + fmt.Printf("%v\n", v) + } + +Example to Get a Secret + + secret, err := secrets.Get(client, secretID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", secret) + +Example to Get a Payload + + payload, err := secrets.GetPayload(client, secretID).Extract() + if err != nil { + panic(err) + } + + fmt.Println(string(payload)) + +Example to Create a Secrets + + createOpts := secrets.CreateOpts{ + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + Name: "mysecret", + Payload: "super-secret", + PayloadContentType: "text/plain", + SecretType: secrets.OpaqueSecret, + } + + secret, err := secrets.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(secret.SecretRef) + +Example to Add a Payload + + updateOpts := secrets.UpdateOpts{ + ContentType: "text/plain", + Payload: "super-secret", + } + + err := secrets.Update(client, secretID, updateOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Delete a Secrets + + err := secrets.Delete(client, secretID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Create Metadata for a Secret + + createOpts := secrets.MetadataOpts{ + "foo": "bar", + "something": "something else", + } + + ref, err := secrets.CreateMetadata(client, secretID, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", ref) + +Example to Get Metadata for a Secret + + metadata, err := secrets.GetMetadata(client, secretID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", metadata) + +Example to Add Metadata to a Secret + + metadatumOpts := secrets.MetadatumOpts{ + Key: "foo", + Value: "bar", + } + + err := secrets.CreateMetadatum(client, secretID, metadatumOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Update Metadata of a Secret + + metadatumOpts := secrets.MetadatumOpts{ + Key: "foo", + Value: "bar", + } + + metadatum, err := secrets.UpdateMetadatum(client, secretID, metadatumOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%v\n", metadatum) + +Example to Delete Metadata of a Secret + + err := secrets.DeleteMetadatum(client, secretID, "foo").ExtractErr() + if err != nil { + panic(err) + } +*/ +package secrets diff --git a/openstack/keymanager/v1/secrets/requests.go b/openstack/keymanager/v1/secrets/requests.go new file mode 100644 index 0000000000..900cbcaaa6 --- /dev/null +++ b/openstack/keymanager/v1/secrets/requests.go @@ -0,0 +1,412 @@ +package secrets + +import ( + "fmt" + "net/url" + "strings" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// DateFilter represents a valid filter to use for filtering +// secrets by their date during a list. +type DateFilter string + +const ( + DateFilterGT DateFilter = "gt" + DateFilterGTE DateFilter = "gte" + DateFilterLT DateFilter = "lt" + DateFilterLTE DateFilter = "lte" +) + +// DateQuery represents a date field to be used for listing secrets. +// If no filter is specified, the query will act as if "equal" is used. +type DateQuery struct { + Date time.Time + Filter DateFilter +} + +// SecretType represents a valid secret type. +type SecretType string + +const ( + SymmetricSecret SecretType = "symmetric" + PublicSecret SecretType = "public" + PrivateSecret SecretType = "private" + PassphraseSecret SecretType = "passphrase" + CertificateSecret SecretType = "certificate" + OpaqueSecret SecretType = "opaque" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToSecretListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // Offset is the starting index within the total list of the secrets that + // you would like to retrieve. + Offset int `q:"offset"` + + // Limit is the maximum number of records to return. + Limit int `q:"limit"` + + // Name will select all secrets with a matching name. + Name string `q:"name"` + + // Alg will select all secrets with a matching algorithm. + Alg string `q:"alg"` + + // Mode will select all secrets with a matching mode. + Mode string `q:"mode"` + + // Bits will select all secrets with a matching bit length. + Bits int `q:"bits"` + + // SecretType will select all secrets with a matching secret type. + SecretType SecretType `q:"secret_type"` + + // ACLOnly will select all secrets with an ACL that contains the user. + ACLOnly *bool `q:"acl_only"` + + // CreatedQuery will select all secrets with a created date matching + // the query. + CreatedQuery *DateQuery + + // UpdatedQuery will select all secrets with an updated date matching + // the query. + UpdatedQuery *DateQuery + + // ExpirationQuery will select all secrets with an expiration date + // matching the query. + ExpirationQuery *DateQuery + + // Sort will sort the results in the requested order. + Sort string `q:"sort"` +} + +// ToSecretListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToSecretListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + params := q.Query() + + if opts.CreatedQuery != nil { + created := opts.CreatedQuery.Date.Format(time.RFC3339) + if v := opts.CreatedQuery.Filter; v != "" { + created = fmt.Sprintf("%s:%s", v, created) + } + + params.Add("created", created) + } + + if opts.UpdatedQuery != nil { + updated := opts.UpdatedQuery.Date.Format(time.RFC3339) + if v := opts.UpdatedQuery.Filter; v != "" { + updated = fmt.Sprintf("%s:%s", v, updated) + } + + params.Add("updated", updated) + } + + if opts.ExpirationQuery != nil { + expiration := opts.ExpirationQuery.Date.Format(time.RFC3339) + if v := opts.ExpirationQuery.Filter; v != "" { + expiration = fmt.Sprintf("%s:%s", v, expiration) + } + + params.Add("expiration", expiration) + } + + q = &url.URL{RawQuery: params.Encode()} + + return q.String(), err +} + +// List retrieves a list of Secrets. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToSecretListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return SecretPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details of a secrets. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// GetPayloadOpts represents options used for obtaining a payload. +type GetPayloadOpts struct { + PayloadContentType string `h:"Accept"` +} + +// GetPayloadOptsBuilder allows extensions to add additional parameters to +// the GetPayload request. +type GetPayloadOptsBuilder interface { + ToSecretPayloadGetParams() (map[string]string, error) +} + +// ToSecretPayloadGetParams formats a GetPayloadOpts into a query string. +func (opts GetPayloadOpts) ToSecretPayloadGetParams() (map[string]string, error) { + return gophercloud.BuildHeaders(opts) +} + +// GetPayload retrieves the payload of a secret. +func GetPayload(client *gophercloud.ServiceClient, id string, opts GetPayloadOptsBuilder) (r PayloadResult) { + h := map[string]string{"Accept": "text/plain"} + + if opts != nil { + headers, err := opts.ToSecretPayloadGetParams() + if err != nil { + r.Err = err + return + } + for k, v := range headers { + h[k] = v + } + } + + url := payloadURL(client, id) + resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{200}, + }) + + if resp != nil { + r.Header = resp.Header + r.Body = resp.Body + } + r.Err = err + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToSecretCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a secrets. +type CreateOpts struct { + // Algorithm is the algorithm of the secret. + Algorithm string `json:"algorithm,omitempty"` + + // BitLength is the bit length of the secret. + BitLength int `json:"bit_length,omitempty"` + + // Mode is the mode of encryption for the secret. + Mode string `json:"mode,omitempty"` + + // Name is the name of the secret + Name string `json:"name,omitempty"` + + // Payload is the secret. + Payload string `json:"payload,omitempty"` + + // PayloadContentType is the content type of the payload. + PayloadContentType string `json:"payload_content_type,omitempty"` + + // PayloadContentEncoding is the content encoding of the payload. + PayloadContentEncoding string `json:"payload_content_encoding,omitempty"` + + // SecretType is the type of secret. + SecretType SecretType `json:"secret_type,omitempty"` +} + +// ToSecretCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToSecretCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create creates a new secrets. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToSecretCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// Delete deletes a secrets. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToSecretUpdateRequest() (string, map[string]string, error) +} + +// UpdateOpts represents parameters to add a payload to an existing +// secret which does not already contain a payload. +type UpdateOpts struct { + // ContentType represents the content type of the payload. + ContentType string `h:"Content-Type"` + + // ContentEncoding represents the content encoding of the payload. + ContentEncoding string `h:"Content-Encoding"` + + // Payload is the payload of the secret. + Payload string +} + +// ToUpdateCreateRequest formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToSecretUpdateRequest() (string, map[string]string, error) { + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + return "", nil, err + } + + return opts.Payload, h, nil +} + +// Update modifies the attributes of a secrets. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + url := updateURL(client, id) + h := make(map[string]string) + var b string + + if opts != nil { + payload, headers, err := opts.ToSecretUpdateRequest() + if err != nil { + r.Err = err + return + } + + for k, v := range headers { + h[k] = v + } + + b = payload + } + + resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ + RawBody: strings.NewReader(b), + MoreHeaders: h, + OkCodes: []int{204}, + }) + r.Err = err + if resp != nil { + r.Header = resp.Header + } + + return +} + +// GetMetadata will list metadata for a given secret. +func GetMetadata(client *gophercloud.ServiceClient, secretID string) (r MetadataResult) { + _, r.Err = client.Get(metadataURL(client, secretID), &r.Body, nil) + return +} + +// MetadataOpts is a map that contains key-value pairs for secret metadata. +type MetadataOpts map[string]string + +// CreateMetadataOptsBuilder allows extensions to add additional parameters to +// the CreateMetadata request. +type CreateMetadataOptsBuilder interface { + ToMetadataCreateMap() (map[string]interface{}, error) +} + +// ToMetadataCreateMap converts a MetadataOpts into a request body. +func (opts MetadataOpts) ToMetadataCreateMap() (map[string]interface{}, error) { + return map[string]interface{}{"metadata": opts}, nil +} + +// CreateMetadata will set metadata for a given secret. +func CreateMetadata(client *gophercloud.ServiceClient, secretID string, opts CreateMetadataOptsBuilder) (r MetadataCreateResult) { + b, err := opts.ToMetadataCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// GetMetadatum will get a single key/value metadata from a secret. +func GetMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumResult) { + _, r.Err = client.Get(metadatumURL(client, secretID, key), &r.Body, nil) + return +} + +// MetadatumOpts represents a single metadata. +type MetadatumOpts struct { + Key string `json:"key" required:"true"` + Value string `json:"value" required:"true"` +} + +// CreateMetadatumOptsBuilder allows extensions to add additional parameters to +// the CreateMetadatum request. +type CreateMetadatumOptsBuilder interface { + ToMetadatumCreateMap() (map[string]interface{}, error) +} + +// ToMetadatumCreateMap converts a MetadatumOpts into a request body. +func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// CreateMetadatum will add a single key/value metadata to a secret. +func CreateMetadatum(client *gophercloud.ServiceClient, secretID string, opts CreateMetadatumOptsBuilder) (r MetadatumCreateResult) { + b, err := opts.ToMetadatumCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// UpdateMetadatumOptsBuilder allows extensions to add additional parameters to +// the UpdateMetadatum request. +type UpdateMetadatumOptsBuilder interface { + ToMetadatumUpdateMap() (map[string]interface{}, string, error) +} + +// ToMetadatumUpdateMap converts a MetadataOpts into a request body. +func (opts MetadatumOpts) ToMetadatumUpdateMap() (map[string]interface{}, string, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + return b, opts.Key, err +} + +// UpdateMetadatum will update a single key/value metadata to a secret. +func UpdateMetadatum(client *gophercloud.ServiceClient, secretID string, opts UpdateMetadatumOptsBuilder) (r MetadatumResult) { + b, key, err := opts.ToMetadatumUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(metadatumURL(client, secretID, key), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// DeleteMetadatum will delete an individual metadatum from a secret. +func DeleteMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumDeleteResult) { + _, r.Err = client.Delete(metadatumURL(client, secretID, key), nil) + return +} diff --git a/openstack/keymanager/v1/secrets/results.go b/openstack/keymanager/v1/secrets/results.go new file mode 100644 index 0000000000..a3230a8b4f --- /dev/null +++ b/openstack/keymanager/v1/secrets/results.go @@ -0,0 +1,227 @@ +package secrets + +import ( + "encoding/json" + "io" + "io/ioutil" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Secret represents a secret stored in the key manager service. +type Secret struct { + // BitLength is the bit length of the secret. + BitLength int `json:"bit_length"` + + // Algorithm is the algorithm type of the secret. + Algorithm string `json:"algorithm"` + + // Expiration is the expiration date of the secret. + Expiration time.Time `json:"-"` + + // ContentTypes are the content types of the secret. + ContentTypes map[string]string `json:"content_types"` + + // Created is the created date of the secret. + Created time.Time `json:"-"` + + // CreatorID is the creator of the secret. + CreatorID string `json:"creator_id"` + + // Mode is the mode of the secret. + Mode string `json:"mode"` + + // Name is the name of the secret. + Name string `json:"name"` + + // SecretRef is the URL to the secret. + SecretRef string `json:"secret_ref"` + + // SecretType represents the type of secret. + SecretType string `json:"secret_type"` + + // Status represents the status of the secret. + Status string `json:"status"` + + // Updated is the updated date of the secret. + Updated time.Time `json:"-"` +} + +func (r *Secret) UnmarshalJSON(b []byte) error { + type tmp Secret + var s struct { + tmp + Created gophercloud.JSONRFC3339NoZ `json:"created"` + Updated gophercloud.JSONRFC3339NoZ `json:"updated"` + Expiration gophercloud.JSONRFC3339NoZ `json:"expiration"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Secret(s.tmp) + + r.Created = time.Time(s.Created) + r.Updated = time.Time(s.Updated) + r.Expiration = time.Time(s.Expiration) + + return nil +} + +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult as a Secret. +func (r commonResult) Extract() (*Secret, error) { + var s *Secret + err := r.ExtractInto(&s) + return s, err +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a secrets. +type GetResult struct { + commonResult +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a secrets. +type CreateResult struct { + commonResult +} + +// UpdateResult is the response from an Update operation. Call its ExtractErr 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. +type DeleteResult struct { + gophercloud.ErrResult +} + +// PayloadResult is the response from a GetPayload operation. Call its Extract +// method to extract the payload as a string. +type PayloadResult struct { + gophercloud.Result + 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) +func (r PayloadResult) Extract() ([]byte, error) { + if r.Err != nil { + return nil, r.Err + } + defer r.Body.Close() + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + r.Body.Close() + return body, nil +} + +// SecretPage is a single page of secrets results. +type SecretPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of secrets contains any results. +func (r SecretPage) IsEmpty() (bool, error) { + secrets, err := ExtractSecrets(r) + return len(secrets) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r SecretPage) 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 +} + +// ExtractSecrets returns a slice of Secrets contained in a single page of +// results. +func ExtractSecrets(r pagination.Page) ([]Secret, error) { + var s struct { + Secrets []Secret `json:"secrets"` + } + err := (r.(SecretPage)).ExtractInto(&s) + return s.Secrets, err +} + +// MetadataResult is the result of a metadata request. Call its Extract method +// to interpret it as a map[string]string. +type MetadataResult struct { + gophercloud.Result +} + +// Extract interprets any MetadataResult as map[string]string. +func (r MetadataResult) Extract() (map[string]string, error) { + var s struct { + Metadata map[string]string `json:"metadata"` + } + err := r.ExtractInto(&s) + return s.Metadata, err +} + +// MetadataCreateResult is the result of a metadata create request. Call its +// Extract method to interpret it as a map[string]string. +type MetadataCreateResult struct { + gophercloud.Result +} + +// Extract interprets any MetadataCreateResult as a map[string]string. +func (r MetadataCreateResult) Extract() (map[string]string, error) { + var s map[string]string + err := r.ExtractInto(&s) + return s, err +} + +// Metadatum represents an individual metadata. +type Metadatum struct { + Key string `json:"key"` + Value string `json:"value"` +} + +// MetadatumResult is the result of a metadatum request. Call its +// Extract method to interpret it as a map[string]string. +type MetadatumResult struct { + gophercloud.Result +} + +// Extract interprets any MetadatumResult as a map[string]string. +func (r MetadatumResult) Extract() (*Metadatum, error) { + var s *Metadatum + err := r.ExtractInto(&s) + return s, err +} + +// MetadatumCreateResult is the response from a metadata Create operation. Call +// it's ExtractErr 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. +type MetadatumCreateResult struct { + gophercloud.ErrResult +} + +// MetadatumDeleteResult is the response from a metadatum Delete operation. Call +// its ExtractErr to determine if the request succeeded or failed. +type MetadatumDeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/keymanager/v1/secrets/testing/fixtures.go b/openstack/keymanager/v1/secrets/testing/fixtures.go new file mode 100644 index 0000000000..2b50658b81 --- /dev/null +++ b/openstack/keymanager/v1/secrets/testing/fixtures.go @@ -0,0 +1,355 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListResponse provides a single page of RESOURCE results. +const ListResponse = ` +{ + "secrets": [ + { + "algorithm": "aes", + "bit_length": 256, + "content_types": { + "default": "text/plain" + }, + "created": "2018-06-21T02:49:48", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "expiration": null, + "mode": "cbc", + "name": "mysecret", + "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", + "secret_type": "opaque", + "status": "ACTIVE", + "updated": "2018-06-21T02:49:48" + }, + { + "algorithm": "aes", + "bit_length": 256, + "content_types": { + "default": "text/plain" + }, + "created": "2018-06-21T05:18:45", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "expiration": null, + "mode": "cbc", + "name": "anothersecret", + "secret_ref": "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac", + "secret_type": "opaque", + "status": "ACTIVE", + "updated": "2018-06-21T05:18:45" + } + ], + "total": 2 +}` + +// GetResponse provides a Get result. +const GetResponse = ` +{ + "algorithm": "aes", + "bit_length": 256, + "content_types": { + "default": "text/plain" + }, + "created": "2018-06-21T02:49:48", + "creator_id": "5c70d99f4a8641c38f8084b32b5e5c0e", + "expiration": null, + "mode": "cbc", + "name": "mysecret", + "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", + "secret_type": "opaque", + "status": "ACTIVE", + "updated": "2018-06-21T02:49:48" +}` + +// GetPayloadResponse provides a payload result. +const GetPayloadResponse = `foobar` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "algorithm": "aes", + "bit_length": 256, + "mode": "cbc", + "name": "mysecret", + "payload": "foobar", + "payload_content_type": "text/plain", + "secret_type": "opaque" +}` + +// CreateResponse provides a Create result. +const CreateResponse = ` +{ + "secret_ref": "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c" +}` + +// UpdateRequest provides the input to as Update request. +const UpdateRequest = `foobar` + +// FirstSecret is the first secret in the List request. +var FirstSecret = secrets.Secret{ + Algorithm: "aes", + BitLength: 256, + ContentTypes: map[string]string{ + "default": "text/plain", + }, + Created: time.Date(2018, 6, 21, 2, 49, 48, 0, time.UTC), + CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", + Mode: "cbc", + Name: "mysecret", + SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", + SecretType: "opaque", + Status: "ACTIVE", + Updated: time.Date(2018, 6, 21, 2, 49, 48, 0, time.UTC), +} + +// SecondSecret is the second secret in the List request. +var SecondSecret = secrets.Secret{ + Algorithm: "aes", + BitLength: 256, + ContentTypes: map[string]string{ + "default": "text/plain", + }, + Created: time.Date(2018, 6, 21, 5, 18, 45, 0, time.UTC), + CreatorID: "5c70d99f4a8641c38f8084b32b5e5c0e", + Mode: "cbc", + Name: "anothersecret", + SecretRef: "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac", + SecretType: "opaque", + Status: "ACTIVE", + Updated: time.Date(2018, 6, 21, 5, 18, 45, 0, time.UTC), +} + +// ExpectedSecretsSlice is the slice of secrets expected to be returned from ListResponse. +var ExpectedSecretsSlice = []secrets.Secret{FirstSecret, SecondSecret} + +// ExpectedCreateResult is the result of a create request +var ExpectedCreateResult = secrets.Secret{ + SecretRef: "http://barbican:9311/v1/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", +} + +const GetMetadataResponse = ` +{ + "metadata": { + "foo": "bar", + "something": "something else" + } +}` + +// ExpectedMetadata is the result of a Get or Create request. +var ExpectedMetadata = map[string]string{ + "foo": "bar", + "something": "something else", +} + +const CreateMetadataRequest = ` +{ + "metadata": { + "foo": "bar", + "something": "something else" + } +}` + +const CreateMetadataResponse = ` +{ + "metadata_ref": "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac/metadata" +}` + +// ExpectedCreateMetadataResult is the result of a Metadata create request. +var ExpectedCreateMetadataResult = map[string]string{ + "metadata_ref": "http://barbican:9311/v1/secrets/1b12b69a-8822-442e-a303-da24ade648ac/metadata", +} + +const MetadatumRequest = ` +{ + "key": "foo", + "value": "bar" +}` + +const MetadatumResponse = ` +{ + "key": "foo", + "value": "bar" +}` + +// ExpectedMetadatum is the result of a Metadatum Get, Create, or Update +// request +var ExpectedMetadatum = secrets.Metadatum{ + Key: "foo", + Value: "bar", +} + +// HandleListSecretsSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that responds with a list of two secrets. +func HandleListSecretsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets", 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, ListResponse) + }) +} + +// HandleGetSecretSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that responds with a single secret. +func HandleGetSecretSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", 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, GetResponse) + }) +} + +// HandleGetPayloadSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that responds with a single secret. +func HandleGetPayloadSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/payload", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetPayloadResponse) + }) +} + +// HandleCreateSecretSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret creation. +func HandleCreateSecretSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets", 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, CreateResponse) + }) +} + +// HandleDeleteSecretSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret deletion. +func HandleDeleteSecretSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", 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) + }) +} + +// HandleUpdateSecretSuccessfully creates an HTTP handler at `/secrets` on the +// test handler mux that tests secret updates. +func HandleUpdateSecretSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleGetMetadataSuccessfully creates an HTTP handler at +// `/secrets/uuid/metadata` on the test handler mux that responds with +// retrieved metadata. +func HandleGetMetadataSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata", 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, GetMetadataResponse) + }) +} + +// HandleCreateMetadataSuccessfully creates an HTTP handler at +// `/secrets/uuid/metadata` on the test handler mux that responds with +// a metadata reference URL. +func HandleCreateMetadataSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, CreateMetadataRequest) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateMetadataResponse) + }) +} + +// HandleGetMetadatumSuccessfully creates an HTTP handler at +// `/secrets/uuid/metadata/foo` on the test handler mux that responds with a +// single metadatum. +func HandleGetMetadatumSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata/foo", 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, MetadatumResponse) + }) +} + +// HandleCreateMetadatumSuccessfully creates an HTTP handler at +// `/secrets/uuid/metadata` on the test handler mux that responds with +// a single created metadata. +func HandleCreateMetadatumSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, MetadatumRequest) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, MetadatumResponse) + }) +} + +// HandleUpdateMetadatumSuccessfully creates an HTTP handler at +// `/secrets/uuid/metadata/foo` on the test handler mux that responds with a +// single updated metadatum. +func HandleUpdateMetadatumSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata/foo", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestJSONRequest(t, r, MetadatumRequest) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, MetadatumResponse) + }) +} + +// HandleDeleteMetadatumSuccessfully creates an HTTP handler at +// `/secrets/uuid/metadata/key` on the test handler mux that tests metadata +// deletion. +func HandleDeleteMetadatumSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c/metadata/foo", 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/keymanager/v1/secrets/testing/requests_test.go b/openstack/keymanager/v1/secrets/testing/requests_test.go new file mode 100644 index 0000000000..1dca431261 --- /dev/null +++ b/openstack/keymanager/v1/secrets/testing/requests_test.go @@ -0,0 +1,179 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListSecrets(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSecretsSuccessfully(t) + + count := 0 + err := secrets.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := secrets.ExtractSecrets(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedSecretsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListSecretsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSecretsSuccessfully(t) + + allPages, err := secrets.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := secrets.ExtractSecrets(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedSecretsSlice, actual) +} + +func TestGetSecret(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSecretSuccessfully(t) + + actual, err := secrets.Get(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, FirstSecret, *actual) +} + +func TestCreateSecret(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSecretSuccessfully(t) + + createOpts := secrets.CreateOpts{ + Algorithm: "aes", + BitLength: 256, + Mode: "cbc", + Name: "mysecret", + Payload: "foobar", + PayloadContentType: "text/plain", + SecretType: secrets.OpaqueSecret, + } + + actual, err := secrets.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedCreateResult, *actual) +} + +func TestDeleteSecret(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSecretSuccessfully(t) + + res := secrets.Delete(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateSecret(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateSecretSuccessfully(t) + + updateOpts := secrets.UpdateOpts{ + Payload: "foobar", + } + + err := secrets.Update(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestGetPayloadSecret(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetPayloadSuccessfully(t) + + res := secrets.GetPayload(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", nil) + th.AssertNoErr(t, res.Err) + payload, err := res.Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, GetPayloadResponse, string(payload)) +} + +func TestGetMetadataSuccessfully(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetMetadataSuccessfully(t) + + actual, err := secrets.GetMetadata(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedMetadata, actual) +} + +func TestCreateMetadataSuccessfully(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateMetadataSuccessfully(t) + + createOpts := secrets.MetadataOpts{ + "foo": "bar", + "something": "something else", + } + + actual, err := secrets.CreateMetadata(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", createOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCreateMetadataResult, actual) +} + +func TestGetMetadatumSuccessfully(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetMetadatumSuccessfully(t) + + actual, err := secrets.GetMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "foo").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedMetadatum, *actual) +} + +func TestCreateMetadatumSuccessfully(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateMetadatumSuccessfully(t) + + createOpts := secrets.MetadatumOpts{ + Key: "foo", + Value: "bar", + } + + err := secrets.CreateMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", createOpts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUpdateMetadatumSuccessfully(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateMetadatumSuccessfully(t) + + updateOpts := secrets.MetadatumOpts{ + Key: "foo", + Value: "bar", + } + + actual, err := secrets.UpdateMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedMetadatum, *actual) +} + +func TestDeleteMetadatumSuccessfully(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteMetadatumSuccessfully(t) + + err := secrets.DeleteMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "foo").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/keymanager/v1/secrets/urls.go b/openstack/keymanager/v1/secrets/urls.go new file mode 100644 index 0000000000..ebd636a66e --- /dev/null +++ b/openstack/keymanager/v1/secrets/urls.go @@ -0,0 +1,35 @@ +package secrets + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("secrets") +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("secrets", id) +} + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("secrets") +} + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("secrets", id) +} + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("secrets", id) +} + +func payloadURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("secrets", id, "payload") +} + +func metadataURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("secrets", id, "metadata") +} + +func metadatumURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("secrets", id, "metadata", key) +} From 06b9695ee9d97389d4f773e9d05346e5078573da Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 21 Jun 2018 00:59:03 +0000 Subject: [PATCH 0447/2296] Docs: adding template --- docs/contributor-tutorial/.template/doc.go | 13 ++ .../.template/requests.go | 105 ++++++++++++++++ .../contributor-tutorial/.template/results.go | 83 ++++++++++++ .../.template/testing/fixtures.go | 118 ++++++++++++++++++ .../.template/testing/requests_test.go | 89 +++++++++++++ docs/contributor-tutorial/.template/urls.go | 23 ++++ .../step-05-pull-requests.md | 6 + 7 files changed, 437 insertions(+) create mode 100644 docs/contributor-tutorial/.template/doc.go create mode 100644 docs/contributor-tutorial/.template/requests.go create mode 100644 docs/contributor-tutorial/.template/results.go create mode 100644 docs/contributor-tutorial/.template/testing/fixtures.go create mode 100644 docs/contributor-tutorial/.template/testing/requests_test.go create mode 100644 docs/contributor-tutorial/.template/urls.go diff --git a/docs/contributor-tutorial/.template/doc.go b/docs/contributor-tutorial/.template/doc.go new file mode 100644 index 0000000000..bbf39c5428 --- /dev/null +++ b/docs/contributor-tutorial/.template/doc.go @@ -0,0 +1,13 @@ +/* +Package NAME manages and retrieves RESOURCE in the OpenStack SERVICE Service. + +Example to List RESOURCE + +Example to Create a RESOURCE + +Example to Update a RESOURCE + +Example to Delete a RESOURCE + +*/ +package RESOURCE diff --git a/docs/contributor-tutorial/.template/requests.go b/docs/contributor-tutorial/.template/requests.go new file mode 100644 index 0000000000..1c4bb4d3e9 --- /dev/null +++ b/docs/contributor-tutorial/.template/requests.go @@ -0,0 +1,105 @@ +package RESOURCE + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToResourceListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { +} + +// ToResourceListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToResourceListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List retrieves a list of RESOURCES. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToResourceListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ResourcePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details of a RESOURCE. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToResourceCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a RESOURCE. +type CreateOpts struct { +} + +// ToResourceCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToResourceCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "resource") +} + +// Create creates a new RESOURCE. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToResourceCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// Delete deletes a RESOURCE. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToResourceUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents parameters to update a RESOURCE. +type UpdateOpts struct { +} + +// ToUpdateCreateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToResourceUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "resource") +} + +// Update modifies the attributes of a RESOURCE. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToResourceUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/docs/contributor-tutorial/.template/results.go b/docs/contributor-tutorial/.template/results.go new file mode 100644 index 0000000000..88272e1e1c --- /dev/null +++ b/docs/contributor-tutorial/.template/results.go @@ -0,0 +1,83 @@ +package RESOURCE + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// RESOURCE represents... +type Resource struct { +} + +type commonResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a RESOURCE. +type GetResult struct { + commonResult +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a RESOURCE. +type CreateResult 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 +} + +// UpdateResult is the result of an Update request. Call its Extract method to +// interpret it as a RESOURCE. +type UpdateResult struct { + commonResult +} + +// ResourcePage is a single page of RESOURCE results. +type ResourcePage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of RESOURCES contains any results. +func (r ResourcePage) IsEmpty() (bool, error) { + resources, err := ExtractResources(r) + return len(resources) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r ResourcePage) 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 +} + +// ExtractResources returns a slice of Resources contained in a single page of +// results. +func ExtractResources(r pagination.Page) ([]Resource, error) { + var s struct { + Resources []Resource `json:"resources"` + } + err := (r.(ResourcePage)).ExtractInto(&s) + return s.Resources, err +} + +// Extract interprets any commonResult as a Resource. +func (r commonResult) Extract() (*Resource, error) { + var s struct { + Resource *Resource `json:"resource"` + } + err := r.ExtractInto(&s) + return s.Resource, err +} diff --git a/docs/contributor-tutorial/.template/testing/fixtures.go b/docs/contributor-tutorial/.template/testing/fixtures.go new file mode 100644 index 0000000000..66e2a202a2 --- /dev/null +++ b/docs/contributor-tutorial/.template/testing/fixtures.go @@ -0,0 +1,118 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/service/vN/resources" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListResult provides a single page of RESOURCE results. +const ListResult = ` +{ +} +` + +// GetResult provides a Get result. +const GetResult = ` +{ +} +` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ +} +` + +// UpdateRequest provides the input to as Update request. +const UpdateRequest = ` +{ +} +` + +// UpdateResult provides an update result. +const UpdateResult = ` +{ +} +` + +// FirstResource is the first resource in the List request. +var FirstResource = resources.Resource{} + +// SecondResource is the second resource in the List request. +var SecondResource = resources.Resource{} + +// SecondResourceUpdated is how SecondResource should look after an Update. +var SecondResourceUpdated = resources.Resource{} + +// ExpectedResourcesSlice is the slice of resources expected to be returned from ListResult. +var ExpectedResourcesSlice = []resources.Resource{FirstResource, SecondResource} + +// HandleListResourceSuccessfully creates an HTTP handler at `/resources` on the +// test handler mux that responds with a list of two resources. +func HandleListResourceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/resources", 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, ListResult) + }) +} + +// HandleGetResourceSuccessfully creates an HTTP handler at `/resources` on the +// test handler mux that responds with a single resource. +func HandleGetResourceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/resources/9fe1d3", 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, GetResult) + }) +} + +// HandleCreateResourceSuccessfully creates an HTTP handler at `/resources` on the +// test handler mux that tests resource creation. +func HandleCreateResourceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/resources", 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, GetResult) + }) +} + +// HandleDeleteResourceSuccessfully creates an HTTP handler at `/resources` on the +// test handler mux that tests resource deletion. +func HandleDeleteResourceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/resources/9fe1d3", 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) + }) +} + +// HandleUpdateResourceSuccessfully creates an HTTP handler at `/resources` on the +// test handler mux that tests resource update. +func HandleUpdateResourceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/resources/9fe1d3", 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, UpdateResult) + }) +} diff --git a/docs/contributor-tutorial/.template/testing/requests_test.go b/docs/contributor-tutorial/.template/testing/requests_test.go new file mode 100644 index 0000000000..4d294c8a27 --- /dev/null +++ b/docs/contributor-tutorial/.template/testing/requests_test.go @@ -0,0 +1,89 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/service/vN/resources" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListResources(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListResourcesSuccessfully(t) + + count := 0 + err := resources.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := resources.ExtractResources(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedResourcesSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListResourcesAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListResourcesSuccessfully(t) + + allPages, err := resources.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := resources.ExtractResources(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedResourcesSlice, actual) +} + +func TestGetResource(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetResourceSuccessfully(t) + + actual, err := resources.Get(client.ServiceClient(), "9fe1d3").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondResource, *actual) +} + +func TestCreateResource(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateResourceSuccessfully(t) + + createOpts := resources.CreateOpts{ + Name: "resource two", + } + + actual, err := resources.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondResource, *actual) +} + +func TestDeleteResource(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteResourceSuccessfully(t) + + res := resources.Delete(client.ServiceClient(), "9fe1d3") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateResource(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateResourceSuccessfully(t) + + updateOpts := resources.UpdateOpts{ + Description: "Staging Resource", + } + + actual, err := resources.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondResourceUpdated, *actual) +} diff --git a/docs/contributor-tutorial/.template/urls.go b/docs/contributor-tutorial/.template/urls.go new file mode 100644 index 0000000000..603b8124c9 --- /dev/null +++ b/docs/contributor-tutorial/.template/urls.go @@ -0,0 +1,23 @@ +package RESOURCE + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("resource") +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("resource", id) +} + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("resource") +} + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("resource", id) +} + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("resource", id) +} diff --git a/docs/contributor-tutorial/step-05-pull-requests.md b/docs/contributor-tutorial/step-05-pull-requests.md index 9bf1e0a4d8..cb340a92ca 100644 --- a/docs/contributor-tutorial/step-05-pull-requests.md +++ b/docs/contributor-tutorial/step-05-pull-requests.md @@ -58,6 +58,12 @@ The following Pull Requests are good examples of how to do this: * https://github.com/gophercloud/gophercloud/pull/587 * https://github.com/gophercloud/gophercloud/pull/594 +You can also use the provided [template](/docs/contributor-tutorial/.template) +as it contains a lot of the repeated boiler plate code seen in each resource. +However, please make sure to thoroughly review and edit it as needed. +Leaving templated portions in-place might be interpreted as rushing through +the work and will require further rounds of review to fix. + ### Adding an Entire OpenStack Project To add an entire OpenStack project, you must break each set of API calls into From 7bdf4337cb3d5a8eca9895544a00a0aeb1f37933 Mon Sep 17 00:00:00 2001 From: Aya Igarashi Date: Fri, 29 Jun 2018 10:37:15 +0900 Subject: [PATCH 0448/2296] Add ProvisioningStatus fields to /v2/loadbalancer (#1075) * Add ProvisioningStatus fields to /v2/loadbalancer * remove unappropriate comments --- .../loadbalancer/v2/listeners/results.go | 4 +++ .../v2/loadbalancers/testing/fixtures.go | 32 ++++++++++++------- openstack/loadbalancer/v2/monitors/results.go | 4 +++ openstack/loadbalancer/v2/pools/results.go | 8 +++++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 1051c69b9b..3e98423270 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -54,6 +54,10 @@ type Listener struct { // Pools are the pools which are part of this listener. Pools []pools.Pool `json:"pools"` + + // The provisioning status of the Listener. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` } // ListenerPage is the page returned by a pager when traversing over a diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index f2d1f65ca9..d042f07344 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -102,18 +102,22 @@ const LoadbalancerStatuesesTree = ` "listeners": [{ "id": "db902c0c-d5ff-4753-b465-668ad9656918", "name": "db", + "provisioning_status": "ACTIVE", "pools": [{ "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "name": "db", + "provisioning_status": "ACTIVE", "healthmonitor": { "id": "67306cda-815d-4354-9fe4-59e09da9c3c5", - "type":"PING" + "type":"PING", + "provisioning_status": "ACTIVE" }, "members":[{ "id": "2a280670-c202-4b0b-a562-34077415aabf", "name": "db", "address": "10.0.2.11", - "protocol_port": 80 + "protocol_port": 80, + "provisioning_status": "ACTIVE" }] }] }] @@ -171,20 +175,24 @@ var ( ProvisioningStatus: "PENDING_UPDATE", OperatingStatus: "ACTIVE", Listeners: []listeners.Listener{{ - ID: "db902c0c-d5ff-4753-b465-668ad9656918", - Name: "db", + ID: "db902c0c-d5ff-4753-b465-668ad9656918", + Name: "db", + ProvisioningStatus: "ACTIVE", Pools: []pools.Pool{{ - ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", - Name: "db", + ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + Name: "db", + ProvisioningStatus: "ACTIVE", Monitor: monitors.Monitor{ - ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", - Type: "PING", + ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", + Type: "PING", + ProvisioningStatus: "ACTIVE", }, Members: []pools.Member{{ - ID: "2a280670-c202-4b0b-a562-34077415aabf", - Name: "db", - Address: "10.0.2.11", - ProtocolPort: 80, + ID: "2a280670-c202-4b0b-a562-34077415aabf", + Name: "db", + Address: "10.0.2.11", + ProtocolPort: 80, + ProvisioningStatus: "ACTIVE", }}, }}, }}, diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index f66579e979..ccf7d98509 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -70,6 +70,10 @@ type Monitor struct { // List of pools that are associated with the health monitor. Pools []PoolID `json:"pools"` + + // The provisioning status of the Monitor. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` } // MonitorPage is the page returned by a pager when traversing over a diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index 40ba5a528f..fe0d93325f 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -92,6 +92,10 @@ type Pool struct { // The Monitor associated with this Pool. Monitor monitors.Monitor `json:"healthmonitor"` + + // The provisioning status of the pool. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` } // PoolPage is the page returned by a pager when traversing over a @@ -196,6 +200,10 @@ type Member struct { // The unique ID for the Member. ID string `json:"id"` + + // The provisioning status of the pool. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` } // MemberPage is the page returned by a pager when traversing over a From 0357c094d765ae5942172c73fb912677ff1c0112 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 29 Jun 2018 10:41:56 -0700 Subject: [PATCH 0449/2296] Changed from Cluster to ClusterID --- openstack/clustering/v1/receivers/results.go | 2 +- openstack/clustering/v1/receivers/testing/fixtures.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index fabb008c9f..9299921586 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -13,7 +13,7 @@ type Receiver struct { Action string `json:"action"` Actor map[string]interface{} `json:"actor"` Channel map[string]interface{} `json:"channel"` - Cluster string `json:"cluster_id"` + ClusterID string `json:"cluster_id"` CreatedAt time.Time `json:"-"` Domain string `json:"domain"` ID string `json:"id"` diff --git a/openstack/clustering/v1/receivers/testing/fixtures.go b/openstack/clustering/v1/receivers/testing/fixtures.go index 08712c8b95..498d162dd8 100644 --- a/openstack/clustering/v1/receivers/testing/fixtures.go +++ b/openstack/clustering/v1/receivers/testing/fixtures.go @@ -48,7 +48,7 @@ var ExpectedReceiver = receivers.Receiver{ Channel: map[string]interface{}{ "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", }, - Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", CreatedAt: time.Date(2015, 11, 4, 5, 21, 41, 0, time.UTC), Domain: "Default", ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", @@ -126,7 +126,7 @@ var ExpectedUpdateReceiver = receivers.Receiver{ Channel: map[string]interface{}{ "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", }, - Cluster: "ae63a10b-4a90-452c-aef1-113a0b255ee3", + ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", CreatedAt: time.Date(2015, 6, 27, 5, 9, 43, 0, time.UTC), Domain: "Default", ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", From 198064c9bbb308ca90ce2d87253acc45785f1bcd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 29 Jun 2018 20:19:07 +0000 Subject: [PATCH 0450/2296] Identity v3: Fixing Role Assignments unit test --- .../identity/v3/roles/testing/fixtures.go | 41 ++++++++++++++++++- .../v3/roles/testing/requests_test.go | 34 +++++++++++---- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index fc56220bc8..9bcc2c7d07 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -371,7 +371,7 @@ var RoleOnResource = roles.Role{ // from ListAssignmentsOnResourceOutput. var ExpectedRolesOnResourceSlice = []roles.Role{RoleOnResource} -func HandleListAssignmentsOnResourceSuccessfully(t *testing.T) { +func HandleListAssignmentsOnResourceSuccessfully_ProjectsUsers(t *testing.T) { fn := func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "Accept", "application/json") @@ -383,7 +383,46 @@ func HandleListAssignmentsOnResourceSuccessfully(t *testing.T) { } th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles", fn) +} + +func HandleListAssignmentsOnResourceSuccessfully_ProjectsGroups(t *testing.T) { + fn := 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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListAssignmentsOnResourceOutput) + } + th.Mux.HandleFunc("/projects/{project_id}/groups/{group_id}/roles", fn) +} + +func HandleListAssignmentsOnResourceSuccessfully_DomainsUsers(t *testing.T) { + fn := 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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListAssignmentsOnResourceOutput) + } + th.Mux.HandleFunc("/domains/{domain_id}/users/{user_id}/roles", fn) +} + +func HandleListAssignmentsOnResourceSuccessfully_DomainsGroups(t *testing.T) { + fn := 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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListAssignmentsOnResourceOutput) + } + th.Mux.HandleFunc("/domains/{domain_id}/groups/{group_id}/roles", fn) } diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 8372c4ff88..c8ac5a9b03 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -147,10 +147,10 @@ func TestListAssignmentsSinglePage(t *testing.T) { th.CheckEquals(t, count, 1) } -func TestListAssignmentsOnResource(t *testing.T) { +func TestListAssignmentsOnResource_ProjectsUsers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - HandleListAssignmentsOnResourceSuccessfully(t) + HandleListAssignmentsOnResourceSuccessfully_ProjectsUsers(t) count := 0 err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ @@ -167,9 +167,15 @@ func TestListAssignmentsOnResource(t *testing.T) { }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) +} - count = 0 - err = roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ +func TestListAssignmentsOnResource_DomainsUsers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAssignmentsOnResourceSuccessfully_DomainsUsers(t) + + count := 0 + err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ UserID: "{user_id}", DomainID: "{domain_id}", }).EachPage(func(page pagination.Page) (bool, error) { @@ -183,9 +189,15 @@ func TestListAssignmentsOnResource(t *testing.T) { }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) +} + +func TestListAssignmentsOnResource_ProjectsGroups(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAssignmentsOnResourceSuccessfully_ProjectsGroups(t) - count = 0 - err = roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ + count := 0 + err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ GroupID: "{group_id}", ProjectID: "{project_id}", }).EachPage(func(page pagination.Page) (bool, error) { @@ -199,9 +211,15 @@ func TestListAssignmentsOnResource(t *testing.T) { }) th.AssertNoErr(t, err) th.CheckEquals(t, count, 1) +} - count = 0 - err = roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ +func TestListAssignmentsOnResource_DomainsGroups(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAssignmentsOnResourceSuccessfully_DomainsGroups(t) + + count := 0 + err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).EachPage(func(page pagination.Page) (bool, error) { From 7410ec1f8fd7318de7fbdd22ab357794697ed07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Va=C5=A1ek?= Date: Fri, 29 Jun 2018 23:51:37 +0200 Subject: [PATCH 0451/2296] Add revoke access to Manila (#1077) * manila: added RevokeAccess * manila: added unit and acceptance tests for RevokeAccess * manila: fixed revoke access unit test --- .../openstack/sharedfilesystems/v2/shares.go | 8 ++++ .../sharedfilesystems/v2/shares_test.go | 25 +++++++++++++ .../sharedfilesystems/v2/shares/requests.go | 37 +++++++++++++++++++ .../sharedfilesystems/v2/shares/results.go | 5 +++ .../v2/shares/testing/fixtures.go | 18 +++++++++ .../v2/shares/testing/request_test.go | 16 ++++++++ openstack/sharedfilesystems/v2/shares/urls.go | 4 ++ 7 files changed, 113 insertions(+) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index 5a3cc333b7..f7d12c327c 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -53,6 +53,14 @@ func GrantAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares. }).Extract() } +// RevokeAccess will revoke an exisiting access of a share. A fatal error will occur +// if this operation fails. +func RevokeAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, accessRight *shares.AccessRight) error { + return shares.RevokeAccess(client, share.ID, shares.RevokeAccessOpts{ + AccessID: accessRight.ID, + }).ExtractErr() +} + // GetAccessRightsSlice will retrieve all access rules assigned to a share. // A fatal error will occur if this operation fails. func GetAccessRightsSlice(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) ([]shares.AccessRight, error) { diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 9de10a5252..dcfa046e23 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -27,6 +27,31 @@ func TestShareCreate(t *testing.T) { PrintShare(t, created) } +func TestGrantAndRevokeAccess(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a sharedfs client: %v", err) + } + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + accessRight, err := GrantAccess(t, client, share) + if err != nil { + t.Fatalf("Unable to grant access: %v", err) + } + + PrintAccessRight(t, accessRight) + + if err = RevokeAccess(t, client, share, accessRight); err != nil { + t.Fatalf("Unable to revoke access: %v", err) + } +} + func TestListAccessRights(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 3a4bda096d..38c861c7f1 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -124,6 +124,43 @@ func GrantAccess(client *gophercloud.ServiceClient, id string, opts GrantAccessO return } +// RevokeAccessOptsBuilder allows extensions to add additional parameters to the +// RevokeAccess request. +type RevokeAccessOptsBuilder interface { + ToRevokeAccessMap() (map[string]interface{}, error) +} + +// RevokeAccessOpts contains the options for creation of a RevokeAccess request. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Share Actions, Revoke Access documentation +type RevokeAccessOpts struct { + AccessID string `json:"access_id"` +} + +// ToRevokeAccessMap assembles a request body based on the contents of a +// RevokeAccessOpts. +func (opts RevokeAccessOpts) ToRevokeAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "deny_access") +} + +// RevokeAccess will revoke an existing access to a Share based on the values in RevokeAccessOpts. +// RevokeAccessResult contains only the error. To extract it, call the ExtractErr method on +// the RevokeAccessResult. Client must have Microversion set; minimum supported microversion +// for RevokeAccess is 2.7. +func RevokeAccess(client *gophercloud.ServiceClient, id string, opts RevokeAccessOptsBuilder) (r RevokeAccessResult) { + b, err := opts.ToRevokeAccessMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(revokeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + + return +} + // ListAccessRights lists all access rules assigned to a Share based on its id. To extract // the AccessRight slice from the response, call the Extract method on the ListAccessRightsResult. // Client must have Microversion set; minimum supported microversion for ListAccessRights is 2.7. diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 1df7af21ab..6d875e13e5 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -178,6 +178,11 @@ type GrantAccessResult struct { gophercloud.Result } +// RevokeAccessResult contains the response body and error from a Revoke access request. +type RevokeAccessResult struct { + gophercloud.ErrResult +} + // Extract will get a slice of AccessRight objects from the commonResult func (r ListAccessRightsResult) Extract() ([]AccessRight, error) { var s struct { diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index dc725b9e07..1a653eac27 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -198,6 +198,24 @@ func MockGrantAccessResponse(t *testing.T) { }) } +var revokeAccessRequest = `{ + "deny_access": { + "access_id": "a2f226a5-cee8-430b-8a03-78a59bd84ee8" + } +}` + +func MockRevokeAccessResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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, revokeAccessRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) +} + var listAccessRightsRequest = `{ "access_list": null }` diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index e31cb1a6d3..2b6376dcaa 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -136,6 +136,22 @@ func TestGrantAcessSuccess(t *testing.T) { }) } +func TestRevokeAccessSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockRevokeAccessResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Revoke Access is 2.7 + c.Microversion = "2.7" + + options := &shares.RevokeAccessOpts{AccessID: "a2f226a5-cee8-430b-8a03-78a59bd84ee8"} + + err := shares.RevokeAccess(c, shareID, options).ExtractErr() + th.AssertNoErr(t, err) +} + func TestListAccessRightsSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index 5fa5b9c69c..e0cf824550 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -22,6 +22,10 @@ func grantAccessURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } +func revokeAccessURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} + func listAccessRightsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } From 4d436eada7b3ce3cbcd8ca818525dc0efd79ebef Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Mon, 2 Jul 2018 17:58:39 -0400 Subject: [PATCH 0452/2296] [WIP] Enable Octavia and run its acceptance tests (#1012) https://github.com/theopenlab/openlab-zuul-jobs/issues/143 --- .zuul/playbooks/gophercloud-acceptance-test/run.yaml | 1 + script/acceptancetest | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index 8509b7d348..ce8635a440 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -8,6 +8,7 @@ - 'manila' - 'designate' - 'zun' + - 'octavia' - install-devstack tasks: - name: Run acceptance tests with gophercloud diff --git a/script/acceptancetest b/script/acceptancetest index c26ed456b6..abcf7a3d1f 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -46,6 +46,12 @@ if [[ $? != 0 ]]; then failed=1 fi +# LoadBalancer v2 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/loadbalancer/v2/ +if [[ $? != 0 ]]; then + failed=1 +fi + # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then exit 1 From f24499fe134d183da822d93315eda27880c7ecbe Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Jul 2018 03:50:48 +0000 Subject: [PATCH 0453/2296] Acc Tests: Disabling Octavia --- script/acceptancetest | 6 ------ 1 file changed, 6 deletions(-) diff --git a/script/acceptancetest b/script/acceptancetest index abcf7a3d1f..c26ed456b6 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -46,12 +46,6 @@ if [[ $? != 0 ]]; then failed=1 fi -# LoadBalancer v2 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/loadbalancer/v2/ -if [[ $? != 0 ]]; then - failed=1 -fi - # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then exit 1 From 374582fb6b1cf6e02a1f3b717eb7982189384357 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Jul 2018 18:45:45 +0000 Subject: [PATCH 0454/2296] Core: Modifying BaseEndpoint to remove entire url suffix --- openstack/utils/base_endpoint.go | 9 +++++---- openstack/utils/testing/base_endpoint_test.go | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/openstack/utils/base_endpoint.go b/openstack/utils/base_endpoint.go index d6f9e34eaa..a846731790 100644 --- a/openstack/utils/base_endpoint.go +++ b/openstack/utils/base_endpoint.go @@ -18,11 +18,12 @@ func BaseEndpoint(endpoint string) (string, error) { u.RawQuery, u.Fragment = "", "" + base = u.String() versionRe := regexp.MustCompile("v[0-9.]+/?") - if version := versionRe.FindString(u.Path); version != "" { - base = strings.Replace(u.String(), version, "", -1) - } else { - base = u.String() + + if version := versionRe.FindString(base); version != "" { + versionIndex := strings.Index(base, version) + base = base[:versionIndex] } return base, nil diff --git a/openstack/utils/testing/base_endpoint_test.go b/openstack/utils/testing/base_endpoint_test.go index 5a8152db2c..d67306ae40 100644 --- a/openstack/utils/testing/base_endpoint_test.go +++ b/openstack/utils/testing/base_endpoint_test.go @@ -46,6 +46,14 @@ func TestBaseEndpoint(t *testing.T) { Endpoint: "http://example.com/identity/v2.0", BaseEndpoint: "http://example.com/identity/", }, + { + Endpoint: "http://example.com/identity/v2.0/projects", + BaseEndpoint: "http://example.com/identity/", + }, + { + Endpoint: "http://example.com/v2.0/projects", + BaseEndpoint: "http://example.com/", + }, { Endpoint: "http://example.com/identity/", BaseEndpoint: "http://example.com/identity/", From 8cf44bf1749da68b76be9eb59a37b45641b8fd63 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Tue, 3 Jul 2018 18:55:14 -0700 Subject: [PATCH 0455/2296] Senlin: ProfileTypes Get/List (#1057) * senlin-profiletypes-get * Created Schema and SupportStatus types Renamed TestGetProfileType1 to TestGetProfileType10 for uVersion=1.0 --- .../clustering/v1/profiletypes_test.go | 29 +++ openstack/clustering/v1/profiletypes/doc.go | 29 +++ .../clustering/v1/profiletypes/requests.go | 21 ++ .../clustering/v1/profiletypes/results.go | 53 ++++ .../clustering/v1/profiletypes/testing/doc.go | 2 + .../v1/profiletypes/testing/fixtures.go | 244 ++++++++++++++++++ .../v1/profiletypes/testing/requests_test.go | 55 ++++ openstack/clustering/v1/profiletypes/urls.go | 24 ++ 8 files changed, 457 insertions(+) create mode 100644 acceptance/openstack/clustering/v1/profiletypes_test.go create mode 100644 openstack/clustering/v1/profiletypes/doc.go create mode 100644 openstack/clustering/v1/profiletypes/requests.go create mode 100644 openstack/clustering/v1/profiletypes/results.go create mode 100644 openstack/clustering/v1/profiletypes/testing/doc.go create mode 100644 openstack/clustering/v1/profiletypes/testing/fixtures.go create mode 100644 openstack/clustering/v1/profiletypes/testing/requests_test.go create mode 100644 openstack/clustering/v1/profiletypes/urls.go diff --git a/acceptance/openstack/clustering/v1/profiletypes_test.go b/acceptance/openstack/clustering/v1/profiletypes_test.go new file mode 100644 index 0000000000..2928b2bbcd --- /dev/null +++ b/acceptance/openstack/clustering/v1/profiletypes_test.go @@ -0,0 +1,29 @@ +// +build acceptance clustering profiletypes + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestProfileTypesList(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.5" + + allPages, err := profiletypes.List(client).AllPages() + th.AssertNoErr(t, err) + + allProfileTypes, err := profiletypes.ExtractProfileTypes(allPages) + th.AssertNoErr(t, err) + + for _, profileType := range allProfileTypes { + tools.PrintResource(t, profileType) + } +} diff --git a/openstack/clustering/v1/profiletypes/doc.go b/openstack/clustering/v1/profiletypes/doc.go new file mode 100644 index 0000000000..622ca7da73 --- /dev/null +++ b/openstack/clustering/v1/profiletypes/doc.go @@ -0,0 +1,29 @@ +/* +Package profiletypes lists all profile types and shows details for a profile type from the OpenStack +Clustering Service. + +Example to List ProfileType + + err = profiletypes.List(serviceClient).EachPage(func(page pagination.Page) (bool, error) { + profileTypes, err := profiletypes.ExtractProfileTypes(page) + if err != nil { + return false, err + } + + for _, profileType := range profileTypes { + fmt.Println("%+v\n", profileType) + } + return true, nil + }) + +Example to Get a ProfileType + + profileTypeName := "os.nova.server" + profileType, err := profiletypes.Get(clusteringClient, profileTypeName).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", profileType) + +*/ +package profiletypes diff --git a/openstack/clustering/v1/profiletypes/requests.go b/openstack/clustering/v1/profiletypes/requests.go new file mode 100644 index 0000000000..fc4f53610f --- /dev/null +++ b/openstack/clustering/v1/profiletypes/requests.go @@ -0,0 +1,21 @@ +package profiletypes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, + &gophercloud.RequestOpts{OkCodes: []int{200}}) + + return +} + +// List makes a request against the API to list profile types. +func List(client *gophercloud.ServiceClient) pagination.Pager { + url := listURL(client) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ProfileTypePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/clustering/v1/profiletypes/results.go b/openstack/clustering/v1/profiletypes/results.go new file mode 100644 index 0000000000..f20284d5eb --- /dev/null +++ b/openstack/clustering/v1/profiletypes/results.go @@ -0,0 +1,53 @@ +package profiletypes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + +type Schema map[string]interface{} +type SupportStatus map[string]interface{} + +type ProfileType struct { + Name string `json:"name"` + Schema map[string]Schema `json:"schema"` + SupportStatus map[string][]SupportStatus `json:"support_status"` +} + +func (r commonResult) Extract() (*ProfileType, error) { + var s struct { + ProfileType *ProfileType `json:"profile_type"` + } + err := r.ExtractInto(&s) + return s.ProfileType, err +} + +// ExtractProfileTypes provides access to the list of profiles in a page acquired from the List operation. +func ExtractProfileTypes(r pagination.Page) ([]ProfileType, error) { + var s struct { + ProfileTypes []ProfileType `json:"profile_types"` + } + err := (r.(ProfileTypePage)).ExtractInto(&s) + return s.ProfileTypes, err +} + +// ProfileTypePage contains a single page of all profiles from a ListDetails call. +type ProfileTypePage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if ExtractProfileTypes contains any results. +func (page ProfileTypePage) IsEmpty() (bool, error) { + profileTypes, err := ExtractProfileTypes(page) + return len(profileTypes) == 0, err +} diff --git a/openstack/clustering/v1/profiletypes/testing/doc.go b/openstack/clustering/v1/profiletypes/testing/doc.go new file mode 100644 index 0000000000..7890e62543 --- /dev/null +++ b/openstack/clustering/v1/profiletypes/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_profiletypes_v1 +package testing diff --git a/openstack/clustering/v1/profiletypes/testing/fixtures.go b/openstack/clustering/v1/profiletypes/testing/fixtures.go new file mode 100644 index 0000000000..3ea52dc4a0 --- /dev/null +++ b/openstack/clustering/v1/profiletypes/testing/fixtures.go @@ -0,0 +1,244 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ProfileTypeRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" +const ListResponse = ` +{ + "profile_types": [ + { + "name": "os.nova.server-1.0", + "schema": { + "context": { + "description": "Customized security context for operating containers.", + "required": false, + "type": "Map", + "updatable": false + }, + "name": { + "description": "The name of the container.", + "required": false, + "type": "Map", + "updatable": false + } + } + }, + { + "name": "os.heat.stack-1.0", + "schema": { + "context": { + "default": {}, + "description": "A dictionary for specifying the customized context for stack operations", + "required": false, + "type": "Map", + "updatable": false + }, + "disable_rollback": { + "default": true, + "description": "A boolean specifying whether a stack operation can be rolled back.", + "required": false, + "type": "Boolean", + "updatable": true + }, + "environment": { + "default": {}, + "description": "A map that specifies the environment used for stack operations.", + "required": false, + "type": "Map", + "updatable": true + }, + "files": { + "default": {}, + "description": "Contents of files referenced by the template, if any.", + "required": false, + "type": "Map", + "updatable": true + } + }, + "support_status": { + "1.0": [ + { + "status": "SUPPORTED", + "since": "2016.04" + } + ] + } + } + ] +} +` + +const GetResponse1 = ` +{ + "profile_type": { + "name": "os.nova.server-1.0", + "schema": { + "context": { + "description": "Customized security context for operating containers.", + "required": false, + "type": "Map", + "updatable": false + }, + "name": { + "description": "The name of the container.", + "required": false, + "type": "Map", + "updatable": false + } + } + } +} +` + +const GetResponse15 = ` +{ + "profile_type": { + "name": "os.heat.stack-1.0", + "schema": { + "context": { + "default": {}, + "description": "A dictionary for specifying the customized context for stack operations", + "required": false, + "type": "Map", + "updatable": false + }, + "disable_rollback": { + "default": true, + "description": "A boolean specifying whether a stack operation can be rolled back.", + "required": false, + "type": "Boolean", + "updatable": true + }, + "environment": { + "default": {}, + "description": "A map that specifies the environment used for stack operations.", + "required": false, + "type": "Map", + "updatable": true + }, + "files": { + "default": {}, + "description": "Contents of files referenced by the template, if any.", + "required": false, + "type": "Map", + "updatable": true + } + }, + "support_status": { + "1.0": [ + { + "status": "SUPPORTED", + "since": "2016.04" + } + ] + } + } +} +` + +var ExpectedProfileType1 = profiletypes.ProfileType{ + Name: "os.nova.server-1.0", + Schema: map[string]profiletypes.Schema{ + "context": { + "description": "Customized security context for operating containers.", + "required": false, + "type": "Map", + "updatable": false, + }, + "name": { + "description": "The name of the container.", + "required": false, + "type": "Map", + "updatable": false, + }, + }, +} + +var ExpectedProfileType15 = profiletypes.ProfileType{ + Name: "os.heat.stack-1.0", + Schema: map[string]profiletypes.Schema{ + "context": { + "default": map[string]interface{}{}, + "description": "A dictionary for specifying the customized context for stack operations", + "required": false, + "type": "Map", + "updatable": false, + }, + "disable_rollback": { + "default": true, + "description": "A boolean specifying whether a stack operation can be rolled back.", + "required": false, + "type": "Boolean", + "updatable": true, + }, + "environment": { + "default": map[string]interface{}{}, + "description": "A map that specifies the environment used for stack operations.", + "required": false, + "type": "Map", + "updatable": true, + }, + "files": { + "default": map[string]interface{}{}, + "description": "Contents of files referenced by the template, if any.", + "required": false, + "type": "Map", + "updatable": true, + }, + }, + SupportStatus: map[string][]profiletypes.SupportStatus{ + "1.0": { + { + "status": "SUPPORTED", + "since": "2016.04", + }, + }, + }, +} + +var ExpectedProfileTypes = []profiletypes.ProfileType{ExpectedProfileType1, ExpectedProfileType15} + +func HandleList1Successfully(t *testing.T) { + th.Mux.HandleFunc("/v1/profile-types", 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, ListResponse) + }) +} + +func HandleGet1Successfully(t *testing.T, id string) { + th.Mux.HandleFunc("/v1/profile-types/"+id, 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.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, GetResponse1) + }) +} + +func HandleGet15Successfully(t *testing.T, id string) { + th.Mux.HandleFunc("/v1/profile-types/"+id, 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.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, GetResponse15) + }) +} diff --git a/openstack/clustering/v1/profiletypes/testing/requests_test.go b/openstack/clustering/v1/profiletypes/testing/requests_test.go new file mode 100644 index 0000000000..c4cc7b28c9 --- /dev/null +++ b/openstack/clustering/v1/profiletypes/testing/requests_test.go @@ -0,0 +1,55 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListProfileTypes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleList1Successfully(t) + + pageCount := 0 + err := profiletypes.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + pageCount++ + actual, err := profiletypes.ExtractProfileTypes(page) + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, ExpectedProfileTypes, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if pageCount != 1 { + t.Errorf("Expected 1 page, got %d", pageCount) + } +} + +func TestGetProfileType10(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGet1Successfully(t, ExpectedProfileType1.Name) + + actual, err := profiletypes.Get(fake.ServiceClient(), ExpectedProfileType1.Name).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedProfileType1, *actual) +} + +func TestGetProfileType15(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGet15Successfully(t, ExpectedProfileType15.Name) + + actual, err := profiletypes.Get(fake.ServiceClient(), ExpectedProfileType15.Name).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedProfileType15, *actual) +} diff --git a/openstack/clustering/v1/profiletypes/urls.go b/openstack/clustering/v1/profiletypes/urls.go new file mode 100644 index 0000000000..0a9bfca3f4 --- /dev/null +++ b/openstack/clustering/v1/profiletypes/urls.go @@ -0,0 +1,24 @@ +package profiletypes + +import "github.com/gophercloud/gophercloud" + +const ( + apiVersion = "v1" + apiName = "profile-types" +) + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} + +func profileTypeURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return profileTypeURL(client, id) +} + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From c6aebc73e7fd6841a00c3031cb595813ae651a7a Mon Sep 17 00:00:00 2001 From: Aya Igarashi Date: Wed, 4 Jul 2018 17:46:47 +0900 Subject: [PATCH 0456/2296] Add ProvisioningStatus fields to lbaas_v2 models in the extensions pkg --- .../extensions/lbaas_v2/listeners/results.go | 4 +++ .../loadbalancers/testing/fixtures.go | 32 ++++++++++++------- .../extensions/lbaas_v2/monitors/results.go | 4 +++ .../v2/extensions/lbaas_v2/pools/results.go | 8 +++++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go index e0c134ed51..81cda90361 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go @@ -54,6 +54,10 @@ type Listener struct { // Pools are the pools which are part of this listener. Pools []pools.Pool `json:"pools"` + + // The provisioning status of the listener. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` } // ListenerPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go index a452236566..2bc949b926 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go @@ -102,18 +102,22 @@ const LoadbalancerStatuesesTree = ` "listeners": [{ "id": "db902c0c-d5ff-4753-b465-668ad9656918", "name": "db", + "provisioning_status": "PENDING_UPDATE", "pools": [{ "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "name": "db", + "provisioning_status": "PENDING_UPDATE", "healthmonitor": { "id": "67306cda-815d-4354-9fe4-59e09da9c3c5", - "type":"PING" + "type":"PING", + "provisioning_status": "PENDING_UPDATE" }, "members":[{ "id": "2a280670-c202-4b0b-a562-34077415aabf", "name": "db", "address": "10.0.2.11", - "protocol_port": 80 + "protocol_port": 80, + "provisioning_status": "PENDING_UPDATE" }] }] }] @@ -171,20 +175,24 @@ var ( ProvisioningStatus: "PENDING_UPDATE", OperatingStatus: "ACTIVE", Listeners: []listeners.Listener{{ - ID: "db902c0c-d5ff-4753-b465-668ad9656918", - Name: "db", + ID: "db902c0c-d5ff-4753-b465-668ad9656918", + Name: "db", + ProvisioningStatus: "PENDING_UPDATE", Pools: []pools.Pool{{ - ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", - Name: "db", + ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + Name: "db", + ProvisioningStatus: "PENDING_UPDATE", Monitor: monitors.Monitor{ - ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", - Type: "PING", + ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", + Type: "PING", + ProvisioningStatus: "PENDING_UPDATE", }, Members: []pools.Member{{ - ID: "2a280670-c202-4b0b-a562-34077415aabf", - Name: "db", - Address: "10.0.2.11", - ProtocolPort: 80, + ID: "2a280670-c202-4b0b-a562-34077415aabf", + Name: "db", + Address: "10.0.2.11", + ProtocolPort: 80, + ProvisioningStatus: "PENDING_UPDATE", }}, }}, }}, diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go index ea832cc5d0..a78f7aeb0f 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go @@ -70,6 +70,10 @@ type Monitor struct { // List of pools that are associated with the health monitor. Pools []PoolID `json:"pools"` + + // The provisioning status of the monitor. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` } // MonitorPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go index 56790fff99..01efc768c2 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go @@ -92,6 +92,10 @@ type Pool struct { // The Monitor associated with this Pool. Monitor monitors.Monitor `json:"healthmonitor"` + + // The provisioning status of the pool. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` } // PoolPage is the page returned by a pager when traversing over a @@ -196,6 +200,10 @@ type Member struct { // The unique ID for the Member. ID string `json:"id"` + + // The provisioning status of the member. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` } // MemberPage is the page returned by a pager when traversing over a From c66c8a67be8282e1769621fdd40284c9de63f12d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 5 Jul 2018 05:23:14 +0000 Subject: [PATCH 0457/2296] Core: Fixing issue with extractIntoPtr and Slices --- .../provider/testing/results_test.go | 3 +++ .../v2/networks/testing/requests_test.go | 2 ++ results.go | 26 +++++++++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go index fa8eb8e1f3..709f830627 100644 --- a/openstack/networking/v2/extensions/provider/testing/results_test.go +++ b/openstack/networking/v2/extensions/provider/testing/results_test.go @@ -43,6 +43,9 @@ func TestList(t *testing.T) { th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", actual[1].ID) th.AssertEquals(t, "local", actual[1].NetworkType) th.AssertEquals(t, "1234567890", actual[1].SegmentationID) + th.AssertEquals(t, actual[0].Subnets[0], "54d6f61d-db07-451c-9ab3-b9609b6b6f0b") + th.AssertEquals(t, actual[1].Subnets[0], "08eae331-0402-425a-923c-34f7cfe39c1b") + } func TestGet(t *testing.T) { diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 231d7f087c..adf88d59ba 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -78,6 +78,8 @@ func TestListWithExtensions(t *testing.T) { th.AssertEquals(t, allNetworks[0].Status, "ACTIVE") th.AssertEquals(t, allNetworks[0].PortSecurityEnabled, true) + th.AssertEquals(t, allNetworks[0].Subnets[0], "54d6f61d-db07-451c-9ab3-b9609b6b6f0b") + th.AssertEquals(t, allNetworks[1].Subnets[0], "08eae331-0402-425a-923c-34f7cfe39c1b") } func TestGet(t *testing.T) { diff --git a/results.go b/results.go index c5f245c152..30d3b15599 100644 --- a/results.go +++ b/results.go @@ -89,23 +89,45 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { if typeOfV.Kind() == reflect.Struct { if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) - newType := reflect.New(typeOfV).Elem() for _, v := range m[label].([]interface{}) { + // For each iteration of the slice, we create a new struct. + // This is to work around a bug where elements of a slice + // are reused and not overwritten when the same copy of the + // struct is used: + // + // https://github.com/golang/go/issues/21092 + // https://github.com/golang/go/issues/24155 + // https://play.golang.org/p/NHo3ywlPZli + newType := reflect.New(typeOfV).Elem() + b, err := json.Marshal(v) if err != nil { return err } + // This is needed for structs with an UnmarshalJSON method. + // Technically this is just unmarshalling the response into + // a struct that is never used, but it's good enough to + // trigger the UnmarshalJSON method. for i := 0; i < newType.NumField(); i++ { s := newType.Field(i).Addr().Interface() - err = json.NewDecoder(bytes.NewReader(b)).Decode(s) + + // Unmarshal is used rather than NewDecoder to also work + // around the above-mentioned bug. + err = json.Unmarshal(b, s) if err != nil { return err } } + newSlice = reflect.Append(newSlice, newType) } + + // "to" should now be properly modeled to receive the + // JSON response body and unmarshal into all the correct + // fields of the struct or composed extension struct + // at the end of this method. toValue.Set(newSlice) } } From 95043e339e96acfed86ae6d51df76b7bfc23a172 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 22 Jun 2018 11:20:29 -0700 Subject: [PATCH 0458/2296] senlin-profiletypes-ops --- .../clustering/v1/profiletypes_test.go | 18 +++++++ openstack/clustering/v1/profiletypes/doc.go | 18 +++++++ .../clustering/v1/profiletypes/requests.go | 7 +++ .../clustering/v1/profiletypes/results.go | 14 +++++ .../v1/profiletypes/testing/fixtures.go | 52 +++++++++++++++++++ .../v1/profiletypes/testing/requests_test.go | 19 +++++++ openstack/clustering/v1/profiletypes/urls.go | 4 ++ 7 files changed, 132 insertions(+) diff --git a/acceptance/openstack/clustering/v1/profiletypes_test.go b/acceptance/openstack/clustering/v1/profiletypes_test.go index 2928b2bbcd..039f926f5a 100644 --- a/acceptance/openstack/clustering/v1/profiletypes_test.go +++ b/acceptance/openstack/clustering/v1/profiletypes_test.go @@ -27,3 +27,21 @@ func TestProfileTypesList(t *testing.T) { tools.PrintResource(t, profileType) } } +func TestProfileTypesOpsList(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.5" + + profileTypeName := "os.nova.server-1.0" + allPages, err := profiletypes.ListOps(client, profileTypeName).AllPages() + th.AssertNoErr(t, err) + + ops, err := profiletypes.ExtractOps(allPages) + th.AssertNoErr(t, err) + + for k, v := range ops { + tools.PrintResource(t, k) + tools.PrintResource(t, v) + } +} diff --git a/openstack/clustering/v1/profiletypes/doc.go b/openstack/clustering/v1/profiletypes/doc.go index 622ca7da73..39877ace88 100644 --- a/openstack/clustering/v1/profiletypes/doc.go +++ b/openstack/clustering/v1/profiletypes/doc.go @@ -25,5 +25,23 @@ 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" + allPages, err := profiletypes.ListOps(serviceClient, profileTypeName).AllPages() + if err != nil { + panic(err) + } + + ops, err := profiletypes.ExtractOps(allPages) + if err != nil { + panic(err) + } + + for _, op := range ops { + fmt.Printf("%+v\n", op) + } + */ package profiletypes diff --git a/openstack/clustering/v1/profiletypes/requests.go b/openstack/clustering/v1/profiletypes/requests.go index fc4f53610f..37fe031339 100644 --- a/openstack/clustering/v1/profiletypes/requests.go +++ b/openstack/clustering/v1/profiletypes/requests.go @@ -19,3 +19,10 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { return ProfileTypePage{pagination.LinkedPageBase{PageResult: r}} }) } + +func ListOps(client *gophercloud.ServiceClient, id string) pagination.Pager { + url := listOpsURL(client, id) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return OperationPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/clustering/v1/profiletypes/results.go b/openstack/clustering/v1/profiletypes/results.go index f20284d5eb..3259f68ac3 100644 --- a/openstack/clustering/v1/profiletypes/results.go +++ b/openstack/clustering/v1/profiletypes/results.go @@ -51,3 +51,17 @@ func (page ProfileTypePage) IsEmpty() (bool, error) { profileTypes, err := ExtractProfileTypes(page) return len(profileTypes) == 0, err } + +// OperationPage contains a single page of all profile type operations from a ListOps call. +type OperationPage struct { + pagination.SinglePageBase +} + +// ExtractOps provides access to the list of operations in a page acquired from the ListOps operation. +func ExtractOps(r pagination.Page) (map[string]interface{}, error) { + var s struct { + Operations map[string]interface{} `json:"operations"` + } + err := (r.(OperationPage)).ExtractInto(&s) + return s.Operations, err +} diff --git a/openstack/clustering/v1/profiletypes/testing/fixtures.go b/openstack/clustering/v1/profiletypes/testing/fixtures.go index 3ea52dc4a0..64a112fff6 100644 --- a/openstack/clustering/v1/profiletypes/testing/fixtures.go +++ b/openstack/clustering/v1/profiletypes/testing/fixtures.go @@ -242,3 +242,55 @@ func HandleGet15Successfully(t *testing.T, id string) { fmt.Fprintf(w, GetResponse15) }) } + +const ProfileTypeName = "os.nova.server-1.0" + +const ListOpsResponse = ` +{ + "operations": { + "pause": { + "description": "Pause the server from running.", + "parameter": null + }, + "change_password": { + "description": "Change the administrator password.", + "parameters": { + "admin_pass": { + "description": "New password for the administrator.", + "required": false, + "type": "String" + } + } + } + } +} +` + +var ExpectedOps = map[string]interface{}{ + "change_password": map[string]interface{}{ + "description": "Change the administrator password.", + "parameters": map[string]interface{}{ + "admin_pass": map[string]interface{}{ + "description": "New password for the administrator.", + "required": false, + "type": "String", + }, + }, + }, + "pause": map[string]interface{}{ + "description": "Pause the server from running.", + "parameter": nil, + }, +} + +func HandleListOpsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/profile-types/"+ProfileTypeName+"/ops", 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.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListOpsResponse) + }) +} diff --git a/openstack/clustering/v1/profiletypes/testing/requests_test.go b/openstack/clustering/v1/profiletypes/testing/requests_test.go index c4cc7b28c9..c4f379b645 100644 --- a/openstack/clustering/v1/profiletypes/testing/requests_test.go +++ b/openstack/clustering/v1/profiletypes/testing/requests_test.go @@ -3,6 +3,7 @@ package testing import ( "testing" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -53,3 +54,21 @@ func TestGetProfileType15(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedProfileType15, *actual) } + +func TestListProfileTypesOps(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleListOpsSuccessfully(t) + + allPages, err := profiletypes.ListOps(fake.ServiceClient(), ProfileTypeName).AllPages() + th.AssertNoErr(t, err) + + allPolicyTypes, err := profiletypes.ExtractOps(allPages) + th.AssertNoErr(t, err) + + for k, v := range allPolicyTypes { + tools.PrintResource(t, k) + tools.PrintResource(t, v) + } +} diff --git a/openstack/clustering/v1/profiletypes/urls.go b/openstack/clustering/v1/profiletypes/urls.go index 0a9bfca3f4..cec8bfe46a 100644 --- a/openstack/clustering/v1/profiletypes/urls.go +++ b/openstack/clustering/v1/profiletypes/urls.go @@ -22,3 +22,7 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func listOpsURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "ops") +} From 3dbe241dc30bce6171e15b4922ce860f00ff72e5 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Thu, 5 Jul 2018 17:24:02 +0000 Subject: [PATCH 0459/2296] Convert type of 'command' from string to list The field 'command' of container resource is changed from string to list [1]. This change should not break API consumer if they pin a specific API version. However, Gophercloud doesn't support API microversion yet, so the CI is complaining. This patch attempts to fix it. [1] https://review.openstack.org/#/c/575709/ --- openstack/container/v1/capsules/results.go | 2 +- openstack/container/v1/capsules/testing/fixtures.go | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index 38b3ff4f63..a6e56b9940 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -196,7 +196,7 @@ type Container struct { ImageDriver string `json:"image_driver"` // Command for the container - Command string `json:"command"` + Command []string `json:"command"` // Capsule ID for the container CapsuleID int `json:"capsule_id"` diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 7d8b2eac85..411cad3941 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -266,7 +266,7 @@ const CapsuleGetBody_OldTime = ` }, "ports": [80], "meta": {"key1": "value1"}, - "command": "testcmd", + "command": ["testcmd"], "capsule_id": 1, "runtime": "runc", "cpu": 1, @@ -366,7 +366,7 @@ const CapsuleGetBody_NewTime = ` }, "ports": [80], "meta": {"key1": "value1"}, - "command": "testcmd", + "command": ["testcmd"], "capsule_id": 1, "runtime": "runc", "cpu": 1, @@ -446,7 +446,9 @@ var ExpectedContainer1 = capsules.Container{ WorkDir: "/root", Disk: 0, ContainerID: "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", - Command: "testcmd", + Command: []string{ + "testcmd", + }, Ports: []int{ 80, }, From 229dbb896bea6bdd5e73ff6b8617af453b86c1d2 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Thu, 5 Jul 2018 12:00:02 -0700 Subject: [PATCH 0460/2296] senlin-policies-get --- .../openstack/clustering/v1/policies_test.go | 5 ++ openstack/clustering/v1/policies/doc.go | 10 +++ openstack/clustering/v1/policies/requests.go | 11 ++++ openstack/clustering/v1/policies/results.go | 6 ++ .../v1/policies/testing/fixtures.go | 65 +++++++++++++++++++ .../v1/policies/testing/requests_test.go | 12 ++++ openstack/clustering/v1/policies/urls.go | 4 ++ 7 files changed, 113 insertions(+) diff --git a/acceptance/openstack/clustering/v1/policies_test.go b/acceptance/openstack/clustering/v1/policies_test.go index 8cd6833309..3d58351567 100644 --- a/acceptance/openstack/clustering/v1/policies_test.go +++ b/acceptance/openstack/clustering/v1/policies_test.go @@ -36,6 +36,11 @@ func TestPoliciesCRUD(t *testing.T) { th.AssertEquals(t, found, true) + // Test Get policy + getPolicy, err := policies.Get(client, policy.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getPolicy) + // Test updating policy updateOpts := policies.UpdateOpts{ Name: policy.Name + "-UPDATE", diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index ba5ae7b0d4..ffe75f6e13 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -48,6 +48,16 @@ Example to Create a Policy panic(err) } +Example to Get a Policy + + policyName := "get_policy" + policyDetail, err := policies.Get(clusteringClient, policyName).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", policyDetail) + Example to Update a Policy opts := policies.UpdateOpts{ diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index 4224fe8ace..f3e2ad249b 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -180,3 +180,14 @@ func Validate(client *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r Va } return } + +// Get makes a request against the API to get details for a policy. +func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { + url := policyGetURL(client, policyTypeName) + + _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + + return +} diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index 67d7002311..ca5bf335b3 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -115,6 +115,12 @@ type CreateResult struct { policyResult } +// GetResult is the result of a Get operation. Call its Extract method to +// interpret it as a Policy. +type GetResult struct { + policyResult +} + // UpdateResult is the result of an Update operation. Call its Extract // method to interpret it as a Policy. type UpdateResult struct { diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go index f514214ee3..726fde9861 100644 --- a/openstack/clustering/v1/policies/testing/fixtures.go +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -99,6 +99,33 @@ const PolicyCreateBody = ` } ` +const PolicyGetBody = ` +{ + "policy": { + "created_at": "2018-04-02T21:43:30.000000", + "data": {}, + "domain": null, + "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + "name": "delpol", + "project": "018cd0909fb44cd5bc9b7a3cd664920e", + "spec": { + "description": "A policy for choosing victim node(s) from a cluster for deletion.", + "properties": { + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": true, + "grace_period": 60, + "reduce_desired_capacity": false + }, + "type": "senlin.policy.deletion", + "version": 1 + }, + "type": "senlin.policy.deletion-1.0", + "updated_at": "2018-04-02T00:19:12Z", + "user": "fe43e41739154b72818565e0d2580819" + } +} +` + const PolicyUpdateBody = ` { "policy": { @@ -221,6 +248,7 @@ const PolicyBadValidateBody = ` const PolicyDeleteRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" const PolicyIDtoUpdate = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" +const PolicyIDtoGet = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" const PolicyIDtoDelete = "1" var ExpectedPolicy1 = policies.Policy{ @@ -302,6 +330,30 @@ var ExpectedCreatePolicy = policies.Policy{ User: "fe43e41739154b72818565e0d2580819", } +var ExpectedGetPolicy = policies.Policy{ + CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), + Data: map[string]interface{}{}, + Domain: "", + ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + Name: "delpol", + Project: "018cd0909fb44cd5bc9b7a3cd664920e", + + Spec: policies.Spec{ + Description: "A policy for choosing victim node(s) from a cluster for deletion.", + Properties: map[string]interface{}{ + "criteria": "OLDEST_FIRST", + "destroy_after_deletion": true, + "grace_period": float64(60), + "reduce_desired_capacity": false, + }, + Type: "senlin.policy.deletion", + Version: "1.0", + }, + Type: "senlin.policy.deletion-1.0", + User: "fe43e41739154b72818565e0d2580819", + UpdatedAt: time.Date(2018, 4, 2, 0, 19, 12, 0, time.UTC), +} + var ExpectedUpdatePolicy = policies.Policy{ CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), Data: map[string]interface{}{}, @@ -399,6 +451,19 @@ func HandlePolicyDelete(t *testing.T) { }) } +func HandlePolicyGet(t *testing.T) { + th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoGet, + 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, PolicyGetBody) + }) +} + func HandlePolicyUpdate(t *testing.T) { th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoUpdate, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PATCH") diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go index 7a6fe726fe..3f74bf0b67 100644 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -133,3 +133,15 @@ func TestBadValidatePolicy(t *testing.T) { _, err := policies.Validate(fake.ServiceClient(), opts).Extract() th.AssertEquals(t, false, err == nil) } + +func TestGetPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandlePolicyGet(t) + + actual, err := policies.Get(fake.ServiceClient(), PolicyIDtoGet).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, ExpectedGetPolicy, *actual) +} diff --git a/openstack/clustering/v1/policies/urls.go b/openstack/clustering/v1/policies/urls.go index b8a1ee1c56..d558ab0dcc 100644 --- a/openstack/clustering/v1/policies/urls.go +++ b/openstack/clustering/v1/policies/urls.go @@ -19,6 +19,10 @@ func policyDeleteURL(client *gophercloud.ServiceClient, policyID string) string return client.ServiceURL(apiVersion, apiName, policyID) } +func policyGetURL(client *gophercloud.ServiceClient, policyID string) string { + return client.ServiceURL(apiVersion, apiName, policyID) +} + func updateURL(client *gophercloud.ServiceClient, policyID string) string { return client.ServiceURL(apiVersion, apiName, policyID) } From 19d3956666f33e02b023e9a2299a7d428cd9768a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Va=C5=A1ek?= Date: Fri, 6 Jul 2018 16:59:43 +0200 Subject: [PATCH 0461/2296] Manila: added ListDetail for detailed Share listing (#1095) * manila: added ListDetail for detailed listing of Shares * manila: added unit tests for ListDetail * manila: added an acceptance test for ListDetail * manila: results: removed a debug import * manila: requests: added a couple of fields to ListOpts * manila: SharePage.NextPageURL returns an empty URL upon invalid marker --- .../openstack/sharedfilesystems/v2/shares.go | 11 ++ .../sharedfilesystems/v2/shares_test.go | 23 ++++ .../sharedfilesystems/v2/shares/requests.go | 100 +++++++++++++++++- .../sharedfilesystems/v2/shares/results.go | 87 +++++++++++++++ .../v2/shares/testing/fixtures.go | 68 ++++++++++++ .../v2/shares/testing/request_test.go | 56 ++++++++++ openstack/sharedfilesystems/v2/shares/urls.go | 4 + 7 files changed, 347 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index f7d12c327c..0bd55089fd 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -43,6 +43,17 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share return share, nil } +// ListShares lists all shares that belong to this tenant's project. +// An error will be returned if the shares could not be listed.. +func ListShares(t *testing.T, client *gophercloud.ServiceClient) ([]shares.Share, error) { + r, err := shares.ListDetail(client, &shares.ListOpts{}).AllPages() + if err != nil { + return nil, err + } + + return shares.ExtractShares(r) +} + // GrantAccess will grant access to an existing share. A fatal error will occur if // this operation fails. func GrantAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) (*shares.AccessRight, error) { diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index dcfa046e23..7aea6d0e15 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -27,6 +27,29 @@ func TestShareCreate(t *testing.T) { PrintShare(t, created) } +func TestShareListDetail(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a sharedfs client: %v", err) + } + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + ss, err := ListShares(t, client) + if err != nil { + t.Fatalf("Unable to list shares: %v", err) + } + + for i := range ss { + PrintShare(t, &ss[i]) + } +} + func TestGrantAndRevokeAccess(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 38c861c7f1..b6ba572a50 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -1,6 +1,9 @@ package shares -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. @@ -24,7 +27,7 @@ type CreateOpts struct { // DisplayName is equivalent to Name. The API supports using both // This is an inherited attribute from the block storage API DisplayName string `json:"display_name,omitempty"` - // DisplayDescription is equivalent to Description. The API supports using bot + // DisplayDescription is equivalent to Description. The API supports using both // This is an inherited attribute from the block storage API DisplayDescription string `json:"display_description,omitempty"` // ShareType defines the sharetype. If omitted, a default share type is used @@ -66,6 +69,99 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// ListOpts holds options for listing Shares. It is passed to the +// shares.List function. +type ListOpts struct { + // (Admin only). Defines whether to list the requested resources for all projects. + AllTenants bool `q:"all_tenants"` + // The share name. + Name string `q:"name"` + // Filters by a share status. + Status string `q:"status"` + // The UUID of the share server. + ShareServerID string `q:"share_server_id"` + // One or more metadata key and value pairs as a dictionary of strings. + Metadata map[string]string `q:"metadata"` + // The extra specifications for the share type. + ExtraSpecs map[string]string `q:"extra_specs"` + // The UUID of the share type. + ShareTypeID string `q:"share_type_id"` + // The maximum number of shares to return. + Limit int `q:"limit"` + // The offset to define start point of share or share group listing. + Offset int `q:"offset"` + // The key to sort a list of shares. + SortKey string `q:"sort_key"` + // The direction to sort a list of shares. + SortDir string `q:"sort_dir"` + // The UUID of the share’s base snapshot to filter the request based on. + SnapshotID string `q:"snapshot_id"` + // The share host name. + Host string `q:"host"` + // The share network ID. + ShareNetworkID string `q:"share_network_id"` + // The UUID of the project in which the share was created. Useful with all_tenants parameter. + ProjectID string `q:"project_id"` + // The level of visibility for the share. + IsPublic *bool `q:"is_public"` + // The UUID of a share group to filter resource. + ShareGroupID string `q:"share_group_id"` + // The export location UUID that can be used to filter shares or share instances. + ExportLocationID string `q:"export_location_id"` + // The export location path that can be used to filter shares or share instances. + ExportLocationPath string `q:"export_location_path"` + // The name pattern that can be used to filter shares, share snapshots, share networks or share groups. + NamePattern string `q:"name~"` + // The description pattern that can be used to filter shares, share snapshots, share networks or share groups. + DescriptionPattern string `q:"description~"` + // Whether to show count in API response or not, default is False. + WithCount bool `q:"with_count"` + // DisplayName is equivalent to Name. The API supports using both + // This is an inherited attribute from the block storage API + DisplayName string `q:"display_name"` + // Equivalent to NamePattern. + DisplayNamePattern string `q:"display_name~"` + // VolumeTypeID is deprecated but supported. Either ShareTypeID or VolumeTypeID can be used + VolumeTypeID string `q:"volume_type_id"` + // The UUID of the share group snapshot. + ShareGroupSnapshotID string `q:"share_group_snapshot_id"` + // DisplayDescription is equivalent to Description. The API supports using both + // This is an inherited attribute from the block storage API + DisplayDescription string `q:"display_description"` + // Equivalent to DescriptionPattern + DisplayDescriptionPattern string `q:"display_description~"` +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToShareListQuery() (string, error) +} + +// ToShareListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToShareListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDetail returns []Share 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.ToShareListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + p := SharePage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + // Delete will delete an existing Share with the given UUID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 6d875e13e5..6a40a33cfa 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -2,9 +2,16 @@ package shares import ( "encoding/json" + "net/url" + "strconv" "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +const ( + invalidMarker = "-1" ) // Share contains all information associated with an OpenStack Share @@ -101,6 +108,86 @@ type CreateResult struct { commonResult } +// SharePage is a pagination.pager that is returned from a call to the List function. +type SharePage struct { + pagination.MarkerPageBase +} + +// NextPageURL generates the URL for the page of results after this one. +func (r SharePage) 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 SharePage) LastMarker() (string, error) { + shares, err := ExtractShares(r) + if err != nil { + return invalidMarker, err + } + if len(shares) == 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 SharePage) IsEmpty() (bool, error) { + shares, err := ExtractShares(r) + return len(shares) == 0, err +} + +// ExtractShares extracts and returns a Share slice. It is used while +// iterating over a shares.List call. +func ExtractShares(r pagination.Page) ([]Share, error) { + var s struct { + Shares []Share `json:"shares"` + } + + err := (r.(SharePage)).ExtractInto(&s) + + return s.Shares, err +} + // DeleteResult contains the response body and error from a Delete request. type DeleteResult struct { gophercloud.ErrResult diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 1a653eac27..b581d44e99 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -142,6 +142,73 @@ func MockGetResponse(t *testing.T) { }) } +var listDetailResponse = `{ + "shares": [ + { + "links": [ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", + "rel": "self" + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", + "rel": "bookmark" + } + ], + "availability_zone": "nova", + "share_network_id": "713df749-aac0-4a54-af52-10f6c991e80c", + "share_server_id": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", + "snapshot_id": null, + "id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", + "size": 1, + "share_type": "25747776-08e5-494f-ab40-a64b9d20d8f7", + "share_type_name": "default", + "consistency_group_id": "9397c191-8427-4661-a2e8-b23820dc01d4", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "metadata": { + "project": "my_app", + "aim": "doc" + }, + "status": "available", + "description": "My custom share London", + "host": "manila2@generic1#GENERIC1", + "has_replicas": false, + "replication_type": null, + "task_state": null, + "is_public": true, + "snapshot_support": true, + "name": "my_test_share", + "created_at": "2015-09-18T10:25:24.000000", + "share_proto": "NFS", + "volume_type": "default", + "source_cgsnapshot_member_id": null + } + ] + }` + +var listDetailEmptyResponse = `{"shares": []}` + +// MockDeleteResponse 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) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + r.ParseForm() + marker := r.Form.Get("offset") + + switch marker { + case "": + fmt.Fprint(w, listDetailResponse) + default: + fmt.Fprint(w, listDetailEmptyResponse) + } + }) +} + var getExportLocationsResponse = `{ "export_locations": [ { @@ -159,6 +226,7 @@ func MockGetExportLocationsResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, getExportLocationsResponse) }) diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index 2b6376dcaa..cdd218dde3 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -83,6 +83,62 @@ func TestGet(t *testing.T) { }) } +func TestListDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListDetailResponse(t) + + allPages, err := shares.ListDetail(client.ServiceClient(), &shares.ListOpts{}).AllPages() + + th.AssertNoErr(t, err) + + actual, err := shares.ExtractShares(allPages) + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, actual, []shares.Share{ + shares.Share{ + AvailabilityZone: "nova", + ShareNetworkID: "713df749-aac0-4a54-af52-10f6c991e80c", + ShareServerID: "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", + SnapshotID: "", + ID: shareID, + Size: 1, + ShareType: "25747776-08e5-494f-ab40-a64b9d20d8f7", + ShareTypeName: "default", + ConsistencyGroupID: "9397c191-8427-4661-a2e8-b23820dc01d4", + ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", + Metadata: map[string]string{ + "project": "my_app", + "aim": "doc", + }, + Status: "available", + Description: "My custom share London", + Host: "manila2@generic1#GENERIC1", + HasReplicas: false, + ReplicationType: "", + TaskState: "", + SnapshotSupport: true, + Name: "my_test_share", + CreatedAt: time.Date(2015, time.September, 18, 10, 25, 24, 0, time.UTC), + ShareProto: "NFS", + VolumeType: "default", + SourceCgsnapshotMemberID: "", + IsPublic: true, + Links: []map[string]string{ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", + "rel": "self", + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", + "rel": "bookmark", + }, + }, + }, + }) +} + func TestGetExportLocationsSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index e0cf824550..f139e5c90f 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -6,6 +6,10 @@ func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("shares") } +func listDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("shares", "detail") +} + func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id) } From aafa5a8b5ac53d192056c2ef4a0d5ac8d5d6e696 Mon Sep 17 00:00:00 2001 From: gman Date: Fri, 6 Jul 2018 17:17:42 +0200 Subject: [PATCH 0462/2296] manila: added IDFromName for shares --- .../sharedfilesystems/v2/shares/results.go | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 6a40a33cfa..393f3bbc60 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -198,6 +198,28 @@ type GetResult struct { commonResult } +// IDFromName is a convenience function that returns a share's ID given its name. +func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { + r, err := ListDetail(client, &ListOpts{Name: name}).AllPages() + if err != nil { + return "", err + } + + ss, err := ExtractShares(r) + if err != nil { + return "", err + } + + switch len(ss) { + case 0: + return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "share"} + case 1: + return ss[0].ID, nil + default: + return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: len(ss), ResourceType: "share"} + } +} + // GetExportLocationsResult contains the result body and error from an // GetExportLocations request. type GetExportLocationsResult struct { From c0f11e460ea11e687e215c6f37313e67475040e7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Jul 2018 22:40:25 +0000 Subject: [PATCH 0463/2296] Image Service v2: Fixing nextPageURL This commit fixes nextPageURL so that it uses the base portion of the service endpoint URL combined with the link found in the "next" key of the response. This is because the Image Service uses absolute URL paths which might conflict with published endpoints that advertise a service on /images rather than just /. --- openstack/imageservice/v2/images/requests.go | 7 ++++++- openstack/imageservice/v2/images/results.go | 3 ++- openstack/imageservice/v2/images/urls.go | 22 ++++++++++++++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 88cd4d265e..c2cbfb3276 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -129,7 +129,12 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return ImagePage{pagination.LinkedPageBase{PageResult: r}} + imagePage := ImagePage{ + serviceURL: c.ServiceURL(), + LinkedPageBase: pagination.LinkedPageBase{PageResult: r}, + } + + return imagePage }) } diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index e256068844..dbc3c452fa 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -162,6 +162,7 @@ type DeleteResult struct { // ImagePage represents the results of a List request. type ImagePage struct { + serviceURL string pagination.LinkedPageBase } @@ -186,7 +187,7 @@ func (r ImagePage) NextPageURL() (string, error) { return "", nil } - return nextPageURL(r.URL.String(), s.Next) + return nextPageURL(r.serviceURL, s.Next) } // ExtractImages interprets the results of a single page from a List() call, diff --git a/openstack/imageservice/v2/images/urls.go b/openstack/imageservice/v2/images/urls.go index bf7cea1ef8..1780c3c6ca 100644 --- a/openstack/imageservice/v2/images/urls.go +++ b/openstack/imageservice/v2/images/urls.go @@ -2,8 +2,10 @@ package images import ( "net/url" + "strings" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" ) // `listURL` is a pure function. `listURL(c)` is a URL for which a GET @@ -38,14 +40,26 @@ func deleteURL(c *gophercloud.ServiceClient, imageID string) string { } // builds next page full url based on current url -func nextPageURL(currentURL string, next string) (string, error) { - base, err := url.Parse(currentURL) +func nextPageURL(serviceURL, requestedNext string) (string, error) { + base, err := utils.BaseEndpoint(serviceURL) if err != nil { return "", err } - rel, err := url.Parse(next) + + requestedNextURL, err := url.Parse(requestedNext) + if err != nil { + return "", err + } + + base = gophercloud.NormalizeURL(base) + nextPath := base + strings.TrimPrefix(requestedNextURL.Path, "/") + + nextURL, err := url.Parse(nextPath) if err != nil { return "", err } - return base.ResolveReference(rel).String(), nil + + nextURL.RawQuery = requestedNextURL.RawQuery + + return nextURL.String(), nil } From 7fec423ce7a49b2eed71a57331bd969771393daa Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Jul 2018 04:02:03 +0000 Subject: [PATCH 0464/2296] Acc Tests: Enable ImageService v2 testing in OpenLab --- script/acceptancetest | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/acceptancetest b/script/acceptancetest index c26ed456b6..0541c8f497 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -46,6 +46,12 @@ if [[ $? != 0 ]]; then failed=1 fi +# Image Service v2 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ +if [[ $? != 0 ]]; then + failed=1 +fi + # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then exit 1 From dbe8f69e4b4815e69b8992f60496c347e9e381c5 Mon Sep 17 00:00:00 2001 From: dropje86 Date: Sat, 7 Jul 2018 19:23:56 +0200 Subject: [PATCH 0465/2296] Use ReadSeeker to calculate ETag if possible (#1102) * Use ReadSeeker to calculate ETag if possible Addresses issue #1100 If a readseeker is passed as CreateOpts.Content we do not have to copy the entire content into a temporary buffer, we can just reset the offset instead. * Add comments and improve the code slightly --- .../objectstorage/v1/objects/requests.go | 25 ++++++++--- .../v1/objects/testing/requests_test.go | 41 +++++++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index d1f854cf7b..f0d743dc91 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -7,6 +7,7 @@ import ( "crypto/sha1" "fmt" "io" + "io/ioutil" "strings" "time" @@ -189,16 +190,28 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str return opts.Content, h, q.String(), nil } + // When we're dealing with big files an io.ReadSeeker allows us to efficiently calculate + // the md5 sum. An io.Reader is only readable once which means we have to copy the entire + // file content into memory first. + readSeeker, isReadSeeker := opts.Content.(io.ReadSeeker) + if !isReadSeeker { + data, err := ioutil.ReadAll(opts.Content) + if err != nil { + return nil, nil, "", err + } + readSeeker = bytes.NewReader(data) + } + hash := md5.New() - buf := bytes.NewBuffer([]byte{}) - _, err = io.Copy(io.MultiWriter(hash, buf), opts.Content) - if err != nil { + // io.Copy into md5 is very efficient as it's done in small chunks. + if _, err := io.Copy(hash, readSeeker); err != nil { return nil, nil, "", err } - localChecksum := fmt.Sprintf("%x", hash.Sum(nil)) - h["ETag"] = localChecksum + readSeeker.Seek(0, io.SeekStart) + + h["ETag"] = fmt.Sprintf("%x", hash.Sum(nil)) - return buf, h, q.String(), nil + return readSeeker, h, q.String(), nil } // Create is a function that creates a new object or replaces an existing diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 23d6e5731a..23d6dcf977 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -5,6 +5,7 @@ import ( "crypto/md5" "fmt" "io" + "io/ioutil" "strings" "testing" "time" @@ -266,3 +267,43 @@ func TestETag(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, headers["ETag"], localChecksum) } + +func TestObjectCreateParamsWithoutSeek(t *testing.T) { + content := "I do not implement Seek()" + buf := bytes.NewBuffer([]byte(content)) + + createOpts := objects.CreateOpts{Content: buf} + reader, headers, _, err := createOpts.ToObjectCreateParams() + + th.AssertNoErr(t, err) + + _, ok := reader.(io.ReadSeeker) + th.AssertEquals(t, ok, true) + + c, err := ioutil.ReadAll(reader) + th.AssertNoErr(t, err) + + th.AssertEquals(t, content, string(c)) + + _, ok = headers["ETag"] + th.AssertEquals(t, true, ok) +} + +func TestObjectCreateParamsWithSeek(t *testing.T) { + content := "I implement Seek()" + createOpts := objects.CreateOpts{Content: strings.NewReader(content)} + reader, headers, _, err := createOpts.ToObjectCreateParams() + + th.AssertNoErr(t, err) + + _, ok := reader.(io.ReadSeeker) + th.AssertEquals(t, ok, true) + + c, err := ioutil.ReadAll(reader) + th.AssertNoErr(t, err) + + th.AssertEquals(t, content, string(c)) + + _, ok = headers["ETag"] + th.AssertEquals(t, true, ok) +} From c9e87d42efa1978018819b9c1e7f57fea1e6fd4d Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Sat, 7 Jul 2018 18:00:52 +0000 Subject: [PATCH 0466/2296] zun: move 'restart_policy' under 'spec' Zun has moved 'start_policy' to 'spec' [1]. This is the corresponding change in gophercloud. [1] https://review.openstack.org/#/c/571398/ --- acceptance/openstack/container/v1/capsules_test.go | 2 +- openstack/container/v1/capsules/testing/fixtures.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index 2c0fcaa7f2..2696e1c681 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -26,8 +26,8 @@ func TestCapsule(t *testing.T) { }, "name": "template" }, - "restartPolicy": "Always", "spec": { + "restartPolicy": "Always", "containers": [ { "command": [ diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 411cad3941..db861827bb 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -22,8 +22,8 @@ const ValidJSONTemplate = ` }, "name": "template" }, - "restartPolicy": "Always", "spec": { + "restartPolicy": "Always", "containers": [ { "command": [ @@ -65,8 +65,8 @@ metadata: labels: app: web app1: web1 -restartPolicy: Always spec: + restartPolicy: Always containers: - image: ubuntu command: @@ -91,7 +91,6 @@ spec: var ValidJSONTemplateParsed = map[string]interface{}{ "capsuleVersion": "beta", "kind": "capsule", - "restartPolicy": "Always", "metadata": map[string]interface{}{ "name": "template", "labels": map[string]string{ @@ -100,6 +99,7 @@ var ValidJSONTemplateParsed = map[string]interface{}{ }, }, "spec": map[string]interface{}{ + "restartPolicy": "Always", "containers": []map[string]interface{}{ map[string]interface{}{ "image": "ubuntu", @@ -135,7 +135,6 @@ var ValidJSONTemplateParsed = map[string]interface{}{ var ValidYAMLTemplateParsed = map[string]interface{}{ "capsuleVersion": "beta", "kind": "capsule", - "restartPolicy": "Always", "metadata": map[string]interface{}{ "name": "template", "labels": map[string]string{ @@ -144,6 +143,7 @@ var ValidYAMLTemplateParsed = map[string]interface{}{ }, }, "spec": map[interface{}]interface{}{ + "restartPolicy": "Always", "containers": []map[interface{}]interface{}{ map[interface{}]interface{}{ "image": "ubuntu", From 1cd649a2c8c7348f651a2472bf71c68280645e37 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Sat, 7 Jul 2018 22:43:50 +0000 Subject: [PATCH 0467/2296] Zun: remove internal fields from Capsule/Container * Remove the following fields from Capsule: ID * Remove the following fields from Container: ContainerID, WebsocketUrl, WebsocketToken, CapsuleID, Meta, ID. All those fields are for internal usage and should not be exposed via API. There is a plan (https://review.openstack.org/#/c/575610/) to remove those fields from the server side. This is the corresponding change in Gophercloud. --- openstack/container/v1/capsules/results.go | 21 ---------- .../container/v1/capsules/testing/fixtures.go | 40 ++++--------------- 2 files changed, 8 insertions(+), 53 deletions(-) diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index a6e56b9940..dc2068b407 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -44,9 +44,6 @@ type Capsule struct { // UUID for the capsule UUID string `json:"uuid"` - // ID for the capsule - ID int `json:"id"` - // User ID for the capsule UserID string `json:"user_id"` @@ -110,9 +107,6 @@ type Container struct { // UUID for the container UUID string `json:"uuid"` - // ID for the container - ID int `json:"id"` - // User ID for the container UserID string `json:"user_id"` @@ -147,15 +141,6 @@ type Container struct { // other APIs that might want a server reference. Links []interface{} `json:"links"` - // Container ID for the container - ContainerID string `json:"container_id"` - - // Websocket url for the container - WebsocketUrl string `json:"websocket_url"` - - // Websocket token for the container - WebsocketToken string `json:"websocket_token"` - // auto remove flag token for the container AutoRemove bool `json:"auto_remove"` @@ -198,9 +183,6 @@ type Container struct { // Command for the container Command []string `json:"command"` - // Capsule ID for the container - CapsuleID int `json:"capsule_id"` - // Image for the container Runtime string `json:"runtime"` @@ -213,9 +195,6 @@ type Container struct { // Ports information for the container Ports []int `json:"ports"` - // Meta for the container - Meta map[string]string `json:"meta"` - // Security groups for the container SecurityGroups []string `json:"security_groups"` } diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 411cad3941..576f81a110 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -179,7 +179,6 @@ const CapsuleGetBody_OldTime = ` { "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", "status": "Running", - "id": 1, "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "cpu": 1, @@ -239,7 +238,6 @@ const CapsuleGetBody_OldTime = ` "started_at": "2018-01-12 09:37:26+00:00", "workdir": "/root", "disk": 0, - "id": 1, "security_groups": ["default"], "image_pull_policy": "ifnotpresent", "task_state": "Creating", @@ -251,23 +249,18 @@ const CapsuleGetBody_OldTime = ` "memory": "1024M", "status": "Running", "auto_remove": false, - "container_id": "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", - "websocket_url": "ws://10.10.10.10/", "auto_heal": false, "host": "test-host", "image_driver": "docker", "status_detail": "Just created", "status_reason": "No reason", - "websocket_token": "2ba16a5a-552f-422f-b511-bd786102691f", "name": "test-demo-omicron-13", "restart_policy": { "MaximumRetryCount": "0", "Name": "always" }, "ports": [80], - "meta": {"key1": "value1"}, "command": ["testcmd"], - "capsule_id": 1, "runtime": "runc", "cpu": 1, "interactive": true @@ -279,7 +272,6 @@ const CapsuleGetBody_NewTime = ` { "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", "status": "Running", - "id": 1, "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "cpu": 1, @@ -339,7 +331,6 @@ const CapsuleGetBody_NewTime = ` "started_at": "2018-01-12 09:37:26", "workdir": "/root", "disk": 0, - "id": 1, "security_groups": ["default"], "image_pull_policy": "ifnotpresent", "task_state": "Creating", @@ -351,23 +342,18 @@ const CapsuleGetBody_NewTime = ` "memory": "1024M", "status": "Running", "auto_remove": false, - "container_id": "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", - "websocket_url": "ws://10.10.10.10/", "auto_heal": false, "host": "test-host", "image_driver": "docker", "status_detail": "Just created", "status_reason": "No reason", - "websocket_token": "2ba16a5a-552f-422f-b511-bd786102691f", "name": "test-demo-omicron-13", "restart_policy": { "MaximumRetryCount": "0", "Name": "always" }, "ports": [80], - "meta": {"key1": "value1"}, "command": ["testcmd"], - "capsule_id": 1, "runtime": "runc", "cpu": 1, "interactive": true @@ -381,7 +367,6 @@ const CapsuleListBody = ` { "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", "status": "Running", - "id": 1, "user_id": "d33b18c384574fd2a3299447aac285f0", "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", "cpu": 1, @@ -426,8 +411,6 @@ const CapsuleListBody = ` }` var ExpectedContainer1 = capsules.Container{ - CapsuleID: 1, - ID: 1, Name: "test-demo-omicron-13", UUID: "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", UserID: "d33b18c384574fd2a3299447aac285f0", @@ -440,12 +423,8 @@ var ExpectedContainer1 = capsules.Container{ Labels: map[string]string{ "foo": "bar", }, - Meta: map[string]string{ - "key1": "value1", - }, - WorkDir: "/root", - Disk: 0, - ContainerID: "5109ebe2ca595777e994416208bd681b561b25ce493c34a234a1b68457cb53fb", + WorkDir: "/root", + Disk: 0, Command: []string{ "testcmd", }, @@ -462,14 +441,12 @@ var ExpectedContainer1 = capsules.Container{ Environment: map[string]string{ "USER1": "test", }, - WebsocketToken: "2ba16a5a-552f-422f-b511-bd786102691f", - WebsocketUrl: "ws://10.10.10.10/", - StatusReason: "No reason", - StatusDetail: "Just created", - ImageDriver: "docker", - Interactive: true, - AutoRemove: false, - AutoHeal: false, + StatusReason: "No reason", + StatusDetail: "Just created", + ImageDriver: "docker", + Interactive: true, + AutoRemove: false, + AutoHeal: false, RestartPolicy: map[string]string{ "MaximumRetryCount": "0", "Name": "always", @@ -490,7 +467,6 @@ var ExpectedContainer1 = capsules.Container{ var ExpectedCapsule = capsules.Capsule{ UUID: "cc654059-1a77-47a3-bfcf-715bde5aad9e", Status: "Running", - ID: 1, UserID: "d33b18c384574fd2a3299447aac285f0", ProjectID: "6b8ffef2a0ac42ee87887b9cc98bdf68", CPU: float64(1), From 520520f5f12017a2b07aa73bad64151dfdf9fcab Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Sun, 8 Jul 2018 18:00:09 -0400 Subject: [PATCH 0468/2296] Zun: assert capsule become 'Running' after create (#1106) * Zun: assert capsule become 'Running' after create In the Zun acceptance test, add a step to wait for the capsule to become 'Running' after it is created. Fixes: #1098 * Zun: cleanup some incorrect comments --- acceptance/openstack/container/v1/capsules.go | 31 +++++++++++++++++++ .../openstack/container/v1/capsules_test.go | 7 +++-- openstack/container/v1/capsules/requests.go | 2 +- openstack/container/v1/capsules/results.go | 10 +++--- .../container/v1/capsules/testing/fixtures.go | 2 +- .../v1/capsules/testing/requests_test.go | 4 ++- 6 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 acceptance/openstack/container/v1/capsules.go diff --git a/acceptance/openstack/container/v1/capsules.go b/acceptance/openstack/container/v1/capsules.go new file mode 100644 index 0000000000..6bf682cd11 --- /dev/null +++ b/acceptance/openstack/container/v1/capsules.go @@ -0,0 +1,31 @@ +package v1 + +import ( + "fmt" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" +) + +// WaitForCapsuleStatus will poll a capsule's status until it either matches +// the specified status or the status becomes Failed. +func WaitForCapsuleStatus(client *gophercloud.ServiceClient, capsule *capsules.Capsule, status string) error { + return tools.WaitFor(func() (bool, error) { + latest, err := capsules.Get(client, capsule.UUID).Extract() + if err != nil { + return false, err + } + + if latest.Status == status { + // Success! + return true, nil + } + + if latest.Status == "Failed" { + return false, fmt.Errorf("Capsule in FAILED state") + } + + return false, nil + }) +} diff --git a/acceptance/openstack/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index 2696e1c681..48f001df3b 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -31,7 +31,8 @@ func TestCapsule(t *testing.T) { "containers": [ { "command": [ - "/bin/bash" + "sleep", + "1000000" ], "env": { "ENV1": "/usr/local/bin", @@ -61,7 +62,9 @@ func TestCapsule(t *testing.T) { createOpts := capsules.CreateOpts{ TemplateOpts: template, } - err = capsules.Create(client, createOpts).ExtractErr() + capsule, err := capsules.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + err = WaitForCapsuleStatus(client, capsule, "Running") th.AssertNoErr(t, err) pager := capsules.List(client, nil) err = pager.EachPage(func(page pagination.Page) (bool, error) { diff --git a/openstack/container/v1/capsules/requests.go b/openstack/container/v1/capsules/requests.go index 0c606fa3b5..611c27677a 100644 --- a/openstack/container/v1/capsules/requests.go +++ b/openstack/container/v1/capsules/requests.go @@ -80,7 +80,7 @@ func (opts ListOpts) ToCapsuleListQuery() (string, error) { return q.String(), err } -// List makes a request against the API to list servers accessible to you. +// List makes a request against the API to list capsules accessible to you. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index a6e56b9940..04ccbf58fc 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -25,9 +25,9 @@ type GetResult struct { } // CreateResult is the response from a Create operation. Call its Extract -// method to interpret it as a Server. +// method to interpret it as a Capsule. type CreateResult struct { - gophercloud.ErrResult + commonResult } // DeleteResult represents the result of a delete operation. @@ -39,7 +39,7 @@ type CapsulePage struct { pagination.LinkedPageBase } -// Represents a Container Orchestration Engine Bay, i.e. a cluster +// Represents a Capsule type Capsule struct { // UUID for the capsule UUID string `json:"uuid"` @@ -75,7 +75,7 @@ type Capsule struct { UpdatedAt time.Time `json:"-"` // Links includes HTTP references to the itself, useful for passing along to - // other APIs that might want a server reference. + // other APIs that might want a capsule reference. Links []interface{} `json:"links"` // The capsule version @@ -144,7 +144,7 @@ type Container struct { Name string `json:"name"` // Links includes HTTP references to the itself, useful for passing along to - // other APIs that might want a server reference. + // other APIs that might want a capsule reference. Links []interface{} `json:"links"` // Container ID for the container diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index db861827bb..599e99d5bd 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -569,7 +569,7 @@ func HandleCapsuleCreateSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprintf(w, CapsuleGetBody_NewTime) }) } diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 805d296bc9..03b93cfe0f 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -66,8 +66,10 @@ func TestCreateCapsule(t *testing.T) { createOpts := capsules.CreateOpts{ TemplateOpts: template, } - err := capsules.Create(fakeclient.ServiceClient(), createOpts).ExtractErr() + actualCapsule, err := capsules.Create(fakeclient.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) } func TestListCapsule(t *testing.T) { From feba9d39473334ed79c5b95181a17ee17909db74 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 12 Jul 2018 17:01:47 -0700 Subject: [PATCH 0469/2296] =?UTF-8?q?Bug:=20Senlin-=20Allow=20ListPolicies?= =?UTF-8?q?Opts=20to=20be=20passed=20into=20clusters.ListPolic=E2=80=A6=20?= =?UTF-8?q?(#1114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Senlin: Allow ListPolicies Opts to be passed in to clusters.ListPolicies(...) (#1113) * Rearranged naming ordering --- openstack/clustering/v1/clusters/requests.go | 4 ++-- openstack/clustering/v1/clusters/testing/requests_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index fccf5c621d..eb2215b755 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -405,7 +405,7 @@ func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolic // ListPolicyOptsBuilder allows extensions to add additional parameters to the // ListPolicies request. type ListPoliciesOptsBuilder interface { - ToClusterListPoliciesQuery() (string, error) + ToClusterPoliciesListQuery() (string, error) } // ListPoliciesOpts represents options to list a cluster's policies. @@ -426,7 +426,7 @@ func (opts ListPoliciesOpts) ToClusterPoliciesListQuery() (string, error) { func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts ListPoliciesOptsBuilder) pagination.Pager { url := listPoliciesURL(client, clusterID) if opts != nil { - query, err := opts.ToClusterListPoliciesQuery() + query, err := opts.ToClusterPoliciesListQuery() if err != nil { return pagination.Pager{Err: err} } diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 88abda5626..a98dc93e11 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -297,7 +297,7 @@ func TestListClusterPolicies(t *testing.T) { HandleListPoliciesSuccessfully(t) pageCount := 0 - err := clusters.ListPolicies(fake.ServiceClient(), ExpectedClusterPolicy.ClusterID, nil).EachPage(func(page pagination.Page) (bool, error) { + err := clusters.ListPolicies(fake.ServiceClient(), ExpectedClusterPolicy.ClusterID, clusters.ListPoliciesOpts{Name: "Test"}).EachPage(func(page pagination.Page) (bool, error) { pageCount++ actual, err := clusters.ExtractClusterPolicies(page) th.AssertNoErr(t, err) From ac3df21480397f6cd9fe19cd0849087a94f64f17 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Thu, 12 Jul 2018 18:06:58 -0700 Subject: [PATCH 0470/2296] Fixed Senlin Node's should use "project" field rather than "project_id" #1116 --- openstack/clustering/v1/nodes/results.go | 2 +- .../clustering/v1/nodes/testing/fixtures.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index e1deefd868..57987c27f3 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -23,7 +23,7 @@ type Node struct { PhysicalID string `json:"physical_id"` ProfileID string `json:"profile_id"` ProfileName string `json:"profile_name"` - ProjectID string `json:"project_id"` + Project string `json:"project"` Role string `json:"role"` Status string `json:"status"` StatusReason string `json:"status_reason"` diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go index 25dd92dc55..14ac6dbd1c 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures.go +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -52,7 +52,7 @@ const CreateResponse = `{ "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "project": "eee0b7c083e84501bdd50fb269d2a10e", "role": "", "status": "ACTIVE", "status_reason": "Creation succeeded", @@ -103,7 +103,7 @@ var ExpectedCreate = nodes.Node{ PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Project: "eee0b7c083e84501bdd50fb269d2a10e", Role: "", Status: "ACTIVE", StatusReason: "Creation succeeded", @@ -127,7 +127,7 @@ const ListResponse = ` "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "project": "eee0b7c083e84501bdd50fb269d2a10e", "role": "", "status": "ACTIVE", "status_reason": "Creation succeeded", @@ -150,7 +150,7 @@ var ExpectedList1 = nodes.Node{ PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Project: "eee0b7c083e84501bdd50fb269d2a10e", Role: "", Status: "ACTIVE", StatusReason: "Creation succeeded", @@ -176,7 +176,7 @@ const GetResponse = ` "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "project": "eee0b7c083e84501bdd50fb269d2a10e", "role": "", "status": "ACTIVE", "status_reason": "Creation succeeded", @@ -199,7 +199,7 @@ var ExpectedGet = nodes.Node{ PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Project: "eee0b7c083e84501bdd50fb269d2a10e", Role: "", Status: "ACTIVE", StatusReason: "Creation succeeded", @@ -223,7 +223,7 @@ const UpdateResponse = ` "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", "profile_name": "pcirros", - "project_id": "eee0b7c083e84501bdd50fb269d2a10e", + "project": "eee0b7c083e84501bdd50fb269d2a10e", "role": "", "status": "ACTIVE", "status_reason": "Creation succeeded", @@ -246,7 +246,7 @@ var ExpectedUpdate = nodes.Node{ PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", ProfileName: "pcirros", - ProjectID: "eee0b7c083e84501bdd50fb269d2a10e", + Project: "eee0b7c083e84501bdd50fb269d2a10e", Role: "", Status: "ACTIVE", StatusReason: "Creation succeeded", From 8ec15e75bcc31b32b646d0de326be08aa4cd8c16 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 13 Jul 2018 14:44:13 +0000 Subject: [PATCH 0471/2296] Object Storage v1: Add X-Newest to Container Get --- .../objectstorage/v1/containers_test.go | 6 +++- .../objectstorage/v1/containers/requests.go | 33 +++++++++++++++++-- .../v1/containers/testing/requests_test.go | 5 ++- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index 5673aa10c9..2b0319412d 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -86,7 +86,11 @@ func TestContainers(t *testing.T) { }() // Retrieve a container's metadata. - cm, err := containers.Get(client, cNames[0]).ExtractMetadata() + getOpts := containers.GetOpts{ + Newest: true, + } + + cm, err := containers.Get(client, cNames[0], getOpts).ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { if cm[k] != metadata[strings.Title(k)] { diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 3321dd545b..4799204e70 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -177,12 +177,41 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB return } +// GetOptsBuilder allows extensions to add additional parameters to the Get +// request. +type GetOptsBuilder interface { + ToContainerGetMap() (map[string]string, error) +} + +// GetOpts is a structure that holds options for listing containers. +type GetOpts struct { + Newest bool `h:"X-Newest"` +} + +// ToContainerGetMap formats a GetOpts into a map of headers. +func (opts GetOpts) ToContainerGetMap() (map[string]string, error) { + return gophercloud.BuildHeaders(opts) +} + // Get is a function that retrieves the metadata of a container. To extract just // the custom metadata, pass the GetResult response to the ExtractMetadata // function. -func Get(c *gophercloud.ServiceClient, containerName string) (r GetResult) { +func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder) (r GetResult) { + h := make(map[string]string) + if opts != nil { + headers, err := opts.ToContainerGetMap() + if err != nil { + r.Err = err + return + } + + for k, v := range headers { + h[k] = v + } + } resp, err := c.Head(getURL(c, containerName), &gophercloud.RequestOpts{ - OkCodes: []int{200, 204}, + MoreHeaders: h, + OkCodes: []int{200, 204}, }) if resp != nil { r.Header = resp.Header diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index b2c50141d4..c5eabd29f3 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -124,7 +124,10 @@ func TestGetContainer(t *testing.T) { defer th.TeardownHTTP() HandleGetContainerSuccessfully(t) - res := containers.Get(fake.ServiceClient(), "testContainer") + getOpts := containers.GetOpts{ + Newest: true, + } + res := containers.Get(fake.ServiceClient(), "testContainer", getOpts) _, err := res.ExtractMetadata() th.CheckNoErr(t, err) From 07d3f6f0a63028d4d621f9cb48d12f916a2b99e4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 13 Jul 2018 14:55:17 +0000 Subject: [PATCH 0472/2296] Object Storage v1: Add X-Newest to Object Get --- .../objectstorage/v1/objects_test.go | 5 +++- .../objectstorage/v1/objects/requests.go | 26 ++++++++++++++----- .../v1/objects/testing/requests_test.go | 5 +++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index 1335d0cbc1..dacf469d59 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -127,7 +127,10 @@ func TestObjects(t *testing.T) { }() // Retrieve an object's metadata. - om, err := objects.Get(client, cName, oNames[0], nil).ExtractMetadata() + getOpts := objects.GetOpts{ + Newest: true, + } + om, err := objects.Get(client, cName, oNames[0], getOpts).ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { if om[k] != metadata[strings.Title(k)] { diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index f0d743dc91..e2bc60a781 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -338,20 +338,28 @@ func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts // GetOptsBuilder allows extensions to add additional parameters to the // Get request. type GetOptsBuilder interface { - ToObjectGetQuery() (string, error) + ToObjectGetParams() (map[string]string, string, error) } // 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"` } -// ToObjectGetQuery formats a GetOpts into a query string. -func (opts GetOpts) ToObjectGetQuery() (string, error) { +// ToObjectGetParams formats a GetOpts into a query string and a map of headers. +func (opts GetOpts) ToObjectGetParams() (map[string]string, string, error) { q, err := gophercloud.BuildQueryString(opts) - return q.String(), err + if err != nil { + return nil, "", err + } + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + return nil, q.String(), err + } + return h, q.String(), nil } // Get is a function that retrieves the metadata of an object. To extract just @@ -359,16 +367,22 @@ func (opts GetOpts) ToObjectGetQuery() (string, error) { // function. func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) (r GetResult) { url := getURL(c, containerName, objectName) + h := make(map[string]string) if opts != nil { - query, err := opts.ToObjectGetQuery() + headers, query, err := opts.ToObjectGetParams() if err != nil { r.Err = err return } + for k, v := range headers { + h[k] = v + } url += query } + resp, err := c.Head(url, &gophercloud.RequestOpts{ - OkCodes: []int{200, 204}, + MoreHeaders: h, + OkCodes: []int{200, 204}, }) if resp != nil { r.Header = resp.Header diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 23d6dcf977..c5b34a75b0 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -237,7 +237,10 @@ func TestGetObject(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) - actualHeaders, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", nil).Extract() + getOpts := objects.GetOpts{ + Newest: true, + } + actualHeaders, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", getOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, actualHeaders.StaticLargeObject, true) } From 5c61241d48220c7872baa256b1cb91b55f2814cd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 13 Jul 2018 16:50:59 +0000 Subject: [PATCH 0473/2296] Object Storage v1: Add X-Newest to Object Download --- acceptance/openstack/objectstorage/v1/objects_test.go | 5 ++++- openstack/objectstorage/v1/objects/requests.go | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index dacf469d59..ed3def62a3 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -97,7 +97,10 @@ func TestObjects(t *testing.T) { th.AssertNoErr(t, err) // Download the another object that was create above. - downloadres = objects.Download(client, cName, oNames[1], nil) + 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) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index e2bc60a781..7325cd7d0b 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -86,6 +86,7 @@ type DownloadOpts struct { IfModifiedSince time.Time `h:"If-Modified-Since"` IfNoneMatch string `h:"If-None-Match"` IfUnmodifiedSince time.Time `h:"If-Unmodified-Since"` + Newest bool `h:"X-Newest"` Range string `h:"Range"` Expires string `q:"expires"` MultipartManifest string `q:"multipart-manifest"` From 318c81770b1ce34959d5d5f8644ff3e5910df81f Mon Sep 17 00:00:00 2001 From: Jude Cross Date: Fri, 13 Jul 2018 12:05:27 -0700 Subject: [PATCH 0474/2296] Add delete to Claims --- acceptance/openstack/messaging/v2/claims_test.go | 1 + acceptance/openstack/messaging/v2/messaging.go | 11 +++++++++++ openstack/messaging/v2/claims/doc.go | 9 +++++++++ openstack/messaging/v2/claims/requests.go | 8 ++++++++ openstack/messaging/v2/claims/results.go | 6 ++++++ openstack/messaging/v2/claims/testing/fixtures.go | 11 +++++++++++ .../messaging/v2/claims/testing/requests_test.go | 9 +++++++++ openstack/messaging/v2/claims/urls.go | 4 ++++ 8 files changed, 59 insertions(+) diff --git a/acceptance/openstack/messaging/v2/claims_test.go b/acceptance/openstack/messaging/v2/claims_test.go index d9c3418c99..4ffb9229e9 100644 --- a/acceptance/openstack/messaging/v2/claims_test.go +++ b/acceptance/openstack/messaging/v2/claims_test.go @@ -58,5 +58,6 @@ func TestCRUDClaim(t *testing.T) { } tools.PrintResource(t, updatedClaim) + DeleteClaim(t, client, createdQueueName, claimID) } } diff --git a/acceptance/openstack/messaging/v2/messaging.go b/acceptance/openstack/messaging/v2/messaging.go index 8f8d972fdc..f782770b70 100644 --- a/acceptance/openstack/messaging/v2/messaging.go +++ b/acceptance/openstack/messaging/v2/messaging.go @@ -134,6 +134,17 @@ func GetClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string, return claim, err } +func DeleteClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string, claimID string) error { + t.Logf("Attempting to delete claim: %s", claimID) + err := claims.Delete(client, queueName, claimID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete claim: %s", claimID) + } + t.Logf("Sucessfully deleted claim: %s", claimID) + + return err +} + func ExtractIDs(claim []claims.Messages) ([]string, []string) { var claimIDs []string var messageID []string diff --git a/openstack/messaging/v2/claims/doc.go b/openstack/messaging/v2/claims/doc.go index 5e1b166e81..1d9b1d0e83 100644 --- a/openstack/messaging/v2/claims/doc.go +++ b/openstack/messaging/v2/claims/doc.go @@ -40,5 +40,14 @@ Example to update a claim for a specified Zaqar queue if err != nil { panic(err) } + +Example to delete a claim for a specified Zaqar queue + + queueName := "my_queue" + + err := claims.Delete(messagingClient, queueName, claimID).ExtractErr() + if err != nil { + panic(err) + } */ package claims diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index 8a319bfcd9..21492e527d 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -110,3 +110,11 @@ func Update(client *gophercloud.ServiceClient, queueName string, claimID string, }) return } + +// Delete will delete a Claim for a specified Queue. +func Delete(client *gophercloud.ServiceClient, queueName string, claimID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, queueName, claimID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/messaging/v2/claims/results.go b/openstack/messaging/v2/claims/results.go index d3abe106ab..ec43e582c0 100644 --- a/openstack/messaging/v2/claims/results.go +++ b/openstack/messaging/v2/claims/results.go @@ -31,6 +31,12 @@ type UpdateResult struct { gophercloud.ErrResult } +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + type Messages struct { Age float32 `json:"age"` Href string `json:"href"` diff --git a/openstack/messaging/v2/claims/testing/fixtures.go b/openstack/messaging/v2/claims/testing/fixtures.go index f81bee02be..95ef1468a6 100644 --- a/openstack/messaging/v2/claims/testing/fixtures.go +++ b/openstack/messaging/v2/claims/testing/fixtures.go @@ -120,3 +120,14 @@ func HandleUpdateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// HandleDeleteSuccessfully configures the test server to respond to an Delete request. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims/%s", QueueName, ClaimID), + 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/messaging/v2/claims/testing/requests_test.go b/openstack/messaging/v2/claims/testing/requests_test.go index a4cb4cacbe..e3ee3d39a0 100644 --- a/openstack/messaging/v2/claims/testing/requests_test.go +++ b/openstack/messaging/v2/claims/testing/requests_test.go @@ -47,3 +47,12 @@ func TestUpdate(t *testing.T) { err := claims.Update(fake.ServiceClient(), QueueName, ClaimID, updateOpts).ExtractErr() th.AssertNoErr(t, err) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + err := claims.Delete(fake.ServiceClient(), QueueName, ClaimID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/messaging/v2/claims/urls.go b/openstack/messaging/v2/claims/urls.go index b0990a1934..fae8fb4f45 100644 --- a/openstack/messaging/v2/claims/urls.go +++ b/openstack/messaging/v2/claims/urls.go @@ -18,3 +18,7 @@ func getURL(client *gophercloud.ServiceClient, queueName string, claimID string) func updateURL(client *gophercloud.ServiceClient, queueName string, claimID string) string { return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimID) } + +func deleteURL(client *gophercloud.ServiceClient, queueName string, claimID string) string { + return client.ServiceURL(apiVersion, apiName, queueName, "claims", claimID) +} From f9d2e1b5190d712b157fb99cc6447194735775b0 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Fri, 13 Jul 2018 18:44:00 -0700 Subject: [PATCH 0475/2296] Senlin: Clusters CompleteLifecycle (#1112) * senlin-clusters-completelifecycle * Changed naming casing from CompleteLifeCycle to CompleteLifecycle --- openstack/clustering/v1/clusters/doc.go | 11 ++++++++ openstack/clustering/v1/clusters/requests.go | 27 +++++++++++++++++++ .../v1/clusters/testing/fixtures.go | 14 ++++++++++ .../v1/clusters/testing/requests_test.go | 19 +++++++++++++ 4 files changed, 71 insertions(+) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 90f384c0b5..f731fb5376 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -212,5 +212,16 @@ Example to Check a Cluster if err != nil { panic(err) } + +Example to Complete Life Cycle + + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + lifecycleOpts := clusters.CompleteLifecycleOpts{LifecycleActionTokenID: "2b827124-69e1-496e-9484-33ca769fe4df"} + + action, err := clusters.CompleteLifecycle(computeClient, clusterID, lifecycleOpts).Extract() + if err != nil { + panic(err) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index eb2215b755..2c97e202fc 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -496,3 +496,30 @@ func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { return } + +// ToClusterCompleteLifecycleMap constructs a request body from CompleteLifecycleOpts. +func (opts CompleteLifecycleOpts) ToClusterCompleteLifecycleMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "complete_lifecycle") +} + +type CompleteLifecycleOpts struct { + LifecycleActionTokenID string `json:"lifecycle_action_token" required:"true"` +} + +func CompleteLifecycle(client *gophercloud.ServiceClient, id string, opts CompleteLifecycleOpts) (r ActionResult) { + b, err := opts.ToClusterCompleteLifecycleMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index f5361d276d..26c84821c7 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -608,3 +608,17 @@ func HandleCheckSuccessfully(t *testing.T) { fmt.Fprint(w, ActionResponse) }) } + +func HandleLifecycleSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/2a0ff107-e789-4660-a122-3816c43af703") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprint(w, ActionResponse) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index a98dc93e11..ba4f05a3a3 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -407,3 +407,22 @@ func TestClusterCheck(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } + +func TestLifecycle(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleLifecycleSuccessfully(t) + + opts := clusters.CompleteLifecycleOpts{ + LifecycleActionTokenID: "976528c6-dcf6-4d8d-9f4c-588f4e675f29", + } + + res := clusters.CompleteLifecycle(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", opts) + location := res.Header.Get("Location") + th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/2a0ff107-e789-4660-a122-3816c43af703", location) + + actionID, err := res.Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "2a0ff107-e789-4660-a122-3816c43af703", actionID) +} From f903687273c3b66c33fde46c25dfacf6f5032577 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 14 Jul 2018 08:08:51 -0600 Subject: [PATCH 0476/2296] Blockstorage v3 quota-set support - part 5, Delete (#1124) * Copy compute v2 quotaset to storage v3 quotaset. Monkey see, monkey do. Modify tests and fixtures to use block storage values. Update QuotaSet and UpdateQuotaSet types for block storage. Fix test fixture values. Handle apparent bug in PartialPut test case. Group stimuli and their responses together in fixtures. Improve test fixture parmaeterization. This allows us to more easily see problems with test cases by specifying the expected inputs and outputs in the same place in code rather than the implicit assumption that the fixture has the output expected for the test case. Another advantage is that this brings us that much closer to the ideal of test fixtures consisting purely of a list of data structures which can then be iterated over in a single test case function that glues all the pieces together to validate that the tested code, given some input data, produces the expected output data. Implement data-driven testing approach. Remove redundant test. Absorb TestGetDetail into DDT test cases. Absorb TestUpdate into DDT test cases. Absorb TestPartialUpdate into DDT test cases. Remove unnecessary field initialization. Get rid of defunct fixture function. Absorb TestDelete into DDT test cases. Not expecting a json response in Error test case. Fix blockstorage quotaset api docs. Fix blockstorage QuotaDetail struct. s/tenantID/projectID/ Fix blockstorage quotaset detailed url. Accept HTTP 200 for blockstorage quotaset DELETE OS block storage quotasets don't support groups. Get rid of unused resourceURL. Add blockstorage quotaset GetDefaults function. Add quotaset acceptance tests. s/block storage/compute/ in comments. Fix blockstorage quotaset test json strings. Fix blockstorage quotaset Delete tests. Handle URI query params in blockstorage quotaset tests. Remove blockstorage quotaset delete code. Remove blockstorage quotaset update code. Remove blockstorage quotaset details code. Remove blockstorage quotaset defaults code. Revert "Remove blockstorage quotaset defaults code." This reverts commit 92ea29d4f7b295050500a81a5ebfc1a706b87a25. Revert "Remove blockstorage quotaset details code." This reverts commit 01df962e4b6721c8fb157ab3affb71270d2517b3. Revert "Remove blockstorage quotaset update code." This reverts commit 781fb6c9885dcbdc59ccb93173bbab1ce3658cb0. Revert "Remove blockstorage quotaset delete code." This reverts commit f86185e3ff9249321cb13a05cd0036c48aa2cfd4. * Block Storage: quotasets Delete --- .../blockstorage/v3/quotaset_test.go | 39 +++++++++++++++++++ .../blockstorage/extensions/quotasets/doc.go | 7 ++++ .../extensions/quotasets/requests.go | 36 ++++++++++------- .../extensions/quotasets/results.go | 6 +++ .../extensions/quotasets/testing/fixtures.go | 10 +++++ .../quotasets/testing/requests_test.go | 9 +++++ .../blockstorage/extensions/quotasets/urls.go | 4 ++ 7 files changed, 98 insertions(+), 13 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index 17d3eb1733..f69e6866b8 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -14,6 +14,8 @@ import ( ) func TestQuotasetGet(t *testing.T) { + clients.RequireAdmin(t) + client, projectID := getClientAndProject(t) quotaSet, err := quotasets.Get(client, projectID).Extract() @@ -23,6 +25,8 @@ func TestQuotasetGet(t *testing.T) { } func TestQuotasetGetDefaults(t *testing.T) { + clients.RequireAdmin(t) + client, projectID := getClientAndProject(t) quotaSet, err := quotasets.GetDefaults(client, projectID).Extract() @@ -32,6 +36,8 @@ func TestQuotasetGetDefaults(t *testing.T) { } func TestQuotasetGetUsage(t *testing.T) { + clients.RequireAdmin(t) + client, projectID := getClientAndProject(t) quotaSetUsage, err := quotasets.GetUsage(client, projectID).Extract() @@ -59,6 +65,8 @@ var UpdatedQuotas = quotasets.QuotaSet{ } func TestQuotasetUpdate(t *testing.T) { + clients.RequireAdmin(t) + client, projectID := getClientAndProject(t) // save original quotas @@ -85,6 +93,37 @@ func TestQuotasetUpdate(t *testing.T) { th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) } +func TestQuotasetDelete(t *testing.T) { + clients.RequireAdmin(t) + + client, projectID := getClientAndProject(t) + + // save original quotas + orig, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + + defer func() { + restore := quotasets.UpdateOpts{} + FillUpdateOptsFromQuotaSet(*orig, &restore) + + _, err = quotasets.Update(client, projectID, restore).Extract() + th.AssertNoErr(t, err) + }() + + // Obtain environment default quotaset values to validate deletion. + defaultQuotaSet, err := quotasets.GetDefaults(client, projectID).Extract() + th.AssertNoErr(t, err) + + // Test Delete + err = quotasets.Delete(client, projectID).ExtractErr() + th.AssertNoErr(t, err) + + newQuotas, err := quotasets.Get(client, projectID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, newQuotas.Volumes, defaultQuotaSet.Volumes) +} + // getClientAndProject reduces boilerplate by returning a new blockstorage v3 // ServiceClient and a project ID obtained from the OS_PROJECT_NAME envvar. func getClientAndProject(t *testing.T) (*gophercloud.ServiceClient, string) { diff --git a/openstack/blockstorage/extensions/quotasets/doc.go b/openstack/blockstorage/extensions/quotasets/doc.go index 9b48292806..109f78f506 100644 --- a/openstack/blockstorage/extensions/quotasets/doc.go +++ b/openstack/blockstorage/extensions/quotasets/doc.go @@ -31,5 +31,12 @@ Example to Update a Quota Set } fmt.Printf("%+v\n", quotaset) + +Example to Delete a Quota Set + + err := quotasets.Delete(blockStorageClient, "project-id").ExtractErr() + if err != nil { + panic(err) + } */ package quotasets diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go index 29f0d3aba8..c89b4e3514 100644 --- a/openstack/blockstorage/extensions/quotasets/requests.go +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -27,16 +27,31 @@ func GetUsage(client *gophercloud.ServiceClient, projectID string) (r GetUsageRe // Updates the quotas for the given projectID and returns the new QuotaSet. func Update(client *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { - reqBody, err := opts.ToBlockStorageQuotaUpdateMap() + b, err := opts.ToBlockStorageQuotaUpdateMap() if err != nil { r.Err = err return } - _, r.Err = client.Put(updateURL(client, projectID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Err = client.Put(updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) return r } +// 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) + ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) +} + +// ToBlockStorageQuotaUpdateMap builds the update options into a serializable +// format. +func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "quota_set") +} + // 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 @@ -70,15 +85,10 @@ type UpdateOpts struct { Force bool `json:"force,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) - ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) -} - -// ToBlockStorageQuotaUpdateMap builds the update options into a serializable -// format. -func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "quota_set") +// Resets the quotas for the given tenant to their default values. +func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { + _, r.Err = client.Delete(updateURL(client, projectID), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return } diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/extensions/quotasets/results.go index 0e22ce17aa..8ccfb5bfeb 100644 --- a/openstack/blockstorage/extensions/quotasets/results.go +++ b/openstack/blockstorage/extensions/quotasets/results.go @@ -157,3 +157,9 @@ func (r quotaUsageResult) Extract() (QuotaUsageSet, error) { err := r.ExtractInto(&s) return s.QuotaUsageSet, err } + +// 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 +} diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go index 24ad444081..fe1a5d885d 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go +++ b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go @@ -150,3 +150,13 @@ func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput strin fmt.Fprintf(w, jsonOutput) }) } + +// HandleDeleteSuccessfully tests quotaset deletion. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, 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) + }) +} diff --git a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go index 0b01321f8e..922b8d336e 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go +++ b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go @@ -69,3 +69,12 @@ func TestErrorInToBlockStorageQuotaUpdateMap(t *testing.T) { t.Fatal("Error handling failed") } } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + err := quotasets.Delete(client.ServiceClient(), FirstTenantID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/extensions/quotasets/urls.go b/openstack/blockstorage/extensions/quotasets/urls.go index a5c11442d2..7d8e5ceb7a 100644 --- a/openstack/blockstorage/extensions/quotasets/urls.go +++ b/openstack/blockstorage/extensions/quotasets/urls.go @@ -15,3 +15,7 @@ func getDefaultsURL(c *gophercloud.ServiceClient, projectID string) string { func updateURL(c *gophercloud.ServiceClient, projectID string) string { return getURL(c, projectID) } + +func deleteURL(c *gophercloud.ServiceClient, projectID string) string { + return getURL(c, projectID) +} From 896ea6098732a5142c635d38773d948548806720 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 15 Jul 2018 03:15:50 +0000 Subject: [PATCH 0477/2296] Prefer Assert over Check --- .../.template/testing/requests_test.go | 12 +++++------ .../v1/acls/testing/requests_test.go | 12 +++++------ .../v1/containers/testing/requests_test.go | 20 +++++++++---------- .../v1/orders/testing/requests_test.go | 10 +++++----- .../v1/secrets/testing/requests_test.go | 12 +++++------ 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/contributor-tutorial/.template/testing/requests_test.go b/docs/contributor-tutorial/.template/testing/requests_test.go index 4d294c8a27..22f97a2269 100644 --- a/docs/contributor-tutorial/.template/testing/requests_test.go +++ b/docs/contributor-tutorial/.template/testing/requests_test.go @@ -21,12 +21,12 @@ func TestListResources(t *testing.T) { actual, err := resources.ExtractResources(page) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedResourcesSlice, actual) + th.AssertDeepEquals(t, ExpectedResourcesSlice, actual) return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.AssertEquals(t, count, 1) } func TestListResourcesAllPages(t *testing.T) { @@ -38,7 +38,7 @@ func TestListResourcesAllPages(t *testing.T) { th.AssertNoErr(t, err) actual, err := resources.ExtractResources(allPages) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedResourcesSlice, actual) + th.AssertDeepEquals(t, ExpectedResourcesSlice, actual) } func TestGetResource(t *testing.T) { @@ -48,7 +48,7 @@ func TestGetResource(t *testing.T) { actual, err := resources.Get(client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, SecondResource, *actual) + th.AssertDeepEquals(t, SecondResource, *actual) } func TestCreateResource(t *testing.T) { @@ -62,7 +62,7 @@ func TestCreateResource(t *testing.T) { actual, err := resources.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, SecondResource, *actual) + th.AssertDeepEquals(t, SecondResource, *actual) } func TestDeleteResource(t *testing.T) { @@ -85,5 +85,5 @@ func TestUpdateResource(t *testing.T) { actual, err := resources.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, SecondResourceUpdated, *actual) + th.AssertDeepEquals(t, SecondResourceUpdated, *actual) } diff --git a/openstack/keymanager/v1/acls/testing/requests_test.go b/openstack/keymanager/v1/acls/testing/requests_test.go index d2b31250f1..b978f405f1 100644 --- a/openstack/keymanager/v1/acls/testing/requests_test.go +++ b/openstack/keymanager/v1/acls/testing/requests_test.go @@ -15,7 +15,7 @@ func TestGetSecretACL(t *testing.T) { actual, err := acls.GetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedACL, *actual) + th.AssertDeepEquals(t, ExpectedACL, *actual) } func TestGetContainerACL(t *testing.T) { @@ -25,7 +25,7 @@ func TestGetContainerACL(t *testing.T) { actual, err := acls.GetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedACL, *actual) + th.AssertDeepEquals(t, ExpectedACL, *actual) } func TestSetSecretACL(t *testing.T) { @@ -43,7 +43,7 @@ func TestSetSecretACL(t *testing.T) { actual, err := acls.SetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedSecretACLRef, *actual) + th.AssertDeepEquals(t, ExpectedSecretACLRef, *actual) } func TestSetContainerACL(t *testing.T) { @@ -61,7 +61,7 @@ func TestSetContainerACL(t *testing.T) { actual, err := acls.SetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedContainerACLRef, *actual) + th.AssertDeepEquals(t, ExpectedContainerACLRef, *actual) } func TestDeleteSecretACL(t *testing.T) { @@ -95,7 +95,7 @@ func TestUpdateSecretACL(t *testing.T) { actual, err := acls.UpdateSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedSecretACLRef, *actual) + th.AssertDeepEquals(t, ExpectedSecretACLRef, *actual) } func TestUpdateContainerACL(t *testing.T) { @@ -111,5 +111,5 @@ func TestUpdateContainerACL(t *testing.T) { actual, err := acls.UpdateContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedContainerACLRef, *actual) + th.AssertDeepEquals(t, ExpectedContainerACLRef, *actual) } diff --git a/openstack/keymanager/v1/containers/testing/requests_test.go b/openstack/keymanager/v1/containers/testing/requests_test.go index 94a657d732..2f405e3c55 100644 --- a/openstack/keymanager/v1/containers/testing/requests_test.go +++ b/openstack/keymanager/v1/containers/testing/requests_test.go @@ -21,12 +21,12 @@ func TestListContainers(t *testing.T) { actual, err := containers.ExtractContainers(page) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedContainersSlice, actual) + th.AssertDeepEquals(t, ExpectedContainersSlice, actual) return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.AssertEquals(t, count, 1) } func TestListContainersAllPages(t *testing.T) { @@ -38,7 +38,7 @@ func TestListContainersAllPages(t *testing.T) { th.AssertNoErr(t, err) actual, err := containers.ExtractContainers(allPages) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedContainersSlice, actual) + th.AssertDeepEquals(t, ExpectedContainersSlice, actual) } func TestGetContainer(t *testing.T) { @@ -48,7 +48,7 @@ func TestGetContainer(t *testing.T) { actual, err := containers.Get(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0").Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, FirstContainer, *actual) + th.AssertDeepEquals(t, FirstContainer, *actual) } func TestCreateContainer(t *testing.T) { @@ -69,7 +69,7 @@ func TestCreateContainer(t *testing.T) { actual, err := containers.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, FirstContainer, *actual) + th.AssertDeepEquals(t, FirstContainer, *actual) } func TestDeleteContainer(t *testing.T) { @@ -93,12 +93,12 @@ func TestListConsumers(t *testing.T) { actual, err := containers.ExtractConsumers(page) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedConsumersSlice, actual) + th.AssertDeepEquals(t, ExpectedConsumersSlice, actual) return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.AssertEquals(t, count, 1) } func TestListConsumersAllPages(t *testing.T) { @@ -110,7 +110,7 @@ func TestListConsumersAllPages(t *testing.T) { th.AssertNoErr(t, err) actual, err := containers.ExtractConsumers(allPages) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedConsumersSlice, actual) + th.AssertDeepEquals(t, ExpectedConsumersSlice, actual) } func TestCreateConsumer(t *testing.T) { @@ -125,7 +125,7 @@ func TestCreateConsumer(t *testing.T) { actual, err := containers.CreateConsumer(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", createOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedCreatedConsumer, *actual) + th.AssertDeepEquals(t, ExpectedCreatedConsumer, *actual) } func TestDeleteConsumer(t *testing.T) { @@ -140,5 +140,5 @@ func TestDeleteConsumer(t *testing.T) { actual, err := containers.DeleteConsumer(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", deleteOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, FirstContainer, *actual) + th.AssertDeepEquals(t, FirstContainer, *actual) } diff --git a/openstack/keymanager/v1/orders/testing/requests_test.go b/openstack/keymanager/v1/orders/testing/requests_test.go index a6249630e8..5c9fcb3133 100644 --- a/openstack/keymanager/v1/orders/testing/requests_test.go +++ b/openstack/keymanager/v1/orders/testing/requests_test.go @@ -21,12 +21,12 @@ func TestListOrders(t *testing.T) { actual, err := orders.ExtractOrders(page) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedOrdersSlice, actual) + th.AssertDeepEquals(t, ExpectedOrdersSlice, actual) return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.AssertEquals(t, count, 1) } func TestListOrdersAllPages(t *testing.T) { @@ -38,7 +38,7 @@ func TestListOrdersAllPages(t *testing.T) { th.AssertNoErr(t, err) actual, err := orders.ExtractOrders(allPages) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedOrdersSlice, actual) + th.AssertDeepEquals(t, ExpectedOrdersSlice, actual) } func TestGetOrder(t *testing.T) { @@ -48,7 +48,7 @@ func TestGetOrder(t *testing.T) { actual, err := orders.Get(client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7").Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, SecondOrder, *actual) + th.AssertDeepEquals(t, SecondOrder, *actual) } func TestCreateOrder(t *testing.T) { @@ -68,7 +68,7 @@ func TestCreateOrder(t *testing.T) { actual, err := orders.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, SecondOrder, *actual) + th.AssertDeepEquals(t, SecondOrder, *actual) } func TestDeleteOrder(t *testing.T) { diff --git a/openstack/keymanager/v1/secrets/testing/requests_test.go b/openstack/keymanager/v1/secrets/testing/requests_test.go index 1dca431261..d63879c942 100644 --- a/openstack/keymanager/v1/secrets/testing/requests_test.go +++ b/openstack/keymanager/v1/secrets/testing/requests_test.go @@ -21,12 +21,12 @@ func TestListSecrets(t *testing.T) { actual, err := secrets.ExtractSecrets(page) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedSecretsSlice, actual) + th.AssertDeepEquals(t, ExpectedSecretsSlice, actual) return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.AssertEquals(t, count, 1) } func TestListSecretsAllPages(t *testing.T) { @@ -38,7 +38,7 @@ func TestListSecretsAllPages(t *testing.T) { th.AssertNoErr(t, err) actual, err := secrets.ExtractSecrets(allPages) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedSecretsSlice, actual) + th.AssertDeepEquals(t, ExpectedSecretsSlice, actual) } func TestGetSecret(t *testing.T) { @@ -48,7 +48,7 @@ func TestGetSecret(t *testing.T) { actual, err := secrets.Get(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, FirstSecret, *actual) + th.AssertDeepEquals(t, FirstSecret, *actual) } func TestCreateSecret(t *testing.T) { @@ -68,7 +68,7 @@ func TestCreateSecret(t *testing.T) { actual, err := secrets.Create(client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedCreateResult, *actual) + th.AssertDeepEquals(t, ExpectedCreateResult, *actual) } func TestDeleteSecret(t *testing.T) { @@ -102,7 +102,7 @@ func TestGetPayloadSecret(t *testing.T) { th.AssertNoErr(t, res.Err) payload, err := res.Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, GetPayloadResponse, string(payload)) + th.AssertDeepEquals(t, GetPayloadResponse, string(payload)) } func TestGetMetadataSuccessfully(t *testing.T) { From 28723da342f8b4212086f430c7079995657116f2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 15 Jul 2018 03:32:47 +0000 Subject: [PATCH 0478/2296] Make IDFromName More Efficient When using the IDFromName convenience function, all resources are retrieved which can create a lot of unnecessary traffic. By immediately filtering with the "name" attribute, the traffic can be cut down significantly. --- .../networking/v2/extensions/layer3/floatingips_test.go | 2 +- openstack/blockstorage/v1/snapshots/requests.go | 7 ++++++- openstack/blockstorage/v1/volumes/requests.go | 7 ++++++- openstack/blockstorage/v2/snapshots/requests.go | 7 ++++++- openstack/blockstorage/v2/volumes/requests.go | 7 ++++++- openstack/blockstorage/v3/snapshots/requests.go | 7 ++++++- openstack/blockstorage/v3/volumes/requests.go | 7 ++++++- openstack/compute/v2/servers/requests.go | 7 ++++++- .../networking/v2/extensions/security/groups/requests.go | 7 ++++++- openstack/networking/v2/networks/requests.go | 7 ++++++- openstack/networking/v2/ports/requests.go | 7 ++++++- openstack/networking/v2/subnets/requests.go | 7 ++++++- 12 files changed, 67 insertions(+), 12 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index b38d7283ca..3d2073c1cd 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -138,7 +138,7 @@ func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { fip, err := floatingips.Create(client, createOpts).Extract() if err != nil { - t.Fatalf("Unable to create floating IP: %v") + t.Fatalf("Unable to create floating IP: %v", err) } tools.PrintResource(t, fip) diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go index cb9d0d0e06..fed1252ac9 100644 --- a/openstack/blockstorage/v1/snapshots/requests.go +++ b/openstack/blockstorage/v1/snapshots/requests.go @@ -130,7 +130,12 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go index 566def5181..52d738fa9c 100644 --- a/openstack/blockstorage/v1/volumes/requests.go +++ b/openstack/blockstorage/v1/volumes/requests.go @@ -139,7 +139,12 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go index dc707fd93d..939e502048 100644 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ b/openstack/blockstorage/v2/snapshots/requests.go @@ -142,7 +142,12 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index 2ec10ad55e..cbe96f1532 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -161,7 +161,12 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 7df688ede8..22531c9fdf 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -153,7 +153,12 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 43727409dd..b7977cbc8f 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -174,7 +174,12 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index e0c39e56f2..7d4f80f16c 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -756,7 +756,12 @@ func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageO func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - allPages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + allPages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index ebacc6ee34..2a46ce6aa0 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -128,7 +128,12 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, ListOpts{}).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index bc4460a065..1fbf62092c 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -140,7 +140,12 @@ func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 90416faa19..2a5902d75d 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -151,7 +151,12 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index f4a52a457b..2597947b18 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -221,7 +221,12 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - pages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() if err != nil { return "", err } From 0caa4986e4a4a25b0c9fca247cc3291cdbfc76ad Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 15 Jul 2018 09:14:26 -0600 Subject: [PATCH 0479/2296] Compute v2: AllTenants Usage (#1125) * Compute v2: AllTenants Usage * Compute v2: Changing usage paging to LinkedPage A microversion addition enabled paging support to usage results. This does not add support for the microversion-added parameters, but changes the pagination implementation to support them in the future. * Compute v2: Fixing usage paging The usage API does not work well when using the AllPages method for two reasons: The format of the result is not a standard "list" format. A portion of the result is a list, but the result also contains non-list data that is not captured with AllPages. In addition, some of this data is a calculation done on the usage within the single page. The calculation then changes from page to page and this is also not captured with AllPages. --- acceptance/openstack/compute/v2/usage_test.go | 52 ++++- openstack/compute/v2/extensions/usage/doc.go | 58 ++++-- .../compute/v2/extensions/usage/requests.go | 70 ++++++- .../compute/v2/extensions/usage/results.go | 56 +++++- .../v2/extensions/usage/testing/fixtures.go | 177 ++++++++++++++++++ .../extensions/usage/testing/requests_test.go | 39 +++- openstack/compute/v2/extensions/usage/urls.go | 2 +- 7 files changed, 417 insertions(+), 37 deletions(-) diff --git a/acceptance/openstack/compute/v2/usage_test.go b/acceptance/openstack/compute/v2/usage_test.go index ab6c71db8c..c998b59e2f 100644 --- a/acceptance/openstack/compute/v2/usage_test.go +++ b/acceptance/openstack/compute/v2/usage_test.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -35,15 +36,56 @@ func TestUsageSingleTenant(t *testing.T) { End: &end, } - page, err := usage.SingleTenant(client, tenantID, opts).AllPages() + err = usage.SingleTenant(client, tenantID, opts).EachPage(func(page pagination.Page) (bool, error) { + tenantUsage, err := usage.ExtractSingleTenant(page) + th.AssertNoErr(t, err) + + tools.PrintResource(t, tenantUsage) + if tenantUsage.TotalHours == 0 { + t.Fatalf("TotalHours should not be 0") + } + + return true, nil + }) + th.AssertNoErr(t, err) +} + +func TestUsageAllTenants(t *testing.T) { + t.Skip("This is not passing in OpenLab. Works locally") + + clients.RequireLong(t) - tenantUsage, err := usage.ExtractSingleTenant(page) + client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - tools.PrintResource(t, tenantUsage) + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + DeleteServer(t, client, server) - if tenantUsage.TotalHours == 0 { - t.Fatalf("TotalHours should not be 0") + end := time.Now() + start := end.AddDate(0, -1, 0) + opts := usage.AllTenantsOpts{ + Detailed: true, + Start: &start, + End: &end, } + + err = usage.AllTenants(client, opts).EachPage(func(page pagination.Page) (bool, error) { + allUsage, err := usage.ExtractAllTenants(page) + th.AssertNoErr(t, err) + + tools.PrintResource(t, allUsage) + + if len(allUsage) == 0 { + t.Fatalf("No usage returned") + } + + if allUsage[0].TotalHours == 0 { + t.Fatalf("TotalHours should not be 0") + } + return true, nil + }) + + th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/usage/doc.go b/openstack/compute/v2/extensions/usage/doc.go index 32e8643e4d..16b3a284ba 100644 --- a/openstack/compute/v2/extensions/usage/doc.go +++ b/openstack/compute/v2/extensions/usage/doc.go @@ -2,26 +2,58 @@ Package usage provides information and interaction with the SimpleTenantUsage extension for the OpenStack Compute service. +Due to the way the API responses are formatted, it is not recommended to +query by using the AllPages convenience method. Instead, use the EachPage +method to view each result page-by-page. + +This is because the usage calculations are done _per page_ and not as +an aggregated total of the entire usage set. + Example to Retrieve Usage for a Single Tenant: + start := time.Date(2017, 01, 21, 10, 4, 20, 0, time.UTC) end := time.Date(2017, 01, 21, 10, 4, 20, 0, time.UTC) - singleTenantOpts := usage.SingleTenantOpts{ - Start: &start, - End: &end, - } + singleTenantOpts := usage.SingleTenantOpts{ + Start: &start, + End: &end, + } + + err := usage.SingleTenant(computeClient, tenantID, singleTenantOpts).EachPage(func(page pagination.Page) (bool, error) { + tenantUsage, err := usage.ExtractSingleTenant(page) + if err != nil { + return false, err + } + + fmt.Printf("%+v\n", tenantUsage) + + return true, nil + }) + + if err != nil { + panic(err) + } + +Example to Retrieve Usage for All Tenants: + + allTenantsOpts := usage.AllTenantsOpts{ + Detailed: true, + } + + err := usage.AllTenants(computeClient, allTenantsOpts).EachPage(func(page pagination.Page) (bool, error) { + allTenantsUsage, err := usage.ExtractAllTenants(page) + if err != nil { + return false, err + } - page, err := usage.SingleTenant(computeClient, tenantID, singleTenantOpts).AllPages() - if err != nil { - panic(err) - } + fmt.Printf("%+v\n", allTenantsUsage) - tenantUsage, err := usage.ExtractSingleTenant(page) - if err != nil { - panic(err) - } + return true, nil + }) - fmt.Printf("%+v\n", tenantUsage) + if err != nil { + panic(err) + } */ package usage diff --git a/openstack/compute/v2/extensions/usage/requests.go b/openstack/compute/v2/extensions/usage/requests.go index ee66e2a212..77312227f6 100644 --- a/openstack/compute/v2/extensions/usage/requests.go +++ b/openstack/compute/v2/extensions/usage/requests.go @@ -8,6 +8,36 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// SingleTenantOpts are options for fetching usage of a single tenant. +type SingleTenantOpts struct { + // The ending time to calculate usage statistics on compute and storage resources. + End *time.Time `q:"end"` + + // The beginning time to calculate usage statistics on compute and storage resources. + Start *time.Time `q:"start"` +} + +// SingleTenantOptsBuilder allows extensions to add additional parameters to the +// SingleTenant request. +type SingleTenantOptsBuilder interface { + ToUsageSingleTenantQuery() (string, error) +} + +// ToUsageSingleTenantQuery formats a SingleTenantOpts into a query string. +func (opts SingleTenantOpts) ToUsageSingleTenantQuery() (string, error) { + params := make(url.Values) + if opts.Start != nil { + params.Add("start", opts.Start.Format(gophercloud.RFC3339MilliNoZ)) + } + + if opts.End != nil { + params.Add("end", opts.End.Format(gophercloud.RFC3339MilliNoZ)) + } + + q := &url.URL{RawQuery: params.Encode()} + return q.String(), nil +} + // SingleTenant returns usage data about a single tenant. func SingleTenant(client *gophercloud.ServiceClient, tenantID string, opts SingleTenantOptsBuilder) pagination.Pager { url := getTenantURL(client, tenantID) @@ -19,12 +49,15 @@ func SingleTenant(client *gophercloud.ServiceClient, tenantID string, opts Singl url += query } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return SingleTenantPage{pagination.SinglePageBase(r)} + return SingleTenantPage{pagination.LinkedPageBase{PageResult: r}} }) } -// SingleTenantOpts are options for fetching usage of a single tenant. -type SingleTenantOpts struct { +// AllTenantsOpts are options for fetching usage of all tenants. +type AllTenantsOpts struct { + // Detailed will return detailed results. + Detailed bool + // The ending time to calculate usage statistics on compute and storage resources. End *time.Time `q:"end"` @@ -32,14 +65,14 @@ type SingleTenantOpts struct { Start *time.Time `q:"start"` } -// SingleTenantOptsBuilder allows extensions to add additional parameters to the -// SingleTenant request. -type SingleTenantOptsBuilder interface { - ToUsageSingleTenantQuery() (string, error) +// AllTenantsOptsBuilder allows extensions to add additional parameters to the +// AllTenants request. +type AllTenantsOptsBuilder interface { + ToUsageAllTenantsQuery() (string, error) } -// ToUsageSingleTenantQuery formats a SingleTenantOpts into a query string. -func (opts SingleTenantOpts) ToUsageSingleTenantQuery() (string, error) { +// ToUsageAllTenantsQuery formats a AllTenantsOpts into a query string. +func (opts AllTenantsOpts) ToUsageAllTenantsQuery() (string, error) { params := make(url.Values) if opts.Start != nil { params.Add("start", opts.Start.Format(gophercloud.RFC3339MilliNoZ)) @@ -49,6 +82,25 @@ func (opts SingleTenantOpts) ToUsageSingleTenantQuery() (string, error) { params.Add("end", opts.End.Format(gophercloud.RFC3339MilliNoZ)) } + if opts.Detailed == true { + params.Add("detailed", "1") + } + q := &url.URL{RawQuery: params.Encode()} return q.String(), nil } + +// AllTenants returns usage data about all tenants. +func AllTenants(client *gophercloud.ServiceClient, opts AllTenantsOptsBuilder) pagination.Pager { + url := allTenantsURL(client) + if opts != nil { + query, err := opts.ToUsageAllTenantsQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AllTenantsPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/compute/v2/extensions/usage/results.go b/openstack/compute/v2/extensions/usage/results.go index 39661ba463..f446730ec2 100644 --- a/openstack/compute/v2/extensions/usage/results.go +++ b/openstack/compute/v2/extensions/usage/results.go @@ -117,21 +117,67 @@ func (u *ServerUsage) UnmarshalJSON(b []byte) error { // SingleTenantPage stores a single, only page of TenantUsage results from a // SingleTenant call. type SingleTenantPage struct { - pagination.SinglePageBase + pagination.LinkedPageBase } // IsEmpty determines whether or not a SingleTenantPage is empty. -func (page SingleTenantPage) IsEmpty() (bool, error) { - ks, err := ExtractSingleTenant(page) +func (r SingleTenantPage) IsEmpty() (bool, error) { + ks, err := ExtractSingleTenant(r) return ks == nil, err } +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r SingleTenantPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"tenant_usage_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + // ExtractSingleTenant interprets a SingleTenantPage as a TenantUsage result. func ExtractSingleTenant(page pagination.Page) (*TenantUsage, error) { var s struct { - TenantUsage *TenantUsage `json:"tenant_usage"` - TenantUsageLinks []gophercloud.Link `json:"tenant_usage_links"` + TenantUsage *TenantUsage `json:"tenant_usage"` } err := (page.(SingleTenantPage)).ExtractInto(&s) return s.TenantUsage, err } + +// AllTenantsPage stores a single, only page of TenantUsage results from a +// AllTenants call. +type AllTenantsPage struct { + pagination.LinkedPageBase +} + +// ExtractAllTenants interprets a AllTenantsPage as a TenantUsage result. +func ExtractAllTenants(page pagination.Page) ([]TenantUsage, error) { + var s struct { + TenantUsages []TenantUsage `json:"tenant_usages"` + } + err := (page.(AllTenantsPage)).ExtractInto(&s) + return s.TenantUsages, err +} + +// IsEmpty determines whether or not an AllTenantsPage is empty. +func (r AllTenantsPage) IsEmpty() (bool, error) { + usages, err := ExtractAllTenants(r) + return len(usages) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r AllTenantsPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"tenant_usages_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} diff --git a/openstack/compute/v2/extensions/usage/testing/fixtures.go b/openstack/compute/v2/extensions/usage/testing/fixtures.go index b7c1ae55f5..3e37947a4e 100644 --- a/openstack/compute/v2/extensions/usage/testing/fixtures.go +++ b/openstack/compute/v2/extensions/usage/testing/fixtures.go @@ -12,6 +12,7 @@ import ( ) const FirstTenantID = "aabbccddeeff112233445566" +const SecondTenantID = "665544332211ffeeddccbbaa" // GetSingleTenant holds the fixtures for the content of the request for a // single tenant. @@ -135,3 +136,179 @@ var SingleTenantUsageResults = usage.TenantUsage{ TotalMemoryMBUsage: 644.27116544, TotalVCPUsUsage: 1.25834212, } + +// GetAllTenants holds the fixtures for the content of the request for +// all tenants. +const GetAllTenants = `{ + "tenant_usages": [ + { + "server_usages": [ + { + "ended_at": null, + "flavor": "m1.tiny", + "hours": 0.021675453333333334, + "instance_id": "a70096fd-8196-406b-86c4-045840f53ad7", + "local_gb": 1, + "memory_mb": 512, + "name": "jttest", + "started_at": "2017-11-30T03:23:43.000000", + "state": "active", + "tenant_id": "aabbccddeeff112233445566", + "uptime": 78, + "vcpus": 1 + }, + { + "ended_at": "2017-11-21T04:10:11.000000", + "flavor": "m1.acctest", + "hours": 0.33444444444444443, + "instance_id": "c04e38f2-dcee-4ca8-9466-7708d0a9b6dd", + "local_gb": 15, + "memory_mb": 512, + "name": "basic", + "started_at": "2017-11-21T03:50:07.000000", + "state": "terminated", + "tenant_id": "aabbccddeeff112233445566", + "uptime": 1204, + "vcpus": 1 + }, + { + "ended_at": "2017-11-30T03:21:21.000000", + "flavor": "m1.acctest", + "hours": 0.004166666666666667, + "instance_id": "ceb654fa-e0e8-44fb-8942-e4d0bfad3941", + "local_gb": 15, + "memory_mb": 512, + "name": "ACPTTESTJSxbPQAC34lTnBE1", + "started_at": "2017-11-30T03:21:06.000000", + "state": "terminated", + "tenant_id": "aabbccddeeff112233445566", + "uptime": 15, + "vcpus": 1 + } + ], + "start": "2017-11-02T03:25:01.000000", + "stop": "2017-11-30T03:25:01.000000", + "tenant_id": "aabbccddeeff112233445566", + "total_hours": 1.25834212, + "total_local_gb_usage": 18.571675453333334, + "total_memory_mb_usage": 644.27116544, + "total_vcpus_usage": 1.25834212 + }, + { + "server_usages": [ + { + "ended_at": null, + "flavor": "m1.tiny", + "hours": 0.021675453333333334, + "instance_id": "a70096fd-8196-406b-86c4-045840f53ad7", + "local_gb": 1, + "memory_mb": 512, + "name": "test", + "started_at": "2017-11-30T03:23:43.000000", + "state": "active", + "tenant_id": "665544332211ffeeddccbbaa", + "uptime": 78, + "vcpus": 1 + } + ], + "start": "2017-11-02T03:25:01.000000", + "stop": "2017-11-30T03:25:01.000000", + "tenant_id": "665544332211ffeeddccbbaa", + "total_hours": 0.021675453333333334, + "total_local_gb_usage": 18.571675453333334, + "total_memory_mb_usage": 644.27116544, + "total_vcpus_usage": 1.25834212 + } + ] +}` + +// HandleGetAllTenantsSuccessfully configures the test server to respond to a +// Get request for all tenants. +func HandleGetAllTenantsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-simple-tenant-usage", 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.Fprint(w, GetAllTenants) + }) +} + +// AllTenantsUsageResult is the code fixture for GetAllTenants. +var AllTenantsUsageResult = []usage.TenantUsage{ + { + ServerUsages: []usage.ServerUsage{ + { + Flavor: "m1.tiny", + Hours: 0.021675453333333334, + InstanceID: "a70096fd-8196-406b-86c4-045840f53ad7", + LocalGB: 1, + MemoryMB: 512, + Name: "jttest", + StartedAt: time.Date(2017, 11, 30, 3, 23, 43, 0, time.UTC), + State: "active", + TenantID: "aabbccddeeff112233445566", + Uptime: 78, + VCPUs: 1, + }, + { + Flavor: "m1.acctest", + Hours: 0.33444444444444443, + InstanceID: "c04e38f2-dcee-4ca8-9466-7708d0a9b6dd", + LocalGB: 15, + MemoryMB: 512, + Name: "basic", + StartedAt: time.Date(2017, 11, 21, 3, 50, 7, 0, time.UTC), + EndedAt: time.Date(2017, 11, 21, 4, 10, 11, 0, time.UTC), + State: "terminated", + TenantID: "aabbccddeeff112233445566", + Uptime: 1204, + VCPUs: 1, + }, + { + Flavor: "m1.acctest", + Hours: 0.004166666666666667, + InstanceID: "ceb654fa-e0e8-44fb-8942-e4d0bfad3941", + LocalGB: 15, + MemoryMB: 512, + Name: "ACPTTESTJSxbPQAC34lTnBE1", + StartedAt: time.Date(2017, 11, 30, 3, 21, 6, 0, time.UTC), + EndedAt: time.Date(2017, 11, 30, 3, 21, 21, 0, time.UTC), + State: "terminated", + TenantID: "aabbccddeeff112233445566", + Uptime: 15, + VCPUs: 1, + }, + }, + Start: time.Date(2017, 11, 2, 3, 25, 1, 0, time.UTC), + Stop: time.Date(2017, 11, 30, 3, 25, 1, 0, time.UTC), + TenantID: "aabbccddeeff112233445566", + TotalHours: 1.25834212, + TotalLocalGBUsage: 18.571675453333334, + TotalMemoryMBUsage: 644.27116544, + TotalVCPUsUsage: 1.25834212, + }, + { + ServerUsages: []usage.ServerUsage{ + { + Flavor: "m1.tiny", + Hours: 0.021675453333333334, + InstanceID: "a70096fd-8196-406b-86c4-045840f53ad7", + LocalGB: 1, + MemoryMB: 512, + Name: "test", + StartedAt: time.Date(2017, 11, 30, 3, 23, 43, 0, time.UTC), + State: "active", + TenantID: "665544332211ffeeddccbbaa", + Uptime: 78, + VCPUs: 1, + }, + }, + Start: time.Date(2017, 11, 2, 3, 25, 1, 0, time.UTC), + Stop: time.Date(2017, 11, 30, 3, 25, 1, 0, time.UTC), + TenantID: "665544332211ffeeddccbbaa", + TotalHours: 0.021675453333333334, + TotalLocalGBUsage: 18.571675453333334, + TotalMemoryMBUsage: 644.27116544, + TotalVCPUsUsage: 1.25834212, + }, +} diff --git a/openstack/compute/v2/extensions/usage/testing/requests_test.go b/openstack/compute/v2/extensions/usage/testing/requests_test.go index 1b43f12aec..1b4f27694c 100644 --- a/openstack/compute/v2/extensions/usage/testing/requests_test.go +++ b/openstack/compute/v2/extensions/usage/testing/requests_test.go @@ -4,18 +4,49 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) func TestGetTenant(t *testing.T) { - var getOpts usage.SingleTenantOpts th.SetupHTTP() defer th.TeardownHTTP() HandleGetSingleTenantSuccessfully(t) - page, err := usage.SingleTenant(client.ServiceClient(), FirstTenantID, getOpts).AllPages() + + count := 0 + err := usage.SingleTenant(client.ServiceClient(), FirstTenantID, nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := usage.ExtractSingleTenant(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, &SingleTenantUsageResults, actual) + + return true, nil + }) th.AssertNoErr(t, err) - actual, err := usage.ExtractSingleTenant(page) + th.AssertEquals(t, count, 1) +} + +func TestAllTenants(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetAllTenantsSuccessfully(t) + + getOpts := usage.AllTenantsOpts{ + Detailed: true, + } + + count := 0 + err := usage.AllTenants(client.ServiceClient(), getOpts).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := usage.ExtractAllTenants(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, AllTenantsUsageResult, actual) + + return true, nil + }) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &SingleTenantUsageResults, actual) + th.AssertEquals(t, count, 1) } diff --git a/openstack/compute/v2/extensions/usage/urls.go b/openstack/compute/v2/extensions/usage/urls.go index f172b62211..5063610701 100644 --- a/openstack/compute/v2/extensions/usage/urls.go +++ b/openstack/compute/v2/extensions/usage/urls.go @@ -4,7 +4,7 @@ import "github.com/gophercloud/gophercloud" const resourcePath = "os-simple-tenant-usage" -func getURL(client *gophercloud.ServiceClient) string { +func allTenantsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(resourcePath) } From edd944db6f0236dd272a0a320d2cd02cfeea6425 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 15 Jul 2018 13:37:19 -0600 Subject: [PATCH 0480/2296] Compute v2: BootFromVolume disk_bus and device_type (#1129) * add 'disk_bus', 'device_type' support to create server * omitempty * Compute v2: bootfromvolume updates --- .../compute/v2/bootfromvolume_test.go | 36 ++ .../v2/extensions/bootfromvolume/requests.go | 8 + .../bootfromvolume/testing/fixtures.go | 275 ++++++++++++++++ .../bootfromvolume/testing/requests_test.go | 307 +----------------- 4 files changed, 331 insertions(+), 295 deletions(-) create mode 100644 openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go index 50a2396f94..ec5d72f757 100644 --- a/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -266,3 +266,39 @@ func TestAttachExistingVolume(t *testing.T) { // TODO: volumes_attached extension } + +func TestBootFromNewCustomizedVolume(t *testing.T) { + if testing.Short() { + t.Skip("Skipping test that requires server creation in short mode.") + } + + client, err := clients.NewComputeV2Client() + if err != nil { + t.Fatalf("Unable to create a compute client: %v", err) + } + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + blockDevices := []bootfromvolume.BlockDevice{ + bootfromvolume.BlockDevice{ + DeleteOnTermination: true, + DestinationType: bootfromvolume.DestinationVolume, + SourceType: bootfromvolume.SourceImage, + UUID: choices.ImageID, + VolumeSize: 2, + DeviceType: "disk", + DiskBus: "virtio", + }, + } + + server, err := CreateBootableVolumeServer(t, client, blockDevices) + if err != nil { + t.Fatalf("Unable to create server: %v", err) + } + defer DeleteServer(t, client, server) + + tools.PrintResource(t, server) +} diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go index 9dae14c7a9..30c6170117 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -67,6 +67,14 @@ type BlockDevice struct { // VolumeSize is the size of the volume to create (in gigabytes). This can be // omitted for existing volumes. VolumeSize int `json:"volume_size,omitempty"` + + // DeviceType specifies the device type of the block devices. + // Examples of this are disk, cdrom, floppy, lun, etc. + DeviceType string `json:"device_type,omitempty"` + + // DiskBus is the bus type of the block devices. + // Examples of this are ide, usb, virtio, scsi, etc. + DiskBus string `json:"disk_bus,omitempty"` } // CreateOptsExt is a structure that extends the server `CreateOpts` structure diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go new file mode 100644 index 0000000000..484e40a09a --- /dev/null +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go @@ -0,0 +1,275 @@ +package testing + +import ( + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" +) + +var BaseCreateOpts = servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", +} + +var BaseCreateOptsWithImageRef = servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + ImageRef: "asdfasdfasdf", +} + +const ExpectedNewVolumeRequest = ` +{ + "server": { + "name":"createdserver", + "flavorRef":"performance1-1", + "imageRef":"", + "block_device_mapping_v2":[ + { + "uuid":"123456", + "source_type":"image", + "destination_type":"volume", + "boot_index": 0, + "delete_on_termination": true, + "volume_size": 10 + } + ] + } +} +` + +var NewVolumeRequest = bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: BaseCreateOpts, + BlockDevice: []bootfromvolume.BlockDevice{ + { + UUID: "123456", + SourceType: bootfromvolume.SourceImage, + DestinationType: bootfromvolume.DestinationVolume, + VolumeSize: 10, + DeleteOnTermination: true, + }, + }, +} + +const ExpectedExistingVolumeRequest = ` +{ + "server": { + "name":"createdserver", + "flavorRef":"performance1-1", + "imageRef":"", + "block_device_mapping_v2":[ + { + "uuid":"123456", + "source_type":"volume", + "destination_type":"volume", + "boot_index": 0, + "delete_on_termination": true + } + ] + } +} +` + +var ExistingVolumeRequest = bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: BaseCreateOpts, + BlockDevice: []bootfromvolume.BlockDevice{ + { + UUID: "123456", + SourceType: bootfromvolume.SourceVolume, + DestinationType: bootfromvolume.DestinationVolume, + DeleteOnTermination: true, + }, + }, +} + +const ExpectedImageRequest = ` +{ + "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" + } + ] + } +} +` + +var ImageRequest = bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: BaseCreateOptsWithImageRef, + BlockDevice: []bootfromvolume.BlockDevice{ + { + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: bootfromvolume.DestinationLocal, + SourceType: bootfromvolume.SourceImage, + UUID: "asdfasdfasdf", + }, + }, +} + +const ExpectedMultiEphemeralRequest = ` +{ + "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":"local", + "guest_format":"ext4", + "source_type":"blank", + "volume_size": 1 + }, + { + "boot_index": -1, + "delete_on_termination": true, + "destination_type":"local", + "guest_format":"ext4", + "source_type":"blank", + "volume_size": 1 + } + ] + } +} +` + +var MultiEphemeralRequest = 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.DestinationLocal, + GuestFormat: "ext4", + SourceType: bootfromvolume.SourceBlank, + VolumeSize: 1, + }, + { + BootIndex: -1, + DeleteOnTermination: true, + DestinationType: bootfromvolume.DestinationLocal, + GuestFormat: "ext4", + SourceType: bootfromvolume.SourceBlank, + VolumeSize: 1, + }, + }, +} + +const ExpectedImageAndNewVolumeRequest = ` +{ + "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":"blank", + "volume_size": 1, + "device_type": "disk", + "disk_bus": "scsi" + } + ] + } +} +` + +var ImageAndNewVolumeRequest = 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.SourceBlank, + VolumeSize: 1, + DeviceType: "disk", + DiskBus: "scsi", + }, + }, +} + +const ExpectedImageAndExistingVolumeRequest = ` +{ + "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", + "uuid":"123456", + "volume_size": 1 + } + ] + } +} +` + +var ImageAndExistingVolumeRequest = 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, + 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 7fd3e7d84a..6b59ff722e 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go @@ -3,325 +3,42 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" ) func TestBootFromNewVolume(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - FlavorRef: "performance1-1", - } - ext := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: base, - BlockDevice: []bootfromvolume.BlockDevice{ - { - UUID: "123456", - SourceType: bootfromvolume.SourceImage, - DestinationType: bootfromvolume.DestinationVolume, - VolumeSize: 10, - DeleteOnTermination: true, - }, - }, - } - - expected := ` - { - "server": { - "name":"createdserver", - "flavorRef":"performance1-1", - "imageRef":"", - "block_device_mapping_v2":[ - { - "uuid":"123456", - "source_type":"image", - "destination_type":"volume", - "boot_index": 0, - "delete_on_termination": true, - "volume_size": 10 - } - ] - } - } - ` - actual, err := ext.ToServerCreateMap() + actual, err := NewVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) + th.CheckJSONEquals(t, ExpectedNewVolumeRequest, actual) } func TestBootFromExistingVolume(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - FlavorRef: "performance1-1", - } - - ext := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: base, - BlockDevice: []bootfromvolume.BlockDevice{ - { - UUID: "123456", - SourceType: bootfromvolume.SourceVolume, - DestinationType: bootfromvolume.DestinationVolume, - DeleteOnTermination: true, - }, - }, - } - - expected := ` - { - "server": { - "name":"createdserver", - "flavorRef":"performance1-1", - "imageRef":"", - "block_device_mapping_v2":[ - { - "uuid":"123456", - "source_type":"volume", - "destination_type":"volume", - "boot_index": 0, - "delete_on_termination": true - } - ] - } - } - ` - actual, err := ext.ToServerCreateMap() + actual, err := ExistingVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) + th.CheckJSONEquals(t, ExpectedExistingVolumeRequest, actual) } func TestBootFromImage(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - } - - ext := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: base, - BlockDevice: []bootfromvolume.BlockDevice{ - { - BootIndex: 0, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - SourceType: bootfromvolume.SourceImage, - UUID: "asdfasdfasdf", - }, - }, - } - - expected := ` - { - "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" - } - ] - } - } - ` - actual, err := ext.ToServerCreateMap() + actual, err := ImageRequest.ToServerCreateMap() th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) + th.CheckJSONEquals(t, ExpectedImageRequest, actual) } func TestCreateMultiEphemeralOpts(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - } - - ext := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: base, - BlockDevice: []bootfromvolume.BlockDevice{ - { - BootIndex: 0, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - SourceType: bootfromvolume.SourceImage, - UUID: "asdfasdfasdf", - }, - { - BootIndex: -1, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - GuestFormat: "ext4", - SourceType: bootfromvolume.SourceBlank, - VolumeSize: 1, - }, - { - BootIndex: -1, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - GuestFormat: "ext4", - SourceType: bootfromvolume.SourceBlank, - VolumeSize: 1, - }, - }, - } - - expected := ` - { - "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":"local", - "guest_format":"ext4", - "source_type":"blank", - "volume_size": 1 - }, - { - "boot_index": -1, - "delete_on_termination": true, - "destination_type":"local", - "guest_format":"ext4", - "source_type":"blank", - "volume_size": 1 - } - ] - } - } - ` - actual, err := ext.ToServerCreateMap() + actual, err := MultiEphemeralRequest.ToServerCreateMap() th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) + th.CheckJSONEquals(t, ExpectedMultiEphemeralRequest, actual) } func TestAttachNewVolume(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - } - - ext := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: base, - BlockDevice: []bootfromvolume.BlockDevice{ - { - BootIndex: 0, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - SourceType: bootfromvolume.SourceImage, - UUID: "asdfasdfasdf", - }, - { - BootIndex: 1, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationVolume, - SourceType: bootfromvolume.SourceBlank, - VolumeSize: 1, - }, - }, - } - - expected := ` - { - "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":"blank", - "volume_size": 1 - } - ] - } - } - ` - actual, err := ext.ToServerCreateMap() + actual, err := ImageAndNewVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) + th.CheckJSONEquals(t, ExpectedImageAndNewVolumeRequest, actual) } func TestAttachExistingVolume(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - } - - ext := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: base, - 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, - UUID: "123456", - VolumeSize: 1, - }, - }, - } - - expected := ` - { - "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", - "uuid":"123456", - "volume_size": 1 - } - ] - } - } - ` - actual, err := ext.ToServerCreateMap() + actual, err := ImageAndExistingVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) + th.CheckJSONEquals(t, ExpectedImageAndExistingVolumeRequest, actual) } From 7db9b0de445ad96ebdcb6a6acda8f653c1a5c2c6 Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Mon, 16 Jul 2018 19:36:57 -0700 Subject: [PATCH 0481/2296] Clean up Senlin webhook implementation and test (#1123) * Clean up Senlin webhook implementation and test - Clean up webhook test - Remove unnecessary import for crypto openpgp errors * Fix gofmt error --- .../clustering/v1/webhooktrigger_test.go | 47 +++++++++++++++---- openstack/clustering/v1/webhooks/requests.go | 4 +- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/acceptance/openstack/clustering/v1/webhooktrigger_test.go index 16c34cbea5..4b5434d1c1 100644 --- a/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -18,17 +19,47 @@ func TestClusteringWebhookTrigger(t *testing.T) { t.Fatalf("Unable to create clustering client: %v", err) } - // TODO: need to have cluster receiver created - receiverUUID := "f93f83f6-762b-41b6-b757-80507834d394" - actionID, err := webhooks.Trigger(client, receiverUUID, nil).Extract() + opts := webhooks.TriggerOpts{ + V: "1", + } + + // create profile, cluster and receiver first + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + receiver, err := CreateReceiver(t, client, cluster.ID) + th.AssertNoErr(t, err) + defer DeleteReceiver(t, client, receiver.ID) + + // trigger webhook + actionID, err := webhooks.Trigger(client, receiver.ID, opts).Extract() if err != nil { - // TODO: Uncomment next line once using real receiver - //t.Fatalf("Unable to extract webhooks trigger: %v", err) - t.Logf("TODO: Need to implement webhook trigger once PR receiver") + t.Fatalf("Unable to extract webhooks trigger: %v", err) } else { t.Logf("Webhook trigger action id %s", actionID) } - // TODO: Need to compare to make sure action ID exists - th.AssertEquals(t, true, true) + err = WaitForAction(client, actionID) + if err != nil { + t.Fatalf("Error scaling out cluster %s as a result from webhook trigger: %s:", cluster.ID, err) + } + + // check that new node was created + nodelistopts := nodes.ListOpts{ + ClusterID: cluster.ID, + } + + allPages, err := nodes.List(client, nodelistopts).AllPages() + th.AssertNoErr(t, err) + + allNodes, err := nodes.ExtractNodes(allPages) + th.AssertNoErr(t, err) + + // there should be 2 nodes in the cluster after triggering webhook + th.AssertEquals(t, len(allNodes), 2) } diff --git a/openstack/clustering/v1/webhooks/requests.go b/openstack/clustering/v1/webhooks/requests.go index 7ceff87225..1b211b4447 100644 --- a/openstack/clustering/v1/webhooks/requests.go +++ b/openstack/clustering/v1/webhooks/requests.go @@ -1,10 +1,10 @@ package webhooks import ( + "fmt" "net/url" "github.com/gophercloud/gophercloud" - "golang.org/x/crypto/openpgp/errors" ) // TriggerOpts represents options used for triggering an action @@ -42,7 +42,7 @@ func Trigger(client *gophercloud.ServiceClient, id string, opts TriggerOptsBuild } url += query } else { - r.Err = errors.InvalidArgumentError("Must contain V for TriggerOpt") + r.Err = fmt.Errorf("Must contain V for TriggerOpt") return } From 21d13e9de69ccd9d416580fee37c9231d21bec17 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 17 Jul 2018 02:37:27 +0000 Subject: [PATCH 0482/2296] BaseEndpoint Fix This commit fixes the BaseEndpoint utility function so that only the path is compared rather than the full URL. This is so the FQDN does not get accidentally parsed. --- openstack/utils/base_endpoint.go | 14 ++++++------- openstack/utils/testing/base_endpoint_test.go | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/openstack/utils/base_endpoint.go b/openstack/utils/base_endpoint.go index a846731790..40080f7af2 100644 --- a/openstack/utils/base_endpoint.go +++ b/openstack/utils/base_endpoint.go @@ -9,22 +9,20 @@ import ( // BaseEndpoint will return a URL without the /vX.Y // portion of the URL. func BaseEndpoint(endpoint string) (string, error) { - var base string - u, err := url.Parse(endpoint) if err != nil { - return base, err + return "", err } u.RawQuery, u.Fragment = "", "" - base = u.String() + path := u.Path versionRe := regexp.MustCompile("v[0-9.]+/?") - if version := versionRe.FindString(base); version != "" { - versionIndex := strings.Index(base, version) - base = base[:versionIndex] + if version := versionRe.FindString(path); version != "" { + versionIndex := strings.Index(path, version) + u.Path = path[:versionIndex] } - return base, nil + return u.String(), nil } diff --git a/openstack/utils/testing/base_endpoint_test.go b/openstack/utils/testing/base_endpoint_test.go index d67306ae40..944536b8bf 100644 --- a/openstack/utils/testing/base_endpoint_test.go +++ b/openstack/utils/testing/base_endpoint_test.go @@ -58,6 +58,26 @@ func TestBaseEndpoint(t *testing.T) { Endpoint: "http://example.com/identity/", BaseEndpoint: "http://example.com/identity/", }, + { + Endpoint: "http://dev.example.com:5000/v3", + BaseEndpoint: "http://dev.example.com:5000/", + }, + { + Endpoint: "http://dev.example.com:5000/v3.6", + BaseEndpoint: "http://dev.example.com:5000/", + }, + { + Endpoint: "http://dev.example.com/identity/", + BaseEndpoint: "http://dev.example.com/identity/", + }, + { + Endpoint: "http://dev.example.com/identity/v2.0/projects", + BaseEndpoint: "http://dev.example.com/identity/", + }, + { + Endpoint: "http://dev.example.com/identity/v3.6", + BaseEndpoint: "http://dev.example.com/identity/", + }, } for _, test := range tests { From b15dabc613327c95021cb421aa31b0f29b226be0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 18 Jul 2018 03:29:52 +0000 Subject: [PATCH 0483/2296] Compute v2: cannot pass SubnetID when attaching an interface --- openstack/compute/v2/extensions/attachinterfaces/requests.go | 2 +- openstack/compute/v2/extensions/attachinterfaces/results.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go index 18dade837c..874f7a61ec 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/requests.go +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -28,7 +28,6 @@ type CreateOptsBuilder interface { // CreateOpts specifies parameters of a new interface attachment. type CreateOpts struct { - // PortID is the ID of the port for which you want to create an interface. // The NetworkID and PortID parameters are mutually exclusive. // If you do not specify the PortID parameter, the OpenStack Networking API @@ -43,6 +42,7 @@ type CreateOpts struct { // Slice of FixedIPs. If you request a specific FixedIP address without a // NetworkID, the request returns a Bad Request (400) response code. + // Note: this uses the FixedIP struct, but only the IPAddress field can be used. FixedIPs []FixedIP `json:"fixed_ips,omitempty"` } diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/extensions/attachinterfaces/results.go index a16fa14f75..7d15e1ecb4 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/results.go +++ b/openstack/compute/v2/extensions/attachinterfaces/results.go @@ -37,8 +37,10 @@ type DeleteResult struct { } // FixedIP represents a Fixed IP Address. +// This struct is also used when creating an attachment, +// but it is not possible to specify a SubnetID. type FixedIP struct { - SubnetID string `json:"subnet_id"` + SubnetID string `json:"subnet_id,omitempty"` IPAddress string `json:"ip_address"` } From ef6628a63b53544b9c793f4c72925a1472436141 Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Wed, 18 Jul 2018 08:03:15 -0700 Subject: [PATCH 0484/2296] Magnum Certificates Get implementation (#1133) * Magnum Certificates Get implementation * Fix field names * Fix gofmt errors --- acceptance/clients/clients.go | 21 +++++++ .../containerinfra/v1/certificates_test.go | 22 ++++++++ openstack/client.go | 6 ++ .../containerinfra/v1/certificates/doc.go | 15 +++++ .../v1/certificates/requests.go | 16 ++++++ .../containerinfra/v1/certificates/results.go | 29 ++++++++++ .../v1/certificates/testing/doc.go | 1 + .../v1/certificates/testing/fixtures.go | 55 +++++++++++++++++++ .../v1/certificates/testing/requests_test.go | 23 ++++++++ .../containerinfra/v1/certificates/urls.go | 15 +++++ 10 files changed, 203 insertions(+) create mode 100644 acceptance/openstack/containerinfra/v1/certificates_test.go create mode 100644 openstack/containerinfra/v1/certificates/doc.go create mode 100644 openstack/containerinfra/v1/certificates/requests.go create mode 100644 openstack/containerinfra/v1/certificates/results.go create mode 100644 openstack/containerinfra/v1/certificates/testing/doc.go create mode 100644 openstack/containerinfra/v1/certificates/testing/fixtures.go create mode 100644 openstack/containerinfra/v1/certificates/testing/requests_test.go create mode 100644 openstack/containerinfra/v1/certificates/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index c04de120d4..72dd563faf 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -576,3 +576,24 @@ func configureDebug(client *gophercloud.ProviderClient) *gophercloud.ProviderCli return client } + +// NewContainerInfraV1Client returns a *ServiceClient for making calls +// to the OpenStack Container Infra Management v1 API. An error will be returned +// if authentication or client creation was not possible. +func NewContainerInfraV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewContainerInfraV1(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} diff --git a/acceptance/openstack/containerinfra/v1/certificates_test.go b/acceptance/openstack/containerinfra/v1/certificates_test.go new file mode 100644 index 0000000000..d1b17278a9 --- /dev/null +++ b/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -0,0 +1,22 @@ +// +build acceptance containerinfra + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCertificatesCRUD(t *testing.T) { + client, err := clients.NewContainerInfraV1Client() + th.AssertNoErr(t, err) + + clusterID := "8934d2d1-6bce-4ffa-a017-fb437777269d" + + certificate, err := certificates.Get(client, clusterID).Extract() + th.AssertNoErr(t, err) + t.Log(certificate.Pem) +} diff --git a/openstack/client.go b/openstack/client.go index 4c8c83ae8f..0859d9cb75 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -414,3 +414,9 @@ func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoint sc.ResourceBase = sc.Endpoint + "v1/" return sc, err } + +// NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management +// package. +func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "container-infra") +} diff --git a/openstack/containerinfra/v1/certificates/doc.go b/openstack/containerinfra/v1/certificates/doc.go new file mode 100644 index 0000000000..d949ebfee6 --- /dev/null +++ b/openstack/containerinfra/v1/certificates/doc.go @@ -0,0 +1,15 @@ +// Package certificates contains functionality for working with Magnum Certificate +// resources. +/* +Package certificates provides information and interaction with the certificates through +the OpenStack Container Infra service. + +Example to get certificates + + certificate, err := certificates.Get(serviceClient, "d564b18a-2890-4152-be3d-e05d784ff72").Extract() + if err != nil { + panic(err) + } + +*/ +package certificates diff --git a/openstack/containerinfra/v1/certificates/requests.go b/openstack/containerinfra/v1/certificates/requests.go new file mode 100644 index 0000000000..bda1fcef1b --- /dev/null +++ b/openstack/containerinfra/v1/certificates/requests.go @@ -0,0 +1,16 @@ +package certificates + +import ( + "github.com/gophercloud/gophercloud" +) + +// Get makes a request against the API to get details for a certificate. +func Get(client *gophercloud.ServiceClient, clusterID string) (r GetResult) { + url := getURL(client, clusterID) + + _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + + return +} diff --git a/openstack/containerinfra/v1/certificates/results.go b/openstack/containerinfra/v1/certificates/results.go new file mode 100644 index 0000000000..9d93f1a888 --- /dev/null +++ b/openstack/containerinfra/v1/certificates/results.go @@ -0,0 +1,29 @@ +package certificates + +import ( + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + +// Extract is a function that accepts a result and extracts a certificate resource. +func (r commonResult) Extract() (*Certificate, error) { + var s *Certificate + err := r.ExtractInto(&s) + return s, err +} + +// Represents a template for a Cluster Template +type Certificate struct { + ClusterUUID string `json:"cluster_uuid"` + BayUUID string `json:"bay_uuid"` + Links []gophercloud.Link `json:"links"` + Pem string `json:"pem"` +} diff --git a/openstack/containerinfra/v1/certificates/testing/doc.go b/openstack/containerinfra/v1/certificates/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/containerinfra/v1/certificates/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/containerinfra/v1/certificates/testing/fixtures.go b/openstack/containerinfra/v1/certificates/testing/fixtures.go new file mode 100644 index 0000000000..2e15683f94 --- /dev/null +++ b/openstack/containerinfra/v1/certificates/testing/fixtures.go @@ -0,0 +1,55 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const CertificateResponse = ` +{ + "cluster_uuid": "d564b18a-2890-4152-be3d-e05d784ff727", + "bay_uuid": "d564b18a-2890-4152-be3d-e05d784ff727", + "pem": "FAKE_CERTIFICATE", + "links": [ + { + "href": "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", + "rel": "self" + }, + { + "href": "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", + "rel": "bookmark" + } + ] +}` + +var ExpectedCertificate = certificates.Certificate{ + ClusterUUID: "d564b18a-2890-4152-be3d-e05d784ff727", + BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", + Pem: "FAKE_CERTIFICATE", + Links: []gophercloud.Link{ + {Href: "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "self"}, + {Href: "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "bookmark"}, + }, +} + +func HandleGetCertificateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff72", 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.Header().Add("OpenStack-API-Minimum-Version", "container-infra 1.1") + w.Header().Add("OpenStack-API-Maximum-Version", "container-infra 1.6") + w.Header().Add("OpenStack-API-Version", "container-infra 1.1") + w.Header().Add("X-OpenStack-Request-Id", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, CertificateResponse) + }) +} diff --git a/openstack/containerinfra/v1/certificates/testing/requests_test.go b/openstack/containerinfra/v1/certificates/testing/requests_test.go new file mode 100644 index 0000000000..cc3421c895 --- /dev/null +++ b/openstack/containerinfra/v1/certificates/testing/requests_test.go @@ -0,0 +1,23 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGetCertificates(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetCertificateSuccessfully(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + actual, err := certificates.Get(sc, "d564b18a-2890-4152-be3d-e05d784ff72").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCertificate, *actual) +} diff --git a/openstack/containerinfra/v1/certificates/urls.go b/openstack/containerinfra/v1/certificates/urls.go new file mode 100644 index 0000000000..e7d40937d7 --- /dev/null +++ b/openstack/containerinfra/v1/certificates/urls.go @@ -0,0 +1,15 @@ +package certificates + +import ( + "github.com/gophercloud/gophercloud" +) + +var apiName = "certificates" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiName) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiName, id) +} From 9bb899a7c1d92abca2da4992cff2f284fdf62cce Mon Sep 17 00:00:00 2001 From: scott-chaney <40778338+scott-chaney@users.noreply.github.com> Date: Fri, 20 Jul 2018 18:42:43 -0700 Subject: [PATCH 0485/2296] Implements heat/orchestration stack update PATCH verb. (#1131) * Implements heat/orchestration stack update PATCH verb. * Address PR feedback regarding calls to toStackUpdateMap() * Addresses PR feedback related to documentation. --- openstack/orchestration/v1/stacks/doc.go | 121 +++++++++++++++++- openstack/orchestration/v1/stacks/errors.go | 8 ++ openstack/orchestration/v1/stacks/requests.go | 72 ++++++++--- .../v1/stacks/testing/fixtures.go | 13 ++ .../v1/stacks/testing/requests_test.go | 34 ++++- provider_client.go | 2 +- 6 files changed, 223 insertions(+), 27 deletions(-) diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index 19231b5137..f8f2a6fefb 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -1,8 +1,115 @@ -// Package stacks provides operation for working with Heat stacks. A stack is a -// group of resources (servers, load balancers, databases, and so forth) -// combined to fulfill a useful purpose. Based on a template, Heat orchestration -// engine creates an instantiated set of resources (a stack) to run the -// 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. +/* +Package stacks provides operation for working with Heat stacks. A stack is a +group of resources (servers, load balancers, databases, and so forth) +combined to fulfill a useful purpose. Based on a template, Heat orchestration +engine creates an instantiated set of resources (a stack) to run the +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. + +Summary of Behavior Between Stack Update and UpdatePatch Methods : + +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 +Update() | Parameters ONLY | No update, template is required + +UpdatePatch() | Template AND Parameters WITH Conflict | Parameter takes priority, parameters are set in raw_template.environment overlay +UpdatePatch() | Template ONLY | Template updates, but raw_template.environment overlay is not removed, existing parameter values will remain +UpdatePatch() | Parameters ONLY | Parameters (raw_template.environment) is updated, excluded values are unchanged + +The PUT Update() function will remove parameters from the raw_template.environment overlay +if they are excluded from the operation, whereas PATCH Update() will never be destructive to the +raw_template.environment overlay. It is not possible to expose the raw_template values with a +patch update once they have been added to the environment overlay with the PATCH verb, but +newly added values that do not have a corresponding key in the overlay will display the +raw_template value. + +Example to Update a Stack Using the Update (PUT) Method + + 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, + } + + var params = make(map[string]interface{}) + params["number_of_nodes"] = 2 + + stackName := "my_stack" + stackId := "d68cc349-ccc5-4b44-a17d-07f068c01e5a" + + stackOpts := &stacks.UpdateOpts{ + Parameters: params, + TemplateOpts: &template, + } + + res := stacks.Update(orchestrationClient, stackName, stackId, stackOpts) + if res.Err != nil { + panic(res.Err) + } + +Example to Update a Stack Using the UpdatePatch (PATCH) Method + + var params = make(map[string]interface{}) + params["number_of_nodes"] = 2 + + stackName := "my_stack" + stackId := "d68cc349-ccc5-4b44-a17d-07f068c01e5a" + + stackOpts := &stacks.UpdateOpts{ + Parameters: params, + } + + res := stacks.UpdatePatch(orchestrationClient, stackName, stackId, stackOpts) + if res.Err != nil { + panic(res.Err) + } + +Example YAML Template Containing a Heat::ResourceGroup With Three Nodes + + heat_template_version: 2016-04-08 + + parameters: + number_of_nodes: + type: number + default: 3 + description: the number of nodes + node_flavor: + type: string + default: m1.small + description: node flavor + node_image: + type: string + default: centos7.5-latest + description: node os image + node_network: + type: string + default: my-node-network + description: node network name + + resources: + resource_group: + type: OS::Heat::ResourceGroup + properties: + count: { get_param: number_of_nodes } + resource_def: + type: OS::Nova::Server + properties: + name: my_nova_server_%index% + image: { get_param: node_image } + flavor: { get_param: node_flavor } + networks: + - network: {get_param: node_network} +*/ package stacks diff --git a/openstack/orchestration/v1/stacks/errors.go b/openstack/orchestration/v1/stacks/errors.go index cd6c18f758..a6febe0408 100644 --- a/openstack/orchestration/v1/stacks/errors.go +++ b/openstack/orchestration/v1/stacks/errors.go @@ -31,3 +31,11 @@ type ErrInvalidTemplateFormatVersion struct { func (e ErrInvalidTemplateFormatVersion) Error() string { return fmt.Sprintf("Template format version not found.") } + +type ErrTemplateRequired struct { + gophercloud.BaseError +} + +func (e ErrTemplateRequired) Error() string { + return fmt.Sprintf("Template required for this function.") +} diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index 91f38ee7e6..5df222ff0c 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -265,42 +265,66 @@ type UpdateOptsBuilder interface { ToStackUpdateMap() (map[string]interface{}, error) } +// UpdatePatchOptsBuilder is the interface options structs have to satisfy in order +// to be used in the UpdatePatch operation in this package +type UpdatePatchOptsBuilder interface { + ToStackUpdatePatchMap() (map[string]interface{}, error) +} + // UpdateOpts contains the common options struct used in this package's Update -// operation. +// and UpdatePatch operations. type UpdateOpts struct { // 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 `json:"-" required:"true"` + TemplateOpts *Template `json:"-"` // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. - Parameters map[string]string `json:"parameters,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` // The timeout for stack creation in minutes. Timeout int `json:"timeout_mins,omitempty"` - // A list of tags to assosciate with the Stack + // A list of tags to associate with the Stack Tags []string `json:"-"` } -// ToStackUpdateMap casts a CreateOpts struct to a map. +// ToStackUpdateMap validates that a template was supplied and calls +// the toStackUpdateMap private function. func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "") - if err != nil { - return nil, err + if opts.TemplateOpts == nil { + return nil, ErrTemplateRequired{} } + return toStackUpdateMap(opts) +} - if err := opts.TemplateOpts.Parse(); err != nil { - return nil, err - } +// ToStackUpdatePatchMap calls the private function toStackUpdateMap +// directly. +func (opts UpdateOpts) ToStackUpdatePatchMap() (map[string]interface{}, error) { + return toStackUpdateMap(opts) +} - if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { +// ToStackUpdateMap casts a CreateOpts struct to a map. +func toStackUpdateMap(opts UpdateOpts) (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { return nil, err } - opts.TemplateOpts.fixFileRefs() - b["template"] = string(opts.TemplateOpts.Bin) files := make(map[string]string) - for k, v := range opts.TemplateOpts.Files { - files[k] = v + + if opts.TemplateOpts != nil { + if err := opts.TemplateOpts.Parse(); err != nil { + return nil, err + } + + if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { + return nil, err + } + opts.TemplateOpts.fixFileRefs() + b["template"] = string(opts.TemplateOpts.Bin) + + for k, v := range opts.TemplateOpts.Files { + files[k] = v + } } if opts.EnvironmentOpts != nil { @@ -328,8 +352,8 @@ func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) { return b, nil } -// Update accepts an UpdateOpts struct and updates an existing stack using the values -// provided. +// Update accepts an UpdateOpts struct and updates an existing stack using the +// 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 { @@ -340,6 +364,18 @@ func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts Update return } +// Update accepts an UpdateOpts struct and updates an existing stack using the +// 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 { + r.Err = err + return + } + _, r.Err = c.Patch(updateURL(c, stackName, stackID), b, nil, nil) + return +} + // Delete deletes a stack based on the stack name and stack ID. func Delete(c *gophercloud.ServiceClient, stackName, stackID string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, stackName, stackID), nil) diff --git a/openstack/orchestration/v1/stacks/testing/fixtures.go b/openstack/orchestration/v1/stacks/testing/fixtures.go index f3e3b57d1a..7c8a2f32d2 100644 --- a/openstack/orchestration/v1/stacks/testing/fixtures.go +++ b/openstack/orchestration/v1/stacks/testing/fixtures.go @@ -233,6 +233,19 @@ func HandleUpdateSuccessfully(t *testing.T) { }) } +// HandleUpdatePatchSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` +// on the test handler mux that responds with an `Update` response. +func HandleUpdatePatchSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/stacks/gophercloud-test-stack-2/db6977b2-27aa-4775-9ae7-6213212d4ada", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) +} + // HandleDeleteSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` // on the test handler mux that responds with a `Delete` response. func HandleDeleteSuccessfully(t *testing.T) { diff --git a/openstack/orchestration/v1/stacks/testing/requests_test.go b/openstack/orchestration/v1/stacks/testing/requests_test.go index bdc6229831..f8bd1fe201 100644 --- a/openstack/orchestration/v1/stacks/testing/requests_test.go +++ b/openstack/orchestration/v1/stacks/testing/requests_test.go @@ -133,13 +133,45 @@ func TestUpdateStack(t *testing.T) { } } }`) - updateOpts := stacks.UpdateOpts{ + updateOpts := &stacks.UpdateOpts{ TemplateOpts: template, } err := stacks.Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() th.AssertNoErr(t, err) } +func TestUpdateStackNoTemplate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateSuccessfully(t) + + parameters := make(map[string]interface{}) + parameters["flavor"] = "m1.tiny" + + updateOpts := &stacks.UpdateOpts{ + Parameters: parameters, + } + expected := stacks.ErrTemplateRequired{} + + err := stacks.Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() + th.AssertEquals(t, expected, err) +} + +func TestUpdatePatchStack(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdatePatchSuccessfully(t) + + parameters := make(map[string]interface{}) + parameters["flavor"] = "m1.tiny" + + updateOpts := &stacks.UpdateOpts{ + Parameters: parameters, + } + err := stacks.UpdatePatch(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() + th.AssertNoErr(t, err) +} + func TestDeleteStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/provider_client.go b/provider_client.go index 17e4512743..77368f0140 100644 --- a/provider_client.go +++ b/provider_client.go @@ -378,7 +378,7 @@ func defaultOkCodes(method string) []int { case method == "PUT": return []int{201, 202} case method == "PATCH": - return []int{200, 204} + return []int{200, 202, 204} case method == "DELETE": return []int{202, 204} } From f1bae72a73954e0636d2de309b417bcfa4bd7de7 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Wed, 25 Jul 2018 01:40:24 +0000 Subject: [PATCH 0486/2296] fix pagination for Cinder volumes (#1139) * fix pagination for Cinder volumes * parity with v3 implementation of volume pagination --- openstack/blockstorage/v2/volumes/requests.go | 15 ++++++++++++++- openstack/blockstorage/v2/volumes/results.go | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index cbe96f1532..b7977cbc8f 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -98,6 +98,19 @@ type ListOpts struct { // TenantID will filter by a specific tenant/project ID. // Setting AllTenants is required for this. TenantID string `q:"project_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` } // ToVolumeListQuery formats a ListOpts into a query string. @@ -118,7 +131,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumePage{pagination.SinglePageBase(r)} + return VolumePage{pagination.LinkedPageBase{PageResult: r}} }) } diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/blockstorage/v2/volumes/results.go index 674ec34686..96572b01b4 100644 --- a/openstack/blockstorage/v2/volumes/results.go +++ b/openstack/blockstorage/v2/volumes/results.go @@ -98,7 +98,7 @@ func (r *Volume) UnmarshalJSON(b []byte) error { // VolumePage is a pagination.pager that is returned from a call to the List function. type VolumePage struct { - pagination.SinglePageBase + pagination.LinkedPageBase } // IsEmpty returns true if a ListResult contains no Volumes. @@ -107,6 +107,19 @@ func (r VolumePage) IsEmpty() (bool, error) { return len(volumes) == 0, err } +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r VolumePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"volumes_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. func ExtractVolumes(r pagination.Page) ([]Volume, error) { var s []Volume From 7bfc7361eb1ed8936960754390afbc8a65e52621 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 26 Jul 2018 05:01:20 +0300 Subject: [PATCH 0487/2296] Imageservice V2: implement tasks Get method (#1147) * Imageservice V2: implement tasks List method Add the new "tasks" package with basic structures and List method. Add unit and acceptance test with documentation. * Imageservice tasks: use string type for status Use the standard string type for the task status. Add inline documentation for the pre-defined TaskStatus type and its well-known values. * Imageservice V2: implement tasks Get method Implement Get method for the Imageservice tasks. Add unit test with documentation example. --- .../openstack/imageservice/v2/tasks_test.go | 51 +++++++++ openstack/imageservice/v2/tasks/doc.go | 34 ++++++ openstack/imageservice/v2/tasks/requests.go | 95 +++++++++++++++ openstack/imageservice/v2/tasks/results.go | 108 ++++++++++++++++++ .../imageservice/v2/tasks/testing/doc.go | 2 + .../imageservice/v2/tasks/testing/fixtures.go | 85 ++++++++++++++ .../v2/tasks/testing/requests_test.go | 89 +++++++++++++++ openstack/imageservice/v2/tasks/urls.go | 51 +++++++++ 8 files changed, 515 insertions(+) create mode 100644 acceptance/openstack/imageservice/v2/tasks_test.go create mode 100644 openstack/imageservice/v2/tasks/doc.go create mode 100644 openstack/imageservice/v2/tasks/requests.go create mode 100644 openstack/imageservice/v2/tasks/results.go create mode 100644 openstack/imageservice/v2/tasks/testing/doc.go create mode 100644 openstack/imageservice/v2/tasks/testing/fixtures.go create mode 100644 openstack/imageservice/v2/tasks/testing/requests_test.go create mode 100644 openstack/imageservice/v2/tasks/urls.go diff --git a/acceptance/openstack/imageservice/v2/tasks_test.go b/acceptance/openstack/imageservice/v2/tasks_test.go new file mode 100644 index 0000000000..a8020f06d1 --- /dev/null +++ b/acceptance/openstack/imageservice/v2/tasks_test.go @@ -0,0 +1,51 @@ +// +build acceptance imageservice tasks + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestTasksListEachPage(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + th.AssertNoErr(t, err) + + listOpts := tasks.ListOpts{ + Limit: 1, + } + + pager := tasks.List(client, listOpts) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + tasks, err := tasks.ExtractTasks(page) + th.AssertNoErr(t, err) + + for _, task := range tasks { + tools.PrintResource(t, task) + } + + return true, nil + }) +} + +func TestTasksListAllPages(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + th.AssertNoErr(t, err) + + listOpts := tasks.ListOpts{} + + allPages, err := tasks.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allTasks, err := tasks.ExtractTasks(allPages) + th.AssertNoErr(t, err) + + for _, i := range allTasks { + tools.PrintResource(t, i) + } +} diff --git a/openstack/imageservice/v2/tasks/doc.go b/openstack/imageservice/v2/tasks/doc.go new file mode 100644 index 0000000000..64daa4717a --- /dev/null +++ b/openstack/imageservice/v2/tasks/doc.go @@ -0,0 +1,34 @@ +/* +Package tasks enables management and retrieval of tasks from the OpenStack +Imageservice. + +Example to List Tasks + + listOpts := tasks.ListOpts{ + Owner: "424e7cf0243c468ca61732ba45973b3e", + } + + allPages, err := tasks.List(imagesClient, listOpts).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) + } + +Example to Get a Task + + task, err := tasks.Get(imagesClient, "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", task) +*/ +package tasks diff --git a/openstack/imageservice/v2/tasks/requests.go b/openstack/imageservice/v2/tasks/requests.go new file mode 100644 index 0000000000..18b8327414 --- /dev/null +++ b/openstack/imageservice/v2/tasks/requests.go @@ -0,0 +1,95 @@ +package tasks + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// TaskStatus represents valid task status. +// You can use this type to compare the actual status of a task to a one of the +// pre-defined statuses. +type TaskStatus string + +const ( + // TaskStatusPending represents status of the pending task. + TaskStatusPending TaskStatus = "pending" + + // TaskStatusProcessing represents status of the processing task. + TaskStatusProcessing TaskStatus = "processing" + + // TaskStatusSuccess represents status of the success task. + TaskStatusSuccess TaskStatus = "success" + + // TaskStatusFailure represents status of the failure task. + TaskStatusFailure TaskStatus = "failure" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToTaskListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the OpenStack Imageservice tasks API. +type ListOpts struct { + // Integer value for the limit of values to return. + Limit int `q:"limit"` + + // ID of the task at which you want to set a marker. + Marker string `q:"marker"` + + // SortDir allows to select sort direction. + // It can be "asc" or "desc" (default). + SortDir string `q:"sort_dir"` + + // SortKey allows to sort by one of the following tTask attributes: + // - created_at + // - expires_at + // - status + // - type + // - updated_at + // Default is created_at. + SortKey string `q:"sort_key"` + + // ID filters on the identifier of the task. + ID string `json:"id"` + + // Type filters on the type of the task. + Type string `json:"type"` + + // Status filters on the status of the task. + Status TaskStatus `q:"status"` +} + +// ToTaskListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTaskListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of the tasks. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToTaskListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + taskPage := TaskPage{ + serviceURL: c.ServiceURL(), + LinkedPageBase: pagination.LinkedPageBase{PageResult: r}, + } + + return taskPage + }) +} + +// Get retrieves a specific Imageservice task based on its ID. +func Get(c *gophercloud.ServiceClient, taskID string) (r GetResult) { + _, r.Err = c.Get(getURL(c, taskID), &r.Body, nil) + return +} diff --git a/openstack/imageservice/v2/tasks/results.go b/openstack/imageservice/v2/tasks/results.go new file mode 100644 index 0000000000..38928362af --- /dev/null +++ b/openstack/imageservice/v2/tasks/results.go @@ -0,0 +1,108 @@ +package tasks + +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret it as a Task. +type GetResult struct { + commonResult +} + +// Task represents a single task of the OpenStack Image service. +type Task struct { + // ID is a unique identifier of the task. + ID string `json:"id"` + + // Type represents the type of the task. + Type string `json:"type"` + + // Status represents current status of the task. + // You can use the TaskStatus custom type to unmarshal raw JSON response into + // the pre-defined valid task status. + Status string `json:"status"` + + // Input represents different parameters for the task. + Input map[string]interface{} `json:"input"` + + // Result represents task result details. + Result map[string]interface{} `json:"result"` + + // Owner is a unique identifier of the task owner. + Owner string `json:"owner"` + + // Message represents human-readable message that is usually populated + // on task failure. + Message string `json:"message"` + + // ExpiresAt contains the timestamp of when the task will become a subject of + // removal. + ExpiresAt time.Time `json:"expires_at"` + + // CreatedAt contains the task creation timestamp. + CreatedAt time.Time `json:"created_at"` + + // UpdatedAt contains the latest timestamp of when the task was updated. + UpdatedAt time.Time `json:"updated_at"` + + // Self contains URI for the task. + Self string `json:"self"` + + // Schema the path to the JSON-schema that represent the task. + Schema string `json:"schema"` +} + +// Extract interprets any commonResult as a Task. +func (r commonResult) Extract() (*Task, error) { + var s *Task + err := r.ExtractInto(&s) + return s, err +} + +// TaskPage represents the results of a List request. +type TaskPage struct { + serviceURL string + pagination.LinkedPageBase +} + +// IsEmpty returns true if a TaskPage contains no Tasks results. +func (r TaskPage) IsEmpty() (bool, error) { + tasks, err := ExtractTasks(r) + return len(tasks) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to +// the next page of results. +func (r TaskPage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + + if s.Next == "" { + return "", nil + } + + return nextPageURL(r.serviceURL, s.Next) +} + +// ExtractTasks interprets the results of a single page from a List() call, +// producing a slice of Task entities. +func ExtractTasks(r pagination.Page) ([]Task, error) { + var s struct { + Tasks []Task `json:"tasks"` + } + err := (r.(TaskPage)).ExtractInto(&s) + return s.Tasks, err +} diff --git a/openstack/imageservice/v2/tasks/testing/doc.go b/openstack/imageservice/v2/tasks/testing/doc.go new file mode 100644 index 0000000000..8d4a768447 --- /dev/null +++ b/openstack/imageservice/v2/tasks/testing/doc.go @@ -0,0 +1,2 @@ +// tasks unit tests +package testing diff --git a/openstack/imageservice/v2/tasks/testing/fixtures.go b/openstack/imageservice/v2/tasks/testing/fixtures.go new file mode 100644 index 0000000000..dadf6c2ebe --- /dev/null +++ b/openstack/imageservice/v2/tasks/testing/fixtures.go @@ -0,0 +1,85 @@ +package testing + +import ( + "time" + + "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" +) + +// TasksListResult represents raw server response from a server to a list call. +const TasksListResult = ` +{ + "schema": "/v2/schemas/tasks", + "tasks": [ + { + "status": "pending", + "self": "/v2/tasks/1252f636-1246-4319-bfba-c47cde0efbe0", + "updated_at": "2018-07-25T08:59:14Z", + "id": "1252f636-1246-4319-bfba-c47cde0efbe0", + "owner": "424e7cf0243c468ca61732ba45973b3e", + "type": "import", + "created_at": "2018-07-25T08:59:13Z", + "schema": "/v2/schemas/task" + }, + { + "status": "processing", + "self": "/v2/tasks/349a51f4-d51d-47b6-82da-4fa516f0ca32", + "updated_at": "2018-07-25T08:56:19Z", + "id": "349a51f4-d51d-47b6-82da-4fa516f0ca32", + "owner": "fb57277ef2f84a0e85b9018ec2dedbf7", + "type": "import", + "created_at": "2018-07-25T08:56:17Z", + "schema": "/v2/schemas/task" + } + ], + "first": "/v2/tasks?sort_key=status&sort_dir=desc&limit=20" +} +` + +// Task1 is an expected representation of a first task from the TasksListResult. +var Task1 = tasks.Task{ + ID: "1252f636-1246-4319-bfba-c47cde0efbe0", + Status: string(tasks.TaskStatusPending), + Type: "import", + Owner: "424e7cf0243c468ca61732ba45973b3e", + CreatedAt: time.Date(2018, 7, 25, 8, 59, 13, 0, time.UTC), + UpdatedAt: time.Date(2018, 7, 25, 8, 59, 14, 0, time.UTC), + Self: "/v2/tasks/1252f636-1246-4319-bfba-c47cde0efbe0", + Schema: "/v2/schemas/task", +} + +// Task2 is an expected representation of a first task from the TasksListResult. +var Task2 = tasks.Task{ + ID: "349a51f4-d51d-47b6-82da-4fa516f0ca32", + Status: string(tasks.TaskStatusProcessing), + Type: "import", + Owner: "fb57277ef2f84a0e85b9018ec2dedbf7", + CreatedAt: time.Date(2018, 7, 25, 8, 56, 17, 0, time.UTC), + UpdatedAt: time.Date(2018, 7, 25, 8, 56, 19, 0, time.UTC), + Self: "/v2/tasks/349a51f4-d51d-47b6-82da-4fa516f0ca32", + Schema: "/v2/schemas/task", +} + +// TasksGetResult represents raw server response from a server to a get call. +const TasksGetResult = ` +{ + "status": "pending", + "created_at": "2018-07-25T08:59:13Z", + "updated_at": "2018-07-25T08:59:14Z", + "self": "/v2/tasks/1252f636-1246-4319-bfba-c47cde0efbe0", + "result": null, + "owner": "424e7cf0243c468ca61732ba45973b3e", + "input": { + "image_properties": { + "container_format": "bare", + "disk_format": "raw" + }, + "import_from_format": "raw", + "import_from": "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img" + }, + "message": "", + "type": "import", + "id": "1252f636-1246-4319-bfba-c47cde0efbe0", + "schema": "/v2/schemas/task" +} +` diff --git a/openstack/imageservice/v2/tasks/testing/requests_test.go b/openstack/imageservice/v2/tasks/testing/requests_test.go new file mode 100644 index 0000000000..b941a78d7d --- /dev/null +++ b/openstack/imageservice/v2/tasks/testing/requests_test.go @@ -0,0 +1,89 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + fakeclient "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, TasksListResult) + }) + + count := 0 + + tasks.List(fakeclient.ServiceClient(), tasks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := tasks.ExtractTasks(page) + if err != nil { + t.Errorf("Failed to extract tasks: %v", err) + return false, nil + } + + expected := []tasks.Task{ + Task1, + Task2, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/tasks/1252f636-1246-4319-bfba-c47cde0efbe0", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, TasksGetResult) + }) + + s, err := tasks.Get(fakeclient.ServiceClient(), "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, string(tasks.TaskStatusPending)) + th.AssertEquals(t, s.CreatedAt, time.Date(2018, 7, 25, 8, 59, 13, 0, time.UTC)) + th.AssertEquals(t, s.UpdatedAt, time.Date(2018, 7, 25, 8, 59, 14, 0, time.UTC)) + th.AssertEquals(t, s.Self, "/v2/tasks/1252f636-1246-4319-bfba-c47cde0efbe0") + th.AssertEquals(t, s.Owner, "424e7cf0243c468ca61732ba45973b3e") + th.AssertEquals(t, s.Message, "") + th.AssertEquals(t, s.Type, "import") + th.AssertEquals(t, s.ID, "1252f636-1246-4319-bfba-c47cde0efbe0") + th.AssertEquals(t, s.Schema, "/v2/schemas/task") + th.AssertDeepEquals(t, s.Result, map[string]interface{}(nil)) + th.AssertDeepEquals(t, s.Input, map[string]interface{}{ + "import_from": "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", + "import_from_format": "raw", + "image_properties": map[string]interface{}{ + "container_format": "bare", + "disk_format": "raw", + }, + }) +} diff --git a/openstack/imageservice/v2/tasks/urls.go b/openstack/imageservice/v2/tasks/urls.go new file mode 100644 index 0000000000..abed8b8a28 --- /dev/null +++ b/openstack/imageservice/v2/tasks/urls.go @@ -0,0 +1,51 @@ +package tasks + +import ( + "net/url" + "strings" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" +) + +const resourcePath = "tasks" + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, taskID string) string { + return c.ServiceURL(resourcePath, taskID) +} + +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +func getURL(c *gophercloud.ServiceClient, taskID string) string { + return resourceURL(c, taskID) +} + +func nextPageURL(serviceURL, requestedNext string) (string, error) { + base, err := utils.BaseEndpoint(serviceURL) + if err != nil { + return "", err + } + + requestedNextURL, err := url.Parse(requestedNext) + if err != nil { + return "", err + } + + base = gophercloud.NormalizeURL(base) + nextPath := base + strings.TrimPrefix(requestedNextURL.Path, "/") + + nextURL, err := url.Parse(nextPath) + if err != nil { + return "", err + } + + nextURL.RawQuery = requestedNextURL.RawQuery + + return nextURL.String(), nil +} From cdda3872d86caee0502fde4c7ba78abcc3ae0b4a Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Wed, 25 Jul 2018 19:26:19 -0700 Subject: [PATCH 0488/2296] Magnum: Manage Cluster Templates Create (#1067) * magnum-clustertemplates-create * Use actual server's clustertemplates response for unit test rather than from doc * Add UserID and ProjectID Use default time.Time unmarshalling Removed empty time unit test * Constructing unit test url by appending "v1" to endpoint url * Fixed using the proper response code * Clean up doc by removing non-Create sections * Reorg the createopt fields to match the source code Added missing fields * Removed ClusterDistro field * Fixed unit test to match input with output * Changed bool fields to *bool * Added choices.FlavorID to acceptance test * Sort field names lexigraphically * Changed from int to *int to allow json null * Added OS_MAGNUM_IMAGE_ID --- acceptance/clients/clients.go | 5 + .../v1/clustertemplates_test.go | 67 ++++++++++ .../containerinfra/v1/containerinfra.go | 83 +++++++++++++ acceptance/openstack/containerinfra/v1/pkg.go | 1 + .../containerinfra/v1/clustertemplates/doc.go | 42 +++++++ .../v1/clustertemplates/requests.go | 66 ++++++++++ .../v1/clustertemplates/results.go | 60 +++++++++ .../v1/clustertemplates/testing/doc.go | 1 + .../v1/clustertemplates/testing/fixtures.go | 114 ++++++++++++++++++ .../clustertemplates/testing/requests_test.go | 59 +++++++++ .../v1/clustertemplates/urls.go | 15 +++ 11 files changed, 513 insertions(+) create mode 100644 acceptance/openstack/containerinfra/v1/clustertemplates_test.go create mode 100644 acceptance/openstack/containerinfra/v1/containerinfra.go create mode 100644 acceptance/openstack/containerinfra/v1/pkg.go create mode 100644 openstack/containerinfra/v1/clustertemplates/doc.go create mode 100644 openstack/containerinfra/v1/clustertemplates/requests.go create mode 100644 openstack/containerinfra/v1/clustertemplates/results.go create mode 100644 openstack/containerinfra/v1/clustertemplates/testing/doc.go create mode 100644 openstack/containerinfra/v1/clustertemplates/testing/fixtures.go create mode 100644 openstack/containerinfra/v1/clustertemplates/testing/requests_test.go create mode 100644 openstack/containerinfra/v1/clustertemplates/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 72dd563faf..e1458ebd13 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -29,6 +29,9 @@ type AcceptanceTestChoices struct { // FloatingIPPool contains the name of the pool from where to obtain floating IPs. FloatingIPPoolName string + // MagnumImageID contains the ID of a valid magnum image. + MagnumImageID string + // NetworkName is the name of a network to launch the instance on. NetworkName string @@ -51,6 +54,7 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { imageID := os.Getenv("OS_IMAGE_ID") flavorID := os.Getenv("OS_FLAVOR_ID") flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE") + magnumImageID := os.Getenv("OS_MAGNUM_IMAGE_ID") networkName := os.Getenv("OS_NETWORK_NAME") floatingIPPoolName := os.Getenv("OS_POOL_NAME") externalNetworkID := os.Getenv("OS_EXTGW_ID") @@ -102,6 +106,7 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { FlavorID: flavorID, FlavorIDResize: flavorIDResize, FloatingIPPoolName: floatingIPPoolName, + MagnumImageID: magnumImageID, NetworkName: networkName, ExternalNetworkID: externalNetworkID, ShareNetworkID: shareNetworkID, diff --git a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go new file mode 100644 index 0000000000..48e76d3f65 --- /dev/null +++ b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -0,0 +1,67 @@ +// +build acceptance containerinfra + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + //"github.com/gophercloud/gophercloud/acceptance/tools" + //"github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestClusterTemplatesCRUD(t *testing.T) { + client, err := clients.NewContainerInfraV1Client() + th.AssertNoErr(t, err) + + clusterTemplate, err := CreateClusterTemplate(t, client) + th.AssertNoErr(t, err) + t.Log(clusterTemplate.Name) + /* + defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) + + // Test clusters list + allPages, err := clustertemplates.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allClusterTemplates, err := clustertemplates.ExtractClusterTemplates(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allClusterTemplates { + if v.UUID == clusterTemplate.UUID { + found = true + } + } + + th.AssertEquals(t, found, true) + + // Test cluster update + updateOpts := []clustertemplates.UpdateOptsBuilder{ + clustertemplates.UpdateOpts{ + Path: "/master_lb_enabled", + Value: "false", + Op: "replace", + }, + clustertemplates.UpdateOpts{ + Path: "/registry_enabled", + Value: "false", + Op: "replace", + }, + clustertemplates.UpdateOpts{ + Path: "/labels/test", + Value: "test", + Op: "add", + }, + } + + updateClusterTemplate, err := clustertemplates.Update(client, clusterTemplate.UUID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, false, updateClusterTemplate.MasterLBEnabled) + th.AssertEquals(t, false, updateClusterTemplate.RegistryEnabled) + th.AssertEquals(t, "test", updateClusterTemplate.Labels["test"]) + tools.PrintResource(t, updateClusterTemplate) + + */ +} diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go new file mode 100644 index 0000000000..e4c24b09db --- /dev/null +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -0,0 +1,83 @@ +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// CreateClusterTemplate will create a random cluster tempalte. An error will be returned if the +// cluster-template could not be created. +func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*clustertemplates.ClusterTemplate, error) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + return nil, err + } + + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create cluster template: %s", name) + + boolFalse := false + createOpts := clustertemplates.CreateOpts{ + Name: name, + MasterFlavorID: "m1.small", + Public: &boolFalse, + ServerType: "vm", + ExternalNetworkID: choices.ExternalNetworkID, + ImageID: choices.MagnumImageID, + RegistryEnabled: &boolFalse, + DockerStorageDriver: "devicemapper", + COE: "swarm", + FlavorID: choices.FlavorID, + MasterLBEnabled: &boolFalse, + DNSNameServer: "8.8.8.8", + FloatingIPEnabled: &boolFalse, + } + + res := clustertemplates.Create(client, createOpts) + if res.Err != nil { + return nil, res.Err + } + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, true, requestID != "") + + t.Logf("Cluster Template %s request ID: %s", name, requestID) + + clusterTemplate, err := res.Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created cluster template: %s", clusterTemplate.Name) + + tools.PrintResource(t, clusterTemplate) + tools.PrintResource(t, clusterTemplate.CreatedAt) + + th.AssertEquals(t, name, clusterTemplate.Name) + th.AssertEquals(t, choices.ExternalNetworkID, clusterTemplate.ExternalNetworkID) + th.AssertEquals(t, choices.MagnumImageID, clusterTemplate.ImageID) + + return clusterTemplate, nil +} + +/* +// DeleteClusterTemplate will delete a given cluster-template. A fatal error will occur if the +// cluster-template could not be deleted. This works best as a deferred function. +func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete cluster-template: %s", id) + + err := clustertemplates.Delete(client, id).ExtractErr() + if err != nil { + t.Fatalf("Error deleting cluster-template %s: %s:", id, err) + } + + t.Logf("Successfully deleted cluster-template: %s", id) + + return +} +*/ diff --git a/acceptance/openstack/containerinfra/v1/pkg.go b/acceptance/openstack/containerinfra/v1/pkg.go new file mode 100644 index 0000000000..b7b1f993d5 --- /dev/null +++ b/acceptance/openstack/containerinfra/v1/pkg.go @@ -0,0 +1 @@ +package v1 diff --git a/openstack/containerinfra/v1/clustertemplates/doc.go b/openstack/containerinfra/v1/clustertemplates/doc.go new file mode 100644 index 0000000000..b07b6e64df --- /dev/null +++ b/openstack/containerinfra/v1/clustertemplates/doc.go @@ -0,0 +1,42 @@ +// Package clustertemplates contains functionality for working with Magnum Cluster Templates +// resources. +/* +Package clustertemplates provides information and interaction with the cluster-templates through +the OpenStack Container Infra service. + +Example to Create Cluster Template + + boolFalse := false + boolTrue := true + createOpts := clustertemplates.CreateOpts{ + Name: "test-cluster-template", + Labels: map[string]string{}, + FixedSubnet: "", + MasterFlavorID: "", + NoProxy: "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", + HTTPSProxy: "http://10.164.177.169:8080", + TLSDisabled: &boolFalse, + KeyPairID: "kp", + Public: &boolFalse, + HTTPProxy: "http://10.164.177.169:8080", + ServerType: "vm", + ExternalNetworkID: "public", + ImageID: "fedora-atomic-latest", + VolumeDriver: "cinder", + RegistryEnabled: &boolFalse, + DockerStorageDriver: "devicemapper", + NetworkDriver: "flannel", + FixedNetwork: "", + COE: "kubernetes", + FlavorID: "m1.small", + MasterLBEnabled: &boolTrue, + DNSNameServer: "8.8.8.8", + } + + clustertemplate, err := clustertemplates.Create(serviceClient, createOpts).Extract() + if err != nil { + panic(err) + } + +*/ +package clustertemplates diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go new file mode 100644 index 0000000000..59628f65d8 --- /dev/null +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -0,0 +1,66 @@ +package clustertemplates + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder Builder. +type CreateOptsBuilder interface { + ToClusterCreateMap() (map[string]interface{}, error) +} + +// CreateOpts params +type CreateOpts struct { + APIServerPort *int `json:"apiserver_port,omitempty"` + COE string `json:"coe" required:"true"` + DNSNameServer string `json:"dns_nameserver,omitempty"` + DockerStorageDriver string `json:"docker_storage_driver,omitempty"` + DockerVolumeSize *int `json:"docker_volume_size,omitempty"` + ExternalNetworkID string `json:"external_network_id,omitempty"` + FixedNetwork string `json:"fixed_network,omitempty"` + FixedSubnet string `json:"fixed_subnet,omitempty"` + FlavorID string `json:"flavor_id,omitempty"` + FloatingIPEnabled *bool `json:"floating_ip_enabled,omitempty"` + HTTPProxy string `json:"http_proxy,omitempty"` + HTTPSProxy string `json:"https_proxy,omitempty"` + ImageID string `json:"image_id" required:"true"` + InsecureRegistry string `json:"insecure_registry,omitempty"` + KeyPairID string `json:"keypair_id,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + MasterFlavorID string `json:"master_flavor_id,omitempty"` + MasterLBEnabled *bool `json:"master_lb_enabled,omitempty"` + Name string `json:"name,omitempty"` + NetworkDriver string `json:"network_driver,omitempty"` + NoProxy string `json:"no_proxy,omitempty"` + Public *bool `json:"public,omitempty"` + RegistryEnabled *bool `json:"registry_enabled,omitempty"` + ServerType string `json:"server_type,omitempty"` + TLSDisabled *bool `json:"tls_disabled,omitempty"` + VolumeDriver string `json:"volume_driver,omitempty"` +} + +// ToClusterCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create requests the creation of a new cluster. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToClusterCreateMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go new file mode 100644 index 0000000000..e4978ff2a1 --- /dev/null +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -0,0 +1,60 @@ +package clustertemplates + +import ( + "time" + + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Create operations. +type CreateResult struct { + commonResult +} + +// Extract is a function that accepts a result and extracts a cluster-template resource. +func (r commonResult) Extract() (*ClusterTemplate, error) { + var s *ClusterTemplate + err := r.ExtractInto(&s) + return s, err +} + +// Represents a template for a Cluster Template +type ClusterTemplate struct { + APIServerPort string `json:"apiserver_port"` + COE string `json:"coe"` + ClusterDistro string `json:"cluster_distro"` + CreatedAt time.Time `json:"created_at"` + DNSNameServer string `json:"dns_nameserver"` + DockerStorageDriver string `json:"docker_storage_driver"` + DockerVolumeSize int `json:"docker_volume_size"` + ExternalNetworkID string `json:"external_network_id"` + FixedNetwork string `json:"fixed_network"` + FixedSubnet string `json:"fixed_subnet"` + FlavorID string `json:"flavor_id"` + FloatingIPEnabled bool `json:"floating_ip_enabled"` + HTTPProxy string `json:"http_proxy"` + HTTPSProxy string `json:"https_proxy"` + ImageID string `json:"image_id"` + InsecureRegistry string `json:"insecure_registry"` + KeyPairID string `json:"keypair_id"` + Labels map[string]string `json:"labels"` + Links []gophercloud.Link `json:"links"` + MasterFlavorID string `json:"master_flavor_id"` + MasterLBEnabled bool `json:"master_lb_enabled"` + Name string `json:"name"` + NetworkDriver string `json:"network_driver"` + NoProxy string `json:"no_proxy"` + ProjectID string `json:"project_id"` + Public bool `json:"public"` + RegistryEnabled bool `json:"registry_enabled"` + ServerType string `json:"server_type"` + TLSDisabled bool `json:"tls_disabled"` + UUID string `json:"uuid"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` + VolumeDriver string `json:"volume_driver"` +} diff --git a/openstack/containerinfra/v1/clustertemplates/testing/doc.go b/openstack/containerinfra/v1/clustertemplates/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/containerinfra/v1/clustertemplates/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go new file mode 100644 index 0000000000..84893f203f --- /dev/null +++ b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go @@ -0,0 +1,114 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ClusterTemplateResponse = ` +{ + "insecure_registry": null, + "links": [ + { + "href": "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", + "rel": "self" + }, + { + "href": "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", + "rel": "bookmark" + } + ], + "http_proxy": "http://10.164.177.169:8080", + "updated_at": null, + "floating_ip_enabled": true, + "fixed_subnet": null, + "master_flavor_id": null, + "user_id": "c48d66144e9c4a54ae2b164b85cfefe3", + "uuid": "79c0f9e5-93b8-4719-8fab-063afc67bffe", + "no_proxy": "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", + "https_proxy": "http://10.164.177.169:8080", + "tls_disabled": false, + "keypair_id": "kp", + "project_id": "76bd201dbc1641729904ab190d3390c6", + "public": false, + "labels": null, + "docker_volume_size": 3, + "server_type": "vm", + "external_network_id": "public", + "cluster_distro": "fedora-atomic", + "image_id": "Fedora-Atomic-27-20180212.2.x86_64", + "volume_driver": "cinder", + "registry_enabled": false, + "docker_storage_driver": "devicemapper", + "apiserver_port": null, + "name": "kubernetes-dev", + "created_at": "2018-06-27T16:52:21+00:00", + "network_driver": "flannel", + "fixed_network": null, + "coe": "kubernetes", + "flavor_id": "m1.small", + "master_lb_enabled": true, + "dns_nameserver": "8.8.8.8" +}` + +var ExpectedClusterTemplate = clustertemplates.ClusterTemplate{ + InsecureRegistry: "", + Links: []gophercloud.Link{ + {Href: "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", Rel: "self"}, + {Href: "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", Rel: "bookmark"}, + }, + HTTPProxy: "http://10.164.177.169:8080", + UpdatedAt: time.Time{}, + FloatingIPEnabled: true, + FixedSubnet: "", + MasterFlavorID: "", + UserID: "c48d66144e9c4a54ae2b164b85cfefe3", + UUID: "79c0f9e5-93b8-4719-8fab-063afc67bffe", + NoProxy: "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", + HTTPSProxy: "http://10.164.177.169:8080", + TLSDisabled: false, + KeyPairID: "kp", + ProjectID: "76bd201dbc1641729904ab190d3390c6", + Public: false, + Labels: map[string]string(nil), + DockerVolumeSize: 3, + ServerType: "vm", + ExternalNetworkID: "public", + ClusterDistro: "fedora-atomic", + ImageID: "Fedora-Atomic-27-20180212.2.x86_64", + VolumeDriver: "cinder", + RegistryEnabled: false, + DockerStorageDriver: "devicemapper", + APIServerPort: "", + Name: "kubernetes-dev", + CreatedAt: time.Date(2018, 6, 27, 16, 52, 21, 0, time.UTC), + NetworkDriver: "flannel", + FixedNetwork: "", + COE: "kubernetes", + FlavorID: "m1.small", + MasterLBEnabled: true, + DNSNameServer: "8.8.8.8", +} + +func HandleCreateClusterTemplateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clustertemplates", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("OpenStack-API-Minimum-Version", "container-infra 1.1") + w.Header().Add("OpenStack-API-Maximum-Version", "container-infra 1.6") + w.Header().Add("OpenStack-API-Version", "container-infra 1.1") + w.Header().Add("X-OpenStack-Request-Id", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.WriteHeader(http.StatusCreated) + + fmt.Fprint(w, ClusterTemplateResponse) + }) +} diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go new file mode 100644 index 0000000000..22572574d4 --- /dev/null +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -0,0 +1,59 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateClusterTemplate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCreateClusterTemplateSuccessfully(t) + + boolFalse := false + boolTrue := true + dockerVolumeSize := 3 + opts := clustertemplates.CreateOpts{ + Name: "kubernetes-dev", + Labels: map[string]string{}, + FixedSubnet: "", + MasterFlavorID: "", + NoProxy: "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", + HTTPSProxy: "http://10.164.177.169:8080", + TLSDisabled: &boolFalse, + KeyPairID: "kp", + Public: &boolFalse, + HTTPProxy: "http://10.164.177.169:8080", + DockerVolumeSize: &dockerVolumeSize, + ServerType: "vm", + ExternalNetworkID: "public", + ImageID: "Fedora-Atomic-27-20180212.2.x86_64", + VolumeDriver: "cinder", + RegistryEnabled: &boolFalse, + DockerStorageDriver: "devicemapper", + NetworkDriver: "flannel", + FixedNetwork: "", + COE: "kubernetes", + FlavorID: "m1.small", + MasterLBEnabled: &boolTrue, + DNSNameServer: "8.8.8.8", + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + res := clustertemplates.Create(sc, opts) + th.AssertNoErr(t, res.Err) + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, "req-781e9bdc-4163-46eb-91c9-786c53188bbb", requestID) + + actual, err := res.Extract() + th.AssertNoErr(t, err) + + actual.CreatedAt = actual.CreatedAt.UTC() + th.AssertDeepEquals(t, ExpectedClusterTemplate, *actual) +} diff --git a/openstack/containerinfra/v1/clustertemplates/urls.go b/openstack/containerinfra/v1/clustertemplates/urls.go new file mode 100644 index 0000000000..8aee327b09 --- /dev/null +++ b/openstack/containerinfra/v1/clustertemplates/urls.go @@ -0,0 +1,15 @@ +package clustertemplates + +import ( + "github.com/gophercloud/gophercloud" +) + +var apiName = "clustertemplates" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiName) +} + +func createURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From ef73824443002e03808dec29aa2ab10d5009bb2d Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 26 Jul 2018 17:23:01 +0300 Subject: [PATCH 0489/2296] Compute V2: SortDir and SortKey for flavors Add SortKey and SortDir fields for the compute/v2/flavors package. --- openstack/compute/v2/flavors/requests.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 4b406df957..539019e90d 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -52,6 +52,14 @@ type ListOpts struct { MinDisk int `q:"minDisk"` MinRAM int `q:"minRam"` + // SortDir allows to select sort direction. + // It can be "asc" or "desc" (default). + SortDir string `q:"sort_dir"` + + // SortKey allows to sort by one of the flavors attributes. + // Default is flavorid. + SortKey string `q:"sort_key"` + // Marker and Limit control paging. // Marker instructs List where to start listing from. Marker string `q:"marker"` From e3c477b35092bb32ec5848f840e1fc1e1fb27359 Mon Sep 17 00:00:00 2001 From: Sergey Date: Fri, 27 Jul 2018 03:35:45 +0300 Subject: [PATCH 0490/2296] Fix ToServerRebootMap() method (#1152) * Fix method * Fix tests --- acceptance/openstack/compute/v2/servers_test.go | 2 +- docs/MIGRATING.md | 2 +- openstack/compute/v2/servers/requests.go | 2 +- openstack/compute/v2/servers/testing/requests_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index eeca3ab801..92652c47bb 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -271,7 +271,7 @@ func TestServersActionReboot(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - rebootOpts := &servers.RebootOpts{ + rebootOpts := servers.RebootOpts{ Type: servers.SoftReboot, } diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index aa383c9cc9..ef1a2bb900 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -16,7 +16,7 @@ * `servers.Reboot` now requires a `servers.RebootOpts` struct: ```golang - rebootOpts := &servers.RebootOpts{ + rebootOpts := servers.RebootOpts{ Type: servers.SoftReboot, } res := servers.Reboot(client, server.ID, rebootOpts) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 7d4f80f16c..cf7e94d027 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -383,7 +383,7 @@ type RebootOpts struct { } // ToServerRebootMap builds a body for the reboot request. -func (opts *RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { +func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "reboot") } diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 6bf33b5e3d..b11e25c382 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -311,7 +311,7 @@ func TestRebootServer(t *testing.T) { defer th.TeardownHTTP() HandleRebootSuccessfully(t) - res := servers.Reboot(client.ServiceClient(), "1234asdf", &servers.RebootOpts{ + res := servers.Reboot(client.ServiceClient(), "1234asdf", servers.RebootOpts{ Type: servers.SoftReboot, }) th.AssertNoErr(t, res.Err) From 0b1388b3a0bed0b601e81294e978cc8656223a5a Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 27 Jul 2018 03:42:17 +0300 Subject: [PATCH 0491/2296] Imageservice V2: implement tasks Create method (#1148) * Imageservice V2: implement tasks List method Add the new "tasks" package with basic structures and List method. Add unit and acceptance test with documentation. * Imageservice V2: implement tasks Create method Implement Create method for the Imageservice tasks. Add unit and acceptance tests with documentation. * Imageservice tasks: use string type for status Use the standard string type for the task status. Add inline documentation for the pre-defined TaskStatus type and its well-known values. * Imageservice tasks: add Create doc example Add an example of how do we create a Task. --- .../openstack/imageservice/v2/imageservice.go | 32 ++++++++++++ .../openstack/imageservice/v2/tasks_test.go | 12 +++++ openstack/imageservice/v2/tasks/doc.go | 21 ++++++++ openstack/imageservice/v2/tasks/requests.go | 33 +++++++++++++ openstack/imageservice/v2/tasks/results.go | 6 +++ .../imageservice/v2/tasks/testing/fixtures.go | 39 +++++++++++++++ .../v2/tasks/testing/requests_test.go | 49 +++++++++++++++++++ openstack/imageservice/v2/tasks/urls.go | 4 ++ 8 files changed, 196 insertions(+) diff --git a/acceptance/openstack/imageservice/v2/imageservice.go b/acceptance/openstack/imageservice/v2/imageservice.go index 54035b0fcc..f61500e67b 100644 --- a/acceptance/openstack/imageservice/v2/imageservice.go +++ b/acceptance/openstack/imageservice/v2/imageservice.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" + "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -63,3 +64,34 @@ func DeleteImage(t *testing.T, client *gophercloud.ServiceClient, image *images. t.Logf("Deleted image: %s", image.ID) } + +// ImportImageURL contains an URL of a test image that can be imported. +const ImportImageURL = "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img" + +// CreateTask will create a task to import the CirrOS image. +// An error will be returned if a task couldn't be created. +func CreateTask(t *testing.T, client *gophercloud.ServiceClient, imageURL string) (*tasks.Task, error) { + t.Logf("Attempting to create an Imageservice import task with image: %s", imageURL) + opts := 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": imageURL, + }, + } + task, err := tasks.Create(client, opts).Extract() + if err != nil { + return nil, err + } + + newTask, err := tasks.Get(client, task.ID).Extract() + if err != nil { + return nil, err + } + + return newTask, nil +} diff --git a/acceptance/openstack/imageservice/v2/tasks_test.go b/acceptance/openstack/imageservice/v2/tasks_test.go index a8020f06d1..1e0fbf3f2d 100644 --- a/acceptance/openstack/imageservice/v2/tasks_test.go +++ b/acceptance/openstack/imageservice/v2/tasks_test.go @@ -49,3 +49,15 @@ func TestTasksListAllPages(t *testing.T) { tools.PrintResource(t, i) } } + +func TestTaskCreate(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + th.AssertNoErr(t, err) + + task, err := CreateTask(t, client, ImportImageURL) + if err != nil { + t.Fatalf("Unable to create an Imageservice task: %v", err) + } + + tools.PrintResource(t, task) +} diff --git a/openstack/imageservice/v2/tasks/doc.go b/openstack/imageservice/v2/tasks/doc.go index 64daa4717a..28ed82e55c 100644 --- a/openstack/imageservice/v2/tasks/doc.go +++ b/openstack/imageservice/v2/tasks/doc.go @@ -29,6 +29,27 @@ Example to Get a Task panic(err) } + 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) */ package tasks diff --git a/openstack/imageservice/v2/tasks/requests.go b/openstack/imageservice/v2/tasks/requests.go index 18b8327414..94fe45d9c5 100644 --- a/openstack/imageservice/v2/tasks/requests.go +++ b/openstack/imageservice/v2/tasks/requests.go @@ -93,3 +93,36 @@ func Get(c *gophercloud.ServiceClient, taskID string) (r GetResult) { _, r.Err = c.Get(getURL(c, taskID), &r.Body, nil) return } + +// CreateOptsBuilder allows to add additional parameters to the Create request. +type CreateOptsBuilder interface { + ToTaskCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters of a new Imageservice task. +type CreateOpts struct { + Type string `json:"type" required:"true"` + Input map[string]interface{} `json:"input"` +} + +// ToTaskCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToTaskCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +// Create requests the creation of a new Imageservice task on the server. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTaskCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/imageservice/v2/tasks/results.go b/openstack/imageservice/v2/tasks/results.go index 38928362af..3a7f5ca12b 100644 --- a/openstack/imageservice/v2/tasks/results.go +++ b/openstack/imageservice/v2/tasks/results.go @@ -17,6 +17,12 @@ type GetResult struct { commonResult } +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret it as a Task. +type CreateResult struct { + commonResult +} + // Task represents a single task of the OpenStack Image service. type Task struct { // ID is a unique identifier of the task. diff --git a/openstack/imageservice/v2/tasks/testing/fixtures.go b/openstack/imageservice/v2/tasks/testing/fixtures.go index dadf6c2ebe..6a1a9d3e93 100644 --- a/openstack/imageservice/v2/tasks/testing/fixtures.go +++ b/openstack/imageservice/v2/tasks/testing/fixtures.go @@ -83,3 +83,42 @@ const TasksGetResult = ` "schema": "/v2/schemas/task" } ` + +// TaskCreateRequest represents a request to create a task. +const TaskCreateRequest = ` +{ + "input": { + "image_properties": { + "container_format": "bare", + "disk_format": "raw" + }, + "import_from_format": "raw", + "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img" + }, + "type": "import" +} +` + +// TaskCreateResult represents a raw server response to the TaskCreateRequest. +const TaskCreateResult = ` +{ + "status": "pending", + "created_at": "2018-07-25T11:07:54Z", + "updated_at": "2018-07-25T11:07:54Z", + "self": "/v2/tasks/d550c87d-86ed-430a-9895-c7a1f5ce87e9", + "result": null, + "owner": "fb57277ef2f84a0e85b9018ec2dedbf7", + "input": { + "image_properties": { + "container_format": "bare", + "disk_format": "raw" + }, + "import_from_format": "raw", + "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img" + }, + "message": "", + "type": "import", + "id": "d550c87d-86ed-430a-9895-c7a1f5ce87e9", + "schema": "/v2/schemas/task" +} +` diff --git a/openstack/imageservice/v2/tasks/testing/requests_test.go b/openstack/imageservice/v2/tasks/testing/requests_test.go index b941a78d7d..bc9af17aca 100644 --- a/openstack/imageservice/v2/tasks/testing/requests_test.go +++ b/openstack/imageservice/v2/tasks/testing/requests_test.go @@ -87,3 +87,52 @@ func TestGet(t *testing.T) { }, }) } + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + th.TestJSONRequest(t, r, TaskCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, TaskCreateResult) + }) + + opts := 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", + }, + } + s, err := tasks.Create(fakeclient.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, string(tasks.TaskStatusPending)) + th.AssertEquals(t, s.CreatedAt, time.Date(2018, 7, 25, 11, 7, 54, 0, time.UTC)) + th.AssertEquals(t, s.UpdatedAt, time.Date(2018, 7, 25, 11, 7, 54, 0, time.UTC)) + th.AssertEquals(t, s.Self, "/v2/tasks/d550c87d-86ed-430a-9895-c7a1f5ce87e9") + th.AssertEquals(t, s.Owner, "fb57277ef2f84a0e85b9018ec2dedbf7") + th.AssertEquals(t, s.Message, "") + th.AssertEquals(t, s.Type, "import") + th.AssertEquals(t, s.ID, "d550c87d-86ed-430a-9895-c7a1f5ce87e9") + th.AssertEquals(t, s.Schema, "/v2/schemas/task") + th.AssertDeepEquals(t, s.Result, map[string]interface{}(nil)) + th.AssertDeepEquals(t, s.Input, map[string]interface{}{ + "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img", + "import_from_format": "raw", + "image_properties": map[string]interface{}{ + "container_format": "bare", + "disk_format": "raw", + }, + }) +} diff --git a/openstack/imageservice/v2/tasks/urls.go b/openstack/imageservice/v2/tasks/urls.go index abed8b8a28..8133f38356 100644 --- a/openstack/imageservice/v2/tasks/urls.go +++ b/openstack/imageservice/v2/tasks/urls.go @@ -26,6 +26,10 @@ func getURL(c *gophercloud.ServiceClient, taskID string) string { return resourceURL(c, taskID) } +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + func nextPageURL(serviceURL, requestedNext string) (string, error) { base, err := utils.BaseEndpoint(serviceURL) if err != nil { From f8826f28e31a2f66e771939532bcae910ed2420f Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 27 Jul 2018 18:06:35 +0300 Subject: [PATCH 0492/2296] Networking V2: Implement IP availabilities Get (#1154) * Networking V2: Implement IP availabilities List Add the new Networking service extensions "networkipavailabilities" package with the List method. Add unit and acceptance tests with documentation. * Net availabilities: add IPVersion to ListOpts Allow to filter network IP availabilities on the IP protocol version. * Networking V2: Implement IP availabilities Get Add Get method in the Networking service extensions "networkipavailabilities" package. Add unit test with documentation. --- .../networkipavailabilities_test.go | 32 +++++ .../extensions/networkipavailabilities/pkg.go | 1 + .../extensions/networkipavailabilities/doc.go | 30 ++++ .../networkipavailabilities/requests.go | 61 ++++++++ .../networkipavailabilities/results.go | 96 +++++++++++++ .../networkipavailabilities/testing/doc.go | 2 + .../testing/fixtures.go | 130 ++++++++++++++++++ .../testing/requests_test.go | 87 ++++++++++++ .../networkipavailabilities/urls.go | 21 +++ 9 files changed, 460 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go create mode 100644 acceptance/openstack/networking/v2/extensions/networkipavailabilities/pkg.go create mode 100644 openstack/networking/v2/extensions/networkipavailabilities/doc.go create mode 100644 openstack/networking/v2/extensions/networkipavailabilities/requests.go create mode 100644 openstack/networking/v2/extensions/networkipavailabilities/results.go create mode 100644 openstack/networking/v2/extensions/networkipavailabilities/testing/doc.go create mode 100644 openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/networkipavailabilities/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go new file mode 100644 index 0000000000..f141d204a8 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking networkipavailabilities + +package networkipavailabilities + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" +) + +func TestNetworkIPAvailabilityList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := networkipavailabilities.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list network IP availabilities: %v", err) + } + + allAvailabilities, err := networkipavailabilities.ExtractNetworkIPAvailabilities(allPages) + if err != nil { + t.Fatalf("Unable to extract network IP availabilities: %v", err) + } + + for _, availability := range allAvailabilities { + tools.PrintResource(t, availability) + } +} diff --git a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/pkg.go b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/pkg.go new file mode 100644 index 0000000000..7399a405be --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/pkg.go @@ -0,0 +1 @@ +package networkipavailabilities diff --git a/openstack/networking/v2/extensions/networkipavailabilities/doc.go b/openstack/networking/v2/extensions/networkipavailabilities/doc.go new file mode 100644 index 0000000000..9109473693 --- /dev/null +++ b/openstack/networking/v2/extensions/networkipavailabilities/doc.go @@ -0,0 +1,30 @@ +/* +Package networkipavailabilities provides the ability to retrieve and manage +networkipavailabilities through the Neutron API. + +Example of Listing NetworkIPAvailabilities + + allPages, err := networkipavailabilities.List(networkClient, networkipavailabilities.ListOpts{}).AllPages() + if err != nil { + panic(err) + } + + allAvailabilities, err := subnetpools.ExtractSubnetPools(allPages) + if err != nil { + panic(err) + } + + 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) + } + + fmt.Printf("%+v\n", availability) +*/ +package networkipavailabilities diff --git a/openstack/networking/v2/extensions/networkipavailabilities/requests.go b/openstack/networking/v2/extensions/networkipavailabilities/requests.go new file mode 100644 index 0000000000..c024d7a7b8 --- /dev/null +++ b/openstack/networking/v2/extensions/networkipavailabilities/requests.go @@ -0,0 +1,61 @@ +package networkipavailabilities + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToNetworkIPAvailabilityListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the Neutron API. +type ListOpts struct { + // NetworkName allows to filter on the identifier of a network. + NetworkID string `q:"network_id"` + + // NetworkName allows to filter on the name of a network. + NetworkName string `q:"network_name"` + + // IPVersion allows to filter on the version of the IP protocol. + // You can use the well-known IP versions with the gophercloud.IPVersion type. + IPVersion string `q:"ip_version"` + + // ProjectID allows to filter on the Identity project field. + ProjectID string `q:"project_id"` + + // TenantID allows to filter on the Identity project field. + TenantID string `q:"tenant_id"` +} + +// ToNetworkIPAvailabilityListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToNetworkIPAvailabilityListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// networkipavailabilities. It accepts a ListOpts struct, which allows you to +// filter the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToNetworkIPAvailabilityListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return NetworkIPAvailabilityPage{pagination.SinglePageBase(r)} + }) +} + +// Get retrieves a specific NetworkIPAvailability based on its ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/networkipavailabilities/results.go b/openstack/networking/v2/extensions/networkipavailabilities/results.go new file mode 100644 index 0000000000..4bcf10f49d --- /dev/null +++ b/openstack/networking/v2/extensions/networkipavailabilities/results.go @@ -0,0 +1,96 @@ +package networkipavailabilities + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret it as a NetworkIPAvailability. +type GetResult struct { + commonResult +} + +// Extract is a function that accepts a result and extracts a NetworkIPAvailability. +func (r commonResult) Extract() (*NetworkIPAvailability, error) { + var s struct { + NetworkIPAvailability *NetworkIPAvailability `json:"network_ip_availability"` + } + err := r.ExtractInto(&s) + return s.NetworkIPAvailability, err +} + +// NetworkIPAvailability represents availability details for a single network. +type NetworkIPAvailability struct { + // NetworkID contains an unique identifier of the network. + NetworkID string `json:"network_id"` + + // NetworkName represents human-readable name of the network. + NetworkName string `json:"network_name"` + + // ProjectID is the ID of the Identity project. + ProjectID string `json:"project_id"` + + // TenantID is the ID of the Identity project. + TenantID string `json:"tenant_id"` + + // SubnetIPAvailabilities contains availability details for every subnet + // that is associated to the network. + SubnetIPAvailabilities []SubnetIPAvailability `json:"subnet_ip_availability"` + + // TotalIPs represents a number of IP addresses in the network. + TotalIPs int `json:"total_ips"` + + // UsedIPs represents a number of used IP addresses in the network. + UsedIPs int `json:"used_ips"` +} + +// SubnetIPAvailability represents availability details for a single subnet. +type SubnetIPAvailability struct { + // SubnetID contains an unique identifier of the subnet. + SubnetID string `json:"subnet_id"` + + // SubnetName represents human-readable name of the subnet. + SubnetName string `json:"subnet_name"` + + // CIDR represents prefix in the CIDR format. + CIDR string `json:"cidr"` + + // IPVersion is the IP protocol version. + IPVersion int `json:"ip_version"` + + // TotalIPs represents a number of IP addresses in the subnet. + TotalIPs int `json:"total_ips"` + + // UsedIPs represents a number of used IP addresses in the subnet. + UsedIPs int `json:"used_ips"` +} + +// NetworkIPAvailabilityPage stores a single page of NetworkIPAvailabilities +// from the List call. +type NetworkIPAvailabilityPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a NetworkIPAvailability is empty. +func (r NetworkIPAvailabilityPage) IsEmpty() (bool, error) { + networkipavailabilities, err := ExtractNetworkIPAvailabilities(r) + return len(networkipavailabilities) == 0, err +} + +// ExtractNetworkIPAvailabilities interprets the results of a single page from +// a List() API call, producing a slice of NetworkIPAvailabilities structures. +func ExtractNetworkIPAvailabilities(r pagination.Page) ([]NetworkIPAvailability, error) { + var s struct { + NetworkIPAvailabilities []NetworkIPAvailability `json:"network_ip_availabilities"` + } + err := (r.(NetworkIPAvailabilityPage)).ExtractInto(&s) + if err != nil { + return nil, err + } + return s.NetworkIPAvailabilities, nil +} diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/doc.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/doc.go new file mode 100644 index 0000000000..baf115fc0d --- /dev/null +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/doc.go @@ -0,0 +1,2 @@ +// networkipavailabilities unit tests +package testing diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go new file mode 100644 index 0000000000..2cd993d6bd --- /dev/null +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go @@ -0,0 +1,130 @@ +package testing + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" +) + +// NetworkIPAvailabilityListResult represents raw server response from a server to a list call. +const NetworkIPAvailabilityListResult = ` +{ + "network_ip_availabilities": [ + { + "network_id": "080ee064-036d-405a-a307-3bde4a213a1b", + "network_name": "private", + "project_id": "fb57277ef2f84a0e85b9018ec2dedbf7", + "subnet_ip_availability": [ + { + "cidr": "10.0.0.64/26", + "ip_version": 4, + "subnet_id": "497ac4d3-0b92-42cf-82de-71302ab2b656", + "subnet_name": "second-private-subnet", + "total_ips": 61, + "used_ips": 12 + }, + { + "cidr": "10.0.0.0/26", + "ip_version": 4, + "subnet_id": "521f47e7-c4fb-452c-b71a-851da38cc571", + "subnet_name": "private-subnet", + "total_ips": 61, + "used_ips": 2 + } + ], + "tenant_id": "fb57277ef2f84a0e85b9018ec2dedbf7", + "total_ips": 122, + "used_ips": 14 + }, + { + "network_id": "cf11ab78-2302-49fa-870f-851a08c7afb8", + "network_name": "public", + "project_id": "424e7cf0243c468ca61732ba45973b3e", + "subnet_ip_availability": [ + { + "cidr": "203.0.113.0/24", + "ip_version": 4, + "subnet_id": "4afe6e5f-9649-40db-b18f-64c7ead942bd", + "subnet_name": "public-subnet", + "total_ips": 253, + "used_ips": 3 + } + ], + "tenant_id": "424e7cf0243c468ca61732ba45973b3e", + "total_ips": 253, + "used_ips": 3 + } + ] +} +` + +// NetworkIPAvailability1 is an expected representation of a first object from the ResourceListResult. +var NetworkIPAvailability1 = networkipavailabilities.NetworkIPAvailability{ + NetworkID: "080ee064-036d-405a-a307-3bde4a213a1b", + NetworkName: "private", + ProjectID: "fb57277ef2f84a0e85b9018ec2dedbf7", + TenantID: "fb57277ef2f84a0e85b9018ec2dedbf7", + TotalIPs: 122, + UsedIPs: 14, + SubnetIPAvailabilities: []networkipavailabilities.SubnetIPAvailability{ + { + SubnetID: "497ac4d3-0b92-42cf-82de-71302ab2b656", + SubnetName: "second-private-subnet", + CIDR: "10.0.0.64/26", + IPVersion: int(gophercloud.IPv4), + TotalIPs: 61, + UsedIPs: 12, + }, + { + SubnetID: "521f47e7-c4fb-452c-b71a-851da38cc571", + SubnetName: "private-subnet", + CIDR: "10.0.0.0/26", + IPVersion: int(gophercloud.IPv4), + TotalIPs: 61, + UsedIPs: 2, + }, + }, +} + +// NetworkIPAvailability2 is an expected representation of a first object from the ResourceListResult. +var NetworkIPAvailability2 = networkipavailabilities.NetworkIPAvailability{ + NetworkID: "cf11ab78-2302-49fa-870f-851a08c7afb8", + NetworkName: "public", + ProjectID: "424e7cf0243c468ca61732ba45973b3e", + TenantID: "424e7cf0243c468ca61732ba45973b3e", + TotalIPs: 253, + UsedIPs: 3, + SubnetIPAvailabilities: []networkipavailabilities.SubnetIPAvailability{ + { + SubnetID: "4afe6e5f-9649-40db-b18f-64c7ead942bd", + SubnetName: "public-subnet", + CIDR: "203.0.113.0/24", + IPVersion: int(gophercloud.IPv4), + TotalIPs: 253, + UsedIPs: 3, + }, + }, +} + +// NetworkIPAvailabilityGetResult represents raw server response from a server to a get call. +const NetworkIPAvailabilityGetResult = ` +{ + "network_ip_availability": { + "network_id": "cf11ab78-2302-49fa-870f-851a08c7afb8", + "network_name": "public", + "project_id": "424e7cf0243c468ca61732ba45973b3e", + "subnet_ip_availability": [ + { + "cidr": "203.0.113.0/24", + "ip_version": 4, + "subnet_id": "4afe6e5f-9649-40db-b18f-64c7ead942bd", + "subnet_name": "public-subnet", + "total_ips": 253, + "used_ips": 3 + } + ], + "tenant_id": "424e7cf0243c468ca61732ba45973b3e", + "total_ips": 253, + "used_ips": 3 + } +} +` diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go new file mode 100644 index 0000000000..820debb2fb --- /dev/null +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go @@ -0,0 +1,87 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud" + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" + "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/network-ip-availabilities", 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, NetworkIPAvailabilityListResult) + }) + + count := 0 + + networkipavailabilities.List(fake.ServiceClient(), networkipavailabilities.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := networkipavailabilities.ExtractNetworkIPAvailabilities(page) + if err != nil { + t.Errorf("Failed to extract network IP availabilities: %v", err) + return false, nil + } + + expected := []networkipavailabilities.NetworkIPAvailability{ + NetworkIPAvailability1, + NetworkIPAvailability2, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/network-ip-availabilities/cf11ab78-2302-49fa-870f-851a08c7afb8", 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, NetworkIPAvailabilityGetResult) + }) + + s, err := networkipavailabilities.Get(fake.ServiceClient(), "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.NetworkID, "cf11ab78-2302-49fa-870f-851a08c7afb8") + th.AssertEquals(t, s.NetworkName, "public") + th.AssertEquals(t, s.ProjectID, "424e7cf0243c468ca61732ba45973b3e") + th.AssertEquals(t, s.TenantID, "424e7cf0243c468ca61732ba45973b3e") + th.AssertEquals(t, s.TotalIPs, 253) + th.AssertEquals(t, s.UsedIPs, 3) + th.AssertDeepEquals(t, s.SubnetIPAvailabilities, []networkipavailabilities.SubnetIPAvailability{ + { + SubnetID: "4afe6e5f-9649-40db-b18f-64c7ead942bd", + SubnetName: "public-subnet", + CIDR: "203.0.113.0/24", + IPVersion: int(gophercloud.IPv4), + TotalIPs: 253, + UsedIPs: 3, + }, + }) +} diff --git a/openstack/networking/v2/extensions/networkipavailabilities/urls.go b/openstack/networking/v2/extensions/networkipavailabilities/urls.go new file mode 100644 index 0000000000..5e3228ff3a --- /dev/null +++ b/openstack/networking/v2/extensions/networkipavailabilities/urls.go @@ -0,0 +1,21 @@ +package networkipavailabilities + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "network-ip-availabilities" + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, networkIPAvailabilityID string) string { + return c.ServiceURL(resourcePath, networkIPAvailabilityID) +} + +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +func getURL(c *gophercloud.ServiceClient, networkIPAvailabilityID string) string { + return resourceURL(c, networkIPAvailabilityID) +} From 17fbdef4973a169346c363da9a93c500ade5921f Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 30 Jul 2018 11:14:05 +0300 Subject: [PATCH 0493/2296] Imageservice V2: add imageimport Get request Add the new "imageimport" package and implement Get request to retrieve Import API information. Add unit and acceptance tests with documentation. --- .../imageservice/v2/imageimport_test.go | 21 ++++++++++ .../openstack/imageservice/v2/imageservice.go | 12 ++++++ openstack/imageservice/v2/imageimport/doc.go | 14 +++++++ .../imageservice/v2/imageimport/requests.go | 20 ++++++++++ .../imageservice/v2/imageimport/results.go | 32 ++++++++++++++++ .../v2/imageimport/testing/doc.go | 1 + .../v2/imageimport/testing/fixtures.go | 15 ++++++++ .../v2/imageimport/testing/requests_test.go | 38 +++++++++++++++++++ openstack/imageservice/v2/imageimport/urls.go | 12 ++++++ 9 files changed, 165 insertions(+) create mode 100644 acceptance/openstack/imageservice/v2/imageimport_test.go create mode 100644 openstack/imageservice/v2/imageimport/doc.go create mode 100644 openstack/imageservice/v2/imageimport/requests.go create mode 100644 openstack/imageservice/v2/imageimport/results.go create mode 100644 openstack/imageservice/v2/imageimport/testing/doc.go create mode 100644 openstack/imageservice/v2/imageimport/testing/fixtures.go create mode 100644 openstack/imageservice/v2/imageimport/testing/requests_test.go create mode 100644 openstack/imageservice/v2/imageimport/urls.go diff --git a/acceptance/openstack/imageservice/v2/imageimport_test.go b/acceptance/openstack/imageservice/v2/imageimport_test.go new file mode 100644 index 0000000000..6d74604676 --- /dev/null +++ b/acceptance/openstack/imageservice/v2/imageimport_test.go @@ -0,0 +1,21 @@ +// +build acceptance imageservice imageimport + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestGetImportInfo(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + th.AssertNoErr(t, err) + + importInfo, err := GetImportInfo(t, client) + th.AssertNoErr(t, err) + + tools.PrintResource(t, importInfo) +} diff --git a/acceptance/openstack/imageservice/v2/imageservice.go b/acceptance/openstack/imageservice/v2/imageservice.go index f61500e67b..12ad27e51e 100644 --- a/acceptance/openstack/imageservice/v2/imageservice.go +++ b/acceptance/openstack/imageservice/v2/imageservice.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" th "github.com/gophercloud/gophercloud/testhelper" @@ -95,3 +96,14 @@ func CreateTask(t *testing.T, client *gophercloud.ServiceClient, imageURL string return newTask, nil } + +// GetImportInfo will retrieve Import API information. +func GetImportInfo(t *testing.T, client *gophercloud.ServiceClient) (*imageimport.ImportInfo, error) { + t.Log("Attempting to get the Imageservice Import API information") + importInfo, err := imageimport.Get(client).Extract() + if err != nil { + return nil, err + } + + return importInfo, nil +} diff --git a/openstack/imageservice/v2/imageimport/doc.go b/openstack/imageservice/v2/imageimport/doc.go new file mode 100644 index 0000000000..6862f3172c --- /dev/null +++ b/openstack/imageservice/v2/imageimport/doc.go @@ -0,0 +1,14 @@ +/* +Package imageimport enables management of images import and retrieval of the +Imageservice Import API information. + +Example to Get an information about the Import API + + importInfo, err := imageimport.Get(imagesClient).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", importInfo) +*/ +package imageimport diff --git a/openstack/imageservice/v2/imageimport/requests.go b/openstack/imageservice/v2/imageimport/requests.go new file mode 100644 index 0000000000..b374b0e47c --- /dev/null +++ b/openstack/imageservice/v2/imageimport/requests.go @@ -0,0 +1,20 @@ +package imageimport + +import "github.com/gophercloud/gophercloud" + +// ImportMethod represents valid Import API method. +type ImportMethod string + +const ( + // GlanceDirectMethod represents glance-direct Import API method. + GlanceDirectMethod ImportMethod = "glance-direct" + + // WebDownloadMethod represents web-download Import API method. + WebDownloadMethod ImportMethod = "web-download" +) + +// Get retrieves Import API information data. +func Get(c *gophercloud.ServiceClient) (r GetResult) { + _, r.Err = c.Get(infoURL(c), &r.Body, nil) + return +} diff --git a/openstack/imageservice/v2/imageimport/results.go b/openstack/imageservice/v2/imageimport/results.go new file mode 100644 index 0000000000..c579c7b156 --- /dev/null +++ b/openstack/imageservice/v2/imageimport/results.go @@ -0,0 +1,32 @@ +package imageimport + +import "github.com/gophercloud/gophercloud" + +type commonResult struct { + gophercloud.Result +} + +// GetResult represents the result of a get operation. Call its Extract method +// to interpret it as ImportInfo. +type GetResult struct { + commonResult +} + +// ImportInfo represents information data for the Import API. +type ImportInfo struct { + ImportMethods ImportMethods `json:"import-methods"` +} + +// ImportMethods contains information about available Import API methods. +type ImportMethods struct { + Description string `json:"description"` + Type string `json:"type"` + Value []string `json:"value"` +} + +// Extract is a function that accepts a result and extracts ImportInfo. +func (r commonResult) Extract() (*ImportInfo, error) { + var s *ImportInfo + err := r.ExtractInto(&s) + return s, err +} diff --git a/openstack/imageservice/v2/imageimport/testing/doc.go b/openstack/imageservice/v2/imageimport/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/imageservice/v2/imageimport/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/imageservice/v2/imageimport/testing/fixtures.go b/openstack/imageservice/v2/imageimport/testing/fixtures.go new file mode 100644 index 0000000000..7cef5cff06 --- /dev/null +++ b/openstack/imageservice/v2/imageimport/testing/fixtures.go @@ -0,0 +1,15 @@ +package testing + +// ImportGetResult represents raw server response on a Get request. +const ImportGetResult = ` +{ + "import-methods": { + "description": "Import methods available.", + "type": "array", + "value": [ + "glance-direct", + "web-download" + ] + } +} +` diff --git a/openstack/imageservice/v2/imageimport/testing/requests_test.go b/openstack/imageservice/v2/imageimport/testing/requests_test.go new file mode 100644 index 0000000000..01434bd622 --- /dev/null +++ b/openstack/imageservice/v2/imageimport/testing/requests_test.go @@ -0,0 +1,38 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport" + th "github.com/gophercloud/gophercloud/testhelper" + fakeclient "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/info/import", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ImportGetResult) + }) + + validImportMethods := []string{ + string(imageimport.GlanceDirectMethod), + string(imageimport.WebDownloadMethod), + } + + s, err := imageimport.Get(fakeclient.ServiceClient()).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.ImportMethods.Description, "Import methods available.") + th.AssertEquals(t, s.ImportMethods.Type, "array") + th.AssertDeepEquals(t, s.ImportMethods.Value, validImportMethods) +} diff --git a/openstack/imageservice/v2/imageimport/urls.go b/openstack/imageservice/v2/imageimport/urls.go new file mode 100644 index 0000000000..6ec37669b3 --- /dev/null +++ b/openstack/imageservice/v2/imageimport/urls.go @@ -0,0 +1,12 @@ +package imageimport + +import "github.com/gophercloud/gophercloud" + +const ( + infoPath = "info" + resourcePath = "import" +) + +func infoURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(infoPath, resourcePath) +} From 547e908d48585abcb18c224fe942605a0ba560f1 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 30 Jul 2018 15:04:51 +0300 Subject: [PATCH 0494/2296] Imageservice V2: add Stage request to imagedata Add Stage request for the imagedata package with tests and documentation. Provide acceptance test with new helper functions to work with local image files. --- .../imageservice/v2/imagedata_test.go | 29 +++++++++++ .../openstack/imageservice/v2/imageservice.go | 50 +++++++++++++++++++ openstack/imageservice/v2/imagedata/doc.go | 15 ++++++ .../imageservice/v2/imagedata/requests.go | 11 ++++ .../imageservice/v2/imagedata/results.go | 6 +++ .../v2/imagedata/testing/fixtures.go | 17 +++++++ .../v2/imagedata/testing/requests_test.go | 14 ++++++ openstack/imageservice/v2/imagedata/urls.go | 12 ++++- 8 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/imageservice/v2/imagedata_test.go diff --git a/acceptance/openstack/imageservice/v2/imagedata_test.go b/acceptance/openstack/imageservice/v2/imagedata_test.go new file mode 100644 index 0000000000..d19c38d4a9 --- /dev/null +++ b/acceptance/openstack/imageservice/v2/imagedata_test.go @@ -0,0 +1,29 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestImageStage(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + th.AssertNoErr(t, err) + + image, err := CreateEmptyImage(t, client) + th.AssertNoErr(t, err) + defer DeleteImage(t, client, image) + + imageFileName := tools.RandomString("image_", 8) + imageFilepath := "/tmp/" + imageFileName + imageURL := ImportImageURL + + err = DownloadImageFileFromURL(t, imageURL, imageFilepath) + th.AssertNoErr(t, err) + defer DeleteImageFile(t, imageFilepath) + + err = StageImage(t, client, imageFilepath, image.ID) + th.AssertNoErr(t, err) +} diff --git a/acceptance/openstack/imageservice/v2/imageservice.go b/acceptance/openstack/imageservice/v2/imageservice.go index 12ad27e51e..e8f153d2e0 100644 --- a/acceptance/openstack/imageservice/v2/imageservice.go +++ b/acceptance/openstack/imageservice/v2/imageservice.go @@ -3,10 +3,14 @@ package v2 import ( + "io" + "net/http" + "os" "testing" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" @@ -107,3 +111,49 @@ func GetImportInfo(t *testing.T, client *gophercloud.ServiceClient) (*imageimpor return importInfo, nil } + +// StageImage will stage local image file to the referenced remote queued image. +func StageImage(t *testing.T, client *gophercloud.ServiceClient, filepath, imageID string) error { + imageData, err := os.Open(filepath) + if err != nil { + return err + } + defer imageData.Close() + + return imagedata.Stage(client, imageID, imageData).ExtractErr() +} + +// DownloadImageFileFromURL will download an image from the specified URL and +// place it into the specified path. +func DownloadImageFileFromURL(t *testing.T, url, filepath string) error { + file, err := os.Create(filepath) + if err != nil { + return err + } + defer file.Close() + + t.Logf("Attempting to download image from %s", url) + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + size, err := io.Copy(file, resp.Body) + if err != nil { + return err + } + + t.Logf("Downloaded image with size of %d bytes in %s", size, filepath) + return nil +} + +// DeleteImageFile will delete local image file. +func DeleteImageFile(t *testing.T, filepath string) { + err := os.Remove(filepath) + if err != nil { + t.Fatalf("Unable to delete image file %s", filepath) + } + + t.Logf("Successfully deleted image file %s", filepath) +} diff --git a/openstack/imageservice/v2/imagedata/doc.go b/openstack/imageservice/v2/imagedata/doc.go index a2f5e58b89..0c12bf2e07 100644 --- a/openstack/imageservice/v2/imagedata/doc.go +++ b/openstack/imageservice/v2/imagedata/doc.go @@ -16,6 +16,21 @@ Example to Upload Image Data panic(err) } +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) + } + Example to Download Image Data imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" diff --git a/openstack/imageservice/v2/imagedata/requests.go b/openstack/imageservice/v2/imagedata/requests.go index 4761e488c6..545c561f36 100644 --- a/openstack/imageservice/v2/imagedata/requests.go +++ b/openstack/imageservice/v2/imagedata/requests.go @@ -16,6 +16,17 @@ func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r Upl return } +// Stage performs PUT call on the existing image object in the Imageservice with +// the provided file. +// Existing image object must be in the "queued" status. +func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { + _, r.Err = client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, + OkCodes: []int{204}, + }) + return +} + // Download retrieves an image. func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { var resp *http.Response diff --git a/openstack/imageservice/v2/imagedata/results.go b/openstack/imageservice/v2/imagedata/results.go index 895d28ba8a..17a3f0e0f7 100644 --- a/openstack/imageservice/v2/imagedata/results.go +++ b/openstack/imageservice/v2/imagedata/results.go @@ -13,6 +13,12 @@ type UploadResult struct { gophercloud.ErrResult } +// StageResult is the result of a stage image operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type StageResult struct { + gophercloud.ErrResult +} + // DownloadResult is the result of a download image operation. Call its Extract // method to gain access to the image data. type DownloadResult struct { diff --git a/openstack/imageservice/v2/imagedata/testing/fixtures.go b/openstack/imageservice/v2/imagedata/testing/fixtures.go index fe93fc9730..64c44bdf6f 100644 --- a/openstack/imageservice/v2/imagedata/testing/fixtures.go +++ b/openstack/imageservice/v2/imagedata/testing/fixtures.go @@ -26,6 +26,23 @@ func HandlePutImageDataSuccessfully(t *testing.T) { }) } +// HandleStageImageDataSuccessfully setup +func HandleStageImageDataSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/stage", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + b, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Unable to read request body: %v", err) + } + + th.AssertByteArrayEquals(t, []byte{5, 3, 7, 24}, b) + + w.WriteHeader(http.StatusNoContent) + }) +} + // HandleGetImageDataSuccessfully setup func HandleGetImageDataSuccessfully(t *testing.T) { th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/imageservice/v2/imagedata/testing/requests_test.go b/openstack/imageservice/v2/imagedata/testing/requests_test.go index 4ac42d0e73..7155f61b63 100644 --- a/openstack/imageservice/v2/imagedata/testing/requests_test.go +++ b/openstack/imageservice/v2/imagedata/testing/requests_test.go @@ -25,6 +25,20 @@ func TestUpload(t *testing.T) { th.AssertNoErr(t, err) } +func TestStage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleStageImageDataSuccessfully(t) + + err := imagedata.Stage( + fakeclient.ServiceClient(), + "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + readSeekerOfBytes([]byte{5, 3, 7, 24})).ExtractErr() + + th.AssertNoErr(t, err) +} + func readSeekerOfBytes(bs []byte) io.ReadSeeker { return &RS{bs: bs} } diff --git a/openstack/imageservice/v2/imagedata/urls.go b/openstack/imageservice/v2/imagedata/urls.go index ccd6416e53..d9615ba7fb 100644 --- a/openstack/imageservice/v2/imagedata/urls.go +++ b/openstack/imageservice/v2/imagedata/urls.go @@ -2,10 +2,20 @@ package imagedata import "github.com/gophercloud/gophercloud" +const ( + rootPath = "images" + uploadPath = "file" + stagePath = "stage" +) + // `imageDataURL(c,i)` is the URL for the binary image data for the // image identified by ID `i` in the service `c`. func uploadURL(c *gophercloud.ServiceClient, imageID string) string { - return c.ServiceURL("images", imageID, "file") + return c.ServiceURL(rootPath, imageID, uploadPath) +} + +func stageURL(c *gophercloud.ServiceClient, imageID string) string { + return c.ServiceURL(rootPath, imageID, stagePath) } func downloadURL(c *gophercloud.ServiceClient, imageID string) string { From 48c37fb710d1abbfa130f672b4c91329d266c8c2 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Mon, 30 Jul 2018 20:03:39 -0700 Subject: [PATCH 0495/2296] Magnum: Manage Cluster Templates Delete (#1068) * magnum-clustertemplates-delete Fixed hard coded MasterFlavorID for clustertemplates.Create to use choices.FlavorID * Remove inconsistent Extract() for clustertemplates.Delete() --- .../containerinfra/v1/clustertemplates_test.go | 4 ++-- .../openstack/containerinfra/v1/containerinfra.go | 4 +--- .../containerinfra/v1/clustertemplates/doc.go | 8 ++++++++ .../containerinfra/v1/clustertemplates/requests.go | 8 ++++++++ .../containerinfra/v1/clustertemplates/results.go | 6 ++++++ .../v1/clustertemplates/testing/fixtures.go | 14 ++++++++++++++ .../v1/clustertemplates/testing/requests_test.go | 14 ++++++++++++++ .../containerinfra/v1/clustertemplates/urls.go | 8 ++++++++ 8 files changed, 61 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index 48e76d3f65..d1d35c6cac 100644 --- a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -18,9 +18,9 @@ func TestClusterTemplatesCRUD(t *testing.T) { clusterTemplate, err := CreateClusterTemplate(t, client) th.AssertNoErr(t, err) t.Log(clusterTemplate.Name) - /* - defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) + defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) + /* // Test clusters list allPages, err := clustertemplates.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go index e4c24b09db..3b11802dc0 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -24,7 +24,7 @@ func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*cl boolFalse := false createOpts := clustertemplates.CreateOpts{ Name: name, - MasterFlavorID: "m1.small", + MasterFlavorID: choices.FlavorID, Public: &boolFalse, ServerType: "vm", ExternalNetworkID: choices.ExternalNetworkID, @@ -65,7 +65,6 @@ func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*cl return clusterTemplate, nil } -/* // DeleteClusterTemplate will delete a given cluster-template. A fatal error will occur if the // cluster-template could not be deleted. This works best as a deferred function. func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id string) { @@ -80,4 +79,3 @@ func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id s return } -*/ diff --git a/openstack/containerinfra/v1/clustertemplates/doc.go b/openstack/containerinfra/v1/clustertemplates/doc.go index b07b6e64df..0bef35359a 100644 --- a/openstack/containerinfra/v1/clustertemplates/doc.go +++ b/openstack/containerinfra/v1/clustertemplates/doc.go @@ -38,5 +38,13 @@ Example to Create Cluster Template panic(err) } +Example to Delete Cluster Template + + clusterTemplateID := "dc6d336e3fc4c0a951b5698cd1236ee" + err := clustertemplates.Delete(serviceClient, clusterTemplateID).ExtractErr() + if err != nil { + panic(err) + } + */ package clustertemplates diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index 59628f65d8..c87e5ccc7c 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -64,3 +64,11 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } + +// Delete deletes the specified cluster ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + var result *http.Response + result, r.Err = client.Delete(deleteURL(client, id), nil) + r.Header = result.Header + return +} diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go index e4978ff2a1..3be9c16335 100644 --- a/openstack/containerinfra/v1/clustertemplates/results.go +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -15,6 +15,12 @@ type CreateResult struct { commonResult } +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // Extract is a function that accepts a result and extracts a cluster-template resource. func (r commonResult) Extract() (*ClusterTemplate, error) { var s *ClusterTemplate diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go index 84893f203f..db081d6dba 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go @@ -112,3 +112,17 @@ func HandleCreateClusterTemplateSuccessfully(t *testing.T) { fmt.Fprint(w, ClusterTemplateResponse) }) } + +func HandleDeleteClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clustertemplates/6dc6d336e3fc4c0a951b5698cd1236ee", 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.Header().Add("OpenStack-API-Minimum-Version", "container-infra 1.1") + w.Header().Add("OpenStack-API-Maximum-Version", "container-infra 1.6") + w.Header().Add("OpenStack-API-Version", "container-infra 1.1") + w.Header().Add("X-OpenStack-Request-Id", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index 22572574d4..93392e9bfb 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -57,3 +57,17 @@ func TestCreateClusterTemplate(t *testing.T) { actual.CreatedAt = actual.CreatedAt.UTC() th.AssertDeepEquals(t, ExpectedClusterTemplate, *actual) } + +func TestDeleteClusterTemplate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleDeleteClusterSuccessfully(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + res := clustertemplates.Delete(sc, "6dc6d336e3fc4c0a951b5698cd1236ee") + th.AssertNoErr(t, res.Err) + requestID := res.Header["X-Openstack-Request-Id"][0] + th.AssertEquals(t, "req-781e9bdc-4163-46eb-91c9-786c53188bbb", requestID) +} diff --git a/openstack/containerinfra/v1/clustertemplates/urls.go b/openstack/containerinfra/v1/clustertemplates/urls.go index 8aee327b09..0bdc73ca77 100644 --- a/openstack/containerinfra/v1/clustertemplates/urls.go +++ b/openstack/containerinfra/v1/clustertemplates/urls.go @@ -10,6 +10,14 @@ func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } +func idURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiName, id) +} + func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 453046b3e894322b7f67fd3153112b4bd5ddbaf6 Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Mon, 30 Jul 2018 20:11:54 -0700 Subject: [PATCH 0496/2296] Magnum certificates create (#1137) * Magnum Certificates Create implementation * Fix gofmt errors * Fix review comments - change Csr and Pem to CSR and PEM - combine Get and Create APIs to use single Certificate struct * Fixes from review comments --- .../containerinfra/v1/certificates_test.go | 26 ++++++++-- .../containerinfra/v1/certificates/doc.go | 12 +++++ .../v1/certificates/requests.go | 40 ++++++++++++++++ .../containerinfra/v1/certificates/results.go | 10 +++- .../v1/certificates/testing/fixtures.go | 47 ++++++++++++++++++- .../v1/certificates/testing/requests_test.go | 19 ++++++++ .../containerinfra/v1/certificates/urls.go | 4 ++ 7 files changed, 152 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/certificates_test.go b/acceptance/openstack/containerinfra/v1/certificates_test.go index d1b17278a9..2ca6faa3c1 100644 --- a/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -14,9 +14,29 @@ func TestCertificatesCRUD(t *testing.T) { client, err := clients.NewContainerInfraV1Client() th.AssertNoErr(t, err) - clusterID := "8934d2d1-6bce-4ffa-a017-fb437777269d" + clusterUUID := "8934d2d1-6bce-4ffa-a017-fb437777269d" - certificate, err := certificates.Get(client, clusterID).Extract() + opts := certificates.CreateOpts{ + BayUUID: clusterUUID, + CSR: "-----BEGIN CERTIFICATE REQUEST-----\n" + + "MIIByjCCATMCAQAwgYkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh" + + "MRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMR8w" + + "HQYDVQQLExZJbmZvcm1hdGlvbiBUZWNobm9sb2d5MRcwFQYDVQQDEw53d3cuZ29v" + + "Z2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApZtYJCHJ4VpVXHfV" + + "IlstQTlO4qC03hjX+ZkPyvdYd1Q4+qbAeTwXmCUKYHThVRd5aXSqlPzyIBwieMZr" + + "WFlRQddZ1IzXAlVRDWwAo60KecqeAXnnUK+5fXoTI/UgWshre8tJ+x/TMHaQKR/J" + + "cIWPhqaQhsJuzZbvAdGA80BLxdMCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBAIhl" + + "4PvFq+e7ipARgI5ZM+GZx6mpCz44DTo0JkwfRDf+BtrsaC0q68eTf2XhYOsq4fkH" + + "Q0uA0aVog3f5iJxCa3Hp5gxbJQ6zV6kJ0TEsuaaOhEko9sdpCoPOnRBm2i/XRD2D" + + "6iNh8f8z0ShGsFqjDgFHyF3o+lUyj+UC6H1QW7bn\n" + + "-----END CERTIFICATE REQUEST-----", + } + + createResponse, err := certificates.Create(client, opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, opts.CSR, createResponse.CSR) + + certificate, err := certificates.Get(client, clusterUUID).Extract() th.AssertNoErr(t, err) - t.Log(certificate.Pem) + t.Log(certificate.PEM) } diff --git a/openstack/containerinfra/v1/certificates/doc.go b/openstack/containerinfra/v1/certificates/doc.go index d949ebfee6..7bc04501bc 100644 --- a/openstack/containerinfra/v1/certificates/doc.go +++ b/openstack/containerinfra/v1/certificates/doc.go @@ -11,5 +11,17 @@ Example to get certificates panic(err) } +Example to create certificates + + opts := certificates.CreateOpts{ + BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", + CSR: "-----BEGIN CERTIFICATE REQUEST-----\nMIIEfzCCAmcCAQAwFDESMBAGA1UEAxMJWW91ciBOYW1lMIICIjANBgkqhkiG9w0B\n-----END CERTIFICATE REQUEST-----\n", + } + + response, err := certificates.Create(sc, opts).Extract() + if err != nil { + panic(err) + } + */ package certificates diff --git a/openstack/containerinfra/v1/certificates/requests.go b/openstack/containerinfra/v1/certificates/requests.go index bda1fcef1b..aa8dc51437 100644 --- a/openstack/containerinfra/v1/certificates/requests.go +++ b/openstack/containerinfra/v1/certificates/requests.go @@ -1,9 +1,29 @@ package certificates import ( + "net/http" + "github.com/gophercloud/gophercloud" ) +// CreateOptsBuilder allows extensions to add additional parameters +// to the Create request. +type CreateOptsBuilder interface { + ToCertificateCreateMap() (map[string]interface{}, error) +} + +// CreateOpts represents options used to create a certificate. +type CreateOpts struct { + ClusterUUID string `json:"cluster_uuid,omitempty" xor:"BayUUID"` + BayUUID string `json:"bay_uuid,omitempty" xor:"ClusterUUID"` + CSR string `json:"csr" required:"true"` +} + +// ToCertificateCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToCertificateCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + // Get makes a request against the API to get details for a certificate. func Get(client *gophercloud.ServiceClient, clusterID string) (r GetResult) { url := getURL(client, clusterID) @@ -14,3 +34,23 @@ func Get(client *gophercloud.ServiceClient, clusterID string) (r GetResult) { return } + +// Create requests the creation of a new certificate. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToCertificateCreateMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/containerinfra/v1/certificates/results.go b/openstack/containerinfra/v1/certificates/results.go index 9d93f1a888..898ce85313 100644 --- a/openstack/containerinfra/v1/certificates/results.go +++ b/openstack/containerinfra/v1/certificates/results.go @@ -13,6 +13,11 @@ type GetResult struct { commonResult } +// CreateResult is the response of a Create operations. +type CreateResult struct { + commonResult +} + // Extract is a function that accepts a result and extracts a certificate resource. func (r commonResult) Extract() (*Certificate, error) { var s *Certificate @@ -20,10 +25,11 @@ func (r commonResult) Extract() (*Certificate, error) { return s, err } -// Represents a template for a Cluster Template +// Represents a Certificate type Certificate struct { ClusterUUID string `json:"cluster_uuid"` BayUUID string `json:"bay_uuid"` Links []gophercloud.Link `json:"links"` - Pem string `json:"pem"` + PEM string `json:"pem"` + CSR string `json:"csr"` } diff --git a/openstack/containerinfra/v1/certificates/testing/fixtures.go b/openstack/containerinfra/v1/certificates/testing/fixtures.go index 2e15683f94..1a9839616d 100644 --- a/openstack/containerinfra/v1/certificates/testing/fixtures.go +++ b/openstack/containerinfra/v1/certificates/testing/fixtures.go @@ -28,10 +28,39 @@ const CertificateResponse = ` ] }` +const CreateCertificateResponse = ` +{ + "cluster_uuid": "d564b18a-2890-4152-be3d-e05d784ff727", + "bay_uuid": "d564b18a-2890-4152-be3d-e05d784ff727", + "pem": "FAKE_CERTIFICATE_PEM", + "csr": "FAKE_CERTIFICATE_CSR", + "links": [ + { + "href": "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", + "rel": "self" + }, + { + "href": "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", + "rel": "bookmark" + } + ] +}` + var ExpectedCertificate = certificates.Certificate{ ClusterUUID: "d564b18a-2890-4152-be3d-e05d784ff727", BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", - Pem: "FAKE_CERTIFICATE", + PEM: "FAKE_CERTIFICATE", + Links: []gophercloud.Link{ + {Href: "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "self"}, + {Href: "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "bookmark"}, + }, +} + +var ExpectedCreateCertificateResponse = certificates.Certificate{ + ClusterUUID: "d564b18a-2890-4152-be3d-e05d784ff727", + BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", + PEM: "FAKE_CERTIFICATE_PEM", + CSR: "FAKE_CERTIFICATE_CSR", Links: []gophercloud.Link{ {Href: "http://10.63.176.154:9511/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "self"}, {Href: "http://10.63.176.154:9511/certificates/d564b18a-2890-4152-be3d-e05d784ff727", Rel: "bookmark"}, @@ -53,3 +82,19 @@ func HandleGetCertificateSuccessfully(t *testing.T) { fmt.Fprint(w, CertificateResponse) }) } + +func HandleCreateCertificateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/certificates/", 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.Header().Add("OpenStack-API-Minimum-Version", "container-infra 1.1") + w.Header().Add("OpenStack-API-Maximum-Version", "container-infra 1.6") + w.Header().Add("OpenStack-API-Version", "container-infra 1.1") + w.Header().Add("X-OpenStack-Request-Id", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.WriteHeader(http.StatusCreated) + + fmt.Fprint(w, CreateCertificateResponse) + }) +} diff --git a/openstack/containerinfra/v1/certificates/testing/requests_test.go b/openstack/containerinfra/v1/certificates/testing/requests_test.go index cc3421c895..eaaf9a2a9d 100644 --- a/openstack/containerinfra/v1/certificates/testing/requests_test.go +++ b/openstack/containerinfra/v1/certificates/testing/requests_test.go @@ -21,3 +21,22 @@ func TestGetCertificates(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCertificate, *actual) } + +func TestCreateCertificates(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCreateCertificateSuccessfully(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + opts := certificates.CreateOpts{ + BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", + CSR: "FAKE_CERTIFICATE_CSR", + } + + actual, err := certificates.Create(sc, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCreateCertificateResponse, *actual) +} diff --git a/openstack/containerinfra/v1/certificates/urls.go b/openstack/containerinfra/v1/certificates/urls.go index e7d40937d7..50d5b55ae2 100644 --- a/openstack/containerinfra/v1/certificates/urls.go +++ b/openstack/containerinfra/v1/certificates/urls.go @@ -13,3 +13,7 @@ func commonURL(client *gophercloud.ServiceClient) string { func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL(apiName, id) } + +func createURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 5a70be10fdef2e0bc31ef6633fdc581d8e9777dc Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 31 Jul 2018 06:19:30 +0300 Subject: [PATCH 0497/2296] Compute V2: implement server OS-SRV-USG extension (#1161) * Compute V2: implement server OS-SRV-USG extension Add the new "serverusage" package with basic structure and UnmarshalJSON method. Add unit test and update TestServersWithExtensionsCreateDestroy aceptance test. Add documentation example. * Compute V2: fix OS-SRV-USG documentation Use "computeClient" variable name instead of fake.Serviceclient(). * Compute V2: update OS-SRV-USG acc test Fix checks for extendedServer LaunchedAt, TerminatedAt fields. --- .../openstack/compute/v2/servers_test.go | 4 ++ .../compute/v2/extensions/serverusage/doc.go | 20 +++++++++ .../v2/extensions/serverusage/results.go | 34 ++++++++++++++ .../v2/extensions/serverusage/testing/doc.go | 1 + .../serverusage/testing/fixtures.go | 20 +++++++++ .../serverusage/testing/requests_test.go | 44 +++++++++++++++++++ 6 files changed, 123 insertions(+) create mode 100644 openstack/compute/v2/extensions/serverusage/doc.go create mode 100644 openstack/compute/v2/extensions/serverusage/results.go create mode 100644 openstack/compute/v2/extensions/serverusage/testing/doc.go create mode 100644 openstack/compute/v2/extensions/serverusage/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/serverusage/testing/requests_test.go diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 92652c47bb..11484a5c61 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -14,6 +14,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedstatus" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/serverusage" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" @@ -88,6 +89,7 @@ func TestServersWithExtensionsCreateDestroy(t *testing.T) { servers.Server availabilityzones.ServerAvailabilityZoneExt extendedstatus.ServerExtendedStatusExt + serverusage.UsageExt } client, err := clients.NewComputeV2Client() @@ -105,6 +107,8 @@ func TestServersWithExtensionsCreateDestroy(t *testing.T) { th.AssertEquals(t, int(extendedServer.PowerState), extendedstatus.RUNNING) th.AssertEquals(t, extendedServer.TaskState, "") th.AssertEquals(t, extendedServer.VmState, "active") + th.AssertEquals(t, extendedServer.LaunchedAt.IsZero(), false) + th.AssertEquals(t, extendedServer.TerminatedAt.IsZero(), true) } func TestServersWithoutImageRef(t *testing.T) { diff --git a/openstack/compute/v2/extensions/serverusage/doc.go b/openstack/compute/v2/extensions/serverusage/doc.go new file mode 100644 index 0000000000..0f3127f042 --- /dev/null +++ b/openstack/compute/v2/extensions/serverusage/doc.go @@ -0,0 +1,20 @@ +/* +Package serverusage provides the ability the ability to extend a server result +with the extended usage information. + +Example to Get an extended information: + + 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) + } + + fmt.Printf("%+v\n", serverWithUsageExt) +*/ +package serverusage diff --git a/openstack/compute/v2/extensions/serverusage/results.go b/openstack/compute/v2/extensions/serverusage/results.go new file mode 100644 index 0000000000..a80abb6bc9 --- /dev/null +++ b/openstack/compute/v2/extensions/serverusage/results.go @@ -0,0 +1,34 @@ +package serverusage + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" +) + +// UsageExt represents OS-SRV-USG server response fields. +type UsageExt struct { + LaunchedAt time.Time `json:"-"` + TerminatedAt time.Time `json:"-"` +} + +// UnmarshalJSON helps to unmarshal UsageExt fields into needed values. +func (r *UsageExt) UnmarshalJSON(b []byte) error { + type tmp UsageExt + var s struct { + tmp + LaunchedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:launched_at"` + TerminatedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:terminated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = UsageExt(s.tmp) + + r.LaunchedAt = time.Time(s.LaunchedAt) + r.TerminatedAt = time.Time(s.TerminatedAt) + + return nil +} diff --git a/openstack/compute/v2/extensions/serverusage/testing/doc.go b/openstack/compute/v2/extensions/serverusage/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/compute/v2/extensions/serverusage/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/compute/v2/extensions/serverusage/testing/fixtures.go b/openstack/compute/v2/extensions/serverusage/testing/fixtures.go new file mode 100644 index 0000000000..2d2cf2ef53 --- /dev/null +++ b/openstack/compute/v2/extensions/serverusage/testing/fixtures.go @@ -0,0 +1,20 @@ +package testing + +// ServerWithUsageExtResult represents a raw server response from the Compute API +// with OS-SRV-USG data. +// Most of the actual fields were deleted from the response. +const ServerWithUsageExtResult = ` +{ + "server": { + "OS-SRV-USG:launched_at": "2018-07-27T09:15:55.000000", + "OS-SRV-USG:terminated_at": null, + "created": "2018-07-27T09:15:48Z", + "updated": "2018-07-27T09:15:55Z", + "id": "d650a0ce-17c3-497d-961a-43c4af80998a", + "name": "test_instance", + "status": "ACTIVE", + "user_id": "0f2f3822679e4b3ea073e5d1c6ed5f02", + "tenant_id": "424e7cf0243c468ca61732ba45973b3e" + } +} +` diff --git a/openstack/compute/v2/extensions/serverusage/testing/requests_test.go b/openstack/compute/v2/extensions/serverusage/testing/requests_test.go new file mode 100644 index 0000000000..b05dee5786 --- /dev/null +++ b/openstack/compute/v2/extensions/serverusage/testing/requests_test.go @@ -0,0 +1,44 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/serverusage" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestServerWithUsageExt(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/d650a0ce-17c3-497d-961a-43c4af80998a", 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.Fprintf(w, ServerWithUsageExtResult) + }) + + type serverUsageExt struct { + servers.Server + serverusage.UsageExt + } + var serverWithUsageExt serverUsageExt + err := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) + th.AssertNoErr(t, err) + + th.AssertEquals(t, serverWithUsageExt.LaunchedAt, time.Date(2018, 07, 27, 9, 15, 55, 0, time.UTC)) + th.AssertEquals(t, serverWithUsageExt.TerminatedAt, time.Time{}) + th.AssertEquals(t, serverWithUsageExt.Created, time.Date(2018, 07, 27, 9, 15, 48, 0, time.UTC)) + th.AssertEquals(t, serverWithUsageExt.Updated, time.Date(2018, 07, 27, 9, 15, 55, 0, time.UTC)) + th.AssertEquals(t, serverWithUsageExt.ID, "d650a0ce-17c3-497d-961a-43c4af80998a") + th.AssertEquals(t, serverWithUsageExt.Name, "test_instance") + th.AssertEquals(t, serverWithUsageExt.Status, "ACTIVE") + th.AssertEquals(t, serverWithUsageExt.UserID, "0f2f3822679e4b3ea073e5d1c6ed5f02") + th.AssertEquals(t, serverWithUsageExt.TenantID, "424e7cf0243c468ca61732ba45973b3e") +} From f87a77e917810ce50087a5ee25db00ad7e3d04fd Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 15 Jun 2018 13:35:14 -0700 Subject: [PATCH 0498/2296] magnum-clustertemplates-list sorted unit test json fields and expected golang struct fields --- .../v1/clustertemplates_test.go | 28 +- .../containerinfra/v1/containerinfra.go | 20 +- .../containerinfra/v1/clustertemplates/doc.go | 20 ++ .../v1/clustertemplates/requests.go | 41 +++ .../v1/clustertemplates/results.go | 38 +++ .../v1/clustertemplates/testing/fixtures.go | 267 +++++++++++++----- .../clustertemplates/testing/requests_test.go | 28 ++ .../v1/clustertemplates/urls.go | 4 + 8 files changed, 359 insertions(+), 87 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index d1d35c6cac..86c15e8095 100644 --- a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -6,8 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" - //"github.com/gophercloud/gophercloud/acceptance/tools" - //"github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -20,23 +19,24 @@ func TestClusterTemplatesCRUD(t *testing.T) { t.Log(clusterTemplate.Name) defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) - /* - // Test clusters list - allPages, err := clustertemplates.List(client, nil).AllPages() - th.AssertNoErr(t, err) - allClusterTemplates, err := clustertemplates.ExtractClusterTemplates(allPages) - th.AssertNoErr(t, err) + // Test clusters list + allPages, err := clustertemplates.List(client, nil).AllPages() + th.AssertNoErr(t, err) - var found bool - for _, v := range allClusterTemplates { - if v.UUID == clusterTemplate.UUID { - found = true - } + allClusterTemplates, err := clustertemplates.ExtractClusterTemplates(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allClusterTemplates { + if v.UUID == clusterTemplate.UUID { + found = true } + } - th.AssertEquals(t, found, true) + th.AssertEquals(t, found, true) + /* // Test cluster update updateOpts := []clustertemplates.UpdateOptsBuilder{ clustertemplates.UpdateOpts{ diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go index 3b11802dc0..22d852fc5f 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -23,19 +23,19 @@ func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*cl boolFalse := false createOpts := clustertemplates.CreateOpts{ - Name: name, - MasterFlavorID: choices.FlavorID, - Public: &boolFalse, - ServerType: "vm", - ExternalNetworkID: choices.ExternalNetworkID, - ImageID: choices.MagnumImageID, - RegistryEnabled: &boolFalse, - DockerStorageDriver: "devicemapper", COE: "swarm", - FlavorID: choices.FlavorID, - MasterLBEnabled: &boolFalse, DNSNameServer: "8.8.8.8", + DockerStorageDriver: "devicemapper", + ExternalNetworkID: choices.ExternalNetworkID, + FlavorID: choices.FlavorID, FloatingIPEnabled: &boolFalse, + ImageID: choices.MagnumImageID, + MasterFlavorID: choices.FlavorID, + MasterLBEnabled: &boolFalse, + Name: name, + Public: &boolFalse, + RegistryEnabled: &boolFalse, + ServerType: "vm", } res := clustertemplates.Create(client, createOpts) diff --git a/openstack/containerinfra/v1/clustertemplates/doc.go b/openstack/containerinfra/v1/clustertemplates/doc.go index 0bef35359a..59113e8e25 100644 --- a/openstack/containerinfra/v1/clustertemplates/doc.go +++ b/openstack/containerinfra/v1/clustertemplates/doc.go @@ -46,5 +46,25 @@ Example to Delete Cluster Template panic(err) } +Example to List Clusters Templates + + listOpts := clustertemplates.ListOpts{ + Limit: 20, + } + + allPages, err := clustertemplates.List(serviceClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allClusterTemplates, err := clusters.ExtractClusterTemplates(allPages) + if err != nil { + panic(err) + } + + for _, clusterTemplate := range allClusterTemplates { + fmt.Printf("%+v\n", clusterTemplate) + } + */ package clustertemplates diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index c87e5ccc7c..bdf0a7f9d1 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder Builder. @@ -72,3 +73,43 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { r.Header = result.Header return } + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToClusterTemplateListQuery() (string, error) +} + +// ListOpts allows the sorting of paginated collections through +// the API. SortKey allows you to sort by a particular cluster templates attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for pagination. +type ListOpts struct { + Marker string `q:"marker"` + Limit int `q:"limit"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToClusterTemplateListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToClusterTemplateListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// cluster-templates. It accepts a ListOptsBuilder, which allows you to sort +// the returned collection for greater efficiency. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToClusterTemplateListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ClusterTemplatePage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go index 3be9c16335..2d6a268abf 100644 --- a/openstack/containerinfra/v1/clustertemplates/results.go +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -4,6 +4,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { @@ -64,3 +65,40 @@ type ClusterTemplate struct { UserID string `json:"user_id"` VolumeDriver string `json:"volume_driver"` } + +// ClusterTemplatePage is the page returned by a pager when traversing over a +// collection of cluster-templates. +type ClusterTemplatePage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of cluster template 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 ClusterTemplatePage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Next, nil +} + +// IsEmpty checks whether a ClusterTemplatePage struct is empty. +func (r ClusterTemplatePage) IsEmpty() (bool, error) { + is, err := ExtractClusterTemplates(r) + return len(is) == 0, err +} + +// ExtractClusterTemplates accepts a Page struct, specifically a ClusterTemplatePage struct, +// and extracts the elements into a slice of cluster templates structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractClusterTemplates(r pagination.Page) ([]ClusterTemplate, error) { + var s struct { + ClusterTemplates []ClusterTemplate `json:"clustertemplates"` + } + err := (r.(ClusterTemplatePage)).ExtractInto(&s) + return s.ClusterTemplates, err +} diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go index db081d6dba..4d4860b347 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go @@ -14,89 +14,218 @@ import ( const ClusterTemplateResponse = ` { + "apiserver_port": null, + "cluster_distro": "fedora-atomic", + "coe": "kubernetes", + "created_at": "2018-06-27T16:52:21+00:00", + "dns_nameserver": "8.8.8.8", + "docker_storage_driver": "devicemapper", + "docker_volume_size": 3, + "external_network_id": "public", + "fixed_network": null, + "fixed_subnet": null, + "flavor_id": "m1.small", + "floating_ip_enabled": true, + "http_proxy": "http://10.164.177.169:8080", + "https_proxy": "http://10.164.177.169:8080", + "image_id": "Fedora-Atomic-27-20180212.2.x86_64", "insecure_registry": null, + "keypair_id": "kp", + "labels": null, "links": [ - { - "href": "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", - "rel": "self" - }, - { - "href": "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", - "rel": "bookmark" - } + { + "href": "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", + "rel": "self" + }, + { + "href": "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", + "rel": "bookmark" + } ], - "http_proxy": "http://10.164.177.169:8080", - "updated_at": null, - "floating_ip_enabled": true, - "fixed_subnet": null, "master_flavor_id": null, - "user_id": "c48d66144e9c4a54ae2b164b85cfefe3", - "uuid": "79c0f9e5-93b8-4719-8fab-063afc67bffe", + "master_lb_enabled": true, + "name": "kubernetes-dev", + "network_driver": "flannel", "no_proxy": "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", - "https_proxy": "http://10.164.177.169:8080", - "tls_disabled": false, - "keypair_id": "kp", "project_id": "76bd201dbc1641729904ab190d3390c6", "public": false, - "labels": null, - "docker_volume_size": 3, - "server_type": "vm", - "external_network_id": "public", - "cluster_distro": "fedora-atomic", - "image_id": "Fedora-Atomic-27-20180212.2.x86_64", - "volume_driver": "cinder", "registry_enabled": false, - "docker_storage_driver": "devicemapper", - "apiserver_port": null, - "name": "kubernetes-dev", - "created_at": "2018-06-27T16:52:21+00:00", - "network_driver": "flannel", - "fixed_network": null, - "coe": "kubernetes", - "flavor_id": "m1.small", - "master_lb_enabled": true, - "dns_nameserver": "8.8.8.8" + "server_type": "vm", + "tls_disabled": false, + "updated_at": null, + "user_id": "c48d66144e9c4a54ae2b164b85cfefe3", + "uuid": "79c0f9e5-93b8-4719-8fab-063afc67bffe", + "volume_driver": "cinder" +}` + +const ClusterTemplateListResponse = ` +{ + "clustertemplates": [ + { + "apiserver_port": null, + "cluster_distro": "fedora-atomic", + "coe": "kubernetes", + "created_at": "2018-06-27T16:52:21+00:00", + "dns_nameserver": "8.8.8.8", + "docker_storage_driver": "devicemapper", + "docker_volume_size": 3, + "external_network_id": "public", + "fixed_network": null, + "fixed_subnet": null, + "flavor_id": "m1.small", + "floating_ip_enabled": true, + "http_proxy": "http://10.164.177.169:8080", + "https_proxy": "http://10.164.177.169:8080", + "image_id": "Fedora-Atomic-27-20180212.2.x86_64", + "insecure_registry": null, + "keypair_id": "kp", + "labels": null, + "links": [ + { + "href": "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", + "rel": "self" + }, + { + "href": "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", + "rel": "bookmark" + } + ], + "master_flavor_id": null, + "master_lb_enabled": true, + "name": "kubernetes-dev", + "network_driver": "flannel", + "no_proxy": "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", + "project_id": "76bd201dbc1641729904ab190d3390c6", + "public": false, + "registry_enabled": false, + "server_type": "vm", + "tls_disabled": false, + "updated_at": null, + "user_id": "c48d66144e9c4a54ae2b164b85cfefe3", + "uuid": "79c0f9e5-93b8-4719-8fab-063afc67bffe", + "volume_driver": "cinder" + }, + { + "apiserver_port": null, + "cluster_distro": "fedora-atomic", + "coe": "kubernetes", + "created_at": null, + "dns_nameserver": "8.8.8.8", + "docker_storage_driver": null, + "docker_volume_size": 5, + "external_network_id": "public", + "fixed_network": null, + "fixed_subnet": null, + "flavor_id": "m1.small", + "http_proxy": null, + "https_proxy": null, + "image_id": "fedora-atomic-latest", + "insecure_registry": null, + "keypair_id": "testkey", + "labels": {}, + "links": [ + { + "href": "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", + "rel": "bookmark" + }, + { + "href": "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", + "rel": "self" + } + ], + "master_flavor_id": null, + "master_lb_enabled": false, + "name": "kubernetes-dev", + "network_driver": "flannel", + "no_proxy": null, + "public": false, + "registry_enabled": false, + "server_type": "vm", + "tls_disabled": false, + "updated_at": null, + "uuid": "472807c2-f175-4946-9765-149701a5aba7", + "volume_driver": null + } + ] }` var ExpectedClusterTemplate = clustertemplates.ClusterTemplate{ - InsecureRegistry: "", - Links: []gophercloud.Link{ - {Href: "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", Rel: "self"}, - {Href: "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", Rel: "bookmark"}, - }, - HTTPProxy: "http://10.164.177.169:8080", - UpdatedAt: time.Time{}, - FloatingIPEnabled: true, + APIServerPort: "", + COE: "kubernetes", + ClusterDistro: "fedora-atomic", + CreatedAt: time.Date(2018, 6, 27, 16, 52, 21, 0, time.UTC), + DNSNameServer: "8.8.8.8", + DockerStorageDriver: "devicemapper", + DockerVolumeSize: 3, + ExternalNetworkID: "public", + FixedNetwork: "", FixedSubnet: "", - MasterFlavorID: "", - UserID: "c48d66144e9c4a54ae2b164b85cfefe3", - UUID: "79c0f9e5-93b8-4719-8fab-063afc67bffe", - NoProxy: "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", + FlavorID: "m1.small", + FloatingIPEnabled: true, + HTTPProxy: "http://10.164.177.169:8080", HTTPSProxy: "http://10.164.177.169:8080", - TLSDisabled: false, + ImageID: "Fedora-Atomic-27-20180212.2.x86_64", + InsecureRegistry: "", KeyPairID: "kp", - ProjectID: "76bd201dbc1641729904ab190d3390c6", - Public: false, Labels: map[string]string(nil), - DockerVolumeSize: 3, - ServerType: "vm", - ExternalNetworkID: "public", - ClusterDistro: "fedora-atomic", - ImageID: "Fedora-Atomic-27-20180212.2.x86_64", - VolumeDriver: "cinder", - RegistryEnabled: false, - DockerStorageDriver: "devicemapper", + Links: []gophercloud.Link{ + {Href: "http://10.63.176.154:9511/v1/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", Rel: "self"}, + {Href: "http://10.63.176.154:9511/clustertemplates/79c0f9e5-93b8-4719-8fab-063afc67bffe", Rel: "bookmark"}, + }, + MasterFlavorID: "", + MasterLBEnabled: true, + Name: "kubernetes-dev", + NetworkDriver: "flannel", + NoProxy: "10.0.0.0/8,172.0.0.0/8,192.0.0.0/8,localhost", + ProjectID: "76bd201dbc1641729904ab190d3390c6", + Public: false, + RegistryEnabled: false, + ServerType: "vm", + TLSDisabled: false, + UUID: "79c0f9e5-93b8-4719-8fab-063afc67bffe", + UpdatedAt: time.Time{}, + UserID: "c48d66144e9c4a54ae2b164b85cfefe3", + VolumeDriver: "cinder", +} + +var ExpectedClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ APIServerPort: "", - Name: "kubernetes-dev", - CreatedAt: time.Date(2018, 6, 27, 16, 52, 21, 0, time.UTC), - NetworkDriver: "flannel", - FixedNetwork: "", COE: "kubernetes", - FlavorID: "m1.small", - MasterLBEnabled: true, + ClusterDistro: "fedora-atomic", + CreatedAt: time.Time{}, DNSNameServer: "8.8.8.8", + DockerStorageDriver: "", + DockerVolumeSize: 5, + ExternalNetworkID: "public", + FixedNetwork: "", + FixedSubnet: "", + FlavorID: "m1.small", + HTTPProxy: "", + HTTPSProxy: "", + ImageID: "fedora-atomic-latest", + InsecureRegistry: "", + KeyPairID: "testkey", + Labels: map[string]string{}, + Links: []gophercloud.Link{ + {Href: "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "bookmark"}, + {Href: "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "self"}, + }, + MasterFlavorID: "", + MasterLBEnabled: false, + Name: "kubernetes-dev", + NetworkDriver: "flannel", + NoProxy: "", + Public: false, + RegistryEnabled: false, + ServerType: "vm", + TLSDisabled: false, + UUID: "472807c2-f175-4946-9765-149701a5aba7", + UpdatedAt: time.Time{}, + VolumeDriver: "", } +var ExpectedClusterTemplates = []clustertemplates.ClusterTemplate{ExpectedClusterTemplate, ExpectedClusterTemplate_EmptyTime} + func HandleCreateClusterTemplateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clustertemplates", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -126,3 +255,15 @@ func HandleDeleteClusterSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleListClusterTemplateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clustertemplates", 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, ClusterTemplateListResponse) + }) +} diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index 93392e9bfb..db4782f4e0 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -71,3 +72,30 @@ func TestDeleteClusterTemplate(t *testing.T) { requestID := res.Header["X-Openstack-Request-Id"][0] th.AssertEquals(t, "req-781e9bdc-4163-46eb-91c9-786c53188bbb", requestID) } + +func TestListClusterTemplates(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleListClusterTemplateSuccessfully(t) + + count := 0 + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + clustertemplates.List(sc, clustertemplates.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := clustertemplates.ExtractClusterTemplates(page) + th.AssertNoErr(t, err) + for idx, _ := range actual { + actual[idx].CreatedAt = actual[idx].CreatedAt.UTC() + } + th.AssertDeepEquals(t, ExpectedClusterTemplates, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/containerinfra/v1/clustertemplates/urls.go b/openstack/containerinfra/v1/clustertemplates/urls.go index 0bdc73ca77..92b7f89038 100644 --- a/openstack/containerinfra/v1/clustertemplates/urls.go +++ b/openstack/containerinfra/v1/clustertemplates/urls.go @@ -21,3 +21,7 @@ func createURL(client *gophercloud.ServiceClient) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 8abbc3ed008691b905b158d20d2020775c6bc022 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Tue, 31 Jul 2018 12:43:41 -0700 Subject: [PATCH 0499/2296] Removed HeaderResult for policies Delete --- acceptance/openstack/clustering/v1/clustering.go | 2 +- openstack/clustering/v1/policies/results.go | 13 +------------ .../clustering/v1/policies/testing/fixtures.go | 2 +- .../clustering/v1/policies/testing/requests_test.go | 7 ++++--- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go index 35da0a6607..4122beb906 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -337,7 +337,7 @@ func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, id string) { func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete policy: %s", id) - _, err := policies.Delete(client, id).Extract() + err := policies.Delete(client, id).ExtractErr() if err != nil { t.Fatalf("Error deleting policy %s: %s:", id, err) } diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index ca5bf335b3..592a7e5575 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -136,18 +136,7 @@ type ValidateResult struct { // DeleteResult is the result of a Delete operation. Call its Extract // method to interpret it as a DeleteHeader. type DeleteResult struct { - gophercloud.HeaderResult -} - -// DeleteHeader contains the delete information from a delete policy request. -type DeleteHeader struct { - RequestID string `json:"X-OpenStack-Request-ID"` -} - -func (r DeleteResult) Extract() (*DeleteHeader, error) { - var s *DeleteHeader - err := r.HeaderResult.ExtractInto(&s) - return s, err + gophercloud.ErrResult } // PolicyPage contains a list page of all policies from a List call. diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go index 726fde9861..cf03210184 100644 --- a/openstack/clustering/v1/policies/testing/fixtures.go +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -446,7 +446,7 @@ func HandlePolicyDelete(t *testing.T) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Add("X-OpenStack-Request-ID", PolicyDeleteRequestID) + w.Header().Add("X-OpenStack-Request-Id", PolicyDeleteRequestID) w.WriteHeader(http.StatusNoContent) }) } diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go index 3f74bf0b67..7bf3a527ee 100644 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -65,10 +65,11 @@ func TestDeletePolicy(t *testing.T) { HandlePolicyDelete(t) - actual, err := policies.Delete(fake.ServiceClient(), PolicyIDtoDelete).Extract() - th.AssertNoErr(t, err) + res := policies.Delete(fake.ServiceClient(), PolicyIDtoDelete) + th.AssertNoErr(t, res.ExtractErr()) - th.AssertEquals(t, PolicyDeleteRequestID, actual.RequestID) + requestID := res.Header["X-Openstack-Request-Id"][0] + th.AssertEquals(t, PolicyDeleteRequestID, requestID) } func TestUpdatePolicy(t *testing.T) { From 342e6b7f3411701bfd21178466a7c1720483e83e Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Tue, 31 Jul 2018 14:05:50 -0700 Subject: [PATCH 0500/2296] Magnum Certificates Update implementation --- .../containerinfra/v1/certificates_test.go | 3 +++ openstack/containerinfra/v1/certificates/doc.go | 6 ++++++ .../containerinfra/v1/certificates/requests.go | 9 +++++++++ openstack/containerinfra/v1/certificates/results.go | 5 +++++ .../v1/certificates/testing/fixtures.go | 11 +++++++++++ .../v1/certificates/testing/requests_test.go | 13 +++++++++++++ openstack/containerinfra/v1/certificates/urls.go | 4 ++++ 7 files changed, 51 insertions(+) diff --git a/acceptance/openstack/containerinfra/v1/certificates_test.go b/acceptance/openstack/containerinfra/v1/certificates_test.go index 2ca6faa3c1..c3860f9a71 100644 --- a/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -39,4 +39,7 @@ func TestCertificatesCRUD(t *testing.T) { certificate, err := certificates.Get(client, clusterUUID).Extract() th.AssertNoErr(t, err) t.Log(certificate.PEM) + + err = certificates.Update(client, clusterUUID).ExtractErr() + th.AssertNoErr(t, err) } diff --git a/openstack/containerinfra/v1/certificates/doc.go b/openstack/containerinfra/v1/certificates/doc.go index 7bc04501bc..b5c39f7e66 100644 --- a/openstack/containerinfra/v1/certificates/doc.go +++ b/openstack/containerinfra/v1/certificates/doc.go @@ -23,5 +23,11 @@ Example to create certificates panic(err) } +Example to update certificates + + err := certificates.Update(client, clusterUUID).ExtractErr() + if err != nil { + panic(err) + } */ package certificates diff --git a/openstack/containerinfra/v1/certificates/requests.go b/openstack/containerinfra/v1/certificates/requests.go index aa8dc51437..011546b683 100644 --- a/openstack/containerinfra/v1/certificates/requests.go +++ b/openstack/containerinfra/v1/certificates/requests.go @@ -54,3 +54,12 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } + +// Update will rotate the CA certificate for a cluster +func Update(client *gophercloud.ServiceClient, clusterID string) (r UpdateResult) { + _, r.Err = client.Patch(updateURL(client, clusterID), nil, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} diff --git a/openstack/containerinfra/v1/certificates/results.go b/openstack/containerinfra/v1/certificates/results.go index 898ce85313..654e585fd4 100644 --- a/openstack/containerinfra/v1/certificates/results.go +++ b/openstack/containerinfra/v1/certificates/results.go @@ -18,6 +18,11 @@ type CreateResult struct { commonResult } +// UpdateResult is the response of an Update operations. +type UpdateResult struct { + gophercloud.ErrResult +} + // Extract is a function that accepts a result and extracts a certificate resource. func (r commonResult) Extract() (*Certificate, error) { var s *Certificate diff --git a/openstack/containerinfra/v1/certificates/testing/fixtures.go b/openstack/containerinfra/v1/certificates/testing/fixtures.go index 1a9839616d..0634b7e25d 100644 --- a/openstack/containerinfra/v1/certificates/testing/fixtures.go +++ b/openstack/containerinfra/v1/certificates/testing/fixtures.go @@ -98,3 +98,14 @@ func HandleCreateCertificateSuccessfully(t *testing.T) { fmt.Fprint(w, CreateCertificateResponse) }) } + +func HandleUpdateCertificateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/certificates/d564b18a-2890-4152-be3d-e05d784ff72", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.WriteHeader(http.StatusAccepted) + fmt.Fprintf(w, `{}`) + }) +} diff --git a/openstack/containerinfra/v1/certificates/testing/requests_test.go b/openstack/containerinfra/v1/certificates/testing/requests_test.go index eaaf9a2a9d..7ad95e9cc9 100644 --- a/openstack/containerinfra/v1/certificates/testing/requests_test.go +++ b/openstack/containerinfra/v1/certificates/testing/requests_test.go @@ -40,3 +40,16 @@ func TestCreateCertificates(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreateCertificateResponse, *actual) } + +func TestUpdateCertificates(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleUpdateCertificateSuccessfully(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + err := certificates.Update(sc, "d564b18a-2890-4152-be3d-e05d784ff72").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/containerinfra/v1/certificates/urls.go b/openstack/containerinfra/v1/certificates/urls.go index 50d5b55ae2..87c831d88e 100644 --- a/openstack/containerinfra/v1/certificates/urls.go +++ b/openstack/containerinfra/v1/certificates/urls.go @@ -17,3 +17,7 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiName, id) +} From c76d66c593427e9cf186628f088c9c6be4bc29d5 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Wed, 1 Aug 2018 07:22:37 +0300 Subject: [PATCH 0501/2296] Compute V2: partial support for the server OS-EXT-SRV-ATTR extension (#1163) * Compute V2: add server OS-EXT-SRV-ATTR extension Add the new "extendedserverattributes" package with basic structure. Add unit test and update TestServersWithExtensionsCreateDestroy aceptance test. Add documentation example. * Compute V2: extend docs about OS-EXT-SRV-ATTR Add a note about microversion usage for the OS-EXT-SRV-ATTR extension. Set microversion in the TestServersWithExtensionsCreateDestroy acceptance test. * Compute V2: remove OS-EXT-SRV-ATTR microversions Remove fields in OS-EXT-SRV-ATTR that are dependent on the API microversion support. Full OS-EXT-SRV-ATTR can be added after full microversion implementation in the Gophercloud. * Compute V2: fix OS-EXT-SRV-ATTR unit test Remove disabled fields from the TestServerWithUsageExt. Those fields may be enabled after implementing full API microversions support in the Gophercloud. * Compute V2: remove acc test for OS-EXT-SRV-ATTR We can't assert anything for the OS-EXT-SRV-ATTR fields that are available without microversions. Acceptance test for OS-EXT-SRV-ATTR may be added after implementing full API microversions support in the Gophercloud. --- .../extendedserverattributes/doc.go | 20 ++++++++++ .../extendedserverattributes/results.go | 19 ++++++++++ .../extendedserverattributes/testing/doc.go | 1 + .../testing/fixtures.go | 28 ++++++++++++++ .../testing/requests_test.go | 37 +++++++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 openstack/compute/v2/extensions/extendedserverattributes/doc.go create mode 100644 openstack/compute/v2/extensions/extendedserverattributes/results.go create mode 100644 openstack/compute/v2/extensions/extendedserverattributes/testing/doc.go create mode 100644 openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go diff --git a/openstack/compute/v2/extensions/extendedserverattributes/doc.go b/openstack/compute/v2/extensions/extendedserverattributes/doc.go new file mode 100644 index 0000000000..9626f4e186 --- /dev/null +++ b/openstack/compute/v2/extensions/extendedserverattributes/doc.go @@ -0,0 +1,20 @@ +/* +Package extendedserverattributes provides the ability to extend a +server result with the extended usage information. + +Example to Get an extended information: + + 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) + } + + fmt.Printf("%+v\n", serverWithAttributesExt) +*/ +package extendedserverattributes diff --git a/openstack/compute/v2/extensions/extendedserverattributes/results.go b/openstack/compute/v2/extensions/extendedserverattributes/results.go new file mode 100644 index 0000000000..abdf7a8b76 --- /dev/null +++ b/openstack/compute/v2/extensions/extendedserverattributes/results.go @@ -0,0 +1,19 @@ +package extendedserverattributes + +// ServerAttributesExt represents OS-EXT-SRV-ATTR server response fields. +// +// Following fields will be added after implementing full API microversion +// support in the Gophercloud: +// +// - OS-EXT-SRV-ATTR:reservation_id" +// - OS-EXT-SRV-ATTR:launch_index" +// - OS-EXT-SRV-ATTR:hostname" +// - OS-EXT-SRV-ATTR:kernel_id" +// - OS-EXT-SRV-ATTR:ramdisk_id" +// - OS-EXT-SRV-ATTR:root_device_name" +// - OS-EXT-SRV-ATTR:user_data" +type ServerAttributesExt struct { + Host string `json:"OS-EXT-SRV-ATTR:host"` + InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` + HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` +} diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/doc.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go new file mode 100644 index 0000000000..c2f0b83e80 --- /dev/null +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go @@ -0,0 +1,28 @@ +package testing + +// ServerWithAttributesExtResult represents a raw server response from the +// Compute API with OS-EXT-SRV-ATTR data. +// Most of the actual fields were deleted from the response. +const ServerWithAttributesExtResult = ` +{ + "server": { + "OS-EXT-SRV-ATTR:user_data": "", + "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", + "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", + "OS-EXT-SRV-ATTR:hostname": "test00", + "OS-EXT-SRV-ATTR:reservation_id": "r-ky9gim1l", + "OS-EXT-SRV-ATTR:ramdisk_id": "", + "OS-EXT-SRV-ATTR:host": "compute01", + "OS-EXT-SRV-ATTR:kernel_id": "", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "compute01", + "OS-EXT-SRV-ATTR:launch_index": 0, + "created": "2018-07-27T09:15:48Z", + "updated": "2018-07-27T09:15:55Z", + "id": "d650a0ce-17c3-497d-961a-43c4af80998a", + "name": "test_instance", + "status": "ACTIVE", + "user_id": "0f2f3822679e4b3ea073e5d1c6ed5f02", + "tenant_id": "424e7cf0243c468ca61732ba45973b3e" + } +} +` diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go new file mode 100644 index 0000000000..0fa2b3d0d6 --- /dev/null +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go @@ -0,0 +1,37 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedserverattributes" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestServerWithUsageExt(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/d650a0ce-17c3-497d-961a-43c4af80998a", 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.Fprintf(w, ServerWithAttributesExtResult) + }) + + type serverAttributesExt struct { + servers.Server + extendedserverattributes.ServerAttributesExt + } + var serverWithAttributesExt serverAttributesExt + err := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) + th.AssertNoErr(t, err) + + th.AssertEquals(t, serverWithAttributesExt.Host, "compute01") + th.AssertEquals(t, serverWithAttributesExt.InstanceName, "instance-00000001") + th.AssertEquals(t, serverWithAttributesExt.HypervisorHostname, "compute01") +} From 1032a9839bfa35c9a4607478d86c81849c75fbde Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 2 Aug 2018 06:56:19 +0300 Subject: [PATCH 0502/2296] Compute V2: add Unrescue action (#1169) * Compute V2: migrate rescue to separate package Move the Rescue action of the Compute service to its own package to leverage common Gophercloud structuring. * Compute V2: add image to rescue options This commit adds RescueImageRef field to the rescue options of the "rescueunrescue" package. Also it resctuctures unit tests of that package to more common structure and adds "rescue_image_ref" options to test fixtures. * Compute V2: add rescueunrescue package docs Add documentation for the "rescueunrescue" package. * Compute V2: add acc test for rescue extension Add acceptance test TestServerRescue along with the helper RescueServer. * Compute V2: extend rescue acceptance test Rename TestServerRescue to TestServerRescueUnrescue to indicate that this test will be used to test unrescue call too. * Compute V2: update rescue inline comments Use "place" verb rather that "send" or "put" in all rescue related comments. * Compute V2: use common Result for the Rescue Rescue operation result can contain some data so it's better to use the common "gophercloud.Result" type for this API call. * Compute V2: add Unrescue action Add Unrescue request to the "rescueunrescue" package. Update TestServerRescueUnrescue acceptance test to also run Unrescue call. Provide documentation and unit test. --- acceptance/openstack/compute/v2/compute.go | 30 ++++++++++++ .../compute/v2/rescueunrescue_test.go | 25 ++++++++++ .../v2/extensions/rescueunrescue/doc.go | 28 +++++++++++ .../v2/extensions/rescueunrescue/requests.go | 48 ++++++++++++++++++ .../v2/extensions/rescueunrescue/results.go | 28 +++++++++++ .../extensions/rescueunrescue/testing/doc.go | 1 + .../rescueunrescue/testing/fixtures.go | 25 ++++++++++ .../rescueunrescue/testing/requests_test.go | 49 +++++++++++++++++++ .../v2/extensions/rescueunrescue/urls.go | 7 +++ openstack/compute/v2/servers/requests.go | 33 ------------- openstack/compute/v2/servers/results.go | 15 ------ .../compute/v2/servers/testing/fixtures.go | 12 ----- .../v2/servers/testing/requests_test.go | 14 ------ 13 files changed, 241 insertions(+), 74 deletions(-) create mode 100644 acceptance/openstack/compute/v2/rescueunrescue_test.go create mode 100644 openstack/compute/v2/extensions/rescueunrescue/doc.go create mode 100644 openstack/compute/v2/extensions/rescueunrescue/requests.go create mode 100644 openstack/compute/v2/extensions/rescueunrescue/results.go create mode 100644 openstack/compute/v2/extensions/rescueunrescue/testing/doc.go create mode 100644 openstack/compute/v2/extensions/rescueunrescue/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/rescueunrescue/urls.go diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 570378ddfd..89bd08de43 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -19,6 +19,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" @@ -973,3 +974,32 @@ func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOp dest.ServerGroupMembers = &src.ServerGroupMembers dest.MetadataItems = &src.MetadataItems } + +// RescueServer will place the specified server into rescue mode. +func RescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { + t.Logf("Attempting to put server %s into rescue mode", server.ID) + _, err := rescueunrescue.Rescue(client, server.ID, rescueunrescue.RescueOpts{}).Extract() + if err != nil { + return err + } + + if err := WaitForComputeStatus(client, server, "RESCUE"); err != nil { + return err + } + + return nil +} + +// UnrescueServer will return server from rescue mode. +func UnrescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { + t.Logf("Attempting to return server %s from rescue mode", server.ID) + if err := rescueunrescue.Unrescue(client, server.ID).ExtractErr(); err != nil { + return err + } + + if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { + return err + } + + return nil +} diff --git a/acceptance/openstack/compute/v2/rescueunrescue_test.go b/acceptance/openstack/compute/v2/rescueunrescue_test.go new file mode 100644 index 0000000000..bbc38fafa8 --- /dev/null +++ b/acceptance/openstack/compute/v2/rescueunrescue_test.go @@ -0,0 +1,25 @@ +// +build acceptance compute rescueunrescue + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestServerRescueUnrescue(t *testing.T) { + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) + + err = RescueServer(t, client, server) + th.AssertNoErr(t, err) + + err = UnrescueServer(t, client, server) + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/rescueunrescue/doc.go b/openstack/compute/v2/extensions/rescueunrescue/doc.go new file mode 100644 index 0000000000..2081018cdb --- /dev/null +++ b/openstack/compute/v2/extensions/rescueunrescue/doc.go @@ -0,0 +1,28 @@ +/* +Package rescueunrescue provides the ability to place a server into rescue mode +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" + + 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) + +Example to Unrescue a server + + serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" + + if err := rescueunrescue.Unrescue(computeClient, serverID).ExtractErr(); err != nil { + panic(err) + } +*/ +package rescueunrescue diff --git a/openstack/compute/v2/extensions/rescueunrescue/requests.go b/openstack/compute/v2/extensions/rescueunrescue/requests.go new file mode 100644 index 0000000000..61a23aad66 --- /dev/null +++ b/openstack/compute/v2/extensions/rescueunrescue/requests.go @@ -0,0 +1,48 @@ +package rescueunrescue + +import "github.com/gophercloud/gophercloud" + +// RescueOptsBuilder is an interface that allows extensions to override the +// default structure of a Rescue request. +type RescueOptsBuilder interface { + ToServerRescueMap() (map[string]interface{}, error) +} + +// RescueOpts represents the configuration options used to control a Rescue +// option. +type RescueOpts struct { + // AdminPass is the desired administrative password for the instance in + // RESCUE mode. + // If it's left blank, the server will generate a password. + AdminPass string `json:"adminPass,omitempty"` + + // RescueImageRef contains reference on an image that needs to be used as + // rescue image. + // If it's left blank, the server will be rescued with the default image. + RescueImageRef string `json:"rescue_image_ref,omitempty"` +} + +// ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON +// request body for the Rescue request. +func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "rescue") +} + +// Rescue instructs the provider to place the server into RESCUE mode. +func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { + b, err := opts.ToServerRescueMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Unrescue instructs the provider to return the server from RESCUE mode. +func Unrescue(client *gophercloud.ServiceClient, id string) (r UnrescueResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) + return +} diff --git a/openstack/compute/v2/extensions/rescueunrescue/results.go b/openstack/compute/v2/extensions/rescueunrescue/results.go new file mode 100644 index 0000000000..1966b15c46 --- /dev/null +++ b/openstack/compute/v2/extensions/rescueunrescue/results.go @@ -0,0 +1,28 @@ +package rescueunrescue + +import "github.com/gophercloud/gophercloud" + +type commonResult struct { + gophercloud.Result +} + +// RescueResult is the response from a Rescue operation. Call its Extract +// method to retrieve adminPass for a rescued server. +type RescueResult struct { + commonResult +} + +// UnrescueResult is the response from an UnRescue operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type UnrescueResult struct { + gophercloud.ErrResult +} + +// Extract interprets any RescueResult as an AdminPass, if possible. +func (r RescueResult) Extract() (string, error) { + var s struct { + AdminPass string `json:"adminPass"` + } + err := r.ExtractInto(&s) + return s.AdminPass, err +} diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/doc.go b/openstack/compute/v2/extensions/rescueunrescue/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/compute/v2/extensions/rescueunrescue/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/fixtures.go b/openstack/compute/v2/extensions/rescueunrescue/testing/fixtures.go new file mode 100644 index 0000000000..c822193d6a --- /dev/null +++ b/openstack/compute/v2/extensions/rescueunrescue/testing/fixtures.go @@ -0,0 +1,25 @@ +package testing + +// RescueRequest represents request to rescue a server. +const RescueRequest = ` +{ + "rescue": { + "adminPass": "aUPtawPzE9NU", + "rescue_image_ref": "115e5c5b-72f0-4a0a-9067-60706545248c" + } +} +` + +// RescueResult represents a raw server response to a RescueRequest. +const RescueResult = ` +{ + "adminPass": "aUPtawPzE9NU" +} +` + +// UnrescueRequest represents request to unrescue a server. +const UnrescueRequest = ` +{ + "unrescue": null +} +` diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go b/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go new file mode 100644 index 0000000000..fda9023e2f --- /dev/null +++ b/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go @@ -0,0 +1,49 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestRescue(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/3f54d05f-3430-4d80-aa07-63e6af9e2488/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, RescueRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, RescueResult) + }) + + s, err := rescueunrescue.Rescue(fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488", rescueunrescue.RescueOpts{ + AdminPass: "aUPtawPzE9NU", + RescueImageRef: "115e5c5b-72f0-4a0a-9067-60706545248c", + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "aUPtawPzE9NU", s) +} + +func TestUnrescue(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/3f54d05f-3430-4d80-aa07-63e6af9e2488/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, UnrescueRequest) + + w.WriteHeader(http.StatusAccepted) + }) + + err := rescueunrescue.Unrescue(fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/rescueunrescue/urls.go b/openstack/compute/v2/extensions/rescueunrescue/urls.go new file mode 100644 index 0000000000..fa5cd3735f --- /dev/null +++ b/openstack/compute/v2/extensions/rescueunrescue/urls.go @@ -0,0 +1,7 @@ +package rescueunrescue + +import "github.com/gophercloud/gophercloud" + +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "action") +} diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index cf7e94d027..a6530f8d65 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -545,39 +545,6 @@ func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) return } -// RescueOptsBuilder is an interface that allows extensions to override the -// default structure of a Rescue request. -type RescueOptsBuilder interface { - ToServerRescueMap() (map[string]interface{}, error) -} - -// RescueOpts represents the configuration options used to control a Rescue -// option. -type RescueOpts struct { - // AdminPass is the desired administrative password for the instance in - // RESCUE mode. If it's left blank, the server will generate a password. - AdminPass string `json:"adminPass,omitempty"` -} - -// ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON -// request body for the Rescue request. -func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "rescue") -} - -// Rescue instructs the provider to place the server into RESCUE mode. -func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { - b, err := opts.ToServerRescueMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - return -} - // ResetMetadataOptsBuilder allows extensions to add additional parameters to // the Reset request. type ResetMetadataOptsBuilder interface { diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index ac685e77b3..f973d1ea0e 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -68,12 +68,6 @@ type ActionResult struct { gophercloud.ErrResult } -// RescueResult is the response from a Rescue operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type RescueResult struct { - ActionResult -} - // CreateImageResult is the response from a CreateImage operation. Call its // ExtractImageID method to retrieve the ID of the newly created image. type CreateImageResult struct { @@ -149,15 +143,6 @@ func (r CreateImageResult) ExtractImageID() (string, error) { return imageID, nil } -// Extract interprets any RescueResult as an AdminPass, if possible. -func (r RescueResult) Extract() (string, error) { - var s struct { - AdminPass string `json:"adminPass"` - } - err := r.ExtractInto(&s) - return s.AdminPass, err -} - // Server represents a server/instance in the OpenStack cloud. type Server struct { // ID uniquely identifies this server amongst all other servers, diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index d6af0193b0..1d4e99c06b 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -888,18 +888,6 @@ func HandleRebuildSuccessfully(t *testing.T, response string) { }) } -// HandleServerRescueSuccessfully sets up the test server to respond to a server Rescue request. -func HandleServerRescueSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/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, `{ "rescue": { "adminPass": "1234567890" } }`) - - w.WriteHeader(http.StatusOK) - w.Write([]byte(`{ "adminPass": "1234567890" }`)) - }) -} - // HandleMetadatumGetSuccessfully sets up the test server to respond to a metadatum Get request. func HandleMetadatumGetSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index b11e25c382..46ae45dbf4 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -383,20 +383,6 @@ func TestRevertResize(t *testing.T) { th.AssertNoErr(t, res.Err) } -func TestRescue(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleServerRescueSuccessfully(t) - - res := servers.Rescue(client.ServiceClient(), "1234asdf", servers.RescueOpts{ - AdminPass: "1234567890", - }) - th.AssertNoErr(t, res.Err) - adminPass, _ := res.Extract() - th.AssertEquals(t, "1234567890", adminPass) -} - func TestGetMetadatum(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 8492827764225bb89735f577b7d14ecc6c711892 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 15 Jun 2018 13:35:14 -0700 Subject: [PATCH 0503/2296] magnum-clustertemplates-get --- .../v1/clustertemplates_test.go | 4 ++ .../v1/clustertemplates/requests.go | 10 +++ .../v1/clustertemplates/results.go | 5 ++ .../v1/clustertemplates/testing/fixtures.go | 67 +++++++++++++++++++ .../clustertemplates/testing/requests_test.go | 28 ++++++++ .../v1/clustertemplates/urls.go | 4 ++ 6 files changed, 118 insertions(+) diff --git a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index 86c15e8095..e4b112be22 100644 --- a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -36,6 +36,10 @@ func TestClusterTemplatesCRUD(t *testing.T) { th.AssertEquals(t, found, true) + template, err := clustertemplates.Get(client, clusterTemplate.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, clusterTemplate.UUID, template.UUID) + /* // Test cluster update updateOpts := []clustertemplates.UpdateOptsBuilder{ diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index bdf0a7f9d1..38569b009b 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -113,3 +113,13 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return ClusterTemplatePage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Get retrieves a specific cluster-template based on its unique ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + var result *http.Response + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go index 2d6a268abf..a85bcfb901 100644 --- a/openstack/containerinfra/v1/clustertemplates/results.go +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -22,6 +22,11 @@ type DeleteResult struct { gophercloud.ErrResult } +// GetResult is the response of a Get operations. +type GetResult struct { + commonResult +} + // Extract is a function that accepts a result and extracts a cluster-template resource. func (r commonResult) Extract() (*ClusterTemplate, error) { var s *ClusterTemplate diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go index 4d4860b347..1458ca2ceb 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go @@ -58,6 +58,49 @@ const ClusterTemplateResponse = ` "volume_driver": "cinder" }` +const ClusterTemplateResponse_EmptyTime = ` +{ + "apiserver_port": null, + "cluster_distro": "fedora-atomic", + "coe": "kubernetes", + "created_at": null, + "dns_nameserver": "8.8.8.8", + "docker_storage_driver": null, + "docker_volume_size": 5, + "external_network_id": "public", + "fixed_network": null, + "fixed_subnet": null, + "flavor_id": "m1.small", + "http_proxy": null, + "https_proxy": null, + "image_id": "fedora-atomic-latest", + "insecure_registry": null, + "keypair_id": "testkey", + "labels": {}, + "links": [ + { + "href": "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", + "rel": "bookmark" + }, + { + "href": "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", + "rel": "self" + } + ], + "master_flavor_id": null, + "master_lb_enabled": false, + "name": "kubernetes-dev", + "network_driver": "flannel", + "no_proxy": null, + "public": false, + "registry_enabled": false, + "server_type": "vm", + "tls_disabled": false, + "updated_at": null, + "uuid": "472807c2-f175-4946-9765-149701a5aba7", + "volume_driver": null +}` + const ClusterTemplateListResponse = ` { "clustertemplates": [ @@ -267,3 +310,27 @@ func HandleListClusterTemplateSuccessfully(t *testing.T) { fmt.Fprint(w, ClusterTemplateListResponse) }) } + +func HandleGetClusterTemplateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ClusterTemplateResponse) + }) +} + +func HandleGetClusterTemplateEmptyTimeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ClusterTemplateResponse_EmptyTime) + }) +} diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index db4782f4e0..c84a90b6cb 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -99,3 +99,31 @@ func TestListClusterTemplates(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestGetClusterTemplate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetClusterTemplateSuccessfully(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + actual, err := clustertemplates.Get(sc, "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + th.AssertNoErr(t, err) + actual.CreatedAt = actual.CreatedAt.UTC() + th.AssertDeepEquals(t, ExpectedClusterTemplate, *actual) +} + +func TestGetClusterTemplateEmptyTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetClusterTemplateEmptyTimeSuccessfully(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + actual, err := clustertemplates.Get(sc, "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + th.AssertNoErr(t, err) + actual.CreatedAt = actual.CreatedAt.UTC() + th.AssertDeepEquals(t, ExpectedClusterTemplate_EmptyTime, *actual) +} diff --git a/openstack/containerinfra/v1/clustertemplates/urls.go b/openstack/containerinfra/v1/clustertemplates/urls.go index 92b7f89038..d1e68c80c4 100644 --- a/openstack/containerinfra/v1/clustertemplates/urls.go +++ b/openstack/containerinfra/v1/clustertemplates/urls.go @@ -25,3 +25,7 @@ func deleteURL(client *gophercloud.ServiceClient, id string) string { func listURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func getURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 9cdae6530f121aca6d530dc54a6c82a49108328c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Va=C5=A1ek?= Date: Fri, 3 Aug 2018 05:38:03 +0200 Subject: [PATCH 0504/2296] Manila: add extend and shrink share functionality (#1175) * manila: added shares.Extend, shares.Shrink * manila: added Extend and Shrink unit tests * manila: added TestExtendAndShrink acceptance test * manila: Extend and Shrink opts builders should follow naming conventions; fixed comments --- .../openstack/sharedfilesystems/v2/shares.go | 10 +++ .../sharedfilesystems/v2/shares_test.go | 40 ++++++++++ .../sharedfilesystems/v2/shares/requests.go | 74 +++++++++++++++++++ .../sharedfilesystems/v2/shares/results.go | 10 +++ .../v2/shares/testing/fixtures.go | 41 +++++++++- .../v2/shares/testing/request_test.go | 28 +++++++ openstack/sharedfilesystems/v2/shares/urls.go | 8 ++ 7 files changed, 210 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index 0bd55089fd..9aaf3e5865 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -109,6 +109,16 @@ func PrintAccessRight(t *testing.T, accessRight *shares.AccessRight) { t.Logf("Access rule %s", string(asJSON)) } +// ExtendShare extends the capacity of an existing share +func ExtendShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, newSize int) error { + return shares.Extend(client, share.ID, &shares.ExtendOpts{NewSize: newSize}).ExtractErr() +} + +// ShrinkShare shrinks the capacity of an existing share +func ShrinkShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, newSize int) error { + return shares.Shrink(client, share.ID, &shares.ShrinkOpts{NewSize: newSize}).ExtractErr() +} + func waitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := shares.Get(c, id).Extract() diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 7aea6d0e15..fd8885c766 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -108,3 +108,43 @@ func TestListAccessRights(t *testing.T) { PrintAccessRight(t, &r) } } + +func TestExtendAndShrink(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a sharedfs client: %v", err) + } + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + err = ExtendShare(t, client, share, 2) + if err != nil { + t.Fatalf("Unable to extend a share: %v", err) + } + + // We need to wait till the Extend operation is done + err = waitForStatus(client, share.ID, "available", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + t.Logf("Share %s successfuly extended", share.ID) + + err = ShrinkShare(t, client, share, 1) + if err != nil { + t.Fatalf("Unable to shrink a share: %v", err) + } + + // We need to wait till the Shrink operation is done + err = waitForStatus(client, share.ID, "available", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + t.Logf("Share %s successfuly shrunk", share.ID) +} diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index b6ba572a50..529d38503b 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -267,3 +267,77 @@ func ListAccessRights(client *gophercloud.ServiceClient, id string) (r ListAcces }) return } + +// ExtendOptsBuilder allows extensions to add additional parameters to the +// Extend request. +type ExtendOptsBuilder interface { + ToShareExtendMap() (map[string]interface{}, error) +} + +// ExtendOpts contains options for extending a Share. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Share Actions, Extend share documentation +type ExtendOpts struct { + // New size in GBs. + NewSize int `json:"new_size"` +} + +// ToShareExtendMap assembles a request body based on the contents of a +// ExtendOpts. +func (opts ExtendOpts) ToShareExtendMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "extend") +} + +// Extend will extend the capacity of an existing share. ExtendResult contains only the error. +// To extract it, call the ExtractErr method on the ExtendResult. +// Client must have Microversion set; minimum supported microversion for Extend is 2.7. +func Extend(client *gophercloud.ServiceClient, id string, opts ExtendOptsBuilder) (r ExtendResult) { + b, err := opts.ToShareExtendMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(extendURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} + +// ShrinkOptsBuilder allows extensions to add additional parameters to the +// Shrink request. +type ShrinkOptsBuilder interface { + ToShareShrinkMap() (map[string]interface{}, error) +} + +// ShrinkOpts contains options for shrinking a Share. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Share Actions, Shrink share documentation +type ShrinkOpts struct { + // New size in GBs. + NewSize int `json:"new_size"` +} + +// ToShareShrinkMap assembles a request body based on the contents of a +// ShrinkOpts. +func (opts ShrinkOpts) ToShareShrinkMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "shrink") +} + +// Shrink will shrink the capacity of an existing share. ShrinkResult contains only the error. +// To extract it, call the ExtractErr method on the ShrinkResult. +// Client must have Microversion set; minimum supported microversion for Shrink is 2.7. +func Shrink(client *gophercloud.ServiceClient, id string, opts ShrinkOptsBuilder) (r ShrinkResult) { + b, err := opts.ToShareShrinkMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(shrinkURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 393f3bbc60..df26c66969 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -305,3 +305,13 @@ func (r ListAccessRightsResult) Extract() ([]AccessRight, error) { type ListAccessRightsResult struct { gophercloud.Result } + +// ExtendResult contains the response body and error from an Extend request. +type ExtendResult struct { + gophercloud.ErrResult +} + +// ShrinkResult contains the response body and error from a Shrink request. +type ShrinkResult struct { + gophercloud.ErrResult +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index b581d44e99..0e60d9142e 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -188,7 +188,7 @@ var listDetailResponse = `{ var listDetailEmptyResponse = `{"shares": []}` -// MockDeleteResponse creates a mock detailed-list response +// 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") @@ -272,6 +272,7 @@ var revokeAccessRequest = `{ } }` +// MockRevokeAccessResponse creates a mock revoke access response func MockRevokeAccessResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -315,3 +316,41 @@ func MockListAccessRightsResponse(t *testing.T) { fmt.Fprintf(w, listAccessRightsResponse) }) } + +var extendRequest = `{ + "extend": { + "new_size": 2 + } + }` + +// MockExtendResponse creates a mock extend share response +func MockExtendResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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, extendRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) +} + +var shrinkRequest = `{ + "shrink": { + "new_size": 1 + } + }` + +// MockShrinkResponse creates a mock shrink share response +func MockShrinkResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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, shrinkRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index cdd218dde3..7513c3345d 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -233,3 +233,31 @@ func TestListAccessRightsSuccess(t *testing.T) { }, }) } + +func TestExtendSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockExtendResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 + c.Microversion = "2.7" + + err := shares.Extend(c, shareID, &shares.ExtendOpts{NewSize: 2}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestShrinkSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockShrinkResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 + c.Microversion = "2.7" + + err := shares.Shrink(c, shareID, &shares.ShrinkOpts{NewSize: 1}).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index f139e5c90f..f50589e7d3 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -33,3 +33,11 @@ func revokeAccessURL(c *gophercloud.ServiceClient, id string) string { func listAccessRightsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } + +func extendURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} + +func shrinkURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} From bd4e15124d9c22f4c6708b0e2e31ccb8ae8142ac Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Sun, 12 Aug 2018 14:57:55 -0700 Subject: [PATCH 0505/2296] Magnum: Manage Cluster Templates Update (#1071) * magnum-clustertemplates-update * Fixed documentation error for clustertemplates.updateOpts' Value not a string Added type UpdateOp: AddOp, RemoveOp, ReplaceOp Remove param checking for UpdateOp and leave it to the back-end for param validation * Removed unnecessary testing block of code --- .../v1/clustertemplates_test.go | 51 +++-- .../containerinfra/v1/clustertemplates/doc.go | 20 ++ .../v1/clustertemplates/requests.go | 53 +++++ .../v1/clustertemplates/results.go | 5 + .../v1/clustertemplates/testing/fixtures.go | 199 ++++++++++++++++++ .../clustertemplates/testing/requests_test.go | 83 ++++++++ .../v1/clustertemplates/urls.go | 4 + 7 files changed, 389 insertions(+), 26 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index e4b112be22..fb27d0971f 100644 --- a/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -40,32 +41,30 @@ func TestClusterTemplatesCRUD(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, clusterTemplate.UUID, template.UUID) - /* - // Test cluster update - updateOpts := []clustertemplates.UpdateOptsBuilder{ - clustertemplates.UpdateOpts{ - Path: "/master_lb_enabled", - Value: "false", - Op: "replace", - }, - clustertemplates.UpdateOpts{ - Path: "/registry_enabled", - Value: "false", - Op: "replace", - }, - clustertemplates.UpdateOpts{ - Path: "/labels/test", - Value: "test", - Op: "add", - }, - } + // Test cluster update + updateOpts := []clustertemplates.UpdateOptsBuilder{ + clustertemplates.UpdateOpts{ + Op: clustertemplates.ReplaceOp, + Path: "/master_lb_enabled", + Value: "false", + }, + clustertemplates.UpdateOpts{ + Op: clustertemplates.ReplaceOp, + Path: "/registry_enabled", + Value: "false", + }, + clustertemplates.UpdateOpts{ + Op: clustertemplates.AddOp, + Path: "/labels/test", + Value: "test", + }, + } - updateClusterTemplate, err := clustertemplates.Update(client, clusterTemplate.UUID, updateOpts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, false, updateClusterTemplate.MasterLBEnabled) - th.AssertEquals(t, false, updateClusterTemplate.RegistryEnabled) - th.AssertEquals(t, "test", updateClusterTemplate.Labels["test"]) - tools.PrintResource(t, updateClusterTemplate) + updateClusterTemplate, err := clustertemplates.Update(client, clusterTemplate.UUID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, false, updateClusterTemplate.MasterLBEnabled) + th.AssertEquals(t, false, updateClusterTemplate.RegistryEnabled) + th.AssertEquals(t, "test", updateClusterTemplate.Labels["test"]) + tools.PrintResource(t, updateClusterTemplate) - */ } diff --git a/openstack/containerinfra/v1/clustertemplates/doc.go b/openstack/containerinfra/v1/clustertemplates/doc.go index 59113e8e25..12289c2abb 100644 --- a/openstack/containerinfra/v1/clustertemplates/doc.go +++ b/openstack/containerinfra/v1/clustertemplates/doc.go @@ -66,5 +66,25 @@ Example to List Clusters Templates fmt.Printf("%+v\n", clusterTemplate) } +Example to Update Cluster Template + + updateOpts := []clustertemplates.UpdateOptsBuilder{ + clustertemplates.UpdateOpts{ + Op: clustertemplates.ReplaceOp, + Path: "/master_lb_enabled", + Value: "True", + }, + clustertemplates.UpdateOpts{ + Op: clustertemplates.ReplaceOp, + Path: "/registry_enabled", + Value: "True", + }, + } + + clustertemplate, err := clustertemplates.Update(serviceClient, updateOpts).Extract() + if err != nil { + panic(err) + } + */ package clustertemplates diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index 38569b009b..c3ceba0744 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -123,3 +123,56 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { } return } + +type UpdateOp string + +const ( + AddOp UpdateOp = "add" + RemoveOp UpdateOp = "remove" + ReplaceOp UpdateOp = "replace" +) + +type UpdateOpts struct { + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value string `json:"value,omitempty"` +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToClusterTemplateUpdateMap() (map[string]interface{}, error) +} + +// ToClusterUpdateMap assembles a request body based on the contents of +// UpdateOpts. +func (opts UpdateOpts) ToClusterTemplateUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return b, nil +} + +// Update implements cluster updated request. +func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { + var o []map[string]interface{} + for _, opt := range opts { + b, err := opt.ToClusterTemplateUpdateMap() + if err != nil { + r.Err = err + return r + } + o = append(o, b) + } + var result *http.Response + result, r.Err = client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go index a85bcfb901..04415cf3b7 100644 --- a/openstack/containerinfra/v1/clustertemplates/results.go +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -27,6 +27,11 @@ type GetResult struct { commonResult } +// UpdateResult is the response of a Update operations. +type UpdateResult struct { + commonResult +} + // Extract is a function that accepts a result and extracts a cluster-template resource. func (r commonResult) Extract() (*ClusterTemplate, error) { var s *ClusterTemplate diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go index 1458ca2ceb..249f4b33b1 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go @@ -334,3 +334,202 @@ func HandleGetClusterTemplateEmptyTimeSuccessfully(t *testing.T) { fmt.Fprint(w, ClusterTemplateResponse_EmptyTime) }) } + +const UpdateResponse = ` +{ + "apiserver_port": null, + "cluster_distro": "fedora-atomic", + "coe": "kubernetes", + "created_at": "2016-08-10T13:47:01+00:00", + "dns_nameserver": "8.8.8.8", + "docker_storage_driver": null, + "docker_volume_size": 5, + "external_network_id": "public", + "fixed_network": null, + "fixed_subnet": null, + "flavor_id": "m1.small", + "http_proxy": null, + "https_proxy": null, + "image_id": "fedora-atomic-latest", + "insecure_registry": null, + "keypair_id": "testkey", + "labels": {}, + "links": [ + { + "href": "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", + "rel": "self" + }, + { + "href": "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", + "rel": "bookmark" + } + ], + "master_flavor_id": null, + "master_lb_enabled": false, + "name": "kubernetes-dev", + "network_driver": "flannel", + "no_proxy": null, + "public": false, + "registry_enabled": false, + "server_type": "vm", + "tls_disabled": false, + "updated_at": null, + "uuid": "472807c2-f175-4946-9765-149701a5aba7", + "volume_driver": null +}` + +const UpdateResponse_EmptyTime = ` +{ + "apiserver_port": null, + "cluster_distro": "fedora-atomic", + "coe": "kubernetes", + "created_at": null, + "dns_nameserver": "8.8.8.8", + "docker_storage_driver": null, + "docker_volume_size": 5, + "external_network_id": "public", + "fixed_network": null, + "fixed_subnet": null, + "flavor_id": "m1.small", + "http_proxy": null, + "https_proxy": null, + "image_id": "fedora-atomic-latest", + "insecure_registry": null, + "keypair_id": "testkey", + "labels": {}, + "links": [ + { + "href": "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", + "rel": "self" + }, + { + "href": "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", + "rel": "bookmark" + } + ], + "master_flavor_id": null, + "master_lb_enabled": false, + "name": "kubernetes-dev", + "network_driver": "flannel", + "no_proxy": null, + "public": false, + "registry_enabled": false, + "server_type": "vm", + "tls_disabled": false, + "updated_at": null, + "uuid": "472807c2-f175-4946-9765-149701a5aba7", + "volume_driver": null +}` + +const UpdateResponse_InvalidUpdate = ` +{ + "errors": [{\"status\": 400, \"code\": \"client\", \"links\": [], \"title\": \"'add' and 'replace' operations needs value\", \"detail\": \"'add' and 'replace' operations needs value\", \"request_id\": \"\"}] +}` + +var ExpectedUpdateClusterTemplate = clustertemplates.ClusterTemplate{ + APIServerPort: "", + COE: "kubernetes", + ClusterDistro: "fedora-atomic", + CreatedAt: time.Date(2016, 8, 10, 13, 47, 01, 0, time.UTC), + DNSNameServer: "8.8.8.8", + DockerStorageDriver: "", + DockerVolumeSize: 5, + ExternalNetworkID: "public", + FixedNetwork: "", + FixedSubnet: "", + FlavorID: "m1.small", + HTTPProxy: "", + HTTPSProxy: "", + ImageID: "fedora-atomic-latest", + InsecureRegistry: "", + KeyPairID: "testkey", + Labels: map[string]string{}, + Links: []gophercloud.Link{ + {Href: "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "self"}, + {Href: "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "bookmark"}, + }, + MasterFlavorID: "", + MasterLBEnabled: false, + Name: "kubernetes-dev", + NetworkDriver: "flannel", + NoProxy: "", + Public: false, + RegistryEnabled: false, + ServerType: "vm", + TLSDisabled: false, + UUID: "472807c2-f175-4946-9765-149701a5aba7", + UpdatedAt: time.Time{}, + VolumeDriver: "", +} + +var ExpectedUpdateClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ + APIServerPort: "", + COE: "kubernetes", + ClusterDistro: "fedora-atomic", + CreatedAt: time.Time{}, + DNSNameServer: "8.8.8.8", + DockerStorageDriver: "", + DockerVolumeSize: 5, + ExternalNetworkID: "public", + FixedNetwork: "", + FixedSubnet: "", + FlavorID: "m1.small", + HTTPProxy: "", + HTTPSProxy: "", + ImageID: "fedora-atomic-latest", + InsecureRegistry: "", + KeyPairID: "testkey", + Labels: map[string]string{}, + Links: []gophercloud.Link{ + {Href: "http://65.61.151.130:9511/v1/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "self"}, + {Href: "http://65.61.151.130:9511/clustertemplates/472807c2-f175-4946-9765-149701a5aba7", Rel: "bookmark"}, + }, + MasterFlavorID: "", + MasterLBEnabled: false, + Name: "kubernetes-dev", + NetworkDriver: "flannel", + NoProxy: "", + Public: false, + RegistryEnabled: false, + ServerType: "vm", + TLSDisabled: false, + UUID: "472807c2-f175-4946-9765-149701a5aba7", + UpdatedAt: time.Time{}, + VolumeDriver: "", +} + +func HandleUpdateClusterTemplateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, UpdateResponse) + }) +} + +func HandleUpdateClusterTemplateEmptyTimeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, UpdateResponse_EmptyTime) + }) +} + +func HandleUpdateClusterTemplateInvalidUpdate(t *testing.T) { + th.Mux.HandleFunc("/v1/clustertemplates/7d85f602-a948-4a30-afd4-e84f47471c15", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + + fmt.Fprint(w, UpdateResponse_EmptyTime) + }) +} diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index c84a90b6cb..cbed6b1365 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -127,3 +127,86 @@ func TestGetClusterTemplateEmptyTime(t *testing.T) { actual.CreatedAt = actual.CreatedAt.UTC() th.AssertDeepEquals(t, ExpectedClusterTemplate_EmptyTime, *actual) } + +func TestUpdateClusterTemplate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleUpdateClusterTemplateSuccessfully(t) + + updateOpts := []clustertemplates.UpdateOptsBuilder{ + clustertemplates.UpdateOpts{ + Path: "/master_lb_enabled", + Value: "True", + Op: clustertemplates.ReplaceOp, + }, + clustertemplates.UpdateOpts{ + Path: "/registry_enabled", + Value: "True", + Op: clustertemplates.ReplaceOp, + }, + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + res := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts) + th.AssertNoErr(t, res.Err) + + actual, err := res.Extract() + th.AssertNoErr(t, err) + actual.CreatedAt = actual.CreatedAt.UTC() + th.AssertDeepEquals(t, ExpectedUpdateClusterTemplate, *actual) +} + +func TestUpdateClusterTemplateEmptyTime(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleUpdateClusterTemplateEmptyTimeSuccessfully(t) + + updateOpts := []clustertemplates.UpdateOptsBuilder{ + clustertemplates.UpdateOpts{ + Op: clustertemplates.ReplaceOp, + Path: "/master_lb_enabled", + Value: "True", + }, + clustertemplates.UpdateOpts{ + Op: clustertemplates.ReplaceOp, + Path: "/registry_enabled", + Value: "True", + }, + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + actual, err := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedUpdateClusterTemplate_EmptyTime, *actual) +} + +func TestUpdateClusterTemplateInvalidUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleUpdateClusterTemplateInvalidUpdate(t) + + updateOpts := []clustertemplates.UpdateOptsBuilder{ + clustertemplates.UpdateOpts{ + Op: clustertemplates.ReplaceOp, + Path: "/master_lb_enabled", + }, + clustertemplates.UpdateOpts{ + Op: clustertemplates.RemoveOp, + Path: "/master_lb_enabled", + }, + clustertemplates.UpdateOpts{ + Op: clustertemplates.AddOp, + Path: "/master_lb_enabled", + }, + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + _, err := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts).Extract() + th.AssertEquals(t, true, err != nil) +} diff --git a/openstack/containerinfra/v1/clustertemplates/urls.go b/openstack/containerinfra/v1/clustertemplates/urls.go index d1e68c80c4..f90fb85108 100644 --- a/openstack/containerinfra/v1/clustertemplates/urls.go +++ b/openstack/containerinfra/v1/clustertemplates/urls.go @@ -29,3 +29,7 @@ func listURL(client *gophercloud.ServiceClient) string { func getURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 69b1773a4cfb2743ab6661acb860b1873eb9b041 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Tue, 14 Aug 2018 15:36:50 +1200 Subject: [PATCH 0506/2296] Octavia - List Amphoare (#1180) * Octavia - List Amphoare * Make field name consistent with json response field name * Fix ExtractAmphorae * Add fields CreatedAt and UpdatedAt for Amphora --- .../loadbalancer/v2/amphorae_test.go | 32 +++++ openstack/loadbalancer/v2/amphorae/doc.go | 25 ++++ .../loadbalancer/v2/amphorae/requests.go | 53 +++++++ openstack/loadbalancer/v2/amphorae/results.go | 129 +++++++++++++++++ .../loadbalancer/v2/amphorae/testing/doc.go | 1 + .../v2/amphorae/testing/fixtures.go | 133 ++++++++++++++++++ .../v2/amphorae/testing/requests_test.go | 51 +++++++ openstack/loadbalancer/v2/amphorae/urls.go | 16 +++ 8 files changed, 440 insertions(+) create mode 100644 acceptance/openstack/loadbalancer/v2/amphorae_test.go create mode 100644 openstack/loadbalancer/v2/amphorae/doc.go create mode 100644 openstack/loadbalancer/v2/amphorae/requests.go create mode 100644 openstack/loadbalancer/v2/amphorae/results.go create mode 100644 openstack/loadbalancer/v2/amphorae/testing/doc.go create mode 100644 openstack/loadbalancer/v2/amphorae/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/amphorae/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/amphorae/urls.go diff --git a/acceptance/openstack/loadbalancer/v2/amphorae_test.go b/acceptance/openstack/loadbalancer/v2/amphorae_test.go new file mode 100644 index 0000000000..0e3369c49a --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/amphorae_test.go @@ -0,0 +1,32 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" +) + +func TestAmphoraeList(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewLoadBalancerV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := amphorae.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list amphorae: %v", err) + } + + allAmphorae, err := amphorae.ExtractAmphorae(allPages) + if err != nil { + t.Fatalf("Unable to extract amphorae: %v", err) + } + + for _, amphora := range allAmphorae { + tools.PrintResource(t, amphora) + } +} diff --git a/openstack/loadbalancer/v2/amphorae/doc.go b/openstack/loadbalancer/v2/amphorae/doc.go new file mode 100644 index 0000000000..61b0b8b6b2 --- /dev/null +++ b/openstack/loadbalancer/v2/amphorae/doc.go @@ -0,0 +1,25 @@ +/* +Package amphorae provides information and interaction with Amphorae +of OpenStack Load-balancing service. + +Example to List Amphorae + + listOpts := amphorae.ListOpts{ + LoadbalancerID: "6bd55cd3-802e-447e-a518-1e74e23bb106", + } + + allPages, err := amphorae.List(octaviaClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allAmphorae, err := amphorae.ExtractAmphorae(allPages) + if err != nil { + panic(err) + } + + for _, amphora := range allAmphorae { + fmt.Printf("%+v\n", amphora) + } +*/ +package amphorae diff --git a/openstack/loadbalancer/v2/amphorae/requests.go b/openstack/loadbalancer/v2/amphorae/requests.go new file mode 100644 index 0000000000..5c2a76f695 --- /dev/null +++ b/openstack/loadbalancer/v2/amphorae/requests.go @@ -0,0 +1,53 @@ +package amphorae + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToAmphoraListQuery() (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 Amphorae attributes you want to see returned. SortKey allows you to +// sort by a particular attribute. SortDir sets the direction, and is +// either `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + LoadbalancerID string `q:"loadbalancer_id"` + ImageID string `q:"image_id"` + Role string `q:"role"` + Status string `q:"status"` + HAPortID string `q:"ha_port_id"` + VRRPPortID string `q:"vrrp_port_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToAmphoraListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToAmphoraListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// amphorae. 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.ToAmphoraListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return AmphoraPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/loadbalancer/v2/amphorae/results.go b/openstack/loadbalancer/v2/amphorae/results.go new file mode 100644 index 0000000000..f24b5998fe --- /dev/null +++ b/openstack/loadbalancer/v2/amphorae/results.go @@ -0,0 +1,129 @@ +package amphorae + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Amphora is virtual machine, container, dedicated hardware, appliance or device that actually performs the task of +// load balancing in the Octavia system. +type Amphora struct { + // The unique ID for the Amphora. + ID string `json:"id"` + + // The ID of the load balancer. + LoadbalancerID string `json:"loadbalancer_id"` + + // The management IP of the amphora. + LBNetworkIP string `json:"lb_network_ip"` + + // The ID of the amphora resource in the compute system. + ComputeID string `json:"compute_id"` + + // The IP address of the Virtual IP (VIP). + HAIP string `json:"ha_ip"` + + // The ID of the Virtual IP (VIP) port. + HAPortID string `json:"ha_port_id"` + + // The date the certificate for the amphora expires. + CertExpiration time.Time `json:"-"` + + // Whether the certificate is in the process of being replaced. + CertBusy bool `json:"cert_busy"` + + // The role of the amphora. One of STANDALONE, MASTER, BACKUP. + Role string `json:"role"` + + // The status of the amphora. One of: BOOTING, ALLOCATED, READY, PENDING_CREATE, PENDING_DELETE, DELETED, ERROR. + Status string `json:"status"` + + // The vrrp port’s ID in the networking system. + VRRPPortID string `json:"vrrp_port_id"` + + // The address of the vrrp port on the amphora. + VRRPIP string `json:"vrrp_ip"` + + // The bound interface name of the vrrp port on the amphora. + VRRPInterface string `json:"vrrp_interface"` + + // The vrrp group’s ID for the amphora. + VRRPID int `json:"vrrp_id"` + + // The priority of the amphora in the vrrp group. + VRRPPriority int `json:"vrrp_priority"` + + // The availability zone of a compute instance, cached at create time. This is not guaranteed to be current. May be + // an empty-string if the compute service does not use zones. + CachedZone string `json:"cached_zone"` + + // The ID of the glance image used for the amphora. + ImageID string `json:"image_id"` + + // The UTC date and timestamp when the resource was created. + CreatedAt time.Time `json:"-"` + + // The UTC date and timestamp when the resource was last updated. + UpdatedAt time.Time `json:"-"` +} + +func (a *Amphora) UnmarshalJSON(b []byte) error { + type tmp Amphora + var s struct { + tmp + CertExpiration gophercloud.JSONRFC3339NoZ `json:"cert_expiration"` + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *a = Amphora(s.tmp) + + a.CreatedAt = time.Time(s.CreatedAt) + a.UpdatedAt = time.Time(s.UpdatedAt) + a.CertExpiration = time.Time(s.CertExpiration) + + return nil +} + +// AmphoraPage is the page returned by a pager when traversing over a +// collection of amphorae. +type AmphoraPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of amphoraes 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 AmphoraPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"amphorae_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a AmphoraPage struct is empty. +func (r AmphoraPage) IsEmpty() (bool, error) { + is, err := ExtractAmphorae(r) + return len(is) == 0, err +} + +// ExtractAmphorae accepts a Page struct, specifically a AmphoraPage +// struct, and extracts the elements into a slice of Amphora structs. In +// other words, a generic collection is mapped into a relevant slice. +func ExtractAmphorae(r pagination.Page) ([]Amphora, error) { + var s struct { + Amphorae []Amphora `json:"amphorae"` + } + err := (r.(AmphoraPage)).ExtractInto(&s) + return s.Amphorae, err +} diff --git a/openstack/loadbalancer/v2/amphorae/testing/doc.go b/openstack/loadbalancer/v2/amphorae/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/loadbalancer/v2/amphorae/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/loadbalancer/v2/amphorae/testing/fixtures.go b/openstack/loadbalancer/v2/amphorae/testing/fixtures.go new file mode 100644 index 0000000000..cc5dec8e73 --- /dev/null +++ b/openstack/loadbalancer/v2/amphorae/testing/fixtures.go @@ -0,0 +1,133 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "time" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// AmphoraeListBody contains the canned body of a amphora list response. +const AmphoraeListBody = ` +{ + "amphorae": [ + { + "cached_zone": "nova", + "cert_busy": false, + "cert_expiration": "2020-08-08T23:44:31", + "compute_id": "667bb225-69aa-44b1-8908-694dc624c267", + "created_at": "2018-08-09T23:44:31", + "ha_ip": "10.0.0.6", + "ha_port_id": "35254b63-9361-4561-9b8f-2bb4e3be60e3", + "id": "45f40289-0551-483a-b089-47214bc2a8a4", + "image_id": "5d1aed06-2624-43f5-a413-9212263c3d53", + "lb_network_ip": "192.168.0.6", + "loadbalancer_id": "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", + "role": "MASTER", + "status": "READY", + "updated_at": "2018-08-09T23:51:06", + "vrrp_id": 1, + "vrrp_interface": "eth1", + "vrrp_ip": "10.0.0.4", + "vrrp_port_id": "dcf0c8b5-6a08-4658-997d-eac97f2b9bbd", + "vrrp_priority": 100 + }, + { + "cached_zone": "nova", + "cert_busy": false, + "cert_expiration": "2020-08-08T23:44:30", + "compute_id": "9cd0f9a2-fe12-42fc-a7e3-5b6fbbe20395", + "created_at": "2018-08-09T23:44:31", + "ha_ip": "10.0.0.6", + "ha_port_id": "35254b63-9361-4561-9b8f-2bb4e3be60e3", + "id": "7f890893-ced0-46ed-8697-33415d070e5a", + "image_id": "5d1aed06-2624-43f5-a413-9212263c3d53", + "lb_network_ip": "192.168.0.17", + "loadbalancer_id": "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", + "role": "BACKUP", + "status": "READY", + "updated_at": "2018-08-09T23:51:06", + "vrrp_id": 1, + "vrrp_interface": "eth1", + "vrrp_ip": "10.0.0.21", + "vrrp_port_id": "13c88c77-207d-4f85-8f7a-84344592e367", + "vrrp_priority": 90 + } + ], + "amphorae_links": [] +} +` + +// FirstAmphora is the first resource in the List request. +var FirstAmphora = amphorae.Amphora{ + CachedZone: "nova", + CertBusy: false, + CertExpiration: time.Date(2020, 8, 8, 23, 44, 31, 0, time.UTC), + ComputeID: "667bb225-69aa-44b1-8908-694dc624c267", + CreatedAt: time.Date(2018, 8, 9, 23, 44, 31, 0, time.UTC), + HAIP: "10.0.0.6", + HAPortID: "35254b63-9361-4561-9b8f-2bb4e3be60e3", + ID: "45f40289-0551-483a-b089-47214bc2a8a4", + ImageID: "5d1aed06-2624-43f5-a413-9212263c3d53", + LBNetworkIP: "192.168.0.6", + LoadbalancerID: "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", + Role: "MASTER", + Status: "READY", + UpdatedAt: time.Date(2018, 8, 9, 23, 51, 6, 0, time.UTC), + VRRPID: 1, + VRRPInterface: "eth1", + VRRPIP: "10.0.0.4", + VRRPPortID: "dcf0c8b5-6a08-4658-997d-eac97f2b9bbd", + VRRPPriority: 100, +} + +// SecondAmphora is the second resource in the List request. +var SecondAmphora = amphorae.Amphora{ + CachedZone: "nova", + CertBusy: false, + CertExpiration: time.Date(2020, 8, 8, 23, 44, 30, 0, time.UTC), + ComputeID: "9cd0f9a2-fe12-42fc-a7e3-5b6fbbe20395", + CreatedAt: time.Date(2018, 8, 9, 23, 44, 31, 0, time.UTC), + HAIP: "10.0.0.6", + HAPortID: "35254b63-9361-4561-9b8f-2bb4e3be60e3", + ID: "7f890893-ced0-46ed-8697-33415d070e5a", + ImageID: "5d1aed06-2624-43f5-a413-9212263c3d53", + LBNetworkIP: "192.168.0.17", + LoadbalancerID: "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", + Role: "BACKUP", + Status: "READY", + UpdatedAt: time.Date(2018, 8, 9, 23, 51, 6, 0, time.UTC), + VRRPID: 1, + VRRPInterface: "eth1", + VRRPIP: "10.0.0.21", + VRRPPortID: "13c88c77-207d-4f85-8f7a-84344592e367", + VRRPPriority: 90, +} + +// ExpectedAmphoraeSlice is the slice of amphorae expected to be returned from ListResponse. +var ExpectedAmphoraeSlice = []amphorae.Amphora{FirstAmphora, SecondAmphora} + +// HandleAmphoraListSuccessfully sets up the test server to respond to a amphorae List request. +func HandleAmphoraListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/octavia/amphorae", 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, AmphoraeListBody) + case "7f890893-ced0-46ed-8697-33415d070e5a": + fmt.Fprintf(w, `{ "amphorae": [] }`) + default: + t.Fatalf("/v2.0/octavia/amphorae invoked with unexpected marker=[%s]", marker) + } + }) +} diff --git a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go new file mode 100644 index 0000000000..eaba251672 --- /dev/null +++ b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go @@ -0,0 +1,51 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" + fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListAmphorae(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAmphoraListSuccessfully(t) + + pages := 0 + err := amphorae.List(fake.ServiceClient(), amphorae.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := amphorae.ExtractAmphorae(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 amphorae, got %d", len(actual)) + } + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllAmphorae(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAmphoraListSuccessfully(t) + + allPages, err := amphorae.List(fake.ServiceClient(), amphorae.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := amphorae.ExtractAmphorae(allPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, 2, len(actual)) + th.AssertDeepEquals(t, ExpectedAmphoraeSlice, actual) +} diff --git a/openstack/loadbalancer/v2/amphorae/urls.go b/openstack/loadbalancer/v2/amphorae/urls.go new file mode 100644 index 0000000000..2f9fb7e8fb --- /dev/null +++ b/openstack/loadbalancer/v2/amphorae/urls.go @@ -0,0 +1,16 @@ +package amphorae + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "octavia" + resourcePath = "amphorae" +) + +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 05ff628727cff7fedd56dac996908e2cc4f1e842 Mon Sep 17 00:00:00 2001 From: "Sean M. Collins" Date: Tue, 14 Aug 2018 16:10:19 -0400 Subject: [PATCH 0507/2296] Update README.md (#1183) Fix link to issue tracker. `/issues` was being transformed into https://github.com/gophercloud/gophercloud/blob/master/issues, which 404's. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c5bfce796..ad29041d9b 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ See the [contributing guide](./.github/CONTRIBUTING.md). ## Help and feedback If you're struggling with something or have spotted a potential bug, feel free -to submit an issue to our [bug tracker](/issues). +to submit an issue to our [bug tracker](https://github.com/gophercloud/gophercloud/issues). ## Thank You From 625c20f54a034d0eb47ae2fa3923735b8ce94b72 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Wed, 15 Aug 2018 13:52:20 +1200 Subject: [PATCH 0508/2296] Octavia - Get Amphora (#1181) --- .../loadbalancer/v2/amphorae/requests.go | 6 +++ openstack/loadbalancer/v2/amphorae/results.go | 19 ++++++++++ .../v2/amphorae/testing/fixtures.go | 37 +++++++++++++++++++ .../v2/amphorae/testing/requests_test.go | 14 +++++++ 4 files changed, 76 insertions(+) diff --git a/openstack/loadbalancer/v2/amphorae/requests.go b/openstack/loadbalancer/v2/amphorae/requests.go index 5c2a76f695..557d052fb6 100644 --- a/openstack/loadbalancer/v2/amphorae/requests.go +++ b/openstack/loadbalancer/v2/amphorae/requests.go @@ -51,3 +51,9 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { return AmphoraPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Get retrieves a particular amphora based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/loadbalancer/v2/amphorae/results.go b/openstack/loadbalancer/v2/amphorae/results.go index f24b5998fe..9560a53df1 100644 --- a/openstack/loadbalancer/v2/amphorae/results.go +++ b/openstack/loadbalancer/v2/amphorae/results.go @@ -127,3 +127,22 @@ func ExtractAmphorae(r pagination.Page) ([]Amphora, error) { err := (r.(AmphoraPage)).ExtractInto(&s) return s.Amphorae, err } + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts an amphora. +func (r commonResult) Extract() (*Amphora, error) { + var s struct { + Amphora *Amphora `json:"amphora"` + } + err := r.ExtractInto(&s) + return s.Amphora, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as an amphora. +type GetResult struct { + commonResult +} diff --git a/openstack/loadbalancer/v2/amphorae/testing/fixtures.go b/openstack/loadbalancer/v2/amphorae/testing/fixtures.go index cc5dec8e73..c5ed496c7b 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/fixtures.go +++ b/openstack/loadbalancer/v2/amphorae/testing/fixtures.go @@ -63,6 +63,32 @@ const AmphoraeListBody = ` } ` +const SingleAmphoraBody = ` +{ + "amphora": { + "cached_zone": "nova", + "cert_busy": false, + "cert_expiration": "2020-08-08T23:44:31", + "compute_id": "667bb225-69aa-44b1-8908-694dc624c267", + "created_at": "2018-08-09T23:44:31", + "ha_ip": "10.0.0.6", + "ha_port_id": "35254b63-9361-4561-9b8f-2bb4e3be60e3", + "id": "45f40289-0551-483a-b089-47214bc2a8a4", + "image_id": "5d1aed06-2624-43f5-a413-9212263c3d53", + "lb_network_ip": "192.168.0.6", + "loadbalancer_id": "882f2a9d-9d53-4bd0-b0e9-08e9d0de11f9", + "role": "MASTER", + "status": "READY", + "updated_at": "2018-08-09T23:51:06", + "vrrp_id": 1, + "vrrp_interface": "eth1", + "vrrp_ip": "10.0.0.4", + "vrrp_port_id": "dcf0c8b5-6a08-4658-997d-eac97f2b9bbd", + "vrrp_priority": 100 + } +} +` + // FirstAmphora is the first resource in the List request. var FirstAmphora = amphorae.Amphora{ CachedZone: "nova", @@ -131,3 +157,14 @@ func HandleAmphoraListSuccessfully(t *testing.T) { } }) } + +// HandleAmphoraGetSuccessfully sets up the test server to respond to am amphora Get request. +func HandleAmphoraGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/octavia/amphorae/45f40289-0551-483a-b089-47214bc2a8a4", 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, SingleAmphoraBody) + }) +} diff --git a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go index eaba251672..bc50042586 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go +++ b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go @@ -49,3 +49,17 @@ func TestListAllAmphorae(t *testing.T) { th.AssertEquals(t, 2, len(actual)) th.AssertDeepEquals(t, ExpectedAmphoraeSlice, actual) } + +func TestGetAmphora(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAmphoraGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := amphorae.Get(client, "45f40289-0551-483a-b089-47214bc2a8a4").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, FirstAmphora, *actual) +} From 83835c772d1adf71e8df7440d5da9ad039dc049e Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Wed, 15 Aug 2018 05:05:10 +0300 Subject: [PATCH 0509/2296] Magnum: fix Cluster templates APIServerPort type (#1186) OpenStack Magnum Cluster template response uses int type instead of string. This commit fixes ClusterTemplate field type and unit tests. --- openstack/containerinfra/v1/clustertemplates/results.go | 2 +- .../v1/clustertemplates/testing/fixtures.go | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go index 04415cf3b7..8b416f7e70 100644 --- a/openstack/containerinfra/v1/clustertemplates/results.go +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -41,7 +41,7 @@ func (r commonResult) Extract() (*ClusterTemplate, error) { // Represents a template for a Cluster Template type ClusterTemplate struct { - APIServerPort string `json:"apiserver_port"` + APIServerPort int `json:"apiserver_port"` COE string `json:"coe"` ClusterDistro string `json:"cluster_distro"` CreatedAt time.Time `json:"created_at"` diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go index 249f4b33b1..7eca134c53 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/fixtures.go @@ -14,7 +14,7 @@ import ( const ClusterTemplateResponse = ` { - "apiserver_port": null, + "apiserver_port": 8081, "cluster_distro": "fedora-atomic", "coe": "kubernetes", "created_at": "2018-06-27T16:52:21+00:00", @@ -105,7 +105,7 @@ const ClusterTemplateListResponse = ` { "clustertemplates": [ { - "apiserver_port": null, + "apiserver_port": 8081, "cluster_distro": "fedora-atomic", "coe": "kubernetes", "created_at": "2018-06-27T16:52:21+00:00", @@ -193,7 +193,7 @@ const ClusterTemplateListResponse = ` }` var ExpectedClusterTemplate = clustertemplates.ClusterTemplate{ - APIServerPort: "", + APIServerPort: 8081, COE: "kubernetes", ClusterDistro: "fedora-atomic", CreatedAt: time.Date(2018, 6, 27, 16, 52, 21, 0, time.UTC), @@ -232,7 +232,6 @@ var ExpectedClusterTemplate = clustertemplates.ClusterTemplate{ } var ExpectedClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ - APIServerPort: "", COE: "kubernetes", ClusterDistro: "fedora-atomic", CreatedAt: time.Time{}, @@ -427,7 +426,6 @@ const UpdateResponse_InvalidUpdate = ` }` var ExpectedUpdateClusterTemplate = clustertemplates.ClusterTemplate{ - APIServerPort: "", COE: "kubernetes", ClusterDistro: "fedora-atomic", CreatedAt: time.Date(2016, 8, 10, 13, 47, 01, 0, time.UTC), @@ -463,7 +461,6 @@ var ExpectedUpdateClusterTemplate = clustertemplates.ClusterTemplate{ } var ExpectedUpdateClusterTemplate_EmptyTime = clustertemplates.ClusterTemplate{ - APIServerPort: "", COE: "kubernetes", ClusterDistro: "fedora-atomic", CreatedAt: time.Time{}, From 185230dfbd12fc178f22e1e2a5c4cf0e3ef5ae45 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 16 Aug 2018 22:16:43 -0600 Subject: [PATCH 0510/2296] Image Service v2: Support Updating Properties (#1188) This commit adds support to update image properties. Technically it can update any image field with a string value, so this duplicates the functionality of some existing Update calls. However, there's no other way to implement this since the OpenStack Image Service treats all properties as first-class fields. --- .../openstack/imageservice/v2/images_test.go | 42 ++++++++++++ openstack/imageservice/v2/images/requests.go | 38 +++++++++-- .../v2/images/testing/fixtures.go | 57 ++++++++++++++++ .../v2/images/testing/requests_test.go | 68 +++++++++++++++++++ 4 files changed, 201 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/acceptance/openstack/imageservice/v2/images_test.go index 9c6cf32a06..0fd657b98c 100644 --- a/acceptance/openstack/imageservice/v2/images_test.go +++ b/acceptance/openstack/imageservice/v2/images_test.go @@ -138,3 +138,45 @@ func TestImagesFilter(t *testing.T) { t.Fatalf("Query resulted in no results") } } + +func TestImagesUpdate(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + th.AssertNoErr(t, err) + + image, err := CreateEmptyImage(t, client) + th.AssertNoErr(t, err) + defer DeleteImage(t, client, image) + + newTags := []string{"foo", "bar"} + + updateOpts := images.UpdateOpts{ + images.ReplaceImageName{NewName: image.Name + "foo"}, + images.ReplaceImageTags{NewTags: newTags}, + images.UpdateImageProperty{ + Op: images.AddOp, + Name: "hw_disk_bus", + Value: "scsi", + }, + images.UpdateImageProperty{ + Op: images.RemoveOp, + Name: "architecture", + }, + } + + newImage, err := images.Update(client, image.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newImage) + tools.PrintResource(t, newImage.Properties) + + th.AssertEquals(t, newImage.Name, image.Name+"foo") + th.AssertDeepEquals(t, newImage.Tags, newTags) + + // Because OpenStack is now adding additional properties automatically, + // it's not possible to do an easy AssertDeepEquals. + th.AssertEquals(t, newImage.Properties["hw_disk_bus"], "scsi") + + if _, ok := newImage.Properties["architecture"]; ok { + t.Fatal("architecture property still exists") + } +} diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index c2cbfb3276..16290d395a 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -271,11 +271,11 @@ type UpdateVisibility struct { } // ToImagePatchMap assembles a request body based on UpdateVisibility. -func (u UpdateVisibility) ToImagePatchMap() map[string]interface{} { +func (r UpdateVisibility) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/visibility", - "value": u.Visibility, + "value": r.Visibility, } } @@ -299,11 +299,11 @@ type ReplaceImageChecksum struct { } // ReplaceImageChecksum assembles a request body based on ReplaceImageChecksum. -func (rc ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { +func (r ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/checksum", - "value": rc.Checksum, + "value": r.Checksum, } } @@ -320,3 +320,33 @@ func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { "value": r.NewTags, } } + +// UpdateOp represents a valid update operation. +type UpdateOp string + +const ( + AddOp UpdateOp = "add" + ReplaceOp UpdateOp = "replace" + RemoveOp UpdateOp = "remove" +) + +// UpdateImageProperty represents an update property request. +type UpdateImageProperty struct { + Op UpdateOp + Name string + Value string +} + +// ToImagePatchMap assembles a request body based on UpdateImageProperty. +func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { + updateMap := map[string]interface{}{ + "op": r.Op, + "path": fmt.Sprintf("/%s", r.Name), + } + + if r.Value != "" { + updateMap["value"] = r.Value + } + + return updateMap +} diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index ddfe0438ed..413ae6bc4e 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -388,3 +388,60 @@ func HandleImageListByTagsSuccessfully(t *testing.T) { }`) }) } + +// HandleImageUpdatePropertiesSuccessfully setup +func HandleImageUpdatePropertiesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + th.TestJSONRequest(t, r, `[ + { + "op": "add", + "path": "/hw_disk_bus", + "value": "scsi" + }, + { + "op": "add", + "path": "/hw_disk_bus_model", + "value": "virtio-scsi" + }, + { + "op": "add", + "path": "/hw_scsi_model", + "value": "virtio-scsi" + } + ]`) + + th.AssertEquals(t, "application/openstack-images-v2.1-json-patch", r.Header.Get("Content-Type")) + + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, `{ + "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + "name": "Fedora 17", + "status": "active", + "visibility": "public", + "size": 2254249, + "checksum": "2cec138d7dae2aa59038ef8c9aec2390", + "tags": [ + "fedora", + "beefy" + ], + "created_at": "2012-08-10T19:23:50Z", + "updated_at": "2012-08-12T11:11:33Z", + "self": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", + "schema": "/v2/schemas/image", + "owner": "", + "min_ram": 0, + "min_disk": 0, + "disk_format": "", + "virtual_size": 0, + "container_format": "", + "hw_disk_bus": "scsi", + "hw_disk_bus_model": "virtio-scsi", + "hw_scsi_model": "virtio-scsi" + }`) + }) +} diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index ac71db86d6..ed9a344f49 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -388,3 +388,71 @@ func TestImageListByTags(t *testing.T) { th.AssertDeepEquals(t, expectedImage, allImages[0]) } + +func TestUpdateImageProperties(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleImageUpdatePropertiesSuccessfully(t) + + actualImage, err := images.Update(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", images.UpdateOpts{ + images.UpdateImageProperty{ + Op: images.AddOp, + Name: "hw_disk_bus", + Value: "scsi", + }, + images.UpdateImageProperty{ + Op: images.AddOp, + Name: "hw_disk_bus_model", + Value: "virtio-scsi", + }, + images.UpdateImageProperty{ + Op: images.AddOp, + Name: "hw_scsi_model", + Value: "virtio-scsi", + }, + }).Extract() + + th.AssertNoErr(t, err) + + sizebytes := int64(2254249) + checksum := "2cec138d7dae2aa59038ef8c9aec2390" + file := actualImage.File + createdDate := actualImage.CreatedAt + lastUpdate := actualImage.UpdatedAt + schema := "/v2/schemas/image" + + expectedImage := images.Image{ + ID: "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + Name: "Fedora 17", + Status: images.ImageStatusActive, + Visibility: images.ImageVisibilityPublic, + + SizeBytes: sizebytes, + Checksum: checksum, + + Tags: []string{ + "fedora", + "beefy", + }, + + Owner: "", + MinRAMMegabytes: 0, + MinDiskGigabytes: 0, + + DiskFormat: "", + ContainerFormat: "", + File: file, + CreatedAt: createdDate, + UpdatedAt: lastUpdate, + Schema: schema, + VirtualSize: 0, + Properties: map[string]interface{}{ + "hw_disk_bus": "scsi", + "hw_disk_bus_model": "virtio-scsi", + "hw_scsi_model": "virtio-scsi", + }, + } + + th.AssertDeepEquals(t, &expectedImage, actualImage) +} From 3e2792714ede2bf33a19a18ca0726285965c7b3c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 21 Aug 2018 04:18:33 +0000 Subject: [PATCH 0511/2296] Acc Tests: Skip role filtering when testing on master --- .../openstack/identity/v3/roles_test.go | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 52867f66f6..c9a395f832 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -89,17 +89,55 @@ func TestRolesCRUD(t *testing.T) { th.AssertEquals(t, found, true) + updateOpts := roles.UpdateOpts{ + Extra: map[string]interface{}{ + "description": "updated test role description", + }, + } + + newRole, err := roles.Update(client, role.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newRole) + tools.PrintResource(t, newRole.Extra) + + th.AssertEquals(t, newRole.Extra["description"], "updated test role description") +} + +func TestRolesFilterList(t *testing.T) { + clients.RequireAdmin(t) + + // For some reason this is not longer working. + // It might be a temporary issue. + clients.SkipRelease(t, "master") + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + createOpts := roles.CreateOpts{ + Name: "testrole", + Extra: map[string]interface{}{ + "description": "test role description", + }, + } + + // Create Role in the default domain + role, err := CreateRole(t, client, &createOpts) + th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) + + var listOpts roles.ListOpts listOpts.Filters = map[string]string{ "name__contains": "TEST", } - allPages, err = roles.List(client, listOpts).AllPages() + allPages, err := roles.List(client, listOpts).AllPages() th.AssertNoErr(t, err) - allRoles, err = roles.ExtractRoles(allPages) + allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) - found = false + found := false for _, r := range allRoles { tools.PrintResource(t, r) tools.PrintResource(t, r.Extra) @@ -112,7 +150,7 @@ func TestRolesCRUD(t *testing.T) { th.AssertEquals(t, found, true) listOpts.Filters = map[string]string{ - "name__contains": "foo", + "name__contains": "reader", } allPages, err = roles.List(client, listOpts).AllPages() @@ -132,20 +170,6 @@ func TestRolesCRUD(t *testing.T) { } th.AssertEquals(t, found, false) - - updateOpts := roles.UpdateOpts{ - Extra: map[string]interface{}{ - "description": "updated test role description", - }, - } - - newRole, err := roles.Update(client, role.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newRole) - tools.PrintResource(t, newRole.Extra) - - th.AssertEquals(t, newRole.Extra["description"], "updated test role description") } func TestRoleListAssignmentForUserOnProject(t *testing.T) { From 3523240b32bd0f156a4587cce2101d476c593906 Mon Sep 17 00:00:00 2001 From: Vadim ponomarev Date: Wed, 22 Aug 2018 22:38:00 +0300 Subject: [PATCH 0512/2296] Fix loadbalancer's status request Change GetStatuses method to use "/status" instead alias "/statuses" when get information from Octavia's API (see: https://review.openstack.org/#/c/560179/). --- openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go | 2 +- openstack/loadbalancer/v2/loadbalancers/urls.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index d042f07344..139d19dbf0 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -255,7 +255,7 @@ func HandleLoadbalancerGetSuccessfully(t *testing.T) { // HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request. func HandleLoadbalancerGetStatusesTree(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/statuses", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/status", 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") diff --git a/openstack/loadbalancer/v2/loadbalancers/urls.go b/openstack/loadbalancer/v2/loadbalancers/urls.go index 73cf5dc126..c33c177f68 100644 --- a/openstack/loadbalancer/v2/loadbalancers/urls.go +++ b/openstack/loadbalancer/v2/loadbalancers/urls.go @@ -5,7 +5,7 @@ import "github.com/gophercloud/gophercloud" const ( rootPath = "lbaas" resourcePath = "loadbalancers" - statusPath = "statuses" + statusPath = "status" ) func rootURL(c *gophercloud.ServiceClient) string { From df49d562e0bafbfbcdee13b5c786d0437a5cc75d Mon Sep 17 00:00:00 2001 From: Vadim ponomarev Date: Wed, 22 Aug 2018 22:52:55 +0300 Subject: [PATCH 0513/2296] Fix test TestGetLoadbalancerStatusesTree Fix test TestGetLoadbalancerStatusesTree for check real request body instead part of structure. --- .../v2/loadbalancers/testing/fixtures.go | 50 ++++++++++--------- .../v2/loadbalancers/testing/requests_test.go | 2 +- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index d042f07344..5f67895a9b 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -90,8 +90,8 @@ const PostUpdateLoadbalancerBody = ` } ` -// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer. -const LoadbalancerStatuesesTree = ` +// GetLoadbalancerStatusesBody is the canned request body of a Get request on loadbalancer's status. +const GetLoadbalancerStatusesBody = ` { "statuses" : { "loadbalancer": { @@ -169,33 +169,35 @@ var ( ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", } - LoadbalancerStatusesTree = loadbalancers.LoadBalancer{ - ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - Name: "db_lb", - ProvisioningStatus: "PENDING_UPDATE", - OperatingStatus: "ACTIVE", - Listeners: []listeners.Listener{{ - ID: "db902c0c-d5ff-4753-b465-668ad9656918", - Name: "db", - ProvisioningStatus: "ACTIVE", - Pools: []pools.Pool{{ - ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + LoadbalancerStatusesTree = loadbalancers.StatusTree{ + Loadbalancer: &loadbalancers.LoadBalancer{ + ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + Name: "db_lb", + ProvisioningStatus: "PENDING_UPDATE", + OperatingStatus: "ACTIVE", + Listeners: []listeners.Listener{{ + ID: "db902c0c-d5ff-4753-b465-668ad9656918", Name: "db", ProvisioningStatus: "ACTIVE", - Monitor: monitors.Monitor{ - ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", - Type: "PING", - ProvisioningStatus: "ACTIVE", - }, - Members: []pools.Member{{ - ID: "2a280670-c202-4b0b-a562-34077415aabf", + Pools: []pools.Pool{{ + ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", Name: "db", - Address: "10.0.2.11", - ProtocolPort: 80, ProvisioningStatus: "ACTIVE", + Monitor: monitors.Monitor{ + ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", + Type: "PING", + ProvisioningStatus: "ACTIVE", + }, + Members: []pools.Member{{ + ID: "2a280670-c202-4b0b-a562-34077415aabf", + Name: "db", + Address: "10.0.2.11", + ProtocolPort: 80, + ProvisioningStatus: "ACTIVE", + }}, }}, }}, - }}, + }, } ) @@ -260,7 +262,7 @@ func HandleLoadbalancerGetStatusesTree(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, LoadbalancerStatuesesTree) + fmt.Fprintf(w, GetLoadbalancerStatusesBody) }) } diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 51374e0460..c3f3a80a04 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -115,7 +115,7 @@ func TestGetLoadbalancerStatusesTree(t *testing.T) { t.Fatalf("Unexpected Get error: %v", err) } - th.CheckDeepEquals(t, LoadbalancerStatusesTree, *(actual.Loadbalancer)) + th.CheckDeepEquals(t, LoadbalancerStatusesTree, *actual) } func TestDeleteLoadbalancer(t *testing.T) { From bb6e68e3c0d128bafa41ed9cad2c9c929286a24b Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 23 Aug 2018 18:51:52 -0700 Subject: [PATCH 0514/2296] Magnum: Manage Clusters Create (#1178) * magnum-clusters-create * Refactored CreateResult.Extract() by extracting UUID struct within the function Fixed missing docker_volume_size Format change in fixtures for ClusterCreateResponse Added missing fields for cluster struct --- acceptance/clients/clients.go | 5 ++ .../containerinfra/v1/clusters_test.go | 23 +++++++ .../containerinfra/v1/containerinfra.go | 52 +++++++++++++++ openstack/containerinfra/v1/clusters/doc.go | 28 ++++++++ .../containerinfra/v1/clusters/requests.go | 51 ++++++++++++++ .../containerinfra/v1/clusters/results.go | 53 +++++++++++++++ .../containerinfra/v1/clusters/testing/doc.go | 1 + .../v1/clusters/testing/fixtures.go | 66 +++++++++++++++++++ .../v1/clusters/testing/requests_test.go | 45 +++++++++++++ openstack/containerinfra/v1/clusters/urls.go | 15 +++++ 10 files changed, 339 insertions(+) create mode 100644 acceptance/openstack/containerinfra/v1/clusters_test.go create mode 100644 openstack/containerinfra/v1/clusters/doc.go create mode 100644 openstack/containerinfra/v1/clusters/requests.go create mode 100644 openstack/containerinfra/v1/clusters/results.go create mode 100644 openstack/containerinfra/v1/clusters/testing/doc.go create mode 100644 openstack/containerinfra/v1/clusters/testing/fixtures.go create mode 100644 openstack/containerinfra/v1/clusters/testing/requests_test.go create mode 100644 openstack/containerinfra/v1/clusters/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index e1458ebd13..dc77708bb9 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -29,6 +29,9 @@ type AcceptanceTestChoices struct { // FloatingIPPool contains the name of the pool from where to obtain floating IPs. FloatingIPPoolName string + // MagnumKeypair contains the ID of a valid key pair. + MagnumKeypair string + // MagnumImageID contains the ID of a valid magnum image. MagnumImageID string @@ -55,6 +58,7 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { flavorID := os.Getenv("OS_FLAVOR_ID") flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE") magnumImageID := os.Getenv("OS_MAGNUM_IMAGE_ID") + magnumKeypair := os.Getenv("OS_MAGNUM_KEYPAIR") networkName := os.Getenv("OS_NETWORK_NAME") floatingIPPoolName := os.Getenv("OS_POOL_NAME") externalNetworkID := os.Getenv("OS_EXTGW_ID") @@ -107,6 +111,7 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { FlavorIDResize: flavorIDResize, FloatingIPPoolName: floatingIPPoolName, MagnumImageID: magnumImageID, + MagnumKeypair: magnumKeypair, NetworkName: networkName, ExternalNetworkID: externalNetworkID, ShareNetworkID: shareNetworkID, diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go new file mode 100644 index 0000000000..e0affb451a --- /dev/null +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -0,0 +1,23 @@ +// +build acceptance containerinfra + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestClustersCRUD(t *testing.T) { + client, err := clients.NewContainerInfraV1Client() + th.AssertNoErr(t, err) + + clusterTemplate, err := CreateClusterTemplate(t, client) + th.AssertNoErr(t, err) + defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) + + clusterID, err := CreateCluster(t, client, clusterTemplate.UUID) + tools.PrintResource(t, clusterID) +} diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go index 22d852fc5f..868347276f 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -79,3 +80,54 @@ func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id s return } + +// CreateCluster will create a random cluster. An error will be returned if the +// cluster could not be created. +func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string) (string, error) { + clusterName := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create cluster: %s using template %s", clusterName, clusterTemplateID) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + return "", err + } + + masterCount := 1 + nodeCount := 1 + createTimeout := 100 + createOpts := clusters.CreateOpts{ + ClusterTemplateID: clusterTemplateID, + CreateTimeout: &createTimeout, + FlavorID: choices.FlavorID, + Keypair: choices.MagnumKeypair, + Labels: map[string]string{}, + MasterCount: &masterCount, + MasterFlavorID: choices.FlavorID, + Name: clusterName, + NodeCount: &nodeCount, + } + + createResult := clusters.Create(client, createOpts) + th.AssertNoErr(t, createResult.Err) + if len(createResult.Header["X-Openstack-Request-Id"]) > 0 { + t.Logf("Cluster Create Request ID: %s", createResult.Header["X-Openstack-Request-Id"][0]) + } + + clusterID, err := createResult.Extract() + if err != nil { + return "", err + } + + t.Logf("Cluster created: %+v", clusterID) + + // TODO: Uncomment this later when GET is merged in + /* + err = WaitForCluster(client, clusterID, "CREATE_COMPLETE") + if err != nil { + return clusterID, err + } + */ + + t.Logf("Successfully created cluster: %s id: %s", clusterName, clusterID) + return clusterID, nil +} diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go new file mode 100644 index 0000000000..f8f4ef9703 --- /dev/null +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -0,0 +1,28 @@ +/* +Package clusters contains functionality for working with Magnum Cluster resources. + +Example to Create a Cluster + + masterCount := 1 + nodeCount := 1 + createTimeout := 30 + opts := clusters.CreateOpts{ + ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", + CreateTimeout: &createTimeout, + DiscoveryURL: "", + FlavorID: "m1.small", + KeyPair: "my_keypair", + Labels: map[string]string{}, + MasterCount: &masterCount, + MasterFlavorID: "m1.small", + Name: "k8s", + NodeCount: &nodeCount, + } + + cluster, err := clusters.Create(serviceClient, createOpts).Extract() + if err != nil { + panic(err) + } + +*/ +package clusters diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go new file mode 100644 index 0000000000..a8493b842b --- /dev/null +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -0,0 +1,51 @@ +package clusters + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder Builder. +type CreateOptsBuilder interface { + ToClusterCreateMap() (map[string]interface{}, error) +} + +// CreateOpts params +type CreateOpts struct { + ClusterTemplateID string `json:"cluster_template_id" required:"true"` + CreateTimeout *int `json:"create_timeout"` + DiscoveryURL string `json:"discovery_url,omitempty"` + DockerVolumeSize *int `json:"docker_volume_size,omitempty"` + FlavorID string `json:"flavor_id,omitempty"` + Keypair string `json:"keypair,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + MasterCount *int `json:"master_count,omitempty"` + MasterFlavorID string `json:"master_flavor_id,omitempty"` + Name string `json:"name"` + NodeCount *int `json:"node_count,omitempty"` +} + +// ToClusterCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create requests the creation of a new cluster. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToClusterCreateMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go new file mode 100644 index 0000000000..a1696e902b --- /dev/null +++ b/openstack/containerinfra/v1/clusters/results.go @@ -0,0 +1,53 @@ +package clusters + +import ( + "time" + + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Create operations. +type CreateResult struct { + commonResult +} + +func (r CreateResult) Extract() (clusterID string, err error) { + var s struct { + UUID string + } + err = r.ExtractInto(&s) + return s.UUID, err +} + +type Cluster struct { + APIAddress string `json:"api_address"` + COEVersion string `json:"coe_version"` + ClusterTemplateID string `json:"cluster_template_id"` + ContainerVersion string `json:"container_version"` + CreateTimeout int `json:"create_timeout"` + CreatedAt time.Time `json:"created_at"` + DiscoveryURL string `json:"discovery_url"` + DockerVolumeSize int `json:"docker_volume_size"` + Faults map[string]string `json:"faults"` + FlavorID string `json:"flavor_id"` + KeyPair string `json:"keypair"` + Labels map[string]string `json:"labels"` + Links []gophercloud.Link `json:"links"` + MasterFlavorID string `json:"master_flavor_id"` + MasterAddresses []string `json:"master_addresses"` + MasterCount int `json:"master_count"` + Name string `json:"name"` + NodeAddresses []string `json:"node_addresses"` + NodeCount int `json:"node_count"` + ProjectID string `json:"project_id"` + StackID string `json:"stack_id"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + UUID string `json:"uuid"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` +} diff --git a/openstack/containerinfra/v1/clusters/testing/doc.go b/openstack/containerinfra/v1/clusters/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/containerinfra/v1/clusters/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go new file mode 100644 index 0000000000..1b3ccf4b7b --- /dev/null +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -0,0 +1,66 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const clusterUUID = "746e779a-751a-456b-a3e9-c883d734946f" +const requestUUID = "req-781e9bdc-4163-46eb-91c9-786c53188bbb" + +var ClusterCreateResponse = fmt.Sprintf(` + { + "uuid":"%s" + }`, clusterUUID) + +var ExpectedCluster = clusters.Cluster{ + APIAddress: "https://172.24.4.6:6443", + COEVersion: "v1.2.0", + ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", + CreateTimeout: 60, + CreatedAt: time.Date(2016, 8, 29, 6, 51, 31, 0, time.UTC), + DiscoveryURL: "https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", + Links: []gophercloud.Link{ + { + Href: "http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", + Rel: "self", + }, + { + Href: "http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", + Rel: "bookmark", + }, + }, + KeyPair: "my-keypair", + MasterAddresses: []string{"172.24.4.6"}, + MasterCount: 1, + Name: "k8s", + NodeAddresses: []string{"172.24.4.13"}, + NodeCount: 1, + StackID: "9c6f1169-7300-4d08-a444-d2be38758719", + Status: "CREATE_COMPLETE", + StatusReason: "Stack CREATE completed successfully", + UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), + UUID: clusterUUID, +} + +var ExpectedClusterUUID = clusterUUID + +func HandleCreateClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-Id", requestUUID) + w.WriteHeader(http.StatusAccepted) + + fmt.Fprint(w, ClusterCreateResponse) + }) +} diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go new file mode 100644 index 0000000000..2b0ec00fff --- /dev/null +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -0,0 +1,45 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCreateClusterSuccessfully(t) + + masterCount := 1 + nodeCount := 1 + createTimeout := 30 + opts := clusters.CreateOpts{ + ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", + CreateTimeout: &createTimeout, + DiscoveryURL: "", + FlavorID: "m1.small", + Keypair: "my_keypair", + Labels: map[string]string{}, + MasterCount: &masterCount, + MasterFlavorID: "m1.small", + Name: "k8s", + NodeCount: &nodeCount, + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + res := clusters.Create(sc, opts) + th.AssertNoErr(t, res.Err) + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, requestUUID, requestID) + + actual, err := res.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, clusterUUID, actual) +} diff --git a/openstack/containerinfra/v1/clusters/urls.go b/openstack/containerinfra/v1/clusters/urls.go new file mode 100644 index 0000000000..859cedf5bb --- /dev/null +++ b/openstack/containerinfra/v1/clusters/urls.go @@ -0,0 +1,15 @@ +package clusters + +import ( + "github.com/gophercloud/gophercloud" +) + +var apiName = "clusters" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiName) +} + +func createURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 3bb4676e61b8eda86b9d50ab2906708b78017115 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Fri, 24 Aug 2018 04:02:31 +0200 Subject: [PATCH 0515/2296] Set reauth func to nil before making a new request (#1192) * Set reauth func to nil before making a new request * Add tests --- provider_client.go | 4 ++++ testing/provider_client_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/provider_client.go b/provider_client.go index 77368f0140..95fa11a788 100644 --- a/provider_client.go +++ b/provider_client.go @@ -295,7 +295,11 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) seeker.Seek(0, 0) } } + // make a new call to request with a nil reauth func in order to avoid infinite loop + reauthFunc := client.ReauthFunc + client.ReauthFunc = nil resp, err = client.Request(method, url, options) + client.ReauthFunc = reauthFunc if err != nil { switch err.(type) { case *ErrUnexpectedResponseCode: diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 15385beb0b..1c6da1b927 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "net/http" + "reflect" "sync" "testing" "time" @@ -122,3 +123,34 @@ func TestConcurrentReauth(t *testing.T) { th.AssertEquals(t, 1, info.numreauths) } + +func TestReauthEndLoop(t *testing.T) { + + p := new(gophercloud.ProviderClient) + p.UseTokenLock() + p.SetToken(client.TokenID) + p.ReauthFunc = func() error { + // Reauth func is working and returns no error + return nil + } + + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + // route always return 401 + w.WriteHeader(http.StatusUnauthorized) + return + }) + + reqopts := new(gophercloud.RequestOpts) + _, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) + if err == nil { + t.Errorf("request ends with a nil error") + return + } + + if reflect.TypeOf(err) != reflect.TypeOf(&gophercloud.ErrErrorAfterReauthentication{}) { + t.Errorf("error is not an ErrErrorAfterReauthentication") + } +} From 74b837606f70a1037e0d6f69c54fa2d671d3be6c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 24 Aug 2018 09:29:17 -0600 Subject: [PATCH 0516/2296] go vet fixes (#1198) --- openstack/clustering/v1/policies/results.go | 2 +- openstack/clustering/v1/profiles/results.go | 2 +- openstack/compute/v2/extensions/secgroups/results.go | 2 +- openstack/compute/v2/flavors/results.go | 2 +- openstack/db/v1/configurations/results.go | 4 ++-- openstack/imageservice/v2/images/results.go | 5 +++-- openstack/objectstorage/v1/objects/results.go | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index 592a7e5575..46c88da9b8 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -65,7 +65,7 @@ type Spec struct { Description string `json:"description"` Properties map[string]interface{} `json:"properties"` Type string `json:"type"` - Version string `json:"version"` + Version string `json:"-"` } func (r *Spec) UnmarshalJSON(b []byte) error { diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index 5b457d563c..30bacadfee 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -58,7 +58,7 @@ func (r *Profile) UnmarshalJSON(b []byte) error { // Spec represents a profile spec. type Spec struct { Type string `json:"type"` - Version string `json:"version"` + Version string `json:"-"` Properties map[string]interface{} `json:"properties"` } diff --git a/openstack/compute/v2/extensions/secgroups/results.go b/openstack/compute/v2/extensions/secgroups/results.go index cf08547e90..0468892206 100644 --- a/openstack/compute/v2/extensions/secgroups/results.go +++ b/openstack/compute/v2/extensions/secgroups/results.go @@ -72,7 +72,7 @@ type Rule struct { IPRange IPRange `json:"ip_range"` // The security group ID to which this rule belongs. - ParentGroupID string `json:"parent_group_id"` + ParentGroupID string `json:"-"` // Not documented. Group Group diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 525cddaea2..92fe1b1809 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -59,7 +59,7 @@ type Flavor struct { RxTxFactor float64 `json:"rxtx_factor"` // Swap is the amount of swap space, measured in MB. - Swap int `json:"swap"` + Swap int `json:"-"` // VCPUs indicates how many (virtual) CPUs are available for this flavor. VCPUs int `json:"vcpus"` diff --git a/openstack/db/v1/configurations/results.go b/openstack/db/v1/configurations/results.go index 3516330249..13bcbffe8b 100644 --- a/openstack/db/v1/configurations/results.go +++ b/openstack/db/v1/configurations/results.go @@ -10,8 +10,8 @@ import ( // Config represents a configuration group API resource. type Config struct { - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` + Created time.Time `json:"-"` + Updated time.Time `json:"-"` DatastoreName string `json:"datastore_name"` DatastoreVersionID string `json:"datastore_version_id"` DatastoreVersionName string `json:"datastore_version_name"` diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index dbc3c452fa..676181e1f4 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -57,7 +57,7 @@ type Image struct { Checksum string `json:"checksum"` // SizeBytes is the size of the data that's associated with the image. - SizeBytes int64 `json:"size"` + SizeBytes int64 `json:"-"` // Metadata is a set of metadata associated with the image. // Image metadata allow for meaningfully define the image properties @@ -67,7 +67,7 @@ type Image struct { // Properties is a set of key-value pairs, if any, that are associated with // the image. - Properties map[string]interface{} `json:"-"` + Properties map[string]interface{} // CreatedAt is the date when the image has been created. CreatedAt time.Time `json:"created_at"` @@ -119,6 +119,7 @@ func (r *Image) UnmarshalJSON(b []byte) error { } if resultMap, ok := result.(map[string]interface{}); ok { delete(resultMap, "self") + delete(resultMap, "size") r.Properties = internal.RemainingKeys(Image{}, resultMap) } diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 49a0abcdb6..7e47593b19 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -411,7 +411,7 @@ 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"` + ContentLength int64 `json:"-"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` From a498b89088a54cd586baec45e86dceb1308a2158 Mon Sep 17 00:00:00 2001 From: Vadim Ponomarev Date: Sat, 25 Aug 2018 05:26:02 +0300 Subject: [PATCH 0517/2296] Add support to getting statistics for loadbalancer and listener (#1190) * Add methods for getting statistics for loadbalancer and listener. - Add GetStats method for a loadbalancer (see: https://developer.openstack.org/api-ref/load-balancer/v2/#get-load-balancer-statistics); - Add GetStats method for a listener (see: https://developer.openstack.org/api-ref/load-balancer/v2/#get-listener-statistics); * Rename structures. Add acceptance tests. - Rename structures StatsTree to Stats - Rename GetStatsResult to StatsResult - Add acceptance tests for loadbalancer's and listener's statistics * Add examples of get statistics to the documentation --- .../loadbalancer/v2/loadbalancers_test.go | 14 ++++++++ openstack/loadbalancer/v2/listeners/doc.go | 8 +++++ .../loadbalancer/v2/listeners/requests.go | 6 ++++ .../loadbalancer/v2/listeners/results.go | 33 +++++++++++++++++++ .../v2/listeners/testing/fixtures.go | 31 +++++++++++++++++ .../v2/listeners/testing/requests_test.go | 14 ++++++++ openstack/loadbalancer/v2/listeners/urls.go | 9 +++-- .../loadbalancer/v2/loadbalancers/doc.go | 8 +++++ .../loadbalancer/v2/loadbalancers/requests.go | 6 ++++ .../loadbalancer/v2/loadbalancers/results.go | 33 +++++++++++++++++++ .../v2/loadbalancers/testing/fixtures.go | 31 +++++++++++++++++ .../v2/loadbalancers/testing/requests_test.go | 14 ++++++++ .../loadbalancer/v2/loadbalancers/urls.go | 11 +++++-- 13 files changed, 213 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 6fd0f5f0a5..1599697a64 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -72,6 +72,13 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newLB) + lbStats, err := loadbalancers.GetStats(lbClient, lb.ID).Extract() + if err != nil { + t.Fatalf("Unable to get loadbalancer's statistics: %v", err) + } + + tools.PrintResource(t, lbStats) + // Because of the time it takes to create a loadbalancer, // this test will include some other resources. @@ -101,6 +108,13 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newListener) + listenerStats, err := listeners.GetStats(lbClient, listener.ID).Extract() + if err != nil { + t.Fatalf("Unable to get listener's statistics: %v", err) + } + + tools.PrintResource(t, listenerStats) + // L7 policy policy, err := CreateL7Policy(t, lbClient, listener, lb) if err != nil { diff --git a/openstack/loadbalancer/v2/listeners/doc.go b/openstack/loadbalancer/v2/listeners/doc.go index 108cdb03d8..4ccb967755 100644 --- a/openstack/loadbalancer/v2/listeners/doc.go +++ b/openstack/loadbalancer/v2/listeners/doc.go @@ -59,5 +59,13 @@ Example to Delete a Listener if err != nil { panic(err) } + +Example to Get the Statistics of a Listener + + listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" + stats, err := listeners.GetStats(networkClient, listenerID).Extract() + if err != nil { + panic(err) + } */ package listeners diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 5662ef8a11..ac587e258d 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -192,3 +192,9 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } + +// GetStats will return the shows the current statistics of a particular Listeners. +func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { + _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 3e98423270..c3e7129157 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -60,6 +60,23 @@ type Listener struct { ProvisioningStatus string `json:"provisioning_status"` } +type Stats struct { + // The currently active connections. + ActiveConnections int `json:"active_connections"` + + // The total bytes received. + BytesIn int `json:"bytes_in"` + + // The total bytes sent. + BytesOut int `json:"bytes_out"` + + // The total requests that were unable to be fulfilled. + RequestErrors int `json:"request_errors"` + + // The total connections handled. + TotalConnections int `json:"total_connections"` +} + // ListenerPage is the page returned by a pager when traversing over a // collection of listeners. type ListenerPage struct { @@ -133,3 +150,19 @@ type UpdateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// StatsResult represents the result of a GetStats operation. +// Call its Extract method to interpret it as a Stats. +type StatsResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts the status of +// a Listener. +func (r StatsResult) Extract() (*Stats, error) { + var s struct { + Stats *Stats `json:"stats"` + } + err := r.ExtractInto(&s) + return s.Stats, err +} diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index 07faa8d49a..966bcc455f 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -85,6 +85,19 @@ const PostUpdateListenerBody = ` } ` +// GetListenerStatsBody is the canned request body of a Get request on listener's statistics. +const GetListenerStatsBody = ` +{ + "stats": { + "active_connections": 0, + "bytes_in": 9532, + "bytes_out": 22033, + "request_errors": 46, + "total_connections": 112 + } +} +` + var ( ListenerWeb = listeners.Listener{ ID: "db902c0c-d5ff-4753-b465-668ad9656918", @@ -127,6 +140,13 @@ var ( DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, } + ListenerStatsTree = listeners.Stats{ + ActiveConnections: 0, + BytesIn: 9532, + BytesOut: 22033, + RequestErrors: 46, + TotalConnections: 112, + } ) // HandleListenerListSuccessfully sets up the test server to respond to a listener List request. @@ -211,3 +231,14 @@ func HandleListenerUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, PostUpdateListenerBody) }) } + +// HandleListenerGetStatsTree sets up the test server to respond to a listener Get stats tree request. +func HandleListenerGetStatsTree(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304/stats", 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, GetListenerStatsBody) + }) +} diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 4123b305ce..f403fb8e9f 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -135,3 +135,17 @@ func TestUpdateListener(t *testing.T) { th.CheckDeepEquals(t, ListenerUpdated, *actual) } + +func TestGetListenerStatsTree(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListenerGetStatsTree(t) + + client := fake.ServiceClient() + actual, err := listeners.GetStats(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, ListenerStatsTree, *actual) +} diff --git a/openstack/loadbalancer/v2/listeners/urls.go b/openstack/loadbalancer/v2/listeners/urls.go index 02fb1eb39e..e9e3bccd3e 100644 --- a/openstack/loadbalancer/v2/listeners/urls.go +++ b/openstack/loadbalancer/v2/listeners/urls.go @@ -3,8 +3,9 @@ package listeners import "github.com/gophercloud/gophercloud" const ( - rootPath = "lbaas" - resourcePath = "listeners" + rootPath = "lbaas" + resourcePath = "listeners" + statisticsPath = "stats" ) func rootURL(c *gophercloud.ServiceClient) string { @@ -14,3 +15,7 @@ func rootURL(c *gophercloud.ServiceClient) string { func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } + +func statisticsRootURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id, statisticsPath) +} diff --git a/openstack/loadbalancer/v2/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go index b0a20b8fab..244e19882a 100644 --- a/openstack/loadbalancer/v2/loadbalancers/doc.go +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -72,5 +72,13 @@ Example to Get the Status of a Load Balancer if err != nil { panic(err) } + +Example to Get the Statistics of a Load Balancer + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + stats, err := loadbalancers.GetStats(networkClient, LBID).Extract() + if err != nil { + panic(err) + } */ package loadbalancers diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 0a62f59bc5..99b60fb458 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -203,3 +203,9 @@ func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) return } + +// GetStats will return the shows the current statistics of a particular LoadBalancer. +func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { + _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 62d1b668b9..69e8a8b701 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -58,6 +58,23 @@ type StatusTree struct { Loadbalancer *LoadBalancer `json:"loadbalancer"` } +type Stats struct { + // The currently active connections. + ActiveConnections int `json:"active_connections"` + + // The total bytes received. + BytesIn int `json:"bytes_in"` + + // The total bytes sent. + BytesOut int `json:"bytes_out"` + + // The total requests that were unable to be fulfilled. + RequestErrors int `json:"request_errors"` + + // The total connections handled. + TotalConnections int `json:"total_connections"` +} + // LoadBalancerPage is the page returned by a pager when traversing over a // collection of load balancers. type LoadBalancerPage struct { @@ -124,6 +141,22 @@ func (r GetStatusesResult) Extract() (*StatusTree, error) { return s.Statuses, err } +// StatsResult represents the result of a GetStats operation. +// Call its Extract method to interpret it as a Stats. +type StatsResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts the status of +// a Loadbalancer. +func (r StatsResult) Extract() (*Stats, error) { + var s struct { + Stats *Stats `json:"stats"` + } + err := r.ExtractInto(&s) + return s.Stats, err +} + // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a LoadBalancer. type CreateResult struct { diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index 238261c6fe..f76a6abc0b 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -126,6 +126,19 @@ const GetLoadbalancerStatusesBody = ` } ` +// LoadbalancerStatsTree is the canned request body of a Get request on loadbalancer's statistics. +const GetLoadbalancerStatsBody = ` +{ + "stats": { + "active_connections": 0, + "bytes_in": 9532, + "bytes_out": 22033, + "request_errors": 46, + "total_connections": 112 + } +} +` + var ( LoadbalancerWeb = loadbalancers.LoadBalancer{ ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", @@ -199,6 +212,13 @@ var ( }}, }, } + LoadbalancerStatsTree = loadbalancers.Stats{ + ActiveConnections: 0, + BytesIn: 9532, + BytesOut: 22033, + RequestErrors: 46, + TotalConnections: 112, + } ) // HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request. @@ -292,3 +312,14 @@ func HandleLoadbalancerUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, PostUpdateLoadbalancerBody) }) } + +// HandleLoadbalancerGetStatsTree sets up the test server to respond to a loadbalancer Get stats tree request. +func HandleLoadbalancerGetStatsTree(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/stats", 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, GetLoadbalancerStatsBody) + }) +} diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index c3f3a80a04..acf22f9104 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -160,3 +160,17 @@ func TestCascadingDeleteLoadbalancer(t *testing.T) { err = loadbalancers.Delete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", deleteOpts).ExtractErr() th.AssertNoErr(t, err) } + +func TestGetLoadbalancerStatsTree(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerGetStatsTree(t) + + client := fake.ServiceClient() + actual, err := loadbalancers.GetStats(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, LoadbalancerStatsTree, *actual) +} diff --git a/openstack/loadbalancer/v2/loadbalancers/urls.go b/openstack/loadbalancer/v2/loadbalancers/urls.go index c33c177f68..70267c358d 100644 --- a/openstack/loadbalancer/v2/loadbalancers/urls.go +++ b/openstack/loadbalancer/v2/loadbalancers/urls.go @@ -3,9 +3,10 @@ package loadbalancers import "github.com/gophercloud/gophercloud" const ( - rootPath = "lbaas" - resourcePath = "loadbalancers" - statusPath = "status" + rootPath = "lbaas" + resourcePath = "loadbalancers" + statusPath = "status" + statisticsPath = "stats" ) func rootURL(c *gophercloud.ServiceClient) string { @@ -19,3 +20,7 @@ func resourceURL(c *gophercloud.ServiceClient, id string) string { func statusRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, statusPath) } + +func statisticsRootURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id, statisticsPath) +} From f29afc2cceca860199ee88cd355a4d0a37b3fad2 Mon Sep 17 00:00:00 2001 From: Levi Blackstone Date: Tue, 28 Aug 2018 17:51:45 -0600 Subject: [PATCH 0518/2296] Update objectstorage object unmarshal (#1216) Recent versions of OpenStack (post Newton) now return a different timestamp format for the Last-Modified response header. Update the unmarshal logic to support either format. --- openstack/objectstorage/v1/objects/results.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 7e47593b19..dd7c7044d0 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -25,8 +25,7 @@ type Object struct { // Hash represents the MD5 checksum value of the object's content. Hash string `json:"hash"` - // LastModified is the time the object was last modified, represented - // as a string. + // LastModified is the time the object was last modified. LastModified time.Time `json:"-"` // Name is the unique name for the object. @@ -40,7 +39,7 @@ func (r *Object) UnmarshalJSON(b []byte) error { type tmp Object var s *struct { tmp - LastModified gophercloud.JSONRFC3339MilliNoZ `json:"last_modified"` + LastModified string `json:"last_modified"` } err := json.Unmarshal(b, &s) @@ -50,10 +49,18 @@ func (r *Object) UnmarshalJSON(b []byte) error { *r = Object(s.tmp) - r.LastModified = time.Time(s.LastModified) + if s.LastModified != "" { + t, err := time.Parse(gophercloud.RFC3339MilliNoZ, s.LastModified) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339Milli, s.LastModified) + if err != nil { + return err + } + } + r.LastModified = t + } return nil - } // ObjectPage is a single page of objects that is returned from a call to the From b1f1a3ef06d380ba2ae01636c404ee7dca31f2bc Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Wed, 29 Aug 2018 19:00:01 -0700 Subject: [PATCH 0519/2296] Magnum: Manage Clusters Get/List (#1200) * magnum-clusters-get_list * Removed unused code --- .../containerinfra/v1/clusters_test.go | 15 ++ .../containerinfra/v1/containerinfra.go | 36 +++- openstack/containerinfra/v1/clusters/doc.go | 29 +++ .../containerinfra/v1/clusters/requests.go | 51 ++++++ .../containerinfra/v1/clusters/results.go | 42 +++++ .../v1/clusters/testing/fixtures.go | 165 ++++++++++++++++++ .../v1/clusters/testing/requests_test.go | 43 +++++ openstack/containerinfra/v1/clusters/urls.go | 8 + 8 files changed, 382 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index e0affb451a..fe5cc4c911 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -20,4 +21,18 @@ func TestClustersCRUD(t *testing.T) { clusterID, err := CreateCluster(t, client, clusterTemplate.UUID) tools.PrintResource(t, clusterID) + + allPages, err := clusters.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allClusters, err := clusters.ExtractClusters(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allClusters { + if v.UUID == clusterID { + found = true + } + } + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go index 868347276f..678f030fad 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -1,6 +1,8 @@ package v1 import ( + "fmt" + "strings" "testing" "github.com/gophercloud/gophercloud" @@ -120,14 +122,34 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTempl t.Logf("Cluster created: %+v", clusterID) - // TODO: Uncomment this later when GET is merged in - /* - err = WaitForCluster(client, clusterID, "CREATE_COMPLETE") - if err != nil { - return clusterID, err - } - */ + err = WaitForCluster(client, clusterID, "CREATE_COMPLETE") + if err != nil { + return clusterID, err + } t.Logf("Successfully created cluster: %s id: %s", clusterName, clusterID) return clusterID, nil } + +func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status string) error { + return tools.WaitFor(func() (bool, error) { + cluster, err := clusters.Get(client, clusterID).Extract() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETE_COMPLETE" { + return true, nil + } + + return false, err + } + + if cluster.Status == status { + return true, nil + } + + if strings.Contains(cluster.Status, "FAILED") { + return false, fmt.Errorf("Cluster %s FAILED. Status=%s StatusReason=%s", clusterID, cluster.Status, cluster.StatusReason) + } + + return false, nil + }) +} diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index f8f4ef9703..2a0a2bac5c 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -24,5 +24,34 @@ Example to Create a Cluster panic(err) } +Example to Get a Cluster + + clusterName := "cluster123" + cluster, err := clusters.Get(serviceClient, clusterName).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", cluster) + +Example to List Clusters + + listOpts := clusters.ListOpts{ + Limit: 20, + } + + allPages, err := clusters.List(serviceClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allClusters, err := clusters.ExtractClusters(allPages) + if err != nil { + panic(err) + } + + for _, cluster := range allClusters { + fmt.Printf("%+v\n", cluster) + } + */ package clusters diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index a8493b842b..b1b4ec7ec9 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder Builder. @@ -49,3 +50,53 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } + +// Get retrieves a specific clusters based on its unique ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + var result *http.Response + result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + if r.Err == nil { + r.Header = result.Header + } + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToClustersListQuery() (string, error) +} + +// ListOpts allows the sorting of paginated collections through +// the API. SortKey allows you to sort by a particular cluster attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for pagination. +type ListOpts struct { + Marker string `q:"marker"` + Limit int `q:"limit"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToClustersListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToClustersListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// clusters. It accepts a ListOptsBuilder, which allows you to sort +// the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToClustersListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return ClusterPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index a1696e902b..11e648227a 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -4,6 +4,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { @@ -15,6 +16,11 @@ type CreateResult struct { commonResult } +// GetResult represents the result of a get operation. +type GetResult struct { + commonResult +} + func (r CreateResult) Extract() (clusterID string, err error) { var s struct { UUID string @@ -23,6 +29,13 @@ func (r CreateResult) Extract() (clusterID string, err error) { return s.UUID, err } +// Extract is a function that accepts a result and extracts a cluster resource. +func (r commonResult) Extract() (*Cluster, error) { + var s *Cluster + err := r.ExtractInto(&s) + return s, err +} + type Cluster struct { APIAddress string `json:"api_address"` COEVersion string `json:"coe_version"` @@ -51,3 +64,32 @@ type Cluster struct { UpdatedAt time.Time `json:"updated_at"` UserID string `json:"user_id"` } + +type ClusterPage struct { + pagination.LinkedPageBase +} + +func (r ClusterPage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Next, nil +} + +// IsEmpty checks whether a ClusterPage struct is empty. +func (r ClusterPage) IsEmpty() (bool, error) { + is, err := ExtractClusters(r) + return len(is) == 0, err +} + +func ExtractClusters(r pagination.Page) ([]Cluster, error) { + var s struct { + Clusters []Cluster `json:"clusters"` + } + err := (r.(ClusterPage)).ExtractInto(&s) + return s.Clusters, err +} diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index 1b3ccf4b7b..357b404d58 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -13,6 +13,7 @@ import ( ) const clusterUUID = "746e779a-751a-456b-a3e9-c883d734946f" +const clusterUUID2 = "846e779a-751a-456b-a3e9-c883d734946f" const requestUUID = "req-781e9bdc-4163-46eb-91c9-786c53188bbb" var ClusterCreateResponse = fmt.Sprintf(` @@ -50,6 +51,36 @@ var ExpectedCluster = clusters.Cluster{ UUID: clusterUUID, } +var ExpectedCluster2 = clusters.Cluster{ + APIAddress: "https://172.24.4.6:6443", + COEVersion: "v1.2.0", + ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", + CreateTimeout: 60, + CreatedAt: time.Time{}, + DiscoveryURL: "https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", + Links: []gophercloud.Link{ + { + Href: "http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", + Rel: "self", + }, + { + Href: "http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", + Rel: "bookmark", + }, + }, + KeyPair: "my-keypair", + MasterAddresses: []string{"172.24.4.6"}, + MasterCount: 1, + Name: "k8s", + NodeAddresses: []string{"172.24.4.13"}, + NodeCount: 1, + StackID: "9c6f1169-7300-4d08-a444-d2be38758719", + Status: "CREATE_COMPLETE", + StatusReason: "Stack CREATE completed successfully", + UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), + UUID: clusterUUID2, +} + var ExpectedClusterUUID = clusterUUID func HandleCreateClusterSuccessfully(t *testing.T) { @@ -64,3 +95,137 @@ func HandleCreateClusterSuccessfully(t *testing.T) { fmt.Fprint(w, ClusterCreateResponse) }) } + +func HandleGetClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID, 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, ClusterGetResponse) + }) +} + +var ClusterGetResponse = fmt.Sprintf(` +{ + "status":"CREATE_COMPLETE", + "uuid":"%s", + "links":[ + { + "href":"http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", + "rel":"self" + }, + { + "href":"http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", + "rel":"bookmark" + } + ], + "stack_id":"9c6f1169-7300-4d08-a444-d2be38758719", + "created_at":"2016-08-29T06:51:31+00:00", + "api_address":"https://172.24.4.6:6443", + "discovery_url":"https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", + "updated_at":"2016-08-29T06:53:24+00:00", + "master_count":1, + "coe_version": "v1.2.0", + "keypair":"my-keypair", + "cluster_template_id":"0562d357-8641-4759-8fed-8173f02c9633", + "master_addresses":[ + "172.24.4.6" + ], + "node_count":1, + "node_addresses":[ + "172.24.4.13" + ], + "status_reason":"Stack CREATE completed successfully", + "create_timeout":60, + "name":"k8s" +}`, clusterUUID) + +var ClusterListResponse = fmt.Sprintf(` +{ + "clusters": [ + { + "api_address":"https://172.24.4.6:6443", + "cluster_template_id":"0562d357-8641-4759-8fed-8173f02c9633", + "coe_version": "v1.2.0", + "create_timeout":60, + "created_at":"2016-08-29T06:51:31+00:00", + "discovery_url":"https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", + "keypair":"my-keypair", + "links":[ + { + "href":"http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", + "rel":"self" + }, + { + "href":"http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", + "rel":"bookmark" + } + ], + "master_addresses":[ + "172.24.4.6" + ], + "master_count":1, + "name":"k8s", + "node_addresses":[ + "172.24.4.13" + ], + "node_count":1, + "stack_id":"9c6f1169-7300-4d08-a444-d2be38758719", + "status":"CREATE_COMPLETE", + "status_reason":"Stack CREATE completed successfully", + "updated_at":"2016-08-29T06:53:24+00:00", + "uuid":"%s" + }, + { + "api_address":"https://172.24.4.6:6443", + "cluster_template_id":"0562d357-8641-4759-8fed-8173f02c9633", + "coe_version": "v1.2.0", + "create_timeout":60, + "created_at":null, + "discovery_url":"https://discovery.etcd.io/cbeb580da58915809d59ee69348a84f3", + "keypair":"my-keypair", + "links":[ + { + "href":"http://10.164.180.104:9511/v1/clusters/746e779a-751a-456b-a3e9-c883d734946f", + "rel":"self" + }, + { + "href":"http://10.164.180.104:9511/clusters/746e779a-751a-456b-a3e9-c883d734946f", + "rel":"bookmark" + } + ], + "master_addresses":[ + "172.24.4.6" + ], + "master_count":1, + "name":"k8s", + "node_addresses":[ + "172.24.4.13" + ], + "node_count":1, + "stack_id":"9c6f1169-7300-4d08-a444-d2be38758719", + "status":"CREATE_COMPLETE", + "status_reason":"Stack CREATE completed successfully", + "updated_at":null, + "uuid":"%s" + } + ] +}`, clusterUUID, clusterUUID2) + +var ExpectedClusters = []clusters.Cluster{ExpectedCluster} + +func HandleListClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters", 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.Header().Add("X-OpenStack-Request-Id", requestUUID) + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ClusterListResponse) + }) +} diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index 2b0ec00fff..2fd41ef19c 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -43,3 +44,45 @@ func TestCreateCluster(t *testing.T) { th.AssertDeepEquals(t, clusterUUID, actual) } + +func TestGetCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetClusterSuccessfully(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + actual, err := clusters.Get(sc, "746e779a-751a-456b-a3e9-c883d734946f").Extract() + th.AssertNoErr(t, err) + actual.CreatedAt = actual.CreatedAt.UTC() + actual.UpdatedAt = actual.UpdatedAt.UTC() + th.AssertDeepEquals(t, ExpectedCluster, *actual) +} + +func TestListClusters(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleListClusterSuccessfully(t) + + count := 0 + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + clusters.List(sc, clusters.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := clusters.ExtractClusters(page) + th.AssertNoErr(t, err) + for idx := range actual { + actual[idx].CreatedAt = actual[idx].CreatedAt.UTC() + actual[idx].UpdatedAt = actual[idx].UpdatedAt.UTC() + } + th.AssertDeepEquals(t, ExpectedClusters, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/containerinfra/v1/clusters/urls.go b/openstack/containerinfra/v1/clusters/urls.go index 859cedf5bb..d2a36bdb25 100644 --- a/openstack/containerinfra/v1/clusters/urls.go +++ b/openstack/containerinfra/v1/clusters/urls.go @@ -13,3 +13,11 @@ func commonURL(client *gophercloud.ServiceClient) string { func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("clusters", id) +} + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("clusters") +} From 04600647c164f7caedebd69f203eaa448f4f8c82 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Sun, 2 Sep 2018 17:08:17 +0200 Subject: [PATCH 0520/2296] Workflowv2 workflows create (#1202) * Add Workflowv2 clients * Add workflow create * Create function to get a workflow definition for testing purpose * Rename ToWorkflowCreateQuery to ToWorkflowCreateParams - less ambiguous * Add missing object fields - tags, created_at, updated_at * Fix pointer reference on nil field * Change created_at and updated_at to time.Time --- acceptance/clients/clients.go | 21 +++++ acceptance/openstack/workflow/v2/workflow.go | 55 ++++++++++++ .../openstack/workflow/v2/workflows_test.go | 19 ++++ openstack/client.go | 5 ++ openstack/workflow/v2/workflows/doc.go | 67 ++++++++++++++ openstack/workflow/v2/workflows/requests.go | 56 ++++++++++++ openstack/workflow/v2/workflows/results.go | 82 +++++++++++++++++ .../v2/workflows/testing/requests_test.go | 90 +++++++++++++++++++ openstack/workflow/v2/workflows/urls.go | 9 ++ 9 files changed, 404 insertions(+) create mode 100644 acceptance/openstack/workflow/v2/workflow.go create mode 100644 acceptance/openstack/workflow/v2/workflows_test.go create mode 100644 openstack/workflow/v2/workflows/doc.go create mode 100644 openstack/workflow/v2/workflows/requests.go create mode 100644 openstack/workflow/v2/workflows/results.go create mode 100644 openstack/workflow/v2/workflows/testing/requests_test.go create mode 100644 openstack/workflow/v2/workflows/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index dc77708bb9..3f446d7e66 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -607,3 +607,24 @@ func NewContainerInfraV1Client() (*gophercloud.ServiceClient, error) { Region: os.Getenv("OS_REGION_NAME"), }) } + +// NewWorkflowV2Client returns a *ServiceClient for making calls +// to the OpenStack Workflow v2 API (Mistral). An error will be returned if +// authentication or client creation failed. +func NewWorkflowV2Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewWorkflowV2(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} diff --git a/acceptance/openstack/workflow/v2/workflow.go b/acceptance/openstack/workflow/v2/workflow.go new file mode 100644 index 0000000000..12f1cf934e --- /dev/null +++ b/acceptance/openstack/workflow/v2/workflow.go @@ -0,0 +1,55 @@ +package v2 + +import ( + "fmt" + "strings" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// GetEchoWorkflowDefinition returns a simple workflow definition that does nothing except a simple "echo" command. +func GetEchoWorkflowDefinition(workflowName string) string { + return fmt.Sprintf(`--- +version: '2.0' + +%s: + description: Simple workflow example + type: direct + + tasks: + test: + action: std.echo output="Hello World!"`, workflowName) +} + +// CreateWorkflow creates a workflow on Mistral API. +// The created workflow is a dummy workflow that performs a simple echo. +func CreateWorkflow(t *testing.T, client *gophercloud.ServiceClient) (*workflows.Workflow, error) { + workflowName := tools.RandomString("workflow_echo_", 5) + + definition := GetEchoWorkflowDefinition(workflowName) + + t.Logf("Attempting to create workflow: %s", workflowName) + + opts := &workflows.CreateOpts{ + Namespace: "some-namespace", + Scope: "private", + Definition: strings.NewReader(definition), + } + workflowList, err := workflows.Create(client, opts).Extract() + if err != nil { + return nil, err + } + th.AssertEquals(t, 1, len(workflowList)) + + workflow := workflowList[0] + + t.Logf("Workflow created: %s", workflowName) + + th.AssertEquals(t, workflowName, workflow.Name) + + return &workflow, nil +} diff --git a/acceptance/openstack/workflow/v2/workflows_test.go b/acceptance/openstack/workflow/v2/workflows_test.go new file mode 100644 index 0000000000..f8bda024c4 --- /dev/null +++ b/acceptance/openstack/workflow/v2/workflows_test.go @@ -0,0 +1,19 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestWorkflowsCreate(t *testing.T) { + client, err := clients.NewWorkflowV2Client() + th.AssertNoErr(t, err) + + workflow, err := CreateWorkflow(t, client) + th.AssertNoErr(t, err) + + tools.PrintResource(t, workflow) +} diff --git a/openstack/client.go b/openstack/client.go index 0859d9cb75..6d93af5b41 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -420,3 +420,8 @@ func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoint func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "container-infra") } + +// NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package. +func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "workflowv2") +} diff --git a/openstack/workflow/v2/workflows/doc.go b/openstack/workflow/v2/workflows/doc.go new file mode 100644 index 0000000000..a71c82df80 --- /dev/null +++ b/openstack/workflow/v2/workflows/doc.go @@ -0,0 +1,67 @@ +/* +Package workflows provides interaction with the workflows API in the OpenStack Mistral service. + +Workflow represents a process that can be described in a various number of ways and that can do some job interesting to the end user. +Each workflow consists of tasks (at least one) describing what exact steps should be made during workflow execution. + +Example to list workflows + + listOpts := workflows.ListOpts{ + Namespace: "some-namespace", + } + + allPages, err := workflows.List(mistralClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allWorkflows, err := workflows.ExtractWorkflows(allPages) + if err != nil { + panic(err) + } + + for _, workflow := range allWorkflows { + fmt.Printf("%+v\n", workflow) + } + +Example to create a workflow + + workflowDefinition := `--- +version: '2.0' + +create_vm: +description: Simple workflow example +type: direct + +input: + - vm_name + - image_ref + - flavor_ref +output: + vm_id: <% $.vm_id %> + +tasks: + create_server: + action: nova.servers_create name=<% $.vm_name %> image=<% $.image_ref %> flavor=<% $.flavor_ref %> + publish: + vm_id: <% task(create_server).result.id %> + on-success: + - wait_for_instance + + wait_for_instance: + action: nova.servers_find id=<% $.vm_id %> status='ACTIVE' + retry: + delay: 5 + count: 15` + + createOpts := &workflows.CreateOpts{ + Definition: strings.NewReader(workflowDefinition), + Namespace: "some-namespace", + } + + execution, err := workflows.Create(fake.ServiceClient(), opts).Extract() + if err != nil { + panic(err) + } +*/ +package workflows diff --git a/openstack/workflow/v2/workflows/requests.go b/openstack/workflow/v2/workflows/requests.go new file mode 100644 index 0000000000..34bcabe223 --- /dev/null +++ b/openstack/workflow/v2/workflows/requests.go @@ -0,0 +1,56 @@ +package workflows + +import ( + "io" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder allows extension to add additional parameters to the Create request. +type CreateOptsBuilder interface { + ToWorkflowCreateParams() (io.Reader, string, error) +} + +// CreateOpts specifies parameters used to create a cron trigger. +type CreateOpts struct { + // Scope is the scope of the workflow. + // Allowed values are "private" and "public". + Scope string `q:"scope"` + + // Namespace will define the namespace of the workflow. + Namespace string `q:"namespace"` + + // Definition is the workflow definition written in Mistral Workflow Language v2. + Definition io.Reader +} + +// ToWorkflowCreateParams constructs a request query string from CreateOpts. +func (opts CreateOpts) ToWorkflowCreateParams() (io.Reader, string, error) { + q, err := gophercloud.BuildQueryString(opts) + return opts.Definition, q.String(), err +} + +// Create requests the creation of a new execution. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + url := createURL(client) + var b io.Reader + if opts != nil { + tmpB, query, err := opts.ToWorkflowCreateParams() + if err != nil { + r.Err = err + return + } + url += query + b = tmpB + } + + _, r.Err = client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ + RawBody: b, + MoreHeaders: map[string]string{ + "Content-Type": "text/plain", + "Accept": "", // Drop default JSON Accept header + }, + }) + + return +} diff --git a/openstack/workflow/v2/workflows/results.go b/openstack/workflow/v2/workflows/results.go new file mode 100644 index 0000000000..cccd591794 --- /dev/null +++ b/openstack/workflow/v2/workflows/results.go @@ -0,0 +1,82 @@ +package workflows + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" +) + +// CreateResult is the response of a Post operations. Call its Extract method to interpret it as a list of Workflows. +type CreateResult struct { + gophercloud.Result +} + +// Extract helps to get created Workflow struct from a Create function. +func (r CreateResult) Extract() ([]Workflow, error) { + var s struct { + Workflows []Workflow `json:"workflows"` + } + err := r.ExtractInto(&s) + return s.Workflows, err +} + +// Workflow represents a workflow execution on OpenStack mistral API. +type Workflow struct { + // ID is the workflow's unique ID. + ID string `json:"id"` + + // Definition is the workflow definition in Mistral v2 DSL. + Definition string `json:"definition"` + + // Name is the name of the workflow. + Name string `json:"name"` + + // Namespace is the namespace of the workflow. + Namespace string `json:"namespace"` + + // Input represents the needed input to execute the workflow. + // This parameter is a list of each input, comma separated. + Input string `json:"input"` + + // ProjectID is the project id owner of the workflow. + ProjectID string `json:"project_id"` + + // Scope is the scope of the workflow. + // Values can be "private" or "public". + Scope string `json:"scope"` + + // Tags is a list of tags associated to the workflow. + Tags []string `json:"tags"` + + // CreatedAt is the creation date of the workflow. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the last update date of the workflow. + UpdatedAt *time.Time `json:"-"` +} + +// UnmarshalJSON implements unmarshalling custom types +func (r *Workflow) UnmarshalJSON(b []byte) error { + type tmp Workflow + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` + UpdatedAt *gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = Workflow(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + if s.UpdatedAt != nil { + t := time.Time(*s.UpdatedAt) + r.UpdatedAt = &t + } + + return nil +} diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go new file mode 100644 index 0000000000..e521073ee7 --- /dev/null +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -0,0 +1,90 @@ +package testing + +import ( + "fmt" + "net/http" + "reflect" + "strings" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateWorkflow(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + definition := `--- +version: '2.0' + +simple_echo: + description: Simple workflow example + type: direct + + tasks: + test: + action: std.echo output="Hello World!"` + + th.Mux.HandleFunc("/workflows", 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", "text/plain") + th.TestFormValues(t, r, map[string]string{ + "namespace": "some-namespace", + "scope": "private", + }) + th.TestBody(t, r, definition) + + w.WriteHeader(http.StatusCreated) + w.Header().Add("Content-Type", "application/json") + + fmt.Fprintf(w, `{ + "workflows": [ + { + "created_at": "1970-01-01 00:00:00", + "definition": "Workflow Definition in Mistral DSL v2", + "id": "1", + "input": "param1, param2", + "name": "flow", + "namespace": "some-namespace", + "project_id": "p1", + "scope": "private", + "updated_at": "1970-01-01 00:00:00" + } + ] + }`) + }) + + opts := &workflows.CreateOpts{ + Namespace: "some-namespace", + Scope: "private", + Definition: strings.NewReader(definition), + } + + actual, err := workflows.Create(fake.ServiceClient(), opts).Extract() + if err != nil { + t.Fatalf("Unable to create workflow: %v", err) + } + + updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + expected := []workflows.Workflow{ + workflows.Workflow{ + ID: "1", + Definition: "Workflow Definition in Mistral DSL v2", + Name: "flow", + Namespace: "some-namespace", + Input: "param1, param2", + ProjectID: "p1", + Scope: "private", + CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), + UpdatedAt: &updated, + }, + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/workflow/v2/workflows/urls.go b/openstack/workflow/v2/workflows/urls.go new file mode 100644 index 0000000000..aca6dfe792 --- /dev/null +++ b/openstack/workflow/v2/workflows/urls.go @@ -0,0 +1,9 @@ +package workflows + +import ( + "github.com/gophercloud/gophercloud" +) + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("workflows") +} From 78e47ea90127c6c5a89518a9631b4d0cfa18f319 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Sun, 2 Sep 2018 00:19:18 +1200 Subject: [PATCH 0521/2296] Octavia: Failover Loadbalancer --- openstack/loadbalancer/v2/loadbalancers/doc.go | 9 +++++++++ openstack/loadbalancer/v2/loadbalancers/requests.go | 8 ++++++++ openstack/loadbalancer/v2/loadbalancers/results.go | 6 ++++++ .../loadbalancer/v2/loadbalancers/testing/fixtures.go | 10 ++++++++++ .../v2/loadbalancers/testing/requests_test.go | 9 +++++++++ openstack/loadbalancer/v2/loadbalancers/urls.go | 5 +++++ 6 files changed, 47 insertions(+) diff --git a/openstack/loadbalancer/v2/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go index 244e19882a..77cb8321b0 100644 --- a/openstack/loadbalancer/v2/loadbalancers/doc.go +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -80,5 +80,14 @@ Example to Get the Statistics of a Load Balancer if err != nil { panic(err) } + +Example to Failover a Load Balancers + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + + err := loadbalancers.Failover(networkClient, lbID).ExtractErr() + if err != nil { + panic(err) + } */ package loadbalancers diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 99b60fb458..a50d8ef192 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -209,3 +209,11 @@ func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) return } + +// Failover performs a failover of a load balancer. +func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { + _, r.Err = c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 69e8a8b701..cb7fa60eef 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -180,3 +180,9 @@ type UpdateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// FailoverResult represents the result of a failover operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type FailoverResult struct { + gophercloud.ErrResult +} diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index f76a6abc0b..8730d8c286 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -323,3 +323,13 @@ func HandleLoadbalancerGetStatsTree(t *testing.T) { fmt.Fprintf(w, GetLoadbalancerStatsBody) }) } + +// HandleLoadbalancerFailoverSuccessfully sets up the test server to respond to a loadbalancer failover request. +func HandleLoadbalancerFailoverSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/failover", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index acf22f9104..b23625bebb 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -174,3 +174,12 @@ func TestGetLoadbalancerStatsTree(t *testing.T) { th.CheckDeepEquals(t, LoadbalancerStatsTree, *actual) } + +func TestFailoverLoadbalancer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerFailoverSuccessfully(t) + + res := loadbalancers.Failover(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/loadbalancer/v2/loadbalancers/urls.go b/openstack/loadbalancer/v2/loadbalancers/urls.go index 70267c358d..7b184e35f6 100644 --- a/openstack/loadbalancer/v2/loadbalancers/urls.go +++ b/openstack/loadbalancer/v2/loadbalancers/urls.go @@ -7,6 +7,7 @@ const ( resourcePath = "loadbalancers" statusPath = "status" statisticsPath = "stats" + failoverPath = "failover" ) func rootURL(c *gophercloud.ServiceClient) string { @@ -24,3 +25,7 @@ func statusRootURL(c *gophercloud.ServiceClient, id string) string { func statisticsRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, statisticsPath) } + +func failoverRootURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id, failoverPath) +} From 07d15af37699505fb5535c0962f49a10379d12ff Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 3 Sep 2018 09:50:40 -0600 Subject: [PATCH 0522/2296] Acc Tests: Fix compute v2 lock/unlock test (#1223) --- acceptance/clients/conditions.go | 7 +++++++ acceptance/openstack/compute/v2/servers_test.go | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index 9cbbc0744f..bc8b306d08 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -12,6 +12,13 @@ func RequireAdmin(t *testing.T) { } } +// RequireNonAdmin will restrict a test to only be run by non-admin users. +func RequireNonAdmin(t *testing.T) { + if os.Getenv("OS_USERNAME") == "admin" { + t.Skip("must be a non-admin to run this test") + } +} + // RequireDNS will restrict a test to only be run in environments // that support DNSaaS. func RequireDNS(t *testing.T) { diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 11484a5c61..bd7c8af767 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -439,6 +439,7 @@ func TestServersActionSuspend(t *testing.T) { func TestServersActionLock(t *testing.T) { clients.RequireLong(t) + clients.RequireNonAdmin(t) client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) @@ -451,9 +452,11 @@ func TestServersActionLock(t *testing.T) { err = lockunlock.Lock(client, server.ID).ExtractErr() th.AssertNoErr(t, err) + t.Logf("Attempting to delete locked server %s", server.ID) err = servers.Delete(client, server.ID).ExtractErr() - th.AssertNoErr(t, err) + th.AssertEquals(t, err != nil, true) + t.Logf("Attempting to unlock server %s", server.ID) err = lockunlock.Unlock(client, server.ID).ExtractErr() th.AssertNoErr(t, err) From 13af1c2a38cfbc60a7a13062338838301035d91d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 5 Sep 2018 19:46:48 -0600 Subject: [PATCH 0523/2296] Orchestration v1: Doc updates, Time fixes, and Acceptance Tests (#1221) * Correct Time format for orchestration resources Support both RFC3339Milli, and RFC3339MilliNoZ. Appears part of heat use RFC3339MilliNoZ and been unified to RFC3339Milli after newton [2]. [1] https://github.com/gophercloud/gophercloud/issues/185#issuecomment-417321330 [2] https://github.com/openstack/heat/commit/f655726560bd59cc7d3e18af1d8d53b14c007a7d * Normalize Parameters format for stacks in Orchestration Normalize to use type `map[string]interface{}` for all Parameters. * Improve doc for Orchestration Service (#949) * Orchestration v1: fixing up acceptance tests * Orchestration v1: Modifying formats used to parse timestamps * Orchestration v1: Fixing struct pointers * Acc Tests: Enabling orchestration tests * Orchestration v1: Doc nits --- acceptance/clients/clients.go | 21 +++ .../orchestration/v1/buildinfo_test.go | 6 +- .../openstack/orchestration/v1/common.go | 44 ------ .../orchestration/v1/hello-compute.json | 13 -- .../orchestration/v1/orchestration.go | 116 ++++++++++++++ acceptance/openstack/orchestration/v1/pkg.go | 1 - .../orchestration/v1/stackevents_test.go | 68 +++----- .../orchestration/v1/stackresources_test.go | 63 +++----- .../openstack/orchestration/v1/stacks_test.go | 148 +++--------------- .../orchestration/v1/stacktemplates_test.go | 73 +++------ openstack/orchestration/v1/stackevents/doc.go | 21 ++- .../orchestration/v1/stackevents/results.go | 14 +- .../v1/stackevents/testing/fixtures.go | 31 ++-- .../v1/stackevents/testing/requests_test.go | 2 +- .../orchestration/v1/stackresources/doc.go | 74 ++++++++- .../v1/stackresources/results.go | 29 +++- .../v1/stackresources/testing/fixtures.go | 23 +-- openstack/orchestration/v1/stacks/doc.go | 112 +++++++++++++ openstack/orchestration/v1/stacks/requests.go | 6 +- openstack/orchestration/v1/stacks/results.go | 87 ++++++++-- .../v1/stacks/testing/fixtures.go | 21 +-- .../v1/stacks/testing/requests_test.go | 23 +++ .../orchestration/v1/stacktemplates/doc.go | 44 +++++- script/acceptancetest | 7 + 24 files changed, 648 insertions(+), 399 deletions(-) delete mode 100644 acceptance/openstack/orchestration/v1/common.go delete mode 100644 acceptance/openstack/orchestration/v1/hello-compute.json create mode 100644 acceptance/openstack/orchestration/v1/orchestration.go delete mode 100644 acceptance/openstack/orchestration/v1/pkg.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 3f446d7e66..323d0bcf47 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -628,3 +628,24 @@ func NewWorkflowV2Client() (*gophercloud.ServiceClient, error) { Region: os.Getenv("OS_REGION_NAME"), }) } + +// NewOrchestrationV1Client returns a *ServiceClient for making calls +// to the OpenStack Orchestration v1 API. An error will be returned +// if authentication or client creation was not possible. +func NewOrchestrationV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewOrchestrationV1(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} diff --git a/acceptance/openstack/orchestration/v1/buildinfo_test.go b/acceptance/openstack/orchestration/v1/buildinfo_test.go index 1b48662916..03806a36d6 100644 --- a/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -5,14 +5,14 @@ package v1 import ( "testing" + "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo" th "github.com/gophercloud/gophercloud/testhelper" ) func TestBuildInfo(t *testing.T) { - // Create a provider client for making the HTTP requests. - // See common.go in this directory for more information. - client := newClient(t) + client, err := clients.NewOrchestrationV1Client() + th.AssertNoErr(t, err) bi, err := buildinfo.Get(client).Extract() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/orchestration/v1/common.go b/acceptance/openstack/orchestration/v1/common.go deleted file mode 100644 index 4eec2e3156..0000000000 --- a/acceptance/openstack/orchestration/v1/common.go +++ /dev/null @@ -1,44 +0,0 @@ -// +build acceptance - -package v1 - -import ( - "fmt" - "os" - "testing" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - th "github.com/gophercloud/gophercloud/testhelper" -) - -var template = fmt.Sprintf(` -{ - "heat_template_version": "2013-05-23", - "description": "Simple template to test heat commands", - "parameters": {}, - "resources": { - "hello_world": { - "type":"OS::Nova::Server", - "properties": { - "flavor": "%s", - "image": "%s", - "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n" - } - } - } -}`, os.Getenv("OS_FLAVOR_ID"), os.Getenv("OS_IMAGE_ID")) - -func newClient(t *testing.T) *gophercloud.ServiceClient { - ao, err := openstack.AuthOptionsFromEnv() - th.AssertNoErr(t, err) - - client, err := openstack.AuthenticatedClient(ao) - th.AssertNoErr(t, err) - - c, err := openstack.NewOrchestrationV1(client, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), - }) - th.AssertNoErr(t, err) - return c -} diff --git a/acceptance/openstack/orchestration/v1/hello-compute.json b/acceptance/openstack/orchestration/v1/hello-compute.json deleted file mode 100644 index 11cfc80534..0000000000 --- a/acceptance/openstack/orchestration/v1/hello-compute.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "heat_template_version": "2013-05-23", - "resources": { - "compute_instance": { - "type": "OS::Nova::Server", - "properties": { - "flavor": "m1.small", - "image": "cirros-0.3.2-x86_64-disk", - "name": "Single Compute Instance" - } - } - } -} diff --git a/acceptance/openstack/orchestration/v1/orchestration.go b/acceptance/openstack/orchestration/v1/orchestration.go new file mode 100644 index 0000000000..f3223a81a9 --- /dev/null +++ b/acceptance/openstack/orchestration/v1/orchestration.go @@ -0,0 +1,116 @@ +package v1 + +import ( + "fmt" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +const basicTemplateResourceName = "secgroup_1" +const basicTemplate = ` + { + "heat_template_version": "2013-05-23", + "description": "Simple template to test heat commands", + "resources": { + "secgroup_1": { + "type": "OS::Neutron::SecurityGroup", + "properties": { + "description": "Gophercloud test", + "name": "secgroup_1" + } + } + } + } +` + +const validateTemplate = ` + { + "heat_template_version": "2013-05-23", + "description": "Simple template to test heat commands", + "parameters": { + "flavor": { + "default": "m1.tiny", + "type": "string" + } + }, + "resources": { + "hello_world": { + "type": "OS::Nova::Server", + "properties": { + "key_name": "heat_key", + "flavor": { + "get_param": "flavor" + }, + "image": "ad091b52-742f-469e-8f3c-fd81cadf0743", + "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n" + } + } + } + } +` + +// CreateStack will create a heat stack with a randomly generated name. +// An error will be returned if the stack failed to be created. +func CreateStack(t *testing.T, client *gophercloud.ServiceClient) (*stacks.RetrievedStack, error) { + stackName := tools.RandomString("ACCPTEST", 8) + t.Logf("Attempting to create stack %s", stackName) + + template := new(stacks.Template) + template.Bin = []byte(basicTemplate) + + createOpts := stacks.CreateOpts{ + Name: stackName, + Timeout: 60, + TemplateOpts: template, + DisableRollback: gophercloud.Disabled, + } + + stack, err := stacks.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForStackStatus(client, stackName, stack.ID, "CREATE_COMPLETE"); err != nil { + return nil, err + } + + newStack, err := stacks.Get(client, stackName, stack.ID).Extract() + return newStack, err +} + +// DeleteStack deletes a stack via its ID. +// A fatal error will occur if the stack failed to be deleted. This works +// best when used as a deferred function. +func DeleteStack(t *testing.T, client *gophercloud.ServiceClient, stackName, stackID string) { + t.Logf("Attempting to delete stack %s (%s)", stackName, stackID) + + err := stacks.Delete(client, stackName, stackID).ExtractErr() + if err != nil { + t.Fatalf("Failed to delete stack %s: %s", stackID, err) + } + + t.Logf("Deleted stack: %s", stackID) +} + +// WaitForStackStatus will wait until a stack has reached a certain status. +func WaitForStackStatus(client *gophercloud.ServiceClient, stackName, stackID, status string) error { + return tools.WaitFor(func() (bool, error) { + latest, err := stacks.Get(client, stackName, stackID).Extract() + if err != nil { + return false, err + } + + if latest.Status == status { + return true, nil + } + + if latest.Status == "ERROR" { + return false, fmt.Errorf("Stack in ERROR state") + } + + return false, nil + }) +} diff --git a/acceptance/openstack/orchestration/v1/pkg.go b/acceptance/openstack/orchestration/v1/pkg.go deleted file mode 100644 index b7b1f993d5..0000000000 --- a/acceptance/openstack/orchestration/v1/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package v1 diff --git a/acceptance/openstack/orchestration/v1/stackevents_test.go b/acceptance/openstack/orchestration/v1/stackevents_test.go index 4be4bf676a..7bab0fe06a 100644 --- a/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -5,64 +5,36 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + //"github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" - "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestStackEvents(t *testing.T) { - // Create a provider client for making the HTTP requests. - // See common.go in this directory for more information. - client := newClient(t) - - stackName := "postman_stack_2" - resourceName := "hello_world" - var eventID string + client, err := clients.NewOrchestrationV1Client() + th.AssertNoErr(t, err) - createOpts := stacks.CreateOpts{ - Name: stackName, - Template: template, - Timeout: 5, - } - stack, err := stacks.Create(client, createOpts).Extract() + stack, err := CreateStack(t, client) th.AssertNoErr(t, err) - t.Logf("Created stack: %+v\n", stack) - defer func() { - err := stacks.Delete(client, stackName, stack.ID).ExtractErr() - th.AssertNoErr(t, err) - t.Logf("Deleted stack (%s)", stackName) - }() - err = gophercloud.WaitFor(60, func() (bool, error) { - getStack, err := stacks.Get(client, stackName, stack.ID).Extract() - if err != nil { - return false, err - } - if getStack.Status == "CREATE_COMPLETE" { - return true, nil - } - return false, nil - }) + defer DeleteStack(t, client, stack.Name, stack.ID) - err = stackevents.List(client, stackName, stack.ID, nil).EachPage(func(page pagination.Page) (bool, error) { - events, err := stackevents.ExtractEvents(page) - th.AssertNoErr(t, err) - t.Logf("listed events: %+v\n", events) - eventID = events[0].ID - return false, nil - }) + allPages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages() th.AssertNoErr(t, err) + allEvents, err := stackevents.ExtractEvents(allPages) + th.AssertNoErr(t, err) + + th.AssertEquals(t, len(allEvents), 4) - err = stackevents.ListResourceEvents(client, stackName, stack.ID, resourceName, nil).EachPage(func(page pagination.Page) (bool, error) { - resourceEvents, err := stackevents.ExtractEvents(page) + /* + allPages is currently broke + allPages, err = stackevents.ListResourceEvents(client, stack.Name, stack.ID, basicTemplateResourceName, nil).AllPages() + th.AssertNoErr(t, err) + allEvents, err = stackevents.ExtractEvents(allPages) th.AssertNoErr(t, err) - t.Logf("listed resource events: %+v\n", resourceEvents) - return false, nil - }) - th.AssertNoErr(t, err) - event, err := stackevents.Get(client, stackName, stack.ID, resourceName, eventID).Extract() - th.AssertNoErr(t, err) - t.Logf("retrieved event: %+v\n", event) + for _, v := range allEvents { + tools.PrintResource(t, v) + } + */ } diff --git a/acceptance/openstack/orchestration/v1/stackresources_test.go b/acceptance/openstack/orchestration/v1/stackresources_test.go index 50a0f06311..40d5f87cff 100644 --- a/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -5,58 +5,39 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" - "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) func TestStackResources(t *testing.T) { - // Create a provider client for making the HTTP requests. - // See common.go in this directory for more information. - client := newClient(t) + client, err := clients.NewOrchestrationV1Client() + th.AssertNoErr(t, err) - stackName := "postman_stack_2" + stack, err := CreateStack(t, client) + th.AssertNoErr(t, err) + defer DeleteStack(t, client, stack.Name, stack.ID) - createOpts := stacks.CreateOpts{ - Name: stackName, - Template: template, - Timeout: 5, - } - stack, err := stacks.Create(client, createOpts).Extract() + resource, err := stackresources.Get(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() th.AssertNoErr(t, err) - t.Logf("Created stack: %+v\n", stack) - defer func() { - err := stacks.Delete(client, stackName, stack.ID).ExtractErr() - th.AssertNoErr(t, err) - t.Logf("Deleted stack (%s)", stackName) - }() - err = gophercloud.WaitFor(60, func() (bool, error) { - getStack, err := stacks.Get(client, stackName, stack.ID).Extract() - if err != nil { - return false, err - } - if getStack.Status == "CREATE_COMPLETE" { - return true, nil - } - return false, nil - }) + tools.PrintResource(t, resource) - resourceName := "hello_world" - resource, err := stackresources.Get(client, stackName, stack.ID, resourceName).Extract() + metadata, err := stackresources.Metadata(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() th.AssertNoErr(t, err) - t.Logf("Got stack resource: %+v\n", resource) + tools.PrintResource(t, metadata) - metadata, err := stackresources.Metadata(client, stackName, stack.ID, resourceName).Extract() + allPages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages() th.AssertNoErr(t, err) - t.Logf("Got stack resource metadata: %+v\n", metadata) - - err = stackresources.List(client, stackName, stack.ID, stackresources.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - resources, err := stackresources.ExtractResources(page) - th.AssertNoErr(t, err) - t.Logf("resources: %+v\n", resources) - return false, nil - }) + allResources, err := stackresources.ExtractResources(allPages) th.AssertNoErr(t, err) + + var found bool + for _, v := range allResources { + if v.Name == basicTemplateResourceName { + found = true + } + } + + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/orchestration/v1/stacks_test.go b/acceptance/openstack/orchestration/v1/stacks_test.go index c87cc5d00e..a0738d5a97 100644 --- a/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/acceptance/openstack/orchestration/v1/stacks_test.go @@ -5,149 +5,47 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" - "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) -func TestStacks(t *testing.T) { - // Create a provider client for making the HTTP requests. - // See common.go in this directory for more information. - client := newClient(t) - - stackName1 := "gophercloud-test-stack-2" - createOpts := stacks.CreateOpts{ - Name: stackName1, - Template: template, - Timeout: 5, - } - stack, err := stacks.Create(client, createOpts).Extract() +func TestStacksCRUD(t *testing.T) { + client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) - t.Logf("Created stack: %+v\n", stack) - defer func() { - err := stacks.Delete(client, stackName1, stack.ID).ExtractErr() - th.AssertNoErr(t, err) - t.Logf("Deleted stack (%s)", stackName1) - }() - err = gophercloud.WaitFor(60, func() (bool, error) { - getStack, err := stacks.Get(client, stackName1, stack.ID).Extract() - if err != nil { - return false, err - } - if getStack.Status == "CREATE_COMPLETE" { - return true, nil - } - return false, nil - }) - updateOpts := stacks.UpdateOpts{ - Template: template, - Timeout: 20, - } - err = stacks.Update(client, stackName1, stack.ID, updateOpts).ExtractErr() + createdStack, err := CreateStack(t, client) th.AssertNoErr(t, err) - err = gophercloud.WaitFor(60, func() (bool, error) { - getStack, err := stacks.Get(client, stackName1, stack.ID).Extract() - if err != nil { - return false, err - } - if getStack.Status == "UPDATE_COMPLETE" { - return true, nil - } - return false, nil - }) + defer DeleteStack(t, client, createdStack.Name, createdStack.ID) - t.Logf("Updated stack") + tools.PrintResource(t, createdStack) + tools.PrintResource(t, createdStack.CreationTime) - err = stacks.List(client, nil).EachPage(func(page pagination.Page) (bool, error) { - stackList, err := stacks.ExtractStacks(page) - th.AssertNoErr(t, err) - - t.Logf("Got stack list: %+v\n", stackList) + template := new(stacks.Template) + template.Bin = []byte(basicTemplate) + updateOpts := stacks.UpdateOpts{ + TemplateOpts: template, + Timeout: 20, + } - return true, nil - }) + err = stacks.Update(client, createdStack.Name, createdStack.ID, updateOpts).ExtractErr() th.AssertNoErr(t, err) - getStack, err := stacks.Get(client, stackName1, stack.ID).Extract() + err = WaitForStackStatus(client, createdStack.Name, createdStack.ID, "UPDATE_COMPLETE") th.AssertNoErr(t, err) - t.Logf("Got stack: %+v\n", getStack) - abandonedStack, err := stacks.Abandon(client, stackName1, stack.ID).Extract() + var found bool + allPages, err := stacks.List(client, nil).AllPages() th.AssertNoErr(t, err) - t.Logf("Abandonded stack %+v\n", abandonedStack) + allStacks, err := stacks.ExtractStacks(allPages) th.AssertNoErr(t, err) -} - -// Test using the updated interface -func TestStacksNewTemplateFormat(t *testing.T) { - // Create a provider client for making the HTTP requests. - // See common.go in this directory for more information. - client := newClient(t) - stackName1 := "gophercloud-test-stack-2" - templateOpts := new(osStacks.Template) - templateOpts.Bin = []byte(template) - createOpts := osStacks.CreateOpts{ - Name: stackName1, - TemplateOpts: templateOpts, - Timeout: 5, - } - stack, err := stacks.Create(client, createOpts).Extract() - th.AssertNoErr(t, err) - t.Logf("Created stack: %+v\n", stack) - defer func() { - err := stacks.Delete(client, stackName1, stack.ID).ExtractErr() - th.AssertNoErr(t, err) - t.Logf("Deleted stack (%s)", stackName1) - }() - err = gophercloud.WaitFor(60, func() (bool, error) { - getStack, err := stacks.Get(client, stackName1, stack.ID).Extract() - if err != nil { - return false, err + for _, v := range allStacks { + if v.ID == createdStack.ID { + found = true } - if getStack.Status == "CREATE_COMPLETE" { - return true, nil - } - return false, nil - }) - - updateOpts := osStacks.UpdateOpts{ - TemplateOpts: templateOpts, - Timeout: 20, } - err = stacks.Update(client, stackName1, stack.ID, updateOpts).ExtractErr() - th.AssertNoErr(t, err) - err = gophercloud.WaitFor(60, func() (bool, error) { - getStack, err := stacks.Get(client, stackName1, stack.ID).Extract() - if err != nil { - return false, err - } - if getStack.Status == "UPDATE_COMPLETE" { - return true, nil - } - return false, nil - }) - - t.Logf("Updated stack") - - err = stacks.List(client, nil).EachPage(func(page pagination.Page) (bool, error) { - stackList, err := osStacks.ExtractStacks(page) - th.AssertNoErr(t, err) - t.Logf("Got stack list: %+v\n", stackList) - - return true, nil - }) - th.AssertNoErr(t, err) - - getStack, err := stacks.Get(client, stackName1, stack.ID).Extract() - th.AssertNoErr(t, err) - t.Logf("Got stack: %+v\n", getStack) - - abandonedStack, err := stacks.Abandon(client, stackName1, stack.ID).Extract() - th.AssertNoErr(t, err) - t.Logf("Abandonded stack %+v\n", abandonedStack) - th.AssertNoErr(t, err) + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/acceptance/openstack/orchestration/v1/stacktemplates_test.go index 9992e0c044..ba00d88a9f 100644 --- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -5,71 +5,34 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates" th "github.com/gophercloud/gophercloud/testhelper" ) -func TestStackTemplates(t *testing.T) { - // Create a provider client for making the HTTP requests. - // See common.go in this directory for more information. - client := newClient(t) +func TestStackTemplatesCRUD(t *testing.T) { + client, err := clients.NewOrchestrationV1Client() + th.AssertNoErr(t, err) - stackName := "postman_stack_2" + stack, err := CreateStack(t, client) + th.AssertNoErr(t, err) + defer DeleteStack(t, client, stack.Name, stack.ID) - createOpts := stacks.CreateOpts{ - Name: stackName, - Template: template, - Timeout: 5, - } - stack, err := stacks.Create(client, createOpts).Extract() + tmpl, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() th.AssertNoErr(t, err) - t.Logf("Created stack: %+v\n", stack) - defer func() { - err := stacks.Delete(client, stackName, stack.ID).ExtractErr() - th.AssertNoErr(t, err) - t.Logf("Deleted stack (%s)", stackName) - }() - err = gophercloud.WaitFor(60, func() (bool, error) { - getStack, err := stacks.Get(client, stackName, stack.ID).Extract() - if err != nil { - return false, err - } - if getStack.Status == "CREATE_COMPLETE" { - return true, nil - } - return false, nil - }) + tools.PrintResource(t, tmpl) +} - tmpl, err := stacktemplates.Get(client, stackName, stack.ID).Extract() +func TestStackTemplatesValidate(t *testing.T) { + client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) - t.Logf("retrieved template: %+v\n", tmpl) - validateOpts := osStacktemplates.ValidateOpts{ - Template: `{"heat_template_version": "2013-05-23", - "description": "Simple template to test heat commands", - "parameters": { - "flavor": { - "default": "m1.tiny", - "type": "string", - }, - }, - "resources": { - "hello_world": { - "type": "OS::Nova::Server", - "properties": { - "key_name": "heat_key", - "flavor": { - "get_param": "flavor", - }, - "image": "ad091b52-742f-469e-8f3c-fd81cadf0743", - "user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n", - }, - }, - }, - }`} + validateOpts := stacktemplates.ValidateOpts{ + Template: validateTemplate, + } + validatedTemplate, err := stacktemplates.Validate(client, validateOpts).Extract() th.AssertNoErr(t, err) - t.Logf("validated template: %+v\n", validatedTemplate) + tools.PrintResource(t, validatedTemplate) } diff --git a/openstack/orchestration/v1/stackevents/doc.go b/openstack/orchestration/v1/stackevents/doc.go index 51cdd97473..0787858391 100644 --- a/openstack/orchestration/v1/stackevents/doc.go +++ b/openstack/orchestration/v1/stackevents/doc.go @@ -1,4 +1,19 @@ -// Package stackevents provides operations for finding, listing, and retrieving -// stack events. Stack events are events that take place on stacks such as -// updating and abandoning. +/* +Package stackevents provides operations for finding, listing, and retrieving +stack events. Stack events are events that take place on stacks such as +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) +*/ package stackevents diff --git a/openstack/orchestration/v1/stackevents/results.go b/openstack/orchestration/v1/stackevents/results.go index 46fb0ff088..75f7d3f386 100644 --- a/openstack/orchestration/v1/stackevents/results.go +++ b/openstack/orchestration/v1/stackevents/results.go @@ -34,8 +34,9 @@ func (r *Event) UnmarshalJSON(b []byte) error { type tmp Event var s struct { tmp - Time gophercloud.JSONRFC3339NoZ `json:"event_time"` + Time string `json:"event_time"` } + err := json.Unmarshal(b, &s) if err != nil { return err @@ -43,7 +44,16 @@ func (r *Event) UnmarshalJSON(b []byte) error { *r = Event(s.tmp) - r.Time = time.Time(s.Time) + if s.Time != "" { + t, err := time.Parse(time.RFC3339, s.Time) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.Time) + if err != nil { + return err + } + } + r.Time = t + } return nil } diff --git a/openstack/orchestration/v1/stackevents/testing/fixtures.go b/openstack/orchestration/v1/stackevents/testing/fixtures.go index a40e8d4f60..01a64583c1 100644 --- a/openstack/orchestration/v1/stackevents/testing/fixtures.go +++ b/openstack/orchestration/v1/stackevents/testing/fixtures.go @@ -12,11 +12,14 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +var Timestamp1, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z") +var Timestamp2, _ = time.Parse(time.RFC3339, "2018-06-26T07:59:17Z") + // FindExpected represents the expected object from a Find request. var FindExpected = []stackevents.Event{ { ResourceName: "hello_world", - Time: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC), + Time: Timestamp1, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", @@ -39,7 +42,7 @@ var FindExpected = []stackevents.Event{ }, { ResourceName: "hello_world", - Time: time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC), + Time: Timestamp2, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", @@ -68,7 +71,7 @@ const FindOutput = ` "events": [ { "resource_name": "hello_world", - "event_time": "2015-02-05T21:33:11", + "event_time": "2018-06-26T07:58:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", @@ -91,7 +94,7 @@ const FindOutput = ` }, { "resource_name": "hello_world", - "event_time": "2015-02-05T21:33:27", + "event_time": "2018-06-26T07:59:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", @@ -133,7 +136,7 @@ func HandleFindSuccessfully(t *testing.T, output string) { var ListExpected = []stackevents.Event{ { ResourceName: "hello_world", - Time: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC), + Time: Timestamp1, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", @@ -156,7 +159,7 @@ var ListExpected = []stackevents.Event{ }, { ResourceName: "hello_world", - Time: time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC), + Time: Timestamp2, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", @@ -185,7 +188,7 @@ const ListOutput = ` "events": [ { "resource_name": "hello_world", - "event_time": "2015-02-05T21:33:11", + "event_time": "2018-06-26T07:58:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", @@ -208,7 +211,7 @@ const ListOutput = ` }, { "resource_name": "hello_world", - "event_time": "2015-02-05T21:33:27", + "event_time": "2018-06-26T07:59:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", @@ -258,7 +261,7 @@ func HandleListSuccessfully(t *testing.T, output string) { var ListResourceEventsExpected = []stackevents.Event{ { ResourceName: "hello_world", - Time: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC), + Time: Timestamp1, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", @@ -281,7 +284,7 @@ var ListResourceEventsExpected = []stackevents.Event{ }, { ResourceName: "hello_world", - Time: time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC), + Time: Timestamp2, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", @@ -310,7 +313,7 @@ const ListResourceEventsOutput = ` "events": [ { "resource_name": "hello_world", - "event_time": "2015-02-05T21:33:11", + "event_time": "2018-06-26T07:58:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a", @@ -333,7 +336,7 @@ const ListResourceEventsOutput = ` }, { "resource_name": "hello_world", - "event_time": "2015-02-05T21:33:27", + "event_time": "2018-06-26T07:59:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", @@ -382,7 +385,7 @@ func HandleListResourceEventsSuccessfully(t *testing.T, output string) { // GetExpected represents the expected object from a Get request. var GetExpected = &stackevents.Event{ ResourceName: "hello_world", - Time: time.Date(2015, 2, 5, 21, 33, 27, 0, time.UTC), + Time: Timestamp2, Links: []gophercloud.Link{ { Href: "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", @@ -409,7 +412,7 @@ const GetOutput = ` { "event":{ "resource_name": "hello_world", - "event_time": "2015-02-05T21:33:27", + "event_time": "2018-06-26T07:59:17Z", "links": [ { "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18", diff --git a/openstack/orchestration/v1/stackevents/testing/requests_test.go b/openstack/orchestration/v1/stackevents/testing/requests_test.go index 0ad3fc31f6..6dabfd23ef 100644 --- a/openstack/orchestration/v1/stackevents/testing/requests_test.go +++ b/openstack/orchestration/v1/stackevents/testing/requests_test.go @@ -48,7 +48,7 @@ func TestListResourceEvents(t *testing.T) { count := 0 err := stackevents.ListResourceEvents(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", nil).EachPage(func(page pagination.Page) (bool, error) { count++ - actual, err := stackevents.ExtractEvents(page) + actual, err := stackevents.ExtractResourceEvents(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ListResourceEventsExpected, actual) diff --git a/openstack/orchestration/v1/stackresources/doc.go b/openstack/orchestration/v1/stackresources/doc.go index e4f8b08dcc..ae282dc08c 100644 --- a/openstack/orchestration/v1/stackresources/doc.go +++ b/openstack/orchestration/v1/stackresources/doc.go @@ -1,5 +1,71 @@ -// Package stackresources provides operations for working with stack resources. -// A resource is a template artifact that represents some component of your -// desired architecture (a Cloud Server, a group of scaled Cloud Servers, a load -// balancer, some configuration management system, and so forth). +/* +Package stackresources provides operations for working with stack resources. +A resource is a template artifact that represents some component of your +desired architecture (a Cloud Server, a group of scaled Cloud Servers, a load +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) + } + +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_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) + +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)) +*/ package stackresources diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go index 59c02a38c1..5ea5242aec 100644 --- a/openstack/orchestration/v1/stackresources/results.go +++ b/openstack/orchestration/v1/stackresources/results.go @@ -28,17 +28,38 @@ func (r *Resource) UnmarshalJSON(b []byte) error { type tmp Resource var s struct { tmp - CreationTime gophercloud.JSONRFC3339NoZ `json:"creation_time"` - UpdatedTime gophercloud.JSONRFC3339NoZ `json:"updated_time"` + CreationTime string `json:"creation_time"` + UpdatedTime string `json:"updated_time"` } + err := json.Unmarshal(b, &s) if err != nil { return err } + *r = Resource(s.tmp) - r.CreationTime = time.Time(s.CreationTime) - r.UpdatedTime = time.Time(s.UpdatedTime) + if s.CreationTime != "" { + t, err := time.Parse(time.RFC3339, s.CreationTime) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.CreationTime) + if err != nil { + return err + } + } + r.CreationTime = t + } + + if s.UpdatedTime != "" { + t, err := time.Parse(time.RFC3339, s.UpdatedTime) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.UpdatedTime) + if err != nil { + return err + } + } + r.UpdatedTime = t + } return nil } diff --git a/openstack/orchestration/v1/stackresources/testing/fixtures.go b/openstack/orchestration/v1/stackresources/testing/fixtures.go index e8903374ec..0e64e9b367 100644 --- a/openstack/orchestration/v1/stackresources/testing/fixtures.go +++ b/openstack/orchestration/v1/stackresources/testing/fixtures.go @@ -12,6 +12,9 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +var Create_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:57:17Z") +var Updated_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z") + // FindExpected represents the expected object from a Find request. var FindExpected = []stackresources.Resource{ { @@ -28,8 +31,8 @@ var FindExpected = []stackresources.Resource{ }, LogicalID: "hello_world", StatusReason: "state changed", - UpdatedTime: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC), - CreationTime: time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC), + UpdatedTime: Updated_time, + CreationTime: Create_time, RequiredBy: []interface{}{}, Status: "CREATE_IN_PROGRESS", PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf", @@ -59,8 +62,8 @@ const FindOutput = ` ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", - "updated_time": "2015-02-05T21:33:11", - "creation_time": "2015-02-05T21:33:10", + "updated_time": "2018-06-26T07:58:17Z", + "creation_time": "2018-06-26T07:57:17Z", "required_by": [], "resource_status": "CREATE_IN_PROGRESS", "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf", @@ -99,8 +102,8 @@ var ListExpected = []stackresources.Resource{ }, LogicalID: "hello_world", StatusReason: "state changed", - UpdatedTime: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC), - CreationTime: time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC), + UpdatedTime: Updated_time, + CreationTime: Create_time, RequiredBy: []interface{}{}, Status: "CREATE_IN_PROGRESS", PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf", @@ -127,11 +130,11 @@ const ListOutput = `{ ], "logical_resource_id": "hello_world", "resource_status_reason": "state changed", - "updated_time": "2015-02-05T21:33:11", + "updated_time": "2018-06-26T07:58:17Z", + "creation_time": "2018-06-26T07:57:17Z", "required_by": [], "resource_status": "CREATE_IN_PROGRESS", "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf", - "creation_time": "2015-02-05T21:33:10", "resource_type": "OS::Nova::Server", "attributes": {"SXSW": "atx"}, "description": "Some resource" @@ -177,7 +180,7 @@ var GetExpected = &stackresources.Resource{ LogicalID: "wordpress_instance", Attributes: map[string]interface{}{"SXSW": "atx"}, StatusReason: "state changed", - UpdatedTime: time.Date(2014, 12, 10, 18, 34, 35, 0, time.UTC), + UpdatedTime: Updated_time, RequiredBy: []interface{}{}, Status: "CREATE_COMPLETE", PhysicalID: "00e3a2fe-c65d-403c-9483-4db9930dd194", @@ -204,7 +207,7 @@ const GetOutput = ` ], "logical_resource_id": "wordpress_instance", "resource_status": "CREATE_COMPLETE", - "updated_time": "2014-12-10T18:34:35", + "updated_time": "2018-06-26T07:58:17Z", "required_by": [], "resource_status_reason": "state changed", "physical_resource_id": "00e3a2fe-c65d-403c-9483-4db9930dd194", diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index f8f2a6fefb..92bda23eae 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -7,6 +7,118 @@ 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 + +import ( + "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"}) + +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) + } + + 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) + +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) + +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) + Summary of Behavior Between Stack Update and UpdatePatch Methods : Function | Test Case | Result diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index 5df222ff0c..779e3fb904 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -30,7 +30,7 @@ type CreateOpts struct { // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. - Parameters map[string]string `json:"parameters,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` // The timeout for stack creation in minutes. Timeout int `json:"timeout_mins,omitempty"` // A list of tags to assosciate with the Stack @@ -127,7 +127,7 @@ type AdoptOpts struct { // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. - Parameters map[string]string `json:"parameters,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` } // ToStackAdoptMap casts a CreateOpts struct to a map. @@ -405,7 +405,7 @@ type PreviewOpts struct { // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. - Parameters map[string]string `json:"parameters,omitempty"` + Parameters map[string]interface{} `json:"parameters,omitempty"` } // ToStackPreviewMap casts a PreviewOpts struct to a map. diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go index 8df541940e..054ab3d74b 100644 --- a/openstack/orchestration/v1/stacks/results.go +++ b/openstack/orchestration/v1/stacks/results.go @@ -63,17 +63,38 @@ func (r *ListedStack) UnmarshalJSON(b []byte) error { type tmp ListedStack var s struct { tmp - CreationTime gophercloud.JSONRFC3339NoZ `json:"creation_time"` - UpdatedTime gophercloud.JSONRFC3339NoZ `json:"updated_time"` + CreationTime string `json:"creation_time"` + UpdatedTime string `json:"updated_time"` } + err := json.Unmarshal(b, &s) if err != nil { return err } + *r = ListedStack(s.tmp) - r.CreationTime = time.Time(s.CreationTime) - r.UpdatedTime = time.Time(s.UpdatedTime) + if s.CreationTime != "" { + t, err := time.Parse(time.RFC3339, s.CreationTime) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.CreationTime) + if err != nil { + return err + } + } + r.CreationTime = t + } + + if s.UpdatedTime != "" { + t, err := time.Parse(time.RFC3339, s.UpdatedTime) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.UpdatedTime) + if err != nil { + return err + } + } + r.UpdatedTime = t + } return nil } @@ -112,17 +133,38 @@ func (r *RetrievedStack) UnmarshalJSON(b []byte) error { type tmp RetrievedStack var s struct { tmp - CreationTime gophercloud.JSONRFC3339NoZ `json:"creation_time"` - UpdatedTime gophercloud.JSONRFC3339NoZ `json:"updated_time"` + CreationTime string `json:"creation_time"` + UpdatedTime string `json:"updated_time"` } + err := json.Unmarshal(b, &s) if err != nil { return err } + *r = RetrievedStack(s.tmp) - r.CreationTime = time.Time(s.CreationTime) - r.UpdatedTime = time.Time(s.UpdatedTime) + if s.CreationTime != "" { + t, err := time.Parse(time.RFC3339, s.CreationTime) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.CreationTime) + if err != nil { + return err + } + } + r.CreationTime = t + } + + if s.UpdatedTime != "" { + t, err := time.Parse(time.RFC3339, s.UpdatedTime) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.UpdatedTime) + if err != nil { + return err + } + } + r.UpdatedTime = t + } return nil } @@ -173,17 +215,38 @@ func (r *PreviewedStack) UnmarshalJSON(b []byte) error { type tmp PreviewedStack var s struct { tmp - CreationTime gophercloud.JSONRFC3339NoZ `json:"creation_time"` - UpdatedTime gophercloud.JSONRFC3339NoZ `json:"updated_time"` + CreationTime string `json:"creation_time"` + UpdatedTime string `json:"updated_time"` } + err := json.Unmarshal(b, &s) if err != nil { return err } + *r = PreviewedStack(s.tmp) - r.CreationTime = time.Time(s.CreationTime) - r.UpdatedTime = time.Time(s.UpdatedTime) + if s.CreationTime != "" { + t, err := time.Parse(time.RFC3339, s.CreationTime) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.CreationTime) + if err != nil { + return err + } + } + r.CreationTime = t + } + + if s.UpdatedTime != "" { + t, err := time.Parse(time.RFC3339, s.UpdatedTime) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.UpdatedTime) + if err != nil { + return err + } + } + r.UpdatedTime = t + } return nil } diff --git a/openstack/orchestration/v1/stacks/testing/fixtures.go b/openstack/orchestration/v1/stacks/testing/fixtures.go index 7c8a2f32d2..16c274da16 100644 --- a/openstack/orchestration/v1/stacks/testing/fixtures.go +++ b/openstack/orchestration/v1/stacks/testing/fixtures.go @@ -12,6 +12,9 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +var Create_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z") +var Updated_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:59:17Z") + // CreateExpected represents the expected object from a Create request. var CreateExpected = &stacks.CreatedStack{ ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", @@ -61,7 +64,7 @@ var ListExpected = []stacks.ListedStack{ }, StatusReason: "Stack CREATE completed successfully", Name: "postman_stack", - CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC), + CreationTime: Create_time, Status: "CREATE_COMPLETE", ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", Tags: []string{"rackspace", "atx"}, @@ -76,8 +79,8 @@ var ListExpected = []stacks.ListedStack{ }, StatusReason: "Stack successfully updated", Name: "gophercloud-test-stack-2", - CreationTime: time.Date(2014, 12, 11, 17, 39, 16, 0, time.UTC), - UpdatedTime: time.Date(2014, 12, 11, 17, 40, 37, 0, time.UTC), + CreationTime: Create_time, + UpdatedTime: Updated_time, Status: "UPDATE_COMPLETE", ID: "db6977b2-27aa-4775-9ae7-6213212d4ada", Tags: []string{"sfo", "satx"}, @@ -98,7 +101,7 @@ const FullListOutput = ` ], "stack_status_reason": "Stack CREATE completed successfully", "stack_name": "postman_stack", - "creation_time": "2015-02-03T20:07:39", + "creation_time": "2018-06-26T07:58:17Z", "updated_time": null, "stack_status": "CREATE_COMPLETE", "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87", @@ -114,8 +117,8 @@ const FullListOutput = ` ], "stack_status_reason": "Stack successfully updated", "stack_name": "gophercloud-test-stack-2", - "creation_time": "2014-12-11T17:39:16", - "updated_time": "2014-12-11T17:40:37", + "creation_time": "2018-06-26T07:58:17Z", + "updated_time": "2018-06-26T07:59:17Z", "stack_status": "UPDATE_COMPLETE", "id": "db6977b2-27aa-4775-9ae7-6213212d4ada", "tags": ["sfo", "satx"] @@ -158,7 +161,7 @@ var GetExpected = &stacks.RetrievedStack{ StatusReason: "Stack CREATE completed successfully", Name: "postman_stack", Outputs: []map[string]interface{}{}, - CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC), + CreationTime: Create_time, Links: []gophercloud.Link{ { Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", @@ -187,7 +190,7 @@ const GetOutput = ` "stack_status_reason": "Stack CREATE completed successfully", "stack_name": "postman_stack", "outputs": [], - "creation_time": "2015-02-03T20:07:39", + "creation_time": "2018-06-26T07:58:17Z", "links": [ { "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", @@ -269,7 +272,7 @@ var PreviewExpected = &stacks.PreviewedStack{ "OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87", }, Name: "postman_stack", - CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC), + CreationTime: Create_time, Links: []gophercloud.Link{ { Href: "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87", diff --git a/openstack/orchestration/v1/stacks/testing/requests_test.go b/openstack/orchestration/v1/stacks/testing/requests_test.go index f8bd1fe201..8acf978ad7 100644 --- a/openstack/orchestration/v1/stacks/testing/requests_test.go +++ b/openstack/orchestration/v1/stacks/testing/requests_test.go @@ -39,6 +39,29 @@ func TestCreateStack(t *testing.T) { th.AssertDeepEquals(t, expected, actual) } +func TestCreateStackMissingRequiredInOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t, CreateOutput) + template := new(stacks.Template) + template.Bin = []byte(` + { + "heat_template_version": "2013-05-23", + "description": "Simple template to test heat commands", + "parameters": { + "flavor": { + "default": "m1.tiny", + "type": "string" + } + } + }`) + createOpts := stacks.CreateOpts{ + DisableRollback: gophercloud.Disabled, + } + r := stacks.Create(fake.ServiceClient(), createOpts) + th.AssertEquals(t, "Missing input for argument [Name]", r.Err.Error()) +} + func TestAdoptStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/orchestration/v1/stacktemplates/doc.go b/openstack/orchestration/v1/stacktemplates/doc.go index 5af0bd62a1..52fe62096b 100644 --- a/openstack/orchestration/v1/stacktemplates/doc.go +++ b/openstack/orchestration/v1/stacktemplates/doc.go @@ -1,8 +1,38 @@ -// Package stacktemplates provides operations for working with Heat templates. -// A Cloud Orchestration template is a portable file, written in a user-readable -// language, that describes how a set of resources should be assembled and what -// software should be installed in order to produce a working stack. The template -// specifies what resources should be used, what attributes can be set, and other -// parameters that are critical to the successful, repeatable automation of a -// specific application stack. +/* +Package stacktemplates provides operations for working with Heat templates. +A Cloud Orchestration template is a portable file, written in a user-readable +language, that describes how a set of resources should be assembled and what +software should be installed in order to produce a working stack. The template +specifies what resources should be used, what attributes can be set, and other +parameters that are critical to the successful, repeatable automation of a +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)) + +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) + } + +*/ package stacktemplates diff --git a/script/acceptancetest b/script/acceptancetest index 0541c8f497..02b3f6740b 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -52,6 +52,13 @@ if [[ $? != 0 ]]; then failed=1 fi +# Orchestration v1 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/orchestration/v1/ +if [[ $? != 0 ]]; then + failed=1 +fi + + # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then exit 1 From fb7c8e157b19d5f4b8ecd3dd9dc24823f2fff8cb Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Mon, 27 Aug 2018 11:10:21 +0200 Subject: [PATCH 0524/2296] Delete workflows --- acceptance/openstack/workflow/v2/workflow.go | 10 ++++++++++ .../openstack/workflow/v2/workflows_test.go | 3 ++- openstack/workflow/v2/workflows/requests.go | 6 ++++++ openstack/workflow/v2/workflows/results.go | 5 +++++ .../v2/workflows/testing/requests_test.go | 15 +++++++++++++++ openstack/workflow/v2/workflows/urls.go | 4 ++++ 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/workflow/v2/workflow.go b/acceptance/openstack/workflow/v2/workflow.go index 12f1cf934e..0fb8a4386d 100644 --- a/acceptance/openstack/workflow/v2/workflow.go +++ b/acceptance/openstack/workflow/v2/workflow.go @@ -53,3 +53,13 @@ func CreateWorkflow(t *testing.T, client *gophercloud.ServiceClient) (*workflows return &workflow, nil } + +// DeleteWorkflow deletes the given workflow. +func DeleteWorkflow(t *testing.T, client *gophercloud.ServiceClient, workflow *workflows.Workflow) { + err := workflows.Delete(client, workflow.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete workflows %s: %v", workflow.Name, err) + } + + t.Logf("Deleted workflow: %s", workflow.Name) +} diff --git a/acceptance/openstack/workflow/v2/workflows_test.go b/acceptance/openstack/workflow/v2/workflows_test.go index f8bda024c4..1d4159f878 100644 --- a/acceptance/openstack/workflow/v2/workflows_test.go +++ b/acceptance/openstack/workflow/v2/workflows_test.go @@ -8,12 +8,13 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestWorkflowsCreate(t *testing.T) { +func TestWorkflowsCreateDelete(t *testing.T) { client, err := clients.NewWorkflowV2Client() th.AssertNoErr(t, err) workflow, err := CreateWorkflow(t, client) th.AssertNoErr(t, err) + defer DeleteWorkflow(t, client, workflow) tools.PrintResource(t, workflow) } diff --git a/openstack/workflow/v2/workflows/requests.go b/openstack/workflow/v2/workflows/requests.go index 34bcabe223..a9df08ddf5 100644 --- a/openstack/workflow/v2/workflows/requests.go +++ b/openstack/workflow/v2/workflows/requests.go @@ -54,3 +54,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } + +// Delete deletes the specified execution. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/workflow/v2/workflows/results.go b/openstack/workflow/v2/workflows/results.go index cccd591794..00425e96cb 100644 --- a/openstack/workflow/v2/workflows/results.go +++ b/openstack/workflow/v2/workflows/results.go @@ -12,6 +12,11 @@ type CreateResult struct { gophercloud.Result } +// DeleteResult is the result from a Delete operation. Call its ExtractErr method to determine the success of the call. +type DeleteResult struct { + gophercloud.ErrResult +} + // Extract helps to get created Workflow struct from a Create function. func (r CreateResult) Extract() ([]Workflow, error) { var s struct { diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index e521073ee7..005b2f2fcb 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -88,3 +88,18 @@ simple_echo: t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestDeleteWorkflow(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/workflows/1", 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) + }) + + res := workflows.Delete(fake.ServiceClient(), "1") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/workflow/v2/workflows/urls.go b/openstack/workflow/v2/workflows/urls.go index aca6dfe792..e3745688a1 100644 --- a/openstack/workflow/v2/workflows/urls.go +++ b/openstack/workflow/v2/workflows/urls.go @@ -7,3 +7,7 @@ import ( func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("workflows") } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("workflows", id) +} From b0494d9592adfe91ac0fb3718b2031e275149bdd Mon Sep 17 00:00:00 2001 From: James McCarthy Date: Thu, 6 Sep 2018 12:42:37 +0000 Subject: [PATCH 0525/2296] Fix blockstorage multiattach typo (the letters t and l were transposed) --- 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 9f5361195e..11109673c8 100644 --- a/openstack/blockstorage/extensions/schedulerstats/results.go +++ b/openstack/blockstorage/extensions/schedulerstats/results.go @@ -29,7 +29,7 @@ type Capabilities struct { TotalVolumes int64 `json:"total_volumes"` FilterFunction string `json:"filter_function"` GoodnessFuction string `json:"goodness_function"` - Mutliattach bool `json:"multiattach"` + Multiattach bool `json:"multiattach"` SparseCopyVolume bool `json:"sparse_copy_volume"` } From 7bea8f54a42cbff9b3e92464810492f1d534b0ea Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Fri, 7 Sep 2018 10:10:06 +0200 Subject: [PATCH 0526/2296] Get workflows --- acceptance/openstack/workflow/v2/workflow.go | 10 +++++ .../openstack/workflow/v2/workflows_test.go | 7 ++- openstack/workflow/v2/workflows/requests.go | 7 +++ openstack/workflow/v2/workflows/results.go | 12 ++++++ .../v2/workflows/testing/requests_test.go | 43 +++++++++++++++++++ openstack/workflow/v2/workflows/urls.go | 4 ++ 6 files changed, 81 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/workflow/v2/workflow.go b/acceptance/openstack/workflow/v2/workflow.go index 0fb8a4386d..ee00496793 100644 --- a/acceptance/openstack/workflow/v2/workflow.go +++ b/acceptance/openstack/workflow/v2/workflow.go @@ -63,3 +63,13 @@ func DeleteWorkflow(t *testing.T, client *gophercloud.ServiceClient, workflow *w t.Logf("Deleted workflow: %s", workflow.Name) } + +// GetWorkflow gets a workflow. +func GetWorkflow(t *testing.T, client *gophercloud.ServiceClient, id string) (*workflows.Workflow, error) { + workflow, err := workflows.Get(client, id).Extract() + if err != nil { + t.Fatalf("Unable to get workflow %s: %v", id, err) + } + t.Logf("Workflow get: %s", workflow.Name) + return workflow, err +} diff --git a/acceptance/openstack/workflow/v2/workflows_test.go b/acceptance/openstack/workflow/v2/workflows_test.go index 1d4159f878..b0a0107b90 100644 --- a/acceptance/openstack/workflow/v2/workflows_test.go +++ b/acceptance/openstack/workflow/v2/workflows_test.go @@ -8,7 +8,7 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestWorkflowsCreateDelete(t *testing.T) { +func TestWorkflowsCreateGetDelete(t *testing.T) { client, err := clients.NewWorkflowV2Client() th.AssertNoErr(t, err) @@ -16,5 +16,8 @@ func TestWorkflowsCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteWorkflow(t, client, workflow) - tools.PrintResource(t, workflow) + workflowget, err := GetWorkflow(t, client, workflow.ID) + th.AssertNoErr(t, err) + + tools.PrintResource(t, workflowget) } diff --git a/openstack/workflow/v2/workflows/requests.go b/openstack/workflow/v2/workflows/requests.go index a9df08ddf5..77442ff10e 100644 --- a/openstack/workflow/v2/workflows/requests.go +++ b/openstack/workflow/v2/workflows/requests.go @@ -60,3 +60,10 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } + +// Get retrieves details of a single execution. +// Use ExtractWorkflow to convert its result into an Workflow. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/workflow/v2/workflows/results.go b/openstack/workflow/v2/workflows/results.go index 00425e96cb..ac149968a3 100644 --- a/openstack/workflow/v2/workflows/results.go +++ b/openstack/workflow/v2/workflows/results.go @@ -26,6 +26,18 @@ func (r CreateResult) Extract() ([]Workflow, error) { return s.Workflows, err } +// GetResult is the response of Get operations. Call its Extract method to interpret it as a Workflow. +type GetResult struct { + gophercloud.Result +} + +// Extract helps to get a Workflow struct from a Get function. +func (r GetResult) Extract() (*Workflow, error) { + var s Workflow + err := r.ExtractInto(&s) + return &s, err +} + // Workflow represents a workflow execution on OpenStack mistral API. type Workflow struct { // ID is the workflow's unique ID. diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index 005b2f2fcb..3905c9f0c0 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -103,3 +103,46 @@ func TestDeleteWorkflow(t *testing.T) { res := workflows.Delete(fake.ServiceClient(), "1") th.AssertNoErr(t, res.Err) } + +func TestGetWorkflow(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/workflows/1", 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, ` + { + "created_at": "1970-01-01 00:00:00", + "definition": "Workflow Definition in Mistral DSL v2", + "id": "1", + "input": "param1, param2", + "name": "flow", + "namespace": "some-namespace", + "project_id": "p1", + "scope": "private", + "updated_at": "1970-01-01 00:00:00" + } + `) + }) + actual, err := workflows.Get(fake.ServiceClient(), "1").Extract() + if err != nil { + t.Fatalf("Unable to get workflow: %v", err) + } + + updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + expected := &workflows.Workflow{ + ID: "1", + Definition: "Workflow Definition in Mistral DSL v2", + Name: "flow", + Namespace: "some-namespace", + Input: "param1, param2", + ProjectID: "p1", + Scope: "private", + CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), + UpdatedAt: &updated, + } + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/workflow/v2/workflows/urls.go b/openstack/workflow/v2/workflows/urls.go index e3745688a1..edcfaa71fc 100644 --- a/openstack/workflow/v2/workflows/urls.go +++ b/openstack/workflow/v2/workflows/urls.go @@ -11,3 +11,7 @@ func createURL(client *gophercloud.ServiceClient) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("workflows", id) } + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("workflows", id) +} From bbd99443d97d9a34a2e05df365d9bfbe608014c4 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Mon, 10 Sep 2018 16:45:00 +0200 Subject: [PATCH 0527/2296] List workflows --- acceptance/openstack/workflow/v2/workflow.go | 14 +++++ .../openstack/workflow/v2/workflows_test.go | 15 +++++ openstack/workflow/v2/workflows/doc.go | 11 +++- openstack/workflow/v2/workflows/requests.go | 54 ++++++++++++++++- openstack/workflow/v2/workflows/results.go | 33 +++++++++++ .../v2/workflows/testing/requests_test.go | 59 +++++++++++++++++++ openstack/workflow/v2/workflows/urls.go | 4 ++ 7 files changed, 188 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/workflow/v2/workflow.go b/acceptance/openstack/workflow/v2/workflow.go index ee00496793..1643d3e47b 100644 --- a/acceptance/openstack/workflow/v2/workflow.go +++ b/acceptance/openstack/workflow/v2/workflow.go @@ -73,3 +73,17 @@ func GetWorkflow(t *testing.T, client *gophercloud.ServiceClient, id string) (*w t.Logf("Workflow get: %s", workflow.Name) return workflow, err } + +// ListWorkflows lists the workflows. +func ListWorkflows(t *testing.T, client *gophercloud.ServiceClient, opts workflows.ListOptsBuilder) ([]workflows.Workflow, error) { + allPages, err := workflows.List(client, opts).AllPages() + if err != nil { + t.Fatalf("Unable to list workflows: %v", err) + } + workflowsList, err := workflows.ExtractWorkflows(allPages) + if err != nil { + t.Fatalf("Unable to extract workflows: %v", err) + } + t.Logf("Workflows list find, length: %d", len(workflowsList)) + return workflowsList, err +} diff --git a/acceptance/openstack/workflow/v2/workflows_test.go b/acceptance/openstack/workflow/v2/workflows_test.go index b0a0107b90..f0de6708cd 100644 --- a/acceptance/openstack/workflow/v2/workflows_test.go +++ b/acceptance/openstack/workflow/v2/workflows_test.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -21,3 +22,17 @@ func TestWorkflowsCreateGetDelete(t *testing.T) { tools.PrintResource(t, workflowget) } + +func TestWorkflowsList(t *testing.T) { + client, err := clients.NewWorkflowV2Client() + th.AssertNoErr(t, err) + workflow, err := CreateWorkflow(t, client) + th.AssertNoErr(t, err) + defer DeleteWorkflow(t, client, workflow) + list, err := ListWorkflows(t, client, &workflows.ListOpts{ + Name: workflow.Name, + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(list)) + tools.PrintResource(t, list) +} diff --git a/openstack/workflow/v2/workflows/doc.go b/openstack/workflow/v2/workflows/doc.go index a71c82df80..93a92e8b0c 100644 --- a/openstack/workflow/v2/workflows/doc.go +++ b/openstack/workflow/v2/workflows/doc.go @@ -24,6 +24,15 @@ Example to list workflows fmt.Printf("%+v\n", workflow) } +Example to get a workflow by its ID + + workflow, err := workflows.Get(mistralClient, "workflow-id").Extract() + if err != nil { + t.Fatalf("Unable to get workflow %s: %v", id, err) + } + + fmt.Printf("%+v\n", workflow) + Example to create a workflow workflowDefinition := `--- @@ -59,7 +68,7 @@ tasks: Namespace: "some-namespace", } - execution, err := workflows.Create(fake.ServiceClient(), opts).Extract() + execution, err := workflows.Create(mistralClient, opts).Extract() if err != nil { panic(err) } diff --git a/openstack/workflow/v2/workflows/requests.go b/openstack/workflow/v2/workflows/requests.go index 77442ff10e..a703fa66a9 100644 --- a/openstack/workflow/v2/workflows/requests.go +++ b/openstack/workflow/v2/workflows/requests.go @@ -4,6 +4,7 @@ import ( "io" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extension to add additional parameters to the Create request. @@ -62,8 +63,59 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { } // Get retrieves details of a single execution. -// Use ExtractWorkflow to convert its result into an Workflow. +// Use Extract to convert its result into an Workflow. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } + +// ListOptsBuilder allows extension to add additional parameters to the List request. +type ListOptsBuilder interface { + ToWorkflowListQuery() (string, error) +} + +// ListOpts filters the result returned by the List() function. +type ListOpts struct { + // Name allows to filter by workflow name. + Name string `q:"name"` + // Namespace allows to filter by workflow namespace. + Namespace string `q:"namespace"` + // Definition allows to filter by workflow definition. + Definition string `q:"definition"` + // Scope filters by the workflow's scope. + // Values can be "private" or "public". + Scope string `q:"scope"` + // SortDir allows to select sort direction. + // It can be "asc" or "desc" (default). + SortDir string `q:"sort_dir"` + // SortKey allows to sort by one of the cron trigger attributes. + SortKey string `q:"sort_key"` + // Marker and Limit control paging. + // Marker instructs List where to start listing from. + Marker string `q:"marker"` + // Limit instructs List to refrain from sending excessively large lists of + // cron triggers. + Limit int `q:"limit"` +} + +// ToWorkflowListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToWorkflowListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List performs a call to list cron triggers. +// You may provide options to filter the results. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToWorkflowListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return WorkflowPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/workflow/v2/workflows/results.go b/openstack/workflow/v2/workflows/results.go index ac149968a3..1e4803e877 100644 --- a/openstack/workflow/v2/workflows/results.go +++ b/openstack/workflow/v2/workflows/results.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateResult is the response of a Post operations. Call its Extract method to interpret it as a list of Workflows. @@ -97,3 +98,35 @@ func (r *Workflow) UnmarshalJSON(b []byte) error { return nil } + +// WorkflowPage contains a single page of all workflows from a List call. +type WorkflowPage struct { + pagination.LinkedPageBase +} + +// IsEmpty checks if an WorkflowPage contains any results. +func (r WorkflowPage) IsEmpty() (bool, error) { + exec, err := ExtractWorkflows(r) + return len(exec) == 0, err +} + +// NextPageURL finds the next page URL in a page in order to navigate to the next page of results. +func (r WorkflowPage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Next, nil +} + +// ExtractWorkflows get the list of cron triggers from a page acquired from the List call. +func ExtractWorkflows(r pagination.Page) ([]Workflow, error) { + var s struct { + Workflows []Workflow `json:"workflows"` + } + err := (r.(WorkflowPage)).ExtractInto(&s) + return s.Workflows, err +} diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index 3905c9f0c0..08a9d17a56 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -146,3 +147,61 @@ func TestGetWorkflow(t *testing.T) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestListWorkflows(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/workflows", 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") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, `{ + "next": "%s/workflows?marker=1", + "workflows": [ + { + "created_at": "1970-01-01 00:00:00", + "definition": "Workflow Definition in Mistral DSL v2", + "id": "1", + "input": "param1, param2", + "name": "flow", + "namespace": "some-namespace", + "project_id": "p1", + "scope": "private", + "updated_at": "1970-01-01 00:00:00" + } + ] + }`, th.Server.URL) + case "1": + fmt.Fprintf(w, `{ "workflows": [] }`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) + pages := 0 + // Get all workflows + err := workflows.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + pages++ + actual, err := workflows.ExtractWorkflows(page) + if err != nil { + return false, err + } + updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + expected := []workflows.Workflow{ + {ID: "1", Definition: "Workflow Definition in Mistral DSL v2", Name: "flow", Namespace: "some-namespace", Input: "param1, param2", ProjectID: "p1", Scope: "private", CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt: &updated}, + } + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } + return true, nil + }) + if err != nil { + t.Fatal(err) + } + if pages != 1 { + t.Errorf("Expected one page, got %d", pages) + } +} diff --git a/openstack/workflow/v2/workflows/urls.go b/openstack/workflow/v2/workflows/urls.go index edcfaa71fc..6c3f80d4b8 100644 --- a/openstack/workflow/v2/workflows/urls.go +++ b/openstack/workflow/v2/workflows/urls.go @@ -15,3 +15,7 @@ func deleteURL(client *gophercloud.ServiceClient, id string) string { func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("workflows", id) } + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("workflows") +} From d3d2712922bfab7d32b0c1c1a9fe539a17138930 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Mon, 10 Sep 2018 18:56:07 -0700 Subject: [PATCH 0528/2296] Magnum: Manage Clusters Update (#1218) * magnum-clusters-update * Removed result named parameters Extract() for CreateResult and UpdateResult --- .../containerinfra/v1/clusters_test.go | 25 ++++++++++ openstack/containerinfra/v1/clusters/doc.go | 20 ++++++++ .../containerinfra/v1/clusters/requests.go | 49 +++++++++++++++++++ .../containerinfra/v1/clusters/results.go | 25 +++++++--- .../v1/clusters/testing/fixtures.go | 18 +++++++ .../v1/clusters/testing/requests_test.go | 33 +++++++++++++ openstack/containerinfra/v1/clusters/urls.go | 8 +++ 7 files changed, 172 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index fe5cc4c911..4292e48874 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -35,4 +35,29 @@ func TestClustersCRUD(t *testing.T) { } } th.AssertEquals(t, found, true) + updateOpts := []clusters.UpdateOptsBuilder{ + clusters.UpdateOpts{ + Op: clusters.ReplaceOp, + Path: "/node_count", + Value: "2", + }, + } + updateResult := clusters.Update(client, clusterID, updateOpts) + th.AssertNoErr(t, updateResult.Err) + + if len(updateResult.Header["X-Openstack-Request-Id"]) > 0 { + t.Logf("Cluster Update Request ID: %s", updateResult.Header["X-Openstack-Request-Id"][0]) + } + + clusterID, err = updateResult.Extract() + th.AssertNoErr(t, err) + + err = WaitForCluster(client, clusterID, "SUCCESS") + th.AssertNoErr(t, err) + + newCluster, err := clusters.Get(client, clusterID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newCluster.UUID, clusterID) + + tools.PrintResource(t, newCluster) } diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index 2a0a2bac5c..94a411d027 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -53,5 +53,25 @@ Example to List Clusters fmt.Printf("%+v\n", cluster) } +Example to Update a Cluster + + updateOpts := []clusters.UpdateOptsBuilder{ + clusters.UpdateOpts{ + Op: clusters.ReplaceOp, + Path: "/master_lb_enabled", + Value: "True", + }, + clusters.UpdateOpts{ + Op: clusters.ReplaceOp, + Path: "/registry_enabled", + Value: "True", + }, + } + clusterUUID, err := clusters.Update(serviceClient, clusterUUID, updateOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%s\n", clusterUUID) + */ package clusters diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index b1b4ec7ec9..4a1ce3018f 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -100,3 +100,52 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { return ClusterPage{pagination.LinkedPageBase{PageResult: r}} }) } + +type UpdateOp string + +const ( + AddOp UpdateOp = "add" + RemoveOp UpdateOp = "remove" + ReplaceOp UpdateOp = "replace" +) + +type UpdateOpts struct { + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value string `json:"value,omitempty"` +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToClustersUpdateMap() (map[string]interface{}, error) +} + +// ToClusterUpdateMap assembles a request body based on the contents of +// UpdateOpts. +func (opts UpdateOpts) ToClustersUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update implements cluster updated request. +func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { + var o []map[string]interface{} + for _, opt := range opts { + b, err := opt.ToClustersUpdateMap() + if err != nil { + r.Err = err + return r + } + o = append(o, b) + } + + var result *http.Response + result, r.Err = client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 11e648227a..71d9ad38f6 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -21,19 +21,32 @@ type GetResult struct { commonResult } -func (r CreateResult) Extract() (clusterID string, err error) { +// Extract is a function that accepts a result and extracts a cluster resource. +func (r commonResult) Extract() (*Cluster, error) { + var s *Cluster + err := r.ExtractInto(&s) + return s, err +} + +// UpdateResult is the response of a Update operations. +type UpdateResult struct { + commonResult +} + +func (r CreateResult) Extract() (string, error) { var s struct { UUID string } - err = r.ExtractInto(&s) + err := r.ExtractInto(&s) return s.UUID, err } -// Extract is a function that accepts a result and extracts a cluster resource. -func (r commonResult) Extract() (*Cluster, error) { - var s *Cluster +func (r UpdateResult) Extract() (string, error) { + var s struct { + UUID string + } err := r.ExtractInto(&s) - return s, err + return s.UUID, err } type Cluster struct { diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index 357b404d58..b1697c9787 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -229,3 +229,21 @@ func HandleListClusterSuccessfully(t *testing.T) { fmt.Fprint(w, ClusterListResponse) }) } + +var UpdateResponse = fmt.Sprintf(` +{ + "uuid":"%s" +}`, clusterUUID) + +func HandleUpdateClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-Id", requestUUID) + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, UpdateResponse) + }) +} diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index 2fd41ef19c..f0708483e4 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -86,3 +86,36 @@ func TestListClusters(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestUpdateCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleUpdateClusterSuccessfully(t) + + updateOpts := []clusters.UpdateOptsBuilder{ + clusters.UpdateOpts{ + Op: clusters.ReplaceOp, + Path: "/master_lb_enabled", + Value: "True", + }, + clusters.UpdateOpts{ + Op: clusters.ReplaceOp, + Path: "/registry_enabled", + Value: "True", + }, + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + res := clusters.Update(sc, clusterUUID, updateOpts) + th.AssertNoErr(t, res.Err) + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, requestUUID, requestID) + + actual, err := res.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, clusterUUID, actual) +} diff --git a/openstack/containerinfra/v1/clusters/urls.go b/openstack/containerinfra/v1/clusters/urls.go index d2a36bdb25..21036aec90 100644 --- a/openstack/containerinfra/v1/clusters/urls.go +++ b/openstack/containerinfra/v1/clusters/urls.go @@ -10,6 +10,10 @@ func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } +func idURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiName, id) +} + func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } @@ -21,3 +25,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("clusters") } + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From ab26b4981fb978f41999e13d11351fceea7c586d Mon Sep 17 00:00:00 2001 From: Jesper Schmitz Mouridsen Date: Wed, 12 Sep 2018 03:52:30 +0200 Subject: [PATCH 0529/2296] Networking v2: Security Group Rule Description (#1231) * Add description field to security group rule * Trying to fix testing * Fix whitespace --- openstack/networking/v2/extensions/security/rules/requests.go | 3 +++ openstack/networking/v2/extensions/security/rules/results.go | 3 +++ .../v2/extensions/security/rules/testing/requests_test.go | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 96cce2817d..2a0a3bd45c 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -88,6 +88,9 @@ type CreateOpts struct { // group rule is applied. Direction RuleDirection `json:"direction" required:"true"` + // String description of each rule, optional + Description string `json:"description" required:"false"` + // Must be "IPv4" or "IPv6", and addresses represented in CIDR must match the // ingress or egress rules. EtherType RuleEtherType `json:"ethertype" required:"true"` diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index 377e753140..3bf5501d92 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -17,6 +17,9 @@ type SecGroupRule struct { // instance. An egress rule is applied to traffic leaving the instance. Direction string + // Descripton of the rule + Description string `json:"description"` + // Must be IPv4 or IPv6, and addresses represented in CIDR must match the // ingress or egress rules. EtherType string `json:"ethertype"` diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 968fd04d8f..8d0edabb81 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -66,6 +66,7 @@ func TestList(t *testing.T) { expected := []rules.SecGroupRule{ { + Description: "", Direction: "egress", EtherType: "IPv6", ID: "3c0e45ff-adaf-4124-b083-bf390e5482ff", @@ -113,6 +114,7 @@ func TestCreate(t *testing.T) { th.TestJSONRequest(t, r, ` { "security_group_rule": { + "description": "test description of rule", "direction": "ingress", "port_range_min": 80, "ethertype": "IPv4", @@ -130,6 +132,7 @@ func TestCreate(t *testing.T) { fmt.Fprintf(w, ` { "security_group_rule": { + "description": "test description of rule", "direction": "ingress", "ethertype": "IPv4", "id": "2bc0accf-312e-429a-956e-e4407625eb62", @@ -146,6 +149,7 @@ func TestCreate(t *testing.T) { }) opts := rules.CreateOpts{ + Description: "test description of rule", Direction: "ingress", PortRangeMin: 80, EtherType: rules.EtherType4, From a5e4374c99156e0f98429d363ace306d8f9fff6d Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Wed, 12 Sep 2018 16:46:39 +1200 Subject: [PATCH 0530/2296] Support batch update members (#1229) * Support batch update members * Refactor BatchUpdateMembers method * Fix the acceptance test --- .../loadbalancer/v2/loadbalancers_test.go | 22 +++++++ openstack/loadbalancer/v2/pools/doc.go | 25 ++++++++ openstack/loadbalancer/v2/pools/requests.go | 34 ++++++++++- openstack/loadbalancer/v2/pools/results.go | 6 ++ .../loadbalancer/v2/pools/testing/fixtures.go | 30 ++++++++++ .../v2/pools/testing/requests_test.go | 59 +++++++++++++++++++ 6 files changed, 174 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 1599697a64..54d5698a7e 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -235,6 +235,28 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newMember) + newWeight = tools.RandomInt(11, 100) + memberOpts := pools.BatchUpdateMemberOpts{ + Address: member.Address, + ProtocolPort: member.ProtocolPort, + Weight: newWeight, + } + batchMembers := []pools.BatchUpdateMemberOpts{memberOpts} + if err := pools.BatchUpdateMembers(lbClient, pool.ID, batchMembers).ExtractErr(); err != nil { + t.Fatalf("Unable to batch update members") + } + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newMember, err = pools.GetMember(lbClient, pool.ID, member.ID).Extract() + if err != nil { + t.Fatalf("Unable to get member") + } + + tools.PrintResource(t, newMember) + // Monitor monitor, err := CreateMonitor(t, lbClient, lb, newPool) if err != nil { diff --git a/openstack/loadbalancer/v2/pools/doc.go b/openstack/loadbalancer/v2/pools/doc.go index 2d57ed4393..0135e60d7b 100644 --- a/openstack/loadbalancer/v2/pools/doc.go +++ b/openstack/loadbalancer/v2/pools/doc.go @@ -120,5 +120,30 @@ Example to Delete a Member if err != nil { panic(err) } + +Example to Update Members: + + poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + + member1 := pools.BatchUpdateMemberOpts{ + Address: "192.0.2.16", + ProtocolPort: 80, + Name: "web-server-1", + SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + Weight: 20, + } + member2 := pools.BatchUpdateMemberOpts{ + Address: "192.0.2.17", + ProtocolPort: 80, + Name: "web-server-2", + Weight: 10, + SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + } + members := []pools.BatchUpdateMemberOpts{member1, member2} + + err := pools.BatchUpdateMembers(networkClient, poolID, members).ExtractErr() + if err != nil { + panic(err) + } */ package pools diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 538b2f1b2a..6c8dfd3994 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -261,8 +261,8 @@ type CreateMemberOpts struct { ProjectID string `json:"project_id,omitempty"` // A positive integer value that indicates the relative portion of traffic - // that this member should receive from the pool. For example, a member with - // a weight of 10 receives five times as much traffic as a member with a + // that this member should receive from the pool. For example, a member with + // a weight of 10 receives five times as much traffic as a member with a // weight of 2. Weight int `json:"weight,omitempty"` @@ -338,6 +338,36 @@ func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, return } +// BatchUpdateMemberOptsBuilder allows extensions to add additional parameters to the BatchUpdateMembers request. +type BatchUpdateMemberOptsBuilder interface { + ToBatchMemberUpdateMap() (map[string]interface{}, error) +} + +type BatchUpdateMemberOpts CreateMemberOpts + +// ToBatchMemberUpdateMap builds a request body from BatchUpdateMemberOpts. +func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// BatchUpdateMembers updates the pool members in batch +func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []BatchUpdateMemberOpts) (r UpdateMembersResult) { + var members []map[string]interface{} + for _, opt := range opts { + b, err := opt.ToBatchMemberUpdateMap() + if err != nil { + r.Err = err + return + } + members = append(members, b) + } + + b := map[string]interface{}{"members": members} + + _, r.Err = c.Put(memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + return +} + // DisassociateMember will remove and disassociate a Member from a particular // Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index fe0d93325f..52935b4f1d 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -274,6 +274,12 @@ type UpdateMemberResult struct { commonMemberResult } +// UpdateMembersResult represents the result of an UpdateMembers operation. +// Call its ExtractErr method to determine if the request succeeded or failed. +type UpdateMembersResult struct { + gophercloud.ErrResult +} + // DeleteMemberResult represents the result of a DeleteMember operation. // Call its ExtractErr method to determine if the request succeeded or failed. type DeleteMemberResult struct { diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures.go b/openstack/loadbalancer/v2/pools/testing/fixtures.go index 9d929525d6..8104708282 100644 --- a/openstack/loadbalancer/v2/pools/testing/fixtures.go +++ b/openstack/loadbalancer/v2/pools/testing/fixtures.go @@ -386,3 +386,33 @@ func HandleMemberUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, PostUpdateMemberBody) }) } + +// HandleMembersUpdateSuccessfully sets up the test server to respond to a batch member Update request. +func HandleMembersUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", 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, `{ + "members": [ + { + "name": "web-server-1", + "weight": 20, + "subnet_id": "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + "address": "192.0.2.16", + "protocol_port": 80 + }, + { + "name": "web-server-2", + "weight": 10, + "subnet_id": "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + "address": "192.0.2.17", + "protocol_port": 80 + } + ] + }`) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go index 33c5c73642..4afc89020f 100644 --- a/openstack/loadbalancer/v2/pools/testing/requests_test.go +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -260,3 +260,62 @@ func TestUpdateMember(t *testing.T) { th.CheckDeepEquals(t, MemberUpdated, *actual) } + +func TestBatchUpdateMembers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleMembersUpdateSuccessfully(t) + + member1 := pools.BatchUpdateMemberOpts{ + Address: "192.0.2.16", + ProtocolPort: 80, + Name: "web-server-1", + SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + Weight: 20, + } + member2 := pools.BatchUpdateMemberOpts{ + Address: "192.0.2.17", + ProtocolPort: 80, + Name: "web-server-2", + Weight: 10, + SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + } + members := []pools.BatchUpdateMemberOpts{member1, member2} + + res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", members) + th.AssertNoErr(t, res.Err) +} + +func TestRequiredBatchUpdateMemberOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ + { + Name: "web-server-1", + }, + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + + res = pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ + { + Address: "192.0.2.17", + Name: "web-server-1", + }, + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + + res = pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ + { + ProtocolPort: 80, + Name: "web-server-1", + }, + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } +} From 094932778463719439140621686b0a4446a116d1 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 13 Sep 2018 18:42:38 -0700 Subject: [PATCH 0531/2296] Magnum: Manage Clusters Delete (#1232) * magnum-cluster-delete * Fixed for WaitForCluster() to wait for status of UPDATE_COMPLETE rather than SUCCESS for cluster update --- .../containerinfra/v1/clusters_test.go | 3 ++- .../containerinfra/v1/containerinfra.go | 24 +++++++++++++++++ openstack/containerinfra/v1/clusters/doc.go | 8 ++++++ .../containerinfra/v1/clusters/requests.go | 8 ++++++ .../containerinfra/v1/clusters/results.go | 6 +++++ .../v1/clusters/testing/fixtures.go | 11 ++++++++ .../v1/clusters/testing/requests_test.go | 26 +++++++++++++++++++ openstack/containerinfra/v1/clusters/urls.go | 4 +++ 8 files changed, 89 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index 4292e48874..95d2da005c 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -21,6 +21,7 @@ func TestClustersCRUD(t *testing.T) { clusterID, err := CreateCluster(t, client, clusterTemplate.UUID) tools.PrintResource(t, clusterID) + defer DeleteCluster(t, client, clusterID) allPages, err := clusters.List(client, nil).AllPages() th.AssertNoErr(t, err) @@ -52,7 +53,7 @@ func TestClustersCRUD(t *testing.T) { clusterID, err = updateResult.Extract() th.AssertNoErr(t, err) - err = WaitForCluster(client, clusterID, "SUCCESS") + err = WaitForCluster(client, clusterID, "UPDATE_COMPLETE") th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, clusterID).Extract() diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go index 678f030fad..6d97dee2dc 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -131,6 +131,30 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTempl return clusterID, nil } +func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { + t.Logf("Attempting to delete cluster: %s", id) + + r := clusters.Delete(client, id) + err := clusters.Delete(client, id).ExtractErr() + deleteRequestID := "" + idKey := "X-Openstack-Request-Id" + if len(r.Header[idKey]) > 0 { + deleteRequestID = r.Header[idKey][0] + } + if err != nil { + t.Fatalf("Error deleting cluster. requestID=%s clusterID=%s: err%s:", deleteRequestID, id, err) + } + + err = WaitForCluster(client, id, "DELETE_COMPLETE") + if err != nil { + t.Fatalf("Error deleting cluster %s: %s:", id, err) + } + + t.Logf("Successfully deleted cluster: %s", id) + + return +} + func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status string) error { return tools.WaitFor(func() (bool, error) { cluster, err := clusters.Get(client, clusterID).Extract() diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index 94a411d027..11f7ba6f14 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -73,5 +73,13 @@ Example to Update a Cluster } fmt.Printf("%s\n", clusterUUID) +Example to Delete a Cluster + + clusterUUID := "dc6d336e3fc4c0a951b5698cd1236ee" + err := clusters.Delete(serviceClient, clusterUUID).ExtractErr() + if err != nil { + panic(err) + } + */ package clusters diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 4a1ce3018f..47088c4543 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -61,6 +61,14 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } +// Delete deletes the specified cluster ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + var result *http.Response + result, r.Err = client.Delete(deleteURL(client, id), nil) + r.Header = result.Header + return +} + // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 71d9ad38f6..b386b9be2a 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -16,6 +16,12 @@ type CreateResult struct { commonResult } +// DeleteResult is the result from a Delete operation. Call its Extract or ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // GetResult represents the result of a get operation. type GetResult struct { commonResult diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index b1697c9787..e6baaf8bea 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -247,3 +247,14 @@ func HandleUpdateClusterSuccessfully(t *testing.T) { fmt.Fprint(w, UpdateResponse) }) } + +func HandleDeleteClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID, 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.Header().Add("X-OpenStack-Request-Id", requestUUID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index f0708483e4..a27f08d8d5 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -119,3 +119,29 @@ func TestUpdateCluster(t *testing.T) { th.AssertDeepEquals(t, clusterUUID, actual) } + +func TestDeleteCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleDeleteClusterSuccessfully(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + r := clusters.Delete(sc, clusterUUID) + err := r.ExtractErr() + th.AssertNoErr(t, err) + + uuid := "" + idKey := "X-Openstack-Request-Id" + if len(r.Header[idKey]) > 0 { + uuid = r.Header[idKey][0] + if uuid == "" { + t.Errorf("No value for header [%s]", idKey) + } + } else { + t.Errorf("Missing header [%s]", idKey) + } + + th.AssertEquals(t, requestUUID, uuid) +} diff --git a/openstack/containerinfra/v1/clusters/urls.go b/openstack/containerinfra/v1/clusters/urls.go index 21036aec90..9cc64893e9 100644 --- a/openstack/containerinfra/v1/clusters/urls.go +++ b/openstack/containerinfra/v1/clusters/urls.go @@ -18,6 +18,10 @@ func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} + func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("clusters", id) } From d46a2c66c298c157604c08083d2e0e99a1821092 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Fri, 14 Sep 2018 03:55:32 +0200 Subject: [PATCH 0532/2296] Create Cron triggers (#1210) --- .../openstack/workflow/v2/crontrigger.go | 36 ++++++ .../workflow/v2/crontriggers_test.go | 22 ++++ acceptance/openstack/workflow/v2/workflow.go | 5 +- openstack/workflow/v2/crontriggers/doc.go | 41 +++++++ .../workflow/v2/crontriggers/requests.go | 67 +++++++++++ openstack/workflow/v2/crontriggers/results.go | 109 ++++++++++++++++++ .../v2/crontriggers/testing/requests_test.go | 86 ++++++++++++++ openstack/workflow/v2/crontriggers/urls.go | 7 ++ 8 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/workflow/v2/crontrigger.go create mode 100644 acceptance/openstack/workflow/v2/crontriggers_test.go create mode 100644 openstack/workflow/v2/crontriggers/doc.go create mode 100644 openstack/workflow/v2/crontriggers/requests.go create mode 100644 openstack/workflow/v2/crontriggers/results.go create mode 100644 openstack/workflow/v2/crontriggers/testing/requests_test.go create mode 100644 openstack/workflow/v2/crontriggers/urls.go diff --git a/acceptance/openstack/workflow/v2/crontrigger.go b/acceptance/openstack/workflow/v2/crontrigger.go new file mode 100644 index 0000000000..fd39743367 --- /dev/null +++ b/acceptance/openstack/workflow/v2/crontrigger.go @@ -0,0 +1,36 @@ +package v2 + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" + "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// CreateCronTrigger creates a cron trigger for the given workflow. +func CreateCronTrigger(t *testing.T, client *gophercloud.ServiceClient, workflow *workflows.Workflow) (*crontriggers.CronTrigger, error) { + crontriggerName := tools.RandomString("crontrigger_", 5) + t.Logf("Attempting to create cron trigger: %s", crontriggerName) + + firstExecution := time.Now().AddDate(1, 0, 0) + createOpts := crontriggers.CreateOpts{ + WorkflowID: workflow.ID, + Name: crontriggerName, + Pattern: "0 0 1 1 *", + WorkflowInput: map[string]interface{}{ + "msg": "Hello World!", + }, + FirstExecutionTime: &firstExecution, + } + crontrigger, err := crontriggers.Create(client, createOpts).Extract() + if err != nil { + return crontrigger, err + } + t.Logf("Cron trigger created: %s", crontriggerName) + th.AssertEquals(t, crontrigger.Name, crontriggerName) + return crontrigger, nil +} diff --git a/acceptance/openstack/workflow/v2/crontriggers_test.go b/acceptance/openstack/workflow/v2/crontriggers_test.go new file mode 100644 index 0000000000..d22818ec2f --- /dev/null +++ b/acceptance/openstack/workflow/v2/crontriggers_test.go @@ -0,0 +1,22 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCronTriggersCreateDelete(t *testing.T) { + client, err := clients.NewWorkflowV2Client() + th.AssertNoErr(t, err) + + workflow, err := CreateWorkflow(t, client) + th.AssertNoErr(t, err) + + trigger, err := CreateCronTrigger(t, client, workflow) + th.AssertNoErr(t, err) + + tools.PrintResource(t, trigger) +} diff --git a/acceptance/openstack/workflow/v2/workflow.go b/acceptance/openstack/workflow/v2/workflow.go index 1643d3e47b..b0120d7b55 100644 --- a/acceptance/openstack/workflow/v2/workflow.go +++ b/acceptance/openstack/workflow/v2/workflow.go @@ -20,9 +20,12 @@ version: '2.0' description: Simple workflow example type: direct + input: + - msg + tasks: test: - action: std.echo output="Hello World!"`, workflowName) + action: std.echo output="<%% $.msg %%>"`, workflowName) } // CreateWorkflow creates a workflow on Mistral API. diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go new file mode 100644 index 0000000000..6f0d03f9a6 --- /dev/null +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -0,0 +1,41 @@ +/* +Package crontriggers provides interaction with the cron triggers API in the OpenStack Mistral service. + +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. + +Example to list cron triggers + + listOpts := crontriggers.ListOpts{ + WorkflowID: "w1", + } + allPages, err := crontriggers.List(mistralClient, listOpts).AllPages() + if err != nil { + panic(err) + } + allCrontriggers, err := crontriggers.ExtractCronTriggers(allPages) + if err != nil { + panic(err) + } + for _, ex := range allCrontriggers { + fmt.Printf("%+v\n", ex) + } + +Example to create a cron trigger + + createOpts := &crontriggers.CreateOpts{ + WorkflowID: "w1", + WorkflowParams: map[string]interface{}{ + "msg": "hello", + }, + WorkflowInput: map[string]interface{}{ + "msg": "world", + }, + Name: "trigger", + } + crontrigger, err := crontriggers.Create(mistralClient, opts).Extract() + if err != nil { + panic(err) + } +*/ +package crontriggers diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go new file mode 100644 index 0000000000..ec7dcee809 --- /dev/null +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -0,0 +1,67 @@ +package crontriggers + +import ( + "time" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder allows extension to add additional parameters to the Create request. +type CreateOptsBuilder interface { + ToCronTriggerCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters used to create a cron trigger. +type CreateOpts struct { + // Name is the cron trigger name. + Name string `json:"name"` + + // Pattern is a Unix crontab patterns format to execute the workflow. + Pattern string `json:"pattern"` + + // RemainingExecutions sets the number of executions for the trigger. + RemainingExecutions int `json:"remaining_executions,omitempty"` + + // WorkflowID is the unique id of the workflow. + WorkflowID string `json:"workflow_id,omitempty" or:"WorkflowName"` + + // WorkflowName is the name of the workflow. + // It is recommended to refer to workflow by the WorkflowID parameter instead of WorkflowName. + WorkflowName string `json:"workflow_name,omitempty" or:"WorkflowID"` + + // WorkflowParams defines workflow type specific parameters. + WorkflowParams map[string]interface{} `json:"workflow_params,omitempty"` + + // WorkflowInput defines workflow input values. + WorkflowInput map[string]interface{} `json:"workflow_input,omitempty"` + + // FirstExecutionTime defines the first execution time of the trigger. + FirstExecutionTime *time.Time `json:"-"` +} + +// ToCronTriggerCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToCronTriggerCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if opts.FirstExecutionTime != nil { + b["first_execution_time"] = opts.FirstExecutionTime.Format("2006-01-02 15:04") + } + + return b, nil +} + +// Create requests the creation of a new cron trigger. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToCronTriggerCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(createURL(client), b, &r.Body, nil) + + return +} diff --git a/openstack/workflow/v2/crontriggers/results.go b/openstack/workflow/v2/crontriggers/results.go new file mode 100644 index 0000000000..44731d3240 --- /dev/null +++ b/openstack/workflow/v2/crontriggers/results.go @@ -0,0 +1,109 @@ +package crontriggers + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Post operations. Call its Extract method to interpret it as a CronTrigger. +type CreateResult struct { + commonResult +} + +// Extract helps to get a CronTrigger struct from a Get or a Create function. +func (r commonResult) Extract() (*CronTrigger, error) { + var s CronTrigger + err := r.ExtractInto(&s) + return &s, err +} + +// CronTrigger represents a workflow cron trigger on OpenStack mistral API. +type CronTrigger struct { + // ID is the cron trigger's unique ID. + ID string `json:"id"` + + // Name is the name of the cron trigger. + Name string `json:"name"` + + // Pattern is the cron-like style pattern to execute the workflow. + // Example of value: "* * * * *" + Pattern string `json:"pattern"` + + // ProjectID is the project id owner of the cron trigger. + ProjectID string `json:"project_id"` + + // RemainingExecutions is the number of remaining executions of this trigger. + RemainingExecutions int `json:"remaining_executions"` + + // Scope is the scope of the trigger. + // Values can be "private" or "public". + Scope string `json:"scope"` + + // WorkflowID is the ID of the workflow linked to the trigger. + WorkflowID string `json:"workflow_id"` + + // WorkflowName is the name of the workflow linked to the trigger. + WorkflowName string `json:"workflow_name"` + + // WorkflowInput contains the workflow input values. + WorkflowInput map[string]interface{} `json:"-"` + + // WorkflowParams contains workflow type specific parameters. + WorkflowParams map[string]interface{} `json:"-"` + + // CreatedAt contains the cron trigger creation date. + CreatedAt time.Time `json:"-"` + + // FirstExecutionTime is the date of the first execution of the trigger. + FirstExecutionTime *time.Time `json:"-"` + + // NextExecutionTime is the date of the next execution of the trigger. + NextExecutionTime *time.Time `json:"-"` +} + +// UnmarshalJSON implements unmarshalling custom types +func (r *CronTrigger) UnmarshalJSON(b []byte) error { + type tmp CronTrigger + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` + FirstExecutionTime *gophercloud.JSONRFC3339ZNoTNoZ `json:"first_execution_time"` + NextExecutionTime *gophercloud.JSONRFC3339ZNoTNoZ `json:"next_execution_time"` + WorkflowInput string `json:"workflow_input"` + WorkflowParams string `json:"workflow_params"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = CronTrigger(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + if s.FirstExecutionTime != nil { + t := time.Time(*s.FirstExecutionTime) + r.FirstExecutionTime = &t + } + + if s.NextExecutionTime != nil { + t := time.Time(*s.NextExecutionTime) + r.NextExecutionTime = &t + } + + if err := json.Unmarshal([]byte(s.WorkflowInput), &r.WorkflowInput); err != nil { + return err + } + + if err := json.Unmarshal([]byte(s.WorkflowParams), &r.WorkflowParams); err != nil { + return err + } + + return nil +} diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go new file mode 100644 index 0000000000..89bbb26c28 --- /dev/null +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -0,0 +1,86 @@ +package testing + +import ( + "fmt" + "net/http" + "reflect" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateCronTrigger(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/cron_triggers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusCreated) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "created_at": "1970-01-01 00:00:00", + "id": "1", + "name": "trigger", + "pattern": "* * * * *", + "project_id": "p1", + "remaining_executions": 42, + "scope": "private", + "updated_at": "1970-01-01 00:00:00", + "first_execution_time": "1970-01-01 00:00:00", + "next_execution_time": "1970-01-01 00:00:00", + "workflow_id": "w1", + "workflow_input": "{\"msg\": \"hello\"}", + "workflow_name": "my_wf", + "workflow_params": "{\"msg\": \"world\"}" + } + `) + }) + + firstExecution := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + opts := &crontriggers.CreateOpts{ + WorkflowID: "w1", + Name: "trigger", + FirstExecutionTime: &firstExecution, + WorkflowParams: map[string]interface{}{ + "msg": "world", + }, + WorkflowInput: map[string]interface{}{ + "msg": "hello", + }, + } + + actual, err := crontriggers.Create(fake.ServiceClient(), opts).Extract() + if err != nil { + t.Fatalf("Unable to create cron trigger: %v", err) + } + + expected := &crontriggers.CronTrigger{ + ID: "1", + Name: "trigger", + Pattern: "* * * * *", + ProjectID: "p1", + RemainingExecutions: 42, + Scope: "private", + WorkflowID: "w1", + WorkflowName: "my_wf", + WorkflowParams: map[string]interface{}{ + "msg": "world", + }, + WorkflowInput: map[string]interface{}{ + "msg": "hello", + }, + CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), + FirstExecutionTime: &firstExecution, + NextExecutionTime: &firstExecution, + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/workflow/v2/crontriggers/urls.go b/openstack/workflow/v2/crontriggers/urls.go new file mode 100644 index 0000000000..77ae164a6d --- /dev/null +++ b/openstack/workflow/v2/crontriggers/urls.go @@ -0,0 +1,7 @@ +package crontriggers + +import "github.com/gophercloud/gophercloud" + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("cron_triggers") +} From 50b061b8db2238f774135e3652a2e1630a609c8b Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Fri, 14 Sep 2018 04:09:22 +0200 Subject: [PATCH 0533/2296] Create Executions (#1206) --- acceptance/openstack/workflow/v2/execution.go | 37 ++++++ .../openstack/workflow/v2/executions_test.go | 23 ++++ openstack/workflow/v2/executions/doc.go | 41 +++++++ openstack/workflow/v2/executions/requests.go | 53 +++++++++ openstack/workflow/v2/executions/results.go | 109 ++++++++++++++++++ .../v2/executions/testing/requests_test.go | 81 +++++++++++++ openstack/workflow/v2/executions/urls.go | 7 ++ 7 files changed, 351 insertions(+) create mode 100644 acceptance/openstack/workflow/v2/execution.go create mode 100644 acceptance/openstack/workflow/v2/executions_test.go create mode 100644 openstack/workflow/v2/executions/doc.go create mode 100644 openstack/workflow/v2/executions/requests.go create mode 100644 openstack/workflow/v2/executions/results.go create mode 100644 openstack/workflow/v2/executions/testing/requests_test.go create mode 100644 openstack/workflow/v2/executions/urls.go diff --git a/acceptance/openstack/workflow/v2/execution.go b/acceptance/openstack/workflow/v2/execution.go new file mode 100644 index 0000000000..68cefa262b --- /dev/null +++ b/acceptance/openstack/workflow/v2/execution.go @@ -0,0 +1,37 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" + "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// CreateExecution creates an execution for the given workflow. +func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow *workflows.Workflow) (*executions.Execution, error) { + executionDescription := tools.RandomString("execution_", 5) + + t.Logf("Attempting to create execution: %s", executionDescription) + createOpts := executions.CreateOpts{ + ID: executionDescription, + WorkflowID: workflow.ID, + WorkflowNamespace: workflow.Namespace, + Description: executionDescription, + Input: map[string]interface{}{ + "msg": "Hello World!", + }, + } + execution, err := executions.Create(client, createOpts).Extract() + if err != nil { + return execution, err + } + + t.Logf("Execution created: %s", executionDescription) + + th.AssertEquals(t, execution.Description, executionDescription) + + return execution, nil +} diff --git a/acceptance/openstack/workflow/v2/executions_test.go b/acceptance/openstack/workflow/v2/executions_test.go new file mode 100644 index 0000000000..8db7a7e8b8 --- /dev/null +++ b/acceptance/openstack/workflow/v2/executions_test.go @@ -0,0 +1,23 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestExecutionsCreate(t *testing.T) { + client, err := clients.NewWorkflowV2Client() + th.AssertNoErr(t, err) + + workflow, err := CreateWorkflow(t, client) + th.AssertNoErr(t, err) + defer DeleteWorkflow(t, client, workflow) + + execution, err := CreateExecution(t, client, workflow) + th.AssertNoErr(t, err) + + tools.PrintResource(t, execution) +} diff --git a/openstack/workflow/v2/executions/doc.go b/openstack/workflow/v2/executions/doc.go new file mode 100644 index 0000000000..0092e25579 --- /dev/null +++ b/openstack/workflow/v2/executions/doc.go @@ -0,0 +1,41 @@ +/* +Package executions provides interaction with the execution API in the OpenStack Mistral service. + +An execution is a particular execution of a specific workflow. Each execution contains all information about workflow itself, about execution process, state, input and output data. + +Example to list executions + + listOpts := executions.ListOpts{ + WorkflowID: "w1", + } + + allPages, err := executions.List(mistralClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allExecutions, err := executions.ExtractExecutions(allPages) + if err != nil { + panic(err) + } + + for _, ex := range allExecutions { + fmt.Printf("%+v\n", ex) + } + +Example to create an execution + + createOpts := &executions.CreateOpts{ + WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", + Input: map[string]interface{}{ + "msg": "Hello", + }, + Description: "this is a description", + } + + execution, err := executions.Create(mistralClient, opts).Extract() + if err != nil { + panic(err) + } +*/ +package executions diff --git a/openstack/workflow/v2/executions/requests.go b/openstack/workflow/v2/executions/requests.go new file mode 100644 index 0000000000..6f3f150e09 --- /dev/null +++ b/openstack/workflow/v2/executions/requests.go @@ -0,0 +1,53 @@ +package executions + +import "github.com/gophercloud/gophercloud" + +// CreateOptsBuilder allows extension to add additional parameters to the Create request. +type CreateOptsBuilder interface { + ToExecutionCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters used to create an execution. +type CreateOpts struct { + // ID is the unique ID of the execution. + ID string `json:"id,omitempty"` + + // SourceExecutionID can be set to create an execution based on another existing execution. + SourceExecutionID string `json:"source_execution_id,omitempty"` + + // WorkflowID is the unique id of the workflow. + WorkflowID string `json:"workflow_id,omitempty" or:"WorkflowName"` + + // WorkflowName is the name identifier of the workflow. + WorkflowName string `json:"workflow_name,omitempty" or:"WorkflowID"` + + // WorkflowNamespace is the namespace of the workflow. + WorkflowNamespace string `json:"workflow_namespace,omitempty"` + + // Input is a JSON structure containing workflow input values, serialized as string. + Input map[string]interface{} `json:"input,omitempty"` + + // Params define workflow type specific parameters. + Params map[string]interface{} `json:"params,omitempty"` + + // Description is the description of the workflow execution. + Description string `json:"description,omitempty"` +} + +// ToExecutionCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToExecutionCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create requests the creation of a new execution. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToExecutionCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(createURL(client), b, &r.Body, nil) + + return +} diff --git a/openstack/workflow/v2/executions/results.go b/openstack/workflow/v2/executions/results.go new file mode 100644 index 0000000000..8fb40fe7c5 --- /dev/null +++ b/openstack/workflow/v2/executions/results.go @@ -0,0 +1,109 @@ +package executions + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Post operations. Call its Extract method to interpret it as an Execution. +type CreateResult struct { + commonResult +} + +// Extract helps to get an Execution struct from a Get or a Create function. +func (r commonResult) Extract() (*Execution, error) { + var s Execution + err := r.ExtractInto(&s) + return &s, err +} + +// Execution represents a workflow execution on OpenStack mistral API. +type Execution struct { + // ID is the execution's unique ID. + ID string `json:"id"` + + // CreatedAt contains the execution creation date. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the last update of the execution. + UpdatedAt time.Time `json:"-"` + + // RootExecutionID is the parent execution ID. + RootExecutionID *string `json:"root_execution_id"` + + // TaskExecutionID is the task execution ID. + TaskExecutionID *string `json:"task_execution_id"` + + // Description is the description of the execution. + Description string `json:"description"` + + // Input contains the workflow input values. + Input map[string]interface{} `json:"-"` + + // Ouput contains the workflow output values. + Output map[string]interface{} `json:"-"` + + // Params contains workflow type specific parameters. + Params map[string]interface{} `json:"-"` + + // ProjectID is the project id owner of the execution. + ProjectID string `json:"project_id"` + + // State is the current state of the execution. State can be one of: IDLE, RUNNING, SUCCESS, ERROR, PAUSED, CANCELLED. + State string `json:"state"` + + // StateInfo contains an optional state information string. + StateInfo *string `json:"state_info"` + + // WorkflowID is the ID of the workflow linked to the execution. + WorkflowID string `json:"workflow_id"` + + // WorkflowName is the name of the workflow linked to the execution. + WorkflowName string `json:"workflow_name"` + + // WorkflowNamespace is the namespace of the workflow linked to the execution. + WorkflowNamespace string `json:"workflow_namespace"` +} + +// UnmarshalJSON implements unmarshalling custom types +func (r *Execution) UnmarshalJSON(b []byte) error { + type tmp Execution + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` + Input string `json:"input"` + Output string `json:"output"` + Params string `json:"params"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = Execution(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + if err := json.Unmarshal([]byte(s.Input), &r.Input); err != nil { + return err + } + + if err := json.Unmarshal([]byte(s.Output), &r.Output); err != nil { + return err + } + + if err := json.Unmarshal([]byte(s.Params), &r.Params); err != nil { + return err + } + + return nil +} diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go new file mode 100644 index 0000000000..c5f729a5f2 --- /dev/null +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -0,0 +1,81 @@ +package testing + +import ( + "fmt" + "net/http" + "reflect" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateExecution(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/executions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusCreated) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "created_at": "2018-09-12 14:48:49", + "description": "description", + "id": "50bb59f1-eb77-4017-a77f-6d575b002667", + "input": "{\"msg\": \"Hello\"}", + "output": "{}", + "params": "{\"namespace\": \"\", \"env\": {}}", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", + "root_execution_id": null, + "state": "SUCCESS", + "state_info": null, + "task_execution_id": null, + "updated_at": "2018-09-12 14:48:49", + "workflow_id": "6656c143-a009-4bcb-9814-cc100a20bbfa", + "workflow_name": "echo", + "workflow_namespace": "" + } + `) + }) + + opts := &executions.CreateOpts{ + WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", + Input: map[string]interface{}{ + "msg": "Hello", + }, + Description: "description", + } + + actual, err := executions.Create(fake.ServiceClient(), opts).Extract() + if err != nil { + t.Fatalf("Unable to create execution: %v", err) + } + + expected := &executions.Execution{ + ID: "50bb59f1-eb77-4017-a77f-6d575b002667", + Description: "description", + Input: map[string]interface{}{ + "msg": "Hello", + }, + Params: map[string]interface{}{ + "namespace": "", + "env": map[string]interface{}{}, + }, + Output: map[string]interface{}{}, + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", + State: "SUCCESS", + WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", + WorkflowName: "echo", + CreatedAt: time.Date(2018, time.September, 12, 14, 48, 49, 0, time.UTC), + UpdatedAt: time.Date(2018, time.September, 12, 14, 48, 49, 0, time.UTC), + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/workflow/v2/executions/urls.go b/openstack/workflow/v2/executions/urls.go new file mode 100644 index 0000000000..306ce2da02 --- /dev/null +++ b/openstack/workflow/v2/executions/urls.go @@ -0,0 +1,7 @@ +package executions + +import "github.com/gophercloud/gophercloud" + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("executions") +} From 4939fc92cf47275eb7f99f22be71a38682cc193e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 15 Sep 2018 03:03:05 +0000 Subject: [PATCH 0534/2296] Acc Tests: Fixes for Identity Roles * Create Roles instead of using existing ones * Use a Domain ID of "default" --- .../openstack/identity/v3/roles_test.go | 79 +++++++++++++++---- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index c9a395f832..9affedf3fb 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_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/domains" + "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -56,7 +57,8 @@ func TestRolesCRUD(t *testing.T) { th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ - Name: "testrole", + Name: "testrole", + DomainID: "default", Extra: map[string]interface{}{ "description": "test role description", }, @@ -70,7 +72,9 @@ func TestRolesCRUD(t *testing.T) { tools.PrintResource(t, role) tools.PrintResource(t, role.Extra) - var listOpts roles.ListOpts + listOpts := roles.ListOpts{ + DomainID: "default", + } allPages, err := roles.List(client, listOpts).AllPages() th.AssertNoErr(t, err) @@ -115,7 +119,8 @@ func TestRolesFilterList(t *testing.T) { th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ - Name: "testrole", + Name: "testrole", + DomainID: "default", Extra: map[string]interface{}{ "description": "test role description", }, @@ -182,8 +187,12 @@ func TestRoleListAssignmentForUserOnProject(t *testing.T) { th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) - role, err := FindRole(t, client) + 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) @@ -242,8 +251,12 @@ func TestRoleListAssignmentForUserOnDomain(t *testing.T) { th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) - role, err := FindRole(t, client) + 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) @@ -301,10 +314,17 @@ func TestRoleListAssignmentForGroupOnProject(t *testing.T) { th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) - role, err := FindRole(t, client) + roleCreateOpts := roles.CreateOpts{ + DomainID: "default", + } + role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) - group, err := CreateGroup(t, client, nil) + groupCreateOpts := &groups.CreateOpts{ + DomainID: "default", + } + group, err := CreateGroup(t, client, groupCreateOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) @@ -361,10 +381,17 @@ func TestRoleListAssignmentForGroupOnDomain(t *testing.T) { th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) - role, err := FindRole(t, client) + roleCreateOpts := roles.CreateOpts{ + DomainID: "default", + } + role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) - group, err := CreateGroup(t, client, nil) + groupCreateOpts := &groups.CreateOpts{ + DomainID: "default", + } + group, err := CreateGroup(t, client, groupCreateOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) @@ -420,8 +447,12 @@ func TestRolesAssignToUserOnProject(t *testing.T) { th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) - role, err := FindRole(t, client) + 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) @@ -482,8 +513,12 @@ func TestRolesAssignToUserOnDomain(t *testing.T) { th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) - role, err := FindRole(t, client) + 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) @@ -545,10 +580,17 @@ func TestRolesAssignToGroupOnDomain(t *testing.T) { th.AssertNoErr(t, err) defer DeleteDomain(t, client, domain.ID) - role, err := FindRole(t, client) + roleCreateOpts := roles.CreateOpts{ + DomainID: "default", + } + role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) - group, err := CreateGroup(t, client, nil) + groupCreateOpts := &groups.CreateOpts{ + DomainID: "default", + } + group, err := CreateGroup(t, client, groupCreateOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) @@ -606,10 +648,17 @@ func TestRolesAssignToGroupOnProject(t *testing.T) { th.AssertNoErr(t, err) defer DeleteProject(t, client, project.ID) - role, err := FindRole(t, client) + roleCreateOpts := roles.CreateOpts{ + DomainID: "default", + } + role, err := CreateRole(t, client, &roleCreateOpts) th.AssertNoErr(t, err) + defer DeleteRole(t, client, role.ID) - group, err := CreateGroup(t, client, nil) + groupCreateOpts := &groups.CreateOpts{ + DomainID: "default", + } + group, err := CreateGroup(t, client, groupCreateOpts) th.AssertNoErr(t, err) defer DeleteGroup(t, client, group.ID) From ea7289ebdf06687b792c087e2516317579d3003b Mon Sep 17 00:00:00 2001 From: Saverio Proto Date: Mon, 3 Sep 2018 15:40:57 +0200 Subject: [PATCH 0535/2296] Add support to authenticate with application credential --- auth_options.go | 66 +++++++++++++++++-- errors.go | 7 ++ openstack/auth_env.go | 31 ++++++--- openstack/identity/v3/tokens/requests.go | 23 +++++-- .../v3/tokens/testing/requests_test.go | 42 ++++++++++++ 5 files changed, 148 insertions(+), 21 deletions(-) diff --git a/auth_options.go b/auth_options.go index 5e693585c2..f28c8d2adc 100644 --- a/auth_options.go +++ b/auth_options.go @@ -84,6 +84,12 @@ type AuthOptions struct { // Scope determines the scoping of the authentication request. Scope *AuthScope `json:"-"` + + // Authentication through Application Credentials requires supplying name, project and secret + // For project we can use TenantID + ApplicationCredentialID string `json:"-"` + ApplicationCredentialName string `json:"-"` + ApplicationCredentialSecret string `json:"-"` } // AuthScope allows a created token to be limited to a specific domain or project. @@ -142,7 +148,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s type userReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` - Password string `json:"password"` + Password string `json:"password,omitempty"` Domain *domainReq `json:"domain,omitempty"` } @@ -154,10 +160,18 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s ID string `json:"id"` } + type applicationCredentialReq struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + User *userReq `json:"user,omitempty"` + Secret *string `json:"secret,omitempty"` + } + type identityReq struct { - Methods []string `json:"methods"` - Password *passwordReq `json:"password,omitempty"` - Token *tokenReq `json:"token,omitempty"` + Methods []string `json:"methods"` + Password *passwordReq `json:"password,omitempty"` + Token *tokenReq `json:"token,omitempty"` + ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` } type authReq struct { @@ -171,6 +185,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // Populate the request structure based on the provided arguments. Create and return an error // if insufficient or incompatible information is present. var req request + var userRequest userReq if opts.Password == "" { if opts.TokenID != "" { @@ -194,8 +209,49 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s req.Auth.Identity.Token = &tokenReq{ ID: opts.TokenID, } + + } else if opts.ApplicationCredentialID != "" { + // Configure the request for ApplicationCredentialID authentication. + // https://github.com/openstack/keystoneauth/blob/stable/rocky/keystoneauth1/identity/v3/application_credential.py#L48-L67 + // There are three kinds of possible application_credential requests + // 1. application_credential id + secret + // 2. application_credential name + secret + user_id + // 3. application_credential name + secret + username + domain_id / domain_name + if opts.ApplicationCredentialSecret == "" { + return nil, ErrAppCredMissingSecret{} + } + req.Auth.Identity.Methods = []string{"application_credential"} + req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ + ID: &opts.ApplicationCredentialID, + Secret: &opts.ApplicationCredentialSecret, + } + } else if opts.ApplicationCredentialName != "" { + if opts.ApplicationCredentialSecret == "" { + return nil, ErrAppCredMissingSecret{} + } + // make sure that only one of DomainName or DomainID were provided + if opts.DomainID == "" && opts.DomainName == "" { + return nil, ErrDomainIDOrDomainName{} + } + req.Auth.Identity.Methods = []string{"application_credential"} + if opts.DomainID != "" { + userRequest = userReq{ + Name: &opts.Username, + Domain: &domainReq{ID: &opts.DomainID}, + } + } else if opts.DomainName != "" { + userRequest = userReq{ + Name: &opts.Username, + Domain: &domainReq{Name: &opts.DomainName}, + } + } + req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ + Name: &opts.ApplicationCredentialName, + User: &userRequest, + Secret: &opts.ApplicationCredentialSecret, + } } else { - // If no password or token ID are available, authentication can't continue. + // If no password or token ID or ApplicationCredential are available, authentication can't continue. return nil, ErrMissingPassword{} } } else { diff --git a/errors.go b/errors.go index a5fa68d6d5..4bf1024684 100644 --- a/errors.go +++ b/errors.go @@ -451,3 +451,10 @@ type ErrScopeEmpty struct{ BaseError } func (e ErrScopeEmpty) Error() string { return "You must provide either a Project or Domain in a Scope" } + +// ErrAppCredMissingSecret indicates that no Application Credential Secret was provided with Application Credential ID or Name +type ErrAppCredMissingSecret struct{ BaseError } + +func (e ErrAppCredMissingSecret) Error() string { + return "You must provide an Application Credential Secret" +} diff --git a/openstack/auth_env.go b/openstack/auth_env.go index 994b5550c9..33c9aec441 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -38,6 +38,9 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { tenantName := os.Getenv("OS_TENANT_NAME") domainID := os.Getenv("OS_DOMAIN_ID") domainName := os.Getenv("OS_DOMAIN_NAME") + applicationCredentialID := os.Getenv("OS_APPLICATION_CREDENTIAL_ID") + applicationCredentialName := os.Getenv("OS_APPLICATION_CREDENTIAL_NAME") + applicationCredentialSecret := os.Getenv("OS_APPLICATION_CREDENTIAL_SECRET") // If OS_PROJECT_ID is set, overwrite tenantID with the value. if v := os.Getenv("OS_PROJECT_ID"); v != "" { @@ -63,22 +66,32 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { return nilOptions, err } - if password == "" { + if password == "" && applicationCredentialID == "" && applicationCredentialName == "" { err := gophercloud.ErrMissingEnvironmentVariable{ EnvironmentVariable: "OS_PASSWORD", } return nilOptions, err } + if (applicationCredentialID != "" || applicationCredentialName != "") && applicationCredentialSecret == "" { + err := gophercloud.ErrMissingEnvironmentVariable{ + EnvironmentVariable: "OS_APPLICATION_CREDENTIAL_SECRET", + } + return nilOptions, err + } + ao := gophercloud.AuthOptions{ - IdentityEndpoint: authURL, - UserID: userID, - Username: username, - Password: password, - TenantID: tenantID, - TenantName: tenantName, - DomainID: domainID, - DomainName: domainName, + IdentityEndpoint: authURL, + UserID: userID, + Username: username, + Password: password, + TenantID: tenantID, + TenantName: tenantName, + DomainID: domainID, + DomainName: domainName, + ApplicationCredentialID: applicationCredentialID, + ApplicationCredentialName: applicationCredentialName, + ApplicationCredentialSecret: applicationCredentialSecret, } return ao, nil diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index 6e99a793c5..2d20fa6f4b 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -52,19 +52,28 @@ type AuthOptions struct { // authentication token ID. TokenID string `json:"-"` + // Authentication through Application Credentials requires supplying name, project and secret + // For project we can use TenantID + ApplicationCredentialID string `json:"-"` + ApplicationCredentialName string `json:"-"` + ApplicationCredentialSecret string `json:"-"` + Scope Scope `json:"-"` } // ToTokenV3CreateMap builds a request body from AuthOptions. func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { gophercloudAuthOpts := gophercloud.AuthOptions{ - Username: opts.Username, - UserID: opts.UserID, - Password: opts.Password, - DomainID: opts.DomainID, - DomainName: opts.DomainName, - AllowReauth: opts.AllowReauth, - TokenID: opts.TokenID, + Username: opts.Username, + UserID: opts.UserID, + Password: opts.Password, + DomainID: opts.DomainID, + DomainName: opts.DomainName, + AllowReauth: opts.AllowReauth, + TokenID: opts.TokenID, + ApplicationCredentialID: opts.ApplicationCredentialID, + ApplicationCredentialName: opts.ApplicationCredentialName, + ApplicationCredentialSecret: opts.ApplicationCredentialSecret, } return gophercloudAuthOpts.ToTokenV3CreateMap(scope) diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index 3891ae4ab5..8dc7939841 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -275,6 +275,48 @@ func TestCreateProjectNameAndDomainNameScope(t *testing.T) { `) } +func TestCreateApplicationCredentialIDAndSecret(t *testing.T) { + authTokenPost(t, tokens.AuthOptions{ApplicationCredentialID: "12345abcdef", ApplicationCredentialSecret: "mysecret"}, nil, ` + { + "auth": { + "identity": { + "application_credential": { + "id": "12345abcdef", + "secret": "mysecret" + }, + "methods": [ + "application_credential" + ] + } + } + } + `) +} + +func TestCreateApplicationCredentialNameAndSecret(t *testing.T) { + authTokenPost(t, tokens.AuthOptions{ApplicationCredentialName: "myappcred", ApplicationCredentialSecret: "mysecret", Username: "fenris", DomainName: "evil-plans"}, nil, ` + { + "auth": { + "identity": { + "application_credential": { + "name": "myappcred", + "secret": "mysecret", + "user": { + "name": "fenris", + "domain": { + "name": "evil-plans" + } + } + }, + "methods": [ + "application_credential" + ] + } + } + } + `) +} + func TestCreateExtractsTokenFromResponse(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() From 5864abf58cf4e1ad1b5363f345a396f2d0918745 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 14 Sep 2018 03:19:21 +0000 Subject: [PATCH 0536/2296] Acc Tests: Network v2 cleanup --- .../networking/v2/extensions/extensions.go | 10 + .../v2/extensions/fwaas/firewall_test.go | 125 +++------- .../networking/v2/extensions/fwaas/fwaas.go | 15 ++ .../networking/v2/extensions/fwaas/pkg.go | 1 - .../v2/extensions/fwaas/policy_test.go | 57 ++--- .../v2/extensions/fwaas/rule_test.go | 53 ++--- .../v2/extensions/layer3/floatingips_test.go | 106 ++++----- .../networking/v2/extensions/layer3/layer3.go | 10 +- .../v2/extensions/layer3/routers_test.go | 92 ++++---- .../networking/v2/extensions/lbaas/pkg.go | 1 - .../v2/extensions/lbaas_v2/lbaas_v2.go | 11 + .../v2/extensions/lbaas_v2/listeners_test.go | 32 --- .../extensions/lbaas_v2/loadbalancers_test.go | 82 ++----- .../v2/extensions/lbaas_v2/monitors_test.go | 32 --- .../networking/v2/extensions/lbaas_v2/pkg.go | 1 - .../v2/extensions/lbaas_v2/pools_test.go | 32 --- .../networkipavailabilities_test.go | 13 +- .../extensions/networkipavailabilities/pkg.go | 1 - .../openstack/networking/v2/extensions/pkg.go | 1 - .../v2/extensions/portsbinding/pkg.go | 1 - .../extensions/portsbinding/portsbinding.go | 3 + .../portsbinding/portsbinding_test.go | 23 +- .../v2/extensions/rbacpolicies/pkg.go | 1 - .../extensions/rbacpolicies/rbacpolicies.go | 4 + .../rbacpolicies/rbacpolicies_test.go | 53 ++--- .../networking/v2/extensions/security_test.go | 79 +++---- .../v2/extensions/subnetpools/subnetpools.go | 4 + .../subnetpools/subnetpools_test.go | 39 ++-- .../v2/extensions/vpnaas/group_test.go | 30 +-- .../v2/extensions/vpnaas/ikepolicy_test.go | 30 +-- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 29 +-- .../v2/extensions/vpnaas/service_test.go | 29 +-- .../extensions/vpnaas/siteconnection_test.go | 63 ++--- .../networking/v2/extensions/vpnaas/vpnaas.go | 16 +- .../openstack/networking/v2/networking.go | 137 +++++++---- .../openstack/networking/v2/networks_test.go | 98 +++----- acceptance/openstack/networking/v2/pkg.go | 1 - .../openstack/networking/v2/ports_test.go | 221 +++++------------- .../openstack/networking/v2/subnets_test.go | 121 +++------- script/acceptancetest | 31 +++ 40 files changed, 628 insertions(+), 1060 deletions(-) delete mode 100644 acceptance/openstack/networking/v2/extensions/fwaas/pkg.go delete mode 100644 acceptance/openstack/networking/v2/extensions/lbaas/pkg.go delete mode 100644 acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go delete mode 100644 acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go delete mode 100644 acceptance/openstack/networking/v2/extensions/lbaas_v2/pkg.go delete mode 100644 acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go delete mode 100644 acceptance/openstack/networking/v2/extensions/networkipavailabilities/pkg.go delete mode 100644 acceptance/openstack/networking/v2/extensions/pkg.go delete mode 100644 acceptance/openstack/networking/v2/extensions/portsbinding/pkg.go delete mode 100644 acceptance/openstack/networking/v2/extensions/rbacpolicies/pkg.go delete mode 100644 acceptance/openstack/networking/v2/pkg.go diff --git a/acceptance/openstack/networking/v2/extensions/extensions.go b/acceptance/openstack/networking/v2/extensions/extensions.go index 06068322e1..69b846750f 100644 --- a/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/acceptance/openstack/networking/v2/extensions/extensions.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateExternalNetwork will create an external network. An error will be @@ -39,6 +40,8 @@ func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*ne t.Logf("Created external network: %s", networkName) + th.AssertEquals(t, network.Name, networkName) + return network, nil } @@ -65,6 +68,9 @@ func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient t.Logf("Successfully created port: %s", portName) + th.AssertEquals(t, port.Name, portName) + th.AssertEquals(t, port.NetworkID, networkID) + return port, nil } @@ -86,6 +92,8 @@ func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*grou t.Logf("Created security group: %s", secGroup.ID) + th.AssertEquals(t, secGroup.Name, secGroupName) + return secGroup, nil } @@ -114,6 +122,8 @@ func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, se t.Logf("Created security group rule: %s", rule.ID) + th.AssertEquals(t, rule.SecGroupID, secGroupID) + return rule, nil } diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go index 89d378ee7c..7b32f9a2bd 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go @@ -10,61 +10,31 @@ import ( "github.com/gophercloud/gophercloud/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" ) -func TestFirewallList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - allPages, err := firewalls.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list firewalls: %v", err) - } - - allFirewalls, err := firewalls.ExtractFirewalls(allPages) - if err != nil { - t.Fatalf("Unable to extract firewalls: %v", err) - } - - for _, firewall := range allFirewalls { - tools.PrintResource(t, firewall) - } -} - func TestFirewallCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) router, err := layer3.CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) - if err != nil { - t.Fatalf("Unable to create rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) - if err != nil { - t.Fatalf("Unable to create policy: %v", err) - } + th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewall(t, client, policy.ID) - if err != nil { - t.Fatalf("Unable to create firewall: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) @@ -75,58 +45,57 @@ func TestFirewallCRUD(t *testing.T) { } _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update firewall: %v", err) - } + th.AssertNoErr(t, err) newFirewall, err := firewalls.Get(client, firewall.ID).Extract() - if err != nil { - t.Fatalf("Unable to get firewall: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) + + allPages, err := firewalls.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allFirewalls, err := firewalls.ExtractFirewalls(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, firewall := range allFirewalls { + if firewall.ID == newFirewall.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } func TestFirewallCRUDRouter(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) router, err := layer3.CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) - if err != nil { - t.Fatalf("Unable to create rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) - if err != nil { - t.Fatalf("Unable to create policy: %v", err) - } + th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID) - if err != nil { - t.Fatalf("Unable to create firewall: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) router2, err := layer3.CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router2.ID) firewallUpdateOpts := firewalls.UpdateOpts{ @@ -140,50 +109,36 @@ func TestFirewallCRUDRouter(t *testing.T) { } _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update firewall: %v", err) - } + th.AssertNoErr(t, err) newFirewall, err := firewalls.Get(client, firewall.ID).Extract() - if err != nil { - t.Fatalf("Unable to get firewall: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) } func TestFirewallCRUDRemoveRouter(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) router, err := layer3.CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) rule, err := CreateRule(t, client) - if err != nil { - t.Fatalf("Unable to create rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) - if err != nil { - t.Fatalf("Unable to create policy: %v", err) - } + th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID) - if err != nil { - t.Fatalf("Unable to create firewall: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFirewall(t, client, firewall.ID) tools.PrintResource(t, firewall) @@ -199,14 +154,10 @@ func TestFirewallCRUDRemoveRouter(t *testing.T) { } _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update firewall: %v", err) - } + th.AssertNoErr(t, err) newFirewall, err := firewalls.Get(client, firewall.ID).Extract() - if err != nil { - t.Fatalf("Unable to get firewall: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) } diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index 83aa1a400f..c7fbd4ce40 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateFirewall will create a Firewaill with a random name and a specified @@ -39,6 +40,8 @@ func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID st t.Logf("Successfully created firewall %s", firewallName) + th.AssertEquals(t, firewall.Name, firewallName) + return firewall, nil } @@ -72,6 +75,8 @@ func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, pol t.Logf("Successfully created firewall %s", firewallName) + th.AssertEquals(t, firewall.Name, firewallName) + return firewall, nil } @@ -96,6 +101,9 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string t.Logf("Successfully created policy %s", policyName) + th.AssertEquals(t, policy.Name, policyName) + th.AssertEquals(t, len(policy.Rules), 1) + return policy, nil } @@ -129,6 +137,13 @@ func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, e t.Logf("Rule %s successfully created", ruleName) + th.AssertEquals(t, rule.Name, ruleName) + th.AssertEquals(t, rule.Protocol, rules.ProtocolTCP) + th.AssertEquals(t, rule.SourceIPAddress, sourceAddress) + th.AssertEquals(t, rule.SourcePort, sourcePort) + th.AssertEquals(t, rule.DestinationIPAddress, destinationAddress) + th.AssertEquals(t, rule.DestinationPort, destinationPort) + return rule, nil } diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/pkg.go b/acceptance/openstack/networking/v2/extensions/fwaas/pkg.go deleted file mode 100644 index 206bf3313a..0000000000 --- a/acceptance/openstack/networking/v2/extensions/fwaas/pkg.go +++ /dev/null @@ -1 +0,0 @@ -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 3220d821a3..6a7e7e4a21 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go @@ -8,47 +8,21 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestPolicyList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - allPages, err := policies.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list policies: %v", err) - } - - allPolicies, err := policies.ExtractPolicies(allPages) - if err != nil { - t.Fatalf("Unable to extract policies: %v", err) - } - - for _, policy := range allPolicies { - tools.PrintResource(t, policy) - } -} - func TestPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) rule, err := CreateRule(t, client) - if err != nil { - t.Fatalf("Unable to create rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) policy, err := CreatePolicy(t, client, rule.ID) - if err != nil { - t.Fatalf("Unable to create policy: %v", err) - } + th.AssertNoErr(t, err) defer DeletePolicy(t, client, policy.ID) tools.PrintResource(t, policy) @@ -58,14 +32,25 @@ func TestPolicyCRUD(t *testing.T) { } _, err = policies.Update(client, policy.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update policy: %v", err) - } + th.AssertNoErr(t, err) newPolicy, err := policies.Get(client, policy.ID).Extract() - if err != nil { - t.Fatalf("Unable to get policy: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) + + allPages, err := policies.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err := policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, policy := range allPolicies { + if policy.ID == newPolicy.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go index 4521a60b81..6f5968b30c 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go @@ -8,39 +8,15 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestRuleList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - allPages, err := rules.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list rules: %v", err) - } - - allRules, err := rules.ExtractRules(allPages) - if err != nil { - t.Fatalf("Unable to extract rules: %v", err) - } - - for _, rule := range allRules { - tools.PrintResource(t, rule) - } -} - func TestRuleCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) rule, err := CreateRule(t, client) - if err != nil { - t.Fatalf("Unable to create rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) @@ -51,14 +27,25 @@ func TestRuleCRUD(t *testing.T) { } _, err = rules.Update(client, rule.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update rule: %v", err) - } + th.AssertNoErr(t, err) newRule, err := rules.Get(client, rule.ID).Extract() - if err != nil { - t.Fatalf("Unable to get rule: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newRule) + + allPages, err := rules.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allRules, err := rules.ExtractRules(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, rule := range allRules { + if rule.ID == newRule.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index 3d2073c1cd..e3c0252b76 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -12,81 +12,75 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestLayer3FloatingIPsList(t *testing.T) { +func TestLayer3FloatingIPsCreateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) - listOpts := floatingips.ListOpts{ - Status: "DOWN", - } - allPages, err := floatingips.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list floating IPs: %v", err) - } + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, "") + th.AssertNoErr(t, err) + defer DeleteFloatingIP(t, client, fip.ID) + + newFip, err := floatingips.Get(client, fip.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newFip) + + allPages, err := floatingips.List(client, floatingips.ListOpts{}).AllPages() + th.AssertNoErr(t, err) allFIPs, err := floatingips.ExtractFloatingIPs(allPages) - if err != nil { - t.Fatalf("Unable to extract floating IPs: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, fip := range allFIPs { - tools.PrintResource(t, fip) + if fip.ID == newFip.ID { + found = true + } } + + th.AssertEquals(t, found, true) } -func TestLayer3FloatingIPsCreateDelete(t *testing.T) { +func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { + clients.SkipRelease(t, "master") + clients.RequireAdmin(t) + client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatalf("Unable to get choices: %v", err) - } + th.AssertNoErr(t, err) netid, err := networks.IDFromName(client, choices.NetworkName) - if err != nil { - t.Fatalf("Unable to find network id: %v", err) - } + th.AssertNoErr(t, err) subnet, err := networking.CreateSubnet(t, client, netid) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) router, err := CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) port, err := networking.CreatePort(t, client, netid, subnet.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) _, err = CreateRouterInterface(t, client, port.ID, router.ID) - if err != nil { - t.Fatalf("Unable to create router interface: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRouterInterface(t, client, port.ID, router.ID) fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, port.ID) - if err != nil { - t.Fatalf("Unable to create floating IP: %v", err) - } + th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) newFip, err := floatingips.Get(client, fip.ID).Extract() - if err != nil { - t.Fatalf("Unable to get floating ip: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newFip) @@ -96,40 +90,32 @@ func TestLayer3FloatingIPsCreateDelete(t *testing.T) { } newFip, err = floatingips.Update(client, fip.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to disassociate floating IP: %v", err) - } + th.AssertNoErr(t, err) } func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { + clients.RequireAdmin(t) + username := os.Getenv("OS_USERNAME") if username != "admin" { t.Skip("must be admin to run this test") } client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatalf("Unable to get choices: %v", err) - } + th.AssertNoErr(t, err) listOpts := subnets.ListOpts{ NetworkID: choices.ExternalNetworkID, } subnetPages, err := subnets.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list subnets: %v", err) - } + th.AssertNoErr(t, err) allSubnets, err := subnets.ExtractSubnets(subnetPages) - if err != nil { - t.Fatalf("Unable to extract subnets: %v", err) - } + th.AssertNoErr(t, err) createOpts := floatingips.CreateOpts{ FloatingNetworkID: choices.ExternalNetworkID, @@ -137,9 +123,7 @@ func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { } fip, err := floatingips.Create(client, createOpts).Extract() - if err != nil { - t.Fatalf("Unable to create floating IP: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, fip) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 3d017be889..29213e90f6 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateFloatingIP creates a floating IP on a given network and port. An error @@ -69,6 +70,8 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou t.Logf("Created router: %s", routerName) + th.AssertEquals(t, router.Name, routerName) + return router, nil } @@ -80,14 +83,9 @@ func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID str t.Logf("Attempting to create router: %s", routerName) adminStateUp := true - gatewayInfo := routers.GatewayInfo{ - NetworkID: networkID, - } - createOpts := routers.CreateOpts{ Name: routerName, AdminStateUp: &adminStateUp, - GatewayInfo: &gatewayInfo, } router, err := routers.Create(client, createOpts).Extract() @@ -101,6 +99,8 @@ func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID str t.Logf("Created router: %s", routerName) + th.AssertEquals(t, router.Name, routerName) + return router, nil } diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 4be922e9f5..bc90404ec9 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -10,40 +10,61 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestLayer3RouterList(t *testing.T) { +func TestLayer3RouterCreateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) + th.AssertNoErr(t, err) + + network, err := networking.CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + router, err := CreateRouter(t, client, network.ID) + th.AssertNoErr(t, err) + defer DeleteRouter(t, client, router.ID) + + tools.PrintResource(t, router) + + newName := tools.RandomString("TESTACC-", 8) + updateOpts := routers.UpdateOpts{ + Name: newName, } + _, err = routers.Update(client, router.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + newRouter, err := routers.Get(client, router.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newRouter) + listOpts := routers.ListOpts{} allPages, err := routers.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list routers: %v", err) - } + th.AssertNoErr(t, err) allRouters, err := routers.ExtractRouters(allPages) - if err != nil { - t.Fatalf("Unable to extract routers: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, router := range allRouters { - tools.PrintResource(t, router) + if router.ID == newRouter.ID { + found = true + } } + + th.AssertEquals(t, found, true) } func TestLayer3ExternalRouterCreateDelete(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) router, err := CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) tools.PrintResource(t, router) @@ -54,46 +75,35 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { } _, err = routers.Update(client, router.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update router: %v", err) - } + th.AssertNoErr(t, err) newRouter, err := routers.Get(client, router.ID).Extract() - if err != nil { - t.Fatalf("Unable to get router: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newRouter) } func TestLayer3RouterInterface(t *testing.T) { + clients.SkipRelease(t, "master") + clients.RequireAdmin(t) + client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatalf("Unable to get choices: %v", err) - } + th.AssertNoErr(t, err) netid, err := networks.IDFromName(client, choices.NetworkName) - if err != nil { - t.Fatalf("Unable to find network id: %v", err) - } + th.AssertNoErr(t, err) subnet, err := networking.CreateSubnet(t, client, netid) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) router, err := CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) aiOpts := routers.AddInterfaceOpts{ @@ -101,9 +111,7 @@ func TestLayer3RouterInterface(t *testing.T) { } iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract() - if err != nil { - t.Fatalf("Failed to add interface to router: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, router) tools.PrintResource(t, iface) @@ -113,7 +121,5 @@ func TestLayer3RouterInterface(t *testing.T) { } _, err = routers.RemoveInterface(client, router.ID, riOpts).Extract() - if err != nil { - t.Fatalf("Failed to remove interface from router: %v", err) - } + th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/pkg.go b/acceptance/openstack/networking/v2/extensions/lbaas/pkg.go deleted file mode 100644 index f5a7df7b75..0000000000 --- a/acceptance/openstack/networking/v2/extensions/lbaas/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package lbaas diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index 093f835b9b..dc00327323 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" + th "github.com/gophercloud/gophercloud/testhelper" ) const loadbalancerActiveTimeoutSeconds = 300 @@ -43,6 +44,8 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, listener.ProtocolPort, listenerPort) + return listener, nil } @@ -73,6 +76,8 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI t.Logf("LoadBalancer %s is active", lbName) + th.AssertEquals(t, lb.Name, lbName) + return lb, nil } @@ -110,6 +115,8 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan return member, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, member.Name, memberName) + return member, nil } @@ -140,6 +147,8 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, monitor.Name, monitorName) + return monitor, nil } @@ -169,6 +178,8 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, pool.Name, poolName) + return pool, nil } diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go deleted file mode 100644 index 2d2dd03695..0000000000 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build acceptance networking lbaas_v2 listeners - -package lbaas_v2 - -import ( - "testing" - - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" -) - -func TestListenersList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - allPages, err := listeners.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list listeners: %v", err) - } - - allListeners, err := listeners.ExtractListeners(allPages) - if err != nil { - t.Fatalf("Unable to extract listeners: %v", err) - } - - for _, listener := range allListeners { - tools.PrintResource(t, listener) - } -} 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 650eb2cc49..71889682ae 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -12,23 +12,18 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestLoadbalancersList(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := loadbalancers.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list loadbalancers: %v", err) - } + th.AssertNoErr(t, err) allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) - if err != nil { - t.Fatalf("Unable to extract loadbalancers: %v", err) - } + th.AssertNoErr(t, err) for _, lb := range allLoadbalancers { tools.PrintResource(t, lb) @@ -37,32 +32,22 @@ func TestLoadbalancersList(t *testing.T) { func TestLoadbalancersCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) lb, err := CreateLoadBalancer(t, client, subnet.ID) - if err != nil { - t.Fatalf("Unable to create loadbalancer: %v", err) - } + th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, client, lb.ID) newLB, err := loadbalancers.Get(client, lb.ID).Extract() - if err != nil { - t.Fatalf("Unable to get loadbalancer: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newLB) @@ -71,61 +56,47 @@ func TestLoadbalancersCRUD(t *testing.T) { // Listener listener, err := CreateListener(t, client, lb) - if err != nil { - t.Fatalf("Unable to create listener: %v", err) - } + th.AssertNoErr(t, err) defer DeleteListener(t, client, lb.ID, listener.ID) updateListenerOpts := listeners.UpdateOpts{ Description: "Some listener description", } _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() - if err != nil { - t.Fatalf("Unable to update listener") - } + th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newListener, err := listeners.Get(client, listener.ID).Extract() - if err != nil { - t.Fatalf("Unable to get listener") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newListener) // Pool pool, err := CreatePool(t, client, lb) - if err != nil { - t.Fatalf("Unable to create pool: %v", err) - } + th.AssertNoErr(t, err) defer DeletePool(t, client, lb.ID, pool.ID) updatePoolOpts := pools.UpdateOpts{ Description: "Some pool description", } _, err = pools.Update(client, pool.ID, updatePoolOpts).Extract() - if err != nil { - t.Fatalf("Unable to update pool") - } + th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPool, err := pools.Get(client, pool.ID).Extract() - if err != nil { - t.Fatalf("Unable to get pool") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPool) // Member member, err := CreateMember(t, client, lb, newPool, subnet.ID, subnet.CIDR) - if err != nil { - t.Fatalf("Unable to create member: %v", err) - } + th.AssertNoErr(t, err) defer DeleteMember(t, client, lb.ID, pool.ID, member.ID) newWeight := tools.RandomInt(11, 100) @@ -133,26 +104,20 @@ func TestLoadbalancersCRUD(t *testing.T) { Weight: newWeight, } _, err = pools.UpdateMember(client, pool.ID, member.ID, updateMemberOpts).Extract() - if err != nil { - t.Fatalf("Unable to update pool") - } + th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMember, err := pools.GetMember(client, pool.ID, member.ID).Extract() - if err != nil { - t.Fatalf("Unable to get member") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newMember) // Monitor monitor, err := CreateMonitor(t, client, lb, newPool) - if err != nil { - t.Fatalf("Unable to create monitor: %v", err) - } + th.AssertNoErr(t, err) defer DeleteMonitor(t, client, lb.ID, monitor.ID) newDelay := tools.RandomInt(20, 30) @@ -160,19 +125,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Delay: newDelay, } _, err = monitors.Update(client, monitor.ID, updateMonitorOpts).Extract() - if err != nil { - t.Fatalf("Unable to update monitor") - } + th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMonitor, err := monitors.Get(client, monitor.ID).Extract() - if err != nil { - t.Fatalf("Unable to get monitor") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) - } diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go deleted file mode 100644 index b312370722..0000000000 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build acceptance networking lbaas_v2 monitors - -package lbaas_v2 - -import ( - "testing" - - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" -) - -func TestMonitorsList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - allPages, err := monitors.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list monitors: %v", err) - } - - allMonitors, err := monitors.ExtractMonitors(allPages) - if err != nil { - t.Fatalf("Unable to extract monitors: %v", err) - } - - for _, monitor := range allMonitors { - tools.PrintResource(t, monitor) - } -} diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pkg.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pkg.go deleted file mode 100644 index 24b7482a56..0000000000 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pkg.go +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index b4f55a0f63..0000000000 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build acceptance networking lbaas_v2 pools - -package lbaas_v2 - -import ( - "testing" - - "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" -) - -func TestPoolsList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - allPages, err := pools.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list pools: %v", err) - } - - allPools, err := pools.ExtractPools(allPages) - if err != nil { - t.Fatalf("Unable to extract pools: %v", err) - } - - for _, pool := range allPools { - tools.PrintResource(t, pool) - } -} diff --git a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go index f141d204a8..34eb9cbf98 100644 --- a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go +++ b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go @@ -8,23 +8,18 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestNetworkIPAvailabilityList(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := networkipavailabilities.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list network IP availabilities: %v", err) - } + th.AssertNoErr(t, err) allAvailabilities, err := networkipavailabilities.ExtractNetworkIPAvailabilities(allPages) - if err != nil { - t.Fatalf("Unable to extract network IP availabilities: %v", err) - } + th.AssertNoErr(t, err) for _, availability := range allAvailabilities { tools.PrintResource(t, availability) diff --git a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/pkg.go b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/pkg.go deleted file mode 100644 index 7399a405be..0000000000 --- a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package networkipavailabilities diff --git a/acceptance/openstack/networking/v2/extensions/pkg.go b/acceptance/openstack/networking/v2/extensions/pkg.go deleted file mode 100644 index aeec0fa756..0000000000 --- a/acceptance/openstack/networking/v2/extensions/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package extensions diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/pkg.go b/acceptance/openstack/networking/v2/extensions/portsbinding/pkg.go deleted file mode 100644 index 5dae1b1660..0000000000 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package portsbinding diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index 5b7dc90787..d7a0b4aeae 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/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" ) // PortWithBindingExt represents a port with the binding fields @@ -44,5 +45,7 @@ func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, network t.Logf("Successfully created port: %s", portName) + th.AssertEquals(t, s.Name, portName) + return s, nil } diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index 6ce205925a..806eb7a19e 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -9,26 +9,23 @@ import ( networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestPortsbindingCRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networking.CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) // Define a host @@ -36,9 +33,7 @@ func TestPortsbindingCRUD(t *testing.T) { // Create port port, err := CreatePortsbinding(t, client, network.ID, subnet.ID, hostID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) @@ -49,9 +44,7 @@ func TestPortsbindingCRUD(t *testing.T) { Name: newPortName, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPort) } diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/pkg.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/pkg.go deleted file mode 100644 index f682aeab06..0000000000 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package rbacpolicies diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go index 46903dff78..b6e80d2420 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateRBACPolicy will create a rbac-policy. An error will be returned if the @@ -25,6 +26,9 @@ func CreateRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, tenantID, } t.Logf("Successfully created rbac_policy") + + th.AssertEquals(t, rbacPolicy.ObjectID, networkID) + return rbacPolicy, nil } diff --git a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index db838d2816..b07bab1718 100644 --- a/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -3,7 +3,6 @@ package rbacpolicies import ( - "os" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -11,56 +10,42 @@ import ( networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestRBACPolicyCRUD(t *testing.T) { - username := os.Getenv("OS_USERNAME") - if username != "admin" { - t.Skip("must be admin to run this test") - } + clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create a network network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) tools.PrintResource(t, network) identityClient, err := clients.NewIdentityV3Client() - if err != nil { - t.Fatalf("Unable to obtain an identity client: %v") - } + th.AssertNoErr(t, err) // Create a project/tenant project, err := projects.CreateProject(t, identityClient, nil) - if err != nil { - t.Fatalf("Unable to create project: %v", err) - } + th.AssertNoErr(t, err) defer projects.DeleteProject(t, identityClient, project.ID) tools.PrintResource(t, project) // Create a rbac-policy rbacPolicy, err := CreateRBACPolicy(t, client, project.ID, network.ID) - if err != nil { - t.Fatalf("Unable to create rbac-policy: %v", err) - } + th.AssertNoErr(t, err) defer DeleteRBACPolicy(t, client, rbacPolicy.ID) tools.PrintResource(t, rbacPolicy) // Create another project/tenant for rbac-update project2, err := projects.CreateProject(t, identityClient, nil) - if err != nil { - t.Fatalf("Unable to create project2: %v", err) - } + th.AssertNoErr(t, err) defer projects.DeleteProject(t, identityClient, project2.ID) tools.PrintResource(t, project2) @@ -71,25 +56,21 @@ func TestRBACPolicyCRUD(t *testing.T) { } _, err = rbacpolicies.Update(client, rbacPolicy.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update rbac-policy: %v", err) - } + th.AssertNoErr(t, err) // Get the rbac-policy by ID t.Logf("Get rbac_policy by ID") newrbacPolicy, err := rbacpolicies.Get(client, rbacPolicy.ID).Extract() - if err != nil { - t.Fatalf("Unable to retrieve rbac policy: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newrbacPolicy) } func TestRBACPolicyList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) type rbacPolicy struct { rbacpolicies.RBACPolicy @@ -98,14 +79,10 @@ func TestRBACPolicyList(t *testing.T) { var allRBACPolicies []rbacPolicy allPages, err := rbacpolicies.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list rbac policies: %v", err) - } + th.AssertNoErr(t, err) err = rbacpolicies.ExtractRBACPolicesInto(allPages, &allRBACPolicies) - if err != nil { - t.Fatalf("Unable to extract rbac policies: %v", err) - } + th.AssertNoErr(t, err) for _, rbacpolicy := range allRBACPolicies { tools.PrintResource(t, rbacpolicy) diff --git a/acceptance/openstack/networking/v2/extensions/security_test.go b/acceptance/openstack/networking/v2/extensions/security_test.go index 3810a42014..8f3a291364 100644 --- a/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/acceptance/openstack/networking/v2/extensions/security_test.go @@ -9,46 +9,19 @@ import ( networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestSecurityGroupsList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - listOpts := groups.ListOpts{} - allPages, err := groups.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list groups: %v", err) - } - - allGroups, err := groups.ExtractGroups(allPages) - if err != nil { - t.Fatalf("Unable to extract groups: %v", err) - } - - for _, group := range allGroups { - tools.PrintResource(t, group) - } -} - func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) group, err := CreateSecurityGroup(t, client) - if err != nil { - t.Fatalf("Unable to create security group: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSecurityGroup(t, client, group.ID) rule, err := CreateSecurityGroupRule(t, client, group.ID) - if err != nil { - t.Fatalf("Unable to create security group rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSecurityGroupRule(t, client, rule.ID) tools.PrintResource(t, group) @@ -58,47 +31,49 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { } newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update security group: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) + + listOpts := groups.ListOpts{} + allPages, err := groups.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allGroups, err := groups.ExtractGroups(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, group := range allGroups { + if group.ID == newGroup.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } func TestSecurityGroupsPort(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) subnet, err := networking.CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) group, err := CreateSecurityGroup(t, client) - if err != nil { - t.Fatalf("Unable to create security group: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSecurityGroup(t, client, group.ID) rule, err := CreateSecurityGroupRule(t, client, group.ID) - if err != nil { - t.Fatalf("Unable to create security group rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSecurityGroupRule(t, client, rule.ID) port, err := CreatePortWithSecurityGroup(t, client, network.ID, subnet.ID, group.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeletePort(t, client, port.ID) tools.PrintResource(t, port) diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go index fdf6318d52..a2f14a4691 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateSubnetPool will create a subnetpool. An error will be returned if the @@ -28,6 +29,9 @@ func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetp } t.Logf("Successfully created the subnetpool.") + + th.AssertEquals(t, subnetPool.Name, subnetPoolName) + return subnetPool, nil } diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index bdef6e6794..f0e9b792f6 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -8,19 +8,16 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestSubnetPoolsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create a subnetpool subnetPool, err := CreateSubnetPool(t, client) - if err != nil { - t.Fatalf("Unable to create a subnetpool: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnetPool(t, client, subnetPool.ID) tools.PrintResource(t, subnetPool) @@ -31,35 +28,25 @@ func TestSubnetPoolsCRUD(t *testing.T) { } _, err = subnetpools.Update(client, subnetPool.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update the subnetpool: %v", err) - } + th.AssertNoErr(t, err) newSubnetPool, err := subnetpools.Get(client, subnetPool.ID).Extract() - if err != nil { - t.Fatalf("Unable to get subnetpool: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newSubnetPool) -} - -func TestSubnetPoolsList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } allPages, err := subnetpools.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list subnetpools: %v", err) - } + th.AssertNoErr(t, err) allSubnetPools, err := subnetpools.ExtractSubnetPools(allPages) - if err != nil { - t.Fatalf("Unable to extract subnetpools: %v", err) - } + th.AssertNoErr(t, err) + var found bool for _, subnetpool := range allSubnetPools { - tools.PrintResource(t, subnetpool) + if subnetpool.ID == newSubnetPool.ID { + found = true + } } + + th.AssertEquals(t, found, true) } diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index 853065d219..c45daf0e43 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -8,23 +8,18 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestGroupList(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := endpointgroups.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list endpoint groups: %v", err) - } + th.AssertNoErr(t, err) allGroups, err := endpointgroups.ExtractEndpointGroups(allPages) - if err != nil { - t.Fatalf("Unable to extract endpoint groups: %v", err) - } + th.AssertNoErr(t, err) for _, group := range allGroups { tools.PrintResource(t, group) @@ -33,21 +28,15 @@ func TestGroupList(t *testing.T) { func TestGroupCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) group, err := CreateEndpointGroup(t, client) - if err != nil { - t.Fatalf("Unable to create Endpoint group: %v", err) - } + th.AssertNoErr(t, err) defer DeleteEndpointGroup(t, client, group.ID) tools.PrintResource(t, group) newGroup, err := endpointgroups.Get(client, group.ID).Extract() - if err != nil { - t.Fatalf("Unable to retrieve Endpoint group: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) updatedName := "updatedname" @@ -57,9 +46,6 @@ func TestGroupCRUD(t *testing.T) { Description: &updatedDescription, } updatedGroup, err := endpointgroups.Update(client, group.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update endpoint group: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, updatedGroup) - } diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index 2efa1e1b65..c14c9fb5e3 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -8,23 +8,18 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestIKEPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := ikepolicies.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list IKE policies: %v", err) - } + th.AssertNoErr(t, err) allPolicies, err := ikepolicies.ExtractPolicies(allPages) - if err != nil { - t.Fatalf("Unable to extract IKE policies: %v", err) - } + th.AssertNoErr(t, err) for _, policy := range allPolicies { tools.PrintResource(t, policy) @@ -33,22 +28,16 @@ func TestIKEPolicyList(t *testing.T) { func TestIKEPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) policy, err := CreateIKEPolicy(t, client) - if err != nil { - t.Fatalf("Unable to create IKE policy: %v", err) - } + th.AssertNoErr(t, err) defer DeleteIKEPolicy(t, client, policy.ID) tools.PrintResource(t, policy) newPolicy, err := ikepolicies.Get(client, policy.ID).Extract() - if err != nil { - t.Fatalf("Unable to get IKE policy: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) updatedName := "updatedname" @@ -61,9 +50,6 @@ func TestIKEPolicyCRUD(t *testing.T) { }, } updatedPolicy, err := ikepolicies.Update(client, policy.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update IKE policy: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, updatedPolicy) - } diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index 2fdee7dd92..7589590ee4 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -8,23 +8,18 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestIPSecPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := ipsecpolicies.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list IPSec policies: %v", err) - } + th.AssertNoErr(t, err) allPolicies, err := ipsecpolicies.ExtractPolicies(allPages) - if err != nil { - t.Fatalf("Unable to extract policies: %v", err) - } + th.AssertNoErr(t, err) for _, policy := range allPolicies { tools.PrintResource(t, policy) @@ -33,14 +28,10 @@ func TestIPSecPolicyList(t *testing.T) { func TestIPSecPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) policy, err := CreateIPSecPolicy(t, client) - if err != nil { - t.Fatalf("Unable to create IPSec policy: %v", err) - } + th.AssertNoErr(t, err) defer DeleteIPSecPolicy(t, client, policy.ID) tools.PrintResource(t, policy) @@ -50,14 +41,10 @@ func TestIPSecPolicyCRUD(t *testing.T) { } policy, err = ipsecpolicies.Update(client, policy.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update IPSec policy: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, policy) newPolicy, err := ipsecpolicies.Get(client, policy.ID).Extract() - if err != nil { - t.Fatalf("Unable to get IPSec policy: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) } diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index f88aa7611d..d7bc05ce5e 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -9,23 +9,18 @@ import ( layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestServiceList(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := services.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list services: %v", err) - } + th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) - if err != nil { - t.Fatalf("Unable to extract services: %v", err) - } + th.AssertNoErr(t, err) for _, service := range allServices { tools.PrintResource(t, service) @@ -34,26 +29,18 @@ func TestServiceList(t *testing.T) { func TestServiceCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) router, err := layer3.CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) service, err := CreateService(t, client, router.ID) - if err != nil { - t.Fatalf("Unable to create service: %v", err) - } + th.AssertNoErr(t, err) defer DeleteService(t, client, service.ID) newService, err := services.Get(client, service.ID).Extract() - if err != nil { - t.Fatalf("Unable to get service: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, service) tools.PrintResource(t, newService) diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index 5bf7560747..254ddd1726 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -12,23 +12,19 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" + + th "github.com/gophercloud/gophercloud/testhelper" ) func TestConnectionList(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := siteconnections.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list IPSec site connections: %v", err) - } + th.AssertNoErr(t, err) allConnections, err := siteconnections.ExtractConnections(allPages) - if err != nil { - t.Fatalf("Unable to extract IPSec site connections: %v", err) - } + th.AssertNoErr(t, err) for _, connection := range allConnections { tools.PrintResource(t, connection) @@ -37,28 +33,20 @@ func TestConnectionList(t *testing.T) { func TestConnectionCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := networks.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer networks.DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := networks.CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer networks.DeleteSubnet(t, client, subnet.ID) router, err := layer3.CreateExternalRouter(t, client) - if err != nil { - t.Fatalf("Unable to create router: %v", err) - } + th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router.ID) // Link router and subnet @@ -67,9 +55,7 @@ func TestConnectionCRUD(t *testing.T) { } _, err = routers.AddInterface(client, router.ID, aiOpts).Extract() - if err != nil { - t.Fatalf("Failed to add interface to router: %v", err) - } + th.AssertNoErr(t, err) defer func() { riOpts := routers.RemoveInterfaceOpts{ SubnetID: subnet.ID, @@ -79,47 +65,32 @@ func TestConnectionCRUD(t *testing.T) { // Create all needed resources for the connection service, err := CreateService(t, client, router.ID) - if err != nil { - t.Fatalf("Unable to create service: %v", err) - } + th.AssertNoErr(t, err) defer DeleteService(t, client, service.ID) ikepolicy, err := CreateIKEPolicy(t, client) - if err != nil { - t.Fatalf("Unable to create IKE policy: %v", err) - } + th.AssertNoErr(t, err) defer DeleteIKEPolicy(t, client, ikepolicy.ID) ipsecpolicy, err := CreateIPSecPolicy(t, client) - if err != nil { - t.Fatalf("Unable to create IPSec Policy: %v", err) - } + th.AssertNoErr(t, err) defer DeleteIPSecPolicy(t, client, ipsecpolicy.ID) peerEPGroup, err := CreateEndpointGroup(t, client) - if err != nil { - t.Fatalf("Unable to create Endpoint Group with CIDR endpoints: %v", err) - } + th.AssertNoErr(t, err) defer DeleteEndpointGroup(t, client, peerEPGroup.ID) localEPGroup, err := CreateEndpointGroupWithSubnet(t, client, subnet.ID) - if err != nil { - t.Fatalf("Unable to create Endpoint Group with subnet endpoints: %v", err) - } + th.AssertNoErr(t, err) defer DeleteEndpointGroup(t, client, localEPGroup.ID) conn, err := CreateSiteConnection(t, client, ikepolicy.ID, ipsecpolicy.ID, service.ID, peerEPGroup.ID, localEPGroup.ID) - if err != nil { - t.Fatalf("Unable to create IPSec Site Connection: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSiteConnection(t, client, conn.ID) newConnection, err := siteconnections.Get(client, conn.ID).Extract() - if err != nil { - t.Fatalf("Unable to get connection: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, conn) tools.PrintResource(t, newConnection) - } diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index 2194a4c70e..2ba6a09050 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateService will create a Service with a random name and a specified router ID @@ -32,6 +33,8 @@ func CreateService(t *testing.T, client *gophercloud.ServiceClient, routerID str t.Logf("Successfully created service %s", serviceName) + th.AssertEquals(t, service.Name, serviceName) + return service, nil } @@ -67,6 +70,8 @@ func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecp t.Logf("Successfully created IPSec policy %s", policyName) + th.AssertEquals(t, policy.Name, policyName) + return policy, nil } @@ -90,6 +95,8 @@ func CreateIKEPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ikepolic t.Logf("Successfully created IKE policy %s", policyName) + th.AssertEquals(t, policy.Name, policyName) + return policy, nil } @@ -143,6 +150,8 @@ func CreateEndpointGroup(t *testing.T, client *gophercloud.ServiceClient) (*endp t.Logf("Successfully created group %s", groupName) + th.AssertEquals(t, group.Name, groupName) + return group, nil } @@ -168,6 +177,8 @@ func CreateEndpointGroupWithCIDR(t *testing.T, client *gophercloud.ServiceClient t.Logf("Successfully created group %s", groupName) t.Logf("%v", group) + th.AssertEquals(t, group.Name, groupName) + return group, nil } @@ -207,6 +218,8 @@ func CreateEndpointGroupWithSubnet(t *testing.T, client *gophercloud.ServiceClie t.Logf("Successfully created group %s", groupName) + th.AssertEquals(t, group.Name, groupName) + return group, nil } @@ -239,6 +252,8 @@ func CreateSiteConnection(t *testing.T, client *gophercloud.ServiceClient, ikepo t.Logf("Successfully created IPSec Site Connection %s", connectionName) + th.AssertEquals(t, connection.Name, connectionName) + return connection, nil } @@ -254,5 +269,4 @@ func DeleteSiteConnection(t *testing.T, client *gophercloud.ServiceClient, siteC } t.Logf("Deleted site connection: %s", siteConnectionID) - } diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index 2abf4f9180..89bb09b134 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -11,8 +11,15 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" + th "github.com/gophercloud/gophercloud/testhelper" ) +// PortWithExtraDHCPOpts represents a port with extra DHCP options configuration. +type PortWithExtraDHCPOpts struct { + ports.Port + extradhcpopts.ExtraDHCPOptsExt +} + // CreateNetwork will create basic network. An error will be returned if the // network could not be created. func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) { @@ -30,6 +37,9 @@ func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.N } t.Logf("Successfully created network.") + + th.AssertEquals(t, network.Name, networkName) + return network, nil } @@ -56,6 +66,9 @@ func CreateNetworkWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceC } t.Logf("Successfully created network.") + + th.AssertEquals(t, network.Name, networkName) + return network, nil } @@ -89,6 +102,8 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subn t.Logf("Successfully created port: %s", portName) + th.AssertEquals(t, port.Name, portName) + return newPort, nil } @@ -124,6 +139,8 @@ func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClie t.Logf("Successfully created port: %s", portName) + th.AssertEquals(t, port.Name, portName) + return newPort, nil } @@ -163,9 +180,55 @@ func CreatePortWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClie t.Logf("Successfully created port: %s", portName) + th.AssertEquals(t, port.Name, portName) + return newPort, nil } +// CreatePortWithExtraDHCPOpts will create a port with DHCP options on the +// specified subnet. An error will be returned if the port could not be created. +func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*PortWithExtraDHCPOpts, error) { + portName := tools.RandomString("TESTACC-", 8) + + t.Logf("Attempting to create port: %s", portName) + + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + Name: portName, + AdminStateUp: gophercloud.Enabled, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + } + + createOpts := extradhcpopts.CreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{ + { + OptName: "test_option_1", + OptValue: "test_value_1", + }, + }, + } + port := &PortWithExtraDHCPOpts{} + + err := ports.Create(client, createOpts).ExtractInto(port) + if err != nil { + return nil, err + } + + if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + return nil, err + } + + err = ports.Get(client, port.ID).ExtractInto(port) + if err != nil { + return port, err + } + + t.Logf("Successfully created port: %s", portName) + + return port, nil +} + // CreateSubnet will create a subnet on the specified Network ID. An error // will be returned if the subnet could not be created. func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { @@ -190,6 +253,11 @@ func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID str } t.Logf("Successfully created subnet.") + + th.AssertEquals(t, subnet.Name, subnetName) + th.AssertEquals(t, subnet.GatewayIP, subnetGateway) + th.AssertEquals(t, subnet.CIDR, subnetCIDR) + return subnet, nil } @@ -200,6 +268,8 @@ func CreateSubnetWithDefaultGateway(t *testing.T, client *gophercloud.ServiceCli subnetName := tools.RandomString("TESTACC-", 8) subnetOctet := tools.RandomInt(1, 250) subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet) + defaultGateway := fmt.Sprintf("192.168.%d.1", subnetOctet) + createOpts := subnets.CreateOpts{ NetworkID: networkID, CIDR: subnetCIDR, @@ -216,6 +286,11 @@ func CreateSubnetWithDefaultGateway(t *testing.T, client *gophercloud.ServiceCli } t.Logf("Successfully created subnet.") + + th.AssertEquals(t, subnet.Name, subnetName) + th.AssertEquals(t, subnet.GatewayIP, defaultGateway) + th.AssertEquals(t, subnet.CIDR, subnetCIDR) + return subnet, nil } @@ -252,6 +327,11 @@ func CreateSubnetWithNoGateway(t *testing.T, client *gophercloud.ServiceClient, } t.Logf("Successfully created subnet.") + + th.AssertEquals(t, subnet.Name, subnetName) + th.AssertEquals(t, subnet.GatewayIP, "") + th.AssertEquals(t, subnet.CIDR, subnetCIDR) + return subnet, nil } @@ -278,6 +358,10 @@ func CreateSubnetWithSubnetPool(t *testing.T, client *gophercloud.ServiceClient, } t.Logf("Successfully created subnet.") + + th.AssertEquals(t, subnet.Name, subnetName) + th.AssertEquals(t, subnet.CIDR, subnetCIDR) + return subnet, nil } @@ -302,6 +386,9 @@ func CreateSubnetWithSubnetPoolNoCIDR(t *testing.T, client *gophercloud.ServiceC } t.Logf("Successfully created subnet.") + + th.AssertEquals(t, subnet.Name, subnetName) + return subnet, nil } @@ -361,53 +448,3 @@ func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string, secs return false, nil }) } - -// PortWithExtraDHCPOpts represents a port with extra DHCP options configuration. -type PortWithExtraDHCPOpts struct { - ports.Port - extradhcpopts.ExtraDHCPOptsExt -} - -// CreatePortWithExtraDHCPOpts will create a port with DHCP options on the -// specified subnet. An error will be returned if the port could not be created. -func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*PortWithExtraDHCPOpts, error) { - portName := tools.RandomString("TESTACC-", 8) - - t.Logf("Attempting to create port: %s", portName) - - portCreateOpts := ports.CreateOpts{ - NetworkID: networkID, - Name: portName, - AdminStateUp: gophercloud.Enabled, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, - } - - createOpts := extradhcpopts.CreateOptsExt{ - CreateOptsBuilder: portCreateOpts, - ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{ - { - OptName: "test_option_1", - OptValue: "test_value_1", - }, - }, - } - port := &PortWithExtraDHCPOpts{} - - err := ports.Create(client, createOpts).ExtractInto(port) - if err != nil { - return nil, err - } - - if err := WaitForPortToCreate(client, port.ID, 60); err != nil { - return nil, err - } - - err = ports.Get(client, port.ID).ExtractInto(port) - if err != nil { - return port, err - } - - t.Logf("Successfully created port: %s", portName) - - return port, nil -} diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index d7300a35d5..d9a15b5c8e 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -13,44 +13,12 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestNetworksList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - type networkWithExt struct { - networks.Network - portsecurity.PortSecurityExt - } - - var allNetworks []networkWithExt - - allPages, err := networks.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } - - err = networks.ExtractNetworksInto(allPages, &allNetworks) - if err != nil { - t.Fatalf("Unable to extract networks: %v", err) - } - - for _, network := range allNetworks { - tools.PrintResource(t, network) - } -} - func TestNetworksExternalList(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatalf("Unable to fetch environment information: %s", err) - } + th.AssertNoErr(t, err) type networkWithExt struct { networks.Network @@ -69,14 +37,10 @@ func TestNetworksExternalList(t *testing.T) { } allPages, err := networks.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } + th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &allNetworks) - if err != nil { - t.Fatalf("Unable to extract networks: %v", err) - } + th.AssertNoErr(t, err) var found bool for _, network := range allNetworks { @@ -97,9 +61,7 @@ func TestNetworksExternalList(t *testing.T) { } allPages, err = networks.List(client, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } + th.AssertNoErr(t, err) v, err := networks.ExtractNetworks(allPages) th.AssertEquals(t, len(v), 0) @@ -107,15 +69,11 @@ func TestNetworksExternalList(t *testing.T) { func TestNetworksCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create a network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) tools.PrintResource(t, network) @@ -126,23 +84,39 @@ func TestNetworksCRUD(t *testing.T) { } _, err = networks.Update(client, network.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update network: %v", err) - } + th.AssertNoErr(t, err) newNetwork, err := networks.Get(client, network.ID).Extract() - if err != nil { - t.Fatalf("Unable to retrieve network: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) + + type networkWithExt struct { + networks.Network + portsecurity.PortSecurityExt + } + + var allNetworks []networkWithExt + + allPages, err := networks.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + err = networks.ExtractNetworksInto(allPages, &allNetworks) + th.AssertNoErr(t, err) + + var found bool + for _, network := range allNetworks { + if network.ID == newNetwork.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } func TestNetworksPortSecurityCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create a network without port security network, err := CreateNetworkWithoutPortSecurity(t, client) @@ -157,9 +131,7 @@ func TestNetworksPortSecurityCRUD(t *testing.T) { } err = networks.Get(client, network.ID).ExtractInto(&networkWithExtensions) - if err != nil { - t.Fatalf("Unable to retrieve network: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, networkWithExtensions) @@ -171,9 +143,7 @@ func TestNetworksPortSecurityCRUD(t *testing.T) { } err = networks.Update(client, network.ID, updateOpts).ExtractInto(&networkWithExtensions) - if err != nil { - t.Fatalf("Unable to update network: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, networkWithExtensions) } diff --git a/acceptance/openstack/networking/v2/pkg.go b/acceptance/openstack/networking/v2/pkg.go deleted file mode 100644 index 5ec3cc8e83..0000000000 --- a/acceptance/openstack/networking/v2/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package v2 diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 5b820d7e57..65dfb18637 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -11,54 +11,26 @@ import ( "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" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestPortsList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - allPages, err := ports.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list ports: %v", err) - } - - allPorts, err := ports.ExtractPorts(allPages) - if err != nil { - t.Fatalf("Unable to extract ports: %v", err) - } - - for _, port := range allPorts { - tools.PrintResource(t, port) - } -} - func TestPortsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) if len(port.SecurityGroups) != 1 { @@ -73,47 +45,50 @@ func TestPortsCRUD(t *testing.T) { Name: newPortName, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPort) + + allPages, err := ports.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allPorts, err := ports.ExtractPorts(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, port := range allPorts { + if port.ID == newPort.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } func TestPortsRemoveSecurityGroups(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) // Create a Security Group group, err := extensions.CreateSecurityGroup(t, client) - if err != nil { - t.Fatalf("Unable to create security group: %v", err) - } + th.AssertNoErr(t, err) defer extensions.DeleteSecurityGroup(t, client, group.ID) // Add the group to the port @@ -121,18 +96,14 @@ func TestPortsRemoveSecurityGroups(t *testing.T) { SecurityGroups: &[]string{group.ID}, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) // Remove the group updateOpts = ports.UpdateOpts{ SecurityGroups: &[]string{}, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -143,36 +114,26 @@ func TestPortsRemoveSecurityGroups(t *testing.T) { func TestPortsDontAlterSecurityGroups(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create a Security Group group, err := extensions.CreateSecurityGroup(t, client) - if err != nil { - t.Fatalf("Unable to create security group: %v", err) - } + th.AssertNoErr(t, err) defer extensions.DeleteSecurityGroup(t, client, group.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) @@ -182,18 +143,14 @@ func TestPortsDontAlterSecurityGroups(t *testing.T) { SecurityGroups: &[]string{group.ID}, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) // Update the port again updateOpts = ports.UpdateOpts{ Name: "some_port", } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -204,29 +161,21 @@ func TestPortsDontAlterSecurityGroups(t *testing.T) { func TestPortsWithNoSecurityGroup(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePortWithNoSecurityGroup(t, client, network.ID, subnet.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) @@ -238,29 +187,21 @@ func TestPortsWithNoSecurityGroup(t *testing.T) { func TestPortsRemoveAddressPair(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) @@ -272,18 +213,14 @@ func TestPortsRemoveAddressPair(t *testing.T) { }, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) // Remove the address pair updateOpts = ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{}, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -294,29 +231,21 @@ func TestPortsRemoveAddressPair(t *testing.T) { func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePort(t, client, network.ID, subnet.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) @@ -328,9 +257,7 @@ func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { }, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -339,9 +266,7 @@ func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { Name: "some_port", } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -352,29 +277,21 @@ func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { func TestPortsPortSecurityCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create port port, err := CreatePortWithoutPortSecurity(t, client, network.ID, subnet.ID) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) var portWithExt struct { @@ -383,9 +300,7 @@ func TestPortsPortSecurityCRUD(t *testing.T) { } err = ports.Get(client, port.ID).ExtractInto(&portWithExt) - if err != nil { - t.Fatalf("Unable to create port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, portWithExt) @@ -397,38 +312,28 @@ func TestPortsPortSecurityCRUD(t *testing.T) { } err = ports.Update(client, port.ID, updateOpts).ExtractInto(&portWithExt) - if err != nil { - t.Fatalf("Unable to update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, portWithExt) } func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create a Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create a network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create a Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create a subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) // Create a port with extra DHCP options. port, err := CreatePortWithExtraDHCPOpts(t, client, network.ID, subnet.ID) - if err != nil { - t.Fatalf("Unable to create a port: %v", err) - } + th.AssertNoErr(t, err) defer DeletePort(t, client, port.ID) tools.PrintResource(t, port) @@ -458,9 +363,7 @@ func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { newPort := &PortWithExtraDHCPOpts{} err = ports.Update(client, port.ID, updateOpts).ExtractInto(newPort) - if err != nil { - t.Fatalf("Could not update port: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPort) } diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index 6eb46074e5..28c8479c79 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -11,47 +11,21 @@ import ( subnetpools "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/subnetpools" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" + th "github.com/gophercloud/gophercloud/testhelper" ) -func TestSubnetsList(t *testing.T) { - client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } - - allPages, err := subnets.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list subnets: %v", err) - } - - allSubnets, err := subnets.ExtractSubnets(allPages) - if err != nil { - t.Fatalf("Unable to extract subnets: %v", err) - } - - for _, subnet := range allSubnets { - tools.PrintResource(t, subnet) - } -} - func TestSubnetCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) @@ -62,37 +36,42 @@ func TestSubnetCRUD(t *testing.T) { Name: newSubnetName, } _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update subnet: %v", err) - } + th.AssertNoErr(t, err) // Get subnet newSubnet, err := subnets.Get(client, subnet.ID).Extract() - if err != nil { - t.Fatalf("Unable to get subnet: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newSubnet) + + allPages, err := subnets.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allSubnets, err := subnets.ExtractSubnets(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, subnet := range allSubnets { + if subnet.ID == newSubnet.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } func TestSubnetsDefaultGateway(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnetWithDefaultGateway(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) @@ -107,9 +86,7 @@ func TestSubnetsDefaultGateway(t *testing.T) { } newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update subnet") - } + th.AssertNoErr(t, err) if newSubnet.GatewayIP != "" { t.Fatalf("Gateway was not updated correctly") @@ -118,22 +95,16 @@ func TestSubnetsDefaultGateway(t *testing.T) { func TestSubnetsNoGateway(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create Subnet subnet, err := CreateSubnetWithNoGateway(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) @@ -149,9 +120,7 @@ func TestSubnetsNoGateway(t *testing.T) { } newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update subnet") - } + th.AssertNoErr(t, err) if newSubnet.GatewayIP == "" { t.Fatalf("Gateway was not updated correctly") @@ -160,29 +129,21 @@ func TestSubnetsNoGateway(t *testing.T) { func TestSubnetsWithSubnetPool(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create SubnetPool subnetPool, err := subnetpools.CreateSubnetPool(t, client) - if err != nil { - t.Fatalf("Unable to create subnet pool: %v", err) - } + th.AssertNoErr(t, err) defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) // Create Subnet subnet, err := CreateSubnetWithSubnetPool(t, client, network.ID, subnetPool.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) @@ -194,29 +155,21 @@ func TestSubnetsWithSubnetPool(t *testing.T) { func TestSubnetsWithSubnetPoolNoCIDR(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create Network network, err := CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer DeleteNetwork(t, client, network.ID) // Create SubnetPool subnetPool, err := subnetpools.CreateSubnetPool(t, client) - if err != nil { - t.Fatalf("Unable to create subnet pool: %v", err) - } + th.AssertNoErr(t, err) defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) // Create Subnet subnet, err := CreateSubnetWithSubnetPoolNoCIDR(t, client, network.ID, subnetPool.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer DeleteSubnet(t, client, subnet.ID) tools.PrintResource(t, subnet) diff --git a/script/acceptancetest b/script/acceptancetest index 02b3f6740b..ca4939b5d5 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -22,6 +22,37 @@ if [[ $? != 0 ]]; then failed=1 fi +# Networking v2 - layer3 +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/layer3 +if [[ $? != 0 ]]; then + failed=1 +fi + +# Networking v2 - networkipavailabilities +# requires bug fix +#go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/networkipavailabilities +#if [[ $? != 0 ]]; then +# failed=1 +#fi + +# Networking v2 - portsbinding +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/portsbinding +if [[ $? != 0 ]]; then + failed=1 +fi + +# Networking v2 - rbacpolicies +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/rbacpolicies +if [[ $? != 0 ]]; then + failed=1 +fi + +# Networking v2 - subnetpools +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/subnetpools +if [[ $? != 0 ]]; then + failed=1 +fi + # Block Storage v2 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ if [[ $? != 0 ]]; then From c05f4379d0bfd9cc16dd5090d3fe2774c6458bd3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 21 Sep 2018 02:27:13 +0000 Subject: [PATCH 0537/2296] Acc Tests: Enable Block Storage extensions tests --- .../extensions/schedulerstats_test.go | 15 ++-- .../blockstorage/extensions/services_test.go | 15 ++-- .../extensions/volumeactions_test.go | 74 +++++-------------- script/acceptancetest | 6 ++ 4 files changed, 38 insertions(+), 72 deletions(-) diff --git a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go index 76093f4c12..d0cd973c07 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -8,27 +8,24 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestSchedulerStatsList(t *testing.T) { + clients.RequireAdmin(t) + blockClient, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) listOpts := schedulerstats.ListOpts{ Detail: true, } allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() - if err != nil { - t.Fatalf("Unable to query schedulerstats: %v", err) - } + th.AssertNoErr(t, err) allStats, err := schedulerstats.ExtractStoragePools(allPages) - if err != nil { - t.Fatalf("Unable to extract pools: %v", err) - } + th.AssertNoErr(t, err) for _, stat := range allStats { tools.PrintResource(t, stat) diff --git a/acceptance/openstack/blockstorage/extensions/services_test.go b/acceptance/openstack/blockstorage/extensions/services_test.go index b65f586ec8..df38e98405 100644 --- a/acceptance/openstack/blockstorage/extensions/services_test.go +++ b/acceptance/openstack/blockstorage/extensions/services_test.go @@ -8,23 +8,20 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestServicesList(t *testing.T) { + clients.RequireAdmin(t) + blockClient, err := clients.NewBlockStorageV3Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := services.List(blockClient, services.ListOpts{}).AllPages() - if err != nil { - t.Fatalf("Unable to list services: %v", err) - } + th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) - if err != nil { - t.Fatalf("Unable to extract services") - } + th.AssertNoErr(t, err) for _, service := range allServices { tools.PrintResource(t, service) diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 0c88516765..fe26bcdb37 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" + th "github.com/gophercloud/gophercloud/testhelper" blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" @@ -15,111 +16,76 @@ import ( func TestVolumeActionsUploadImageDestroy(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) + computeClient, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) volumeImage, err := CreateUploadImage(t, blockClient, volume) - if err != nil { - t.Fatalf("Unable to upload volume-backed image: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, volumeImage) err = DeleteUploadedImage(t, computeClient, volumeImage.ImageName) - if err != nil { - t.Fatalf("Unable to delete volume-backed image: %v", err) - } + th.AssertNoErr(t, err) } func TestVolumeActionsAttachCreateDestroy(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) computeClient, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := compute.CreateServer(t, computeClient) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } + th.AssertNoErr(t, err) defer compute.DeleteServer(t, computeClient, server) volume, err := blockstorage.CreateVolume(t, blockClient) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) err = CreateVolumeAttach(t, blockClient, volume, server) - if err != nil { - t.Fatalf("Unable to attach volume: %v", err) - } + th.AssertNoErr(t, err) newVolume, err := volumes.Get(blockClient, volume.ID).Extract() - if err != nil { - t.Fatal("Unable to get updated volume information: %v", err) - } + th.AssertNoErr(t, err) DeleteVolumeAttach(t, blockClient, newVolume) } func TestVolumeActionsReserveUnreserve(t *testing.T) { client, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create blockstorage client: %v", err) - } + th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, client, volume) err = CreateVolumeReserve(t, client, volume) - if err != nil { - t.Fatalf("Unable to create volume reserve: %v", err) - } + th.AssertNoErr(t, err) defer DeleteVolumeReserve(t, client, volume) } func TestVolumeActionsExtendSize(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() - if err != nil { - t.Fatalf("Unable to create a blockstorage client: %v", err) - } + th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } + th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, blockClient, volume) tools.PrintResource(t, volume) err = ExtendVolumeSize(t, blockClient, volume) - if err != nil { - t.Fatalf("Unable to resize volume: %v", err) - } + th.AssertNoErr(t, err) newVolume, err := volumes.Get(blockClient, volume.ID).Extract() - if err != nil { - t.Fatal("Unable to get updated volume information: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newVolume) } diff --git a/script/acceptancetest b/script/acceptancetest index ca4939b5d5..5a1858f4cb 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -65,6 +65,12 @@ if [[ $? != 0 ]]; then failed=1 fi +# Block Storage extensions +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/extensions/ +if [[ $? != 0 ]]; then + failed=1 +fi + # Compute v2 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ if [[ $? != 0 ]]; then From 201218d4cf3e519cb4e2d99767ba14161683ff29 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Mon, 27 Aug 2018 13:39:09 +0200 Subject: [PATCH 0538/2296] Delete cron triggers --- acceptance/openstack/workflow/v2/crontrigger.go | 10 ++++++++++ .../openstack/workflow/v2/crontriggers_test.go | 2 ++ openstack/workflow/v2/crontriggers/requests.go | 6 ++++++ openstack/workflow/v2/crontriggers/results.go | 5 +++++ .../v2/crontriggers/testing/requests_test.go | 15 +++++++++++++++ openstack/workflow/v2/crontriggers/urls.go | 4 ++++ 6 files changed, 42 insertions(+) diff --git a/acceptance/openstack/workflow/v2/crontrigger.go b/acceptance/openstack/workflow/v2/crontrigger.go index fd39743367..cd82fbb643 100644 --- a/acceptance/openstack/workflow/v2/crontrigger.go +++ b/acceptance/openstack/workflow/v2/crontrigger.go @@ -34,3 +34,13 @@ func CreateCronTrigger(t *testing.T, client *gophercloud.ServiceClient, workflow th.AssertEquals(t, crontrigger.Name, crontriggerName) return crontrigger, nil } + +// DeleteCronTrigger deletes a cron trigger. +func DeleteCronTrigger(t *testing.T, client *gophercloud.ServiceClient, crontrigger *crontriggers.CronTrigger) { + err := crontriggers.Delete(client, crontrigger.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete cron trigger %s: %v", crontrigger.Name, err) + } + + t.Logf("Deleted crontrigger: %s", crontrigger.Name) +} diff --git a/acceptance/openstack/workflow/v2/crontriggers_test.go b/acceptance/openstack/workflow/v2/crontriggers_test.go index d22818ec2f..a2ea36d92e 100644 --- a/acceptance/openstack/workflow/v2/crontriggers_test.go +++ b/acceptance/openstack/workflow/v2/crontriggers_test.go @@ -14,9 +14,11 @@ func TestCronTriggersCreateDelete(t *testing.T) { workflow, err := CreateWorkflow(t, client) th.AssertNoErr(t, err) + defer DeleteWorkflow(t, client, workflow) trigger, err := CreateCronTrigger(t, client, workflow) th.AssertNoErr(t, err) + defer DeleteCronTrigger(t, client, trigger) tools.PrintResource(t, trigger) } diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index ec7dcee809..79a1c84ec5 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -65,3 +65,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } + +// Delete deletes the specified cron trigger. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/workflow/v2/crontriggers/results.go b/openstack/workflow/v2/crontriggers/results.go index 44731d3240..564c44560f 100644 --- a/openstack/workflow/v2/crontriggers/results.go +++ b/openstack/workflow/v2/crontriggers/results.go @@ -16,6 +16,11 @@ type CreateResult struct { commonResult } +// DeleteResult is the result from a Delete operation. Call its ExtractErr method to determine the success of the call. +type DeleteResult struct { + gophercloud.ErrResult +} + // Extract helps to get a CronTrigger struct from a Get or a Create function. func (r commonResult) Extract() (*CronTrigger, error) { var s CronTrigger diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index 89bbb26c28..99ae1288f7 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -84,3 +84,18 @@ func TestCreateCronTrigger(t *testing.T) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestDeleteCronTrigger(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/cron_triggers/1", 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) + }) + + res := crontriggers.Delete(fake.ServiceClient(), "1") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/workflow/v2/crontriggers/urls.go b/openstack/workflow/v2/crontriggers/urls.go index 77ae164a6d..90b80241c5 100644 --- a/openstack/workflow/v2/crontriggers/urls.go +++ b/openstack/workflow/v2/crontriggers/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("cron_triggers") } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("cron_triggers", id) +} From 60a338eb0b71be8ae270808b99ab20199494cda5 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Mon, 27 Aug 2018 11:58:36 +0200 Subject: [PATCH 0539/2296] Get executions --- acceptance/openstack/workflow/v2/execution.go | 21 +++++++ openstack/workflow/v2/executions/requests.go | 7 +++ openstack/workflow/v2/executions/results.go | 5 ++ .../v2/executions/testing/requests_test.go | 59 +++++++++++++++++++ openstack/workflow/v2/executions/urls.go | 4 ++ 5 files changed, 96 insertions(+) diff --git a/acceptance/openstack/workflow/v2/execution.go b/acceptance/openstack/workflow/v2/execution.go index 68cefa262b..e2d5b6233f 100644 --- a/acceptance/openstack/workflow/v2/execution.go +++ b/acceptance/openstack/workflow/v2/execution.go @@ -1,6 +1,7 @@ package v2 import ( + "fmt" "testing" "github.com/gophercloud/gophercloud" @@ -33,5 +34,25 @@ func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow * th.AssertEquals(t, execution.Description, executionDescription) + t.Logf("Wait for execution status SUCCESS: %s", executionDescription) + th.AssertNoErr(t, tools.WaitFor(func() (bool, error) { + latest, err := executions.Get(client, execution.ID).Extract() + if err != nil { + return false, err + } + + if latest.State == "SUCCESS" { + execution = latest + return true, nil + } + + if latest.State == "ERROR" { + return false, fmt.Errorf("Execution in ERROR state") + } + + return false, nil + })) + t.Logf("Execution success: %s", executionDescription) + return execution, nil } diff --git a/openstack/workflow/v2/executions/requests.go b/openstack/workflow/v2/executions/requests.go index 6f3f150e09..81e47789e5 100644 --- a/openstack/workflow/v2/executions/requests.go +++ b/openstack/workflow/v2/executions/requests.go @@ -51,3 +51,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } + +// Get retrieves details of a single execution. +// Use ExtractExecution to convert its result into an Execution. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/workflow/v2/executions/results.go b/openstack/workflow/v2/executions/results.go index 8fb40fe7c5..299da3ec54 100644 --- a/openstack/workflow/v2/executions/results.go +++ b/openstack/workflow/v2/executions/results.go @@ -16,6 +16,11 @@ type CreateResult struct { commonResult } +// GetResult is the response of Get operations. Call its Extract method to interpret it as an Execution. +type GetResult struct { + commonResult +} + // Extract helps to get an Execution struct from a Get or a Create function. func (r commonResult) Extract() (*Execution, error) { var s Execution diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index c5f729a5f2..293d9b0061 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -79,3 +79,62 @@ func TestCreateExecution(t *testing.T) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestGetExecution(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/executions/50bb59f1-eb77-4017-a77f-6d575b002667", 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, ` + { + "created_at": "2018-09-12 14:48:49", + "description": "description", + "id": "50bb59f1-eb77-4017-a77f-6d575b002667", + "input": "{\"msg\": \"Hello\"}", + "output": "{}", + "params": "{\"namespace\": \"\", \"env\": {}}", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", + "root_execution_id": null, + "state": "SUCCESS", + "state_info": null, + "task_execution_id": null, + "updated_at": "2018-09-12 14:48:49", + "workflow_id": "6656c143-a009-4bcb-9814-cc100a20bbfa", + "workflow_name": "echo", + "workflow_namespace": "" + } + `) + }) + + actual, err := executions.Get(fake.ServiceClient(), "50bb59f1-eb77-4017-a77f-6d575b002667").Extract() + if err != nil { + t.Fatalf("Unable to get execution: %v", err) + } + + expected := &executions.Execution{ + ID: "50bb59f1-eb77-4017-a77f-6d575b002667", + Description: "description", + Input: map[string]interface{}{ + "msg": "Hello", + }, + Params: map[string]interface{}{ + "namespace": "", + "env": map[string]interface{}{}, + }, + Output: map[string]interface{}{}, + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", + State: "SUCCESS", + WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", + WorkflowName: "echo", + CreatedAt: time.Date(2018, time.September, 12, 14, 48, 49, 0, time.UTC), + UpdatedAt: time.Date(2018, time.September, 12, 14, 48, 49, 0, time.UTC), + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/workflow/v2/executions/urls.go b/openstack/workflow/v2/executions/urls.go index 306ce2da02..ce1910fec5 100644 --- a/openstack/workflow/v2/executions/urls.go +++ b/openstack/workflow/v2/executions/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("executions") } + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("executions", id) +} From f6e733b4f167aa2f91028064fe73cc84bd566520 Mon Sep 17 00:00:00 2001 From: James McCarthy Date: Tue, 18 Sep 2018 10:29:13 +0000 Subject: [PATCH 0540/2296] Block Storage: Add additional multiattach support (#1228) Add missing fields for multiattach support --- .../openstack/blockstorage/v3/volumes_test.go | 28 +++++++++++++++++++ openstack/blockstorage/v2/volumes/requests.go | 2 ++ openstack/blockstorage/v3/volumes/requests.go | 2 ++ 3 files changed, 32 insertions(+) diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index 32ad756c0a..0cb7b8b8ec 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -48,3 +49,30 @@ func TestVolumes(t *testing.T) { th.AssertNoErr(t, err) } + +func TestVolumesMultiAttach(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volumeName := tools.RandomString("ACPTTEST", 16) + + volOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + Description: "Testing creation of multiattach enabled volume", + Multiattach: true, + } + + vol, err := volumes.Create(client, volOpts).Extract() + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, vol.ID, "available", 60) + th.AssertNoErr(t, err) + + th.AssertEquals(t, vol.Multiattach, true) + + err = volumes.Delete(client, vol.ID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index b7977cbc8f..4c8dca707b 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -38,6 +38,8 @@ type CreateOpts struct { ImageID string `json:"imageRef,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` + // Multiattach denotes if the volume is multi-attach capable. + Multiattach bool `json:"multiattach,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index b7977cbc8f..4c8dca707b 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -38,6 +38,8 @@ type CreateOpts struct { ImageID string `json:"imageRef,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` + // Multiattach denotes if the volume is multi-attach capable. + Multiattach bool `json:"multiattach,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a From 1278ec98b0ec8fe624041fd8371472d1870e6168 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 22 Sep 2018 01:53:55 +0000 Subject: [PATCH 0541/2296] Acc Tests: Fixing Networking v2 Floating IP test --- .../networking/v2/extensions/layer3/floatingips_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index e3c0252b76..5e4ba0134a 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -3,7 +3,6 @@ package layer3 import ( - "os" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -96,11 +95,6 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { clients.RequireAdmin(t) - username := os.Getenv("OS_USERNAME") - if username != "admin" { - t.Skip("must be admin to run this test") - } - client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -109,6 +103,7 @@ func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { listOpts := subnets.ListOpts{ NetworkID: choices.ExternalNetworkID, + IPVersion: 4, } subnetPages, err := subnets.List(client, listOpts).AllPages() From 18467f6f603e28a497899347af332b6e041f6650 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Wed, 26 Sep 2018 14:46:40 +0200 Subject: [PATCH 0542/2296] Delete executions --- acceptance/openstack/workflow/v2/execution.go | 9 +++++++++ acceptance/openstack/workflow/v2/executions_test.go | 1 + openstack/workflow/v2/executions/requests.go | 6 ++++++ openstack/workflow/v2/executions/results.go | 5 +++++ .../workflow/v2/executions/testing/requests_test.go | 12 ++++++++++++ openstack/workflow/v2/executions/urls.go | 4 ++++ 6 files changed, 37 insertions(+) diff --git a/acceptance/openstack/workflow/v2/execution.go b/acceptance/openstack/workflow/v2/execution.go index e2d5b6233f..cf6d27e467 100644 --- a/acceptance/openstack/workflow/v2/execution.go +++ b/acceptance/openstack/workflow/v2/execution.go @@ -56,3 +56,12 @@ func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow * return execution, nil } + +// DeleteExecution deletes an execution. +func DeleteExecution(t *testing.T, client *gophercloud.ServiceClient, execution *executions.Execution) { + err := executions.Delete(client, execution.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete executions %s: %v", execution.Description, err) + } + t.Logf("Deleted executions: %s", execution.Description) +} diff --git a/acceptance/openstack/workflow/v2/executions_test.go b/acceptance/openstack/workflow/v2/executions_test.go index 8db7a7e8b8..b286bc71f2 100644 --- a/acceptance/openstack/workflow/v2/executions_test.go +++ b/acceptance/openstack/workflow/v2/executions_test.go @@ -18,6 +18,7 @@ func TestExecutionsCreate(t *testing.T) { execution, err := CreateExecution(t, client, workflow) th.AssertNoErr(t, err) + defer DeleteExecution(t, client, execution) tools.PrintResource(t, execution) } diff --git a/openstack/workflow/v2/executions/requests.go b/openstack/workflow/v2/executions/requests.go index 81e47789e5..dbe431ec39 100644 --- a/openstack/workflow/v2/executions/requests.go +++ b/openstack/workflow/v2/executions/requests.go @@ -58,3 +58,9 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } + +// Delete deletes the specified execution. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/workflow/v2/executions/results.go b/openstack/workflow/v2/executions/results.go index 299da3ec54..b6a6b96e84 100644 --- a/openstack/workflow/v2/executions/results.go +++ b/openstack/workflow/v2/executions/results.go @@ -28,6 +28,11 @@ func (r commonResult) Extract() (*Execution, error) { return &s, err } +// DeleteResult is the result from a Delete operation. Call its ExtractErr method to determine the success of the call. +type DeleteResult struct { + gophercloud.ErrResult +} + // Execution represents a workflow execution on OpenStack mistral API. type Execution struct { // ID is the execution's unique ID. diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index 293d9b0061..ac66b76a56 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -138,3 +138,15 @@ func TestGetExecution(t *testing.T) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestDeleteExecution(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/executions/1", 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) + }) + res := executions.Delete(fake.ServiceClient(), "1") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/workflow/v2/executions/urls.go b/openstack/workflow/v2/executions/urls.go index ce1910fec5..97321857a0 100644 --- a/openstack/workflow/v2/executions/urls.go +++ b/openstack/workflow/v2/executions/urls.go @@ -9,3 +9,7 @@ func createURL(client *gophercloud.ServiceClient) string { func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("executions", id) } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("executions", id) +} From dee26897cc174be18c9087159f9ad0547f3557f4 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Wed, 26 Sep 2018 17:23:38 +0200 Subject: [PATCH 0543/2296] Get Crontriggers --- .../openstack/workflow/v2/crontrigger.go | 10 ++ .../workflow/v2/crontriggers_test.go | 7 +- .../workflow/v2/crontriggers/requests.go | 7 ++ openstack/workflow/v2/crontriggers/results.go | 5 + .../v2/crontriggers/testing/requests_test.go | 93 +++++++++++++++---- openstack/workflow/v2/crontriggers/urls.go | 4 + 6 files changed, 107 insertions(+), 19 deletions(-) diff --git a/acceptance/openstack/workflow/v2/crontrigger.go b/acceptance/openstack/workflow/v2/crontrigger.go index cd82fbb643..baa06240f8 100644 --- a/acceptance/openstack/workflow/v2/crontrigger.go +++ b/acceptance/openstack/workflow/v2/crontrigger.go @@ -44,3 +44,13 @@ func DeleteCronTrigger(t *testing.T, client *gophercloud.ServiceClient, crontrig t.Logf("Deleted crontrigger: %s", crontrigger.Name) } + +// GetCronTrigger gets a cron trigger. +func GetCronTrigger(t *testing.T, client *gophercloud.ServiceClient, id string) (*crontriggers.CronTrigger, error) { + crontrigger, err := crontriggers.Get(client, id).Extract() + if err != nil { + t.Fatalf("Unable to get cron trigger %s: %v", id, err) + } + t.Logf("Cron trigger %s get", id) + return crontrigger, err +} diff --git a/acceptance/openstack/workflow/v2/crontriggers_test.go b/acceptance/openstack/workflow/v2/crontriggers_test.go index a2ea36d92e..5e07e50c8e 100644 --- a/acceptance/openstack/workflow/v2/crontriggers_test.go +++ b/acceptance/openstack/workflow/v2/crontriggers_test.go @@ -8,7 +8,7 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestCronTriggersCreateDelete(t *testing.T) { +func TestCronTriggersCreateGetDelete(t *testing.T) { client, err := clients.NewWorkflowV2Client() th.AssertNoErr(t, err) @@ -20,5 +20,10 @@ func TestCronTriggersCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCronTrigger(t, client, trigger) + gettrigger, err := GetCronTrigger(t, client, trigger.ID) + th.AssertNoErr(t, err) + + th.AssertEquals(t, trigger.ID, gettrigger.ID) + tools.PrintResource(t, trigger) } diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index 79a1c84ec5..7916721da6 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -71,3 +71,10 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } + +// Get retrieves details of a single cron trigger. +// Use Extract to convert its result into an CronTrigger. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/workflow/v2/crontriggers/results.go b/openstack/workflow/v2/crontriggers/results.go index 564c44560f..2d14b13d40 100644 --- a/openstack/workflow/v2/crontriggers/results.go +++ b/openstack/workflow/v2/crontriggers/results.go @@ -16,6 +16,11 @@ type CreateResult struct { commonResult } +// GetResult is the response of Get operations. Call its Extract method to interpret it as a CronTrigger. +type GetResult struct { + commonResult +} + // DeleteResult is the result from a Delete operation. Call its ExtractErr method to determine the success of the call. type DeleteResult struct { gophercloud.ErrResult diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index 99ae1288f7..4a32eb6843 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -24,25 +24,25 @@ func TestCreateCronTrigger(t *testing.T) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { - "created_at": "1970-01-01 00:00:00", - "id": "1", - "name": "trigger", - "pattern": "* * * * *", - "project_id": "p1", + "created_at": "2018-09-12 15:48:18", + "first_execution_time": "2018-09-12 17:48:00", + "id": "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", + "name": "crontrigger", + "next_execution_time": "2018-09-12 17:48:00", + "pattern": "0 0 1 1 *", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "remaining_executions": 42, "scope": "private", - "updated_at": "1970-01-01 00:00:00", - "first_execution_time": "1970-01-01 00:00:00", - "next_execution_time": "1970-01-01 00:00:00", - "workflow_id": "w1", + "updated_at": null, + "workflow_id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "workflow_input": "{\"msg\": \"hello\"}", - "workflow_name": "my_wf", + "workflow_name": "workflow_echo", "workflow_params": "{\"msg\": \"world\"}" } `) }) - firstExecution := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + firstExecution := time.Date(2018, time.September, 12, 17, 48, 0, 0, time.UTC) opts := &crontriggers.CreateOpts{ WorkflowID: "w1", Name: "trigger", @@ -61,21 +61,21 @@ func TestCreateCronTrigger(t *testing.T) { } expected := &crontriggers.CronTrigger{ - ID: "1", - Name: "trigger", - Pattern: "* * * * *", - ProjectID: "p1", + ID: "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", + Name: "crontrigger", + Pattern: "0 0 1 1 *", + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", RemainingExecutions: 42, Scope: "private", - WorkflowID: "w1", - WorkflowName: "my_wf", + WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", + WorkflowName: "workflow_echo", WorkflowParams: map[string]interface{}{ "msg": "world", }, WorkflowInput: map[string]interface{}{ "msg": "hello", }, - CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), + CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), FirstExecutionTime: &firstExecution, NextExecutionTime: &firstExecution, } @@ -99,3 +99,60 @@ func TestDeleteCronTrigger(t *testing.T) { res := crontriggers.Delete(fake.ServiceClient(), "1") th.AssertNoErr(t, res.Err) } + +func TestGetCronTrigger(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/cron_triggers/1", 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, ` + { + "created_at": "2018-09-12 15:48:18", + "first_execution_time": "2018-09-12 17:48:00", + "id": "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", + "name": "crontrigger", + "next_execution_time": "2018-09-12 17:48:00", + "pattern": "0 0 1 1 *", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", + "remaining_executions": 42, + "scope": "private", + "updated_at": null, + "workflow_id": "604a3a1e-94e3-4066-a34a-aa56873ef236", + "workflow_input": "{\"msg\": \"hello\"}", + "workflow_name": "workflow_echo", + "workflow_params": "{\"msg\": \"world\"}" + } + `) + }) + actual, err := crontriggers.Get(fake.ServiceClient(), "1").Extract() + if err != nil { + t.Fatalf("Unable to get cron trigger: %v", err) + } + + firstExecution := time.Date(2018, time.September, 12, 17, 48, 0, 0, time.UTC) + + expected := &crontriggers.CronTrigger{ + ID: "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", + Name: "crontrigger", + Pattern: "0 0 1 1 *", + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", + RemainingExecutions: 42, + Scope: "private", + WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", + WorkflowName: "workflow_echo", + WorkflowParams: map[string]interface{}{ + "msg": "world", + }, + WorkflowInput: map[string]interface{}{ + "msg": "hello", + }, + CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), + FirstExecutionTime: &firstExecution, + NextExecutionTime: &firstExecution, + } + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} diff --git a/openstack/workflow/v2/crontriggers/urls.go b/openstack/workflow/v2/crontriggers/urls.go index 90b80241c5..75ff45589d 100644 --- a/openstack/workflow/v2/crontriggers/urls.go +++ b/openstack/workflow/v2/crontriggers/urls.go @@ -9,3 +9,7 @@ func createURL(client *gophercloud.ServiceClient) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("cron_triggers", id) } + +func getURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("cron_triggers", id) +} From 7b35dc557d33759e58c0efc0e77aa3ba83367422 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Thu, 27 Sep 2018 14:30:00 +0200 Subject: [PATCH 0544/2296] Crontrigger list --- .../openstack/workflow/v2/crontrigger.go | 14 +++ .../workflow/v2/crontriggers_test.go | 18 ++++ openstack/workflow/v2/crontriggers/doc.go | 33 +++++-- .../workflow/v2/crontriggers/requests.go | 52 ++++++++++ openstack/workflow/v2/crontriggers/results.go | 33 +++++++ .../v2/crontriggers/testing/requests_test.go | 95 ++++++++++++++++++- openstack/workflow/v2/crontriggers/urls.go | 4 + 7 files changed, 237 insertions(+), 12 deletions(-) diff --git a/acceptance/openstack/workflow/v2/crontrigger.go b/acceptance/openstack/workflow/v2/crontrigger.go index baa06240f8..20a7fd653e 100644 --- a/acceptance/openstack/workflow/v2/crontrigger.go +++ b/acceptance/openstack/workflow/v2/crontrigger.go @@ -54,3 +54,17 @@ func GetCronTrigger(t *testing.T, client *gophercloud.ServiceClient, id string) t.Logf("Cron trigger %s get", id) return crontrigger, err } + +// ListCronTriggers lists cron triggers. +func ListCronTriggers(t *testing.T, client *gophercloud.ServiceClient, opts crontriggers.ListOptsBuilder) ([]crontriggers.CronTrigger, error) { + allPages, err := crontriggers.List(client, opts).AllPages() + if err != nil { + t.Fatalf("Unable to list cron triggers: %v", err) + } + crontriggersList, err := crontriggers.ExtractCronTriggers(allPages) + if err != nil { + t.Fatalf("Unable to extract cron triggers: %v", err) + } + t.Logf("Cron triggers list found, length: %d", len(crontriggersList)) + return crontriggersList, err +} diff --git a/acceptance/openstack/workflow/v2/crontriggers_test.go b/acceptance/openstack/workflow/v2/crontriggers_test.go index 5e07e50c8e..95bd356e3f 100644 --- a/acceptance/openstack/workflow/v2/crontriggers_test.go +++ b/acceptance/openstack/workflow/v2/crontriggers_test.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -27,3 +28,20 @@ func TestCronTriggersCreateGetDelete(t *testing.T) { tools.PrintResource(t, trigger) } + +func TestCronTriggersList(t *testing.T) { + client, err := clients.NewWorkflowV2Client() + th.AssertNoErr(t, err) + workflow, err := CreateWorkflow(t, client) + th.AssertNoErr(t, err) + defer DeleteWorkflow(t, client, workflow) + trigger, err := CreateCronTrigger(t, client, workflow) + th.AssertNoErr(t, err) + defer DeleteCronTrigger(t, client, trigger) + list, err := ListCronTriggers(t, client, &crontriggers.ListOpts{ + Name: trigger.Name, + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(list)) + tools.PrintResource(t, list) +} diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go index 6f0d03f9a6..d92281c16b 100644 --- a/openstack/workflow/v2/crontriggers/doc.go +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -4,10 +4,10 @@ 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. -Example to list cron triggers +List cron triggers listOpts := crontriggers.ListOpts{ - WorkflowID: "w1", + WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", } allPages, err := crontriggers.List(mistralClient, listOpts).AllPages() if err != nil { @@ -17,25 +17,44 @@ Example to list cron triggers if err != nil { panic(err) } - for _, ex := range allCrontriggers { - fmt.Printf("%+v\n", ex) + for _, ct := range allCrontriggers { + fmt.Printf("%+v\n", ct) } -Example to create a cron trigger +Create a cron trigger. This example will start the workflow "echo" each day at 8am, and it will end after 10 executions. createOpts := &crontriggers.CreateOpts{ - WorkflowID: "w1", + Name: "daily", + Pattern: "0 8 * * *", + WorkflowName: "echo", + RemainingExecutions: 10, WorkflowParams: map[string]interface{}{ "msg": "hello", }, WorkflowInput: map[string]interface{}{ "msg": "world", }, - Name: "trigger", } crontrigger, err := crontriggers.Create(mistralClient, opts).Extract() if err != nil { panic(err) } + +Get a cron trigger + + crontrigger, err := crontriggers.Get(mistralClient, "0520ffd8-f7f1-4f2e-845b-55d953a1cf46").Extract() + if err != nil { + panic(err) + } + + fmt.Printf(%+v\n", crontrigger) + +Delete a cron trigger + + res := crontriggers.Delete(mistralClient, "0520ffd8-f7f1-4f2e-845b-55d953a1cf46") + if res.Err != nil { + panic(res.Err) + } + */ package crontriggers diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index 7916721da6..c911fe56c3 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -4,6 +4,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extension to add additional parameters to the Create request. @@ -78,3 +79,54 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } + +// ListOptsBuilder allows extension to add additional parameters to the List request. +type ListOptsBuilder interface { + ToCronTriggerListQuery() (string, error) +} + +// ListOpts filters the result returned by the List() function. +type ListOpts struct { + // WorkflowName allows to filter by workflow name. + WorkflowName string `q:"workflow_name"` + // WorkflowID allows to filter by workflow id. + WorkflowID string `q:"workflow_id"` + // Name allows to filter by trigger name. + Name string `q:"name"` + // Scope filters by the trigger's scope. + // Values can be "private" or "public". + Scope string `q:"scope"` + // SortDir allows to select sort direction. + // It can be "asc" or "desc" (default). + SortDir string `q:"sort_dir"` + // SortKey allows to sort by one of the cron trigger attributes. + SortKey string `q:"sort_key"` + // Marker and Limit control paging. + // Marker instructs List where to start listing from. + Marker string `q:"marker"` + // Limit instructs List to refrain from sending excessively large lists of + // cron triggers. + Limit int `q:"limit"` +} + +// ToCronTriggerListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToCronTriggerListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List performs a call to list cron triggers. +// You may provide options to filter the results. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToCronTriggerListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return CronTriggerPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/workflow/v2/crontriggers/results.go b/openstack/workflow/v2/crontriggers/results.go index 2d14b13d40..02568fba5e 100644 --- a/openstack/workflow/v2/crontriggers/results.go +++ b/openstack/workflow/v2/crontriggers/results.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { @@ -117,3 +118,35 @@ func (r *CronTrigger) UnmarshalJSON(b []byte) error { return nil } + +// CronTriggerPage contains a single page of all cron triggers from a List call. +type CronTriggerPage struct { + pagination.LinkedPageBase +} + +// IsEmpty checks if an CronTriggerPage contains any results. +func (r CronTriggerPage) IsEmpty() (bool, error) { + exec, err := ExtractCronTriggers(r) + return len(exec) == 0, err +} + +// NextPageURL finds the next page URL in a page in order to navigate to the next page of results. +func (r CronTriggerPage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Next, nil +} + +// ExtractCronTriggers get the list of cron triggers from a page acquired from the List call. +func ExtractCronTriggers(r pagination.Page) ([]CronTrigger, error) { + var s struct { + CronTriggers []CronTrigger `json:"cron_triggers"` + } + err := (r.(CronTriggerPage)).ExtractInto(&s) + return s.CronTriggers, err +} diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index 4a32eb6843..dfe4011a9d 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -44,7 +45,7 @@ func TestCreateCronTrigger(t *testing.T) { firstExecution := time.Date(2018, time.September, 12, 17, 48, 0, 0, time.UTC) opts := &crontriggers.CreateOpts{ - WorkflowID: "w1", + WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", Name: "trigger", FirstExecutionTime: &firstExecution, WorkflowParams: map[string]interface{}{ @@ -89,21 +90,21 @@ func TestDeleteCronTrigger(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/cron_triggers/1", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/cron_triggers/0520ffd8-f7f1-4f2e-845b-55d953a1cf46", 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) }) - res := crontriggers.Delete(fake.ServiceClient(), "1") + res := crontriggers.Delete(fake.ServiceClient(), "0520ffd8-f7f1-4f2e-845b-55d953a1cf46") th.AssertNoErr(t, res.Err) } func TestGetCronTrigger(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/cron_triggers/1", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/cron_triggers/0520ffd8-f7f1-4f2e-845b-55d953a1cf46", 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") @@ -126,7 +127,7 @@ func TestGetCronTrigger(t *testing.T) { } `) }) - actual, err := crontriggers.Get(fake.ServiceClient(), "1").Extract() + actual, err := crontriggers.Get(fake.ServiceClient(), "0520ffd8-f7f1-4f2e-845b-55d953a1cf46").Extract() if err != nil { t.Fatalf("Unable to get cron trigger: %v", err) } @@ -156,3 +157,87 @@ func TestGetCronTrigger(t *testing.T) { t.Errorf("Expected %#v, but was %#v", expected, actual) } } + +func TestListCronTriggers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/cron_triggers", 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") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, `{ + "cron_triggers": [ + { + "created_at": "2018-09-12 15:48:18", + "first_execution_time": "2018-09-12 17:48:00", + "id": "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", + "name": "crontrigger", + "next_execution_time": "2018-09-12 17:48:00", + "pattern": "0 0 1 1 *", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", + "remaining_executions": 42, + "scope": "private", + "updated_at": null, + "workflow_id": "604a3a1e-94e3-4066-a34a-aa56873ef236", + "workflow_input": "{\"msg\": \"hello\"}", + "workflow_name": "workflow_echo", + "workflow_params": "{\"msg\": \"world\"}" + } + ], + "next": "%s/cron_triggers?marker=0520ffd8-f7f1-4f2e-845b-55d953a1cf46" + }`, th.Server.URL) + case "0520ffd8-f7f1-4f2e-845b-55d953a1cf46": + fmt.Fprintf(w, `{ "cron_triggers": [] }`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) + pages := 0 + // Get all cron triggers + err := crontriggers.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + pages++ + actual, err := crontriggers.ExtractCronTriggers(page) + if err != nil { + return false, err + } + + firstExecution := time.Date(2018, time.September, 12, 17, 48, 0, 0, time.UTC) + + expected := []crontriggers.CronTrigger{ + crontriggers.CronTrigger{ + ID: "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", + Name: "crontrigger", + Pattern: "0 0 1 1 *", + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", + RemainingExecutions: 42, + Scope: "private", + WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", + WorkflowName: "workflow_echo", + WorkflowParams: map[string]interface{}{ + "msg": "world", + }, + WorkflowInput: map[string]interface{}{ + "msg": "hello", + }, + CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), + FirstExecutionTime: &firstExecution, + NextExecutionTime: &firstExecution, + }, + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } + return true, nil + }) + if err != nil { + t.Fatal(err) + } + if pages != 1 { + t.Errorf("Expected one page, got %d", pages) + } +} diff --git a/openstack/workflow/v2/crontriggers/urls.go b/openstack/workflow/v2/crontriggers/urls.go index 75ff45589d..853dbe30c4 100644 --- a/openstack/workflow/v2/crontriggers/urls.go +++ b/openstack/workflow/v2/crontriggers/urls.go @@ -13,3 +13,7 @@ func deleteURL(client *gophercloud.ServiceClient, id string) string { func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("cron_triggers", id) } + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("cron_triggers") +} From 20af3d03e51a4ef678368932807fb6e0cb075106 Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Thu, 27 Sep 2018 09:58:40 -0700 Subject: [PATCH 0545/2296] Fix documentation of ListDetail to List to match the actual implementation. --- openstack/clustering/v1/clusters/doc.go | 2 +- openstack/clustering/v1/profiletypes/results.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index f731fb5376..94d6d60873 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -30,7 +30,7 @@ Example to List Clusters Name: "testcluster", } - allPages, err := clusters.ListDetail(serviceClient, listOpts).AllPages() + allPages, err := clusters.List(serviceClient, listOpts).AllPages() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/profiletypes/results.go b/openstack/clustering/v1/profiletypes/results.go index 3259f68ac3..5364727f67 100644 --- a/openstack/clustering/v1/profiletypes/results.go +++ b/openstack/clustering/v1/profiletypes/results.go @@ -41,7 +41,7 @@ func ExtractProfileTypes(r pagination.Page) ([]ProfileType, error) { return s.ProfileTypes, err } -// ProfileTypePage contains a single page of all profiles from a ListDetails call. +// ProfileTypePage contains a single page of all profiles from a List call. type ProfileTypePage struct { pagination.LinkedPageBase } From bfc006765209a570e9a783bd7e4372e24fb72781 Mon Sep 17 00:00:00 2001 From: James McCarthy Date: Fri, 28 Sep 2018 23:43:55 +0100 Subject: [PATCH 0546/2296] Block Storage v2: Remove multiattach field from volumes Add missing fields for multiattach support - to avoid support issues, remove multiattach field from blockstorage v2. "In Cinder the functionality is available from microversion '3.50' or higher." --- openstack/blockstorage/v2/volumes/requests.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index 4c8dca707b..b7977cbc8f 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -38,8 +38,6 @@ type CreateOpts struct { ImageID string `json:"imageRef,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` - // Multiattach denotes if the volume is multi-attach capable. - Multiattach bool `json:"multiattach,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a From 78256f274b2a32b3deebad5b1e9e6542d49e665e Mon Sep 17 00:00:00 2001 From: itsc0lin <43658752+itsc0lin@users.noreply.github.com> Date: Mon, 1 Oct 2018 18:31:08 -0700 Subject: [PATCH 0547/2296] Add ProtocolUDP support for UDP load balancers introduced in Octavia Rocky (#1253) --- .../networking/v2/extensions/lbaas_v2/listeners/requests.go | 1 + openstack/networking/v2/extensions/lbaas_v2/pools/requests.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index dd190f606f..8f47f5d4f1 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -11,6 +11,7 @@ type Protocol string // Supported attributes for create/update operations. const ( ProtocolTCP Protocol = "TCP" + ProtocolUDP Protocol = "UDP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index 11564be83f..3454d71ca5 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -68,6 +68,7 @@ const ( LBMethodSourceIp LBMethod = "SOURCE_IP" ProtocolTCP Protocol = "TCP" + ProtocolUDP Protocol = "UDP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) @@ -87,7 +88,7 @@ type CreateOpts struct { LBMethod LBMethod `json:"lb_algorithm" required:"true"` // The protocol used by the pool members, you can use either - // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. + // ProtocolTCP, ProtocolUDP, ProtocolHTTP, or ProtocolHTTPS. Protocol Protocol `json:"protocol" required:"true"` // The Loadbalancer on which the members of the pool will be associated with. From 12bd25dcbf16eb2a3fa8332bd1136de2304cbdd0 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Mon, 1 Oct 2018 20:31:10 -0700 Subject: [PATCH 0548/2296] Fixed where creating profile and policies will send spec "version" in the json to the API backend (#1250) --- openstack/clustering/v1/policies/results.go | 13 +++++++++++++ openstack/clustering/v1/profiles/results.go | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index 46c88da9b8..89b565aaf9 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -94,6 +94,19 @@ func (r *Spec) UnmarshalJSON(b []byte) error { return nil } +func (r Spec) MarshalJSON() ([]byte, error) { + spec := struct { + Type string `json:"type"` + Version string `json:"version"` + Properties map[string]interface{} `json:"properties"` + }{ + Type: r.Type, + Version: r.Version, + Properties: r.Properties, + } + return json.Marshal(spec) +} + // policyResult is the resposne of a base Policy result. type policyResult struct { gophercloud.Result diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index 30bacadfee..f1a3770dae 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -89,6 +89,19 @@ func (r *Spec) UnmarshalJSON(b []byte) error { return nil } +func (r Spec) MarshalJSON() ([]byte, error) { + spec := struct { + Type string `json:"type"` + Version string `json:"version"` + Properties map[string]interface{} `json:"properties"` + }{ + Type: r.Type, + Version: r.Version, + Properties: r.Properties, + } + return json.Marshal(spec) +} + // commonResult is the base result of a Profile operation. type commonResult struct { gophercloud.Result From 449932d14421e059cbe2a27312d3735460435eae Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Sat, 6 Oct 2018 04:00:03 +0200 Subject: [PATCH 0549/2296] Networking v2 Trunk support - Create (#1258) This patch adds the Networking extension trunk support for Create operations. Signed-off-by: Antoni Segura Puimedon --- .../networking/v2/extensions/trunks/trunks.go | 36 ++++ .../v2/extensions/trunks/trunks_test.go | 53 ++++++ .../networking/v2/extensions/trunks/doc.go | 51 ++++++ .../v2/extensions/trunks/requests.go | 41 +++++ .../v2/extensions/trunks/results.go | 74 ++++++++ .../v2/extensions/trunks/testing/doc.go | 2 + .../v2/extensions/trunks/testing/fixtures.go | 162 ++++++++++++++++++ .../trunks/testing/requests_test.go | 89 ++++++++++ .../networking/v2/extensions/trunks/urls.go | 13 ++ 9 files changed, 521 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/trunks/trunks.go create mode 100644 acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go create mode 100644 openstack/networking/v2/extensions/trunks/doc.go create mode 100644 openstack/networking/v2/extensions/trunks/requests.go create mode 100644 openstack/networking/v2/extensions/trunks/results.go create mode 100644 openstack/networking/v2/extensions/trunks/testing/doc.go create mode 100644 openstack/networking/v2/extensions/trunks/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/trunks/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/trunks/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks.go new file mode 100644 index 0000000000..ca0233c133 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks.go @@ -0,0 +1,36 @@ +package trunks + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" +) + +func CreateTrunk(t *testing.T, client *gophercloud.ServiceClient, parentPortID string, subportIDs ...string) (trunk *trunks.Trunk, err error) { + trunkName := tools.RandomString("TESTACC-", 8) + iTrue := true + opts := trunks.CreateOpts{ + Name: trunkName, + Description: "Trunk created by gophercloud", + AdminStateUp: &iTrue, + PortID: parentPortID, + } + + opts.Subports = make([]trunks.Subport, len(subportIDs)) + for id, subportID := range subportIDs { + opts.Subports[id] = trunks.Subport{ + SegmentationID: id + 1, + SegmentationType: "vlan", + PortID: subportID, + } + } + + t.Logf("Attempting to create trunk: %s", opts.Name) + trunk, err = trunks.Create(client, opts).Extract() + if err == nil { + t.Logf("Successfully created trunk") + } + return +} diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go new file mode 100644 index 0000000000..fd07d3ae87 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -0,0 +1,53 @@ +// +build acceptance trunks + +package trunks + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/acceptance/tools" +) + +func TestTrunkCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := v2.CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + + // Create Subnet + subnet, err := v2.CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + + // Create port + parentPort, err := v2.CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + + subport1, err := v2.CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + + subport2, err := v2.CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + + trunk, err := CreateTrunk(t, client, parentPort.ID, subport1.ID, subport2.ID) + if err != nil { + t.Fatalf("Unable to create trunk: %v", err) + } + + tools.PrintResource(t, trunk) +} diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go new file mode 100644 index 0000000000..7bd093ecde --- /dev/null +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -0,0 +1,51 @@ +/* +Package trunks provides the ability to retrieve and manage trunks through the Neutron API. +Trunks allow you to multiplex multiple ports traffic on a single port. For example, you could +have a compute instance port be the parent port of a trunk and inside the VM run workloads +using other ports, without the need of plugging those ports. + +Example of a new empty Trunk creation + + iTrue := true + createOpts := trunks.CreateOpts{ + Name: "gophertrunk", + Description: "Trunk created by gophercloud", + AdminStateUp: &iTrue, + PortID: "a6f0560c-b7a8-401f-bf6e-d0a5c851ae10", + } + + trunk, err := trunks.Create(networkClient, trunkOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", trunk) + +Example of a new Trunk creation with 2 subports + + iTrue := true + createOpts := trunks.CreateOpts{ + Name: "gophertrunk", + Description: "Trunk created by gophercloud", + AdminStateUp: &iTrue, + PortID: "a6f0560c-b7a8-401f-bf6e-d0a5c851ae10", + Subports: []trunks.Subport{ + { + SegmentationID: 1, + SegmentationType: "vlan", + PortID: "bf4efcc0-b1c7-4674-81f0-31f58a33420a", + }, + { + SegmentationID: 10, + SegmentationType: "vlan", + PortID: "2cf671b9-02b3-4121-9e85-e0af3548d112", + }, + }, + } + + trunk, err := trunks.Create(client, trunkOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", trunk) +*/ +package trunks diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go new file mode 100644 index 0000000000..1c024373e7 --- /dev/null +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -0,0 +1,41 @@ +package trunks + +import ( + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToTrunkCreateMap() (map[string]interface{}, error) +} + +// CreateOpts represents the attributes used when creating a new trunk. +type CreateOpts struct { + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` + PortID string `json:"port_id" required:"true"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Subports []Subport `json:"sub_ports"` +} + +// ToTrunkCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToTrunkCreateMap() (map[string]interface{}, error) { + if opts.Subports == nil { + opts.Subports = []Subport{} + } + return gophercloud.BuildRequestBody(opts, "trunk") +} + +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + body, err := opts.ToTrunkCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = c.Post(createURL(c), body, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go new file mode 100644 index 0000000000..b38f6243fb --- /dev/null +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -0,0 +1,74 @@ +package trunks + +import ( + "time" + + "github.com/gophercloud/gophercloud" +) + +type Subport struct { + SegmentationID int `json:"segmentation_id"` + SegmentationType string `json:"segmentation_type"` + PortID string `json:"port_id"` +} + +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Trunk. +type CreateResult struct { + commonResult +} + +type Trunk struct { + // Indicates whether the trunk is currently operational. Possible values include + // `ACTIVE', `DOWN', `BUILD', 'DEGRADED' or `ERROR'. + Status string `json:"status"` + + // A list of ports associated with the trunk + Subports []Subport `json:"sub_ports"` + + // Human-readable name for the trunk. Might not be unique. + Name string `json:"name,omitempty"` + + // The administrative state of the trunk. If false (down), the trunk does not + // forward packets. + AdminStateUp bool `json:"admin_state_up,omitempty"` + + // ProjectID is the project owner of the trunk. + ProjectID string `json:"project_id"` + + // TenantID is the project owner of the trunk. + TenantID string `json:"tenant_id"` + + // The date and time when the resource was created. + CreatedAt time.Time `json:"created_at"` + + // The date and time when the resource was updated, + // if the resource has not been updated, this field will show as null. + UpdatedAt time.Time `json:"updated_at"` + + RevisionNumber int `json:"revision_number"` + + // UUID of the trunk's parent port + PortID string `json:"port_id"` + + // UUID for the trunk resource + ID string `json:"id"` + + // Display description. + Description string `json:"description"` + + // A list of tags associated with the trunk + Tags []string `json:"tags,omitempty"` +} + +func (r commonResult) Extract() (*Trunk, error) { + var s struct { + Trunk *Trunk `json:"trunk"` + } + err := r.ExtractInto(&s) + return s.Trunk, err +} diff --git a/openstack/networking/v2/extensions/trunks/testing/doc.go b/openstack/networking/v2/extensions/trunks/testing/doc.go new file mode 100644 index 0000000000..11dd613171 --- /dev/null +++ b/openstack/networking/v2/extensions/trunks/testing/doc.go @@ -0,0 +1,2 @@ +// trunks unit tests +package testing diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures.go new file mode 100644 index 0000000000..0b5b787fa9 --- /dev/null +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures.go @@ -0,0 +1,162 @@ +package testing + +import ( + "time" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" +) + +const CreateRequest = ` +{ + "trunk": { + "admin_state_up": true, + "description": "Trunk created by gophercloud", + "name": "gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "sub_ports": [ + { + "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + "segmentation_id": 1, + "segmentation_type": "vlan" + }, + { + "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + "segmentation_id": 2, + "segmentation_type": "vlan" + } + ] + } +}` + +const CreateResponse = ` +{ + "trunk": { + "admin_state_up": true, + "created_at": "2018-10-03T13:57:24Z", + "description": "Trunk created by gophercloud", + "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", + "name": "gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "project_id": "e153f3f9082240a5974f667cfe1036e3", + "revision_number": 1, + "status": "ACTIVE", + "sub_ports": [ + { + "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + "segmentation_id": 1, + "segmentation_type": "vlan" + }, + { + "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + "segmentation_id": 2, + "segmentation_type": "vlan" + } + ], + "tags": [], + "tenant_id": "e153f3f9082240a5974f667cfe1036e3", + "updated_at": "2018-10-03T13:57:26Z" + } +}` + +const CreateNoSubportsRequest = ` +{ + "trunk": { + "admin_state_up": true, + "description": "Trunk created by gophercloud", + "name": "gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "sub_ports": [] + } +}` + +const CreateNoSubportsResponse = ` +{ + "trunk": { + "admin_state_up": true, + "created_at": "2018-10-03T13:57:24Z", + "description": "Trunk created by gophercloud", + "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", + "name": "gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "project_id": "e153f3f9082240a5974f667cfe1036e3", + "revision_number": 1, + "status": "ACTIVE", + "sub_ports": [], + "tags": [], + "tenant_id": "e153f3f9082240a5974f667cfe1036e3", + "updated_at": "2018-10-03T13:57:26Z" + } +}` + +var ExpectedSubports = []trunks.Subport{ + { + PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + SegmentationID: 1, + SegmentationType: "vlan", + }, + { + PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + SegmentationID: 2, + SegmentationType: "vlan", + }, +} + +func ExpectedTrunkSlice() (exp []trunks.Trunk, err error) { + trunk1CreatedAt, err := time.Parse(time.RFC3339, "2018-10-01T15:29:39Z") + if err != nil { + return nil, err + } + + trunk1UpdatedAt, err := time.Parse(time.RFC3339, "2018-10-01T15:43:04Z") + if err != nil { + return nil, err + } + exp = make([]trunks.Trunk, 2) + exp[0] = trunks.Trunk{ + AdminStateUp: true, + Description: "", + ID: "3e72aa1b-d0da-48f2-831a-fd1c5f3f99c2", + Name: "mytrunk", + PortID: "16c425d3-d7fc-40b8-b94c-cc95da45b270", + ProjectID: "e153f3f9082240a5974f667cfe1036e3", + TenantID: "e153f3f9082240a5974f667cfe1036e3", + RevisionNumber: 3, + Status: "ACTIVE", + Subports: []trunks.Subport{ + { + PortID: "424da4b7-7868-4db2-bb71-05155601c6e4", + SegmentationID: 11, + SegmentationType: "vlan", + }, + }, + Tags: []string{}, + CreatedAt: trunk1CreatedAt, + UpdatedAt: trunk1UpdatedAt, + } + + trunk2CreatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:24Z") + if err != nil { + return nil, err + } + + trunk2UpdatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:26Z") + if err != nil { + return nil, err + } + exp[1] = trunks.Trunk{ + AdminStateUp: true, + Description: "Trunk created by gophercloud", + ID: "f6a9718c-5a64-43e3-944f-4deccad8e78c", + Name: "gophertrunk", + PortID: "c373d2fa-3d3b-4492-924c-aff54dea19b6", + ProjectID: "e153f3f9082240a5974f667cfe1036e3", + TenantID: "e153f3f9082240a5974f667cfe1036e3", + RevisionNumber: 1, + Status: "ACTIVE", + Subports: ExpectedSubports, + Tags: []string{}, + CreatedAt: trunk2CreatedAt, + UpdatedAt: trunk2UpdatedAt, + } + return +} diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go new file mode 100644 index 0000000000..3f4ff0cceb --- /dev/null +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -0,0 +1,89 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks", 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) + }) + + iTrue := true + options := trunks.CreateOpts{ + Name: "gophertrunk", + Description: "Trunk created by gophercloud", + AdminStateUp: &iTrue, + Subports: []trunks.Subport{ + { + SegmentationID: 1, + SegmentationType: "vlan", + PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + }, + { + SegmentationID: 2, + SegmentationType: "vlan", + PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + }, + }, + } + _, err := trunks.Create(fake.ServiceClient(), options).Extract() + if err == nil { + t.Fatalf("Failed to detect missing parent PortID field") + } + options.PortID = "c373d2fa-3d3b-4492-924c-aff54dea19b6" + n, err := trunks.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Status, "ACTIVE") + expectedTrunks, err := ExpectedTrunkSlice() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, &expectedTrunks[1], n) +} + +func TestCreateNoSubports(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks", 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, CreateNoSubportsRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, CreateNoSubportsResponse) + }) + + iTrue := true + options := trunks.CreateOpts{ + Name: "gophertrunk", + Description: "Trunk created by gophercloud", + AdminStateUp: &iTrue, + PortID: "c373d2fa-3d3b-4492-924c-aff54dea19b6", + } + n, err := trunks.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Status, "ACTIVE") + th.AssertEquals(t, 0, len(n.Subports)) +} diff --git a/openstack/networking/v2/extensions/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go new file mode 100644 index 0000000000..446dd374f0 --- /dev/null +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -0,0 +1,13 @@ +package trunks + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "trunks" + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From 91ddad0a0675d864cb88b6f9d853c75175657465 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Fri, 5 Oct 2018 19:29:05 -0700 Subject: [PATCH 0550/2296] Added missing Octavia fields to Member (#1255) * Added following fields to Member: ProjectID, CreatedAt, UpdatedAt, OperatingStatus, Backup, MonitorAddress, MonitorPort * Moved missing octavia pool-member fields to package loadbalancer from networking extensions --- openstack/loadbalancer/v2/pools/results.go | 38 +++++++++++++++++ .../loadbalancer/v2/pools/testing/fixtures.go | 42 ++++++++++++++----- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index 52935b4f1d..f4674cd0d4 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -1,6 +1,9 @@ package pools import ( + "encoding/json" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/pagination" @@ -204,6 +207,24 @@ type Member struct { // The provisioning status of the pool. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` + + // DateTime when the member was created + CreatedAt time.Time `json:"-"` + + // DateTime when the member was updated + UpdatedAt time.Time `json:"-"` + + // The operating status of the member + OperatingStatus string `json:"operating_status"` + + // Is the member a backup? Backup members only receive traffic when all non-backup members are down. + Backup bool `json:"backup"` + + // An alternate IP address used for health monitoring a backend member. + MonitorAddress string `json:"monitor_address"` + + // An alternate protocol port used for health monitoring a backend member. + MonitorPort int `json:"monitor_port"` } // MemberPage is the page returned by a pager when traversing over a @@ -247,6 +268,23 @@ type commonMemberResult struct { gophercloud.Result } +func (r *Member) UnmarshalJSON(b []byte) error { + type tmp Member + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Member(s.tmp) + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + return nil +} + // ExtractMember is a function that accepts a result and extracts a member. func (r commonMemberResult) Extract() (*Member, error) { var s struct { diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures.go b/openstack/loadbalancer/v2/pools/testing/fixtures.go index 8104708282..4c1b8f18c1 100644 --- a/openstack/loadbalancer/v2/pools/testing/fixtures.go +++ b/openstack/loadbalancer/v2/pools/testing/fixtures.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" th "github.com/gophercloud/gophercloud/testhelper" @@ -234,7 +235,14 @@ const MembersListBody = ` "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, - "protocol_port": 80 + "protocol_port": 80, + "provisioning_status": "ACTIVE", + "created_at": "2018-08-23T20:05:21", + "updated_at": "2018-08-23T21:22:53", + "operating_status": "ONLINE", + "backup": false, + "monitor_address": "192.168.1.111", + "monitor_port": 80 } ] } @@ -251,7 +259,14 @@ const SingleMemberBody = ` "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", "project_id": "2ffc6e22aae24e4795f87155d24c896f", "admin_state_up":false, - "protocol_port": 80 + "protocol_port": 80, + "provisioning_status": "ACTIVE", + "created_at": "2018-08-23T20:05:21", + "updated_at": "2018-08-23T21:22:53", + "operating_status": "ONLINE", + "backup": false, + "monitor_address": "192.168.1.111", + "monitor_port": 80 } } ` @@ -284,14 +299,21 @@ var ( ProtocolPort: 80, } MemberDb = pools.Member{ - SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - ProjectID: "2ffc6e22aae24e4795f87155d24c896f", - AdminStateUp: false, - Name: "db", - ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", - Address: "10.0.2.11", - Weight: 10, - ProtocolPort: 80, + SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", + ProjectID: "2ffc6e22aae24e4795f87155d24c896f", + AdminStateUp: false, + Name: "db", + ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + Address: "10.0.2.11", + Weight: 10, + ProtocolPort: 80, + ProvisioningStatus: "ACTIVE", + CreatedAt: time.Date(2018, 8, 23, 20, 05, 21, 0, time.UTC), + UpdatedAt: time.Date(2018, 8, 23, 21, 22, 53, 0, time.UTC), + OperatingStatus: "ONLINE", + Backup: false, + MonitorAddress: "192.168.1.111", + MonitorPort: 80, } MemberUpdated = pools.Member{ SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", From 4a7ec64645ff4cbf261ff32739fee55685c12573 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 5 Oct 2018 23:11:36 -0600 Subject: [PATCH 0551/2296] Acc Tests: Enabling network extensions (#1263) --- .../networking/v2/extensions/provider_test.go | 13 ++++--------- script/acceptancetest | 6 ++++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/provider_test.go b/acceptance/openstack/networking/v2/extensions/provider_test.go index 44726ce27e..45893fbd33 100644 --- a/acceptance/openstack/networking/v2/extensions/provider_test.go +++ b/acceptance/openstack/networking/v2/extensions/provider_test.go @@ -9,26 +9,21 @@ import ( networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestNetworksProviderCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a network client: %v", err) - } + th.AssertNoErr(t, err) // Create a network network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) getResult := networks.Get(client, network.ID) newNetwork, err := getResult.Extract() - if err != nil { - t.Fatalf("Unable to extract network: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) } diff --git a/script/acceptancetest b/script/acceptancetest index 5a1858f4cb..ae30151b28 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -22,6 +22,12 @@ if [[ $? != 0 ]]; then failed=1 fi +# Networking v2 - extensions +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions +if [[ $? != 0 ]]; then + failed=1 +fi + # Networking v2 - layer3 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/layer3 if [[ $? != 0 ]]; then From 603c223db2b7124beb6b77edb36e7477a6aead25 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 6 Oct 2018 09:32:41 -0600 Subject: [PATCH 0552/2296] Networking v2: Make Security Group Rule Description omitempty (#1265) This commit modifies the description field of security group rule CreateOpts to be omitted if not specified. This is because older release of OpenStack do not support this field. --- 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 2a0a3bd45c..fad787e304 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -89,7 +89,7 @@ type CreateOpts struct { Direction RuleDirection `json:"direction" required:"true"` // String description of each rule, optional - Description string `json:"description" required:"false"` + Description string `json:"description,omitempty"` // Must be "IPv4" or "IPv6", and addresses represented in CIDR must match the // ingress or egress rules. From 9f17b1a551367fceba2fb23792e93d48cd597924 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 6 Oct 2018 15:42:11 +0000 Subject: [PATCH 0553/2296] Networking v2: Remove ProtocolUDP from LBaaS v2 --- .../networking/v2/extensions/lbaas_v2/listeners/requests.go | 1 - openstack/networking/v2/extensions/lbaas_v2/pools/requests.go | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index 8f47f5d4f1..dd190f606f 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -11,7 +11,6 @@ type Protocol string // Supported attributes for create/update operations. const ( ProtocolTCP Protocol = "TCP" - ProtocolUDP Protocol = "UDP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index 3454d71ca5..11564be83f 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -68,7 +68,6 @@ const ( LBMethodSourceIp LBMethod = "SOURCE_IP" ProtocolTCP Protocol = "TCP" - ProtocolUDP Protocol = "UDP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) @@ -88,7 +87,7 @@ type CreateOpts struct { LBMethod LBMethod `json:"lb_algorithm" required:"true"` // The protocol used by the pool members, you can use either - // ProtocolTCP, ProtocolUDP, ProtocolHTTP, or ProtocolHTTPS. + // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. Protocol Protocol `json:"protocol" required:"true"` // The Loadbalancer on which the members of the pool will be associated with. From 4a3377a4cb8a1ff2fc0404d932e0ae70cb283f00 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 6 Oct 2018 15:42:33 +0000 Subject: [PATCH 0554/2296] Octavia v2: Add ProtocolUDP support --- openstack/loadbalancer/v2/listeners/requests.go | 1 + openstack/loadbalancer/v2/pools/requests.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index ac587e258d..061d3cc2ed 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -11,6 +11,7 @@ type Protocol string // Supported attributes for create/update operations. const ( ProtocolTCP Protocol = "TCP" + ProtocolUDP Protocol = "UDP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 6c8dfd3994..a95000f384 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -66,6 +66,7 @@ const ( LBMethodSourceIp LBMethod = "SOURCE_IP" ProtocolTCP Protocol = "TCP" + ProtocolUDP Protocol = "UDP" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) @@ -85,7 +86,7 @@ type CreateOpts struct { LBMethod LBMethod `json:"lb_algorithm" required:"true"` // The protocol used by the pool members, you can use either - // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. + // ProtocolTCP, ProtocolUDP, ProtocolHTTP, or ProtocolHTTPS. Protocol Protocol `json:"protocol" required:"true"` // The Loadbalancer on which the members of the pool will be associated with. From 0ca2a92d95bfa78efcc87ef024cf6a8d28969bba Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Thu, 4 Oct 2018 14:53:43 +0200 Subject: [PATCH 0555/2296] Networking v2 Trunk support - Delete This patch adds the Networking extension trunk support for the Delete operation. Signed-off-by: Antoni Segura Puimedon --- .../networking/v2/extensions/trunks/trunks.go | 10 ++++++++++ .../networking/v2/extensions/trunks/trunks_test.go | 6 ++++++ openstack/networking/v2/extensions/trunks/doc.go | 8 ++++++++ .../networking/v2/extensions/trunks/requests.go | 6 ++++++ .../networking/v2/extensions/trunks/results.go | 6 ++++++ .../v2/extensions/trunks/testing/requests_test.go | 14 ++++++++++++++ openstack/networking/v2/extensions/trunks/urls.go | 8 ++++++++ 7 files changed, 58 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks.go index ca0233c133..18fc920fd6 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks.go @@ -34,3 +34,13 @@ func CreateTrunk(t *testing.T, client *gophercloud.ServiceClient, parentPortID s } return } + +func DeleteTrunk(t *testing.T, client *gophercloud.ServiceClient, trunkID string) { + t.Logf("Attempting to delete trunk: %s", trunkID) + err := trunks.Delete(client, trunkID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete trunk %s: %v", trunkID, err) + } + + t.Logf("Deleted trunk: %s", trunkID) +} diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index fd07d3ae87..f898ef2509 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -21,33 +21,39 @@ func TestTrunkCRUD(t *testing.T) { 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 := CreateTrunk(t, client, parentPort.ID, subport1.ID, subport2.ID) if err != nil { t.Fatalf("Unable to create trunk: %v", err) } + defer DeleteTrunk(t, client, trunk.ID) tools.PrintResource(t, trunk) } diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 7bd093ecde..6d563dedbf 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -47,5 +47,13 @@ Example of a new Trunk creation with 2 subports panic(err) } fmt.Printf("%+v\n", trunk) + +Example of deleting a Trunk + + trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" + err := trunks.Delete(networkClient, trunkID).ExtractErr() + if err != nil { + panic(err) + } */ package trunks diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index 1c024373e7..f01b946e37 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -39,3 +39,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(createURL(c), body, &r.Body, nil) return } + +// Delete accepts a unique ID and deletes the trunk associated with it. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(deleteURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index b38f6243fb..ef4393a9ff 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -22,6 +22,12 @@ type CreateResult 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 +} + type Trunk struct { // Indicates whether the trunk is currently operational. Possible values include // `ACTIVE', `DOWN', `BUILD', 'DEGRADED' or `ERROR'. diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index 3f4ff0cceb..55c266a25c 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -87,3 +87,17 @@ func TestCreateNoSubports(t *testing.T) { th.AssertEquals(t, n.Status, "ACTIVE") th.AssertEquals(t, 0, len(n.Subports)) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c", 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) + }) + + res := trunks.Delete(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go index 446dd374f0..36622cddd2 100644 --- a/openstack/networking/v2/extensions/trunks/urls.go +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -8,6 +8,14 @@ func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} + func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From 04bbc2d26b10fcf3c69206bc1412a8bd5ab7b34b Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Thu, 4 Oct 2018 16:39:39 +0000 Subject: [PATCH 0556/2296] Neutron Standard Attributes Tag ReplaceAll support This initial patch adds only the ability to Replace all tags on a given resource, as described in the API docs: https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag-extension https://developer.openstack.org/api-ref/network/v2/#replace-all-tags Subsequent PRs can add the other operations supported by this API. Issue: #1260 --- .../v2/extensions/attributestags_test.go | 32 ++++++++++++++++ .../v2/extensions/attributestags/doc.go | 19 ++++++++++ .../v2/extensions/attributestags/requests.go | 36 ++++++++++++++++++ .../v2/extensions/attributestags/results.go | 24 ++++++++++++ .../attributestags/testing/fixtures.go | 13 +++++++ .../attributestags/testing/requests_test.go | 37 +++++++++++++++++++ .../v2/extensions/attributestags/urls.go | 11 ++++++ 7 files changed, 172 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/attributestags_test.go create mode 100644 openstack/networking/v2/extensions/attributestags/doc.go create mode 100644 openstack/networking/v2/extensions/attributestags/requests.go create mode 100644 openstack/networking/v2/extensions/attributestags/results.go create mode 100644 openstack/networking/v2/extensions/attributestags/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/attributestags/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/attributestags/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go new file mode 100644 index 0000000000..8cf787aff3 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking tags + +package extensions + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestTags(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create Network + network, err := networking.CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + tagReplaceAllOpts := attributestags.ReplaceAllOpts{ + Tags: []string{"abc", "123"}, + } + tags, err := attributestags.ReplaceAll(client, "networks", network.ID, tagReplaceAllOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, tags, []string{"abc", "123"}) + + // FIXME(shardy) - when the networks Get schema supports tags we should + // verify the tags are set in the Get response +} diff --git a/openstack/networking/v2/extensions/attributestags/doc.go b/openstack/networking/v2/extensions/attributestags/doc.go new file mode 100644 index 0000000000..178b257d5c --- /dev/null +++ b/openstack/networking/v2/extensions/attributestags/doc.go @@ -0,0 +1,19 @@ +/* +Package attributestags manages Tags on Resources created by the OpenStack Neutron Service. + +This enables tagging via a standard interface for resources types which support it. + +See https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag-extension for more information on the underlying API. + +Example to ReplaceAll Resource Tags + + network, err := networks.Create(conn, createOpts).Extract() + + tagReplaceAllOpts := attributestags.ReplaceAllOpts{ + Tags: []string{"abc", "123"}, + } + attributestags.ReplaceAll(conn, "networks", network.ID, tagReplaceAllOpts) + + +*/ +package attributestags diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go new file mode 100644 index 0000000000..9489fe3834 --- /dev/null +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -0,0 +1,36 @@ +package attributestags + +import ( + "github.com/gophercloud/gophercloud" +) + +// ReplaceAllOptsBuilder allows extensions to add additional parameters to +// the ReplaceAll request. +type ReplaceAllOptsBuilder interface { + ToAttributeTagsReplaceAllMap() (map[string]interface{}, error) +} + +// ReplaceAllOpts provides options used to create Tags on a Resource +type ReplaceAllOpts struct { + Tags []string `json:"tags" required:"true"` +} + +// ToAttributeTagsReplaceAllMap formats a ReplaceAllOpts into the body of the +// replace request +func (opts ReplaceAllOpts) ToAttributeTagsReplaceAllMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// ReplaceAll updates all tags on a resource, replacing any existing tags +func ReplaceAll(client *gophercloud.ServiceClient, resourceType string, resourceID string, opts ReplaceAllOptsBuilder) (r ReplaceAllResult) { + b, err := opts.ToAttributeTagsReplaceAllMap() + url := replaceURL(client, resourceType, resourceID) + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/attributestags/results.go b/openstack/networking/v2/extensions/attributestags/results.go new file mode 100644 index 0000000000..5057c845db --- /dev/null +++ b/openstack/networking/v2/extensions/attributestags/results.go @@ -0,0 +1,24 @@ +package attributestags + +import ( + "github.com/gophercloud/gophercloud" +) + +type tagResult struct { + gophercloud.Result +} + +// Extract interprets tagResult to return the list of tags +func (r tagResult) Extract() ([]string, error) { + var s struct { + Tags []string `json:"tags"` + } + err := r.ExtractInto(&s) + return s.Tags, err +} + +// ReplaceAllResult represents the result of a replace operation. +// Call its Extract method to interpret it as a slice of strings. +type ReplaceAllResult struct { + tagResult +} diff --git a/openstack/networking/v2/extensions/attributestags/testing/fixtures.go b/openstack/networking/v2/extensions/attributestags/testing/fixtures.go new file mode 100644 index 0000000000..ef706457bd --- /dev/null +++ b/openstack/networking/v2/extensions/attributestags/testing/fixtures.go @@ -0,0 +1,13 @@ +package testing + +const attributestagsReplaceAllRequest = ` +{ + "tags": ["abc", "xyz"] +} +` + +const attributestagsReplaceAllResult = ` +{ + "tags": ["abc", "xyz"] +} +` diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go new file mode 100644 index 0000000000..ca82fe17a1 --- /dev/null +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -0,0 +1,37 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestReplaceAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/fakeid/tags", 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, attributestagsReplaceAllRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, attributestagsReplaceAllResult) + }) + + opts := attributestags.ReplaceAllOpts{ + Tags: []string{"abc", "xyz"}, + } + res, err := attributestags.ReplaceAll(fake.ServiceClient(), "networks", "fakeid", opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, res, []string{"abc", "xyz"}) +} diff --git a/openstack/networking/v2/extensions/attributestags/urls.go b/openstack/networking/v2/extensions/attributestags/urls.go new file mode 100644 index 0000000000..e4f30e902d --- /dev/null +++ b/openstack/networking/v2/extensions/attributestags/urls.go @@ -0,0 +1,11 @@ +package attributestags + +import "github.com/gophercloud/gophercloud" + +const ( + tagsPath = "tags" +) + +func replaceURL(c *gophercloud.ServiceClient, r_type string, id string) string { + return c.ServiceURL(r_type, id, tagsPath) +} From 22f0e30652f5f9af14969859b12e1415963a322f Mon Sep 17 00:00:00 2001 From: Xian Chaobo Date: Tue, 9 Oct 2018 00:42:08 +0800 Subject: [PATCH 0557/2296] Add support volume scheduler hint option (#1241) Signed-off-by: XianChaobo --- .../extensions/schedulerhints_test.go | 59 +++++++++ .../extensions/schedulerhints/doc.go | 52 ++++++++ .../extensions/schedulerhints/requests.go | 124 ++++++++++++++++++ .../extensions/schedulerhints/testing/doc.go | 2 + .../schedulerhints/testing/requests_test.go | 59 +++++++++ 5 files changed, 296 insertions(+) create mode 100644 acceptance/openstack/blockstorage/extensions/schedulerhints_test.go create mode 100644 openstack/blockstorage/extensions/schedulerhints/doc.go create mode 100644 openstack/blockstorage/extensions/schedulerhints/requests.go create mode 100644 openstack/blockstorage/extensions/schedulerhints/testing/doc.go create mode 100644 openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go diff --git a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go new file mode 100644 index 0000000000..19c1b8a80d --- /dev/null +++ b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -0,0 +1,59 @@ +// +build acceptance blockstorage + +package extensions + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerhints" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestSchedulerHints(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volumeName := tools.RandomString("ACPTTEST", 16) + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + + volume1, err := volumes.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, volume1.ID, "available", 60) + th.AssertNoErr(t, err) + defer volumes.Delete(client, volume1.ID) + + volumeName = tools.RandomString("ACPTTEST", 16) + base := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + + schedulerHints := schedulerhints.SchedulerHints{ + SameHost: []string{ + volume1.ID, + }, + } + + createOptsWithHints := schedulerhints.CreateOptsExt{ + VolumeCreateOptsBuilder: base, + SchedulerHints: schedulerHints, + } + + volume2, err := volumes.Create(client, createOptsWithHints).Extract() + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, volume2.ID, "available", 60) + th.AssertNoErr(t, err) + + err = volumes.Delete(client, volume2.ID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/extensions/schedulerhints/doc.go b/openstack/blockstorage/extensions/schedulerhints/doc.go new file mode 100644 index 0000000000..3a0d451b65 --- /dev/null +++ b/openstack/blockstorage/extensions/schedulerhints/doc.go @@ -0,0 +1,52 @@ +/* +Package schedulerhints extends the volume create request with the ability to +specify additional parameters which determine where the volume will be +created in the OpenStack cloud. + +Example to Place Volume B on a Different Host than Volume A + + schedulerHints := schedulerhints.SchedulerHints{ + DifferentHost: []string{ + "volume-a-uuid", + } + } + + volumeCreateOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10, + } + + createOpts := schedulerhints.CreateOptsExt{ + VolumeCreateOptsBuilder: volumeCreateOpts, + SchedulerHints: schedulerHints, + } + + volume, err := volumes.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Place Volume B on the Same Host as Volume A + + schedulerHints := schedulerhints.SchedulerHints{ + SameHost: []string{ + "volume-a-uuid", + } + } + + volumeCreateOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10 + } + + createOpts := schedulerhints.CreateOptsExt{ + VolumeCreateOptsBuilder: volumeCreateOpts, + SchedulerHints: schedulerHints, + } + + volume, err := volumes.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } +*/ +package schedulerhints diff --git a/openstack/blockstorage/extensions/schedulerhints/requests.go b/openstack/blockstorage/extensions/schedulerhints/requests.go new file mode 100644 index 0000000000..05b722da41 --- /dev/null +++ b/openstack/blockstorage/extensions/schedulerhints/requests.go @@ -0,0 +1,124 @@ +package schedulerhints + +import ( + "regexp" + + "github.com/gophercloud/gophercloud" +) + +// SchedulerHints represents a set of scheduling hints that are passed to the +// OpenStack scheduler. +type SchedulerHints struct { + // DifferentHost will place the volume on a different back-end that does not + // host the given volumes. + DifferentHost []string + + // SameHost will place the volume on a back-end that hosts the given volumes. + SameHost []string + + // LocalToInstance will place volume on same host on a given instance + LocalToInstance string + + // Query is a conditional statement that results in back-ends able to + // host the volume. + Query string + + // AdditionalProperies are arbitrary key/values that are not validated by nova. + AdditionalProperties map[string]interface{} +} + +// VolumeCreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type VolumeCreateOptsBuilder interface { + ToVolumeCreateMap() (map[string]interface{}, error) +} + +// CreateOptsBuilder builds the scheduler hints into a serializable format. +type CreateOptsBuilder interface { + ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) +} + +// ToVolumeSchedulerHintsMap builds the scheduler hints into a serializable format. +func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) { + sh := make(map[string]interface{}) + + uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") + + if len(opts.DifferentHost) > 0 { + for _, diffHost := range opts.DifferentHost { + if !uuidRegex.MatchString(diffHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.DifferentHost" + err.Value = opts.DifferentHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["different_host"] = opts.DifferentHost + } + + if len(opts.SameHost) > 0 { + for _, sameHost := range opts.SameHost { + if !uuidRegex.MatchString(sameHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.SameHost" + err.Value = opts.SameHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["same_host"] = opts.SameHost + } + + if opts.LocalToInstance != "" { + if !uuidRegex.MatchString(opts.LocalToInstance) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" + err.Value = opts.LocalToInstance + err.Info = "The instance must be in UUID format." + return nil, err + } + sh["local_to_instance"] = opts.LocalToInstance + } + + if opts.Query != "" { + sh["query"] = opts.Query + } + + if opts.AdditionalProperties != nil { + for k, v := range opts.AdditionalProperties { + sh[k] = v + } + } + + return sh, nil +} + +// CreateOptsExt adds a SchedulerHints option to the base CreateOpts. +type CreateOptsExt struct { + VolumeCreateOptsBuilder + + // SchedulerHints provides a set of hints to the scheduler. + SchedulerHints CreateOptsBuilder +} + +// ToVolumeCreateMap adds the SchedulerHints option to the base volume creation options. +func (opts CreateOptsExt) ToVolumeCreateMap() (map[string]interface{}, error) { + base, err := opts.VolumeCreateOptsBuilder.ToVolumeCreateMap() + if err != nil { + return nil, err + } + + schedulerHints, err := opts.SchedulerHints.ToVolumeSchedulerHintsCreateMap() + if err != nil { + return nil, err + } + + if len(schedulerHints) == 0 { + return base, nil + } + + base["OS-SCH-HNT:scheduler_hints"] = schedulerHints + + return base, nil +} diff --git a/openstack/blockstorage/extensions/schedulerhints/testing/doc.go b/openstack/blockstorage/extensions/schedulerhints/testing/doc.go new file mode 100644 index 0000000000..1915aef2fe --- /dev/null +++ b/openstack/blockstorage/extensions/schedulerhints/testing/doc.go @@ -0,0 +1,2 @@ +// schedulerhints unit tests +package testing diff --git a/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go b/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go new file mode 100644 index 0000000000..f477f0c38f --- /dev/null +++ b/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go @@ -0,0 +1,59 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerhints" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreateOpts(t *testing.T) { + + base := volumes.CreateOpts{ + Size: 10, + Name: "testvolume", + } + schedulerHints := schedulerhints.SchedulerHints{ + DifferentHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + SameHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", + AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + } + + ext := schedulerhints.CreateOptsExt{ + VolumeCreateOptsBuilder: base, + SchedulerHints: schedulerHints, + } + + expected := ` + { + "volume": { + "size": 10, + "name": "testvolume" + }, + "OS-SCH-HNT:scheduler_hints": { + "different_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "same_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "local_to_instance": "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", + "mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" + } + } + ` + actual, err := ext.ToVolumeCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} From ea435569891f5712d91134b54ec698490bb8107d Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Mon, 8 Oct 2018 14:42:32 +0000 Subject: [PATCH 0558/2296] Neutron Standard Attributes Tag List support This patch adds the ability to List all tags on a given resource. As described in the API docs: https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag-extension https://developer.openstack.org/api-ref/network/v2/#obtain-tag-list Issue: #1260 --- .../v2/extensions/attributestags_test.go | 12 +++++++---- .../v2/extensions/attributestags/doc.go | 2 ++ .../v2/extensions/attributestags/requests.go | 9 +++++++++ .../v2/extensions/attributestags/results.go | 4 ++++ .../attributestags/testing/fixtures.go | 6 ++++++ .../attributestags/testing/requests_test.go | 20 +++++++++++++++++++ .../v2/extensions/attributestags/urls.go | 4 ++++ 7 files changed, 53 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 8cf787aff3..cb48163e4e 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -21,12 +21,16 @@ func TestTags(t *testing.T) { defer networking.DeleteNetwork(t, client, network.ID) tagReplaceAllOpts := attributestags.ReplaceAllOpts{ - Tags: []string{"abc", "123"}, + // Note Neutron returns tags sorted, and although the API + // docs say list of tags, it's a set e.g no duplicates + Tags: []string{"a", "b", "c"}, } tags, err := attributestags.ReplaceAll(client, "networks", network.ID, tagReplaceAllOpts).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, tags, []string{"abc", "123"}) + th.AssertDeepEquals(t, tags, []string{"a", "b", "c"}) - // FIXME(shardy) - when the networks Get schema supports tags we should - // verify the tags are set in the Get response + // Verify the tags are set in the List response + tags, err = attributestags.List(client, "networks", network.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, tags, []string{"a", "b", "c"}) } diff --git a/openstack/networking/v2/extensions/attributestags/doc.go b/openstack/networking/v2/extensions/attributestags/doc.go index 178b257d5c..9026faaca8 100644 --- a/openstack/networking/v2/extensions/attributestags/doc.go +++ b/openstack/networking/v2/extensions/attributestags/doc.go @@ -14,6 +14,8 @@ Example to ReplaceAll Resource Tags } attributestags.ReplaceAll(conn, "networks", network.ID, tagReplaceAllOpts) +Example to List all Resource Tags + tags, err = attributestags.List(conn, "networks", network.ID).Extract() */ package attributestags diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index 9489fe3834..997f72e726 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -34,3 +34,12 @@ func ReplaceAll(client *gophercloud.ServiceClient, resourceType string, resource }) return } + +// List all tags on a resource +func List(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r ListResult) { + url := listURL(client, resourceType, resourceID) + _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/attributestags/results.go b/openstack/networking/v2/extensions/attributestags/results.go index 5057c845db..af2650ae18 100644 --- a/openstack/networking/v2/extensions/attributestags/results.go +++ b/openstack/networking/v2/extensions/attributestags/results.go @@ -22,3 +22,7 @@ func (r tagResult) Extract() ([]string, error) { type ReplaceAllResult struct { tagResult } + +type ListResult struct { + tagResult +} diff --git a/openstack/networking/v2/extensions/attributestags/testing/fixtures.go b/openstack/networking/v2/extensions/attributestags/testing/fixtures.go index ef706457bd..b17ad46e81 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/fixtures.go +++ b/openstack/networking/v2/extensions/attributestags/testing/fixtures.go @@ -11,3 +11,9 @@ const attributestagsReplaceAllResult = ` "tags": ["abc", "xyz"] } ` + +const attributestagsListResult = ` +{ + "tags": ["abc", "xyz"] +} +` diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go index ca82fe17a1..3c3d538657 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -35,3 +35,23 @@ func TestReplaceAll(t *testing.T) { th.AssertDeepEquals(t, res, []string{"abc", "xyz"}) } + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/fakeid/tags", 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, attributestagsListResult) + }) + + res, err := attributestags.List(fake.ServiceClient(), "networks", "fakeid").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, res, []string{"abc", "xyz"}) +} diff --git a/openstack/networking/v2/extensions/attributestags/urls.go b/openstack/networking/v2/extensions/attributestags/urls.go index e4f30e902d..f713db90cb 100644 --- a/openstack/networking/v2/extensions/attributestags/urls.go +++ b/openstack/networking/v2/extensions/attributestags/urls.go @@ -9,3 +9,7 @@ const ( func replaceURL(c *gophercloud.ServiceClient, r_type string, id string) string { return c.ServiceURL(r_type, id, tagsPath) } + +func listURL(c *gophercloud.ServiceClient, r_type string, id string) string { + return c.ServiceURL(r_type, id, tagsPath) +} From 26adfeee9a3fc2f2afcc7d6a5c25239493c1389f Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Thu, 27 Sep 2018 14:30:29 +0200 Subject: [PATCH 0559/2296] Workflow: improve doc + tests --- .../workflow/v2/crontriggers_test.go | 13 +- openstack/workflow/v2/crontriggers/doc.go | 23 ++- .../workflow/v2/crontriggers/requests.go | 135 +++++++++++++++++- openstack/workflow/v2/crontriggers/results.go | 12 +- .../v2/crontriggers/testing/requests_test.go | 32 +++++ openstack/workflow/v2/workflows/doc.go | 59 ++++---- .../v2/workflows/testing/requests_test.go | 108 ++++++++------ 7 files changed, 290 insertions(+), 92 deletions(-) diff --git a/acceptance/openstack/workflow/v2/crontriggers_test.go b/acceptance/openstack/workflow/v2/crontriggers_test.go index 95bd356e3f..48642cd5f2 100644 --- a/acceptance/openstack/workflow/v2/crontriggers_test.go +++ b/acceptance/openstack/workflow/v2/crontriggers_test.go @@ -2,6 +2,7 @@ package v2 import ( "testing" + "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" @@ -39,7 +40,17 @@ func TestCronTriggersList(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCronTrigger(t, client, trigger) list, err := ListCronTriggers(t, client, &crontriggers.ListOpts{ - Name: trigger.Name, + Name: &crontriggers.ListFilter{ + Filter: crontriggers.FilterEQ, + Value: trigger.Name, + }, + Pattern: &crontriggers.ListFilter{ + Value: "0 0 1 1 *", + }, + CreatedAt: &crontriggers.ListDateFilter{ + Filter: crontriggers.FilterGT, + Value: time.Now().AddDate(-1, 0, 0), + }, }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(list)) diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go index d92281c16b..f000450cf5 100644 --- a/openstack/workflow/v2/crontriggers/doc.go +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -6,17 +6,30 @@ Once a trigger is created it will run a specified workflow according to its prop 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. + listOpts := crontriggers.ListOpts{ - WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", + WorkflowName: &executions.ListFilter{ + Value: "Workflow1,Workflow2", + Filter: executions.FilterIN, + }, + CreatedAt: &executions.ListDateFilter{ + Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), + Filter: executions.FilterGTE, + }, } + allPages, err := crontriggers.List(mistralClient, listOpts).AllPages() if err != nil { panic(err) } + allCrontriggers, err := crontriggers.ExtractCronTriggers(allPages) if err != nil { panic(err) } + for _, ct := range allCrontriggers { fmt.Printf("%+v\n", ct) } @@ -24,10 +37,10 @@ List cron triggers Create a cron trigger. This example will start the workflow "echo" each day at 8am, and it will end after 10 executions. createOpts := &crontriggers.CreateOpts{ - Name: "daily", - Pattern: "0 8 * * *", - WorkflowName: "echo", - RemainingExecutions: 10, + Name: "daily", + Pattern: "0 8 * * *", + WorkflowName: "echo", + RemainingExecutions: 10, WorkflowParams: map[string]interface{}{ "msg": "hello", }, diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index c911fe56c3..63a800d9bd 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -1,6 +1,10 @@ package crontriggers import ( + "encoding/json" + "fmt" + "net/url" + "reflect" "time" "github.com/gophercloud/gophercloud" @@ -91,16 +95,36 @@ type ListOpts struct { WorkflowName string `q:"workflow_name"` // WorkflowID allows to filter by workflow id. WorkflowID string `q:"workflow_id"` - // Name allows to filter by trigger name. - Name string `q:"name"` + // WorkflowInput allows to filter by specific workflow inputs. + WorkflowInput map[string]interface{} `q:"-"` + // WorkflowParams allows to filter by specific workflow parameters. + WorkflowParams map[string]interface{} `q:"-"` // Scope filters by the trigger's scope. // Values can be "private" or "public". Scope string `q:"scope"` - // SortDir allows to select sort direction. + // Name allows to filter by trigger name. + Name *ListFilter `q:"-"` + // Pattern allows to filter by pattern. + Pattern *ListFilter `q:"-"` + // RemainingExecutions allows to filter by remaining executions. + RemainingExecutions *ListIntFilter `q:"-"` + // FirstExecutionTime allows to filter by first execution time. + FirstExecutionTime *ListDateFilter `q:"-"` + // NextExecutionTime allows to filter by next execution time. + NextExecutionTime *ListDateFilter `q:"-"` + // CreatedAt allows to filter by trigger creation date. + CreatedAt *ListDateFilter `q:"-"` + // UpdatedAt allows to filter by trigger last update date. + UpdatedAt *ListDateFilter `q:"-"` + // ProjectID allows to filter by given project id. Admin required. + ProjectID string `q:"project_id"` + // AllProjects requests to get executions of all projects. Admin required. + AllProjects int `q:"all_projects"` + // SortDirs allows to select sort direction. // It can be "asc" or "desc" (default). - SortDir string `q:"sort_dir"` - // SortKey allows to sort by one of the cron trigger attributes. - SortKey string `q:"sort_key"` + SortDirs string `q:"sort_dirs"` + // SortKeys allows to sort by one of the cron trigger attributes. + SortKeys string `q:"sort_key"` // Marker and Limit control paging. // Marker instructs List where to start listing from. Marker string `q:"marker"` @@ -109,10 +133,107 @@ type ListOpts struct { Limit int `q:"limit"` } +// ListFilter allows to filter string parameters with different filters. +// Empty value for Filter checks for equality. +type ListFilter struct { + Filter FilterType + Value string +} + +func (l ListFilter) String() string { + if l.Filter != "" { + return fmt.Sprintf("%s:%s", l.Filter, l.Value) + } + return l.Value +} + +// ListDateFilter allows to filter date parameters with different filters. +// Empty value for Filter checks for equality. +type ListDateFilter struct { + Filter FilterType + Value time.Time +} + +func (l ListDateFilter) String() string { + v := l.Value.Format(gophercloud.RFC3339ZNoTNoZ) + if l.Filter != "" { + return fmt.Sprintf("%s:%s", l.Filter, v) + } + return v +} + +// ListIntFilter allows to filter integer parameters with different filters. +// Empty value for Filter checks for equality. +type ListIntFilter struct { + Filter FilterType + Value int +} + +func (l ListIntFilter) String() string { + v := fmt.Sprintf("%d", l.Value) + if l.Filter != "" { + return fmt.Sprintf("%s:%s", l.Filter, v) + } + return v +} + +// FilterType represents a valid filter to use for filtering executions. +type FilterType string + +const ( + // FilterEQ checks equality. + FilterEQ = "eq" + // FilterNEQ checks non equality. + FilterNEQ = "neq" + // FilterIN checks for belonging in a list, comma separated. + FilterIN = "in" + // FilterNIN checks for values that does not belong from a list, comma separated. + FilterNIN = "nin" + // FilterGT checks for values strictly greater. + FilterGT = "gt" + // FilterGTE checks for values greater or equal. + FilterGTE = "gte" + // FilterLT checks for values strictly lower. + FilterLT = "lt" + // FilterLTE checks for values lower or equal. + FilterLTE = "lte" + // FilterHas checks for values that contains the requested parameter. + FilterHas = "has" +) + // ToCronTriggerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToCronTriggerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) - return q.String(), err + if err != nil { + return "", err + } + params := q.Query() + + for queryParam, value := range map[string]map[string]interface{}{"workflow_params": opts.WorkflowParams, "workflow_input": opts.WorkflowInput} { + if value != nil { + b, err := json.Marshal(value) + if err != nil { + return "", err + } + params.Add(queryParam, string(b)) + } + } + + for queryParam, value := range map[string]fmt.Stringer{ + "name": opts.Name, + "pattern": opts.Pattern, + "remaining_executions": opts.RemainingExecutions, + "first_execution_time": opts.FirstExecutionTime, + "next_execution_time": opts.NextExecutionTime, + "created_at": opts.CreatedAt, + "updated_at": opts.UpdatedAt, + } { + if !reflect.ValueOf(value).IsNil() { + params.Add(queryParam, value.String()) + } + } + q = &url.URL{RawQuery: params.Encode()} + return q.String(), nil } // List performs a call to list cron triggers. diff --git a/openstack/workflow/v2/crontriggers/results.go b/openstack/workflow/v2/crontriggers/results.go index 02568fba5e..5a1b4f2485 100644 --- a/openstack/workflow/v2/crontriggers/results.go +++ b/openstack/workflow/v2/crontriggers/results.go @@ -108,12 +108,16 @@ func (r *CronTrigger) UnmarshalJSON(b []byte) error { r.NextExecutionTime = &t } - if err := json.Unmarshal([]byte(s.WorkflowInput), &r.WorkflowInput); err != nil { - return err + if s.WorkflowInput != "" { + if err := json.Unmarshal([]byte(s.WorkflowInput), &r.WorkflowInput); err != nil { + return err + } } - if err := json.Unmarshal([]byte(s.WorkflowParams), &r.WorkflowParams); err != nil { - return err + if s.WorkflowParams != "" { + if err := json.Unmarshal([]byte(s.WorkflowParams), &r.WorkflowParams); err != nil { + return err + } } return nil diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index dfe4011a9d..77e27745cf 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -3,6 +3,7 @@ package testing import ( "fmt" "net/http" + "net/url" "reflect" "testing" "time" @@ -241,3 +242,34 @@ func TestListCronTriggers(t *testing.T) { t.Errorf("Expected one page, got %d", pages) } } + +func TestToExecutionListQuery(t *testing.T) { + for expected, opts := range map[string]*crontriggers.ListOpts{ + newValue("workflow_input", `{"msg":"Hello"}`): &crontriggers.ListOpts{ + WorkflowInput: map[string]interface{}{ + "msg": "Hello", + }, + }, + newValue("name", `neq:not_name`): &crontriggers.ListOpts{ + Name: &crontriggers.ListFilter{ + Filter: crontriggers.FilterNEQ, + Value: "not_name", + }, + }, + newValue("created_at", `gt:2018-01-01 00:00:00`): &crontriggers.ListOpts{ + CreatedAt: &crontriggers.ListDateFilter{ + Filter: crontriggers.FilterGT, + Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, + } { + actual, _ := opts.ToCronTriggerListQuery() + th.AssertEquals(t, expected, actual) + } +} + +func newValue(param, value string) string { + v := url.Values{} + v.Add(param, value) + return "?" + v.Encode() +} diff --git a/openstack/workflow/v2/workflows/doc.go b/openstack/workflow/v2/workflows/doc.go index 93a92e8b0c..4e59fff996 100644 --- a/openstack/workflow/v2/workflows/doc.go +++ b/openstack/workflow/v2/workflows/doc.go @@ -4,7 +4,9 @@ Package workflows provides interaction with the workflows API in the OpenStack M Workflow represents a process that can be described in a various number of ways and that can do some job interesting to the end user. Each workflow consists of tasks (at least one) describing what exact steps should be made during workflow execution. -Example to list workflows +Workflow definition is written in Mistral Workflow Language v2. You can find all specification here: https://docs.openstack.org/mistral/latest/user/wf_lang_v2.html + +List workflows listOpts := workflows.ListOpts{ Namespace: "some-namespace", @@ -24,53 +26,48 @@ Example to list workflows fmt.Printf("%+v\n", workflow) } -Example to get a workflow by its ID +Get a workflow - workflow, err := workflows.Get(mistralClient, "workflow-id").Extract() + workflow, err := workflows.Get(mistralClient, "604a3a1e-94e3-4066-a34a-aa56873ef236").Extract() if err != nil { t.Fatalf("Unable to get workflow %s: %v", id, err) } fmt.Printf("%+v\n", workflow) -Example to create a workflow +Create a workflow workflowDefinition := `--- -version: '2.0' - -create_vm: -description: Simple workflow example -type: direct - -input: - - vm_name - - image_ref - - flavor_ref -output: - vm_id: <% $.vm_id %> - -tasks: - create_server: - action: nova.servers_create name=<% $.vm_name %> image=<% $.image_ref %> flavor=<% $.flavor_ref %> - publish: - vm_id: <% task(create_server).result.id %> - on-success: - - wait_for_instance - - wait_for_instance: - action: nova.servers_find id=<% $.vm_id %> status='ACTIVE' - retry: - delay: 5 - count: 15` + 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", } - execution, err := workflows.Create(mistralClient, opts).Extract() + workflow, err := workflows.Create(mistralClient, opts).Extract() if err != nil { panic(err) } + + fmt.Printf("%+v\n", workflow) + +Delete a workflow + + res := workflows.Delete(fake.ServiceClient(), "604a3a1e-94e3-4066-a34a-aa56873ef236") + if res.Err != nil { + panic(res.Err) + } */ package workflows diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index 08a9d17a56..388e630a9a 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -21,13 +21,15 @@ func TestCreateWorkflow(t *testing.T) { definition := `--- version: '2.0' -simple_echo: +workflow_echo: description: Simple workflow example type: direct + input: + - msg tasks: - test: - action: std.echo output="Hello World!"` + test: + action: std.echo output="<% $.msg %>"` th.Mux.HandleFunc("/workflows", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -45,15 +47,16 @@ simple_echo: fmt.Fprintf(w, `{ "workflows": [ { - "created_at": "1970-01-01 00:00:00", - "definition": "Workflow Definition in Mistral DSL v2", - "id": "1", - "input": "param1, param2", - "name": "flow", + "created_at": "2018-09-12 15:48:17", + "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<%% $.msg %%>\"", + "id": "604a3a1e-94e3-4066-a34a-aa56873ef236", + "input": "msg", + "name": "workflow_echo", "namespace": "some-namespace", - "project_id": "p1", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "scope": "private", - "updated_at": "1970-01-01 00:00:00" + "tags": [], + "updated_at": "2018-09-12 15:48:17" } ] }`) @@ -70,17 +73,18 @@ simple_echo: t.Fatalf("Unable to create workflow: %v", err) } - updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + updated := time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC) expected := []workflows.Workflow{ workflows.Workflow{ - ID: "1", - Definition: "Workflow Definition in Mistral DSL v2", - Name: "flow", + ID: "604a3a1e-94e3-4066-a34a-aa56873ef236", + Definition: "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", + Name: "workflow_echo", Namespace: "some-namespace", - Input: "param1, param2", - ProjectID: "p1", + Input: "msg", + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", Scope: "private", - CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), + Tags: []string{}, + CreatedAt: time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC), UpdatedAt: &updated, }, } @@ -94,14 +98,14 @@ func TestDeleteWorkflow(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - th.Mux.HandleFunc("/workflows/1", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/workflows/604a3a1e-94e3-4066-a34a-aa56873ef236", 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) }) - res := workflows.Delete(fake.ServiceClient(), "1") + res := workflows.Delete(fake.ServiceClient(), "604a3a1e-94e3-4066-a34a-aa56873ef236") th.AssertNoErr(t, res.Err) } @@ -114,15 +118,16 @@ func TestGetWorkflow(t *testing.T) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, ` { - "created_at": "1970-01-01 00:00:00", - "definition": "Workflow Definition in Mistral DSL v2", - "id": "1", - "input": "param1, param2", - "name": "flow", + "created_at": "2018-09-12 15:48:17", + "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<%% $.msg %%>\"", + "id": "604a3a1e-94e3-4066-a34a-aa56873ef236", + "input": "msg", + "name": "workflow_echo", "namespace": "some-namespace", - "project_id": "p1", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "scope": "private", - "updated_at": "1970-01-01 00:00:00" + "tags": [], + "updated_at": "2018-09-12 15:48:17" } `) }) @@ -131,16 +136,17 @@ func TestGetWorkflow(t *testing.T) { t.Fatalf("Unable to get workflow: %v", err) } - updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + updated := time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC) expected := &workflows.Workflow{ - ID: "1", - Definition: "Workflow Definition in Mistral DSL v2", - Name: "flow", + ID: "604a3a1e-94e3-4066-a34a-aa56873ef236", + Definition: "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", + Name: "workflow_echo", Namespace: "some-namespace", - Input: "param1, param2", - ProjectID: "p1", + Input: "msg", + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", Scope: "private", - CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), + Tags: []string{}, + CreatedAt: time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC), UpdatedAt: &updated, } if !reflect.DeepEqual(expected, actual) { @@ -160,22 +166,23 @@ func TestListWorkflows(t *testing.T) { switch marker { case "": fmt.Fprintf(w, `{ - "next": "%s/workflows?marker=1", + "next": "%s/workflows?marker=604a3a1e-94e3-4066-a34a-aa56873ef236", "workflows": [ { - "created_at": "1970-01-01 00:00:00", - "definition": "Workflow Definition in Mistral DSL v2", - "id": "1", - "input": "param1, param2", - "name": "flow", + "created_at": "2018-09-12 15:48:17", + "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<%% $.msg %%>\"", + "id": "604a3a1e-94e3-4066-a34a-aa56873ef236", + "input": "msg", + "name": "workflow_echo", "namespace": "some-namespace", - "project_id": "p1", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", "scope": "private", - "updated_at": "1970-01-01 00:00:00" + "tags": [], + "updated_at": "2018-09-12 15:48:17" } ] }`, th.Server.URL) - case "1": + case "604a3a1e-94e3-4066-a34a-aa56873ef236": fmt.Fprintf(w, `{ "workflows": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) @@ -189,10 +196,23 @@ func TestListWorkflows(t *testing.T) { if err != nil { return false, err } - updated := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + + updated := time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC) expected := []workflows.Workflow{ - {ID: "1", Definition: "Workflow Definition in Mistral DSL v2", Name: "flow", Namespace: "some-namespace", Input: "param1, param2", ProjectID: "p1", Scope: "private", CreatedAt: time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC), UpdatedAt: &updated}, + workflows.Workflow{ + ID: "604a3a1e-94e3-4066-a34a-aa56873ef236", + Definition: "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", + Name: "workflow_echo", + Namespace: "some-namespace", + Input: "msg", + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", + Scope: "private", + Tags: []string{}, + CreatedAt: time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC), + UpdatedAt: &updated, + }, } + if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) } From 573203606e5de6d11c4296e26564687804bbbd94 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Thu, 27 Sep 2018 14:39:26 +0200 Subject: [PATCH 0560/2296] Execution list --- acceptance/openstack/workflow/v2/execution.go | 16 ++ .../openstack/workflow/v2/executions_test.go | 30 +++ openstack/workflow/v2/executions/doc.go | 37 +++- openstack/workflow/v2/executions/requests.go | 175 +++++++++++++++++- openstack/workflow/v2/executions/results.go | 51 ++++- .../v2/executions/testing/requests_test.go | 115 ++++++++++++ openstack/workflow/v2/executions/urls.go | 4 + 7 files changed, 417 insertions(+), 11 deletions(-) diff --git a/acceptance/openstack/workflow/v2/execution.go b/acceptance/openstack/workflow/v2/execution.go index cf6d27e467..6eb6d048da 100644 --- a/acceptance/openstack/workflow/v2/execution.go +++ b/acceptance/openstack/workflow/v2/execution.go @@ -65,3 +65,19 @@ func DeleteExecution(t *testing.T, client *gophercloud.ServiceClient, execution } t.Logf("Deleted executions: %s", execution.Description) } + +// ListExecutions lists the executions. +func ListExecutions(t *testing.T, client *gophercloud.ServiceClient, opts executions.ListOptsBuilder) ([]executions.Execution, error) { + allPages, err := executions.List(client, opts).AllPages() + if err != nil { + t.Fatalf("Unable to list executions: %v", err) + } + + executionsList, err := executions.ExtractExecutions(allPages) + if err != nil { + t.Fatalf("Unable to extract executions: %v", err) + } + + t.Logf("Executions list find, length: %d", len(executionsList)) + return executionsList, err +} diff --git a/acceptance/openstack/workflow/v2/executions_test.go b/acceptance/openstack/workflow/v2/executions_test.go index b286bc71f2..86c0dd858b 100644 --- a/acceptance/openstack/workflow/v2/executions_test.go +++ b/acceptance/openstack/workflow/v2/executions_test.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -22,3 +23,32 @@ func TestExecutionsCreate(t *testing.T) { tools.PrintResource(t, execution) } + +func TestExecutionsList(t *testing.T) { + client, err := clients.NewWorkflowV2Client() + th.AssertNoErr(t, err) + + workflow, err := CreateWorkflow(t, client) + th.AssertNoErr(t, err) + defer DeleteWorkflow(t, client, workflow) + + execution, err := CreateExecution(t, client, workflow) + th.AssertNoErr(t, err) + defer DeleteExecution(t, client, execution) + + list, err := ListExecutions(t, client, &executions.ListOpts{ + Description: &executions.ListFilter{ + Value: execution.Description, + }, + CreatedAt: &executions.ListDateFilter{ + Filter: executions.FilterGTE, + Value: execution.CreatedAt, + }, + Input: execution.Input, + }) + + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(list)) + + tools.PrintResource(t, list) +} diff --git a/openstack/workflow/v2/executions/doc.go b/openstack/workflow/v2/executions/doc.go index 0092e25579..6e1777bd5a 100644 --- a/openstack/workflow/v2/executions/doc.go +++ b/openstack/workflow/v2/executions/doc.go @@ -1,12 +1,25 @@ /* Package executions provides interaction with the execution API in the OpenStack Mistral service. -An execution is a particular execution of a specific workflow. Each execution contains all information about workflow itself, about execution process, state, input and output data. +An execution is a one-shot execution of a specific workflow. Each execution contains all information about workflow itself, about execution process, state, input and output data. -Example to list executions +An execution represents also the execution of a cron trigger. Each run of a cron trigger will generate an execution. +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. + + // List all executions from a given workflow list with a creation date upper than 2018-01-01 00:00:00 listOpts := executions.ListOpts{ - WorkflowID: "w1", + WorkflowName: &executions.ListFilter{ + Value: "Workflow1,Workflow2", + Filter: executions.FilterIN, + }, + CreatedAt: &executions.ListDateFilter{ + Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), + Filter: executions.FilterGTE, + }, } allPages, err := executions.List(mistralClient, listOpts).AllPages() @@ -23,7 +36,7 @@ Example to list executions fmt.Printf("%+v\n", ex) } -Example to create an execution +Create an execution createOpts := &executions.CreateOpts{ WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", @@ -37,5 +50,21 @@ Example to create an execution if err != nil { panic(err) } + +Get an execution + + execution, err := executions.Get(mistralClient, "50bb59f1-eb77-4017-a77f-6d575b002667").Extract() + if err != nil { + panic(err) + } + fmt.Printf(%+v\n", execution) + +Delete an execution + + res := executions.Delete(mistralClient, "50bb59f1-eb77-4017-a77f-6d575b002667") + if res.Err != nil { + panic(res.Err) + } + */ package executions diff --git a/openstack/workflow/v2/executions/requests.go b/openstack/workflow/v2/executions/requests.go index dbe431ec39..ea35499413 100644 --- a/openstack/workflow/v2/executions/requests.go +++ b/openstack/workflow/v2/executions/requests.go @@ -1,6 +1,15 @@ package executions -import "github.com/gophercloud/gophercloud" +import ( + "encoding/json" + "fmt" + "net/url" + "reflect" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) // CreateOptsBuilder allows extension to add additional parameters to the Create request. type CreateOptsBuilder interface { @@ -64,3 +73,167 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } + +// ListOptsBuilder allows extension to add additional parameters to the List request. +type ListOptsBuilder interface { + ToExecutionListQuery() (string, error) +} + +// ListOpts filters the result returned by the List() function. +type ListOpts struct { + // WorkflowName allows to filter by workflow name. + WorkflowName *ListFilter `q:"-"` + // WorkflowID allows to filter by workflow id. + WorkflowID string `q:"workflow_id"` + // Description allows to filter by execution description. + Description *ListFilter `q:"-"` + // Params allows to filter by specific parameters. + Params map[string]interface{} `q:"-"` + // TaskExecutionID allows to filter with a specific task execution id. + TaskExecutionID string `q:"task_execution_id"` + // RootExecutionID allows to filter with a specific root execution id. + RootExecutionID string `q:"root_execution_id"` + // State allows to filter by execution state. + // Possible values are IDLE, RUNNING, PAUSED, SUCCESS, ERROR, CANCELLED. + State *ListFilter `q:"-"` + // StateInfo allows to filter by state info. + StateInfo *ListFilter `q:"-"` + // Input allows to filter by specific input. + Input map[string]interface{} `q:"-"` + // Output allows to filter by specific output. + Output map[string]interface{} `q:"-"` + // CreatedAt allows to filter by execution creation date. + CreatedAt *ListDateFilter `q:"-"` + // UpdatedAt allows to filter by last execution update date. + UpdatedAt *ListDateFilter `q:"-"` + // IncludeOutput requests to include the output for all executions in the list. + IncludeOutput bool `q:"-"` + // ProjectID allows to filter by given project id. Admin required. + ProjectID string `q:"project_id"` + // AllProjects requests to get executions of all projects. Admin required. + AllProjects int `q:"all_projects"` + // SortDir allows to select sort direction. + // It can be "asc" or "desc" (default). + SortDirs string `q:"sort_dirs"` + // SortKey allows to sort by one of the execution attributes. + SortKeys string `q:"sort_keys"` + // Marker and Limit control paging. + // Marker instructs List where to start listing from. + Marker string `q:"marker"` + // Limit instructs List to refrain from sending excessively large lists of + // executions. + Limit int `q:"limit"` +} + +// ListFilter allows to filter string parameters with different filters. +// Empty value for Filter checks for equality. +type ListFilter struct { + Filter FilterType + Value string +} + +func (l ListFilter) String() string { + if l.Filter != "" { + return fmt.Sprintf("%s:%s", l.Filter, l.Value) + } + + return l.Value +} + +// ListDateFilter allows to filter date parameters with different filters. +// Empty value for Filter checks for equality. +type ListDateFilter struct { + Filter FilterType + Value time.Time +} + +func (l ListDateFilter) String() string { + v := l.Value.Format(gophercloud.RFC3339ZNoTNoZ) + + if l.Filter != "" { + return fmt.Sprintf("%s:%s", l.Filter, v) + } + + return v +} + +// FilterType represents a valid filter to use for filtering executions. +type FilterType string + +const ( + // FilterEQ checks equality. + FilterEQ = "eq" + // FilterNEQ checks non equality. + FilterNEQ = "neq" + // FilterIN checks for belonging in a list, comma separated. + FilterIN = "in" + // FilterNIN checks for values that does not belong from a list, comma separated. + FilterNIN = "nin" + // FilterGT checks for values strictly greater. + FilterGT = "gt" + // FilterGTE checks for values greater or equal. + FilterGTE = "gte" + // FilterLT checks for values strictly lower. + FilterLT = "lt" + // FilterLTE checks for values lower or equal. + FilterLTE = "lte" + // FilterHas checks for values that contains the requested parameter. + FilterHas = "has" +) + +// ToExecutionListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToExecutionListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + + if opts.IncludeOutput { + params.Add("include_output", "1") + } + + for queryParam, value := range map[string]map[string]interface{}{"params": opts.Params, "input": opts.Input, "output": opts.Output} { + if value != nil { + b, err := json.Marshal(value) + if err != nil { + return "", err + } + params.Add(queryParam, string(b)) + } + } + + for queryParam, value := range map[string]fmt.Stringer{ + "created_at": opts.CreatedAt, + "updated_at": opts.UpdatedAt, + "workflow_name": opts.WorkflowName, + "description": opts.Description, + "state": opts.State, + "state_info": opts.StateInfo, + } { + if !reflect.ValueOf(value).IsNil() { + params.Add(queryParam, value.String()) + } + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), nil +} + +// List performs a call to list executions. +// You may provide options to filter the executions. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToExecutionListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ExecutionPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/workflow/v2/executions/results.go b/openstack/workflow/v2/executions/results.go index b6a6b96e84..0c73370859 100644 --- a/openstack/workflow/v2/executions/results.go +++ b/openstack/workflow/v2/executions/results.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type commonResult struct { @@ -103,17 +104,55 @@ func (r *Execution) UnmarshalJSON(b []byte) error { r.CreatedAt = time.Time(s.CreatedAt) r.UpdatedAt = time.Time(s.UpdatedAt) - if err := json.Unmarshal([]byte(s.Input), &r.Input); err != nil { - return err + if s.Input != "" { + if err := json.Unmarshal([]byte(s.Input), &r.Input); err != nil { + return err + } } - if err := json.Unmarshal([]byte(s.Output), &r.Output); err != nil { - return err + if s.Output != "" { + if err := json.Unmarshal([]byte(s.Output), &r.Output); err != nil { + return err + } } - if err := json.Unmarshal([]byte(s.Params), &r.Params); err != nil { - return err + if s.Params != "" { + if err := json.Unmarshal([]byte(s.Params), &r.Params); err != nil { + return err + } } return nil } + +// ExecutionPage contains a single page of all executions from a List call. +type ExecutionPage struct { + pagination.LinkedPageBase +} + +// IsEmpty checks if an ExecutionPage contains any results. +func (r ExecutionPage) IsEmpty() (bool, error) { + exec, err := ExtractExecutions(r) + return len(exec) == 0, err +} + +// NextPageURL finds the next page URL in a page in order to navigate to the next page of results. +func (r ExecutionPage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Next, nil +} + +// ExtractExecutions get the list of executions from a page acquired from the List call. +func ExtractExecutions(r pagination.Page) ([]Execution, error) { + var s struct { + Executions []Execution `json:"executions"` + } + err := (r.(ExecutionPage)).ExtractInto(&s) + return s.Executions, err +} diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index ac66b76a56..dfe278294a 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -3,11 +3,13 @@ package testing import ( "fmt" "net/http" + "net/url" "reflect" "testing" "time" "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -150,3 +152,116 @@ func TestDeleteExecution(t *testing.T) { res := executions.Delete(fake.ServiceClient(), "1") th.AssertNoErr(t, res.Err) } + +func TestListExecutions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/executions", 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") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, `{ + "executions": [ + { + "created_at": "2018-09-12 14:48:49", + "description": "description", + "id": "50bb59f1-eb77-4017-a77f-6d575b002667", + "input": "{\"msg\": \"Hello\"}", + "params": "{\"namespace\": \"\", \"env\": {}}", + "project_id": "778c0f25df0d492a9a868ee9e2fbb513", + "root_execution_id": null, + "state": "SUCCESS", + "state_info": null, + "task_execution_id": null, + "updated_at": "2018-09-12 14:48:49", + "workflow_id": "6656c143-a009-4bcb-9814-cc100a20bbfa", + "workflow_name": "echo", + "workflow_namespace": "" + } + ], + "next": "%s/executions?marker=50bb59f1-eb77-4017-a77f-6d575b002667" + }`, th.Server.URL) + case "50bb59f1-eb77-4017-a77f-6d575b002667": + fmt.Fprintf(w, `{ "executions": [] }`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) + pages := 0 + // Get all executions + err := executions.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + pages++ + actual, err := executions.ExtractExecutions(page) + if err != nil { + return false, err + } + + expected := []executions.Execution{ + executions.Execution{ + ID: "50bb59f1-eb77-4017-a77f-6d575b002667", + Description: "description", + Input: map[string]interface{}{ + "msg": "Hello", + }, + Params: map[string]interface{}{ + "namespace": "", + "env": map[string]interface{}{}, + }, + ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", + State: "SUCCESS", + WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", + WorkflowName: "echo", + CreatedAt: time.Date(2018, time.September, 12, 14, 48, 49, 0, time.UTC), + UpdatedAt: time.Date(2018, time.September, 12, 14, 48, 49, 0, time.UTC), + }, + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } + return true, nil + }) + if err != nil { + t.Fatal(err) + } + if pages != 1 { + t.Errorf("Expected one page, got %d", pages) + } +} + +func TestToExecutionListQuery(t *testing.T) { + for expected, opts := range map[string]*executions.ListOpts{ + newValue("input", `{"msg":"Hello"}`): &executions.ListOpts{ + Input: map[string]interface{}{ + "msg": "Hello", + }, + }, + newValue("description", `neq:not_description`): &executions.ListOpts{ + Description: &executions.ListFilter{ + Filter: executions.FilterNEQ, + Value: "not_description", + }, + }, + newValue("created_at", `gt:2018-01-01 00:00:00`): &executions.ListOpts{ + CreatedAt: &executions.ListDateFilter{ + Filter: executions.FilterGT, + Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, + } { + actual, _ := opts.ToExecutionListQuery() + + th.AssertEquals(t, expected, actual) + } +} + +func newValue(param, value string) string { + v := url.Values{} + v.Add(param, value) + + return "?" + v.Encode() +} diff --git a/openstack/workflow/v2/executions/urls.go b/openstack/workflow/v2/executions/urls.go index 97321857a0..d593e9d28f 100644 --- a/openstack/workflow/v2/executions/urls.go +++ b/openstack/workflow/v2/executions/urls.go @@ -13,3 +13,7 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("executions", id) } + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("executions") +} From 088c02cfa1d1a40d8d6752cac5ece7b099ea4f78 Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Tue, 9 Oct 2018 21:21:11 +0200 Subject: [PATCH 0561/2296] Networking v2 Trunk support - List (#1272) This patch adds the Networking extension trunk support for List operations. Signed-off-by: Antoni Segura Puimedon --- .../v2/extensions/trunks/trunks_test.go | 22 +++++++ .../networking/v2/extensions/trunks/doc.go | 17 ++++++ .../v2/extensions/trunks/requests.go | 57 +++++++++++++++++++ .../v2/extensions/trunks/results.go | 20 +++++++ .../v2/extensions/trunks/testing/fixtures.go | 53 +++++++++++++++++ .../trunks/testing/requests_test.go | 38 +++++++++++++ .../networking/v2/extensions/trunks/urls.go | 4 ++ 7 files changed, 211 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index f898ef2509..3b76fa04fc 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" ) func TestTrunkCRUD(t *testing.T) { @@ -57,3 +58,24 @@ func TestTrunkCRUD(t *testing.T) { tools.PrintResource(t, trunk) } + +func TestTrunkList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + allPages, err := trunks.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list trunks: %v", err) + } + + allTrunks, err := trunks.ExtractTrunks(allPages) + if err != nil { + t.Fatalf("Unable to extract trunks: %v", err) + } + + for _, trunk := range allTrunks { + tools.PrintResource(t, trunk) + } +} diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 6d563dedbf..79dd0a57e1 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -55,5 +55,22 @@ Example of deleting a Trunk if err != nil { panic(err) } + +Example of listing Trunks + + listOpts := trunks.ListOpts{} + allPages, err := trunks.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + allTrunks, err := trunks.ExtractTrunks(allPages) + if err != nil { + panic(err) + } + for _, trunk := range allTrunks { + fmt.Printf("%+v\n", trunk) + } + + */ package trunks diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index f01b946e37..2cbec47b2c 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -2,6 +2,7 @@ package trunks import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -45,3 +46,59 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, id), nil) return } + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToTrunkListQuery() (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 trunk attributes you want to see returned. SortKey allows you to sort +// by a particular trunk attribute. SortDir sets the direction, and is either +// `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + AdminStateUp *bool `q:"admin_state_up"` + Description string `q:"description"` + ID string `q:"id"` + Name string `q:"name"` + PortID string `q:"port_id"` + RevisionNumber string `q:"revision_number"` + Status string `q:"status"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + SortDir string `q:"sort_dir"` + SortKey string `q:"sort_key"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` +} + +// ToTrunkListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTrunkListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// trunks. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those trunks that are owned by the tenant +// who submits the request, unless the request is submitted by a user with +// administrative rights. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToTrunkListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return TrunkPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index ef4393a9ff..8323c97988 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -4,6 +4,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type Subport struct { @@ -78,3 +79,22 @@ func (r commonResult) Extract() (*Trunk, error) { err := r.ExtractInto(&s) return s.Trunk, err } + +// TrunkPage is the page returned by a pager when traversing a collection of +// trunk resources. +type TrunkPage struct { + pagination.LinkedPageBase +} + +func (page TrunkPage) IsEmpty() (bool, error) { + trunks, err := ExtractTrunks(page) + return len(trunks) == 0, err +} + +func ExtractTrunks(page pagination.Page) ([]Trunk, error) { + var a struct { + Trunks []Trunk `json:"trunks"` + } + err := (page.(TrunkPage)).ExtractInto(&a) + return a.Trunks, err +} diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures.go index 0b5b787fa9..91ffcd4ca8 100644 --- a/openstack/networking/v2/extensions/trunks/testing/fixtures.go +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures.go @@ -88,6 +88,59 @@ const CreateNoSubportsResponse = ` } }` +const ListResponse = ` +{ + "trunks": [ + { + "admin_state_up": true, + "created_at": "2018-10-01T15:29:39Z", + "description": "", + "id": "3e72aa1b-d0da-48f2-831a-fd1c5f3f99c2", + "name": "mytrunk", + "port_id": "16c425d3-d7fc-40b8-b94c-cc95da45b270", + "project_id": "e153f3f9082240a5974f667cfe1036e3", + "revision_number": 3, + "status": "ACTIVE", + "sub_ports": [ + { + "port_id": "424da4b7-7868-4db2-bb71-05155601c6e4", + "segmentation_id": 11, + "segmentation_type": "vlan" + } + ], + "tags": [], + "tenant_id": "e153f3f9082240a5974f667cfe1036e3", + "updated_at": "2018-10-01T15:43:04Z" + }, + { + "admin_state_up": true, + "created_at": "2018-10-03T13:57:24Z", + "description": "Trunk created by gophercloud", + "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", + "name": "gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "project_id": "e153f3f9082240a5974f667cfe1036e3", + "revision_number": 1, + "status": "ACTIVE", + "sub_ports": [ + { + "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + "segmentation_id": 1, + "segmentation_type": "vlan" + }, + { + "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + "segmentation_id": 2, + "segmentation_type": "vlan" + } + ], + "tags": [], + "tenant_id": "e153f3f9082240a5974f667cfe1036e3", + "updated_at": "2018-10-03T13:57:26Z" + } + ] +}` + var ExpectedSubports = []trunks.Subport{ { PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index 55c266a25c..a938d0face 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -7,6 +7,7 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -101,3 +102,40 @@ func TestDelete(t *testing.T) { res := trunks.Delete(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c") th.AssertNoErr(t, res.Err) } + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks", 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, ListResponse) + }) + + client := fake.ServiceClient() + count := 0 + + trunks.List(client, trunks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := trunks.ExtractTrunks(page) + if err != nil { + t.Errorf("Failed to extract trunks: %v", err) + return false, err + } + + expected, err := ExpectedTrunkSlice() + th.AssertNoErr(t, err) + 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/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go index 36622cddd2..bb26c375f3 100644 --- a/openstack/networking/v2/extensions/trunks/urls.go +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -19,3 +19,7 @@ func createURL(c *gophercloud.ServiceClient) string { func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From f2f15d653e071430d87a72258cee54b968f8a0ba Mon Sep 17 00:00:00 2001 From: Sendu Bala Date: Tue, 9 Oct 2018 20:22:28 +0100 Subject: [PATCH 0562/2296] Resolve #209 - Always be able to handle large integers in JSON parsing. (#1270) * Resolve #209 - Always be able to handle large integers in JSON parsing. * Revert no longer necessary change to UseNumber() on the JSON decoder. --- .../v2/extensions/quotasets/testing/fixtures.go | 12 ++++++------ .../compute/v2/flavors/testing/requests_test.go | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go index 2915d31037..c0955c5ca9 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go +++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go @@ -22,7 +22,7 @@ const GetOutput = ` "injected_file_content_bytes" : 10240, "injected_files" : 5, "metadata_items" : 128, - "ram" : 200000, + "ram" : 9216000, "key_pairs" : 10, "injected_file_path_bytes" : 255, "server_groups" : 2, @@ -73,7 +73,7 @@ const GetDetailsOutput = ` }, "ram" : { "in_use": 0, - "limit": 200000, + "limit": 9216000, "reserved": 0 }, "key_pairs" : { @@ -110,7 +110,7 @@ var FirstQuotaSet = quotasets.QuotaSet{ InjectedFiles: 5, KeyPairs: 10, MetadataItems: 128, - RAM: 200000, + RAM: 9216000, SecurityGroupRules: 20, SecurityGroups: 10, Cores: 200, @@ -127,7 +127,7 @@ var FirstQuotaDetailsSet = quotasets.QuotaDetailSet{ InjectedFiles: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 5}, KeyPairs: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10}, MetadataItems: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 128}, - RAM: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 200000}, + RAM: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 9216000}, SecurityGroupRules: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 20}, SecurityGroups: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10}, Cores: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 200}, @@ -137,7 +137,7 @@ var FirstQuotaDetailsSet = quotasets.QuotaDetailSet{ } //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":200000,"security_group_rules":20,"security_groups":10,"server_groups":2,"server_group_members":3}}` +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 const PartialUpdateBody = `{"quota_set":{"cores":200, "force":true}}` @@ -151,7 +151,7 @@ var UpdatedQuotaSet = quotasets.UpdateOpts{ InjectedFiles: gophercloud.IntToPointer(5), KeyPairs: gophercloud.IntToPointer(10), MetadataItems: gophercloud.IntToPointer(128), - RAM: gophercloud.IntToPointer(200000), + RAM: gophercloud.IntToPointer(9216000), SecurityGroupRules: gophercloud.IntToPointer(20), SecurityGroups: gophercloud.IntToPointer(10), Cores: gophercloud.IntToPointer(200), diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index fba0d4776b..14a9388edb 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -35,7 +35,7 @@ func TestListFlavors(t *testing.T) { "name": "m1.tiny", "vcpus": 1, "disk": 1, - "ram": 512, + "ram": 9216000, "swap":"", "os-flavor-access:is_public": true, "OS-FLV-EXT-DATA:ephemeral": 10 @@ -87,7 +87,7 @@ func TestListFlavors(t *testing.T) { } expected := []flavors.Flavor{ - {ID: "1", Name: "m1.tiny", VCPUs: 1, Disk: 1, RAM: 512, Swap: 0, IsPublic: true, Ephemeral: 10}, + {ID: "1", Name: "m1.tiny", VCPUs: 1, Disk: 1, RAM: 9216000, Swap: 0, IsPublic: true, Ephemeral: 10}, {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}, } From 8e38cd79a73242588b9419e7356f0451be583447 Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Thu, 4 Oct 2018 15:42:16 +0200 Subject: [PATCH 0563/2296] Networking v2 Trunk support - Get This patch adds the Networking extension trunk support for the Get operation. Signed-off-by: Antoni Segura Puimedon --- .../v2/extensions/trunks/trunks_test.go | 5 ++++ .../networking/v2/extensions/trunks/doc.go | 7 +++++ .../v2/extensions/trunks/requests.go | 6 ++++ .../v2/extensions/trunks/results.go | 6 ++++ .../v2/extensions/trunks/testing/fixtures.go | 30 +++++++++++++++++++ .../trunks/testing/requests_test.go | 21 +++++++++++++ .../networking/v2/extensions/trunks/urls.go | 4 +++ 7 files changed, 79 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 3b76fa04fc..617fa59231 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -56,6 +56,11 @@ func TestTrunkCRUD(t *testing.T) { } defer DeleteTrunk(t, client, trunk.ID) + _, err = trunks.Get(client, trunk.ID).Extract() + if err != nil { + t.Fatalf("Unable to get trunk: %v", err) + } + tools.PrintResource(t, trunk) } diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 79dd0a57e1..1515e6fa55 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -71,6 +71,13 @@ Example of listing Trunks fmt.Printf("%+v\n", trunk) } +Example of getting a Trunk + trunkID = "52d8d124-3dc9-4563-9fef-bad3187ecf2d" + trunk, err := trunks.Get(networkClient, trunkID).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", trunk) */ package trunks diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index 2cbec47b2c..cd26459b35 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -102,3 +102,9 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { return TrunkPage{pagination.LinkedPageBase{PageResult: r}} }) } + +// Get retrieves a specific trunk based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index 8323c97988..be7eea3689 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -29,6 +29,12 @@ type DeleteResult struct { gophercloud.ErrResult } +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Trunk. +type GetResult struct { + commonResult +} + type Trunk struct { // Indicates whether the trunk is currently operational. Possible values include // `ACTIVE', `DOWN', `BUILD', 'DEGRADED' or `ERROR'. diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures.go index 91ffcd4ca8..ded6e7d334 100644 --- a/openstack/networking/v2/extensions/trunks/testing/fixtures.go +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures.go @@ -141,6 +141,36 @@ const ListResponse = ` ] }` +const GetResponse = ` +{ + "trunk": { + "admin_state_up": true, + "created_at": "2018-10-03T13:57:24Z", + "description": "Trunk created by gophercloud", + "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", + "name": "gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "project_id": "e153f3f9082240a5974f667cfe1036e3", + "revision_number": 1, + "status": "ACTIVE", + "sub_ports": [ + { + "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + "segmentation_id": 1, + "segmentation_type": "vlan" + }, + { + "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + "segmentation_id": 2, + "segmentation_type": "vlan" + } + ], + "tags": [], + "tenant_id": "e153f3f9082240a5974f667cfe1036e3", + "updated_at": "2018-10-03T13:57:26Z" + } +}` + var ExpectedSubports = []trunks.Subport{ { PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index a938d0face..c172490f5b 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -139,3 +139,24 @@ func TestList(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c", 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, GetResponse) + }) + + n, err := trunks.Get(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() + th.AssertNoErr(t, err) + expectedTrunks, err := ExpectedTrunkSlice() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expectedTrunks[1], n) +} diff --git a/openstack/networking/v2/extensions/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go index bb26c375f3..2bd54a5ed6 100644 --- a/openstack/networking/v2/extensions/trunks/urls.go +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -23,3 +23,7 @@ func deleteURL(c *gophercloud.ServiceClient, id string) string { func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From ffc517a9deb40095edaa740ce041477f78b6c4c4 Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Thu, 4 Oct 2018 15:58:15 +0200 Subject: [PATCH 0564/2296] Networking v2 Trunk support - Update This patch adds the Networking extension trunk support for the Update operation. Signed-off-by: Antoni Segura Puimedon --- .../v2/extensions/trunks/trunks_test.go | 13 +++++++ .../networking/v2/extensions/trunks/doc.go | 16 ++++++++ .../v2/extensions/trunks/requests.go | 26 +++++++++++++ .../v2/extensions/trunks/results.go | 6 +++ .../v2/extensions/trunks/testing/fixtures.go | 39 +++++++++++++++++++ .../trunks/testing/requests_test.go | 33 ++++++++++++++++ .../networking/v2/extensions/trunks/urls.go | 4 ++ 7 files changed, 137 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 617fa59231..34be0c0083 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -61,6 +61,19 @@ func TestTrunkCRUD(t *testing.T) { t.Fatalf("Unable to get trunk: %v", err) } + // Update Trunk + updateOpts := trunks.UpdateOpts{ + Name: "updated_gophertrunk", + } + updatedTrunk, err := trunks.Update(client, trunk.ID, updateOpts).Extract() + if err != nil { + t.Fatalf("Unable to update trunk: %v", err) + } + + if trunk.Name == updatedTrunk.Name { + t.Fatalf("Trunk name was not updated correctly") + } + tools.PrintResource(t, trunk) } diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 1515e6fa55..b5729299ba 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -79,5 +79,21 @@ Example of getting a Trunk panic(err) } fmt.Printf("%+v\n", trunk) + +Example of updating a Trunk + + trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" + subports, err := trunks.GetSubports(client, trunkID).Extract() + iFalse := false + updateOpts := trunks.UpdateOpts{ + AdminStateUp: &iFalse, + Name: "updated_gophertrunk", + Description: "trunk updated by gophercloud", + } + trunk, err = trunks.Update(networkClient, trunkID, updateOpts).Extract() + if err != nil { + log.Fatal(err) + } + fmt.Printf("%+v\n", trunk) */ package trunks diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index cd26459b35..059935927e 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -108,3 +108,29 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } + +type UpdateOptsBuilder interface { + ToTrunkUpdateMap() (map[string]interface{}, error) +} + +type UpdateOpts struct { + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` +} + +func (opts UpdateOpts) ToTrunkUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "trunk") +} + +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + body, err := opts.ToTrunkUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(updateURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index be7eea3689..89e4fb42a6 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -35,6 +35,12 @@ type GetResult struct { commonResult } +// UpdateResult is the result of an Update request. Call its Extract method to +// interpret it as a Trunk. +type UpdateResult struct { + commonResult +} + type Trunk struct { // Indicates whether the trunk is currently operational. Possible values include // `ACTIVE', `DOWN', `BUILD', 'DEGRADED' or `ERROR'. diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures.go index ded6e7d334..5aef8ea6d5 100644 --- a/openstack/networking/v2/extensions/trunks/testing/fixtures.go +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures.go @@ -171,6 +171,45 @@ const GetResponse = ` } }` +const UpdateRequest = ` +{ + "trunk": { + "admin_state_up": false, + "description": "gophertrunk updated by gophercloud", + "name": "updated_gophertrunk" + } +}` + +const UpdateResponse = ` +{ + "trunk": { + "admin_state_up": false, + "created_at": "2018-10-03T13:57:24Z", + "description": "gophertrunk updated by gophercloud", + "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", + "name": "updated_gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "project_id": "e153f3f9082240a5974f667cfe1036e3", + "revision_number": 6, + "status": "ACTIVE", + "sub_ports": [ + { + "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + "segmentation_id": 1, + "segmentation_type": "vlan" + }, + { + "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + "segmentation_id": 2, + "segmentation_type": "vlan" + } + ], + "tags": [], + "tenant_id": "e153f3f9082240a5974f667cfe1036e3", + "updated_at": "2018-10-03T13:57:33Z" + } +}` + var ExpectedSubports = []trunks.Subport{ { PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index c172490f5b..57b0b907bf 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -160,3 +160,36 @@ func TestGet(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expectedTrunks[1], n) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c", 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, UpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateResponse) + }) + + iFalse := false + name := "updated_gophertrunk" + description := "gophertrunk updated by gophercloud" + options := trunks.UpdateOpts{ + Name: name, + AdminStateUp: &iFalse, + Description: description, + } + n, err := trunks.Update(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Name, name) + th.AssertEquals(t, n.AdminStateUp, iFalse) + th.AssertEquals(t, n.Description, description) +} diff --git a/openstack/networking/v2/extensions/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go index 2bd54a5ed6..7565e5199a 100644 --- a/openstack/networking/v2/extensions/trunks/urls.go +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -27,3 +27,7 @@ func listURL(c *gophercloud.ServiceClient) string { func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From ff61561c2ef6434724676ae61dc3c2ee566b64f9 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Mon, 8 Oct 2018 13:03:57 +0000 Subject: [PATCH 0565/2296] Neutron attributestags DeleteAll support As described in the API docs: https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag-extension https://developer.openstack.org/api-ref/network/v2/#remove-all-tags Issue: #1260 --- .../v2/extensions/attributestags_test.go | 7 +++++++ .../v2/extensions/attributestags/doc.go | 4 ++++ .../v2/extensions/attributestags/requests.go | 9 +++++++++ .../v2/extensions/attributestags/results.go | 6 ++++++ .../attributestags/testing/requests_test.go | 16 ++++++++++++++++ .../v2/extensions/attributestags/urls.go | 4 ++++ 6 files changed, 46 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index cb48163e4e..603c19a040 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -33,4 +33,11 @@ func TestTags(t *testing.T) { tags, err = attributestags.List(client, "networks", network.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, tags, []string{"a", "b", "c"}) + + // Delete all tags + err = attributestags.DeleteAll(client, "networks", network.ID).ExtractErr() + th.AssertNoErr(t, err) + tags, err = attributestags.List(client, "networks", network.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, tags, []string{}) } diff --git a/openstack/networking/v2/extensions/attributestags/doc.go b/openstack/networking/v2/extensions/attributestags/doc.go index 9026faaca8..ff9cb56370 100644 --- a/openstack/networking/v2/extensions/attributestags/doc.go +++ b/openstack/networking/v2/extensions/attributestags/doc.go @@ -17,5 +17,9 @@ Example to ReplaceAll Resource Tags Example to List all Resource Tags tags, err = attributestags.List(conn, "networks", network.ID).Extract() + +Example to Delete all Resource Tags + + err = attributestags.DeleteAll(conn, "networks", network.ID).ExtractErr() */ package attributestags diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index 997f72e726..77ee9636c2 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -43,3 +43,12 @@ func List(client *gophercloud.ServiceClient, resourceType string, resourceID str }) return } + +// DeleteAll deletes all tags on a resource +func DeleteAll(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r DeleteResult) { + url := deleteAllURL(client, resourceType, resourceID) + _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/networking/v2/extensions/attributestags/results.go b/openstack/networking/v2/extensions/attributestags/results.go index af2650ae18..b204c3973b 100644 --- a/openstack/networking/v2/extensions/attributestags/results.go +++ b/openstack/networking/v2/extensions/attributestags/results.go @@ -26,3 +26,9 @@ type ReplaceAllResult struct { type ListResult struct { tagResult } + +// DeleteResult is the result from a Delete/DeleteAll operation. +// Call its ExtractErr method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go index 3c3d538657..48e4bd0840 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -55,3 +55,19 @@ func TestList(t *testing.T) { th.AssertDeepEquals(t, res, []string{"abc", "xyz"}) } + +func TestDeleteAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/fakeid/tags", 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 := attributestags.DeleteAll(fake.ServiceClient(), "networks", "fakeid").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/attributestags/urls.go b/openstack/networking/v2/extensions/attributestags/urls.go index f713db90cb..c8bb8622f6 100644 --- a/openstack/networking/v2/extensions/attributestags/urls.go +++ b/openstack/networking/v2/extensions/attributestags/urls.go @@ -13,3 +13,7 @@ func replaceURL(c *gophercloud.ServiceClient, r_type string, id string) string { func listURL(c *gophercloud.ServiceClient, r_type string, id string) string { return c.ServiceURL(r_type, id, tagsPath) } + +func deleteAllURL(c *gophercloud.ServiceClient, r_type string, id string) string { + return c.ServiceURL(r_type, id, tagsPath) +} From d3886e260145918091c08892116885bef4d0c911 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Tue, 9 Oct 2018 13:00:52 +0000 Subject: [PATCH 0566/2296] Neutron attributestags Add tag support As described in the API docs: https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag-extension https://developer.openstack.org/api-ref/network/v2/#add-a-tag Issue: #1260 --- .../v2/extensions/attributestags_test.go | 6 +++++- .../v2/extensions/attributestags/doc.go | 5 +++++ .../v2/extensions/attributestags/requests.go | 9 +++++++++ .../v2/extensions/attributestags/results.go | 6 ++++++ .../attributestags/testing/requests_test.go | 16 ++++++++++++++++ .../v2/extensions/attributestags/urls.go | 4 ++++ 6 files changed, 45 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 603c19a040..dd8075e48d 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -29,10 +29,14 @@ func TestTags(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, tags, []string{"a", "b", "c"}) + // Add a tag + err = attributestags.Add(client, "networks", network.ID, "d").ExtractErr() + th.AssertNoErr(t, err) + // Verify the tags are set in the List response tags, err = attributestags.List(client, "networks", network.ID).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, tags, []string{"a", "b", "c"}) + th.AssertDeepEquals(t, tags, []string{"a", "b", "c", "d"}) // Delete all tags err = attributestags.DeleteAll(client, "networks", network.ID).ExtractErr() diff --git a/openstack/networking/v2/extensions/attributestags/doc.go b/openstack/networking/v2/extensions/attributestags/doc.go index ff9cb56370..c04e4699df 100644 --- a/openstack/networking/v2/extensions/attributestags/doc.go +++ b/openstack/networking/v2/extensions/attributestags/doc.go @@ -21,5 +21,10 @@ Example to List all Resource Tags Example to Delete all Resource Tags err = attributestags.DeleteAll(conn, "networks", network.ID).ExtractErr() + +Example to Add a tag to a Resource + + err = attributestags.Add(client, "networks", network.ID, "atag").ExtractErr() + */ package attributestags diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index 77ee9636c2..3d79bf3115 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -52,3 +52,12 @@ func DeleteAll(client *gophercloud.ServiceClient, resourceType string, resourceI }) return } + +// Add a tag on a resource +func Add(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r AddResult) { + url := addURL(client, resourceType, resourceID, tag) + _, r.Err = client.Put(url, nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/networking/v2/extensions/attributestags/results.go b/openstack/networking/v2/extensions/attributestags/results.go index b204c3973b..fe618ba8fa 100644 --- a/openstack/networking/v2/extensions/attributestags/results.go +++ b/openstack/networking/v2/extensions/attributestags/results.go @@ -32,3 +32,9 @@ type ListResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// AddResult is the result from an Add operation. +// Call its ExtractErr method to determine if the call succeeded or failed. +type AddResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go index 48e4bd0840..be6cc08842 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -71,3 +71,19 @@ func TestDeleteAll(t *testing.T) { err := attributestags.DeleteAll(fake.ServiceClient(), "networks", "fakeid").ExtractErr() th.AssertNoErr(t, err) } + +func TestAdd(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/fakeid/tags/atag", 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") + w.WriteHeader(http.StatusCreated) + }) + + err := attributestags.Add(fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/attributestags/urls.go b/openstack/networking/v2/extensions/attributestags/urls.go index c8bb8622f6..a2189e5f60 100644 --- a/openstack/networking/v2/extensions/attributestags/urls.go +++ b/openstack/networking/v2/extensions/attributestags/urls.go @@ -17,3 +17,7 @@ func listURL(c *gophercloud.ServiceClient, r_type string, id string) string { func deleteAllURL(c *gophercloud.ServiceClient, r_type string, id string) string { return c.ServiceURL(r_type, id, tagsPath) } + +func addURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) string { + return c.ServiceURL(r_type, id, tagsPath, tag) +} From 564458ab063fa961f046d1362f661430d6c285fa Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Tue, 9 Oct 2018 13:04:33 +0000 Subject: [PATCH 0567/2296] Neutron attributestags Delete tag support As described in the API docs: https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag-extension https://developer.openstack.org/api-ref/network/v2/#remove-a-tag Issue: #1260 --- .../v2/extensions/attributestags_test.go | 8 ++++++-- .../v2/extensions/attributestags/doc.go | 3 +++ .../v2/extensions/attributestags/requests.go | 9 +++++++++ .../attributestags/testing/requests_test.go | 16 ++++++++++++++++ .../v2/extensions/attributestags/urls.go | 4 ++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index dd8075e48d..fabc86619f 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -33,10 +33,14 @@ func TestTags(t *testing.T) { err = attributestags.Add(client, "networks", network.ID, "d").ExtractErr() th.AssertNoErr(t, err) - // Verify the tags are set in the List response + // Delete a tag + err = attributestags.Delete(client, "networks", network.ID, "a").ExtractErr() + th.AssertNoErr(t, err) + + // Verify expected tags are set in the List response tags, err = attributestags.List(client, "networks", network.ID).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, tags, []string{"a", "b", "c", "d"}) + th.AssertDeepEquals(t, tags, []string{"b", "c", "d"}) // Delete all tags err = attributestags.DeleteAll(client, "networks", network.ID).ExtractErr() diff --git a/openstack/networking/v2/extensions/attributestags/doc.go b/openstack/networking/v2/extensions/attributestags/doc.go index c04e4699df..137893a6c0 100644 --- a/openstack/networking/v2/extensions/attributestags/doc.go +++ b/openstack/networking/v2/extensions/attributestags/doc.go @@ -26,5 +26,8 @@ Example to Add a tag to a Resource 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() */ package attributestags diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index 3d79bf3115..6e4e1f14db 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -61,3 +61,12 @@ func Add(client *gophercloud.ServiceClient, resourceType string, resourceID stri }) return } + +// Delete a tag on a resource +func Delete(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r DeleteResult) { + url := deleteURL(client, resourceType, resourceID, tag) + _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go index be6cc08842..4fcda67a9a 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -87,3 +87,19 @@ func TestAdd(t *testing.T) { err := attributestags.Add(fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() th.AssertNoErr(t, err) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/fakeid/tags/atag", 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 := attributestags.Delete(fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/attributestags/urls.go b/openstack/networking/v2/extensions/attributestags/urls.go index a2189e5f60..f11caf7842 100644 --- a/openstack/networking/v2/extensions/attributestags/urls.go +++ b/openstack/networking/v2/extensions/attributestags/urls.go @@ -21,3 +21,7 @@ func deleteAllURL(c *gophercloud.ServiceClient, r_type string, id string) string func addURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) string { return c.ServiceURL(r_type, id, tagsPath, tag) } + +func deleteURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) string { + return c.ServiceURL(r_type, id, tagsPath, tag) +} From f48d3a7c36c5be2a737ea63293797bcc9995f1c5 Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Thu, 11 Oct 2018 11:39:02 +0200 Subject: [PATCH 0568/2296] Networking v2 Trunk support - Get Subports This patch adds the Networking extension trunk support for the GetSubports operation. Signed-off-by: Antoni Segura Puimedon --- .../v2/extensions/trunks/trunks_test.go | 9 ++++++++ .../networking/v2/extensions/trunks/doc.go | 6 ++++++ .../v2/extensions/trunks/requests.go | 7 +++++++ .../v2/extensions/trunks/results.go | 14 +++++++++++++ .../v2/extensions/trunks/testing/fixtures.go | 16 ++++++++++++++ .../trunks/testing/requests_test.go | 21 +++++++++++++++++++ .../networking/v2/extensions/trunks/urls.go | 4 ++++ 7 files changed, 77 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 34be0c0083..8655ae4601 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestTrunkCRUD(t *testing.T) { @@ -74,6 +75,14 @@ func TestTrunkCRUD(t *testing.T) { t.Fatalf("Trunk name was not updated correctly") } + // Get subports + subports, err := trunks.GetSubports(client, trunk.ID).Extract() + if err != nil { + t.Fatalf("Unable to get subports from the Trunk: %v", err) + } + th.AssertDeepEquals(t, trunk.Subports[0], subports[0]) + th.AssertDeepEquals(t, trunk.Subports[1], subports[1]) + tools.PrintResource(t, trunk) } diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index b5729299ba..4aa3154bd5 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -95,5 +95,11 @@ Example of updating a Trunk log.Fatal(err) } fmt.Printf("%+v\n", trunk) + +Example of showing subports of a Trunk + + trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" + subports, err := trunks.GetSubports(client, trunkID).Extract() + fmt.Printf("%+v\n", subports) */ package trunks diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index 059935927e..fa5fca01c4 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -134,3 +134,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r }) return } + +func GetSubports(c *gophercloud.ServiceClient, id string) (r GetSubportsResult) { + _, r.Err = c.Get(getSubportsURL(c, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index 89e4fb42a6..df271c20a1 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -41,6 +41,12 @@ type UpdateResult struct { commonResult } +// GetSubportsResult is the result of a Get request on the trunks subports +// resource. Call its Extract method to interpret it as a slice of Subport. +type GetSubportsResult struct { + commonResult +} + type Trunk struct { // Indicates whether the trunk is currently operational. Possible values include // `ACTIVE', `DOWN', `BUILD', 'DEGRADED' or `ERROR'. @@ -110,3 +116,11 @@ func ExtractTrunks(page pagination.Page) ([]Trunk, error) { err := (page.(TrunkPage)).ExtractInto(&a) return a.Trunks, err } + +func (r GetSubportsResult) Extract() ([]Subport, error) { + var s struct { + Subports []Subport `json:"sub_ports"` + } + err := r.ExtractInto(&s) + return s.Subports, err +} diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures.go index 5aef8ea6d5..fa16a58154 100644 --- a/openstack/networking/v2/extensions/trunks/testing/fixtures.go +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures.go @@ -210,6 +210,22 @@ const UpdateResponse = ` } }` +const ListSubportsResponse = ` +{ + "sub_ports": [ + { + "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + "segmentation_id": 1, + "segmentation_type": "vlan" + }, + { + "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + "segmentation_id": 2, + "segmentation_type": "vlan" + } + ] +}` + var ExpectedSubports = []trunks.Subport{ { PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index 57b0b907bf..457bdb486b 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -193,3 +193,24 @@ func TestUpdate(t *testing.T) { th.AssertEquals(t, n.AdminStateUp, iFalse) th.AssertEquals(t, n.Description, description) } + +func TestGetSubports(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c/get_subports", 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, ListSubportsResponse) + }) + + client := fake.ServiceClient() + + subports, err := trunks.GetSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedSubports, subports) +} diff --git a/openstack/networking/v2/extensions/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go index 7565e5199a..cf180450d1 100644 --- a/openstack/networking/v2/extensions/trunks/urls.go +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -31,3 +31,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func updateURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func getSubportsURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "get_subports") +} From b10131e8f634c2068f48313ec6c829483e377e36 Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Thu, 11 Oct 2018 10:56:30 +0200 Subject: [PATCH 0569/2296] BuildRequestQuery to check slice of struct fields This patch enhances BuildRequestQuery so that in case you have a required struct field that is defined as a slice of structs, all the structs in the slice will be checked. This makes it consistent with the current behavior for struct fields. Signed-off-by: Antoni Segura Puimedon --- params.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/params.go b/params.go index 19b8cf7bf8..b9986660cb 100644 --- a/params.go +++ b/params.go @@ -120,6 +120,22 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, continue } + if v.Kind() == reflect.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) { + sliceValue := v + if sliceValue.Kind() == reflect.Ptr { + sliceValue = sliceValue.Elem() + } + + for i := 0; i < sliceValue.Len(); i++ { + element := sliceValue.Index(i) + if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) { + _, err := BuildRequestBody(element.Interface(), "") + if err != nil { + return nil, err + } + } + } + } if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) { if zero { //fmt.Printf("value before change: %+v\n", optsValue.Field(i)) From 04bda5b641aba482bef5e7fd25e8e0c8b6b54f0e Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Thu, 11 Oct 2018 11:03:43 +0200 Subject: [PATCH 0570/2296] Networking trunks - Create: Verify missing fields BuildRequestBody had a bug that made it overlook the required fields in slices of structs. This patch adds a unit test for trunk creation not to miss any of the subports's fields. Signed-off-by: Antoni Segura Puimedon --- .../v2/extensions/trunks/results.go | 6 ++-- .../trunks/testing/requests_test.go | 30 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index df271c20a1..31153c8265 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -8,9 +8,9 @@ import ( ) type Subport struct { - SegmentationID int `json:"segmentation_id"` - SegmentationType string `json:"segmentation_type"` - PortID string `json:"port_id"` + SegmentationID int `json:"segmentation_id" required:"true"` + SegmentationType string `json:"segmentation_type" required:"true"` + PortID string `json:"port_id" required:"true"` } type commonResult struct { diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index 457bdb486b..5c5c07d0a2 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -214,3 +214,33 @@ func TestGetSubports(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedSubports, subports) } + +func TestMissingFields(t *testing.T) { + iTrue := true + opts := trunks.CreateOpts{ + Name: "gophertrunk", + PortID: "c373d2fa-3d3b-4492-924c-aff54dea19b6", + Description: "Trunk created by gophercloud", + AdminStateUp: &iTrue, + Subports: []trunks.Subport{ + { + SegmentationID: 1, + SegmentationType: "vlan", + PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + }, + { + SegmentationID: 2, + SegmentationType: "vlan", + PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + }, + { + PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + }, + }, + } + + _, err := opts.ToTrunkCreateMap() + if err == nil { + t.Fatalf("Failed to detect missing subport fields") + } +} From ecee4bf3886877d32b2cc7573712eecaf44cb9bd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 12 Oct 2018 17:06:39 +0000 Subject: [PATCH 0571/2296] gofmt fixes --- .../extensions/volumeactions_test.go | 5 +-- .../clustering/v1/webhooktrigger_test.go | 3 +- acceptance/openstack/compute/v2/compute.go | 3 +- .../openstack/compute/v2/flavors_test.go | 3 +- .../openstack/compute/v2/keypairs_test.go | 3 +- .../extensions/vpnaas/siteconnection_test.go | 4 +- .../orchestration/v1/orchestration.go | 1 - .../orchestration/v1/stackevents_test.go | 1 - .../schedulerhints/testing/requests_test.go | 1 - .../v1/webhooks/testing/requests_test.go | 3 +- .../extensions/quotasets/testing/fixtures.go | 2 +- .../v2/amphorae/testing/fixtures.go | 1 - .../v2/loadbalancers/testing/fixtures.go | 5 +-- .../loadbalancers/testing/fixtures.go | 5 +-- .../ikepolicies/testing/requests_test.go | 38 +++++++++---------- 15 files changed, 32 insertions(+), 46 deletions(-) diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index fe26bcdb37..53d1f4fb65 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -6,12 +6,11 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" + compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" th "github.com/gophercloud/gophercloud/testhelper" - - blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v2" - compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" ) func TestVolumeActionsUploadImageDestroy(t *testing.T) { diff --git a/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/acceptance/openstack/clustering/v1/webhooktrigger_test.go index 4b5434d1c1..11d4f6ee0d 100644 --- a/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -6,9 +6,8 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" - "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/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 89bd08de43..dfde54ae39 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -12,6 +12,7 @@ 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/compute/v2/extensions/aggregates" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" @@ -28,8 +29,6 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" - - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" "golang.org/x/crypto/ssh" ) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index d4aa341a74..3972b17bfa 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -6,11 +6,10 @@ 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/openstack/compute/v2/flavors" th "github.com/gophercloud/gophercloud/testhelper" - - identity "github.com/gophercloud/gophercloud/acceptance/openstack/identity/v3" ) func TestFlavorsList(t *testing.T) { diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go index 4bb0ed97d7..d2df89aad2 100644 --- a/acceptance/openstack/compute/v2/keypairs_test.go +++ b/acceptance/openstack/compute/v2/keypairs_test.go @@ -5,13 +5,12 @@ package v2 import ( "testing" - "golang.org/x/crypto/ssh" - "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/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" + "golang.org/x/crypto/ssh" ) const keyName = "gophercloud_test_key_pair" diff --git a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index 254ddd1726..72d025ea7a 100644 --- a/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -8,11 +8,9 @@ import ( "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/openstack/networking/v2/extensions/layer3/routers" - "github.com/gophercloud/gophercloud/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/orchestration/v1/orchestration.go b/acceptance/openstack/orchestration/v1/orchestration.go index f3223a81a9..6981537931 100644 --- a/acceptance/openstack/orchestration/v1/orchestration.go +++ b/acceptance/openstack/orchestration/v1/orchestration.go @@ -7,7 +7,6 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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/acceptance/openstack/orchestration/v1/stackevents_test.go index 7bab0fe06a..53c5970a21 100644 --- a/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" - //"github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" th "github.com/gophercloud/gophercloud/testhelper" ) diff --git a/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go b/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go index f477f0c38f..2ba27c7ef7 100644 --- a/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go +++ b/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go @@ -5,7 +5,6 @@ import ( "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/openstack/clustering/v1/webhooks/testing/requests_test.go b/openstack/clustering/v1/webhooks/testing/requests_test.go index 9d0da6f335..4a519fdf6a 100644 --- a/openstack/clustering/v1/webhooks/testing/requests_test.go +++ b/openstack/clustering/v1/webhooks/testing/requests_test.go @@ -1,12 +1,11 @@ package testing import ( + "encoding/json" "fmt" "net/http" "testing" - "encoding/json" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go index c0955c5ca9..b30d6eae38 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go +++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go @@ -121,7 +121,7 @@ var FirstQuotaSet = quotasets.QuotaSet{ // FirstQuotaDetailsSet is the first result in ListOutput. var FirstQuotaDetailsSet = quotasets.QuotaDetailSet{ - ID: FirstTenantID, + ID: FirstTenantID, InjectedFileContentBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10240}, InjectedFilePathBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 255}, InjectedFiles: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 5}, diff --git a/openstack/loadbalancer/v2/amphorae/testing/fixtures.go b/openstack/loadbalancer/v2/amphorae/testing/fixtures.go index c5ed496c7b..45744cf6bb 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/fixtures.go +++ b/openstack/loadbalancer/v2/amphorae/testing/fixtures.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "testing" - "time" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index 8730d8c286..6b54c4850f 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -5,13 +5,12 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) // LoadbalancersListBody contains the canned body of a loadbalancer list response. diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go index 2bc949b926..4a72d2dfa2 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go @@ -5,13 +5,12 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) // LoadbalancersListBody contains the canned body of a loadbalancer list response. diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index b3ec548da7..9c3b08f120 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -74,13 +74,13 @@ func TestCreate(t *testing.T) { IKEVersion: "v2", TenantID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", - Lifetime: expectedLifetime, - ProjectID: "9145d91459d248b1b02fdaca97c6a75d", + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + Lifetime: expectedLifetime, + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", } th.AssertDeepEquals(t, expected, *actual) } @@ -130,12 +130,12 @@ func TestGet(t *testing.T) { TenantID: "9145d91459d248b1b02fdaca97c6a75d", ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", - Lifetime: expectedLifetime, + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Lifetime: expectedLifetime, } th.AssertDeepEquals(t, expected, *actual) } @@ -208,12 +208,12 @@ func TestList(t *testing.T) { TenantID: "9145d91459d248b1b02fdaca97c6a75d", ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", - Lifetime: expectedLifetime, + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Lifetime: expectedLifetime, }, } From 5be3e9ba56a95f5e8cad3fd6a9a550e70798b1ac Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Fri, 12 Oct 2018 18:54:52 -0700 Subject: [PATCH 0572/2296] Senlin: Profiles Validate (#1248) * senlin-profile-validate * goimports format fix --- .../openstack/clustering/v1/profiles_test.go | 27 ++++++++ openstack/clustering/v1/profiles/doc.go | 25 +++++++ openstack/clustering/v1/profiles/requests.go | 34 ++++++++++ openstack/clustering/v1/profiles/results.go | 5 ++ .../v1/profiles/testing/fixtures.go | 68 +++++++++++++++++++ .../v1/profiles/testing/requests_test.go | 31 +++++++++ openstack/clustering/v1/profiles/urls.go | 4 ++ 7 files changed, 194 insertions(+) diff --git a/acceptance/openstack/clustering/v1/profiles_test.go b/acceptance/openstack/clustering/v1/profiles_test.go index f426f83232..9a7986b8bc 100644 --- a/acceptance/openstack/clustering/v1/profiles_test.go +++ b/acceptance/openstack/clustering/v1/profiles_test.go @@ -47,3 +47,30 @@ func TestProfilesCRUD(t *testing.T) { tools.PrintResource(t, newProfile) tools.PrintResource(t, newProfile.UpdatedAt) } + +func TestProfileValidate(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.2" + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + opts := profiles.ValidateOpts{ + Spec: profile.Spec, + } + validatedProfile, err := profiles.Validate(client, opts).Extract() + th.AssertNoErr(t, err) + + // Do not validate the following fields for AssertDeepEquals() because the actual fields are either missing or hardcoded. + profile.CreatedAt = validatedProfile.CreatedAt + profile.Domain = validatedProfile.Domain + profile.ID = validatedProfile.ID + profile.Metadata = validatedProfile.Metadata + profile.Name = "validated_profile" + profile.UpdatedAt = validatedProfile.UpdatedAt + + th.AssertDeepEquals(t, validatedProfile, profile) + tools.PrintResource(t, validatedProfile) +} diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index 13676df8c1..4e27eb8861 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -81,5 +81,30 @@ Example to Delete a Profile panic(err) } +Example to Validate a profile + + serviceClient.Microversion = "1.2" + + validateOpts := profiles.ValidateOpts{ + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.micro", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + } + + profile, err := profiles.Validate(serviceClient, validateOpts).Extract() + if err != nil { + panic(err) + } + */ package profiles diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index 5fb10f8159..7d48ddafd7 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -137,3 +137,37 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { } return } + +// ValidateOptsBuilder allows extensions to add additional parameters to the +// Validate request. +type ValidateOptsBuilder interface { + ToProfileValidateMap() (map[string]interface{}, error) +} + +// ValidateOpts params +type ValidateOpts struct { + Spec Spec `json:"spec" required:"true"` +} + +// ToProfileValidateMap formats a CreateOpts into a body map. +func (opts ValidateOpts) ToProfileValidateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "profile") +} + +// Validate profile. +func Validate(client *gophercloud.ServiceClient, opts ValidateOpts) (r ValidateResult) { + b, err := opts.ToProfileValidateMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index f1a3770dae..8ce15bad82 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -140,6 +140,11 @@ type DeleteResult struct { gophercloud.ErrResult } +// ValidateResult is the response of a Validate operations. +type ValidateResult struct { + commonResult +} + // ProfilePage contains a single page of all profiles from a List operation. type ProfilePage struct { pagination.LinkedPageBase diff --git a/openstack/clustering/v1/profiles/testing/fixtures.go b/openstack/clustering/v1/profiles/testing/fixtures.go index f989fc3ac5..590f69ed46 100644 --- a/openstack/clustering/v1/profiles/testing/fixtures.go +++ b/openstack/clustering/v1/profiles/testing/fixtures.go @@ -336,6 +336,61 @@ var ExpectedUpdate = profiles.Profile{ User: "5e5bf8027826429c96af157f68dc9072", } +const ValidateResponse = ` +{ + "profile": { + "created_at": "2016-01-03T16:22:23Z", + "domain": null, + "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + "metadata": {}, + "name": "pserver", + "project": "42d9e9663331431f97b75e25136307ff", + "spec": { + "properties": { + "flavor": "t2.micro", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": [ + { + "network": "private" + } + ] + }, + "type": "os.nova.server", + "version": "1.0" + }, + "type": "os.nova.server-1.0", + "updated_at": "2016-01-03T17:22:23Z", + "user": "5e5bf8027826429c96af157f68dc9072" + } +}` + +var ExpectedValidate = profiles.Profile{ + CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), + Domain: "", + ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", + Metadata: map[string]interface{}{}, + Name: "pserver", + Project: "42d9e9663331431f97b75e25136307ff", + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.micro", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + Type: "os.nova.server-1.0", + UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), + User: "5e5bf8027826429c96af157f68dc9072", +} + func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -393,3 +448,16 @@ func HandleDeleteSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleValidateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/profiles/validate", 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, "OpenStack-API-Version", "clustering 1.2") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ValidateResponse) + }) +} diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go index 1793c4bf1f..8ddde9233c 100644 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -107,3 +107,34 @@ func TestDeleteProfile(t *testing.T) { deleteResult := profiles.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } + +func TestValidateProfile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleValidateSuccessfully(t) + + validateOpts := profiles.ValidateOpts{ + Spec: profiles.Spec{ + Properties: map[string]interface{}{ + "flavor": "t2.micro", + "image": "cirros-0.3.4-x86_64-uec", + "key_name": "oskey", + "name": "cirros_server", + "networks": []interface{}{ + map[string]interface{}{"network": "private"}, + }, + }, + Type: "os.nova.server", + Version: "1.0", + }, + } + + client := fake.ServiceClient() + client.Microversion = "1.2" + client.Type = "clustering" + + profile, err := profiles.Validate(client, validateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedValidate, *profile) +} diff --git a/openstack/clustering/v1/profiles/urls.go b/openstack/clustering/v1/profiles/urls.go index 060d91ddc2..5a3d56aa52 100644 --- a/openstack/clustering/v1/profiles/urls.go +++ b/openstack/clustering/v1/profiles/urls.go @@ -32,3 +32,7 @@ func updateURL(client *gophercloud.ServiceClient, id string) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func validateURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName, "validate") +} From ce12fa916e6e7b0f6b1045bef1949c02e813c6a7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 13 Oct 2018 03:00:04 +0000 Subject: [PATCH 0573/2296] Networking v2: Allow an LBaaS v2 member weight of 0 --- .../openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go | 2 +- .../networking/v2/extensions/lbaas_v2/loadbalancers_test.go | 2 +- openstack/networking/v2/extensions/lbaas_v2/pools/doc.go | 6 ++++-- .../networking/v2/extensions/lbaas_v2/pools/requests.go | 4 ++-- .../v2/extensions/lbaas_v2/pools/testing/requests_test.go | 6 ++++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index dc00327323..7e26c8dd94 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -97,7 +97,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan createOpts := pools.CreateMemberOpts{ Name: memberName, ProtocolPort: memberPort, - Weight: memberWeight, + Weight: &memberWeight, Address: memberAddress, SubnetID: subnetID, } 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 71889682ae..770e495ec4 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -101,7 +101,7 @@ func TestLoadbalancersCRUD(t *testing.T) { newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ - Weight: newWeight, + Weight: &newWeight, } _, err = pools.UpdateMember(client, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go b/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go index 2d57ed4393..ce74fda82d 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go @@ -83,12 +83,13 @@ Example to Create a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + weight := 10 createOpts := pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", Address: "10.0.2.11", ProtocolPort: 80, - Weight: 10, + Weight: &weight, } member, err := pools.CreateMember(networkClient, poolID, createOpts).Extract() @@ -101,9 +102,10 @@ Example to Update a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" + weight := 4 updateOpts := pools.UpdateMemberOpts{ Name: "new-name", - Weight: 4, + Weight: &weight, } member, err := pools.UpdateMember(networkClient, poolID, memberID, updateOpts).Extract() diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index 11564be83f..472b9dd30c 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -274,7 +274,7 @@ type CreateMemberOpts struct { // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a // weight of 2. - Weight int `json:"weight,omitempty"` + Weight *int `json:"weight,omitempty"` // If you omit this parameter, LBaaS uses the vip_subnet_id parameter value // for the subnet UUID. @@ -323,7 +323,7 @@ type UpdateMemberOpts struct { // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a // weight of 2. - Weight int `json:"weight,omitempty"` + Weight *int `json:"weight,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go index 4af00ecc25..ee6a7d8a2a 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go @@ -189,13 +189,14 @@ func TestCreateMember(t *testing.T) { defer th.TeardownHTTP() HandleMemberCreationSuccessfully(t, SingleMemberBody) + weight := 10 actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", TenantID: "2ffc6e22aae24e4795f87155d24c896f", Address: "10.0.2.11", ProtocolPort: 80, - Weight: 10, + Weight: &weight, }).Extract() th.AssertNoErr(t, err) @@ -249,10 +250,11 @@ func TestUpdateMember(t *testing.T) { defer th.TeardownHTTP() HandleMemberUpdateSuccessfully(t) + weight := 4 client := fake.ServiceClient() actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ Name: "newMemberName", - Weight: 4, + Weight: &weight, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From 0c3339d7f89721f28eab8c8e68acde8e7b0defb3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 13 Oct 2018 03:07:35 +0000 Subject: [PATCH 0574/2296] Octavia v2: Allow a member weight of 0 --- .../openstack/loadbalancer/v2/loadbalancer.go | 2 +- .../openstack/loadbalancer/v2/loadbalancers_test.go | 6 +++--- openstack/loadbalancer/v2/pools/doc.go | 13 +++++++++---- openstack/loadbalancer/v2/pools/requests.go | 4 ++-- .../loadbalancer/v2/pools/testing/requests_test.go | 13 +++++++++---- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index c15dd068e4..eb2452e356 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -93,7 +93,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan createOpts := pools.CreateMemberOpts{ Name: memberName, ProtocolPort: memberPort, - Weight: memberWeight, + Weight: &memberWeight, Address: memberAddress, SubnetID: subnetID, } diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 54d5698a7e..8dd24d3028 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -217,7 +217,7 @@ func TestLoadbalancersCRUD(t *testing.T) { newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ - Weight: newWeight, + Weight: &newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() if err != nil { @@ -239,7 +239,7 @@ func TestLoadbalancersCRUD(t *testing.T) { memberOpts := pools.BatchUpdateMemberOpts{ Address: member.Address, ProtocolPort: member.ProtocolPort, - Weight: newWeight, + Weight: &newWeight, } batchMembers := []pools.BatchUpdateMemberOpts{memberOpts} if err := pools.BatchUpdateMembers(lbClient, pool.ID, batchMembers).ExtractErr(); err != nil { @@ -383,7 +383,7 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ - Weight: newWeight, + Weight: &newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() if err != nil { diff --git a/openstack/loadbalancer/v2/pools/doc.go b/openstack/loadbalancer/v2/pools/doc.go index 0135e60d7b..d26de312cb 100644 --- a/openstack/loadbalancer/v2/pools/doc.go +++ b/openstack/loadbalancer/v2/pools/doc.go @@ -83,12 +83,13 @@ Example to Create a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + weight := 10 createOpts := pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", Address: "10.0.2.11", ProtocolPort: 80, - Weight: 10, + Weight: &weight, } member, err := pools.CreateMember(networkClient, poolID, createOpts).Extract() @@ -101,9 +102,10 @@ Example to Update a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" + weight := 4 updateOpts := pools.UpdateMemberOpts{ Name: "new-name", - Weight: 4, + Weight: &weight, } member, err := pools.UpdateMember(networkClient, poolID, memberID, updateOpts).Extract() @@ -125,18 +127,21 @@ Example to Update Members: poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + weight_1 := 20 member1 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.16", ProtocolPort: 80, Name: "web-server-1", SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", - Weight: 20, + Weight: &weight_1, } + + weight_2 := 10 member2 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.17", ProtocolPort: 80, Name: "web-server-2", - Weight: 10, + Weight: &weight_2, SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", } members := []pools.BatchUpdateMemberOpts{member1, member2} diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index a95000f384..6eb8fcba06 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -265,7 +265,7 @@ type CreateMemberOpts struct { // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a // weight of 2. - Weight int `json:"weight,omitempty"` + Weight *int `json:"weight,omitempty"` // If you omit this parameter, LBaaS uses the vip_subnet_id parameter value // for the subnet UUID. @@ -314,7 +314,7 @@ type UpdateMemberOpts struct { // that this member should receive from the pool. For example, a member with // a weight of 10 receives five times as much traffic as a member with a // weight of 2. - Weight int `json:"weight,omitempty"` + Weight *int `json:"weight,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go index 4afc89020f..48dd2c0007 100644 --- a/openstack/loadbalancer/v2/pools/testing/requests_test.go +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -189,13 +189,14 @@ func TestCreateMember(t *testing.T) { defer th.TeardownHTTP() HandleMemberCreationSuccessfully(t, SingleMemberBody) + weight := 10 actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", ProjectID: "2ffc6e22aae24e4795f87155d24c896f", Address: "10.0.2.11", ProtocolPort: 80, - Weight: 10, + Weight: &weight, }).Extract() th.AssertNoErr(t, err) @@ -249,10 +250,11 @@ func TestUpdateMember(t *testing.T) { defer th.TeardownHTTP() HandleMemberUpdateSuccessfully(t) + weight := 4 client := fake.ServiceClient() actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ Name: "newMemberName", - Weight: 4, + Weight: &weight, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) @@ -266,18 +268,21 @@ func TestBatchUpdateMembers(t *testing.T) { defer th.TeardownHTTP() HandleMembersUpdateSuccessfully(t) + weight_1 := 20 member1 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.16", ProtocolPort: 80, Name: "web-server-1", SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", - Weight: 20, + Weight: &weight_1, } + + weight_2 := 10 member2 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.17", ProtocolPort: 80, Name: "web-server-2", - Weight: 10, + Weight: &weight_2, SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", } members := []pools.BatchUpdateMemberOpts{member1, member2} From 758d0f7adbdc69ffc4dfc36387602a791b80dfa3 Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Thu, 11 Oct 2018 22:58:52 +0200 Subject: [PATCH 0575/2296] Networking v2 Trunk support - AddSubports This patch adds the Networking extension trunk support for the AddSubports operation. Signed-off-by: Antoni Segura Puimedon --- .../v2/extensions/trunks/trunks_test.go | 69 +++++++++++++++++++ .../networking/v2/extensions/trunks/doc.go | 23 +++++++ .../v2/extensions/trunks/requests.go | 24 +++++++ .../v2/extensions/trunks/results.go | 11 +++ .../v2/extensions/trunks/testing/fixtures.go | 42 +++++++++++ .../trunks/testing/requests_test.go | 28 ++++++++ .../networking/v2/extensions/trunks/urls.go | 4 ++ 7 files changed, 201 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 8655ae4601..10bbb64c25 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -106,3 +106,72 @@ func TestTrunkList(t *testing.T) { tools.PrintResource(t, trunk) } } + +func TestTrunkSubportOperation(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // 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 := CreateTrunk(t, client, parentPort.ID) + if err != nil { + t.Fatalf("Unable to create trunk: %v", err) + } + defer DeleteTrunk(t, client, trunk.ID) + + // Add subports to the trunk + addSubportsOpts := trunks.AddSubportsOpts{ + Subports: []trunks.Subport{ + { + SegmentationID: 1, + SegmentationType: "vlan", + PortID: subport1.ID, + }, + { + SegmentationID: 11, + SegmentationType: "vlan", + PortID: subport2.ID, + }, + }, + } + updatedTrunk, err := trunks.AddSubports(client, trunk.ID, addSubportsOpts).Extract() + if err != nil { + t.Fatalf("Unable to add subports to the Trunk: %v", err) + } + th.AssertEquals(t, 2, len(updatedTrunk.Subports)) + th.AssertDeepEquals(t, addSubportsOpts.Subports[0], updatedTrunk.Subports[0]) + th.AssertDeepEquals(t, addSubportsOpts.Subports[1], updatedTrunk.Subports[1]) +} diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 4aa3154bd5..7f9ecbd7d1 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -101,5 +101,28 @@ Example of showing subports of a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" subports, err := trunks.GetSubports(client, trunkID).Extract() fmt.Printf("%+v\n", subports) + +Example of adding two subports to a Trunk + + trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" + addSubportsOpts := trunks.AddSubportsOpts{ + Subports: []trunks.Subport{ + { + SegmentationID: 1, + SegmentationType: "vlan", + PortID: "bf4efcc0-b1c7-4674-81f0-31f58a33420a", + }, + { + SegmentationID: 10, + SegmentationType: "vlan", + PortID: "2cf671b9-02b3-4121-9e85-e0af3548d112", + }, + }, + } + trunk, err := trunks.AddSubports(client, trunkID, addSubportsOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", trunk) */ package trunks diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index fa5fca01c4..75b4e6bbef 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -141,3 +141,27 @@ func GetSubports(c *gophercloud.ServiceClient, id string) (r GetSubportsResult) }) return } + +type AddSubportsOpts struct { + Subports []Subport `json:"sub_ports" required:"true"` +} + +type AddSubportsOptsBuilder interface { + ToTrunkAddSubportsMap() (map[string]interface{}, error) +} + +func (opts AddSubportsOpts) ToTrunkAddSubportsMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +func AddSubports(c *gophercloud.ServiceClient, id string, opts AddSubportsOptsBuilder) (r UpdateSubportsResult) { + body, err := opts.ToTrunkAddSubportsMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(addSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index 31153c8265..6d979ef7a2 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -47,6 +47,12 @@ type GetSubportsResult struct { commonResult } +// UpdateSubportsResult is the result of either an AddSubports or a RemoveSubports +// request. Call its Extract method to interpret it as a Trunk. +type UpdateSubportsResult struct { + commonResult +} + type Trunk struct { // Indicates whether the trunk is currently operational. Possible values include // `ACTIVE', `DOWN', `BUILD', 'DEGRADED' or `ERROR'. @@ -124,3 +130,8 @@ func (r GetSubportsResult) Extract() ([]Subport, error) { err := r.ExtractInto(&s) return s.Subports, err } + +func (r UpdateSubportsResult) Extract() (t *Trunk, err error) { + err = r.ExtractInto(&t) + return +} diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures.go index fa16a58154..89ac26cb11 100644 --- a/openstack/networking/v2/extensions/trunks/testing/fixtures.go +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures.go @@ -226,6 +226,36 @@ const ListSubportsResponse = ` ] }` +const AddSubportsRequest = ListSubportsResponse + +const AddSubportsResponse = ` +{ + "admin_state_up": true, + "created_at": "2018-10-03T13:57:24Z", + "description": "Trunk created by gophercloud", + "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", + "name": "gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "project_id": "e153f3f9082240a5974f667cfe1036e3", + "revision_number": 2, + "status": "ACTIVE", + "sub_ports": [ + { + "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", + "segmentation_id": 1, + "segmentation_type": "vlan" + }, + { + "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab", + "segmentation_id": 2, + "segmentation_type": "vlan" + } + ], + "tags": [], + "tenant_id": "e153f3f9082240a5974f667cfe1036e3", + "updated_at": "2018-10-03T13:57:30Z" +}` + var ExpectedSubports = []trunks.Subport{ { PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", @@ -298,3 +328,15 @@ func ExpectedTrunkSlice() (exp []trunks.Trunk, err error) { } return } + +func ExpectedSubportsAddedTrunk() (exp trunks.Trunk, err error) { + trunkUpdatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:30Z") + expectedTrunks, err := ExpectedTrunkSlice() + if err != nil { + return + } + exp = expectedTrunks[1] + exp.RevisionNumber += 1 + exp.UpdatedAt = trunkUpdatedAt + return +} diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index 5c5c07d0a2..d98fb132a4 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -244,3 +244,31 @@ func TestMissingFields(t *testing.T) { t.Fatalf("Failed to detect missing subport fields") } } + +func TestAddSubports(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c/add_subports", 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, AddSubportsRequest) + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, AddSubportsResponse) + }) + + client := fake.ServiceClient() + + opts := trunks.AddSubportsOpts{ + Subports: ExpectedSubports, + } + + trunk, err := trunks.AddSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c", opts).Extract() + th.AssertNoErr(t, err) + expectedTrunk, err := ExpectedSubportsAddedTrunk() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expectedTrunk, trunk) +} diff --git a/openstack/networking/v2/extensions/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go index cf180450d1..cf39bfc63d 100644 --- a/openstack/networking/v2/extensions/trunks/urls.go +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -35,3 +35,7 @@ func updateURL(c *gophercloud.ServiceClient, id string) string { func getSubportsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "get_subports") } + +func addSubportsURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "add_subports") +} From a6115359664cd46359ff2f8b589826d1983e5b96 Mon Sep 17 00:00:00 2001 From: Nathan Castelein Date: Sun, 14 Oct 2018 04:16:50 +0200 Subject: [PATCH 0576/2296] Handle filters on list parameters (#1281) * Handle filters on list parameters * Fix WorkflowName filter for cron triggers --- acceptance/openstack/workflow/v2/workflow.go | 3 + .../openstack/workflow/v2/workflows_test.go | 10 +- openstack/workflow/v2/crontriggers/doc.go | 8 +- .../workflow/v2/crontriggers/requests.go | 3 +- .../v2/crontriggers/testing/requests_test.go | 6 + openstack/workflow/v2/workflows/requests.go | 113 ++++++++++++++++-- .../v2/workflows/testing/requests_test.go | 29 +++++ 7 files changed, 155 insertions(+), 17 deletions(-) diff --git a/acceptance/openstack/workflow/v2/workflow.go b/acceptance/openstack/workflow/v2/workflow.go index b0120d7b55..de95d0ca60 100644 --- a/acceptance/openstack/workflow/v2/workflow.go +++ b/acceptance/openstack/workflow/v2/workflow.go @@ -19,6 +19,9 @@ version: '2.0' %s: description: Simple workflow example type: direct + tags: + - tag1 + - tag2 input: - msg diff --git a/acceptance/openstack/workflow/v2/workflows_test.go b/acceptance/openstack/workflow/v2/workflows_test.go index f0de6708cd..a5fdde6413 100644 --- a/acceptance/openstack/workflow/v2/workflows_test.go +++ b/acceptance/openstack/workflow/v2/workflows_test.go @@ -2,6 +2,7 @@ package v2 import ( "testing" + "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" @@ -30,7 +31,14 @@ func TestWorkflowsList(t *testing.T) { th.AssertNoErr(t, err) defer DeleteWorkflow(t, client, workflow) list, err := ListWorkflows(t, client, &workflows.ListOpts{ - Name: workflow.Name, + Name: &workflows.ListFilter{ + Value: workflow.Name, + }, + Tags: []string{"tag1"}, + CreatedAt: &workflows.ListDateFilter{ + Filter: workflows.FilterGT, + Value: time.Now().AddDate(-1, 0, 0), + }, }) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(list)) diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go index f000450cf5..36e36d4a61 100644 --- a/openstack/workflow/v2/crontriggers/doc.go +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -37,10 +37,10 @@ Default Filter checks equality, but you can override it with provided filter typ Create a cron trigger. This example will start the workflow "echo" each day at 8am, and it will end after 10 executions. createOpts := &crontriggers.CreateOpts{ - Name: "daily", - Pattern: "0 8 * * *", - WorkflowName: "echo", - RemainingExecutions: 10, + Name: "daily", + Pattern: "0 8 * * *", + WorkflowName: "echo", + RemainingExecutions: 10, WorkflowParams: map[string]interface{}{ "msg": "hello", }, diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index 63a800d9bd..2d9ee99f5e 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -92,7 +92,7 @@ type ListOptsBuilder interface { // ListOpts filters the result returned by the List() function. type ListOpts struct { // WorkflowName allows to filter by workflow name. - WorkflowName string `q:"workflow_name"` + WorkflowName *ListFilter `q:"-"` // WorkflowID allows to filter by workflow id. WorkflowID string `q:"workflow_id"` // WorkflowInput allows to filter by specific workflow inputs. @@ -220,6 +220,7 @@ func (opts ListOpts) ToCronTriggerListQuery() (string, error) { } for queryParam, value := range map[string]fmt.Stringer{ + "workflow_name": opts.WorkflowName, "name": opts.Name, "pattern": opts.Pattern, "remaining_executions": opts.RemainingExecutions, diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index 77e27745cf..ef911406eb 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -256,6 +256,12 @@ func TestToExecutionListQuery(t *testing.T) { Value: "not_name", }, }, + newValue("workflow_name", `eq:workflow`): &crontriggers.ListOpts{ + WorkflowName: &crontriggers.ListFilter{ + Filter: crontriggers.FilterEQ, + Value: "workflow", + }, + }, newValue("created_at", `gt:2018-01-01 00:00:00`): &crontriggers.ListOpts{ CreatedAt: &crontriggers.ListDateFilter{ Filter: crontriggers.FilterGT, diff --git a/openstack/workflow/v2/workflows/requests.go b/openstack/workflow/v2/workflows/requests.go index a703fa66a9..d077c7da0b 100644 --- a/openstack/workflow/v2/workflows/requests.go +++ b/openstack/workflow/v2/workflows/requests.go @@ -1,7 +1,12 @@ package workflows import ( + "fmt" "io" + "net/url" + "reflect" + "strings" + "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" @@ -76,32 +81,118 @@ type ListOptsBuilder interface { // ListOpts filters the result returned by the List() function. type ListOpts struct { - // Name allows to filter by workflow name. - Name string `q:"name"` - // Namespace allows to filter by workflow namespace. - Namespace string `q:"namespace"` - // Definition allows to filter by workflow definition. - Definition string `q:"definition"` // Scope filters by the workflow's scope. // Values can be "private" or "public". Scope string `q:"scope"` - // SortDir allows to select sort direction. + // CreatedAt allows to filter by workflow creation date. + CreatedAt *ListDateFilter `q:"-"` + // UpdatedAt allows to filter by last execution update date. + UpdatedAt *ListDateFilter `q:"-"` + // Name allows to filter by workflow name. + Name *ListFilter `q:"-"` + // Tags allows to filter by tags. + Tags []string + // Definition allows to filter by workflow definition. + Definition *ListFilter `q:"-"` + // Namespace allows to filter by workflow namespace. + Namespace *ListFilter `q:"-"` + // SortDirs allows to select sort direction. // It can be "asc" or "desc" (default). - SortDir string `q:"sort_dir"` - // SortKey allows to sort by one of the cron trigger attributes. - SortKey string `q:"sort_key"` + SortDirs string `q:"sort_dirs"` + // SortKeys allows to sort by one of the cron trigger attributes. + SortKeys string `q:"sort_keys"` // Marker and Limit control paging. // Marker instructs List where to start listing from. Marker string `q:"marker"` // Limit instructs List to refrain from sending excessively large lists of // cron triggers. Limit int `q:"limit"` + // ProjectID allows to filter by given project id. Admin required. + ProjectID string `q:"project_id"` + // AllProjects requests to get executions of all projects. Admin required. + AllProjects int `q:"all_projects"` +} + +// ListFilter allows to filter string parameters with different filters. +// Empty value for Filter checks for equality. +type ListFilter struct { + Filter FilterType + Value string +} + +func (l ListFilter) String() string { + if l.Filter != "" { + return fmt.Sprintf("%s:%s", l.Filter, l.Value) + } + return l.Value +} + +// ListDateFilter allows to filter date parameters with different filters. +// Empty value for Filter checks for equality. +type ListDateFilter struct { + Filter FilterType + Value time.Time } +func (l ListDateFilter) String() string { + v := l.Value.Format(gophercloud.RFC3339ZNoTNoZ) + if l.Filter != "" { + return fmt.Sprintf("%s:%s", l.Filter, v) + } + return v +} + +// FilterType represents a valid filter to use for filtering executions. +type FilterType string + +const ( + // FilterEQ checks equality. + FilterEQ = "eq" + // FilterNEQ checks non equality. + FilterNEQ = "neq" + // FilterIN checks for belonging in a list, comma separated. + FilterIN = "in" + // FilterNIN checks for values that does not belong from a list, comma separated. + FilterNIN = "nin" + // FilterGT checks for values strictly greater. + FilterGT = "gt" + // FilterGTE checks for values greater or equal. + FilterGTE = "gte" + // FilterLT checks for values strictly lower. + FilterLT = "lt" + // FilterLTE checks for values lower or equal. + FilterLTE = "lte" + // FilterHas checks for values that contains the requested parameter. + FilterHas = "has" +) + // ToWorkflowListQuery formats a ListOpts into a query string. func (opts ListOpts) ToWorkflowListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) - return q.String(), err + if err != nil { + return "", err + } + params := q.Query() + + if opts.Tags != nil { + params.Add("tags", strings.Join(opts.Tags, ",")) + } + + for queryParam, value := range map[string]fmt.Stringer{ + "created_at": opts.CreatedAt, + "updated_at": opts.UpdatedAt, + "name": opts.Name, + "definition": opts.Definition, + "namespace": opts.Namespace, + } { + if !reflect.ValueOf(value).IsNil() { + params.Add(queryParam, value.String()) + } + } + + q = &url.URL{RawQuery: params.Encode()} + + return q.String(), nil } // List performs a call to list cron triggers. diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index 388e630a9a..f0c2755744 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -3,6 +3,7 @@ package testing import ( "fmt" "net/http" + "net/url" "reflect" "strings" "testing" @@ -225,3 +226,31 @@ func TestListWorkflows(t *testing.T) { t.Errorf("Expected one page, got %d", pages) } } + +func TestToWorkflowListQuery(t *testing.T) { + for expected, opts := range map[string]*workflows.ListOpts{ + newValue("tags", `tag1,tag2`): &workflows.ListOpts{ + Tags: []string{"tag1", "tag2"}, + }, + newValue("name", `neq:invalid_name`): &workflows.ListOpts{ + Name: &workflows.ListFilter{ + Filter: workflows.FilterNEQ, + Value: "invalid_name", + }, + }, + newValue("created_at", `gt:2018-01-01 00:00:00`): &workflows.ListOpts{ + CreatedAt: &workflows.ListDateFilter{ + Filter: workflows.FilterGT, + Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), + }, + }, + } { + actual, _ := opts.ToWorkflowListQuery() + th.AssertEquals(t, expected, actual) + } +} +func newValue(param, value string) string { + v := url.Values{} + v.Add(param, value) + return "?" + v.Encode() +} From c8947f7d1c5126259bed8769515ce34399db23ca Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 13 Oct 2018 22:34:07 -0600 Subject: [PATCH 0577/2296] Network v2: Fix Upper Limit of IPs (#1273) * Network v2: Fix Upper Limit of IPs The networkipavailabilities extension requires an increase to the upper limit for both Total IPs and Used IPs. This is to accommodate a large amount of IPv6 addresses. This commit changes the type of these fields from int to big.Int. * Using string type instead of big.Int --- .../networkipavailabilities_test.go | 6 ++- .../networkipavailabilities/results.go | 51 +++++++++++++++++-- .../testing/fixtures.go | 36 ++++++------- .../testing/requests_test.go | 12 +++-- script/acceptancetest | 8 +-- 5 files changed, 81 insertions(+), 32 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go index 34eb9cbf98..cfebed0a30 100644 --- a/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go +++ b/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go @@ -22,6 +22,10 @@ func TestNetworkIPAvailabilityList(t *testing.T) { th.AssertNoErr(t, err) for _, availability := range allAvailabilities { - tools.PrintResource(t, availability) + for _, subnet := range availability.SubnetIPAvailabilities { + tools.PrintResource(t, subnet) + tools.PrintResource(t, subnet.TotalIPs) + tools.PrintResource(t, subnet.UsedIPs) + } } } diff --git a/openstack/networking/v2/extensions/networkipavailabilities/results.go b/openstack/networking/v2/extensions/networkipavailabilities/results.go index 4bcf10f49d..db62b73ca4 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/results.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/results.go @@ -1,6 +1,9 @@ package networkipavailabilities import ( + "encoding/json" + "math/big" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -43,10 +46,30 @@ type NetworkIPAvailability struct { SubnetIPAvailabilities []SubnetIPAvailability `json:"subnet_ip_availability"` // TotalIPs represents a number of IP addresses in the network. - TotalIPs int `json:"total_ips"` + TotalIPs string `json:"-"` // UsedIPs represents a number of used IP addresses in the network. - UsedIPs int `json:"used_ips"` + UsedIPs string `json:"-"` +} + +func (r *NetworkIPAvailability) UnmarshalJSON(b []byte) error { + type tmp NetworkIPAvailability + var s struct { + tmp + TotalIPs big.Int `json:"total_ips"` + UsedIPs big.Int `json:"used_ips"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = NetworkIPAvailability(s.tmp) + + r.TotalIPs = s.TotalIPs.String() + r.UsedIPs = s.UsedIPs.String() + + return err } // SubnetIPAvailability represents availability details for a single subnet. @@ -64,10 +87,30 @@ type SubnetIPAvailability struct { IPVersion int `json:"ip_version"` // TotalIPs represents a number of IP addresses in the subnet. - TotalIPs int `json:"total_ips"` + TotalIPs string `json:"-"` // UsedIPs represents a number of used IP addresses in the subnet. - UsedIPs int `json:"used_ips"` + UsedIPs string `json:"-"` +} + +func (r *SubnetIPAvailability) UnmarshalJSON(b []byte) error { + type tmp SubnetIPAvailability + var s struct { + tmp + TotalIPs big.Int `json:"total_ips"` + UsedIPs big.Int `json:"used_ips"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = SubnetIPAvailability(s.tmp) + + r.TotalIPs = s.TotalIPs.String() + r.UsedIPs = s.UsedIPs.String() + + return err } // NetworkIPAvailabilityPage stores a single page of NetworkIPAvailabilities diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go index 2cd993d6bd..6a88f39bbe 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures.go @@ -15,12 +15,12 @@ const NetworkIPAvailabilityListResult = ` "project_id": "fb57277ef2f84a0e85b9018ec2dedbf7", "subnet_ip_availability": [ { - "cidr": "10.0.0.64/26", - "ip_version": 4, + "cidr": "fdbc:bf53:567e::/64", + "ip_version": 6, "subnet_id": "497ac4d3-0b92-42cf-82de-71302ab2b656", - "subnet_name": "second-private-subnet", - "total_ips": 61, - "used_ips": 12 + "subnet_name": "ipv6-private-subnet", + "total_ips": 18446744073709552000, + "used_ips": 2 }, { "cidr": "10.0.0.0/26", @@ -63,24 +63,24 @@ var NetworkIPAvailability1 = networkipavailabilities.NetworkIPAvailability{ NetworkName: "private", ProjectID: "fb57277ef2f84a0e85b9018ec2dedbf7", TenantID: "fb57277ef2f84a0e85b9018ec2dedbf7", - TotalIPs: 122, - UsedIPs: 14, + TotalIPs: "122", + UsedIPs: "14", SubnetIPAvailabilities: []networkipavailabilities.SubnetIPAvailability{ { SubnetID: "497ac4d3-0b92-42cf-82de-71302ab2b656", - SubnetName: "second-private-subnet", - CIDR: "10.0.0.64/26", - IPVersion: int(gophercloud.IPv4), - TotalIPs: 61, - UsedIPs: 12, + SubnetName: "ipv6-private-subnet", + CIDR: "fdbc:bf53:567e::/64", + IPVersion: int(gophercloud.IPv6), + TotalIPs: "18446744073709552000", + UsedIPs: "2", }, { SubnetID: "521f47e7-c4fb-452c-b71a-851da38cc571", SubnetName: "private-subnet", CIDR: "10.0.0.0/26", IPVersion: int(gophercloud.IPv4), - TotalIPs: 61, - UsedIPs: 2, + TotalIPs: "61", + UsedIPs: "2", }, }, } @@ -91,16 +91,16 @@ var NetworkIPAvailability2 = networkipavailabilities.NetworkIPAvailability{ NetworkName: "public", ProjectID: "424e7cf0243c468ca61732ba45973b3e", TenantID: "424e7cf0243c468ca61732ba45973b3e", - TotalIPs: 253, - UsedIPs: 3, + TotalIPs: "253", + UsedIPs: "3", SubnetIPAvailabilities: []networkipavailabilities.SubnetIPAvailability{ { SubnetID: "4afe6e5f-9649-40db-b18f-64c7ead942bd", SubnetName: "public-subnet", CIDR: "203.0.113.0/24", IPVersion: int(gophercloud.IPv4), - TotalIPs: 253, - UsedIPs: 3, + TotalIPs: "253", + UsedIPs: "3", }, }, } diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go index 820debb2fb..1b352e2a18 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go @@ -28,7 +28,7 @@ func TestList(t *testing.T) { count := 0 - networkipavailabilities.List(fake.ServiceClient(), networkipavailabilities.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := networkipavailabilities.List(fake.ServiceClient(), networkipavailabilities.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := networkipavailabilities.ExtractNetworkIPAvailabilities(page) if err != nil { @@ -46,6 +46,8 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) + if count != 1 { t.Errorf("Expected 1 page, got %d", count) } @@ -72,16 +74,16 @@ func TestGet(t *testing.T) { th.AssertEquals(t, s.NetworkName, "public") th.AssertEquals(t, s.ProjectID, "424e7cf0243c468ca61732ba45973b3e") th.AssertEquals(t, s.TenantID, "424e7cf0243c468ca61732ba45973b3e") - th.AssertEquals(t, s.TotalIPs, 253) - th.AssertEquals(t, s.UsedIPs, 3) + th.AssertEquals(t, s.TotalIPs, "253") + th.AssertEquals(t, s.UsedIPs, "3") th.AssertDeepEquals(t, s.SubnetIPAvailabilities, []networkipavailabilities.SubnetIPAvailability{ { SubnetID: "4afe6e5f-9649-40db-b18f-64c7ead942bd", SubnetName: "public-subnet", CIDR: "203.0.113.0/24", IPVersion: int(gophercloud.IPv4), - TotalIPs: 253, - UsedIPs: 3, + TotalIPs: "253", + UsedIPs: "3", }, }) } diff --git a/script/acceptancetest b/script/acceptancetest index ae30151b28..b0391289c0 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -36,10 +36,10 @@ fi # Networking v2 - networkipavailabilities # requires bug fix -#go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/networkipavailabilities -#if [[ $? != 0 ]]; then -# failed=1 -#fi +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/networkipavailabilities +if [[ $? != 0 ]]; then + failed=1 +fi # Networking v2 - portsbinding go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/portsbinding From b5258c144a42d24428b51961d35a73c1bd40bea4 Mon Sep 17 00:00:00 2001 From: Tim Clark Date: Mon, 15 Oct 2018 13:28:27 -0400 Subject: [PATCH 0578/2296] Fix lbaas_v2 pools docs Corrects errant reference to Monitors Signed-off-by: Tim Clark --- openstack/networking/v2/extensions/lbaas_v2/pools/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go b/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go index 2d57ed4393..a16ecab529 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go @@ -13,7 +13,7 @@ Example to List Pools panic(err) } - allPools, err := pools.ExtractMonitors(allPages) + allPools, err := pools.ExtractPools(allPages) if err != nil { panic(err) } From 65e0de33115b6f24444bd279c1dea3375261e116 Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Tue, 16 Oct 2018 03:51:00 +0200 Subject: [PATCH 0579/2296] Networking v2 Trunk support - RemoveSubports (#1295) This patch adds the Networking extension trunk support for the RemoveSubports operation. Signed-off-by: Antoni Segura Puimedon --- .../v2/extensions/trunks/trunks_test.go | 13 ++++++ .../networking/v2/extensions/trunks/doc.go | 15 +++++++ .../v2/extensions/trunks/requests.go | 28 +++++++++++++ .../v2/extensions/trunks/testing/fixtures.go | 42 +++++++++++++++++++ .../trunks/testing/requests_test.go | 31 ++++++++++++++ .../networking/v2/extensions/trunks/urls.go | 4 ++ 6 files changed, 133 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 10bbb64c25..4a5802ecc9 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -174,4 +174,17 @@ func TestTrunkSubportOperation(t *testing.T) { th.AssertEquals(t, 2, len(updatedTrunk.Subports)) th.AssertDeepEquals(t, addSubportsOpts.Subports[0], updatedTrunk.Subports[0]) th.AssertDeepEquals(t, addSubportsOpts.Subports[1], updatedTrunk.Subports[1]) + + // Remove the Subports from the trunk + subRemoveOpts := trunks.RemoveSubportsOpts{ + Subports: []trunks.RemoveSubport{ + {PortID: subport1.ID}, + {PortID: subport2.ID}, + }, + } + updatedAgainTrunk, err := trunks.RemoveSubports(client, trunk.ID, subRemoveOpts).Extract() + if err != nil { + t.Fatalf("Unable to remove subports from the Trunk: %v", err) + } + th.AssertDeepEquals(t, trunk.Subports, updatedAgainTrunk.Subports) } diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 7f9ecbd7d1..82496ffd0b 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -124,5 +124,20 @@ Example of adding two subports to a Trunk panic(err) } fmt.Printf("%+v\n", trunk) + +Example of deleting two subports from a Trunk + + trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" + removeSubportsOpts := trunks.RemoveSubportsOpts{ + Subports: []trunks.RemoveSubport{ + {PortID: "bf4efcc0-b1c7-4674-81f0-31f58a33420a"}, + {PortID: "2cf671b9-02b3-4121-9e85-e0af3548d112"}, + }, + } + trunk, err := trunks.RemoveSubports(networkClient, trunkID, removeSubportsOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", trunk) */ package trunks diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index 75b4e6bbef..575a20cf4b 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -165,3 +165,31 @@ func AddSubports(c *gophercloud.ServiceClient, id string, opts AddSubportsOptsBu }) return } + +type RemoveSubport struct { + PortID string `json:"port_id" required:"true"` +} + +type RemoveSubportsOpts struct { + Subports []RemoveSubport `json:"sub_ports"` +} + +type RemoveSubportsOptsBuilder interface { + ToTrunkRemoveSubportsMap() (map[string]interface{}, error) +} + +func (opts RemoveSubportsOpts) ToTrunkRemoveSubportsMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +func RemoveSubports(c *gophercloud.ServiceClient, id string, opts RemoveSubportsOptsBuilder) (r UpdateSubportsResult) { + body, err := opts.ToTrunkRemoveSubportsMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(removeSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures.go index 89ac26cb11..b84c5a5736 100644 --- a/openstack/networking/v2/extensions/trunks/testing/fixtures.go +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures.go @@ -256,6 +256,35 @@ const AddSubportsResponse = ` "updated_at": "2018-10-03T13:57:30Z" }` +const RemoveSubportsRequest = ` +{ + "sub_ports": [ + { + "port_id": "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b" + }, + { + "port_id": "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab" + } + ] +}` + +const RemoveSubportsResponse = ` +{ + "admin_state_up": true, + "created_at": "2018-10-03T13:57:24Z", + "description": "Trunk created by gophercloud", + "id": "f6a9718c-5a64-43e3-944f-4deccad8e78c", + "name": "gophertrunk", + "port_id": "c373d2fa-3d3b-4492-924c-aff54dea19b6", + "project_id": "e153f3f9082240a5974f667cfe1036e3", + "revision_number": 2, + "status": "ACTIVE", + "sub_ports": [], + "tags": [], + "tenant_id": "e153f3f9082240a5974f667cfe1036e3", + "updated_at": "2018-10-03T13:57:27Z" +}` + var ExpectedSubports = []trunks.Subport{ { PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b", @@ -340,3 +369,16 @@ func ExpectedSubportsAddedTrunk() (exp trunks.Trunk, err error) { exp.UpdatedAt = trunkUpdatedAt return } + +func ExpectedSubportsRemovedTrunk() (exp trunks.Trunk, err error) { + trunkUpdatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:27Z") + expectedTrunks, err := ExpectedTrunkSlice() + if err != nil { + return + } + exp = expectedTrunks[1] + exp.RevisionNumber += 1 + exp.UpdatedAt = trunkUpdatedAt + exp.Subports = []trunks.Subport{} + return +} diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index d98fb132a4..595da01036 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -272,3 +272,34 @@ func TestAddSubports(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expectedTrunk, trunk) } + +func TestRemoveSubports(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/trunks/f6a9718c-5a64-43e3-944f-4deccad8e78c/remove_subports", 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, RemoveSubportsRequest) + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, RemoveSubportsResponse) + }) + + client := fake.ServiceClient() + + opts := trunks.RemoveSubportsOpts{ + Subports: []trunks.RemoveSubport{ + {PortID: "28e452d7-4f8a-4be4-b1e6-7f3db4c0430b"}, + {PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab"}, + }, + } + trunk, err := trunks.RemoveSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c", opts).Extract() + + th.AssertNoErr(t, err) + expectedTrunk, err := ExpectedSubportsRemovedTrunk() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expectedTrunk, trunk) +} diff --git a/openstack/networking/v2/extensions/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go index cf39bfc63d..ac7dff0961 100644 --- a/openstack/networking/v2/extensions/trunks/urls.go +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -39,3 +39,7 @@ func getSubportsURL(c *gophercloud.ServiceClient, id string) string { func addSubportsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "add_subports") } + +func removeSubportsURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "remove_subports") +} From e95a560a0afa70666022f4d4d773732fdeb77797 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Tue, 16 Oct 2018 11:44:20 +0100 Subject: [PATCH 0580/2296] Fix issues in attributestags acceptance test Discussion on #1286 highlighted some issues: * The order of the DeepEquals is wrong, which means if the actual result is an empty list no equivalence is tested * The DeepEquals can't be used to test the result of DeleteAll, for the same reason * On older OpenStack versions the tags aren't returned in a sorted order Closes Issue: #1299 --- .../networking/v2/extensions/attributestags_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index fabc86619f..5cfac495fc 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -3,6 +3,7 @@ package extensions import ( + "sort" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -21,13 +22,13 @@ func TestTags(t *testing.T) { defer networking.DeleteNetwork(t, client, network.ID) tagReplaceAllOpts := attributestags.ReplaceAllOpts{ - // Note Neutron returns tags sorted, and although the API - // docs say list of tags, it's a set e.g no duplicates + // docs say list of tags, but it's a set e.g no duplicates Tags: []string{"a", "b", "c"}, } tags, err := attributestags.ReplaceAll(client, "networks", network.ID, tagReplaceAllOpts).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, tags, []string{"a", "b", "c"}) + sort.Strings(tags) // Ensure ordering, older OpenStack versions aren't sorted... + th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) // Add a tag err = attributestags.Add(client, "networks", network.ID, "d").ExtractErr() @@ -40,12 +41,13 @@ func TestTags(t *testing.T) { // Verify expected tags are set in the List response tags, err = attributestags.List(client, "networks", network.ID).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, tags, []string{"b", "c", "d"}) + sort.Strings(tags) + th.AssertDeepEquals(t, []string{"b", "c", "d"}, tags) // Delete all tags err = attributestags.DeleteAll(client, "networks", network.ID).ExtractErr() th.AssertNoErr(t, err) tags, err = attributestags.List(client, "networks", network.ID).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, tags, []string{}) + th.AssertEquals(t, 0, len(tags)) } From 16ff7561fba7c67ae54e7d9a39f91be2c286a0b7 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Thu, 11 Oct 2018 13:58:16 +0000 Subject: [PATCH 0581/2296] Add Tags to resources that support std-attributes-tags Since we added the ability to set resource tags via the attributestags extension, it is helpful to expose those tags as they are included in the GET response for resources that support tagging ref: https://docs.openstack.org/neutron/pike/contributor/internals/tag.html Issue: #1285 --- .../networking/v2/extensions/attributestags_test.go | 8 ++++++++ .../v2/extensions/layer3/floatingips/results.go | 3 +++ .../networking/v2/extensions/layer3/routers/results.go | 3 +++ .../networking/v2/extensions/rbacpolicies/results.go | 3 +++ .../networking/v2/extensions/security/groups/results.go | 3 +++ openstack/networking/v2/extensions/subnetpools/results.go | 3 +++ openstack/networking/v2/networks/results.go | 3 +++ openstack/networking/v2/ports/results.go | 3 +++ openstack/networking/v2/subnets/results.go | 3 +++ 9 files changed, 32 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 5cfac495fc..00e875d4ce 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_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/openstack/networking/v2/extensions/attributestags" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -30,6 +31,13 @@ func TestTags(t *testing.T) { sort.Strings(tags) // Ensure ordering, older OpenStack versions aren't sorted... th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) + // Verify the tags are also set in the object Get response + gnetwork, err := networks.Get(client, network.ID).Extract() + th.AssertNoErr(t, err) + tags = gnetwork.Tags + sort.Strings(tags) + th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) + // Add a tag err = attributestags.Add(client, "networks", network.ID, "d").ExtractErr() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index 8b1a517645..e5a8edafb7 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -42,6 +42,9 @@ type FloatingIP struct { // RouterID is the ID of the router used for this floating IP. RouterID string `json:"router_id"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` } type commonResult struct { diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index dffdce8f48..17287acf53 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -67,6 +67,9 @@ type Router struct { // Availability zone hints groups network nodes that run services like DHCP, L3, FW, and others. // Used to make network resources highly available. AvailabilityZoneHints []string `json:"availability_zone_hints"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` } // RouterPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go index a62facc0dc..1327b17b99 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/results.go +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -69,6 +69,9 @@ type RBACPolicy struct { // ProjectID is the ID of the project. ProjectID string `json:"project_id"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` } // RBACPolicyPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go index 66915e6e55..468952b3e4 100644 --- a/openstack/networking/v2/extensions/security/groups/results.go +++ b/openstack/networking/v2/extensions/security/groups/results.go @@ -27,6 +27,9 @@ type SecGroup struct { // ProjectID is the project owner of the security group. ProjectID string `json:"project_id"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` } // SecGroupPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index e761eac44d..e97e1e60ae 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -111,6 +111,9 @@ type SubnetPool struct { // RevisionNumber is the revision number of the subnetpool. RevisionNumber int `json:"revision_number"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` } func (r *SubnetPool) UnmarshalJSON(b []byte) error { diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index 62f4b3c3a5..4d683d906d 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -76,6 +76,9 @@ type Network struct { // Availability zone hints groups network nodes that run services like DHCP, L3, FW, and others. // Used to make network resources highly available. AvailabilityZoneHints []string `json:"availability_zone_hints"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` } // NetworkPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index 66937fd989..ca7b49f400 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -101,6 +101,9 @@ type Port struct { // Identifies the list of IP addresses the port will recognize/accept AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` } // PortPage is the page returned by a pager when traversing over a collection diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index 493e5c042e..968cee1f4d 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -106,6 +106,9 @@ type Subnet struct { // SubnetPoolID is the id of the subnet pool associated with the subnet. SubnetPoolID string `json:"subnetpool_id"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` } // SubnetPage is the page returned by a pager when traversing over a collection From 8a6d8d414a29630aa35410be75949491c3dc7ce6 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Fri, 12 Oct 2018 16:37:36 +0000 Subject: [PATCH 0582/2296] Enable list query by tag for Neutron resources For resources that support std-attribute-tags we can query by tag so add the ability to do this, with an acceptance test showing the interface working with Network resources. Issue: #1289 --- .../v2/extensions/attributestags_test.go | 89 ++++++++++++++++--- .../extensions/layer3/floatingips/requests.go | 4 + .../v2/extensions/layer3/routers/requests.go | 4 + .../v2/extensions/rbacpolicies/requests.go | 4 + .../v2/extensions/security/groups/requests.go | 20 +++-- .../v2/extensions/subnetpools/requests.go | 4 + openstack/networking/v2/networks/requests.go | 4 + openstack/networking/v2/ports/requests.go | 4 + openstack/networking/v2/subnets/requests.go | 4 + 9 files changed, 116 insertions(+), 21 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 00e875d4ce..93b82f8099 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -3,40 +3,49 @@ package extensions import ( + "fmt" "sort" "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/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) -func TestTags(t *testing.T) { - client, err := clients.NewNetworkV2Client() - th.AssertNoErr(t, err) - +func createNetworkWithTags(t *testing.T, client *gophercloud.ServiceClient, tags []string) (network *networks.Network) { // Create Network network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) - defer networking.DeleteNetwork(t, client, network.ID) tagReplaceAllOpts := attributestags.ReplaceAllOpts{ // docs say list of tags, but it's a set e.g no duplicates - Tags: []string{"a", "b", "c"}, + Tags: tags, } - tags, err := attributestags.ReplaceAll(client, "networks", network.ID, tagReplaceAllOpts).Extract() + rtags, err := attributestags.ReplaceAll(client, "networks", network.ID, tagReplaceAllOpts).Extract() th.AssertNoErr(t, err) - sort.Strings(tags) // Ensure ordering, older OpenStack versions aren't sorted... - th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) + sort.Strings(rtags) // Ensure ordering, older OpenStack versions aren't sorted... + th.AssertDeepEquals(t, rtags, tags) // Verify the tags are also set in the object Get response gnetwork, err := networks.Get(client, network.ID).Extract() th.AssertNoErr(t, err) - tags = gnetwork.Tags - sort.Strings(tags) - th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) + rtags = gnetwork.Tags + sort.Strings(rtags) + th.AssertDeepEquals(t, rtags, tags) + return network +} + +func TestTags(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create Network + network := createNetworkWithTags(t, client, []string{"a", "b", "c"}) + defer networking.DeleteNetwork(t, client, network.ID) // Add a tag err = attributestags.Add(client, "networks", network.ID, "d").ExtractErr() @@ -47,7 +56,7 @@ func TestTags(t *testing.T) { th.AssertNoErr(t, err) // Verify expected tags are set in the List response - tags, err = attributestags.List(client, "networks", network.ID).Extract() + tags, err := attributestags.List(client, "networks", network.ID).Extract() th.AssertNoErr(t, err) sort.Strings(tags) th.AssertDeepEquals(t, []string{"b", "c", "d"}, tags) @@ -59,3 +68,57 @@ func TestTags(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(tags)) } + +func listNetworkWithTagOpts(t *testing.T, client *gophercloud.ServiceClient, listOpts networks.ListOpts) (ids []string) { + allPages, err := networks.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + allNetworks, err := networks.ExtractNetworks(allPages) + th.AssertNoErr(t, err) + for _, network := range allNetworks { + ids = append(ids, network.ID) + } + return ids +} + +func TestQueryByTags(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a random tag to ensure we only get networks created + // by this test + testtag := tools.RandomString("zzz-tag-", 8) + + // Create Networks + network1 := createNetworkWithTags( + t, client, []string{"a", "b", "c", testtag}) + defer networking.DeleteNetwork(t, client, network1.ID) + + network2 := createNetworkWithTags( + t, client, []string{"b", "c", "d", testtag}) + defer networking.DeleteNetwork(t, client, network2.ID) + + // Tags - Networks that match all tags will be returned + listOpts := networks.ListOpts{ + Tags: fmt.Sprintf("a,b,c,%s", testtag)} + ids := listNetworkWithTagOpts(t, client, listOpts) + th.AssertDeepEquals(t, []string{network1.ID}, ids) + + // TagsAny - Networks that match any tag will be returned + listOpts = networks.ListOpts{ + SortKey: "id", SortDir: "asc", + TagsAny: fmt.Sprintf("a,b,c,%s", testtag)} + ids = listNetworkWithTagOpts(t, client, listOpts) + expected_ids := []string{network1.ID, network2.ID} + sort.Strings(expected_ids) + th.AssertDeepEquals(t, expected_ids, ids) + + // NotTags - Networks that match all tags will be excluded + listOpts = networks.ListOpts{Tags: testtag, NotTags: "a,b,c"} + ids = listNetworkWithTagOpts(t, client, listOpts) + th.AssertDeepEquals(t, []string{network2.ID}, ids) + + // NotTagsAny - Networks that match any tag will be excluded. + listOpts = networks.ListOpts{Tags: testtag, NotTagsAny: "d"} + ids = listNetworkWithTagOpts(t, client, listOpts) + th.AssertDeepEquals(t, []string{network1.ID}, ids) +} diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index d82a1bc8e2..2d44adc75c 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -24,6 +24,10 @@ type ListOpts struct { SortDir string `q:"sort_dir"` RouterID string `q:"router_id"` Status string `q:"status"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` } // List returns a Pager which allows you to iterate over a collection of diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index 8b2bde530e..9c8bbbeb37 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -22,6 +22,10 @@ type ListOpts struct { 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"` } // List returns a Pager which allows you to iterate over a collection of diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index 532a2f23d2..e388e1a168 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -27,6 +27,10 @@ type ListOpts struct { 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"` } // ToRBACPolicyListQuery formats a ListOpts into a query string. diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index 2a46ce6aa0..c89ea63087 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -11,14 +11,18 @@ import ( // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` + ID string `q:"id"` + Name string `q:"name"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` } // List returns a Pager which allows you to iterate over a collection of diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index e7ee96aef6..c54813b85d 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -36,6 +36,10 @@ type ListOpts struct { 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"` } // ToSubnetPoolListQuery formats a ListOpts into a query string. diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 1fbf62092c..f49622031d 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -28,6 +28,10 @@ type ListOpts struct { 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"` } // ToNetworkListQuery formats a ListOpts into a query string. diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 2a5902d75d..d519d7699b 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -31,6 +31,10 @@ type ListOpts struct { 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"` } // ToPortListQuery formats a ListOpts into a query string. diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 2597947b18..4f460aee1a 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -33,6 +33,10 @@ type ListOpts struct { 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"` } // ToSubnetListQuery formats a ListOpts into a query string. From 1c8c149260404714c20c6e86f33c4de84fc4463f Mon Sep 17 00:00:00 2001 From: Antoni Segura Puimedon Date: Wed, 17 Oct 2018 12:28:53 +0200 Subject: [PATCH 0583/2296] Networking V2 - Trunk tags testing Signed-off-by: Antoni Segura Puimedon --- .../v2/extensions/trunks/trunks_test.go | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 4a5802ecc9..566272f0e1 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -3,11 +3,13 @@ package trunks import ( + "sort" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -188,3 +190,87 @@ func TestTrunkSubportOperation(t *testing.T) { } th.AssertDeepEquals(t, trunk.Subports, updatedAgainTrunk.Subports) } + +func TestTrunkTags(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // 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 := CreateTrunk(t, client, parentPort.ID, subport1.ID, subport2.ID) + if err != nil { + t.Fatalf("Unable to create trunk: %v", err) + } + defer DeleteTrunk(t, client, trunk.ID) + + tagReplaceAllOpts := attributestags.ReplaceAllOpts{ + // docs say list of tags, but it's a set e.g no duplicates + Tags: []string{"a", "b", "c"}, + } + tags, err := attributestags.ReplaceAll(client, "trunks", trunk.ID, tagReplaceAllOpts).Extract() + if err != nil { + t.Fatalf("Unable to set trunk tags: %v", err) + } + + gtrunk, err := trunks.Get(client, trunk.ID).Extract() + if err != nil { + t.Fatalf("Unable to get trunk: %v", err) + } + tags = gtrunk.Tags + sort.Strings(tags) // Ensure ordering, older OpenStack versions aren't sorted... + th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) + + // Add a tag + err = attributestags.Add(client, "trunks", trunk.ID, "d").ExtractErr() + th.AssertNoErr(t, err) + + // Delete a tag + err = attributestags.Delete(client, "trunks", trunk.ID, "a").ExtractErr() + th.AssertNoErr(t, err) + + // Verify expected tags are set in the List response + tags, err = attributestags.List(client, "trunks", trunk.ID).Extract() + th.AssertNoErr(t, err) + sort.Strings(tags) + th.AssertDeepEquals(t, []string{"b", "c", "d"}, tags) + + // Delete all tags + err = attributestags.DeleteAll(client, "trunks", trunk.ID).ExtractErr() + th.AssertNoErr(t, err) + tags, err = attributestags.List(client, "trunks", trunk.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(tags)) +} From 257c4deb52735d833bcd41931eb6687e9ff7e81d Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Tue, 9 Oct 2018 16:37:00 +0000 Subject: [PATCH 0584/2296] Neutron attributestags Confirm tag support As described in the API docs: https://developer.openstack.org/api-ref/network/v2/#standard-attributes-tag-extension https://developer.openstack.org/api-ref/network/v2/#confirm-a-tag Issue: #1260 --- .../v2/extensions/attributestags_test.go | 7 ++++ .../v2/extensions/attributestags/doc.go | 4 +++ .../v2/extensions/attributestags/requests.go | 9 +++++ .../v2/extensions/attributestags/results.go | 17 ++++++++++ .../attributestags/testing/requests_test.go | 33 +++++++++++++++++++ .../v2/extensions/attributestags/urls.go | 4 +++ 6 files changed, 74 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 00e875d4ce..d8de48a910 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -52,6 +52,13 @@ func TestTags(t *testing.T) { sort.Strings(tags) th.AssertDeepEquals(t, []string{"b", "c", "d"}, tags) + // Confirm tags exist/don't exist + exists, err := attributestags.Confirm(client, "networks", network.ID, "d").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, exists) + noexists, err := attributestags.Confirm(client, "networks", network.ID, "a").Extract() + th.AssertEquals(t, false, noexists) + // Delete all tags err = attributestags.DeleteAll(client, "networks", network.ID).ExtractErr() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/attributestags/doc.go b/openstack/networking/v2/extensions/attributestags/doc.go index 137893a6c0..3257dd1bad 100644 --- a/openstack/networking/v2/extensions/attributestags/doc.go +++ b/openstack/networking/v2/extensions/attributestags/doc.go @@ -29,5 +29,9 @@ Example to Add a tag to a Resource Example to Delete a tag from a Resource err = attributestags.Delete(client, "networks", network.ID, "atag").ExtractErr() + +Example to confirm if a tag exists on a resource + + exists, _ := attributestags.Confirm(client, "networks", network.ID, "atag").Extract() */ package attributestags diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index 6e4e1f14db..a08bccbb68 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -70,3 +70,12 @@ func Delete(client *gophercloud.ServiceClient, resourceType string, resourceID s }) return } + +// Confirm if a tag exists on a resource +func Confirm(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r ConfirmResult) { + url := confirmURL(client, resourceType, resourceID, tag) + _, r.Err = client.Get(url, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/networking/v2/extensions/attributestags/results.go b/openstack/networking/v2/extensions/attributestags/results.go index fe618ba8fa..cea8045beb 100644 --- a/openstack/networking/v2/extensions/attributestags/results.go +++ b/openstack/networking/v2/extensions/attributestags/results.go @@ -38,3 +38,20 @@ type DeleteResult struct { type AddResult struct { gophercloud.ErrResult } + +// ConfirmResult is the result from an Confirm operation. +type ConfirmResult struct { + gophercloud.Result +} + +func (r ConfirmResult) Extract() (bool, error) { + exists := r.Err == nil + + if r.Err != nil { + if _, ok := r.Err.(gophercloud.ErrDefault404); ok { + r.Err = nil + } + } + + return exists, r.Err +} diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go index 4fcda67a9a..4a005faf76 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -103,3 +103,36 @@ func TestDelete(t *testing.T) { err := attributestags.Delete(fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() th.AssertNoErr(t, err) } + +func TestConfirmTrue(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/fakeid/tags/atag", 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.StatusNoContent) + }) + + exists, err := attributestags.Confirm(fake.ServiceClient(), "networks", "fakeid", "atag").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, exists) +} + +func TestConfirmFalse(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/fakeid/tags/atag", 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.StatusNotFound) + }) + + exists, _ := attributestags.Confirm(fake.ServiceClient(), "networks", "fakeid", "atag").Extract() + th.AssertEquals(t, false, exists) +} diff --git a/openstack/networking/v2/extensions/attributestags/urls.go b/openstack/networking/v2/extensions/attributestags/urls.go index f11caf7842..973e00c47c 100644 --- a/openstack/networking/v2/extensions/attributestags/urls.go +++ b/openstack/networking/v2/extensions/attributestags/urls.go @@ -25,3 +25,7 @@ func addURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) func deleteURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) string { return c.ServiceURL(r_type, id, tagsPath, tag) } + +func confirmURL(c *gophercloud.ServiceClient, r_type string, id string, tag string) string { + return c.ServiceURL(r_type, id, tagsPath, tag) +} From 40d9330c0dee401b1805f332c1fd867d7a6843cc Mon Sep 17 00:00:00 2001 From: Jack Kuei Date: Fri, 23 Mar 2018 14:02:00 -0700 Subject: [PATCH 0585/2296] senlin-clusters-addnodes --- .../openstack/clustering/v1/clusters_test.go | 53 +++++++++++++++++++ openstack/clustering/v1/clusters/doc.go | 12 +++++ openstack/clustering/v1/clusters/requests.go | 23 ++++++++ .../v1/clusters/testing/fixtures.go | 13 +++++ .../v1/clusters/testing/requests_test.go | 14 +++++ openstack/clustering/v1/clusters/urls.go | 4 ++ 6 files changed, 119 insertions(+) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index ba759b9bd6..7835ecdf65 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -3,6 +3,7 @@ package v1 import ( + "sort" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -265,3 +266,55 @@ func TestClustersRecovery(t *testing.T) { tools.PrintResource(t, newCluster) } + +func TestClustersAddNode(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + node1, err := CreateNode(t, client, "", profile.ID) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node1.ID) + + node2, err := CreateNode(t, client, "", profile.ID) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node2.ID) + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + + nodeIDs := []string{node1.ID, node2.ID} + nodeIDs = append(nodeIDs, cluster.Nodes...) + + nodeNames := []string{node1.Name, node2.Name} + addNodesOpts := clusters.AddNodesOpts{ + Nodes: nodeNames, + } + actionID, err := clusters.AddNodes(client, cluster.ID, addNodesOpts).Extract() + if err != nil { + t.Fatalf("Unable to add nodes to cluster: %v", err) + } + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + + sort.Strings(nodeIDs) + sort.Strings(cluster.Nodes) + + tools.PrintResource(t, nodeIDs) + tools.PrintResource(t, cluster.Nodes) + + th.AssertDeepEquals(t, nodeIDs, cluster.Nodes) + + tools.PrintResource(t, cluster) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 94d6d60873..9e29f9a54b 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -223,5 +223,17 @@ Example to Complete Life Cycle panic(err) } +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) + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 2c97e202fc..a792f68bb9 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -523,3 +523,26 @@ func CompleteLifecycle(client *gophercloud.ServiceClient, id string, opts Comple return } + +func (opts AddNodesOpts) ToClusterNodeMap(nodeAction string) (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, nodeAction) +} + +// NodeOpts params +type AddNodesOpts struct { + Nodes []string `json:"nodes" required:"true"` +} + +func AddNodes(client *gophercloud.ServiceClient, id string, opts AddNodesOpts) (r ActionResult) { + b, err := opts.ToClusterNodeMap("add_nodes") + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + r.Header = result.Header + return +} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 26c84821c7..16ff20bff0 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -622,3 +622,16 @@ func HandleLifecycleSuccessfully(t *testing.T) { fmt.Fprint(w, ActionResponse) }) } + +func HandleAddNodesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprint(w, ActionResponse) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index ba4f05a3a3..775ef2e45f 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -426,3 +426,17 @@ func TestLifecycle(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, "2a0ff107-e789-4660-a122-3816c43af703", actionID) } + +func TestAddNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleAddNodesSuccessfully(t) + + opts := clusters.AddNodesOpts{ + Nodes: []string{"node1", "node2", "node3"}, + } + result, err := clusters.AddNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index a4e0db0c1f..c8d2664d06 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -44,3 +44,7 @@ func listPoliciesURL(client *gophercloud.ServiceClient, clusterID string) string func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID string) string { return client.ServiceURL(apiVersion, apiName, clusterID, "policies", policyID) } + +func nodeURL(client *gophercloud.ServiceClient, id string) string { + return actionURL(client, id) +} From cbd1d4bdbfa53deb18191f576456b4ae3e3662fb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 25 Oct 2018 10:41:08 -0600 Subject: [PATCH 0586/2296] Core: Fix Unchecked Type Assertion (#1305) This commit fixes an unchecked type assertion when extracting results into a slice pointer. The type assertion would previously fail when a paged result was empty. --- .../extensions/volumetenants_test.go | 46 +++++++++++++++ results.go | 58 ++++++++++--------- 2 files changed, 76 insertions(+), 28 deletions(-) create mode 100644 acceptance/openstack/blockstorage/extensions/volumetenants_test.go diff --git a/acceptance/openstack/blockstorage/extensions/volumetenants_test.go b/acceptance/openstack/blockstorage/extensions/volumetenants_test.go new file mode 100644 index 0000000000..5396880c55 --- /dev/null +++ b/acceptance/openstack/blockstorage/extensions/volumetenants_test.go @@ -0,0 +1,46 @@ +// +build acceptance blockstorage + +package extensions + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + blockstorage "github.com/gophercloud/gophercloud/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" +) + +func TestVolumeTenants(t *testing.T) { + type volumeWithTenant struct { + volumes.Volume + volumetenants.VolumeTenantExt + } + + var allVolumes []volumeWithTenant + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + listOpts := volumes.ListOpts{ + Name: "I SHOULD NOT EXIST", + } + allPages, err := volumes.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(allVolumes)) + + volume1, err := blockstorage.CreateVolume(t, client) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, client, volume1) + + allPages, err = volumes.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + th.AssertNoErr(t, err) + th.AssertEquals(t, true, len(allVolumes) > 0) +} diff --git a/results.go b/results.go index 30d3b15599..94a16bff0b 100644 --- a/results.go +++ b/results.go @@ -90,38 +90,40 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) - for _, v := range m[label].([]interface{}) { - // For each iteration of the slice, we create a new struct. - // This is to work around a bug where elements of a slice - // are reused and not overwritten when the same copy of the - // struct is used: - // - // https://github.com/golang/go/issues/21092 - // https://github.com/golang/go/issues/24155 - // https://play.golang.org/p/NHo3ywlPZli - newType := reflect.New(typeOfV).Elem() - - b, err := json.Marshal(v) - if err != nil { - return err - } - - // This is needed for structs with an UnmarshalJSON method. - // Technically this is just unmarshalling the response into - // a struct that is never used, but it's good enough to - // trigger the UnmarshalJSON method. - for i := 0; i < newType.NumField(); i++ { - s := newType.Field(i).Addr().Interface() - - // Unmarshal is used rather than NewDecoder to also work - // around the above-mentioned bug. - err = json.Unmarshal(b, s) + if mSlice, ok := m[label].([]interface{}); ok { + for _, v := range mSlice { + // For each iteration of the slice, we create a new struct. + // This is to work around a bug where elements of a slice + // are reused and not overwritten when the same copy of the + // struct is used: + // + // https://github.com/golang/go/issues/21092 + // https://github.com/golang/go/issues/24155 + // https://play.golang.org/p/NHo3ywlPZli + newType := reflect.New(typeOfV).Elem() + + b, err := json.Marshal(v) if err != nil { return err } - } - newSlice = reflect.Append(newSlice, newType) + // This is needed for structs with an UnmarshalJSON method. + // Technically this is just unmarshalling the response into + // a struct that is never used, but it's good enough to + // trigger the UnmarshalJSON method. + for i := 0; i < newType.NumField(); i++ { + s := newType.Field(i).Addr().Interface() + + // Unmarshal is used rather than NewDecoder to also work + // around the above-mentioned bug. + err = json.Unmarshal(b, s) + if err != nil { + return err + } + } + + newSlice = reflect.Append(newSlice, newType) + } } // "to" should now be properly modeled to receive the From 7bd760e8eb1d7c94108db464cc992df1620278d1 Mon Sep 17 00:00:00 2001 From: wayne Date: Mon, 29 Oct 2018 09:40:26 -0500 Subject: [PATCH 0587/2296] Blockstorage v2, v3: Implement cascading volume deletion (#1311) For #1309 For proof this is a valid feature see the following lines of Cinder code: * https://github.com/openstack/cinder/blob/31a885c046a28f795cfc1e83ec5ceca4ef005301/cinder/api/v2/volumes.py#L71 * https://github.com/openstack/cinder/blob/3ef65dfaf3e9f8932c5bbe8cddb1a2a40855f625/cinder/volume/api.py#L379 * https://github.com/openstack/cinder/blob/3ef65dfaf3e9f8932c5bbe8cddb1a2a40855f625/cinder/volume/api.py#L479 * https://developer.openstack.org/api-ref/block-storage/v3/index.html?expanded=delete-a-volume-detail#delete-a-volume --- .../extensions/schedulerhints_test.go | 4 +- .../extensions/volumeactions_test.go | 2 +- .../blockstorage/noauth/blockstorage.go | 2 +- .../openstack/blockstorage/v2/blockstorage.go | 2 +- .../openstack/blockstorage/v2/volumes_test.go | 53 ++++++++++++++++++ .../openstack/blockstorage/v3/blockstorage.go | 2 +- .../openstack/blockstorage/v3/volumes_test.go | 55 ++++++++++++++++++- openstack/blockstorage/v2/volumes/requests.go | 32 ++++++++++- .../v2/volumes/testing/requests_test.go | 2 +- openstack/blockstorage/v3/volumes/requests.go | 32 ++++++++++- .../v3/volumes/testing/requests_test.go | 2 +- 11 files changed, 175 insertions(+), 13 deletions(-) diff --git a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go index 19c1b8a80d..bfb99bb070 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -29,7 +29,7 @@ func TestSchedulerHints(t *testing.T) { err = volumes.WaitForStatus(client, volume1.ID, "available", 60) th.AssertNoErr(t, err) - defer volumes.Delete(client, volume1.ID) + defer volumes.Delete(client, volume1.ID, volumes.DeleteOpts{}) volumeName = tools.RandomString("ACPTTEST", 16) base := volumes.CreateOpts{ @@ -54,6 +54,6 @@ func TestSchedulerHints(t *testing.T) { err = volumes.WaitForStatus(client, volume2.ID, "available", 60) th.AssertNoErr(t, err) - err = volumes.Delete(client, volume2.ID).ExtractErr() + err = volumes.Delete(client, volume2.ID, volumes.DeleteOpts{}).ExtractErr() th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 53d1f4fb65..42fb8888d4 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -108,7 +108,7 @@ func TestVolumeConns(t *testing.T) { th.AssertNoErr(t, err) t.Logf("Deleting volume") - err = volumes.Delete(client, cv.ID).ExtractErr() + err = volumes.Delete(client, cv.ID, volumes.DeleteOpts{}).ExtractErr() th.AssertNoErr(t, err) }() diff --git a/acceptance/openstack/blockstorage/noauth/blockstorage.go b/acceptance/openstack/blockstorage/noauth/blockstorage.go index 9e8aeab9dc..d453dac94a 100644 --- a/acceptance/openstack/blockstorage/noauth/blockstorage.go +++ b/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -78,7 +78,7 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo // DeleteVolume will delete a volume. A fatal error will occur if the volume // failed to be deleted. This works best when used as a deferred function. func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { - err := volumes.Delete(client, volume.ID).ExtractErr() + err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } diff --git a/acceptance/openstack/blockstorage/v2/blockstorage.go b/acceptance/openstack/blockstorage/v2/blockstorage.go index 5b6ea75e06..b05ac230ad 100644 --- a/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -118,7 +118,7 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { t.Logf("Attempting to delete volume: %s", volume.ID) - err := volumes.Delete(client, volume.ID).ExtractErr() + err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go index 03f659f5e1..042f77a629 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -5,9 +5,11 @@ package v2 import ( "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/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" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -59,3 +61,54 @@ func TestVolumesCreateForceDestroy(t *testing.T) { err = volumeactions.ForceDelete(client, newVolume.ID).ExtractErr() th.AssertNoErr(t, err) } + +func TestVolumesCascadeDelete(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + vol, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, vol.ID, "available", 60) + th.AssertNoErr(t, err) + + snapshot1, err := CreateSnapshot(t, client, vol) + th.AssertNoErr(t, err) + + snapshot2, err := CreateSnapshot(t, client, vol) + th.AssertNoErr(t, err) + + t.Logf("Attempting to delete volume: %s", vol.ID) + + deleteOpts := volumes.DeleteOpts{Cascade: true} + err = volumes.Delete(client, vol.ID, deleteOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", vol.ID, err) + } + + for _, sid := range []string{snapshot1.ID, snapshot2.ID} { + err := gophercloud.WaitFor(120, func() (bool, error) { + _, err := snapshots.Get(client, sid).Extract() + if err != nil { + return true, nil + } + return false, nil + }) + th.AssertNoErr(t, err) + t.Logf("Successfully deleted snapshot: %s", sid) + } + + err = gophercloud.WaitFor(120, func() (bool, error) { + _, err := volumes.Get(client, vol.ID).Extract() + if err != nil { + return true, nil + } + return false, nil + }) + th.AssertNoErr(t, err) + + t.Logf("Successfully deleted volume: %s", vol.ID) + +} diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index aee4fcea01..b842a54327 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -130,7 +130,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { t.Logf("Attempting to delete volume: %s", volume.ID) - err := volumes.Delete(client, volume.ID).ExtractErr() + err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index 0cb7b8b8ec..edbcd93ce9 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -5,8 +5,10 @@ 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/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -73,6 +75,57 @@ func TestVolumesMultiAttach(t *testing.T) { th.AssertEquals(t, vol.Multiattach, true) - err = volumes.Delete(client, vol.ID).ExtractErr() + err = volumes.Delete(client, vol.ID, volumes.DeleteOpts{}).ExtractErr() th.AssertNoErr(t, err) } + +func TestVolumesCascadeDelete(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + vol, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + + err = volumes.WaitForStatus(client, vol.ID, "available", 60) + th.AssertNoErr(t, err) + + snapshot1, err := CreateSnapshot(t, client, vol) + th.AssertNoErr(t, err) + + snapshot2, err := CreateSnapshot(t, client, vol) + th.AssertNoErr(t, err) + + t.Logf("Attempting to delete volume: %s", vol.ID) + + deleteOpts := volumes.DeleteOpts{Cascade: true} + err = volumes.Delete(client, vol.ID, deleteOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete volume %s: %v", vol.ID, err) + } + + for _, sid := range []string{snapshot1.ID, snapshot2.ID} { + err := gophercloud.WaitFor(120, func() (bool, error) { + _, err := snapshots.Get(client, sid).Extract() + if err != nil { + return true, nil + } + return false, nil + }) + th.AssertNoErr(t, err) + t.Logf("Successfully deleted snapshot: %s", sid) + } + + err = gophercloud.WaitFor(120, func() (bool, error) { + _, err := volumes.Get(client, vol.ID).Extract() + if err != nil { + return true, nil + } + return false, nil + }) + th.AssertNoErr(t, err) + + t.Logf("Successfully deleted volume: %s", vol.ID) + +} diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index b7977cbc8f..5549fb07d1 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -61,9 +61,37 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// DeleteOptsBuilder allows extensions to add additional parameters to the +// Delete request. +type DeleteOptsBuilder interface { + ToVolumeDeleteQuery() (string, error) +} + +// DeleteOpts contains options for deleting a Volume. This object is passed to +// the volumes.Delete function. +type DeleteOpts struct { + // Delete all snapshots of this volume as well. + Cascade bool `q:"cascade"` +} + +// ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. +func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // Delete will delete the existing Volume with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) +func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { + url := deleteURL(client, id) + if opts != nil { + query, err := opts.ToVolumeDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + _, r.Err = client.Delete(url, nil) return } diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index cd7dcf5ecb..723094f6c8 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -220,7 +220,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", volumes.DeleteOpts{}) th.AssertNoErr(t, res.Err) } diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 4c8dca707b..8ae160fca8 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -63,9 +63,37 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// DeleteOptsBuilder allows extensions to add additional parameters to the +// Delete request. +type DeleteOptsBuilder interface { + ToVolumeDeleteQuery() (string, error) +} + +// DeleteOpts contains options for deleting a Volume. This object is passed to +// the volumes.Delete function. +type DeleteOpts struct { + // Delete all snapshots of this volume as well. + Cascade bool `q:"cascade"` +} + +// ToLoadBalancerDeleteQuery formats a DeleteOpts into a query string. +func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // Delete will delete the existing Volume with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) +func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { + url := deleteURL(client, id) + if opts != nil { + query, err := opts.ToVolumeDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + _, r.Err = client.Delete(url, nil) return } diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index 5a1e46b653..834c9280f1 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -220,7 +220,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", volumes.DeleteOpts{}) th.AssertNoErr(t, res.Err) } From e2ee3bb2803ac88f8fb7be5858014172c67ed992 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Tue, 30 Oct 2018 18:00:34 +0800 Subject: [PATCH 0588/2296] Enable neutron trunk extension in integration tests Related-Bugs: theopenlab/openlab-zuul-jobs#341 Closes: theopenlab/openlab#92 --- .zuul/playbooks/gophercloud-acceptance-test/run.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index ce8635a440..26ed039213 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -9,6 +9,7 @@ - 'designate' - 'zun' - 'octavia' + - 'neutron-ext' - install-devstack tasks: - name: Run acceptance tests with gophercloud From c0a73c840c35fc20fd77cafbf2a3c5bcee72c31d Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Wed, 31 Oct 2018 04:19:58 +0800 Subject: [PATCH 0589/2296] Support acceptance test against OpenStack Rocky release (#1238) Input comment "recheck stable/rocky" in pull request comments, that will trigger acceptance tests against OpenStack Rocky release. OpenLab will report test result in followint comments automatically. Related-Bug: theopenlab/openlab#82 --- .zuul.yaml | 12 ++++++++++++ acceptance/openstack/compute/v2/quotaset_test.go | 4 ++++ acceptance/openstack/identity/v3/roles_test.go | 2 ++ .../v2/extensions/layer3/floatingips_test.go | 10 +++++----- .../networking/v2/extensions/layer3/routers_test.go | 11 ++++------- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 92fd9d4826..8c31ea160e 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -22,6 +22,15 @@ global_env: OS_BRANCH: stable/queens +- job: + name: gophercloud-acceptance-test-rocky + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on rocky branch + vars: + global_env: + OS_BRANCH: stable/rocky + - job: name: gophercloud-acceptance-test-pike parent: gophercloud-acceptance-test @@ -80,6 +89,9 @@ recheck-queens: jobs: - gophercloud-acceptance-test-queens + recheck-rocky: + jobs: + - gophercloud-acceptance-test-rocky periodic: jobs: - gophercloud-unittest diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go index 867d3fb55c..8ed9372627 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -17,6 +17,8 @@ import ( func TestQuotasetGet(t *testing.T) { clients.SkipRelease(t, "master") + clients.SkipRelease(t, "stable/queens") + clients.SkipRelease(t, "stable/rocky") client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) @@ -103,6 +105,8 @@ var UpdatedQuotas = quotasets.QuotaSet{ func TestQuotasetUpdateDelete(t *testing.T) { clients.SkipRelease(t, "master") + clients.SkipRelease(t, "stable/queens") + clients.SkipRelease(t, "stable/rocky") clients.RequireAdmin(t) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 9affedf3fb..37003aa17f 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -114,6 +114,8 @@ func TestRolesFilterList(t *testing.T) { // For some reason this is not longer working. // It might be a temporary issue. clients.SkipRelease(t, "master") + clients.SkipRelease(t, "stable/queens") + clients.SkipRelease(t, "stable/rocky") client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index 5e4ba0134a..2d1f8d28c0 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -9,7 +9,6 @@ import ( networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -47,7 +46,6 @@ func TestLayer3FloatingIPsCreateDelete(t *testing.T) { } func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { - clients.SkipRelease(t, "master") clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() @@ -56,10 +54,12 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - netid, err := networks.IDFromName(client, choices.NetworkName) + // Create Network + network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) - subnet, err := networking.CreateSubnet(t, client, netid) + subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) @@ -67,7 +67,7 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteRouter(t, client, router.ID) - port, err := networking.CreatePort(t, client, netid, subnet.ID) + port, err := networking.CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) _, err = CreateRouterInterface(t, client, port.ID, router.ID) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index bc90404ec9..3801c091c4 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -9,7 +9,6 @@ import ( networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -84,19 +83,17 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { } func TestLayer3RouterInterface(t *testing.T) { - clients.SkipRelease(t, "master") clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - netid, err := networks.IDFromName(client, choices.NetworkName) + // Create Network + network, err := networking.CreateNetwork(t, client) th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) - subnet, err := networking.CreateSubnet(t, client, netid) + subnet, err := networking.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, client, subnet.ID) From dfac4f80d03d689384c3388539c6353545fc1ed4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 1 Nov 2018 02:24:30 +0000 Subject: [PATCH 0590/2296] Acc Tests: Enable networking trunks --- script/acceptancetest | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/acceptancetest b/script/acceptancetest index b0391289c0..606f9e02d2 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -59,6 +59,12 @@ if [[ $? != 0 ]]; then failed=1 fi +# Networking v2 - trunks +go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/trunks +if [[ $? != 0 ]]; then + failed=1 +fi + # Block Storage v2 go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ if [[ $? != 0 ]]; then From aa00757ee3ab58e53520b6cb910ca0543116400a Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 1 Nov 2018 20:16:06 -0700 Subject: [PATCH 0591/2296] Senlin: Clusters RemoveNodes (#1310) * senlin-clusters-removenodes * Changed method naming to more explicitly names. From method names from ToClusterNodeMap() to ToClusterAddNodeMap() and ToClusterREmoveNodeMap() --- .../openstack/clustering/v1/clustering.go | 12 ++++-- .../openstack/clustering/v1/clusters_test.go | 41 +++++++++++++++++++ openstack/clustering/v1/clusters/doc.go | 11 +++++ openstack/clustering/v1/clusters/requests.go | 29 +++++++++++-- .../v1/clusters/testing/fixtures.go | 11 +++++ .../v1/clusters/testing/requests_test.go | 11 +++++ 6 files changed, 108 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go index 4122beb906..8201592d3f 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -317,12 +317,18 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete node: %s", id) - err := nodes.Delete(client, id).ExtractErr() + res := nodes.Delete(client, id) + if res.Err != nil { + t.Fatalf("Error deleting node %s: %s:", id, res.Err) + } + + actionID, err := GetActionID(res.Header) if err != nil { - t.Fatalf("Error deleting node %s: %s:", id, err) + t.Fatalf("Error getting actionID %s: %s:", id, err) } - err = WaitForNodeStatus(client, id, "DELETED") + err = WaitForAction(client, actionID) + if err != nil { t.Fatalf("Error deleting node %s: %s", id, err) } diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 7835ecdf65..803b5845c5 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -281,10 +281,12 @@ func TestClustersAddNode(t *testing.T) { node1, err := CreateNode(t, client, "", profile.ID) th.AssertNoErr(t, err) + // Even tho deleting the cluster will delete the nodes but only if added into cluster successfully. defer DeleteNode(t, client, node1.ID) node2, err := CreateNode(t, client, "", profile.ID) th.AssertNoErr(t, err) + // Even tho deleting the cluster will delete the nodes but only if added into cluster successfully. defer DeleteNode(t, client, node2.ID) cluster, err = clusters.Get(client, cluster.ID).Extract() @@ -318,3 +320,42 @@ func TestClustersAddNode(t *testing.T) { tools.PrintResource(t, cluster) } + +func TestClustersRemoveNodeFromCluster(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, cluster) + + opt := clusters.RemoveNodesOpts{Nodes: cluster.Nodes} + res := clusters.RemoveNodes(client, cluster.ID, opt) + err = res.ExtractErr() + th.AssertNoErr(t, err) + + for _, n := range cluster.Nodes { + defer DeleteNode(t, client, n) + } + + actionID, err := GetActionID(res.Header) + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, 0, len(cluster.Nodes)) + + tools.PrintResource(t, cluster) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 9e29f9a54b..7f018e51f0 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -235,5 +235,16 @@ Example to add nodes to a cluster } fmt.Println("action=", actionID) +Example to remove nodes from a cluster + + removeNodesOpts := clusters.RemoveNodesOpts{ + Nodes: []string{"node-123"}, + } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + err := clusters.RemoveNodes(serviceClient, clusterID, removeNodesOpts).ExtractErr() + if err != nil { + panic(err) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index a792f68bb9..d6ac0ecc6c 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -524,17 +524,16 @@ func CompleteLifecycle(client *gophercloud.ServiceClient, id string, opts Comple return } -func (opts AddNodesOpts) ToClusterNodeMap(nodeAction string) (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, nodeAction) +func (opts AddNodesOpts) ToClusterAddNodeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "add_nodes") } -// NodeOpts params type AddNodesOpts struct { Nodes []string `json:"nodes" required:"true"` } func AddNodes(client *gophercloud.ServiceClient, id string, opts AddNodesOpts) (r ActionResult) { - b, err := opts.ToClusterNodeMap("add_nodes") + b, err := opts.ToClusterAddNodeMap() if err != nil { r.Err = err return @@ -546,3 +545,25 @@ func AddNodes(client *gophercloud.ServiceClient, id string, opts AddNodesOpts) ( r.Header = result.Header return } + +func (opts RemoveNodesOpts) ToClusterRemoveNodeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "del_nodes") +} + +type RemoveNodesOpts struct { + Nodes []string `json:"nodes" required:"true"` +} + +func RemoveNodes(client *gophercloud.ServiceClient, clusterID string, opts RemoveNodesOpts) (r DeleteResult) { + b, err := opts.ToClusterRemoveNodeMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(nodeURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + r.Header = result.Header + return +} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 16ff20bff0..e31c54122a 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -635,3 +635,14 @@ func HandleAddNodesSuccessfully(t *testing.T) { fmt.Fprint(w, ActionResponse) }) } + +func HandleRemoveNodesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, ActionResponse) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 775ef2e45f..7b97e6c15b 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -440,3 +440,14 @@ func TestAddNodes(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") } + +func TestRemoveNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRemoveNodesSuccessfully(t) + opts := clusters.RemoveNodesOpts{ + Nodes: []string{"node1", "node2", "node3"}, + } + err := clusters.RemoveNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).ExtractErr() + th.AssertNoErr(t, err) +} From 04bc8f551ef2b34dad9d1d26634aa7a4d565f11f Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 8 Nov 2018 18:43:38 -0800 Subject: [PATCH 0592/2296] Magnum: Manage Quotas Create (#1235) * magnum-quotas-create * Acceptance test creates temp ProjectID rather than having caller use env variable to pass in ProjectID Fixed unmarshal json for int to use only float64 * Added logic to whether to use existing project name or create a temp project name for acceptance test * Removed env for OS_MAGNUM_PROJECT_ID * Removed ProjectName from acceptance test --- .../containerinfra/v1/containerinfra.go | 45 +++++++++++++++ .../containerinfra/v1/quotas_test.go | 20 +++++++ openstack/containerinfra/v1/quotas/doc.go | 18 ++++++ .../containerinfra/v1/quotas/requests.go | 43 ++++++++++++++ openstack/containerinfra/v1/quotas/results.go | 57 +++++++++++++++++++ .../containerinfra/v1/quotas/testing/doc.go | 1 + .../v1/quotas/testing/fixtures.go | 36 ++++++++++++ .../v1/quotas/testing/requests_test.go | 36 ++++++++++++ openstack/containerinfra/v1/quotas/urls.go | 15 +++++ 9 files changed, 271 insertions(+) create mode 100644 acceptance/openstack/containerinfra/v1/quotas_test.go create mode 100644 openstack/containerinfra/v1/quotas/doc.go create mode 100644 openstack/containerinfra/v1/quotas/requests.go create mode 100644 openstack/containerinfra/v1/quotas/results.go create mode 100644 openstack/containerinfra/v1/quotas/testing/doc.go create mode 100644 openstack/containerinfra/v1/quotas/testing/fixtures.go create mode 100644 openstack/containerinfra/v1/quotas/testing/requests_test.go create mode 100644 openstack/containerinfra/v1/quotas/urls.go diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go index 6d97dee2dc..748952df72 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -7,9 +7,11 @@ import ( "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/openstack/containerinfra/v1/clusters" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/quotas" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -177,3 +179,46 @@ func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status return false, nil }) } + +// CreateQuota will create a random quota. An error will be returned if the +// quota could not be created. +func CreateQuota(t *testing.T, client *gophercloud.ServiceClient) (*quotas.Quotas, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create quota: %s", name) + + idClient, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + project, err := idv3.CreateProject(t, idClient, nil) + th.AssertNoErr(t, err) + defer idv3.DeleteProject(t, idClient, project.ID) + + createOpts := quotas.CreateOpts{ + Resource: "Cluster", + ProjectID: project.ID, + HardLimit: 10, + } + + res := quotas.Create(client, createOpts) + if res.Err != nil { + return nil, res.Err + } + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, true, requestID != "") + + t.Logf("Quota %s request ID: %s", name, requestID) + + quota, err := res.Extract() + if err == nil { + t.Logf("Successfully created quota: %s", quota.ProjectID) + + tools.PrintResource(t, quota) + + th.AssertEquals(t, project.ID, quota.ProjectID) + th.AssertEquals(t, "Cluster", quota.Resource) + th.AssertEquals(t, 10, quota.HardLimit) + } + + return quota, err +} diff --git a/acceptance/openstack/containerinfra/v1/quotas_test.go b/acceptance/openstack/containerinfra/v1/quotas_test.go new file mode 100644 index 0000000000..b6e83bcaa1 --- /dev/null +++ b/acceptance/openstack/containerinfra/v1/quotas_test.go @@ -0,0 +1,20 @@ +// +build acceptance containerinfra + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestQuotasCRUD(t *testing.T) { + client, err := clients.NewContainerInfraV1Client() + th.AssertNoErr(t, err) + + quota, err := CreateQuota(t, client) + th.AssertNoErr(t, err) + tools.PrintResource(t, quota) +} diff --git a/openstack/containerinfra/v1/quotas/doc.go b/openstack/containerinfra/v1/quotas/doc.go new file mode 100644 index 0000000000..62b13b2db0 --- /dev/null +++ b/openstack/containerinfra/v1/quotas/doc.go @@ -0,0 +1,18 @@ +/* +Package quotas contains functionality for working with Magnum Quota API. + +Example to Create a Quota + + createOpts := quotas.CreateOpts{ + ProjectID: "aa5436ab58144c768ca4e9d2e9f5c3b2", + Resource: "Cluster", + HardLimit: 10, + } + + quota, err := quotas.Create(serviceClient, createOpts).Extract() + if err != nil { + panic(err) + } + +*/ +package quotas diff --git a/openstack/containerinfra/v1/quotas/requests.go b/openstack/containerinfra/v1/quotas/requests.go new file mode 100644 index 0000000000..21c5af7b39 --- /dev/null +++ b/openstack/containerinfra/v1/quotas/requests.go @@ -0,0 +1,43 @@ +package quotas + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" +) + +// CreateOptsBuilder Builder. +type CreateOptsBuilder interface { + ToQuotaCreateMap() (map[string]interface{}, error) +} + +// CreateOpts params +type CreateOpts struct { + ProjectID string `json:"project_id"` + Resource string `json:"resource"` + HardLimit int `json:"hard_limit"` +} + +// ToQuotaCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToQuotaCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create requests the creation of a new quota. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToQuotaCreateMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/containerinfra/v1/quotas/results.go b/openstack/containerinfra/v1/quotas/results.go new file mode 100644 index 0000000000..0fc4f97d78 --- /dev/null +++ b/openstack/containerinfra/v1/quotas/results.go @@ -0,0 +1,57 @@ +package quotas + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/gophercloud/gophercloud" +) + +type commonResult struct { + gophercloud.Result +} + +// CreateResult is the response of a Create operations. +type CreateResult struct { + commonResult +} + +// Extract is a function that accepts a result and extracts a quota resource. +func (r commonResult) Extract() (*Quotas, error) { + var s *Quotas + err := r.ExtractInto(&s) + return s, err +} + +type Quotas struct { + Resource string `json:"resource"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + HardLimit int `json:"hard_limit"` + ProjectID string `json:"project_id"` + ID string `json:"-"` +} + +func (r *Quotas) UnmarshalJSON(b []byte) error { + type tmp Quotas + var s struct { + tmp + ID interface{} `json:"id"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Quotas(s.tmp) + + switch t := s.ID.(type) { + case float64: + r.ID = fmt.Sprint(t) + case string: + r.ID = t + } + + return nil +} diff --git a/openstack/containerinfra/v1/quotas/testing/doc.go b/openstack/containerinfra/v1/quotas/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/containerinfra/v1/quotas/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/containerinfra/v1/quotas/testing/fixtures.go b/openstack/containerinfra/v1/quotas/testing/fixtures.go new file mode 100644 index 0000000000..8e02448dad --- /dev/null +++ b/openstack/containerinfra/v1/quotas/testing/fixtures.go @@ -0,0 +1,36 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const projectID = "aa5436ab58144c768ca4e9d2e9f5c3b2" +const requestUUID = "req-781e9bdc-4163-46eb-91c9-786c53188bbb" + +var CreateResponse = fmt.Sprintf(` +{ + "resource": "Cluster", + "created_at": "2017-01-17T17:35:48+00:00", + "updated_at": null, + "hard_limit": 1, + "project_id": "%s", + "id": 26 +}`, projectID) + +func HandleCreateQuotaSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/quotas", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-Id", requestUUID) + w.WriteHeader(http.StatusCreated) + + fmt.Fprint(w, CreateResponse) + }) +} diff --git a/openstack/containerinfra/v1/quotas/testing/requests_test.go b/openstack/containerinfra/v1/quotas/testing/requests_test.go new file mode 100644 index 0000000000..80e19a62c7 --- /dev/null +++ b/openstack/containerinfra/v1/quotas/testing/requests_test.go @@ -0,0 +1,36 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/quotas" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateQuota(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCreateQuotaSuccessfully(t) + + opts := quotas.CreateOpts{ + ProjectID: "aa5436ab58144c768ca4e9d2e9f5c3b2", + Resource: "Cluster", + HardLimit: 10, + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + res := quotas.Create(sc, opts) + th.AssertNoErr(t, res.Err) + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, requestUUID, requestID) + + quota, err := res.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, projectID, quota.ProjectID) +} diff --git a/openstack/containerinfra/v1/quotas/urls.go b/openstack/containerinfra/v1/quotas/urls.go new file mode 100644 index 0000000000..332b3f3c4b --- /dev/null +++ b/openstack/containerinfra/v1/quotas/urls.go @@ -0,0 +1,15 @@ +package quotas + +import ( + "github.com/gophercloud/gophercloud" +) + +var apiName = "quotas" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiName) +} + +func createURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} From 3a7818a07cfc862267a0b03635075a444c92dcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Depriester?= Date: Wed, 14 Nov 2018 21:47:05 +0100 Subject: [PATCH 0593/2296] enable versioning with X-History-Location (#1318) --- openstack/objectstorage/v1/containers/requests.go | 3 +++ openstack/objectstorage/v1/containers/results.go | 1 + 2 files changed, 4 insertions(+) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 4799204e70..89aa996839 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -74,6 +74,7 @@ type CreateOpts struct { DetectContentType bool `h:"X-Detect-Content-Type"` IfNoneMatch string `h:"If-None-Match"` VersionsLocation string `h:"X-Versions-Location"` + HistoryLocation string `h:"X-History-Location"` } // ToContainerCreateMap formats a CreateOpts into a map of headers. @@ -137,6 +138,8 @@ type UpdateOpts struct { DetectContentType bool `h:"X-Detect-Content-Type"` RemoveVersionsLocation string `h:"X-Remove-Versions-Location"` VersionsLocation string `h:"X-Versions-Location"` + RemoveHistoryLocation string `h:"X-Remove-History-Location"` + HistoryLocation string `h:"X-History-Location"` } // 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 e8c78880a4..cce2190ff9 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -100,6 +100,7 @@ type GetHeader struct { Read []string `json:"-"` TransID string `json:"X-Trans-Id"` VersionsLocation string `json:"X-Versions-Location"` + HistoryLocation string `json:"X-History-Location"` Write []string `json:"-"` StoragePolicy string `json:"X-Storage-Policy"` } From facb2a814c3187a94fa457ebfc9122745d9dc4df Mon Sep 17 00:00:00 2001 From: scott-chaney <40778338+scott-chaney@users.noreply.github.com> Date: Fri, 30 Nov 2018 18:22:30 -0800 Subject: [PATCH 0594/2296] Adds support for FindStack by stack_name or stack_id (#1287) * Adds support for FindStack() by stack_name or stack_id * Fixes formatting --- openstack/orchestration/v1/stacks/doc.go | 12 ++++++++++++ openstack/orchestration/v1/stacks/requests.go | 6 ++++++ .../orchestration/v1/stacks/testing/fixtures.go | 12 ++++++++++++ .../orchestration/v1/stacks/testing/requests_test.go | 12 ++++++++++++ openstack/orchestration/v1/stacks/urls.go | 4 ++++ 5 files changed, 46 insertions(+) diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index 92bda23eae..33fc3271c4 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -111,6 +111,18 @@ Example for Get Stack } fmt.Println("Get Stack: Name: ", stack.Name, ", ID: ", stack.ID, ", Status: ", stack.Status) +Example for Find Stack + + find_result := stacks.Find(client, stackIdentity) + if find_result.Err != nil { + panic(find_result.Err) + } + stack, err := find_result.Extract() + if err != nil { + panic(err) + } + fmt.Println("Find Stack: Name: ", stack.Name, ", ID: ", stack.ID, ", Status: ", stack.Status) + Example for Delete Stack del_r := stacks.Delete(client, stackName, created_stack.ID) diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index 779e3fb904..d874bd8d8f 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -259,6 +259,12 @@ func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) return } +// Find retrieves a stack based on the stack name or stack ID. +func Find(c *gophercloud.ServiceClient, stackIdentity string) (r GetResult) { + _, r.Err = c.Get(findURL(c, stackIdentity), &r.Body, nil) + return +} + // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the Update operation in this package. type UpdateOptsBuilder interface { diff --git a/openstack/orchestration/v1/stacks/testing/fixtures.go b/openstack/orchestration/v1/stacks/testing/fixtures.go index 16c274da16..a69bfa3724 100644 --- a/openstack/orchestration/v1/stacks/testing/fixtures.go +++ b/openstack/orchestration/v1/stacks/testing/fixtures.go @@ -223,6 +223,18 @@ func HandleGetSuccessfully(t *testing.T, output string) { }) } +func HandleFindSuccessfully(t *testing.T, output string) { + th.Mux.HandleFunc("/stacks/16ef0584-4458-41eb-87c8-0dc8d5f66c87", 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().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, output) + }) +} + // HandleUpdateSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87` // on the test handler mux that responds with an `Update` response. func HandleUpdateSuccessfully(t *testing.T) { diff --git a/openstack/orchestration/v1/stacks/testing/requests_test.go b/openstack/orchestration/v1/stacks/testing/requests_test.go index 8acf978ad7..6f0eb3a8e9 100644 --- a/openstack/orchestration/v1/stacks/testing/requests_test.go +++ b/openstack/orchestration/v1/stacks/testing/requests_test.go @@ -139,6 +139,18 @@ func TestGetStack(t *testing.T) { th.AssertDeepEquals(t, expected, actual) } +func TestFindStack(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFindSuccessfully(t, GetOutput) + + actual, err := stacks.Find(fake.ServiceClient(), "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() + th.AssertNoErr(t, err) + + expected := GetExpected + th.AssertDeepEquals(t, expected, actual) +} + func TestUpdateStack(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/orchestration/v1/stacks/urls.go b/openstack/orchestration/v1/stacks/urls.go index b00be54e2a..b909caac86 100644 --- a/openstack/orchestration/v1/stacks/urls.go +++ b/openstack/orchestration/v1/stacks/urls.go @@ -18,6 +18,10 @@ func getURL(c *gophercloud.ServiceClient, name, id string) string { return c.ServiceURL("stacks", name, id) } +func findURL(c *gophercloud.ServiceClient, identity string) string { + return c.ServiceURL("stacks", identity) +} + func updateURL(c *gophercloud.ServiceClient, name, id string) string { return getURL(c, name, id) } From 8f4eb476f72cbb430de821134208c549a643e99b Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Fri, 30 Nov 2018 18:28:31 -0800 Subject: [PATCH 0595/2296] senlin-clusters-replacenodes (#1328) --- .../openstack/clustering/v1/clusters_test.go | 41 +++++++++++++++++++ openstack/clustering/v1/clusters/doc.go | 11 +++++ openstack/clustering/v1/clusters/requests.go | 22 ++++++++++ .../v1/clusters/testing/fixtures.go | 11 +++++ .../v1/clusters/testing/requests_test.go | 12 ++++++ 5 files changed, 97 insertions(+) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 803b5845c5..440625b85b 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -4,6 +4,7 @@ package v1 import ( "sort" + "strings" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -359,3 +360,43 @@ func TestClustersRemoveNodeFromCluster(t *testing.T) { tools.PrintResource(t, cluster) } + +func TestClustersReplaceNode(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.3" + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + node1, err := CreateNode(t, client, "", profile.ID) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node1.ID) + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, len(cluster.Nodes) > 0) + for _, n := range cluster.Nodes { + defer DeleteNode(t, client, n) + } + + nodeIDToBeReplaced := cluster.Nodes[0] + opts := clusters.ReplaceNodesOpts{Nodes: map[string]string{nodeIDToBeReplaced: node1.ID}} + actionID, err := clusters.ReplaceNodes(client, cluster.ID, opts).Extract() + th.AssertNoErr(t, err) + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + + clusterNodes := strings.Join(cluster.Nodes, ",") + th.AssertEquals(t, true, strings.Contains(clusterNodes, node1.ID)) + th.AssertEquals(t, false, strings.Contains(clusterNodes, nodeIDToBeReplaced)) + tools.PrintResource(t, cluster) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index 7f018e51f0..a516cbff2b 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -246,5 +246,16 @@ Example to remove nodes from a cluster panic(err) } +Example to replace nodes for a cluster + + replaceNodesOpts := clusters.ReplaceNodesOpts{ + Nodes: map[string]string{"node-1234": "node-5678"}, + } + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + actionID, err := clusters.ReplaceNodes(serviceClient, clusterID, replaceNodesOpts).Extract() + if err != nil { + panic(err) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index d6ac0ecc6c..2952cf145e 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -567,3 +567,25 @@ func RemoveNodes(client *gophercloud.ServiceClient, clusterID string, opts Remov r.Header = result.Header return } + +func (opts ReplaceNodesOpts) ToClusterReplaceNodeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "replace_nodes") +} + +type ReplaceNodesOpts struct { + Nodes map[string]string `json:"nodes" required:"true"` +} + +func ReplaceNodes(client *gophercloud.ServiceClient, id string, opts ReplaceNodesOpts) (r ActionResult) { + b, err := opts.ToClusterReplaceNodeMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + r.Header = result.Header + return +} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index e31c54122a..ed74bc48dd 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -646,3 +646,14 @@ func HandleRemoveNodesSuccessfully(t *testing.T) { fmt.Fprint(w, ActionResponse) }) } + +func HandleReplaceNodeSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, ActionResponse) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 7b97e6c15b..73782a24b2 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -451,3 +451,15 @@ func TestRemoveNodes(t *testing.T) { err := clusters.RemoveNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).ExtractErr() th.AssertNoErr(t, err) } + +func TestReplaceNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleReplaceNodeSuccessfully(t) + opts := clusters.ReplaceNodesOpts{ + Nodes: map[string]string{"node-1234": "node-5678"}, + } + actionID, err := clusters.ReplaceNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") +} From fac2636416a98a198b71b7ebd8d6acba6a989cd4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 2 Dec 2018 14:17:02 -0700 Subject: [PATCH 0596/2296] core: Revert setting reauth func to nil (#1327) This commit reverts PR #1192. The goal of #1192 was to set the reauth function to nil when performing reauthentication in order to prevent an infinite loop. However, this was not done in a concurrent-safe way. Having reviewed the use-case in more detail, I feel that scenarios in which this will happen are more site-specific rather than an issue with Gophercloud and a custom reauth function should be used. Included in this PR is a simple unit test which verifies that a custom reauthentication function can be used for these scenarios. However, it's important to not use this example in production. Rather, users should use the example provided in the FAQ. --- provider_client.go | 14 +++----- testing/provider_client_test.go | 60 ++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/provider_client.go b/provider_client.go index 95fa11a788..7455751dfa 100644 --- a/provider_client.go +++ b/provider_client.go @@ -126,11 +126,11 @@ func (client *ProviderClient) SetToken(t string) { client.TokenID = t } -//Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is -//called because of a 401 response, the caller may pass the previous token. In -//this case, the reauthentication can be skipped if another thread has already -//reauthenticated in the meantime. If no previous token is known, an empty -//string should be passed instead to force unconditional reauthentication. +// Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is +// called because of a 401 response, the caller may pass the previous token. In +// this case, the reauthentication can be skipped if another thread has already +// reauthenticated in the meantime. If no previous token is known, an empty +// string should be passed instead to force unconditional reauthentication. func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { if client.ReauthFunc == nil { return nil @@ -295,11 +295,7 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) seeker.Seek(0, 0) } } - // make a new call to request with a nil reauth func in order to avoid infinite loop - reauthFunc := client.ReauthFunc - client.ReauthFunc = nil resp, err = client.Request(method, url, options) - client.ReauthFunc = reauthFunc if err != nil { switch err.(type) { case *ErrUnexpectedResponseCode: diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 1c6da1b927..24af155384 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/ioutil" "net/http" - "reflect" "sync" "testing" "time" @@ -125,12 +124,33 @@ func TestConcurrentReauth(t *testing.T) { } func TestReauthEndLoop(t *testing.T) { + var info = struct { + reauthAttempts int + maxReauthReached bool + mut *sync.RWMutex + }{ + 0, + false, + new(sync.RWMutex), + } + + numconc := 20 p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(client.TokenID) p.ReauthFunc = func() error { - // Reauth func is working and returns no error + info.mut.Lock() + defer info.mut.Unlock() + + if info.reauthAttempts > 5 { + info.maxReauthReached = true + return fmt.Errorf("Max reauthentication attempts reached") + } + + p.AuthenticatedHeaders() + info.reauthAttempts++ + return nil } @@ -144,13 +164,35 @@ func TestReauthEndLoop(t *testing.T) { }) reqopts := new(gophercloud.RequestOpts) - _, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) - if err == nil { - t.Errorf("request ends with a nil error") - return - } - if reflect.TypeOf(err) != reflect.TypeOf(&gophercloud.ErrErrorAfterReauthentication{}) { - t.Errorf("error is not an ErrErrorAfterReauthentication") + // counters for the upcoming errors + errAfter := 0 + errUnable := 0 + + wg := new(sync.WaitGroup) + for i := 0; i < numconc; i++ { + wg.Add(1) + go func() { + defer wg.Done() + _, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) + + // ErrErrorAfter... will happen after a successful reauthentication, + // but the service still responds with a 401. + if _, ok := err.(*gophercloud.ErrErrorAfterReauthentication); ok { + errAfter++ + } + + // ErrErrorUnable... will happen when the custom reauth func reports + // an error. + if _, ok := err.(*gophercloud.ErrUnableToReauthenticate); ok { + errUnable++ + } + }() } + + wg.Wait() + th.AssertEquals(t, info.reauthAttempts, 6) + th.AssertEquals(t, info.maxReauthReached, true) + th.AssertEquals(t, errAfter, 6) + th.AssertEquals(t, errUnable, 14) } From 16bd6b7824d06391fd5b8f1cfb83795dd85345d4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 4 Dec 2018 19:50:52 -0700 Subject: [PATCH 0597/2296] goimports fixes (#1340) --- .../openstack/networking/v2/extensions/trunks/trunks_test.go | 2 +- openstack/container/v1/capsules/template.go | 2 +- openstack/orchestration/v1/stacks/utils.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 566272f0e1..57dd481ef7 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" - "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" + v2 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" diff --git a/openstack/container/v1/capsules/template.go b/openstack/container/v1/capsules/template.go index ad72ca9ff2..c55428fa46 100644 --- a/openstack/container/v1/capsules/template.go +++ b/openstack/container/v1/capsules/template.go @@ -3,7 +3,7 @@ package capsules import ( "encoding/json" - "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v2" ) // Template is a structure that represents OpenStack Zun Capsule templates diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go index 71d9e35150..8f8d4cc4c0 100644 --- a/openstack/orchestration/v1/stacks/utils.go +++ b/openstack/orchestration/v1/stacks/utils.go @@ -10,7 +10,7 @@ import ( "strings" "github.com/gophercloud/gophercloud" - "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v2" ) // Client is an interface that expects a Get method similar to http.Get. This From e1cdd49b805cca71f9199998edf2c811899fe5ad Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 5 Dec 2018 07:38:00 +0100 Subject: [PATCH 0598/2296] Don't omit invert=false (#1314) --- openstack/loadbalancer/v2/l7policies/requests.go | 2 +- openstack/loadbalancer/v2/l7policies/testing/fixtures.go | 7 ++++--- .../loadbalancer/v2/l7policies/testing/requests_test.go | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index c8887ae3b6..9b444641ee 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -306,7 +306,7 @@ type UpdateRuleOpts struct { // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. - Invert bool `json:"invert,omitempty"` + Invert *bool `json:"invert,omitempty"` } // ToRuleUpdateMap builds a request body from UpdateRuleOpts. diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index e14cf898d1..e4c951728b 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -76,7 +76,7 @@ var ( Value: "/images*", ProjectID: "e3cd678b11784734bc366148aa37580e", Key: "", - Invert: false, + Invert: true, AdminStateUp: true, } RuleHostName = l7policies.Rule{ @@ -239,7 +239,7 @@ const SingleRuleBody = ` { "rule": { "compare_type": "REGEX", - "invert": false, + "invert": true, "admin_state_up": true, "value": "/images*", "key": null, @@ -276,7 +276,7 @@ const RulesListBody = ` "rules":[ { "compare_type": "REGEX", - "invert": false, + "invert": true, "admin_state_up": true, "value": "/images*", "key": null, @@ -365,6 +365,7 @@ func HandleRuleUpdateSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, `{ "rule": { "compare_type": "REGEX", + "invert": false, "type": "PATH", "value": "/images/special*" } diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index dc84754505..d7531c8b34 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -260,10 +260,12 @@ func TestUpdateRule(t *testing.T) { HandleRuleUpdateSuccessfully(t) client := fake.ServiceClient() + tmpBool := false actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", + Invert: &tmpBool, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From 9d88c34913a98b99b621982f092f117d52eaac42 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 6 Dec 2018 17:03:19 +0100 Subject: [PATCH 0599/2296] Add organizational unit (OU) support for the securityservices (#1315) --- openstack/sharedfilesystems/v2/securityservices/requests.go | 6 ++++++ openstack/sharedfilesystems/v2/securityservices/results.go | 2 ++ 2 files changed, 8 insertions(+) diff --git a/openstack/sharedfilesystems/v2/securityservices/requests.go b/openstack/sharedfilesystems/v2/securityservices/requests.go index 8ef1ba1166..c87cbb5bbb 100644 --- a/openstack/sharedfilesystems/v2/securityservices/requests.go +++ b/openstack/sharedfilesystems/v2/securityservices/requests.go @@ -32,6 +32,8 @@ type CreateOpts struct { Description string `json:"description,omitempty"` // The DNS IP address that is used inside the tenant network DNSIP string `json:"dns_ip,omitempty"` + // The security service organizational unit (OU). Minimum supported microversion for OU is 2.44. + OU string `json:"ou,omitempty"` // The security service user or group name that is used by the tenant User string `json:"user,omitempty"` // The user password, if you specify a user @@ -90,6 +92,8 @@ type ListOpts struct { Name string `q:"name"` // The DNS IP address that is used inside the tenant network DNSIP string `q:"dns_ip"` + // The security service organizational unit (OU). Minimum supported microversion for OU is 2.44. + OU string `q:"ou"` // The security service user or group name that is used by the tenant User string `q:"user"` // The security service host name or IP address @@ -145,6 +149,8 @@ type UpdateOpts struct { Type string `json:"type,omitempty"` // The DNS IP address that is used inside the tenant network DNSIP string `json:"dns_ip,omitempty"` + // The security service organizational unit (OU). Minimum supported microversion for OU is 2.44. + OU *string `json:"ou,omitempty"` // The security service user or group name that is used by the tenant User string `json:"user,omitempty"` // The user password, if you specify a user diff --git a/openstack/sharedfilesystems/v2/securityservices/results.go b/openstack/sharedfilesystems/v2/securityservices/results.go index ce18b8f76f..355f7c76a2 100644 --- a/openstack/sharedfilesystems/v2/securityservices/results.go +++ b/openstack/sharedfilesystems/v2/securityservices/results.go @@ -27,6 +27,8 @@ type SecurityService struct { Description string `json:"description"` // The DNS IP address that is used inside the tenant network DNSIP string `json:"dns_ip"` + // The security service organizational unit (OU) + OU string `json:"ou"` // The security service user or group name that is used by the tenant User string `json:"user"` // The user password, if you specify a user From 3038305ba4ed00cfd4ea8403ae65588051f15bb8 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 7 Dec 2018 03:44:49 +0100 Subject: [PATCH 0600/2296] Allow empty strings in JSON marshalling (#1341) * Allow empty strings in JSON marshalling * Fix acceptance tests --- .../blockstorage/v3/volumetypes_test.go | 3 +- .../openstack/compute/v2/secgroup_test.go | 3 +- .../openstack/identity/v3/domains_test.go | 3 +- .../openstack/identity/v3/groups_test.go | 3 +- .../openstack/identity/v3/regions_test.go | 3 +- .../portsbinding/portsbinding_test.go | 2 +- .../networking/v2/extensions/security_test.go | 3 +- .../v2/extensions/trunks/trunks_test.go | 3 +- .../openstack/networking/v2/ports_test.go | 10 +++-- .../v2/securityservices_test.go | 14 ++++--- .../v2/sharenetworks_test.go | 10 +++-- openstack/blockstorage/v1/volumes/requests.go | 4 +- .../v1/volumes/testing/requests_test.go | 3 +- openstack/blockstorage/v2/volumes/requests.go | 4 +- .../v2/volumes/testing/requests_test.go | 3 +- openstack/blockstorage/v3/volumes/requests.go | 4 +- .../v3/volumes/testing/requests_test.go | 3 +- .../blockstorage/v3/volumetypes/requests.go | 6 +-- .../v3/volumetypes/testing/requests_test.go | 3 +- .../extensions/quotasets/testing/fixtures.go | 2 +- .../v2/extensions/secgroups/requests.go | 18 ++++----- .../secgroups/testing/requests_test.go | 3 +- openstack/db/v1/configurations/requests.go | 2 +- openstack/dns/v2/recordsets/requests.go | 2 +- .../v2/recordsets/testing/requests_test.go | 3 +- openstack/dns/v2/zones/requests.go | 2 +- .../dns/v2/zones/testing/requests_test.go | 3 +- openstack/identity/v2/tenants/requests.go | 2 +- .../v2/tenants/testing/requests_test.go | 3 +- openstack/identity/v3/domains/requests.go | 2 +- .../v3/domains/testing/requests_test.go | 3 +- openstack/identity/v3/groups/requests.go | 2 +- .../v3/groups/testing/requests_test.go | 3 +- openstack/identity/v3/projects/requests.go | 2 +- .../v3/projects/testing/requests_test.go | 3 +- openstack/identity/v3/regions/requests.go | 2 +- .../v3/regions/testing/requests_test.go | 3 +- openstack/identity/v3/users/requests.go | 2 +- .../loadbalancer/v2/l7policies/requests.go | 6 +-- .../v2/l7policies/testing/requests_test.go | 7 ++-- .../loadbalancer/v2/listeners/requests.go | 4 +- .../v2/listeners/testing/requests_test.go | 3 +- .../loadbalancer/v2/loadbalancers/requests.go | 4 +- .../v2/loadbalancers/testing/requests_test.go | 3 +- .../loadbalancer/v2/monitors/requests.go | 2 +- .../v2/monitors/testing/requests_test.go | 3 +- openstack/loadbalancer/v2/pools/requests.go | 6 +-- .../v2/pools/testing/requests_test.go | 6 ++- .../v2/extensions/fwaas/firewalls/requests.go | 10 ++--- .../fwaas/firewalls/testing/requests_test.go | 6 ++- .../v2/extensions/fwaas/policies/requests.go | 4 +- .../fwaas/policies/testing/requests_test.go | 6 ++- .../routerinsertion/testing/requests_test.go | 12 ++++-- .../v2/extensions/lbaas/pools/requests.go | 2 +- .../lbaas/pools/testing/requests_test.go | 3 +- .../extensions/lbaas_v2/listeners/requests.go | 4 +- .../listeners/testing/requests_test.go | 3 +- .../lbaas_v2/loadbalancers/requests.go | 4 +- .../loadbalancers/testing/requests_test.go | 3 +- .../extensions/lbaas_v2/monitors/requests.go | 2 +- .../monitors/testing/requests_test.go | 3 +- .../v2/extensions/lbaas_v2/pools/requests.go | 6 +-- .../lbaas_v2/pools/testing/requests_test.go | 6 ++- .../portsbinding/testing/requests_test.go | 3 +- .../v2/extensions/security/groups/requests.go | 2 +- .../v2/extensions/trunks/requests.go | 6 +-- .../trunks/testing/requests_test.go | 4 +- .../ikepolicies/testing/requests_test.go | 38 +++++++++---------- openstack/networking/v2/ports/requests.go | 6 +-- .../v2/ports/testing/requests_test.go | 18 ++++++--- .../v2/securityservices/requests.go | 14 +++---- .../securityservices/testing/requests_test.go | 3 +- .../v2/sharenetworks/requests.go | 4 +- .../v2/sharenetworks/testing/requests_test.go | 12 ++++-- 74 files changed, 217 insertions(+), 160 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 635d054731..cfcb93620e 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -38,8 +38,9 @@ func TestVolumeTypes(t *testing.T) { th.AssertEquals(t, found, true) var isPublic = false + var name = vt.Name + "-UPDATED" updateOpts := volumetypes.UpdateOpts{ - Name: vt.Name + "-UPDATED", + Name: &name, IsPublic: &isPublic, } diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/acceptance/openstack/compute/v2/secgroup_test.go index 6f3028432a..047af24728 100644 --- a/acceptance/openstack/compute/v2/secgroup_test.go +++ b/acceptance/openstack/compute/v2/secgroup_test.go @@ -45,9 +45,10 @@ func TestSecGroupsCRUD(t *testing.T) { tools.PrintResource(t, securityGroup) newName := tools.RandomString("secgroup_", 4) + description := tools.RandomString("dec_", 10) updateOpts := secgroups.UpdateOpts{ Name: newName, - Description: tools.RandomString("dec_", 10), + Description: &description, } updatedSecurityGroup, err := secgroups.Update(client, securityGroup.ID, updateOpts).Extract() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go index 7d146464c5..36062c9c99 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -75,8 +75,9 @@ func TestDomainsCRUD(t *testing.T) { th.AssertEquals(t, domain.Description, "Testing Domain") var iFalse bool = false + var description = "Staging Test Domain" updateOpts := domains.UpdateOpts{ - Description: "Staging Test Domain", + Description: &description, Enabled: &iFalse, } diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go index 18c5ae0578..101524f136 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -33,8 +33,9 @@ func TestGroupCRUD(t *testing.T) { tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) + var description = "Test Groups" updateOpts := groups.UpdateOpts{ - Description: "Test Groups", + Description: &description, Extra: map[string]interface{}{ "email": "thetestgroup@example.com", }, diff --git a/acceptance/openstack/identity/v3/regions_test.go b/acceptance/openstack/identity/v3/regions_test.go index f44a65be8b..0457269c9c 100644 --- a/acceptance/openstack/identity/v3/regions_test.go +++ b/acceptance/openstack/identity/v3/regions_test.go @@ -75,8 +75,9 @@ func TestRegionsCRUD(t *testing.T) { tools.PrintResource(t, region) tools.PrintResource(t, region.Extra) + var description = "Region A for testing" updateOpts := regions.UpdateOpts{ - Description: "Region A for testing", + Description: &description, /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index 806eb7a19e..191fd7da8c 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -41,7 +41,7 @@ func TestPortsbindingCRUD(t *testing.T) { // Update port newPortName := tools.RandomString("TESTACC-", 8) updateOpts := ports.UpdateOpts{ - Name: newPortName, + Name: &newPortName, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/security_test.go b/acceptance/openstack/networking/v2/extensions/security_test.go index 8f3a291364..ed7a5c787a 100644 --- a/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/acceptance/openstack/networking/v2/extensions/security_test.go @@ -26,8 +26,9 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { tools.PrintResource(t, group) + var description = "A security group" updateOpts := groups.UpdateOpts{ - Description: "A security group", + Description: &description, } newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 57dd481ef7..b3aeba3dd9 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -65,8 +65,9 @@ func TestTrunkCRUD(t *testing.T) { } // Update Trunk + var name = "updated_gophertrunk" updateOpts := trunks.UpdateOpts{ - Name: "updated_gophertrunk", + Name: &name, } updatedTrunk, err := trunks.Update(client, trunk.ID, updateOpts).Extract() if err != nil { diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 65dfb18637..5e871d2140 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -42,7 +42,7 @@ func TestPortsCRUD(t *testing.T) { // Update port newPortName := tools.RandomString("TESTACC-", 8) updateOpts := ports.UpdateOpts{ - Name: newPortName, + Name: &newPortName, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) @@ -146,8 +146,9 @@ func TestPortsDontAlterSecurityGroups(t *testing.T) { th.AssertNoErr(t, err) // Update the port again + var name = "some_port" updateOpts = ports.UpdateOpts{ - Name: "some_port", + Name: &name, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) @@ -262,8 +263,9 @@ func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { tools.PrintResource(t, newPort) // Remove the address pair + var name = "some_port" updateOpts = ports.UpdateOpts{ - Name: "some_port", + Name: &name, } newPort, err = ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) @@ -341,7 +343,7 @@ func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { // Update the port with extra DHCP options. newPortName := tools.RandomString("TESTACC-", 8) portUpdateOpts := ports.UpdateOpts{ - Name: newPortName, + Name: &newPortName, } existingOpt := port.ExtraDHCPOpts[0] diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index ecf8cc9a1c..6ffa6160c1 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -108,9 +108,11 @@ func TestSecurityServiceUpdate(t *testing.T) { t.Fatalf("Unable to create security service: %v", err) } + name := "NewName" + description := "New security service description" options := securityservices.UpdateOpts{ - Name: "NewName", - Description: "New security service description", + Name: &name, + Description: &description, Type: "ldap", } @@ -124,12 +126,12 @@ func TestSecurityServiceUpdate(t *testing.T) { t.Errorf("Unable to retrieve the security service: %v", err) } - if newSecurityService.Name != options.Name { - t.Fatalf("Security service name was expeted to be: %s", options.Name) + if newSecurityService.Name != *options.Name { + t.Fatalf("Security service name was expeted to be: %s", *options.Name) } - if newSecurityService.Description != options.Description { - t.Fatalf("Security service description was expeted to be: %s", options.Description) + if newSecurityService.Description != *options.Description { + t.Fatalf("Security service description was expeted to be: %s", *options.Description) } if newSecurityService.Type != options.Type { diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index 7bf760f8ae..10173a5ed9 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -52,13 +52,15 @@ func TestShareNetworkUpdate(t *testing.T) { t.Errorf("Unable to retrieve shareNetwork: %v", err) } + name := "NewName" + description := "New share network description" options := sharenetworks.UpdateOpts{ - Name: "NewName", - Description: "New share network description", + Name: &name, + Description: &description, } - expectedShareNetwork.Name = options.Name - expectedShareNetwork.Description = options.Description + expectedShareNetwork.Name = *options.Name + expectedShareNetwork.Description = *options.Description _, err = sharenetworks.Update(client, shareNetwork.ID, options).Extract() if err != nil { diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go index 52d738fa9c..1da94238b9 100644 --- a/openstack/blockstorage/v1/volumes/requests.go +++ b/openstack/blockstorage/v1/volumes/requests.go @@ -110,8 +110,8 @@ type UpdateOptsBuilder interface { // to the volumes.Update function. For more information about the parameters, see // the Volume object. type UpdateOpts struct { - Name string `json:"display_name,omitempty"` - Description string `json:"display_description,omitempty"` + Name *string `json:"display_name,omitempty"` + Description *string `json:"display_description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } diff --git a/openstack/blockstorage/v1/volumes/testing/requests_test.go b/openstack/blockstorage/v1/volumes/testing/requests_test.go index c4ce23a75a..0ba830e038 100644 --- a/openstack/blockstorage/v1/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v1/volumes/testing/requests_test.go @@ -145,7 +145,8 @@ func TestUpdate(t *testing.T) { MockUpdateResponse(t) - options := volumes.UpdateOpts{Name: "vol-002"} + var name = "vol-002" + options := volumes.UpdateOpts{Name: &name} v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index 5549fb07d1..c27ddbf67c 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -173,8 +173,8 @@ type UpdateOptsBuilder interface { // to the volumes.Update function. For more information about the parameters, see // the Volume object. type UpdateOpts struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 723094f6c8..349f746a33 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -230,7 +230,8 @@ func TestUpdate(t *testing.T) { MockUpdateResponse(t) - options := volumes.UpdateOpts{Name: "vol-002"} + var name = "vol-002" + options := volumes.UpdateOpts{Name: &name} v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 8ae160fca8..25f70b27c1 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -175,8 +175,8 @@ type UpdateOptsBuilder interface { // to the volumes.Update function. For more information about the parameters, see // the Volume object. type UpdateOpts struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index 834c9280f1..5b9af2c8ad 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -230,7 +230,8 @@ func TestUpdate(t *testing.T) { MockUpdateResponse(t) - options := volumes.UpdateOpts{Name: "vol-002"} + var name = "vol-002" + options := volumes.UpdateOpts{Name: &name} v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 82a79bcf6a..a477c97d9f 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -112,9 +112,9 @@ type UpdateOptsBuilder interface { // to the volumetypes.Update function. For more information about the parameters, see // the Volume Type object. type UpdateOpts struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - IsPublic *bool `json:"is_public,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + IsPublic *bool `json:"is_public,omitempty"` } // ToVolumeUpdateMap assembles a request body based on the contents of an diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 00c3e6ea9e..b1435805d9 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -106,8 +106,9 @@ func TestUpdate(t *testing.T) { MockUpdateResponse(t) var isPublic = true + var name = "vol-type-002" options := volumetypes.UpdateOpts{ - Name: "vol-type-002", + Name: &name, IsPublic: &isPublic, } diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go index b30d6eae38..c0955c5ca9 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures.go +++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures.go @@ -121,7 +121,7 @@ var FirstQuotaSet = quotasets.QuotaSet{ // FirstQuotaDetailsSet is the first result in ListOutput. var FirstQuotaDetailsSet = quotasets.QuotaDetailSet{ - ID: FirstTenantID, + ID: FirstTenantID, InjectedFileContentBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 10240}, InjectedFilePathBytes: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 255}, InjectedFiles: quotasets.QuotaDetail{InUse: 0, Reserved: 0, Limit: 5}, diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go index 8b93f08b07..92a0331e18 100644 --- a/openstack/compute/v2/extensions/secgroups/requests.go +++ b/openstack/compute/v2/extensions/secgroups/requests.go @@ -23,19 +23,14 @@ func ListByServer(client *gophercloud.ServiceClient, serverID string) pagination return commonList(client, listByServerURL(client, serverID)) } -// GroupOpts is the underlying struct responsible for creating or updating -// security groups. It therefore represents the mutable attributes of a -// security group. -type GroupOpts struct { +// CreateOpts is the struct responsible for creating a security group. +type CreateOpts struct { // the name of your security group. Name string `json:"name" required:"true"` // the description of your security group. - Description string `json:"description" required:"true"` + Description string `json:"description,omitempty"` } -// CreateOpts is the struct responsible for creating a security group. -type CreateOpts GroupOpts - // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { @@ -61,7 +56,12 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // UpdateOpts is the struct responsible for updating an existing security group. -type UpdateOpts GroupOpts +type UpdateOpts struct { + // the name of your security group. + Name string `json:"name,omitempty"` + // the description of your security group. + Description *string `json:"description,omitempty"` +} // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. diff --git a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go b/openstack/compute/v2/extensions/secgroups/testing/requests_test.go index b5207646be..01ab9f7356 100644 --- a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go +++ b/openstack/compute/v2/extensions/secgroups/testing/requests_test.go @@ -115,9 +115,10 @@ func TestUpdate(t *testing.T) { mockUpdateGroupResponse(t, groupID) + description := "new_desc" opts := secgroups.UpdateOpts{ Name: "new_name", - Description: "new_desc", + Description: &description, } group, err := secgroups.Update(client.ServiceClient(), groupID, opts).Extract() diff --git a/openstack/db/v1/configurations/requests.go b/openstack/db/v1/configurations/requests.go index 6851c58765..32bfb1dd41 100644 --- a/openstack/db/v1/configurations/requests.go +++ b/openstack/db/v1/configurations/requests.go @@ -80,7 +80,7 @@ type UpdateOpts struct { // Associates the configuration group with a particular datastore. Datastore *DatastoreOpts `json:"datastore,omitempty"` // A human-readable explanation for the group. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` } // ToConfigUpdateMap will cast an UpdateOpts struct into a JSON map. diff --git a/openstack/dns/v2/recordsets/requests.go b/openstack/dns/v2/recordsets/requests.go index 2d6ecdc3dc..2bc4579727 100644 --- a/openstack/dns/v2/recordsets/requests.go +++ b/openstack/dns/v2/recordsets/requests.go @@ -119,7 +119,7 @@ type UpdateOptsBuilder interface { // RecordSet. type UpdateOpts struct { // Description is a description of the RecordSet. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // TTL is the time to live of the RecordSet. TTL int `json:"ttl,omitempty"` diff --git a/openstack/dns/v2/recordsets/testing/requests_test.go b/openstack/dns/v2/recordsets/testing/requests_test.go index 21630152ff..40285265df 100644 --- a/openstack/dns/v2/recordsets/testing/requests_test.go +++ b/openstack/dns/v2/recordsets/testing/requests_test.go @@ -109,9 +109,10 @@ func TestUpdate(t *testing.T) { defer th.TeardownHTTP() HandleUpdateSuccessfully(t) + var description = "Updated description" updateOpts := recordsets.UpdateOpts{ TTL: 0, - Description: "Updated description", + Description: &description, Records: []string{"10.1.0.2", "10.1.0.3"}, } diff --git a/openstack/dns/v2/zones/requests.go b/openstack/dns/v2/zones/requests.go index f87deadce7..78b08ae4f8 100644 --- a/openstack/dns/v2/zones/requests.go +++ b/openstack/dns/v2/zones/requests.go @@ -134,7 +134,7 @@ type UpdateOpts struct { Masters []string `json:"masters,omitempty"` // Description of the zone. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` } // ToZoneUpdateMap formats an UpdateOpts structure into a request body. diff --git a/openstack/dns/v2/zones/testing/requests_test.go b/openstack/dns/v2/zones/testing/requests_test.go index 412b34933f..0c9857cbda 100644 --- a/openstack/dns/v2/zones/testing/requests_test.go +++ b/openstack/dns/v2/zones/testing/requests_test.go @@ -72,9 +72,10 @@ func TestUpdate(t *testing.T) { defer th.TeardownHTTP() HandleUpdateSuccessfully(t) + var description = "Updated Description" updateOpts := zones.UpdateOpts{ TTL: 600, - Description: "Updated Description", + Description: &description, } UpdatedZone := CreatedZone diff --git a/openstack/identity/v2/tenants/requests.go b/openstack/identity/v2/tenants/requests.go index 60f58c8ce3..f21a58f10c 100644 --- a/openstack/identity/v2/tenants/requests.go +++ b/openstack/identity/v2/tenants/requests.go @@ -85,7 +85,7 @@ type UpdateOpts struct { Name string `json:"name,omitempty"` // Description is the description of the tenant. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // Enabled sets the tenant status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` diff --git a/openstack/identity/v2/tenants/testing/requests_test.go b/openstack/identity/v2/tenants/testing/requests_test.go index 86f2c94722..585bdcf2e9 100644 --- a/openstack/identity/v2/tenants/testing/requests_test.go +++ b/openstack/identity/v2/tenants/testing/requests_test.go @@ -73,9 +73,10 @@ func TestUpdateTenant(t *testing.T) { mockUpdateTenantResponse(t) id := "5c62ef576dc7444cbb73b1fe84b97648" + description := "This is new name" opts := tenants.UpdateOpts{ Name: "new_name", - Description: "This is new name", + Description: &description, Enabled: gophercloud.Enabled, } diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index 14fbd27eb3..215af0fca1 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -101,7 +101,7 @@ type UpdateOpts struct { Name string `json:"name,omitempty"` // Description is the description of the domain. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // Enabled sets the domain status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` diff --git a/openstack/identity/v3/domains/testing/requests_test.go b/openstack/identity/v3/domains/testing/requests_test.go index 73ac97aa73..07eeb06ca0 100644 --- a/openstack/identity/v3/domains/testing/requests_test.go +++ b/openstack/identity/v3/domains/testing/requests_test.go @@ -79,8 +79,9 @@ func TestUpdateDomain(t *testing.T) { defer th.TeardownHTTP() HandleUpdateDomainSuccessfully(t) + var description = "Staging Domain" updateOpts := domains.UpdateOpts{ - Description: "Staging Domain", + Description: &description, } actual, err := domains.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index cc991204cf..032b544804 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -133,7 +133,7 @@ type UpdateOpts struct { Name string `json:"name,omitempty"` // Description is a description of the group. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // DomainID is the ID of the domain the group belongs to. DomainID string `json:"domain_id,omitempty"` diff --git a/openstack/identity/v3/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go index b62779a9ad..380b9779ad 100644 --- a/openstack/identity/v3/groups/testing/requests_test.go +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -111,8 +111,9 @@ func TestUpdateGroup(t *testing.T) { defer th.TeardownHTTP() HandleUpdateGroupSuccessfully(t) + var description = "L2 Support Team" updateOpts := groups.UpdateOpts{ - Description: "L2 Support Team", + Description: &description, Extra: map[string]interface{}{ "email": "supportteam@example.com", }, diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 30f3d1f9ea..0e46169541 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -152,7 +152,7 @@ type UpdateOpts struct { ParentID string `json:"parent_id,omitempty"` // Description is the description of the project. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` } // ToUpdateCreateMap formats a UpdateOpts into an update request. diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 9a6664889f..746d654e1c 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -100,9 +100,10 @@ func TestUpdateProject(t *testing.T) { defer th.TeardownHTTP() HandleUpdateProjectSuccessfully(t) + var description = "The team that is bright red" updateOpts := projects.UpdateOpts{ Name: "Bright Red Team", - Description: "The team that is bright red", + Description: &description, } actual, err := projects.Update(client.ServiceClient(), "1234", updateOpts).Extract() diff --git a/openstack/identity/v3/regions/requests.go b/openstack/identity/v3/regions/requests.go index aa588fdff4..b5889ad3ec 100644 --- a/openstack/identity/v3/regions/requests.go +++ b/openstack/identity/v3/regions/requests.go @@ -105,7 +105,7 @@ type UpdateOptsBuilder interface { // UpdateOpts provides options for updating a region. type UpdateOpts struct { // Description is a description of the region. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // ParentRegionID is the ID of the parent region. ParentRegionID string `json:"parent_region_id,omitempty"` diff --git a/openstack/identity/v3/regions/testing/requests_test.go b/openstack/identity/v3/regions/testing/requests_test.go index 7f57557048..7210121c3b 100644 --- a/openstack/identity/v3/regions/testing/requests_test.go +++ b/openstack/identity/v3/regions/testing/requests_test.go @@ -77,8 +77,9 @@ func TestUpdateRegion(t *testing.T) { defer th.TeardownHTTP() HandleUpdateRegionSuccessfully(t) + var description = "First West sub-region of RegionOne" updateOpts := regions.UpdateOpts{ - Description: "First West sub-region of RegionOne", + Description: &description, /* // Due to a bug in Keystone, the Extra column of the Region table // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index ed3d1a026e..7a40ec762d 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -178,7 +178,7 @@ type UpdateOpts struct { DefaultProjectID string `json:"default_project_id,omitempty"` // Description is a description of the user. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // DomainID is the ID of the domain the user belongs to. DomainID string `json:"domain_id,omitempty"` diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 9b444641ee..1bd784751e 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -163,11 +163,11 @@ type UpdateOpts struct { // 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"` + RedirectPoolID *string `json:"redirect_pool_id,omitempty"` // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. - RedirectURL string `json:"redirect_url,omitempty"` + RedirectURL *string `json:"redirect_url,omitempty"` } // ToL7PolicyUpdateMap builds a request body from UpdateOpts. @@ -302,7 +302,7 @@ type UpdateRuleOpts struct { Value string `json:"value,omitempty"` // The key to use for the comparison. For example, the name of the cookie to evaluate. - Key string `json:"key,omitempty"` + Key *string `json:"key,omitempty"` // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index d7531c8b34..23d866a624 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -115,11 +115,12 @@ func TestUpdateL7Policy(t *testing.T) { client := fake.ServiceClient() newName := "NewL7PolicyName" + redirectURL := "http://www.new-example.com" actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, Action: l7policies.ActionRedirectToURL, - RedirectURL: "http://www.new-example.com", + RedirectURL: &redirectURL, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) @@ -260,12 +261,12 @@ func TestUpdateRule(t *testing.T) { HandleRuleUpdateSuccessfully(t) client := fake.ServiceClient() - tmpBool := false + invert := false actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", - Invert: &tmpBool, + Invert: &invert, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 061d3cc2ed..12f0d2fa2c 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -150,10 +150,10 @@ type UpdateOptsBuilder interface { // UpdateOpts represents options for updating a Listener. type UpdateOpts struct { // Human-readable name for the Listener. Does not have to be unique. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Human-readable description for the Listener. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // The maximum number of connections allowed for the Listener. ConnLimit *int `json:"connection_limit,omitempty"` diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index f403fb8e9f..449358b6d9 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -125,8 +125,9 @@ func TestUpdateListener(t *testing.T) { client := fake.ServiceClient() i1001 := 1001 + name := "NewListenerName" actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ - Name: "NewListenerName", + Name: &name, ConnLimit: &i1001, }).Extract() if err != nil { diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index a50d8ef192..2f821c55ed 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -134,10 +134,10 @@ type UpdateOptsBuilder interface { // operation. type UpdateOpts struct { // Human-readable name for the Loadbalancer. Does not have to be unique. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Human-readable description for the Loadbalancer. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index b23625bebb..22a6555366 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -133,8 +133,9 @@ func TestUpdateLoadbalancer(t *testing.T) { HandleLoadbalancerUpdateSuccessfully(t) client := fake.ServiceClient() + name := "NewLoadbalancerName" actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ - Name: "NewLoadbalancerName", + Name: &name, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index c173e1c64e..f728f5a823 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -223,7 +223,7 @@ type UpdateOpts struct { ExpectedCodes string `json:"expected_codes,omitempty"` // The Name of the Monitor. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). diff --git a/openstack/loadbalancer/v2/monitors/testing/requests_test.go b/openstack/loadbalancer/v2/monitors/testing/requests_test.go index d850fe199b..8ccfb950dc 100644 --- a/openstack/loadbalancer/v2/monitors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -113,8 +113,9 @@ func TestUpdateHealthmonitor(t *testing.T) { HandleHealthmonitorUpdateSuccessfully(t) client := fake.ServiceClient() + name := "NewHealthmonitorName" actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ - Name: "NewHealthmonitorName", + Name: &name, Delay: 3, Timeout: 20, MaxRetries: 10, diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 6eb8fcba06..9a10b6dcd0 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -149,10 +149,10 @@ type UpdateOptsBuilder interface { // operation. type UpdateOpts struct { // Name of the pool. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Human-readable description for the pool. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // The algorithm used to distribute load between the members of the pool. The // current specification supports LBMethodRoundRobin, LBMethodLeastConnections @@ -308,7 +308,7 @@ type UpdateMemberOptsBuilder interface { // operation. type UpdateMemberOpts struct { // Name of the Member. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // A positive integer value that indicates the relative portion of traffic // that this member should receive from the pool. For example, a member with diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go index 48dd2c0007..106cb0303c 100644 --- a/openstack/loadbalancer/v2/pools/testing/requests_test.go +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -98,8 +98,9 @@ func TestUpdatePool(t *testing.T) { HandlePoolUpdateSuccessfully(t) client := fake.ServiceClient() + name := "NewPoolName" actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ - Name: "NewPoolName", + Name: &name, LBMethod: pools.LBMethodLeastConnections, }).Extract() if err != nil { @@ -252,8 +253,9 @@ func TestUpdateMember(t *testing.T) { weight := 4 client := fake.ServiceClient() + name := "newMemberName" actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ - Name: "newMemberName", + Name: &name, Weight: &weight, }).Extract() if err != nil { diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go index cbd6c1fb0d..c710f94a8d 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go @@ -107,11 +107,11 @@ type UpdateOptsBuilder interface { // UpdateOpts contains the values used when updating a firewall. type UpdateOpts struct { - PolicyID string `json:"firewall_policy_id" required:"true"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - Shared *bool `json:"shared,omitempty"` + PolicyID string `json:"firewall_policy_id" required:"true"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Shared *bool `json:"shared,omitempty"` } // ToFirewallUpdateMap casts a CreateOpts struct to a map. diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go index 13eca65b69..f7fae9fdc5 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go @@ -315,9 +315,11 @@ func TestUpdate(t *testing.T) { `) }) + var name = "fw" + var description = "updated fw" options := firewalls.UpdateOpts{ - Name: "fw", - Description: "updated fw", + Name: &name, + Description: &description, AdminStateUp: gophercloud.Disabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go index 40ab7a8c46..fcecf6a50d 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas/policies/requests.go @@ -107,8 +107,8 @@ type UpdateOptsBuilder interface { // UpdateOpts contains the values used when updating a firewall policy. type UpdateOpts struct { - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` Shared *bool `json:"shared,omitempty"` Audited *bool `json:"audited,omitempty"` Rules []string `json:"firewall_rules,omitempty"` diff --git a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go index 11b9848f52..ed7070cc25 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go @@ -246,9 +246,11 @@ func TestUpdate(t *testing.T) { `) }) + var name = "policy" + var description = "Firewall policy" options := policies.UpdateOpts{ - Name: "policy", - Description: "Firewall policy", + Name: &name, + Description: &description, Rules: []string{ "98a58c87-76be-ae7c-a74e-b77fffb88d95", "11a58c87-76be-ae7c-a74e-b77fffb88a32", diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go index ac7a2be8d2..0eff5fcd49 100644 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go @@ -165,9 +165,11 @@ func TestUpdate(t *testing.T) { `) }) + var name = "fw" + var description = "updated fw" firewallUpdateOpts := firewalls.UpdateOpts{ - Name: "fw", - Description: "updated fw", + Name: &name, + Description: &description, AdminStateUp: gophercloud.Disabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } @@ -219,9 +221,11 @@ func TestUpdateWithNoRouters(t *testing.T) { `) }) + var name = "fw" + var description = "updated fw" firewallUpdateOpts := firewalls.UpdateOpts{ - Name: "fw", - Description: "updated fw", + Name: &name, + Description: &description, AdminStateUp: gophercloud.Disabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go index b3593548d3..f5f4e9a0df 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas/pools/requests.go @@ -123,7 +123,7 @@ type UpdateOptsBuilder interface { // UpdateOpts contains the values used when updating a pool. type UpdateOpts struct { // Name of the pool. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // LBMethod is the algorithm used to distribute load between the members of // the pool. The current specification supports LBMethodRoundRobin and diff --git a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go index de038cb8cd..dce13dd745 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go @@ -252,7 +252,8 @@ func TestUpdate(t *testing.T) { `) }) - options := pools.UpdateOpts{Name: "SuperPool", LBMethod: pools.LBMethodLeastConnections} + var name = "SuperPool" + options := pools.UpdateOpts{Name: &name, LBMethod: pools.LBMethodLeastConnections} n, err := pools.Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index dd190f606f..582c03241a 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -154,10 +154,10 @@ type UpdateOptsBuilder interface { // UpdateOpts represents options for updating a Listener. type UpdateOpts struct { // Human-readable name for the Listener. Does not have to be unique. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Human-readable description for the Listener. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // The maximum number of connections allowed for the Listener. ConnLimit *int `json:"connection_limit,omitempty"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go index d463f6e859..5bcf4d5a99 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go @@ -125,8 +125,9 @@ func TestUpdateListener(t *testing.T) { client := fake.ServiceClient() i1001 := 1001 + name := "NewListenerName" actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ - Name: "NewListenerName", + Name: &name, ConnLimit: &i1001, }).Extract() if err != nil { diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index 1ed23c3c82..8b2ef08916 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -141,10 +141,10 @@ type UpdateOptsBuilder interface { // operation. type UpdateOpts struct { // Human-readable name for the Loadbalancer. Does not have to be unique. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Human-readable description for the Loadbalancer. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go index e370c669bf..964471b9ef 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go @@ -133,8 +133,9 @@ func TestUpdateLoadbalancer(t *testing.T) { HandleLoadbalancerUpdateSuccessfully(t) client := fake.ServiceClient() + name := "NewLoadbalancerName" actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ - Name: "NewLoadbalancerName", + Name: &name, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go index c173e1c64e..f728f5a823 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go @@ -223,7 +223,7 @@ type UpdateOpts struct { ExpectedCodes string `json:"expected_codes,omitempty"` // The Name of the Monitor. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go index 743d9c1c6b..09389c840a 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go @@ -113,8 +113,9 @@ func TestUpdateHealthmonitor(t *testing.T) { HandleHealthmonitorUpdateSuccessfully(t) client := fake.ServiceClient() + name := "NewHealthmonitorName" actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ - Name: "NewHealthmonitorName", + Name: &name, Delay: 3, Timeout: 20, MaxRetries: 10, diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index 472b9dd30c..f427ae7bf5 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -154,10 +154,10 @@ type UpdateOptsBuilder interface { // operation. type UpdateOpts struct { // Name of the pool. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Human-readable description for the pool. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // The algorithm used to distribute load between the members of the pool. The // current specification supports LBMethodRoundRobin, LBMethodLeastConnections @@ -317,7 +317,7 @@ type UpdateMemberOptsBuilder interface { // operation. type UpdateMemberOpts struct { // Name of the Member. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // A positive integer value that indicates the relative portion of traffic // that this member should receive from the pool. For example, a member with diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go index ee6a7d8a2a..14802fd4c4 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go @@ -98,8 +98,9 @@ func TestUpdatePool(t *testing.T) { HandlePoolUpdateSuccessfully(t) client := fake.ServiceClient() + name := "NewPoolName" actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ - Name: "NewPoolName", + Name: &name, LBMethod: pools.LBMethodLeastConnections, }).Extract() if err != nil { @@ -252,8 +253,9 @@ func TestUpdateMember(t *testing.T) { weight := 4 client := fake.ServiceClient() + name := "newMemberName" actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ - Name: "newMemberName", + Name: &name, Weight: &weight, }).Extract() if err != nil { diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go index 57901a6b0b..95f5a645f9 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go @@ -156,8 +156,9 @@ func TestUpdate(t *testing.T) { portsbinding.PortsBindingExt } + name := "new_port_name" portUpdateOpts := ports.UpdateOpts{ - Name: "new_port_name", + Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index c89ea63087..4f4d01774a 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -92,7 +92,7 @@ type UpdateOpts struct { Name string `json:"name,omitempty"` // Describes the security group. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` } // ToSecGroupUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index 575a20cf4b..447a0d4113 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -114,9 +114,9 @@ type UpdateOptsBuilder interface { } type UpdateOpts struct { - AdminStateUp *bool `json:"admin_state_up,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` } func (opts UpdateOpts) ToTrunkUpdateMap() (map[string]interface{}, error) { diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index 595da01036..8f2b377852 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -182,9 +182,9 @@ func TestUpdate(t *testing.T) { name := "updated_gophertrunk" description := "gophertrunk updated by gophercloud" options := trunks.UpdateOpts{ - Name: name, + Name: &name, AdminStateUp: &iFalse, - Description: description, + Description: &description, } n, err := trunks.Update(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c", options).Extract() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index 9c3b08f120..b3ec548da7 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -74,13 +74,13 @@ func TestCreate(t *testing.T) { IKEVersion: "v2", TenantID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", - Lifetime: expectedLifetime, - ProjectID: "9145d91459d248b1b02fdaca97c6a75d", + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + Lifetime: expectedLifetime, + ProjectID: "9145d91459d248b1b02fdaca97c6a75d", } th.AssertDeepEquals(t, expected, *actual) } @@ -130,12 +130,12 @@ func TestGet(t *testing.T) { TenantID: "9145d91459d248b1b02fdaca97c6a75d", ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", - Lifetime: expectedLifetime, + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Lifetime: expectedLifetime, } th.AssertDeepEquals(t, expected, *actual) } @@ -208,12 +208,12 @@ func TestList(t *testing.T) { TenantID: "9145d91459d248b1b02fdaca97c6a75d", ProjectID: "9145d91459d248b1b02fdaca97c6a75d", Phase1NegotiationMode: "main", - PFS: "Group5", - EncryptionAlgorithm: "aes-128", - Description: "IKE policy", - Name: "policy", - ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", - Lifetime: expectedLifetime, + PFS: "Group5", + EncryptionAlgorithm: "aes-128", + Description: "IKE policy", + Name: "policy", + ID: "5c561d9d-eaea-45f6-ae3e-08d1a7080828", + Lifetime: expectedLifetime, }, } diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index d519d7699b..105e5c925a 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -116,11 +116,11 @@ type UpdateOptsBuilder interface { // UpdateOpts represents the attributes used when updating an existing port. type UpdateOpts struct { - Name string `json:"name,omitempty"` + Name *string `json:"name,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"` + 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"` } diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index af91cf99d3..a674e7eaa0 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -377,8 +377,9 @@ func TestUpdate(t *testing.T) { fmt.Fprintf(w, UpdateResponse) }) + name := "new_port_name" options := ports.UpdateOpts{ - Name: "new_port_name", + Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, @@ -418,8 +419,9 @@ func TestUpdateOmitSecurityGroups(t *testing.T) { fmt.Fprintf(w, UpdateOmitSecurityGroupsResponse) }) + name := "new_port_name" options := ports.UpdateOpts{ - Name: "new_port_name", + Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, @@ -495,8 +497,9 @@ func TestRemoveSecurityGroups(t *testing.T) { fmt.Fprintf(w, RemoveSecurityGroupResponse) }) + name := "new_port_name" options := ports.UpdateOpts{ - Name: "new_port_name", + Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, @@ -536,8 +539,9 @@ func TestRemoveAllowedAddressPairs(t *testing.T) { fmt.Fprintf(w, RemoveAllowedAddressPairsResponse) }) + name := "new_port_name" options := ports.UpdateOpts{ - Name: "new_port_name", + Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, @@ -573,8 +577,9 @@ func TestDontUpdateAllowedAddressPairs(t *testing.T) { fmt.Fprintf(w, DontUpdateAllowedAddressPairsResponse) }) + name := "new_port_name" options := ports.UpdateOpts{ - Name: "new_port_name", + Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, @@ -731,8 +736,9 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { fmt.Fprintf(w, UpdateWithExtraDHCPOptsResponse) }) + name := "updated-port-with-dhcp-opts" portUpdateOpts := ports.UpdateOpts{ - Name: "updated-port-with-dhcp-opts", + Name: &name, FixedIPs: []ports.IP{ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, }, diff --git a/openstack/sharedfilesystems/v2/securityservices/requests.go b/openstack/sharedfilesystems/v2/securityservices/requests.go index c87cbb5bbb..8bfec43cf8 100644 --- a/openstack/sharedfilesystems/v2/securityservices/requests.go +++ b/openstack/sharedfilesystems/v2/securityservices/requests.go @@ -142,23 +142,23 @@ type UpdateOptsBuilder interface { // the SecurityService object. type UpdateOpts struct { // The security service name - Name string `json:"name"` + Name *string `json:"name"` // The security service description - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // The security service type. A valid value is ldap, kerberos, or active_directory Type string `json:"type,omitempty"` // The DNS IP address that is used inside the tenant network - DNSIP string `json:"dns_ip,omitempty"` + DNSIP *string `json:"dns_ip,omitempty"` // The security service organizational unit (OU). Minimum supported microversion for OU is 2.44. OU *string `json:"ou,omitempty"` // The security service user or group name that is used by the tenant - User string `json:"user,omitempty"` + User *string `json:"user,omitempty"` // The user password, if you specify a user - Password string `json:"password,omitempty"` + Password *string `json:"password,omitempty"` // The security service domain - Domain string `json:"domain,omitempty"` + Domain *string `json:"domain,omitempty"` // The security service host name or IP address - Server string `json:"server,omitempty"` + Server *string `json:"server,omitempty"` } // ToSecurityServiceUpdateMap assembles a request body based on the contents of an diff --git a/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go b/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go index 06d9153d3a..1dcd6353e1 100644 --- a/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go @@ -201,7 +201,8 @@ func TestUpdate(t *testing.T) { Password: "supersecret", } - options := securityservices.UpdateOpts{Name: "SecServ2"} + name := "SecServ2" + options := securityservices.UpdateOpts{Name: &name} s, err := securityservices.Update(client.ServiceClient(), "securityServiceID", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, s) diff --git a/openstack/sharedfilesystems/v2/sharenetworks/requests.go b/openstack/sharedfilesystems/v2/sharenetworks/requests.go index cdc026c011..15e664ec38 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/requests.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/requests.go @@ -135,9 +135,9 @@ type UpdateOptsBuilder interface { // the ShareNetwork object. type UpdateOpts struct { // The share network name - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // The share network description - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // The UUID of the Neutron network to set up for share servers NeutronNetID string `json:"neutron_net_id,omitempty"` // The UUID of the Neutron subnet to set up for share servers diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go index efa4691861..61beefca21 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go @@ -191,9 +191,11 @@ func TestUpdateNeutron(t *testing.T) { ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", } + name := "net_my2" + description := "new description" options := sharenetworks.UpdateOpts{ - Name: "net_my2", - Description: "new description", + Name: &name, + Description: &description, NeutronNetID: "new-neutron-id", NeutronSubnetID: "new-neutron-subnet-id", } @@ -226,9 +228,11 @@ func TestUpdateNova(t *testing.T) { ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", } + name := "net_my2" + description := "new description" options := sharenetworks.UpdateOpts{ - Name: "net_my2", - Description: "new description", + Name: &name, + Description: &description, NovaNetID: "new-nova-id", } From d3bcea3cf97e0f06b9e272e9c4a1b5909e7db216 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 7 Dec 2018 18:13:49 +0100 Subject: [PATCH 0601/2296] Add DefaultPoolID update support (#1313) --- openstack/loadbalancer/v2/listeners/requests.go | 3 +++ .../networking/v2/extensions/lbaas_v2/listeners/requests.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 12f0d2fa2c..6c698e6669 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -152,6 +152,9 @@ type UpdateOpts struct { // Human-readable name for the Listener. Does not have to be unique. Name *string `json:"name,omitempty"` + // The ID of the default pool with which the Listener is associated. + DefaultPoolID *string `json:"default_pool_id,omitempty"` + // Human-readable description for the Listener. Description *string `json:"description,omitempty"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index 582c03241a..5c2bb78a98 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -156,6 +156,9 @@ type UpdateOpts struct { // Human-readable name for the Listener. Does not have to be unique. Name *string `json:"name,omitempty"` + // The ID of the default pool with which the Listener is associated. + DefaultPoolID *string `json:"default_pool_id,omitempty"` + // Human-readable description for the Listener. Description *string `json:"description,omitempty"` From ccd1eeca106d874ef0c83600d8a245c113213a05 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 8 Dec 2018 05:49:31 +0300 Subject: [PATCH 0602/2296] NetworkingV2: add vlan-transparent Update method (#1338) * NetworkingV2: add vlantransparent extension Add extension for OpenStack Networking V2 Networks with the "vlan-transparent" attribute. Add basic structure with List method, unit-test and documentation. * NetworkingV2: refactor vlan-transparent tests Simplify unit-test for List method and add unit-test and documentaion for the Get method. * NetworkingV2: fix transparent VLAN docs Use networkClient in docs example. * NetworkingV2: add VLAN transparent acc test Add basic mock for VLAN transparent extension acceptance test. * NetworkingV2: add VLAN transparent list helper Add ListVLANTransparentNetworks acceptance tests helper. * NetworkingV2: add vlan-transparent Create method Add a method to create an OpenStack Networking V2 Network with the "vlan-transparent" attribute set. Provide unit-test with documentation. * NetworkingV2: fix VLAN transparent create docs Fix example in docs. * NetworkingV2: extend VLAN transparent acc test Add CreateVLANTransparentNetwork acceptance test helper. * NetworkingV2: add vlan-transparent Update method Add a method to update an OpenStack Networking V2 Network with the "vlan-transparent" attribute. * NetworkingV2: fix VLAN transparent update Fix VLAN transparent extension docs and add update call into acceptance test. --- .../vlantransparent/vlantransparent.go | 106 +++++++++++ .../vlantransparent/vlantransparent_test.go | 45 +++++ .../v2/extensions/vlantransparent/doc.go | 97 ++++++++++ .../v2/extensions/vlantransparent/requests.go | 84 +++++++++ .../v2/extensions/vlantransparent/results.go | 8 + .../extensions/vlantransparent/testing/doc.go | 2 + .../vlantransparent/testing/fixtures.go | 133 ++++++++++++++ .../vlantransparent/testing/requests_test.go | 171 ++++++++++++++++++ 8 files changed, 646 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go create mode 100644 acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go create mode 100644 openstack/networking/v2/extensions/vlantransparent/doc.go create mode 100644 openstack/networking/v2/extensions/vlantransparent/requests.go create mode 100644 openstack/networking/v2/extensions/vlantransparent/results.go create mode 100644 openstack/networking/v2/extensions/vlantransparent/testing/doc.go create mode 100644 openstack/networking/v2/extensions/vlantransparent/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go diff --git a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go new file mode 100644 index 0000000000..19a2378b03 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go @@ -0,0 +1,106 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/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" +) + +// VLANTransparentNetwork represents OpenStack V2 Networking Network with the +// "vlan-transparent" extension enabled. +type VLANTransparentNetwork struct { + networks.Network + vlantransparent.TransparentExt +} + +// ListVLANTransparentNetworks will list networks with the "vlan-transparent" +// extension. An error will be returned networks could not be listed. +func ListVLANTransparentNetworks(t *testing.T, client *gophercloud.ServiceClient) ([]*VLANTransparentNetwork, error) { + iTrue := true + networkListOpts := networks.ListOpts{} + listOpts := vlantransparent.ListOptsExt{ + ListOptsBuilder: networkListOpts, + VLANTransparent: &iTrue, + } + + var allNetworks []*VLANTransparentNetwork + + t.Log("Attempting to list VLAN-transparent networks") + + allPages, err := networks.List(client, listOpts).AllPages() + if err != nil { + return nil, err + } + err = networks.ExtractNetworksInto(allPages, &allNetworks) + if err != nil { + return nil, err + } + + t.Log("Successfully retrieved networks.") + + return allNetworks, nil +} + +// CreateVLANTransparentNetwork will create a network with the +// "vlan-transparent" extension. An error will be returned if the network could +// not be created. +func CreateVLANTransparentNetwork(t *testing.T, client *gophercloud.ServiceClient) (*VLANTransparentNetwork, error) { + networkName := tools.RandomString("TESTACC-", 8) + networkCreateOpts := networks.CreateOpts{ + Name: networkName, + } + + iTrue := true + createOpts := vlantransparent.CreateOptsExt{ + CreateOptsBuilder: &networkCreateOpts, + VLANTransparent: &iTrue, + } + + t.Logf("Attempting to create a VLAN-transparent network: %s", networkName) + + var network VLANTransparentNetwork + err := networks.Create(client, createOpts).ExtractInto(&network) + if err != nil { + return nil, err + } + + t.Logf("Successfully created the network.") + + th.AssertEquals(t, networkName, network.Name) + + return &network, nil +} + +// UpdateVLANTransparentNetwork will update a network with the +// "vlan-transparent" extension. An error will be returned if the network could +// not be updated. +func UpdateVLANTransparentNetwork(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*VLANTransparentNetwork, error) { + networkName := tools.RandomString("TESTACC-NEW-", 6) + networkUpdateOpts := networks.UpdateOpts{ + Name: networkName, + } + + iFalse := false + updateOpts := vlantransparent.UpdateOptsExt{ + UpdateOptsBuilder: &networkUpdateOpts, + VLANTransparent: &iFalse, + } + + t.Logf("Attempting to update a VLAN-transparent network: %s", networkID) + + var network VLANTransparentNetwork + err := networks.Update(client, networkID, updateOpts).ExtractInto(&network) + if err != nil { + return nil, err + } + + t.Logf("Successfully updated the network.") + + th.AssertEquals(t, networkName, network.Name) + + return &network, nil +} diff --git a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go new file mode 100644 index 0000000000..248ab04ffa --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go @@ -0,0 +1,45 @@ +// +build acceptance networking vlantransparent + +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" + 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) + + // Create a VLAN transparent network. + network, err := CreateVLANTransparentNetwork(t, client) + th.AssertNoErr(t, err) + defer networkingv2.DeleteNetwork(t, client, network.ID) + + tools.PrintResource(t, network) + + // Update the created VLAN transparent network. + newNetwork, err := UpdateVLANTransparentNetwork(t, client, network.ID) + th.AssertNoErr(t, err) + + tools.PrintResource(t, newNetwork) + + // Check that the created VLAN transparent network exists. + vlanTransparentNetworks, err := ListVLANTransparentNetworks(t, client) + th.AssertNoErr(t, err) + + var found bool + for _, vlanTransparentNetwork := range vlanTransparentNetworks { + if vlanTransparentNetwork.ID == network.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/openstack/networking/v2/extensions/vlantransparent/doc.go b/openstack/networking/v2/extensions/vlantransparent/doc.go new file mode 100644 index 0000000000..a309a9667b --- /dev/null +++ b/openstack/networking/v2/extensions/vlantransparent/doc.go @@ -0,0 +1,97 @@ +/* +Package vlantransparent provides the ability to retrieve and manage networks +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.Println("%+v\n", network) + } + +Example of Getting a Network with the vlan-transparent extension + + var network struct { + networks.Network + vlantransparent.TransparentExt + } + + err := networks.Get(networkClient, "db193ab3-96e3-4cb3-8fc5-05f4296d0324").ExtractInto(&network) + if err != nil { + panic(err) + } + + fmt.Println("%+v\n", network) + +Example of Creating Network with the vlan-transparent extension + + iTrue := true + networkCreateOpts := networks.CreateOpts{ + Name: "private", + } + + createOpts := vlantransparent.CreateOptsExt{ + CreateOptsBuilder: &networkCreateOpts, + VLANTransparent: &iTrue, + } + + var network struct { + networks.Network + vlantransparent.TransparentExt + } + + err := networks.Create(networkClient, createOpts).ExtractInto(&network) + if err != nil { + panic(err) + } + + fmt.Println("%+v\n", network) + +Example of Updating Network with the vlan-transparent extension + + iFalse := false + networkUpdateOpts := networks.UpdateOpts{ + Name: "new_network_name", + } + + updateOpts := vlantransparent.UpdateOptsExt{ + UpdateOptsBuilder: &networkUpdateOpts, + VLANTransparent: &iFalse, + } + + var network struct { + networks.Network + vlantransparent.TransparentExt + } + + err := networks.Update(networkClient, updateOpts).ExtractInto(&network) + if err != nil { + panic(err) + } + + fmt.Println("%+v\n", network) +*/ +package vlantransparent diff --git a/openstack/networking/v2/extensions/vlantransparent/requests.go b/openstack/networking/v2/extensions/vlantransparent/requests.go new file mode 100644 index 0000000000..65504cf3ea --- /dev/null +++ b/openstack/networking/v2/extensions/vlantransparent/requests.go @@ -0,0 +1,84 @@ +package vlantransparent + +import ( + "net/url" + "strconv" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" +) + +// ListOptsExt adds the vlan-transparent network options to the base ListOpts. +type ListOptsExt struct { + networks.ListOptsBuilder + VLANTransparent *bool `q:"vlan_transparent"` +} + +// ToNetworkListQuery adds the vlan_transparent option to the base network +// list options. +func (opts ListOptsExt) ToNetworkListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts.ListOptsBuilder) + if err != nil { + return "", err + } + + params := q.Query() + if opts.VLANTransparent != nil { + v := strconv.FormatBool(*opts.VLANTransparent) + params.Add("vlan_transparent", v) + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), err +} + +// CreateOptsExt is the structure used when creating new vlan-transparent +// network resources. It embeds networks.CreateOpts and so inherits all of its +// required and optional fields, with the addition of the VLANTransparent field. +type CreateOptsExt struct { + networks.CreateOptsBuilder + VLANTransparent *bool `json:"vlan_transparent,omitempty"` +} + +// ToNetworkCreateMap adds the vlan_transparent option to the base network +// creation options. +func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() + if err != nil { + return nil, err + } + + if opts.VLANTransparent == nil { + return base, nil + } + + networkMap := base["network"].(map[string]interface{}) + networkMap["vlan_transparent"] = opts.VLANTransparent + + return base, nil +} + +// UpdateOptsExt is the structure used when updating existing vlan-transparent +// network resources. It embeds networks.UpdateOpts and so inherits all of its +// required and optional fields, with the addition of the VLANTransparent field. +type UpdateOptsExt struct { + networks.UpdateOptsBuilder + VLANTransparent *bool `json:"vlan_transparent,omitempty"` +} + +// ToNetworkUpdateMap casts an UpdateOpts struct to a map. +func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() + if err != nil { + return nil, err + } + + if opts.VLANTransparent == nil { + return base, nil + } + + networkMap := base["network"].(map[string]interface{}) + networkMap["vlan_transparent"] = opts.VLANTransparent + + return base, nil +} diff --git a/openstack/networking/v2/extensions/vlantransparent/results.go b/openstack/networking/v2/extensions/vlantransparent/results.go new file mode 100644 index 0000000000..62eae2091a --- /dev/null +++ b/openstack/networking/v2/extensions/vlantransparent/results.go @@ -0,0 +1,8 @@ +package vlantransparent + +// TransparentExt represents a decorated form of a network with +// "vlan-transparent" extension attributes. +type TransparentExt struct { + // VLANTransparent whether the network is a VLAN transparent network or not. + VLANTransparent bool `json:"vlan_transparent"` +} diff --git a/openstack/networking/v2/extensions/vlantransparent/testing/doc.go b/openstack/networking/v2/extensions/vlantransparent/testing/doc.go new file mode 100644 index 0000000000..edc6f82230 --- /dev/null +++ b/openstack/networking/v2/extensions/vlantransparent/testing/doc.go @@ -0,0 +1,2 @@ +// vlantransparent extension unit tests +package testing diff --git a/openstack/networking/v2/extensions/vlantransparent/testing/fixtures.go b/openstack/networking/v2/extensions/vlantransparent/testing/fixtures.go new file mode 100644 index 0000000000..558e1377a8 --- /dev/null +++ b/openstack/networking/v2/extensions/vlantransparent/testing/fixtures.go @@ -0,0 +1,133 @@ +package testing + +// NetworksVLANTransparentListResult represents raw HTTP response for the List +// request. +const NetworksVLANTransparentListResult = ` +{ + "networks": [ + { + "status": "ACTIVE", + "subnets": [ + "08eae331-0402-425a-923c-34f7cfe39c1b" + ], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 1234567890, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": false, + "port_security_enabled": false, + "vlan_transparent": true + }, + { + "status": "ACTIVE", + "subnets": [ + "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + ], + "name": "public", + "admin_state_up": true, + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "shared": true, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": true, + "port_security_enabled": true + } + ] +}` + +// NetworksVLANTransparentGetResult represents raw HTTP response for the Get +// request. +const NetworksVLANTransparentGetResult = ` +{ + "network": { + "status": "ACTIVE", + "subnets": [ + "08eae331-0402-425a-923c-34f7cfe39c1b" + ], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 1234567890, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": false, + "port_security_enabled": false, + "vlan_transparent": true + } +}` + +// NetworksVLANTransparentCreateRequest represents raw HTTP Create request. +const NetworksVLANTransparentCreateRequest = ` +{ + "network": { + "name": "private", + "admin_state_up": true, + "vlan_transparent": true + } +}` + +// NetworksVLANTransparentCreateResult represents raw HTTP response for the +// Create request. +const NetworksVLANTransparentCreateResult = ` +{ + "network": { + "status": "ACTIVE", + "subnets": [ + "08eae331-0402-425a-923c-34f7cfe39c1b" + ], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 1234567890, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": false, + "port_security_enabled": false, + "vlan_transparent": true + } +} +` + +// NetworksVLANTransparentUpdateRequest represents raw HTTP Update request. +const NetworksVLANTransparentUpdateRequest = ` +{ + "network": { + "name": "new_network_name", + "admin_state_up": false, + "vlan_transparent": false + } +}` + +// NetworksVLANTransparentUpdateResult represents raw HTTP response for the +// Update request. +const NetworksVLANTransparentUpdateResult = ` +{ + "network": { + "status": "ACTIVE", + "subnets": [ + "08eae331-0402-425a-923c-34f7cfe39c1b" + ], + "name": "new_network_name", + "admin_state_up": false, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 1234567890, + "provider:physical_network": null, + "provider:network_type": "local", + "router:external": false, + "port_security_enabled": false, + "vlan_transparent": false + } +} +` diff --git a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go new file mode 100644 index 0000000000..df39ca3d44 --- /dev/null +++ b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go @@ -0,0 +1,171 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vlantransparent" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks", 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, NetworksVLANTransparentListResult) + }) + + type networkVLANTransparentExt struct { + networks.Network + vlantransparent.TransparentExt + } + var actual []networkVLANTransparentExt + + allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + err = networks.ExtractNetworksInto(allPages, &actual) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", actual[0].ID) + th.AssertEquals(t, "private", actual[0].Name) + th.AssertEquals(t, true, actual[0].AdminStateUp) + th.AssertEquals(t, "ACTIVE", actual[0].Status) + th.AssertDeepEquals(t, []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, actual[0].Subnets) + th.AssertEquals(t, "26a7980765d0414dbc1fc1f88cdb7e6e", actual[0].TenantID) + th.AssertEquals(t, false, actual[0].Shared) + th.AssertEquals(t, true, actual[0].VLANTransparent) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/db193ab3-96e3-4cb3-8fc5-05f4296d0324", 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, NetworksVLANTransparentGetResult) + }) + + var s struct { + networks.Network + vlantransparent.TransparentExt + } + + err := networks.Get(fake.ServiceClient(), "db193ab3-96e3-4cb3-8fc5-05f4296d0324").ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) + th.AssertEquals(t, "private", s.Name) + th.AssertEquals(t, true, s.AdminStateUp) + th.AssertEquals(t, "ACTIVE", s.Status) + th.AssertDeepEquals(t, []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, s.Subnets) + th.AssertEquals(t, "26a7980765d0414dbc1fc1f88cdb7e6e", s.TenantID) + th.AssertEquals(t, false, s.Shared) + th.AssertEquals(t, true, s.VLANTransparent) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks", 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, NetworksVLANTransparentCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, NetworksVLANTransparentCreateResult) + }) + + iTrue := true + networkCreateOpts := networks.CreateOpts{ + Name: "private", + AdminStateUp: &iTrue, + } + vlanTransparentCreateOpts := vlantransparent.CreateOptsExt{ + CreateOptsBuilder: &networkCreateOpts, + VLANTransparent: &iTrue, + } + + var s struct { + networks.Network + vlantransparent.TransparentExt + } + + err := networks.Create(fake.ServiceClient(), vlanTransparentCreateOpts).ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) + th.AssertEquals(t, "private", s.Name) + th.AssertEquals(t, true, s.AdminStateUp) + th.AssertEquals(t, "ACTIVE", s.Status) + th.AssertDeepEquals(t, []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, s.Subnets) + th.AssertEquals(t, "26a7980765d0414dbc1fc1f88cdb7e6e", s.TenantID) + th.AssertEquals(t, false, s.Shared) + th.AssertEquals(t, true, s.VLANTransparent) +} + +func TestUpdate(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.TestJSONRequest(t, r, NetworksVLANTransparentUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, NetworksVLANTransparentUpdateResult) + }) + + iFalse := false + networkUpdateOpts := networks.UpdateOpts{ + Name: "new_network_name", + AdminStateUp: &iFalse, + } + + vlanTransparentUpdateOpts := vlantransparent.UpdateOptsExt{ + UpdateOptsBuilder: &networkUpdateOpts, + VLANTransparent: &iFalse, + } + + var s struct { + networks.Network + vlantransparent.TransparentExt + } + + err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", vlanTransparentUpdateOpts).ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) + th.AssertEquals(t, "new_network_name", s.Name) + th.AssertEquals(t, false, s.AdminStateUp) + th.AssertEquals(t, "ACTIVE", s.Status) + th.AssertDeepEquals(t, []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, s.Subnets) + th.AssertEquals(t, "26a7980765d0414dbc1fc1f88cdb7e6e", s.TenantID) + th.AssertEquals(t, false, s.Shared) + th.AssertEquals(t, false, s.VLANTransparent) +} From 26de66c23d78a75fc37e5dad5b6f16ffbfe9ee31 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 8 Dec 2018 03:58:21 +0100 Subject: [PATCH 0603/2296] Add description support for network resources (#1344) * security_group_rules * security_groups * ports * subnets, * networks * routers * floatingips * subnetpools --- .../extensions/layer3/floatingips/requests.go | 5 +++- .../extensions/layer3/floatingips/results.go | 3 +++ .../v2/extensions/layer3/routers/requests.go | 3 +++ .../v2/extensions/layer3/routers/results.go | 3 +++ .../v2/extensions/security/groups/requests.go | 25 ++++++++++--------- .../v2/extensions/security/rules/requests.go | 1 + openstack/networking/v2/networks/requests.go | 9 ++++--- openstack/networking/v2/networks/results.go | 3 +++ openstack/networking/v2/ports/requests.go | 3 +++ openstack/networking/v2/ports/results.go | 3 +++ openstack/networking/v2/subnets/requests.go | 7 ++++++ openstack/networking/v2/subnets/results.go | 3 +++ 12 files changed, 52 insertions(+), 16 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 2d44adc75c..471ad1e8d9 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -12,6 +12,7 @@ import ( // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` + Description string `q:"description"` FloatingNetworkID string `q:"floating_network_id"` PortID string `q:"port_id"` FixedIP string `q:"fixed_ip_address"` @@ -54,6 +55,7 @@ type CreateOptsBuilder interface { // resource. The only required fields are FloatingNetworkID and PortID which // refer to the external network and internal port respectively. type CreateOpts struct { + Description string `json:"description,omitempty"` FloatingNetworkID string `json:"floating_network_id" required:"true"` FloatingIP string `json:"floating_ip_address,omitempty"` PortID string `json:"port_id,omitempty"` @@ -120,7 +122,8 @@ type UpdateOptsBuilder interface { // linked to. To associate the floating IP with a new internal port, provide its // ID. To disassociate the floating IP from all ports, provide an empty string. type UpdateOpts struct { - PortID *string `json:"port_id"` + Description *string `json:"description,omitempty"` + PortID *string `json:"port_id"` } // ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index e5a8edafb7..b0b25a9d66 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -15,6 +15,9 @@ type FloatingIP struct { // ID is the unique identifier for the floating IP instance. ID string `json:"id"` + // Description for the floating IP instance. + Description string `json:"description"` + // FloatingNetworkID is the UUID of the external network where the floating // IP is to be created. FloatingNetworkID string `json:"floating_network_id"` diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index 9c8bbbeb37..cf499f9873 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -13,6 +13,7 @@ import ( type ListOpts struct { ID string `q:"id"` Name string `q:"name"` + Description string `q:"description"` AdminStateUp *bool `q:"admin_state_up"` Distributed *bool `q:"distributed"` Status string `q:"status"` @@ -55,6 +56,7 @@ type CreateOptsBuilder interface { // no required values. type CreateOpts struct { Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` Distributed *bool `json:"distributed,omitempty"` TenantID string `json:"tenant_id,omitempty"` @@ -101,6 +103,7 @@ type UpdateOptsBuilder interface { // UpdateOpts contains the values used when updating a router. type UpdateOpts struct { Name string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` Distributed *bool `json:"distributed,omitempty"` GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index 17287acf53..765391445f 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -51,6 +51,9 @@ type Router struct { // unique. Name string `json:"name"` + // Description for the router. + Description string `json:"description"` + // ID is the unique identifier for the router. ID string `json:"id"` diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index 4f4d01774a..a22cd306e8 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -11,18 +11,19 @@ import ( // sort by a particular network attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - Tags string `q:"tags"` - TagsAny string `q:"tags-any"` - NotTags string `q:"not-tags"` - NotTagsAny string `q:"not-tags-any"` + ID string `q:"id"` + Name string `q:"name"` + Description string `q:"description"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tags string `q:"tags"` + TagsAny string `q:"tags-any"` + NotTags string `q:"not-tags"` + NotTagsAny string `q:"not-tags-any"` } // List returns a Pager which allows you to iterate over a collection of diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index fad787e304..c7741ffcd2 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -14,6 +14,7 @@ type ListOpts struct { Direction string `q:"direction"` EtherType string `q:"ethertype"` ID string `q:"id"` + Description string `q:"description"` PortRangeMax int `q:"port_range_max"` PortRangeMin int `q:"port_range_min"` Protocol string `q:"protocol"` diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index f49622031d..d52d099a67 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -19,6 +19,7 @@ type ListOptsBuilder interface { 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"` @@ -73,6 +74,7 @@ type CreateOptsBuilder interface { type CreateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` Shared *bool `json:"shared,omitempty"` TenantID string `json:"tenant_id,omitempty"` ProjectID string `json:"project_id,omitempty"` @@ -109,9 +111,10 @@ type UpdateOptsBuilder interface { // UpdateOpts represents options used to update a network. type UpdateOpts struct { - AdminStateUp *bool `json:"admin_state_up,omitempty"` - Name string `json:"name,omitempty"` - Shared *bool `json:"shared,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Name string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Shared *bool `json:"shared,omitempty"` } // ToNetworkUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index 4d683d906d..f03067415f 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -52,6 +52,9 @@ type Network struct { // Human-readable name for the network. Might not be unique. Name string `json:"name"` + // Description for the network + Description string `json:"description"` + // The administrative state of network. If false (down), the network does not // forward packets. AdminStateUp bool `json:"admin_state_up"` diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 105e5c925a..f5f7d761ce 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -19,6 +19,7 @@ type ListOptsBuilder interface { 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"` @@ -80,6 +81,7 @@ type CreateOptsBuilder interface { 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"` @@ -117,6 +119,7 @@ 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"` diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index ca7b49f400..3941b62300 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -68,6 +68,9 @@ type Port struct { // Human-readable name for the port. Might not be unique. Name string `json:"name"` + // Describes the port. + Description string `json:"description"` + // Administrative state of port. If false (down), port does not forward // packets. AdminStateUp bool `json:"admin_state_up"` diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 4f460aee1a..bb73e030c0 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -18,6 +18,7 @@ type ListOptsBuilder interface { // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { Name string `q:"name"` + Description string `q:"description"` EnableDHCP *bool `q:"enable_dhcp"` NetworkID string `q:"network_id"` TenantID string `q:"tenant_id"` @@ -89,6 +90,9 @@ type CreateOpts struct { // Name is a human-readable name of the subnet. Name string `json:"name,omitempty"` + // Description of the subnet. + Description string `json:"description,omitempty"` + // The UUID of the project who owns the Subnet. Only administrative users // can specify a project UUID other than their own. TenantID string `json:"tenant_id,omitempty"` @@ -167,6 +171,9 @@ type UpdateOpts struct { // Name is a human-readable name of the subnet. Name string `json:"name,omitempty"` + // Description of the subnet. + Description *string `json:"description,omitempty"` + // AllocationPools are IP Address pools that will be available for DHCP. AllocationPools []AllocationPool `json:"allocation_pools,omitempty"` diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index 968cee1f4d..cf0397019a 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -68,6 +68,9 @@ type Subnet struct { // Human-readable name for the subnet. Might not be unique. Name string `json:"name"` + // Description for the subnet. + Description string `json:"description"` + // IP version, either `4' or `6'. IPVersion int `json:"ip_version"` From 210732b6910c6ccdb81e2ab6653ddee13255f478 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 9 Dec 2018 23:33:48 +0100 Subject: [PATCH 0604/2296] feature/shared file systems: update share (#1343) * sfs/shares: add update share with tests * Fix manila v2 shares acceptance tests * Remove useless go routine from manila share tests * Add "display" prefix in manila share update opts * Temporarily disable manila requierd ENV variables --- acceptance/README.md | 3 +- acceptance/clients/clients.go | 28 +++++--- .../sharedfilesystems/v2/sharenetworks.go | 20 ++++-- .../v2/sharenetworks_test.go | 14 ++-- .../openstack/sharedfilesystems/v2/shares.go | 37 ++++++++--- .../sharedfilesystems/v2/shares_test.go | 53 +++++++++++++++ .../sharedfilesystems/v2/shares/requests.go | 38 +++++++++++ .../sharedfilesystems/v2/shares/results.go | 9 +++ .../v2/shares/testing/fixtures.go | 64 +++++++++++++++++++ .../v2/shares/testing/request_test.go | 22 +++++++ openstack/sharedfilesystems/v2/shares/urls.go | 4 ++ script/acceptancetest | 6 ++ script/stackenv | 7 +- 13 files changed, 272 insertions(+), 33 deletions(-) diff --git a/acceptance/README.md b/acceptance/README.md index ab35695748..b2a5b35101 100644 --- a/acceptance/README.md +++ b/acceptance/README.md @@ -75,7 +75,8 @@ to set them manually. #### Shared file systems |Name|Description| |---|---| -|`OS_SHARE_NETWORK_ID`| The share network ID to use when creating shares| +|`OS_NETWORK_ID`| The network ID to use when creating shared network| +|`OS_SUBNET_ID`| The subnet ID to use when creating shared network| ### 3. Run the test suite diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 323d0bcf47..fc495c0fc4 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -38,12 +38,15 @@ type AcceptanceTestChoices struct { // NetworkName is the name of a network to launch the instance on. NetworkName string + // NetworkID is the ID of a network to launch the instance on. + NetworkID string + + // SubnetID is the ID of a subnet to launch the instance on. + SubnetID string + // ExternalNetworkID is the network ID of the external network. ExternalNetworkID string - // ShareNetworkID is the Manila Share network ID - ShareNetworkID string - // DBDatastoreType is the datastore type for DB tests. DBDatastoreType string @@ -60,9 +63,10 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { magnumImageID := os.Getenv("OS_MAGNUM_IMAGE_ID") magnumKeypair := os.Getenv("OS_MAGNUM_KEYPAIR") networkName := os.Getenv("OS_NETWORK_NAME") + networkID := os.Getenv("OS_NETWORK_ID") + subnetID := os.Getenv("OS_SUBNET_ID") floatingIPPoolName := os.Getenv("OS_POOL_NAME") externalNetworkID := os.Getenv("OS_EXTGW_ID") - shareNetworkID := os.Getenv("OS_SHARE_NETWORK_ID") dbDatastoreType := os.Getenv("OS_DB_DATASTORE_TYPE") dbDatastoreVersion := os.Getenv("OS_DB_DATASTORE_VERSION") @@ -82,12 +86,19 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { if externalNetworkID == "" { missing = append(missing, "OS_EXTGW_ID") } + + /* // Temporarily disabled, see https://github.com/gophercloud/gophercloud/issues/1345 + if networkID == "" { + missing = append(missing, "OS_NETWORK_ID") + } + if subnetID == "" { + missing = append(missing, "OS_SUBNET_ID") + } + */ + if networkName == "" { networkName = "private" } - if shareNetworkID == "" { - missing = append(missing, "OS_SHARE_NETWORK_ID") - } notDistinct := "" if flavorID == flavorIDResize { notDistinct = "OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE must be distinct." @@ -113,8 +124,9 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { MagnumImageID: magnumImageID, MagnumKeypair: magnumKeypair, NetworkName: networkName, + NetworkID: networkID, + SubnetID: subnetID, ExternalNetworkID: externalNetworkID, - ShareNetworkID: shareNetworkID, DBDatastoreType: dbDatastoreType, DBDatastoreVersion: dbDatastoreVersion, }, nil diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go index b0aefd8577..f27f0df566 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks" ) @@ -15,12 +16,19 @@ func CreateShareNetwork(t *testing.T, client *gophercloud.ServiceClient) (*share t.Skip("Skipping test that requires share network creation in short mode.") } + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + return nil, err + } + shareNetworkName := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create share network: %s", shareNetworkName) createOpts := sharenetworks.CreateOpts{ - Name: shareNetworkName, - Description: "This is a shared network", + Name: shareNetworkName, + NeutronNetID: choices.NetworkID, + NeutronSubnetID: choices.SubnetID, + Description: "This is a shared network", } shareNetwork, err := sharenetworks.Create(client, createOpts).Extract() @@ -33,13 +41,13 @@ func CreateShareNetwork(t *testing.T, client *gophercloud.ServiceClient) (*share // DeleteShareNetwork will delete a share network. An error will occur if // the share network was unable to be deleted. -func DeleteShareNetwork(t *testing.T, client *gophercloud.ServiceClient, shareNetwork *sharenetworks.ShareNetwork) { - err := sharenetworks.Delete(client, shareNetwork.ID).ExtractErr() +func DeleteShareNetwork(t *testing.T, client *gophercloud.ServiceClient, shareNetworkID string) { + err := sharenetworks.Delete(client, shareNetworkID).ExtractErr() if err != nil { - t.Fatalf("Failed to delete share network %s: %v", shareNetwork.ID, err) + t.Fatalf("Failed to delete share network %s: %v", shareNetworkID, err) } - t.Logf("Deleted share network: %s", shareNetwork.ID) + t.Logf("Deleted share network: %s", shareNetworkID) } // PrintShareNetwork will print a share network and all of its attributes. diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index 10173a5ed9..e13e1a28ea 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -31,7 +31,7 @@ func TestShareNetworkCreateDestroy(t *testing.T) { PrintShareNetwork(t, shareNetwork) - defer DeleteShareNetwork(t, client, shareNetwork) + defer DeleteShareNetwork(t, client, shareNetwork.ID) } // Create a share network and update the name and description. Get the share @@ -79,7 +79,7 @@ func TestShareNetworkUpdate(t *testing.T) { PrintShareNetwork(t, shareNetwork) - defer DeleteShareNetwork(t, client, shareNetwork) + defer DeleteShareNetwork(t, client, shareNetwork.ID) } func TestShareNetworkListDetail(t *testing.T) { @@ -115,13 +115,13 @@ func TestShareNetworkListFiltering(t *testing.T) { if err != nil { t.Fatalf("Unable to create share network: %v", err) } - defer DeleteShareNetwork(t, client, shareNetwork) + defer DeleteShareNetwork(t, client, shareNetwork.ID) shareNetwork, err = CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } - defer DeleteShareNetwork(t, client, shareNetwork) + defer DeleteShareNetwork(t, client, shareNetwork.ID) options := sharenetworks.ListOpts{ Name: shareNetwork.Name, @@ -155,13 +155,13 @@ func TestShareNetworkListPagination(t *testing.T) { if err != nil { t.Fatalf("Unable to create share network: %v", err) } - defer DeleteShareNetwork(t, client, shareNetwork) + defer DeleteShareNetwork(t, client, shareNetwork.ID) shareNetwork, err = CreateShareNetwork(t, client) if err != nil { t.Fatalf("Unable to create share network: %v", err) } - defer DeleteShareNetwork(t, client, shareNetwork) + defer DeleteShareNetwork(t, client, shareNetwork.ID) count := 0 @@ -201,7 +201,7 @@ func TestShareNetworkAddRemoveSecurityService(t *testing.T) { if err != nil { t.Fatalf("Unable to create share network: %v", err) } - defer DeleteShareNetwork(t, client, shareNetwork) + defer DeleteShareNetwork(t, client, shareNetwork.ID) options := sharenetworks.AddSecurityServiceOpts{ SecurityServiceID: securityService.ID, diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index 9aaf3e5865..12f876d274 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" ) @@ -17,26 +16,33 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share t.Skip("Skipping test that requres share creation in short mode.") } - choices, err := clients.AcceptanceTestChoicesFromEnv() + shareNetwork, err := CreateShareNetwork(t, client) if err != nil { - t.Fatalf("Unable to fetch environment information") + return nil, err } + t.Logf("Share network id %s", shareNetwork.ID) - t.Logf("Share network id %s", choices.ShareNetworkID) + iTrue := true createOpts := shares.CreateOpts{ Size: 1, Name: "My Test Share", + Description: "My Test Description", ShareProto: "NFS", - ShareNetworkID: choices.ShareNetworkID, + ShareNetworkID: shareNetwork.ID, + IsPublic: &iTrue, } share, err := shares.Create(client, createOpts).Extract() if err != nil { + t.Logf("Failed to create %s share", share.ID) + DeleteShare(t, client, share) return share, err } err = waitForStatus(client, share.ID, "available", 600) if err != nil { + t.Logf("Failed to get %s share status", share.ID) + DeleteShare(t, client, share) return share, err } @@ -60,7 +66,7 @@ func GrantAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares. return shares.GrantAccess(client, share.ID, shares.GrantAccessOpts{ AccessType: "ip", AccessTo: "0.0.0.0/32", - AccessLevel: "r", + AccessLevel: "ro", }).Extract() } @@ -83,10 +89,17 @@ func GetAccessRightsSlice(t *testing.T, client *gophercloud.ServiceClient, share func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) { err := shares.Delete(client, share.ID).ExtractErr() if err != nil { - t.Fatalf("Unable to delete share %s: %v", share.ID, err) + t.Errorf("Unable to delete share %s: %v", share.ID, err) + } + + err = waitForStatus(client, share.ID, "deleted", 600) + if err != nil { + t.Errorf("Failed to wait for 'deleted' status for %s share: %v", share.ID, err) + } else { + t.Logf("Deleted share: %s", share.ID) } - t.Logf("Deleted share: %s", share.ID) + DeleteShareNetwork(t, client, share.ShareNetworkID) } // PrintShare prints some information of the share @@ -123,6 +136,14 @@ func waitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) er return gophercloud.WaitFor(secs, func() (bool, error) { current, err := shares.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 } diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index fd8885c766..b511d47dce 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestShareCreate(t *testing.T) { @@ -27,6 +28,55 @@ func TestShareCreate(t *testing.T) { PrintShare(t, created) } +func TestShareUpdate(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create shared file system client: %v", err) + } + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create share: %v", err) + } + + defer DeleteShare(t, client, share) + + expectedShare, err := shares.Get(client, share.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve share: %v", err) + } + + name := "NewName" + description := "" + iFalse := false + options := shares.UpdateOpts{ + DisplayName: &name, + DisplayDescription: &description, + IsPublic: &iFalse, + } + + expectedShare.Name = name + expectedShare.Description = description + expectedShare.IsPublic = iFalse + + _, err = shares.Update(client, share.ID, options).Extract() + if err != nil { + t.Errorf("Unable to update share: %v", err) + } + + updatedShare, err := shares.Get(client, share.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve share: %v", err) + } + + // Update time has to be set in order to get the assert equal to pass + expectedShare.UpdatedAt = updatedShare.UpdatedAt + + PrintShare(t, share) + + th.CheckDeepEquals(t, expectedShare, updatedShare) +} + func TestShareListDetail(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { @@ -55,6 +105,7 @@ func TestGrantAndRevokeAccess(t *testing.T) { if err != nil { t.Fatalf("Unable to create a sharedfs client: %v", err) } + client.Microversion = "2.7" share, err := CreateShare(t, client) if err != nil { @@ -80,6 +131,7 @@ func TestListAccessRights(t *testing.T) { if err != nil { t.Fatalf("Unable to create a sharedfs client: %v", err) } + client.Microversion = "2.7" share, err := CreateShare(t, client) if err != nil { @@ -114,6 +166,7 @@ func TestExtendAndShrink(t *testing.T) { if err != nil { t.Fatalf("Unable to create a sharedfs client: %v", err) } + client.Microversion = "2.7" share, err := CreateShare(t, client) if err != nil { diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 529d38503b..2028b4831e 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -341,3 +341,41 @@ func Shrink(client *gophercloud.ServiceClient, id string, opts ShrinkOptsBuilder return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToShareUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Share. This object is passed +// to the share.Update function. For more information about the parameters, see +// the Share object. +type UpdateOpts struct { + // Share name. Manila share update logic doesn't have a "name" alias. + DisplayName *string `json:"display_name,omitempty"` + // Share description. Manila share update logic doesn't have a "description" alias. + DisplayDescription *string `json:"display_description,omitempty"` + // Determines whether or not the share is public + IsPublic *bool `json:"is_public,omitempty"` +} + +// ToShareUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToShareUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "share") +} + +// Update will update the Share with provided information. To extract the updated +// Share from the response, call the Extract method on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToShareUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index df26c66969..0deae373b6 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -71,6 +71,8 @@ type Share struct { SourceCgsnapshotMemberID string `json:"source_cgsnapshot_member_id"` // Timestamp when the share was created CreatedAt time.Time `json:"-"` + // Timestamp when the share was updated + UpdatedAt time.Time `json:"-"` } func (r *Share) UnmarshalJSON(b []byte) error { @@ -78,6 +80,7 @@ func (r *Share) UnmarshalJSON(b []byte) error { var s struct { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -86,6 +89,7 @@ func (r *Share) UnmarshalJSON(b []byte) error { *r = Share(s.tmp) r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) return nil } @@ -198,6 +202,11 @@ type GetResult struct { commonResult } +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + // IDFromName is a convenience function that returns a share's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { r, err := ListDetail(client, &ListOpts{Name: name}).AllPages() diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 0e60d9142e..9048e9a913 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -90,6 +90,70 @@ func MockDeleteResponse(t *testing.T) { }) } +var updateRequest = `{ + "share": { + "display_name": "my_new_test_share", + "display_description": "", + "is_public": false + } + }` + +var updateResponse = ` +{ + "share": { + "links": [ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", + "rel": "self" + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", + "rel": "bookmark" + } + ], + "availability_zone": "nova", + "share_network_id": "713df749-aac0-4a54-af52-10f6c991e80c", + "export_locations": [], + "share_server_id": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", + "share_group_id": null, + "snapshot_id": null, + "id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", + "size": 1, + "share_type": "25747776-08e5-494f-ab40-a64b9d20d8f7", + "share_type_name": "default", + "export_location": null, + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "metadata": { + "project": "my_app", + "aim": "doc" + }, + "status": "error", + "description": "", + "host": "manila2@generic1#GENERIC1", + "task_state": null, + "is_public": false, + "snapshot_support": true, + "name": "my_new_test_share", + "created_at": "2015-09-18T10:25:24.000000", + "share_proto": "NFS", + "volume_type": "default" + } +} +` + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID, 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, updateRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, updateResponse) + }) +} + var getResponse = `{ "share": { "links": [ diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index 7513c3345d..75053bd9f2 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -24,6 +24,28 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, n.ShareProto, "NFS") } +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + name := "my_new_test_share" + description := "" + iFalse := false + options := &shares.UpdateOpts{ + DisplayName: &name, + DisplayDescription: &description, + IsPublic: &iFalse, + } + n, err := shares.Update(client.ServiceClient(), shareID, options).Extract() + + th.AssertNoErr(t, err) + th.AssertEquals(t, n.Name, "my_new_test_share") + th.AssertEquals(t, n.Description, "") + th.AssertEquals(t, n.IsPublic, false) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index f50589e7d3..02fba24d2d 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -18,6 +18,10 @@ func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id) } +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id) +} + func getExportLocationsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "export_locations") } diff --git a/script/acceptancetest b/script/acceptancetest index 606f9e02d2..6bc02d9f2b 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -107,6 +107,12 @@ if [[ $? != 0 ]]; then failed=1 fi +# Shared Filesystems v2 +# Temporarily disabled, see https://github.com/gophercloud/gophercloud/issues/1345 +true || go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/sharedfilesystems/v2/ +if [[ $? != 0 ]]; then + failed=1 +fi # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then diff --git a/script/stackenv b/script/stackenv index 6a742a32f1..af7451cc87 100644 --- a/script/stackenv +++ b/script/stackenv @@ -7,18 +7,19 @@ 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 _NETWORK_ID=$(openstack network show private -c id -f value) +_SUBNET_ID=$(openstack network show private -c subnets -f value | cut -d, -f1) _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 ' ') echo export OS_IMAGE_NAME="$_IMAGE_NAME" >> openrc echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc -echo export OS_NETWORK_ID=$_NETWORK_ID >> openrc -echo export OS_EXTGW_ID=$_EXTGW_ID >> openrc +echo export OS_NETWORK_ID="$_NETWORK_ID" >> openrc +echo export OS_SUBNET_ID="$_SUBNET_ID" >> openrc +echo export OS_EXTGW_ID="$_EXTGW_ID" >> openrc 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_SHARE_NETWORK_ID=foobar >> openrc echo export OS_DOMAIN_ID=default >> openrc source openrc admin admin popd From dd064e4da516dfe710fd7b8cf0bf4431dcfa4682 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 10 Dec 2018 01:31:39 +0100 Subject: [PATCH 0605/2296] Add empty strings update tests (#1347) --- .../openstack/blockstorage/v1/blockstorage.go | 6 +- .../openstack/blockstorage/v1/volumes_test.go | 17 +++ .../openstack/blockstorage/v2/blockstorage.go | 10 +- .../openstack/blockstorage/v2/volumes_test.go | 14 ++ .../openstack/blockstorage/v3/blockstorage.go | 22 ++- .../openstack/blockstorage/v3/volumes_test.go | 14 ++ .../blockstorage/v3/volumetypes_test.go | 15 +- .../openstack/compute/v2/secgroup_test.go | 3 +- .../openstack/db/v1/configurations_test.go | 33 +++- .../openstack/dns/v2/recordsets_test.go | 5 +- acceptance/openstack/dns/v2/zones_test.go | 5 +- acceptance/openstack/identity/v2/identity.go | 3 + .../openstack/identity/v2/tenant_test.go | 5 +- .../openstack/identity/v3/domains_test.go | 9 +- .../openstack/identity/v3/groups_test.go | 15 +- acceptance/openstack/identity/v3/identity.go | 3 + .../openstack/identity/v3/projects_test.go | 8 +- .../openstack/identity/v3/regions_test.go | 4 +- .../openstack/identity/v3/users_test.go | 13 +- .../openstack/loadbalancer/v2/loadbalancer.go | 8 + .../loadbalancer/v2/loadbalancers_test.go | 64 ++++++-- .../networking/v2/extensions/extensions.go | 14 +- .../v2/extensions/fwaas/firewall_test.go | 18 ++- .../networking/v2/extensions/fwaas/fwaas.go | 15 +- .../v2/extensions/fwaas/policy_test.go | 7 +- .../networking/v2/extensions/layer3/layer3.go | 10 ++ .../v2/extensions/layer3/routers_test.go | 12 +- .../v2/extensions/lbaas/members_test.go | 4 +- .../v2/extensions/lbaas/monitors_test.go | 4 +- .../v2/extensions/lbaas/pools_test.go | 4 +- .../v2/extensions/lbaas/vips_test.go | 4 +- .../v2/extensions/lbaas_v2/lbaas_v2.go | 17 +++ .../extensions/lbaas_v2/loadbalancers_test.go | 22 ++- .../extensions/portsbinding/portsbinding.go | 3 + .../portsbinding/portsbinding_test.go | 8 +- .../networking/v2/extensions/security_test.go | 6 +- .../v2/extensions/subnetpools/subnetpools.go | 7 +- .../subnetpools/subnetpools_test.go | 6 +- .../v2/extensions/trunks/trunks_test.go | 13 +- .../openstack/networking/v2/networking.go | 21 ++- .../openstack/networking/v2/networks_test.go | 6 +- .../openstack/networking/v2/ports_test.go | 9 +- .../openstack/networking/v2/subnets_test.go | 6 +- .../sharedfilesystems/v2/securityservices.go | 6 +- .../v2/securityservices_test.go | 14 +- .../v2/sharenetworks_test.go | 10 +- script/acceptancetest | 144 +++++++----------- 47 files changed, 480 insertions(+), 186 deletions(-) diff --git a/acceptance/openstack/blockstorage/v1/blockstorage.go b/acceptance/openstack/blockstorage/v1/blockstorage.go index 41f24e1ab2..c2b35333f5 100644 --- a/acceptance/openstack/blockstorage/v1/blockstorage.go +++ b/acceptance/openstack/blockstorage/v1/blockstorage.go @@ -50,11 +50,13 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol } volumeName := tools.RandomString("ACPTTEST", 16) + volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, + Size: 1, + Name: volumeName, + Description: volumeDescription, } volume, err := volumes.Create(client, createOpts).Extract() diff --git a/acceptance/openstack/blockstorage/v1/volumes_test.go b/acceptance/openstack/blockstorage/v1/volumes_test.go index 9a555009fb..bdbadf1d56 100644 --- a/acceptance/openstack/blockstorage/v1/volumes_test.go +++ b/acceptance/openstack/blockstorage/v1/volumes_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestVolumesList(t *testing.T) { @@ -49,4 +50,20 @@ func TestVolumesCreateDestroy(t *testing.T) { } tools.PrintResource(t, newVolume) + th.AssertEquals(t, volume.Name, newVolume.Name) + th.AssertEquals(t, volume.Description, newVolume.Description) + + // Update volume + updatedVolumeName := "" + updatedVolumeDescription := "" + updateOpts := volumes.UpdateOpts{ + Name: &updatedVolumeName, + Description: &updatedVolumeDescription, + } + updatedVolume, err := volumes.Update(client, volume.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedVolume) + th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) + th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) } diff --git a/acceptance/openstack/blockstorage/v2/blockstorage.go b/acceptance/openstack/blockstorage/v2/blockstorage.go index b05ac230ad..b6b93b4c1a 100644 --- a/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -46,11 +46,13 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol // error will be returned if the volume was unable to be created. func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { volumeName := tools.RandomString("ACPTTEST", 16) + volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, + Size: 1, + Name: volumeName, + Description: volumeDescription, } volume, err := volumes.Create(client, createOpts).Extract() @@ -63,11 +65,11 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } + tools.PrintResource(t, volume) th.AssertEquals(t, volume.Name, volumeName) + th.AssertEquals(t, volume.Description, volumeDescription) th.AssertEquals(t, volume.Size, 1) - tools.PrintResource(t, volume) - t.Logf("Successfully created volume: %s", volume.ID) return volume, nil diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go index 042f77a629..f3b164ca0c 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -27,6 +27,20 @@ func TestVolumesCreateDestroy(t *testing.T) { newVolume, err := volumes.Get(client, volume.ID).Extract() th.AssertNoErr(t, err) + // Update volume + updatedVolumeName := "" + updatedVolumeDescription := "" + updateOpts := volumes.UpdateOpts{ + Name: &updatedVolumeName, + Description: &updatedVolumeDescription, + } + updatedVolume, err := volumes.Update(client, volume.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedVolume) + th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) + th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) + allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index b842a54327..3eccab04af 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -37,9 +37,9 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol return snapshot, err } + tools.PrintResource(t, snapshot) th.AssertEquals(t, snapshot.Name, snapshotName) th.AssertEquals(t, snapshot.VolumeID, volume.ID) - tools.PrintResource(t, snapshot) t.Logf("Successfully created snapshot: %s", snapshot.ID) @@ -50,11 +50,13 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol // error will be returned if the volume was unable to be created. func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { volumeName := tools.RandomString("ACPTTEST", 16) + volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) t.Logf("Attempting to create volume: %s", volumeName) createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, + Size: 1, + Name: volumeName, + Description: volumeDescription, } volume, err := volumes.Create(client, createOpts).Extract() @@ -67,8 +69,10 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } - th.AssertEquals(t, volume.Name, volumeName) tools.PrintResource(t, volume) + th.AssertEquals(t, volume.Name, volumeName) + th.AssertEquals(t, volume.Description, volumeDescription) + th.AssertEquals(t, volume.Size, 1) t.Logf("Successfully created volume: %s", volume.ID) @@ -79,12 +83,13 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol // error will be returned if the volume was unable to be created. func CreateVolumeType(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{"volume_backend_name": "fake_backend_name"}, - Description: "create_from_gophercloud", + Description: description, } vt, err := volumetypes.Create(client, createOpts).Extract() @@ -92,8 +97,13 @@ func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumet return nil, err } - th.AssertEquals(t, vt.IsPublic, true) tools.PrintResource(t, vt) + th.AssertEquals(t, vt.IsPublic, true) + th.AssertEquals(t, vt.Name, name) + th.AssertEquals(t, vt.Description, description) + // TODO: For some reason returned extra_specs are empty even in API reference: https://developer.openstack.org/api-ref/block-storage/v3/?expanded=create-a-volume-type-detail#volume-types-types + // "extra_specs": {} + // th.AssertEquals(t, vt.ExtraSpecs, createOpts.ExtraSpecs) t.Logf("Successfully created volume type: %s", vt.ID) diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index edbcd93ce9..999ec1a61e 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -28,6 +28,20 @@ func TestVolumes(t *testing.T) { th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume2) + // Update volume + updatedVolumeName := "" + updatedVolumeDescription := "" + updateOpts := volumes.UpdateOpts{ + Name: &updatedVolumeName, + Description: &updatedVolumeDescription, + } + updatedVolume, err := volumes.Update(client, volume1.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedVolume) + th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) + th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) + listOpts := volumes.ListOpts{ Limit: 1, } diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index cfcb93620e..99657d794b 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -37,17 +37,20 @@ func TestVolumeTypes(t *testing.T) { th.AssertEquals(t, found, true) - var isPublic = false - var name = vt.Name + "-UPDATED" + isPublic := false + name := vt.Name + "-UPDATED" + description := "" updateOpts := volumetypes.UpdateOpts{ - Name: &name, - IsPublic: &isPublic, + Name: &name, + Description: &description, + IsPublic: &isPublic, } newVT, err := volumetypes.Update(client, vt.ID, updateOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, vt.Name+"-UPDATED", newVT.Name) - th.AssertEquals(t, false, newVT.IsPublic) tools.PrintResource(t, newVT) + th.AssertEquals(t, name, newVT.Name) + th.AssertEquals(t, description, newVT.Description) + th.AssertEquals(t, isPublic, newVT.IsPublic) } diff --git a/acceptance/openstack/compute/v2/secgroup_test.go b/acceptance/openstack/compute/v2/secgroup_test.go index 047af24728..4404665711 100644 --- a/acceptance/openstack/compute/v2/secgroup_test.go +++ b/acceptance/openstack/compute/v2/secgroup_test.go @@ -45,7 +45,7 @@ func TestSecGroupsCRUD(t *testing.T) { tools.PrintResource(t, securityGroup) newName := tools.RandomString("secgroup_", 4) - description := tools.RandomString("dec_", 10) + description := "" updateOpts := secgroups.UpdateOpts{ Name: newName, Description: &description, @@ -58,6 +58,7 @@ func TestSecGroupsCRUD(t *testing.T) { t.Logf("Updated %s's name to %s", updatedSecurityGroup.ID, updatedSecurityGroup.Name) th.AssertEquals(t, updatedSecurityGroup.Name, newName) + th.AssertEquals(t, updatedSecurityGroup.Description, description) } func TestSecGroupsRuleCreate(t *testing.T) { diff --git a/acceptance/openstack/db/v1/configurations_test.go b/acceptance/openstack/db/v1/configurations_test.go index 87e883f458..ed5041702c 100644 --- a/acceptance/openstack/db/v1/configurations_test.go +++ b/acceptance/openstack/db/v1/configurations_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestConfigurationsCRUD(t *testing.T) { @@ -41,10 +42,38 @@ func TestConfigurationsCRUD(t *testing.T) { t.Fatalf("Unable to create configuration: %v", err) } + readCgroup, err := configurations.Get(client, cgroup.ID).Extract() + if err != nil { + t.Fatalf("Unable to read configuration: %v", err) + } + + tools.PrintResource(t, readCgroup) + th.AssertEquals(t, readCgroup.Name, createOpts.Name) + th.AssertEquals(t, readCgroup.Description, createOpts.Description) + // TODO: verify datastore + //th.AssertDeepEquals(t, readCgroup.Datastore, datastore) + + // Update cgroup + newCgroupName := "New configuration name" + newCgroupDescription := "" + updateOpts := configurations.UpdateOpts{ + Name: newCgroupName, + Description: &newCgroupDescription, + } + err = configurations.Update(client, cgroup.ID, updateOpts).ExtractErr() + th.AssertNoErr(t, err) + + newCgroup, err := configurations.Get(client, cgroup.ID).Extract() + if err != nil { + t.Fatalf("Unable to read updated configuration: %v", err) + } + + tools.PrintResource(t, newCgroup) + th.AssertEquals(t, newCgroup.Name, newCgroupName) + th.AssertEquals(t, newCgroup.Description, newCgroupDescription) + err = configurations.Delete(client, cgroup.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete configuration: %v", err) } - - tools.PrintResource(t, cgroup) } diff --git a/acceptance/openstack/dns/v2/recordsets_test.go b/acceptance/openstack/dns/v2/recordsets_test.go index d2d862ba55..8cd4b7231d 100644 --- a/acceptance/openstack/dns/v2/recordsets_test.go +++ b/acceptance/openstack/dns/v2/recordsets_test.go @@ -72,8 +72,9 @@ func TestRecordSetsCRUD(t *testing.T) { tools.PrintResource(t, &rs) + description := "" updateOpts := recordsets.UpdateOpts{ - Description: "New description", + Description: &description, TTL: 0, } @@ -82,5 +83,5 @@ func TestRecordSetsCRUD(t *testing.T) { tools.PrintResource(t, &newRS) - th.AssertEquals(t, newRS.Description, "New description") + th.AssertEquals(t, newRS.Description, description) } diff --git a/acceptance/openstack/dns/v2/zones_test.go b/acceptance/openstack/dns/v2/zones_test.go index 263e9fea0a..e07867e9be 100644 --- a/acceptance/openstack/dns/v2/zones_test.go +++ b/acceptance/openstack/dns/v2/zones_test.go @@ -40,8 +40,9 @@ func TestZonesCRUD(t *testing.T) { th.AssertEquals(t, found, true) + description := "" updateOpts := zones.UpdateOpts{ - Description: "New description", + Description: &description, TTL: 0, } @@ -50,5 +51,5 @@ func TestZonesCRUD(t *testing.T) { tools.PrintResource(t, &newZone) - th.AssertEquals(t, newZone.Description, "New description") + th.AssertEquals(t, newZone.Description, description) } diff --git a/acceptance/openstack/identity/v2/identity.go b/acceptance/openstack/identity/v2/identity.go index b8e9ee2207..f74812193b 100644 --- a/acceptance/openstack/identity/v2/identity.go +++ b/acceptance/openstack/identity/v2/identity.go @@ -34,6 +34,7 @@ func AddUserRole(t *testing.T, client *gophercloud.ServiceClient, tenant *tenant // unable to be created. func CreateTenant(t *testing.T, client *gophercloud.ServiceClient, c *tenants.CreateOpts) (*tenants.Tenant, error) { name := tools.RandomString("ACPTTEST", 8) + description := tools.RandomString("ACPTTEST-DESC", 8) t.Logf("Attempting to create tenant: %s", name) var createOpts tenants.CreateOpts @@ -44,6 +45,7 @@ func CreateTenant(t *testing.T, client *gophercloud.ServiceClient, c *tenants.Cr } createOpts.Name = name + createOpts.Description = description tenant, err := tenants.Create(client, createOpts).Extract() if err != nil { @@ -53,6 +55,7 @@ func CreateTenant(t *testing.T, client *gophercloud.ServiceClient, c *tenants.Cr t.Logf("Successfully created project %s with ID %s", name, tenant.ID) th.AssertEquals(t, name, tenant.Name) + th.AssertEquals(t, description, tenant.Description) return tenant, nil } diff --git a/acceptance/openstack/identity/v2/tenant_test.go b/acceptance/openstack/identity/v2/tenant_test.go index 13a1c08c9e..f53270760a 100644 --- a/acceptance/openstack/identity/v2/tenant_test.go +++ b/acceptance/openstack/identity/v2/tenant_test.go @@ -52,8 +52,9 @@ func TestTenantsCRUD(t *testing.T) { tools.PrintResource(t, tenant) + description := "" updateOpts := tenants.UpdateOpts{ - Description: "some tenant", + Description: &description, } newTenant, err := tenants.Update(client, tenant.ID, updateOpts).Extract() @@ -61,5 +62,5 @@ func TestTenantsCRUD(t *testing.T) { tools.PrintResource(t, newTenant) - th.AssertEquals(t, newTenant.Description, "some tenant") + th.AssertEquals(t, newTenant.Description, description) } diff --git a/acceptance/openstack/identity/v3/domains_test.go b/acceptance/openstack/identity/v3/domains_test.go index 36062c9c99..71a335ea88 100644 --- a/acceptance/openstack/identity/v3/domains_test.go +++ b/acceptance/openstack/identity/v3/domains_test.go @@ -61,8 +61,9 @@ func TestDomainsCRUD(t *testing.T) { th.AssertNoErr(t, err) var iTrue bool = true + var description = "Testing Domain" createOpts := domains.CreateOpts{ - Description: "Testing Domain", + Description: description, Enabled: &iTrue, } @@ -72,10 +73,10 @@ func TestDomainsCRUD(t *testing.T) { tools.PrintResource(t, domain) - th.AssertEquals(t, domain.Description, "Testing Domain") + th.AssertEquals(t, domain.Description, description) var iFalse bool = false - var description = "Staging Test Domain" + description = "" updateOpts := domains.UpdateOpts{ Description: &description, Enabled: &iFalse, @@ -86,5 +87,5 @@ func TestDomainsCRUD(t *testing.T) { tools.PrintResource(t, newDomain) - th.AssertEquals(t, newDomain.Description, "Staging Test Domain") + th.AssertEquals(t, newDomain.Description, description) } diff --git a/acceptance/openstack/identity/v3/groups_test.go b/acceptance/openstack/identity/v3/groups_test.go index 101524f136..c168817882 100644 --- a/acceptance/openstack/identity/v3/groups_test.go +++ b/acceptance/openstack/identity/v3/groups_test.go @@ -17,9 +17,11 @@ func TestGroupCRUD(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) + description := "Test Groups" + domainID := "default" createOpts := groups.CreateOpts{ - Name: "testgroup", - DomainID: "default", + Description: description, + DomainID: domainID, Extra: map[string]interface{}{ "email": "testgroup@example.com", }, @@ -33,7 +35,11 @@ func TestGroupCRUD(t *testing.T) { tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) - var description = "Test Groups" + th.AssertEquals(t, group.Description, description) + th.AssertEquals(t, group.DomainID, domainID) + th.AssertDeepEquals(t, group.Extra, createOpts.Extra) + + description = "" updateOpts := groups.UpdateOpts{ Description: &description, Extra: map[string]interface{}{ @@ -47,6 +53,9 @@ func TestGroupCRUD(t *testing.T) { tools.PrintResource(t, newGroup) tools.PrintResource(t, newGroup.Extra) + th.AssertEquals(t, newGroup.Description, description) + th.AssertDeepEquals(t, newGroup.Extra, updateOpts.Extra) + listOpts := groups.ListOpts{ DomainID: "default", } diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 88dee1ec93..718e5bc7ee 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -21,6 +21,7 @@ import ( // unable to be created. func CreateProject(t *testing.T, client *gophercloud.ServiceClient, c *projects.CreateOpts) (*projects.Project, error) { name := tools.RandomString("ACPTTEST", 8) + description := tools.RandomString("ACPTTEST-DESC", 8) t.Logf("Attempting to create project: %s", name) var createOpts projects.CreateOpts @@ -31,6 +32,7 @@ func CreateProject(t *testing.T, client *gophercloud.ServiceClient, c *projects. } createOpts.Name = name + createOpts.Description = description project, err := projects.Create(client, createOpts).Extract() if err != nil { @@ -40,6 +42,7 @@ func CreateProject(t *testing.T, client *gophercloud.ServiceClient, c *projects. t.Logf("Successfully created project %s with ID %s", name, project.ID) th.AssertEquals(t, project.Name, name) + th.AssertEquals(t, project.Description, description) return project, nil } diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index 7256b80d81..38848be1ed 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -117,15 +117,19 @@ func TestProjectsCRUD(t *testing.T) { tools.PrintResource(t, project) - var iFalse bool = false + description := "" + iFalse := false updateOpts := projects.UpdateOpts{ - Enabled: &iFalse, + Description: &description, + Enabled: &iFalse, } updatedProject, err := projects.Update(client, project.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) + th.AssertEquals(t, updatedProject.Description, description) + th.AssertEquals(t, updatedProject.Enabled, iFalse) } func TestProjectsDomain(t *testing.T) { diff --git a/acceptance/openstack/identity/v3/regions_test.go b/acceptance/openstack/identity/v3/regions_test.go index 0457269c9c..f4a0b9456b 100644 --- a/acceptance/openstack/identity/v3/regions_test.go +++ b/acceptance/openstack/identity/v3/regions_test.go @@ -75,7 +75,7 @@ func TestRegionsCRUD(t *testing.T) { tools.PrintResource(t, region) tools.PrintResource(t, region.Extra) - var description = "Region A for testing" + var description = "" updateOpts := regions.UpdateOpts{ Description: &description, /* @@ -94,4 +94,6 @@ func TestRegionsCRUD(t *testing.T) { tools.PrintResource(t, newRegion) tools.PrintResource(t, newRegion.Extra) + + th.AssertEquals(t, newRegion.Description, description) } diff --git a/acceptance/openstack/identity/v3/users_test.go b/acceptance/openstack/identity/v3/users_test.go index 5bf2fcb5fb..2e283b5ec0 100644 --- a/acceptance/openstack/identity/v3/users_test.go +++ b/acceptance/openstack/identity/v3/users_test.go @@ -122,6 +122,7 @@ func TestUserCRUD(t *testing.T) { createOpts := users.CreateOpts{ DefaultProjectID: project.ID, + Description: "test description", Password: "foobar", DomainID: "default", Options: map[users.Option]interface{}{ @@ -143,9 +144,16 @@ func TestUserCRUD(t *testing.T) { tools.PrintResource(t, user) tools.PrintResource(t, user.Extra) + th.AssertEquals(t, user.Description, createOpts.Description) + th.AssertEquals(t, user.DomainID, createOpts.DomainID) + iFalse := false + name := "newtestuser" + description := "" updateOpts := users.UpdateOpts{ - Enabled: &iFalse, + Name: name, + Description: &description, + Enabled: &iFalse, Options: map[users.Option]interface{}{ users.MultiFactorAuthRules: nil, }, @@ -160,6 +168,9 @@ func TestUserCRUD(t *testing.T) { tools.PrintResource(t, newUser) tools.PrintResource(t, newUser.Extra) + th.AssertEquals(t, newUser.Name, name) + th.AssertEquals(t, newUser.Description, description) + th.AssertEquals(t, newUser.Enabled, iFalse) th.AssertEquals(t, newUser.Extra["disabled_reason"], "DDOS") } diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index eb2452e356..1c7e68176f 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -22,12 +22,14 @@ const loadbalancerDeleteTimeoutSeconds = 300 // be created. func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) { listenerName := tools.RandomString("TESTACCT-", 8) + listenerDescription := tools.RandomString("TESTACCT-DESC-", 8) listenerPort := tools.RandomInt(1, 100) t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) createOpts := listeners.CreateOpts{ Name: listenerName, + Description: listenerDescription, LoadbalancerID: lb.ID, Protocol: "TCP", ProtocolPort: listenerPort, @@ -51,11 +53,13 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal // subnet. An error will be returned if the loadbalancer could not be created. func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*loadbalancers.LoadBalancer, error) { lbName := tools.RandomString("TESTACCT-", 8) + lbDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) createOpts := loadbalancers.CreateOpts{ Name: lbName, + Description: lbDescription, VipSubnetID: subnetID, AdminStateUp: gophercloud.Enabled, } @@ -149,11 +153,13 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala // created. func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) { poolName := tools.RandomString("TESTACCT-", 8) + poolDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create pool %s", poolName) createOpts := pools.CreateOpts{ Name: poolName, + Description: poolDescription, Protocol: pools.ProtocolTCP, LoadbalancerID: lb.ID, LBMethod: pools.LBMethodLeastConnections, @@ -178,11 +184,13 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance // created. func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) { policyName := tools.RandomString("TESTACCT-", 8) + policyDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create l7 policy %s", policyName) createOpts := l7policies.CreateOpts{ Name: policyName, + Description: policyDescription, ListenerID: listener.ID, Action: l7policies.ActionRedirectToURL, RedirectURL: "http://www.example.com", diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 8dd24d3028..b8c2034f9f 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -13,6 +13,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestLoadbalancersList(t *testing.T) { @@ -65,6 +66,23 @@ func TestLoadbalancersCRUD(t *testing.T) { } defer DeleteLoadBalancer(t, lbClient, lb.ID) + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + lbDescription := "" + updateLoadBalancerOpts := loadbalancers.UpdateOpts{ + Description: &lbDescription, + } + _, err = loadbalancers.Update(lbClient, lb.ID, updateLoadBalancerOpts).Extract() + if err != nil { + t.Fatalf("Unable to update loadbalancer %v: ", err) + } + + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() if err != nil { t.Fatalf("Unable to get loadbalancer: %v", err) @@ -72,6 +90,8 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newLB) + th.AssertEquals(t, newLB.Description, lbDescription) + lbStats, err := loadbalancers.GetStats(lbClient, lb.ID).Extract() if err != nil { t.Fatalf("Unable to get loadbalancer's statistics: %v", err) @@ -89,15 +109,20 @@ func TestLoadbalancersCRUD(t *testing.T) { } defer DeleteListener(t, lbClient, lb.ID, listener.ID) + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + listenerDescription := "" updateListenerOpts := listeners.UpdateOpts{ - Description: "Some listener description", + Description: &listenerDescription, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() if err != nil { t.Fatalf("Unable to update listener") } - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -108,6 +133,8 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newListener) + th.AssertEquals(t, newListener.Description, listenerDescription) + listenerStats, err := listeners.GetStats(lbClient, listener.ID).Extract() if err != nil { t.Fatalf("Unable to get listener's statistics: %v", err) @@ -122,7 +149,7 @@ func TestLoadbalancersCRUD(t *testing.T) { } defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) - newDescription := "New l7 policy description" + newDescription := "" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, } @@ -131,7 +158,7 @@ func TestLoadbalancersCRUD(t *testing.T) { t.Fatalf("Unable to update l7 policy") } - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -142,6 +169,8 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newPolicy) + th.AssertEquals(t, newPolicy.Description, newDescription) + // L7 rule rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) if err != nil { @@ -171,7 +200,7 @@ func TestLoadbalancersCRUD(t *testing.T) { t.Fatalf("Unable to update l7 rule: %v", err) } - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -189,15 +218,16 @@ func TestLoadbalancersCRUD(t *testing.T) { } defer DeletePool(t, lbClient, lb.ID, pool.ID) + poolDescription := "" updatePoolOpts := pools.UpdateOpts{ - Description: "Some pool description", + Description: &poolDescription, } _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() if err != nil { t.Fatalf("Unable to update pool") } - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -208,6 +238,8 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newPool) + th.AssertEquals(t, newPool.Description, poolDescription) + // Member member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) if err != nil { @@ -215,8 +247,10 @@ func TestLoadbalancersCRUD(t *testing.T) { } defer DeleteMember(t, lbClient, lb.ID, pool.ID, member.ID) + memberName := "" newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ + Name: &memberName, Weight: &newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() @@ -224,7 +258,7 @@ func TestLoadbalancersCRUD(t *testing.T) { t.Fatalf("Unable to update pool") } - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -234,6 +268,7 @@ func TestLoadbalancersCRUD(t *testing.T) { } tools.PrintResource(t, newMember) + th.AssertEquals(t, newMember.Name, memberName) newWeight = tools.RandomInt(11, 100) memberOpts := pools.BatchUpdateMemberOpts{ @@ -246,7 +281,7 @@ func TestLoadbalancersCRUD(t *testing.T) { t.Fatalf("Unable to batch update members") } - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -264,8 +299,10 @@ func TestLoadbalancersCRUD(t *testing.T) { } defer DeleteMonitor(t, lbClient, lb.ID, monitor.ID) + monName := "" newDelay := tools.RandomInt(20, 30) updateMonitorOpts := monitors.UpdateOpts{ + Name: &monName, Delay: newDelay, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() @@ -273,7 +310,7 @@ func TestLoadbalancersCRUD(t *testing.T) { t.Fatalf("Unable to update monitor") } - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -284,6 +321,7 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newMonitor) + th.AssertEquals(t, newMonitor.Name, monName) } func TestLoadbalancersCascadeCRUD(t *testing.T) { @@ -331,8 +369,9 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { t.Fatalf("Unable to create listener: %v", err) } + listenerDescription := "Some listener description" updateListenerOpts := listeners.UpdateOpts{ - Description: "Some listener description", + Description: &listenerDescription, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() if err != nil { @@ -356,8 +395,9 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { t.Fatalf("Unable to create pool: %v", err) } + poolDescription := "Some pool description" updatePoolOpts := pools.UpdateOpts{ - Description: "Some pool description", + Description: &poolDescription, } _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() if err != nil { diff --git a/acceptance/openstack/networking/v2/extensions/extensions.go b/acceptance/openstack/networking/v2/extensions/extensions.go index 69b846750f..a05d0ed78f 100644 --- a/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/acceptance/openstack/networking/v2/extensions/extensions.go @@ -17,6 +17,7 @@ import ( // returned if the creation failed. func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) { networkName := tools.RandomString("TESTACC-", 8) + networkDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create external network: %s", networkName) @@ -25,6 +26,7 @@ func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*ne networkCreateOpts := networks.CreateOpts{ Name: networkName, + Description: networkDescription, AdminStateUp: &adminStateUp, } @@ -41,6 +43,7 @@ func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*ne t.Logf("Created external network: %s", networkName) th.AssertEquals(t, network.Name, networkName) + th.AssertEquals(t, network.Description, networkDescription) return network, nil } @@ -49,6 +52,7 @@ func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*ne // attached. An error will be returned if the port could not be created. func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, secGroupID string) (*ports.Port, error) { portName := tools.RandomString("TESTACC-", 8) + portDescription := tools.RandomString("TESTACC-DESC-", 8) iFalse := false t.Logf("Attempting to create port: %s", portName) @@ -56,6 +60,7 @@ func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient createOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, + Description: portDescription, AdminStateUp: &iFalse, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, SecurityGroups: &[]string{secGroupID}, @@ -69,6 +74,7 @@ func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient t.Logf("Successfully created port: %s", portName) th.AssertEquals(t, port.Name, portName) + th.AssertEquals(t, port.Description, portDescription) th.AssertEquals(t, port.NetworkID, networkID) return port, nil @@ -78,11 +84,13 @@ func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient // An error will be returned if one was failed to be created. func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*groups.SecGroup, error) { secGroupName := tools.RandomString("TESTACC-", 8) + secGroupDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create security group: %s", secGroupName) createOpts := groups.CreateOpts{ - Name: secGroupName, + Name: secGroupName, + Description: secGroupDescription, } secGroup, err := groups.Create(client, createOpts).Extract() @@ -93,6 +101,7 @@ func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*grou t.Logf("Created security group: %s", secGroup.ID) th.AssertEquals(t, secGroup.Name, secGroupName) + th.AssertEquals(t, secGroup.Description, secGroupDescription) return secGroup, nil } @@ -103,10 +112,12 @@ func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*grou func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) (*rules.SecGroupRule, error) { t.Logf("Attempting to create security group rule in group: %s", secGroupID) + description := "Rule description" fromPort := tools.RandomInt(80, 89) toPort := tools.RandomInt(90, 99) createOpts := rules.CreateOpts{ + Description: description, Direction: "ingress", EtherType: "IPv4", SecGroupID: secGroupID, @@ -123,6 +134,7 @@ func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, se t.Logf("Created security group rule: %s", rule.ID) th.AssertEquals(t, rule.SecGroupID, secGroupID) + th.AssertEquals(t, rule.Description, description) return rule, nil } diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go index 7b32f9a2bd..4779491fda 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go @@ -39,18 +39,24 @@ func TestFirewallCRUD(t *testing.T) { tools.PrintResource(t, firewall) - updateOpts := firewalls.UpdateOpts{ + fwName := "" + fwDescription := "" + fwUpdateOpts := firewalls.UpdateOpts{ + Name: &fwName, + Description: &fwDescription, PolicyID: policy.ID, - Description: "Some firewall description", } - _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() + _, err = firewalls.Update(client, firewall.ID, fwUpdateOpts).Extract() th.AssertNoErr(t, err) newFirewall, err := firewalls.Get(client, firewall.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) + th.AssertEquals(t, newFirewall.Name, fwName) + th.AssertEquals(t, newFirewall.Description, fwDescription) + th.AssertEquals(t, newFirewall.PolicyID, policy.ID) allPages, err := firewalls.List(client, nil).AllPages() th.AssertNoErr(t, err) @@ -98,9 +104,10 @@ func TestFirewallCRUDRouter(t *testing.T) { th.AssertNoErr(t, err) defer layer3.DeleteRouter(t, client, router2.ID) + description := "Some firewall description" firewallUpdateOpts := firewalls.UpdateOpts{ PolicyID: policy.ID, - Description: "Some firewall description", + Description: &description, } updateOpts := routerinsertion.UpdateOptsExt{ @@ -143,9 +150,10 @@ func TestFirewallCRUDRemoveRouter(t *testing.T) { tools.PrintResource(t, firewall) + description := "Some firewall description" firewallUpdateOpts := firewalls.UpdateOpts{ PolicyID: policy.ID, - Description: "Some firewall description", + Description: &description, } updateOpts := routerinsertion.UpdateOptsExt{ diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index c7fbd4ce40..66065bd2c8 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -18,12 +18,14 @@ import ( // policy ID. An error will be returned if the firewall could not be created. func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*firewalls.Firewall, error) { firewallName := tools.RandomString("TESTACC-", 8) + firewallDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create firewall %s", firewallName) iTrue := true createOpts := firewalls.CreateOpts{ Name: firewallName, + Description: firewallDescription, PolicyID: policyID, AdminStateUp: &iTrue, } @@ -41,6 +43,7 @@ func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID st t.Logf("Successfully created firewall %s", firewallName) th.AssertEquals(t, firewall.Name, firewallName) + th.AssertEquals(t, firewall.Description, firewallDescription) return firewall, nil } @@ -50,12 +53,14 @@ func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID st // returned if the firewall could not be created. func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, policyID string, routerID string) (*firewalls.Firewall, error) { firewallName := tools.RandomString("TESTACC-", 8) + firewallDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create firewall %s", firewallName) firewallCreateOpts := firewalls.CreateOpts{ - Name: firewallName, - PolicyID: policyID, + Name: firewallName, + Description: firewallDescription, + PolicyID: policyID, } createOpts := routerinsertion.CreateOptsExt{ @@ -76,6 +81,7 @@ func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, pol t.Logf("Successfully created firewall %s", firewallName) th.AssertEquals(t, firewall.Name, firewallName) + th.AssertEquals(t, firewall.Description, firewallDescription) return firewall, nil } @@ -84,11 +90,13 @@ func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, pol // rule. An error will be returned if the rule could not be created. func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string) (*policies.Policy, error) { policyName := tools.RandomString("TESTACC-", 8) + policyDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create policy %s", policyName) createOpts := policies.CreateOpts{ - Name: policyName, + Name: policyName, + Description: policyDescription, Rules: []string{ ruleID, }, @@ -102,6 +110,7 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string t.Logf("Successfully created policy %s", policyName) th.AssertEquals(t, policy.Name, policyName) + th.AssertEquals(t, policy.Description, policyDescription) th.AssertEquals(t, len(policy.Rules), 1) return policy, nil diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go index 6a7e7e4a21..ab0d7c9008 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go @@ -27,8 +27,11 @@ func TestPolicyCRUD(t *testing.T) { tools.PrintResource(t, policy) + name := "" + description := "" updateOpts := policies.UpdateOpts{ - Description: "Some policy description", + Name: &name, + Description: &description, } _, err = policies.Update(client, policy.ID, updateOpts).Extract() @@ -38,6 +41,8 @@ func TestPolicyCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) + th.AssertEquals(t, newPolicy.Name, name) + th.AssertEquals(t, newPolicy.Description, description) allPages, err := policies.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 29213e90f6..74dd00c33c 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -17,7 +17,9 @@ import ( func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, networkID, portID string) (*floatingips.FloatingIP, error) { t.Logf("Attempting to create floating IP on port: %s", portID) + fipDescription := "Test floating IP" createOpts := &floatingips.CreateOpts{ + Description: fipDescription, FloatingNetworkID: networkID, PortID: portID, } @@ -29,6 +31,8 @@ func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, networkID t.Logf("Created floating IP.") + th.AssertEquals(t, floatingIP.Description, fipDescription) + return floatingIP, err } @@ -43,6 +47,7 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou } routerName := tools.RandomString("TESTACC-", 8) + routerDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create external router: %s", routerName) @@ -55,6 +60,7 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou createOpts := routers.CreateOpts{ Name: routerName, + Description: routerDescription, AdminStateUp: &adminStateUp, GatewayInfo: &gatewayInfo, } @@ -71,6 +77,7 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou t.Logf("Created router: %s", routerName) th.AssertEquals(t, router.Name, routerName) + th.AssertEquals(t, router.Description, routerDescription) return router, nil } @@ -79,12 +86,14 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou // returned if the creation failed. func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*routers.Router, error) { routerName := tools.RandomString("TESTACC-", 8) + routerDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create router: %s", routerName) adminStateUp := true createOpts := routers.CreateOpts{ Name: routerName, + Description: routerDescription, AdminStateUp: &adminStateUp, } @@ -100,6 +109,7 @@ func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID str t.Logf("Created router: %s", routerName) th.AssertEquals(t, router.Name, routerName) + th.AssertEquals(t, router.Description, routerDescription) return router, nil } diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 3801c091c4..f79aa5a3ec 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -27,8 +27,10 @@ func TestLayer3RouterCreateDelete(t *testing.T) { tools.PrintResource(t, router) newName := tools.RandomString("TESTACC-", 8) + newDescription := "" updateOpts := routers.UpdateOpts{ - Name: newName, + Name: newName, + Description: &newDescription, } _, err = routers.Update(client, router.ID, updateOpts).Extract() @@ -38,6 +40,8 @@ func TestLayer3RouterCreateDelete(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newRouter) + th.AssertEquals(t, newRouter.Name, newName) + th.AssertEquals(t, newRouter.Description, newDescription) listOpts := routers.ListOpts{} allPages, err := routers.List(client, listOpts).AllPages() @@ -69,8 +73,10 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { tools.PrintResource(t, router) newName := tools.RandomString("TESTACC-", 8) + newDescription := "" updateOpts := routers.UpdateOpts{ - Name: newName, + Name: newName, + Description: &newDescription, } _, err = routers.Update(client, router.ID, updateOpts).Extract() @@ -80,6 +86,8 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newRouter) + th.AssertEquals(t, newRouter.Name, newName) + th.AssertEquals(t, newRouter.Description, newDescription) } func TestLayer3RouterInterface(t *testing.T) { diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go index 75dec83986..c57bc7ecc6 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go @@ -71,12 +71,12 @@ func TestMembersCRUD(t *testing.T) { _, err = members.Update(client, member.ID, updateOpts).Extract() if err != nil { - t.Fatalf("Unable to update member: %v") + t.Fatalf("Unable to update member: %v", err) } newMember, err := members.Get(client, member.ID).Extract() if err != nil { - t.Fatalf("Unable to get member: %v") + t.Fatalf("Unable to get member: %v", err) } tools.PrintResource(t, newMember) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go index 56b413afb0..31ce3fad98 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go @@ -51,12 +51,12 @@ func TestMonitorsCRUD(t *testing.T) { _, err = monitors.Update(client, monitor.ID, updateOpts).Extract() if err != nil { - t.Fatalf("Unable to update monitor: %v") + t.Fatalf("Unable to update monitor: %v", err) } newMonitor, err := monitors.Get(client, monitor.ID).Extract() if err != nil { - t.Fatalf("Unable to get monitor: %v") + t.Fatalf("Unable to get monitor: %v", err) } tools.PrintResource(t, newMonitor) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go index b53237c0e3..e1eb940678 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go @@ -64,12 +64,12 @@ func TestPoolsCRUD(t *testing.T) { _, err = pools.Update(client, pool.ID, updateOpts).Extract() if err != nil { - t.Fatalf("Unable to update pool: %v") + t.Fatalf("Unable to update pool: %v", err) } newPool, err := pools.Get(client, pool.ID).Extract() if err != nil { - t.Fatalf("Unable to get pool: %v") + t.Fatalf("Unable to get pool: %v", err) } tools.PrintResource(t, newPool) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go b/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go index a63dc63c6c..4e54170e70 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go @@ -71,12 +71,12 @@ func TestVIPsCRUD(t *testing.T) { _, err = vips.Update(client, vip.ID, updateOpts).Extract() if err != nil { - t.Fatalf("Unable to update vip: %v") + t.Fatalf("Unable to update vip: %v", err) } newVIP, err := vips.Get(client, vip.ID).Extract() if err != nil { - t.Fatalf("Unable to get vip: %v") + t.Fatalf("Unable to get vip: %v", err) } tools.PrintResource(t, newVIP) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index 7e26c8dd94..5b5ffd730d 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -22,12 +22,14 @@ const loadbalancerDeleteTimeoutSeconds = 300 // be created. func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) { listenerName := tools.RandomString("TESTACCT-", 8) + listenerDescription := tools.RandomString("TESTACCT-DESC-", 8) listenerPort := tools.RandomInt(1, 100) t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) createOpts := listeners.CreateOpts{ Name: listenerName, + Description: listenerDescription, LoadbalancerID: lb.ID, Protocol: "TCP", ProtocolPort: listenerPort, @@ -44,6 +46,10 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, listener.Name, listenerName) + th.AssertEquals(t, listener.Description, listenerDescription) + th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID) + th.AssertEquals(t, listener.Protocol, "TCP") th.AssertEquals(t, listener.ProtocolPort, listenerPort) return listener, nil @@ -53,11 +59,13 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal // subnet. An error will be returned if the loadbalancer could not be created. func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*loadbalancers.LoadBalancer, error) { lbName := tools.RandomString("TESTACCT-", 8) + lbDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) createOpts := loadbalancers.CreateOpts{ Name: lbName, + Description: lbDescription, VipSubnetID: subnetID, AdminStateUp: gophercloud.Enabled, } @@ -77,6 +85,9 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI t.Logf("LoadBalancer %s is active", lbName) th.AssertEquals(t, lb.Name, lbName) + th.AssertEquals(t, lb.Description, lbDescription) + th.AssertEquals(t, lb.VipSubnetID, subnetID) + th.AssertEquals(t, lb.AdminStateUp, gophercloud.Enabled) return lb, nil } @@ -157,11 +168,13 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala // created. func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) { poolName := tools.RandomString("TESTACCT-", 8) + poolDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create pool %s", poolName) createOpts := pools.CreateOpts{ Name: poolName, + Description: poolDescription, Protocol: pools.ProtocolTCP, LoadbalancerID: lb.ID, LBMethod: pools.LBMethodLeastConnections, @@ -179,6 +192,10 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance } th.AssertEquals(t, pool.Name, poolName) + th.AssertEquals(t, pool.Description, poolDescription) + th.AssertEquals(t, pool.Protocol, pools.ProtocolTCP) + th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID) + th.AssertEquals(t, pool.LBMethod, pools.LBMethodLeastConnections) return pool, nil } 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 770e495ec4..b39346bd27 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -59,8 +59,11 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteListener(t, client, lb.ID, listener.ID) + listenerName := "" + listenerDescription := "" updateListenerOpts := listeners.UpdateOpts{ - Description: "Some listener description", + Name: &listenerName, + Description: &listenerDescription, } _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) @@ -73,14 +76,19 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newListener) + th.AssertEquals(t, newListener.Name, listenerName) + th.AssertEquals(t, newListener.Description, listenerDescription) // Pool pool, err := CreatePool(t, client, lb) th.AssertNoErr(t, err) defer DeletePool(t, client, lb.ID, pool.ID) + poolName := "" + poolDescription := "" updatePoolOpts := pools.UpdateOpts{ - Description: "Some pool description", + Name: &poolName, + Description: &poolDescription, } _, err = pools.Update(client, pool.ID, updatePoolOpts).Extract() th.AssertNoErr(t, err) @@ -93,14 +101,18 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newPool) + th.AssertEquals(t, newPool.Name, poolName) + th.AssertEquals(t, newPool.Description, poolDescription) // Member member, err := CreateMember(t, client, lb, newPool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) defer DeleteMember(t, client, lb.ID, pool.ID, member.ID) + memberName := "" newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ + Name: &memberName, Weight: &newWeight, } _, err = pools.UpdateMember(client, pool.ID, member.ID, updateMemberOpts).Extract() @@ -114,14 +126,18 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newMember) + th.AssertEquals(t, newMember.Name, memberName) + th.AssertEquals(t, newMember.Weight, newWeight) // Monitor monitor, err := CreateMonitor(t, client, lb, newPool) th.AssertNoErr(t, err) defer DeleteMonitor(t, client, lb.ID, monitor.ID) + monName := "" newDelay := tools.RandomInt(20, 30) updateMonitorOpts := monitors.UpdateOpts{ + Name: &monName, Delay: newDelay, } _, err = monitors.Update(client, monitor.ID, updateMonitorOpts).Extract() @@ -135,4 +151,6 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) + th.AssertEquals(t, newMonitor.Name, newMonitor) + th.AssertEquals(t, newMonitor.Delay, newDelay) } diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index d7a0b4aeae..ed0549d5bc 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -20,6 +20,7 @@ type PortWithBindingExt struct { // returned if the port could not be created. func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string) (PortWithBindingExt, error) { portName := tools.RandomString("TESTACC-", 8) + portDescription := tools.RandomString("TESTACC-PORT-DESC-", 8) iFalse := false t.Logf("Attempting to create port: %s", portName) @@ -27,6 +28,7 @@ func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, network portCreateOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, + Description: portDescription, AdminStateUp: &iFalse, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } @@ -46,6 +48,7 @@ func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, network t.Logf("Successfully created port: %s", portName) th.AssertEquals(t, s.Name, portName) + th.AssertEquals(t, s.Description, portDescription) return s, nil } diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index 191fd7da8c..d697e090e3 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -39,12 +39,16 @@ func TestPortsbindingCRUD(t *testing.T) { tools.PrintResource(t, port) // Update port - newPortName := tools.RandomString("TESTACC-", 8) + newPortName := "" + newPortDescription := "" updateOpts := ports.UpdateOpts{ - Name: &newPortName, + Name: &newPortName, + Description: &newPortDescription, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) + th.AssertEquals(t, newPort.Description, newPortName) + th.AssertEquals(t, newPort.Description, newPortDescription) } diff --git a/acceptance/openstack/networking/v2/extensions/security_test.go b/acceptance/openstack/networking/v2/extensions/security_test.go index ed7a5c787a..3bf44a8071 100644 --- a/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/acceptance/openstack/networking/v2/extensions/security_test.go @@ -26,8 +26,10 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { tools.PrintResource(t, group) - var description = "A security group" + var name = "Update group" + var description = "" updateOpts := groups.UpdateOpts{ + Name: name, Description: &description, } @@ -35,6 +37,8 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) + th.AssertEquals(t, newGroup.Name, name) + th.AssertEquals(t, newGroup.Description, description) listOpts := groups.ListOpts{} allPages, err := groups.List(client, listOpts).AllPages() diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go index a2f14a4691..8c110263e0 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go @@ -13,12 +13,14 @@ import ( // subnetpool could not be created. func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetpools.SubnetPool, error) { subnetPoolName := tools.RandomString("TESTACC-", 8) + subnetPoolDescription := tools.RandomString("TESTACC-DESC-", 8) subnetPoolPrefixes := []string{ "10.0.0.0/8", } createOpts := subnetpools.CreateOpts{ - Name: subnetPoolName, - Prefixes: subnetPoolPrefixes, + Name: subnetPoolName, + Description: subnetPoolDescription, + Prefixes: subnetPoolPrefixes, } t.Logf("Attempting to create a subnetpool: %s", subnetPoolName) @@ -31,6 +33,7 @@ func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetp t.Logf("Successfully created the subnetpool.") th.AssertEquals(t, subnetPool.Name, subnetPoolName) + th.AssertEquals(t, subnetPool.Description, subnetPoolDescription) return subnetPool, nil } diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index f0e9b792f6..e5c9c320ed 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -23,8 +23,10 @@ func TestSubnetPoolsCRUD(t *testing.T) { tools.PrintResource(t, subnetPool) newName := tools.RandomString("TESTACC-", 8) + newDescription := "" updateOpts := &subnetpools.UpdateOpts{ - Name: newName, + Name: newName, + Description: &newDescription, } _, err = subnetpools.Update(client, subnetPool.ID, updateOpts).Extract() @@ -34,6 +36,8 @@ func TestSubnetPoolsCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newSubnetPool) + th.AssertEquals(t, newSubnetPool.Name, newName) + th.AssertEquals(t, newSubnetPool.Description, newDescription) allPages, err := subnetpools.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index b3aeba3dd9..7f562d65ce 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -65,9 +65,11 @@ func TestTrunkCRUD(t *testing.T) { } // Update Trunk - var name = "updated_gophertrunk" + name := "" + description := "" updateOpts := trunks.UpdateOpts{ - Name: &name, + Name: &name, + Description: &description, } updatedTrunk, err := trunks.Update(client, trunk.ID, updateOpts).Extract() if err != nil { @@ -78,6 +80,13 @@ func TestTrunkCRUD(t *testing.T) { t.Fatalf("Trunk name was not updated correctly") } + if trunk.Description == updatedTrunk.Description { + t.Fatalf("Trunk description was not updated correctly") + } + + th.AssertDeepEquals(t, updatedTrunk.Name, name) + th.AssertDeepEquals(t, updatedTrunk.Description, description) + // Get subports subports, err := trunks.GetSubports(client, trunk.ID).Extract() if err != nil { diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index 89bb09b134..b92eac91e2 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -24,8 +24,10 @@ type PortWithExtraDHCPOpts struct { // network could not be created. func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) { networkName := tools.RandomString("TESTACC-", 8) + networkDescription := tools.RandomString("TESTACC-DESC-", 8) createOpts := networks.CreateOpts{ Name: networkName, + Description: networkDescription, AdminStateUp: gophercloud.Enabled, } @@ -39,6 +41,7 @@ func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.N t.Logf("Successfully created network.") th.AssertEquals(t, network.Name, networkName) + th.AssertEquals(t, network.Description, networkDescription) return network, nil } @@ -76,12 +79,14 @@ func CreateNetworkWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceC // returned if the port could not be created. func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { portName := tools.RandomString("TESTACC-", 8) + portDescription := tools.RandomString("TESTACC-DESC-", 8) t.Logf("Attempting to create port: %s", portName) createOpts := ports.CreateOpts{ NetworkID: networkID, Name: portName, + Description: portDescription, AdminStateUp: gophercloud.Enabled, FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, } @@ -103,6 +108,7 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subn t.Logf("Successfully created port: %s", portName) th.AssertEquals(t, port.Name, portName) + th.AssertEquals(t, port.Description, portDescription) return newPort, nil } @@ -233,16 +239,18 @@ func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient // will be returned if the subnet could not be created. func CreateSubnet(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) createOpts := subnets.CreateOpts{ - NetworkID: networkID, - CIDR: subnetCIDR, - IPVersion: 4, - Name: subnetName, - EnableDHCP: gophercloud.Disabled, - GatewayIP: &subnetGateway, + NetworkID: networkID, + CIDR: subnetCIDR, + IPVersion: 4, + Name: subnetName, + Description: subnetDescription, + EnableDHCP: gophercloud.Disabled, + GatewayIP: &subnetGateway, } t.Logf("Attempting to create subnet: %s", subnetName) @@ -255,6 +263,7 @@ func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID str 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) diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index d9a15b5c8e..8e08d90531 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -79,8 +79,10 @@ func TestNetworksCRUD(t *testing.T) { tools.PrintResource(t, network) newName := tools.RandomString("TESTACC-", 8) + newDescription := "" updateOpts := &networks.UpdateOpts{ - Name: newName, + Name: newName, + Description: &newDescription, } _, err = networks.Update(client, network.ID, updateOpts).Extract() @@ -90,6 +92,8 @@ func TestNetworksCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) + th.AssertEquals(t, newNetwork.Name, newName) + th.AssertEquals(t, newNetwork.Description, newDescription) type networkWithExt struct { networks.Network diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index 5e871d2140..b50edbb0f1 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -40,15 +40,20 @@ func TestPortsCRUD(t *testing.T) { tools.PrintResource(t, port) // Update port - newPortName := tools.RandomString("TESTACC-", 8) + newPortName := "" + newPortDescription := "" updateOpts := ports.UpdateOpts{ - Name: &newPortName, + Name: &newPortName, + Description: &newPortDescription, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) + th.AssertEquals(t, newPort.Name, newPortName) + th.AssertEquals(t, newPort.Description, newPortDescription) + allPages, err := ports.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index 28c8479c79..0270a3430f 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -32,8 +32,10 @@ func TestSubnetCRUD(t *testing.T) { // Update Subnet newSubnetName := tools.RandomString("TESTACC-", 8) + newSubnetDescription := "" updateOpts := subnets.UpdateOpts{ - Name: newSubnetName, + Name: newSubnetName, + Description: &newSubnetDescription, } _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) @@ -43,6 +45,8 @@ func TestSubnetCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newSubnet) + th.AssertEquals(t, newSubnet.Name, newSubnetName) + th.AssertEquals(t, newSubnet.Description, newSubnetDescription) allPages, err := subnets.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices.go b/acceptance/openstack/sharedfilesystems/v2/securityservices.go index 265323d479..149cf6fea3 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices.go +++ b/acceptance/openstack/sharedfilesystems/v2/securityservices.go @@ -16,11 +16,13 @@ func CreateSecurityService(t *testing.T, client *gophercloud.ServiceClient) (*se } securityServiceName := tools.RandomString("ACPTTEST", 16) + securityServiceDescription := tools.RandomString("ACPTTEST-DESC", 16) t.Logf("Attempting to create security service: %s", securityServiceName) createOpts := securityservices.CreateOpts{ - Name: securityServiceName, - Type: "kerberos", + Name: securityServiceName, + Description: securityServiceDescription, + Type: "kerberos", } securityService, err := securityservices.Create(client, createOpts).Extract() diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index 6ffa6160c1..0d72c36b47 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -27,6 +27,10 @@ func TestSecurityServiceCreateDelete(t *testing.T) { t.Fatalf("Security service name was expeted to be: %s", securityService.Name) } + if newSecurityService.Description != securityService.Description { + t.Fatalf("Security service description was expeted to be: %s", securityService.Description) + } + PrintSecurityService(t, securityService) defer DeleteSecurityService(t, client, securityService) @@ -109,7 +113,7 @@ func TestSecurityServiceUpdate(t *testing.T) { } name := "NewName" - description := "New security service description" + description := "" options := securityservices.UpdateOpts{ Name: &name, Description: &description, @@ -126,12 +130,12 @@ func TestSecurityServiceUpdate(t *testing.T) { t.Errorf("Unable to retrieve the security service: %v", err) } - if newSecurityService.Name != *options.Name { - t.Fatalf("Security service name was expeted to be: %s", *options.Name) + if newSecurityService.Name != name { + t.Fatalf("Security service name was expeted to be: %s", name) } - if newSecurityService.Description != *options.Description { - t.Fatalf("Security service description was expeted to be: %s", *options.Description) + if newSecurityService.Description != description { + t.Fatalf("Security service description was expeted to be: %s", description) } if newSecurityService.Type != options.Type { diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index e13e1a28ea..727539886e 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -29,6 +29,10 @@ func TestShareNetworkCreateDestroy(t *testing.T) { t.Fatalf("Share network name was expeted to be: %s", shareNetwork.Name) } + if newShareNetwork.Description != shareNetwork.Description { + t.Fatalf("Share network description was expeted to be: %s", shareNetwork.Description) + } + PrintShareNetwork(t, shareNetwork) defer DeleteShareNetwork(t, client, shareNetwork.ID) @@ -53,14 +57,14 @@ func TestShareNetworkUpdate(t *testing.T) { } name := "NewName" - description := "New share network description" + description := "" options := sharenetworks.UpdateOpts{ Name: &name, Description: &description, } - expectedShareNetwork.Name = *options.Name - expectedShareNetwork.Description = *options.Description + expectedShareNetwork.Name = name + expectedShareNetwork.Description = description _, err = sharenetworks.Update(client, shareNetwork.ID, options).Extract() if err != nil { diff --git a/script/acceptancetest b/script/acceptancetest index 6bc02d9f2b..9fc4d8eeeb 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -10,109 +10,81 @@ failed= # Run the acceptance tests. # Check the error code after each suite, but do not exit early if a suite failed. -# Identity v3 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/identity/v3/ -if [[ $? != 0 ]]; then - failed=1 -fi +ACCEPTANCE_TESTS=( +acceptance/openstack/blockstorage/extensions -# Networking v2 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/ -if [[ $? != 0 ]]; then - failed=1 -fi +# snapshots_test.go:16: Unable to create a blockstorage client: CinderEndpoint is required +# acceptance/openstack/blockstorage/noauth -# Networking v2 - extensions -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions -if [[ $? != 0 ]]; then - failed=1 -fi +# snapshots_test.go:21: Unable to retrieve snapshots: Resource not found +# acceptance/openstack/blockstorage/v1 -# Networking v2 - layer3 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/layer3 -if [[ $? != 0 ]]; then - failed=1 -fi +acceptance/openstack/blockstorage/v2 +acceptance/openstack/blockstorage/v3 -# Networking v2 - networkipavailabilities -# requires bug fix -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/networkipavailabilities -if [[ $? != 0 ]]; then - failed=1 -fi +# No suitable endpoint could be found in the service catalog. +# acceptance/openstack/clustering/v1 -# Networking v2 - portsbinding -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/portsbinding -if [[ $? != 0 ]]; then - failed=1 -fi +acceptance/openstack/compute/v2 -# Networking v2 - rbacpolicies -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/rbacpolicies -if [[ $? != 0 ]]; then - failed=1 -fi +# No suitable endpoint could be found in the service catalog. +# acceptance/openstack/containerinfra/v1 -# Networking v2 - subnetpools -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/subnetpools -if [[ $? != 0 ]]; then - failed=1 -fi +acceptance/openstack/container/v1 -# Networking v2 - trunks -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/networking/v2/extensions/trunks -if [[ $? != 0 ]]; then - failed=1 -fi +# Unable to create a DB client: No suitable endpoint could be found in the service catalog. +# acceptance/openstack/db/v1 -# Block Storage v2 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v2/ -if [[ $? != 0 ]]; then - failed=1 -fi +acceptance/openstack/dns/v2 +acceptance/openstack/identity/v2 +acceptance/openstack/identity/v3 +acceptance/openstack/imageservice/v2 +acceptance/openstack/keymanager/v1 +acceptance/openstack/loadbalancer/v2 -# Block Storage v3 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/v3/ -if [[ $? != 0 ]]; then - failed=1 -fi +# No suitable endpoint could be found in the service catalog. +# acceptance/openstack/messaging/v2 -# Block Storage extensions -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/blockstorage/extensions/ -if [[ $? != 0 ]]; then - failed=1 -fi +acceptance/openstack/networking/v2 +acceptance/openstack/networking/v2/extensions -# Compute v2 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/compute/v2/ -if [[ $? != 0 ]]; then - failed=1 -fi +# Resource not found +# acceptance/openstack/networking/v2/extensions/fwaas -# Container v1 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/container/v1/ -if [[ $? != 0 ]]; then - failed=1 -fi +acceptance/openstack/networking/v2/extensions/layer3 -# Image Service v2 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/imageservice/v2/ -if [[ $? != 0 ]]; then - failed=1 -fi +# Unable to create: Resource not found +# acceptance/openstack/networking/v2/extensions/lbaas -# Orchestration v1 -go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/orchestration/v1/ -if [[ $? != 0 ]]; then - failed=1 -fi +# Resource not found +# acceptance/openstack/networking/v2/extensions/lbaas_v2 + +acceptance/openstack/networking/v2/extensions/networkipavailabilities +acceptance/openstack/networking/v2/extensions/portsbinding +acceptance/openstack/networking/v2/extensions/qos/ruletypes +acceptance/openstack/networking/v2/extensions/rbacpolicies +acceptance/openstack/networking/v2/extensions/subnetpools +acceptance/openstack/networking/v2/extensions/trunks + +# Unable to create: Resource not found +# acceptance/openstack/networking/v2/extensions/vpnaas + +acceptance/openstack/objectstorage/v1 +acceptance/openstack/orchestration/v1 -# Shared Filesystems v2 # Temporarily disabled, see https://github.com/gophercloud/gophercloud/issues/1345 -true || go test -v -timeout $timeout -tags "fixtures acceptance" ./acceptance/openstack/sharedfilesystems/v2/ -if [[ $? != 0 ]]; then - failed=1 -fi +# acceptance/openstack/sharedfilesystems/v2 + +# No suitable endpoint could be found in the service catalog +# acceptance/openstack/workflow/v2 +) + +for acceptance_test in ${ACCEPTANCE_TESTS[@]}; do + go test -v -timeout $timeout -tags "fixtures acceptance" ./${acceptance_test} + if [[ $? != 0 ]]; then + failed=1 + fi +done # If any of the test suites failed, exit 1 if [[ -n $failed ]]; then From 8d92992d8ef31180f12a6b2d8e3ef666c6c90577 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 12 Dec 2018 01:13:15 +0100 Subject: [PATCH 0606/2296] Octavia: update l7policies fields --- .../loadbalancer/v2/l7policies/requests.go | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 1bd784751e..f73ab93e69 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -62,6 +62,10 @@ type CreateOpts struct { // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. RedirectURL string `json:"redirect_url,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToL7PolicyCreateMap builds a request body from CreateOpts. @@ -90,11 +94,14 @@ type ListOptsBuilder interface { // the API. type ListOpts struct { Name string `q:"name"` + Description string `q:"description"` ListenerID string `q:"listener_id"` Action string `q:"action"` ProjectID string `q:"project_id"` RedirectPoolID string `q:"redirect_pool_id"` RedirectURL string `q:"redirect_url"` + Position int32 `q:"position"` + AdminStateUp bool `q:"admin_state_up"` ID string `q:"id"` Limit int `q:"limit"` Marker string `q:"marker"` @@ -168,6 +175,10 @@ type UpdateOpts struct { // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. RedirectURL *string `json:"redirect_url,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToL7PolicyUpdateMap builds a request body from UpdateOpts. @@ -210,6 +221,10 @@ type CreateRuleOpts struct { // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. Invert bool `json:"invert,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToRuleCreateMap builds a request body from CreateRuleOpts. @@ -237,13 +252,18 @@ type ListRulesOptsBuilder interface { // ListRulesOpts allows the filtering and sorting of paginated collections // through the API. type ListRulesOpts struct { - RuleType RuleType `q:"type"` - ProjectID string `q:"project_id"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` + RuleType RuleType `q:"type"` + ProjectID string `q:"project_id"` + CompareType CompareType `q:"compare_type"` + Value string `q:"value"` + Key string `q:"key"` + Invert bool `q:"invert"` + AdminStateUp bool `q:"admin_state_up"` + ID string `q:"id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` } // ToRulesListQuery formats a ListOpts into a query string. @@ -307,6 +327,10 @@ type UpdateRuleOpts struct { // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. Invert *bool `json:"invert,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` } // ToRuleUpdateMap builds a request body from UpdateRuleOpts. From 566ee20c44a422d4b05478aff7c5f668315100d8 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 12 Dec 2018 01:14:46 +0100 Subject: [PATCH 0607/2296] LBaaSv2: Copy l7policies to lbaas_v2 from Octavia --- .../v2/extensions/lbaas_v2/l7policies/doc.go | 123 ++++++ .../lbaas_v2/l7policies/requests.go | 352 ++++++++++++++++ .../extensions/lbaas_v2/l7policies/results.go | 223 +++++++++++ .../lbaas_v2/l7policies/testing/doc.go | 2 + .../lbaas_v2/l7policies/testing/fixtures.go | 376 ++++++++++++++++++ .../l7policies/testing/requests_test.go | 295 ++++++++++++++ .../v2/extensions/lbaas_v2/l7policies/urls.go | 25 ++ 7 files changed, 1396 insertions(+) create mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/doc.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go new file mode 100644 index 0000000000..813579905c --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go @@ -0,0 +1,123 @@ +/* +Package l7policies provides information and interaction with L7Policies and +Rules of the LBaaS v2 extension for the OpenStack Networking service. + +Example to Create a L7Policy + + createOpts := l7policies.CreateOpts{ + Name: "redirect-example.com", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + } + l7policy, err := l7policies.Create(lbClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to List L7Policies + + listOpts := l7policies.ListOpts{ + ListenerID: "c79a4468-d788-410c-bf79-9a8ef6354852", + } + allPages, err := l7policies.List(lbClient, listOpts).AllPages() + if err != nil { + panic(err) + } + allL7Policies, err := l7policies.ExtractL7Policies(allPages) + if err != nil { + panic(err) + } + for _, l7policy := range allL7Policies { + fmt.Printf("%+v\n", l7policy) + } + +Example to Get a L7Policy + + l7policy, err := l7policies.Get(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d").Extract() + if err != nil { + panic(err) + } + +Example to Delete a L7Policy + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + err := l7policies.Delete(lbClient, l7policyID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Update a L7Policy + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + name := "new-name" + updateOpts := l7policies.UpdateOpts{ + Name: &name, + } + l7policy, err := l7policies.Update(lbClient, l7policyID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Create a Rule + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + createOpts := l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images*", + } + rule, err := l7policies.CreateRule(lbClient, l7policyID, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to List L7 Rules + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + listOpts := l7policies.ListRulesOpts{ + RuleType: l7policies.TypePath, + } + allPages, err := l7policies.ListRules(lbClient, l7policyID, listOpts).AllPages() + if err != nil { + panic(err) + } + allRules, err := l7policies.ExtractRules(allPages) + if err != nil { + panic(err) + } + for _, rule := allRules { + fmt.Printf("%+v\n", rule) + } + +Example to Get a l7 rule + + l7rule, err := l7policies.GetRule(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d", "53ad8ab8-40fa-11e8-a508-00224d6b7bc1").Extract() + if err != nil { + panic(err) + } + +Example to Delete a l7 rule + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" + err := l7policies.DeleteRule(lbClient, l7policyID, ruleID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Update a Rule + + l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" + ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" + updateOpts := l7policies.UpdateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images/special*", + } + rule, err := l7policies.UpdateRule(lbClient, l7policyID, ruleID, updateOpts).Extract() + if err != nil { + panic(err) + } +*/ +package l7policies diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go new file mode 100644 index 0000000000..470c336e49 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go @@ -0,0 +1,352 @@ +package l7policies + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToL7PolicyCreateMap() (map[string]interface{}, error) +} + +type Action string +type RuleType string +type CompareType string + +const ( + ActionRedirectToPool Action = "REDIRECT_TO_POOL" + ActionRedirectToURL Action = "REDIRECT_TO_URL" + ActionReject Action = "REJECT" + + TypeCookie RuleType = "COOKIE" + TypeFileType RuleType = "FILE_TYPE" + TypeHeader RuleType = "HEADER" + TypeHostName RuleType = "HOST_NAME" + TypePath RuleType = "PATH" + + CompareTypeContains CompareType = "CONTAINS" + CompareTypeEndWith CompareType = "ENDS_WITH" + CompareTypeEqual CompareType = "EQUAL_TO" + CompareTypeRegex CompareType = "REGEX" + CompareTypeStartWith CompareType = "STARTS_WITH" +) + +// CreateOpts is the common options struct used in this package's Create +// operation. +type CreateOpts struct { + // Name of the L7 policy. + Name string `json:"name,omitempty"` + + // The ID of the listener. + ListenerID string `json:"listener_id" required:"true"` + + // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. + Action Action `json:"action" required:"true"` + + // The position of this policy on the listener. + Position int32 `json:"position,omitempty"` + + // A human-readable description for the resource. + Description string `json:"description,omitempty"` + + // TenantID is the UUID of the tenant who owns the L7 policy in octavia. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id,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"` + + // Requests matching this policy will be redirected to this URL. + // Only valid if action is REDIRECT_TO_URL. + RedirectURL string `json:"redirect_url,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToL7PolicyCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "l7policy") +} + +// Create accepts a CreateOpts struct and uses the values to create a new l7policy. +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToL7PolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToL7PolicyListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. +type ListOpts struct { + Name string `q:"name"` + Description string `q:"description"` + ListenerID string `q:"listener_id"` + Action string `q:"action"` + TenantID string `q:"tenant_id"` + RedirectPoolID string `q:"redirect_pool_id"` + RedirectURL string `q:"redirect_url"` + Position int32 `q:"position"` + AdminStateUp bool `q:"admin_state_up"` + ID string `q:"id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToL7PolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToL7PolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// l7policies. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +// +// Default policy settings return only those l7policies that are owned by the +// project who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToL7PolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return L7PolicyPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves a particular l7policy based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +// Delete will permanently delete a particular l7policy based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToL7PolicyUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts is the common options struct used in this package's Update +// operation. +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. + Action Action `json:"action,omitempty"` + + // The position of this policy on the listener. + Position int32 `json:"position,omitempty"` + + // A human-readable description for the resource, empty string is allowed. + Description *string `json:"description,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"` + + // Requests matching this policy will be redirected to this URL. + // Only valid if action is REDIRECT_TO_URL. + RedirectURL *string `json:"redirect_url,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToL7PolicyUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "l7policy") +} + +// Update allows l7policy to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToL7PolicyUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// CreateRuleOpts is the common options struct used in this package's CreateRule +// operation. +type CreateRuleOpts struct { + // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. + RuleType RuleType `json:"type" required:"true"` + + // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. + CompareType CompareType `json:"compare_type" required:"true"` + + // The value to use for the comparison. For example, the file type to compare. + Value string `json:"value" required:"true"` + + // TenantID is the UUID of the tenant who owns the rule in octavia. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id,omitempty"` + + // The key to use for the comparison. For example, the name of the cookie to evaluate. + Key string `json:"key,omitempty"` + + // When true the logic of the rule is inverted. For example, with invert true, + // equal to would become not equal to. Default is false. + Invert bool `json:"invert,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToRuleCreateMap builds a request body from CreateRuleOpts. +func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "rule") +} + +// CreateRule will create and associate a Rule with a particular L7Policy. +func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) { + b, err := opts.ToRuleCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + return +} + +// ListRulesOptsBuilder allows extensions to add additional parameters to the +// ListRules request. +type ListRulesOptsBuilder interface { + ToRulesListQuery() (string, error) +} + +// ListRulesOpts allows the filtering and sorting of paginated collections +// through the API. +type ListRulesOpts struct { + RuleType RuleType `q:"type"` + TenantID string `q:"tenant_id"` + CompareType CompareType `q:"compare_type"` + Value string `q:"value"` + Key string `q:"key"` + Invert bool `q:"invert"` + AdminStateUp bool `q:"admin_state_up"` + ID string `q:"id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToRulesListQuery formats a ListOpts into a query string. +func (opts ListRulesOpts) ToRulesListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListRules returns a Pager which allows you to iterate over a collection of +// rules. It accepts a ListRulesOptsBuilder, which allows you to filter and +// sort the returned collection for greater efficiency. +// +// Default policy settings return only those rules that are owned by the +// project who submits the request, unless an admin user submits the request. +func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOptsBuilder) pagination.Pager { + url := ruleRootURL(c, policyID) + if opts != nil { + query, err := opts.ToRulesListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return RulePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetRule retrieves a particular L7Policy Rule based on its unique ID. +func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { + _, r.Err = c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) + return +} + +// DeleteRule will remove a Rule from a particular L7Policy. +func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { + _, r.Err = c.Delete(ruleResourceURL(c, policyID, ruleID), nil) + return +} + +// UpdateRuleOptsBuilder allows to add additional parameters to the PUT request. +type UpdateRuleOptsBuilder interface { + ToRuleUpdateMap() (map[string]interface{}, error) +} + +// UpdateRuleOpts is the common options struct used in this package's Update +// operation. +type UpdateRuleOpts struct { + // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. + RuleType RuleType `json:"type,omitempty"` + + // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. + CompareType CompareType `json:"compare_type,omitempty"` + + // The value to use for the comparison. For example, the file type to compare. + Value string `json:"value,omitempty"` + + // The key to use for the comparison. For example, the name of the cookie to evaluate. + Key *string `json:"key,omitempty"` + + // When true the logic of the rule is inverted. For example, with invert true, + // equal to would become not equal to. Default is false. + Invert *bool `json:"invert,omitempty"` + + // The administrative state of the Loadbalancer. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToRuleUpdateMap builds a request body from UpdateRuleOpts. +func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "rule") +} + +// UpdateRule allows Rule to be updated. +func UpdateRule(c *gophercloud.ServiceClient, policyID string, ruleID string, opts UpdateRuleOptsBuilder) (r UpdateRuleResult) { + b, err := opts.ToRuleUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + return +} diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go new file mode 100644 index 0000000000..4dd52cd4e6 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go @@ -0,0 +1,223 @@ +package l7policies + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// L7Policy is a collection of L7 rules associated with a Listener, and which +// may also have an association to a back-end pool. +type L7Policy struct { + // The unique ID for the L7 policy. + ID string `json:"id"` + + // Name of the L7 policy. + Name string `json:"name"` + + // The ID of the listener. + ListenerID string `json:"listener_id"` + + // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. + Action string `json:"action"` + + // The position of this policy on the listener. + Position int32 `json:"position"` + + // A human-readable description for the resource. + Description string `json:"description"` + + // TenantID is the UUID of the tenant who owns the L7 policy in octavia. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id"` + + // 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"` + + // Requests matching this policy will be redirected to this URL. + // Only valid if action is REDIRECT_TO_URL. + RedirectURL string `json:"redirect_url"` + + // The administrative state of the L7 policy, which is up (true) or down (false). + AdminStateUp bool `json:"admin_state_up"` + + // Rules are List of associated L7 rule IDs. + Rules []Rule `json:"rules"` +} + +// Rule represents layer 7 load balancing rule. +type Rule struct { + // The unique ID for the L7 rule. + ID string `json:"id"` + + // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. + RuleType string `json:"type"` + + // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. + CompareType string `json:"compare_type"` + + // The value to use for the comparison. For example, the file type to compare. + Value string `json:"value"` + + // TenantID is the UUID of the tenant who owns the rule in octavia. + // Only administrative users can specify a project UUID other than their own. + TenantID string `json:"tenant_id"` + + // The key to use for the comparison. For example, the name of the cookie to evaluate. + Key string `json:"key"` + + // When true the logic of the rule is inverted. For example, with invert true, + // equal to would become not equal to. Default is false. + Invert bool `json:"invert"` + + // The administrative state of the L7 rule, which is up (true) or down (false). + AdminStateUp bool `json:"admin_state_up"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a l7policy. +func (r commonResult) Extract() (*L7Policy, error) { + var s struct { + L7Policy *L7Policy `json:"l7policy"` + } + err := r.ExtractInto(&s) + return s.L7Policy, err +} + +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret the result as a L7Policy. +type CreateResult struct { + commonResult +} + +// L7PolicyPage is the page returned by a pager when traversing over a +// collection of l7policies. +type L7PolicyPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of l7policies 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 L7PolicyPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"l7policies_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a L7PolicyPage struct is empty. +func (r L7PolicyPage) IsEmpty() (bool, error) { + is, err := ExtractL7Policies(r) + return len(is) == 0, err +} + +// ExtractL7Policies accepts a Page struct, specifically a L7PolicyPage struct, +// and extracts the elements into a slice of L7Policy structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractL7Policies(r pagination.Page) ([]L7Policy, error) { + var s struct { + L7Policies []L7Policy `json:"l7policies"` + } + err := (r.(L7PolicyPage)).ExtractInto(&s) + return s.L7Policies, err +} + +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret the result as a L7Policy. +type GetResult 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 +} + +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret the result as a L7Policy. +type UpdateResult struct { + commonResult +} + +type commonRuleResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a rule. +func (r commonRuleResult) Extract() (*Rule, error) { + var s struct { + Rule *Rule `json:"rule"` + } + err := r.ExtractInto(&s) + return s.Rule, err +} + +// CreateRuleResult represents the result of a CreateRule operation. +// Call its Extract method to interpret it as a Rule. +type CreateRuleResult struct { + commonRuleResult +} + +// RulePage is the page returned by a pager when traversing over a +// collection of Rules in a L7Policy. +type RulePage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of rules 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 RulePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"rules_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a RulePage struct is empty. +func (r RulePage) IsEmpty() (bool, error) { + is, err := ExtractRules(r) + return len(is) == 0, err +} + +// ExtractRules accepts a Page struct, specifically a RulePage struct, +// and extracts the elements into a slice of Rules structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractRules(r pagination.Page) ([]Rule, error) { + var s struct { + Rules []Rule `json:"rules"` + } + err := (r.(RulePage)).ExtractInto(&s) + return s.Rules, err +} + +// GetRuleResult represents the result of a GetRule operation. +// Call its Extract method to interpret it as a Rule. +type GetRuleResult struct { + commonRuleResult +} + +// DeleteRuleResult represents the result of a DeleteRule operation. +// Call its ExtractErr method to determine if the request succeeded or failed. +type DeleteRuleResult struct { + gophercloud.ErrResult +} + +// UpdateRuleResult represents the result of an UpdateRule operation. +// Call its Extract method to interpret it as a Rule. +type UpdateRuleResult struct { + commonRuleResult +} diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/doc.go new file mode 100644 index 0000000000..f8068dfb6b --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/doc.go @@ -0,0 +1,2 @@ +// l7policies unit tests +package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go new file mode 100644 index 0000000000..e30b3f7f75 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go @@ -0,0 +1,376 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// SingleL7PolicyBody is the canned body of a Get request on an existing l7policy. +const SingleL7PolicyBody = ` +{ + "l7policy": { + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "description": "", + "admin_state_up": true, + "redirect_pool_id": null, + "redirect_url": "http://www.example.com", + "action": "REDIRECT_TO_URL", + "position": 1, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", + "name": "redirect-example.com", + "rules": [] + } +} +` + +var ( + L7PolicyToURL = l7policies.L7Policy{ + ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", + Name: "redirect-example.com", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: "REDIRECT_TO_URL", + Position: 1, + Description: "", + TenantID: "e3cd678b11784734bc366148aa37580e", + RedirectPoolID: "", + RedirectURL: "http://www.example.com", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + } + L7PolicyToPool = l7policies.L7Policy{ + ID: "964f4ba4-f6cd-405c-bebd-639460af7231", + Name: "redirect-pool", + ListenerID: "be3138a3-5cf7-4513-a4c2-bb137e668bab", + Action: "REDIRECT_TO_POOL", + Position: 1, + Description: "", + TenantID: "c1f7910086964990847dc6c8b128f63c", + RedirectPoolID: "bac433c6-5bea-4311-80da-bd1cd90fbd25", + RedirectURL: "", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + } + L7PolicyUpdated = l7policies.L7Policy{ + ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", + Name: "NewL7PolicyName", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: "REDIRECT_TO_URL", + Position: 1, + Description: "Redirect requests to example.com", + TenantID: "e3cd678b11784734bc366148aa37580e", + RedirectPoolID: "", + RedirectURL: "http://www.new-example.com", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + } + RulePath = l7policies.Rule{ + ID: "16621dbb-a736-4888-a57a-3ecd53df784c", + RuleType: "PATH", + CompareType: "REGEX", + Value: "/images*", + TenantID: "e3cd678b11784734bc366148aa37580e", + Key: "", + Invert: true, + AdminStateUp: true, + } + RuleHostName = l7policies.Rule{ + ID: "d24521a0-df84-4468-861a-a531af116d1e", + RuleType: "HOST_NAME", + CompareType: "EQUAL_TO", + Value: "www.example.com", + TenantID: "e3cd678b11784734bc366148aa37580e", + Key: "", + Invert: false, + AdminStateUp: true, + } + RuleUpdated = l7policies.Rule{ + ID: "16621dbb-a736-4888-a57a-3ecd53df784c", + RuleType: "PATH", + CompareType: "REGEX", + Value: "/images/special*", + TenantID: "e3cd678b11784734bc366148aa37580e", + Key: "", + Invert: false, + AdminStateUp: true, + } +) + +// HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request +// with a given response. +func HandleL7PolicyCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies", 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, `{ + "l7policy": { + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "redirect_url": "http://www.example.com", + "name": "redirect-example.com", + "action": "REDIRECT_TO_URL" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// L7PoliciesListBody contains the canned body of a l7policy list response. +const L7PoliciesListBody = ` +{ + "l7policies": [ + { + "redirect_pool_id": null, + "description": "", + "admin_state_up": true, + "rules": [], + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "redirect_url": "http://www.example.com", + "action": "REDIRECT_TO_URL", + "position": 1, + "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", + "name": "redirect-example.com" + }, + { + "redirect_pool_id": "bac433c6-5bea-4311-80da-bd1cd90fbd25", + "description": "", + "admin_state_up": true, + "rules": [], + "tenant_id": "c1f7910086964990847dc6c8b128f63c", + "listener_id": "be3138a3-5cf7-4513-a4c2-bb137e668bab", + "action": "REDIRECT_TO_POOL", + "position": 1, + "id": "964f4ba4-f6cd-405c-bebd-639460af7231", + "name": "redirect-pool" + } + ] +} +` + +// PostUpdateL7PolicyBody is the canned response body of a Update request on an existing l7policy. +const PostUpdateL7PolicyBody = ` +{ + "l7policy": { + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "description": "Redirect requests to example.com", + "admin_state_up": true, + "redirect_pool_id": null, + "redirect_url": "http://www.new-example.com", + "action": "REDIRECT_TO_URL", + "position": 1, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", + "name": "NewL7PolicyName", + "rules": [] + } +} +` + +// HandleL7PolicyListSuccessfully sets up the test server to respond to a l7policy List request. +func HandleL7PolicyListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies", 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, L7PoliciesListBody) + case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": + fmt.Fprintf(w, `{ "l7policies": [] }`) + default: + t.Fatalf("/v2.0/lbaas/l7policies invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleL7PolicyGetSuccessfully sets up the test server to respond to a l7policy Get request. +func HandleL7PolicyGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, SingleL7PolicyBody) + }) +} + +// HandleL7PolicyDeletionSuccessfully sets up the test server to respond to a l7policy deletion request. +func HandleL7PolicyDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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) + }) +} + +// HandleL7PolicyUpdateSuccessfully sets up the test server to respond to a l7policy Update request. +func HandleL7PolicyUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, `{ + "l7policy": { + "name": "NewL7PolicyName", + "action": "REDIRECT_TO_URL", + "redirect_url": "http://www.new-example.com" + } + }`) + + fmt.Fprintf(w, PostUpdateL7PolicyBody) + }) +} + +// SingleRuleBody is the canned body of a Get request on an existing rule. +const SingleRuleBody = ` +{ + "rule": { + "compare_type": "REGEX", + "invert": true, + "admin_state_up": true, + "value": "/images*", + "key": null, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "type": "PATH", + "id": "16621dbb-a736-4888-a57a-3ecd53df784c" + } +} +` + +// HandleRuleCreationSuccessfully sets up the test server to respond to a rule creation request +// with a given response. +func HandleRuleCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", 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, `{ + "rule": { + "compare_type": "REGEX", + "type": "PATH", + "value": "/images*" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// RulesListBody contains the canned body of a rule list response. +const RulesListBody = ` +{ + "rules":[ + { + "compare_type": "REGEX", + "invert": true, + "admin_state_up": true, + "value": "/images*", + "key": null, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "type": "PATH", + "id": "16621dbb-a736-4888-a57a-3ecd53df784c" + }, + { + "compare_type": "EQUAL_TO", + "invert": false, + "admin_state_up": true, + "value": "www.example.com", + "key": null, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "type": "HOST_NAME", + "id": "d24521a0-df84-4468-861a-a531af116d1e" + } + ] +} +` + +// HandleRuleListSuccessfully sets up the test server to respond to a rule List request. +func HandleRuleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", 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, RulesListBody) + case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": + fmt.Fprintf(w, `{ "rules": [] }`) + default: + t.Fatalf("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleRuleGetSuccessfully sets up the test server to respond to a rule Get request. +func HandleRuleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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, SingleRuleBody) + }) +} + +// HandleRuleDeletionSuccessfully sets up the test server to respond to a rule deletion request. +func HandleRuleDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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) + }) +} + +// PostUpdateRuleBody is the canned response body of a Update request on an existing rule. +const PostUpdateRuleBody = ` +{ + "rule": { + "compare_type": "REGEX", + "invert": false, + "admin_state_up": true, + "value": "/images/special*", + "key": null, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "type": "PATH", + "id": "16621dbb-a736-4888-a57a-3ecd53df784c" + } +} +` + +// HandleRuleUpdateSuccessfully sets up the test server to respond to a rule Update request. +func HandleRuleUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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, `{ + "rule": { + "compare_type": "REGEX", + "invert": false, + "type": "PATH", + "value": "/images/special*" + } + }`) + + fmt.Fprintf(w, PostUpdateRuleBody) + }) +} diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go new file mode 100644 index 0000000000..7e9f271b4b --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go @@ -0,0 +1,295 @@ +package testing + +import ( + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreateL7Policy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyCreationSuccessfully(t, SingleL7PolicyBody) + + actual, err := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ + Name: "redirect-example.com", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + }).Extract() + + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, L7PolicyToURL, *actual) +} + +func TestRequiredL7PolicyCreateOpts(t *testing.T) { + // no param specified. + res := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + + // Action is invalid. + res = l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: l7policies.Action("invalid"), + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } +} + +func TestListL7Policies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyListSuccessfully(t) + + pages := 0 + err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := l7policies.ExtractL7Policies(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 l7policies, got %d", len(actual)) + } + th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) + th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllL7Policies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyListSuccessfully(t) + + allPages, err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := l7policies.ExtractL7Policies(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) + th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) +} + +func TestGetL7Policy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := l7policies.Get(client, "8a1412f0-4c32-4257-8b07-af4770b604fd").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, L7PolicyToURL, *actual) +} + +func TestDeleteL7Policy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyDeletionSuccessfully(t) + + res := l7policies.Delete(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateL7Policy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyUpdateSuccessfully(t) + + client := fake.ServiceClient() + newName := "NewL7PolicyName" + redirectURL := "http://www.new-example.com" + actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", + l7policies.UpdateOpts{ + Name: &newName, + Action: l7policies.ActionRedirectToURL, + RedirectURL: &redirectURL, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, L7PolicyUpdated, *actual) +} + +func TestUpdateL7PolicyWithInvalidOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + res := l7policies.Update(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ + Action: l7policies.Action("invalid"), + }) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestCreateRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleCreationSuccessfully(t, SingleRuleBody) + + actual, err := l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images*", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, RulePath, *actual) +} + +func TestRequiredRuleCreateOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + res := l7policies.CreateRule(fake.ServiceClient(), "", l7policies.CreateRuleOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + RuleType: l7policies.RuleType("invalid"), + CompareType: l7policies.CompareTypeRegex, + Value: "/images*", + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } + res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareType("invalid"), + Value: "/images*", + }) + if res.Err == nil { + t.Fatalf("Expected error, but got none") + } +} + +func TestListRules(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleListSuccessfully(t) + + pages := 0 + err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := l7policies.ExtractRules(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 rules, got %d", len(actual)) + } + th.CheckDeepEquals(t, RulePath, actual[0]) + th.CheckDeepEquals(t, RuleHostName, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllRules(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleListSuccessfully(t) + + allPages, err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).AllPages() + th.AssertNoErr(t, err) + + actual, err := l7policies.ExtractRules(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, RulePath, actual[0]) + th.CheckDeepEquals(t, RuleHostName, actual[1]) +} + +func TestGetRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleGetSuccessfully(t) + + client := fake.ServiceClient() + actual, err := l7policies.GetRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, RulePath, *actual) +} + +func TestDeleteRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleDeletionSuccessfully(t) + + res := l7policies.DeleteRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRuleUpdateSuccessfully(t) + + client := fake.ServiceClient() + invert := false + actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images/special*", + Invert: &invert, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, RuleUpdated, *actual) +} + +func TestUpdateRuleWithInvalidOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + res := l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ + RuleType: l7policies.RuleType("invalid"), + }) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + + res = l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ + CompareType: l7policies.CompareType("invalid"), + }) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go new file mode 100644 index 0000000000..ecb607a8e8 --- /dev/null +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go @@ -0,0 +1,25 @@ +package l7policies + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "l7policies" + rulePath = "rules" +) + +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) +} + +func ruleRootURL(c *gophercloud.ServiceClient, policyID string) string { + return c.ServiceURL(rootPath, resourcePath, policyID, rulePath) +} + +func ruleResourceURL(c *gophercloud.ServiceClient, policyID string, ruleID string) string { + return c.ServiceURL(rootPath, resourcePath, policyID, rulePath, ruleID) +} From 43082639726e82cea2aa1987bc4d1c2bc8822a80 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 12 Dec 2018 01:15:50 +0100 Subject: [PATCH 0608/2296] LBaaSv2: Add TERMINATED_HTTPS protocol type to listeners --- .../v2/extensions/lbaas_v2/listeners/requests.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index 5c2bb78a98..5d30efc2a2 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -10,9 +10,10 @@ type Protocol string // Supported attributes for create/update operations. const ( - ProtocolTCP Protocol = "TCP" - ProtocolHTTP Protocol = "HTTP" - ProtocolHTTPS Protocol = "HTTPS" + ProtocolTCP Protocol = "TCP" + ProtocolHTTP Protocol = "HTTP" + ProtocolHTTPS Protocol = "HTTPS" + ProtocolTerminatedHTTPS Protocol = "TERMINATED_HTTPS" ) // ListOptsBuilder allows extensions to add additional parameters to the From 79602f8d4dc35eaa7387d30605dffd878da177d0 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 12 Dec 2018 01:16:49 +0100 Subject: [PATCH 0609/2296] LBaaSv2: Add stats logic into the loadbalancer --- .../extensions/lbaas_v2/loadbalancers/doc.go | 8 ++ .../lbaas_v2/loadbalancers/requests.go | 6 ++ .../lbaas_v2/loadbalancers/results.go | 33 +++++++ .../loadbalancers/testing/fixtures.go | 91 +++++++++++++------ .../loadbalancers/testing/requests_test.go | 16 +++- .../extensions/lbaas_v2/loadbalancers/urls.go | 11 ++- 6 files changed, 132 insertions(+), 33 deletions(-) diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go index eea43391a8..c6d53a7b05 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go @@ -67,5 +67,13 @@ Example to Get the Status of a Load Balancer if err != nil { panic(err) } + +Example to Get the Statistics of a Load Balancer + + lbID := "d67d56a6-4a86-4688-a282-f46444705c64" + stats, err := loadbalancers.GetStats(networkClient, LBID).Extract() + if err != nil { + panic(err) + } */ package loadbalancers diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index 8b2ef08916..f5b1413482 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -196,3 +196,9 @@ func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) return } + +// GetStats will return the shows the current statistics of a particular LoadBalancer. +func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { + _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go index 42fff57131..ea07363455 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go @@ -58,6 +58,23 @@ type StatusTree struct { Loadbalancer *LoadBalancer `json:"loadbalancer"` } +type Stats struct { + // The currently active connections. + ActiveConnections int `json:"active_connections"` + + // The total bytes received. + BytesIn int `json:"bytes_in"` + + // The total bytes sent. + BytesOut int `json:"bytes_out"` + + // The total requests that were unable to be fulfilled. + RequestErrors int `json:"request_errors"` + + // The total connections handled. + TotalConnections int `json:"total_connections"` +} + // LoadBalancerPage is the page returned by a pager when traversing over a // collection of load balancers. type LoadBalancerPage struct { @@ -124,6 +141,22 @@ func (r GetStatusesResult) Extract() (*StatusTree, error) { return s.Statuses, err } +// StatsResult represents the result of a GetStats operation. +// Call its Extract method to interpret it as a Stats. +type StatsResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts the status of +// a Loadbalancer. +func (r StatsResult) Extract() (*Stats, error) { + var s struct { + Stats *Stats `json:"stats"` + } + err := r.ExtractInto(&s) + return s.Stats, err +} + // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a LoadBalancer. type CreateResult struct { diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go index 4a72d2dfa2..8132bda303 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go @@ -89,8 +89,8 @@ const PostUpdateLoadbalancerBody = ` } ` -// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer. -const LoadbalancerStatuesesTree = ` +// GetLoadbalancerStatusesBody is the canned request body of a Get request on loadbalancer's status. +const GetLoadbalancerStatusesBody = ` { "statuses" : { "loadbalancer": { @@ -101,22 +101,22 @@ const LoadbalancerStatuesesTree = ` "listeners": [{ "id": "db902c0c-d5ff-4753-b465-668ad9656918", "name": "db", - "provisioning_status": "PENDING_UPDATE", + "provisioning_status": "ACTIVE", "pools": [{ "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "name": "db", - "provisioning_status": "PENDING_UPDATE", + "provisioning_status": "ACTIVE", "healthmonitor": { "id": "67306cda-815d-4354-9fe4-59e09da9c3c5", "type":"PING", - "provisioning_status": "PENDING_UPDATE" + "provisioning_status": "ACTIVE" }, "members":[{ "id": "2a280670-c202-4b0b-a562-34077415aabf", "name": "db", "address": "10.0.2.11", "protocol_port": 80, - "provisioning_status": "PENDING_UPDATE" + "provisioning_status": "ACTIVE" }] }] }] @@ -125,6 +125,19 @@ const LoadbalancerStatuesesTree = ` } ` +// LoadbalancerStatsTree is the canned request body of a Get request on loadbalancer's statistics. +const GetLoadbalancerStatsBody = ` +{ + "stats": { + "active_connections": 0, + "bytes_in": 9532, + "bytes_out": 22033, + "request_errors": 46, + "total_connections": 112 + } +} +` + var ( LoadbalancerWeb = loadbalancers.LoadBalancer{ ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", @@ -168,33 +181,42 @@ var ( ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", } - LoadbalancerStatusesTree = loadbalancers.LoadBalancer{ - ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - Name: "db_lb", - ProvisioningStatus: "PENDING_UPDATE", - OperatingStatus: "ACTIVE", - Listeners: []listeners.Listener{{ - ID: "db902c0c-d5ff-4753-b465-668ad9656918", - Name: "db", + LoadbalancerStatusesTree = loadbalancers.StatusTree{ + Loadbalancer: &loadbalancers.LoadBalancer{ + ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", + Name: "db_lb", ProvisioningStatus: "PENDING_UPDATE", - Pools: []pools.Pool{{ - ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", + OperatingStatus: "ACTIVE", + Listeners: []listeners.Listener{{ + ID: "db902c0c-d5ff-4753-b465-668ad9656918", Name: "db", - ProvisioningStatus: "PENDING_UPDATE", - Monitor: monitors.Monitor{ - ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", - Type: "PING", - ProvisioningStatus: "PENDING_UPDATE", - }, - Members: []pools.Member{{ - ID: "2a280670-c202-4b0b-a562-34077415aabf", + ProvisioningStatus: "ACTIVE", + Pools: []pools.Pool{{ + ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", Name: "db", - Address: "10.0.2.11", - ProtocolPort: 80, - ProvisioningStatus: "PENDING_UPDATE", + ProvisioningStatus: "ACTIVE", + Monitor: monitors.Monitor{ + ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", + Type: "PING", + ProvisioningStatus: "ACTIVE", + }, + Members: []pools.Member{{ + ID: "2a280670-c202-4b0b-a562-34077415aabf", + Name: "db", + Address: "10.0.2.11", + ProtocolPort: 80, + ProvisioningStatus: "ACTIVE", + }}, }}, }}, - }}, + }, + } + LoadbalancerStatsTree = loadbalancers.Stats{ + ActiveConnections: 0, + BytesIn: 9532, + BytesOut: 22033, + RequestErrors: 46, + TotalConnections: 112, } ) @@ -259,7 +281,7 @@ func HandleLoadbalancerGetStatusesTree(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, LoadbalancerStatuesesTree) + fmt.Fprintf(w, GetLoadbalancerStatusesBody) }) } @@ -289,3 +311,14 @@ func HandleLoadbalancerUpdateSuccessfully(t *testing.T) { fmt.Fprintf(w, PostUpdateLoadbalancerBody) }) } + +// HandleLoadbalancerGetStatsTree sets up the test server to respond to a loadbalancer Get stats tree request. +func HandleLoadbalancerGetStatsTree(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/stats", 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, GetLoadbalancerStatsBody) + }) +} diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go index 964471b9ef..2d741cc3ca 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go @@ -115,7 +115,7 @@ func TestGetLoadbalancerStatusesTree(t *testing.T) { t.Fatalf("Unexpected Get error: %v", err) } - th.CheckDeepEquals(t, LoadbalancerStatusesTree, *(actual.Loadbalancer)) + th.CheckDeepEquals(t, LoadbalancerStatusesTree, *actual) } func TestDeleteLoadbalancer(t *testing.T) { @@ -160,3 +160,17 @@ func TestCascadingDeleteLoadbalancer(t *testing.T) { err = loadbalancers.CascadingDelete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() th.AssertNoErr(t, err) } + +func TestGetLoadbalancerStatsTree(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleLoadbalancerGetStatsTree(t) + + client := fake.ServiceClient() + actual, err := loadbalancers.GetStats(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, LoadbalancerStatsTree, *actual) +} diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go index 73cf5dc126..2d2a99b779 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go @@ -3,9 +3,10 @@ package loadbalancers import "github.com/gophercloud/gophercloud" const ( - rootPath = "lbaas" - resourcePath = "loadbalancers" - statusPath = "statuses" + rootPath = "lbaas" + resourcePath = "loadbalancers" + statusPath = "statuses" + statisticsPath = "stats" ) func rootURL(c *gophercloud.ServiceClient) string { @@ -19,3 +20,7 @@ func resourceURL(c *gophercloud.ServiceClient, id string) string { func statusRootURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id, statusPath) } + +func statisticsRootURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id, statisticsPath) +} From dd0bc4e83b37f0c72733a64841a9fe04b331b4af Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 11 Dec 2018 00:45:05 +0100 Subject: [PATCH 0610/2296] LBaaSv2 / Octavia: update acceptance tests --- .../openstack/loadbalancer/v2/loadbalancer.go | 37 ++- .../loadbalancer/v2/loadbalancers_test.go | 220 +++++------------- .../v2/extensions/lbaas_v2/l7policies_test.go | 32 +++ .../v2/extensions/lbaas_v2/lbaas_v2.go | 115 ++++++++- .../v2/extensions/lbaas_v2/listeners_test.go | 32 +++ .../extensions/lbaas_v2/loadbalancers_test.go | 77 +++++- .../v2/extensions/lbaas_v2/monitors_test.go | 32 +++ .../v2/extensions/lbaas_v2/pools_test.go | 32 +++ script/stackenv | 2 +- 9 files changed, 406 insertions(+), 173 deletions(-) create mode 100644 acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go create mode 100644 acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go create mode 100644 acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go create mode 100644 acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 1c7e68176f..e8f3329428 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -12,6 +12,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" + th "github.com/gophercloud/gophercloud/testhelper" ) const loadbalancerActiveTimeoutSeconds = 300 @@ -31,7 +32,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal Name: listenerName, Description: listenerDescription, LoadbalancerID: lb.ID, - Protocol: "TCP", + Protocol: listeners.ProtocolTCP, ProtocolPort: listenerPort, } @@ -46,6 +47,12 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, listener.Name, listenerName) + th.AssertEquals(t, listener.Description, listenerDescription) + th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID) + th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolTCP)) + th.AssertEquals(t, listener.ProtocolPort, listenerPort) + return listener, nil } @@ -78,6 +85,11 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI t.Logf("LoadBalancer %s is active", lbName) + th.AssertEquals(t, lb.Name, lbName) + th.AssertEquals(t, lb.Description, lbDescription) + th.AssertEquals(t, lb.VipSubnetID, subnetID) + th.AssertEquals(t, lb.AdminStateUp, true) + return lb, nil } @@ -115,6 +127,8 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan return member, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, member.Name, memberName) + return member, nil } @@ -131,7 +145,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala Delay: 10, Timeout: 5, MaxRetries: 5, - Type: "PING", + Type: monitors.TypePING, } monitor, err := monitors.Create(client, createOpts).Extract() @@ -145,6 +159,9 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, monitor.Name, monitorName) + th.AssertEquals(t, monitor.Type, monitors.TypePING) + return monitor, nil } @@ -176,6 +193,12 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, pool.Name, poolName) + th.AssertEquals(t, pool.Description, poolDescription) + th.AssertEquals(t, pool.Protocol, string(pools.ProtocolTCP)) + th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID) + th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections)) + return pool, nil } @@ -207,6 +230,12 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, policy.Name, policyName) + th.AssertEquals(t, policy.Description, policyDescription) + th.AssertEquals(t, policy.ListenerID, listener.ID) + th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL)) + th.AssertEquals(t, policy.RedirectURL, "http://www.example.com") + return policy, nil } @@ -231,6 +260,10 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active") } + th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath)) + th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith)) + th.AssertEquals(t, rule.Value, "/api") + return rule, nil } diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index b8c2034f9f..bf9b50244d 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -18,19 +18,13 @@ import ( func TestLoadbalancersList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() - if err != nil { - t.Fatalf("Unable to create a loadbalancer client: %v", err) - } + th.AssertNoErr(t, err) allPages, err := loadbalancers.List(client, nil).AllPages() - if err != nil { - t.Fatalf("Unable to list loadbalancers: %v", err) - } + th.AssertNoErr(t, err) allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) - if err != nil { - t.Fatalf("Unable to extract loadbalancers: %v", err) - } + th.AssertNoErr(t, err) for _, lb := range allLoadbalancers { tools.PrintResource(t, lb) @@ -39,63 +33,43 @@ func TestLoadbalancersList(t *testing.T) { func TestLoadbalancersCRUD(t *testing.T) { netClient, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a networking client: %v", err) - } + th.AssertNoErr(t, err) lbClient, err := clients.NewLoadBalancerV2Client() - if err != nil { - t.Fatalf("Unable to create a loadbalancer client: %v", err) - } + th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, netClient) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, netClient, network.ID) subnet, err := networking.CreateSubnet(t, netClient, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) - if err != nil { - t.Fatalf("Unable to create loadbalancer: %v", err) - } + th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, lbClient, lb.ID) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - lbDescription := "" updateLoadBalancerOpts := loadbalancers.UpdateOpts{ Description: &lbDescription, } _, err = loadbalancers.Update(lbClient, lb.ID, updateLoadBalancerOpts).Extract() - if err != nil { - t.Fatalf("Unable to update loadbalancer %v: ", err) - } + th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() - if err != nil { - t.Fatalf("Unable to get loadbalancer: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newLB) th.AssertEquals(t, newLB.Description, lbDescription) lbStats, err := loadbalancers.GetStats(lbClient, lb.ID).Extract() - if err != nil { - t.Fatalf("Unable to get loadbalancer's statistics: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, lbStats) @@ -104,49 +78,38 @@ func TestLoadbalancersCRUD(t *testing.T) { // Listener listener, err := CreateListener(t, lbClient, lb) - if err != nil { - t.Fatalf("Unable to create listener: %v", err) - } + th.AssertNoErr(t, err) defer DeleteListener(t, lbClient, lb.ID, listener.ID) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - + listenerName := "" listenerDescription := "" updateListenerOpts := listeners.UpdateOpts{ + Name: &listenerName, Description: &listenerDescription, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() - if err != nil { - t.Fatalf("Unable to update listener") - } + th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newListener, err := listeners.Get(lbClient, listener.ID).Extract() - if err != nil { - t.Fatalf("Unable to get listener") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newListener) + th.AssertEquals(t, newListener.Name, listenerName) th.AssertEquals(t, newListener.Description, listenerDescription) listenerStats, err := listeners.GetStats(lbClient, listener.ID).Extract() - if err != nil { - t.Fatalf("Unable to get listener's statistics: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, listenerStats) // L7 policy policy, err := CreateL7Policy(t, lbClient, listener, lb) - if err != nil { - t.Fatalf("Unable to create l7 policy: %v", err) - } + th.AssertNoErr(t, err) defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) newDescription := "" @@ -154,18 +117,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Description: &newDescription, } _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() - if err != nil { - t.Fatalf("Unable to update l7 policy") - } + th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPolicy, err := l7policies.Get(lbClient, policy.ID).Extract() - if err != nil { - t.Fatalf("Unable to get l7 policy: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) @@ -173,19 +132,13 @@ func TestLoadbalancersCRUD(t *testing.T) { // L7 rule rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) - if err != nil { - t.Fatalf("Unable to create l7 rule: %v", err) - } + th.AssertNoErr(t, err) defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() - if err != nil { - t.Fatalf("Unable to get l7 rules: %v", err) - } + th.AssertNoErr(t, err) allRules, err := l7policies.ExtractRules(allPages) - if err != nil { - t.Fatalf("Unable to extract l7 rules: %v", err) - } + th.AssertNoErr(t, err) for _, rule := range allRules { tools.PrintResource(t, rule) } @@ -196,55 +149,45 @@ func TestLoadbalancersCRUD(t *testing.T) { Value: "/images/special*", } _, err = l7policies.UpdateRule(lbClient, policy.ID, rule.ID, updateL7ruleOpts).Extract() - if err != nil { - t.Fatalf("Unable to update l7 rule: %v", err) - } + th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newRule, err := l7policies.GetRule(lbClient, newPolicy.ID, rule.ID).Extract() - if err != nil { - t.Fatalf("Unable to get l7 rule: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newRule) // Pool pool, err := CreatePool(t, lbClient, lb) - if err != nil { - t.Fatalf("Unable to create pool: %v", err) - } + 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() - if err != nil { - t.Fatalf("Unable to update pool") - } + th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPool, err := pools.Get(lbClient, pool.ID).Extract() - if err != nil { - t.Fatalf("Unable to get pool") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPool) - + th.AssertEquals(t, newPool.Name, poolName) th.AssertEquals(t, newPool.Description, poolDescription) // Member member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) - if err != nil { - t.Fatalf("Unable to create member: %v", err) - } + th.AssertNoErr(t, err) defer DeleteMember(t, lbClient, lb.ID, pool.ID, member.ID) memberName := "" @@ -254,18 +197,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Weight: &newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() - if err != nil { - t.Fatalf("Unable to update pool") - } + th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() - if err != nil { - t.Fatalf("Unable to get member") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newMember) th.AssertEquals(t, newMember.Name, memberName) @@ -286,17 +225,13 @@ func TestLoadbalancersCRUD(t *testing.T) { } newMember, err = pools.GetMember(lbClient, pool.ID, member.ID).Extract() - if err != nil { - t.Fatalf("Unable to get member") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newMember) // Monitor monitor, err := CreateMonitor(t, lbClient, lb, newPool) - if err != nil { - t.Fatalf("Unable to create monitor: %v", err) - } + th.AssertNoErr(t, err) defer DeleteMonitor(t, lbClient, lb.ID, monitor.ID) monName := "" @@ -306,57 +241,42 @@ func TestLoadbalancersCRUD(t *testing.T) { Delay: newDelay, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() - if err != nil { - t.Fatalf("Unable to update monitor") - } + th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() - if err != nil { - t.Fatalf("Unable to get monitor") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) th.AssertEquals(t, newMonitor.Name, monName) + th.AssertEquals(t, newMonitor.Delay, newDelay) } func TestLoadbalancersCascadeCRUD(t *testing.T) { netClient, err := clients.NewNetworkV2Client() - if err != nil { - t.Fatalf("Unable to create a networking client: %v", err) - } + th.AssertNoErr(t, err) lbClient, err := clients.NewLoadBalancerV2Client() - if err != nil { - t.Fatalf("Unable to create a loadbalancer client: %v", err) - } + th.AssertNoErr(t, err) network, err := networking.CreateNetwork(t, netClient) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, netClient, network.ID) subnet, err := networking.CreateSubnet(t, netClient, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } + th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) - if err != nil { - t.Fatalf("Unable to create loadbalancer: %v", err) - } + th.AssertNoErr(t, err) defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() - if err != nil { - t.Fatalf("Unable to get loadbalancer: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, newLB) @@ -365,105 +285,81 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { // Listener listener, err := CreateListener(t, lbClient, lb) - if err != nil { - t.Fatalf("Unable to create listener: %v", err) - } + th.AssertNoErr(t, err) listenerDescription := "Some listener description" updateListenerOpts := listeners.UpdateOpts{ Description: &listenerDescription, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() - if err != nil { - t.Fatalf("Unable to update listener") - } + th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newListener, err := listeners.Get(lbClient, listener.ID).Extract() - if err != nil { - t.Fatalf("Unable to get listener") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newListener) // Pool pool, err := CreatePool(t, lbClient, lb) - if err != nil { - t.Fatalf("Unable to create pool: %v", err) - } + th.AssertNoErr(t, err) poolDescription := "Some pool description" updatePoolOpts := pools.UpdateOpts{ Description: &poolDescription, } _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() - if err != nil { - t.Fatalf("Unable to update pool") - } + th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newPool, err := pools.Get(lbClient, pool.ID).Extract() - if err != nil { - t.Fatalf("Unable to get pool") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newPool) // Member member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) - if err != nil { - t.Fatalf("Unable to create member: %v", err) - } + th.AssertNoErr(t, err) newWeight := tools.RandomInt(11, 100) updateMemberOpts := pools.UpdateMemberOpts{ Weight: &newWeight, } _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() - if err != nil { - t.Fatalf("Unable to update pool") - } + th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() - if err != nil { - t.Fatalf("Unable to get member") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newMember) // Monitor monitor, err := CreateMonitor(t, lbClient, lb, newPool) - if err != nil { - t.Fatalf("Unable to create monitor: %v", err) - } + th.AssertNoErr(t, err) newDelay := tools.RandomInt(20, 30) updateMonitorOpts := monitors.UpdateOpts{ Delay: newDelay, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() - if err != nil { - t.Fatalf("Unable to update monitor") - } + th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() - if err != nil { - t.Fatalf("Unable to get monitor") - } + th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go new file mode 100644 index 0000000000..fc7c3d9e36 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking loadbalancer l7policies + +package lbaas_v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" +) + +func TestL7PoliciesList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := l7policies.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list l7policies: %v", err) + } + + allL7Policies, err := l7policies.ExtractL7Policies(allPages) + if err != nil { + t.Fatalf("Unable to extract l7policies: %v", err) + } + + for _, policy := range allL7Policies { + tools.PrintResource(t, policy) + } +} diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index 5b5ffd730d..90909d9206 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" @@ -31,7 +32,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal Name: listenerName, Description: listenerDescription, LoadbalancerID: lb.ID, - Protocol: "TCP", + Protocol: listeners.ProtocolHTTP, ProtocolPort: listenerPort, } @@ -49,7 +50,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal th.AssertEquals(t, listener.Name, listenerName) th.AssertEquals(t, listener.Description, listenerDescription) th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID) - th.AssertEquals(t, listener.Protocol, "TCP") + th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolHTTP)) th.AssertEquals(t, listener.ProtocolPort, listenerPort) return listener, nil @@ -87,7 +88,7 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI th.AssertEquals(t, lb.Name, lbName) th.AssertEquals(t, lb.Description, lbDescription) th.AssertEquals(t, lb.VipSubnetID, subnetID) - th.AssertEquals(t, lb.AdminStateUp, gophercloud.Enabled) + th.AssertEquals(t, lb.AdminStateUp, true) return lb, nil } @@ -144,7 +145,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala Delay: 10, Timeout: 5, MaxRetries: 5, - Type: "PING", + Type: monitors.TypePING, } monitor, err := monitors.Create(client, createOpts).Extract() @@ -159,6 +160,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala } th.AssertEquals(t, monitor.Name, monitorName) + th.AssertEquals(t, monitor.Type, monitors.TypePING) return monitor, nil } @@ -175,7 +177,7 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance createOpts := pools.CreateOpts{ Name: poolName, Description: poolDescription, - Protocol: pools.ProtocolTCP, + Protocol: pools.ProtocolHTTP, LoadbalancerID: lb.ID, LBMethod: pools.LBMethodLeastConnections, } @@ -193,13 +195,112 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance th.AssertEquals(t, pool.Name, poolName) th.AssertEquals(t, pool.Description, poolDescription) - th.AssertEquals(t, pool.Protocol, pools.ProtocolTCP) + th.AssertEquals(t, pool.Protocol, string(pools.ProtocolHTTP)) th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID) - th.AssertEquals(t, pool.LBMethod, pools.LBMethodLeastConnections) + th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections)) return pool, nil } +// CreateL7Policy will create a l7 policy with a random name with a specified listener +// and loadbalancer. An error will be returned if the l7 policy could not be +// created. +func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) { + policyName := tools.RandomString("TESTACCT-", 8) + policyDescription := tools.RandomString("TESTACCT-DESC-", 8) + + t.Logf("Attempting to create l7 policy %s on the %s listener ID", policyName, listener.ID) + + createOpts := l7policies.CreateOpts{ + Name: policyName, + Description: policyDescription, + ListenerID: listener.ID, + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + } + + policy, err := l7policies.Create(client, createOpts).Extract() + if err != nil { + return policy, err + } + + t.Logf("Successfully created l7 policy %s", policyName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active") + } + + th.AssertEquals(t, policy.Name, policyName) + th.AssertEquals(t, policy.Description, policyDescription) + th.AssertEquals(t, policy.ListenerID, listener.ID) + th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL)) + th.AssertEquals(t, policy.RedirectURL, "http://www.example.com") + + return policy, nil +} + +// CreateL7Rule creates a l7 rule for specified l7 policy. +func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer) (*l7policies.Rule, error) { + t.Logf("Attempting to create l7 rule for policy %s", policyID) + + createOpts := l7policies.CreateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeStartWith, + Value: "/api", + } + + rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract() + if err != nil { + return rule, err + } + + t.Logf("Successfully created l7 rule for policy %s", policyID) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active") + } + + th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath)) + th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith)) + th.AssertEquals(t, rule.Value, "/api") + + return rule, nil +} + +// DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if +// the l7 policy could not be deleted. This works best when used as a deferred +// function. +func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) { + t.Logf("Attempting to delete l7 policy %s", policyID) + + if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete l7 policy: %v", err) + } + + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + t.Logf("Successfully deleted l7 policy %s", policyID) +} + +// DeleteL7Rule will delete a specified l7 rule. A fatal error will occur if +// the l7 rule could not be deleted. This works best when used as a deferred +// function. +func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) { + t.Logf("Attempting to delete l7 rule %s", ruleID) + + if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete l7 rule: %v", err) + } + + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + t.Logf("Successfully deleted l7 rule %s", ruleID) +} + // DeleteListener will delete a specified listener. A fatal error will occur if // the listener could not be deleted. This works best when used as a deferred // function. diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go new file mode 100644 index 0000000000..30136b0494 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking loadbalancer listeners + +package lbaas_v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" +) + +func TestListenersList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := listeners.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list listeners: %v", err) + } + + allListeners, err := listeners.ExtractListeners(allPages) + if err != nil { + t.Fatalf("Unable to extract listeners: %v", err) + } + + for _, listener := range allListeners { + tools.PrintResource(t, listener) + } +} 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 b39346bd27..6121dfe878 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_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/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" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" @@ -46,11 +47,29 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, client, lb.ID) + lbDescription := "" + updateLoadBalancerOpts := loadbalancers.UpdateOpts{ + Description: &lbDescription, + } + _, err = loadbalancers.Update(client, lb.ID, updateLoadBalancerOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + newLB, err := loadbalancers.Get(client, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newLB) + th.AssertEquals(t, newLB.Description, lbDescription) + + lbStats, err := loadbalancers.GetStats(client, lb.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, lbStats) + // Because of the time it takes to create a loadbalancer, // this test will include some other resources. @@ -76,9 +95,64 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newListener) + th.AssertEquals(t, newListener.Name, listenerName) th.AssertEquals(t, newListener.Description, listenerDescription) + // L7 policy + policy, err := CreateL7Policy(t, client, listener, lb) + th.AssertNoErr(t, err) + defer DeleteL7Policy(t, client, lb.ID, policy.ID) + + newDescription := "" + updateL7policyOpts := l7policies.UpdateOpts{ + Description: &newDescription, + } + _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newPolicy, err := l7policies.Get(client, policy.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPolicy) + + th.AssertEquals(t, newPolicy.Description, newDescription) + + // L7 rule + rule, err := CreateL7Rule(t, client, newPolicy.ID, lb) + th.AssertNoErr(t, err) + defer DeleteL7Rule(t, client, lb.ID, policy.ID, rule.ID) + + allPages, err := l7policies.ListRules(client, 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) + } + + /* NOT supported on F5 driver */ + updateL7ruleOpts := l7policies.UpdateRuleOpts{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images/special*", + } + _, err = l7policies.UpdateRule(client, policy.ID, rule.ID, updateL7ruleOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newRule, err := l7policies.GetRule(client, newPolicy.ID, rule.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newRule) + // Pool pool, err := CreatePool(t, client, lb) th.AssertNoErr(t, err) @@ -151,6 +225,7 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) - th.AssertEquals(t, newMonitor.Name, newMonitor) + + th.AssertEquals(t, newMonitor.Name, monName) th.AssertEquals(t, newMonitor.Delay, newDelay) } diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go new file mode 100644 index 0000000000..84b0c867d7 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking loadbalancer monitors + +package lbaas_v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" +) + +func TestMonitorsList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := monitors.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list monitors: %v", err) + } + + allMonitors, err := monitors.ExtractMonitors(allPages) + if err != nil { + t.Fatalf("Unable to extract monitors: %v", err) + } + + for _, monitor := range allMonitors { + tools.PrintResource(t, monitor) + } +} diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go new file mode 100644 index 0000000000..bcab7fd55c --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go @@ -0,0 +1,32 @@ +// +build acceptance networking loadbalancer pools + +package lbaas_v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" +) + +func TestPoolsList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a loadbalancer client: %v", err) + } + + allPages, err := pools.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list pools: %v", err) + } + + allPools, err := pools.ExtractPools(allPages) + if err != nil { + t.Fatalf("Unable to extract pools: %v", err) + } + + for _, pool := range allPools { + tools.PrintResource(t, pool) + } +} diff --git a/script/stackenv b/script/stackenv index af7451cc87..af4a5bc13e 100644 --- a/script/stackenv +++ b/script/stackenv @@ -7,7 +7,7 @@ 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 _NETWORK_ID=$(openstack network show private -c id -f value) -_SUBNET_ID=$(openstack network show private -c subnets -f value | cut -d, -f1) +_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 ' ') From acbd6abe1624b9ea9541f84146a079ac0c24d7b5 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 13 Dec 2018 17:47:04 +0100 Subject: [PATCH 0611/2296] LBaaS_v2: L7policy redirect_pool_id and redirect_url should not be omitted during update (#1351) * LBaaSv2/Octavia: Improve LB tests * LBaaSv2/Octavia: Don't omit null redirect_pool_id, redirect_url and key in l7policy --- .../openstack/loadbalancer/v2/loadbalancer.go | 58 ++++++++++++------- .../loadbalancer/v2/loadbalancers_test.go | 27 +++++++++ .../v2/extensions/lbaas_v2/lbaas_v2.go | 58 ++++++++++++------- .../extensions/lbaas_v2/loadbalancers_test.go | 27 +++++++++ .../loadbalancer/v2/l7policies/requests.go | 6 +- .../v2/l7policies/testing/fixtures.go | 2 + .../lbaas_v2/l7policies/requests.go | 6 +- .../lbaas_v2/l7policies/testing/fixtures.go | 2 + 8 files changed, 140 insertions(+), 46 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index e8f3329428..c8131207ac 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -44,7 +44,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal t.Logf("Successfully created listener %s", listenerName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, listener.Name, listenerName) @@ -124,7 +124,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan t.Logf("Successfully created member %s", memberName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return member, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return member, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, member.Name, memberName) @@ -156,7 +156,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala t.Logf("Successfully created monitor: %s", monitorName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, monitor.Name, monitorName) @@ -190,7 +190,7 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance t.Logf("Successfully created pool %s", poolName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, pool.Name, poolName) @@ -227,7 +227,7 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l t.Logf("Successfully created l7 policy %s", policyName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, policy.Name, policyName) @@ -257,7 +257,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri t.Logf("Successfully created l7 rule for policy %s", policyID) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath)) @@ -274,11 +274,13 @@ func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, polic t.Logf("Attempting to delete l7 policy %s", policyID) if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete l7 policy: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete l7 policy: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted l7 policy %s", policyID) @@ -291,11 +293,13 @@ func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyI t.Logf("Attempting to delete l7 rule %s", ruleID) if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete l7 rule: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete l7 rule: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted l7 rule %s", ruleID) @@ -308,11 +312,13 @@ func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, liste t.Logf("Attempting to delete listener %s", listenerID) if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete listener: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete listener: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted listener %s", listenerID) @@ -325,11 +331,13 @@ func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, t.Logf("Attempting to delete member %s", memberID) if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete member: %s", memberID) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete member: %s", memberID) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted member %s", memberID) @@ -346,13 +354,15 @@ func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID st } if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil { - t.Fatalf("Unable to delete loadbalancer: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete loadbalancer: %v", err) + } } t.Logf("Waiting for loadbalancer %s to delete", lbID) if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Loadbalancer did not delete in time.") + t.Fatalf("Loadbalancer did not delete in time: %s", err) } t.Logf("Successfully deleted loadbalancer %s", lbID) @@ -388,11 +398,13 @@ func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monito t.Logf("Attempting to delete monitor %s", monitorID) if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete monitor: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete monitor: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted monitor %s", monitorID) @@ -404,11 +416,13 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st t.Logf("Attempting to delete pool %s", poolID) if err := pools.Delete(client, poolID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete pool: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete pool: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted pool %s", poolID) @@ -433,6 +447,10 @@ func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status st return true, nil } + if current.ProvisioningStatus == "ERROR" { + return false, fmt.Errorf("Load balancer is in ERROR state") + } + return false, nil }) } diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index bf9b50244d..aaa247b7cf 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -115,6 +115,7 @@ func TestLoadbalancersCRUD(t *testing.T) { newDescription := "" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, + RedirectURL: &policy.RedirectURL, } _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) @@ -129,6 +130,7 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) + th.AssertEquals(t, newPolicy.RedirectURL, policy.RedirectURL) // L7 rule rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) @@ -185,6 +187,31 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertEquals(t, newPool.Name, poolName) th.AssertEquals(t, newPool.Description, poolDescription) + // Update L7policy to redirect to pool + updateL7policyOpts = l7policies.UpdateOpts{ + Action: l7policies.ActionRedirectToPool, + RedirectPoolID: &newPool.ID, + } + _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); 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) + + // 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, newPool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index 90909d9206..a780af1150 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -44,7 +44,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal t.Logf("Successfully created listener %s", listenerName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, listener.Name, listenerName) @@ -124,7 +124,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan t.Logf("Successfully created member %s", memberName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return member, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return member, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, member.Name, memberName) @@ -156,7 +156,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala t.Logf("Successfully created monitor: %s", monitorName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, monitor.Name, monitorName) @@ -190,7 +190,7 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance t.Logf("Successfully created pool %s", poolName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, pool.Name, poolName) @@ -227,7 +227,7 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l t.Logf("Successfully created l7 policy %s", policyName) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, policy.Name, policyName) @@ -257,7 +257,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri t.Logf("Successfully created l7 rule for policy %s", policyID) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active") + return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath)) @@ -274,11 +274,13 @@ func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, polic t.Logf("Attempting to delete l7 policy %s", policyID) if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete l7 policy: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete l7 policy: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted l7 policy %s", policyID) @@ -291,11 +293,13 @@ func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyI t.Logf("Attempting to delete l7 rule %s", ruleID) if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete l7 rule: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete l7 rule: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted l7 rule %s", ruleID) @@ -308,11 +312,13 @@ func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, liste t.Logf("Attempting to delete listener %s", listenerID) if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete listener: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete listener: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted listener %s", listenerID) @@ -325,11 +331,13 @@ func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, t.Logf("Attempting to delete member %s", memberID) if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete member: %s", memberID) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete member: %s", memberID) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted member %s", memberID) @@ -342,13 +350,15 @@ func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID st t.Logf("Attempting to delete loadbalancer %s", lbID) if err := loadbalancers.Delete(client, lbID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete loadbalancer: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete loadbalancer: %v", err) + } } t.Logf("Waiting for loadbalancer %s to delete", lbID) if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Loadbalancer did not delete in time.") + t.Fatalf("Loadbalancer did not delete in time: %s", err) } t.Logf("Successfully deleted loadbalancer %s", lbID) @@ -361,11 +371,13 @@ func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monito t.Logf("Attempting to delete monitor %s", monitorID) if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete monitor: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete monitor: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted monitor %s", monitorID) @@ -377,11 +389,13 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st t.Logf("Attempting to delete pool %s", poolID) if err := pools.Delete(client, poolID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete pool: %v", err) + if _, ok := err.(gophercloud.ErrDefault404); !ok { + t.Fatalf("Unable to delete pool: %v", err) + } } if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") + t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } t.Logf("Successfully deleted pool %s", poolID) @@ -406,6 +420,10 @@ func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status st return true, nil } + if current.ProvisioningStatus == "ERROR" { + return false, fmt.Errorf("Load balancer is in ERROR state") + } + return false, nil }) } 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 6121dfe878..29d64247db 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -107,6 +107,7 @@ func TestLoadbalancersCRUD(t *testing.T) { newDescription := "" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, + RedirectURL: &policy.RedirectURL, } _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) @@ -121,6 +122,7 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) + th.AssertEquals(t, newPolicy.RedirectURL, policy.RedirectURL) // L7 rule rule, err := CreateL7Rule(t, client, newPolicy.ID, lb) @@ -178,6 +180,31 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertEquals(t, newPool.Name, poolName) th.AssertEquals(t, newPool.Description, poolDescription) + // Update L7policy to redirect to pool + updateL7policyOpts = l7policies.UpdateOpts{ + Action: l7policies.ActionRedirectToPool, + RedirectPoolID: &newPool.ID, + } + _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newPolicy, err = l7policies.Get(client, 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) + + // Workaround for proper delete order + defer DeleteL7Policy(t, client, lb.ID, policy.ID) + defer DeleteL7Rule(t, client, lb.ID, policy.ID, rule.ID) + // Member member, err := CreateMember(t, client, lb, newPool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index f73ab93e69..59da64bcb7 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -170,11 +170,11 @@ type UpdateOpts struct { // 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"` + RedirectPoolID *string `json:"redirect_pool_id"` // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. - RedirectURL *string `json:"redirect_url,omitempty"` + RedirectURL *string `json:"redirect_url"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). @@ -322,7 +322,7 @@ type UpdateRuleOpts struct { Value string `json:"value,omitempty"` // The key to use for the comparison. For example, the name of the cookie to evaluate. - Key *string `json:"key,omitempty"` + Key *string `json:"key"` // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index e4c951728b..b44c06150f 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -226,6 +226,7 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) { "l7policy": { "name": "NewL7PolicyName", "action": "REDIRECT_TO_URL", + "redirect_pool_id": null, "redirect_url": "http://www.new-example.com" } }`) @@ -366,6 +367,7 @@ func HandleRuleUpdateSuccessfully(t *testing.T) { "rule": { "compare_type": "REGEX", "invert": false, + "key": null, "type": "PATH", "value": "/images/special*" } diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go index 470c336e49..07c78c082d 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go @@ -170,11 +170,11 @@ type UpdateOpts struct { // 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"` + RedirectPoolID *string `json:"redirect_pool_id"` // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. - RedirectURL *string `json:"redirect_url,omitempty"` + RedirectURL *string `json:"redirect_url"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). @@ -322,7 +322,7 @@ type UpdateRuleOpts struct { Value string `json:"value,omitempty"` // The key to use for the comparison. For example, the name of the cookie to evaluate. - Key *string `json:"key,omitempty"` + Key *string `json:"key"` // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go index e30b3f7f75..5cfa308b0a 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go @@ -226,6 +226,7 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) { "l7policy": { "name": "NewL7PolicyName", "action": "REDIRECT_TO_URL", + "redirect_pool_id": null, "redirect_url": "http://www.new-example.com" } }`) @@ -366,6 +367,7 @@ func HandleRuleUpdateSuccessfully(t *testing.T) { "rule": { "compare_type": "REGEX", "invert": false, + "key": null, "type": "PATH", "value": "/images/special*" } From 02b0d266bc97833917117e229371104329a65ff0 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 14 Dec 2018 02:08:04 +0100 Subject: [PATCH 0612/2296] LBaaSv2/Octavia: add "L7Policies" member into the Listerner struct (#1354) --- openstack/loadbalancer/v2/listeners/results.go | 4 ++++ .../networking/v2/extensions/lbaas_v2/listeners/results.go | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index c3e7129157..847a03fb40 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -2,6 +2,7 @@ package listeners import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" "github.com/gophercloud/gophercloud/pagination" ) @@ -55,6 +56,9 @@ type Listener struct { // Pools are the pools which are part of this listener. Pools []pools.Pool `json:"pools"` + // L7policies are the L7 policies which are part of this listener. + L7Policies []l7policies.L7Policy `json:"l7policies"` + // The provisioning status of the Listener. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go index 81cda90361..ae10579322 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go @@ -2,6 +2,7 @@ package listeners import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" "github.com/gophercloud/gophercloud/pagination" ) @@ -55,6 +56,11 @@ type Listener struct { // Pools are the pools which are part of this listener. Pools []pools.Pool `json:"pools"` + // L7policies are the L7 policies which are part of this listener. + // This field seems to only be returned during a call to a load balancer's /status + // see: https://github.com/gophercloud/gophercloud/issues/1352 + L7Policies []l7policies.L7Policy `json:"l7policies"` + // The provisioning status of the listener. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` From ab9182e79a63718371479e0b0e4969d55ab175ff Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 14 Dec 2018 20:23:35 -0700 Subject: [PATCH 0613/2296] Fix l7policy update (#1357) * Neutron LBaaS v2: Fix l7policy update fields * Octavia v2: Fix l7policy update fields * Acc Tests: Updating loadbalancer tests for L7 policy update --- .../loadbalancer/v2/loadbalancers_test.go | 5 +- .../extensions/lbaas_v2/loadbalancers_test.go | 5 +- .../loadbalancer/v2/l7policies/requests.go | 34 ++++++++++-- .../v2/l7policies/testing/fixtures.go | 52 ++++++++++++++++++- .../v2/l7policies/testing/requests_test.go | 22 ++++++++ .../lbaas_v2/l7policies/requests.go | 34 ++++++++++-- .../lbaas_v2/l7policies/testing/fixtures.go | 52 ++++++++++++++++++- .../l7policies/testing/requests_test.go | 22 ++++++++ 8 files changed, 209 insertions(+), 17 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index aaa247b7cf..f105f5573e 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -115,7 +115,6 @@ func TestLoadbalancersCRUD(t *testing.T) { newDescription := "" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, - RedirectURL: &policy.RedirectURL, } _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) @@ -130,7 +129,6 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) - th.AssertEquals(t, newPolicy.RedirectURL, policy.RedirectURL) // L7 rule rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) @@ -188,9 +186,11 @@ func TestLoadbalancersCRUD(t *testing.T) { 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) @@ -207,6 +207,7 @@ func TestLoadbalancersCRUD(t *testing.T) { 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) 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 29d64247db..6856dcbbaa 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -107,7 +107,6 @@ func TestLoadbalancersCRUD(t *testing.T) { newDescription := "" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, - RedirectURL: &policy.RedirectURL, } _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) @@ -122,7 +121,6 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) - th.AssertEquals(t, newPolicy.RedirectURL, policy.RedirectURL) // L7 rule rule, err := CreateL7Rule(t, client, newPolicy.ID, lb) @@ -181,9 +179,11 @@ func TestLoadbalancersCRUD(t *testing.T) { 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(client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) @@ -200,6 +200,7 @@ func TestLoadbalancersCRUD(t *testing.T) { 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, client, lb.ID, policy.ID) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 59da64bcb7..fc1c809afa 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -170,11 +170,11 @@ type UpdateOpts struct { // 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"` + RedirectPoolID *string `json:"redirect_pool_id,omitempty"` // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. - RedirectURL *string `json:"redirect_url"` + RedirectURL *string `json:"redirect_url,omitempty"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). @@ -183,7 +183,22 @@ type UpdateOpts struct { // ToL7PolicyUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "l7policy") + b, err := gophercloud.BuildRequestBody(opts, "l7policy") + if err != nil { + return nil, err + } + + m := b["l7policy"].(map[string]interface{}) + + if m["redirect_pool_id"] == "" { + m["redirect_pool_id"] = nil + } + + if m["redirect_url"] == "" { + m["redirect_url"] = nil + } + + return b, nil } // Update allows l7policy to be updated. @@ -322,7 +337,7 @@ type UpdateRuleOpts struct { Value string `json:"value,omitempty"` // The key to use for the comparison. For example, the name of the cookie to evaluate. - Key *string `json:"key"` + Key *string `json:"key,omitempty"` // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. @@ -335,7 +350,16 @@ type UpdateRuleOpts struct { // ToRuleUpdateMap builds a request body from UpdateRuleOpts. func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "rule") + b, err := gophercloud.BuildRequestBody(opts, "rule") + if err != nil { + return nil, err + } + + if m := b["rule"].(map[string]interface{}); m["key"] == "" { + m["key"] = nil + } + + return b, nil } // UpdateRule allows Rule to be updated. diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go index b44c06150f..fea5bad570 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures.go @@ -69,6 +69,19 @@ var ( AdminStateUp: true, Rules: []l7policies.Rule{}, } + L7PolicyNullRedirectURLUpdated = l7policies.L7Policy{ + ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", + Name: "NewL7PolicyName", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: "REDIRECT_TO_URL", + Position: 1, + Description: "Redirect requests to example.com", + ProjectID: "e3cd678b11784734bc366148aa37580e", + RedirectPoolID: "", + RedirectURL: "", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + } RulePath = l7policies.Rule{ ID: "16621dbb-a736-4888-a57a-3ecd53df784c", RuleType: "PATH", @@ -162,7 +175,6 @@ const PostUpdateL7PolicyBody = ` "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", "description": "Redirect requests to example.com", "admin_state_up": true, - "redirect_pool_id": null, "redirect_url": "http://www.new-example.com", "action": "REDIRECT_TO_URL", "position": 1, @@ -174,6 +186,25 @@ const PostUpdateL7PolicyBody = ` } ` +// PostUpdateL7PolicyNullRedirectURLBody is the canned response body of a Update request +// on an existing l7policy with a null redirect_url . +const PostUpdateL7PolicyNullRedirectURLBody = ` +{ + "l7policy": { + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "description": "Redirect requests to example.com", + "admin_state_up": true, + "redirect_url": null, + "action": "REDIRECT_TO_URL", + "position": 1, + "project_id": "e3cd678b11784734bc366148aa37580e", + "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", + "name": "NewL7PolicyName", + "rules": [] + } +} +` + // HandleL7PolicyListSuccessfully sets up the test server to respond to a l7policy List request. func HandleL7PolicyListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies", func(w http.ResponseWriter, r *http.Request) { @@ -226,7 +257,6 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) { "l7policy": { "name": "NewL7PolicyName", "action": "REDIRECT_TO_URL", - "redirect_pool_id": null, "redirect_url": "http://www.new-example.com" } }`) @@ -235,6 +265,24 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) { }) } +// HandleL7PolicyUpdateNullRedirectURLSuccessfully sets up the test server to respond to a l7policy Update request. +func HandleL7PolicyUpdateNullRedirectURLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, `{ + "l7policy": { + "name": "NewL7PolicyName", + "redirect_url": null + } + }`) + + fmt.Fprintf(w, PostUpdateL7PolicyNullRedirectURLBody) + }) +} + // SingleRuleBody is the canned body of a Get request on an existing rule. const SingleRuleBody = ` { diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index 23d866a624..76a10c55f2 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -129,6 +129,26 @@ func TestUpdateL7Policy(t *testing.T) { th.CheckDeepEquals(t, L7PolicyUpdated, *actual) } +func TestUpdateL7PolicyNullRedirectURL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyUpdateNullRedirectURLSuccessfully(t) + + client := fake.ServiceClient() + newName := "NewL7PolicyName" + redirectURL := "" + actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", + l7policies.UpdateOpts{ + Name: &newName, + RedirectURL: &redirectURL, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, L7PolicyNullRedirectURLUpdated, *actual) +} + func TestUpdateL7PolicyWithInvalidOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -262,10 +282,12 @@ func TestUpdateRule(t *testing.T) { client := fake.ServiceClient() invert := false + key := "" actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", + Key: &key, Invert: &invert, }).Extract() if err != nil { diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go index 07c78c082d..9d2b3a0d35 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go @@ -170,11 +170,11 @@ type UpdateOpts struct { // 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"` + RedirectPoolID *string `json:"redirect_pool_id,omitempty"` // Requests matching this policy will be redirected to this URL. // Only valid if action is REDIRECT_TO_URL. - RedirectURL *string `json:"redirect_url"` + RedirectURL *string `json:"redirect_url,omitempty"` // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). @@ -183,7 +183,22 @@ type UpdateOpts struct { // ToL7PolicyUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "l7policy") + b, err := gophercloud.BuildRequestBody(opts, "l7policy") + if err != nil { + return nil, err + } + + m := b["l7policy"].(map[string]interface{}) + + if m["redirect_pool_id"] == "" { + m["redirect_pool_id"] = nil + } + + if m["redirect_url"] == "" { + m["redirect_url"] = nil + } + + return b, nil } // Update allows l7policy to be updated. @@ -322,7 +337,7 @@ type UpdateRuleOpts struct { Value string `json:"value,omitempty"` // The key to use for the comparison. For example, the name of the cookie to evaluate. - Key *string `json:"key"` + Key *string `json:"key,omitempty"` // When true the logic of the rule is inverted. For example, with invert true, // equal to would become not equal to. Default is false. @@ -335,7 +350,16 @@ type UpdateRuleOpts struct { // ToRuleUpdateMap builds a request body from UpdateRuleOpts. func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "rule") + b, err := gophercloud.BuildRequestBody(opts, "rule") + if err != nil { + return nil, err + } + + if m := b["rule"].(map[string]interface{}); m["key"] == "" { + m["key"] = nil + } + + return b, nil } // UpdateRule allows Rule to be updated. diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go index 5cfa308b0a..87c6e66d22 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures.go @@ -69,6 +69,19 @@ var ( AdminStateUp: true, Rules: []l7policies.Rule{}, } + L7PolicyNullRedirectURLUpdated = l7policies.L7Policy{ + ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", + Name: "NewL7PolicyName", + ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", + Action: "REDIRECT_TO_URL", + Position: 1, + Description: "Redirect requests to example.com", + TenantID: "e3cd678b11784734bc366148aa37580e", + RedirectPoolID: "", + RedirectURL: "", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + } RulePath = l7policies.Rule{ ID: "16621dbb-a736-4888-a57a-3ecd53df784c", RuleType: "PATH", @@ -174,6 +187,26 @@ const PostUpdateL7PolicyBody = ` } ` +// PostUpdateL7PolicyNullRedirectURLBody is the canned response body of a Update request +// on an existing l7policy with a null redirect_url . +const PostUpdateL7PolicyNullRedirectURLBody = ` +{ + "l7policy": { + "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", + "description": "Redirect requests to example.com", + "admin_state_up": true, + "redirect_pool_id": null, + "redirect_url": null, + "action": "REDIRECT_TO_URL", + "position": 1, + "tenant_id": "e3cd678b11784734bc366148aa37580e", + "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", + "name": "NewL7PolicyName", + "rules": [] + } +} +` + // HandleL7PolicyListSuccessfully sets up the test server to respond to a l7policy List request. func HandleL7PolicyListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v2.0/lbaas/l7policies", func(w http.ResponseWriter, r *http.Request) { @@ -226,7 +259,6 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) { "l7policy": { "name": "NewL7PolicyName", "action": "REDIRECT_TO_URL", - "redirect_pool_id": null, "redirect_url": "http://www.new-example.com" } }`) @@ -235,6 +267,24 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) { }) } +// HandleL7PolicyUpdateNullRedirectURLSuccessfully sets up the test server to respond to a l7policy Update request. +func HandleL7PolicyUpdateNullRedirectURLSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, `{ + "l7policy": { + "name": "NewL7PolicyName", + "redirect_url": null + } + }`) + + fmt.Fprintf(w, PostUpdateL7PolicyNullRedirectURLBody) + }) +} + // SingleRuleBody is the canned body of a Get request on an existing rule. const SingleRuleBody = ` { diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go index 7e9f271b4b..f8e67f4b80 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go @@ -129,6 +129,26 @@ func TestUpdateL7Policy(t *testing.T) { th.CheckDeepEquals(t, L7PolicyUpdated, *actual) } +func TestUpdateL7PolicyNullRedirectURL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleL7PolicyUpdateNullRedirectURLSuccessfully(t) + + client := fake.ServiceClient() + newName := "NewL7PolicyName" + redirectURL := "" + actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", + l7policies.UpdateOpts{ + Name: &newName, + RedirectURL: &redirectURL, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, L7PolicyNullRedirectURLUpdated, *actual) +} + func TestUpdateL7PolicyWithInvalidOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -262,11 +282,13 @@ func TestUpdateRule(t *testing.T) { client := fake.ServiceClient() invert := false + key := "" actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", Invert: &invert, + Key: &key, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From bdd8b1ecd793ffb03d43314c66565be3d6a7a582 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 15 Dec 2018 23:49:39 +0100 Subject: [PATCH 0614/2296] LBaaS_v2: Listener's default_pool_id should not be omitted during update (#1356) * LBaaS_v2: Listener's default_pool_id should not be omitted during update * LBaaS_v2: Listener's empty default_pool_id tests --- .../loadbalancer/v2/loadbalancers_test.go | 37 +++++++++++++++++++ .../extensions/lbaas_v2/loadbalancers_test.go | 37 +++++++++++++++++++ .../loadbalancer/v2/listeners/requests.go | 11 +++++- .../v2/listeners/testing/fixtures.go | 1 + .../v2/listeners/testing/requests_test.go | 6 ++- .../extensions/lbaas_v2/listeners/requests.go | 11 +++++- .../lbaas_v2/listeners/testing/fixtures.go | 1 + .../listeners/testing/requests_test.go | 6 ++- 8 files changed, 104 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index f105f5573e..4db772104c 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -213,6 +213,43 @@ func TestLoadbalancersCRUD(t *testing.T) { 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, + } + _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newListener, err = listeners.Get(lbClient, listener.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newListener) + + th.AssertEquals(t, newListener.DefaultPoolID, pool.ID) + + // Remove listener's default pool ID + emptyPoolID := "" + updateListenerOpts = listeners.UpdateOpts{ + DefaultPoolID: &emptyPoolID, + } + _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newListener, err = listeners.Get(lbClient, listener.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newListener) + + th.AssertEquals(t, newListener.DefaultPoolID, "") + // Member member, err := CreateMember(t, lbClient, lb, newPool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, 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 6856dcbbaa..49184277de 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -206,6 +206,43 @@ func TestLoadbalancersCRUD(t *testing.T) { defer DeleteL7Policy(t, client, lb.ID, policy.ID) defer DeleteL7Rule(t, client, lb.ID, policy.ID, rule.ID) + // Update listener's default pool ID + updateListenerOpts = listeners.UpdateOpts{ + DefaultPoolID: &pool.ID, + } + _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newListener, err = listeners.Get(client, listener.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newListener) + + th.AssertEquals(t, newListener.DefaultPoolID, pool.ID) + + // Remove listener's default pool ID + emptyPoolID := "" + updateListenerOpts = listeners.UpdateOpts{ + DefaultPoolID: &emptyPoolID, + } + _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + newListener, err = listeners.Get(client, listener.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newListener) + + th.AssertEquals(t, newListener.DefaultPoolID, "") + // Member member, err := CreateMember(t, client, lb, newPool, subnet.ID, subnet.CIDR) th.AssertNoErr(t, err) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 6c698e6669..c6412eb657 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -174,7 +174,16 @@ type UpdateOpts struct { // ToListenerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "listener") + b, err := gophercloud.BuildRequestBody(opts, "listener") + if err != nil { + return nil, err + } + + if m := b["listener"].(map[string]interface{}); m["default_pool_id"] == "" { + m["default_pool_id"] = nil + } + + return b, nil } // Update is an operation which modifies the attributes of the specified diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index 966bcc455f..310fd391a3 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -224,6 +224,7 @@ func HandleListenerUpdateSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, `{ "listener": { "name": "NewListenerName", + "default_pool_id": null, "connection_limit": 1001 } }`) diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 449358b6d9..3ceeddc4d4 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -126,9 +126,11 @@ func TestUpdateListener(t *testing.T) { client := fake.ServiceClient() i1001 := 1001 name := "NewListenerName" + defaultPoolID := "" actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ - Name: &name, - ConnLimit: &i1001, + Name: &name, + ConnLimit: &i1001, + DefaultPoolID: &defaultPoolID, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index 5d30efc2a2..f2966b6c44 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -179,7 +179,16 @@ type UpdateOpts struct { // ToListenerUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "listener") + b, err := gophercloud.BuildRequestBody(opts, "listener") + if err != nil { + return nil, err + } + + if m := b["listener"].(map[string]interface{}); m["default_pool_id"] == "" { + m["default_pool_id"] = nil + } + + return b, nil } // Update is an operation which modifies the attributes of the specified diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go index fa4fa25c18..5a5f050324 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures.go @@ -204,6 +204,7 @@ func HandleListenerUpdateSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, `{ "listener": { "name": "NewListenerName", + "default_pool_id": null, "connection_limit": 1001 } }`) diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go index 5bcf4d5a99..80f56709d6 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go @@ -126,9 +126,11 @@ func TestUpdateListener(t *testing.T) { client := fake.ServiceClient() i1001 := 1001 name := "NewListenerName" + defaultPoolID := "" actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ - Name: &name, - ConnLimit: &i1001, + Name: &name, + ConnLimit: &i1001, + DefaultPoolID: &defaultPoolID, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From 239b98b295da175b9e38cca4363af37d291c8b83 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Tue, 18 Dec 2018 18:54:29 -0800 Subject: [PATCH 0615/2296] Senlin: Nodes Op (#1353) * Senlin: Nodes Op * Changed function naming from abbreviation 'Op' to wording 'Operation'. Changed the struct OperationOpts's Operation field to a required field. * Fixed doc indentation for nodes ops --- .../openstack/clustering/v1/nodes_test.go | 54 ++++++++++++++++ openstack/clustering/v1/nodes/doc.go | 14 +++++ openstack/clustering/v1/nodes/requests.go | 61 +++++++++++++++++++ openstack/clustering/v1/nodes/results.go | 15 +++++ .../clustering/v1/nodes/testing/fixtures.go | 19 ++++++ .../v1/nodes/testing/requests_test.go | 14 +++++ openstack/clustering/v1/nodes/urls.go | 4 ++ 7 files changed, 181 insertions(+) diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index 0890d45ff1..780b88291b 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -67,3 +67,57 @@ func TestNodesCRUD(t *testing.T) { tools.PrintResource(t, node) tools.PrintResource(t, node.Metadata) } + +// Performs an operation on a node +func TestNodesOps(t *testing.T) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.4" + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + node, err := CreateNode(t, client, cluster.ID, profile.ID) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node.ID) + + ops := []nodes.OperationOpts{ + // 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.RebootOperation, Params: nodes.OperationParams{"type": "SOFT"}}, + {Operation: nodes.ChangePasswordOperation, Params: nodes.OperationParams{"admin_pass": "test"}}, + {Operation: nodes.LockOperation}, + {Operation: nodes.UnlockOperation}, + {Operation: nodes.SuspendOperation}, + {Operation: nodes.ResumeOperation}, + {Operation: nodes.RescueOperation, Params: nodes.OperationParams{"image_ref": choices.ImageID}}, + {Operation: nodes.PauseOperation}, + {Operation: nodes.UnpauseOperation}, + {Operation: nodes.StopOperation}, + {Operation: nodes.StartOperation}, + } + + for _, op := range ops { + opName := string(op.Operation) + t.Logf("Attempting to perform '%s' on node: %s", opName, node.ID) + actionID, res := nodes.Ops(client, node.ID, op).Extract() + th.AssertNoErr(t, res) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + node, err = nodes.Get(client, node.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "Operation '"+opName+"' succeeded", node.StatusReason) + t.Logf("Successfully performed '%s' on node: %s", opName, node.ID) + } +} diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index 3257b8d12d..cffe2a3cd0 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -70,5 +70,19 @@ Example to Get a Node } fmt.Printf("%+v\n", node) + +Example to Perform an Operation on a Node + + serviceClient.Microversion = "1.4" + nodeID := "node123" + operationOpts := nodes.OperationOpts{ + Operation: nodes.RebootOperation, + Params: nodes.OperationParams{"type": "SOFT"}, + } + actionID, err := nodes.Ops(serviceClient, nodeID, operationOpts).Extract() + if err != nil { + panic(err) + } + */ package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index 98bbd6d730..e7c4c36038 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -144,3 +144,64 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { } return } + +// OperationName represents valid values for node operation +type OperationName string + +const ( + // Nova Profile Op Names + RebootOperation OperationName = "reboot" + RebuildOperation OperationName = "rebuild" + ChangePasswordOperation OperationName = "change_password" + PauseOperation OperationName = "pause" + UnpauseOperation OperationName = "unpause" + SuspendOperation OperationName = "suspend" + ResumeOperation OperationName = "resume" + LockOperation OperationName = "lock" + UnlockOperation OperationName = "unlock" + StartOperation OperationName = "start" + StopOperation OperationName = "stop" + RescueOperation OperationName = "rescue" + UnrescueOperation OperationName = "unrescue" + EvacuateOperation OperationName = "evacuate" + + // Heat Pofile Op Names + AbandonOperation OperationName = "abandon" +) + +// ToNodeOperationMap constructs a request body from OperationOpts. +func (opts OperationOpts) ToNodeOperationMap() (map[string]interface{}, error) { + optsMap := map[string]interface{}{string(opts.Operation): opts.Params} + return optsMap, nil +} + +// OperationOptsBuilder allows extensions to add additional parameters to the +// Op request. +type OperationOptsBuilder interface { + ToNodeOperationMap() (map[string]interface{}, error) +} +type OperationParams map[string]interface{} + +// OperationOpts represents options used to perform an operation on a node +type OperationOpts struct { + Operation OperationName `json:"operation" required:"true"` + Params OperationParams `json:"params,omitempty"` +} + +func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { + b, err := opts.ToNodeOperationMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index 57987c27f3..8921a7649e 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -127,3 +127,18 @@ func ExtractNodes(r pagination.Page) ([]Node, error) { err := (r.(NodePage)).ExtractInto(&s) return s.Nodes, err } + +// ActionResult is the response of Senlin actions. Call its Extract method to +// obtain the Action ID of the action. +type ActionResult struct { + gophercloud.Result +} + +// Extract interprets any Action result as an Action. +func (r ActionResult) Extract() (string, error) { + var s struct { + Action string `json:"action"` + } + err := r.ExtractInto(&s) + return s.Action, err +} diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go index 14ac6dbd1c..7c3872cfbe 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures.go +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -254,6 +254,13 @@ var ExpectedUpdate = nodes.Node{ User: "ab79b9647d074e46ac223a8fa297b846", } +const OperationActionResponse = ` +{ + "action": "2a0ff107-e789-4660-a122-3816c43af703" +}` + +const OperationExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" + func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -314,3 +321,15 @@ func HandleUpdateSuccessfully(t *testing.T) { fmt.Fprint(w, UpdateResponse) }) } + +func HandleOpsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/nodes/7d85f602-a948-4a30-afd4-e84f47471c15/ops", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprint(w, OperationActionResponse) + }) +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 1a8e72adc1..94f95676bd 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -103,3 +103,17 @@ func TestUpdateNode(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdate, *actual) } + +func TestOpsNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleOpsSuccessfully(t) + + nodeOpts := nodes.OperationOpts{ + Operation: nodes.PauseOperation, + } + actual, err := nodes.Ops(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", nodeOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, OperationExpectedActionID, actual) +} diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go index 5479145d0b..b8abdb4047 100644 --- a/openstack/clustering/v1/nodes/urls.go +++ b/openstack/clustering/v1/nodes/urls.go @@ -32,3 +32,7 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func opsURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "ops") +} From 31a8542aa50ad15464fa8121e014e7a280b283c0 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 20 Dec 2018 16:22:42 +0100 Subject: [PATCH 0616/2296] LBaaSv2 / Octavia: Add provisioning_status and operating_status fields (#1364) for L7 structures --- .../loadbalancer/v2/l7policies/results.go | 14 ++++++++++++ .../extensions/lbaas_v2/l7policies/results.go | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index 43c8b74d8a..dafcceed14 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -41,6 +41,13 @@ type L7Policy struct { // The administrative state of the L7 policy, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` + // The provisioning status of the L7 policy. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` + + // The operating status of the L7 policy. + OperatingStatus string `json:"operating_status"` + // Rules are List of associated L7 rule IDs. Rules []Rule `json:"rules"` } @@ -72,6 +79,13 @@ type Rule struct { // The administrative state of the L7 rule, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` + + // The provisioning status of the L7 rule. + // This value is ACTIVE, PENDING_* or ERROR. + ProvisioningStatus string `json:"provisioning_status"` + + // The operating status of the L7 policy. + OperatingStatus string `json:"operating_status"` } type commonResult struct { diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go index 4dd52cd4e6..5153b1b90c 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go @@ -41,6 +41,17 @@ type L7Policy struct { // The administrative state of the L7 policy, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` + // The provisioning status of the L7 policy. + // This value is ACTIVE, PENDING_* or ERROR. + // This field seems to only be returned during a call to a load balancer's /status + // see: https://github.com/gophercloud/gophercloud/issues/1362 + ProvisioningStatus string `json:"provisioning_status"` + + // The operating status of the L7 policy. + // This field seems to only be returned during a call to a load balancer's /status + // see: https://github.com/gophercloud/gophercloud/issues/1362 + OperatingStatus string `json:"operating_status"` + // Rules are List of associated L7 rule IDs. Rules []Rule `json:"rules"` } @@ -72,6 +83,17 @@ type Rule struct { // The administrative state of the L7 rule, which is up (true) or down (false). AdminStateUp bool `json:"admin_state_up"` + + // The provisioning status of the L7 rule. + // This value is ACTIVE, PENDING_* or ERROR. + // This field seems to only be returned during a call to a load balancer's /status + // see: https://github.com/gophercloud/gophercloud/issues/1362 + ProvisioningStatus string `json:"provisioning_status"` + + // The operating status of the L7 policy. + // This field seems to only be returned during a call to a load balancer's /status + // see: https://github.com/gophercloud/gophercloud/issues/1362 + OperatingStatus string `json:"operating_status"` } type commonResult struct { From 5f7c75bfb7e8aa25dd469f23b8bd6817c0ec1270 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 20 Dec 2018 16:23:42 +0100 Subject: [PATCH 0617/2296] LBaaSv2 / Octavia: Add pools list into the loadbalancer structure (#1367) --- openstack/loadbalancer/v2/loadbalancers/results.go | 4 ++++ .../v2/extensions/lbaas_v2/loadbalancers/results.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index cb7fa60eef..54c9caa859 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -3,6 +3,7 @@ package loadbalancers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" "github.com/gophercloud/gophercloud/pagination" ) @@ -51,6 +52,9 @@ type LoadBalancer struct { // Listeners are the listeners related to this Loadbalancer. Listeners []listeners.Listener `json:"listeners"` + + // Pools are the pools related to this Loadbalancer. + Pools []pools.Pool `json:"pools"` } // StatusTree represents the status of a loadbalancer. diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go index ea07363455..7f423c933d 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go @@ -3,6 +3,7 @@ package loadbalancers import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" "github.com/gophercloud/gophercloud/pagination" ) @@ -51,6 +52,9 @@ type LoadBalancer struct { // Listeners are the listeners related to this Loadbalancer. Listeners []listeners.Listener `json:"listeners"` + + // Pools are the pools related to this Loadbalancer. + Pools []pools.Pool `json:"pools"` } // StatusTree represents the status of a loadbalancer. From 3cff075abb212005f66a66088a622c7ea5138bd4 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 20 Dec 2018 16:26:57 +0100 Subject: [PATCH 0618/2296] LBaaSv2 / Octavia: Add operating_status field into the pool and monitor structures (#1368) --- openstack/loadbalancer/v2/monitors/results.go | 3 +++ openstack/loadbalancer/v2/pools/results.go | 3 +++ .../networking/v2/extensions/lbaas_v2/pools/results.go | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index ccf7d98509..2c3a815bae 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -74,6 +74,9 @@ type Monitor struct { // The provisioning status of the Monitor. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` + + // The operating status of the monitor. + OperatingStatus string `json:"operating_status"` } // MonitorPage is the page returned by a pager when traversing over a diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index f4674cd0d4..1dc0ed90ac 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -99,6 +99,9 @@ type Pool struct { // The provisioning status of the pool. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` + + // The operating status of the pool. + OperatingStatus string `json:"operating_status"` } // PoolPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go index 01efc768c2..fba0d3a878 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go @@ -96,6 +96,11 @@ type Pool struct { // The provisioning status of the pool. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` + + // The operating status of the pool. + // This field seems to only be returned during a call to a load balancer's /status + // see: https://github.com/gophercloud/gophercloud/issues/1362 + OperatingStatus string `json:"operating_status"` } // PoolPage is the page returned by a pager when traversing over a @@ -204,6 +209,11 @@ type Member struct { // The provisioning status of the member. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` + + // The operating status of the member. + // This field seems to only be returned during a call to a load balancer's /status + // see: https://github.com/gophercloud/gophercloud/issues/1362 + OperatingStatus string `json:"operating_status"` } // MemberPage is the page returned by a pager when traversing over a From 52046867450031e40bd4e94332fe5dbdf5cf51ca Mon Sep 17 00:00:00 2001 From: Zhang Yong Date: Thu, 20 Dec 2018 23:44:12 +0800 Subject: [PATCH 0619/2296] Amend example code (#1369) --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 30067aa352..131cc8e303 100644 --- a/doc.go +++ b/doc.go @@ -41,7 +41,7 @@ pass in the parent provider, like so: opts := gophercloud.EndpointOpts{Region: "RegionOne"} - client := openstack.NewComputeV2(provider, opts) + client, err := openstack.NewComputeV2(provider, opts) Resources From 94924357ebf6c7d448c70d65082ff7ca6f78ddc5 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 21 Dec 2018 03:37:37 +0100 Subject: [PATCH 0620/2296] Manila: add messages support (#1359) --- .../sharedfilesystems/v2/messages/messages.go | 19 +++ .../v2/messages/messages_test.go | 105 +++++++++++++++ .../sharedfilesystems/v2/messages/pkg.go | 3 + .../sharedfilesystems/v2/messages/requests.go | 66 +++++++++ .../sharedfilesystems/v2/messages/results.go | 99 ++++++++++++++ .../v2/messages/testing/fixtures.go | 115 ++++++++++++++++ .../v2/messages/testing/requests_test.go | 125 ++++++++++++++++++ .../sharedfilesystems/v2/messages/urls.go | 15 +++ 8 files changed, 547 insertions(+) create mode 100644 acceptance/openstack/sharedfilesystems/v2/messages/messages.go create mode 100644 acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go create mode 100644 acceptance/openstack/sharedfilesystems/v2/messages/pkg.go create mode 100644 openstack/sharedfilesystems/v2/messages/requests.go create mode 100644 openstack/sharedfilesystems/v2/messages/results.go create mode 100644 openstack/sharedfilesystems/v2/messages/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/messages/testing/requests_test.go create mode 100644 openstack/sharedfilesystems/v2/messages/urls.go diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/messages.go b/acceptance/openstack/sharedfilesystems/v2/messages/messages.go new file mode 100644 index 0000000000..3bc24e960d --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/messages/messages.go @@ -0,0 +1,19 @@ +package messages + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" +) + +// DeleteMessage will delete a message. An error will occur if +// the message was unable to be deleted. +func DeleteMessage(t *testing.T, client *gophercloud.ServiceClient, message *messages.Message) { + err := messages.Delete(client, message.ID).ExtractErr() + if err != nil { + t.Fatalf("Failed to delete message %s: %v", message.ID, err) + } + + t.Logf("Deleted message: %s", message.ID) +} diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go new file mode 100644 index 0000000000..faf8fc66ec --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go @@ -0,0 +1,105 @@ +package messages + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" +) + +const requestID = "req-6f52cd8b-25a1-42cf-b497-7babf70f55f4" +const minimumManilaMessagesMicroVersion = "2.37" + +func TestMessageList(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = minimumManilaMessagesMicroVersion + + allPages, err := messages.List(client, messages.ListOpts{}).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve messages: %v", err) + } + + allMessages, err := messages.ExtractMessages(allPages) + if err != nil { + t.Fatalf("Unable to extract messages: %v", err) + } + + for _, message := range allMessages { + tools.PrintResource(t, message) + } +} + +// The test creates 2 messages and verifies that only the one(s) with +// a particular name are being listed +func TestMessageListFiltering(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = minimumManilaMessagesMicroVersion + + options := messages.ListOpts{ + RequestID: requestID, + } + + allPages, err := messages.List(client, options).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve messages: %v", err) + } + + allMessages, err := messages.ExtractMessages(allPages) + if err != nil { + t.Fatalf("Unable to extract messages: %v", err) + } + + for _, listedMessage := range allMessages { + if listedMessage.RequestID != options.RequestID { + t.Fatalf("The request id of the message was expected to be %s", options.RequestID) + } + tools.PrintResource(t, listedMessage) + } +} + +// 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) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create shared file system client: %v", err) + } + client.Microversion = minimumManilaMessagesMicroVersion + + options := messages.ListOpts{ + RequestID: requestID, + } + + allPages, err := messages.List(client, options).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve messages: %v", err) + } + + allMessages, err := messages.ExtractMessages(allPages) + if err != nil { + t.Fatalf("Unable to extract messages: %v", err) + } + + var messageID string + for _, listedMessage := range allMessages { + if listedMessage.RequestID != options.RequestID { + t.Fatalf("The request id of the message was expected to be %s", options.RequestID) + } + tools.PrintResource(t, listedMessage) + messageID = listedMessage.ID + } + + message, err := messages.Get(client, messageID).Extract() + if err != nil { + t.Fatalf("Unable to retrieve the message: %v", err) + } + + DeleteMessage(t, client, message) +} diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go b/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go new file mode 100644 index 0000000000..0046213058 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go @@ -0,0 +1,3 @@ +// The v2 package contains acceptance tests for the Openstack Manila V2 messages package + +package messages diff --git a/openstack/sharedfilesystems/v2/messages/requests.go b/openstack/sharedfilesystems/v2/messages/requests.go new file mode 100644 index 0000000000..2fad8e6da1 --- /dev/null +++ b/openstack/sharedfilesystems/v2/messages/requests.go @@ -0,0 +1,66 @@ +package messages + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Delete will delete the existing Message with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToMessageListQuery() (string, error) +} + +// ListOpts holds options for listing Messages. It is passed to the +// messages.List function. +type ListOpts struct { + // The message ID + ID string `q:"id"` + // The ID of the action during which the message was created + ActionID string `q:"action_id"` + // The ID of the message detail + DetailID string `q:"detail_id"` + // The message level + MessageLevel string `q:"message_level"` + // The UUID of the request during which the message was created + RequestID string `q:"request_id"` + // The UUID of the resource for which the message was created + ResourceID string `q:"resource_id"` + // The type of the resource for which the message was created + ResourceType string `q:"resource_type"` +} + +// ToMessageListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToMessageListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Messages 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.ToMessageListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return MessagePage{pagination.SinglePageBase(r)} + }) +} + +// Get retrieves the Message with the provided ID. To extract the Message +// object from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/sharedfilesystems/v2/messages/results.go b/openstack/sharedfilesystems/v2/messages/results.go new file mode 100644 index 0000000000..9c48ea07b8 --- /dev/null +++ b/openstack/sharedfilesystems/v2/messages/results.go @@ -0,0 +1,99 @@ +package messages + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Message contains all the information associated with an OpenStack +// Message. +type Message struct { + // The message ID + ID string `json:"id"` + // The UUID of the project where the message was created + ProjectID string `json:"project_id"` + // The ID of the action during which the message was created + ActionID string `json:"action_id"` + // The ID of the message detail + DetailID string `json:"detail_id"` + // The message level + MessageLevel string `json:"message_level"` + // The UUID of the request during which the message was created + RequestID string `json:"request_id"` + // The UUID of the resource for which the message was created + ResourceID string `json:"resource_id"` + // The type of the resource for which the message was created + ResourceType string `json:"resource_type"` + // The message text + UserMessage string `json:"user_message"` + // The date and time stamp when the message was created + CreatedAt time.Time `json:"-"` + // The date and time stamp when the message will expire + ExpiresAt time.Time `json:"-"` +} + +func (r *Message) UnmarshalJSON(b []byte) error { + type tmp Message + 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 = Message(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.ExpiresAt = time.Time(s.ExpiresAt) + + return nil +} + +type commonResult struct { + gophercloud.Result +} + +// MessagePage is a pagination.pager that is returned from a call to the List function. +type MessagePage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a ListResult contains no Messages. +func (r MessagePage) IsEmpty() (bool, error) { + messages, err := ExtractMessages(r) + return len(messages) == 0, err +} + +// ExtractMessages extracts and returns Messages. It is used while +// iterating over a messages.List call. +func ExtractMessages(r pagination.Page) ([]Message, error) { + var s struct { + Messages []Message `json:"messages"` + } + err := (r.(MessagePage)).ExtractInto(&s) + return s.Messages, err +} + +// Extract will get the Message object out of the commonResult object. +func (r commonResult) Extract() (*Message, error) { + var s struct { + Message *Message `json:"message"` + } + err := r.ExtractInto(&s) + return s.Message, 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 +} diff --git a/openstack/sharedfilesystems/v2/messages/testing/fixtures.go b/openstack/sharedfilesystems/v2/messages/testing/fixtures.go new file mode 100644 index 0000000000..da1c98adb9 --- /dev/null +++ b/openstack/sharedfilesystems/v2/messages/testing/fixtures.go @@ -0,0 +1,115 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/messages/messageID", 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 MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/messages", 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, ` + { + "messages": [ + { + "resource_id": "0d0b883f-95ef-406c-b930-55612ee48a6d", + "message_level": "ERROR", + "user_message": "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", + "expires_at": "2019-01-06T08:53:38.000000", + "id": "143a6cc2-1998-44d0-8356-22070b0ebdaa", + "created_at": "2018-12-07T08:53:38.000000", + "detail_id": "004", + "request_id": "req-21767eee-22ca-40a4-b6c0-ae7d35cd434f", + "project_id": "a5e9d48232dc4aa59a716b5ced963584", + "resource_type": "SHARE", + "action_id": "002" + }, + { + "resource_id": "4336d74f-3bdc-4f27-9657-c01ec63680bf", + "message_level": "ERROR", + "user_message": "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", + "expires_at": "2019-01-06T08:53:34.000000", + "id": "2076373e-13a7-4b84-9e67-15ce8cceaff8", + "created_at": "2018-12-07T08:53:34.000000", + "detail_id": "004", + "request_id": "req-957792ed-f38b-42db-a86a-850f815cbbe9", + "project_id": "a5e9d48232dc4aa59a716b5ced963584", + "resource_type": "SHARE", + "action_id": "002" + } + ] + }`) + }) +} + +func MockFilteredListResponse(t *testing.T) { + th.Mux.HandleFunc("/messages", 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, ` + { + "messages": [ + { + "resource_id": "4336d74f-3bdc-4f27-9657-c01ec63680bf", + "message_level": "ERROR", + "user_message": "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", + "expires_at": "2019-01-06T08:53:34.000000", + "id": "2076373e-13a7-4b84-9e67-15ce8cceaff8", + "created_at": "2018-12-07T08:53:34.000000", + "detail_id": "004", + "request_id": "req-957792ed-f38b-42db-a86a-850f815cbbe9", + "project_id": "a5e9d48232dc4aa59a716b5ced963584", + "resource_type": "SHARE", + "action_id": "002" + } + ] + }`) + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/messages/2076373e-13a7-4b84-9e67-15ce8cceaff8", 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, ` + { + "message": { + "resource_id": "4336d74f-3bdc-4f27-9657-c01ec63680bf", + "message_level": "ERROR", + "user_message": "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", + "expires_at": "2019-01-06T08:53:34.000000", + "id": "2076373e-13a7-4b84-9e67-15ce8cceaff8", + "created_at": "2018-12-07T08:53:34.000000", + "detail_id": "004", + "request_id": "req-957792ed-f38b-42db-a86a-850f815cbbe9", + "project_id": "a5e9d48232dc4aa59a716b5ced963584", + "resource_type": "SHARE", + "action_id": "002" + } + }`) + }) +} diff --git a/openstack/sharedfilesystems/v2/messages/testing/requests_test.go b/openstack/sharedfilesystems/v2/messages/testing/requests_test.go new file mode 100644 index 0000000000..c92297c6c5 --- /dev/null +++ b/openstack/sharedfilesystems/v2/messages/testing/requests_test.go @@ -0,0 +1,125 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// Verifies that message deletion works +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := messages.Delete(client.ServiceClient(), "messageID") + th.AssertNoErr(t, res.Err) +} + +// Verifies that messages can be listed correctly +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allPages, err := messages.List(client.ServiceClient(), &messages.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := messages.ExtractMessages(allPages) + th.AssertNoErr(t, err) + expected := []messages.Message{ + { + ResourceID: "0d0b883f-95ef-406c-b930-55612ee48a6d", + MessageLevel: "ERROR", + UserMessage: "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", + ExpiresAt: time.Date(2019, 1, 6, 8, 53, 38, 0, time.UTC), + ID: "143a6cc2-1998-44d0-8356-22070b0ebdaa", + CreatedAt: time.Date(2018, 12, 7, 8, 53, 38, 0, time.UTC), + DetailID: "004", + RequestID: "req-21767eee-22ca-40a4-b6c0-ae7d35cd434f", + ProjectID: "a5e9d48232dc4aa59a716b5ced963584", + ResourceType: "SHARE", + ActionID: "002", + }, + { + ResourceID: "4336d74f-3bdc-4f27-9657-c01ec63680bf", + MessageLevel: "ERROR", + UserMessage: "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", + ExpiresAt: time.Date(2019, 1, 6, 8, 53, 34, 0, time.UTC), + ID: "2076373e-13a7-4b84-9e67-15ce8cceaff8", + CreatedAt: time.Date(2018, 12, 7, 8, 53, 34, 0, time.UTC), + DetailID: "004", + RequestID: "req-957792ed-f38b-42db-a86a-850f815cbbe9", + ProjectID: "a5e9d48232dc4aa59a716b5ced963584", + ResourceType: "SHARE", + ActionID: "002", + }, + } + + th.CheckDeepEquals(t, expected, actual) +} + +// Verifies that messages list can be called with query parameters +func TestFilteredList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockFilteredListResponse(t) + + options := &messages.ListOpts{ + RequestID: "req-21767eee-22ca-40a4-b6c0-ae7d35cd434f", + } + + allPages, err := messages.List(client.ServiceClient(), options).AllPages() + th.AssertNoErr(t, err) + actual, err := messages.ExtractMessages(allPages) + th.AssertNoErr(t, err) + expected := []messages.Message{ + { + ResourceID: "4336d74f-3bdc-4f27-9657-c01ec63680bf", + MessageLevel: "ERROR", + UserMessage: "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", + ExpiresAt: time.Date(2019, 1, 6, 8, 53, 34, 0, time.UTC), + ID: "2076373e-13a7-4b84-9e67-15ce8cceaff8", + CreatedAt: time.Date(2018, 12, 7, 8, 53, 34, 0, time.UTC), + DetailID: "004", + RequestID: "req-957792ed-f38b-42db-a86a-850f815cbbe9", + ProjectID: "a5e9d48232dc4aa59a716b5ced963584", + ResourceType: "SHARE", + ActionID: "002", + }, + } + + th.CheckDeepEquals(t, expected, actual) +} + +// Verifies that it is possible to get a message +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + expected := messages.Message{ + ResourceID: "4336d74f-3bdc-4f27-9657-c01ec63680bf", + MessageLevel: "ERROR", + UserMessage: "create: Could not find an existing share server or allocate one on the share network provided. You may use a different share network, or verify the network details in the share network and retry your request. If this doesn't work, contact your administrator to troubleshoot issues with your network.", + ExpiresAt: time.Date(2019, 1, 6, 8, 53, 34, 0, time.UTC), + ID: "2076373e-13a7-4b84-9e67-15ce8cceaff8", + CreatedAt: time.Date(2018, 12, 7, 8, 53, 34, 0, time.UTC), + DetailID: "004", + RequestID: "req-957792ed-f38b-42db-a86a-850f815cbbe9", + ProjectID: "a5e9d48232dc4aa59a716b5ced963584", + ResourceType: "SHARE", + ActionID: "002", + } + + n, err := messages.Get(client.ServiceClient(), "2076373e-13a7-4b84-9e67-15ce8cceaff8").Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, &expected, n) +} diff --git a/openstack/sharedfilesystems/v2/messages/urls.go b/openstack/sharedfilesystems/v2/messages/urls.go new file mode 100644 index 0000000000..7c2c54a87f --- /dev/null +++ b/openstack/sharedfilesystems/v2/messages/urls.go @@ -0,0 +1,15 @@ +package messages + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("messages") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("messages", id) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return getURL(c, id) +} From cebde53d290ee1e67a943f22f80a6e9770a8b47e Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 3 Jan 2019 06:12:13 +0100 Subject: [PATCH 0621/2296] Manila share errors (#1360) * Manila: enable acceptance tests with verbose error messaging * Manila tests: set "dhss_false" share type * Manila tests: increase extend timeout * Manila: disable shrinking tests due to LVM limitation --- .../openstack/sharedfilesystems/v2/shares.go | 69 +++++++++++++------ .../sharedfilesystems/v2/shares_test.go | 6 +- script/acceptancetest | 4 +- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index 12f876d274..dd52917b93 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -3,9 +3,13 @@ package v2 import ( "encoding/json" "fmt" + "strings" "testing" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" ) @@ -16,20 +20,14 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share t.Skip("Skipping test that requres share creation in short mode.") } - shareNetwork, err := CreateShareNetwork(t, client) - if err != nil { - return nil, err - } - t.Logf("Share network id %s", shareNetwork.ID) - iTrue := true createOpts := shares.CreateOpts{ - Size: 1, - Name: "My Test Share", - Description: "My Test Description", - ShareProto: "NFS", - ShareNetworkID: shareNetwork.ID, - IsPublic: &iTrue, + Size: 1, + Name: "My Test Share", + Description: "My Test Description", + ShareProto: "NFS", + ShareType: "dhss_false", + IsPublic: &iTrue, } share, err := shares.Create(client, createOpts).Extract() @@ -39,7 +37,7 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share return share, err } - err = waitForStatus(client, share.ID, "available", 600) + err = waitForStatus(t, client, share.ID, "available", 600) if err != nil { t.Logf("Failed to get %s share status", share.ID) DeleteShare(t, client, share) @@ -92,14 +90,12 @@ func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares. t.Errorf("Unable to delete share %s: %v", share.ID, err) } - err = waitForStatus(client, share.ID, "deleted", 600) + err = waitForStatus(t, client, share.ID, "deleted", 600) if err != nil { t.Errorf("Failed to wait for 'deleted' status for %s share: %v", share.ID, err) } else { t.Logf("Deleted share: %s", share.ID) } - - DeleteShareNetwork(t, client, share.ShareNetworkID) } // PrintShare prints some information of the share @@ -132,8 +128,28 @@ func ShrinkShare(t *testing.T, client *gophercloud.ServiceClient, share *shares. return shares.Shrink(client, share.ID, &shares.ShrinkOpts{NewSize: newSize}).ExtractErr() } -func waitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func printMessages(t *testing.T, c *gophercloud.ServiceClient, id string) error { + c.Microversion = "2.37" + + allPages, err := messages.List(c, messages.ListOpts{ResourceID: id}).AllPages() + if err != nil { + return fmt.Errorf("Unable to retrieve messages: %v", err) + } + + allMessages, err := messages.ExtractMessages(allPages) + if err != nil { + return fmt.Errorf("Unable to extract messages: %v", err) + } + + for _, message := range allMessages { + tools.PrintResource(t, message) + } + + return nil +} + +func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string, secs int) error { + err := gophercloud.WaitFor(secs, func() (bool, error) { current, err := shares.Get(c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { @@ -147,14 +163,23 @@ func waitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) er return false, err } - if current.Status == "error" { - return true, fmt.Errorf("An error occurred") - } - 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 fmt.Errorf("Share status is '%s' and unable to get manila messages: %s", err, mErr) + } + } + + return err } diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index b511d47dce..e0598f68a4 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -181,23 +181,25 @@ func TestExtendAndShrink(t *testing.T) { } // We need to wait till the Extend operation is done - err = waitForStatus(client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available", 120) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s successfuly extended", share.ID) + /* disable shrinking for the LVM dhss=false err = ShrinkShare(t, client, share, 1) if err != nil { t.Fatalf("Unable to shrink a share: %v", err) } // We need to wait till the Shrink operation is done - err = waitForStatus(client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available", 300) if err != nil { t.Fatalf("Share status error: %v", err) } t.Logf("Share %s successfuly shrunk", share.ID) + */ } diff --git a/script/acceptancetest b/script/acceptancetest index 9fc4d8eeeb..472f19e3a9 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -71,9 +71,7 @@ acceptance/openstack/networking/v2/extensions/trunks acceptance/openstack/objectstorage/v1 acceptance/openstack/orchestration/v1 - -# Temporarily disabled, see https://github.com/gophercloud/gophercloud/issues/1345 -# acceptance/openstack/sharedfilesystems/v2 +acceptance/openstack/sharedfilesystems/v2 # No suitable endpoint could be found in the service catalog # acceptance/openstack/workflow/v2 From a1de06b8040b6da28b21c6f32b446b836d475b04 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 4 Jan 2019 04:12:19 +0100 Subject: [PATCH 0622/2296] Manila: add response error messages support (#1378) --- .../sharedfilesystems/v2/errors/errors.go | 44 +++++++++++++++++++ .../v2/errors/testing/fixtures.go | 42 ++++++++++++++++++ .../v2/errors/testing/request_test.go | 34 ++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 openstack/sharedfilesystems/v2/errors/errors.go create mode 100644 openstack/sharedfilesystems/v2/errors/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/errors/testing/request_test.go diff --git a/openstack/sharedfilesystems/v2/errors/errors.go b/openstack/sharedfilesystems/v2/errors/errors.go new file mode 100644 index 0000000000..cd88a8ac1f --- /dev/null +++ b/openstack/sharedfilesystems/v2/errors/errors.go @@ -0,0 +1,44 @@ +package errors + +import ( + "encoding/json" + "fmt" + + "github.com/gophercloud/gophercloud" +) + +type ManilaError struct { + Code int `json:"code"` + Message string `json:"message"` + Details string `json:"details"` +} + +type ErrorDetails map[string]ManilaError + +// error types from provider_client.go +func ExtractErrorInto(rawError error, errorDetails *ErrorDetails) (err error) { + switch e := rawError.(type) { + case gophercloud.ErrDefault400: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + case gophercloud.ErrDefault401: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + case gophercloud.ErrDefault403: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + case gophercloud.ErrDefault404: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + case gophercloud.ErrDefault405: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + case gophercloud.ErrDefault408: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + case gophercloud.ErrDefault429: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + case gophercloud.ErrDefault500: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + case gophercloud.ErrDefault503: + err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) + default: + err = fmt.Errorf("Unable to extract detailed error message") + } + + return err +} diff --git a/openstack/sharedfilesystems/v2/errors/testing/fixtures.go b/openstack/sharedfilesystems/v2/errors/testing/fixtures.go new file mode 100644 index 0000000000..dc7a394fb4 --- /dev/null +++ b/openstack/sharedfilesystems/v2/errors/testing/fixtures.go @@ -0,0 +1,42 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const shareEndpoint = "/shares" + +var createRequest = `{ + "share": { + "name": "my_test_share", + "size": 1, + "share_proto": "NFS", + "snapshot_id": "70bfbebc-d3ff-4528-8bbb-58422daa280b" + } + }` + +var createResponse = `{ + "itemNotFound": { + "code": 404, + "message": "ShareSnapshotNotFound: Snapshot 70bfbebc-d3ff-4528-8bbb-58422daa280b could not be found." + } +}` + +// 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.TestJSONRequest(t, r, createRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + fmt.Fprintf(w, createResponse) + }) +} diff --git a/openstack/sharedfilesystems/v2/errors/testing/request_test.go b/openstack/sharedfilesystems/v2/errors/testing/request_test.go new file mode 100644 index 0000000000..4d590d5e7d --- /dev/null +++ b/openstack/sharedfilesystems/v2/errors/testing/request_test.go @@ -0,0 +1,34 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/errors" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := &shares.CreateOpts{Size: 1, Name: "my_test_share", ShareProto: "NFS", SnapshotID: "70bfbebc-d3ff-4528-8bbb-58422daa280b"} + _, err := shares.Create(client.ServiceClient(), options).Extract() + + if err == nil { + t.Fatal("Expected error") + } + + detailedErr := errors.ErrorDetails{} + e := errors.ExtractErrorInto(err, &detailedErr) + th.AssertNoErr(t, e) + + for k, msg := range detailedErr { + th.AssertEquals(t, k, "itemNotFound") + th.AssertEquals(t, msg.Code, 404) + th.AssertEquals(t, msg.Message, "ShareSnapshotNotFound: Snapshot 70bfbebc-d3ff-4528-8bbb-58422daa280b could not be found.") + } +} From f27ceddc323ff01fdd909ac8377fb06b12db7f4f Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 6 Jan 2019 03:17:28 +0300 Subject: [PATCH 0623/2296] NetworkingV2: add Address scopes Create method (#1381) * NetworkingV2: implement address-scopes List call Add "address-scopes" package with List method. * Address scopes: add unit tests for List request Add basic unit test for the List request. * Address scopres: fix resource URL Use "address-scopes" instead of "addressscopes". * NetworkingV2: implement address-scopes Get call Implement address-scopes Get method with tests and documentation. * Address scopes: fix URL for Get request test Fix test handler URL. * NetworkingV2: add Address scopes Create method Implement Create method for the "address-scopes" extension with tests and documentation example. * Address scopes: fix documentation Fix documentation format and package name in the "openstack/networking/v2/extensions/layer3/addressscopes/doc.go". * Address scopes: rename IPversion to IPVersion Use "IPVersion" as the field name of the Address scope. --- .../v2/extensions/layer3/addressscopes/doc.go | 43 +++++++ .../layer3/addressscopes/requests.go | 107 +++++++++++++++++ .../layer3/addressscopes/results.go | 87 ++++++++++++++ .../layer3/addressscopes/testing/doc.go | 2 + .../layer3/addressscopes/testing/fixtures.go | 88 ++++++++++++++ .../addressscopes/testing/requests_test.go | 109 ++++++++++++++++++ .../extensions/layer3/addressscopes/urls.go | 25 ++++ 7 files changed, 461 insertions(+) create mode 100644 openstack/networking/v2/extensions/layer3/addressscopes/doc.go create mode 100644 openstack/networking/v2/extensions/layer3/addressscopes/requests.go create mode 100644 openstack/networking/v2/extensions/layer3/addressscopes/results.go create mode 100644 openstack/networking/v2/extensions/layer3/addressscopes/testing/doc.go create mode 100644 openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/layer3/addressscopes/urls.go diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go new file mode 100644 index 0000000000..7711da15f3 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go @@ -0,0 +1,43 @@ +/* +Package addressscopes provides the ability to retrieve and manage Address scopes through the Neutron API. + +Example of Listing Address scopes + + listOpts := addressscopes.ListOpts{ + IPVersion: 6, + } + + allPages, err := addressscopes.List(networkClient, listOpts).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) + } + +Example to Get an Address scope + + 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) + } +*/ +package addressscopes diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go new file mode 100644 index 0000000000..5f7b20bf29 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go @@ -0,0 +1,107 @@ +package addressscopes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToAddressScopeListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the Neutron API. Filtering is achieved by passing in struct field values +// that map to the address-scope attributes you want to see returned. +// SortKey allows you to sort by a particular address-scope attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for the pagination. +type ListOpts struct { + ID string `q:"id"` + Name string `q:"name"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + IPVersion int `q:"ip_version"` + Shared *bool `q:"shared"` + Description string `q:"description"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToAddressScopeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToAddressScopeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// address-scopes. It accepts a ListOpts struct, which allows you to filter and +// sort the returned collection for greater efficiency. +// +// Default policy settings return only the address-scopes owned by the project +// of the user submitting the request, unless the user has the administrative +// role. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToAddressScopeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return AddressScopePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves a specific address-scope based on its ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + return +} + +// CreateOptsBuilder allows to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToAddressScopeCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters of a new address-scope. +type CreateOpts struct { + // Name is the human-readable name of the address-scope. + Name string `json:"name"` + + // TenantID is the id of the Identity project. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the id of the Identity project. + ProjectID string `json:"project_id,omitempty"` + + // IPVersion is the IP protocol version. + IPVersion int `json:"ip_version"` + + // Shared indicates whether this address-scope is shared across all projects. + Shared bool `json:"shared,omitempty"` +} + +// ToAddressScopeCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToAddressScopeCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "address_scope") +} + +// Create requests the creation of a new address-scope on the server. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToAddressScopeCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/results.go b/openstack/networking/v2/extensions/layer3/addressscopes/results.go new file mode 100644 index 0000000000..9614735301 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/addressscopes/results.go @@ -0,0 +1,87 @@ +package addressscopes + +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 an address-scope resource. +func (r commonResult) Extract() (*AddressScope, error) { + var s struct { + AddressScope *AddressScope `json:"address_scope"` + } + err := r.ExtractInto(&s) + return s.AddressScope, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a SubnetPool. +type GetResult struct { + commonResult +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a SubnetPool. +type CreateResult struct { + commonResult +} + +// AddressScope represents a Neutron address-scope. +type AddressScope struct { + // ID is the id of the address-scope. + ID string `json:"id"` + + // Name is the human-readable name of the address-scope. + Name string `json:"name"` + + // TenantID is the id of the Identity project. + TenantID string `json:"tenant_id"` + + // ProjectID is the id of the Identity project. + ProjectID string `json:"project_id"` + + // IPVersion is the IP protocol version. + IPVersion int `json:"ip_version"` + + // Shared indicates whether this address-scope is shared across all projects. + Shared bool `json:"shared"` +} + +// AddressScopePage stores a single page of AddressScopes from a List() API call. +type AddressScopePage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of address-scope 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 AddressScopePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"address_scopes_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty determines whether or not a AddressScopePage is empty. +func (r AddressScopePage) IsEmpty() (bool, error) { + addressScopes, err := ExtractAddressScopes(r) + return len(addressScopes) == 0, err +} + +// ExtractAddressScopes interprets the results of a single page from a List() +// API call, producing a slice of AddressScopes structs. +func ExtractAddressScopes(r pagination.Page) ([]AddressScope, error) { + var s struct { + AddressScopes []AddressScope `json:"address_scopes"` + } + err := (r.(AddressScopePage)).ExtractInto(&s) + return s.AddressScopes, err +} diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/doc.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/doc.go new file mode 100644 index 0000000000..7787611308 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/doc.go @@ -0,0 +1,2 @@ +// subnetpools unit tests +package testing diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go new file mode 100644 index 0000000000..ed02143689 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go @@ -0,0 +1,88 @@ +package testing + +import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" + +// AddressScopesListResult represents raw response for the List request. +const AddressScopesListResult = ` +{ + "address_scopes": [ + { + "name": "scopev4", + "tenant_id": "4a9807b773404e979b19633f38370643", + "ip_version": 4, + "shared": false, + "project_id": "4a9807b773404e979b19633f38370643", + "id": "9cc35860-522a-4d35-974d-51d4b011801e" + }, + { + "name": "scopev6", + "tenant_id": "4a9807b773404e979b19633f38370643", + "ip_version": 6, + "shared": true, + "project_id": "4a9807b773404e979b19633f38370643", + "id": "be992b82-bf42-4ab7-bf7b-6baa8759d388" + } + ] +} +` + +// AddressScope1 represents first unmarshalled address scope from the +// AddressScopesListResult. +var AddressScope1 = addressscopes.AddressScope{ + ID: "9cc35860-522a-4d35-974d-51d4b011801e", + Name: "scopev4", + TenantID: "4a9807b773404e979b19633f38370643", + ProjectID: "4a9807b773404e979b19633f38370643", + IPVersion: 4, + Shared: false, +} + +// AddressScope2 represents second unmarshalled address scope from the +// AddressScopesListResult. +var AddressScope2 = addressscopes.AddressScope{ + ID: "be992b82-bf42-4ab7-bf7b-6baa8759d388", + Name: "scopev6", + TenantID: "4a9807b773404e979b19633f38370643", + ProjectID: "4a9807b773404e979b19633f38370643", + IPVersion: 6, + Shared: true, +} + +// AddressScopesGetResult represents raw response for the Get request. +const AddressScopesGetResult = ` +{ + "address_scope": { + "name": "scopev4", + "tenant_id": "4a9807b773404e979b19633f38370643", + "ip_version": 4, + "shared": false, + "project_id": "4a9807b773404e979b19633f38370643", + "id": "9cc35860-522a-4d35-974d-51d4b011801e" + } +} +` + +// AddressScopeCreateRequest represents raw Create request. +const AddressScopeCreateRequest = ` +{ + "address_scope": { + "ip_version": 4, + "shared": true, + "name": "test0" + } +} +` + +// AddressScopeCreateResult represents raw Create response. +const AddressScopeCreateResult = ` +{ + "address_scope": { + "name": "test0", + "tenant_id": "4a9807b773404e979b19633f38370643", + "ip_version": 4, + "shared": true, + "project_id": "4a9807b773404e979b19633f38370643", + "id": "9cc35860-522a-4d35-974d-51d4b011801e" + } +} +` diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go new file mode 100644 index 0000000000..7fc2aa3104 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go @@ -0,0 +1,109 @@ +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/addressscopes" + "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/address-scopes", 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, AddressScopesListResult) + }) + + count := 0 + + addressscopes.List(fake.ServiceClient(), addressscopes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := addressscopes.ExtractAddressScopes(page) + if err != nil { + t.Errorf("Failed to extract addressscopes: %v", err) + return false, nil + } + + expected := []addressscopes.AddressScope{ + AddressScope1, + AddressScope2, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/address-scopes/9cc35860-522a-4d35-974d-51d4b011801e", 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, AddressScopesGetResult) + }) + + s, err := addressscopes.Get(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.ID, "9cc35860-522a-4d35-974d-51d4b011801e") + th.AssertEquals(t, s.Name, "scopev4") + th.AssertEquals(t, s.TenantID, "4a9807b773404e979b19633f38370643") + th.AssertEquals(t, s.ProjectID, "4a9807b773404e979b19633f38370643") + th.AssertEquals(t, s.IPVersion, 4) + th.AssertEquals(t, s.Shared, false) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/address-scopes", 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, AddressScopeCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, AddressScopeCreateResult) + }) + + opts := addressscopes.CreateOpts{ + IPVersion: 4, + Shared: true, + Name: "test0", + } + s, err := addressscopes.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "test0") + th.AssertEquals(t, s.Shared, true) + th.AssertEquals(t, s.IPVersion, 4) + th.AssertEquals(t, s.TenantID, "4a9807b773404e979b19633f38370643") + th.AssertEquals(t, s.ProjectID, "4a9807b773404e979b19633f38370643") + th.AssertEquals(t, s.ID, "9cc35860-522a-4d35-974d-51d4b011801e") +} diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/urls.go b/openstack/networking/v2/extensions/layer3/addressscopes/urls.go new file mode 100644 index 0000000000..e64cf16b82 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/addressscopes/urls.go @@ -0,0 +1,25 @@ +package addressscopes + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "address-scopes" + +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From fe38d37a1a6485939d216458f05f778b8083d70d Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 10 Jan 2019 05:20:06 +0300 Subject: [PATCH 0624/2296] NetworkingV2: implement Address scopes acc tests (#1387) * NetworkingV2: add Address scopes Update method Implement address-scopes Update method. Provide unit test and doc example. * NetworkingV2: add Address scopes Delete method Implement address-scopes Delete method. Provide unit test and doc example. * NetworkingV2: implement Address scopes acc tests Add address-scopes acceptance tests helpers and implement the TestAddressScopesCRUD test. * Networking V2: fix Address scopes UpdateOpts Use pointer to string for the "Name" field. --- .../extensions/layer3/addressscopes_test.go | 53 +++++++++++++++++++ .../networking/v2/extensions/layer3/layer3.go | 39 ++++++++++++++ .../v2/extensions/layer3/addressscopes/doc.go | 21 ++++++++ .../layer3/addressscopes/requests.go | 40 ++++++++++++++ .../layer3/addressscopes/results.go | 12 +++++ .../layer3/addressscopes/testing/fixtures.go | 24 +++++++++ .../addressscopes/testing/requests_test.go | 44 +++++++++++++++ .../extensions/layer3/addressscopes/urls.go | 8 +++ 8 files changed, 241 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go diff --git a/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go b/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go new file mode 100644 index 0000000000..4d7cff3538 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go @@ -0,0 +1,53 @@ +// +build acceptance networking layer3 addressscopes + +package layer3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestAddressScopesCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create an address-scope + addressScope, err := CreateAddressScope(t, client) + th.AssertNoErr(t, err) + defer DeleteAddressScope(t, client, addressScope.ID) + + tools.PrintResource(t, addressScope) + + newName := tools.RandomString("TESTACC-", 8) + updateOpts := &addressscopes.UpdateOpts{ + Name: &newName, + } + + _, err = addressscopes.Update(client, addressScope.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + newAddressScope, err := addressscopes.Get(client, addressScope.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newAddressScope) + th.AssertEquals(t, newAddressScope.Name, newName) + + allPages, err := addressscopes.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allAddressScopes, err := addressscopes.ExtractAddressScopes(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, addressScope := range allAddressScopes { + if addressScope.ID == newAddressScope.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 74dd00c33c..075d3b3375 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -3,6 +3,8 @@ package layer3 import ( "testing" + "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" @@ -258,3 +260,40 @@ func WaitForRouterInterfaceToDetach(client *gophercloud.ServiceClient, routerInt return false, nil }) } + +// CreateAddressScope will create an address-scope. An error will be returned if +// the address-scope could not be created. +func CreateAddressScope(t *testing.T, client *gophercloud.ServiceClient) (*addressscopes.AddressScope, error) { + addressScopeName := tools.RandomString("TESTACC-", 8) + createOpts := addressscopes.CreateOpts{ + Name: addressScopeName, + IPVersion: 4, + } + + t.Logf("Attempting to create an address-scope: %s", addressScopeName) + + addressScope, err := addressscopes.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created the addressscopes.") + + th.AssertEquals(t, addressScope.Name, addressScopeName) + th.AssertEquals(t, addressScope.IPVersion, int(gophercloud.IPv4)) + + return addressScope, nil +} + +// DeleteAddressScope will delete an address-scope with the specified ID. +// A fatal error will occur if the delete was not successful. +func DeleteAddressScope(t *testing.T, client *gophercloud.ServiceClient, addressScopeID string) { + t.Logf("Attempting to delete the address-scope: %s", addressScopeID) + + err := addressscopes.Delete(client, addressScopeID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete address-scope %s: %v", addressScopeID, err) + } + + t.Logf("Deleted address-scope: %s", addressScopeID) +} diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go index 7711da15f3..d68d2b764a 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go @@ -39,5 +39,26 @@ Example to Create a new Address scope 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, + } + + 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) + } */ package addressscopes diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go index 5f7b20bf29..defa8e1b5a 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go @@ -105,3 +105,43 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToAddressScopeUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update an address-scope. +type UpdateOpts struct { + // Name is the human-readable name of the address-scope. + Name *string `json:"name,omitempty"` + + // Shared indicates whether this address-scope is shared across all projects. + Shared *bool `json:"shared,omitempty"` +} + +// ToAddressScopeUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToAddressScopeUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "address_scope") +} + +// Update accepts a UpdateOpts struct and updates an existing address-scope +// using the values provided. +func Update(c *gophercloud.ServiceClient, addressScopeID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToAddressScopeUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(updateURL(c, addressScopeID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Delete accepts a unique ID and deletes the address-scope associated with it. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(deleteURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/results.go b/openstack/networking/v2/extensions/layer3/addressscopes/results.go index 9614735301..5f78dfeef2 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/results.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/results.go @@ -30,6 +30,18 @@ type CreateResult struct { commonResult } +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as an AddressScope. +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 +} + // AddressScope represents a Neutron address-scope. type AddressScope struct { // ID is the id of the address-scope. diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go index ed02143689..42b896d3c3 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures.go @@ -86,3 +86,27 @@ const AddressScopeCreateResult = ` } } ` + +// AddressScopeUpdateRequest represents raw Update request. +const AddressScopeUpdateRequest = ` +{ + "address_scope": { + "name": "test1", + "shared": true + } +} +` + +// AddressScopeUpdateResult represents raw Update response. +const AddressScopeUpdateResult = ` +{ + "address_scope": { + "name": "test1", + "tenant_id": "4a9807b773404e979b19633f38370643", + "ip_version": 4, + "shared": true, + "project_id": "4a9807b773404e979b19633f38370643", + "id": "9cc35860-522a-4d35-974d-51d4b011801e" + } +} +` diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go index 7fc2aa3104..caad3a2f3f 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go @@ -107,3 +107,47 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, s.ProjectID, "4a9807b773404e979b19633f38370643") th.AssertEquals(t, s.ID, "9cc35860-522a-4d35-974d-51d4b011801e") } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/address-scopes/9cc35860-522a-4d35-974d-51d4b011801e", 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, AddressScopeUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, AddressScopeUpdateResult) + }) + + shared := true + newName := "test1" + updateOpts := addressscopes.UpdateOpts{ + Name: &newName, + Shared: &shared, + } + s, err := addressscopes.Update(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e", updateOpts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "test1") + th.AssertEquals(t, s.Shared, true) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/address-scopes/9cc35860-522a-4d35-974d-51d4b011801e", 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) + }) + + res := addressscopes.Delete(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/urls.go b/openstack/networking/v2/extensions/layer3/addressscopes/urls.go index e64cf16b82..9fe7e01a03 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/urls.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/urls.go @@ -23,3 +23,11 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From d0d0044f7584f29519058fe862de1ddcc18ccebc Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 10 Jan 2019 03:36:29 +0100 Subject: [PATCH 0625/2296] Manila: add share snapshots support (list, get) (#1382) --- .../v2/snapshots/requests.go | 73 +++++++ .../sharedfilesystems/v2/snapshots/results.go | 178 ++++++++++++++++++ .../v2/snapshots/testing/fixtures.go | 102 ++++++++++ .../v2/snapshots/testing/request_test.go | 81 ++++++++ .../sharedfilesystems/v2/snapshots/urls.go | 11 ++ 5 files changed, 445 insertions(+) create mode 100644 openstack/sharedfilesystems/v2/snapshots/requests.go create mode 100644 openstack/sharedfilesystems/v2/snapshots/results.go create mode 100644 openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go create mode 100644 openstack/sharedfilesystems/v2/snapshots/testing/request_test.go create mode 100644 openstack/sharedfilesystems/v2/snapshots/urls.go diff --git a/openstack/sharedfilesystems/v2/snapshots/requests.go b/openstack/sharedfilesystems/v2/snapshots/requests.go new file mode 100644 index 0000000000..255479ab96 --- /dev/null +++ b/openstack/sharedfilesystems/v2/snapshots/requests.go @@ -0,0 +1,73 @@ +package snapshots + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOpts holds options for listing Snapshots. It is passed to the +// snapshots.List function. +type ListOpts struct { + // (Admin only). Defines whether to list the requested resources for all projects. + AllTenants bool `q:"all_tenants"` + // The snapshot name. + Name string `q:"name"` + // Filter by a snapshot description. + Description string `q:"description"` + // Filters by a share from which the snapshot was created. + ShareID string `q:"share_id"` + // Filters by a snapshot size in GB. + Size int `q:"size"` + // Filters by a snapshot status. + Status string `q:"status"` + // The maximum number of snapshots to return. + Limit int `q:"limit"` + // The offset to define start point of snapshot or snapshot group listing. + Offset int `q:"offset"` + // The key to sort a list of snapshots. + SortKey string `q:"sort_key"` + // The direction to sort a list of snapshots. + SortDir string `q:"sort_dir"` + // The UUID of the project in which the snapshot was created. Useful with all_tenants parameter. + ProjectID string `q:"project_id"` + // The name pattern that can be used to filter snapshots, snapshot snapshots, snapshot networks or snapshot groups. + NamePattern string `q:"name~"` + // The description pattern that can be used to filter snapshots, snapshot snapshots, snapshot networks or snapshot groups. + DescriptionPattern string `q:"description~"` +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToSnapshotListQuery() (string, error) +} + +// ToSnapshotListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToSnapshotListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDetail returns []Snapshot 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.ToSnapshotListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + p := SnapshotPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// Get will get a single snapshot with given UUID +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/sharedfilesystems/v2/snapshots/results.go b/openstack/sharedfilesystems/v2/snapshots/results.go new file mode 100644 index 0000000000..41f474403b --- /dev/null +++ b/openstack/sharedfilesystems/v2/snapshots/results.go @@ -0,0 +1,178 @@ +package snapshots + +import ( + "encoding/json" + "net/url" + "strconv" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +const ( + invalidMarker = "-1" +) + +// Snapshot contains all information associated with an OpenStack Snapshot +type Snapshot struct { + // The UUID of the snapshot + ID string `json:"id"` + // The name of the snapshot + Name string `json:"name,omitempty"` + // A description of the snapshot + Description string `json:"description,omitempty"` + // UUID of the share from which the snapshot was created + ShareID string `json:"share_id"` + // The shared file system protocol + ShareProto string `json:"share_proto"` + // Size of the snapshot share in GB + ShareSize int `json:"share_size"` + // Size of the snapshot in GB + Size int `json:"size"` + // The snapshot status + Status string `json:"status"` + // The UUID of the project in which the snapshot was created + ProjectID string `json:"project_id"` + // Timestamp when the snapshot was created + CreatedAt time.Time `json:"-"` + // Snapshot links for pagination + Links []map[string]string `json:"links"` +} + +func (r *Snapshot) UnmarshalJSON(b []byte) error { + type tmp Snapshot + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Snapshot(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + + return nil +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Snapshot object from the commonResult +func (r commonResult) Extract() (*Snapshot, error) { + var s struct { + Snapshot *Snapshot `json:"snapshot"` + } + err := r.ExtractInto(&s) + return s.Snapshot, err +} + +// SnapshotPage is a pagination.pager that is returned from a call to the List function. +type SnapshotPage struct { + pagination.MarkerPageBase +} + +// NextPageURL generates the URL for the page of results after this one. +func (r SnapshotPage) 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 SnapshotPage) LastMarker() (string, error) { + snapshots, err := ExtractSnapshots(r) + if err != nil { + return invalidMarker, err + } + if len(snapshots) == 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 SnapshotPage) IsEmpty() (bool, error) { + snapshots, err := ExtractSnapshots(r) + return len(snapshots) == 0, err +} + +// ExtractSnapshots extracts and returns a Snapshot slice. It is used while +// iterating over a snapshots.List call. +func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { + var s struct { + Snapshots []Snapshot `json:"snapshots"` + } + + err := (r.(SnapshotPage)).ExtractInto(&s) + + return s.Snapshots, err +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// IDFromName is a convenience function that returns a snapshot's ID given its name. +func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { + r, err := ListDetail(client, &ListOpts{Name: name}).AllPages() + if err != nil { + return "", err + } + + ss, err := ExtractSnapshots(r) + if err != nil { + return "", err + } + + switch len(ss) { + case 0: + return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} + case 1: + return ss[0].ID, nil + default: + return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: len(ss), ResourceType: "snapshot"} + } +} diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go new file mode 100644 index 0000000000..dfeaa1b582 --- /dev/null +++ b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go @@ -0,0 +1,102 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ( + snapshotEndpoint = "/snapshots" + snapshotID = "bc082e99-3bdb-4400-b95e-b85c7a41622c" +) + +var getResponse = `{ + "snapshot": { + "status": "available", + "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", + "description": null, + "links": [ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", + "rel": "self" + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", + "rel": "bookmark" + } + ], + "id": "bc082e99-3bdb-4400-b95e-b85c7a41622c", + "size": 1, + "user_id": "619e2ad074321cf246b03a89e95afee95fb26bb0b2d1fc7ba3bd30fcca25588a", + "name": "new_app_snapshot", + "created_at": "2019-01-06T11:11:02.000000", + "share_proto": "NFS", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "share_size": 1 + } +}` + +// MockGetResponse creates a mock get response +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc(snapshotEndpoint+"/"+snapshotID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, getResponse) + }) +} + +var listDetailResponse = `{ + "snapshots": [ + { + "status": "available", + "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", + "description": null, + "links": [ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", + "rel": "self" + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", + "rel": "bookmark" + } + ], + "id": "bc082e99-3bdb-4400-b95e-b85c7a41622c", + "size": 1, + "user_id": "619e2ad074321cf246b03a89e95afee95fb26bb0b2d1fc7ba3bd30fcca25588a", + "name": "new_app_snapshot", + "created_at": "2019-01-06T11:11:02.000000", + "share_proto": "NFS", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "share_size": 1 + } + ] +}` + +var listDetailEmptyResponse = `{"snapshots": []}` + +// MockListDetailResponse creates a mock detailed-list response +func MockListDetailResponse(t *testing.T) { + th.Mux.HandleFunc(snapshotEndpoint+"/detail", 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) + + r.ParseForm() + marker := r.Form.Get("offset") + + switch marker { + case "": + fmt.Fprint(w, listDetailResponse) + default: + fmt.Fprint(w, listDetailEmptyResponse) + } + }) +} diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go new file mode 100644 index 0000000000..d22c1dde73 --- /dev/null +++ b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go @@ -0,0 +1,81 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + s, err := snapshots.Get(client.ServiceClient(), snapshotID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, s, &snapshots.Snapshot{ + ID: snapshotID, + Name: "new_app_snapshot", + Description: "", + ShareID: "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", + ShareProto: "NFS", + ShareSize: 1, + Size: 1, + Status: "available", + ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", + CreatedAt: time.Date(2019, time.January, 06, 11, 11, 02, 0, time.UTC), + Links: []map[string]string{ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", + "rel": "self", + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", + "rel": "bookmark", + }, + }, + }) +} + +func TestListDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListDetailResponse(t) + + allPages, err := snapshots.ListDetail(client.ServiceClient(), &snapshots.ListOpts{}).AllPages() + + th.AssertNoErr(t, err) + + actual, err := snapshots.ExtractSnapshots(allPages) + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, actual, []snapshots.Snapshot{ + snapshots.Snapshot{ + ID: snapshotID, + Name: "new_app_snapshot", + Description: "", + ShareID: "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", + ShareProto: "NFS", + ShareSize: 1, + Size: 1, + Status: "available", + ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", + CreatedAt: time.Date(2019, time.January, 06, 11, 11, 02, 0, time.UTC), + Links: []map[string]string{ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", + "rel": "self", + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/bc082e99-3bdb-4400-b95e-b85c7a41622c", + "rel": "bookmark", + }, + }, + }, + }) +} diff --git a/openstack/sharedfilesystems/v2/snapshots/urls.go b/openstack/sharedfilesystems/v2/snapshots/urls.go new file mode 100644 index 0000000000..6c27c42854 --- /dev/null +++ b/openstack/sharedfilesystems/v2/snapshots/urls.go @@ -0,0 +1,11 @@ +package snapshots + +import "github.com/gophercloud/gophercloud" + +func listDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("snapshots", "detail") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id) +} From 6c87d5c360091411b473814d52900653a55ea497 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Wed, 9 Jan 2019 19:08:51 -0800 Subject: [PATCH 0626/2296] Senlin: Node Recover (#1383) * Senlin: Node Recover * Changed erroneous comment from 'cluster' to 'node' * Use actionURL rather than recoverURL * Uncommented RecoverAction for RECREATE. Changed commented github link to use commit hash --- .../openstack/clustering/v1/nodes_test.go | 62 +++++++++++++++++++ openstack/clustering/v1/nodes/doc.go | 14 +++++ openstack/clustering/v1/nodes/requests.go | 35 ++++++++++- .../clustering/v1/nodes/testing/fixtures.go | 18 ++++++ .../v1/nodes/testing/requests_test.go | 14 +++++ openstack/clustering/v1/nodes/urls.go | 4 ++ 6 files changed, 146 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index 780b88291b..b283d9e4e4 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -121,3 +121,65 @@ func TestNodesOps(t *testing.T) { t.Logf("Successfully performed '%s' on node: %s", opName, node.ID) } } + +func TestNodesRecover(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.6" + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + node, err := CreateNode(t, client, cluster.ID, profile.ID) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node.ID) + + checkTrue := true + checkFalse := false + + // TODO: nodes.RebuildRecovery is commented out as of 12/14/2018 the API backend can't perform the action without returning error + ops := []nodes.RecoverOpts{ + // Microversion < 1.6 legacy support where argument DOES NOT support Check + nodes.RecoverOpts{}, + nodes.RecoverOpts{Operation: nodes.RebootRecovery}, + // nodes.RecoverOpts{Operation: nodes.RebuildRecovery}, + + // MicroVersion >= 1.6 that supports Check where Check is true + nodes.RecoverOpts{Check: &checkTrue}, + nodes.RecoverOpts{Operation: nodes.RebootRecovery, Check: &checkTrue}, + //nodes.RecoverOpts{Operation: nodes.RebuildRecovery, Check: &checkTrue}, + + // MicroVersion >= 1.6 that supports Check where Check is false + nodes.RecoverOpts{Check: &checkFalse}, + nodes.RecoverOpts{Operation: nodes.RebootRecovery, Check: &checkFalse}, + //nodes.RecoverOpts{Operation: nodes.RebuildRecovery, Check: &checkFalse}, + } + + for _, recoverOpt := range ops { + if recoverOpt.Check != nil { + t.Logf("Attempting to recover by using '%s' check=%t on node: %s", recoverOpt.Operation, *recoverOpt.Check, node.ID) + } else { + t.Logf("Attempting to recover by using '%s' on node: %s", recoverOpt.Operation, node.ID) + } + + actionID, err := nodes.Recover(client, node.ID, recoverOpt).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + if recoverOpt.Check != nil { + t.Logf("Successfully recovered by using '%s' check=%t on node: %s", recoverOpt.Operation, *recoverOpt.Check, node.ID) + } else { + t.Logf("Successfully recovered by using '%s' on node: %s", recoverOpt.Operation, node.ID) + } + + node, err := nodes.Get(client, node.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, node) + } +} diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index cffe2a3cd0..ab09b51098 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -84,5 +84,19 @@ Example to Perform an Operation on a Node panic(err) } +Example to Recover a Node + + nodeID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + check := true + recoverOpts := nodes.RecoverOpts{ + Operation: nodes.RebuildRecovery, + Check: &check, + } + actionID, err := nodes.Recover(computeClient, nodeID, recoverOpts).Extract() + if err != nil { + panic(err) + } + fmt.Println("action=", actionID) + */ package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index e7c4c36038..773bf3df4e 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -60,7 +60,7 @@ type UpdateOpts struct { Metadata map[string]interface{} `json:"metadata,omitempty"` } -// ToClusterUpdateMap constructs a request body from UpdateOpts. +// ToNodeUpdateMap constructs a request body from UpdateOpts. func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "node") } @@ -205,3 +205,36 @@ func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder return } + +func (opts RecoverOpts) ToNodeRecoverMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "recover") +} + +// RecoverAction represents valid values for recovering a node. +type RecoverAction string + +const ( + RebootRecovery RecoverAction = "REBOOT" + RebuildRecovery RecoverAction = "REBUILD" + // RECREATE currently is NOT supported. See https://github.com/openstack/senlin/blob/b30b2b8496b2b8af243ccd5292f38aec7a95664f/senlin/profiles/base.py#L533 + RecreateRecovery RecoverAction = "RECREATE" +) + +type RecoverOpts struct { + Operation RecoverAction `json:"operation,omitempty"` + Check *bool `json:"check,omitempty"` +} + +func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r ActionResult) { + b, err := opts.ToNodeRecoverMap() + if err != nil { + r.Err = err + return + } + var result *http.Response + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + r.Header = result.Header + return +} diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go index 7c3872cfbe..cfad670a40 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures.go +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -261,6 +261,13 @@ const OperationActionResponse = ` const OperationExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" +const ActionResponse = ` +{ + "action": "2a0ff107-e789-4660-a122-3816c43af703" +}` + +const ExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" + func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -333,3 +340,14 @@ func HandleOpsSuccessfully(t *testing.T) { fmt.Fprint(w, OperationActionResponse) }) } + +func HandleRecoverSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/nodes/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-edce3528-864f-41fb-8759-f4707925cc09") + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, ActionResponse) + }) +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 94f95676bd..ba1cc9316d 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -117,3 +117,17 @@ func TestOpsNode(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, OperationExpectedActionID, actual) } + +func TestNodeRecover(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleRecoverSuccessfully(t) + recoverOpts := nodes.RecoverOpts{ + Operation: nodes.RebuildRecovery, + Check: new(bool), + } + actionID, err := nodes.Recover(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, ExpectedActionID, actionID) +} diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go index b8abdb4047..1224dcb066 100644 --- a/openstack/clustering/v1/nodes/urls.go +++ b/openstack/clustering/v1/nodes/urls.go @@ -9,6 +9,10 @@ func commonURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiVersion, apiName) } +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "actions") +} + func createURL(client *gophercloud.ServiceClient) string { return commonURL(client) } From 07de4ce3c8b94e997c75205cfee74c33ffdaada7 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Thu, 10 Jan 2019 11:25:37 -0600 Subject: [PATCH 0627/2296] LBaaSv2 / Octavia: Add client and member data timeouts (#1393) (#1394) Signed-off-by: Monty Taylor --- openstack/loadbalancer/v2/listeners/doc.go | 3 ++ .../loadbalancer/v2/listeners/requests.go | 40 +++++++++++++------ .../loadbalancer/v2/listeners/results.go | 6 +++ .../v2/listeners/testing/fixtures.go | 22 ++++++++-- .../v2/listeners/testing/requests_test.go | 9 +++-- 5 files changed, 60 insertions(+), 20 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/doc.go b/openstack/loadbalancer/v2/listeners/doc.go index 4ccb967755..eeea6eea82 100644 --- a/openstack/loadbalancer/v2/listeners/doc.go +++ b/openstack/loadbalancer/v2/listeners/doc.go @@ -43,8 +43,11 @@ Example to Update a Listener listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" i1001 := 1001 + i181000 := 181000 updateOpts := listeners.UpdateOpts{ ConnLimit: &i1001, + TimeoutClientData: &i181000, + TimeoutMemberData: &i181000, } listener, err := listeners.Update(networkClient, listenerID, updateOpts).Extract() diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index c6412eb657..2c5139986e 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -28,19 +28,21 @@ type ListOptsBuilder interface { // sort by a particular listener 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"` - AdminStateUp *bool `q:"admin_state_up"` - ProjectID string `q:"project_id"` - LoadbalancerID string `q:"loadbalancer_id"` - DefaultPoolID string `q:"default_pool_id"` - Protocol string `q:"protocol"` - ProtocolPort int `q:"protocol_port"` - ConnectionLimit int `q:"connection_limit"` - 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"` + AdminStateUp *bool `q:"admin_state_up"` + ProjectID string `q:"project_id"` + LoadbalancerID string `q:"loadbalancer_id"` + DefaultPoolID string `q:"default_pool_id"` + Protocol string `q:"protocol"` + ProtocolPort int `q:"protocol_port"` + ConnectionLimit int `q:"connection_limit"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + TimeoutClientData *int `q:"timeout_client_data"` + TimeoutMemberData *int `q:"timeout_member_data"` } // ToListenerListQuery formats a ListOpts into a query string. @@ -111,6 +113,12 @@ type CreateOpts struct { // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Frontend client inactivity timeout in milliseconds + TimeoutClientData *int `json:"timeout_client_data,omitempty"` + + // Backend member inactivity timeout in milliseconds + TimeoutMemberData *int `json:"timeout_member_data,omitempty"` } // ToListenerCreateMap builds a request body from CreateOpts. @@ -170,6 +178,12 @@ type UpdateOpts struct { // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Frontend client inactivity timeout in milliseconds + TimeoutClientData *int `json:"timeout_client_data,omitempty"` + + // Backend member inactivity timeout in milliseconds + TimeoutMemberData *int `json:"timeout_member_data,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 847a03fb40..45164b3335 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -62,6 +62,12 @@ type Listener struct { // The provisioning status of the Listener. // This value is ACTIVE, PENDING_* or ERROR. ProvisioningStatus string `json:"provisioning_status"` + + // Frontend client inactivity timeout in milliseconds + TimeoutClientData int `json:"timeout_client_data"` + + // Backend member inactivity timeout in milliseconds + TimeoutMemberData int `json:"timeout_member_data"` } type Stats struct { diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index 310fd391a3..47c07aaaa6 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -39,7 +39,9 @@ const ListenersListBody = ` "connection_limit": 2000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] + "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], + "timeout_client_data": 50000, + "timeout_member_data": 50000 } ] } @@ -60,7 +62,10 @@ const SingleListenerBody = ` "connection_limit": 2000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] + "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], + "timeout_client_data": 50000, + "timeout_member_data": 50000 + } } ` @@ -80,7 +85,10 @@ const PostUpdateListenerBody = ` "connection_limit": 1000, "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] + "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], + "timeout_client_data": 181000, + "timeout_member_data": 181000 + } } ` @@ -125,6 +133,8 @@ var ( AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, + TimeoutClientData: 50000, + TimeoutMemberData: 50000, } ListenerUpdated = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -139,6 +149,8 @@ var ( AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, + TimeoutClientData: 181000, + TimeoutMemberData: 181000, } ListenerStatsTree = listeners.Stats{ ActiveConnections: 0, @@ -225,7 +237,9 @@ func HandleListenerUpdateSuccessfully(t *testing.T) { "listener": { "name": "NewListenerName", "default_pool_id": null, - "connection_limit": 1001 + "connection_limit": 1001, + "timeout_client_data": 181000, + "timeout_member_data": 181000 } }`) diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 3ceeddc4d4..f1a4093725 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -125,12 +125,15 @@ func TestUpdateListener(t *testing.T) { client := fake.ServiceClient() i1001 := 1001 + i181000 := 181000 name := "NewListenerName" defaultPoolID := "" actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ - Name: &name, - ConnLimit: &i1001, - DefaultPoolID: &defaultPoolID, + Name: &name, + ConnLimit: &i1001, + DefaultPoolID: &defaultPoolID, + TimeoutMemberData: &i181000, + TimeoutClientData: &i181000, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From b9ea9cb68cf5803ea1567c404b549a783c8264b2 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 11 Jan 2019 18:58:16 +0300 Subject: [PATCH 0628/2296] NetworkingV2: add prefixlen for Subnet Create (#1374) Add Prefixlen field into the Subnet Create call. Provide unit and acceptance tests with additional helpers. --- .../v2/extensions/subnetpools/subnetpools.go | 7 +-- .../openstack/networking/v2/networking.go | 29 +++++++++++ .../openstack/networking/v2/subnets_test.go | 35 +++++++++++++ openstack/networking/v2/subnets/requests.go | 4 ++ .../networking/v2/subnets/testing/fixtures.go | 13 +++++ .../v2/subnets/testing/requests_test.go | 49 +++++++++++++++++++ 6 files changed, 134 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go index 8c110263e0..6380264b10 100644 --- a/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go +++ b/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go @@ -18,9 +18,10 @@ func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetp "10.0.0.0/8", } createOpts := subnetpools.CreateOpts{ - Name: subnetPoolName, - Description: subnetPoolDescription, - Prefixes: subnetPoolPrefixes, + Name: subnetPoolName, + Description: subnetPoolDescription, + Prefixes: subnetPoolPrefixes, + DefaultPrefixLen: 24, } t.Logf("Attempting to create a subnetpool: %s", subnetPoolName) diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index b92eac91e2..6690656103 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -401,6 +401,35 @@ func CreateSubnetWithSubnetPoolNoCIDR(t *testing.T, client *gophercloud.ServiceC return subnet, nil } +// CreateSubnetWithSubnetPoolPrefixlen will create a subnet associated with the +// provided subnetpool on the specified Network ID and with overwritten +// prefixlen instead of the default subnetpool prefixlen. +// An error will be returned if the subnet or the subnetpool could not be created. +func CreateSubnetWithSubnetPoolPrefixlen(t *testing.T, client *gophercloud.ServiceClient, networkID string, subnetPoolID string) (*subnets.Subnet, error) { + subnetName := tools.RandomString("TESTACC-", 8) + createOpts := subnets.CreateOpts{ + NetworkID: networkID, + IPVersion: 4, + Name: subnetName, + EnableDHCP: gophercloud.Disabled, + SubnetPoolID: subnetPoolID, + Prefixlen: 12, + } + + 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) + + return subnet, nil +} + // DeleteNetwork will delete a network with a specified ID. A fatal error will // occur if the delete was not successful. This works best when used as a // deferred function. diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index 0270a3430f..e7d55b4f44 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -182,3 +182,38 @@ func TestSubnetsWithSubnetPoolNoCIDR(t *testing.T) { t.Fatalf("A subnet pool was not associated.") } } + +func TestSubnetsWithSubnetPoolPrefixlen(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 SubnetPool + subnetPool, err := subnetpools.CreateSubnetPool(t, client) + th.AssertNoErr(t, err) + defer subnetpools.DeleteSubnetPool(t, client, subnetPool.ID) + + // Create Subnet + subnet, err := CreateSubnetWithSubnetPoolPrefixlen(t, client, network.ID, subnetPool.ID) + th.AssertNoErr(t, err) + defer DeleteSubnet(t, client, subnet.ID) + + tools.PrintResource(t, subnet) + + if subnet.GatewayIP == "" { + t.Fatalf("A subnet pool was not associated.") + } + + cidrParts := strings.Split(subnet.CIDR, "/") + if len(cidrParts) != 2 { + t.Fatalf("Got invalid CIDR for subnet '%s': %s", subnet.ID, subnet.CIDR) + } + + if cidrParts[1] != "12" { + t.Fatalf("Got invalid prefix length for subnet '%s': wanted 12 but got '%s'", subnet.ID, cidrParts[1]) + } +} diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index bb73e030c0..d2c4a29f7c 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -131,6 +131,10 @@ type CreateOpts struct { // SubnetPoolID is the id of the subnet pool that subnet should be associated to. SubnetPoolID string `json:"subnetpool_id,omitempty"` + + // Prefixlen is used when user creates a subnet from the subnetpool. It will + // overwrite the "default_prefixlen" value of the referenced subnetpool. + Prefixlen int `json:"prefixlen,omitempty"` } // ToSubnetCreateMap builds a request body from CreateOpts. diff --git a/openstack/networking/v2/subnets/testing/fixtures.go b/openstack/networking/v2/subnets/testing/fixtures.go index 8cac911eb3..38cdbc8559 100644 --- a/openstack/networking/v2/subnets/testing/fixtures.go +++ b/openstack/networking/v2/subnets/testing/fixtures.go @@ -348,6 +348,19 @@ const SubnetCreateRequestWithNoCIDR = ` } ` +const SubnetCreateRequestWithPrefixlen = ` +{ + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ip_version": 4, + "dns_nameservers": ["foo"], + "host_routes": [{"destination":"","nexthop": "bar"}], + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b", + "prefixlen": 12 + } +} +` + const SubnetUpdateRequest = ` { "subnet": { diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index bef6794b26..abcc3dd781 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -334,6 +334,55 @@ func TestCreateWithNoCIDR(t *testing.T) { th.AssertEquals(t, s.SubnetPoolID, "b80340c7-9960-4f67-a99c-02501656284b") } +func TestCreateWithPrefixlen(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/subnets", 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, SubnetCreateRequestWithPrefixlen) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, SubnetCreateResult) + }) + + opts := subnets.CreateOpts{ + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + IPVersion: 4, + DNSNameservers: []string{"foo"}, + HostRoutes: []subnets.HostRoute{ + {NextHop: "bar"}, + }, + SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", + Prefixlen: 12, + } + s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "") + 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.AllocationPools, []subnets.AllocationPool{ + { + Start: "192.168.199.2", + End: "192.168.199.254", + }, + }) + th.AssertDeepEquals(t, s.HostRoutes, []subnets.HostRoute{}) + th.AssertEquals(t, s.IPVersion, 4) + th.AssertEquals(t, s.GatewayIP, "192.168.199.1") + th.AssertEquals(t, s.CIDR, "192.168.199.0/24") + th.AssertEquals(t, s.ID, "3b80198d-4f7b-4f77-9ef5-774d54e17126") + th.AssertEquals(t, s.SubnetPoolID, "b80340c7-9960-4f67-a99c-02501656284b") +} + func TestRequiredCreateOpts(t *testing.T) { res := subnets.Create(fake.ServiceClient(), subnets.CreateOpts{}) if res.Err == nil { From 5062cf9e07e7eb338d4c23425ec4cba9fda6da0e Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 12 Jan 2019 03:31:10 +0100 Subject: [PATCH 0629/2296] Manila: add share snapshot support (tests) (#1392) * Manila: add share snapshots support (create) * Manila: Remove "force" from snapshots create method * Manila: add share snapshots support (delete) * Manila: add share snapshots support (update) * Manila: share tests cleanup before snapshots support * Manila: add share snapshots acceptance tests --- .../sharedfilesystems/v2/securityservices.go | 17 --- .../v2/securityservices_test.go | 9 +- .../sharedfilesystems/v2/sharenetworks.go | 17 --- .../v2/sharenetworks_test.go | 11 +- .../openstack/sharedfilesystems/v2/shares.go | 30 +---- .../sharedfilesystems/v2/shares_test.go | 21 ++-- .../sharedfilesystems/v2/sharetypes.go | 8 -- .../sharedfilesystems/v2/sharetypes_test.go | 11 +- .../sharedfilesystems/v2/snapshots.go | 101 +++++++++++++++ .../sharedfilesystems/v2/snapshots_test.go | 118 ++++++++++++++++++ .../v2/snapshots/requests.go | 88 +++++++++++++ .../sharedfilesystems/v2/snapshots/results.go | 15 +++ .../v2/snapshots/testing/fixtures.go | 104 +++++++++++++++ .../v2/snapshots/testing/request_test.go | 46 +++++++ .../sharedfilesystems/v2/snapshots/urls.go | 12 ++ 15 files changed, 516 insertions(+), 92 deletions(-) create mode 100644 acceptance/openstack/sharedfilesystems/v2/snapshots.go create mode 100644 acceptance/openstack/sharedfilesystems/v2/snapshots_test.go diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices.go b/acceptance/openstack/sharedfilesystems/v2/securityservices.go index 149cf6fea3..342a91789e 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices.go +++ b/acceptance/openstack/sharedfilesystems/v2/securityservices.go @@ -43,20 +43,3 @@ func DeleteSecurityService(t *testing.T, client *gophercloud.ServiceClient, secu t.Logf("Deleted security service: %s", securityService.ID) } - -// PrintSecurityService will print a security service and all of its attributes. -func PrintSecurityService(t *testing.T, securityService *securityservices.SecurityService) { - t.Logf("ID: %s", securityService.ID) - t.Logf("Project ID: %s", securityService.ProjectID) - t.Logf("Domain: %s", securityService.Domain) - t.Logf("Status: %s", securityService.Status) - t.Logf("Type: %s", securityService.Type) - t.Logf("Name: %s", securityService.Name) - t.Logf("Description: %s", securityService.Description) - t.Logf("DNS IP: %s", securityService.DNSIP) - t.Logf("User: %s", securityService.User) - t.Logf("Password: %s", securityService.Password) - t.Logf("Server: %s", securityService.Server) - t.Logf("Created at: %v", securityService.CreatedAt) - t.Logf("Updated at: %v", securityService.UpdatedAt) -} diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index 0d72c36b47..34b8a79e79 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/securityservices_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/sharedfilesystems/v2/securityservices" ) @@ -31,7 +32,7 @@ func TestSecurityServiceCreateDelete(t *testing.T) { t.Fatalf("Security service description was expeted to be: %s", securityService.Description) } - PrintSecurityService(t, securityService) + tools.PrintResource(t, securityService) defer DeleteSecurityService(t, client, securityService) } @@ -53,7 +54,7 @@ func TestSecurityServiceList(t *testing.T) { } for _, securityService := range allSecurityServices { - PrintSecurityService(t, &securityService) + tools.PrintResource(t, &securityService) } } @@ -95,7 +96,7 @@ func TestSecurityServiceListFiltering(t *testing.T) { if listedSecurityService.Name != securityService.Name { t.Fatalf("The name of the security service was expected to be %s", securityService.Name) } - PrintSecurityService(t, &listedSecurityService) + tools.PrintResource(t, &listedSecurityService) } } @@ -142,7 +143,7 @@ func TestSecurityServiceUpdate(t *testing.T) { t.Fatalf("Security service type was expected to be: %s", options.Type) } - PrintSecurityService(t, securityService) + tools.PrintResource(t, securityService) defer DeleteSecurityService(t, client, securityService) } diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go index f27f0df566..55aad83eee 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go @@ -49,20 +49,3 @@ func DeleteShareNetwork(t *testing.T, client *gophercloud.ServiceClient, shareNe t.Logf("Deleted share network: %s", shareNetworkID) } - -// PrintShareNetwork will print a share network and all of its attributes. -func PrintShareNetwork(t *testing.T, sharenetwork *sharenetworks.ShareNetwork) { - t.Logf("ID: %s", sharenetwork.ID) - t.Logf("Project ID: %s", sharenetwork.ProjectID) - t.Logf("Neutron network ID: %s", sharenetwork.NeutronNetID) - t.Logf("Neutron sub-network ID: %s", sharenetwork.NeutronSubnetID) - t.Logf("Nova network ID: %s", sharenetwork.NovaNetID) - t.Logf("Network type: %s", sharenetwork.NetworkType) - t.Logf("Segmentation ID: %d", sharenetwork.SegmentationID) - t.Logf("CIDR: %s", sharenetwork.CIDR) - t.Logf("IP version: %d", sharenetwork.IPVersion) - t.Logf("Name: %s", sharenetwork.Name) - t.Logf("Description: %s", sharenetwork.Description) - t.Logf("Created at: %v", sharenetwork.CreatedAt) - t.Logf("Updated at: %v", sharenetwork.UpdatedAt) -} diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index 727539886e..16b2bfbc9e 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_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/sharedfilesystems/v2/sharenetworks" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -33,7 +34,7 @@ func TestShareNetworkCreateDestroy(t *testing.T) { t.Fatalf("Share network description was expeted to be: %s", shareNetwork.Description) } - PrintShareNetwork(t, shareNetwork) + tools.PrintResource(t, shareNetwork) defer DeleteShareNetwork(t, client, shareNetwork.ID) } @@ -81,7 +82,7 @@ func TestShareNetworkUpdate(t *testing.T) { th.CheckDeepEquals(t, expectedShareNetwork, updatedShareNetwork) - PrintShareNetwork(t, shareNetwork) + tools.PrintResource(t, shareNetwork) defer DeleteShareNetwork(t, client, shareNetwork.ID) } @@ -103,7 +104,7 @@ func TestShareNetworkListDetail(t *testing.T) { } for _, shareNetwork := range allShareNetworks { - PrintShareNetwork(t, &shareNetwork) + tools.PrintResource(t, &shareNetwork) } } @@ -145,7 +146,7 @@ func TestShareNetworkListFiltering(t *testing.T) { if listedShareNetwork.Name != shareNetwork.Name { t.Fatalf("The name of the share network was expected to be %s", shareNetwork.Name) } - PrintShareNetwork(t, &listedShareNetwork) + tools.PrintResource(t, &listedShareNetwork) } } @@ -225,5 +226,5 @@ func TestShareNetworkAddRemoveSecurityService(t *testing.T) { t.Errorf("Unable to remove security service: %v", err) } - PrintShareNetwork(t, shareNetwork) + tools.PrintResource(t, shareNetwork) } diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index dd52917b93..dee6abfe95 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -1,7 +1,6 @@ package v2 import ( - "encoding/json" "fmt" "strings" "testing" @@ -32,9 +31,8 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share share, err := shares.Create(client, createOpts).Extract() if err != nil { - t.Logf("Failed to create %s share", share.ID) - DeleteShare(t, client, share) - return share, err + t.Logf("Failed to create share") + return nil, err } err = waitForStatus(t, client, share.ID, "available", 600) @@ -98,26 +96,6 @@ func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares. } } -// PrintShare prints some information of the share -func PrintShare(t *testing.T, share *shares.Share) { - asJSON, err := json.MarshalIndent(share, "", " ") - if err != nil { - t.Logf("Cannot print the contents of %s", share.ID) - } - - t.Logf("Share %s", string(asJSON)) -} - -// PrintAccessRight prints contents of an access rule -func PrintAccessRight(t *testing.T, accessRight *shares.AccessRight) { - asJSON, err := json.MarshalIndent(accessRight, "", " ") - if err != nil { - t.Logf("Cannot print access rule") - } - - t.Logf("Access rule %s", string(asJSON)) -} - // ExtendShare extends the capacity of an existing share func ExtendShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, newSize int) error { return shares.Extend(client, share.ID, &shares.ExtendOpts{NewSize: newSize}).ExtractErr() @@ -128,7 +106,7 @@ func ShrinkShare(t *testing.T, client *gophercloud.ServiceClient, share *shares. return shares.Shrink(client, share.ID, &shares.ShrinkOpts{NewSize: newSize}).ExtractErr() } -func printMessages(t *testing.T, c *gophercloud.ServiceClient, id string) error { +func PrintMessages(t *testing.T, c *gophercloud.ServiceClient, id string) error { c.Microversion = "2.37" allPages, err := messages.List(c, messages.ListOpts{ResourceID: id}).AllPages() @@ -175,7 +153,7 @@ func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string }) if err != nil { - mErr := printMessages(t, c, id) + mErr := PrintMessages(t, c, id) if mErr != nil { return fmt.Errorf("Share status is '%s' and unable to get manila messages: %s", err, mErr) } diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index e0598f68a4..1aef7d6973 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_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/sharedfilesystems/v2/shares" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -11,7 +12,7 @@ import ( func TestShareCreate(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { - t.Fatalf("Unable to create a sharedfs client: %v", err) + t.Fatalf("Unable to create a shared file system client: %v", err) } share, err := CreateShare(t, client) @@ -25,7 +26,7 @@ func TestShareCreate(t *testing.T) { if err != nil { t.Errorf("Unable to retrieve share: %v", err) } - PrintShare(t, created) + tools.PrintResource(t, created) } func TestShareUpdate(t *testing.T) { @@ -72,7 +73,7 @@ func TestShareUpdate(t *testing.T) { // Update time has to be set in order to get the assert equal to pass expectedShare.UpdatedAt = updatedShare.UpdatedAt - PrintShare(t, share) + tools.PrintResource(t, share) th.CheckDeepEquals(t, expectedShare, updatedShare) } @@ -80,7 +81,7 @@ func TestShareUpdate(t *testing.T) { func TestShareListDetail(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { - t.Fatalf("Unable to create a sharedfs client: %v", err) + t.Fatalf("Unable to create a shared file system client: %v", err) } share, err := CreateShare(t, client) @@ -96,14 +97,14 @@ func TestShareListDetail(t *testing.T) { } for i := range ss { - PrintShare(t, &ss[i]) + tools.PrintResource(t, &ss[i]) } } func TestGrantAndRevokeAccess(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { - t.Fatalf("Unable to create a sharedfs client: %v", err) + t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" @@ -119,7 +120,7 @@ func TestGrantAndRevokeAccess(t *testing.T) { t.Fatalf("Unable to grant access: %v", err) } - PrintAccessRight(t, accessRight) + tools.PrintResource(t, accessRight) if err = RevokeAccess(t, client, share, accessRight); err != nil { t.Fatalf("Unable to revoke access: %v", err) @@ -129,7 +130,7 @@ func TestGrantAndRevokeAccess(t *testing.T) { func TestListAccessRights(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { - t.Fatalf("Unable to create a sharedfs client: %v", err) + t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" @@ -157,14 +158,14 @@ func TestListAccessRights(t *testing.T) { t.Logf("Share %s has %d access rule(s):", share.ID, len(rs)) for _, r := range rs { - PrintAccessRight(t, &r) + tools.PrintResource(t, &r) } } func TestExtendAndShrink(t *testing.T) { client, err := clients.NewSharedFileSystemV2Client() if err != nil { - t.Fatalf("Unable to create a sharedfs client: %v", err) + t.Fatalf("Unable to create a shared file system client: %v", err) } client.Microversion = "2.7" diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes.go b/acceptance/openstack/sharedfilesystems/v2/sharetypes.go index 97b44bd0ef..4debc1fd33 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharetypes.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharetypes.go @@ -46,11 +46,3 @@ func DeleteShareType(t *testing.T, client *gophercloud.ServiceClient, shareType t.Logf("Deleted share type: %s", shareType.ID) } - -// PrintShareType will print a share type and all of its attributes. -func PrintShareType(t *testing.T, shareType *sharetypes.ShareType) { - t.Logf("Name: %s", shareType.Name) - t.Logf("ID: %s", shareType.ID) - t.Logf("OS share type access is public: %t", shareType.IsPublic) - t.Logf("Extra specs: %#v", shareType.ExtraSpecs) -} diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index 9efe0359d1..e5cb2ac82a 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharetypes_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/sharedfilesystems/v2/sharetypes" ) @@ -18,7 +19,7 @@ func TestShareTypeCreateDestroy(t *testing.T) { t.Fatalf("Unable to create share type: %v", err) } - PrintShareType(t, shareType) + tools.PrintResource(t, shareType) defer DeleteShareType(t, client, shareType) } @@ -40,7 +41,7 @@ func TestShareTypeList(t *testing.T) { } for _, shareType := range allShareTypes { - PrintShareType(t, &shareType) + tools.PrintResource(t, &shareType) } } @@ -55,7 +56,7 @@ func TestShareTypeGetDefault(t *testing.T) { t.Fatalf("Unable to retrieve the default share type: %v", err) } - PrintShareType(t, shareType) + tools.PrintResource(t, shareType) } func TestShareTypeExtraSpecs(t *testing.T) { @@ -105,7 +106,7 @@ func TestShareTypeExtraSpecs(t *testing.T) { t.Fatalf("my_new_key was expected to be unset for Share type: %s", shareType.Name) } - PrintShareType(t, shareType) + tools.PrintResource(t, shareType) defer DeleteShareType(t, client, shareType) } @@ -155,7 +156,7 @@ func TestShareTypeAccess(t *testing.T) { t.Fatalf("No access should be left for the share type: %s", shareType.Name) } - PrintShareType(t, shareType) + tools.PrintResource(t, shareType) defer DeleteShareType(t, client, shareType) diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots.go b/acceptance/openstack/sharedfilesystems/v2/snapshots.go new file mode 100644 index 0000000000..852e8844c1 --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots.go @@ -0,0 +1,101 @@ +package v2 + +import ( + "fmt" + "strings" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" +) + +// CreateSnapshot will create a snapshot from the share ID with a name. An error will +// be returned if the snapshot could not be created +func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, shareID string) (*snapshots.Snapshot, error) { + if testing.Short() { + t.Skip("Skipping test that requres share creation in short mode.") + } + + createOpts := snapshots.CreateOpts{ + ShareID: shareID, + Name: "My Test Snapshot", + Description: "My Test Description", + } + + snapshot, err := snapshots.Create(client, createOpts).Extract() + if err != nil { + t.Logf("Failed to create snapshot") + return nil, err + } + + err = waitForSnapshotStatus(t, client, snapshot.ID, "available", 600) + if err != nil { + t.Logf("Failed to get %s snapshot status", snapshot.ID) + return snapshot, err + } + + return snapshot, nil +} + +// ListSnapshots lists all snapshots that belong to this tenant's project. +// An error will be returned if the snapshots could not be listed.. +func ListSnapshots(t *testing.T, client *gophercloud.ServiceClient) ([]snapshots.Snapshot, error) { + r, err := snapshots.ListDetail(client, &snapshots.ListOpts{}).AllPages() + if err != nil { + return nil, err + } + + return snapshots.ExtractSnapshots(r) +} + +// DeleteSnapshot will delete a snapshot. A fatal error will occur if the snapshot +// failed to be deleted. This works best when used as a deferred function. +func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { + err := snapshots.Delete(client, snapshot.ID).ExtractErr() + if err != nil { + t.Errorf("Unable to delete snapshot %s: %v", snapshot.ID, err) + } + + err = waitForSnapshotStatus(t, client, snapshot.ID, "deleted", 600) + if err != nil { + t.Errorf("Failed to wait for 'deleted' status for %s snapshot: %v", snapshot.ID, err) + } else { + t.Logf("Deleted snapshot: %s", snapshot.ID) + } +} + +func waitForSnapshotStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string, secs int) error { + err := gophercloud.WaitFor(secs, func() (bool, error) { + current, err := snapshots.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 fmt.Errorf("Snapshot status is '%s' and unable to get manila messages: %s", err, mErr) + } + } + + return err +} diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go new file mode 100644 index 0000000000..d94055902b --- /dev/null +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -0,0 +1,118 @@ +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestSnapshotCreate(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + + 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) + + created, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + t.Fatalf("Unable to retrieve a snapshot: %v", err) + } + + tools.PrintResource(t, created) +} + +func TestSnapshotUpdate(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create shared file system client: %v", err) + } + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create 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) + + expectedSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve snapshot: %v", err) + } + + name := "NewName" + description := "" + options := snapshots.UpdateOpts{ + DisplayName: &name, + DisplayDescription: &description, + } + + expectedSnapshot.Name = name + expectedSnapshot.Description = description + + _, err = snapshots.Update(client, snapshot.ID, options).Extract() + if err != nil { + t.Errorf("Unable to update snapshot: %v", err) + } + + updatedSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + if err != nil { + t.Errorf("Unable to retrieve snapshot: %v", err) + } + + tools.PrintResource(t, snapshot) + + th.CheckDeepEquals(t, expectedSnapshot, updatedSnapshot) +} + +func TestSnapshotListDetail(t *testing.T) { + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + + 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) + + ss, err := ListSnapshots(t, client) + if err != nil { + t.Fatalf("Unable to list snapshots: %v", err) + } + + for i := range ss { + tools.PrintResource(t, &ss[i]) + } +} diff --git a/openstack/sharedfilesystems/v2/snapshots/requests.go b/openstack/sharedfilesystems/v2/snapshots/requests.go index 255479ab96..1f2f7d7cc9 100644 --- a/openstack/sharedfilesystems/v2/snapshots/requests.go +++ b/openstack/sharedfilesystems/v2/snapshots/requests.go @@ -5,6 +5,52 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToSnapshotCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains the options for create a Snapshot. This object is +// passed to snapshots.Create(). For more information about these parameters, +// please refer to the Snapshot object, or the shared file systems API v2 +// documentation +type CreateOpts struct { + // The UUID of the share from which to create a snapshot + ShareID string `json:"share_id" required:"true"` + // Defines the snapshot name + Name string `json:"name,omitempty"` + // Defines the snapshot description + Description string `json:"description,omitempty"` + // DisplayName is equivalent to Name. The API supports using both + // This is an inherited attribute from the block storage API + DisplayName string `json:"display_name,omitempty"` + // DisplayDescription is equivalent to Description. The API supports using both + // This is an inherited attribute from the block storage API + DisplayDescription string `json:"display_description,omitempty"` +} + +// ToSnapshotCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "snapshot") +} + +// Create will create a new Snapshot based on the values in CreateOpts. To extract +// the Snapshot object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToSnapshotCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + return +} + // ListOpts holds options for listing Snapshots. It is passed to the // snapshots.List function. type ListOpts struct { @@ -66,8 +112,50 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat }) } +// Delete will delete an existing Snapshot with the given UUID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + // Get will get a single snapshot with given UUID func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToSnapshotUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Snapshot. This object is passed +// to the snapshot.Update function. For more information about the parameters, see +// the Snapshot object. +type UpdateOpts struct { + // Snapshot name. Manila snapshot update logic doesn't have a "name" alias. + DisplayName *string `json:"display_name,omitempty"` + // Snapshot description. Manila snapshot update logic doesn't have a "description" alias. + DisplayDescription *string `json:"display_description,omitempty"` +} + +// ToSnapshotUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "snapshot") +} + +// Update will update the Snapshot with provided information. To extract the updated +// Snapshot from the response, call the Extract method on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToSnapshotUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/sharedfilesystems/v2/snapshots/results.go b/openstack/sharedfilesystems/v2/snapshots/results.go index 41f474403b..4952dbd34a 100644 --- a/openstack/sharedfilesystems/v2/snapshots/results.go +++ b/openstack/sharedfilesystems/v2/snapshots/results.go @@ -70,6 +70,11 @@ func (r commonResult) Extract() (*Snapshot, error) { return s.Snapshot, err } +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + // SnapshotPage is a pagination.pager that is returned from a call to the List function. type SnapshotPage struct { pagination.MarkerPageBase @@ -150,11 +155,21 @@ func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { return s.Snapshots, 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 } +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + // IDFromName is a convenience function that returns a snapshot's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { r, err := ListDetail(client, &ListOpts{Name: name}).AllPages() diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go index dfeaa1b582..c02ef10c71 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures.go @@ -12,8 +12,112 @@ import ( const ( snapshotEndpoint = "/snapshots" snapshotID = "bc082e99-3bdb-4400-b95e-b85c7a41622c" + shareID = "19865c43-3b91-48c9-85a0-7ac4d6bb0efe" ) +var createRequest = `{ + "snapshot": { + "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", + "name": "test snapshot", + "description": "test description" + } +}` + +var createResponse = `{ + "snapshot": { + "status": "creating", + "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", + "description": "test description", + "links": [ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/9897f5ca-2559-4a4c-b761-d3439c0c9455", + "rel": "self" + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/9897f5ca-2559-4a4c-b761-d3439c0c9455", + "rel": "bookmark" + } + ], + "id": "bc082e99-3bdb-4400-b95e-b85c7a41622c", + "size": 1, + "user_id": "619e2ad074321cf246b03a89e95afee95fb26bb0b2d1fc7ba3bd30fcca25588a", + "name": "test snapshot", + "created_at": "2019-01-09T10:22:39.613550", + "share_proto": "NFS", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "share_size": 1 + } +}` + +// MockCreateResponse creates a mock response +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc(snapshotEndpoint, 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.StatusAccepted) + fmt.Fprintf(w, createResponse) + }) +} + +// MockDeleteResponse creates a mock delete response +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc(snapshotEndpoint+"/"+snapshotID, 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) + }) +} + +var updateRequest = `{ + "snapshot": { + "display_name": "my_new_test_snapshot", + "display_description": "" + } +}` + +var updateResponse = `{ + "snapshot": { + "status": "available", + "share_id": "19865c43-3b91-48c9-85a0-7ac4d6bb0efe", + "description": "", + "links": [ + { + "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/9897f5ca-2559-4a4c-b761-d3439c0c9455", + "rel": "self" + }, + { + "href": "http://172.18.198.54:8786/16e1ab15c35a457e9c2b2aa189f544e1/snapshots/9897f5ca-2559-4a4c-b761-d3439c0c9455", + "rel": "bookmark" + } + ], + "id": "9897f5ca-2559-4a4c-b761-d3439c0c9455", + "size": 1, + "user_id": "619e2ad074321cf246b03a89e95afee95fb26bb0b2d1fc7ba3bd30fcca25588a", + "name": "my_new_test_snapshot", + "created_at": "2019-01-09T10:22:39.613550", + "share_proto": "NFS", + "project_id": "16e1ab15c35a457e9c2b2aa189f544e1", + "share_size": 1 + } +}` + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc(snapshotEndpoint+"/"+snapshotID, 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, updateRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, updateResponse) + }) +} + var getResponse = `{ "snapshot": { "status": "available", diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go index d22c1dde73..eb588e5fd0 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go @@ -9,6 +9,52 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := &snapshots.CreateOpts{ShareID: shareID, Name: "test snapshot", Description: "test description"} + n, err := snapshots.Create(client.ServiceClient(), options).Extract() + + th.AssertNoErr(t, err) + th.AssertEquals(t, n.Name, "test snapshot") + th.AssertEquals(t, n.Description, "test description") + th.AssertEquals(t, n.ShareProto, "NFS") + th.AssertEquals(t, n.ShareSize, 1) + th.AssertEquals(t, n.Size, 1) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + name := "my_new_test_snapshot" + description := "" + options := &snapshots.UpdateOpts{ + DisplayName: &name, + DisplayDescription: &description, + } + n, err := snapshots.Update(client.ServiceClient(), snapshotID, options).Extract() + + th.AssertNoErr(t, err) + th.AssertEquals(t, n.Name, "my_new_test_snapshot") + th.AssertEquals(t, n.Description, "") +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + result := snapshots.Delete(client.ServiceClient(), snapshotID) + th.AssertNoErr(t, result.Err) +} + func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/sharedfilesystems/v2/snapshots/urls.go b/openstack/sharedfilesystems/v2/snapshots/urls.go index 6c27c42854..a07e3ec873 100644 --- a/openstack/sharedfilesystems/v2/snapshots/urls.go +++ b/openstack/sharedfilesystems/v2/snapshots/urls.go @@ -2,10 +2,22 @@ package snapshots import "github.com/gophercloud/gophercloud" +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("snapshots") +} + func listDetailURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots", "detail") } +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id) +} + func getURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id) } + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("snapshots", id) +} From 80c596f406a38975149db30d9ceddb774f3bfdb7 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 12 Jan 2019 05:32:49 +0300 Subject: [PATCH 0630/2296] Networking V2: add Agent Get request (#1391) * Networking V2: add Agents List request Add "networking/v2/extensions/agents" package with List request. Provide unit test and documentation. * Networking V2 Agents: rename admin_state_up field Rename "AdminStateUP" into "AdminStateUp". * Networking V2: add Agent Get request Add Get method for the "networking/v2/extensions/agents" package. Provide unit test and documentation. * Networking V2: add Agents basic acc test Add TestAgentsRead for testing reading of real Networking V2 agents. * Networking V2 Agents: fix Get unit test Rename "AdminStateUP" into "AdminStateUp" in TestGet() function. --- .../v2/extensions/agents/agents_test.go | 28 ++++ .../networking/v2/extensions/agents/doc.go | 2 + .../networking/v2/extensions/agents/doc.go | 32 +++++ .../v2/extensions/agents/requests.go | 66 +++++++++ .../v2/extensions/agents/results.go | 128 ++++++++++++++++++ .../v2/extensions/agents/testing/doc.go | 2 + .../v2/extensions/agents/testing/fixtures.go | 125 +++++++++++++++++ .../agents/testing/requests_test.go | 90 ++++++++++++ .../networking/v2/extensions/agents/urls.go | 21 +++ 9 files changed, 494 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/agents/agents_test.go create mode 100644 acceptance/openstack/networking/v2/extensions/agents/doc.go create mode 100644 openstack/networking/v2/extensions/agents/doc.go create mode 100644 openstack/networking/v2/extensions/agents/requests.go create mode 100644 openstack/networking/v2/extensions/agents/results.go create mode 100644 openstack/networking/v2/extensions/agents/testing/doc.go create mode 100644 openstack/networking/v2/extensions/agents/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/agents/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/agents/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go new file mode 100644 index 0000000000..972b784513 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -0,0 +1,28 @@ +// +build acceptance networking agents + +package agents + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestAgentsRead(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + allPages, err := agents.List(client, agents.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allAgents, err := agents.ExtractAgents(allPages) + th.AssertNoErr(t, err) + + for _, agent := range allAgents { + t.Logf("Retrieved Networking V2 agent: %s", agent.ID) + tools.PrintResource(t, agent) + } +} diff --git a/acceptance/openstack/networking/v2/extensions/agents/doc.go b/acceptance/openstack/networking/v2/extensions/agents/doc.go new file mode 100644 index 0000000000..0a76caef26 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/agents/doc.go @@ -0,0 +1,2 @@ +// agents acceptance tests +package agents diff --git a/openstack/networking/v2/extensions/agents/doc.go b/openstack/networking/v2/extensions/agents/doc.go new file mode 100644 index 0000000000..7706b730e5 --- /dev/null +++ b/openstack/networking/v2/extensions/agents/doc.go @@ -0,0 +1,32 @@ +/* +Package agents provides the ability to retrieve and manage Agents through the Neutron API. + +Example of Listing Agents + + listOpts := agents.ListOpts{ + AgentType: "Open vSwitch agent", + } + + allPages, err := agents.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allAgents, err := agents.ExtractAgents(allPages) + if err != nil { + panic(err) + } + + for _, agent := range allAgents { + fmt.Printf("%+v\n", agent) + } + +Example to Get an Agent + + agentID = "76af7b1f-d61b-4526-94f7-d2e14e2698df" + agent, err := agents.Get(networkClient, agentID).Extract() + if err != nil { + panic(err) + } +*/ +package agents diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go new file mode 100644 index 0000000000..e0e2a040a6 --- /dev/null +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -0,0 +1,66 @@ +package agents + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToAgentListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the Neutron API. Filtering is achieved by passing in struct field values +// that map to the agent attributes you want to see returned. +// SortKey allows you to sort by a particular agent attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for the pagination. +type ListOpts struct { + ID string `json:"id"` + AgentType string `json:"agent_type"` + Alive *bool `json:"alive"` + AvailabilityZone string `json:"availability_zone"` + Binary string `json:"binary"` + Description string `json:"description"` + Host string `json:"host"` + Topic string `json:"topic"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToAgentListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToAgentListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// agents. It accepts a ListOpts struct, which allows you to filter and +// sort the returned collection for greater efficiency. +// +// Default policy settings return only the agents owned by the project +// of the user submitting the request, unless the user has the administrative +// role. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToAgentListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return AgentPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves a specific agent based on its ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/agents/results.go b/openstack/networking/v2/extensions/agents/results.go new file mode 100644 index 0000000000..de4ce00646 --- /dev/null +++ b/openstack/networking/v2/extensions/agents/results.go @@ -0,0 +1,128 @@ +package agents + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts an agent resource. +func (r commonResult) Extract() (*Agent, error) { + var s struct { + Agent *Agent `json:"agent"` + } + err := r.ExtractInto(&s) + return s.Agent, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as an Agent. +type GetResult struct { + commonResult +} + +// Agent represents a Neutron agent. +type Agent struct { + // ID is the id of the agent. + ID string `json:"id"` + + // AdminStateUp is an administrative state of the agent. + AdminStateUp bool `json:"admin_state_up"` + + // AgentType is a type of the agent. + AgentType string `json:"agent_type"` + + // Alive indicates whether agent is alive or not. + Alive bool `json:"alive"` + + // AvailabilityZone is a zone of the agent. + AvailabilityZone string `json:"availability_zone"` + + // Binary is an executable binary of the agent. + Binary string `json:"binary"` + + // Configurations is a configuration specific key/value pairs that are + // determined by the agent binary and type. + Configurations map[string]interface{} `json:"configurations"` + + // CreatedAt is a creation timestamp. + CreatedAt time.Time `json:"-"` + + // StartedAt is a starting timestamp. + StartedAt time.Time `json:"-"` + + // HeartbeatTimestamp is a last heartbeat timestamp. + HeartbeatTimestamp time.Time `json:"-"` + + // Description contains agent description. + Description string `json:"description"` + + // Host is a hostname of the agent system. + Host string `json:"host"` + + // Topic contains name of AMQP topic. + Topic string `json:"topic"` +} + +// UnmarshalJSON helps to convert the timestamps into the time.Time type. +func (r *Agent) UnmarshalJSON(b []byte) error { + type tmp Agent + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` + StartedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"started_at"` + HeartbeatTimestamp gophercloud.JSONRFC3339ZNoTNoZ `json:"heartbeat_timestamp"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Agent(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.StartedAt = time.Time(s.StartedAt) + r.HeartbeatTimestamp = time.Time(s.HeartbeatTimestamp) + + return nil +} + +// AgentPage stores a single page of Agents from a List() API call. +type AgentPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of agent 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 AgentPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"agents_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty determines whether or not a AgentPage is empty. +func (r AgentPage) IsEmpty() (bool, error) { + agents, err := ExtractAgents(r) + return len(agents) == 0, err +} + +// ExtractAgents interprets the results of a single page from a List() +// API call, producing a slice of Agents structs. +func ExtractAgents(r pagination.Page) ([]Agent, error) { + var s struct { + Agents []Agent `json:"agents"` + } + err := (r.(AgentPage)).ExtractInto(&s) + return s.Agents, err +} diff --git a/openstack/networking/v2/extensions/agents/testing/doc.go b/openstack/networking/v2/extensions/agents/testing/doc.go new file mode 100644 index 0000000000..59460b8a64 --- /dev/null +++ b/openstack/networking/v2/extensions/agents/testing/doc.go @@ -0,0 +1,2 @@ +// agents unit tests +package testing diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures.go b/openstack/networking/v2/extensions/agents/testing/fixtures.go new file mode 100644 index 0000000000..e47ca79003 --- /dev/null +++ b/openstack/networking/v2/extensions/agents/testing/fixtures.go @@ -0,0 +1,125 @@ +package testing + +import ( + "time" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" +) + +// AgentsListResult represents raw response for the List request. +const AgentsListResult = ` +{ + "agents": [ + { + "admin_state_up": true, + "agent_type": "Open vSwitch agent", + "alive": true, + "availability_zone": null, + "binary": "neutron-openvswitch-agent", + "configurations": { + "datapath_type": "system", + "extensions": [ + "qos" + ] + }, + "created_at": "2017-07-26 23:15:44", + "description": null, + "heartbeat_timestamp": "2019-01-09 10:28:53", + "host": "compute1", + "id": "59186d7b-b512-4fdf-bbaf-5804ffde8811", + "started_at": "2018-06-26 21:46:19", + "topic": "N/A" + }, + { + "admin_state_up": true, + "agent_type": "Open vSwitch agent", + "alive": true, + "availability_zone": null, + "binary": "neutron-openvswitch-agent", + "configurations": { + "datapath_type": "system", + "extensions": [ + "qos" + ] + }, + "created_at": "2017-01-22 14:00:50", + "description": null, + "heartbeat_timestamp": "2019-01-09 10:28:50", + "host": "compute2", + "id": "76af7b1f-d61b-4526-94f7-d2e14e2698df", + "started_at": "2018-11-06 12:09:17", + "topic": "N/A" + } + ] +} +` + +// Agent1 represents first unmarshalled address scope from the +// AgentsListResult. +var Agent1 = agents.Agent{ + ID: "59186d7b-b512-4fdf-bbaf-5804ffde8811", + AdminStateUp: true, + AgentType: "Open vSwitch agent", + Alive: true, + Binary: "neutron-openvswitch-agent", + Configurations: map[string]interface{}{ + "datapath_type": "system", + "extensions": []interface{}{ + "qos", + }, + }, + CreatedAt: time.Date(2017, 7, 26, 23, 15, 44, 0, time.UTC), + StartedAt: time.Date(2018, 6, 26, 21, 46, 19, 0, time.UTC), + HeartbeatTimestamp: time.Date(2019, 1, 9, 10, 28, 53, 0, time.UTC), + Host: "compute1", + Topic: "N/A", +} + +// Agent2 represents second unmarshalled address scope from the +// AgentsListResult. +var Agent2 = agents.Agent{ + ID: "76af7b1f-d61b-4526-94f7-d2e14e2698df", + AdminStateUp: true, + AgentType: "Open vSwitch agent", + Alive: true, + Binary: "neutron-openvswitch-agent", + Configurations: map[string]interface{}{ + "datapath_type": "system", + "extensions": []interface{}{ + "qos", + }, + }, + CreatedAt: time.Date(2017, 1, 22, 14, 00, 50, 0, time.UTC), + StartedAt: time.Date(2018, 11, 6, 12, 9, 17, 0, time.UTC), + HeartbeatTimestamp: time.Date(2019, 1, 9, 10, 28, 50, 0, time.UTC), + Host: "compute2", + Topic: "N/A", +} + +// AgentsGetResult represents raw response for the Get request. +const AgentsGetResult = ` +{ + "agent": { + "binary": "neutron-openvswitch-agent", + "description": null, + "availability_zone": null, + "heartbeat_timestamp": "2019-01-09 11:43:01", + "admin_state_up": true, + "alive": true, + "id": "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", + "topic": "N/A", + "host": "compute3", + "agent_type": "Open vSwitch agent", + "started_at": "2018-06-26 21:46:20", + "created_at": "2017-07-26 23:02:05", + "configurations": { + "ovs_hybrid_plug": false, + "datapath_type": "system", + "vhostuser_socket_dir": "/var/run/openvswitch", + "log_agent_heartbeats": false, + "l2_population": true, + "enable_distributed_routing": false + } + } +} +` diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go new file mode 100644 index 0000000000..3ee2f84dac --- /dev/null +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -0,0 +1,90 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" + "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/agents", 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, AgentsListResult) + }) + + count := 0 + + agents.List(fake.ServiceClient(), agents.ListOpts{}).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{ + Agent1, + Agent2, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a", 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, AgentsGetResult) + }) + + s, err := agents.Get(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.ID, "43583cf5-472e-4dc8-af5b-6aed4c94ee3a") + th.AssertEquals(t, s.Binary, "neutron-openvswitch-agent") + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.Alive, true) + th.AssertEquals(t, s.Topic, "N/A") + th.AssertEquals(t, s.Host, "compute3") + th.AssertEquals(t, s.AgentType, "Open vSwitch agent") + th.AssertEquals(t, s.HeartbeatTimestamp, time.Date(2019, 1, 9, 11, 43, 01, 0, time.UTC)) + th.AssertEquals(t, s.StartedAt, time.Date(2018, 6, 26, 21, 46, 20, 0, time.UTC)) + th.AssertEquals(t, s.CreatedAt, time.Date(2017, 7, 26, 23, 2, 5, 0, time.UTC)) + th.AssertDeepEquals(t, s.Configurations, map[string]interface{}{ + "ovs_hybrid_plug": false, + "datapath_type": "system", + "vhostuser_socket_dir": "/var/run/openvswitch", + "log_agent_heartbeats": false, + "l2_population": true, + "enable_distributed_routing": false, + }) +} diff --git a/openstack/networking/v2/extensions/agents/urls.go b/openstack/networking/v2/extensions/agents/urls.go new file mode 100644 index 0000000000..33b84ee75f --- /dev/null +++ b/openstack/networking/v2/extensions/agents/urls.go @@ -0,0 +1,21 @@ +package agents + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "agents" + +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From f3e11158805c399b3aae2a980ed4a343b5b7424a Mon Sep 17 00:00:00 2001 From: Alvaro Aleman Date: Sat, 12 Jan 2019 20:57:00 +0100 Subject: [PATCH 0631/2296] ProviderClient: Dont panic (#1402) --- provider_client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/provider_client.go b/provider_client.go index 7455751dfa..e2d03243f6 100644 --- a/provider_client.go +++ b/provider_client.go @@ -3,6 +3,7 @@ package gophercloud import ( "bytes" "encoding/json" + "errors" "io" "io/ioutil" "net/http" @@ -192,7 +193,7 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) // io.ReadSeeker as-is. Default the content-type to application/json. if options.JSONBody != nil { if options.RawBody != nil { - panic("Please provide only one of JSONBody or RawBody to gophercloud.Request().") + return nil, errors.New("please provide only one of JSONBody or RawBody to gophercloud.Request()") } rendered, err := json.Marshal(options.JSONBody) From b8a40d168295758ab3dbb63759c8476a02750551 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 12 Jan 2019 21:16:48 +0100 Subject: [PATCH 0632/2296] Manila: add sorting into the manila messages list options (#1401) --- openstack/sharedfilesystems/v2/messages/requests.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openstack/sharedfilesystems/v2/messages/requests.go b/openstack/sharedfilesystems/v2/messages/requests.go index 2fad8e6da1..44e0d94bd1 100644 --- a/openstack/sharedfilesystems/v2/messages/requests.go +++ b/openstack/sharedfilesystems/v2/messages/requests.go @@ -34,6 +34,12 @@ type ListOpts struct { ResourceID string `q:"resource_id"` // The type of the resource for which the message was created ResourceType string `q:"resource_type"` + // The key to sort a list of messages + SortKey string `q:"sort_key"` + // The key to sort a list of messages + SortDir string `q:"sort_dir"` + // The maximum number of messages to return + Limit int `q:"limit"` } // ToMessageListQuery formats a ListOpts into a query string. From cc2dd4edd0609857e53ef6feba69f84e29f5b60f Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Mon, 14 Jan 2019 18:41:02 -0800 Subject: [PATCH 0633/2296] Senlin: Node Check (#1398) --- .../openstack/clustering/v1/nodes_test.go | 30 +++++++++++++++++++ openstack/clustering/v1/nodes/doc.go | 9 ++++++ openstack/clustering/v1/nodes/requests.go | 15 ++++++++++ .../clustering/v1/nodes/testing/fixtures.go | 11 +++++++ .../v1/nodes/testing/requests_test.go | 11 +++++++ 5 files changed, 76 insertions(+) diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index b283d9e4e4..5f65ce3893 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -183,3 +183,33 @@ func TestNodesRecover(t *testing.T) { tools.PrintResource(t, node) } } + +func TestNodeCheck(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + node, err := CreateNode(t, client, cluster.ID, profile.ID) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node.ID) + + t.Logf("Attempting to check on node: %s", node.ID) + + actionID, err := nodes.Check(client, node.ID).Extract() + th.AssertNoErr(t, err) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + node, err = nodes.Get(client, node.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "Check: Node is ACTIVE.", node.StatusReason) + tools.PrintResource(t, node) +} diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index ab09b51098..2bd644604b 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -98,5 +98,14 @@ Example to Recover a Node } fmt.Println("action=", actionID) +Example to Check a Node + + nodeID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + actionID, err := nodes.Check(serviceClient, nodeID).Extract() + if err != nil { + panic(err) + } + fmt.Println("action=", actionID) + */ package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index 773bf3df4e..a9ac890b96 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -238,3 +238,18 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r r.Header = result.Header return } + +func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { + b := map[string]interface{}{ + "check": map[string]interface{}{}, + } + + var result *http.Response + result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/nodes/testing/fixtures.go b/openstack/clustering/v1/nodes/testing/fixtures.go index cfad670a40..1f972276f6 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures.go +++ b/openstack/clustering/v1/nodes/testing/fixtures.go @@ -351,3 +351,14 @@ func HandleRecoverSuccessfully(t *testing.T) { fmt.Fprint(w, ActionResponse) }) } + +func HandleCheckSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/nodes/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-ID", "req-edce3528-864f-41fb-8759-f4707925cc09") + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, ActionResponse) + }) +} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index ba1cc9316d..154bfb4729 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -131,3 +131,14 @@ func TestNodeRecover(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } + +func TestNodeCheck(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCheckSuccessfully(t) + + actionID, err := nodes.Check(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, ExpectedActionID, actionID) +} From a9f90060ebd9d4e0be53e429d2623746b700468c Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Tue, 15 Jan 2019 16:04:18 +1300 Subject: [PATCH 0634/2296] Support tags for getting load balancer (#1405) --- openstack/loadbalancer/v2/loadbalancers/results.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 54c9caa859..916c3693a9 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -55,6 +55,10 @@ type LoadBalancer struct { // Pools are the pools related to this Loadbalancer. Pools []pools.Pool `json:"pools"` + + // Tags is a list of resource tags. Tags are arbitrarily defined strings + // attached to the resource. + Tags []string `json:"tags"` } // StatusTree represents the status of a loadbalancer. From 258a61a0642d95c00f84e38736d4e25ff329ed93 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Wed, 16 Jan 2019 16:25:14 +1300 Subject: [PATCH 0635/2296] Support to create lb with tags (#1406) --- .../openstack/loadbalancer/v2/loadbalancer.go | 3 +++ .../loadbalancer/v2/loadbalancers/doc.go | 1 + .../loadbalancer/v2/loadbalancers/requests.go | 3 +++ .../v2/loadbalancers/testing/fixtures.go | 20 ++++++++++++++----- .../v2/loadbalancers/testing/requests_test.go | 1 + 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index c8131207ac..d96b905db6 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -64,11 +64,13 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) + tags := []string{"test"} createOpts := loadbalancers.CreateOpts{ Name: lbName, Description: lbDescription, VipSubnetID: subnetID, AdminStateUp: gophercloud.Enabled, + Tags: tags, } lb, err := loadbalancers.Create(client, createOpts).Extract() @@ -89,6 +91,7 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI th.AssertEquals(t, lb.Description, lbDescription) th.AssertEquals(t, lb.VipSubnetID, subnetID) th.AssertEquals(t, lb.AdminStateUp, true) + th.AssertDeepEquals(t, lb.Tags, tags) return lb, nil } diff --git a/openstack/loadbalancer/v2/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go index 77cb8321b0..d474d6f70f 100644 --- a/openstack/loadbalancer/v2/loadbalancers/doc.go +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -31,6 +31,7 @@ Example to Create a Load Balancer VipAddress: "10.30.176.48", Flavor: "medium", Provider: "haproxy", + Tags: []string{"test", "stage"}, } lb, err := loadbalancers.Create(networkClient, createOpts).Extract() diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 2f821c55ed..ef46f25224 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -97,6 +97,9 @@ type CreateOpts struct { // The name of the provider. Provider string `json:"provider,omitempty"` + + // Tags is a set of resource tags. + Tags []string `json:"tags,omitempty"` } // ToLoadBalancerCreateMap builds a request body from CreateOpts. diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index 6b54c4850f..d81dacca2a 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -29,7 +29,8 @@ const LoadbalancersListBody = ` "provider": "haproxy", "admin_state_up": true, "provisioning_status": "ACTIVE", - "operating_status": "ONLINE" + "operating_status": "ONLINE", + "tags": ["test", "stage"] }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -43,7 +44,8 @@ const LoadbalancersListBody = ` "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", - "operating_status": "OFFLINE" + "operating_status": "OFFLINE", + "tags": ["test", "stage"] } ] } @@ -64,7 +66,8 @@ const SingleLoadbalancerBody = ` "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", - "operating_status": "OFFLINE" + "operating_status": "OFFLINE", + "tags": ["test", "stage"] } } ` @@ -84,7 +87,8 @@ const PostUpdateLoadbalancerBody = ` "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", - "operating_status": "OFFLINE" + "operating_status": "OFFLINE", + "tags": ["test", "stage"] } } ` @@ -98,6 +102,7 @@ const GetLoadbalancerStatusesBody = ` "name": "db_lb", "provisioning_status": "PENDING_UPDATE", "operating_status": "ACTIVE", + "tags": ["test", "stage"], "listeners": [{ "id": "db902c0c-d5ff-4753-b465-668ad9656918", "name": "db", @@ -152,6 +157,7 @@ var ( AdminStateUp: true, ProvisioningStatus: "ACTIVE", OperatingStatus: "ONLINE", + Tags: []string{"test", "stage"}, } LoadbalancerDb = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -166,6 +172,7 @@ var ( AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", + Tags: []string{"test", "stage"}, } LoadbalancerUpdated = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -180,6 +187,7 @@ var ( AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", + Tags: []string{"test", "stage"}, } LoadbalancerStatusesTree = loadbalancers.StatusTree{ Loadbalancer: &loadbalancers.LoadBalancer{ @@ -187,6 +195,7 @@ var ( Name: "db_lb", ProvisioningStatus: "PENDING_UPDATE", OperatingStatus: "ACTIVE", + Tags: []string{"test", "stage"}, Listeners: []listeners.Listener{{ ID: "db902c0c-d5ff-4753-b465-668ad9656918", Name: "db", @@ -253,7 +262,8 @@ func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { "vip_address": "10.30.176.48", "flavor": "medium", "provider": "haproxy", - "admin_state_up": true + "admin_state_up": true, + "tags": ["test", "stage"] } }`) diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 22a6555366..1f66c5d89f 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -65,6 +65,7 @@ func TestCreateLoadbalancer(t *testing.T) { VipAddress: "10.30.176.48", Flavor: "medium", Provider: "haproxy", + Tags: []string{"test", "stage"}, }).Extract() th.AssertNoErr(t, err) From 890f8679ad1c0470d7cd9f38a931720bd0411cfe Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 16 Jan 2019 21:25:35 +0100 Subject: [PATCH 0636/2296] Networking: add omitempty for fip port_id (#1411) --- .../v2/extensions/layer3/floatingips_test.go | 2 +- .../v2/extensions/layer3/floatingips/doc.go | 2 +- .../v2/extensions/layer3/floatingips/requests.go | 13 +++++++++++-- .../layer3/floatingips/testing/requests_test.go | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index 2d1f8d28c0..dd572da2e2 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -85,7 +85,7 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { // Disassociate the floating IP updateOpts := floatingips.UpdateOpts{ - PortID: nil, + PortID: new(string), } newFip, err = floatingips.Update(client, fip.ID, updateOpts).Extract() diff --git a/openstack/networking/v2/extensions/layer3/floatingips/doc.go b/openstack/networking/v2/extensions/layer3/floatingips/doc.go index bf5ec6807c..a71a3ec88a 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/doc.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/doc.go @@ -52,7 +52,7 @@ Example to Disassociate a Floating IP with a Port fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" updateOpts := floatingips.UpdateOpts{ - PortID: nil, + PortID: new(string), } fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract() diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 471ad1e8d9..77efef6869 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -123,13 +123,22 @@ type UpdateOptsBuilder interface { // ID. To disassociate the floating IP from all ports, provide an empty string. type UpdateOpts struct { Description *string `json:"description,omitempty"` - PortID *string `json:"port_id"` + PortID *string `json:"port_id,omitempty"` } // ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder // interface func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "floatingip") + b, err := gophercloud.BuildRequestBody(opts, "floatingip") + if err != nil { + return nil, err + } + + if m := b["floatingip"].(map[string]interface{}); m["port_id"] == "" { + m["port_id"] = nil + } + + return b, nil } // Update allows floating IP resources to be updated. Currently, the only way to diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index 89c028e8a7..cef20e1f62 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -393,7 +393,7 @@ func TestDisassociate(t *testing.T) { `) }) - ip, err := floatingips.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: nil}).Extract() + ip, err := floatingips.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: new(string)}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, "", ip.FixedIP) From e340f5f89555fed5511a8b8bbf454f0eaff74de7 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Thu, 17 Jan 2019 17:38:39 +1300 Subject: [PATCH 0637/2296] Octavia: Update load balancer tags (#1413) --- openstack/loadbalancer/v2/loadbalancers/doc.go | 8 +++----- openstack/loadbalancer/v2/loadbalancers/requests.go | 3 +++ .../loadbalancer/v2/loadbalancers/testing/fixtures.go | 7 ++++--- .../v2/loadbalancers/testing/requests_test.go | 2 ++ 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/openstack/loadbalancer/v2/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go index d474d6f70f..83cc6c44f4 100644 --- a/openstack/loadbalancer/v2/loadbalancers/doc.go +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -31,7 +31,7 @@ Example to Create a Load Balancer VipAddress: "10.30.176.48", Flavor: "medium", Provider: "haproxy", - Tags: []string{"test", "stage"}, + Tags: []string{"test", "stage"}, } lb, err := loadbalancers.Create(networkClient, createOpts).Extract() @@ -42,12 +42,10 @@ Example to Create a Load Balancer Example to Update a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - - i1001 := 1001 + name := "new-name" updateOpts := loadbalancers.UpdateOpts{ - Name: "new-name", + Name: &name, } - lb, err := loadbalancers.Update(networkClient, lbID, updateOpts).Extract() if err != nil { panic(err) diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index ef46f25224..aa0f53b398 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -145,6 +145,9 @@ type UpdateOpts struct { // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Tags is a set of resource tags. + Tags *[]string `json:"tags,omitempty"` } // ToLoadBalancerUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index d81dacca2a..73299e1d2a 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -88,7 +88,7 @@ const PostUpdateLoadbalancerBody = ` "admin_state_up": true, "provisioning_status": "PENDING_CREATE", "operating_status": "OFFLINE", - "tags": ["test", "stage"] + "tags": ["test"] } } ` @@ -187,7 +187,7 @@ var ( AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", OperatingStatus: "OFFLINE", - Tags: []string{"test", "stage"}, + Tags: []string{"test"}, } LoadbalancerStatusesTree = loadbalancers.StatusTree{ Loadbalancer: &loadbalancers.LoadBalancer{ @@ -314,7 +314,8 @@ func HandleLoadbalancerUpdateSuccessfully(t *testing.T) { th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "loadbalancer": { - "name": "NewLoadbalancerName" + "name": "NewLoadbalancerName", + "tags": ["test"] } }`) diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 1f66c5d89f..78798eaf8d 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -135,8 +135,10 @@ func TestUpdateLoadbalancer(t *testing.T) { client := fake.ServiceClient() name := "NewLoadbalancerName" + tags := []string{"test"} actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ Name: &name, + Tags: &tags, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From cc333bbb72434c93672ac0f63adc262744db1dd2 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Thu, 17 Jan 2019 20:37:40 -0800 Subject: [PATCH 0638/2296] Senlin: Cluster Collect Attributes Across A Cluster (#1409) * Senlin: Cluster Collect Attributes Across a Cluster * Renamed ToClusterCollectPath() to ToClusterCollectMap() Removed redundant check for nil for Collect() --- .../openstack/clustering/v1/clusters_test.go | 48 +++++++++++++++++++ openstack/clustering/v1/clusters/requests.go | 33 +++++++++++++ openstack/clustering/v1/clusters/results.go | 18 +++++++ .../v1/clusters/testing/fixtures.go | 27 +++++++++++ .../v1/clusters/testing/requests_test.go | 12 +++++ openstack/clustering/v1/clusters/urls.go | 4 ++ 6 files changed, 142 insertions(+) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 440625b85b..7e04917bce 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -400,3 +400,51 @@ func TestClustersReplaceNode(t *testing.T) { th.AssertEquals(t, false, strings.Contains(clusterNodes, nodeIDToBeReplaced)) tools.PrintResource(t, cluster) } + +func TestClustersCollectAttributes(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.2" + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, len(cluster.Nodes) > 0) + + _, err = CreateNode(t, client, cluster.ID, profile.ID) + th.AssertNoErr(t, err) + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, len(cluster.Nodes) > 0) + + for _, n := range cluster.Nodes { + defer DeleteNode(t, client, n) + } + + opts := clusters.CollectOpts{ + Path: "status", + } + attrs, err := clusters.Collect(client, cluster.ID, opts).Extract() + th.AssertNoErr(t, err) + for _, attr := range attrs { + th.AssertEquals(t, attr.Value, "ACTIVE") + } + + opts = clusters.CollectOpts{ + Path: "data.placement.zone", + } + attrs, err = clusters.Collect(client, cluster.ID, opts).Extract() + th.AssertNoErr(t, err) + for _, attr := range attrs { + th.AssertEquals(t, attr.Value, "nova") + } + +} diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 2952cf145e..731a3eda7c 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -589,3 +589,36 @@ func ReplaceNodes(client *gophercloud.ServiceClient, id string, opts ReplaceNode r.Header = result.Header return } + +type CollectOptsBuilder interface { + ToClusterCollectMap() (string, error) +} + +// CollectOpts represents options to collect attribute values across a cluster +type CollectOpts struct { + Path string `q:"path" required:"true"` +} + +func (opts CollectOpts) ToClusterCollectMap() (string, error) { + return opts.Path, nil +} + +// Collect instructs OpenStack to aggregate attribute values across a cluster +func Collect(client *gophercloud.ServiceClient, id string, opts CollectOptsBuilder) (r CollectResult) { + query, err := opts.ToClusterCollectMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Get(collectURL(client, id, query), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index b9d7aa2be8..8548d4bb4c 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -84,6 +84,11 @@ type ClusterPolicy struct { PolicyType string `json:"policy_type"` } +type ClusterAttributes struct { + ID string `json:"id"` + Value interface{} `json:"value"` +} + // Action represents an OpenStack Clustering action. type Action struct { Action string `json:"action"` @@ -181,6 +186,10 @@ func (r ActionResult) Extract() (string, error) { return s.Action, err } +type CollectResult struct { + gophercloud.Result +} + // ExtractClusters returns a slice of Clusters from the List operation. func ExtractClusters(r pagination.Page) ([]Cluster, error) { var s struct { @@ -199,3 +208,12 @@ func ExtractClusterPolicies(r pagination.Page) ([]ClusterPolicy, error) { err := (r.(ClusterPolicyPage)).ExtractInto(&s) return s.ClusterPolicies, err } + +// Extract returns collected attributes across a cluster +func (r CollectResult) Extract() ([]ClusterAttributes, error) { + var s struct { + Attributes []ClusterAttributes `json:"cluster_attributes"` + } + err := r.ExtractInto(&s) + return s.Attributes, err +} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index ed74bc48dd..4128e6581a 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -381,6 +381,22 @@ const GetPolicyResponse = ` } }` +const CollectResponse = ` +{ + "cluster_attributes": [{ + "id": "foo", + "value": "bar" + } + ] +}` + +var ExpectedCollectAttributes = []clusters.ClusterAttributes{ + { + ID: "foo", + Value: string("bar"), + }, +} + func HandleCreateClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") @@ -657,3 +673,14 @@ func HandleReplaceNodeSuccessfully(t *testing.T) { fmt.Fprint(w, ActionResponse) }) } + +func HandleClusterCollectSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/attrs/foo.bar", 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.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, CollectResponse) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 73782a24b2..94bc3d68b1 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -463,3 +463,15 @@ func TestReplaceNodes(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") } + +func TestClusterCollect(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleClusterCollectSuccessfully(t) + opts := clusters.CollectOpts{ + Path: "foo.bar", + } + attributes, err := clusters.Collect(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedCollectAttributes, attributes) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index c8d2664d06..15ee4473f7 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -48,3 +48,7 @@ func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID func nodeURL(client *gophercloud.ServiceClient, id string) string { return actionURL(client, id) } + +func collectURL(client *gophercloud.ServiceClient, clusterID string, path string) string { + return client.ServiceURL(apiVersion, apiName, clusterID, "attrs", path) +} From e102249148e2e09eb5efab07b7c3ea74c161e935 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 19 Jan 2019 04:36:13 +0100 Subject: [PATCH 0639/2296] Networking V2: omit empty ExternalFixedIP IPAddress for the router (#1415) --- .../v2/extensions/layer3/routers_test.go | 26 +++++++++++++++++++ .../v2/extensions/layer3/routers/results.go | 2 +- .../layer3/routers/testing/requests_test.go | 15 ++++++++--- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index f79aa5a3ec..2ec02f0230 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -72,11 +72,35 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { tools.PrintResource(t, router) + efi := []routers.ExternalFixedIP{} + for _, extIP := range router.GatewayInfo.ExternalFixedIPs { + efi = append(efi, + routers.ExternalFixedIP{ + IPAddress: extIP.IPAddress, + SubnetID: extIP.SubnetID, + }, + ) + } + // Add a new external router IP + efi = append(efi, + routers.ExternalFixedIP{ + SubnetID: router.GatewayInfo.ExternalFixedIPs[0].SubnetID, + }, + ) + + enableSNAT := true + gatewayInfo := routers.GatewayInfo{ + NetworkID: router.GatewayInfo.NetworkID, + EnableSNAT: &enableSNAT, + ExternalFixedIPs: efi, + } + newName := tools.RandomString("TESTACC-", 8) newDescription := "" updateOpts := routers.UpdateOpts{ Name: newName, Description: &newDescription, + GatewayInfo: &gatewayInfo, } _, err = routers.Update(client, router.ID, updateOpts).Extract() @@ -88,6 +112,8 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { tools.PrintResource(t, newRouter) th.AssertEquals(t, newRouter.Name, newName) th.AssertEquals(t, newRouter.Description, newDescription) + th.AssertEquals(t, *newRouter.GatewayInfo.EnableSNAT, enableSNAT) + th.AssertDeepEquals(t, newRouter.GatewayInfo.ExternalFixedIPs, efi) } func TestLayer3RouterInterface(t *testing.T) { diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index 765391445f..a6fa4a35ea 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -16,7 +16,7 @@ type GatewayInfo struct { // ExternalFixedIP is the IP address and subnet ID of the external gateway of a // router. type ExternalFixedIP struct { - IPAddress string `json:"ip_address"` + IPAddress string `json:"ip_address,omitempty"` SubnetID string `json:"subnet_id"` } diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 2f7b93d4b9..7bd1c4edf6 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -137,7 +137,10 @@ func TestCreate(t *testing.T) { "admin_state_up": false, "external_gateway_info":{ "enable_snat": false, - "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b" + "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b", + "external_fixed_ips": [ + {"subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] }, "availability_zone_hints": ["zone1", "zone2"] } @@ -171,9 +174,15 @@ func TestCreate(t *testing.T) { asu := false enableSNAT := false + efi := []routers.ExternalFixedIP{ + routers.ExternalFixedIP{ + SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def", + }, + } gwi := routers.GatewayInfo{ - NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", - EnableSNAT: &enableSNAT, + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + EnableSNAT: &enableSNAT, + ExternalFixedIPs: efi, } options := routers.CreateOpts{ Name: "foo_router", From 2625befdfbdc61bc34521c9909f2911255dcfc41 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Wed, 23 Jan 2019 19:45:58 -0800 Subject: [PATCH 0640/2296] Fix bug where messaging claims-create causes panic crash because HTTP POST encounters error and has no response object. (#1421) --- openstack/messaging/v2/claims/requests.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index 21492e527d..d3da9889df 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -56,6 +56,9 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts resp, r.Err = client.Post(url, b, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) + if r.Err != nil { + return + } // If the Claim has no content return an empty CreateResult if resp.StatusCode == 204 { r.Body = CreateResult{} From bb1ef8ce758cba00104a697a03ab07c77940f87e Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 25 Jan 2019 13:42:42 +0100 Subject: [PATCH 0641/2296] Auth: update application credential requirements logic (#1419) * Auth: update application credential requirements logic * Remove inline func --- auth_options.go | 38 ++++++++++++++++++++++++++++---------- openstack/auth_env.go | 24 ++++++++++++++++++++---- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/auth_options.go b/auth_options.go index f28c8d2adc..5ffa8d1e0a 100644 --- a/auth_options.go +++ b/auth_options.go @@ -185,7 +185,6 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // Populate the request structure based on the provided arguments. Create and return an error // if insufficient or incompatible information is present. var req request - var userRequest userReq if opts.Password == "" { if opts.TokenID != "" { @@ -229,25 +228,44 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s if opts.ApplicationCredentialSecret == "" { return nil, ErrAppCredMissingSecret{} } - // make sure that only one of DomainName or DomainID were provided - if opts.DomainID == "" && opts.DomainName == "" { - return nil, ErrDomainIDOrDomainName{} + + var userRequest *userReq + + if opts.UserID != "" { + // UserID could be used without the domain information + userRequest = &userReq{ + ID: &opts.UserID, + } } - req.Auth.Identity.Methods = []string{"application_credential"} - if opts.DomainID != "" { - userRequest = userReq{ + + if userRequest == nil && opts.Username == "" { + // Make sure that Username or UserID are provided + return nil, ErrUsernameOrUserID{} + } + + if userRequest == nil && opts.DomainID != "" { + userRequest = &userReq{ Name: &opts.Username, Domain: &domainReq{ID: &opts.DomainID}, } - } else if opts.DomainName != "" { - userRequest = userReq{ + } + + if userRequest == nil && opts.DomainName != "" { + userRequest = &userReq{ Name: &opts.Username, Domain: &domainReq{Name: &opts.DomainName}, } } + + // Make sure that DomainID or DomainName are provided among Username + if userRequest == nil { + return nil, ErrDomainIDOrDomainName{} + } + + req.Auth.Identity.Methods = []string{"application_credential"} req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ Name: &opts.ApplicationCredentialName, - User: &userRequest, + User: userRequest, Secret: &opts.ApplicationCredentialSecret, } } else { diff --git a/openstack/auth_env.go b/openstack/auth_env.go index 33c9aec441..0bb1f48375 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -59,11 +59,14 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { return nilOptions, err } - if username == "" && userID == "" { - err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ - EnvironmentVariables: []string{"OS_USERNAME", "OS_USERID"}, + if userID == "" && username == "" { + // Empty username and userID could be ignored, when applicationCredentialID and applicationCredentialSecret are set + if applicationCredentialID == "" && applicationCredentialSecret == "" { + err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ + EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, + } + return nilOptions, err } - return nilOptions, err } if password == "" && applicationCredentialID == "" && applicationCredentialName == "" { @@ -80,6 +83,19 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { return nilOptions, err } + if applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" { + if userID == "" && username == "" { + return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ + EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, + } + } + if username != "" && domainID == "" && domainName == "" { + return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ + EnvironmentVariables: []string{"OS_DOMAIN_ID", "OS_DOMAIN_NAME"}, + } + } + } + ao := gophercloud.AuthOptions{ IdentityEndpoint: authURL, UserID: userID, From 319dfe5a0439086c946dbafe7fafd4863847b719 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 26 Jan 2019 17:21:14 +0100 Subject: [PATCH 0642/2296] Docs: Adding Microversion documentation (#1320) --- docs/FAQ.md | 4 +++ docs/MICROVERSIONS.md | 79 +++++++++++++++++++++++++++++++++++++++++++ docs/STYLEGUIDE.md | 6 ++++ 3 files changed, 89 insertions(+) create mode 100644 docs/MICROVERSIONS.md diff --git a/docs/FAQ.md b/docs/FAQ.md index 88a366a288..4bc8a8a470 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -1,5 +1,9 @@ # Tips +## Handling Microversions + +Please see our dedicated document [here](MICROVERSIONS.md). + ## Implementing default logging and re-authentication attempts You can implement custom logging and/or limit re-auth attempts by creating a custom HTTP client diff --git a/docs/MICROVERSIONS.md b/docs/MICROVERSIONS.md new file mode 100644 index 0000000000..18b4d4a176 --- /dev/null +++ b/docs/MICROVERSIONS.md @@ -0,0 +1,79 @@ +# Microversions + +## Table of Contents + +* [Introduction](#introduction) +* [Client Configuration](#client-configuration) +* [Gophercloud Developer Information](#gophercloud-developer-information) +* [Application Developer Information](#application-developer-information) + +## Introduction + +Microversions are an OpenStack API ability which allows developers to add and +remove features while still retaining backwards compatibility for all prior +versions of the API. + +More information can be found here: + +> Note: these links are not an exhaustive reference for microversions. + +* http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html +* https://developer.openstack.org/api-guide/compute/microversions.html +* https://github.com/openstack/keystoneauth/blob/master/doc/source/using-sessions.rst + +## Client Configuration + +You can set a specific microversion on a Service Client by doing the following: + +```go +client, err := openstack.NewComputeV2(providerClient, nil) +client.Microversion = "2.52" +``` + +## Gophercloud Developer Information + +Microversions change several aspects about API interaction. + +### Existing Fields, New Values + +This is when an existing field behaves like an "enum" and a new valid value +is possible by setting the client's microversion to a specific version. + +An example of this can be seen with Nova/Compute's Server Group `policy` field +and the introduction of the [`soft-affinity`](https://developer.openstack.org/api-ref/compute/?expanded=create-server-group-detail#create-server-group) +value. + +Unless Gophercloud is limiting the valid values that are passed to the +Nova/Compute service, no changes are required in Gophercloud. + +### New Request Fields + +This is when a microversion enables a new field to be used in an API request. +When implementing this kind of change, it is imperative that the field has +the `omitempty` attribute set. If `omitempty` is not set, then the field will +be used for _all_ microversions and possibly cause an error from the API +service. You may need to use a pointer field in order for this to work. + +When adding a new field, please make sure to include a GoDoc comment about +what microversions the field is valid for. + +### New Response Fields + +This is when a microversion includes new fields in the API response. The +correct way of implementing this in Gophercloud is to _not_ add the field +to the resource's "result" struct (in the `results.go` file), but instead +add a custom "extract" method to a new `microversions.go` file. This is +to ensure that base API interaction does not break with the introduction +of new fields. + +### Modified Response Fields + +This is when a microversion modifies an existing field in an API response +to be formatted differently than the base API. Research is still ongoing +on how to best handle this scenario in Gophercloud. + +## Application Developer Information + +Gophercloud does not perform any validation checks on the API request to make +sure it is valid for a specific microversion. It is up to you to ensure that +the API request is using the correct fields and functions for the microversion. diff --git a/docs/STYLEGUIDE.md b/docs/STYLEGUIDE.md index 22a2900941..fb09b72f6a 100644 --- a/docs/STYLEGUIDE.md +++ b/docs/STYLEGUIDE.md @@ -55,6 +55,8 @@ - The following should be used in most cases: + - `microversions.go`: contains all the response methods for fields or actions + added in a microversion. - `requests.go`: contains all the functions that make HTTP requests and the types associated with the HTTP request (parameters for URL, body, etc) - `results.go`: contains all the response objects and their methods @@ -77,3 +79,7 @@ last parameter an `interface` named `OptsBuilder` (eg `ListOptsBuilder`). This `interface` should have at the least a method named `ToQuery` (eg `ToServerListQuery`). + +### Microversions + +- Please see our dedicated document [here](MICROVERSIONS.md). From c818fa66e4c88b30db28038fe3f18f2f4a0db9a8 Mon Sep 17 00:00:00 2001 From: Maciej Zimnoch Date: Sat, 26 Jan 2019 18:24:59 +0100 Subject: [PATCH 0643/2296] Block requests requiring authorization until reauth process finishes (#1408) * Block requests requiring authorization until reauth process finishes * Don't block AuthenticatedHeaders since it leads to deadlock on Reauth * Added IsThrowaway field to determine if provider client is tac --- openstack/client.go | 2 + provider_client.go | 38 +++++++++-- testing/provider_client_test.go | 116 +++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 10 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index 6d93af5b41..7a7a1803f1 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -150,6 +150,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once tac := *client + tac.IsThrowaway = true tac.ReauthFunc = nil tac.TokenID = "" tao := options @@ -206,6 +207,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once tac := *client + tac.IsThrowaway = true tac.ReauthFunc = nil tac.TokenID = "" var tao tokens3.AuthOptionsBuilder diff --git a/provider_client.go b/provider_client.go index e2d03243f6..f2aae976cf 100644 --- a/provider_client.go +++ b/provider_client.go @@ -72,6 +72,10 @@ type ProviderClient struct { // authentication functions for different Identity service versions. ReauthFunc func() error + // IsThrowaway determines whether if this client is a throw-away client. It's a copy of user's provider client + // with the token and reauth func zeroed. Such client can be used to perform reauthorization. + IsThrowaway bool + mut *sync.RWMutex reauthmut *reauthlock @@ -79,19 +83,23 @@ type ProviderClient struct { type reauthlock struct { sync.RWMutex - reauthing bool + reauthing bool + reauthingErr error + done *sync.Cond } // AuthenticatedHeaders returns a map of HTTP headers that are common for all -// authenticated service requests. +// authenticated service requests. Blocks if Reauthenticate is in progress. func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { + if client.IsThrowaway { + return + } if client.reauthmut != nil { - client.reauthmut.RLock() - if client.reauthmut.reauthing { - client.reauthmut.RUnlock() - return + client.reauthmut.Lock() + for client.reauthmut.reauthing { + client.reauthmut.done.Wait() } - client.reauthmut.RUnlock() + client.reauthmut.Unlock() } t := client.Token() if t == "" { @@ -140,11 +148,25 @@ func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { if client.mut == nil { return client.ReauthFunc() } + + client.reauthmut.Lock() + if client.reauthmut.reauthing { + for !client.reauthmut.reauthing { + client.reauthmut.done.Wait() + } + err = client.reauthmut.reauthingErr + client.reauthmut.Unlock() + return err + } + client.reauthmut.Unlock() + client.mut.Lock() defer client.mut.Unlock() client.reauthmut.Lock() client.reauthmut.reauthing = true + client.reauthmut.done = sync.NewCond(client.reauthmut) + client.reauthmut.reauthingErr = nil client.reauthmut.Unlock() if previousToken == "" || client.TokenID == previousToken { @@ -153,6 +175,8 @@ func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { client.reauthmut.Lock() client.reauthmut.reauthing = false + client.reauthmut.reauthingErr = err + client.reauthmut.done.Broadcast() client.reauthmut.Unlock() return } diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 24af155384..a7dbc5f437 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -43,9 +43,11 @@ func TestUserAgent(t *testing.T) { func TestConcurrentReauth(t *testing.T) { var info = struct { - numreauths int - mut *sync.RWMutex + numreauths int + failedAuths int + mut *sync.RWMutex }{ + 0, 0, new(sync.RWMutex), } @@ -59,12 +61,14 @@ func TestConcurrentReauth(t *testing.T) { p.UseTokenLock() p.SetToken(prereauthTok) p.ReauthFunc = func() error { + p.IsThrowaway = true time.Sleep(1 * time.Second) p.AuthenticatedHeaders() info.mut.Lock() info.numreauths++ info.mut.Unlock() p.TokenID = postreauthTok + p.IsThrowaway = false return nil } @@ -74,6 +78,9 @@ func TestConcurrentReauth(t *testing.T) { th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-Auth-Token") != postreauthTok { w.WriteHeader(http.StatusUnauthorized) + info.mut.Lock() + info.failedAuths++ + info.mut.Unlock() return } info.mut.RLock() @@ -147,8 +154,9 @@ func TestReauthEndLoop(t *testing.T) { info.maxReauthReached = true return fmt.Errorf("Max reauthentication attempts reached") } - + p.IsThrowaway = true p.AuthenticatedHeaders() + p.IsThrowaway = false info.reauthAttempts++ return nil @@ -196,3 +204,105 @@ func TestReauthEndLoop(t *testing.T) { th.AssertEquals(t, errAfter, 6) th.AssertEquals(t, errUnable, 14) } + +func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { + var info = struct { + numreauths int + failedAuths int + reauthCh chan struct{} + mut *sync.RWMutex + }{ + 0, + 0, + make(chan struct{}), + new(sync.RWMutex), + } + + numconc := 20 + + prereauthTok := client.TokenID + postreauthTok := "12345678" + + p := new(gophercloud.ProviderClient) + p.UseTokenLock() + p.SetToken(prereauthTok) + p.ReauthFunc = func() error { + info.mut.RLock() + if info.numreauths == 0 { + info.mut.RUnlock() + close(info.reauthCh) + time.Sleep(1 * time.Second) + } else { + info.mut.RUnlock() + } + p.IsThrowaway = true + p.AuthenticatedHeaders() + info.mut.Lock() + info.numreauths++ + info.mut.Unlock() + p.TokenID = postreauthTok + p.IsThrowaway = false + return nil + } + + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("X-Auth-Token") != postreauthTok { + info.mut.Lock() + info.failedAuths++ + info.mut.Unlock() + w.WriteHeader(http.StatusUnauthorized) + return + } + info.mut.RLock() + hasReauthed := info.numreauths != 0 + info.mut.RUnlock() + + if hasReauthed { + th.CheckEquals(t, p.Token(), postreauthTok) + } + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, `{}`) + }) + + wg := new(sync.WaitGroup) + reqopts := new(gophercloud.RequestOpts) + reqopts.MoreHeaders = map[string]string{ + "X-Auth-Token": prereauthTok, + } + + for i := 0; i < numconc; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + if i != 0 { + <-info.reauthCh + } + resp, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) + th.CheckNoErr(t, err) + if resp == nil { + t.Errorf("got a nil response") + return + } + if resp.Body == nil { + t.Errorf("response body was nil") + return + } + defer resp.Body.Close() + actual, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("error reading response body: %s", err) + return + } + th.CheckByteArrayEquals(t, []byte(`{}`), actual) + }(i) + } + + wg.Wait() + + th.AssertEquals(t, 1, info.numreauths) + th.AssertEquals(t, 1, info.failedAuths) +} From decc9fbb4ecfb100d850d2e043ac848973316c69 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Tue, 29 Jan 2019 04:07:41 -0800 Subject: [PATCH 0644/2296] Senlin: Cluster Operations (#1423) --- .../openstack/clustering/v1/clusters_test.go | 60 ++++++++++++++++ openstack/clustering/v1/clusters/doc.go | 26 +++++++ openstack/clustering/v1/clusters/requests.go | 70 +++++++++++++++++++ .../v1/clusters/testing/fixtures.go | 19 +++++ .../v1/clusters/testing/requests_test.go | 16 +++++ openstack/clustering/v1/clusters/urls.go | 4 ++ 6 files changed, 195 insertions(+) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 7e04917bce..8bc02f041e 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -448,3 +449,62 @@ func TestClustersCollectAttributes(t *testing.T) { } } + +// Performs an operation on a cluster +func TestClustersOps(t *testing.T) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.4" + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + node, err := CreateNode(t, client, cluster.ID, profile.ID) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node.ID) + + ops := []clusters.OperationOpts{ + // 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.RebootOperation, Params: clusters.OperationParams{"type": "SOFT"}}, + {Operation: clusters.ChangePasswordOperation, Params: clusters.OperationParams{"admin_pass": "test"}}, + {Operation: clusters.LockOperation}, + {Operation: clusters.UnlockOperation}, + {Operation: clusters.SuspendOperation}, + {Operation: clusters.ResumeOperation}, + {Operation: clusters.RescueOperation, Params: clusters.OperationParams{"image_ref": choices.ImageID}}, + {Operation: clusters.PauseOperation}, + {Operation: clusters.UnpauseOperation}, + {Operation: clusters.StopOperation}, + {Operation: clusters.StartOperation}, + } + + for _, op := range ops { + opName := string(op.Operation) + t.Logf("Attempting to perform '%s' on cluster: %s", opName, cluster.ID) + actionID, res := clusters.Ops(client, cluster.ID, op).Extract() + th.AssertNoErr(t, res) + + err = WaitForAction(client, actionID) + th.AssertNoErr(t, err) + + action, err := actions.Get(client, actionID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "SUCCEEDED", action.Status) + + t.Logf("Successfully performed '%s' on cluster: %s", opName, cluster.ID) + } + + cluster, err = clusters.Get(client, cluster.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, cluster) +} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index a516cbff2b..7d28ac5945 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -257,5 +257,31 @@ Example to replace nodes for a cluster panic(err) } +Example to collect node attributes across a cluster + + serviceClient.Microversion = "1.2" + clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" + opts := clusters.CollectOpts{ + Path: "status", + } + attrs, err := clusters.Collect(serviceClient, clusterID, opts).Extract() + if err != nil { + panic(err) + } + +Example to perform an operation on a cluster + + serviceClient.Microversion = "1.4" + clusterID := "cluster123" + operationOpts := clusters.OperationOpts{ + Operation: clusters.RebootOperation, + Filters: clusters.OperationFilters{"role": "slave"}, + Params: clusters.OperationParams{"type": "SOFT"}, + } + actionID, err := clusters.Ops(serviceClient, clusterID, operationOpts).Extract() + if err != nil { + panic(err) + } + */ package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 731a3eda7c..43312ed4d3 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -622,3 +622,73 @@ func Collect(client *gophercloud.ServiceClient, id string, opts CollectOptsBuild return } + +// OperationName represents valid values for cluster operation +type OperationName string + +const ( + // Nova Profile Op Names + RebootOperation OperationName = "reboot" + RebuildOperation OperationName = "rebuild" + ChangePasswordOperation OperationName = "change_password" + PauseOperation OperationName = "pause" + UnpauseOperation OperationName = "unpause" + SuspendOperation OperationName = "suspend" + ResumeOperation OperationName = "resume" + LockOperation OperationName = "lock" + UnlockOperation OperationName = "unlock" + StartOperation OperationName = "start" + StopOperation OperationName = "stop" + RescueOperation OperationName = "rescue" + UnrescueOperation OperationName = "unrescue" + EvacuateOperation OperationName = "evacuate" + + // Heat Pofile Op Names + AbandonOperation OperationName = "abandon" +) + +// ToClusterOperationMap constructs a request body from OperationOpts. +func (opts OperationOpts) ToClusterOperationMap() (map[string]interface{}, error) { + operationArg := struct { + Filters OperationFilters `json:"filters,omitempty"` + Params OperationParams `json:"params,omitempty"` + }{ + Filters: opts.Filters, + Params: opts.Params, + } + + return gophercloud.BuildRequestBody(operationArg, string(opts.Operation)) +} + +// OperationOptsBuilder allows extensions to add additional parameters to the +// Op request. +type OperationOptsBuilder interface { + ToClusterOperationMap() (map[string]interface{}, error) +} +type OperationFilters map[string]interface{} +type OperationParams map[string]interface{} + +// OperationOpts represents options used to perform an operation on a cluster +type OperationOpts struct { + Operation OperationName `json:"operation" required:"true"` + Filters OperationFilters `json:"filters,omitempty"` + Params OperationParams `json:"params,omitempty"` +} + +func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { + b, err := opts.ToClusterOperationMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/clustering/v1/clusters/testing/fixtures.go b/openstack/clustering/v1/clusters/testing/fixtures.go index 4128e6581a..3d47671ef1 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures.go +++ b/openstack/clustering/v1/clusters/testing/fixtures.go @@ -342,6 +342,13 @@ const ActionResponse = ` const ExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" +const OperationActionResponse = ` +{ + "action": "2a0ff107-e789-4660-a122-3816c43af703" +}` + +const OperationExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" + const ListPoliciesResult = `{ "cluster_policies": [ { @@ -684,3 +691,15 @@ func HandleClusterCollectSuccessfully(t *testing.T) { fmt.Fprint(w, CollectResponse) }) } + +func HandleOpsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/ops", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprint(w, OperationActionResponse) + }) +} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 94bc3d68b1..1de7161a4b 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -475,3 +475,19 @@ func TestClusterCollect(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCollectAttributes, attributes) } + +func TestOperation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleOpsSuccessfully(t) + + clusterOpts := clusters.OperationOpts{ + Operation: clusters.PauseOperation, + Filters: clusters.OperationFilters{"role": "slave"}, + Params: clusters.OperationParams{"type": "soft"}, + } + actual, err := clusters.Ops(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", clusterOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, OperationExpectedActionID, actual) +} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index 15ee4473f7..ea1fa0d52c 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -52,3 +52,7 @@ func nodeURL(client *gophercloud.ServiceClient, id string) string { func collectURL(client *gophercloud.ServiceClient, clusterID string, path string) string { return client.ServiceURL(apiVersion, apiName, clusterID, "attrs", path) } + +func opsURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "ops") +} From cc9c99918988100699c8ec6f0d7a73dfa5b71fda Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Wed, 30 Jan 2019 23:51:14 +1300 Subject: [PATCH 0645/2296] Octavia: filter load balancer by tags (#1424) --- .../openstack/loadbalancer/v2/loadbalancer.go | 12 ++-- .../loadbalancer/v2/loadbalancers_test.go | 69 ++++++++++++++++++- .../loadbalancer/v2/loadbalancers/requests.go | 36 +++++----- 3 files changed, 95 insertions(+), 22 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index d96b905db6..26a5c961df 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -58,19 +58,20 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal // 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) (*loadbalancers.LoadBalancer, error) { +func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string) (*loadbalancers.LoadBalancer, error) { lbName := tools.RandomString("TESTACCT-", 8) lbDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) - tags := []string{"test"} createOpts := loadbalancers.CreateOpts{ Name: lbName, Description: lbDescription, VipSubnetID: subnetID, AdminStateUp: gophercloud.Enabled, - Tags: tags, + } + if len(tags) > 0 { + createOpts.Tags = tags } lb, err := loadbalancers.Create(client, createOpts).Extract() @@ -91,7 +92,10 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI th.AssertEquals(t, lb.Description, lbDescription) th.AssertEquals(t, lb.VipSubnetID, subnetID) th.AssertEquals(t, lb.AdminStateUp, true) - th.AssertDeepEquals(t, lb.Tags, tags) + + if len(tags) > 0 { + th.AssertDeepEquals(t, lb.Tags, tags) + } return lb, nil } diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 4db772104c..e4bae39582 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -31,6 +31,69 @@ func TestLoadbalancersList(t *testing.T) { } } +func TestLoadbalancersListByTags(t *testing.T) { + netClient, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + lbClient, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + network, err := networking.CreateNetwork(t, netClient) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, netClient, network.ID) + + subnet, err := networking.CreateSubnet(t, netClient, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, netClient, subnet.ID) + + // 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) + th.AssertNoErr(t, err) + defer DeleteLoadBalancer(t, lbClient, lb.ID) + + tags = []string{"tag1"} + listOpts := loadbalancers.ListOpts{ + Tags: tags, + } + allPages, err := loadbalancers.List(lbClient, listOpts).AllPages() + th.AssertNoErr(t, err) + allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(allLoadbalancers)) + + tags = []string{"test"} + listOpts = loadbalancers.ListOpts{ + TagsNot: tags, + } + allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() + th.AssertNoErr(t, err) + allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(allLoadbalancers)) + + tags = []string{"tag1", "tag3"} + listOpts = loadbalancers.ListOpts{ + TagsAny: tags, + } + allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() + th.AssertNoErr(t, err) + allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(allLoadbalancers)) + + tags = []string{"tag1", "test"} + listOpts = loadbalancers.ListOpts{ + TagsNotAny: tags, + } + allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() + th.AssertNoErr(t, err) + allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(allLoadbalancers)) +} + func TestLoadbalancersCRUD(t *testing.T) { netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -46,7 +109,8 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) - lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) + tags := []string{"test"} + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags) th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, lbClient, lb.ID) @@ -336,7 +400,8 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) - lb, err := CreateLoadBalancer(t, lbClient, subnet.ID) + tags := []string{"test"} + 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 aa0f53b398..e9147350da 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -17,22 +17,26 @@ type ListOptsBuilder interface { // sort by a particular attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - Description string `q:"description"` - AdminStateUp *bool `q:"admin_state_up"` - ProjectID string `q:"project_id"` - ProvisioningStatus string `q:"provisioning_status"` - VipAddress string `q:"vip_address"` - VipPortID string `q:"vip_port_id"` - VipSubnetID string `q:"vip_subnet_id"` - ID string `q:"id"` - OperatingStatus string `q:"operating_status"` - Name string `q:"name"` - Flavor string `q:"flavor"` - Provider string `q:"provider"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` + Description string `q:"description"` + AdminStateUp *bool `q:"admin_state_up"` + ProjectID string `q:"project_id"` + ProvisioningStatus string `q:"provisioning_status"` + VipAddress string `q:"vip_address"` + VipPortID string `q:"vip_port_id"` + VipSubnetID string `q:"vip_subnet_id"` + ID string `q:"id"` + OperatingStatus string `q:"operating_status"` + Name string `q:"name"` + Flavor string `q:"flavor"` + Provider string `q:"provider"` + 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"` + TagsNot []string `q:"not-tags"` + TagsNotAny []string `q:"not-tags-any"` } // ToLoadBalancerListQuery formats a ListOpts into a query string. From bd754a358bac4c1f48ff6b8d794fbf732883d879 Mon Sep 17 00:00:00 2001 From: "Iskander (Alex) Sharipov" Date: Sat, 2 Feb 2019 19:12:49 +0300 Subject: [PATCH 0646/2296] fix strings.Contains args order (#1433) Swapped the arguments order, since the original code was looking suspicious. Signed-off-by: Iskander Sharipov --- acceptance/openstack/compute/v2/servers_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index bd7c8af767..872294aaea 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -118,7 +118,7 @@ func TestServersWithoutImageRef(t *testing.T) { server, err := CreateServerWithoutImageRef(t, client) if err != nil { if err400, ok := err.(*gophercloud.ErrUnexpectedResponseCode); ok { - if !strings.Contains("Missing imageRef attribute", string(err400.Body)) { + if !strings.Contains(string(err400.Body), "Missing imageRef attribute") { defer DeleteServer(t, client, server) } } From 4b050c030d2f0ef577141e9cbaa41fa7ba225832 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Sat, 2 Feb 2019 09:34:15 -0800 Subject: [PATCH 0647/2296] Senlin: Events Get/List (#1430) --- .../openstack/clustering/v1/events_test.go | 31 +++++ openstack/clustering/v1/events/doc.go | 33 +++++ openstack/clustering/v1/events/requests.go | 53 +++++++ openstack/clustering/v1/events/results.go | 66 +++++++++ openstack/clustering/v1/events/testing/doc.go | 2 + .../clustering/v1/events/testing/fixtures.go | 131 ++++++++++++++++++ .../v1/events/testing/requests_test.go | 45 ++++++ openstack/clustering/v1/events/urls.go | 22 +++ 8 files changed, 383 insertions(+) create mode 100644 acceptance/openstack/clustering/v1/events_test.go create mode 100644 openstack/clustering/v1/events/doc.go create mode 100644 openstack/clustering/v1/events/requests.go create mode 100644 openstack/clustering/v1/events/results.go create mode 100644 openstack/clustering/v1/events/testing/doc.go create mode 100644 openstack/clustering/v1/events/testing/fixtures.go create mode 100644 openstack/clustering/v1/events/testing/requests_test.go create mode 100644 openstack/clustering/v1/events/urls.go diff --git a/acceptance/openstack/clustering/v1/events_test.go b/acceptance/openstack/clustering/v1/events_test.go new file mode 100644 index 0000000000..447fecd54e --- /dev/null +++ b/acceptance/openstack/clustering/v1/events_test.go @@ -0,0 +1,31 @@ +// +build acceptance clustering events + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestEventsList(t *testing.T) { + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + opts := events.ListOpts{ + Limit: 200, + } + + allPages, err := events.List(client, opts).AllPages() + th.AssertNoErr(t, err) + + allEvents, err := events.ExtractEvents(allPages) + th.AssertNoErr(t, err) + + for _, event := range allEvents { + tools.PrintResource(t, event) + } +} diff --git a/openstack/clustering/v1/events/doc.go b/openstack/clustering/v1/events/doc.go new file mode 100644 index 0000000000..05db559001 --- /dev/null +++ b/openstack/clustering/v1/events/doc.go @@ -0,0 +1,33 @@ +/* +Package events provides listing and retrieving of senlin events for the +OpenStack Clustering Service. + +Example to List Events + + opts := events.ListOpts{ + Limit: 5, + } + + err = events.List(serviceClient, opts).EachPage(func(page pagination.Page) (bool, error) { + eventInfos, err := events.ExtractEvents(page) + if err != nil { + return false, err + } + + for _, eventInfo := range eventInfos { + fmt.Println("%+v\n", eventInfo) + } + return true, nil + }) + +Example to Get an Event + + eventID := "edce3528-864f-41fb-8759-f4707925cc09" + event, err := events.Get(serviceClient, eventID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("Event %+v: ", event) +*/ +package events diff --git a/openstack/clustering/v1/events/requests.go b/openstack/clustering/v1/events/requests.go new file mode 100644 index 0000000000..c962122ef8 --- /dev/null +++ b/openstack/clustering/v1/events/requests.go @@ -0,0 +1,53 @@ +package events + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToEventListQuery() (string, error) +} + +// ListOpts represents options used to list events. +type ListOpts struct { + Limit int `q:"limit,omitempty"` + Level int `q:"level,omitempty"` + Marker string `q:"marker,omitempty"` + Sort string `q:"sort,omitempty"` + GlobalProject *bool `q:"global_project,omitempty"` + OID string `q:"oid,omitempty"` + OType string `q:"otype,omitempty"` + OName string `q:"oname,omitempty"` + ClusterID string `q:"cluster_id,omitempty"` + Action string `q:"action"` +} + +// ToEventListQuery builds a query string from ListOpts. +func (opts ListOpts) ToEventListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of events. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToEventListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return EventPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details of a single event. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + return +} diff --git a/openstack/clustering/v1/events/results.go b/openstack/clustering/v1/events/results.go new file mode 100644 index 0000000000..b8542d2685 --- /dev/null +++ b/openstack/clustering/v1/events/results.go @@ -0,0 +1,66 @@ +package events + +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Event represents a detailed Event. +type Event struct { + Action string `json:"action"` + Cluster string `json:"cluster"` + ClusterID string `json:"cluster_id"` + ID string `json:"id"` + Level string `json:"level"` + Metadata map[string]interface{} `json:"meta_data"` + OID string `json:"oid"` + OName string `json:"oname"` + OType string `json:"otype"` + Project string `json:"project"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + Timestamp time.Time `json:"timestamp"` + User string `json:"user"` +} + +// commonResult is the response of a base result. +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult-based result as an Event. +func (r commonResult) Extract() (*Event, error) { + var s struct { + Event *Event `json:"event"` + } + err := r.ExtractInto(&s) + return s.Event, err +} + +// GetResult is the response of a Get operations. Call its Extract method to +// interpret it as an Event. +type GetResult struct { + commonResult +} + +// EventPage contains a single page of all events from a List call. +type EventPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a EventPage contains any results. +func (r EventPage) IsEmpty() (bool, error) { + events, err := ExtractEvents(r) + return len(events) == 0, err +} + +// ExtractEvents returns a slice of Events from the List operation. +func ExtractEvents(r pagination.Page) ([]Event, error) { + var s struct { + Events []Event `json:"events"` + } + err := (r.(EventPage)).ExtractInto(&s) + return s.Events, err +} diff --git a/openstack/clustering/v1/events/testing/doc.go b/openstack/clustering/v1/events/testing/doc.go new file mode 100644 index 0000000000..93a668d499 --- /dev/null +++ b/openstack/clustering/v1/events/testing/doc.go @@ -0,0 +1,2 @@ +// clustering_events_v1 +package testing diff --git a/openstack/clustering/v1/events/testing/fixtures.go b/openstack/clustering/v1/events/testing/fixtures.go new file mode 100644 index 0000000000..6e6b8fd33f --- /dev/null +++ b/openstack/clustering/v1/events/testing/fixtures.go @@ -0,0 +1,131 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ListResponse = ` +{ + "events": [ + { + "action": "CLUSTER_CREATE", + "cluster": null, + "cluster_id": null, + "id": "edce3528-864f-41fb-8759-f4707925cc09", + "level": "INFO", + "meta_data": {}, + "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", + "oname": "cluster001", + "otype": "CLUSTER", + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "status": "start", + "status_reason": "Initializing", + "timestamp": "2015-03-05T08:53:15Z", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + }, + { + "action": "NODE_DELETE", + "cluster": null, + "cluster_id": null, + "id": "abcd1234-864f-41fb-8759-f4707925dd10", + "level": "INFO", + "meta_data": {}, + "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", + "oname": "node119", + "otype": "node", + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "status": "start", + "status_reason": "84492c96", + "timestamp": "2015-03-06T18:53:15Z", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + } + ] +} +` + +const GetResponse = ` +{ + "event": { + "action": "CLUSTER_CREATE", + "cluster_id": null, + "id": "edce3528-864f-41fb-8759-f4707925cc09", + "level": "INFO", + "meta_data": {}, + "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", + "oname": "cluster001", + "otype": "CLUSTER", + "project": "f1fe61dcda2f4618a14c10dc7abc214d", + "status": "start", + "status_reason": "Initializing", + "timestamp": "2015-03-05T08:53:15Z", + "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" + } +} +` + +var ExpectedEvent1 = events.Event{ + Action: "CLUSTER_CREATE", + Cluster: "", + ClusterID: "", + ID: "edce3528-864f-41fb-8759-f4707925cc09", + Level: "INFO", + Metadata: map[string]interface{}{}, + OID: "0df0931b-e251-4f2e-8719-4ebfda3627ba", + OName: "cluster001", + OType: "CLUSTER", + Project: "f1fe61dcda2f4618a14c10dc7abc214d", + Status: "start", + StatusReason: "Initializing", + Timestamp: time.Date(2015, 3, 5, 8, 53, 15, 0, time.UTC), + User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", +} + +var ExpectedEvent2 = events.Event{ + Action: "NODE_DELETE", + Cluster: "", + ClusterID: "", + ID: "abcd1234-864f-41fb-8759-f4707925dd10", + Level: "INFO", + Metadata: map[string]interface{}{}, + OID: "0df0931b-e251-4f2e-8719-4ebfda3627ba", + OName: "node119", + OType: "node", + Project: "f1fe61dcda2f4618a14c10dc7abc214d", + Status: "start", + StatusReason: "84492c96", + Timestamp: time.Date(2015, 3, 6, 18, 53, 15, 0, time.UTC), + User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", +} + +var ExpectedEvents = []events.Event{ExpectedEvent1, ExpectedEvent2} + +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/events", 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, ListResponse) + }) +} + +func HandleGetSuccessfully(t *testing.T, id string) { + th.Mux.HandleFunc("/v1/events/"+id, 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, GetResponse) + }) +} diff --git a/openstack/clustering/v1/events/testing/requests_test.go b/openstack/clustering/v1/events/testing/requests_test.go new file mode 100644 index 0000000000..e06d28f205 --- /dev/null +++ b/openstack/clustering/v1/events/testing/requests_test.go @@ -0,0 +1,45 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" + + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListEvents(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleListSuccessfully(t) + + pageCount := 0 + err := events.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + pageCount++ + actual, err := events.ExtractEvents(page) + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, ExpectedEvents, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if pageCount != 1 { + t.Errorf("Expected 1 page, got %d", pageCount) + } +} + +func TestGetEvent(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetSuccessfully(t, ExpectedEvent1.ID) + + actual, err := events.Get(fake.ServiceClient(), ExpectedEvent1.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedEvent1, *actual) +} diff --git a/openstack/clustering/v1/events/urls.go b/openstack/clustering/v1/events/urls.go new file mode 100644 index 0000000000..65d9c1a8f4 --- /dev/null +++ b/openstack/clustering/v1/events/urls.go @@ -0,0 +1,22 @@ +package events + +import "github.com/gophercloud/gophercloud" + +var apiVersion = "v1" +var apiName = "events" + +func commonURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiVersion, apiName) +} + +func listURL(client *gophercloud.ServiceClient) string { + return commonURL(client) +} + +func idURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return idURL(client, id) +} From 660c5458a954e9b66e25871703cf727de9d4aaff Mon Sep 17 00:00:00 2001 From: itsc0lin <43658752+itsc0lin@users.noreply.github.com> Date: Sat, 2 Feb 2019 14:18:54 -0800 Subject: [PATCH 0648/2296] Add PROXY protocol for listener/pool requests to LBaaS v2 (#1323) --- openstack/loadbalancer/v2/listeners/requests.go | 1 + openstack/loadbalancer/v2/pools/requests.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 2c5139986e..005bd1e79a 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -12,6 +12,7 @@ type Protocol string const ( ProtocolTCP Protocol = "TCP" ProtocolUDP Protocol = "UDP" + ProtocolPROXY Protocol = "PROXY" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 9a10b6dcd0..fbf16dc288 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -67,6 +67,7 @@ const ( ProtocolTCP Protocol = "TCP" ProtocolUDP Protocol = "UDP" + ProtocolPROXY Protocol = "PROXY" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" ) @@ -86,7 +87,7 @@ type CreateOpts struct { LBMethod LBMethod `json:"lb_algorithm" required:"true"` // The protocol used by the pool members, you can use either - // ProtocolTCP, ProtocolUDP, ProtocolHTTP, or ProtocolHTTPS. + // ProtocolTCP, ProtocolUDP, ProtocolPROXY, ProtocolHTTP, or ProtocolHTTPS. Protocol Protocol `json:"protocol" required:"true"` // The Loadbalancer on which the members of the pool will be associated with. From 5632e563e44d7b0c02359fbc16902043dc45ee3f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 3 Feb 2019 11:31:58 -0700 Subject: [PATCH 0649/2296] Compute v2: Servers Tags (#1115) * Core: Adding Microversion helper functions * Compute v2: Add Tags to CreateOpts * Compute v2: Add ability to extract server tags * Removing Tags type * Server Tags acceptance test * Core: Removing Microversion helper functions --- acceptance/openstack/compute/v2/compute.go | 61 +++++++++++ .../openstack/compute/v2/servers_test.go | 39 +++++-- openstack/compute/v2/servers/microversions.go | 11 ++ openstack/compute/v2/servers/requests.go | 4 + .../compute/v2/servers/testing/fixtures.go | 102 ++++++++++++++++++ .../v2/servers/testing/requests_test.go | 26 +++++ 6 files changed, 233 insertions(+), 10 deletions(-) create mode 100644 openstack/compute/v2/servers/microversions.go diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index dfde54ae39..6d750c66ec 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -29,6 +29,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" + "golang.org/x/crypto/ssh" ) @@ -529,6 +530,66 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient return server, nil } +// CreateServerWithTags creates a basic instance with a randomly generated name. +// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. +// The image will be the value of the OS_IMAGE_ID environment variable. +// The instance will be launched on the network specified in OS_NETWORK_NAME. +// Two tags will be assigned to the server. +// An error will be returned if the instance was unable to be created. +func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*servers.Server, error) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create server: %s", name) + + pwd := tools.MakeNewPassword("") + + server, err := servers.Create(client, servers.CreateOpts{ + Name: name, + FlavorRef: choices.FlavorID, + ImageRef: choices.ImageID, + AdminPass: pwd, + Networks: []servers.Network{ + servers.Network{UUID: networkID}, + }, + Metadata: map[string]string{ + "abc": "def", + }, + Personality: servers.Personality{ + &servers.File{ + Path: "/etc/test", + Contents: []byte("hello world"), + }, + }, + Tags: []string{"tag1", "tag2"}, + }).Extract() + if err != nil { + return server, err + } + + if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { + return nil, err + } + + res := servers.Get(client, server.ID) + if res.Err != nil { + return nil, res.Err + } + + newServer, err := res.Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newServer.Name, name) + + tags, err := res.ExtractTags() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, tags, []string{"tag1", "tag2"}) + + return newServer, nil +} + // CreateServerGroup will create a server with a random name. An error will be // returned if the server group failed to be created. func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) { diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 872294aaea..ee6f4b9521 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -17,6 +17,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/serverusage" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -465,25 +466,43 @@ func TestServersActionLock(t *testing.T) { } func TestServersConsoleOutput(t *testing.T) { + clients.RequireLong(t) + client, err := clients.NewComputeV2Client() - if err != nil { - t.Fatalf("Unable to create a compute client: %v", err) - } + th.AssertNoErr(t, err) server, err := CreateServer(t, client) - if err != nil { - t.Fatalf("Unable to create server: %v", err) - } - + th.AssertNoErr(t, err) defer DeleteServer(t, client, server) outputOpts := &servers.ShowConsoleOutputOpts{ Length: 4, } output, err := servers.ShowConsoleOutput(client, server.ID, outputOpts).Extract() - if err != nil { - t.Fatal(err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, output) } + +func TestServersTags(t *testing.T) { + clients.RequireLong(t) + clients.SkipRelease(t, "mitaka") + clients.SkipRelease(t, "newton") + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + client.Microversion = "2.52" + + networkClient, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + networkID, err := networks.IDFromName(networkClient, choices.NetworkName) + th.AssertNoErr(t, err) + + server, err := CreateServerWithTags(t, client, networkID) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) +} diff --git a/openstack/compute/v2/servers/microversions.go b/openstack/compute/v2/servers/microversions.go new file mode 100644 index 0000000000..84ec9f31d3 --- /dev/null +++ b/openstack/compute/v2/servers/microversions.go @@ -0,0 +1,11 @@ +package servers + +// ExtractTags will extract the tags of a server. +// This requires the client to be set to microversion 2.26 or later. +func (r serverResult) ExtractTags() ([]string, error) { + var s struct { + Tags []string `json:"tags"` + } + err := r.ExtractInto(&s) + return s.Tags, err +} diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index a6530f8d65..646bd4ccf1 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -189,6 +189,10 @@ type CreateOpts struct { // ServiceClient will allow calls to be made to retrieve an image or // flavor ID by name. ServiceClient *gophercloud.ServiceClient `json:"-"` + + // Tags allows a server to be tagged with single-word metadata. + // Requires microversion 2.52 or later. + Tags []string `json:"tags,omitempty"` } // ToServerCreateMap assembles a request body based on the contents of a diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index 1d4e99c06b..acd90f3d40 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -391,6 +391,93 @@ const ConsoleOutputBody = `{ "output": "abc" }` +const ServerWithTagsCreateRequest = ` +{ + "server": { + "name": "derp", + "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", + "flavorRef": "1", + "tags": ["foo", "bar"] + } +}` + +// SingleServerWithTagsBody is the canned body of a Get request on an existing server with tags. +const SingleServerWithTagsBody = ` +{ + "server": { + "status": "ACTIVE", + "updated": "2014-09-25T13:04:49Z", + "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", + "OS-EXT-SRV-ATTR:host": "devstack", + "addresses": { + "private": [ + { + "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", + "version": 4, + "addr": "10.0.0.31", + "OS-EXT-IPS:type": "fixed" + } + ] + }, + "links": [ + { + "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", + "rel": "self" + }, + { + "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", + "rel": "bookmark" + } + ], + "key_name": null, + "image": { + "id": "f90f6034-2570-4974-8351-6b49732ef2eb", + "links": [ + { + "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", + "rel": "bookmark" + } + ] + }, + "OS-EXT-STS:task_state": null, + "OS-EXT-STS:vm_state": "active", + "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", + "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", + "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", + "flavor": { + "id": "1", + "links": [ + { + "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", + "rel": "bookmark" + } + ] + }, + "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba", + "security_groups": [ + { + "name": "default" + } + ], + "OS-SRV-USG:terminated_at": null, + "OS-EXT-AZ:availability_zone": "nova", + "user_id": "9349aff8be7545ac9d2f1d00999a23cd", + "name": "derp", + "created": "2014-09-25T13:04:41Z", + "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", + "OS-DCF:diskConfig": "MANUAL", + "os-extended-volumes:volumes_attached": [], + "accessIPv4": "", + "accessIPv6": "", + "progress": 0, + "OS-EXT-STS:power_state": 1, + "config_drive": "", + "metadata": {}, + "tags": ["foo", "bar"] + } +} +` + var ( herpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:10:02Z") herpTimeUpdated, _ = time.Parse(time.RFC3339, "2014-09-25T13:10:10Z") @@ -1080,3 +1167,18 @@ func HandlePasswordGetSuccessfully(t *testing.T) { fmt.Fprintf(w, ServerPasswordBody) }) } + +// HandleServerWithTagsCreationSuccessfully sets up the test server to respond +// to a server creation request with a given response. +func HandleServerWithTagsCreationSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers", 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, ServerWithTagsCreateRequest) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + + fmt.Fprintf(w, SingleServerWithTagsBody) + }) +} diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 46ae45dbf4..0bcaca0175 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -550,3 +550,29 @@ func TestMarshalPersonality(t *testing.T) { t.Fatal("file contents incorrect") } } + +func TestCreateServerWithTags(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleServerWithTagsCreationSuccessfully(t) + + c := client.ServiceClient() + c.Microversion = "2.52" + + tags := []string{"foo", "bar"} + createOpts := servers.CreateOpts{ + Name: "derp", + ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", + FlavorRef: "1", + Tags: tags, + } + res := servers.Create(c, createOpts) + th.AssertNoErr(t, res.Err) + actualServer, err := res.Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ServerDerp, *actualServer) + + actualTags, err := res.ExtractTags() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, tags, actualTags) +} From a2b0ad6ce68c8302027db1a5f9dbb03b0c8ab072 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 3 Feb 2019 19:18:47 -0700 Subject: [PATCH 0650/2296] Core: Reauth Concurrency fixes (#1435) * Core: Reauth Concurrency fixes * Testing: Run a separate unit test script in Travis * Testing: Fix tests which were causing unit tests to fail --- .travis.yml | 1 + openstack/client.go | 4 +-- .../v1/capsules/testing/requests_test.go | 10 +++--- .../v2/images/testing/requests_test.go | 2 -- provider_client.go | 31 +++++++++++++++---- script/unittest | 4 ++- testing/provider_client_test.go | 20 +++++++----- 7 files changed, 49 insertions(+), 23 deletions(-) diff --git a/.travis.yml b/.travis.yml index 02728f4968..ef5629be6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ before_script: - go vet ./... script: - ./script/coverage +- ./script/unittest - ./script/format after_success: - $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out diff --git a/openstack/client.go b/openstack/client.go index 7a7a1803f1..2cef3f0b8a 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -150,7 +150,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once tac := *client - tac.IsThrowaway = true + tac.SetThrowaway(true) tac.ReauthFunc = nil tac.TokenID = "" tao := options @@ -207,7 +207,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once tac := *client - tac.IsThrowaway = true + tac.SetThrowaway(true) tac.ReauthFunc = nil tac.TokenID = "" var tao tokens3.AuthOptionsBuilder diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 03b93cfe0f..56376a08ea 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -81,11 +81,13 @@ func TestListCapsule(t *testing.T) { createdAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+00:00") updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoT, "2018-01-12 09:37:25+01:00") - ExpectedCapsule.CreatedAt = createdAt - ExpectedCapsule.UpdatedAt = updatedAt - ExpectedCapsule.Containers = nil + ec := ExpectedCapsule + + ec.CreatedAt = createdAt + ec.UpdatedAt = updatedAt + ec.Containers = nil - expected := []capsules.Capsule{ExpectedCapsule} + expected := []capsules.Capsule{ec} count := 0 results := capsules.List(fakeclient.ServiceClient(), nil) diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index ed9a344f49..5fa0ad2a66 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -16,8 +16,6 @@ func TestListImage(t *testing.T) { HandleImageListSuccessfully(t) - t.Logf("Test setup %+v\n", th.Server) - t.Logf("Id\tName\tOwner\tChecksum\tSizeBytes") pager := images.List(fakeclient.ServiceClient(), images.ListOpts{Limit: 1}) diff --git a/provider_client.go b/provider_client.go index f2aae976cf..233ade9417 100644 --- a/provider_client.go +++ b/provider_client.go @@ -72,9 +72,9 @@ type ProviderClient struct { // authentication functions for different Identity service versions. ReauthFunc func() error - // IsThrowaway determines whether if this client is a throw-away client. It's a copy of user's provider client + // Throwaway determines whether if this client is a throw-away client. It's a copy of user's provider client // with the token and reauth func zeroed. Such client can be used to perform reauthorization. - IsThrowaway bool + Throwaway bool mut *sync.RWMutex @@ -91,7 +91,7 @@ type reauthlock struct { // AuthenticatedHeaders returns a map of HTTP headers that are common for all // authenticated service requests. Blocks if Reauthenticate is in progress. func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { - if client.IsThrowaway { + if client.IsThrowaway() { return } if client.reauthmut != nil { @@ -135,6 +135,24 @@ func (client *ProviderClient) SetToken(t string) { client.TokenID = t } +// IsThrowaway safely reads the value of the client Throwaway field. +func (client *ProviderClient) IsThrowaway() bool { + if client.reauthmut != nil { + client.reauthmut.RLock() + defer client.reauthmut.RUnlock() + } + return client.Throwaway +} + +// SetThrowaway safely sets the value of the client Throwaway field. +func (client *ProviderClient) SetThrowaway(v bool) { + if client.reauthmut != nil { + client.reauthmut.Lock() + defer client.reauthmut.Unlock() + } + client.Throwaway = v +} + // Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is // called because of a 401 response, the caller may pass the previous token. In // this case, the reauthentication can be skipped if another thread has already @@ -276,13 +294,14 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } // Allow default OkCodes if none explicitly set - if options.OkCodes == nil { - options.OkCodes = defaultOkCodes(method) + okc := options.OkCodes + if okc == nil { + okc = defaultOkCodes(method) } // Validate the HTTP response status. var ok bool - for _, code := range options.OkCodes { + for _, code := range okc { if resp.StatusCode == code { ok = true break diff --git a/script/unittest b/script/unittest index 2c65d06034..609f74cd0c 100755 --- a/script/unittest +++ b/script/unittest @@ -2,4 +2,6 @@ # # Run the unit tests. -exec go test ./testing ./.../testing $@ +# Do extra rounds of testing to help identify reauth concurrency issues. +# All other packages are tested in the `coverage` tests. +go test -v -race -count=5 ./testing diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index a7dbc5f437..89291b88da 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -61,14 +61,14 @@ func TestConcurrentReauth(t *testing.T) { p.UseTokenLock() p.SetToken(prereauthTok) p.ReauthFunc = func() error { - p.IsThrowaway = true + p.SetThrowaway(true) time.Sleep(1 * time.Second) p.AuthenticatedHeaders() info.mut.Lock() info.numreauths++ info.mut.Unlock() p.TokenID = postreauthTok - p.IsThrowaway = false + p.SetThrowaway(false) return nil } @@ -142,6 +142,7 @@ func TestReauthEndLoop(t *testing.T) { } numconc := 20 + mut := new(sync.RWMutex) p := new(gophercloud.ProviderClient) p.UseTokenLock() @@ -154,9 +155,9 @@ func TestReauthEndLoop(t *testing.T) { info.maxReauthReached = true return fmt.Errorf("Max reauthentication attempts reached") } - p.IsThrowaway = true + p.SetThrowaway(true) p.AuthenticatedHeaders() - p.IsThrowaway = false + p.SetThrowaway(false) info.reauthAttempts++ return nil @@ -184,6 +185,9 @@ func TestReauthEndLoop(t *testing.T) { defer wg.Done() _, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) + mut.Lock() + defer mut.Unlock() + // ErrErrorAfter... will happen after a successful reauthentication, // but the service still responds with a 401. if _, ok := err.(*gophercloud.ErrErrorAfterReauthentication); ok { @@ -201,8 +205,8 @@ func TestReauthEndLoop(t *testing.T) { wg.Wait() th.AssertEquals(t, info.reauthAttempts, 6) th.AssertEquals(t, info.maxReauthReached, true) - th.AssertEquals(t, errAfter, 6) - th.AssertEquals(t, errUnable, 14) + th.AssertEquals(t, errAfter > 1, true) + th.AssertEquals(t, errUnable < 20, true) } func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { @@ -235,13 +239,13 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { } else { info.mut.RUnlock() } - p.IsThrowaway = true + p.SetThrowaway(true) p.AuthenticatedHeaders() info.mut.Lock() info.numreauths++ info.mut.Unlock() p.TokenID = postreauthTok - p.IsThrowaway = false + p.SetThrowaway(false) return nil } From df38e1611dbe76a187bbcec07797714af9671066 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Wed, 6 Feb 2019 02:10:53 +0000 Subject: [PATCH 0651/2296] add gophercloud.AuthResult (#1226) * add gophercloud.AuthResult The API design in this commit is (again) wildly different from the various proposals that I came up with in #1141. Let's see what Joe thinks of this. * replace IsAnAuthResult by the more plausible ExtractTokenID Also, since the first argument of SetTokenAndAuthResult is now redundant, remove it and rename the method to SetTokenFromAuthResult accordingly. * address review comments Mostly style. SetTokenFromAuthResult is renamed back to SetTokenAndAuthResult to make it clear that client.authResult is set by it. --- auth_result.go | 52 ++++++++++++++++++++++ openstack/client.go | 15 +++---- openstack/identity/v2/tokens/results.go | 15 +++++++ openstack/identity/v3/tokens/results.go | 7 +++ provider_client.go | 58 ++++++++++++++++++++++++- 5 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 auth_result.go diff --git a/auth_result.go b/auth_result.go new file mode 100644 index 0000000000..a0fba23b02 --- /dev/null +++ b/auth_result.go @@ -0,0 +1,52 @@ +package gophercloud + +/* +AuthResult is the result from the request that was used to obtain a provider +client's Keystone token. It is returned from ProviderClient.GetAuthResult(). + +The following types satisfy this interface: + + github.com/gophercloud/gophercloud/openstack/identity/v2/tokens.CreateResult + github.com/gophercloud/gophercloud/openstack/identity/v3/tokens.CreateResult + +Usage example: + + import ( + "github.com/gophercloud/gophercloud" + tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + ) + + func GetAuthenticatedUserID(providerClient *gophercloud.ProviderClient) (string, error) { + r := providerClient.GetAuthResult() + if r == nil { + //ProviderClient did not use openstack.Authenticate(), e.g. because token + //was set manually with ProviderClient.SetToken() + return "", errors.New("no AuthResult available") + } + switch r := r.(type) { + case *tokens2.CreateResult: + u, err := r.ExtractUser() + if err != nil { + return "", err + } + return u.ID, nil + case *tokens3.CreateResult: + u, err := r.ExtractUser() + if err != nil { + return "", err + } + return u.ID, nil + default: + panic(fmt.Sprintf("got unexpected AuthResult type %t", r)) + } + } + +Both implementing types share a lot of methods by name, like ExtractUser() in +this example. But those methods cannot be part of the AuthResult interface +because the return types are different (in this case, type tokens2.User vs. +type tokens3.User). +*/ +type AuthResult interface { + ExtractTokenID() (string, error) +} diff --git a/openstack/client.go b/openstack/client.go index 2cef3f0b8a..8fbd9dc7cf 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -135,7 +135,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc result := tokens2.Create(v2Client, v2Opts) - token, err := result.ExtractToken() + err = client.SetTokenAndAuthResult(result) if err != nil { return err } @@ -152,7 +152,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc tac := *client tac.SetThrowaway(true) tac.ReauthFunc = nil - tac.TokenID = "" + tac.SetTokenAndAuthResult(nil) tao := options tao.AllowReauth = false client.ReauthFunc = func() error { @@ -160,11 +160,10 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc if err != nil { return err } - client.TokenID = tac.TokenID + client.CopyTokenFrom(&tac) return nil } } - client.TokenID = token.ID client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { return V2EndpointURL(catalog, opts) } @@ -190,7 +189,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au result := tokens3.Create(v3Client, opts) - token, err := result.ExtractToken() + err = client.SetTokenAndAuthResult(result) if err != nil { return err } @@ -200,8 +199,6 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au return err } - client.TokenID = token.ID - if opts.CanReauth() { // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, @@ -209,7 +206,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au tac := *client tac.SetThrowaway(true) tac.ReauthFunc = nil - tac.TokenID = "" + tac.SetTokenAndAuthResult(nil) var tao tokens3.AuthOptionsBuilder switch ot := opts.(type) { case *gophercloud.AuthOptions: @@ -228,7 +225,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au if err != nil { return err } - client.TokenID = tac.TokenID + client.CopyTokenFrom(&tac) return nil } } diff --git a/openstack/identity/v2/tokens/results.go b/openstack/identity/v2/tokens/results.go index b11326772b..ee5da37f46 100644 --- a/openstack/identity/v2/tokens/results.go +++ b/openstack/identity/v2/tokens/results.go @@ -135,6 +135,21 @@ func (r CreateResult) ExtractToken() (*Token, error) { }, nil } +// ExtractTokenID implements the gophercloud.AuthResult interface. The returned +// string is the same as the ID field of the Token struct returned from +// ExtractToken(). +func (r CreateResult) ExtractTokenID() (string, error) { + var s struct { + Access struct { + Token struct { + ID string `json:"id"` + } `json:"token"` + } `json:"access"` + } + err := r.ExtractInto(&s) + return s.Access.Token.ID, err +} + // ExtractServiceCatalog returns the ServiceCatalog that was generated along // with the user's Token. func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index ebdca58f65..6f26c96bcd 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -102,6 +102,13 @@ func (r commonResult) ExtractToken() (*Token, error) { return &s, err } +// ExtractTokenID implements the gophercloud.AuthResult interface. The returned +// string is the same as the ID field of the Token struct returned from +// ExtractToken(). +func (r CreateResult) ExtractTokenID() (string, error) { + return r.Header.Get("X-Subject-Token"), r.Err +} + // ExtractServiceCatalog returns the ServiceCatalog that was generated along // with the user's Token. func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) { diff --git a/provider_client.go b/provider_client.go index 233ade9417..365b4cbcdf 100644 --- a/provider_client.go +++ b/provider_client.go @@ -79,6 +79,8 @@ type ProviderClient struct { mut *sync.RWMutex reauthmut *reauthlock + + authResult AuthResult } type reauthlock struct { @@ -115,6 +117,20 @@ func (client *ProviderClient) UseTokenLock() { client.reauthmut = new(reauthlock) } +// GetAuthResult returns the result from the request that was used to obtain a +// provider client's Keystone token. +// +// The result is nil when authentication has not yet taken place, when the token +// was set manually with SetToken(), or when a ReauthFunc was used that does not +// record the AuthResult. +func (client *ProviderClient) GetAuthResult() AuthResult { + if client.mut != nil { + client.mut.RLock() + defer client.mut.RUnlock() + } + return client.authResult +} + // Token safely reads the value of the auth token from the ProviderClient. Applications should // call this method to access the token instead of the TokenID field func (client *ProviderClient) Token() string { @@ -126,13 +142,53 @@ func (client *ProviderClient) Token() string { } // SetToken safely sets the value of the auth token in the ProviderClient. Applications may -// use this method in a custom ReauthFunc +// use this method in a custom ReauthFunc. +// +// WARNING: This function is deprecated. Use SetTokenAndAuthResult() instead. func (client *ProviderClient) SetToken(t string) { if client.mut != nil { client.mut.Lock() defer client.mut.Unlock() } client.TokenID = t + client.authResult = nil +} + +// SetTokenAndAuthResult safely sets the value of the auth token in the +// ProviderClient and also records the AuthResult that was returned from the +// token creation request. Applications may call this in a custom ReauthFunc. +func (client *ProviderClient) SetTokenAndAuthResult(r AuthResult) error { + tokenID := "" + var err error + if r != nil { + tokenID, err = r.ExtractTokenID() + if err != nil { + return err + } + } + + if client.mut != nil { + client.mut.Lock() + defer client.mut.Unlock() + } + client.TokenID = tokenID + client.authResult = r + return nil +} + +// CopyTokenFrom safely copies the token from another ProviderClient into the +// this one. +func (client *ProviderClient) CopyTokenFrom(other *ProviderClient) { + if client.mut != nil { + client.mut.Lock() + defer client.mut.Unlock() + } + if other.mut != nil && other.mut != client.mut { + other.mut.RLock() + defer other.mut.RUnlock() + } + client.TokenID = other.TokenID + client.authResult = other.authResult } // IsThrowaway safely reads the value of the client Throwaway field. From 1cbc795ec026e23d7f57020792803d257b7100b2 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 27 Jul 2018 10:50:44 +0300 Subject: [PATCH 0652/2296] Compute V2: implement remoteconsoles Create method Add the new "remoteconsoles" Compute extension package with needed structures and Create method. Add unit and acceptance test with documentation. --- acceptance/openstack/compute/v2/compute.go | 18 ++++ .../compute/v2/remoteconsoles_test.go | 27 ++++++ .../v2/extensions/remoteconsoles/doc.go | 19 +++++ .../v2/extensions/remoteconsoles/requests.go | 83 +++++++++++++++++++ .../v2/extensions/remoteconsoles/results.go | 38 +++++++++ .../extensions/remoteconsoles/testing/doc.go | 1 + .../remoteconsoles/testing/fixtures.go | 22 +++++ .../remoteconsoles/testing/requests_test.go | 40 +++++++++ .../v2/extensions/remoteconsoles/urls.go | 17 ++++ 9 files changed, 265 insertions(+) create mode 100644 acceptance/openstack/compute/v2/remoteconsoles_test.go create mode 100644 openstack/compute/v2/extensions/remoteconsoles/doc.go create mode 100644 openstack/compute/v2/extensions/remoteconsoles/requests.go create mode 100644 openstack/compute/v2/extensions/remoteconsoles/results.go create mode 100644 openstack/compute/v2/extensions/remoteconsoles/testing/doc.go create mode 100644 openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/remoteconsoles/urls.go diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 6d750c66ec..1bad518206 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -20,6 +20,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/remoteconsoles" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" @@ -1063,3 +1064,20 @@ func UnrescueServer(t *testing.T, client *gophercloud.ServiceClient, server *ser return nil } + +// CreateRemoteConsole will create a remote noVNC console for the specified server. +func CreateRemoteConsole(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*remoteconsoles.RemoteConsole, error) { + createOpts := remoteconsoles.CreateOpts{ + Protocol: string(remoteconsoles.RemoteConsoleVNCProtocol), + Type: string(remoteconsoles.RemoteConsoleNoVNCType), + } + + t.Logf("Attempting to create a %s console for the server %s", createOpts.Type, serverID) + remoteConsole, err := remoteconsoles.Create(client, serverID, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created console: %s", remoteConsole.URL) + return remoteConsole, nil +} diff --git a/acceptance/openstack/compute/v2/remoteconsoles_test.go b/acceptance/openstack/compute/v2/remoteconsoles_test.go new file mode 100644 index 0000000000..dbd831325c --- /dev/null +++ b/acceptance/openstack/compute/v2/remoteconsoles_test.go @@ -0,0 +1,27 @@ +// +build acceptance compute remoteconsoles + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestRemoteConsoleCreate(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) + + remoteConsole, err := CreateRemoteConsole(t, client, server.ID) + th.AssertNoErr(t, err) + + tools.PrintResource(t, remoteConsole) +} diff --git a/openstack/compute/v2/extensions/remoteconsoles/doc.go b/openstack/compute/v2/extensions/remoteconsoles/doc.go new file mode 100644 index 0000000000..6e7eee01fc --- /dev/null +++ b/openstack/compute/v2/extensions/remoteconsoles/doc.go @@ -0,0 +1,19 @@ +/* +Package remoteconsoles provides the ability to create server remote consoles +through the Compute API. + +Example of Creating a new RemoteConsole + + createOpts := remoteconsoles.CreateOpts{ + Protocol: string(remoteconsoles.RemoteConsoleVNCProtocol), + Type: string(remoteconsoles.RemoteConsoleNoVNCType), + } + serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" + remtoteConsole, err := remoteconsoles.Create(computeClient, serverID, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", task) +*/ +package remoteconsoles diff --git a/openstack/compute/v2/extensions/remoteconsoles/requests.go b/openstack/compute/v2/extensions/remoteconsoles/requests.go new file mode 100644 index 0000000000..7a2e437c80 --- /dev/null +++ b/openstack/compute/v2/extensions/remoteconsoles/requests.go @@ -0,0 +1,83 @@ +package remoteconsoles + +import ( + "github.com/gophercloud/gophercloud" +) + +// RemoteConsoleProtocol represents valid remote console protocol. +// It can be used to create a remote console with one of the pre-defined protocol. +type RemoteConsoleProtocol string + +const ( + // RemoteConsoleVNCProtocol represents the VNC console protocol. + RemoteConsoleVNCProtocol RemoteConsoleProtocol = "vnc" + + // RemoteConsoleSPICEProtocol represents the SPICE console protocol. + RemoteConsoleSPICEProtocol RemoteConsoleProtocol = "spice" + + // RemoteConsoleRDPProtocol represents the RDP console protocol. + RemoteConsoleRDPProtocol RemoteConsoleProtocol = "rdp" + + // RemoteConsoleSerialProtocol represents the serial console protocol. + RemoteConsoleSerialProtocol RemoteConsoleProtocol = "serial" + + // RemoteConsoleMKSProtocol represents the MKS console protocol. + RemoteConsoleMKSProtocol RemoteConsoleProtocol = "mks" +) + +// RemoteConsoleType represents valid remote console type. +// It can be used to create a remote console with one of the pre-defined type. +type RemoteConsoleType string + +const ( + // RemoteConsoleNoVNCType represents the VNC console type. + RemoteConsoleNoVNCType RemoteConsoleType = "novnc" + + // RemoteConsoleXVPVNCType represents the XVP VNC console type. + RemoteConsoleXVPVNCType RemoteConsoleType = "xvpvnc" + + // RemoteConsoleRDPHTML5Type represents the RDP HTML5 console type. + RemoteConsoleRDPHTML5Type RemoteConsoleType = "rdp-html5" + + // RemoteConsoleSPICEHTML5Type represents the SPICE HTML5 console type. + RemoteConsoleSPICEHTML5Type RemoteConsoleType = "spice-html5" + + // RemoteConsoleSerialType represents the serial console type. + RemoteConsoleSerialType RemoteConsoleType = "serial" + + // RemoteConsoleWebMKSType represents the web MKS console type. + RemoteConsoleWebMKSType RemoteConsoleType = "webmks" +) + +// CreateOptsBuilder allows to add additional parameters to the Create request. +type CreateOptsBuilder interface { + ToRemoteConsoleCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters to the Create request. +type CreateOpts struct { + // Protocol specifies the protocol of a new remote console. + Protocol string `json:"protocol" required:"true"` + + // Type specifies the type of a new remote console. + Type string `json:"type" required:"true"` +} + +// ToRemoteConsoleCreateMap builds a request body from the CreateOpts. +func (opts CreateOpts) ToRemoteConsoleCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "remote_console") +} + +// Create requests the creation of a new remote console on the specified server. +func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { + reqBody, err := opts.ToRemoteConsoleCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(createURL(client, serverID), reqBody, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/remoteconsoles/results.go b/openstack/compute/v2/extensions/remoteconsoles/results.go new file mode 100644 index 0000000000..684c637086 --- /dev/null +++ b/openstack/compute/v2/extensions/remoteconsoles/results.go @@ -0,0 +1,38 @@ +package remoteconsoles + +import "github.com/gophercloud/gophercloud" + +type commonResult struct { + gophercloud.Result +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a RemoteConsole. +type CreateResult struct { + commonResult +} + +// RemoteConsole represents the Compute service remote console object. +type RemoteConsole struct { + // Protocol contains remote console protocol. + // You can use the RemoteConsoleProtocol custom type to unmarshal raw JSON + // response into the pre-defined valid console protocol. + Protocol string `json:"protocol"` + + // Type contains remote console type. + // You can use the RemoteConsoleType custom type to unmarshal raw JSON + // response into the pre-defined valid console type. + Type string `json:"type"` + + // URL can be used to connect to the remote console. + URL string `json:"url"` +} + +// Extract interprets any commonResult as a RemoteConsole. +func (r commonResult) Extract() (*RemoteConsole, error) { + var s struct { + RemoteConsole *RemoteConsole `json:"remote_console"` + } + err := r.ExtractInto(&s) + return s.RemoteConsole, err +} diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/doc.go b/openstack/compute/v2/extensions/remoteconsoles/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/compute/v2/extensions/remoteconsoles/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go b/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go new file mode 100644 index 0000000000..9644a4895e --- /dev/null +++ b/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures.go @@ -0,0 +1,22 @@ +package testing + +// RemoteConsoleCreateRequest represents a request to create a remote console. +const RemoteConsoleCreateRequest = ` +{ + "remote_console": { + "protocol": "vnc", + "type": "novnc" + } +} +` + +// RemoteConsoleCreateResult represents a raw server responce to the RemoteConsoleCreateRequest. +const RemoteConsoleCreateResult = ` +{ + "remote_console": { + "protocol": "vnc", + "type": "novnc", + "url": "http://192.168.0.4:6080/vnc_auto.html?token=9a2372b9-6a0e-4f71-aca1-56020e6bb677" + } +} +` diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go b/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go new file mode 100644 index 0000000000..afcd8393ad --- /dev/null +++ b/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go @@ -0,0 +1,40 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/remoteconsoles" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/b16ba811-199d-4ffd-8839-ba96c1185a67/remote-consoles", 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, RemoteConsoleCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, RemoteConsoleCreateResult) + }) + + opts := remoteconsoles.CreateOpts{ + Protocol: string(remoteconsoles.RemoteConsoleVNCProtocol), + Type: string(remoteconsoles.RemoteConsoleNoVNCType), + } + s, err := remoteconsoles.Create(fake.ServiceClient(), "b16ba811-199d-4ffd-8839-ba96c1185a67", opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Protocol, string(remoteconsoles.RemoteConsoleVNCProtocol)) + th.AssertEquals(t, s.Type, string(remoteconsoles.RemoteConsoleNoVNCType)) + th.AssertEquals(t, s.URL, "http://192.168.0.4:6080/vnc_auto.html?token=9a2372b9-6a0e-4f71-aca1-56020e6bb677") +} diff --git a/openstack/compute/v2/extensions/remoteconsoles/urls.go b/openstack/compute/v2/extensions/remoteconsoles/urls.go new file mode 100644 index 0000000000..b4d7ec2bbf --- /dev/null +++ b/openstack/compute/v2/extensions/remoteconsoles/urls.go @@ -0,0 +1,17 @@ +package remoteconsoles + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "servers" + + resourcePath = "remote-consoles" +) + +func rootURL(c *gophercloud.ServiceClient, serverID string) string { + return c.ServiceURL(rootPath, serverID, resourcePath) +} + +func createURL(c *gophercloud.ServiceClient, serverID string) string { + return rootURL(c, serverID) +} From 0defaba605c8b61c736a3829853b17ba738a8075 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 27 Jul 2018 13:37:28 +0300 Subject: [PATCH 0653/2296] Compute V2: set microversion in console test We need to specify microversion for the remote console acceptance test. Support for the "remote-consoles" API was added in the 2.6 microversion. --- acceptance/openstack/compute/v2/remoteconsoles_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acceptance/openstack/compute/v2/remoteconsoles_test.go b/acceptance/openstack/compute/v2/remoteconsoles_test.go index dbd831325c..9dd5863c9c 100644 --- a/acceptance/openstack/compute/v2/remoteconsoles_test.go +++ b/acceptance/openstack/compute/v2/remoteconsoles_test.go @@ -16,6 +16,8 @@ func TestRemoteConsoleCreate(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) + client.Microversion = "2.6" + server, err := CreateServer(t, client) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) From 4037dc8cf6222b41ad198afb46dc91bff9316bb0 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 27 Jul 2018 13:57:04 +0300 Subject: [PATCH 0654/2296] Compute V2: extend documentation for the consoles Add a note about Nova microversion for the "remoteconsole" package usage. --- openstack/compute/v2/extensions/remoteconsoles/doc.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/remoteconsoles/doc.go b/openstack/compute/v2/extensions/remoteconsoles/doc.go index 6e7eee01fc..3ffd1a6152 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/doc.go +++ b/openstack/compute/v2/extensions/remoteconsoles/doc.go @@ -1,14 +1,20 @@ /* Package remoteconsoles provides the ability to create server remote consoles through the Compute API. +You need to specify at least "2.6" microversion for the ComputeClient to use +that API. Example of Creating a new RemoteConsole + computeClient, err := openstack.NewComputeV2(providerClient, endpointOptions) + computeClient.Microversion = "2.6" + createOpts := remoteconsoles.CreateOpts{ Protocol: string(remoteconsoles.RemoteConsoleVNCProtocol), Type: string(remoteconsoles.RemoteConsoleNoVNCType), } - serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" + serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" + remtoteConsole, err := remoteconsoles.Create(computeClient, serverID, createOpts).Extract() if err != nil { panic(err) From 9432f1c87e340eb5ddea65afc6b00171ca57b80c Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 29 Jul 2018 21:09:26 +0300 Subject: [PATCH 0655/2296] Compute V2: fix remoteconsoles options Use strict types for the remoteconsoles Create request option fields. Change names for console types and protocols. Update tests and documentation example. --- acceptance/openstack/compute/v2/compute.go | 4 +- .../v2/extensions/remoteconsoles/doc.go | 8 +-- .../v2/extensions/remoteconsoles/requests.go | 56 +++++++++---------- .../remoteconsoles/testing/requests_test.go | 8 +-- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 1bad518206..72e641060e 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -1068,8 +1068,8 @@ func UnrescueServer(t *testing.T, client *gophercloud.ServiceClient, server *ser // CreateRemoteConsole will create a remote noVNC console for the specified server. func CreateRemoteConsole(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*remoteconsoles.RemoteConsole, error) { createOpts := remoteconsoles.CreateOpts{ - Protocol: string(remoteconsoles.RemoteConsoleVNCProtocol), - Type: string(remoteconsoles.RemoteConsoleNoVNCType), + Protocol: remoteconsoles.ConsoleProtocolVNC, + Type: remoteconsoles.ConsoleTypeNoVNC, } t.Logf("Attempting to create a %s console for the server %s", createOpts.Type, serverID) diff --git a/openstack/compute/v2/extensions/remoteconsoles/doc.go b/openstack/compute/v2/extensions/remoteconsoles/doc.go index 3ffd1a6152..1ab269b47e 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/doc.go +++ b/openstack/compute/v2/extensions/remoteconsoles/doc.go @@ -10,9 +10,9 @@ Example of Creating a new RemoteConsole computeClient.Microversion = "2.6" createOpts := remoteconsoles.CreateOpts{ - Protocol: string(remoteconsoles.RemoteConsoleVNCProtocol), - Type: string(remoteconsoles.RemoteConsoleNoVNCType), - } + Protocol: remoteconsoles.ConsoleProtocolVNC, + Type: remoteconsoles.ConsoleTypeNoVNC, + } serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" remtoteConsole, err := remoteconsoles.Create(computeClient, serverID, createOpts).Extract() @@ -20,6 +20,6 @@ Example of Creating a new RemoteConsole panic(err) } - fmt.Printf("%+v\n", task) + fmt.Printf("Console URL: %s\n", remtoteConsole.URL) */ package remoteconsoles diff --git a/openstack/compute/v2/extensions/remoteconsoles/requests.go b/openstack/compute/v2/extensions/remoteconsoles/requests.go index 7a2e437c80..5c41035f70 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/requests.go +++ b/openstack/compute/v2/extensions/remoteconsoles/requests.go @@ -4,49 +4,49 @@ import ( "github.com/gophercloud/gophercloud" ) -// RemoteConsoleProtocol represents valid remote console protocol. +// ConsoleProtocol represents valid remote console protocol. // It can be used to create a remote console with one of the pre-defined protocol. -type RemoteConsoleProtocol string +type ConsoleProtocol string const ( - // RemoteConsoleVNCProtocol represents the VNC console protocol. - RemoteConsoleVNCProtocol RemoteConsoleProtocol = "vnc" + // ConsoleProtocolVNC represents the VNC console protocol. + ConsoleProtocolVNC ConsoleProtocol = "vnc" - // RemoteConsoleSPICEProtocol represents the SPICE console protocol. - RemoteConsoleSPICEProtocol RemoteConsoleProtocol = "spice" + // ConsoleProtocolSPICE represents the SPICE console protocol. + ConsoleProtocolSPICE ConsoleProtocol = "spice" - // RemoteConsoleRDPProtocol represents the RDP console protocol. - RemoteConsoleRDPProtocol RemoteConsoleProtocol = "rdp" + // ConsoleProtocolRDP represents the RDP console protocol. + ConsoleProtocolRDP ConsoleProtocol = "rdp" - // RemoteConsoleSerialProtocol represents the serial console protocol. - RemoteConsoleSerialProtocol RemoteConsoleProtocol = "serial" + // ConsoleProtocolSerial represents the Serial console protocol. + ConsoleProtocolSerial ConsoleProtocol = "serial" - // RemoteConsoleMKSProtocol represents the MKS console protocol. - RemoteConsoleMKSProtocol RemoteConsoleProtocol = "mks" + // ConsoleProtocolMKS represents the MKS console protocol. + ConsoleProtocolMKS ConsoleProtocol = "mks" ) -// RemoteConsoleType represents valid remote console type. +// ConsoleType represents valid remote console type. // It can be used to create a remote console with one of the pre-defined type. -type RemoteConsoleType string +type ConsoleType string const ( - // RemoteConsoleNoVNCType represents the VNC console type. - RemoteConsoleNoVNCType RemoteConsoleType = "novnc" + // ConsoleTypeNoVNC represents the VNC console type. + ConsoleTypeNoVNC ConsoleType = "novnc" - // RemoteConsoleXVPVNCType represents the XVP VNC console type. - RemoteConsoleXVPVNCType RemoteConsoleType = "xvpvnc" + // ConsoleTypeXVPVNC represents the XVP VNC console type. + ConsoleTypeXVPVNC ConsoleType = "xvpvnc" - // RemoteConsoleRDPHTML5Type represents the RDP HTML5 console type. - RemoteConsoleRDPHTML5Type RemoteConsoleType = "rdp-html5" + // ConsoleTypeRDPHTML5 represents the RDP HTML5 console type. + ConsoleTypeRDPHTML5 ConsoleType = "rdp-html5" - // RemoteConsoleSPICEHTML5Type represents the SPICE HTML5 console type. - RemoteConsoleSPICEHTML5Type RemoteConsoleType = "spice-html5" + // ConsoleTypeSPICEHTML5 represents the SPICE HTML5 console type. + ConsoleTypeSPICEHTML5 ConsoleType = "spice-html5" - // RemoteConsoleSerialType represents the serial console type. - RemoteConsoleSerialType RemoteConsoleType = "serial" + // ConsoleTypeSerial represents the Serial console type. + ConsoleTypeSerial ConsoleType = "serial" - // RemoteConsoleWebMKSType represents the web MKS console type. - RemoteConsoleWebMKSType RemoteConsoleType = "webmks" + // ConsoleTypeWebMKS represents the Web MKS console type. + ConsoleTypeWebMKS ConsoleType = "webmks" ) // CreateOptsBuilder allows to add additional parameters to the Create request. @@ -57,10 +57,10 @@ type CreateOptsBuilder interface { // CreateOpts specifies parameters to the Create request. type CreateOpts struct { // Protocol specifies the protocol of a new remote console. - Protocol string `json:"protocol" required:"true"` + Protocol ConsoleProtocol `json:"protocol" required:"true"` // Type specifies the type of a new remote console. - Type string `json:"type" required:"true"` + Type ConsoleType `json:"type" required:"true"` } // ToRemoteConsoleCreateMap builds a request body from the CreateOpts. diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go b/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go index afcd8393ad..f0b2c8c4c9 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go +++ b/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go @@ -28,13 +28,13 @@ func TestCreate(t *testing.T) { }) opts := remoteconsoles.CreateOpts{ - Protocol: string(remoteconsoles.RemoteConsoleVNCProtocol), - Type: string(remoteconsoles.RemoteConsoleNoVNCType), + Protocol: remoteconsoles.ConsoleProtocolVNC, + Type: remoteconsoles.ConsoleTypeNoVNC, } s, err := remoteconsoles.Create(fake.ServiceClient(), "b16ba811-199d-4ffd-8839-ba96c1185a67", opts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, s.Protocol, string(remoteconsoles.RemoteConsoleVNCProtocol)) - th.AssertEquals(t, s.Type, string(remoteconsoles.RemoteConsoleNoVNCType)) + th.AssertEquals(t, s.Protocol, string(remoteconsoles.ConsoleProtocolVNC)) + th.AssertEquals(t, s.Type, string(remoteconsoles.ConsoleTypeNoVNC)) th.AssertEquals(t, s.URL, "http://192.168.0.4:6080/vnc_auto.html?token=9a2372b9-6a0e-4f71-aca1-56020e6bb677") } From a4bfa26ed9a148f27abfd707aabc81ac8f134208 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Wed, 6 Feb 2019 19:54:56 +0000 Subject: [PATCH 0656/2296] fix example code for type AuthResult (#1439) --- auth_result.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth_result.go b/auth_result.go index a0fba23b02..2e4699b978 100644 --- a/auth_result.go +++ b/auth_result.go @@ -25,13 +25,13 @@ Usage example: return "", errors.New("no AuthResult available") } switch r := r.(type) { - case *tokens2.CreateResult: + case tokens2.CreateResult: u, err := r.ExtractUser() if err != nil { return "", err } return u.ID, nil - case *tokens3.CreateResult: + case tokens3.CreateResult: u, err := r.ExtractUser() if err != nil { return "", err From 83b528acebb44597e8a241b7215185c2bdc8e47d Mon Sep 17 00:00:00 2001 From: Yosuke Kuno Date: Thu, 7 Feb 2019 05:10:33 +0900 Subject: [PATCH 0657/2296] Image Service v2: Add Updating MinDisk (#1438) --- .../openstack/imageservice/v2/images_test.go | 1 + openstack/imageservice/v2/images/requests.go | 14 ++++++++++++++ .../imageservice/v2/images/testing/fixtures.go | 7 ++++++- .../v2/images/testing/requests_test.go | 3 ++- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/acceptance/openstack/imageservice/v2/images_test.go index 0fd657b98c..17069c3dc6 100644 --- a/acceptance/openstack/imageservice/v2/images_test.go +++ b/acceptance/openstack/imageservice/v2/images_test.go @@ -152,6 +152,7 @@ func TestImagesUpdate(t *testing.T) { updateOpts := images.UpdateOpts{ images.ReplaceImageName{NewName: image.Name + "foo"}, images.ReplaceImageTags{NewTags: newTags}, + images.ReplaceImageMinDisk{NewMinDisk: 21}, images.UpdateImageProperty{ Op: images.AddOp, Name: "hw_disk_bus", diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 16290d395a..4e487ea9e6 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -321,6 +321,20 @@ func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { } } +// ReplaceImageMinDisk represents an updated min_disk property request. +type ReplaceImageMinDisk struct { + NewMinDisk int +} + +// ToImagePatchMap assembles a request body based on ReplaceImageTags. +func (r ReplaceImageMinDisk) ToImagePatchMap() map[string]interface{} { + return map[string]interface{}{ + "op": "replace", + "path": "/min_disk", + "value": r.NewMinDisk, + } +} + // UpdateOp represents a valid update operation. type UpdateOp string diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index 413ae6bc4e..0ff03be95c 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -312,6 +312,11 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "fedora", "beefy" ] + }, + { + "op": "replace", + "path": "/min_disk", + "value": 21 } ]`) @@ -337,7 +342,7 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "schema": "/v2/schemas/image", "owner": "", "min_ram": 0, - "min_disk": 0, + "min_disk": 21, "disk_format": "", "virtual_size": 0, "container_format": "", diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index 5fa0ad2a66..86126c76d9 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -254,6 +254,7 @@ func TestUpdateImage(t *testing.T) { actualImage, err := images.Update(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", images.UpdateOpts{ images.ReplaceImageName{NewName: "Fedora 17"}, images.ReplaceImageTags{NewTags: []string{"fedora", "beefy"}}, + images.ReplaceImageMinDisk{NewMinDisk: 21}, }).Extract() th.AssertNoErr(t, err) @@ -281,7 +282,7 @@ func TestUpdateImage(t *testing.T) { Owner: "", MinRAMMegabytes: 0, - MinDiskGigabytes: 0, + MinDiskGigabytes: 21, DiskFormat: "", ContainerFormat: "", From ce8a8b4aed986e460d95ad09bfcf619386a25b4e Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 8 Feb 2019 04:29:41 +0100 Subject: [PATCH 0658/2296] Networking V2: add possibility to update FIP port's fixed IP (#1426) --- .../v2/extensions/layer3/floatingips_test.go | 80 ++++++++++++++++++- .../networking/v2/extensions/layer3/layer3.go | 48 +++++++++++ .../openstack/networking/v2/networking.go | 42 ++++++++++ .../extensions/layer3/floatingips/requests.go | 1 + 4 files changed, 170 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index dd572da2e2..ac50e9efaf 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -69,6 +69,8 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { port, err := networking.CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) + // not required, since "DeleteRouterInterface" above removes the port + // defer networking.DeletePort(t, client, port.ID) _, err = CreateRouterInterface(t, client, port.ID, router.ID) th.AssertNoErr(t, err) @@ -88,7 +90,83 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { PortID: new(string), } - newFip, err = floatingips.Update(client, fip.ID, updateOpts).Extract() + _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + newFip, err = floatingips.Get(client, fip.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newFip) + + th.AssertEquals(t, newFip.PortID, "") +} + +func TestLayer3FloatingIPsWithFixedIPsExternalCreateDelete(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + // Create Network + 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 := CreateExternalRouter(t, client) + th.AssertNoErr(t, err) + defer DeleteRouter(t, client, router.ID) + + port, err := networking.CreatePortWithMultipleFixedIPs(t, client, network.ID, subnet.ID) + th.AssertNoErr(t, err) + defer networking.DeletePort(t, client, port.ID) + + var fixedIPs []string + for _, fixedIP := range port.FixedIPs { + fixedIPs = append(fixedIPs, fixedIP.IPAddress) + } + + iface, err := CreateRouterInterfaceOnSubnet(t, client, subnet.ID, router.ID) + th.AssertNoErr(t, err) + defer DeleteRouterInterface(t, client, iface.PortID, router.ID) + + fip, err := CreateFloatingIPWithFixedIP(t, client, choices.ExternalNetworkID, port.ID, fixedIPs[0]) + th.AssertNoErr(t, err) + defer DeleteFloatingIP(t, client, fip.ID) + + newFip, err := floatingips.Get(client, fip.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newFip) + + // Associate the floating IP with another fixed IP + updateOpts := floatingips.UpdateOpts{ + PortID: &port.ID, + FixedIP: fixedIPs[1], + } + + _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + newFip, err = floatingips.Get(client, fip.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newFip) + + th.AssertEquals(t, newFip.FixedIP, fixedIPs[1]) + + // Disassociate the floating IP + updateOpts = floatingips.UpdateOpts{ + PortID: new(string), + } + + _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 075d3b3375..abc562c837 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -38,6 +38,32 @@ func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, networkID return floatingIP, err } +// CreateFloatingIPWithFixedIP creates a floating IP on a given network and port with a +// defined fixed IP. An error will be returned if the creation failed. +func CreateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, networkID, portID, fixedIP string) (*floatingips.FloatingIP, error) { + t.Logf("Attempting to create floating IP on port: %s and address: %s", portID, fixedIP) + + fipDescription := "Test floating IP" + createOpts := &floatingips.CreateOpts{ + Description: fipDescription, + FloatingNetworkID: networkID, + PortID: portID, + FixedIP: fixedIP, + } + + floatingIP, err := floatingips.Create(client, createOpts).Extract() + if err != nil { + return floatingIP, err + } + + t.Logf("Created floating IP.") + + th.AssertEquals(t, floatingIP.Description, fipDescription) + th.AssertEquals(t, floatingIP.FixedIP, fixedIP) + + return floatingIP, err +} + // CreateExternalRouter creates a router on the external network. This requires // the OS_EXTGW_ID environment variable to be set. An error is returned if the // creation failed. @@ -138,6 +164,28 @@ func CreateRouterInterface(t *testing.T, client *gophercloud.ServiceClient, port return iface, nil } +// CreateRouterInterfaceOnSubnet will attach a subnet to a router. An error will be +// returned if the operation fails. +func CreateRouterInterfaceOnSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID, routerID string) (*routers.InterfaceInfo, error) { + t.Logf("Attempting to add subnet %s to router %s", subnetID, routerID) + + aiOpts := routers.AddInterfaceOpts{ + SubnetID: subnetID, + } + + iface, err := routers.AddInterface(client, routerID, aiOpts).Extract() + if err != nil { + return iface, err + } + + if err := WaitForRouterInterfaceToAttach(client, iface.PortID, 60); err != nil { + return iface, err + } + + t.Logf("Successfully added subnet %s to router %s", subnetID, routerID) + return iface, nil +} + // DeleteRouter deletes a router of a specified ID. A fatal error will occur // if the deletion failed. This works best when used as a deferred function. func DeleteRouter(t *testing.T, client *gophercloud.ServiceClient, routerID string) { diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index 6690656103..6fc87f28d8 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -235,6 +235,48 @@ func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient return port, nil } +// CreatePortWithMultipleFixedIPs will create a port with two FixedIPs on the +// specified subnet. An error will be returned if the port could not be created. +func CreatePortWithMultipleFixedIPs(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) { + portName := tools.RandomString("TESTACC-", 8) + portDescription := tools.RandomString("TESTACC-DESC-", 8) + + t.Logf("Attempting to create port with two fixed IPs: %s", portName) + + createOpts := ports.CreateOpts{ + NetworkID: networkID, + Name: portName, + Description: portDescription, + AdminStateUp: gophercloud.Enabled, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}, ports.IP{SubnetID: subnetID}}, + } + + port, err := ports.Create(client, createOpts).Extract() + if err != nil { + return port, err + } + + if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + return port, err + } + + newPort, err := ports.Get(client, port.ID).Extract() + if err != nil { + return newPort, err + } + + t.Logf("Successfully created port: %s", portName) + + th.AssertEquals(t, port.Name, portName) + th.AssertEquals(t, port.Description, portDescription) + + if len(port.FixedIPs) != 2 { + t.Fatalf("Failed to create a port with two fixed IPs: %s", portName) + } + + return newPort, nil +} + // CreateSubnet will create a subnet on the specified Network ID. An error // will be returned if the subnet could not be created. func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 77efef6869..b7e6e9189d 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -124,6 +124,7 @@ type UpdateOptsBuilder interface { type UpdateOpts struct { Description *string `json:"description,omitempty"` PortID *string `json:"port_id,omitempty"` + FixedIP string `json:"fixed_ip_address,omitempty"` } // ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder From c69ff05c5e0f14820d2a1a6d2b89b25d9c2be6a5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 7 Feb 2019 21:24:34 -0700 Subject: [PATCH 0659/2296] Acc Tests: Increase timeouts for loadbalancers (#1444) --- acceptance/openstack/loadbalancer/v2/loadbalancer.go | 4 ++-- .../openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 26a5c961df..880e29403b 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -15,8 +15,8 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -const loadbalancerActiveTimeoutSeconds = 300 -const loadbalancerDeleteTimeoutSeconds = 300 +const loadbalancerActiveTimeoutSeconds = 600 +const loadbalancerDeleteTimeoutSeconds = 600 // CreateListener will create a listener for a given load balancer on a random // port with a random name. An error will be returned if the listener could not diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index a780af1150..db84121104 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -15,8 +15,8 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -const loadbalancerActiveTimeoutSeconds = 300 -const loadbalancerDeleteTimeoutSeconds = 300 +const loadbalancerActiveTimeoutSeconds = 600 +const loadbalancerDeleteTimeoutSeconds = 600 // CreateListener will create a listener for a given load balancer on a random // port with a random name. An error will be returned if the listener could not From bc37892e196848d7d56ba0db974ba1d7d5c43b2a Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 8 Feb 2019 07:26:52 +0300 Subject: [PATCH 0660/2296] ContainerInfraV1: add ListDetail method (#1441) Add ListDetail method with documentation and tests. This method uses different URL than List method. --- .../containerinfra/v1/clusters_test.go | 14 ++++++++++ openstack/containerinfra/v1/clusters/doc.go | 16 +++++++++++ .../containerinfra/v1/clusters/requests.go | 18 +++++++++++++ .../v1/clusters/testing/fixtures.go | 13 +++++++++ .../v1/clusters/testing/requests_test.go | 27 +++++++++++++++++++ openstack/containerinfra/v1/clusters/urls.go | 4 +++ 6 files changed, 92 insertions(+) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index 95d2da005c..c5f540cbf9 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -60,5 +60,19 @@ func TestClustersCRUD(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.UUID, clusterID) + allPagesDetail, err := clusters.ListDetail(client, nil).AllPages() + th.AssertNoErr(t, err) + + allClustersDetail, err := clusters.ExtractClusters(allPages) + th.AssertNoErr(t, err) + + var foundDetail bool + for _, v := range allClustersDetail { + if v.UUID == clusterID { + foundDetail = true + } + } + th.AssertEquals(t, foundDetail, true) + tools.PrintResource(t, newCluster) } diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index 11f7ba6f14..3fd28d10c6 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -53,6 +53,22 @@ Example to List Clusters fmt.Printf("%+v\n", cluster) } +Example to List Clusters with detailed information + + allPagesDetail, err := clusters.ListDetail(serviceClient, clusters.ListOpts{}).AllPages() + if err != nil { + panic(err) + } + + allClustersDetail, err := clusters.ExtractClusters(allPagesDetail) + if err != nil { + panic(err) + } + + for _, clusterDetail := range allClustersDetail { + fmt.Printf("%+v\n", clusterDetail) + } + Example to Update a Cluster updateOpts := []clusters.UpdateOptsBuilder{ diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 47088c4543..c6aff5ff71 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -109,6 +109,24 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } +// ListDetail returns a Pager which allows you to iterate over a collection of +// clusters with detailed information. +// It accepts a ListOptsBuilder, which allows you to sort the returned +// collection for greater efficiency. +func ListDetail(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listDetailURL(c) + if opts != nil { + query, err := opts.ToClustersListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return ClusterPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + type UpdateOp string const ( diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index e6baaf8bea..de86e411b9 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -230,6 +230,19 @@ func HandleListClusterSuccessfully(t *testing.T) { }) } +func HandleListDetailClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/detail", 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.Header().Add("X-OpenStack-Request-Id", requestUUID) + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ClusterListResponse) + }) +} + var UpdateResponse = fmt.Sprintf(` { "uuid":"%s" diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index a27f08d8d5..96cda0876a 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -87,6 +87,33 @@ func TestListClusters(t *testing.T) { } } +func TestListDetailClusters(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleListDetailClusterSuccessfully(t) + + count := 0 + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + clusters.ListDetail(sc, clusters.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := clusters.ExtractClusters(page) + th.AssertNoErr(t, err) + for idx := range actual { + actual[idx].CreatedAt = actual[idx].CreatedAt.UTC() + actual[idx].UpdatedAt = actual[idx].UpdatedAt.UTC() + } + th.AssertDeepEquals(t, ExpectedClusters, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + func TestUpdateCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/containerinfra/v1/clusters/urls.go b/openstack/containerinfra/v1/clusters/urls.go index 9cc64893e9..6d132f74b8 100644 --- a/openstack/containerinfra/v1/clusters/urls.go +++ b/openstack/containerinfra/v1/clusters/urls.go @@ -30,6 +30,10 @@ func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("clusters") } +func listDetailURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("clusters", "detail") +} + func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } From 98bc0d059b0783212ccb786782aace186e2cc162 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Tue, 12 Feb 2019 10:23:58 -0500 Subject: [PATCH 0661/2296] Baremetal v1 API: Nodes (#1431) * Add Bare Metal V1 API for Node CRUD * Fix docs and PATCH, add Owner to Node * Provide ListDetail function --- acceptance/clients/clients.go | 21 + .../openstack/baremetal/v1/baremetal.go | 41 + .../openstack/baremetal/v1/nodes_test.go | 66 ++ openstack/baremetal/v1/nodes/doc.go | 79 ++ openstack/baremetal/v1/nodes/requests.go | 303 ++++++++ openstack/baremetal/v1/nodes/results.go | 225 ++++++ openstack/baremetal/v1/nodes/testing/doc.go | 2 + .../baremetal/v1/nodes/testing/fixtures.go | 710 ++++++++++++++++++ .../v1/nodes/testing/requests_test.go | 154 ++++ openstack/baremetal/v1/nodes/urls.go | 27 + openstack/client.go | 6 + service_client.go | 2 + 12 files changed, 1636 insertions(+) create mode 100644 acceptance/openstack/baremetal/v1/baremetal.go create mode 100644 acceptance/openstack/baremetal/v1/nodes_test.go create mode 100644 openstack/baremetal/v1/nodes/doc.go create mode 100644 openstack/baremetal/v1/nodes/requests.go create mode 100644 openstack/baremetal/v1/nodes/results.go create mode 100644 openstack/baremetal/v1/nodes/testing/doc.go create mode 100644 openstack/baremetal/v1/nodes/testing/fixtures.go create mode 100644 openstack/baremetal/v1/nodes/testing/requests_test.go create mode 100644 openstack/baremetal/v1/nodes/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index fc495c0fc4..a0b5b33e40 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -254,6 +254,27 @@ func NewComputeV2Client() (*gophercloud.ServiceClient, error) { }) } +// NewBareMetalV1Client returns a *ServiceClient for making calls +// to the OpenStack Bare Metal v1 API. An error will be returned +// if authentication or client creation was not possible. +func NewBareMetalV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewBareMetalV1(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} + // NewDBV1Client returns a *ServiceClient for making calls // to the OpenStack Database v1 API. An error will be returned // if authentication or client creation was not possible. diff --git a/acceptance/openstack/baremetal/v1/baremetal.go b/acceptance/openstack/baremetal/v1/baremetal.go new file mode 100644 index 0000000000..31a18bbf03 --- /dev/null +++ b/acceptance/openstack/baremetal/v1/baremetal.go @@ -0,0 +1,41 @@ +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" +) + +// CreateNode creates a basic node with a randomly generated name. +func CreateNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, error) { + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create bare metal node: %s", name) + + node, err := nodes.Create(client, nodes.CreateOpts{ + Name: name, + Driver: "ipmi", + BootInterface: "pxe", + DriverInfo: map[string]interface{}{ + "ipmi_port": "6230", + "ipmi_username": "admin", + "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", + "ipmi_address": "192.168.122.1", + "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", + "ipmi_password": "admin", + }, + }).Extract() + + return node, err +} + +// DeleteNode deletes a bare metal node via its UUID. +func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Node) { + err := nodes.Delete(client, node.UUID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete node %s: %s", node.UUID, err) + } + + t.Logf("Deleted server: %s", node.UUID) +} diff --git a/acceptance/openstack/baremetal/v1/nodes_test.go b/acceptance/openstack/baremetal/v1/nodes_test.go new file mode 100644 index 0000000000..6654011a58 --- /dev/null +++ b/acceptance/openstack/baremetal/v1/nodes_test.go @@ -0,0 +1,66 @@ +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/pagination" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNodesCreateDestroy(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) + + found := false + err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + nodeList, err := nodes.ExtractNodes(page) + if err != nil { + return false, err + } + + for _, n := range nodeList { + if n.UUID == node.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + + th.AssertEquals(t, found, true) +} + +func TestNodesUpdate(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) + + updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ + nodes.UpdateOperation{ + Op: nodes.ReplaceOp, + Path: "/maintenance", + Value: "true", + }, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.Maintenance, true) +} diff --git a/openstack/baremetal/v1/nodes/doc.go b/openstack/baremetal/v1/nodes/doc.go new file mode 100644 index 0000000000..1199fc09c3 --- /dev/null +++ b/openstack/baremetal/v1/nodes/doc.go @@ -0,0 +1,79 @@ +package nodes + +/* +Package nodes provides information and interaction with the nodes API +resource in the OpenStack Bare Metal service. + + // Example to List Nodes with Detail + nodes.ListDetail(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + nodeList, err := nodes.ExtractNodes(page) + if err != nil { + return false, err + } + + for _, n := range nodeList { + // Do something + } + + return true, nil + }) + + // Example to List Nodes + nodes.List(client, nodes.ListOpts{ + ProvisionState: Deploying, + Fields: []string{"name"}, + }).EachPage(func(page pagination.Page) (bool, error) { + nodeList, err := nodes.ExtractNodes(page) + if err != nil { + return false, err + } + + for _, n := range nodeList { + // Do something + } + + return true, nil + }) + + // Example to Create Node + createNode, err := nodes.Create(client, nodes.CreateOpts{ + Driver: "ipmi", + BootInterface: "pxe", + Name: "coconuts", + DriverInfo: map[string]interface{}{ + "ipmi_port": "6230", + "ipmi_username": "admin", + "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", + "ipmi_address": "192.168.122.1", + "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", + "ipmi_password": "admin", + }, + }).Extract() + if err != nil { + panic(err) + } + + // Example to Get Node + showNode, err := nodes.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() + if err != nil { + panic(err) + } + + // Example to Update Node + updateNode, err := nodes.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", nodes.UpdateOpts{ + nodes.UpdateOperation{ + Op: ReplaceOp, + Path: "/maintenance", + Value: "true", + }, + }).Extract() + if err != nil { + panic(err) + } + + // Example to Delete Node + err = nodes.Delete(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() + if err != nil { + panic(err) + } +*/ diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go new file mode 100644 index 0000000000..7547a989ec --- /dev/null +++ b/openstack/baremetal/v1/nodes/requests.go @@ -0,0 +1,303 @@ +package nodes + +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 { + ToNodeListQuery() (string, error) + ToNodeListDetailQuery() (string, error) +} + +// Provision state reports the current provision state of the node, these are only used in filtering +type ProvisionState string + +const ( + Enroll ProvisionState = "enroll" + Verifying = "verifying" + Manageable = "manageable" + Available = "available" + Active = "active" + DeployWait = "wait call-back" + Deploying = "deploying" + DeployFail = "deploy failed" + DeployDone = "deploy complete" + Deleting = "deleting" + Deleted = "deleted" + Cleaning = "cleaning" + CleanWait = "clean wait" + CleanFail = "clean failed" + Error = "error" + Rebuild = "rebuild" + Inpsecting = "inspecting" + InspectFail = "inspect failed" + InspectWait = "inspect wait" + Adopting = "adopting" + AdoptFail = "adopt failed" + Rescue = "rescue" + RescueFail = "rescue failed" + Rescuing = "rescuing" + UnrescueFail = "unrescue failed" +) + +// 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 node attributes you want to see returned. Marker and Limit are used +// for pagination. +type ListOpts struct { + // Filter the list by specific instance UUID + InstanceUUID string `q:"instance_uuid"` + + // Filter the list by chassis UUID + ChassisUUID string `q:"chassis_uuid"` + + // Filter the list by maintenance set to True or False + Maintenance bool `q:"maintenance"` + + // Nodes which are, or are not, associated with an instance_uuid. + Associated bool `q:"associated"` + + // Only return those with the specified provision_state. + ProvisionState ProvisionState `q:"provision_state"` + + // Filter the list with the specified driver. + Driver string `q:"driver"` + + // Filter the list with the specified resource class. + ResourceClass string `q:"resource_class"` + + // Filter the list with the specified conductor_group. + ConductorGroup string `q:"conductor_group"` + + // Filter the list with the specified fault. + Fault string `q:"fault"` + + // 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"` + + // A string or UUID of the tenant who owns the baremetal node. + Owner string `q:"owner"` +} + +// ToNodeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToNodeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list nodes accessible to you. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToNodeListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return NodePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// ToNodeListDetailQuery formats a ListOpts into a query string for the list details API. +func (opts ListOpts) ToNodeListDetailQuery() (string, error) { + // Detail endpoint can't filter by Fields + if len(opts.Fields) > 0 { + return "", fmt.Errorf("fields is not a valid option when getting a detailed listing of nodes") + } + + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Return a list of bare metal Nodes with complete details. Some filtering is possible by passing in flags in ListOpts, +// but you cannot limit by the fields returned. +func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + // This URL is deprecated. In the future, we should compare the microversion and if >= 1.43, hit the listURL + // with ListOpts{Detail: true,} + url := listDetailURL(client) + if opts != nil { + query, err := opts.ToNodeListDetailQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return NodePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get requests details on a single node, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToNodeCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies node creation parameters. +type CreateOpts struct { + // The boot interface for a Node, e.g. “pxe”. + BootInterface string `json:"boot_interface,omitempty"` + + // The conductor group for a node. Case-insensitive string up to 255 characters, containing a-z, 0-9, _, -, and .. + ConductorGroup string `json:"conductor_group,omitempty"` + + // The console interface for a node, e.g. “no-console”. + ConsoleInterface string `json:"console_interface,omitempty"` + + // The deploy interface for a node, e.g. “iscsi”. + DeployInterface string `json:"deploy_interface,omitempty"` + + // All the metadata required by the driver to manage this Node. List of fields varies between drivers, and can + // be retrieved from the /v1/drivers//properties resource. + DriverInfo map[string]interface{} `json:"driver_info,omitempty"` + + // name of the driver used to manage this Node. + Driver string `json:"driver,omitempty"` + + // A set of one or more arbitrary metadata key and value pairs. + Extra map[string]interface{} `json:"extra,omitempty"` + + // The interface used for node inspection, e.g. “no-inspect”. + InspectInterface string `json:"inspect_interface,omitempty"` + + // Interface for out-of-band node management, e.g. “ipmitool”. + ManagementInterface string `json:"management_interface,omitempty"` + + // Human-readable identifier for the Node resource. May be undefined. Certain words are reserved. + Name string `json:"name,omitempty"` + + // Which Network Interface provider to use when plumbing the network connections for this Node. + NetworkInterface string `json:"network_interface,omitempty"` + + // Interface used for performing power actions on the node, e.g. “ipmitool”. + PowerInterface string `json:"power_interface,omitempty"` + + // Physical characteristics of this Node. Populated during inspection, if performed. Can be edited via the REST + // API at any time. + Properties map[string]interface{} `json:"properties,omitempty"` + + // Interface used for configuring RAID on this node, e.g. “no-raid”. + RAIDInterface string `json:"raid_interface,omitempty"` + + // The interface used for node rescue, e.g. “no-rescue”. + RescueInterface string `json:"rescue_interface,omitempty"` + + // A string which can be used by external schedulers to identify this Node as a unit of a specific type + // of resource. + ResourceClass string `json:"resource_class,omitempty"` + + // Interface used for attaching and detaching volumes on this node, e.g. “cinder”. + StorageInterface string `json:"storage_interface,omitempty"` + + // The UUID for the resource. + UUID string `json:"uuid,omitempty"` + + // Interface for vendor-specific functionality on this node, e.g. “no-vendor”. + VendorInterface string `json:"vendor_interface,omitempty"` + + // A string or UUID of the tenant who owns the baremetal node. + Owner string `json:"owner,omitempty"` +} + +// ToNodeCreateMap assembles a request body based on the contents of a CreateOpts. +func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return body, nil +} + +// Create requests a node to be created +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + reqBody, err := opts.ToNodeCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(createURL(client), reqBody, &r.Body, nil) + return +} + +type Patch interface { + ToNodeUpdateMap() map[string]interface{} +} + +// UpdateOpts is a slice of Patches used to update a node +type UpdateOpts []Patch + +type UpdateOp string + +const ( + ReplaceOp UpdateOp = "replace" + AddOp UpdateOp = "add" + RemoveOp UpdateOp = "remove" +) + +type UpdateOperation struct { + Op UpdateOp `json:"op,required"` + Path string `json:"path,required"` + Value string `json:"value,omitempty"` +} + +func (opts UpdateOperation) ToNodeUpdateMap() map[string]interface{} { + return map[string]interface{}{ + "op": opts.Op, + "path": opts.Path, + "value": opts.Value, + } +} + +// Update requests that a node be updated +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + body := make([]map[string]interface{}, len(opts)) + for i, patch := range opts { + body[i] = patch.ToNodeUpdateMap() + } + + resp, err := client.Request("PATCH", updateURL(client, id), &gophercloud.RequestOpts{ + JSONBody: &body, + OkCodes: []int{200}, + }) + + r.Body = resp.Body + r.Header = resp.Header + r.Err = err + + return +} + +// Delete requests that a node be removed +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go new file mode 100644 index 0000000000..9bd3a96085 --- /dev/null +++ b/openstack/baremetal/v1/nodes/results.go @@ -0,0 +1,225 @@ +package nodes + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type nodeResult struct { + gophercloud.Result +} + +// Extract interprets any nodeResult as a Node, if possible. +func (r nodeResult) Extract() (*Node, error) { + var s Node + err := r.ExtractInto(&s) + return &s, err +} + +func (r nodeResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "") +} + +func ExtractNodesInto(r pagination.Page, v interface{}) error { + return r.(NodePage).Result.ExtractIntoSlicePtr(v, "nodes") +} + +// Node represents a node in the OpenStack Bare Metal API. +type Node struct { + // UUID for the resource. + UUID string `json:"uuid"` + + // Identifier for the Node resource. May be undefined. Certain words are reserved. + Name string `json:"name"` + + // Current power state of this Node. Usually, “power on” or “power off”, but may be “None” + // if Ironic is unable to determine the power state (eg, due to hardware failure). + PowerState string `json:"power_state"` + + // A power state transition has been requested, this field represents the requested (ie, “target”) + // state either “power on”, “power off”, “rebooting”, “soft power off” or “soft rebooting”. + TargetPowerState string `json:"target_power_state"` + + // Current provisioning state of this Node. + ProvisionState string `json:"provision_state"` + + // A provisioning action has been requested, this field represents the requested (ie, “target”) state. Note + // that a Node may go through several states during its transition to this target state. For instance, when + // requesting an instance be deployed to an AVAILABLE Node, the Node may go through the following state + // change progression: AVAILABLE -> DEPLOYING -> DEPLOYWAIT -> DEPLOYING -> ACTIVE + TargetProvisionState string `json:"target_provision_state"` + + // Whether or not this Node is currently in “maintenance mode”. Setting a Node into maintenance mode removes it + // from the available resource pool and halts some internal automation. This can happen manually (eg, via an API + // request) or automatically when Ironic detects a hardware fault that prevents communication with the machine. + Maintenance bool `json:"maintenance"` + + // Description of the reason why this Node was placed into maintenance mode + MaintenanceReason string `json:"maintenance_reason"` + + // Fault indicates the active fault detected by ironic, typically the Node is in “maintenance mode”. None means no + // fault has been detected by ironic. “power failure” indicates ironic failed to retrieve power state from this + // node. There are other possible types, e.g., “clean failure” and “rescue abort failure”. + Fault string `json:"fault"` + + // Error from the most recent (last) transaction that started but failed to finish. + LastError string `json:"last_error"` + + // Name of an Ironic Conductor host which is holding a lock on this node, if a lock is held. Usually “null”, + // but this field can be useful for debugging. + Reservation string `json:"reservation"` + + // Name of the driver. + Driver string `json:"driver"` + + // The metadata required by the driver to manage this Node. List of fields varies between drivers, and can be + // retrieved from the /v1/drivers//properties resource. + DriverInfo map[string]interface{} `json:"driver_info"` + + // Metadata set and stored by the Node’s driver. This field is read-only. + DriverInternalInfo map[string]interface{} `json:"driver_internal_info"` + + // Characteristics of this Node. Populated by ironic-inspector during inspection. May be edited via the REST + // API at any time. + Properties map[string]interface{} `json:"properties"` + + // Used to customize the deployed image. May include root partition size, a base 64 encoded config drive, and other + // metadata. Note that this field is erased automatically when the instance is deleted (this is done by requesting + // the Node provision state be changed to DELETED). + InstanceInfo map[string]interface{} `json:"instance_info"` + + // ID of the Nova instance associated with this Node. + InstanceUUID string `json:"instance_uuid"` + + // ID of the chassis associated with this Node. May be empty or None. + ChassisUUID string `json:"chassis_uuid"` + + // Set of one or more arbitrary metadata key and value pairs. + Extra map[string]interface{} `json:"extra"` + + // Whether console access is enabled or disabled on this node. + ConsoleEnabled bool `json:"console_enabled"` + + // The current RAID configuration of the node. Introduced with the cleaning feature. + RAIDConfig map[string]interface{} `json:"raid_config"` + + // The requested RAID configuration of the node, which will be applied when the Node next transitions + // through the CLEANING state. Introduced with the cleaning feature. + TargetRAIDConfig map[string]interface{} `json:"target_raid_config"` + + // Current clean step. Introduced with the cleaning feature. + CleanStep map[string]interface{} `json:"clean_step"` + + // Current deploy step. + DeployStep map[string]interface{} `json:"deploy_step"` + + // String which can be used by external schedulers to identify this Node as a unit of a specific type of resource. + // For more details, see: https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html + ResourceClass string `json:"resource_class"` + + // Boot interface for a Node, e.g. “pxe”. + BootInterface string `json:"boot_interface"` + + // Console interface for a node, e.g. “no-console”. + ConsoleInterface string `json:"console_interface"` + + // Deploy interface for a node, e.g. “iscsi”. + DeployInterface string `json:"deploy_interface"` + + // Interface used for node inspection, e.g. “no-inspect”. + InspectInterface string `json:"inspect_interface"` + + // For out-of-band node management, e.g. “ipmitool”. + ManagementInterface string `json:"management_interface"` + + // Network Interface provider to use when plumbing the network connections for this Node. + NetworkInterface string `json:"network_interface"` + + // used for performing power actions on the node, e.g. “ipmitool”. + PowerInterface string `json:"power_interface"` + + // Used for configuring RAID on this node, e.g. “no-raid”. + RAIDInterface string `json:"raid_interface"` + + // Interface used for node rescue, e.g. “no-rescue”. + RescueInterface string `json:"rescue_interface"` + + // Used for attaching and detaching volumes on this node, e.g. “cinder”. + StorageInterface string `json:"storage_interface"` + + // Array of traits for this node. + Traits []string `json:"traits"` + + // For vendor-specific functionality on this node, e.g. “no-vendor”. + VendorInterface string `json:"vendor_interface"` + + // Conductor group for a node. Case-insensitive string up to 255 characters, containing a-z, 0-9, _, -, and .. + ConductorGroup string `json:"conductor_group"` + + // The node is protected from undeploying, rebuilding and deletion. + Protected bool `json:"protected"` + + // Reason the node is marked as protected. + ProtectedReason string `json:"protected_reason"` + + // A string or UUID of the tenant who owns the baremetal node. + Owner string `json:"owner"` +} + +// NodePage 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 ExtractNodes call. +type NodePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a page contains no Node results. +func (r NodePage) IsEmpty() (bool, error) { + s, err := ExtractNodes(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r NodePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"nodes_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractNodes interprets the results of a single page from a List() call, +// producing a slice of Node entities. +func ExtractNodes(r pagination.Page) ([]Node, error) { + var s []Node + err := ExtractNodesInto(r, &s) + return s, err +} + +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as a Node. +type GetResult struct { + nodeResult +} + +// CreateResult is the response from a Create operation. +type CreateResult struct { + nodeResult +} + +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Node. +type UpdateResult struct { + nodeResult +} + +// 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/baremetal/v1/nodes/testing/doc.go b/openstack/baremetal/v1/nodes/testing/doc.go new file mode 100644 index 0000000000..df837788f2 --- /dev/null +++ b/openstack/baremetal/v1/nodes/testing/doc.go @@ -0,0 +1,2 @@ +// nodes unit tests +package testing diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go new file mode 100644 index 0000000000..d915ff3237 --- /dev/null +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -0,0 +1,710 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// NodeListBody contains the canned body of a nodes.List response, without detail. +const NodeListBody = ` + { + "nodes": [ + { + "instance_uuid": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", + "rel": "bookmark" + } + ], + "maintenance": false, + "name": "foo", + "power_state": null, + "provision_state": "enroll" + }, + { + "instance_uuid": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", + "rel": "bookmark" + } + ], + "maintenance": false, + "name": "bar", + "power_state": null, + "provision_state": "enroll" + }, + { + "instance_uuid": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", + "rel": "bookmark" + } + ], + "maintenance": false, + "name": "baz", + "power_state": null, + "provision_state": "enroll" + } + ] +} +` + +// NodeListDetailBody contains the canned body of a nodes.ListDetail response. +const NodeListDetailBody = ` + { + "nodes": [ + { + "bios_interface": "no-bios", + "boot_interface": "pxe", + "chassis_uuid": null, + "clean_step": {}, + "conductor_group": "", + "console_enabled": false, + "console_interface": "no-console", + "created_at": "2019-01-31T19:59:28+00:00", + "deploy_interface": "iscsi", + "deploy_step": {}, + "driver": "ipmi", + "driver_info": { + "ipmi_port": "6230", + "ipmi_username": "admin", + "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", + "ipmi_address": "192.168.122.1", + "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", + "ipmi_password": "admin" + + }, + "driver_internal_info": {}, + "extra": {}, + "fault": null, + "inspect_interface": "no-inspect", + "inspection_finished_at": null, + "inspection_started_at": null, + "instance_info": {}, + "instance_uuid": null, + "last_error": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", + "rel": "bookmark" + } + ], + "maintenance": false, + "maintenance_reason": null, + "management_interface": "ipmitool", + "name": "foo", + "network_interface": "flat", + "portgroups": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", + "rel": "bookmark" + } + ], + "ports": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", + "rel": "bookmark" + } + ], + "power_interface": "ipmitool", + "power_state": null, + "properties": {}, + "provision_state": "enroll", + "provision_updated_at": null, + "raid_config": {}, + "raid_interface": "no-raid", + "rescue_interface": "no-rescue", + "reservation": null, + "resource_class": null, + "states": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", + "rel": "bookmark" + } + ], + "storage_interface": "noop", + "target_power_state": null, + "target_provision_state": null, + "target_raid_config": {}, + "traits": [], + "updated_at": null, + "uuid": "d2630783-6ec8-4836-b556-ab427c4b581e", + "vendor_interface": "ipmitool", + "volume": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", + "rel": "bookmark" + } + ] + }, + { + "bios_interface": "no-bios", + "boot_interface": "pxe", + "chassis_uuid": null, + "clean_step": {}, + "conductor_group": "", + "console_enabled": false, + "console_interface": "no-console", + "created_at": "2019-01-31T19:59:29+00:00", + "deploy_interface": "iscsi", + "deploy_step": {}, + "driver": "ipmi", + "driver_info": {}, + "driver_internal_info": {}, + "extra": {}, + "fault": null, + "inspect_interface": "no-inspect", + "inspection_finished_at": null, + "inspection_started_at": null, + "instance_info": {}, + "instance_uuid": null, + "last_error": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", + "rel": "bookmark" + } + ], + "maintenance": false, + "maintenance_reason": null, + "management_interface": "ipmitool", + "name": "bar", + "network_interface": "flat", + "portgroups": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/portgroups", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/portgroups", + "rel": "bookmark" + } + ], + "ports": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/ports", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/ports", + "rel": "bookmark" + } + ], + "power_interface": "ipmitool", + "power_state": null, + "properties": {}, + "provision_state": "enroll", + "provision_updated_at": null, + "raid_config": {}, + "raid_interface": "no-raid", + "rescue_interface": "no-rescue", + "reservation": null, + "resource_class": null, + "states": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/states", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/states", + "rel": "bookmark" + } + ], + "storage_interface": "noop", + "target_power_state": null, + "target_provision_state": null, + "target_raid_config": {}, + "traits": [], + "updated_at": null, + "uuid": "08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", + "vendor_interface": "ipmitool", + "volume": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/volume", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/volume", + "rel": "bookmark" + } + ] + }, + { + "bios_interface": "no-bios", + "boot_interface": "pxe", + "chassis_uuid": null, + "clean_step": {}, + "conductor_group": "", + "console_enabled": false, + "console_interface": "no-console", + "created_at": "2019-01-31T19:59:30+00:00", + "deploy_interface": "iscsi", + "deploy_step": {}, + "driver": "ipmi", + "driver_info": {}, + "driver_internal_info": {}, + "extra": {}, + "fault": null, + "inspect_interface": "no-inspect", + "inspection_finished_at": null, + "inspection_started_at": null, + "instance_info": {}, + "instance_uuid": null, + "last_error": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", + "rel": "bookmark" + } + ], + "maintenance": false, + "maintenance_reason": null, + "management_interface": "ipmitool", + "name": "baz", + "network_interface": "flat", + "portgroups": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/portgroups", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/portgroups", + "rel": "bookmark" + } + ], + "ports": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/ports", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/ports", + "rel": "bookmark" + } + ], + "power_interface": "ipmitool", + "power_state": null, + "properties": {}, + "provision_state": "enroll", + "provision_updated_at": null, + "raid_config": {}, + "raid_interface": "no-raid", + "rescue_interface": "no-rescue", + "reservation": null, + "resource_class": null, + "states": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/states", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/states", + "rel": "bookmark" + } + ], + "storage_interface": "noop", + "target_power_state": null, + "target_provision_state": null, + "target_raid_config": {}, + "traits": [], + "updated_at": null, + "uuid": "c9afd385-5d89-4ecb-9e1c-68194da6b474", + "vendor_interface": "ipmitool", + "volume": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/volume", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/volume", + "rel": "bookmark" + } + ] + } + ] +} +` + +// SingleNodeBody is the canned body of a Get request on an existing node. +const SingleNodeBody = ` +{ + "bios_interface": "no-bios", + "boot_interface": "pxe", + "chassis_uuid": null, + "clean_step": {}, + "conductor_group": "", + "console_enabled": false, + "console_interface": "no-console", + "created_at": "2019-01-31T19:59:28+00:00", + "deploy_interface": "iscsi", + "deploy_step": {}, + "driver": "ipmi", + "driver_info": { + "ipmi_port": "6230", + "ipmi_username": "admin", + "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", + "ipmi_address": "192.168.122.1", + "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", + "ipmi_password": "admin" + }, + "driver_internal_info": {}, + "extra": {}, + "fault": null, + "inspect_interface": "no-inspect", + "inspection_finished_at": null, + "inspection_started_at": null, + "instance_info": {}, + "instance_uuid": null, + "last_error": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", + "rel": "bookmark" + } + ], + "maintenance": false, + "maintenance_reason": null, + "management_interface": "ipmitool", + "name": "foo", + "network_interface": "flat", + "portgroups": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", + "rel": "bookmark" + } + ], + "ports": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", + "rel": "bookmark" + } + ], + "power_interface": "ipmitool", + "power_state": null, + "properties": {}, + "provision_state": "enroll", + "provision_updated_at": null, + "raid_config": {}, + "raid_interface": "no-raid", + "rescue_interface": "no-rescue", + "reservation": null, + "resource_class": null, + "states": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", + "rel": "bookmark" + } + ], + "storage_interface": "noop", + "target_power_state": null, + "target_provision_state": null, + "target_raid_config": {}, + "traits": [], + "updated_at": null, + "uuid": "d2630783-6ec8-4836-b556-ab427c4b581e", + "vendor_interface": "ipmitool", + "volume": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", + "rel": "bookmark" + } + ] +} +` + +var ( + NodeFoo = nodes.Node{ + UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", + Name: "foo", + PowerState: "", + TargetPowerState: "", + ProvisionState: "enroll", + TargetProvisionState: "", + Maintenance: false, + MaintenanceReason: "", + Fault: "", + LastError: "", + Reservation: "", + Driver: "ipmi", + DriverInfo: map[string]interface{}{ + "ipmi_port": "6230", + "ipmi_username": "admin", + "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", + "ipmi_address": "192.168.122.1", + "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", + "ipmi_password": "admin", + }, + DriverInternalInfo: map[string]interface{}{}, + Properties: map[string]interface{}{}, + InstanceInfo: map[string]interface{}{}, + InstanceUUID: "", + ChassisUUID: "", + Extra: map[string]interface{}{}, + ConsoleEnabled: false, + RAIDConfig: map[string]interface{}{}, + TargetRAIDConfig: map[string]interface{}{}, + CleanStep: map[string]interface{}{}, + DeployStep: map[string]interface{}{}, + ResourceClass: "", + BootInterface: "pxe", + ConsoleInterface: "no-console", + DeployInterface: "iscsi", + InspectInterface: "no-inspect", + ManagementInterface: "ipmitool", + NetworkInterface: "flat", + PowerInterface: "ipmitool", + RAIDInterface: "no-raid", + RescueInterface: "no-rescue", + StorageInterface: "noop", + Traits: []string{}, + VendorInterface: "ipmitool", + ConductorGroup: "", + Protected: false, + ProtectedReason: "", + } + + NodeBar = nodes.Node{ + UUID: "08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", + Name: "bar", + PowerState: "", + TargetPowerState: "", + ProvisionState: "enroll", + TargetProvisionState: "", + Maintenance: false, + MaintenanceReason: "", + Fault: "", + LastError: "", + Reservation: "", + Driver: "ipmi", + DriverInfo: map[string]interface{}{}, + DriverInternalInfo: map[string]interface{}{}, + Properties: map[string]interface{}{}, + InstanceInfo: map[string]interface{}{}, + InstanceUUID: "", + ChassisUUID: "", + Extra: map[string]interface{}{}, + ConsoleEnabled: false, + RAIDConfig: map[string]interface{}{}, + TargetRAIDConfig: map[string]interface{}{}, + CleanStep: map[string]interface{}{}, + DeployStep: map[string]interface{}{}, + ResourceClass: "", + BootInterface: "pxe", + ConsoleInterface: "no-console", + DeployInterface: "iscsi", + InspectInterface: "no-inspect", + ManagementInterface: "ipmitool", + NetworkInterface: "flat", + PowerInterface: "ipmitool", + RAIDInterface: "no-raid", + RescueInterface: "no-rescue", + StorageInterface: "noop", + Traits: []string{}, + VendorInterface: "ipmitool", + ConductorGroup: "", + Protected: false, + ProtectedReason: "", + } + + NodeBaz = nodes.Node{ + UUID: "c9afd385-5d89-4ecb-9e1c-68194da6b474", + Name: "baz", + PowerState: "", + TargetPowerState: "", + ProvisionState: "enroll", + TargetProvisionState: "", + Maintenance: false, + MaintenanceReason: "", + Fault: "", + LastError: "", + Reservation: "", + Driver: "ipmi", + DriverInfo: map[string]interface{}{}, + DriverInternalInfo: map[string]interface{}{}, + Properties: map[string]interface{}{}, + InstanceInfo: map[string]interface{}{}, + InstanceUUID: "", + ChassisUUID: "", + Extra: map[string]interface{}{}, + ConsoleEnabled: false, + RAIDConfig: map[string]interface{}{}, + TargetRAIDConfig: map[string]interface{}{}, + CleanStep: map[string]interface{}{}, + DeployStep: map[string]interface{}{}, + ResourceClass: "", + BootInterface: "pxe", + ConsoleInterface: "no-console", + DeployInterface: "iscsi", + InspectInterface: "no-inspect", + ManagementInterface: "ipmitool", + NetworkInterface: "flat", + PowerInterface: "ipmitool", + RAIDInterface: "no-raid", + RescueInterface: "no-rescue", + StorageInterface: "noop", + Traits: []string{}, + VendorInterface: "ipmitool", + ConductorGroup: "", + Protected: false, + ProtectedReason: "", + } +) + +// HandleNodeListSuccessfully sets up the test server to respond to a server List request. +func HandleNodeListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes", 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, NodeListBody) + + case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": + fmt.Fprintf(w, `{ "servers": [] }`) + default: + t.Fatalf("/nodes invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleNodeListSuccessfully sets up the test server to respond to a server List request. +func HandleNodeListDetailSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/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") + r.ParseForm() + + fmt.Fprintf(w, NodeListDetailBody) + }) +} + +// HandleServerCreationSuccessfully sets up the test server to respond to a server creation request +// with a given response. +func HandleNodeCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/nodes", 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, `{ + "boot_interface": "pxe", + "driver": "ipmi", + "driver_info": { + "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", + "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", + "ipmi_address": "192.168.122.1", + "ipmi_password": "admin", + "ipmi_port": "6230", + "ipmi_username": "admin" + }, + "name": "foo" + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// HandleNodeDeletionSuccessfully sets up the test server to respond to a server deletion request. +func HandleNodeDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/asdfasdfasdf", 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 HandleNodeGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/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, SingleNodeBody) + }) +} + +func HandleNodeUpdateSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/nodes/1234asdf", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + 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, `[{"op": "replace", "path": "/driver", "value": "new-driver"}]`) + + fmt.Fprintf(w, response) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go new file mode 100644 index 0000000000..81a04b2858 --- /dev/null +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -0,0 +1,154 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListDetailNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeListDetailSuccessfully(t) + + pages := 0 + err := nodes.ListDetail(client.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := nodes.ExtractNodes(page) + if err != nil { + return false, err + } + + if len(actual) != 3 { + t.Fatalf("Expected 3 nodes, got %d", len(actual)) + } + th.CheckDeepEquals(t, NodeFoo, actual[0]) + th.CheckDeepEquals(t, NodeBar, actual[1]) + th.CheckDeepEquals(t, NodeBaz, actual[2]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeListSuccessfully(t) + + pages := 0 + err := nodes.List(client.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := nodes.ExtractNodes(page) + if err != nil { + return false, err + } + + if len(actual) != 3 { + t.Fatalf("Expected 3 nodes, got %d", len(actual)) + } + th.AssertEquals(t, "foo", actual[0].Name) + th.AssertEquals(t, "bar", actual[1].Name) + th.AssertEquals(t, "baz", actual[2].Name) + + 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 + opts := nodes.ListOpts{ + Fields: []string{"name", "uuid"}, + } + + _, err := opts.ToNodeListDetailQuery() + th.AssertEquals(t, err.Error(), "fields is not a valid option when getting a detailed listing of nodes") + + // Regular ListOpts can + query, err := opts.ToNodeListQuery() + th.AssertEquals(t, query, "?fields=name&fields=uuid") + th.AssertNoErr(t, err) +} + +func TestCreateNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeCreationSuccessfully(t, SingleNodeBody) + + actual, err := nodes.Create(client.ServiceClient(), nodes.CreateOpts{ + Name: "foo", + Driver: "ipmi", + BootInterface: "pxe", + DriverInfo: map[string]interface{}{ + "ipmi_port": "6230", + "ipmi_username": "admin", + "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", + "ipmi_address": "192.168.122.1", + "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", + "ipmi_password": "admin", + }, + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, NodeFoo, *actual) +} + +func TestDeleteNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeDeletionSuccessfully(t) + + res := nodes.Delete(client.ServiceClient(), "asdfasdfasdf") + th.AssertNoErr(t, res.Err) +} + +func TestGetNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeGetSuccessfully(t) + + c := client.ServiceClient() + actual, err := nodes.Get(c, "1234asdf").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, NodeFoo, *actual) +} + +func TestUpdateNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeUpdateSuccessfully(t, SingleNodeBody) + + c := client.ServiceClient() + actual, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ + nodes.UpdateOperation{ + Op: nodes.ReplaceOp, + Path: "/driver", + Value: "new-driver", + }, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, NodeFoo, *actual) + +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go new file mode 100644 index 0000000000..8defdea95f --- /dev/null +++ b/openstack/baremetal/v1/nodes/urls.go @@ -0,0 +1,27 @@ +package nodes + +import "github.com/gophercloud/gophercloud" + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("nodes") +} + +func listURL(client *gophercloud.ServiceClient) string { + return createURL(client) +} + +func listDetailURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("nodes", "detail") +} + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return deleteURL(client, id) +} + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return deleteURL(client, id) +} diff --git a/openstack/client.go b/openstack/client.go index 8fbd9dc7cf..064a70dbed 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -304,6 +304,12 @@ func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointO return sc, nil } +// NewBareMetalV1 creates a ServiceClient that may be used with the v1 +// bare metal package. +func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "baremetal") +} + // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 // object storage package. func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { diff --git a/service_client.go b/service_client.go index 2734510e1b..c889201f95 100644 --- a/service_client.go +++ b/service_client.go @@ -129,6 +129,8 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion case "volume": opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion + case "baremetal": + opts.MoreHeaders["X-OpenStack-Ironic-API-Version"] = client.Microversion } if client.Type != "" { From 892256c468586d66a6864daeee52441afc0f7e38 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 12 Feb 2019 18:17:53 +0000 Subject: [PATCH 0662/2296] Enable go modules (#1448) * Enable go modules * travis: Add go 1.11 as target --- .travis.yml | 12 +++++++----- go.mod | 7 +++++++ go.sum | 8 ++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/.travis.yml b/.travis.yml index ef5629be6d..0955f170ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,19 @@ language: go sudo: false install: -- go get golang.org/x/crypto/ssh -- go get -v -tags 'fixtures acceptance' ./... -- go get github.com/wadey/gocovmerge -- go get github.com/mattn/goveralls -- go get golang.org/x/tools/cmd/goimports +- GO111MODULE=off go get golang.org/x/crypto/ssh +- GO111MODULE=off go get -v -tags 'fixtures acceptance' ./... +- GO111MODULE=off go get github.com/wadey/gocovmerge +- GO111MODULE=off go get github.com/mattn/goveralls +- GO111MODULE=off go get golang.org/x/tools/cmd/goimports go: - "1.10" +- "1.11" - "tip" env: global: - secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ=" + - GO111MODULE=on before_script: - go vet ./... script: diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000..d1ee3b472e --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module github.com/gophercloud/gophercloud + +require ( + golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 + golang.org/x/sys v0.0.0-20190209173611-3b5209105503 // indirect + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000..33cb0be8aa --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From b18d22ae2c8b9ef046c980e62422369b5aea953f Mon Sep 17 00:00:00 2001 From: Luke Griffith Date: Wed, 13 Feb 2019 20:21:28 +0000 Subject: [PATCH 0663/2296] Adding property VipNetworkId. (#1422) * adding property VipNetworkId. * removed comments * go fmt --- openstack/loadbalancer/v2/loadbalancers/requests.go | 8 +++++++- openstack/loadbalancer/v2/loadbalancers/results.go | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index e9147350da..05b0367f69 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -24,6 +24,7 @@ type ListOpts struct { VipAddress string `q:"vip_address"` VipPortID string `q:"vip_port_id"` VipSubnetID string `q:"vip_subnet_id"` + VipNetworkID string `q:"vip_network_id"` ID string `q:"id"` OperatingStatus string `q:"operating_status"` Name string `q:"name"` @@ -80,11 +81,16 @@ type CreateOpts struct { // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` - // The network on which to allocate the Loadbalancer's address. A project can + // The subnet on which to allocate the Loadbalancer's address. A project can // only create Loadbalancers on networks authorized by policy (e.g. networks // that belong to them or networks that are shared). VipSubnetID string `json:"vip_subnet_id" required:"true"` + // The network on which to allocate the Loadbalancer's address. A tenant can + // only create Loadbalancers on networks authorized by policy (e.g. networks + // that belong to them or networks that are shared). + VipNetworkID string `json:"vip_network_id,omitempty"` + // ProjectID is the UUID of the project who owns the Loadbalancer. // Only administrative users can specify a project UUID other than their own. ProjectID string `json:"project_id,omitempty"` diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 916c3693a9..73372938cf 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -35,6 +35,10 @@ type LoadBalancer struct { // Loadbalancer address. VipSubnetID string `json:"vip_subnet_id"` + // The UUID of the network on which to allocate the virtual IP for the + // Loadbalancer address. + VipNetworkID string `json:"vip_network_id"` + // The unique ID for the LoadBalancer. ID string `json:"id"` From 1bb91ee3a92e96de241f4f17f06413fb1e2154ef Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 15 Feb 2019 03:31:30 +0100 Subject: [PATCH 0664/2296] Identity V3: Add application credentials package (#1443) * Identity V3: Add application credentials package * Identity V3: Use maps for roles in application credentials acceptance tests * Remove the code commented out * Use map of strings in acceptance tests * Mark name field as required --- .../v3/applicationcredentials_test.go | 168 ++++++++ .../v3/applicationcredentials/requests.go | 95 ++++ .../v3/applicationcredentials/results.go | 127 ++++++ .../testing/fixtures.go | 404 ++++++++++++++++++ .../testing/requests_test.go | 124 ++++++ .../v3/applicationcredentials/urls.go | 19 + 6 files changed, 937 insertions(+) create mode 100644 acceptance/openstack/identity/v3/applicationcredentials_test.go create mode 100644 openstack/identity/v3/applicationcredentials/requests.go create mode 100644 openstack/identity/v3/applicationcredentials/results.go create mode 100644 openstack/identity/v3/applicationcredentials/testing/fixtures.go create mode 100644 openstack/identity/v3/applicationcredentials/testing/requests_test.go create mode 100644 openstack/identity/v3/applicationcredentials/urls.go diff --git a/acceptance/openstack/identity/v3/applicationcredentials_test.go b/acceptance/openstack/identity/v3/applicationcredentials_test.go new file mode 100644 index 0000000000..c442daa097 --- /dev/null +++ b/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -0,0 +1,168 @@ +// +build acceptance + +package v3 + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestApplicationCredentialsCRD(t *testing.T) { + clients.RequireAdmin(t) + + // 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{} + for _, role := range roles { + rolesMap[role.Name] = role.Name + rolesMap[role.ID] = role.ID + } + return rolesMap + } + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + authOptions := tokens.AuthOptions{ + Username: ao.Username, + Password: ao.Password, + DomainName: ao.DomainName, + DomainID: ao.DomainID, + // We need a scope to get the token roles list + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, + } + + token, err := tokens.Create(client, &authOptions).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) + + user, err := tokens.Get(client, token.ID).ExtractUser() + th.AssertNoErr(t, err) + tools.PrintResource(t, user) + + roles, err := tokens.Get(client, token.ID).ExtractRoles() + th.AssertNoErr(t, err) + tools.PrintResource(t, roles) + + project, err := tokens.Get(client, token.ID).ExtractProject() + th.AssertNoErr(t, err) + tools.PrintResource(t, project) + + // prepare create parameters + var apRoles []applicationcredentials.Role + for i, role := range roles { + if i%2 == 0 { + apRoles = append(apRoles, applicationcredentials.Role{Name: role.Name}) + } else { + apRoles = append(apRoles, applicationcredentials.Role{ID: role.ID}) + } + if i > 4 { + break + } + } + tools.PrintResource(t, apRoles) + + // restricted, limited TTL, with limited roles, autogenerated secret + createOpts := applicationcredentials.CreateOpts{ + Name: "test-ac", + Description: "test application credential", + Roles: apRoles, + ExpiresAt: "2119-01-01T12:12:12Z", + } + + applicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() + th.AssertNoErr(t, err) + defer applicationcredentials.Delete(client, user.ID, applicationCredential.ID) + tools.PrintResource(t, applicationCredential) + + if applicationCredential.Secret == "" { + t.Fatalf("Application credential secret was not generated") + } + + th.AssertEquals(t, applicationCredential.ExpiresAt.UTC().Format(time.RFC3339), createOpts.ExpiresAt) + th.AssertEquals(t, applicationCredential.Name, createOpts.Name) + th.AssertEquals(t, applicationCredential.Description, createOpts.Description) + th.AssertEquals(t, applicationCredential.Unrestricted, false) + th.AssertEquals(t, applicationCredential.ProjectID, project.ID) + + checkACroles := rolesToMap(applicationCredential.Roles) + for i, role := range roles { + if i%2 == 0 { + th.AssertEquals(t, checkACroles[role.Name], role.Name) + } else { + th.AssertEquals(t, checkACroles[role.ID], role.ID) + } + if i > 4 { + break + } + } + + // Get an application credential + getApplicationCredential, err := applicationcredentials.Get(client, user.ID, applicationCredential.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getApplicationCredential) + + if getApplicationCredential.Secret != "" { + t.Fatalf("Application credential secret should not be returned by a GET request") + } + + th.AssertEquals(t, getApplicationCredential.ExpiresAt.UTC().Format(time.RFC3339), createOpts.ExpiresAt) + th.AssertEquals(t, getApplicationCredential.Name, createOpts.Name) + th.AssertEquals(t, getApplicationCredential.Description, createOpts.Description) + th.AssertEquals(t, getApplicationCredential.Unrestricted, false) + th.AssertEquals(t, getApplicationCredential.ProjectID, project.ID) + + checkACroles = rolesToMap(getApplicationCredential.Roles) + for i, role := range roles { + if i%2 == 0 { + th.AssertEquals(t, checkACroles[role.Name], role.Name) + } else { + th.AssertEquals(t, checkACroles[role.ID], role.ID) + } + if i > 4 { + break + } + } + + // unrestricted, unlimited TTL, with all possible roles, with a custom secret + createOpts = applicationcredentials.CreateOpts{ + Name: "super-test-ac", + Description: "test unrestricted application credential", + Unrestricted: true, + Secret: "myprecious", + } + + newApplicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() + th.AssertNoErr(t, err) + defer applicationcredentials.Delete(client, user.ID, newApplicationCredential.ID) + tools.PrintResource(t, newApplicationCredential) + + th.AssertEquals(t, newApplicationCredential.ExpiresAt.UTC(), time.Time{}) + th.AssertEquals(t, newApplicationCredential.Name, createOpts.Name) + th.AssertEquals(t, newApplicationCredential.Description, createOpts.Description) + th.AssertEquals(t, newApplicationCredential.Secret, createOpts.Secret) + th.AssertEquals(t, newApplicationCredential.Unrestricted, true) + th.AssertEquals(t, newApplicationCredential.ExpiresAt.UTC(), time.Time{}) + th.AssertEquals(t, newApplicationCredential.ProjectID, project.ID) + + checkACroles = rolesToMap(newApplicationCredential.Roles) + for _, role := range roles { + th.AssertEquals(t, checkACroles[role.Name], role.Name) + th.AssertEquals(t, checkACroles[role.ID], role.ID) + } +} diff --git a/openstack/identity/v3/applicationcredentials/requests.go b/openstack/identity/v3/applicationcredentials/requests.go new file mode 100644 index 0000000000..c7f089a519 --- /dev/null +++ b/openstack/identity/v3/applicationcredentials/requests.go @@ -0,0 +1,95 @@ +package applicationcredentials + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToApplicationCredentialListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // Name filters the response by an application credential name + Name string `json:"name"` +} + +// ToApplicationCredentialListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToApplicationCredentialListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the ApplicationCredentials to which the current token has access. +func List(client *gophercloud.ServiceClient, userID string, opts ListOptsBuilder) pagination.Pager { + url := listURL(client, userID) + if opts != nil { + query, err := opts.ToApplicationCredentialListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ApplicationCredentialPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details on a single user, by ID. +func Get(client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, userID, id), &r.Body, nil) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToApplicationCredentialCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create an application credential. +type CreateOpts struct { + // The name of the application credential. + Name string `json:"name,omitempty" required:"true"` + // A description of the application credential’s purpose. + Description string `json:"description,omitempty"` + // A flag indicating whether the application credential may be used for creation or destruction of other application credentials or trusts. + // Defaults to false + Unrestricted bool `json:"unrestricted"` + // The secret for the application credential, either generated by the server or provided by the user. + // This is only ever shown once in the response to a create request. It is not stored nor ever shown again. + // If the secret is lost, a new application credential must be created. + Secret string `json:"secret,omitempty"` + // A list of one or more roles that this application credential has associated with its project. + // A token using this application credential will have these same roles. + Roles []Role `json:"roles,omitempty"` + // The expiration time of the application credential, if one was specified. + ExpiresAt string `json:"expires_at,omitempty"` +} + +// ToApplicationCredentialCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToApplicationCredentialCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "application_credential") +} + +// Create creates a new ApplicationCredential. +func Create(client *gophercloud.ServiceClient, userID string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToApplicationCredentialCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// Delete deletes an application credential. +func Delete(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, userID, id), nil) + return +} diff --git a/openstack/identity/v3/applicationcredentials/results.go b/openstack/identity/v3/applicationcredentials/results.go new file mode 100644 index 0000000000..f687997636 --- /dev/null +++ b/openstack/identity/v3/applicationcredentials/results.go @@ -0,0 +1,127 @@ +package applicationcredentials + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type Role struct { + // DomainID is the domain ID the role belongs to. + DomainID string `json:"domain_id,omitempty"` + // ID is the unique ID of the role. + ID string `json:"id,omitempty"` + // Name is the role name + Name string `json:"name,omitempty"` +} + +// ApplicationCredential represents the application credential object +type ApplicationCredential struct { + // The ID of the application credential. + ID string `json:"id"` + // The name of the application credential. + Name string `json:"name"` + // A description of the application credential’s purpose. + Description string `json:"description"` + // A flag indicating whether the application credential may be used for creation or destruction of other application credentials or trusts. + // Defaults to false + Unrestricted bool `json:"unrestricted"` + // The secret for the application credential, either generated by the server or provided by the user. + // This is only ever shown once in the response to a create request. It is not stored nor ever shown again. + // If the secret is lost, a new application credential must be created. + Secret string `json:"secret"` + // The ID of the project the application credential was created for and that authentication requests using this application credential will be scoped to. + ProjectID string `json:"project_id"` + // A list of one or more roles that this application credential has associated with its project. + // A token using this application credential will have these same roles. + Roles []Role `json:"roles"` + // The expiration time of the application credential, if one was specified. + ExpiresAt time.Time `json:"-"` + // Links contains referencing links to the application credential. + Links map[string]interface{} `json:"links"` +} + +func (r *ApplicationCredential) UnmarshalJSON(b []byte) error { + type tmp ApplicationCredential + var s struct { + tmp + ExpiresAt gophercloud.JSONRFC3339MilliNoZ `json:"expires_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ApplicationCredential(s.tmp) + + r.ExpiresAt = time.Time(s.ExpiresAt) + + return nil +} + +type applicationCredentialResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as an ApplicationCredential. +type GetResult struct { + applicationCredentialResult +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as an ApplicationCredential. +type CreateResult struct { + applicationCredentialResult +} + +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// an ApplicationCredentialPage is a single page of an ApplicationCredential results. +type ApplicationCredentialPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an ApplicationCredentialPage contains any results. +func (r ApplicationCredentialPage) IsEmpty() (bool, error) { + applicationCredentials, err := ExtractApplicationCredentials(r) + return len(applicationCredentials) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r ApplicationCredentialPage) 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 +} + +// Extractan ApplicationCredentials returns a slice of ApplicationCredentials contained in a single page of results. +func ExtractApplicationCredentials(r pagination.Page) ([]ApplicationCredential, error) { + var s struct { + ApplicationCredentials []ApplicationCredential `json:"application_credentials"` + } + err := (r.(ApplicationCredentialPage)).ExtractInto(&s) + return s.ApplicationCredentials, err +} + +// Extract interprets any application_credential results as an ApplicationCredential. +func (r applicationCredentialResult) Extract() (*ApplicationCredential, error) { + var s struct { + ApplicationCredential *ApplicationCredential `json:"application_credential"` + } + err := r.ExtractInto(&s) + return s.ApplicationCredential, err +} diff --git a/openstack/identity/v3/applicationcredentials/testing/fixtures.go b/openstack/identity/v3/applicationcredentials/testing/fixtures.go new file mode 100644 index 0000000000..ba6b54b379 --- /dev/null +++ b/openstack/identity/v3/applicationcredentials/testing/fixtures.go @@ -0,0 +1,404 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const userID = "2844b2a08be147a08ef58317d6471f1f" +const applicationCredentialID = "f741662395b249c9b8acdebf1722c5ae" + +// ListOutput provides a single page of ApplicationCredential results. +const ListOutput = ` +{ + "links": { + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", + "previous": null, + "next": null + }, + "application_credentials": [ + { + "links": { + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7" + }, + "description": null, + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13", + "domain_id": null, + "name": "compute_viewer" + } + ], + "expires_at": null, + "unrestricted": false, + "project_id": "53c2b94f63fb4f43a21b92d119ce549f", + "id": "c4859fb437df4b87a51a8f5adcfb0bc7", + "name": "test1" + }, + { + "links": { + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb" + }, + "description": null, + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13", + "domain_id": null, + "name": "compute_viewer" + }, + { + "id": "4494bc5bea1a4105ad7fbba6a7eb9ef4", + "domain_id": null, + "name": "network_viewer" + } + ], + "expires_at": "2019-03-12T12:12:12.000000", + "unrestricted": true, + "project_id": "53c2b94f63fb4f43a21b92d119ce549f", + "id": "6b8cc7647da64166a4a3cc0c88ebbabb", + "name": "test2" + } + ] +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "application_credential": { + "links": { + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae" + }, + "description": null, + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13", + "domain_id": null, + "name": "compute_viewer" + } + ], + "expires_at": null, + "unrestricted": false, + "project_id": "53c2b94f63fb4f43a21b92d119ce549f", + "id": "f741662395b249c9b8acdebf1722c5ae", + "name": "test" + } +} +` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "application_credential": { + "secret": "mysecret", + "unrestricted": false, + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13" + } + ], + "name": "test" + } +} +` + +const CreateResponse = ` +{ + "application_credential": { + "links": { + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae" + }, + "description": null, + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13", + "domain_id": null, + "name": "compute_viewer" + } + ], + "expires_at": null, + "secret": "mysecret", + "unrestricted": false, + "project_id": "53c2b94f63fb4f43a21b92d119ce549f", + "id": "f741662395b249c9b8acdebf1722c5ae", + "name": "test" + } +} +` + +// CreateNoOptionsRequest provides the input to a Create request with no Secret. +const CreateNoSecretRequest = ` +{ + "application_credential": { + "unrestricted": false, + "name": "test1", + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13" + } + ] + } +} +` + +const CreateNoSecretResponse = ` +{ + "application_credential": { + "links": { + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7" + }, + "description": null, + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13", + "domain_id": null, + "name": "compute_viewer" + } + ], + "expires_at": null, + "secret": "generated_secret", + "unrestricted": false, + "project_id": "53c2b94f63fb4f43a21b92d119ce549f", + "id": "c4859fb437df4b87a51a8f5adcfb0bc7", + "name": "test1" + } +} +` + +const CreateUnrestrictedRequest = ` +{ + "application_credential": { + "unrestricted": true, + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13" + }, + { + "id": "4494bc5bea1a4105ad7fbba6a7eb9ef4" + } + ], + "expires_at": "2019-03-12T12:12:12.000000", + "name": "test2" + } +} +` + +const CreateUnrestrictedResponse = ` +{ + "application_credential": { + "links": { + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb" + }, + "description": null, + "roles": [ + { + "id": "31f87923ae4a4d119aa0b85dcdbeed13", + "domain_id": null, + "name": "compute_viewer" + }, + { + "id": "4494bc5bea1a4105ad7fbba6a7eb9ef4", + "domain_id": null, + "name": "network_viewer" + } + ], + "expires_at": "2019-03-12T12:12:12.000000", + "secret": "generated_secret", + "unrestricted": true, + "project_id": "53c2b94f63fb4f43a21b92d119ce549f", + "id": "6b8cc7647da64166a4a3cc0c88ebbabb", + "name": "test2" + } +} +` + +var nilTime time.Time +var ApplicationCredential = applicationcredentials.ApplicationCredential{ + ID: "f741662395b249c9b8acdebf1722c5ae", + Name: "test", + Description: "", + Unrestricted: false, + Secret: "", + ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", + Roles: []applicationcredentials.Role{ + applicationcredentials.Role{ + ID: "31f87923ae4a4d119aa0b85dcdbeed13", + Name: "compute_viewer", + }, + }, + ExpiresAt: nilTime, + Links: map[string]interface{}{ + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae", + }, +} + +var ApplicationCredentialNoSecretResponse = applicationcredentials.ApplicationCredential{ + ID: "c4859fb437df4b87a51a8f5adcfb0bc7", + Name: "test1", + Description: "", + Unrestricted: false, + Secret: "generated_secret", + ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", + Roles: []applicationcredentials.Role{ + applicationcredentials.Role{ + ID: "31f87923ae4a4d119aa0b85dcdbeed13", + Name: "compute_viewer", + }, + }, + ExpiresAt: nilTime, + Links: map[string]interface{}{ + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7", + }, +} + +var ApplationCredentialExpiresAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2019-03-12T12:12:12.000000") +var UnrestrictedApplicationCredential = applicationcredentials.ApplicationCredential{ + ID: "6b8cc7647da64166a4a3cc0c88ebbabb", + Name: "test2", + Description: "", + Unrestricted: true, + Secret: "", + ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", + Roles: []applicationcredentials.Role{ + applicationcredentials.Role{ + ID: "31f87923ae4a4d119aa0b85dcdbeed13", + Name: "compute_viewer", + }, + applicationcredentials.Role{ + ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", + Name: "network_viewer", + }, + }, + ExpiresAt: ApplationCredentialExpiresAt, + Links: map[string]interface{}{ + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb", + }, +} + +var FirstApplicationCredential = applicationcredentials.ApplicationCredential{ + ID: "c4859fb437df4b87a51a8f5adcfb0bc7", + Name: "test1", + Description: "", + Unrestricted: false, + Secret: "", + ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", + Roles: []applicationcredentials.Role{ + applicationcredentials.Role{ + ID: "31f87923ae4a4d119aa0b85dcdbeed13", + Name: "compute_viewer", + }, + }, + ExpiresAt: nilTime, + Links: map[string]interface{}{ + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7", + }, +} + +var SecondApplicationCredential = applicationcredentials.ApplicationCredential{ + ID: "6b8cc7647da64166a4a3cc0c88ebbabb", + Name: "test2", + Description: "", + Unrestricted: true, + Secret: "", + ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", + Roles: []applicationcredentials.Role{ + applicationcredentials.Role{ + ID: "31f87923ae4a4d119aa0b85dcdbeed13", + Name: "compute_viewer", + }, + applicationcredentials.Role{ + ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", + Name: "network_viewer", + }, + }, + ExpiresAt: ApplationCredentialExpiresAt, + Links: map[string]interface{}{ + "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb", + }, +} + +// ExpectedApplicationCredentialsSlice is the slice of application credentials expected to be returned from ListOutput. +var ExpectedApplicationCredentialsSlice = []applicationcredentials.ApplicationCredential{FirstApplicationCredential, SecondApplicationCredential} + +// HandleListApplicationCredentialsSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that responds with a list of two applicationcredentials. +func HandleListApplicationCredentialsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", 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) + }) +} + +// HandleGetApplicationCredentialSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that responds with a single application credential. +func HandleGetApplicationCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae", 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) + }) +} + +// HandleCreateApplicationCredentialSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that tests application credential creation. +func HandleCreateApplicationCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", 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, CreateResponse) + }) +} + +// HandleCreateNoOptionsApplicationCredentialSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that tests application credential creation. +func HandleCreateNoSecretApplicationCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", 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, CreateNoSecretRequest) + + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateNoSecretResponse) + }) +} + +func HandleCreateUnrestrictedApplicationCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials", 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, CreateUnrestrictedRequest) + + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateUnrestrictedResponse) + }) +} + +// HandleDeleteApplicationCredentialSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that tests application credential deletion. +func HandleDeleteApplicationCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae", 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/applicationcredentials/testing/requests_test.go b/openstack/identity/v3/applicationcredentials/testing/requests_test.go new file mode 100644 index 0000000000..6a410e3098 --- /dev/null +++ b/openstack/identity/v3/applicationcredentials/testing/requests_test.go @@ -0,0 +1,124 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListApplicationCredentials(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListApplicationCredentialsSuccessfully(t) + + count := 0 + err := applicationcredentials.List(client.ServiceClient(), userID, nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := applicationcredentials.ExtractApplicationCredentials(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedApplicationCredentialsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListApplicationCredentialsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListApplicationCredentialsSuccessfully(t) + + allPages, err := applicationcredentials.List(client.ServiceClient(), userID, nil).AllPages() + th.AssertNoErr(t, err) + actual, err := applicationcredentials.ExtractApplicationCredentials(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedApplicationCredentialsSlice, actual) + th.AssertDeepEquals(t, ExpectedApplicationCredentialsSlice[0].Roles, []applicationcredentials.Role{{ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer"}}) + th.AssertDeepEquals(t, ExpectedApplicationCredentialsSlice[1].Roles, []applicationcredentials.Role{applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer"}, applicationcredentials.Role{ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", Name: "network_viewer"}}) +} + +func TestGetApplicationCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetApplicationCredentialSuccessfully(t) + + actual, err := applicationcredentials.Get(client.ServiceClient(), userID, applicationCredentialID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ApplicationCredential, *actual) +} + +func TestCreateApplicationCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateApplicationCredentialSuccessfully(t) + + createOpts := applicationcredentials.CreateOpts{ + Name: "test", + Secret: "mysecret", + Roles: []applicationcredentials.Role{ + applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, + }, + } + + ApplicationCredentialResponse := ApplicationCredential + ApplicationCredentialResponse.Secret = "mysecret" + + actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ApplicationCredentialResponse, *actual) +} + +func TestCreateNoSecretApplicationCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateNoSecretApplicationCredentialSuccessfully(t) + + createOpts := applicationcredentials.CreateOpts{ + Name: "test1", + Roles: []applicationcredentials.Role{ + applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, + }, + } + + actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ApplicationCredentialNoSecretResponse, *actual) +} + +func TestCreateUnrestrictedApplicationCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateUnrestrictedApplicationCredentialSuccessfully(t) + + createOpts := applicationcredentials.CreateOpts{ + Name: "test2", + Unrestricted: true, + Roles: []applicationcredentials.Role{ + applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, + applicationcredentials.Role{ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4"}, + }, + ExpiresAt: "2019-03-12T12:12:12.000000", + } + + UnrestrictedApplicationCredentialResponse := UnrestrictedApplicationCredential + UnrestrictedApplicationCredentialResponse.Secret = "generated_secret" + + actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, UnrestrictedApplicationCredentialResponse, *actual) +} + +func TestDeleteApplicationCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteApplicationCredentialSuccessfully(t) + + res := applicationcredentials.Delete(client.ServiceClient(), userID, applicationCredentialID) + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/applicationcredentials/urls.go b/openstack/identity/v3/applicationcredentials/urls.go new file mode 100644 index 0000000000..1476206938 --- /dev/null +++ b/openstack/identity/v3/applicationcredentials/urls.go @@ -0,0 +1,19 @@ +package applicationcredentials + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "application_credentials") +} + +func getURL(client *gophercloud.ServiceClient, userID string, id string) string { + return client.ServiceURL("users", userID, "application_credentials", id) +} + +func createURL(client *gophercloud.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "application_credentials") +} + +func deleteURL(client *gophercloud.ServiceClient, userID string, id string) string { + return client.ServiceURL("users", userID, "application_credentials", id) +} From 9e57e2f8ff0cf02e6ae3e8a567311e74a9424d48 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Thu, 14 Feb 2019 21:32:00 -0500 Subject: [PATCH 0665/2296] Baremetal API: noauth support (#1449) --- acceptance/clients/clients.go | 20 ++++-- acceptance/openstack/baremetal/noauth/doc.go | 8 +++ .../openstack/baremetal/noauth/nodes_test.go | 67 +++++++++++++++++++ openstack/baremetal/noauth/doc.go | 18 +++++ openstack/baremetal/noauth/requests.go | 36 ++++++++++ .../baremetal/noauth/testing/requests_test.go | 16 +++++ 6 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 acceptance/openstack/baremetal/noauth/doc.go create mode 100644 acceptance/openstack/baremetal/noauth/nodes_test.go create mode 100644 openstack/baremetal/noauth/doc.go create mode 100644 openstack/baremetal/noauth/requests.go create mode 100644 openstack/baremetal/noauth/testing/requests_test.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index a0b5b33e40..c212bdf8bf 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -11,7 +11,8 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" + baremetalNoAuth "github.com/gophercloud/gophercloud/openstack/baremetal/noauth" + blockstorageNoAuth "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" ) // AcceptanceTestChoices contains image and flavor selections for use by the acceptance tests. @@ -199,7 +200,7 @@ func NewBlockStorageV3Client() (*gophercloud.ServiceClient, error) { // making calls to the OpenStack Block Storage v2 API. An error will be // returned if client creation was not possible. func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { - client, err := noauth.NewClient(gophercloud.AuthOptions{ + client, err := blockstorageNoAuth.NewClient(gophercloud.AuthOptions{ Username: os.Getenv("OS_USERNAME"), TenantName: os.Getenv("OS_TENANT_NAME"), }) @@ -209,7 +210,7 @@ func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { client = configureDebug(client) - return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{ + return blockstorageNoAuth.NewBlockStorageNoAuth(client, blockstorageNoAuth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) } @@ -218,7 +219,7 @@ func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { // making calls to the OpenStack Block Storage v2 API. An error will be // returned if client creation was not possible. func NewBlockStorageV3NoAuthClient() (*gophercloud.ServiceClient, error) { - client, err := noauth.NewClient(gophercloud.AuthOptions{ + client, err := blockstorageNoAuth.NewClient(gophercloud.AuthOptions{ Username: os.Getenv("OS_USERNAME"), TenantName: os.Getenv("OS_TENANT_NAME"), }) @@ -228,7 +229,7 @@ func NewBlockStorageV3NoAuthClient() (*gophercloud.ServiceClient, error) { client = configureDebug(client) - return noauth.NewBlockStorageNoAuth(client, noauth.EndpointOpts{ + return blockstorageNoAuth.NewBlockStorageNoAuth(client, blockstorageNoAuth.EndpointOpts{ CinderEndpoint: os.Getenv("CINDER_ENDPOINT"), }) } @@ -275,6 +276,15 @@ func NewBareMetalV1Client() (*gophercloud.ServiceClient, error) { }) } +// NewBareMetalV1NoAuthClient returns a *ServiceClient for making calls +// to the OpenStack Bare Metal v1 API. An error will be returned +// if authentication or client creation was not possible. +func NewBareMetalV1NoAuthClient() (*gophercloud.ServiceClient, error) { + return baremetalNoAuth.NewBareMetalNoAuth(baremetalNoAuth.EndpointOpts{ + IronicEndpoint: os.Getenv("IRONIC_ENDPOINT"), + }) +} + // NewDBV1Client returns a *ServiceClient for making calls // to the OpenStack Database v1 API. An error will be returned // if authentication or client creation was not possible. diff --git a/acceptance/openstack/baremetal/noauth/doc.go b/acceptance/openstack/baremetal/noauth/doc.go new file mode 100644 index 0000000000..e5869bf3c5 --- /dev/null +++ b/acceptance/openstack/baremetal/noauth/doc.go @@ -0,0 +1,8 @@ +package noauth + +/* +Acceptance tests for Ironic endpoints with auth_strategy=noauth. Specify +IRONIC_ENDPOINT environment variable. For example: + + IRONIC_ENDPOINT="http://127.0.0.1:6385/v1" go test ./acceptance/openstack/baremetal/noauth/... +*/ diff --git a/acceptance/openstack/baremetal/noauth/nodes_test.go b/acceptance/openstack/baremetal/noauth/nodes_test.go new file mode 100644 index 0000000000..32796092a1 --- /dev/null +++ b/acceptance/openstack/baremetal/noauth/nodes_test.go @@ -0,0 +1,67 @@ +package noauth + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/pagination" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNodesCreateDestroy(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1NoAuthClient() + th.AssertNoErr(t, err) + client.Microversion = "1.50" + + node, err := v1.CreateNode(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + + found := false + err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + nodeList, err := nodes.ExtractNodes(page) + if err != nil { + return false, err + } + + for _, n := range nodeList { + if n.UUID == node.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + + th.AssertEquals(t, found, true) +} + +func TestNodesUpdate(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1NoAuthClient() + th.AssertNoErr(t, err) + client.Microversion = "1.50" + + node, err := v1.CreateNode(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + + updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ + nodes.UpdateOperation{ + Op: nodes.ReplaceOp, + Path: "/maintenance", + Value: "true", + }, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.Maintenance, true) +} diff --git a/openstack/baremetal/noauth/doc.go b/openstack/baremetal/noauth/doc.go new file mode 100644 index 0000000000..64106fc6b6 --- /dev/null +++ b/openstack/baremetal/noauth/doc.go @@ -0,0 +1,18 @@ +package noauth + +/* +Package noauth provides support for noauth bare metal endpoints. + + Example of obtaining and using a client: + + client, err := noauth.NewBareMetalNoAuth(noauth.EndpointOpts{ + IronicEndpoint: "http://localhost:6385/v1/", + }) + if err != nil { + panic(err) + } + + client.Microversion = "1.50" + + nodes.ListDetail(client, nodes.ListOpts{}) +*/ diff --git a/openstack/baremetal/noauth/requests.go b/openstack/baremetal/noauth/requests.go new file mode 100644 index 0000000000..b5a1b58f0d --- /dev/null +++ b/openstack/baremetal/noauth/requests.go @@ -0,0 +1,36 @@ +package noauth + +import ( + "fmt" + + "github.com/gophercloud/gophercloud" +) + +// EndpointOpts specifies a "noauth" Ironic Endpoint. +type EndpointOpts struct { + // IronicEndpoint [required] is currently only used with "noauth" Ironic. + // An Ironic endpoint with "auth_strategy=noauth" is necessary, for example: + // http://ironic.example.com:6385/v1. + IronicEndpoint string +} + +func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc := new(gophercloud.ServiceClient) + if eo.IronicEndpoint == "" { + return nil, fmt.Errorf("IronicEndpoint is required") + } + + sc.Endpoint = gophercloud.NormalizeURL(eo.IronicEndpoint) + sc.ProviderClient = client + return sc, nil +} + +// NewBareMetalNoAuth creates a ServiceClient that may be used to access a +// "noauth" bare metal service. +func NewBareMetalNoAuth(eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) + + sc.Type = "baremetal" + + return sc, err +} diff --git a/openstack/baremetal/noauth/testing/requests_test.go b/openstack/baremetal/noauth/testing/requests_test.go new file mode 100644 index 0000000000..4d107b9ffe --- /dev/null +++ b/openstack/baremetal/noauth/testing/requests_test.go @@ -0,0 +1,16 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/noauth" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNoAuth(t *testing.T) { + noauthClient, err := noauth.NewBareMetalNoAuth(noauth.EndpointOpts{ + IronicEndpoint: "http://ironic:6385/v1", + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, "", noauthClient.TokenID) +} From dcc6e84aef1b6713accea7d7d7252ad2bc0e7034 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Sat, 16 Feb 2019 17:41:16 -0500 Subject: [PATCH 0666/2296] Baremetal V1 API: Node management (#1450) * Baremetal V1 API: Node management * Addressed comments * Add missing newline --- openstack/baremetal/v1/nodes/doc.go | 18 ++ openstack/baremetal/v1/nodes/requests.go | 70 ++++++++ openstack/baremetal/v1/nodes/results.go | 76 ++++++++ .../baremetal/v1/nodes/testing/fixtures.go | 162 ++++++++++++++++++ .../v1/nodes/testing/requests_test.go | 56 ++++++ openstack/baremetal/v1/nodes/urls.go | 16 ++ 6 files changed, 398 insertions(+) diff --git a/openstack/baremetal/v1/nodes/doc.go b/openstack/baremetal/v1/nodes/doc.go index 1199fc09c3..40b9fe826e 100644 --- a/openstack/baremetal/v1/nodes/doc.go +++ b/openstack/baremetal/v1/nodes/doc.go @@ -76,4 +76,22 @@ resource in the OpenStack Bare Metal service. if err != nil { panic(err) } + + // Example to Validate Node + validation, err := nodes.Validate(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + + // Example to inject non-masking interrupts + err := nodes.InjectNMI(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").ExtractErr() + + // Example to get array of supported boot devices for a node + bootDevices, err := nodes.GetSupportedBootDevices(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + + // Example to set boot device for a node + err := nodes.SetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", nodes.BootDeviceOpts{ + BootDevice: "pxe", + Persistent: false, + }) + + // Example to get boot device for a node + bootDevice, err := nodes.GetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() */ diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 7547a989ec..5aa961e4a8 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -301,3 +301,73 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } + +// Request that Ironic validate 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. +func Validate(client *gophercloud.ServiceClient, id string) (r ValidateResult) { + _, r.Err = client.Get(validateURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Inject NMI (Non-Masking Interrupts) for the given Node. This feature can be used for hardware diagnostics, and +// actual support depends on a driver. +func InjectNMI(client *gophercloud.ServiceClient, id string) (r InjectNMIResult) { + _, r.Err = client.Put(injectNMIURL(client, id), map[string]string{}, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} + +type BootDeviceOpts struct { + BootDevice string `json:"boot_device"` // e.g., 'pxe', 'disk', etc. + Persistent bool `json:"persistent"` // Whether this is one-time or not +} + +// BootDeviceOptsBuilder allows extensions to add additional parameters to the +// SetBootDevice request. +type BootDeviceOptsBuilder interface { + ToBootDeviceMap() (map[string]interface{}, error) +} + +// ToBootDeviceSetMap assembles a request body based on the contents of a BootDeviceOpts. +func (opts BootDeviceOpts) ToBootDeviceMap() (map[string]interface{}, error) { + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return body, nil +} + +// Set the boot device for the given Node, and set it persistently or for one-time boot. The exact behaviour +// of this depends on the hardware driver. +func SetBootDevice(client *gophercloud.ServiceClient, id string, bootDevice BootDeviceOptsBuilder) (r SetBootDeviceResult) { + reqBody, err := bootDevice.ToBootDeviceMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Put(bootDeviceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} + +// Get the current boot device for the given Node. +func GetBootDevice(client *gophercloud.ServiceClient, id string) (r BootDeviceResult) { + _, r.Err = client.Get(bootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Retrieve the acceptable set of supported boot devices for a specific Node. +func GetSupportedBootDevices(client *gophercloud.ServiceClient, id string) (r SupportedBootDeviceResult) { + _, r.Err = client.Get(supportedBootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 9bd3a96085..7df7b45c84 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -16,6 +16,30 @@ func (r nodeResult) Extract() (*Node, error) { return &s, err } +// Extract interprets a BootDeviceResult as BootDeviceOpts, if possible. +func (r BootDeviceResult) Extract() (*BootDeviceOpts, error) { + var s BootDeviceOpts + err := r.ExtractInto(&s) + return &s, err +} + +// Extract interprets a SupportedBootDeviceResult as an array of supported boot devices, if possible. +func (r SupportedBootDeviceResult) Extract() ([]string, error) { + var s struct { + Devices []string `json:"supported_boot_devices"` + } + + err := r.ExtractInto(&s) + return s.Devices, err +} + +// Extract interprets a ValidateResult as NodeValidation, if possible. +func (r ValidateResult) Extract() (*NodeValidation, error) { + var s NodeValidation + err := r.ExtractInto(&s) + return &s, err +} + func (r nodeResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "") } @@ -223,3 +247,55 @@ type UpdateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// ValidateResult is the response from a Validate operation. Call its Extract +// method to interpret it as a NodeValidation struct. +type ValidateResult struct { + gophercloud.Result +} + +// InjectNMIResult is the response from an InjectNMI operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type InjectNMIResult struct { + gophercloud.ErrResult +} + +// BootDeviceResult is the response from a GetBootDevice operation. Call its Extract +// method to interpret it as a BootDeviceOpts struct. +type BootDeviceResult struct { + gophercloud.Result +} + +// BootDeviceResult is the response from a GetBootDevice operation. Call its Extract +// method to interpret it as a BootDeviceOpts struct. +type SetBootDeviceResult struct { + gophercloud.ErrResult +} + +// SupportedBootDeviceResult is the response from a GetSupportedBootDevices operation. Call its Extract +// method to interpret it as an array of supported boot device values. +type SupportedBootDeviceResult struct { + gophercloud.Result +} + +// Each element in the response will contain a “result” variable, which will have a value of “true” or “false”, and +// also potentially a reason. A value of nil indicates that the Node’s driver does not support that interface. +type DriverValidation struct { + Result bool `json:"result"` + 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. +type NodeValidation struct { + Boot DriverValidation `json:"boot"` + Console DriverValidation `json:"console"` + Deploy DriverValidation `json:"deploy"` + Inspect DriverValidation `json:"inspect"` + Management DriverValidation `json:"management"` + Network DriverValidation `json:"network"` + Power DriverValidation `json:"power"` + RAID DriverValidation `json:"raid"` + Rescue DriverValidation `json:"rescue"` + Storage DriverValidation `json:"storage"` +} diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index d915ff3237..ea6d91c8ad 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -480,6 +480,67 @@ const SingleNodeBody = ` } ` +const NodeValidationBody = ` +{ + "bios": { + "reason": "Driver ipmi does not support bios (disabled or not implemented).", + "result": false + }, + "boot": { + "reason": "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", + "result": false + }, + "console": { + "reason": "Driver ipmi does not support console (disabled or not implemented).", + "result": false + }, + "deploy": { + "reason": "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", + "result": false + }, + "inspect": { + "reason": "Driver ipmi does not support inspect (disabled or not implemented).", + "result": false + }, + "management": { + "result": true + }, + "network": { + "result": true + }, + "power": { + "result": true + }, + "raid": { + "reason": "Driver ipmi does not support raid (disabled or not implemented).", + "result": false + }, + "rescue": { + "reason": "Driver ipmi does not support rescue (disabled or not implemented).", + "result": false + }, + "storage": { + "result": true + } +} +` + +const NodeBootDeviceBody = ` +{ + "boot_device":"pxe", + "persistent":false +} +` + +const NodeSupportedBootDeviceBody = ` +{ + "supported_boot_devices": [ + "pxe", + "disk" + ] +} +` + var ( NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", @@ -531,6 +592,55 @@ var ( ProtectedReason: "", } + NodeFooValidation = nodes.NodeValidation{ + Boot: nodes.DriverValidation{ + Result: false, + Reason: "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", + }, + Console: nodes.DriverValidation{ + Result: false, + Reason: "Driver ipmi does not support console (disabled or not implemented).", + }, + Deploy: nodes.DriverValidation{ + Result: false, + Reason: "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", + }, + Inspect: nodes.DriverValidation{ + Result: false, + Reason: "Driver ipmi does not support inspect (disabled or not implemented).", + }, + Management: nodes.DriverValidation{ + Result: true, + }, + Network: nodes.DriverValidation{ + Result: true, + }, + Power: nodes.DriverValidation{ + Result: true, + }, + RAID: nodes.DriverValidation{ + Result: false, + Reason: "Driver ipmi does not support raid (disabled or not implemented).", + }, + Rescue: nodes.DriverValidation{ + Result: false, + Reason: "Driver ipmi does not support rescue (disabled or not implemented).", + }, + Storage: nodes.DriverValidation{ + Result: true, + }, + } + + NodeBootDevice = nodes.BootDeviceOpts{ + BootDevice: "pxe", + Persistent: false, + } + + NodeSupportedBootDevice = []string{ + "pxe", + "disk", + } + NodeBar = nodes.Node{ UUID: "08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", Name: "bar", @@ -708,3 +818,55 @@ func HandleNodeUpdateSuccessfully(t *testing.T, response string) { fmt.Fprintf(w, response) }) } + +func HandleNodeValidateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/validate", 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, NodeValidationBody) + }) +} + +// HandleInjectNMISuccessfully sets up the test server to respond to a node InjectNMI request +func HandleInjectNMISuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/management/inject_nmi", 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, "{}") + + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleSetBootDeviceSuccessfully sets up the test server to respond to a set boot device request for a node +func HandleSetBootDeviceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/management/boot_device", 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, NodeBootDeviceBody) + + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleGetBootDeviceSuccessfully sets up the test server to respond to a get boot device request for a node +func HandleGetBootDeviceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/management/boot_device", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, NodeBootDeviceBody) + }) +} + +// HandleGetBootDeviceSuccessfully sets up the test server to respond to a get boot device request for a node +func HandleGetSupportedBootDeviceSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/management/boot_device/supported", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, NodeSupportedBootDeviceBody) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 81a04b2858..14819d3e18 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -152,3 +152,59 @@ func TestUpdateNode(t *testing.T) { th.CheckDeepEquals(t, NodeFoo, *actual) } + +func TestValidateNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeValidateSuccessfully(t) + + c := client.ServiceClient() + actual, err := nodes.Validate(c, "1234asdf").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeFooValidation, *actual) +} + +func TestInjectNMI(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleInjectNMISuccessfully(t) + + c := client.ServiceClient() + err := nodes.InjectNMI(c, "1234asdf").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestSetBootDevice(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSetBootDeviceSuccessfully(t) + + c := client.ServiceClient() + err := nodes.SetBootDevice(c, "1234asdf", nodes.BootDeviceOpts{ + BootDevice: "pxe", + Persistent: false, + }).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestGetBootDevice(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetBootDeviceSuccessfully(t) + + c := client.ServiceClient() + bootDevice, err := nodes.GetBootDevice(c, "1234asdf").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeBootDevice, *bootDevice) +} + +func TestGetSupportedBootDevices(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSupportedBootDeviceSuccessfully(t) + + c := client.ServiceClient() + bootDevices, err := nodes.GetSupportedBootDevices(c, "1234asdf").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeSupportedBootDevice, bootDevices) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index 8defdea95f..cd6fafa937 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -25,3 +25,19 @@ func getURL(client *gophercloud.ServiceClient, id string) string { func updateURL(client *gophercloud.ServiceClient, id string) string { return deleteURL(client, id) } + +func validateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "validate") +} + +func injectNMIURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "management", "inject_nmi") +} + +func bootDeviceURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "management", "boot_device") +} + +func supportedBootDeviceURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "management", "boot_device", "supported") +} From d6801284f5e8eb368096bcdef55f81a33eb16b23 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 20 Feb 2019 18:44:23 -0700 Subject: [PATCH 0667/2296] Acc Tests: Temporarily disabling octavia/lbaas (#1462) --- .zuul/playbooks/gophercloud-acceptance-test/run.yaml | 2 +- script/acceptancetest | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index 26ed039213..202ed962a5 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -8,7 +8,7 @@ - 'manila' - 'designate' - 'zun' - - 'octavia' + #- 'octavia' - 'neutron-ext' - install-devstack tasks: diff --git a/script/acceptancetest b/script/acceptancetest index 472f19e3a9..486c85bd65 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -39,8 +39,8 @@ 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/keymanager/v1 +#acceptance/openstack/loadbalancer/v2 # No suitable endpoint could be found in the service catalog. # acceptance/openstack/messaging/v2 From cd411dce037737a7f4c814738dcf57c61c407ab1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 21 Feb 2019 09:21:25 -0700 Subject: [PATCH 0668/2296] Container Infra v1: Use interface for update value (#1463) This commit changes the Value field of UpdateOpts to be an interface{} instead of a string. This is to send type-specific values to the API service. --- acceptance/openstack/containerinfra/v1/clusters_test.go | 2 +- openstack/containerinfra/v1/clusters/requests.go | 6 +++--- openstack/containerinfra/v1/clustertemplates/requests.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index c5f540cbf9..1e2bf85351 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -63,7 +63,7 @@ func TestClustersCRUD(t *testing.T) { allPagesDetail, err := clusters.ListDetail(client, nil).AllPages() th.AssertNoErr(t, err) - allClustersDetail, err := clusters.ExtractClusters(allPages) + allClustersDetail, err := clusters.ExtractClusters(allPagesDetail) th.AssertNoErr(t, err) var foundDetail bool diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index c6aff5ff71..66035adcbf 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -136,9 +136,9 @@ const ( ) type UpdateOpts struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value string `json:"value,omitempty"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value interface{} `json:"value,omitempty"` } // UpdateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index c3ceba0744..3b2aac58cc 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -133,9 +133,9 @@ const ( ) type UpdateOpts struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value string `json:"value,omitempty"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value interface{} `json:"value,omitempty"` } // UpdateOptsBuilder allows extensions to add additional parameters to the From 3f3cc5a566b2284815a4b6feb9e8c8f3d74aecdf Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 21 Feb 2019 09:49:56 -0700 Subject: [PATCH 0669/2296] Core: Fix deadlock with reauthentication (#1464) * Core: Fix deadlock with reauthentication This commit fixes a deadlock issue during reauthentication. It removes a possibly unnecessary locking of the client's locking mechanism so the reauthentication process can safely lock the client. * Core: Add a little documentation about client mutexes --- .../openstack/identity/v3/reauth_test.go | 34 +++++++++++++++++++ provider_client.go | 10 +++--- 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 acceptance/openstack/identity/v3/reauth_test.go diff --git a/acceptance/openstack/identity/v3/reauth_test.go b/acceptance/openstack/identity/v3/reauth_test.go new file mode 100644 index 0000000000..27363db04e --- /dev/null +++ b/acceptance/openstack/identity/v3/reauth_test.go @@ -0,0 +1,34 @@ +// +build acceptance + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack" + th "github.com/gophercloud/gophercloud/testhelper" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" +) + +func TestReauthAuthResultDeadlock(t *testing.T) { + clients.RequireAdmin(t) + + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + ao.AllowReauth = true + + provider, err := openstack.AuthenticatedClient(ao) + th.AssertNoErr(t, err) + + provider.SetToken("this is not a valid token") + + client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) + pages, err := projects.List(client, nil).AllPages() + th.AssertNoErr(t, err) + _, err = projects.ExtractProjects(pages) + th.AssertNoErr(t, err) +} diff --git a/provider_client.go b/provider_client.go index 365b4cbcdf..6ed659b52c 100644 --- a/provider_client.go +++ b/provider_client.go @@ -76,13 +76,18 @@ type ProviderClient struct { // with the token and reauth func zeroed. Such client can be used to perform reauthorization. Throwaway bool + // mut is a mutex for the client. It protects read and write access to client attributes such as getting + // and setting the TokenID. mut *sync.RWMutex + // reauthmut is a mutex for reauthentication it attempts to ensure that only one reauthentication + // attempt happens at one time. reauthmut *reauthlock authResult AuthResult } +// reauthlock represents a set of attributes used to help in the reauthentication process. type reauthlock struct { sync.RWMutex reauthing bool @@ -219,7 +224,7 @@ func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { return nil } - if client.mut == nil { + if client.reauthmut == nil { return client.ReauthFunc() } @@ -234,9 +239,6 @@ func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { } client.reauthmut.Unlock() - client.mut.Lock() - defer client.mut.Unlock() - client.reauthmut.Lock() client.reauthmut.reauthing = true client.reauthmut.done = sync.NewCond(client.reauthmut) From d453f46b89c812a592d0be5deaab069a5ed380b9 Mon Sep 17 00:00:00 2001 From: JackKuei <37349764+JackKuei@users.noreply.github.com> Date: Sat, 23 Feb 2019 09:13:43 -0800 Subject: [PATCH 0670/2296] Senlin: Receivers Notify (#1447) --- .../openstack/clustering/v1/clustering.go | 41 +++++++++++++++++-- .../openstack/clustering/v1/receivers_test.go | 25 ++++++++++- .../clustering/v1/webhooktrigger_test.go | 2 +- openstack/clustering/v1/receivers/doc.go | 9 ++++ openstack/clustering/v1/receivers/requests.go | 12 ++++++ openstack/clustering/v1/receivers/results.go | 12 ++++++ .../v1/receivers/testing/fixtures.go | 12 ++++++ .../v1/receivers/testing/requests_test.go | 11 +++++ openstack/clustering/v1/receivers/urls.go | 4 ++ 9 files changed, 122 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clustering.go b/acceptance/openstack/clustering/v1/clustering.go index 8201592d3f..6c7a406796 100644 --- a/acceptance/openstack/clustering/v1/clustering.go +++ b/acceptance/openstack/clustering/v1/clustering.go @@ -253,9 +253,9 @@ func CreateProfile(t *testing.T, client *gophercloud.ServiceClient) (*profiles.P return profile, nil } -// CreateReceiver will create a random profile. An error will be returned if the -// profile could not be created. -func CreateReceiver(t *testing.T, client *gophercloud.ServiceClient, clusterID string) (*receivers.Receiver, error) { +// CreateWebhookReceiver will create a random webhook receiver. An error will be returned if the +// receiver could not be created. +func CreateWebhookReceiver(t *testing.T, client *gophercloud.ServiceClient, clusterID string) (*receivers.Receiver, error) { name := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create receiver: %s", name) @@ -276,7 +276,40 @@ func CreateReceiver(t *testing.T, client *gophercloud.ServiceClient, clusterID s return nil, err } - t.Logf("Successfully created receiver: %s", receiver.ID) + t.Logf("Successfully created webhook receiver: %s", receiver.ID) + + tools.PrintResource(t, receiver) + tools.PrintResource(t, receiver.CreatedAt) + + th.AssertEquals(t, name, receiver.Name) + th.AssertEquals(t, createOpts.Action, receiver.Action) + + return receiver, nil +} + +// CreateMessageReceiver will create a message receiver with a random name. An error will be returned if the +// receiver could not be created. +func CreateMessageReceiver(t *testing.T, client *gophercloud.ServiceClient, clusterID string) (*receivers.Receiver, error) { + name := tools.RandomString("TESTACC-", 8) + t.Logf("Attempting to create receiver: %s", name) + + createOpts := receivers.CreateOpts{ + Name: name, + ClusterID: clusterID, + Type: receivers.MessageReceiver, + } + + res := receivers.Create(client, createOpts) + if res.Err != nil { + return nil, res.Err + } + + receiver, err := res.Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created message receiver: %s", receiver.ID) tools.PrintResource(t, receiver) tools.PrintResource(t, receiver.CreatedAt) diff --git a/acceptance/openstack/clustering/v1/receivers_test.go b/acceptance/openstack/clustering/v1/receivers_test.go index ff0ca8eea2..fbf5565c93 100644 --- a/acceptance/openstack/clustering/v1/receivers_test.go +++ b/acceptance/openstack/clustering/v1/receivers_test.go @@ -23,7 +23,7 @@ func TestReceiversCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) - receiver, err := CreateReceiver(t, client, cluster.ID) + receiver, err := CreateWebhookReceiver(t, client, cluster.ID) th.AssertNoErr(t, err) defer DeleteReceiver(t, client, receiver.ID) @@ -57,3 +57,26 @@ func TestReceiversCRUD(t *testing.T) { th.AssertEquals(t, receiver.Name, newName) } + +func TestReceiversNotify(t *testing.T) { + t.Parallel() + client, err := clients.NewClusteringV1Client() + th.AssertNoErr(t, err) + + profile, err := CreateProfile(t, client) + th.AssertNoErr(t, err) + defer DeleteProfile(t, client, profile.ID) + + cluster, err := CreateCluster(t, client, profile.ID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, cluster.ID) + + receiver, err := CreateMessageReceiver(t, client, cluster.ID) + th.AssertNoErr(t, err) + defer DeleteReceiver(t, client, receiver.ID) + t.Logf("Created Mesage Receiver Name:[%s] Message Receiver ID:[%s]", receiver.Name, receiver.ID) + + requestID, err := receivers.Notify(client, receiver.ID).Extract() + th.AssertNoErr(t, err) + t.Logf("Receiver Notify Service Request ID: %s", requestID) +} diff --git a/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/acceptance/openstack/clustering/v1/webhooktrigger_test.go index 11d4f6ee0d..627e8c02cc 100644 --- a/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -31,7 +31,7 @@ func TestClusteringWebhookTrigger(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) - receiver, err := CreateReceiver(t, client, cluster.ID) + receiver, err := CreateWebhookReceiver(t, client, cluster.ID) th.AssertNoErr(t, err) defer DeleteReceiver(t, client, receiver.ID) diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index 0217136a62..92506c856b 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -68,5 +68,14 @@ Example to List Receivers } return true, nil }) + +Example to Notify a Receiver + + receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" + requestID, err := receivers.Notify(serviceClient, receiverID).Extract() + if err != nil { + panic(err) + } + */ package receivers diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index 1c528c732d..27d45136cb 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -143,3 +143,15 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } + +// Notify Notifies message type receiver +func Notify(client *gophercloud.ServiceClient, id string) (r NotifyResult) { + result, err := client.Post(notifyURL(client, id), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + + if err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index 9299921586..a25192b6a9 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -93,6 +93,12 @@ type DeleteResult struct { gophercloud.ErrResult } +// NotifyResult is the result from a Notify operation. Call its Extract +// method to determine if the call succeeded or failed. +type NotifyResult struct { + commonResult +} + // ReceiverPage contains a single page of all nodes from a List operation. type ReceiverPage struct { pagination.LinkedPageBase @@ -112,3 +118,9 @@ func ExtractReceivers(r pagination.Page) ([]Receiver, error) { err := (r.(ReceiverPage)).ExtractInto(&s) return s.Receivers, err } + +// Extract returns action for notify receivers +func (r NotifyResult) Extract() (string, error) { + requestID := r.Header.Get("X-Openstack-Request-Id") + return requestID, r.Err +} diff --git a/openstack/clustering/v1/receivers/testing/fixtures.go b/openstack/clustering/v1/receivers/testing/fixtures.go index 498d162dd8..8be67870bb 100644 --- a/openstack/clustering/v1/receivers/testing/fixtures.go +++ b/openstack/clustering/v1/receivers/testing/fixtures.go @@ -169,6 +169,7 @@ const ListResponse = ` }` var ExpectedReceiversList = []receivers.Receiver{ExpectedUpdateReceiver} +var ExpectedNotifyRequestID = "66a81d68-bf48-4af5-897b-a3bfef7279a8" func HandleCreateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { @@ -227,3 +228,14 @@ func HandleDeleteSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleNotifySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee/notify", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-Id", ExpectedNotifyRequestID) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go index 02f8f0692d..d04ac24abf 100644 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -92,3 +92,14 @@ func TestDeleteReceiver(t *testing.T) { deleteResult := receivers.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } + +func TestNotifyReceivers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleNotifySuccessfully(t) + + requestID, err := receivers.Notify(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, ExpectedNotifyRequestID, requestID) +} diff --git a/openstack/clustering/v1/receivers/urls.go b/openstack/clustering/v1/receivers/urls.go index be97ded533..62ad621b26 100644 --- a/openstack/clustering/v1/receivers/urls.go +++ b/openstack/clustering/v1/receivers/urls.go @@ -32,3 +32,7 @@ func listURL(client *gophercloud.ServiceClient) string { func deleteURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func notifyURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL(apiVersion, apiName, id, "notify") +} From 7185d16ec2c2af4d6ff1cfca27329aa0f172b5e8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Feb 2019 10:32:46 -0700 Subject: [PATCH 0671/2296] Acc Tests: Disabling Zun/Containers temporarily (#1472) --- .zuul/playbooks/gophercloud-acceptance-test/run.yaml | 2 +- script/acceptancetest | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index 202ed962a5..ac2d0a9e47 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -7,7 +7,7 @@ enable_services: - 'manila' - 'designate' - - 'zun' + #- 'zun' #- 'octavia' - 'neutron-ext' - install-devstack diff --git a/script/acceptancetest b/script/acceptancetest index 486c85bd65..70c39ab545 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -30,7 +30,8 @@ acceptance/openstack/compute/v2 # No suitable endpoint could be found in the service catalog. # acceptance/openstack/containerinfra/v1 -acceptance/openstack/container/v1 +# Disabled temporarily +#acceptance/openstack/container/v1 # Unable to create a DB client: No suitable endpoint could be found in the service catalog. # acceptance/openstack/db/v1 From 044608ead567f2eb44e56cb7cc46647b72b33394 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 24 Feb 2019 22:28:15 +0100 Subject: [PATCH 0672/2296] Networking V2: Fix nested struct unmarshaling for the port binding profile (#1474) --- .../networking/v2/extensions/portsbinding/results.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/openstack/networking/v2/extensions/portsbinding/results.go b/openstack/networking/v2/extensions/portsbinding/results.go index c39f7df50f..4d8f856a0b 100644 --- a/openstack/networking/v2/extensions/portsbinding/results.go +++ b/openstack/networking/v2/extensions/portsbinding/results.go @@ -1,11 +1,5 @@ package portsbinding -// IP is a sub-struct that represents an individual IP. -type IP struct { - SubnetID string `json:"subnet_id"` - IPAddress string `json:"ip_address"` -} - // PortsBindingExt represents a decorated form of a Port with the additional // port binding information. type PortsBindingExt struct { @@ -26,5 +20,5 @@ type PortsBindingExt struct { // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. - Profile map[string]string `json:"binding:profile"` + Profile map[string]interface{} `json:"binding:profile"` } From ccaa267cb11183095eae75516a17b1b971c9aadb Mon Sep 17 00:00:00 2001 From: Sergey Reshetnyak Date: Mon, 25 Feb 2019 01:44:22 +0400 Subject: [PATCH 0673/2296] Networking V2: Fix deleting external gateway for router (#1470) --- openstack/networking/v2/extensions/layer3/routers/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index a6fa4a35ea..857e1947e1 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -8,7 +8,7 @@ import ( // GatewayInfo represents the information of an external gateway for any // particular network router. type GatewayInfo struct { - NetworkID string `json:"network_id"` + NetworkID string `json:"network_id,omitempty"` EnableSNAT *bool `json:"enable_snat,omitempty"` ExternalFixedIPs []ExternalFixedIP `json:"external_fixed_ips,omitempty"` } From d81f1761c624be9740dfe56e41314d9141bbda34 Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Mon, 25 Feb 2019 06:56:22 +0900 Subject: [PATCH 0674/2296] Compute V2: Create multiple servers (#1471) * Compute v2: Create multiple servers * remove return_reservation_id from CreateOpts * remove this too... --- openstack/compute/v2/servers/requests.go | 16 +++++++++++++- .../compute/v2/servers/testing/fixtures.go | 22 +++++++++++++++++++ .../v2/servers/testing/requests_test.go | 17 ++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 646bd4ccf1..ee8e93b1dc 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -183,9 +183,15 @@ type CreateOpts struct { // AccessIPv4 specifies an IPv4 address for the instance. AccessIPv4 string `json:"accessIPv4,omitempty"` - // AccessIPv6 pecifies an IPv6 address for the instance. + // AccessIPv6 specifies an IPv6 address for the instance. AccessIPv6 string `json:"accessIPv6,omitempty"` + // Min specifies Minimum number of servers to launch. + Min int `json:"min_count,omitempty"` + + // Max specifies Maximum number of servers to launch. + Max int `json:"max_count,omitempty"` + // ServiceClient will allow calls to be made to retrieve an image or // flavor ID by name. ServiceClient *gophercloud.ServiceClient `json:"-"` @@ -276,6 +282,14 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { b["flavorRef"] = flavorID } + if opts.Min != 0 { + b["min_count"] = opts.Min + } + + if opts.Max != 0 { + b["max_count"] = opts.Max + } + return map[string]interface{}{"server": b}, nil } diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index acd90f3d40..db3819abc0 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -774,6 +774,28 @@ func HandleServerCreationSuccessfully(t *testing.T, response string) { }) } +// HandleServerCreationWithCustomFieldSuccessfully sets up the test server to respond to a server creation request +// with a given response. +func HandleServersCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/servers", 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, `{ + "server": { + "name": "derp", + "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", + "flavorRef": "1", + "min_count": 3, + "max_count": 3 + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + // HandleServerCreationWithCustomFieldSuccessfully sets up the test server to respond to a server creation request // with a given response. func HandleServerCreationWithCustomFieldSuccessfully(t *testing.T, response string) { diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 0bcaca0175..b79f1e631e 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -100,6 +100,23 @@ func TestCreateServer(t *testing.T) { th.CheckDeepEquals(t, ServerDerp, *actual) } +func TestCreateServers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleServersCreationSuccessfully(t, SingleServerBody) + + actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ + Name: "derp", + ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", + FlavorRef: "1", + Min: 3, + Max: 3, + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ServerDerp, *actual) +} + func TestCreateServerWithCustomField(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 307b801773c3c1018ca059dd511b9a08714e97d6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 24 Feb 2019 16:10:35 -0700 Subject: [PATCH 0675/2296] Acc Tests: Add test to clear external gateway (#1475) --- .../networking/v2/extensions/layer3/routers_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 2ec02f0230..73390757b4 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -114,6 +114,15 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { th.AssertEquals(t, newRouter.Description, newDescription) th.AssertEquals(t, *newRouter.GatewayInfo.EnableSNAT, enableSNAT) th.AssertDeepEquals(t, newRouter.GatewayInfo.ExternalFixedIPs, efi) + + // Test Gateway removal + updateOpts = routers.UpdateOpts{ + GatewayInfo: &routers.GatewayInfo{}, + } + + newRouter, err = routers.Update(client, router.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, newRouter.GatewayInfo, routers.GatewayInfo{}) } func TestLayer3RouterInterface(t *testing.T) { From 2c53651e4c147f45decd369a876f93c968a5d90e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 25 Feb 2019 08:22:40 -0700 Subject: [PATCH 0676/2296] Containers v1: Implement Capsules Microversion 1.32 (#1469) * Container v1: Capsule v1.32 support * Contaienrs v1: Updating acceptance tests * Container v1: Refactor of microversion update * Acc Tests: Re-enabling Zun/Containers * acc test updates --- .../gophercloud-acceptance-test/run.yaml | 2 +- acceptance/clients/clients.go | 2 + acceptance/openstack/container/v1/capsules.go | 24 +++- .../openstack/container/v1/capsules_test.go | 112 ++++++++++-------- acceptance/openstack/container/v1/fixtures.go | 47 ++++++++ .../container/v1/capsules/microversions.go | 101 ++++++++++++++++ openstack/container/v1/capsules/results.go | 60 +++++++++- .../container/v1/capsules/testing/fixtures.go | 100 ++++++++++++++++ .../v1/capsules/testing/requests_test.go | 38 ++++++ script/acceptancetest | 3 +- 10 files changed, 425 insertions(+), 64 deletions(-) create mode 100644 acceptance/openstack/container/v1/fixtures.go create mode 100644 openstack/container/v1/capsules/microversions.go diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index ac2d0a9e47..202ed962a5 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -7,7 +7,7 @@ enable_services: - 'manila' - 'designate' - #- 'zun' + - 'zun' #- 'octavia' - 'neutron-ext' - install-devstack diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index c212bdf8bf..9d957d1edf 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -590,6 +590,8 @@ func NewContainerV1Client() (*gophercloud.ServiceClient, error) { return nil, err } + client = configureDebug(client) + return openstack.NewContainerV1(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) diff --git a/acceptance/openstack/container/v1/capsules.go b/acceptance/openstack/container/v1/capsules.go index 6bf682cd11..08467ce2b9 100644 --- a/acceptance/openstack/container/v1/capsules.go +++ b/acceptance/openstack/container/v1/capsules.go @@ -10,22 +10,38 @@ import ( // WaitForCapsuleStatus will poll a capsule's status until it either matches // the specified status or the status becomes Failed. -func WaitForCapsuleStatus(client *gophercloud.ServiceClient, capsule *capsules.Capsule, status string) error { +func WaitForCapsuleStatus(client *gophercloud.ServiceClient, uuid, status string) error { return tools.WaitFor(func() (bool, error) { - latest, err := capsules.Get(client, capsule.UUID).Extract() + v, err := capsules.Get(client, uuid).Extract() if err != nil { return false, err } - if latest.Status == status { + var newStatus string + if capsule, ok := v.(*capsules.Capsule); ok { + newStatus = capsule.Status + } + + if capsule, ok := v.(*capsules.CapsuleV132); ok { + newStatus = capsule.Status + } + + fmt.Println(status) + fmt.Println(newStatus) + + if newStatus == status { // Success! return true, nil } - if latest.Status == "Failed" { + if newStatus == "Failed" { return false, fmt.Errorf("Capsule in FAILED state") } + if newStatus == "Error" { + return false, fmt.Errorf("Capsule in ERROR state") + } + return false, nil }) } diff --git a/acceptance/openstack/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index 48f001df3b..98fc8e42ec 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -1,3 +1,5 @@ +// +build acceptance containers capsules + package v1 import ( @@ -9,71 +11,79 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestCapsule(t *testing.T) { +func TestCapsuleBase(t *testing.T) { client, err := clients.NewContainerV1Client() - if err != nil { - t.Fatalf("Unable to create an container v1 client: %v", err) - } th.AssertNoErr(t, err) + template := new(capsules.Template) - template.Bin = []byte(`{ - "capsuleVersion": "beta", - "kind": "capsule", - "metadata": { - "labels": { - "app": "web", - "app1": "web1" - }, - "name": "template" - }, - "spec": { - "restartPolicy": "Always", - "containers": [ - { - "command": [ - "sleep", - "1000000" - ], - "env": { - "ENV1": "/usr/local/bin", - "ENV2": "/usr/bin" - }, - "image": "ubuntu", - "imagePullPolicy": "ifnotpresent", - "ports": [ - { - "containerPort": 80, - "hostPort": 80, - "name": "nginx-port", - "protocol": "TCP" - } - ], - "resources": { - "requests": { - "cpu": 1, - "memory": 1024 - } - }, - "workDir": "/root" - } - ] + template.Bin = []byte(capsuleTemplate) + + createOpts := capsules.CreateOpts{ + TemplateOpts: template, + } + + v, err := capsules.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + capsule := v.(*capsules.Capsule) + + err = WaitForCapsuleStatus(client, capsule.UUID, "Running") + th.AssertNoErr(t, err) + + pager := capsules.List(client, nil) + err = pager.EachPage(func(page pagination.Page) (bool, error) { + v, err := capsules.ExtractCapsules(page) + th.AssertNoErr(t, err) + allCapsules := v.([]capsules.Capsule) + + for _, m := range allCapsules { + capsuleUUID := m.UUID + if capsuleUUID != capsule.UUID { + continue + } + capsule, err := capsules.Get(client, capsuleUUID).ExtractBase() + + th.AssertNoErr(t, err) + th.AssertEquals(t, capsule.MetaName, "template") + + err = capsules.Delete(client, capsuleUUID).ExtractErr() + th.AssertNoErr(t, err) + } - }`) + return true, nil + }) + th.AssertNoErr(t, err) +} + +func TestCapsuleV132(t *testing.T) { + client, err := clients.NewContainerV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.32" + + template := new(capsules.Template) + template.Bin = []byte(capsuleTemplate) + createOpts := capsules.CreateOpts{ TemplateOpts: template, } - capsule, err := capsules.Create(client, createOpts).Extract() + + capsule, err := capsules.Create(client, createOpts).ExtractV132() th.AssertNoErr(t, err) - err = WaitForCapsuleStatus(client, capsule, "Running") + + err = WaitForCapsuleStatus(client, capsule.UUID, "Running") th.AssertNoErr(t, err) + pager := capsules.List(client, nil) err = pager.EachPage(func(page pagination.Page) (bool, error) { - CapsuleList, err := capsules.ExtractCapsules(page) + allCapsules, err := capsules.ExtractCapsulesV132(page) th.AssertNoErr(t, err) - for _, m := range CapsuleList { + for _, m := range allCapsules { capsuleUUID := m.UUID - capsule, err := capsules.Get(client, capsuleUUID).Extract() + if capsuleUUID != capsule.UUID { + continue + } + capsule, err := capsules.Get(client, capsuleUUID).ExtractV132() th.AssertNoErr(t, err) th.AssertEquals(t, capsule.MetaName, "template") diff --git a/acceptance/openstack/container/v1/fixtures.go b/acceptance/openstack/container/v1/fixtures.go new file mode 100644 index 0000000000..9464af41f6 --- /dev/null +++ b/acceptance/openstack/container/v1/fixtures.go @@ -0,0 +1,47 @@ +package v1 + +const capsuleTemplate = ` + { + "capsuleVersion": "beta", + "kind": "capsule", + "metadata": { + "labels": { + "app": "web", + "app1": "web1" + }, + "name": "template" + }, + "spec": { + "restartPolicy": "Always", + "containers": [ + { + "command": [ + "sleep", + "1000000" + ], + "env": { + "ENV1": "/usr/local/bin", + "ENV2": "/usr/bin" + }, + "image": "ubuntu", + "imagePullPolicy": "ifnotpresent", + "ports": [ + { + "containerPort": 80, + "hostPort": 80, + "name": "nginx-port", + "protocol": "TCP" + } + ], + "resources": { + "requests": { + "cpu": 1, + "memory": 1024 + } + }, + "workDir": "/root" + } + ] + } + } +` diff --git a/openstack/container/v1/capsules/microversions.go b/openstack/container/v1/capsules/microversions.go new file mode 100644 index 0000000000..da8f34a510 --- /dev/null +++ b/openstack/container/v1/capsules/microversions.go @@ -0,0 +1,101 @@ +package capsules + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ExtractV132 is a function that accepts a result and extracts a capsule resource. +func (r commonResult) ExtractV132() (*CapsuleV132, error) { + var s *CapsuleV132 + err := r.ExtractInto(&s) + return s, err +} + +// Represents a Capsule at microversion vXY or greater. +type CapsuleV132 struct { + // UUID for the capsule + UUID string `json:"uuid"` + + // User ID for the capsule + UserID string `json:"user_id"` + + // Project ID for the capsule + ProjectID string `json:"project_id"` + + // cpu for the capsule + CPU float64 `json:"cpu"` + + // Memory for the capsule + Memory string `json:"memory"` + + // The name of the capsule + MetaName string `json:"name"` + + // Indicates whether capsule is currently operational. + Status string `json:"status"` + + // Indicates whether capsule is currently operational. + StatusReason string `json:"status_reason"` + + // The created time of the capsule. + CreatedAt time.Time `json:"-"` + + // The updated time of the capsule. + UpdatedAt time.Time `json:"-"` + + // Links includes HTTP references to the itself, useful for passing along to + // other APIs that might want a capsule reference. + Links []interface{} `json:"links"` + + // The capsule restart policy + RestartPolicy map[string]string `json:"restart_policy"` + + // The capsule metadata labels + MetaLabels map[string]string `json:"labels"` + + // The capsule IP addresses + Addresses map[string][]Address `json:"addresses"` + + // The container object inside capsule + Containers []Container `json:"containers"` + + // The capsule host + Host string `json:"host"` +} + +// ExtractCapsulesV132 accepts a Page struct, specifically a CapsulePage struct, +// and extracts the elements into a slice of CapsuleV132 structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractCapsulesV132(r pagination.Page) ([]CapsuleV132, error) { + var s struct { + Capsules []CapsuleV132 `json:"capsules"` + } + err := (r.(CapsulePage)).ExtractInto(&s) + return s.Capsules, err +} + +func (r *CapsuleV132) UnmarshalJSON(b []byte) error { + type tmp CapsuleV132 + + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = CapsuleV132(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index 05d17aa9ab..ea88af4ce5 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -2,6 +2,7 @@ package capsules import ( "encoding/json" + "fmt" "time" "github.com/gophercloud/gophercloud" @@ -12,13 +13,30 @@ type commonResult struct { gophercloud.Result } -// Extract is a function that accepts a result and extracts a capsule resource. -func (r commonResult) Extract() (*Capsule, error) { +// ExtractBase is a function that accepts a result and extracts +// a base a capsule resource. +func (r commonResult) ExtractBase() (*Capsule, error) { var s *Capsule err := r.ExtractInto(&s) return s, err } +// Extract is a function that accepts a result and extracts a capsule result. +// The result will be returned as an interface{} where it should be able to +// be casted as either a Capsule or CapsuleV132. +func (r commonResult) Extract() (interface{}, error) { + s, err := r.ExtractBase() + if err == nil { + return s, nil + } + + if _, ok := err.(*json.UnmarshalTypeError); !ok { + return s, err + } + + return r.ExtractV132() +} + // GetResult represents the result of a get operation. type GetResult struct { commonResult @@ -224,20 +242,50 @@ func (r CapsulePage) NextPageURL() (string, error) { // IsEmpty checks whether a CapsulePage struct is empty. func (r CapsulePage) IsEmpty() (bool, error) { is, err := ExtractCapsules(r) - return len(is) == 0, err + if err != nil { + return false, err + } + + if v, ok := is.([]Capsule); ok { + return len(v) == 0, nil + } + + if v, ok := is.([]CapsuleV132); ok { + return len(v) == 0, nil + } + + return false, fmt.Errorf("Unable to determine Capsule type") } -// ExtractCapsules accepts a Page struct, specifically a CapsulePage struct, +// ExtractCapsulesBase accepts a Page struct, specifically a CapsulePage struct, // and extracts the elements into a slice of Capsule structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractCapsules(r pagination.Page) ([]Capsule, error) { +// a generic collection is mapped into the relevant slice. +func ExtractCapsulesBase(r pagination.Page) ([]Capsule, error) { var s struct { Capsules []Capsule `json:"capsules"` } + err := (r.(CapsulePage)).ExtractInto(&s) return s.Capsules, err } +// ExtractCapsules accepts a Page struct, specifically a CapsulePage struct, +// and extracts the elements into an interface. +// This interface should be able to be casted as either a Capsule or +// CapsuleV132 struct +func ExtractCapsules(r pagination.Page) (interface{}, error) { + s, err := ExtractCapsulesBase(r) + if err == nil { + return s, nil + } + + if _, ok := err.(*json.UnmarshalTypeError); !ok { + return nil, err + } + + return ExtractCapsulesV132(r) +} + func (r *Capsule) UnmarshalJSON(b []byte) error { type tmp Capsule diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 18430d5413..4d6e785124 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -410,6 +410,51 @@ const CapsuleListBody = ` ] }` +const CapsuleV132ListBody = ` +{ + "capsules": [ + { + "uuid": "cc654059-1a77-47a3-bfcf-715bde5aad9e", + "status": "Running", + "user_id": "d33b18c384574fd2a3299447aac285f0", + "project_id": "6b8ffef2a0ac42ee87887b9cc98bdf68", + "cpu": 1, + "memory": "1024M", + "name": "test", + "labels": {"web": "app"}, + "created_at": "2018-01-12 09:37:25", + "updated_at": "2018-01-12 09:37:25", + "links": [ + { + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self" + }, + { + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark" + } + ], + "restart_policy": { + "MaximumRetryCount": "0", + "Name": "always" + }, + "addresses": { + "b1295212-64e1-471d-aa01-25ff46f9818d": [ + { + "version": 4, + "preserve_on_delete": false, + "addr": "172.24.4.11", + "port": "8439060f-381a-4386-a518-33d5a4058636", + "subnet_id": "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a" + } + ] + }, + "host": "test-host", + "status_reason": "No reason" + } + ] +}` + var ExpectedContainer1 = capsules.Container{ Name: "test-demo-omicron-13", UUID: "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", @@ -513,6 +558,49 @@ var ExpectedCapsule = capsules.Capsule{ }, } +var ExpectedCapsuleV132 = capsules.CapsuleV132{ + UUID: "cc654059-1a77-47a3-bfcf-715bde5aad9e", + Status: "Running", + UserID: "d33b18c384574fd2a3299447aac285f0", + ProjectID: "6b8ffef2a0ac42ee87887b9cc98bdf68", + CPU: float64(1), + Memory: "1024M", + MetaName: "test", + Links: []interface{}{ + map[string]interface{}{ + "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", + "rel": "bookmark", + }, + }, + RestartPolicy: map[string]string{ + "MaximumRetryCount": "0", + "Name": "always", + }, + MetaLabels: map[string]string{ + "web": "app", + }, + Addresses: map[string][]capsules.Address{ + "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ + { + PreserveOnDelete: false, + Addr: "172.24.4.11", + Port: "8439060f-381a-4386-a518-33d5a4058636", + Version: float64(4), + SubnetID: "4a2bcd64-93ad-4436-9f48-3a7f9b267e0a", + }, + }, + }, + Host: "test-host", + StatusReason: "No reason", + Containers: []capsules.Container{ + ExpectedContainer1, + }, +} + // HandleCapsuleGetOldTimeSuccessfully test setup func HandleCapsuleGetOldTimeSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", func(w http.ResponseWriter, r *http.Request) { @@ -561,6 +649,18 @@ func HandleCapsuleListSuccessfully(t *testing.T) { }) } +// HandleCapsuleV132ListSuccessfully test setup +func HandleCapsuleV132ListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/capsules/", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, CapsuleV132ListBody) + }) +} + func HandleCapsuleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/capsules/963a239d-3946-452b-be5a-055eab65a421", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 56376a08ea..2d5e7c8ea0 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -110,6 +110,44 @@ func TestListCapsule(t *testing.T) { } } +func TestListCapsuleV132(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleCapsuleV132ListSuccessfully(t) + + createdAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:25") + updatedAt, _ := time.Parse(gophercloud.RFC3339ZNoTNoZ, "2018-01-12 09:37:25") + + ec := ExpectedCapsuleV132 + + ec.CreatedAt = createdAt + ec.UpdatedAt = updatedAt + ec.Containers = nil + + expected := []capsules.CapsuleV132{ec} + + count := 0 + results := capsules.List(fakeclient.ServiceClient(), nil) + err := results.EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := capsules.ExtractCapsules(page) + if err != nil { + t.Errorf("Failed to extract capsules: %v", err) + return false, err + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/script/acceptancetest b/script/acceptancetest index 70c39ab545..486c85bd65 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -30,8 +30,7 @@ acceptance/openstack/compute/v2 # No suitable endpoint could be found in the service catalog. # acceptance/openstack/containerinfra/v1 -# Disabled temporarily -#acceptance/openstack/container/v1 +acceptance/openstack/container/v1 # Unable to create a DB client: No suitable endpoint could be found in the service catalog. # acceptance/openstack/db/v1 From 077038c4f65ca6324adea7af6907d0d770131c32 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 26 Feb 2019 06:11:32 +0100 Subject: [PATCH 0677/2296] Networking V2: Allow nested structs in port bindings create/update (#1478) * Networking V2: Allow empty host_id while updating port bindings * Networking V2: Allow nested structs in port bindings create/update --- .../portsbinding/portsbinding_test.go | 23 ++++++++++++++++++- .../v2/extensions/portsbinding/requests.go | 10 ++++---- .../portsbinding/testing/requests_test.go | 3 ++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index d697e090e3..47c8412e08 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_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/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -37,18 +38,38 @@ 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") // Update port newPortName := "" newPortDescription := "" + newHostID := "127.0.0.1" updateOpts := ports.UpdateOpts{ Name: &newPortName, Description: &newPortDescription, } - newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + + var finalUpdateOpts ports.UpdateOptsBuilder + + finalUpdateOpts = portsbinding.UpdateOptsExt{ + UpdateOptsBuilder: updateOpts, + HostID: &newHostID, + VNICType: "baremetal", + } + + var newPort PortWithBindingExt + + _, err = ports.Update(client, port.ID, finalUpdateOpts).Extract() + th.AssertNoErr(t, err) + + // Read the updated port + err = ports.Get(client, port.ID).ExtractInto(&newPort) 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, "baremetal") } diff --git a/openstack/networking/v2/extensions/portsbinding/requests.go b/openstack/networking/v2/extensions/portsbinding/requests.go index 3c287463aa..647b67ff18 100644 --- a/openstack/networking/v2/extensions/portsbinding/requests.go +++ b/openstack/networking/v2/extensions/portsbinding/requests.go @@ -20,7 +20,7 @@ type CreateOptsExt struct { // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. - Profile map[string]string `json:"binding:profile,omitempty"` + Profile map[string]interface{} `json:"binding:profile,omitempty"` } // ToPortCreateMap casts a CreateOpts struct to a map. @@ -54,7 +54,7 @@ type UpdateOptsExt struct { ports.UpdateOptsBuilder // The ID of the host where the port is allocated. - HostID string `json:"binding:host_id,omitempty"` + HostID *string `json:"binding:host_id,omitempty"` // The virtual network interface card (vNIC) type that is bound to the // neutron port. @@ -63,7 +63,7 @@ type UpdateOptsExt struct { // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. - Profile map[string]string `json:"binding:profile,omitempty"` + Profile map[string]interface{} `json:"binding:profile,omitempty"` } // ToPortUpdateMap casts an UpdateOpts struct to a map. @@ -75,8 +75,8 @@ func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { port := base["port"].(map[string]interface{}) - if opts.HostID != "" { - port["binding:host_id"] = opts.HostID + if opts.HostID != nil { + port["binding:host_id"] = *opts.HostID } if opts.VNICType != "" { diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go index 95f5a645f9..8f3da66743 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go @@ -165,9 +165,10 @@ func TestUpdate(t *testing.T) { SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, } + hostID := "HOST1" updateOpts := portsbinding.UpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - HostID: "HOST1", + HostID: &hostID, VNICType: "normal", } From 811f1e8d3f5caeaa32b8858db83f7d49e8f23045 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Tue, 26 Feb 2019 11:49:17 -0500 Subject: [PATCH 0678/2296] Baremetal API v1: Node provision state (#1452) * Baremetal API v1: Node provision states * Fix JSON required values --- openstack/baremetal/v1/nodes/requests.go | 65 ++++++++++++++++ openstack/baremetal/v1/nodes/results.go | 6 ++ .../baremetal/v1/nodes/testing/fixtures.go | 39 ++++++++++ .../v1/nodes/testing/requests_test.go | 75 +++++++++++++++++++ openstack/baremetal/v1/nodes/urls.go | 4 + 5 files changed, 189 insertions(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 5aa961e4a8..c270d5b971 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -45,6 +45,22 @@ const ( UnrescueFail = "unrescue failed" ) +// TargetProvisionState is used when setting the provision state for a node. +type TargetProvisionState string + +const ( + TargetActive TargetProvisionState = "active" + TargetDeleted = "deleted" + TargetManage = "manage" + TargetProvide = "provide" + TargetInspect = "inspect" + TargetAbort = "abort" + TargetClean = "clean" + TargetAdopt = "adopt" + TargetRescue = "rescue" + TargetUnrescue = "unrescue" +) + // 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 node attributes you want to see returned. Marker and Limit are used @@ -371,3 +387,52 @@ func GetSupportedBootDevices(client *gophercloud.ServiceClient, id string) (r Su }) return } + +// A cleaning step has required keys ‘interface’ and ‘step’, and optional key ‘args’. If specified, +// the value for ‘args’ is a keyword variable argument dictionary that is passed to the cleaning step +// method. +type CleanStep struct { + Interface string `json:"interface" required:"true"` + Step string `json:"step" required:"true"` + Args map[string]string `json:"args,omitempty"` +} + +// ProvisionStateOptsBuilder allows extensions to add additional parameters to the +// ChangeProvisionState request. +type ProvisionStateOptsBuilder interface { + ToProvisionStateMap() (map[string]interface{}, error) +} + +// ProvisionStateOpts for a request to change a node's provision state. A config drive should be base64-encoded +// gzipped ISO9660 image. +type ProvisionStateOpts struct { + Target TargetProvisionState `json:"target" required:"true"` + ConfigDrive string `json:"configdrive,omitempty"` + CleanSteps []CleanStep `json:"clean_steps,omitempty"` + RescuePassword string `json:"rescue_password,omitempty"` +} + +// ToProvisionStateMap assembles a request body based on the contents of a CreateOpts. +func (opts ProvisionStateOpts) ToProvisionStateMap() (map[string]interface{}, error) { + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return body, nil +} + +// Request a change to the Node’s provision state. Acceptable target states depend on the Node’s current provision +// state. More detailed documentation of the Ironic State Machine is available in the developer docs. +func ChangeProvisionState(client *gophercloud.ServiceClient, id string, opts ProvisionStateOptsBuilder) (r ChangeStateResult) { + reqBody, err := opts.ToProvisionStateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Put(provisionStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 7df7b45c84..2aa6eecf67 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -299,3 +299,9 @@ type NodeValidation struct { Rescue DriverValidation `json:"rescue"` Storage DriverValidation `json:"storage"` } + +// ChangeStateResult is the response from any state change operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type ChangeStateResult struct { + gophercloud.ErrResult +} diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index ea6d91c8ad..6d6b9f9e47 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -541,6 +541,27 @@ const NodeSupportedBootDeviceBody = ` } ` +const NodeProvisionStateActiveBody = ` +{ + "target": "active", + "configdrive": "http://127.0.0.1/images/test-node-config-drive.iso.gz" +} +` +const NodeProvisionStateCleanBody = ` +{ + "target": "clean", + "clean_steps": [ + { + "interface": "deploy", + "step": "upgrade_firmware", + "args": { + "force": "True" + } + } + ] +} +` + var ( NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", @@ -870,3 +891,21 @@ func HandleGetSupportedBootDeviceSuccessfully(t *testing.T) { fmt.Fprintf(w, NodeSupportedBootDeviceBody) }) } + +func HandleNodeChangeProvisionStateActive(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateActiveBody) + w.WriteHeader(http.StatusAccepted) + }) +} + +func HandleNodeChangeProvisionStateClean(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateCleanBody) + 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 14819d3e18..6c2b5fdbd5 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -3,6 +3,7 @@ package testing import ( "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -208,3 +209,77 @@ func TestGetSupportedBootDevices(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeSupportedBootDevice, bootDevices) } + +func TestNodeChangeProvisionStateActive(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeChangeProvisionStateActive(t) + + c := client.ServiceClient() + err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + Target: nodes.TargetActive, + ConfigDrive: "http://127.0.0.1/images/test-node-config-drive.iso.gz", + }).ExtractErr() + + th.AssertNoErr(t, err) +} + +func TestNodeChangeProvisionStateClean(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeChangeProvisionStateClean(t) + + c := client.ServiceClient() + err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + Target: nodes.TargetClean, + CleanSteps: []nodes.CleanStep{ + { + Interface: "deploy", + Step: "upgrade_firmware", + Args: map[string]string{ + "force": "True", + }, + }, + }, + }).ExtractErr() + + th.AssertNoErr(t, err) +} + +func TestCleanStepRequiresInterface(t *testing.T) { + c := client.ServiceClient() + err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + Target: nodes.TargetClean, + CleanSteps: []nodes.CleanStep{ + { + Step: "upgrade_firmware", + Args: map[string]string{ + "force": "True", + }, + }, + }, + }).ExtractErr() + + if _, ok := err.(gophercloud.ErrMissingInput); !ok { + t.Fatal("ErrMissingInput was expected to occur") + } +} + +func TestCleanStepRequiresStep(t *testing.T) { + c := client.ServiceClient() + err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + Target: nodes.TargetClean, + CleanSteps: []nodes.CleanStep{ + { + Interface: "deploy", + Args: map[string]string{ + "force": "True", + }, + }, + }, + }).ExtractErr() + + if _, ok := err.(gophercloud.ErrMissingInput); !ok { + t.Fatal("ErrMissingInput was expected to occur") + } +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index cd6fafa937..3fffc110fc 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -41,3 +41,7 @@ func bootDeviceURL(client *gophercloud.ServiceClient, id string) string { func supportedBootDeviceURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "management", "boot_device", "supported") } + +func provisionStateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "states", "provision") +} From 423572c9202a5edc998190069d0fb9803766975f Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Tue, 26 Feb 2019 11:50:14 -0500 Subject: [PATCH 0679/2296] Baremetal API V1: Handle required opts correctly (#1482) --- openstack/baremetal/v1/nodes/requests.go | 22 ++++++++------- .../v1/nodes/testing/requests_test.go | 28 +++++++++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index c270d5b971..23a3229389 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -265,7 +265,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } type Patch interface { - ToNodeUpdateMap() map[string]interface{} + ToNodeUpdateMap() (map[string]interface{}, error) } // UpdateOpts is a slice of Patches used to update a node @@ -280,24 +280,26 @@ const ( ) type UpdateOperation struct { - Op UpdateOp `json:"op,required"` - Path string `json:"path,required"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` Value string `json:"value,omitempty"` } -func (opts UpdateOperation) ToNodeUpdateMap() map[string]interface{} { - return map[string]interface{}{ - "op": opts.Op, - "path": opts.Path, - "value": opts.Value, - } +func (opts UpdateOperation) ToNodeUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") } // Update requests that a node be updated func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { body := make([]map[string]interface{}, len(opts)) for i, patch := range opts { - body[i] = patch.ToNodeUpdateMap() + result, err := patch.ToNodeUpdateMap() + if err != nil { + r.Err = err + return + } + + body[i] = result } resp, err := client.Request("PATCH", updateURL(client, id), &gophercloud.RequestOpts{ diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 6c2b5fdbd5..a80ee55db5 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -151,9 +151,37 @@ func TestUpdateNode(t *testing.T) { } th.CheckDeepEquals(t, NodeFoo, *actual) +} + +func TestUpdateRequiredOp(t *testing.T) { + c := client.ServiceClient() + _, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ + nodes.UpdateOperation{ + Path: "/driver", + Value: "new-driver", + }, + }).Extract() + + if _, ok := err.(gophercloud.ErrMissingInput); !ok { + t.Fatal("ErrMissingInput was expected to occur") + } } +func TestUpdateRequiredPath(t *testing.T) { + c := client.ServiceClient() + _, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ + nodes.UpdateOperation{ + Op: nodes.ReplaceOp, + Value: "new-driver", + }, + }).Extract() + + if _, ok := err.(gophercloud.ErrMissingInput); !ok { + t.Fatal("ErrMissingInput was expected to occur") + } +} + func TestValidateNode(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 490361a9a1c1bb9fdcdb7d370f03cd3a86b4b301 Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Tue, 26 Feb 2019 15:31:17 -0300 Subject: [PATCH 0680/2296] Baremetal V1 API Ports (#1453) * Baremetal V1 API: Ports This commit add the Ports API calls according to [1] - List Ports (GET) - Create Port (POST) - List Detailed Ports (GET) - Show Port Details (GET) - Update a Port (PATCH) - Delete Port (DELETE) [1] https://developer.openstack.org/api-ref/baremetal/?expanded=#ports-ports * Updates for Ports API - Fix Address in doc - Renamed IsSmartnic to IsSmartNIC - Fixed Indentation using go fmt - Fixed acceptance test * Update Tests - Fix acceptance for noauth and v1 to use correct field - Fix undefined field for Port in requests_test * Update Acceptance and Unit Tests -Fixed acceptance tests (validated using DevStack - Stein on Ubuntu) -Fixed unnit tests -Fixed Links variable type * Fix noauth acceptance - Missing v1 before the functions to create fake node and delete * Fixes in requests and acceptance -Added header for acceptance tests -Change `bool` to `*bool` to allow true and false for PXEEnabled and IsSmartNIC -Change UpdateOperation Value to `interface{}` to avoid problems when not using string (e.g, booleans, jsons) * Fix value for type *bool Type `*bool` requires the pointer for a variable with `bool` value * Fix typo in variable name on acceptance - `itrue` is undefined, change to `iTrue` that is defined on line 71. * Fix testing for *bool type Don't set bool value directly to PXEEnabled since it's `*bool` type --- .../openstack/baremetal/noauth/ports_test.go | 73 ++++++ .../openstack/baremetal/v1/baremetal.go | 49 ++++ .../openstack/baremetal/v1/ports_test.go | 72 +++++ openstack/baremetal/v1/ports/doc.go | 73 ++++++ openstack/baremetal/v1/ports/requests.go | 221 ++++++++++++++++ openstack/baremetal/v1/ports/results.go | 129 +++++++++ openstack/baremetal/v1/ports/testing/doc.go | 2 + .../baremetal/v1/ports/testing/fixtures.go | 247 ++++++++++++++++++ .../v1/ports/testing/requests_test.go | 144 ++++++++++ openstack/baremetal/v1/ports/urls.go | 31 +++ 10 files changed, 1041 insertions(+) create mode 100644 acceptance/openstack/baremetal/noauth/ports_test.go create mode 100644 acceptance/openstack/baremetal/v1/ports_test.go create mode 100644 openstack/baremetal/v1/ports/doc.go create mode 100644 openstack/baremetal/v1/ports/requests.go create mode 100644 openstack/baremetal/v1/ports/results.go create mode 100644 openstack/baremetal/v1/ports/testing/doc.go create mode 100644 openstack/baremetal/v1/ports/testing/fixtures.go create mode 100644 openstack/baremetal/v1/ports/testing/requests_test.go create mode 100644 openstack/baremetal/v1/ports/urls.go diff --git a/acceptance/openstack/baremetal/noauth/ports_test.go b/acceptance/openstack/baremetal/noauth/ports_test.go new file mode 100644 index 0000000000..2498cdb68f --- /dev/null +++ b/acceptance/openstack/baremetal/noauth/ports_test.go @@ -0,0 +1,73 @@ +// +build acceptance baremetal ports + +package noauth + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" + "github.com/gophercloud/gophercloud/pagination" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestPortsCreateDestroy(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1NoAuthClient() + th.AssertNoErr(t, err) + client.Microversion = "1.53" + + node, err := v1.CreateFakeNode(t, client) + port, err := v1.CreatePort(t, client, node) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + defer v1.DeletePort(t, client, port) + + found := false + err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + portList, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } + + for _, p := range portList { + if p.UUID == port.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + + th.AssertEquals(t, found, true) +} + +func TestPortsUpdate(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1NoAuthClient() + th.AssertNoErr(t, err) + client.Microversion = "1.53" + + node, err := v1.CreateFakeNode(t, client) + port, err := v1.CreatePort(t, client, node) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + defer v1.DeletePort(t, client, port) + + updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ + ports.UpdateOperation{ + Op: ports.ReplaceOp, + Path: "/address", + Value: "aa:bb:cc:dd:ee:ff", + }, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.Address, "aa:bb:cc:dd:ee:ff") +} diff --git a/acceptance/openstack/baremetal/v1/baremetal.go b/acceptance/openstack/baremetal/v1/baremetal.go index 31a18bbf03..c4f717a866 100644 --- a/acceptance/openstack/baremetal/v1/baremetal.go +++ b/acceptance/openstack/baremetal/v1/baremetal.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" ) // CreateNode creates a basic node with a randomly generated name. @@ -39,3 +40,51 @@ func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Nod t.Logf("Deleted server: %s", node.UUID) } + +// CreateFakeNode creates a node with fake-hardware to use for port tests. +func CreateFakeNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, error) { + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create bare metal node: %s", name) + + node, err := nodes.Create(client, nodes.CreateOpts{ + Name: name, + Driver: "fake-hardware", + BootInterface: "pxe", + DriverInfo: map[string]interface{}{ + "ipmi_port": "6230", + "ipmi_username": "admin", + "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", + "ipmi_address": "192.168.122.1", + "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", + "ipmi_password": "admin", + }, + }).Extract() + + return node, err +} + +// CreatePort - creates a port for a node with a fixed Address +func CreatePort(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Node) (*ports.Port, error) { + mac := "e6:72:1f:52:00:f4" + t.Logf("Attempting to create Port for Node: %s with Address: %s", node.UUID, mac) + + iTrue := true + port, err := ports.Create(client, ports.CreateOpts{ + NodeUUID: node.UUID, + Address: mac, + PXEEnabled: &iTrue, + }).Extract() + + return port, err +} + +// DeletePort - deletes a port via its UUID +func DeletePort(t *testing.T, client *gophercloud.ServiceClient, port *ports.Port) { + err := ports.Delete(client, port.UUID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete port %s: %s", port.UUID, err) + } + + t.Logf("Deleted port: %s", port.UUID) + +} diff --git a/acceptance/openstack/baremetal/v1/ports_test.go b/acceptance/openstack/baremetal/v1/ports_test.go new file mode 100644 index 0000000000..3547e95cc7 --- /dev/null +++ b/acceptance/openstack/baremetal/v1/ports_test.go @@ -0,0 +1,72 @@ +// +build acceptance baremetal ports + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" + "github.com/gophercloud/gophercloud/pagination" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestPortsCreateDestroy(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.53" + + node, err := CreateFakeNode(t, client) + port, err := CreatePort(t, client, node) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node) + defer DeletePort(t, client, port) + + found := false + err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + portList, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } + + for _, p := range portList { + if p.UUID == port.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + + th.AssertEquals(t, found, true) +} + +func TestPortsUpdate(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.53" + + node, err := CreateFakeNode(t, client) + port, err := CreatePort(t, client, node) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node) + defer DeletePort(t, client, port) + + updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ + ports.UpdateOperation{ + Op: ports.ReplaceOp, + Path: "/address", + Value: "aa:bb:cc:dd:ee:ff", + }, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.Address, "aa:bb:cc:dd:ee:ff") +} diff --git a/openstack/baremetal/v1/ports/doc.go b/openstack/baremetal/v1/ports/doc.go new file mode 100644 index 0000000000..4e02409bbd --- /dev/null +++ b/openstack/baremetal/v1/ports/doc.go @@ -0,0 +1,73 @@ +package 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, ports.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 + } + + return true, nil + }) + + // Example to List Ports + ports.List(client, ports.ListOpts{ + Limit: 10, + }).EachPage(func(page pagination.Page) (bool, error) { + portList, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } + + for _, n := range portList { + // Do something + } + + return true, nil + }) + + // Example to Create a Port + createPort, err := ports.Create(client, ports.CreateOpts{ + NodeUUID: "e8920409-e07e-41bb-8cc1-72acb103e2dd", + Address: "00:1B:63:84:45:E6", + PhysicalNetwork: "my-network", + }).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) + } + + // Example to Update a Port + updatePort, err := ports.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", ports.UpdateOpts{ + ports.UpdateOperation{ + Op: ReplaceOp, + Path: "/address", + Value: "22:22:22:22:22:22", + }, + }).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) + +*/ diff --git a/openstack/baremetal/v1/ports/requests.go b/openstack/baremetal/v1/ports/requests.go new file mode 100644 index 0000000000..89dc4b5696 --- /dev/null +++ b/openstack/baremetal/v1/ports/requests.go @@ -0,0 +1,221 @@ +package ports + +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 { + ToPortListQuery() (string, error) + ToPortListDetailQuery() (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 node attributes you want to see returned. Marker and Limit are used +// for pagination. +type ListOpts struct { + // Filter the list by the name or uuid of the Node + Node string `q:"node"` + + // Filter the list by the Node uuid + NodeUUID string `q:"node_uuid"` + + // Filter the list with the specified Portgroup (name or UUID) + PortGroup string `q:"portgroup"` + + // Filter the list with the specified physical hardware address, typically MAC + Address string `q:"address"` + + // 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. + // Valid value is asc (ascending) or desc (descending). Default is asc. + SortDir string `q:"sort_dir"` + + // Sorts the response by the this attribute value. Default is id. + SortKey string `q:"sort_key"` +} + +// ToPortListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPortListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list ports accessible to you. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToPortListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return PortPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// ToPortListDetailQuery formats a ListOpts into a query string for the list details API. +func (opts ListOpts) ToPortListDetailQuery() (string, error) { + // Detail endpoint can't filter by Fields + if len(opts.Fields) > 0 { + return "", fmt.Errorf("fields is not a valid option when getting a detailed listing of ports") + } + + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDetail - Return a list ports with complete details. +// Some filtering is possible by passing in flags in "ListOpts", +// but you cannot limit by the fields returned. +func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listDetailURL(client) + if opts != nil { + query, err := opts.ToPortListDetailQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return PortPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get - requests the details off a port, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPortCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies port creation parameters. +type CreateOpts struct { + // UUID of the Node this resource belongs to. + NodeUUID string `json:"node_uuid,omitempty"` + + // Physical hardware address of this network Port, + // typically the hardware MAC address. + Address string `json:"address,omitempty"` + + // UUID of the Portgroup this resource belongs to. + PortGroupUUID string `json:"portgroup_uuid,omitempty"` + + // The Port binding profile. If specified, must contain switch_id (only a MAC + // address or an OpenFlow based datapath_id of the switch are accepted in this + // field) and port_id (identifier of the physical port on the switch to which + // node’s port is connected to) fields. switch_info is an optional string + // field to be used to store any vendor-specific information. + LocalLinkConnection map[string]interface{} `json:"local_link_connection,omitempty"` + + // Indicates whether PXE is enabled or disabled on the Port. + PXEEnabled *bool `json:"pxe_enabled,omitempty"` + + // The name of the physical network to which a port is connected. May be empty. + PhysicalNetwork string `json:"physical_network,omitempty"` + + // A set of one or more arbitrary metadata key and value pairs. + Extra map[string]interface{} `json:"extra,omitempty"` + + // Indicates whether the Port is a Smart NIC port. + IsSmartNIC *bool `json:"is_smartnic,omitempty"` +} + +// ToPortCreateMap assembles a request body based on the contents of a CreateOpts. +func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return body, nil +} + +// Create - requests the creation of a port +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + reqBody, err := opts.ToPortCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(createURL(client), reqBody, &r.Body, nil) + return +} + +// TODO Update +type Patch interface { + ToPortUpdateMap() map[string]interface{} +} + +// UpdateOpts is a slice of Patches used to update a port +type UpdateOpts []Patch + +type UpdateOp string + +const ( + ReplaceOp UpdateOp = "replace" + AddOp UpdateOp = "add" + RemoveOp UpdateOp = "remove" +) + +type UpdateOperation struct { + Op UpdateOp `json:"op,required"` + Path string `json:"path,required"` + Value interface{} `json:"value,omitempty"` +} + +func (opts UpdateOperation) ToPortUpdateMap() map[string]interface{} { + return map[string]interface{}{ + "op": opts.Op, + "path": opts.Path, + "value": opts.Value, + } +} + +// Update - requests the update of a port +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + body := make([]map[string]interface{}, len(opts)) + for i, patch := range opts { + body[i] = patch.ToPortUpdateMap() + } + + resp, err := client.Request("PATCH", updateURL(client, id), &gophercloud.RequestOpts{ + JSONBody: &body, + OkCodes: []int{200}, + }) + + r.Body = resp.Body + r.Header = resp.Header + r.Err = err + + return +} + +// Delete - requests the deletion of a port +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/baremetal/v1/ports/results.go b/openstack/baremetal/v1/ports/results.go new file mode 100644 index 0000000000..f6695b595d --- /dev/null +++ b/openstack/baremetal/v1/ports/results.go @@ -0,0 +1,129 @@ +package ports + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type portResult struct { + gophercloud.Result +} + +func (r portResult) Extract() (*Port, error) { + var s Port + err := r.ExtractInto(&s) + return &s, err +} + +func (r portResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "") +} + +func ExtractPortsInto(r pagination.Page, v interface{}) error { + return r.(PortPage).Result.ExtractIntoSlicePtr(v, "ports") +} + +// Port represents a port in the OpenStack Bare Metal API. +type Port struct { + // UUID for the resource. + UUID string `json:"uuid"` + + // Physical hardware address of this network Port, + // typically the hardware MAC address. + Address string `json:"address"` + + // UUID of the Node this resource belongs to. + NodeUUID string `json:"node_uuid"` + + // UUID of the Portgroup this resource belongs to. + PortGroupUUID string `json:"portgroup_uuid"` + + // The Port binding profile. If specified, must contain switch_id (only a MAC + // address or an OpenFlow based datapath_id of the switch are accepted in this + // field) and port_id (identifier of the physical port on the switch to which + // node’s port is connected to) fields. switch_info is an optional string + // field to be used to store any vendor-specific information. + LocalLinkConnection map[string]interface{} `json:"local_link_connection"` + + // Indicates whether PXE is enabled or disabled on the Port. + PXEEnabled bool `json:"pxe_enabled"` + + // The name of the physical network to which a port is connected. + // May be empty. + PhysicalNetwork string `json:"physical_network"` + + // Internal metadata set and stored by the Port. This field is read-only. + InternalInfo map[string]interface{} `json:"internal_info"` + + // A set of one or more arbitrary metadata key and value pairs. + Extra map[string]interface{} `json:"extra"` + + // The UTC date and time when the resource was created, ISO 8601 format. + CreatedAt string `json:"created_at"` + + // The UTC date and time when the resource was updated, ISO 8601 format. + // May be “null”. + UpdatedAt string `json:"updated_at"` + + // A list of relative links. Includes the self and bookmark links. + Links []interface{} `json:"links"` + + // Indicates whether the Port is a Smart NIC port. + IsSmartNIC bool `json:"is_smartnic"` +} + +// PortPage abstracts the raw results of making a List() request against +// the API. +type PortPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a page contains no Port results. +func (r PortPage) IsEmpty() (bool, error) { + s, err := ExtractPorts(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r PortPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"ports_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractPorts interprets the results of a single page from a List() call, +// producing a slice of Port entities. +func ExtractPorts(r pagination.Page) ([]Port, error) { + var s []Port + err := ExtractPortsInto(r, &s) + return s, err +} + +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as a Port. +type GetResult struct { + portResult +} + +// CreateResult is the response from a Create operation. +type CreateResult struct { + portResult +} + +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Port. +type UpdateResult struct { + portResult +} + +// 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/baremetal/v1/ports/testing/doc.go b/openstack/baremetal/v1/ports/testing/doc.go new file mode 100644 index 0000000000..bf82f4eb0d --- /dev/null +++ b/openstack/baremetal/v1/ports/testing/doc.go @@ -0,0 +1,2 @@ +// ports unit tests +package testing diff --git a/openstack/baremetal/v1/ports/testing/fixtures.go b/openstack/baremetal/v1/ports/testing/fixtures.go new file mode 100644 index 0000000000..787511a144 --- /dev/null +++ b/openstack/baremetal/v1/ports/testing/fixtures.go @@ -0,0 +1,247 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// PortListBody contains the canned body of a ports.List response, without detail. +const PortListBody = ` +{ + "ports": [ + { + "uuid": "3abe3f36-9708-4e9f-b07e-0f898061d3a7", + "links": [ + { + "href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", + "rel": "self" + }, + { + "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", + "rel": "bookmark" + } + ], + "address": "52:54:00:0a:af:d1" + }, + { + "uuid": "f2845e11-dbd4-4728-a8c0-30d19f48924a", + "links": [ + { + "href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", + "rel": "self" + }, + { + "href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", + "rel": "bookmark" + } + ], + "address": "52:54:00:4d:87:e6" + } + ] +} +` + +// PortListDetailBody contains the canned body of a port.ListDetail response. +const PortListDetailBody = ` +{ + "ports": [ + { + "local_link_connection": {}, + "node_uuid": "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", + "uuid": "3abe3f36-9708-4e9f-b07e-0f898061d3a7", + "links": [ + { + "href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", + "rel": "self" + }, + { + "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", + "rel": "bookmark" + } + ], + "extra": {}, + "pxe_enabled": true, + "portgroup_uuid": null, + "updated_at": "2019-02-15T09:55:19+00:00", + "physical_network": null, + "address": "52:54:00:0a:af:d1", + "internal_info": { + + }, + "created_at": "2019-02-15T09:52:23+00:00" + }, + { + "local_link_connection": {}, + "node_uuid": "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", + "uuid": "f2845e11-dbd4-4728-a8c0-30d19f48924a", + "links": [ + { + "href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", + "rel": "self" + }, + { + "href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", + "rel": "bookmark" + } + ], + "extra": {}, + "pxe_enabled": true, + "portgroup_uuid": null, + "updated_at": "2019-02-15T09:55:19+00:00", + "physical_network": null, + "address": "52:54:00:4d:87:e6", + "internal_info": {}, + "created_at": "2019-02-15T09:52:24+00:00" + } + ] +} +` + +// SinglePortBody is the canned body of a Get request on an existing port. +const SinglePortBody = ` +{ + "local_link_connection": { + + }, + "node_uuid": "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", + "uuid": "f2845e11-dbd4-4728-a8c0-30d19f48924a", + "links": [ + { + "href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", + "rel": "self" + }, + { + "href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", + "rel": "bookmark" + } + ], + "extra": { + + }, + "pxe_enabled": true, + "portgroup_uuid": null, + "updated_at": "2019-02-15T09:55:19+00:00", + "physical_network": null, + "address": "52:54:00:4d:87:e6", + "internal_info": { + + }, + "created_at": "2019-02-15T09:52:24+00:00" +} +` + +var ( + PortFoo = ports.Port{ + UUID: "f2845e11-dbd4-4728-a8c0-30d19f48924a", + NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", + Address: "52:54:00:4d:87:e6", + PXEEnabled: true, + LocalLinkConnection: map[string]interface{}{}, + InternalInfo: map[string]interface{}{}, + Extra: map[string]interface{}{}, + CreatedAt: "2019-02-15T09:52:24+00:00", + UpdatedAt: "2019-02-15T09:55:19+00:00", + Links: []interface{}{map[string]interface{}{"href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "self"}, map[string]interface{}{"href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "bookmark"}}, + } + + PortBar = ports.Port{ + UUID: "3abe3f36-9708-4e9f-b07e-0f898061d3a7", + NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", + Address: "52:54:00:0a:af:d1", + PXEEnabled: true, + LocalLinkConnection: map[string]interface{}{}, + InternalInfo: map[string]interface{}{}, + Extra: map[string]interface{}{}, + CreatedAt: "2019-02-15T09:52:23+00:00", + UpdatedAt: "2019-02-15T09:55:19+00:00", + Links: []interface{}{map[string]interface{}{"href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "self"}, map[string]interface{}{"rel": "bookmark", "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7"}}, + } +) + +// HandlePortListSuccessfully sets up the test server to respond to a port List request. +func HandlePortListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/ports", 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, PortListBody) + + case "f2845e11-dbd4-4728-a8c0-30d19f48924a": + fmt.Fprintf(w, `{ "ports": [] }`) + default: + t.Fatalf("/ports invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandlePortListSuccessfully sets up the test server to respond to a port List request. +func HandlePortListDetailSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/ports/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") + r.ParseForm() + + fmt.Fprintf(w, PortListDetailBody) + }) +} + +// HandleSPortCreationSuccessfully sets up the test server to respond to a port creation request +// with a given response. +func HandlePortCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/ports", 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, `{ + "node_uuid": "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", + "address": "52:54:00:4d:87:e6", + "pxe_enabled": true + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// HandlePortDeletionSuccessfully sets up the test server to respond to a port deletion request. +func HandlePortDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", 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 HandlePortGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", 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, SinglePortBody) + }) +} + +func HandlePortUpdateSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + 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, `[{"op": "replace", "path": "/address", "value": "22:22:22:22:22:22"}]`) + + fmt.Fprintf(w, response) + }) +} diff --git a/openstack/baremetal/v1/ports/testing/requests_test.go b/openstack/baremetal/v1/ports/testing/requests_test.go new file mode 100644 index 0000000000..cf9519b53d --- /dev/null +++ b/openstack/baremetal/v1/ports/testing/requests_test.go @@ -0,0 +1,144 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListDetailPorts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePortListDetailSuccessfully(t) + + pages := 0 + err := ports.ListDetail(client.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 ports, got %d", len(actual)) + } + th.CheckDeepEquals(t, PortBar, actual[0]) + th.CheckDeepEquals(t, PortFoo, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListPorts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePortListSuccessfully(t) + + pages := 0 + err := ports.List(client.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 ports, got %d", len(actual)) + } + th.AssertEquals(t, "3abe3f36-9708-4e9f-b07e-0f898061d3a7", actual[0].UUID) + th.AssertEquals(t, "f2845e11-dbd4-4728-a8c0-30d19f48924a", actual[1].UUID) + + 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 + opts := ports.ListOpts{ + Fields: []string{"uuid", "address"}, + } + + _, err := opts.ToPortListDetailQuery() + th.AssertEquals(t, err.Error(), "fields is not a valid option when getting a detailed listing of ports") + + // Regular ListOpts can + query, err := opts.ToPortListQuery() + th.AssertEquals(t, query, "?fields=uuid&fields=address") + th.AssertNoErr(t, err) +} + +func TestCreatePort(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePortCreationSuccessfully(t, SinglePortBody) + + iTrue := true + actual, err := ports.Create(client.ServiceClient(), ports.CreateOpts{ + NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", + Address: "52:54:00:4d:87:e6", + PXEEnabled: &iTrue, + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, PortFoo, *actual) +} + +func TestDeletePort(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePortDeletionSuccessfully(t) + + res := ports.Delete(client.ServiceClient(), "3abe3f36-9708-4e9f-b07e-0f898061d3a7") + th.AssertNoErr(t, res.Err) +} + +func TestGetPort(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePortGetSuccessfully(t) + + c := client.ServiceClient() + actual, err := ports.Get(c, "f2845e11-dbd4-4728-a8c0-30d19f48924a").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, PortFoo, *actual) +} + +func TestUpdatePort(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandlePortUpdateSuccessfully(t, SinglePortBody) + + c := client.ServiceClient() + actual, err := ports.Update(c, "f2845e11-dbd4-4728-a8c0-30d19f48924a", ports.UpdateOpts{ + ports.UpdateOperation{ + Op: ports.ReplaceOp, + Path: "/address", + Value: "22:22:22:22:22:22", + }, + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, PortFoo, *actual) +} diff --git a/openstack/baremetal/v1/ports/urls.go b/openstack/baremetal/v1/ports/urls.go new file mode 100644 index 0000000000..436c954150 --- /dev/null +++ b/openstack/baremetal/v1/ports/urls.go @@ -0,0 +1,31 @@ +package ports + +import "github.com/gophercloud/gophercloud" + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("ports") +} + +func listURL(client *gophercloud.ServiceClient) string { + return createURL(client) +} + +func listDetailURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("ports", "detail") +} + +func resourceURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("ports", id) +} + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return resourceURL(client, id) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return resourceURL(client, id) +} + +func updateURL(client *gophercloud.ServiceClient, id string) string { + return resourceURL(client, id) +} From 8dba33c1680df245dc5bb7ad1de6dd6867782938 Mon Sep 17 00:00:00 2001 From: lbischof <1837725+lbischof@users.noreply.github.com> Date: Wed, 27 Feb 2019 17:36:19 +0100 Subject: [PATCH 0681/2296] Correct reference to wrong function name (#1484) --- openstack/compute/v2/flavors/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 539019e90d..753024a18b 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -148,7 +148,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Get retrieves details of a single flavor. Use ExtractFlavor to convert its +// Get retrieves details of a single flavor. Use Extract to convert its // result into a Flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) From e8cb60dfd96d0ed65e52d91f87ac67d68f9e7f93 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 27 Feb 2019 18:35:24 +0100 Subject: [PATCH 0682/2296] Fix typos in tags for ListOpts (#1487) --- .../v3/applicationcredentials/requests.go | 2 +- .../networking/v2/extensions/agents/requests.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/openstack/identity/v3/applicationcredentials/requests.go b/openstack/identity/v3/applicationcredentials/requests.go index c7f089a519..61111d2ef2 100644 --- a/openstack/identity/v3/applicationcredentials/requests.go +++ b/openstack/identity/v3/applicationcredentials/requests.go @@ -14,7 +14,7 @@ type ListOptsBuilder interface { // ListOpts provides options to filter the List results. type ListOpts struct { // Name filters the response by an application credential name - Name string `json:"name"` + Name string `q:"name"` } // ToApplicationCredentialListQuery formats a ListOpts into a query string. diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index e0e2a040a6..566f4dea46 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -18,14 +18,14 @@ type ListOptsBuilder interface { // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for the pagination. type ListOpts struct { - ID string `json:"id"` - AgentType string `json:"agent_type"` - Alive *bool `json:"alive"` - AvailabilityZone string `json:"availability_zone"` - Binary string `json:"binary"` - Description string `json:"description"` - Host string `json:"host"` - Topic string `json:"topic"` + ID string `q:"id"` + AgentType string `q:"agent_type"` + Alive *bool `q:"alive"` + AvailabilityZone string `q:"availability_zone"` + Binary string `q:"binary"` + Description string `q:"description"` + Host string `q:"host"` + Topic string `q:"topic"` Limit int `q:"limit"` Marker string `q:"marker"` SortKey string `q:"sort_key"` From cfa8434422e74c7965e33f428931db21cfb58c29 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Wed, 27 Feb 2019 12:35:56 -0500 Subject: [PATCH 0683/2296] Baremetal API v1: Node Power State (#1479) --- openstack/baremetal/v1/nodes/requests.go | 46 +++++++++++++++++++ openstack/baremetal/v1/nodes/results.go | 6 +++ .../baremetal/v1/nodes/testing/fixtures.go | 14 ++++++ .../v1/nodes/testing/requests_test.go | 15 ++++++ openstack/baremetal/v1/nodes/urls.go | 10 +++- 5 files changed, 90 insertions(+), 1 deletion(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 23a3229389..50fe489557 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -438,3 +438,49 @@ func ChangeProvisionState(client *gophercloud.ServiceClient, id string, opts Pro }) return } + +type TargetPowerState string + +// TargetPowerState is used when changing the power state of a node. +const ( + PowerOn TargetPowerState = "power on" + PowerOff = "power off" + Rebooting = "rebooting" + SoftPowerOff = "soft power off" + SoftRebooting = "soft rebooting" +) + +// PowerStateOptsBuilder allows extensions to add additional parameters to the ChangePowerState request. +type PowerStateOptsBuilder interface { + ToPowerStateMap() (map[string]interface{}, error) +} + +// PowerStateOpts for a request to change a node's power state. +type PowerStateOpts struct { + Target TargetPowerState `json:"target" required:"true"` + Timeout int `json:"timeout,omitempty"` +} + +// ToPowerStateMap assembles a request body based on the contents of a PowerStateOpts. +func (opts PowerStateOpts) ToPowerStateMap() (map[string]interface{}, error) { + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return body, nil +} + +// Request to change a Node's power state. +func ChangePowerState(client *gophercloud.ServiceClient, id string, opts PowerStateOptsBuilder) (r ChangePowerStateResult) { + reqBody, err := opts.ToPowerStateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Put(powerStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 2aa6eecf67..8d61b8d3bd 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -278,6 +278,12 @@ type SupportedBootDeviceResult struct { gophercloud.Result } +// ChangePowerStateResult is the response from a ChangePowerState operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type ChangePowerStateResult struct { + gophercloud.ErrResult +} + // Each element in the response will contain a “result” variable, which will have a value of “true” or “false”, and // also potentially a reason. A value of nil indicates that the Node’s driver does not support that interface. type DriverValidation struct { diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 6d6b9f9e47..9197bd6027 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -909,3 +909,17 @@ func HandleNodeChangeProvisionStateClean(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +// HandleChangePowerStateSuccessfully sets up the test server to respond to a change power state request for a node +func HandleChangePowerStateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/power", 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, `{ + "target": "power on", + "timeout": 100 + }`) + + 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 a80ee55db5..51b1b3b83a 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -311,3 +311,18 @@ func TestCleanStepRequiresStep(t *testing.T) { t.Fatal("ErrMissingInput was expected to occur") } } + +func TestChangePowerState(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleChangePowerStateSuccessfully(t) + + opts := nodes.PowerStateOpts{ + Target: nodes.PowerOn, + Timeout: 100, + } + + c := client.ServiceClient() + err := nodes.ChangePowerState(c, "1234asdf", opts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index 3fffc110fc..8f9fe05e9c 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -42,6 +42,14 @@ func supportedBootDeviceURL(client *gophercloud.ServiceClient, id string) string return client.ServiceURL("nodes", id, "management", "boot_device", "supported") } +func statesResourceURL(client *gophercloud.ServiceClient, id string, state string) string { + return client.ServiceURL("nodes", id, "states", state) +} + +func powerStateURL(client *gophercloud.ServiceClient, id string) string { + return statesResourceURL(client, id, "power") +} + func provisionStateURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("nodes", id, "states", "provision") + return statesResourceURL(client, id, "provision") } From 0db1d64585aac4784c017d9b744992f1232342bf Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Fri, 1 Mar 2019 16:18:07 +0100 Subject: [PATCH 0684/2296] support context.Context for outgoing HTTP requests (#1480) --- provider_client.go | 7 +++++++ testing/provider_client_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/provider_client.go b/provider_client.go index 6ed659b52c..88ff64367d 100644 --- a/provider_client.go +++ b/provider_client.go @@ -2,6 +2,7 @@ package gophercloud import ( "bytes" + "context" "encoding/json" "errors" "io" @@ -76,6 +77,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 context.Context + // mut is a mutex for the client. It protects read and write access to client attributes such as getting // and setting the TokenID. mut *sync.RWMutex @@ -314,6 +318,9 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) if err != nil { return nil, err } + if client.Context != nil { + req = req.WithContext(client.Context) + } // Populate the request headers. Apply options.MoreHeaders last, to give the caller the chance to // modify or omit any header. diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 89291b88da..81ff66f997 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -1,9 +1,12 @@ package testing import ( + "context" "fmt" "io/ioutil" "net/http" + "net/http/httptest" + "strings" "sync" "testing" "time" @@ -310,3 +313,28 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { th.AssertEquals(t, 1, info.numreauths) th.AssertEquals(t, 1, info.failedAuths) } + +func TestRequestWithContext(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "OK") + })) + defer ts.Close() + + ctx, cancel := context.WithCancel(context.Background()) + p := &gophercloud.ProviderClient{Context: ctx} + + res, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{}) + th.AssertNoErr(t, err) + _, err = ioutil.ReadAll(res.Body) + res.Body.Close() + th.AssertNoErr(t, err) + + cancel() + res, err = p.Request("GET", ts.URL, &gophercloud.RequestOpts{}) + if err == nil { + t.Fatal("expecting error, got nil") + } + if !strings.Contains(err.Error(), ctx.Err().Error()) { + t.Fatalf("expecting error to contain: %q, got %q", ctx.Err().Error(), err.Error()) + } +} From fca40860790e5f7f88d5ba9e35829f1a5a15380a Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Sat, 2 Mar 2019 04:24:20 +1300 Subject: [PATCH 0685/2296] Octavia: Support to specify vip_port_id for creating load balancer (#1490) --- .../loadbalancer/v2/loadbalancers/requests.go | 8 +++++++- .../v2/loadbalancers/testing/fixtures.go | 1 + .../v2/loadbalancers/testing/requests_test.go | 20 +------------------ 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 05b0367f69..4ff4210fa2 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -81,10 +81,16 @@ type CreateOpts struct { // Human-readable description for the Loadbalancer. Description string `json:"description,omitempty"` + // Providing a neutron port ID for the vip_port_id tells Octavia to use this + // port for the VIP. If the port has more than one subnet you must specify + // either the vip_subnet_id or vip_address to clarify which address should + // be used for the VIP. + VipPortID string `json:"vip_port_id,omitempty"` + // The subnet on which to allocate the Loadbalancer's address. A project can // only create Loadbalancers on networks authorized by policy (e.g. networks // that belong to them or networks that are shared). - VipSubnetID string `json:"vip_subnet_id" required:"true"` + VipSubnetID string `json:"vip_subnet_id,omitempty"` // The network on which to allocate the Loadbalancer's address. A tenant can // only create Loadbalancers on networks authorized by policy (e.g. networks diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index 73299e1d2a..a9562e8ffa 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -258,6 +258,7 @@ func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { th.TestJSONRequest(t, r, `{ "loadbalancer": { "name": "db_lb", + "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "flavor": "medium", diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 78798eaf8d..1965bd9fa8 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -61,6 +61,7 @@ func TestCreateLoadbalancer(t *testing.T) { actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{ Name: "db_lb", AdminStateUp: gophercloud.Enabled, + VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", Flavor: "medium", @@ -72,25 +73,6 @@ func TestCreateLoadbalancer(t *testing.T) { th.CheckDeepEquals(t, LoadbalancerDb, *actual) } -func TestRequiredCreateOpts(t *testing.T) { - res := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } -} - func TestGetLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 8788235096d743c9abd213ba53adca6b0e9d0b58 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Fri, 1 Mar 2019 11:36:06 -0500 Subject: [PATCH 0686/2296] Baremetal API V1: Update can use any serializable value (#1494) --- openstack/baremetal/v1/nodes/requests.go | 15 +++++++++------ openstack/baremetal/v1/nodes/testing/fixtures.go | 2 +- .../baremetal/v1/nodes/testing/requests_test.go | 8 +++++--- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 50fe489557..07fe2222b3 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -280,9 +280,9 @@ const ( ) type UpdateOperation struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value string `json:"value,omitempty"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value interface{} `json:"value,omitempty"` } func (opts UpdateOperation) ToNodeUpdateMap() (map[string]interface{}, error) { @@ -307,9 +307,12 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up OkCodes: []int{200}, }) - r.Body = resp.Body - r.Header = resp.Header - r.Err = err + if err != nil { + r.Err = err + } else { + r.Body = resp.Body + r.Header = resp.Header + } return } diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 9197bd6027..45700c0b87 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -834,7 +834,7 @@ func HandleNodeUpdateSuccessfully(t *testing.T, response string) { 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, `[{"op": "replace", "path": "/driver", "value": "new-driver"}]`) + th.TestJSONRequest(t, r, `[{"op": "replace", "path": "/properties", "value": {"root_gb": 25}}]`) fmt.Fprintf(w, response) }) diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 51b1b3b83a..9bdb1cb89b 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -141,9 +141,11 @@ func TestUpdateNode(t *testing.T) { c := client.ServiceClient() actual, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ nodes.UpdateOperation{ - Op: nodes.ReplaceOp, - Path: "/driver", - Value: "new-driver", + Op: nodes.ReplaceOp, + Path: "/properties", + Value: map[string]interface{}{ + "root_gb": 25, + }, }, }).Extract() if err != nil { From 71f66c29139556381ce304ec5c4b25a5ce3b4d8b Mon Sep 17 00:00:00 2001 From: Leigh Jenkin Date: Sat, 2 Mar 2019 14:01:57 +1100 Subject: [PATCH 0687/2296] check for project id if project name specified (#1492) --- openstack/auth_env.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/openstack/auth_env.go b/openstack/auth_env.go index 0bb1f48375..0e8d90ff82 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -13,15 +13,19 @@ AuthOptionsFromEnv fills out an identity.AuthOptions structure with the settings found on the various OpenStack OS_* environment variables. The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, -OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. +OS_PASSWORD and OS_PROJECT_ID. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, -or an error will result. OS_TENANT_ID, OS_TENANT_NAME, OS_PROJECT_ID, and -OS_PROJECT_NAME are optional. +or an error will result. OS_PROJECT_ID, is optional. -OS_TENANT_ID and OS_TENANT_NAME are mutually exclusive to OS_PROJECT_ID and -OS_PROJECT_NAME. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will -still be referred as "tenant" in Gophercloud. +OS_TENANT_ID and OS_TENANT_NAME are deprecated forms of OS_PROJECT_ID and +OS_PROJECT_NAME and the latter are expected against a v3 auth api. + +If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will still be referred +as "tenant" in Gophercloud. + +If OS_PROJECT_NAME is set, it requires OS_PROJECT_ID to be set as well to +handle projects not on the default domain. To use this function, first set the OS_* environment variables (for example, by sourcing an `openrc` file), then: @@ -83,6 +87,13 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { return nilOptions, err } + if domainID == "" && domainName == "" && tenantID == "" && tenantName != "" { + err := gophercloud.ErrMissingEnvironmentVariable{ + EnvironmentVariable: "OS_PROJECT_ID", + } + return nilOptions, err + } + if applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" { if userID == "" && username == "" { return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ From a6b91de121ba18ec75e416818b019c5d094baf43 Mon Sep 17 00:00:00 2001 From: Himanshu Varshney Date: Mon, 4 Mar 2019 02:39:02 +0530 Subject: [PATCH 0688/2296] Identity V3: Add credentials package (#1457) (#1460) * Add credentials package. * Addressed comments. * Add credential acceptance test. --- .../openstack/identity/v3/credentials_test.go | 94 ++++++++ openstack/identity/v3/credentials/requests.go | 125 ++++++++++ openstack/identity/v3/credentials/results.go | 94 ++++++++ .../v3/credentials/testing/fixtures.go | 217 ++++++++++++++++++ .../v3/credentials/testing/requests_test.go | 99 ++++++++ openstack/identity/v3/credentials/urls.go | 23 ++ 6 files changed, 652 insertions(+) create mode 100644 acceptance/openstack/identity/v3/credentials_test.go create mode 100644 openstack/identity/v3/credentials/requests.go create mode 100644 openstack/identity/v3/credentials/results.go create mode 100644 openstack/identity/v3/credentials/testing/fixtures.go create mode 100644 openstack/identity/v3/credentials/testing/requests_test.go create mode 100644 openstack/identity/v3/credentials/urls.go diff --git a/acceptance/openstack/identity/v3/credentials_test.go b/acceptance/openstack/identity/v3/credentials_test.go new file mode 100644 index 0000000000..1910c60b34 --- /dev/null +++ b/acceptance/openstack/identity/v3/credentials_test.go @@ -0,0 +1,94 @@ +// +build acceptance + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCredentialsCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + authOptions := tokens.AuthOptions{ + Username: ao.Username, + Password: ao.Password, + DomainName: ao.DomainName, + DomainID: ao.DomainID, + // We need a scope to get the token roles list + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, + } + token, err := tokens.Create(client, &authOptions).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) + + user, err := tokens.Get(client, token.ID).ExtractUser() + th.AssertNoErr(t, err) + tools.PrintResource(t, user) + + project, err := tokens.Get(client, token.ID).ExtractProject() + th.AssertNoErr(t, err) + tools.PrintResource(t, project) + + createOpts := credentials.CreateOpts{ + ProjectID: project.ID, + Type: "ec2", + UserID: user.ID, + Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + } + + // Create a credential + credential, err := credentials.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + + // Delete a credential + defer credentials.Delete(client, credential.ID) + tools.PrintResource(t, credential) + + th.AssertEquals(t, credential.Blob, createOpts.Blob) + th.AssertEquals(t, credential.Type, createOpts.Type) + th.AssertEquals(t, credential.UserID, createOpts.UserID) + th.AssertEquals(t, credential.ProjectID, createOpts.ProjectID) + + // Get a credential + getCredential, err := credentials.Get(client, credential.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getCredential) + + th.AssertEquals(t, getCredential.Blob, createOpts.Blob) + th.AssertEquals(t, getCredential.Type, createOpts.Type) + th.AssertEquals(t, getCredential.UserID, createOpts.UserID) + th.AssertEquals(t, getCredential.ProjectID, createOpts.ProjectID) + + updateOpts := credentials.UpdateOpts{ + ProjectID: project.ID, + Type: "ec2", + UserID: user.ID, + Blob: "{\"access\":\"181920\",\"secret\":\"mySecret\"}", + } + + // Update a credential + updateCredential, err := credentials.Update(client, credential.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, updateCredential) + + th.AssertEquals(t, updateCredential.Blob, updateOpts.Blob) + +} diff --git a/openstack/identity/v3/credentials/requests.go b/openstack/identity/v3/credentials/requests.go new file mode 100644 index 0000000000..bf36a95521 --- /dev/null +++ b/openstack/identity/v3/credentials/requests.go @@ -0,0 +1,125 @@ +package credentials + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request +type ListOptsBuilder interface { + ToCredentialListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // UserID filters the response by a credential user_id + UserID string `q:"user_id"` + // Type filters the response by a credential type + Type string `q:"type"` +} + +// ToCredentialListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToCredentialListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List enumerates the Credentials to which the current token has access. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToCredentialListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return CredentialPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details on a single user, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToCredentialCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a credential. +type CreateOpts struct { + // Serialized blob containing the credentials + Blob string `json:"blob" required:"true"` + // ID of the project. + ProjectID string `json:"project_id,omitempty"` + // The type of the credential. + Type string `json:"type" required:"true"` + // ID of the user who owns the credential. + UserID string `json:"user_id" required:"true"` +} + +// ToCredentialCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToCredentialCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "credential") +} + +// Create creates a new Credential. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToCredentialCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// Delete deletes a credential. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToCredentialsUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents parameters to update a credential. +type UpdateOpts struct { + // Serialized blob containing the credentials. + Blob string `json:"blob,omitempty"` + // ID of the project. + ProjectID string `json:"project_id,omitempty"` + // The type of the credential. + Type string `json:"type,omitempty"` + // ID of the user who owns the credential. + UserID string `json:"user_id,omitempty"` +} + +// ToUpdateCreateMap formats a UpdateOpts into an update request. +func (opts UpdateOpts) ToCredentialsUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "credential") +} + +// Update modifies the attributes of a Credential. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToCredentialsUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/identity/v3/credentials/results.go b/openstack/identity/v3/credentials/results.go new file mode 100644 index 0000000000..fe4b413e44 --- /dev/null +++ b/openstack/identity/v3/credentials/results.go @@ -0,0 +1,94 @@ +package credentials + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Credential represents the Credential object +type Credential struct { + // The ID of the credential. + ID string `json:"id"` + // Serialized Blob Credential. + Blob string `json:"blob"` + // ID of the user who owns the credential. + UserID string `json:"user_id"` + // The type of the credential. + Type string `json:"type"` + // The ID of the project the credential was created for. + ProjectID string `json:"project_id"` + // Links contains referencing links to the credential. + Links map[string]interface{} `json:"links"` +} + +type credentialResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Credential. +type GetResult struct { + credentialResult +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Credential. +type CreateResult struct { + credentialResult +} + +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// UpdateResult is the result of an Update request. Call its Extract method to +// interpret it as a Credential +type UpdateResult struct { + credentialResult +} + +// a CredentialPage is a single page of a Credential results. +type CredentialPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a CredentialPage contains any results. +func (r CredentialPage) IsEmpty() (bool, error) { + credentials, err := ExtractCredentials(r) + return len(credentials) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r CredentialPage) 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 +} + +// Extract a Credential returns a slice of Credentials contained in a single page of results. +func ExtractCredentials(r pagination.Page) ([]Credential, error) { + var s struct { + Credentials []Credential `json:"credentials"` + } + err := (r.(CredentialPage)).ExtractInto(&s) + return s.Credentials, err +} + +// Extract interprets any credential results as a Credential. +func (r credentialResult) Extract() (*Credential, error) { + var s struct { + Credential *Credential `json:"credential"` + } + err := r.ExtractInto(&s) + return s.Credential, err +} diff --git a/openstack/identity/v3/credentials/testing/fixtures.go b/openstack/identity/v3/credentials/testing/fixtures.go new file mode 100644 index 0000000000..b765538c0e --- /dev/null +++ b/openstack/identity/v3/credentials/testing/fixtures.go @@ -0,0 +1,217 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const userID = "bb5476fd12884539b41d5a88f838d773" +const credentialID = "3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" +const projectID = "731fc6f265cd486d900f16e84c5cb594" + +// ListOutput provides a single page of Credential results. +const ListOutput = ` +{ + "credentials": [ + { + "user_id": "bb5476fd12884539b41d5a88f838d773", + "links": { + "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" + }, + "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + "project_id": "731fc6f265cd486d900f16e84c5cb594", + "type": "ec2", + "id": "3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" + }, + { + "user_id": "6f556708d04b4ea6bc72d7df2296b71a", + "links": { + "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609" + }, + "blob": "{\"access\":\"7da79ff0aa364e1396f067e352b9b79a\",\"secret\":\"7a18d68ba8834b799d396f3ff6f1e98c\"}", + "project_id": "1a1d14690f3c4ec5bf5f321c5fde3c16", + "type": "ec2", + "id": "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609" + } + ], + "links": { + "self": "http://identity/v3/credentials", + "previous": null, + "next": null + } +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "credential": { + "user_id": "bb5476fd12884539b41d5a88f838d773", + "links": { + "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" + }, + "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + "project_id": "731fc6f265cd486d900f16e84c5cb594", + "type": "ec2", + "id": "3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510" + } +} +` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "credential": { + "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + "project_id": "731fc6f265cd486d900f16e84c5cb594", + "type": "ec2", + "user_id": "bb5476fd12884539b41d5a88f838d773" + } +} +` + +// UpdateRequest provides the input to as Update request. +const UpdateRequest = ` +{ + "credential": { + "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + "project_id": "731fc6f265cd486d900f16e84c5cb594", + "type": "ec2", + "user_id": "bb5476fd12884539b41d5a88f838d773" + } +} +` + +// UpdateOutput provides an update result. +const UpdateOutput = ` +{ + "credential": { + "user_id": "bb5476fd12884539b41d5a88f838d773", + "links": { + "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609" + }, + "blob": "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + "project_id": "731fc6f265cd486d900f16e84c5cb594", + "type": "ec2", + "id": "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609" + } +} +` + +var Credential = credentials.Credential{ + ID: credentialID, + ProjectID: projectID, + Type: "ec2", + UserID: userID, + Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + Links: map[string]interface{}{ + "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", + }, +} + +var FirstCredential = credentials.Credential{ + ID: credentialID, + ProjectID: projectID, + Type: "ec2", + UserID: userID, + Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + Links: map[string]interface{}{ + "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", + }, +} + +var SecondCredential = credentials.Credential{ + ID: "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", + ProjectID: "1a1d14690f3c4ec5bf5f321c5fde3c16", + Type: "ec2", + UserID: "6f556708d04b4ea6bc72d7df2296b71a", + Blob: "{\"access\":\"7da79ff0aa364e1396f067e352b9b79a\",\"secret\":\"7a18d68ba8834b799d396f3ff6f1e98c\"}", + Links: map[string]interface{}{ + "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", + }, +} + +// SecondCredentialUpdated is how SecondCredential should look after an Update. +var SecondCredentialUpdated = credentials.Credential{ + ID: "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", + ProjectID: projectID, + Type: "ec2", + UserID: userID, + Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + Links: map[string]interface{}{ + "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", + }, +} + +// ExpectedCredentialsSlice is the slice of credentials expected to be returned from ListOutput. +var ExpectedCredentialsSlice = []credentials.Credential{FirstCredential, SecondCredential} + +// HandleListCredentialsSuccessfully creates an HTTP handler at `/credentials` on the +// test handler mux that responds with a list of two credentials. +func HandleListCredentialsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/credentials", 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) + }) +} + +// HandleGetCredentialSuccessfully creates an HTTP handler at `/credentials` on the +// test handler mux that responds with a single credential. +func HandleGetCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", 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) + }) +} + +// HandleCreateCredentialSuccessfully creates an HTTP handler at `/credentials` on the +// test handler mux that tests credential creation. +func HandleCreateCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/credentials", 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, GetOutput) + }) +} + +// HandleDeleteCredentialSuccessfully creates an HTTP handler at `/credentials` on the +// test handler mux that tests credential deletion. +func HandleDeleteCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", 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) + }) +} + +// HandleUpdateCredentialsSuccessfully creates an HTTP handler at `/credentials` on the +// test handler mux that tests credentials update. +func HandleUpdateCredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", 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/credentials/testing/requests_test.go b/openstack/identity/v3/credentials/testing/requests_test.go new file mode 100644 index 0000000000..f901db5e8a --- /dev/null +++ b/openstack/identity/v3/credentials/testing/requests_test.go @@ -0,0 +1,99 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListCredentials(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListCredentialsSuccessfully(t) + + count := 0 + err := credentials.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := credentials.ExtractCredentials(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedCredentialsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListCredentialsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListCredentialsSuccessfully(t) + + allPages, err := credentials.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := credentials.ExtractCredentials(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedCredentialsSlice, actual) + th.AssertDeepEquals(t, ExpectedCredentialsSlice[0].Blob, "{\"access\":\"181920\",\"secret\":\"secretKey\"}") + th.AssertDeepEquals(t, ExpectedCredentialsSlice[1].Blob, "{\"access\":\"7da79ff0aa364e1396f067e352b9b79a\",\"secret\":\"7a18d68ba8834b799d396f3ff6f1e98c\"}") +} + +func TestGetCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetCredentialSuccessfully(t) + + actual, err := credentials.Get(client.ServiceClient(), credentialID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, Credential, *actual) +} + +func TestCreateCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateCredentialSuccessfully(t) + + createOpts := credentials.CreateOpts{ + ProjectID: projectID, + Type: "ec2", + UserID: userID, + Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + } + + CredentialResponse := Credential + + actual, err := credentials.Create(client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, CredentialResponse, *actual) +} + +func TestDeleteCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteCredentialSuccessfully(t) + + res := credentials.Delete(client.ServiceClient(), credentialID) + th.AssertNoErr(t, res.Err) +} + +func TestUpdateCredential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateCredentialSuccessfully(t) + + updateOpts := credentials.UpdateOpts{ + ProjectID: "731fc6f265cd486d900f16e84c5cb594", + Type: "ec2", + UserID: "bb5476fd12884539b41d5a88f838d773", + Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + } + + actual, err := credentials.Update(client.ServiceClient(), "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondCredentialUpdated, *actual) +} diff --git a/openstack/identity/v3/credentials/urls.go b/openstack/identity/v3/credentials/urls.go new file mode 100644 index 0000000000..5ec51d1e34 --- /dev/null +++ b/openstack/identity/v3/credentials/urls.go @@ -0,0 +1,23 @@ +package credentials + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("credentials") +} + +func getURL(client *gophercloud.ServiceClient, credentialID string) string { + return client.ServiceURL("credentials", credentialID) +} + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("credentials") +} + +func deleteURL(client *gophercloud.ServiceClient, credentialID string) string { + return client.ServiceURL("credentials", credentialID) +} + +func updateURL(client *gophercloud.ServiceClient, credentialID string) string { + return client.ServiceURL("credentials", credentialID) +} From f83aee3da90f3f337d024070e2e68b04fb035149 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 3 Mar 2019 15:44:50 -0700 Subject: [PATCH 0689/2296] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0955f170ea..9153a00fc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ install: go: - "1.10" - "1.11" +- "1.12" - "tip" env: global: From 22dfca83d56c1450608147f865b955312d484935 Mon Sep 17 00:00:00 2001 From: Riccardo Pittau Date: Wed, 6 Mar 2019 21:01:22 +0100 Subject: [PATCH 0690/2296] Baremetal V1 API Drivers (#1461) This patch adds the Drivers API calls according to Baremetal API reference [1] List Drivers (GET) Show driver details (GET) Show driver properties (GET) Show driver logical disk properties (GET) [1] https://developer.openstack.org/api-ref/baremetal/#drivers-drivers --- openstack/baremetal/v1/drivers/doc.go | 41 ++ openstack/baremetal/v1/drivers/requests.go | 70 +++ openstack/baremetal/v1/drivers/results.go | 198 +++++++++ openstack/baremetal/v1/drivers/testing/doc.go | 2 + .../baremetal/v1/drivers/testing/fixtures.go | 407 ++++++++++++++++++ .../v1/drivers/testing/requests_test.go | 84 ++++ openstack/baremetal/v1/drivers/urls.go | 19 + 7 files changed, 821 insertions(+) create mode 100644 openstack/baremetal/v1/drivers/doc.go create mode 100644 openstack/baremetal/v1/drivers/requests.go create mode 100644 openstack/baremetal/v1/drivers/results.go create mode 100644 openstack/baremetal/v1/drivers/testing/doc.go create mode 100644 openstack/baremetal/v1/drivers/testing/fixtures.go create mode 100644 openstack/baremetal/v1/drivers/testing/requests_test.go create mode 100644 openstack/baremetal/v1/drivers/urls.go diff --git a/openstack/baremetal/v1/drivers/doc.go b/openstack/baremetal/v1/drivers/doc.go new file mode 100644 index 0000000000..5eb8346852 --- /dev/null +++ b/openstack/baremetal/v1/drivers/doc.go @@ -0,0 +1,41 @@ +package drivers + +/* +Package drivers contains the functionality for Listing drivers, driver details, +driver properties and driver logical disk properties + +API reference: https://developer.openstack.org/api-ref/baremetal/#drivers-drivers + + // Example to List Drivers + drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) { + driversList, err := drivers.ExtractDrivers(page) + if err != nil { + return false, err + } + + for _, n := range driversList { + // Do something + } + + return true, nil + }) + + // Example to Get single Driver Details + showDriverDetails, err := drivers.GetDriverDetails(client, "ipmi").Extract() + if err != nil { + panic(err) + } + + // Example to Get single Driver Properties + showDriverProperties, err := drivers.GetDriverProperties(client, "ipmi").Extract() + if err != nil { + panic(err) + } + + // Example to Get single Driver Logical Disk Properties + showDriverDiskProperties, err := drivers.GetDriverDiskProperties(client, "ipmi").Extract() + if err != nil { + panic(err) + } + +*/ diff --git a/openstack/baremetal/v1/drivers/requests.go b/openstack/baremetal/v1/drivers/requests.go new file mode 100644 index 0000000000..ab962c8ef7 --- /dev/null +++ b/openstack/baremetal/v1/drivers/requests.go @@ -0,0 +1,70 @@ +package drivers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListDriversOptsBuilder allows extensions to add additional parameters to the +// ListDrivers request. +type ListDriversOptsBuilder interface { + ToListDriversOptsQuery() (string, error) +} + +// ListDriversOpts defines query options that can be passed to ListDrivers +type ListDriversOpts struct { + // Provide detailed information about the drivers + Detail bool `q:"detail"` + + // Filter the list by the type of the driver + Type string `q:"type"` +} + +// ToListDriversOptsQuery formats a ListOpts into a query string +func (opts ListDriversOpts) ToListDriversOptsQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDrivers makes a request against the API to list all drivers +func ListDrivers(client *gophercloud.ServiceClient, opts ListDriversOptsBuilder) pagination.Pager { + url := driversURL(client) + if opts != nil { + query, err := opts.ToListDriversOptsQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return DriverPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetDriverDetails Shows details for a driver +func GetDriverDetails(client *gophercloud.ServiceClient, driverName string) (r GetDriverResult) { + _, r.Err = client.Get(driverDetailsURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// GetDriverProperties Shows the required and optional parameters that +// driverName expects to be supplied in the driver_info field for every +// Node it manages +func GetDriverProperties(client *gophercloud.ServiceClient, driverName string) (r GetPropertiesResult) { + _, r.Err = client.Get(driverPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// GetDriverDiskProperties Show the required and optional parameters that +// driverName expects to be supplied in the node’s raid_config field, if a +// RAID configuration change is requested. +func GetDriverDiskProperties(client *gophercloud.ServiceClient, driverName string) (r GetDiskPropertiesResult) { + _, r.Err = client.Get(driverDiskPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/baremetal/v1/drivers/results.go b/openstack/baremetal/v1/drivers/results.go new file mode 100644 index 0000000000..424079c8ea --- /dev/null +++ b/openstack/baremetal/v1/drivers/results.go @@ -0,0 +1,198 @@ +package drivers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type driverResult struct { + gophercloud.Result +} + +// Extract interprets any driverResult as a Driver, if possible. +func (r driverResult) Extract() (*Driver, error) { + var s Driver + err := r.ExtractInto(&s) + return &s, err +} + +func (r driverResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "") +} + +func ExtractDriversInto(r pagination.Page, v interface{}) error { + return r.(DriverPage).Result.ExtractIntoSlicePtr(v, "drivers") +} + +// Driver represents a driver in the OpenStack Bare Metal API. +type Driver struct { + // Name and Identifier of the driver + Name string `json:"name"` + + // A list of active hosts that support this driver + Hosts []string `json:"hosts"` + + // Type of this driver (“classic” or “dynamic”) + Type string `json:"type"` + + // The default bios interface used for a node with a dynamic driver, + // if no bios interface is specified for the node. + DefaultBiosInterface string `json:"default_bios_interface"` + + // The default boot interface used for a node with a dynamic driver, + // if no boot interface is specified for the node. + DefaultBootInterface string `json:"default_boot_interface"` + + // The default console interface used for a node with a dynamic driver, + // if no console interface is specified for the node. + DefaultConsoleInterface string `json:"default_console_interface"` + + // The default deploy interface used for a node with a dynamic driver, + // if no deploy interface is specified for the node. + DefaultDeployInterface string `json:"default_deploy_interface"` + + // The default inspection interface used for a node with a dynamic driver, + // if no inspection interface is specified for the node. + DefaultInspectInterface string `json:"default_inspect_interface"` + + // The default management interface used for a node with a dynamic driver, + // if no management interface is specified for the node. + DefaultManagementInterface string `json:"default_management_interface"` + + // The default network interface used for a node with a dynamic driver, + // if no network interface is specified for the node. + DefaultNetworkInterface string `json:"default_network_interface"` + + // The default power interface used for a node with a dynamic driver, + // if no power interface is specified for the node. + DefaultPowerInterface string `json:"default_power_interface"` + + // The default RAID interface used for a node with a dynamic driver, + // if no RAID interface is specified for the node. + DefaultRaidInterface string `json:"default_raid_interface"` + + // The default rescue interface used for a node with a dynamic driver, + // if no rescue interface is specified for the node. + DefaultRescueInterface string `json:"default_rescue_interface"` + + // The default storage interface used for a node with a dynamic driver, + // if no storage interface is specified for the node. + DefaultStorageInterface string `json:"default_storage_interface"` + + // The default vendor interface used for a node with a dynamic driver, + // if no vendor interface is specified for the node. + DefaultVendorInterface string `json:"default_vendor_interface"` + + // The enabled bios interfaces for this driver. + EnabledBiosInterfaces []string `json:"enabled_bios_interfaces"` + + // The enabled boot interfaces for this driver. + EnabledBootInterfaces []string `json:"enabled_boot_interfaces"` + + // The enabled console interfaces for this driver. + EnabledConsoleInterface []string `json:"enabled_console_interfaces"` + + // The enabled deploy interfaces for this driver. + EnabledDeployInterfaces []string `json:"enabled_deploy_interfaces"` + + // The enabled inspection interfaces for this driver. + EnabledInspectInterfaces []string `json:"enabled_inspect_interfaces"` + + // The enabled management interfaces for this driver. + EnabledManagementInterfaces []string `json:"enabled_management_interfaces"` + + // The enabled network interfaces for this driver. + EnabledNetworkInterfaces []string `json:"enabled_network_interfaces"` + + // The enabled power interfaces for this driver. + EnabledPowerInterfaces []string `json:"enabled_power_interfaces"` + + // The enabled rescue interfaces for this driver. + EnabledRescueInterfaces []string `json:"enabled_rescue_interfaces"` + + // The enabled RAID interfaces for this driver. + EnabledRaidInterfaces []string `json:"enabled_raid_interfaces"` + + // The enabled storage interfaces for this driver. + EnabledStorageInterfaces []string `json:"enabled_storage_interfaces"` + + // The enabled vendor interfaces for this driver. + EnabledVendorInterfaces []string `json:"enabled_vendor_interfaces"` + + //A list of relative links. Includes the self and bookmark links. + Links []interface{} `json:"links"` + + // A list of links to driver properties. + Properties []interface{} `json:"properties"` +} + +// DriverPage abstracts the raw results of making a ListDrivers() request +// against the API. +type DriverPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a page contains no Driver results. +func (r DriverPage) IsEmpty() (bool, error) { + s, err := ExtractDrivers(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r DriverPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"drivers_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractDrivers interprets the results of a single page from ListDrivers() +// call, producing a slice of Driver entities. +func ExtractDrivers(r pagination.Page) ([]Driver, error) { + var s []Driver + err := ExtractDriversInto(r, &s) + return s, err +} + +// GetDriverResult is the response from a Get operation. +// Call its Extract method to interpret it as a Driver. +type GetDriverResult struct { + driverResult +} + +// DriverProperties represents driver properties in the OpenStack Bare Metal API. +type DriverProperties map[string]interface{} + +// Extract interprets any GetPropertiesResult as DriverProperties, if possible. +func (r GetPropertiesResult) Extract() (*DriverProperties, error) { + var s DriverProperties + err := r.ExtractInto(&s) + return &s, err +} + +// GetPropertiesResult is the response from a GetDriverProperties operation. +// Call its Extract method to interpret it as DriverProperties. +type GetPropertiesResult struct { + gophercloud.Result +} + +// DiskProperties represents driver disk properties in the OpenStack Bare Metal API. +type DiskProperties map[string]interface{} + +// Extract interprets any GetDiskPropertiesResult as DiskProperties, if possible. +func (r GetDiskPropertiesResult) Extract() (*DiskProperties, error) { + var s DiskProperties + err := r.ExtractInto(&s) + return &s, err +} + +// GetDiskPropertiesResult is the response from a GetDriverDiskProperties operation. +// Call its Extract method to interpret it as DiskProperties. +type GetDiskPropertiesResult struct { + gophercloud.Result +} diff --git a/openstack/baremetal/v1/drivers/testing/doc.go b/openstack/baremetal/v1/drivers/testing/doc.go new file mode 100644 index 0000000000..266af92b04 --- /dev/null +++ b/openstack/baremetal/v1/drivers/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing contains drivers unit tests +package testing diff --git a/openstack/baremetal/v1/drivers/testing/fixtures.go b/openstack/baremetal/v1/drivers/testing/fixtures.go new file mode 100644 index 0000000000..a785b905c1 --- /dev/null +++ b/openstack/baremetal/v1/drivers/testing/fixtures.go @@ -0,0 +1,407 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/drivers" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListDriversBody contains the canned body of a drivers.ListDrivers response, without details. +const ListDriversBody = ` +{ + "drivers": [ + { + "hosts": [ + "897ab1dad809" + ], + "links": [ + { + "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/drivers/agent_ipmitool", + "rel": "bookmark" + } + ], + "name": "agent_ipmitool", + "properties": [ + { + "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool/properties", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/drivers/agent_ipmitool/properties", + "rel": "bookmark" + } + ], + "type": "classic" + }, + { + "hosts": [ + "897ab1dad809" + ], + "links": [ + { + "href": "http://127.0.0.1:6385/v1/drivers/fake", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/drivers/fake", + "rel": "bookmark" + } + ], + "name": "fake", + "properties": [ + { + "href": "http://127.0.0.1:6385/v1/drivers/fake/properties", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/drivers/fake/properties", + "rel": "bookmark" + } + ], + "type": "classic" + }, + { + "hosts": [ + "897ab1dad809" + ], + "links": [ + { + "href": "http://127.0.0.1:6385/v1/drivers/ipmi", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/drivers/ipmi", + "rel": "bookmark" + } + ], + "name": "ipmi", + "properties": [ + { + "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/drivers/ipmi/properties", + "rel": "bookmark" + } + ], + "type": "dynamic" + } + ] +} +` +const SingleDriverDetails = ` +{ + "default_bios_interface": "no-bios", + "default_boot_interface": "pxe", + "default_console_interface": "no-console", + "default_deploy_interface": "iscsi", + "default_inspect_interface": "no-inspect", + "default_management_interface": "ipmitool", + "default_network_interface": "flat", + "default_power_interface": "ipmitool", + "default_raid_interface": "no-raid", + "default_rescue_interface": "no-rescue", + "default_storage_interface": "noop", + "default_vendor_interface": "no-vendor", + "enabled_bios_interfaces": [ + "no-bios" + ], + "enabled_boot_interfaces": [ + "pxe" + ], + "enabled_console_interfaces": [ + "no-console" + ], + "enabled_deploy_interfaces": [ + "iscsi", + "direct" + ], + "enabled_inspect_interfaces": [ + "no-inspect" + ], + "enabled_management_interfaces": [ + "ipmitool" + ], + "enabled_network_interfaces": [ + "flat", + "noop" + ], + "enabled_power_interfaces": [ + "ipmitool" + ], + "enabled_raid_interfaces": [ + "no-raid", + "agent" + ], + "enabled_rescue_interfaces": [ + "no-rescue" + ], + "enabled_storage_interfaces": [ + "noop" + ], + "enabled_vendor_interfaces": [ + "no-vendor" + ], + "hosts": [ + "897ab1dad809" + ], + "links": [ + { + "href": "http://127.0.0.1:6385/v1/drivers/ipmi", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/drivers/ipmi", + "rel": "bookmark" + } + ], + "name": "ipmi", + "properties": [ + { + "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/drivers/ipmi/properties", + "rel": "bookmark" + } + ], + "type": "dynamic" +} +` + +const SingleDriverProperties = ` +{ + "deploy_forces_oob_reboot": "Whether Ironic should force a reboot of the Node via the out-of-band channel after deployment is complete. Provides compatibility with older deploy ramdisks. Defaults to False. Optional.", + "deploy_kernel": "UUID (from Glance) of the deployment kernel. Required.", + "deploy_ramdisk": "UUID (from Glance) of the ramdisk that is mounted at boot time. Required.", + "image_http_proxy": "URL of a proxy server for HTTP connections. Optional.", + "image_https_proxy": "URL of a proxy server for HTTPS connections. Optional.", + "image_no_proxy": "A comma-separated list of host names, IP addresses and domain names (with optional :port) that will be excluded from proxying. To denote a domain name, use a dot to prefix the domain name. This value will be ignored if ` + "``image_http_proxy`` and ``image_https_proxy``" + ` are not specified. Optional.", + "ipmi_address": "IP address or hostname of the node. Required.", + "ipmi_bridging": "bridging_type; default is \"no\". One of \"single\", \"dual\", \"no\". Optional.", + "ipmi_disable_boot_timeout": "By default ironic will send a raw IPMI command to disable the 60 second timeout for booting. Setting this option to False will NOT send that command; default value is True. Optional.", + "ipmi_force_boot_device": "Whether Ironic should specify the boot device to the BMC each time the server is turned on, eg. because the BMC is not capable of remembering the selected boot device across power cycles; default value is False. Optional.", + "ipmi_local_address": "local IPMB address for bridged requests. Used only if ipmi_bridging is set to \"single\" or \"dual\". Optional.", + "ipmi_password": "password. Optional.", + "ipmi_port": "remote IPMI RMCP port. Optional.", + "ipmi_priv_level": "privilege level; default is ADMINISTRATOR. One of ADMINISTRATOR, CALLBACK, OPERATOR, USER. Optional.", + "ipmi_protocol_version": "the version of the IPMI protocol; default is \"2.0\". One of \"1.5\", \"2.0\". Optional.", + "ipmi_target_address": "destination address for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".", + "ipmi_target_channel": "destination channel for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".", + "ipmi_terminal_port": "node's UDP port to connect to. Only required for console access.", + "ipmi_transit_address": "transit address for bridged request. Required only if ipmi_bridging is set to \"dual\".", + "ipmi_transit_channel": "transit channel for bridged request. Required only if ipmi_bridging is set to \"dual\".", + "ipmi_username": "username; default is NULL user. Optional." +} +` + +const SingleDriverDiskProperties = ` +{ + "controller": "Controller to use for this logical disk. If not specified, the driver will choose a suitable RAID controller on the bare metal node. Optional.", + "disk_type": "The type of disk preferred. Valid values are 'hdd' and 'ssd'. If this is not specified, disk type will not be a selection criterion for choosing backing physical disks. Optional.", + "interface_type": "The interface type of disk. Valid values are 'sata', 'scsi' and 'sas'. If this is not specified, interface type will not be a selection criterion for choosing backing physical disks. Optional.", + "is_root_volume": "Specifies whether this disk is a root volume. By default, this is False. Optional.", + "number_of_physical_disks": "Number of physical disks to use for this logical disk. By default, the driver uses the minimum number of disks required for that RAID level. Optional.", + "physical_disks": "The physical disks to use for this logical disk. If not specified, the driver will choose suitable physical disks to use. Optional.", + "raid_level": "RAID level for the logical disk. Valid values are 'JBOD', '0', '1', '2', '5', '6', '1+0', '5+0' and '6+0'. Required.", + "share_physical_disks": "Specifies whether other logical disks can share physical disks with this logical disk. By default, this is False. Optional.", + "size_gb": "Size in GiB (Integer) for the logical disk. Use 'MAX' as size_gb if this logical disk is supposed to use the rest of the space available. Required.", + "volume_name": "Name of the volume to be created. If this is not specified, it will be auto-generated. Optional." +} +` + +var ( + DriverAgentIpmitool = drivers.Driver{ + Name: "agent_ipmitool", + Type: "classic", + Hosts: []string{"897ab1dad809"}, + Links: []interface{}{ + map[string]interface{}{ + "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://127.0.0.1:6385/drivers/agent_ipmitool", + "rel": "bookmark", + }, + }, + Properties: []interface{}{ + map[string]interface{}{ + "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool/properties", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://127.0.0.1:6385/drivers/agent_ipmitool/properties", + "rel": "bookmark", + }, + }, + } + + DriverFake = drivers.Driver{ + Name: "fake", + Type: "classic", + Hosts: []string{"897ab1dad809"}, + Links: []interface{}{ + map[string]interface{}{ + "href": "http://127.0.0.1:6385/v1/drivers/fake", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://127.0.0.1:6385/drivers/fake", + "rel": "bookmark", + }, + }, + Properties: []interface{}{ + map[string]interface{}{ + "href": "http://127.0.0.1:6385/v1/drivers/fake/properties", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://127.0.0.1:6385/drivers/fake/properties", + "rel": "bookmark", + }, + }, + } + + DriverIpmi = drivers.Driver{ + Name: "ipmi", + Type: "dynamic", + Hosts: []string{"897ab1dad809"}, + DefaultBiosInterface: "no-bios", + DefaultBootInterface: "pxe", + DefaultConsoleInterface: "no-console", + DefaultDeployInterface: "iscsi", + DefaultInspectInterface: "no-inspect", + DefaultManagementInterface: "ipmitool", + DefaultNetworkInterface: "flat", + DefaultPowerInterface: "ipmitool", + DefaultRaidInterface: "no-raid", + DefaultRescueInterface: "no-rescue", + DefaultStorageInterface: "noop", + DefaultVendorInterface: "no-vendor", + EnabledBiosInterfaces: []string{"no-bios"}, + EnabledBootInterfaces: []string{"pxe"}, + EnabledConsoleInterface: []string{"no-console"}, + EnabledDeployInterfaces: []string{"iscsi", "direct"}, + EnabledInspectInterfaces: []string{"no-inspect"}, + EnabledManagementInterfaces: []string{"ipmitool"}, + EnabledNetworkInterfaces: []string{"flat", "noop"}, + EnabledPowerInterfaces: []string{"ipmitool"}, + EnabledRescueInterfaces: []string{"no-rescue"}, + EnabledRaidInterfaces: []string{"no-raid", "agent"}, + EnabledStorageInterfaces: []string{"noop"}, + EnabledVendorInterfaces: []string{"no-vendor"}, + Links: []interface{}{ + map[string]interface{}{ + "href": "http://127.0.0.1:6385/v1/drivers/ipmi", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://127.0.0.1:6385/drivers/ipmi", + "rel": "bookmark", + }, + }, + Properties: []interface{}{ + map[string]interface{}{ + "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties", + "rel": "self", + }, + map[string]interface{}{ + "href": "http://127.0.0.1:6385/drivers/ipmi/properties", + "rel": "bookmark", + }, + }, + } + + DriverIpmiToolProperties = drivers.DriverProperties{ + "deploy_forces_oob_reboot": "Whether Ironic should force a reboot of the Node via the out-of-band channel after deployment is complete. Provides compatibility with older deploy ramdisks. Defaults to False. Optional.", + "deploy_kernel": "UUID (from Glance) of the deployment kernel. Required.", + "deploy_ramdisk": "UUID (from Glance) of the ramdisk that is mounted at boot time. Required.", + "image_http_proxy": "URL of a proxy server for HTTP connections. Optional.", + "image_https_proxy": "URL of a proxy server for HTTPS connections. Optional.", + "image_no_proxy": "A comma-separated list of host names, IP addresses and domain names (with optional :port) that will be excluded from proxying. To denote a domain name, use a dot to prefix the domain name. This value will be ignored if ``image_http_proxy`` and ``image_https_proxy`` are not specified. Optional.", + "ipmi_address": "IP address or hostname of the node. Required.", + "ipmi_bridging": "bridging_type; default is \"no\". One of \"single\", \"dual\", \"no\". Optional.", + "ipmi_disable_boot_timeout": "By default ironic will send a raw IPMI command to disable the 60 second timeout for booting. Setting this option to False will NOT send that command; default value is True. Optional.", + "ipmi_force_boot_device": "Whether Ironic should specify the boot device to the BMC each time the server is turned on, eg. because the BMC is not capable of remembering the selected boot device across power cycles; default value is False. Optional.", + "ipmi_local_address": "local IPMB address for bridged requests. Used only if ipmi_bridging is set to \"single\" or \"dual\". Optional.", + "ipmi_password": "password. Optional.", + "ipmi_port": "remote IPMI RMCP port. Optional.", + "ipmi_priv_level": "privilege level; default is ADMINISTRATOR. One of ADMINISTRATOR, CALLBACK, OPERATOR, USER. Optional.", + "ipmi_protocol_version": "the version of the IPMI protocol; default is \"2.0\". One of \"1.5\", \"2.0\". Optional.", + "ipmi_target_address": "destination address for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".", + "ipmi_target_channel": "destination channel for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".", + "ipmi_terminal_port": "node's UDP port to connect to. Only required for console access.", + "ipmi_transit_address": "transit address for bridged request. Required only if ipmi_bridging is set to \"dual\".", + "ipmi_transit_channel": "transit channel for bridged request. Required only if ipmi_bridging is set to \"dual\".", + "ipmi_username": "username; default is NULL user. Optional.", + } + + DriverIpmiToolDisk = drivers.DiskProperties{ + "controller": "Controller to use for this logical disk. If not specified, the driver will choose a suitable RAID controller on the bare metal node. Optional.", + "disk_type": "The type of disk preferred. Valid values are 'hdd' and 'ssd'. If this is not specified, disk type will not be a selection criterion for choosing backing physical disks. Optional.", + "interface_type": "The interface type of disk. Valid values are 'sata', 'scsi' and 'sas'. If this is not specified, interface type will not be a selection criterion for choosing backing physical disks. Optional.", + "is_root_volume": "Specifies whether this disk is a root volume. By default, this is False. Optional.", + "number_of_physical_disks": "Number of physical disks to use for this logical disk. By default, the driver uses the minimum number of disks required for that RAID level. Optional.", + "physical_disks": "The physical disks to use for this logical disk. If not specified, the driver will choose suitable physical disks to use. Optional.", + "raid_level": "RAID level for the logical disk. Valid values are 'JBOD', '0', '1', '2', '5', '6', '1+0', '5+0' and '6+0'. Required.", + "share_physical_disks": "Specifies whether other logical disks can share physical disks with this logical disk. By default, this is False. Optional.", + "size_gb": "Size in GiB (Integer) for the logical disk. Use 'MAX' as size_gb if this logical disk is supposed to use the rest of the space available. Required.", + "volume_name": "Name of the volume to be created. If this is not specified, it will be auto-generated. Optional.", + } +) + +// HandleListDriversSuccessfully sets up the test server to respond to a drivers ListDrivers request. +func HandleListDriversSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/drivers", 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, ListDriversBody) + }) +} + +// HandleGetDriverDetailsSuccessfully sets up the test server to respond to a drivers GetDriverDetails request. +func HandleGetDriverDetailsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/drivers/ipmi", 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, SingleDriverDetails) + }) +} + +// HandleGetDriverPropertiesSuccessfully sets up the test server to respond to a drivers GetDriverProperties request. +func HandleGetDriverPropertiesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/drivers/agent_ipmitool/properties", 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, SingleDriverProperties) + }) +} + +// HandleGetDriverDiskPropertiesSuccessfully sets up the test server to respond to a drivers GetDriverDiskProperties request. +func HandleGetDriverDiskPropertiesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/drivers/agent_ipmitool/raid/logical_disk_properties", 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, SingleDriverDiskProperties) + }) +} diff --git a/openstack/baremetal/v1/drivers/testing/requests_test.go b/openstack/baremetal/v1/drivers/testing/requests_test.go new file mode 100644 index 0000000000..28e5365183 --- /dev/null +++ b/openstack/baremetal/v1/drivers/testing/requests_test.go @@ -0,0 +1,84 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/drivers" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListDrivers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListDriversSuccessfully(t) + + pages := 0 + err := drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := drivers.ExtractDrivers(page) + if err != nil { + return false, err + } + + if len(actual) != 3 { + t.Fatalf("Expected 3 drivers, got %d", len(actual)) + } + + th.CheckDeepEquals(t, DriverAgentIpmitool, actual[0]) + th.CheckDeepEquals(t, DriverFake, actual[1]) + th.AssertEquals(t, "ipmi", actual[2].Name) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestGetDriverDetails(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetDriverDetailsSuccessfully(t) + + c := client.ServiceClient() + actual, err := drivers.GetDriverDetails(c, "ipmi").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, DriverIpmi, *actual) +} + +func TestGetDriverProperties(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetDriverPropertiesSuccessfully(t) + + c := client.ServiceClient() + actual, err := drivers.GetDriverProperties(c, "agent_ipmitool").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, DriverIpmiToolProperties, *actual) +} + +func TestGetDriverDiskProperties(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetDriverDiskPropertiesSuccessfully(t) + + c := client.ServiceClient() + actual, err := drivers.GetDriverDiskProperties(c, "agent_ipmitool").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, DriverIpmiToolDisk, *actual) +} diff --git a/openstack/baremetal/v1/drivers/urls.go b/openstack/baremetal/v1/drivers/urls.go new file mode 100644 index 0000000000..d5ddba7d89 --- /dev/null +++ b/openstack/baremetal/v1/drivers/urls.go @@ -0,0 +1,19 @@ +package drivers + +import "github.com/gophercloud/gophercloud" + +func driversURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("drivers") +} + +func driverDetailsURL(client *gophercloud.ServiceClient, driverName string) string { + return client.ServiceURL("drivers", driverName) +} + +func driverPropertiesURL(client *gophercloud.ServiceClient, driverName string) string { + return client.ServiceURL("drivers", driverName, "properties") +} + +func driverDiskPropertiesURL(client *gophercloud.ServiceClient, driverName string) string { + return client.ServiceURL("drivers", driverName, "raid", "logical_disk_properties") +} From 02db1fdbf11c1d21135de07297a365a80eb0607b Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Thu, 7 Mar 2019 01:49:29 -0300 Subject: [PATCH 0691/2296] Baremetal acceptance job (#1483) * Baremetal acceptance job This commit aims to create a new job to run only baremetal acceptance tests on DevStack since OpenLab already have a PR to support Ironic. - Create a new job `gophercloud-acceptance-test-ironic` - Create a playbook for the job - Create a script to run only ironic acceptance * Add gophercloud-acceptance-test-ironic to check This commit adds the `gophercloud-acceptance-test-ironic` to the check queue * Add required-projects and OVERRIDE_ENABLE_SERVICES Trying to setup DevStack with Ironic in the new job we got `The /opt/stack/new/ironic project was not found`, adding ironic and other projects to required-projects and also setting the enable services. * Use PROJECTS instead of required-projects `required-projects` can't find `openstack/` * Require only ironic in PROJECTS Try to use only `openstack/ironic` * Add missing tasks field The playbook didn't have the `tasks` field * Update ENABLED_SERVICES Change ENABLE_SERVICES to the ones in ironic-standalone job in OpenStack. * Change permissions for acacceptancetest-ironic Change permissions to 755 like acceptancetest file to avoid Permission denied when running on the job. --- .zuul.yaml | 8 +++++ .../run.yaml | 26 +++++++++++++++++ script/acceptancetest-ironic | 29 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml create mode 100755 script/acceptancetest-ironic diff --git a/.zuul.yaml b/.zuul.yaml index 8c31ea160e..69fb4709f5 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -13,6 +13,13 @@ Run gophercloud acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml +- job: + name: gophercloud-acceptance-test-ironic + parent: golang-test + description: | + Run gophercloud ironic acceptance test on master branch + run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml + - job: name: gophercloud-acceptance-test-queens parent: gophercloud-acceptance-test @@ -74,6 +81,7 @@ jobs: - gophercloud-unittest - gophercloud-acceptance-test + - gophercloud-acceptance-test-ironic recheck-mitaka: jobs: - gophercloud-acceptance-test-mitaka diff --git a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml new file mode 100644 index 0000000000..64b098c282 --- /dev/null +++ b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml @@ -0,0 +1,26 @@ +- hosts: all + become: yes + roles: + - config-golang + - clone-devstack-gate-to-workspace + - role: create-devstack-local-conf + enable_services: + - 'ironic' + - role: install-devstack + environment: + 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,tempest' + PROJECTS: 'openstack/ironic' + tasks: + - name: Run ironic acceptance tests with gophercloud + shell: + cmd: | + set -e + set -o pipefail + set -x + echo $(export |grep OS_BRANCH) + + go get ./... || true + ./script/acceptancetest-ironic -v 2>&1 | tee $TEST_RESULTS_TXT + executable: /bin/bash + chdir: '{{ zuul.project.src_dir }}' + environment: '{{ global_env }}' diff --git a/script/acceptancetest-ironic b/script/acceptancetest-ironic new file mode 100755 index 0000000000..aa1ce2bd33 --- /dev/null +++ b/script/acceptancetest-ironic @@ -0,0 +1,29 @@ +#!/bin/bash +# +set -x + +source `dirname $0`/stackenv + +timeout="60m" +failed= + +# Run the Ironic acceptance tests. +# Check the error code after each suite, but do not exit early if a suite failed. + +ACCEPTANCE_TESTS=( +acceptance/openstack/baremetal/v1 +) + +for acceptance_test in ${ACCEPTANCE_TESTS[@]}; do + go test -v -timeout $timeout -tags "fixtures acceptance" ./${acceptance_test} + if [[ $? != 0 ]]; then + failed=1 + fi +done + +# If any of the test suites failed, exit 1 +if [[ -n $failed ]]; then + exit 1 +fi + +exit 0 From fe1ba5ce12dda9d3056d303dfe080d5eccdd1780 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 7 Mar 2019 23:06:56 +0100 Subject: [PATCH 0692/2296] Acceptance: Allow JSON slices unmarshalling in debug mode (#1496) --- acceptance/clients/http.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/acceptance/clients/http.go b/acceptance/clients/http.go index ed09a1ac52..c441d8abb6 100644 --- a/acceptance/clients/http.go +++ b/acceptance/clients/http.go @@ -102,14 +102,25 @@ func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, contentType stri // formatJSON will try to pretty-format a JSON body. // It will also mask known fields which contain sensitive information. func (lrt *LogRoundTripper) formatJSON(raw []byte) string { - var data map[string]interface{} + var rawData interface{} - err := json.Unmarshal(raw, &data) + err := json.Unmarshal(raw, &rawData) if err != nil { log.Printf("[DEBUG] Unable to parse OpenStack JSON: %s", err) return string(raw) } + data, ok := rawData.(map[string]interface{}) + if !ok { + pretty, err := json.MarshalIndent(rawData, "", " ") + if err != nil { + log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err) + return string(raw) + } + + return string(pretty) + } + // Mask known password fields if v, ok := data["auth"].(map[string]interface{}); ok { if v, ok := v["identity"].(map[string]interface{}); ok { From 276f2d2fb9144ed56ee08f06514d247da3ad23d2 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 12 Mar 2019 20:41:25 +0100 Subject: [PATCH 0693/2296] Networking V2: Add DNS extension support (#1481) * Networking V2: Add DNS extension support * test port * fip * net * Acceptance tests * Enable network listing * Remove network DNS filtering * Remove floating IP DNS filtering * Updated comments --- .../networking/v2/extensions/dns/dns.go | 135 ++++++ .../networking/v2/extensions/dns/dns_test.go | 266 ++++++++++++ .../networking/v2/extensions/dns/requests.go | 171 ++++++++ .../networking/v2/extensions/dns/results.go | 30 ++ .../v2/extensions/dns/testing/fixtures.go | 320 +++++++++++++++ .../extensions/dns/testing/requests_test.go | 387 ++++++++++++++++++ .../extensions/layer3/floatingips/requests.go | 27 +- .../extensions/layer3/floatingips/results.go | 14 +- .../layer3/floatingips/testing/fixtures.go | 50 +++ .../floatingips/testing/requests_test.go | 29 +- .../v2/networks/testing/fixtures.go | 12 +- .../networking/v2/ports/testing/fixtures.go | 16 + script/acceptancetest | 1 + 13 files changed, 1416 insertions(+), 42 deletions(-) create mode 100644 acceptance/openstack/networking/v2/extensions/dns/dns.go create mode 100644 acceptance/openstack/networking/v2/extensions/dns/dns_test.go create mode 100644 openstack/networking/v2/extensions/dns/requests.go create mode 100644 openstack/networking/v2/extensions/dns/results.go create mode 100644 openstack/networking/v2/extensions/dns/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/dns/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go diff --git a/acceptance/openstack/networking/v2/extensions/dns/dns.go b/acceptance/openstack/networking/v2/extensions/dns/dns.go new file mode 100644 index 0000000000..dba9dfed9b --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/dns/dns.go @@ -0,0 +1,135 @@ +package dns + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/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" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// PortWithDNSExt represents a port with the DNS fields +type PortWithDNSExt struct { + ports.Port + dns.PortDNSExt +} + +// FloatingIPWithDNSExt represents a floating IP with the DNS fields +type FloatingIPWithDNSExt struct { + floatingips.FloatingIP + dns.FloatingIPDNSExt +} + +// NetworkWithDNSExt represents a network with the DNS fields +type NetworkWithDNSExt struct { + networks.Network + dns.NetworkDNSExt +} + +// CreatePortDNS will create a port with a DNS name on the specified subnet. An +// error will be returned if the port could not be created. +func CreatePortDNS(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, dnsName string) (*PortWithDNSExt, error) { + portName := tools.RandomString("TESTACC-", 8) + portDescription := tools.RandomString("TESTACC-PORT-DESC-", 8) + iFalse := true + + t.Logf("Attempting to create port: %s", portName) + + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + Name: portName, + Description: portDescription, + AdminStateUp: &iFalse, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + } + + createOpts := dns.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + DNSName: dnsName, + } + + var port PortWithDNSExt + + err := ports.Create(client, createOpts).ExtractInto(&port) + if err != nil { + return &port, err + } + + t.Logf("Successfully created port: %s", portName) + + th.AssertEquals(t, port.Name, portName) + th.AssertEquals(t, port.Description, portDescription) + th.AssertEquals(t, port.DNSName, dnsName) + + return &port, nil +} + +// CreateFloatingIPDNS creates a floating IP with the DNS extension on a given +// network and port. An error will be returned if the creation failed. +func CreateFloatingIPDNS(t *testing.T, client *gophercloud.ServiceClient, networkID, portID, dnsName, dnsDomain string) (*FloatingIPWithDNSExt, error) { + t.Logf("Attempting to create floating IP on port: %s", portID) + + fipDescription := "Test floating IP" + fipCreateOpts := &floatingips.CreateOpts{ + Description: fipDescription, + FloatingNetworkID: networkID, + PortID: portID, + } + + createOpts := dns.FloatingIPCreateOptsExt{ + CreateOptsBuilder: fipCreateOpts, + DNSName: dnsName, + DNSDomain: dnsDomain, + } + + var floatingIP FloatingIPWithDNSExt + err := floatingips.Create(client, createOpts).ExtractInto(&floatingIP) + if err != nil { + return &floatingIP, err + } + + t.Logf("Created floating IP.") + + th.AssertEquals(t, floatingIP.Description, fipDescription) + th.AssertEquals(t, floatingIP.FloatingNetworkID, networkID) + th.AssertEquals(t, floatingIP.PortID, portID) + th.AssertEquals(t, floatingIP.DNSName, dnsName) + th.AssertEquals(t, floatingIP.DNSDomain, dnsDomain) + + return &floatingIP, err +} + +// 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) { + networkName := tools.RandomString("TESTACC-", 8) + networkCreateOpts := networks.CreateOpts{ + Name: networkName, + AdminStateUp: gophercloud.Enabled, + } + + createOpts := dns.NetworkCreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + DNSDomain: dnsDomanin, + } + + t.Logf("Attempting to create network: %s", networkName) + + var network NetworkWithDNSExt + + err := networks.Create(client, createOpts).ExtractInto(&network) + if err != nil { + return &network, err + } + + t.Logf("Successfully created network.") + + th.AssertEquals(t, network.Name, networkName) + th.AssertEquals(t, network.DNSDomain, dnsDomanin) + + 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 new file mode 100644 index 0000000000..1e74df805f --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -0,0 +1,266 @@ +// +build acceptance networking + +package dns + +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/openstack/common/extensions" + "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" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestDNSPortCRUDL(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + extension, err := extensions.Get(client, "dns-integration").Extract() + if err != nil { + t.Skip("This test requires dns-integration Neutron extension") + } + tools.PrintResource(t, extension) + + // Create Network + networkDNSDomain := "local." + network, err := CreateNetworkDNS(t, client, networkDNSDomain) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := networking.CreateSubnet(t, client, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, client, subnet.ID) + + // Create port + portDNSName := "port" + port, err := CreatePortDNS(t, client, network.ID, subnet.ID, portDNSName) + th.AssertNoErr(t, err) + defer networking.DeletePort(t, client, port.ID) + + tools.PrintResource(t, port) + + if os.Getenv("OS_BRANCH") == "stable/mitaka" { + // List port successfully + var listOpts ports.ListOptsBuilder + listOpts = dns.PortListOptsExt{ + ListOptsBuilder: ports.ListOpts{}, + DNSName: portDNSName, + } + var listedPorts []PortWithDNSExt + i := 0 + err = ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + i++ + err := ports.ExtractPortsInto(page, &listedPorts) + if err != nil { + t.Errorf("Failed to extract ports: %v", err) + return false, err + } + + tools.PrintResource(t, listedPorts) + + th.AssertEquals(t, 1, len(listedPorts)) + th.CheckDeepEquals(t, *port, listedPorts[0]) + + return true, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, i) + + // List port unsuccessfully + listOpts = dns.PortListOptsExt{ + ListOptsBuilder: ports.ListOpts{}, + DNSName: "foo", + } + i = 0 + err = ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + i++ + err := ports.ExtractPortsInto(page, &listedPorts) + if err != nil { + t.Errorf("Failed to extract ports: %v", err) + return false, err + } + + tools.PrintResource(t, listedPorts) + + th.AssertEquals(t, 1, len(listedPorts)) + th.CheckDeepEquals(t, *port, listedPorts[0]) + + return true, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, i) + } + + // Get port + var getPort PortWithDNSExt + err = ports.Get(client, port.ID).ExtractInto(&getPort) + th.AssertNoErr(t, err) + + tools.PrintResource(t, getPort) + th.AssertDeepEquals(t, *port, getPort) + + // Update port + newPortName := "" + newPortDescription := "" + newDNSName := "" + portUpdateOpts := ports.UpdateOpts{ + Name: &newPortName, + Description: &newPortDescription, + } + var updateOpts ports.UpdateOptsBuilder + updateOpts = dns.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + DNSName: &newDNSName, + } + + var newPort PortWithDNSExt + err = ports.Update(client, port.ID, updateOpts).ExtractInto(&newPort) + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPort) + th.AssertEquals(t, newPort.Description, newPortName) + th.AssertEquals(t, newPort.Description, newPortDescription) + th.AssertEquals(t, newPort.DNSName, newDNSName) + + // Get updated port + var getNewPort PortWithDNSExt + err = ports.Get(client, port.ID).ExtractInto(&getNewPort) + th.AssertNoErr(t, err) + + tools.PrintResource(t, getNewPort) + // workaround for update race condition + newPort.DNSAssignment = nil + getNewPort.DNSAssignment = nil + th.AssertDeepEquals(t, newPort, getNewPort) +} + +func TestDNSFloatingIPCRDL(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + extension, err := extensions.Get(client, "dns-integration").Extract() + if err != nil { + t.Skip("This test requires dns-integration Neutron extension") + } + tools.PrintResource(t, extension) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + // Create Network + networkDNSDomain := "local." + network, err := CreateNetworkDNS(t, client, networkDNSDomain) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := networking.CreateSubnet(t, client, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, client, subnet.ID) + + // Create Router + router, err := layer3.CreateExternalRouter(t, client) + th.AssertNoErr(t, err) + defer layer3.DeleteRouter(t, client, router.ID) + + // Create router interface + routerPort, err := networking.CreatePort(t, client, network.ID, subnet.ID) + th.AssertNoErr(t, err) + _, err = layer3.CreateRouterInterface(t, client, routerPort.ID, router.ID) + th.AssertNoErr(t, err) + defer layer3.DeleteRouterInterface(t, client, routerPort.ID, router.ID) + + // Create port + portDNSName := "port" + port, err := CreatePortDNS(t, client, network.ID, subnet.ID, portDNSName) + th.AssertNoErr(t, err) + defer networking.DeletePort(t, client, port.ID) + + tools.PrintResource(t, port) + + // Create floating IP + fipDNSName := "fip" + fipDNSDomain := "local." + fip, err := CreateFloatingIPDNS(t, client, choices.ExternalNetworkID, port.ID, fipDNSName, fipDNSDomain) + th.AssertNoErr(t, err) + defer layer3.DeleteFloatingIP(t, client, fip.ID) + + // Get floating IP + var getFip FloatingIPWithDNSExt + err = floatingips.Get(client, fip.ID).ExtractInto(&getFip) + th.AssertNoErr(t, err) + + tools.PrintResource(t, getFip) + th.AssertDeepEquals(t, *fip, getFip) +} + +func TestDNSNetwork(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + extension, err := extensions.Get(client, "dns-integration").Extract() + if err != nil { + t.Skip("This test requires dns-integration Neutron extension") + } + tools.PrintResource(t, extension) + + // Create Network + networkDNSDomain := "local." + network, err := CreateNetworkDNS(t, client, networkDNSDomain) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + // Get network + var getNetwork NetworkWithDNSExt + err = networks.Get(client, network.ID).ExtractInto(&getNetwork) + th.AssertNoErr(t, err) + + tools.PrintResource(t, getNetwork) + th.AssertDeepEquals(t, *network, getNetwork) + + // Update network + newNetworkName := "" + newNetworkDescription := "" + newNetworkDNSDomain := "" + networkUpdateOpts := networks.UpdateOpts{ + Name: newNetworkName, + Description: &newNetworkDescription, + } + var updateOpts networks.UpdateOptsBuilder + updateOpts = dns.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + DNSDomain: &newNetworkDNSDomain, + } + + var newNetwork NetworkWithDNSExt + err = networks.Update(client, network.ID, updateOpts).ExtractInto(&newNetwork) + th.AssertNoErr(t, err) + + tools.PrintResource(t, newNetwork) + th.AssertEquals(t, newNetwork.Description, newNetworkName) + th.AssertEquals(t, newNetwork.Description, newNetworkDescription) + th.AssertEquals(t, newNetwork.DNSDomain, newNetworkDNSDomain) + + // Get updated network + var getNewNetwork NetworkWithDNSExt + err = networks.Get(client, network.ID).ExtractInto(&getNewNetwork) + th.AssertNoErr(t, err) + + tools.PrintResource(t, getNewNetwork) + th.AssertDeepEquals(t, newNetwork, getNewNetwork) +} diff --git a/openstack/networking/v2/extensions/dns/requests.go b/openstack/networking/v2/extensions/dns/requests.go new file mode 100644 index 0000000000..b7bd62d2c8 --- /dev/null +++ b/openstack/networking/v2/extensions/dns/requests.go @@ -0,0 +1,171 @@ +package dns + +import ( + "net/url" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" +) + +// PortListOptsExt adds the DNS options to the base port ListOpts. +type PortListOptsExt struct { + ports.ListOptsBuilder + + DNSName string `q:"dns_name"` +} + +// ToPortListQuery adds the DNS options to the base port list options. +func (opts PortListOptsExt) ToPortListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts.ListOptsBuilder) + if err != nil { + return "", err + } + + params := q.Query() + + if opts.DNSName != "" { + params.Add("dns_name", opts.DNSName) + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), err +} + +// PortCreateOptsExt adds port DNS options to the base ports.CreateOpts. +type PortCreateOptsExt struct { + // CreateOptsBuilder is the interface options structs have to satisfy in order + // to be used in the main Create operation in this package. + ports.CreateOptsBuilder + + // Set DNS name to the port + DNSName string `json:"dns_name,omitempty"` +} + +// ToPortCreateMap casts a CreateOpts struct to a map. +func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToPortCreateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]interface{}) + + if opts.DNSName != "" { + port["dns_name"] = opts.DNSName + } + + return base, nil +} + +// PortUpdateOptsExt adds DNS options to the base ports.UpdateOpts +type PortUpdateOptsExt struct { + // UpdateOptsBuilder is the interface options structs have to satisfy in order + // to be used in the main Update operation in this package. + ports.UpdateOptsBuilder + + // Set DNS name to the port + DNSName *string `json:"dns_name,omitempty"` +} + +// ToPortUpdateMap casts an UpdateOpts struct to a map. +func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]interface{}) + + if opts.DNSName != nil { + port["dns_name"] = *opts.DNSName + } + + return base, nil +} + +// FloatingIPCreateOptsExt adds floating IP DNS options to the base floatingips.CreateOpts. +type FloatingIPCreateOptsExt struct { + // CreateOptsBuilder is the interface options structs have to satisfy in order + // to be used in the main Create operation in this package. + floatingips.CreateOptsBuilder + + // Set DNS name to the floating IPs + DNSName string `json:"dns_name,omitempty"` + + // Set DNS domain to the floating IPs + DNSDomain string `json:"dns_domain,omitempty"` +} + +// ToFloatingIPCreateMap casts a CreateOpts struct to a map. +func (opts FloatingIPCreateOptsExt) ToFloatingIPCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToFloatingIPCreateMap() + if err != nil { + return nil, err + } + + floatingip := base["floatingip"].(map[string]interface{}) + + if opts.DNSName != "" { + floatingip["dns_name"] = opts.DNSName + } + + if opts.DNSDomain != "" { + floatingip["dns_domain"] = opts.DNSDomain + } + + return base, nil +} + +// NetworkCreateOptsExt adds network DNS options to the base networks.CreateOpts. +type NetworkCreateOptsExt struct { + // CreateOptsBuilder is the interface options structs have to satisfy in order + // to be used in the main Create operation in this package. + networks.CreateOptsBuilder + + // Set DNS domain to the network + DNSDomain string `json:"dns_domain,omitempty"` +} + +// ToNetworkCreateMap casts a CreateOpts struct to a map. +func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() + if err != nil { + return nil, err + } + + network := base["network"].(map[string]interface{}) + + if opts.DNSDomain != "" { + network["dns_domain"] = opts.DNSDomain + } + + return base, nil +} + +// NetworkUpdateOptsExt adds network DNS options to the base networks.UpdateOpts +type NetworkUpdateOptsExt struct { + // UpdateOptsBuilder is the interface options structs have to satisfy in order + // to be used in the main Update operation in this package. + networks.UpdateOptsBuilder + + // Set DNS domain to the network + DNSDomain *string `json:"dns_domain,omitempty"` +} + +// ToNetworkUpdateMap casts an UpdateOpts struct to a map. +func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() + if err != nil { + return nil, err + } + + network := base["network"].(map[string]interface{}) + + if opts.DNSDomain != nil { + network["dns_domain"] = *opts.DNSDomain + } + + return base, nil +} diff --git a/openstack/networking/v2/extensions/dns/results.go b/openstack/networking/v2/extensions/dns/results.go new file mode 100644 index 0000000000..ce5752fc59 --- /dev/null +++ b/openstack/networking/v2/extensions/dns/results.go @@ -0,0 +1,30 @@ +package dns + +// PortDNSExt represents a decorated form of a Port with the additional +// Port DNS information. +type PortDNSExt struct { + // The DNS name of the port. + DNSName string `json:"dns_name"` + + // The DNS assignment of the port. + DNSAssignment []map[string]string `json:"dns_assignment"` +} + +// FloatingIPDNSExt represents a decorated form of a Floating IP with the +// additional Floating IP DNS information. +type FloatingIPDNSExt struct { + // The DNS name of the floating IP, assigned to the external DNS + // service. + DNSName string `json:"dns_name"` + + // The DNS domain of the floating IP, assigned to the external DNS + // service. + DNSDomain string `json:"dns_domain"` +} + +// NetworkDNSExt represents a decorated form of a Network with the additional +// Network DNS information. +type NetworkDNSExt struct { + // The DNS domain of the network. + DNSDomain string `json:"dns_domain"` +} diff --git a/openstack/networking/v2/extensions/dns/testing/fixtures.go b/openstack/networking/v2/extensions/dns/testing/fixtures.go new file mode 100644 index 0000000000..6da93ec82c --- /dev/null +++ b/openstack/networking/v2/extensions/dns/testing/fixtures.go @@ -0,0 +1,320 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + floatingiptest "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/testing" + networktest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" + porttest "github.com/gophercloud/gophercloud/openstack/networking/v2/ports/testing" + th "github.com/gophercloud/gophercloud/testhelper" +) + +const NetworkCreateRequest = ` +{ + "network": { + "name": "private", + "admin_state_up": true, + "dns_domain": "local." + } +}` + +const NetworkCreateResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local.", + "dns_domain": "local." + } +}` + +const NetworkUpdateRequest = ` +{ + "network": { + "name": "new_network_name", + "admin_state_up": false, + "dns_domain": "" + } +}` + +const NetworkUpdateResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], + "name": "new_network_name", + "admin_state_up": false, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "provider:segmentation_id": 9876543210, + "provider:physical_network": null, + "provider:network_type": "local.", + "dns_domain": "" + } +}` + +func PortHandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + th.AssertEquals(t, r.RequestURI, "/v2.0/ports?dns_name=test-port") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, porttest.ListResponse) + }) +} + +func PortHandleGet(t *testing.T) { + th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", 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, porttest.GetResponse) + }) +} + +func PortHandleCreate(t *testing.T) { + 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, ` +{ + "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"], + "dns_name": "test-port" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "port": { + "status": "DOWN", + "name": "private-port", + "allowed_address_pairs": [], + "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" + } + ], + "dns_name": "test-port", + "dns_assignment": [ + { + "hostname": "test-port", + "ip_address": "172.24.4.2", + "fqdn": "test-port.openstack.local." + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ], + "device_id": "" + } +} + `) + }) +} + +func PortHandleUpdate(t *testing.T) { + 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, ` +{ + "port": { + "name": "new_port_name", + "fixed_ips": [ + { + "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", + "ip_address": "10.0.0.3" + } + ], + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ], + "dns_name": "test-port1" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "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" + } + ], + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "security_groups": [ + "f0ac4394-7e4a-4409-9701-ba8be283dbc3" + ], + "device_id": "", + "dns_name": "test-port1", + "dns_assignment": [ + { + "hostname": "test-port1", + "ip_address": "172.24.4.2", + "fqdn": "test-port1.openstack.local." + } + ] + } +} + `) + }) +} + +func FloatingIPHandleList(t *testing.T) { + th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + th.AssertEquals(t, r.RequestURI, "/v2.0/floatingips?dns_domain=local.&dns_name=test-fip") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, floatingiptest.ListResponseDNS) + }) +} + +func FloatingIPHandleGet(t *testing.T) { + th.Mux.HandleFunc("/v2.0/floatingips/2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", 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, fmt.Sprintf(`{"floatingip": %s}`, floatingiptest.FipDNS)) + }) +} + +func FloatingIPHandleCreate(t *testing.T) { + th.Mux.HandleFunc("/v2.0/floatingips", 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, ` +{ + "floatingip": { + "floating_network_id": "6d67c30a-ddb4-49a1-bec3-a65b286b4170", + "dns_name": "test-fip", + "dns_domain": "local." + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, fmt.Sprintf(`{"floatingip": %s}`, floatingiptest.FipDNS)) + }) +} + +func NetworkHandleList(t *testing.T) { + th.Mux.HandleFunc("/v2.0/networks", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + th.AssertEquals(t, r.RequestURI, "/v2.0/networks?dns_domain=local.") + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, networktest.ListResponse) + }) +} + +func NetworkHandleGet(t *testing.T) { + th.Mux.HandleFunc("/v2.0/networks/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, networktest.GetResponse) + }) +} + +func NetworkHandleCreate(t *testing.T) { + th.Mux.HandleFunc("/v2.0/networks", 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, NetworkCreateRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, NetworkCreateResponse) + }) +} + +func NetworkHandleUpdate(t *testing.T) { + th.Mux.HandleFunc("/v2.0/networks/db193ab3-96e3-4cb3-8fc5-05f4296d0324", 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, NetworkUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, NetworkUpdateResponse) + }) +} diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go new file mode 100644 index 0000000000..910842ffc1 --- /dev/null +++ b/openstack/networking/v2/extensions/dns/testing/requests_test.go @@ -0,0 +1,387 @@ +package testing + +import ( + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "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" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/testhelper" +) + +type PortDNS struct { + ports.Port + dns.PortDNSExt +} + +type FloatingIPDNS struct { + floatingips.FloatingIP + dns.FloatingIPDNSExt +} + +type NetworkDNS struct { + networks.Network + dns.NetworkDNSExt +} + +func TestPortList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + PortHandleListSuccessfully(t) + + var actual []PortDNS + + expected := []PortDNS{ + { + Port: ports.Port{ + Status: "ACTIVE", + Name: "", + AdminStateUp: true, + NetworkID: "70c1db1f-b701-45bd-96e0-a313ee3430b3", + TenantID: "", + DeviceOwner: "network:router_gateway", + MACAddress: "fa:16:3e:58:42:ed", + FixedIPs: []ports.IP{ + { + SubnetID: "008ba151-0b8c-4a67-98b5-0d2b87666062", + IPAddress: "172.24.4.2", + }, + }, + ID: "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", + SecurityGroups: []string{}, + DeviceID: "9ae135f4-b6e0-4dad-9e91-3c223e385824", + }, + PortDNSExt: dns.PortDNSExt{ + DNSName: "test-port", + DNSAssignment: []map[string]string{ + { + "hostname": "test-port", + "ip_address": "172.24.4.2", + "fqdn": "test-port.openstack.local.", + }, + }, + }, + }, + } + + var listOptsBuilder ports.ListOptsBuilder + listOptsBuilder = dns.PortListOptsExt{ + ListOptsBuilder: ports.ListOpts{}, + DNSName: "test-port", + } + + allPages, err := ports.List(fake.ServiceClient(), listOptsBuilder).AllPages() + th.AssertNoErr(t, err) + + err = ports.ExtractPortsInto(allPages, &actual) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expected, actual) +} + +func TestPortGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + PortHandleGet(t) + + var s PortDNS + + err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, "ACTIVE") + th.AssertEquals(t, s.Name, "") + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "7e02058126cc4950b75f9970368ba177") + th.AssertEquals(t, s.DeviceOwner, "network:router_interface") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:23:fd:d7") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"}, + }) + th.AssertEquals(t, s.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2") + th.AssertDeepEquals(t, s.SecurityGroups, []string{}) + th.AssertEquals(t, s.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e") + + th.AssertEquals(t, s.DNSName, "test-port") + th.AssertDeepEquals(t, s.DNSAssignment, []map[string]string{ + { + "hostname": "test-port", + "ip_address": "172.24.4.2", + "fqdn": "test-port.openstack.local.", + }, + }) +} + +func TestPortCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + PortHandleCreate(t) + + var s PortDNS + + asu := true + portCreateOpts := 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"}, + } + + createOpts := dns.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + DNSName: "test-port", + } + + err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, "DOWN") + th.AssertEquals(t, s.Name, "private-port") + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, s.DeviceOwner, "") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }) + th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) + + th.AssertEquals(t, s.DNSName, "test-port") + th.AssertDeepEquals(t, s.DNSAssignment, []map[string]string{ + { + "hostname": "test-port", + "ip_address": "172.24.4.2", + "fqdn": "test-port.openstack.local.", + }, + }) +} + +func TestPortRequiredCreateOpts(t *testing.T) { + res := ports.Create(fake.ServiceClient(), dns.PortCreateOptsExt{CreateOptsBuilder: ports.CreateOpts{}}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestPortUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + PortHandleUpdate(t) + + var s PortDNS + + name := "new_port_name" + portUpdateOpts := 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"}, + } + + dnsName := "test-port1" + updateOpts := dns.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + DNSName: &dnsName, + } + + err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Name, "new_port_name") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"}, + }) + th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}) + th.AssertEquals(t, s.DNSName, "test-port1") + th.AssertDeepEquals(t, s.DNSAssignment, []map[string]string{ + { + "hostname": "test-port1", + "ip_address": "172.24.4.2", + "fqdn": "test-port1.openstack.local.", + }, + }) +} + +func TestFloatingIPGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + FloatingIPHandleGet(t) + + var actual FloatingIPDNS + err := floatingips.Get(fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").ExtractInto(&actual) + th.AssertNoErr(t, err) + + expected := FloatingIPDNS{ + FloatingIP: floatingips.FloatingIP{ + FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", + FixedIP: "", + FloatingIP: "192.0.0.4", + TenantID: "017d8de156df4177889f31a9bd6edc00", + Status: "DOWN", + PortID: "", + ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", + RouterID: "1117c30a-ddb4-49a1-bec3-a65b286b4170", + }, + FloatingIPDNSExt: dns.FloatingIPDNSExt{ + DNSName: "test-fip", + DNSDomain: "local.", + }, + } + + th.CheckDeepEquals(t, expected, actual) +} + +func TestFloatingIPCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + FloatingIPHandleCreate(t) + + var actual FloatingIPDNS + + fipCreateOpts := floatingips.CreateOpts{ + FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", + } + + options := dns.FloatingIPCreateOptsExt{ + CreateOptsBuilder: fipCreateOpts, + DNSName: "test-fip", + DNSDomain: "local.", + } + + err := floatingips.Create(fake.ServiceClient(), options).ExtractInto(&actual) + th.AssertNoErr(t, err) + + expected := FloatingIPDNS{ + FloatingIP: floatingips.FloatingIP{ + FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", + FixedIP: "", + FloatingIP: "192.0.0.4", + TenantID: "017d8de156df4177889f31a9bd6edc00", + Status: "DOWN", + PortID: "", + ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", + RouterID: "1117c30a-ddb4-49a1-bec3-a65b286b4170", + }, + FloatingIPDNSExt: dns.FloatingIPDNSExt{ + DNSName: "test-fip", + DNSDomain: "local.", + }, + } + + th.CheckDeepEquals(t, expected, actual) +} + +func TestNetworkGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + NetworkHandleGet(t) + + var actual NetworkDNS + + err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&actual) + th.AssertNoErr(t, err) + + expected := NetworkDNS{ + Network: networks.Network{ + Name: "public", + Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, + Status: "ACTIVE", + TenantID: "4fd44f30292945e481c7b8a0c8908869", + AdminStateUp: true, + Shared: true, + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + }, + NetworkDNSExt: dns.NetworkDNSExt{ + DNSDomain: "local.", + }, + } + + th.CheckDeepEquals(t, expected, actual) +} + +func TestNetworkCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + NetworkHandleCreate(t) + + var actual NetworkDNS + + iTrue := true + networkCreateOpts := networks.CreateOpts{Name: "private", AdminStateUp: &iTrue} + createOpts := dns.NetworkCreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + DNSDomain: "local.", + } + + err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&actual) + th.AssertNoErr(t, err) + + expected := NetworkDNS{ + Network: networks.Network{ + Name: "private", + Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, + Status: "ACTIVE", + TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", + AdminStateUp: true, + Shared: false, + ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + }, + NetworkDNSExt: dns.NetworkDNSExt{ + DNSDomain: "local.", + }, + } + + th.CheckDeepEquals(t, expected, actual) +} + +func TestNetworkUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + NetworkHandleUpdate(t) + + var actual NetworkDNS + + networkUpdateOpts := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: new(bool)} + updateOpts := dns.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + DNSDomain: new(string), + } + + err := networks.Update(fake.ServiceClient(), "db193ab3-96e3-4cb3-8fc5-05f4296d0324", updateOpts).ExtractInto(&actual) + th.AssertNoErr(t, err) + + expected := NetworkDNS{ + Network: networks.Network{ + Name: "new_network_name", + Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, + Status: "ACTIVE", + TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", + AdminStateUp: false, + Shared: false, + ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + }, + NetworkDNSExt: dns.NetworkDNSExt{ + DNSDomain: "", + }, + } + + th.CheckDeepEquals(t, expected, actual) +} diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index b7e6e9189d..0c0db64d8d 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -5,6 +5,12 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToFloatingIPListQuery() (string, error) +} + // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the floating IP attributes you want to see returned. SortKey allows you to @@ -31,16 +37,25 @@ type ListOpts struct { NotTagsAny string `q:"not-tags-any"` } +// ToNetworkListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToFloatingIPListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // List returns a Pager which allows you to iterate over a collection of // floating IP resources. It accepts a ListOpts struct, which allows you to // filter and sort the returned collection for greater efficiency. -func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { - q, err := gophercloud.BuildQueryString(&opts) - if err != nil { - return pagination.Pager{Err: err} +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToFloatingIPListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query } - u := rootURL(c) + q.String() - return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return FloatingIPPage{pagination.LinkedPageBase{PageResult: r}} }) } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index b0b25a9d66..a9709ccec3 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -56,11 +56,13 @@ type commonResult struct { // Extract will extract a FloatingIP resource from a result. func (r commonResult) Extract() (*FloatingIP, error) { - var s struct { - FloatingIP *FloatingIP `json:"floatingip"` - } + var s FloatingIP err := r.ExtractInto(&s) - return s.FloatingIP, err + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "floatingip") } // CreateResult represents the result of a create operation. Call its Extract @@ -123,3 +125,7 @@ func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { err := (r.(FloatingIPPage)).ExtractInto(&s) return s.FloatingIPs, err } + +func ExtractFloatingIPsInto(r pagination.Page, v interface{}) error { + return r.(FloatingIPPage).Result.ExtractIntoSlicePtr(v, "floatingips") +} diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go new file mode 100644 index 0000000000..0f665462ab --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go @@ -0,0 +1,50 @@ +package testing + +import ( + "fmt" +) + +const FipDNS = `{ + "floating_network_id": "6d67c30a-ddb4-49a1-bec3-a65b286b4170", + "router_id": null, + "fixed_ip_address": null, + "floating_ip_address": "192.0.0.4", + "tenant_id": "017d8de156df4177889f31a9bd6edc00", + "status": "DOWN", + "port_id": null, + "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", + "router_id": "1117c30a-ddb4-49a1-bec3-a65b286b4170", + "dns_domain": "local.", + "dns_name": "test-fip" + }` + +const FipNoDNS = `{ + "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64", + "router_id": "0a24cb83-faf5-4d7f-b723-3144ed8a2167", + "fixed_ip_address": "192.0.0.2", + "floating_ip_address": "10.0.0.3", + "tenant_id": "017d8de156df4177889f31a9bd6edc00", + "status": "DOWN", + "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25", + "id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63", + "router_id": "2227c30a-ddb4-49a1-bec3-a65b286b4170", + "dns_domain": "", + "dns_name": "" + }` + +var ListResponse = fmt.Sprintf(` +{ + "floatingips": [ +%s, +%s + ] +} +`, FipDNS, FipNoDNS) + +var ListResponseDNS = fmt.Sprintf(` +{ + "floatingips": [ +%s + ] +} +`, FipDNS) diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index cef20e1f62..ceba7a9464 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -22,34 +22,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "floatingips": [ - { - "floating_network_id": "6d67c30a-ddb4-49a1-bec3-a65b286b4170", - "router_id": null, - "fixed_ip_address": null, - "floating_ip_address": "192.0.0.4", - "tenant_id": "017d8de156df4177889f31a9bd6edc00", - "status": "DOWN", - "port_id": null, - "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", - "router_id": "1117c30a-ddb4-49a1-bec3-a65b286b4170" - }, - { - "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64", - "router_id": "0a24cb83-faf5-4d7f-b723-3144ed8a2167", - "fixed_ip_address": "192.0.0.2", - "floating_ip_address": "10.0.0.3", - "tenant_id": "017d8de156df4177889f31a9bd6edc00", - "status": "DOWN", - "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25", - "id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63", - "router_id": "2227c30a-ddb4-49a1-bec3-a65b286b4170" - } - ] -} - `) + fmt.Fprintf(w, ListResponse) }) count := 0 diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index 9632d448a5..a2d197ca0e 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -21,7 +21,8 @@ const ListResponse = ` "provider:physical_network": null, "provider:network_type": "local", "router:external": true, - "port_security_enabled": true + "port_security_enabled": true, + "dns_domain": "local." }, { "status": "ACTIVE", @@ -37,7 +38,8 @@ const ListResponse = ` "provider:physical_network": null, "provider:network_type": "local", "router:external": false, - "port_security_enabled": false + "port_security_enabled": false, + "dns_domain": "" } ] }` @@ -58,7 +60,8 @@ const GetResponse = ` "provider:physical_network": null, "provider:network_type": "local", "router:external": true, - "port_security_enabled": true + "port_security_enabled": true, + "dns_domain": "local." } }` @@ -82,7 +85,8 @@ const CreateResponse = ` "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, "provider:physical_network": null, - "provider:network_type": "local" + "provider:network_type": "local", + "dns_domain": "" } }` diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index cfce9b0912..97ad08ac2d 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -21,6 +21,14 @@ const ListResponse = ` ], "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", "security_groups": [], + "dns_name": "test-port", + "dns_assignment": [ + { + "hostname": "test-port", + "ip_address": "172.24.4.2", + "fqdn": "test-port.openstack.local." + } + ], "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824", "port_security_enabled": false } @@ -57,6 +65,14 @@ const GetResponse = ` ], "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", "security_groups": [], + "dns_name": "test-port", + "dns_assignment": [ + { + "hostname": "test-port", + "ip_address": "172.24.4.2", + "fqdn": "test-port.openstack.local." + } + ], "device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e" } } diff --git a/script/acceptancetest b/script/acceptancetest index 486c85bd65..269300138e 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -47,6 +47,7 @@ acceptance/openstack/imageservice/v2 acceptance/openstack/networking/v2 acceptance/openstack/networking/v2/extensions +acceptance/openstack/networking/v2/extensions/dns # Resource not found # acceptance/openstack/networking/v2/extensions/fwaas From 007769a86dc5d6a0fd6365ae181a2cdc165fcb80 Mon Sep 17 00:00:00 2001 From: Chris Moos Date: Tue, 12 Mar 2019 19:05:46 -0700 Subject: [PATCH 0694/2296] Block Storage V3: Add volume image metadata field. (#1503) --- openstack/blockstorage/v3/volumes/results.go | 2 ++ openstack/blockstorage/v3/volumes/testing/fixtures.go | 4 ++++ openstack/blockstorage/v3/volumes/testing/requests_test.go | 1 + 3 files changed, 7 insertions(+) diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index 87f71262c1..3a33b5864b 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -77,6 +77,8 @@ type Volume struct { ConsistencyGroupID string `json:"consistencygroup_id"` // Multiattach denotes if the volume is multi-attach capable. Multiattach bool `json:"multiattach"` + // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. + VolumeImageMetadata map[string]string `json:"volume_image_metadata"` } // UnmarshalJSON another unmarshalling function diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures.go b/openstack/blockstorage/v3/volumes/testing/fixtures.go index 74efb1a256..dca8634c03 100644 --- a/openstack/blockstorage/v3/volumes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumes/testing/fixtures.go @@ -141,6 +141,10 @@ func MockGetResponse(t *testing.T) { "os-vol-mig-status-attr:migstat": null, "metadata": {}, "status": "available", + "volume_image_metadata": { + "container_format": "bare", + "image_name": "centos" + }, "description": null } } diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index 5b9af2c8ad..4745198dd8 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -250,6 +250,7 @@ func TestGetWithExtensions(t *testing.T) { err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) + th.AssertEquals(t, "centos", s.Volume.VolumeImageMetadata["image_name"]) err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) if err == nil { From 8967900e042e71c45e72abde356c4b4b4c1406df Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Tue, 12 Mar 2019 23:18:53 -0400 Subject: [PATCH 0695/2296] baremetal v1: add support for 409 errors (#1498) For #1497 Add ErrDefault409 and convert http.StatusConflict errors to ErrDefault409. Signed-off-by: Doug Hellmann --- errors.go | 11 +++++ .../baremetal/v1/nodes/testing/fixtures.go | 23 +++++++++++ .../v1/nodes/testing/requests_test.go | 41 +++++++++++++++++++ provider_client.go | 5 +++ 4 files changed, 80 insertions(+) diff --git a/errors.go b/errors.go index 4bf1024684..0bcb3af7f0 100644 --- a/errors.go +++ b/errors.go @@ -122,6 +122,11 @@ type ErrDefault408 struct { ErrUnexpectedResponseCode } +// ErrDefault409 is the default error type returned on a 409 HTTP response code. +type ErrDefault409 struct { + ErrUnexpectedResponseCode +} + // ErrDefault429 is the default error type returned on a 429 HTTP response code. type ErrDefault429 struct { ErrUnexpectedResponseCode @@ -211,6 +216,12 @@ type Err408er interface { Error408(ErrUnexpectedResponseCode) error } +// Err409er is the interface resource error types implement to override the error message +// from a 409 error. +type Err409er interface { + Error409(ErrUnexpectedResponseCode) error +} + // Err429er is the interface resource error types implement to override the error message // from a 429 error. type Err429er interface { diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 45700c0b87..65aee61e70 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -910,6 +910,15 @@ func HandleNodeChangeProvisionStateClean(t *testing.T) { }) } +func HandleNodeChangeProvisionStateCleanWithConflict(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateCleanBody) + w.WriteHeader(http.StatusConflict) + }) +} + // HandleChangePowerStateSuccessfully sets up the test server to respond to a change power state request for a node func HandleChangePowerStateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/power", func(w http.ResponseWriter, r *http.Request) { @@ -923,3 +932,17 @@ func HandleChangePowerStateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +// HandleChangePowerStateWithConflict sets up the test server to respond to a change power state request for a node with a 409 error +func HandleChangePowerStateWithConflict(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/power", 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, `{ + "target": "power on", + "timeout": 100 + }`) + + w.WriteHeader(http.StatusConflict) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 9bdb1cb89b..5117cd2513 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -276,6 +276,30 @@ func TestNodeChangeProvisionStateClean(t *testing.T) { th.AssertNoErr(t, err) } +func TestNodeChangeProvisionStateCleanWithConflict(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeChangeProvisionStateCleanWithConflict(t) + + c := client.ServiceClient() + err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + Target: nodes.TargetClean, + CleanSteps: []nodes.CleanStep{ + { + Interface: "deploy", + Step: "upgrade_firmware", + Args: map[string]string{ + "force": "True", + }, + }, + }, + }).ExtractErr() + + if _, ok := err.(gophercloud.ErrDefault409); !ok { + t.Fatal("ErrDefault409 was expected to occur") + } +} + func TestCleanStepRequiresInterface(t *testing.T) { c := client.ServiceClient() err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ @@ -328,3 +352,20 @@ func TestChangePowerState(t *testing.T) { err := nodes.ChangePowerState(c, "1234asdf", opts).ExtractErr() th.AssertNoErr(t, err) } + +func TestChangePowerStateWithConflict(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleChangePowerStateWithConflict(t) + + opts := nodes.PowerStateOpts{ + Target: nodes.PowerOn, + Timeout: 100, + } + + c := client.ServiceClient() + err := nodes.ChangePowerState(c, "1234asdf", opts).ExtractErr() + if _, ok := err.(gophercloud.ErrDefault409); !ok { + t.Fatal("ErrDefault409 was expected to occur") + } +} diff --git a/provider_client.go b/provider_client.go index 88ff64367d..fce00462fd 100644 --- a/provider_client.go +++ b/provider_client.go @@ -443,6 +443,11 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) if error408er, ok := errType.(Err408er); ok { err = error408er.Error408(respErr) } + case http.StatusConflict: + err = ErrDefault409{respErr} + if error409er, ok := errType.(Err409er); ok { + err = error409er.Error409(respErr) + } case 429: err = ErrDefault429{respErr} if error429er, ok := errType.(Err429er); ok { From 32225edcb95e34f926834ae229ccf2d88de83c68 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 14 Mar 2019 04:21:12 +0300 Subject: [PATCH 0696/2296] Compute V2: add 2.3 microversion OS-EXT-SRV-ATTR (#1501) * Compute V2: add 2.3 microversion OS-EXT-SRV-ATTR Add microversions.go to extendedserverattributes extension with support to retrieve the following fields from the servers responses: - OS-EXT-SRV-ATTR:reservation_id - OS-EXT-SRV-ATTR:launch_index - OS-EXT-SRV-ATTR:ramdisk_id - OS-EXT-SRV-ATTR:kernel_id - OS-EXT-SRV-ATTR:hostname - OS-EXT-SRV-ATTR:root_device_name - OS-EXT-SRV-ATTR:userdata Update unit tests and documentation. * Compute V2: add extended attributes acc test Add TestServersWithExtendedAttributesCreateDestroy. --- .../openstack/compute/v2/servers_test.go | 54 ++++++++++++ .../extendedserverattributes/doc.go | 49 ++++++++++- .../extendedserverattributes/microversions.go | 82 +++++++++++++++++++ .../extendedserverattributes/results.go | 15 +--- .../testing/requests_test.go | 21 +++++ 5 files changed, 208 insertions(+), 13 deletions(-) create mode 100644 openstack/compute/v2/extensions/extendedserverattributes/microversions.go diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index ee6f4b9521..caacf4613b 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedstatus" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" @@ -506,3 +507,56 @@ func TestServersTags(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) } + +func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { + clients.RequireLong(t) + clients.RequireAdmin(t) + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + client.Microversion = "2.3" + + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) + + result := servers.Get(client, server.ID) + th.AssertNoErr(t, result.Err) + + reservationID, err := extendedserverattributes.ExtractReservationID(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, reservationID != "", true) + t.Logf("reservationID: %s", reservationID) + + launchIndex, err := extendedserverattributes.ExtractLaunchIndex(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, launchIndex, 0) + t.Logf("launchIndex: %d", launchIndex) + + ramdiskID, err := extendedserverattributes.ExtractRamdiskID(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, ramdiskID == "", true) + t.Logf("ramdiskID: %s", ramdiskID) + + kernelID, err := extendedserverattributes.ExtractKernelID(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, kernelID == "", true) + t.Logf("kernelID: %s", kernelID) + + hostname, err := extendedserverattributes.ExtractHostname(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, hostname != "", true) + t.Logf("hostname: %s", hostname) + + rootDeviceName, err := extendedserverattributes.ExtractRootDeviceName(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, rootDeviceName != "", true) + t.Logf("rootDeviceName: %s", rootDeviceName) + + userData, err := extendedserverattributes.ExtractUserData(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, userData == "", true) + t.Logf("userData: %s", userData) +} diff --git a/openstack/compute/v2/extensions/extendedserverattributes/doc.go b/openstack/compute/v2/extensions/extendedserverattributes/doc.go index 9626f4e186..53e24dbe1b 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/doc.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/doc.go @@ -2,7 +2,7 @@ Package extendedserverattributes provides the ability to extend a server result with the extended usage information. -Example to Get an extended information: +Example to Get basic extended information: type serverAttributesExt struct { servers.Server @@ -16,5 +16,52 @@ Example to Get an extended information: } 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") + + 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) + + 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) + + 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) + + 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/extendedserverattributes/microversions.go b/openstack/compute/v2/extensions/extendedserverattributes/microversions.go new file mode 100644 index 0000000000..709f84d965 --- /dev/null +++ b/openstack/compute/v2/extensions/extendedserverattributes/microversions.go @@ -0,0 +1,82 @@ +package extendedserverattributes + +import ( + "github.com/gophercloud/gophercloud" +) + +// ExtractReservationID will extract the reservation_id attribute. +// This requires the client to be set to microversion 2.3 or later. +func ExtractReservationID(r gophercloud.Result) (string, error) { + var s struct { + ReservationID string `json:"OS-EXT-SRV-ATTR:reservation_id"` + } + err := r.ExtractIntoStructPtr(&s, "server") + + return s.ReservationID, err +} + +// ExtractLaunchIndex will extract the launch_index attribute. +// This requires the client to be set to microversion 2.3 or later. +func ExtractLaunchIndex(r gophercloud.Result) (int, error) { + var s struct { + LaunchIndex int `json:"OS-EXT-SRV-ATTR:launch_index"` + } + err := r.ExtractIntoStructPtr(&s, "server") + + return s.LaunchIndex, err +} + +// ExtractRamdiskID will extract the ramdisk_id attribute. +// This requires the client to be set to microversion 2.3 or later. +func ExtractRamdiskID(r gophercloud.Result) (string, error) { + var s struct { + RamdiskID string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` + } + err := r.ExtractIntoStructPtr(&s, "server") + + return s.RamdiskID, err +} + +// ExtractKernelID will extract the kernel_id attribute. +// This requires the client to be set to microversion 2.3 or later. +func ExtractKernelID(r gophercloud.Result) (string, error) { + var s struct { + KernelID string `json:"OS-EXT-SRV-ATTR:kernel_id"` + } + err := r.ExtractIntoStructPtr(&s, "server") + + return s.KernelID, err +} + +// ExtractHostname will extract the hostname attribute. +// This requires the client to be set to microversion 2.3 or later. +func ExtractHostname(r gophercloud.Result) (string, error) { + var s struct { + Hostname string `json:"OS-EXT-SRV-ATTR:hostname"` + } + err := r.ExtractIntoStructPtr(&s, "server") + + return s.Hostname, err +} + +// ExtractRootDeviceName will extract the root_device_name attribute. +// This requires the client to be set to microversion 2.3 or later. +func ExtractRootDeviceName(r gophercloud.Result) (string, error) { + var s struct { + RootDeviceName string `json:"OS-EXT-SRV-ATTR:root_device_name"` + } + err := r.ExtractIntoStructPtr(&s, "server") + + return s.RootDeviceName, err +} + +// ExtractUserData will extract the userdata attribute. +// This requires the client to be set to microversion 2.3 or later. +func ExtractUserData(r gophercloud.Result) (string, error) { + var s struct { + Userdata string `json:"OS-EXT-SRV-ATTR:userdata"` + } + err := r.ExtractIntoStructPtr(&s, "server") + + return s.Userdata, err +} diff --git a/openstack/compute/v2/extensions/extendedserverattributes/results.go b/openstack/compute/v2/extensions/extendedserverattributes/results.go index abdf7a8b76..6c2ffc9818 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/results.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/results.go @@ -1,17 +1,8 @@ package extendedserverattributes -// ServerAttributesExt represents OS-EXT-SRV-ATTR server response fields. -// -// Following fields will be added after implementing full API microversion -// support in the Gophercloud: -// -// - OS-EXT-SRV-ATTR:reservation_id" -// - OS-EXT-SRV-ATTR:launch_index" -// - OS-EXT-SRV-ATTR:hostname" -// - OS-EXT-SRV-ATTR:kernel_id" -// - OS-EXT-SRV-ATTR:ramdisk_id" -// - OS-EXT-SRV-ATTR:root_device_name" -// - OS-EXT-SRV-ATTR:user_data" +// ServerAttributesExt represents basic OS-EXT-SRV-ATTR server response fields. +// You should use extract methods from microversions.go to retrieve additional +// fields. type ServerAttributesExt struct { Host string `json:"OS-EXT-SRV-ATTR:host"` InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go index 0fa2b3d0d6..360d295b42 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go @@ -28,10 +28,31 @@ func TestServerWithUsageExt(t *testing.T) { extendedserverattributes.ServerAttributesExt } var serverWithAttributesExt serverAttributesExt + + result := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a") + + // Extract basic fields. err := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) th.AssertNoErr(t, err) + // Extract additional fields. + reservationID, err := extendedserverattributes.ExtractReservationID(result.Result) + th.AssertNoErr(t, err) + + launchIndex, err := extendedserverattributes.ExtractLaunchIndex(result.Result) + th.AssertNoErr(t, err) + + hostname, err := extendedserverattributes.ExtractHostname(result.Result) + th.AssertNoErr(t, err) + + rootDeviceName, err := extendedserverattributes.ExtractRootDeviceName(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, serverWithAttributesExt.Host, "compute01") th.AssertEquals(t, serverWithAttributesExt.InstanceName, "instance-00000001") th.AssertEquals(t, serverWithAttributesExt.HypervisorHostname, "compute01") + th.AssertEquals(t, reservationID, "r-ky9gim1l") + th.AssertEquals(t, launchIndex, 0) + th.AssertEquals(t, hostname, "test00") + th.AssertEquals(t, rootDeviceName, "/dev/sda") } From 98f06626886d5bfeb1592e180ef27e536103091c Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 14 Mar 2019 04:23:13 +0300 Subject: [PATCH 0697/2296] Compute V2: add support for service string UUID (#1500) * Compute V2: add support for hypervisor string UUID This commit adds support for the Compute API 2.53 microversion hypervisors implementation by adding string UUIDs in the basic and uptime hypervisor result structure. It also updates unit and acceptance tests and extends the hypervisor package documentation. * Compute V2: print unexpected type in hypervisor We need to show what kind of unexpected type we have in UnmarshalJSON method. * Compute V2: add UUID for hyperviors services This commit adds support for hypervisors services UUID string representation and updates unit tests. * Compute V2: add support for service string UUID This commit adds suport Compute API 2.53 microversion services implementation by adding string UUIDs in the basic service result structure. It also updates unit tests. * Compute V2: update hypervisors extension docs Update comments for retrieving a single hypervisor and uptime data. --- .../openstack/compute/v2/hypervisors_test.go | 4 +- .../compute/v2/extensions/hypervisors/doc.go | 27 ++- .../v2/extensions/hypervisors/requests.go | 12 +- .../v2/extensions/hypervisors/results.go | 87 ++++++- .../hypervisors/testing/fixtures.go | 147 +++++++++++- .../hypervisors/testing/requests_test.go | 43 ++++ .../compute/v2/extensions/services/results.go | 18 +- .../extensions/services/testing/fixtures.go | 212 ++++++++++++++---- .../services/testing/requests_test.go | 32 +++ 9 files changed, 504 insertions(+), 78 deletions(-) diff --git a/acceptance/openstack/compute/v2/hypervisors_test.go b/acceptance/openstack/compute/v2/hypervisors_test.go index 29d49a277e..0b57cc5758 100644 --- a/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/acceptance/openstack/compute/v2/hypervisors_test.go @@ -80,7 +80,7 @@ func TestHypervisorsGetUptime(t *testing.T) { th.AssertEquals(t, hypervisorID, hypervisor.ID) } -func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (int, error) { +func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { allPages, err := hypervisors.List(client).AllPages() th.AssertNoErr(t, err) @@ -91,5 +91,5 @@ func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (int, erro return allHypervisors[0].ID, nil } - return 0, fmt.Errorf("Unable to get hypervisor ID") + return "", fmt.Errorf("Unable to get hypervisor ID") } diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index b8eb699edb..dc767de371 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -4,14 +4,24 @@ and shows summary statistics for all hypervisors over all compute nodes in the O Example of Show Hypervisor Details - hypervisorID := 42 - hypervisor, err := hypervisors.Get(computeClient, 42).Extract() + hypervisorID := "42" + hypervisor, err := hypervisors.Get(computeClient, hypervisorID).Extract() if err != nil { panic(err) } fmt.Printf("%+v\n", hypervisor) +Example of Show Hypervisor Details with Compute API microversion greater that 2.53 + + hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" + hypervisor, err := hypervisors.Get(computeClient, hypervisorID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", hypervisor) + Example of Retrieving Details of All Hypervisors allPages, err := hypervisors.List(computeClient).AllPages() @@ -28,7 +38,7 @@ Example of Retrieving Details of All Hypervisors fmt.Printf("%+v\n", hypervisor) } -Example of Show Hypervisor Statistics +Example of Show Hypervisors Statistics hypervisorsStatistics, err := hypervisors.GetStatistics(computeClient).Extract() if err != nil { @@ -39,7 +49,7 @@ Example of Show Hypervisor Statistics Example of Show Hypervisor Uptime - hypervisorID := 42 + hypervisorID := "42" hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() if err != nil { panic(err) @@ -47,5 +57,14 @@ Example of Show Hypervisor Uptime fmt.Printf("%+v\n", hypervisorUptime) +Example of Show Hypervisor Uptime with Compute API microversion greater that 2.53 + + hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" + hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", hypervisorUptime) */ package hypervisors diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/extensions/hypervisors/requests.go index b6f1c541cb..db16543e22 100644 --- a/openstack/compute/v2/extensions/hypervisors/requests.go +++ b/openstack/compute/v2/extensions/hypervisors/requests.go @@ -1,8 +1,6 @@ package hypervisors import ( - "strconv" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -23,18 +21,16 @@ func GetStatistics(client *gophercloud.ServiceClient) (r StatisticsResult) { } // Get makes a request against the API to get details for specific hypervisor. -func Get(client *gophercloud.ServiceClient, hypervisorID int) (r HypervisorResult) { - v := strconv.Itoa(hypervisorID) - _, r.Err = client.Get(hypervisorsGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ +func Get(client *gophercloud.ServiceClient, hypervisorID string) (r HypervisorResult) { + _, r.Err = client.Get(hypervisorsGetURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // GetUptime makes a request against the API to get uptime for specific hypervisor. -func GetUptime(client *gophercloud.ServiceClient, hypervisorID int) (r UptimeResult) { - v := strconv.Itoa(hypervisorID) - _, r.Err = client.Get(hypervisorsUptimeURL(client, v), &r.Body, &gophercloud.RequestOpts{ +func GetUptime(client *gophercloud.ServiceClient, hypervisorID string) (r UptimeResult) { + _, r.Err = client.Get(hypervisorsUptimeURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index 7f3fafe1aa..7136da784b 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -3,6 +3,7 @@ package hypervisors import ( "encoding/json" "fmt" + "strconv" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" @@ -27,10 +28,40 @@ type CPUInfo struct { // Service represents a Compute service running on the hypervisor. type Service struct { Host string `json:"host"` - ID int `json:"id"` + ID string `json:"-"` DisabledReason string `json:"disabled_reason"` } +func (r *Service) UnmarshalJSON(b []byte) error { + type tmp Service + var s struct { + tmp + ID interface{} `json:"id"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = Service(s.tmp) + + // OpenStack Compute service returns ID in string representation since + // 2.53 microversion API (Pike release). + switch t := s.ID.(type) { + case int: + r.ID = strconv.Itoa(t) + case float64: + r.ID = strconv.Itoa(int(t)) + case string: + r.ID = t + default: + return fmt.Errorf("ID has unexpected type: %T", t) + } + + return nil +} + // Hypervisor represents a hypervisor in the OpenStack cloud. type Hypervisor struct { // A structure that contains cpu information like arch, model, vendor, @@ -72,7 +103,7 @@ type Hypervisor struct { HypervisorVersion int `json:"-"` // ID is the unique ID of the hypervisor. - ID int `json:"id"` + ID string `json:"-"` // LocalGB is the disk space in the hypervisor, measured in GB. LocalGB int `json:"-"` @@ -103,6 +134,7 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { type tmp Hypervisor var s struct { tmp + ID interface{} `json:"id"` CPUInfo interface{} `json:"cpu_info"` HypervisorVersion interface{} `json:"hypervisor_version"` FreeDiskGB interface{} `json:"free_disk_gb"` @@ -146,7 +178,7 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { case float64: r.HypervisorVersion = int(t) default: - return fmt.Errorf("Hypervisor version of unexpected type") + return fmt.Errorf("Hypervisor version has unexpected type: %T", t) } switch t := s.FreeDiskGB.(type) { @@ -155,7 +187,7 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { case float64: r.FreeDiskGB = int(t) default: - return fmt.Errorf("Free disk GB of unexpected type") + return fmt.Errorf("Free disk GB has unexpected type: %T", t) } switch t := s.LocalGB.(type) { @@ -164,7 +196,20 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { case float64: r.LocalGB = int(t) default: - return fmt.Errorf("Local GB of unexpected type") + return fmt.Errorf("Local GB has unexpected type: %T", t) + } + + // OpenStack Compute service returns ID in string representation since + // 2.53 microversion API (Pike release). + switch t := s.ID.(type) { + case int: + r.ID = strconv.Itoa(t) + case float64: + r.ID = strconv.Itoa(int(t)) + case string: + r.ID = t + default: + return fmt.Errorf("ID has unexpected type: %T", t) } return nil @@ -264,7 +309,7 @@ type Uptime struct { HypervisorHostname string `json:"hypervisor_hostname"` // The id of the hypervisor. - ID int `json:"id"` + ID string `json:"-"` // The state of the hypervisor. One of up or down. State string `json:"state"` @@ -276,6 +321,36 @@ type Uptime struct { Uptime string `json:"uptime"` } +func (r *Uptime) UnmarshalJSON(b []byte) error { + type tmp Uptime + var s struct { + tmp + ID interface{} `json:"id"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = Uptime(s.tmp) + + // OpenStack Compute service returns ID in string representation since + // 2.53 microversion API (Pike release). + switch t := s.ID.(type) { + case int: + r.ID = strconv.Itoa(t) + case float64: + r.ID = strconv.Itoa(int(t)) + case string: + r.ID = t + default: + return fmt.Errorf("ID has unexpected type: %T", t) + } + + return nil +} + type UptimeResult struct { gophercloud.Result } diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go index 1dc05fb9b0..c1d0dd8970 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go @@ -3,7 +3,6 @@ package testing import ( "fmt" "net/http" - "strconv" "testing" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" @@ -11,9 +10,11 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +// HypervisorListBodyPre253 represents a raw hypervisor list from the Compute +// API with microversion older than 2.53. // The first hypervisor represents what the specification says (~Newton) // The second is exactly the same, but what you can get off a real system (~Kilo) -const HypervisorListBody = ` +const HypervisorListBodyPre253 = ` { "hypervisors": [ { @@ -84,6 +85,78 @@ const HypervisorListBody = ` ] }` +// HypervisorListBody represents a raw hypervisor list result with Pike+ release. +const HypervisorListBody = ` +{ + "hypervisors": [ + { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "current_workload": 0, + "status": "enabled", + "state": "up", + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 2002000, + "id": "c48f6247-abe4-4a24-824e-ea39e108874f", + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "e6a37ee802d74863ab8b91ade8f12a67", + "id": "9c2566e7-7a54-4777-a1ae-c2662f0c407c", + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + }, + { + "cpu_info": "{\"arch\": \"x86_64\", \"model\": \"Nehalem\", \"vendor\": \"Intel\", \"features\": [\"pge\", \"clflush\"], \"topology\": {\"cores\": 1, \"threads\": 1, \"sockets\": 4}}", + "current_workload": 0, + "status": "enabled", + "state": "up", + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 2.002e+06, + "id": "c48f6247-abe4-4a24-824e-ea39e108874f", + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "e6a37ee802d74863ab8b91ade8f12a67", + "id": "9c2566e7-7a54-4777-a1ae-c2662f0c407c", + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } + ] +}` + const HypervisorsStatisticsBody = ` { "hypervisor_statistics": { @@ -103,6 +176,7 @@ const HypervisorsStatisticsBody = ` } ` +// HypervisorGetBody represents a raw hypervisor GET result with Pike+ release. const HypervisorGetBody = ` { "hypervisor":{ @@ -130,7 +204,7 @@ const HypervisorGetBody = ` "hypervisor_hostname":"fake-mini", "hypervisor_type":"fake", "hypervisor_version":2002000, - "id":1, + "id":"c48f6247-abe4-4a24-824e-ea39e108874f", "local_gb":1028, "local_gb_used":0, "memory_mb":8192, @@ -138,7 +212,7 @@ const HypervisorGetBody = ` "running_vms":0, "service":{ "host":"e6a37ee802d74863ab8b91ade8f12a67", - "id":2, + "id":"9c2566e7-7a54-4777-a1ae-c2662f0c407c", "disabled_reason":null }, "vcpus":1, @@ -147,11 +221,13 @@ const HypervisorGetBody = ` } ` +// HypervisorUptimeBody represents a raw hypervisor uptime request result with +// Pike+ release. const HypervisorUptimeBody = ` { "hypervisor": { "hypervisor_hostname": "fake-mini", - "id": 1, + "id": "c48f6247-abe4-4a24-824e-ea39e108874f", "state": "up", "status": "enabled", "uptime": " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14" @@ -160,6 +236,45 @@ const HypervisorUptimeBody = ` ` var ( + HypervisorFakePre253 = hypervisors.Hypervisor{ + CPUInfo: hypervisors.CPUInfo{ + Arch: "x86_64", + Model: "Nehalem", + Vendor: "Intel", + Features: []string{ + "pge", + "clflush", + }, + Topology: hypervisors.Topology{ + Cores: 1, + Threads: 1, + Sockets: 4, + }, + }, + CurrentWorkload: 0, + Status: "enabled", + State: "up", + DiskAvailableLeast: 0, + HostIP: "1.1.1.1", + FreeDiskGB: 1028, + FreeRamMB: 7680, + HypervisorHostname: "fake-mini", + HypervisorType: "fake", + HypervisorVersion: 2002000, + ID: "1", + LocalGB: 1028, + LocalGBUsed: 0, + MemoryMB: 8192, + MemoryMBUsed: 512, + RunningVMs: 0, + Service: hypervisors.Service{ + Host: "e6a37ee802d74863ab8b91ade8f12a67", + ID: "2", + DisabledReason: "", + }, + VCPUs: 1, + VCPUsUsed: 0, + } HypervisorFake = hypervisors.Hypervisor{ CPUInfo: hypervisors.CPUInfo{ Arch: "x86_64", @@ -185,7 +300,7 @@ var ( HypervisorHostname: "fake-mini", HypervisorType: "fake", HypervisorVersion: 2002000, - ID: 1, + ID: "c48f6247-abe4-4a24-824e-ea39e108874f", LocalGB: 1028, LocalGBUsed: 0, MemoryMB: 8192, @@ -193,7 +308,7 @@ var ( RunningVMs: 0, Service: hypervisors.Service{ Host: "e6a37ee802d74863ab8b91ade8f12a67", - ID: 2, + ID: "9c2566e7-7a54-4777-a1ae-c2662f0c407c", DisabledReason: "", }, VCPUs: 1, @@ -215,7 +330,7 @@ var ( } HypervisorUptimeExpected = hypervisors.Uptime{ HypervisorHostname: "fake-mini", - ID: 1, + ID: "c48f6247-abe4-4a24-824e-ea39e108874f", State: "up", Status: "enabled", Uptime: " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14", @@ -232,6 +347,16 @@ func HandleHypervisorsStatisticsSuccessfully(t *testing.T) { }) } +func HandleHypervisorListPre253Successfully(t *testing.T) { + testhelper.Mux.HandleFunc("/os-hypervisors/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") + fmt.Fprintf(w, HypervisorListBodyPre253) + }) +} + func HandleHypervisorListSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") @@ -243,8 +368,7 @@ func HandleHypervisorListSuccessfully(t *testing.T) { } func HandleHypervisorGetSuccessfully(t *testing.T) { - v := strconv.Itoa(HypervisorFake.ID) - testhelper.Mux.HandleFunc("/os-hypervisors/"+v, func(w http.ResponseWriter, r *http.Request) { + testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) @@ -254,8 +378,7 @@ func HandleHypervisorGetSuccessfully(t *testing.T) { } func HandleHypervisorUptimeSuccessfully(t *testing.T) { - v := strconv.Itoa(HypervisorFake.ID) - testhelper.Mux.HandleFunc("/os-hypervisors/"+v+"/uptime", func(w http.ResponseWriter, r *http.Request) { + testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID+"/uptime", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go index 95f9636c0c..417b05f200 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go @@ -9,6 +9,49 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestListHypervisorsPre253(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleHypervisorListPre253Successfully(t) + + pages := 0 + err := hypervisors.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := hypervisors.ExtractHypervisors(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 hypervisors, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[0]) + testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[1]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllHypervisorsPre253(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleHypervisorListPre253Successfully(t) + + allPages, err := hypervisors.List(client.ServiceClient()).AllPages() + testhelper.AssertNoErr(t, err) + actual, err := hypervisors.ExtractHypervisors(allPages) + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[0]) + testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[1]) +} + func TestListHypervisors(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() diff --git a/openstack/compute/v2/extensions/services/results.go b/openstack/compute/v2/extensions/services/results.go index 1ffc99cf9d..df80fc2a3b 100644 --- a/openstack/compute/v2/extensions/services/results.go +++ b/openstack/compute/v2/extensions/services/results.go @@ -2,6 +2,8 @@ package services import ( "encoding/json" + "fmt" + "strconv" "time" "github.com/gophercloud/gophercloud" @@ -20,7 +22,7 @@ type Service struct { Host string `json:"host"` // The id of the service. - ID int `json:"id"` + ID string `json:"-"` // The state of the service. One of up or down. State string `json:"state"` @@ -40,6 +42,7 @@ func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp + ID interface{} `json:"id"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) @@ -50,6 +53,19 @@ func (r *Service) UnmarshalJSON(b []byte) error { r.UpdatedAt = time.Time(s.UpdatedAt) + // OpenStack Compute service returns ID in string representation since + // 2.53 microversion API (Pike release). + switch t := s.ID.(type) { + case int: + r.ID = strconv.Itoa(t) + case float64: + r.ID = strconv.Itoa(int(t)) + case string: + r.ID = t + default: + return fmt.Errorf("ID has unexpected type: %T", t) + } + return nil } diff --git a/openstack/compute/v2/extensions/services/testing/fixtures.go b/openstack/compute/v2/extensions/services/testing/fixtures.go index 79e704a7a7..9f149dace5 100644 --- a/openstack/compute/v2/extensions/services/testing/fixtures.go +++ b/openstack/compute/v2/extensions/services/testing/fixtures.go @@ -11,8 +11,9 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) -// ServiceListBody is sample response to the List call -const ServiceListBody = ` +// ServiceListBodyPre253 represents a raw service list from the Compute API +// with microversion older than 2.53. +const ServiceListBodyPre253 = ` { "services": [ { @@ -63,55 +64,176 @@ const ServiceListBody = ` } ` -// First service from the ServiceListBody -var FirstFakeService = services.Service{ - Binary: "nova-scheduler", - DisabledReason: "test1", - Host: "host1", - ID: 1, - State: "up", - Status: "disabled", - UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), - Zone: "internal", -} +var ( + // FirstFakeServicePre253 represents the first service from the + // ServiceListBodyPre253. + FirstFakeServicePre253 = services.Service{ + Binary: "nova-scheduler", + DisabledReason: "test1", + Host: "host1", + ID: "1", + State: "up", + Status: "disabled", + UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), + Zone: "internal", + } -// Second service from the ServiceListBody -var SecondFakeService = services.Service{ - Binary: "nova-compute", - DisabledReason: "test2", - Host: "host1", - ID: 2, - State: "up", - Status: "disabled", - UpdatedAt: time.Date(2012, 10, 29, 13, 42, 5, 0, time.UTC), - Zone: "nova", -} + // SecondFakeServicePre253 represents the second service from the + // ServiceListBodyPre253. + SecondFakeServicePre253 = services.Service{ + Binary: "nova-compute", + DisabledReason: "test2", + Host: "host1", + ID: "2", + State: "up", + Status: "disabled", + UpdatedAt: time.Date(2012, 10, 29, 13, 42, 5, 0, time.UTC), + Zone: "nova", + } + + // ThirdFakeServicePre253 represents the third service from the + // ServiceListBodyPre253. + ThirdFakeServicePre253 = services.Service{ + Binary: "nova-scheduler", + DisabledReason: "", + Host: "host2", + ID: "3", + State: "down", + Status: "enabled", + UpdatedAt: time.Date(2012, 9, 19, 6, 55, 34, 0, time.UTC), + Zone: "internal", + } -// Third service from the ServiceListBody -var ThirdFakeService = services.Service{ - Binary: "nova-scheduler", - DisabledReason: "", - Host: "host2", - ID: 3, - State: "down", - Status: "enabled", - UpdatedAt: time.Date(2012, 9, 19, 6, 55, 34, 0, time.UTC), - Zone: "internal", + // FourthFakeServicePre253 represents the fourth service from the + // ServiceListBodyPre253. + FourthFakeServicePre253 = services.Service{ + Binary: "nova-compute", + DisabledReason: "test4", + Host: "host2", + ID: "4", + State: "down", + Status: "disabled", + UpdatedAt: time.Date(2012, 9, 18, 8, 3, 38, 0, time.UTC), + Zone: "nova", + } +) + +// ServiceListBody represents a raw service list result with Pike+ release. +const ServiceListBody = ` +{ + "services": [ + { + "id": "4c720fa0-02c3-4834-8279-9eecf9edb6cb", + "binary": "nova-scheduler", + "disabled_reason": "test1", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:02.000000", + "forced_down": false, + "zone": "internal" + }, + { + "id": "1fdfec3e-ee03-4e36-b99b-71cf2967b70c", + "binary": "nova-compute", + "disabled_reason": "test2", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:05.000000", + "forced_down": false, + "zone": "nova" + }, + { + "id": "bd0b2e30-809e-4160-bd3d-f23ca30e9b68", + "binary": "nova-scheduler", + "disabled_reason": null, + "host": "host2", + "state": "down", + "status": "enabled", + "updated_at": "2012-09-19T06:55:34.000000", + "forced_down": false, + "zone": "internal" + }, + { + "id": "fe41c476-33e2-4ac3-ad21-3ffaf1b9c644", + "binary": "nova-compute", + "disabled_reason": "test4", + "host": "host2", + "state": "down", + "status": "disabled", + "updated_at": "2012-09-18T08:03:38.000000", + "forced_down": false, + "zone": "nova" + } + ] } +` -// Fourth service from the ServiceListBody -var FourthFakeService = services.Service{ - Binary: "nova-compute", - DisabledReason: "test4", - Host: "host2", - ID: 4, - State: "down", - Status: "disabled", - UpdatedAt: time.Date(2012, 9, 18, 8, 3, 38, 0, time.UTC), - Zone: "nova", +var ( + // FirstFakeService represents the first service from the ServiceListBody. + FirstFakeService = services.Service{ + Binary: "nova-scheduler", + DisabledReason: "test1", + Host: "host1", + ID: "4c720fa0-02c3-4834-8279-9eecf9edb6cb", + State: "up", + Status: "disabled", + UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), + Zone: "internal", + } + + // SecondFakeService represents the second service from the ServiceListBody. + SecondFakeService = services.Service{ + Binary: "nova-compute", + DisabledReason: "test2", + Host: "host1", + ID: "1fdfec3e-ee03-4e36-b99b-71cf2967b70c", + State: "up", + Status: "disabled", + UpdatedAt: time.Date(2012, 10, 29, 13, 42, 5, 0, time.UTC), + Zone: "nova", + } + + // ThirdFakeService represents the third service from the ServiceListBody. + ThirdFakeService = services.Service{ + Binary: "nova-scheduler", + DisabledReason: "", + Host: "host2", + ID: "bd0b2e30-809e-4160-bd3d-f23ca30e9b68", + State: "down", + Status: "enabled", + UpdatedAt: time.Date(2012, 9, 19, 6, 55, 34, 0, time.UTC), + Zone: "internal", + } + + // FourthFakeService represents the fourth service from the ServiceListBody. + FourthFakeService = services.Service{ + Binary: "nova-compute", + DisabledReason: "test4", + Host: "host2", + ID: "fe41c476-33e2-4ac3-ad21-3ffaf1b9c644", + State: "down", + Status: "disabled", + UpdatedAt: time.Date(2012, 9, 18, 8, 3, 38, 0, time.UTC), + Zone: "nova", + } +) + +// HandleListPre253Successfully configures the test server to respond to a List +// request to a Compute server API pre 2.53 microversion release. +func HandleListPre253Successfully(t *testing.T) { + th.Mux.HandleFunc("/os-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, ServiceListBodyPre253) + }) } -// HandleListSuccessfully configures the test server to respond to a List request. +// HandleListSuccessfully configures the test server to respond to a List +// request to a Compute server with Pike+ release. func HandleListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-services", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") diff --git a/openstack/compute/v2/extensions/services/testing/requests_test.go b/openstack/compute/v2/extensions/services/testing/requests_test.go index 7f998814be..b8972c9bc3 100644 --- a/openstack/compute/v2/extensions/services/testing/requests_test.go +++ b/openstack/compute/v2/extensions/services/testing/requests_test.go @@ -9,6 +9,38 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestListServicesPre253(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleListPre253Successfully(t) + + pages := 0 + err := services.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := services.ExtractServices(page) + if err != nil { + return false, err + } + + if len(actual) != 4 { + t.Fatalf("Expected 4 services, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, FirstFakeServicePre253, actual[0]) + testhelper.CheckDeepEquals(t, SecondFakeServicePre253, actual[1]) + testhelper.CheckDeepEquals(t, ThirdFakeServicePre253, actual[2]) + testhelper.CheckDeepEquals(t, FourthFakeServicePre253, actual[3]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + func TestListServices(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() From 737db56db180519300c1b7b326e40d431de10206 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 Mar 2019 19:24:14 -0600 Subject: [PATCH 0698/2296] Update doc.go --- openstack/compute/v2/extensions/hypervisors/doc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index dc767de371..8f7d773e74 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -12,7 +12,7 @@ Example of Show Hypervisor Details fmt.Printf("%+v\n", hypervisor) -Example of Show Hypervisor Details with Compute API microversion greater that 2.53 +Example of Show Hypervisor Details with Compute API microversion greater than 2.53 hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" hypervisor, err := hypervisors.Get(computeClient, hypervisorID).Extract() @@ -57,7 +57,7 @@ Example of Show Hypervisor Uptime fmt.Printf("%+v\n", hypervisorUptime) -Example of Show Hypervisor Uptime with Compute API microversion greater that 2.53 +Example of Show Hypervisor Uptime with Compute API microversion greater than 2.53 hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() From 6e3895ed427a63be0fe60dcb8a31e564495ee6e9 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 14 Mar 2019 09:57:36 +0300 Subject: [PATCH 0699/2296] Imageservice V2: add Create to imageimport (#1167) * Imageservice V2: add Create to imageimport Implement Create method that allows to import image data to the existing image. Add unit and acceptance tests with documentation. * Imageservice V2: fix imageimport tests and docs Remove EOF checks in the imageimport tests and documentation. Fix documentation formatting. * Imageservice V2: fix empty body errors Remove body object from the Create reques in imageimport package to prevent EOF errors. --- .../imageservice/v2/imageimport_test.go | 12 +++++++ .../openstack/imageservice/v2/imageservice.go | 11 +++++++ openstack/imageservice/v2/imageimport/doc.go | 23 ++++++++++--- .../imageservice/v2/imageimport/requests.go | 33 +++++++++++++++++++ .../imageservice/v2/imageimport/results.go | 6 ++++ .../v2/imageimport/testing/fixtures.go | 10 ++++++ .../v2/imageimport/testing/requests_test.go | 22 +++++++++++++ openstack/imageservice/v2/imageimport/urls.go | 5 +++ 8 files changed, 117 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/imageservice/v2/imageimport_test.go b/acceptance/openstack/imageservice/v2/imageimport_test.go index 6d74604676..e3f95d6c9a 100644 --- a/acceptance/openstack/imageservice/v2/imageimport_test.go +++ b/acceptance/openstack/imageservice/v2/imageimport_test.go @@ -19,3 +19,15 @@ func TestGetImportInfo(t *testing.T) { tools.PrintResource(t, importInfo) } + +func TestCreateImport(t *testing.T) { + client, err := clients.NewImageServiceV2Client() + th.AssertNoErr(t, err) + + image, err := CreateEmptyImage(t, client) + th.AssertNoErr(t, err) + defer DeleteImage(t, client, image) + + err = ImportImage(t, client, image.ID) + th.AssertNoErr(t, err) +} diff --git a/acceptance/openstack/imageservice/v2/imageservice.go b/acceptance/openstack/imageservice/v2/imageservice.go index e8f153d2e0..fd55741aa6 100644 --- a/acceptance/openstack/imageservice/v2/imageservice.go +++ b/acceptance/openstack/imageservice/v2/imageservice.go @@ -157,3 +157,14 @@ func DeleteImageFile(t *testing.T, filepath string) { t.Logf("Successfully deleted image file %s", filepath) } + +// ImportImage will import image data from the remote source to the Imageservice. +func ImportImage(t *testing.T, client *gophercloud.ServiceClient, imageID string) error { + importOpts := imageimport.CreateOpts{ + Name: imageimport.WebDownloadMethod, + URI: ImportImageURL, + } + + t.Logf("Attempting to import image data for %s from %s", imageID, importOpts.URI) + return imageimport.Create(client, imageID, importOpts).ExtractErr() +} diff --git a/openstack/imageservice/v2/imageimport/doc.go b/openstack/imageservice/v2/imageimport/doc.go index 6862f3172c..16f8ce32f5 100644 --- a/openstack/imageservice/v2/imageimport/doc.go +++ b/openstack/imageservice/v2/imageimport/doc.go @@ -4,11 +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 + + opts := 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, opts).ExtractErr() + if err != nil { + panic(err) + } */ package imageimport diff --git a/openstack/imageservice/v2/imageimport/requests.go b/openstack/imageservice/v2/imageimport/requests.go index b374b0e47c..38920051b3 100644 --- a/openstack/imageservice/v2/imageimport/requests.go +++ b/openstack/imageservice/v2/imageimport/requests.go @@ -18,3 +18,36 @@ func Get(c *gophercloud.ServiceClient) (r GetResult) { _, r.Err = c.Get(infoURL(c), &r.Body, nil) return } + +// CreateOptsBuilder allows to add additional parameters to the Create request. +type CreateOptsBuilder interface { + ToImportCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters of a new image import. +type CreateOpts struct { + Name ImportMethod `json:"name"` + URI string `json:"uri"` +} + +// ToImportCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToImportCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return map[string]interface{}{"method": b}, nil +} + +// Create requests the creation of a new image import on the server. +func Create(client *gophercloud.ServiceClient, imageID string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToImportCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(importURL(client, imageID), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} diff --git a/openstack/imageservice/v2/imageimport/results.go b/openstack/imageservice/v2/imageimport/results.go index c579c7b156..2158c20da6 100644 --- a/openstack/imageservice/v2/imageimport/results.go +++ b/openstack/imageservice/v2/imageimport/results.go @@ -12,6 +12,12 @@ type GetResult struct { commonResult } +// CreateResult is the result of import Create operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type CreateResult struct { + gophercloud.ErrResult +} + // ImportInfo represents information data for the Import API. type ImportInfo struct { ImportMethods ImportMethods `json:"import-methods"` diff --git a/openstack/imageservice/v2/imageimport/testing/fixtures.go b/openstack/imageservice/v2/imageimport/testing/fixtures.go index 7cef5cff06..f934d45762 100644 --- a/openstack/imageservice/v2/imageimport/testing/fixtures.go +++ b/openstack/imageservice/v2/imageimport/testing/fixtures.go @@ -13,3 +13,13 @@ const ImportGetResult = ` } } ` + +// ImportCreateRequest represents a request to create image import. +const ImportCreateRequest = ` +{ + "method": { + "name": "web-download", + "uri": "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img" + } +} +` diff --git a/openstack/imageservice/v2/imageimport/testing/requests_test.go b/openstack/imageservice/v2/imageimport/testing/requests_test.go index 01434bd622..d0c98df698 100644 --- a/openstack/imageservice/v2/imageimport/testing/requests_test.go +++ b/openstack/imageservice/v2/imageimport/testing/requests_test.go @@ -36,3 +36,25 @@ func TestGet(t *testing.T) { th.AssertEquals(t, s.ImportMethods.Type, "array") th.AssertDeepEquals(t, s.ImportMethods.Value, validImportMethods) } + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/import", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) + th.TestJSONRequest(t, r, ImportCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + fmt.Fprintf(w, `{}`) + }) + + opts := imageimport.CreateOpts{ + Name: imageimport.WebDownloadMethod, + URI: "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", + } + err := imageimport.Create(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", opts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/imageservice/v2/imageimport/urls.go b/openstack/imageservice/v2/imageimport/urls.go index 6ec37669b3..20310eb093 100644 --- a/openstack/imageservice/v2/imageimport/urls.go +++ b/openstack/imageservice/v2/imageimport/urls.go @@ -3,6 +3,7 @@ package imageimport import "github.com/gophercloud/gophercloud" const ( + rootPath = "images" infoPath = "info" resourcePath = "import" ) @@ -10,3 +11,7 @@ const ( func infoURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(infoPath, resourcePath) } + +func importURL(c *gophercloud.ServiceClient, imageID string) string { + return c.ServiceURL(rootPath, imageID, resourcePath) +} From 954aa14363ced787c28efcfcd15ae6945eb862fb Mon Sep 17 00:00:00 2001 From: Riccardo Pittau Date: Fri, 15 Mar 2019 03:04:55 +0100 Subject: [PATCH 0700/2296] Baremetal Inspector Client Node Introspection (#1486) For #1485 This patch adds the Node Introspection API calls according to [1] Start Introspection (POST) Get Introspection status (GET) List all Introspection statuses (GET) Abort Introspection (POST) Get Introspection Data (GET) Reapply Introspection on stored data (POST) Source code reference: https://github.com/openstack/ironic-inspector/tree/master/ironic_inspector [1] https://developer.openstack.org/api-ref/baremetal-introspection/#node-introspection --- acceptance/clients/clients.go | 21 + .../v1/introspection/doc.go | 40 ++ .../v1/introspection/requests.go | 116 ++++++ .../v1/introspection/results.go | 260 ++++++++++++ .../v1/introspection/testing/doc.go | 1 + .../v1/introspection/testing/fixtures.go | 374 ++++++++++++++++++ .../v1/introspection/testing/requests_test.go | 98 +++++ .../v1/introspection/urls.go | 23 ++ openstack/client.go | 6 + service_client.go | 2 + 10 files changed, 941 insertions(+) create mode 100644 openstack/baremetalintrospection/v1/introspection/doc.go create mode 100644 openstack/baremetalintrospection/v1/introspection/requests.go create mode 100644 openstack/baremetalintrospection/v1/introspection/results.go create mode 100644 openstack/baremetalintrospection/v1/introspection/testing/doc.go create mode 100644 openstack/baremetalintrospection/v1/introspection/testing/fixtures.go create mode 100644 openstack/baremetalintrospection/v1/introspection/testing/requests_test.go create mode 100644 openstack/baremetalintrospection/v1/introspection/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index 9d957d1edf..e8490edd1c 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -285,6 +285,27 @@ func NewBareMetalV1NoAuthClient() (*gophercloud.ServiceClient, error) { }) } +// NewBareMetalIntrospectionV1Client returns a *ServiceClient for making calls +// to the OpenStack Bare Metal Introspection v1 API. An error will be returned +// if authentication or client creation was not possible. +func NewBareMetalIntrospectionV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewBareMetalIntrospectionV1(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} + // NewDBV1Client returns a *ServiceClient for making calls // to the OpenStack Database v1 API. An error will be returned // if authentication or client creation was not possible. diff --git a/openstack/baremetalintrospection/v1/introspection/doc.go b/openstack/baremetalintrospection/v1/introspection/doc.go new file mode 100644 index 0000000000..0604e2f6a4 --- /dev/null +++ b/openstack/baremetalintrospection/v1/introspection/doc.go @@ -0,0 +1,40 @@ +package introspection + +/* +Package introspection contains the functionality for Starting introspection, +Get introspection status, List all introspection statuses, Abort an +introspection, Get stored introspection data and reapply introspection on +stored data. + +API reference https://developer.openstack.org/api-ref/baremetal-introspection/#node-introspection + + // Example to Start Introspection + introspection.StartIntrospection(client, NodeUUID, introspection.StartOpts{}).ExtractErr() + + // Example to Get an Introspection status + introspection.GetIntrospectionStatus(client, NodeUUID).Extract() + if err != nil { + panic(err) + } + + // Example to List all introspection statuses + introspection.ListIntrospections(client.ServiceClient(), introspection.ListIntrospectionsOpts{}).EachPage(func(page pagination.Page) (bool, error) { + introspectionsList, err := introspection.ExtractIntrospections(page) + if err != nil { + return false, err + } + for _, n := range introspectionsList { + // Do something + } + return true, nil + }) + + // Example to Abort an Introspection + introspection.AbortIntrospection(client, NodeUUID).ExtractErr() + + // Example to Get stored Introspection Data + introspection.GetIntrospectionData(c, NodeUUID).Extract() + + // Example to apply Introspection Data + introspection.ApplyIntrospectionData(c, NodeUUID).ExtractErr() +*/ diff --git a/openstack/baremetalintrospection/v1/introspection/requests.go b/openstack/baremetalintrospection/v1/introspection/requests.go new file mode 100644 index 0000000000..a2d02ccde7 --- /dev/null +++ b/openstack/baremetalintrospection/v1/introspection/requests.go @@ -0,0 +1,116 @@ +package introspection + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListIntrospectionsOptsBuilder allows extensions to add additional parameters to the +// ListIntrospections request. +type ListIntrospectionsOptsBuilder interface { + ToIntrospectionsListQuery() (string, error) +} + +// ListIntrospectionsOpts allows the filtering and sorting of paginated collections through +// the Introspection API. Filtering is achieved by passing in struct field values that map to +// the node attributes you want to see returned. Marker and Limit are used +// for pagination. +type ListIntrospectionsOpts struct { + // Requests a page size of items. + Limit int `q:"limit"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToIntrospectionsListQuery formats a ListIntrospectionsOpts into a query string. +func (opts ListIntrospectionsOpts) ToIntrospectionsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListIntrospections makes a request against the Inspector API to list the current introspections. +func ListIntrospections(client *gophercloud.ServiceClient, opts ListIntrospectionsOptsBuilder) pagination.Pager { + url := listIntrospectionsURL(client) + if opts != nil { + query, err := opts.ToIntrospectionsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + var rpage = IntrospectionPage{pagination.LinkedPageBase{PageResult: r}} + return rpage + }) +} + +// GetIntrospectionStatus makes a request against the Inspector API to get the +// status of a single introspection. +func GetIntrospectionStatus(client *gophercloud.ServiceClient, nodeID string) (r GetIntrospectionStatusResult) { + _, r.Err = client.Get(introspectionURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// StartOptsBuilder allows extensions to add additional parameters to the +// Start request. +type StartOptsBuilder interface { + ToStartIntrospectionQuery() (string, error) +} + +// StartOpts represents options to start an introspection. +type StartOpts struct { + // Whether the current installation of ironic-inspector can manage PXE booting of nodes. + ManageBoot *bool `q:"manage_boot"` +} + +// ToStartIntrospectionQuery converts a StartOpts into a request. +func (opts StartOpts) ToStartIntrospectionQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// StartIntrospection initiate hardware introspection for node NodeID . +// All power management configuration for this node needs to be done prior to calling the endpoint. +func StartIntrospection(client *gophercloud.ServiceClient, nodeID string, opts StartOptsBuilder) (r StartResult) { + _, err := opts.ToStartIntrospectionQuery() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(introspectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} + +// AbortIntrospection abort running introspection. +func AbortIntrospection(client *gophercloud.ServiceClient, nodeID string) (r AbortResult) { + _, r.Err = client.Post(abortIntrospectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} + +// GetIntrospectionData return stored data from successful introspection. +func GetIntrospectionData(client *gophercloud.ServiceClient, nodeID string) (r DataResult) { + _, r.Err = client.Get(introspectionDataURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// ReApplyIntrospection triggers introspection on stored unprocessed data. +// No data is allowed to be sent along with the request. +func ReApplyIntrospection(client *gophercloud.ServiceClient, nodeID string) (r ApplyDataResult) { + _, r.Err = client.Post(introspectionUnprocessedDataURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go new file mode 100644 index 0000000000..41be701af4 --- /dev/null +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -0,0 +1,260 @@ +package introspection + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type introspectionResult struct { + gophercloud.Result +} + +// Extract interprets any introspectionResult as an Introspection, if possible. +func (r introspectionResult) Extract() (*Introspection, error) { + var s Introspection + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto will extract a response body into an Introspection struct. +func (r introspectionResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "") +} + +// ExtractIntrospectionsInto will extract a collection of introspectResult pages into a +// slice of Introspection entities. +func ExtractIntrospectionsInto(r pagination.Page, v interface{}) error { + return r.(IntrospectionPage).Result.ExtractIntoSlicePtr(v, "introspection") +} + +// ExtractIntrospections interprets the results of a single page from a +// ListIntrospections() call, producing a slice of Introspection entities. +func ExtractIntrospections(r pagination.Page) ([]Introspection, error) { + var s []Introspection + err := ExtractIntrospectionsInto(r, &s) + return s, err +} + +// IntrospectionPage abstracts the raw results of making a ListIntrospections() +// request against the Inspector 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 ExtractIntrospections call. +type IntrospectionPage struct { + pagination.LinkedPageBase +} + +// Introspection represents an introspection in the OpenStack Bare Metal Introspector API. +type Introspection struct { + // Whether introspection is finished + Finished bool `json:"finished"` + + // State of the introspection + State string `json:"state"` + + // Error message, can be null; "Canceled by operator" in case introspection was aborted + Error string `json:"error"` + + // UUID of the introspection + UUID string `json:"uuid"` + + // UTC ISO8601 timestamp + StartedAt time.Time `json:"-"` + + // UTC ISO8601 timestamp or null + FinishedAt time.Time `json:"-"` + + // Link to the introspection URL + Links []interface{} `json:"links"` +} + +// IsEmpty returns true if a page contains no Introspection results. +func (r IntrospectionPage) IsEmpty() (bool, error) { + s, err := ExtractIntrospections(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r IntrospectionPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"introspection_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// UnmarshalJSON trie to convert values for started_at and finished_at from the +// json response into RFC3339 standard. Since Introspection API can remove the +// Z from the format, if the conversion fails, it falls back to an RFC3339 +// with no Z format supported by gophercloud. +func (r *Introspection) UnmarshalJSON(b []byte) error { + type tmp Introspection + var s struct { + tmp + StartedAt string `json:"started_at"` + FinishedAt string `json:"finished_at"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = Introspection(s.tmp) + + if s.StartedAt != "" { + t, err := time.Parse(time.RFC3339, s.StartedAt) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.StartedAt) + if err != nil { + return err + } + } + r.StartedAt = t + } + + if s.FinishedAt != "" { + t, err := time.Parse(time.RFC3339, s.FinishedAt) + if err != nil { + t, err = time.Parse(gophercloud.RFC3339NoZ, s.FinishedAt) + if err != nil { + return err + } + } + r.FinishedAt = t + } + + return nil +} + +// GetIntrospectionStatusResult is the response from a GetIntrospectionStatus operation. +// Call its Extract method to interpret it as an Introspection. +type GetIntrospectionStatusResult struct { + introspectionResult +} + +// StartResult is the response from a StartIntrospection operation. +// Call its ExtractErr method to determine if the call succeeded or failed. +type StartResult struct { + gophercloud.ErrResult +} + +// AbortResult is the response from an AbortIntrospection operation. +// Call its ExtractErr method to determine if the call succeeded or failed. +type AbortResult struct { + gophercloud.ErrResult +} + +// Data represents the full introspection data collected. +// The format and contents of the stored data depends on the ramdisk used +// and plugins enabled both in the ramdisk and in inspector itself. +// This structure has been provided for basic compatibility but it +// will need extensions +type Data struct { + AllInterfaces map[string]BaseInterfaceType `json:"all_interfaces"` + BootInterface string `json:"boot_interface"` + CPUArch string `json:"cpu_arch"` + CPUs int `json:"cpus"` + Error string `json:"error"` + Interfaces map[string]BaseInterfaceType `json:"interfaces"` + Inventory InventoryType `json:"inventory"` + IPMIAddress string `json:"ipmi_address"` + LocalGB int `json:"local_gb"` + MACs []string `json:"macs"` + MemoryMB int `json:"memory_mb"` + RootDisk RootDiskType `json:"root_disk"` +} + +// Sub Types defined under Data and deeper in the structure + +type BaseInterfaceType struct { + ClientID string `json:"client_id"` + IP string `json:"ip"` + MAC string `json:"mac"` + PXE bool `json:"pxe"` +} + +type BootInfoType struct { + CurrentBootMode string `json:"current_boot_mode"` + PXEInterface string `json:"pxe_interface"` +} + +type CPUType struct { + Architecture string `json:"architecture"` + Count int `json:"count"` + Flags []string `json:"flags"` + Frequency string `json:"frequency"` + ModelName string `json:"model_name"` +} + +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 map[string]interface{} `json:"lldp"` + MACAddress string `json:"mac_address"` + Name string `json:"name"` + Product string `json:"product"` + Vendor string `json:"vendor"` +} + +type InventoryType struct { + BmcAddress string `json:"bmc_address"` + Boot BootInfoType `json:"boot"` + CPU CPUType `json:"cpu"` + Disks []RootDiskType `json:"disks"` + Interfaces []InterfaceType `json:"interfaces"` + Memory MemoryType `json:"memory"` + SystemVendor SystemVendorType `json:"system_vendor"` +} + +type MemoryType struct { + PhysicalMb int `json:"physical_mb"` + Total int `json:"total"` +} + +type RootDiskType struct { + Hctl string `json:"hctl"` + Model string `json:"model"` + Name string `json:"name"` + Rotational bool `json:"rotational"` + Serial string `json:"serial"` + Size int `json:"size"` + Vendor string `json:"vendor"` + Wwn string `json:"wwn"` + WwnVendorExtension string `json:"wwn_vendor_extension"` + WwnWithExtension string `json:"wwn_with_extension"` +} + +type SystemVendorType struct { + Manufacturer string `json:"manufacturer"` + ProductName string `json:"product_name"` + SerialNumber string `json:"serial_number"` +} + +// Extract interprets any IntrospectionDataResult as IntrospectionData, if possible. +func (r DataResult) Extract() (*Data, error) { + var s Data + err := r.ExtractInto(&s) + return &s, err +} + +// DataResult represents the response from a GetIntrospectionData operation. +// Call its Extract method to interpret it as a Data. +type DataResult struct { + gophercloud.Result +} + +// ApplyDataResult is the response from an ApplyData operation. +// Call its ExtractErr method to determine if the call succeeded or failed. +type ApplyDataResult struct { + gophercloud.ErrResult +} diff --git a/openstack/baremetalintrospection/v1/introspection/testing/doc.go b/openstack/baremetalintrospection/v1/introspection/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/baremetalintrospection/v1/introspection/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go new file mode 100644 index 0000000000..d8ace03a25 --- /dev/null +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -0,0 +1,374 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// IntrospectionListBody contains the canned body of a introspection.IntrospectionList response. +const IntrospectionListBody = ` +{ + "introspection": [ + { + "error": null, + "finished": true, + "finished_at": "2017-08-17T11:36:16", + "links": [ + { + "href": "http://127.0.0.1:5050/v1/introspection/05ccda19-581b-49bf-8f5a-6ded99701d87", + "rel": "self" + } + ], + "started_at": "2017-08-17T11:33:43", + "state": "finished", + "uuid": "05ccda19-581b-49bf-8f5a-6ded99701d87" + }, + { + "error": null, + "finished": true, + "finished_at": "2017-08-16T12:24:30", + "links": [ + { + "href": "http://127.0.0.1:5050/v1/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", + "rel": "self" + } + ], + "started_at": "2017-08-16T12:22:01", + "state": "finished", + "uuid": "c244557e-899f-46fa-a1ff-5b2c6718616b" + } + ] +} +` + +// IntrospectionStatus contains the respnse of a single introspection satus. +const IntrospectionStatus = ` +{ + "error": null, + "finished": true, + "finished_at": "2017-08-16T12:24:30", + "links": [ + { + "href": "http://127.0.0.1:5050/v1/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", + "rel": "self" + } + ], + "started_at": "2017-08-16T12:22:01", + "state": "finished", + "uuid": "c244557e-899f-46fa-a1ff-5b2c6718616b" +} +` + +// IntrospectionDataJSONSample contains sample data reported by the introspection process. +const IntrospectionDataJSONSample = ` +{ + "cpu_arch":"x86_64", + "macs":[ + "52:54:00:4e:3d:30" + ], + "root_disk":{ + "rotational":true, + "vendor":"0x1af4", + "name":"/dev/vda", + "hctl":null, + "wwn_vendor_extension":null, + "wwn_with_extension":null, + "model":"", + "wwn":null, + "serial":null, + "size":13958643712 + }, + "interfaces": { + "eth0": { + "ip":"172.24.42.100", + "mac":"52:54:00:4e:3d:30", + "pxe":true, + "client_id":null + } + }, + "cpus":2, + "boot_interface":"52:54:00:4e:3d:30", + "memory_mb":2048, + "ipmi_address":"192.167.2.134", + "inventory":{ + "bmc_address":"192.167.2.134", + "interfaces":[ + { + "lldp":null, + "product":"0x0001", + "vendor":"0x1af4", + "name":"eth1", + "has_carrier":true, + "ipv4_address":"172.24.42.101", + "client_id":null, + "mac_address":"52:54:00:47:20:4d" + }, + { + "lldp":null, + "product":"0x0001", + "vendor":"0x1af4", + "name":"eth0", + "has_carrier":true, + "ipv4_address":"172.24.42.100", + "client_id":null, + "mac_address":"52:54:00:4e:3d:30" + } + ], + "disks":[ + { + "rotational":true, + "vendor":"0x1af4", + "name":"/dev/vda", + "hctl":null, + "wwn_vendor_extension":null, + "wwn_with_extension":null, + "model":"", + "wwn":null, + "serial":null, + "size":13958643712 + } + ], + "boot":{ + "current_boot_mode":"bios", + "pxe_interface":"52:54:00:4e:3d:30" + }, + "system_vendor":{ + "serial_number":"Not Specified", + "product_name":"Bochs", + "manufacturer":"Bochs" + }, + "memory":{ + "physical_mb":2048, + "total":2105864192 + }, + "cpu":{ + "count":2, + "frequency":"2100.084", + "flags": [ + "fpu", + "mmx", + "fxsr", + "sse", + "sse2" + ], + "architecture":"x86_64" + } + }, + "error":null, + "local_gb":12, + "all_interfaces":{ + "eth1":{ + "ip":"172.24.42.101", + "mac":"52:54:00:47:20:4d", + "pxe":false, + "client_id":null + }, + "eth0":{ + "ip":"172.24.42.100", + "mac":"52:54:00:4e:3d:30", + "pxe":true, + "client_id":null + } + } +} +` + +var ( + fooTimeStarted, _ = time.Parse(gophercloud.RFC3339NoZ, "2017-08-17T11:33:43") + fooTimeFinished, _ = time.Parse(gophercloud.RFC3339NoZ, "2017-08-17T11:36:16") + IntrospectionFoo = introspection.Introspection{ + Finished: true, + State: "finished", + Error: "", + UUID: "05ccda19-581b-49bf-8f5a-6ded99701d87", + StartedAt: fooTimeStarted, + FinishedAt: fooTimeFinished, + Links: []interface{}{ + map[string]interface{}{ + "href": "http://127.0.0.1:5050/v1/introspection/05ccda19-581b-49bf-8f5a-6ded99701d87", + "rel": "self", + }, + }, + } + + barTimeStarted, _ = time.Parse(gophercloud.RFC3339NoZ, "2017-08-16T12:22:01") + barTimeFinished, _ = time.Parse(gophercloud.RFC3339NoZ, "2017-08-16T12:24:30") + IntrospectionBar = introspection.Introspection{ + Finished: true, + State: "finished", + Error: "", + UUID: "c244557e-899f-46fa-a1ff-5b2c6718616b", + StartedAt: barTimeStarted, + FinishedAt: barTimeFinished, + Links: []interface{}{ + map[string]interface{}{ + "href": "http://127.0.0.1:5050/v1/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", + "rel": "self", + }, + }, + } + + IntrospectionDataRes = introspection.Data{ + CPUArch: "x86_64", + MACs: []string{"52:54:00:4e:3d:30"}, + RootDisk: introspection.RootDiskType{ + Rotational: true, + Model: "", + Name: "/dev/vda", + Size: 13958643712, + Vendor: "0x1af4", + }, + Interfaces: map[string]introspection.BaseInterfaceType{ + "eth0": { + IP: "172.24.42.100", + MAC: "52:54:00:4e:3d:30", + PXE: true, + }, + }, + CPUs: 2, + BootInterface: "52:54:00:4e:3d:30", + MemoryMB: 2048, + IPMIAddress: "192.167.2.134", + Inventory: introspection.InventoryType{ + SystemVendor: introspection.SystemVendorType{ + Manufacturer: "Bochs", + ProductName: "Bochs", + SerialNumber: "Not Specified", + }, + BmcAddress: "192.167.2.134", + Boot: introspection.BootInfoType{ + CurrentBootMode: "bios", + PXEInterface: "52:54:00:4e:3d:30", + }, + CPU: introspection.CPUType{ + Count: 2, + Flags: []string{"fpu", "mmx", "fxsr", "sse", "sse2"}, + Frequency: "2100.084", + Architecture: "x86_64", + }, + Disks: []introspection.RootDiskType{ + introspection.RootDiskType{ + Rotational: true, + Model: "", + Name: "/dev/vda", + Size: 13958643712, + Vendor: "0x1af4", + }, + }, + Interfaces: []introspection.InterfaceType{ + introspection.InterfaceType{ + Vendor: "0x1af4", + HasCarrier: true, + MACAddress: "52:54:00:47:20:4d", + Name: "eth1", + Product: "0x0001", + IPV4Address: "172.24.42.101", + }, + introspection.InterfaceType{ + IPV4Address: "172.24.42.100", + MACAddress: "52:54:00:4e:3d:30", + Name: "eth0", + Product: "0x0001", + HasCarrier: true, + Vendor: "0x1af4", + }, + }, + Memory: introspection.MemoryType{ + PhysicalMb: 2048.0, + Total: 2.105864192e+09, + }, + }, + Error: "", + LocalGB: 12, + AllInterfaces: map[string]introspection.BaseInterfaceType{ + "eth1": { + IP: "172.24.42.101", + MAC: "52:54:00:47:20:4d", + PXE: false, + }, + "eth0": { + IP: "172.24.42.100", + MAC: "52:54:00:4e:3d:30", + PXE: true, + }, + }, + } +) + +// HandleListIntrospectionsSuccessfully sets up the test server to respond to a server ListIntrospections request. +func HandleListIntrospectionsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/introspection", 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, IntrospectionListBody) + + case "c244557e-899f-46fa-a1ff-5b2c6718616b": + fmt.Fprintf(w, `{ "introspection": [] }`) + + default: + t.Fatalf("/introspection invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleGetIntrospectionStatusSuccessfully sets up the test server to respond to a GetIntrospectionStatus request. +func HandleGetIntrospectionStatusSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", 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, IntrospectionStatus) + }) +} + +// HandleStartIntrospectionSuccessfully sets up the test server to respond to a StartIntrospection request. +func HandleStartIntrospectionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +// HandleAbortIntrospectionSuccessfully sets up the test server to respond to an AbortIntrospection request. +func HandleAbortIntrospectionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b/abort", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} + +// HandleGetIntrospectionDataSuccessfully sets up the test server to respond to a GetIntrospectionData request. +func HandleGetIntrospectionDataSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b/data", 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, IntrospectionDataJSONSample) + }) +} + +// HandleReApplyIntrospectionSuccessfully sets up the test server to respond to a ReApplyIntrospection request. +func HandleReApplyIntrospectionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b/data/unprocessed", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go b/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go new file mode 100644 index 0000000000..d27ee30a51 --- /dev/null +++ b/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go @@ -0,0 +1,98 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListIntrospections(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListIntrospectionsSuccessfully(t) + + pages := 0 + err := introspection.ListIntrospections(client.ServiceClient(), introspection.ListIntrospectionsOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := introspection.ExtractIntrospections(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 introspections, got %d", len(actual)) + } + th.CheckDeepEquals(t, IntrospectionFoo, actual[0]) + th.CheckDeepEquals(t, IntrospectionBar, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestGetIntrospectionStatus(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetIntrospectionStatusSuccessfully(t) + + c := client.ServiceClient() + actual, err := introspection.GetIntrospectionStatus(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, IntrospectionBar, *actual) +} + +func TestStartIntrospection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleStartIntrospectionSuccessfully(t) + + c := client.ServiceClient() + err := introspection.StartIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b", introspection.StartOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestAbortIntrospection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAbortIntrospectionSuccessfully(t) + + c := client.ServiceClient() + err := introspection.AbortIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestGetIntrospectionData(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetIntrospectionDataSuccessfully(t) + + c := client.ServiceClient() + actual, err := introspection.GetIntrospectionData(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, IntrospectionDataRes, *actual) +} + +func TestReApplyIntrospection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleReApplyIntrospectionSuccessfully(t) + + c := client.ServiceClient() + err := introspection.ReApplyIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetalintrospection/v1/introspection/urls.go b/openstack/baremetalintrospection/v1/introspection/urls.go new file mode 100644 index 0000000000..e480613749 --- /dev/null +++ b/openstack/baremetalintrospection/v1/introspection/urls.go @@ -0,0 +1,23 @@ +package introspection + +import "github.com/gophercloud/gophercloud" + +func listIntrospectionsURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("introspection") +} + +func introspectionURL(client *gophercloud.ServiceClient, nodeID string) string { + return client.ServiceURL("introspection", nodeID) +} + +func abortIntrospectionURL(client *gophercloud.ServiceClient, nodeID string) string { + return client.ServiceURL("introspection", nodeID, "abort") +} + +func introspectionDataURL(client *gophercloud.ServiceClient, nodeID string) string { + return client.ServiceURL("introspection", nodeID, "data") +} + +func introspectionUnprocessedDataURL(client *gophercloud.ServiceClient, nodeID string) string { + return client.ServiceURL("introspection", nodeID, "data", "unprocessed") +} diff --git a/openstack/client.go b/openstack/client.go index 064a70dbed..50f239711e 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -310,6 +310,12 @@ func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointO return initClientOpts(client, eo, "baremetal") } +// NewBareMetalIntrospectionV1 creates a ServiceClient that may be used with the v1 +// bare metal introspection package. +func NewBareMetalIntrospectionV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "baremetal-inspector") +} + // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 // object storage package. func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { diff --git a/service_client.go b/service_client.go index c889201f95..f222f05a66 100644 --- a/service_client.go +++ b/service_client.go @@ -131,6 +131,8 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion case "baremetal": opts.MoreHeaders["X-OpenStack-Ironic-API-Version"] = client.Microversion + case "baremetal-introspection": + opts.MoreHeaders["X-OpenStack-Ironic-Inspector-API-Version"] = client.Microversion } if client.Type != "" { From f802042b220641108e8b6775d6cfe4f71097533e Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Sun, 17 Mar 2019 22:30:53 -0300 Subject: [PATCH 0701/2296] Change UpdatedAt and CreatedAt to time.Time (#1509) * Change UpdateAt and CreateAt to time.Time - Change type of the variables to `time.Time` in results.go - Add UnmarshalJSON function * Update tests for time - Update the `fixtures.go` using time.Parse to created and updated values. * Update time format Change time format to gophercloud.JSONRFC3339ZNoT since it seems to allow the ISO8601 with "+00:00" * Use go format RFC3339 The Ironic time format is a proper RFC3339 format so we don't need a custom UnmarshalJSON --- openstack/baremetal/v1/ports/results.go | 6 ++++-- openstack/baremetal/v1/ports/testing/fixtures.go | 15 ++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/openstack/baremetal/v1/ports/results.go b/openstack/baremetal/v1/ports/results.go index f6695b595d..506b6c64a7 100644 --- a/openstack/baremetal/v1/ports/results.go +++ b/openstack/baremetal/v1/ports/results.go @@ -1,6 +1,8 @@ package ports import ( + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -59,11 +61,11 @@ type Port struct { Extra map[string]interface{} `json:"extra"` // The UTC date and time when the resource was created, ISO 8601 format. - CreatedAt string `json:"created_at"` + CreatedAt time.Time `json:"created_at"` // The UTC date and time when the resource was updated, ISO 8601 format. // May be “null”. - UpdatedAt string `json:"updated_at"` + UpdatedAt time.Time `json:"updated_at"` // A list of relative links. Includes the self and bookmark links. Links []interface{} `json:"links"` diff --git a/openstack/baremetal/v1/ports/testing/fixtures.go b/openstack/baremetal/v1/ports/testing/fixtures.go index 787511a144..dafeed845b 100644 --- a/openstack/baremetal/v1/ports/testing/fixtures.go +++ b/openstack/baremetal/v1/ports/testing/fixtures.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" th "github.com/gophercloud/gophercloud/testhelper" @@ -136,7 +137,11 @@ const SinglePortBody = ` ` var ( - PortFoo = ports.Port{ + fooCreated, _ = time.Parse(time.RFC3339, "2019-02-15T09:52:24+00:00") + fooUpdated, _ = time.Parse(time.RFC3339, "2019-02-15T09:55:19+00:00") + BarCreated, _ = time.Parse(time.RFC3339, "2019-02-15T09:52:23+00:00") + BarUpdated, _ = time.Parse(time.RFC3339, "2019-02-15T09:55:19+00:00") + PortFoo = ports.Port{ UUID: "f2845e11-dbd4-4728-a8c0-30d19f48924a", NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", Address: "52:54:00:4d:87:e6", @@ -144,8 +149,8 @@ var ( LocalLinkConnection: map[string]interface{}{}, InternalInfo: map[string]interface{}{}, Extra: map[string]interface{}{}, - CreatedAt: "2019-02-15T09:52:24+00:00", - UpdatedAt: "2019-02-15T09:55:19+00:00", + CreatedAt: fooCreated, + UpdatedAt: fooUpdated, Links: []interface{}{map[string]interface{}{"href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "self"}, map[string]interface{}{"href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "bookmark"}}, } @@ -157,8 +162,8 @@ var ( LocalLinkConnection: map[string]interface{}{}, InternalInfo: map[string]interface{}{}, Extra: map[string]interface{}{}, - CreatedAt: "2019-02-15T09:52:23+00:00", - UpdatedAt: "2019-02-15T09:55:19+00:00", + CreatedAt: BarCreated, + UpdatedAt: BarUpdated, Links: []interface{}{map[string]interface{}{"href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "self"}, map[string]interface{}{"rel": "bookmark", "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7"}}, } ) From ff9851476e985a4f27ed846d6babd7b24b763da5 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 18 Mar 2019 02:57:31 +0100 Subject: [PATCH 0702/2296] Networking V2: Add network MTU extension support (#1495) * Networking V2: Add network MTU extension support * Minor mtu acceptance tests fixes --- .../networking/v2/extensions/mtu/mtu.go | 61 +++++++ .../networking/v2/extensions/mtu/mtu_test.go | 133 +++++++++++++++ .../networking/v2/extensions/mtu/requests.go | 87 ++++++++++ .../networking/v2/extensions/mtu/results.go | 8 + .../v2/extensions/mtu/testing/fixtures.go | 55 +++++++ .../extensions/mtu/testing/requests_test.go | 24 +++ .../v2/extensions/mtu/testing/results_test.go | 151 ++++++++++++++++++ .../v2/networks/testing/fixtures.go | 9 +- script/acceptancetest | 1 + 9 files changed, 526 insertions(+), 3 deletions(-) create mode 100644 acceptance/openstack/networking/v2/extensions/mtu/mtu.go create mode 100644 acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go create mode 100644 openstack/networking/v2/extensions/mtu/requests.go create mode 100644 openstack/networking/v2/extensions/mtu/results.go create mode 100644 openstack/networking/v2/extensions/mtu/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/mtu/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/mtu/testing/results_test.go diff --git a/acceptance/openstack/networking/v2/extensions/mtu/mtu.go b/acceptance/openstack/networking/v2/extensions/mtu/mtu.go new file mode 100644 index 0000000000..36c06f9e8d --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/mtu/mtu.go @@ -0,0 +1,61 @@ +package mtu + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/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" +) + +type NetworkMTU struct { + networks.Network + mtu.NetworkMTUExt +} + +// CreateNetworkWithMTU will create a network with custom MTU. An error will be +// returned if the creation failed. +func CreateNetworkWithMTU(t *testing.T, client *gophercloud.ServiceClient, networkMTU *int) (*NetworkMTU, error) { + networkName := tools.RandomString("TESTACC-", 8) + networkDescription := tools.RandomString("TESTACC-DESC-", 8) + + t.Logf("Attempting to create a network with custom MTU: %s", networkName) + + adminStateUp := true + + var createOpts networks.CreateOptsBuilder + createOpts = networks.CreateOpts{ + Name: networkName, + Description: networkDescription, + AdminStateUp: &adminStateUp, + } + + if *networkMTU > 0 { + createOpts = mtu.CreateOptsExt{ + CreateOptsBuilder: createOpts, + MTU: *networkMTU, + } + } + + var network NetworkMTU + + err := networks.Create(client, createOpts).ExtractInto(&network) + if err != nil { + return &network, err + } + + t.Logf("Created a network with custom MTU: %s", networkName) + + th.AssertEquals(t, network.Name, networkName) + th.AssertEquals(t, network.Description, networkDescription) + th.AssertEquals(t, network.AdminStateUp, adminStateUp) + if *networkMTU > 0 { + th.AssertEquals(t, network.MTU, *networkMTU) + } else { + *networkMTU = network.MTU + } + + return &network, nil +} diff --git a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go new file mode 100644 index 0000000000..7664729615 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -0,0 +1,133 @@ +// +build acceptance networking + +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/openstack/common/extensions" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestMTUNetworkCRUDL(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + extension, err := extensions.Get(client, "net-mtu").Extract() + if err != nil { + t.Skip("This test requires net-mtu Neutron extension") + } + tools.PrintResource(t, extension) + + mtuWritable, _ := extensions.Get(client, "net-mtu-writable").Extract() + tools.PrintResource(t, mtuWritable) + + // Create Network + var networkMTU int + if mtuWritable != nil { + networkMTU = 1449 + } + network, err := CreateNetworkWithMTU(t, client, &networkMTU) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + // MTU filtering is supported only in read-only MTU extension + // https://bugs.launchpad.net/neutron/+bug/1818317 + if mtuWritable == nil { + // List network successfully + var listOpts networks.ListOptsBuilder + listOpts = mtu.ListOptsExt{ + ListOptsBuilder: networks.ListOpts{}, + MTU: networkMTU, + } + var listedNetworks []NetworkMTU + i := 0 + err = networks.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + i++ + err := networks.ExtractNetworksInto(page, &listedNetworks) + if err != nil { + t.Errorf("Failed to extract networks: %v", err) + return false, err + } + + tools.PrintResource(t, listedNetworks) + + th.AssertEquals(t, 1, len(listedNetworks)) + th.CheckDeepEquals(t, *network, listedNetworks[0]) + + return true, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, i) + + // List network unsuccessfully + listOpts = mtu.ListOptsExt{ + ListOptsBuilder: networks.ListOpts{}, + MTU: 1, + } + i = 0 + err = networks.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + i++ + err := networks.ExtractNetworksInto(page, &listedNetworks) + if err != nil { + t.Errorf("Failed to extract networks: %v", err) + return false, err + } + + tools.PrintResource(t, listedNetworks) + + th.AssertEquals(t, 1, len(listedNetworks)) + th.CheckDeepEquals(t, *network, listedNetworks[0]) + + return true, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, i) + } + + // Get network + var getNetwork NetworkMTU + err = networks.Get(client, network.ID).ExtractInto(&getNetwork) + th.AssertNoErr(t, err) + + tools.PrintResource(t, getNetwork) + th.AssertDeepEquals(t, *network, getNetwork) + + if mtuWritable != nil { + // Update network + newNetworkDescription := "" + newNetworkMTU := 1350 + networkUpdateOpts := networks.UpdateOpts{ + Description: &newNetworkDescription, + } + var updateOpts networks.UpdateOptsBuilder + updateOpts = mtu.UpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + MTU: newNetworkMTU, + } + + var newNetwork NetworkMTU + err = networks.Update(client, network.ID, updateOpts).ExtractInto(&newNetwork) + th.AssertNoErr(t, err) + + tools.PrintResource(t, newNetwork) + th.AssertEquals(t, newNetwork.Description, newNetworkDescription) + th.AssertEquals(t, newNetwork.MTU, newNetworkMTU) + + // Get updated network + var getNewNetwork NetworkMTU + err = networks.Get(client, network.ID).ExtractInto(&getNewNetwork) + th.AssertNoErr(t, err) + + tools.PrintResource(t, getNewNetwork) + th.AssertDeepEquals(t, newNetwork, getNewNetwork) + } +} diff --git a/openstack/networking/v2/extensions/mtu/requests.go b/openstack/networking/v2/extensions/mtu/requests.go new file mode 100644 index 0000000000..bffb7f5ef7 --- /dev/null +++ b/openstack/networking/v2/extensions/mtu/requests.go @@ -0,0 +1,87 @@ +package mtu + +import ( + "fmt" + "net/url" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" +) + +// ListOptsExt adds an MTU option to the base ListOpts. +type ListOptsExt struct { + networks.ListOptsBuilder + + // The maximum transmission unit (MTU) value to address fragmentation. + // Minimum value is 68 for IPv4, and 1280 for IPv6. + MTU int `q:"mtu"` +} + +// ToNetworkListQuery adds the router:external option to the base network +// list options. +func (opts ListOptsExt) ToNetworkListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts.ListOptsBuilder) + if err != nil { + return "", err + } + + params := q.Query() + if opts.MTU > 0 { + params.Add("mtu", fmt.Sprintf("%d", opts.MTU)) + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), err +} + +// CreateOptsExt adds an MTU option to the base Network CreateOpts. +type CreateOptsExt struct { + networks.CreateOptsBuilder + + // The maximum transmission unit (MTU) value to address fragmentation. + // Minimum value is 68 for IPv4, and 1280 for IPv6. + MTU int `json:"mtu,omitempty"` +} + +// ToNetworkCreateMap adds an MTU to the base network creation options. +func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() + if err != nil { + return nil, err + } + + if opts.MTU == 0 { + return base, nil + } + + networkMap := base["network"].(map[string]interface{}) + networkMap["mtu"] = opts.MTU + + return base, nil +} + +// CreateOptsExt adds an MTU option to the base Network UpdateOpts. +type UpdateOptsExt struct { + networks.UpdateOptsBuilder + + // The maximum transmission unit (MTU) value to address fragmentation. + // Minimum value is 68 for IPv4, and 1280 for IPv6. + MTU int `json:"mtu,omitempty"` +} + +// ToNetworkUpdateMap adds an MTU to the base network uptade options. +func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() + if err != nil { + return nil, err + } + + if opts.MTU == 0 { + return base, nil + } + + networkMap := base["network"].(map[string]interface{}) + networkMap["mtu"] = opts.MTU + + return base, nil +} diff --git a/openstack/networking/v2/extensions/mtu/results.go b/openstack/networking/v2/extensions/mtu/results.go new file mode 100644 index 0000000000..497c9c37a0 --- /dev/null +++ b/openstack/networking/v2/extensions/mtu/results.go @@ -0,0 +1,8 @@ +package mtu + +// NetworkMTUExt represents an extended form of a Network with additional MTU field. +type NetworkMTUExt struct { + // The maximum transmission unit (MTU) value to address fragmentation. + // Minimum value is 68 for IPv4, and 1280 for IPv6. + MTU int `json:"mtu"` +} diff --git a/openstack/networking/v2/extensions/mtu/testing/fixtures.go b/openstack/networking/v2/extensions/mtu/testing/fixtures.go new file mode 100644 index 0000000000..c69f9013ff --- /dev/null +++ b/openstack/networking/v2/extensions/mtu/testing/fixtures.go @@ -0,0 +1,55 @@ +package testing + +// These fixtures are here instead of in the underlying networks package +// because all network tests (including extensions) would have to +// implement the NetworkMTUExt extention for create/update tests +// to pass. + +const CreateRequest = ` +{ + "network": { + "name": "private", + "admin_state_up": true, + "mtu": 1500 + } +}` + +const CreateResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": ["08eae331-0402-425a-923c-34f7cfe39c1b"], + "name": "private", + "admin_state_up": true, + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "shared": false, + "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "mtu": 1500 + } +}` + +const UpdateRequest = ` +{ + "network": { + "name": "new_network_name", + "admin_state_up": false, + "shared": true, + "mtu": 1350 + } +}` + +const UpdateResponse = ` +{ + "network": { + "status": "ACTIVE", + "subnets": [], + "name": "new_network_name", + "admin_state_up": false, + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "shared": true, + "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", + "mtu": 1350 + } +}` + +const ExpectedListOpts = "?id=d32019d3-bc6e-4319-9c1d-6722fc136a22&mtu=1500" diff --git a/openstack/networking/v2/extensions/mtu/testing/requests_test.go b/openstack/networking/v2/extensions/mtu/testing/requests_test.go new file mode 100644 index 0000000000..1b69ce41ee --- /dev/null +++ b/openstack/networking/v2/extensions/mtu/testing/requests_test.go @@ -0,0 +1,24 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListExternal(t *testing.T) { + networkListOpts := networks.ListOpts{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + } + + listOpts := mtu.ListOptsExt{ + ListOptsBuilder: networkListOpts, + MTU: 1500, + } + + actual, err := listOpts.ToNetworkListQuery() + th.AssertNoErr(t, err) + th.AssertEquals(t, ExpectedListOpts, actual) +} diff --git a/openstack/networking/v2/extensions/mtu/testing/results_test.go b/openstack/networking/v2/extensions/mtu/testing/results_test.go new file mode 100644 index 0000000000..e2779e9f3a --- /dev/null +++ b/openstack/networking/v2/extensions/mtu/testing/results_test.go @@ -0,0 +1,151 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" + th "github.com/gophercloud/gophercloud/testhelper" +) + +type NetworkMTU struct { + networks.Network + mtu.NetworkMTUExt +} + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks", 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, nettest.ListResponse) + }) + + type NetworkWithMTUExt struct { + networks.Network + mtu.NetworkMTUExt + } + var actual []NetworkWithMTUExt + + allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + err = networks.ExtractNetworksInto(allPages, &actual) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", actual[0].ID) + th.AssertEquals(t, 1500, actual[0].MTU) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/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, nettest.GetResponse) + }) + + var s NetworkMTU + + err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", s.ID) + th.AssertEquals(t, 1500, s.MTU) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks", 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) + }) + + iTrue := true + networkCreateOpts := networks.CreateOpts{ + Name: "private", + AdminStateUp: &iTrue, + } + + mtuCreateOpts := mtu.CreateOptsExt{ + CreateOptsBuilder: &networkCreateOpts, + MTU: 1500, + } + + var s NetworkMTU + + err := networks.Create(fake.ServiceClient(), mtuCreateOpts).ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) + th.AssertEquals(t, iTrue, s.AdminStateUp) + th.AssertEquals(t, 1500, s.MTU) +} + +func TestUpdate(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.TestJSONRequest(t, r, UpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateResponse) + }) + + iTrue := true + iFalse := false + networkUpdateOpts := networks.UpdateOpts{ + Name: "new_network_name", + AdminStateUp: &iFalse, + Shared: &iTrue, + } + + mtuUpdateOpts := mtu.UpdateOptsExt{ + UpdateOptsBuilder: &networkUpdateOpts, + MTU: 1350, + } + + var s NetworkMTU + + err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", mtuUpdateOpts).ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, "4e8e5957-649f-477b-9e5b-f1f75b21c03c", s.ID) + th.AssertEquals(t, "new_network_name", s.Name) + th.AssertEquals(t, iFalse, s.AdminStateUp) + th.AssertEquals(t, iTrue, s.Shared) + th.AssertEquals(t, 1350, s.MTU) +} diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index a2d197ca0e..756fa6b18a 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -22,7 +22,8 @@ const ListResponse = ` "provider:network_type": "local", "router:external": true, "port_security_enabled": true, - "dns_domain": "local." + "dns_domain": "local.", + "mtu": 1500 }, { "status": "ACTIVE", @@ -39,7 +40,8 @@ const ListResponse = ` "provider:network_type": "local", "router:external": false, "port_security_enabled": false, - "dns_domain": "" + "dns_domain": "", + "mtu": 1500 } ] }` @@ -61,7 +63,8 @@ const GetResponse = ` "provider:network_type": "local", "router:external": true, "port_security_enabled": true, - "dns_domain": "local." + "dns_domain": "local.", + "mtu": 1500 } }` diff --git a/script/acceptancetest b/script/acceptancetest index 269300138e..8d320ecdfc 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -60,6 +60,7 @@ acceptance/openstack/networking/v2/extensions/layer3 # Resource not found # acceptance/openstack/networking/v2/extensions/lbaas_v2 +acceptance/openstack/networking/v2/extensions/mtu acceptance/openstack/networking/v2/extensions/networkipavailabilities acceptance/openstack/networking/v2/extensions/portsbinding acceptance/openstack/networking/v2/extensions/qos/ruletypes From 1328949142b15973176f2930e053a44b39fd5a91 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 20 Mar 2019 01:32:14 +0000 Subject: [PATCH 0703/2296] Acc Tests: Enable Octavia --- .zuul/playbooks/gophercloud-acceptance-test/run.yaml | 2 +- script/acceptancetest | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index 202ed962a5..26ed039213 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -8,7 +8,7 @@ - 'manila' - 'designate' - 'zun' - #- 'octavia' + - 'octavia' - 'neutron-ext' - install-devstack tasks: diff --git a/script/acceptancetest b/script/acceptancetest index 8d320ecdfc..68a5f91b0f 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -39,8 +39,8 @@ 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/keymanager/v1 +acceptance/openstack/loadbalancer/v2 # No suitable endpoint could be found in the service catalog. # acceptance/openstack/messaging/v2 From 812e1f8054e56b0cab7ae53e4b76488f3687dd14 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 21 Mar 2019 14:49:05 +0000 Subject: [PATCH 0704/2296] Acc Tests: Disabling TestVolumeActionsUploadImageDestroy --- .../openstack/blockstorage/extensions/volumeactions_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 42fb8888d4..9bd9e3cfd5 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -14,6 +14,8 @@ import ( ) func TestVolumeActionsUploadImageDestroy(t *testing.T) { + t.Skip("This test is diabled because it sometimes fails in OpenLab") + blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) From 96c8e97b6eb7635614bca6a9944ea8cc24891fa2 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Thu, 21 Mar 2019 15:03:46 +1300 Subject: [PATCH 0705/2296] Octavia: Support insert_headers for creating listener --- openstack/loadbalancer/v2/listeners/requests.go | 3 +++ openstack/loadbalancer/v2/listeners/results.go | 3 +++ .../v2/listeners/testing/fixtures.go | 17 +++++++++++++---- .../v2/listeners/testing/requests_test.go | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 005bd1e79a..25f411aa56 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -120,6 +120,9 @@ type CreateOpts struct { // Backend member inactivity timeout in milliseconds TimeoutMemberData *int `json:"timeout_member_data,omitempty"` + + // A dictionary of optional headers to insert into the request before it is sent to the backend member. + InsertHeaders map[string]string `json:"insert_headers,omitempty"` } // ToListenerCreateMap builds a request body from CreateOpts. diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 45164b3335..e8c6a79998 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -68,6 +68,9 @@ type Listener struct { // Backend member inactivity timeout in milliseconds TimeoutMemberData int `json:"timeout_member_data"` + + // A dictionary of optional headers to insert into the request before it is sent to the backend member. + InsertHeaders map[string]string `json:"insert_headers"` } type Stats struct { diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index 47c07aaaa6..5731c1cdbd 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -41,7 +41,10 @@ const ListenersListBody = ` "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], "timeout_client_data": 50000, - "timeout_member_data": 50000 + "timeout_member_data": 50000, + "insert_headers": { + "X-Forwarded-For": "true" + } } ] } @@ -64,8 +67,10 @@ const SingleListenerBody = ` "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], "timeout_client_data": 50000, - "timeout_member_data": 50000 - + "timeout_member_data": 50000, + "insert_headers": { + "X-Forwarded-For": "true" + } } } ` @@ -135,6 +140,7 @@ var ( SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, TimeoutClientData: 50000, TimeoutMemberData: 50000, + InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, } ListenerUpdated = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -195,7 +201,10 @@ func HandleListenerCreationSuccessfully(t *testing.T, response string) { "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", - "protocol_port": 3306 + "protocol_port": 3306, + "insert_headers": { + "X-Forwarded-For": "true" + } } }`) diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index f1a4093725..0b3fce4c9b 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -66,6 +66,7 @@ func TestCreateListener(t *testing.T) { DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ProtocolPort: 3306, + InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, }).Extract() th.AssertNoErr(t, err) From ffae9d886f6bb1da7b4a5a839852d941f337dd16 Mon Sep 17 00:00:00 2001 From: Pavel Ivanov Date: Fri, 22 Mar 2019 15:14:43 +0300 Subject: [PATCH 0706/2296] Fix OK codes in keypair creation --- openstack/compute/v2/extensions/keypairs/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go index 4e5e499e3a..b72807770e 100644 --- a/openstack/compute/v2/extensions/keypairs/requests.go +++ b/openstack/compute/v2/extensions/keypairs/requests.go @@ -68,7 +68,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, + OkCodes: []int{200, 201}, }) return } From a127a1b294c369554364af633d29d213ba88ef16 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 22 Mar 2019 19:12:45 -0600 Subject: [PATCH 0707/2296] Update fixtures.go --- .../loadbalancer/v2/listeners/testing/fixtures.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index 5731c1cdbd..1ea6d49a03 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -68,9 +68,9 @@ const SingleListenerBody = ` "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], "timeout_client_data": 50000, "timeout_member_data": 50000, - "insert_headers": { - "X-Forwarded-For": "true" - } + "insert_headers": { + "X-Forwarded-For": "true" + } } } ` @@ -202,9 +202,9 @@ func HandleListenerCreationSuccessfully(t *testing.T, response string) { "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", "protocol_port": 3306, - "insert_headers": { - "X-Forwarded-For": "true" - } + "insert_headers": { + "X-Forwarded-For": "true" + } } }`) From 2846dccaf5f7e9a1688646e04aff1bd7f4fa5a95 Mon Sep 17 00:00:00 2001 From: Feilong Wang Date: Sat, 23 Mar 2019 17:42:59 +1300 Subject: [PATCH 0708/2296] Support Magnum resize API (#1516) Recently in Magnum we introduced a new API v1/clusters//actions/resize [1] to enable the capability that consumer can resize the node count of the cluster and meanwhile the API allows to pass in the node instance ID so as to delete particular nodes. That's very useful for the auto-healing and auto-scaling cases which Magnum will leverage k8s cluster Autoscaler to achieve that. Without this new API, the consumer has to call Heat API firstly to remove the particular to remove the nodes and then call Magnum API again to update the node count, which is not ideal obviously. Hence the new API was proposed. More information please refer [2] [3]. [1] https://review.openstack.org/638572 [2] https://review.openstack.org/631378 [3] kubernetes/autoscaler#1690 --- .../containerinfra/v1/clusters/requests.go | 37 +++++++++++++++++++ .../containerinfra/v1/clusters/results.go | 5 +++ .../v1/clusters/testing/fixtures.go | 19 ++++++++++ .../v1/clusters/testing/requests_test.go | 27 ++++++++++++++ openstack/containerinfra/v1/clusters/urls.go | 4 ++ 5 files changed, 92 insertions(+) diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 66035adcbf..5d5198018c 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -175,3 +175,40 @@ func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuild } return } + +// ResizeOptsBuilder allows extensions to add additional parameters to the +// Resize request. +type ResizeOptsBuilder interface { + ToClusterResizeMap() (map[string]interface{}, error) +} + +// ResizeOpts params +type ResizeOpts struct { + NodeCount *int `json:"node_count" required:"true"` + NodesToRemove []string `json:"nodes_to_remove,omitempty"` + NodeGroup string `json:"nodegroup,omitempty"` +} + +// ToClusterResizeMap constructs a request body from ResizeOpts. +func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Resize an existing cluster node count. +func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ResizeResult) { + b, err := opts.ToClusterResizeMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(resizeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) + + if r.Err == nil { + r.Header = result.Header + } + return +} diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index b386b9be2a..b4a8734c29 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -39,6 +39,11 @@ type UpdateResult struct { commonResult } +// ResizeResult is the response of a Resize operations. +type ResizeResult struct { + commonResult +} + func (r CreateResult) Extract() (string, error) { var s struct { UUID string diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index de86e411b9..ff41a02b4d 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -271,3 +271,22 @@ func HandleDeleteClusterSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +var ResizeResponse = fmt.Sprintf(` +{ + "uuid": "%s", + "node_count": 2 +}`, clusterUUID) + +func HandleResizeClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/actions/resize", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-Id", requestUUID) + w.WriteHeader(http.StatusAccepted) + + fmt.Fprint(w, ResizeResponse) + }) +} diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index 96cda0876a..0efff3f437 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -172,3 +172,30 @@ func TestDeleteCluster(t *testing.T) { th.AssertEquals(t, requestUUID, uuid) } + +func TestResizeCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResizeClusterSuccessfully(t) + + nodeCount := 2 + + var opts clusters.ResizeOptsBuilder + opts = clusters.ResizeOpts{ + NodeCount: &nodeCount, + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + res := clusters.Resize(sc, clusterUUID, opts) + th.AssertNoErr(t, res.Err) + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, requestUUID, requestID) + + actual, err := res.Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, nodeCount, actual.NodeCount) +} diff --git a/openstack/containerinfra/v1/clusters/urls.go b/openstack/containerinfra/v1/clusters/urls.go index 6d132f74b8..f0fcf1aeff 100644 --- a/openstack/containerinfra/v1/clusters/urls.go +++ b/openstack/containerinfra/v1/clusters/urls.go @@ -37,3 +37,7 @@ func listDetailURL(client *gophercloud.ServiceClient) string { func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } + +func resizeURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("clusters", id, "actions/resize") +} From 873f28e270b0da6a99e290778c1ce306189a80b2 Mon Sep 17 00:00:00 2001 From: Feilong Wang Date: Tue, 26 Mar 2019 07:20:07 +1300 Subject: [PATCH 0709/2296] Add apiversions package for Magnum (#1521) New package "apiversions" is introducing so that the consumer of Magnum API can discovery the correct version to leverage. --- openstack/containerinfra/apiversions/doc.go | 3 + .../containerinfra/apiversions/errors.go | 23 +++++ .../containerinfra/apiversions/requests.go | 19 ++++ .../containerinfra/apiversions/results.go | 68 ++++++++++++++ .../containerinfra/apiversions/testing/doc.go | 2 + .../apiversions/testing/fixtures.go | 88 +++++++++++++++++++ .../apiversions/testing/requests_test.go | 37 ++++++++ openstack/containerinfra/apiversions/urls.go | 20 +++++ 8 files changed, 260 insertions(+) create mode 100644 openstack/containerinfra/apiversions/doc.go create mode 100644 openstack/containerinfra/apiversions/errors.go create mode 100644 openstack/containerinfra/apiversions/requests.go create mode 100644 openstack/containerinfra/apiversions/results.go create mode 100644 openstack/containerinfra/apiversions/testing/doc.go create mode 100644 openstack/containerinfra/apiversions/testing/fixtures.go create mode 100644 openstack/containerinfra/apiversions/testing/requests_test.go create mode 100644 openstack/containerinfra/apiversions/urls.go diff --git a/openstack/containerinfra/apiversions/doc.go b/openstack/containerinfra/apiversions/doc.go new file mode 100644 index 0000000000..9dd2edddaa --- /dev/null +++ b/openstack/containerinfra/apiversions/doc.go @@ -0,0 +1,3 @@ +// Package apiversions provides information and interaction with the different +// API versions for the Container Infra service, code-named Magnum. +package apiversions diff --git a/openstack/containerinfra/apiversions/errors.go b/openstack/containerinfra/apiversions/errors.go new file mode 100644 index 0000000000..8f0f7628de --- /dev/null +++ b/openstack/containerinfra/apiversions/errors.go @@ -0,0 +1,23 @@ +package apiversions + +import ( + "fmt" +) + +// ErrVersionNotFound is the error when the requested API version +// could not be found. +type ErrVersionNotFound struct{} + +func (e ErrVersionNotFound) Error() string { + return fmt.Sprintf("Unable to find requested API version") +} + +// ErrMultipleVersionsFound is the error when a request for an API +// version returns multiple results. +type ErrMultipleVersionsFound struct { + Count int +} + +func (e ErrMultipleVersionsFound) Error() string { + return fmt.Sprintf("Found %d API versions", e.Count) +} diff --git a/openstack/containerinfra/apiversions/requests.go b/openstack/containerinfra/apiversions/requests.go new file mode 100644 index 0000000000..2e1f6639b5 --- /dev/null +++ b/openstack/containerinfra/apiversions/requests.go @@ -0,0 +1,19 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List lists all the API versions available to end-users. +func List(c *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { + return APIVersionPage{pagination.SinglePageBase(r)} + }) +} + +// Get will get a specific API version, specified by major ID. +func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { + _, r.Err = client.Get(getURL(client, v), &r.Body, nil) + return +} diff --git a/openstack/containerinfra/apiversions/results.go b/openstack/containerinfra/apiversions/results.go new file mode 100644 index 0000000000..b2959802de --- /dev/null +++ b/openstack/containerinfra/apiversions/results.go @@ -0,0 +1,68 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// APIVersion represents an API version for the Container Infra service. +type APIVersion struct { + // ID is the unique identifier of the API version. + ID string `json:"id"` + + // MinVersion is the minimum microversion supported. + MinVersion string `json:"min_version"` + + // Status is the API versions status. + Status string `json:"status"` + + // Version is the maximum microversion supported. + Version string `json:"max_version"` +} + +// APIVersionPage is the page returned by a pager when traversing over a +// collection of API versions. +type APIVersionPage struct { + pagination.SinglePageBase +} + +// IsEmpty checks whether an APIVersionPage struct is empty. +func (r APIVersionPage) IsEmpty() (bool, error) { + is, err := ExtractAPIVersions(r) + return len(is) == 0, err +} + +// ExtractAPIVersions takes a collection page, extracts all of the elements, +// and returns them a slice of APIVersion structs. It is effectively a cast. +func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { + var s struct { + Versions []APIVersion `json:"versions"` + } + err := (r.(APIVersionPage)).ExtractInto(&s) + return s.Versions, err +} + +// GetResult represents the result of a get operation. +type GetResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts an API version resource. +func (r GetResult) Extract() (*APIVersion, error) { + var s struct { + Versions []APIVersion `json:"versions"` + } + err := r.ExtractInto(&s) + if err != nil { + return nil, err + } + + switch len(s.Versions) { + case 0: + return nil, ErrVersionNotFound{} + case 1: + return &s.Versions[0], nil + default: + return nil, ErrMultipleVersionsFound{Count: len(s.Versions)} + } +} diff --git a/openstack/containerinfra/apiversions/testing/doc.go b/openstack/containerinfra/apiversions/testing/doc.go new file mode 100644 index 0000000000..12e4bda0f9 --- /dev/null +++ b/openstack/containerinfra/apiversions/testing/doc.go @@ -0,0 +1,2 @@ +// apiversions_v1 +package testing diff --git a/openstack/containerinfra/apiversions/testing/fixtures.go b/openstack/containerinfra/apiversions/testing/fixtures.go new file mode 100644 index 0000000000..b5a13ea486 --- /dev/null +++ b/openstack/containerinfra/apiversions/testing/fixtures.go @@ -0,0 +1,88 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const MagnumAPIVersionResponse = ` +{ + "versions":[ + { + "status":"CURRENT", + "min_version":"1.1", + "max_version":"1.7", + "id":"v1", + "links":[ + { + "href":"http://10.164.180.104:9511/v1/", + "rel":"self" + } + ] + } + ], + "name":"OpenStack Magnum API", + "description":"Magnum is an OpenStack project which aims to provide container management." + } +` + +const MagnumAllAPIVersionsResponse = ` +{ + "versions":[ + { + "status":"CURRENT", + "min_version":"1.1", + "max_version":"1.7", + "id":"v1", + "links":[ + { + "href":"http://10.164.180.104:9511/v1/", + "rel":"self" + } + ] + } + ], + "name":"OpenStack Magnum API", + "description":"Magnum is an OpenStack project which aims to provide container management." + } +` + +var MagnumAPIVersion1Result = apiversions.APIVersion{ + ID: "v1", + Status: "CURRENT", + MinVersion: "1.1", + Version: "1.7", +} + +var MagnumAllAPIVersionResults = []apiversions.APIVersion{ + MagnumAPIVersion1Result, +} + +func MockListResponse(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", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, MagnumAllAPIVersionsResponse) + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/v1/", 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, MagnumAPIVersionResponse) + }) +} diff --git a/openstack/containerinfra/apiversions/testing/requests_test.go b/openstack/containerinfra/apiversions/testing/requests_test.go new file mode 100644 index 0000000000..cd21ad7792 --- /dev/null +++ b/openstack/containerinfra/apiversions/testing/requests_test.go @@ -0,0 +1,37 @@ +package testing + +import ( + "fmt" + "testing" + + "github.com/gophercloud/gophercloud/openstack/containerinfra/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAPIVersions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + + actual, err := apiversions.ExtractAPIVersions(allVersions) + th.AssertNoErr(t, err) + fmt.Println(actual) + th.AssertDeepEquals(t, MagnumAllAPIVersionResults, actual) +} + +func TestGetAPIVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, MagnumAPIVersion1Result, *actual) +} diff --git a/openstack/containerinfra/apiversions/urls.go b/openstack/containerinfra/apiversions/urls.go new file mode 100644 index 0000000000..6a30ca91e2 --- /dev/null +++ b/openstack/containerinfra/apiversions/urls.go @@ -0,0 +1,20 @@ +package apiversions + +import ( + "net/url" + "strings" + + "github.com/gophercloud/gophercloud" +) + +func getURL(c *gophercloud.ServiceClient, version string) string { + u, _ := url.Parse(c.ServiceURL("")) + u.Path = "/" + strings.TrimRight(version, "/") + "/" + return u.String() +} + +func listURL(c *gophercloud.ServiceClient) string { + u, _ := url.Parse(c.ServiceURL("")) + u.Path = "/" + return u.String() +} From 16be87900da3014836eb0a3a1ca5cd6d967b2e9f Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Tue, 26 Mar 2019 16:54:43 +0800 Subject: [PATCH 0710/2296] Move periodic jobs back to OpenLab We hope to move all of outside periodic jobs back to OpenLab projects.yaml, so that we can plan the periodic testing resource usage amount for OpenLab and avoid periodic jobs define confusion in different project branch maintaining policy. Periodic jobs will be moved to theopenlab/openlab-zuul-jobs#479 and will be triggered at UTC-0 04:00 and 16:00 of everyday. Related-Bug: theopenlab/openlab#173 --- .zuul.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 69fb4709f5..776ed5a796 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -100,7 +100,3 @@ recheck-rocky: jobs: - gophercloud-acceptance-test-rocky - periodic: - jobs: - - gophercloud-unittest - - gophercloud-acceptance-test From 2e9e55040ce562d7c410e677a2ed93c7e2bb973a Mon Sep 17 00:00:00 2001 From: Maysa De Macedo Souza Date: Wed, 27 Mar 2019 10:28:00 +0100 Subject: [PATCH 0711/2296] Add support to Load Balancer api version requests (#1524) * Add support to Load Balancer api version requests * Update comment to have method name --- openstack/loadbalancer/v2/apiversions/doc.go | 22 +++++ .../loadbalancer/v2/apiversions/requests.go | 13 +++ .../loadbalancer/v2/apiversions/results.go | 32 +++++++ .../v2/apiversions/testing/doc.go | 2 + .../v2/apiversions/testing/fixture.go | 93 +++++++++++++++++++ .../v2/apiversions/testing/requests_test.go | 24 +++++ openstack/loadbalancer/v2/apiversions/urls.go | 7 ++ 7 files changed, 193 insertions(+) create mode 100644 openstack/loadbalancer/v2/apiversions/doc.go create mode 100644 openstack/loadbalancer/v2/apiversions/requests.go create mode 100644 openstack/loadbalancer/v2/apiversions/results.go create mode 100644 openstack/loadbalancer/v2/apiversions/testing/doc.go create mode 100644 openstack/loadbalancer/v2/apiversions/testing/fixture.go create mode 100644 openstack/loadbalancer/v2/apiversions/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/apiversions/urls.go diff --git a/openstack/loadbalancer/v2/apiversions/doc.go b/openstack/loadbalancer/v2/apiversions/doc.go new file mode 100644 index 0000000000..d9b5eff9d9 --- /dev/null +++ b/openstack/loadbalancer/v2/apiversions/doc.go @@ -0,0 +1,22 @@ +/* +Package apiversions provides information and interaction with the different +API versions for the OpenStack Load Balancer service. This functionality is not +restricted to this particular version. + +Example to List API Versions + + allPages, err := apiversions.List(loadbalancerClient).AllPages() + if err != nil { + panic(err) + } + + allVersions, err := apiversions.ExtractAPIVersions(allPages) + if err != nil { + panic(err) + } + + for _, version := range allVersions { + fmt.Printf("%+v\n", version) + } +*/ +package apiversions diff --git a/openstack/loadbalancer/v2/apiversions/requests.go b/openstack/loadbalancer/v2/apiversions/requests.go new file mode 100644 index 0000000000..ed71797ccf --- /dev/null +++ b/openstack/loadbalancer/v2/apiversions/requests.go @@ -0,0 +1,13 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List lists all the load balancer API versions available to end-users. +func List(c *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(c, apiVersionsURL(c), func(r pagination.PageResult) pagination.Page { + return APIVersionPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/loadbalancer/v2/apiversions/results.go b/openstack/loadbalancer/v2/apiversions/results.go new file mode 100644 index 0000000000..b031cb8236 --- /dev/null +++ b/openstack/loadbalancer/v2/apiversions/results.go @@ -0,0 +1,32 @@ +package apiversions + +import "github.com/gophercloud/gophercloud/pagination" + +// APIVersion represents an API version for load balancer. It contains +// the status of the API, and its unique ID. +type APIVersion struct { + Status string `son:"status"` + ID string `json:"id"` +} + +// APIVersionPage is the page returned by a pager when traversing over a +// collection of API versions. +type APIVersionPage struct { + pagination.SinglePageBase +} + +// IsEmpty checks whether an APIVersionPage struct is empty. +func (r APIVersionPage) IsEmpty() (bool, error) { + is, err := ExtractAPIVersions(r) + return len(is) == 0, err +} + +// ExtractAPIVersions takes a collection page, extracts all of the elements, +// and returns them a slice of APIVersion structs. It is effectively a cast. +func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { + var s struct { + Versions []APIVersion `json:"versions"` + } + err := (r.(APIVersionPage)).ExtractInto(&s) + return s.Versions, err +} diff --git a/openstack/loadbalancer/v2/apiversions/testing/doc.go b/openstack/loadbalancer/v2/apiversions/testing/doc.go new file mode 100644 index 0000000000..cc76de0a62 --- /dev/null +++ b/openstack/loadbalancer/v2/apiversions/testing/doc.go @@ -0,0 +1,2 @@ +// apiversions unit tests +package testing diff --git a/openstack/loadbalancer/v2/apiversions/testing/fixture.go b/openstack/loadbalancer/v2/apiversions/testing/fixture.go new file mode 100644 index 0000000000..4c78c58cbc --- /dev/null +++ b/openstack/loadbalancer/v2/apiversions/testing/fixture.go @@ -0,0 +1,93 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const OctaviaAllAPIVersionsResponse = ` +{ + "versions": [ + { + "id": "v1", + "links": [ + { + "href": "http://10.0.0.105:9876/v1", + "rel": "self" + } + ], + "status": "DEPRECATED", + "updated": "2014-12-11T00:00:00Z" + }, + { + "id": "v2.0", + "links": [ + { + "href": "http://10.0.0.105:9876/v2", + "rel": "self" + } + ], + "status": "SUPPORTED", + "updated": "2016-12-11T00:00:00Z" + }, + { + "id": "v2.1", + "links": [ + { + "href": "http://10.0.0.105:9876/v2", + "rel": "self" + } + ], + "status": "SUPPORTED", + "updated": "2018-04-20T00:00:00Z" + }, + { + "id": "v2.2", + "links": [ + { + "href": "http://10.0.0.105:9876/v2", + "rel": "self" + } + ], + "status": "CURRENT", + "updated": "2018-07-31T00:00:00Z" + } + ] +} +` + +var OctaviaAllAPIVersionResults = []apiversions.APIVersion{ + apiversions.APIVersion{ + ID: "v1", + Status: "DEPRECATED", + }, + apiversions.APIVersion{ + ID: "v2.0", + Status: "SUPPORTED", + }, + apiversions.APIVersion{ + ID: "v2.1", + Status: "SUPPORTED", + }, + apiversions.APIVersion{ + ID: "v2.2", + Status: "CURRENT", + }, +} + +func MockListResponse(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", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, OctaviaAllAPIVersionsResponse) + }) +} diff --git a/openstack/loadbalancer/v2/apiversions/testing/requests_test.go b/openstack/loadbalancer/v2/apiversions/testing/requests_test.go new file mode 100644 index 0000000000..016c64da72 --- /dev/null +++ b/openstack/loadbalancer/v2/apiversions/testing/requests_test.go @@ -0,0 +1,24 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListVersions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + + actual, err := apiversions.ExtractAPIVersions(allVersions) + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, OctaviaAllAPIVersionResults, actual) +} diff --git a/openstack/loadbalancer/v2/apiversions/urls.go b/openstack/loadbalancer/v2/apiversions/urls.go new file mode 100644 index 0000000000..0205405a04 --- /dev/null +++ b/openstack/loadbalancer/v2/apiversions/urls.go @@ -0,0 +1,7 @@ +package apiversions + +import "github.com/gophercloud/gophercloud" + +func apiVersionsURL(c *gophercloud.ServiceClient) string { + return c.Endpoint +} From 33e54f40ffcfee569ae243b8d1d137d5363b4a28 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 28 Mar 2019 09:06:03 -0600 Subject: [PATCH 0712/2296] Fix apiversions base URLs (#1530) * Block Storage: Update apiversions for base endpoint * ContainerInfra: Update apiversions for base endpoint * LoadBalancer: Update apiversions for base endpoint * Networking: Update apiversions for base endpoint * Orchestration: Update apiversions for base endpoint * Shared Filesystem: Update apiversions for base endpoint --- .../openstack/networking/v2/apiversion_test.go | 2 +- openstack/blockstorage/v1/apiversions/urls.go | 12 +++++++----- openstack/containerinfra/apiversions/urls.go | 14 +++++++------- openstack/loadbalancer/v2/apiversions/requests.go | 2 +- openstack/loadbalancer/v2/apiversions/urls.go | 13 ++++++++++--- openstack/networking/v2/apiversions/requests.go | 4 ++-- openstack/networking/v2/apiversions/urls.go | 13 +++++++++---- openstack/orchestration/v1/apiversions/requests.go | 2 +- openstack/orchestration/v1/apiversions/urls.go | 13 ++++++++++--- openstack/sharedfilesystems/apiversions/urls.go | 14 +++++++------- 10 files changed, 55 insertions(+), 34 deletions(-) diff --git a/acceptance/openstack/networking/v2/apiversion_test.go b/acceptance/openstack/networking/v2/apiversion_test.go index c6f8f261b3..2fb4a23210 100644 --- a/acceptance/openstack/networking/v2/apiversion_test.go +++ b/acceptance/openstack/networking/v2/apiversion_test.go @@ -39,7 +39,7 @@ func TestAPIResourcesList(t *testing.T) { allPages, err := apiversions.ListVersionResources(client, "v2.0").AllPages() if err != nil { - t.Fatalf("Unable to list api version reosources: %v", err) + t.Fatalf("Unable to list api version resources: %v", err) } allVersionResources, err := apiversions.ExtractVersionResources(allPages) diff --git a/openstack/blockstorage/v1/apiversions/urls.go b/openstack/blockstorage/v1/apiversions/urls.go index d1861ac196..41aebdc5f2 100644 --- a/openstack/blockstorage/v1/apiversions/urls.go +++ b/openstack/blockstorage/v1/apiversions/urls.go @@ -1,18 +1,20 @@ package apiversions import ( - "net/url" "strings" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { - return c.ServiceURL(strings.TrimRight(version, "/") + "/") + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" + return endpoint } func listURL(c *gophercloud.ServiceClient) string { - u, _ := url.Parse(c.ServiceURL("")) - u.Path = "/" - return u.String() + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint } diff --git a/openstack/containerinfra/apiversions/urls.go b/openstack/containerinfra/apiversions/urls.go index 6a30ca91e2..41aebdc5f2 100644 --- a/openstack/containerinfra/apiversions/urls.go +++ b/openstack/containerinfra/apiversions/urls.go @@ -1,20 +1,20 @@ package apiversions import ( - "net/url" "strings" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { - u, _ := url.Parse(c.ServiceURL("")) - u.Path = "/" + strings.TrimRight(version, "/") + "/" - return u.String() + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" + return endpoint } func listURL(c *gophercloud.ServiceClient) string { - u, _ := url.Parse(c.ServiceURL("")) - u.Path = "/" - return u.String() + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint } diff --git a/openstack/loadbalancer/v2/apiversions/requests.go b/openstack/loadbalancer/v2/apiversions/requests.go index ed71797ccf..18c88de077 100644 --- a/openstack/loadbalancer/v2/apiversions/requests.go +++ b/openstack/loadbalancer/v2/apiversions/requests.go @@ -7,7 +7,7 @@ import ( // List lists all the load balancer API versions available to end-users. func List(c *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(c, apiVersionsURL(c), func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } diff --git a/openstack/loadbalancer/v2/apiversions/urls.go b/openstack/loadbalancer/v2/apiversions/urls.go index 0205405a04..a6a35d4225 100644 --- a/openstack/loadbalancer/v2/apiversions/urls.go +++ b/openstack/loadbalancer/v2/apiversions/urls.go @@ -1,7 +1,14 @@ package apiversions -import "github.com/gophercloud/gophercloud" +import ( + "strings" -func apiVersionsURL(c *gophercloud.ServiceClient) string { - return c.Endpoint + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" +) + +func listURL(c *gophercloud.ServiceClient) string { + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint } diff --git a/openstack/networking/v2/apiversions/requests.go b/openstack/networking/v2/apiversions/requests.go index c5494fb357..0d027be8b6 100644 --- a/openstack/networking/v2/apiversions/requests.go +++ b/openstack/networking/v2/apiversions/requests.go @@ -7,7 +7,7 @@ import ( // ListVersions lists all the Neutron API versions available to end-users. func ListVersions(c *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(c, apiVersionsURL(c), func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } @@ -16,7 +16,7 @@ func ListVersions(c *gophercloud.ServiceClient) pagination.Pager { // particular API versions. Typical resources for Neutron might be: networks, // subnets, etc. func ListVersionResources(c *gophercloud.ServiceClient, v string) pagination.Pager { - return pagination.NewPager(c, apiInfoURL(c, v), func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(c, getURL(c, v), func(r pagination.PageResult) pagination.Page { return APIVersionResourcePage{pagination.SinglePageBase(r)} }) } diff --git a/openstack/networking/v2/apiversions/urls.go b/openstack/networking/v2/apiversions/urls.go index 0fa743776d..41aebdc5f2 100644 --- a/openstack/networking/v2/apiversions/urls.go +++ b/openstack/networking/v2/apiversions/urls.go @@ -4,12 +4,17 @@ import ( "strings" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" ) -func apiVersionsURL(c *gophercloud.ServiceClient) string { - return c.Endpoint +func getURL(c *gophercloud.ServiceClient, version string) string { + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" + return endpoint } -func apiInfoURL(c *gophercloud.ServiceClient, version string) string { - return c.Endpoint + strings.TrimRight(version, "/") + "/" +func listURL(c *gophercloud.ServiceClient) string { + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint } diff --git a/openstack/orchestration/v1/apiversions/requests.go b/openstack/orchestration/v1/apiversions/requests.go index ff383cff68..f0cd34e2a0 100644 --- a/openstack/orchestration/v1/apiversions/requests.go +++ b/openstack/orchestration/v1/apiversions/requests.go @@ -7,7 +7,7 @@ import ( // ListVersions lists all the Neutron API versions available to end-users func ListVersions(c *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(c, apiVersionsURL(c), func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { return APIVersionPage{pagination.SinglePageBase(r)} }) } diff --git a/openstack/orchestration/v1/apiversions/urls.go b/openstack/orchestration/v1/apiversions/urls.go index 0205405a04..a6a35d4225 100644 --- a/openstack/orchestration/v1/apiversions/urls.go +++ b/openstack/orchestration/v1/apiversions/urls.go @@ -1,7 +1,14 @@ package apiversions -import "github.com/gophercloud/gophercloud" +import ( + "strings" -func apiVersionsURL(c *gophercloud.ServiceClient) string { - return c.Endpoint + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" +) + +func listURL(c *gophercloud.ServiceClient) string { + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint } diff --git a/openstack/sharedfilesystems/apiversions/urls.go b/openstack/sharedfilesystems/apiversions/urls.go index 6a30ca91e2..41aebdc5f2 100644 --- a/openstack/sharedfilesystems/apiversions/urls.go +++ b/openstack/sharedfilesystems/apiversions/urls.go @@ -1,20 +1,20 @@ package apiversions import ( - "net/url" "strings" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { - u, _ := url.Parse(c.ServiceURL("")) - u.Path = "/" + strings.TrimRight(version, "/") + "/" - return u.String() + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" + return endpoint } func listURL(c *gophercloud.ServiceClient) string { - u, _ := url.Parse(c.ServiceURL("")) - u.Path = "/" - return u.String() + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint } From 4d3066f119fa80ac92c713ac747573df3e05ea28 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Fri, 29 Mar 2019 21:38:20 -0400 Subject: [PATCH 0713/2296] Baremetal API V1: Allocations (#1528) --- .../baremetal/noauth/allocations_test.go | 45 +++++ .../baremetal/v1/allocations_test.go | 44 +++++ .../openstack/baremetal/v1/baremetal.go | 24 +++ .../openstack/baremetal/v1/nodes_test.go | 2 + .../baremetal/v1/allocations/requests.go | 131 ++++++++++++++ openstack/baremetal/v1/allocations/results.go | 114 ++++++++++++ .../v1/allocations/testing/fixtures.go | 168 ++++++++++++++++++ .../v1/allocations/testing/requests_test.go | 79 ++++++++ openstack/baremetal/v1/allocations/urls.go | 23 +++ 9 files changed, 630 insertions(+) create mode 100644 acceptance/openstack/baremetal/noauth/allocations_test.go create mode 100644 acceptance/openstack/baremetal/v1/allocations_test.go create mode 100644 openstack/baremetal/v1/allocations/requests.go create mode 100644 openstack/baremetal/v1/allocations/results.go create mode 100644 openstack/baremetal/v1/allocations/testing/fixtures.go create mode 100644 openstack/baremetal/v1/allocations/testing/requests_test.go create mode 100644 openstack/baremetal/v1/allocations/urls.go diff --git a/acceptance/openstack/baremetal/noauth/allocations_test.go b/acceptance/openstack/baremetal/noauth/allocations_test.go new file mode 100644 index 0000000000..dc66a107bd --- /dev/null +++ b/acceptance/openstack/baremetal/noauth/allocations_test.go @@ -0,0 +1,45 @@ +// +build acceptance baremetal allocations + +package noauth + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestAllocationsCreateDestroy(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1NoAuthClient() + th.AssertNoErr(t, err) + + client.Microversion = "1.52" + + allocation, err := v1.CreateAllocation(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteAllocation(t, client, allocation) + + found := false + err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + allocationList, err := allocations.ExtractAllocations(page) + if err != nil { + return false, err + } + + for _, a := range allocationList { + if a.UUID == allocation.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, found, true) +} diff --git a/acceptance/openstack/baremetal/v1/allocations_test.go b/acceptance/openstack/baremetal/v1/allocations_test.go new file mode 100644 index 0000000000..e342dc1fe6 --- /dev/null +++ b/acceptance/openstack/baremetal/v1/allocations_test.go @@ -0,0 +1,44 @@ +// +build acceptance baremetal allocations + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestAllocationsCreateDestroy(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.52" + + allocation, err := CreateAllocation(t, client) + th.AssertNoErr(t, err) + defer DeleteAllocation(t, client, allocation) + + found := false + err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + allocationList, err := allocations.ExtractAllocations(page) + if err != nil { + return false, err + } + + for _, a := range allocationList { + if a.UUID == allocation.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, found, true) +} diff --git a/acceptance/openstack/baremetal/v1/baremetal.go b/acceptance/openstack/baremetal/v1/baremetal.go index c4f717a866..bce8da5c56 100644 --- a/acceptance/openstack/baremetal/v1/baremetal.go +++ b/acceptance/openstack/baremetal/v1/baremetal.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" ) @@ -41,6 +42,29 @@ func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Nod t.Logf("Deleted server: %s", node.UUID) } +// CreateAllocation creates an allocation +func CreateAllocation(t *testing.T, client *gophercloud.ServiceClient) (*allocations.Allocation, error) { + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create bare metal allocation: %s", name) + + allocation, err := allocations.Create(client, allocations.CreateOpts{ + Name: name, + ResourceClass: "baremetal", + }).Extract() + + return allocation, err +} + +// DeleteAllocation deletes a bare metal allocation via its UUID. +func DeleteAllocation(t *testing.T, client *gophercloud.ServiceClient, allocation *allocations.Allocation) { + err := allocations.Delete(client, allocation.UUID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete allocation %s: %s", allocation.UUID, err) + } + + t.Logf("Deleted allocation: %s", allocation.UUID) +} + // CreateFakeNode creates a node with fake-hardware to use for port tests. func CreateFakeNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, error) { name := tools.RandomString("ACPTTEST", 16) diff --git a/acceptance/openstack/baremetal/v1/nodes_test.go b/acceptance/openstack/baremetal/v1/nodes_test.go index 6654011a58..2d12904174 100644 --- a/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/acceptance/openstack/baremetal/v1/nodes_test.go @@ -1,3 +1,5 @@ +// +build acceptance baremetal nodes + package v1 import ( diff --git a/openstack/baremetal/v1/allocations/requests.go b/openstack/baremetal/v1/allocations/requests.go new file mode 100644 index 0000000000..7acb0021a7 --- /dev/null +++ b/openstack/baremetal/v1/allocations/requests.go @@ -0,0 +1,131 @@ +package allocations + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToAllocationCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies allocation creation parameters +type CreateOpts struct { + // The requested resource class for the allocation. + ResourceClass string `json:"resource_class" required:"true"` + + // The list of nodes (names or UUIDs) that should be considered for this allocation. If not provided, all available nodes will be considered. + CandidateNodes []string `json:"candidate_nodes,omitempty"` + + // The unique name of the Allocation. + Name string `json:"name,omitempty"` + + // The list of requested traits for the allocation. + Traits []string `json:"traits,omitempty"` + + // The UUID for the resource. + UUID string `json:"uuid,omitempty"` + + // A set of one or more arbitrary metadata key and value pairs. + Extra map[string]string `json:"extra,omitempty"` +} + +// ToAllocationCreateMap assembles a request body based on the contents of a CreateOpts. +func (opts CreateOpts) ToAllocationCreateMap() (map[string]interface{}, error) { + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return body, nil +} + +// Create requests a node to be created +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + reqBody, err := opts.ToAllocationCreateMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(createURL(client), reqBody, &r.Body, nil) + return +} + +type AllocationState string + +var ( + Allocating AllocationState = "allocating" + Active = "active" + Error = "error" +) + +// ListOptsBuilder allows extensions to add additional parameters to the List request. +type ListOptsBuilder interface { + ToAllocationListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through the API. +type ListOpts struct { + // Filter the list of allocations by the node UUID or name. + Node string `q:"node"` + + // Filter the list of returned nodes, and only return the ones with the specified resource class. + ResourceClass string `q:"resource_class"` + + // Filter the list of allocations by the allocation state, one of active, allocating or error. + State AllocationState `q:"state"` + + // 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. + // Valid value is asc (ascending) or desc (descending). Default is asc. + SortDir string `q:"sort_dir"` + + // Sorts the response by the this attribute value. Default is id. + SortKey string `q:"sort_key"` +} + +// ToAllocationListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToAllocationListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list allocations accessible to you. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToAllocationListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AllocationPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get requests the details of an allocation by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Delete requests the deletion of an allocation +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} diff --git a/openstack/baremetal/v1/allocations/results.go b/openstack/baremetal/v1/allocations/results.go new file mode 100644 index 0000000000..cbd2115523 --- /dev/null +++ b/openstack/baremetal/v1/allocations/results.go @@ -0,0 +1,114 @@ +package allocations + +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type Allocation struct { + // The UUID for the resource. + UUID string `json:"uuid"` + + // A list of UUIDs of the nodes that are candidates for this allocation. + CandidateNodes []string `json:"candidate_nodes"` + + // The error message for the allocation if it is in the error state, null otherwise. + LastError string `json:"last_error"` + + // The unique name of the allocation. + Name string `json:"name"` + + // The UUID of the node assigned to the allocation. Will be null if a node is not yet assigned. + NodeUUID string `json:"node_uuid"` + + // The current state of the allocation. One of: allocation, active, error + State string `json:"state"` + + // The resource class requested for the allocation. + ResourceClass string `json:"resource_class"` + + // The list of the traits requested for the allocation. + Traits []string `json:"traits"` + + // A set of one or more arbitrary metadata key and value pairs. + Extra map[string]string `json:"extra"` + + // 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"` + + // A list of relative links. Includes the self and bookmark links. + Links []interface{} `json:"links"` +} + +type allocationResult struct { + gophercloud.Result +} + +func (r allocationResult) Extract() (*Allocation, error) { + var s Allocation + err := r.ExtractInto(&s) + return &s, err +} + +func (r allocationResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "") +} + +func ExtractAllocationsInto(r pagination.Page, v interface{}) error { + return r.(AllocationPage).Result.ExtractIntoSlicePtr(v, "allocations") +} + +// AllocationPage abstracts the raw results of making a List() request against +// the API. +type AllocationPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a page contains no Allocation results. +func (r AllocationPage) IsEmpty() (bool, error) { + s, err := ExtractAllocations(r) + return len(s) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r AllocationPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"allocations_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractAllocations interprets the results of a single page from a List() call, +// producing a slice of Allocation entities. +func ExtractAllocations(r pagination.Page) ([]Allocation, error) { + var s []Allocation + err := ExtractAllocationsInto(r, &s) + return s, err +} + +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as a Allocation. +type GetResult struct { + allocationResult +} + +// CreateResult is the response from a Create operation. +type CreateResult struct { + allocationResult +} + +// 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/baremetal/v1/allocations/testing/fixtures.go b/openstack/baremetal/v1/allocations/testing/fixtures.go new file mode 100644 index 0000000000..340a7c5c17 --- /dev/null +++ b/openstack/baremetal/v1/allocations/testing/fixtures.go @@ -0,0 +1,168 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const AllocationListBody = ` +{ + "allocations": [ + { + "candidate_nodes": [], + "created_at": "2019-02-20T09:43:58+00:00", + "extra": {}, + "last_error": null, + "links": [ + { + "href": "http://127.0.0.1:6385/v1/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", + "rel": "bookmark" + } + ], + "name": "allocation-1", + "node_uuid": "6d85703a-565d-469a-96ce-30b6de53079d", + "resource_class": "bm-large", + "state": "active", + "traits": [], + "updated_at": "2019-02-20T09:43:58+00:00", + "uuid": "5344a3e2-978a-444e-990a-cbf47c62ef88" + }, + { + "candidate_nodes": [], + "created_at": "2019-02-20T09:43:58+00:00", + "extra": {}, + "last_error": "Failed to process allocation eff80f47-75f0-4d41-b1aa-cf07c201adac: no available nodes match the resource class bm-large.", + "links": [ + { + "href": "http://127.0.0.1:6385/v1/allocations/eff80f47-75f0-4d41-b1aa-cf07c201adac", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/allocations/eff80f47-75f0-4d41-b1aa-cf07c201adac", + "rel": "bookmark" + } + ], + "name": "allocation-2", + "node_uuid": null, + "resource_class": "bm-large", + "state": "error", + "traits": [ + "CUSTOM_GOLD" + ], + "updated_at": "2019-02-20T09:43:58+00:00", + "uuid": "eff80f47-75f0-4d41-b1aa-cf07c201adac" + } + ] +} +` + +const SingleAllocationBody = ` +{ + "candidate_nodes": ["344a3e2-978a-444e-990a-cbf47c62ef88"], + "created_at": "2019-02-20T09:43:58+00:00", + "extra": {}, + "last_error": null, + "links": [ + { + "href": "http://127.0.0.1:6385/v1/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", + "rel": "self" + }, + { + "href": "http://127.0.0.1:6385/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", + "rel": "bookmark" + } + ], + "name": "allocation-1", + "node_uuid": null, + "resource_class": "baremetal", + "state": "allocating", + "traits": ["foo"], + "updated_at": null, + "uuid": "5344a3e2-978a-444e-990a-cbf47c62ef88" +}` + +var ( + createdAt, _ = time.Parse(time.RFC3339, "2019-02-20T09:43:58+00:00") + + Allocation1 = allocations.Allocation{ + UUID: "5344a3e2-978a-444e-990a-cbf47c62ef88", + CandidateNodes: []string{"344a3e2-978a-444e-990a-cbf47c62ef88"}, + Name: "allocation-1", + State: "allocating", + ResourceClass: "baremetal", + Traits: []string{"foo"}, + Extra: map[string]string{}, + CreatedAt: createdAt, + Links: []interface{}{map[string]interface{}{"href": "http://127.0.0.1:6385/v1/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "self"}, map[string]interface{}{"href": "http://127.0.0.1:6385/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "bookmark"}}, + } +) + +// HandleAllocationListSuccessfully sets up the test server to respond to a allocation List request. +func HandleAllocationListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/allocations", 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, AllocationListBody) + + case "eff80f47-75f0-4d41-b1aa-cf07c201adac": + fmt.Fprintf(w, `{ "allocations": [] }`) + default: + t.Fatalf("/allocations invoked with unexpected marker=[%s]", marker) + } + }) +} + +// HandleAllocationCreationSuccessfully sets up the test server to respond to a allocation creation request +// with a given response. +func HandleAllocationCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/allocations", 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, `{ + "name": "allocation-1", + "resource_class": "baremetal", + "candidate_nodes": ["344a3e2-978a-444e-990a-cbf47c62ef88"], + "traits": ["foo"] + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + +// HandleAllocationDeletionSuccessfully sets up the test server to respond to a allocation deletion request. +func HandleAllocationDeletionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/allocations/344a3e2-978a-444e-990a-cbf47c62ef88", 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 HandleAllocationGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/allocations/344a3e2-978a-444e-990a-cbf47c62ef88", 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, SingleAllocationBody) + }) +} diff --git a/openstack/baremetal/v1/allocations/testing/requests_test.go b/openstack/baremetal/v1/allocations/testing/requests_test.go new file mode 100644 index 0000000000..6d7b228d08 --- /dev/null +++ b/openstack/baremetal/v1/allocations/testing/requests_test.go @@ -0,0 +1,79 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAllocations(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAllocationListSuccessfully(t) + + pages := 0 + err := allocations.List(client.ServiceClient(), allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := allocations.ExtractAllocations(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 allocations, got %d", len(actual)) + } + th.AssertEquals(t, "5344a3e2-978a-444e-990a-cbf47c62ef88", actual[0].UUID) + th.AssertEquals(t, "eff80f47-75f0-4d41-b1aa-cf07c201adac", actual[1].UUID) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestCreateAllocation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAllocationCreationSuccessfully(t, SingleAllocationBody) + + actual, err := allocations.Create(client.ServiceClient(), allocations.CreateOpts{ + Name: "allocation-1", + ResourceClass: "baremetal", + CandidateNodes: []string{"344a3e2-978a-444e-990a-cbf47c62ef88"}, + Traits: []string{"foo"}, + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, Allocation1, *actual) +} + +func TestDeleteAllocation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAllocationDeletionSuccessfully(t) + + res := allocations.Delete(client.ServiceClient(), "344a3e2-978a-444e-990a-cbf47c62ef88") + th.AssertNoErr(t, res.Err) +} + +func TestGetAllocation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAllocationGetSuccessfully(t) + + c := client.ServiceClient() + actual, err := allocations.Get(c, "344a3e2-978a-444e-990a-cbf47c62ef88").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, Allocation1, *actual) +} diff --git a/openstack/baremetal/v1/allocations/urls.go b/openstack/baremetal/v1/allocations/urls.go new file mode 100644 index 0000000000..7163bbe334 --- /dev/null +++ b/openstack/baremetal/v1/allocations/urls.go @@ -0,0 +1,23 @@ +package allocations + +import "github.com/gophercloud/gophercloud" + +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("allocations") +} + +func listURL(client *gophercloud.ServiceClient) string { + return createURL(client) +} + +func resourceURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("allocations", id) +} + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return resourceURL(client, id) +} + +func getURL(client *gophercloud.ServiceClient, id string) string { + return resourceURL(client, id) +} From 35c7fd233bfd9854ce9b90257c5dfbb7af5df967 Mon Sep 17 00:00:00 2001 From: Kien Nguyen-Tuan Date: Fri, 5 Apr 2019 21:39:50 +0700 Subject: [PATCH 0714/2296] Add extra Heat list stacks function parameters (#1534) For #1533 Signed-off-by: Kien Nguyen --- openstack/orchestration/v1/stacks/requests.go | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index d874bd8d8f..d7fd4f16fd 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -214,16 +214,54 @@ type ListOptsBuilder interface { // 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 network attributes you want to see returned. SortKey allows you to sort -// by a particular network attribute. SortDir sets the direction, and is either -// `asc' or `desc'. Marker and Limit are used for pagination. +// the network attributes you want to see returned. type ListOpts struct { - Status string `q:"status"` - Name string `q:"name"` - Marker string `q:"marker"` - Limit int `q:"limit"` + // TenantID is the UUID of the tenant. A tenant is also known as + // a project. + TenantID string `q:"tenant_id"` + + // ID filters the stack list by a stack ID + ID string `q:"id"` + + // Status filters the stack list by a status. + Status string `q:"status"` + + // Name filters the stack list by a name. + Name string `q:"name"` + + // Marker is the ID of last-seen item. + Marker string `q:"marker"` + + // Limit is an integer value for the limit of values to return. + Limit int `q:"limit"` + + // SortKey allows you to sort by stack_name, stack_status, creation_time, or + // update_time key. SortKey SortKey `q:"sort_keys"` + + // SortDir sets the direction, and is either `asc` or `desc`. SortDir SortDir `q:"sort_dir"` + + // AllTenants is a bool to show all tenants. + AllTenants bool `q:"global_tenant"` + + // ShowDeleted set to `true` to include deleted stacks in the list. + ShowDeleted bool `q:"show_deleted"` + + // ShowNested set to `true` to include nested stacks in the list. + ShowNested bool `q:"show_nested"` + + // Tags lists stacks that contain one or more simple string tags. + Tags string `q:"tags"` + + // TagsAny lists stacks that contain one or more simple string tags. + TagsAny string `q:"tags_any"` + + // NotTags lists stacks that do not contain one or more simple string tags. + NotTags string `q:"not_tags"` + + // NotTagsAny lists stacks that do not contain one or more simple string tags. + NotTagsAny string `q:"not_tags_any"` } // ToStackListQuery formats a ListOpts into a query string. From 6c61c88383e41a300f10777cebf3b8d31c91b0e3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 6 Apr 2019 14:11:14 -0600 Subject: [PATCH 0715/2296] Core: Updating docs (#1540) --- doc.go | 63 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/doc.go b/doc.go index 131cc8e303..953ca822a9 100644 --- a/doc.go +++ b/doc.go @@ -9,20 +9,37 @@ Provider structs represent the cloud providers that offer and manage a collection of services. You will generally want to create one Provider client per OpenStack cloud. + It is now recommended to use the `clientconfig` package found at + https://github.com/gophercloud/utils/tree/master/openstack/clientconfig + for all authentication purposes. + + The below documentation is still relevant. clientconfig simply implements + the below and presents it in an easier and more flexible way. + Use your OpenStack credentials to create a Provider client. The IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in information provided by the cloud operator. Additionally, the cloud may refer to TenantID or TenantName as project_id and project_name. Credentials are specified like so: - opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - Username: "{username}", - Password: "{password}", - TenantID: "{tenant_id}", - } + 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) +You can authenticate with a token by doing: + + opts := gophercloud.AuthOptions{ + IdentityEndpoint: "https://openstack.example.com:5000/v2.0", + TokenID: "{token_id}", + TenantID: "{tenant_id}", + } + + provider, err := openstack.AuthenticatedClient(opts) You may also use the openstack.AuthOptionsFromEnv() helper function. This function reads in standard environment variables frequently found in an @@ -39,16 +56,16 @@ operations for a particular OpenStack service. Examples of services include: Compute, Object Storage, Block Storage. In order to define one, you need to pass in the parent provider, like so: - opts := gophercloud.EndpointOpts{Region: "RegionOne"} + opts := gophercloud.EndpointOpts{Region: "RegionOne"} - client, err := openstack.NewComputeV2(provider, opts) + client, err := openstack.NewComputeV2(provider, opts) Resources Resource structs are the domain models that services make use of in order to work with and represent the state of API resources: - server, err := servers.Get(client, "{serverId}").Extract() + server, err := servers.Get(client, "{serverId}").Extract() Intermediate Result structs are returned for API operations, which allow generic access to the HTTP headers, response body, and any errors associated @@ -56,11 +73,11 @@ with the network transaction. To turn a result into a usable resource struct, you must call the Extract method which is chained to the response, or an Extract function from an applicable extension: - result := servers.Get(client, "{serverId}") + result := servers.Get(client, "{serverId}") - // Attempt to extract the disk configuration from the OS-DCF disk config - // extension: - config, err := diskconfig.ExtractGet(result) + // Attempt to extract the disk configuration from the OS-DCF disk config + // extension: + config, err := diskconfig.ExtractGet(result) All requests that enumerate a collection return a Pager struct that is used to iterate through the results one page at a time. Use the EachPage method on that @@ -68,17 +85,17 @@ Pager to handle each successive Page in a closure, then use the appropriate extraction method from that request's package to interpret that Page as a slice of results: - err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) { - s, err := servers.ExtractServers(page) - if err != nil { - return false, err - } + err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) { + s, err := servers.ExtractServers(page) + if err != nil { + return false, err + } - // Handle the []servers.Server slice. + // Handle the []servers.Server slice. - // Return "false" or an error to prematurely stop fetching new pages. - return true, nil - }) + // Return "false" or an error to prematurely stop fetching new pages. + return true, nil + }) If you want to obtain the entire collection of pages without doing any intermediary processing on each page, you can use the AllPages method: From 84f87ef22a90a6cfcda118cabd08d272ce689c11 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 8 Apr 2019 09:52:19 -0600 Subject: [PATCH 0716/2296] Networking v2: Allow DNS Nameservers to be cleared (#1536) --- .../openstack/networking/v2/subnets_test.go | 47 +++++++++++++++++++ openstack/networking/v2/subnets/doc.go | 3 +- openstack/networking/v2/subnets/requests.go | 2 +- .../v2/subnets/testing/requests_test.go | 3 +- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index e7d55b4f44..fafc1cb532 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -217,3 +217,50 @@ func TestSubnetsWithSubnetPoolPrefixlen(t *testing.T) { t.Fatalf("Got invalid prefix length for subnet '%s': wanted 12 but got '%s'", subnet.ID, cidrParts[1]) } } + +func TestSubnetDNSNameservers(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) + + // Update Subnet + dnsNameservers := []string{"1.1.1.1"} + updateOpts := subnets.UpdateOpts{ + DNSNameservers: &dnsNameservers, + } + _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + // Get subnet + newSubnet, err := subnets.Get(client, subnet.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newSubnet) + th.AssertEquals(t, len(newSubnet.DNSNameservers), 1) + + // Update Subnet again + dnsNameservers = []string{} + updateOpts = subnets.UpdateOpts{ + DNSNameservers: &dnsNameservers, + } + _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + // Get subnet + newSubnet, err = subnets.Get(client, subnet.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newSubnet) + th.AssertEquals(t, len(newSubnet.DNSNameservers), 0) +} diff --git a/openstack/networking/v2/subnets/doc.go b/openstack/networking/v2/subnets/doc.go index d0ed8dff06..93fd21f2ae 100644 --- a/openstack/networking/v2/subnets/doc.go +++ b/openstack/networking/v2/subnets/doc.go @@ -97,10 +97,11 @@ Example to Create a Subnet With a Default Gateway Example to Update a Subnet subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" + dnsNameservers := []string{"8.8.8.8"} updateOpts := subnets.UpdateOpts{ Name: "new_name", - DNSNameservers: []string{"8.8.8.8}, + DNSNameservers: &dnsNameservers, } 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 d2c4a29f7c..b56d3cb677 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -188,7 +188,7 @@ type UpdateOpts struct { GatewayIP *string `json:"gateway_ip,omitempty"` // DNSNameservers are the nameservers to be set via DHCP. - DNSNameservers []string `json:"dns_nameservers,omitempty"` + DNSNameservers *[]string `json:"dns_nameservers,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/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index abcc3dd781..986ba0ba81 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -417,9 +417,10 @@ func TestUpdate(t *testing.T) { fmt.Fprintf(w, SubnetUpdateResponse) }) + dnsNameservers := []string{"foo"} opts := subnets.UpdateOpts{ Name: "my_new_subnet", - DNSNameservers: []string{"foo"}, + DNSNameservers: &dnsNameservers, HostRoutes: &[]subnets.HostRoute{ {NextHop: "bar"}, }, From 25776bfb84350b95e3cd6e2cfdcc5452e0185c64 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 8 Apr 2019 09:52:42 -0600 Subject: [PATCH 0717/2296] KeyManager v1: Adding Expiration to Secrets (#1537) --- acceptance/openstack/keymanager/v1/keymanager.go | 3 +++ openstack/keymanager/v1/secrets/requests.go | 14 +++++++++++++- .../keymanager/v1/secrets/testing/fixtures.go | 3 ++- .../keymanager/v1/secrets/testing/requests_test.go | 3 +++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/keymanager/v1/keymanager.go b/acceptance/openstack/keymanager/v1/keymanager.go index 99a85f9687..c7c72ef7c2 100644 --- a/acceptance/openstack/keymanager/v1/keymanager.go +++ b/acceptance/openstack/keymanager/v1/keymanager.go @@ -470,6 +470,7 @@ func CreateSecretWithPayload(t *testing.T, client *gophercloud.ServiceClient, pa t.Logf("Attempting to create secret %s", secretName) + expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, @@ -478,6 +479,7 @@ func CreateSecretWithPayload(t *testing.T, client *gophercloud.ServiceClient, pa Payload: payload, PayloadContentType: "text/plain", SecretType: secrets.OpaqueSecret, + Expiration: &expiration, } secret, err := secrets.Create(client, createOpts).Extract() @@ -501,6 +503,7 @@ func CreateSecretWithPayload(t *testing.T, client *gophercloud.ServiceClient, pa th.AssertEquals(t, secret.Name, secretName) th.AssertEquals(t, secret.Algorithm, "aes") + th.AssertEquals(t, secret.Expiration, expiration) return secret, nil } diff --git a/openstack/keymanager/v1/secrets/requests.go b/openstack/keymanager/v1/secrets/requests.go index 900cbcaaa6..1b1dfc5b82 100644 --- a/openstack/keymanager/v1/secrets/requests.go +++ b/openstack/keymanager/v1/secrets/requests.go @@ -223,11 +223,23 @@ type CreateOpts struct { // SecretType is the type of secret. SecretType SecretType `json:"secret_type,omitempty"` + + // Expiration is the expiration date of the secret. + Expiration *time.Time `json:"-"` } // ToSecretCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToSecretCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "") + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if opts.Expiration != nil { + b["expiration"] = opts.Expiration.Format(gophercloud.RFC3339NoZ) + } + + return b, nil } // Create creates a new secrets. diff --git a/openstack/keymanager/v1/secrets/testing/fixtures.go b/openstack/keymanager/v1/secrets/testing/fixtures.go index 2b50658b81..f62fa83608 100644 --- a/openstack/keymanager/v1/secrets/testing/fixtures.go +++ b/openstack/keymanager/v1/secrets/testing/fixtures.go @@ -82,7 +82,8 @@ const CreateRequest = ` "name": "mysecret", "payload": "foobar", "payload_content_type": "text/plain", - "secret_type": "opaque" + "secret_type": "opaque", + "expiration": "2028-06-21T02:49:48" }` // CreateResponse provides a Create result. diff --git a/openstack/keymanager/v1/secrets/testing/requests_test.go b/openstack/keymanager/v1/secrets/testing/requests_test.go index d63879c942..0b486f800d 100644 --- a/openstack/keymanager/v1/secrets/testing/requests_test.go +++ b/openstack/keymanager/v1/secrets/testing/requests_test.go @@ -2,6 +2,7 @@ package testing import ( "testing" + "time" "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" "github.com/gophercloud/gophercloud/pagination" @@ -56,6 +57,7 @@ func TestCreateSecret(t *testing.T) { defer th.TeardownHTTP() HandleCreateSecretSuccessfully(t) + expiration := time.Date(2028, 6, 21, 2, 49, 48, 0, time.UTC) createOpts := secrets.CreateOpts{ Algorithm: "aes", BitLength: 256, @@ -64,6 +66,7 @@ func TestCreateSecret(t *testing.T) { Payload: "foobar", PayloadContentType: "text/plain", SecretType: secrets.OpaqueSecret, + Expiration: &expiration, } actual, err := secrets.Create(client.ServiceClient(), createOpts).Extract() From e04311112983ed9f6638f1eca3a0f996962e6900 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 8 Apr 2019 09:53:22 -0600 Subject: [PATCH 0718/2296] Compute v2: Account for empty cpu_info in hypervisors (#1541) --- .../v2/extensions/hypervisors/results.go | 8 ++- .../hypervisors/testing/fixtures.go | 72 +++++++++++++++++++ .../hypervisors/testing/requests_test.go | 12 ++++ 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index 7136da784b..5dab0f3d37 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -165,9 +165,11 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { return fmt.Errorf("CPUInfo has unexpected type: %T", t) } - err = json.Unmarshal(tmpb, &r.CPUInfo) - if err != nil { - return err + if len(tmpb) != 0 { + err = json.Unmarshal(tmpb, &r.CPUInfo) + if err != nil { + return err + } } // These fields may be returned as a scientific notation, so they need diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go index c1d0dd8970..34e61f5b78 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go @@ -221,6 +221,39 @@ const HypervisorGetBody = ` } ` +// HypervisorGetEmptyCPUInfoBody represents a raw hypervisor GET result with +// no cpu_info +const HypervisorGetEmptyCPUInfoBody = ` +{ + "hypervisor":{ + "cpu_info": "", + "current_workload":0, + "status":"enabled", + "state":"up", + "disk_available_least":0, + "host_ip":"1.1.1.1", + "free_disk_gb":1028, + "free_ram_mb":7680, + "hypervisor_hostname":"fake-mini", + "hypervisor_type":"fake", + "hypervisor_version":2002000, + "id":"c48f6247-abe4-4a24-824e-ea39e108874f", + "local_gb":1028, + "local_gb_used":0, + "memory_mb":8192, + "memory_mb_used":512, + "running_vms":0, + "service":{ + "host":"e6a37ee802d74863ab8b91ade8f12a67", + "id":"9c2566e7-7a54-4777-a1ae-c2662f0c407c", + "disabled_reason":null + }, + "vcpus":1, + "vcpus_used":0 + } +} +` + // HypervisorUptimeBody represents a raw hypervisor uptime request result with // Pike+ release. const HypervisorUptimeBody = ` @@ -275,6 +308,7 @@ var ( VCPUs: 1, VCPUsUsed: 0, } + HypervisorFake = hypervisors.Hypervisor{ CPUInfo: hypervisors.CPUInfo{ Arch: "x86_64", @@ -314,6 +348,33 @@ var ( VCPUs: 1, VCPUsUsed: 0, } + + HypervisorEmptyCPUInfo = hypervisors.Hypervisor{ + CurrentWorkload: 0, + Status: "enabled", + State: "up", + DiskAvailableLeast: 0, + HostIP: "1.1.1.1", + FreeDiskGB: 1028, + FreeRamMB: 7680, + HypervisorHostname: "fake-mini", + HypervisorType: "fake", + HypervisorVersion: 2002000, + ID: "c48f6247-abe4-4a24-824e-ea39e108874f", + LocalGB: 1028, + LocalGBUsed: 0, + MemoryMB: 8192, + MemoryMBUsed: 512, + RunningVMs: 0, + Service: hypervisors.Service{ + Host: "e6a37ee802d74863ab8b91ade8f12a67", + ID: "9c2566e7-7a54-4777-a1ae-c2662f0c407c", + DisabledReason: "", + }, + VCPUs: 1, + VCPUsUsed: 0, + } + HypervisorsStatisticsExpected = hypervisors.Statistics{ Count: 1, CurrentWorkload: 0, @@ -328,6 +389,7 @@ var ( VCPUs: 2, VCPUsUsed: 0, } + HypervisorUptimeExpected = hypervisors.Uptime{ HypervisorHostname: "fake-mini", ID: "c48f6247-abe4-4a24-824e-ea39e108874f", @@ -377,6 +439,16 @@ func HandleHypervisorGetSuccessfully(t *testing.T) { }) } +func HandleHypervisorGetEmptyCPUInfoSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, 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") + fmt.Fprintf(w, HypervisorGetEmptyCPUInfoBody) + }) +} + func HandleHypervisorUptimeSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID+"/uptime", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go index 417b05f200..21428d60a3 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go @@ -119,6 +119,18 @@ func TestGetHypervisor(t *testing.T) { testhelper.CheckDeepEquals(t, &expected, actual) } +func TestGetHypervisorEmptyCPUInfo(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleHypervisorGetEmptyCPUInfoSuccessfully(t) + + expected := HypervisorEmptyCPUInfo + + actual, err := hypervisors.Get(client.ServiceClient(), expected.ID).Extract() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, &expected, actual) +} + func TestHypervisorsUptime(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() From 21e0d66a03296e9406941af4313c7cae2756e844 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 8 Apr 2019 10:01:15 -0600 Subject: [PATCH 0719/2296] Object Storage v1: Enable removal of container metadata (#1538) This commit enables metadata to be removed from a container. Gophercloud internally strips empty strings from headers to enable deletion of headers before they are sent in the request. This conflicts with the ability to actually send an empty string when needed. Thereforer, a new UpdateOpt has been added that sends the special X-Remove header with a dummy value of "remove". --- .../objectstorage/v1/containers_test.go | 21 +++++++++++++++---- openstack/objectstorage/v1/containers/doc.go | 3 +++ .../objectstorage/v1/containers/requests.go | 7 +++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index 2b0319412d..8a86762f06 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -77,12 +77,25 @@ func TestContainers(t *testing.T) { th.AssertNoErr(t, updateres.Err) // After the tests are done, delete the metadata that was set. defer func() { - tempMap := make(map[string]string) - for k := range metadata { - tempMap[k] = "" + temp := []string{} + for k, _ := range metadata { + temp = append(temp, k) } - res := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: tempMap}) + res := containers.Update(client, cNames[0], &containers.UpdateOpts{RemoveMetadata: temp}) th.AssertNoErr(t, res.Err) + + // confirm the metadata was removed + getOpts := containers.GetOpts{ + Newest: true, + } + + cm, err := containers.Get(client, cNames[0], getOpts).ExtractMetadata() + th.AssertNoErr(t, err) + for k, _ := range metadata { + if _, ok := cm[k]; ok { + t.Errorf("Unexpected custom metadata with key: %s", k) + } + } }() // Retrieve a container's metadata. diff --git a/openstack/objectstorage/v1/containers/doc.go b/openstack/objectstorage/v1/containers/doc.go index 9e5f664198..ffc4f05297 100644 --- a/openstack/objectstorage/v1/containers/doc.go +++ b/openstack/objectstorage/v1/containers/doc.go @@ -73,6 +73,9 @@ Example to Update a Container Metadata: map[string]string{ "bar": "baz", }, + RemoveMetadata: []string{ + "foo", + }, } container, err := containers.Update(objectStorageClient, containerName, updateOpts).Extract() diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 89aa996839..ca99bb2a6a 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -130,6 +130,7 @@ type UpdateOptsBuilder interface { // deleting a container's metadata. type UpdateOpts struct { Metadata map[string]string + RemoveMetadata []string ContainerRead string `h:"X-Container-Read"` ContainerSyncTo string `h:"X-Container-Sync-To"` ContainerSyncKey string `h:"X-Container-Sync-Key"` @@ -148,9 +149,15 @@ func (opts UpdateOpts) ToContainerUpdateMap() (map[string]string, error) { if err != nil { return nil, err } + for k, v := range opts.Metadata { h["X-Container-Meta-"+k] = v } + + for _, k := range opts.RemoveMetadata { + h["X-Remove-Container-Meta-"+k] = "remove" + } + return h, nil } From 6c7ac67f8855bb8bdc19ef8356d303c02d976342 Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Mon, 8 Apr 2019 12:03:24 -0400 Subject: [PATCH 0720/2296] Remove imagePullPolicy from acceptance test (#1542) Zun is going to require admin privilege [1] to create resource with this field specified. Gophercloud acceptance test should use non-admin credential to test so we remove this field from acceptance test. [1] https://review.openstack.org/#/c/650585/ --- acceptance/openstack/container/v1/fixtures.go | 1 - 1 file changed, 1 deletion(-) diff --git a/acceptance/openstack/container/v1/fixtures.go b/acceptance/openstack/container/v1/fixtures.go index 9464af41f6..a9480d4b44 100644 --- a/acceptance/openstack/container/v1/fixtures.go +++ b/acceptance/openstack/container/v1/fixtures.go @@ -24,7 +24,6 @@ const capsuleTemplate = ` "ENV2": "/usr/bin" }, "image": "ubuntu", - "imagePullPolicy": "ifnotpresent", "ports": [ { "containerPort": 80, From 506b4894bad996f2c0cff60595ae82841f46109b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 6 Apr 2019 19:32:32 +0000 Subject: [PATCH 0721/2296] Networking v2: Allow Network names to be cleared --- acceptance/openstack/networking/v2/extensions/dns/dns_test.go | 2 +- .../v2/extensions/vlantransparent/vlantransparent.go | 2 +- acceptance/openstack/networking/v2/networks_test.go | 2 +- .../networking/v2/extensions/dns/testing/requests_test.go | 3 ++- .../networking/v2/extensions/external/testing/results_test.go | 3 ++- openstack/networking/v2/extensions/mtu/testing/results_test.go | 3 ++- .../networking/v2/extensions/provider/testing/results_test.go | 3 ++- .../v2/extensions/vlantransparent/testing/requests_test.go | 3 ++- openstack/networking/v2/networks/doc.go | 3 ++- openstack/networking/v2/networks/requests.go | 2 +- openstack/networking/v2/networks/testing/requests_test.go | 3 ++- 11 files changed, 18 insertions(+), 11 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index 1e74df805f..0cb62551c5 100644 --- a/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -238,7 +238,7 @@ func TestDNSNetwork(t *testing.T) { newNetworkDescription := "" newNetworkDNSDomain := "" networkUpdateOpts := networks.UpdateOpts{ - Name: newNetworkName, + Name: &newNetworkName, Description: &newNetworkDescription, } var updateOpts networks.UpdateOptsBuilder diff --git a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go index 19a2378b03..e91ada32d6 100644 --- a/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go +++ b/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go @@ -81,7 +81,7 @@ func CreateVLANTransparentNetwork(t *testing.T, client *gophercloud.ServiceClien func UpdateVLANTransparentNetwork(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*VLANTransparentNetwork, error) { networkName := tools.RandomString("TESTACC-NEW-", 6) networkUpdateOpts := networks.UpdateOpts{ - Name: networkName, + Name: &networkName, } iFalse := false diff --git a/acceptance/openstack/networking/v2/networks_test.go b/acceptance/openstack/networking/v2/networks_test.go index 8e08d90531..e00ab0a32e 100644 --- a/acceptance/openstack/networking/v2/networks_test.go +++ b/acceptance/openstack/networking/v2/networks_test.go @@ -81,7 +81,7 @@ func TestNetworksCRUD(t *testing.T) { newName := tools.RandomString("TESTACC-", 8) newDescription := "" updateOpts := &networks.UpdateOpts{ - Name: newName, + Name: &newName, Description: &newDescription, } diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go index 910842ffc1..2bcb7cba8c 100644 --- a/openstack/networking/v2/extensions/dns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/dns/testing/requests_test.go @@ -359,7 +359,8 @@ func TestNetworkUpdate(t *testing.T) { var actual NetworkDNS - networkUpdateOpts := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: new(bool)} + name := "new_network_name" + networkUpdateOpts := networks.UpdateOpts{Name: &name, AdminStateUp: new(bool)} updateOpts := dns.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, DNSDomain: new(string), diff --git a/openstack/networking/v2/extensions/external/testing/results_test.go b/openstack/networking/v2/extensions/external/testing/results_test.go index 64a508824d..a64d23c3c4 100644 --- a/openstack/networking/v2/extensions/external/testing/results_test.go +++ b/openstack/networking/v2/extensions/external/testing/results_test.go @@ -122,8 +122,9 @@ func TestUpdate(t *testing.T) { iTrue := true iFalse := false + name := "new_network_name" networkUpdateOpts := networks.UpdateOpts{ - Name: "new_network_name", + Name: &name, AdminStateUp: &iFalse, Shared: &iTrue, } diff --git a/openstack/networking/v2/extensions/mtu/testing/results_test.go b/openstack/networking/v2/extensions/mtu/testing/results_test.go index e2779e9f3a..ff5f9ae35b 100644 --- a/openstack/networking/v2/extensions/mtu/testing/results_test.go +++ b/openstack/networking/v2/extensions/mtu/testing/results_test.go @@ -127,8 +127,9 @@ func TestUpdate(t *testing.T) { iTrue := true iFalse := false + name := "new_network_name" networkUpdateOpts := networks.UpdateOpts{ - Name: "new_network_name", + Name: &name, AdminStateUp: &iFalse, Shared: &iTrue, } diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go index 709f830627..b242103177 100644 --- a/openstack/networking/v2/extensions/provider/testing/results_test.go +++ b/openstack/networking/v2/extensions/provider/testing/results_test.go @@ -211,7 +211,8 @@ func TestUpdate(t *testing.T) { } iTrue := true - options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: gophercloud.Disabled, Shared: &iTrue} + 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) th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go index df39ca3d44..0632a6c9e9 100644 --- a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go @@ -142,8 +142,9 @@ func TestUpdate(t *testing.T) { }) iFalse := false + name := "new_network_name" networkUpdateOpts := networks.UpdateOpts{ - Name: "new_network_name", + Name: &name, AdminStateUp: &iFalse, } diff --git a/openstack/networking/v2/networks/doc.go b/openstack/networking/v2/networks/doc.go index e768b71f82..9d1dd5a7ea 100644 --- a/openstack/networking/v2/networks/doc.go +++ b/openstack/networking/v2/networks/doc.go @@ -45,8 +45,9 @@ Example to Update a Network networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78" + name := "new_name" updateOpts := networks.UpdateOpts{ - Name: "new_name", + Name: &name, } network, err := networks.Update(networkClient, networkID, updateOpts).Extract() diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index d52d099a67..8006c48167 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -112,7 +112,7 @@ type UpdateOptsBuilder interface { // UpdateOpts represents options used to update a network. type UpdateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` Shared *bool `json:"shared,omitempty"` } diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index adf88d59ba..1664f3e5cb 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -197,7 +197,8 @@ func TestUpdate(t *testing.T) { }) iTrue, iFalse := true, false - options := networks.UpdateOpts{Name: "new_network_name", AdminStateUp: &iFalse, Shared: &iTrue} + name := "new_network_name" + options := networks.UpdateOpts{Name: &name, AdminStateUp: &iFalse, Shared: &iTrue} n, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) From 6cf0507b18c54a3c9211b4aabb49452aed86f901 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 6 Apr 2019 19:32:43 +0000 Subject: [PATCH 0722/2296] Networking v2: Allow Subnet names to be cleared --- .../openstack/networking/v2/subnets_test.go | 2 +- openstack/networking/v2/subnets/doc.go | 3 ++- openstack/networking/v2/subnets/requests.go | 2 +- .../v2/subnets/testing/requests_test.go | 15 ++++++++++----- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/networking/v2/subnets_test.go b/acceptance/openstack/networking/v2/subnets_test.go index fafc1cb532..8b96293451 100644 --- a/acceptance/openstack/networking/v2/subnets_test.go +++ b/acceptance/openstack/networking/v2/subnets_test.go @@ -34,7 +34,7 @@ func TestSubnetCRUD(t *testing.T) { newSubnetName := tools.RandomString("TESTACC-", 8) newSubnetDescription := "" updateOpts := subnets.UpdateOpts{ - Name: newSubnetName, + Name: &newSubnetName, Description: &newSubnetDescription, } _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() diff --git a/openstack/networking/v2/subnets/doc.go b/openstack/networking/v2/subnets/doc.go index 93fd21f2ae..7d3a1b9b65 100644 --- a/openstack/networking/v2/subnets/doc.go +++ b/openstack/networking/v2/subnets/doc.go @@ -98,9 +98,10 @@ Example to Update a Subnet subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" dnsNameservers := []string{"8.8.8.8"} + name := "new_name" updateOpts := subnets.UpdateOpts{ - Name: "new_name", + Name: &name, DNSNameservers: &dnsNameservers, } diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index b56d3cb677..3e56bf389a 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -173,7 +173,7 @@ type UpdateOptsBuilder interface { // UpdateOpts represents the attributes used when updating an existing subnet. type UpdateOpts struct { // Name is a human-readable name of the subnet. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Description of the subnet. Description *string `json:"description,omitempty"` diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index 986ba0ba81..abd75319ee 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -418,8 +418,9 @@ func TestUpdate(t *testing.T) { }) dnsNameservers := []string{"foo"} + name := "my_new_subnet" opts := subnets.UpdateOpts{ - Name: "my_new_subnet", + Name: &name, DNSNameservers: &dnsNameservers, HostRoutes: &[]subnets.HostRoute{ {NextHop: "bar"}, @@ -450,8 +451,9 @@ func TestUpdateGateway(t *testing.T) { }) var gatewayIP = "10.0.0.1" + name := "my_new_subnet" opts := subnets.UpdateOpts{ - Name: "my_new_subnet", + Name: &name, GatewayIP: &gatewayIP, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() @@ -480,8 +482,9 @@ func TestUpdateRemoveGateway(t *testing.T) { }) var noGateway = "" + name := "my_new_subnet" opts := subnets.UpdateOpts{ - Name: "my_new_subnet", + Name: &name, GatewayIP: &noGateway, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() @@ -516,8 +519,9 @@ func TestUpdateHostRoutes(t *testing.T) { }, } + name := "my_new_subnet" opts := subnets.UpdateOpts{ - Name: "my_new_subnet", + Name: &name, HostRoutes: &HostRoutes, } s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() @@ -574,8 +578,9 @@ func TestUpdateAllocationPool(t *testing.T) { fmt.Fprintf(w, SubnetUpdateAllocationPoolResponse) }) + name := "my_new_subnet" opts := subnets.UpdateOpts{ - Name: "my_new_subnet", + Name: &name, AllocationPools: []subnets.AllocationPool{ { Start: "10.1.0.2", From c8c6f0a33fd268375d58a741653358bbc35e431c Mon Sep 17 00:00:00 2001 From: Colin Gibbons <43658752+itsc0lin@users.noreply.github.com> Date: Tue, 9 Apr 2019 18:23:05 -0700 Subject: [PATCH 0723/2296] Loadbalancer v2: Listener createopts TimeoutMemberConnect & TimeoutTCPInspect (#1544) * Add TimeoutMemberConnect and TimeoutTCPInspect create opts for listeners * Cleaning up linting * Update type for Listener results and additional testing parameters * Add new createopts to more tests * removing omitempty --- .../loadbalancer/v2/listeners/requests.go | 44 ++++++++++++------- .../loadbalancer/v2/listeners/results.go | 6 +++ .../v2/listeners/testing/fixtures.go | 16 ++++++- .../v2/listeners/testing/requests_test.go | 12 ++--- 4 files changed, 56 insertions(+), 22 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 25f411aa56..c512e4d72f 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -29,21 +29,23 @@ type ListOptsBuilder interface { // sort by a particular listener 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"` - AdminStateUp *bool `q:"admin_state_up"` - ProjectID string `q:"project_id"` - LoadbalancerID string `q:"loadbalancer_id"` - DefaultPoolID string `q:"default_pool_id"` - Protocol string `q:"protocol"` - ProtocolPort int `q:"protocol_port"` - ConnectionLimit int `q:"connection_limit"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - TimeoutClientData *int `q:"timeout_client_data"` - TimeoutMemberData *int `q:"timeout_member_data"` + ID string `q:"id"` + Name string `q:"name"` + AdminStateUp *bool `q:"admin_state_up"` + ProjectID string `q:"project_id"` + LoadbalancerID string `q:"loadbalancer_id"` + DefaultPoolID string `q:"default_pool_id"` + Protocol string `q:"protocol"` + ProtocolPort int `q:"protocol_port"` + ConnectionLimit int `q:"connection_limit"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + TimeoutClientData *int `q:"timeout_client_data"` + TimeoutMemberData *int `q:"timeout_member_data"` + TimeoutMemberConnect *int `q:"timeout_member_connect"` + TimeoutTCPInspect *int `q:"timeout_tcp_inspect"` } // ToListenerListQuery formats a ListOpts into a query string. @@ -121,6 +123,12 @@ type CreateOpts struct { // Backend member inactivity timeout in milliseconds TimeoutMemberData *int `json:"timeout_member_data,omitempty"` + // Backend member connection timeout in milliseconds + TimeoutMemberConnect *int `json:"timeout_member_connect,omitempty"` + + // Time, in milliseconds, to wait for additional TCP packets for content inspection + TimeoutTCPInspect *int `json:"timeout_tcp_inspect,omitempty"` + // A dictionary of optional headers to insert into the request before it is sent to the backend member. InsertHeaders map[string]string `json:"insert_headers,omitempty"` } @@ -188,6 +196,12 @@ type UpdateOpts struct { // Backend member inactivity timeout in milliseconds TimeoutMemberData *int `json:"timeout_member_data,omitempty"` + + // Backend member connection timeout in milliseconds + TimeoutMemberConnect *int `json:"timeout_member_connect,omitempty"` + + // Time, in milliseconds, to wait for additional TCP packets for content inspection + TimeoutTCPInspect *int `json:"timeout_tcp_inspect,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index e8c6a79998..a8f7682101 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -69,6 +69,12 @@ type Listener struct { // Backend member inactivity timeout in milliseconds TimeoutMemberData int `json:"timeout_member_data"` + // Backend member connection timeout in milliseconds + TimeoutMemberConnect int `json:"timeout_member_connect"` + + // Time, in milliseconds, to wait for additional TCP packets for content inspection + TimeoutTCPInspect int `json:"timeout_tcp_inspect"` + // A dictionary of optional headers to insert into the request before it is sent to the backend member. InsertHeaders map[string]string `json:"insert_headers"` } diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index 1ea6d49a03..bd65d3ac02 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -42,6 +42,8 @@ const ListenersListBody = ` "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], "timeout_client_data": 50000, "timeout_member_data": 50000, + "timeout_member_connect": 5000, + "timeout_tcp_inspect": 0, "insert_headers": { "X-Forwarded-For": "true" } @@ -68,6 +70,8 @@ const SingleListenerBody = ` "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], "timeout_client_data": 50000, "timeout_member_data": 50000, + "timeout_member_connect": 5000, + "timeout_tcp_inspect": 0, "insert_headers": { "X-Forwarded-For": "true" } @@ -92,7 +96,9 @@ const PostUpdateListenerBody = ` "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], "timeout_client_data": 181000, - "timeout_member_data": 181000 + "timeout_member_data": 181000, + "timeout_member_connect": 181000, + "timeout_tcp_inspect": 181000 } } @@ -140,6 +146,8 @@ var ( SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, TimeoutClientData: 50000, TimeoutMemberData: 50000, + TimeoutMemberConnect: 5000, + TimeoutTCPInspect: 0, InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, } ListenerUpdated = listeners.Listener{ @@ -157,6 +165,8 @@ var ( SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, TimeoutClientData: 181000, TimeoutMemberData: 181000, + TimeoutMemberConnect: 181000, + TimeoutTCPInspect: 181000, } ListenerStatsTree = listeners.Stats{ ActiveConnections: 0, @@ -248,7 +258,9 @@ func HandleListenerUpdateSuccessfully(t *testing.T) { "default_pool_id": null, "connection_limit": 1001, "timeout_client_data": 181000, - "timeout_member_data": 181000 + "timeout_member_data": 181000, + "timeout_member_connect": 181000, + "timeout_tcp_inspect": 181000 } }`) diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 0b3fce4c9b..775810951d 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -130,11 +130,13 @@ func TestUpdateListener(t *testing.T) { name := "NewListenerName" defaultPoolID := "" actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ - Name: &name, - ConnLimit: &i1001, - DefaultPoolID: &defaultPoolID, - TimeoutMemberData: &i181000, - TimeoutClientData: &i181000, + Name: &name, + ConnLimit: &i1001, + DefaultPoolID: &defaultPoolID, + TimeoutMemberData: &i181000, + TimeoutClientData: &i181000, + TimeoutMemberConnect: &i181000, + TimeoutTCPInspect: &i181000, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From 1d93848c29c42ba3ebb1dbdff7a192a70d112003 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 17 Apr 2019 17:15:14 -0400 Subject: [PATCH 0724/2296] Baremetal Inspector noauth support (#1531) For #1485 Add noauth support for the baremetal introspection (ironic-inspector) service. Signed-off-by: Zane Bitter --- .../baremetalintrospection/noauth/doc.go | 16 +++++++++ .../baremetalintrospection/noauth/requests.go | 36 +++++++++++++++++++ .../noauth/testing/requests_test.go | 16 +++++++++ 3 files changed, 68 insertions(+) create mode 100644 openstack/baremetalintrospection/noauth/doc.go create mode 100644 openstack/baremetalintrospection/noauth/requests.go create mode 100644 openstack/baremetalintrospection/noauth/testing/requests_test.go diff --git a/openstack/baremetalintrospection/noauth/doc.go b/openstack/baremetalintrospection/noauth/doc.go new file mode 100644 index 0000000000..60438d3c5f --- /dev/null +++ b/openstack/baremetalintrospection/noauth/doc.go @@ -0,0 +1,16 @@ +package noauth + +/* +Package noauth provides support for noauth bare metal introspection endpoints. + + Example of obtaining and using a client: + + client, err := noauth.NewBareMetalIntrospectionNoAuth(noauth.EndpointOpts{ + IronicInspectorEndpoint: "http://localhost:5050/v1/", + }) + if err != nil { + panic(err) + } + + introspection.GetIntrospectionStatus(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") +*/ diff --git a/openstack/baremetalintrospection/noauth/requests.go b/openstack/baremetalintrospection/noauth/requests.go new file mode 100644 index 0000000000..a1c0857c96 --- /dev/null +++ b/openstack/baremetalintrospection/noauth/requests.go @@ -0,0 +1,36 @@ +package noauth + +import ( + "fmt" + + "github.com/gophercloud/gophercloud" +) + +// EndpointOpts specifies a "noauth" Ironic Inspector Endpoint. +type EndpointOpts struct { + // IronicInspectorEndpoint [required] is currently only used with "noauth" Ironic introspection. + // An Ironic inspector endpoint with "auth_strategy=noauth" is necessary, for example: + // http://ironic.example.com:5050/v1. + IronicInspectorEndpoint string +} + +func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc := new(gophercloud.ServiceClient) + if eo.IronicInspectorEndpoint == "" { + return nil, fmt.Errorf("IronicInspectorEndpoint is required") + } + + sc.Endpoint = gophercloud.NormalizeURL(eo.IronicInspectorEndpoint) + sc.ProviderClient = client + return sc, nil +} + +// NewBareMetalIntrospectionNoAuth creates a ServiceClient that may be used to access a +// "noauth" bare metal introspection service. +func NewBareMetalIntrospectionNoAuth(eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) + + sc.Type = "baremetal-inspector" + + return sc, err +} diff --git a/openstack/baremetalintrospection/noauth/testing/requests_test.go b/openstack/baremetalintrospection/noauth/testing/requests_test.go new file mode 100644 index 0000000000..33f3d7e4ed --- /dev/null +++ b/openstack/baremetalintrospection/noauth/testing/requests_test.go @@ -0,0 +1,16 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/noauth" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNoAuth(t *testing.T) { + noauthClient, err := noauth.NewBareMetalIntrospectionNoAuth(noauth.EndpointOpts{ + IronicInspectorEndpoint: "http://ironic:5050/v1", + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, "", noauthClient.TokenID) +} From da189bd6ace172710e0124d3a0d8f43a4b38a3e5 Mon Sep 17 00:00:00 2001 From: h00130372 Date: Thu, 18 Apr 2019 09:57:01 +0800 Subject: [PATCH 0725/2296] Support acceptance test against OpenStack Stein release Input comment "recheck stable/stein" in pull request comments, that will trigger acceptance tests against OpenStack Stein release. OpenLab will report test result in followint comments automatically. Related-Bug: theopenlab/openlab#231 --- .zuul.yaml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 776ed5a796..135e3b203a 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -21,13 +21,13 @@ run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml - job: - name: gophercloud-acceptance-test-queens + name: gophercloud-acceptance-test-stein parent: gophercloud-acceptance-test description: | - Run gophercloud acceptance test on queens branch + Run gophercloud acceptance test on stein branch vars: global_env: - OS_BRANCH: stable/queens + OS_BRANCH: stable/stein - job: name: gophercloud-acceptance-test-rocky @@ -38,6 +38,15 @@ global_env: OS_BRANCH: stable/rocky +- job: + name: gophercloud-acceptance-test-queens + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on queens branch + vars: + global_env: + OS_BRANCH: stable/queens + - job: name: gophercloud-acceptance-test-pike parent: gophercloud-acceptance-test @@ -100,3 +109,6 @@ recheck-rocky: jobs: - gophercloud-acceptance-test-rocky + recheck-stein: + jobs: + - gophercloud-acceptance-test-stein From bee12df7a499db758d24e5a3376a75f94de13ff5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 18 Apr 2019 02:18:26 +0000 Subject: [PATCH 0726/2296] Docs: Updating baremetal/inspector docs --- openstack/baremetal/noauth/doc.go | 5 +- openstack/baremetal/v1/drivers/doc.go | 68 ++++++++-------- openstack/baremetal/v1/nodes/doc.go | 79 ++++++++++++------ openstack/baremetal/v1/ports/doc.go | 42 ++++++---- .../baremetalintrospection/noauth/doc.go | 5 +- .../v1/introspection/doc.go | 81 ++++++++++++------- 6 files changed, 172 insertions(+), 108 deletions(-) diff --git a/openstack/baremetal/noauth/doc.go b/openstack/baremetal/noauth/doc.go index 64106fc6b6..9f83357b2a 100644 --- a/openstack/baremetal/noauth/doc.go +++ b/openstack/baremetal/noauth/doc.go @@ -1,9 +1,7 @@ -package noauth - /* Package noauth provides support for noauth bare metal endpoints. - Example of obtaining and using a client: +Example of obtaining and using a client: client, err := noauth.NewBareMetalNoAuth(noauth.EndpointOpts{ IronicEndpoint: "http://localhost:6385/v1/", @@ -16,3 +14,4 @@ Package noauth provides support for noauth bare metal endpoints. nodes.ListDetail(client, nodes.ListOpts{}) */ +package noauth diff --git a/openstack/baremetal/v1/drivers/doc.go b/openstack/baremetal/v1/drivers/doc.go index 5eb8346852..252c01b76a 100644 --- a/openstack/baremetal/v1/drivers/doc.go +++ b/openstack/baremetal/v1/drivers/doc.go @@ -1,41 +1,43 @@ -package drivers - /* Package drivers contains the functionality for Listing drivers, driver details, driver properties and driver logical disk properties API reference: https://developer.openstack.org/api-ref/baremetal/#drivers-drivers - // Example to List Drivers - drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) { - driversList, err := drivers.ExtractDrivers(page) - if err != nil { - return false, err - } - - for _, n := range driversList { - // Do something - } - - return true, nil - }) - - // Example to Get single Driver Details - showDriverDetails, err := drivers.GetDriverDetails(client, "ipmi").Extract() - if err != nil { - panic(err) - } - - // Example to Get single Driver Properties - showDriverProperties, err := drivers.GetDriverProperties(client, "ipmi").Extract() - if err != nil { - panic(err) - } - - // Example to Get single Driver Logical Disk Properties - showDriverDiskProperties, err := drivers.GetDriverDiskProperties(client, "ipmi").Extract() - if err != nil { - panic(err) - } +Example to List Drivers + + drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) { + driversList, err := drivers.ExtractDrivers(page) + if err != nil { + return false, err + } + + for _, n := range driversList { + // Do something + } + + return true, nil + }) + +Example to Get single Driver Details + showDriverDetails, err := drivers.GetDriverDetails(client, "ipmi").Extract() + if err != nil { + panic(err) + } + +Example to Get single Driver Properties + + showDriverProperties, err := drivers.GetDriverProperties(client, "ipmi").Extract() + if err != nil { + panic(err) + } + +Example to Get single Driver Logical Disk Properties + + showDriverDiskProperties, err := drivers.GetDriverDiskProperties(client, "ipmi").Extract() + if err != nil { + panic(err) + } */ +package drivers diff --git a/openstack/baremetal/v1/nodes/doc.go b/openstack/baremetal/v1/nodes/doc.go index 40b9fe826e..37f60b5f3e 100644 --- a/openstack/baremetal/v1/nodes/doc.go +++ b/openstack/baremetal/v1/nodes/doc.go @@ -1,10 +1,9 @@ -package nodes - /* Package nodes provides information and interaction with the nodes API resource in the OpenStack Bare Metal service. - // Example to List Nodes with Detail +Example to List Nodes with Detail + nodes.ListDetail(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { @@ -18,11 +17,14 @@ resource in the OpenStack Bare Metal service. return true, nil }) - // Example to List Nodes - nodes.List(client, nodes.ListOpts{ - ProvisionState: Deploying, - Fields: []string{"name"}, - }).EachPage(func(page pagination.Page) (bool, error) { +Example to List Nodes + + listOpts := nodes.ListOpts{ + ProvisionState: nodes.Deploying, + Fields: []string{"name"}, + } + + nodes.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err @@ -35,8 +37,9 @@ resource in the OpenStack Bare Metal service. return true, nil }) - // Example to Create Node - createNode, err := nodes.Create(client, nodes.CreateOpts{ +Example to Create Node + + createOpts := nodes.CreateOpts Driver: "ipmi", BootInterface: "pxe", Name: "coconuts", @@ -48,50 +51,80 @@ resource in the OpenStack Bare Metal service. "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, - }).Extract() + } + + createNode, err := nodes.Create(client, createOpts).Extract() if err != nil { panic(err) } - // Example to Get Node +Example to Get Node + showNode, err := nodes.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() if err != nil { panic(err) } - // Example to Update Node - updateNode, err := nodes.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", nodes.UpdateOpts{ +Example to Update Node + + updateOpts := nodes.UpdateOpts{ nodes.UpdateOperation{ Op: ReplaceOp, Path: "/maintenance", Value: "true", }, - }).Extract() + } + + updateNode, err := nodes.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() if err != nil { panic(err) } - // Example to Delete Node +Example to Delete Node + err = nodes.Delete(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() if err != nil { panic(err) } - // Example to Validate Node +Example to Validate Node + validation, err := nodes.Validate(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + if err != nil { + panic(err) + } + +Example to inject non-masking interrupts - // Example to inject non-masking interrupts err := nodes.InjectNMI(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").ExtractErr() + if err != nil { + panic(err) + } + +Example to get array of supported boot devices for a node - // Example to get array of supported boot devices for a node bootDevices, err := nodes.GetSupportedBootDevices(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + if err != nil { + panic(err) + } + +Example to set boot device for a node - // Example to set boot device for a node - err := nodes.SetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", nodes.BootDeviceOpts{ + bootOpts := nodes.BootDeviceOpts{ BootDevice: "pxe", Persistent: false, - }) + } + + err := nodes.SetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", bootOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to get boot device for a node - // Example to get boot device for a node bootDevice, err := nodes.GetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + if err != nil { + panic(err) + } */ +package nodes diff --git a/openstack/baremetal/v1/ports/doc.go b/openstack/baremetal/v1/ports/doc.go index 4e02409bbd..eb0579bed5 100644 --- a/openstack/baremetal/v1/ports/doc.go +++ b/openstack/baremetal/v1/ports/doc.go @@ -1,5 +1,3 @@ -package ports - /* Package ports contains the functionality to Listing, Searching, Creating, Updating, and Deleting of bare metal Port resources @@ -7,8 +5,9 @@ package ports API reference: https://developer.openstack.org/api-ref/baremetal/#ports-ports - // Example to List Ports with Detail - ports.ListDetail(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { +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 @@ -21,10 +20,13 @@ package ports return true, nil }) - // Example to List Ports - ports.List(client, ports.ListOpts{ +Example to List Ports + + listOpts := ports.ListOpts{ Limit: 10, - }).EachPage(func(page pagination.Page) (bool, error) { + } + + ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err @@ -37,37 +39,47 @@ package ports return true, nil }) - // Example to Create a Port - createPort, err := ports.Create(client, ports.CreateOpts{ +Example to Create a Port + + createOpts := ports.CreateOpts{ NodeUUID: "e8920409-e07e-41bb-8cc1-72acb103e2dd", Address: "00:1B:63:84:45:E6", PhysicalNetwork: "my-network", - }).Extract() + } + + createPort, err := ports.Create(client, createOpts).Extract() if err != nil { panic(err) } - // Example to Get a Port +Example to Get a Port + showPort, err := ports.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() if err != nil { panic(err) } - // Example to Update a Port - updatePort, err := ports.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", ports.UpdateOpts{ +Example to Update a Port + + updateOpts := ports.UpdateOpts{ ports.UpdateOperation{ Op: ReplaceOp, Path: "/address", Value: "22:22:22:22:22:22", }, - }).Extract() + } + + updatePort, err := ports.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() if err != nil { panic(err) } - // Example to Delete a Port +Example to Delete a Port + err = ports.Delete(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() if err != nil { panic(err) + } */ +package ports diff --git a/openstack/baremetalintrospection/noauth/doc.go b/openstack/baremetalintrospection/noauth/doc.go index 60438d3c5f..014180c28b 100644 --- a/openstack/baremetalintrospection/noauth/doc.go +++ b/openstack/baremetalintrospection/noauth/doc.go @@ -1,9 +1,7 @@ -package noauth - /* Package noauth provides support for noauth bare metal introspection endpoints. - Example of obtaining and using a client: +Example of obtaining and using a client: client, err := noauth.NewBareMetalIntrospectionNoAuth(noauth.EndpointOpts{ IronicInspectorEndpoint: "http://localhost:5050/v1/", @@ -14,3 +12,4 @@ Package noauth provides support for noauth bare metal introspection endpoints. introspection.GetIntrospectionStatus(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") */ +package noauth diff --git a/openstack/baremetalintrospection/v1/introspection/doc.go b/openstack/baremetalintrospection/v1/introspection/doc.go index 0604e2f6a4..4238238694 100644 --- a/openstack/baremetalintrospection/v1/introspection/doc.go +++ b/openstack/baremetalintrospection/v1/introspection/doc.go @@ -1,5 +1,3 @@ -package introspection - /* Package introspection contains the functionality for Starting introspection, Get introspection status, List all introspection statuses, Abort an @@ -8,33 +6,54 @@ stored data. API reference https://developer.openstack.org/api-ref/baremetal-introspection/#node-introspection - // Example to Start Introspection - introspection.StartIntrospection(client, NodeUUID, introspection.StartOpts{}).ExtractErr() - - // Example to Get an Introspection status - introspection.GetIntrospectionStatus(client, NodeUUID).Extract() - if err != nil { - panic(err) - } - - // Example to List all introspection statuses - introspection.ListIntrospections(client.ServiceClient(), introspection.ListIntrospectionsOpts{}).EachPage(func(page pagination.Page) (bool, error) { - introspectionsList, err := introspection.ExtractIntrospections(page) - if err != nil { - return false, err - } - for _, n := range introspectionsList { - // Do something - } - return true, nil - }) - - // Example to Abort an Introspection - introspection.AbortIntrospection(client, NodeUUID).ExtractErr() - - // Example to Get stored Introspection Data - introspection.GetIntrospectionData(c, NodeUUID).Extract() - - // Example to apply Introspection Data - introspection.ApplyIntrospectionData(c, NodeUUID).ExtractErr() +Example to Start Introspection + + err := introspection.StartIntrospection(client, NodeUUID, introspection.StartOpts{}).ExtractErr() + if err != nil { + panic(err) + } + +Example to Get an Introspection status + + _, err := introspection.GetIntrospectionStatus(client, NodeUUID).Extract() + if err != nil { + panic(err) + } + +Example to List all introspection statuses + + introspection.ListIntrospections(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + introspectionsList, err := introspection.ExtractIntrospections(page) + if err != nil { + return false, err + } + + for _, n := range introspectionsList { + // Do something + } + + return true, nil + }) + +Example to Abort an Introspection + + err := introspection.AbortIntrospection(client, NodeUUID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Get stored Introspection Data + + v, err := introspection.GetIntrospectionData(c, NodeUUID).Extract() + if err != nil { + panic(err) + } + +Example to apply Introspection Data + + err := introspection.ApplyIntrospectionData(c, NodeUUID).ExtractErr() + if err != nil { + panic(err) + } */ +package introspection From e53f839b56ea1cf6fb0e0d25a5c86d139e02b5cd Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Fri, 19 Apr 2019 15:03:54 -0400 Subject: [PATCH 0727/2296] Baremetal introspection: correct handling of LLDP data (#1548) * Baremetal introspection: correct handling of LLDP data The LLDP data in the inventory doesn't appear as a JSON object, but as a JSON array of [type, value] pairs such as: [[1, "04112233aabbcc"], [2, "07373334"], ...] (There can be multiple fields with the same type, which is why they are not returned as a JSON object.) There is a place where the raw LLDP data is processed into a JSON object - under the `lldp_processed` key in the top-level `all_interfaces` list (i.e. not in the inventory), here represented by BaseInterfaceType. This patch corrects handling for LLDP data in the inventory (i.e. in InterfaceType) and adds handling for it in BaseInterfaceType Signed-off-by: Zane Bitter * Baremetal introspection: Change Lldp to LLDP Coding style prefers all caps for initialisms. Signed-off-by: Zane Bitter * Baremetal introspection: Check LLDP data in tests Signed-off-by: Zane Bitter * Baremetal introspection: Add tests for LLDP parse errors Add tests for handling of invalid data in the code that deserialises LLDP TLV data from JSON. Signed-off-by: Zane Bitter --- .../v1/introspection/results.go | 61 ++++++++++++++----- .../v1/introspection/testing/fixtures.go | 30 +++++++-- .../v1/introspection/testing/results_test.go | 29 +++++++++ 3 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 openstack/baremetalintrospection/v1/introspection/testing/results_test.go diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 41be701af4..5cb35e999a 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -2,6 +2,7 @@ package introspection import ( "encoding/json" + "fmt" "time" "github.com/gophercloud/gophercloud" @@ -174,10 +175,11 @@ type Data struct { // Sub Types defined under Data and deeper in the structure type BaseInterfaceType struct { - ClientID string `json:"client_id"` - IP string `json:"ip"` - MAC string `json:"mac"` - PXE bool `json:"pxe"` + ClientID string `json:"client_id"` + IP string `json:"ip"` + MAC string `json:"mac"` + PXE bool `json:"pxe"` + LLDPProcessed map[string]interface{} `json:"lldp_processed"` } type BootInfoType struct { @@ -193,17 +195,22 @@ type CPUType struct { ModelName string `json:"model_name"` } +type LLDPTLVType struct { + Type int + Value string +} + 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 map[string]interface{} `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"` + LLDP []LLDPTLVType `json:"lldp"` + MACAddress string `json:"mac_address"` + Name string `json:"name"` + Product string `json:"product"` + Vendor string `json:"vendor"` } type InventoryType struct { @@ -240,6 +247,32 @@ type SystemVendorType struct { SerialNumber string `json:"serial_number"` } +// UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure +func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { + var list []interface{} + if err := json.Unmarshal(data, &list); err != nil { + return err + } + + if len(list) != 2 { + return fmt.Errorf("Invalid LLDP TLV key-value pair") + } + + fieldtype, ok := list[0].(float64) + if !ok { + return fmt.Errorf("LLDP TLV key is not number") + } + + value, ok := list[1].(string) + if !ok { + return fmt.Errorf("LLDP TLV value is not string") + } + + r.Type = int(fieldtype) + r.Value = value + return nil +} + // Extract interprets any IntrospectionDataResult as IntrospectionData, if possible. func (r DataResult) Extract() (*Data, error) { var s Data diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index d8ace03a25..58cb166781 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -101,7 +101,7 @@ const IntrospectionDataJSONSample = ` "bmc_address":"192.167.2.134", "interfaces":[ { - "lldp":null, + "lldp":[], "product":"0x0001", "vendor":"0x1af4", "name":"eth1", @@ -111,7 +111,10 @@ const IntrospectionDataJSONSample = ` "mac_address":"52:54:00:47:20:4d" }, { - "lldp":null, + "lldp": [ + [1, "04112233aabbcc"], + [5, "737730312d646973742d31622d623132"] + ], "product":"0x0001", "vendor":"0x1af4", "name":"eth0", @@ -174,8 +177,12 @@ const IntrospectionDataJSONSample = ` "ip":"172.24.42.100", "mac":"52:54:00:4e:3d:30", "pxe":true, - "client_id":null - } + "client_id":null, + "lldp_processed":{ + "switch_chassis_id":"11:22:33:aa:bb:cc", + "switch_system_name":"sw01-dist-1b-b12" + } + } } } ` @@ -270,6 +277,7 @@ var ( Name: "eth1", Product: "0x0001", IPV4Address: "172.24.42.101", + LLDP: []introspection.LLDPTLVType{}, }, introspection.InterfaceType{ IPV4Address: "172.24.42.100", @@ -278,6 +286,16 @@ var ( Product: "0x0001", HasCarrier: true, Vendor: "0x1af4", + LLDP: []introspection.LLDPTLVType{ + introspection.LLDPTLVType{ + Type: 1, + Value: "04112233aabbcc", + }, + introspection.LLDPTLVType{ + Type: 5, + Value: "737730312d646973742d31622d623132", + }, + }, }, }, Memory: introspection.MemoryType{ @@ -297,6 +315,10 @@ var ( IP: "172.24.42.100", MAC: "52:54:00:4e:3d:30", PXE: true, + LLDPProcessed: map[string]interface{}{ + "switch_chassis_id": "11:22:33:aa:bb:cc", + "switch_system_name": "sw01-dist-1b-b12", + }, }, }, } diff --git a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go new file mode 100644 index 0000000000..87383ed6a1 --- /dev/null +++ b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go @@ -0,0 +1,29 @@ +package testing + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" +) + +func TestLLDPTLVErrors(t *testing.T) { + badInputs := []string{ + "[1]", + "[1, 2]", + "[\"foo\", \"bar\"]", + } + + for _, input := range badInputs { + var output introspection.LLDPTLVType + err := json.Unmarshal([]byte(input), &output) + if err == nil { + t.Errorf("No JSON parse error for invalid LLDP TLV %s", input) + } + if !strings.Contains(err.Error(), "LLDP TLV") { + t.Errorf("Unexpected JSON parse error \"%s\" for invalid LLDP TLV %s", + err, input) + } + } +} From fe629955684802399c46e2a8e9aeecc4459d2923 Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Mon, 22 Apr 2019 22:30:55 +0800 Subject: [PATCH 0728/2296] Support acceptance test against OpenStack Stein release (#1553) We have to skip several tests to support acceptance test against OpenStack Stein release like we do for master, stable/queen and stable/rocky. Related-Bug: theopenlab/openlab#231 --- acceptance/openstack/compute/v2/quotaset_test.go | 2 ++ acceptance/openstack/identity/v3/roles_test.go | 1 + 2 files changed, 3 insertions(+) diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go index 8ed9372627..7666456a74 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -19,6 +19,7 @@ func TestQuotasetGet(t *testing.T) { clients.SkipRelease(t, "master") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") + clients.SkipRelease(t, "stable/stein") client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) @@ -107,6 +108,7 @@ func TestQuotasetUpdateDelete(t *testing.T) { clients.SkipRelease(t, "master") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") + clients.SkipRelease(t, "stable/stein") clients.RequireAdmin(t) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 37003aa17f..05708dcd57 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -116,6 +116,7 @@ func TestRolesFilterList(t *testing.T) { clients.SkipRelease(t, "master") clients.SkipRelease(t, "stable/queens") clients.SkipRelease(t, "stable/rocky") + clients.SkipRelease(t, "stable/stein") client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) From 666dacaccf07d7817496783e81aaf359de233c54 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Tue, 23 Apr 2019 12:50:01 -0400 Subject: [PATCH 0729/2296] Baremetal: Fix typo in state name (#1556) Inpsecting -> Inspecting Signed-off-by: Zane Bitter --- openstack/baremetal/v1/nodes/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 07fe2222b3..787ca3c91c 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -34,7 +34,7 @@ const ( CleanFail = "clean failed" Error = "error" Rebuild = "rebuild" - Inpsecting = "inspecting" + Inspecting = "inspecting" InspectFail = "inspect failed" InspectWait = "inspect wait" Adopting = "adopting" From b9b92a82580638d091517faa0a8a9655657ed263 Mon Sep 17 00:00:00 2001 From: redixin Date: Wed, 24 Apr 2019 03:11:12 +0000 Subject: [PATCH 0730/2296] Baremetal: Fix microversion headers in node.Update (#1555) * Baremetal: Fix microversion headers in node.Update * Baremetal: Use client.Patch instead of client.Request in ports.Update --- openstack/baremetal/v1/nodes/requests.go | 11 +---------- openstack/baremetal/v1/ports/requests.go | 7 +------ 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 787ca3c91c..aab75488b6 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -301,19 +301,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up body[i] = result } - - resp, err := client.Request("PATCH", updateURL(client, id), &gophercloud.RequestOpts{ + _, r.Err = client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ JSONBody: &body, OkCodes: []int{200}, }) - - if err != nil { - r.Err = err - } else { - r.Body = resp.Body - r.Header = resp.Header - } - return } diff --git a/openstack/baremetal/v1/ports/requests.go b/openstack/baremetal/v1/ports/requests.go index 89dc4b5696..a5da3f8347 100644 --- a/openstack/baremetal/v1/ports/requests.go +++ b/openstack/baremetal/v1/ports/requests.go @@ -202,15 +202,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up body[i] = patch.ToPortUpdateMap() } - resp, err := client.Request("PATCH", updateURL(client, id), &gophercloud.RequestOpts{ + _, r.Err = client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ JSONBody: &body, OkCodes: []int{200}, }) - - r.Body = resp.Body - r.Header = resp.Header - r.Err = err - return } From 60507118a582789eaa9c8dcefc1374c7f5431b9b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 26 Apr 2019 20:01:17 -0600 Subject: [PATCH 0731/2296] Block Storage: Backups (#1552) * inital creation of v2/backups cp -a snapshots backups * simple sed s/snapshot/backup on existing code the backup and snapshot functionality of Cinder do much the same thing with respect to how it interacts with the underlying objects ( above the driver ). I've simply replaced all instances of [Ss]napshot with backup. Future work will be tweaking the code to make it work properly with the backup API of Cinder. * mend * added a couple of options for Backups backups.List() works, but .Create() does not. I keep getting HTTP 400. Then, I get the same for snapshots.Create() which should work ( because I haven't messed with it ). Trying blockstorge/v3 * inital copy and replace of blockstorage/v3/backups same as v2, "cp -a" and "sed -i -e "s/snapshot/backup/g" * v3 backup works to list and create. very much still a work in progress. * Block Storage: Move backups to extensions * Block Storage: clean up backups * Block Storage: Add backup acceptance tests * Block Storage: Microversion results for backups --- .../blockstorage/extensions/backups_test.go | 41 ++++ .../blockstorage/extensions/extensions.go | 63 ++++++ .../blockstorage/extensions/backups/doc.go | 61 ++++++ .../extensions/backups/microversions.go | 21 ++ .../extensions/backups/requests.go | 184 ++++++++++++++++++ .../extensions/backups/results.go | 155 +++++++++++++++ .../extensions/backups/testing/fixtures.go | 133 +++++++++++++ .../backups/testing/requests_test.go | 96 +++++++++ .../blockstorage/extensions/backups/urls.go | 23 +++ 9 files changed, 777 insertions(+) create mode 100644 acceptance/openstack/blockstorage/extensions/backups_test.go create mode 100644 openstack/blockstorage/extensions/backups/doc.go create mode 100644 openstack/blockstorage/extensions/backups/microversions.go create mode 100644 openstack/blockstorage/extensions/backups/requests.go create mode 100644 openstack/blockstorage/extensions/backups/results.go create mode 100644 openstack/blockstorage/extensions/backups/testing/fixtures.go create mode 100644 openstack/blockstorage/extensions/backups/testing/requests_test.go create mode 100644 openstack/blockstorage/extensions/backups/urls.go diff --git a/acceptance/openstack/blockstorage/extensions/backups_test.go b/acceptance/openstack/blockstorage/extensions/backups_test.go new file mode 100644 index 0000000000..41e45c5e6d --- /dev/null +++ b/acceptance/openstack/blockstorage/extensions/backups_test.go @@ -0,0 +1,41 @@ +// +build acceptance blockstorage + +package extensions + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" + + blockstorage "github.com/gophercloud/gophercloud/acceptance/openstack/blockstorage/v3" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestBackupsCRUD(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + backup, err := CreateBackup(t, blockClient, volume.ID) + th.AssertNoErr(t, err) + defer DeleteBackup(t, blockClient, backup.ID) + + allPages, err := backups.List(blockClient, nil).AllPages() + th.AssertNoErr(t, err) + + allBackups, err := backups.ExtractBackups(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allBackups { + if backup.Name == v.Name { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index 2856b8b655..0be9e7ee31 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -8,10 +8,12 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/testhelper" ) // CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be @@ -171,3 +173,64 @@ func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *v return nil } + +// CreateBackup will create a backup based on a volume. An error will be +// will be returned if the backup could not be created. +func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID string) (*backups.Backup, error) { + t.Logf("Attempting to create a backup of volume %s", volumeID) + + backupName := tools.RandomString("ACPTTEST", 16) + createOpts := backups.CreateOpts{ + VolumeID: volumeID, + Name: backupName, + } + + backup, err := backups.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + err = WaitForBackupStatus(client, backup.ID, "available", 120) + if err != nil { + return nil, err + } + + backup, err = backups.Get(client, backup.ID).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created backup %s", backup.ID) + tools.PrintResource(t, backup) + + th.AssertEquals(t, backup.Name, backupName) + + return backup, nil +} + +// DeleteBackup will delete a backup. A fatal error will occur if the backup +// could not be deleted. This works best when used as a deferred function. +func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { + if err := backups.Delete(client, backupID).ExtractErr(); err != nil { + t.Fatalf("Unable to delete backup %s: %s", backupID, err) + } + + t.Logf("Deleted backup %s", backupID) +} + +// WaitForBackupStatus will continually poll a backup, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := backups.Get(client, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} diff --git a/openstack/blockstorage/extensions/backups/doc.go b/openstack/blockstorage/extensions/backups/doc.go new file mode 100644 index 0000000000..0897f639bd --- /dev/null +++ b/openstack/blockstorage/extensions/backups/doc.go @@ -0,0 +1,61 @@ +/* +Package backups provides information and interaction with backups in the +OpenStack Block Storage service. A backup is a point in time copy of the +data contained in an external storage volume, and can be controlled +programmatically. + +Example to List Backups + + listOpts := backups.ListOpts{ + VolumeID: "uuid", + } + + allPages, err := backups.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allBackups, err := backups.ExtractBackups(allPages) + if err != nil { + panic(err) + } + + for _, backup := range allBackups { + fmt.Println(backup) + } + +Example to Create a Backup + + createOpts := backups.CreateOpts{ + VolumeID: "uuid", + Name: "my-backup", + } + + backup, err := backups.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) + +Example to Update a Backup + + updateOpts := backups.UpdateOpts{ + Name: "new-name", + } + + backup, err := backups.Update(client, "uuid", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) + +Example to Delete a Backup + + err := backups.Delete(client, "uuid").ExtractErr() + if err != nil { + panic(err) + } +*/ +package backups diff --git a/openstack/blockstorage/extensions/backups/microversions.go b/openstack/blockstorage/extensions/backups/microversions.go new file mode 100644 index 0000000000..4c12690c58 --- /dev/null +++ b/openstack/blockstorage/extensions/backups/microversions.go @@ -0,0 +1,21 @@ +package backups + +// ExtractMetadata will extract the metadata of a backup. +// This requires the client to be set to microversion 3.43 or later. +func (r commonResult) ExtractMetadata() (map[string]string, error) { + var s struct { + Metadata map[string]string `json:"metadata"` + } + err := r.ExtractInto(&s) + return s.Metadata, err +} + +// ExtractAvailaiblityZone will extract the availability zone of a backup. +// This requires the client to be set to microversion 3.51 or later. +func (r commonResult) ExtractAvailabilityZone() (string, error) { + var s struct { + AvailabilityZone string `json:"availability_zone"` + } + err := r.ExtractInto(&s) + return s.AvailabilityZone, err +} diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/extensions/backups/requests.go new file mode 100644 index 0000000000..a312f1daa0 --- /dev/null +++ b/openstack/blockstorage/extensions/backups/requests.go @@ -0,0 +1,184 @@ +package backups + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToBackupCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Backup. This object is passed to +// the backups.Create function. For more information about these parameters, +// see the Backup object. +type CreateOpts struct { + // VolumeID is the ID of the volume to create the backup from. + VolumeID string `json:"volume_id" required:"true"` + + // Force will force the creation of a backup regardless of the + //volume's status. + Force bool `json:"force,omitempty"` + + // Name is the name of the backup. + Name string `json:"name,omitempty"` + + // Description is the description of the backup. + Description string `json:"description,omitempty"` + + // Metadata is metadata for the backup. + // Requires microversion 3.43 or later. + Metadata map[string]string `json:"metadata,omitempty"` + + // Container is a container to store the backup. + Container string `json:"container,omitempty"` + + // Incremental is whether the backup should be incremental or not. + Incremental bool `json:"incremental,omitempty"` + + // SnapshotID is the ID of a snapshot to backup. + SnapshotID string `json:"snapshot_id,omitempty"` + + // AvailabilityZone is an availability zone to locate the volume or snapshot. + // Requires microversion 3.51 or later. + AvailabilityZone string `json:"availability_zone,omitempty"` +} + +// ToBackupCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "backup") +} + +// Create will create a new Backup based on the values in CreateOpts. To +// extract the Backup object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToBackupCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} + +// Delete will delete the existing Backup with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// Get retrieves the Backup with the provided ID. To extract the Backup +// object from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToBackupListQuery() (string, error) +} + +type ListOpts struct { + // AllTenants will retrieve backups of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Name will filter by the specified backup name. + // This does not work in later microversions. + Name string `q:"name"` + + // Status will filter by the specified status. + // This does not work in later microversions. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required to use this. + TenantID string `q:"project_id"` + + // VolumeID will filter by a specified volume ID. + // This does not work in later microversions. + VolumeID string `q:"volume_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToBackupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToBackupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Backups 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.ToBackupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return BackupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToBackupUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Backup. +type UpdateOpts struct { + // Name is the name of the backup. + Name *string `json:"name,omitempty"` + + // Description is the description of the backup. + Description *string `json:"description,omitempty"` + + // Metadata is metadata for the backup. + // Requires microversion 3.43 or later. + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToBackupUpdateMap assembles a request body based on the contents of +// an UpdateOpts. +func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update will update the Backup with provided information. To extract +// the updated Backup from the response, call the Extract method on the +// UpdateResult. +// Requires microversion 3.9 or later. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToBackupUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/blockstorage/extensions/backups/results.go b/openstack/blockstorage/extensions/backups/results.go new file mode 100644 index 0000000000..6293f979f1 --- /dev/null +++ b/openstack/blockstorage/extensions/backups/results.go @@ -0,0 +1,155 @@ +package backups + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Backup contains all the information associated with a Cinder Backup. +type Backup struct { + // ID is the Unique identifier of the backup. + ID string `json:"id"` + + // CreatedAt is the date the backup was created. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the date the backup was updated. + UpdatedAt time.Time `json:"-"` + + // Name is the display name of the backup. + Name string `json:"name"` + + // Description is the description of the backup. + Description string `json:"description"` + + // VolumeID is the ID of the Volume from which this backup was created. + VolumeID string `json:"volume_id"` + + // SnapshotID is the ID of the snapshot from which this backup was created. + SnapshotID string `json:"snapshot_id"` + + // Status is the status of the backup. + Status string `json:"status"` + + // Size is the size of the backup, in GB. + Size int `json:"size"` + + // Object Count is the number of objects in the backup. + ObjectCount int `json:"object_count"` + + // Container is the container where the backup is stored. + Container string `json:"container"` + + // AvailabilityZone is the availability zone of the backup. + AvailabilityZone string `json:"availability_zone"` + + // HasDependentBackups is whether there are other backups + // depending on this backup. + HasDependentBackups bool `json:"has_dependent_backups"` + + // FailReason has the reason for the backup failure. + FailReason string `json:"fail_reason"` + + // IsIncremental is whether this is an incremental backup. + IsIncremental bool `json:"is_incremental"` + + // DataTimestamp is the time when the data on the volume was first saved. + DataTimestamp time.Time `json:"-"` + + // ProjectID is the ID of the project that owns the backup. This is + // an admin-only field. + ProjectID string `json:"os-backup-project-attr:project_id"` +} + +// 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 +} + +// BackupPage is a pagination.Pager that is returned from a call to the List function. +type BackupPage struct { + pagination.LinkedPageBase +} + +// UnmarshalJSON converts our JSON API response into our backup struct +func (r *Backup) UnmarshalJSON(b []byte) error { + type tmp Backup + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + DataTimestamp gophercloud.JSONRFC3339MilliNoZ `json:"data_timestamp"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Backup(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DataTimestamp = time.Time(s.DataTimestamp) + + return err +} + +// IsEmpty returns true if a BackupPage contains no Backups. +func (r BackupPage) IsEmpty() (bool, error) { + volumes, err := ExtractBackups(r) + return len(volumes) == 0, err +} + +func (page BackupPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"backups_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractBackups extracts and returns Backups. It is used while iterating over a backups.List call. +func ExtractBackups(r pagination.Page) ([]Backup, error) { + var s []Backup + err := ExtractBackupsInto(r, &s) + return s, err +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Backup object out of the commonResult object. +func (r commonResult) Extract() (*Backup, error) { + var s Backup + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup") +} + +func ExtractBackupsInto(r pagination.Page, v interface{}) error { + return r.(BackupPage).Result.ExtractIntoSlicePtr(v, "backups") +} diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures.go b/openstack/blockstorage/extensions/backups/testing/fixtures.go new file mode 100644 index 0000000000..088a88ae8d --- /dev/null +++ b/openstack/blockstorage/extensions/backups/testing/fixtures.go @@ -0,0 +1,133 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ListResponse = ` +{ + "backups": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "backup-001", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "description": "Daily Backup", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "backup-002", + "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", + "description": "Weekly Backup", + "status": "available", + "size": 25, + "created_at": "2017-05-30T03:35:03.000000" + } + ], + "backups_links": [ + { + "href": "%s/backups?marker=1", + "rel": "next" + } + ] +} +` + +const GetResponse = ` +{ + "backup": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "backup-001", + "description": "Daily backup", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} +` +const CreateRequest = ` +{ + "backup": { + "volume_id": "1234", + "name": "backup-001" + } +} +` + +const CreateResponse = ` +{ + "backup": { + "volume_id": "1234", + "name": "backup-001", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "description": "Daily backup", + "volume_id": "1234", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} +` + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/backups", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ListResponse, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"backups": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/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, GetResponse) + }) +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/backups", 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.StatusAccepted) + + fmt.Fprintf(w, CreateResponse) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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/blockstorage/extensions/backups/testing/requests_test.go b/openstack/blockstorage/extensions/backups/testing/requests_test.go new file mode 100644 index 0000000000..d9d4953734 --- /dev/null +++ b/openstack/blockstorage/extensions/backups/testing/requests_test.go @@ -0,0 +1,96 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + count := 0 + + backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := backups.ExtractBackups(page) + if err != nil { + t.Errorf("Failed to extract backups: %v", err) + return false, err + } + + expected := []backups.Backup{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "backup-001", + VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + Status: "available", + Size: 30, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Daily Backup", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "backup-002", + VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", + Status: "available", + Size: 25, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Weekly Backup", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := backups.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "backup-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := backups.CreateOpts{VolumeID: "1234", Name: "backup-001"} + n, err := backups.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.Name, "backup-001") + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := backups.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/extensions/backups/urls.go b/openstack/blockstorage/extensions/backups/urls.go new file mode 100644 index 0000000000..ed09773519 --- /dev/null +++ b/openstack/blockstorage/extensions/backups/urls.go @@ -0,0 +1,23 @@ +package backups + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups") +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} From e0311c0920d47879e99ac9826142bc097eacd5b7 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Wed, 1 May 2019 21:45:45 -0400 Subject: [PATCH 0732/2296] Baremetal API V1: Support new config drive JSON for nodes (#1510) Config drives may be a URL, a base64-encoded gzipped ISO9660 image, or beginning in Ironic API microversion 1.56, a JSON blob with user, network, and meta data. --- openstack/baremetal/v1/nodes/requests.go | 10 ++++- .../baremetal/v1/nodes/testing/fixtures.go | 45 +++++++++++++++++++ .../v1/nodes/testing/requests_test.go | 16 +++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index aab75488b6..ffb2287dca 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -399,11 +399,19 @@ type ProvisionStateOptsBuilder interface { ToProvisionStateMap() (map[string]interface{}, error) } +// Starting with Ironic API version 1.56, a configdrive may be a JSON object with structured data. +// Prior to this version, it must be a base64-encoded, gzipped ISO9660 image. +type ConfigDrive struct { + MetaData map[string]interface{} `json:"meta_data,omitempty"` + NetworkData map[string]interface{} `json:"network_data,omitempty"` + UserData interface{} `json:"user_data,omitempty"` +} + // ProvisionStateOpts for a request to change a node's provision state. A config drive should be base64-encoded // gzipped ISO9660 image. type ProvisionStateOpts struct { Target TargetProvisionState `json:"target" required:"true"` - ConfigDrive string `json:"configdrive,omitempty"` + ConfigDrive interface{} `json:"configdrive,omitempty"` CleanSteps []CleanStep `json:"clean_steps,omitempty"` RescuePassword string `json:"rescue_password,omitempty"` } diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 65aee61e70..4b10e50980 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -562,6 +562,27 @@ const NodeProvisionStateCleanBody = ` } ` +const NodeProvisionStateConfigDriveBody = ` +{ + "target": "active", + "configdrive": { + "user_data": { + "ignition": { + "version": "2.2.0" + }, + "systemd": { + "units": [ + { + "enabled": true, + "name": "example.service" + } + ] + } + } + } +} +` + var ( NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", @@ -747,6 +768,21 @@ var ( Protected: false, ProtectedReason: "", } + + ConfigDriveMap = nodes.ConfigDrive{ + UserData: map[string]interface{}{ + "ignition": map[string]string{ + "version": "2.2.0", + }, + "systemd": map[string]interface{}{ + "units": []map[string]interface{}{{ + "name": "example.service", + "enabled": true, + }, + }, + }, + }, + } ) // HandleNodeListSuccessfully sets up the test server to respond to a server List request. @@ -919,6 +955,15 @@ func HandleNodeChangeProvisionStateCleanWithConflict(t *testing.T) { }) } +func HandleNodeChangeProvisionStateConfigDrive(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateConfigDriveBody) + w.WriteHeader(http.StatusAccepted) + }) +} + // HandleChangePowerStateSuccessfully sets up the test server to respond to a change power state request for a node func HandleChangePowerStateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/power", func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 5117cd2513..2a1afa418a 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -254,6 +254,22 @@ func TestNodeChangeProvisionStateActive(t *testing.T) { th.AssertNoErr(t, err) } +func TestHandleNodeChangeProvisionStateConfigDrive(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleNodeChangeProvisionStateConfigDrive(t) + + c := client.ServiceClient() + + err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + Target: nodes.TargetActive, + ConfigDrive: ConfigDriveMap, + }).ExtractErr() + + th.AssertNoErr(t, err) +} + func TestNodeChangeProvisionStateClean(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From cf50ab59cfdcadbeee67883655f16253b5695c90 Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Fri, 3 May 2019 21:12:03 -0400 Subject: [PATCH 0733/2296] Baremetal API v1: declare types for all constants (#1558) In a const() block, the type of the first item does not cascade to the rest of the items. Fixes #1557 --- openstack/baremetal/v1/nodes/requests.go | 74 ++++++++++++------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index ffb2287dca..4a19dfeede 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -19,30 +19,30 @@ type ProvisionState string const ( Enroll ProvisionState = "enroll" - Verifying = "verifying" - Manageable = "manageable" - Available = "available" - Active = "active" - DeployWait = "wait call-back" - Deploying = "deploying" - DeployFail = "deploy failed" - DeployDone = "deploy complete" - Deleting = "deleting" - Deleted = "deleted" - Cleaning = "cleaning" - CleanWait = "clean wait" - CleanFail = "clean failed" - Error = "error" - Rebuild = "rebuild" - Inspecting = "inspecting" - InspectFail = "inspect failed" - InspectWait = "inspect wait" - Adopting = "adopting" - AdoptFail = "adopt failed" - Rescue = "rescue" - RescueFail = "rescue failed" - Rescuing = "rescuing" - UnrescueFail = "unrescue failed" + Verifying ProvisionState = "verifying" + Manageable ProvisionState = "manageable" + Available ProvisionState = "available" + Active ProvisionState = "active" + DeployWait ProvisionState = "wait call-back" + Deploying ProvisionState = "deploying" + DeployFail ProvisionState = "deploy failed" + DeployDone ProvisionState = "deploy complete" + Deleting ProvisionState = "deleting" + Deleted ProvisionState = "deleted" + Cleaning ProvisionState = "cleaning" + CleanWait ProvisionState = "clean wait" + CleanFail ProvisionState = "clean failed" + Error ProvisionState = "error" + Rebuild ProvisionState = "rebuild" + Inspecting ProvisionState = "inspecting" + InspectFail ProvisionState = "inspect failed" + InspectWait ProvisionState = "inspect wait" + Adopting ProvisionState = "adopting" + AdoptFail ProvisionState = "adopt failed" + Rescue ProvisionState = "rescue" + RescueFail ProvisionState = "rescue failed" + Rescuing ProvisionState = "rescuing" + UnrescueFail ProvisionState = "unrescue failed" ) // TargetProvisionState is used when setting the provision state for a node. @@ -50,15 +50,15 @@ type TargetProvisionState string const ( TargetActive TargetProvisionState = "active" - TargetDeleted = "deleted" - TargetManage = "manage" - TargetProvide = "provide" - TargetInspect = "inspect" - TargetAbort = "abort" - TargetClean = "clean" - TargetAdopt = "adopt" - TargetRescue = "rescue" - TargetUnrescue = "unrescue" + TargetDeleted TargetProvisionState = "deleted" + TargetManage TargetProvisionState = "manage" + TargetProvide TargetProvisionState = "provide" + TargetInspect TargetProvisionState = "inspect" + TargetAbort TargetProvisionState = "abort" + TargetClean TargetProvisionState = "clean" + TargetAdopt TargetProvisionState = "adopt" + TargetRescue TargetProvisionState = "rescue" + TargetUnrescue TargetProvisionState = "unrescue" ) // ListOpts allows the filtering and sorting of paginated collections through @@ -446,10 +446,10 @@ type TargetPowerState string // TargetPowerState is used when changing the power state of a node. const ( PowerOn TargetPowerState = "power on" - PowerOff = "power off" - Rebooting = "rebooting" - SoftPowerOff = "soft power off" - SoftRebooting = "soft rebooting" + PowerOff TargetPowerState = "power off" + Rebooting TargetPowerState = "rebooting" + SoftPowerOff TargetPowerState = "soft power off" + SoftRebooting TargetPowerState = "soft rebooting" ) // PowerStateOptsBuilder allows extensions to add additional parameters to the ChangePowerState request. From 6f9faf57fddcafd31b69d28122dce7bb5ce3bc5c Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Fri, 3 May 2019 21:13:07 -0400 Subject: [PATCH 0734/2296] Baremetal API V1: Support set RAID config (#1559) --- .../openstack/baremetal/noauth/nodes_test.go | 28 +++++ .../openstack/baremetal/v1/baremetal.go | 1 + .../openstack/baremetal/v1/nodes_test.go | 28 +++++ openstack/baremetal/v1/nodes/requests.go | 105 ++++++++++++++++++ .../baremetal/v1/nodes/testing/fixtures.go | 40 +++++++ .../v1/nodes/testing/requests_test.go | 45 ++++++++ openstack/baremetal/v1/nodes/urls.go | 4 + 7 files changed, 251 insertions(+) diff --git a/acceptance/openstack/baremetal/noauth/nodes_test.go b/acceptance/openstack/baremetal/noauth/nodes_test.go index 32796092a1..8095cf59a0 100644 --- a/acceptance/openstack/baremetal/noauth/nodes_test.go +++ b/acceptance/openstack/baremetal/noauth/nodes_test.go @@ -65,3 +65,31 @@ func TestNodesUpdate(t *testing.T) { th.AssertEquals(t, updated.Maintenance, true) } + +func TestNodesRAIDConfig(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1NoAuthClient() + th.AssertNoErr(t, err) + client.Microversion = "1.50" + + node, err := v1.CreateNode(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + + sizeGB := 100 + isTrue := true + + err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ + LogicalDisks: []nodes.LogicalDisk{ + { + SizeGB: &sizeGB, + IsRootVolume: &isTrue, + RAIDLevel: nodes.RAID5, + DiskType: nodes.HDD, + NumberOfPhysicalDisks: 5, + }, + }, + }).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/acceptance/openstack/baremetal/v1/baremetal.go b/acceptance/openstack/baremetal/v1/baremetal.go index bce8da5c56..332fb4c159 100644 --- a/acceptance/openstack/baremetal/v1/baremetal.go +++ b/acceptance/openstack/baremetal/v1/baremetal.go @@ -19,6 +19,7 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, e Name: name, Driver: "ipmi", BootInterface: "pxe", + RAIDInterface: "agent", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", "ipmi_username": "admin", diff --git a/acceptance/openstack/baremetal/v1/nodes_test.go b/acceptance/openstack/baremetal/v1/nodes_test.go index 2d12904174..27afaef806 100644 --- a/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/acceptance/openstack/baremetal/v1/nodes_test.go @@ -66,3 +66,31 @@ func TestNodesUpdate(t *testing.T) { th.AssertEquals(t, updated.Maintenance, true) } + +func TestNodesRAIDConfig(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.50" + + node, err := CreateNode(t, client) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node) + + sizeGB := 100 + isTrue := true + + err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ + LogicalDisks: []nodes.LogicalDisk{ + { + SizeGB: &sizeGB, + IsRootVolume: &isTrue, + RAIDLevel: nodes.RAID5, + DiskType: nodes.HDD, + NumberOfPhysicalDisks: 5, + }, + }, + }).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 4a19dfeede..4707f7b250 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -486,3 +486,108 @@ func ChangePowerState(client *gophercloud.ServiceClient, id string, opts PowerSt }) return } + +// This is the desired RAID configuration on the bare metal node. +type RAIDConfigOpts struct { + LogicalDisks []LogicalDisk `json:"logical_disks"` +} + +// RAIDConfigOptsBuilder allows extensions to modify a set RAID config request. +type RAIDConfigOptsBuilder interface { + ToRAIDConfigMap() (map[string]interface{}, error) +} + +// RAIDLevel type is used to specify the RAID level for a logical disk. +type RAIDLevel string + +const ( + RAID0 RAIDLevel = "0" + RAID1 RAIDLevel = "1" + RAID2 RAIDLevel = "2" + RAID5 RAIDLevel = "5" + RAID6 RAIDLevel = "6" + RAID10 RAIDLevel = "1+0" + RAID50 RAIDLevel = "5+0" + RAID60 RAIDLevel = "6+0" +) + +// DiskType is used to specify the disk type for a logical disk, e.g. hdd or ssd. +type DiskType string + +const ( + HDD DiskType = "hdd" + SSD DiskType = "ssd" +) + +// InterfaceType is used to specify the interface for a logical disk. +type InterfaceType string + +const ( + SATA DiskType = "sata" + SCSI DiskType = "scsi" + SAS DiskType = "sas" +) + +type LogicalDisk struct { + // Size (Integer) of the logical disk to be created in GiB. If unspecified, "MAX" will be used. + SizeGB *int `json:"size_gb"` + + // RAID level for the logical disk. + RAIDLevel RAIDLevel `json:"raid_level" required:"true"` + + // Name of the volume. Should be unique within the Node. If not specified, volume name will be auto-generated. + VolumeName string `json:"volume_name,omitempty"` + + // Set to true if this is the root volume. At most one logical disk can have this set to true. + IsRootVolume *bool `json:"is_root_volume,omitempty"` + + // Set to true if this logical disk can share physical disks with other logical disks. + SharePhysicalDisks *bool `json:"share_physical_disks,omitempty"` + + // If this is not specified, disk type will not be a criterion to find backing physical disks + DiskType DiskType `json:"disk_type,omitempty"` + + // If this is not specified, interface type will not be a criterion to find backing physical disks. + InterfaceType InterfaceType `json:"interface_type,omitempty"` + + // Integer, number of disks to use for the logical disk. Defaults to minimum number of disks required + // for the particular RAID level. + NumberOfPhysicalDisks int `json:"number_of_physical_disks,omitempty"` + + // The name of the controller as read by the RAID interface. + Controller string `json:"controller,omitempty"` + + // A list of physical disks to use as read by the RAID interface. + PhysicalDisks []string `json:"physical_disks,omitempty"` +} + +func (opts RAIDConfigOpts) ToRAIDConfigMap() (map[string]interface{}, error) { + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + for _, v := range body["logical_disks"].([]interface{}) { + if logicalDisk, ok := v.(map[string]interface{}); ok { + if logicalDisk["size_gb"] == nil { + logicalDisk["size_gb"] = "MAX" + } + } + } + + return body, nil +} + +// Request to change a Node's RAID config. +func SetRAIDConfig(client *gophercloud.ServiceClient, id string, raidConfigOptsBuilder RAIDConfigOptsBuilder) (r ChangeStateResult) { + reqBody, err := raidConfigOptsBuilder.ToRAIDConfigMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Put(raidConfigURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 4b10e50980..1717f7a586 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -991,3 +991,43 @@ func HandleChangePowerStateWithConflict(t *testing.T) { w.WriteHeader(http.StatusConflict) }) } + +func HandleSetRAIDConfig(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/raid", 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, ` + { + "logical_disks" : [ + { + "size_gb" : 100, + "is_root_volume" : true, + "raid_level" : "1" + } + ] + } + `) + + w.WriteHeader(http.StatusNoContent) + }) +} + +func HandleSetRAIDConfigMaxSize(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/raid", 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, ` + { + "logical_disks" : [ + { + "size_gb" : "MAX", + "is_root_volume" : true, + "raid_level" : "1" + } + ] + } + `) + + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 2a1afa418a..20c578895d 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -385,3 +385,48 @@ func TestChangePowerStateWithConflict(t *testing.T) { t.Fatal("ErrDefault409 was expected to occur") } } + +func TestSetRAIDConfig(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSetRAIDConfig(t) + + sizeGB := 100 + isRootVolume := true + + config := nodes.RAIDConfigOpts{ + LogicalDisks: []nodes.LogicalDisk{ + { + SizeGB: &sizeGB, + IsRootVolume: &isRootVolume, + RAIDLevel: nodes.RAID1, + }, + }, + } + + c := client.ServiceClient() + err := nodes.SetRAIDConfig(c, "1234asdf", config).ExtractErr() + th.AssertNoErr(t, err) +} + +// Without specifying a size, we need to send a string: "MAX" +func TestSetRAIDConfigMaxSize(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSetRAIDConfigMaxSize(t) + + isRootVolume := true + + config := nodes.RAIDConfigOpts{ + LogicalDisks: []nodes.LogicalDisk{ + { + IsRootVolume: &isRootVolume, + RAIDLevel: nodes.RAID1, + }, + }, + } + + c := client.ServiceClient() + err := nodes.SetRAIDConfig(c, "1234asdf", config).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index 8f9fe05e9c..c7ef550365 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -53,3 +53,7 @@ func powerStateURL(client *gophercloud.ServiceClient, id string) string { func provisionStateURL(client *gophercloud.ServiceClient, id string) string { return statesResourceURL(client, id, "provision") } + +func raidConfigURL(client *gophercloud.ServiceClient, id string) string { + return statesResourceURL(client, id, "raid") +} From 844afee4f565b62ca26e65f6487014c959a917fb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 8 May 2019 19:35:33 -0600 Subject: [PATCH 0735/2296] DNSv2: Fix recordsets TTL update (#1563) --- .../openstack/dns/v2/recordsets_test.go | 27 ++++++++++++++++++- openstack/dns/v2/recordsets/requests.go | 17 ++++++++---- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/dns/v2/recordsets_test.go b/acceptance/openstack/dns/v2/recordsets_test.go index 8cd4b7231d..683384e1e7 100644 --- a/acceptance/openstack/dns/v2/recordsets_test.go +++ b/acceptance/openstack/dns/v2/recordsets_test.go @@ -75,7 +75,6 @@ func TestRecordSetsCRUD(t *testing.T) { description := "" updateOpts := recordsets.UpdateOpts{ Description: &description, - TTL: 0, } newRS, err := recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() @@ -84,4 +83,30 @@ func TestRecordSetsCRUD(t *testing.T) { tools.PrintResource(t, &newRS) th.AssertEquals(t, newRS.Description, description) + + records := []string{"10.1.0.3"} + updateOpts = recordsets.UpdateOpts{ + Records: records, + } + + newRS, err = recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, &newRS) + + th.AssertDeepEquals(t, newRS.Records, records) + th.AssertEquals(t, newRS.TTL, 3600) + + ttl := 0 + updateOpts = recordsets.UpdateOpts{ + TTL: &ttl, + } + + newRS, err = recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, &newRS) + + th.AssertDeepEquals(t, newRS.Records, records) + th.AssertEquals(t, newRS.TTL, ttl) } diff --git a/openstack/dns/v2/recordsets/requests.go b/openstack/dns/v2/recordsets/requests.go index 2bc4579727..1d9a77bcf5 100644 --- a/openstack/dns/v2/recordsets/requests.go +++ b/openstack/dns/v2/recordsets/requests.go @@ -122,7 +122,7 @@ type UpdateOpts struct { Description *string `json:"description,omitempty"` // TTL is the time to live of the RecordSet. - TTL int `json:"ttl,omitempty"` + TTL *int `json:"ttl,omitempty"` // Records are the DNS records of the RecordSet. Records []string `json:"records,omitempty"` @@ -135,10 +135,17 @@ func (opts UpdateOpts) ToRecordSetUpdateMap() (map[string]interface{}, error) { return nil, err } - if opts.TTL > 0 { - b["ttl"] = opts.TTL - } else { - b["ttl"] = nil + // If opts.TTL was actually set, use 0 as a special value to send "null", + // even though the result from the API is 0. + // + // Otherwise, don't send the TTL field. + if opts.TTL != nil { + ttl := *(opts.TTL) + if ttl > 0 { + b["ttl"] = ttl + } else { + b["ttl"] = nil + } } return b, nil From 7892efa714f10951c5483a28c7471d8051b12975 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 8 May 2019 21:26:23 -0600 Subject: [PATCH 0736/2296] DNSv2: Fixing unit test (#1564) --- openstack/dns/v2/recordsets/testing/requests_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openstack/dns/v2/recordsets/testing/requests_test.go b/openstack/dns/v2/recordsets/testing/requests_test.go index 40285265df..07f8cebabf 100644 --- a/openstack/dns/v2/recordsets/testing/requests_test.go +++ b/openstack/dns/v2/recordsets/testing/requests_test.go @@ -110,8 +110,9 @@ func TestUpdate(t *testing.T) { HandleUpdateSuccessfully(t) var description = "Updated description" + ttl := 0 updateOpts := recordsets.UpdateOpts{ - TTL: 0, + TTL: &ttl, Description: &description, Records: []string{"10.1.0.2", "10.1.0.3"}, } From 1992d5238d782a2ae0063b16eb9705548e6b351b Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 15 May 2019 03:18:19 +0200 Subject: [PATCH 0737/2296] Don't print token on token auth method (#1567) --- acceptance/clients/http.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/acceptance/clients/http.go b/acceptance/clients/http.go index c441d8abb6..9e64b7c5c6 100644 --- a/acceptance/clients/http.go +++ b/acceptance/clients/http.go @@ -129,6 +129,9 @@ func (lrt *LogRoundTripper) formatJSON(raw []byte) string { v["password"] = "***" } } + if v, ok := v["token"].(map[string]interface{}); ok { + v["id"] = "***" + } } } From ad4210895ed0335a9b5e178b51f22e16a4fc6a8d Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 16 May 2019 16:46:03 +0200 Subject: [PATCH 0738/2296] Hide application credential auth while debug (#1571) --- acceptance/clients/http.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/acceptance/clients/http.go b/acceptance/clients/http.go index 9e64b7c5c6..c8d266f89d 100644 --- a/acceptance/clients/http.go +++ b/acceptance/clients/http.go @@ -129,6 +129,9 @@ func (lrt *LogRoundTripper) formatJSON(raw []byte) string { v["password"] = "***" } } + if v, ok := v["application_credential"].(map[string]interface{}); ok { + v["secret"] = "***" + } if v, ok := v["token"].(map[string]interface{}); ok { v["id"] = "***" } From b3a23cc94cc51857a508733a0bd3d6ef0bfc16d9 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Thu, 16 May 2019 18:57:35 +0200 Subject: [PATCH 0739/2296] do not swallow connection errors in tokens.Get() (#1570) --- openstack/identity/v3/tokens/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index 2d20fa6f4b..e4d766b232 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -134,9 +134,9 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { OkCodes: []int{200, 203}, }) if resp != nil { - r.Err = err r.Header = resp.Header } + r.Err = err return } From 9c4c78674f59b5e8ae0e006c62139a93582009a1 Mon Sep 17 00:00:00 2001 From: Feilong Wang Date: Tue, 21 May 2019 11:56:22 +1200 Subject: [PATCH 0740/2296] Add parent_resource attribute for Heat stack resource (#1576) The attribute `parent_resource` is helpful when building a reverse mapping between the resource and their parents. [1] https://github.com/openstack/heat/blob/master/heat/rpc/api.py#L76 --- .../v1/stackresources/results.go | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go index 5ea5242aec..3e7613b07f 100644 --- a/openstack/orchestration/v1/stackresources/results.go +++ b/openstack/orchestration/v1/stackresources/results.go @@ -10,18 +10,19 @@ import ( // Resource represents a stack resource. type Resource struct { - Attributes map[string]interface{} `json:"attributes"` - CreationTime time.Time `json:"-"` - Description string `json:"description"` - Links []gophercloud.Link `json:"links"` - LogicalID string `json:"logical_resource_id"` - Name string `json:"resource_name"` - PhysicalID string `json:"physical_resource_id"` - RequiredBy []interface{} `json:"required_by"` - Status string `json:"resource_status"` - StatusReason string `json:"resource_status_reason"` - Type string `json:"resource_type"` - UpdatedTime time.Time `json:"-"` + Attributes map[string]interface{} `json:"attributes"` + CreationTime time.Time `json:"-"` + Description string `json:"description"` + Links []gophercloud.Link `json:"links"` + LogicalID string `json:"logical_resource_id"` + Name string `json:"resource_name"` + PhysicalID string `json:"physical_resource_id"` + RequiredBy []interface{} `json:"required_by"` + Status string `json:"resource_status"` + StatusReason string `json:"resource_status_reason"` + Type string `json:"resource_type"` + UpdatedTime time.Time `json:"-"` + ParentResource string `json:"parent_resource"` } func (r *Resource) UnmarshalJSON(b []byte) error { From e87e5f90e7e67a309e0fe96dac144b824447607f Mon Sep 17 00:00:00 2001 From: Feilong Wang Date: Tue, 21 May 2019 11:57:22 +1200 Subject: [PATCH 0741/2296] Support mark unhealthy action for stack resource (#1574) Heat supports mark a resource as unhealthy via API, which is very useful for auto healing use case. After marked a resource unhealthy, the status of the resource will be 'CHECK_FAILED', then the resource will be rebuilt as long as a Heat stack update triggered. --- .../orchestration/v1/stackresources_test.go | 13 +++++++ .../v1/stackresources/requests.go | 36 +++++++++++++++++++ .../v1/stackresources/results.go | 5 +++ .../v1/stackresources/testing/fixtures.go | 13 +++++++ .../stackresources/testing/requests_test.go | 13 +++++++ .../orchestration/v1/stackresources/urls.go | 4 +++ 6 files changed, 84 insertions(+) diff --git a/acceptance/openstack/orchestration/v1/stackresources_test.go b/acceptance/openstack/orchestration/v1/stackresources_test.go index 40d5f87cff..02eb78bbab 100644 --- a/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -27,6 +27,19 @@ func TestStackResources(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, metadata) + markUnhealthyOpts := &stackresources.MarkUnhealthyOpts{ + MarkUnhealthy: true, + ResourceStatusReason: "Wrong security policy is detected.", + } + + err = stackresources.MarkUnhealthy(client, stack.Name, stack.ID, basicTemplateResourceName, markUnhealthyOpts).ExtractErr() + th.AssertNoErr(t, err) + + unhealthyResource, err := stackresources.Get(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "CHECK_FAILED", unhealthyResource.Status) + tools.PrintResource(t, unhealthyResource) + allPages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages() th.AssertNoErr(t, err) allResources, err := stackresources.ExtractResources(allPages) diff --git a/openstack/orchestration/v1/stackresources/requests.go b/openstack/orchestration/v1/stackresources/requests.go index f368b76c6d..306ada629d 100644 --- a/openstack/orchestration/v1/stackresources/requests.go +++ b/openstack/orchestration/v1/stackresources/requests.go @@ -75,3 +75,39 @@ func Template(c *gophercloud.ServiceClient, resourceType string) (r TemplateResu _, r.Err = c.Get(templateURL(c, resourceType), &r.Body, nil) return } + +// MarkUnhealthyOpts contains the common options struct used in this package's +// MarkUnhealthy operations. +type MarkUnhealthyOpts struct { + // A boolean indicating whether the target resource should be marked as unhealthy. + MarkUnhealthy bool `json:"mark_unhealthy"` + // The reason for the current stack resource state. + ResourceStatusReason string `json:"resource_status_reason,omitempty"` +} + +// MarkUnhealthyOptsBuilder is the interface options structs have to satisfy in order +// to be used in the MarkUnhealthy operation in this package +type MarkUnhealthyOptsBuilder interface { + ToMarkUnhealthyMap() (map[string]interface{}, error) +} + +// ToMarkUnhealthyMap validates that a template was supplied and calls +// the ToMarkUnhealthyMap private function. +func (opts MarkUnhealthyOpts) ToMarkUnhealthyMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +// MarkUnhealthy marks the specified resource in the stack as unhealthy. +func MarkUnhealthy(c *gophercloud.ServiceClient, stackName, stackID, resourceName string, opts MarkUnhealthyOptsBuilder) (r MarkUnhealthyResult) { + b, err := opts.ToMarkUnhealthyMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Patch(markUnhealthyURL(c, stackName, stackID, resourceName), b, nil, nil) + return +} diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go index 3e7613b07f..f86f85df3b 100644 --- a/openstack/orchestration/v1/stackresources/results.go +++ b/openstack/orchestration/v1/stackresources/results.go @@ -205,3 +205,8 @@ func (r TemplateResult) Extract() ([]byte, error) { template, err := json.MarshalIndent(r.Body, "", " ") return template, err } + +// MarkUnhealthyResult represents the result of a mark unhealthy operation. +type MarkUnhealthyResult struct { + gophercloud.ErrResult +} diff --git a/openstack/orchestration/v1/stackresources/testing/fixtures.go b/openstack/orchestration/v1/stackresources/testing/fixtures.go index 0e64e9b367..9382736441 100644 --- a/openstack/orchestration/v1/stackresources/testing/fixtures.go +++ b/openstack/orchestration/v1/stackresources/testing/fixtures.go @@ -441,3 +441,16 @@ func HandleGetTemplateSuccessfully(t *testing.T, output string) { fmt.Fprintf(w, output) }) } + +// HandleMarkUnhealthySuccessfully creates an HTTP handler at `/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance` +// on the test handler mux that responds with a `MarkUnhealthy` response. +func HandleMarkUnhealthySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/stacks/teststack/0b1771bd-9336-4f2b-ae86-a80f971faf1e/resources/wordpress_instance", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + }) +} diff --git a/openstack/orchestration/v1/stackresources/testing/requests_test.go b/openstack/orchestration/v1/stackresources/testing/requests_test.go index c714047fa3..55ca4a0be4 100644 --- a/openstack/orchestration/v1/stackresources/testing/requests_test.go +++ b/openstack/orchestration/v1/stackresources/testing/requests_test.go @@ -110,3 +110,16 @@ func TestGetResourceTemplate(t *testing.T) { expected := GetTemplateExpected th.AssertDeepEquals(t, expected, string(actual)) } + +func TestMarkUnhealthyResource(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleMarkUnhealthySuccessfully(t) + + markUnhealthyOpts := &stackresources.MarkUnhealthyOpts{ + MarkUnhealthy: true, + ResourceStatusReason: "Kubelet.Ready is Unknown more than 10 mins.", + } + err := stackresources.MarkUnhealthy(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance", markUnhealthyOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/orchestration/v1/stackresources/urls.go b/openstack/orchestration/v1/stackresources/urls.go index bbddc69cbc..6b332f17e8 100644 --- a/openstack/orchestration/v1/stackresources/urls.go +++ b/openstack/orchestration/v1/stackresources/urls.go @@ -29,3 +29,7 @@ func schemaURL(c *gophercloud.ServiceClient, typeName string) string { func templateURL(c *gophercloud.ServiceClient, typeName string) string { return c.ServiceURL("resource_types", typeName, "template") } + +func markUnhealthyURL(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) string { + return c.ServiceURL("stacks", stackName, stackID, "resources", resourceName) +} From 4885c347dcf48fc518798d37fb93ffa7bdaa43e9 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 23 May 2019 22:30:39 +0200 Subject: [PATCH 0742/2296] Proper port binding unset (#1579) --- .../networking/v2/extensions/portsbinding/portsbinding.go | 3 ++- .../v2/extensions/portsbinding/portsbinding_test.go | 7 ++++++- .../networking/v2/extensions/portsbinding/requests.go | 7 ++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index ed0549d5bc..ef3179e6a9 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -18,7 +18,7 @@ type PortWithBindingExt struct { // CreatePortsbinding will create a port on the specified subnet. An error will be // returned if the port could not be created. -func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string) (PortWithBindingExt, error) { +func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string, profile map[string]interface{}) (PortWithBindingExt, error) { portName := tools.RandomString("TESTACC-", 8) portDescription := tools.RandomString("TESTACC-PORT-DESC-", 8) iFalse := false @@ -36,6 +36,7 @@ func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, network createOpts := portsbinding.CreateOptsExt{ CreateOptsBuilder: portCreateOpts, HostID: hostID, + Profile: profile, } var s PortWithBindingExt diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go index 47c8412e08..d699c786a6 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -31,20 +31,23 @@ func TestPortsbindingCRUD(t *testing.T) { // Define a host hostID := "localhost" + profile := map[string]interface{}{"foo": "bar"} // Create port - port, err := CreatePortsbinding(t, client, network.ID, subnet.ID, hostID) + port, err := CreatePortsbinding(t, client, network.ID, subnet.ID, hostID, profile) th.AssertNoErr(t, err) 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) // Update port newPortName := "" newPortDescription := "" newHostID := "127.0.0.1" + newProfile := map[string]interface{}{} updateOpts := ports.UpdateOpts{ Name: &newPortName, Description: &newPortDescription, @@ -56,6 +59,7 @@ func TestPortsbindingCRUD(t *testing.T) { UpdateOptsBuilder: updateOpts, HostID: &newHostID, VNICType: "baremetal", + Profile: newProfile, } var newPort PortWithBindingExt @@ -72,4 +76,5 @@ func TestPortsbindingCRUD(t *testing.T) { th.AssertEquals(t, newPort.Description, newPortDescription) th.AssertEquals(t, newPort.HostID, newHostID) th.AssertEquals(t, newPort.VNICType, "baremetal") + th.AssertDeepEquals(t, newPort.Profile, newProfile) } diff --git a/openstack/networking/v2/extensions/portsbinding/requests.go b/openstack/networking/v2/extensions/portsbinding/requests.go index 647b67ff18..e38000904e 100644 --- a/openstack/networking/v2/extensions/portsbinding/requests.go +++ b/openstack/networking/v2/extensions/portsbinding/requests.go @@ -84,7 +84,12 @@ func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { } if opts.Profile != nil { - port["binding:profile"] = opts.Profile + if len(opts.Profile) == 0 { + // send null instead of the empty json object ("{}") + port["binding:profile"] = nil + } else { + port["binding:profile"] = opts.Profile + } } return base, nil From 7cdf87ec09734d2c1aa9dd3a2dbed17fe16e393c Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 25 May 2019 09:16:35 +0300 Subject: [PATCH 0743/2296] Networking V2: add QoS policy extension for Port (#1580) * Networking V2: add QoS policy extension for Port Allow to Create a Port with the QoS policy. Allow to add or delete a QoS policy from an existing Port. Allow to retrieve a Port with QoS policy from the API. * Networking V2: cleanup QoS for Ports Cleanup format, docs and tests. --- .../v2/extensions/qos/policies/doc.go | 93 +++++++++++ .../v2/extensions/qos/policies/requests.go | 59 +++++++ .../v2/extensions/qos/policies/results.go | 7 + .../qos/policies/testing/fixtures.go | 68 ++++++++ .../qos/policies/testing/requests_test.go | 152 ++++++++++++++++++ 5 files changed, 379 insertions(+) create mode 100644 openstack/networking/v2/extensions/qos/policies/doc.go create mode 100644 openstack/networking/v2/extensions/qos/policies/requests.go create mode 100644 openstack/networking/v2/extensions/qos/policies/results.go create mode 100644 openstack/networking/v2/extensions/qos/policies/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/qos/policies/testing/requests_test.go diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go new file mode 100644 index 0000000000..f9184e1d6d --- /dev/null +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -0,0 +1,93 @@ +/* +Package policies provides information and interaction with the QoS policy extension +for the OpenStack Networking service. + +Example to Get a Port with a QoS policy + + var portWithQoS struct { + ports.Port + policies.QoSPolicyExt + } + + portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + + err = ports.Get(client, portID).ExtractInto(&portWithQoS) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Port: %+v\n", portWithQoS) + +Example to Create a Port with a QoS policy + + var portWithQoS struct { + ports.Port + policies.QoSPolicyExt + } + + policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + networkID := "7069db8d-e817-4b39-a654-d2dd76e73d36" + + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + } + + createOpts := policies.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + QoSPolicyID: policyID, + } + + err = ports.Create(client, createOpts).ExtractInto(&portWithQoS) + if err != nil { + panic(err) + } + + fmt.Printf("Port: %+v\n", portWithQoS) + +Example to add a QoS policy to an existing Port + + var portWithQoS struct { + ports.Port + policies.QoSPolicyExt + } + + portUpdateOpts := ports.UpdateOpts{} + + policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + + updateOpts := policies.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + QoSPolicyID: &policyID, + } + + err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) + if err != nil { + panic(err) + } + + fmt.Printf("Port: %+v\n", portWithQoS) + +Example to delete a QoS policy from the existing Port + + var portWithQoS struct { + ports.Port + policies.QoSPolicyExt + } + + portUpdateOpts := ports.UpdateOpts{} + + 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) + } + + fmt.Printf("Port: %+v\n", portWithQoS) +*/ +package policies diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go new file mode 100644 index 0000000000..56da4d8214 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -0,0 +1,59 @@ +package policies + +import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" +) + +// PortCreateOptsExt adds QoS options to the base ports.CreateOpts. +type PortCreateOptsExt struct { + ports.CreateOptsBuilder + + // QoSPolicyID represents an associated QoS policy. + QoSPolicyID string `json:"qos_policy_id,omitempty"` +} + +// ToPortCreateMap casts a CreateOpts struct to a map. +func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToPortCreateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]interface{}) + + if opts.QoSPolicyID != "" { + port["qos_policy_id"] = opts.QoSPolicyID + } + + return base, nil +} + +// PortUpdateOptsExt adds QoS options to the base ports.UpdateOpts. +type PortUpdateOptsExt struct { + ports.UpdateOptsBuilder + + // QoSPolicyID represents an associated QoS policy. + // Setting it to a pointer of an empty string will remove associated QoS policy from port. + QoSPolicyID *string `json:"qos_policy_id,omitempty"` +} + +// ToPortUpdateMap casts a UpdateOpts struct to a map. +func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]interface{}) + + if opts.QoSPolicyID != nil { + qosPolicyID := *opts.QoSPolicyID + if qosPolicyID != "" { + port["qos_policy_id"] = qosPolicyID + } else { + port["qos_policy_id"] = nil + } + } + + return base, nil +} diff --git a/openstack/networking/v2/extensions/qos/policies/results.go b/openstack/networking/v2/extensions/qos/policies/results.go new file mode 100644 index 0000000000..d44f8a3ca6 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/policies/results.go @@ -0,0 +1,7 @@ +package policies + +// QoSPolicyExt represents additional resource attributes available with the QoS extension. +type QoSPolicyExt struct { + // QoSPolicyID represents an associated QoS policy. + QoSPolicyID string `json:"qos_policy_id"` +} diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go new file mode 100644 index 0000000000..12849a2261 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go @@ -0,0 +1,68 @@ +package testing + +const GetPortResponse = ` +{ + "port": { + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const CreatePortRequest = ` +{ + "port": { + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const CreatePortResponse = ` +{ + "port": { + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const UpdatePortWithPolicyRequest = ` +{ + "port": { + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const UpdatePortWithPolicyResponse = ` +{ + "port": { + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const UpdatePortWithoutPolicyRequest = ` +{ + "port": { + "qos_policy_id": null + } +} +` + +const UpdatePortWithoutPolicyResponse = ` +{ + "port": { + "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", + "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "qos_policy_id": "" + } +} +` diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go new file mode 100644 index 0000000000..ef606c449f --- /dev/null +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -0,0 +1,152 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestGetPort(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, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + _, err := fmt.Fprintf(w, GetPortResponse) + th.AssertNoErr(t, err) + }) + + var p struct { + ports.Port + policies.QoSPolicyExt + } + err := ports.Get(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d").ExtractInto(&p) + th.AssertNoErr(t, err) + + th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, p.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") +} + +func TestCreatePort(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, CreatePortRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + _, err := fmt.Fprintf(w, CreatePortResponse) + th.AssertNoErr(t, err) + }) + + var p struct { + ports.Port + policies.QoSPolicyExt + } + portCreateOpts := ports.CreateOpts{ + NetworkID: "a87cc70a-3e15-4acf-8205-9b711a3531b7", + } + createOpts := policies.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + QoSPolicyID: "591e0597-39a6-4665-8149-2111d8de9a08", + } + err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&p) + th.AssertNoErr(t, err) + + th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, p.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, p.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") +} + +func TestUpdatePortWithPolicy(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, UpdatePortWithPolicyRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + _, err := fmt.Fprintf(w, UpdatePortWithPolicyResponse) + th.AssertNoErr(t, err) + }) + + policyID := "591e0597-39a6-4665-8149-2111d8de9a08" + + var p struct { + ports.Port + policies.QoSPolicyExt + } + portUpdateOpts := ports.UpdateOpts{} + updateOpts := policies.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + QoSPolicyID: &policyID, + } + err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&p) + th.AssertNoErr(t, err) + + th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, p.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, p.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") +} + +func TestUpdatePortWithoutPolicy(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, UpdatePortWithoutPolicyRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + _, err := fmt.Fprintf(w, UpdatePortWithoutPolicyResponse) + th.AssertNoErr(t, err) + }) + + policyID := "" + + var p struct { + ports.Port + policies.QoSPolicyExt + } + portUpdateOpts := ports.UpdateOpts{} + updateOpts := policies.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + QoSPolicyID: &policyID, + } + err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&p) + th.AssertNoErr(t, err) + + th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, p.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, p.QoSPolicyID, "") +} From c2d73b246b48e239d3f03c455905e06fe26e33c3 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 25 May 2019 12:18:29 +0300 Subject: [PATCH 0744/2296] Networking V2: add QoS policy extension for Net (#1581) Allow to Create a Network with the QoS policy. Allow to add or delete a QoS policy from an existing Network. Allow to retrieve a Network with QoS policy from the API. --- .../v2/extensions/qos/policies/doc.go | 88 +++++++++++ .../v2/extensions/qos/policies/requests.go | 55 +++++++ .../qos/policies/testing/fixtures.go | 66 ++++++++ .../qos/policies/testing/requests_test.go | 142 ++++++++++++++++++ 4 files changed, 351 insertions(+) diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index f9184e1d6d..18a3c456a9 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -89,5 +89,93 @@ Example to delete a QoS policy from the existing Port } fmt.Printf("Port: %+v\n", portWithQoS) + +Example to Get a Network with a QoS policy + + var networkWithQoS struct { + networks.Network + policies.QoSPolicyExt + } + + networkID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + + err = networks.Get(client, networkID).ExtractInto(&networkWithQoS) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Network: %+v\n", networkWithQoS) + +Example to Create a Network with a QoS policy + + var networkWithQoS struct { + networks.Network + policies.QoSPolicyExt + } + + policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + networkID := "7069db8d-e817-4b39-a654-d2dd76e73d36" + + networkCreateOpts := networks.CreateOpts{ + NetworkID: networkID, + } + + createOpts := policies.NetworkCreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + QoSPolicyID: policyID, + } + + err = networks.Create(client, createOpts).ExtractInto(&networkWithQoS) + if err != nil { + panic(err) + } + + fmt.Printf("Network: %+v\n", networkWithQoS) + +Example to add a QoS policy to an existing Network + + var networkWithQoS struct { + networks.Network + policies.QoSPolicyExt + } + + networkUpdateOpts := networks.UpdateOpts{} + + policyID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + + updateOpts := policies.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + QoSPolicyID: &policyID, + } + + err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) + if err != nil { + panic(err) + } + + fmt.Printf("Network: %+v\n", networkWithQoS) + +Example to delete a QoS policy from the existing Network + + var networkWithQoS struct { + networks.Network + policies.QoSPolicyExt + } + + networkUpdateOpts := networks.UpdateOpts{} + + 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) + } + + fmt.Printf("Network: %+v\n", networkWithQoS) */ package policies diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index 56da4d8214..20984f3717 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -1,6 +1,7 @@ package policies import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) @@ -57,3 +58,57 @@ func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) return base, nil } + +// NetworkCreateOptsExt adds QoS options to the base networks.CreateOpts. +type NetworkCreateOptsExt struct { + networks.CreateOptsBuilder + + // QoSPolicyID represents an associated QoS policy. + QoSPolicyID string `json:"qos_policy_id,omitempty"` +} + +// ToNetworkCreateMap casts a CreateOpts struct to a map. +func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() + if err != nil { + return nil, err + } + + network := base["network"].(map[string]interface{}) + + if opts.QoSPolicyID != "" { + network["qos_policy_id"] = opts.QoSPolicyID + } + + return base, nil +} + +// NetworkUpdateOptsExt adds QoS options to the base networks.UpdateOpts. +type NetworkUpdateOptsExt struct { + networks.UpdateOptsBuilder + + // QoSPolicyID represents an associated QoS policy. + // Setting it to a pointer of an empty string will remove associated QoS policy from network. + QoSPolicyID *string `json:"qos_policy_id,omitempty"` +} + +// ToNetworkUpdateMap casts a UpdateOpts struct to a map. +func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { + base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() + if err != nil { + return nil, err + } + + network := base["network"].(map[string]interface{}) + + if opts.QoSPolicyID != nil { + qosPolicyID := *opts.QoSPolicyID + if qosPolicyID != "" { + network["qos_policy_id"] = qosPolicyID + } else { + network["qos_policy_id"] = nil + } + } + + return base, nil +} diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go index 12849a2261..445fce88fe 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go @@ -66,3 +66,69 @@ const UpdatePortWithoutPolicyResponse = ` } } ` + +const GetNetworkResponse = ` +{ + "network": { + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const CreateNetworkRequest = ` +{ + "network": { + "name": "private", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const CreateNetworkResponse = ` +{ + "network": { + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const UpdateNetworkWithPolicyRequest = ` +{ + "network": { + "name": "updated", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const UpdateNetworkWithPolicyResponse = ` +{ + "network": { + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "name": "updated", + "qos_policy_id": "591e0597-39a6-4665-8149-2111d8de9a08" + } +} +` + +const UpdateNetworkWithoutPolicyRequest = ` +{ + "network": { + "qos_policy_id": null + } +} +` + +const UpdateNetworkWithoutPolicyResponse = ` +{ + "network": { + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "id": "65c0ee9f-d634-4522-8954-51021b570b0d", + "qos_policy_id": "" + } +} +` diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index ef606c449f..9f65371758 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -7,6 +7,7 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -150,3 +151,144 @@ func TestUpdatePortWithoutPolicy(t *testing.T) { th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, p.QoSPolicyID, "") } + +func TestGetNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/65c0ee9f-d634-4522-8954-51021b570b0d", 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) + + _, err := fmt.Fprintf(w, GetNetworkResponse) + th.AssertNoErr(t, err) + }) + + var n struct { + networks.Network + policies.QoSPolicyExt + } + err := networks.Get(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d").ExtractInto(&n) + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, n.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") +} + +func TestCreateNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks", 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, CreateNetworkRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + _, err := fmt.Fprintf(w, CreateNetworkResponse) + th.AssertNoErr(t, err) + }) + + var n struct { + networks.Network + policies.QoSPolicyExt + } + networkCreateOpts := networks.CreateOpts{ + Name: "private", + } + createOpts := policies.NetworkCreateOptsExt{ + CreateOptsBuilder: networkCreateOpts, + QoSPolicyID: "591e0597-39a6-4665-8149-2111d8de9a08", + } + err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&n) + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") + th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, n.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") +} + +func TestUpdateNetworkWithPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/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, UpdateNetworkWithPolicyRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + _, err := fmt.Fprintf(w, UpdateNetworkWithPolicyResponse) + th.AssertNoErr(t, err) + }) + + policyID := "591e0597-39a6-4665-8149-2111d8de9a08" + name := "updated" + + var n struct { + networks.Network + policies.QoSPolicyExt + } + networkUpdateOpts := networks.UpdateOpts{ + Name: &name, + } + updateOpts := policies.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + QoSPolicyID: &policyID, + } + err := networks.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&n) + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") + th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, n.Name, "updated") + th.AssertEquals(t, n.QoSPolicyID, "591e0597-39a6-4665-8149-2111d8de9a08") +} + +func TestUpdateNetworkWithoutPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/networks/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, UpdateNetworkWithoutPolicyRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + _, err := fmt.Fprintf(w, UpdateNetworkWithoutPolicyResponse) + th.AssertNoErr(t, err) + }) + + policyID := "" + + var n struct { + networks.Network + policies.QoSPolicyExt + } + networkUpdateOpts := networks.UpdateOpts{} + updateOpts := policies.NetworkUpdateOptsExt{ + UpdateOptsBuilder: networkUpdateOpts, + QoSPolicyID: &policyID, + } + err := networks.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&n) + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") + th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, n.QoSPolicyID, "") +} From 5d29e7d2929f72457b542c9785ba6b474cce2845 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 27 May 2019 10:26:39 -0600 Subject: [PATCH 0745/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2..efa60e7722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +## Unreleased + +## 0.1.0 (May 27, 2019) + +Initial tagged release. From c1bd8da50d35879fab5a72e4fa82e3f2e122ad7c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 27 May 2019 10:27:00 -0600 Subject: [PATCH 0746/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efa60e7722..4a3752ab9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## 0.2.0 (Unreleased) ## 0.1.0 (May 27, 2019) From 3ad89c4ea008e0e0b729720040b0be25a35d4615 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 28 May 2019 11:20:55 +0300 Subject: [PATCH 0747/2296] Networking V2: add bw limit rule Create (#1584) * Networking V2: add bw limit rules List Allow to list bandwidth limit rules. * Networking V2: cleanup bw limit rules List Remove debug and use plural name for List-related methods and structures. * Networking V2: add bw limit rules Get Allow to get a specific bandwidth limit rule. * Networking V2: add bw limit rule Create Allow to create a bandwidth limit rule. --- .../networking/v2/extensions/qos/rules/doc.go | 38 ++++++ .../v2/extensions/qos/rules/requests.go | 100 ++++++++++++++++ .../v2/extensions/qos/rules/results.go | 76 ++++++++++++ .../v2/extensions/qos/rules/testing/doc.go | 2 + .../extensions/qos/rules/testing/fixtures.go | 48 ++++++++ .../qos/rules/testing/requests_test.go | 111 ++++++++++++++++++ .../v2/extensions/qos/rules/urls.go | 29 +++++ 7 files changed, 404 insertions(+) create mode 100644 openstack/networking/v2/extensions/qos/rules/doc.go create mode 100644 openstack/networking/v2/extensions/qos/rules/requests.go create mode 100644 openstack/networking/v2/extensions/qos/rules/results.go create mode 100644 openstack/networking/v2/extensions/qos/rules/testing/doc.go create mode 100644 openstack/networking/v2/extensions/qos/rules/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/qos/rules/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/qos/rules/urls.go diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go new file mode 100644 index 0000000000..500f3e5ad3 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -0,0 +1,38 @@ +/* +Package rules provides the ability to retrieve and manage QoS policy rules through the Neutron API. + +Example of Listing BandwidthLimitRules + + listOpts := rules.BandwidthLimitRulesListOpts{ + MaxKBps: 3000, + } + + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + + allPages, err := rules.BandwidthLimitRulesList(networkClient, policyID, listOpts).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) + } + +Example of Getting a single BandwidthLimitRule + + 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) + } + + fmt.Printf("Rule: %+v\n", rule) +*/ +package rules diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go new file mode 100644 index 0000000000..3a836e23ff --- /dev/null +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -0,0 +1,100 @@ +package rules + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type BandwidthLimitRulesListOptsBuilder interface { + ToBandwidthLimitRulesListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the Neutron API. Filtering is achieved by passing in struct field values +// that map to the BandwidthLimitRules attributes you want to see returned. +// SortKey allows you to sort by a particular BandwidthLimitRule attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for the pagination. +type BandwidthLimitRulesListOpts struct { + ID string `q:"id"` + TenantID string `q:"tenant_id"` + MaxKBps int `q:"max_kbps"` + MaxBurstKBps int `q:"max_burst_kbps"` + Direction string `q:"direction"` + 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"` +} + +// ToBandwidthLimitRulesListQuery formats a ListOpts into a query string. +func (opts BandwidthLimitRulesListOpts) ToBandwidthLimitRulesListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// BandwidthLimitRuleList returns a Pager which allows you to iterate over a collection of +// BandwidthLimitRules. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +func BandwidthLimitRulesList(c *gophercloud.ServiceClient, policyID string, opts BandwidthLimitRulesListOptsBuilder) pagination.Pager { + url := listBandwidthLimitRulesURL(c, policyID) + if opts != nil { + query, err := opts.ToBandwidthLimitRulesListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return BandwidthLimitRulePage{pagination.LinkedPageBase{PageResult: r}} + + }) +} + +// GetBandwidthLimitRule retrieves a specific BandwidthLimitRule based on its ID. +func GetBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetBandwidthLimitRuleResult) { + _, r.Err = c.Get(getBandwidthLimitRuleURL(c, policyID, ruleID), &r.Body, nil) + return +} + +// CreateBandwidthLimitRuleOptsBuilder allows to add additional parameters to the +// CreateBandwidthLimitRule request. +type CreateBandwidthLimitRuleOptsBuilder interface { + ToBandwidthLimitRuleCreateMap() (map[string]interface{}, error) +} + +// CreateBandwidthLimitRuleOpts specifies parameters of a new BandwidthLimitRule. +type CreateBandwidthLimitRuleOpts struct { + // MaxKBps is a maximum kilobits per second. It's a required parameter. + MaxKBps int `json:"max_kbps"` + + // MaxBurstKBps is a maximum burst size in kilobits. + MaxBurstKBps int `json:"max_burst_kbps,omitempty"` + + // Direction represents the direction of traffic. + Direction string `json:"direction,omitempty"` +} + +// ToBandwidthLimitRuleCreateMap constructs a request body from CreateBandwidthLimitRuleOpts. +func (opts CreateBandwidthLimitRuleOpts) ToBandwidthLimitRuleCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "bandwidth_limit_rule") +} + +// CreateBandwidthLimitRule requests the creation of a new BandwidthLimitRule on the server. +func CreateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID string, opts CreateBandwidthLimitRuleOptsBuilder) (r CreateBandwidthLimitRuleResult) { + b, err := opts.ToBandwidthLimitRuleCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createBandwidthLimitRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go new file mode 100644 index 0000000000..14ff7d3794 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -0,0 +1,76 @@ +package rules + +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 BandwidthLimitRule. +func (r commonResult) ExtractBandwidthLimitRule() (*BandwidthLimitRule, error) { + var s struct { + BandwidthLimitRule *BandwidthLimitRule `json:"bandwidth_limit_rule"` + } + err := r.ExtractInto(&s) + return s.BandwidthLimitRule, err +} + +// GetBandwidthLimitRuleResult represents the result of a Get operation. Call its Extract +// method to interpret it as a BandwidthLimitRule. +type GetBandwidthLimitRuleResult struct { + commonResult +} + +// CreateBandwidthLimitRuleResult represents the result of a Create operation. Call its Extract +// method to interpret it as a BandwidthLimitRule. +type CreateBandwidthLimitRuleResult struct { + commonResult +} + +// BandwidthLimitRule represents a QoS policy rule to set bandwidth limits. +type BandwidthLimitRule struct { + // ID is a unique ID of the policy. + ID string `json:"id"` + + // TenantID is the ID of the Identity project. + TenantID string `json:"tenant_id"` + + // MaxKBps is a maximum kilobits per second. + MaxKBps int `json:"max_kbps"` + + // MaxBurstKBps is a maximum burst size in kilobits. + MaxBurstKBps int `json:"max_burst_kbps"` + + // Direction represents the direction of traffic. + Direction string `json:"direction"` + + // Tags optionally set via extensions/attributestags. + Tags []string `json:"tags"` +} + +// BandwidthLimitRulePage stores a single page of BandwidthLimitRules from a List() API call. +type BandwidthLimitRulePage struct { + pagination.LinkedPageBase +} + +// IsEmpty checks whether a BandwidthLimitRulePage is empty. +func (r BandwidthLimitRulePage) IsEmpty() (bool, error) { + is, err := ExtractBandwidthLimitRules(r) + return len(is) == 0, err +} + +// ExtractBandwidthLimitRules accepts a BandwidthLimitRulePage, and extracts the elements into a slice of +// BandwidthLimitRules. +func ExtractBandwidthLimitRules(r pagination.Page) ([]BandwidthLimitRule, error) { + var s []BandwidthLimitRule + err := ExtractBandwidthLimitRulesInto(r, &s) + return s, err +} + +// ExtractBandwidthLimitRulesInto extracts the elements into a slice of RBAC Policy structs. +func ExtractBandwidthLimitRulesInto(r pagination.Page, v interface{}) error { + return r.(BandwidthLimitRulePage).Result.ExtractIntoSlicePtr(v, "bandwidth_limit_rules") +} diff --git a/openstack/networking/v2/extensions/qos/rules/testing/doc.go b/openstack/networking/v2/extensions/qos/rules/testing/doc.go new file mode 100644 index 0000000000..6bc3a4615c --- /dev/null +++ b/openstack/networking/v2/extensions/qos/rules/testing/doc.go @@ -0,0 +1,2 @@ +// QoS policy rules unit tests +package testing diff --git a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go new file mode 100644 index 0000000000..f5f78b10fd --- /dev/null +++ b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go @@ -0,0 +1,48 @@ +package testing + +// BandwidthLimitRulesListResult represents a raw result of a List call to BandwidthLimitRules. +const BandwidthLimitRulesListResult = ` +{ + "bandwidth_limit_rules": [ + { + "max_kbps": 3000, + "direction": "egress", + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "max_burst_kbps": 300 + } + ] +} +` + +// BandwidthLimitRulesGetResult represents a raw result of a Get call to a specific BandwidthLimitRule. +const BandwidthLimitRulesGetResult = ` +{ + "bandwidth_limit_rule": { + "max_kbps": 3000, + "direction": "egress", + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "max_burst_kbps": 300 + } +} +` + +// BandwidthLimitRulesCreateRequest represents a raw body of a Create BandwidthLimitRule call. +const BandwidthLimitRulesCreateRequest = ` +{ + "bandwidth_limit_rule": { + "max_kbps": 2000, + "max_burst_kbps": 200 + } +} +` + +// BandwidthLimitRulesCreateResult represents a raw result of a Create BandwidthLimitRule call. +const BandwidthLimitRulesCreateResult = ` +{ + "bandwidth_limit_rule": { + "max_kbps": 2000, + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "max_burst_kbps": 200 + } +} +` diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go new file mode 100644 index 0000000000..686c6b5e18 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -0,0 +1,111 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" + "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/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_rules", 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, BandwidthLimitRulesListResult) + }) + + count := 0 + + err := rules.BandwidthLimitRulesList( + fake.ServiceClient(), + "501005fa-3b56-4061-aaca-3f24995112e1", + rules.BandwidthLimitRulesListOpts{}, + ).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := rules.ExtractBandwidthLimitRules(page) + if err != nil { + t.Errorf("Failed to extract bandwith limit rules: %v", err) + return false, nil + } + + expected := []rules.BandwidthLimitRule{ + { + ID: "30a57f4a-336b-4382-8275-d708babd2241", + MaxKBps: 3000, + MaxBurstKBps: 300, + Direction: "egress", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, BandwidthLimitRulesGetResult) + }) + + r, err := rules.GetBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractBandwidthLimitRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") + th.AssertEquals(t, r.Direction, "egress") + th.AssertEquals(t, r.MaxBurstKBps, 300) + th.AssertEquals(t, r.MaxKBps, 3000) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_rules", 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, BandwidthLimitRulesCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, BandwidthLimitRulesCreateResult) + }) + + opts := rules.CreateBandwidthLimitRuleOpts{ + MaxKBps: 2000, + MaxBurstKBps: 200, + } + r, err := rules.CreateBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractBandwidthLimitRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, 200, r.MaxBurstKBps) + th.AssertEquals(t, 2000, r.MaxKBps) +} diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go new file mode 100644 index 0000000000..5be147e0b7 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -0,0 +1,29 @@ +package rules + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "qos/policies" + + bandwidthLimitRulesResourcePath = "bandwidth_limit_rules" +) + +func bandwidthLimitRulesRootURL(c *gophercloud.ServiceClient, policyID string) string { + return c.ServiceURL(rootPath, policyID, bandwidthLimitRulesResourcePath) +} + +func bandwidthLimitRulesResourceURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return c.ServiceURL(rootPath, policyID, bandwidthLimitRulesResourcePath, ruleID) +} + +func listBandwidthLimitRulesURL(c *gophercloud.ServiceClient, policyID string) string { + return bandwidthLimitRulesRootURL(c, policyID) +} + +func getBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return bandwidthLimitRulesResourceURL(c, policyID, ruleID) +} + +func createBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID string) string { + return bandwidthLimitRulesRootURL(c, policyID) +} From 3d2cc2ea99c279a91150728c779f98edaff49677 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 1 Jun 2019 12:47:38 +0300 Subject: [PATCH 0748/2296] Networking V2: cleanup QoS BW limit rule docs (#1589) * Networking V2: rename qos bw rules List call Use ListBandwidthLimitRules as a method name for the List call. * Networking V2: add bw limit rule Update Allow to update a bandwidth limit rule. * Networking V2: cleanup QoS BW limit rule docs Add an example of BW limit rule creation. Fix client name in limit rule update. --- .../networking/v2/extensions/qos/rules/doc.go | 39 +++++++++++++++++- .../v2/extensions/qos/rules/requests.go | 40 ++++++++++++++++++- .../v2/extensions/qos/rules/results.go | 6 +++ .../extensions/qos/rules/testing/fixtures.go | 21 ++++++++++ .../qos/rules/testing/requests_test.go | 38 ++++++++++++++++-- .../v2/extensions/qos/rules/urls.go | 4 ++ 6 files changed, 141 insertions(+), 7 deletions(-) diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index 500f3e5ad3..cf3b0d8bd9 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -9,7 +9,7 @@ Example of Listing BandwidthLimitRules policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - allPages, err := rules.BandwidthLimitRulesList(networkClient, policyID, listOpts).AllPages() + allPages, err := rules.ListBandwidthLimitRules(networkClient, policyID, listOpts).AllPages() if err != nil { panic(err) } @@ -34,5 +34,42 @@ Example of Getting a single BandwidthLimitRule } fmt.Printf("Rule: %+v\n", rule) + +Example of Creating a single BandwidthLimitRule + + opts := rules.CreateBandwidthLimitRuleOpts{ + MaxKBps: 2000, + MaxBurstKBps: 200, + } + + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + + rule, err := rules.CreateBandwidthLimitRule(networkClient, policyID, opts).ExtractBandwidthLimitRule() + if err != nil { + panic(err) + } + + fmt.Printf("Rule: %+v\n", rule) + +Example of Updating a single BandwidthLimitRule + + maxKBps := 500 + maxBurstKBps := 0 + + opts := rules.UpdateBandwidthLimitRuleOpts{ + MaxKBps: &maxKBps, + MaxBurstKBps: &maxBurstKBps, + } + + 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) + } + + fmt.Printf("Rule: %+v\n", rule) + */ package rules diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index 3a836e23ff..6bb4bef6e7 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -39,10 +39,10 @@ func (opts BandwidthLimitRulesListOpts) ToBandwidthLimitRulesListQuery() (string return q.String(), err } -// BandwidthLimitRuleList returns a Pager which allows you to iterate over a collection of +// ListBandwidthLimitRules returns a Pager which allows you to iterate over a collection of // BandwidthLimitRules. It accepts a ListOpts struct, which allows you to filter and sort // the returned collection for greater efficiency. -func BandwidthLimitRulesList(c *gophercloud.ServiceClient, policyID string, opts BandwidthLimitRulesListOptsBuilder) pagination.Pager { +func ListBandwidthLimitRules(c *gophercloud.ServiceClient, policyID string, opts BandwidthLimitRulesListOptsBuilder) pagination.Pager { url := listBandwidthLimitRulesURL(c, policyID) if opts != nil { query, err := opts.ToBandwidthLimitRulesListQuery() @@ -98,3 +98,39 @@ func CreateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID string }) return } + +// UpdateBandwidthLimitRuleOptsBuilder allows to add additional parameters to the +// UpdateBandwidthLimitRule request. +type UpdateBandwidthLimitRuleOptsBuilder interface { + ToBandwidthLimitRuleUpdateMap() (map[string]interface{}, error) +} + +// UpdateBandwidthLimitRuleOpts specifies parameters for the Update call. +type UpdateBandwidthLimitRuleOpts struct { + // MaxKBps is a maximum kilobits per second. + MaxKBps *int `json:"max_kbps,omitempty"` + + // MaxBurstKBps is a maximum burst size in kilobits. + MaxBurstKBps *int `json:"max_burst_kbps,omitempty"` + + // Direction represents the direction of traffic. + Direction string `json:"direction,omitempty"` +} + +// ToBandwidthLimitRuleUpdateMap constructs a request body from UpdateBandwidthLimitRuleOpts. +func (opts UpdateBandwidthLimitRuleOpts) ToBandwidthLimitRuleUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "bandwidth_limit_rule") +} + +// UpdateBandwidthLimitRule requests the creation of a new BandwidthLimitRule on the server. +func UpdateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateBandwidthLimitRuleOptsBuilder) (r UpdateBandwidthLimitRuleResult) { + b, err := opts.ToBandwidthLimitRuleUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateBandwidthLimitRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index 14ff7d3794..566a2108d5 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -30,6 +30,12 @@ type CreateBandwidthLimitRuleResult struct { commonResult } +// UpdateBandwidthLimitRuleResult represents the result of a Update operation. Call its Extract +// method to interpret it as a BandwidthLimitRule. +type UpdateBandwidthLimitRuleResult struct { + commonResult +} + // BandwidthLimitRule represents a QoS policy rule to set bandwidth limits. type BandwidthLimitRule struct { // ID is a unique ID of the policy. diff --git a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go index f5f78b10fd..37093cc1b1 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go @@ -46,3 +46,24 @@ const BandwidthLimitRulesCreateResult = ` } } ` + +// BandwidthLimitRulesUpdateRequest represents a raw body of a Update BandwidthLimitRule call. +const BandwidthLimitRulesUpdateRequest = ` +{ + "bandwidth_limit_rule": { + "max_kbps": 500, + "max_burst_kbps": 0 + } +} +` + +// BandwidthLimitRulesUpdateResult represents a raw result of a Update BandwidthLimitRule call. +const BandwidthLimitRulesUpdateResult = ` +{ + "bandwidth_limit_rule": { + "max_kbps": 500, + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "max_burst_kbps": 0 + } +} +` diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index 686c6b5e18..bb4861fd30 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -11,7 +11,7 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestList(t *testing.T) { +func TestListBandwidthLimitRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -27,7 +27,7 @@ func TestList(t *testing.T) { count := 0 - err := rules.BandwidthLimitRulesList( + err := rules.ListBandwidthLimitRules( fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", rules.BandwidthLimitRulesListOpts{}, @@ -59,7 +59,7 @@ func TestList(t *testing.T) { } } -func TestGet(t *testing.T) { +func TestGetBandwidthLimitRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -82,7 +82,7 @@ func TestGet(t *testing.T) { th.AssertEquals(t, r.MaxKBps, 3000) } -func TestCreate(t *testing.T) { +func TestCreateBandwidthLimitRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -109,3 +109,33 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, 200, r.MaxBurstKBps) th.AssertEquals(t, 2000, r.MaxKBps) } + +func TestUpdateBandwidthLimitRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, BandwidthLimitRulesUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, BandwidthLimitRulesUpdateResult) + }) + + maxKBps := 500 + maxBurstKBps := 0 + opts := rules.UpdateBandwidthLimitRuleOpts{ + MaxKBps: &maxKBps, + MaxBurstKBps: &maxBurstKBps, + } + r, err := rules.UpdateBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractBandwidthLimitRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, 0, r.MaxBurstKBps) + th.AssertEquals(t, 500, r.MaxKBps) +} diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go index 5be147e0b7..45c80d1179 100644 --- a/openstack/networking/v2/extensions/qos/rules/urls.go +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -27,3 +27,7 @@ func getBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID str func createBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID string) string { return bandwidthLimitRulesRootURL(c, policyID) } + +func updateBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return bandwidthLimitRulesResourceURL(c, policyID, ruleID) +} From 8a1e87cf3973915d6c9ffbb238bda9543b536af1 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 1 Jun 2019 15:08:48 +0300 Subject: [PATCH 0749/2296] Networking V2: add bw limit rule Delete (#1590) Allow to delete a bandwidth limit rule. --- .../networking/v2/extensions/qos/rules/doc.go | 9 +++++++++ .../networking/v2/extensions/qos/rules/requests.go | 6 ++++++ .../networking/v2/extensions/qos/rules/results.go | 6 ++++++ .../extensions/qos/rules/testing/requests_test.go | 14 ++++++++++++++ .../networking/v2/extensions/qos/rules/urls.go | 4 ++++ 5 files changed, 39 insertions(+) diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index cf3b0d8bd9..657b0ba1a4 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -71,5 +71,14 @@ Example of Updating a single BandwidthLimitRule fmt.Printf("Rule: %+v\n", rule) +Example of Deleting a single BandwidthLimitRule + + 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) + } */ package rules diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index 6bb4bef6e7..bd81dd77ad 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -134,3 +134,9 @@ func UpdateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID, ruleI }) return } + +// Delete accepts policy and rule ID and deletes the BandwidthLimitRule associated with them. +func DeleteBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteBandwidthLimitRuleResult) { + _, r.Err = c.Delete(deleteBandwidthLimitRuleURL(c, policyID, ruleID), nil) + return +} diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index 566a2108d5..1811866ffd 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -36,6 +36,12 @@ type UpdateBandwidthLimitRuleResult struct { commonResult } +// DeleteBandwidthLimitRuleResult represents the result of a Delete operation. Call its Extract +// method to interpret it as a BandwidthLimitRule. +type DeleteBandwidthLimitRuleResult struct { + gophercloud.ErrResult +} + // BandwidthLimitRule represents a QoS policy rule to set bandwidth limits. type BandwidthLimitRule struct { // ID is a unique ID of the policy. diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index bb4861fd30..1b1764cf53 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -139,3 +139,17 @@ func TestUpdateBandwidthLimitRule(t *testing.T) { th.AssertEquals(t, 0, r.MaxBurstKBps) th.AssertEquals(t, 500, r.MaxKBps) } + +func TestDeleteBandwidthLimitRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/bandwidth_limit_rules/30a57f4a-336b-4382-8275-d708babd2241", 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) + }) + + res := rules.DeleteBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go index 45c80d1179..b9982760d0 100644 --- a/openstack/networking/v2/extensions/qos/rules/urls.go +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -31,3 +31,7 @@ func createBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID string) func updateBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return bandwidthLimitRulesResourceURL(c, policyID, ruleID) } + +func deleteBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return bandwidthLimitRulesResourceURL(c, policyID, ruleID) +} From 9aa8e5b3de9ccccd72fe94d6c14e6999bc4031f4 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 2 Jun 2019 09:50:49 +0300 Subject: [PATCH 0750/2296] Networking V2: add QoS policies List (#1591) Add method to List QoS policies. --- .../v2/extensions/qos/policies/doc.go | 22 +++++ .../v2/extensions/qos/policies/requests.go | 56 +++++++++++++ .../v2/extensions/qos/policies/results.go | 83 +++++++++++++++++++ .../qos/policies/testing/fixtures.go | 83 +++++++++++++++++++ .../qos/policies/testing/requests_test.go | 41 +++++++++ .../v2/extensions/qos/policies/urls.go | 13 +++ 6 files changed, 298 insertions(+) create mode 100644 openstack/networking/v2/extensions/qos/policies/urls.go diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index 18a3c456a9..c0b73d947e 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -177,5 +177,27 @@ Example to delete a QoS policy from the existing Network } fmt.Printf("Network: %+v\n", networkWithQoS) + +Example to list QoS policies + + shared := true + listOpts := policies.ListOpts{ + Name: "shared-policy", + Shared: &shared, + } + + allPages, err := policies.List(networkClient, listOpts).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) + } */ package policies diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index 20984f3717..afc8c4770d 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -1,8 +1,10 @@ package policies import ( + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/pagination" ) // PortCreateOptsExt adds QoS options to the base ports.CreateOpts. @@ -112,3 +114,57 @@ func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, e return base, nil } + +// PolicyListOptsBuilder allows extensions to add additional parameters to the List request. +type PolicyListOptsBuilder interface { + ToPolicyListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the Neutron API. Filtering is achieved by passing in struct field values +// that map to the Policy attributes you want to see returned. +// SortKey allows you to sort by a particular BandwidthLimitRule attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for the pagination. +type ListOpts struct { + ID string `q:"id"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + Name string `q:"name"` + Description string `q:"description"` + RevisionNumber *int `q:"revision_number"` + IsDefault *bool `q:"is_default"` + Shared *bool `q:"shared"` + 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"` +} + +// ToPolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// Policy. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts PolicyListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToPolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return PolicyPage{pagination.LinkedPageBase{PageResult: r}} + + }) +} diff --git a/openstack/networking/v2/extensions/qos/policies/results.go b/openstack/networking/v2/extensions/qos/policies/results.go index d44f8a3ca6..a9b8590a2c 100644 --- a/openstack/networking/v2/extensions/qos/policies/results.go +++ b/openstack/networking/v2/extensions/qos/policies/results.go @@ -1,7 +1,90 @@ package policies +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + // QoSPolicyExt represents additional resource attributes available with the QoS extension. type QoSPolicyExt struct { // QoSPolicyID represents an associated QoS policy. QoSPolicyID string `json:"qos_policy_id"` } + +// Policy represents a QoS policy. +type Policy struct { + // ID is the id of the policy. + ID string `json:"id"` + + // Name is the human-readable name of the policy. + Name string `json:"name"` + + // TenantID is the id of the Identity project. + TenantID string `json:"tenant_id"` + + // ProjectID is the id of the Identity project. + ProjectID string `json:"project_id"` + + // CreatedAt is the time at which the policy has been created. + CreatedAt time.Time `json:"created_at"` + + // UpdatedAt is the time at which the policy has been created. + UpdatedAt time.Time `json:"updated_at"` + + // IsDefault indicates if the policy is default policy or not. + IsDefault bool `json:"is_default"` + + // Description is thehuman-readable description for the resource. + Description string `json:"description"` + + // Shared indicates whether this policy is shared across all projects. + Shared bool `json:"shared"` + + // RevisionNumber represents revision number of the policy. + RevisionNumber int `json:"revision_number"` + + // Rules represents QoS rules of the policy. + Rules []map[string]interface{} `json:"rules"` + + // Tags optionally set via extensions/attributestags + Tags []string `json:"tags"` +} + +// PolicyPage stores a single page of Policies from a List() API call. +type PolicyPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of policies 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 PolicyPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"policies_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a PolicyPage is empty. +func (r PolicyPage) IsEmpty() (bool, error) { + is, err := ExtractPolicies(r) + return len(is) == 0, err +} + +// ExtractPolicies accepts a PolicyPage, and extracts the elements into a slice of Policies. +func ExtractPolicies(r pagination.Page) ([]Policy, error) { + var s []Policy + err := ExtractPolicysInto(r, &s) + return s, err +} + +// ExtractPoliciesInto extracts the elements into a slice of RBAC Policy structs. +func ExtractPolicysInto(r pagination.Page, v interface{}) error { + return r.(PolicyPage).Result.ExtractIntoSlicePtr(v, "policies") +} diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go index 445fce88fe..ed2edee126 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go @@ -1,5 +1,11 @@ package testing +import ( + "time" + + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" +) + const GetPortResponse = ` { "port": { @@ -132,3 +138,80 @@ const UpdateNetworkWithoutPolicyResponse = ` } } ` + +const ListPoliciesResponse = ` +{ + "policies": [ + { + "name": "bw-limiter", + "tags": [], + "rules": [ + { + "max_kbps": 3000, + "direction": "egress", + "qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", + "type": "bandwidth_limit", + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "max_burst_kbps": 300 + } + ], + "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", + "created_at": "2019-05-19T11:17:50Z", + "updated_at": "2019-05-19T11:17:57Z", + "is_default": false, + "revision_number": 1, + "shared": false, + "project_id": "a77cbe0998374aed9a6798ad6c61677e", + "id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", + "description": "" + }, + { + "name": "no-rules", + "tags": [], + "rules": [], + "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", + "created_at": "2019-06-01T10:38:58Z", + "updated_at": "2019-06-01T10:38:58Z", + "is_default": false, + "revision_number": 0, + "shared": false, + "project_id": "a77cbe0998374aed9a6798ad6c61677e", + "id": "d6e7c2fe-24dc-43be-a088-24b47d4f8f88", + "description": "" + } + ] +} +` + +var Policy1 = policies.Policy{ + Name: "bw-limiter", + Rules: []map[string]interface{}{ + { + "type": "bandwidth_limit", + "max_kbps": float64(3000), + "direction": "egress", + "qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", + "max_burst_kbps": float64(300), + "id": "30a57f4a-336b-4382-8275-d708babd2241", + }, + }, + Tags: []string{}, + TenantID: "a77cbe0998374aed9a6798ad6c61677e", + CreatedAt: time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC), + UpdatedAt: time.Date(2019, 5, 19, 11, 17, 57, 0, time.UTC), + RevisionNumber: 1, + ProjectID: "a77cbe0998374aed9a6798ad6c61677e", + ID: "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", +} + +var Policy2 = policies.Policy{ + Name: "no-rules", + Tags: []string{}, + Rules: []map[string]interface{}{}, + TenantID: "a77cbe0998374aed9a6798ad6c61677e", + CreatedAt: time.Date(2019, 6, 1, 10, 38, 58, 0, time.UTC), + UpdatedAt: time.Date(2019, 6, 1, 10, 38, 58, 0, time.UTC), + RevisionNumber: 0, + ProjectID: "a77cbe0998374aed9a6798ad6c61677e", + ID: "d6e7c2fe-24dc-43be-a088-24b47d4f8f88", +} diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index 9f65371758..c87dbc3fbb 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -292,3 +293,43 @@ func TestUpdateNetworkWithoutPolicy(t *testing.T) { th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertEquals(t, n.QoSPolicyID, "") } + +func TestListPolicies(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies", 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, ListPoliciesResponse) + }) + + count := 0 + + err := policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := policies.ExtractPolicies(page) + if err != nil { + t.Errorf("Failed to extract policies: %v", err) + return false, nil + } + + expected := []policies.Policy{ + Policy1, + Policy2, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/networking/v2/extensions/qos/policies/urls.go b/openstack/networking/v2/extensions/qos/policies/urls.go new file mode 100644 index 0000000000..31dfe45576 --- /dev/null +++ b/openstack/networking/v2/extensions/qos/policies/urls.go @@ -0,0 +1,13 @@ +package policies + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "qos/policies" + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From 8c3739dd6f1dce949c2f5205a3ff0d86db0b5737 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 2 Jun 2019 12:31:35 +0300 Subject: [PATCH 0751/2296] Networking: add QoS policies Get, cleanup QoS doc (#1593) Add Get function for QoS policies package. Cleanup QoS extension documentation. Add acceptance test for QoS policies CRUD that will be temporary disabled. --- .../v2/extensions/qos/policies/policies.go | 1 + .../extensions/qos/policies/policies_test.go | 51 +++++++++++++++ .../v2/extensions/qos/policies/doc.go | 63 +++++++++++-------- .../v2/extensions/qos/policies/requests.go | 6 ++ .../v2/extensions/qos/policies/results.go | 19 ++++++ .../qos/policies/testing/fixtures.go | 28 +++++++++ .../qos/policies/testing/requests_test.go | 38 +++++++++++ .../v2/extensions/qos/policies/urls.go | 8 +++ 8 files changed, 188 insertions(+), 26 deletions(-) create mode 100644 acceptance/openstack/networking/v2/extensions/qos/policies/policies.go create mode 100644 acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go diff --git a/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go b/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go new file mode 100644 index 0000000000..a01ffa69c5 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go @@ -0,0 +1 @@ +package policies diff --git a/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go b/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go new file mode 100644 index 0000000000..25ffd7f1b6 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -0,0 +1,51 @@ +// +build acceptance networking qos policies + +package policies + +import ( + "testing" +) + +func TestPoliciesCRUD(t *testing.T) { + //client, err := clients.NewNetworkV2Client() + //th.AssertNoErr(t, err) + + // Create a QoS policy + //policy, err := CreatePolicy(t, client) + //th.AssertNoErr(t, err) + //defer DeletePolicy(t, client, policy.ID) + // + //tools.PrintResource(t, policy) + // + //newName := tools.RandomString("TESTACC-", 8) + //newDescription := "" + //updateOpts := &policies.UpdateOpts{ + // Name: newName, + // Description: &newDescription, + //} + // + //_, err = policies.Update(client, policy.ID, updateOpts).Extract() + //th.AssertNoErr(t, err) + + //newPolicy, err := policies.Get(client, policy.ID).Extract() + //th.AssertNoErr(t, err) + // + //tools.PrintResource(t, newPolicy) + //th.AssertEquals(t, newPolicy.Name, newName) + //th.AssertEquals(t, newPolicy.Description, newDescription) + // + //allPages, err := policies.List(client, nil).AllPages() + //th.AssertNoErr(t, err) + // + //allPolicies, err := policies.ExtractPolicies(allPages) + //th.AssertNoErr(t, err) + // + //var found bool + //for _, policy := range allPolicies { + // if policy.ID == newPolicy.ID { + // found = true + // } + //} + // + //th.AssertEquals(t, found, true) +} diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index c0b73d947e..98b07d436c 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -4,7 +4,7 @@ for the OpenStack Networking service. Example to Get a Port with a QoS policy - var portWithQoS struct { + var portWithQoS struct { ports.Port policies.QoSPolicyExt } @@ -20,31 +20,31 @@ Example to Get a Port with a QoS policy 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 +Example to Add a QoS policy to an existing Port var portWithQoS struct { ports.Port @@ -57,7 +57,7 @@ Example to add a QoS policy to an existing Port updateOpts := policies.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - QoSPolicyID: &policyID, + QoSPolicyID: &policyID, } err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) @@ -67,7 +67,7 @@ Example to add a QoS policy to an existing Port fmt.Printf("Port: %+v\n", portWithQoS) -Example to delete a QoS policy from the existing Port +Example to Delete a QoS policy from the existing Port var portWithQoS struct { ports.Port @@ -80,7 +80,7 @@ Example to delete a QoS policy from the existing Port updateOpts := policies.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, - QoSPolicyID: &policyID, + QoSPolicyID: &policyID, } err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) @@ -145,7 +145,7 @@ Example to add a QoS policy to an existing Network updateOpts := policies.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, - QoSPolicyID: &policyID, + QoSPolicyID: &policyID, } err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) @@ -168,7 +168,7 @@ Example to delete a QoS policy from the existing Network updateOpts := policies.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, - QoSPolicyID: &policyID, + QoSPolicyID: &policyID, } err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) @@ -178,7 +178,7 @@ Example to delete a QoS policy from the existing Network fmt.Printf("Network: %+v\n", networkWithQoS) -Example to list QoS policies +Example to List QoS policies shared := true listOpts := policies.ListOpts{ @@ -199,5 +199,16 @@ Example to list QoS policies for _, policy := range allPolicies { fmt.Printf("%+v\n", policy) } + +Example to Get a specific QoS policy + + policyID := "30a57f4a-336b-4382-8275-d708babd2241" + + policy, err := policies.Get(networkClient, policyID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", policy) */ package policies diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index afc8c4770d..75fc3b6fe3 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -168,3 +168,9 @@ func List(c *gophercloud.ServiceClient, opts PolicyListOptsBuilder) pagination.P }) } + +// Get retrieves a specific QoS policy based on its ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/qos/policies/results.go b/openstack/networking/v2/extensions/qos/policies/results.go index a9b8590a2c..21342d96b7 100644 --- a/openstack/networking/v2/extensions/qos/policies/results.go +++ b/openstack/networking/v2/extensions/qos/policies/results.go @@ -13,6 +13,25 @@ type QoSPolicyExt struct { QoSPolicyID string `json:"qos_policy_id"` } +type commonResult struct { + gophercloud.Result +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a QoS policy. +type GetResult struct { + commonResult +} + +// Extract is a function that accepts a result and extracts a QoS policy resource. +func (r commonResult) Extract() (*Policy, error) { + var s struct { + Policy *Policy `json:"policy"` + } + err := r.ExtractInto(&s) + return s.Policy, err +} + // Policy represents a QoS policy. type Policy struct { // ID is the id of the policy. diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go index ed2edee126..cfd1e7fa9e 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go @@ -215,3 +215,31 @@ var Policy2 = policies.Policy{ ProjectID: "a77cbe0998374aed9a6798ad6c61677e", ID: "d6e7c2fe-24dc-43be-a088-24b47d4f8f88", } + +const GetPolicyResponse = ` +{ + "policy": { + "name": "bw-limiter", + "tags": [], + "rules": [ + { + "max_kbps": 3000, + "direction": "egress", + "qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", + "type": "bandwidth_limit", + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "max_burst_kbps": 300 + } + ], + "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", + "created_at": "2019-05-19T11:17:50Z", + "updated_at": "2019-05-19T11:17:57Z", + "is_default": false, + "revision_number": 1, + "shared": false, + "project_id": "a77cbe0998374aed9a6798ad6c61677e", + "id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", + "description": "" + } +} +` diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index c87dbc3fbb..6d79666d9f 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" @@ -333,3 +334,40 @@ func TestListPolicies(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestGetPolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/30a57f4a-336b-4382-8275-d708babd2241", 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, GetPolicyResponse) + }) + + p, err := policies.Get(fake.ServiceClient(), "30a57f4a-336b-4382-8275-d708babd2241").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "bw-limiter", p.Name) + th.AssertDeepEquals(t, []string{}, p.Tags) + th.AssertDeepEquals(t, []map[string]interface{}{ + { + "max_kbps": float64(3000), + "direction": "egress", + "qos_policy_id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", + "type": "bandwidth_limit", + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "max_burst_kbps": float64(300), + }, + }, p.Rules) + th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.TenantID) + th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.ProjectID) + th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC), p.CreatedAt) + th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 57, 0, time.UTC), p.UpdatedAt) + th.AssertEquals(t, 1, p.RevisionNumber) + th.AssertEquals(t, "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", p.ID) +} diff --git a/openstack/networking/v2/extensions/qos/policies/urls.go b/openstack/networking/v2/extensions/qos/policies/urls.go index 31dfe45576..2f043e0b27 100644 --- a/openstack/networking/v2/extensions/qos/policies/urls.go +++ b/openstack/networking/v2/extensions/qos/policies/urls.go @@ -8,6 +8,14 @@ func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} + func listURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From 2d58570b41872577fb5dfd229f8747251db5be62 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 2 Jun 2019 14:00:57 +0300 Subject: [PATCH 0752/2296] Networking V2: add QoS DSCP marking rules List (#1594) Add QoS DSCP marking rules List function. --- .../networking/v2/extensions/qos/rules/doc.go | 20 ++++++++ .../v2/extensions/qos/rules/requests.go | 50 +++++++++++++++++++ .../v2/extensions/qos/rules/results.go | 39 +++++++++++++++ .../extensions/qos/rules/testing/fixtures.go | 12 +++++ .../qos/rules/testing/requests_test.go | 46 +++++++++++++++++ .../v2/extensions/qos/rules/urls.go | 9 ++++ 6 files changed, 176 insertions(+) diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index 657b0ba1a4..568b80a7c4 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -80,5 +80,25 @@ Example of Deleting a single BandwidthLimitRule if err != nil { panic(err) } + +Example of Listing DSCP marking rules + + listOpts := rules.DSCPMarkingRulesListOpts{} + + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + + allPages, err := rules.ListDSCPMarkingRules(networkClient, policyID, listOpts).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) + } */ package rules diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index bd81dd77ad..8741fd7499 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -140,3 +140,53 @@ func DeleteBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID str _, r.Err = c.Delete(deleteBandwidthLimitRuleURL(c, policyID, ruleID), nil) return } + +// DSCPMarkingRulesListOptsBuilder allows extensions to add additional parameters to the +// List request. +type DSCPMarkingRulesListOptsBuilder interface { + ToDSCPMarkingRulesListQuery() (string, error) +} + +// DSCPMarkingRulesListOpts allows the filtering and sorting of paginated collections through +// the Neutron API. Filtering is achieved by passing in struct field values +// that map to the DSCPMarking attributes you want to see returned. +// SortKey allows you to sort by a particular DSCPMarkingRule attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for the pagination. +type DSCPMarkingRulesListOpts struct { + ID string `q:"id"` + TenantID string `q:"tenant_id"` + DSCPMark int `q:"dscp_mark"` + 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"` +} + +// ToDSCPMarkingRulesListQuery formats a ListOpts into a query string. +func (opts DSCPMarkingRulesListOpts) ToDSCPMarkingRulesListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDSCPMarkingRules returns a Pager which allows you to iterate over a collection of +// DSCPMarkingRules. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +func ListDSCPMarkingRules(c *gophercloud.ServiceClient, policyID string, opts DSCPMarkingRulesListOptsBuilder) pagination.Pager { + url := listDSCPMarkingRulesURL(c, policyID) + if opts != nil { + query, err := opts.ToDSCPMarkingRulesListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return DSCPMarkingRulePage{pagination.LinkedPageBase{PageResult: r}} + + }) +} diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index 1811866ffd..b326c62973 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -86,3 +86,42 @@ func ExtractBandwidthLimitRules(r pagination.Page) ([]BandwidthLimitRule, error) func ExtractBandwidthLimitRulesInto(r pagination.Page, v interface{}) error { return r.(BandwidthLimitRulePage).Result.ExtractIntoSlicePtr(v, "bandwidth_limit_rules") } + +// DSCPMarkingRule represents a QoS policy rule to set DSCP marking. +type DSCPMarkingRule struct { + // ID is a unique ID of the policy. + ID string `json:"id"` + + // TenantID is the ID of the Identity project. + TenantID string `json:"tenant_id"` + + // DSCPMark contains DSCP mark value. + DSCPMark int `json:"dscp_mark"` + + // Tags optionally set via extensions/attributestags. + Tags []string `json:"tags"` +} + +// DSCPMarkingRulePage stores a single page of DSCPMarkingRules from a List() API call. +type DSCPMarkingRulePage struct { + pagination.LinkedPageBase +} + +// IsEmpty checks whether a DSCPMarkingRulePage is empty. +func (r DSCPMarkingRulePage) IsEmpty() (bool, error) { + is, err := ExtractDSCPMarkingRules(r) + return len(is) == 0, err +} + +// ExtractDSCPMarkingRules accepts a DSCPMarkingRulePage, and extracts the elements into a slice of +// DSCPMarkingRules. +func ExtractDSCPMarkingRules(r pagination.Page) ([]DSCPMarkingRule, error) { + var s []DSCPMarkingRule + err := ExtractDSCPMarkingRulesInto(r, &s) + return s, err +} + +// ExtractDSCPMarkingRulesInto extracts the elements into a slice of RBAC Policy structs. +func ExtractDSCPMarkingRulesInto(r pagination.Page, v interface{}) error { + return r.(DSCPMarkingRulePage).Result.ExtractIntoSlicePtr(v, "dscp_marking_rules") +} diff --git a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go index 37093cc1b1..9934faac38 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go @@ -67,3 +67,15 @@ const BandwidthLimitRulesUpdateResult = ` } } ` + +// DSCPMarkingRulesListResult represents a raw result of a List call to DSCPMarkingRules. +const DSCPMarkingRulesListResult = ` +{ + "dscp_marking_rules": [ + { + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "dscp_mark": 20 + } + ] +} +` diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index 1b1764cf53..f3c71b8a59 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -153,3 +153,49 @@ func TestDeleteBandwidthLimitRule(t *testing.T) { res := rules.DeleteBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") th.AssertNoErr(t, res.Err) } + +func TestListDSCPMarkingRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_rules", 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, DSCPMarkingRulesListResult) + }) + + count := 0 + + err := rules.ListDSCPMarkingRules( + fake.ServiceClient(), + "501005fa-3b56-4061-aaca-3f24995112e1", + rules.DSCPMarkingRulesListOpts{}, + ).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := rules.ExtractDSCPMarkingRules(page) + if err != nil { + t.Errorf("Failed to extract DSCP marking rules: %v", err) + return false, nil + } + + expected := []rules.DSCPMarkingRule{ + { + ID: "30a57f4a-336b-4382-8275-d708babd2241", + DSCPMark: 20, + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go index b9982760d0..00768f74cd 100644 --- a/openstack/networking/v2/extensions/qos/rules/urls.go +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -6,6 +6,7 @@ const ( rootPath = "qos/policies" bandwidthLimitRulesResourcePath = "bandwidth_limit_rules" + dscpMarkingRulesResourcePath = "dscp_marking_rules" ) func bandwidthLimitRulesRootURL(c *gophercloud.ServiceClient, policyID string) string { @@ -35,3 +36,11 @@ func updateBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID func deleteBandwidthLimitRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return bandwidthLimitRulesResourceURL(c, policyID, ruleID) } + +func dscpMarkingRulesRootURL(c *gophercloud.ServiceClient, policyID string) string { + return c.ServiceURL(rootPath, policyID, dscpMarkingRulesResourcePath) +} + +func listDSCPMarkingRulesURL(c *gophercloud.ServiceClient, policyID string) string { + return dscpMarkingRulesRootURL(c, policyID) +} From 1ead9840a84fd65df5b9840c88290a22c7179e3c Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sun, 2 Jun 2019 17:15:53 +0300 Subject: [PATCH 0753/2296] Networking V2: add QoS policy Create call (#1595) Implement QoS policy Create function. Add tests, docs and acceptance test helper. --- .../v2/extensions/qos/policies/policies.go | 35 ++++++++++++++ .../v2/extensions/qos/policies/doc.go | 15 ++++++ .../v2/extensions/qos/policies/requests.go | 47 ++++++++++++++++++- .../v2/extensions/qos/policies/results.go | 6 +++ .../qos/policies/testing/fixtures.go | 30 ++++++++++++ .../qos/policies/testing/requests_test.go | 38 +++++++++++++++ .../v2/extensions/qos/policies/urls.go | 4 ++ 7 files changed, 174 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go b/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go index a01ffa69c5..7bd276c470 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go +++ b/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go @@ -1 +1,36 @@ package policies + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// CreateQoSPolicy will create a QoS policy. An error will be returned if the +// QoS policy could not be created. +func CreateQoSPolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies.Policy, error) { + policyName := tools.RandomString("TESTACC-", 8) + policyDescription := tools.RandomString("TESTACC-DESC-", 8) + + createOpts := policies.CreateOpts{ + Name: policyName, + Description: policyDescription, + } + + t.Logf("Attempting to create a QoS policy: %s", policyName) + + policy, err := policies.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Succesfully created a QoS policy") + + th.AssertEquals(t, policyName, policy.Name) + th.AssertEquals(t, policyDescription, policy.Description) + + return policy, nil +} diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index 98b07d436c..c93bd70f2a 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -209,6 +209,21 @@ Example to Get a specific QoS policy panic(err) } + fmt.Printf("%+v\n", policy) + +Example to Create a QoS policy + + opts := policies.CreateOpts{ + Name: "shared-default-policy", + Shared: true, + IsDefault: true, + } + + policy, err := policies.Create(networkClient, opts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%+v\n", policy) */ package policies diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index 75fc3b6fe3..20a6ad36ad 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -123,7 +123,7 @@ type PolicyListOptsBuilder interface { // ListOpts allows the filtering and sorting of paginated collections through // the Neutron API. Filtering is achieved by passing in struct field values // that map to the Policy attributes you want to see returned. -// SortKey allows you to sort by a particular BandwidthLimitRule attribute. +// SortKey allows you to sort by a particular Policy attribute. // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for the pagination. type ListOpts struct { @@ -174,3 +174,48 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } + +// CreateOptsBuilder allows to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPolicyCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies parameters of a new QoS policy. +type CreateOpts struct { + // Name is the human-readable name of the QoS policy. + Name string `json:"name"` + + // TenantID is the id of the Identity project. + TenantID string `json:"tenant_id,omitempty"` + + // ProjectID is the id of the Identity project. + ProjectID string `json:"project_id,omitempty"` + + // Shared indicates whether this QoS policy is shared across all projects. + Shared bool `json:"shared,omitempty"` + + // Description is the human-readable description for the QoS policy. + Description string `json:"description,omitempty"` + + // IsDefault indicates if this QoS policy is default policy or not. + IsDefault bool `json:"is_default,omitempty"` +} + +// ToPolicyCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "policy") +} + +// Create requests the creation of a new QoS policy on the server. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToPolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/networking/v2/extensions/qos/policies/results.go b/openstack/networking/v2/extensions/qos/policies/results.go index 21342d96b7..b61bd87da6 100644 --- a/openstack/networking/v2/extensions/qos/policies/results.go +++ b/openstack/networking/v2/extensions/qos/policies/results.go @@ -23,6 +23,12 @@ type GetResult struct { commonResult } +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret it as a QoS policy. +type CreateResult struct { + commonResult +} + // Extract is a function that accepts a result and extracts a QoS policy resource. func (r commonResult) Extract() (*Policy, error) { var s struct { diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go index cfd1e7fa9e..40a3e0ea5f 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go @@ -243,3 +243,33 @@ const GetPolicyResponse = ` } } ` + +const CreatePolicyRequest = ` +{ + "policy": { + "name": "shared-default-policy", + "is_default": true, + "shared": true, + "description": "use-me" + } +} +` + +const CreatePolicyResponse = ` +{ + "policy": { + "name": "shared-default-policy", + "tags": [], + "rules": [], + "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", + "created_at": "2019-05-19T11:17:50Z", + "updated_at": "2019-05-19T11:17:57Z", + "is_default": true, + "revision_number": 0, + "shared": true, + "project_id": "a77cbe0998374aed9a6798ad6c61677e", + "id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", + "description": "use-me" + } +} +` diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index 6d79666d9f..d648ac424e 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -371,3 +371,41 @@ func TestGetPolicy(t *testing.T) { th.AssertEquals(t, 1, p.RevisionNumber) th.AssertEquals(t, "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", p.ID) } + +func TestCreatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies", 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, CreatePolicyRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, CreatePolicyResponse) + }) + + opts := policies.CreateOpts{ + Name: "shared-default-policy", + Shared: true, + IsDefault: true, + Description: "use-me", + } + p, err := policies.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "shared-default-policy", p.Name) + th.AssertEquals(t, true, p.Shared) + th.AssertEquals(t, true, p.IsDefault) + th.AssertEquals(t, "use-me", p.Description) + th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.TenantID) + th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.ProjectID) + th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC), p.CreatedAt) + th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 57, 0, time.UTC), p.UpdatedAt) + th.AssertEquals(t, 0, p.RevisionNumber) + th.AssertEquals(t, "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", p.ID) +} diff --git a/openstack/networking/v2/extensions/qos/policies/urls.go b/openstack/networking/v2/extensions/qos/policies/urls.go index 2f043e0b27..092395c765 100644 --- a/openstack/networking/v2/extensions/qos/policies/urls.go +++ b/openstack/networking/v2/extensions/qos/policies/urls.go @@ -19,3 +19,7 @@ func listURL(c *gophercloud.ServiceClient) string { func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From 9b7f77d53b8309c6d9b469490abf59c8f9b5fe37 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 2 Jun 2019 08:47:42 -0600 Subject: [PATCH 0754/2296] Update CHANGELOG.md --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a3752ab9c..6efd7f2a59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ ## 0.2.0 (Unreleased) +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) + ## 0.1.0 (May 27, 2019) Initial tagged release. From d3689243f32191808b17f02a4274cf7e11873602 Mon Sep 17 00:00:00 2001 From: nareiber Date: Mon, 3 Jun 2019 18:21:23 -0700 Subject: [PATCH 0755/2296] Diagnostics Compute Extension (#1592) * Diagnostics extension * Linting --- .../compute/v2/extensions/diagnostics/doc.go | 14 +++++++++++ .../v2/extensions/diagnostics/requests.go | 11 +++++++++ .../v2/extensions/diagnostics/results.go | 16 +++++++++++++ .../diagnostics/testing/fixtures.go | 21 +++++++++++++++++ .../diagnostics/testing/requests_test.go | 23 +++++++++++++++++++ .../compute/v2/extensions/diagnostics/urls.go | 8 +++++++ 6 files changed, 93 insertions(+) create mode 100644 openstack/compute/v2/extensions/diagnostics/doc.go create mode 100644 openstack/compute/v2/extensions/diagnostics/requests.go create mode 100644 openstack/compute/v2/extensions/diagnostics/results.go create mode 100644 openstack/compute/v2/extensions/diagnostics/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/diagnostics/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/diagnostics/urls.go diff --git a/openstack/compute/v2/extensions/diagnostics/doc.go b/openstack/compute/v2/extensions/diagnostics/doc.go new file mode 100644 index 0000000000..8141120c3e --- /dev/null +++ b/openstack/compute/v2/extensions/diagnostics/doc.go @@ -0,0 +1,14 @@ +/* +Package diagnostics returns details about a nova instance diagnostics + +Example of Show Diagnostics + + diags, err := diagnostics.Get(computeClient, serverId).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", diags) + +*/ +package diagnostics diff --git a/openstack/compute/v2/extensions/diagnostics/requests.go b/openstack/compute/v2/extensions/diagnostics/requests.go new file mode 100644 index 0000000000..b0b4412229 --- /dev/null +++ b/openstack/compute/v2/extensions/diagnostics/requests.go @@ -0,0 +1,11 @@ +package diagnostics + +import ( + "github.com/gophercloud/gophercloud" +) + +// Diagnostics +func Get(client *gophercloud.ServiceClient, serverId string) (r serverDiagnosticsResult) { + _, r.Err = client.Get(serverDiagnosticsURL(client, serverId), &r.Body, nil) + return +} diff --git a/openstack/compute/v2/extensions/diagnostics/results.go b/openstack/compute/v2/extensions/diagnostics/results.go new file mode 100644 index 0000000000..6a8dd19efd --- /dev/null +++ b/openstack/compute/v2/extensions/diagnostics/results.go @@ -0,0 +1,16 @@ +package diagnostics + +import ( + "github.com/gophercloud/gophercloud" +) + +type serverDiagnosticsResult struct { + gophercloud.Result +} + +// Extract interprets any diagnostic response as a map +func (r serverDiagnosticsResult) Extract() (*map[string]interface{}, error) { + var s map[string]interface{} + err := r.ExtractInto(&s) + return &s, err +} diff --git a/openstack/compute/v2/extensions/diagnostics/testing/fixtures.go b/openstack/compute/v2/extensions/diagnostics/testing/fixtures.go new file mode 100644 index 0000000000..e36b8a403f --- /dev/null +++ b/openstack/compute/v2/extensions/diagnostics/testing/fixtures.go @@ -0,0 +1,21 @@ +package testing + +import ( + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// HandleDiagnosticGetSuccessfully sets up the test server to respond to a diagnostic Get request. +func HandleDiagnosticGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/1234asdf/diagnostics", 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") + + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"cpu0_time":173,"memory":524288}`)) + }) +} diff --git a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go b/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go new file mode 100644 index 0000000000..4461617152 --- /dev/null +++ b/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go @@ -0,0 +1,23 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diagnostics" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGetDiagnostics(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleDiagnosticGetSuccessfully(t) + + expected := map[string]interface{}{"cpu0_time": float64(173), "memory": float64(524288)} + + res, err := diagnostics.Get(client.ServiceClient(), "1234asdf").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, res) +} diff --git a/openstack/compute/v2/extensions/diagnostics/urls.go b/openstack/compute/v2/extensions/diagnostics/urls.go new file mode 100644 index 0000000000..72ae68de45 --- /dev/null +++ b/openstack/compute/v2/extensions/diagnostics/urls.go @@ -0,0 +1,8 @@ +package diagnostics + +import "github.com/gophercloud/gophercloud" + +// serverDiagnosticsURL returns the diagnostics url for a nova instance/server +func serverDiagnosticsURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "diagnostics") +} From e15bc592646bc9139350e5f29453fa47df0084b8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 4 Jun 2019 01:25:06 +0000 Subject: [PATCH 0756/2296] Compute v2: return map instead of pointer to map for diagnostics --- .../openstack/compute/v2/diagnostics_test.go | 28 +++++++++++++++++++ .../v2/extensions/diagnostics/results.go | 4 +-- .../diagnostics/testing/requests_test.go | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 acceptance/openstack/compute/v2/diagnostics_test.go diff --git a/acceptance/openstack/compute/v2/diagnostics_test.go b/acceptance/openstack/compute/v2/diagnostics_test.go new file mode 100644 index 0000000000..21ca6768f4 --- /dev/null +++ b/acceptance/openstack/compute/v2/diagnostics_test.go @@ -0,0 +1,28 @@ +// +build acceptance compute limits + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diagnostics" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestDiagnostics(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) + + diag, err := diagnostics.Get(client, server.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, diag) +} diff --git a/openstack/compute/v2/extensions/diagnostics/results.go b/openstack/compute/v2/extensions/diagnostics/results.go index 6a8dd19efd..00ee3f316f 100644 --- a/openstack/compute/v2/extensions/diagnostics/results.go +++ b/openstack/compute/v2/extensions/diagnostics/results.go @@ -9,8 +9,8 @@ type serverDiagnosticsResult struct { } // Extract interprets any diagnostic response as a map -func (r serverDiagnosticsResult) Extract() (*map[string]interface{}, error) { +func (r serverDiagnosticsResult) Extract() (map[string]interface{}, error) { var s map[string]interface{} err := r.ExtractInto(&s) - return &s, err + return s, err } diff --git a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go b/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go index 4461617152..9d8f3a3dc7 100644 --- a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go +++ b/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go @@ -19,5 +19,5 @@ func TestGetDiagnostics(t *testing.T) { res, err := diagnostics.Get(client.ServiceClient(), "1234asdf").Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, &expected, res) + th.AssertDeepEquals(t, expected, res) } From 5b88a56c7bc00815e830c04d25365439ec7b8f70 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 4 Jun 2019 01:25:19 +0000 Subject: [PATCH 0757/2296] Acc Tests: Add test for Compute v2 diagnostics --- acceptance/openstack/compute/v2/diagnostics_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/acceptance/openstack/compute/v2/diagnostics_test.go b/acceptance/openstack/compute/v2/diagnostics_test.go index 21ca6768f4..43b0ee9a5a 100644 --- a/acceptance/openstack/compute/v2/diagnostics_test.go +++ b/acceptance/openstack/compute/v2/diagnostics_test.go @@ -25,4 +25,7 @@ func TestDiagnostics(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, diag) + + _, ok := diag["memory"] + th.AssertEquals(t, true, ok) } From 1cba0e1eddb84cc01782457da79d210aea1b2725 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 4 Jun 2019 01:28:53 +0000 Subject: [PATCH 0758/2296] Orchestration v1: Fix ordering of fields --- openstack/orchestration/v1/stackresources/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go index f86f85df3b..8b9495839a 100644 --- a/openstack/orchestration/v1/stackresources/results.go +++ b/openstack/orchestration/v1/stackresources/results.go @@ -16,13 +16,13 @@ type Resource struct { Links []gophercloud.Link `json:"links"` LogicalID string `json:"logical_resource_id"` Name string `json:"resource_name"` + ParentResource string `json:"parent_resource"` PhysicalID string `json:"physical_resource_id"` RequiredBy []interface{} `json:"required_by"` Status string `json:"resource_status"` StatusReason string `json:"resource_status_reason"` Type string `json:"resource_type"` UpdatedTime time.Time `json:"-"` - ParentResource string `json:"parent_resource"` } func (r *Resource) UnmarshalJSON(b []byte) error { From 226e95fdfe4efa31469971c8c558a022268990c7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 3 Jun 2019 21:33:45 -0600 Subject: [PATCH 0759/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6efd7f2a59..b4289db235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ IMPROVEMENTS * 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) ## 0.1.0 (May 27, 2019) From b8b6f3e2be05908d2c212e3588f16e3a2eba4530 Mon Sep 17 00:00:00 2001 From: Mikael Johansson Date: Tue, 4 Jun 2019 17:21:16 +0200 Subject: [PATCH 0760/2296] Fix typos (#1599) --- openstack/loadbalancer/v2/pools/urls.go | 4 ++-- openstack/networking/v2/extensions/lbaas_v2/pools/urls.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openstack/loadbalancer/v2/pools/urls.go b/openstack/loadbalancer/v2/pools/urls.go index bceca67707..e7443c4f19 100644 --- a/openstack/loadbalancer/v2/pools/urls.go +++ b/openstack/loadbalancer/v2/pools/urls.go @@ -20,6 +20,6 @@ func memberRootURL(c *gophercloud.ServiceClient, poolId string) string { return c.ServiceURL(rootPath, resourcePath, poolId, memberPath) } -func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memeberID string) string { - return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memeberID) +func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memberID string) string { + return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memberID) } diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go index bceca67707..e7443c4f19 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go @@ -20,6 +20,6 @@ func memberRootURL(c *gophercloud.ServiceClient, poolId string) string { return c.ServiceURL(rootPath, resourcePath, poolId, memberPath) } -func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memeberID string) string { - return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memeberID) +func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memberID string) string { + return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memberID) } From 94fc098a1159c5483887cc355040459eb6bc3375 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 10 Jun 2019 12:45:04 +0300 Subject: [PATCH 0761/2296] Networking V2: add QoS policies acceptance tests (#1603) * Networking V2: add QoS policy Update call Implement QoS policy update call. * Networking V2: add QoS policy Delete call Implement QoS policy delete method. * Networking V2: add QoS policies acceptance tests Add TestPoliciesCRUD acceptance test and CreateQoSPolicy, DeleteQoSPolicy helpers. * Networking V2: use 200 code for Update policy call Use StatusOK for Update QoS policy call. --- .../v2/extensions/qos/policies/policies.go | 13 +++ .../extensions/qos/policies/policies_test.go | 87 ++++++++++--------- .../v2/extensions/qos/policies/doc.go | 28 ++++++ .../v2/extensions/qos/policies/requests.go | 46 ++++++++++ .../v2/extensions/qos/policies/results.go | 12 +++ .../qos/policies/testing/fixtures.go | 29 +++++++ .../qos/policies/testing/requests_test.go | 53 +++++++++++ .../v2/extensions/qos/policies/urls.go | 8 ++ script/acceptancetest | 1 + 9 files changed, 236 insertions(+), 41 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go b/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go index 7bd276c470..88b3228df9 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go +++ b/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go @@ -34,3 +34,16 @@ func CreateQoSPolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies return policy, nil } + +// DeleteQoSPolicy will delete a QoS policy with a specified ID. +// A fatal error will occur if the delete was not successful. +func DeleteQoSPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { + t.Logf("Attempting to delete the QoS policy: %s", policyID) + + err := policies.Delete(client, policyID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete QoS policy %s: %v", policyID, err) + } + + t.Logf("Deleted QoS policy: %s", policyID) +} 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 25ffd7f1b6..85ad63adf1 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -4,48 +4,53 @@ package policies import ( "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestPoliciesCRUD(t *testing.T) { - //client, err := clients.NewNetworkV2Client() - //th.AssertNoErr(t, err) - - // Create a QoS policy - //policy, err := CreatePolicy(t, client) - //th.AssertNoErr(t, err) - //defer DeletePolicy(t, client, policy.ID) - // - //tools.PrintResource(t, policy) - // - //newName := tools.RandomString("TESTACC-", 8) - //newDescription := "" - //updateOpts := &policies.UpdateOpts{ - // Name: newName, - // Description: &newDescription, - //} - // - //_, err = policies.Update(client, policy.ID, updateOpts).Extract() - //th.AssertNoErr(t, err) - - //newPolicy, err := policies.Get(client, policy.ID).Extract() - //th.AssertNoErr(t, err) - // - //tools.PrintResource(t, newPolicy) - //th.AssertEquals(t, newPolicy.Name, newName) - //th.AssertEquals(t, newPolicy.Description, newDescription) - // - //allPages, err := policies.List(client, nil).AllPages() - //th.AssertNoErr(t, err) - // - //allPolicies, err := policies.ExtractPolicies(allPages) - //th.AssertNoErr(t, err) - // - //var found bool - //for _, policy := range allPolicies { - // if policy.ID == newPolicy.ID { - // found = true - // } - //} - // - //th.AssertEquals(t, found, true) + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a QoS policy. + policy, err := CreateQoSPolicy(t, client) + th.AssertNoErr(t, err) + defer DeleteQoSPolicy(t, client, policy.ID) + + tools.PrintResource(t, policy) + + newName := tools.RandomString("TESTACC-", 8) + newDescription := "" + updateOpts := &policies.UpdateOpts{ + Name: newName, + Description: &newDescription, + } + + _, err = policies.Update(client, policy.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + newPolicy, err := policies.Get(client, policy.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPolicy) + th.AssertEquals(t, newPolicy.Name, newName) + th.AssertEquals(t, newPolicy.Description, newDescription) + + allPages, err := policies.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err := policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, policy := range allPolicies { + if policy.ID == newPolicy.ID { + found = true + } + } + + th.AssertEquals(t, found, true) } diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index c93bd70f2a..4cd9c2c307 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -225,5 +225,33 @@ Example to Create a QoS 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, + } + + policyID := "30a57f4a-336b-4382-8275-d708babd2241" + + policy, err := policies.Update(networkClient, policyID, opts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", policy) + +Example to Delete a QoS policy + + policyID := "30a57f4a-336b-4382-8275-d708babd2241" + + err := policies.Delete(networkClient, policyID).ExtractErr() + if err != nil { + panic(err) + } */ package policies diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index 20a6ad36ad..8e8b905db3 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -219,3 +219,49 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create }) return } + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToPolicyUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update a QoS policy. +type UpdateOpts struct { + // Name is the human-readable name of the QoS policy. + Name string `json:"name,omitempty"` + + // Shared indicates whether this QoS policy is shared across all projects. + Shared *bool `json:"shared,omitempty"` + + // Description is the human-readable description for the QoS policy. + Description *string `json:"description,omitempty"` + + // IsDefault indicates if this QoS policy is default policy or not. + IsDefault *bool `json:"is_default,omitempty"` +} + +// ToPolicyUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "policy") +} + +// Update accepts a UpdateOpts struct and updates an existing policy using the +// values provided. +func Update(c *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToPolicyUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(updateURL(c, policyID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Delete accepts a unique ID and deletes the QoS policy associated with it. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(deleteURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/qos/policies/results.go b/openstack/networking/v2/extensions/qos/policies/results.go index b61bd87da6..4378181335 100644 --- a/openstack/networking/v2/extensions/qos/policies/results.go +++ b/openstack/networking/v2/extensions/qos/policies/results.go @@ -29,6 +29,18 @@ type CreateResult struct { commonResult } +// UpdateResult represents the result of a Create operation. Call its Extract +// method to interpret it as a QoS policy. +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 +} + // Extract is a function that accepts a result and extracts a QoS policy resource. func (r commonResult) Extract() (*Policy, error) { var s struct { diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go index 40a3e0ea5f..f77ba74136 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/fixtures.go @@ -273,3 +273,32 @@ const CreatePolicyResponse = ` } } ` + +const UpdatePolicyRequest = ` +{ + "policy": { + "name": "new-name", + "shared": true, + "description": "" + } +} +` + +const UpdatePolicyResponse = ` +{ + "policy": { + "name": "new-name", + "tags": [], + "rules": [], + "tenant_id": "a77cbe0998374aed9a6798ad6c61677e", + "created_at": "2019-05-19T11:17:50Z", + "updated_at": "2019-06-01T13:17:57Z", + "is_default": false, + "revision_number": 1, + "shared": true, + "project_id": "a77cbe0998374aed9a6798ad6c61677e", + "id": "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", + "description": "" + } +} +` diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index d648ac424e..cc7c2d9004 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -409,3 +409,56 @@ func TestCreatePolicy(t *testing.T) { th.AssertEquals(t, 0, p.RevisionNumber) th.AssertEquals(t, "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", p.ID) } + +func TestUpdatePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/d6ae28ce-fcb5-4180-aa62-d260a27e09ae", 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, UpdatePolicyRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdatePolicyResponse) + }) + + shared := true + description := "" + opts := policies.UpdateOpts{ + Name: "new-name", + Shared: &shared, + Description: &description, + } + p, err := policies.Update(fake.ServiceClient(), "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "new-name", p.Name) + th.AssertEquals(t, true, p.Shared) + th.AssertEquals(t, false, p.IsDefault) + th.AssertEquals(t, "", p.Description) + th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.TenantID) + th.AssertEquals(t, "a77cbe0998374aed9a6798ad6c61677e", p.ProjectID) + th.AssertEquals(t, time.Date(2019, 5, 19, 11, 17, 50, 0, time.UTC), p.CreatedAt) + th.AssertEquals(t, time.Date(2019, 6, 1, 13, 17, 57, 0, time.UTC), p.UpdatedAt) + th.AssertEquals(t, 1, p.RevisionNumber) + th.AssertEquals(t, "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", p.ID) +} + +func TestDeletePolicy(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/d6ae28ce-fcb5-4180-aa62-d260a27e09ae", 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) + }) + + res := policies.Delete(fake.ServiceClient(), "d6ae28ce-fcb5-4180-aa62-d260a27e09ae") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/qos/policies/urls.go b/openstack/networking/v2/extensions/qos/policies/urls.go index 092395c765..31ceff9cd5 100644 --- a/openstack/networking/v2/extensions/qos/policies/urls.go +++ b/openstack/networking/v2/extensions/qos/policies/urls.go @@ -23,3 +23,11 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func createURL(c *gophercloud.ServiceClient) string { return rootURL(c) } + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} diff --git a/script/acceptancetest b/script/acceptancetest index 68a5f91b0f..24598fc990 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -63,6 +63,7 @@ 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/ruletypes acceptance/openstack/networking/v2/extensions/rbacpolicies acceptance/openstack/networking/v2/extensions/subnetpools From 5d7c57d98d5884d9c6a09b9e62a95979b2a9a762 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 10 Jun 2019 15:01:07 +0300 Subject: [PATCH 0762/2296] Networking V2: add QoS DSCPMarking rule Update (#1605) * Networking V2: add QoS DSCPMarking rule Create Implement QoS DSCPMarking rule creating. * Networking V2: add QoS DSCPMarking rule Update Implement QoS DSCPMarking rule Update call. --- .../networking/v2/extensions/qos/rules/doc.go | 33 ++++++++++ .../v2/extensions/qos/rules/requests.go | 60 +++++++++++++++++++ .../v2/extensions/qos/rules/results.go | 21 +++++++ .../extensions/qos/rules/testing/fixtures.go | 38 ++++++++++++ .../qos/rules/testing/requests_test.go | 55 +++++++++++++++++ .../v2/extensions/qos/rules/urls.go | 12 ++++ 6 files changed, 219 insertions(+) diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index 568b80a7c4..f09ef845e8 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -100,5 +100,38 @@ Example of Listing DSCP marking rules for _, dscpMarkingRule := range allDSCPMarkingRules { fmt.Printf("%+v\n", dscpMarkingRule) } + +Example of Creating a single DSCPMarkingRule + + opts := rules.CreateDSCPMarkingRuleOpts{ + DSCPMark: 20, + } + + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + + rule, err := rules.CreateDSCPMarkingRule(networkClient, policyID, opts).ExtractDSCPMarkingRule() + if err != nil { + panic(err) + } + + fmt.Printf("Rule: %+v\n", rule) + +Example of Updating a single DSCPMarkingRule + + dscpMark := 26 + + opts := rules.UpdateDSCPMarkingRuleOpts{ + DSCPMark: &dscpMark, + } + + 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) + } + + fmt.Printf("Rule: %+v\n", rule) */ package rules diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index 8741fd7499..c6f3b6c1cb 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -190,3 +190,63 @@ func ListDSCPMarkingRules(c *gophercloud.ServiceClient, policyID string, opts DS }) } + +// CreateDSCPMarkingRuleOptsBuilder allows to add additional parameters to the +// CreateDSCPMarkingRule request. +type CreateDSCPMarkingRuleOptsBuilder interface { + ToDSCPMarkingRuleCreateMap() (map[string]interface{}, error) +} + +// CreateDSCPMarkingRuleOpts specifies parameters of a new DSCPMarkingRule. +type CreateDSCPMarkingRuleOpts struct { + // DSCPMark contains DSCP mark value. + DSCPMark int `json:"dscp_mark"` +} + +// ToDSCPMarkingRuleCreateMap constructs a request body from CreateDSCPMarkingRuleOpts. +func (opts CreateDSCPMarkingRuleOpts) ToDSCPMarkingRuleCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "dscp_marking_rule") +} + +// CreateDSCPMarkingRule requests the creation of a new DSCPMarkingRule on the server. +func CreateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID string, opts CreateDSCPMarkingRuleOptsBuilder) (r CreateDSCPMarkingRuleResult) { + b, err := opts.ToDSCPMarkingRuleCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createDSCPMarkingRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// UpdateDSCPMarkingRuleOptsBuilder allows to add additional parameters to the +// UpdateDSCPMarkingRule request. +type UpdateDSCPMarkingRuleOptsBuilder interface { + ToDSCPMarkingRuleUpdateMap() (map[string]interface{}, error) +} + +// UpdateDSCPMarkingRuleOpts specifies parameters for the Update call. +type UpdateDSCPMarkingRuleOpts struct { + // DSCPMark contains DSCP mark value. + DSCPMark *int `json:"dscp_mark,omitempty"` +} + +// ToDSCPMarkingRuleUpdateMap constructs a request body from UpdateDSCPMarkingRuleOpts. +func (opts UpdateDSCPMarkingRuleOpts) ToDSCPMarkingRuleUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "dscp_marking_rule") +} + +// UpdateDSCPMarkingRule requests the creation of a new DSCPMarkingRule on the server. +func UpdateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateDSCPMarkingRuleOptsBuilder) (r UpdateDSCPMarkingRuleResult) { + b, err := opts.ToDSCPMarkingRuleUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateDSCPMarkingRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index b326c62973..ba5e8270f4 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -87,6 +87,27 @@ func ExtractBandwidthLimitRulesInto(r pagination.Page, v interface{}) error { return r.(BandwidthLimitRulePage).Result.ExtractIntoSlicePtr(v, "bandwidth_limit_rules") } +// Extract is a function that accepts a result and extracts a DSCPMarkingRule. +func (r commonResult) ExtractDSCPMarkingRule() (*DSCPMarkingRule, error) { + var s struct { + DSCPMarkingRule *DSCPMarkingRule `json:"dscp_marking_rule"` + } + err := r.ExtractInto(&s) + return s.DSCPMarkingRule, err +} + +// CreateDSCPMarkingRuleResult represents the result of a Create operation. Call its Extract +// method to interpret it as a DSCPMarkingRule. +type CreateDSCPMarkingRuleResult struct { + commonResult +} + +// UpdateDSCPMarkingRuleResult represents the result of a Update operation. Call its Extract +// method to interpret it as a DSCPMarkingRule. +type UpdateDSCPMarkingRuleResult struct { + commonResult +} + // DSCPMarkingRule represents a QoS policy rule to set DSCP marking. type DSCPMarkingRule struct { // ID is a unique ID of the policy. diff --git a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go index 9934faac38..de15090a22 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go @@ -79,3 +79,41 @@ const DSCPMarkingRulesListResult = ` ] } ` + +// DSCPMarkingRuleCreateRequest represents a raw body of a Create DSCPMarkingRule call. +const DSCPMarkingRuleCreateRequest = ` +{ + "dscp_marking_rule": { + "dscp_mark": 20 + } +} +` + +// DSCPMarkingRuleCreateResult represents a raw result of a Update DSCPMarkingRule call. +const DSCPMarkingRuleCreateResult = ` +{ + "dscp_marking_rule": { + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "dscp_mark": 20 + } +} +` + +// DSCPMarkingRuleUpdateRequest represents a raw body of a Update DSCPMarkingRule call. +const DSCPMarkingRuleUpdateRequest = ` +{ + "dscp_marking_rule": { + "dscp_mark": 26 + } +} +` + +// DSCPMarkingRuleUpdateResult represents a raw result of a Update DSCPMarkingRule call. +const DSCPMarkingRuleUpdateResult = ` +{ + "dscp_marking_rule": { + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "dscp_mark": 26 + } +} +` diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index f3c71b8a59..72f58c3d5f 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -199,3 +199,58 @@ func TestListDSCPMarkingRule(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestCreateDSCPMarkingRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_rules", 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, DSCPMarkingRuleCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, DSCPMarkingRuleCreateResult) + }) + + opts := rules.CreateDSCPMarkingRuleOpts{ + DSCPMark: 20, + } + r, err := rules.CreateDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractDSCPMarkingRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "30a57f4a-336b-4382-8275-d708babd2241", r.ID) + th.AssertEquals(t, 20, r.DSCPMark) +} + +func TestUpdateDSCPMarkingRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, DSCPMarkingRuleUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, DSCPMarkingRuleUpdateResult) + }) + + dscpMark := 26 + opts := rules.UpdateDSCPMarkingRuleOpts{ + DSCPMark: &dscpMark, + } + r, err := rules.UpdateDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractDSCPMarkingRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "30a57f4a-336b-4382-8275-d708babd2241", r.ID) + th.AssertEquals(t, 26, r.DSCPMark) +} diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go index 00768f74cd..63ef0a763c 100644 --- a/openstack/networking/v2/extensions/qos/rules/urls.go +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -41,6 +41,18 @@ func dscpMarkingRulesRootURL(c *gophercloud.ServiceClient, policyID string) stri return c.ServiceURL(rootPath, policyID, dscpMarkingRulesResourcePath) } +func dscpMarkingRulesResourceURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return c.ServiceURL(rootPath, policyID, dscpMarkingRulesResourcePath, ruleID) +} + func listDSCPMarkingRulesURL(c *gophercloud.ServiceClient, policyID string) string { return dscpMarkingRulesRootURL(c, policyID) } + +func createDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID string) string { + return dscpMarkingRulesRootURL(c, policyID) +} + +func updateDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return dscpMarkingRulesResourceURL(c, policyID, ruleID) +} From 41909449906eb1ff5c657524d3c51858265b4672 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 10 Jun 2019 21:06:53 +0300 Subject: [PATCH 0763/2296] Networking QoS DSCP Marking: fix Update call code (#1609) * Networking V2: add QoS DSCP rule Get call Implement QoS DSCP marking rule Get call. * Networking V2: add QoS DSCP rule Delete call Implement QoS DSP marking rule Delete. * Networking QoS DSCP Marking: fix Update call code Use http.StatusOK code for Update call. --- .../networking/v2/extensions/qos/rules/doc.go | 22 +++++++++++ .../v2/extensions/qos/rules/requests.go | 14 ++++++- .../v2/extensions/qos/rules/results.go | 12 ++++++ .../extensions/qos/rules/testing/fixtures.go | 10 +++++ .../qos/rules/testing/requests_test.go | 37 ++++++++++++++++++- .../v2/extensions/qos/rules/urls.go | 8 ++++ 6 files changed, 101 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index f09ef845e8..b3032b8404 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -101,6 +101,18 @@ Example of Listing DSCP marking rules fmt.Printf("%+v\n", dscpMarkingRule) } +Example of Getting a single DSCPMarkingRule + + 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) + } + + fmt.Printf("Rule: %+v\n", rule) + Example of Creating a single DSCPMarkingRule opts := rules.CreateDSCPMarkingRuleOpts{ @@ -133,5 +145,15 @@ Example of Updating a single DSCPMarkingRule } fmt.Printf("Rule: %+v\n", rule) + +Example of Deleting a single DSCPMarkingRule + + 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) + } */ package rules diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index c6f3b6c1cb..01bf2b9736 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -191,6 +191,12 @@ func ListDSCPMarkingRules(c *gophercloud.ServiceClient, policyID string, opts DS }) } +// GetDSCPMarkingRule retrieves a specific DSCPMarkingRule based on its ID. +func GetDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetDSCPMarkingRuleResult) { + _, r.Err = c.Get(getDSCPMarkingRuleURL(c, policyID, ruleID), &r.Body, nil) + return +} + // CreateDSCPMarkingRuleOptsBuilder allows to add additional parameters to the // CreateDSCPMarkingRule request. type CreateDSCPMarkingRuleOptsBuilder interface { @@ -246,7 +252,13 @@ func UpdateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID, ruleID s return } _, r.Err = client.Put(updateDSCPMarkingRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, + OkCodes: []int{200}, }) return } + +// DeleteDSCPMarkingRule accepts policy and rule ID and deletes the DSCPMarkingRule associated with them. +func DeleteDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteDSCPMarkingRuleResult) { + _, r.Err = c.Delete(deleteDSCPMarkingRuleURL(c, policyID, ruleID), nil) + return +} diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index ba5e8270f4..5fe33c700f 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -96,6 +96,12 @@ func (r commonResult) ExtractDSCPMarkingRule() (*DSCPMarkingRule, error) { return s.DSCPMarkingRule, err } +// GetDSCPMarkingRuleResult represents the result of a Get operation. Call its Extract +// method to interpret it as a DSCPMarkingRule. +type GetDSCPMarkingRuleResult struct { + commonResult +} + // CreateDSCPMarkingRuleResult represents the result of a Create operation. Call its Extract // method to interpret it as a DSCPMarkingRule. type CreateDSCPMarkingRuleResult struct { @@ -108,6 +114,12 @@ type UpdateDSCPMarkingRuleResult struct { commonResult } +// DeleteDSCPMarkingRuleResult represents the result of a Delete operation. Call its Extract +// method to interpret it as a DSCPMarkingRule. +type DeleteDSCPMarkingRuleResult struct { + gophercloud.ErrResult +} + // DSCPMarkingRule represents a QoS policy rule to set DSCP marking. type DSCPMarkingRule struct { // ID is a unique ID of the policy. diff --git a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go index de15090a22..7ca1102cb7 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go @@ -80,6 +80,16 @@ const DSCPMarkingRulesListResult = ` } ` +// DSCPMarkingRuleGetResult represents a raw result of a Get DSCPMarkingRule call. +const DSCPMarkingRuleGetResult = ` +{ + "dscp_marking_rule": { + "id": "30a57f4a-336b-4382-8275-d708babd2241", + "dscp_mark": 26 + } +} +` + // DSCPMarkingRuleCreateRequest represents a raw body of a Create DSCPMarkingRule call. const DSCPMarkingRuleCreateRequest = ` { diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index 72f58c3d5f..d1357b8d6a 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -200,6 +200,27 @@ func TestListDSCPMarkingRule(t *testing.T) { } } +func TestGetDSCPMarkingRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, DSCPMarkingRuleGetResult) + }) + + r, err := rules.GetDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractDSCPMarkingRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") + th.AssertEquals(t, 26, r.DSCPMark) +} + func TestCreateDSCPMarkingRule(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -239,7 +260,7 @@ func TestUpdateDSCPMarkingRule(t *testing.T) { th.TestJSONRequest(t, r, DSCPMarkingRuleUpdateRequest) w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) + w.WriteHeader(http.StatusOK) fmt.Fprintf(w, DSCPMarkingRuleUpdateResult) }) @@ -254,3 +275,17 @@ func TestUpdateDSCPMarkingRule(t *testing.T) { th.AssertEquals(t, "30a57f4a-336b-4382-8275-d708babd2241", r.ID) th.AssertEquals(t, 26, r.DSCPMark) } + +func TestDeleteDSCPMarkingRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/dscp_marking_rules/30a57f4a-336b-4382-8275-d708babd2241", 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) + }) + + res := rules.DeleteDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go index 63ef0a763c..2a278611b1 100644 --- a/openstack/networking/v2/extensions/qos/rules/urls.go +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -49,6 +49,10 @@ func listDSCPMarkingRulesURL(c *gophercloud.ServiceClient, policyID string) stri return dscpMarkingRulesRootURL(c, policyID) } +func getDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return dscpMarkingRulesResourceURL(c, policyID, ruleID) +} + func createDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID string) string { return dscpMarkingRulesRootURL(c, policyID) } @@ -56,3 +60,7 @@ func createDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID string) str func updateDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return dscpMarkingRulesResourceURL(c, policyID, ruleID) } + +func deleteDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return dscpMarkingRulesResourceURL(c, policyID, ruleID) +} From a22a3d3447407086ed7b59d41b9c79043cf705f8 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 10 Jun 2019 21:07:34 +0300 Subject: [PATCH 0764/2296] Networking V2: add QoS bw limit rule acc tests (#1606) * Networking V2: add QoS bw limit rule acc tests Implement TestBandwidthLimitRulesCRUD test and CreateBandwidthLimitRule tests helper. * Networking V2 rule QoS: 200 status code for Update Use http.StatusOK for QoS BWLimit rule Update call. --- .../v2/extensions/qos/rules/rules.go | 35 ++++++++++++ .../v2/extensions/qos/rules/rules_test.go | 55 +++++++++++++++++++ .../v2/extensions/qos/rules/requests.go | 2 +- .../qos/rules/testing/requests_test.go | 2 +- script/acceptancetest | 1 + 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 acceptance/openstack/networking/v2/extensions/qos/rules/rules.go create mode 100644 acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go diff --git a/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go b/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go new file mode 100644 index 0000000000..7c9429097e --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go @@ -0,0 +1,35 @@ +package rules + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// CreateBandwidthLimitRule will create a QoS BandwidthLimitRule associated with the provided QoS policy. +// An error will be returned if the QoS rule could not be created. +func CreateBandwidthLimitRule(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*rules.BandwidthLimitRule, error) { + maxKBps := 3000 + maxBurstKBps := 300 + + createOpts := rules.CreateBandwidthLimitRuleOpts{ + MaxKBps: maxKBps, + MaxBurstKBps: maxBurstKBps, + } + + t.Logf("Attempting to create a QoS bandwidth limit rule with max_kbps: %d, max_burst_kbps: %d", maxKBps, maxBurstKBps) + + rule, err := rules.CreateBandwidthLimitRule(client, policyID, createOpts).ExtractBandwidthLimitRule() + if err != nil { + return nil, err + } + + t.Logf("Succesfully created a QoS bandwidth limit rule") + + th.AssertEquals(t, maxKBps, rule.MaxKBps) + th.AssertEquals(t, maxBurstKBps, rule.MaxBurstKBps) + + return rule, nil +} diff --git a/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go b/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go new file mode 100644 index 0000000000..0598d9e91f --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -0,0 +1,55 @@ +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/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestBandwidthLimitRulesCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a QoS policy + policy, err := accpolicies.CreateQoSPolicy(t, client) + th.AssertNoErr(t, err) + defer policies.Delete(client, policy.ID) + + tools.PrintResource(t, policy) + + // Create a QoS policy rule. + rule, err := CreateBandwidthLimitRule(t, client, policy.ID) + th.AssertNoErr(t, err) + defer rules.DeleteBandwidthLimitRule(client, policy.ID, rule.ID) + + // Update the QoS policy rule. + newMaxBurstKBps := 0 + updateOpts := rules.UpdateBandwidthLimitRuleOpts{ + MaxBurstKBps: &newMaxBurstKBps, + } + newRule, err := rules.UpdateBandwidthLimitRule(client, policy.ID, rule.ID, updateOpts).ExtractBandwidthLimitRule() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newRule) + th.AssertEquals(t, newRule.MaxBurstKBps, 0) + + allPages, err := rules.ListBandwidthLimitRules(client, policy.ID, rules.BandwidthLimitRulesListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allRules, err := rules.ExtractBandwidthLimitRules(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, rule := range allRules { + if rule.ID == newRule.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index 01bf2b9736..1d4c3d254b 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -130,7 +130,7 @@ func UpdateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID, ruleI return } _, r.Err = client.Put(updateBandwidthLimitRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, + OkCodes: []int{200}, }) return } diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index d1357b8d6a..d560e973ae 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -122,7 +122,7 @@ func TestUpdateBandwidthLimitRule(t *testing.T) { th.TestJSONRequest(t, r, BandwidthLimitRulesUpdateRequest) w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) + w.WriteHeader(http.StatusOK) fmt.Fprintf(w, BandwidthLimitRulesUpdateResult) }) diff --git a/script/acceptancetest b/script/acceptancetest index 24598fc990..69341a9c6f 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -64,6 +64,7 @@ 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/rbacpolicies acceptance/openstack/networking/v2/extensions/subnetpools From 59ceaa689826d33e9d5ea530b60ba86057cbfb01 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 10 Jun 2019 19:27:59 -0600 Subject: [PATCH 0765/2296] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4289db235..69048aeddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,16 @@ IMPROVEMENTS * 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) + +BUG FIXES + +* Updated `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` to use return code 200 [GH-1606](https://github.com/gophercloud/gophercloud/pull/1606) ## 0.1.0 (May 27, 2019) From 368f26bad44cdb8c9d85666683f545648e7a1d0e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 10 Jun 2019 19:49:43 -0600 Subject: [PATCH 0766/2296] Update step-02-issues.md --- docs/contributor-tutorial/step-02-issues.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contributor-tutorial/step-02-issues.md b/docs/contributor-tutorial/step-02-issues.md index a3ae2a237b..3c1937612b 100644 --- a/docs/contributor-tutorial/step-02-issues.md +++ b/docs/contributor-tutorial/step-02-issues.md @@ -25,9 +25,9 @@ Feature Request --------------- If you've noticed that a feature is missing from Gophercloud, you'll also -need to create an issue before doing any work. This is start a discussion about -whether or not the feature should be included in Gophercloud. We don't want to -want to see you put in hours of work only to learn that the feature is out of +need to create an issue before doing any work. This is to start a discussion +about whether or not the feature should be included in Gophercloud. We don't +want to see you put in hours of work only to learn that the feature is out of scope of the project. Feature requests can come in different forms: From 46d131b669762bc8c44f6523abd7c8a8e1df1a8b Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 11 Jun 2019 16:44:04 +0300 Subject: [PATCH 0767/2296] Networking V2: add QoS min bw rule Create call (#1615) * Networking V2: add QoS min bw rule List call Implement ListMinimumBandwidthRules with MinimumBandwidthRule struct and tests. * Networking V2: add QoS min bw rule Get call Implement GetMinimumBandwidthRule tests. Fix MinimumBandwidthRules docs. * Networking V2: add QoS min bw rule Create call Implement CreateMinimumBandwidthRule call with test. --- .../networking/v2/extensions/qos/rules/doc.go | 49 ++++++++++ .../v2/extensions/qos/rules/requests.go | 90 ++++++++++++++++++ .../v2/extensions/qos/rules/results.go | 63 ++++++++++++ .../extensions/qos/rules/testing/fixtures.go | 43 +++++++++ .../qos/rules/testing/requests_test.go | 95 +++++++++++++++++++ .../v2/extensions/qos/rules/urls.go | 25 ++++- 6 files changed, 363 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index b3032b8404..0a12c304a3 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -155,5 +155,54 @@ Example of Deleting a single DSCPMarkingRule if err != nil { panic(err) } + +Example of Listing MinimumBandwidthRules + + listOpts := rules.MinimumBandwidthRulesListOpts{ + MinKBps: 3000, + } + + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + + allPages, err := rules.ListMinimumBandwidthRules(networkClient, policyID, listOpts).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) + } + +Example of Getting a single MinimumBandwidthRule + + 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) + } + + fmt.Printf("Rule: %+v\n", rule) + +Example of Creating a single MinimumBandwidthRule + + opts := rules.CreateMinimumBandwidthRuleOpts{ + MinKBps: 2000, + } + + policyID := "501005fa-3b56-4061-aaca-3f24995112e1" + + rule, err := rules.CreateMinimumBandwidthRule(networkClient, policyID, opts).ExtractMinimumBandwidthRule() + if err != nil { + panic(err) + } + + fmt.Printf("Rule: %+v\n", rule) */ package rules diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index 1d4c3d254b..5c3060b968 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -262,3 +262,93 @@ func DeleteDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string _, r.Err = c.Delete(deleteDSCPMarkingRuleURL(c, policyID, ruleID), nil) return } + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type MinimumBandwidthRulesListOptsBuilder interface { + ToMinimumBandwidthRulesListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the Neutron API. Filtering is achieved by passing in struct field values +// that map to the MinimumBandwidthRules attributes you want to see returned. +// SortKey allows you to sort by a particular MinimumBandwidthRule attribute. +// SortDir sets the direction, and is either `asc' or `desc'. +// Marker and Limit are used for the pagination. +type MinimumBandwidthRulesListOpts struct { + ID string `q:"id"` + TenantID string `q:"tenant_id"` + MinKBps int `q:"min_kbps"` + Direction string `q:"direction"` + 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"` +} + +// ToMinimumBandwidthRulesListQuery formats a ListOpts into a query string. +func (opts MinimumBandwidthRulesListOpts) ToMinimumBandwidthRulesListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListMinimumBandwidthRules returns a Pager which allows you to iterate over a collection of +// MinimumBandwidthRules. It accepts a ListOpts struct, which allows you to filter and sort +// the returned collection for greater efficiency. +func ListMinimumBandwidthRules(c *gophercloud.ServiceClient, policyID string, opts MinimumBandwidthRulesListOptsBuilder) pagination.Pager { + url := listMinimumBandwidthRulesURL(c, policyID) + if opts != nil { + query, err := opts.ToMinimumBandwidthRulesListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return MinimumBandwidthRulePage{pagination.LinkedPageBase{PageResult: r}} + + }) +} + +// GetMinimumBandwidthRule retrieves a specific MinimumBandwidthRule based on its ID. +func GetMinimumBandwidthRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetMinimumBandwidthRuleResult) { + _, r.Err = c.Get(getMinimumBandwidthRuleURL(c, policyID, ruleID), &r.Body, nil) + return +} + +// CreateMinimumBandwidthRuleOptsBuilder allows to add additional parameters to the +// CreateMinimumBandwidthRule request. +type CreateMinimumBandwidthRuleOptsBuilder interface { + ToMinimumBandwidthRuleCreateMap() (map[string]interface{}, error) +} + +// CreateMinimumBandwidthRuleOpts specifies parameters of a new MinimumBandwidthRule. +type CreateMinimumBandwidthRuleOpts struct { + // MaxKBps is a minimum kilobits per second. It's a required parameter. + MinKBps int `json:"min_kbps"` + + // Direction represents the direction of traffic. + Direction string `json:"direction,omitempty"` +} + +// ToMinimumBandwidthRuleCreateMap constructs a request body from CreateMinimumBandwidthRuleOpts. +func (opts CreateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "minimum_bandwidth_rule") +} + +// CreateMinimumBandwidthRule requests the creation of a new MinimumBandwidthRule on the server. +func CreateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID string, opts CreateMinimumBandwidthRuleOptsBuilder) (r CreateMinimumBandwidthRuleResult) { + b, err := opts.ToMinimumBandwidthRuleCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createMinimumBandwidthRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index 5fe33c700f..a04ab26eeb 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -158,3 +158,66 @@ func ExtractDSCPMarkingRules(r pagination.Page) ([]DSCPMarkingRule, error) { func ExtractDSCPMarkingRulesInto(r pagination.Page, v interface{}) error { return r.(DSCPMarkingRulePage).Result.ExtractIntoSlicePtr(v, "dscp_marking_rules") } + +// Extract is a function that accepts a result and extracts a BandwidthLimitRule. +func (r commonResult) ExtractMinimumBandwidthRule() (*MinimumBandwidthRule, error) { + var s struct { + MinimumBandwidthRule *MinimumBandwidthRule `json:"minimum_bandwidth_rule"` + } + err := r.ExtractInto(&s) + return s.MinimumBandwidthRule, err +} + +// GetMinimumBandwidthRuleResult represents the result of a Get operation. Call its Extract +// method to interpret it as a MinimumBandwidthRule. +type GetMinimumBandwidthRuleResult struct { + commonResult +} + +// CreateMinimumBandwidthRuleResult represents the result of a Create operation. Call its Extract +// method to interpret it as a MinimumBandwidthtRule. +type CreateMinimumBandwidthRuleResult struct { + commonResult +} + +// MinimumBandwidthRule represents a QoS policy rule to set minimum bandwidth. +type MinimumBandwidthRule struct { + // ID is a unique ID of the rule. + ID string `json:"id"` + + // TenantID is the ID of the Identity project. + TenantID string `json:"tenant_id"` + + // MaxKBps is a maximum kilobits per second. + MinKBps int `json:"min_kbps"` + + // Direction represents the direction of traffic. + Direction string `json:"direction"` + + // Tags optionally set via extensions/attributestags. + Tags []string `json:"tags"` +} + +// MinimumBandwidthRulePage stores a single page of MinimumBandwidthRules from a List() API call. +type MinimumBandwidthRulePage struct { + pagination.LinkedPageBase +} + +// IsEmpty checks whether a MinimumBandwidthRulePage is empty. +func (r MinimumBandwidthRulePage) IsEmpty() (bool, error) { + is, err := ExtractMinimumBandwidthRules(r) + return len(is) == 0, err +} + +// ExtractMinimumBandwidthRules accepts a MinimumBandwidthRulePage, and extracts the elements into a slice of +// MinimumBandwidthRules. +func ExtractMinimumBandwidthRules(r pagination.Page) ([]MinimumBandwidthRule, error) { + var s []MinimumBandwidthRule + err := ExtractMinimumBandwidthRulesInto(r, &s) + return s, err +} + +// ExtractMinimumBandwidthRulesInto extracts the elements into a slice of RBAC Policy structs. +func ExtractMinimumBandwidthRulesInto(r pagination.Page, v interface{}) error { + return r.(MinimumBandwidthRulePage).Result.ExtractIntoSlicePtr(v, "minimum_bandwidth_rules") +} diff --git a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go index 7ca1102cb7..4361485544 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go @@ -127,3 +127,46 @@ const DSCPMarkingRuleUpdateResult = ` } } ` + +// MinimumBandwidthRulesListResult represents a raw result of a List call to MinimumBandwidthRules. +const MinimumBandwidthRulesListResult = ` +{ + "minimum_bandwidth_rules": [ + { + "min_kbps": 3000, + "direction": "egress", + "id": "30a57f4a-336b-4382-8275-d708babd2241" + } + ] +} +` + +// MinimumBandwidthRulesGetResult represents a raw result of a Get call to a specific MinimumBandwidthRule. +const MinimumBandwidthRulesGetResult = ` +{ + "minimum_bandwidth_rule": { + "min_kbps": 3000, + "direction": "egress", + "id": "30a57f4a-336b-4382-8275-d708babd2241" + } +} +` + +// MinimumBandwidthRulesCreateRequest represents a raw body of a Create MinimumBandwidthRule call. +const MinimumBandwidthRulesCreateRequest = ` +{ + "minimum_bandwidth_rule": { + "min_kbps": 2000 + } +} +` + +// MinimumBandwidthRulesCreateResult represents a raw result of a Create MinimumBandwidthRule call. +const MinimumBandwidthRulesCreateResult = ` +{ + "minimum_bandwidth_rule": { + "min_kbps": 2000, + "id": "30a57f4a-336b-4382-8275-d708babd2241" + } +} +` diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index d560e973ae..2704f9b1d2 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -289,3 +289,98 @@ func TestDeleteDSCPMarkingRule(t *testing.T) { res := rules.DeleteDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") th.AssertNoErr(t, res.Err) } + +func TestListMinimumBandwidthRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_rules", 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, MinimumBandwidthRulesListResult) + }) + + count := 0 + + err := rules.ListMinimumBandwidthRules( + fake.ServiceClient(), + "501005fa-3b56-4061-aaca-3f24995112e1", + rules.MinimumBandwidthRulesListOpts{}, + ).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := rules.ExtractMinimumBandwidthRules(page) + if err != nil { + t.Errorf("Failed to extract minimum bandwith rules: %v", err) + return false, nil + } + + expected := []rules.MinimumBandwidthRule{ + { + ID: "30a57f4a-336b-4382-8275-d708babd2241", + Direction: "egress", + MinKBps: 3000, + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGetMinimumBandwidthRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, MinimumBandwidthRulesGetResult) + }) + + r, err := rules.GetMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractMinimumBandwidthRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") + th.AssertEquals(t, r.Direction, "egress") + th.AssertEquals(t, r.MinKBps, 3000) +} + +func TestCreateMinimumBandwidthRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_rules", 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, MinimumBandwidthRulesCreateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, MinimumBandwidthRulesCreateResult) + }) + + opts := rules.CreateMinimumBandwidthRuleOpts{ + MinKBps: 2000, + } + r, err := rules.CreateMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractMinimumBandwidthRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, 2000, r.MinKBps) +} diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go index 2a278611b1..35c7e0c5c5 100644 --- a/openstack/networking/v2/extensions/qos/rules/urls.go +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -5,8 +5,9 @@ import "github.com/gophercloud/gophercloud" const ( rootPath = "qos/policies" - bandwidthLimitRulesResourcePath = "bandwidth_limit_rules" - dscpMarkingRulesResourcePath = "dscp_marking_rules" + bandwidthLimitRulesResourcePath = "bandwidth_limit_rules" + dscpMarkingRulesResourcePath = "dscp_marking_rules" + minimumBandwidthRulesResourcePath = "minimum_bandwidth_rules" ) func bandwidthLimitRulesRootURL(c *gophercloud.ServiceClient, policyID string) string { @@ -64,3 +65,23 @@ func updateDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID str func deleteDSCPMarkingRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { return dscpMarkingRulesResourceURL(c, policyID, ruleID) } + +func minimumBandwidthRulesRootURL(c *gophercloud.ServiceClient, policyID string) string { + return c.ServiceURL(rootPath, policyID, minimumBandwidthRulesResourcePath) +} + +func minimumBandwidthRulesResourceURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return c.ServiceURL(rootPath, policyID, minimumBandwidthRulesResourcePath, ruleID) +} + +func listMinimumBandwidthRulesURL(c *gophercloud.ServiceClient, policyID string) string { + return minimumBandwidthRulesRootURL(c, policyID) +} + +func getMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return minimumBandwidthRulesResourceURL(c, policyID, ruleID) +} + +func createMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID string) string { + return minimumBandwidthRulesRootURL(c, policyID) +} From 8b7d9c2fd33b81ea0adee423d55a118fcc1229da Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 11 Jun 2019 23:10:52 +0300 Subject: [PATCH 0768/2296] Networking V2: add QoS DSCP marking acc test (#1612) Implement TestDSCPMarkingRulesCRUD and add CreateDSCPMarkingRule test helper. --- .../v2/extensions/qos/rules/rules.go | 23 ++++++++++ .../v2/extensions/qos/rules/rules_test.go | 43 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go b/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go index 7c9429097e..10a8f9cf57 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go +++ b/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go @@ -33,3 +33,26 @@ func CreateBandwidthLimitRule(t *testing.T, client *gophercloud.ServiceClient, p return rule, nil } + +// CreateDSCPMarkingRule will create a QoS DSCPMarkingRule associated with the provided QoS policy. +// An error will be returned if the QoS rule could not be created. +func CreateDSCPMarkingRule(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*rules.DSCPMarkingRule, error) { + dscpMark := 26 + + createOpts := rules.CreateDSCPMarkingRuleOpts{ + DSCPMark: dscpMark, + } + + t.Logf("Attempting to create a QoS DSCP marking rule with dscp_mark: %d", dscpMark) + + rule, err := rules.CreateDSCPMarkingRule(client, policyID, createOpts).ExtractDSCPMarkingRule() + if err != nil { + return nil, err + } + + t.Logf("Succesfully created a QoS DSCP marking rule") + + th.AssertEquals(t, dscpMark, rule.DSCPMark) + + return rule, nil +} 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 0598d9e91f..a55b285b30 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -53,3 +53,46 @@ func TestBandwidthLimitRulesCRUD(t *testing.T) { th.AssertEquals(t, found, true) } + +func TestDSCPMarkingRulesCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a QoS policy + policy, err := accpolicies.CreateQoSPolicy(t, client) + th.AssertNoErr(t, err) + defer policies.Delete(client, policy.ID) + + tools.PrintResource(t, policy) + + // Create a QoS policy rule. + rule, err := CreateDSCPMarkingRule(t, client, policy.ID) + th.AssertNoErr(t, err) + defer rules.DeleteDSCPMarkingRule(client, policy.ID, rule.ID) + + // Update the QoS policy rule. + dscpMark := 20 + updateOpts := rules.UpdateDSCPMarkingRuleOpts{ + DSCPMark: &dscpMark, + } + newRule, err := rules.UpdateDSCPMarkingRule(client, policy.ID, rule.ID, updateOpts).ExtractDSCPMarkingRule() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newRule) + th.AssertEquals(t, newRule.DSCPMark, 20) + + allPages, err := rules.ListDSCPMarkingRules(client, policy.ID, rules.DSCPMarkingRulesListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allRules, err := rules.ExtractDSCPMarkingRules(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, rule := range allRules { + if rule.ID == newRule.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} From a4af273957ae721130854a55a95c2abae645d336 Mon Sep 17 00:00:00 2001 From: "Brad P. Crochet" Date: Tue, 11 Jun 2019 21:34:22 -0400 Subject: [PATCH 0769/2296] Baremetal Introspection: Add hostname to introspection data (#1617) A patch is being made to the Ironic Python Agent to return the hostname that the introspected machine thinks it has. This enables Gophercloud to pass that along. Requires: https://review.opendev.org/#/c/663991/ --- openstack/baremetalintrospection/v1/introspection/results.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 5cb35e999a..d303499205 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -170,6 +170,7 @@ type Data struct { MACs []string `json:"macs"` MemoryMB int `json:"memory_mb"` RootDisk RootDiskType `json:"root_disk"` + Hostname string `json:"hostname,omitempty"` } // Sub Types defined under Data and deeper in the structure From 4e17bace39f9cbf5b2b2d467e432790a14e6ad6c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 11 Jun 2019 19:36:17 -0600 Subject: [PATCH 0770/2296] Baremetal Introspection: Remove omitempty from hostname field --- 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 d303499205..ada823116d 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -170,7 +170,7 @@ type Data struct { MACs []string `json:"macs"` MemoryMB int `json:"memory_mb"` RootDisk RootDiskType `json:"root_disk"` - Hostname string `json:"hostname,omitempty"` + Hostname string `json:"hostname"` } // Sub Types defined under Data and deeper in the structure From 039e7d451a06d8ba2398ee6d0488077b02101ea5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 12 Jun 2019 08:54:09 -0600 Subject: [PATCH 0771/2296] Compute v2: Scheduler Hints Fix (#1620) The "query" part of the scheduler_hints has never been correct. Instead of sending a nested JSON structure, the Nova API actually expects a marshalled JSON string which it will then decode. --- .../v2/extensions/schedulerhints/requests.go | 14 +++++++++++++- .../schedulerhints/testing/requests_test.go | 14 ++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/openstack/compute/v2/extensions/schedulerhints/requests.go b/openstack/compute/v2/extensions/schedulerhints/requests.go index 3fabeddef3..963934507f 100644 --- a/openstack/compute/v2/extensions/schedulerhints/requests.go +++ b/openstack/compute/v2/extensions/schedulerhints/requests.go @@ -1,6 +1,7 @@ package schedulerhints import ( + "encoding/json" "net" "regexp" "strings" @@ -105,7 +106,18 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa err.Info = "Must be a conditional statement in the format of [op,variable,value]" return nil, err } - sh["query"] = opts.Query + + // The query needs to be sent as a marshalled string. + b, err := json.Marshal(opts.Query) + if err != nil { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.Query" + err.Value = opts.Query + err.Info = "Must be a conditional statement in the format of [op,variable,value]" + return nil, err + } + + sh["query"] = string(b) } if opts.TargetCell != "" { diff --git a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go index c387eab860..393c0f73ce 100644 --- a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go +++ b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go @@ -25,7 +25,7 @@ func TestCreateOpts(t *testing.T) { "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, - Query: []interface{}{">=", "$free_ram_mb", "1024"}, + Query: []interface{}{"=", "$free_ram_mb", "1024"}, TargetCell: "foobar", BuildNearHostIP: "192.168.1.1/24", AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, @@ -53,9 +53,7 @@ func TestCreateOpts(t *testing.T) { "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287" ], - "query": [ - ">=", "$free_ram_mb", "1024" - ], + "query": "[\"=\",\"$free_ram_mb\",\"1024\"]", "target_cell": "foobar", "build_near_host_ip": "192.168.1.1", "cidr": "/24", @@ -85,7 +83,7 @@ func TestCreateOptsWithComplexQuery(t *testing.T) { "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, - Query: []interface{}{"and", []string{">=", "$free_ram_mb", "1024"}, []string{">=", "$free_disk_mb", "204800"}}, + Query: []interface{}{"and", []string{"=", "$free_ram_mb", "1024"}, []string{"=", "$free_disk_mb", "204800"}}, TargetCell: "foobar", BuildNearHostIP: "192.168.1.1/24", AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, @@ -113,11 +111,7 @@ func TestCreateOptsWithComplexQuery(t *testing.T) { "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287" ], - "query": [ - "and", - [">=", "$free_ram_mb", "1024"], - [">=", "$free_disk_mb", "204800"] - ], + "query": "[\"and\",[\"=\",\"$free_ram_mb\",\"1024\"],[\"=\",\"$free_disk_mb\",\"204800\"]]", "target_cell": "foobar", "build_near_host_ip": "192.168.1.1", "cidr": "/24", From f5fa3f85d3a9eedceffc45b7555ab23cea4ba4a1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 12 Jun 2019 08:59:05 -0600 Subject: [PATCH 0772/2296] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69048aeddb..5081d91ba2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,10 +18,16 @@ IMPROVEMENTS * 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-1617](https://github.com/gophercloud/gophercloud/pull/1617) + 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) From b43c1681f0b07b2f716d044c43c97ae41d266f08 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 12 Jun 2019 21:30:34 -0400 Subject: [PATCH 0773/2296] Baremetal introspection: Read extra_hardware data (#1611) If the extra_hardware processing hook is enabled in ironic-inspector, then additional data is provided as a top-level object named "extra", with a number of sections, each of which is also an object containing objects with various key/value data. Signed-off-by: Zane Bitter --- .../v1/introspection/results.go | 15 +++++ .../v1/introspection/testing/results_test.go | 60 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index ada823116d..4b9ada1505 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -171,6 +171,7 @@ type Data struct { MemoryMB int `json:"memory_mb"` RootDisk RootDiskType `json:"root_disk"` Hostname string `json:"hostname"` + Extra ExtraHardwareDataType `json:"extra"` } // Sub Types defined under Data and deeper in the structure @@ -248,6 +249,20 @@ type SystemVendorType struct { SerialNumber string `json:"serial_number"` } +type ExtraHardwareData map[string]interface{} + +type ExtraHardwareDataSection map[string]ExtraHardwareData + +type ExtraHardwareDataType struct { + CPU ExtraHardwareDataSection `json:"cpu"` + Disk ExtraHardwareDataSection `json:"disk"` + Firmware ExtraHardwareDataSection `json:"firmware"` + IPMI ExtraHardwareDataSection `json:"ipmi"` + Memory ExtraHardwareDataSection `json:"memory"` + Network ExtraHardwareDataSection `json:"network"` + System ExtraHardwareDataSection `json:"system"` +} + // UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { var list []interface{} diff --git a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go index 87383ed6a1..84392ee30b 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go @@ -27,3 +27,63 @@ func TestLLDPTLVErrors(t *testing.T) { } } } + +func TestExtraHardware(t *testing.T) { + extraJson := `{ + "cpu": { + "logical": {"number": 16}, + "physical": { + "clock": 2105032704, + "cores": 8, + "flags": "lm fpu fpu_exception wp vme de"} + }, + "disk": { + "sda": { + "rotational": 1, + "vendor": "TEST" + } + }, + "firmware": { + "bios": { + "date": "01/01/1970", + "vendor": "test" + } + }, + "ipmi": { + "Fan1A RPM": {"unit": "RPM", "value": 3120}, + "Fan1B RPM": {"unit": "RPM", "value": 2280} + }, + "memory": { + "bank0": { + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" + }, + "bank1": { + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" + } + }, + "network": { + "em1": { + "Autonegotiate": "on", + "loopback": "off [fixed]" + }, + "p2p1": { + "Autonegotiate": "on", + "loopback": "off [fixed]" + } + }, + "system": { + "ipmi": {"channel": 1}, + "kernel": {"arch": "x86_64", "version": "3.10.0"}, + "motherboard": {"vendor": "Test"}, + "product": {"name": "test", "vendor": "Test"} + } + }` + + var output introspection.ExtraHardwareDataType + err := json.Unmarshal([]byte(extraJson), &output) + if err != nil { + t.Errorf("Failed to unmarshal ExtraHardware data: %s", err) + } +} From 121330611ad0613f0c10ecd0a78fa94a304d0033 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 13 Jun 2019 13:16:24 +0300 Subject: [PATCH 0774/2296] Networking V2: add QoS min bw rules acc test (#1624) * Networking V2: add QoS min bw rule Update call Implement UpdateMinimumBandwidthRule function with tests and docs. * Networking V2: add QoS min bw rule Delete call Implement DeleteMinimumBandwidthRule function with tests and docs. * Networking V2: add QoS min bw rules acc test Implement TestMinimumBandwidthRulesCRUD and CreateMinimumBandwidthRule test helper. --- .../v2/extensions/qos/rules/rules.go | 23 ++++++++++ .../v2/extensions/qos/rules/rules_test.go | 43 +++++++++++++++++++ .../networking/v2/extensions/qos/rules/doc.go | 28 ++++++++++++ .../v2/extensions/qos/rules/requests.go | 39 +++++++++++++++++ .../v2/extensions/qos/rules/results.go | 12 ++++++ .../extensions/qos/rules/testing/fixtures.go | 19 ++++++++ .../qos/rules/testing/requests_test.go | 41 ++++++++++++++++++ .../v2/extensions/qos/rules/urls.go | 8 ++++ 8 files changed, 213 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go b/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go index 10a8f9cf57..eb760670d2 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go +++ b/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go @@ -56,3 +56,26 @@ func CreateDSCPMarkingRule(t *testing.T, client *gophercloud.ServiceClient, poli return rule, nil } + +// CreateMinimumBandwidthRule will create a QoS MinimumBandwidthRule associated with the provided QoS policy. +// An error will be returned if the QoS rule could not be created. +func CreateMinimumBandwidthRule(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*rules.MinimumBandwidthRule, error) { + minKBps := 1000 + + createOpts := rules.CreateMinimumBandwidthRuleOpts{ + MinKBps: minKBps, + } + + t.Logf("Attempting to create a QoS minimum bandwidth rule with min_kbps: %d", minKBps) + + rule, err := rules.CreateMinimumBandwidthRule(client, policyID, createOpts).ExtractMinimumBandwidthRule() + if err != nil { + return nil, err + } + + t.Logf("Succesfully created a QoS minimum bandwidth rule") + + th.AssertEquals(t, minKBps, rule.MinKBps) + + return rule, nil +} 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 a55b285b30..be382dba60 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -96,3 +96,46 @@ func TestDSCPMarkingRulesCRUD(t *testing.T) { th.AssertEquals(t, found, true) } + +func TestMinimumBandwidthRulesCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a QoS policy + policy, err := accpolicies.CreateQoSPolicy(t, client) + th.AssertNoErr(t, err) + defer policies.Delete(client, policy.ID) + + tools.PrintResource(t, policy) + + // Create a QoS policy rule. + rule, err := CreateMinimumBandwidthRule(t, client, policy.ID) + th.AssertNoErr(t, err) + defer rules.DeleteMinimumBandwidthRule(client, policy.ID, rule.ID) + + // Update the QoS policy rule. + minKBps := 500 + updateOpts := rules.UpdateMinimumBandwidthRuleOpts{ + MinKBps: &minKBps, + } + newRule, err := rules.UpdateMinimumBandwidthRule(client, policy.ID, rule.ID, updateOpts).ExtractMinimumBandwidthRule() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newRule) + th.AssertEquals(t, newRule.MinKBps, 500) + + allPages, err := rules.ListMinimumBandwidthRules(client, policy.ID, rules.MinimumBandwidthRulesListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allRules, err := rules.ExtractMinimumBandwidthRules(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, rule := range allRules { + if rule.ID == newRule.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index 0a12c304a3..8940e2f16e 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -204,5 +204,33 @@ Example of Creating a single MinimumBandwidthRule } fmt.Printf("Rule: %+v\n", rule) + +Example of Updating a single MinimumBandwidthRule + + minKBps := 500 + + opts := rules.UpdateMinimumBandwidthRuleOpts{ + MinKBps: &minKBps, + } + + 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) + } + + fmt.Printf("Rule: %+v\n", rule) + +Example of Deleting a single MinimumBandwidthRule + + 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) + } */ package rules diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index 5c3060b968..a003efa899 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -352,3 +352,42 @@ func CreateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID stri }) return } + +// UpdateMinimumBandwidthRuleOptsBuilder allows to add additional parameters to the +// UpdateMinimumBandwidthRule request. +type UpdateMinimumBandwidthRuleOptsBuilder interface { + ToMinimumBandwidthRuleUpdateMap() (map[string]interface{}, error) +} + +// UpdateMinimumBandwidthRuleOpts specifies parameters for the Update call. +type UpdateMinimumBandwidthRuleOpts struct { + // MaxKBps is a minimum kilobits per second. It's a required parameter. + MinKBps *int `json:"min_kbps,omitempty"` + + // Direction represents the direction of traffic. + Direction string `json:"direction,omitempty"` +} + +// ToMinimumBandwidthRuleUpdateMap constructs a request body from UpdateMinimumBandwidthRuleOpts. +func (opts UpdateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "minimum_bandwidth_rule") +} + +// UpdateMinimumBandwidthRule requests the creation of a new MinimumBandwidthRule on the server. +func UpdateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateMinimumBandwidthRuleOptsBuilder) (r UpdateMinimumBandwidthRuleResult) { + b, err := opts.ToMinimumBandwidthRuleUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateMinimumBandwidthRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// DeleteMinimumBandwidthRule accepts policy and rule ID and deletes the MinimumBandwidthRule associated with them. +func DeleteMinimumBandwidthRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteMinimumBandwidthRuleResult) { + _, r.Err = c.Delete(deleteMinimumBandwidthRuleURL(c, policyID, ruleID), nil) + return +} diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index a04ab26eeb..ec193465b7 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -180,6 +180,18 @@ type CreateMinimumBandwidthRuleResult struct { commonResult } +// UpdateMinimumBandwidthRuleResult represents the result of a Update operation. Call its Extract +// method to interpret it as a MinimumBandwidthRule. +type UpdateMinimumBandwidthRuleResult struct { + commonResult +} + +// DeleteMinimumBandwidthRuleResult represents the result of a Delete operation. Call its Extract +// method to interpret it as a MinimumBandwidthRule. +type DeleteMinimumBandwidthRuleResult struct { + gophercloud.ErrResult +} + // MinimumBandwidthRule represents a QoS policy rule to set minimum bandwidth. type MinimumBandwidthRule struct { // ID is a unique ID of the rule. diff --git a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go index 4361485544..5b2bac67bb 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/fixtures.go @@ -170,3 +170,22 @@ const MinimumBandwidthRulesCreateResult = ` } } ` + +// MinimumBandwidthRulesUpdateRequest represents a raw body of a Update MinimumBandwidthRule call. +const MinimumBandwidthRulesUpdateRequest = ` +{ + "minimum_bandwidth_rule": { + "min_kbps": 500 + } +} +` + +// MinimumBandwidthRulesUpdateResult represents a raw result of a Update MinimumBandwidthRule call. +const MinimumBandwidthRulesUpdateResult = ` +{ + "minimum_bandwidth_rule": { + "min_kbps": 500, + "id": "30a57f4a-336b-4382-8275-d708babd2241" + } +} +` diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index 2704f9b1d2..d295bb197c 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -384,3 +384,44 @@ func TestCreateMinimumBandwidthRule(t *testing.T) { th.AssertEquals(t, 2000, r.MinKBps) } + +func TestUpdateMinimumBandwidthRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_rules/30a57f4a-336b-4382-8275-d708babd2241", 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, MinimumBandwidthRulesUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, MinimumBandwidthRulesUpdateResult) + }) + + minKBps := 500 + opts := rules.UpdateMinimumBandwidthRuleOpts{ + MinKBps: &minKBps, + } + r, err := rules.UpdateMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractMinimumBandwidthRule() + th.AssertNoErr(t, err) + + th.AssertEquals(t, 500, r.MinKBps) +} + +func TestDeleteMinimumBandwidthRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/qos/policies/501005fa-3b56-4061-aaca-3f24995112e1/minimum_bandwidth_rules/30a57f4a-336b-4382-8275-d708babd2241", 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) + }) + + res := rules.DeleteMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go index 35c7e0c5c5..6db2bce8a9 100644 --- a/openstack/networking/v2/extensions/qos/rules/urls.go +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -85,3 +85,11 @@ func getMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID, ruleID s func createMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID string) string { return minimumBandwidthRulesRootURL(c, policyID) } + +func updateMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return minimumBandwidthRulesResourceURL(c, policyID, ruleID) +} + +func deleteMinimumBandwidthRuleURL(c *gophercloud.ServiceClient, policyID, ruleID string) string { + return minimumBandwidthRulesResourceURL(c, policyID, ruleID) +} From 75c6f25947188acd3c5b5e2ecd225d2a349c08cf Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 13 Jun 2019 13:20:19 +0300 Subject: [PATCH 0775/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5081d91ba2..e6012d1291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ IMPROVEMENTS * 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-1617](https://github.com/gophercloud/gophercloud/pull/1617) +* 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) BUG FIXES From 061277e601da80c0d5fabe28d1f8afb717bb56b0 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 13 Jun 2019 16:05:16 +0300 Subject: [PATCH 0776/2296] Networking V2: add QoS rule type Get call (#1625) Implement GetRuleType function with unit test. Update QoS rule types documentation. Update QoS rule types acceptance test. Extend RuleType struct with additional fields. Unfortunately the ParameterValues field is implemented as interface{} given it's dynamic behavior (it can be map or slice in different situtations). --- .../qos/ruletypes/ruletypes_test.go | 13 +++- .../v2/extensions/qos/ruletypes/doc.go | 12 +++- .../v2/extensions/qos/ruletypes/requests.go | 6 ++ .../v2/extensions/qos/ruletypes/results.go | 41 ++++++++++- .../qos/ruletypes/testing/fixtures.go | 68 +++++++++++++++++++ .../qos/ruletypes/testing/requests_test.go | 29 ++++++++ .../v2/extensions/qos/ruletypes/urls.go | 4 ++ 7 files changed, 168 insertions(+), 5 deletions(-) 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 e14844b56a..2de4d7b0d9 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -8,7 +8,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/ruletypes" ) -func TestListRuleTypes(t *testing.T) { +func TestRuleTypes(t *testing.T) { client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -28,4 +28,15 @@ func TestListRuleTypes(t *testing.T) { } tools.PrintResource(t, ruleTypes) + + if len(ruleTypes) > 0 { + t.Logf("Trying to get rule type: %s", ruleTypes[0].Type) + + ruleType, err := ruletypes.GetRuleType(client, ruleTypes[0].Type).Extract() + if err != nil { + t.Fatalf("Failed to get rule type %s: %s", ruleTypes[0].Type, err) + } + + tools.PrintResource(t, ruleType) + } } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/doc.go b/openstack/networking/v2/extensions/qos/ruletypes/doc.go index 5cfc6884c1..f46fe83879 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/doc.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/doc.go @@ -1,7 +1,7 @@ /* Package ruletypes contains functionality for working with Neutron 'quality of service' rule-type resources. -Example: You can list rule-types in the following way: +Example of Listing QoS rule types page, err := ruletypes.ListRuleTypes(client).AllPages() if err != nil { @@ -15,5 +15,15 @@ Example: You can list rule-types in the following way: fmt.Printf("%v <- Rule Types\n", rules) +Example of Getting a single QoS rule type by it's name + + ruleTypeName := "bandwidth_limit" + + ruleType, err := ruletypes.Get(networkClient, ruleTypeName).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", ruleTypeName) */ package ruletypes diff --git a/openstack/networking/v2/extensions/qos/ruletypes/requests.go b/openstack/networking/v2/extensions/qos/ruletypes/requests.go index e193cea5a2..61121f6b0e 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/requests.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/requests.go @@ -11,3 +11,9 @@ func ListRuleTypes(c *gophercloud.ServiceClient) (result pagination.Pager) { return ListRuleTypesPage{pagination.SinglePageBase(r)} }) } + +// GetRuleType retrieves a specific QoS RuleType based on its name. +func GetRuleType(c *gophercloud.ServiceClient, name string) (r GetResult) { + _, r.Err = c.Get(getRuleTypeURL(c, name), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/qos/ruletypes/results.go b/openstack/networking/v2/extensions/qos/ruletypes/results.go index f62818819e..a5cfe9d0be 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/results.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/results.go @@ -1,10 +1,45 @@ package ruletypes -import "github.com/gophercloud/gophercloud/pagination" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) -// The result of listing the qos rule types +type commonResult struct { + gophercloud.Result +} + +func (r commonResult) Extract() (*RuleType, error) { + var s struct { + RuleType *RuleType `json:"rule_type"` + } + err := r.ExtractInto(&s) + return s.RuleType, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a RuleType. +type GetResult struct { + commonResult +} + +// RuleType represents a single QoS rule type. type RuleType struct { - Type string `json:"type"` + Type string `json:"type"` + Drivers []Driver `json:"drivers"` +} + +// Driver represents a single QoS driver. +type Driver struct { + Name string `json:"name"` + SupportedParameters []SupportedParameter `json:"supported_parameters"` +} + +// SupportedParameter represents a single set of supported parameters for a some QoS driver's . +type SupportedParameter struct { + ParameterName string `json:"parameter_name"` + ParameterType string `json:"parameter_type"` + ParameterValues interface{} `json:"parameter_values"` } type ListRuleTypesPage struct { diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go index 63556695d6..066a5514a3 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/testing/fixtures.go @@ -15,5 +15,73 @@ const ( } ] } +` + + GetRuleTypeResponse = ` +{ + "rule_type": { + "drivers": [ + { + "name": "linuxbridge", + "supported_parameters": [ + { + "parameter_values": { + "start": 0, + "end": 2147483647 + }, + "parameter_type": "range", + "parameter_name": "max_kbps" + }, + { + "parameter_values": [ + "ingress", + "egress" + ], + "parameter_type": "choices", + "parameter_name": "direction" + }, + { + "parameter_values": { + "start": 0, + "end": 2147483647 + }, + "parameter_type": "range", + "parameter_name": "max_burst_kbps" + } + ] + }, + { + "name": "openvswitch", + "supported_parameters": [ + { + "parameter_values": { + "start": 0, + "end": 2147483647 + }, + "parameter_type": "range", + "parameter_name": "max_kbps" + }, + { + "parameter_values": [ + "ingress", + "egress" + ], + "parameter_type": "choices", + "parameter_name": "direction" + }, + { + "parameter_values": { + "start": 0, + "end": 2147483647 + }, + "parameter_type": "range", + "parameter_name": "max_burst_kbps" + } + ] + } + ], + "type": "bandwidth_limit" + } +} ` ) diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go index 79559efe1a..b27a9a93b6 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go @@ -39,3 +39,32 @@ func TestListRuleTypes(t *testing.T) { expected := []ruletypes.RuleType{{Type: "bandwidth_limit"}, {Type: "dscp_marking"}, {Type: "minimum_bandwidth"}} th.AssertDeepEquals(t, expected, rules) } + +func TestGetRuleType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/qos/rule-types/bandwidth_limit", 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) + + _, err := fmt.Fprintf(w, GetRuleTypeResponse) + th.AssertNoErr(t, err) + }) + + r, err := ruletypes.GetRuleType(fake.ServiceClient(), "bandwidth_limit").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "bandwidth_limit", r.Type) + + th.AssertEquals(t, 2, len(r.Drivers)) + + th.AssertEquals(t, "linuxbridge", r.Drivers[0].Name) + th.AssertEquals(t, 3, len(r.Drivers[0].SupportedParameters)) + + th.AssertEquals(t, "openvswitch", r.Drivers[1].Name) + th.AssertEquals(t, 3, len(r.Drivers[1].SupportedParameters)) +} diff --git a/openstack/networking/v2/extensions/qos/ruletypes/urls.go b/openstack/networking/v2/extensions/qos/ruletypes/urls.go index 15795100c2..a6f23ac744 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/urls.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func listRuleTypesURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("qos", "rule-types") } + +func getRuleTypeURL(c *gophercloud.ServiceClient, name string) string { + return c.ServiceURL("qos", "rule-types", name) +} From c3d856222a40eb5d3a317532b3624b18585d73f2 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 13 Jun 2019 16:06:16 +0300 Subject: [PATCH 0777/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6012d1291..83d6e9b25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ IMPROVEMENTS * Added `Hostname` to `baremetalintrospection/v1/introspection.Data` [GH-1617](https://github.com/gophercloud/gophercloud/pull/1617) * 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) BUG FIXES From fb199c55e58f1071ad4aca5063e98abcdf76b632 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 13 Jun 2019 19:50:22 -0600 Subject: [PATCH 0778/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d6e9b25b..7edd6a5223 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ IMPROVEMENTS * 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) BUG FIXES From be7c1c0751ed4e68a747f987c8f7b1ba2571e3d0 Mon Sep 17 00:00:00 2001 From: Pratyush Singhal Date: Sat, 15 Jun 2019 07:12:02 +0530 Subject: [PATCH 0779/2296] Adds support for setting image metadata for a volume (#1621) * feat: add support for setting image metadata to a volume Signed-off-by: Pratyush singhal * test: add test for volumeactions SetImageMetadata method Signed-off-by: Pratyush singhal * refactor: change SetImageMetadata to ImageMetadata Signed-off-by: Pratyush singhal * refactor: fix a typo in ImageMetadataOptsBuilder Signed-off-by: Pratyush singhal * test: add acceptance test for volumeactions.ImageMetadata Signed-off-by: Pratyush singhal * refactor: change volumeactions ImageMetadata action to SetImageMetadata Signed-off-by: Pratyush singhal --- .../blockstorage/extensions/extensions.go | 18 +++++++++++ .../extensions/volumeactions_test.go | 12 +++++++ .../extensions/volumeactions/requests.go | 31 +++++++++++++++++++ .../extensions/volumeactions/results.go | 6 ++++ .../volumeactions/testing/fixtures.go | 22 +++++++++++++ .../volumeactions/testing/requests_test.go | 16 ++++++++++ 6 files changed, 105 insertions(+) diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index 0be9e7ee31..797a127cc0 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -174,6 +174,24 @@ func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *v return nil } +// SetImageMetadata will apply the metadata to a volume. +func SetImageMetadata(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to apply image metadata to volume %s", volume.ID) + + imageMetadataOpts := volumeactions.ImageMetadataOpts{ + Metadata: map[string]string{ + "image_name": "testimage", + }, + } + + err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts).ExtractErr() + if err != nil { + return err + } + + return nil +} + // CreateBackup will create a backup based on a volume. An error will be // will be returned if the backup could not be created. func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID string) (*backups.Backup, error) { diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 9bd9e3cfd5..ec19cb8ea0 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -91,6 +91,18 @@ func TestVolumeActionsExtendSize(t *testing.T) { tools.PrintResource(t, newVolume) } +func TestVolumeActionsImageMetadata(t *testing.T) { + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + err = SetImageMetadata(t, blockClient, volume) + 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 d18bff555b..fdf322d3e9 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -267,3 +267,34 @@ func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteRes _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) return } + +// ImageMetadataOptsBuilder allows extensions to add additional parameters to the +// ImageMetadataRequest request. +type ImageMetadataOptsBuilder interface { + ToImageMetadataMap() (map[string]interface{}, error) +} + +// ImageMetadataOpts contains options for setting image metadata to a volume. +type ImageMetadataOpts struct { + // The image metadata to add to the volume as a set of metadata key and value pairs. + Metadata map[string]string `json:"metadata"` +} + +// ToImageMetadataMap assembles a request body based on the contents of a +// ImageMetadataOpts. +func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") +} + +// SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. +func SetImageMetadata(client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { + b, err := opts.ToImageMetadataMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index 5cadd360f2..aacee4c53b 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -29,6 +29,12 @@ type UploadImageResult struct { gophercloud.Result } +// SetImageMetadataResult contains the response body and error from an SetImageMetadata +// request. +type SetImageMetadataResult struct { + gophercloud.ErrResult +} + // ReserveResult contains the response body and error from a Reserve request. type ReserveResult struct { gophercloud.ErrResult diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go index 4a0c21701e..032b5809f8 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go @@ -285,3 +285,25 @@ func MockForceDeleteResponse(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +func MockSetImageMetadataResponse(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-set_image_metadata": { + "metadata": { + "label": "test" + } + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, `{}`) + }) +} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go index 38fae47889..c0919bb5ea 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go @@ -164,3 +164,19 @@ func TestForceDelete(t *testing.T) { res := volumeactions.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } + +func TestSetImageMetadata(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetImageMetadataResponse(t) + + options := &volumeactions.ImageMetadataOpts{ + Metadata: map[string]string{ + "label": "test", + }, + } + + err := volumeactions.SetImageMetadata(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} From 8a90230500337955d4f9e92370e6040c9e80a00d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 14 Jun 2019 19:42:56 -0600 Subject: [PATCH 0780/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edd6a5223..255c434955 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ IMPROVEMENTS * 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 From 68c40a3a1526113a638b7851c760fcbbcdfa1acd Mon Sep 17 00:00:00 2001 From: "Brad P. Crochet" Date: Mon, 17 Jun 2019 10:25:13 -0400 Subject: [PATCH 0781/2296] Baremetal Introspection: Move hostname to InventoryType (#1627) The IPA patch added the hostname to the Inventory field, not as a top-level field. This moves the hostname into the InventoryType where it belongs. --- .../v1/introspection/results.go | 2 +- .../v1/introspection/testing/fixtures.go | 4 +- .../v1/introspection/testing/results_test.go | 77 +++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 4b9ada1505..3e6055eff1 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -170,7 +170,6 @@ type Data struct { MACs []string `json:"macs"` MemoryMB int `json:"memory_mb"` RootDisk RootDiskType `json:"root_disk"` - Hostname string `json:"hostname"` Extra ExtraHardwareDataType `json:"extra"` } @@ -223,6 +222,7 @@ type InventoryType struct { Interfaces []InterfaceType `json:"interfaces"` Memory MemoryType `json:"memory"` SystemVendor SystemVendorType `json:"system_vendor"` + Hostname string `json:"hostname"` } type MemoryType struct { diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index 58cb166781..4c5f537665 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -162,7 +162,8 @@ const IntrospectionDataJSONSample = ` "sse2" ], "architecture":"x86_64" - } + }, + "hostname": "myawesomehost" }, "error":null, "local_gb":12, @@ -302,6 +303,7 @@ var ( PhysicalMb: 2048.0, Total: 2.105864192e+09, }, + Hostname: "myawesomehost", }, Error: "", LocalGB: 12, diff --git a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go index 84392ee30b..4717d961ba 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go @@ -87,3 +87,80 @@ func TestExtraHardware(t *testing.T) { t.Errorf("Failed to unmarshal ExtraHardware data: %s", err) } } + +func TestHostnameInInventory(t *testing.T) { + inventoryJson := `{ + "bmc_address":"192.167.2.134", + "interfaces":[ + { + "lldp":[], + "product":"0x0001", + "vendor":"0x1af4", + "name":"eth1", + "has_carrier":true, + "ipv4_address":"172.24.42.101", + "client_id":null, + "mac_address":"52:54:00:47:20:4d" + }, + { + "lldp": [ + [1, "04112233aabbcc"], + [5, "737730312d646973742d31622d623132"] + ], + "product":"0x0001", + "vendor":"0x1af4", + "name":"eth0", + "has_carrier":true, + "ipv4_address":"172.24.42.100", + "client_id":null, + "mac_address":"52:54:00:4e:3d:30" + } + ], + "disks":[ + { + "rotational":true, + "vendor":"0x1af4", + "name":"/dev/vda", + "hctl":null, + "wwn_vendor_extension":null, + "wwn_with_extension":null, + "model":"", + "wwn":null, + "serial":null, + "size":13958643712 + } + ], + "boot":{ + "current_boot_mode":"bios", + "pxe_interface":"52:54:00:4e:3d:30" + }, + "system_vendor":{ + "serial_number":"Not Specified", + "product_name":"Bochs", + "manufacturer":"Bochs" + }, + "memory":{ + "physical_mb":2048, + "total":2105864192 + }, + "cpu":{ + "count":2, + "frequency":"2100.084", + "flags": [ + "fpu", + "mmx", + "fxsr", + "sse", + "sse2" + ], + "architecture":"x86_64" + }, + "hostname": "master-0" + }` + + var output introspection.InventoryType + err := json.Unmarshal([]byte(inventoryJson), &output) + if err != nil { + t.Errorf("Failed to unmarshal Inventory data: %s", err) + } +} From 1727d5cd31c1f5b187f6f3f21d62641530343cd6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 17 Jun 2019 08:25:44 -0600 Subject: [PATCH 0782/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 255c434955..83ed8782c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ IMPROVEMENTS * 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-1617](https://github.com/gophercloud/gophercloud/pull/1617) +* 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) From 157432124aab520975efedb5e54b95287785578d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 17 Jun 2019 08:26:30 -0600 Subject: [PATCH 0783/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ed8782c8..2993e18519 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.2.0 (Unreleased) +## 0.3.0 (Unreleaesd) + +## 0.2.0 (June 17, 2019) IMPROVEMENTS From c0ec4eeeb319380c15a9b003404345850b871a7b Mon Sep 17 00:00:00 2001 From: Stephen Benjamin Date: Wed, 26 Jun 2019 16:04:13 -0400 Subject: [PATCH 0784/2296] Baremetal API: Return versions supported by the API (#1577) --- openstack/baremetal/apiversions/doc.go | 13 +++ openstack/baremetal/apiversions/requests.go | 17 +++ openstack/baremetal/apiversions/results.go | 62 ++++++++++ .../baremetal/apiversions/testing/fixtures.go | 106 ++++++++++++++++++ .../apiversions/testing/requests_test.go | 33 ++++++ openstack/baremetal/apiversions/urls.go | 13 +++ 6 files changed, 244 insertions(+) create mode 100644 openstack/baremetal/apiversions/doc.go create mode 100644 openstack/baremetal/apiversions/requests.go create mode 100644 openstack/baremetal/apiversions/results.go create mode 100644 openstack/baremetal/apiversions/testing/fixtures.go create mode 100644 openstack/baremetal/apiversions/testing/requests_test.go create mode 100644 openstack/baremetal/apiversions/urls.go diff --git a/openstack/baremetal/apiversions/doc.go b/openstack/baremetal/apiversions/doc.go new file mode 100644 index 0000000000..93b5adc885 --- /dev/null +++ b/openstack/baremetal/apiversions/doc.go @@ -0,0 +1,13 @@ +/* +Package apiversions provides information about the versions supported by a specific Ironic API. + + Example to list versions + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + + Example to get a specific version + + actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() + +*/ +package apiversions diff --git a/openstack/baremetal/apiversions/requests.go b/openstack/baremetal/apiversions/requests.go new file mode 100644 index 0000000000..3c586e6b9f --- /dev/null +++ b/openstack/baremetal/apiversions/requests.go @@ -0,0 +1,17 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" +) + +// List lists all the API versions available to end users. +func List(client *gophercloud.ServiceClient) (r ListResult) { + _, r.Err = client.Get(listURL(client), &r.Body, nil) + return +} + +// Get will get a specific API version, specified by major ID. +func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { + _, r.Err = client.Get(getURL(client, v), &r.Body, nil) + return +} diff --git a/openstack/baremetal/apiversions/results.go b/openstack/baremetal/apiversions/results.go new file mode 100644 index 0000000000..982758000d --- /dev/null +++ b/openstack/baremetal/apiversions/results.go @@ -0,0 +1,62 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" +) + +// APIVersions represents the result from getting a list of all versions available +type APIVersions struct { + DefaultVersion APIVersion `json:"default_version"` + Versions []APIVersion `json:"versions"` +} + +// APIVersion represents an API version for Ironic +type APIVersion struct { + // ID is the unique identifier of the API version. + ID string `json:"id"` + + // MinVersion is the minimum microversion supported. + MinVersion string `json:"min_version"` + + // Status is the API versions status. + Status string `json:"status"` + + // Version is the maximum microversion supported. + Version string `json:"version"` +} + +// GetResult represents the result of a get operation. +type GetResult struct { + gophercloud.Result +} + +// ListResult represents the result of a list operation. +type ListResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a get result and extracts an API version resource. +func (r GetResult) Extract() (*APIVersion, error) { + var s struct { + Version APIVersion `json:"version"` + } + + err := r.ExtractInto(&s) + if err != nil { + return nil, err + } + + return &s.Version, nil +} + +// Extract is a function that accepts a list result and extracts an APIVersions resource +func (r ListResult) Extract() (*APIVersions, error) { + var version APIVersions + + err := r.ExtractInto(&version) + if err != nil { + return nil, err + } + + return &version, nil +} diff --git a/openstack/baremetal/apiversions/testing/fixtures.go b/openstack/baremetal/apiversions/testing/fixtures.go new file mode 100644 index 0000000000..1b9b201717 --- /dev/null +++ b/openstack/baremetal/apiversions/testing/fixtures.go @@ -0,0 +1,106 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const IronicAPIAllVersionResponse = ` +{ + "default_version": { + "status": "CURRENT", + "min_version": "1.1", + "version": "1.56", + "id": "v1", + "links": [ + { + "href": "http://localhost:6385/v1/", + "rel": "self" + } + ] + }, + "versions": [ + { + "status": "CURRENT", + "min_version": "1.1", + "version": "1.56", + "id": "v1", + "links": [ + { + "href": "http://localhost:6385/v1/", + "rel": "self" + } + ] + } + ], + "name": "OpenStack Ironic API", + "description": "Ironic is an OpenStack project which aims to provision baremetal machines." +} +` + +const IronicAPIVersionResponse = ` +{ + "media_types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.ironic.v1+json" + } + ], + "version": { + "status": "CURRENT", + "min_version": "1.1", + "version": "1.56", + "id": "v1", + "links": [ + { + "href": "http://localhost:6385/v1/", + "rel": "self" + } + ] + }, + "id": "v1" +} +` + +var IronicAPIVersion1Result = apiversions.APIVersion{ + ID: "v1", + Status: "CURRENT", + MinVersion: "1.1", + Version: "1.56", +} + +var IronicAllAPIVersionResults = apiversions.APIVersions{ + DefaultVersion: IronicAPIVersion1Result, + Versions: []apiversions.APIVersion{ + IronicAPIVersion1Result, + }, +} + +func MockListResponse(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", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, IronicAPIAllVersionResponse) + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/v1/", 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, IronicAPIVersionResponse) + }) +} diff --git a/openstack/baremetal/apiversions/testing/requests_test.go b/openstack/baremetal/apiversions/testing/requests_test.go new file mode 100644 index 0000000000..5b2b3a6505 --- /dev/null +++ b/openstack/baremetal/apiversions/testing/requests_test.go @@ -0,0 +1,33 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAPIVersions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + actual, err := apiversions.List(client.ServiceClient()).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, IronicAllAPIVersionResults, *actual) +} + +func TestGetAPIVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, IronicAPIVersion1Result, *actual) +} diff --git a/openstack/baremetal/apiversions/urls.go b/openstack/baremetal/apiversions/urls.go new file mode 100644 index 0000000000..5c101310ac --- /dev/null +++ b/openstack/baremetal/apiversions/urls.go @@ -0,0 +1,13 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" +) + +func getURL(c *gophercloud.ServiceClient, version string) string { + return c.ServiceURL(version) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL() +} From 2949719e825824850b54d993d4654feb0b6dd9df Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 26 Jun 2019 14:15:51 -0600 Subject: [PATCH 0785/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2993e18519..1f457a52b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## 0.3.0 (Unreleaesd) +* Added `openstack/baremetal/apiversions.List` [GH-1577] +* Added `openstack/baremetal/apiversions.Get` [GH-1577] + ## 0.2.0 (June 17, 2019) IMPROVEMENTS From 85ccb4654a4f4d793c6ba2add05542c8a53a541b Mon Sep 17 00:00:00 2001 From: LongKB Date: Fri, 5 Jul 2019 02:58:30 +0700 Subject: [PATCH 0786/2296] Update CleanStep struct to support manual cleaning (#1638) Currently, the **Args** property in CleanStep struct has **map[string]string that does not match with manual cleaning steps in Ironic. It should be dictionary in python[1][2] and interface{} in golang. For example: BIOS setting for a node: ``` [ { "interface": "bios", "step": "apply_configuration", "args": { "settings": [{"name": "hyper_threading_enabled", "value": "false"}] } } ] ``` This PR aims to update **Args** in **CleanStep** struct. [1] https://github.com/openstack/ironic/blob/master/ironic/api/controllers/v1/node.py#L76 [2] https://github.com/openstack/ironic/blob/master/ironic/api/controllers/v1/node.py#L624 Signed-off-by: Kim Bao Long --- openstack/baremetal/v1/nodes/requests.go | 6 +++--- openstack/baremetal/v1/nodes/testing/requests_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 4707f7b250..0de1e66060 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -388,9 +388,9 @@ func GetSupportedBootDevices(client *gophercloud.ServiceClient, id string) (r Su // the value for ‘args’ is a keyword variable argument dictionary that is passed to the cleaning step // method. type CleanStep struct { - Interface string `json:"interface" required:"true"` - Step string `json:"step" required:"true"` - Args map[string]string `json:"args,omitempty"` + Interface string `json:"interface" required:"true"` + Step string `json:"step" required:"true"` + Args map[string]interface{} `json:"args,omitempty"` } // ProvisionStateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 20c578895d..481f37990a 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -282,7 +282,7 @@ func TestNodeChangeProvisionStateClean(t *testing.T) { { Interface: "deploy", Step: "upgrade_firmware", - Args: map[string]string{ + Args: map[string]interface{}{ "force": "True", }, }, @@ -304,7 +304,7 @@ func TestNodeChangeProvisionStateCleanWithConflict(t *testing.T) { { Interface: "deploy", Step: "upgrade_firmware", - Args: map[string]string{ + Args: map[string]interface{}{ "force": "True", }, }, @@ -323,7 +323,7 @@ func TestCleanStepRequiresInterface(t *testing.T) { CleanSteps: []nodes.CleanStep{ { Step: "upgrade_firmware", - Args: map[string]string{ + Args: map[string]interface{}{ "force": "True", }, }, @@ -342,7 +342,7 @@ func TestCleanStepRequiresStep(t *testing.T) { CleanSteps: []nodes.CleanStep{ { Interface: "deploy", - Args: map[string]string{ + Args: map[string]interface{}{ "force": "True", }, }, From a26f5d964f8449ff58831bbd52395f00aada4391 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 4 Jul 2019 15:59:58 -0400 Subject: [PATCH 0787/2296] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f457a52b1..6291b5cd7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ ## 0.3.0 (Unreleaesd) +IMPROVEMENTS + * Added `openstack/baremetal/apiversions.List` [GH-1577] * Added `openstack/baremetal/apiversions.Get` [GH-1577] +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) + ## 0.2.0 (June 17, 2019) IMPROVEMENTS From 3b05c006441c40ed2ed2654b04913f0b18343a52 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 9 Jul 2019 14:36:12 +0300 Subject: [PATCH 0788/2296] Compute: "policy", "rules" add servergroups Create (#1636) * Compute V2: "policy", "rules" for servergroups GET Add "policy" and "rules" extract methods to servergroups package. Those methods are available in 2.64 microversion. * Compute: "policy", "rules" add servergroups Create Add "policy" and "rules" fields for servergroups Create call. Those fields are available in 2.64 microversion. Remove required attribute from the "policies" field. Add "omitempty" attribute to the Rules.MaxServerPerHost field. * Compute V2: fix servergroups rules and tests Use pointer to Rules in servergroups create options to prevent its addition to create options without microversion. Update servergroups package tests to test for cases with and without microversion 2.64. --- acceptance/openstack/compute/v2/compute.go | 28 +++++++ .../openstack/compute/v2/servergroup_test.go | 32 ++++++++ .../compute/v2/extensions/servergroups/doc.go | 46 +++++++++++ .../extensions/servergroups/microversions.go | 32 ++++++++ .../v2/extensions/servergroups/requests.go | 14 +++- .../v2/extensions/servergroups/results.go | 2 + .../servergroups/testing/fixtures.go | 76 +++++++++++++++++++ .../servergroups/testing/requests_test.go | 51 +++++++++++++ 8 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 openstack/compute/v2/extensions/servergroups/microversions.go diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 72e641060e..d99b4d5c6f 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -614,6 +614,34 @@ func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy s return sg, nil } +// CreateServerGroupMicroversion will create a server with a random name using 2.64 microversion. An error will be +// returned if the server group failed to be created. +func CreateServerGroupMicroversion(t *testing.T, client *gophercloud.ServiceClient) (*servergroups.ServerGroup, error) { + name := tools.RandomString("ACPTTEST", 16) + policy := "anti-affinity" + maxServerPerHost := 3 + + t.Logf("Attempting to create %s server group with max server per host = %d: %s", policy, maxServerPerHost, name) + + sg, err := servergroups.Create(client, &servergroups.CreateOpts{ + Name: name, + Policy: policy, + Rules: &servergroups.Rules{ + MaxServerPerHost: maxServerPerHost, + }, + }).Extract() + + if err != nil { + return nil, err + } + + t.Logf("Successfully created server group %s", name) + + th.AssertEquals(t, sg.Name, name) + + return sg, nil +} + // CreateServerInServerGroup works like CreateServer but places the instance in // a specified Server Group. func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) (*servers.Server, error) { diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/acceptance/openstack/compute/v2/servergroup_test.go index 8b7af0f3c1..4b7899e766 100644 --- a/acceptance/openstack/compute/v2/servergroup_test.go +++ b/acceptance/openstack/compute/v2/servergroup_test.go @@ -69,3 +69,35 @@ func TestServergroupsAffinityPolicy(t *testing.T) { th.AssertEquals(t, firstServer.HostID, secondServer.HostID) } + +func TestServergroupsMicroversionCreateDelete(t *testing.T) { + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + client.Microversion = "2.64" + serverGroup, err := CreateServerGroupMicroversion(t, client) + th.AssertNoErr(t, err) + defer DeleteServerGroup(t, client, serverGroup) + + serverGroup, err = servergroups.Get(client, serverGroup.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, serverGroup) + + allPages, err := servergroups.List(client).AllPages() + th.AssertNoErr(t, err) + + allServerGroups, err := servergroups.ExtractServerGroups(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, sg := range allServerGroups { + tools.PrintResource(t, serverGroup) + + if sg.ID == serverGroup.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/openstack/compute/v2/extensions/servergroups/doc.go b/openstack/compute/v2/extensions/servergroups/doc.go index 814bde37f3..268ef77c81 100644 --- a/openstack/compute/v2/extensions/servergroups/doc.go +++ b/openstack/compute/v2/extensions/servergroups/doc.go @@ -29,6 +29,34 @@ Example to Create a Server Group panic(err) } +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) + } + + policy, err := servergroups.ExtractPolicy(result.Result) + if err != nil { + panic(err) + } + + rules, err := servergroups.ExtractRules(result.Result) + if err != nil { + panic(err) + } + Example to Delete a Server Group sgID := "7a6f29ad-e34d-4368-951a-58a08f11cfb7" @@ -36,5 +64,23 @@ Example to Delete a Server Group if err != nil { panic(err) } + +Example to get additional fields with microversion 2.64 or later + + computeClient.Microversion = "2.64" + result := servergroups.Get(computeClient, "616fb98f-46ca-475e-917e-2563e5a8cd19") + + policy, err := servergroups.ExtractPolicy(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("Policy: %s\n", policy) + + rules, err := servergroups.ExtractRules(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("Max server per host: %s\n", rules.MaxServerPerHost) + */ package servergroups diff --git a/openstack/compute/v2/extensions/servergroups/microversions.go b/openstack/compute/v2/extensions/servergroups/microversions.go new file mode 100644 index 0000000000..4899d9c572 --- /dev/null +++ b/openstack/compute/v2/extensions/servergroups/microversions.go @@ -0,0 +1,32 @@ +package servergroups + +import "github.com/gophercloud/gophercloud" + +// ExtractPolicy will extract the policy attribute. +// This requires the client to be set to microversion 2.64 or later. +func ExtractPolicy(r gophercloud.Result) (string, error) { + var s struct { + Policy string `json:"policy"` + } + err := r.ExtractIntoStructPtr(&s, "server_group") + + return s.Policy, err +} + +// ExtractRules will extract the rules attribute. +// This requires the client to be set to microversion 2.64 or later. +func ExtractRules(r gophercloud.Result) (Rules, error) { + var s struct { + Rules Rules `json:"rules"` + } + err := r.ExtractIntoStructPtr(&s, "server_group") + + return s.Rules, err +} + +// Rules represents set of rules for a policy. +type Rules struct { + // MaxServerPerHost specifies how many servers can reside on a single compute host. + // It can be used only with the "anti-affinity" policy. + MaxServerPerHost int `json:"max_server_per_host,omitempty"` +} diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go index 1439a5a34c..5740afaf05 100644 --- a/openstack/compute/v2/extensions/servergroups/requests.go +++ b/openstack/compute/v2/extensions/servergroups/requests.go @@ -21,11 +21,19 @@ type CreateOptsBuilder interface { // CreateOpts specifies Server Group creation parameters. type CreateOpts struct { - // Name is the name of the server group + // Name is the name of the server group. Name string `json:"name" required:"true"` - // Policies are the server group policies - Policies []string `json:"policies" required:"true"` + // Policies are the server group policies. + Policies []string `json:"policies,omitempty"` + + // Policy specifies the name of a policy. + // Requires microversion 2.64 or later. + Policy string `json:"policy,omitempty"` + + // Rules specifies the set of rules. + // Requires microversion 2.64 or later. + Rules *Rules `json:"rules,omitempty"` } // ToServerGroupCreateMap constructs a request body from CreateOpts. diff --git a/openstack/compute/v2/extensions/servergroups/results.go b/openstack/compute/v2/extensions/servergroups/results.go index b9aeef9815..fd6f2c6c76 100644 --- a/openstack/compute/v2/extensions/servergroups/results.go +++ b/openstack/compute/v2/extensions/servergroups/results.go @@ -6,6 +6,8 @@ import ( ) // A ServerGroup creates a policy for instance placement in the cloud. +// You should use extract methods from microversions.go to retrieve additional +// fields. type ServerGroup struct { // ID is the unique ID of the Server Group. ID string `json:"id"` diff --git a/openstack/compute/v2/extensions/servergroups/testing/fixtures.go b/openstack/compute/v2/extensions/servergroups/testing/fixtures.go index b53757a40f..fc4a4c7bd7 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/fixtures.go +++ b/openstack/compute/v2/extensions/servergroups/testing/fixtures.go @@ -51,6 +51,25 @@ const GetOutput = ` } ` +// GetOutputMicroversion is a sample response to a Get call with microversion set to 2.64 +const GetOutputMicroversion = ` +{ + "server_group": { + "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", + "name": "test", + "policies": [ + "anti-affinity" + ], + "policy": "anti-affinity", + "rules": { + "max_server_per_host": 3 + }, + "members": [], + "metadata": {} + } +} +` + // CreateOutput is a sample response to a Post call const CreateOutput = ` { @@ -66,6 +85,25 @@ const CreateOutput = ` } ` +// CreateOutputMicroversion is a sample response to a Post call with microversion set to 2.64 +const CreateOutputMicroversion = ` +{ + "server_group": { + "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", + "name": "test", + "policies": [ + "anti-affinity" + ], + "policy": "anti-affinity", + "rules": { + "max_server_per_host": 3 + }, + "members": [], + "metadata": {} + } +} +` + // FirstServerGroup is the first result in ListOutput. var FirstServerGroup = servergroups.ServerGroup{ ID: "616fb98f-46ca-475e-917e-2563e5a8cd19", @@ -126,6 +164,18 @@ func HandleGetSuccessfully(t *testing.T) { }) } +// HandleGetMicroversionSuccessfully configures the test server to respond to a Get request +// for an existing server group with microversion set to 2.64 +func HandleGetMicroversionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-server-groups/4d8c3732-a248-40ed-bebc-539a6ffd25c0", 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, GetOutputMicroversion) + }) +} + // HandleCreateSuccessfully configures the test server to respond to a Create request // for a new server group func HandleCreateSuccessfully(t *testing.T) { @@ -148,6 +198,32 @@ func HandleCreateSuccessfully(t *testing.T) { }) } +// HandleCreateMicroversionSuccessfully configures the test server to respond to a Create request +// for a new server group with microversion set to 2.64 +func HandleCreateMicroversionSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-server-groups", 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, ` +{ + "server_group": { + "name": "test", + "policies": [ + "anti-affinity" + ], + "policy": "anti-affinity", + "rules": { + "max_server_per_host": 3 + } + } +} +`) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, CreateOutputMicroversion) + }) +} + // HandleDeleteSuccessfully configures the test server to respond to a Delete request for a // an existing server group func HandleDeleteSuccessfully(t *testing.T) { diff --git a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go index d86fa56861..bb37b89752 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go +++ b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go @@ -40,6 +40,35 @@ func TestCreate(t *testing.T) { th.CheckDeepEquals(t, &CreatedServerGroup, actual) } +func TestCreateMicroversion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateMicroversionSuccessfully(t) + + result := servergroups.Create(client.ServiceClient(), servergroups.CreateOpts{ + Name: "test", + Policies: []string{"anti-affinity"}, + Policy: "anti-affinity", + Rules: &servergroups.Rules{ + MaxServerPerHost: 3, + }, + }) + + // Extract basic fields. + actual, err := result.Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &CreatedServerGroup, actual) + + // Extract additional fields. + policy, err := servergroups.ExtractPolicy(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, "anti-affinity", policy) + + rules, err := servergroups.ExtractRules(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, 3, rules.MaxServerPerHost) +} + func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -50,6 +79,28 @@ func TestGet(t *testing.T) { th.CheckDeepEquals(t, &FirstServerGroup, actual) } +func TestGetMicroversion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetMicroversionSuccessfully(t) + + result := servergroups.Get(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0") + + // Extract basic fields. + actual, err := result.Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &FirstServerGroup, actual) + + // Extract additional fields. + policy, err := servergroups.ExtractPolicy(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, "anti-affinity", policy) + + rules, err := servergroups.ExtractRules(result.Result) + th.AssertNoErr(t, err) + th.AssertEquals(t, 3, rules.MaxServerPerHost) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 2d40ff7a3874e2b376ff245d549e7757048cd65c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 9 Jul 2019 21:14:24 -0400 Subject: [PATCH 0789/2296] Update doc.go --- .../compute/v2/extensions/servergroups/doc.go | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/openstack/compute/v2/extensions/servergroups/doc.go b/openstack/compute/v2/extensions/servergroups/doc.go index 268ef77c81..7811d32ecf 100644 --- a/openstack/compute/v2/extensions/servergroups/doc.go +++ b/openstack/compute/v2/extensions/servergroups/doc.go @@ -31,31 +31,31 @@ Example to Create a Server Group Example to Create a Server Group with additional microversion 2.64 fields - createOpts := servergroups.CreateOpts{ + createOpts := servergroups.CreateOpts{ Name: "my_sg", Policy: "anti-affinity", - Rules: &servergroups.Rules{ - MaxServerPerHost: 3, - }, + Rules: &servergroups.Rules{ + MaxServerPerHost: 3, + }, } - computeClient.Microversion = "2.64" + computeClient.Microversion = "2.64" result := servergroups.Create(computeClient, createOpts) - serverGroup, err := result.Extract() - if err != nil { + serverGroup, err := result.Extract() + if err != nil { panic(err) } - policy, err := servergroups.ExtractPolicy(result.Result) - if err != nil { - panic(err) - } + policy, err := servergroups.ExtractPolicy(result.Result) + if err != nil { + panic(err) + } - rules, err := servergroups.ExtractRules(result.Result) - if err != nil { - panic(err) - } + rules, err := servergroups.ExtractRules(result.Result) + if err != nil { + panic(err) + } Example to Delete a Server Group @@ -67,20 +67,20 @@ Example to Delete a Server Group Example to get additional fields with microversion 2.64 or later - computeClient.Microversion = "2.64" - result := servergroups.Get(computeClient, "616fb98f-46ca-475e-917e-2563e5a8cd19") + computeClient.Microversion = "2.64" + result := servergroups.Get(computeClient, "616fb98f-46ca-475e-917e-2563e5a8cd19") - policy, err := servergroups.ExtractPolicy(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("Policy: %s\n", policy) + policy, err := servergroups.ExtractPolicy(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("Policy: %s\n", policy) - rules, err := servergroups.ExtractRules(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("Max server per host: %s\n", rules.MaxServerPerHost) + rules, err := servergroups.ExtractRules(result.Result) + if err != nil { + panic(err) + } + fmt.Printf("Max server per host: %s\n", rules.MaxServerPerHost) */ package servergroups From cf12c3b6673548de0992d655149f82e27d1cef08 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 11 Jul 2019 15:25:33 -0400 Subject: [PATCH 0790/2296] Load Balancer v2: Remove Health Monitor Validation (#1640) Octavia now supports defaults for when the url_path and expected_codes are not supplied in the request body. This commit removes the local validation for these fields to allow the API defaults to be used. Users working with earlier versions of Octavia will need to be mindful that they cannot rely on defaults being provided by the API and therefore must provide explicit values. --- .../loadbalancer/v2/monitors/requests.go | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index f728f5a823..aaddc2f062 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -108,7 +108,6 @@ type CreateOpts struct { MaxRetries int `json:"max_retries" required:"true"` // URI path that will be accessed if Monitor type is HTTP or HTTPS. - // Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` // The HTTP method used for requests by the Monitor. If this attribute @@ -116,8 +115,7 @@ type CreateOpts struct { HTTPMethod string `json:"http_method,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. + // a single status like "200", or a range like "200-202". ExpectedCodes string `json:"expected_codes,omitempty"` // TenantID is the UUID of the project who owns the Monitor. @@ -138,24 +136,7 @@ type CreateOpts struct { // ToMonitorCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "healthmonitor") - if err != nil { - return nil, err - } - - switch opts.Type { - case TypeHTTP, TypeHTTPS: - switch opts.URLPath { - case "": - return nil, fmt.Errorf("URLPath must be provided for HTTP and HTTPS") - } - switch opts.ExpectedCodes { - case "": - return nil, fmt.Errorf("ExpectedCodes must be provided for HTTP and HTTPS") - } - } - - return b, nil + return gophercloud.BuildRequestBody(opts, "healthmonitor") } /* From 20ac60d04cad27f3eb276259085a60caef82035f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 11 Jul 2019 15:26:23 -0400 Subject: [PATCH 0791/2296] Update requests.go --- openstack/loadbalancer/v2/monitors/requests.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index aaddc2f062..c05289504c 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -115,7 +115,8 @@ type CreateOpts struct { HTTPMethod string `json:"http_method,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". + // a single status like "200", a range like "200-202", or a combination like + // "200-202, 401". ExpectedCodes string `json:"expected_codes,omitempty"` // TenantID is the UUID of the project who owns the Monitor. From 17584a22adf89c48eb1f2518e71326b3b01ba573 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 11 Jul 2019 15:30:54 -0400 Subject: [PATCH 0792/2296] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6291b5cd7a..bb6fcd4cc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,15 @@ IMPROVEMENTS -* Added `openstack/baremetal/apiversions.List` [GH-1577] -* Added `openstack/baremetal/apiversions.Get` [GH-1577] +* Added `openstack/baremetal/apiversions.List` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) +* Added `openstack/baremetal/apiversions.Get` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) +* Added `openstack/compute/v2/extensions/servergroups.CreateOpts.Policy` [GH-1636](https://github.com/gophercloud/gophercloud/pull/1636) 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 `openstack/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) From 00c1d0b31215aa8ee476f9df2fa7f000cfe5130f Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 16 Jul 2019 08:03:14 +0300 Subject: [PATCH 0793/2296] Identity V3: add Trust Delete function (#1644) * Identity V3: add Trust Create and Extract funcions Implement Create and Extract functions for v3/extenstions/trusts package with unit tests and docs. This commit also adds Role struct to this package. * Identity V3: add Trust Delete function Implement Delete function for the v3/extensions/trusts package with tests and docs. --- .../identity/v3/extensions/trusts/doc.go | 32 ++++++++ .../identity/v3/extensions/trusts/requests.go | 80 ++++++++++++++++++- .../identity/v3/extensions/trusts/results.go | 33 ++++++++ .../v3/extensions/trusts/testing/fixtures.go | 68 ++++++++++++++++ .../trusts/testing/requests_test.go | 35 ++++++++ .../identity/v3/extensions/trusts/urls.go | 21 +++++ 6 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 openstack/identity/v3/extensions/trusts/urls.go diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go index 8db7724f2b..7c1f2c9243 100644 --- a/openstack/identity/v3/extensions/trusts/doc.go +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -22,5 +22,37 @@ Example to Create a Token with Username, Password, and Trust ID if err != nil { panic(err) } + +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) + +Example to Delete a Trust + + trustID := "3422b7c113894f5d90665e1a79655e23" + err := trusts.Delete(identityClient, trustID).ExtractErr() + if err != nil { + panic(err) + } */ package trusts diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index 438fba61de..5f2a4ee711 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -1,6 +1,11 @@ package trusts -import "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" +) // AuthOptsExt extends the base Identity v3 tokens AuthOpts with a TrustID. type AuthOptsExt struct { @@ -37,3 +42,76 @@ func (opts AuthOptsExt) ToTokenV3ScopeMap() (map[string]interface{}, error) { func (opts AuthOptsExt) CanReauth() bool { return opts.AuthOptionsBuilder.CanReauth() } + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToTrustCreateMap() (map[string]interface{}, error) +} + +// CreateOpts provides options used to create a new trust. +type CreateOpts struct { + // Impersonation allows the trustee to impersonate the trustor. + Impersonation bool `json:"impersonation" required:"true"` + + // TrusteeUserID is a user who is capable of consuming the trust. + TrusteeUserID string `json:"trustee_user_id" required:"true"` + + // TrustorUserID is a user who created the trust. + TrustorUserID string `json:"trustor_user_id" required:"true"` + + // AllowRedelegation enables redelegation of a trust. + AllowRedelegation bool `json:"allow_redelegation,omitempty"` + + // ExpiresAt sets expiration time on trust. + ExpiresAt *time.Time `json:"-"` + + // ProjectID identifies the project. + ProjectID string `json:"project_id,omitempty"` + + // RedelegationCount specifies a depth of the redelegation chain. + RedelegationCount int `json:"redelegation_count,omitempty"` + + // RemainingUses specifies how many times a trust can be used to get a token. + RemainingUses int `json:"remaining_uses,omitempty"` + + // Roles specifies roles that need to be granted to trustee. + Roles []Role `json:"roles,omitempty"` +} + +// ToTrustCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToTrustCreateMap() (map[string]interface{}, error) { + parent := "trust" + + b, err := gophercloud.BuildRequestBody(opts, parent) + if err != nil { + return nil, err + } + + if opts.ExpiresAt != nil { + if v, ok := b[parent].(map[string]interface{}); ok { + v["expires_at"] = opts.ExpiresAt.Format(gophercloud.RFC3339Milli) + } + } + + return b, nil +} + +// Create creates a new Trust. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTrustCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// Delete deletes a trust. +func Delete(client *gophercloud.ServiceClient, trustID string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, trustID), nil) + return +} diff --git a/openstack/identity/v3/extensions/trusts/results.go b/openstack/identity/v3/extensions/trusts/results.go index e6912e612c..fa60b88de7 100644 --- a/openstack/identity/v3/extensions/trusts/results.go +++ b/openstack/identity/v3/extensions/trusts/results.go @@ -1,5 +1,32 @@ package trusts +import "github.com/gophercloud/gophercloud" + +type trustResult struct { + gophercloud.Result +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a Trust. +type CreateResult struct { + trustResult +} + +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// Extract interprets any trust result as a Trust. +func (r trustResult) Extract() (*Trust, error) { + var s struct { + Trust *Trust `json:"trust"` + } + err := r.ExtractInto(&s) + return s.Trust, err +} + // TrusteeUser represents the trusted user ID of a trust. type TrusteeUser struct { ID string `json:"id"` @@ -21,6 +48,12 @@ type Trust struct { RedelegationCount int `json:"redelegation_count"` } +// Role specifies a single role that is granted to a trustee. +type Role struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + // TokenExt represents an extension of the base token result. type TokenExt struct { Trust Trust `json:"OS-TRUST:trust"` diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures.go b/openstack/identity/v3/extensions/trusts/testing/fixtures.go index e3115264b0..a1e8984a55 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures.go +++ b/openstack/identity/v3/extensions/trusts/testing/fixtures.go @@ -7,8 +7,51 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" ) +const CreateRequest = ` +{ + "trust": { + "expires_at": "2019-12-01T14:00:00.999999Z", + "impersonation": true, + "allow_redelegation": true, + "project_id": "9b71012f5a4a4aef9193f1995fe159b2", + "roles": [ + { + "name": "member" + } + ], + "trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf", + "trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3" + } +} +` + +const CreateResponse = ` +{ + "trust": { + "expires_at": "2019-12-01T14:00:00.999999Z", + "id": "3422b7c113894f5d90665e1a79655e23", + "impersonation": true, + "redelegation_count": 10, + "project_id": "9b71012f5a4a4aef9193f1995fe159b2", + "remaining_uses": null, + "roles": [ + { + "id": "b627fca5-beb0-471a-9857-0e852b719e76", + "links": { + "self": "http://example.com/identity/v3/roles/b627fca5-beb0-471a-9857-0e852b719e76" + }, + "name": "member" + } + ], + "trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf", + "trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3" + } +} +` + // HandleCreateTokenWithTrustID verifies that providing certain AuthOptions and Scope results in an expected JSON structure. func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilder, requestJSON string) { testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { @@ -65,3 +108,28 @@ func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilde }`) }) } + +// HandleCreateTrust creates an HTTP handler at `/OS-TRUST/trusts` on the +// test handler mux that tests trust creation. +func HandleCreateTrust(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "POST") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + testhelper.TestJSONRequest(t, r, CreateRequest) + + w.WriteHeader(http.StatusCreated) + _, err := fmt.Fprintf(w, CreateResponse) + testhelper.AssertNoErr(t, err) + }) +} + +// HandleDeleteUserSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that tests user deletion. +func HandleDeleteTrust(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-TRUST/trusts/3422b7c113894f5d90665e1a79655e23", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "DELETE") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index f8a65adb5c..5c24e2cde5 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -72,3 +72,38 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) { th.AssertDeepEquals(t, expected, actual) } + +func TestCreateTrust(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateTrust(t) + + expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC) + result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{ + ExpiresAt: &expiresAt, + Impersonation: true, + AllowRedelegation: true, + ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", + Roles: []trusts.Role{ + { + Name: "member", + }, + }, + TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", + TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "3422b7c113894f5d90665e1a79655e23", result.ID) + th.AssertEquals(t, true, result.Impersonation) + th.AssertEquals(t, 10, result.RedelegationCount) +} + +func TestDeleteTrust(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteTrust(t) + + res := trusts.Delete(client.ServiceClient(), "3422b7c113894f5d90665e1a79655e23") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/extensions/trusts/urls.go b/openstack/identity/v3/extensions/trusts/urls.go new file mode 100644 index 0000000000..d17509b545 --- /dev/null +++ b/openstack/identity/v3/extensions/trusts/urls.go @@ -0,0 +1,21 @@ +package trusts + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "OS-TRUST/trusts" + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} + +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} From bb5679b7e4e2d2c89cf5274bc10f5224df7ffd99 Mon Sep 17 00:00:00 2001 From: naumvd95 Date: Fri, 26 Jul 2019 00:26:49 +0200 Subject: [PATCH 0794/2296] [Neutron][FloatingIp] updated_at/created_at fields (#1647) * time.Time object * mapped to existing json fields related issue: https://github.com/gophercloud/gophercloud/issues/1646 --- .../extensions/dns/testing/requests_test.go | 15 +++++++++++ .../extensions/layer3/floatingips/results.go | 7 +++++ .../layer3/floatingips/testing/fixtures.go | 4 +++ .../floatingips/testing/requests_test.go | 26 +++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go index 2bcb7cba8c..ea76e5939d 100644 --- a/openstack/networking/v2/extensions/dns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/dns/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/dns" @@ -225,12 +226,19 @@ func TestFloatingIPGet(t *testing.T) { err := floatingips.Get(fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").ExtractInto(&actual) th.AssertNoErr(t, err) + createdTime, err := time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") + th.AssertNoErr(t, err) + updatedTime, err := time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") + th.AssertNoErr(t, err) + expected := FloatingIPDNS{ FloatingIP: floatingips.FloatingIP{ FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", + Created: createdTime, + Updated: updatedTime, Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", @@ -266,12 +274,19 @@ func TestFloatingIPCreate(t *testing.T) { err := floatingips.Create(fake.ServiceClient(), options).ExtractInto(&actual) th.AssertNoErr(t, err) + createdTime, err := time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") + th.AssertNoErr(t, err) + updatedTime, err := time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") + th.AssertNoErr(t, err) + expected := FloatingIPDNS{ FloatingIP: floatingips.FloatingIP{ FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", + Created: createdTime, + Updated: updatedTime, Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index a9709ccec3..c9c05629f0 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -1,6 +1,8 @@ package floatingips import ( + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -37,6 +39,11 @@ type FloatingIP struct { // specify a project identifier other than its own. TenantID string `json:"tenant_id"` + // Updated and Created contain ISO-8601 timestamps of when the state of the + // floating ip last changed, and when it was created. + Updated time.Time `json:"updated_at"` + Created time.Time `json:"created_at"` + // ProjectID is the project owner of the floating IP. ProjectID string `json:"project_id"` diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go index 0f665462ab..97c38cc0b8 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go @@ -10,6 +10,8 @@ const FipDNS = `{ "fixed_ip_address": null, "floating_ip_address": "192.0.0.4", "tenant_id": "017d8de156df4177889f31a9bd6edc00", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "status": "DOWN", "port_id": null, "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", @@ -24,6 +26,8 @@ const FipNoDNS = `{ "fixed_ip_address": "192.0.0.2", "floating_ip_address": "10.0.0.3", "tenant_id": "017d8de156df4177889f31a9bd6edc00", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "status": "DOWN", "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25", "id": "ada25a95-f321-4f59-b0e0-f3a970dd3d63", diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index ceba7a9464..edf105aae5 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" @@ -35,12 +36,19 @@ func TestList(t *testing.T) { return false, err } + createdTime, err := time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") + th.AssertNoErr(t, err) + updatedTime, err := time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") + th.AssertNoErr(t, err) + expected := []floatingips.FloatingIP{ { FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", + Created: createdTime, + Updated: updatedTime, Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", @@ -51,6 +59,8 @@ func TestList(t *testing.T) { FixedIP: "192.0.0.2", FloatingIP: "10.0.0.3", TenantID: "017d8de156df4177889f31a9bd6edc00", + Created: createdTime, + Updated: updatedTime, Status: "DOWN", PortID: "74a342ce-8e07-4e91-880c-9f834b68fa25", ID: "ada25a95-f321-4f59-b0e0-f3a970dd3d63", @@ -122,6 +132,8 @@ func TestCreate(t *testing.T) { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", "tenant_id": "4969c491a3c74ee4af974e6d800c62de", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "fixed_ip_address": "10.0.0.3", "floating_ip_address": "", @@ -142,6 +154,8 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) + th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.Created.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.Updated.Format(time.RFC3339)) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "", ip.FloatingIP) th.AssertEquals(t, "ce705c24-c1ef-408a-bda3-7bbd946164ab", ip.PortID) @@ -173,6 +187,8 @@ func TestCreateEmptyPort(t *testing.T) { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", "tenant_id": "4969c491a3c74ee4af974e6d800c62de", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "fixed_ip_address": "10.0.0.3", "floating_ip_address": "", @@ -191,6 +207,8 @@ func TestCreateEmptyPort(t *testing.T) { th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) + th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.Created.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.Updated.Format(time.RFC3339)) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "", ip.FloatingIP) th.AssertEquals(t, "", ip.PortID) @@ -223,6 +241,8 @@ func TestCreateWithSubnetID(t *testing.T) { "floatingip": { "router_id": null, "tenant_id": "4969c491a3c74ee4af974e6d800c62de", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", "fixed_ip_address": null, "floating_ip_address": "172.24.4.3", @@ -243,6 +263,8 @@ func TestCreateWithSubnetID(t *testing.T) { th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) + th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.Created.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.Updated.Format(time.RFC3339)) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "172.24.4.3", ip.FloatingIP) th.AssertEquals(t, "", ip.PortID) @@ -267,6 +289,8 @@ func TestGet(t *testing.T) { "fixed_ip_address": "192.0.0.2", "floating_ip_address": "10.0.0.3", "tenant_id": "017d8de156df4177889f31a9bd6edc00", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "status": "DOWN", "port_id": "74a342ce-8e07-4e91-880c-9f834b68fa25", "id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7", @@ -284,6 +308,8 @@ func TestGet(t *testing.T) { th.AssertEquals(t, "74a342ce-8e07-4e91-880c-9f834b68fa25", ip.PortID) th.AssertEquals(t, "192.0.0.2", ip.FixedIP) th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", ip.TenantID) + th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.Created.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.Updated.Format(time.RFC3339)) th.AssertEquals(t, "DOWN", ip.Status) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "1117c30a-ddb4-49a1-bec3-a65b286b4170", ip.RouterID) From 8181c07f1244fcc667cd9fd1ce33335f2282c7f8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 25 Jul 2019 16:49:47 -0600 Subject: [PATCH 0795/2296] Networking v2: Rename Created and Updated Floating IP Fields (#1650) This commit renames Created and Updated to CreatedAt and UpdatedAt so they match their respective JSON tags. --- .../extensions/dns/testing/requests_test.go | 8 +++---- .../extensions/layer3/floatingips/results.go | 8 +++---- .../floatingips/testing/requests_test.go | 24 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go index ea76e5939d..d7a017aca4 100644 --- a/openstack/networking/v2/extensions/dns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/dns/testing/requests_test.go @@ -237,8 +237,8 @@ func TestFloatingIPGet(t *testing.T) { FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", - Created: createdTime, - Updated: updatedTime, + CreatedAt: createdTime, + UpdatedAt: updatedTime, Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", @@ -285,8 +285,8 @@ func TestFloatingIPCreate(t *testing.T) { FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", - Created: createdTime, - Updated: updatedTime, + CreatedAt: createdTime, + UpdatedAt: updatedTime, Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index c9c05629f0..341cc04c96 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -39,10 +39,10 @@ type FloatingIP struct { // specify a project identifier other than its own. TenantID string `json:"tenant_id"` - // Updated and Created contain ISO-8601 timestamps of when the state of the - // floating ip last changed, and when it was created. - Updated time.Time `json:"updated_at"` - Created time.Time `json:"created_at"` + // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of + // the floating ip last changed, and when it was created. + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` // ProjectID is the project owner of the floating IP. ProjectID string `json:"project_id"` diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index edf105aae5..668e9b66f6 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -47,8 +47,8 @@ func TestList(t *testing.T) { FixedIP: "", FloatingIP: "192.0.0.4", TenantID: "017d8de156df4177889f31a9bd6edc00", - Created: createdTime, - Updated: updatedTime, + CreatedAt: createdTime, + UpdatedAt: updatedTime, Status: "DOWN", PortID: "", ID: "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", @@ -59,8 +59,8 @@ func TestList(t *testing.T) { FixedIP: "192.0.0.2", FloatingIP: "10.0.0.3", TenantID: "017d8de156df4177889f31a9bd6edc00", - Created: createdTime, - Updated: updatedTime, + CreatedAt: createdTime, + UpdatedAt: updatedTime, Status: "DOWN", PortID: "74a342ce-8e07-4e91-880c-9f834b68fa25", ID: "ada25a95-f321-4f59-b0e0-f3a970dd3d63", @@ -154,8 +154,8 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) - th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.Created.Format(time.RFC3339)) - th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.Updated.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.CreatedAt.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.UpdatedAt.Format(time.RFC3339)) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "", ip.FloatingIP) th.AssertEquals(t, "ce705c24-c1ef-408a-bda3-7bbd946164ab", ip.PortID) @@ -207,8 +207,8 @@ func TestCreateEmptyPort(t *testing.T) { th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) - th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.Created.Format(time.RFC3339)) - th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.Updated.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.CreatedAt.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.UpdatedAt.Format(time.RFC3339)) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "", ip.FloatingIP) th.AssertEquals(t, "", ip.PortID) @@ -263,8 +263,8 @@ func TestCreateWithSubnetID(t *testing.T) { th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID) - th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.Created.Format(time.RFC3339)) - th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.Updated.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.CreatedAt.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.UpdatedAt.Format(time.RFC3339)) th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID) th.AssertEquals(t, "172.24.4.3", ip.FloatingIP) th.AssertEquals(t, "", ip.PortID) @@ -308,8 +308,8 @@ func TestGet(t *testing.T) { th.AssertEquals(t, "74a342ce-8e07-4e91-880c-9f834b68fa25", ip.PortID) th.AssertEquals(t, "192.0.0.2", ip.FixedIP) th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", ip.TenantID) - th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.Created.Format(time.RFC3339)) - th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.Updated.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T04:15:37Z", ip.CreatedAt.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", ip.UpdatedAt.Format(time.RFC3339)) th.AssertEquals(t, "DOWN", ip.Status) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) th.AssertEquals(t, "1117c30a-ddb4-49a1-bec3-a65b286b4170", ip.RouterID) From 73bf16e49026cf677599f6c3bce0d70d695062ad Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 25 Jul 2019 16:53:57 -0600 Subject: [PATCH 0796/2296] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb6fcd4cc0..c87fe28cca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,17 @@ IMPROVEMENTS -* Added `openstack/baremetal/apiversions.List` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) -* Added `openstack/baremetal/apiversions.Get` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) -* Added `openstack/compute/v2/extensions/servergroups.CreateOpts.Policy` [GH-1636](https://github.com/gophercloud/gophercloud/pull/1636) +* 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) 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 `openstack/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) +* 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) From c888843a14a037d75bb302a196d12068973bf11f Mon Sep 17 00:00:00 2001 From: naumvd95 Date: Mon, 29 Jul 2019 17:16:56 +0200 Subject: [PATCH 0797/2296] [Neutron][SecurityGroup] updated_at/created_at fields (#1654) for: #1648 * time.Time object * mapped to existing json fields General API reference: https://developer.openstack.org/api-ref/network/v2/?expanded=show-security-group-detail#show-security-group that fields defined in standard_attr.py , that means that it should be applied to all neutron-resources though decorator: https://github.com/openstack/neutron/blob/041203f1bb1a9cb814ab58c6088d0229cfe9d9bb/neutron/db/standard_attr.py#L181 https://github.com/openstack/neutron/blob/041203f1bb1a9cb814ab58c6088d0229cfe9d9bb/neutron/db/models/securitygroup.py#L26 --- .../v2/extensions/security/groups/results.go | 7 ++++ .../security/groups/testing/fixtures.go | 39 +++++++++++++------ .../security/groups/testing/requests_test.go | 5 +++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go index 468952b3e4..2397b9ee4c 100644 --- a/openstack/networking/v2/extensions/security/groups/results.go +++ b/openstack/networking/v2/extensions/security/groups/results.go @@ -1,6 +1,8 @@ package groups import ( + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" "github.com/gophercloud/gophercloud/pagination" @@ -25,6 +27,11 @@ type SecGroup struct { // TenantID is the project owner of the security group. TenantID string `json:"tenant_id"` + // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of the + // security group last changed, and when it was created. + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` + // ProjectID is the project owner of the security group. ProjectID string `json:"project_id"` diff --git a/openstack/networking/v2/extensions/security/groups/testing/fixtures.go b/openstack/networking/v2/extensions/security/groups/testing/fixtures.go index 9e4a931fe4..f508d09cd1 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/fixtures.go +++ b/openstack/networking/v2/extensions/security/groups/testing/fixtures.go @@ -1,6 +1,8 @@ package testing import ( + "time" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" ) @@ -13,19 +15,28 @@ const SecurityGroupListResponse = ` "id": "85cc3048-abc3-43cc-89b3-377341426ac5", "name": "default", "security_group_rules": [], - "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z" } ] } ` -var SecurityGroup1 = groups.SecGroup{ - Description: "default", - ID: "85cc3048-abc3-43cc-89b3-377341426ac5", - Name: "default", - Rules: []rules.SecGroupRule{}, - TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", -} +var ( + createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") + updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") + + SecurityGroup1 = groups.SecGroup{ + Description: "default", + ID: "85cc3048-abc3-43cc-89b3-377341426ac5", + Name: "default", + Rules: []rules.SecGroupRule{}, + TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", + CreatedAt: createdTime, + UpdatedAt: updatedTime, + } +) const SecurityGroupCreateRequest = ` { @@ -68,7 +79,9 @@ const SecurityGroupCreateResponse = ` "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } ], - "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z" } } ` @@ -113,7 +126,9 @@ const SecurityGroupUpdateResponse = ` "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } ], - "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z" } } ` @@ -150,7 +165,9 @@ const SecurityGroupGetResponse = ` "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } ], - "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z" } } ` diff --git a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go index 5bb8c70c5b..270b29a1cb 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" @@ -93,6 +94,8 @@ func TestUpdate(t *testing.T) { th.AssertEquals(t, "newer-webservers", sg.Name) th.AssertEquals(t, "security group for webservers", sg.Description) th.AssertEquals(t, "2076db17-a522-4506-91de-c6dd8e837028", sg.ID) + th.AssertEquals(t, "2019-06-30T04:15:37Z", sg.CreatedAt.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", sg.UpdatedAt.Format(time.RFC3339)) } func TestGet(t *testing.T) { @@ -117,6 +120,8 @@ func TestGet(t *testing.T) { th.AssertEquals(t, "default", sg.Name) th.AssertEquals(t, 2, len(sg.Rules)) th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sg.TenantID) + th.AssertEquals(t, "2019-06-30T04:15:37Z", sg.CreatedAt.Format(time.RFC3339)) + th.AssertEquals(t, "2019-06-30T05:18:49Z", sg.UpdatedAt.Format(time.RFC3339)) } func TestDelete(t *testing.T) { From bc34d9cc163044205e304f551781151f61e9a5f3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 29 Jul 2019 09:18:33 -0600 Subject: [PATCH 0798/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c87fe28cca..56ae197559 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ IMPROVEMENTS * 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) + BUG FIXES From 1a274bd784e969de2414fabfb014f0f39f4b27ca Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 29 Jul 2019 19:44:02 -0600 Subject: [PATCH 0799/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56ae197559..3c270c1ea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ IMPROVEMENTS * 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) + BUG FIXES From c9199810c90ac691936ebcda1b78b77625144fb6 Mon Sep 17 00:00:00 2001 From: naumvd95 Date: Tue, 30 Jul 2019 03:44:25 +0200 Subject: [PATCH 0800/2296] [Neutron][Network] updated_at/created_at fields (#1657) for: #1655 * time.Time object * mapped to existing json fields General API reference:https://docs.openstack.org/api-ref/network/v2/?expanded=show-network-details-detail#show-network-details that fields defined in standard_attr.py , that means that it should be applied to all neutron-resources though decorator: https://github.com/openstack/neutron/blob/041203f1bb1a9cb814ab58c6088d0229cfe9d9bb/neutron/db/standard_attr.py#L181 https://github.com/openstack/neutron/blob/041203f1bb1a9cb814ab58c6088d0229cfe9d9bb/neutron/db/models_v2.py#L253 --- .../v2/extensions/dns/testing/fixtures.go | 4 ++ .../extensions/dns/testing/requests_test.go | 19 +++--- openstack/networking/v2/networks/results.go | 7 ++ .../v2/networks/testing/fixtures.go | 65 +++++++++++++------ .../v2/networks/testing/requests_test.go | 11 ++++ 5 files changed, 77 insertions(+), 29 deletions(-) diff --git a/openstack/networking/v2/extensions/dns/testing/fixtures.go b/openstack/networking/v2/extensions/dns/testing/fixtures.go index 6da93ec82c..d5fd33324b 100644 --- a/openstack/networking/v2/extensions/dns/testing/fixtures.go +++ b/openstack/networking/v2/extensions/dns/testing/fixtures.go @@ -29,6 +29,8 @@ const NetworkCreateResponse = ` "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, @@ -55,6 +57,8 @@ const NetworkUpdateResponse = ` "name": "new_network_name", "admin_state_up": false, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go index d7a017aca4..3a792e97b2 100644 --- a/openstack/networking/v2/extensions/dns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/dns/testing/requests_test.go @@ -27,6 +27,9 @@ type NetworkDNS struct { dns.NetworkDNSExt } +var createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") +var updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") + func TestPortList(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -226,11 +229,6 @@ func TestFloatingIPGet(t *testing.T) { err := floatingips.Get(fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").ExtractInto(&actual) th.AssertNoErr(t, err) - createdTime, err := time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") - th.AssertNoErr(t, err) - updatedTime, err := time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") - th.AssertNoErr(t, err) - expected := FloatingIPDNS{ FloatingIP: floatingips.FloatingIP{ FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", @@ -274,11 +272,6 @@ func TestFloatingIPCreate(t *testing.T) { err := floatingips.Create(fake.ServiceClient(), options).ExtractInto(&actual) th.AssertNoErr(t, err) - createdTime, err := time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") - th.AssertNoErr(t, err) - updatedTime, err := time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") - th.AssertNoErr(t, err) - expected := FloatingIPDNS{ FloatingIP: floatingips.FloatingIP{ FloatingNetworkID: "6d67c30a-ddb4-49a1-bec3-a65b286b4170", @@ -318,6 +311,8 @@ func TestNetworkGet(t *testing.T) { Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, Status: "ACTIVE", TenantID: "4fd44f30292945e481c7b8a0c8908869", + CreatedAt: createdTime, + UpdatedAt: updatedTime, AdminStateUp: true, Shared: true, ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", @@ -354,6 +349,8 @@ func TestNetworkCreate(t *testing.T) { Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, Status: "ACTIVE", TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", + CreatedAt: createdTime, + UpdatedAt: updatedTime, AdminStateUp: true, Shared: false, ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", @@ -390,6 +387,8 @@ func TestNetworkUpdate(t *testing.T) { Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, Status: "ACTIVE", TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", + CreatedAt: createdTime, + UpdatedAt: updatedTime, AdminStateUp: false, Shared: false, ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index f03067415f..4786b07b6b 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -1,6 +1,8 @@ package networks import ( + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -70,6 +72,11 @@ type Network struct { // TenantID is the project owner of the network. TenantID string `json:"tenant_id"` + // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of the + // network last changed, and when it was created. + UpdatedAt time.Time `json:"updated_at"` + CreatedAt time.Time `json:"created_at"` + // ProjectID is the project owner of the network. ProjectID string `json:"project_id"` diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index 756fa6b18a..15e3dc0260 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -1,6 +1,8 @@ package testing import ( + "time" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" ) @@ -15,6 +17,8 @@ const ListResponse = ` "name": "public", "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": true, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "provider:segmentation_id": 9876543210, @@ -33,6 +37,8 @@ const ListResponse = ` "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 1234567890, @@ -56,6 +62,8 @@ const GetResponse = ` "name": "public", "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": true, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "provider:segmentation_id": 9876543210, @@ -84,6 +92,8 @@ const CreateResponse = ` "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, @@ -110,6 +120,8 @@ const CreatePortSecurityResponse = ` "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", "provider:segmentation_id": 9876543210, @@ -147,6 +159,8 @@ const UpdateResponse = ` "name": "new_network_name", "admin_state_up": false, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": true, "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", "provider:segmentation_id": 1234567890, @@ -170,6 +184,8 @@ const UpdatePortSecurityResponse = ` "name": "private", "admin_state_up": true, "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "created_at": "2019-06-30T04:15:37Z", + "updated_at": "2019-06-30T05:18:49Z", "shared": false, "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c", "provider:segmentation_id": 9876543210, @@ -179,24 +195,35 @@ const UpdatePortSecurityResponse = ` } }` -var Network1 = networks.Network{ - Status: "ACTIVE", - Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, - Name: "public", - AdminStateUp: true, - TenantID: "4fd44f30292945e481c7b8a0c8908869", - Shared: true, - ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", -} - -var Network2 = networks.Network{ - Status: "ACTIVE", - Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, - Name: "private", - AdminStateUp: true, - TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", - Shared: false, - ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", -} +var createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") +var updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") + +var ( + Network1 = networks.Network{ + Status: "ACTIVE", + Subnets: []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}, + Name: "public", + AdminStateUp: true, + TenantID: "4fd44f30292945e481c7b8a0c8908869", + CreatedAt: createdTime, + UpdatedAt: updatedTime, + Shared: true, + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + } +) + +var ( + Network2 = networks.Network{ + Status: "ACTIVE", + Subnets: []string{"08eae331-0402-425a-923c-34f7cfe39c1b"}, + Name: "private", + AdminStateUp: true, + TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", + CreatedAt: createdTime, + UpdatedAt: updatedTime, + Shared: false, + ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + } +) var ExpectedNetworkSlice = []networks.Network{Network1, Network2} diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 1664f3e5cb..7bd24066f2 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" @@ -80,6 +81,10 @@ func TestListWithExtensions(t *testing.T) { th.AssertEquals(t, allNetworks[0].PortSecurityEnabled, true) th.AssertEquals(t, allNetworks[0].Subnets[0], "54d6f61d-db07-451c-9ab3-b9609b6b6f0b") th.AssertEquals(t, allNetworks[1].Subnets[0], "08eae331-0402-425a-923c-34f7cfe39c1b") + th.AssertEquals(t, allNetworks[0].CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") + th.AssertEquals(t, allNetworks[0].UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") + th.AssertEquals(t, allNetworks[1].CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") + th.AssertEquals(t, allNetworks[1].UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } func TestGet(t *testing.T) { @@ -99,6 +104,8 @@ func TestGet(t *testing.T) { n, err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &Network1, n) + th.AssertEquals(t, n.CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") + th.AssertEquals(t, n.UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } func TestGetWithExtensions(t *testing.T) { @@ -150,6 +157,8 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, n.Status, "ACTIVE") th.AssertDeepEquals(t, &Network2, n) + th.AssertEquals(t, n.CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") + th.AssertEquals(t, n.UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } func TestCreateWithOptionalFields(t *testing.T) { @@ -206,6 +215,8 @@ func TestUpdate(t *testing.T) { th.AssertEquals(t, n.AdminStateUp, false) th.AssertEquals(t, n.Shared, true) th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c") + th.AssertEquals(t, n.CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") + th.AssertEquals(t, n.UpdatedAt.Format(time.RFC3339), "2019-06-30T05:18:49Z") } func TestDelete(t *testing.T) { From 4e334c7788b085f3e14631c2efc9c83c59bd1fbf Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 30 Jul 2019 22:09:02 +0200 Subject: [PATCH 0801/2296] Allow modification of the container secret refs (#1659) * Allow modification of the container secret refs * Fix copy-paste typo --- .../keymanager/v1/containers_test.go | 10 ++++ .../openstack/keymanager/v1/keymanager.go | 48 +++++++++++++++++++ .../keymanager/v1/containers/requests.go | 44 ++++++++++++++++- openstack/keymanager/v1/containers/results.go | 19 ++++++++ openstack/keymanager/v1/containers/urls.go | 8 ++++ 5 files changed, 128 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/keymanager/v1/containers_test.go b/acceptance/openstack/keymanager/v1/containers_test.go index 2b5f4d01fe..0b3bb5f96c 100644 --- a/acceptance/openstack/keymanager/v1/containers_test.go +++ b/acceptance/openstack/keymanager/v1/containers_test.go @@ -23,12 +23,22 @@ func TestGenericContainersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) + payload1 := tools.RandomString("SUPERSECRET-", 8) + secret1, err := CreateSecretWithPayload(t, client, payload1) + th.AssertNoErr(t, err) + secretID1, err := ParseID(secret1.SecretRef) + th.AssertNoErr(t, err) + defer DeleteSecret(t, client, secretID1) + container, err := CreateGenericContainer(t, client, secret) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) th.AssertNoErr(t, err) defer DeleteContainer(t, client, containerID) + err = ReplaceGenericContainerSecretRef(t, client, container, secret, secret1) + th.AssertNoErr(t, err) + allPages, err := containers.List(client, nil).AllPages() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/keymanager/v1/keymanager.go b/acceptance/openstack/keymanager/v1/keymanager.go index c7c72ef7c2..e4b6f95015 100644 --- a/acceptance/openstack/keymanager/v1/keymanager.go +++ b/acceptance/openstack/keymanager/v1/keymanager.go @@ -337,6 +337,54 @@ func CreateGenericContainer(t *testing.T, client *gophercloud.ServiceClient, sec return container, nil } +// ReplaceGenericContainerSecretRef will replace the container old secret +// reference with a new one. An error will be returned if the reference could +// not be replaced. +func ReplaceGenericContainerSecretRef(t *testing.T, client *gophercloud.ServiceClient, container *containers.Container, secretOld *secrets.Secret, secretNew *secrets.Secret) error { + containerID, err := ParseID(container.ContainerRef) + if err != nil { + return err + } + + t.Logf("Attempting to remove an old secret reference %s", secretOld.SecretRef) + + res1 := containers.DeleteSecretRef(client, containerID, containers.SecretRef{Name: secretOld.Name, SecretRef: secretOld.SecretRef}) + if res1.Err != nil { + return res1.Err + } + + t.Logf("Successfully removed old secret reference: %s", secretOld.SecretRef) + + t.Logf("Attempting to remove a new secret reference %s", secretNew.SecretRef) + + newRef := containers.SecretRef{Name: secretNew.Name, SecretRef: secretNew.SecretRef} + res2 := containers.CreateSecretRef(client, containerID, newRef) + if res2.Err != nil { + return res2.Err + } + + c, err := res2.Extract() + if err != nil { + return err + } + tools.PrintResource(t, c) + + t.Logf("Successfully created new secret reference: %s", secretNew.SecretRef) + + updatedContainer, err := containers.Get(client, containerID).Extract() + if err != nil { + return err + } + + tools.PrintResource(t, container) + + th.AssertEquals(t, updatedContainer.Name, container.Name) + th.AssertEquals(t, updatedContainer.Type, container.Type) + th.AssertEquals(t, updatedContainer.SecretRefs[0], newRef) + + return nil +} + // CreatePassphraseSecret will create a random passphrase secret. // An error will be returned if the secret could not be created. func CreatePassphraseSecret(t *testing.T, client *gophercloud.ServiceClient, passphrase string) (*secrets.Secret, error) { diff --git a/openstack/keymanager/v1/containers/requests.go b/openstack/keymanager/v1/containers/requests.go index 190438ed28..189b6c501c 100644 --- a/openstack/keymanager/v1/containers/requests.go +++ b/openstack/keymanager/v1/containers/requests.go @@ -74,7 +74,7 @@ type CreateOpts struct { Name string `json:"name"` // SecretRefs is a list of secret refs for the container. - SecretRefs []SecretRef `json:"secret_refs"` + SecretRefs []SecretRef `json:"secret_refs,omitempty"` } // ToContainerCreateMap formats a CreateOpts into a create request. @@ -210,3 +210,45 @@ func DeleteConsumer(client *gophercloud.ServiceClient, containerID string, opts }) return } + +// SecretRefBuilder allows extensions to add additional parameters to the +// Create request. +type SecretRefBuilder interface { + ToContainerSecretRefMap() (map[string]interface{}, error) +} + +// ToContainerSecretRefMap formats a SecretRefBuilder into a create +// request. +func (opts SecretRef) ToContainerSecretRefMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// CreateSecret creates a new consumer. +func CreateSecretRef(client *gophercloud.ServiceClient, containerID string, opts SecretRefBuilder) (r CreateSecretRefResult) { + b, err := opts.ToContainerSecretRefMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createSecretRefURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// DeleteSecret deletes a consumer. +func DeleteSecretRef(client *gophercloud.ServiceClient, containerID string, opts SecretRefBuilder) (r DeleteSecretRefResult) { + url := deleteSecretRefURL(client, containerID) + + b, err := opts.ToContainerSecretRefMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Request("DELETE", url, &gophercloud.RequestOpts{ + JSONBody: b, + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/keymanager/v1/containers/results.go b/openstack/keymanager/v1/containers/results.go index c94c9ac671..ce7e28f786 100644 --- a/openstack/keymanager/v1/containers/results.go +++ b/openstack/keymanager/v1/containers/results.go @@ -230,3 +230,22 @@ func ExtractConsumers(r pagination.Page) ([]Consumer, error) { err := (r.(ConsumerPage)).ExtractInto(&s) return s.Consumers, err } + +// Extract interprets any CreateSecretRefResult as a Container +func (r CreateSecretRefResult) Extract() (*Container, error) { + var c *Container + err := r.ExtractInto(&c) + return c, err +} + +// CreateSecretRefResult is the response from a CreateSecretRef operation. +// Call its Extract method to interpret it as a container. +type CreateSecretRefResult struct { + // This is not a typo. + commonResult +} + +// DeleteSecretRefResult is the response from a DeleteSecretRef operation. +type DeleteSecretRefResult struct { + gophercloud.ErrResult +} diff --git a/openstack/keymanager/v1/containers/urls.go b/openstack/keymanager/v1/containers/urls.go index 236ebc8133..8077e459ca 100644 --- a/openstack/keymanager/v1/containers/urls.go +++ b/openstack/keymanager/v1/containers/urls.go @@ -29,3 +29,11 @@ func createConsumerURL(client *gophercloud.ServiceClient, id string) string { func deleteConsumerURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("containers", id, "consumers") } + +func createSecretRefURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("containers", id, "secrets") +} + +func deleteSecretRefURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("containers", id, "secrets") +} From 60b07a48481f187c2bb741ae44d5aa190803a0e9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 30 Jul 2019 14:10:23 -0600 Subject: [PATCH 0802/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c270c1ea4..cb96118e04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ IMPROVEMENTS * 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) From 8014bd3133fe04a8499e12dc3710be7cfe58ef66 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 30 Jul 2019 21:36:51 -0600 Subject: [PATCH 0803/2296] Unit Test: Modify Object Storage fixtures for time parsing (#1661) --- .../objectstorage/v1/accounts/testing/fixtures.go | 6 +++--- .../objectstorage/v1/accounts/testing/requests_test.go | 10 +++------- .../objectstorage/v1/containers/testing/fixtures.go | 4 ++-- .../v1/containers/testing/requests_test.go | 5 ++--- openstack/objectstorage/v1/objects/testing/fixtures.go | 4 ++-- .../objectstorage/v1/objects/testing/requests_test.go | 6 +----- 6 files changed, 13 insertions(+), 22 deletions(-) diff --git a/openstack/objectstorage/v1/accounts/testing/fixtures.go b/openstack/objectstorage/v1/accounts/testing/fixtures.go index e1d2182699..f7254cc3b7 100644 --- a/openstack/objectstorage/v1/accounts/testing/fixtures.go +++ b/openstack/objectstorage/v1/accounts/testing/fixtures.go @@ -20,7 +20,7 @@ func HandleGetAccountSuccessfully(t *testing.T) { w.Header().Set("X-Account-Meta-Quota-Bytes", "42") w.Header().Set("X-Account-Bytes-Used", "14") w.Header().Set("X-Account-Meta-Subject", "books") - w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT") + w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 UTC") w.WriteHeader(http.StatusNoContent) }) @@ -37,7 +37,7 @@ func HandleGetAccountNoQuotaSuccessfully(t *testing.T) { w.Header().Set("X-Account-Object-Count", "5") w.Header().Set("X-Account-Bytes-Used", "14") w.Header().Set("X-Account-Meta-Subject", "books") - w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT") + w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 UTC") w.WriteHeader(http.StatusNoContent) }) @@ -51,7 +51,7 @@ func HandleUpdateAccountSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "X-Account-Meta-Gophercloud-Test", "accounts") - w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 GMT") + w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 UTC") w.WriteHeader(http.StatusNoContent) }) } diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go index c396227e4d..c7e1695052 100644 --- a/openstack/objectstorage/v1/accounts/testing/requests_test.go +++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go @@ -9,10 +9,6 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) -var ( - loc, _ = time.LoadLocation("GMT") -) - func TestUpdateAccount(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -23,7 +19,7 @@ func TestUpdateAccount(t *testing.T) { th.AssertNoErr(t, res.Err) expected := &accounts.UpdateHeader{ - Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, loc), // Fri, 17 Jan 2014 16:09:56 GMT + Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, time.UTC), } actual, err := res.Extract() th.AssertNoErr(t, err) @@ -49,7 +45,7 @@ func TestGetAccount(t *testing.T) { ContainerCount: 2, ObjectCount: 5, BytesUsed: 14, - Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, loc), // Fri, 17 Jan 2014 16:09:56 GMT + Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, time.UTC), } actual, err := res.Extract() th.AssertNoErr(t, err) @@ -74,7 +70,7 @@ func TestGetAccountNoQuota(t *testing.T) { ContainerCount: 2, ObjectCount: 5, BytesUsed: 14, - Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, loc), // Fri, 17 Jan 2014 16:09:56 GMT + Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, time.UTC), } actual, err := res.Extract() th.AssertNoErr(t, err) diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 5915323ad4..23169f804a 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -105,7 +105,7 @@ func HandleCreateContainerSuccessfully(t *testing.T) { w.Header().Add("X-Container-Meta-Foo", "bar") w.Header().Set("Content-Length", "0") w.Header().Set("Content-Type", "text/html; charset=UTF-8") - w.Header().Set("Date", "Wed, 17 Aug 2016 19:25:43 GMT") + w.Header().Set("Date", "Wed, 17 Aug 2016 19:25:43 UTC") w.Header().Set("X-Trans-Id", "tx554ed59667a64c61866f1-0058b4ba37") w.WriteHeader(http.StatusNoContent) }) @@ -142,7 +142,7 @@ func HandleGetContainerSuccessfully(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Accept-Ranges", "bytes") w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Header().Set("Date", "Wed, 17 Aug 2016 19:25:43 GMT") + w.Header().Set("Date", "Wed, 17 Aug 2016 19:25:43 UTC") w.Header().Set("X-Container-Bytes-Used", "100") w.Header().Set("X-Container-Object-Count", "4") w.Header().Set("X-Container-Read", "test") diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index c5eabd29f3..379910106f 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -12,7 +12,6 @@ import ( var ( metadata = map[string]string{"gophercloud-test": "containers"} - loc, _ = time.LoadLocation("GMT") ) func TestListContainerInfo(t *testing.T) { @@ -92,7 +91,7 @@ func TestCreateContainer(t *testing.T) { expected := &containers.CreateHeader{ ContentLength: 0, ContentType: "text/html; charset=UTF-8", - Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, loc), //Wed, 17 Aug 2016 19:25:43 GMT + Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, time.UTC), TransID: "tx554ed59667a64c61866f1-0058b4ba37", } actual, err := res.Extract() @@ -135,7 +134,7 @@ func TestGetContainer(t *testing.T) { AcceptRanges: "bytes", BytesUsed: 100, ContentType: "application/json; charset=utf-8", - Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, loc), //Wed, 17 Aug 2016 19:25:43 GMT + Date: time.Date(2016, time.August, 17, 19, 25, 43, 0, time.UTC), ObjectCount: 4, Read: []string{"test"}, TransID: "tx554ed59667a64c61866f1-0057b4ba37", diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 0ede1d6201..520e8b48e9 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -20,7 +20,7 @@ func HandleDownloadObjectSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") - w.Header().Set("Date", "Wed, 10 Nov 2009 23:00:00 GMT") + w.Header().Set("Date", "Wed, 10 Nov 2009 23:00:00 UTC") w.Header().Set("X-Static-Large-Object", "True") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Successful download with Gophercloud") @@ -32,7 +32,7 @@ func HandleDownloadObjectSuccessfully(t *testing.T) { var ExpectedListInfo = []objects.Object{ { Hash: "451e372e48e0f6b1114fa0724aa79fa1", - LastModified: time.Date(2016, time.August, 17, 22, 11, 58, 602650000, time.UTC), //"2016-08-17T22:11:58.602650" + LastModified: time.Date(2016, time.August, 17, 22, 11, 58, 602650000, time.UTC), Bytes: 14, Name: "goodbye", ContentType: "application/octet-stream", diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index c5b34a75b0..e7ba01534e 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -16,10 +16,6 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) -var ( - loc, _ = time.LoadLocation("GMT") -) - func TestDownloadReader(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -49,7 +45,7 @@ func TestDownloadExtraction(t *testing.T) { expected := &objects.DownloadHeader{ ContentLength: 36, ContentType: "text/plain; charset=utf-8", - Date: time.Date(2009, time.November, 10, 23, 0, 0, 0, loc), + Date: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), StaticLargeObject: true, } actual, err := response.Extract() From d4334eb11a019bed922ea2377bbe47fa56085395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Va=C5=A1ek?= Date: Thu, 1 Aug 2019 02:43:45 +0200 Subject: [PATCH 0804/2296] Manila: add support for share metadata modification (#1656) * Manila: add functionality for showing, setting, updating and unsetting share metadata * Manila: add unit and acceptance tests for share metadata * refactoring: unset metadata -> delete metadatum * updated description for Set/UpdateMetadata, denoting the difference between the two * refactoring: show metadata -> get metadata * added GetMetadatum * merged {Get,Set,Update}MetadataResult types into MetadataResult; GetMetadatumResult now returns a map * updated tests --- .../sharedfilesystems/v2/shares_test.go | 59 ++++++++++ .../sharedfilesystems/v2/shares/requests.go | 97 +++++++++++++++++ .../sharedfilesystems/v2/shares/results.go | 33 ++++++ .../v2/shares/testing/fixtures.go | 103 ++++++++++++++++++ .../v2/shares/testing/request_test.go | 66 ++++++++++- openstack/sharedfilesystems/v2/shares/urls.go | 20 ++++ 6 files changed, 377 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 1aef7d6973..2361309e20 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -204,3 +204,62 @@ func TestExtendAndShrink(t *testing.T) { t.Logf("Share %s successfuly shrunk", share.ID) */ } + +func TestShareMetadata(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.7" + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + const ( + k = "key" + v1 = "value1" + v2 = "value2" + ) + + checkMetadataEq := func(m map[string]string, value string) { + if m == nil || len(m) != 1 || m[k] != value { + t.Fatalf("Unexpected metadata contents %v", m) + } + } + + metadata, err := shares.SetMetadata(client, share.ID, shares.SetMetadataOpts{Metadata: map[string]string{k: v1}}).Extract() + if err != nil { + t.Fatalf("Unable to set share metadata: %v", err) + } + checkMetadataEq(metadata, v1) + + metadata, err = shares.UpdateMetadata(client, share.ID, shares.UpdateMetadataOpts{Metadata: map[string]string{k: v2}}).Extract() + if err != nil { + t.Fatalf("Unable to update share metadata: %v", err) + } + checkMetadataEq(metadata, v2) + + metadata, err = shares.GetMetadatum(client, share.ID, k).Extract() + if err != nil { + t.Fatalf("Unable to get share metadatum: %v", err) + } + checkMetadataEq(metadata, v2) + + err = shares.DeleteMetadatum(client, share.ID, k).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete share metadatum: %v", err) + } + + metadata, err = shares.GetMetadata(client, share.ID).Extract() + if err != nil { + t.Fatalf("Unable to get share metadata: %v", err) + } + + if metadata == nil || len(metadata) != 0 { + t.Fatalf("Unexpected metadata contents %v, expected an empty map", metadata) + } +} diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 2028b4831e..568d35a71d 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -379,3 +379,100 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder }) return } + +// GetMetadata retrieves metadata of the specified share. To extract the retrieved +// metadata from the response, call the Extract method on the MetadataResult. +func GetMetadata(client *gophercloud.ServiceClient, id string) (r MetadataResult) { + _, r.Err = client.Get(getMetadataURL(client, id), &r.Body, nil) + return +} + +// GetMetadatum retrieves a single metadata item of the specified share. To extract the retrieved +// metadata from the response, call the Extract method on the GetMetadatumResult. +func GetMetadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { + _, r.Err = client.Get(getMetadatumURL(client, id, key), &r.Body, nil) + return +} + +// SetMetadataOpts contains options for setting share metadata. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Share Metadata, Show share metadata documentation. +type SetMetadataOpts struct { + Metadata map[string]string `json:"metadata"` +} + +// ToSetMetadataMap assembles a request body based on the contents of an +// SetMetadataOpts. +func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// SetMetadataOptsBuilder allows extensions to add additional parameters to the +// SetMetadata request. +type SetMetadataOptsBuilder interface { + ToSetMetadataMap() (map[string]interface{}, error) +} + +// SetMetadata sets metadata of the specified share. +// Existing metadata items are either kept or overwritten by the metadata from the request. +// To extract the updated metadata from the response, call the Extract +// method on the MetadataResult. +func SetMetadata(client *gophercloud.ServiceClient, id string, opts SetMetadataOptsBuilder) (r MetadataResult) { + b, err := opts.ToSetMetadataMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(setMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + + return +} + +// UpdateMetadataOpts contains options for updating share metadata. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Share Metadata, Update share metadata documentation. +type UpdateMetadataOpts struct { + Metadata map[string]string `json:"metadata"` +} + +// ToUpdateMetadataMap assembles a request body based on the contents of an +// UpdateMetadataOpts. +func (opts UpdateMetadataOpts) ToUpdateMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// UpdateMetadataOptsBuilder allows extensions to add additional parameters to the +// UpdateMetadata request. +type UpdateMetadataOptsBuilder interface { + ToUpdateMetadataMap() (map[string]interface{}, error) +} + +// UpdateMetadata updates metadata of the specified share. +// All existing metadata items are discarded and replaced by the metadata from the request. +// To extract the updated metadata from the response, call the Extract +// method on the MetadataResult. +func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r MetadataResult) { + b, err := opts.ToUpdateMetadataMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + + return +} + +// DeleteMetadatum deletes a single key-value pair from the metadata of the specified share. +func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { + _, r.Err = client.Delete(deleteMetadatumURL(client, id, key), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + + return +} diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 0deae373b6..05fe327fba 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -324,3 +324,36 @@ type ExtendResult struct { type ShrinkResult struct { gophercloud.ErrResult } + +// GetMetadatumResult contains the response body and error from a GetMetadatum request. +type GetMetadatumResult struct { + gophercloud.Result +} + +// Extract will get the string-string map from GetMetadatumResult +func (r GetMetadatumResult) Extract() (map[string]string, error) { + var s struct { + Meta map[string]string `json:"meta"` + } + err := r.ExtractInto(&s) + return s.Meta, err +} + +// MetadataResult contains the response body and error from GetMetadata, SetMetadata or UpdateMetadata requests. +type MetadataResult struct { + gophercloud.Result +} + +// Extract will get the string-string map from MetadataResult +func (r MetadataResult) Extract() (map[string]string, error) { + var s struct { + Metadata map[string]string `json:"metadata"` + } + err := r.ExtractInto(&s) + return s.Metadata, err +} + +// DeleteMetadatumResult contains the response body and error from a DeleteMetadatum request. +type DeleteMetadatumResult struct { + gophercloud.ErrResult +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 9048e9a913..17d1f47fed 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -418,3 +418,106 @@ func MockShrinkResponse(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +var getMetadataResponse = `{ + "metadata": { + "foo": "bar" + } + }` + +// MockGetMetadataResponse creates a mock get metadata response +func MockGetMetadataResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata", 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.Fprint(w, getMetadataResponse) + }) +} + +var getMetadatumResponse = `{ + "meta": { + "foo": "bar" + } + }` + +// MockGetMetadatumResponse creates a mock get metadatum response +func MockGetMetadatumResponse(t *testing.T, key string) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata/"+key, 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.Fprint(w, getMetadatumResponse) + }) +} + +var setMetadataRequest = `{ + "metadata": { + "foo": "bar" + } + }` + +var setMetadataResponse = `{ + "metadata": { + "foo": "bar" + } + }` + +// MockSetMetadataResponse creates a mock set metadata response +func MockSetMetadataResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata", 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, setMetadataRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, setMetadataResponse) + }) +} + +var updateMetadataRequest = `{ + "metadata": { + "foo": "bar" + } + }` + +var updateMetadataResponse = `{ + "metadata": { + "foo": "bar" + } + }` + +// MockUpdateMetadataResponse creates a mock update metadata response +func MockUpdateMetadataResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata", 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, updateMetadataRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, updateMetadataResponse) + }) +} + +var deleteMetadatumRequest = `{ + "metadata": { + "foo": "bar" + } + }` + +// MockDeleteMetadatumResponse creates a mock unset metadata response +func MockDeleteMetadatumResponse(t *testing.T, key string) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata/"+key, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusOK) + }) +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index 75053bd9f2..06bc9a187e 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -119,7 +119,7 @@ func TestListDetail(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, []shares.Share{ - shares.Share{ + { AvailabilityZone: "nova", ShareNetworkID: "713df749-aac0-4a54-af52-10f6c991e80c", ShareServerID: "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", @@ -283,3 +283,67 @@ func TestShrinkSuccess(t *testing.T) { err := shares.Shrink(c, shareID, &shares.ShrinkOpts{NewSize: 1}).ExtractErr() th.AssertNoErr(t, err) } + +func TestGetMetadataSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetMetadataResponse(t) + + c := client.ServiceClient() + + actual, err := shares.GetMetadata(c, shareID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) +} + +func TestGetMetadatumSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetMetadatumResponse(t, "foo") + + c := client.ServiceClient() + + actual, err := shares.GetMetadatum(c, shareID, "foo").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) +} + +func TestSetMetadataSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetMetadataResponse(t) + + c := client.ServiceClient() + + actual, err := shares.SetMetadata(c, shareID, &shares.SetMetadataOpts{Metadata: map[string]string{"foo": "bar"}}).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) +} + +func TestUpdateMetadataSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateMetadataResponse(t) + + c := client.ServiceClient() + + actual, err := shares.UpdateMetadata(c, shareID, &shares.UpdateMetadataOpts{Metadata: map[string]string{"foo": "bar"}}).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) +} + +func TestUnsetMetadataSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteMetadatumResponse(t, "foo") + + c := client.ServiceClient() + + err := shares.DeleteMetadatum(c, shareID, "foo").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index 02fba24d2d..f9a99c83bf 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -45,3 +45,23 @@ func extendURL(c *gophercloud.ServiceClient, id string) string { func shrinkURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } + +func getMetadataURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "metadata") +} + +func getMetadatumURL(c *gophercloud.ServiceClient, id, key string) string { + return c.ServiceURL("shares", id, "metadata", key) +} + +func setMetadataURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "metadata") +} + +func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "metadata") +} + +func deleteMetadatumURL(c *gophercloud.ServiceClient, id, key string) string { + return c.ServiceURL("shares", id, "metadata", key) +} From f3ea41e8a75a3c2b193a1791311edb749274c4bc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 31 Jul 2019 18:45:24 -0600 Subject: [PATCH 0805/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb96118e04..2e79ed407c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ IMPROVEMENTS * 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) From 7f00c45bfdd8b6a1ebe4cba70a4180e7721454c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Va=C5=A1ek?= Date: Thu, 1 Aug 2019 02:47:31 +0200 Subject: [PATCH 0806/2296] manila share-types: added IDFromName convenience function (#1662) --- .../v2/sharetypes/results.go | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/openstack/sharedfilesystems/v2/sharetypes/results.go b/openstack/sharedfilesystems/v2/sharetypes/results.go index f60d757766..c0eb4b2ec4 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/results.go +++ b/openstack/sharedfilesystems/v2/sharetypes/results.go @@ -33,6 +33,40 @@ func (r commonResult) Extract() (*ShareType, error) { return s.ShareType, err } +// IDFromName is a convenience function that returns a share-type's ID given its name. +func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { + r, err := List(client, &ListOpts{}).AllPages() + if err != nil { + return "", nil + } + + ss, err := ExtractShareTypes(r) + if err != nil { + return "", err + } + + var ( + count int + id string + ) + + for _, s := range ss { + if s.Name == name { + count++ + id = s.ID + } + } + + switch count { + case 0: + return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "share type"} + case 1: + return id, nil + default: + return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "share type"} + } +} + // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult From 3127bc66fde994a2c438d251bce4320b598b0465 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 31 Jul 2019 18:48:12 -0600 Subject: [PATCH 0807/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e79ed407c..975c587ad8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ IMPROVEMENTS * 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) From 0398b0cd16bfffade0883973c745180adbbe8918 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 31 Jul 2019 18:50:34 -0600 Subject: [PATCH 0808/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 975c587ad8..d0b120de1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.3.0 (Unreleaesd) +## 0.4.0 (Unreleased) + +## 0.3.0 (July 31, 2019) IMPROVEMENTS From da90435583c09293c844b570465534b6e2eb7697 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 5 Aug 2019 04:19:59 +0200 Subject: [PATCH 0809/2296] Identity V3: Fix RegionID type (#1664) --- openstack/identity/v3/endpoints/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 0764a118e2..ebcd755a6a 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -62,7 +62,7 @@ type ListOpts struct { ServiceID string `q:"service_id"` // RegionID is the ID of the region the Endpoint refers to. - RegionID int `q:"region_id"` + RegionID string `q:"region_id"` } // ToEndpointListParams builds a list request from the List options. From 951d29d7ead8543b9b6e3f38b971ae952df0a527 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 4 Aug 2019 20:21:09 -0600 Subject: [PATCH 0810/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0b120de1b..1cbeeb00cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.4.0 (Unreleased) +BUG FIXES + +* Changed `identity/v3/endpoints.ListOpts.RegionID` from `int` to `string` [GH-1664](https://github.com/gophercloud/gophercloud/pull/1664) + ## 0.3.0 (July 31, 2019) IMPROVEMENTS From 5e36666331ae19e211a1302b47e236827ea92a29 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 5 Aug 2019 16:39:13 +0300 Subject: [PATCH 0811/2296] Blockstorage: add groups to quotaset result (#1668) Allow to retrieve groups attribute for the Blockstorage quotaset. Update tests. --- .../blockstorage/v3/quotaset_test.go | 3 + .../extensions/quotasets/results.go | 8 ++ .../extensions/quotasets/testing/fixtures.go | 124 ++++++++++-------- 3 files changed, 78 insertions(+), 57 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index f69e6866b8..309cdce15e 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -53,6 +53,7 @@ var UpdateQuotaOpts = quotasets.UpdateOpts{ PerVolumeGigabytes: gophercloud.IntToPointer(50), Backups: gophercloud.IntToPointer(2), BackupGigabytes: gophercloud.IntToPointer(300), + Groups: gophercloud.IntToPointer(350), } var UpdatedQuotas = quotasets.QuotaSet{ @@ -62,6 +63,7 @@ var UpdatedQuotas = quotasets.QuotaSet{ PerVolumeGigabytes: 50, Backups: 2, BackupGigabytes: 300, + Groups: 350, } func TestQuotasetUpdate(t *testing.T) { @@ -142,4 +144,5 @@ func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOp dest.PerVolumeGigabytes = &src.PerVolumeGigabytes dest.Backups = &src.Backups dest.BackupGigabytes = &src.BackupGigabytes + dest.Groups = &src.Groups } diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/extensions/quotasets/results.go index 8ccfb5bfeb..e29569e8b4 100644 --- a/openstack/blockstorage/extensions/quotasets/results.go +++ b/openstack/blockstorage/extensions/quotasets/results.go @@ -31,6 +31,9 @@ type QuotaSet struct { // BackupGigabytes is the size (GB) of backups that are allowed for each // project. BackupGigabytes int `json:"backup_gigabytes"` + + // Groups is the number of groups that are allowed for each project. + Groups int `json:"groups,omitempty"` } // QuotaUsageSet represents details of both operational limits of block @@ -71,6 +74,11 @@ type QuotaUsageSet struct { // Note: allocated attribute is available only when nested quota is // enabled. BackupGigabytes QuotaUsage `json:"backup_gigabytes"` + + // Groups is the number of groups that are allowed for each project. + // Note: allocated attribute is available only when nested quota is + // enabled. + Groups QuotaUsage `json:"groups"` } // QuotaUsage is a set of details about a single operational limit that allows diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go index fe1a5d885d..38b413e71a 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go +++ b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go @@ -15,14 +15,15 @@ const FirstTenantID = "555544443333222211110000ffffeeee" var getExpectedJSONBody = ` { - "quota_set" : { - "volumes" : 8, - "snapshots" : 9, - "gigabytes" : 10, - "per_volume_gigabytes" : 11, - "backups" : 12, - "backup_gigabytes" : 13 - } + "quota_set" : { + "volumes" : 8, + "snapshots" : 9, + "gigabytes" : 10, + "per_volume_gigabytes" : 11, + "backups" : 12, + "backup_gigabytes" : 13, + "groups": 14 + } }` var getExpectedQuotaSet = quotasets.QuotaSet{ @@ -32,43 +33,48 @@ var getExpectedQuotaSet = quotasets.QuotaSet{ PerVolumeGigabytes: 11, Backups: 12, BackupGigabytes: 13, + Groups: 14, } var getUsageExpectedJSONBody = ` { - "quota_set" : { - "id": "555544443333222211110000ffffeeee", - "volumes" : { - "in_use": 15, - "limit": 16, - "reserved": 17 - }, - "snapshots" : { - "in_use": 18, - "limit": 19, - "reserved": 20 - }, - "gigabytes" : { - "in_use": 21, - "limit": 22, - "reserved": 23 - }, - "per_volume_gigabytes" : { - "in_use": 24, - "limit": 25, - "reserved": 26 - }, - "backups" : { - "in_use": 27, - "limit": 28, - "reserved": 29 - }, - "backup_gigabytes" : { - "in_use": 30, - "limit": 31, - "reserved": 32 - } - } + "quota_set" : { + "id": "555544443333222211110000ffffeeee", + "volumes" : { + "in_use": 15, + "limit": 16, + "reserved": 17 + }, + "snapshots" : { + "in_use": 18, + "limit": 19, + "reserved": 20 + }, + "gigabytes" : { + "in_use": 21, + "limit": 22, + "reserved": 23 + }, + "per_volume_gigabytes" : { + "in_use": 24, + "limit": 25, + "reserved": 26 + }, + "backups" : { + "in_use": 27, + "limit": 28, + "reserved": 29 + }, + "backup_gigabytes" : { + "in_use": 30, + "limit": 31, + "reserved": 32 + }, + "groups" : { + "in_use": 40, + "limit": 41, + "reserved": 42 + } } }` @@ -80,18 +86,20 @@ var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, + Groups: quotasets.QuotaUsage{InUse: 40, Limit: 41, Reserved: 42}, } var fullUpdateExpectedJSONBody = ` { - "quota_set": { - "volumes": 8, - "snapshots": 9, - "gigabytes": 10, - "per_volume_gigabytes": 11, - "backups": 12, - "backup_gigabytes": 13 - } + "quota_set": { + "volumes": 8, + "snapshots": 9, + "gigabytes": 10, + "per_volume_gigabytes": 11, + "backups": 12, + "backup_gigabytes": 13, + "groups": 14 + } }` var fullUpdateOpts = quotasets.UpdateOpts{ @@ -101,6 +109,7 @@ var fullUpdateOpts = quotasets.UpdateOpts{ PerVolumeGigabytes: gophercloud.IntToPointer(11), Backups: gophercloud.IntToPointer(12), BackupGigabytes: gophercloud.IntToPointer(13), + Groups: gophercloud.IntToPointer(14), } var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ @@ -110,18 +119,19 @@ var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ PerVolumeGigabytes: 11, Backups: 12, BackupGigabytes: 13, + Groups: 14, } var partialUpdateExpectedJSONBody = ` { - "quota_set": { - "volumes": 200, - "snapshots": 0, - "gigabytes": 0, - "per_volume_gigabytes": 0, - "backups": 0, - "backup_gigabytes": 0 - } + "quota_set": { + "volumes": 200, + "snapshots": 0, + "gigabytes": 0, + "per_volume_gigabytes": 0, + "backups": 0, + "backup_gigabytes": 0 + } }` var partialUpdateOpts = quotasets.UpdateOpts{ From ef348b6550902eca77d61502a1feb1d2afbfda35 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 5 Aug 2019 16:40:41 +0300 Subject: [PATCH 0812/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cbeeb00cb..88417c72b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.4.0 (Unreleased) +IMPROVEMENTS + +* Added `blockstorage/extensions/quotasets.results.QuotaSet.Groups` [GH-1668] +* Added `blockstorage/extensions/quotasets.results.QuotaUsageSet.Groups` [GH-1668] + BUG FIXES * Changed `identity/v3/endpoints.ListOpts.RegionID` from `int` to `string` [GH-1664](https://github.com/gophercloud/gophercloud/pull/1664) From 41e9d3b964118025deebfd6810ce751c08cbdc9c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 7 Aug 2019 11:49:33 -0600 Subject: [PATCH 0813/2296] Networking v2: Support two time formats for network resources (#1671) --- .../extensions/layer3/floatingips/results.go | 43 +++++++++- .../layer3/floatingips/testing/fixtures.go | 4 +- .../floatingips/testing/requests_test.go | 4 +- .../v2/extensions/security/groups/results.go | 43 +++++++++- .../security/groups/testing/fixtures.go | 4 +- .../security/groups/testing/requests_test.go | 4 +- .../v2/extensions/subnetpools/results.go | 79 +++++++++++++++++-- .../subnetpools/testing/fixtures.go | 8 +- .../subnetpools/testing/requests_test.go | 4 +- openstack/networking/v2/networks/results.go | 43 +++++++++- .../v2/networks/testing/fixtures.go | 8 +- .../v2/networks/testing/requests_test.go | 4 +- 12 files changed, 218 insertions(+), 30 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index 341cc04c96..0d287dffaa 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -1,6 +1,7 @@ package floatingips import ( + "encoding/json" "time" "github.com/gophercloud/gophercloud" @@ -41,8 +42,8 @@ type FloatingIP struct { // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of // the floating ip last changed, and when it was created. - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"-"` + CreatedAt time.Time `json:"-"` // ProjectID is the project owner of the floating IP. ProjectID string `json:"project_id"` @@ -57,6 +58,44 @@ type FloatingIP struct { Tags []string `json:"tags"` } +func (r *FloatingIP) UnmarshalJSON(b []byte) error { + type tmp FloatingIP + + // Support for older neutron time format + var s1 struct { + tmp + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = FloatingIP(s1.tmp) + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + return nil + } + + // Support for newer neutron time format + var s2 struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + } + + err = json.Unmarshal(b, &s2) + if err != nil { + return err + } + + *r = FloatingIP(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil +} + type commonResult struct { gophercloud.Result } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go index 97c38cc0b8..f7d3f736b9 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go @@ -10,8 +10,8 @@ const FipDNS = `{ "fixed_ip_address": null, "floating_ip_address": "192.0.0.4", "tenant_id": "017d8de156df4177889f31a9bd6edc00", - "created_at": "2019-06-30T04:15:37Z", - "updated_at": "2019-06-30T05:18:49Z", + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49", "status": "DOWN", "port_id": null, "id": "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index 668e9b66f6..26ff84c720 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -28,7 +28,7 @@ func TestList(t *testing.T) { count := 0 - floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := floatingips.ExtractFloatingIPs(page) if err != nil { @@ -73,6 +73,8 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) + if count != 1 { t.Errorf("Expected 1 page, got %d", count) } diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go index 2397b9ee4c..960862bb38 100644 --- a/openstack/networking/v2/extensions/security/groups/results.go +++ b/openstack/networking/v2/extensions/security/groups/results.go @@ -1,6 +1,7 @@ package groups import ( + "encoding/json" "time" "github.com/gophercloud/gophercloud" @@ -29,8 +30,8 @@ type SecGroup struct { // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of the // security group last changed, and when it was created. - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"-"` + CreatedAt time.Time `json:"-"` // ProjectID is the project owner of the security group. ProjectID string `json:"project_id"` @@ -39,6 +40,44 @@ type SecGroup struct { Tags []string `json:"tags"` } +func (r *SecGroup) UnmarshalJSON(b []byte) error { + type tmp SecGroup + + // 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 = SecGroup(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 = SecGroup(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil +} + // SecGroupPage is the page returned by a pager when traversing over a // collection of security groups. type SecGroupPage struct { diff --git a/openstack/networking/v2/extensions/security/groups/testing/fixtures.go b/openstack/networking/v2/extensions/security/groups/testing/fixtures.go index f508d09cd1..0f91859b99 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/fixtures.go +++ b/openstack/networking/v2/extensions/security/groups/testing/fixtures.go @@ -16,8 +16,8 @@ const SecurityGroupListResponse = ` "name": "default", "security_group_rules": [], "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550", - "created_at": "2019-06-30T04:15:37Z", - "updated_at": "2019-06-30T05:18:49Z" + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49" } ] } diff --git a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go index 270b29a1cb..306490f114 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go @@ -28,7 +28,7 @@ func TestList(t *testing.T) { count := 0 - groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := groups.ExtractGroups(page) if err != nil { @@ -42,6 +42,8 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) + if count != 1 { t.Errorf("Expected 1 page, got %d", count) } diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index e97e1e60ae..eadf005781 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -67,10 +67,10 @@ type SubnetPool struct { ProjectID string `json:"project_id"` // CreatedAt is the time at which subnetpool has been created. - CreatedAt time.Time `json:"created_at"` + CreatedAt time.Time `json:"-"` // UpdatedAt is the time at which subnetpool has been created. - UpdatedAt time.Time `json:"updated_at"` + UpdatedAt time.Time `json:"-"` // Prefixes is the list of subnet prefixes to assign to the subnetpool. // Neutron API merges adjacent prefixes and treats them as a single prefix. @@ -118,20 +118,83 @@ type SubnetPool struct { func (r *SubnetPool) UnmarshalJSON(b []byte) error { type tmp SubnetPool - var s struct { + + // Support for older neutron time format + var s1 struct { + tmp + DefaultPrefixLen interface{} `json:"default_prefixlen"` + MinPrefixLen interface{} `json:"min_prefixlen"` + MaxPrefixLen interface{} `json:"max_prefixlen"` + + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = SubnetPool(s1.tmp) + + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + switch t := s1.DefaultPrefixLen.(type) { + case string: + if r.DefaultPrefixLen, err = strconv.Atoi(t); err != nil { + return err + } + case float64: + r.DefaultPrefixLen = int(t) + default: + return fmt.Errorf("DefaultPrefixLen has unexpected type: %T", t) + } + + switch t := s1.MinPrefixLen.(type) { + case string: + if r.MinPrefixLen, err = strconv.Atoi(t); err != nil { + return err + } + case float64: + r.MinPrefixLen = int(t) + default: + return fmt.Errorf("MinPrefixLen has unexpected type: %T", t) + } + + switch t := s1.MaxPrefixLen.(type) { + case string: + if r.MaxPrefixLen, err = strconv.Atoi(t); err != nil { + return err + } + case float64: + r.MaxPrefixLen = int(t) + default: + return fmt.Errorf("MaxPrefixLen has unexpected type: %T", t) + } + + return nil + } + + // Support for newer neutron time format + var s2 struct { tmp DefaultPrefixLen interface{} `json:"default_prefixlen"` MinPrefixLen interface{} `json:"min_prefixlen"` MaxPrefixLen interface{} `json:"max_prefixlen"` + + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } - err := json.Unmarshal(b, &s) + + err = json.Unmarshal(b, &s2) if err != nil { return err } - *r = SubnetPool(s.tmp) + *r = SubnetPool(s2.tmp) + + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) - switch t := s.DefaultPrefixLen.(type) { + switch t := s2.DefaultPrefixLen.(type) { case string: if r.DefaultPrefixLen, err = strconv.Atoi(t); err != nil { return err @@ -142,7 +205,7 @@ func (r *SubnetPool) UnmarshalJSON(b []byte) error { return fmt.Errorf("DefaultPrefixLen has unexpected type: %T", t) } - switch t := s.MinPrefixLen.(type) { + switch t := s2.MinPrefixLen.(type) { case string: if r.MinPrefixLen, err = strconv.Atoi(t); err != nil { return err @@ -153,7 +216,7 @@ func (r *SubnetPool) UnmarshalJSON(b []byte) error { return fmt.Errorf("MinPrefixLen has unexpected type: %T", t) } - switch t := s.MaxPrefixLen.(type) { + switch t := s2.MaxPrefixLen.(type) { case string: if r.MaxPrefixLen, err = strconv.Atoi(t); err != nil { return err diff --git a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go index 2742028905..e6389d410f 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/fixtures.go @@ -55,7 +55,7 @@ const SubnetPoolsListResult = ` }, { "address_scope_id": null, - "created_at": "2017-12-28T07:21:27Z", + "created_at": "2017-12-28T07:21:27", "default_prefixlen": "64", "default_quota": 4, "description": "PublicPool", @@ -72,7 +72,7 @@ const SubnetPoolsListResult = ` "revision_number": 1, "shared": true, "tenant_id": "ceb366d50ad54fe39717df3af60f9945", - "updated_at": "2017-12-28T07:21:27Z" + "updated_at": "2017-12-28T07:21:27" } ] } @@ -159,11 +159,11 @@ const SubnetPoolGetResult = ` "is_default": true, "project_id": "1e2b9857295a4a3e841809ef492812c5", "tenant_id": "1e2b9857295a4a3e841809ef492812c5", - "created_at": "2018-01-01T00:00:01Z", + "created_at": "2018-01-01T00:00:01", "prefixes": [ "2001:db8::a3/64" ], - "updated_at": "2018-01-01T00:10:10Z", + "updated_at": "2018-01-01T00:10:10", "ip_version": 6, "shared": false, "description": "ipv6 prefixes", diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index 3d138d37b3..72df6af960 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -28,7 +28,7 @@ func TestList(t *testing.T) { count := 0 - subnetpools.List(fake.ServiceClient(), subnetpools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := subnetpools.List(fake.ServiceClient(), subnetpools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := subnetpools.ExtractSubnetPools(page) if err != nil { @@ -47,6 +47,8 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) + if count != 1 { t.Errorf("Expected 1 page, got %d", count) } diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index 4786b07b6b..80ca45c06e 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -1,6 +1,7 @@ package networks import ( + "encoding/json" "time" "github.com/gophercloud/gophercloud" @@ -74,8 +75,8 @@ type Network struct { // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of the // network last changed, and when it was created. - UpdatedAt time.Time `json:"updated_at"` - CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"-"` + CreatedAt time.Time `json:"-"` // ProjectID is the project owner of the network. ProjectID string `json:"project_id"` @@ -91,6 +92,44 @@ type Network struct { Tags []string `json:"tags"` } +func (r *Network) UnmarshalJSON(b []byte) error { + type tmp Network + + // 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 = Network(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 = Network(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil +} + // NetworkPage is the page returned by a pager when traversing over a // collection of networks. type NetworkPage struct { diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index 15e3dc0260..7a8b69a129 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -17,8 +17,8 @@ const ListResponse = ` "name": "public", "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "created_at": "2019-06-30T04:15:37Z", - "updated_at": "2019-06-30T05:18:49Z", + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49", "shared": true, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "provider:segmentation_id": 9876543210, @@ -62,8 +62,8 @@ const GetResponse = ` "name": "public", "admin_state_up": true, "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "created_at": "2019-06-30T04:15:37Z", - "updated_at": "2019-06-30T05:18:49Z", + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49", "shared": true, "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", "provider:segmentation_id": 9876543210, diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 7bd24066f2..5721c77c7d 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -30,7 +30,7 @@ func TestList(t *testing.T) { client := fake.ServiceClient() count := 0 - networks.List(client, networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := networks.List(client, networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := networks.ExtractNetworks(page) if err != nil { @@ -43,6 +43,8 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) + if count != 1 { t.Errorf("Expected 1 page, got %d", count) } From 25a84d593c972d3cad3254cba2f548565134e5a0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 7 Aug 2019 11:50:45 -0600 Subject: [PATCH 0814/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88417c72b6..e8c6b8edff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ IMPROVEMENTS 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) ## 0.3.0 (July 31, 2019) From 21b1606ed9b19dc897cedb8380816957ade01793 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Tue, 13 Aug 2019 02:20:40 +1200 Subject: [PATCH 0815/2296] Magnum: Allow setting fixed_network when creating cluster (#1674) --- openstack/containerinfra/v1/clusters/requests.go | 1 + openstack/containerinfra/v1/clusters/results.go | 1 + .../containerinfra/v1/clusters/testing/fixtures.go | 11 ++++++++--- .../v1/clusters/testing/requests_test.go | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 5d5198018c..f574956d6e 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -25,6 +25,7 @@ type CreateOpts struct { MasterFlavorID string `json:"master_flavor_id,omitempty"` Name string `json:"name"` NodeCount *int `json:"node_count,omitempty"` + FixedNetwork string `json:"fixed_network,omitempty"` } // ToClusterCreateMap constructs a request body from CreateOpts. diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index b4a8734c29..1e95e5b6f2 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -87,6 +87,7 @@ type Cluster struct { UUID string `json:"uuid"` UpdatedAt time.Time `json:"updated_at"` UserID string `json:"user_id"` + FixedNetwork string `json:"fixed_network"` } type ClusterPage struct { diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index ff41a02b4d..b215877035 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -49,6 +49,7 @@ var ExpectedCluster = clusters.Cluster{ StatusReason: "Stack CREATE completed successfully", UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), UUID: clusterUUID, + FixedNetwork: "private_network", } var ExpectedCluster2 = clusters.Cluster{ @@ -79,6 +80,7 @@ var ExpectedCluster2 = clusters.Cluster{ StatusReason: "Stack CREATE completed successfully", UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), UUID: clusterUUID2, + FixedNetwork: "private_network", } var ExpectedClusterUUID = clusterUUID @@ -140,7 +142,8 @@ var ClusterGetResponse = fmt.Sprintf(` ], "status_reason":"Stack CREATE completed successfully", "create_timeout":60, - "name":"k8s" + "name":"k8s", + "fixed_network": "private_network" }`, clusterUUID) var ClusterListResponse = fmt.Sprintf(` @@ -177,7 +180,8 @@ var ClusterListResponse = fmt.Sprintf(` "status":"CREATE_COMPLETE", "status_reason":"Stack CREATE completed successfully", "updated_at":"2016-08-29T06:53:24+00:00", - "uuid":"%s" + "uuid":"%s", + "fixed_network": "private_network" }, { "api_address":"https://172.24.4.6:6443", @@ -210,7 +214,8 @@ var ClusterListResponse = fmt.Sprintf(` "status":"CREATE_COMPLETE", "status_reason":"Stack CREATE completed successfully", "updated_at":null, - "uuid":"%s" + "uuid":"%s", + "fixed_network": "private_network" } ] }`, clusterUUID, clusterUUID2) diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index 0efff3f437..21db165a9b 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -29,6 +29,7 @@ func TestCreateCluster(t *testing.T) { MasterFlavorID: "m1.small", Name: "k8s", NodeCount: &nodeCount, + FixedNetwork: "private_network", } sc := fake.ServiceClient() From 33168d2a45f53aef99156d77699ae306a98d0628 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 12 Aug 2019 08:21:42 -0600 Subject: [PATCH 0816/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8c6b8edff..30e7a41a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ IMPROVEMENTS * Added `blockstorage/extensions/quotasets.results.QuotaSet.Groups` [GH-1668] * Added `blockstorage/extensions/quotasets.results.QuotaUsageSet.Groups` [GH-1668] +* Added `containerinfra/v1/clusters.CreateOpts.FixedNetwork` [GH-1674] BUG FIXES From 152a1e7efcd0f6cfd3965391a8bd4530eb042c12 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Tue, 13 Aug 2019 13:15:32 +1200 Subject: [PATCH 0817/2296] Containerinfra: Allow setting fixed_subnet when creating cluster (#1676) --- openstack/containerinfra/v1/clusters/requests.go | 1 + openstack/containerinfra/v1/clusters/results.go | 1 + .../containerinfra/v1/clusters/testing/fixtures.go | 11 ++++++++--- .../v1/clusters/testing/requests_test.go | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index f574956d6e..c7933ef135 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -26,6 +26,7 @@ type CreateOpts struct { Name string `json:"name"` NodeCount *int `json:"node_count,omitempty"` FixedNetwork string `json:"fixed_network,omitempty"` + FixedSubnet string `json:"fixed_subnet,omitempty"` } // ToClusterCreateMap constructs a request body from CreateOpts. diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 1e95e5b6f2..6a0a4808bc 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -88,6 +88,7 @@ type Cluster struct { UpdatedAt time.Time `json:"updated_at"` UserID string `json:"user_id"` FixedNetwork string `json:"fixed_network"` + FixedSubnet string `json:"fixed_subnet"` } type ClusterPage struct { diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index b215877035..320d094f52 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -50,6 +50,7 @@ var ExpectedCluster = clusters.Cluster{ UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), UUID: clusterUUID, FixedNetwork: "private_network", + FixedSubnet: "private_subnet", } var ExpectedCluster2 = clusters.Cluster{ @@ -81,6 +82,7 @@ var ExpectedCluster2 = clusters.Cluster{ UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), UUID: clusterUUID2, FixedNetwork: "private_network", + FixedSubnet: "private_subnet", } var ExpectedClusterUUID = clusterUUID @@ -143,7 +145,8 @@ var ClusterGetResponse = fmt.Sprintf(` "status_reason":"Stack CREATE completed successfully", "create_timeout":60, "name":"k8s", - "fixed_network": "private_network" + "fixed_network": "private_network", + "fixed_subnet": "private_subnet" }`, clusterUUID) var ClusterListResponse = fmt.Sprintf(` @@ -181,7 +184,8 @@ var ClusterListResponse = fmt.Sprintf(` "status_reason":"Stack CREATE completed successfully", "updated_at":"2016-08-29T06:53:24+00:00", "uuid":"%s", - "fixed_network": "private_network" + "fixed_network": "private_network", + "fixed_subnet": "private_subnet" }, { "api_address":"https://172.24.4.6:6443", @@ -215,7 +219,8 @@ var ClusterListResponse = fmt.Sprintf(` "status_reason":"Stack CREATE completed successfully", "updated_at":null, "uuid":"%s", - "fixed_network": "private_network" + "fixed_network": "private_network", + "fixed_subnet": "private_subnet" } ] }`, clusterUUID, clusterUUID2) diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index 21db165a9b..b5550643ec 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -30,6 +30,7 @@ func TestCreateCluster(t *testing.T) { Name: "k8s", NodeCount: &nodeCount, FixedNetwork: "private_network", + FixedSubnet: "private_subnet", } sc := fake.ServiceClient() From 050ca06b556840e0db6c84a87b49939f97990fbc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 12 Aug 2019 19:16:09 -0600 Subject: [PATCH 0818/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30e7a41a31..d1b3e5119b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ IMPROVEMENTS * Added `blockstorage/extensions/quotasets.results.QuotaSet.Groups` [GH-1668] * Added `blockstorage/extensions/quotasets.results.QuotaUsageSet.Groups` [GH-1668] * Added `containerinfra/v1/clusters.CreateOpts.FixedNetwork` [GH-1674] +* Added `containerinfra/v1/clusters.CreateOpts.FixedSubnet` [GH-1676] BUG FIXES From 5b7518307fb5b7148b632cdee6fd3fddacca78f8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 12 Aug 2019 19:19:59 -0600 Subject: [PATCH 0819/2296] Acc Tests: Modify some tests to skip some releases (#1630) --- .../blockstorage/extensions/backups_test.go | 2 ++ .../extensions/schedulerhints_test.go | 2 ++ .../extensions/schedulerstats_test.go | 4 +++ .../blockstorage/extensions/services_test.go | 1 + .../extensions/volumetenants_test.go | 2 ++ .../openstack/blockstorage/v2/volumes_test.go | 2 +- .../blockstorage/v3/quotaset_test.go | 9 ++++++ .../blockstorage/v3/snapshots_test.go | 1 + .../openstack/blockstorage/v3/volumes_test.go | 6 ++++ .../blockstorage/v3/volumetypes_test.go | 1 + .../openstack/compute/v2/quotaset_test.go | 8 +++++ .../openstack/compute/v2/servergroup_test.go | 6 ++++ .../openstack/compute/v2/servers_test.go | 8 +++-- .../openstack/container/v1/capsules_test.go | 14 ++++++++ .../v3/applicationcredentials_test.go | 4 +++ .../openstack/identity/v3/roles_test.go | 4 +++ .../imageservice/v2/imagedata_test.go | 7 ++++ .../imageservice/v2/imageimport_test.go | 12 +++++++ .../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 +++++++++++++++++++ .../loadbalancer/v2/amphorae_test.go | 8 +++++ .../loadbalancer/v2/l7policies_test.go | 7 ++++ .../loadbalancer/v2/listeners_test.go | 7 ++++ .../loadbalancer/v2/loadbalancers_test.go | 28 ++++++++++++++++ .../loadbalancer/v2/monitors_test.go | 7 ++++ .../openstack/loadbalancer/v2/pools_test.go | 7 ++++ .../v2/extensions/attributestags_test.go | 2 ++ .../networking/v2/extensions/mtu/mtu_test.go | 3 ++ .../v2/extensions/qos/rules/rules_test.go | 4 +++ .../qos/ruletypes/ruletypes_test.go | 4 +++ .../v2/extensions/trunks/trunks_test.go | 10 ++++++ .../orchestration/v1/buildinfo_test.go | 2 ++ .../orchestration/v1/stackevents_test.go | 2 ++ .../orchestration/v1/stackresources_test.go | 2 ++ .../openstack/orchestration/v1/stacks_test.go | 2 ++ .../orchestration/v1/stacktemplates_test.go | 4 +++ .../sharedfilesystems/v2/shares_test.go | 21 ++++++++++++ .../sharedfilesystems/v2/snapshots_test.go | 9 ++++++ 40 files changed, 277 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/blockstorage/extensions/backups_test.go b/acceptance/openstack/blockstorage/extensions/backups_test.go index 41e45c5e6d..aa5e6c1b09 100644 --- a/acceptance/openstack/blockstorage/extensions/backups_test.go +++ b/acceptance/openstack/blockstorage/extensions/backups_test.go @@ -13,6 +13,8 @@ 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 bfb99bb070..f2623fca41 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -13,6 +13,8 @@ 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 d0cd973c07..b13cb0ec6b 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -13,6 +13,10 @@ 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.NewBlockStorageV2Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/blockstorage/extensions/services_test.go b/acceptance/openstack/blockstorage/extensions/services_test.go index df38e98405..47043e51c7 100644 --- a/acceptance/openstack/blockstorage/extensions/services_test.go +++ b/acceptance/openstack/blockstorage/extensions/services_test.go @@ -12,6 +12,7 @@ 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/volumetenants_test.go b/acceptance/openstack/blockstorage/extensions/volumetenants_test.go index 5396880c55..4de21abe4a 100644 --- a/acceptance/openstack/blockstorage/extensions/volumetenants_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumetenants_test.go @@ -13,6 +13,8 @@ import ( ) func TestVolumeTenants(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + type volumeWithTenant struct { volumes.Volume volumetenants.VolumeTenantExt diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go index f3b164ca0c..39136bbf00 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -79,7 +79,7 @@ func TestVolumesCreateForceDestroy(t *testing.T) { func TestVolumesCascadeDelete(t *testing.T) { clients.RequireLong(t) - client, err := clients.NewBlockStorageV3Client() + client, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) vol, err := CreateVolume(t, client) diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index 309cdce15e..fe076c7ee7 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -14,6 +14,7 @@ import ( ) func TestQuotasetGet(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, projectID := getClientAndProject(t) @@ -25,6 +26,7 @@ func TestQuotasetGet(t *testing.T) { } func TestQuotasetGetDefaults(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, projectID := getClientAndProject(t) @@ -36,6 +38,7 @@ func TestQuotasetGetDefaults(t *testing.T) { } func TestQuotasetGetUsage(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, projectID := getClientAndProject(t) @@ -67,6 +70,9 @@ var UpdatedQuotas = quotasets.QuotaSet{ } 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) @@ -96,6 +102,9 @@ 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 60bfb70597..1e45c9e96c 100644 --- a/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -12,6 +12,7 @@ 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/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index 999ec1a61e..195e5c6ff9 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -15,6 +15,7 @@ import ( ) func TestVolumes(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") clients.RequireLong(t) client, err := clients.NewBlockStorageV3Client() @@ -68,6 +69,10 @@ 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) @@ -94,6 +99,7 @@ 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 99657d794b..2afe90c1d1 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -12,6 +12,7 @@ import ( ) func TestVolumeTypes(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") clients.RequireAdmin(t) client, err := clients.NewBlockStorageV3Client() diff --git a/acceptance/openstack/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go index 7666456a74..80cc6dadd4 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -17,6 +17,10 @@ import ( 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") @@ -106,6 +110,10 @@ 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") diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/acceptance/openstack/compute/v2/servergroup_test.go index 4b7899e766..0429ff9f5e 100644 --- a/acceptance/openstack/compute/v2/servergroup_test.go +++ b/acceptance/openstack/compute/v2/servergroup_test.go @@ -71,6 +71,12 @@ 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 caacf4613b..1cb303b866 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -85,6 +85,8 @@ func TestServersCreateDestroy(t *testing.T) { } func TestServersWithExtensionsCreateDestroy(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + clients.RequireLong(t) var extendedServer struct { @@ -487,8 +489,9 @@ func TestServersConsoleOutput(t *testing.T) { func TestServersTags(t *testing.T) { clients.RequireLong(t) - clients.SkipRelease(t, "mitaka") - clients.SkipRelease(t, "newton") + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + clients.SkipRelease(t, "stable/ocata") choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) @@ -513,6 +516,7 @@ func TestServersWithExtendedAttributesCreateDestroy(t *testing.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/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index 98fc8e42ec..e7180ed069 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -12,6 +12,12 @@ import ( ) func TestCapsuleBase(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.NewContainerV1Client() th.AssertNoErr(t, err) @@ -55,6 +61,14 @@ func TestCapsuleBase(t *testing.T) { } func TestCapsuleV132(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") + clients.SkipRelease(t, "stable/stein") + client, err := clients.NewContainerV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/identity/v3/applicationcredentials_test.go b/acceptance/openstack/identity/v3/applicationcredentials_test.go index c442daa097..94dde2f0e2 100644 --- a/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -16,6 +16,10 @@ import ( func TestApplicationCredentialsCRD(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") // maps are required, because Application Credential roles are returned in a random order rolesToMap := func(roles []applicationcredentials.Role) map[string]string { diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 05708dcd57..33aa8fa6e3 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -114,6 +114,10 @@ func TestRolesFilterList(t *testing.T) { // For some reason this is not longer working. // It might be a temporary issue. 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") diff --git a/acceptance/openstack/imageservice/v2/imagedata_test.go b/acceptance/openstack/imageservice/v2/imagedata_test.go index d19c38d4a9..2ba59b2cdc 100644 --- a/acceptance/openstack/imageservice/v2/imagedata_test.go +++ b/acceptance/openstack/imageservice/v2/imagedata_test.go @@ -9,6 +9,13 @@ 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 e3f95d6c9a..a5f7f16398 100644 --- a/acceptance/openstack/imageservice/v2/imageimport_test.go +++ b/acceptance/openstack/imageservice/v2/imageimport_test.go @@ -11,6 +11,12 @@ 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) @@ -21,6 +27,12 @@ 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) diff --git a/acceptance/openstack/keymanager/v1/acls_test.go b/acceptance/openstack/keymanager/v1/acls_test.go index 54a123a2b2..c6a0649480 100644 --- a/acceptance/openstack/keymanager/v1/acls_test.go +++ b/acceptance/openstack/keymanager/v1/acls_test.go @@ -12,6 +12,10 @@ 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 0b3bb5f96c..d8bb78970d 100644 --- a/acceptance/openstack/keymanager/v1/containers_test.go +++ b/acceptance/openstack/keymanager/v1/containers_test.go @@ -13,6 +13,10 @@ 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) @@ -56,6 +60,10 @@ 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) @@ -100,6 +108,10 @@ 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) @@ -144,6 +156,10 @@ 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 de44debe91..5105bdbc55 100644 --- a/acceptance/openstack/keymanager/v1/orders_test.go +++ b/acceptance/openstack/keymanager/v1/orders_test.go @@ -14,6 +14,9 @@ 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() @@ -52,6 +55,9 @@ 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 dd00e569ad..fffed0fe7a 100644 --- a/acceptance/openstack/keymanager/v1/secrets_test.go +++ b/acceptance/openstack/keymanager/v1/secrets_test.go @@ -13,6 +13,10 @@ 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) @@ -55,6 +59,10 @@ 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) @@ -80,6 +88,10 @@ 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) @@ -155,6 +167,10 @@ 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) @@ -170,6 +186,10 @@ 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) @@ -189,6 +209,10 @@ 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) @@ -208,6 +232,10 @@ 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) @@ -226,6 +254,10 @@ 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) diff --git a/acceptance/openstack/loadbalancer/v2/amphorae_test.go b/acceptance/openstack/loadbalancer/v2/amphorae_test.go index 0e3369c49a..97151ea7a0 100644 --- a/acceptance/openstack/loadbalancer/v2/amphorae_test.go +++ b/acceptance/openstack/loadbalancer/v2/amphorae_test.go @@ -1,3 +1,5 @@ +// +build acceptance containers capsules + package v2 import ( @@ -10,6 +12,12 @@ 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 { diff --git a/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/acceptance/openstack/loadbalancer/v2/l7policies_test.go index d6f0925870..9e2e899e34 100644 --- a/acceptance/openstack/loadbalancer/v2/l7policies_test.go +++ b/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -11,6 +11,13 @@ 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 f643676544..df470f922f 100644 --- a/acceptance/openstack/loadbalancer/v2/listeners_test.go +++ b/acceptance/openstack/loadbalancer/v2/listeners_test.go @@ -11,6 +11,13 @@ 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/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index e4bae39582..55c954930f 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -17,6 +17,13 @@ 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) @@ -32,6 +39,13 @@ 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") + netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -95,6 +109,13 @@ func TestLoadbalancersListByTags(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") + netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -386,6 +407,13 @@ 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") + 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 93688fdb2e..e13c453eec 100644 --- a/acceptance/openstack/loadbalancer/v2/monitors_test.go +++ b/acceptance/openstack/loadbalancer/v2/monitors_test.go @@ -11,6 +11,13 @@ 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 f7ec2a4ac4..c2174c3c2e 100644 --- a/acceptance/openstack/loadbalancer/v2/pools_test.go +++ b/acceptance/openstack/loadbalancer/v2/pools_test.go @@ -11,6 +11,13 @@ 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/networking/v2/extensions/attributestags_test.go b/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 96dc418bd8..3484e6a912 100644 --- a/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -88,6 +88,8 @@ 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/mtu/mtu_test.go b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 7664729615..47dd195153 100644 --- a/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -17,6 +17,9 @@ 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) 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 be382dba60..b9b451fb59 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -55,6 +55,8 @@ func TestBandwidthLimitRulesCRUD(t *testing.T) { } func TestDSCPMarkingRulesCRUD(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -98,6 +100,8 @@ func TestDSCPMarkingRulesCRUD(t *testing.T) { } func TestMinimumBandwidthRulesCRUD(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + client, err := clients.NewNetworkV2Client() 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 2de4d7b0d9..6040255268 100644 --- a/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go +++ b/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -9,6 +9,10 @@ import ( ) 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) diff --git a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 7f562d65ce..df969b6d8c 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -15,6 +15,8 @@ import ( ) func TestTrunkCRUD(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -99,6 +101,8 @@ func TestTrunkCRUD(t *testing.T) { } func TestTrunkList(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -120,6 +124,8 @@ func TestTrunkList(t *testing.T) { } func TestTrunkSubportOperation(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) @@ -202,6 +208,10 @@ 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") + client, err := clients.NewNetworkV2Client() if err != nil { t.Fatalf("Unable to create a network client: %v", err) diff --git a/acceptance/openstack/orchestration/v1/buildinfo_test.go b/acceptance/openstack/orchestration/v1/buildinfo_test.go index 03806a36d6..c9564f22b9 100644 --- a/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -11,6 +11,8 @@ 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 53c5970a21..21d0c7d26d 100644 --- a/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -11,6 +11,8 @@ import ( ) func TestStackEvents(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + 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 02eb78bbab..5d1bf676e9 100644 --- a/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -12,6 +12,8 @@ import ( ) func TestStackResources(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + 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 a0738d5a97..ac21fed28d 100644 --- a/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/acceptance/openstack/orchestration/v1/stacks_test.go @@ -12,6 +12,8 @@ import ( ) func TestStacksCRUD(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + 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 ba00d88a9f..0db96e2eb9 100644 --- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -12,6 +12,8 @@ import ( ) func TestStackTemplatesCRUD(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) @@ -25,6 +27,8 @@ func TestStackTemplatesCRUD(t *testing.T) { } func TestStackTemplatesValidate(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 2361309e20..28ebc8e969 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -10,6 +10,9 @@ 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) @@ -30,6 +33,9 @@ func TestShareCreate(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) @@ -79,6 +85,9 @@ 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) @@ -102,6 +111,9 @@ 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) @@ -128,6 +140,9 @@ 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) @@ -163,6 +178,9 @@ 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) @@ -206,6 +224,9 @@ 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) diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index d94055902b..5f4313b0b7 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -10,6 +10,9 @@ 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) @@ -38,6 +41,9 @@ 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) @@ -88,6 +94,9 @@ 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 15741a80fb431274cc05ab26be3e1adc60155731 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Wed, 14 Aug 2019 03:35:20 +1200 Subject: [PATCH 0820/2296] Containerinfra: Allow setting floating_ip_enabled for creating cluster (#1677) --- .../containerinfra/v1/clusters/requests.go | 1 + .../containerinfra/v1/clusters/results.go | 1 + .../v1/clusters/testing/fixtures.go | 57 ++++++++++--------- .../v1/clusters/testing/requests_test.go | 2 + 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index c7933ef135..c0b21bc148 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -25,6 +25,7 @@ type CreateOpts struct { MasterFlavorID string `json:"master_flavor_id,omitempty"` Name string `json:"name"` NodeCount *int `json:"node_count,omitempty"` + FloatingIPEnabled *bool `json:"floating_ip_enabled,omitempty"` FixedNetwork string `json:"fixed_network,omitempty"` FixedSubnet string `json:"fixed_subnet,omitempty"` } diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 6a0a4808bc..6e940fa4a3 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -87,6 +87,7 @@ type Cluster struct { UUID string `json:"uuid"` UpdatedAt time.Time `json:"updated_at"` UserID string `json:"user_id"` + FloatingIPEnabled bool `json:"floating_ip_enabled"` FixedNetwork string `json:"fixed_network"` FixedSubnet string `json:"fixed_subnet"` } diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index 320d094f52..d92f8b8a62 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -38,19 +38,20 @@ var ExpectedCluster = clusters.Cluster{ Rel: "bookmark", }, }, - KeyPair: "my-keypair", - MasterAddresses: []string{"172.24.4.6"}, - MasterCount: 1, - Name: "k8s", - NodeAddresses: []string{"172.24.4.13"}, - NodeCount: 1, - StackID: "9c6f1169-7300-4d08-a444-d2be38758719", - Status: "CREATE_COMPLETE", - StatusReason: "Stack CREATE completed successfully", - UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), - UUID: clusterUUID, - FixedNetwork: "private_network", - FixedSubnet: "private_subnet", + KeyPair: "my-keypair", + MasterAddresses: []string{"172.24.4.6"}, + MasterCount: 1, + Name: "k8s", + NodeAddresses: []string{"172.24.4.13"}, + NodeCount: 1, + StackID: "9c6f1169-7300-4d08-a444-d2be38758719", + Status: "CREATE_COMPLETE", + StatusReason: "Stack CREATE completed successfully", + UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), + UUID: clusterUUID, + FloatingIPEnabled: true, + FixedNetwork: "private_network", + FixedSubnet: "private_subnet", } var ExpectedCluster2 = clusters.Cluster{ @@ -70,19 +71,20 @@ var ExpectedCluster2 = clusters.Cluster{ Rel: "bookmark", }, }, - KeyPair: "my-keypair", - MasterAddresses: []string{"172.24.4.6"}, - MasterCount: 1, - Name: "k8s", - NodeAddresses: []string{"172.24.4.13"}, - NodeCount: 1, - StackID: "9c6f1169-7300-4d08-a444-d2be38758719", - Status: "CREATE_COMPLETE", - StatusReason: "Stack CREATE completed successfully", - UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), - UUID: clusterUUID2, - FixedNetwork: "private_network", - FixedSubnet: "private_subnet", + KeyPair: "my-keypair", + MasterAddresses: []string{"172.24.4.6"}, + MasterCount: 1, + Name: "k8s", + NodeAddresses: []string{"172.24.4.13"}, + NodeCount: 1, + StackID: "9c6f1169-7300-4d08-a444-d2be38758719", + Status: "CREATE_COMPLETE", + StatusReason: "Stack CREATE completed successfully", + UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), + UUID: clusterUUID2, + FloatingIPEnabled: true, + FixedNetwork: "private_network", + FixedSubnet: "private_subnet", } var ExpectedClusterUUID = clusterUUID @@ -145,6 +147,7 @@ var ClusterGetResponse = fmt.Sprintf(` "status_reason":"Stack CREATE completed successfully", "create_timeout":60, "name":"k8s", + "floating_ip_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet" }`, clusterUUID) @@ -184,6 +187,7 @@ var ClusterListResponse = fmt.Sprintf(` "status_reason":"Stack CREATE completed successfully", "updated_at":"2016-08-29T06:53:24+00:00", "uuid":"%s", + "floating_ip_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet" }, @@ -219,6 +223,7 @@ var ClusterListResponse = fmt.Sprintf(` "status_reason":"Stack CREATE completed successfully", "updated_at":null, "uuid":"%s", + "floating_ip_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet" } diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index b5550643ec..75814d819c 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -3,6 +3,7 @@ package testing import ( "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -29,6 +30,7 @@ func TestCreateCluster(t *testing.T) { MasterFlavorID: "m1.small", Name: "k8s", NodeCount: &nodeCount, + FloatingIPEnabled: gophercloud.Enabled, FixedNetwork: "private_network", FixedSubnet: "private_subnet", } From 2f51fa62cff864eaa19dcd660f1227e382fa7060 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 13 Aug 2019 09:35:53 -0600 Subject: [PATCH 0821/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1b3e5119b..97a85ffe0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ IMPROVEMENTS * Added `blockstorage/extensions/quotasets.results.QuotaUsageSet.Groups` [GH-1668] * Added `containerinfra/v1/clusters.CreateOpts.FixedNetwork` [GH-1674] * Added `containerinfra/v1/clusters.CreateOpts.FixedSubnet` [GH-1676] +* Added `containerinfra/v1/clusters.CreateOpts.FloatingIPEnabled` [GH-1677] BUG FIXES From df6b889d931de2e000da2015c28db95d69b10d71 Mon Sep 17 00:00:00 2001 From: naumvd95 Date: Tue, 20 Aug 2019 20:53:00 +0200 Subject: [PATCH 0822/2296] [Octavia][Loadbalancers] updated_at/created_at fields (#1681) for: #1680 time.Time object mapped to existing json fields --- .../loadbalancer/v2/loadbalancers/results.go | 46 +++++++++++++++++++ .../v2/loadbalancers/testing/fixtures.go | 18 ++++++++ 2 files changed, 64 insertions(+) diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 73372938cf..2cd54a61df 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -1,6 +1,9 @@ package loadbalancers import ( + "encoding/json" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" @@ -21,6 +24,11 @@ type LoadBalancer struct { // Owner of the LoadBalancer. ProjectID string `json:"project_id"` + // UpdatedAt and CreatedAt contain ISO-8601 timestamps of when the state of the + // loadbalancer last changed, and when it was created. + UpdatedAt time.Time `json:"-"` + CreatedAt time.Time `json:"-"` + // The provisioning status of the LoadBalancer. // This value is ACTIVE, PENDING_CREATE or ERROR. ProvisioningStatus string `json:"provisioning_status"` @@ -65,6 +73,44 @@ type LoadBalancer struct { Tags []string `json:"tags"` } +func (r *LoadBalancer) UnmarshalJSON(b []byte) error { + type tmp LoadBalancer + + // 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 = LoadBalancer(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 = LoadBalancer(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil +} + // StatusTree represents the status of a loadbalancer. type StatusTree struct { Loadbalancer *LoadBalancer `json:"loadbalancer"` diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index a9562e8ffa..83a2ee4225 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" @@ -20,6 +21,8 @@ const LoadbalancersListBody = ` { "id": "c331058c-6a40-4144-948e-b9fb1df9db4b", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49", "name": "web_lb", "description": "lb config for the web tier", "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154", @@ -35,6 +38,8 @@ const LoadbalancersListBody = ` { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49", "name": "db_lb", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -57,6 +62,8 @@ const SingleLoadbalancerBody = ` "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49", "name": "db_lb", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -78,6 +85,8 @@ const PostUpdateLoadbalancerBody = ` "loadbalancer": { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", "project_id": "54030507-44f7-473c-9342-b4d14a95f692", + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49", "name": "NewLoadbalancerName", "description": "lb config for the db tier", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -143,10 +152,15 @@ const GetLoadbalancerStatsBody = ` } ` +var createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") +var updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") + var ( LoadbalancerWeb = loadbalancers.LoadBalancer{ ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", + CreatedAt: createdTime, + UpdatedAt: updatedTime, Name: "web_lb", Description: "lb config for the web tier", VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154", @@ -162,6 +176,8 @@ var ( LoadbalancerDb = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", + CreatedAt: createdTime, + UpdatedAt: updatedTime, Name: "db_lb", Description: "lb config for the db tier", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -177,6 +193,8 @@ var ( LoadbalancerUpdated = loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", ProjectID: "54030507-44f7-473c-9342-b4d14a95f692", + CreatedAt: createdTime, + UpdatedAt: updatedTime, Name: "NewLoadbalancerName", Description: "lb config for the db tier", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", From 316b95d650b18dbc38cb5d830e7d28921e20df2e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 20 Aug 2019 12:53:59 -0600 Subject: [PATCH 0823/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97a85ffe0b..23180c1441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ IMPROVEMENTS * Added `containerinfra/v1/clusters.CreateOpts.FixedNetwork` [GH-1674] * Added `containerinfra/v1/clusters.CreateOpts.FixedSubnet` [GH-1676] * Added `containerinfra/v1/clusters.CreateOpts.FloatingIPEnabled` [GH-1677] +* Added `CreatedAt` and `UpdatedAt` to `loadbalancers/v2/loadbalancers.LoadBalancer` [GH-1681] BUG FIXES From 0024e7a55080553c02d3a8266e578b4d0f2ad000 Mon Sep 17 00:00:00 2001 From: LongKB Date: Thu, 22 Aug 2019 22:52:54 +0700 Subject: [PATCH 0824/2296] Fix InterfaceType enums (#1683) This PR aims to change the type of InterfaceType enums from `DiskType` to `InterfaceType` Signed-off-by: Kim Bao Long --- openstack/baremetal/v1/nodes/requests.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 0de1e66060..aab7a3ff24 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -523,9 +523,9 @@ const ( type InterfaceType string const ( - SATA DiskType = "sata" - SCSI DiskType = "scsi" - SAS DiskType = "sas" + SATA InterfaceType = "sata" + SCSI InterfaceType = "scsi" + SAS InterfaceType = "sas" ) type LogicalDisk struct { From aa85070daf8982f91c77f1aa4269c4f52bf7b4b6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 22 Aug 2019 09:54:43 -0600 Subject: [PATCH 0825/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23180c1441..8062723548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ 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) From 993aa824c19a18630583587c5533e779309559d7 Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Mon, 2 Sep 2019 17:28:32 +0200 Subject: [PATCH 0826/2296] Create Port Forwarding For Floating IP (#1651) * Add port forwarding for floating ip * Fix unit tests * delete unused test * Add port forwarding as its own resource * correct format * minor fixes * Fixed documentation, removed projectid --- acceptance/clients/conditions.go | 8 +++ .../v2/extensions/layer3/floatingips_test.go | 2 +- .../networking/v2/extensions/layer3/layer3.go | 33 ++++++++- .../extensions/layer3/portforwardings_test.go | 57 ++++++++++++++++ .../layer3/floatingips/testing/fixtures.go | 27 ++++++++ .../extensions/layer3/portforwarding/doc.go | 20 ++++++ .../layer3/portforwarding/requests.go | 37 ++++++++++ .../layer3/portforwarding/results.go | 48 +++++++++++++ .../layer3/portforwarding/testing/doc.go | 2 + .../portforwarding/testing/requests_test.go | 68 +++++++++++++++++++ .../extensions/layer3/portforwarding/urls.go | 14 ++++ 11 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go create mode 100644 openstack/networking/v2/extensions/layer3/portforwarding/doc.go create mode 100644 openstack/networking/v2/extensions/layer3/portforwarding/requests.go create mode 100644 openstack/networking/v2/extensions/layer3/portforwarding/results.go create mode 100644 openstack/networking/v2/extensions/layer3/portforwarding/testing/doc.go create mode 100644 openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/layer3/portforwarding/urls.go diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index bc8b306d08..e0390ff4fa 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -19,6 +19,14 @@ func RequireNonAdmin(t *testing.T) { } } +// RequirePortForwarding will restrict a test to only be run in environments +// that support port forwarding +func RequirePortForwarding(t *testing.T) { + if os.Getenv("OS_PORTFORWARDING_ENVIRONMENT") == "" { + t.Skip("this test requires support for port forwarding") + } +} + // RequireDNS will restrict a test to only be run in environments // that support DNSaaS. func RequireDNS(t *testing.T) { diff --git a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index ac50e9efaf..5d87120bbc 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -69,7 +69,7 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { port, err := networking.CreatePort(t, client, network.ID, subnet.ID) th.AssertNoErr(t, err) - // not required, since "DeleteRouterInterface" above removes the port + // not required, since "DeleteRouterInterface" below removes the port // defer networking.DeletePort(t, client, port.ID) _, err = CreateRouterInterface(t, client, port.ID, router.ID) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index abc562c837..8f01fbb552 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -9,6 +9,8 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/portforwarding" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/testhelper" @@ -64,6 +66,33 @@ func CreateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient return floatingIP, err } +// CreatePortForwarding creates a port forwarding for a given floating IP +// and port. An error will be returned if the creation failed. +func CreatePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID string, portID string, portFixedIPs []ports.IP) (*portforwarding.PortForwarding, error) { + t.Logf("Attempting to create Port forwarding for floating IP with ID: %s", fipID) + + fixedIP := portFixedIPs[0] + internalIP := fixedIP.IPAddress + createOpts := &portforwarding.CreateOpts{ + Protocol: "tcp", + InternalPort: 25, + ExternalPort: 2230, + InternalIPAddress: internalIP, + InternalPortID: portID, + } + + pf, err := portforwarding.Create(client, fipID, createOpts).Extract() + if err != nil { + return pf, err + } + + t.Logf("Created Port Forwarding.") + + th.AssertEquals(t, pf.Protocol, "tcp") + + return pf, err +} + // CreateExternalRouter creates a router on the external network. This requires // the OS_EXTGW_ID environment variable to be set. An error is returned if the // creation failed. @@ -80,10 +109,8 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou t.Logf("Attempting to create external router: %s", routerName) adminStateUp := true - enableSNAT := false gatewayInfo := routers.GatewayInfo{ - NetworkID: choices.ExternalNetworkID, - EnableSNAT: &enableSNAT, + NetworkID: choices.ExternalNetworkID, } createOpts := routers.CreateOpts{ diff --git a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go new file mode 100644 index 0000000000..0910ab2395 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -0,0 +1,57 @@ +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/layer3/floatingips" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestLayer3PortForwardingsCreateDelete(t *testing.T) { + clients.RequirePortForwarding(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + // Create Network + 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 := CreateExternalRouter(t, client) + th.AssertNoErr(t, err) + defer DeleteRouter(t, client, router.ID) + + port, err := networking.CreatePort(t, client, network.ID, subnet.ID) + th.AssertNoErr(t, err) + // not required, since "DeleteRouterInterface" below removes the port + // defer networking.DeletePort(t, client, port.ID) + + _, err = CreateRouterInterface(t, client, port.ID, router.ID) + th.AssertNoErr(t, err) + defer DeleteRouterInterface(t, client, port.ID, router.ID) + + fip, err := CreateFloatingIP(t, client, choices.ExternalNetworkID, "") + th.AssertNoErr(t, err) + defer DeleteFloatingIP(t, client, fip.ID) + + newFip, err := floatingips.Get(client, fip.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newFip) + + pf, err := CreatePortForwarding(t, client, fip.ID, port.ID, port.FixedIPs) + th.AssertNoErr(t, err) + tools.PrintResource(t, pf) + +} diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go index f7d3f736b9..df58bad1d4 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go @@ -36,6 +36,24 @@ const FipNoDNS = `{ "dns_name": "" }` +const PoFw = `{ + "protocol": "tcp", + "internal_ip_address": "10.0.0.24", + "internal_port": 25, + "internal_port_id": "070ef0b2-0175-4299-be5c-01fea8cca522", + "external_port": 2229, + "id": "1798dc82-c0ed-4b79-b12d-4c3c18f90eb2" + }` + +const PoFw_second = `{ + "protocol": "tcp", + "internal_ip_address": "10.0.0.11", + "internal_port": 25, + "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", + "external_port": 2230, + "id": "e0a0274e-4d19-4eab-9e12-9e77a8caf3ea" + }` + var ListResponse = fmt.Sprintf(` { "floatingips": [ @@ -52,3 +70,12 @@ var ListResponseDNS = fmt.Sprintf(` ] } `, FipDNS) + +var ListPortForwardingsResponse = fmt.Sprintf(` +{ + "port_forwardings": [ +%s, +%s + ] +} +`, PoFw, PoFw_second) diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go new file mode 100644 index 0000000000..a85bbf376d --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go @@ -0,0 +1,20 @@ +/* +package portforwarding enables management and retrieval of port forwarding resources for Floating IPs from the +OpenStack Networking service. + +Example to Create a Port Forwarding for a floating IP + + createOpts := &portforwarding.CreateOpts{ + Protocol: "tcp", + InternalPort: 25, + ExternalPort: 2230, + InternalIPAddress: internalIP, + InternalPortID: portID, + } + + pf, err := portforwarding.Create(networkingClient, floatingIPID, createOpts).Extract() + if err != nil { + panic(err) + } +*/ +package portforwarding diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go new file mode 100644 index 0000000000..8d1c36f399 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -0,0 +1,37 @@ +package portforwarding + +import "github.com/gophercloud/gophercloud" + +// CreateOpts contains all the values needed to create a new port forwarding +// resource. All attributes are required. +type CreateOpts struct { + InternalPortID string `json:"internal_port_id"` + InternalIPAddress string `json:"internal_ip_address"` + InternalPort int `json:"internal_port"` + ExternalPort int `json:"external_port"` + Protocol string `json:"protocol"` +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToPortForwardingCreateMap() (map[string]interface{}, error) +} + +// ToPortForwardingCreateMap allows CreateOpts to satisfy the CreateOptsBuilder +// interface +func (opts CreateOpts) ToPortForwardingCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "port_forwarding") +} + +// Create accepts a CreateOpts struct and uses the values provided to create a +// new port forwarding for an existing floating IP. +func Create(c *gophercloud.ServiceClient, floatingIpId string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToPortForwardingCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(portForwardingUrl(c, floatingIpId), b, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go new file mode 100644 index 0000000000..adf93686d3 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -0,0 +1,48 @@ +package portforwarding + +import ( + "github.com/gophercloud/gophercloud" +) + +type PortForwarding struct { + // The ID of the floating IP port forwarding + ID string `json:"id"` + + // The ID of the Neutron port associated to the floating IP port forwarding. + InternalPortID string `json:"internal_port_id"` + + // The TCP/UDP/other protocol port number of the port forwarding’s floating IP address. + ExternalPort int `json:"external_port"` + + // The IP protocol used in the floating IP port forwarding. + Protocol string `json:"protocol"` + + // The TCP/UDP/other protocol port number of the Neutron port fixed + // IP address associated to the floating ip port forwarding. + InternalPort int `json:"internal_port"` + + // The fixed IPv4 address of the Neutron port associated + // to the floating IP port forwarding. + InternalIPAddress string `json:"internal_ip_address"` +} + +type commonResult struct { + gophercloud.Result +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a PortForwarding. +type CreateResult struct { + commonResult +} + +// Extract will extract a Port Forwarding resource from a result. +func (r commonResult) Extract() (*PortForwarding, error) { + var s PortForwarding + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "port_forwarding") +} diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/doc.go new file mode 100644 index 0000000000..b307609afe --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/doc.go @@ -0,0 +1,2 @@ +// port forwarding unit tests +package testing diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go new file mode 100644 index 0000000000..28e661ba60 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -0,0 +1,68 @@ +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/portforwarding" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/floatingips/2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e/port_forwardings", 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, ` +{ + "port_forwarding": { + "protocol": "tcp", + "internal_ip_address": "10.0.0.11", + "internal_port": 25, + "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", + "external_port": 2230 + } +} + + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "port_forwarding": { + "protocol": "tcp", + "internal_ip_address": "10.0.0.11", + "internal_port": 25, + "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", + "external_port": 2230, + "id": "725ade3c-9760-4880-8080-8fc2dbab9acc" + } +}`) + }) + + options := portforwarding.CreateOpts{ + Protocol: "tcp", + InternalIPAddress: "10.0.0.11", + InternalPort: 25, + ExternalPort: 2230, + InternalPortID: "1238be08-a2a8-4b8d-addf-fb5e2250e480", + } + + pf, err := portforwarding.Create(fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "725ade3c-9760-4880-8080-8fc2dbab9acc", pf.ID) + th.AssertEquals(t, "10.0.0.11", pf.InternalIPAddress) + th.AssertEquals(t, 25, pf.InternalPort) + th.AssertEquals(t, "1238be08-a2a8-4b8d-addf-fb5e2250e480", pf.InternalPortID) + th.AssertEquals(t, 2230, pf.ExternalPort) + th.AssertEquals(t, "tcp", pf.Protocol) +} diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/urls.go b/openstack/networking/v2/extensions/layer3/portforwarding/urls.go new file mode 100644 index 0000000000..13eb158016 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/portforwarding/urls.go @@ -0,0 +1,14 @@ +package portforwarding + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "floatingips" +const portForwardingPath = "port_forwardings" + +func portForwardingUrl(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, portForwardingPath) +} + +func singlePortForwardingUrl(c *gophercloud.ServiceClient, id string, portForwardingID string) string { + return c.ServiceURL(resourcePath, id, portForwardingPath, portForwardingID) +} From eb67707a43b2594c074efe58f09ca969b6564d5f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Sep 2019 09:30:38 -0600 Subject: [PATCH 0827/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8062723548..d65ea4446d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ IMPROVEMENTS * Added `containerinfra/v1/clusters.CreateOpts.FixedSubnet` [GH-1676] * Added `containerinfra/v1/clusters.CreateOpts.FloatingIPEnabled` [GH-1677] * Added `CreatedAt` and `UpdatedAt` to `loadbalancers/v2/loadbalancers.LoadBalancer` [GH-1681] +* Added `networking/v2/extensions/layer3/portforwarding.Create` [GH-1651] BUG FIXES From 94f7560b43a6f0960c579428cad272cd0b35e09a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Sep 2019 09:33:14 -0600 Subject: [PATCH 0828/2296] Update fixtures.go --- .../layer3/floatingips/testing/fixtures.go | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go index df58bad1d4..f7d3f736b9 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/fixtures.go @@ -36,24 +36,6 @@ const FipNoDNS = `{ "dns_name": "" }` -const PoFw = `{ - "protocol": "tcp", - "internal_ip_address": "10.0.0.24", - "internal_port": 25, - "internal_port_id": "070ef0b2-0175-4299-be5c-01fea8cca522", - "external_port": 2229, - "id": "1798dc82-c0ed-4b79-b12d-4c3c18f90eb2" - }` - -const PoFw_second = `{ - "protocol": "tcp", - "internal_ip_address": "10.0.0.11", - "internal_port": 25, - "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", - "external_port": 2230, - "id": "e0a0274e-4d19-4eab-9e12-9e77a8caf3ea" - }` - var ListResponse = fmt.Sprintf(` { "floatingips": [ @@ -70,12 +52,3 @@ var ListResponseDNS = fmt.Sprintf(` ] } `, FipDNS) - -var ListPortForwardingsResponse = fmt.Sprintf(` -{ - "port_forwardings": [ -%s, -%s - ] -} -`, PoFw, PoFw_second) From 42e94bf06b106711642266d26fd92e7574638df1 Mon Sep 17 00:00:00 2001 From: Andrew Karpow Date: Mon, 2 Sep 2019 17:43:08 +0200 Subject: [PATCH 0829/2296] Network v2: add listDHCPNetworks for agents extension (#1686) this adds the listDHCPNetworks call for agents, that fetches a list of networks scheduled to a specific agent. --- .../v2/extensions/agents/requests.go | 7 ++++ .../v2/extensions/agents/results.go | 17 +++++++++ .../v2/extensions/agents/testing/fixtures.go | 38 +++++++++++++++++++ .../agents/testing/requests_test.go | 32 ++++++++++++++++ .../networking/v2/extensions/agents/urls.go | 9 +++++ 5 files changed, 103 insertions(+) diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 566f4dea46..3d5d77f741 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -64,3 +64,10 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(getURL(c, id), &r.Body, nil) return } + +// ListDHCPNetworks returns a list of networks scheduled to a specific +// dhcp agent +func ListDHCPNetworks(c *gophercloud.ServiceClient, id string) (r ListDHCPNetworksResult) { + _, r.Err = c.Get(listDHCPNetworksURL(c, id), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/agents/results.go b/openstack/networking/v2/extensions/agents/results.go index de4ce00646..2b3f0194ba 100644 --- a/openstack/networking/v2/extensions/agents/results.go +++ b/openstack/networking/v2/extensions/agents/results.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/pagination" ) @@ -126,3 +127,19 @@ func ExtractAgents(r pagination.Page) ([]Agent, error) { err := (r.(AgentPage)).ExtractInto(&s) return s.Agents, err } + +// ListDHCPNetworksResult is the response from a List operation. +// Call its Extract method to interpret it as networks. +type ListDHCPNetworksResult struct { + gophercloud.Result +} + +// Extract interprets any ListDHCPNetworksResult as an array of networks. +func (r ListDHCPNetworksResult) Extract() ([]networks.Network, error) { + var s struct { + Networks []networks.Network `json:"networks"` + } + + err := r.ExtractInto(&s) + return s.Networks, err +} diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures.go b/openstack/networking/v2/extensions/agents/testing/fixtures.go index e47ca79003..bfb47841c1 100644 --- a/openstack/networking/v2/extensions/agents/testing/fixtures.go +++ b/openstack/networking/v2/extensions/agents/testing/fixtures.go @@ -123,3 +123,41 @@ const AgentsGetResult = ` } } ` + +// AgentDHCPNetworksListResult represents raw response for the ListDHCPNetworks request. +const AgentDHCPNetworksListResult = ` +{ + "networks": [ + { + "admin_state_up": true, + "availability_zone_hints": [], + "availability_zones": [ + "nova" + ], + "created_at": "2016-03-08T20:19:41", + "dns_domain": "my-domain.org.", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ipv4_address_scope": null, + "ipv6_address_scope": null, + "l2_adjacency": false, + "mtu": 1500, + "name": "net1", + "port_security_enabled": true, + "project_id": "4fd44f30292945e481c7b8a0c8908869", + "qos_policy_id": "6a8454ade84346f59e8d40665f878b2e", + "revision_number": 1, + "router:external": false, + "shared": false, + "status": "ACTIVE", + "subnets": [ + "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + ], + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "updated_at": "2016-03-08T20:19:41", + "vlan_transparent": true, + "description": "", + "is_default": false + } + ] +} +` diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index 3ee2f84dac..afbf2c1c87 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -88,3 +88,35 @@ func TestGet(t *testing.T) { "enable_distributed_routing": false, }) } + +func TestListDHCPNetworks(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/dhcp-networks", 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, AgentDHCPNetworksListResult) + }) + + s, err := agents.ListDHCPNetworks(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() + th.AssertNoErr(t, err) + + var nilSlice []string + th.AssertEquals(t, len(s), 1) + th.AssertEquals(t, s[0].ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertEquals(t, s[0].AdminStateUp, true) + th.AssertEquals(t, s[0].ProjectID, "4fd44f30292945e481c7b8a0c8908869") + th.AssertEquals(t, s[0].Shared, false) + th.AssertEquals(t, s[0].Name, "net1") + th.AssertEquals(t, s[0].Status, "ACTIVE") + th.AssertDeepEquals(t, s[0].Tags, nilSlice) + th.AssertEquals(t, s[0].TenantID, "4fd44f30292945e481c7b8a0c8908869") + th.AssertDeepEquals(t, s[0].AvailabilityZoneHints, []string{}) + th.AssertDeepEquals(t, s[0].Subnets, []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}) + +} diff --git a/openstack/networking/v2/extensions/agents/urls.go b/openstack/networking/v2/extensions/agents/urls.go index 33b84ee75f..1386a848ef 100644 --- a/openstack/networking/v2/extensions/agents/urls.go +++ b/openstack/networking/v2/extensions/agents/urls.go @@ -3,6 +3,7 @@ package agents import "github.com/gophercloud/gophercloud" const resourcePath = "agents" +const dhcpNetworksResourcePath = "dhcp-networks" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) @@ -19,3 +20,11 @@ func listURL(c *gophercloud.ServiceClient) string { func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func dhcpNetworksURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, dhcpNetworksResourcePath) +} + +func listDHCPNetworksURL(c *gophercloud.ServiceClient, id string) string { + return dhcpNetworksURL(c, id) +} From 363d7a098dd08e47e7bc4a2bddda0a1e438dec7e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Sep 2019 09:44:00 -0600 Subject: [PATCH 0830/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d65ea4446d..57d37dcdef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ IMPROVEMENTS * Added `containerinfra/v1/clusters.CreateOpts.FloatingIPEnabled` [GH-1677] * Added `CreatedAt` and `UpdatedAt` to `loadbalancers/v2/loadbalancers.LoadBalancer` [GH-1681] * Added `networking/v2/extensions/layer3/portforwarding.Create` [GH-1651] +* Added `networking/v2/extensions/agents.ListDHCPNetworks` [GH-1686] BUG FIXES From 56495ee0456a4b5d704e89570bdca20578308432 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 3 Sep 2019 06:31:49 +0100 Subject: [PATCH 0831/2296] Compute V2: add tags Check call (#1679) * Compute V2: add tags extension with List call Add the new tags extension with the List method. Provide unit test and documentation. * Compute V2: add tags Check call Add Check method for tags extension. Update docs and inline comments. Provide unit tests. --- openstack/compute/v2/extensions/tags/doc.go | 32 +++++++++ .../compute/v2/extensions/tags/requests.go | 21 ++++++ .../compute/v2/extensions/tags/results.go | 37 ++++++++++ .../compute/v2/extensions/tags/testing/doc.go | 2 + .../v2/extensions/tags/testing/fixtures.go | 8 +++ .../extensions/tags/testing/requests_test.go | 67 +++++++++++++++++++ openstack/compute/v2/extensions/tags/urls.go | 24 +++++++ 7 files changed, 191 insertions(+) create mode 100644 openstack/compute/v2/extensions/tags/doc.go create mode 100644 openstack/compute/v2/extensions/tags/requests.go create mode 100644 openstack/compute/v2/extensions/tags/results.go create mode 100644 openstack/compute/v2/extensions/tags/testing/doc.go create mode 100644 openstack/compute/v2/extensions/tags/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/tags/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/tags/urls.go diff --git a/openstack/compute/v2/extensions/tags/doc.go b/openstack/compute/v2/extensions/tags/doc.go new file mode 100644 index 0000000000..91b58b9ff5 --- /dev/null +++ b/openstack/compute/v2/extensions/tags/doc.go @@ -0,0 +1,32 @@ +/* +Package tags manages Tags on Compute V2 servers. + +This extension is available since 2.26 Compute V2 API microversion. + +Example to List all server Tags + + client.Microversion = "2.62" + + serverTags, err := tags.List(client, serverID).Extract() + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Tags: %v\n", serverTags) + +Example to Check if the specific Tag exists on a server. + + client.Microversion = "2.62" + + 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) + } +*/ +package tags diff --git a/openstack/compute/v2/extensions/tags/requests.go b/openstack/compute/v2/extensions/tags/requests.go new file mode 100644 index 0000000000..fd490a669b --- /dev/null +++ b/openstack/compute/v2/extensions/tags/requests.go @@ -0,0 +1,21 @@ +package tags + +import "github.com/gophercloud/gophercloud" + +// List all tags on a server. +func List(client *gophercloud.ServiceClient, serverID string) (r ListResult) { + url := listURL(client, serverID) + _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Check if a tag exists on a server. +func Check(client *gophercloud.ServiceClient, serverID, tag string) (r CheckResult) { + url := checkURL(client, serverID, tag) + _, r.Err = client.Get(url, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/compute/v2/extensions/tags/results.go b/openstack/compute/v2/extensions/tags/results.go new file mode 100644 index 0000000000..08d310cb30 --- /dev/null +++ b/openstack/compute/v2/extensions/tags/results.go @@ -0,0 +1,37 @@ +package tags + +import "github.com/gophercloud/gophercloud" + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a tags resource. +func (r commonResult) Extract() ([]string, error) { + var s struct { + Tags []string `json:"tags"` + } + err := r.ExtractInto(&s) + return s.Tags, err +} + +type ListResult struct { + commonResult +} + +// CheckResult is the result from the Check operation. +type CheckResult struct { + gophercloud.Result +} + +func (r CheckResult) Extract() (bool, error) { + exists := r.Err == nil + + if r.Err != nil { + if _, ok := r.Err.(gophercloud.ErrDefault404); ok { + r.Err = nil + } + } + + return exists, r.Err +} diff --git a/openstack/compute/v2/extensions/tags/testing/doc.go b/openstack/compute/v2/extensions/tags/testing/doc.go new file mode 100644 index 0000000000..fce31e5224 --- /dev/null +++ b/openstack/compute/v2/extensions/tags/testing/doc.go @@ -0,0 +1,2 @@ +// tags unit tests +package testing diff --git a/openstack/compute/v2/extensions/tags/testing/fixtures.go b/openstack/compute/v2/extensions/tags/testing/fixtures.go new file mode 100644 index 0000000000..4a51bdf539 --- /dev/null +++ b/openstack/compute/v2/extensions/tags/testing/fixtures.go @@ -0,0 +1,8 @@ +package testing + +// TagsListResponse represents a raw tags response. +const TagsListResponse = ` +{ + "tags": ["foo", "bar", "baz"] +} +` diff --git a/openstack/compute/v2/extensions/tags/testing/requests_test.go b/openstack/compute/v2/extensions/tags/testing/requests_test.go new file mode 100644 index 0000000000..602f64b71d --- /dev/null +++ b/openstack/compute/v2/extensions/tags/testing/requests_test.go @@ -0,0 +1,67 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tags" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/uuid1/tags", 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) + + _, err := fmt.Fprintf(w, TagsListResponse) + th.AssertNoErr(t, err) + }) + + expected := []string{"foo", "bar", "baz"} + actual, err := tags.List(client.ServiceClient(), "uuid1").Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) +} + +func TestCheckOk(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/uuid1/tags/foo", 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.StatusNoContent) + }) + + exists, err := tags.Check(client.ServiceClient(), "uuid1", "foo").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, exists) +} + +func TestCheckFail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/uuid1/tags/bar", 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.StatusNotFound) + }) + + exists, err := tags.Check(client.ServiceClient(), "uuid1", "bar").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, false, exists) +} diff --git a/openstack/compute/v2/extensions/tags/urls.go b/openstack/compute/v2/extensions/tags/urls.go new file mode 100644 index 0000000000..273393502e --- /dev/null +++ b/openstack/compute/v2/extensions/tags/urls.go @@ -0,0 +1,24 @@ +package tags + +import "github.com/gophercloud/gophercloud" + +const ( + rootResourcePath = "servers" + resourcePath = "tags" +) + +func rootURL(c *gophercloud.ServiceClient, serverID string) string { + return c.ServiceURL(rootResourcePath, serverID, resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, serverID, tag string) string { + return c.ServiceURL(rootResourcePath, serverID, resourcePath, tag) +} + +func listURL(c *gophercloud.ServiceClient, serverID string) string { + return rootURL(c, serverID) +} + +func checkURL(c *gophercloud.ServiceClient, serverID, tag string) string { + return resourceURL(c, serverID, tag) +} From 5a84890f52c6c20aec49a0d3dee832c4c7f2701b Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 4 Sep 2019 03:28:23 +0200 Subject: [PATCH 0832/2296] Add delete function as well as unit & acceptance tests (#1652) --- .../networking/v2/extensions/layer3/layer3.go | 13 +++++++++++++ .../v2/extensions/layer3/portforwardings_test.go | 1 + .../v2/extensions/layer3/portforwarding/doc.go | 10 ++++++++++ .../extensions/layer3/portforwarding/requests.go | 6 ++++++ .../v2/extensions/layer3/portforwarding/results.go | 6 ++++++ .../layer3/portforwarding/testing/requests_test.go | 14 ++++++++++++++ 6 files changed, 50 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 8f01fbb552..996df6cfeb 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -93,6 +93,19 @@ func CreatePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID return pf, err } +// DeletePortForwarding deletes a Port Forwarding with a given ID and a given floating IP ID. +// A fatal error is returned if the deletion fails. Works best as a deferred function +func DeletePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID string, pfID string) { + t.Logf("Attempting to delete the port forwarding with ID %s for floating IP with ID %s", pfID, fipID) + + err := portforwarding.Delete(client, fipID, pfID).ExtractErr() + if err != nil { + t.Fatalf("Failed to delete Port forwarding with ID %s for floating IP with ID %s", pfID, fipID) + } + t.Logf("Successfully deleted the port forwarding with ID %s for floating IP with ID %s", pfID, fipID) + +} + // CreateExternalRouter creates a router on the external network. This requires // the OS_EXTGW_ID environment variable to be set. An error is returned if the // creation failed. diff --git a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index 0910ab2395..2423d81b06 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -52,6 +52,7 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { pf, err := CreatePortForwarding(t, client, fip.ID, port.ID, port.FixedIPs) th.AssertNoErr(t, err) + defer DeletePortForwarding(t, client, fip.ID, pf.ID) tools.PrintResource(t, pf) } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go index a85bbf376d..acc68b1ed5 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go @@ -13,6 +13,16 @@ Example to Create a Port Forwarding for a floating IP } pf, err := portforwarding.Create(networkingClient, floatingIPID, createOpts).Extract() + + if err != nil { + panic(err) + } + +Example to Delete a Port forwarding + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" + err := floatingips.Delete(networkClient, fipID, pfID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index 8d1c36f399..6809e659cf 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -35,3 +35,9 @@ func Create(c *gophercloud.ServiceClient, floatingIpId string, opts CreateOptsBu _, r.Err = c.Post(portForwardingUrl(c, floatingIpId), b, &r.Body, nil) return } + +// Delete will permanently delete a particular port forwarding for a given floating ID. +func Delete(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r DeleteResult) { + _, r.Err = c.Delete(singlePortForwardingUrl(c, floatingIpId, pfId), nil) + return +} diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go index adf93686d3..06ef608385 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/results.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -36,6 +36,12 @@ 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 +} + // Extract will extract a Port Forwarding resource from a result. func (r commonResult) Extract() (*PortForwarding, error) { var s PortForwarding diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go index 28e661ba60..926027d72d 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -66,3 +66,17 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, 2230, pf.ExternalPort) th.AssertEquals(t, "tcp", pf.Protocol) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7/port_forwardings/725ade3c-9760-4880-8080-8fc2dbab9acc", 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) + }) + + res := portforwarding.Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc") + th.AssertNoErr(t, res.Err) +} From d3ed8bcb4560158a1afe738c798760c8d969ea1e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Sep 2019 19:30:20 -0600 Subject: [PATCH 0833/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d37dcdef..927609e6b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ IMPROVEMENTS * Added `CreatedAt` and `UpdatedAt` to `loadbalancers/v2/loadbalancers.LoadBalancer` [GH-1681] * Added `networking/v2/extensions/layer3/portforwarding.Create` [GH-1651] * Added `networking/v2/extensions/agents.ListDHCPNetworks` [GH-1686] +* Added `networking/v2/extensions/layer3/portforwarding.Delete` [GH-1652] +* Added `compute/v2/extensions/tags.List` [GH-1679] +* Added `compute/v2/extensions/tags.Check` [GH-1679] BUG FIXES From a84f8b330d669c6dff53ee9743a7579ac9541b69 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Sep 2019 19:31:01 -0600 Subject: [PATCH 0834/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 927609e6b1..32f03d54af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.4.0 (Unreleased) +## 0.5.0 (Unreleased) + +## 0.4.0 (September 3, 2019) IMPROVEMENTS From 8f47ec3ee2dbf1aaaa2467807f4f7d1eb1d80c4f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 8 Sep 2019 10:56:06 -0600 Subject: [PATCH 0835/2296] Compute v2: Adding VolumeType to bootfromvolume (#1690) --- .../v2/extensions/bootfromvolume/requests.go | 4 +++ .../bootfromvolume/testing/fixtures.go | 35 +++++++++++++++++++ .../bootfromvolume/testing/requests_test.go | 7 +++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go index 30c6170117..d2346d4b42 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -75,6 +75,10 @@ type BlockDevice struct { // DiskBus is the bus type of the block devices. // Examples of this are ide, usb, virtio, scsi, etc. DiskBus string `json:"disk_bus,omitempty"` + + // VolumeType is the volume type of the block device. + // This requires Compute API microversion 2.67 or later. + VolumeType string `json:"volume_type,omitempty"` } // CreateOptsExt is a structure that extends the server `CreateOpts` structure diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go index 484e40a09a..cb89173aaa 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures.go @@ -273,3 +273,38 @@ var ImageAndExistingVolumeRequest = bootfromvolume.CreateOptsExt{ }, }, } + +const ExpectedNewVolumeTypeRequest = ` +{ + "server": { + "name":"createdserver", + "flavorRef":"performance1-1", + "imageRef":"", + "block_device_mapping_v2":[ + { + "uuid":"123456", + "source_type":"image", + "destination_type":"volume", + "boot_index": 0, + "delete_on_termination": true, + "volume_size": 10, + "volume_type": "ssd" + } + ] + } +} +` + +var NewVolumeTypeRequest = bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: BaseCreateOpts, + BlockDevice: []bootfromvolume.BlockDevice{ + { + UUID: "123456", + SourceType: bootfromvolume.SourceImage, + DestinationType: bootfromvolume.DestinationVolume, + VolumeSize: 10, + DeleteOnTermination: true, + VolumeType: "ssd", + }, + }, +} diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go index 6b59ff722e..69afc865c9 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go @@ -7,7 +7,6 @@ import ( ) func TestBootFromNewVolume(t *testing.T) { - actual, err := NewVolumeRequest.ToServerCreateMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedNewVolumeRequest, actual) @@ -42,3 +41,9 @@ func TestAttachExistingVolume(t *testing.T) { th.AssertNoErr(t, err) th.CheckJSONEquals(t, ExpectedImageAndExistingVolumeRequest, actual) } + +func TestBootFromNewVolumeType(t *testing.T) { + actual, err := NewVolumeTypeRequest.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, ExpectedNewVolumeTypeRequest, actual) +} From 95df56e81afebd7daeb68d39a4766f5c5be23a75 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 8 Sep 2019 10:56:52 -0600 Subject: [PATCH 0836/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f03d54af..86ccda4bdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.5.0 (Unreleased) +IMPROVEMENTS + +* Added `VolumeType` to `cmopute/v2/extensions/bootfromvolume.BlockDevice` [GH-1690] + ## 0.4.0 (September 3, 2019) IMPROVEMENTS From 8460541c9f34d191828a4ebdfa8ea5c00459f12a Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Wed, 11 Sep 2019 22:14:03 +0200 Subject: [PATCH 0837/2296] List Port Forwardings (#1688) * Add list request * Add list results * Add unit test * Add acceptance test * Add documentation * Delete unused test code * Fix some formatting issues, remove some unused lines of code * Add limit & marker --- .../extensions/layer3/portforwardings_test.go | 22 ++++++-- .../extensions/layer3/portforwarding/doc.go | 17 ++++++ .../layer3/portforwarding/requests.go | 51 +++++++++++++++++- .../layer3/portforwarding/results.go | 38 +++++++++++++ .../layer3/portforwarding/testing/fixtures.go | 30 +++++++++++ .../portforwarding/testing/requests_test.go | 54 +++++++++++++++++++ 6 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 openstack/networking/v2/extensions/layer3/portforwarding/testing/fixtures.go diff --git a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index 2423d81b06..768e6950fc 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -7,6 +7,7 @@ import ( networking "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/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" ) @@ -50,9 +51,24 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { tools.PrintResource(t, newFip) - pf, err := CreatePortForwarding(t, client, fip.ID, port.ID, port.FixedIPs) + newPf, err := CreatePortForwarding(t, client, fip.ID, port.ID, port.FixedIPs) th.AssertNoErr(t, err) - defer DeletePortForwarding(t, client, fip.ID, pf.ID) - tools.PrintResource(t, pf) + defer DeletePortForwarding(t, client, fip.ID, newPf.ID) + tools.PrintResource(t, newPf) + + allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fip.ID).AllPages() + th.AssertNoErr(t, err) + + allPFs, err := portforwarding.ExtractPortForwardings(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, pf := range allPFs { + if pf.ID == newPf.ID { + found = true + } + } + + th.AssertEquals(t, true, found) } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go index acc68b1ed5..37d68c92fe 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go @@ -2,6 +2,23 @@ package portforwarding enables management and retrieval of port forwarding resources for Floating IPs from the OpenStack Networking service. +Example to list all Port Forwardings for a floating IP + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fipID).AllPages() + if err != nil { + panic(err) + } + + allPFs, err := portforwarding.ExtractPortForwardings(allPages) + if err != nil { + panic(err) + } + + for _, pf := range allPFs { + fmt.Printf("%+v\n", pf) + } + Example to Create a Port Forwarding for a floating IP createOpts := &portforwarding.CreateOpts{ diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index 6809e659cf..3a553d5ca3 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -1,6 +1,55 @@ package portforwarding -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type ListOptsBuilder interface { + ToPortForwardingListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the port forwarding attributes you want to see returned. SortKey allows you to +// sort by a particular network attribute. SortDir sets the direction, and is +// either `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + ID string `q:"id"` + InternalPortID string `q:"internal_port_id"` + ExternalPort string `q:"external_port"` + InternalIPAddress string `q:"internal_ip_address"` + Protocol string `q:"protocol"` + InternalPort string `q:"internal_port"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Fields string `q:"fields"` + Limit int `q:"limit"` + Marker string `q:"marker"` +} + +// ToPortForwardingListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPortForwardingListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// Port Forwarding resources. It accepts a ListOpts struct, which allows you to +// filter and sort the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder, id string) pagination.Pager { + url := portForwardingUrl(c, id) + if opts != nil { + query, err := opts.ToPortForwardingListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return PortForwardingPage{pagination.LinkedPageBase{PageResult: r}} + }) +} // CreateOpts contains all the values needed to create a new port forwarding // resource. All attributes are required. diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go index 06ef608385..bbcc8f2c09 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/results.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -2,6 +2,7 @@ package portforwarding import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type PortForwarding struct { @@ -52,3 +53,40 @@ func (r commonResult) Extract() (*PortForwarding, error) { func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "port_forwarding") } + +// PortForwardingPage is the page returned by a pager when traversing over a +// collection of port forwardings. +type PortForwardingPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of port forwardings 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 PortForwardingPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"port_forwarding_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a PortForwardingPage struct is empty. +func (r PortForwardingPage) IsEmpty() (bool, error) { + is, err := ExtractPortForwardings(r) + return len(is) == 0, err +} + +// ExtractPortForwardings accepts a Page struct, specifically a PortForwardingPage +// struct, and extracts the elements into a slice of PortForwarding structs. In +// other words, a generic collection is mapped into a relevant slice. +func ExtractPortForwardings(r pagination.Page) ([]PortForwarding, error) { + var s struct { + PortForwardings []PortForwarding `json:"port_forwardings"` + } + err := (r.(PortForwardingPage)).ExtractInto(&s) + return s.PortForwardings, err +} diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/fixtures.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/fixtures.go new file mode 100644 index 0000000000..b9362c4859 --- /dev/null +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/fixtures.go @@ -0,0 +1,30 @@ +package testing + +import "fmt" + +const PoFw = `{ + "protocol": "tcp", + "internal_ip_address": "10.0.0.24", + "internal_port": 25, + "internal_port_id": "070ef0b2-0175-4299-be5c-01fea8cca522", + "external_port": 2229, + "id": "1798dc82-c0ed-4b79-b12d-4c3c18f90eb2" + }` + +const PoFw_second = `{ + "protocol": "tcp", + "internal_ip_address": "10.0.0.11", + "internal_port": 25, + "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", + "external_port": 2230, + "id": "e0a0274e-4d19-4eab-9e12-9e77a8caf3ea" + }` + +var ListResponse = fmt.Sprintf(` +{ + "port_forwardings": [ +%s, +%s + ] +} +`, PoFw, PoFw_second) diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go index 926027d72d..9ceee379dd 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -7,9 +7,63 @@ import ( fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/portforwarding" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) +func TestPortForwardingList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/floatingips/2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e/port_forwardings", 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, ListResponse) + }) + + count := 0 + + portforwarding.List(fake.ServiceClient(), portforwarding.ListOpts{}, "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := portforwarding.ExtractPortForwardings(page) + if err != nil { + t.Errorf("Failed to extract port forwardings: %v", err) + return false, err + } + + expected := []portforwarding.PortForwarding{ + { + Protocol: "tcp", + InternalIPAddress: "10.0.0.24", + InternalPort: 25, + InternalPortID: "070ef0b2-0175-4299-be5c-01fea8cca522", + ExternalPort: 2229, + ID: "1798dc82-c0ed-4b79-b12d-4c3c18f90eb2", + }, + { + Protocol: "tcp", + InternalIPAddress: "10.0.0.11", + InternalPort: 25, + InternalPortID: "1238be08-a2a8-4b8d-addf-fb5e2250e480", + ExternalPort: 2230, + ID: "e0a0274e-4d19-4eab-9e12-9e77a8caf3ea", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 1445d4a8917be3df5597df12de33f1377ebd9b36 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Sep 2019 14:14:36 -0600 Subject: [PATCH 0838/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86ccda4bdf..6673f89135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ IMPROVEMENTS * Added `VolumeType` to `cmopute/v2/extensions/bootfromvolume.BlockDevice` [GH-1690] +* Added `networking/v2/extensions/layer3/portforwarding.List` [GH-1688] ## 0.4.0 (September 3, 2019) From 1aef106241e71405050a2434017e520ce766b5ab Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 13 Sep 2019 21:21:25 +0300 Subject: [PATCH 0839/2296] Compute V2: test Tags Check, List, ReplaceAll, Add (#1696) * Compute V2: add Replace method for Tags Implement compute/v2/extensions/tags.Replace. * Compute V2: rename Tags Replace to ReplaceAll Use ReplaceAll name like it's done in networking extension.t * Compute V2: implement Add method for Tags Implement compute/v2/extensions/tags.Add. Update package documentation. * Compute V2: test Tags Check, List, ReplaceAll, Add Update Compute V2 acceptance tests to use Tags Check, List, ReplaceAll, and Add methods. --- .../openstack/compute/v2/servers_test.go | 26 +++++++++ openstack/compute/v2/extensions/tags/doc.go | 22 +++++++- .../compute/v2/extensions/tags/requests.go | 38 +++++++++++++ .../compute/v2/extensions/tags/results.go | 10 ++++ .../v2/extensions/tags/testing/fixtures.go | 14 +++++ .../extensions/tags/testing/requests_test.go | 54 +++++++++++++++++++ openstack/compute/v2/extensions/tags/urls.go | 8 +++ 7 files changed, 171 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 1cb303b866..f2da293410 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -17,6 +17,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/serverusage" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tags" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" @@ -506,9 +507,34 @@ func TestServersTags(t *testing.T) { networkID, err := networks.IDFromName(networkClient, choices.NetworkName) th.AssertNoErr(t, err) + // Create server with tags. server, err := CreateServerWithTags(t, client, networkID) th.AssertNoErr(t, err) defer DeleteServer(t, client, server) + + // Check all tags. + allTags, err := tags.List(client, server.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, []string{"tag1", "tag2"}, allTags) + + // Check single tag. + exists, err := tags.Check(client, server.ID, "tag2").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, exists) + + // Add new tag. + newTags, err := tags.ReplaceAll(client, server.ID, tags.ReplaceAllOpts{Tags: []string{"tag3", "tag4"}}).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, []string{"tag3", "tag4"}, newTags) + + // Add new single tag. + err = tags.Add(client, server.ID, "tag5").ExtractErr() + th.AssertNoErr(t, err) + + // Check current tags. + newAllTags, err := tags.List(client, server.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, []string{"tag3", "tag4", "tag5"}, newAllTags) } func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { diff --git a/openstack/compute/v2/extensions/tags/doc.go b/openstack/compute/v2/extensions/tags/doc.go index 91b58b9ff5..1e26bc389a 100644 --- a/openstack/compute/v2/extensions/tags/doc.go +++ b/openstack/compute/v2/extensions/tags/doc.go @@ -14,7 +14,7 @@ Example to List all server Tags fmt.Printf("Tags: %v\n", serverTags) -Example to Check if the specific Tag exists on a server. +Example to Check if the specific Tag exists on a server client.Microversion = "2.62" @@ -28,5 +28,25 @@ Example to Check if the specific Tag exists on a server. } else { log.Printf("Tag %s is not set\n", tag) } + +Example to Replace all Tags on a server + + client.Microversion = "2.62" + + 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) + +Example to Add a new Tag on a server + + client.Microversion = "2.62" + + err := tags.Add(client, serverID, "foo").ExtractErr() + if err != nil { + log.Fatal(err) + } */ package tags diff --git a/openstack/compute/v2/extensions/tags/requests.go b/openstack/compute/v2/extensions/tags/requests.go index fd490a669b..faf0d3c7dc 100644 --- a/openstack/compute/v2/extensions/tags/requests.go +++ b/openstack/compute/v2/extensions/tags/requests.go @@ -19,3 +19,41 @@ func Check(client *gophercloud.ServiceClient, serverID, tag string) (r CheckResu }) return } + +// ReplaceAllOptsBuilder allows to add additional parameters to the ReplaceAll request. +type ReplaceAllOptsBuilder interface { + ToTagsReplaceAllMap() (map[string]interface{}, error) +} + +// ReplaceAllOpts provides options used to replace Tags on a server. +type ReplaceAllOpts struct { + Tags []string `json:"tags" required:"true"` +} + +// ToTagsReplaceAllMap formats a ReplaceALlOpts into the body of the ReplaceAll request. +func (opts ReplaceAllOpts) ToTagsReplaceAllMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// ReplaceAll replaces all Tags on a server. +func ReplaceAll(client *gophercloud.ServiceClient, serverID string, opts ReplaceAllOptsBuilder) (r ReplaceAllResult) { + b, err := opts.ToTagsReplaceAllMap() + url := replaceAllURL(client, serverID) + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Add adds a new Tag on a server. +func Add(client *gophercloud.ServiceClient, serverID, tag string) (r AddResult) { + url := addURL(client, serverID, tag) + _, r.Err = client.Put(url, nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201, 204}, + }) + return +} diff --git a/openstack/compute/v2/extensions/tags/results.go b/openstack/compute/v2/extensions/tags/results.go index 08d310cb30..816ed84380 100644 --- a/openstack/compute/v2/extensions/tags/results.go +++ b/openstack/compute/v2/extensions/tags/results.go @@ -35,3 +35,13 @@ func (r CheckResult) Extract() (bool, error) { return exists, r.Err } + +// ReplaceAllResult is the result from the ReplaceAll operation. +type ReplaceAllResult struct { + commonResult +} + +// AddResult is the result from the Add operation. +type AddResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/tags/testing/fixtures.go b/openstack/compute/v2/extensions/tags/testing/fixtures.go index 4a51bdf539..8b62cda42e 100644 --- a/openstack/compute/v2/extensions/tags/testing/fixtures.go +++ b/openstack/compute/v2/extensions/tags/testing/fixtures.go @@ -6,3 +6,17 @@ const TagsListResponse = ` "tags": ["foo", "bar", "baz"] } ` + +// TagsReplaceAllRequest represents a raw tags Replace request. +const TagsReplaceAllRequest = ` +{ + "tags": ["tag1", "tag2", "tag3"] +} +` + +// TagsReplaceAllResponse represents a raw tags Replace response. +const TagsReplaceAllResponse = ` +{ + "tags": ["tag1", "tag2", "tag3"] +} +` diff --git a/openstack/compute/v2/extensions/tags/testing/requests_test.go b/openstack/compute/v2/extensions/tags/testing/requests_test.go index 602f64b71d..3027a761f8 100644 --- a/openstack/compute/v2/extensions/tags/testing/requests_test.go +++ b/openstack/compute/v2/extensions/tags/testing/requests_test.go @@ -65,3 +65,57 @@ func TestCheckFail(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, false, exists) } + +func TestReplaceAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/uuid1/tags", 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) + + _, err := fmt.Fprintf(w, TagsReplaceAllResponse) + th.AssertNoErr(t, err) + }) + + expected := []string{"tag1", "tag2", "tag3"} + actual, err := tags.ReplaceAll(client.ServiceClient(), "uuid1", tags.ReplaceAllOpts{Tags: []string{"tag1", "tag2", "tag3"}}).Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) +} + +func TestAddCreated(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/uuid1/tags/foo", 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.StatusCreated) + }) + + err := tags.Add(client.ServiceClient(), "uuid1", "foo").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestAddExists(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/uuid1/tags/foo", 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.StatusNoContent) + }) + + err := tags.Add(client.ServiceClient(), "uuid1", "foo").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/tags/urls.go b/openstack/compute/v2/extensions/tags/urls.go index 273393502e..a59ceca48f 100644 --- a/openstack/compute/v2/extensions/tags/urls.go +++ b/openstack/compute/v2/extensions/tags/urls.go @@ -22,3 +22,11 @@ func listURL(c *gophercloud.ServiceClient, serverID string) string { func checkURL(c *gophercloud.ServiceClient, serverID, tag string) string { return resourceURL(c, serverID, tag) } + +func replaceAllURL(c *gophercloud.ServiceClient, serverID string) string { + return rootURL(c, serverID) +} + +func addURL(c *gophercloud.ServiceClient, serverID, tag string) string { + return resourceURL(c, serverID, tag) +} From 716f7484aaace8dc8fd102323fe9a532572080ef Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Sun, 15 Sep 2019 03:55:12 +0200 Subject: [PATCH 0840/2296] Get Port Forwarding (#1698) * Add Get method * Add unit test * Add acceptance test & doc --- .../extensions/layer3/portforwardings_test.go | 9 +++-- .../extensions/layer3/portforwarding/doc.go | 10 ++++++ .../layer3/portforwarding/requests.go | 6 ++++ .../layer3/portforwarding/results.go | 6 ++++ .../portforwarding/testing/requests_test.go | 36 +++++++++++++++++++ 5 files changed, 64 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index 768e6950fc..4ad6f2bedc 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -51,10 +51,13 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { tools.PrintResource(t, newFip) - newPf, err := CreatePortForwarding(t, client, fip.ID, port.ID, port.FixedIPs) + pf, err := CreatePortForwarding(t, client, fip.ID, port.ID, port.FixedIPs) + th.AssertNoErr(t, err) + defer DeletePortForwarding(t, client, fip.ID, pf.ID) + tools.PrintResource(t, pf) + + newPf, err := portforwarding.Get(client, fip.ID, pf.ID).Extract() th.AssertNoErr(t, err) - defer DeletePortForwarding(t, client, fip.ID, newPf.ID) - tools.PrintResource(t, newPf) allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fip.ID).AllPages() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go index 37d68c92fe..db746435bf 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go @@ -19,6 +19,16 @@ Example to list all Port Forwardings for a floating IP fmt.Printf("%+v\n", pf) } +Example to Get a Port Forwarding with a certain ID + + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" + pf, err := portforwarding.Get(client, fipID, pfID).Extract() + if err != nil { + panic(err) + } + + Example to Create a Port Forwarding for a floating IP createOpts := &portforwarding.CreateOpts{ diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index 3a553d5ca3..180d2c76f3 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -51,6 +51,12 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder, id string) paginat }) } +// Get retrieves a particular port forwarding resource based on its unique ID. +func Get(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r GetResult) { + _, r.Err = c.Get(singlePortForwardingUrl(c, floatingIpId, pfId), &r.Body, nil) + return +} + // CreateOpts contains all the values needed to create a new port forwarding // resource. All attributes are required. type CreateOpts struct { diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go index bbcc8f2c09..588d529af6 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/results.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -37,6 +37,12 @@ type CreateResult struct { commonResult } +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a PortForwarding. +type GetResult 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 { diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go index 9ceee379dd..83e573546e 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -121,6 +121,42 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, "tcp", pf.Protocol) } +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7/port_forwardings/725ade3c-9760-4880-8080-8fc2dbab9acc", 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, ` +{ + "port_forwarding": { + "protocol": "tcp", + "internal_ip_address": "10.0.0.11", + "internal_port": 25, + "internal_port_id": "1238be08-a2a8-4b8d-addf-fb5e2250e480", + "external_port": 2230, + "id": "725ade3c-9760-4880-8080-8fc2dbab9acc" + } +} + `) + }) + + pf, err := portforwarding.Get(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "tcp", pf.Protocol) + th.AssertEquals(t, "725ade3c-9760-4880-8080-8fc2dbab9acc", pf.ID) + th.AssertEquals(t, "10.0.0.11", pf.InternalIPAddress) + th.AssertEquals(t, 25, pf.InternalPort) + th.AssertEquals(t, "1238be08-a2a8-4b8d-addf-fb5e2250e480", pf.InternalPortID) + th.AssertEquals(t, 2230, pf.ExternalPort) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 95cd5f36ac11583b9c2191cd5ff7c3dcdafe3d0e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 14 Sep 2019 19:57:11 -0600 Subject: [PATCH 0841/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6673f89135..648260e508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ IMPROVEMENTS * Added `VolumeType` to `cmopute/v2/extensions/bootfromvolume.BlockDevice` [GH-1690] * Added `networking/v2/extensions/layer3/portforwarding.List` [GH-1688] +* Added `networking/v2/extensions/layer3/portforwarding.Get` [GH-1698] +* Added `compute/v2/extensions/tags.ReplaceAll` [GH-1696] +* Added `compute/v2/extensions/tags.Add` [GH-1696] ## 0.4.0 (September 3, 2019) From 9898047a3d11362d5f2ef1d46ce66a0bf67f696c Mon Sep 17 00:00:00 2001 From: Simon Reinkemeier Date: Tue, 17 Sep 2019 07:08:54 +0200 Subject: [PATCH 0842/2296] Update port forwarding (#1703) * Add update function * Add unit test * Add acceptance test * Add doc * Add omitempty to protocol --- .../extensions/layer3/portforwardings_test.go | 12 ++++ .../extensions/layer3/portforwarding/doc.go | 17 +++++- .../layer3/portforwarding/requests.go | 39 ++++++++++++ .../layer3/portforwarding/results.go | 6 ++ .../portforwarding/testing/requests_test.go | 61 +++++++++++++++++++ 5 files changed, 134 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index 4ad6f2bedc..0aa35e0d3b 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -59,6 +59,18 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { newPf, err := portforwarding.Get(client, fip.ID, pf.ID).Extract() th.AssertNoErr(t, err) + updateOpts := portforwarding.UpdateOpts{ + Protocol: "udp", + InternalPort: 30, + ExternalPort: 678, + } + + _, err = portforwarding.Update(client, fip.ID, newPf.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + newPf, err = portforwarding.Get(client, fip.ID, pf.ID).Extract() + th.AssertNoErr(t, err) + allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fip.ID).AllPages() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go index db746435bf..661a06531e 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go @@ -45,11 +45,26 @@ Example to Create a Port Forwarding for a floating IP panic(err) } +Example to Update a Port Forwarding + + updateOpts := portforwarding.UpdateOpts{ + Protocol: "udp", + InternalPort: 30, + ExternalPort: 678, + } + fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" + pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" + + pf, err := portforwarding.Update(client, fipID, pfID, updateOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a Port forwarding fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" - err := floatingips.Delete(networkClient, fipID, pfID).ExtractErr() + err := portforwarding.Delete(networkClient, fipID, pfID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index 180d2c76f3..0fa5c1a669 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -91,6 +91,45 @@ func Create(c *gophercloud.ServiceClient, floatingIpId string, opts CreateOptsBu return } +// UpdateOpts contains the values used when updating a port forwarding resource. +type UpdateOpts struct { + InternalPortID string `json:"internal_port_id,omitempty"` + InternalIPAddress string `json:"internal_ip_address,omitempty"` + InternalPort int `json:"internal_port,omitempty"` + ExternalPort int `json:"external_port,omitempty"` + Protocol string `json:"protocol,omitempty"` +} + +// ToPortForwardingUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder +// interface +func (opts UpdateOpts) ToPortForwardingUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "port_forwarding") + if err != nil { + return nil, err + } + + return b, nil +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToPortForwardingUpdateMap() (map[string]interface{}, error) +} + +// Update allows port forwarding resources to be updated. +func Update(c *gophercloud.ServiceClient, fipID string, pfID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToPortForwardingUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(singlePortForwardingUrl(c, fipID, pfID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + // Delete will permanently delete a particular port forwarding for a given floating ID. func Delete(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r DeleteResult) { _, r.Err = c.Delete(singlePortForwardingUrl(c, floatingIpId, pfId), nil) diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go index 588d529af6..3aa1f3eb6b 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/results.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -43,6 +43,12 @@ type GetResult struct { commonResult } +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a PortForwarding. +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 { diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go index 83e573546e..a2b509b0d3 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -170,3 +170,64 @@ func TestDelete(t *testing.T) { res := portforwarding.Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc") th.AssertNoErr(t, res.Err) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/floatingips/2f245a7b-796b-4f26-9cf9-9e82d248fda7/port_forwardings/725ade3c-9760-4880-8080-8fc2dbab9acc", 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, ` +{ + "port_forwarding": { + "protocol": "udp", + "internal_port": 37, + "internal_port_id": "99889dc2-19a7-4edb-b9d0-d2ace8d1e144", + "external_port": 1960 + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "port_forwarding": { + "protocol": "udp", + "internal_ip_address": "10.0.0.14", + "internal_port": 37, + "internal_port_id": "99889dc2-19a7-4edb-b9d0-d2ace8d1e144", + "external_port": 1960, + "id": "725ade3c-9760-4880-8080-8fc2dbab9acc" + } +} +`) + }) + + updatedProtocol := "udp" + updatedInternalPort := 37 + updatedInternalPortID := "99889dc2-19a7-4edb-b9d0-d2ace8d1e144" + updatedExternalPort := 1960 + options := portforwarding.UpdateOpts{ + Protocol: updatedProtocol, + InternalPort: updatedInternalPort, + InternalPortID: updatedInternalPortID, + ExternalPort: updatedExternalPort, + } + + actual, err := portforwarding.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc", options).Extract() + th.AssertNoErr(t, err) + expected := portforwarding.PortForwarding{ + Protocol: "udp", + InternalIPAddress: "10.0.0.14", + InternalPort: 37, + ID: "725ade3c-9760-4880-8080-8fc2dbab9acc", + InternalPortID: "99889dc2-19a7-4edb-b9d0-d2ace8d1e144", + ExternalPort: 1960, + } + th.AssertDeepEquals(t, expected, *actual) +} From c1ae5ed189fea28b1222fb6f9d4daf80471e2855 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 16 Sep 2019 23:09:26 -0600 Subject: [PATCH 0843/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 648260e508..832d09ff03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ IMPROVEMENTS * Added `networking/v2/extensions/layer3/portforwarding.Get` [GH-1698] * Added `compute/v2/extensions/tags.ReplaceAll` [GH-1696] * Added `compute/v2/extensions/tags.Add` [GH-1696] +* Added `networking/v2/extensions/layer3/portforwarding.Update` [GH-1703] ## 0.4.0 (September 3, 2019) From f0c76d17f1fdbe99ca5b74f319ed3bbcc615bb59 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 19 Sep 2019 04:10:12 +0200 Subject: [PATCH 0844/2296] Identity V3: Add ExtractDomain method for the identity/v3/tokens package (#1712) * Identity V3: Add ExtractDomain method for the identity/v3/tokens package * Add domain scope unit tests --- openstack/identity/v3/tokens/results.go | 9 ++ .../identity/v3/tokens/testing/fixtures.go | 108 ++++++++++++++++++ .../v3/tokens/testing/results_test.go | 9 ++ 3 files changed, 126 insertions(+) diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index 6f26c96bcd..8af4d634cf 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -144,6 +144,15 @@ func (r commonResult) ExtractProject() (*Project, error) { return s.Project, err } +// ExtractDomain returns Domain to which User is authorized. +func (r commonResult) ExtractDomain() (*Domain, error) { + var s struct { + Domain *Domain `json:"domain"` + } + err := r.ExtractInto(&s) + return s.Domain, err +} + // CreateResult is the response from a Create request. Use ExtractToken() // to interpret it as a Token, or ExtractServiceCatalog() to interpret it // as a service catalog. diff --git a/openstack/identity/v3/tokens/testing/fixtures.go b/openstack/identity/v3/tokens/testing/fixtures.go index e6f44178a4..503e1dd7de 100644 --- a/openstack/identity/v3/tokens/testing/fixtures.go +++ b/openstack/identity/v3/tokens/testing/fixtures.go @@ -110,6 +110,98 @@ const TokenOutput = ` } }` +const DomainToken = ` +{ + "token": { + "domain": { + "id": "default", + "name": "Default" + }, + "methods": [ + "password" + ], + "roles":[ + { + "id":"434426788d5a451faf763b0e6db5aefb", + "name":"admin" + } + ], + "expires_at": "2019-09-18T23:12:32.000000Z", + "catalog":[ + { + "endpoints":[ + { + "url":"http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", + "interface":"admin", + "region":"RegionOne", + "region_id":"RegionOne", + "id":"3eac9e7588eb4eb2a4650cf5e079505f" + }, + { + "url":"http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", + "interface":"internal", + "region":"RegionOne", + "region_id":"RegionOne", + "id":"6b33fabc69c34ea782a3f6282582b59f" + }, + { + "url":"http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", + "interface":"public", + "region":"RegionOne", + "region_id":"RegionOne", + "id":"dae63c71bee24070a71f5425e7a916b5" + } + ], + "type":"compute", + "id":"17e0fa04647d4155a7933ee624dd66da", + "name":"nova" + }, + { + "endpoints":[ + { + "url":"http://127.0.0.1:35357/v3", + "interface":"admin", + "region":"RegionOne", + "region_id":"RegionOne", + "id":"0539aeff80954a0bb756cec496768d3d" + }, + { + "url":"http://127.0.0.1:5000/v3", + "interface":"public", + "region":"RegionOne", + "region_id":"RegionOne", + "id":"15bdf2d0853e4c939993d29548b1b56f" + }, + { + "url":"http://127.0.0.1:5000/v3", + "interface":"internal", + "region":"RegionOne", + "region_id":"RegionOne", + "id":"3b4423c54ba343c58226bc424cb11c4b" + } + ], + "type":"identity", + "id":"1cde0ea8cb3c49d8928cb172ca825ca5", + "name":"keystone" + } + ], + "user":{ + "domain":{ + "id":"default", + "name":"Default" + }, + "password_expires_at":null, + "name":"admin", + "id":"0fe36e73809d46aeae6705c39077b1b3" + }, + "audit_ids": [ + "P4QTZuYXS1u8SC6b3BSK1g" + ], + "issued_at": "2019-09-18T15:12:32.000000Z" + } +} +` + var expectedTokenTime, _ = time.Parse(gophercloud.RFC3339Milli, "2017-06-03T02:19:49.000000Z") var ExpectedToken = tokens.Token{ @@ -206,6 +298,12 @@ var ExpectedProject = tokens.Project{ Name: "admin", } +// ExpectedDomain contains expected domain extracted from token response. +var ExpectedDomain = tokens.Domain{ + ID: "default", + Name: "Default", +} + func getGetResult(t *testing.T) tokens.GetResult { result := tokens.GetResult{} result.Header = http.Header{ @@ -215,3 +313,13 @@ func getGetResult(t *testing.T) tokens.GetResult { testhelper.AssertNoErr(t, err) return result } + +func getGetDomainResult(t *testing.T) tokens.GetResult { + result := tokens.GetResult{} + result.Header = http.Header{ + "X-Subject-Token": []string{testTokenID}, + } + err := json.Unmarshal([]byte(DomainToken), &result.Body) + testhelper.AssertNoErr(t, err) + return result +} diff --git a/openstack/identity/v3/tokens/testing/results_test.go b/openstack/identity/v3/tokens/testing/results_test.go index d55a538bc1..c889818878 100644 --- a/openstack/identity/v3/tokens/testing/results_test.go +++ b/openstack/identity/v3/tokens/testing/results_test.go @@ -50,3 +50,12 @@ func TestExtractProject(t *testing.T) { testhelper.CheckDeepEquals(t, &ExpectedProject, project) } + +func TestExtractDomain(t *testing.T) { + result := getGetDomainResult(t) + + domain, err := result.ExtractDomain() + testhelper.AssertNoErr(t, err) + + testhelper.CheckDeepEquals(t, &ExpectedDomain, domain) +} From 87fc45b21de6e501d3909b59fadd50a8ea4b234a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 18 Sep 2019 20:11:36 -0600 Subject: [PATCH 0845/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 832d09ff03..e46638b5be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ IMPROVEMENTS * Added `compute/v2/extensions/tags.ReplaceAll` [GH-1696] * Added `compute/v2/extensions/tags.Add` [GH-1696] * Added `networking/v2/extensions/layer3/portforwarding.Update` [GH-1703] +* Added `ExtractDomain` method to token results in `identity/v3/tokens` [GH-1712] ## 0.4.0 (September 3, 2019) From d35214b350b91a520bcb9859fa58aad64a0bb628 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Thu, 19 Sep 2019 14:31:19 +1200 Subject: [PATCH 0846/2296] Octavia: Support allowed_cidrs for listener (#1710) --- .../loadbalancer/v2/listeners/requests.go | 6 +++ .../loadbalancer/v2/listeners/results.go | 3 ++ .../v2/listeners/testing/fixtures.go | 49 +++++++++++++------ .../v2/listeners/testing/requests_test.go | 1 + 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index c512e4d72f..36d00baf85 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -131,6 +131,9 @@ type CreateOpts struct { // A dictionary of optional headers to insert into the request before it is sent to the backend member. InsertHeaders map[string]string `json:"insert_headers,omitempty"` + + // A list of IPv4, IPv6 or mix of both CIDRs + AllowedCIDRs []string `json:"allowed_cidrs,omitempty"` } // ToListenerCreateMap builds a request body from CreateOpts. @@ -202,6 +205,9 @@ type UpdateOpts struct { // Time, in milliseconds, to wait for additional TCP packets for content inspection TimeoutTCPInspect *int `json:"timeout_tcp_inspect,omitempty"` + + // A list of IPv4, IPv6 or mix of both CIDRs + AllowedCIDRs []string `json:"allowed_cidrs,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index a8f7682101..7bf6734ef7 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -77,6 +77,9 @@ type Listener struct { // A dictionary of optional headers to insert into the request before it is sent to the backend member. InsertHeaders map[string]string `json:"insert_headers"` + + // A list of IPv4, IPv6 or mix of both CIDRs + AllowedCIDRs []string `json:"allowed_cidrs"` } type Stats struct { diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index bd65d3ac02..1789791ec3 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -25,7 +25,11 @@ const ListenersListBody = ` "default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d", "admin_state_up": true, "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] + "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"], + "allowed_cidrs": [ + "192.0.2.0/24", + "198.51.100.0/24" + ] }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -46,7 +50,11 @@ const ListenersListBody = ` "timeout_tcp_inspect": 0, "insert_headers": { "X-Forwarded-For": "true" - } + }, + "allowed_cidrs": [ + "192.0.2.0/24", + "198.51.100.0/24" + ] } ] } @@ -72,9 +80,13 @@ const SingleListenerBody = ` "timeout_member_data": 50000, "timeout_member_connect": 5000, "timeout_tcp_inspect": 0, - "insert_headers": { - "X-Forwarded-For": "true" - } + "insert_headers": { + "X-Forwarded-For": "true" + }, + "allowed_cidrs": [ + "192.0.2.0/24", + "198.51.100.0/24" + ] } } ` @@ -99,7 +111,6 @@ const PostUpdateListenerBody = ` "timeout_member_data": 181000, "timeout_member_connect": 181000, "timeout_tcp_inspect": 181000 - } } ` @@ -130,6 +141,7 @@ var ( AdminStateUp: true, DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, + AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, } ListenerDb = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -149,6 +161,7 @@ var ( TimeoutMemberConnect: 5000, TimeoutTCPInspect: 0, InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, + AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, } ListenerUpdated = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -204,18 +217,22 @@ func HandleListenerCreationSuccessfully(t *testing.T, response string) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, `{ - "listener": { - "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab", - "protocol": "TCP", - "name": "db", - "admin_state_up": true, - "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", - "protocol_port": 3306, + "listener": { + "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab", + "protocol": "TCP", + "name": "db", + "admin_state_up": true, + "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", + "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", + "protocol_port": 3306, "insert_headers": { "X-Forwarded-For": "true" - } - } + }, + "allowed_cidrs": [ + "192.0.2.0/24", + "198.51.100.0/24" + ] + } }`) w.WriteHeader(http.StatusAccepted) diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 775810951d..76345259d0 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -67,6 +67,7 @@ func TestCreateListener(t *testing.T) { DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ProtocolPort: 3306, InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, + AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, }).Extract() th.AssertNoErr(t, err) From 3d65851f4b6868efed5114aeb499305b0ff86870 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 18 Sep 2019 20:36:08 -0600 Subject: [PATCH 0847/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e46638b5be..2fdeaf1751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ IMPROVEMENTS * Added `compute/v2/extensions/tags.Add` [GH-1696] * Added `networking/v2/extensions/layer3/portforwarding.Update` [GH-1703] * Added `ExtractDomain` method to token results in `identity/v3/tokens` [GH-1712] +* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.CreateOpts` [GH-1710] +* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1710] +* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.Listener` [GH-1710] ## 0.4.0 (September 3, 2019) From 34bae44cf8128e18fd29b1e908001ec95b56165f Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Thu, 19 Sep 2019 19:00:02 +0300 Subject: [PATCH 0848/2296] Neutron LB V2: use UpdateOptsBuilder (#1706) * Neutron LB V2: use UpdateOptsBuilder Use the UpdateOptsBuilder instead of the specific UpdateOpts struct type in extensions/lbaas_v2/listeners/requests.Update. * Neutron LB V2: use UpdateOptsBuilder Use the UpdateOptsBuilder instead of the specific UpdateOpts struct type in extensions/lbaas_v2/loadbalancers/requests.Update. --- go.mod | 2 ++ .../networking/v2/extensions/lbaas_v2/listeners/requests.go | 2 +- .../networking/v2/extensions/lbaas_v2/loadbalancers/requests.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d1ee3b472e..44973d1b76 100644 --- a/go.mod +++ b/go.mod @@ -5,3 +5,5 @@ require ( golang.org/x/sys v0.0.0-20190209173611-3b5209105503 // indirect gopkg.in/yaml.v2 v2.2.2 ) + +go 1.13 diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index f2966b6c44..b0ea23de9c 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -193,7 +193,7 @@ func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { // Update is an operation which modifies the attributes of the specified // Listener. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToListenerUpdateMap() if err != nil { r.Err = err diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index f5b1413482..a0383bb42e 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -158,7 +158,7 @@ func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) // Update is an operation which modifies the attributes of the specified // LoadBalancer. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLoadBalancerUpdateMap() if err != nil { r.Err = err From ee96a03bd4bd72846607d0867fad3a656ed0b700 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 20 Sep 2019 08:29:08 +0300 Subject: [PATCH 0849/2296] Acc tests: update Compute V2 Tags test (#1701) * Compute V2: add Delete method for Tags Implement compute/v2/extensions/tags.Delete. * Compute V2: add DeleteAll method for Tags Implement compute/v2/extensions/tags.DeleteAll. Update package documentation. * Acc tests: update Compute V2 Tags test Add Delete and DeletAll checks into TestServersTags. --- .../openstack/compute/v2/servers_test.go | 18 ++++++++ openstack/compute/v2/extensions/tags/doc.go | 18 ++++++++ .../compute/v2/extensions/tags/requests.go | 18 ++++++++ .../compute/v2/extensions/tags/results.go | 5 +++ .../extensions/tags/testing/requests_test.go | 44 ++++++++++++++++--- openstack/compute/v2/extensions/tags/urls.go | 8 ++++ 6 files changed, 105 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index f2da293410..4773da4a5a 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -535,6 +535,24 @@ func TestServersTags(t *testing.T) { newAllTags, err := tags.List(client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag3", "tag4", "tag5"}, newAllTags) + + // Remove single tag. + err = tags.Delete(client, server.ID, "tag4").ExtractErr() + th.AssertNoErr(t, err) + + // Check that tag doesn't exist anymore. + exists, err = tags.Check(client, server.ID, "tag4").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, false, exists) + + // Remove all tags. + err = tags.DeleteAll(client, server.ID).ExtractErr() + th.AssertNoErr(t, err) + + // Check that there are no more tags. + currentTags, err := tags.List(client, server.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(currentTags)) } func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { diff --git a/openstack/compute/v2/extensions/tags/doc.go b/openstack/compute/v2/extensions/tags/doc.go index 1e26bc389a..bcecd62446 100644 --- a/openstack/compute/v2/extensions/tags/doc.go +++ b/openstack/compute/v2/extensions/tags/doc.go @@ -48,5 +48,23 @@ Example to Add a new Tag on a server if err != nil { log.Fatal(err) } + +Example to Delete a Tag on a server + + client.Microversion = "2.62" + + err := tags.Delete(client, serverID, "foo").ExtractErr() + if err != nil { + log.Fatal(err) + } + +Example to Delete all Tags on a server + + client.Microversion = "2.62" + + err := tags.DeleteAll(client, serverID).ExtractErr() + if err != nil { + log.Fatal(err) + } */ package tags diff --git a/openstack/compute/v2/extensions/tags/requests.go b/openstack/compute/v2/extensions/tags/requests.go index faf0d3c7dc..3b43096411 100644 --- a/openstack/compute/v2/extensions/tags/requests.go +++ b/openstack/compute/v2/extensions/tags/requests.go @@ -57,3 +57,21 @@ func Add(client *gophercloud.ServiceClient, serverID, tag string) (r AddResult) }) return } + +// Delete removes a tag from a server. +func Delete(client *gophercloud.ServiceClient, serverID, tag string) (r DeleteResult) { + url := deleteURL(client, serverID, tag) + _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} + +// DeleteAll removes all tag from a server. +func DeleteAll(client *gophercloud.ServiceClient, serverID string) (r DeleteResult) { + url := deleteAllURL(client, serverID) + _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/compute/v2/extensions/tags/results.go b/openstack/compute/v2/extensions/tags/results.go index 816ed84380..c73b7a6cb0 100644 --- a/openstack/compute/v2/extensions/tags/results.go +++ b/openstack/compute/v2/extensions/tags/results.go @@ -45,3 +45,8 @@ type ReplaceAllResult struct { type AddResult struct { gophercloud.ErrResult } + +// DeleteResult is the result from the Delete operation. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/tags/testing/requests_test.go b/openstack/compute/v2/extensions/tags/testing/requests_test.go index 3027a761f8..bc0eb8ec5e 100644 --- a/openstack/compute/v2/extensions/tags/testing/requests_test.go +++ b/openstack/compute/v2/extensions/tags/testing/requests_test.go @@ -15,7 +15,7 @@ func TestList(t *testing.T) { defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") + th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") @@ -37,7 +37,7 @@ func TestCheckOk(t *testing.T) { defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/foo", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") + th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") @@ -54,7 +54,7 @@ func TestCheckFail(t *testing.T) { defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/bar", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") + th.TestMethod(t, r, http.MethodGet) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") @@ -71,7 +71,7 @@ func TestReplaceAll(t *testing.T) { defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") + th.TestMethod(t, r, http.MethodPut) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") @@ -93,7 +93,7 @@ func TestAddCreated(t *testing.T) { defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/foo", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") + th.TestMethod(t, r, http.MethodPut) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") @@ -109,7 +109,7 @@ func TestAddExists(t *testing.T) { defer th.TeardownHTTP() th.Mux.HandleFunc("/servers/uuid1/tags/foo", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") + th.TestMethod(t, r, http.MethodPut) th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") @@ -119,3 +119,35 @@ func TestAddExists(t *testing.T) { err := tags.Add(client.ServiceClient(), "uuid1", "foo").ExtractErr() th.AssertNoErr(t, err) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/uuid1/tags/foo", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodDelete) + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + err := tags.Delete(client.ServiceClient(), "uuid1", "foo").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDeleteAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/servers/uuid1/tags", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodDelete) + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + err := tags.DeleteAll(client.ServiceClient(), "uuid1").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/tags/urls.go b/openstack/compute/v2/extensions/tags/urls.go index a59ceca48f..64296887a5 100644 --- a/openstack/compute/v2/extensions/tags/urls.go +++ b/openstack/compute/v2/extensions/tags/urls.go @@ -30,3 +30,11 @@ func replaceAllURL(c *gophercloud.ServiceClient, serverID string) string { func addURL(c *gophercloud.ServiceClient, serverID, tag string) string { return resourceURL(c, serverID, tag) } + +func deleteURL(c *gophercloud.ServiceClient, serverID, tag string) string { + return resourceURL(c, serverID, tag) +} + +func deleteAllURL(c *gophercloud.ServiceClient, serverID string) string { + return rootURL(c, serverID) +} From 6afed24c8cd9c42dc9ff29354f3fd3711a1ab9fc Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 20 Sep 2019 08:34:13 +0300 Subject: [PATCH 0850/2296] Update CHANGELOG.md --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fdeaf1751..09e9fed8d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,15 @@ IMPROVEMENTS * Added `AllowedCIDRs` to `loadbalancer/v2/listeners.CreateOpts` [GH-1710] * Added `AllowedCIDRs` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1710] * Added `AllowedCIDRs` to `loadbalancer/v2/listeners.Listener` [GH-1710] +* Added `compute/v2/extensions/tags.Add` [GH-1695] +* Added `compute/v2/extensions/tags.ReplaceAll` [GH-1694] +* Added `compute/v2/extensions/tags.Delete` [GH-1699] +* Added `compute/v2/extensions/tags.DeleteAll` [GH-1700] + +BUG FIXES + +* Changed struct type for options in `networking/v2/extensions/lbaas_v2/listeners` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1705] +* Changed struct type for options in `networking/v2/extensions/lbaas_v2/loadbalancers` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1706] ## 0.4.0 (September 3, 2019) From 6e93a6ba3b096d93472caabbd6c9576e20cf0ff8 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Fri, 20 Sep 2019 10:47:09 +0300 Subject: [PATCH 0851/2296] Remove go version from go.mod (#1713) Remove go version to not break compatibility with old environments. --- go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.mod b/go.mod index 44973d1b76..d1ee3b472e 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,3 @@ require ( golang.org/x/sys v0.0.0-20190209173611-3b5209105503 // indirect gopkg.in/yaml.v2 v2.2.2 ) - -go 1.13 From 36aaa4d3437eaaebf8722753597c2fc8e12618b7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 22 Sep 2019 16:33:09 -0600 Subject: [PATCH 0852/2296] Acc Tests: Disabling sharedfilesystem temporarily (#1715) --- script/acceptancetest | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/acceptancetest b/script/acceptancetest index 69341a9c6f..34bb53128e 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -75,7 +75,9 @@ acceptance/openstack/networking/v2/extensions/trunks acceptance/openstack/objectstorage/v1 acceptance/openstack/orchestration/v1 -acceptance/openstack/sharedfilesystems/v2 + +# Disabled temporarily +#acceptance/openstack/sharedfilesystems/v2 # No suitable endpoint could be found in the service catalog # acceptance/openstack/workflow/v2 From 13466e32a4a33946a48a259784a7f1f470d78513 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 25 Sep 2019 09:02:48 -0600 Subject: [PATCH 0853/2296] Acc Tests: Fixing subnet creation and re-enabling sharedfilesystem (#1717) --- script/acceptancetest | 4 +--- script/stackenv | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/script/acceptancetest b/script/acceptancetest index 34bb53128e..69341a9c6f 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -75,9 +75,7 @@ acceptance/openstack/networking/v2/extensions/trunks acceptance/openstack/objectstorage/v1 acceptance/openstack/orchestration/v1 - -# Disabled temporarily -#acceptance/openstack/sharedfilesystems/v2 +acceptance/openstack/sharedfilesystems/v2 # No suitable endpoint could be found in the service catalog # acceptance/openstack/workflow/v2 diff --git a/script/stackenv b/script/stackenv index af4a5bc13e..a65e29c677 100644 --- a/script/stackenv +++ b/script/stackenv @@ -1,5 +1,5 @@ # Prep the testing environment by creating the required testing resources and -# environment variables. This env is for theopenlab CI jobs, you might need +# environment variables. This env is for theopenlab CI jobs, you might need # to modify this according to your setup pushd /opt/stack/new/devstack @@ -7,7 +7,7 @@ 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 _NETWORK_ID=$(openstack network show private -c id -f value) -_SUBNET_ID=$(openstack subnet show private_subnet -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 ' ') @@ -23,4 +23,3 @@ echo export OS_FLAVOR_ID_RESIZE=98 >> openrc echo export OS_DOMAIN_ID=default >> openrc source openrc admin admin popd - From 06336d5594deb66a80d9e8b1a31c17a2041a5957 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 29 Sep 2019 21:46:47 -0600 Subject: [PATCH 0854/2296] Block Storage v1: Allow 202 when creating volumes (#1720) --- openstack/blockstorage/v1/volumes/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go index 1da94238b9..3e3ede1996 100644 --- a/openstack/blockstorage/v1/volumes/requests.go +++ b/openstack/blockstorage/v1/volumes/requests.go @@ -42,7 +42,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, + OkCodes: []int{200, 201, 202}, }) return } From 863d5406e68f374ee607452672b18a177134bbbc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 29 Sep 2019 21:48:51 -0600 Subject: [PATCH 0855/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09e9fed8d1..2efae0f832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ BUG FIXES * Changed struct type for options in `networking/v2/extensions/lbaas_v2/listeners` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1705] * Changed struct type for options in `networking/v2/extensions/lbaas_v2/loadbalancers` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1706] +* Fixed issue with `blockstorage/v1/volumes.Create` where the response was expected to be 202 [GH-1720] ## 0.4.0 (September 3, 2019) From d2315933e69c207e6458e424d954006cd5e2ad1e Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 3 Oct 2019 22:56:38 +0200 Subject: [PATCH 0856/2296] LBaaSv2: Properly handle update TLS and AllowedCIDRs references (#1723) --- openstack/loadbalancer/v2/listeners/requests.go | 4 ++-- .../networking/v2/extensions/lbaas_v2/listeners/requests.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 36d00baf85..0dea46abd9 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -185,10 +185,10 @@ type UpdateOpts struct { ConnLimit *int `json:"connection_limit,omitempty"` // A reference to a Barbican container of TLS secrets. - DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` + DefaultTlsContainerRef *string `json:"default_tls_container_ref,omitempty"` // A list of references to TLS secrets. - SniContainerRefs []string `json:"sni_container_refs,omitempty"` + SniContainerRefs *[]string `json:"sni_container_refs,omitempty"` // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index b0ea23de9c..aa4f7d15d7 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -167,10 +167,10 @@ type UpdateOpts struct { ConnLimit *int `json:"connection_limit,omitempty"` // A reference to a Barbican container of TLS secrets. - DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` + DefaultTlsContainerRef *string `json:"default_tls_container_ref,omitempty"` // A list of references to TLS secrets. - SniContainerRefs []string `json:"sni_container_refs,omitempty"` + SniContainerRefs *[]string `json:"sni_container_refs,omitempty"` // The administrative state of the Listener. A valid value is true (UP) // or false (DOWN). From dee9bb4c60eccb45e80e3d2d0f4ced546f421ad6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 3 Oct 2019 14:59:02 -0600 Subject: [PATCH 0857/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2efae0f832..374c3e047f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,11 @@ BUG FIXES * Changed struct type for options in `networking/v2/extensions/lbaas_v2/listeners` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1705] * Changed struct type for options in `networking/v2/extensions/lbaas_v2/loadbalancers` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1706] * Fixed issue with `blockstorage/v1/volumes.Create` where the response was expected to be 202 [GH-1720] +* Changed `DefaultTlsContainerRef` from `string` to `*string` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. +* Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. +* Changed `DefaultTlsContainerRef` from `string` to `*string` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. +* Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. + ## 0.4.0 (September 3, 2019) From ebedf40f8b434a0549ceba4e47a8f139f36992d9 Mon Sep 17 00:00:00 2001 From: freddebacker <35524902+freddebacker@users.noreply.github.com> Date: Fri, 4 Oct 2019 16:11:33 +0200 Subject: [PATCH 0858/2296] Add importing status to ImageStatus enum (#1725) --- openstack/imageservice/v2/images/types.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openstack/imageservice/v2/images/types.go b/openstack/imageservice/v2/images/types.go index d2f9cbd3bf..147be19927 100644 --- a/openstack/imageservice/v2/images/types.go +++ b/openstack/imageservice/v2/images/types.go @@ -35,6 +35,10 @@ const ( // ImageStatusDeactivated denotes that access to image data is not allowed to // any non-admin user. ImageStatusDeactivated ImageStatus = "deactivated" + + // ImageStatusImporting denotes that an import call has been made but that + // the image is not yet ready for use. + ImageStatusImporting ImageStatus = "importing" ) // ImageVisibility denotes an image that is fully available in Glance. From d845749a2684cd8037bdafb0ff89117a0446fe5c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 4 Oct 2019 08:12:20 -0600 Subject: [PATCH 0859/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 374c3e047f..947b0ba167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ IMPROVEMENTS * Added `compute/v2/extensions/tags.ReplaceAll` [GH-1694] * Added `compute/v2/extensions/tags.Delete` [GH-1699] * Added `compute/v2/extensions/tags.DeleteAll` [GH-1700] +* Added `ImageStatusImporting` as an image status [GH-1725] BUG FIXES From 37f025635558fa98a2a0164e31dd9ec3a680ab06 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 4 Oct 2019 11:01:59 -0600 Subject: [PATCH 0860/2296] Updating Go Modules (#1729) * Updating Go Modules * Travis: Removing Go 1.10, Adding 1.13 --- .travis.yml | 2 +- go.mod | 4 ++-- go.sum | 8 ++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9153a00fc5..31f80f8dbb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,9 @@ install: - GO111MODULE=off go get github.com/mattn/goveralls - GO111MODULE=off go get golang.org/x/tools/cmd/goimports go: -- "1.10" - "1.11" - "1.12" +- "1.13" - "tip" env: global: diff --git a/go.mod b/go.mod index d1ee3b472e..55c66f8542 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/gophercloud/gophercloud require ( - golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 - golang.org/x/sys v0.0.0-20190209173611-3b5209105503 // indirect + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 + golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3 // indirect gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index 33cb0be8aa..b3889ce2c1 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,15 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3 h1:2AmBLzhAfXj+2HCW09VCkJtHIYgHTIPcTeYqgP7Bwt0= +golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= From 2f1f16b804fcf7bb7c960c2b1ab0675160a62e65 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Sun, 6 Oct 2019 04:15:35 +1300 Subject: [PATCH 0861/2296] loadbalancer: Fix AllowedCIDRs for listener update (#1727) --- openstack/loadbalancer/v2/listeners/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 0dea46abd9..22bd7d8ef1 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -207,7 +207,7 @@ type UpdateOpts struct { TimeoutTCPInspect *int `json:"timeout_tcp_inspect,omitempty"` // A list of IPv4, IPv6 or mix of both CIDRs - AllowedCIDRs []string `json:"allowed_cidrs,omitempty"` + AllowedCIDRs *[]string `json:"allowed_cidrs,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. From 88bb612a053478ff6f4f9c13ce652639ee553760 Mon Sep 17 00:00:00 2001 From: redixin Date: Mon, 7 Oct 2019 04:54:00 +0300 Subject: [PATCH 0862/2296] Add disk/by_path to available introspection data (#1730) Disk by path (e.g. /dev/disk/by-path/pci-0000:00:07.0) is available in ironic inspector results and may be required by some projects. --- openstack/baremetalintrospection/v1/introspection/results.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 3e6055eff1..1159ae705f 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -234,6 +234,7 @@ type RootDiskType struct { Hctl string `json:"hctl"` Model string `json:"model"` Name string `json:"name"` + ByPath string `json:"by_path"` Rotational bool `json:"rotational"` Serial string `json:"serial"` Size int `json:"size"` From b198dde5988ba8c03f18cdf6077035c231c6acc4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 6 Oct 2019 19:55:43 -0600 Subject: [PATCH 0863/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 947b0ba167..d9e4d9cc51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ IMPROVEMENTS * Added `compute/v2/extensions/tags.Delete` [GH-1699] * Added `compute/v2/extensions/tags.DeleteAll` [GH-1700] * Added `ImageStatusImporting` as an image status [GH-1725] +* Added `ByPath` to `baremetalintrospection/v1/introspection.RootDiskType` [GH-1730] BUG FIXES From 0ef034d8b463562a083e72c2a6627e8d0debd936 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 7 Oct 2019 08:15:04 +0300 Subject: [PATCH 0864/2296] Compute V2: allow to read Tags from Server body (#1734) Add compute/v2/extensions/tags.ServerTagsExt to allow read tags right from a Server body. Fix compute/v2/extensions/tags to use the valid microversion. Add ServerTagsExt usage example to compute/v2/extensions/tags docs. Add ServerTagsExt into ServerTags acceptance test. --- .../openstack/compute/v2/servers_test.go | 15 ++++++++ openstack/compute/v2/extensions/tags/doc.go | 37 ++++++++++++++++--- .../compute/v2/extensions/tags/results.go | 6 +++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 4773da4a5a..221983827a 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -512,6 +512,21 @@ func TestServersTags(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) + // All the following calls should work with "2.26" microversion. + client.Microversion = "2.26" + + // Check server tags in body. + type serverWithTagsExt struct { + servers.Server + tags.ServerTagsExt + } + + serverWithTags := serverWithTagsExt{} + + err = servers.Get(client, server.ID).ExtractInto(&serverWithTags) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, []string{"tag1", "tag2"}, serverWithTags.Tags) + // Check all tags. allTags, err := tags.List(client, server.ID).Extract() th.AssertNoErr(t, err) diff --git a/openstack/compute/v2/extensions/tags/doc.go b/openstack/compute/v2/extensions/tags/doc.go index bcecd62446..a521ba2c28 100644 --- a/openstack/compute/v2/extensions/tags/doc.go +++ b/openstack/compute/v2/extensions/tags/doc.go @@ -5,7 +5,7 @@ This extension is available since 2.26 Compute V2 API microversion. Example to List all server Tags - client.Microversion = "2.62" + client.Microversion = "2.26" serverTags, err := tags.List(client, serverID).Extract() if err != nil { @@ -16,7 +16,7 @@ Example to List all server Tags Example to Check if the specific Tag exists on a server - client.Microversion = "2.62" + client.Microversion = "2.26" exists, err := tags.Check(client, serverID, tag).Extract() if err != nil { @@ -31,7 +31,7 @@ Example to Check if the specific Tag exists on a server Example to Replace all Tags on a server - client.Microversion = "2.62" + client.Microversion = "2.26" newTags, err := tags.ReplaceAll(client, serverID, tags.ReplaceAllOpts{Tags: []string{"foo", "bar"}}).Extract() if err != nil { @@ -42,7 +42,7 @@ Example to Replace all Tags on a server Example to Add a new Tag on a server - client.Microversion = "2.62" + client.Microversion = "2.26" err := tags.Add(client, serverID, "foo").ExtractErr() if err != nil { @@ -51,7 +51,7 @@ Example to Add a new Tag on a server Example to Delete a Tag on a server - client.Microversion = "2.62" + client.Microversion = "2.26" err := tags.Delete(client, serverID, "foo").ExtractErr() if err != nil { @@ -60,11 +60,36 @@ Example to Delete a Tag on a server Example to Delete all Tags on a server - client.Microversion = "2.62" + client.Microversion = "2.26" err := tags.DeleteAll(client, serverID).ExtractErr() if err != nil { log.Fatal(err) } + +Example of Extend server result with Tags: + + client.Microversion = "2.26" + + type ServerWithTags struct { + servers.Server + tags.ServerTagsExt + } + + var allServers []ServerWithTags + + allPages, err := servers.List(client, nil).AllPages() + if err != nil { + log.Fatal(err) + } + + err = servers.ExtractServersInto(allPages, &allServers) + if err != nil { + log.Fatal(err) + } + + for _, server := range allServers { + fmt.Println(server.Tags) + } */ package tags diff --git a/openstack/compute/v2/extensions/tags/results.go b/openstack/compute/v2/extensions/tags/results.go index c73b7a6cb0..294744e7fe 100644 --- a/openstack/compute/v2/extensions/tags/results.go +++ b/openstack/compute/v2/extensions/tags/results.go @@ -2,6 +2,12 @@ package tags import "github.com/gophercloud/gophercloud" +// ServerTagsExt is an extension to the base Server object. +type ServerTagsExt struct { + // Tags contains a list of server tags. + Tags []string `json:"tags"` +} + type commonResult struct { gophercloud.Result } From d02d262f38179965f1d3e65990dd2127dbf913fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BCringer?= Date: Tue, 8 Oct 2019 04:26:53 +0200 Subject: [PATCH 0865/2296] added attachments (#1732) * added attachments * fix tests * fix tests * fix tests * review fixes --- openstack/compute/v2/servers/results.go | 7 ++++ .../compute/v2/servers/testing/fixtures.go | 37 ++++++++++++------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index f973d1ea0e..cec633e77a 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -212,10 +212,17 @@ type Server struct { // to it. SecurityGroups []map[string]interface{} `json:"security_groups"` + // AttachedVolumes includes the volume attachments of this instance + AttachedVolumes []AttachedVolume `json:"os-extended-volumes:volumes_attached"` + // Fault contains failure information about a server. Fault Fault `json:"fault"` } +type AttachedVolume struct { + ID string `json:"id"` +} + type Fault struct { Code int `json:"code"` Created time.Time `json:"created"` diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index db3819abc0..029a224bf9 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -78,7 +78,11 @@ const ServerListBody = ` "created": "2014-09-25T13:10:02Z", "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", "OS-DCF:diskConfig": "MANUAL", - "os-extended-volumes:volumes_attached": [], + "os-extended-volumes:volumes_attached": [ + { + "id": "2bdbc40f-a277-45d4-94ac-d9881c777d33" + } + ], "accessIPv4": "", "accessIPv6": "", "progress": 0, @@ -530,6 +534,11 @@ var ( Created: herpTimeCreated, TenantID: "fcad67a6189847c4aecfa3c81a05783b", Metadata: map[string]string{}, + AttachedVolumes: []servers.AttachedVolume{ + { + ID: "2bdbc40f-a277-45d4-94ac-d9881c777d33", + }, + }, SecurityGroups: []map[string]interface{}{ map[string]interface{}{ "name": "default", @@ -582,12 +591,13 @@ var ( }, }, }, - ID: "9e5476bd-a4ec-4653-93d6-72c93aa682ba", - UserID: "9349aff8be7545ac9d2f1d00999a23cd", - Name: "derp", - Created: derpTimeCreated, - TenantID: "fcad67a6189847c4aecfa3c81a05783b", - Metadata: map[string]string{}, + ID: "9e5476bd-a4ec-4653-93d6-72c93aa682ba", + UserID: "9349aff8be7545ac9d2f1d00999a23cd", + Name: "derp", + Created: derpTimeCreated, + TenantID: "fcad67a6189847c4aecfa3c81a05783b", + Metadata: map[string]string{}, + AttachedVolumes: []servers.AttachedVolume{}, SecurityGroups: []map[string]interface{}{ map[string]interface{}{ "name": "default", @@ -634,12 +644,13 @@ var ( }, }, }, - ID: "9e5476bd-a4ec-4653-93d6-72c93aa682bb", - UserID: "9349aff8be7545ac9d2f1d00999a23cd", - Name: "merp", - Created: merpTimeCreated, - TenantID: "fcad67a6189847c4aecfa3c81a05783b", - Metadata: map[string]string{}, + ID: "9e5476bd-a4ec-4653-93d6-72c93aa682bb", + UserID: "9349aff8be7545ac9d2f1d00999a23cd", + Name: "merp", + Created: merpTimeCreated, + TenantID: "fcad67a6189847c4aecfa3c81a05783b", + Metadata: map[string]string{}, + AttachedVolumes: []servers.AttachedVolume{}, SecurityGroups: []map[string]interface{}{ map[string]interface{}{ "name": "default", From dc8355d92f2146ed0e8ba3130db67087bfa8f213 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 7 Oct 2019 20:27:49 -0600 Subject: [PATCH 0866/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9e4d9cc51..c3a4d3fbd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ IMPROVEMENTS -* Added `VolumeType` to `cmopute/v2/extensions/bootfromvolume.BlockDevice` [GH-1690] +* Added `VolumeType` to `compute/v2/extensions/bootfromvolume.BlockDevice` [GH-1690] * Added `networking/v2/extensions/layer3/portforwarding.List` [GH-1688] * Added `networking/v2/extensions/layer3/portforwarding.Get` [GH-1698] * Added `compute/v2/extensions/tags.ReplaceAll` [GH-1696] @@ -18,6 +18,7 @@ IMPROVEMENTS * Added `compute/v2/extensions/tags.DeleteAll` [GH-1700] * Added `ImageStatusImporting` as an image status [GH-1725] * Added `ByPath` to `baremetalintrospection/v1/introspection.RootDiskType` [GH-1730] +* Added `AttachedVolumes` to `compute/v2/servers.Server` [GH-1732] BUG FIXES From 8fb63ffa30caa6a25ccef347f71837308fe77250 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 7 Oct 2019 20:33:13 -0600 Subject: [PATCH 0867/2296] Update CHANGELOG.md --- CHANGELOG.md | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3a4d3fbd6..f6f035a17b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,50 +2,50 @@ IMPROVEMENTS -* Added `VolumeType` to `compute/v2/extensions/bootfromvolume.BlockDevice` [GH-1690] -* Added `networking/v2/extensions/layer3/portforwarding.List` [GH-1688] -* Added `networking/v2/extensions/layer3/portforwarding.Get` [GH-1698] -* Added `compute/v2/extensions/tags.ReplaceAll` [GH-1696] -* Added `compute/v2/extensions/tags.Add` [GH-1696] -* Added `networking/v2/extensions/layer3/portforwarding.Update` [GH-1703] -* Added `ExtractDomain` method to token results in `identity/v3/tokens` [GH-1712] -* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.CreateOpts` [GH-1710] -* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1710] -* Added `AllowedCIDRs` to `loadbalancer/v2/listeners.Listener` [GH-1710] -* Added `compute/v2/extensions/tags.Add` [GH-1695] -* Added `compute/v2/extensions/tags.ReplaceAll` [GH-1694] -* Added `compute/v2/extensions/tags.Delete` [GH-1699] -* Added `compute/v2/extensions/tags.DeleteAll` [GH-1700] -* Added `ImageStatusImporting` as an image status [GH-1725] -* Added `ByPath` to `baremetalintrospection/v1/introspection.RootDiskType` [GH-1730] -* Added `AttachedVolumes` to `compute/v2/servers.Server` [GH-1732] +* 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) BUG FIXES -* Changed struct type for options in `networking/v2/extensions/lbaas_v2/listeners` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1705] -* Changed struct type for options in `networking/v2/extensions/lbaas_v2/loadbalancers` to `UpdateOptsBuilder` interface instead of specific UpdateOpts type [GH-1706] -* Fixed issue with `blockstorage/v1/volumes.Create` where the response was expected to be 202 [GH-1720] -* Changed `DefaultTlsContainerRef` from `string` to `*string` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. -* Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `loadbalancer/v2/listeners.UpdateOpts` to allow the value to be removed during update. -* Changed `DefaultTlsContainerRef` from `string` to `*string` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. -* Changed `SniContainerRefs` from `[]string{}` to `*[]string{}` in `networking/v2/extensions/lbaas_v2/listeners.UpdateOpts` to allow the value to be removed during update. +* 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] -* Added `blockstorage/extensions/quotasets.results.QuotaUsageSet.Groups` [GH-1668] -* Added `containerinfra/v1/clusters.CreateOpts.FixedNetwork` [GH-1674] -* Added `containerinfra/v1/clusters.CreateOpts.FixedSubnet` [GH-1676] -* Added `containerinfra/v1/clusters.CreateOpts.FloatingIPEnabled` [GH-1677] -* Added `CreatedAt` and `UpdatedAt` to `loadbalancers/v2/loadbalancers.LoadBalancer` [GH-1681] -* Added `networking/v2/extensions/layer3/portforwarding.Create` [GH-1651] -* Added `networking/v2/extensions/agents.ListDHCPNetworks` [GH-1686] -* Added `networking/v2/extensions/layer3/portforwarding.Delete` [GH-1652] -* Added `compute/v2/extensions/tags.List` [GH-1679] -* Added `compute/v2/extensions/tags.Check` [GH-1679] +* 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 From 1effcf51002d387fb1dd1ed9e92a0ed77107e7c7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 7 Oct 2019 20:34:54 -0600 Subject: [PATCH 0868/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6f035a17b..a702f4bbdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ IMPROVEMENTS * 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] BUG FIXES From f3883c74503f75daa271e53fd3973d64bfae4699 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 11 Oct 2019 17:12:39 +0200 Subject: [PATCH 0869/2296] Octavia LBaaSv2: Allow setting an empty members list (#1736) --- openstack/loadbalancer/v2/pools/requests.go | 2 +- .../loadbalancer/v2/pools/testing/fixtures.go | 15 +++++++++++++++ .../v2/pools/testing/requests_test.go | 9 +++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index fbf16dc288..2b57e499e0 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -354,7 +354,7 @@ func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]interface // BatchUpdateMembers updates the pool members in batch func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []BatchUpdateMemberOpts) (r UpdateMembersResult) { - var members []map[string]interface{} + members := []map[string]interface{}{} for _, opt := range opts { b, err := opt.ToBatchMemberUpdateMap() if err != nil { diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures.go b/openstack/loadbalancer/v2/pools/testing/fixtures.go index 4c1b8f18c1..3dd296ac87 100644 --- a/openstack/loadbalancer/v2/pools/testing/fixtures.go +++ b/openstack/loadbalancer/v2/pools/testing/fixtures.go @@ -438,3 +438,18 @@ func HandleMembersUpdateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +// HandleEmptyMembersUpdateSuccessfully sets up the test server to respond to an empty batch member Update request. +func HandleEmptyMembersUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", 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, `{ + "members": [] + }`) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go index 106cb0303c..492820a119 100644 --- a/openstack/loadbalancer/v2/pools/testing/requests_test.go +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -293,6 +293,15 @@ func TestBatchUpdateMembers(t *testing.T) { th.AssertNoErr(t, res.Err) } +func TestEmptyBatchUpdateMembers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleEmptyMembersUpdateSuccessfully(t) + + res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{}) + th.AssertNoErr(t, res.Err) +} + func TestRequiredBatchUpdateMemberOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 9c34d1968ad7554db7fc49f1f43fe63633d3320f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 11 Oct 2019 09:13:43 -0600 Subject: [PATCH 0870/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a702f4bbdb..e1894e2863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ IMPROVEMENTS * 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) BUG FIXES From f1122b79c3ddef266cbdd0b2ed65711d82d35b59 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 13 Oct 2019 19:49:48 +0200 Subject: [PATCH 0871/2296] Octavia LBaaS v2: Allow unsetting the member's subnet id in batch members update (#1738) --- openstack/loadbalancer/v2/pools/requests.go | 42 ++++++++++++++++++- .../v2/pools/testing/requests_test.go | 18 ++++---- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 2b57e499e0..79265d9bb5 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -345,11 +345,49 @@ type BatchUpdateMemberOptsBuilder interface { ToBatchMemberUpdateMap() (map[string]interface{}, error) } -type BatchUpdateMemberOpts CreateMemberOpts +// BatchUpdateMemberOpts is the common options struct used in this package's BatchUpdateMembers +// operation. +type BatchUpdateMemberOpts struct { + // The IP address of the member to receive traffic from the load balancer. + Address string `json:"address" required:"true"` + + // The port on which to listen for client traffic. + ProtocolPort int `json:"protocol_port" required:"true"` + + // Name of the Member. + Name *string `json:"name,omitempty"` + + // ProjectID is the UUID of the project who owns the Member. + // Only administrative users can specify a project UUID other than their own. + ProjectID string `json:"project_id,omitempty"` + + // A positive integer value that indicates the relative portion of traffic + // that this member should receive from the pool. For example, a member with + // a weight of 10 receives five times as much traffic as a member with a + // weight of 2. + Weight *int `json:"weight,omitempty"` + + // If you omit this parameter, LBaaS uses the vip_subnet_id parameter value + // for the subnet UUID. + SubnetID *string `json:"subnet_id,omitempty"` + + // The administrative state of the Pool. A valid value is true (UP) + // or false (DOWN). + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} // ToBatchMemberUpdateMap builds a request body from BatchUpdateMemberOpts. func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "") + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if b["subnet_id"] == "" { + b["subnet_id"] = nil + } + + return b, nil } // BatchUpdateMembers updates the pool members in batch diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go index 492820a119..2b88cb2cfd 100644 --- a/openstack/loadbalancer/v2/pools/testing/requests_test.go +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -270,22 +270,25 @@ func TestBatchUpdateMembers(t *testing.T) { defer th.TeardownHTTP() HandleMembersUpdateSuccessfully(t) + name_1 := "web-server-1" weight_1 := 20 + subnetID := "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa" member1 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.16", ProtocolPort: 80, - Name: "web-server-1", - SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + Name: &name_1, + SubnetID: &subnetID, Weight: &weight_1, } + name_2 := "web-server-2" weight_2 := 10 member2 := pools.BatchUpdateMemberOpts{ Address: "192.0.2.17", ProtocolPort: 80, - Name: "web-server-2", + Name: &name_2, Weight: &weight_2, - SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + SubnetID: &subnetID, } members := []pools.BatchUpdateMemberOpts{member1, member2} @@ -306,9 +309,10 @@ func TestRequiredBatchUpdateMemberOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() + name := "web-server-1" res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { - Name: "web-server-1", + Name: &name, }, }) if res.Err == nil { @@ -318,7 +322,7 @@ func TestRequiredBatchUpdateMemberOpts(t *testing.T) { res = pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { Address: "192.0.2.17", - Name: "web-server-1", + Name: &name, }, }) if res.Err == nil { @@ -328,7 +332,7 @@ func TestRequiredBatchUpdateMemberOpts(t *testing.T) { res = pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { ProtocolPort: 80, - Name: "web-server-1", + Name: &name, }, }) if res.Err == nil { From c6323632a30041decdaf5ea0475e80515d7668c0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Oct 2019 11:51:26 -0600 Subject: [PATCH 0872/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1894e2863..b44e323920 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ IMPROVEMENTS * 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 From cf0980d0cbc9d8fd2f8923d04102fa19aa4fae69 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 13 Oct 2019 11:52:44 -0600 Subject: [PATCH 0873/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b44e323920..4e7e4dda3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.5.0 (Unreleased) +## 0.6.0 (Unreleased) + +## 0.5.0 (October 13, 2019) IMPROVEMENTS From 0c7b9d111e3774e334c49c863f431cf8c63a57e4 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 14 Oct 2019 18:39:47 +0300 Subject: [PATCH 0874/2296] Networking V2: add quotas package (#1742) * Networking V2: add quotas package Add openstack/networking/v2/extensions/quotas.Get. Add unit and acceptance tests. * Networking V2 acc tests: fix +build tag for quotas Add a blank line. --- .../v2/extensions/quotas/quotas_test.go | 25 +++++++++ .../networking/v2/extensions/quotas/doc.go | 12 +++++ .../v2/extensions/quotas/requests.go | 9 ++++ .../v2/extensions/quotas/results.go | 52 +++++++++++++++++++ .../v2/extensions/quotas/testing/doc.go | 2 + .../v2/extensions/quotas/testing/fixtures.go | 31 +++++++++++ .../quotas/testing/requests_test.go | 30 +++++++++++ .../networking/v2/extensions/quotas/urls.go | 9 ++++ script/acceptancetest | 1 + 9 files changed, 171 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go create mode 100644 openstack/networking/v2/extensions/quotas/doc.go create mode 100644 openstack/networking/v2/extensions/quotas/requests.go create mode 100644 openstack/networking/v2/extensions/quotas/results.go create mode 100644 openstack/networking/v2/extensions/quotas/testing/doc.go create mode 100644 openstack/networking/v2/extensions/quotas/testing/fixtures.go create mode 100644 openstack/networking/v2/extensions/quotas/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/quotas/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go b/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go new file mode 100644 index 0000000000..3876dc307e --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go @@ -0,0 +1,25 @@ +// +build acceptance networking quotas + +package quotas + +import ( + "os" + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestQuotasGet(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + quotasInfo, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotasInfo) +} diff --git a/openstack/networking/v2/extensions/quotas/doc.go b/openstack/networking/v2/extensions/quotas/doc.go new file mode 100644 index 0000000000..749a7545c6 --- /dev/null +++ b/openstack/networking/v2/extensions/quotas/doc.go @@ -0,0 +1,12 @@ +/* +Package quotas provides the ability to retrieve and manage Networking quotas through the Neutron API. + +Example to Get project quotas + + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + quotasInfo, err := quotas.Get(networkClient, projectID).Extract() + if err != nil { + log.Fatal(err) + } +*/ +package quotas diff --git a/openstack/networking/v2/extensions/quotas/requests.go b/openstack/networking/v2/extensions/quotas/requests.go new file mode 100644 index 0000000000..159ca5e5e7 --- /dev/null +++ b/openstack/networking/v2/extensions/quotas/requests.go @@ -0,0 +1,9 @@ +package quotas + +import "github.com/gophercloud/gophercloud" + +// Get returns Networking Quotas for a project. +func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { + _, r.Err = client.Get(getURL(client, projectID), &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/quotas/results.go b/openstack/networking/v2/extensions/quotas/results.go new file mode 100644 index 0000000000..6d99b2b8eb --- /dev/null +++ b/openstack/networking/v2/extensions/quotas/results.go @@ -0,0 +1,52 @@ +package quotas + +import "github.com/gophercloud/gophercloud" + +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:"quota"` + } + 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 Networking quotas for a project. +type Quota struct { + // FloatingIP represents a number of floating IPs. A "-1" value means no limit. + FloatingIP int `json:"floatingip"` + + // Network represents a number of networks. A "-1" value means no limit. + Network int `json:"network"` + + // Port represents a number of ports. A "-1" value means no limit. + Port int `json:"port"` + + // RBACPolicy represents a number of RBAC policies. A "-1" value means no limit. + RBACPolicy int `json:"rbac_policy"` + + // Router represents a number of routers. A "-1" value means no limit. + Router int `json:"router"` + + // SecurityGroup represents a number of security groups. A "-1" value means no limit. + SecurityGroup int `json:"security_group"` + + // SecurityGroupRule represents a number of security group rules. A "-1" value means no limit. + SecurityGroupRule int `json:"security_group_rule"` + + // Subnet represents a number of subnets. A "-1" value means no limit. + Subnet int `json:"subnet"` + + // SubnetPool represents a number of subnet pools. A "-1" value means no limit. + SubnetPool int `json:"subnetpool"` +} diff --git a/openstack/networking/v2/extensions/quotas/testing/doc.go b/openstack/networking/v2/extensions/quotas/testing/doc.go new file mode 100644 index 0000000000..404d517542 --- /dev/null +++ b/openstack/networking/v2/extensions/quotas/testing/doc.go @@ -0,0 +1,2 @@ +// quotas unit tests +package testing diff --git a/openstack/networking/v2/extensions/quotas/testing/fixtures.go b/openstack/networking/v2/extensions/quotas/testing/fixtures.go new file mode 100644 index 0000000000..1f092c4cb0 --- /dev/null +++ b/openstack/networking/v2/extensions/quotas/testing/fixtures.go @@ -0,0 +1,31 @@ +package testing + +import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" + +const GetResponseRaw = ` +{ + "quota": { + "floatingip": 15, + "network": 20, + "port": 25, + "rbac_policy": -1, + "router": 30, + "security_group": 35, + "security_group_rule": 40, + "subnet": 45, + "subnetpool": -1 + } +} +` + +var GetResponse = quotas.Quota{ + FloatingIP: 15, + Network: 20, + Port: 25, + RBACPolicy: -1, + Router: 30, + SecurityGroup: 35, + SecurityGroupRule: 40, + Subnet: 45, + SubnetPool: -1, +} diff --git a/openstack/networking/v2/extensions/quotas/testing/requests_test.go b/openstack/networking/v2/extensions/quotas/testing/requests_test.go new file mode 100644 index 0000000000..99959531c4 --- /dev/null +++ b/openstack/networking/v2/extensions/quotas/testing/requests_test.go @@ -0,0 +1,30 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/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.Fprintf(w, GetResponseRaw) + }) + + q, err := quotas.Get(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &GetResponse) +} diff --git a/openstack/networking/v2/extensions/quotas/urls.go b/openstack/networking/v2/extensions/quotas/urls.go new file mode 100644 index 0000000000..8c33b42841 --- /dev/null +++ b/openstack/networking/v2/extensions/quotas/urls.go @@ -0,0 +1,9 @@ +package quotas + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "quotas" + +func getURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID) +} diff --git a/script/acceptancetest b/script/acceptancetest index 69341a9c6f..7aca654bdb 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -66,6 +66,7 @@ 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 From a31e379ad56eb9e8a2122b5712cfb0573638351e Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 15 Oct 2019 23:23:24 +0300 Subject: [PATCH 0875/2296] Networking V2: add quotas.Update method (#1747) Add openstack/networking/v2/extensions/quotas.Update. Add unit and acceptance tests. Update package docs. --- .../networking/v2/extensions/quotas/quotas.go | 18 ++++++ .../v2/extensions/quotas/quotas_test.go | 39 +++++++++++++ .../networking/v2/extensions/quotas/doc.go | 24 ++++++++ .../v2/extensions/quotas/requests.go | 56 +++++++++++++++++++ .../v2/extensions/quotas/results.go | 6 ++ .../v2/extensions/quotas/testing/fixtures.go | 28 ++++++++++ .../quotas/testing/requests_test.go | 31 ++++++++++ .../networking/v2/extensions/quotas/urls.go | 10 +++- 8 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/networking/v2/extensions/quotas/quotas.go diff --git a/acceptance/openstack/networking/v2/extensions/quotas/quotas.go b/acceptance/openstack/networking/v2/extensions/quotas/quotas.go new file mode 100644 index 0000000000..8b1a95a5c0 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/quotas/quotas.go @@ -0,0 +1,18 @@ +package quotas + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" +) + +var updateOpts = quotas.UpdateOpts{ + FloatingIP: gophercloud.IntToPointer(10), + Network: gophercloud.IntToPointer(-1), + Port: gophercloud.IntToPointer(11), + RBACPolicy: gophercloud.IntToPointer(0), + Router: gophercloud.IntToPointer(-1), + SecurityGroup: gophercloud.IntToPointer(12), + SecurityGroupRule: gophercloud.IntToPointer(13), + Subnet: gophercloud.IntToPointer(14), + SubnetPool: gophercloud.IntToPointer(15), +} diff --git a/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go b/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go index 3876dc307e..048e93e5f5 100644 --- a/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go +++ b/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go @@ -3,7 +3,9 @@ package quotas import ( + "log" "os" + "reflect" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -23,3 +25,40 @@ func TestQuotasGet(t *testing.T) { tools.PrintResource(t, quotasInfo) } + +func TestQuotasUpdate(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + originalQuotas, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() + th.AssertNoErr(t, err) + + newQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newQuotas) + + if reflect.DeepEqual(originalQuotas, newQuotas) { + log.Fatal("Original and New Networking Quotas are the same") + } + + // Restore original quotas. + restoredQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), quotas.UpdateOpts{ + FloatingIP: &originalQuotas.FloatingIP, + Network: &originalQuotas.Network, + Port: &originalQuotas.Port, + RBACPolicy: &originalQuotas.RBACPolicy, + Router: &originalQuotas.Router, + SecurityGroup: &originalQuotas.SecurityGroup, + SecurityGroupRule: &originalQuotas.SecurityGroupRule, + Subnet: &originalQuotas.Subnet, + SubnetPool: &originalQuotas.SubnetPool, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, originalQuotas, restoredQuotas) + + tools.PrintResource(t, restoredQuotas) +} diff --git a/openstack/networking/v2/extensions/quotas/doc.go b/openstack/networking/v2/extensions/quotas/doc.go index 749a7545c6..563e9c688b 100644 --- a/openstack/networking/v2/extensions/quotas/doc.go +++ b/openstack/networking/v2/extensions/quotas/doc.go @@ -8,5 +8,29 @@ Example to Get project quotas if err != nil { log.Fatal(err) } + + 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), + } + 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/quotas/requests.go b/openstack/networking/v2/extensions/quotas/requests.go index 159ca5e5e7..973e46e104 100644 --- a/openstack/networking/v2/extensions/quotas/requests.go +++ b/openstack/networking/v2/extensions/quotas/requests.go @@ -7,3 +7,59 @@ func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { _, r.Err = client.Get(getURL(client, projectID), &r.Body, nil) 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 Networking Quotas. +type UpdateOpts struct { + // FloatingIP represents a number of floating IPs. A "-1" value means no limit. + FloatingIP *int `json:"floatingip,omitempty"` + + // Network represents a number of networks. A "-1" value means no limit. + Network *int `json:"network,omitempty"` + + // Port represents a number of ports. A "-1" value means no limit. + Port *int `json:"port,omitempty"` + + // RBACPolicy represents a number of RBAC policies. A "-1" value means no limit. + RBACPolicy *int `json:"rbac_policy,omitempty"` + + // Router represents a number of routers. A "-1" value means no limit. + Router *int `json:"router,omitempty"` + + // SecurityGroup represents a number of security groups. A "-1" value means no limit. + SecurityGroup *int `json:"security_group,omitempty"` + + // SecurityGroupRule represents a number of security group rules. A "-1" value means no limit. + SecurityGroupRule *int `json:"security_group_rule,omitempty"` + + // Subnet represents a number of subnets. A "-1" value means no limit. + Subnet *int `json:"subnet,omitempty"` + + // SubnetPool represents a number of subnet pools. A "-1" value means no limit. + SubnetPool *int `json:"subnetpool,omitempty"` +} + +// ToQuotaUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "quota") +} + +// Update accepts a UpdateOpts struct and updates an existing Networking Quotas using the +// values provided. +func Update(c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + + return +} diff --git a/openstack/networking/v2/extensions/quotas/results.go b/openstack/networking/v2/extensions/quotas/results.go index 6d99b2b8eb..6219abb017 100644 --- a/openstack/networking/v2/extensions/quotas/results.go +++ b/openstack/networking/v2/extensions/quotas/results.go @@ -21,6 +21,12 @@ type GetResult struct { commonResult } +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Quota. +type UpdateResult struct { + commonResult +} + // Quota contains Networking quotas for a project. type Quota struct { // FloatingIP represents a number of floating IPs. A "-1" value means no limit. diff --git a/openstack/networking/v2/extensions/quotas/testing/fixtures.go b/openstack/networking/v2/extensions/quotas/testing/fixtures.go index 1f092c4cb0..fd8b6181af 100644 --- a/openstack/networking/v2/extensions/quotas/testing/fixtures.go +++ b/openstack/networking/v2/extensions/quotas/testing/fixtures.go @@ -29,3 +29,31 @@ var GetResponse = quotas.Quota{ Subnet: 45, SubnetPool: -1, } + +const UpdateRequestResponseRaw = ` +{ + "quota": { + "floatingip": 0, + "network": -1, + "port": 5, + "rbac_policy": 10, + "router": 15, + "security_group": 20, + "security_group_rule": -1, + "subnet": 25, + "subnetpool": 0 + } +} +` + +var UpdateResponse = quotas.Quota{ + FloatingIP: 0, + Network: -1, + Port: 5, + RBACPolicy: 10, + Router: 15, + SecurityGroup: 20, + SecurityGroupRule: -1, + Subnet: 25, + SubnetPool: 0, +} diff --git a/openstack/networking/v2/extensions/quotas/testing/requests_test.go b/openstack/networking/v2/extensions/quotas/testing/requests_test.go index 99959531c4..8d7ffad36c 100644 --- a/openstack/networking/v2/extensions/quotas/testing/requests_test.go +++ b/openstack/networking/v2/extensions/quotas/testing/requests_test.go @@ -5,6 +5,7 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" th "github.com/gophercloud/gophercloud/testhelper" @@ -28,3 +29,33 @@ func TestGet(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &GetResponse) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/quotas/0a73845280574ad389c292f6a74afa76", 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") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateRequestResponseRaw) + }) + + q, err := quotas.Update(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", 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), + }).Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &UpdateResponse) +} diff --git a/openstack/networking/v2/extensions/quotas/urls.go b/openstack/networking/v2/extensions/quotas/urls.go index 8c33b42841..cf7dcbde65 100644 --- a/openstack/networking/v2/extensions/quotas/urls.go +++ b/openstack/networking/v2/extensions/quotas/urls.go @@ -4,6 +4,14 @@ import "github.com/gophercloud/gophercloud" const resourcePath = "quotas" -func getURL(c *gophercloud.ServiceClient, projectID string) string { +func resourceURL(c *gophercloud.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID) } + +func getURL(c *gophercloud.ServiceClient, projectID string) string { + return resourceURL(c, projectID) +} + +func updateURL(c *gophercloud.ServiceClient, projectID string) string { + return resourceURL(c, projectID) +} From eaf713e4ccee1dc36e6bb81265192b269fbaa4cb Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Tue, 15 Oct 2019 23:25:05 +0300 Subject: [PATCH 0876/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7e4dda3b..c987c0cfeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## 0.6.0 (Unreleased) +* Added `networking/v2/extensions/quotas.Get` [GH-1742] +* Added `networking/v2/extensions/quotas.Update` [GH-1747] + ## 0.5.0 (October 13, 2019) IMPROVEMENTS From 87733c4331049ee41633a3c79a25fe8d7b724b69 Mon Sep 17 00:00:00 2001 From: Kevin Bulebush Date: Tue, 15 Oct 2019 21:52:23 -0400 Subject: [PATCH 0877/2296] Fixes Load Balancer Flavor Selection (#1744) The attribute to specify a Load Balancer flavor by ID is 'flavor_id' and not 'flavor' for both Octavia and Neutron LBaaSv2. References: https://docs.openstack.org/api-ref/network/v2/#load-balancer-as-a-service-2-0-deprecated https://docs.openstack.org/api-ref/load-balancer/v2/?expanded=create-a-load-balancer-detail#create-a-load-balancer --- openstack/loadbalancer/v2/loadbalancers/doc.go | 2 +- .../loadbalancer/v2/loadbalancers/requests.go | 4 ++-- .../loadbalancer/v2/loadbalancers/results.go | 2 +- .../v2/loadbalancers/testing/fixtures.go | 16 ++++++++-------- .../v2/loadbalancers/testing/requests_test.go | 2 +- .../v2/extensions/lbaas_v2/loadbalancers/doc.go | 2 +- .../lbaas_v2/loadbalancers/requests.go | 4 ++-- .../extensions/lbaas_v2/loadbalancers/results.go | 2 +- .../lbaas_v2/loadbalancers/testing/fixtures.go | 16 ++++++++-------- .../loadbalancers/testing/requests_test.go | 2 +- 10 files changed, 26 insertions(+), 26 deletions(-) diff --git a/openstack/loadbalancer/v2/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go index 83cc6c44f4..5c2b42d60a 100644 --- a/openstack/loadbalancer/v2/loadbalancers/doc.go +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -29,7 +29,7 @@ Example to Create a Load Balancer AdminStateUp: gophercloud.Enabled, VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", - Flavor: "medium", + FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", Tags: []string{"test", "stage"}, } diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 4ff4210fa2..f32a7f9853 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -28,7 +28,7 @@ type ListOpts struct { ID string `q:"id"` OperatingStatus string `q:"operating_status"` Name string `q:"name"` - Flavor string `q:"flavor"` + FlavorID string `q:"flavor_id"` Provider string `q:"provider"` Limit int `q:"limit"` Marker string `q:"marker"` @@ -109,7 +109,7 @@ type CreateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` // The UUID of a flavor. - Flavor string `json:"flavor,omitempty"` + FlavorID string `json:"flavor_id,omitempty"` // The name of the provider. Provider string `json:"provider,omitempty"` diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 2cd54a61df..14d64a3586 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -57,7 +57,7 @@ type LoadBalancer struct { Name string `json:"name"` // The UUID of a flavor if set. - Flavor string `json:"flavor"` + FlavorID string `json:"flavor_id"` // The name of the provider. Provider string `json:"provider"` diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index 83a2ee4225..f93b294db3 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -28,7 +28,7 @@ const LoadbalancersListBody = ` "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154", "vip_address": "10.30.176.47", "vip_port_id": "2a22e552-a347-44fd-b530-1f2b1b2a6735", - "flavor": "small", + "flavor_id": "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "ACTIVE", @@ -45,7 +45,7 @@ const LoadbalancersListBody = ` "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor": "medium", + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", @@ -69,7 +69,7 @@ const SingleLoadbalancerBody = ` "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor": "medium", + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", @@ -92,7 +92,7 @@ const PostUpdateLoadbalancerBody = ` "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor": "medium", + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", @@ -166,7 +166,7 @@ var ( VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154", VipAddress: "10.30.176.47", VipPortID: "2a22e552-a347-44fd-b530-1f2b1b2a6735", - Flavor: "small", + FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "ACTIVE", @@ -183,7 +183,7 @@ var ( VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", - Flavor: "medium", + FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", @@ -200,7 +200,7 @@ var ( VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", - Flavor: "medium", + FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", @@ -279,7 +279,7 @@ func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", - "flavor": "medium", + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "tags": ["test", "stage"] diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 1965bd9fa8..9684bc280f 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -64,7 +64,7 @@ func TestCreateLoadbalancer(t *testing.T) { VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", - Flavor: "medium", + FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", Tags: []string{"test", "stage"}, }).Extract() diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go index c6d53a7b05..cb4ba4c380 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go @@ -29,7 +29,7 @@ Example to Create a Load Balancer AdminStateUp: gophercloud.Enabled, VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", - Flavor: "medium", + FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", } diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index a0383bb42e..3aaaa0d0c1 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -30,7 +30,7 @@ type ListOpts struct { ID string `q:"id"` OperatingStatus string `q:"operating_status"` Name string `q:"name"` - Flavor string `q:"flavor"` + FlavorID string `q:"flavor_id"` Provider string `q:"provider"` Limit int `q:"limit"` Marker string `q:"marker"` @@ -100,7 +100,7 @@ type CreateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` // The UUID of a flavor. - Flavor string `json:"flavor,omitempty"` + FlavorID string `json:"flavor_id,omitempty"` // The name of the provider. Provider string `json:"provider,omitempty"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go index 7f423c933d..286f0b7c11 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go @@ -45,7 +45,7 @@ type LoadBalancer struct { Name string `json:"name"` // The UUID of a flavor if set. - Flavor string `json:"flavor"` + FlavorID string `json:"flavor_id"` // The name of the provider. Provider string `json:"provider"` diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go index 8132bda303..cebb4eb085 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures.go @@ -25,7 +25,7 @@ const LoadbalancersListBody = ` "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154", "vip_address": "10.30.176.47", "vip_port_id": "2a22e552-a347-44fd-b530-1f2b1b2a6735", - "flavor": "small", + "flavor_id": "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "ACTIVE", @@ -39,7 +39,7 @@ const LoadbalancersListBody = ` "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor": "medium", + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", @@ -60,7 +60,7 @@ const SingleLoadbalancerBody = ` "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor": "medium", + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", @@ -80,7 +80,7 @@ const PostUpdateLoadbalancerBody = ` "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor": "medium", + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", @@ -147,7 +147,7 @@ var ( VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154", VipAddress: "10.30.176.47", VipPortID: "2a22e552-a347-44fd-b530-1f2b1b2a6735", - Flavor: "small", + FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "ACTIVE", @@ -161,7 +161,7 @@ var ( VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", - Flavor: "medium", + FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", @@ -175,7 +175,7 @@ var ( VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", - Flavor: "medium", + FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", @@ -251,7 +251,7 @@ func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { "name": "db_lb", "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", "vip_address": "10.30.176.48", - "flavor": "medium", + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", "provider": "haproxy", "admin_state_up": true } diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go index 2d741cc3ca..ab0561c71b 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go @@ -63,7 +63,7 @@ func TestCreateLoadbalancer(t *testing.T) { AdminStateUp: gophercloud.Enabled, VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", VipAddress: "10.30.176.48", - Flavor: "medium", + FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", Provider: "haproxy", }).Extract() th.AssertNoErr(t, err) From 6f9cb195db5276cf596293ac30b08c93acd12851 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Oct 2019 19:55:29 -0600 Subject: [PATCH 0878/2296] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c987c0cfeb..1944ad482a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,14 @@ ## 0.6.0 (Unreleased) -* Added `networking/v2/extensions/quotas.Get` [GH-1742] -* Added `networking/v2/extensions/quotas.Update` [GH-1747] +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) + +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) ## 0.5.0 (October 13, 2019) From 8faecdad725d05f9c7375461cbf4f3dbbec6e527 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Thu, 17 Oct 2019 01:45:49 +0000 Subject: [PATCH 0879/2296] avoid reauthentication loops (#1746) * avoid reauthentication loops It does not make sense to reauthenticate multiple times for a single request. When the backing service returns 401 for a brand-new token, reauthenticating again will just send us into an infinite loop. Closes #1745. * rewrite ProviderClient.Reauthenticate() The old implementation has several places where the reauth mutex is unlocked before being locked again. This allows concurrent goroutines to glitch through reauthentication without the token having changed at all. Since mutexes and esp. RW mutexes are incredibly hard to get right, I propose this alternate implementation that coordinates concurrent Reauthenticate calls with channels. * add testcase for reauthentication loop avoidance --- provider_client.go | 88 +++++++++++++++++++++++---------- testing/provider_client_test.go | 50 +++++++++++++++++++ 2 files changed, 113 insertions(+), 25 deletions(-) diff --git a/provider_client.go b/provider_client.go index fce00462fd..b5bf2014da 100644 --- a/provider_client.go +++ b/provider_client.go @@ -94,9 +94,10 @@ type ProviderClient struct { // reauthlock represents a set of attributes used to help in the reauthentication process. type reauthlock struct { sync.RWMutex - reauthing bool - reauthingErr error - done *sync.Cond + //This channel is non-nil during reauthentication. It can be used to ask the + //goroutine doing Reauthenticate() for its result. Look at the implementation + //of Reauthenticate() for details. + ongoing chan<- (chan<- error) } // AuthenticatedHeaders returns a map of HTTP headers that are common for all @@ -106,11 +107,15 @@ func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { return } if client.reauthmut != nil { + //if a Reauthenticate is in progress, wait for it to complete client.reauthmut.Lock() - for client.reauthmut.reauthing { - client.reauthmut.done.Wait() - } + ongoing := client.reauthmut.ongoing client.reauthmut.Unlock() + if ongoing != nil { + responseChannel := make(chan error) + ongoing <- responseChannel + _ = <-responseChannel + } } t := client.Token() if t == "" { @@ -223,7 +228,7 @@ func (client *ProviderClient) SetThrowaway(v bool) { // this case, the reauthentication can be skipped if another thread has already // reauthenticated in the meantime. If no previous token is known, an empty // string should be passed instead to force unconditional reauthentication. -func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { +func (client *ProviderClient) Reauthenticate(previousToken string) error { if client.ReauthFunc == nil { return nil } @@ -232,33 +237,50 @@ func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { return client.ReauthFunc() } + messages := make(chan (chan<- error)) + + //check if a Reauthenticate is in progress, or start one if not client.reauthmut.Lock() - if client.reauthmut.reauthing { - for !client.reauthmut.reauthing { - client.reauthmut.done.Wait() - } - err = client.reauthmut.reauthingErr - client.reauthmut.Unlock() - return err + ongoing := client.reauthmut.ongoing + if ongoing == nil { + client.reauthmut.ongoing = messages } client.reauthmut.Unlock() - client.reauthmut.Lock() - client.reauthmut.reauthing = true - client.reauthmut.done = sync.NewCond(client.reauthmut) - client.reauthmut.reauthingErr = nil - client.reauthmut.Unlock() + //if Reauthenticate is running elsewhere, wait for its result + if ongoing != nil { + responseChannel := make(chan error) + ongoing <- responseChannel + return <-responseChannel + } + //perform the actual reauthentication + var err error if previousToken == "" || client.TokenID == previousToken { err = client.ReauthFunc() + } else { + err = nil } + //mark Reauthenticate as finished client.reauthmut.Lock() - client.reauthmut.reauthing = false - client.reauthmut.reauthingErr = err - client.reauthmut.done.Broadcast() + client.reauthmut.ongoing = nil client.reauthmut.Unlock() - return + + //report result to all other interested goroutines + // + //This happens in a separate goroutine because another goroutine might have + //acquired a copy of `client.reauthmut.ongoing` before we cleared it, but not + //have come around to sending its request. By answering in a goroutine, we + //can have that goroutine linger until all responseChannels have been sent. + //When GC has collected all sendings ends of the channel, our receiving end + //will be closed and the goroutine will end. + go func() { + for responseChannel := range messages { + responseChannel <- err + } + }() + return err } // RequestOpts customizes the behavior of the provider.Request() method. @@ -285,11 +307,26 @@ type RequestOpts struct { ErrorContext error } +//requestState contains temporary state for a single ProviderClient.Request() call. +type requestState struct { + // This flag indicates if we have reauthenticated during this request because of a 401 response. + // It ensures that we don't reauthenticate multiple times for a single request. If we + // reauthenticate, but keep getting 401 responses with the fresh token, reauthenticating some more + // will just get us into an infinite loop. + hasReauthenticated bool +} + 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{ + hasReauthenticated: false, + }) +} + +func (client *ProviderClient) doRequest(method, url string, options *RequestOpts, state *requestState) (*http.Response, error) { var body io.Reader var contentType *string @@ -392,7 +429,7 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) err = error400er.Error400(respErr) } case http.StatusUnauthorized: - if client.ReauthFunc != nil { + if client.ReauthFunc != nil && !state.hasReauthenticated { err = client.Reauthenticate(prereqtok) if err != nil { e := &ErrUnableToReauthenticate{} @@ -404,7 +441,8 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) seeker.Seek(0, 0) } } - resp, err = client.Request(method, url, options) + state.hasReauthenticated = true + resp, err = client.doRequest(method, url, options, state) if err != nil { switch err.(type) { case *ErrUnexpectedResponseCode: diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 81ff66f997..58cd4743fa 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -314,6 +314,56 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { th.AssertEquals(t, 1, info.failedAuths) } +func TestRequestReauthsAtMostOnce(t *testing.T) { + //There was an issue where Gophercloud would go into an infinite + //reauthentication loop with buggy services that send 401 even for fresh + //tokens. This test simulates such a service and checks that a call to + //ProviderClient.Request() will not try to reauthenticate more than once. + + reauthCounter := 0 + var reauthCounterMutex sync.Mutex + + p := new(gophercloud.ProviderClient) + p.UseTokenLock() + p.SetToken(client.TokenID) + p.ReauthFunc = func() error { + reauthCounterMutex.Lock() + reauthCounter++ + reauthCounterMutex.Unlock() + //The actual token value does not matter, the endpoint does not check it. + return nil + } + + th.SetupHTTP() + defer th.TeardownHTTP() + + requestCounter := 0 + var requestCounterMutex sync.Mutex + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + requestCounterMutex.Lock() + requestCounter++ + //avoid infinite loop + if requestCounter == 10 { + http.Error(w, "too many requests", http.StatusTooManyRequests) + return + } + requestCounterMutex.Unlock() + + //always reply 401, even immediately after reauthenticate + http.Error(w, "unauthorized", http.StatusUnauthorized) + }) + + _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + expectedErrorMessage := "Successfully re-authenticated, but got error executing request: Authentication failed" + th.AssertEquals(t, expectedErrorMessage, err.Error()) + + //^ The expected error message indicates that we reauthenticated once (that's + //the part before the colon), but when encountering another 401 response, we + //did not attempt reauthentication again and just passed that 401 response to + //the caller as ErrDefault401. +} + func TestRequestWithContext(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "OK") From 1df1eb43298e273781444524b008e06189491347 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 16 Oct 2019 19:48:39 -0600 Subject: [PATCH 0880/2296] Update provider_client.go --- provider_client.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/provider_client.go b/provider_client.go index b5bf2014da..885bf07a7b 100644 --- a/provider_client.go +++ b/provider_client.go @@ -94,9 +94,9 @@ type ProviderClient struct { // reauthlock represents a set of attributes used to help in the reauthentication process. type reauthlock struct { sync.RWMutex - //This channel is non-nil during reauthentication. It can be used to ask the - //goroutine doing Reauthenticate() for its result. Look at the implementation - //of Reauthenticate() for details. + // This channel is non-nil during reauthentication. It can be used to ask the + // goroutine doing Reauthenticate() for its result. Look at the implementation + // of Reauthenticate() for details. ongoing chan<- (chan<- error) } @@ -107,7 +107,7 @@ func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { return } if client.reauthmut != nil { - //if a Reauthenticate is in progress, wait for it to complete + // If a Reauthenticate is in progress, wait for it to complete. client.reauthmut.Lock() ongoing := client.reauthmut.ongoing client.reauthmut.Unlock() @@ -239,7 +239,7 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error { messages := make(chan (chan<- error)) - //check if a Reauthenticate is in progress, or start one if not + // Check if a Reauthenticate is in progress, or start one if not. client.reauthmut.Lock() ongoing := client.reauthmut.ongoing if ongoing == nil { @@ -247,14 +247,14 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error { } client.reauthmut.Unlock() - //if Reauthenticate is running elsewhere, wait for its result + // If Reauthenticate is running elsewhere, wait for its result. if ongoing != nil { responseChannel := make(chan error) ongoing <- responseChannel return <-responseChannel } - //perform the actual reauthentication + // Perform the actual reauthentication. var err error if previousToken == "" || client.TokenID == previousToken { err = client.ReauthFunc() @@ -262,19 +262,19 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error { err = nil } - //mark Reauthenticate as finished + // Mark Reauthenticate as finished. client.reauthmut.Lock() client.reauthmut.ongoing = nil client.reauthmut.Unlock() - //report result to all other interested goroutines + // Report result to all other interested goroutines. // - //This happens in a separate goroutine because another goroutine might have - //acquired a copy of `client.reauthmut.ongoing` before we cleared it, but not - //have come around to sending its request. By answering in a goroutine, we - //can have that goroutine linger until all responseChannels have been sent. - //When GC has collected all sendings ends of the channel, our receiving end - //will be closed and the goroutine will end. + // This happens in a separate goroutine because another goroutine might have + // acquired a copy of `client.reauthmut.ongoing` before we cleared it, but not + // have come around to sending its request. By answering in a goroutine, we + // can have that goroutine linger until all responseChannels have been sent. + // When GC has collected all sendings ends of the channel, our receiving end + // will be closed and the goroutine will end. go func() { for responseChannel := range messages { responseChannel <- err @@ -307,7 +307,7 @@ type RequestOpts struct { ErrorContext error } -//requestState contains temporary state for a single ProviderClient.Request() call. +// requestState contains temporary state for a single ProviderClient.Request() call. type requestState struct { // This flag indicates if we have reauthenticated during this request because of a 401 response. // It ensures that we don't reauthenticate multiple times for a single request. If we From df973126db225cc968103e160d9837cbe5672c1f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 16 Oct 2019 19:49:28 -0600 Subject: [PATCH 0881/2296] Update provider_client_test.go --- testing/provider_client_test.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 58cd4743fa..de175029c3 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -315,10 +315,10 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { } func TestRequestReauthsAtMostOnce(t *testing.T) { - //There was an issue where Gophercloud would go into an infinite - //reauthentication loop with buggy services that send 401 even for fresh - //tokens. This test simulates such a service and checks that a call to - //ProviderClient.Request() will not try to reauthenticate more than once. + // There was an issue where Gophercloud would go into an infinite + // reauthentication loop with buggy services that send 401 even for fresh + // tokens. This test simulates such a service and checks that a call to + // ProviderClient.Request() will not try to reauthenticate more than once. reauthCounter := 0 var reauthCounterMutex sync.Mutex @@ -354,14 +354,13 @@ func TestRequestReauthsAtMostOnce(t *testing.T) { http.Error(w, "unauthorized", http.StatusUnauthorized) }) + // The expected error message indicates that we reauthenticated once (that's + // the part before the colon), but when encountering another 401 response, we + // did not attempt reauthentication again and just passed that 401 response to + // the caller as ErrDefault401. _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) expectedErrorMessage := "Successfully re-authenticated, but got error executing request: Authentication failed" th.AssertEquals(t, expectedErrorMessage, err.Error()) - - //^ The expected error message indicates that we reauthenticated once (that's - //the part before the colon), but when encountering another 401 response, we - //did not attempt reauthentication again and just passed that 401 response to - //the caller as ErrDefault401. } func TestRequestWithContext(t *testing.T) { From 7b1656d32294b84b6b13ddb7e0017fb7a27be1f2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 16 Oct 2019 19:51:14 -0600 Subject: [PATCH 0882/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1944ad482a..eb7a42ad78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ ## 0.6.0 (Unreleased) +UPGRADE NODES + +* 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 From f2af8681eacc20362fea2c8ea42040d438843a3c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 16 Oct 2019 19:51:24 -0600 Subject: [PATCH 0883/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb7a42ad78..18e8a40ea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.6.0 (Unreleased) -UPGRADE NODES +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. From 47e5c17172b72ca3c78c9122418c5cf66bcba747 Mon Sep 17 00:00:00 2001 From: Petr Kotas Date: Thu, 17 Oct 2019 16:21:52 +0200 Subject: [PATCH 0884/2296] Bump go-yaml version to cover fixed ddos heuristic (#1751) go-yaml preceding 2.2.4 had vulnerability to ddos attack via billion laughs bomb. Such attack lead to program to be unresponsive. Signed-off-by: Petr Kotas --- go.mod | 4 ++-- go.sum | 16 ++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 55c66f8542..1eebf17ed7 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,6 @@ module github.com/gophercloud/gophercloud require ( golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 - golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3 // indirect - gopkg.in/yaml.v2 v2.2.2 + gopkg.in/yaml.v2 v2.2.4 ) + diff --git a/go.sum b/go.sum index b3889ce2c1..27dc9b3055 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,8 @@ -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3 h1:2AmBLzhAfXj+2HCW09VCkJtHIYgHTIPcTeYqgP7Bwt0= -golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From bb2463ad4f9db951cbbcef3e03e5cb8b2df933d3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 17 Oct 2019 08:24:04 -0600 Subject: [PATCH 0885/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18e8a40ea3..42c1b619a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ 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. +DEPENDENCIES + + + IMPROVEMENTS * Added `networking/v2/extensions/quotas.Get` [GH-1742](https://github.com/gophercloud/gophercloud/pull/1742) @@ -14,6 +18,7 @@ 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) From a8bdb516e71d7c586306501b30b80f06f53c013e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 17 Oct 2019 08:24:39 -0600 Subject: [PATCH 0886/2296] Update CHANGELOG.md --- CHANGELOG.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42c1b619a1..e1044e68a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,11 @@ -## 0.6.0 (Unreleased) +## 0.7.0 (Unreleased) + +## 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. -DEPENDENCIES - - - IMPROVEMENTS * Added `networking/v2/extensions/quotas.Get` [GH-1742](https://github.com/gophercloud/gophercloud/pull/1742) From 6d1c56b9dce2797d7993415be11dc9ba62a1ad09 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 17 Oct 2019 17:36:50 -0600 Subject: [PATCH 0887/2296] Acc Tests: Fixing gophercloud-acceptance-test-ironic (#1754) --- .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml index 64b098c282..03210cca84 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml @@ -9,7 +9,7 @@ - role: install-devstack environment: 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,tempest' - PROJECTS: 'openstack/ironic' + PROJECTS: 'openstack/ironic-python-agent-builder openstack/ironic' tasks: - name: Run ironic acceptance tests with gophercloud shell: From 2f683dc9dc65096a9b4f747407ae92c34db65565 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 19 Oct 2019 04:05:05 +0200 Subject: [PATCH 0888/2296] Auth: Allow TokenID passthrough when the scope is not set (#1752) --- openstack/client.go | 53 +++++++++++++++++++++---- openstack/identity/v3/tokens/results.go | 7 ++++ 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index 50f239711e..e77584157f 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -187,16 +187,53 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au v3Client.Endpoint = endpoint } - result := tokens3.Create(v3Client, opts) - - err = client.SetTokenAndAuthResult(result) - if err != nil { - return err + var catalog *tokens3.ServiceCatalog + + var tokenID string + // passthroughToken allows to passthrough the token without a scope + var passthroughToken bool + switch v := opts.(type) { + case *gophercloud.AuthOptions: + tokenID = v.TokenID + passthroughToken = (v.Scope == nil || *v.Scope == gophercloud.AuthScope{}) + case *tokens3.AuthOptions: + tokenID = v.TokenID + passthroughToken = (v.Scope == tokens3.Scope{}) } - catalog, err := result.ExtractServiceCatalog() - if err != nil { - return err + if tokenID != "" && passthroughToken { + // passing through the token ID without requesting a new scope + if opts.CanReauth() { + return fmt.Errorf("cannot use AllowReauth, when the token ID is defined and auth scope is not set") + } + + v3Client.SetToken(tokenID) + result := tokens3.Get(v3Client, tokenID) + if result.Err != nil { + return result.Err + } + + err = client.SetTokenAndAuthResult(result) + if err != nil { + return err + } + + catalog, err = result.ExtractServiceCatalog() + if err != nil { + return err + } + } else { + result := tokens3.Create(v3Client, opts) + + err = client.SetTokenAndAuthResult(result) + if err != nil { + return err + } + + catalog, err = result.ExtractServiceCatalog() + if err != nil { + return err + } } if opts.CanReauth() { diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index 8af4d634cf..f1e17e9f75 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -109,6 +109,13 @@ func (r CreateResult) ExtractTokenID() (string, error) { return r.Header.Get("X-Subject-Token"), r.Err } +// ExtractTokenID implements the gophercloud.AuthResult interface. The returned +// string is the same as the ID field of the Token struct returned from +// ExtractToken(). +func (r GetResult) ExtractTokenID() (string, error) { + return r.Header.Get("X-Subject-Token"), r.Err +} + // ExtractServiceCatalog returns the ServiceCatalog that was generated along // with the user's Token. func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) { From 0907b320e0ac3a4de50c0fbda5c67064086eecee Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 18 Oct 2019 20:05:56 -0600 Subject: [PATCH 0889/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1044e68a4..145d057175 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.7.0 (Unreleased) +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) + ## 0.6.0 (October 17, 2019) UPGRADE NOTES From ee22944fce6c0a4944d085ab289e79435afe4676 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 24 Oct 2019 08:07:15 -0600 Subject: [PATCH 0890/2296] Compute v2: Move ServerTagsExt to servers.TagsExt (#1760) This commit moves the tags extension's ServerTagsExt to the servers package. While this is slighly nuanced, the tags extension can be thought of to work directly on a server's tags and the ability to extract a server's tags can be part of the servers package since the original ExtractTags microversion support already exists. --- .../openstack/compute/v2/servers_test.go | 2 +- openstack/compute/v2/extensions/tags/doc.go | 25 ------------------- .../compute/v2/extensions/tags/results.go | 6 ----- openstack/compute/v2/servers/doc.go | 25 +++++++++++++++++++ openstack/compute/v2/servers/microversions.go | 18 +++++++++++++ 5 files changed, 44 insertions(+), 32 deletions(-) diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 221983827a..fa2d759a4a 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -518,7 +518,7 @@ func TestServersTags(t *testing.T) { // Check server tags in body. type serverWithTagsExt struct { servers.Server - tags.ServerTagsExt + servers.TagsExt } serverWithTags := serverWithTagsExt{} diff --git a/openstack/compute/v2/extensions/tags/doc.go b/openstack/compute/v2/extensions/tags/doc.go index a521ba2c28..f3ef25a15e 100644 --- a/openstack/compute/v2/extensions/tags/doc.go +++ b/openstack/compute/v2/extensions/tags/doc.go @@ -66,30 +66,5 @@ Example to Delete all Tags on a server if err != nil { log.Fatal(err) } - -Example of Extend server result with Tags: - - client.Microversion = "2.26" - - type ServerWithTags struct { - servers.Server - tags.ServerTagsExt - } - - var allServers []ServerWithTags - - allPages, err := servers.List(client, nil).AllPages() - if err != nil { - log.Fatal(err) - } - - err = servers.ExtractServersInto(allPages, &allServers) - if err != nil { - log.Fatal(err) - } - - for _, server := range allServers { - fmt.Println(server.Tags) - } */ package tags diff --git a/openstack/compute/v2/extensions/tags/results.go b/openstack/compute/v2/extensions/tags/results.go index 294744e7fe..c73b7a6cb0 100644 --- a/openstack/compute/v2/extensions/tags/results.go +++ b/openstack/compute/v2/extensions/tags/results.go @@ -2,12 +2,6 @@ package tags import "github.com/gophercloud/gophercloud" -// ServerTagsExt is an extension to the base Server object. -type ServerTagsExt struct { - // Tags contains a list of server tags. - Tags []string `json:"tags"` -} - type commonResult struct { gophercloud.Result } diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index 3b0ab78362..3926a382c3 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -111,5 +111,30 @@ Example to Snapshot a Server if err != nil { panic(err) } + +Example of Extend server result with Tags: + + client.Microversion = "2.26" + + type ServerWithTags struct { + servers.Server + servers.TagsExt + } + + var allServers []ServerWithTags + + allPages, err := servers.List(client, nil).AllPages() + if err != nil { + log.Fatal(err) + } + + err = servers.ExtractServersInto(allPages, &allServers) + if err != nil { + log.Fatal(err) + } + + for _, server := range allServers { + fmt.Println(server.Tags) + } */ package servers diff --git a/openstack/compute/v2/servers/microversions.go b/openstack/compute/v2/servers/microversions.go index 84ec9f31d3..b5d4be7506 100644 --- a/openstack/compute/v2/servers/microversions.go +++ b/openstack/compute/v2/servers/microversions.go @@ -1,7 +1,25 @@ package servers +// TagsExt is an extension to the base Server struct. +// Use this in combination with the Server struct to create +// a composed struct in order to extract a slice of servers +// with the Tag field. +// +// This requires the client to be set to microversion 2.26 or later. +// +// To interact with a server's tags directly, see the +// openstack/compute/v2/extensions/tags package. +type TagsExt struct { + // Tags contains a list of server tags. + Tags []string `json:"tags"` +} + // ExtractTags will extract the tags of a server. +// // This requires the client to be set to microversion 2.26 or later. +// +// To interact with a server's tags directly, see the +// openstack/compute/v2/extensions/tags package. func (r serverResult) ExtractTags() ([]string, error) { var s struct { Tags []string `json:"tags"` From 96d83b95d97e4877b0a5a9b2b61980e4efcf4b44 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 24 Oct 2019 08:07:57 -0600 Subject: [PATCH 0891/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 145d057175..224e6509b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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) ## 0.6.0 (October 17, 2019) From ac31aca5bde5f04c0853905762fdfd54720588bf Mon Sep 17 00:00:00 2001 From: Mike Fedosin Date: Fri, 25 Oct 2019 20:49:14 +0200 Subject: [PATCH 0892/2296] Implement server tags filtering support (#1759) Server tags filtering support was added in Nova API since 2.26, so we can implement it here as well. This patch adds new filters `tags`, `tags-any`, `not-tags`, `not-tags-any` to server list options. --- openstack/compute/v2/servers/requests.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index ee8e93b1dc..75103b2eb5 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -55,6 +55,22 @@ type ListOpts struct { // TenantID lists servers for a particular tenant. // Setting "AllTenants = true" is required. TenantID string `q:"tenant_id"` + + // This requires the client to be set to microversion 2.26 or later. + // Tags filters on specific server tags. All tags must be present for the server. + Tags string `q:"tags"` + + // This requires the client to be set to microversion 2.26 or later. + // TagsAny filters on specific server tags. At least one of the tags must be present for the server. + TagsAny string `q:"tags-any"` + + // This requires the client to be set to microversion 2.26 or later. + // NotTags filters on specific server tags. All tags must be absent for the server. + NotTags string `q:"not-tags"` + + // This requires the client to be set to microversion 2.26 or later. + // NotTagsAny filters on specific server tags. At least one of the tags must be absent for the server. + NotTagsAny string `q:"not-tags-any"` } // ToServerListQuery formats a ListOpts into a query string. From 6ad562af8c1fd8a69397a13caf58fa184d115b56 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 25 Oct 2019 12:50:32 -0600 Subject: [PATCH 0893/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 224e6509b8..e2e4c82da8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ 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) ## 0.6.0 (October 17, 2019) From d326b2933ecae50049b41b08b66451c07a190d81 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 2 Nov 2019 00:04:22 +0100 Subject: [PATCH 0894/2296] Identity V3: Add access rules support for application credentials (#1758) * Identity V3: Add access rules support for application credentials * Omit unused "Links" structure elements * Add AccessRule acceptance tests --- .../v3/applicationcredentials_test.go | 132 ++++++++++++++++++ go.mod | 1 - .../v3/applicationcredentials/requests.go | 22 +++ .../v3/applicationcredentials/results.go | 66 +++++++++ .../testing/fixtures.go | 118 ++++++++++++++++ .../testing/requests_test.go | 46 ++++++ .../v3/applicationcredentials/urls.go | 12 ++ 7 files changed, 396 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/identity/v3/applicationcredentials_test.go b/acceptance/openstack/identity/v3/applicationcredentials_test.go index 94dde2f0e2..3046f7dd7f 100644 --- a/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -170,3 +170,135 @@ func TestApplicationCredentialsCRD(t *testing.T) { th.AssertEquals(t, checkACroles[role.ID], role.ID) } } + +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) + + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + authOptions := tokens.AuthOptions{ + Username: ao.Username, + Password: ao.Password, + DomainName: ao.DomainName, + DomainID: ao.DomainID, + // We need a scope to get the token roles list + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, + } + + token, err := tokens.Create(client, &authOptions).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) + + user, err := tokens.Get(client, token.ID).ExtractUser() + th.AssertNoErr(t, err) + tools.PrintResource(t, user) + + // prepare create parameters + apAccessRules := []applicationcredentials.AccessRule{ + { + Path: "/v2.0/metrics", + Service: "monitoring", + Method: "GET", + }, + { + Path: "/v2.0/metrics", + Service: "monitoring", + Method: "PUT", + }, + } + + tools.PrintResource(t, apAccessRules) + + // restricted, limited TTL, with limited roles, autogenerated secret + createOpts := applicationcredentials.CreateOpts{ + Name: "test-ac", + Description: "test application credential", + AccessRules: apAccessRules, + ExpiresAt: "2119-01-01T12:12:12Z", + } + + applicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() + th.AssertNoErr(t, err) + defer applicationcredentials.Delete(client, user.ID, applicationCredential.ID) + tools.PrintResource(t, applicationCredential) + + if applicationCredential.Secret == "" { + t.Fatalf("Application credential secret was not generated") + } + + th.AssertEquals(t, applicationCredential.ExpiresAt.UTC().Format(time.RFC3339), createOpts.ExpiresAt) + th.AssertEquals(t, applicationCredential.Name, createOpts.Name) + th.AssertEquals(t, applicationCredential.Description, createOpts.Description) + th.AssertEquals(t, applicationCredential.Unrestricted, false) + + for i, rule := range applicationCredential.AccessRules { + th.AssertEquals(t, rule.Path, apAccessRules[i].Path) + th.AssertEquals(t, rule.Service, apAccessRules[i].Service) + th.AssertEquals(t, rule.Method, apAccessRules[i].Method) + } + + // Get an application credential + getApplicationCredential, err := applicationcredentials.Get(client, user.ID, applicationCredential.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getApplicationCredential) + + if getApplicationCredential.Secret != "" { + t.Fatalf("Application credential secret should not be returned by a GET request") + } + + th.AssertEquals(t, getApplicationCredential.ExpiresAt.UTC().Format(time.RFC3339), createOpts.ExpiresAt) + th.AssertEquals(t, getApplicationCredential.Name, createOpts.Name) + th.AssertEquals(t, getApplicationCredential.Description, createOpts.Description) + th.AssertEquals(t, getApplicationCredential.Unrestricted, false) + + for i, rule := range applicationCredential.AccessRules { + th.AssertEquals(t, rule.Path, apAccessRules[i].Path) + th.AssertEquals(t, rule.Service, apAccessRules[i].Service) + th.AssertEquals(t, rule.Method, apAccessRules[i].Method) + } + + // test list + allPages, err := applicationcredentials.ListAccessRules(client, user.ID).AllPages() + th.AssertNoErr(t, err) + actual, err := applicationcredentials.ExtractAccessRules(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, getApplicationCredential.AccessRules, actual) + + // test individual get + for i, rule := range actual { + getRule, err := applicationcredentials.GetAccessRule(client, user.ID, rule.ID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, actual[i], *getRule) + } + + res := applicationcredentials.Delete(client, user.ID, applicationCredential.ID) + th.AssertNoErr(t, res.Err) + + // test delete + for _, rule := range actual { + res := applicationcredentials.DeleteAccessRule(client, user.ID, rule.ID) + th.AssertNoErr(t, res.Err) + } + + allPages, err = applicationcredentials.ListAccessRules(client, user.ID).AllPages() + th.AssertNoErr(t, err) + actual, err = applicationcredentials.ExtractAccessRules(allPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, len(actual), 0) +} diff --git a/go.mod b/go.mod index 1eebf17ed7..1d58ea20f3 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,3 @@ require ( golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 gopkg.in/yaml.v2 v2.2.4 ) - diff --git a/openstack/identity/v3/applicationcredentials/requests.go b/openstack/identity/v3/applicationcredentials/requests.go index 61111d2ef2..218a925cf0 100644 --- a/openstack/identity/v3/applicationcredentials/requests.go +++ b/openstack/identity/v3/applicationcredentials/requests.go @@ -66,6 +66,8 @@ type CreateOpts struct { // A list of one or more roles that this application credential has associated with its project. // A token using this application credential will have these same roles. Roles []Role `json:"roles,omitempty"` + // A list of access rules objects. + AccessRules []AccessRule `json:"access_rules,omitempty"` // The expiration time of the application credential, if one was specified. ExpiresAt string `json:"expires_at,omitempty"` } @@ -93,3 +95,23 @@ func Delete(client *gophercloud.ServiceClient, userID string, id string) (r Dele _, r.Err = client.Delete(deleteURL(client, userID, id), nil) return } + +// ListAccessRules enumerates the AccessRules to which the current user has access. +func ListAccessRules(client *gophercloud.ServiceClient, userID string) pagination.Pager { + url := listAccessRulesURL(client, userID) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessRulePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetAccessRule retrieves details on a single access rule by ID. +func GetAccessRule(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessRuleResult) { + _, r.Err = client.Get(getAccessRuleURL(client, userID, id), &r.Body, nil) + return +} + +// DeleteAccessRule deletes an access rule. +func DeleteAccessRule(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteAccessRuleURL(client, userID, id), nil) + return +} diff --git a/openstack/identity/v3/applicationcredentials/results.go b/openstack/identity/v3/applicationcredentials/results.go index f687997636..c8d3a42ec8 100644 --- a/openstack/identity/v3/applicationcredentials/results.go +++ b/openstack/identity/v3/applicationcredentials/results.go @@ -17,6 +17,20 @@ type Role struct { Name string `json:"name,omitempty"` } +// ApplicationCredential represents the access rule object +type AccessRule struct { + // The ID of the access rule + ID string `json:"id,omitempty"` + // The API path that the application credential is permitted to access + Path string `json:"path,omitempty"` + // The request method that the application credential is permitted to use for a + // given API endpoint + Method string `json:"method,omitempty"` + // The service type identifier for the service that the application credential + // is permitted to access + Service string `json:"service,omitempty"` +} + // ApplicationCredential represents the application credential object type ApplicationCredential struct { // The ID of the application credential. @@ -39,6 +53,8 @@ type ApplicationCredential struct { Roles []Role `json:"roles"` // The expiration time of the application credential, if one was specified. ExpiresAt time.Time `json:"-"` + // A list of access rules objects. + AccessRules []AccessRule `json:"access_rules,omitempty"` // Links contains referencing links to the application credential. Links map[string]interface{} `json:"links"` } @@ -125,3 +141,53 @@ func (r applicationCredentialResult) Extract() (*ApplicationCredential, error) { err := r.ExtractInto(&s) return s.ApplicationCredential, err } + +// GetAccessRuleResult is the response from a Get operation. Call its Extract method +// to interpret it as an AccessRule. +type GetAccessRuleResult struct { + gophercloud.Result +} + +// an AccessRulePage is a single page of an AccessRule results. +type AccessRulePage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an AccessRulePage contains any results. +func (r AccessRulePage) IsEmpty() (bool, error) { + accessRules, err := ExtractAccessRules(r) + return len(accessRules) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r AccessRulePage) 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 +} + +// ExtractAccessRules returns a slice of AccessRules contained in a single page of results. +func ExtractAccessRules(r pagination.Page) ([]AccessRule, error) { + var s struct { + AccessRules []AccessRule `json:"access_rules"` + } + err := (r.(AccessRulePage)).ExtractInto(&s) + return s.AccessRules, err +} + +// Extract interprets any access_rule results as an AccessRule. +func (r GetAccessRuleResult) Extract() (*AccessRule, error) { + var s struct { + AccessRule *AccessRule `json:"access_rule"` + } + err := r.ExtractInto(&s) + return s.AccessRule, err +} diff --git a/openstack/identity/v3/applicationcredentials/testing/fixtures.go b/openstack/identity/v3/applicationcredentials/testing/fixtures.go index ba6b54b379..86dbf4c335 100644 --- a/openstack/identity/v3/applicationcredentials/testing/fixtures.go +++ b/openstack/identity/v3/applicationcredentials/testing/fixtures.go @@ -14,6 +14,7 @@ import ( const userID = "2844b2a08be147a08ef58317d6471f1f" const applicationCredentialID = "f741662395b249c9b8acdebf1722c5ae" +const accessRuleID = "07d719df00f349ef8de77d542edf010c" // ListOutput provides a single page of ApplicationCredential results. const ListOutput = ` @@ -84,6 +85,14 @@ const GetOutput = ` "name": "compute_viewer" } ], + "access_rules": [ + { + "path": "/v2.0/metrics", + "id": "07d719df00f349ef8de77d542edf010c", + "service": "monitoring", + "method": "GET" + } + ], "expires_at": null, "unrestricted": false, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", @@ -104,6 +113,13 @@ const CreateRequest = ` "id": "31f87923ae4a4d119aa0b85dcdbeed13" } ], + "access_rules": [ + { + "path": "/v2.0/metrics", + "method": "GET", + "service": "monitoring" + } + ], "name": "test" } } @@ -123,6 +139,14 @@ const CreateResponse = ` "name": "compute_viewer" } ], + "access_rules": [ + { + "path": "/v2.0/metrics", + "id": "07d719df00f349ef8de77d542edf010c", + "service": "monitoring", + "method": "GET" + } + ], "expires_at": null, "secret": "mysecret", "unrestricted": false, @@ -219,6 +243,42 @@ const CreateUnrestrictedResponse = ` } ` +// ListAccessRulesOutput provides a single page of AccessRules results. +const ListAccessRulesOutput = ` +{ + "links": { + "self": "https://example.com/identity/v3/users/2844b2a08be147a08ef58317d6471f1f/access_rules", + "previous": null, + "next": null + }, + "access_rules": [ + { + "path": "/v2.0/metrics", + "links": { + "self": "https://example.com/identity/v3/access_rules/07d719df00f349ef8de77d542edf010c" + }, + "id": "07d719df00f349ef8de77d542edf010c", + "service": "monitoring", + "method": "GET" + } + ] +}` + +// GetAccessRuleOutput provides a Get result. +const GetAccessRuleOutput = ` +{ + "access_rule": { + "path": "/v2.0/metrics", + "links": { + "self": "https://example.com/identity/v3/access_rules/07d719df00f349ef8de77d542edf010c" + }, + "id": "07d719df00f349ef8de77d542edf010c", + "service": "monitoring", + "method": "GET" + } +} +` + var nilTime time.Time var ApplicationCredential = applicationcredentials.ApplicationCredential{ ID: "f741662395b249c9b8acdebf1722c5ae", @@ -233,6 +293,14 @@ var ApplicationCredential = applicationcredentials.ApplicationCredential{ Name: "compute_viewer", }, }, + AccessRules: []applicationcredentials.AccessRule{ + { + ID: "07d719df00f349ef8de77d542edf010c", + Path: "/v2.0/metrics", + Method: "GET", + Service: "monitoring", + }, + }, ExpiresAt: nilTime, Links: map[string]interface{}{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae", @@ -324,6 +392,17 @@ var SecondApplicationCredential = applicationcredentials.ApplicationCredential{ }, } +var AccessRule = applicationcredentials.AccessRule{ + Path: "/v2.0/metrics", + ID: "07d719df00f349ef8de77d542edf010c", + Service: "monitoring", + Method: "GET", +} + +var ExpectedAccessRulesSlice = []applicationcredentials.AccessRule{ + AccessRule, +} + // ExpectedApplicationCredentialsSlice is the slice of application credentials expected to be returned from ListOutput. var ExpectedApplicationCredentialsSlice = []applicationcredentials.ApplicationCredential{FirstApplicationCredential, SecondApplicationCredential} @@ -402,3 +481,42 @@ func HandleDeleteApplicationCredentialSuccessfully(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// HandleListAccessRulesSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that responds with a list of two applicationcredentials. +func HandleListAccessRulesSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/access_rules", 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, ListAccessRulesOutput) + }) +} + +// HandleGetAccessRuleSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that responds with a single application credential. +func HandleGetAccessRuleSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/access_rules/07d719df00f349ef8de77d542edf010c", 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, GetAccessRuleOutput) + }) +} + +// HandleDeleteAccessRuleSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that tests application credential deletion. +func HandleDeleteAccessRuleSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/access_rules/07d719df00f349ef8de77d542edf010c", 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/applicationcredentials/testing/requests_test.go b/openstack/identity/v3/applicationcredentials/testing/requests_test.go index 6a410e3098..00dc548c25 100644 --- a/openstack/identity/v3/applicationcredentials/testing/requests_test.go +++ b/openstack/identity/v3/applicationcredentials/testing/requests_test.go @@ -64,6 +64,13 @@ func TestCreateApplicationCredential(t *testing.T) { Roles: []applicationcredentials.Role{ applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, }, + AccessRules: []applicationcredentials.AccessRule{ + { + Path: "/v2.0/metrics", + Method: "GET", + Service: "monitoring", + }, + }, } ApplicationCredentialResponse := ApplicationCredential @@ -122,3 +129,42 @@ func TestDeleteApplicationCredential(t *testing.T) { res := applicationcredentials.Delete(client.ServiceClient(), userID, applicationCredentialID) th.AssertNoErr(t, res.Err) } + +func TestListAccessRules(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAccessRulesSuccessfully(t) + + count := 0 + err := applicationcredentials.ListAccessRules(client.ServiceClient(), userID).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := applicationcredentials.ExtractAccessRules(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedAccessRulesSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestGetAccessRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetAccessRuleSuccessfully(t) + + actual, err := applicationcredentials.GetAccessRule(client.ServiceClient(), userID, accessRuleID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, AccessRule, *actual) +} + +func TestDeleteAccessRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteAccessRuleSuccessfully(t) + + res := applicationcredentials.DeleteAccessRule(client.ServiceClient(), userID, accessRuleID) + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/applicationcredentials/urls.go b/openstack/identity/v3/applicationcredentials/urls.go index 1476206938..f809385f25 100644 --- a/openstack/identity/v3/applicationcredentials/urls.go +++ b/openstack/identity/v3/applicationcredentials/urls.go @@ -17,3 +17,15 @@ func createURL(client *gophercloud.ServiceClient, userID string) string { func deleteURL(client *gophercloud.ServiceClient, userID string, id string) string { return client.ServiceURL("users", userID, "application_credentials", id) } + +func listAccessRulesURL(client *gophercloud.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "access_rules") +} + +func getAccessRuleURL(client *gophercloud.ServiceClient, userID string, id string) string { + return client.ServiceURL("users", userID, "access_rules", id) +} + +func deleteAccessRuleURL(client *gophercloud.ServiceClient, userID string, id string) string { + return client.ServiceURL("users", userID, "access_rules", id) +} From 1630f49d9a7d989ec87cd8dee925a37877643ad0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 1 Nov 2019 17:05:50 -0600 Subject: [PATCH 0895/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e4c82da8..e4e91ce97f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ 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] ## 0.6.0 (October 17, 2019) From d7ebd7045c192596b4c04c61393fd5ce1b49a528 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 1 Nov 2019 17:06:05 -0600 Subject: [PATCH 0896/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e91ce97f..52a65220ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ 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] +* Added `AccessRule` to `identity/v3/applicationcredentials` [GH-1758](https://github.com/gophercloud/gophercloud/pull/1758) ## 0.6.0 (October 17, 2019) From dd0bb13095e1f2394f7061c059b2da8ca6a3b98b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 11 Nov 2019 13:48:08 -0700 Subject: [PATCH 0897/2296] Don't Error on Multiple Endpoints (#1766) This commit changes the behvior when multiple endpoints are discovered. Instead of returning an error, the first endpoint will be used, discarding the other endpoints. This change helps keep behavior in line with the Python SDK. --- openstack/endpoint_location.go | 16 ++++++++------ openstack/errors.go | 24 --------------------- openstack/testing/endpoint_location_test.go | 17 +++++++-------- 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/openstack/endpoint_location.go b/openstack/endpoint_location.go index 12c8aebcf7..509700790e 100644 --- a/openstack/endpoint_location.go +++ b/openstack/endpoint_location.go @@ -29,11 +29,12 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt } } - // Report an error if the options were ambiguous. + // If multiple endpoints were found, use the first result + // and disregard the other endpoints. + // + // This behavior matches the Python library. See GH-1764. if len(endpoints) > 1 { - err := &ErrMultipleMatchingEndpointsV2{} - err.Endpoints = endpoints - return "", err + endpoints = endpoints[0:1] } // Extract the appropriate URL from the matching Endpoint. @@ -91,9 +92,12 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpt } } - // Report an error if the options were ambiguous. + // If multiple endpoints were found, use the first result + // and disregard the other endpoints. + // + // This behavior matches the Python library. See GH-1764. if len(endpoints) > 1 { - return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints} + endpoints = endpoints[0:1] } // Extract the URL from the matching Endpoint. diff --git a/openstack/errors.go b/openstack/errors.go index df410b1c61..cba6ae5f00 100644 --- a/openstack/errors.go +++ b/openstack/errors.go @@ -4,8 +4,6 @@ import ( "fmt" "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" ) // ErrEndpointNotFound is the error when no suitable endpoint can be found @@ -24,28 +22,6 @@ func (e ErrInvalidAvailabilityProvided) Error() string { return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value) } -// ErrMultipleMatchingEndpointsV2 is the error when more than one endpoint -// for the given options is found in the v2 catalog -type ErrMultipleMatchingEndpointsV2 struct { - gophercloud.BaseError - Endpoints []tokens2.Endpoint -} - -func (e ErrMultipleMatchingEndpointsV2) Error() string { - return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) -} - -// ErrMultipleMatchingEndpointsV3 is the error when more than one endpoint -// for the given options is found in the v3 catalog -type ErrMultipleMatchingEndpointsV3 struct { - gophercloud.BaseError - Endpoints []tokens3.Endpoint -} - -func (e ErrMultipleMatchingEndpointsV3) Error() string { - return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) -} - // ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not // found type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput } diff --git a/openstack/testing/endpoint_location_test.go b/openstack/testing/endpoint_location_test.go index 5ac773eee6..8ad36b4378 100644 --- a/openstack/testing/endpoint_location_test.go +++ b/openstack/testing/endpoint_location_test.go @@ -1,7 +1,6 @@ package testing import ( - "strings" "testing" "github.com/gophercloud/gophercloud" @@ -90,14 +89,14 @@ func TestV2EndpointNone(t *testing.T) { } func TestV2EndpointMultiple(t *testing.T) { - _, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{ + actual, err := openstack.V2EndpointURL(&catalog2, gophercloud.EndpointOpts{ Type: "same", Region: "same", Availability: gophercloud.AvailabilityPublic, }) - if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") { - t.Errorf("Received unexpected error: %v", err) - } + + th.AssertNoErr(t, err) + th.AssertEquals(t, "https://public.correct.com/", actual) } func TestV2EndpointBadAvailability(t *testing.T) { @@ -234,14 +233,14 @@ func TestV3EndpointNone(t *testing.T) { } func TestV3EndpointMultiple(t *testing.T) { - _, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ + actual, err := openstack.V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ Type: "same", Region: "same", Availability: gophercloud.AvailabilityPublic, }) - if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") { - t.Errorf("Received unexpected error: %v", err) - } + + th.AssertNoErr(t, err) + th.AssertEquals(t, "https://public.correct.com/", actual) } func TestV3EndpointBadAvailability(t *testing.T) { From b311dd7d8f73be7bfe16094403cd3f440646f13f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 11 Nov 2019 13:48:55 -0700 Subject: [PATCH 0898/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a65220ce..0dd665327b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ IMPROVEMENTS * 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). ## 0.6.0 (October 17, 2019) From d8a57ca40e7fcb673173a6af373043f117dc9ce2 Mon Sep 17 00:00:00 2001 From: Colin Fowler Date: Tue, 19 Nov 2019 15:54:43 +0000 Subject: [PATCH 0899/2296] [wip] FWaaS v2.0 Rule Create (#1768) * FWaaSv2 Rule Create * minor formatting changes in imports to pass ./script/format * Add specific type for Action as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346643142 * rename PolicyID to FirewallPolicyID as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346644820 * removed Position as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346646305 * Add ProjectID to Rule * Initial acceptance test for creating rule * convert Protocol to string in acceptance tests to prevent AssertEquals failures --- .../v2/extensions/fwaas_v2/fwaas_v2.go | 53 +++++++ .../v2/extensions/fwaas_v2/rule_test.go | 20 +++ .../networking/v2/extensions/fwaas_v2/doc.go | 3 + .../v2/extensions/fwaas_v2/rules/requests.go | 85 +++++++++++ .../v2/extensions/fwaas_v2/rules/results.go | 42 ++++++ .../extensions/fwaas_v2/rules/testing/doc.go | 2 + .../fwaas_v2/rules/testing/requests_test.go | 135 ++++++++++++++++++ .../v2/extensions/fwaas_v2/rules/urls.go | 12 ++ 8 files changed, 352 insertions(+) create mode 100644 acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go create mode 100644 acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/doc.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/rules/requests.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/rules/results.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/rules/testing/doc.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/rules/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/rules/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go new file mode 100644 index 0000000000..e75b35cdf2 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -0,0 +1,53 @@ +package fwaas_v2 + +import ( + "fmt" + "strconv" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" + th "github.com/gophercloud/gophercloud/testhelper" +) + +// CreateRule will create a Firewall Rule with a random source address and +//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) + sourceAddress := fmt.Sprintf("192.168.1.%d", tools.RandomInt(1, 100)) + sourcePort := strconv.Itoa(tools.RandomInt(1, 100)) + destinationAddress := fmt.Sprintf("192.168.2.%d", tools.RandomInt(1, 100)) + destinationPort := strconv.Itoa(tools.RandomInt(1, 100)) + + t.Logf("Attempting to create rule %s with source %s:%s and destination %s:%s", + ruleName, sourceAddress, sourcePort, destinationAddress, destinationPort) + + createOpts := rules.CreateOpts{ + Name: ruleName, + Protocol: rules.ProtocolTCP, + Action: rules.ActionAllow, + SourceIPAddress: sourceAddress, + SourcePort: sourcePort, + DestinationIPAddress: destinationAddress, + DestinationPort: destinationPort, + } + + rule, err := rules.Create(client, createOpts).Extract() + if err != nil { + return rule, err + } + + t.Logf("Rule %s successfully created", ruleName) + + th.AssertEquals(t, rule.Name, ruleName) + 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.DestinationIPAddress, destinationAddress) + th.AssertEquals(t, rule.DestinationPort, destinationPort) + + return rule, nil +} diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go new file mode 100644 index 0000000000..e2200e5a52 --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -0,0 +1,20 @@ +// +build acceptance networking fwaas_v2 + +package fwaas_v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestRuleC(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + rule, err := CreateRule(t, client) + th.AssertNoErr(t, err) + tools.PrintResource(t, rule) +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/doc.go b/openstack/networking/v2/extensions/fwaas_v2/doc.go new file mode 100644 index 0000000000..52ae478c9b --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/doc.go @@ -0,0 +1,3 @@ +// Package fwaas provides information and interaction with the Firewall +// as a Service extension for the OpenStack Networking service. +package fwaas_v2 diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go new file mode 100644 index 0000000000..17d2f6a668 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -0,0 +1,85 @@ +package rules + +import ( + "github.com/gophercloud/gophercloud" +) + +type ( + // Protocol represents a valid rule protocol + Protocol string +) + +const ( + // ProtocolAny is to allow any protocol + ProtocolAny Protocol = "any" + + // ProtocolICMP is to allow the ICMP protocol + ProtocolICMP Protocol = "icmp" + + // ProtocolTCP is to allow the TCP protocol + ProtocolTCP Protocol = "tcp" + + // ProtocolUDP is to allow the UDP protocol + ProtocolUDP Protocol = "udp" +) + +type ( + // Action represents a valid rule protocol + Action string +) + +const ( + // ActionAllow is to allow traffic + ActionAllow Action = "allow" + + // ActionDeny is to deny traffic + ActionDeny Action = "deny" + + // ActionTCP is to reject traffic + ActionReject Action = "reject" +) + +type CreateOptsBuilder interface { + ToRuleCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new firewall rule. +type CreateOpts struct { + Protocol Protocol `json:"protocol" required:"true"` + Action Action `json:"action" required:"true"` + TenantID string `json:"tenant_id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + IPVersion gophercloud.IPVersion `json:"ip_version,omitempty"` + SourceIPAddress string `json:"source_ip_address,omitempty"` + DestinationIPAddress string `json:"destination_ip_address,omitempty"` + SourcePort string `json:"source_port,omitempty"` + DestinationPort string `json:"destination_port,omitempty"` + Shared *bool `json:"shared,omitempty"` + Enabled *bool `json:"enabled,omitempty"` +} + +// ToRuleCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "firewall_rule") + if err != nil { + return nil, err + } + + if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" { + m["protocol"] = nil + } + + return b, nil +} + +// Create accepts a CreateOpts struct and uses the values to create a new firewall rule +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToRuleCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go new file mode 100644 index 0000000000..12e39527e0 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go @@ -0,0 +1,42 @@ +package rules + +import ( + "github.com/gophercloud/gophercloud" +) + +// Rule represents a firewall rule +type Rule struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Protocol string `json:"protocol"` + Action string `json:"action"` + IPVersion int `json:"ip_version,omitempty"` + SourceIPAddress string `json:"source_ip_address,omitempty"` + DestinationIPAddress string `json:"destination_ip_address,omitempty"` + SourcePort string `json:"source_port,omitempty"` + DestinationPort string `json:"destination_port,omitempty"` + Shared bool `json:"shared,omitempty"` + Enabled bool `json:"enabled,omitempty"` + FirewallPolicyID string `json:"firewall_policy_id"` + TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a firewall rule. +func (r commonResult) Extract() (*Rule, error) { + var s struct { + Rule *Rule `json:"firewall_rule"` + } + err := r.ExtractInto(&s) + return s.Rule, err +} + +// CreateResult represents the result of a create operation. +type CreateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/testing/doc.go b/openstack/networking/v2/extensions/fwaas_v2/rules/testing/doc.go new file mode 100644 index 0000000000..481ae2e5e9 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/testing/doc.go @@ -0,0 +1,2 @@ +// networking_extensions_fwaas_rules_v2 +package testing 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 new file mode 100644 index 0000000000..9df195a735 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/testing/requests_test.go @@ -0,0 +1,135 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_rules", 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, ` +{ + "firewall_rule": { + "protocol": "tcp", + "description": "ssh rule", + "destination_ip_address": "192.168.1.0/24", + "destination_port": "22", + "name": "ssh_form_any", + "action": "allow", + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "firewall_rule":{ + "protocol": "tcp", + "description": "ssh rule", + "source_port": null, + "source_ip_address": null, + "destination_ip_address": "192.168.1.0/24", + "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "position": 2, + "destination_port": "22", + "id": "f03bd950-6c56-4f5e-a307-45967078f507", + "name": "ssh_form_any", + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "enabled": true, + "action": "allow", + "ip_version": 4, + "shared": false + } +} + `) + }) + + options := rules.CreateOpts{ + TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", + Protocol: rules.ProtocolTCP, + Description: "ssh rule", + DestinationIPAddress: "192.168.1.0/24", + DestinationPort: "22", + Name: "ssh_form_any", + Action: "allow", + } + + _, err := rules.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) +} + +func TestCreateAnyProtocol(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_rules", 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, ` +{ + "firewall_rule": { + "protocol": null, + "description": "any to 192.168.1.0/24", + "destination_ip_address": "192.168.1.0/24", + "name": "any_to_192.168.1.0/24", + "action": "allow", + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "firewall_rule":{ + "protocol": null, + "description": "any to 192.168.1.0/24", + "source_port": null, + "source_ip_address": null, + "destination_ip_address": "192.168.1.0/24", + "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "position": 2, + "destination_port": null, + "id": "f03bd950-6c56-4f5e-a307-45967078f507", + "name": "any_to_192.168.1.0/24", + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "enabled": true, + "action": "allow", + "ip_version": 4, + "shared": false + } +} + `) + }) + + options := rules.CreateOpts{ + TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", + Protocol: rules.ProtocolAny, + Description: "any to 192.168.1.0/24", + DestinationIPAddress: "192.168.1.0/24", + Name: "any_to_192.168.1.0/24", + Action: "allow", + } + + _, err := rules.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go b/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go new file mode 100644 index 0000000000..9684609a62 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go @@ -0,0 +1,12 @@ +package rules + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "fwaas" + resourcePath = "firewall_rules" +) + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, resourcePath) +} From bee62241ad2c5b15b27906b87e7920a87b7652f4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 19 Nov 2019 08:55:33 -0700 Subject: [PATCH 0900/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dd665327b..54df1cfa7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ IMPROVEMENTS * 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). ## 0.6.0 (October 17, 2019) From 72bd9e4fb4a2d0929324a6e2c2d39ec17e8f19cc Mon Sep 17 00:00:00 2001 From: Colin Fowler Date: Fri, 22 Nov 2019 03:05:11 +0000 Subject: [PATCH 0901/2296] [wip] FWaaS v2.0 Rule Delete (depends on PR 1768) (#1771) * FWaaSv2 Rule Create * minor formatting changes in imports to pass ./script/format * Add specific type for Action as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346643142 * rename PolicyID to FirewallPolicyID as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346644820 * removed Position as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346646305 * Add ProjectID to Rule * Initial acceptance test for creating rule * FWaaSv2 Rule Delete * Added Deletion of Rule to Rules acceptance test * convert Protocol to string in acceptance tests to prevent AssertEquals failures * minor refactor of TestRuleCD acceptance test * fix formatting with goimports * minor goimports fixes (no newlines at end of file) --- .../networking/v2/extensions/fwaas_v2/fwaas_v2.go | 14 ++++++++++++++ .../networking/v2/extensions/fwaas_v2/rule_test.go | 4 +++- .../v2/extensions/fwaas_v2/rules/requests.go | 6 ++++++ .../v2/extensions/fwaas_v2/rules/results.go | 5 +++++ .../fwaas_v2/rules/testing/requests_test.go | 14 ++++++++++++++ .../v2/extensions/fwaas_v2/rules/urls.go | 4 ++++ 6 files changed, 46 insertions(+), 1 deletion(-) 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 e75b35cdf2..e44427d6af 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -51,3 +51,17 @@ func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, e return rule, nil } + +// DeleteRule will delete a rule 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 DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { + t.Logf("Attempting to delete rule: %s", ruleID) + + err := rules.Delete(client, ruleID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete rule %s: %v", ruleID, err) + } + + t.Logf("Deleted rule: %s", ruleID) +} 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 e2200e5a52..664b9f9fb8 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -10,11 +10,13 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestRuleC(t *testing.T) { +func TestRuleCD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) rule, err := CreateRule(t, client) th.AssertNoErr(t, err) + defer DeleteRule(t, client, rule.ID) + tools.PrintResource(t, rule) } diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index 17d2f6a668..6125be18de 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -83,3 +83,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) return } + +// Delete will permanently delete a particular firewall rule based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go index 12e39527e0..f7b6765d82 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go @@ -36,6 +36,11 @@ func (r commonResult) Extract() (*Rule, error) { return s.Rule, err } +// DeleteResult represents the result of a delete operation. +type DeleteResult struct { + gophercloud.ErrResult +} + // CreateResult represents the result of a create operation. type CreateResult struct { commonResult 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 9df195a735..5292906353 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 @@ -133,3 +133,17 @@ func TestCreateAnyProtocol(t *testing.T) { _, err := rules.Create(fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_rules/4ec89077-d057-4a2b-911f-60a3b47ee304", 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) + }) + + res := rules.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go b/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go index 9684609a62..926f368fb3 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go @@ -10,3 +10,7 @@ const ( 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 bfdd239f72ca4487a20903b52029d953b6e70b54 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 21 Nov 2019 20:05:52 -0700 Subject: [PATCH 0902/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54df1cfa7b..edada37fd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ IMPROVEMENTS * 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). ## 0.6.0 (October 17, 2019) From 2567326a4c71208f8b4717a784a912212b5eff7d Mon Sep 17 00:00:00 2001 From: Luis Tomas Bolivar Date: Fri, 22 Nov 2019 04:09:01 +0100 Subject: [PATCH 0903/2296] Add providers support to openstack loadbalancer (#1765) --- .../loadbalancer/v2/providers_test.go | 39 ++++++++++ openstack/loadbalancer/v2/providers/doc.go | 22 ++++++ .../loadbalancer/v2/providers/requests.go | 44 ++++++++++++ .../loadbalancer/v2/providers/results.go | 71 +++++++++++++++++++ .../loadbalancer/v2/providers/testing/doc.go | 2 + .../v2/providers/testing/fixtures.go | 56 +++++++++++++++ .../v2/providers/testing/requests_test.go | 53 ++++++++++++++ openstack/loadbalancer/v2/providers/urls.go | 12 ++++ 8 files changed, 299 insertions(+) create mode 100644 acceptance/openstack/loadbalancer/v2/providers_test.go create mode 100644 openstack/loadbalancer/v2/providers/doc.go create mode 100644 openstack/loadbalancer/v2/providers/requests.go create mode 100644 openstack/loadbalancer/v2/providers/results.go create mode 100644 openstack/loadbalancer/v2/providers/testing/doc.go create mode 100644 openstack/loadbalancer/v2/providers/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/providers/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/providers/urls.go diff --git a/acceptance/openstack/loadbalancer/v2/providers_test.go b/acceptance/openstack/loadbalancer/v2/providers_test.go new file mode 100644 index 0000000000..4426150f60 --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/providers_test.go @@ -0,0 +1,39 @@ +// +build acceptance networking loadbalancer providers + +package v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" +) + +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) + } + + allPages, err := providers.List(client, nil).AllPages() + if err != nil { + t.Fatalf("Unable to list providers: %v", err) + } + + allProviders, err := providers.ExtractProviders(allPages) + if err != nil { + t.Fatalf("Unable to extract providers: %v", err) + } + + for _, provider := range allProviders { + tools.PrintResource(t, provider) + } +} diff --git a/openstack/loadbalancer/v2/providers/doc.go b/openstack/loadbalancer/v2/providers/doc.go new file mode 100644 index 0000000000..695294b8a6 --- /dev/null +++ b/openstack/loadbalancer/v2/providers/doc.go @@ -0,0 +1,22 @@ +/* +Package providers provides information about the supported providers +at OpenStack Octavia Load Balancing service. + +Example to List Providers + + allPages, err := providers.List(lbClient).AllPages() + if err != nil { + panic(err) + } + + allProviders, err := providers.ExtractProviders(allPages) + if err != nil { + panic(err) + } + + for _, p := range allProviders { + fmt.Printf("%+v\n", p) + } + +*/ +package providers diff --git a/openstack/loadbalancer/v2/providers/requests.go b/openstack/loadbalancer/v2/providers/requests.go new file mode 100644 index 0000000000..f386276483 --- /dev/null +++ b/openstack/loadbalancer/v2/providers/requests.go @@ -0,0 +1,44 @@ +package providers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToProviderListQuery() (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 Provider attributes you want to see returned. +type ListOpts struct { + Fields []string `q:"fields"` +} + +// ToProviderListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToProviderListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// providers. +// +// Default policy settings return only those providers that are owned by +// the project who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToProviderListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return ProviderPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/loadbalancer/v2/providers/results.go b/openstack/loadbalancer/v2/providers/results.go new file mode 100644 index 0000000000..7a7d8afcee --- /dev/null +++ b/openstack/loadbalancer/v2/providers/results.go @@ -0,0 +1,71 @@ +package providers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Provider is the Octavia driver that implements the load balancing mechanism +type Provider struct { + // Human-readable description for the Loadbalancer. + Description string `json:"description"` + + // Human-readable name for the Provider. + Name string `json:"name"` +} + +// ProviderPage is the page returned by a pager when traversing over a +// collection of providers. +type ProviderPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of providers 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 ProviderPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"providers_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a ProviderPage struct is empty. +func (r ProviderPage) IsEmpty() (bool, error) { + is, err := ExtractProviders(r) + return len(is) == 0, err +} + +// ExtractProviders accepts a Page struct, specifically a ProviderPage +// struct, and extracts the elements into a slice of Provider structs. In +// other words, a generic collection is mapped into a relevant slice. +func ExtractProviders(r pagination.Page) ([]Provider, error) { + var s struct { + Providers []Provider `json:"providers"` + } + err := (r.(ProviderPage)).ExtractInto(&s) + return s.Providers, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a provider. +func (r commonResult) Extract() (*Provider, error) { + var s struct { + Provider *Provider `json:"provider"` + } + err := r.ExtractInto(&s) + return s.Provider, err +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a Provider. +type GetResult struct { + commonResult +} diff --git a/openstack/loadbalancer/v2/providers/testing/doc.go b/openstack/loadbalancer/v2/providers/testing/doc.go new file mode 100644 index 0000000000..6950ca6105 --- /dev/null +++ b/openstack/loadbalancer/v2/providers/testing/doc.go @@ -0,0 +1,2 @@ +// providers unit tests +package testing diff --git a/openstack/loadbalancer/v2/providers/testing/fixtures.go b/openstack/loadbalancer/v2/providers/testing/fixtures.go new file mode 100644 index 0000000000..95753a45b9 --- /dev/null +++ b/openstack/loadbalancer/v2/providers/testing/fixtures.go @@ -0,0 +1,56 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ProvidersListBody contains the canned body of a provider list response. +const ProvidersListBody = ` +{ + "providers":[ + { + "name": "amphora", + "description": "The Octavia Amphora driver." + }, + { + "name": "ovn", + "description": "The Octavia OVN driver" + } + ] +} +` + +var ( + ProviderAmphora = providers.Provider{ + Name: "amphora", + Description: "The Octavia Amphora driver.", + } + ProviderOVN = providers.Provider{ + Name: "ovn", + Description: "The Octavia OVN driver", + } +) + +// HandleProviderListSuccessfully sets up the test server to respond to a provider List request. +func HandleProviderListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/lbaas/providers", 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, ProvidersListBody) + default: + t.Fatalf("/v2.0/lbaas/providers invoked with unexpected marker=[%s]", marker) + } + }) +} diff --git a/openstack/loadbalancer/v2/providers/testing/requests_test.go b/openstack/loadbalancer/v2/providers/testing/requests_test.go new file mode 100644 index 0000000000..b8a879eba0 --- /dev/null +++ b/openstack/loadbalancer/v2/providers/testing/requests_test.go @@ -0,0 +1,53 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" + fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestListProviders(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleProviderListSuccessfully(t) + + pages := 0 + err := providers.List(fake.ServiceClient(), providers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := providers.ExtractProviders(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 providers, got %d", len(actual)) + } + th.CheckDeepEquals(t, ProviderAmphora, actual[0]) + th.CheckDeepEquals(t, ProviderOVN, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllProviders(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleProviderListSuccessfully(t) + + allPages, err := providers.List(fake.ServiceClient(), providers.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := providers.ExtractProviders(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ProviderAmphora, actual[0]) + th.CheckDeepEquals(t, ProviderOVN, actual[1]) +} diff --git a/openstack/loadbalancer/v2/providers/urls.go b/openstack/loadbalancer/v2/providers/urls.go new file mode 100644 index 0000000000..00d1c8880c --- /dev/null +++ b/openstack/loadbalancer/v2/providers/urls.go @@ -0,0 +1,12 @@ +package providers + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "lbaas" + resourcePath = "providers" +) + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, resourcePath) +} From d8ac278c1c9d94da29038ad44eb80909d30c00e9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 21 Nov 2019 20:09:53 -0700 Subject: [PATCH 0904/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index edada37fd2..e6188539f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ IMPROVEMENTS * 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). ## 0.6.0 (October 17, 2019) From 8eab1785dc241a2c132d77446d748d829f67c774 Mon Sep 17 00:00:00 2001 From: Colin Fowler Date: Tue, 26 Nov 2019 02:42:06 +0000 Subject: [PATCH 0905/2296] [wip] FWaaS v2.0 Rule Get (depends on PR 1771) (#1772) * FWaaSv2 Rule Create * minor formatting changes in imports to pass ./script/format * Add specific type for Action as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346643142 * rename PolicyID to FirewallPolicyID as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346644820 * removed Position as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346646305 * Add ProjectID to Rule * Initial acceptance test for creating rule * FWaaSv2 Rule Delete * Added Deletion of Rule to Rules acceptance test * convert Protocol to string in acceptance tests to prevent AssertEquals failures * minor refactor of TestRuleCD acceptance test * fix formatting with goimports * Rules Get --- .../v2/extensions/fwaas_v2/rule_test.go | 9 +++- .../v2/extensions/fwaas_v2/rules/requests.go | 6 +++ .../v2/extensions/fwaas_v2/rules/results.go | 5 ++ .../fwaas_v2/rules/testing/requests_test.go | 51 +++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) 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 664b9f9fb8..ddd4f40699 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -7,10 +7,12 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" th "github.com/gophercloud/gophercloud/testhelper" ) -func TestRuleCD(t *testing.T) { +func TestRuleCRD(t *testing.T) { + client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -19,4 +21,9 @@ func TestRuleCD(t *testing.T) { defer DeleteRule(t, client, rule.ID) tools.PrintResource(t, rule) + + newRule, err := rules.Get(client, rule.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newRule) } diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index 6125be18de..d6c22ec9b0 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -84,6 +84,12 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul return } +// Get retrieves a particular firewall rule based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + // Delete will permanently delete a particular firewall rule 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/rules/results.go b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go index f7b6765d82..00d0147dce 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go @@ -36,6 +36,11 @@ func (r commonResult) Extract() (*Rule, error) { return s.Rule, err } +// GetResult represents the result of a get operation. +type GetResult struct { + commonResult +} + // DeleteResult represents the result of a delete operation. type DeleteResult struct { gophercloud.ErrResult 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 5292906353..7e119e469a 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 @@ -134,6 +134,57 @@ func TestCreateAnyProtocol(t *testing.T) { th.AssertNoErr(t, err) } +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", 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, ` +{ + "firewall_rule":{ + "protocol": "tcp", + "description": "ssh rule", + "source_port": null, + "source_ip_address": null, + "destination_ip_address": "192.168.1.0/24", + "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "position": 2, + "destination_port": "22", + "id": "f03bd950-6c56-4f5e-a307-45967078f507", + "name": "ssh_form_any", + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "enabled": true, + "action": "allow", + "ip_version": 4, + "shared": false + } +} + `) + }) + + rule, err := rules.Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "tcp", rule.Protocol) + th.AssertEquals(t, "ssh rule", rule.Description) + th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress) + th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.FirewallPolicyID) + th.AssertEquals(t, "22", rule.DestinationPort) + 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, true, rule.Enabled) + th.AssertEquals(t, "allow", rule.Action) + th.AssertEquals(t, 4, rule.IPVersion) + th.AssertEquals(t, false, rule.Shared) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 6e4366bf5dcc036d93267b490846938c3f8691b3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 25 Nov 2019 19:42:34 -0700 Subject: [PATCH 0906/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6188539f4..5a461391ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ IMPROVEMENTS * 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). ## 0.6.0 (October 17, 2019) From 20f58269748c805c35479c4e81572ad415193d0e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 26 Nov 2019 08:21:13 -0700 Subject: [PATCH 0907/2296] Acc Tests: Fix TestImagesUpdate (#1780) --- acceptance/openstack/imageservice/v2/images_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/acceptance/openstack/imageservice/v2/images_test.go index 17069c3dc6..01aedf3b02 100644 --- a/acceptance/openstack/imageservice/v2/images_test.go +++ b/acceptance/openstack/imageservice/v2/images_test.go @@ -3,6 +3,7 @@ package v2 import ( + "sort" "testing" "time" @@ -171,6 +172,9 @@ func TestImagesUpdate(t *testing.T) { tools.PrintResource(t, newImage.Properties) th.AssertEquals(t, newImage.Name, image.Name+"foo") + + sort.Strings(newTags) + sort.Strings(newImage.Tags) th.AssertDeepEquals(t, newImage.Tags, newTags) // Because OpenStack is now adding additional properties automatically, From 11777ea8729d9fb1d9afd19e6fce1c9b7c9ff90b Mon Sep 17 00:00:00 2001 From: Colin Fowler Date: Wed, 27 Nov 2019 02:39:14 +0000 Subject: [PATCH 0908/2296] [wip] FWaaS v2.0 Rule Update (depends on PR 1772) (#1776) * FWaaSv2 Rule Create * minor formatting changes in imports to pass ./script/format * Add specific type for Action as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346643142 * rename PolicyID to FirewallPolicyID as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346644820 * removed Position as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346646305 * Add ProjectID to Rule * Initial acceptance test for creating rule * FWaaSv2 Rule Delete * Added Deletion of Rule to Rules acceptance test * convert Protocol to string in acceptance tests to prevent AssertEquals failures * minor refactor of TestRuleCD acceptance test * fix formatting with goimports * Rules Get * Rule Update * Use Protocol and Action in UpdateOpts struct * Use Protocol and Action types in TestUpdate unit test * expand acceptance test for rule updates --- .../v2/extensions/fwaas_v2/rule_test.go | 14 +++- .../v2/extensions/fwaas_v2/rules/requests.go | 41 +++++++++++ .../v2/extensions/fwaas_v2/rules/results.go | 5 ++ .../fwaas_v2/rules/testing/requests_test.go | 69 +++++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) 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 ddd4f40699..ff561744af 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -11,7 +11,7 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestRuleCRD(t *testing.T) { +func TestRuleCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -22,6 +22,18 @@ func TestRuleCRD(t *testing.T) { tools.PrintResource(t, rule) + ruleDescription := "Some rule description" + ruleProtocol := rules.ProtocolICMP + updateOpts := rules.UpdateOpts{ + Description: &ruleDescription, + Protocol: &ruleProtocol, + } + + ruleUpdated, err := rules.Update(client, rule.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, ruleUpdated.Description, ruleDescription) + th.AssertEquals(t, ruleUpdated.Protocol, string(ruleProtocol)) + newRule, err := rules.Get(client, rule.ID).Extract() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index d6c22ec9b0..391da9bd50 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -90,6 +90,47 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } +// UpdateOptsBuilder is the interface options structs have to satisfy in order +// to be used in the main Update operation in this package. Since many +// extensions decorate or modify the common logic, it is useful for them to +// satisfy a basic interface in order for them to be used. +type UpdateOptsBuilder interface { + ToRuleUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contains the values used when updating a firewall rule. +type UpdateOpts struct { + Protocol *Protocol `json:"protocol,omitempty"` + Action *Action `json:"action,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + IPVersion *gophercloud.IPVersion `json:"ip_version,omitempty"` + SourceIPAddress *string `json:"source_ip_address,omitempty"` + DestinationIPAddress *string `json:"destination_ip_address,omitempty"` + SourcePort *string `json:"source_port,omitempty"` + DestinationPort *string `json:"destination_port,omitempty"` + Shared *bool `json:"shared,omitempty"` + Enabled *bool `json:"enabled,omitempty"` +} + +// ToRuleUpdateMap casts a UpdateOpts struct to a map. +func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "firewall_rule") +} + +// Update allows firewall policies to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToRuleUpdateMap() + 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 rule 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/rules/results.go b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go index 00d0147dce..11ce172589 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go @@ -41,6 +41,11 @@ type GetResult struct { commonResult } +// UpdateResult represents the result of an update operation. +type UpdateResult struct { + commonResult +} + // DeleteResult represents the result of a delete operation. type DeleteResult struct { gophercloud.ErrResult 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 7e119e469a..9448988487 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 @@ -5,6 +5,7 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" th "github.com/gophercloud/gophercloud/testhelper" @@ -185,6 +186,74 @@ func TestGet(t *testing.T) { th.AssertEquals(t, false, rule.Shared) } +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", 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_rule":{ + "protocol": "tcp", + "description": "ssh rule", + "destination_ip_address": "192.168.1.0/24", + "destination_port": "22", + "name": "ssh_form_any", + "action": "allow", + "enabled": false + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "firewall_rule":{ + "protocol": "tcp", + "description": "ssh rule", + "destination_ip_address": "192.168.1.0/24", + "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "position": 2, + "destination_port": "22", + "id": "f03bd950-6c56-4f5e-a307-45967078f507", + "name": "ssh_form_any", + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "enabled": false, + "action": "allow", + "ip_version": 4, + "shared": false + } +} + `) + }) + + newProtocol := rules.ProtocolTCP + newDescription := "ssh rule" + newDestinationIP := "192.168.1.0/24" + newDestintionPort := "22" + newName := "ssh_form_any" + newAction := rules.ActionAllow + + options := rules.UpdateOpts{ + Protocol: &newProtocol, + Description: &newDescription, + DestinationIPAddress: &newDestinationIP, + DestinationPort: &newDestintionPort, + Name: &newName, + Action: &newAction, + Enabled: gophercloud.Disabled, + } + + _, err := rules.Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract() + th.AssertNoErr(t, err) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 978be3847c66d261eda8d67a577b5eacb304b2bf Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 26 Nov 2019 19:39:39 -0700 Subject: [PATCH 0909/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a461391ed..f5a1924d70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ IMPROVEMENTS * 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). ## 0.6.0 (October 17, 2019) From 4654070080585e5358af3b00c8f07029821c0179 Mon Sep 17 00:00:00 2001 From: Colin Fowler Date: Fri, 29 Nov 2019 21:22:42 +0000 Subject: [PATCH 0910/2296] [wip] FWaaS v2.0 Rule List (#1783) * FWaaSv2 Rule Create * minor formatting changes in imports to pass ./script/format * Add specific type for Action as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346643142 * rename PolicyID to FirewallPolicyID as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346644820 * removed Position as per https://github.com/gophercloud/gophercloud/pull/1768/files/ad42c6277e24d42c4dacb5cb0ccf10181d2c8306#r346646305 * Add ProjectID to Rule * Initial acceptance test for creating rule * FWaaSv2 Rule Delete * Added Deletion of Rule to Rules acceptance test * convert Protocol to string in acceptance tests to prevent AssertEquals failures * minor refactor of TestRuleCD acceptance test * fix formatting with goimports * Rules Get * Rule Update * Use Protocol and Action in UpdateOpts struct * Use Protocol and Action types in TestUpdate unit test * expand acceptance test for rule updates * Rule List * Change Enabled and Shared in ListOpts to * bool as per https://github.com/gophercloud/gophercloud/pull/1783#pullrequestreview-324039113 --- .../v2/extensions/fwaas_v2/rule_test.go | 17 +++ .../v2/extensions/fwaas_v2/rules/requests.go | 69 ++++++++++++ .../v2/extensions/fwaas_v2/rules/results.go | 38 +++++++ .../fwaas_v2/rules/testing/requests_test.go | 106 ++++++++++++++++++ 4 files changed, 230 insertions(+) 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 ff561744af..06689f196d 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -38,4 +38,21 @@ func TestRuleCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, newRule) + + allPages, err := rules.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allRules, err := rules.ExtractRules(allPages) + th.AssertNoErr(t, err) + + t.Logf("Attempting to find rule %s\n", newRule.ID) + var found bool + for _, rule := range allRules { + if rule.ID == newRule.ID { + found = true + t.Logf("Found rule %s\n", newRule.ID) + } + } + + th.AssertEquals(t, found, true) } diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index 391da9bd50..caddd9d2d4 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -2,6 +2,7 @@ package rules import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type ( @@ -39,6 +40,74 @@ const ( ActionReject Action = "reject" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToRuleListQuery() (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 Firewall rule attributes you want to see returned. SortKey allows you to +// sort by a particular firewall rule attribute. SortDir sets the direction, and is +// either `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + TenantID string `q:"tenant_id"` + Name string `q:"name"` + Description string `q:"description"` + Protocol Protocol `q:"protocol"` + Action Action `q:"action"` + IPVersion int `q:"ip_version"` + SourceIPAddress string `q:"source_ip_address"` + DestinationIPAddress string `q:"destination_ip_address"` + SourcePort string `q:"source_port"` + DestinationPort string `q:"destination_port"` + Enabled *bool `q:"enabled"` + ID string `q:"id"` + Shared *bool `q:"shared"` + ProjectID string `q:"project_id"` + FirewallPolicyID string `q:"firewall_policy_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToRuleListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToRuleListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), nil +} + +// List returns a Pager which allows you to iterate over a collection of +// firewall rules. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. +// +// Default policy settings return only those firewall rules that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + + if opts != nil { + query, err := opts.ToRuleListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return RulePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOptsBuilder is the interface options structs have to satisfy in order +// to be used in the main Create operation in this package. Since many +// extensions decorate or modify the common logic, it is useful for them to +// satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { ToRuleCreateMap() (map[string]interface{}, error) } diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go index 11ce172589..6308682522 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go @@ -2,6 +2,7 @@ package rules import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // Rule represents a firewall rule @@ -23,6 +24,43 @@ type Rule struct { ProjectID string `json:"project_id"` } +// RulePage is the page returned by a pager when traversing over a +// collection of firewall rules. +type RulePage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of firewall rules 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 RulePage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"firewall_rules_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a RulePage struct is empty. +func (r RulePage) IsEmpty() (bool, error) { + is, err := ExtractRules(r) + return len(is) == 0, err +} + +// ExtractRules accepts a Page struct, specifically a RouterPage struct, +// and extracts the elements into a slice of Router structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractRules(r pagination.Page) ([]Rule, error) { + var s struct { + Rules []Rule `json:"firewall_rules"` + } + err := (r.(RulePage)).ExtractInto(&s) + return s.Rules, err +} + type commonResult struct { gophercloud.Result } 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 9448988487..2d509d37ca 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 @@ -8,9 +8,115 @@ import ( "github.com/gophercloud/gophercloud" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" + "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/fwaas/firewall_rules", 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, ` +{ + "firewall_rules": [ + { + "protocol": "tcp", + "description": "ssh rule", + "source_port": null, + "source_ip_address": null, + "destination_ip_address": "192.168.1.0/24", + "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "destination_port": "22", + "id": "f03bd950-6c56-4f5e-a307-45967078f507", + "name": "ssh_form_any", + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "enabled": true, + "action": "allow", + "ip_version": 4, + "shared": false + }, + { + "protocol": "udp", + "description": "udp rule", + "source_port": null, + "source_ip_address": null, + "destination_ip_address": null, + "firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", + "destination_port": null, + "id": "ab7bd950-6c56-4f5e-a307-45967078f890", + "name": "deny_all_udp", + "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", + "enabled": true, + "action": "deny", + "ip_version": 4, + "shared": false + } + ] +} + `) + }) + + count := 0 + + rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := rules.ExtractRules(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + + expected := []rules.Rule{ + { + Protocol: string(rules.ProtocolTCP), + Description: "ssh rule", + SourcePort: "", + SourceIPAddress: "", + DestinationIPAddress: "192.168.1.0/24", + FirewallPolicyID: "e2a5fb51-698c-4898-87e8-f1eee6b50919", + DestinationPort: "22", + ID: "f03bd950-6c56-4f5e-a307-45967078f507", + Name: "ssh_form_any", + TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", + Enabled: true, + Action: string(rules.ActionAllow), + IPVersion: 4, + Shared: false, + }, + { + Protocol: "udp", + Description: "udp rule", + SourcePort: "", + SourceIPAddress: "", + DestinationIPAddress: "", + FirewallPolicyID: "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", + DestinationPort: "", + ID: "ab7bd950-6c56-4f5e-a307-45967078f890", + Name: "deny_all_udp", + TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", + Enabled: true, + Action: "deny", + IPVersion: 4, + Shared: false, + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 794fbdbba08a7d1b848500304c708c980a6a272f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 29 Nov 2019 14:23:21 -0700 Subject: [PATCH 0911/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5a1924d70..629fb27b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ IMPROVEMENTS * 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). ## 0.6.0 (October 17, 2019) From 3682d32ae22214bc697971938d057b192594fde5 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 30 Nov 2019 17:44:59 +0300 Subject: [PATCH 0912/2296] LoadBalancer V2: add monitor List MaxRetriesDown (#1788) * LoadBalancer V2: add monitor Create MaxRetriesDown Add loadbalancer/v2/monitors.CreateOpts.MaxRetriesDown. Update tests and docs. * LoadBalancer V2: add monitor Update MaxRetriesDown Add loadbalancer/v2/monitors.UpdateOpts.MaxRetriesDown. Update tests and docs. * LoadBalancer V2: add monitor body MaxRetriesDown Add loadbalancer/v2/monitors.Monitor.MaxRetriesDown. Update tests and docs. * LoadBalancer V2: add monitor List MaxRetriesDown Add loadbalancer/v2/monitors.ListOpts.MaxRetriesDown. Update tests and docs. --- .../openstack/loadbalancer/v2/loadbalancer.go | 17 +++-- .../loadbalancer/v2/loadbalancers_test.go | 13 +++- openstack/loadbalancer/v2/monitors/doc.go | 30 ++++---- .../loadbalancer/v2/monitors/requests.go | 45 ++++++----- openstack/loadbalancer/v2/monitors/results.go | 4 + .../v2/monitors/testing/fixtures.go | 75 +++++++++++-------- .../v2/monitors/testing/requests_test.go | 32 ++++---- 7 files changed, 127 insertions(+), 89 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 880e29403b..8a08d5232a 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -147,12 +147,13 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala t.Logf("Attempting to create monitor %s", monitorName) createOpts := monitors.CreateOpts{ - PoolID: pool.ID, - Name: monitorName, - Delay: 10, - Timeout: 5, - MaxRetries: 5, - Type: monitors.TypePING, + PoolID: pool.ID, + Name: monitorName, + Delay: 10, + Timeout: 5, + MaxRetries: 5, + MaxRetriesDown: 4, + Type: monitors.TypePING, } monitor, err := monitors.Create(client, createOpts).Extract() @@ -168,6 +169,10 @@ 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.Timeout, 5) + th.AssertEquals(t, monitor.MaxRetries, 5) + th.AssertEquals(t, monitor.MaxRetriesDown, 4) return monitor, nil } diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 55c954930f..a0eadd54a1 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -386,9 +386,11 @@ func TestLoadbalancersCRUD(t *testing.T) { monName := "" newDelay := tools.RandomInt(20, 30) + newMaxRetriesDown := tools.RandomInt(4, 10) updateMonitorOpts := monitors.UpdateOpts{ - Name: &monName, - Delay: newDelay, + Name: &monName, + Delay: newDelay, + MaxRetriesDown: newMaxRetriesDown, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) @@ -404,6 +406,7 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertEquals(t, newMonitor.Name, monName) th.AssertEquals(t, newMonitor.Delay, newDelay) + th.AssertEquals(t, newMonitor.MaxRetriesDown, newMaxRetriesDown) } func TestLoadbalancersCascadeCRUD(t *testing.T) { @@ -506,8 +509,10 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { th.AssertNoErr(t, err) newDelay := tools.RandomInt(20, 30) + newMaxRetriesDown := tools.RandomInt(4, 10) updateMonitorOpts := monitors.UpdateOpts{ - Delay: newDelay, + Delay: newDelay, + MaxRetriesDown: newMaxRetriesDown, } _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) @@ -521,4 +526,6 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { tools.PrintResource(t, newMonitor) + th.AssertEquals(t, newMonitor.Delay, newDelay) + th.AssertEquals(t, newMonitor.MaxRetriesDown, newMaxRetriesDown) } diff --git a/openstack/loadbalancer/v2/monitors/doc.go b/openstack/loadbalancer/v2/monitors/doc.go index 6ed8c8fb5f..b191b45e9c 100644 --- a/openstack/loadbalancer/v2/monitors/doc.go +++ b/openstack/loadbalancer/v2/monitors/doc.go @@ -25,14 +25,15 @@ Example to List Monitors Example to Create a Monitor createOpts := monitors.CreateOpts{ - Type: "HTTP", - Name: "db", - PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", - Delay: 20, - Timeout: 10, - MaxRetries: 5, - URLPath: "/check", - ExpectedCodes: "200-299", + Type: "HTTP", + Name: "db", + PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", + Delay: 20, + Timeout: 10, + MaxRetries: 5, + MaxRetriesDown: 4, + URLPath: "/check", + ExpectedCodes: "200-299", } monitor, err := monitors.Create(networkClient, createOpts).Extract() @@ -45,12 +46,13 @@ Example to Update a Monitor monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" updateOpts := monitors.UpdateOpts{ - Name: "NewHealthmonitorName", - Delay: 3, - Timeout: 20, - MaxRetries: 10, - URLPath: "/another_check", - ExpectedCodes: "301", + Name: "NewHealthmonitorName", + Delay: 3, + Timeout: 20, + MaxRetries: 10, + MaxRetriesDown: 8, + URLPath: "/another_check", + ExpectedCodes: "301", } monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index c05289504c..1a7e8b8f48 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -19,24 +19,25 @@ 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"` - 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"` + 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"` } // ToMonitorListQuery formats a ListOpts into a query string. @@ -107,6 +108,10 @@ 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 + // status to ERROR. Must be a number between 1 and 10. + MaxRetriesDown int `json:"max_retries_down,omitempty"` + // URI path that will be accessed if Monitor type is HTTP or HTTPS. URLPath string `json:"url_path,omitempty"` @@ -191,6 +196,10 @@ type UpdateOpts struct { // status to INACTIVE. Must be a number between 1 and 10. MaxRetries int `json:"max_retries,omitempty"` + // Number of permissible ping failures befor changing the member's + // status to ERROR. Must be a number between 1 and 10. + MaxRetriesDown int `json:"max_retries_down,omitempty"` + // URI path that will be accessed if Monitor type is HTTP or HTTPS. // Required for HTTP(S) types. URLPath string `json:"url_path,omitempty"` diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index 2c3a815bae..dec9395192 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -50,6 +50,10 @@ type Monitor struct { // member to INACTIVE. A valid value is from 1 to 10. MaxRetries int `json:"max_retries"` + // Number of allowed connection failures before changing the status of the + // member to Error. A valid value is from 1 to 10. + MaxRetriesDown int `json:"max_retries_down"` + // The HTTP method that the monitor uses for requests. HTTPMethod string `json:"http_method"` diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures.go b/openstack/loadbalancer/v2/monitors/testing/fixtures.go index 23c097b909..c6ae88494d 100644 --- a/openstack/loadbalancer/v2/monitors/testing/fixtures.go +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures.go @@ -20,6 +20,7 @@ const HealthmonitorsListBody = ` "delay":10, "name":"web", "max_retries":1, + "max_retries_down":7, "timeout":1, "type":"PING", "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}], @@ -32,6 +33,7 @@ const HealthmonitorsListBody = ` "name":"db", "expected_codes":"200", "max_retries":2, + "max_retries_down":4, "http_method":"GET", "timeout":2, "url_path":"/", @@ -53,6 +55,7 @@ const SingleHealthmonitorBody = ` "name":"db", "expected_codes":"200", "max_retries":2, + "max_retries_down":4, "http_method":"GET", "timeout":2, "url_path":"/", @@ -73,6 +76,7 @@ const PostUpdateHealthmonitorBody = ` "name":"NewHealthmonitorName", "expected_codes":"301", "max_retries":10, + "max_retries_down":8, "http_method":"GET", "timeout":20, "url_path":"/another_check", @@ -85,43 +89,46 @@ const PostUpdateHealthmonitorBody = ` var ( HealthmonitorWeb = monitors.Monitor{ - AdminStateUp: true, - Name: "web", - ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", - Delay: 10, - MaxRetries: 1, - Timeout: 1, - Type: "PING", - ID: "466c8345-28d8-4f84-a246-e04380b0461d", - Pools: []monitors.PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}}, + 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"}}, } HealthmonitorDb = monitors.Monitor{ - AdminStateUp: true, - Name: "db", - ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", - Delay: 5, - ExpectedCodes: "200", - MaxRetries: 2, - Timeout: 2, - URLPath: "/", - Type: "HTTP", - HTTPMethod: "GET", - ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", - Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, + AdminStateUp: true, + Name: "db", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", + Delay: 5, + ExpectedCodes: "200", + MaxRetries: 2, + MaxRetriesDown: 4, + Timeout: 2, + URLPath: "/", + Type: "HTTP", + HTTPMethod: "GET", + ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", + Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } HealthmonitorUpdated = monitors.Monitor{ - AdminStateUp: true, - Name: "NewHealthmonitorName", - ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", - Delay: 3, - ExpectedCodes: "301", - MaxRetries: 10, - Timeout: 20, - URLPath: "/another_check", - Type: "HTTP", - HTTPMethod: "GET", - ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", - Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, + AdminStateUp: true, + Name: "NewHealthmonitorName", + ProjectID: "83657cfcdfe44cd5920adaf26c48ceea", + Delay: 3, + ExpectedCodes: "301", + MaxRetries: 10, + MaxRetriesDown: 8, + Timeout: 20, + URLPath: "/another_check", + Type: "HTTP", + HTTPMethod: "GET", + ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", + Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, } ) @@ -160,6 +167,7 @@ func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) { "name":"db", "timeout":10, "max_retries":5, + "max_retries_down":4, "url_path":"/check", "expected_codes":"200-299" } @@ -205,6 +213,7 @@ func HandleHealthmonitorUpdateSuccessfully(t *testing.T) { "delay": 3, "timeout": 20, "max_retries": 10, + "max_retries_down": 8, "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 8ccfb950dc..5b4bc6732b 100644 --- a/openstack/loadbalancer/v2/monitors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -58,15 +58,16 @@ func TestCreateHealthmonitor(t *testing.T) { HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody) actual, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ - Type: "HTTP", - Name: "db", - PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", - ProjectID: "453105b9-1754-413f-aab1-55f1af620750", - Delay: 20, - Timeout: 10, - MaxRetries: 5, - URLPath: "/check", - ExpectedCodes: "200-299", + Type: "HTTP", + Name: "db", + PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", + ProjectID: "453105b9-1754-413f-aab1-55f1af620750", + Delay: 20, + Timeout: 10, + MaxRetries: 5, + MaxRetriesDown: 4, + URLPath: "/check", + ExpectedCodes: "200-299", }).Extract() th.AssertNoErr(t, err) @@ -115,12 +116,13 @@ func TestUpdateHealthmonitor(t *testing.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, - URLPath: "/another_check", - ExpectedCodes: "301", + Name: &name, + Delay: 3, + Timeout: 20, + MaxRetries: 10, + MaxRetriesDown: 8, + URLPath: "/another_check", + ExpectedCodes: "301", }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From 6dad56fdca42773a2993258183682d7ebd2197e6 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Sat, 30 Nov 2019 17:49:17 +0300 Subject: [PATCH 0913/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 629fb27b2c..c00ba7aa24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ IMPROVEMENTS * 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). ## 0.6.0 (October 17, 2019) From ee13cae93769b396348930be7557a2a2ba7f902f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Dec 2019 19:44:59 -0700 Subject: [PATCH 0914/2296] vendor: Updating go mod dependencies (#1793) --- go.mod | 11 +++++++++-- go.sum | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 1d58ea20f3..46e16507dc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,13 @@ module github.com/gophercloud/gophercloud require ( - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 - gopkg.in/yaml.v2 v2.2.4 + golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e + golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect + golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect + golang.org/x/text v0.3.2 // indirect + golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 // indirect + golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.2.7 ) diff --git a/go.sum b/go.sum index 27dc9b3055..9a0b94de1f 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,26 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU= +golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 2dedcf0248df266611ed8891f29fc91deef34bef Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Dec 2019 19:48:41 -0700 Subject: [PATCH 0915/2296] Update CHANGELOG.md --- CHANGELOG.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c00ba7aa24..ff2c2528a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,17 +6,18 @@ IMPROVEMENTS * 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). +* 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) From c99da270f3181ddc1b3d5c5e7d040971024cb71a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Dec 2019 19:49:01 -0700 Subject: [PATCH 0916/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff2c2528a5..34f052380e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.7.0 (Unreleased) +## 0.8.0 (Unreleased) + +## 0.7.0 (December 3, 2019) IMPROVEMENTS From 0fb2abc5e7e95c73466ce2733c1e6d4f01811e94 Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Mon, 9 Dec 2019 13:42:54 -0800 Subject: [PATCH 0917/2296] openstack/networking/v2/ports/testing: fix dropped error (#1797) --- openstack/networking/v2/ports/testing/requests_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index a674e7eaa0..7cb00b4c53 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -93,6 +93,7 @@ func TestListWithExtensions(t *testing.T) { th.AssertNoErr(t, err) err = ports.ExtractPortsInto(allPages, &allPorts) + th.AssertNoErr(t, err) th.AssertEquals(t, allPorts[0].Status, "ACTIVE") th.AssertEquals(t, allPorts[0].PortSecurityEnabled, false) From 433bd8d804b9282bbdd612b690f945b2d59872c1 Mon Sep 17 00:00:00 2001 From: Thomas Hartland Date: Tue, 10 Dec 2019 04:16:39 +0000 Subject: [PATCH 0918/2296] ContainerInfra: extract ID from cluster resize result (#1649) --- openstack/containerinfra/v1/clusters/results.go | 8 ++++++++ openstack/containerinfra/v1/clusters/testing/fixtures.go | 3 +-- .../containerinfra/v1/clusters/testing/requests_test.go | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 6e940fa4a3..9fb05d203f 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -60,6 +60,14 @@ func (r UpdateResult) Extract() (string, error) { return s.UUID, err } +func (r ResizeResult) Extract() (string, error) { + var s struct { + UUID string + } + err := r.ExtractInto(&s) + return s.UUID, err +} + type Cluster struct { APIAddress string `json:"api_address"` COEVersion string `json:"coe_version"` diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index d92f8b8a62..5369503c78 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -289,8 +289,7 @@ func HandleDeleteClusterSuccessfully(t *testing.T) { var ResizeResponse = fmt.Sprintf(` { - "uuid": "%s", - "node_count": 2 + "uuid": "%s" }`, clusterUUID) func HandleResizeClusterSuccessfully(t *testing.T) { diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index 75814d819c..ee3e2f8913 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -201,5 +201,5 @@ func TestResizeCluster(t *testing.T) { actual, err := res.Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, nodeCount, actual.NodeCount) + th.AssertDeepEquals(t, clusterUUID, actual) } From f95a3f01ea5c46a1f80a48b9237fd69d006cbff0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 9 Dec 2019 21:18:38 -0700 Subject: [PATCH 0919/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34f052380e..b9cc1751a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.8.0 (Unreleased) +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) + ## 0.7.0 (December 3, 2019) IMPROVEMENTS From 7aa2e52d21f9c621caf111307a1e3590884abc47 Mon Sep 17 00:00:00 2001 From: Thomas Hartland Date: Tue, 10 Dec 2019 04:20:42 +0000 Subject: [PATCH 0920/2296] Containerinfra: acceptance test timeout changes and fixes (#1794) * Add WaitForTimeout to acceptance tools To be used in situations where a 300 second timeout is not long enough or otherwise unsuitable. The WaitFor function is changed to simply call WaitForTimeout and pass the same 300 second timeout, keeping the same behaviour. * Set timeout for cluster creation in containerinfra acceptance tests Keeps the same timeout as previously by default, but can be changed for cluster templates that require a longer timeout. * Check cluster creation error in containerinfra CRUD acceptance * Use integer for replacing node count in containerinfra acceptance tests Required for compatibility with Magnum APIs running on Python 3. See https://github.com/gophercloud/gophercloud/issues/1458 and https://github.com/gophercloud/gophercloud/pull/1463 * Use overlay2 for template storage driver in contianerinfra acceptance With the storage driver set to devicemapper the magnum API did not accept clusters created with the template, as docker_volume_size was not set. Instead of setting the volume size, overlay2 can be used with no volume and is the preferred storage driver. --- .../containerinfra/v1/clusters_test.go | 6 ++-- .../containerinfra/v1/containerinfra.go | 29 ++++++++++++------- acceptance/tools/tools.go | 14 +++++++-- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/clusters_test.go b/acceptance/openstack/containerinfra/v1/clusters_test.go index 1e2bf85351..2e23afae0a 100644 --- a/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -4,6 +4,7 @@ package v1 import ( "testing" + "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" @@ -20,6 +21,7 @@ func TestClustersCRUD(t *testing.T) { defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) clusterID, err := CreateCluster(t, client, clusterTemplate.UUID) + th.AssertNoErr(t, err) tools.PrintResource(t, clusterID) defer DeleteCluster(t, client, clusterID) @@ -40,7 +42,7 @@ func TestClustersCRUD(t *testing.T) { clusters.UpdateOpts{ Op: clusters.ReplaceOp, Path: "/node_count", - Value: "2", + Value: 2, }, } updateResult := clusters.Update(client, clusterID, updateOpts) @@ -53,7 +55,7 @@ func TestClustersCRUD(t *testing.T) { clusterID, err = updateResult.Extract() th.AssertNoErr(t, err) - err = WaitForCluster(client, clusterID, "UPDATE_COMPLETE") + err = WaitForCluster(client, clusterID, "UPDATE_COMPLETE", time.Second*300) th.AssertNoErr(t, err) newCluster, err := clusters.Get(client, clusterID).Extract() diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go index 748952df72..26ac924494 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -2,8 +2,10 @@ package v1 import ( "fmt" + "math" "strings" "testing" + "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -30,7 +32,7 @@ func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*cl createOpts := clustertemplates.CreateOpts{ COE: "swarm", DNSNameServer: "8.8.8.8", - DockerStorageDriver: "devicemapper", + DockerStorageDriver: "overlay2", ExternalNetworkID: choices.ExternalNetworkID, FlavorID: choices.FlavorID, FloatingIPEnabled: &boolFalse, @@ -85,9 +87,9 @@ func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id s return } -// CreateCluster will create a random cluster. An error will be returned if the -// cluster could not be created. -func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string) (string, error) { +// CreateClusterTimeout will create a random cluster and wait for it to reach CREATE_COMPLETE status +// within the given timeout duration. An error will be returned if the cluster could not be created. +func CreateClusterTimeout(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string, timeout time.Duration) (string, error) { clusterName := tools.RandomString("TESTACC-", 8) t.Logf("Attempting to create cluster: %s using template %s", clusterName, clusterTemplateID) @@ -98,7 +100,8 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTempl masterCount := 1 nodeCount := 1 - createTimeout := 100 + // createTimeout is the creation timeout on the magnum side in minutes + createTimeout := int(math.Ceil(timeout.Minutes())) createOpts := clusters.CreateOpts{ ClusterTemplateID: clusterTemplateID, CreateTimeout: &createTimeout, @@ -124,7 +127,7 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTempl t.Logf("Cluster created: %+v", clusterID) - err = WaitForCluster(client, clusterID, "CREATE_COMPLETE") + err = WaitForCluster(client, clusterID, "CREATE_COMPLETE", timeout) if err != nil { return clusterID, err } @@ -133,6 +136,12 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTempl return clusterID, nil } +// CreateCluster will create a random cluster. An error will be returned if the +// cluster could not be created. Has a timeout of 300 seconds. +func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string) (string, error) { + return CreateClusterTimeout(t, client, clusterTemplateID, 300*time.Second) +} + func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete cluster: %s", id) @@ -147,7 +156,7 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Fatalf("Error deleting cluster. requestID=%s clusterID=%s: err%s:", deleteRequestID, id, err) } - err = WaitForCluster(client, id, "DELETE_COMPLETE") + err = WaitForCluster(client, id, "DELETE_COMPLETE", 300*time.Second) if err != nil { t.Fatalf("Error deleting cluster %s: %s:", id, err) } @@ -157,8 +166,8 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { return } -func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status string) error { - return tools.WaitFor(func() (bool, error) { +func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status string, timeout time.Duration) error { + return tools.WaitForTimeout(func() (bool, error) { cluster, err := clusters.Get(client, clusterID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETE_COMPLETE" { @@ -177,7 +186,7 @@ func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status } return false, nil - }) + }, timeout) } // CreateQuota will create a random quota. An error will be returned if the diff --git a/acceptance/tools/tools.go b/acceptance/tools/tools.go index d2fd298d38..08e2e86f1d 100644 --- a/acceptance/tools/tools.go +++ b/acceptance/tools/tools.go @@ -9,12 +9,20 @@ import ( "time" ) -// ErrTimeout is returned if WaitFor takes longer than 300 second to happen. +// ErrTimeout is returned if WaitFor/WaitForTimeout take longer than their timeout duration. var ErrTimeout = errors.New("Timed out") -// WaitFor polls a predicate function once per second to wait for a certain state to arrive. +// WaitFor uses WaitForTimeout to poll a predicate function once per second to +// wait for a certain state to arrive, with a default timeout of 300 seconds. func WaitFor(predicate func() (bool, error)) error { - for i := 0; i < 300; i++ { + return WaitForTimeout(predicate, 300*time.Second) +} + +// WaitForTimeout polls a predicate function once per second to wait for a +// certain state to arrive, or until the given timeout is reached. +func WaitForTimeout(predicate func() (bool, error), timeout time.Duration) error { + startTime := time.Now() + for time.Since(startTime) < timeout { time.Sleep(1 * time.Second) satisfied, err := predicate() From bfa5322a13d61e65eb4a7a08b63f9b33d0e3f459 Mon Sep 17 00:00:00 2001 From: Kentrix Date: Thu, 12 Dec 2019 09:18:23 +1300 Subject: [PATCH 0921/2296] Added Shelve, Shelve-offload and Unshelve API (#1799) * Added Shelve, Shelve-offload and Unshelve API * Refractored actionURL and move the function into compute/v2/extensions * Added support for optional availability_zone argument for unshelve * Fixed up shelveunshelve doc.go and renamed UnshelvedOpts -> UnshelveOpts --- .../v2/extensions/evacuate/requests.go | 3 +- .../compute/v2/extensions/evacuate/urls.go | 9 --- .../v2/extensions/lockunlock/requests.go | 13 ++--- .../compute/v2/extensions/migrate/requests.go | 5 +- .../compute/v2/extensions/migrate/urls.go | 9 --- .../v2/extensions/pauseunpause/requests.go | 13 ++--- .../v2/extensions/rescueunrescue/requests.go | 9 ++- .../v2/extensions/resetstate/requests.go | 3 +- .../compute/v2/extensions/resetstate/urls.go | 9 --- .../v2/extensions/shelveunshelve/doc.go | 24 ++++++++ .../v2/extensions/shelveunshelve/requests.go | 57 ++++++++++++++++++ .../v2/extensions/shelveunshelve/results.go | 21 +++++++ .../shelveunshelve/testing/fixtures.go | 50 ++++++++++++++++ .../shelveunshelve/testing/requests_test.go | 58 +++++++++++++++++++ .../v2/extensions/startstop/requests.go | 13 ++--- .../v2/extensions/suspendresume/requests.go | 13 ++--- .../extensions/{rescueunrescue => }/urls.go | 4 +- 17 files changed, 249 insertions(+), 64 deletions(-) delete mode 100644 openstack/compute/v2/extensions/evacuate/urls.go delete mode 100644 openstack/compute/v2/extensions/migrate/urls.go delete mode 100644 openstack/compute/v2/extensions/resetstate/urls.go create mode 100644 openstack/compute/v2/extensions/shelveunshelve/doc.go create mode 100644 openstack/compute/v2/extensions/shelveunshelve/requests.go create mode 100644 openstack/compute/v2/extensions/shelveunshelve/results.go create mode 100644 openstack/compute/v2/extensions/shelveunshelve/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go rename openstack/compute/v2/extensions/{rescueunrescue => }/urls.go (54%) diff --git a/openstack/compute/v2/extensions/evacuate/requests.go b/openstack/compute/v2/extensions/evacuate/requests.go index 3ea7af4642..b0455acc73 100644 --- a/openstack/compute/v2/extensions/evacuate/requests.go +++ b/openstack/compute/v2/extensions/evacuate/requests.go @@ -2,6 +2,7 @@ package evacuate import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // EvacuateOptsBuilder allows extensions to add additional parameters to the @@ -34,7 +35,7 @@ func Evacuate(client *gophercloud.ServiceClient, id string, opts EvacuateOptsBui r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return diff --git a/openstack/compute/v2/extensions/evacuate/urls.go b/openstack/compute/v2/extensions/evacuate/urls.go deleted file mode 100644 index a8896aea0b..0000000000 --- a/openstack/compute/v2/extensions/evacuate/urls.go +++ /dev/null @@ -1,9 +0,0 @@ -package evacuate - -import ( - "github.com/gophercloud/gophercloud" -) - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("servers", id, "action") -} diff --git a/openstack/compute/v2/extensions/lockunlock/requests.go b/openstack/compute/v2/extensions/lockunlock/requests.go index 5243d0fba5..3ddf118467 100644 --- a/openstack/compute/v2/extensions/lockunlock/requests.go +++ b/openstack/compute/v2/extensions/lockunlock/requests.go @@ -1,19 +1,18 @@ package lockunlock -import "github.com/gophercloud/gophercloud" - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("servers", id, "action") -} +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" +) // Lock is the operation responsible for locking a Compute server. func Lock(client *gophercloud.ServiceClient, id string) (r LockResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) return } // Unlock is the operation responsible for unlocking a Compute server. func Unlock(client *gophercloud.ServiceClient, id string) (r UnlockResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/migrate/requests.go b/openstack/compute/v2/extensions/migrate/requests.go index 90ae62e381..5c7833c386 100644 --- a/openstack/compute/v2/extensions/migrate/requests.go +++ b/openstack/compute/v2/extensions/migrate/requests.go @@ -2,11 +2,12 @@ package migrate import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // Migrate will initiate a migration of the instance to another host. func Migrate(client *gophercloud.ServiceClient, id string) (r MigrateResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) return } @@ -45,6 +46,6 @@ func LiveMigrate(client *gophercloud.ServiceClient, id string, opts LiveMigrateO r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), b, nil, nil) return } diff --git a/openstack/compute/v2/extensions/migrate/urls.go b/openstack/compute/v2/extensions/migrate/urls.go deleted file mode 100644 index b8e2a437d7..0000000000 --- a/openstack/compute/v2/extensions/migrate/urls.go +++ /dev/null @@ -1,9 +0,0 @@ -package migrate - -import ( - "github.com/gophercloud/gophercloud" -) - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("servers", id, "action") -} diff --git a/openstack/compute/v2/extensions/pauseunpause/requests.go b/openstack/compute/v2/extensions/pauseunpause/requests.go index aeb880343f..2096173999 100644 --- a/openstack/compute/v2/extensions/pauseunpause/requests.go +++ b/openstack/compute/v2/extensions/pauseunpause/requests.go @@ -1,19 +1,18 @@ package pauseunpause -import "github.com/gophercloud/gophercloud" - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("servers", id, "action") -} +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" +) // Pause is the operation responsible for pausing a Compute server. func Pause(client *gophercloud.ServiceClient, id string) (r PauseResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) return } // Unpause is the operation responsible for unpausing a Compute server. func Unpause(client *gophercloud.ServiceClient, id string) (r UnpauseResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/rescueunrescue/requests.go b/openstack/compute/v2/extensions/rescueunrescue/requests.go index 61a23aad66..fd7534b97e 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/requests.go +++ b/openstack/compute/v2/extensions/rescueunrescue/requests.go @@ -1,6 +1,9 @@ package rescueunrescue -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" +) // RescueOptsBuilder is an interface that allows extensions to override the // default structure of a Rescue request. @@ -35,7 +38,7 @@ func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return @@ -43,6 +46,6 @@ func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder // Unrescue instructs the provider to return the server from RESCUE mode. func Unrescue(client *gophercloud.ServiceClient, id string) (r UnrescueResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/resetstate/requests.go b/openstack/compute/v2/extensions/resetstate/requests.go index 628e44aa47..e98ba32bd2 100644 --- a/openstack/compute/v2/extensions/resetstate/requests.go +++ b/openstack/compute/v2/extensions/resetstate/requests.go @@ -2,6 +2,7 @@ package resetstate import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" ) // ServerState refers to the states usable in ResetState Action @@ -18,6 +19,6 @@ const ( // ResetState will reset the state of a server func ResetState(client *gophercloud.ServiceClient, id string, state ServerState) (r ResetResult) { stateMap := map[string]interface{}{"state": state} - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/resetstate/urls.go b/openstack/compute/v2/extensions/resetstate/urls.go deleted file mode 100644 index c6da6d9304..0000000000 --- a/openstack/compute/v2/extensions/resetstate/urls.go +++ /dev/null @@ -1,9 +0,0 @@ -package resetstate - -import ( - "github.com/gophercloud/gophercloud" -) - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("servers", id, "action") -} diff --git a/openstack/compute/v2/extensions/shelveunshelve/doc.go b/openstack/compute/v2/extensions/shelveunshelve/doc.go new file mode 100644 index 0000000000..a5274a1545 --- /dev/null +++ b/openstack/compute/v2/extensions/shelveunshelve/doc.go @@ -0,0 +1,24 @@ +/* +Package shelveunshelve provides functionality to start and stop servers that have +been provisioned by the OpenStack Compute service. + +Example to Shelve, Shelve-offload and Unshelve a Server + + serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" + + err := shelveunshelve.Shelve(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + + err := shelveunshelve.ShelveOffload(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + + err := shelveunshelve.Unshelve(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package shelveunshelve diff --git a/openstack/compute/v2/extensions/shelveunshelve/requests.go b/openstack/compute/v2/extensions/shelveunshelve/requests.go new file mode 100644 index 0000000000..b238e220c4 --- /dev/null +++ b/openstack/compute/v2/extensions/shelveunshelve/requests.go @@ -0,0 +1,57 @@ +package shelveunshelve + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" +) + +// Shelve is the operation responsible for shelving a Compute server. +func Shelve(client *gophercloud.ServiceClient, id string) (r ShelveResult) { + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) + return +} + +// ShelveOffload is the operation responsible for Shelve-Offload a Compute server. +func ShelveOffload(client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) + return +} + +// UnshelveOptsBuilder allows extensions to add additional parameters to the +// Unshelve request. +type UnshelveOptsBuilder interface { + ToUnshelveMap() (map[string]interface{}, error) +} + +// UnshelveOpts specifies parameters of shelve-offload action. +type UnshelveOpts struct { + // Sets the availability zone to unshelve a server + // Available only after nova 2.77 + AvailabilityZone string `json:"availability_zone,omitempty"` +} + +func (opts UnshelveOpts) ToUnshelveMap() (map[string]interface{}, error) { + // Key 'availabilty_zone' is required if the unshelve action is an object + // i.e {"unshelve": {}} will be rejected + b, err := gophercloud.BuildRequestBody(opts, "unshelve") + if err != nil { + return nil, err + } + + if _, ok := b["unshelve"].(map[string]interface{})["availability_zone"]; !ok { + b["unshelve"] = nil + } + + return b, err +} + +// Unshelve is the operation responsible for unshelve a Compute server. +func Unshelve(client *gophercloud.ServiceClient, id string, opts UnshelveOptsBuilder) (r UnshelveResult) { + b, err := opts.ToUnshelveMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(extensions.ActionURL(client, id), b, nil, nil) + return +} diff --git a/openstack/compute/v2/extensions/shelveunshelve/results.go b/openstack/compute/v2/extensions/shelveunshelve/results.go new file mode 100644 index 0000000000..7d2cefffe2 --- /dev/null +++ b/openstack/compute/v2/extensions/shelveunshelve/results.go @@ -0,0 +1,21 @@ +package shelveunshelve + +import "github.com/gophercloud/gophercloud" + +// ShelveResult is the response from a Shelve operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type ShelveResult struct { + gophercloud.ErrResult +} + +// ShelveOffloadResult is the response from a Shelve operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type ShelveOffloadResult struct { + gophercloud.ErrResult +} + +// UnshelveResult is the response from Stop operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type UnshelveResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures.go b/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures.go new file mode 100644 index 0000000000..0f13cee671 --- /dev/null +++ b/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures.go @@ -0,0 +1,50 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func mockShelveServerResponse(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, `{"shelve": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} + +func mockShelveOffloadServerResponse(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, `{"shelveOffload": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} + +func mockUnshelveServerResponseWithAvailabilityZone(t *testing.T, id string, az 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, fmt.Sprintf(`{ + "unshelve": { + "availability_zone": "%s" + } + }`, az)) + w.WriteHeader(http.StatusAccepted) + }) +} + +func mockUnshelveServerResponseNoAvailabilityZone(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, `{"unshelve": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go b/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go new file mode 100644 index 0000000000..e2a2f8a1e7 --- /dev/null +++ b/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go @@ -0,0 +1,58 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/shelveunshelve" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const serverID = "{serverId}" +const availabilityZone = "test-zone" + +func TestShelve(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockShelveServerResponse(t, serverID) + + err := shelveunshelve.Shelve(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestShelveOffload(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockShelveOffloadServerResponse(t, serverID) + + err := shelveunshelve.ShelveOffload(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUnshelveNoAvailabilityZone(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + unshelveOpts := shelveunshelve.UnshelveOpts{} + + mockUnshelveServerResponseNoAvailabilityZone(t, serverID) + + err := shelveunshelve.Unshelve(client.ServiceClient(), serverID, unshelveOpts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUnshelveWithAvailabilityZone(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + unshelveOpts := shelveunshelve.UnshelveOpts{ + AvailabilityZone: availabilityZone, + } + + mockUnshelveServerResponseWithAvailabilityZone(t, serverID, availabilityZone) + + err := shelveunshelve.Unshelve(client.ServiceClient(), serverID, unshelveOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/startstop/requests.go b/openstack/compute/v2/extensions/startstop/requests.go index 5b4f3f39dd..db55a52aa0 100644 --- a/openstack/compute/v2/extensions/startstop/requests.go +++ b/openstack/compute/v2/extensions/startstop/requests.go @@ -1,19 +1,18 @@ package startstop -import "github.com/gophercloud/gophercloud" - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("servers", id, "action") -} +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" +) // Start is the operation responsible for starting a Compute server. func Start(client *gophercloud.ServiceClient, id string) (r StartResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) return } // Stop is the operation responsible for stopping a Compute server. func Stop(client *gophercloud.ServiceClient, id string) (r StopResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/suspendresume/requests.go b/openstack/compute/v2/extensions/suspendresume/requests.go index 0abbe72121..c154488146 100644 --- a/openstack/compute/v2/extensions/suspendresume/requests.go +++ b/openstack/compute/v2/extensions/suspendresume/requests.go @@ -1,19 +1,18 @@ package suspendresume -import "github.com/gophercloud/gophercloud" - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("servers", id, "action") -} +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" +) // Suspend is the operation responsible for suspending a Compute server. func Suspend(client *gophercloud.ServiceClient, id string) (r SuspendResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) return } // Resume is the operation responsible for resuming a Compute server. func Resume(client *gophercloud.ServiceClient, id string) (r UnsuspendResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) + _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) return } diff --git a/openstack/compute/v2/extensions/rescueunrescue/urls.go b/openstack/compute/v2/extensions/urls.go similarity index 54% rename from openstack/compute/v2/extensions/rescueunrescue/urls.go rename to openstack/compute/v2/extensions/urls.go index fa5cd3735f..9d40e49507 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/urls.go +++ b/openstack/compute/v2/extensions/urls.go @@ -1,7 +1,7 @@ -package rescueunrescue +package extensions import "github.com/gophercloud/gophercloud" -func actionURL(client *gophercloud.ServiceClient, id string) string { +func ActionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") } From 5324e2b60471151cd3870df3b6e9a9c2d85d563e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Dec 2019 13:19:32 -0700 Subject: [PATCH 0922/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9cc1751a0..9d88cb4410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ 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) ## 0.7.0 (December 3, 2019) From f38b26d564ff71f84295aa2fa0fd302d39dc6f52 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Dec 2019 13:20:31 -0700 Subject: [PATCH 0923/2296] Update doc.go --- openstack/compute/v2/extensions/shelveunshelve/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/shelveunshelve/doc.go b/openstack/compute/v2/extensions/shelveunshelve/doc.go index a5274a1545..0d33f47fcd 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/doc.go +++ b/openstack/compute/v2/extensions/shelveunshelve/doc.go @@ -16,7 +16,7 @@ Example to Shelve, Shelve-offload and Unshelve a Server panic(err) } - err := shelveunshelve.Unshelve(computeClient, serverID).ExtractErr() + err := shelveunshelve.Unshelve(computeClient, serverID, nil).ExtractErr() if err != nil { panic(err) } From 8d733f5ddc5d6502d064731be61f82b81beb754b Mon Sep 17 00:00:00 2001 From: Thomas Hartland Date: Wed, 11 Dec 2019 20:23:01 +0000 Subject: [PATCH 0924/2296] ContainerInfra: nodegroups Get (#1774) * Add containerinfra nodegroups Get method * Add containerinfra nodegroups List method * Add containerinfra nodegroups Get/List tests * Add containerinfra nodegroups Get/List docs * Add containerinfra nodegroups Get/List acceptance tests --- .../containerinfra/v1/containerinfra.go | 27 +- .../containerinfra/v1/nodegroups_test.go | 73 ++++ openstack/containerinfra/v1/nodegroups/doc.go | 57 ++++ .../containerinfra/v1/nodegroups/requests.go | 72 ++++ .../containerinfra/v1/nodegroups/results.go | 80 +++++ .../v1/nodegroups/testing/fixtures.go | 316 ++++++++++++++++++ .../v1/nodegroups/testing/requests_test.go | 122 +++++++ .../containerinfra/v1/nodegroups/urls.go | 13 + 8 files changed, 755 insertions(+), 5 deletions(-) create mode 100644 acceptance/openstack/containerinfra/v1/nodegroups_test.go create mode 100644 openstack/containerinfra/v1/nodegroups/doc.go create mode 100644 openstack/containerinfra/v1/nodegroups/requests.go create mode 100644 openstack/containerinfra/v1/nodegroups/results.go create mode 100644 openstack/containerinfra/v1/nodegroups/testing/fixtures.go create mode 100644 openstack/containerinfra/v1/nodegroups/testing/requests_test.go create mode 100644 openstack/containerinfra/v1/nodegroups/urls.go diff --git a/acceptance/openstack/containerinfra/v1/containerinfra.go b/acceptance/openstack/containerinfra/v1/containerinfra.go index 26ac924494..796c0149ff 100644 --- a/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -17,20 +17,20 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -// CreateClusterTemplate will create a random cluster tempalte. An error will be returned if the -// cluster-template could not be created. -func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*clustertemplates.ClusterTemplate, error) { +// CreateClusterTemplateCOE will create a random cluster template for the specified orchestration engine. +// An error will be returned if the cluster template could not be created. +func CreateClusterTemplateCOE(t *testing.T, client *gophercloud.ServiceClient, coe string) (*clustertemplates.ClusterTemplate, error) { choices, err := clients.AcceptanceTestChoicesFromEnv() if err != nil { return nil, err } name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create cluster template: %s", name) + t.Logf("Attempting to create %s cluster template: %s", coe, name) boolFalse := false createOpts := clustertemplates.CreateOpts{ - COE: "swarm", + COE: coe, DNSNameServer: "8.8.8.8", DockerStorageDriver: "overlay2", ExternalNetworkID: choices.ExternalNetworkID, @@ -72,6 +72,18 @@ func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*cl return clusterTemplate, nil } +// CreateClusterTemplate will create a random swarm cluster template. +// An error will be returned if the cluster template could not be created. +func CreateClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*clustertemplates.ClusterTemplate, error) { + return CreateClusterTemplateCOE(t, client, "swarm") +} + +// CreateKubernetesClusterTemplate will create a random kubernetes cluster template. +// An error will be returned if the cluster template could not be created. +func CreateKubernetesClusterTemplate(t *testing.T, client *gophercloud.ServiceClient) (*clustertemplates.ClusterTemplate, error) { + return CreateClusterTemplateCOE(t, client, "kubernetes") +} + // DeleteClusterTemplate will delete a given cluster-template. A fatal error will occur if the // cluster-template could not be deleted. This works best as a deferred function. func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id string) { @@ -142,6 +154,11 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTempl return CreateClusterTimeout(t, client, clusterTemplateID, 300*time.Second) } +// CreateKubernetesCluster is the same as CreateCluster with a longer timeout necessary for creating a kubernetes cluster +func CreateKubernetesCluster(t *testing.T, client *gophercloud.ServiceClient, clusterTemplateID string) (string, error) { + return CreateClusterTimeout(t, client, clusterTemplateID, 900*time.Second) +} + func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete cluster: %s", id) diff --git a/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/acceptance/openstack/containerinfra/v1/nodegroups_test.go new file mode 100644 index 0000000000..27f2ff1c8e --- /dev/null +++ b/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -0,0 +1,73 @@ +// +build acceptance containerinfra + +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNodeGroupsCRUD(t *testing.T) { + // API not available until Magnum train + 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.NewContainerInfraV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.9" + + clusterTemplate, err := CreateKubernetesClusterTemplate(t, client) + th.AssertNoErr(t, err) + defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) + + clusterID, err := CreateKubernetesCluster(t, client, clusterTemplate.UUID) + th.AssertNoErr(t, err) + defer DeleteCluster(t, client, clusterID) + + t.Run("list", func(t *testing.T) { testNodeGroupsList(t, client, clusterID) }) + t.Run("listone-get", func(t *testing.T) { testNodeGroupGet(t, client, clusterID) }) +} + +func testNodeGroupsList(t *testing.T, client *gophercloud.ServiceClient, clusterID string) { + allPages, err := nodegroups.List(client, clusterID, nil).AllPages() + th.AssertNoErr(t, err) + + allNodeGroups, err := nodegroups.ExtractNodeGroups(allPages) + th.AssertNoErr(t, err) + + // By default two node groups should be created + th.AssertEquals(t, 2, len(allNodeGroups)) +} + +func testNodeGroupGet(t *testing.T, client *gophercloud.ServiceClient, clusterID string) { + listOpts := nodegroups.ListOpts{ + Role: "worker", + } + allPages, err := nodegroups.List(client, clusterID, listOpts).AllPages() + th.AssertNoErr(t, err) + + allNodeGroups, err := nodegroups.ExtractNodeGroups(allPages) + th.AssertNoErr(t, err) + + // Should be one worker node group + th.AssertEquals(t, 1, len(allNodeGroups)) + + ngID := allNodeGroups[0].UUID + + ng, err := nodegroups.Get(client, clusterID, ngID).Extract() + th.AssertNoErr(t, err) + + // Should have got the same node group as from the list + th.AssertEquals(t, ngID, ng.UUID) + th.AssertEquals(t, "worker", ng.Role) +} diff --git a/openstack/containerinfra/v1/nodegroups/doc.go b/openstack/containerinfra/v1/nodegroups/doc.go new file mode 100644 index 0000000000..4f3c84381f --- /dev/null +++ b/openstack/containerinfra/v1/nodegroups/doc.go @@ -0,0 +1,57 @@ +/* +Package nodegroups provides methods for interacting with the Magnum node group API. + +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) + } + + 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) + + +Example of Listing node groups: + + listOpts := nodegroup.ListOpts{ + Role: "worker", + } + + 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) + } +*/ +package nodegroups diff --git a/openstack/containerinfra/v1/nodegroups/requests.go b/openstack/containerinfra/v1/nodegroups/requests.go new file mode 100644 index 0000000000..be9eeb7b78 --- /dev/null +++ b/openstack/containerinfra/v1/nodegroups/requests.go @@ -0,0 +1,72 @@ +package nodegroups + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Get makes a request to the Magnum API to retrieve a node group +// with the given ID/name belonging to the given cluster. +// Use the Extract method of the returned GetResult to extract the +// node group from the result. +func Get(client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r GetResult) { + var response *http.Response + response, r.Err = client.Get(getURL(client, clusterID, nodeGroupID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + if r.Err == nil { + r.Header = response.Header + } + return +} + +type ListOptsBuilder interface { + ToNodeGroupsListQuery() (string, error) +} + +// ListOpts is used to filter and sort the node groups of a cluster +// when using List. +type ListOpts struct { + // Pagination marker for large data sets. (UUID field from node group). + Marker int `q:"marker"` + // Maximum number of resources to return in a single page. + Limit int `q:"limit"` + // Column to sort results by. Default: id. + SortKey string `q:"sort_key"` + // Direction to sort. "asc" or "desc". Default: asc. + SortDir string `q:"sort_dir"` + // List all nodegroups with the specified role. + Role string `q:"role"` +} + +func (opts ListOpts) ToNodeGroupsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request to the Magnum API to retrieve node groups +// belonging to the given cluster. The request can be modified to +// filter or sort the list using the options available in ListOpts. +// +// Use the AllPages method of the returned Pager to ensure that +// all node groups are returned (for example when using the Limit +// option to limit the number of node groups returned per page). +// +// Not all node group fields are returned in a list request. +// Only the fields UUID, Name, FlavorID, ImageID, +// NodeCount, Role, IsDefault, Status and StackID +// are returned, all other fields are omitted +// and will have their zero value when extracted. +func List(client *gophercloud.ServiceClient, clusterID string, opts ListOptsBuilder) pagination.Pager { + url := listURL(client, clusterID) + if opts != nil { + query, err := opts.ToNodeGroupsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return NodeGroupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/containerinfra/v1/nodegroups/results.go b/openstack/containerinfra/v1/nodegroups/results.go new file mode 100644 index 0000000000..a886df76ec --- /dev/null +++ b/openstack/containerinfra/v1/nodegroups/results.go @@ -0,0 +1,80 @@ +package nodegroups + +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +func (r commonResult) Extract() (*NodeGroup, error) { + var s NodeGroup + err := r.ExtractInto(&s) + return &s, err +} + +// GetResult is the response from a Get request. +// Use the Extract method to retrieve the NodeGroup itself. +type GetResult struct { + commonResult +} + +// NodeGroup is the API representation of a Magnum node group. +type NodeGroup struct { + ID int `json:"id"` + UUID string `json:"uuid"` + Name string `json:"name"` + ClusterID string `json:"cluster_id"` + ProjectID string `json:"project_id"` + DockerVolumeSize *int `json:"docker_volume_size"` + Labels map[string]string `json:"labels"` + Links []gophercloud.Link `json:"links"` + FlavorID string `json:"flavor_id"` + ImageID string `json:"image_id"` + NodeAddresses []string `json:"node_addresses"` + NodeCount int `json:"node_count"` + Role string `json:"role"` + MinNodeCount int `json:"min_node_count"` + MaxNodeCount *int `json:"max_node_count"` + IsDefault bool `json:"is_default"` + StackID string `json:"stack_id"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + Version string `json:"version"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type NodeGroupPage struct { + pagination.LinkedPageBase +} + +func (r NodeGroupPage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Next, nil +} + +func (r NodeGroupPage) IsEmpty() (bool, error) { + s, err := ExtractNodeGroups(r) + return len(s) == 0, err +} + +// ExtractNodeGroups takes a Page of node groups as returned from List +// or from AllPages and extracts it as a slice of NodeGroups. +func ExtractNodeGroups(r pagination.Page) ([]NodeGroup, error) { + var s struct { + NodeGroups []NodeGroup `json:"nodegroups"` + } + err := (r.(NodeGroupPage)).ExtractInto(&s) + return s.NodeGroups, err +} diff --git a/openstack/containerinfra/v1/nodegroups/testing/fixtures.go b/openstack/containerinfra/v1/nodegroups/testing/fixtures.go new file mode 100644 index 0000000000..f86d9d3b20 --- /dev/null +++ b/openstack/containerinfra/v1/nodegroups/testing/fixtures.go @@ -0,0 +1,316 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ( + clusterUUID = "bda75056-3a57-4ada-b943-658ac27beea0" + badClusterUUID = "252e2f37-d83e-4848-be39-eed1b41211ac" + + nodeGroup1UUID = "b2e581be-2eec-45b8-921a-c85fbc23aaa3" + nodeGroup2UUID = "2457febf-520f-4be3-abb9-96b892d7b5a0" + badNodeGroupUUID = "4973f3aa-40a2-4857-bf9e-c15faffb08c8" +) + +var ( + nodeGroup1Created, _ = time.Parse(time.RFC3339, "2019-10-18T14:03:37+00:00") + nodeGroup1Updated, _ = time.Parse(time.RFC3339, "2019-10-18T14:18:35+00:00") +) + +var expectedNodeGroup1 = nodegroups.NodeGroup{ + ID: 9, + UUID: nodeGroup1UUID, + Name: "default-master", + ClusterID: clusterUUID, + ProjectID: "e91d02d561374de6b49960a27b3f08d0", + DockerVolumeSize: nil, + Labels: map[string]string{ + "kube_tag": "v1.14.7", + }, + Links: []gophercloud.Link{ + { + Href: "http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/b2e581be-2eec-45b8-921a-c85fbc23aaa3", + Rel: "self", + }, + { + Href: "http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/b2e581be-2eec-45b8-921a-c85fbc23aaa3", + Rel: "bookmark", + }, + }, + FlavorID: "", + ImageID: "Fedora-AtomicHost-29-20190820.0.x86_64", + NodeAddresses: []string{"172.24.4.19"}, + NodeCount: 1, + Role: "master", + MinNodeCount: 1, + MaxNodeCount: nil, + IsDefault: true, + StackID: "3cd55bb0-1115-4838-8eca-cefc13f7a21b", + Status: "UPDATE_COMPLETE", + StatusReason: "Stack UPDATE completed successfully", + Version: "", + CreatedAt: nodeGroup1Created, + UpdatedAt: nodeGroup1Updated, +} + +func handleGetNodeGroupSuccess(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup1UUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodGet) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, nodeGroupGetResponse) + }) +} + +func handleGetNodeGroupNotFound(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+badNodeGroupUUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodGet) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + + fmt.Fprint(w, nodeGroupGetNotFoundResponse) + }) +} + +func handleGetNodeGroupClusterNotFound(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+badClusterUUID+"/nodegroups/"+badNodeGroupUUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodGet) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + + fmt.Fprint(w, nodeGroupGetClusterNotFoundResponse) + }) +} + +func handleListNodeGroupsSuccess(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodGet) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, nodeGroupListResponse) + }) +} + +func handleListNodeGroupsLimitSuccess(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodGet) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + r.ParseForm() + if marker, ok := r.Form["marker"]; !ok { + // No marker, this is the first request. + th.TestFormValues(t, r, map[string]string{"limit": "1"}) + fmt.Fprintf(w, nodeGroupListLimitResponse1, th.Endpoint()) + } else { + switch marker[0] { + case nodeGroup1UUID: + // Marker is the UUID of the first node group, return the second. + fmt.Fprintf(w, nodeGroupListLimitResponse2, th.Endpoint()) + case nodeGroup2UUID: + // Marker is the UUID of the second node group, there are no more to return. + fmt.Fprint(w, nodeGroupListLimitResponse3) + } + } + }) +} + +func handleListNodeGroupsClusterNotFound(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+badClusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodGet) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + + fmt.Fprint(w, nodeGroupListClusterNotFoundResponse) + }) +} + +var nodeGroupGetResponse = fmt.Sprintf(` +{ + "links":[ + { + "href":"http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/b2e581be-2eec-45b8-921a-c85fbc23aaa3", + "rel":"self" + }, + { + "href":"http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/b2e581be-2eec-45b8-921a-c85fbc23aaa3", + "rel":"bookmark" + } + ], + "labels":{ + "kube_tag":"v1.14.7" + }, + "updated_at":"2019-10-18T14:18:35+00:00", + "cluster_id":"%s", + "min_node_count":1, + "id":9, + "uuid":"%s", + "version":null, + "role":"master", + "node_count":1, + "project_id":"e91d02d561374de6b49960a27b3f08d0", + "status":"UPDATE_COMPLETE", + "docker_volume_size":null, + "max_node_count":null, + "is_default":true, + "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", + "node_addresses":[ + "172.24.4.19" + ], + "status_reason":"Stack UPDATE completed successfully", + "name":"default-master", + "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", + "created_at":"2019-10-18T14:03:37+00:00", + "flavor_id":null +}`, clusterUUID, nodeGroup1UUID) + +// nodeGroupGetNotFoundResponse is the returned error when there is a cluster with the requested ID but it does not have the requested node group. +var nodeGroupGetNotFoundResponse = fmt.Sprintf(` +{ + "errors":[ + { + "status":404, + "code":"client", + "links":[ + + ], + "title":"Nodegroup %s could not be found", + "request_id":"" + } + ] +}`, badNodeGroupUUID) + +// nodeGroupGetClusterNotFoundResponse is the returned error when there is no cluster with the requested ID. +var nodeGroupGetClusterNotFoundResponse = fmt.Sprintf(` +{ + "errors":[ + { + "status":404, + "code":"client", + "links":[ + + ], + "title":"Cluster %s could not be found", + "request_id":"" + } + ] +}`, badClusterUUID) + +var nodeGroupListResponse = fmt.Sprintf(` +{ + "nodegroups":[ + { + "status":"UPDATE_COMPLETE", + "is_default":true, + "uuid":"%s", + "max_node_count":null, + "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", + "min_node_count":1, + "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", + "role":"master", + "flavor_id":null, + "node_count":1, + "name":"default-master" + }, + { + "status":"UPDATE_COMPLETE", + "is_default":true, + "uuid":"%s", + "max_node_count":null, + "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", + "min_node_count":1, + "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", + "role":"worker", + "flavor_id":"m1.small", + "node_count":1, + "name":"default-worker" + } + ] +}`, nodeGroup1UUID, nodeGroup2UUID) + +// nodeGroupListLimitResponse1 is the first response when requesting the list of node groups with a limit of 1. +// It returns the URL for the next page with the marker of the first node group. +var nodeGroupListLimitResponse1 = fmt.Sprintf(` +{ + "nodegroups":[ + { + "status":"UPDATE_COMPLETE", + "is_default":true, + "name":"default-master", + "max_node_count":null, + "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", + "min_node_count":1, + "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", + "cluster_id":"bda75056-3a57-4ada-b943-658ac27beea0", + "flavor_id":null, + "role":"master", + "node_count":1, + "uuid":"%s" + } + ], + "next":"%%s/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups?sort_key=id&sort_dir=asc&limit=1&marker=%s" +}`, nodeGroup1UUID, nodeGroup1UUID) + +// nodeGroupListLimitResponse2 is returned when making a request to the URL given by "next" in the first response. +var nodeGroupListLimitResponse2 = fmt.Sprintf(` +{ + "nodegroups":[ + { + "status":"UPDATE_COMPLETE", + "is_default":true, + "name":"default-worker", + "max_node_count":null, + "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", + "min_node_count":1, + "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", + "cluster_id":"bda75056-3a57-4ada-b943-658ac27beea0", + "flavor_id":"m1.small", + "role":"worker", + "node_count":1, + "uuid":"%s" + } + ], + "next":"%%s/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups?sort_key=id&sort_dir=asc&limit=1&marker=%s" +}`, nodeGroup2UUID, nodeGroup2UUID) + +// nodeGroupListLimitResponse3 is the response when listing node groups using a marker and all node groups have already been returned. +var nodeGroupListLimitResponse3 = `{"nodegroups": []}` + +// nodeGroupListClusterNotFoundResponse is the error returned when the list operation fails because there is no cluster with the requested ID. +var nodeGroupListClusterNotFoundResponse = fmt.Sprintf(` +{ + "errors":[ + { + "status":404, + "code":"client", + "links":[ + + ], + "title":"Cluster %s could not be found", + "request_id":"" + } + ] +}`, badClusterUUID) diff --git a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go new file mode 100644 index 0000000000..3315390336 --- /dev/null +++ b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go @@ -0,0 +1,122 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +// TestGetNodeGroupSuccess gets a node group successfully. +func TestGetNodeGroupSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleGetNodeGroupSuccess(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + ng, err := nodegroups.Get(sc, clusterUUID, nodeGroup1UUID).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expectedNodeGroup1, *ng) +} + +// TestGetNodeGroupNotFound tries to get a node group which does not exist. +func TestGetNodeGroupNotFound(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleGetNodeGroupNotFound(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + _, err := nodegroups.Get(sc, clusterUUID, badNodeGroupUUID).Extract() + th.AssertEquals(t, true, err != nil) + + _, isNotFound := err.(gophercloud.ErrDefault404) + th.AssertEquals(t, true, isNotFound) +} + +// TestGetNodeGroupClusterNotFound tries to get a node group in +// a cluster which does not exist. +func TestGetNodeGroupClusterNotFound(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleGetNodeGroupClusterNotFound(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + _, err := nodegroups.Get(sc, badClusterUUID, badNodeGroupUUID).Extract() + th.AssertEquals(t, true, err != nil) + + _, isNotFound := err.(gophercloud.ErrDefault404) + th.AssertEquals(t, true, isNotFound) +} + +// TestListNodeGroupsSuccess lists the node groups of a cluster successfully. +func TestListNodeGroupsSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleListNodeGroupsSuccess(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + ngPages, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + ngs, err := nodegroups.ExtractNodeGroups(ngPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, 2, len(ngs)) + th.AssertEquals(t, nodeGroup1UUID, ngs[0].UUID) + th.AssertEquals(t, nodeGroup2UUID, ngs[1].UUID) +} + +// TestListNodeGroupsLimitSuccess tests listing node groups +// with each returned page limited to one node group and +// also giving a URL to get the next page. +func TestListNodeGroupsLimitSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleListNodeGroupsLimitSuccess(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + listOpts := nodegroups.ListOpts{Limit: 1} + ngPages, err := nodegroups.List(sc, clusterUUID, listOpts).AllPages() + th.AssertNoErr(t, err) + + ngs, err := nodegroups.ExtractNodeGroups(ngPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, 2, len(ngs)) + th.AssertEquals(t, nodeGroup1UUID, ngs[0].UUID) + th.AssertEquals(t, nodeGroup2UUID, ngs[1].UUID) +} + +// TestListNodeGroupsClusterNotFound tries to list node groups +// of a cluster which does not exist. +func TestListNodeGroupsClusterNotFound(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleListNodeGroupsClusterNotFound(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + _, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages() + th.AssertEquals(t, true, err != nil) + + _, isNotFound := err.(gophercloud.ErrDefault404) + th.AssertEquals(t, true, isNotFound) +} diff --git a/openstack/containerinfra/v1/nodegroups/urls.go b/openstack/containerinfra/v1/nodegroups/urls.go new file mode 100644 index 0000000000..5054f730be --- /dev/null +++ b/openstack/containerinfra/v1/nodegroups/urls.go @@ -0,0 +1,13 @@ +package nodegroups + +import ( + "github.com/gophercloud/gophercloud" +) + +func getURL(c *gophercloud.ServiceClient, clusterID, nodeGroupID string) string { + return c.ServiceURL("clusters", clusterID, "nodegroups", nodeGroupID) +} + +func listURL(c *gophercloud.ServiceClient, clusterID string) string { + return c.ServiceURL("clusters", clusterID, "nodegroups") +} From f940f50ff1f774107ecff13425265c5361682f74 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Dec 2019 13:24:11 -0700 Subject: [PATCH 0925/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d88cb4410..1dc0a60e56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ IMPROVEMENTS * 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) ## 0.7.0 (December 3, 2019) From 69c8f86373d2f6635398ea9fb2c9fa947f1f6e8d Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Sun, 15 Dec 2019 01:03:36 +0800 Subject: [PATCH 0926/2296] [WIP]Switch to install devstack on ubuntu 18.04 (#1795) * Switch to install devstack on ubuntu 18.04 This change switch to install devstack on ubuntu bionic, because openstack/devstack has dropped xenial support. Related-Bug: theopenlab/openlab#412 * Update shares_test.go --- .zuul.yaml | 2 ++ .zuul/playbooks/gophercloud-acceptance-test/run.yaml | 2 +- acceptance/openstack/sharedfilesystems/v2/shares_test.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 135e3b203a..f1b824e237 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -12,6 +12,7 @@ description: | Run gophercloud acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml + nodeset: ubuntu-bionic - job: name: gophercloud-acceptance-test-ironic @@ -19,6 +20,7 @@ description: | Run gophercloud ironic acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml + nodeset: ubuntu-bionic - job: name: gophercloud-acceptance-test-stein diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index 26ed039213..ac870d0a42 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -19,7 +19,7 @@ set -o pipefail set -x echo $(export |grep OS_BRANCH) - + export OS_DEBUG=1 go get ./... || true ./script/acceptancetest -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 28ebc8e969..6ee8a94d26 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -118,7 +118,7 @@ func TestGrantAndRevokeAccess(t *testing.T) { if err != nil { t.Fatalf("Unable to create a shared file system client: %v", err) } - client.Microversion = "2.7" + client.Microversion = "2.49" share, err := CreateShare(t, client) if err != nil { From ee236b613d68048df9296e106c532a8012aa4505 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 15 Dec 2019 11:20:51 -0700 Subject: [PATCH 0927/2296] Acc Tests: Disable Shared Filesystem access tests (#1802) --- acceptance/openstack/sharedfilesystems/v2/shares_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 6ee8a94d26..8a7ea999fb 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -111,6 +111,7 @@ func TestShareListDetail(t *testing.T) { } func TestGrantAndRevokeAccess(t *testing.T) { + t.Skip("Currently failing in OpenLab") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") @@ -140,6 +141,7 @@ func TestGrantAndRevokeAccess(t *testing.T) { } func TestListAccessRights(t *testing.T) { + t.Skip("Currently failing in OpenLab") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") From a06ca8757f510f228c85598a663f2a1ea361240c Mon Sep 17 00:00:00 2001 From: Thomas Hartland Date: Sun, 15 Dec 2019 18:25:24 +0000 Subject: [PATCH 0928/2296] Containerinfra: nodegroups Create/Update/Delete (#1800) * Add containerinfra nodegroups Create method * Add containerinfra nodegroups Create tests * Add containerinfra nodegroups Create docs * Make whitespace consistent in node groups documentation * Add containerinfra nodegroups Create acceptance test * Add containerinfra nodegroups Update method * Add containerinfra nodegroups Update tests * Add containerinfra nodegroups Update docs * Add containerinfra nodegroups Delete method * Add containerinfra nodegroups Delete tests * Add containerinfra nodegroups Delete docs * Add containerinfra nodegroups Update acceptance test * Add containerinfra nodegroups Delete acceptance test --- .../containerinfra/v1/nodegroups_test.go | 99 +++++ openstack/containerinfra/v1/nodegroups/doc.go | 71 ++- .../containerinfra/v1/nodegroups/requests.go | 109 +++++ .../containerinfra/v1/nodegroups/results.go | 18 + .../v1/nodegroups/testing/fixtures.go | 414 ++++++++++++++++++ .../v1/nodegroups/testing/requests_test.go | 243 ++++++++++ .../containerinfra/v1/nodegroups/urls.go | 12 + 7 files changed, 961 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/acceptance/openstack/containerinfra/v1/nodegroups_test.go index 27f2ff1c8e..f5117f1c15 100644 --- a/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -3,10 +3,13 @@ package v1 import ( + "fmt" "testing" + "time" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -34,8 +37,26 @@ func TestNodeGroupsCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCluster(t, client, clusterID) + var nodeGroupID string + t.Run("list", func(t *testing.T) { testNodeGroupsList(t, client, clusterID) }) t.Run("listone-get", func(t *testing.T) { testNodeGroupGet(t, client, clusterID) }) + t.Run("create", func(t *testing.T) { nodeGroupID = testNodeGroupCreate(t, client, clusterID) }) + + t.Logf("Created nodegroup: %s", nodeGroupID) + + // Wait for the node group to finish creating + err = tools.WaitForTimeout(func() (bool, error) { + ng, err := nodegroups.Get(client, clusterID, nodeGroupID).Extract() + if err != nil { + return false, fmt.Errorf("error waiting for node group to create: %v", err) + } + return (ng.Status == "CREATE_COMPLETE"), nil + }, 900*time.Second) + th.AssertNoErr(t, err) + + t.Run("update", func(t *testing.T) { testNodeGroupUpdate(t, client, clusterID, nodeGroupID) }) + t.Run("delete", func(t *testing.T) { testNodeGroupDelete(t, client, clusterID, nodeGroupID) }) } func testNodeGroupsList(t *testing.T, client *gophercloud.ServiceClient, clusterID string) { @@ -71,3 +92,81 @@ func testNodeGroupGet(t *testing.T, client *gophercloud.ServiceClient, clusterID th.AssertEquals(t, ngID, ng.UUID) th.AssertEquals(t, "worker", ng.Role) } + +func testNodeGroupCreate(t *testing.T, client *gophercloud.ServiceClient, clusterID string) string { + name := tools.RandomString("test-ng-", 8) + + // have to create two nodes for the Update test (can't set minimum above actual node count) + two := 2 + createOpts := nodegroups.CreateOpts{ + Name: name, + NodeCount: &two, + } + + ng, err := nodegroups.Create(client, clusterID, createOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, name, ng.Name) + + return ng.UUID +} + +func testNodeGroupUpdate(t *testing.T, client *gophercloud.ServiceClient, clusterID, nodeGroupID string) { + // Node group starts with min=1, max=unset + // Set min, then set max, then set both + + updateOpts := []nodegroups.UpdateOptsBuilder{ + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/min_node_count", + Value: 2, + }, + } + ng, err := nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, 2, ng.MinNodeCount) + + updateOpts = []nodegroups.UpdateOptsBuilder{ + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/max_node_count", + Value: 5, + }, + } + ng, err = nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, false, ng.MaxNodeCount == nil) + th.AssertEquals(t, 5, *ng.MaxNodeCount) + + updateOpts = []nodegroups.UpdateOptsBuilder{ + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/min_node_count", + Value: 1, + }, + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/max_node_count", + Value: 3, + }, + } + ng, err = nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, false, ng.MaxNodeCount == nil) + th.AssertEquals(t, 1, ng.MinNodeCount) + th.AssertEquals(t, 3, *ng.MaxNodeCount) +} + +func testNodeGroupDelete(t *testing.T, client *gophercloud.ServiceClient, clusterID, nodeGroupID string) { + err := nodegroups.Delete(client, clusterID, nodeGroupID).ExtractErr() + th.AssertNoErr(t, err) + + // Wait for the node group to be deleted + err = tools.WaitFor(func() (bool, error) { + _, err := nodegroups.Get(client, clusterID, nodeGroupID).Extract() + if _, ok := err.(gophercloud.ErrDefault404); ok { + return true, nil + } + return false, nil + }) + th.AssertNoErr(t, err) +} diff --git a/openstack/containerinfra/v1/nodegroups/doc.go b/openstack/containerinfra/v1/nodegroups/doc.go index 4f3c84381f..c354de396c 100644 --- a/openstack/containerinfra/v1/nodegroups/doc.go +++ b/openstack/containerinfra/v1/nodegroups/doc.go @@ -29,7 +29,7 @@ Example of Getting a node group: ng, err := nodegroups.Get(client, clusterUUID, nodeGroupUUID).Extract() if err != nil { - panic(err) + panic(err) } fmt.Printf("%#v\n", ng) @@ -37,21 +37,82 @@ Example of Getting a node group: Example of Listing node groups: listOpts := nodegroup.ListOpts{ - Role: "worker", + Role: "worker", } allPages, err := nodegroups.List(client, clusterUUID, listOpts).AllPages() if err != nil { - panic(err) + panic(err) } ngs, err := nodegroups.ExtractNodeGroups(allPages) if err != nil { - panic(err) + panic(err) } for _, ng := range ngs { - fmt.Printf("%#v\n", ng) + 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) + } + + // Add the new label + labels := cluster.Labels + labels["availability_zone"] = "A" + + maxNodes := 5 + createOpts := nodegroups.CreateOpts{ + Name: "new-nodegroup", + MinNodeCount: 2, + MaxNodeCount: &maxNodes, + Labels: labels, + } + + 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, + }, + } + + 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) + } */ package nodegroups diff --git a/openstack/containerinfra/v1/nodegroups/requests.go b/openstack/containerinfra/v1/nodegroups/requests.go index be9eeb7b78..56040ad61a 100644 --- a/openstack/containerinfra/v1/nodegroups/requests.go +++ b/openstack/containerinfra/v1/nodegroups/requests.go @@ -70,3 +70,112 @@ func List(client *gophercloud.ServiceClient, clusterID string, opts ListOptsBuil return NodeGroupPage{pagination.LinkedPageBase{PageResult: r}} }) } + +type CreateOptsBuilder interface { + ToNodeGroupCreateMap() (map[string]interface{}, error) +} + +// CreateOpts is used to set available fields upon node group creation. +// +// If unset, some fields have defaults or will inherit from the cluster value. +type CreateOpts struct { + Name string `json:"name" required:"true"` + DockerVolumeSize *int `json:"docker_volume_size,omitempty"` + // Labels will default to the cluster labels if unset. + Labels map[string]string `json:"labels,omitempty"` + NodeCount *int `json:"node_count,omitempty"` + MinNodeCount int `json:"min_node_count,omitempty"` + // MaxNodeCount can be left unset for no maximum node count. + MaxNodeCount *int `json:"max_node_count,omitempty"` + // Role defaults to "worker" if unset. + Role string `json:"role,omitempty"` + // 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"` +} + +func (opts CreateOpts) ToNodeGroupCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create makes a request to the Magnum API to create a node group +// for the the given cluster. +// Use the Extract method of the returned CreateResult to extract the +// returned node group. +func Create(client *gophercloud.ServiceClient, clusterID string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToNodeGroupCreateMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + result, r.Err = client.Post(createURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + + if r.Err == nil { + r.Header = result.Header + } + + return +} + +type UpdateOptsBuilder interface { + ToResourceUpdateMap() (map[string]interface{}, error) +} + +type UpdateOp string + +const ( + AddOp UpdateOp = "add" + RemoveOp UpdateOp = "remove" + ReplaceOp UpdateOp = "replace" +) + +// UpdateOpts is used to define the action taken when updating a node group. +// +// Valid Ops are "add", "remove", "replace" +// Valid Paths are "/min_node_count" and "/max_node_count" +type UpdateOpts struct { + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value interface{} `json:"value,omitempty"` +} + +func (opts UpdateOpts) ToResourceUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update makes a request to the Magnum API to update a field of +// the given node group belonging to the given cluster. More than +// one UpdateOpts can be passed at a time. +// Use the Extract method of the returned UpdateResult to extract the +// updated node group from the result. +func Update(client *gophercloud.ServiceClient, clusterID string, nodeGroupID string, opts []UpdateOptsBuilder) (r UpdateResult) { + var o []map[string]interface{} + for _, opt := range opts { + b, err := opt.ToResourceUpdateMap() + if err != nil { + r.Err = err + return + } + o = append(o, b) + } + + var result *http.Response + result, r.Err = client.Patch(updateURL(client, clusterID, nodeGroupID), o, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + + if r.Err == nil { + r.Header = result.Header + } + + return +} + +// Delete makes a request to the Magnum API to delete a node group. +func Delete(client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r DeleteResult) { + var result *http.Response + result, r.Err = client.Delete(deleteURL(client, clusterID, nodeGroupID), nil) + r.Header = result.Header + return +} diff --git a/openstack/containerinfra/v1/nodegroups/results.go b/openstack/containerinfra/v1/nodegroups/results.go index a886df76ec..994c9c8fd2 100644 --- a/openstack/containerinfra/v1/nodegroups/results.go +++ b/openstack/containerinfra/v1/nodegroups/results.go @@ -23,6 +23,24 @@ type GetResult struct { commonResult } +// CreateResult is the response from a Create request. +// Use the Extract method to retrieve the created node group. +type CreateResult struct { + commonResult +} + +// UpdateResult is the response from an Update request. +// Use the Extract method to retrieve the updated node group. +type UpdateResult struct { + commonResult +} + +// DeleteResult is the response from a Delete request. +// Use the ExtractErr method to extract the error from the result. +type DeleteResult struct { + gophercloud.ErrResult +} + // NodeGroup is the API representation of a Magnum node group. type NodeGroup struct { ID int `json:"id"` diff --git a/openstack/containerinfra/v1/nodegroups/testing/fixtures.go b/openstack/containerinfra/v1/nodegroups/testing/fixtures.go index f86d9d3b20..0cb13f7ab7 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/fixtures.go +++ b/openstack/containerinfra/v1/nodegroups/testing/fixtures.go @@ -24,6 +24,9 @@ const ( var ( nodeGroup1Created, _ = time.Parse(time.RFC3339, "2019-10-18T14:03:37+00:00") nodeGroup1Updated, _ = time.Parse(time.RFC3339, "2019-10-18T14:18:35+00:00") + + nodeGroup2Created, _ = time.Parse(time.RFC3339, "2019-10-18T14:03:37+00:00") + nodeGroup2Updated, _ = time.Parse(time.RFC3339, "2019-10-18T14:18:36+00:00") ) var expectedNodeGroup1 = nodegroups.NodeGroup{ @@ -62,6 +65,66 @@ var expectedNodeGroup1 = nodegroups.NodeGroup{ UpdatedAt: nodeGroup1Updated, } +var expectedCreatedNodeGroup = nodegroups.NodeGroup{ + UUID: "12542dd8-9588-42a7-a2ff-06f49049920c", + Name: "test-ng", + ClusterID: clusterUUID, + ProjectID: "e91d02d561374de6b49960a27b3f08d0", + Labels: map[string]string{ + "kube_tag": "v1.14.7", + }, + Links: []gophercloud.Link{ + { + Href: "http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/12542dd8-9588-42a7-a2ff-06f49049920c", + Rel: "self", + }, + { + Href: "http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/12542dd8-9588-42a7-a2ff-06f49049920c", + Rel: "bookmark", + }, + }, + FlavorID: "m1.small", + ImageID: "Fedora-AtomicHost-29-20190820.0.x86_64", + NodeCount: 1, + MinNodeCount: 1, + Role: "worker", +} + +var maxNodesThree = 3 +var expectedUpdatedNodeGroup = nodegroups.NodeGroup{ + ID: 10, + UUID: nodeGroup2UUID, + Name: "default-worker", + ClusterID: clusterUUID, + ProjectID: "e91d02d561374de6b49960a27b3f08d0", + Labels: map[string]string{ + "kube_tag": "v1.14.7", + }, + Links: []gophercloud.Link{ + { + Href: "http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/2457febf-520f-4be3-abb9-96b892d7b5a0", + Rel: "self", + }, + { + Href: "http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/2457febf-520f-4be3-abb9-96b892d7b5a0", + Rel: "bookmark", + }, + }, + FlavorID: "m1.small", + ImageID: "Fedora-AtomicHost-29-20190820.0.x86_64", + NodeAddresses: []string{"172.24.4.17"}, + NodeCount: 1, + MinNodeCount: 1, + MaxNodeCount: &maxNodesThree, + IsDefault: true, + Role: "worker", + StackID: "3cd55bb0-1115-4838-8eca-cefc13f7a21b", + Status: "UPDATE_COMPLETE", + StatusReason: "Stack UPDATE completed successfully", + CreatedAt: nodeGroup2Created, + UpdatedAt: nodeGroup2Updated, +} + func handleGetNodeGroupSuccess(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup1UUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, http.MethodGet) @@ -148,6 +211,147 @@ func handleListNodeGroupsClusterNotFound(t *testing.T) { }) } +func handleCreateNodeGroupSuccess(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodPost) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, nodeGroupCreateResponse) + }) +} + +func handleCreateNodeGroupDuplicate(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodPost) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusConflict) + + fmt.Fprintf(w, nodeGroupCreateDuplicateResponse) + }) +} + +func handleCreateNodeGroupMaster(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodPost) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + + fmt.Fprintf(w, nodeGroupCreateMasterResponse) + }) +} + +func handleCreateNodeGroupBadSizes(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodPost) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusConflict) + + fmt.Fprintf(w, nodeGroupCreateBadSizesResponse) + }) +} + +func handleUpdateNodeGroupSuccess(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodPatch) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, nodeGroupUpdateResponse) + }) +} + +func handleUpdateNodeGroupInternal(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodPatch) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + + fmt.Fprintf(w, nodeGroupUpdateInternalResponse) + }) +} + +func handleUpdateNodeGroupBadField(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodPatch) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + + fmt.Fprintf(w, nodeGroupUpdateBadFieldResponse) + }) +} + +func handleUpdateNodeGroupBadMin(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodPatch) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusConflict) + + fmt.Fprintf(w, nodeGroupUpdateBadMinResponse) + }) +} + +func handleDeleteNodeGroupSuccess(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodDelete) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +func handleDeleteNodeGroupNotFound(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+badNodeGroupUUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodDelete) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + + fmt.Fprintf(w, nodeGroupDeleteNotFoundResponse) + }) +} + +func handleDeleteNodeGroupClusterNotFound(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+badClusterUUID+"/nodegroups/"+badNodeGroupUUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodDelete) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + + fmt.Fprintf(w, nodeGroupDeleteClusterNotFoundResponse) + }) +} + +func handleDeleteNodeGroupDefault(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/nodegroups/"+nodeGroup2UUID, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, http.MethodDelete) + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + + fmt.Fprintf(w, nodeGroupDeleteDefaultResponse) + }) +} + var nodeGroupGetResponse = fmt.Sprintf(` { "links":[ @@ -314,3 +518,213 @@ var nodeGroupListClusterNotFoundResponse = fmt.Sprintf(` } ] }`, badClusterUUID) + +var nodeGroupCreateResponse = fmt.Sprintf(` +{ + "uuid":"12542dd8-9588-42a7-a2ff-06f49049920c", + "links":[ + { + "href":"http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/12542dd8-9588-42a7-a2ff-06f49049920c", + "rel":"self" + }, + { + "href":"http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/12542dd8-9588-42a7-a2ff-06f49049920c", + "rel":"bookmark" + } + ], + "max_node_count":null, + "labels":{ + "kube_tag":"v1.14.7" + }, + "min_node_count":1, + "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", + "cluster_id":"%s", + "flavor_id":"m1.small", + "role":"worker", + "node_count":1, + "project_id":"e91d02d561374de6b49960a27b3f08d0", + "name":"test-ng" +}`, clusterUUID) + +var nodeGroupCreateDuplicateResponse = ` +{ + "errors":[ + { + "status":409, + "code":"client", + "links":[ + + ], + "title":"A node group with name default-worker already exists in the cluster kube", + "detail":"A node group with name default-worker already exists in the cluster kube.", + "request_id":"" + } + ] +}` + +var nodeGroupCreateMasterResponse = ` +{ + "errors":[ + { + "status":400, + "code":"client", + "links":[ + + ], + "title":"Creating master nodegroups is currently not supported", + "detail":"Creating master nodegroups is currently not supported.", + "request_id":"" + } + ] +}` + +var nodeGroupCreateBadSizesResponse = ` +{ + "errors":[ + { + "status":409, + "code":"client", + "links":[ + + ], + "title":"max_node_count for new-ng is invalid (min_node_count (5) should be less or equal to max_node_count (3))", + "detail":"max_node_count for new-ng is invalid (min_node_count (5) should be less or equal to max_node_count (3)).", + "request_id":"" + } + ] +}` + +var nodeGroupUpdateResponse = fmt.Sprintf(` +{ + "links":[ + { + "href":"http://123.456.789.0:9511/v1/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/2457febf-520f-4be3-abb9-96b892d7b5a0", + "rel":"self" + }, + { + "href":"http://123.456.789.0:9511/clusters/bda75056-3a57-4ada-b943-658ac27beea0/nodegroups/2457febf-520f-4be3-abb9-96b892d7b5a0", + "rel":"bookmark" + } + ], + "labels":{ + "kube_tag":"v1.14.7" + }, + "updated_at":"2019-10-18T14:18:36+00:00", + "cluster_id":"%s", + "min_node_count":1, + "id":10, + "uuid":"%s", + "version":null, + "role":"worker", + "node_count":1, + "project_id":"e91d02d561374de6b49960a27b3f08d0", + "status":"UPDATE_COMPLETE", + "docker_volume_size":null, + "max_node_count":3, + "is_default":true, + "image_id":"Fedora-AtomicHost-29-20190820.0.x86_64", + "node_addresses":[ + "172.24.4.17" + ], + "status_reason":"Stack UPDATE completed successfully", + "name":"default-worker", + "stack_id":"3cd55bb0-1115-4838-8eca-cefc13f7a21b", + "created_at":"2019-10-18T14:03:37+00:00", + "flavor_id":"m1.small" +}`, clusterUUID, nodeGroup2UUID) + +var nodeGroupUpdateInternalResponse = ` +{ + "errors":[ + { + "status":400, + "code":"client", + "links":[ + + ], + "title":"'/name' is an internal attribute and can not be updated", + "detail":"'/name' is an internal attribute and can not be updated", + "request_id":"" + } + ] +}` + +var nodeGroupUpdateBadFieldResponse = ` +{ + "errors":[ + { + "status":400, + "code":"client", + "links":[ + + ], + "title":"Couldn't apply patch '[{'path': '/bad_field', 'value': u'abc123', 'op': u'replace'}]'", + "detail":"Couldn't apply patch '[{'path': '/bad_field', 'value': u'abc123', 'op': u'replace'}]'. Reason: can't replace non-existent object 'bad_field'", + "request_id":"" + } + ] +}` + +var nodeGroupUpdateBadMinResponse = ` +{ + "errors":[ + { + "status":409, + "code":"client", + "links":[ + + ], + "title":"max_node_count for test-ng is invalid (min_node_count (5) should be less or equal to max_node_count (3))", + "detail":"max_node_count for test-ng is invalid (min_node_count (5) should be less or equal to max_node_count (3)).", + "request_id":"" + } + ] +}` + +var nodeGroupDeleteNotFoundResponse = fmt.Sprintf(` +{ + "errors":[ + { + "status":404, + "code":"client", + "links":[ + + ], + "title":"Nodegroup %s could not be found", + "detail":"Nodegroup %s could not be found.\nTraceback (most recent call last):\n\n File \"/opt/stack/magnum/magnum/conductor/handlers/indirection_api.py\", line 33, in _object_dispatch\n return getattr(target, method)(context, *args, **kwargs)\n\n File \"/usr/local/lib/python2.7/dist-packages/oslo_versionedobjects/base.py\", line 184, in wrapper\n result = fn(cls, context, *args, **kwargs)\n\n File \"/opt/stack/magnum/magnum/objects/nodegroup.py\", line 83, in get\n return cls.get_by_uuid(context, cluster_id, nodegroup_id)\n\n File \"/usr/local/lib/python2.7/dist-packages/oslo_versionedobjects/base.py\", line 184, in wrapper\n result = fn(cls, context, *args, **kwargs)\n\n File \"/opt/stack/magnum/magnum/objects/nodegroup.py\", line 109, in get_by_uuid\n db_nodegroup = cls.dbapi.get_nodegroup_by_uuid(context, cluster, uuid)\n\n File \"/opt/stack/magnum/magnum/db/sqlalchemy/api.py\", line 866, in get_nodegroup_by_uuid\n raise exception.NodeGroupNotFound(nodegroup=nodegroup_uuid)\n\nNodeGroupNotFound: Nodegroup %s could not be found.\n", + "request_id":"" + } + ] +}`, badNodeGroupUUID, badNodeGroupUUID, badNodeGroupUUID) + +var nodeGroupDeleteClusterNotFoundResponse = fmt.Sprintf(` +{ + "errors":[ + { + "status":404, + "code":"client", + "links":[ + + ], + "title":"Cluster %s could not be found", + "detail":"Cluster %s could not be found.\nTraceback (most recent call last):\n\n File \"/opt/stack/magnum/magnum/conductor/handlers/indirection_api.py\", line 33, in _object_dispatch\n return getattr(target, method)(context, *args, **kwargs)\n\n File \"/usr/local/lib/python2.7/dist-packages/oslo_versionedobjects/base.py\", line 184, in wrapper\n result = fn(cls, context, *args, **kwargs)\n\n File \"/opt/stack/magnum/magnum/objects/cluster.py\", line 198, in get_by_uuid\n db_cluster = cls.dbapi.get_cluster_by_uuid(context, uuid)\n\n File \"/opt/stack/magnum/magnum/db/sqlalchemy/api.py\", line 238, in get_cluster_by_uuid\n raise exception.ClusterNotFound(cluster=cluster_uuid)\n\nClusterNotFound: Cluster %s could not be found.\n", + "request_id":"" + } + ] +}`, badClusterUUID, badClusterUUID, badClusterUUID) + +var nodeGroupDeleteDefaultResponse = ` +{ + "errors":[ + { + "status":400, + "code":"client", + "links":[ + + ], + "title":"Deleting a default nodegroup is not supported", + "detail":"Deleting a default nodegroup is not supported.", + "request_id":"" + } + ] +}` diff --git a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go index 3315390336..3a758f7b47 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go +++ b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go @@ -120,3 +120,246 @@ func TestListNodeGroupsClusterNotFound(t *testing.T) { _, isNotFound := err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, isNotFound) } + +// TestCreateNodeGroupSuccess creates a node group successfully. +func TestCreateNodeGroupSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleCreateNodeGroupSuccess(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + createOpts := nodegroups.CreateOpts{ + Name: "test-ng", + } + + ng, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expectedCreatedNodeGroup, *ng) +} + +// TestCreateNodeGroupDuplicate creates a node group with +// the same name as an existing one. +func TestCreateNodeGroupDuplicate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleCreateNodeGroupDuplicate(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + createOpts := nodegroups.CreateOpts{ + Name: "default-worker", + } + + _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() + th.AssertEquals(t, true, err != nil) + _, isNotAccepted := err.(gophercloud.ErrDefault409) + th.AssertEquals(t, true, isNotAccepted) +} + +// TestCreateNodeGroupMaster creates a node group with +// role=master which is not allowed. +func TestCreateNodeGroupMaster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleCreateNodeGroupMaster(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + createOpts := nodegroups.CreateOpts{ + Name: "new-ng", + Role: "master", + } + + _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() + th.AssertEquals(t, true, err != nil) + _, isBadRequest := err.(gophercloud.ErrDefault400) + th.AssertEquals(t, true, isBadRequest) +} + +// TestCreateNodeGroupBadSizes creates a node group with +// min_nodes greater than max_nodes. +func TestCreateNodeGroupBadSizes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleCreateNodeGroupBadSizes(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + maxNodes := 3 + createOpts := nodegroups.CreateOpts{ + Name: "default-worker", + MinNodeCount: 5, + MaxNodeCount: &maxNodes, + } + + _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() + th.AssertEquals(t, true, err != nil) + _, isNotAccepted := err.(gophercloud.ErrDefault409) + th.AssertEquals(t, true, isNotAccepted) +} + +// TestUpdateNodeGroupSuccess updates a node group successfully. +func TestUpdateNodeGroupSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleUpdateNodeGroupSuccess(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + updateOpts := []nodegroups.UpdateOptsBuilder{ + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/max_node_count", + Value: 3, + }, + } + + ng, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expectedUpdatedNodeGroup, *ng) +} + +// TestUpdateNodeGroupInternal tries to update an internal +// property of the node group. +func TestUpdateNodeGroupInternal(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleUpdateNodeGroupInternal(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + updateOpts := []nodegroups.UpdateOptsBuilder{ + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/name", + Value: "newname", + }, + } + + _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() + th.AssertEquals(t, true, err != nil) + _, isBadRequest := err.(gophercloud.ErrDefault400) + th.AssertEquals(t, true, isBadRequest) +} + +// TestUpdateNodeGroupBadField tries to update a +// field of the node group that does not exist. +func TestUpdateNodeGroupBadField(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleUpdateNodeGroupBadField(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + updateOpts := []nodegroups.UpdateOptsBuilder{ + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/bad_field", + Value: "abc123", + }, + } + + _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() + th.AssertEquals(t, true, err != nil) + _, isBadRequest := err.(gophercloud.ErrDefault400) + th.AssertEquals(t, true, isBadRequest) +} + +// TestUpdateNodeGroupBadMin tries to set a minimum node count +// greater than the current node count +func TestUpdateNodeGroupBadMin(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleUpdateNodeGroupBadMin(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + updateOpts := []nodegroups.UpdateOptsBuilder{ + nodegroups.UpdateOpts{ + Op: nodegroups.ReplaceOp, + Path: "/min_node_count", + Value: 5, + }, + } + + _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() + th.AssertEquals(t, true, err != nil) + _, isNotAccepted := err.(gophercloud.ErrDefault409) + th.AssertEquals(t, true, isNotAccepted) +} + +// TestDeleteNodeGroupSuccess deletes a node group successfully. +func TestDeleteNodeGroupSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleDeleteNodeGroupSuccess(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + err := nodegroups.Delete(sc, clusterUUID, nodeGroup2UUID).ExtractErr() + th.AssertNoErr(t, err) +} + +// TestDeleteNodeGroupNotFound tries to delete a node group that does not exist. +func TestDeleteNodeGroupNotFound(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleDeleteNodeGroupNotFound(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + err := nodegroups.Delete(sc, clusterUUID, badNodeGroupUUID).ExtractErr() + _, isNotFound := err.(gophercloud.ErrDefault404) + th.AssertEquals(t, true, isNotFound) +} + +// TestDeleteNodeGroupClusterNotFound tries to delete a node group in a cluster that does not exist. +func TestDeleteNodeGroupClusterNotFound(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleDeleteNodeGroupClusterNotFound(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + err := nodegroups.Delete(sc, badClusterUUID, badNodeGroupUUID).ExtractErr() + _, isNotFound := err.(gophercloud.ErrDefault404) + th.AssertEquals(t, true, isNotFound) +} + +// TestDeleteNodeGroupDefault tries to delete a protected default node group. +func TestDeleteNodeGroupDefault(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + handleDeleteNodeGroupDefault(t) + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + + err := nodegroups.Delete(sc, clusterUUID, nodeGroup2UUID).ExtractErr() + _, isBadRequest := err.(gophercloud.ErrDefault400) + th.AssertEquals(t, true, isBadRequest) +} diff --git a/openstack/containerinfra/v1/nodegroups/urls.go b/openstack/containerinfra/v1/nodegroups/urls.go index 5054f730be..a7dfa1e774 100644 --- a/openstack/containerinfra/v1/nodegroups/urls.go +++ b/openstack/containerinfra/v1/nodegroups/urls.go @@ -11,3 +11,15 @@ func getURL(c *gophercloud.ServiceClient, clusterID, nodeGroupID string) string func listURL(c *gophercloud.ServiceClient, clusterID string) string { return c.ServiceURL("clusters", clusterID, "nodegroups") } + +func createURL(c *gophercloud.ServiceClient, clusterID string) string { + return c.ServiceURL("clusters", clusterID, "nodegroups") +} + +func updateURL(c *gophercloud.ServiceClient, clusterID, nodeGroupID string) string { + return c.ServiceURL("clusters", clusterID, "nodegroups", nodeGroupID) +} + +func deleteURL(c *gophercloud.ServiceClient, clusterID, nodeGroupID string) string { + return c.ServiceURL("clusters", clusterID, "nodegroups", nodeGroupID) +} From 4d9bb630e4411e0965b651951ad57d7fba334e7b Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche Date: Wed, 8 Jan 2020 03:52:37 +0100 Subject: [PATCH 0929/2296] Fix sort_key / sort_keys (#1809) Just add missing s in sort_keys query param of cron triggers : https://docs.openstack.org/mistral/latest/api/v2.html#get--v2-workflows --- openstack/workflow/v2/crontriggers/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index 2d9ee99f5e..c279254923 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -124,7 +124,7 @@ type ListOpts struct { // It can be "asc" or "desc" (default). SortDirs string `q:"sort_dirs"` // SortKeys allows to sort by one of the cron trigger attributes. - SortKeys string `q:"sort_key"` + SortKeys string `q:"sort_keys"` // Marker and Limit control paging. // Marker instructs List where to start listing from. Marker string `q:"marker"` From de4ad422a3ef887560b2098f032688f97b5a1bae Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 7 Jan 2020 19:53:47 -0700 Subject: [PATCH 0930/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc0a60e56..508303cd39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ IMPROVEMENTS * 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) +BUG FIXES + +* Changed `sort_key` to `sort_keys` in ` workflow/v2/crontriggers.ListOpts` [GH-1809](https://github.com/gophercloud/gophercloud/pull/1809) + ## 0.7.0 (December 3, 2019) IMPROVEMENTS From aea1ee1f6d95652ca9cfdc28577e10f322b951e1 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 8 Jan 2020 19:42:09 -0500 Subject: [PATCH 0931/2296] Fix ironic acceptance tests (#1812) The Ironic configuration being used in the tests enables only the iPXE and Fake boot interfaces, so use those instead of PXE. --- acceptance/openstack/baremetal/v1/baremetal.go | 4 ++-- acceptance/openstack/baremetal/v1/ports_test.go | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/baremetal/v1/baremetal.go b/acceptance/openstack/baremetal/v1/baremetal.go index 332fb4c159..1278de533a 100644 --- a/acceptance/openstack/baremetal/v1/baremetal.go +++ b/acceptance/openstack/baremetal/v1/baremetal.go @@ -18,7 +18,7 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, e node, err := nodes.Create(client, nodes.CreateOpts{ Name: name, Driver: "ipmi", - BootInterface: "pxe", + BootInterface: "ipxe", RAIDInterface: "agent", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", @@ -74,7 +74,7 @@ func CreateFakeNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Nod node, err := nodes.Create(client, nodes.CreateOpts{ Name: name, Driver: "fake-hardware", - BootInterface: "pxe", + BootInterface: "fake", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", "ipmi_username": "admin", diff --git a/acceptance/openstack/baremetal/v1/ports_test.go b/acceptance/openstack/baremetal/v1/ports_test.go index 3547e95cc7..b938c193c3 100644 --- a/acceptance/openstack/baremetal/v1/ports_test.go +++ b/acceptance/openstack/baremetal/v1/ports_test.go @@ -20,9 +20,10 @@ func TestPortsCreateDestroy(t *testing.T) { client.Microversion = "1.53" node, err := CreateFakeNode(t, client) - port, err := CreatePort(t, client, node) th.AssertNoErr(t, err) defer DeleteNode(t, client, node) + port, err := CreatePort(t, client, node) + th.AssertNoErr(t, err) defer DeletePort(t, client, port) found := false @@ -54,9 +55,10 @@ func TestPortsUpdate(t *testing.T) { client.Microversion = "1.53" node, err := CreateFakeNode(t, client) - port, err := CreatePort(t, client, node) th.AssertNoErr(t, err) defer DeleteNode(t, client, node) + port, err := CreatePort(t, client, node) + th.AssertNoErr(t, err) defer DeletePort(t, client, port) updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ From af6dbf2cfaa10c5798c0a6a633388219a3417fc0 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 8 Jan 2020 22:29:22 -0500 Subject: [PATCH 0932/2296] Orchestration: Add support for resource_type endpoints (#1806) Add client support for listing available resource types in Heat, getting their schemata, and downloading example templates for use as provider templates. Fixes #1805 Signed-off-by: Zane Bitter --- .../orchestration/v1/resourcetypes/doc.go | 21 + .../v1/resourcetypes/requests.go | 118 ++++++ .../orchestration/v1/resourcetypes/results.go | 150 +++++++ .../v1/resourcetypes/testing/doc.go | 3 + .../v1/resourcetypes/testing/fixtures.go | 387 ++++++++++++++++++ .../v1/resourcetypes/testing/requests_test.go | 84 ++++ .../orchestration/v1/resourcetypes/urls.go | 19 + 7 files changed, 782 insertions(+) create mode 100644 openstack/orchestration/v1/resourcetypes/doc.go create mode 100644 openstack/orchestration/v1/resourcetypes/requests.go create mode 100644 openstack/orchestration/v1/resourcetypes/results.go create mode 100644 openstack/orchestration/v1/resourcetypes/testing/doc.go create mode 100644 openstack/orchestration/v1/resourcetypes/testing/fixtures.go create mode 100644 openstack/orchestration/v1/resourcetypes/testing/requests_test.go create mode 100644 openstack/orchestration/v1/resourcetypes/urls.go diff --git a/openstack/orchestration/v1/resourcetypes/doc.go b/openstack/orchestration/v1/resourcetypes/doc.go new file mode 100644 index 0000000000..cfc6076530 --- /dev/null +++ b/openstack/orchestration/v1/resourcetypes/doc.go @@ -0,0 +1,21 @@ +/* +Package resourcetypes provides operations for listing available resource types, +obtaining their properties schema, and generating example templates that can be +customised to use as provider templates. + +Example of listing available resource types: + + 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) + } +*/ +package resourcetypes diff --git a/openstack/orchestration/v1/resourcetypes/requests.go b/openstack/orchestration/v1/resourcetypes/requests.go new file mode 100644 index 0000000000..064f89f124 --- /dev/null +++ b/openstack/orchestration/v1/resourcetypes/requests.go @@ -0,0 +1,118 @@ +package resourcetypes + +import ( + "github.com/gophercloud/gophercloud" +) + +// SupportStatus is a type for specifying by which support status to filter the +// list of resource types. +type SupportStatus string + +const ( + // SupportStatusUnknown is returned when the resource type does not have a + // support status. + SupportStatusUnknown SupportStatus = "UNKNOWN" + // SupportStatusSupported indicates a resource type that is expected to + // work. + SupportStatusSupported SupportStatus = "SUPPORTED" + // SupportStatusDeprecated indicates a resource type that is in the process + // being removed, and may or may not be replaced by something else. + SupportStatusDeprecated SupportStatus = "DEPRECATED" + // SupportStatusHidden indicates a resource type that has been removed. + // Existing stacks that contain resources of this type can still be + // deleted or updated to remove the resources, but they may not actually + // do anything any more. + SupportStatusHidden SupportStatus = "HIDDEN" + // SupportStatusUnsupported indicates a resource type that is provided for + // preview or other purposes and should not be relied upon. + SupportStatusUnsupported SupportStatus = "UNSUPPORTED" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToResourceTypeListQuery() (string, error) +} + +// ListOpts allows the filtering of collections through the API. +type ListOpts struct { + // Filters the resource type list by a regex on the name. + NameRegex string `q:"name"` + // Filters the resource list by the specified SupportStatus. + SupportStatus SupportStatus `q:"support_status"` + // Return descriptions as well as names of resource types + WithDescription bool `q:"with_description"` +} + +// ToResourceTypeListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToResourceTypeListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list available resource types. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) (r ListResult) { + url := listURL(client) + + if opts == nil { + opts = ListOpts{} + } + query, err := opts.ToResourceTypeListQuery() + if err != nil { + r.Err = err + return + } + url += query + + _, r.Err = client.Get(url, &r.Body, nil) + return +} + +// GetSchema retreives the schema for a given resource type. +func GetSchema(client *gophercloud.ServiceClient, resourceType string) (r GetSchemaResult) { + _, r.Err = client.Get(getSchemaURL(client, resourceType), &r.Body, nil) + return +} + +// GenerateTemplateOptsBuilder allows extensions to add additional parameters +// to the GenerateTemplate request. +type GenerateTemplateOptsBuilder interface { + ToGenerateTemplateQuery() (string, error) +} + +type GeneratedTemplateType string + +const ( + TemplateTypeHOT GeneratedTemplateType = "hot" + TemplateTypeCFn GeneratedTemplateType = "cfn" +) + +// GenerateTemplateOpts allows the filtering of collections through the API. +type GenerateTemplateOpts struct { + TemplateType GeneratedTemplateType `q:"template_type"` +} + +// ToGenerateTemplateQuery formats a GenerateTemplateOpts into a query string. +func (opts GenerateTemplateOpts) ToGenerateTemplateQuery() (string, error) { + if opts.TemplateType == "" { + opts.TemplateType = TemplateTypeHOT + } + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// GenerateTemplate retreives an example template for a given resource type. +func GenerateTemplate(client *gophercloud.ServiceClient, resourceType string, opts GenerateTemplateOptsBuilder) (r TemplateResult) { + url := generateTemplateURL(client, resourceType) + if opts == nil { + opts = GenerateTemplateOpts{} + } + query, err := opts.ToGenerateTemplateQuery() + if err != nil { + r.Err = err + return + } + url += query + _, r.Err = client.Get(url, &r.Body, nil) + return +} diff --git a/openstack/orchestration/v1/resourcetypes/results.go b/openstack/orchestration/v1/resourcetypes/results.go new file mode 100644 index 0000000000..f5b13a481d --- /dev/null +++ b/openstack/orchestration/v1/resourcetypes/results.go @@ -0,0 +1,150 @@ +package resourcetypes + +import ( + "github.com/gophercloud/gophercloud" +) + +// ResourceTypeSummary contains the result of listing an available resource +// type. +type ResourceTypeSummary struct { + ResourceType string `json:"resource_type"` + Description string `json:"description"` +} + +// PropertyType represents the expected type of a property or attribute value. +type PropertyType string + +const ( + // StringProperty indicates a string property type. + StringProperty PropertyType = "string" + // IntegerProperty indicates an integer property type. + IntegerProperty PropertyType = "integer" + // NumberProperty indicates a number property type. It may be an integer or + // float. + NumberProperty PropertyType = "number" + // BooleanProperty indicates a boolean property type. + BooleanProperty PropertyType = "boolean" + // MapProperty indicates a map property type. + MapProperty PropertyType = "map" + // ListProperty indicates a list property type. + ListProperty PropertyType = "list" + // UntypedProperty indicates a property that could have any type. + UntypedProperty PropertyType = "any" +) + +// AttributeSchema is the schema of a resource attribute +type AttributeSchema struct { + Description string `json:"description,omitempty"` + Type PropertyType `json:"type"` +} + +// MinMaxConstraint is a type of constraint with minimum and maximum values. +// This is used for both Range and Length constraints. +type MinMaxConstraint struct { + Min float64 `json:"min,omitempty"` + Max float64 `json:"max,omitempty"` +} + +// ModuloConstraint constrains an integer to have a certain value given a +// particular modulus. +type ModuloConstraint struct { + Step int `json:"step,omitempty"` + Offset int `json:"offset,omitempty"` +} + +// ConstraintSchema describes all possible types of constraints. Besides the +// description, only one other field is ever set at a time. +type ConstraintSchema struct { + Description string `json:"description,omitempty"` + Range *MinMaxConstraint `json:"range,omitempty"` + Length *MinMaxConstraint `json:"length,omitempty"` + Modulo *ModuloConstraint `json:"modulo,omitempty"` + AllowedValues *[]interface{} `json:"allowed_values,omitempty"` + AllowedPattern *string `json:"allowed_pattern,omitempty"` + CustomConstraint *string `json:"custom_constraint,omitempty"` +} + +// PropertySchema is the schema of a resource property. +type PropertySchema struct { + Type PropertyType `json:"type"` + Description string `json:"description,omitempty"` + Default interface{} `json:"default,omitempty"` + Constraints []ConstraintSchema `json:"constraints,omitempty"` + Required bool `json:"required"` + Immutable bool `json:"immutable"` + UpdateAllowed bool `json:"update_allowed"` + Schema map[string]PropertySchema `json:"schema,omitempty"` +} + +// SupportStatusDetails contains information about the support status of the +// resource and its history. +type SupportStatusDetails struct { + Status SupportStatus `json:"status"` + Message string `json:"message,omitempty"` + Version string `json:"version,omitempty"` + PreviousStatus *SupportStatusDetails `json:"previous_status,omitempty"` +} + +// ResourceSchema is the schema for a resource type, its attributes, and +// properties. +type ResourceSchema struct { + ResourceType string `json:"resource_type"` + SupportStatus SupportStatusDetails `json:"support_status"` + Attributes map[string]AttributeSchema `json:"attributes"` + Properties map[string]PropertySchema `json:"properties"` +} + +// ListResult represents the result of a List operation. +type ListResult struct { + gophercloud.Result +} + +// Extract returns a slice of ResourceTypeSummary objects and is called after +// a List operation. +func (r ListResult) Extract() (rts []ResourceTypeSummary, err error) { + var full struct { + ResourceTypes []ResourceTypeSummary `json:"resource_types"` + } + err = r.ExtractInto(&full) + if err == nil { + rts = full.ResourceTypes + return + } + + var basic struct { + ResourceTypes []string `json:"resource_types"` + } + err2 := r.ExtractInto(&basic) + if err2 == nil { + err = nil + rts = make([]ResourceTypeSummary, len(basic.ResourceTypes)) + for i, n := range basic.ResourceTypes { + rts[i] = ResourceTypeSummary{ResourceType: n} + } + } + return +} + +// GetSchemaResult represents the result of a GetSchema operation. +type GetSchemaResult struct { + gophercloud.Result +} + +// Extract returns a ResourceSchema object and is called after a GetSchema +// operation. +func (r GetSchemaResult) Extract() (rts ResourceSchema, err error) { + err = r.ExtractInto(&rts) + return +} + +// TemplateResult represents the result of a Template get operation. +type TemplateResult struct { + gophercloud.Result +} + +// Extract returns a Template object and is called after a Template get +// operation. +func (r TemplateResult) Extract() (template map[string]interface{}, err error) { + err = r.ExtractInto(&template) + return +} diff --git a/openstack/orchestration/v1/resourcetypes/testing/doc.go b/openstack/orchestration/v1/resourcetypes/testing/doc.go new file mode 100644 index 0000000000..2e762e5005 --- /dev/null +++ b/openstack/orchestration/v1/resourcetypes/testing/doc.go @@ -0,0 +1,3 @@ +// orchestration_resourcetypes_v1 + +package testing diff --git a/openstack/orchestration/v1/resourcetypes/testing/fixtures.go b/openstack/orchestration/v1/resourcetypes/testing/fixtures.go new file mode 100644 index 0000000000..ed1faf1e18 --- /dev/null +++ b/openstack/orchestration/v1/resourcetypes/testing/fixtures.go @@ -0,0 +1,387 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/orchestration/v1/resourcetypes" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const BasicListOutput = ` +{ + "resource_types": [ + "OS::Nova::Server", + "OS::Heat::Stack" + ] +} +` + +var BasicListExpected = []resourcetypes.ResourceTypeSummary{ + resourcetypes.ResourceTypeSummary{ + ResourceType: "OS::Nova::Server", + }, + resourcetypes.ResourceTypeSummary{ + ResourceType: "OS::Heat::Stack", + }, +} + +const FullListOutput = ` +{ + "resource_types": [ + { + "description": "A Nova Server", + "resource_type": "OS::Nova::Server" + }, + { + "description": "A Heat Stack", + "resource_type": "OS::Heat::Stack" + } + ] +} +` + +var FullListExpected = []resourcetypes.ResourceTypeSummary{ + resourcetypes.ResourceTypeSummary{ + ResourceType: "OS::Nova::Server", + Description: "A Nova Server", + }, + resourcetypes.ResourceTypeSummary{ + ResourceType: "OS::Heat::Stack", + Description: "A Heat Stack", + }, +} + +const listFilterRegex = "OS::Heat::.*" +const FilteredListOutput = ` +{ + "resource_types": [ + { + "description": "A Heat Stack", + "resource_type": "OS::Heat::Stack" + } + ] +} +` + +var FilteredListExpected = []resourcetypes.ResourceTypeSummary{ + resourcetypes.ResourceTypeSummary{ + ResourceType: "OS::Heat::Stack", + Description: "A Heat Stack", + }, +} + +// HandleListSuccessfully creates an HTTP handler at `/resource_types` +// on the test handler mux that responds with a `List` response. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/resource_types", + 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().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + r.ParseForm() + var output string + if r.Form.Get("with_description") == "true" { + if r.Form.Get("name") == listFilterRegex { + output = FilteredListOutput + } else { + output = FullListOutput + } + } else { + output = BasicListOutput + } + fmt.Fprint(w, output) + }) +} + +var glanceImageConstraint = "glance.image" + +var GetSchemaExpected = resourcetypes.ResourceSchema{ + ResourceType: "OS::Test::TestServer", + SupportStatus: resourcetypes.SupportStatusDetails{ + Status: resourcetypes.SupportStatusDeprecated, + Message: "Bye bye.", + Version: "10.0.0", + PreviousStatus: &resourcetypes.SupportStatusDetails{ + Status: resourcetypes.SupportStatusSupported, + }, + }, + Attributes: map[string]resourcetypes.AttributeSchema{ + "show": resourcetypes.AttributeSchema{ + Description: "Detailed information about resource.", + Type: resourcetypes.MapProperty, + }, + "tags": resourcetypes.AttributeSchema{ + Description: "Tags from the server.", + Type: resourcetypes.ListProperty, + }, + "name": resourcetypes.AttributeSchema{ + Description: "Name of the server.", + Type: resourcetypes.StringProperty, + }, + }, + Properties: map[string]resourcetypes.PropertySchema{ + "name": resourcetypes.PropertySchema{ + Type: resourcetypes.StringProperty, + Description: "Server name.", + UpdateAllowed: true, + }, + "image": resourcetypes.PropertySchema{ + Type: resourcetypes.StringProperty, + Description: "The ID or name of the image to boot with.", + Required: true, + Constraints: []resourcetypes.ConstraintSchema{ + resourcetypes.ConstraintSchema{ + CustomConstraint: &glanceImageConstraint, + }, + }, + }, + "block_device_mapping": resourcetypes.PropertySchema{ + Type: resourcetypes.ListProperty, + Description: "Block device mappings for this server.", + Schema: map[string]resourcetypes.PropertySchema{ + "*": resourcetypes.PropertySchema{ + Type: resourcetypes.MapProperty, + Schema: map[string]resourcetypes.PropertySchema{ + "ephemeral_format": resourcetypes.PropertySchema{ + Type: resourcetypes.StringProperty, + Description: "The format of the local ephemeral block device.", + Constraints: []resourcetypes.ConstraintSchema{ + resourcetypes.ConstraintSchema{ + AllowedValues: &[]interface{}{ + "ext3", "ext4", "xfs", + }, + }, + }, + }, + "ephemeral_size": resourcetypes.PropertySchema{ + Type: resourcetypes.IntegerProperty, + Description: "The size of the local ephemeral block device, in GB.", + Constraints: []resourcetypes.ConstraintSchema{ + resourcetypes.ConstraintSchema{ + Range: &resourcetypes.MinMaxConstraint{ + Min: 1, + }, + }, + }, + }, + "delete_on_termination": resourcetypes.PropertySchema{ + Type: resourcetypes.BooleanProperty, + Description: "Delete volume on server termination.", + Default: true, + Immutable: true, + }, + }, + }, + }, + }, + "image_update_policy": resourcetypes.PropertySchema{ + Type: resourcetypes.StringProperty, + Description: "Policy on how to apply an image-id update.", + Default: "REBUILD", + Constraints: []resourcetypes.ConstraintSchema{ + resourcetypes.ConstraintSchema{ + AllowedValues: &[]interface{}{ + "REBUILD", "REPLACE", + }, + }, + }, + UpdateAllowed: true, + }, + }, +} + +const GetSchemaOutput = ` +{ + "resource_type": "OS::Test::TestServer", + "support_status": { + "status": "DEPRECATED", + "message": "Bye bye.", + "version": "10.0.0", + "previous_status": { + "status": "SUPPORTED", + "message": null, + "version": null, + "previous_status": null + } + }, + "attributes": { + "show": { + "type": "map", + "description": "Detailed information about resource." + }, + "tags": { + "type": "list", + "description": "Tags from the server." + }, + "name": { + "type": "string", + "description": "Name of the server." + } + }, + "properties": { + "name": { + "update_allowed": true, + "required": false, + "type": "string", + "description": "Server name.", + "immutable": false + }, + "image": { + "description": "The ID or name of the image to boot with.", + "required": true, + "update_allowed": false, + "type": "string", + "immutable": false, + "constraints": [ + { + "custom_constraint": "glance.image" + } + ] + }, + "block_device_mapping": { + "description": "Block device mappings for this server.", + "required": false, + "update_allowed": false, + "type": "list", + "immutable": false, + "schema": { + "*": { + "update_allowed": false, + "required": false, + "type": "map", + "immutable": false, + "schema": { + "ephemeral_format": { + "description": "The format of the local ephemeral block device.", + "required": false, + "update_allowed": false, + "type": "string", + "immutable": false, + "constraints": [ + { + "allowed_values": [ + "ext3", + "ext4", + "xfs" + ] + } + ] + }, + "ephemeral_size": { + "description": "The size of the local ephemeral block device, in GB.", + "required": false, + "update_allowed": false, + "type": "integer", + "immutable": false, + "constraints": [ + { + "range": { + "min": 1 + } + } + ] + }, + "delete_on_termination": { + "update_allowed": false, + "default": true, + "required": false, + "type": "boolean", + "description": "Delete volume on server termination.", + "immutable": true + } + } + } + } + }, + "image_update_policy": { + "description": "Policy on how to apply an image-id update.", + "default": "REBUILD", + "required": false, + "update_allowed": true, + "type": "string", + "immutable": false, + "constraints": [ + { + "allowed_values": [ + "REBUILD", + "REPLACE" + ] + } + ] + } + } +} +` + +// HandleGetSchemaSuccessfully creates an HTTP handler at +// `/resource_types/OS::Test::TestServer` on the test handler mux that +// responds with a `GetSchema` response. +func HandleGetSchemaSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/resource_types/OS::Test::TestServer", + 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().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, GetSchemaOutput) + }) +} + +const GenerateTemplateOutput = ` +{ + "outputs": { + "OS::stack_id": { + "value": { + "get_resource": "NoneResource" + } + }, + "show": { + "description": "Detailed information about resource.", + "value": { + "get_attr": [ + "NoneResource", + "show" + ] + } + } + }, + "heat_template_version": "2016-10-14", + "description": "Initial template of NoneResource", + "parameters": {}, + "resources": { + "NoneResource": { + "type": "OS::Heat::None", + "properties": {} + } + } +} +` + +// HandleGenerateTemplateSuccessfully creates an HTTP handler at +// `/resource_types/OS::Heat::None/template` on the test handler mux that +// responds with a template. +func HandleGenerateTemplateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/resource_types/OS::Heat::None/template", + 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().Set("Content-Type", "application/json") + r.ParseForm() + if r.Form.Get("template_type") == "hot" { + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, GenerateTemplateOutput) + } else { + w.WriteHeader(http.StatusBadRequest) + } + }) +} diff --git a/openstack/orchestration/v1/resourcetypes/testing/requests_test.go b/openstack/orchestration/v1/resourcetypes/testing/requests_test.go new file mode 100644 index 0000000000..a6753ff38d --- /dev/null +++ b/openstack/orchestration/v1/resourcetypes/testing/requests_test.go @@ -0,0 +1,84 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/orchestration/v1/resourcetypes" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestBasicListResourceTypes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + result := resourcetypes.List(fake.ServiceClient(), nil) + th.AssertNoErr(t, result.Err) + + actual, err := result.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, BasicListExpected, actual) +} + +func TestFullListResourceTypes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + result := resourcetypes.List(fake.ServiceClient(), resourcetypes.ListOpts{ + WithDescription: true, + }) + th.AssertNoErr(t, result.Err) + + actual, err := result.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, FullListExpected, actual) +} + +func TestFilteredListResourceTypes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + result := resourcetypes.List(fake.ServiceClient(), resourcetypes.ListOpts{ + NameRegex: listFilterRegex, + WithDescription: true, + }) + th.AssertNoErr(t, result.Err) + + actual, err := result.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, FilteredListExpected, actual) +} + +func TestGetSchema(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSchemaSuccessfully(t) + + result := resourcetypes.GetSchema(fake.ServiceClient(), "OS::Test::TestServer") + th.AssertNoErr(t, result.Err) + + actual, err := result.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, GetSchemaExpected, actual) +} + +func TestGenerateTemplate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGenerateTemplateSuccessfully(t) + + result := resourcetypes.GenerateTemplate(fake.ServiceClient(), "OS::Heat::None", nil) + th.AssertNoErr(t, result.Err) + + actual, err := result.Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "2016-10-14", actual["heat_template_version"]) +} diff --git a/openstack/orchestration/v1/resourcetypes/urls.go b/openstack/orchestration/v1/resourcetypes/urls.go new file mode 100644 index 0000000000..0c4bd075ac --- /dev/null +++ b/openstack/orchestration/v1/resourcetypes/urls.go @@ -0,0 +1,19 @@ +package resourcetypes + +import "github.com/gophercloud/gophercloud" + +const ( + resTypesPath = "resource_types" +) + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resTypesPath) +} + +func getSchemaURL(c *gophercloud.ServiceClient, resourceType string) string { + return c.ServiceURL(resTypesPath, resourceType) +} + +func generateTemplateURL(c *gophercloud.ServiceClient, resourceType string) string { + return c.ServiceURL(resTypesPath, resourceType, "template") +} From 20a03f8500ac2f3122382b4427d7b1873afe24dc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 8 Jan 2020 20:30:36 -0700 Subject: [PATCH 0933/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 508303cd39..108875bce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ IMPROVEMENTS * 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) BUG FIXES From acb07a6bb4c90a2018c3dcb6d64b51d01c2758a5 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 11 Jan 2020 05:36:29 +0100 Subject: [PATCH 0934/2296] Barbican V1: Allow multiple ACL types in one request (#1816) --- .../openstack/keymanager/v1/acls_test.go | 14 ++++++---- openstack/keymanager/v1/acls/requests.go | 17 +++++++++-- .../v1/acls/testing/requests_test.go | 28 ++++++++++++------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/acceptance/openstack/keymanager/v1/acls_test.go b/acceptance/openstack/keymanager/v1/acls_test.go index c6a0649480..b3de4f54ab 100644 --- a/acceptance/openstack/keymanager/v1/acls_test.go +++ b/acceptance/openstack/keymanager/v1/acls_test.go @@ -30,9 +30,11 @@ func TestACLCRUD(t *testing.T) { users := []string{user} iFalse := false setOpts := acls.SetOpts{ - Type: "read", - Users: &users, - ProjectAccess: &iFalse, + acls.SetOpt{ + Type: "read", + Users: &users, + ProjectAccess: &iFalse, + }, } aclRef, err := acls.SetSecretACL(client, secretID, setOpts).Extract() @@ -55,8 +57,10 @@ func TestACLCRUD(t *testing.T) { newUsers := []string{} updateOpts := acls.SetOpts{ - Type: "read", - Users: &newUsers, + acls.SetOpt{ + Type: "read", + Users: &newUsers, + }, } aclRef, err = acls.UpdateSecretACL(client, secretID, updateOpts).Extract() diff --git a/openstack/keymanager/v1/acls/requests.go b/openstack/keymanager/v1/acls/requests.go index b83c56075d..5c5daf9430 100644 --- a/openstack/keymanager/v1/acls/requests.go +++ b/openstack/keymanager/v1/acls/requests.go @@ -22,8 +22,8 @@ type SetOptsBuilder interface { ToACLSetMap() (map[string]interface{}, error) } -// SetOpts represents options to set an ACL on a resource. -type SetOpts struct { +// SetOpt represents options to set a particular ACL type on a resource. +type SetOpt struct { // Type is the type of ACL to set. ie: read. Type string `json:"-" required:"true"` @@ -34,9 +34,20 @@ type SetOpts struct { ProjectAccess *bool `json:"project-access,omitempty"` } +// SetOpts represents options to set an ACL on a resource. +type SetOpts []SetOpt + // ToACLSetMap formats a SetOpts into a set request. func (opts SetOpts) ToACLSetMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, opts.Type) + b := make(map[string]interface{}) + for _, v := range opts { + m, err := gophercloud.BuildRequestBody(v, v.Type) + if err != nil { + return nil, err + } + b[v.Type] = m[v.Type] + } + return b, nil } // SetContainerACL will set an ACL on a container. diff --git a/openstack/keymanager/v1/acls/testing/requests_test.go b/openstack/keymanager/v1/acls/testing/requests_test.go index b978f405f1..febac2b38d 100644 --- a/openstack/keymanager/v1/acls/testing/requests_test.go +++ b/openstack/keymanager/v1/acls/testing/requests_test.go @@ -36,9 +36,11 @@ func TestSetSecretACL(t *testing.T) { users := []string{"GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW"} iFalse := false setOpts := acls.SetOpts{ - Type: "read", - Users: &users, - ProjectAccess: &iFalse, + acls.SetOpt{ + Type: "read", + Users: &users, + ProjectAccess: &iFalse, + }, } actual, err := acls.SetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() @@ -54,9 +56,11 @@ func TestSetContainerACL(t *testing.T) { users := []string{"GG27dVwR9gBMnsOaRoJ1DFJmZfdVjIdW"} iFalse := false setOpts := acls.SetOpts{ - Type: "read", - Users: &users, - ProjectAccess: &iFalse, + acls.SetOpt{ + Type: "read", + Users: &users, + ProjectAccess: &iFalse, + }, } actual, err := acls.SetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() @@ -89,8 +93,10 @@ func TestUpdateSecretACL(t *testing.T) { newUsers := []string{} updateOpts := acls.SetOpts{ - Type: "read", - Users: &newUsers, + acls.SetOpt{ + Type: "read", + Users: &newUsers, + }, } actual, err := acls.UpdateSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() @@ -105,8 +111,10 @@ func TestUpdateContainerACL(t *testing.T) { newUsers := []string{} updateOpts := acls.SetOpts{ - Type: "read", - Users: &newUsers, + acls.SetOpt{ + Type: "read", + Users: &newUsers, + }, } actual, err := acls.UpdateContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() From 4f96abfe3f492f1fe85cbb03718bbd9b7ca306a9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 10 Jan 2020 21:39:57 -0700 Subject: [PATCH 0935/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 108875bce1..643a967968 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.8.0 (Unreleased) +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) @@ -11,6 +15,7 @@ IMPROVEMENTS * 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) BUG FIXES From fab5573aeafcbb8fdb3b9e9aaefd9af683471097 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 12 Jan 2020 20:03:09 -0700 Subject: [PATCH 0936/2296] Block Storage: Fix Capabilities Parsing (#1817) It appears that the max_over_subscription_ratio stat returned by the schedulerstats extension can be both a string and float64 depending on the OpenStack release. This commit enables both to be supported, converting either value to a string. --- .../extensions/schedulerstats/results.go | 17 +++++++++-- .../schedulerstats/testing/fixtures.go | 28 +++++++++++-------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/openstack/blockstorage/extensions/schedulerstats/results.go b/openstack/blockstorage/extensions/schedulerstats/results.go index 11109673c8..fe5ea2b4fc 100644 --- a/openstack/blockstorage/extensions/schedulerstats/results.go +++ b/openstack/blockstorage/extensions/schedulerstats/results.go @@ -3,6 +3,7 @@ package schedulerstats import ( "encoding/json" "math" + "strconv" "github.com/gophercloud/gophercloud/pagination" ) @@ -23,7 +24,7 @@ type Capabilities struct { LocationInfo string `json:"location_info"` QoSSupport bool `json:"QoS_support"` ProvisionedCapacityGB float64 `json:"provisioned_capacity_gb"` - MaxOverSubscriptionRatio string `json:"max_over_subscription_ratio"` + MaxOverSubscriptionRatio string `json:"-"` ThinProvisioningSupport bool `json:"thin_provisioning_support"` ThickProvisioningSupport bool `json:"thick_provisioning_support"` TotalVolumes int64 `json:"total_volumes"` @@ -44,8 +45,9 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { type tmp Capabilities var s struct { tmp - FreeCapacityGB interface{} `json:"free_capacity_gb"` - TotalCapacityGB interface{} `json:"total_capacity_gb"` + FreeCapacityGB interface{} `json:"free_capacity_gb"` + MaxOverSubscriptionRatio interface{} `json:"max_over_subscription_ratio"` + TotalCapacityGB interface{} `json:"total_capacity_gb"` } err := json.Unmarshal(b, &s) if err != nil { @@ -72,6 +74,15 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { r.FreeCapacityGB = parseCapacity(s.FreeCapacityGB) r.TotalCapacityGB = parseCapacity(s.TotalCapacityGB) + if s.MaxOverSubscriptionRatio != nil { + switch t := s.MaxOverSubscriptionRatio.(type) { + case float64: + r.MaxOverSubscriptionRatio = strconv.FormatFloat(t, 'f', -1, 64) + case string: + r.MaxOverSubscriptionRatio = t + } + } + return nil } diff --git a/openstack/blockstorage/extensions/schedulerstats/testing/fixtures.go b/openstack/blockstorage/extensions/schedulerstats/testing/fixtures.go index 4031e29724..3af0718a42 100644 --- a/openstack/blockstorage/extensions/schedulerstats/testing/fixtures.go +++ b/openstack/blockstorage/extensions/schedulerstats/testing/fixtures.go @@ -33,6 +33,7 @@ const StoragePoolsListBodyDetail = ` "filter_function": null, "free_capacity_gb": 64765, "goodness_function": null, + "max_over_subscription_ratio": "1.5", "multiattach": false, "reserved_percentage": 0, "storage_protocol": "ceph", @@ -49,6 +50,7 @@ const StoragePoolsListBodyDetail = ` "filter_function": null, "free_capacity_gb": "unknown", "goodness_function": null, + "max_over_subscription_ratio": 1.5, "multiattach": false, "reserved_percentage": 0, "storage_protocol": "ceph", @@ -67,24 +69,26 @@ var ( StoragePoolFake1 = schedulerstats.StoragePool{ Name: "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd", Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.2.0", - FreeCapacityGB: 64765, - StorageProtocol: "ceph", - TotalCapacityGB: 787947.93, - VendorName: "Open Source", - VolumeBackendName: "cinder.volumes.ssd", + DriverVersion: "1.2.0", + FreeCapacityGB: 64765, + MaxOverSubscriptionRatio: "1.5", + StorageProtocol: "ceph", + TotalCapacityGB: 787947.93, + VendorName: "Open Source", + VolumeBackendName: "cinder.volumes.ssd", }, } StoragePoolFake2 = schedulerstats.StoragePool{ Name: "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd", Capabilities: schedulerstats.Capabilities{ - DriverVersion: "1.2.0", - FreeCapacityGB: 0.0, - StorageProtocol: "ceph", - TotalCapacityGB: math.Inf(1), - VendorName: "Open Source", - VolumeBackendName: "cinder.volumes.hdd", + DriverVersion: "1.2.0", + FreeCapacityGB: 0.0, + MaxOverSubscriptionRatio: "1.5", + StorageProtocol: "ceph", + TotalCapacityGB: math.Inf(1), + VendorName: "Open Source", + VolumeBackendName: "cinder.volumes.hdd", }, } ) From 1a1afd8a7b9f5f44eebd269834a41e12dc5ded73 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 12 Jan 2020 20:04:39 -0700 Subject: [PATCH 0937/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 643a967968..d058d81125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ IMPROVEMENTS 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) ## 0.7.0 (December 3, 2019) From 82e93e0e62514233224359f41a3901f43c7395a0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 12 Jan 2020 20:53:38 -0700 Subject: [PATCH 0938/2296] BlockStorage: apiversions (#458) * BlockStorage: apiversions * BlockStorage v1: Add deprecation note to apiversions * BlockStorage: Fix URL when retrieving API version * BlockStorage: Add acceptance test for apiversions * BlockStorage: Base all Results on List This commit removes the Get request and adds the ability to extract a single API version via a ExtractAPIVersion function. This is done to handle cases where older Block Storage v2 API endpoints return a different style format than found in newer releases of Cinder. Since the result of a List request to the base API URL is the same across all releases, a consistent response can be retrieved. --- .../blockstorage/apiversions_test.go | 51 +++++++ openstack/blockstorage/apiversions/doc.go | 31 ++++ openstack/blockstorage/apiversions/errors.go | 23 +++ .../blockstorage/apiversions/requests.go | 13 ++ openstack/blockstorage/apiversions/results.go | 64 ++++++++ .../blockstorage/apiversions/testing/doc.go | 2 + .../apiversions/testing/fixtures.go | 141 ++++++++++++++++++ .../apiversions/testing/requests_test.go | 119 +++++++++++++++ openstack/blockstorage/apiversions/urls.go | 13 ++ openstack/blockstorage/v1/apiversions/doc.go | 12 +- 10 files changed, 467 insertions(+), 2 deletions(-) create mode 100644 acceptance/openstack/blockstorage/apiversions_test.go create mode 100644 openstack/blockstorage/apiversions/doc.go create mode 100644 openstack/blockstorage/apiversions/errors.go create mode 100644 openstack/blockstorage/apiversions/requests.go create mode 100644 openstack/blockstorage/apiversions/results.go create mode 100644 openstack/blockstorage/apiversions/testing/doc.go create mode 100644 openstack/blockstorage/apiversions/testing/fixtures.go create mode 100644 openstack/blockstorage/apiversions/testing/requests_test.go create mode 100644 openstack/blockstorage/apiversions/urls.go diff --git a/acceptance/openstack/blockstorage/apiversions_test.go b/acceptance/openstack/blockstorage/apiversions_test.go new file mode 100644 index 0000000000..1adf5c3e8b --- /dev/null +++ b/acceptance/openstack/blockstorage/apiversions_test.go @@ -0,0 +1,51 @@ +// +build acceptance blockstorage + +package blockstorage + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" +) + +func TestAPIVersionsList(t *testing.T) { + client, err := clients.NewBlockStorageV2Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := apiversions.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve API versions: %v", err) + } + + allVersions, err := apiversions.ExtractAPIVersions(allPages) + if err != nil { + t.Fatalf("Unable to extract API versions: %v", err) + } + + for _, v := range allVersions { + tools.PrintResource(t, v) + } +} + +func TestAPIVersionsGet(t *testing.T) { + client, err := clients.NewBlockStorageV2Client() + if err != nil { + t.Fatalf("Unable to create a blockstorage client: %v", err) + } + + allPages, err := apiversions.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to retrieve API versions: %v", err) + } + + v, err := apiversions.ExtractAPIVersion(allPages, "v3.0") + if err != nil { + t.Fatalf("Unable to extract API version: %v", err) + } + + tools.PrintResource(t, v) +} diff --git a/openstack/blockstorage/apiversions/doc.go b/openstack/blockstorage/apiversions/doc.go new file mode 100644 index 0000000000..05470516c4 --- /dev/null +++ b/openstack/blockstorage/apiversions/doc.go @@ -0,0 +1,31 @@ +/* +Package apiversions provides information and interaction with the different +API versions for the OpenStack Block Storage service, code-named Cinder. + +Example of Retrieving all API Versions + + allPages, err := apiversions.List(client).AllPages() + if err != nil { + panic("Unable to get API versions: %s", err) + } + + allVersions, err := apiversions.ExtractAPIVersions(allPages) + if err != nil { + panic("Unable to extract API versions: %s", err) + } + + for _, version := range versions { + fmt.Printf("%+v\n", version) + } + + +Example of Retrieving an API Version + + version, err := apiversions.Get(client, "v3").Extract() + if err != nil { + panic("Unable to get API version: %s", err) + } + + fmt.Printf("%+v\n", version) +*/ +package apiversions diff --git a/openstack/blockstorage/apiversions/errors.go b/openstack/blockstorage/apiversions/errors.go new file mode 100644 index 0000000000..8f0f7628de --- /dev/null +++ b/openstack/blockstorage/apiversions/errors.go @@ -0,0 +1,23 @@ +package apiversions + +import ( + "fmt" +) + +// ErrVersionNotFound is the error when the requested API version +// could not be found. +type ErrVersionNotFound struct{} + +func (e ErrVersionNotFound) Error() string { + return fmt.Sprintf("Unable to find requested API version") +} + +// ErrMultipleVersionsFound is the error when a request for an API +// version returns multiple results. +type ErrMultipleVersionsFound struct { + Count int +} + +func (e ErrMultipleVersionsFound) Error() string { + return fmt.Sprintf("Found %d API versions", e.Count) +} diff --git a/openstack/blockstorage/apiversions/requests.go b/openstack/blockstorage/apiversions/requests.go new file mode 100644 index 0000000000..d2d3851d1f --- /dev/null +++ b/openstack/blockstorage/apiversions/requests.go @@ -0,0 +1,13 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List lists all the Cinder API versions available to end-users. +func List(c *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { + return APIVersionPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/blockstorage/apiversions/results.go b/openstack/blockstorage/apiversions/results.go new file mode 100644 index 0000000000..08adcb72ca --- /dev/null +++ b/openstack/blockstorage/apiversions/results.go @@ -0,0 +1,64 @@ +package apiversions + +import ( + "time" + + "github.com/gophercloud/gophercloud/pagination" +) + +// APIVersion represents an API version for Cinder. +type APIVersion struct { + // ID is the unique identifier of the API version. + ID string `json:"id"` + + // MinVersion is the minimum microversion supported. + MinVersion string `json:"min_version"` + + // Status represents the status of the API version. + Status string `json:"status"` + + // Updated is the date the API version was updated. + Updated time.Time `json:"updated"` + + // Version is the current version and microversion. + Version string `json:"version"` +} + +// APIVersionPage is the page returned by a pager when traversing over a +// collection of API versions. +type APIVersionPage struct { + pagination.SinglePageBase +} + +// IsEmpty checks whether an APIVersionPage struct is empty. +func (r APIVersionPage) IsEmpty() (bool, error) { + is, err := ExtractAPIVersions(r) + return len(is) == 0, err +} + +// ExtractAPIVersions takes a collection page, extracts all of the elements, +// and returns them a slice of APIVersion structs. It is effectively a cast. +func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { + var s struct { + Versions []APIVersion `json:"versions"` + } + err := (r.(APIVersionPage)).ExtractInto(&s) + return s.Versions, err +} + +// ExtractAPIVersion takes a List result and extracts a single requested +// version, which is returned as an APIVersion +func ExtractAPIVersion(r pagination.Page, v string) (*APIVersion, error) { + allVersions, err := ExtractAPIVersions(r) + if err != nil { + return nil, err + } + + for _, version := range allVersions { + if version.ID == v { + return &version, nil + } + } + + return nil, ErrVersionNotFound{} +} diff --git a/openstack/blockstorage/apiversions/testing/doc.go b/openstack/blockstorage/apiversions/testing/doc.go new file mode 100644 index 0000000000..12e4bda0f9 --- /dev/null +++ b/openstack/blockstorage/apiversions/testing/doc.go @@ -0,0 +1,2 @@ +// apiversions_v1 +package testing diff --git a/openstack/blockstorage/apiversions/testing/fixtures.go b/openstack/blockstorage/apiversions/testing/fixtures.go new file mode 100644 index 0000000000..3be1ee66b4 --- /dev/null +++ b/openstack/blockstorage/apiversions/testing/fixtures.go @@ -0,0 +1,141 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const APIListResponse = ` +{ + "versions": [ + { + "id": "v1.0", + "links": [ + { + "href": "http://docs.openstack.org/", + "rel": "describedby", + "type": "text/html" + }, + { + "href": "http://localhost:8776/v1/", + "rel": "self" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.volume+json;version=1" + } + ], + "min_version": "", + "status": "DEPRECATED", + "updated": "2016-05-02T20:25:19Z", + "version": "" + }, + { + "id": "v2.0", + "links": [ + { + "href": "http://docs.openstack.org/", + "rel": "describedby", + "type": "text/html" + }, + { + "href": "http://localhost:8776/v2/", + "rel": "self" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.volume+json;version=1" + } + ], + "min_version": "", + "status": "SUPPORTED", + "updated": "2014-06-28T12:20:21Z", + "version": "" + }, + { + "id": "v3.0", + "links": [ + { + "href": "http://docs.openstack.org/", + "rel": "describedby", + "type": "text/html" + }, + { + "href": "http://localhost:8776/v3/", + "rel": "self" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.volume+json;version=1" + } + ], + "min_version": "3.0", + "status": "CURRENT", + "updated": "2016-02-08T12:20:21Z", + "version": "3.27" + } + ] +} +` + +const APIListOldResponse = ` +{ + "versions": [ + { + "status": "CURRENT", + "updated": "2012-01-04T11:33:21Z", + "id": "v1.0", + "links": [ + { + "href": "http://23.253.228.211:8776/v1/", + "rel": "self" + } + ] + }, + { + "status": "CURRENT", + "updated": "2012-11-21T11:33:21Z", + "id": "v2.0", + "links": [ + { + "href": "http://23.253.228.211:8776/v2/", + "rel": "self" + } + ] + } + ] +}` + +func MockListResponse(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", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, APIListResponse) + }) +} + +func MockListOldResponse(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", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, APIListOldResponse) + }) +} diff --git a/openstack/blockstorage/apiversions/testing/requests_test.go b/openstack/blockstorage/apiversions/testing/requests_test.go new file mode 100644 index 0000000000..ca9688243a --- /dev/null +++ b/openstack/blockstorage/apiversions/testing/requests_test.go @@ -0,0 +1,119 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListVersions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := apiversions.ExtractAPIVersions(allVersions) + th.AssertNoErr(t, err) + + expected := []apiversions.APIVersion{ + { + ID: "v1.0", + Status: "DEPRECATED", + Updated: time.Date(2016, 5, 2, 20, 25, 19, 0, time.UTC), + }, + { + ID: "v2.0", + Status: "SUPPORTED", + Updated: time.Date(2014, 6, 28, 12, 20, 21, 0, time.UTC), + }, + { + ID: "v3.0", + MinVersion: "3.0", + Status: "CURRENT", + Updated: time.Date(2016, 2, 8, 12, 20, 21, 0, time.UTC), + Version: "3.27", + }, + } + + th.AssertDeepEquals(t, expected, actual) +} + +func TestListOldVersions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListOldResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := apiversions.ExtractAPIVersions(allVersions) + th.AssertNoErr(t, err) + + expected := []apiversions.APIVersion{ + { + ID: "v1.0", + Status: "CURRENT", + Updated: time.Date(2012, 1, 4, 11, 33, 21, 0, time.UTC), + }, + { + ID: "v2.0", + Status: "CURRENT", + Updated: time.Date(2012, 11, 21, 11, 33, 21, 0, time.UTC), + }, + } + + th.AssertDeepEquals(t, expected, actual) +} + +func TestGetVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := apiversions.ExtractAPIVersion(allVersions, "v3.0") + th.AssertNoErr(t, err) + + expected := apiversions.APIVersion{ + ID: "v3.0", + MinVersion: "3.0", + Status: "CURRENT", + Updated: time.Date(2016, 2, 8, 12, 20, 21, 0, time.UTC), + Version: "3.27", + } + + th.AssertEquals(t, actual.ID, expected.ID) + th.AssertEquals(t, actual.Status, expected.Status) + th.AssertEquals(t, actual.Updated, expected.Updated) +} + +func TestGetOldVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListOldResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := apiversions.ExtractAPIVersion(allVersions, "v2.0") + th.AssertNoErr(t, err) + + expected := apiversions.APIVersion{ + ID: "v2.0", + MinVersion: "", + Status: "CURRENT", + Updated: time.Date(2012, 11, 21, 11, 33, 21, 0, time.UTC), + Version: "", + } + + th.AssertEquals(t, actual.ID, expected.ID) + th.AssertEquals(t, actual.Status, expected.Status) + th.AssertEquals(t, actual.Updated, expected.Updated) +} diff --git a/openstack/blockstorage/apiversions/urls.go b/openstack/blockstorage/apiversions/urls.go new file mode 100644 index 0000000000..d3ec8e661d --- /dev/null +++ b/openstack/blockstorage/apiversions/urls.go @@ -0,0 +1,13 @@ +package apiversions + +import ( + "net/url" + + "github.com/gophercloud/gophercloud" +) + +func listURL(c *gophercloud.ServiceClient) string { + u, _ := url.Parse(c.ServiceURL("")) + u.Path = "/" + return u.String() +} diff --git a/openstack/blockstorage/v1/apiversions/doc.go b/openstack/blockstorage/v1/apiversions/doc.go index e3af39f513..f9355ab622 100644 --- a/openstack/blockstorage/v1/apiversions/doc.go +++ b/openstack/blockstorage/v1/apiversions/doc.go @@ -1,3 +1,11 @@ -// Package apiversions provides information and interaction with the different -// API versions for the OpenStack Block Storage service, code-named Cinder. +/* +Package apiversions provides information and interaction with the different +API versions for the OpenStack Block Storage service, code-named Cinder. + +This package is deprecated and should only be used in environments where the +older API version format is expected. + +Consider using the gophercloud/openstack/blockstorage/apiversions package +instead. +*/ package apiversions From 56696251507ee8be3173ebce08130c1f23c04348 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 12 Jan 2020 20:55:00 -0700 Subject: [PATCH 0939/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d058d81125..ded060fb1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ IMPROVEMENTS * 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) BUG FIXES From 35cc70865ed282b7973c52b43f2474868307658e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 13 Jan 2020 17:46:37 -0700 Subject: [PATCH 0940/2296] Add convenient access to error's status code (#1820) * Generic error interface to get status code * Rename GenericError interface Co-authored-by: Ludovic Lamarche --- errors.go | 17 +++++++++++++++++ testing/errors_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 testing/errors_test.go diff --git a/errors.go b/errors.go index 0bcb3af7f0..eb899a4a08 100644 --- a/errors.go +++ b/errors.go @@ -92,6 +92,23 @@ func (e ErrUnexpectedResponseCode) Error() string { return e.choseErrString() } +// GetStatusCode returns the actual status code of the error. +func (e ErrUnexpectedResponseCode) GetStatusCode() int { + return e.Actual +} + +// StatusCodeError is a convenience interface to easily allow access to the +// status code field of the various ErrDefault* types. +// +// By using this interface, you only have to make a single type cast of +// the returned error to err.(StatusCodeError) and then call GetStatusCode() +// instead of having a large switch statement checking for each of the +// ErrDefault* types. +type StatusCodeError interface { + Error() string + GetStatusCode() int +} + // ErrDefault400 is the default error type returned on a 400 HTTP response code. type ErrDefault400 struct { ErrUnexpectedResponseCode diff --git a/testing/errors_test.go b/testing/errors_test.go new file mode 100644 index 0000000000..4a42790af8 --- /dev/null +++ b/testing/errors_test.go @@ -0,0 +1,24 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestGetResponseCode(t *testing.T) { + respErr := gophercloud.ErrUnexpectedResponseCode{ + URL: "http://example.com", + Method: "GET", + Expected: []int{200}, + Actual: 404, + Body: nil, + } + + var err404 error = gophercloud.ErrDefault404{ErrUnexpectedResponseCode: respErr} + + err, ok := err404.(gophercloud.StatusCodeError) + th.AssertEquals(t, true, ok) + th.AssertEquals(t, err.GetStatusCode(), 404) +} From 572d2afb743f2de027616a151aa419b9e56a2b92 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 13 Jan 2020 17:47:21 -0700 Subject: [PATCH 0941/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ded060fb1e..44f36337cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ IMPROVEMENTS * 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) BUG FIXES From b3ae963bb2a802f6b95ffe8a85df22d626971b35 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 13 Jan 2020 17:48:26 -0700 Subject: [PATCH 0942/2296] Compute v2: Add Pagination Support to Usage (#1819) This commit adds pagination support to the compute v2 usage extension. --- .../compute/v2/extensions/usage/requests.go | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/openstack/compute/v2/extensions/usage/requests.go b/openstack/compute/v2/extensions/usage/requests.go index 77312227f6..eb36f59b7e 100644 --- a/openstack/compute/v2/extensions/usage/requests.go +++ b/openstack/compute/v2/extensions/usage/requests.go @@ -15,6 +15,14 @@ type SingleTenantOpts struct { // The beginning time to calculate usage statistics on compute and storage resources. Start *time.Time `q:"start"` + + // Limit limits the amount of results returned by the API. + // This requires the client to be set to microversion 2.40 or later. + Limit int `q:"limit"` + + // Marker instructs the API call where to start listing from. + // This requires the client to be set to microversion 2.40 or later. + Marker string `q:"marker"` } // SingleTenantOptsBuilder allows extensions to add additional parameters to the @@ -25,7 +33,13 @@ type SingleTenantOptsBuilder interface { // ToUsageSingleTenantQuery formats a SingleTenantOpts into a query string. func (opts SingleTenantOpts) ToUsageSingleTenantQuery() (string, error) { - params := make(url.Values) + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + if opts.Start != nil { params.Add("start", opts.Start.Format(gophercloud.RFC3339MilliNoZ)) } @@ -34,7 +48,7 @@ func (opts SingleTenantOpts) ToUsageSingleTenantQuery() (string, error) { params.Add("end", opts.End.Format(gophercloud.RFC3339MilliNoZ)) } - q := &url.URL{RawQuery: params.Encode()} + q = &url.URL{RawQuery: params.Encode()} return q.String(), nil } @@ -63,6 +77,14 @@ type AllTenantsOpts struct { // The beginning time to calculate usage statistics on compute and storage resources. Start *time.Time `q:"start"` + + // Limit limits the amount of results returned by the API. + // This requires the client to be set to microversion 2.40 or later. + Limit int `q:"limit"` + + // Marker instructs the API call where to start listing from. + // This requires the client to be set to microversion 2.40 or later. + Marker string `q:"marker"` } // AllTenantsOptsBuilder allows extensions to add additional parameters to the @@ -73,7 +95,13 @@ type AllTenantsOptsBuilder interface { // ToUsageAllTenantsQuery formats a AllTenantsOpts into a query string. func (opts AllTenantsOpts) ToUsageAllTenantsQuery() (string, error) { - params := make(url.Values) + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + if opts.Start != nil { params.Add("start", opts.Start.Format(gophercloud.RFC3339MilliNoZ)) } @@ -86,7 +114,7 @@ func (opts AllTenantsOpts) ToUsageAllTenantsQuery() (string, error) { params.Add("detailed", "1") } - q := &url.URL{RawQuery: params.Encode()} + q = &url.URL{RawQuery: params.Encode()} return q.String(), nil } From 91ae4a777351002940bd5a5cdfd78f3e09bf8edd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 13 Jan 2020 17:50:01 -0700 Subject: [PATCH 0943/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44f36337cf..636491be46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ IMPROVEMENTS * 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) BUG FIXES From 1aa0976bb8dd5b5705b626f6871229e334c8d5c3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 13 Jan 2020 17:50:12 -0700 Subject: [PATCH 0944/2296] Acc Tests: Re-enable TestVolumeActionsUploadImageDestroy (#1519) --- .../openstack/blockstorage/extensions/volumeactions_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index ec19cb8ea0..607a70c02f 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -14,8 +14,6 @@ import ( ) func TestVolumeActionsUploadImageDestroy(t *testing.T) { - t.Skip("This test is diabled because it sometimes fails in OpenLab") - blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) From 75e9c9cd0210ba7eb5ac12d35299a9c69c85dfab Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Mon, 13 Jan 2020 16:58:49 -0800 Subject: [PATCH 0945/2296] Add list resource providers API for placement service (#1815) * Add list resource providers API for placement service * fix variable capitalization * remove change in go.mod * Fix review comments --- acceptance/clients/clients.go | 21 +++++ acceptance/openstack/placement/v1/pkg.go | 2 + .../placement/v1/resourceproviders_test.go | 25 +++++ go.mod | 1 + openstack/client.go | 5 + .../placement/v1/resourceproviders/doc.go | 21 +++++ .../v1/resourceproviders/requests.go | 60 ++++++++++++ .../placement/v1/resourceproviders/results.go | 51 +++++++++++ .../v1/resourceproviders/testing/doc.go | 2 + .../v1/resourceproviders/testing/fixtures.go | 91 +++++++++++++++++++ .../testing/requests_test.go | 38 ++++++++ .../placement/v1/resourceproviders/urls.go | 11 +++ 12 files changed, 328 insertions(+) create mode 100644 acceptance/openstack/placement/v1/pkg.go create mode 100644 acceptance/openstack/placement/v1/resourceproviders_test.go create mode 100644 openstack/placement/v1/resourceproviders/doc.go create mode 100644 openstack/placement/v1/resourceproviders/requests.go create mode 100644 openstack/placement/v1/resourceproviders/results.go create mode 100644 openstack/placement/v1/resourceproviders/testing/doc.go create mode 100644 openstack/placement/v1/resourceproviders/testing/fixtures.go create mode 100644 openstack/placement/v1/resourceproviders/testing/requests_test.go create mode 100644 openstack/placement/v1/resourceproviders/urls.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index e8490edd1c..dedc59d7b9 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -715,3 +715,24 @@ func NewOrchestrationV1Client() (*gophercloud.ServiceClient, error) { Region: os.Getenv("OS_REGION_NAME"), }) } + +// NewPlacementV1Client returns a *ServiceClient for making calls +// to the OpenStack Placement API. An error will be returned +// if authentication or client creation was not possible. +func NewPlacementV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewPlacementV1(client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} diff --git a/acceptance/openstack/placement/v1/pkg.go b/acceptance/openstack/placement/v1/pkg.go new file mode 100644 index 0000000000..d8a8b16cb9 --- /dev/null +++ b/acceptance/openstack/placement/v1/pkg.go @@ -0,0 +1,2 @@ +// Package placement contains acceptance tests for the Openstack Placement service. +package v1 diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go new file mode 100644 index 0000000000..836726137f --- /dev/null +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -0,0 +1,25 @@ +package v1 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestResourceProviderList(t *testing.T) { + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + allPages, err := resourceproviders.List(client, resourceproviders.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allResourceProviders, err := resourceproviders.ExtractResourceProviders(allPages) + th.AssertNoErr(t, err) + + for _, v := range allResourceProviders { + tools.PrintResource(t, v) + } +} diff --git a/go.mod b/go.mod index 46e16507dc..655f3c8b4c 100644 --- a/go.mod +++ b/go.mod @@ -11,3 +11,4 @@ require ( gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.2.7 ) + diff --git a/openstack/client.go b/openstack/client.go index e77584157f..4a938b7d8a 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -473,3 +473,8 @@ func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.Endp func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { return initClientOpts(client, eo, "workflowv2") } + +// NewPlacementV1 creates a ServiceClient that may be used with the placement package. +func NewPlacementV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + return initClientOpts(client, eo, "placement") +} diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go new file mode 100644 index 0000000000..fcafd93ce4 --- /dev/null +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -0,0 +1,21 @@ +/* +Package resourceproviders lists all resource providers from the OpenStack Placement service. + +Example to list resource providers + + allPages, err := resourceproviders.List(placementClient, resourceproviders.ListOpts{}).AllPages() + if err != nil { + panic(err) + } + + allResourceProviders, err := resourceproviders.ExtractResourceProviders(allPages) + if err != nil { + panic(err) + } + + for _, r := range allResourceProviders { + fmt.Printf("%+v\n", r) + } + +*/ +package resourceproviders diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go new file mode 100644 index 0000000000..70f20b11f8 --- /dev/null +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -0,0 +1,60 @@ +package resourceproviders + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToResourceProviderListQuery() (string, error) +} + +// ListOpts allows the filtering resource providers. Filtering is achieved by +// passing in struct field values that map to the resource provider attributes +// you want to see returned. +type ListOpts struct { + // Name is the name of the resource provider to filter the list + Name string `q:"name"` + + // UUID is the uuid of the resource provider to filter the list + UUID string `q:"uuid"` + + // MemberOf is a string representing aggregate uuids to filter or exclude from the list + MemberOf string `q:"member_of"` + + // Resources is a comma-separated list of string indicating an amount of resource + // of a specified class that a provider must have the capacity and availability to serve + Resources string `q:"resources"` + + // InTree is a string that represents a resource provider UUID. The returned resource + // providers will be in the same provider tree as the specified provider. + InTree string `q:"in_tree"` + + // Required is comma-delimited list of string trait names. + Required string `q:"required"` +} + +// ToResourceProviderListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToResourceProviderListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list resource providers. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := resourceProvidersListURL(client) + + if opts != nil { + query, err := opts.ToResourceProviderListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ResourceProvidersPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go new file mode 100644 index 0000000000..85eee0e1ac --- /dev/null +++ b/openstack/placement/v1/resourceproviders/results.go @@ -0,0 +1,51 @@ +package resourceproviders + +import "github.com/gophercloud/gophercloud/pagination" + +type ResourceProviderLinks struct { + Href string `json:"href"` + Rel string `json:"rel"` +} + +// ResourceProvider are entities which provider consumable inventory of one or more classes of resource +type ResourceProvider struct { + // Generation is a consistent view marker that assists with the management of concurrent resource provider updates. + Generation int `json:"generation"` + + // UUID of a resource provider. + UUID string `json:"uuid"` + + // Links is a list of links associated with one resource provider. + Links []ResourceProviderLinks `json:"links"` + + // Name of one resource provider. + Name string `json:"name"` + + // The ParentProviderUUID contains the UUID of the immediate parent of the resource provider. + // Requires microversion 1.14 or above + ParentProviderUUID string `json:"parent_provider_uuid"` + + // The RootProviderUUID contains the read-only UUID of the top-most provider in this provider tree. + // Requires microversion 1.14 or above + RootProviderUUID string `json:"root_provider_uuid"` +} + +// ResourceProvidersPage contains a single page of all resource providers from a List call. +type ResourceProvidersPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines if a ResourceProvidersPage contains any results. +func (page ResourceProvidersPage) IsEmpty() (bool, error) { + resourceProviders, err := ExtractResourceProviders(page) + return len(resourceProviders) == 0, err +} + +// ExtractResourceProviders returns a slice of ResourceProvider from a List operation. +func ExtractResourceProviders(r pagination.Page) ([]ResourceProvider, error) { + var s struct { + ResourceProviders []ResourceProvider `json:"resource_providers"` + } + err := (r.(ResourceProvidersPage)).ExtractInto(&s) + return s.ResourceProviders, err +} diff --git a/openstack/placement/v1/resourceproviders/testing/doc.go b/openstack/placement/v1/resourceproviders/testing/doc.go new file mode 100644 index 0000000000..4726ab6a6a --- /dev/null +++ b/openstack/placement/v1/resourceproviders/testing/doc.go @@ -0,0 +1,2 @@ +// placement resource providers +package testing diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures.go new file mode 100644 index 0000000000..79b1ec62bc --- /dev/null +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -0,0 +1,91 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" + + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ResourceProvidersBody = ` +{ + "resource_providers": [ + { + "generation": 1, + "uuid": "99c09379-6e52-4ef8-9a95-b9ce6f68452e", + "links": [ + { + "href": "/resource_providers/99c09379-6e52-4ef8-9a95-b9ce6f68452e", + "rel": "self" + } + ], + "name": "vgr.localdomain", + "parent_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8", + "root_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8" + }, + { + "generation": 2, + "uuid": "d0b381e9-8761-42de-8e6c-bba99a96d5f5", + "links": [ + { + "href": "/resource_providers/d0b381e9-8761-42de-8e6c-bba99a96d5f5", + "rel": "self" + } + ], + "name": "pony1", + "parent_provider_uuid": null, + "root_provider_uuid": "d0b381e9-8761-42de-8e6c-bba99a96d5f5" + } + ] +} +` + +var ExpectedResourceProvider1 = resourceproviders.ResourceProvider{ + Generation: 1, + UUID: "99c09379-6e52-4ef8-9a95-b9ce6f68452e", + Links: []resourceproviders.ResourceProviderLinks{ + { + Href: "/resource_providers/99c09379-6e52-4ef8-9a95-b9ce6f68452e", + Rel: "self", + }, + }, + Name: "vgr.localdomain", + ParentProviderUUID: "542df8ed-9be2-49b9-b4db-6d3183ff8ec8", + RootProviderUUID: "542df8ed-9be2-49b9-b4db-6d3183ff8ec8", +} + +var ExpectedResourceProvider2 = resourceproviders.ResourceProvider{ + Generation: 2, + UUID: "d0b381e9-8761-42de-8e6c-bba99a96d5f5", + Links: []resourceproviders.ResourceProviderLinks{ + { + Href: "/resource_providers/d0b381e9-8761-42de-8e6c-bba99a96d5f5", + Rel: "self", + }, + }, + Name: "pony1", + ParentProviderUUID: "", + RootProviderUUID: "d0b381e9-8761-42de-8e6c-bba99a96d5f5", +} + +var ExpectedResourceProviders = []resourceproviders.ResourceProvider{ + ExpectedResourceProvider1, + ExpectedResourceProvider2, +} + +func HandleResourceProviderList(t *testing.T) { + th.Mux.HandleFunc("/resource_providers", + 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, ResourceProvidersBody) + }) +} diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go new file mode 100644 index 0000000000..532b31669f --- /dev/null +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -0,0 +1,38 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" + + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListResourceProviders(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderList(t) + + count := 0 + err := resourceproviders.List(fake.ServiceClient(), resourceproviders.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := resourceproviders.ExtractResourceProviders(page) + if err != nil { + t.Errorf("Failed to extract resource providers: %v", err) + return false, err + } + th.AssertDeepEquals(t, ExpectedResourceProviders, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go new file mode 100644 index 0000000000..6b60f03614 --- /dev/null +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -0,0 +1,11 @@ +package resourceproviders + +import "github.com/gophercloud/gophercloud" + +const ( + apiName = "resource_providers" +) + +func resourceProvidersListURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiName) +} From 32df808940b0d853622c4e0a8b0c988e48a97ce4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 13 Jan 2020 17:59:40 -0700 Subject: [PATCH 0946/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 636491be46..199563a123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ IMPROVEMENTS * 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) BUG FIXES From 7cb8183c13bd769728a0af887b438c26ef31bdd5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 13 Jan 2020 18:00:19 -0700 Subject: [PATCH 0947/2296] Update resourceproviders_test.go --- acceptance/openstack/placement/v1/resourceproviders_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 836726137f..28c06e4bb5 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -10,6 +10,8 @@ import ( ) func TestResourceProviderList(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) From 79f393baede6563f25e28015afe80b4d31d9bbeb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 13 Jan 2020 18:15:58 -0700 Subject: [PATCH 0948/2296] Update resourceproviders_test.go --- acceptance/openstack/placement/v1/resourceproviders_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 28c06e4bb5..16cd33e181 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -11,7 +11,7 @@ import ( func TestResourceProviderList(t *testing.T) { clients.RequireAdmin(t) - + client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) From e84b9648ab97e1d38e4794c7ecbc9f30ac5d0207 Mon Sep 17 00:00:00 2001 From: Fabian Ruff Date: Tue, 14 Jan 2020 20:18:27 +0100 Subject: [PATCH 0949/2296] Allow for custom options in loadbalancer pool CreateMember api call (#1822) Fixes #1821 --- openstack/loadbalancer/v2/pools/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 79265d9bb5..8328c65256 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -283,7 +283,7 @@ func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) } // CreateMember will create and associate a Member with a particular Pool. -func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMemberOpts) (r CreateMemberResult) { +func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMemberOptsBuilder) (r CreateMemberResult) { b, err := opts.ToMemberCreateMap() if err != nil { r.Err = err From c2ed05a2271d2cd15d802372e69bee73dc22f64d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 14 Jan 2020 12:19:52 -0700 Subject: [PATCH 0950/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 199563a123..f410c2113a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ IMPROVEMENTS * 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) BUG FIXES From 1236c25f3850ef4beb3dedfe0ba844192e57c2a2 Mon Sep 17 00:00:00 2001 From: Fabian Ruff Date: Wed, 15 Jan 2020 02:21:15 +0100 Subject: [PATCH 0951/2296] Add missing options to CreateMemberOpts (#1824) For #1823 --- openstack/loadbalancer/v2/pools/requests.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 8328c65256..04df4ddb85 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -275,6 +275,15 @@ type CreateMemberOpts struct { // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Is the member a backup? Backup members only receive traffic when all non-backup members are down. + Backup *bool `json:"backup,omitempty"` + + // An alternate IP address used for health monitoring a backend member. + MonitorAddress string `json:"monitor_address,omitempty"` + + // An alternate protocol port used for health monitoring a backend member. + MonitorPort *int `json:"monitor_port,omitempty"` } // ToMemberCreateMap builds a request body from CreateMemberOpts. From 6c729b2e59f52b47b5b8ac0f1fd839b6a12be0ec Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 14 Jan 2020 18:22:48 -0700 Subject: [PATCH 0952/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f410c2113a..274097b8d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ IMPROVEMENTS * 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) BUG FIXES From 7daa96f5aa5a6cf1ff4c9cd878081af6f642e525 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 14 Jan 2020 18:23:35 -0700 Subject: [PATCH 0953/2296] Trust basic acc test (#1818) * Identity V3: add Trust Create and Extract funcions Implement Create and Extract functions for v3/extenstions/trusts package with unit tests and docs. This commit also adds Role struct to this package. * Acceptance: add Identity V3 Trusts CRUD test Add TestTrustCRUD acceptance test with some helpers to create and delete trusts. * Identity V3: fix Trust Create options Remove required tag from the "impersonation" argument since it's boolean so we can just don't set "omitempty" tag to always send it in the Create request. * Acc Tests: Fix Identity Trust test Co-authored-by: Andrei Ozerov --- acceptance/openstack/identity/v3/identity.go | 26 +++++ .../openstack/identity/v3/trusts_test.go | 96 +++++++++++++++++++ .../identity/v3/extensions/trusts/doc.go | 2 +- .../identity/v3/extensions/trusts/requests.go | 2 +- .../v3/extensions/trusts/testing/fixtures.go | 4 +- .../trusts/testing/requests_test.go | 3 +- 6 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 acceptance/openstack/identity/v3/trusts_test.go diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 718e5bc7ee..133cf1d765 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -6,6 +6,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" @@ -342,3 +343,28 @@ func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, err return role, nil } + +// CreateTrust will create a trust with the provided options. +// An error will be returned if the trust was unable to be created. +func CreateTrust(t *testing.T, client *gophercloud.ServiceClient, createOpts trusts.CreateOpts) (*trusts.Trust, error) { + trust, err := trusts.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created trust %s", trust.ID) + + return trust, nil +} + +// DeleteTrust will delete a trust by ID. A fatal error will occur if +// the trust failed to be deleted. This works best when using it as +// a deferred function. +func DeleteTrust(t *testing.T, client *gophercloud.ServiceClient, trustID string) { + err := trusts.Delete(client, trustID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete trust %s: %v", trustID, err) + } + + t.Logf("Deleted trust: %s", trustID) +} diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/acceptance/openstack/identity/v3/trusts_test.go new file mode 100644 index 0000000000..afe43fdba0 --- /dev/null +++ b/acceptance/openstack/identity/v3/trusts_test.go @@ -0,0 +1,96 @@ +// +build acceptance identity trusts + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" + "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/openstack/identity/v3/users" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestTrustCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + // Generate a token and obtain the Admin user's ID from it. + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + authOptions := tokens.AuthOptions{ + Username: ao.Username, + Password: ao.Password, + DomainName: ao.DomainName, + DomainID: ao.DomainID, + } + + token, err := tokens.Create(client, &authOptions).Extract() + th.AssertNoErr(t, err) + adminUser, err := tokens.Get(client, token.ID).ExtractUser() + th.AssertNoErr(t, err) + + // Get the admin and member role IDs. + adminRoleID := "" + memberRoleID := "" + allPages, err := roles.List(client, nil).AllPages() + th.AssertNoErr(t, err) + allRoles, err := roles.ExtractRoles(allPages) + th.AssertNoErr(t, err) + + for _, v := range allRoles { + if v.Name == "admin" { + adminRoleID = v.ID + } + + if v.Name == "member" { + memberRoleID = v.ID + } + } + + // Create a project to apply the trust. + trusteeProject, err := CreateProject(t, client, nil) + th.AssertNoErr(t, err) + defer DeleteProject(t, client, trusteeProject.ID) + + tools.PrintResource(t, trusteeProject) + + // Add the admin user to the trustee project. + assignOpts := roles.AssignOpts{ + UserID: adminUser.ID, + ProjectID: trusteeProject.ID, + } + + err = roles.Assign(client, adminRoleID, assignOpts).ExtractErr() + th.AssertNoErr(t, err) + + // Create a user as the trustee. + trusteeUserCreateOpts := users.CreateOpts{ + Password: "secret", + DomainID: "default", + } + trusteeUser, err := CreateUser(t, client, &trusteeUserCreateOpts) + th.AssertNoErr(t, err) + defer DeleteUser(t, client, trusteeUser.ID) + + // Create a trust. + trust, err := CreateTrust(t, client, trusts.CreateOpts{ + TrusteeUserID: trusteeUser.ID, + TrustorUserID: adminUser.ID, + ProjectID: trusteeProject.ID, + Roles: []trusts.Role{ + { + ID: memberRoleID, + }, + }, + }) + th.AssertNoErr(t, err) + defer DeleteTrust(t, client, trust.ID) +} diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go index 7c1f2c9243..0ff8069930 100644 --- a/openstack/identity/v3/extensions/trusts/doc.go +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -38,7 +38,7 @@ Example to Create a Trust }, TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", - } + } trust, err := trusts.Create(identityClient, createOpts).Extract() if err != nil { diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index 5f2a4ee711..362c94b6a5 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -52,7 +52,7 @@ type CreateOptsBuilder interface { // CreateOpts provides options used to create a new trust. type CreateOpts struct { // Impersonation allows the trustee to impersonate the trustor. - Impersonation bool `json:"impersonation" required:"true"` + Impersonation bool `json:"impersonation"` // TrusteeUserID is a user who is capable of consuming the trust. TrusteeUserID string `json:"trustee_user_id" required:"true"` diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures.go b/openstack/identity/v3/extensions/trusts/testing/fixtures.go index a1e8984a55..ee6ea94455 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures.go +++ b/openstack/identity/v3/extensions/trusts/testing/fixtures.go @@ -14,7 +14,7 @@ const CreateRequest = ` { "trust": { "expires_at": "2019-12-01T14:00:00.999999Z", - "impersonation": true, + "impersonation": false, "allow_redelegation": true, "project_id": "9b71012f5a4a4aef9193f1995fe159b2", "roles": [ @@ -33,7 +33,7 @@ const CreateResponse = ` "trust": { "expires_at": "2019-12-01T14:00:00.999999Z", "id": "3422b7c113894f5d90665e1a79655e23", - "impersonation": true, + "impersonation": false, "redelegation_count": 10, "project_id": "9b71012f5a4a4aef9193f1995fe159b2", "remaining_uses": null, diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index 5c24e2cde5..b84fdda1f8 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -81,7 +81,6 @@ func TestCreateTrust(t *testing.T) { expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC) result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{ ExpiresAt: &expiresAt, - Impersonation: true, AllowRedelegation: true, ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", Roles: []trusts.Role{ @@ -95,7 +94,7 @@ func TestCreateTrust(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, "3422b7c113894f5d90665e1a79655e23", result.ID) - th.AssertEquals(t, true, result.Impersonation) + th.AssertEquals(t, false, result.Impersonation) th.AssertEquals(t, 10, result.RedelegationCount) } From 1adfef53562e7baff93c2142032388859b5da2f4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 14 Jan 2020 18:24:54 -0700 Subject: [PATCH 0954/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 274097b8d8..cf59c643ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ IMPROVEMENTS * 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) BUG FIXES From 5391bb776e583100ba7a161ce4e6ccb07e174a6c Mon Sep 17 00:00:00 2001 From: Roderick Schaefer Date: Thu, 16 Jan 2020 02:04:54 +0100 Subject: [PATCH 0955/2296] Fixes edge case having an lb endpoint with trailing version number (#1829) --- openstack/client.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openstack/client.go b/openstack/client.go index 4a938b7d8a..0b21beef93 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -3,6 +3,7 @@ package openstack import ( "fmt" "reflect" + "strings" "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" @@ -432,7 +433,11 @@ func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi // load balancer service. func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "load-balancer") - sc.ResourceBase = sc.Endpoint + "v2.0/" + + // Fixes edge case having an OpenStack lb endpoint with trailing version number. + endpoint := strings.Replace(sc.Endpoint, "v2.0/", "", -1) + + sc.ResourceBase = endpoint + "v2.0/" return sc, err } From 8f98547ac91a8613da9c72cc43ec7ae4badacd56 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 15 Jan 2020 18:06:18 -0700 Subject: [PATCH 0956/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf59c643ee..a2c56698da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ 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) ## 0.7.0 (December 3, 2019) From 46fdd1830e9a58a60f7489b973ed3789a48cb1f1 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 16 Jan 2020 02:12:25 +0100 Subject: [PATCH 0957/2296] Octavia: test batch members removal (#1827) --- .../loadbalancer/v2/loadbalancers_test.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index a0eadd54a1..51e9686ec6 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -366,7 +366,7 @@ func TestLoadbalancersCRUD(t *testing.T) { Weight: &newWeight, } batchMembers := []pools.BatchUpdateMemberOpts{memberOpts} - if err := pools.BatchUpdateMembers(lbClient, pool.ID, batchMembers).ExtractErr(); err != nil { + if err = pools.BatchUpdateMembers(lbClient, pool.ID, batchMembers).ExtractErr(); err != nil { t.Fatalf("Unable to batch update members") } @@ -379,6 +379,20 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newMember) + // delete members from the pool + if err = pools.BatchUpdateMembers(lbClient, pool.ID, []pools.BatchUpdateMemberOpts{}).ExtractErr(); err != nil { + t.Fatalf("Unable to delete members") + } + + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + t.Fatalf("Timed out waiting for loadbalancer to become active") + } + + pool, err = pools.Get(lbClient, pool.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, pool) + // Monitor monitor, err := CreateMonitor(t, lbClient, lb, newPool) th.AssertNoErr(t, err) From 84317c7f93fe24080c986f0c6acb85cbc07d9931 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 3 Feb 2020 12:46:51 +0300 Subject: [PATCH 0958/2296] LoadBalancer V2: add InsertHeaders into UpdateOpts (#1835) Add openstack/loadbalancer/v2/listeners.UpdateOpts.InsertHeaders. --- .../loadbalancer/v2/loadbalancers_test.go | 9 +++++++-- openstack/loadbalancer/v2/listeners/requests.go | 3 +++ .../v2/listeners/testing/fixtures.go | 16 ++++++++++++++-- .../v2/listeners/testing/requests_test.go | 4 ++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 51e9686ec6..a9a1be228d 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -168,9 +168,13 @@ func TestLoadbalancersCRUD(t *testing.T) { listenerName := "" listenerDescription := "" + listenerHeaders := map[string]string{ + "X-Forwarded-For": "true", + } updateListenerOpts := listeners.UpdateOpts{ - Name: &listenerName, - Description: &listenerDescription, + Name: &listenerName, + Description: &listenerDescription, + InsertHeaders: listenerHeaders, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) @@ -186,6 +190,7 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertEquals(t, newListener.Name, listenerName) th.AssertEquals(t, newListener.Description, listenerDescription) + th.AssertDeepEquals(t, newListener.InsertHeaders, listenerHeaders) listenerStats, err := listeners.GetStats(lbClient, listener.ID).Extract() th.AssertNoErr(t, err) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 22bd7d8ef1..3b3ed622a2 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -206,6 +206,9 @@ type UpdateOpts struct { // Time, in milliseconds, to wait for additional TCP packets for content inspection TimeoutTCPInspect *int `json:"timeout_tcp_inspect,omitempty"` + // A dictionary of optional headers to insert into the request before it is sent to the backend member. + InsertHeaders map[string]string `json:"insert_headers,omitempty"` + // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs *[]string `json:"allowed_cidrs,omitempty"` } diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index 1789791ec3..b74f2638b9 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -110,7 +110,11 @@ const PostUpdateListenerBody = ` "timeout_client_data": 181000, "timeout_member_data": 181000, "timeout_member_connect": 181000, - "timeout_tcp_inspect": 181000 + "timeout_tcp_inspect": 181000, + "insert_headers": { + "X-Forwarded-For": "true", + "X-Forwarded-Port": "false" + } } } ` @@ -180,6 +184,10 @@ var ( TimeoutMemberData: 181000, TimeoutMemberConnect: 181000, TimeoutTCPInspect: 181000, + InsertHeaders: map[string]string{ + "X-Forwarded-For": "true", + "X-Forwarded-Port": "false", + }, } ListenerStatsTree = listeners.Stats{ ActiveConnections: 0, @@ -277,7 +285,11 @@ func HandleListenerUpdateSuccessfully(t *testing.T) { "timeout_client_data": 181000, "timeout_member_data": 181000, "timeout_member_connect": 181000, - "timeout_tcp_inspect": 181000 + "timeout_tcp_inspect": 181000, + "insert_headers": { + "X-Forwarded-For": "true", + "X-Forwarded-Port": "false" + } } }`) diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 76345259d0..c4673cf92b 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -138,6 +138,10 @@ func TestUpdateListener(t *testing.T) { TimeoutClientData: &i181000, TimeoutMemberConnect: &i181000, TimeoutTCPInspect: &i181000, + InsertHeaders: map[string]string{ + "X-Forwarded-For": "true", + "X-Forwarded-Port": "false", + }, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From 109fdb27b96f8a8e2f2bbd0d65d4cebce6497ff1 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Mon, 3 Feb 2020 12:47:45 +0300 Subject: [PATCH 0959/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2c56698da..abb3e112a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ IMPROVEMENTS * 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] BUG FIXES From a7a1d217251f61b7ee6ae967fc6b277fa21de190 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 3 Feb 2020 12:14:53 -0700 Subject: [PATCH 0960/2296] Acc Tests: Removing Trusty (#1832) --- .zuul.yaml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index f1b824e237..b6649a3d6b 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -76,16 +76,6 @@ global_env: OS_BRANCH: stable/newton -- job: - name: gophercloud-acceptance-test-mitaka - parent: gophercloud-acceptance-test - description: | - Run gophercloud acceptance test on mitaka branch - vars: - global_env: - OS_BRANCH: stable/mitaka - nodeset: ubuntu-trusty - - project: name: gophercloud/gophercloud check: @@ -93,9 +83,6 @@ - gophercloud-unittest - gophercloud-acceptance-test - gophercloud-acceptance-test-ironic - recheck-mitaka: - jobs: - - gophercloud-acceptance-test-mitaka recheck-newton: jobs: - gophercloud-acceptance-test-newton From ca606dc5605ae99ce4828465153d3bc81ebc2d68 Mon Sep 17 00:00:00 2001 From: Andrei Ozerov Date: Wed, 5 Feb 2020 21:40:23 +0300 Subject: [PATCH 0961/2296] LoadBalancer V2: use map ptr for insert headers (#1837) * LoadBalancer V2: use map ptr for insert headers Use pointer to a map to allow unsetting headers for listener. * LoadBalancer V2: check empty insert headers Update TestLoadbalancersCRUD to check empty insert headers map. --- .../openstack/loadbalancer/v2/loadbalancers_test.go | 7 +++++-- openstack/loadbalancer/v2/listeners/requests.go | 2 +- .../loadbalancer/v2/listeners/testing/requests_test.go | 9 +++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index a9a1be228d..2b63fed177 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -174,7 +174,7 @@ func TestLoadbalancersCRUD(t *testing.T) { updateListenerOpts := listeners.UpdateOpts{ Name: &listenerName, Description: &listenerDescription, - InsertHeaders: listenerHeaders, + InsertHeaders: &listenerHeaders, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) @@ -303,9 +303,11 @@ func TestLoadbalancersCRUD(t *testing.T) { defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) - // Update listener's default pool ID + // Update listener's default pool ID and remove headers + listenerHeaders = map[string]string{} updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &pool.ID, + InsertHeaders: &listenerHeaders, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) @@ -320,6 +322,7 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newListener) th.AssertEquals(t, newListener.DefaultPoolID, pool.ID) + th.AssertDeepEquals(t, newListener.InsertHeaders, listenerHeaders) // Remove listener's default pool ID emptyPoolID := "" diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 3b3ed622a2..2d2f71f24f 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -207,7 +207,7 @@ type UpdateOpts struct { TimeoutTCPInspect *int `json:"timeout_tcp_inspect,omitempty"` // A dictionary of optional headers to insert into the request before it is sent to the backend member. - InsertHeaders map[string]string `json:"insert_headers,omitempty"` + InsertHeaders *map[string]string `json:"insert_headers,omitempty"` // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs *[]string `json:"allowed_cidrs,omitempty"` diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index c4673cf92b..1cdb553ab1 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -130,6 +130,10 @@ func TestUpdateListener(t *testing.T) { i181000 := 181000 name := "NewListenerName" defaultPoolID := "" + insertHeaders := map[string]string{ + "X-Forwarded-For": "true", + "X-Forwarded-Port": "false", + } actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ Name: &name, ConnLimit: &i1001, @@ -138,10 +142,7 @@ func TestUpdateListener(t *testing.T) { TimeoutClientData: &i181000, TimeoutMemberConnect: &i181000, TimeoutTCPInspect: &i181000, - InsertHeaders: map[string]string{ - "X-Forwarded-For": "true", - "X-Forwarded-Port": "false", - }, + InsertHeaders: &insertHeaders, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From b52e4c3fc180eac2a1b23ec6000cee953e4a2cd7 Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Thu, 6 Feb 2020 20:36:45 -0800 Subject: [PATCH 0962/2296] Fix json struct tags (#1840) * openstack/baremetal/v1/ports: remove unsupported JSON required tags * networking/v2/extensions/lbaas/vips: remove duplicate JSON struct tag --- openstack/baremetal/v1/ports/requests.go | 4 ++-- openstack/networking/v2/extensions/lbaas/vips/results.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/baremetal/v1/ports/requests.go b/openstack/baremetal/v1/ports/requests.go index a5da3f8347..2b9aecaabb 100644 --- a/openstack/baremetal/v1/ports/requests.go +++ b/openstack/baremetal/v1/ports/requests.go @@ -182,8 +182,8 @@ const ( ) type UpdateOperation struct { - Op UpdateOp `json:"op,required"` - Path string `json:"path,required"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` Value interface{} `json:"value,omitempty"` } diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go index cb0994a7b2..c99ee1ef6e 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/results.go +++ b/openstack/networking/v2/extensions/lbaas/vips/results.go @@ -125,7 +125,7 @@ type commonResult struct { // Extract is a function that accepts a result and extracts a VirtualIP. func (r commonResult) Extract() (*VirtualIP, error) { var s struct { - VirtualIP *VirtualIP `json:"vip" json:"vip"` + VirtualIP *VirtualIP `json:"vip"` } err := r.ExtractInto(&s) return s.VirtualIP, err From 79d8b3435ff62e17f44d2a3d668994092cf7214c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 6 Feb 2020 21:39:10 -0700 Subject: [PATCH 0963/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index abb3e112a4..ffde6be3c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ 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) From 65905b4557d0b3dda5285f57b3715380f6675068 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 7 Feb 2020 09:54:13 -0700 Subject: [PATCH 0964/2296] Introspection v1: Add NUMA Topology (#1842) * to add the NUMA topology in gophercloud * Add the NUMA topology in gophercloud and the unit test for the same * To add Numa details in IntrospectionDataJsonSample in fixtures.go and test the details for the same in gophercloud * Refactor of NUMA Topology Co-authored-by: sana-aawan <55870484+sana-aawan@users.noreply.github.com> --- .../v1/introspection/results.go | 23 ++ .../v1/introspection/testing/fixtures.go | 310 +++++++++++------- .../v1/introspection/testing/results_test.go | 11 + 3 files changed, 233 insertions(+), 111 deletions(-) diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 1159ae705f..e0d21f3d8e 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -171,6 +171,7 @@ type Data struct { MemoryMB int `json:"memory_mb"` RootDisk RootDiskType `json:"root_disk"` Extra ExtraHardwareDataType `json:"extra"` + NUMATopology NUMATopology `json:"numa_topology"` } // Sub Types defined under Data and deeper in the structure @@ -264,6 +265,28 @@ type ExtraHardwareDataType struct { System ExtraHardwareDataSection `json:"system"` } +type NUMATopology struct { + CPUs []NUMACPU `json:"cpus"` + NICs []NUMANIC `json:"nics"` + RAM []NUMARAM `json:"ram"` +} + +type NUMACPU struct { + CPU int `json:"cpu"` + NUMANode int `json:"numa_node"` + ThreadSiblings []int `json:"thread_siblings"` +} + +type NUMANIC struct { + Name string `json:"name"` + NUMANode int `json:"numa_node"` +} + +type NUMARAM struct { + NUMANode int `json:"numa_node"` + SizeKB int `json:"size_kb"` +} + // UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { var list []interface{} diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index 4c5f537665..c158023bb9 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -69,122 +69,175 @@ const IntrospectionStatus = ` // IntrospectionDataJSONSample contains sample data reported by the introspection process. const IntrospectionDataJSONSample = ` { - "cpu_arch":"x86_64", - "macs":[ - "52:54:00:4e:3d:30" - ], - "root_disk":{ - "rotational":true, - "vendor":"0x1af4", - "name":"/dev/vda", - "hctl":null, - "wwn_vendor_extension":null, - "wwn_with_extension":null, - "model":"", - "wwn":null, - "serial":null, - "size":13958643712 - }, - "interfaces": { - "eth0": { - "ip":"172.24.42.100", - "mac":"52:54:00:4e:3d:30", - "pxe":true, - "client_id":null - } - }, - "cpus":2, - "boot_interface":"52:54:00:4e:3d:30", - "memory_mb":2048, - "ipmi_address":"192.167.2.134", - "inventory":{ - "bmc_address":"192.167.2.134", - "interfaces":[ - { - "lldp":[], - "product":"0x0001", - "vendor":"0x1af4", - "name":"eth1", - "has_carrier":true, - "ipv4_address":"172.24.42.101", - "client_id":null, - "mac_address":"52:54:00:47:20:4d" - }, - { - "lldp": [ - [1, "04112233aabbcc"], - [5, "737730312d646973742d31622d623132"] + "all_interfaces": { + "eth0": { + "client_id": null, + "ip": "172.24.42.100", + "lldp_processed": { + "switch_chassis_id": "11:22:33:aa:bb:cc", + "switch_system_name": "sw01-dist-1b-b12" + }, + "mac": "52:54:00:4e:3d:30", + "pxe": true + }, + "eth1": { + "client_id": null, + "ip": "172.24.42.101", + "mac": "52:54:00:47:20:4d", + "pxe": false + } + }, + "boot_interface": "52:54:00:4e:3d:30", + "cpu_arch": "x86_64", + "cpus": 2, + "error": null, + "interfaces": { + "eth0": { + "client_id": null, + "ip": "172.24.42.100", + "mac": "52:54:00:4e:3d:30", + "pxe": true + } + }, + "inventory": { + "bmc_address": "192.167.2.134", + "boot": { + "current_boot_mode": "bios", + "pxe_interface": "52:54:00:4e:3d:30" + }, + "cpu": { + "architecture": "x86_64", + "count": 2, + "flags": [ + "fpu", + "mmx", + "fxsr", + "sse", + "sse2" ], - "product":"0x0001", - "vendor":"0x1af4", - "name":"eth0", - "has_carrier":true, - "ipv4_address":"172.24.42.100", - "client_id":null, - "mac_address":"52:54:00:4e:3d:30" - } - ], - "disks":[ - { - "rotational":true, - "vendor":"0x1af4", - "name":"/dev/vda", - "hctl":null, - "wwn_vendor_extension":null, - "wwn_with_extension":null, - "model":"", - "wwn":null, - "serial":null, - "size":13958643712 - } - ], - "boot":{ - "current_boot_mode":"bios", - "pxe_interface":"52:54:00:4e:3d:30" - }, - "system_vendor":{ - "serial_number":"Not Specified", - "product_name":"Bochs", - "manufacturer":"Bochs" + "frequency": "2100.084" + }, + "disks": [ + { + "hctl": null, + "model": "", + "name": "/dev/vda", + "rotational": true, + "serial": null, + "size": 13958643712, + "vendor": "0x1af4", + "wwn": null, + "wwn_vendor_extension": null, + "wwn_with_extension": null + } + ], + "hostname": "myawesomehost", + "interfaces": [ + { + "client_id": null, + "has_carrier": true, + "ipv4_address": "172.24.42.101", + "lldp": [], + "mac_address": "52:54:00:47:20:4d", + "name": "eth1", + "product": "0x0001", + "vendor": "0x1af4" + }, + { + "client_id": null, + "has_carrier": true, + "ipv4_address": "172.24.42.100", + "lldp": [ + [ + 1, + "04112233aabbcc" + ], + [ + 5, + "737730312d646973742d31622d623132" + ] + ], + "mac_address": "52:54:00:4e:3d:30", + "name": "eth0", + "product": "0x0001", + "vendor": "0x1af4" + } + ], + "memory": { + "physical_mb": 2048, + "total": 2105864192 + }, + "system_vendor": { + "manufacturer": "Bochs", + "product_name": "Bochs", + "serial_number": "Not Specified" + } + }, + "ipmi_address": "192.167.2.134", + "local_gb": 12, + "macs": [ + "52:54:00:4e:3d:30" + ], + "memory_mb": 2048, + "root_disk": { + "hctl": null, + "model": "", + "name": "/dev/vda", + "rotational": true, + "serial": null, + "size": 13958643712, + "vendor": "0x1af4", + "wwn": null, + "wwn_vendor_extension": null, + "wwn_with_extension": null + } +} +` + +// IntrospectionNUMADataJSONSample contains NUMA sample data +// reported by the introspection process. +const IntrospectionNUMADataJSONSample = ` +{ + "numa_topology": { + "cpus": [ + { + "cpu": 6, + "numa_node": 1, + "thread_siblings": [ + 3, + 27 + ] }, - "memory":{ - "physical_mb":2048, - "total":2105864192 + { + "cpu": 10, + "numa_node": 0, + "thread_siblings": [ + 20, + 44 + ] + } + ], + "nics": [ + { + "name": "p2p1", + "numa_node": 0 }, - "cpu":{ - "count":2, - "frequency":"2100.084", - "flags": [ - "fpu", - "mmx", - "fxsr", - "sse", - "sse2" - ], - "architecture":"x86_64" - }, - "hostname": "myawesomehost" - }, - "error":null, - "local_gb":12, - "all_interfaces":{ - "eth1":{ - "ip":"172.24.42.101", - "mac":"52:54:00:47:20:4d", - "pxe":false, - "client_id":null + { + "name": "p2p2", + "numa_node": 1 + } + ], + "ram": [ + { + "numa_node": 0, + "size_kb": 99289532 }, - "eth0":{ - "ip":"172.24.42.100", - "mac":"52:54:00:4e:3d:30", - "pxe":true, - "client_id":null, - "lldp_processed":{ - "switch_chassis_id":"11:22:33:aa:bb:cc", - "switch_system_name":"sw01-dist-1b-b12" - } + { + "numa_node": 1, + "size_kb": 100663296 } - } + ] + } } ` @@ -324,6 +377,41 @@ var ( }, }, } + + IntrospectionNUMA = introspection.NUMATopology{ + CPUs: []introspection.NUMACPU{ + { + CPU: 6, + NUMANode: 1, + ThreadSiblings: []int{3, 27}, + }, + { + CPU: 10, + NUMANode: 0, + ThreadSiblings: []int{20, 44}, + }, + }, + NICs: []introspection.NUMANIC{ + { + Name: "p2p1", + NUMANode: 0, + }, + { + Name: "p2p2", + NUMANode: 1, + }, + }, + RAM: []introspection.NUMARAM{ + { + NUMANode: 0, + SizeKB: 99289532, + }, + { + NUMANode: 1, + SizeKB: 100663296, + }, + }, + } ) // HandleListIntrospectionsSuccessfully sets up the test server to respond to a server ListIntrospections request. diff --git a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go index 4717d961ba..82a251c058 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestLLDPTLVErrors(t *testing.T) { @@ -88,6 +89,16 @@ func TestExtraHardware(t *testing.T) { } } +func TestIntrospectionNUMA(t *testing.T) { + var output introspection.Data + err := json.Unmarshal([]byte(IntrospectionNUMADataJSONSample), &output) + if err != nil { + t.Fatalf("Failed to unmarshal NUMA Data: %s", err) + } + + th.CheckDeepEquals(t, IntrospectionNUMA, output.NUMATopology) +} + func TestHostnameInInventory(t *testing.T) { inventoryJson := `{ "bmc_address":"192.167.2.134", From 9aaf125d003c4aa5f39a9962717cc24b3a980be9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 7 Feb 2020 09:55:10 -0700 Subject: [PATCH 0965/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffde6be3c5..0a38a94a63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ IMPROVEMENTS * Added `MonitorPort` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) * Changed `Impersonation` to a non-required field in `identity/v3/extensions/trusts.CreateOpts` [GH-1818](https://github.com/gophercloud/gophercloud/pull/1818) * Added `InsertHeaders` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1835] +* Added `NUMATopology` to `baremetalintrospection/v1/introspection.Data` [GH-1842](https://github.com/gophercloud/gophercloud/pull/1842) BUG FIXES From 63bef1ef8e14d04f6858e35eb2dc129c18dc955d Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Sat, 8 Feb 2020 16:10:52 -0800 Subject: [PATCH 0966/2296] Add resource provider create API for placement. (#1841) * Add resource provider create API for placement. * update doc --- .../placement/v1/resourceproviders_test.go | 20 +++++++++ .../placement/v1/resourceproviders/doc.go | 14 +++++- .../v1/resourceproviders/requests.go | 44 +++++++++++++++++++ .../placement/v1/resourceproviders/results.go | 24 +++++++++- .../v1/resourceproviders/testing/fixtures.go | 28 ++++++++++++ .../testing/requests_test.go | 19 ++++++++ 6 files changed, 147 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 16cd33e181..8def92ef66 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -25,3 +25,23 @@ func TestResourceProviderList(t *testing.T) { tools.PrintResource(t, v) } } + +func TestResourceProviderCreate(t *testing.T) { + clients.RequireAdmin(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() + th.AssertNoErr(t, err) + + tools.PrintResource(t, resourceProvider) +} diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index fcafd93ce4..66a55a7dd8 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -1,5 +1,5 @@ /* -Package resourceproviders lists all resource providers from the OpenStack Placement service. +Package resourceproviders creates and lists all resource providers from the OpenStack Placement service. Example to list resource providers @@ -17,5 +17,17 @@ Example to list resource providers fmt.Printf("%+v\n", r) } +Example to create resource providers + + opts := resourceproviders.CreateOpts{ + Name: "new-rp", + UUID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", + } + + rp, err := resourceproviders.Create(placementClient, opts).Extract() + if err != nil { + panic(err) + } + */ package resourceproviders diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 70f20b11f8..2992af05e4 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -1,6 +1,8 @@ package resourceproviders import ( + "net/http" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -58,3 +60,45 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return ResourceProvidersPage{pagination.SinglePageBase(r)} }) } + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToResourceProviderCreateMap() (map[string]interface{}, error) +} + +// CreateOpts represents options used to create a resource provider. +type CreateOpts struct { + Name string `json:"name"` + UUID string `json:"uuid,omitempty"` +} + +// ToResourceProviderCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToResourceProviderCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + return b, nil +} + +// Create makes a request against the API to create a resource provider +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToResourceProviderCreateMap() + if err != nil { + r.Err = err + return + } + + var result *http.Response + + result, r.Err = client.Post(resourceProvidersListURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + if r.Err == nil { + r.Header = result.Header + } + + return +} diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index 85eee0e1ac..350410f621 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -1,6 +1,9 @@ package resourceproviders -import "github.com/gophercloud/gophercloud/pagination" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) type ResourceProviderLinks struct { Href string `json:"href"` @@ -30,6 +33,25 @@ type ResourceProvider struct { RootProviderUUID string `json:"root_provider_uuid"` } +// resourceProviderResult is the resposne of a base ResourceProvider result. +type resourceProviderResult struct { + gophercloud.Result +} + +// Extract interpets any resourceProviderResult-base result as a ResourceProvider. +func (r resourceProviderResult) Extract() (*ResourceProvider, error) { + var s ResourceProvider + err := r.ExtractInto(&s) + + return &s, err +} + +// CreateResult is the result of a Create operation. Call its Extract +// method to interpret it as a ResourceProvider. +type CreateResult 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 79b1ec62bc..e0807a6c6c 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -44,6 +44,22 @@ const ResourceProvidersBody = ` } ` +const ResourceProviderCreateBody = ` +{ + "generation": 1, + "uuid": "99c09379-6e52-4ef8-9a95-b9ce6f68452e", + "links": [ + { + "href": "/resource_providers/99c09379-6e52-4ef8-9a95-b9ce6f68452e", + "rel": "self" + } + ], + "name": "vgr.localdomain", + "parent_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8", + "root_provider_uuid": "542df8ed-9be2-49b9-b4db-6d3183ff8ec8" +} +` + var ExpectedResourceProvider1 = resourceproviders.ResourceProvider{ Generation: 1, UUID: "99c09379-6e52-4ef8-9a95-b9ce6f68452e", @@ -89,3 +105,15 @@ func HandleResourceProviderList(t *testing.T) { fmt.Fprintf(w, ResourceProvidersBody) }) } + +func HandleResourceProviderCreate(t *testing.T) { + th.Mux.HandleFunc("/resource_providers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ResourceProviderCreateBody) + }) +} diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 532b31669f..52b974d250 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -36,3 +36,22 @@ func TestListResourceProviders(t *testing.T) { t.Errorf("Expected 1 page, got %d", count) } } + +func TestCreateResourceProvider(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderCreate(t) + + expected := ExpectedResourceProvider1 + + opts := resourceproviders.CreateOpts{ + Name: ExpectedResourceProvider1.Name, + UUID: ExpectedResourceProvider1.UUID, + } + + actual, err := resourceproviders.Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, &expected, actual) +} From 742e8345ce042814ae8cfd8875129addbc743312 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 8 Feb 2020 17:11:35 -0700 Subject: [PATCH 0967/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a38a94a63..8243ac92b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ IMPROVEMENTS * Changed `Impersonation` to a non-required field in `identity/v3/extensions/trusts.CreateOpts` [GH-1818](https://github.com/gophercloud/gophercloud/pull/1818) * Added `InsertHeaders` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1835] * Added `NUMATopology` to `baremetalintrospection/v1/introspection.Data` [GH-1842](https://github.com/gophercloud/gophercloud/pull/1842) +* Added `placement/v1/resourceproviders.Create` [GH-1841](https://github.com/gophercloud/gophercloud/pull/1841) BUG FIXES From 7ae4f828bfbcc1a08edaf1e61baaa04f7860c37e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 8 Feb 2020 17:12:15 -0700 Subject: [PATCH 0968/2296] Update doc.go --- openstack/placement/v1/resourceproviders/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 66a55a7dd8..9d9bafe18b 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -22,7 +22,7 @@ Example to create resource providers opts := resourceproviders.CreateOpts{ Name: "new-rp", UUID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - } + } rp, err := resourceproviders.Create(placementClient, opts).Extract() if err != nil { From 21353468abed58d85242df15e1be5339ba985662 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 8 Feb 2020 17:12:41 -0700 Subject: [PATCH 0969/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8243ac92b7..faca6a8b85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.8.0 (Unreleased) +## 0.9.0 (Unreleased) + +## 0.8.0 (February 8, 2020) UPGRADE NOTES From 9fb9244fa670478167f62d9826cf7af6a6c3211b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 9 Feb 2020 19:27:40 -0700 Subject: [PATCH 0970/2296] Introspection v1: Cleaning up unit tests (#1844) --- .../v1/introspection/testing/fixtures.go | 146 ++++++++++++++++++ .../v1/introspection/testing/results_test.go | 141 ++--------------- 2 files changed, 158 insertions(+), 129 deletions(-) diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index c158023bb9..97070dbffc 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -194,6 +194,81 @@ const IntrospectionDataJSONSample = ` } ` +// IntrospectionExtraHardwareJSONSample contains extra hardware sample data +// reported by the introspection process. +const IntrospectionExtraHardwareJSONSample = ` +{ + "cpu": { + "logical": { + "number": 16 + }, + "physical": { + "clock": 2105032704, + "cores": 8, + "flags": "lm fpu fpu_exception wp vme de" + } + }, + "disk": { + "sda": { + "rotational": 1, + "vendor": "TEST" + } + }, + "firmware": { + "bios": { + "date": "01/01/1970", + "vendor": "test" + } + }, + "ipmi": { + "Fan1A RPM": { + "unit": "RPM", + "value": 3120 + }, + "Fan1B RPM": { + "unit": "RPM", + "value": 2280 + } + }, + "memory": { + "bank0": { + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" + }, + "bank1": { + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" + } + }, + "network": { + "em1": { + "Autonegotiate": "on", + "loopback": "off [fixed]" + }, + "p2p1": { + "Autonegotiate": "on", + "loopback": "off [fixed]" + } + }, + "system": { + "ipmi": { + "channel": 1 + }, + "kernel": { + "arch": "x86_64", + "version": "3.10.0" + }, + "motherboard": { + "vendor": "Test" + }, + "product": { + "name": "test", + "vendor": "Test" + } + } +} +` + // IntrospectionNUMADataJSONSample contains NUMA sample data // reported by the introspection process. const IntrospectionNUMADataJSONSample = ` @@ -378,6 +453,77 @@ var ( }, } + IntrospectionExtraHardware = introspection.ExtraHardwareDataType{ + CPU: introspection.ExtraHardwareDataSection{ + "logical": map[string]interface{}{ + "number": float64(16), + }, + "physical": map[string]interface{}{ + "clock": float64(2105032704), + "cores": float64(8), + "flags": "lm fpu fpu_exception wp vme de", + }, + }, + Disk: introspection.ExtraHardwareDataSection{ + "sda": map[string]interface{}{ + "rotational": float64(1), + "vendor": "TEST", + }, + }, + Firmware: introspection.ExtraHardwareDataSection{ + "bios": map[string]interface{}{ + "date": "01/01/1970", + "vendor": "test", + }, + }, + IPMI: introspection.ExtraHardwareDataSection{ + "Fan1A RPM": map[string]interface{}{ + "unit": "RPM", + "value": float64(3120), + }, + "Fan1B RPM": map[string]interface{}{ + "unit": "RPM", + "value": float64(2280), + }, + }, + Memory: introspection.ExtraHardwareDataSection{ + "bank0": map[string]interface{}{ + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", + }, + "bank1": map[string]interface{}{ + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", + }, + }, + Network: introspection.ExtraHardwareDataSection{ + "em1": map[string]interface{}{ + "Autonegotiate": "on", + "loopback": "off [fixed]", + }, + "p2p1": map[string]interface{}{ + "Autonegotiate": "on", + "loopback": "off [fixed]", + }, + }, + System: introspection.ExtraHardwareDataSection{ + "ipmi": map[string]interface{}{ + "channel": float64(1), + }, + "kernel": map[string]interface{}{ + "arch": "x86_64", + "version": "3.10.0", + }, + "motherboard": map[string]interface{}{ + "vendor": "Test", + }, + "product": map[string]interface{}{ + "name": "test", + "vendor": "Test", + }, + }, + } + IntrospectionNUMA = introspection.NUMATopology{ CPUs: []introspection.NUMACPU{ { diff --git a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go index 82a251c058..259015277b 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go @@ -20,73 +20,23 @@ func TestLLDPTLVErrors(t *testing.T) { var output introspection.LLDPTLVType err := json.Unmarshal([]byte(input), &output) if err == nil { - t.Errorf("No JSON parse error for invalid LLDP TLV %s", input) + t.Fatalf("No JSON parse error for invalid LLDP TLV %s", input) } + if !strings.Contains(err.Error(), "LLDP TLV") { - t.Errorf("Unexpected JSON parse error \"%s\" for invalid LLDP TLV %s", - err, input) + t.Fatalf("Unexpected JSON parse error \"%s\" for invalid LLDP TLV %s", err, input) } } } func TestExtraHardware(t *testing.T) { - extraJson := `{ - "cpu": { - "logical": {"number": 16}, - "physical": { - "clock": 2105032704, - "cores": 8, - "flags": "lm fpu fpu_exception wp vme de"} - }, - "disk": { - "sda": { - "rotational": 1, - "vendor": "TEST" - } - }, - "firmware": { - "bios": { - "date": "01/01/1970", - "vendor": "test" - } - }, - "ipmi": { - "Fan1A RPM": {"unit": "RPM", "value": 3120}, - "Fan1B RPM": {"unit": "RPM", "value": 2280} - }, - "memory": { - "bank0": { - "clock": 1600000000.0, - "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" - }, - "bank1": { - "clock": 1600000000.0, - "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" - } - }, - "network": { - "em1": { - "Autonegotiate": "on", - "loopback": "off [fixed]" - }, - "p2p1": { - "Autonegotiate": "on", - "loopback": "off [fixed]" - } - }, - "system": { - "ipmi": {"channel": 1}, - "kernel": {"arch": "x86_64", "version": "3.10.0"}, - "motherboard": {"vendor": "Test"}, - "product": {"name": "test", "vendor": "Test"} - } - }` - var output introspection.ExtraHardwareDataType - err := json.Unmarshal([]byte(extraJson), &output) + err := json.Unmarshal([]byte(IntrospectionExtraHardwareJSONSample), &output) if err != nil { - t.Errorf("Failed to unmarshal ExtraHardware data: %s", err) + t.Fatalf("Failed to unmarshal ExtraHardware data: %s", err) } + + th.CheckDeepEquals(t, IntrospectionExtraHardware, output) } func TestIntrospectionNUMA(t *testing.T) { @@ -100,78 +50,11 @@ func TestIntrospectionNUMA(t *testing.T) { } func TestHostnameInInventory(t *testing.T) { - inventoryJson := `{ - "bmc_address":"192.167.2.134", - "interfaces":[ - { - "lldp":[], - "product":"0x0001", - "vendor":"0x1af4", - "name":"eth1", - "has_carrier":true, - "ipv4_address":"172.24.42.101", - "client_id":null, - "mac_address":"52:54:00:47:20:4d" - }, - { - "lldp": [ - [1, "04112233aabbcc"], - [5, "737730312d646973742d31622d623132"] - ], - "product":"0x0001", - "vendor":"0x1af4", - "name":"eth0", - "has_carrier":true, - "ipv4_address":"172.24.42.100", - "client_id":null, - "mac_address":"52:54:00:4e:3d:30" - } - ], - "disks":[ - { - "rotational":true, - "vendor":"0x1af4", - "name":"/dev/vda", - "hctl":null, - "wwn_vendor_extension":null, - "wwn_with_extension":null, - "model":"", - "wwn":null, - "serial":null, - "size":13958643712 - } - ], - "boot":{ - "current_boot_mode":"bios", - "pxe_interface":"52:54:00:4e:3d:30" - }, - "system_vendor":{ - "serial_number":"Not Specified", - "product_name":"Bochs", - "manufacturer":"Bochs" - }, - "memory":{ - "physical_mb":2048, - "total":2105864192 - }, - "cpu":{ - "count":2, - "frequency":"2100.084", - "flags": [ - "fpu", - "mmx", - "fxsr", - "sse", - "sse2" - ], - "architecture":"x86_64" - }, - "hostname": "master-0" - }` - - var output introspection.InventoryType - err := json.Unmarshal([]byte(inventoryJson), &output) + var output introspection.Data + err := json.Unmarshal([]byte(IntrospectionDataJSONSample), &output) if err != nil { - t.Errorf("Failed to unmarshal Inventory data: %s", err) + t.Fatalf("Failed to unmarshal Inventory data: %s", err) } + + th.CheckDeepEquals(t, IntrospectionDataRes.Inventory.Hostname, "myawesomehost") } From 06e2df920248d332e8bf444edea034940ab49d53 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche Date: Thu, 13 Feb 2020 17:02:59 +0100 Subject: [PATCH 0971/2296] add os-instance-actions extension (#1848) --- .../v2/extensions/instanceactions/doc.go | 23 +++++ .../v2/extensions/instanceactions/request.go | 21 ++++ .../v2/extensions/instanceactions/results.go | 78 +++++++++++++++ .../extensions/instanceactions/testing/doc.go | 2 + .../instanceactions/testing/fixtures.go | 99 +++++++++++++++++++ .../instanceactions/testing/request_test.go | 48 +++++++++ .../v2/extensions/instanceactions/urls.go | 11 +++ 7 files changed, 282 insertions(+) create mode 100644 openstack/compute/v2/extensions/instanceactions/doc.go create mode 100644 openstack/compute/v2/extensions/instanceactions/request.go create mode 100644 openstack/compute/v2/extensions/instanceactions/results.go create mode 100644 openstack/compute/v2/extensions/instanceactions/testing/doc.go create mode 100644 openstack/compute/v2/extensions/instanceactions/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/instanceactions/testing/request_test.go create mode 100644 openstack/compute/v2/extensions/instanceactions/urls.go diff --git a/openstack/compute/v2/extensions/instanceactions/doc.go b/openstack/compute/v2/extensions/instanceactions/doc.go new file mode 100644 index 0000000000..3f9b813e40 --- /dev/null +++ b/openstack/compute/v2/extensions/instanceactions/doc.go @@ -0,0 +1,23 @@ +package instanceactions + +/* +Package instanceactions provides the ability to list or get a server instance-action. +Example: + + pages, err := instanceactions.List(client, "server-id").AllPages() + if err != nil { + panic("fail to get actions pages") + } + actions, err := instanceactions.ExtractInstanceActions(pages) + if err != nil { + panic("fail to list instance actions") + } + + for _, action := range actions { + action, err = instanceactions.Get(client, "server-id", action.RequestID).Extract() + if err != nil { + panic("fail to get instance action") + } + fmt.Println(action) + } +*/ diff --git a/openstack/compute/v2/extensions/instanceactions/request.go b/openstack/compute/v2/extensions/instanceactions/request.go new file mode 100644 index 0000000000..27c12829b3 --- /dev/null +++ b/openstack/compute/v2/extensions/instanceactions/request.go @@ -0,0 +1,21 @@ +package instanceactions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List makes a request against the API to list the servers actions. +func List(client *gophercloud.ServiceClient, id string) pagination.Pager { + return pagination.NewPager(client, ListURL(client, id), func(r pagination.PageResult) pagination.Page { + return InstanceActionPage{pagination.SinglePageBase(r)} + }) +} + +// Get makes a request against the API to get a server action. +func Get(client *gophercloud.ServiceClient, serverID, requestID string) (r InstanceActionResult) { + _, r.Err = client.Get(instanceActionsURL(client, serverID, requestID), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/instanceactions/results.go b/openstack/compute/v2/extensions/instanceactions/results.go new file mode 100644 index 0000000000..d39e050f9c --- /dev/null +++ b/openstack/compute/v2/extensions/instanceactions/results.go @@ -0,0 +1,78 @@ +package instanceactions + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// InstanceAction represents an instance action. +type InstanceAction struct { + Action string `json:"action"` + InstanceUUID string `json:"instance_uuid"` + Message string `json:"message"` + ProjectID string `json:"project_id"` + RequestID string `json:"request_id"` + StartTime time.Time `json:"-"` + UserID string `json:"user_id"` +} + +// UnmarshalJSON converts our JSON API response into our instance action struct +func (i *InstanceAction) UnmarshalJSON(b []byte) error { + type tmp InstanceAction + var s struct { + tmp + StartTime gophercloud.JSONRFC3339MilliNoZ `json:"start_time"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *i = InstanceAction(s.tmp) + + i.StartTime = time.Time(s.StartTime) + + return err +} + +// InstanceActionPage abstracts the raw results of making a ListInstanceActiones() 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 ExtractInstanceActions call. +type InstanceActionPage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if an InstanceActionPage contains no instance actions. +func (r InstanceActionPage) IsEmpty() (bool, error) { + instanceactions, err := ExtractInstanceActions(r) + return len(instanceactions) == 0, err +} + +// ListInstanceActiones() call, producing a map of instanceActions. +func ExtractInstanceActions(r pagination.Page) ([]InstanceAction, error) { + var resp []InstanceAction + err := ExtractInstanceActionsInto(r, &resp) + return resp, err +} + +type InstanceActionResult struct { + gophercloud.Result +} + +// Extract interprets any instanceActionResult as an InstanceAction, if possible. +func (r InstanceActionResult) Extract() (InstanceAction, error) { + var s InstanceAction + err := r.ExtractInto(&s) + return s, err +} + +func (r InstanceActionResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "instanceAction") +} + +func ExtractInstanceActionsInto(r pagination.Page, v interface{}) error { + return r.(InstanceActionPage).Result.ExtractIntoSlicePtr(v, "instanceActions") +} diff --git a/openstack/compute/v2/extensions/instanceactions/testing/doc.go b/openstack/compute/v2/extensions/instanceactions/testing/doc.go new file mode 100644 index 0000000000..011355b12c --- /dev/null +++ b/openstack/compute/v2/extensions/instanceactions/testing/doc.go @@ -0,0 +1,2 @@ +// instanceactions unit tests +package testing diff --git a/openstack/compute/v2/extensions/instanceactions/testing/fixtures.go b/openstack/compute/v2/extensions/instanceactions/testing/fixtures.go new file mode 100644 index 0000000000..bf45a41710 --- /dev/null +++ b/openstack/compute/v2/extensions/instanceactions/testing/fixtures.go @@ -0,0 +1,99 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListExpected represents an expected repsonse from a List request. +var ListExpected = []instanceactions.InstanceAction{ + { + Action: "stop", + InstanceUUID: "fcd19ef2-b593-40b1-90a5-fc31063fa95c", + Message: "", + ProjectID: "6f70656e737461636b20342065766572", + RequestID: "req-f8a59f03-76dc-412f-92c2-21f8612be728", + StartTime: time.Date(2018, 04, 25, 1, 26, 29, 000000, time.UTC), + UserID: "admin", + }, + { + Action: "create", + InstanceUUID: "fcd19ef2-b593-40b1-90a5-fc31063fa95c", + Message: "test", + ProjectID: "6f70656e737461636b20342065766572", + RequestID: "req-50189019-626d-47fb-b944-b8342af09679", + StartTime: time.Date(2018, 04, 25, 1, 26, 25, 000000, time.UTC), + UserID: "admin", + }, +} + +// HandleAddressListSuccessfully sets up the test server to respond to a ListAddresses request. +func HandleInstanceActionListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/asdfasdfasdf/os-instance-actions", 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, `{ + "instanceActions": [ + { + "action": "stop", + "instance_uuid": "fcd19ef2-b593-40b1-90a5-fc31063fa95c", + "message": null, + "project_id": "6f70656e737461636b20342065766572", + "request_id": "req-f8a59f03-76dc-412f-92c2-21f8612be728", + "start_time": "2018-04-25T01:26:29.000000", + "user_id": "admin" + }, + { + "action": "create", + "instance_uuid": "fcd19ef2-b593-40b1-90a5-fc31063fa95c", + "message": "test", + "project_id": "6f70656e737461636b20342065766572", + "request_id": "req-50189019-626d-47fb-b944-b8342af09679", + "start_time": "2018-04-25T01:26:25.000000", + "user_id": "admin" + } + ] + }`) + }) +} + +// GetExpected represents an expected repsonse from a Get request. +var GetExpected = instanceactions.InstanceAction{ + Action: "stop", + InstanceUUID: "fcd19ef2-b593-40b1-90a5-fc31063fa95c", + Message: "", + ProjectID: "6f70656e737461636b20342065766572", + RequestID: "req-f8a59f03-76dc-412f-92c2-21f8612be728", + StartTime: time.Date(2018, 04, 25, 1, 26, 29, 000000, time.UTC), + UserID: "admin", +} + +// HandleInstanceActionGetSuccessfully sets up the test server to respond to a Get request. +func HandleInstanceActionGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/asdfasdfasdf/os-instance-actions/okzeorkmkfs", 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, `{ + "instanceAction": + { + "action": "stop", + "instance_uuid": "fcd19ef2-b593-40b1-90a5-fc31063fa95c", + "message": null, + "project_id": "6f70656e737461636b20342065766572", + "request_id": "req-f8a59f03-76dc-412f-92c2-21f8612be728", + "start_time": "2018-04-25T01:26:29.000000", + "user_id": "admin" + } + }`) + }) +} diff --git a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go b/openstack/compute/v2/extensions/instanceactions/testing/request_test.go new file mode 100644 index 0000000000..c8e7350b1b --- /dev/null +++ b/openstack/compute/v2/extensions/instanceactions/testing/request_test.go @@ -0,0 +1,48 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleInstanceActionListSuccessfully(t) + + expected := ListExpected + pages := 0 + err := instanceactions.List(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := instanceactions.ExtractInstanceActions(page) + th.AssertNoErr(t, err) + + if len(actual) != 2 { + t.Fatalf("Expected 2 instance actions, got %d", len(actual)) + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, pages) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleInstanceActionGetSuccessfully(t) + + client := client.ServiceClient() + actual, err := instanceactions.Get(client, "asdfasdfasdf", "okzeorkmkfs").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, GetExpected, actual) +} diff --git a/openstack/compute/v2/extensions/instanceactions/urls.go b/openstack/compute/v2/extensions/instanceactions/urls.go new file mode 100644 index 0000000000..36076f27f2 --- /dev/null +++ b/openstack/compute/v2/extensions/instanceactions/urls.go @@ -0,0 +1,11 @@ +package instanceactions + +import "github.com/gophercloud/gophercloud" + +func ListURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "os-instance-actions") +} + +func instanceActionsURL(client *gophercloud.ServiceClient, serverID, requestID string) string { + return client.ServiceURL("servers", serverID, "os-instance-actions", requestID) +} From f6b5ec3161eca8934d7387203bdbd5aa73a5e58f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 13 Feb 2020 09:03:55 -0700 Subject: [PATCH 0972/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index faca6a8b85..2bb12d3700 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.9.0 (Unreleased) +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) + ## 0.8.0 (February 8, 2020) UPGRADE NOTES From d7ec6236fa9d7d375f6e91adbe85a1b7d184eff7 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche Date: Thu, 13 Feb 2020 17:05:04 +0100 Subject: [PATCH 0973/2296] add fixedIPs filter on network port (#1849) --- openstack/networking/v2/ports/requests.go | 30 +++++++++++++++++++ .../v2/ports/testing/requests_test.go | 17 +++++++++++ 2 files changed, 47 insertions(+) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index f5f7d761ce..18d90a255e 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -1,6 +1,10 @@ package ports import ( + "fmt" + "net/url" + "strings" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -36,11 +40,37 @@ type ListOpts struct { TagsAny string `q:"tags-any"` NotTags string `q:"not-tags"` NotTagsAny string `q:"not-tags-any"` + FixedIPs []FixedIPOpts +} + +type FixedIPOpts struct { + IPAddress string + IPAddressSubstr string + SubnetID string +} + +func (f FixedIPOpts) String() string { + var res []string + if f.IPAddress != "" { + res = append(res, fmt.Sprintf("ip_address=%s", f.IPAddress)) + } + if f.IPAddressSubstr != "" { + res = append(res, fmt.Sprintf("ip_address_substr=%s", f.IPAddressSubstr)) + } + if f.SubnetID != "" { + res = append(res, fmt.Sprintf("subnet_id=%s", f.SubnetID)) + } + return strings.Join(res, ",") } // ToPortListQuery formats a ListOpts into a query string. func (opts ListOpts) ToPortListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) + params := q.Query() + for _, fixedIP := range opts.FixedIPs { + params.Add("fixed_ips", fixedIP.String()) + } + q = &url.URL{RawQuery: params.Encode()} return q.String(), err } diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 7cb00b4c53..c611de6629 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -3,6 +3,7 @@ package testing import ( "fmt" "net/http" + "net/url" "testing" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" @@ -784,3 +785,19 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value2") th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4) } + +func TestPortsListOpts(t *testing.T) { + for expected, opts := range map[string]ports.ListOpts{ + newValue("fixed_ips", `ip_address=1.2.3.4,subnet_id=42`): ports.ListOpts{ + FixedIPs: []ports.FixedIPOpts{{IPAddress: "1.2.3.4", SubnetID: "42"}}, + }, + } { + actual, _ := opts.ToPortListQuery() + th.AssertEquals(t, expected, actual) + } +} +func newValue(param, value string) string { + v := url.Values{} + v.Add(param, value) + return "?" + v.Encode() +} From 917735ee91e24fe1493e57869c3b42ee89bc95d8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 13 Feb 2020 09:05:46 -0700 Subject: [PATCH 0974/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb12d3700..da198503d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ 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) ## 0.8.0 (February 8, 2020) From 0de35761f8d592d42b9477bef087c26c5005c7c3 Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Sat, 15 Feb 2020 09:37:05 -0800 Subject: [PATCH 0975/2296] acceptance/openstack/compute/v2: fix dropped error (#1850) --- acceptance/openstack/compute/v2/compute.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index d99b4d5c6f..223a9362b5 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -337,7 +337,9 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, } newServer, err := servers.Get(client, server.ID).Extract() - + if err != nil { + return server, err + } th.AssertEquals(t, newServer.Name, name) th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) th.AssertEquals(t, newServer.Image["id"], choices.ImageID) From 2bc006e9fac5515eb36f1fbd3bcfada1ed2d095c Mon Sep 17 00:00:00 2001 From: Pirionfr Date: Mon, 17 Feb 2020 00:06:05 +0100 Subject: [PATCH 0976/2296] Identity V3: add Trust Get and List funcions (#1855) * Identity V3: add Trust Get and List funcions Implement Get and List functions for v3/extenstions/trusts package with unit tests and docs. * Fix acceptance test --- acceptance/openstack/identity/v3/identity.go | 27 +++++ .../openstack/identity/v3/trusts_test.go | 8 ++ go.mod | 1 - .../identity/v3/extensions/trusts/doc.go | 28 +++++ .../identity/v3/extensions/trusts/requests.go | 41 +++++++ .../identity/v3/extensions/trusts/results.go | 75 +++++++++--- .../v3/extensions/trusts/testing/fixtures.go | 109 ++++++++++++++++++ .../trusts/testing/requests_test.go | 67 +++++++++-- .../identity/v3/extensions/trusts/urls.go | 4 + 9 files changed, 333 insertions(+), 27 deletions(-) diff --git a/acceptance/openstack/identity/v3/identity.go b/acceptance/openstack/identity/v3/identity.go index 133cf1d765..ade0b15804 100644 --- a/acceptance/openstack/identity/v3/identity.go +++ b/acceptance/openstack/identity/v3/identity.go @@ -368,3 +368,30 @@ func DeleteTrust(t *testing.T, client *gophercloud.ServiceClient, trustID string t.Logf("Deleted trust: %s", trustID) } + +// FindTrust finds all trusts that the current authenticated client has access +// to and returns the first one found. An error will be returned if the lookup +// was unsuccessful. +func FindTrust(t *testing.T, client *gophercloud.ServiceClient) (*trusts.Trust, error) { + t.Log("Attempting to find a trust") + var trust *trusts.Trust + + allPages, err := trusts.List(client, nil).AllPages() + if err != nil { + return nil, err + } + + allTrusts, err := trusts.ExtractTrusts(allPages) + if err != nil { + return nil, err + } + + for _, t := range allTrusts { + trust = &t + break + } + + t.Logf("Successfully found a trust %s ", trust.ID) + + return trust, nil +} diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/acceptance/openstack/identity/v3/trusts_test.go index afe43fdba0..2b83a24e25 100644 --- a/acceptance/openstack/identity/v3/trusts_test.go +++ b/acceptance/openstack/identity/v3/trusts_test.go @@ -93,4 +93,12 @@ func TestTrustCRUD(t *testing.T) { }) th.AssertNoErr(t, err) defer DeleteTrust(t, client, trust.ID) + + trust, err = FindTrust(t, client) + th.AssertNoErr(t, err) + + p, err := trusts.Get(client, trust.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, p) } diff --git a/go.mod b/go.mod index 655f3c8b4c..46e16507dc 100644 --- a/go.mod +++ b/go.mod @@ -11,4 +11,3 @@ require ( gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.2.7 ) - diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go index 0ff8069930..b6118874a3 100644 --- a/openstack/identity/v3/extensions/trusts/doc.go +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -54,5 +54,33 @@ Example to Delete a Trust if err != nil { panic(err) } + +Example to Get a Trust + + trustID := "3422b7c113894f5d90665e1a79655e23" + err := trusts.Get(identityClient, trustID).ExtractErr() + if err != nil { + panic(err) + } + +Example to List a Trust + + listOpts := trusts.ListOpts{ + TrustorUserId: "3422b7c113894f5d90665e1a79655e23", + } + + allPages, err := trusts.List(identityClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allTrusts, err := trusts.ExtractTrusts(allPages) + if err != nil { + panic(err) + } + + for _, trust := range allTrusts { + fmt.Printf("%+v\n", region) + } */ package trusts diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index 362c94b6a5..7ad86e2413 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -5,6 +5,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/pagination" ) // AuthOptsExt extends the base Identity v3 tokens AuthOpts with a TrustID. @@ -97,6 +98,25 @@ func (opts CreateOpts) ToTrustCreateMap() (map[string]interface{}, error) { return b, nil } +type ListOptsBuilder interface { + ToTrustListQuery() (string, error) +} + +// ListOpts provides options to filter the List results. +type ListOpts struct { + // TrustorUserID filters the response by a trustor user Id. + TrustorUserID string `q:"trustor_user_id"` + + // TrusteeUserID filters the response by a trustee user Id. + TrusteeUserID string `q:"trustee_user_id"` +} + +// ToTrustListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTrustListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // Create creates a new Trust. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTrustCreateMap() @@ -115,3 +135,24 @@ func Delete(client *gophercloud.ServiceClient, trustID string) (r DeleteResult) _, r.Err = client.Delete(deleteURL(client, trustID), nil) return } + +// List enumerates the Trust to which the current token has access. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToTrustListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return TrustPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details on a single trust, by ID. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/identity/v3/extensions/trusts/results.go b/openstack/identity/v3/extensions/trusts/results.go index fa60b88de7..9475ae3448 100644 --- a/openstack/identity/v3/extensions/trusts/results.go +++ b/openstack/identity/v3/extensions/trusts/results.go @@ -1,6 +1,9 @@ package trusts -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) type trustResult struct { gophercloud.Result @@ -18,34 +21,70 @@ type DeleteResult struct { gophercloud.ErrResult } -// Extract interprets any trust result as a Trust. -func (r trustResult) Extract() (*Trust, error) { +// TrustPage is a single page of Region results. +type TrustPage struct { + pagination.LinkedPageBase +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as a Trust. +type GetResult struct { + trustResult +} + +// IsEmpty determines whether or not a page of Trusts contains any results. +func (t TrustPage) IsEmpty() (bool, error) { + roles, err := ExtractTrusts(t) + return len(roles) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (t TrustPage) NextPageURL() (string, error) { var s struct { - Trust *Trust `json:"trust"` + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` } - err := r.ExtractInto(&s) - return s.Trust, err + err := t.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err } -// TrusteeUser represents the trusted user ID of a trust. -type TrusteeUser struct { - ID string `json:"id"` +// ExtractProjects returns a slice of Trusts contained in a single page of +// results. +func ExtractTrusts(r pagination.Page) ([]Trust, error) { + var s struct { + Trusts []Trust `json:"trusts"` + } + err := (r.(TrustPage)).ExtractInto(&s) + return s.Trusts, err } -// TrustorUser represents the trusting user ID of a trust. -type TrustorUser struct { - ID string `json:"id"` +// Extract interprets any trust result as a Trust. +func (t trustResult) Extract() (*Trust, error) { + var s struct { + Trust *Trust `json:"trust"` + } + err := t.ExtractInto(&s) + return s.Trust, err } // Trust represents a delegated authorization request between two // identities. type Trust struct { - ID string `json:"id"` - Impersonation bool `json:"impersonation"` - TrusteeUser TrusteeUser `json:"trustee_user"` - TrustorUser TrustorUser `json:"trustor_user"` - RedelegatedTrustID string `json:"redelegated_trust_id"` - RedelegationCount int `json:"redelegation_count"` + ID string `json:"id"` + Impersonation bool `json:"impersonation"` + TrusteeUserID string `json:"trustee_user_id"` + TrustorUserID string `json:"trustor_user_id"` + RedelegatedTrustID string `json:"redelegated_trust_id"` + RedelegationCount int `json:"redelegation_count,omitempty"` + AllowRedelegation bool `json:"allow_redelegation,omitempty"` + ProjectID string `json:"project_id,omitempty"` + RemainingUses bool `json:"remaining_uses,omitempty"` + Roles []Role `json:"roles,omitempty"` } // Role specifies a single role that is granted to a trustee. diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures.go b/openstack/identity/v3/extensions/trusts/testing/fixtures.go index ee6ea94455..84ee6c798f 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures.go +++ b/openstack/identity/v3/extensions/trusts/testing/fixtures.go @@ -5,6 +5,8 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" @@ -52,6 +54,66 @@ const CreateResponse = ` } ` +// GetOutput provides a Get result. +const GetResponse = ` +{ + "trust": { + "id": "987fe8", + "expires_at": "2013-02-27T18:30:59.999999Z", + "impersonation": true, + "links": { + "self": "http://example.com/identity/v3/OS-TRUST/trusts/987fe8" + }, + "roles": [ + { + "id": "ed7b78", + "links": { + "self": "http://example.com/identity/v3/roles/ed7b78" + }, + "name": "member" + } + ], + "roles_links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/OS-TRUST/trusts/1ff900/roles" + }, + "project_id": "0f1233", + "trustee_user_id": "be34d1", + "trustor_user_id": "56ae32" + } +} +` + +// ListOutput provides a single page of Role results. +const ListResponse = ` +{ + "trusts": [ + { + "id": "1ff900", + "expires_at": "2013-02-27T18:30:59.999999Z", + "impersonation": true, + "links": { + "self": "http://example.com/identity/v3/OS-TRUST/trusts/1ff900" + }, + "project_id": "0f1233", + "trustee_user_id": "86c0d5", + "trustor_user_id": "a0fdfd" + }, + { + "id": "f4513a", + "impersonation": false, + "links": { + "self": "http://example.com/identity/v3/OS-TRUST/trusts/f45513a" + }, + "project_id": "0f1233", + "trustee_user_id": "86c0d5", + "trustor_user_id": "3cd2ce" + } + ] +} +` + // HandleCreateTokenWithTrustID verifies that providing certain AuthOptions and Scope results in an expected JSON structure. func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilder, requestJSON string) { testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { @@ -133,3 +195,50 @@ func HandleDeleteTrust(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// HandleGetTrustSuccessfully creates an HTTP handler at `/OS-TRUST/trusts` on the +// test handler mux that responds with a single trusts. +func HandleGetTrustSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetResponse) + }) +} + +var FirstTrust = trusts.Trust{ + ID: "1ff900", + Impersonation: true, + TrusteeUserID: "86c0d5", + TrustorUserID: "a0fdfd", + ProjectID: "0f1233", +} + +var SecondTrust = trusts.Trust{ + ID: "f4513a", + Impersonation: false, + TrusteeUserID: "86c0d5", + TrustorUserID: "3cd2ce", + ProjectID: "0f1233", +} + +// ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. +var ExpectedTrustsSlice = []trusts.Trust{FirstTrust, SecondTrust} + +// HandleListTrustsSuccessfully creates an HTTP handler at `/OS-TRUST/trusts` on the +// test handler mux that responds with a list of two trusts. +func HandleListTrustsSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListResponse) + }) +} diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index b84fdda1f8..198ce7c16c 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -4,6 +4,8 @@ import ( "testing" "time" + "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" @@ -56,14 +58,8 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) { }, trusts.TokenExt{ Trust: trusts.Trust{ - ID: "fe0aef", - Impersonation: false, - TrusteeUser: trusts.TrusteeUser{ - ID: "0ca8f6", - }, - TrustorUser: trusts.TrustorUser{ - ID: "bd263c", - }, + ID: "fe0aef", + Impersonation: false, RedelegatedTrustID: "3ba234", RedelegationCount: 2, }, @@ -106,3 +102,58 @@ func TestDeleteTrust(t *testing.T) { res := trusts.Delete(client.ServiceClient(), "3422b7c113894f5d90665e1a79655e23") th.AssertNoErr(t, res.Err) } + +func TestGetTrust(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetTrustSuccessfully(t) + + res := trusts.Get(client.ServiceClient(), "987fe8") + th.AssertNoErr(t, res.Err) +} + +func TestListTrusts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTrustsSuccessfully(t) + + count := 0 + err := trusts.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := trusts.ExtractTrusts(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedTrustsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListTrustsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTrustsSuccessfully(t) + + allPages, err := trusts.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + actual, err := trusts.ExtractTrusts(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedTrustsSlice, actual) +} + +func TestListTrustsFiltered(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTrustsSuccessfully(t) + trustsListOpts := trusts.ListOpts{ + TrustorUserID: "86c0d5", + } + allPages, err := trusts.List(client.ServiceClient(), trustsListOpts).AllPages() + th.AssertNoErr(t, err) + actual, err := trusts.ExtractTrusts(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedTrustsSlice, actual) +} diff --git a/openstack/identity/v3/extensions/trusts/urls.go b/openstack/identity/v3/extensions/trusts/urls.go index d17509b545..22b62ab83c 100644 --- a/openstack/identity/v3/extensions/trusts/urls.go +++ b/openstack/identity/v3/extensions/trusts/urls.go @@ -19,3 +19,7 @@ func createURL(c *gophercloud.ServiceClient) string { func deleteURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} From 193120096e295dd6fe47e5e047e5fe1a5b231e54 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 16 Feb 2020 16:07:01 -0700 Subject: [PATCH 0977/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da198503d3..60dc999640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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) ## 0.8.0 (February 8, 2020) From 09f9c2ef8f0b89eb3715fb612ee97dd65cc0c1e9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 17 Feb 2020 14:25:29 -0700 Subject: [PATCH 0978/2296] Microversions: Simplify Result Fields (#1854) * Microversion: Refactoring Compute v2 Servers Tags * Microversion: Refactoring Compute v2 Server Groups * Microversion: Refactoring Compute v2 Extended Server Attributes * Microversion: Refactoring Block Storage Backups * Container v1: Inline doc update * Microversion: Updating docs --- acceptance/openstack/compute/v2/compute.go | 5 +- .../openstack/compute/v2/servers_test.go | 58 ++++--------- docs/MICROVERSIONS.md | 26 ++++-- .../extensions/backups/microversions.go | 21 ----- .../extensions/backups/results.go | 11 ++- .../extendedserverattributes/microversions.go | 82 ------------------- .../extendedserverattributes/results.go | 38 ++++++++- .../testing/requests_test.go | 23 +----- .../compute/v2/extensions/servergroups/doc.go | 28 ------- .../extensions/servergroups/microversions.go | 32 -------- .../v2/extensions/servergroups/results.go | 16 ++++ .../servergroups/testing/requests_test.go | 39 ++++----- openstack/compute/v2/servers/doc.go | 25 ------ openstack/compute/v2/servers/microversions.go | 29 ------- openstack/compute/v2/servers/results.go | 4 + .../v2/servers/testing/requests_test.go | 9 +- .../container/v1/capsules/microversions.go | 2 +- 17 files changed, 124 insertions(+), 324 deletions(-) delete mode 100644 openstack/blockstorage/extensions/backups/microversions.go delete mode 100644 openstack/compute/v2/extensions/extendedserverattributes/microversions.go delete mode 100644 openstack/compute/v2/extensions/servergroups/microversions.go delete mode 100644 openstack/compute/v2/servers/microversions.go diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 223a9362b5..d7668359b8 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -585,10 +585,7 @@ func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, netwo newServer, err := res.Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newServer.Name, name) - - tags, err := res.ExtractTags() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, tags, []string{"tag1", "tag2"}) + th.AssertDeepEquals(t, *newServer.Tags, []string{"tag1", "tag2"}) return newServer, nil } diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index fa2d759a4a..96283222a2 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -516,16 +516,9 @@ func TestServersTags(t *testing.T) { client.Microversion = "2.26" // Check server tags in body. - type serverWithTagsExt struct { - servers.Server - servers.TagsExt - } - - serverWithTags := serverWithTagsExt{} - - err = servers.Get(client, server.ID).ExtractInto(&serverWithTags) + serverWithTags, err := servers.Get(client, server.ID).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, []string{"tag1", "tag2"}, serverWithTags.Tags) + th.AssertDeepEquals(t, []string{"tag1", "tag2"}, *serverWithTags.Tags) // Check all tags. allTags, err := tags.List(client, server.ID).Extract() @@ -585,41 +578,22 @@ func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - result := servers.Get(client, server.ID) - th.AssertNoErr(t, result.Err) - - reservationID, err := extendedserverattributes.ExtractReservationID(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, reservationID != "", true) - t.Logf("reservationID: %s", reservationID) - - launchIndex, err := extendedserverattributes.ExtractLaunchIndex(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, launchIndex, 0) - t.Logf("launchIndex: %d", launchIndex) - - ramdiskID, err := extendedserverattributes.ExtractRamdiskID(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, ramdiskID == "", true) - t.Logf("ramdiskID: %s", ramdiskID) - - kernelID, err := extendedserverattributes.ExtractKernelID(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, kernelID == "", true) - t.Logf("kernelID: %s", kernelID) + type serverAttributesExt struct { + servers.Server + extendedserverattributes.ServerAttributesExt + } + var serverWithAttributesExt serverAttributesExt - hostname, err := extendedserverattributes.ExtractHostname(result.Result) + err = servers.Get(client, server.ID).ExtractInto(&serverWithAttributesExt) th.AssertNoErr(t, err) - th.AssertEquals(t, hostname != "", true) - t.Logf("hostname: %s", hostname) - rootDeviceName, err := extendedserverattributes.ExtractRootDeviceName(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, rootDeviceName != "", true) - t.Logf("rootDeviceName: %s", rootDeviceName) + t.Logf("Server With Extended Attributes: %#v", serverWithAttributesExt) - userData, err := extendedserverattributes.ExtractUserData(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, userData == "", true) - t.Logf("userData: %s", userData) + th.AssertEquals(t, *serverWithAttributesExt.ReservationID != "", true) + th.AssertEquals(t, *serverWithAttributesExt.LaunchIndex, 0) + th.AssertEquals(t, *serverWithAttributesExt.RAMDiskID == "", true) + th.AssertEquals(t, *serverWithAttributesExt.KernelID == "", true) + th.AssertEquals(t, *serverWithAttributesExt.Hostname != "", true) + th.AssertEquals(t, *serverWithAttributesExt.RootDeviceName != "", true) + th.AssertEquals(t, serverWithAttributesExt.Userdata == nil, true) } diff --git a/docs/MICROVERSIONS.md b/docs/MICROVERSIONS.md index 18b4d4a176..73ab724edd 100644 --- a/docs/MICROVERSIONS.md +++ b/docs/MICROVERSIONS.md @@ -57,20 +57,30 @@ service. You may need to use a pointer field in order for this to work. When adding a new field, please make sure to include a GoDoc comment about what microversions the field is valid for. +Please see [here](https://github.com/gophercloud/gophercloud/blob/917735ee91e24fe1493e57869c3b42ee89bc95d8/openstack/compute/v2/servers/requests.go#L215-L217) for an example. + ### New Response Fields This is when a microversion includes new fields in the API response. The -correct way of implementing this in Gophercloud is to _not_ add the field -to the resource's "result" struct (in the `results.go` file), but instead -add a custom "extract" method to a new `microversions.go` file. This is -to ensure that base API interaction does not break with the introduction -of new fields. +correct way of implementing this in Gophercloud is to add the field to the +resource's "result" struct (in the `results.go` file) as a *pointer*. This +way, the developer can check for a `nil` value to see if the field was set +from a microversioned result. + +When adding a new field, please make sure to include a GoDoc comment about +what microversions the field is valid for. ### Modified Response Fields -This is when a microversion modifies an existing field in an API response -to be formatted differently than the base API. Research is still ongoing -on how to best handle this scenario in Gophercloud. +This is when the new type of the returned field is incompatible with the +original type. When this happens, an entire new result struct must be +created with new Extract methods to account for both the original result +struct and new result struct. + +These new structs and methods need to be defined in a new `microversions.go` +file. + +Please see [here](https://github.com/gophercloud/gophercloud/blob/917735ee91e24fe1493e57869c3b42ee89bc95d8/openstack/container/v1/capsules/microversions.go) for an example. ## Application Developer Information diff --git a/openstack/blockstorage/extensions/backups/microversions.go b/openstack/blockstorage/extensions/backups/microversions.go deleted file mode 100644 index 4c12690c58..0000000000 --- a/openstack/blockstorage/extensions/backups/microversions.go +++ /dev/null @@ -1,21 +0,0 @@ -package backups - -// ExtractMetadata will extract the metadata of a backup. -// This requires the client to be set to microversion 3.43 or later. -func (r commonResult) ExtractMetadata() (map[string]string, error) { - var s struct { - Metadata map[string]string `json:"metadata"` - } - err := r.ExtractInto(&s) - return s.Metadata, err -} - -// ExtractAvailaiblityZone will extract the availability zone of a backup. -// This requires the client to be set to microversion 3.51 or later. -func (r commonResult) ExtractAvailabilityZone() (string, error) { - var s struct { - AvailabilityZone string `json:"availability_zone"` - } - err := r.ExtractInto(&s) - return s.AvailabilityZone, err -} diff --git a/openstack/blockstorage/extensions/backups/results.go b/openstack/blockstorage/extensions/backups/results.go index 6293f979f1..c58f475334 100644 --- a/openstack/blockstorage/extensions/backups/results.go +++ b/openstack/blockstorage/extensions/backups/results.go @@ -43,9 +43,6 @@ type Backup struct { // Container is the container where the backup is stored. Container string `json:"container"` - // AvailabilityZone is the availability zone of the backup. - AvailabilityZone string `json:"availability_zone"` - // HasDependentBackups is whether there are other backups // depending on this backup. HasDependentBackups bool `json:"has_dependent_backups"` @@ -62,6 +59,14 @@ type Backup struct { // ProjectID is the ID of the project that owns the backup. This is // an admin-only field. ProjectID string `json:"os-backup-project-attr:project_id"` + + // Metadata is metadata about the backup. + // This requires microversion 3.43 or later. + Metadata *map[string]string `json:"metadata"` + + // AvailabilityZone is the Availability Zone of the backup. + // This requires microversion 3.51 or later. + AvailabilityZone *string `json:"availability_zone"` } // CreateResult contains the response body and error from a Create request. diff --git a/openstack/compute/v2/extensions/extendedserverattributes/microversions.go b/openstack/compute/v2/extensions/extendedserverattributes/microversions.go deleted file mode 100644 index 709f84d965..0000000000 --- a/openstack/compute/v2/extensions/extendedserverattributes/microversions.go +++ /dev/null @@ -1,82 +0,0 @@ -package extendedserverattributes - -import ( - "github.com/gophercloud/gophercloud" -) - -// ExtractReservationID will extract the reservation_id attribute. -// This requires the client to be set to microversion 2.3 or later. -func ExtractReservationID(r gophercloud.Result) (string, error) { - var s struct { - ReservationID string `json:"OS-EXT-SRV-ATTR:reservation_id"` - } - err := r.ExtractIntoStructPtr(&s, "server") - - return s.ReservationID, err -} - -// ExtractLaunchIndex will extract the launch_index attribute. -// This requires the client to be set to microversion 2.3 or later. -func ExtractLaunchIndex(r gophercloud.Result) (int, error) { - var s struct { - LaunchIndex int `json:"OS-EXT-SRV-ATTR:launch_index"` - } - err := r.ExtractIntoStructPtr(&s, "server") - - return s.LaunchIndex, err -} - -// ExtractRamdiskID will extract the ramdisk_id attribute. -// This requires the client to be set to microversion 2.3 or later. -func ExtractRamdiskID(r gophercloud.Result) (string, error) { - var s struct { - RamdiskID string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` - } - err := r.ExtractIntoStructPtr(&s, "server") - - return s.RamdiskID, err -} - -// ExtractKernelID will extract the kernel_id attribute. -// This requires the client to be set to microversion 2.3 or later. -func ExtractKernelID(r gophercloud.Result) (string, error) { - var s struct { - KernelID string `json:"OS-EXT-SRV-ATTR:kernel_id"` - } - err := r.ExtractIntoStructPtr(&s, "server") - - return s.KernelID, err -} - -// ExtractHostname will extract the hostname attribute. -// This requires the client to be set to microversion 2.3 or later. -func ExtractHostname(r gophercloud.Result) (string, error) { - var s struct { - Hostname string `json:"OS-EXT-SRV-ATTR:hostname"` - } - err := r.ExtractIntoStructPtr(&s, "server") - - return s.Hostname, err -} - -// ExtractRootDeviceName will extract the root_device_name attribute. -// This requires the client to be set to microversion 2.3 or later. -func ExtractRootDeviceName(r gophercloud.Result) (string, error) { - var s struct { - RootDeviceName string `json:"OS-EXT-SRV-ATTR:root_device_name"` - } - err := r.ExtractIntoStructPtr(&s, "server") - - return s.RootDeviceName, err -} - -// ExtractUserData will extract the userdata attribute. -// This requires the client to be set to microversion 2.3 or later. -func ExtractUserData(r gophercloud.Result) (string, error) { - var s struct { - Userdata string `json:"OS-EXT-SRV-ATTR:userdata"` - } - err := r.ExtractIntoStructPtr(&s, "server") - - return s.Userdata, err -} diff --git a/openstack/compute/v2/extensions/extendedserverattributes/results.go b/openstack/compute/v2/extensions/extendedserverattributes/results.go index 6c2ffc9818..313886f1a6 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/results.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/results.go @@ -4,7 +4,41 @@ package extendedserverattributes // You should use extract methods from microversions.go to retrieve additional // fields. type ServerAttributesExt struct { - Host string `json:"OS-EXT-SRV-ATTR:host"` - InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` + // Host is the host/hypervisor that the instance is hosted on. + Host string `json:"OS-EXT-SRV-ATTR:host"` + + // InstanceName is the name of the instance. + InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` + + // HypervisorHostname is the hostname of the host/hypervisor that the + // instance is hosted on. HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` + + // ReservationID is the reservation ID of the instance. + // This requires microversion 2.3 or later. + ReservationID *string `json:"OS-EXT-SRV-ATTR:reservation_id"` + + // LaunchIndex is the launch index of the instance. + // This requires microversion 2.3 or later. + LaunchIndex *int `json:"OS-EXT-SRV-ATTR:launch_index"` + + // RAMDiskID is the ID of the RAM disk image of the instance. + // This requires microversion 2.3 or later. + RAMDiskID *string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` + + // KernelID is the ID of the kernel image of the instance. + // This requires microversion 2.3 or later. + KernelID *string `json:"OS-EXT-SRV-ATTR:kernel_id"` + + // Hostname is the hostname of the instance. + // This requires microversion 2.3 or later. + Hostname *string `json:"OS-EXT-SRV-ATTR:hostname"` + + // RootDeviceName is the name of the root device of the instance. + // This requires microversion 2.3 or later. + RootDeviceName *string `json:"OS-EXT-SRV-ATTR:root_device_name"` + + // Userdata is the userdata of the instance. + // This requires microversion 2.3 or later. + Userdata *string `json:"OS-EXT-SRV-ATTR:userdata"` } diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go index 360d295b42..c227541243 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go @@ -29,30 +29,15 @@ func TestServerWithUsageExt(t *testing.T) { } var serverWithAttributesExt serverAttributesExt - result := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a") - // Extract basic fields. err := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) th.AssertNoErr(t, err) - // Extract additional fields. - reservationID, err := extendedserverattributes.ExtractReservationID(result.Result) - th.AssertNoErr(t, err) - - launchIndex, err := extendedserverattributes.ExtractLaunchIndex(result.Result) - th.AssertNoErr(t, err) - - hostname, err := extendedserverattributes.ExtractHostname(result.Result) - th.AssertNoErr(t, err) - - rootDeviceName, err := extendedserverattributes.ExtractRootDeviceName(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, serverWithAttributesExt.Host, "compute01") th.AssertEquals(t, serverWithAttributesExt.InstanceName, "instance-00000001") th.AssertEquals(t, serverWithAttributesExt.HypervisorHostname, "compute01") - th.AssertEquals(t, reservationID, "r-ky9gim1l") - th.AssertEquals(t, launchIndex, 0) - th.AssertEquals(t, hostname, "test00") - th.AssertEquals(t, rootDeviceName, "/dev/sda") + th.AssertEquals(t, *serverWithAttributesExt.ReservationID, "r-ky9gim1l") + th.AssertEquals(t, *serverWithAttributesExt.LaunchIndex, 0) + th.AssertEquals(t, *serverWithAttributesExt.Hostname, "test00") + th.AssertEquals(t, *serverWithAttributesExt.RootDeviceName, "/dev/sda") } diff --git a/openstack/compute/v2/extensions/servergroups/doc.go b/openstack/compute/v2/extensions/servergroups/doc.go index 7811d32ecf..936674b051 100644 --- a/openstack/compute/v2/extensions/servergroups/doc.go +++ b/openstack/compute/v2/extensions/servergroups/doc.go @@ -47,16 +47,6 @@ Example to Create a Server Group with additional microversion 2.64 fields panic(err) } - policy, err := servergroups.ExtractPolicy(result.Result) - if err != nil { - panic(err) - } - - rules, err := servergroups.ExtractRules(result.Result) - if err != nil { - panic(err) - } - Example to Delete a Server Group sgID := "7a6f29ad-e34d-4368-951a-58a08f11cfb7" @@ -64,23 +54,5 @@ Example to Delete a Server Group if err != nil { panic(err) } - -Example to get additional fields with microversion 2.64 or later - - computeClient.Microversion = "2.64" - result := servergroups.Get(computeClient, "616fb98f-46ca-475e-917e-2563e5a8cd19") - - policy, err := servergroups.ExtractPolicy(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("Policy: %s\n", policy) - - rules, err := servergroups.ExtractRules(result.Result) - if err != nil { - panic(err) - } - fmt.Printf("Max server per host: %s\n", rules.MaxServerPerHost) - */ package servergroups diff --git a/openstack/compute/v2/extensions/servergroups/microversions.go b/openstack/compute/v2/extensions/servergroups/microversions.go deleted file mode 100644 index 4899d9c572..0000000000 --- a/openstack/compute/v2/extensions/servergroups/microversions.go +++ /dev/null @@ -1,32 +0,0 @@ -package servergroups - -import "github.com/gophercloud/gophercloud" - -// ExtractPolicy will extract the policy attribute. -// This requires the client to be set to microversion 2.64 or later. -func ExtractPolicy(r gophercloud.Result) (string, error) { - var s struct { - Policy string `json:"policy"` - } - err := r.ExtractIntoStructPtr(&s, "server_group") - - return s.Policy, err -} - -// ExtractRules will extract the rules attribute. -// This requires the client to be set to microversion 2.64 or later. -func ExtractRules(r gophercloud.Result) (Rules, error) { - var s struct { - Rules Rules `json:"rules"` - } - err := r.ExtractIntoStructPtr(&s, "server_group") - - return s.Rules, err -} - -// Rules represents set of rules for a policy. -type Rules struct { - // MaxServerPerHost specifies how many servers can reside on a single compute host. - // It can be used only with the "anti-affinity" policy. - MaxServerPerHost int `json:"max_server_per_host,omitempty"` -} diff --git a/openstack/compute/v2/extensions/servergroups/results.go b/openstack/compute/v2/extensions/servergroups/results.go index fd6f2c6c76..de41f12304 100644 --- a/openstack/compute/v2/extensions/servergroups/results.go +++ b/openstack/compute/v2/extensions/servergroups/results.go @@ -32,6 +32,22 @@ type ServerGroup struct { // Metadata includes a list of all user-specified key-value pairs attached // to the Server Group. Metadata map[string]interface{} + + // Policy is the policy of a server group. + // This requires microversion 2.64 or later. + Policy *string `json:"policy"` + + // Rules are the rules of the server group. + // This requires microversion 2.64 or later. + Rules *Rules `json:"rules"` +} + +// Rules represents set of rules for a policy. +// This requires microversion 2.64 or later. +type Rules struct { + // MaxServerPerHost specifies how many servers can reside on a single compute host. + // It can be used only with the "anti-affinity" policy. + MaxServerPerHost int `json:"max_server_per_host"` } // ServerGroupPage stores a single page of all ServerGroups results from a diff --git a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go index bb37b89752..cada74fa00 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go +++ b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go @@ -45,28 +45,23 @@ func TestCreateMicroversion(t *testing.T) { defer th.TeardownHTTP() HandleCreateMicroversionSuccessfully(t) + policy := "anti-affinity" + rules := servergroups.Rules{ + MaxServerPerHost: 3, + } + CreatedServerGroup.Policy = &policy + CreatedServerGroup.Rules = &rules + result := servergroups.Create(client.ServiceClient(), servergroups.CreateOpts{ Name: "test", Policies: []string{"anti-affinity"}, - Policy: "anti-affinity", - Rules: &servergroups.Rules{ - MaxServerPerHost: 3, - }, + Policy: policy, + Rules: &rules, }) - // Extract basic fields. actual, err := result.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedServerGroup, actual) - - // Extract additional fields. - policy, err := servergroups.ExtractPolicy(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, "anti-affinity", policy) - - rules, err := servergroups.ExtractRules(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, 3, rules.MaxServerPerHost) } func TestGet(t *testing.T) { @@ -84,21 +79,19 @@ func TestGetMicroversion(t *testing.T) { defer th.TeardownHTTP() HandleGetMicroversionSuccessfully(t) + policy := "anti-affinity" + rules := servergroups.Rules{ + MaxServerPerHost: 3, + } + FirstServerGroup.Policy = &policy + FirstServerGroup.Rules = &rules + result := servergroups.Get(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0") // Extract basic fields. actual, err := result.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstServerGroup, actual) - - // Extract additional fields. - policy, err := servergroups.ExtractPolicy(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, "anti-affinity", policy) - - rules, err := servergroups.ExtractRules(result.Result) - th.AssertNoErr(t, err) - th.AssertEquals(t, 3, rules.MaxServerPerHost) } func TestDelete(t *testing.T) { diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index 3926a382c3..3b0ab78362 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -111,30 +111,5 @@ Example to Snapshot a Server if err != nil { panic(err) } - -Example of Extend server result with Tags: - - client.Microversion = "2.26" - - type ServerWithTags struct { - servers.Server - servers.TagsExt - } - - var allServers []ServerWithTags - - allPages, err := servers.List(client, nil).AllPages() - if err != nil { - log.Fatal(err) - } - - err = servers.ExtractServersInto(allPages, &allServers) - if err != nil { - log.Fatal(err) - } - - for _, server := range allServers { - fmt.Println(server.Tags) - } */ package servers diff --git a/openstack/compute/v2/servers/microversions.go b/openstack/compute/v2/servers/microversions.go deleted file mode 100644 index b5d4be7506..0000000000 --- a/openstack/compute/v2/servers/microversions.go +++ /dev/null @@ -1,29 +0,0 @@ -package servers - -// TagsExt is an extension to the base Server struct. -// Use this in combination with the Server struct to create -// a composed struct in order to extract a slice of servers -// with the Tag field. -// -// This requires the client to be set to microversion 2.26 or later. -// -// To interact with a server's tags directly, see the -// openstack/compute/v2/extensions/tags package. -type TagsExt struct { - // Tags contains a list of server tags. - Tags []string `json:"tags"` -} - -// ExtractTags will extract the tags of a server. -// -// This requires the client to be set to microversion 2.26 or later. -// -// To interact with a server's tags directly, see the -// openstack/compute/v2/extensions/tags package. -func (r serverResult) ExtractTags() ([]string, error) { - var s struct { - Tags []string `json:"tags"` - } - err := r.ExtractInto(&s) - return s.Tags, err -} diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index cec633e77a..b3028be5d5 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -217,6 +217,10 @@ type Server struct { // Fault contains failure information about a server. Fault Fault `json:"fault"` + + // Tags is a slice/list of string tags in a server. + // The requires microversion 2.26 or later. + Tags *[]string `json:"tags"` } type AttachedVolume struct { diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index b79f1e631e..af9a368548 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -577,6 +577,9 @@ func TestCreateServerWithTags(t *testing.T) { c.Microversion = "2.52" tags := []string{"foo", "bar"} + ServerDerpTags := ServerDerp + ServerDerpTags.Tags = &tags + createOpts := servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", @@ -587,9 +590,5 @@ func TestCreateServerWithTags(t *testing.T) { th.AssertNoErr(t, res.Err) actualServer, err := res.Extract() th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ServerDerp, *actualServer) - - actualTags, err := res.ExtractTags() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, tags, actualTags) + th.CheckDeepEquals(t, ServerDerpTags, *actualServer) } diff --git a/openstack/container/v1/capsules/microversions.go b/openstack/container/v1/capsules/microversions.go index da8f34a510..7eb648ce0f 100644 --- a/openstack/container/v1/capsules/microversions.go +++ b/openstack/container/v1/capsules/microversions.go @@ -15,7 +15,7 @@ func (r commonResult) ExtractV132() (*CapsuleV132, error) { return s, err } -// Represents a Capsule at microversion vXY or greater. +// Represents a Capsule at microversion v1.32 or greater. type CapsuleV132 struct { // UUID for the capsule UUID string `json:"uuid"` From ed4deec00ff1d4d4c8a762af0c6360d4184a4bf4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 17 Feb 2020 14:27:53 -0700 Subject: [PATCH 0979/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60dc999640..5ed8549755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.9.0 (Unreleased) +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) + IMPROVEMENTS * Added `compute/v2/extensions/instanceactions.List` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848) From 3615b33ffa480a6bf062a1b489b5b831c3e36523 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 17 Feb 2020 14:49:59 -0700 Subject: [PATCH 0980/2296] Update MICROVERSIONS.md --- docs/MICROVERSIONS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/MICROVERSIONS.md b/docs/MICROVERSIONS.md index 73ab724edd..2054c9a6ef 100644 --- a/docs/MICROVERSIONS.md +++ b/docs/MICROVERSIONS.md @@ -70,6 +70,8 @@ from a microversioned result. When adding a new field, please make sure to include a GoDoc comment about what microversions the field is valid for. +Please see [here](https://github.com/gophercloud/gophercloud/blob/ed4deec00ff1d4d4c8a762af0c6360d4184a4bf4/openstack/compute/v2/servers/results.go#L221-L223) for an example. + ### Modified Response Fields This is when the new type of the returned field is incompatible with the From 16b987812642e5aba39ee5fe720c12b80dc0b404 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 17 Feb 2020 20:32:05 -0700 Subject: [PATCH 0981/2296] Identity v3: Adding ExpiresAt and DeletedAt fields to Trust results (#1857) --- .../openstack/identity/v3/trusts_test.go | 5 ++++ .../identity/v3/extensions/trusts/results.go | 24 +++++++++++-------- .../v3/extensions/trusts/testing/fixtures.go | 6 ++++- .../trusts/testing/requests_test.go | 3 +-- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/acceptance/openstack/identity/v3/trusts_test.go index 2b83a24e25..4632fd8209 100644 --- a/acceptance/openstack/identity/v3/trusts_test.go +++ b/acceptance/openstack/identity/v3/trusts_test.go @@ -4,6 +4,7 @@ package v3 import ( "testing" + "time" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" @@ -80,11 +81,13 @@ func TestTrustCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteUser(t, client, trusteeUser.ID) + expiresAt := time.Now().Add(time.Minute) // Create a trust. trust, err := CreateTrust(t, client, trusts.CreateOpts{ TrusteeUserID: trusteeUser.ID, TrustorUserID: adminUser.ID, ProjectID: trusteeProject.ID, + ExpiresAt: &expiresAt, Roles: []trusts.Role{ { ID: memberRoleID, @@ -99,6 +102,8 @@ func TestTrustCRUD(t *testing.T) { p, err := trusts.Get(client, trust.ID).Extract() th.AssertNoErr(t, err) + th.AssertEquals(t, p.ExpiresAt.IsZero(), false) + th.AssertEquals(t, p.DeletedAt.IsZero(), true) tools.PrintResource(t, p) } diff --git a/openstack/identity/v3/extensions/trusts/results.go b/openstack/identity/v3/extensions/trusts/results.go index 9475ae3448..60629f9b42 100644 --- a/openstack/identity/v3/extensions/trusts/results.go +++ b/openstack/identity/v3/extensions/trusts/results.go @@ -1,6 +1,8 @@ package trusts import ( + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -75,16 +77,18 @@ func (t trustResult) Extract() (*Trust, error) { // Trust represents a delegated authorization request between two // identities. type Trust struct { - ID string `json:"id"` - Impersonation bool `json:"impersonation"` - TrusteeUserID string `json:"trustee_user_id"` - TrustorUserID string `json:"trustor_user_id"` - RedelegatedTrustID string `json:"redelegated_trust_id"` - RedelegationCount int `json:"redelegation_count,omitempty"` - AllowRedelegation bool `json:"allow_redelegation,omitempty"` - ProjectID string `json:"project_id,omitempty"` - RemainingUses bool `json:"remaining_uses,omitempty"` - Roles []Role `json:"roles,omitempty"` + ID string `json:"id"` + Impersonation bool `json:"impersonation"` + TrusteeUserID string `json:"trustee_user_id"` + TrustorUserID string `json:"trustor_user_id"` + RedelegatedTrustID string `json:"redelegated_trust_id"` + RedelegationCount int `json:"redelegation_count,omitempty"` + AllowRedelegation bool `json:"allow_redelegation,omitempty"` + ProjectID string `json:"project_id,omitempty"` + RemainingUses bool `json:"remaining_uses,omitempty"` + Roles []Role `json:"roles,omitempty"` + DeletedAt time.Time `json:"deleted_at"` + ExpiresAt time.Time `json:"expires_at"` } // Role specifies a single role that is granted to a trustee. diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures.go b/openstack/identity/v3/extensions/trusts/testing/fixtures.go index 84ee6c798f..c0d52be533 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures.go +++ b/openstack/identity/v3/extensions/trusts/testing/fixtures.go @@ -4,9 +4,9 @@ import ( "fmt" "net/http" "testing" + "time" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" @@ -216,6 +216,8 @@ var FirstTrust = trusts.Trust{ TrusteeUserID: "86c0d5", TrustorUserID: "a0fdfd", ProjectID: "0f1233", + ExpiresAt: time.Date(2013, 02, 27, 18, 30, 59, 999999000, time.UTC), + DeletedAt: time.Time{}, } var SecondTrust = trusts.Trust{ @@ -224,6 +226,8 @@ var SecondTrust = trusts.Trust{ TrusteeUserID: "86c0d5", TrustorUserID: "3cd2ce", ProjectID: "0f1233", + ExpiresAt: time.Time{}, + DeletedAt: time.Time{}, } // ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index 198ce7c16c..98cbd563f8 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -4,10 +4,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) From f61d5a0e5e762488629dc8399be07ef20a61aac8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 17 Feb 2020 20:33:12 -0700 Subject: [PATCH 0982/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ed8549755..f227311724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ IMPROVEMENTS * 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) ## 0.8.0 (February 8, 2020) From 14f04f56ec11b3ca479e8bce11756c319eff2587 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche Date: Wed, 19 Feb 2020 04:28:25 +0100 Subject: [PATCH 0983/2296] fix payload of get instance actions (#1851) --- .../v2/extensions/instanceactions/results.go | 78 ++++++++++++++++++- .../instanceactions/testing/fixtures.go | 57 ++++++++++---- 2 files changed, 118 insertions(+), 17 deletions(-) diff --git a/openstack/compute/v2/extensions/instanceactions/results.go b/openstack/compute/v2/extensions/instanceactions/results.go index d39e050f9c..f341ffcf21 100644 --- a/openstack/compute/v2/extensions/instanceactions/results.go +++ b/openstack/compute/v2/extensions/instanceactions/results.go @@ -58,13 +58,85 @@ func ExtractInstanceActions(r pagination.Page) ([]InstanceAction, error) { return resp, err } +// Event represents an event of instance action. +type Event struct { + Event string `json:"event"` + // Host is the host of the event. + // This requires microversion 2.62 or later. + Host *string `json:"host"` + // HostID is the host id of the event. + // This requires microversion 2.62 or later. + HostID *string `json:"hostId"` + Result string `json:"result"` + Traceback string `json:"traceback"` + StartTime time.Time `json:"-"` + FinishTime time.Time `json:"-"` +} + +// UnmarshalJSON converts our JSON API response into our instance action struct +func (e *Event) UnmarshalJSON(b []byte) error { + type tmp Event + var s struct { + tmp + StartTime gophercloud.JSONRFC3339MilliNoZ `json:"start_time"` + FinishTime gophercloud.JSONRFC3339MilliNoZ `json:"finish_time"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *e = Event(s.tmp) + + e.StartTime = time.Time(s.StartTime) + e.FinishTime = time.Time(s.FinishTime) + + return err +} + +// InstanceActionDetail gives more details on instance action. +type InstanceActionDetail struct { + Action string `json:"action"` + InstanceUUID string `json:"instance_uuid"` + Message string `json:"message"` + ProjectID string `json:"project_id"` + RequestID string `json:"request_id"` + UserID string `json:"user_id"` + // Events is the list of events of the action. + // This requires microversion 2.50 or later. + Events *[]Event `json:"events"` + // UpdatedAt last update date of the action. + // This requires microversion 2.58 or later. + UpdatedAt *time.Time `json:"-"` + StartTime time.Time `json:"-"` +} + +// UnmarshalJSON converts our JSON API response into our instance action struct +func (i *InstanceActionDetail) UnmarshalJSON(b []byte) error { + type tmp InstanceActionDetail + var s struct { + tmp + UpdatedAt *gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + StartTime gophercloud.JSONRFC3339MilliNoZ `json:"start_time"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *i = InstanceActionDetail(s.tmp) + + i.UpdatedAt = (*time.Time)(s.UpdatedAt) + i.StartTime = time.Time(s.StartTime) + return err +} + +// InstanceActionResult is the result handler of Get. type InstanceActionResult struct { gophercloud.Result } -// Extract interprets any instanceActionResult as an InstanceAction, if possible. -func (r InstanceActionResult) Extract() (InstanceAction, error) { - var s InstanceAction +// Extract interprets any instanceActionResult as an InstanceActionDetail, if possible. +func (r InstanceActionResult) Extract() (InstanceActionDetail, error) { + var s InstanceActionDetail err := r.ExtractInto(&s) return s, err } diff --git a/openstack/compute/v2/extensions/instanceactions/testing/fixtures.go b/openstack/compute/v2/extensions/instanceactions/testing/fixtures.go index bf45a41710..ced79e74a1 100644 --- a/openstack/compute/v2/extensions/instanceactions/testing/fixtures.go +++ b/openstack/compute/v2/extensions/instanceactions/testing/fixtures.go @@ -33,7 +33,7 @@ var ListExpected = []instanceactions.InstanceAction{ }, } -// HandleAddressListSuccessfully sets up the test server to respond to a ListAddresses request. +// HandleInstanceActionListSuccessfully sets up the test server to respond to a ListAddresses request. func HandleInstanceActionListSuccessfully(t *testing.T) { th.Mux.HandleFunc("/servers/asdfasdfasdf/os-instance-actions", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -65,15 +65,32 @@ func HandleInstanceActionListSuccessfully(t *testing.T) { }) } +var ( + expectedUpdateAt = time.Date(2018, 04, 25, 1, 26, 36, 0, time.UTC) + expectedEventHost = "compute" + expectedEventHostID = "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6" + expectedEvents = []instanceactions.Event{{ + Event: "compute_stop_instance", + Host: &expectedEventHost, + HostID: &expectedEventHostID, + Result: "Success", + StartTime: time.Date(2018, 04, 25, 1, 26, 36, 0, time.UTC), + FinishTime: time.Date(2018, 04, 25, 1, 26, 36, 0, time.UTC), + Traceback: "", + }} +) + // GetExpected represents an expected repsonse from a Get request. -var GetExpected = instanceactions.InstanceAction{ +var GetExpected = instanceactions.InstanceActionDetail{ Action: "stop", - InstanceUUID: "fcd19ef2-b593-40b1-90a5-fc31063fa95c", + InstanceUUID: "4bf3473b-d550-4b65-9409-292d44ab14a2", Message: "", ProjectID: "6f70656e737461636b20342065766572", - RequestID: "req-f8a59f03-76dc-412f-92c2-21f8612be728", - StartTime: time.Date(2018, 04, 25, 1, 26, 29, 000000, time.UTC), + RequestID: "req-0d819d5c-1527-4669-bdf0-ffad31b5105b", + StartTime: time.Date(2018, 04, 25, 1, 26, 36, 0, time.UTC), + UpdatedAt: &expectedUpdateAt, UserID: "admin", + Events: &expectedEvents, } // HandleInstanceActionGetSuccessfully sets up the test server to respond to a Get request. @@ -85,15 +102,27 @@ func HandleInstanceActionGetSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "instanceAction": - { - "action": "stop", - "instance_uuid": "fcd19ef2-b593-40b1-90a5-fc31063fa95c", - "message": null, - "project_id": "6f70656e737461636b20342065766572", - "request_id": "req-f8a59f03-76dc-412f-92c2-21f8612be728", - "start_time": "2018-04-25T01:26:29.000000", - "user_id": "admin" - } + { + "action": "stop", + "events": [ + { + "event": "compute_stop_instance", + "finish_time": "2018-04-25T01:26:36.00000", + "host": "compute", + "hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6", + "result": "Success", + "start_time": "2018-04-25T01:26:36.00000", + "traceback": null + } + ], + "instance_uuid": "4bf3473b-d550-4b65-9409-292d44ab14a2", + "message": null, + "project_id": "6f70656e737461636b20342065766572", + "request_id": "req-0d819d5c-1527-4669-bdf0-ffad31b5105b", + "start_time": "2018-04-25T01:26:36.00000", + "updated_at": "2018-04-25T01:26:36.00000", + "user_id": "admin" + } }`) }) } From 2bff907f52f56fb418c34b73eed9f3ef8ea646c7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 18 Feb 2020 20:29:36 -0700 Subject: [PATCH 0984/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f227311724..554994b746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ IMPROVEMENTS * 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) ## 0.8.0 (February 8, 2020) From e45d978f3a52eca169a1617f4dfccab9821d88d1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 19 Feb 2020 04:27:25 +0000 Subject: [PATCH 0985/2296] Compute v2: InstanceActions ListOpts This commit adds support for list params in the instanceactions extension. It also does some documentation cleanup. --- .../v2/extensions/instanceactions/doc.go | 7 +- .../v2/extensions/instanceactions/request.go | 62 +++++++++++++- .../v2/extensions/instanceactions/results.go | 84 ++++++++++++++----- .../instanceactions/testing/request_test.go | 2 +- .../v2/extensions/instanceactions/urls.go | 2 +- 5 files changed, 129 insertions(+), 28 deletions(-) diff --git a/openstack/compute/v2/extensions/instanceactions/doc.go b/openstack/compute/v2/extensions/instanceactions/doc.go index 3f9b813e40..8e75d50709 100644 --- a/openstack/compute/v2/extensions/instanceactions/doc.go +++ b/openstack/compute/v2/extensions/instanceactions/doc.go @@ -2,12 +2,14 @@ package instanceactions /* Package instanceactions provides the ability to list or get a server instance-action. -Example: - pages, err := instanceactions.List(client, "server-id").AllPages() +Example to List and Get actions: + + pages, err := instanceactions.List(client, "server-id", nil).AllPages() if err != nil { panic("fail to get actions pages") } + actions, err := instanceactions.ExtractInstanceActions(pages) if err != nil { panic("fail to list instance actions") @@ -18,6 +20,7 @@ Example: if err != nil { panic("fail to get instance action") } + fmt.Println(action) } */ diff --git a/openstack/compute/v2/extensions/instanceactions/request.go b/openstack/compute/v2/extensions/instanceactions/request.go index 27c12829b3..326081dfd0 100644 --- a/openstack/compute/v2/extensions/instanceactions/request.go +++ b/openstack/compute/v2/extensions/instanceactions/request.go @@ -1,13 +1,71 @@ package instanceactions import ( + "net/url" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToInstanceActionsListQuery() (string, error) +} + +// ListOpts represents options used to filter instance action results +// in a List request. +type ListOpts struct { + // Limit is an integer value to limit the results to return. + // This requires microversion 2.58 or later. + Limit int `q:"limit"` + + // Marker is the request ID of the last-seen instance action. + // This requires microversion 2.58 or later. + Marker string `q:"marker"` + + // ChangesSince filters the response by actions after the given time. + // This requires microversion 2.58 or later. + ChangesSince *time.Time `q:"changes-since"` + + // ChangesBefore filters the response by actions before the given time. + // This requires microversion 2.66 or later. + ChangesBefore *time.Time `q:"changes-before"` +} + +// ToInstanceActionsListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToInstanceActionsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + + if opts.ChangesSince != nil { + params.Add("changes-since", opts.ChangesSince.Format(time.RFC3339)) + } + + if opts.ChangesBefore != nil { + params.Add("changes-before", opts.ChangesBefore.Format(time.RFC3339)) + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), nil +} + // List makes a request against the API to list the servers actions. -func List(client *gophercloud.ServiceClient, id string) pagination.Pager { - return pagination.NewPager(client, ListURL(client, id), func(r pagination.PageResult) pagination.Page { +func List(client *gophercloud.ServiceClient, id string, opts ListOptsBuilder) pagination.Pager { + url := listURL(client, id) + if opts != nil { + query, err := opts.ToInstanceActionsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return InstanceActionPage{pagination.SinglePageBase(r)} }) } diff --git a/openstack/compute/v2/extensions/instanceactions/results.go b/openstack/compute/v2/extensions/instanceactions/results.go index f341ffcf21..90892a29a0 100644 --- a/openstack/compute/v2/extensions/instanceactions/results.go +++ b/openstack/compute/v2/extensions/instanceactions/results.go @@ -10,13 +10,26 @@ import ( // InstanceAction represents an instance action. type InstanceAction struct { - Action string `json:"action"` - InstanceUUID string `json:"instance_uuid"` - Message string `json:"message"` - ProjectID string `json:"project_id"` - RequestID string `json:"request_id"` - StartTime time.Time `json:"-"` - UserID string `json:"user_id"` + // Action is the name of the action. + Action string `json:"action"` + + // InstanceUUID is the UUID of the instance. + InstanceUUID string `json:"instance_uuid"` + + // Message is the related error message for when an action fails. + Message string `json:"message"` + + // Project ID is the ID of the project which initiated the action. + ProjectID string `json:"project_id"` + + // RequestID is the ID generated when performing the action. + RequestID string `json:"request_id"` + + // StartTime is the time the action started. + StartTime time.Time `json:"-"` + + // UserID is the ID of the user which initiated the action. + UserID string `json:"user_id"` } // UnmarshalJSON converts our JSON API response into our instance action struct @@ -37,7 +50,7 @@ func (i *InstanceAction) UnmarshalJSON(b []byte) error { return err } -// InstanceActionPage abstracts the raw results of making a ListInstanceActiones() request +// InstanceActionPage 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 ExtractInstanceActions call. @@ -51,7 +64,8 @@ func (r InstanceActionPage) IsEmpty() (bool, error) { return len(instanceactions) == 0, err } -// ListInstanceActiones() call, producing a map of instanceActions. +// ExtractInstanceActions interprets a page of results as a slice +// of InstanceAction. func ExtractInstanceActions(r pagination.Page) ([]InstanceAction, error) { var resp []InstanceAction err := ExtractInstanceActionsInto(r, &resp) @@ -60,20 +74,31 @@ func ExtractInstanceActions(r pagination.Page) ([]InstanceAction, error) { // Event represents an event of instance action. type Event struct { + // Event is the name of the event. Event string `json:"event"` + // Host is the host of the event. // This requires microversion 2.62 or later. Host *string `json:"host"` + // HostID is the host id of the event. // This requires microversion 2.62 or later. - HostID *string `json:"hostId"` - Result string `json:"result"` - Traceback string `json:"traceback"` - StartTime time.Time `json:"-"` + HostID *string `json:"hostId"` + + // Result is the result of the event. + Result string `json:"result"` + + // Traceback is the traceback stack if an error occurred. + Traceback string `json:"traceback"` + + // StartTime is the time the action started. + StartTime time.Time `json:"-"` + + // FinishTime is the time the event finished. FinishTime time.Time `json:"-"` } -// UnmarshalJSON converts our JSON API response into our instance action struct +// UnmarshalJSON converts our JSON API response into our instance action struct. func (e *Event) UnmarshalJSON(b []byte) error { type tmp Event var s struct { @@ -93,21 +118,36 @@ func (e *Event) UnmarshalJSON(b []byte) error { return err } -// InstanceActionDetail gives more details on instance action. +// InstanceActionDetail represents the details of an Action. type InstanceActionDetail struct { - Action string `json:"action"` + // Action is the name of the Action. + Action string `json:"action"` + + // InstanceUUID is the UUID of the instance. InstanceUUID string `json:"instance_uuid"` - Message string `json:"message"` - ProjectID string `json:"project_id"` - RequestID string `json:"request_id"` - UserID string `json:"user_id"` + + // Message is the related error message for when an action fails. + Message string `json:"message"` + + // Project ID is the ID of the project which initiated the action. + ProjectID string `json:"project_id"` + + // RequestID is the ID generated when performing the action. + RequestID string `json:"request_id"` + + // UserID is the ID of the user which initiated the action. + UserID string `json:"user_id"` + // Events is the list of events of the action. // This requires microversion 2.50 or later. Events *[]Event `json:"events"` + // UpdatedAt last update date of the action. // This requires microversion 2.58 or later. UpdatedAt *time.Time `json:"-"` - StartTime time.Time `json:"-"` + + // StartTime is the time the action started. + StartTime time.Time `json:"-"` } // UnmarshalJSON converts our JSON API response into our instance action struct @@ -134,7 +174,7 @@ type InstanceActionResult struct { gophercloud.Result } -// Extract interprets any instanceActionResult as an InstanceActionDetail, if possible. +// Extract interprets a result as an InstanceActionDetail. func (r InstanceActionResult) Extract() (InstanceActionDetail, error) { var s InstanceActionDetail err := r.ExtractInto(&s) diff --git a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go b/openstack/compute/v2/extensions/instanceactions/testing/request_test.go index c8e7350b1b..77b7e69412 100644 --- a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go +++ b/openstack/compute/v2/extensions/instanceactions/testing/request_test.go @@ -16,7 +16,7 @@ func TestList(t *testing.T) { expected := ListExpected pages := 0 - err := instanceactions.List(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) { + err := instanceactions.List(client.ServiceClient(), "asdfasdfasdf", nil).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := instanceactions.ExtractInstanceActions(page) diff --git a/openstack/compute/v2/extensions/instanceactions/urls.go b/openstack/compute/v2/extensions/instanceactions/urls.go index 36076f27f2..4239ade7fd 100644 --- a/openstack/compute/v2/extensions/instanceactions/urls.go +++ b/openstack/compute/v2/extensions/instanceactions/urls.go @@ -2,7 +2,7 @@ package instanceactions import "github.com/gophercloud/gophercloud" -func ListURL(client *gophercloud.ServiceClient, id string) string { +func listURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "os-instance-actions") } From a99dff9eb11b26adc19e3b880f24d6e8591dd33b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 19 Feb 2020 04:29:05 +0000 Subject: [PATCH 0986/2296] Acc Tests: instanceactions acceptance tests --- acceptance/openstack/compute/v2/compute.go | 108 +++++++++++++++-- .../compute/v2/instance_actions_test.go | 109 ++++++++++++++++++ .../openstack/compute/v2/network_test.go | 2 +- 3 files changed, 209 insertions(+), 10 deletions(-) create mode 100644 acceptance/openstack/compute/v2/instance_actions_test.go diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index d7668359b8..a105cefd0c 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -29,6 +29,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + neutron "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" "golang.org/x/crypto/ssh" @@ -78,7 +79,7 @@ func AttachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID s t.Fatal(err) } - networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) + networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } @@ -138,7 +139,7 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, t.Fatal(err) } - networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) + networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) if err != nil { return server, err } @@ -306,7 +307,7 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, t.Fatal(err) } - networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) + networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) if err != nil { return server, err } @@ -439,7 +440,7 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser t.Fatal(err) } - networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) + networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } @@ -487,6 +488,59 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser return newServer, nil } +// CreateMicroversionServer creates a basic instance compatible with +// newer microversions with a randomly generated name. +// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. +// The image will be the value of the OS_IMAGE_ID environment variable. +// The instance will be launched on the network specified in OS_NETWORK_NAME. +// An error will be returned if the instance was unable to be created. +func CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) + if err != nil { + return nil, err + } + + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create server: %s", name) + + pwd := tools.MakeNewPassword("") + + server, err := servers.Create(client, servers.CreateOpts{ + Name: name, + FlavorRef: choices.FlavorID, + ImageRef: choices.ImageID, + AdminPass: pwd, + Networks: []servers.Network{ + servers.Network{UUID: networkID}, + }, + Metadata: map[string]string{ + "abc": "def", + }, + }).Extract() + if err != nil { + return server, err + } + + if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { + return nil, err + } + + newServer, err := servers.Get(client, server.ID).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, newServer.Name, name) + th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + + return newServer, nil +} + // CreateServerWithoutImageRef creates a basic instance with a randomly generated name. // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. // The image is intentionally missing to trigger an error. @@ -498,7 +552,7 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient t.Fatal(err) } - networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) + networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } @@ -649,7 +703,7 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, t.Fatal(err) } - networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) + networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } @@ -704,7 +758,7 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, t.Fatal(err) } - networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) + networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) if err != nil { return nil, err } @@ -927,10 +981,10 @@ func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, flo t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID) } -// GetNetworkIDFromNetworks will return the network ID from a specified network +// GetNetworkIDFromOSNetworks will return the network ID from a specified network // UUID using the os-networks API extension. An error will be returned if the // network could not be retrieved. -func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { +func GetNetworkIDFromOSNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { allPages, err := networks.List(client).AllPages() if err != nil { t.Fatalf("Unable to list networks: %v", err) @@ -977,6 +1031,42 @@ func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceCli return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName) } +// GetNetworkIDFromNetworks will return the network UUID for a given network +// name using either the os-tenant-networks API extension or Neutron API. +// An error will be returned if the network could not be retrieved. +func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { + allPages, err := tenantnetworks.List(client).AllPages() + if err == nil { + allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) + if err != nil { + return "", err + } + + for _, network := range allTenantNetworks { + if network.Name == networkName { + return network.ID, nil + } + } + } + + networkClient, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + allPages2, err := neutron.List(networkClient, nil).AllPages() + th.AssertNoErr(t, err) + + allNetworks, err := neutron.ExtractNetworks(allPages2) + th.AssertNoErr(t, err) + + for _, network := range allNetworks { + if network.Name == networkName { + return network.ID, nil + } + } + + return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName) +} + // ImportPublicKey will create a KeyPair with a random name and a specified // public key. An error will be returned if the keypair failed to be created. func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey string) (*keypairs.KeyPair, error) { diff --git a/acceptance/openstack/compute/v2/instance_actions_test.go b/acceptance/openstack/compute/v2/instance_actions_test.go new file mode 100644 index 0000000000..a63f45766d --- /dev/null +++ b/acceptance/openstack/compute/v2/instance_actions_test.go @@ -0,0 +1,109 @@ +// +build acceptance compute limits + +package v2 + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/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" +) + +func TestInstanceActions(t *testing.T) { + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := CreateServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) + + allPages, err := instanceactions.List(client, server.ID, nil).AllPages() + th.AssertNoErr(t, err) + allActions, err := instanceactions.ExtractInstanceActions(allPages) + th.AssertNoErr(t, err) + + var found bool + + for _, action := range allActions { + action, err := instanceactions.Get(client, server.ID, action.RequestID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, action) + + if action.Action == "create" { + found = true + } + } + + th.AssertEquals(t, found, true) +} + +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() + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + client.Microversion = "2.66" + + server, err := CreateMicroversionServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) + + rebootOpts := servers.RebootOpts{ + Type: servers.HardReboot, + } + + err = servers.Reboot(client, server.ID, rebootOpts).ExtractErr() + if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { + t.Fatal(err) + } + + listOpts := instanceactions.ListOpts{ + Limit: 1, + ChangesSince: &now, + } + + allPages, err := instanceactions.List(client, server.ID, listOpts).AllPages() + th.AssertNoErr(t, err) + + allActions, err := instanceactions.ExtractInstanceActions(allPages) + th.AssertNoErr(t, err) + + var found bool + + for _, action := range allActions { + action, err := instanceactions.Get(client, server.ID, action.RequestID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, action) + + if action.Action == "reboot" { + found = true + } + } + + th.AssertEquals(t, found, true) + + listOpts = instanceactions.ListOpts{ + Limit: 1, + ChangesBefore: &now, + } + + allPages, err = instanceactions.List(client, server.ID, listOpts).AllPages() + th.AssertNoErr(t, err) + + allActions, err = instanceactions.ExtractInstanceActions(allPages) + th.AssertNoErr(t, err) + + th.AssertEquals(t, len(allActions), 0) +} diff --git a/acceptance/openstack/compute/v2/network_test.go b/acceptance/openstack/compute/v2/network_test.go index 25fbe4144c..cb5d396c9c 100644 --- a/acceptance/openstack/compute/v2/network_test.go +++ b/acceptance/openstack/compute/v2/network_test.go @@ -43,7 +43,7 @@ func TestNetworksGet(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName) + networkID, err := GetNetworkIDFromOSNetworks(t, client, choices.NetworkName) th.AssertNoErr(t, err) network, err := networks.Get(client, networkID).Extract() From f05a603dc682beb76d83bda54a2b4dd81cefbb87 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 19 Feb 2020 08:57:40 -0700 Subject: [PATCH 0987/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 554994b746..00bc3e0b2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ IMPROVEMENTS * 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) ## 0.8.0 (February 8, 2020) From 6a80c8e64c75826b481a8d03f8ffbede800f3a35 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 21 Feb 2020 09:33:20 -0700 Subject: [PATCH 0988/2296] Core: Support for int64 headers (#1860) --- params.go | 2 ++ testing/params_test.go | 16 +++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/params.go b/params.go index b9986660cb..219c020a24 100644 --- a/params.go +++ b/params.go @@ -450,6 +450,8 @@ func BuildHeaders(opts interface{}) (map[string]string, error) { optsMap[tags[0]] = v.String() case reflect.Int: optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10) + case reflect.Int64: + optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10) case reflect.Bool: optsMap[tags[0]] = strconv.FormatBool(v.Bool()) } diff --git a/testing/params_test.go b/testing/params_test.go index 6025366495..9d0de5d30f 100644 --- a/testing/params_test.go +++ b/testing/params_test.go @@ -90,15 +90,17 @@ func TestBuildQueryString(t *testing.T) { func TestBuildHeaders(t *testing.T) { testStruct := struct { - Accept string `h:"Accept"` - Num int `h:"Number" required:"true"` - Style bool `h:"Style"` + Accept string `h:"Accept"` + ContentLength int64 `h:"Content-Length"` + Num int `h:"Number" required:"true"` + Style bool `h:"Style"` }{ - Accept: "application/json", - Num: 4, - Style: true, + Accept: "application/json", + ContentLength: 256, + Num: 4, + Style: true, } - expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true"} + expected := map[string]string{"Accept": "application/json", "Number": "4", "Style": "true", "Content-Length": "256"} actual, err := gophercloud.BuildHeaders(&testStruct) th.CheckNoErr(t, err) th.CheckDeepEquals(t, expected, actual) From ab34fe07c3bff4b35977237c68d7dabbf3e81e5f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 21 Feb 2020 09:34:16 -0700 Subject: [PATCH 0989/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00bc3e0b2f..a9e5a1da54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ IMPROVEMENTS * 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) +BUG FIXES + +* Added support for `int64` headers, which were previously being silently dropped [GH-1860](https://github.com/gophercloud/gophercloud/pull/1860) + ## 0.8.0 (February 8, 2020) UPGRADE NOTES From 6484dba004402c2a36ee67d976a5a8d85d9705fa Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 29 Feb 2020 03:56:44 +0100 Subject: [PATCH 0990/2296] Object storage: add container temp-url support (#1864) --- openstack/objectstorage/v1/containers/requests.go | 2 ++ openstack/objectstorage/v1/containers/results.go | 2 ++ openstack/objectstorage/v1/objects/requests.go | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index ca99bb2a6a..5f5a4c82fe 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -141,6 +141,8 @@ type UpdateOpts struct { VersionsLocation string `h:"X-Versions-Location"` RemoveHistoryLocation string `h:"X-Remove-History-Location"` 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"` } // 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 cce2190ff9..b94eda95e1 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -103,6 +103,8 @@ type GetHeader struct { HistoryLocation string `json:"X-History-Location"` Write []string `json:"-"` StoragePolicy string `json:"X-Storage-Policy"` + TempURLKey string `json:"X-Container-Meta-Temp-URL-Key"` + TempURLKey2 string `json:"X-Container-Meta-Temp-URL-Key-2"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 7325cd7d0b..a9649d97a8 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -13,6 +13,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" + "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/pagination" ) @@ -482,11 +483,20 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin } duration := time.Duration(opts.TTL) * time.Second expiry := time.Now().Add(duration).Unix() - getHeader, err := accounts.Get(c, nil).Extract() + getHeader, err := containers.Get(c, containerName, nil).Extract() if err != nil { return "", err } - secretKey := []byte(getHeader.TempURLKey) + 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 + } + secretKey := []byte(tempURLKey) url := getURL(c, containerName, objectName) splitPath := strings.Split(url, opts.Split) baseURL, objectPath := splitPath[0], splitPath[1] From 4e6a2f1cd2c98bcc4f617c247360bfbf47f0e7f1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 28 Feb 2020 20:00:38 -0700 Subject: [PATCH 0991/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9e5a1da54..e3461aca92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ IMPROVEMENTS * 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.X-Container-Meta-Temp-URL-Key` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) +* Added `objectstorage/v1/containers.UpdateOpts.X-Container-Meta-Temp-URL-Key2` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) + BUG FIXES From b79ada02ef8be44f980590619e80d11cefe79133 Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Fri, 28 Feb 2020 19:32:55 -0800 Subject: [PATCH 0992/2296] Add resource provider inventories API for placement (#1862) * Add resource provider create API for placement. * update doc * Add resource provider usages API for placement * fix go.mod * Add resource provider inventories API for placement * fix go.mod; * Change Usage to map[string]int * use map[string]Inventory --- .../placement/v1/resourceproviders_test.go | 50 ++++++++ .../placement/v1/resourceproviders/doc.go | 14 +++ .../v1/resourceproviders/requests.go | 10 ++ .../placement/v1/resourceproviders/results.go | 45 +++++++ .../v1/resourceproviders/testing/fixtures.go | 114 ++++++++++++++++++ .../testing/requests_test.go | 22 ++++ .../placement/v1/resourceproviders/urls.go | 8 ++ 7 files changed, 263 insertions(+) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 8def92ef66..67553f19d5 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -45,3 +45,53 @@ func TestResourceProviderCreate(t *testing.T) { tools.PrintResource(t, resourceProvider) } + +func TestResourceProviderUsages(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + 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() + th.AssertNoErr(t, err) + + // now get the usages for the newly created resource provider + usage, err := resourceproviders.GetUsages(client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, usage) +} + +func TestResourceProviderInventories(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + 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() + th.AssertNoErr(t, err) + + // now get the inventories for the newly created resource provider + usage, err := resourceproviders.GetInventories(client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, usage) +} diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 9d9bafe18b..d14b203f79 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -29,5 +29,19 @@ Example to create resource providers panic(err) } +Example to get resource providers usages + + rp, err := resourceproviders.GetUsages(placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } + +Example to get resource providers inventories + + rp, err := resourceproviders.GetInventories(placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } + */ package resourceproviders diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 2992af05e4..d868b50864 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -102,3 +102,13 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } + +func GetUsages(client *gophercloud.ServiceClient, resourceProviderID string) (r GetUsagesResult) { + _, r.Err = client.Get(getResourceProviderUsagesURL(client, resourceProviderID), &r.Body, nil) + return +} + +func GetInventories(client *gophercloud.ServiceClient, resourceProviderID string) (r GetInventoriesResult) { + _, r.Err = client.Get(getResourceProviderInventoriesURL(client, resourceProviderID), &r.Body, nil) + return +} diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index 350410f621..c1d676b480 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -33,6 +33,25 @@ type ResourceProvider struct { RootProviderUUID string `json:"root_provider_uuid"` } +type ResourceProviderUsage struct { + ResourceProviderGeneration int `json:"resource_provider_generation"` + Usages map[string]int `json:"usages"` +} + +type Inventory struct { + AllocationRatio float32 `json:"allocation_ratio"` + MaxUnit int `json:"max_unit"` + MinUnit int `json:"min_unit"` + Reserved int `json:"reserved"` + StepSize int `json:"step_size"` + Total int `json:"total"` +} + +type ResourceProviderInventories struct { + ResourceProviderGeneration int `json:"resource_provider_generation"` + Inventories map[string]Inventory `json:"inventories"` +} + // resourceProviderResult is the resposne of a base ResourceProvider result. type resourceProviderResult struct { gophercloud.Result @@ -71,3 +90,29 @@ func ExtractResourceProviders(r pagination.Page) ([]ResourceProvider, error) { err := (r.(ResourceProvidersPage)).ExtractInto(&s) return s.ResourceProviders, err } + +// GetUsagesResult is the response of a Get usage operations. Call its Extract method +// to interpret it as a ResourceProviderUsage. +type GetUsagesResult struct { + gophercloud.Result +} + +// Extract interprets a GetUsagesResult as a ResourceProviderUsage. +func (r GetUsagesResult) Extract() (*ResourceProviderUsage, error) { + var s ResourceProviderUsage + err := r.ExtractInto(&s) + return &s, err +} + +// GetInventoriesResult is the response of a Get usage operations. Call its Extract method +// to interpret it as a ResourceProviderInventories. +type GetInventoriesResult struct { + gophercloud.Result +} + +// Extract interprets a GetInventoriesResult as a ResourceProviderInventories. +func (r GetInventoriesResult) Extract() (*ResourceProviderInventories, error) { + var s ResourceProviderInventories + err := r.ExtractInto(&s) + return &s, err +} diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures.go index e0807a6c6c..dd7fd7ee66 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -11,6 +11,8 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +const ResourceProviderTestID = "99c09379-6e52-4ef8-9a95-b9ce6f68452e" + const ResourceProvidersBody = ` { "resource_providers": [ @@ -60,6 +62,49 @@ const ResourceProviderCreateBody = ` } ` +const UsagesBody = ` +{ + "resource_provider_generation": 1, + "usages": { + "DISK_GB": 1, + "MEMORY_MB": 512, + "VCPU": 1 + } +} +` + +const InventoriesBody = ` +{ + "inventories": { + "DISK_GB": { + "allocation_ratio": 1.0, + "max_unit": 35, + "min_unit": 1, + "reserved": 0, + "step_size": 1, + "total": 35 + }, + "MEMORY_MB": { + "allocation_ratio": 1.5, + "max_unit": 5825, + "min_unit": 1, + "reserved": 512, + "step_size": 1, + "total": 5825 + }, + "VCPU": { + "allocation_ratio": 16.0, + "max_unit": 4, + "min_unit": 1, + "reserved": 0, + "step_size": 1, + "total": 4 + } + }, + "resource_provider_generation": 7 +} +` + var ExpectedResourceProvider1 = resourceproviders.ResourceProvider{ Generation: 1, UUID: "99c09379-6e52-4ef8-9a95-b9ce6f68452e", @@ -93,6 +138,45 @@ var ExpectedResourceProviders = []resourceproviders.ResourceProvider{ ExpectedResourceProvider2, } +var ExpectedUsages = resourceproviders.ResourceProviderUsage{ + ResourceProviderGeneration: 1, + Usages: map[string]int{ + "DISK_GB": 1, + "MEMORY_MB": 512, + "VCPU": 1, + }, +} + +var ExpectedInventories = resourceproviders.ResourceProviderInventories{ + ResourceProviderGeneration: 7, + Inventories: map[string]resourceproviders.Inventory{ + "DISK_GB": resourceproviders.Inventory{ + AllocationRatio: 1.0, + MaxUnit: 35, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 35, + }, + "MEMORY_MB": resourceproviders.Inventory{ + AllocationRatio: 1.5, + MaxUnit: 5825, + MinUnit: 1, + Reserved: 512, + StepSize: 1, + Total: 5825, + }, + "VCPU": resourceproviders.Inventory{ + AllocationRatio: 16.0, + MaxUnit: 4, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 4, + }, + }, +} + func HandleResourceProviderList(t *testing.T) { th.Mux.HandleFunc("/resource_providers", func(w http.ResponseWriter, r *http.Request) { @@ -117,3 +201,33 @@ func HandleResourceProviderCreate(t *testing.T) { fmt.Fprintf(w, ResourceProviderCreateBody) }) } + +func HandleResourceProviderGetUsages(t *testing.T) { + usageTestUrl := fmt.Sprintf("/resource_providers/%s/usages", ResourceProviderTestID) + + th.Mux.HandleFunc(usageTestUrl, + 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, UsagesBody) + }) +} + +func HandleResourceProviderGetInventories(t *testing.T) { + inventoriesTestUrl := fmt.Sprintf("/resource_providers/%s/inventories", ResourceProviderTestID) + + th.Mux.HandleFunc(inventoriesTestUrl, + 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, InventoriesBody) + }) +} diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 52b974d250..75c5532a42 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -55,3 +55,25 @@ func TestCreateResourceProvider(t *testing.T) { th.AssertDeepEquals(t, &expected, actual) } + +func TestGetResourceProvidersUsages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderGetUsages(t) + + actual, err := resourceproviders.GetUsages(fake.ServiceClient(), ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedUsages, *actual) +} + +func TestGetResourceProvidersInventories(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderGetInventories(t) + + actual, err := resourceproviders.GetInventories(fake.ServiceClient(), ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedInventories, *actual) +} diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 6b60f03614..2caaaded06 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -9,3 +9,11 @@ const ( func resourceProvidersListURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(apiName) } + +func getResourceProviderUsagesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID, "usages") +} + +func getResourceProviderInventoriesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID, "inventories") +} From c021acc4ae8c6d7cc22843f56e45f379a895b7c6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 28 Feb 2020 20:34:59 -0700 Subject: [PATCH 0993/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3461aca92..ce9f6d0c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ IMPROVEMENTS * Added `compute/v2/extensions/instanceactions.ListOpts` [GH-1858](https://github.com/gophercloud/gophercloud/pull/1858) * Added `objectstorage/v1/containers.UpdateOpts.X-Container-Meta-Temp-URL-Key` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) * Added `objectstorage/v1/containers.UpdateOpts.X-Container-Meta-Temp-URL-Key2` [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) BUG FIXES From caf90ee151cea01ae942f907fbb8af71711c767a Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 29 Feb 2020 19:58:59 +0100 Subject: [PATCH 0994/2296] Glance V2: Add an image "min_ram" attribute modification support (#1867) --- openstack/imageservice/v2/images/requests.go | 14 ++++++++++++++ .../imageservice/v2/images/testing/fixtures.go | 7 ++++++- .../v2/images/testing/requests_test.go | 3 ++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 4e487ea9e6..e1fcd09720 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -335,6 +335,20 @@ func (r ReplaceImageMinDisk) ToImagePatchMap() map[string]interface{} { } } +// ReplaceImageMinRam represents an updated min_ram property request. +type ReplaceImageMinRam struct { + NewMinRam int +} + +// ToImagePatchMap assembles a request body based on ReplaceImageTags. +func (r ReplaceImageMinRam) ToImagePatchMap() map[string]interface{} { + return map[string]interface{}{ + "op": "replace", + "path": "/min_ram", + "value": r.NewMinRam, + } +} + // UpdateOp represents a valid update operation. type UpdateOp string diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index 0ff03be95c..625bbe928d 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -317,6 +317,11 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "op": "replace", "path": "/min_disk", "value": 21 + }, + { + "op": "replace", + "path": "/min_ram", + "value": 1024 } ]`) @@ -341,7 +346,7 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "file": "/v2/images/da3b75d9-3f4a-40e7-8a2c-bfab23927dea/file", "schema": "/v2/schemas/image", "owner": "", - "min_ram": 0, + "min_ram": 1024, "min_disk": 21, "disk_format": "", "virtual_size": 0, diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index 86126c76d9..e06d29fb38 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -255,6 +255,7 @@ func TestUpdateImage(t *testing.T) { images.ReplaceImageName{NewName: "Fedora 17"}, images.ReplaceImageTags{NewTags: []string{"fedora", "beefy"}}, images.ReplaceImageMinDisk{NewMinDisk: 21}, + images.ReplaceImageMinRam{NewMinRam: 1024}, }).Extract() th.AssertNoErr(t, err) @@ -281,7 +282,7 @@ func TestUpdateImage(t *testing.T) { }, Owner: "", - MinRAMMegabytes: 0, + MinRAMMegabytes: 1024, MinDiskGigabytes: 21, DiskFormat: "", From e59e714f1df82d145edf133db643e30a6e264722 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 29 Feb 2020 11:59:58 -0700 Subject: [PATCH 0995/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce9f6d0c47..b900b3b953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ IMPROVEMENTS * Added `objectstorage/v1/containers.UpdateOpts.X-Container-Meta-Temp-URL-Key2` [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) BUG FIXES From fa1a4cb02619055db86ceda3221eb2ee5bc65ee5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 29 Feb 2020 12:00:14 -0700 Subject: [PATCH 0996/2296] Object Storage v1: Add TempURL headers to Container CreateOpts (#1865) --- openstack/objectstorage/v1/containers/requests.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 5f5a4c82fe..2e710ab0c6 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -75,6 +75,8 @@ type CreateOpts struct { IfNoneMatch string `h:"If-None-Match"` VersionsLocation string `h:"X-Versions-Location"` 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"` } // ToContainerCreateMap formats a CreateOpts into a map of headers. From 7cab38d5435775a5e3dea912c564271afa433ff0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 29 Feb 2020 12:01:44 -0700 Subject: [PATCH 0997/2296] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b900b3b953..b24a6808af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,14 @@ IMPROVEMENTS * 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.X-Container-Meta-Temp-URL-Key` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) -* Added `objectstorage/v1/containers.UpdateOpts.X-Container-Meta-Temp-URL-Key2` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864) +* 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) + BUG FIXES From 4cc0abf504dcb60481953b14f11c486382c72681 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 2 Mar 2020 16:55:20 +0100 Subject: [PATCH 0998/2296] Cinver V3: Add volume transport support (#1869) --- .../extensions/volumetransfers/doc.go | 65 ++++++ .../extensions/volumetransfers/requests.go | 120 ++++++++++ .../extensions/volumetransfers/results.go | 102 +++++++++ .../volumetransfers/testing/fixtures.go | 216 ++++++++++++++++++ .../volumetransfers/testing/requests_test.go | 90 ++++++++ .../extensions/volumetransfers/urls.go | 23 ++ 6 files changed, 616 insertions(+) create mode 100644 openstack/blockstorage/extensions/volumetransfers/doc.go create mode 100644 openstack/blockstorage/extensions/volumetransfers/requests.go create mode 100644 openstack/blockstorage/extensions/volumetransfers/results.go create mode 100644 openstack/blockstorage/extensions/volumetransfers/testing/fixtures.go create mode 100644 openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go create mode 100644 openstack/blockstorage/extensions/volumetransfers/urls.go diff --git a/openstack/blockstorage/extensions/volumetransfers/doc.go b/openstack/blockstorage/extensions/volumetransfers/doc.go new file mode 100644 index 0000000000..9d842e05b6 --- /dev/null +++ b/openstack/blockstorage/extensions/volumetransfers/doc.go @@ -0,0 +1,65 @@ +/* +Package volumetransfers provides an interaction with volume transfers in the +OpenStack Block Storage service. A volume transfer allows to transfer volumes +between projects withing the same OpenStack region. + +Example to List all Volume Transfer requests being an OpenStack admin + + listOpts := &volumetransfers.ListOpts{ + // this option is available only for OpenStack cloud admin + AllTenants: true, + } + + allPages, err := volumetransfers.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allTransfers, err := volumetransfers.ExtractTransfers(allPages) + if err != nil { + panic(err) + } + + for _, transfer := range allTransfers { + fmt.Println(transfer) + } + +Example to Create a Volume Transfer request + + createOpts := volumetransfers.CreateOpts{ + VolumeID: "uuid", + Name: "my-volume-transfer", + } + + transfer, err := volumetransfers.Create(client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(transfer) + // secret auth key is returned only once as a create response + fmt.Printf("AuthKey: %s\n", transfer.AuthKey) + +Example to Accept a Volume Transfer request from the target project + + acceptOpts := volumetransfers.AcceptOpts{ + // see the create response above + AuthKey: "volume-transfer-secret-auth-key", + } + + // see the transfer ID from the create response above + transfer, err := volumetransfers.Accept(client, "transfer-uuid", acceptOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(transfer) + +Example to Delete a Volume Transfer request from the source project + + err := volumetransfers.Delete(client, "transfer-uuid").ExtractErr() + if err != nil { + panic(err) + } +*/ +package volumetransfers diff --git a/openstack/blockstorage/extensions/volumetransfers/requests.go b/openstack/blockstorage/extensions/volumetransfers/requests.go new file mode 100644 index 0000000000..eb0c04d048 --- /dev/null +++ b/openstack/blockstorage/extensions/volumetransfers/requests.go @@ -0,0 +1,120 @@ +package volumetransfers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOpts contains options for a Volume transfer. +type CreateOpts struct { + // The ID of the volume to transfer. + VolumeID string `json:"volume_id" required:"true"` + + // The name of the volume transfer + Name string `json:"name,omitempty"` +} + +// ToCreateMap assembles a request body based on the contents of a +// TransferOpts. +func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "transfer") +} + +// Create will create a volume tranfer request based on the values in CreateOpts. +func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := opts.ToCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} + +// AcceptOpts contains options for a Volume transfer accept reqeust. +type AcceptOpts struct { + // The auth key of the volume transfer to accept. + AuthKey string `json:"auth_key" required:"true"` +} + +// 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 volume tranfer request based on the values in AcceptOpts. +func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { + b, err := opts.ToAcceptMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} + +// Delete deletes a volume transfer. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + 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 transfers.List +// function. +type ListOpts struct { + // AllTenants will retrieve transfers of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// 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 { + return TransferPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// 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) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} diff --git a/openstack/blockstorage/extensions/volumetransfers/results.go b/openstack/blockstorage/extensions/volumetransfers/results.go new file mode 100644 index 0000000000..3217174a8a --- /dev/null +++ b/openstack/blockstorage/extensions/volumetransfers/results.go @@ -0,0 +1,102 @@ +package volumetransfers + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Transfer represents a Volume Transfer record +type Transfer struct { + ID string `json:"id"` + AuthKey string `json:"auth_key"` + Name string `json:"name"` + VolumeID string `json:"volume_id"` + CreatedAt 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"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Transfer(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + + 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 +} + +// 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.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Transfers. +func (r TransferPage) IsEmpty() (bool, error) { + transfers, err := ExtractTransfers(r) + return len(transfers) == 0, err +} + +func (page TransferPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"transfers_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} diff --git a/openstack/blockstorage/extensions/volumetransfers/testing/fixtures.go b/openstack/blockstorage/extensions/volumetransfers/testing/fixtures.go new file mode 100644 index 0000000000..9714323f9d --- /dev/null +++ b/openstack/blockstorage/extensions/volumetransfers/testing/fixtures.go @@ -0,0 +1,216 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const ListOutput = ` +{ + "transfers": [ + { + "created_at": "2020-02-28T12:44:28.051989", + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null + } + ] +} +` + +const GetOutput = ` +{ + "transfer": { + "created_at": "2020-02-28T12:44:28.051989", + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null + } +} +` + +const CreateRequest = ` +{ + "transfer": { + "volume_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://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null, + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +const AcceptTransferRequest = ` +{ + "accept": { + "auth_key": "9266c59563c84664" + } +} +` + +const AcceptTransferResponse = ` +{ + "transfer": { + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null, + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +var TransferRequest = volumetransfers.CreateOpts{ + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", +} + +var createdAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-02-28T12:44:28.051989") +var TransferResponse = volumetransfers.Transfer{ + ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + AuthKey: "cb67e0e7387d9eac", + Name: "", + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", + CreatedAt: createdAt, + Links: []map[string]string{ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self", + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark", + }, + }, +} + +var TransferListResponse = []volumetransfers.Transfer{TransferResponse} + +var AcceptRequest = volumetransfers.AcceptOpts{ + AuthKey: "9266c59563c84664", +} + +var AcceptResponse = volumetransfers.Transfer{ + ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + Name: "", + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", + Links: []map[string]string{ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self", + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark", + }, + }, +} + +func HandleCreateTransfer(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer", 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("/os-volume-transfer/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) + fmt.Fprintf(w, AcceptTransferResponse) + }) +} + +func HandleDeleteTransfer(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer/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.StatusNoContent) + }) +} + +func HandleListTransfers(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer/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("/os-volume-transfer/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/blockstorage/extensions/volumetransfers/testing/requests_test.go b/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go new file mode 100644 index 0000000000..85dc359620 --- /dev/null +++ b/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go @@ -0,0 +1,90 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" + "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 := volumetransfers.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) + + actual, err := volumetransfers.Accept(client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, AcceptResponse, *actual) +} + +func TestDeleteTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteTransfer(t) + + err := volumetransfers.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 := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := volumetransfers.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 := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).AllPages() + th.AssertNoErr(t, err) + actual, err := volumetransfers.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 := volumetransfers.Get(client.ServiceClient(), TransferResponse.ID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedResponse, *actual) +} diff --git a/openstack/blockstorage/extensions/volumetransfers/urls.go b/openstack/blockstorage/extensions/volumetransfers/urls.go new file mode 100644 index 0000000000..cf06dc4940 --- /dev/null +++ b/openstack/blockstorage/extensions/volumetransfers/urls.go @@ -0,0 +1,23 @@ +package volumetransfers + +import "github.com/gophercloud/gophercloud" + +func transferURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-volume-transfer") +} + +func acceptURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id, "accept") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-volume-transfer", "detail") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id) +} From f94e877d798670f0d5b234e48abdb9974d5b5f21 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Mar 2020 08:57:18 -0700 Subject: [PATCH 0999/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b24a6808af..b21cf95775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,11 @@ IMPROVEMENTS * 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) BUG FIXES From 04ca24e4f830978a2bab1e55443718ee5ffc448e Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 3 Mar 2020 04:36:20 +0100 Subject: [PATCH 1000/2296] Glance V2: Handle empty value in image update opts (#1875) --- openstack/imageservice/v2/images/requests.go | 2 +- openstack/imageservice/v2/images/testing/fixtures.go | 6 ++++++ openstack/imageservice/v2/images/testing/requests_test.go | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index e1fcd09720..d0a9eb572a 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -372,7 +372,7 @@ func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { "path": fmt.Sprintf("/%s", r.Name), } - if r.Value != "" { + if r.Op != RemoveOp { updateMap["value"] = r.Value } diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index 625bbe928d..357072f5cf 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -322,6 +322,11 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "op": "replace", "path": "/min_ram", "value": 1024 + }, + { + "op": "add", + "path": "/empty_value", + "value": "" } ]`) @@ -351,6 +356,7 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "disk_format": "", "virtual_size": 0, "container_format": "", + "empty_value": "", "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index e06d29fb38..d5c3f600c1 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -256,6 +256,11 @@ func TestUpdateImage(t *testing.T) { images.ReplaceImageTags{NewTags: []string{"fedora", "beefy"}}, images.ReplaceImageMinDisk{NewMinDisk: 21}, images.ReplaceImageMinRam{NewMinRam: 1024}, + images.UpdateImageProperty{ + Op: images.AddOp, + Name: "empty_value", + Value: "", + }, }).Extract() th.AssertNoErr(t, err) @@ -296,6 +301,7 @@ func TestUpdateImage(t *testing.T) { "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", + "empty_value": "", }, } From e3cce6f8fe1bcd91ae4b83f76b67c97464af7b0b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Mar 2020 20:37:05 -0700 Subject: [PATCH 1001/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b21cf95775..97f1c7c3b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ IMPROVEMENTS 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) ## 0.8.0 (February 8, 2020) From a87527328216a750678e3bc100b8c0e4cd607667 Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Mon, 2 Mar 2020 19:38:01 -0800 Subject: [PATCH 1002/2296] openstack/networking/v2/extensions/trunks/testing: Fix Two Dropped Errors (#1877) * openstack/networking/v2/extensions/trunks/testing: fix dropped error in ExpectedSubportsRemovedTrunk() * openstack/networking/v2/extensions/trunks/testing: fix dropped error in ExpectedSubportsAddedTrunk() --- .../networking/v2/extensions/trunks/testing/fixtures.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures.go b/openstack/networking/v2/extensions/trunks/testing/fixtures.go index b84c5a5736..d8d7b2aa53 100644 --- a/openstack/networking/v2/extensions/trunks/testing/fixtures.go +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures.go @@ -360,6 +360,9 @@ func ExpectedTrunkSlice() (exp []trunks.Trunk, err error) { func ExpectedSubportsAddedTrunk() (exp trunks.Trunk, err error) { trunkUpdatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:30Z") + if err != nil { + return + } expectedTrunks, err := ExpectedTrunkSlice() if err != nil { return @@ -372,6 +375,9 @@ func ExpectedSubportsAddedTrunk() (exp trunks.Trunk, err error) { func ExpectedSubportsRemovedTrunk() (exp trunks.Trunk, err error) { trunkUpdatedAt, err := time.Parse(time.RFC3339, "2018-10-03T13:57:27Z") + if err != nil { + return + } expectedTrunks, err := ExpectedTrunkSlice() if err != nil { return From 058158b02f57d8076aae06a8e740719bcd631b17 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 3 Mar 2020 16:36:13 +0100 Subject: [PATCH 1003/2296] Glance V2: Introduce an IDFromName helper in images package (#1879) --- openstack/blockstorage/v1/volumes/requests.go | 2 +- openstack/blockstorage/v2/volumes/requests.go | 2 +- openstack/blockstorage/v3/volumes/requests.go | 2 +- openstack/imageservice/v2/images/requests.go | 36 +++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go index 3e3ede1996..511e2c2218 100644 --- a/openstack/blockstorage/v1/volumes/requests.go +++ b/openstack/blockstorage/v1/volumes/requests.go @@ -135,7 +135,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// IDFromName is a convienience function that returns a server's ID given its name. +// IDFromName is a convienience function that returns a volume's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index c27ddbf67c..ed6d2aa9bc 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -198,7 +198,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// IDFromName is a convienience function that returns a server's ID given its name. +// IDFromName is a convienience function that returns a volume's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 25f70b27c1..4f5c66ac98 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -200,7 +200,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// IDFromName is a convienience function that returns a server's ID given its name. +// IDFromName is a convienience function that returns a volume's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index d0a9eb572a..c67a6a9d47 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -378,3 +378,39 @@ func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { return updateMap } + +// IDFromName is a convienience function that returns an image's ID given its name. +func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { + count := 0 + id := "" + + listOpts := ListOpts{ + Name: name, + } + + pages, err := List(client, listOpts).AllPages() + if err != nil { + return "", err + } + + all, err := ExtractImages(pages) + if err != nil { + return "", err + } + + for _, s := range all { + if s.Name == name { + count++ + id = s.ID + } + } + + switch count { + case 0: + return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "image"} + case 1: + return id, nil + default: + return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "image"} + } +} From 3e8758f3750bc83a59ecc321e5f36294895d87b9 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 3 Mar 2020 16:44:28 +0100 Subject: [PATCH 1004/2296] Cinder V3: Support "protected" and "visibility" parameters in "UploadImageOpts (#1873) * Cinder V3: Support "protected" and "visibility" parameters in "UploadImageOpts * Use a simple string for the visibility --- .../blockstorage/extensions/volumeactions/requests.go | 8 ++++++++ .../blockstorage/extensions/volumeactions/results.go | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index fdf322d3e9..c2d86498a8 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -241,6 +241,14 @@ type UploadImageOpts struct { // Force image creation, usable if volume attached to instance. Force bool `json:"force,omitempty"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility,omitempty"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected,omitempty"` } // ToVolumeUploadImageMap assembles a request body based on the contents of a diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index aacee4c53b..8587327cf6 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -157,6 +157,14 @@ type VolumeImage struct { // Current status of the volume. Status string `json:"status"` + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected"` + // The date when this volume was last updated. UpdatedAt time.Time `json:"-"` From 675de7268e2618a557195c92ac49d1cc6d5e0174 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Mar 2020 08:46:31 -0700 Subject: [PATCH 1005/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f1c7c3b9..e381c3ff2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,10 @@ IMPROVEMENTS * Added `InsertHeaders` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1835] * Added `NUMATopology` to `baremetalintrospection/v1/introspection.Data` [GH-1842](https://github.com/gophercloud/gophercloud/pull/1842) * Added `placement/v1/resourceproviders.Create` [GH-1841](https://github.com/gophercloud/gophercloud/pull/1841) +* Added `blockstorage/extensions/volumeactions.UploadImageOpts.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) +* Added `blockstorage/extensions/volumeactions.UploadImageOpts.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) +* Added `blockstorage/extensions/volumeactions.VolumeImage.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) +* Added `blockstorage/extensions/volumeactions.VolumeImage.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873) BUG FIXES From fb6683c1933c592182b01568b4cd4ce2a782d88b Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 4 Mar 2020 05:23:08 +0100 Subject: [PATCH 1006/2296] Compute V2: Fix userdata result extension (#1881) * Compute V2: Fix userdata result extension * Don't decode user_data --- .../compute/v2/extensions/extendedserverattributes/results.go | 2 +- .../v2/extensions/extendedserverattributes/testing/fixtures.go | 2 +- .../extendedserverattributes/testing/requests_test.go | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openstack/compute/v2/extensions/extendedserverattributes/results.go b/openstack/compute/v2/extensions/extendedserverattributes/results.go index 313886f1a6..1640cf5106 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/results.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/results.go @@ -40,5 +40,5 @@ type ServerAttributesExt struct { // Userdata is the userdata of the instance. // This requires microversion 2.3 or later. - Userdata *string `json:"OS-EXT-SRV-ATTR:userdata"` + Userdata *string `json:"OS-EXT-SRV-ATTR:user_data"` } diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go index c2f0b83e80..1034bee09f 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures.go @@ -6,7 +6,7 @@ package testing const ServerWithAttributesExtResult = ` { "server": { - "OS-EXT-SRV-ATTR:user_data": "", + "OS-EXT-SRV-ATTR:user_data": "Zm9v", "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", "OS-EXT-SRV-ATTR:hostname": "test00", diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go index c227541243..a9d5d24b3b 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go @@ -36,6 +36,7 @@ func TestServerWithUsageExt(t *testing.T) { th.AssertEquals(t, serverWithAttributesExt.Host, "compute01") th.AssertEquals(t, serverWithAttributesExt.InstanceName, "instance-00000001") th.AssertEquals(t, serverWithAttributesExt.HypervisorHostname, "compute01") + th.AssertEquals(t, *serverWithAttributesExt.Userdata, "Zm9v") th.AssertEquals(t, *serverWithAttributesExt.ReservationID, "r-ky9gim1l") th.AssertEquals(t, *serverWithAttributesExt.LaunchIndex, 0) th.AssertEquals(t, *serverWithAttributesExt.Hostname, "test00") From cf66cbfe00e475692cf3a982e4180798785e085d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 3 Mar 2020 20:24:35 -0800 Subject: [PATCH 1007/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e381c3ff2a..dea88de805 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ 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) From 15c4d46b8c4d3980f11b0ad5e4bf51f1e1560aa6 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 4 Mar 2020 17:03:52 +0100 Subject: [PATCH 1008/2296] Cinder V3: Restore a volume from a backup (#1871) * Cinder V3: Add a Restore method for backups * Cinder V3: Create a volume from a backup * Add omitempty for the size to simplify the code * Rename BackupRestore to RestoreFromBackup * Back to a normal string for backup_id * Code review --- .../blockstorage/extensions/backups/doc.go | 14 ++++++ .../extensions/backups/requests.go | 31 +++++++++++++ .../extensions/backups/results.go | 29 ++++++++++++ .../extensions/backups/testing/fixtures.go | 34 ++++++++++++++ .../backups/testing/requests_test.go | 15 +++++++ .../blockstorage/extensions/backups/urls.go | 4 ++ openstack/blockstorage/v3/volumes/doc.go | 26 +++++++++-- openstack/blockstorage/v3/volumes/requests.go | 5 ++- openstack/blockstorage/v3/volumes/results.go | 3 ++ .../v3/volumes/testing/fixtures.go | 45 +++++++++++++++++++ .../v3/volumes/testing/requests_test.go | 19 ++++++++ 11 files changed, 220 insertions(+), 5 deletions(-) diff --git a/openstack/blockstorage/extensions/backups/doc.go b/openstack/blockstorage/extensions/backups/doc.go index 0897f639bd..c43e4e2808 100644 --- a/openstack/blockstorage/extensions/backups/doc.go +++ b/openstack/blockstorage/extensions/backups/doc.go @@ -51,6 +51,20 @@ Example to Update a Backup fmt.Println(backup) +Example to Restore a Backup to a Volume + + options := backups.RestoreOpts{ + VolumeID: "1234", + Name: "vol-001", + } + + restore, err := backups.RestoreFromBackup(client, "uuid", options).Extract() + if err != nil { + panic(err) + } + + fmt.Println(restore) + Example to Delete a Backup err := backups.Delete(client, "uuid").ExtractErr() diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/extensions/backups/requests.go index a312f1daa0..c5613e22b9 100644 --- a/openstack/blockstorage/extensions/backups/requests.go +++ b/openstack/blockstorage/extensions/backups/requests.go @@ -182,3 +182,34 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder }) return } + +// RestoreOpts contains options for restoring a Backup. This object is passed to +// the backups.RestoreFromBackup function. +type RestoreOpts struct { + // VolumeID is the ID of the existing volume to restore the backup to. + VolumeID string `json:"volume_id,omitempty"` + + // Name is the name of the new volume to restore the backup to. + Name string `json:"name,omitempty"` +} + +// ToRestoreMap assembles a request body based on the contents of a +// RestoreOpts. +func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "restore") +} + +// RestoreFromBackup will restore a Backup to a volume based on the values in +// RestoreOpts. To extract the Restore object from the response, call the +// Extract method on the RestoreResult. +func RestoreFromBackup(client *gophercloud.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { + b, err := opts.ToRestoreMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} diff --git a/openstack/blockstorage/extensions/backups/results.go b/openstack/blockstorage/extensions/backups/results.go index c58f475334..442b182d52 100644 --- a/openstack/blockstorage/extensions/backups/results.go +++ b/openstack/blockstorage/extensions/backups/results.go @@ -158,3 +158,32 @@ func (r commonResult) ExtractInto(v interface{}) error { func ExtractBackupsInto(r pagination.Page, v interface{}) error { return r.(BackupPage).Result.ExtractIntoSlicePtr(v, "backups") } + +// RestoreResult contains the response body and error from a restore request. +type RestoreResult struct { + commonResult +} + +// Restore contains all the information associated with a Cinder Backup restore +// response. +type Restore struct { + // BackupID is the Unique identifier of the backup. + BackupID string `json:"backup_id"` + + // VolumeID is the Unique identifier of the volume. + VolumeID string `json:"volume_id"` + + // Name is the name of the volume, where the backup was restored to. + VolumeName string `json:"volume_name"` +} + +// Extract will get the Backup restore object out of the RestoreResult object. +func (r RestoreResult) Extract() (*Restore, error) { + var s Restore + err := r.ExtractInto(&s) + return &s, err +} + +func (r RestoreResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "restore") +} diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures.go b/openstack/blockstorage/extensions/backups/testing/fixtures.go index 088a88ae8d..f5cf442915 100644 --- a/openstack/blockstorage/extensions/backups/testing/fixtures.go +++ b/openstack/blockstorage/extensions/backups/testing/fixtures.go @@ -77,6 +77,25 @@ const CreateResponse = ` } ` +const RestoreRequest = ` +{ + "restore": { + "name": "vol-001", + "volume_id": "1234" + } +} +` + +const RestoreResponse = ` +{ + "restore": { + "backup_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "volume_id": "1234", + "volume_name": "vol-001" + } +} +` + func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/backups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -124,6 +143,21 @@ func MockCreateResponse(t *testing.T) { }) } +func MockRestoreResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/restore", 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, RestoreRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, RestoreResponse) + }) +} + func MockDeleteResponse(t *testing.T) { th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") diff --git a/openstack/blockstorage/extensions/backups/testing/requests_test.go b/openstack/blockstorage/extensions/backups/testing/requests_test.go index d9d4953734..a7c4441929 100644 --- a/openstack/blockstorage/extensions/backups/testing/requests_test.go +++ b/openstack/blockstorage/extensions/backups/testing/requests_test.go @@ -85,6 +85,21 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } +func TestRestore(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockRestoreResponse(t) + + options := backups.RestoreOpts{VolumeID: "1234", Name: "vol-001"} + n, err := backups.RestoreFromBackup(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.VolumeName, "vol-001") + th.AssertEquals(t, n.BackupID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/blockstorage/extensions/backups/urls.go b/openstack/blockstorage/extensions/backups/urls.go index ed09773519..0a52688a6a 100644 --- a/openstack/blockstorage/extensions/backups/urls.go +++ b/openstack/blockstorage/extensions/backups/urls.go @@ -21,3 +21,7 @@ func listURL(c *gophercloud.ServiceClient) string { func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("backups", id) } + +func restoreURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "restore") +} diff --git a/openstack/blockstorage/v3/volumes/doc.go b/openstack/blockstorage/v3/volumes/doc.go index 307b8b12d2..0b834852dd 100644 --- a/openstack/blockstorage/v3/volumes/doc.go +++ b/openstack/blockstorage/v3/volumes/doc.go @@ -1,5 +1,23 @@ -// Package volumes provides information and interaction with volumes in the -// OpenStack Block Storage service. A volume is a detachable block storage -// device, akin to a USB hard drive. It can only be attached to one instance at -// a time. +/* +Package volumes provides information and interaction with volumes in the +OpenStack Block Storage service. A volume is a detachable block storage +device, akin to a USB hard drive. It can only be attached to one instance at +a time. + +Example to create a Volume from a Backup + + backupID := "20c792f0-bb03-434f-b653-06ef238e337e" + options := volumes.CreateOpts{ + Name: "vol-001", + BackupID: &backupID, + } + + client.Microversion = "3.47" + volume, err := volumes.Create(client, options).Extract() + if err != nil { + panic(err) + } + + fmt.Println(volume) +*/ package volumes diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 4f5c66ac98..609b545028 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -16,7 +16,7 @@ type CreateOptsBuilder interface { // see the Volume object. type CreateOpts struct { // The size of the volume, in GB - Size int `json:"size" required:"true"` + Size int `json:"size,omitempty"` // The availability zone AvailabilityZone string `json:"availability_zone,omitempty"` // ConsistencyGroupID is the ID of a consistency group @@ -36,6 +36,9 @@ type CreateOpts struct { // The ID of the image from which you want to create the volume. // Required to create a bootable volume. ImageID string `json:"imageRef,omitempty"` + // Specifies the backup ID, from which you want to create the volume. + // Create a volume from a backup is supported since 3.47 microversion + BackupID string `json:"backup_id,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` // Multiattach denotes if the volume is multi-attach capable. diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index 3a33b5864b..6f46685b6e 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -63,6 +63,9 @@ type Volume struct { SnapshotID string `json:"snapshot_id"` // The ID of another block storage volume from which the current volume was created SourceVolID string `json:"source_volid"` + // The backup ID, from which the volume was restored + // This field is supported since 3.47 microversion + BackupID *string `json:"backup_id"` // Arbitrary key-value pairs defined by the user. Metadata map[string]string `json:"metadata"` // UserID is the id of the user who created the volume. diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures.go b/openstack/blockstorage/v3/volumes/testing/fixtures.go index dca8634c03..d67835e5dc 100644 --- a/openstack/blockstorage/v3/volumes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumes/testing/fixtures.go @@ -219,3 +219,48 @@ func MockUpdateResponse(t *testing.T) { `) }) } + +func MockCreateVolumeFromBackupResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes", 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, ` +{ + "volume": { + "name": "vol-001", + "backup_id": "20c792f0-bb03-434f-b653-06ef238e337e" + } +} +`) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, ` +{ + "volume": { + "size": 30, + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "metadata": {}, + "created_at": "2015-09-17T03:32:29.044216", + "encrypted": false, + "bootable": "false", + "availability_zone": "nova", + "attachments": [], + "user_id": "ff1ce52c03ab433aaba9108c2e3ef541", + "status": "creating", + "description": null, + "volume_type": "lvmdriver-1", + "name": "vol-001", + "replication_status": "disabled", + "consistencygroup_id": null, + "source_volid": null, + "snapshot_id": null, + "backup_id": "20c792f0-bb03-434f-b653-06ef238e337e", + "multiattach": false + } +}`) + }) +} diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index 4745198dd8..dcc587f437 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -257,3 +257,22 @@ func TestGetWithExtensions(t *testing.T) { t.Errorf("Expected error when providing non-pointer struct") } } + +func TestCreateFromBackup(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateVolumeFromBackupResponse(t) + + options := volumes.CreateOpts{ + Name: "vol-001", + BackupID: "20c792f0-bb03-434f-b653-06ef238e337e", + } + + v, err := volumes.Create(client.ServiceClient(), options).Extract() + + th.AssertNoErr(t, err) + th.AssertEquals(t, v.Size, 30) + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertEquals(t, *v.BackupID, "20c792f0-bb03-434f-b653-06ef238e337e") +} From 6035fd8f9498cc405fcd6ef5d51752920cad67c2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 4 Mar 2020 08:05:48 -0800 Subject: [PATCH 1009/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dea88de805..3aecc1a7d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,9 @@ IMPROVEMENTS * 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) BUG FIXES From e2b4b95fd580964bc2137de0be6a3d301a3d5979 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 4 Mar 2020 10:56:17 -0700 Subject: [PATCH 1010/2296] Acc Tests: Disable Capsule tests (#1886) --- acceptance/openstack/container/v1/capsules_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/acceptance/openstack/container/v1/capsules_test.go b/acceptance/openstack/container/v1/capsules_test.go index e7180ed069..5ea7f5dbda 100644 --- a/acceptance/openstack/container/v1/capsules_test.go +++ b/acceptance/openstack/container/v1/capsules_test.go @@ -12,6 +12,8 @@ import ( ) func TestCapsuleBase(t *testing.T) { + t.Skip("Currently failing in OpenLab") + clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") @@ -61,6 +63,8 @@ func TestCapsuleBase(t *testing.T) { } func TestCapsuleV132(t *testing.T) { + t.Skip("Currently failing in OpenLab") + clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") From f14c8725b6c0d05e1ea8bf6435124b64bc54333f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 4 Mar 2020 20:24:03 -0800 Subject: [PATCH 1011/2296] openstack/compute/v2/servers/testing: fix dropped error (#1888) --- openstack/compute/v2/servers/testing/results_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/compute/v2/servers/testing/results_test.go b/openstack/compute/v2/servers/testing/results_test.go index d4773dc916..80c2cb2052 100644 --- a/openstack/compute/v2/servers/testing/results_test.go +++ b/openstack/compute/v2/servers/testing/results_test.go @@ -24,6 +24,7 @@ func TestExtractPassword_no_pwd_data(t *testing.T) { resp := servers.GetPasswordResult{Result: gophercloud.Result{Body: dejson}} pwd, err := resp.ExtractPassword(nil) + th.AssertNoErr(t, err) th.AssertEquals(t, pwd, "") } From a043f1f0e19b00abaa6801d36326f29e426686bb Mon Sep 17 00:00:00 2001 From: randomswdev Date: Fri, 6 Mar 2020 18:26:53 +0100 Subject: [PATCH 1012/2296] Add tags to the identity v3 project (#1882) * Add tags to the identity v3 project * Code review changes * Doc Changes * Remove * from ListOpts * Make the acceptance work Co-authored-by: Bernardo Pastorelli --- .../openstack/identity/v3/projects_test.go | 152 ++++++++++++++++++ openstack/identity/v3/projects/doc.go | 12 +- openstack/identity/v3/projects/requests.go | 18 +++ openstack/identity/v3/projects/results.go | 3 + .../identity/v3/projects/testing/fixtures.go | 21 ++- .../v3/projects/testing/requests_test.go | 2 + 6 files changed, 200 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index 38848be1ed..1e589bacba 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -194,3 +194,155 @@ func TestProjectsNested(t *testing.T) { th.AssertEquals(t, project.ParentID, projectMain.ID) } + +func TestProjectsTags(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + createOpts := projects.CreateOpts{ + Tags: []string{"Tag1", "Tag2"}, + } + + projectMain, err := CreateProject(t, client, &createOpts) + th.AssertNoErr(t, err) + defer DeleteProject(t, client, projectMain.ID) + + // Search using all tags + listOpts := projects.ListOpts{ + Tags: "Tag1,Tag2", + } + + allPages, err := projects.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allProjects, err := projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + + found := false + for _, project := range allProjects { + tools.PrintResource(t, project) + + if project.Name == projectMain.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + + // Search using all tags, including a not existing one + listOpts = projects.ListOpts{ + Tags: "Tag1,Tag2,Tag3", + } + + allPages, err = projects.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allProjects, err = projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + + th.AssertEquals(t, len(allProjects), 0) + + // Search matching at least one tag + listOpts = projects.ListOpts{ + TagsAny: "Tag1,Tag2,Tag3", + } + + allPages, err = projects.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allProjects, err = projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + + found = false + for _, project := range allProjects { + tools.PrintResource(t, project) + + if project.Name == projectMain.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + + // Search not matching any single tag + listOpts = projects.ListOpts{ + NotTagsAny: "Tag1", + } + + allPages, err = projects.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allProjects, err = projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + + found = false + for _, project := range allProjects { + tools.PrintResource(t, project) + + if project.Name == projectMain.Name { + found = true + } + } + + th.AssertEquals(t, found, false) + + // Search matching not all tags + listOpts = projects.ListOpts{ + NotTags: "Tag1,Tag2,Tag3", + } + + allPages, err = projects.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allProjects, err = projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + + found = false + for _, project := range allProjects { + tools.PrintResource(t, project) + + if project.Name == "admin" { + found = true + } + } + + th.AssertEquals(t, found, true) + + // Update the tags + updateOpts := projects.UpdateOpts{ + Tags: &[]string{"Tag1"}, + } + + updatedProject, err := projects.Update(client, projectMain.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedProject) + th.AssertEquals(t, len(updatedProject.Tags), 1) + th.AssertEquals(t, updatedProject.Tags[0], "Tag1") + + // Update the project, but not its tags + description := "Test description" + updateOpts = projects.UpdateOpts{ + Description: &description, + } + + updatedProject, err = projects.Update(client, projectMain.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedProject) + th.AssertEquals(t, len(updatedProject.Tags), 1) + th.AssertEquals(t, updatedProject.Tags[0], "Tag1") + + // Remove all Tags + updateOpts = projects.UpdateOpts{ + Tags: &[]string{}, + } + + updatedProject, err = projects.Update(client, projectMain.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedProject) + th.AssertEquals(t, len(updatedProject.Tags), 0) +} diff --git a/openstack/identity/v3/projects/doc.go b/openstack/identity/v3/projects/doc.go index 4f5b45ab65..9c34ee0a6a 100644 --- a/openstack/identity/v3/projects/doc.go +++ b/openstack/identity/v3/projects/doc.go @@ -26,7 +26,8 @@ Example to Create a Project createOpts := projects.CreateOpts{ Name: "project_name", - Description: "Project Description" + Description: "Project Description", + Tags: []string{"FirstTag", "SecondTag"}, } project, err := projects.Create(identityClient, createOpts).Extract() @@ -47,6 +48,15 @@ Example to Update a Project panic(err) } + updateOpts = projects.UpdateOpts{ + Tags: &[]string{"FirstTag"}, + } + + project, err = projects.Update(identityClient, projectID, updateOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a Project projectID := "966b3c7d36a24facaf20b7e458bf2192" diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 0e46169541..e7fde5129c 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -32,6 +32,18 @@ type ListOpts struct { // ParentID filters the response by projects of a given parent project. ParentID string `q:"parent_id"` + // Tags filters on specific project tags. All tags must be present for the project. + Tags string `q:"tags"` + + // TagsAny filters on specific project tags. At least one of the tags must be present for the project. + TagsAny string `q:"tags-any"` + + // NotTags filters on specific project tags. All tags must be absent for the project. + NotTags string `q:"not-tags"` + + // NotTagsAny filters on specific project tags. At least one of the tags must be absent for the project. + NotTagsAny string `q:"not-tags-any"` + // Filters filters the response by custom filters such as // 'name__contains=foo' Filters map[string]string `q:"-"` @@ -104,6 +116,9 @@ type CreateOpts struct { // Description is the description of the project. Description string `json:"description,omitempty"` + + // Tags is a list of tags to associate with the project. + Tags []string `json:"tags,omitempty"` } // ToProjectCreateMap formats a CreateOpts into a create request. @@ -153,6 +168,9 @@ type UpdateOpts struct { // Description is the description of the project. Description *string `json:"description,omitempty"` + + // Tags is a list of tags to associate with the project. + Tags *[]string `json:"tags,omitempty"` } // ToUpdateCreateMap formats a UpdateOpts into an update request. diff --git a/openstack/identity/v3/projects/results.go b/openstack/identity/v3/projects/results.go index a13fa7f2ae..f3557dcbdd 100644 --- a/openstack/identity/v3/projects/results.go +++ b/openstack/identity/v3/projects/results.go @@ -55,6 +55,9 @@ type Project struct { // ParentID is the parent_id of the project. ParentID string `json:"parent_id"` + + // Tags is the list of tags associated with the project. + Tags []string `json:"tags,omitempty"` } // ProjectPage is a single page of Project results. diff --git a/openstack/identity/v3/projects/testing/fixtures.go b/openstack/identity/v3/projects/testing/fixtures.go index caa55679a8..f834c1e87a 100644 --- a/openstack/identity/v3/projects/testing/fixtures.go +++ b/openstack/identity/v3/projects/testing/fixtures.go @@ -21,7 +21,8 @@ const ListOutput = ` "enabled": true, "id": "1234", "name": "Red Team", - "parent_id": null + "parent_id": null, + "tags": ["Red", "Team"] }, { "is_domain": false, @@ -50,8 +51,9 @@ const GetOutput = ` "enabled": true, "id": "1234", "name": "Red Team", - "parent_id": null - } + "parent_id": null, + "tags": ["Red", "Team"] + } } ` @@ -60,7 +62,8 @@ const CreateRequest = ` { "project": { "description": "The team that is red", - "name": "Red Team" + "name": "Red Team", + "tags": ["Red", "Team"] } } ` @@ -70,7 +73,8 @@ const UpdateRequest = ` { "project": { "description": "The team that is bright red", - "name": "Bright Red Team" + "name": "Bright Red Team", + "tags": ["Red"] } } ` @@ -85,8 +89,9 @@ const UpdateOutput = ` "enabled": true, "id": "1234", "name": "Bright Red Team", - "parent_id": null - } + "parent_id": null, + "tags": ["Red"] + } } ` @@ -99,6 +104,7 @@ var RedTeam = projects.Project{ ID: "1234", Name: "Red Team", ParentID: "", + Tags: []string{"Red", "Team"}, } // BlueTeam is a Project fixture. @@ -121,6 +127,7 @@ var UpdatedRedTeam = projects.Project{ ID: "1234", Name: "Bright Red Team", ParentID: "", + Tags: []string{"Red"}, } // ExpectedProjectSlice is the slice of projects expected to be returned from ListOutput. diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 746d654e1c..2a854bcfd5 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -79,6 +79,7 @@ func TestCreateProject(t *testing.T) { createOpts := projects.CreateOpts{ Name: "Red Team", Description: "The team that is red", + Tags: []string{"Red", "Team"}, } actual, err := projects.Create(client.ServiceClient(), createOpts).Extract() @@ -104,6 +105,7 @@ func TestUpdateProject(t *testing.T) { updateOpts := projects.UpdateOpts{ Name: "Bright Red Team", Description: &description, + Tags: &[]string{"Red"}, } actual, err := projects.Update(client.ServiceClient(), "1234", updateOpts).Extract() From d936e6876d28629b889a2849a9d8a23cae0266ab Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 6 Mar 2020 09:28:27 -0800 Subject: [PATCH 1013/2296] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aecc1a7d7..59e907ec07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,13 @@ IMPROVEMENTS * 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) BUG FIXES From c238f02550cec549112523ecd87b2eb55d5673ab Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 11 Mar 2020 03:11:06 +0100 Subject: [PATCH 1014/2296] Compute V2: Support VMs without a network (#1884) * Compute V2: Support VMs without a network * Add "server without a network" acceptance test --- acceptance/openstack/compute/v2/compute.go | 52 +++++++++++++++++ .../openstack/compute/v2/servers_test.go | 58 +++++++++++++++++++ openstack/compute/v2/servers/requests.go | 40 ++++++++----- .../compute/v2/servers/testing/fixtures.go | 21 +++++++ .../v2/servers/testing/requests_test.go | 16 +++++ 5 files changed, 173 insertions(+), 14 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index a105cefd0c..d4a9edf735 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -1198,3 +1198,55 @@ func CreateRemoteConsole(t *testing.T, client *gophercloud.ServiceClient, server t.Logf("Successfully created console: %s", remoteConsole.URL) return remoteConsole, nil } + +// CreateNoNetworkServer creates a basic instance with a randomly generated name. +// The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable. +// The image will be the value of the OS_IMAGE_ID environment variable. +// The instance will be launched without network interfaces attached. +// An error will be returned if the instance was unable to be created. +func CreateServerNoNetwork(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) { + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create server: %s", name) + + pwd := tools.MakeNewPassword("") + + server, err := servers.Create(client, servers.CreateOpts{ + Name: name, + FlavorRef: choices.FlavorID, + ImageRef: choices.ImageID, + AdminPass: pwd, + Networks: "none", + Metadata: map[string]string{ + "abc": "def", + }, + Personality: servers.Personality{ + &servers.File{ + Path: "/etc/test", + Contents: []byte("hello world"), + }, + }, + }).Extract() + if err != nil { + return server, err + } + + if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil { + return nil, err + } + + newServer, err := servers.Get(client, server.ID).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, newServer.Name, name) + th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) + th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + + return newServer, nil +} diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 96283222a2..1517afd337 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -597,3 +597,61 @@ func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { th.AssertEquals(t, *serverWithAttributesExt.RootDeviceName != "", true) th.AssertEquals(t, serverWithAttributesExt.Userdata == nil, true) } + +func TestServerNoNetworkCreateDestroy(t *testing.T) { + clients.RequireLong(t) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + client.Microversion = "2.37" + + server, err := CreateServerNoNetwork(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) + + allPages, err := servers.List(client, servers.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + + allServers, err := servers.ExtractServers(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, s := range allServers { + tools.PrintResource(t, server) + + if s.ID == server.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages() + th.AssertNoErr(t, err) + + allAddresses, err := servers.ExtractAddresses(allAddressPages) + th.AssertNoErr(t, err) + + for network, address := range allAddresses { + t.Logf("Addresses on %s: %+v", network, address) + } + + allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages() + th.AssertNoErr(t, err) + + allInterfaces, err := attachinterfaces.ExtractInterfaces(allInterfacePages) + th.AssertNoErr(t, err) + + for _, iface := range allInterfaces { + t.Logf("Interfaces: %+v", iface) + } + + _, err = servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages() + if err == nil { + t.Fatalf("Instance must not be a member of specified network") + } +} diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 75103b2eb5..d60018fdd9 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -3,6 +3,7 @@ package servers import ( "encoding/base64" "encoding/json" + "fmt" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" @@ -179,7 +180,9 @@ type CreateOpts struct { // Networks dictates how this server will be attached to available networks. // By default, the server will be attached to all isolated networks for the // tenant. - Networks []Network `json:"-"` + // Starting with microversion 2.37 networks can also be an "auto" or "none" + // string. + Networks interface{} `json:"-"` // Metadata contains key-value pairs (up to 255 bytes each) to attach to the // server. @@ -245,21 +248,30 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { b["security_groups"] = securityGroups } - if len(opts.Networks) > 0 { - networks := make([]map[string]interface{}, len(opts.Networks)) - for i, net := range opts.Networks { - networks[i] = make(map[string]interface{}) - if net.UUID != "" { - networks[i]["uuid"] = net.UUID - } - if net.Port != "" { - networks[i]["port"] = net.Port - } - if net.FixedIP != "" { - networks[i]["fixed_ip"] = net.FixedIP + switch v := opts.Networks.(type) { + case []Network: + if len(v) > 0 { + networks := make([]map[string]interface{}, len(v)) + for i, net := range v { + networks[i] = make(map[string]interface{}) + if net.UUID != "" { + networks[i]["uuid"] = net.UUID + } + if net.Port != "" { + networks[i]["port"] = net.Port + } + if net.FixedIP != "" { + networks[i]["fixed_ip"] = net.FixedIP + } } + b["networks"] = networks + } + case string: + if v == "auto" || v == "none" { + b["networks"] = v + } else { + return nil, fmt.Errorf(`networks must be a slice of Network struct or a string with "auto" or "none" values, current value is %q`, v) } - b["networks"] = networks } // If ImageRef isn't provided, check if ImageName was provided to ascertain diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index 029a224bf9..e2159761a9 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -677,6 +677,27 @@ func (opts CreateOptsWithCustomField) ToServerCreateMap() (map[string]interface{ return gophercloud.BuildRequestBody(opts, "server") } +// HandleServerNoNetworkCreationSuccessfully sets up the test server with no +// network to respond to a server creation request with a given response. +func HandleServerNoNetworkCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/servers", 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, `{ + "server": { + "name": "derp", + "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", + "flavorRef": "1", + "networks": "none" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + // HandleServerCreationSuccessfully sets up the test server to respond to a server creation request // with a given response. func HandleServerCreationSuccessfully(t *testing.T, response string) { diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index af9a368548..187bab7a12 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -100,6 +100,22 @@ func TestCreateServer(t *testing.T) { th.CheckDeepEquals(t, ServerDerp, *actual) } +func TestCreateServerNoNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleServerNoNetworkCreationSuccessfully(t, SingleServerBody) + + actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ + Name: "derp", + ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", + FlavorRef: "1", + Networks: "none", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ServerDerp, *actual) +} + func TestCreateServers(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From f2dd9fc2bab839b5ed73fe7446fb47d5dd837aa2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Mar 2020 20:13:29 -0600 Subject: [PATCH 1015/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e907ec07..dc4aedf2f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ 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) @@ -38,6 +40,7 @@ IMPROVEMENTS * 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 From 965816e95e604e19c77b1a086c9737164dc3a4c5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Mar 2020 20:14:02 -0600 Subject: [PATCH 1016/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4aedf2f4..d8bbd1d42c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.9.0 (Unreleased) +## 0.10.0 (Unreleased) + +## 0.9.0 (March 10, 2020) UPGRADE NOTES From 330a03d702f406786a835bafbe30494b823eb41f Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 13 Mar 2020 03:33:56 +0100 Subject: [PATCH 1017/2296] =?UTF-8?q?Cinder=20V3:=20Update=20a=20volume?= =?UTF-8?q?=E2=80=99s=20bootable=20status=20(#1891)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Cinder V3: Update a volume’s bootable status * Add acceptance tests for bootable action --- .../blockstorage/extensions/extensions.go | 46 +++++++++++++++++++ .../extensions/volumeactions_test.go | 12 +++++ .../extensions/volumeactions/doc.go | 11 +++++ .../extensions/volumeactions/requests.go | 25 ++++++++++ .../extensions/volumeactions/results.go | 6 +++ .../volumeactions/testing/fixtures.go | 19 ++++++++ .../volumeactions/testing/requests_test.go | 14 ++++++ 7 files changed, 133 insertions(+) diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index 797a127cc0..220da9fcd5 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -4,6 +4,8 @@ package extensions import ( + "fmt" + "strings" "testing" "github.com/gophercloud/gophercloud" @@ -11,6 +13,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" + v3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" @@ -252,3 +255,46 @@ func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string, s return false, nil }) } + +// SetBootable will set a bootable status to a volume. +func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to apply bootable status to volume %s", volume.ID) + + bootableOpts := volumeactions.BootableOpts{ + Bootable: true, + } + + err := volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() + if err != nil { + return err + } + + vol, err := v3.Get(client, volume.ID).Extract() + if err != nil { + return err + } + + if strings.ToLower(vol.Bootable) != "true" { + return fmt.Errorf("Volume bootable status is %q, expected 'true'", vol.Bootable) + } + + bootableOpts = volumeactions.BootableOpts{ + Bootable: false, + } + + err = volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() + if err != nil { + return err + } + + vol, err = v3.Get(client, volume.ID).Extract() + if err != nil { + return err + } + + if strings.ToLower(vol.Bootable) == "true" { + return fmt.Errorf("Volume bootable status is %q, expected 'false'", vol.Bootable) + } + + return nil +} diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 607a70c02f..fe763c942c 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -101,6 +101,18 @@ func TestVolumeActionsImageMetadata(t *testing.T) { th.AssertNoErr(t, err) } +func TestVolumeActionsSetBootable(t *testing.T) { + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + err = SetBootable(t, blockClient, volume) + 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/doc.go b/openstack/blockstorage/extensions/volumeactions/doc.go index a78d3d0482..0f2dd2cb26 100644 --- a/openstack/blockstorage/extensions/volumeactions/doc.go +++ b/openstack/blockstorage/extensions/volumeactions/doc.go @@ -82,5 +82,16 @@ Example of Initializing a Volume Connection if err != nil { panic(err) } + +Example of Setting a Volume's Bootable status + + options := volumeactions.BootableOpts{ + Bootable: true, + } + + err := volumeactions.SetBootable(client, volume.ID, options).ExtractErr() + if err != nil { + panic(err) + } */ package volumeactions diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index c2d86498a8..5648b126c7 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -306,3 +306,28 @@ func SetImageMetadata(client *gophercloud.ServiceClient, id string, opts ImageMe }) return } + +// BootableOpts contains options for setting bootable status to a volume. +type BootableOpts struct { + // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. + Bootable bool `json:"bootable"` +} + +// ToBootableMap assembles a request body based on the contents of a +// BootableOpts. +func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-set_bootable") +} + +// SetBootable will set bootable status on a volume based on the values in BootableOpts +func SetBootable(client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { + b, err := opts.ToBootableMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index 8587327cf6..691ecc8950 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -35,6 +35,12 @@ type SetImageMetadataResult struct { gophercloud.ErrResult } +// SetBootableResult contains the response body and error from a SetBootable +// request. +type SetBootableResult struct { + gophercloud.ErrResult +} + // ReserveResult contains the response body and error from a Reserve request. type ReserveResult struct { gophercloud.ErrResult diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go index 032b5809f8..4564e71812 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go @@ -307,3 +307,22 @@ func MockSetImageMetadataResponse(t *testing.T) { fmt.Fprintf(w, `{}`) }) } + +func MockSetBootableResponse(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-set_bootable": { + "bootable": true + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", "0") + w.WriteHeader(http.StatusOK) + }) +} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go index c0919bb5ea..f792b8c2d8 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go @@ -180,3 +180,17 @@ func TestSetImageMetadata(t *testing.T) { err := volumeactions.SetImageMetadata(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } + +func TestSetBootable(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetBootableResponse(t) + + options := volumeactions.BootableOpts{ + Bootable: true, + } + + err := volumeactions.SetBootable(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} From 98fb4f433ef5243f0b4396727348c07ecea69a4b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 12 Mar 2020 20:35:10 -0600 Subject: [PATCH 1018/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8bbd1d42c..deeefef39b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.10.0 (Unreleased) +IMPROVEMENTS + +* Added `blockstorage/extensions/volumeactions.SetBootable` [GH-1891](https://github.com/gophercloud/gophercloud/pull/1891) + ## 0.9.0 (March 10, 2020) UPGRADE NOTES From ac724329c1d5a0cb439cd82106c41d1878373d2f Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 15 Mar 2020 03:10:04 +0100 Subject: [PATCH 1019/2296] Cinder V3: Support a backup record export and import (#1894) * Cinder V3: Support a backup record export and import * Add ImportBackup struct * Rename backup import method name * Use ImportResponse struct to store import response * Allow to use nullable fields, where needed * Update docs correspondingly --- .../blockstorage/extensions/backups/doc.go | 49 ++++++ .../extensions/backups/requests.go | 32 ++++ .../extensions/backups/results.go | 144 ++++++++++++++++++ .../extensions/backups/testing/fixtures.go | 90 +++++++++++ .../backups/testing/requests_test.go | 35 +++++ .../blockstorage/extensions/backups/urls.go | 8 + 6 files changed, 358 insertions(+) diff --git a/openstack/blockstorage/extensions/backups/doc.go b/openstack/blockstorage/extensions/backups/doc.go index c43e4e2808..f96e6d3de4 100644 --- a/openstack/blockstorage/extensions/backups/doc.go +++ b/openstack/blockstorage/extensions/backups/doc.go @@ -71,5 +71,54 @@ Example to Delete a Backup if err != nil { panic(err) } + +Example to Export a Backup + + export, err := backups.Export(client, "uuid").Extract() + if err != nil { + panic(err) + } + + fmt.Println(export) + +Example to Import a Backup + + status := "available" + availabilityZone := "region1b" + host := "cinder-backup-host1" + serviceMetadata := "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" + size := 1 + objectCount := 2 + container := "my-test-backup" + service := "cinder.backup.drivers.swift.SwiftBackupDriver" + backupURL, _ := json.Marshal(backups.ImportBackup{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Status: &status, + AvailabilityZone: &availabilityZone, + VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", + UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), + Host: &host, + UserID: "93514e04-a026-4f60-8176-395c859501dd", + ServiceMetadata: &serviceMetadata, + Size: &size, + ObjectCount: &objectCount, + Container: &container, + Service: &service, + CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", + }) + + options := backups.ImportOpts{ + BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", + BackupURL: backupURL, + } + + backup, err := backups.Import(client, options).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) */ package backups diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/extensions/backups/requests.go index c5613e22b9..6829a8c603 100644 --- a/openstack/blockstorage/extensions/backups/requests.go +++ b/openstack/blockstorage/extensions/backups/requests.go @@ -213,3 +213,35 @@ func RestoreFromBackup(client *gophercloud.ServiceClient, id string, opts Restor }) return } + +// Export will export a Backup information. To extract the Backup export record +// object from the response, call the Extract method on the ExportResult. +func Export(client *gophercloud.ServiceClient, id string) (r ExportResult) { + _, r.Err = client.Get(exportURL(client, id), &r.Body, nil) + return +} + +// ImportOpts contains options for importing a Backup. This object is passed to +// the backups.ImportBackup function. +type ImportOpts BackupRecord + +// ToBackupImportMap assembles a request body based on the contents of a +// ImportOpts. +func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "backup-record") +} + +// Import will import a Backup data to a backup based on the values in +// ImportOpts. To extract the Backup object from the response, call the +// Extract method on the ImportResult. +func Import(client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) { + b, err := opts.ToBackupImportMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(importURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} diff --git a/openstack/blockstorage/extensions/backups/results.go b/openstack/blockstorage/extensions/backups/results.go index 442b182d52..bfba515d4a 100644 --- a/openstack/blockstorage/extensions/backups/results.go +++ b/openstack/blockstorage/extensions/backups/results.go @@ -187,3 +187,147 @@ func (r RestoreResult) Extract() (*Restore, error) { func (r RestoreResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "restore") } + +// ExportResult contains the response body and error from an export request. +type ExportResult struct { + commonResult +} + +// BackupRecord contains an information about a backup backend storage. +type BackupRecord struct { + // The service used to perform the backup. + BackupService string `json:"backup_service"` + + // An identifier string to locate the backup. + BackupURL []byte `json:"backup_url"` +} + +// Extract will get the Backup record object out of the ExportResult object. +func (r ExportResult) Extract() (*BackupRecord, error) { + var s BackupRecord + err := r.ExtractInto(&s) + return &s, err +} + +func (r ExportResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup-record") +} + +// ImportResponse struct contains the response of the Backup Import action. +type ImportResponse struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// ImportResult contains the response body and error from an import request. +type ImportResult struct { + gophercloud.Result +} + +// Extract will get the Backup object out of the commonResult object. +func (r ImportResult) Extract() (*ImportResponse, error) { + var s ImportResponse + err := r.ExtractInto(&s) + return &s, err +} + +func (r ImportResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup") +} + +// ImportBackup contains all the information to import a Cinder Backup. +type ImportBackup struct { + ID string `json:"id"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + VolumeID string `json:"volume_id"` + SnapshotID *string `json:"snapshot_id"` + Status *string `json:"status"` + Size *int `json:"size"` + ObjectCount *int `json:"object_count"` + Container *string `json:"container"` + ServiceMetadata *string `json:"service_metadata"` + Service *string `json:"service"` + Host *string `json:"host"` + UserID string `json:"user_id"` + DeletedAt time.Time `json:"-"` + DataTimestamp time.Time `json:"-"` + TempSnapshotID *string `json:"temp_snapshot_id"` + TempVolumeID *string `json:"temp_volume_id"` + RestoreVolumeID *string `json:"restore_volume_id"` + NumDependentBackups *int `json:"num_dependent_backups"` + EncryptionKeyID *string `json:"encryption_key_id"` + ParentID *string `json:"parent_id"` + Deleted bool `json:"deleted"` + DisplayName *string `json:"display_name"` + DisplayDescription *string `json:"display_description"` + DriverInfo interface{} `json:"driver_info"` + FailReason *string `json:"fail_reason"` + ProjectID string `json:"project_id"` + Metadata *map[string]string `json:"metadata"` + AvailabilityZone *string `json:"availability_zone"` +} + +// UnmarshalJSON converts our JSON API response into our backup struct +func (r *ImportBackup) UnmarshalJSON(b []byte) error { + type tmp ImportBackup + var s struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt time.Time `json:"deleted_at"` + DataTimestamp time.Time `json:"data_timestamp"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ImportBackup(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) + r.DataTimestamp = time.Time(s.DataTimestamp) + + return err +} + +// MarshalJSON converts our struct request into JSON backup import request +func (r ImportBackup) MarshalJSON() ([]byte, error) { + type b ImportBackup + type ext struct { + CreatedAt *string `json:"created_at"` + UpdatedAt *string `json:"updated_at"` + DeletedAt *string `json:"deleted_at"` + DataTimestamp *string `json:"data_timestamp"` + } + type tmp struct { + b + ext + } + + var t ext + if r.CreatedAt != (time.Time{}) { + v := r.CreatedAt.Format(time.RFC3339) + t.CreatedAt = &v + } + if r.UpdatedAt != (time.Time{}) { + v := r.UpdatedAt.Format(time.RFC3339) + t.UpdatedAt = &v + } + if r.DeletedAt != (time.Time{}) { + v := r.DeletedAt.Format(time.RFC3339) + t.DeletedAt = &v + } + if r.DataTimestamp != (time.Time{}) { + v := r.DataTimestamp.Format(time.RFC3339) + t.DataTimestamp = &v + } + + s := tmp{ + b(r), + t, + } + + return json.Marshal(s) +} diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures.go b/openstack/blockstorage/extensions/backups/testing/fixtures.go index f5cf442915..78567e337c 100644 --- a/openstack/blockstorage/extensions/backups/testing/fixtures.go +++ b/openstack/blockstorage/extensions/backups/testing/fixtures.go @@ -1,10 +1,13 @@ package testing import ( + "encoding/json" "fmt" "net/http" "testing" + "time" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" th "github.com/gophercloud/gophercloud/testhelper" fake "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -96,6 +99,65 @@ const RestoreResponse = ` } ` +const ExportResponse = ` +{ + "backup-record": { + "backup_service": "cinder.backup.drivers.swift.SwiftBackupDriver", + "backup_url": "eyJpZCI6ImQzMjAxOWQzLWJjNmUtNDMxOS05YzFkLTY3MjJmYzEzNmEyMiIsInZvbHVtZV9pZCI6ImNmOWJjNmZhLWM1YmMtNDFmNi1iYzRlLTZlNzZjMGJlYTk1OSIsInNuYXBzaG90X2lkIjpudWxsLCJzdGF0dXMiOiJhdmFpbGFibGUiLCJzaXplIjoxLCJvYmplY3RfY291bnQiOjIsImNvbnRhaW5lciI6Im15LXRlc3QtYmFja3VwIiwic2VydmljZV9tZXRhZGF0YSI6InZvbHVtZV9jZjliYzZmYS1jNWJjLTQxZjYtYmM0ZS02ZTc2YzBiZWE5NTkvMjAyMDAzMTExOTI4NTUvYXpfcmVnaW9uYl9iYWNrdXBfYjg3YmIxZTUtMGQ0ZS00NDVlLWE1NDgtNWFlNzQyNTYyYmFjIiwic2VydmljZSI6ImNpbmRlci5iYWNrdXAuZHJpdmVycy5zd2lmdC5Td2lmdEJhY2t1cERyaXZlciIsImhvc3QiOiJjaW5kZXItYmFja3VwLWhvc3QxIiwidXNlcl9pZCI6IjkzNTE0ZTA0LWEwMjYtNGY2MC04MTc2LTM5NWM4NTk1MDFkZCIsInRlbXBfc25hcHNob3RfaWQiOm51bGwsInRlbXBfdm9sdW1lX2lkIjpudWxsLCJyZXN0b3JlX3ZvbHVtZV9pZCI6bnVsbCwibnVtX2RlcGVuZGVudF9iYWNrdXBzIjpudWxsLCJlbmNyeXB0aW9uX2tleV9pZCI6bnVsbCwicGFyZW50X2lkIjpudWxsLCJkZWxldGVkIjpmYWxzZSwiZGlzcGxheV9uYW1lIjpudWxsLCJkaXNwbGF5X2Rlc2NyaXB0aW9uIjpudWxsLCJkcml2ZXJfaW5mbyI6bnVsbCwiZmFpbF9yZWFzb24iOm51bGwsInByb2plY3RfaWQiOiIxNGYxYzFmNWQxMmI0NzU1Yjk0ZWRlZjc4ZmY4YjMyNSIsIm1ldGFkYXRhIjpudWxsLCJhdmFpbGFiaWxpdHlfem9uZSI6InJlZ2lvbjFiIiwiY3JlYXRlZF9hdCI6IjIwMjAtMDMtMTFUMTk6MjU6MjRaIiwidXBkYXRlZF9hdCI6IjIwMjAtMDMtMTFUMTk6Mjk6MDhaIiwiZGVsZXRlZF9hdCI6bnVsbCwiZGF0YV90aW1lc3RhbXAiOiIyMDIwLTAzLTExVDE5OjI1OjI0WiJ9" + } +} +` + +const ImportRequest = ExportResponse + +const ImportResponse = ` +{ + "backup": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "links": [ + { + "href": "https://volume/v2/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", + "rel": "self" + }, + { + "href": "https://volume/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", + "rel": "bookmark" + } + ], + "name": null + } +} +` + +var ( + status = "available" + availabilityZone = "region1b" + host = "cinder-backup-host1" + serviceMetadata = "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" + size = 1 + objectCount = 2 + container = "my-test-backup" + service = "cinder.backup.drivers.swift.SwiftBackupDriver" + backupImport = backups.ImportBackup{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Status: &status, + AvailabilityZone: &availabilityZone, + VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", + UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), + Host: &host, + UserID: "93514e04-a026-4f60-8176-395c859501dd", + ServiceMetadata: &serviceMetadata, + Size: &size, + ObjectCount: &objectCount, + Container: &container, + Service: &service, + CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", + } + backupURL, _ = json.Marshal(backupImport) +) + func MockListResponse(t *testing.T) { th.Mux.HandleFunc("/backups", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -165,3 +227,31 @@ func MockDeleteResponse(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func MockExportResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/export_record", 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, ExportResponse) + }) +} + +func MockImportResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/import_record", 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, ImportRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ImportResponse) + }) +} diff --git a/openstack/blockstorage/extensions/backups/testing/requests_test.go b/openstack/blockstorage/extensions/backups/testing/requests_test.go index a7c4441929..cd96af74f1 100644 --- a/openstack/blockstorage/extensions/backups/testing/requests_test.go +++ b/openstack/blockstorage/extensions/backups/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "encoding/json" "testing" "time" @@ -109,3 +110,37 @@ func TestDelete(t *testing.T) { res := backups.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } + +func TestExport(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockExportResponse(t) + + n, err := backups.Export(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.BackupService, "cinder.backup.drivers.swift.SwiftBackupDriver") + th.AssertDeepEquals(t, n.BackupURL, backupURL) + + tmp := backups.ImportBackup{} + err = json.Unmarshal(backupURL, &tmp) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, tmp, backupImport) +} + +func TestImport(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockImportResponse(t) + + options := backups.ImportOpts{ + BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", + BackupURL: backupURL, + } + n, err := backups.Import(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} diff --git a/openstack/blockstorage/extensions/backups/urls.go b/openstack/blockstorage/extensions/backups/urls.go index 0a52688a6a..914f98bdad 100644 --- a/openstack/blockstorage/extensions/backups/urls.go +++ b/openstack/blockstorage/extensions/backups/urls.go @@ -25,3 +25,11 @@ func updateURL(c *gophercloud.ServiceClient, id string) string { func restoreURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("backups", id, "restore") } + +func exportURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "export_record") +} + +func importURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups", "import_record") +} From 405a05ee28691d1dfdb6a198ef3a97ea31de6a8e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 14 Mar 2020 20:12:25 -0600 Subject: [PATCH 1020/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index deeefef39b..d028454abd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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) ## 0.9.0 (March 10, 2020) From b49be8e1fa85ae48559fea7507556d6ec2b41bf1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 15 Mar 2020 20:05:10 -0600 Subject: [PATCH 1021/2296] Removing IDFromName Functions (#1897) These functions will be moved to https://github.com/gophercloud/utils. --- .../blockstorage/extensions/extensions.go | 11 +- .../extensions/volumeactions_test.go | 2 +- .../openstack/compute/v2/servers_test.go | 4 +- .../openstack/networking/v2/networking.go | 38 ++++++ .../blockstorage/v1/snapshots/requests.go | 36 ------ openstack/blockstorage/v1/volumes/requests.go | 36 ------ .../blockstorage/v2/snapshots/requests.go | 36 ------ openstack/blockstorage/v2/volumes/requests.go | 36 ------ .../blockstorage/v3/snapshots/requests.go | 36 ------ openstack/blockstorage/v3/volumes/requests.go | 36 ------ .../diskconfig/testing/requests_test.go | 2 +- openstack/compute/v2/flavors/requests.go | 39 ------ openstack/compute/v2/images/requests.go | 39 ------ openstack/compute/v2/servers/requests.go | 116 +----------------- .../compute/v2/servers/testing/fixtures.go | 2 +- .../v2/servers/testing/requests_test.go | 18 +-- openstack/imageservice/v2/images/requests.go | 36 ------ .../v2/extensions/security/groups/requests.go | 37 ------ openstack/networking/v2/networks/requests.go | 37 ------ openstack/networking/v2/ports/requests.go | 37 ------ openstack/networking/v2/subnets/requests.go | 37 ------ .../sharedfilesystems/v2/shares/results.go | 22 ---- .../v2/sharetypes/results.go | 34 ----- .../sharedfilesystems/v2/snapshots/results.go | 22 ---- 24 files changed, 51 insertions(+), 698 deletions(-) diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index 220da9fcd5..2c66b3679c 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -51,21 +51,14 @@ func CreateUploadImage(t *testing.T, client *gophercloud.ServiceClient, volume * // DeleteUploadedImage deletes uploaded image. An error will be returned // if the deletion request failed. -func DeleteUploadedImage(t *testing.T, client *gophercloud.ServiceClient, imageName string) error { +func DeleteUploadedImage(t *testing.T, client *gophercloud.ServiceClient, imageID string) error { if testing.Short() { t.Skip("Skipping test that requires volume-backed image removing in short mode.") } - t.Logf("Getting image id for image name %s", imageName) - - imageID, err := images.IDFromName(client, imageName) - if err != nil { - return err - } - t.Logf("Removing image %s", imageID) - err = images.Delete(client, imageID).ExtractErr() + err := images.Delete(client, imageID).ExtractErr() if err != nil { return err } diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index fe763c942c..adba0748c8 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -29,7 +29,7 @@ func TestVolumeActionsUploadImageDestroy(t *testing.T) { tools.PrintResource(t, volumeImage) - err = DeleteUploadedImage(t, computeClient, volumeImage.ImageName) + err = DeleteUploadedImage(t, computeClient, volumeImage.ImageID) th.AssertNoErr(t, err) } diff --git a/acceptance/openstack/compute/v2/servers_test.go b/acceptance/openstack/compute/v2/servers_test.go index 1517afd337..dd36253d3a 100644 --- a/acceptance/openstack/compute/v2/servers_test.go +++ b/acceptance/openstack/compute/v2/servers_test.go @@ -8,6 +8,7 @@ import ( "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/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" @@ -19,7 +20,6 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tags" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -315,7 +315,7 @@ func TestServersActionRebuild(t *testing.T) { rebuildOpts := servers.RebuildOpts{ Name: tools.RandomString("ACPTTEST", 16), AdminPass: tools.MakeNewPassword(server.AdminPass), - ImageID: choices.ImageID, + ImageRef: choices.ImageID, } rebuilt, err := servers.Rebuild(client, server.ID, rebuildOpts).Extract() diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index 6fc87f28d8..2540684c68 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -528,3 +528,41 @@ func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string, secs return false, nil }) } + +// This is duplicated from https://github.com/gophercloud/utils +// so that Gophercloud "core" doesn't have a dependency on the +// complementary utils repository. +func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { + count := 0 + id := "" + + listOpts := networks.ListOpts{ + Name: name, + } + + pages, err := networks.List(client, listOpts).AllPages() + if err != nil { + return "", err + } + + all, err := networks.ExtractNetworks(pages) + if err != nil { + return "", err + } + + for _, s := range all { + if s.Name == name { + count++ + id = s.ID + } + } + + switch count { + case 0: + return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "network"} + case 1: + return id, nil + default: + return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "network"} + } +} diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go index fed1252ac9..bf15231383 100644 --- a/openstack/blockstorage/v1/snapshots/requests.go +++ b/openstack/blockstorage/v1/snapshots/requests.go @@ -125,39 +125,3 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet }) return } - -// IDFromName is a convienience function that returns a snapshot's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractSnapshots(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} - } -} diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go index 511e2c2218..40f369a9a2 100644 --- a/openstack/blockstorage/v1/volumes/requests.go +++ b/openstack/blockstorage/v1/volumes/requests.go @@ -134,39 +134,3 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder }) return } - -// IDFromName is a convienience function that returns a volume's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractVolumes(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} - } -} diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go index 939e502048..9df2a961d3 100644 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ b/openstack/blockstorage/v2/snapshots/requests.go @@ -137,39 +137,3 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet }) return } - -// IDFromName is a convienience function that returns a snapshot's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractSnapshots(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} - } -} diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index ed6d2aa9bc..e16c451e3a 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -197,39 +197,3 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder }) return } - -// IDFromName is a convienience function that returns a volume's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractVolumes(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} - } -} diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 22531c9fdf..05257b79c3 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -148,39 +148,3 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet }) return } - -// IDFromName is a convienience function that returns a snapshot's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractSnapshots(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "snapshot"} - } -} diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 609b545028..e54f8cdebc 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -202,39 +202,3 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder }) return } - -// IDFromName is a convienience function that returns a volume's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractVolumes(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} - } -} diff --git a/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go b/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go index 6ce560aa61..5b64931824 100644 --- a/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go +++ b/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go @@ -39,7 +39,7 @@ func TestRebuildOpts(t *testing.T) { base := servers.RebuildOpts{ Name: "rebuiltserver", AdminPass: "swordfish", - ImageID: "asdfasdfasdf", + ImageRef: "asdfasdfasdf", } ext := diskconfig.RebuildOptsExt{ diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 753024a18b..8156c13e94 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -316,42 +316,3 @@ func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r }) return } - -// IDFromName is a convienience function that returns a flavor's ID given its -// name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - allPages, err := ListDetail(client, nil).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractFlavors(allPages) - if err != nil { - return "", err - } - - for _, f := range all { - if f.Name == name { - count++ - id = f.ID - } - } - - switch count { - case 0: - err := &gophercloud.ErrResourceNotFound{} - err.ResourceType = "flavor" - err.Name = name - return "", err - case 1: - return id, nil - default: - err := &gophercloud.ErrMultipleResourcesFound{} - err.ResourceType = "flavor" - err.Name = name - err.Count = count - return "", err - } -} diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go index 558b481b9e..8f1ef36b34 100644 --- a/openstack/compute/v2/images/requests.go +++ b/openstack/compute/v2/images/requests.go @@ -68,42 +68,3 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } - -// IDFromName is a convienience function that returns an image's ID given its -// name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - allPages, err := ListDetail(client, nil).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractImages(allPages) - if err != nil { - return "", err - } - - for _, f := range all { - if f.Name == name { - count++ - id = f.ID - } - } - - switch count { - case 0: - err := &gophercloud.ErrResourceNotFound{} - err.ResourceType = "image" - err.Name = name - return "", err - case 1: - return id, nil - default: - err := &gophercloud.ErrMultipleResourcesFound{} - err.ResourceType = "image" - err.Name = name - err.Count = count - return "", err - } -} diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index d60018fdd9..3f11e440be 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -6,8 +6,6 @@ import ( "fmt" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" - "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/pagination" ) @@ -148,24 +146,14 @@ type CreateOpts struct { // Name is the name to assign to the newly launched server. Name string `json:"name" required:"true"` - // ImageRef [optional; required if ImageName is not provided] is the ID or - // full URL to the image that contains the server's OS and initial state. + // ImageRef is the ID or full URL to the image that contains the + // server's OS and initial state. // Also optional if using the boot-from-volume extension. ImageRef string `json:"imageRef"` - // ImageName [optional; required if ImageRef is not provided] is the name of - // the image that contains the server's OS and initial state. - // Also optional if using the boot-from-volume extension. - ImageName string `json:"-"` - - // FlavorRef [optional; required if FlavorName is not provided] is the ID or - // full URL to the flavor that describes the server's specs. + // FlavorRef is the ID or full URL to the flavor that describes the server's specs. FlavorRef string `json:"flavorRef"` - // FlavorName [optional; required if FlavorRef is not provided] is the name of - // the flavor that describes the server's specs. - FlavorName string `json:"-"` - // SecurityGroups lists the names of the security groups to which this server // should belong. SecurityGroups []string `json:"-"` @@ -223,7 +211,6 @@ type CreateOpts struct { // ToServerCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { - sc := opts.ServiceClient opts.ServiceClient = nil b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { @@ -274,42 +261,6 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { } } - // If ImageRef isn't provided, check if ImageName was provided to ascertain - // the image ID. - if opts.ImageRef == "" { - if opts.ImageName != "" { - if sc == nil { - err := ErrNoClientProvidedForIDByName{} - err.Argument = "ServiceClient" - return nil, err - } - imageID, err := images.IDFromName(sc, opts.ImageName) - if err != nil { - return nil, err - } - b["imageRef"] = imageID - } - } - - // If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID. - if opts.FlavorRef == "" { - if opts.FlavorName == "" { - err := ErrNeitherFlavorIDNorFlavorNameProvided{} - err.Argument = "FlavorRef/FlavorName" - return nil, err - } - if sc == nil { - err := ErrNoClientProvidedForIDByName{} - err.Argument = "ServiceClient" - return nil, err - } - flavorID, err := flavors.IDFromName(sc, opts.FlavorName) - if err != nil { - return nil, err - } - b["flavorRef"] = flavorID - } - if opts.Min != 0 { b["min_count"] = opts.Min } @@ -470,11 +421,8 @@ type RebuildOpts struct { // AdminPass is the server's admin password AdminPass string `json:"adminPass,omitempty"` - // ImageID is the ID of the image you want your server to be provisioned on. - ImageID string `json:"imageRef"` - - // ImageName is readable name of an image. - ImageName string `json:"-"` + // ImageRef is the ID of the image you want your server to be provisioned on. + ImageRef string `json:"imageRef"` // Name to set the server to Name string `json:"name,omitempty"` @@ -505,23 +453,6 @@ func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { return nil, err } - // If ImageRef isn't provided, check if ImageName was provided to ascertain - // the image ID. - if opts.ImageID == "" { - if opts.ImageName != "" { - if opts.ServiceClient == nil { - err := ErrNoClientProvidedForIDByName{} - err.Argument = "ServiceClient" - return nil, err - } - imageID, err := images.IDFromName(opts.ServiceClient, opts.ImageName) - if err != nil { - return nil, err - } - b["imageRef"] = imageID - } - } - return map[string]interface{}{"rebuild": b}, nil } @@ -764,43 +695,6 @@ func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageO return } -// IDFromName is a convienience function that returns a server's ID given its -// name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - allPages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractServers(allPages) - if err != nil { - return "", err - } - - for _, f := range all { - if f.Name == name { - count++ - id = f.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "server"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "server"} - } -} - // GetPassword makes a request against the nova API to get the encrypted // administrative password. func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index e2159761a9..d81047bc1e 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -1017,7 +1017,7 @@ func HandleRebuildSuccessfully(t *testing.T, response string) { "rebuild": { "name": "new-name", "adminPass": "swordfish", - "imageRef": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", + "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", "accessIPv4": "1.2.3.4" } } diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 187bab7a12..2e8e58068d 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -203,22 +203,6 @@ func TestCreateServerWithUserdataEncoded(t *testing.T) { th.CheckDeepEquals(t, ServerDerp, *actual) } -func TestCreateServerWithImageNameAndFlavorName(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleServerCreationSuccessfully(t, SingleServerBody) - - actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ - Name: "derp", - ImageName: "cirros-0.3.2-x86_64-disk", - FlavorName: "m1.tiny", - ServiceClient: client.ServiceClient(), - }).Extract() - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, ServerDerp, *actual) -} - func TestDeleteServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -358,7 +342,7 @@ func TestRebuildServer(t *testing.T) { opts := servers.RebuildOpts{ Name: "new-name", AdminPass: "swordfish", - ImageID: "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", + ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", AccessIPv4: "1.2.3.4", } diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index c67a6a9d47..d0a9eb572a 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -378,39 +378,3 @@ func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { return updateMap } - -// IDFromName is a convienience function that returns an image's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractImages(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "image"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "image"} - } -} diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index a22cd306e8..b45f8c581c 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -127,40 +127,3 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(resourceURL(c, id), nil) return } - -// IDFromName is a convenience function that returns a security group's ID, -// given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractGroups(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "security group"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "security group"} - } -} diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 8006c48167..168a284584 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -141,40 +141,3 @@ func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, networkID), nil) return } - -// IDFromName is a convenience function that returns a network's ID, given -// its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractNetworks(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "network"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "network"} - } -} diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 18d90a255e..5b45c2f10a 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -182,40 +182,3 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, id), nil) return } - -// IDFromName is a convenience function that returns a port's ID, -// given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractPorts(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "port"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "port"} - } -} diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 3e56bf389a..c92ab9bb63 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -230,40 +230,3 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = c.Delete(deleteURL(c, id), nil) return } - -// IDFromName is a convenience function that returns a subnet's ID, -// given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractSubnets(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "subnet"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "subnet"} - } -} diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 05fe327fba..4a7d0e11a5 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -207,28 +207,6 @@ type UpdateResult struct { commonResult } -// IDFromName is a convenience function that returns a share's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - r, err := ListDetail(client, &ListOpts{Name: name}).AllPages() - if err != nil { - return "", err - } - - ss, err := ExtractShares(r) - if err != nil { - return "", err - } - - switch len(ss) { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "share"} - case 1: - return ss[0].ID, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: len(ss), ResourceType: "share"} - } -} - // GetExportLocationsResult contains the result body and error from an // GetExportLocations request. type GetExportLocationsResult struct { diff --git a/openstack/sharedfilesystems/v2/sharetypes/results.go b/openstack/sharedfilesystems/v2/sharetypes/results.go index c0eb4b2ec4..f60d757766 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/results.go +++ b/openstack/sharedfilesystems/v2/sharetypes/results.go @@ -33,40 +33,6 @@ func (r commonResult) Extract() (*ShareType, error) { return s.ShareType, err } -// IDFromName is a convenience function that returns a share-type's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - r, err := List(client, &ListOpts{}).AllPages() - if err != nil { - return "", nil - } - - ss, err := ExtractShareTypes(r) - if err != nil { - return "", err - } - - var ( - count int - id string - ) - - for _, s := range ss { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "share type"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "share type"} - } -} - // CreateResult contains the response body and error from a Create request. type CreateResult struct { commonResult diff --git a/openstack/sharedfilesystems/v2/snapshots/results.go b/openstack/sharedfilesystems/v2/snapshots/results.go index 4952dbd34a..6b4bb9b95c 100644 --- a/openstack/sharedfilesystems/v2/snapshots/results.go +++ b/openstack/sharedfilesystems/v2/snapshots/results.go @@ -169,25 +169,3 @@ type GetResult struct { type UpdateResult struct { commonResult } - -// IDFromName is a convenience function that returns a snapshot's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - r, err := ListDetail(client, &ListOpts{Name: name}).AllPages() - if err != nil { - return "", err - } - - ss, err := ExtractSnapshots(r) - if err != nil { - return "", err - } - - switch len(ss) { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "snapshot"} - case 1: - return ss[0].ID, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: len(ss), ResourceType: "snapshot"} - } -} From 83f764eff3ed2fc528f5e4112b7b6950d8a14772 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 15 Mar 2020 20:05:57 -0600 Subject: [PATCH 1022/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d028454abd..0b9b90fd89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.10.0 (Unreleased) +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) + IMPROVEMENTS * Added `blockstorage/extensions/volumeactions.SetBootable` [GH-1891](https://github.com/gophercloud/gophercloud/pull/1891) From cced16465ebe64509b235d75f75af4f8a3d6c40b Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Sat, 21 Mar 2020 14:59:11 -0700 Subject: [PATCH 1023/2296] Add resource provider traits API for placement (#1899) --- .../placement/v1/resourceproviders_test.go | 25 ++++++++++++++ .../placement/v1/resourceproviders/doc.go | 7 ++++ .../v1/resourceproviders/requests.go | 5 +++ .../placement/v1/resourceproviders/results.go | 22 +++++++++++-- .../v1/resourceproviders/testing/fixtures.go | 33 +++++++++++++++++++ .../testing/requests_test.go | 11 +++++++ .../placement/v1/resourceproviders/urls.go | 4 +++ 7 files changed, 105 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 67553f19d5..5f0daf1a07 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -95,3 +95,28 @@ func TestResourceProviderInventories(t *testing.T) { tools.PrintResource(t, usage) } + +func TestResourceProviderTraits(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + 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() + th.AssertNoErr(t, err) + + // now get the traits for the newly created resource provider + usage, err := resourceproviders.GetTraits(client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, usage) +} diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index d14b203f79..d11000d22f 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -43,5 +43,12 @@ Example to get resource providers inventories panic(err) } +Example to get resource providers traits + + rp, err := resourceproviders.GetTraits(placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } + */ package resourceproviders diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index d868b50864..6e16d27676 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -112,3 +112,8 @@ func GetInventories(client *gophercloud.ServiceClient, resourceProviderID string _, r.Err = client.Get(getResourceProviderInventoriesURL(client, resourceProviderID), &r.Body, nil) return } + +func GetTraits(client *gophercloud.ServiceClient, resourceProviderID string) (r GetTraitsResult) { + _, r.Err = client.Get(getResourceProviderTraitsURL(client, resourceProviderID), &r.Body, nil) + return +} diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index c1d676b480..0562451777 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -52,7 +52,12 @@ type ResourceProviderInventories struct { Inventories map[string]Inventory `json:"inventories"` } -// resourceProviderResult is the resposne of a base ResourceProvider result. +type ResourceProviderTraits struct { + ResourceProviderGeneration int `json:"resource_provider_generation"` + Traits []string `json:"traits"` +} + +// resourceProviderResult is the response of a base ResourceProvider result. type resourceProviderResult struct { gophercloud.Result } @@ -104,7 +109,7 @@ func (r GetUsagesResult) Extract() (*ResourceProviderUsage, error) { return &s, err } -// GetInventoriesResult is the response of a Get usage operations. Call its Extract method +// GetInventoriesResult is the response of a Get inventories operations. Call its Extract method // to interpret it as a ResourceProviderInventories. type GetInventoriesResult struct { gophercloud.Result @@ -116,3 +121,16 @@ func (r GetInventoriesResult) Extract() (*ResourceProviderInventories, error) { err := r.ExtractInto(&s) return &s, err } + +// GetTraitsResult is the response of a Get traits operations. Call its Extract method +// to interpret it as a ResourceProviderTraits. +type GetTraitsResult struct { + gophercloud.Result +} + +// Extract interprets a GetTraitsResult as a ResourceProviderTraits. +func (r GetTraitsResult) Extract() (*ResourceProviderTraits, error) { + var s ResourceProviderTraits + err := r.ExtractInto(&s) + return &s, err +} diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures.go index dd7fd7ee66..34256b7693 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -105,6 +105,16 @@ const InventoriesBody = ` } ` +const TraitsBody = ` +{ + "resource_provider_generation": 1, + "traits": [ + "CUSTOM_HW_FPGA_CLASS1", + "CUSTOM_HW_FPGA_CLASS3" + ] +} +` + var ExpectedResourceProvider1 = resourceproviders.ResourceProvider{ Generation: 1, UUID: "99c09379-6e52-4ef8-9a95-b9ce6f68452e", @@ -177,6 +187,14 @@ var ExpectedInventories = resourceproviders.ResourceProviderInventories{ }, } +var ExpectedTraits = resourceproviders.ResourceProviderTraits{ + ResourceProviderGeneration: 1, + Traits: []string{ + "CUSTOM_HW_FPGA_CLASS1", + "CUSTOM_HW_FPGA_CLASS3", + }, +} + func HandleResourceProviderList(t *testing.T) { th.Mux.HandleFunc("/resource_providers", func(w http.ResponseWriter, r *http.Request) { @@ -231,3 +249,18 @@ func HandleResourceProviderGetInventories(t *testing.T) { fmt.Fprintf(w, InventoriesBody) }) } + +func HandleResourceProviderGetTraits(t *testing.T) { + traitsTestUrl := fmt.Sprintf("/resource_providers/%s/traits", ResourceProviderTestID) + + th.Mux.HandleFunc(traitsTestUrl, + 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, TraitsBody) + }) +} diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 75c5532a42..bf3837b8b0 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -77,3 +77,14 @@ func TestGetResourceProvidersInventories(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedInventories, *actual) } + +func TestGetResourceProvidersTraits(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderGetTraits(t) + + actual, err := resourceproviders.GetTraits(fake.ServiceClient(), ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedTraits, *actual) +} diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 2caaaded06..5f0ef2e347 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -17,3 +17,7 @@ func getResourceProviderUsagesURL(client *gophercloud.ServiceClient, resourcePro func getResourceProviderInventoriesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "inventories") } + +func getResourceProviderTraitsURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID, "traits") +} From 8406d758722f6f35a342c27de3fb13ed5838f3ee Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 21 Mar 2020 16:00:02 -0600 Subject: [PATCH 1024/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b9b90fd89..9ff898944a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ 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) ## 0.9.0 (March 10, 2020) From 747a23a22e5d06749bdd3274ff8f160917d3ef96 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 24 Mar 2020 03:40:07 +0100 Subject: [PATCH 1025/2296] Auth: introduce ec2 credentials auth support (#1900) * Auth: introduce ec2 credentials auth support * Rename and export constants * Move auth params from Params to Headers --- acceptance/openstack/client_test.go | 68 ++++ openstack/client.go | 13 +- .../identity/v3/extensions/ec2tokens/doc.go | 41 +++ .../v3/extensions/ec2tokens/requests.go | 320 ++++++++++++++++++ .../ec2tokens/testing/requests_test.go | 282 +++++++++++++++ .../identity/v3/extensions/ec2tokens/urls.go | 7 + 6 files changed, 730 insertions(+), 1 deletion(-) create mode 100644 openstack/identity/v3/extensions/ec2tokens/doc.go create mode 100644 openstack/identity/v3/extensions/ec2tokens/requests.go create mode 100644 openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go create mode 100644 openstack/identity/v3/extensions/ec2tokens/urls.go diff --git a/acceptance/openstack/client_test.go b/acceptance/openstack/client_test.go index eed3a82ff4..b48492398f 100644 --- a/acceptance/openstack/client_test.go +++ b/acceptance/openstack/client_test.go @@ -8,7 +8,13 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/testhelper" ) func TestAuthenticatedClient(t *testing.T) { @@ -40,6 +46,68 @@ func TestAuthenticatedClient(t *testing.T) { } } +func TestEC2AuthMethod(t *testing.T) { + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + authOptions := tokens.AuthOptions{ + Username: ao.Username, + Password: ao.Password, + DomainName: ao.DomainName, + DomainID: ao.DomainID, + // We need a scope to get the token roles list + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, + } + token, err := tokens.Create(client, &authOptions).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) + + user, err := tokens.Get(client, token.ID).ExtractUser() + th.AssertNoErr(t, err) + tools.PrintResource(t, user) + + project, err := tokens.Get(client, token.ID).ExtractProject() + th.AssertNoErr(t, err) + tools.PrintResource(t, project) + + createOpts := credentials.CreateOpts{ + ProjectID: project.ID, + Type: "ec2", + UserID: user.ID, + Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + } + + // Create a credential + credential, err := credentials.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + + // Delete a credential + defer credentials.Delete(client, credential.ID) + tools.PrintResource(t, credential) + + newClient, err := clients.NewIdentityV3UnauthenticatedClient() + th.AssertNoErr(t, err) + + var ec2AuthOptions tokens.AuthOptionsBuilder + ec2AuthOptions = &ec2tokens.AuthOptions{ + Access: "181920", + Secret: "secretKey", + } + + err = openstack.AuthenticateV3(newClient.ProviderClient, ec2AuthOptions, gophercloud.EndpointOpts{}) + th.AssertNoErr(t, err) + + tools.PrintResource(t, newClient.TokenID) +} + func TestReauth(t *testing.T) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { diff --git a/openstack/client.go b/openstack/client.go index 0b21beef93..f470b8c888 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/openstack/utils" ) @@ -224,7 +225,13 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au return err } } else { - result := tokens3.Create(v3Client, opts) + var result tokens3.CreateResult + switch opts.(type) { + case *ec2tokens.AuthOptions: + result = ec2tokens.Create(v3Client, opts) + default: + result = tokens3.Create(v3Client, opts) + } err = client.SetTokenAndAuthResult(result) if err != nil { @@ -255,6 +262,10 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au o := *ot o.AllowReauth = false tao = &o + case *ec2tokens.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o default: tao = opts } diff --git a/openstack/identity/v3/extensions/ec2tokens/doc.go b/openstack/identity/v3/extensions/ec2tokens/doc.go new file mode 100644 index 0000000000..1f6f807fe0 --- /dev/null +++ b/openstack/identity/v3/extensions/ec2tokens/doc.go @@ -0,0 +1,41 @@ +/* +Package tokens provides information and interaction with the EC2 token API +resource for the OpenStack Identity service. + +For more information, see: +https://docs.openstack.org/api-ref/identity/v2-ext/ + +Example to Create a Token From an EC2 access and secret keys + + var authOptions tokens.AuthOptionsBuilder + authOptions = &ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + } + + token, err := ec2tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + +Example to auth a client using EC2 access and secret keys + + client, err := openstack.NewClient("http://localhost:5000/v3") + if err != nil { + panic(err) + } + + var authOptions tokens.AuthOptionsBuilder + authOptions = &ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + AllowReauth: true, + } + + err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) + if err != nil { + panic(err) + } + +*/ +package ec2tokens diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go new file mode 100644 index 0000000000..9be866d157 --- /dev/null +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -0,0 +1,320 @@ +package ec2tokens + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "encoding/hex" + "fmt" + "math/rand" + "net/url" + "sort" + "strings" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" +) + +const ( + // EC2CredentialsAwsRequestV4 is a constant, used to generate AWS + // Credential V4. + EC2CredentialsAwsRequestV4 = "aws4_request" + // EC2CredentialsHmacSha1V2 is a HMAC SHA1 signature method. Used to + // generate AWS Credential V2. + EC2CredentialsHmacSha1V2 = "HmacSHA1" + // EC2CredentialsHmacSha256V2 is a HMAC SHA256 signature method. Used + // to generate AWS Credential V2. + EC2CredentialsHmacSha256V2 = "HmacSHA256" + // EC2CredentialsAwsHmacV4 is an AWS signature V4 signing method. + // More details: + // https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html + EC2CredentialsAwsHmacV4 = "AWS4-HMAC-SHA256" + // EC2CredentialsTimestampFormatV4 is an AWS signature V4 timestamp + // format. + EC2CredentialsTimestampFormatV4 = "20060102T150405Z" + // EC2CredentialsDateFormatV4 is an AWS signature V4 date format. + EC2CredentialsDateFormatV4 = "20060102" +) + +// AuthOptions represents options for authenticating a user using EC2 credentials. +type AuthOptions struct { + // Access is the EC2 Credential Access ID. + Access string `json:"access" required:"true"` + // Secret is the EC2 Credential Secret, used to calculate signature. + // Not used, when a Signature is is. + Secret string `json:"-"` + // Host is a HTTP request Host header. Used to calculate an AWS + // signature V2. For signature V4 set the Host inside Headers map. + // Optional. + Host string `json:"host"` + // Path is a HTTP request path. Optional. + Path string `json:"path"` + // Verb is a HTTP request method. Optional. + Verb string `json:"verb"` + // Headers is a map of HTTP request headers. Optional. + Headers map[string]string `json:"headers"` + // Region is a region name to calculate an AWS signature V4. Optional. + Region string `json:"-"` + // Service is a service name to calculate an AWS signature V4. Optional. + Service string `json:"-"` + // Params is a map of GET method parameters. Optional. + Params map[string]string `json:"params"` + // AllowReauth allows Gophercloud to re-authenticate automatically + // if/when your token expires. + AllowReauth bool `json:"-"` + // Signature can be either a []byte (encoded to base64 automatically) or + // a string. You can set the singature explicitly, when you already know + // it. In this case default Params won't be automatically set. Optional. + Signature interface{} `json:"signature"` + // BodyHash is a HTTP request body sha256 hash. When nil and Signature + // is not set, a random hash is generated. Optional. + BodyHash *string `json:"body_hash"` + // Timestamp is a timestamp to calculate a V4 signature. Optional. + Timestamp *time.Time `json:"-"` +} + +// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string +// for an AWS signature V2. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L133 +func EC2CredentialsBuildCanonicalQueryStringV2(params map[string]string) string { + var keys []string + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + + var pairs []string + for _, k := range keys { + pairs = append(pairs, fmt.Sprintf("%s=%s", k, url.QueryEscape(params[k]))) + } + + return strings.Join(pairs, "&") +} + +// EC2CredentialsBuildStringToSignV2 builds a string to sign an AWS signature +// V2. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L148 +func EC2CredentialsBuildStringToSignV2(opts AuthOptions) string { + stringToSign := strings.Join([]string{ + opts.Verb, + opts.Host, + opts.Path, + }, "\n") + + return strings.Join([]string{ + stringToSign, + EC2CredentialsBuildCanonicalQueryStringV2(opts.Params), + }, "\n") +} + +// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string +// for an AWS signature V4. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L244 +func EC2CredentialsBuildCanonicalQueryStringV4(verb string, params map[string]string) string { + if verb == "POST" { + return "" + } + return EC2CredentialsBuildCanonicalQueryStringV2(params) +} + +// EC2CredentialsBuildCanonicalHeadersV4 builds a canonical string based on +// "headers" map and "signedHeaders" string parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L216 +func EC2CredentialsBuildCanonicalHeadersV4(headers map[string]string, signedHeaders string) string { + headersLower := make(map[string]string, len(headers)) + for k, v := range headers { + headersLower[strings.ToLower(k)] = v + } + + var headersList []string + for _, h := range strings.Split(signedHeaders, ";") { + if v, ok := headersLower[h]; ok { + headersList = append(headersList, h+":"+v) + } + } + + return strings.Join(headersList, "\n") + "\n" +} + +// EC2CredentialsBuildSignatureKeyV4 builds a HMAC 256 signature key based on +// input parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L169 +func EC2CredentialsBuildSignatureKeyV4(secret, date, region, service string) []byte { + kDate := sumHMAC256([]byte("AWS4"+secret), []byte(date)) + kRegion := sumHMAC256(kDate, []byte(region)) + kService := sumHMAC256(kRegion, []byte(service)) + return sumHMAC256(kService, []byte(EC2CredentialsAwsRequestV4)) +} + +// EC2CredentialsBuildSignatureV4 builds an AWS v4 signature based on input +// parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L251 +func EC2CredentialsBuildSignatureV4(opts AuthOptions, signedHeaders string, date time.Time, bodyHash string) string { + scope := strings.Join([]string{ + date.Format(EC2CredentialsDateFormatV4), + opts.Region, + opts.Service, + EC2CredentialsAwsRequestV4, + }, "/") + + canonicalRequest := strings.Join([]string{ + opts.Verb, + opts.Path, + EC2CredentialsBuildCanonicalQueryStringV4(opts.Verb, opts.Params), + EC2CredentialsBuildCanonicalHeadersV4(opts.Headers, signedHeaders), + signedHeaders, + bodyHash, + }, "\n") + hash := sha256.Sum256([]byte(canonicalRequest)) + + strToSign := strings.Join([]string{ + EC2CredentialsAwsHmacV4, + date.Format(EC2CredentialsTimestampFormatV4), + scope, + hex.EncodeToString(hash[:]), + }, "\n") + + key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, date.Format(EC2CredentialsDateFormatV4), opts.Region, opts.Service) + + return hex.EncodeToString(sumHMAC256(key, []byte(strToSign))) +} + +// ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder interface +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { + return nil, nil +} + +// CanReauth is a method method to satisfy tokens.AuthOptionsBuilder interface +func (opts *AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + +// ToTokenV3CreateMap formats an AuthOptions into a create request. +func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "credentials") + if err != nil { + return nil, err + } + + if opts.Signature != nil { + return b, nil + } + + // calculate signature, when it is not set + c, _ := b["credentials"].(map[string]interface{}) + h := interfaceToMap(c, "headers") + p := interfaceToMap(c, "params") + + // detect and process a signature v2 + if v, ok := p["SignatureVersion"]; ok && v == "2" { + if _, ok := c["body_hash"]; ok { + delete(c, "body_hash") + } + if _, ok := c["headers"]; ok { + delete(c, "headers") + } + if v, ok := p["SignatureMethod"]; ok { + // params is a map of strings + strToSign := EC2CredentialsBuildStringToSignV2(*opts) + switch v { + case EC2CredentialsHmacSha1V2: + // keystone uses this method only when HmacSHA256 is not available on the server side + // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L151..L156 + c["signature"] = sumHMAC1([]byte(opts.Secret), []byte(strToSign)) + return b, nil + case EC2CredentialsHmacSha256V2: + c["signature"] = sumHMAC256([]byte(opts.Secret), []byte(strToSign)) + return b, nil + } + return nil, fmt.Errorf("unsupported signature method: %s", v) + } + return nil, fmt.Errorf("signature method must be provided") + } else if ok { + return nil, fmt.Errorf("unsupported signature version: %s", v) + } + + // it is not a signature v2, but a signature v4 + date := time.Now().UTC() + if opts.Timestamp != nil { + date = *opts.Timestamp + } + if v, _ := c["body_hash"]; v == nil { + // when body_hash is not set, generate a random one + c["body_hash"] = randomBodyHash() + } + + signedHeaders, _ := h["X-Amz-SignedHeaders"] + c["signature"] = EC2CredentialsBuildSignatureV4(*opts, signedHeaders, date, c["body_hash"].(string)) + + h["X-Amz-Date"] = date.Format(EC2CredentialsTimestampFormatV4) + h["Authorization"] = fmt.Sprintf("%s Credential=%s/%s/%s/%s/%s, SignedHeaders=%s, Signature=%s", + EC2CredentialsAwsHmacV4, + opts.Access, + date.Format(EC2CredentialsDateFormatV4), + opts.Region, + opts.Service, + EC2CredentialsAwsRequestV4, + signedHeaders, + c["signature"]) + + 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) { + b, err := opts.ToTokenV3CreateMap(nil) + if err != nil { + r.Err = err + return + } + + resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"X-Auth-Token": ""}, + OkCodes: []int{200}, + }) + r.Err = err + if resp != nil { + r.Header = resp.Header + } + return +} + +// The following are small helper functions used to help build the signature. + +// sumHMAC1 is a func to implement the HMAC SHA1 signature method. +func sumHMAC1(key []byte, data []byte) []byte { + hash := hmac.New(sha1.New, key) + hash.Write(data) + return hash.Sum(nil) +} + +// sumHMAC256 is a func to implement the HMAC SHA256 signature method. +func sumHMAC256(key []byte, data []byte) []byte { + hash := hmac.New(sha256.New, key) + hash.Write(data) + return hash.Sum(nil) +} + +// randomBodyHash is a func to generate a random sha256 hexdigest. +func randomBodyHash() string { + h := make([]byte, 64) + rand.Read(h) + return hex.EncodeToString(h) +} + +// interfaceToMap is a func used to represent a "credentials" map element as a +// "map[string]string" +func interfaceToMap(c map[string]interface{}, key string) map[string]string { + // convert map[string]interface{} to map[string]string + m := make(map[string]string) + if v, _ := c[key].(map[string]interface{}); v != nil { + for k, v := range v { + m[k] = v.(string) + } + } + + c[key] = m + + return m +} diff --git a/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go b/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go new file mode 100644 index 0000000000..4dc7014df8 --- /dev/null +++ b/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go @@ -0,0 +1,282 @@ +package testing + +import ( + "encoding/hex" + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + tokens_testing "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/testing" + "github.com/gophercloud/gophercloud/testhelper" +) + +// authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. +func authTokenPost(t *testing.T, options ec2tokens.AuthOptions, requestJSON string) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + + client := gophercloud.ServiceClient{ + ProviderClient: &gophercloud.ProviderClient{}, + Endpoint: testhelper.Endpoint(), + } + + testhelper.Mux.HandleFunc("/ec2tokens", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "POST") + testhelper.TestHeader(t, r, "Content-Type", "application/json") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestJSONRequest(t, r, requestJSON) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, tokens_testing.TokenOutput) + }) + + expected := &tokens.Token{ + ExpiresAt: time.Date(2017, 6, 3, 2, 19, 49, 0, time.UTC), + } + + actual, err := ec2tokens.Create(&client, &options).Extract() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, expected, actual) +} + +func TestCreateV2(t *testing.T) { + credentials := ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + Host: "localhost", + Path: "/", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + Verb: "GET", + // this should be removed from JSON request + BodyHash: new(string), + // this should be removed from JSON request + Headers: map[string]string{ + "Foo": "Bar", + }, + Params: map[string]string{ + "Action": "Test", + "SignatureMethod": "HmacSHA256", + "SignatureVersion": "2", + }, + } + authTokenPost(t, credentials, `{ + "credentials": { + "access": "a7f1e798b7c2417cba4a02de97dc3cdc", + "host": "localhost", + "params": { + "Action": "Test", + "SignatureMethod": "HmacSHA256", + "SignatureVersion": "2" + }, + "path": "/", + "signature": "Up+MbVbbrvdR5FRkUz+n3nc+VW6xieuN50wh6ONEJ4w=", + "verb": "GET" + } +}`) +} + +func TestCreateV4(t *testing.T) { + bodyHash := "foo" + credentials := ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + BodyHash: &bodyHash, + Timestamp: new(time.Time), + Region: "region1", + Service: "ec2", + Path: "/", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + Verb: "GET", + Headers: map[string]string{ + "Host": "localhost", + }, + Params: map[string]string{ + "Action": "Test", + }, + } + authTokenPost(t, credentials, `{ + "credentials": { + "access": "a7f1e798b7c2417cba4a02de97dc3cdc", + "body_hash": "foo", + "host": "", + "headers": { + "Host": "localhost", + "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101/region1/ec2/aws4_request, SignedHeaders=, Signature=f36f79118f75d7d6ec86ead9a61679cbdcf94c0cbfe5e9cf2407e8406aa82028", + "X-Amz-Date": "00010101T000000Z" + }, + "params": { + "Action": "Test" + }, + "path": "/", + "signature": "f36f79118f75d7d6ec86ead9a61679cbdcf94c0cbfe5e9cf2407e8406aa82028", + "verb": "GET" + } +}`) +} + +func TestCreateV4Empty(t *testing.T) { + credentials := ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + BodyHash: new(string), + Timestamp: new(time.Time), + } + authTokenPost(t, credentials, `{ + "credentials": { + "access": "a7f1e798b7c2417cba4a02de97dc3cdc", + "body_hash": "", + "host": "", + "headers": { + "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101///aws4_request, SignedHeaders=, Signature=140a31abf1efe93a607dcac6cd8f66887b86d2bc8f712c290d9aa06edf428608", + "X-Amz-Date": "00010101T000000Z" + }, + "params": {}, + "path": "", + "signature": "140a31abf1efe93a607dcac6cd8f66887b86d2bc8f712c290d9aa06edf428608", + "verb": "" + } +}`) +} + +func TestCreateV4Headers(t *testing.T) { + credentials := ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + BodyHash: new(string), + Timestamp: new(time.Time), + Region: "region1", + Service: "ec2", + Path: "/", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + Verb: "GET", + Headers: map[string]string{ + "Foo": "Bar", + "Host": "localhost", + }, + Params: map[string]string{ + "Action": "Test", + }, + } + authTokenPost(t, credentials, `{ + "credentials": { + "access": "a7f1e798b7c2417cba4a02de97dc3cdc", + "body_hash": "", + "host": "", + "headers": { + "Foo": "Bar", + "Host": "localhost", + "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101/region1/ec2/aws4_request, SignedHeaders=, Signature=f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", + "X-Amz-Date": "00010101T000000Z" + }, + "params": { + "Action": "Test" + }, + "path": "/", + "signature": "f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", + "verb": "GET" + } +}`) +} + +func TestCreateV4WithSignature(t *testing.T) { + credentials := ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + BodyHash: new(string), + Path: "/", + Signature: "f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", + Verb: "GET", + Headers: map[string]string{ + "Foo": "Bar", + "Host": "localhost", + "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101/region1/ec2/aws4_request, SignedHeaders=, Signature=f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", + "X-Amz-Date": "00010101T000000Z", + }, + Params: map[string]string{ + "Action": "Test", + }, + } + authTokenPost(t, credentials, `{ + "credentials": { + "access": "a7f1e798b7c2417cba4a02de97dc3cdc", + "body_hash": "", + "host": "", + "headers": { + "Foo": "Bar", + "Host": "localhost", + "Authorization": "AWS4-HMAC-SHA256 Credential=a7f1e798b7c2417cba4a02de97dc3cdc/00010101/region1/ec2/aws4_request, SignedHeaders=, Signature=f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", + "X-Amz-Date": "00010101T000000Z" + }, + "params": { + "Action": "Test" + }, + "path": "/", + "signature": "f5cd6995be98e5576a130b30cca277375f10439217ea82169aa8386e83965611", + "verb": "GET" + } +}`) +} + +func TestEC2CredentialsBuildCanonicalQueryStringV2(t *testing.T) { + params := map[string]string{ + "Action": "foo", + "Value": "bar", + } + expected := "Action=foo&Value=bar" + testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalQueryStringV2(params)) +} + +func TestEC2CredentialsBuildStringToSignV2(t *testing.T) { + opts := ec2tokens.AuthOptions{ + Verb: "GET", + Host: "localhost", + Path: "/", + Params: map[string]string{ + "Action": "foo", + "Value": "bar", + }, + } + expected := "GET\nlocalhost\n/\nAction=foo&Value=bar" + testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildStringToSignV2(opts)) +} + +func TestEC2CredentialsBuildCanonicalQueryStringV4(t *testing.T) { + params := map[string]string{ + "Action": "foo", + "Value": "bar", + } + expected := "Action=foo&Value=bar" + testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalQueryStringV4("foo", params)) + testhelper.CheckEquals(t, "", ec2tokens.EC2CredentialsBuildCanonicalQueryStringV4("POST", params)) +} + +func TestEC2CredentialsBuildCanonicalHeadersV4(t *testing.T) { + headers := map[string]string{ + "Foo": "bar", + "Baz": "qux", + } + signedHeaders := "foo;baz" + expected := "foo:bar\nbaz:qux\n" + testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalHeadersV4(headers, signedHeaders)) +} + +func TestEC2CredentialsBuildSignatureKeyV4(t *testing.T) { + expected := "5f06633a42b1324477cb006b24b1266722703b5dcf9186481be6592b9554c38f" + testhelper.CheckEquals(t, expected, hex.EncodeToString((ec2tokens.EC2CredentialsBuildSignatureKeyV4("foo", "bar", "baz", "qux")))) +} + +func TestEC2CredentialsBuildSignatureV4(t *testing.T) { + opts := ec2tokens.AuthOptions{ + Verb: "GET", + Path: "/", + Headers: map[string]string{ + "Host": "localhost", + }, + Params: map[string]string{ + "Action": "foo", + "Value": "bar", + }, + } + expected := "6a5febe41427bf601f0ae7c34dbb0fd67094776138b03fb8e65783d733d302a5" + testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildSignatureV4(opts, "host", time.Time{}, "foo")) +} diff --git a/openstack/identity/v3/extensions/ec2tokens/urls.go b/openstack/identity/v3/extensions/ec2tokens/urls.go new file mode 100644 index 0000000000..0d512d1892 --- /dev/null +++ b/openstack/identity/v3/extensions/ec2tokens/urls.go @@ -0,0 +1,7 @@ +package ec2tokens + +import "github.com/gophercloud/gophercloud" + +func ec2tokensURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("ec2tokens") +} From 75e4c872952fedae76a8abdd13ebe357c0cdeeaf Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 23 Mar 2020 20:41:06 -0600 Subject: [PATCH 1026/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ff898944a..8c28660e19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ IMPROVEMENTS * 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) ## 0.9.0 (March 10, 2020) From 857b524402b708a6a614668411b25f308b41ca8c Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Mon, 23 Mar 2020 20:12:19 -0700 Subject: [PATCH 1027/2296] Adding query parameter to Compute list services API (#1904) * Add resource provider traits API for placement * add opts to Compute API list services * fix doc --- .../openstack/compute/v2/services_test.go | 31 ++++++++++++++++++- .../compute/v2/extensions/services/doc.go | 6 +++- .../v2/extensions/services/requests.go | 31 +++++++++++++++++-- .../services/testing/requests_test.go | 8 +++-- 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/compute/v2/services_test.go b/acceptance/openstack/compute/v2/services_test.go index 5c6484e0e6..4ed1ef4385 100644 --- a/acceptance/openstack/compute/v2/services_test.go +++ b/acceptance/openstack/compute/v2/services_test.go @@ -17,7 +17,7 @@ func TestServicesList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := services.List(client).AllPages() + allPages, err := services.List(client, nil).AllPages() th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) @@ -34,3 +34,32 @@ func TestServicesList(t *testing.T) { th.AssertEquals(t, found, true) } + +func TestServicesListWithOpts(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + opts := services.ListOpts{ + Binary: "nova-scheduler", + } + + allPages, err := services.List(client, opts).AllPages() + th.AssertNoErr(t, err) + + allServices, err := services.ExtractServices(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, service := range allServices { + tools.PrintResource(t, service) + th.AssertEquals(t, service.Binary, "nova-scheduler") + + if service.Binary == "nova-scheduler" { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/openstack/compute/v2/extensions/services/doc.go b/openstack/compute/v2/extensions/services/doc.go index 2d38c42a95..2946da7e99 100644 --- a/openstack/compute/v2/extensions/services/doc.go +++ b/openstack/compute/v2/extensions/services/doc.go @@ -4,7 +4,11 @@ cloud. Example of Retrieving list of all services - allPages, err := services.List(computeClient).AllPages() + opts := services.ListOpts{ + Binary: "nova-scheduler", + } + + allPages, err := services.List(computeClient, opts).AllPages() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/services/requests.go b/openstack/compute/v2/extensions/services/requests.go index d2e31f82d3..05d814a974 100644 --- a/openstack/compute/v2/extensions/services/requests.go +++ b/openstack/compute/v2/extensions/services/requests.go @@ -5,9 +5,36 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to +// the List request. +type ListOptsBuilder interface { + ToServicesListQuery() (string, error) +} + +// ListOpts represents options to list services. +type ListOpts struct { + Binary string `q:"binary"` + Host string `q:"host"` +} + +// ToServicesListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToServicesListQuery() (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) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToServicesListQuery() + 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/compute/v2/extensions/services/testing/requests_test.go b/openstack/compute/v2/extensions/services/testing/requests_test.go index b8972c9bc3..a1e5b58588 100644 --- a/openstack/compute/v2/extensions/services/testing/requests_test.go +++ b/openstack/compute/v2/extensions/services/testing/requests_test.go @@ -15,7 +15,7 @@ func TestListServicesPre253(t *testing.T) { HandleListPre253Successfully(t) pages := 0 - err := services.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := services.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := services.ExtractServices(page) @@ -47,7 +47,11 @@ func TestListServices(t *testing.T) { HandleListSuccessfully(t) pages := 0 - err := services.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + opts := services.ListOpts{ + Binary: "fake-binary", + Host: "host123", + } + err := services.List(client.ServiceClient(), opts).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := services.ExtractServices(page) From fbb6b8b2031841da2c7d7b93b4e33878a2e4fed7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 23 Mar 2020 21:18:41 -0600 Subject: [PATCH 1028/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c28660e19..fc32ec3f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ IMPROVEMENTS * 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) ## 0.9.0 (March 10, 2020) From 07253c9f4712ea1812b38a17a83eb3c4b1bb5f01 Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Tue, 24 Mar 2020 19:59:29 -0700 Subject: [PATCH 1029/2296] Add support for update of Compute service (#1902) * Add resource provider traits API for placement * Add support for updating os-services in Compute API * Add support for updating os-services in Compute API * use string const for status * Add support for updating os-services in Compute API * use string const for status * use list opts in list services call --- .../openstack/compute/v2/services_test.go | 57 +++++++++++++++++++ .../compute/v2/extensions/services/doc.go | 11 ++++ .../v2/extensions/services/requests.go | 41 +++++++++++++ .../compute/v2/extensions/services/results.go | 22 +++++++ .../extensions/services/testing/fixtures.go | 45 +++++++++++++++ .../services/testing/requests_test.go | 14 +++++ .../compute/v2/extensions/services/urls.go | 4 ++ 7 files changed, 194 insertions(+) diff --git a/acceptance/openstack/compute/v2/services_test.go b/acceptance/openstack/compute/v2/services_test.go index 4ed1ef4385..b565986390 100644 --- a/acceptance/openstack/compute/v2/services_test.go +++ b/acceptance/openstack/compute/v2/services_test.go @@ -63,3 +63,60 @@ func TestServicesListWithOpts(t *testing.T) { th.AssertEquals(t, found, true) } + +func TestServicesUpdate(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + listOpts := services.ListOpts{ + Binary: "nova-compute", + } + + client.Microversion = "2.53" + allPages, err := services.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allServices, err := services.ExtractServices(allPages) + th.AssertNoErr(t, err) + + // disable all services + for _, service := range allServices { + opts := services.UpdateOpts{ + Status: services.ServiceDisabled, + } + updated, err := services.Update(client, service.ID, opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.ID, service.ID) + } + + // verify all services are disabled + allPages, err = services.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allServices, err = services.ExtractServices(allPages) + th.AssertNoErr(t, err) + + for _, service := range allServices { + th.AssertEquals(t, service.Status, "disabled") + } + + // reenable all services + allPages, err = services.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allServices, err = services.ExtractServices(allPages) + th.AssertNoErr(t, err) + + for _, service := range allServices { + opts := services.UpdateOpts{ + Status: services.ServiceEnabled, + } + updated, err := services.Update(client, service.ID, opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.ID, service.ID) + } +} diff --git a/openstack/compute/v2/extensions/services/doc.go b/openstack/compute/v2/extensions/services/doc.go index 2946da7e99..c960cb1640 100644 --- a/openstack/compute/v2/extensions/services/doc.go +++ b/openstack/compute/v2/extensions/services/doc.go @@ -21,6 +21,17 @@ Example of Retrieving list of all services for _, service := range allServices { fmt.Printf("%+v\n", service) } + +Example of updating a service + + opts := services.UpdateOpts{ + Status: services.ServiceDisabled, + } + + updated, err := services.Update(client, serviceID, opts).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 05d814a974..ed8907d1d0 100644 --- a/openstack/compute/v2/extensions/services/requests.go +++ b/openstack/compute/v2/extensions/services/requests.go @@ -38,3 +38,44 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa return ServicePage{pagination.SinglePageBase(r)} }) } + +type ServiceStatus string + +const ( + // ServiceEnabled is used to mark a service as being enabled. + ServiceEnabled ServiceStatus = "enabled" + + // ServiceDisabled is used to mark a service as being disabled. + ServiceDisabled ServiceStatus = "disabled" +) + +// UpdateOpts specifies the base attributes that may be updated on a service. +type UpdateOpts struct { + // Status represents the new service status. One of enabled or disabled. + Status ServiceStatus `json:"status,omitempty"` + + // DisabledReason represents the reason for disabling a service. + DisabledReason string `json:"disabled_reason,omitempty"` + + // ForcedDown is a manual override to tell nova that the service in question + // has been fenced manually by the operations team. + ForcedDown bool `json:"forced_down,omitempty"` +} + +// ToServiceUpdateMap formats an UpdateOpts structure into a request body. +func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update requests that various attributes of the indicated service be changed. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + b, err := opts.ToServiceUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/compute/v2/extensions/services/results.go b/openstack/compute/v2/extensions/services/results.go index df80fc2a3b..866c54162f 100644 --- a/openstack/compute/v2/extensions/services/results.go +++ b/openstack/compute/v2/extensions/services/results.go @@ -18,6 +18,9 @@ type Service struct { // The reason for disabling a service. DisabledReason string `json:"disabled_reason"` + // Whether or not service was forced down manually. + ForcedDown bool `json:"forced_down"` + // The name of the host. Host string `json:"host"` @@ -69,6 +72,25 @@ func (r *Service) UnmarshalJSON(b []byte) error { return nil } +type serviceResult struct { + gophercloud.Result +} + +// Extract interprets any UpdateResult as a service, if possible. +func (r serviceResult) Extract() (*Service, error) { + var s struct { + Service Service `json:"service"` + } + err := r.ExtractInto(&s) + return &s.Service, err +} + +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Server. +type UpdateResult struct { + serviceResult +} + // ServicePage represents a single page of all Services from a List request. type ServicePage struct { pagination.SinglePageBase diff --git a/openstack/compute/v2/extensions/services/testing/fixtures.go b/openstack/compute/v2/extensions/services/testing/fixtures.go index 9f149dace5..876d68a538 100644 --- a/openstack/compute/v2/extensions/services/testing/fixtures.go +++ b/openstack/compute/v2/extensions/services/testing/fixtures.go @@ -220,6 +220,37 @@ var ( } ) +// ServiceUpdate represents a raw service from the Compute service update API +const ServiceUpdate = ` +{ + "service": + { + "id": 1, + "binary": "nova-scheduler", + "disabled_reason": "test1", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:02.000000", + "forced_down": false, + "zone": "internal" + } +} +` + +//FakeServiceUpdateBody represents the updated service +var FakeServiceUpdateBody = services.Service{ + Binary: "nova-scheduler", + DisabledReason: "test1", + ForcedDown: false, + Host: "host1", + ID: "1", + State: "up", + Status: "disabled", + UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), + Zone: "internal", +} + // HandleListPre253Successfully configures the test server to respond to a List // request to a Compute server API pre 2.53 microversion release. func HandleListPre253Successfully(t *testing.T) { @@ -243,3 +274,17 @@ func HandleListSuccessfully(t *testing.T) { fmt.Fprintf(w, ServiceListBody) }) } + +// HandleUpdateSuccessfully configures the test server to respond to a Update +// request to a Compute server with Pike+ release. +func HandleUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-services/fake-service-id", 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, `{"status": "disabled"}`) + + fmt.Fprintf(w, ServiceUpdate) + }) +} diff --git a/openstack/compute/v2/extensions/services/testing/requests_test.go b/openstack/compute/v2/extensions/services/testing/requests_test.go index a1e5b58588..fc75471a7c 100644 --- a/openstack/compute/v2/extensions/services/testing/requests_test.go +++ b/openstack/compute/v2/extensions/services/testing/requests_test.go @@ -76,3 +76,17 @@ func TestListServices(t *testing.T) { t.Errorf("Expected 1 page, saw %d", pages) } } + +func TestUpdateService(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleUpdateSuccessfully(t) + + client := client.ServiceClient() + actual, err := services.Update(client, "fake-service-id", services.UpdateOpts{Status: services.ServiceDisabled}).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + testhelper.CheckDeepEquals(t, FakeServiceUpdateBody, *actual) +} diff --git a/openstack/compute/v2/extensions/services/urls.go b/openstack/compute/v2/extensions/services/urls.go index 61d794007e..49690efb25 100644 --- a/openstack/compute/v2/extensions/services/urls.go +++ b/openstack/compute/v2/extensions/services/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-services") } + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-services", id) +} From dd4212459636b7cf7cc3db8aff00cb81f2c55a3c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 24 Mar 2020 21:00:20 -0600 Subject: [PATCH 1030/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc32ec3f77..953823acb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ IMPROVEMENTS * 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) ## 0.9.0 (March 10, 2020) From aac4f27ff0469fe69f8455187ab02f180ececc90 Mon Sep 17 00:00:00 2001 From: zhsj Date: Sat, 28 Mar 2020 09:48:59 +0800 Subject: [PATCH 1031/2296] Support system scope in v3 auth (#1908) https://docs.openstack.org/api-ref/identity/v3/?expanded=password-authentication-with-scoped-authorization-detail#system-scoped-example https://github.com/openstack/keystone/blob/f5c9b094af8442219e75265a87551873e058003b/keystone/auth/schema.py#L97-L102 --- auth_options.go | 9 +++++++ openstack/identity/v3/tokens/requests.go | 1 + .../v3/tokens/testing/requests_test.go | 25 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/auth_options.go b/auth_options.go index 5ffa8d1e0a..6185076055 100644 --- a/auth_options.go +++ b/auth_options.go @@ -98,6 +98,7 @@ type AuthScope struct { ProjectName string DomainID string DomainName string + System bool } // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder @@ -364,6 +365,14 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } } + if opts.Scope.System { + return map[string]interface{}{ + "system": map[string]interface{}{ + "all": true, + }, + }, nil + } + if opts.Scope.ProjectName != "" { // ProjectName provided: either DomainID or DomainName must also be supplied. // ProjectID may not be supplied. diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index e4d766b232..52f8b4d727 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -8,6 +8,7 @@ type Scope struct { ProjectName string DomainID string DomainName string + System bool } // AuthOptionsBuilder provides the ability for extensions to add additional diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index 8dc7939841..af88c9450c 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -275,6 +275,31 @@ func TestCreateProjectNameAndDomainNameScope(t *testing.T) { `) } +func TestCreateSystemScope(t *testing.T) { + options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} + scope := &tokens.Scope{System: true} + authTokenPost(t, options, scope, ` + { + "auth": { + "identity": { + "methods": ["password"], + "password": { + "user": { + "id": "fenris", + "password": "g0t0h311" + } + } + }, + "scope": { + "system": { + "all": true + } + } + } + } + `) +} + func TestCreateApplicationCredentialIDAndSecret(t *testing.T) { authTokenPost(t, tokens.AuthOptions{ApplicationCredentialID: "12345abcdef", ApplicationCredentialSecret: "mysecret"}, nil, ` { From ab68fb5cd2f57fa951819a106ff61cb676dc5bff Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 27 Mar 2020 19:50:00 -0600 Subject: [PATCH 1032/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 953823acb4..44cf51ac26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ IMPROVEMENTS * 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) ## 0.9.0 (March 10, 2020) From b127e897f9a74a348cb147c88941e17319264b85 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 28 Mar 2020 22:58:34 +0100 Subject: [PATCH 1033/2296] Keystone V3: Support S3 tokens signature validation (#1906) * Keystone V3: Support S3 tokens signature validation * Add comment --- .../openstack/identity/v3/credentials_test.go | 67 ++++++++++- .../v3/extensions/ec2tokens/requests.go | 104 ++++++++++++++---- .../ec2tokens/testing/requests_test.go | 15 ++- .../identity/v3/extensions/ec2tokens/urls.go | 4 + 4 files changed, 159 insertions(+), 31 deletions(-) diff --git a/acceptance/openstack/identity/v3/credentials_test.go b/acceptance/openstack/identity/v3/credentials_test.go index 1910c60b34..1dd7e3b5c4 100644 --- a/acceptance/openstack/identity/v3/credentials_test.go +++ b/acceptance/openstack/identity/v3/credentials_test.go @@ -9,13 +9,12 @@ import ( "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/testhelper" ) func TestCredentialsCRUD(t *testing.T) { - clients.RequireAdmin(t) - client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) @@ -90,5 +89,69 @@ func TestCredentialsCRUD(t *testing.T) { tools.PrintResource(t, updateCredential) th.AssertEquals(t, updateCredential.Blob, updateOpts.Blob) +} + +func TestCredentialsValidateS3(t *testing.T) { + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + authOptions := tokens.AuthOptions{ + Username: ao.Username, + Password: ao.Password, + DomainName: ao.DomainName, + DomainID: ao.DomainID, + // We need a scope to get the token roles list + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, + } + token, err := tokens.Create(client, &authOptions).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) + + user, err := tokens.Get(client, token.ID).ExtractUser() + th.AssertNoErr(t, err) + tools.PrintResource(t, user) + project, err := tokens.Get(client, token.ID).ExtractProject() + th.AssertNoErr(t, err) + tools.PrintResource(t, project) + + createOpts := credentials.CreateOpts{ + ProjectID: project.ID, + Type: "ec2", + UserID: user.ID, + Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", + } + + // Create a credential + credential, err := credentials.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + + // Delete a credential + defer credentials.Delete(client, credential.ID) + tools.PrintResource(t, credential) + + th.AssertEquals(t, credential.Blob, createOpts.Blob) + th.AssertEquals(t, credential.Type, createOpts.Type) + th.AssertEquals(t, credential.UserID, createOpts.UserID) + th.AssertEquals(t, credential.ProjectID, createOpts.ProjectID) + + opts := ec2tokens.AuthOptions{ + Access: "181920", + Secret: "secretKey", + // auth will fail if this is not s3 + Service: "s3", + } + + // Validate a credential + token, err = ec2tokens.ValidateS3Token(client, &opts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) } diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go index 9be866d157..f3261b9e0a 100644 --- a/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -72,6 +72,11 @@ type AuthOptions struct { BodyHash *string `json:"body_hash"` // Timestamp is a timestamp to calculate a V4 signature. Optional. Timestamp *time.Time `json:"-"` + // Token is a []byte string (encoded to base64 automatically) which was + // signed by an EC2 secret key. Used by S3 tokens for validation only. + // Token must be set with a Signature. If a Signature is not provided, + // a Token will be generated automatically along with a Signature. + Token []byte `json:"token,omitempty"` } // EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string @@ -95,17 +100,17 @@ func EC2CredentialsBuildCanonicalQueryStringV2(params map[string]string) string // EC2CredentialsBuildStringToSignV2 builds a string to sign an AWS signature // V2. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L148 -func EC2CredentialsBuildStringToSignV2(opts AuthOptions) string { +func EC2CredentialsBuildStringToSignV2(opts AuthOptions) []byte { stringToSign := strings.Join([]string{ opts.Verb, opts.Host, opts.Path, }, "\n") - return strings.Join([]string{ + return []byte(strings.Join([]string{ stringToSign, EC2CredentialsBuildCanonicalQueryStringV2(opts.Params), - }, "\n") + }, "\n")) } // EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string @@ -140,17 +145,17 @@ func EC2CredentialsBuildCanonicalHeadersV4(headers map[string]string, signedHead // EC2CredentialsBuildSignatureKeyV4 builds a HMAC 256 signature key based on // input parameters. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L169 -func EC2CredentialsBuildSignatureKeyV4(secret, date, region, service string) []byte { - kDate := sumHMAC256([]byte("AWS4"+secret), []byte(date)) +func EC2CredentialsBuildSignatureKeyV4(secret, region, service string, date time.Time) []byte { + kDate := sumHMAC256([]byte("AWS4"+secret), []byte(date.Format(EC2CredentialsDateFormatV4))) kRegion := sumHMAC256(kDate, []byte(region)) kService := sumHMAC256(kRegion, []byte(service)) return sumHMAC256(kService, []byte(EC2CredentialsAwsRequestV4)) } -// EC2CredentialsBuildSignatureV4 builds an AWS v4 signature based on input -// parameters. +// EC2CredentialsBuildStringToSignV4 builds an AWS v4 signature string to sign +// based on input parameters. // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L251 -func EC2CredentialsBuildSignatureV4(opts AuthOptions, signedHeaders string, date time.Time, bodyHash string) string { +func EC2CredentialsBuildStringToSignV4(opts AuthOptions, signedHeaders string, bodyHash string, date time.Time) []byte { scope := strings.Join([]string{ date.Format(EC2CredentialsDateFormatV4), opts.Region, @@ -168,16 +173,33 @@ func EC2CredentialsBuildSignatureV4(opts AuthOptions, signedHeaders string, date }, "\n") hash := sha256.Sum256([]byte(canonicalRequest)) - strToSign := strings.Join([]string{ + return []byte(strings.Join([]string{ EC2CredentialsAwsHmacV4, date.Format(EC2CredentialsTimestampFormatV4), scope, hex.EncodeToString(hash[:]), - }, "\n") + }, "\n")) +} - key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, date.Format(EC2CredentialsDateFormatV4), opts.Region, opts.Service) +// EC2CredentialsBuildSignatureV4 builds an AWS v4 signature based on input +// parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L285..L286 +func EC2CredentialsBuildSignatureV4(key []byte, stringToSign []byte) string { + return hex.EncodeToString(sumHMAC256(key, stringToSign)) +} - return hex.EncodeToString(sumHMAC256(key, []byte(strToSign))) +// EC2CredentialsBuildAuthorizationHeaderV4 builds an AWS v4 Authorization +// header based on auth parameters, date and signature +func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders string, signature string, date time.Time) string { + return fmt.Sprintf("%s Credential=%s/%s/%s/%s/%s, SignedHeaders=%s, Signature=%s", + EC2CredentialsAwsHmacV4, + opts.Access, + date.Format(EC2CredentialsDateFormatV4), + opts.Region, + opts.Service, + EC2CredentialsAwsRequestV4, + signedHeaders, + signature) } // ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder interface @@ -221,10 +243,10 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] case EC2CredentialsHmacSha1V2: // keystone uses this method only when HmacSHA256 is not available on the server side // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L151..L156 - c["signature"] = sumHMAC1([]byte(opts.Secret), []byte(strToSign)) + c["signature"] = sumHMAC1([]byte(opts.Secret), strToSign) return b, nil case EC2CredentialsHmacSha256V2: - c["signature"] = sumHMAC256([]byte(opts.Secret), []byte(strToSign)) + c["signature"] = sumHMAC256([]byte(opts.Secret), strToSign) return b, nil } return nil, fmt.Errorf("unsupported signature method: %s", v) @@ -245,18 +267,15 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] } signedHeaders, _ := h["X-Amz-SignedHeaders"] - c["signature"] = EC2CredentialsBuildSignatureV4(*opts, signedHeaders, date, c["body_hash"].(string)) + stringToSign := EC2CredentialsBuildStringToSignV4(*opts, signedHeaders, c["body_hash"].(string), date) + key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, opts.Region, opts.Service, date) + c["signature"] = EC2CredentialsBuildSignatureV4(key, stringToSign) h["X-Amz-Date"] = date.Format(EC2CredentialsTimestampFormatV4) - h["Authorization"] = fmt.Sprintf("%s Credential=%s/%s/%s/%s/%s, SignedHeaders=%s, Signature=%s", - EC2CredentialsAwsHmacV4, - opts.Access, - date.Format(EC2CredentialsDateFormatV4), - opts.Region, - opts.Service, - EC2CredentialsAwsRequestV4, - signedHeaders, - c["signature"]) + h["Authorization"] = EC2CredentialsBuildAuthorizationHeaderV4(*opts, signedHeaders, c["signature"].(string), date) + + // token is only used for S3 tokens validation and will be removed when using EC2 validation + c["token"] = stringToSign return b, nil } @@ -269,6 +288,9 @@ func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tok return } + // delete "token" element, since it is used in s3tokens + deleteBodyElements(b, "token") + resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, @@ -280,6 +302,29 @@ 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) { + b, err := opts.ToTokenV3CreateMap(nil) + if err != nil { + r.Err = err + return + } + + // delete unused element, since it is used in ec2tokens only + deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb") + + resp, err := c.Post(s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"X-Auth-Token": ""}, + OkCodes: []int{200}, + }) + r.Err = err + if resp != nil { + r.Header = resp.Header + } + return +} + // The following are small helper functions used to help build the signature. // sumHMAC1 is a func to implement the HMAC SHA1 signature method. @@ -318,3 +363,14 @@ func interfaceToMap(c map[string]interface{}, key string) map[string]string { return m } + +// deleteBodyElements deletes map body elements +func deleteBodyElements(b map[string]interface{}, elements ...string) { + if c, ok := b["credentials"].(map[string]interface{}); ok { + for _, k := range elements { + if _, ok := c[k]; ok { + delete(c, k) + } + } + } +} diff --git a/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go b/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go index 4dc7014df8..b4e1e0c657 100644 --- a/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go +++ b/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go @@ -236,8 +236,8 @@ func TestEC2CredentialsBuildStringToSignV2(t *testing.T) { "Value": "bar", }, } - expected := "GET\nlocalhost\n/\nAction=foo&Value=bar" - testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildStringToSignV2(opts)) + expected := []byte("GET\nlocalhost\n/\nAction=foo&Value=bar") + testhelper.CheckDeepEquals(t, expected, ec2tokens.EC2CredentialsBuildStringToSignV2(opts)) } func TestEC2CredentialsBuildCanonicalQueryStringV4(t *testing.T) { @@ -261,8 +261,8 @@ func TestEC2CredentialsBuildCanonicalHeadersV4(t *testing.T) { } func TestEC2CredentialsBuildSignatureKeyV4(t *testing.T) { - expected := "5f06633a42b1324477cb006b24b1266722703b5dcf9186481be6592b9554c38f" - testhelper.CheckEquals(t, expected, hex.EncodeToString((ec2tokens.EC2CredentialsBuildSignatureKeyV4("foo", "bar", "baz", "qux")))) + expected := "246626bd815b0a0cae4bedc3f4e124ca25e208cd75fd812d836aeae184de038a" + testhelper.CheckEquals(t, expected, hex.EncodeToString((ec2tokens.EC2CredentialsBuildSignatureKeyV4("foo", "bar", "baz", time.Time{})))) } func TestEC2CredentialsBuildSignatureV4(t *testing.T) { @@ -278,5 +278,10 @@ func TestEC2CredentialsBuildSignatureV4(t *testing.T) { }, } expected := "6a5febe41427bf601f0ae7c34dbb0fd67094776138b03fb8e65783d733d302a5" - testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildSignatureV4(opts, "host", time.Time{}, "foo")) + + date := time.Time{} + stringToSign := ec2tokens.EC2CredentialsBuildStringToSignV4(opts, "host", "foo", date) + key := ec2tokens.EC2CredentialsBuildSignatureKeyV4("", "", "", date) + + testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildSignatureV4(key, stringToSign)) } diff --git a/openstack/identity/v3/extensions/ec2tokens/urls.go b/openstack/identity/v3/extensions/ec2tokens/urls.go index 0d512d1892..84b33b282e 100644 --- a/openstack/identity/v3/extensions/ec2tokens/urls.go +++ b/openstack/identity/v3/extensions/ec2tokens/urls.go @@ -5,3 +5,7 @@ import "github.com/gophercloud/gophercloud" func ec2tokensURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("ec2tokens") } + +func s3tokensURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("s3tokens") +} From c912f30f8c34f12b5d2d2466313e6cacf632e8cf Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 28 Mar 2020 15:59:31 -0600 Subject: [PATCH 1034/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44cf51ac26..f0296f51b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ IMPROVEMENTS * 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) ## 0.9.0 (March 10, 2020) From 8337c28fbd8654e7ba5df6ea7484a2b7f48b9ee7 Mon Sep 17 00:00:00 2001 From: Feilong Wang Date: Sun, 29 Mar 2020 11:00:41 +1300 Subject: [PATCH 1035/2296] Support health_status and health_status_reason attributes (#1910) --- .../containerinfra/v1/clusters/results.go | 60 ++++++++-------- .../v1/clusters/testing/fixtures.go | 70 +++++++++++-------- 2 files changed, 70 insertions(+), 60 deletions(-) diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 9fb05d203f..7e4d2efa4a 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -69,35 +69,37 @@ func (r ResizeResult) Extract() (string, error) { } type Cluster struct { - APIAddress string `json:"api_address"` - COEVersion string `json:"coe_version"` - ClusterTemplateID string `json:"cluster_template_id"` - ContainerVersion string `json:"container_version"` - CreateTimeout int `json:"create_timeout"` - CreatedAt time.Time `json:"created_at"` - DiscoveryURL string `json:"discovery_url"` - DockerVolumeSize int `json:"docker_volume_size"` - Faults map[string]string `json:"faults"` - FlavorID string `json:"flavor_id"` - KeyPair string `json:"keypair"` - Labels map[string]string `json:"labels"` - Links []gophercloud.Link `json:"links"` - MasterFlavorID string `json:"master_flavor_id"` - MasterAddresses []string `json:"master_addresses"` - MasterCount int `json:"master_count"` - Name string `json:"name"` - NodeAddresses []string `json:"node_addresses"` - NodeCount int `json:"node_count"` - ProjectID string `json:"project_id"` - StackID string `json:"stack_id"` - Status string `json:"status"` - StatusReason string `json:"status_reason"` - UUID string `json:"uuid"` - UpdatedAt time.Time `json:"updated_at"` - UserID string `json:"user_id"` - FloatingIPEnabled bool `json:"floating_ip_enabled"` - FixedNetwork string `json:"fixed_network"` - FixedSubnet string `json:"fixed_subnet"` + APIAddress string `json:"api_address"` + COEVersion string `json:"coe_version"` + ClusterTemplateID string `json:"cluster_template_id"` + ContainerVersion string `json:"container_version"` + CreateTimeout int `json:"create_timeout"` + CreatedAt time.Time `json:"created_at"` + DiscoveryURL string `json:"discovery_url"` + DockerVolumeSize int `json:"docker_volume_size"` + Faults map[string]string `json:"faults"` + FlavorID string `json:"flavor_id"` + KeyPair string `json:"keypair"` + Labels map[string]string `json:"labels"` + Links []gophercloud.Link `json:"links"` + MasterFlavorID string `json:"master_flavor_id"` + MasterAddresses []string `json:"master_addresses"` + MasterCount int `json:"master_count"` + Name string `json:"name"` + NodeAddresses []string `json:"node_addresses"` + NodeCount int `json:"node_count"` + ProjectID string `json:"project_id"` + StackID string `json:"stack_id"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + UUID string `json:"uuid"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` + FloatingIPEnabled bool `json:"floating_ip_enabled"` + FixedNetwork string `json:"fixed_network"` + FixedSubnet string `json:"fixed_subnet"` + HealthStatus string `json:"health_status"` + HealthStatusReason string `json:"health_status_reason"` } type ClusterPage struct { diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index 5369503c78..c6a30793ae 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -38,20 +38,22 @@ var ExpectedCluster = clusters.Cluster{ Rel: "bookmark", }, }, - KeyPair: "my-keypair", - MasterAddresses: []string{"172.24.4.6"}, - MasterCount: 1, - Name: "k8s", - NodeAddresses: []string{"172.24.4.13"}, - NodeCount: 1, - StackID: "9c6f1169-7300-4d08-a444-d2be38758719", - Status: "CREATE_COMPLETE", - StatusReason: "Stack CREATE completed successfully", - UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), - UUID: clusterUUID, - FloatingIPEnabled: true, - FixedNetwork: "private_network", - FixedSubnet: "private_subnet", + KeyPair: "my-keypair", + MasterAddresses: []string{"172.24.4.6"}, + MasterCount: 1, + Name: "k8s", + NodeAddresses: []string{"172.24.4.13"}, + NodeCount: 1, + StackID: "9c6f1169-7300-4d08-a444-d2be38758719", + Status: "CREATE_COMPLETE", + StatusReason: "Stack CREATE completed successfully", + UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), + UUID: clusterUUID, + FloatingIPEnabled: true, + FixedNetwork: "private_network", + FixedSubnet: "private_subnet", + HealthStatus: "HEALTHY", + HealthStatusReason: "", } var ExpectedCluster2 = clusters.Cluster{ @@ -71,20 +73,22 @@ var ExpectedCluster2 = clusters.Cluster{ Rel: "bookmark", }, }, - KeyPair: "my-keypair", - MasterAddresses: []string{"172.24.4.6"}, - MasterCount: 1, - Name: "k8s", - NodeAddresses: []string{"172.24.4.13"}, - NodeCount: 1, - StackID: "9c6f1169-7300-4d08-a444-d2be38758719", - Status: "CREATE_COMPLETE", - StatusReason: "Stack CREATE completed successfully", - UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), - UUID: clusterUUID2, - FloatingIPEnabled: true, - FixedNetwork: "private_network", - FixedSubnet: "private_subnet", + KeyPair: "my-keypair", + MasterAddresses: []string{"172.24.4.6"}, + MasterCount: 1, + Name: "k8s", + NodeAddresses: []string{"172.24.4.13"}, + NodeCount: 1, + StackID: "9c6f1169-7300-4d08-a444-d2be38758719", + Status: "CREATE_COMPLETE", + StatusReason: "Stack CREATE completed successfully", + UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), + UUID: clusterUUID2, + FloatingIPEnabled: true, + FixedNetwork: "private_network", + FixedSubnet: "private_subnet", + HealthStatus: "HEALTHY", + HealthStatusReason: "", } var ExpectedClusterUUID = clusterUUID @@ -149,7 +153,9 @@ var ClusterGetResponse = fmt.Sprintf(` "name":"k8s", "floating_ip_enabled": true, "fixed_network": "private_network", - "fixed_subnet": "private_subnet" + "fixed_subnet": "private_subnet", + "health_status": "HEALTHY", + "health_status_reason": "" }`, clusterUUID) var ClusterListResponse = fmt.Sprintf(` @@ -189,7 +195,8 @@ var ClusterListResponse = fmt.Sprintf(` "uuid":"%s", "floating_ip_enabled": true, "fixed_network": "private_network", - "fixed_subnet": "private_subnet" + "fixed_subnet": "private_subnet", + "health_status": "HEALTHY" }, { "api_address":"https://172.24.4.6:6443", @@ -225,7 +232,8 @@ var ClusterListResponse = fmt.Sprintf(` "uuid":"%s", "floating_ip_enabled": true, "fixed_network": "private_network", - "fixed_subnet": "private_subnet" + "fixed_subnet": "private_subnet", + "health_status": "HEALTHY" } ] }`, clusterUUID, clusterUUID2) From 729998bda48bf789c2c64b5e70885456444641da Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 28 Mar 2020 16:01:30 -0600 Subject: [PATCH 1036/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0296f51b2..68c4c1dd8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ IMPROVEMENTS * 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) ## 0.9.0 (March 10, 2020) From e2a7b19a530b918a62543ad86c86aed656050854 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Mon, 30 Mar 2020 08:51:34 +1300 Subject: [PATCH 1037/2296] Octavia: Failover amphora (#1912) --- openstack/loadbalancer/v2/amphorae/doc.go | 9 +++++++++ openstack/loadbalancer/v2/amphorae/requests.go | 8 ++++++++ openstack/loadbalancer/v2/amphorae/results.go | 6 ++++++ openstack/loadbalancer/v2/amphorae/testing/fixtures.go | 10 ++++++++++ .../loadbalancer/v2/amphorae/testing/requests_test.go | 9 +++++++++ openstack/loadbalancer/v2/amphorae/urls.go | 5 +++++ 6 files changed, 47 insertions(+) diff --git a/openstack/loadbalancer/v2/amphorae/doc.go b/openstack/loadbalancer/v2/amphorae/doc.go index 61b0b8b6b2..edce77cd39 100644 --- a/openstack/loadbalancer/v2/amphorae/doc.go +++ b/openstack/loadbalancer/v2/amphorae/doc.go @@ -21,5 +21,14 @@ Example to List Amphorae for _, amphora := range allAmphorae { fmt.Printf("%+v\n", amphora) } + +Example to Failover an amphora + + ampID := "d67d56a6-4a86-4688-a282-f46444705c64" + + err := amphorae.Failover(octaviaClient, ampID).ExtractErr() + if err != nil { + panic(err) + } */ package amphorae diff --git a/openstack/loadbalancer/v2/amphorae/requests.go b/openstack/loadbalancer/v2/amphorae/requests.go index 557d052fb6..7e5377453f 100644 --- a/openstack/loadbalancer/v2/amphorae/requests.go +++ b/openstack/loadbalancer/v2/amphorae/requests.go @@ -57,3 +57,11 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) return } + +// Failover performs a failover of an amphora. +func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { + _, r.Err = c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + return +} diff --git a/openstack/loadbalancer/v2/amphorae/results.go b/openstack/loadbalancer/v2/amphorae/results.go index 9560a53df1..2082a8f40d 100644 --- a/openstack/loadbalancer/v2/amphorae/results.go +++ b/openstack/loadbalancer/v2/amphorae/results.go @@ -146,3 +146,9 @@ func (r commonResult) Extract() (*Amphora, error) { type GetResult struct { commonResult } + +// FailoverResult represents the result of a failover operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type FailoverResult struct { + gophercloud.ErrResult +} diff --git a/openstack/loadbalancer/v2/amphorae/testing/fixtures.go b/openstack/loadbalancer/v2/amphorae/testing/fixtures.go index 45744cf6bb..60d13c35ea 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/fixtures.go +++ b/openstack/loadbalancer/v2/amphorae/testing/fixtures.go @@ -167,3 +167,13 @@ func HandleAmphoraGetSuccessfully(t *testing.T) { fmt.Fprintf(w, SingleAmphoraBody) }) } + +// HandleAmphoraFailoverSuccessfully sets up the test server to respond to an amphora failover request. +func HandleAmphoraFailoverSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v2.0/octavia/amphorae/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/failover", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go index bc50042586..5ade025c72 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go +++ b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go @@ -63,3 +63,12 @@ func TestGetAmphora(t *testing.T) { th.CheckDeepEquals(t, FirstAmphora, *actual) } + +func TestFailoverAmphora(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAmphoraFailoverSuccessfully(t) + + res := amphorae.Failover(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/loadbalancer/v2/amphorae/urls.go b/openstack/loadbalancer/v2/amphorae/urls.go index 2f9fb7e8fb..bab8dfdbe2 100644 --- a/openstack/loadbalancer/v2/amphorae/urls.go +++ b/openstack/loadbalancer/v2/amphorae/urls.go @@ -5,6 +5,7 @@ import "github.com/gophercloud/gophercloud" const ( rootPath = "octavia" resourcePath = "amphorae" + failoverPath = "failover" ) func rootURL(c *gophercloud.ServiceClient) string { @@ -14,3 +15,7 @@ func rootURL(c *gophercloud.ServiceClient) string { func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(rootPath, resourcePath, id) } + +func failoverRootURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id, failoverPath) +} From e2dceb1b9e0b3d2ec714f59139bb7ce7de822bd7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 29 Mar 2020 13:52:36 -0600 Subject: [PATCH 1038/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68c4c1dd8b..4c585458fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ IMPROVEMENTS * 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) ## 0.9.0 (March 10, 2020) From 08898e51b47477cb85e5ff0b9b462d54628657e3 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 2 Apr 2020 21:24:32 +0200 Subject: [PATCH 1039/2296] Keystone V3: Support EC2 credentials CRD (#1916) * Keystone V3: Support EC2 credentials CRD * add doc.go --- .../identity/v3/ec2credentials_test.go | 96 +++++++++++ .../v3/extensions/ec2credentials/doc.go | 21 +++ .../v3/extensions/ec2credentials/requests.go | 50 ++++++ .../v3/extensions/ec2credentials/results.go | 88 ++++++++++ .../ec2credentials/testing/fixtures.go | 162 ++++++++++++++++++ .../ec2credentials/testing/requests_test.go | 75 ++++++++ .../v3/extensions/ec2credentials/urls.go | 19 ++ 7 files changed, 511 insertions(+) create mode 100644 acceptance/openstack/identity/v3/ec2credentials_test.go create mode 100644 openstack/identity/v3/extensions/ec2credentials/doc.go create mode 100644 openstack/identity/v3/extensions/ec2credentials/requests.go create mode 100644 openstack/identity/v3/extensions/ec2credentials/results.go create mode 100644 openstack/identity/v3/extensions/ec2credentials/testing/fixtures.go create mode 100644 openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go create mode 100644 openstack/identity/v3/extensions/ec2credentials/urls.go diff --git a/acceptance/openstack/identity/v3/ec2credentials_test.go b/acceptance/openstack/identity/v3/ec2credentials_test.go new file mode 100644 index 0000000000..382af904f1 --- /dev/null +++ b/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -0,0 +1,96 @@ +// +build acceptance + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestEC2CredentialsCRD(t *testing.T) { + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + authOptions := tokens.AuthOptions{ + Username: ao.Username, + Password: ao.Password, + DomainName: ao.DomainName, + DomainID: ao.DomainID, + // We need a scope to get the token roles list + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, + } + + res := tokens.Create(client, &authOptions) + th.AssertNoErr(t, res.Err) + token, err := res.Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) + + user, err := res.ExtractUser() + th.AssertNoErr(t, err) + tools.PrintResource(t, user) + + project, err := res.ExtractProject() + th.AssertNoErr(t, err) + tools.PrintResource(t, project) + + createOpts := ec2credentials.CreateOpts{ + TenantID: project.ID, + } + + ec2credential, err := ec2credentials.Create(client, user.ID, createOpts).Extract() + th.AssertNoErr(t, err) + defer ec2credentials.Delete(client, user.ID, ec2credential.Access) + tools.PrintResource(t, ec2credential) + + access := ec2credential.Access + secret := ec2credential.Secret + if access == "" { + t.Fatalf("EC2 credential access was not generated") + } + + if secret == "" { + t.Fatalf("EC2 credential secret was not generated") + } + + th.AssertEquals(t, ec2credential.UserID, user.ID) + th.AssertEquals(t, ec2credential.TenantID, project.ID) + + // Get an ec2 credential + getEC2Credential, err := ec2credentials.Get(client, user.ID, ec2credential.Access).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getEC2Credential) + + th.AssertEquals(t, getEC2Credential.UserID, user.ID) + th.AssertEquals(t, getEC2Credential.TenantID, project.ID) + th.AssertEquals(t, getEC2Credential.Access, access) + th.AssertEquals(t, getEC2Credential.Secret, secret) + + allPages, err := ec2credentials.List(client, user.ID).AllPages() + th.AssertNoErr(t, err) + credentials, err := ec2credentials.ExtractCredentials(allPages) + th.AssertNoErr(t, err) + + if v := len(credentials); v != 1 { + t.Fatalf("expected to list one credential, got %d", v) + } + + th.AssertEquals(t, credentials[0].UserID, user.ID) + th.AssertEquals(t, credentials[0].TenantID, project.ID) + th.AssertEquals(t, credentials[0].Access, access) + th.AssertEquals(t, credentials[0].Secret, secret) +} diff --git a/openstack/identity/v3/extensions/ec2credentials/doc.go b/openstack/identity/v3/extensions/ec2credentials/doc.go new file mode 100644 index 0000000000..d20ff95b05 --- /dev/null +++ b/openstack/identity/v3/extensions/ec2credentials/doc.go @@ -0,0 +1,21 @@ +/* +Package ec2credentials provides information and interaction with the EC2 +credentials API resource for the OpenStack Identity service. + +For more information, see: +https://docs.openstack.org/api-ref/identity/v2-ext/ + +Example to Create an EC2 credential + + createOpts := ec2credentials.CreateOpts{ + // project ID of the EC2 credential scope + TenantID: projectID, + } + + credential, err := ec2credentials.Create(identityClient, userID, createOpts).Extract() + if err != nil { + panic(err) + } + +*/ +package ec2credentials diff --git a/openstack/identity/v3/extensions/ec2credentials/requests.go b/openstack/identity/v3/extensions/ec2credentials/requests.go new file mode 100644 index 0000000000..86fe41cf3d --- /dev/null +++ b/openstack/identity/v3/extensions/ec2credentials/requests.go @@ -0,0 +1,50 @@ +package ec2credentials + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List enumerates the Credentials to which the current token has access. +func List(client *gophercloud.ServiceClient, userID string) pagination.Pager { + url := listURL(client, userID) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return CredentialPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves details on a single EC2 credential by ID. +func Get(client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, userID, id), &r.Body, nil) + return +} + +// CreateOpts provides options used to create an EC2 credential. +type CreateOpts struct { + // TenantID is the project ID scope of the EC2 credential. + TenantID string `json:"tenant_id" required:"true"` +} + +// ToCredentialCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToCredentialCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create creates a new EC2 Credential. +func Create(client *gophercloud.ServiceClient, userID string, opts CreateOpts) (r CreateResult) { + b, err := opts.ToCredentialCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + return +} + +// Delete deletes an EC2 credential. +func Delete(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, userID, id), nil) + return +} diff --git a/openstack/identity/v3/extensions/ec2credentials/results.go b/openstack/identity/v3/extensions/ec2credentials/results.go new file mode 100644 index 0000000000..829bb1e8f8 --- /dev/null +++ b/openstack/identity/v3/extensions/ec2credentials/results.go @@ -0,0 +1,88 @@ +package ec2credentials + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Credential represents the application credential object +type Credential struct { + // UserID contains a User ID of the EC2 credential owner. + UserID string `json:"user_id"` + // TenantID contains an EC2 credential project scope. + TenantID string `json:"tenant_id"` + // Access contains an EC2 credential access UUID. + Access string `json:"access"` + // Secret contains an EC2 credential secret UUID. + Secret string `json:"secret"` + // TrustID contains an EC2 credential trust ID scope. + TrustID string `json:"trust_id"` + // Links contains referencing links to the application credential. + Links map[string]interface{} `json:"links"` +} + +type credentialResult struct { + gophercloud.Result +} + +// GetResult is the response from a Get operation. Call its Extract method +// to interpret it as an Credential. +type GetResult struct { + credentialResult +} + +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as an Credential. +type CreateResult struct { + credentialResult +} + +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// an CredentialPage is a single page of an Credential results. +type CredentialPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an CredentialPage contains any results. +func (r CredentialPage) IsEmpty() (bool, error) { + ec2Credentials, err := ExtractCredentials(r) + return len(ec2Credentials) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r CredentialPage) 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 +} + +// Extractan Credentials returns a slice of Credentials contained in a single page of results. +func ExtractCredentials(r pagination.Page) ([]Credential, error) { + var s struct { + Credentials []Credential `json:"credentials"` + } + err := (r.(CredentialPage)).ExtractInto(&s) + return s.Credentials, err +} + +// Extract interprets any Credential results as a Credential. +func (r credentialResult) Extract() (*Credential, error) { + var s struct { + Credential *Credential `json:"credential"` + } + err := r.ExtractInto(&s) + return s.Credential, err +} diff --git a/openstack/identity/v3/extensions/ec2credentials/testing/fixtures.go b/openstack/identity/v3/extensions/ec2credentials/testing/fixtures.go new file mode 100644 index 0000000000..49aea0a331 --- /dev/null +++ b/openstack/identity/v3/extensions/ec2credentials/testing/fixtures.go @@ -0,0 +1,162 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const userID = "2844b2a08be147a08ef58317d6471f1f" +const credentialID = "f741662395b249c9b8acdebf1722c5ae" + +// ListOutput provides a single page of EC2Credential results. +const ListOutput = ` +{ + "credentials": [ + { + "user_id": "2844b2a08be147a08ef58317d6471f1f", + "links": { + "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae" + }, + "tenant_id": "6238dee2fec940a6bf31e49e9faf995a", + "access": "f741662395b249c9b8acdebf1722c5ae", + "secret": "6a61eb0296034c89b49cc51dde9b40aa", + "trust_id": null + }, + { + "user_id": "2844b2a08be147a08ef58317d6471f1f", + "links": { + "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/ad6fc85fc2df49e6b5c23d5b5bdff980" + }, + "tenant_id": "6238dee2fec940a6bf31e49e9faf995a", + "access": "ad6fc85fc2df49e6b5c23d5b5bdff980", + "secret": "eb233f680a204097ac329ebe8dba6d32", + "trust_id": null + } + ], + "links": { + "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2", + "previous": null, + "next": null + } +} +` + +// GetOutput provides a Get result. +const GetOutput = ` +{ + "credential": { + "user_id": "2844b2a08be147a08ef58317d6471f1f", + "links": { + "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae" + }, + "tenant_id": "6238dee2fec940a6bf31e49e9faf995a", + "access": "f741662395b249c9b8acdebf1722c5ae", + "secret": "6a61eb0296034c89b49cc51dde9b40aa", + "trust_id": null + } +} +` + +// CreateRequest provides the input to a Create request. +const CreateRequest = ` +{ + "tenant_id": "6238dee2fec940a6bf31e49e9faf995a" +} +` + +const CreateResponse = ` +{ + "credential": { + "user_id": "2844b2a08be147a08ef58317d6471f1f", + "links": { + "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae" + }, + "tenant_id": "6238dee2fec940a6bf31e49e9faf995a", + "access": "f741662395b249c9b8acdebf1722c5ae", + "secret": "6a61eb0296034c89b49cc51dde9b40aa", + "trust_id": null + } +} +` + +var EC2Credential = ec2credentials.Credential{ + UserID: "2844b2a08be147a08ef58317d6471f1f", + TenantID: "6238dee2fec940a6bf31e49e9faf995a", + Access: "f741662395b249c9b8acdebf1722c5ae", + Secret: "6a61eb0296034c89b49cc51dde9b40aa", + TrustID: "", + Links: map[string]interface{}{ + "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae", + }, +} + +var SecondEC2Credential = ec2credentials.Credential{ + UserID: "2844b2a08be147a08ef58317d6471f1f", + TenantID: "6238dee2fec940a6bf31e49e9faf995a", + Access: "ad6fc85fc2df49e6b5c23d5b5bdff980", + Secret: "eb233f680a204097ac329ebe8dba6d32", + TrustID: "", + Links: map[string]interface{}{ + "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/ad6fc85fc2df49e6b5c23d5b5bdff980", + }, +} + +// ExpectedEC2CredentialsSlice is the slice of application credentials expected to be returned from ListOutput. +var ExpectedEC2CredentialsSlice = []ec2credentials.Credential{EC2Credential, SecondEC2Credential} + +// HandleListEC2CredentialsSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that responds with a list of two applicationcredentials. +func HandleListEC2CredentialsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2", 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) + }) +} + +// HandleGetEC2CredentialSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that responds with a single application credential. +func HandleGetEC2CredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae", 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) + }) +} + +// HandleCreateEC2CredentialSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that tests application credential creation. +func HandleCreateEC2CredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2", 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, CreateResponse) + }) +} + +// HandleDeleteEC2CredentialSuccessfully creates an HTTP handler at `/users` on the +// test handler mux that tests application credential deletion. +func HandleDeleteEC2CredentialSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae", 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/ec2credentials/testing/requests_test.go b/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go new file mode 100644 index 0000000000..3ad394ef8a --- /dev/null +++ b/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go @@ -0,0 +1,75 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListEC2Credentials(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListEC2CredentialsSuccessfully(t) + + count := 0 + err := ec2credentials.List(client.ServiceClient(), userID).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := ec2credentials.ExtractCredentials(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedEC2CredentialsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListEC2CredentialsAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListEC2CredentialsSuccessfully(t) + + allPages, err := ec2credentials.List(client.ServiceClient(), userID).AllPages() + th.AssertNoErr(t, err) + actual, err := ec2credentials.ExtractCredentials(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedEC2CredentialsSlice, actual) +} + +func TestGetEC2Credential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetEC2CredentialSuccessfully(t) + + actual, err := ec2credentials.Get(client.ServiceClient(), userID, credentialID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, EC2Credential, *actual) +} + +func TestCreateEC2Credential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateEC2CredentialSuccessfully(t) + + createOpts := ec2credentials.CreateOpts{ + TenantID: "6238dee2fec940a6bf31e49e9faf995a", + } + + actual, err := ec2credentials.Create(client.ServiceClient(), userID, createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, EC2Credential, *actual) +} + +func TestDeleteEC2Credential(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteEC2CredentialSuccessfully(t) + + res := ec2credentials.Delete(client.ServiceClient(), userID, credentialID) + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/identity/v3/extensions/ec2credentials/urls.go b/openstack/identity/v3/extensions/ec2credentials/urls.go new file mode 100644 index 0000000000..11f69d32e5 --- /dev/null +++ b/openstack/identity/v3/extensions/ec2credentials/urls.go @@ -0,0 +1,19 @@ +package ec2credentials + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "credentials", "OS-EC2") +} + +func getURL(client *gophercloud.ServiceClient, userID string, id string) string { + return client.ServiceURL("users", userID, "credentials", "OS-EC2", id) +} + +func createURL(client *gophercloud.ServiceClient, userID string) string { + return client.ServiceURL("users", userID, "credentials", "OS-EC2") +} + +func deleteURL(client *gophercloud.ServiceClient, userID string, id string) string { + return client.ServiceURL("users", userID, "credentials", "OS-EC2", id) +} From fd02b6d459f29e79f4ea591310b706c00394f5e9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 2 Apr 2020 13:25:30 -0600 Subject: [PATCH 1040/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c585458fb..eca1f2504c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ IMPROVEMENTS * 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) ## 0.9.0 (March 10, 2020) From 8cdd7a53750a7fdf03ace267468b2f712cfcb85f Mon Sep 17 00:00:00 2001 From: Andrei Nistor Date: Fri, 3 Apr 2020 04:57:45 +0300 Subject: [PATCH 1041/2296] Fix orchestration template parsing (#1915) * Fix orchestration template parsing When get_file is used in a template and the referenced file is not json/yaml, gophercloud throws a cryptic error: "Data in neither json nor yaml format." * Fix template validation * Remove TE.Validate() --- .../orchestration/v1/orchestration.go | 32 +++++++++++ .../orchestration/v1/stacktemplates_test.go | 14 +++++ .../orchestration/v1/testdata/samplefile | 1 + openstack/orchestration/v1/stacks/template.go | 10 ++-- .../orchestration/v1/stacks/template_test.go | 54 ++++++++++++++++++- openstack/orchestration/v1/stacks/utils.go | 8 ++- 6 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 acceptance/openstack/orchestration/v1/testdata/samplefile diff --git a/acceptance/openstack/orchestration/v1/orchestration.go b/acceptance/openstack/orchestration/v1/orchestration.go index 6981537931..b26b9a8c19 100644 --- a/acceptance/openstack/orchestration/v1/orchestration.go +++ b/acceptance/openstack/orchestration/v1/orchestration.go @@ -113,3 +113,35 @@ func WaitForStackStatus(client *gophercloud.ServiceClient, stackName, stackID, s return false, nil }) } + +// CreateStackWithFile will create a heat stack with a randomly generated name that uses get_file. +// An error will be returned if the stack failed to be created. +func CreateStackWithFile(t *testing.T, client *gophercloud.ServiceClient) (*stacks.RetrievedStack, error) { + stackName := tools.RandomString("ACCPTEST", 8) + t.Logf("Attempting to create stack %s", stackName) + + template := new(stacks.Template) + template.Bin = []byte(`heat_template_version: 2015-04-30 +resources: + test_resource: + type: OS::Heat::TestResource + properties: + value: + get_file: testdata/samplefile`) + createOpts := stacks.CreateOpts{ + Name: stackName, + Timeout: 1, + TemplateOpts: template, + DisableRollback: gophercloud.Disabled, + } + + stack, err := stacks.Create(client, createOpts).Extract() + th.AssertNoErr(t, err) + + if err := WaitForStackStatus(client, stackName, stack.ID, "CREATE_COMPLETE"); err != nil { + return nil, err + } + + newStack, err := stacks.Get(client, stackName, stack.ID).Extract() + return newStack, err +} diff --git a/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/acceptance/openstack/orchestration/v1/stacktemplates_test.go index 0db96e2eb9..42797ef8b4 100644 --- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -40,3 +40,17 @@ func TestStackTemplatesValidate(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, validatedTemplate) } + +func TestStackTemplateWithFile(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + client, err := clients.NewOrchestrationV1Client() + th.AssertNoErr(t, err) + + stack, err := CreateStackWithFile(t, client) + th.AssertNoErr(t, err) + defer DeleteStack(t, client, stack.Name, stack.ID) + + tmpl, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, tmpl) +} diff --git a/acceptance/openstack/orchestration/v1/testdata/samplefile b/acceptance/openstack/orchestration/v1/testdata/samplefile new file mode 100644 index 0000000000..8737b04b15 --- /dev/null +++ b/acceptance/openstack/orchestration/v1/testdata/samplefile @@ -0,0 +1 @@ +@this is not valid yaml \ No newline at end of file diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index 4cf5aae41a..90f205a1f2 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -85,7 +85,7 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool childTemplate.client = t.client // fetch the contents of the child template - if err := childTemplate.Parse(); err != nil { + if err := childTemplate.Fetch(); err != nil { return err } @@ -93,8 +93,12 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool // required if the child template itself contains references to // other templates if recurse { - if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil { - return err + if err := childTemplate.Parse(); err == nil { + if err := childTemplate.Validate(); err == nil { + if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil { + return err + } + } } } // update parent template with current child templates' content. diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go index 1ad9fc9c71..62419e5d72 100644 --- a/openstack/orchestration/v1/stacks/template_test.go +++ b/openstack/orchestration/v1/stacks/template_test.go @@ -73,7 +73,7 @@ func TestIgnoreIfTemplate(t *testing.T) { } } -func TestGetFileContents(t *testing.T) { +func TestGetFileContentsWithType(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() baseurl, err := getBasePath() @@ -146,3 +146,55 @@ resources: te.Parse() th.AssertDeepEquals(t, expectedParsed, te.Parsed) } + +func TestGetFileContentsWithFile(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + baseurl, err := getBasePath() + th.AssertNoErr(t, err) + fakeURL := strings.Join([]string{baseurl, "somefile"}, "/") + urlparsed, err := url.Parse(fakeURL) + th.AssertNoErr(t, err) + somefile := `Welcome!` + th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, somefile) + }) + + client := fakeClient{BaseClient: getHTTPClient()} + te := new(Template) + te.Bin = []byte(`heat_template_version: 2015-04-30 +resources: + test_resource: + type: OS::Heat::TestResource + properties: + value: {get_file: somefile }`) + te.client = client + + err = te.Parse() + th.AssertNoErr(t, err) + err = te.getFileContents(te.Parsed, ignoreIfTemplate, true) + th.AssertNoErr(t, err) + expectedFiles := map[string]string{ + "somefile": "Welcome!", + } + th.AssertEquals(t, expectedFiles["somefile"], te.Files[fakeURL]) + te.fixFileRefs() + expectedParsed := map[string]interface{}{ + "heat_template_version": "2015-04-30", + "resources": map[string]interface{}{ + "test_resource": map[string]interface{}{ + "type": "OS::Heat::TestResource", + "properties": map[string]interface{}{ + "value": map[string]interface{}{ + "get_file": fakeURL, + }, + }, + }, + }, + } + te.Parse() + th.AssertDeepEquals(t, expectedParsed, te.Parsed) +} diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go index 8f8d4cc4c0..bf0831376d 100644 --- a/openstack/orchestration/v1/stacks/utils.go +++ b/openstack/orchestration/v1/stacks/utils.go @@ -81,6 +81,9 @@ func (t *TE) Fetch() error { if err != nil { return err } + if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { + return fmt.Errorf("error fetching %s: %s", t.URL, resp.Status) + } t.Bin = body return nil } @@ -116,11 +119,6 @@ func (t *TE) Parse() error { return ErrInvalidDataFormat{} } } - return t.Validate() -} - -// Validate validates the contents of TE -func (t *TE) Validate() error { return nil } From b2889ee946f5da229ca8b27a7776013313d846a8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 2 Apr 2020 19:58:55 -0600 Subject: [PATCH 1042/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eca1f2504c..8471dc695f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ IMPROVEMENTS * 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) +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 From 29b4a4a73a81a05d6f7b90ee000bf1fab54c9f39 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Fri, 3 Apr 2020 02:00:31 +0000 Subject: [PATCH 1043/2296] include response headers in ErrUnexpectedResponseCode (#1919) Possible usecases include: - including the X-Openstack-Request-Id when logging the error message - respecting the Retry-After header when backing off after a 429 response Solves #1918. --- errors.go | 12 +++++++----- provider_client.go | 11 ++++++----- testing/errors_test.go | 11 ++++++----- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/errors.go b/errors.go index eb899a4a08..77cabf6a92 100644 --- a/errors.go +++ b/errors.go @@ -2,6 +2,7 @@ package gophercloud import ( "fmt" + "net/http" "strings" ) @@ -77,11 +78,12 @@ func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string { // those listed in OkCodes is encountered. type ErrUnexpectedResponseCode struct { BaseError - URL string - Method string - Expected []int - Actual int - Body []byte + URL string + Method string + Expected []int + Actual int + Body []byte + ResponseHeader http.Header } func (e ErrUnexpectedResponseCode) Error() string { diff --git a/provider_client.go b/provider_client.go index 885bf07a7b..9490c00f9d 100644 --- a/provider_client.go +++ b/provider_client.go @@ -414,11 +414,12 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() respErr := ErrUnexpectedResponseCode{ - URL: url, - Method: method, - Expected: options.OkCodes, - Actual: resp.StatusCode, - Body: body, + URL: url, + Method: method, + Expected: options.OkCodes, + Actual: resp.StatusCode, + Body: body, + ResponseHeader: resp.Header, } errType := options.ErrorContext diff --git a/testing/errors_test.go b/testing/errors_test.go index 4a42790af8..ca655839e6 100644 --- a/testing/errors_test.go +++ b/testing/errors_test.go @@ -9,11 +9,12 @@ import ( func TestGetResponseCode(t *testing.T) { respErr := gophercloud.ErrUnexpectedResponseCode{ - URL: "http://example.com", - Method: "GET", - Expected: []int{200}, - Actual: 404, - Body: nil, + URL: "http://example.com", + Method: "GET", + Expected: []int{200}, + Actual: 404, + Body: nil, + ResponseHeader: nil, } var err404 error = gophercloud.ErrDefault404{ErrUnexpectedResponseCode: respErr} From bd50b94ab72e356970a46335a0c490b4289e742b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 2 Apr 2020 20:01:12 -0600 Subject: [PATCH 1044/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8471dc695f..299d2cc985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ IMPROVEMENTS * 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) BUG FIXES From d9b765a398b2dfb9513846d244f5626e089b5ac6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 3 Apr 2020 23:48:13 -0600 Subject: [PATCH 1045/2296] Fix subjectTokenHeaders Signature (#1923) The subjectTokenHeaders function accepts a ServiceClient, though one is never used in the function. --- openstack/identity/v3/tokens/requests.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index 52f8b4d727..c6b4006d70 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -97,7 +97,7 @@ func (opts *AuthOptions) CanReauth() bool { return opts.AllowReauth } -func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { +func subjectTokenHeaders(subjectToken string) map[string]string { return map[string]string{ "X-Subject-Token": subjectToken, } @@ -131,7 +131,7 @@ func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResu // Get validates and retrieves information about another token. func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), + MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 203}, }) if resp != nil { @@ -144,7 +144,7 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { // Validate determines if a specified token is valid or not. func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), + MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 204, 404}, }) if err != nil { @@ -157,7 +157,7 @@ func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { // Revoke immediately makes specified token invalid. func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { _, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), + MoreHeaders: subjectTokenHeaders(token), }) return } From c6ddf00cb211a62826b720bce0a089d030b9c9b7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 3 Apr 2020 23:49:59 -0600 Subject: [PATCH 1046/2296] Change password fixture (#1925) --- .../v3/tokens/testing/requests_test.go | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index af88c9450c..92b9fe98f0 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -145,7 +145,7 @@ func TestCreateTokenID(t *testing.T) { } func TestCreateProjectIDScope(t *testing.T) { - options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} + options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{ProjectID: "123456"} authTokenPost(t, options, scope, ` { @@ -154,8 +154,8 @@ func TestCreateProjectIDScope(t *testing.T) { "methods": ["password"], "password": { "user": { - "id": "fenris", - "password": "g0t0h311" + "id": "someuser", + "password": "somepassword" } } }, @@ -170,7 +170,7 @@ func TestCreateProjectIDScope(t *testing.T) { } func TestCreateDomainIDScope(t *testing.T) { - options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} + options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{DomainID: "1000"} authTokenPost(t, options, scope, ` { @@ -179,8 +179,8 @@ func TestCreateDomainIDScope(t *testing.T) { "methods": ["password"], "password": { "user": { - "id": "fenris", - "password": "g0t0h311" + "id": "someuser", + "password": "somepassword" } } }, @@ -195,7 +195,7 @@ func TestCreateDomainIDScope(t *testing.T) { } func TestCreateDomainNameScope(t *testing.T) { - options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} + options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{DomainName: "evil-plans"} authTokenPost(t, options, scope, ` { @@ -204,8 +204,8 @@ func TestCreateDomainNameScope(t *testing.T) { "methods": ["password"], "password": { "user": { - "id": "fenris", - "password": "g0t0h311" + "id": "someuser", + "password": "somepassword" } } }, @@ -220,7 +220,7 @@ func TestCreateDomainNameScope(t *testing.T) { } func TestCreateProjectNameAndDomainIDScope(t *testing.T) { - options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} + options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{ProjectName: "world-domination", DomainID: "1000"} authTokenPost(t, options, scope, ` { @@ -229,8 +229,8 @@ func TestCreateProjectNameAndDomainIDScope(t *testing.T) { "methods": ["password"], "password": { "user": { - "id": "fenris", - "password": "g0t0h311" + "id": "someuser", + "password": "somepassword" } } }, @@ -248,7 +248,7 @@ func TestCreateProjectNameAndDomainIDScope(t *testing.T) { } func TestCreateProjectNameAndDomainNameScope(t *testing.T) { - options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} + options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} authTokenPost(t, options, scope, ` { @@ -257,8 +257,8 @@ func TestCreateProjectNameAndDomainNameScope(t *testing.T) { "methods": ["password"], "password": { "user": { - "id": "fenris", - "password": "g0t0h311" + "id": "someuser", + "password": "somepassword" } } }, @@ -276,7 +276,7 @@ func TestCreateProjectNameAndDomainNameScope(t *testing.T) { } func TestCreateSystemScope(t *testing.T) { - options := tokens.AuthOptions{UserID: "fenris", Password: "g0t0h311"} + options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{System: true} authTokenPost(t, options, scope, ` { @@ -285,8 +285,8 @@ func TestCreateSystemScope(t *testing.T) { "methods": ["password"], "password": { "user": { - "id": "fenris", - "password": "g0t0h311" + "id": "someuser", + "password": "somepassword" } } }, @@ -319,7 +319,7 @@ func TestCreateApplicationCredentialIDAndSecret(t *testing.T) { } func TestCreateApplicationCredentialNameAndSecret(t *testing.T) { - authTokenPost(t, tokens.AuthOptions{ApplicationCredentialName: "myappcred", ApplicationCredentialSecret: "mysecret", Username: "fenris", DomainName: "evil-plans"}, nil, ` + authTokenPost(t, tokens.AuthOptions{ApplicationCredentialName: "myappcred", ApplicationCredentialSecret: "mysecret", Username: "someuser", DomainName: "evil-plans"}, nil, ` { "auth": { "identity": { @@ -327,7 +327,7 @@ func TestCreateApplicationCredentialNameAndSecret(t *testing.T) { "name": "myappcred", "secret": "mysecret", "user": { - "name": "fenris", + "name": "someuser", "domain": { "name": "evil-plans" } From 602bb2e43a00f8f362522d0a7568e46a7ef15cfc Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 6 Apr 2020 04:12:32 +0200 Subject: [PATCH 1047/2296] Keystone V3: Introduce TOTP (two factor) auth support (#1922) * Keystone V3: Introduce TOTP (two factor) auth support * Code review --- auth_options.go | 92 +++++++++++++++---- openstack/auth_env.go | 5 +- openstack/identity/v3/tokens/requests.go | 9 ++ .../v3/tokens/testing/requests_test.go | 62 +++++++++++++ 4 files changed, 150 insertions(+), 18 deletions(-) diff --git a/auth_options.go b/auth_options.go index 6185076055..f13cdd2f62 100644 --- a/auth_options.go +++ b/auth_options.go @@ -45,6 +45,9 @@ type AuthOptions struct { Password string `json:"password,omitempty"` + // Passcode is used in TOTP authentication method + Passcode string `json:"passcode,omitempty"` + // At most one of DomainID and DomainName must be provided if using Username // with Identity V3. Otherwise, either are optional. DomainID string `json:"-"` @@ -149,7 +152,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s type userReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` - Password string `json:"password,omitempty"` + Password *string `json:"password,omitempty"` + Passcode *string `json:"passcode,omitempty"` Domain *domainReq `json:"domain,omitempty"` } @@ -168,11 +172,16 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s Secret *string `json:"secret,omitempty"` } + type totpReq struct { + User *userReq `json:"user,omitempty"` + } + type identityReq struct { Methods []string `json:"methods"` Password *passwordReq `json:"password,omitempty"` Token *tokenReq `json:"token,omitempty"` ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` + TOTP *totpReq `json:"totp,omitempty"` } type authReq struct { @@ -187,7 +196,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // if insufficient or incompatible information is present. var req request - if opts.Password == "" { + if opts.Password == "" && opts.Passcode == "" { if opts.TokenID != "" { // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication // parameters. @@ -275,7 +284,14 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } } else { // Password authentication. - req.Auth.Identity.Methods = []string{"password"} + if opts.Password != "" { + req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "password") + } + + // TOTP authentication. + if opts.Passcode != "" { + req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "totp") + } // At least one of Username and UserID must be specified. if opts.Username == "" && opts.UserID == "" { @@ -299,23 +315,46 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } // Configure the request for Username and Password authentication with a DomainID. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ - Name: &opts.Username, - Password: opts.Password, - Domain: &domainReq{ID: &opts.DomainID}, - }, + if opts.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + Name: &opts.Username, + Password: &opts.Password, + Domain: &domainReq{ID: &opts.DomainID}, + }, + } + } + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + Name: &opts.Username, + Passcode: &opts.Passcode, + Domain: &domainReq{ID: &opts.DomainID}, + }, + } } } if opts.DomainName != "" { // Configure the request for Username and Password authentication with a DomainName. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ - Name: &opts.Username, - Password: opts.Password, - Domain: &domainReq{Name: &opts.DomainName}, - }, + if opts.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + Name: &opts.Username, + Password: &opts.Password, + Domain: &domainReq{Name: &opts.DomainName}, + }, + } + } + + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + Name: &opts.Username, + Passcode: &opts.Passcode, + Domain: &domainReq{Name: &opts.DomainName}, + }, + } } } } @@ -330,8 +369,22 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } // Configure the request for UserID and Password authentication. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ID: &opts.UserID, Password: opts.Password}, + if opts.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + ID: &opts.UserID, + Password: &opts.Password, + }, + } + } + + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + ID: &opts.UserID, + Passcode: &opts.Passcode, + }, + } } } } @@ -442,5 +495,10 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } func (opts AuthOptions) CanReauth() bool { + if opts.Passcode != "" { + // cannot reauth using TOTP passcode + return false + } + return opts.AllowReauth } diff --git a/openstack/auth_env.go b/openstack/auth_env.go index 0e8d90ff82..c801de5553 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -38,6 +38,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { username := os.Getenv("OS_USERNAME") userID := os.Getenv("OS_USERID") password := os.Getenv("OS_PASSWORD") + passcode := os.Getenv("OS_PASSCODE") tenantID := os.Getenv("OS_TENANT_ID") tenantName := os.Getenv("OS_TENANT_NAME") domainID := os.Getenv("OS_DOMAIN_ID") @@ -73,8 +74,9 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { } } - if password == "" && applicationCredentialID == "" && applicationCredentialName == "" { + if password == "" && passcode == "" && applicationCredentialID == "" && applicationCredentialName == "" { err := gophercloud.ErrMissingEnvironmentVariable{ + // silently ignore TOTP passcode warning, since it is not a common auth method EnvironmentVariable: "OS_PASSWORD", } return nilOptions, err @@ -112,6 +114,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { UserID: userID, Username: username, Password: password, + Passcode: passcode, TenantID: tenantID, TenantName: tenantName, DomainID: domainID, diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index c6b4006d70..72c15bf00a 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -37,6 +37,9 @@ type AuthOptions struct { Password string `json:"password,omitempty"` + // Passcode is used in TOTP authentication method + Passcode string `json:"passcode,omitempty"` + // At most one of DomainID and DomainName must be provided if using Username // with Identity V3. Otherwise, either are optional. DomainID string `json:"-"` @@ -68,6 +71,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s Username: opts.Username, UserID: opts.UserID, Password: opts.Password, + Passcode: opts.Passcode, DomainID: opts.DomainID, DomainName: opts.DomainName, AllowReauth: opts.AllowReauth, @@ -94,6 +98,11 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } func (opts *AuthOptions) CanReauth() bool { + if opts.Passcode != "" { + // cannot reauth using TOTP passcode + return false + } + return opts.AllowReauth } diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index 92b9fe98f0..e8583a99c7 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -342,6 +342,68 @@ func TestCreateApplicationCredentialNameAndSecret(t *testing.T) { `) } +func TestCreateTOTPProjectNameAndDomainNameScope(t *testing.T) { + options := tokens.AuthOptions{UserID: "someuser", Passcode: "12345678"} + scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} + authTokenPost(t, options, scope, ` + { + "auth": { + "identity": { + "methods": ["totp"], + "totp": { + "user": { + "id": "someuser", + "passcode": "12345678" + } + } + }, + "scope": { + "project": { + "domain": { + "name": "evil-plans" + }, + "name": "world-domination" + } + } + } + } + `) +} + +func TestCreatePasswordTOTPProjectNameAndDomainNameScope(t *testing.T) { + options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword", Passcode: "12345678"} + scope := &tokens.Scope{ProjectName: "world-domination", DomainName: "evil-plans"} + authTokenPost(t, options, scope, ` + { + "auth": { + "identity": { + "methods": ["password","totp"], + "password": { + "user": { + "id": "someuser", + "password": "somepassword" + } + }, + "totp": { + "user": { + "id": "someuser", + "passcode": "12345678" + } + } + }, + "scope": { + "project": { + "domain": { + "name": "evil-plans" + }, + "name": "world-domination" + } + } + } + } + `) +} + func TestCreateExtractsTokenFromResponse(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() From 41239d7cba900437ce094408df1f74e0a2074f70 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 5 Apr 2020 20:13:28 -0600 Subject: [PATCH 1048/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 299d2cc985..4e4bcf9094 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ IMPROVEMENTS * 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) BUG FIXES From 17ba7e9f69baa6dc5369a450cd8c58f2746a2a92 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 11 Apr 2020 00:04:38 +0200 Subject: [PATCH 1049/2296] Manila V2: Add get export location support (#1932) --- .../sharedfilesystems/v2/shares_test.go | 37 +++++++++++++++++++ .../sharedfilesystems/v2/shares/requests.go | 15 ++++++-- .../sharedfilesystems/v2/shares/results.go | 25 ++++++++++--- .../v2/shares/testing/fixtures.go | 29 +++++++++++++-- .../v2/shares/testing/request_test.go | 32 +++++++++++++--- openstack/sharedfilesystems/v2/shares/urls.go | 6 ++- 6 files changed, 125 insertions(+), 19 deletions(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 8a7ea999fb..11932744c1 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -32,6 +32,43 @@ func TestShareCreate(t *testing.T) { tools.PrintResource(t, created) } +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) + } + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + err = waitForStatus(t, client, share.ID, "available", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + client.Microversion = "2.9" + + exportLocations, err := shares.ListExportLocations(client, share.ID).Extract() + if err != nil { + t.Errorf("Unable to list share export locations: %v", err) + } + tools.PrintResource(t, exportLocations) + + exportLocation, err := shares.GetExportLocation(client, share.ID, exportLocations[0].ID).Extract() + if err != nil { + t.Errorf("Unable to get share export location: %v", err) + } + tools.PrintResource(t, exportLocation) + th.AssertEquals(t, exportLocations[0], *exportLocation) +} + func TestShareUpdate(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 568d35a71d..d15a6937cc 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -174,10 +174,17 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// GetExportLocations will get shareID's export locations. -// Client must have Microversion set; minimum supported microversion for GetExportLocations is 2.14. -func GetExportLocations(client *gophercloud.ServiceClient, id string) (r GetExportLocationsResult) { - _, r.Err = client.Get(getExportLocationsURL(client, id), &r.Body, nil) +// ListExportLocations will list shareID's export locations. +// Client must have Microversion set; minimum supported microversion for ListExportLocations is 2.9. +func ListExportLocations(client *gophercloud.ServiceClient, id string) (r ListExportLocationsResult) { + _, r.Err = client.Get(listExportLocationsURL(client, id), &r.Body, nil) + return +} + +// GetExportLocation will get shareID's export location by an ID. +// Client must have Microversion set; minimum supported microversion for GetExportLocation is 2.9. +func GetExportLocation(client *gophercloud.ServiceClient, shareID string, id string) (r GetExportLocationResult) { + _, r.Err = client.Get(getExportLocationURL(client, shareID, id), &r.Body, nil) return } diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 4a7d0e11a5..12115a39cd 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -207,9 +207,15 @@ type UpdateResult struct { commonResult } -// GetExportLocationsResult contains the result body and error from an -// GetExportLocations request. -type GetExportLocationsResult struct { +// 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 } @@ -232,8 +238,8 @@ type ExportLocation struct { Preferred bool `json:"preferred"` } -// Extract will get the Export Locations from the commonResult -func (r GetExportLocationsResult) Extract() ([]ExportLocation, error) { +// Extract will get the Export Locations from the ListExportLocationsResult +func (r ListExportLocationsResult) Extract() ([]ExportLocation, error) { var s struct { ExportLocations []ExportLocation `json:"export_locations"` } @@ -241,6 +247,15 @@ func (r GetExportLocationsResult) Extract() ([]ExportLocation, error) { 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 +} + // AccessRight contains all information associated with an OpenStack share // Grant Access Response type AccessRight struct { diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 17d1f47fed..0a47ad0b7f 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -273,7 +273,7 @@ func MockListDetailResponse(t *testing.T) { }) } -var getExportLocationsResponse = `{ +var listExportLocationsResponse = `{ "export_locations": [ { "path": "127.0.0.1:/var/lib/manila/mnt/share-9a922036-ad26-4d27-b955-7a1e285fa74d", @@ -285,14 +285,35 @@ var getExportLocationsResponse = `{ ] }` -// MockGetExportLocationsResponse creates a mock get export locations response -func MockGetExportLocationsResponse(t *testing.T) { +// MockListExportLocationsResponse creates a mock get export locations response +func MockListExportLocationsResponse(t *testing.T) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, getExportLocationsResponse) + fmt.Fprintf(w, listExportLocationsResponse) + }) +} + +var getExportLocationResponse = `{ + "export_location": { + "path": "127.0.0.1:/var/lib/manila/mnt/share-9a922036-ad26-4d27-b955-7a1e285fa74d", + "share_instance_id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", + "is_admin_only": false, + "id": "80ed63fc-83bc-4afc-b881-da4a345ac83d", + "preferred": false + } +}` + +// MockGetExportLocationResponse creates a mock get export location response +func MockGetExportLocationResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/export_locations/80ed63fc-83bc-4afc-b881-da4a345ac83d", 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, getExportLocationResponse) }) } diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index 06bc9a187e..6fdee3efb0 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -161,17 +161,17 @@ func TestListDetail(t *testing.T) { }) } -func TestGetExportLocationsSuccess(t *testing.T) { +func TestListExportLocationsSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - MockGetExportLocationsResponse(t) + MockListExportLocationsResponse(t) c := client.ServiceClient() - // Client c must have Microversion set; minimum supported microversion for Get Export Locations is 2.14 - c.Microversion = "2.14" + // Client c must have Microversion set; minimum supported microversion for List Export Locations is 2.9 + c.Microversion = "2.9" - s, err := shares.GetExportLocations(c, shareID).Extract() + s, err := shares.ListExportLocations(c, shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, []shares.ExportLocation{ @@ -185,6 +185,28 @@ func TestGetExportLocationsSuccess(t *testing.T) { }) } +func TestGetExportLocationSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetExportLocationResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Get Export Location is 2.9 + c.Microversion = "2.9" + + s, err := shares.GetExportLocation(c, shareID, "80ed63fc-83bc-4afc-b881-da4a345ac83d").Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, s, &shares.ExportLocation{ + Path: "127.0.0.1:/var/lib/manila/mnt/share-9a922036-ad26-4d27-b955-7a1e285fa74d", + ShareInstanceID: "011d21e2-fbc3-4e4a-9993-9ea223f73264", + IsAdminOnly: false, + ID: "80ed63fc-83bc-4afc-b881-da4a345ac83d", + Preferred: false, + }) +} + func TestGrantAcessSuccess(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index f9a99c83bf..099f4a4e63 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -22,10 +22,14 @@ func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id) } -func getExportLocationsURL(c *gophercloud.ServiceClient, id string) string { +func listExportLocationsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "export_locations") } +func getExportLocationURL(c *gophercloud.ServiceClient, shareID, id string) string { + return c.ServiceURL("shares", shareID, "export_locations", id) +} + func grantAccessURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } From 198dfc0b477b40d6927d0f4cafc1456c3bb1032b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 10 Apr 2020 16:06:24 -0600 Subject: [PATCH 1050/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e4bcf9094..1515782e6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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 @@ -24,6 +25,8 @@ IMPROVEMENTS * 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) BUG FIXES From b976854e3de897082c53e99f110fcbe8b51d7ee4 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 11 Apr 2020 21:55:03 +0200 Subject: [PATCH 1051/2296] Manila V2: Introduce share actions (#1931) * Manila V2: Support for Revert to Snapshot action * Manila V2: Support for Share status reset action * Manila V2: Support for Share force delete action * Manila V2: Support for Share unmanage action * Manila V2: Add new share actions docs --- .../openstack/sharedfilesystems/v2/shares.go | 3 + .../sharedfilesystems/v2/shares_test.go | 161 ++++++++++++++++++ openstack/sharedfilesystems/v2/shares/doc.go | 45 +++++ .../sharedfilesystems/v2/shares/requests.go | 104 +++++++++++ .../sharedfilesystems/v2/shares/results.go | 20 +++ .../v2/shares/testing/fixtures.go | 72 ++++++++ .../v2/shares/testing/request_test.go | 56 ++++++ openstack/sharedfilesystems/v2/shares/urls.go | 16 ++ 8 files changed, 477 insertions(+) create mode 100644 openstack/sharedfilesystems/v2/shares/doc.go diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index dee6abfe95..ea4639b13c 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -85,6 +85,9 @@ func GetAccessRightsSlice(t *testing.T, client *gophercloud.ServiceClient, share func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) { err := shares.Delete(client, share.ID).ExtractErr() if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + return + } t.Errorf("Unable to delete share %s: %v", share.ID, err) } diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 11932744c1..8833c25b2f 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -323,3 +323,164 @@ func TestShareMetadata(t *testing.T) { t.Fatalf("Unexpected metadata contents %v, expected an empty map", metadata) } } + +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) + } + client.Microversion = "2.27" + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + err = waitForStatus(t, client, share.ID, "available", 120) + 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", 120) + if err != nil { + t.Fatalf("Snapshot status error: %v", err) + } + + revertOpts := &shares.RevertOpts{ + SnapshotID: snapshot.ID, + } + err = shares.Revert(client, share.ID, revertOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to revert a snapshot: %v", err) + } + + // We need to wait till the Extend operation is done + err = waitForStatus(t, client, share.ID, "available", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + t.Logf("Share %s successfuly reverted", share.ID) +} + +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) + } + client.Microversion = "2.7" + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + err = waitForStatus(t, client, share.ID, "available", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + resetStatusOpts := &shares.ResetStatusOpts{ + Status: "error", + } + err = shares.ResetStatus(client, share.ID, resetStatusOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to reset a share status: %v", err) + } + + // We need to wait till the Extend operation is done + err = waitForStatus(t, client, share.ID, "error", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + t.Logf("Share %s status successfuly reset", share.ID) +} + +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) + } + client.Microversion = "2.7" + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + err = waitForStatus(t, client, share.ID, "available", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + err = shares.ForceDelete(client, share.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to force delete a share: %v", err) + } + + err = waitForStatus(t, client, share.ID, "deleted", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + t.Logf("Share %s was successfuly deleted", share.ID) +} + +func TestUnmanage(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + clients.RequireAdmin(t) + + client, err := clients.NewSharedFileSystemV2Client() + if err != nil { + t.Fatalf("Unable to create a shared file system client: %v", err) + } + client.Microversion = "2.7" + + share, err := CreateShare(t, client) + if err != nil { + t.Fatalf("Unable to create a share: %v", err) + } + + defer DeleteShare(t, client, share) + + err = waitForStatus(t, client, share.ID, "available", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + err = shares.Unmanage(client, share.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to unmanage a share: %v", err) + } + + err = waitForStatus(t, client, share.ID, "deleted", 120) + if err != nil { + t.Fatalf("Share status error: %v", err) + } + + t.Logf("Share %s was successfuly unmanaged", share.ID) +} diff --git a/openstack/sharedfilesystems/v2/shares/doc.go b/openstack/sharedfilesystems/v2/shares/doc.go new file mode 100644 index 0000000000..731dd85e2d --- /dev/null +++ b/openstack/sharedfilesystems/v2/shares/doc.go @@ -0,0 +1,45 @@ +/* +Package shares provides information and interaction with the different +API versions for the Shared File System service, code-named Manila. + +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", + } + manilaClient.Microversion = "2.27" + err := shares.Revert(manilaClient, shareID, opts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Reset a Share Status + opts := &shares.ResetStatusOpts{ + // a new Share Status + Status: "available", + } + manilaClient.Microversion = "2.7" + err := shares.ResetStatus(manilaClient, shareID, opts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Force Delete a Share + manilaClient.Microversion = "2.7" + err := shares.ForceDelete(manilaClient, shareID).ExtractErr() + if err != nil { + panic(err) + } + +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/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index d15a6937cc..692e18604e 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -483,3 +483,107 @@ func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r Delet return } + +// RevertOptsBuilder allows extensions to add additional parameters to the +// Revert request. +type RevertOptsBuilder interface { + ToShareRevertMap() (map[string]interface{}, error) +} + +// RevertOpts contains options for reverting a Share to a snapshot. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Share Actions, Revert share documentation. +// Available only since Manila Microversion 2.27 +type RevertOpts struct { + // SnapshotID is a Snapshot ID to revert a Share to + SnapshotID string `json:"snapshot_id"` +} + +// ToShareRevertMap assembles a request body based on the contents of a +// RevertOpts. +func (opts RevertOpts) ToShareRevertMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "revert") +} + +// Revert will revert the existing share to a Snapshot. RevertResult contains only the error. +// To extract it, call the ExtractErr method on the RevertResult. +// Client must have Microversion set; minimum supported microversion for Revert is 2.27. +func Revert(client *gophercloud.ServiceClient, id string, opts RevertOptsBuilder) (r RevertResult) { + b, err := opts.ToShareRevertMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(revertURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} + +// ResetStatusOptsBuilder allows extensions to add additional parameters to the +// ResetStatus request. +type ResetStatusOptsBuilder interface { + ToShareResetStatusMap() (map[string]interface{}, error) +} + +// ResetStatusOpts contains options for resetting a Share status. +// For more information about these parameters, please, refer to the shared file systems API v2, +// Share Actions, ResetStatus share documentation. +type ResetStatusOpts struct { + // Status is a share status to reset to. Must be "new", "error" or "active". + Status string `json:"status"` +} + +// ToShareResetStatusMap assembles a request body based on the contents of a +// ResetStatusOpts. +func (opts ResetStatusOpts) ToShareResetStatusMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "reset_status") +} + +// ResetStatus will reset the existing share status. ResetStatusResult contains only the error. +// To extract it, call the ExtractErr method on the ResetStatusResult. +// Client must have Microversion set; minimum supported microversion for ResetStatus is 2.7. +func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToShareResetStatusMap() + if err != nil { + r.Err = err + return + } + + _, r.Err = client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} + +// ForceDelete will delete the existing share in any state. ForceDeleteResult contains only the error. +// To extract it, call the ExtractErr method on the ForceDeleteResult. +// Client must have Microversion set; minimum supported microversion for ForceDelete is 2.7. +func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + b := map[string]interface{}{ + "force_delete": nil, + } + _, r.Err = client.Post(forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} + +// Unmanage will remove a share from the management of the Shared File System +// service without deleting the share. UnmanageResult contains only the error. +// To extract it, call the ExtractErr method on the UnmanageResult. +// Client must have Microversion set; minimum supported microversion for Unmanage is 2.7. +func Unmanage(client *gophercloud.ServiceClient, id string) (r UnmanageResult) { + b := map[string]interface{}{ + "unmanage": nil, + } + _, r.Err = client.Post(unmanageURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + + return +} diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 12115a39cd..741f4a0d64 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -350,3 +350,23 @@ func (r MetadataResult) Extract() (map[string]string, error) { type DeleteMetadatumResult struct { gophercloud.ErrResult } + +// RevertResult contains the response error from an Revert request. +type RevertResult struct { + gophercloud.ErrResult +} + +// 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 +} + +// UnmanageResult contains the response error from an Unmanage request. +type UnmanageResult struct { + gophercloud.ErrResult +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 0a47ad0b7f..7117da4104 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -542,3 +542,75 @@ func MockDeleteMetadatumResponse(t *testing.T, key string) { w.WriteHeader(http.StatusOK) }) } + +var revertRequest = `{ + "revert": { + "snapshot_id": "ddeac769-9742-497f-b985-5bcfa94a3fd6" + } + }` + +// MockRevertResponse creates a mock revert share response +func MockRevertResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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, revertRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) +} + +var resetStatusRequest = `{ + "reset_status": { + "status": "error" + } + }` + +// MockResetStatusResponse creates a mock reset status share response +func MockResetStatusResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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 share response +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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) + }) +} + +var unmanageRequest = `{ + "unmanage": null + }` + +// MockUnmanageResponse creates a mock unmanage share response +func MockUnmanageResponse(t *testing.T) { + th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/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, unmanageRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index 6fdee3efb0..91efd4fbda 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -369,3 +369,59 @@ func TestUnsetMetadataSuccess(t *testing.T) { err := shares.DeleteMetadatum(c, shareID, "foo").ExtractErr() th.AssertNoErr(t, err) } + +func TestRevertSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockRevertResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Revert is 2.27 + c.Microversion = "2.27" + + err := shares.Revert(c, shareID, &shares.RevertOpts{SnapshotID: "ddeac769-9742-497f-b985-5bcfa94a3fd6"}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestResetStatusSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResetStatusResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for ResetStatus is 2.7 + c.Microversion = "2.7" + + err := shares.ResetStatus(c, shareID, &shares.ResetStatusOpts{Status: "error"}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestForceDeleteSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockForceDeleteResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for ForceDelete is 2.7 + c.Microversion = "2.7" + + err := shares.ForceDelete(c, shareID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUnmanageSuccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUnmanageResponse(t) + + c := client.ServiceClient() + // Client c must have Microversion set; minimum supported microversion for Unmanage is 2.7 + c.Microversion = "2.7" + + err := shares.Unmanage(c, shareID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index 099f4a4e63..f5c347cbda 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -50,6 +50,22 @@ func shrinkURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "action") } +func revertURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} + +func resetStatusURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} + +func forceDeleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} + +func unmanageURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("shares", id, "action") +} + func getMetadataURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("shares", id, "metadata") } From a466a42571b425079fec91aadcce54ab21b7356f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 11 Apr 2020 13:57:08 -0600 Subject: [PATCH 1052/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1515782e6d..9eabd546e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ IMPROVEMENTS * 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) BUG FIXES From 102532ad449e33018b6e83e8dccb3883f8fd85c1 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 12 Apr 2020 23:15:47 +0200 Subject: [PATCH 1053/2296] Cinder V3: Add support for Cinders Attachment API (#1934) * Add support for Cinders Attachment API * Update attachment package, add tests Co-authored-by: j-griffith --- .../blockstorage/v3/volumeattachments.go | 110 +++++++++ .../blockstorage/v3/volumeattachments_test.go | 39 +++ openstack/blockstorage/v3/attachments/doc.go | 87 +++++++ .../blockstorage/v3/attachments/requests.go | 175 +++++++++++++ .../blockstorage/v3/attachments/results.go | 120 +++++++++ .../v3/attachments/testing/fixtures.go | 233 ++++++++++++++++++ .../v3/attachments/testing/requests_test.go | 119 +++++++++ openstack/blockstorage/v3/attachments/urls.go | 27 ++ openstack/blockstorage/v3/attachments/util.go | 22 ++ 9 files changed, 932 insertions(+) create mode 100644 acceptance/openstack/blockstorage/v3/volumeattachments.go create mode 100644 acceptance/openstack/blockstorage/v3/volumeattachments_test.go create mode 100644 openstack/blockstorage/v3/attachments/doc.go create mode 100644 openstack/blockstorage/v3/attachments/requests.go create mode 100644 openstack/blockstorage/v3/attachments/results.go create mode 100644 openstack/blockstorage/v3/attachments/testing/fixtures.go create mode 100644 openstack/blockstorage/v3/attachments/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/attachments/urls.go create mode 100644 openstack/blockstorage/v3/attachments/util.go diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments.go b/acceptance/openstack/blockstorage/v3/volumeattachments.go new file mode 100644 index 0000000000..510b6841ca --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/volumeattachments.go @@ -0,0 +1,110 @@ +package v3 + +import ( + "fmt" + "testing" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" + v3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" +) + +// CreateVolumeAttachment will attach a volume to an instance. An error will be +// returned if the attachment failed. +func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume, server *servers.Server) error { + if testing.Short() { + t.Skip("Skipping test that requires volume attachment in short mode.") + } + + 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) + + var err error + var attachment *attachments.Attachment + if attachment, err = attachments.Create(client, attachOpts).Extract(); err != nil { + return err + } + + mv := client.Microversion + client.Microversion = "3.44" + defer func() { + client.Microversion = mv + }() + if err = attachments.Complete(client, attachment.ID).ExtractErr(); err != nil { + return err + } + + if err = attachments.WaitForStatus(client, attachment.ID, "attached", 60); err != nil { + e := attachments.Delete(client, attachment.ID).ExtractErr() + if e != nil { + t.Logf("Failed to delete %q attachment: %s", attachment.ID, err) + } + return err + } + + attachment, err = attachments.Get(client, attachment.ID).Extract() + if err != nil { + 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, + } + allPages, err := attachments.List(client, listOpts).AllPages() + if err != nil { + return err + } + + allAttachments, err := attachments.ExtractAttachments(allPages) + if err != nil { + return err + } + + if allAttachments[0].ID != attachment.ID { + return fmt.Errorf("Attachment IDs from get and list are not equal: %q != %q", allAttachments[0].ID, attachment.ID) + } + + t.Logf("Attached volume %s to server %s within %q attachment", volume.ID, server.ID, attachment.ID) + + return nil +} + +// DeleteVolumeAttachment will detach a volume from an instance. A fatal error +// will occur if the attachment failed to be deleted. +func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume) { + t.Logf("Attepting to detach volume volume: %s", volume.ID) + + if err := attachments.Delete(client, volume.Attachments[0].AttachmentID).ExtractErr(); err != nil { + t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) + } + + if err := v3.WaitForStatus(client, volume.ID, "available", 60); err != nil { + t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) + } + + t.Logf("Detached volume: %s", volume.ID) +} diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go new file mode 100644 index 0000000000..a07f87eeef --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -0,0 +1,39 @@ +// +build acceptance blockstorage + +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + compute "github.com/gophercloud/gophercloud/acceptance/openstack/compute/v2" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestVolumeAttachments(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + // minimu required microversion for volume attachments is 3.27 + blockClient.Microversion = "3.27" + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := compute.CreateServer(t, computeClient) + th.AssertNoErr(t, err) + defer compute.DeleteServer(t, computeClient, server) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + err = CreateVolumeAttachment(t, blockClient, volume, server) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + th.AssertNoErr(t, err) + + DeleteVolumeAttachment(t, blockClient, newVolume) +} diff --git a/openstack/blockstorage/v3/attachments/doc.go b/openstack/blockstorage/v3/attachments/doc.go new file mode 100644 index 0000000000..4b193431c5 --- /dev/null +++ b/openstack/blockstorage/v3/attachments/doc.go @@ -0,0 +1,87 @@ +/* +Package attachments provides access to OpenStack Block Storage Attachment +API's. Use of this package requires Cinder version 3.27 at a minimum. + +For more information, see: +https://docs.openstack.org/api-ref/block-storage/v3/index.html#attachments + +Example to List Attachments + + listOpts := &attachments.ListOpts{ + InstanceID: "uuid", + } + + client.Microversion = "3.27" + allPages, err := attachments.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allAttachments, err := attachments.ExtractAttachments(allPages) + if err != nil { + panic(err) + } + + for _, attachment := range allAttachments { + fmt.Println(attachment) + } + +Example to Create Attachment + + opts := &attachments.CreateOpts{ + InstanceiUUID: "uuid", + VolumeUUID: "uuid" + } + + client.Microversion = "3.27" + attachment, err := attachments.Create(client, opts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(attachment) + +Example to Get Attachment + + client.Microversion = "3.27" + attachment, err := attachments.Get(client, "uuid").Extract() + if err != nil { + panic(err) + } + + fmt.Println(attachment) + +Example to Update Attachment + + opts := &attachments.UpdateOpts{ + Connector: map[string]interface{}{ + "mode": "ro", + } + } + + client.Microversion = "3.27" + attachment, err := attachments.Update(client, "uuid", opts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(attachment) + +Example to Complete Attachment + + client.Microversion = "3.44" + err := attachments.Complete(client, "uuid").ExtractErr() + if err != nil { + panic(err) + } + +Example to Delete Attachment + + client.Microversion = "3.27" + err := attachments.Delete(client, "uuid").ExtractErr() + if err != nil { + panic(err) + } + +*/ +package attachments diff --git a/openstack/blockstorage/v3/attachments/requests.go b/openstack/blockstorage/v3/attachments/requests.go new file mode 100644 index 0000000000..5a721cc391 --- /dev/null +++ b/openstack/blockstorage/v3/attachments/requests.go @@ -0,0 +1,175 @@ +package attachments + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToAttachmentCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Volume attachment. This object is +//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 + // record for. + VolumeUUID string `json:"volume_uuid"` + // InstanceUUID is the ID of the Server to create the attachment for. + // When attaching to a Nova Server this is the Nova Server (Instance) + // UUID. + InstanceUUID string `json:"instance_uuid"` + // Connector is an optional map containing all of the needed atachment + // information for exmaple initiator IQN, etc. + Connector map[string]interface{} `json:"connector,omitempty"` + // Mode is an attachment mode. Acceptable values are read-only ('ro') + // and read-and-write ('rw'). Available only since 3.54 microversion. + // For APIs from 3.27 till 3.53 use Connector["mode"] = "rw|ro". + Mode string `json:"mode,omitempty"` +} + +// ToAttachmentCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToAttachmentCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "attachment") +} + +// Create will create a new Attachment based on the values in CreateOpts. To +// extract the Attachment object from the response, call the Extract method on +// the CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToAttachmentCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Delete will delete the existing Attachment with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Get retrieves the Attachment with the provided ID. To extract the Attachment +// object from the response, call the Extract method on the GetResult. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToAttachmentListQuery() (string, error) +} + +// ListOpts holds options for listing Attachments. It is passed to the attachments.List +// function. +type ListOpts struct { + // AllTenants will retrieve attachments of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Status will filter by the specified status. + Status string `q:"status"` + + // ProjectID will filter by a specific tenant/project ID. + ProjectID string `q:"project_id"` + + // VolumeID will filter by a specific volume ID. + VolumeID string `q:"volume_id"` + + // InstanceID will filter by a specific instance ID. + InstanceID string `q:"instance_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToAttachmentListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToAttachmentListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Attachments 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.ToAttachmentListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AttachmentPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToAttachmentUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Attachment. +// This is used to finalize an attachment that was created without a +// connector (reserve). +type UpdateOpts struct { + Connector map[string]interface{} `json:"connector"` +} + +// ToAttachmentUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToAttachmentUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "attachment") +} + +// Update will update the Attachment with provided information. To extract the +// updated Attachment from the response, call the Extract method on the +// UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToAttachmentUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Complete will complete an attachment for a cinder volume. +// Available starting in the 3.44 microversion. +func Complete(client *gophercloud.ServiceClient, id string) (r CompleteResult) { + b := map[string]interface{}{ + "os-complete": nil, + } + _, r.Err = client.Post(completeURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + return +} diff --git a/openstack/blockstorage/v3/attachments/results.go b/openstack/blockstorage/v3/attachments/results.go new file mode 100644 index 0000000000..0a97730ed2 --- /dev/null +++ b/openstack/blockstorage/v3/attachments/results.go @@ -0,0 +1,120 @@ +// Package attachments provides access to OpenStack Block Storage Attachment +// API's. Use of this package requires Cinder version 3.27 at a minimum. +package attachments + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Attachment contains all the information associated with an OpenStack +// Attachment. +type Attachment struct { + // ID is the Unique identifier for the attachment. + ID string `json:"id"` + // VolumeID is the UUID of the Volume associated with this attachment. + VolumeID string `json:"volume_id"` + // Instance is the Instance/Server UUID associated with this attachment. + Instance string `json:"instance"` + // AttachedAt is the time the attachment was created. + AttachedAt time.Time `json:"-"` + // DetachedAt is the time the attachment was detached. + DetachedAt time.Time `json:"-"` + // Status is the current attach status. + Status string `json:"status"` + // AttachMode includes things like Read Only etc. + AttachMode string `json:"attach_mode"` + // ConnectionInfo is the required info for a node to make a connection + // provided by the driver. + ConnectionInfo map[string]interface{} `json:"connection_info"` +} + +// UnmarshalJSON is our unmarshalling helper +func (r *Attachment) UnmarshalJSON(b []byte) error { + type tmp Attachment + var s struct { + tmp + AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` + DetachedAt gophercloud.JSONRFC3339MilliNoZ `json:"detached_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Attachment(s.tmp) + + r.AttachedAt = time.Time(s.AttachedAt) + r.DetachedAt = time.Time(s.DetachedAt) + + return err +} + +// AttachmentPage is a pagination.pager that is returned from a call to the List +// function. +type AttachmentPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a ListResult contains no Attachments. +func (r AttachmentPage) IsEmpty() (bool, error) { + attachments, err := ExtractAttachments(r) + return len(attachments) == 0, err +} + +// ExtractAttachments extracts and returns Attachments. It is used while +// iterating over a attachment.List call. +func ExtractAttachments(r pagination.Page) ([]Attachment, error) { + var s []Attachment + err := ExtractAttachmentsInto(r, &s) + return s, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Attachment object out of the commonResult object. +func (r commonResult) Extract() (*Attachment, error) { + var s Attachment + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a attachment struct. +func (r commonResult) ExtractInto(a interface{}) error { + return r.Result.ExtractIntoStructPtr(a, "attachment") +} + +// ExtractAttachmentsInto similar to ExtractInto but operates on a List of +// attachments. +func ExtractAttachmentsInto(r pagination.Page, a interface{}) error { + return r.(AttachmentPage).Result.ExtractIntoSlicePtr(a, "attachments") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// GetResult contains the response body and error from a Get request. +type GetResult struct { + commonResult +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} + +// CompleteResult contains the response body and error from a Complete request. +type CompleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/attachments/testing/fixtures.go b/openstack/blockstorage/v3/attachments/testing/fixtures.go new file mode 100644 index 0000000000..b19245e57a --- /dev/null +++ b/openstack/blockstorage/v3/attachments/testing/fixtures.go @@ -0,0 +1,233 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +var ( + attachedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-09-16T09:28:52.000000") + detachedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2015-09-16T09:28:52.000000") + expectedAttachment = &attachments.Attachment{ + ID: "05551600-a936-4d4a-ba42-79a037c1-c91a", + VolumeID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Instance: "83ec2e3b-4321-422b-8706-a84185f52a0a", + AttachMode: "rw", + Status: "attaching", + AttachedAt: attachedAt, + DetachedAt: detachedAt, + ConnectionInfo: map[string]interface{}{}, + } +) + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/detail", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` + { + "attachments": [ + { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } + ], + "attachments_links": [ + { + "href": "%s/attachments/detail?marker=1", + "rel": "next" + } + ] +} + `, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"volumes": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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, ` +{ + "attachment": { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } +} + `) + }) +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments", 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, ` +{ + "attachment": { + "instance_uuid": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "connector": { + "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", + "ip": "192.168.1.20", + "platform": "x86_64", + "host": "tempest-1", + "os_type": "linux2", + "multipath": false, + "mountpoint": "/dev/vdb", + "mode": "rw" + }, + "volume_uuid": "289da7f8-6440-407c-9fb4-7db01ec49164" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "attachment": { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } +} + `) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusOK) + }) +} + +func MockUpdateResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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, ` +{ + "attachment": { + "connector": { + "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", + "ip": "192.168.1.20", + "platform": "x86_64", + "host": "tempest-1", + "os_type": "linux2", + "multipath": false, + "mountpoint": "/dev/vdb", + "mode": "rw" + } + } +} + `) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "attachment": { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } +} + `) + }) +} + +func MockUpdateEmptyResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a", 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, ` +{ + "attachment": { + "connector": null + } +} + `) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "attachment": { + "status": "attaching", + "detached_at": "2015-09-16T09:28:52.000000", + "connection_info": {}, + "attached_at": "2015-09-16T09:28:52.000000", + "attach_mode": "rw", + "instance": "83ec2e3b-4321-422b-8706-a84185f52a0a", + "volume_id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "id": "05551600-a936-4d4a-ba42-79a037c1-c91a" + } +} + `) + }) +} + +var completeRequest = ` +{ + "os-complete": null +} +` + +func MockCompleteResponse(t *testing.T) { + th.Mux.HandleFunc("/attachments/05551600-a936-4d4a-ba42-79a037c1-c91a/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, completeRequest) + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/blockstorage/v3/attachments/testing/requests_test.go b/openstack/blockstorage/v3/attachments/testing/requests_test.go new file mode 100644 index 0000000000..2c9793100c --- /dev/null +++ b/openstack/blockstorage/v3/attachments/testing/requests_test.go @@ -0,0 +1,119 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAll(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allPages, err := attachments.List(client.ServiceClient(), &attachments.ListOpts{}).AllPages() + th.AssertNoErr(t, err) + actual, err := attachments.ExtractAttachments(allPages) + th.AssertNoErr(t, err) + + expected := []attachments.Attachment{*expectedAttachment} + + th.CheckDeepEquals(t, expected, actual) + +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + attachment, err := attachments.Get(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expectedAttachment, attachment) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := &attachments.CreateOpts{ + InstanceUUID: "83ec2e3b-4321-422b-8706-a84185f52a0a", + Connector: map[string]interface{}{ + "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", + "ip": "192.168.1.20", + "platform": "x86_64", + "host": "tempest-1", + "os_type": "linux2", + "multipath": false, + "mountpoint": "/dev/vdb", + "mode": "rw", + }, + VolumeUUID: "289da7f8-6440-407c-9fb4-7db01ec49164", + } + attachment, err := attachments.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expectedAttachment, attachment) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := attachments.Delete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a") + th.AssertNoErr(t, res.Err) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + options := &attachments.UpdateOpts{ + Connector: map[string]interface{}{ + "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", + "ip": "192.168.1.20", + "platform": "x86_64", + "host": "tempest-1", + "os_type": "linux2", + "multipath": false, + "mountpoint": "/dev/vdb", + "mode": "rw", + }, + } + attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expectedAttachment, attachment) +} + +func TestUpdateEmpty(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateEmptyResponse(t) + + options := attachments.UpdateOpts{} + attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expectedAttachment, attachment) +} + +func TestComplete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCompleteResponse(t) + + err := attachments.Complete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/v3/attachments/urls.go b/openstack/blockstorage/v3/attachments/urls.go new file mode 100644 index 0000000000..a774d7772f --- /dev/null +++ b/openstack/blockstorage/v3/attachments/urls.go @@ -0,0 +1,27 @@ +package attachments + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("attachments") +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("attachments", "detail") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("attachments", id) +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("attachments", id) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("attachments", id) +} + +func completeURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("attachments", id, "action") +} diff --git a/openstack/blockstorage/v3/attachments/util.go b/openstack/blockstorage/v3/attachments/util.go new file mode 100644 index 0000000000..dca1b888ec --- /dev/null +++ b/openstack/blockstorage/v3/attachments/util.go @@ -0,0 +1,22 @@ +package attachments + +import ( + "github.com/gophercloud/gophercloud" +) + +// WaitForStatus will continually poll the resource, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := Get(c, id).Extract() + if err != nil { + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} From 00c5d8899b0e57693991a0cc2fb72bf1c4fd7151 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 12 Apr 2020 15:17:41 -0600 Subject: [PATCH 1054/2296] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eabd546e8..ee688bb7e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,12 @@ IMPROVEMENTS * 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 From 65804fe9b24146be66c170c01caee23013862d88 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 12 Apr 2020 15:18:13 -0600 Subject: [PATCH 1055/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee688bb7e6..12f472aea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.10.0 (Unreleased) +## 0.11.0 (Unreleased) + +## 0.10.0 (April 12, 2020) UPGRADE NOTES From e54703519a9c3bcf1e3c25450a02614beb583c6d Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 19 Apr 2020 03:56:16 +0200 Subject: [PATCH 1056/2296] Swift: Add "bulk-delete" URL argument support (#1930) --- .../objectstorage/v1/containers_test.go | 37 ++++++++ .../objectstorage/v1/objects_test.go | 84 +++++++++++++++++++ .../objectstorage/v1/accounts/results.go | 8 +- .../objectstorage/v1/containers/requests.go | 36 +++++++- .../objectstorage/v1/containers/results.go | 40 +++++++-- .../v1/containers/testing/fixtures.go | 29 +++++++ .../v1/containers/testing/requests_test.go | 27 ++++-- openstack/objectstorage/v1/containers/urls.go | 4 + .../objectstorage/v1/objects/requests.go | 47 +++++++++-- openstack/objectstorage/v1/objects/results.go | 46 +++++++--- .../v1/objects/testing/fixtures.go | 29 +++++++ .../v1/objects/testing/requests_test.go | 17 ++++ openstack/objectstorage/v1/objects/urls.go | 4 + testhelper/convenience.go | 8 ++ 14 files changed, 373 insertions(+), 43 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index 8a86762f06..1f6738b78b 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -161,3 +161,40 @@ func TestListAllContainers(t *testing.T) { } th.AssertEquals(t, numContainers, len(containerNamesList)) } + +func TestBulkDeleteContainers(t *testing.T) { + client, err := clients.NewObjectStorageV1Client() + if err != nil { + t.Fatalf("Unable to create client: %v", err) + } + + numContainers := 20 + + // Create a slice of random container names. + cNames := make([]string, numContainers) + for i := 0; i < numContainers; i++ { + cNames[i] = tools.RandomString("test&happy?-", 8) + } + + // Create numContainers containers. + for i := 0; i < len(cNames); i++ { + res := containers.Create(client, cNames[i], nil) + th.AssertNoErr(t, res.Err) + } + + expectedResp := containers.BulkDeleteResponse{ + ResponseStatus: "200 OK", + Errors: [][]string{}, + NumberDeleted: numContainers, + } + + resp, err := containers.BulkDelete(client, cNames).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, *resp) + th.AssertDeepEquals(t, *resp, expectedResp) + + for _, c := range cNames { + _, err = containers.Get(client, c, nil).Extract() + th.AssertErr(t, err) + } +} diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index ed3def62a3..841e670a31 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -261,3 +261,87 @@ func TestObjectsListSubdir(t *testing.T) { th.AssertEquals(t, allObjects[0], cSubdir2+"/") t.Logf("%#v\n", allObjects) } + +func TestObjectsBulkDelete(t *testing.T) { + client, err := clients.NewObjectStorageV1Client() + if err != nil { + t.Fatalf("Unable to create client: %v", err) + } + + // Create a random subdirectory name. + cSubdir1 := tools.RandomString("don't worry & be happy?-", 8) + cSubdir2 := tools.RandomString("don't worry & be happy?-", 8) + + // Make a slice of length numObjects to hold the random object names. + oNames1 := make([]string, numObjects) + for i := 0; i < len(oNames1); i++ { + oNames1[i] = cSubdir1 + "/" + tools.RandomString("stranger?things-", 8) + } + + oNames2 := make([]string, numObjects) + for i := 0; i < len(oNames2); i++ { + oNames2[i] = cSubdir2 + "/" + tools.RandomString("freddy's coming for you?-", 8) + } + + // Create a container to hold the test objects. + cName := tools.RandomString("test&happy?-", 8) + _, err = containers.Create(client, cName, nil).Extract() + th.AssertNoErr(t, err) + + // Defer deletion of the container until after testing. + defer func() { + t.Logf("Deleting container %s", cName) + res := containers.Delete(client, cName) + th.AssertNoErr(t, res.Err) + }() + + // Create a slice of buffers to hold the test object content. + oContents1 := make([]*bytes.Buffer, numObjects) + for i := 0; i < numObjects; i++ { + oContents1[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + createOpts := objects.CreateOpts{ + Content: oContents1[i], + } + res := objects.Create(client, cName, oNames1[i], createOpts) + th.AssertNoErr(t, res.Err) + } + + expectedResp := objects.BulkDeleteResponse{ + ResponseStatus: "200 OK", + Errors: [][]string{}, + NumberDeleted: numObjects * 2, + } + + oContents2 := make([]*bytes.Buffer, numObjects) + for i := 0; i < numObjects; i++ { + oContents2[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) + createOpts := objects.CreateOpts{ + Content: oContents2[i], + } + res := objects.Create(client, cName, oNames2[i], createOpts) + th.AssertNoErr(t, res.Err) + } + + // Delete the objects after testing. + resp, err := objects.BulkDelete(client, cName, append(oNames1, oNames2...)).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, *resp, expectedResp) + + // Verify deletion + listOpts := objects.ListOpts{ + Full: true, + Delimiter: "/", + } + + allPages, err := objects.List(client, cName, listOpts).AllPages() + if err != nil { + t.Fatal(err) + } + + allObjects, err := objects.ExtractNames(allPages) + if err != nil { + t.Fatal(err) + } + + th.AssertEquals(t, len(allObjects), 0) +} diff --git a/openstack/objectstorage/v1/accounts/results.go b/openstack/objectstorage/v1/accounts/results.go index 10661e6715..e25ae99abd 100644 --- a/openstack/objectstorage/v1/accounts/results.go +++ b/openstack/objectstorage/v1/accounts/results.go @@ -55,9 +55,9 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { // Extract will return a struct of headers returned from a call to Get. To // obtain a map of headers, call the Extract method on the GetResult. func (r UpdateResult) Extract() (*UpdateHeader, error) { - var s *UpdateHeader + var s UpdateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // GetHeader represents the headers returned in the response from a Get request. @@ -157,9 +157,9 @@ type GetResult struct { // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { - var s *GetHeader + var s GetHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 2e710ab0c6..4a4be32715 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -1,6 +1,9 @@ package containers import ( + "net/url" + "strings" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -104,7 +107,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", createURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) @@ -116,9 +119,34 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB return } +// BulkDelete is a function that bulk deletes containers. +func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDeleteResult) { + // urlencode container 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 + encodedContainers := make([]string, len(containers)) + for i, v := range containers { + encodedContainers[i] = url.QueryEscape(v) + } + resp, err := c.Request("POST", bulkDeleteURL(c), &gophercloud.RequestOpts{ + RawBody: strings.NewReader(strings.Join(encodedContainers, "\n") + "\n"), + MoreHeaders: map[string]string{ + "Accept": "application/json", + "Content-Type": "text/plain", + }, + OkCodes: []int{200}, + }) + if resp != nil { + r.Header = resp.Header + r.Body = resp.Body + } + r.Err = err + return +} + // Delete is a function that deletes a container. func Delete(c *gophercloud.ServiceClient, containerName string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, containerName), nil) + _, r.Err = c.Delete(deleteURL(c, url.QueryEscape(containerName)), nil) return } @@ -180,7 +208,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", updateURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) @@ -223,7 +251,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(getURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index b94eda95e1..d2a13b1d28 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -170,9 +170,9 @@ type GetResult struct { // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { - var s *GetHeader + var s GetHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) @@ -238,9 +238,9 @@ type CreateResult struct { // Extract will return a struct of headers returned from a call to Create. // To extract the headers from the HTTP response, call its Extract method. func (r CreateResult) Extract() (*CreateHeader, error) { - var s *CreateHeader + var s CreateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // UpdateHeader represents the headers returned in the response from a Update @@ -289,9 +289,9 @@ type UpdateResult struct { // Extract will return a struct of headers returned from a call to Update. func (r UpdateResult) Extract() (*UpdateHeader, error) { - var s *UpdateHeader + var s UpdateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // DeleteHeader represents the headers returned in the response from a Delete @@ -333,14 +333,36 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { } // DeleteResult represents the result of a delete operation. To extract the -// the headers from the HTTP response, call its Extract method. +// headers from the HTTP response, call its Extract method. type DeleteResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Delete. func (r DeleteResult) Extract() (*DeleteHeader, error) { - var s *DeleteHeader + var s DeleteHeader err := r.ExtractInto(&s) - return s, err + return &s, err +} + +type BulkDeleteResponse struct { + ResponseStatus string `json:"Response Status"` + ResponseBody string `json:"Response Body"` + Errors [][]string `json:"Errors"` + NumberDeleted int `json:"Number Deleted"` + NumberNotFound int `json:"Number Not Found"` +} + +// BulkDeleteResult represents the result of a bulk delete operation. To extract +// the response object from the HTTP response, call its Extract method. +type BulkDeleteResult struct { + gophercloud.Result +} + +// Extract will return a BulkDeleteResponse struct returned from a BulkDelete +// call. +func (r BulkDeleteResult) Extract() (*BulkDeleteResponse, error) { + var s BulkDeleteResponse + err := r.ExtractInto(&s) + return &s, err } diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 23169f804a..5fd0218f79 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -122,6 +122,35 @@ func HandleDeleteContainerSuccessfully(t *testing.T) { }) } +const bulkDeleteResponse = ` +{ + "Response Status": "foo", + "Response Body": "bar", + "Errors": [], + "Number Deleted": 2, + "Number Not Found": 0 +} +` + +// HandleBulkDeleteSuccessfully creates an HTTP handler at `/` on the test +// handler mux that responds with a `Delete` response. +func HandleBulkDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/", 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, "Content-Type", "text/plain") + th.TestFormValues(t, r, map[string]string{ + "bulk-delete": "true", + }) + th.TestBody(t, r, "testContainer1\ntestContainer2\n") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, bulkDeleteResponse) + }) +} + // HandleUpdateContainerSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `Update` response. func HandleUpdateContainerSuccessfully(t *testing.T) { diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index 379910106f..f18965fdf0 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -95,7 +95,7 @@ func TestCreateContainer(t *testing.T) { TransID: "tx554ed59667a64c61866f1-0058b4ba37", } actual, err := res.Extract() - th.CheckNoErr(t, err) + th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } @@ -105,7 +105,24 @@ func TestDeleteContainer(t *testing.T) { HandleDeleteContainerSuccessfully(t) res := containers.Delete(fake.ServiceClient(), "testContainer") - th.CheckNoErr(t, res.Err) + th.AssertNoErr(t, res.Err) +} + +func TestBulkDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleBulkDeleteSuccessfully(t) + + expected := containers.BulkDeleteResponse{ + ResponseStatus: "foo", + ResponseBody: "bar", + NumberDeleted: 2, + Errors: [][]string{}, + } + + resp, err := containers.BulkDelete(fake.ServiceClient(), []string{"testContainer1", "testContainer2"}).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, *resp) } func TestUpdateContainer(t *testing.T) { @@ -115,7 +132,7 @@ func TestUpdateContainer(t *testing.T) { options := &containers.UpdateOpts{Metadata: map[string]string{"foo": "bar"}} res := containers.Update(fake.ServiceClient(), "testContainer", options) - th.CheckNoErr(t, res.Err) + th.AssertNoErr(t, res.Err) } func TestGetContainer(t *testing.T) { @@ -128,7 +145,7 @@ func TestGetContainer(t *testing.T) { } res := containers.Get(fake.ServiceClient(), "testContainer", getOpts) _, err := res.ExtractMetadata() - th.CheckNoErr(t, err) + th.AssertNoErr(t, err) expected := &containers.GetHeader{ AcceptRanges: "bytes", @@ -142,6 +159,6 @@ func TestGetContainer(t *testing.T) { StoragePolicy: "test_policy", } actual, err := res.Extract() - th.CheckNoErr(t, err) + th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } diff --git a/openstack/objectstorage/v1/containers/urls.go b/openstack/objectstorage/v1/containers/urls.go index 9b380470dd..0044a5e206 100644 --- a/openstack/objectstorage/v1/containers/urls.go +++ b/openstack/objectstorage/v1/containers/urls.go @@ -21,3 +21,7 @@ func deleteURL(c *gophercloud.ServiceClient, container string) string { func updateURL(c *gophercloud.ServiceClient, container string) string { return createURL(c, container) } + +func bulkDeleteURL(c *gophercloud.ServiceClient) string { + return c.Endpoint + "?bulk-delete=true" +} diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index a9649d97a8..f438d633ad 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "io/ioutil" + "net/url" "strings" "time" @@ -53,7 +54,7 @@ func (opts ListOpts) ToObjectListParams() (bool, string, error) { func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager { headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} - url := listURL(c, containerName) + url := listURL(c, url.QueryEscape(containerName)) if opts != nil { full, query, err := opts.ToObjectListParams() if err != nil { @@ -112,7 +113,7 @@ func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, er // To extract just the content, pass the DownloadResult response to the // ExtractContent function. func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) { - url := downloadURL(c, containerName, objectName) + url := downloadURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectDownloadParams() @@ -221,7 +222,7 @@ 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 := createURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) h := make(map[string]string) var b io.Reader if opts != nil { @@ -289,7 +290,7 @@ func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts C h[k] = v } - url := copyURL(c, containerName, objectName) + url := copyURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, @@ -320,7 +321,7 @@ 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 := deleteURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) if opts != nil { query, err := opts.ToObjectDeleteQuery() if err != nil { @@ -368,7 +369,7 @@ 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 := getURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectGetParams() @@ -437,7 +438,7 @@ func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts h[k] = v } } - url := updateURL(c, containerName, objectName) + url := updateURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) @@ -483,7 +484,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin } duration := time.Duration(opts.TTL) * time.Second expiry := time.Now().Add(duration).Unix() - getHeader, err := containers.Get(c, containerName, nil).Extract() + getHeader, err := containers.Get(c, url.QueryEscape(containerName), nil).Extract() if err != nil { return "", err } @@ -497,7 +498,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin tempURLKey = getHeader.TempURLKey } secretKey := []byte(tempURLKey) - url := getURL(c, containerName, objectName) + url := getURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) splitPath := strings.Split(url, opts.Split) baseURL, objectPath := splitPath[0], splitPath[1] objectPath = opts.Split + objectPath @@ -507,3 +508,31 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin 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 } + +// BulkDelete is a function that bulk deletes objects. +func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string) (r BulkDeleteResult) { + // 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 { + encodedObjects[i] = strings.Join([]string{ + url.QueryEscape(container), + url.QueryEscape(v)}, + "/") + } + resp, err := c.Request("POST", bulkDeleteURL(c), &gophercloud.RequestOpts{ + RawBody: strings.NewReader(strings.Join(encodedObjects, "\n") + "\n"), + MoreHeaders: map[string]string{ + "Accept": "application/json", + "Content-Type": "text/plain", + }, + OkCodes: []int{200}, + }) + if resp != nil { + r.Header = resp.Header + r.Body = resp.Body + } + r.Err = err + return +} diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index dd7c7044d0..e841bcf9e2 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -197,9 +197,9 @@ type DownloadResult struct { // Extract will return a struct of headers returned from a call to Download. func (r DownloadResult) Extract() (*DownloadHeader, error) { - var s *DownloadHeader + var s DownloadHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // ExtractContent is a function that takes a DownloadResult's io.Reader body @@ -286,9 +286,9 @@ type GetResult struct { // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { - var s *GetHeader + var s GetHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) @@ -360,9 +360,9 @@ func (r CreateResult) Extract() (*CreateHeader, error) { //if r.Header.Get("ETag") != fmt.Sprintf("%x", localChecksum) { // return nil, ErrWrongChecksum{} //} - var s *CreateHeader + var s CreateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // UpdateHeader represents the headers returned in the response from a @@ -410,9 +410,9 @@ type UpdateResult struct { // Extract will return a struct of headers returned from a call to Update. func (r UpdateResult) Extract() (*UpdateHeader, error) { - var s *UpdateHeader + var s UpdateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // DeleteHeader represents the headers returned in the response from a @@ -460,9 +460,9 @@ type DeleteResult struct { // Extract will return a struct of headers returned from a call to Delete. func (r DeleteResult) Extract() (*DeleteHeader, error) { - var s *DeleteHeader + var s DeleteHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // CopyHeader represents the headers returned in the response from a @@ -518,9 +518,31 @@ type CopyResult struct { // Extract will return a struct of headers returned from a call to Copy. func (r CopyResult) Extract() (*CopyHeader, error) { - var s *CopyHeader + var s CopyHeader err := r.ExtractInto(&s) - return s, err + return &s, err +} + +type BulkDeleteResponse struct { + ResponseStatus string `json:"Response Status"` + ResponseBody string `json:"Response Body"` + Errors [][]string `json:"Errors"` + NumberDeleted int `json:"Number Deleted"` + NumberNotFound int `json:"Number Not Found"` +} + +// BulkDeleteResult represents the result of a bulk delete operation. To extract +// the response object from the HTTP response, call its Extract method. +type BulkDeleteResult struct { + gophercloud.Result +} + +// Extract will return a BulkDeleteResponse struct returned from a BulkDelete +// call. +func (r BulkDeleteResult) Extract() (*BulkDeleteResponse, error) { + var s BulkDeleteResponse + err := r.ExtractInto(&s) + return &s, err } // extractLastMarker is a function that takes a page of objects and returns the diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 520e8b48e9..cd51fdb572 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -224,6 +224,35 @@ func HandleDeleteObjectSuccessfully(t *testing.T) { }) } +const bulkDeleteResponse = ` +{ + "Response Status": "foo", + "Response Body": "bar", + "Errors": [], + "Number Deleted": 2, + "Number Not Found": 0 +} +` + +// HandleBulkDeleteSuccessfully creates an HTTP handler at `/` on the test +// handler mux that responds with a `BulkDelete` response. +func HandleBulkDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/", 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, "Content-Type", "text/plain") + th.TestFormValues(t, r, map[string]string{ + "bulk-delete": "true", + }) + th.TestBody(t, r, "testContainer/testObject1\ntestContainer/testObject2\n") + + 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) { diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index e7ba01534e..97c789efaf 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -213,6 +213,23 @@ func TestDeleteObject(t *testing.T) { th.AssertNoErr(t, res.Err) } +func TestBulkDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleBulkDeleteSuccessfully(t) + + expected := objects.BulkDeleteResponse{ + ResponseStatus: "foo", + ResponseBody: "bar", + NumberDeleted: 2, + Errors: [][]string{}, + } + + resp, err := objects.BulkDelete(fake.ServiceClient(), "testContainer", []string{"testObject1", "testObject2"}).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, *resp) +} + func TestUpateObjectMetadata(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 b3ac304b74..918ec94b9b 100644 --- a/openstack/objectstorage/v1/objects/urls.go +++ b/openstack/objectstorage/v1/objects/urls.go @@ -31,3 +31,7 @@ func downloadURL(c *gophercloud.ServiceClient, container, object string) string func updateURL(c *gophercloud.ServiceClient, container, object string) string { return copyURL(c, container, object) } + +func bulkDeleteURL(c *gophercloud.ServiceClient) string { + return c.Endpoint + "?bulk-delete=true" +} diff --git a/testhelper/convenience.go b/testhelper/convenience.go index 25f6720e82..d1dafb3758 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -340,6 +340,14 @@ func AssertNoErr(t *testing.T, e error) { } } +// AssertErr is a convenience function for checking whether an error value is +// nil +func AssertErr(t *testing.T, e error) { + if e == nil { + logFatal(t, fmt.Sprintf("expected error, got nil")) + } +} + // CheckNoErr is similar to AssertNoErr, except with a non-fatal error func CheckNoErr(t *testing.T, e error) { if e != nil { From 53d02bcea1d57f1cb77e989c81e33f1a2df695e8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 18 Apr 2020 19:57:21 -0600 Subject: [PATCH 1057/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f472aea4..6f856e7ede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.11.0 (Unreleased) +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) + ## 0.10.0 (April 12, 2020) UPGRADE NOTES From 21650dc6da88bbf2dcf7ad3ada251809591a4206 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 19 Apr 2020 08:42:28 -0600 Subject: [PATCH 1058/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f856e7ede..f4262813d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ ## 0.11.0 (Unreleased) +UPGRADE NOTES + +* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) + 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) ## 0.10.0 (April 12, 2020) From 41c3197ccfafa597c7a059949edb152a6b9910a2 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 19 Apr 2020 17:08:53 +0200 Subject: [PATCH 1059/2296] Core: set response headers for all requests (#1942) --- .../.template/requests.go | 12 +- openstack/baremetal/apiversions/requests.go | 6 +- .../baremetal/v1/allocations/requests.go | 9 +- openstack/baremetal/v1/drivers/requests.go | 9 +- openstack/baremetal/v1/nodes/requests.go | 39 +++-- openstack/baremetal/v1/ports/requests.go | 15 +- .../v1/introspection/requests.go | 18 ++- .../extensions/backups/requests.go | 21 ++- .../extensions/quotasets/requests.go | 17 +- .../extensions/volumeactions/requests.go | 36 +++-- .../extensions/volumetransfers/requests.go | 12 +- .../blockstorage/v1/apiversions/requests.go | 3 +- .../blockstorage/v1/snapshots/requests.go | 12 +- openstack/blockstorage/v1/volumes/requests.go | 12 +- .../blockstorage/v1/volumetypes/requests.go | 9 +- .../blockstorage/v2/snapshots/requests.go | 12 +- openstack/blockstorage/v2/volumes/requests.go | 12 +- .../blockstorage/v3/attachments/requests.go | 15 +- .../blockstorage/v3/snapshots/requests.go | 12 +- openstack/blockstorage/v3/volumes/requests.go | 12 +- .../blockstorage/v3/volumetypes/requests.go | 12 +- openstack/cdn/v1/base/requests.go | 6 +- openstack/cdn/v1/flavors/requests.go | 3 +- openstack/cdn/v1/serviceassets/requests.go | 3 +- openstack/cdn/v1/services/requests.go | 12 +- openstack/clustering/v1/actions/requests.go | 3 +- openstack/clustering/v1/clusters/requests.go | 146 +++++------------- openstack/clustering/v1/events/requests.go | 3 +- openstack/clustering/v1/nodes/requests.go | 56 ++----- openstack/clustering/v1/policies/requests.go | 38 ++--- .../clustering/v1/policytypes/requests.go | 4 +- openstack/clustering/v1/profiles/requests.go | 40 ++--- .../clustering/v1/profiletypes/requests.go | 4 +- openstack/clustering/v1/receivers/requests.go | 32 ++-- openstack/clustering/v1/webhooks/requests.go | 3 +- openstack/common/extensions/requests.go | 3 +- .../v2/extensions/aggregates/requests.go | 21 ++- .../extensions/attachinterfaces/requests.go | 9 +- .../v2/extensions/bootfromvolume/requests.go | 3 +- .../v2/extensions/defsecrules/requests.go | 9 +- .../v2/extensions/diagnostics/requests.go | 3 +- .../v2/extensions/evacuate/requests.go | 3 +- .../v2/extensions/floatingips/requests.go | 15 +- .../v2/extensions/hypervisors/requests.go | 9 +- .../v2/extensions/instanceactions/request.go | 3 +- .../v2/extensions/keypairs/requests.go | 9 +- .../compute/v2/extensions/limits/requests.go | 3 +- .../v2/extensions/lockunlock/requests.go | 6 +- .../compute/v2/extensions/migrate/requests.go | 6 +- .../v2/extensions/networks/requests.go | 3 +- .../v2/extensions/pauseunpause/requests.go | 6 +- .../v2/extensions/quotasets/requests.go | 12 +- .../v2/extensions/remoteconsoles/requests.go | 3 +- .../v2/extensions/rescueunrescue/requests.go | 6 +- .../v2/extensions/resetstate/requests.go | 3 +- .../v2/extensions/secgroups/requests.go | 24 ++- .../v2/extensions/servergroups/requests.go | 9 +- .../v2/extensions/services/requests.go | 3 +- .../v2/extensions/shelveunshelve/requests.go | 9 +- .../v2/extensions/startstop/requests.go | 6 +- .../v2/extensions/suspendresume/requests.go | 6 +- .../compute/v2/extensions/tags/requests.go | 18 ++- .../v2/extensions/tenantnetworks/requests.go | 3 +- .../v2/extensions/volumeattach/requests.go | 9 +- openstack/compute/v2/flavors/requests.go | 30 ++-- openstack/compute/v2/images/requests.go | 6 +- openstack/compute/v2/servers/requests.go | 60 ++++--- openstack/container/v1/capsules/requests.go | 9 +- .../containerinfra/apiversions/requests.go | 3 +- .../v1/certificates/requests.go | 21 +-- .../containerinfra/v1/clusters/requests.go | 41 ++--- .../v1/clustertemplates/requests.go | 31 +--- .../containerinfra/v1/nodegroups/requests.go | 34 +--- .../containerinfra/v1/quotas/requests.go | 11 +- openstack/db/v1/configurations/requests.go | 21 ++- openstack/db/v1/databases/requests.go | 6 +- openstack/db/v1/datastores/requests.go | 6 +- openstack/db/v1/flavors/requests.go | 3 +- openstack/db/v1/instances/requests.go | 30 ++-- openstack/db/v1/users/requests.go | 6 +- openstack/dns/v2/recordsets/requests.go | 12 +- openstack/dns/v2/zones/requests.go | 12 +- .../v2/extensions/admin/roles/requests.go | 6 +- openstack/identity/v2/tenants/requests.go | 12 +- openstack/identity/v2/tokens/requests.go | 6 +- openstack/identity/v2/users/requests.go | 12 +- .../v3/applicationcredentials/requests.go | 15 +- openstack/identity/v3/credentials/requests.go | 12 +- openstack/identity/v3/domains/requests.go | 12 +- openstack/identity/v3/endpoints/requests.go | 9 +- .../v3/extensions/ec2credentials/requests.go | 9 +- .../v3/extensions/ec2tokens/requests.go | 10 +- .../identity/v3/extensions/trusts/requests.go | 9 +- openstack/identity/v3/groups/requests.go | 12 +- openstack/identity/v3/policies/requests.go | 12 +- openstack/identity/v3/projects/requests.go | 12 +- openstack/identity/v3/regions/requests.go | 12 +- openstack/identity/v3/roles/requests.go | 18 ++- openstack/identity/v3/services/requests.go | 12 +- openstack/identity/v3/tokens/requests.go | 13 +- openstack/identity/v3/users/requests.go | 31 ++-- .../imageservice/v2/imagedata/requests.go | 15 +- .../imageservice/v2/imageimport/requests.go | 6 +- openstack/imageservice/v2/images/requests.go | 12 +- openstack/imageservice/v2/members/requests.go | 12 +- openstack/imageservice/v2/tasks/requests.go | 6 +- openstack/keymanager/v1/acls/requests.go | 24 ++- .../keymanager/v1/containers/requests.go | 21 ++- openstack/keymanager/v1/orders/requests.go | 9 +- openstack/keymanager/v1/secrets/requests.go | 43 +++--- .../keymanager/v1/secrets/testing/fixtures.go | 1 + .../loadbalancer/v2/amphorae/requests.go | 6 +- .../loadbalancer/v2/l7policies/requests.go | 24 ++- .../loadbalancer/v2/listeners/requests.go | 15 +- .../loadbalancer/v2/loadbalancers/requests.go | 21 ++- .../loadbalancer/v2/monitors/requests.go | 12 +- openstack/loadbalancer/v2/pools/requests.go | 27 ++-- openstack/messaging/v2/claims/requests.go | 15 +- openstack/messaging/v2/messages/requests.go | 15 +- openstack/messaging/v2/queues/requests.go | 24 ++- .../v2/extensions/agents/requests.go | 6 +- .../v2/extensions/attributestags/requests.go | 18 ++- .../v2/extensions/fwaas/firewalls/requests.go | 12 +- .../v2/extensions/fwaas/policies/requests.go | 18 ++- .../v2/extensions/fwaas/rules/requests.go | 12 +- .../v2/extensions/fwaas_v2/rules/requests.go | 12 +- .../layer3/addressscopes/requests.go | 12 +- .../extensions/layer3/floatingips/requests.go | 12 +- .../layer3/portforwarding/requests.go | 12 +- .../v2/extensions/layer3/routers/requests.go | 18 ++- .../v2/extensions/lbaas/members/requests.go | 12 +- .../v2/extensions/lbaas/monitors/requests.go | 12 +- .../v2/extensions/lbaas/pools/requests.go | 18 ++- .../v2/extensions/lbaas/vips/requests.go | 12 +- .../lbaas_v2/l7policies/requests.go | 24 ++- .../extensions/lbaas_v2/listeners/requests.go | 12 +- .../lbaas_v2/loadbalancers/requests.go | 21 ++- .../extensions/lbaas_v2/monitors/requests.go | 12 +- .../v2/extensions/lbaas_v2/pools/requests.go | 24 ++- .../networkipavailabilities/requests.go | 3 +- .../v2/extensions/qos/policies/requests.go | 12 +- .../v2/extensions/qos/rules/requests.go | 36 +++-- .../v2/extensions/qos/ruletypes/requests.go | 3 +- .../v2/extensions/quotas/requests.go | 7 +- .../v2/extensions/rbacpolicies/requests.go | 12 +- .../v2/extensions/security/groups/requests.go | 12 +- .../v2/extensions/security/rules/requests.go | 9 +- .../v2/extensions/subnetpools/requests.go | 12 +- .../v2/extensions/trunks/requests.go | 21 ++- .../vpnaas/endpointgroups/requests.go | 12 +- .../extensions/vpnaas/ikepolicies/requests.go | 12 +- .../vpnaas/ipsecpolicies/requests.go | 12 +- .../v2/extensions/vpnaas/services/requests.go | 12 +- .../vpnaas/siteconnections/requests.go | 13 +- openstack/networking/v2/networks/requests.go | 12 +- openstack/networking/v2/ports/requests.go | 12 +- openstack/networking/v2/subnets/requests.go | 12 +- .../objectstorage/v1/accounts/requests.go | 10 +- .../objectstorage/v1/containers/requests.go | 29 +--- .../objectstorage/v1/objects/requests.go | 44 ++---- .../v1/objects/testing/fixtures.go | 3 + openstack/objectstorage/v1/swauth/requests.go | 8 +- .../orchestration/v1/buildinfo/requests.go | 3 +- .../v1/resourcetypes/requests.go | 9 +- .../orchestration/v1/stackevents/requests.go | 6 +- .../v1/stackresources/requests.go | 18 ++- openstack/orchestration/v1/stacks/requests.go | 27 ++-- .../v1/stacktemplates/requests.go | 6 +- .../v1/resourceproviders/requests.go | 20 +-- .../sharedfilesystems/apiversions/requests.go | 3 +- .../sharedfilesystems/v2/messages/requests.go | 6 +- .../v2/securityservices/requests.go | 12 +- .../v2/sharenetworks/requests.go | 18 ++- .../sharedfilesystems/v2/shares/requests.go | 70 +++++---- .../v2/sharetypes/requests.go | 27 ++-- .../v2/snapshots/requests.go | 12 +- .../workflow/v2/crontriggers/requests.go | 10 +- openstack/workflow/v2/executions/requests.go | 10 +- openstack/workflow/v2/workflows/requests.go | 11 +- service_client.go | 8 + 180 files changed, 1488 insertions(+), 1149 deletions(-) diff --git a/docs/contributor-tutorial/.template/requests.go b/docs/contributor-tutorial/.template/requests.go index 1c4bb4d3e9..2431393d60 100644 --- a/docs/contributor-tutorial/.template/requests.go +++ b/docs/contributor-tutorial/.template/requests.go @@ -38,7 +38,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details of a RESOURCE. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -64,15 +65,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a RESOURCE. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -98,8 +101,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetal/apiversions/requests.go b/openstack/baremetal/apiversions/requests.go index 3c586e6b9f..e9a610095a 100644 --- a/openstack/baremetal/apiversions/requests.go +++ b/openstack/baremetal/apiversions/requests.go @@ -6,12 +6,14 @@ import ( // List lists all the API versions available to end users. func List(client *gophercloud.ServiceClient) (r ListResult) { - _, r.Err = client.Get(listURL(client), &r.Body, nil) + resp, err := client.Get(listURL(client), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will get a specific API version, specified by major ID. func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - _, r.Err = client.Get(getURL(client, v), &r.Body, nil) + resp, err := client.Get(getURL(client, v), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetal/v1/allocations/requests.go b/openstack/baremetal/v1/allocations/requests.go index 7acb0021a7..dfb04ba942 100644 --- a/openstack/baremetal/v1/allocations/requests.go +++ b/openstack/baremetal/v1/allocations/requests.go @@ -50,7 +50,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } - _, r.Err = client.Post(createURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -118,14 +119,16 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get requests the details of an allocation by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of an allocation func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetal/v1/drivers/requests.go b/openstack/baremetal/v1/drivers/requests.go index ab962c8ef7..988f64e635 100644 --- a/openstack/baremetal/v1/drivers/requests.go +++ b/openstack/baremetal/v1/drivers/requests.go @@ -43,9 +43,10 @@ func ListDrivers(client *gophercloud.ServiceClient, opts ListDriversOptsBuilder) // GetDriverDetails Shows details for a driver func GetDriverDetails(client *gophercloud.ServiceClient, driverName string) (r GetDriverResult) { - _, r.Err = client.Get(driverDetailsURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(driverDetailsURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -53,9 +54,10 @@ func GetDriverDetails(client *gophercloud.ServiceClient, driverName string) (r G // driverName expects to be supplied in the driver_info field for every // Node it manages func GetDriverProperties(client *gophercloud.ServiceClient, driverName string) (r GetPropertiesResult) { - _, r.Err = client.Get(driverPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(driverPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -63,8 +65,9 @@ func GetDriverProperties(client *gophercloud.ServiceClient, driverName string) ( // driverName expects to be supplied in the node’s raid_config field, if a // RAID configuration change is requested. func GetDriverDiskProperties(client *gophercloud.ServiceClient, driverName string) (r GetDiskPropertiesResult) { - _, r.Err = client.Get(driverDiskPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(driverDiskPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index aab7a3ff24..233057ad96 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -164,9 +164,10 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // Get requests details on a single node, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -260,7 +261,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } - _, r.Err = client.Post(createURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -301,34 +303,37 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up body[i] = result } - _, r.Err = client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ - JSONBody: &body, - OkCodes: []int{200}, + resp, err := client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests that a node be removed func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Request that Ironic validate 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. func Validate(client *gophercloud.ServiceClient, id string) (r ValidateResult) { - _, r.Err = client.Get(validateURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(validateURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Inject NMI (Non-Masking Interrupts) for the given Node. This feature can be used for hardware diagnostics, and // actual support depends on a driver. func InjectNMI(client *gophercloud.ServiceClient, id string) (r InjectNMIResult) { - _, r.Err = client.Put(injectNMIURL(client, id), map[string]string{}, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(injectNMIURL(client, id), map[string]string{}, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -362,25 +367,28 @@ func SetBootDevice(client *gophercloud.ServiceClient, id string, bootDevice Boot return } - _, r.Err = client.Put(bootDeviceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(bootDeviceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get the current boot device for the given Node. func GetBootDevice(client *gophercloud.ServiceClient, id string) (r BootDeviceResult) { - _, r.Err = client.Get(bootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(bootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Retrieve the acceptable set of supported boot devices for a specific Node. func GetSupportedBootDevices(client *gophercloud.ServiceClient, id string) (r SupportedBootDeviceResult) { - _, r.Err = client.Get(supportedBootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(supportedBootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -435,9 +443,10 @@ func ChangeProvisionState(client *gophercloud.ServiceClient, id string, opts Pro return } - _, r.Err = client.Put(provisionStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(provisionStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -481,9 +490,10 @@ func ChangePowerState(client *gophercloud.ServiceClient, id string, opts PowerSt return } - _, r.Err = client.Put(powerStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(powerStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -586,8 +596,9 @@ func SetRAIDConfig(client *gophercloud.ServiceClient, id string, raidConfigOptsB return } - _, r.Err = client.Put(raidConfigURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(raidConfigURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetal/v1/ports/requests.go b/openstack/baremetal/v1/ports/requests.go index 2b9aecaabb..ed191649d6 100644 --- a/openstack/baremetal/v1/ports/requests.go +++ b/openstack/baremetal/v1/ports/requests.go @@ -99,9 +99,10 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // Get - requests the details off a port, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -161,7 +162,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } - _, r.Err = client.Post(createURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -202,15 +204,16 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up body[i] = patch.ToPortUpdateMap() } - _, r.Err = client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ - JSONBody: &body, - OkCodes: []int{200}, + resp, err := client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete - requests the deletion of a port func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetalintrospection/v1/introspection/requests.go b/openstack/baremetalintrospection/v1/introspection/requests.go index a2d02ccde7..d44b4b8ad6 100644 --- a/openstack/baremetalintrospection/v1/introspection/requests.go +++ b/openstack/baremetalintrospection/v1/introspection/requests.go @@ -48,9 +48,10 @@ func ListIntrospections(client *gophercloud.ServiceClient, opts ListIntrospectio // GetIntrospectionStatus makes a request against the Inspector API to get the // status of a single introspection. func GetIntrospectionStatus(client *gophercloud.ServiceClient, nodeID string) (r GetIntrospectionStatusResult) { - _, r.Err = client.Get(introspectionURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(introspectionURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -81,36 +82,37 @@ func StartIntrospection(client *gophercloud.ServiceClient, nodeID string, opts S return } - _, r.Err = client.Post(introspectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(introspectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // AbortIntrospection abort running introspection. func AbortIntrospection(client *gophercloud.ServiceClient, nodeID string) (r AbortResult) { - _, r.Err = client.Post(abortIntrospectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(abortIntrospectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetIntrospectionData return stored data from successful introspection. func GetIntrospectionData(client *gophercloud.ServiceClient, nodeID string) (r DataResult) { - _, r.Err = client.Get(introspectionDataURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(introspectionDataURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ReApplyIntrospection triggers introspection on stored unprocessed data. // No data is allowed to be sent along with the request. func ReApplyIntrospection(client *gophercloud.ServiceClient, nodeID string) (r ApplyDataResult) { - _, r.Err = client.Post(introspectionUnprocessedDataURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(introspectionUnprocessedDataURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/extensions/backups/requests.go index 6829a8c603..0524a1a73a 100644 --- a/openstack/blockstorage/extensions/backups/requests.go +++ b/openstack/blockstorage/extensions/backups/requests.go @@ -61,22 +61,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Backup with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Backup with the provided ID. To extract the Backup // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,9 +180,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -208,16 +212,18 @@ func RestoreFromBackup(client *gophercloud.ServiceClient, id string, opts Restor r.Err = err return } - _, r.Err = client.Post(restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Export will export a Backup information. To extract the Backup export record // object from the response, call the Extract method on the ExportResult. func Export(client *gophercloud.ServiceClient, id string) (r ExportResult) { - _, r.Err = client.Get(exportURL(client, id), &r.Body, nil) + resp, err := client.Get(exportURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -240,8 +246,9 @@ func Import(client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) r.Err = err return } - _, r.Err = client.Post(importURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(importURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go index c89b4e3514..50cd4fbaf1 100644 --- a/openstack/blockstorage/extensions/quotasets/requests.go +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -8,20 +8,23 @@ import ( // Get returns public data about a previously created QuotaSet. func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { - _, r.Err = client.Get(getURL(client, projectID), &r.Body, nil) + resp, err := client.Get(getURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetDefaults returns public data about the project's default block storage quotas. func GetDefaults(client *gophercloud.ServiceClient, projectID string) (r GetResult) { - _, r.Err = client.Get(getDefaultsURL(client, projectID), &r.Body, nil) + resp, err := client.Get(getDefaultsURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetUsage returns detailed public data about a previously created QuotaSet. func GetUsage(client *gophercloud.ServiceClient, projectID string) (r GetUsageResult) { u := fmt.Sprintf("%s?usage=true", getURL(client, projectID)) - _, r.Err = client.Get(u, &r.Body, nil) + resp, err := client.Get(u, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -33,10 +36,11 @@ func Update(client *gophercloud.ServiceClient, projectID string, opts UpdateOpts return } - _, r.Err = client.Put(updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - return r + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return } // UpdateOptsBuilder enables extensins to add parameters to the update request. @@ -87,8 +91,9 @@ type UpdateOpts struct { // Resets the quotas for the given tenant to their default values. func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { - _, r.Err = client.Delete(updateURL(client, projectID), &gophercloud.RequestOpts{ + resp, err := client.Delete(updateURL(client, projectID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index 5648b126c7..d84828327e 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -47,18 +47,20 @@ func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // BeginDetach will mark the volume as detaching. func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} - _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -87,27 +89,30 @@ func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Reserve will reserve a volume based on volume ID. func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { b := map[string]interface{}{"os-reserve": make(map[string]interface{})} - _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unreserve will unreserve a volume based on volume ID. func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) { b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} - _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -145,9 +150,10 @@ func InitializeConnection(client *gophercloud.ServiceClient, id string, opts Ini r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -183,9 +189,10 @@ func TerminateConnection(client *gophercloud.ServiceClient, id string, opts Term r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -216,9 +223,10 @@ func ExtendSize(client *gophercloud.ServiceClient, id string, opts ExtendSizeOpt r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -264,15 +272,17 @@ func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageO r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ForceDelete will delete the volume regardless of state. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -301,9 +311,10 @@ func SetImageMetadata(client *gophercloud.ServiceClient, id string, opts ImageMe r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -326,8 +337,9 @@ func SetBootable(client *gophercloud.ServiceClient, id string, opts BootableOpts r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/extensions/volumetransfers/requests.go b/openstack/blockstorage/extensions/volumetransfers/requests.go index eb0c04d048..d32ac70f81 100644 --- a/openstack/blockstorage/extensions/volumetransfers/requests.go +++ b/openstack/blockstorage/extensions/volumetransfers/requests.go @@ -27,9 +27,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) r.Err = err return } - _, r.Err = client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -52,15 +53,17 @@ func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r Cr r.Err = err return } - _, r.Err = client.Post(acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a volume transfer. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -115,6 +118,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // 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) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/v1/apiversions/requests.go b/openstack/blockstorage/v1/apiversions/requests.go index 725c13a761..18a5f28640 100644 --- a/openstack/blockstorage/v1/apiversions/requests.go +++ b/openstack/blockstorage/v1/apiversions/requests.go @@ -15,6 +15,7 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { // Get will retrieve the volume type with the provided ID. To extract the volume // type from the result, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - _, r.Err = client.Get(getURL(client, v), &r.Body, nil) + resp, err := client.Get(getURL(client, v), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go index bf15231383..76da1ed631 100644 --- a/openstack/blockstorage/v1/snapshots/requests.go +++ b/openstack/blockstorage/v1/snapshots/requests.go @@ -37,22 +37,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -120,8 +123,9 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet r.Err = err return } - _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go index 40f369a9a2..f89886b767 100644 --- a/openstack/blockstorage/v1/volumes/requests.go +++ b/openstack/blockstorage/v1/volumes/requests.go @@ -41,22 +41,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Volume with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -129,8 +132,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + 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/v1/volumetypes/requests.go b/openstack/blockstorage/v1/volumetypes/requests.go index b95c09addd..55489e6923 100644 --- a/openstack/blockstorage/v1/volumetypes/requests.go +++ b/openstack/blockstorage/v1/volumetypes/requests.go @@ -32,22 +32,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the volume type with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will retrieve the volume type with the provided ID. To extract the volume // type from the result, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go index 9df2a961d3..c0e12139df 100644 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ b/openstack/blockstorage/v2/snapshots/requests.go @@ -37,22 +37,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -132,8 +135,9 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet r.Err = err return } - _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index e16c451e3a..e524d8210d 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -55,9 +55,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -91,14 +92,16 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder } url += query } - _, r.Err = client.Delete(url, nil) + resp, err := client.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -192,8 +195,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + 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/attachments/requests.go b/openstack/blockstorage/v3/attachments/requests.go index 5a721cc391..4cbd5054ec 100644 --- a/openstack/blockstorage/v3/attachments/requests.go +++ b/openstack/blockstorage/v3/attachments/requests.go @@ -46,24 +46,27 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Attachment with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ + resp, err := client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Attachment with the provided ID. To extract the Attachment // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -156,9 +159,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -168,8 +172,9 @@ func Complete(client *gophercloud.ServiceClient, id string) (r CompleteResult) { b := map[string]interface{}{ "os-complete": nil, } - _, r.Err = client.Post(completeURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(completeURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 05257b79c3..6f5a24341f 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -37,22 +37,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Snapshot with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -143,8 +146,9 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet r.Err = err return } - _, r.Err = client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateMetadataURL(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/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index e54f8cdebc..f6063c5954 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -60,9 +60,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -96,14 +97,16 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder } url += query } - _, r.Err = client.Delete(url, nil) + resp, err := client.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -197,8 +200,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + 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/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index a477c97d9f..869d426bf6 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -40,22 +40,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing Volume Type with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume Type with the provided ID. To extract the Volume Type object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -131,8 +134,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + 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/cdn/v1/base/requests.go b/openstack/cdn/v1/base/requests.go index 34d3b724fb..1bb41e462c 100644 --- a/openstack/cdn/v1/base/requests.go +++ b/openstack/cdn/v1/base/requests.go @@ -5,15 +5,17 @@ import "github.com/gophercloud/gophercloud" // Get retrieves the home document, allowing the user to discover the // entire API. func Get(c *gophercloud.ServiceClient) (r GetResult) { - _, r.Err = c.Get(getURL(c), &r.Body, nil) + resp, err := c.Get(getURL(c), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Ping retrieves a ping to the server. func Ping(c *gophercloud.ServiceClient) (r PingResult) { - _, r.Err = c.Get(pingURL(c), nil, &gophercloud.RequestOpts{ + resp, err := c.Get(pingURL(c), nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, MoreHeaders: map[string]string{"Accept": ""}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/cdn/v1/flavors/requests.go b/openstack/cdn/v1/flavors/requests.go index 1977fe365a..dd16443b18 100644 --- a/openstack/cdn/v1/flavors/requests.go +++ b/openstack/cdn/v1/flavors/requests.go @@ -14,6 +14,7 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { // Get retrieves a specific flavor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/cdn/v1/serviceassets/requests.go b/openstack/cdn/v1/serviceassets/requests.go index 80c908fd9d..36200ca37b 100644 --- a/openstack/cdn/v1/serviceassets/requests.go +++ b/openstack/cdn/v1/serviceassets/requests.go @@ -46,6 +46,7 @@ func Delete(c *gophercloud.ServiceClient, idOrURL string, opts DeleteOptsBuilder } url += q } - _, r.Err = c.Delete(url, nil) + resp, err := c.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/cdn/v1/services/requests.go b/openstack/cdn/v1/services/requests.go index 4c0c626606..1ec4810811 100644 --- a/openstack/cdn/v1/services/requests.go +++ b/openstack/cdn/v1/services/requests.go @@ -89,8 +89,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul return r } resp, err := c.Post(createURL(c), &b, nil, nil) - r.Header = resp.Header - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -105,7 +104,8 @@ func Get(c *gophercloud.ServiceClient, idOrURL string) (r GetResult) { } else { url = getURL(c, idOrURL) } - _, r.Err = c.Get(url, &r.Body, nil) + resp, err := c.Get(url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -264,8 +264,7 @@ func Update(c *gophercloud.ServiceClient, idOrURL string, opts UpdateOpts) (r Up JSONBody: &b, OkCodes: []int{202}, }) - r.Header = resp.Header - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -280,6 +279,7 @@ func Delete(c *gophercloud.ServiceClient, idOrURL string) (r DeleteResult) { } else { url = deleteURL(c, idOrURL) } - _, r.Err = c.Delete(url, nil) + resp, err := c.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/actions/requests.go b/openstack/clustering/v1/actions/requests.go index f1a4f43f3e..a9d5b9e046 100644 --- a/openstack/clustering/v1/actions/requests.go +++ b/openstack/clustering/v1/actions/requests.go @@ -46,6 +46,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details of a single action. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 43312ed4d3..d89f86a1ce 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -2,7 +2,6 @@ package clusters import ( "fmt" - "net/http" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" @@ -56,29 +55,19 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - var result *http.Response - result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single cluster. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -153,25 +142,17 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return r } - - var result *http.Response - result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) - - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - var result *http.Response - result, r.Err = client.Delete(deleteURL(client, id), nil) - if r.Err == nil { - r.Header = result.Header - } + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -217,15 +198,10 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -252,14 +228,10 @@ func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOptsBuild r.Err = err return } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -286,14 +258,10 @@ func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOptsBui r.Err = err return } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -321,14 +289,10 @@ func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolic r.Err = err return } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -356,14 +320,10 @@ func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolic r.Err = err return } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -390,15 +350,10 @@ func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolic r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -440,7 +395,8 @@ func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts List // GetPolicy retrieves details of a cluster policy. func GetPolicy(client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetPolicyResult) { - _, r.Err = client.Get(getPolicyURL(client, clusterID, policyID), &r.Body, nil) + resp, err := client.Get(getPolicyURL(client, clusterID, policyID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -469,14 +425,10 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOptsBuild r.Err = err return } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -486,14 +438,10 @@ func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { "check": map[string]interface{}{}, } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -513,14 +461,10 @@ func CompleteLifecycle(client *gophercloud.ServiceClient, id string, opts Comple return } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -538,11 +482,10 @@ func AddNodes(client *gophercloud.ServiceClient, id string, opts AddNodesOpts) ( r.Err = err return } - var result *http.Response - result, r.Err = client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - r.Header = result.Header + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -560,11 +503,10 @@ func RemoveNodes(client *gophercloud.ServiceClient, clusterID string, opts Remov r.Err = err return } - var result *http.Response - result, r.Err = client.Post(nodeURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(nodeURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - r.Header = result.Header + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -582,11 +524,10 @@ func ReplaceNodes(client *gophercloud.ServiceClient, id string, opts ReplaceNode r.Err = err return } - var result *http.Response - result, r.Err = client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - r.Header = result.Header + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -610,16 +551,10 @@ func Collect(client *gophercloud.ServiceClient, id string, opts CollectOptsBuild r.Err = err return } - - var result *http.Response - result, r.Err = client.Get(collectURL(client, id, query), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(collectURL(client, id, query), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -681,14 +616,9 @@ func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/events/requests.go b/openstack/clustering/v1/events/requests.go index c962122ef8..0db8d38863 100644 --- a/openstack/clustering/v1/events/requests.go +++ b/openstack/clustering/v1/events/requests.go @@ -48,6 +48,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details of a single event. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index a9ac890b96..b4e9c0e9f0 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -1,8 +1,6 @@ package nodes import ( - "net/http" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -34,15 +32,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -72,15 +65,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - - var result *http.Response - result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -125,23 +113,17 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Delete deletes the specified node. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - var result *http.Response - result, r.Err = client.Delete(deleteURL(client, id), nil) - if r.Err == nil { - r.Header = result.Header - } + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get makes a request against senlin to get a details of a node type func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -194,15 +176,10 @@ func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -231,11 +208,10 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r r.Err = err return } - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - r.Header = result.Header + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -243,13 +219,9 @@ func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { b := map[string]interface{}{ "check": map[string]interface{}{}, } - - var result *http.Response - result, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index f3e2ad249b..903a5534d6 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -1,8 +1,6 @@ package policies import ( - "net/http" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -90,25 +88,19 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - var result *http.Response - result, r.Err = client.Post(policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete makes a request against the API to delete a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { - var result *http.Response - result, r.Err = client.Delete(policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ + resp, err := client.Delete(policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -135,15 +127,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - - var result *http.Response - result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -170,24 +157,19 @@ func Validate(client *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r Va r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get makes a request against the API to get details for a policy. func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { url := policyGetURL(client, policyTypeName) - - _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/policytypes/requests.go b/openstack/clustering/v1/policytypes/requests.go index ca9a5a3be1..ef8f84b39a 100644 --- a/openstack/clustering/v1/policytypes/requests.go +++ b/openstack/clustering/v1/policytypes/requests.go @@ -18,9 +18,9 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { url := policyTypeGetURL(client, policyTypeName) - _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index 7d48ddafd7..759c0114bb 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -1,8 +1,6 @@ package profiles import ( - "net/http" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -32,27 +30,19 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - var result *http.Response - result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) - - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves detail of a single profile. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -118,23 +108,17 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return r } - var result *http.Response - result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified profile via profile id. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - var result *http.Response - result, r.Err = client.Delete(deleteURL(client, id), nil) - if r.Err == nil { - r.Header = result.Header - } + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -161,13 +145,9 @@ func Validate(client *gophercloud.ServiceClient, opts ValidateOpts) (r ValidateR r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/profiletypes/requests.go b/openstack/clustering/v1/profiletypes/requests.go index 37fe031339..2b944032ce 100644 --- a/openstack/clustering/v1/profiletypes/requests.go +++ b/openstack/clustering/v1/profiletypes/requests.go @@ -6,9 +6,9 @@ import ( ) func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index 27d45136cb..32f7366094 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -1,8 +1,6 @@ package receivers import ( - "net/http" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -43,10 +41,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -75,25 +73,17 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - - var result *http.Response - result, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) - - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single receiver. Use Extract to convert its result into a Receiver. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - if r.Err == nil { - r.Header = result.Header - } + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -140,18 +130,16 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Delete deletes the specified receiver ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Notify Notifies message type receiver func Notify(client *gophercloud.ServiceClient, id string) (r NotifyResult) { - result, err := client.Post(notifyURL(client, id), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(notifyURL(client, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) - - if err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/webhooks/requests.go b/openstack/clustering/v1/webhooks/requests.go index 1b211b4447..12371366f9 100644 --- a/openstack/clustering/v1/webhooks/requests.go +++ b/openstack/clustering/v1/webhooks/requests.go @@ -46,8 +46,9 @@ func Trigger(client *gophercloud.ServiceClient, id string, opts TriggerOptsBuild return } - _, r.Err = client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/common/extensions/requests.go b/openstack/common/extensions/requests.go index 46b7d60cd6..bb301c7a12 100755 --- a/openstack/common/extensions/requests.go +++ b/openstack/common/extensions/requests.go @@ -7,7 +7,8 @@ import ( // Get retrieves information for a specific extension using its alias. func Get(c *gophercloud.ServiceClient, alias string) (r GetResult) { - _, r.Err = c.Get(ExtensionURL(c, alias), &r.Body, nil) + resp, err := c.Get(ExtensionURL(c, alias), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index c37531c56a..5268ee3ad0 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -36,27 +36,30 @@ func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) r.Err = err return } - _, r.Err = client.Post(aggregatesCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(aggregatesCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete makes a request against the API to delete an aggregate. func Delete(client *gophercloud.ServiceClient, aggregateID int) (r DeleteResult) { v := strconv.Itoa(aggregateID) - _, r.Err = client.Delete(aggregatesDeleteURL(client, v), &gophercloud.RequestOpts{ + resp, err := client.Delete(aggregatesDeleteURL(client, v), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get makes a request against the API to get details for a specific aggregate. func Get(client *gophercloud.ServiceClient, aggregateID int) (r GetResult) { v := strconv.Itoa(aggregateID) - _, r.Err = client.Get(aggregatesGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(aggregatesGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -84,9 +87,10 @@ func Update(client *gophercloud.ServiceClient, aggregateID int, opts UpdateOpts) r.Err = err return } - _, r.Err = client.Put(aggregatesUpdateURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(aggregatesUpdateURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -108,9 +112,10 @@ func AddHost(client *gophercloud.ServiceClient, aggregateID int, opts AddHostOpt r.Err = err return } - _, r.Err = client.Post(aggregatesAddHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(aggregatesAddHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -132,9 +137,10 @@ func RemoveHost(client *gophercloud.ServiceClient, aggregateID int, opts RemoveH r.Err = err return } - _, r.Err = client.Post(aggregatesRemoveHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(aggregatesRemoveHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -155,8 +161,9 @@ func SetMetadata(client *gophercloud.ServiceClient, aggregateID int, opts SetMet r.Err = err return } - _, r.Err = client.Post(aggregatesSetMetadataURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(aggregatesSetMetadataURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go index 874f7a61ec..9748ce4ac1 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/requests.go +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -14,9 +14,10 @@ func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { // Get requests details on a single interface attachment by the server and port IDs. func Get(client *gophercloud.ServiceClient, serverID, portID string) (r GetResult) { - _, r.Err = client.Get(getInterfaceURL(client, serverID, portID), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getInterfaceURL(client, serverID, portID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -58,15 +59,17 @@ func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsB r.Err = err return } - _, r.Err = client.Post(createInterfaceURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createInterfaceURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete makes a request against the nova API to detach a single interface from the server. // It needs server and port IDs to make a such request. func Delete(client *gophercloud.ServiceClient, serverID, portID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteInterfaceURL(client, serverID, portID), nil) + resp, err := client.Delete(deleteInterfaceURL(client, serverID, portID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go index d2346d4b42..096d8be7ef 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -125,8 +125,9 @@ func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) ( r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/extensions/defsecrules/requests.go index b63a5d5d63..3a9503249b 100644 --- a/openstack/compute/v2/extensions/defsecrules/requests.go +++ b/openstack/compute/v2/extensions/defsecrules/requests.go @@ -56,20 +56,23 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will return details for a particular default rule. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) + resp, err := client.Get(resourceURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a rule the project's default security group. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(resourceURL(client, id), nil) + resp, err := client.Delete(resourceURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/diagnostics/requests.go b/openstack/compute/v2/extensions/diagnostics/requests.go index b0b4412229..0f322761e1 100644 --- a/openstack/compute/v2/extensions/diagnostics/requests.go +++ b/openstack/compute/v2/extensions/diagnostics/requests.go @@ -6,6 +6,7 @@ import ( // Diagnostics func Get(client *gophercloud.ServiceClient, serverId string) (r serverDiagnosticsResult) { - _, r.Err = client.Get(serverDiagnosticsURL(client, serverId), &r.Body, nil) + resp, err := client.Get(serverDiagnosticsURL(client, serverId), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/evacuate/requests.go b/openstack/compute/v2/extensions/evacuate/requests.go index b0455acc73..b1d3726ab8 100644 --- a/openstack/compute/v2/extensions/evacuate/requests.go +++ b/openstack/compute/v2/extensions/evacuate/requests.go @@ -35,8 +35,9 @@ func Evacuate(client *gophercloud.ServiceClient, id string, opts EvacuateOptsBui r.Err = err return } - _, r.Err = client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/floatingips/requests.go b/openstack/compute/v2/extensions/floatingips/requests.go index a922639dec..0d8104d24b 100644 --- a/openstack/compute/v2/extensions/floatingips/requests.go +++ b/openstack/compute/v2/extensions/floatingips/requests.go @@ -36,21 +36,24 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns data about a previously created Floating IP. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous allocated Floating IP. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -81,7 +84,8 @@ func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts r.Err = err return } - _, r.Err = client.Post(associateURL(client, serverID), b, nil, nil) + resp, err := client.Post(associateURL(client, serverID), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -109,6 +113,7 @@ func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, op r.Err = err return } - _, r.Err = client.Post(disassociateURL(client, serverID), b, nil, nil) + resp, err := client.Post(disassociateURL(client, serverID), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/extensions/hypervisors/requests.go index db16543e22..a7047d98da 100644 --- a/openstack/compute/v2/extensions/hypervisors/requests.go +++ b/openstack/compute/v2/extensions/hypervisors/requests.go @@ -14,24 +14,27 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // Statistics makes a request against the API to get hypervisors statistics. func GetStatistics(client *gophercloud.ServiceClient) (r StatisticsResult) { - _, r.Err = client.Get(hypervisorsStatisticsURL(client), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(hypervisorsStatisticsURL(client), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get makes a request against the API to get details for specific hypervisor. func Get(client *gophercloud.ServiceClient, hypervisorID string) (r HypervisorResult) { - _, r.Err = client.Get(hypervisorsGetURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(hypervisorsGetURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetUptime makes a request against the API to get uptime for specific hypervisor. func GetUptime(client *gophercloud.ServiceClient, hypervisorID string) (r UptimeResult) { - _, r.Err = client.Get(hypervisorsUptimeURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(hypervisorsUptimeURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/instanceactions/request.go b/openstack/compute/v2/extensions/instanceactions/request.go index 326081dfd0..9367f96900 100644 --- a/openstack/compute/v2/extensions/instanceactions/request.go +++ b/openstack/compute/v2/extensions/instanceactions/request.go @@ -72,8 +72,9 @@ func List(client *gophercloud.ServiceClient, id string, opts ListOptsBuilder) pa // Get makes a request against the API to get a server action. func Get(client *gophercloud.ServiceClient, serverID, requestID string) (r InstanceActionResult) { - _, r.Err = client.Get(instanceActionsURL(client, serverID, requestID), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(instanceActionsURL(client, serverID, requestID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go index b72807770e..39056fdc70 100644 --- a/openstack/compute/v2/extensions/keypairs/requests.go +++ b/openstack/compute/v2/extensions/keypairs/requests.go @@ -67,20 +67,23 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns public data about a previously uploaded KeyPair. func Get(client *gophercloud.ServiceClient, name string) (r GetResult) { - _, r.Err = client.Get(getURL(client, name), &r.Body, nil) + resp, err := client.Get(getURL(client, name), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous stored KeyPair from the server. func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, name), nil) + resp, err := client.Delete(deleteURL(client, name), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/limits/requests.go b/openstack/compute/v2/extensions/limits/requests.go index 57573d38c1..5193ce8465 100644 --- a/openstack/compute/v2/extensions/limits/requests.go +++ b/openstack/compute/v2/extensions/limits/requests.go @@ -34,6 +34,7 @@ func Get(client *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { url += query } - _, r.Err = client.Get(url, &r.Body, nil) + resp, err := client.Get(url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/lockunlock/requests.go b/openstack/compute/v2/extensions/lockunlock/requests.go index 3ddf118467..df466bfcbf 100644 --- a/openstack/compute/v2/extensions/lockunlock/requests.go +++ b/openstack/compute/v2/extensions/lockunlock/requests.go @@ -7,12 +7,14 @@ import ( // Lock is the operation responsible for locking a Compute server. func Lock(client *gophercloud.ServiceClient, id string) (r LockResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unlock is the operation responsible for unlocking a Compute server. func Unlock(client *gophercloud.ServiceClient, id string) (r UnlockResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/migrate/requests.go b/openstack/compute/v2/extensions/migrate/requests.go index 5c7833c386..81969e39a4 100644 --- a/openstack/compute/v2/extensions/migrate/requests.go +++ b/openstack/compute/v2/extensions/migrate/requests.go @@ -7,7 +7,8 @@ import ( // Migrate will initiate a migration of the instance to another host. func Migrate(client *gophercloud.ServiceClient, id string) (r MigrateResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -46,6 +47,7 @@ func LiveMigrate(client *gophercloud.ServiceClient, id string, opts LiveMigrateO r.Err = err return } - _, r.Err = client.Post(extensions.ActionURL(client, id), b, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/networks/requests.go b/openstack/compute/v2/extensions/networks/requests.go index 5432a1025c..1a3bc0da79 100644 --- a/openstack/compute/v2/extensions/networks/requests.go +++ b/openstack/compute/v2/extensions/networks/requests.go @@ -14,6 +14,7 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // Get returns data about a previously created Network. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/pauseunpause/requests.go b/openstack/compute/v2/extensions/pauseunpause/requests.go index 2096173999..db3032cfff 100644 --- a/openstack/compute/v2/extensions/pauseunpause/requests.go +++ b/openstack/compute/v2/extensions/pauseunpause/requests.go @@ -7,12 +7,14 @@ import ( // Pause is the operation responsible for pausing a Compute server. func Pause(client *gophercloud.ServiceClient, id string) (r PauseResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unpause is the operation responsible for unpausing a Compute server. func Unpause(client *gophercloud.ServiceClient, id string) (r UnpauseResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go index d5daa7e3fb..6d3af50939 100644 --- a/openstack/compute/v2/extensions/quotasets/requests.go +++ b/openstack/compute/v2/extensions/quotasets/requests.go @@ -6,13 +6,15 @@ import ( // Get returns public data about a previously created QuotaSet. func Get(client *gophercloud.ServiceClient, tenantID string) (r GetResult) { - _, r.Err = client.Get(getURL(client, tenantID), &r.Body, nil) + resp, err := client.Get(getURL(client, tenantID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetDetail returns detailed public data about a previously created QuotaSet. func GetDetail(client *gophercloud.ServiceClient, tenantID string) (r GetDetailResult) { - _, r.Err = client.Get(getDetailURL(client, tenantID), &r.Body, nil) + resp, err := client.Get(getDetailURL(client, tenantID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -24,13 +26,15 @@ func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsB return } - _, r.Err = client.Put(updateURL(client, tenantID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Put(updateURL(client, tenantID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Resets the quotas for the given tenant to their default values. func Delete(client *gophercloud.ServiceClient, tenantID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, tenantID), nil) + resp, err := client.Delete(deleteURL(client, tenantID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/remoteconsoles/requests.go b/openstack/compute/v2/extensions/remoteconsoles/requests.go index 5c41035f70..7620b56957 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/requests.go +++ b/openstack/compute/v2/extensions/remoteconsoles/requests.go @@ -76,8 +76,9 @@ func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsB return } - _, r.Err = client.Post(createURL(client, serverID), reqBody, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client, serverID), reqBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/rescueunrescue/requests.go b/openstack/compute/v2/extensions/rescueunrescue/requests.go index fd7534b97e..d6169e0d2c 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/requests.go +++ b/openstack/compute/v2/extensions/rescueunrescue/requests.go @@ -38,14 +38,16 @@ func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder r.Err = err return } - _, r.Err = client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unrescue instructs the provider to return the server from RESCUE mode. func Unrescue(client *gophercloud.ServiceClient, id string) (r UnrescueResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/resetstate/requests.go b/openstack/compute/v2/extensions/resetstate/requests.go index e98ba32bd2..cf0650df8c 100644 --- a/openstack/compute/v2/extensions/resetstate/requests.go +++ b/openstack/compute/v2/extensions/resetstate/requests.go @@ -19,6 +19,7 @@ const ( // ResetState will reset the state of a server func ResetState(client *gophercloud.ServiceClient, id string, state ServerState) (r ResetResult) { stateMap := map[string]interface{}{"state": state} - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go index 92a0331e18..9a5070bd91 100644 --- a/openstack/compute/v2/extensions/secgroups/requests.go +++ b/openstack/compute/v2/extensions/secgroups/requests.go @@ -49,9 +49,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -82,21 +83,24 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will return details for a particular security group. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) + resp, err := client.Get(resourceURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a security group from the project. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(resourceURL(client, id), nil) + resp, err := client.Delete(resourceURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -151,15 +155,17 @@ func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) ( r.Err = err return } - _, r.Err = client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will permanently delete a rule from a security group. func DeleteRule(client *gophercloud.ServiceClient, id string) (r DeleteRuleResult) { - _, r.Err = client.Delete(resourceRuleURL(client, id), nil) + resp, err := client.Delete(resourceRuleURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -172,12 +178,14 @@ func actionMap(prefix, groupName string) map[string]map[string]string { // AddServer will associate a server and a security group, enforcing the // rules of the group on the server. func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r AddServerResult) { - _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), nil, nil) + resp, err := client.Post(serverActionURL(client, serverID), actionMap("add", groupName), nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RemoveServer will disassociate a server from a security group. func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r RemoveServerResult) { - _, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), nil, nil) + resp, err := client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go index 5740afaf05..7a877718d9 100644 --- a/openstack/compute/v2/extensions/servergroups/requests.go +++ b/openstack/compute/v2/extensions/servergroups/requests.go @@ -48,20 +48,23 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns data about a previously created ServerGroup. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previously allocated ServerGroup. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/services/requests.go b/openstack/compute/v2/extensions/services/requests.go index ed8907d1d0..3533df9c06 100644 --- a/openstack/compute/v2/extensions/services/requests.go +++ b/openstack/compute/v2/extensions/services/requests.go @@ -74,8 +74,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + 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/compute/v2/extensions/shelveunshelve/requests.go b/openstack/compute/v2/extensions/shelveunshelve/requests.go index b238e220c4..afcc85c473 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/requests.go +++ b/openstack/compute/v2/extensions/shelveunshelve/requests.go @@ -7,13 +7,15 @@ import ( // Shelve is the operation responsible for shelving a Compute server. func Shelve(client *gophercloud.ServiceClient, id string) (r ShelveResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ShelveOffload is the operation responsible for Shelve-Offload a Compute server. func ShelveOffload(client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -52,6 +54,7 @@ func Unshelve(client *gophercloud.ServiceClient, id string, opts UnshelveOptsBui r.Err = err return } - _, r.Err = client.Post(extensions.ActionURL(client, id), b, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/startstop/requests.go b/openstack/compute/v2/extensions/startstop/requests.go index db55a52aa0..9ece54b205 100644 --- a/openstack/compute/v2/extensions/startstop/requests.go +++ b/openstack/compute/v2/extensions/startstop/requests.go @@ -7,12 +7,14 @@ import ( // Start is the operation responsible for starting a Compute server. func Start(client *gophercloud.ServiceClient, id string) (r StartResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Stop is the operation responsible for stopping a Compute server. func Stop(client *gophercloud.ServiceClient, id string) (r StopResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/suspendresume/requests.go b/openstack/compute/v2/extensions/suspendresume/requests.go index c154488146..f5ebda5d8b 100644 --- a/openstack/compute/v2/extensions/suspendresume/requests.go +++ b/openstack/compute/v2/extensions/suspendresume/requests.go @@ -7,12 +7,14 @@ import ( // Suspend is the operation responsible for suspending a Compute server. func Suspend(client *gophercloud.ServiceClient, id string) (r SuspendResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Resume is the operation responsible for resuming a Compute server. func Resume(client *gophercloud.ServiceClient, id string) (r UnsuspendResult) { - _, r.Err = client.Post(extensions.ActionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) + resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/tags/requests.go b/openstack/compute/v2/extensions/tags/requests.go index 3b43096411..0ccad4c3a9 100644 --- a/openstack/compute/v2/extensions/tags/requests.go +++ b/openstack/compute/v2/extensions/tags/requests.go @@ -5,18 +5,20 @@ import "github.com/gophercloud/gophercloud" // List all tags on a server. func List(client *gophercloud.ServiceClient, serverID string) (r ListResult) { url := listURL(client, serverID) - _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Check if a tag exists on a server. func Check(client *gophercloud.ServiceClient, serverID, tag string) (r CheckResult) { url := checkURL(client, serverID, tag) - _, r.Err = client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -43,35 +45,39 @@ func ReplaceAll(client *gophercloud.ServiceClient, serverID string, opts Replace r.Err = err return } - _, r.Err = client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Add adds a new Tag on a server. func Add(client *gophercloud.ServiceClient, serverID, tag string) (r AddResult) { url := addURL(client, serverID, tag) - _, r.Err = client.Put(url, nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete removes a tag from a server. func Delete(client *gophercloud.ServiceClient, serverID, tag string) (r DeleteResult) { url := deleteURL(client, serverID, tag) - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteAll removes all tag from a server. func DeleteAll(client *gophercloud.ServiceClient, serverID string) (r DeleteResult) { url := deleteAllURL(client, serverID) - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/tenantnetworks/requests.go b/openstack/compute/v2/extensions/tenantnetworks/requests.go index 00899056fd..80c7b55920 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/requests.go +++ b/openstack/compute/v2/extensions/tenantnetworks/requests.go @@ -14,6 +14,7 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // Get returns data about a previously created Network. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go index 6a262c212e..3b09cdef2b 100644 --- a/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/openstack/compute/v2/extensions/volumeattach/requests.go @@ -40,21 +40,24 @@ func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsB r.Err = err return } - _, r.Err = client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns public data about a previously created VolumeAttachment. func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) { - _, r.Err = client.Get(getURL(client, serverID, attachmentID), &r.Body, nil) + resp, err := client.Get(getURL(client, serverID, attachmentID), &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) { - _, r.Err = client.Delete(deleteURL(client, serverID, attachmentID), nil) + resp, err := client.Delete(deleteURL(client, serverID, attachmentID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 8156c13e94..1b7acd0a7e 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -142,22 +142,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, 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) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified flavor ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -194,9 +197,10 @@ func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsB r.Err = err return } - _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -224,20 +228,23 @@ func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAcces r.Err = err return } - _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ExtraSpecs requests all the extra-specs for the given flavor ID. func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { - _, r.Err = client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) + resp, err := client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { - _, r.Err = client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) + resp, err := client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -264,9 +271,10 @@ func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts C r.Err = err return } - _, r.Err = client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -302,17 +310,19 @@ func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts Up r.Err = err return } - _, r.Err = client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteExtraSpec will delete the key-value pair with the given key for the given // flavor ID. func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { - _, r.Err = client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ + resp, err := client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go index 8f1ef36b34..c51de8dc3a 100644 --- a/openstack/compute/v2/images/requests.go +++ b/openstack/compute/v2/images/requests.go @@ -59,12 +59,14 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // Get returns data about a specific image by its ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified image ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 3f11e440be..01e4df8a8a 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -279,28 +279,32 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(listURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(listURL(client), reqBody, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests that a server previously provisioned be removed from your // account. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ForceDelete forces the deletion of a server. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single server, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -337,9 +341,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -351,7 +356,8 @@ func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword stri "adminPass": newPassword, }, } - _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -405,7 +411,8 @@ func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -464,7 +471,8 @@ func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuild r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, nil) + resp, err := client.Post(actionURL(client, id), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -502,23 +510,26 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ConfirmResize confirms a previous resize operation on a server. // See Resize() for more details. func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 202, 204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RevertResize cancels a previous resize operation on a server. // See Resize() for more details. func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -554,15 +565,17 @@ func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetad r.Err = err return } - _, r.Err = client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Metadata requests all the metadata for the given server ID. func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { - _, r.Err = client.Get(metadataURL(client, id), &r.Body, nil) + resp, err := client.Get(metadataURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -581,9 +594,10 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet r.Err = err return } - _, r.Err = client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -621,23 +635,26 @@ func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts Metadatu r.Err = err return } - _, r.Err = client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Metadatum requests the key-value pair with the given key for the given // server ID. func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { - _, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil) + resp, err := client.Get(metadatumURL(client, id, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMetadatum will delete the key-value pair with the given key for the // given server ID. func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { - _, r.Err = client.Delete(metadatumURL(client, id, key), nil) + resp, err := client.Delete(metadatumURL(client, id, key), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -690,15 +707,15 @@ func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageO resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - r.Err = err - r.Header = resp.Header + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetPassword makes a request against the nova API to get the encrypted // administrative password. func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { - _, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil) + resp, err := client.Get(passwordURL(client, serverId), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -727,8 +744,9 @@ func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowCo r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/container/v1/capsules/requests.go b/openstack/container/v1/capsules/requests.go index 611c27677a..10b14f5757 100644 --- a/openstack/container/v1/capsules/requests.go +++ b/openstack/container/v1/capsules/requests.go @@ -21,9 +21,10 @@ type ListOptsBuilder interface { // Get requests details on a single capsule, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -58,7 +59,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -97,6 +99,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Delete implements Capsule delete request. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/containerinfra/apiversions/requests.go b/openstack/containerinfra/apiversions/requests.go index 2e1f6639b5..244a7acba4 100644 --- a/openstack/containerinfra/apiversions/requests.go +++ b/openstack/containerinfra/apiversions/requests.go @@ -14,6 +14,7 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { // Get will get a specific API version, specified by major ID. func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - _, r.Err = client.Get(getURL(client, v), &r.Body, nil) + resp, err := client.Get(getURL(client, v), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/containerinfra/v1/certificates/requests.go b/openstack/containerinfra/v1/certificates/requests.go index 011546b683..4e02f96183 100644 --- a/openstack/containerinfra/v1/certificates/requests.go +++ b/openstack/containerinfra/v1/certificates/requests.go @@ -1,8 +1,6 @@ package certificates import ( - "net/http" - "github.com/gophercloud/gophercloud" ) @@ -27,11 +25,10 @@ func (opts CreateOpts) ToCertificateCreateMap() (map[string]interface{}, error) // Get makes a request against the API to get details for a certificate. func Get(client *gophercloud.ServiceClient, clusterID string) (r GetResult) { url := getURL(client, clusterID) - - _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -42,24 +39,18 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) - - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Update will rotate the CA certificate for a cluster func Update(client *gophercloud.ServiceClient, clusterID string) (r UpdateResult) { - _, r.Err = client.Patch(updateURL(client, clusterID), nil, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, clusterID), nil, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index c0b21bc148..5fc67d76cb 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -1,8 +1,6 @@ package clusters import ( - "net/http" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -42,33 +40,24 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - var result *http.Response - result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a specific clusters based on its unique ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - if r.Err == nil { - r.Header = result.Header - } + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - var result *http.Response - result, r.Err = client.Delete(deleteURL(client, id), nil) - r.Header = result.Header + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -167,15 +156,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuild } o = append(o, b) } - - var result *http.Response - result, r.Err = client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) - - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -204,14 +188,9 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(resizeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(resizeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) - - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index 3b2aac58cc..252bdbece1 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -1,8 +1,6 @@ package clustertemplates import ( - "net/http" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -54,23 +52,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - var result *http.Response - result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) - - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cluster ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - var result *http.Response - result, r.Err = client.Delete(deleteURL(client, id), nil) - r.Header = result.Header + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -116,11 +108,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves a specific cluster-template based on its unique ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - var result *http.Response - result, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - if r.Err == nil { - r.Header = result.Header - } + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -166,13 +155,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuild } o = append(o, b) } - var result *http.Response - result, r.Err = client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) - - if r.Err == nil { - r.Header = result.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/containerinfra/v1/nodegroups/requests.go b/openstack/containerinfra/v1/nodegroups/requests.go index 56040ad61a..3d694dae2e 100644 --- a/openstack/containerinfra/v1/nodegroups/requests.go +++ b/openstack/containerinfra/v1/nodegroups/requests.go @@ -1,8 +1,6 @@ package nodegroups import ( - "net/http" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -12,11 +10,8 @@ import ( // Use the Extract method of the returned GetResult to extract the // node group from the result. func Get(client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r GetResult) { - var response *http.Response - response, r.Err = client.Get(getURL(client, clusterID, nodeGroupID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - if r.Err == nil { - r.Header = response.Header - } + resp, err := client.Get(getURL(client, clusterID, nodeGroupID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -109,14 +104,8 @@ func Create(client *gophercloud.ServiceClient, clusterID string, opts CreateOpts r.Err = err return } - - var result *http.Response - result, r.Err = client.Post(createURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) - - if r.Err == nil { - r.Header = result.Header - } - + resp, err := client.Post(createURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -161,21 +150,14 @@ func Update(client *gophercloud.ServiceClient, clusterID string, nodeGroupID str } o = append(o, b) } - - var result *http.Response - result, r.Err = client.Patch(updateURL(client, clusterID, nodeGroupID), o, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) - - if r.Err == nil { - r.Header = result.Header - } - + resp, err := client.Patch(updateURL(client, clusterID, nodeGroupID), o, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete makes a request to the Magnum API to delete a node group. func Delete(client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r DeleteResult) { - var result *http.Response - result, r.Err = client.Delete(deleteURL(client, clusterID, nodeGroupID), nil) - r.Header = result.Header + resp, err := client.Delete(deleteURL(client, clusterID, nodeGroupID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/containerinfra/v1/quotas/requests.go b/openstack/containerinfra/v1/quotas/requests.go index 21c5af7b39..f2158c52a4 100644 --- a/openstack/containerinfra/v1/quotas/requests.go +++ b/openstack/containerinfra/v1/quotas/requests.go @@ -1,8 +1,6 @@ package quotas import ( - "net/http" - "github.com/gophercloud/gophercloud" ) @@ -30,14 +28,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - var result *http.Response - result, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) - - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/configurations/requests.go b/openstack/db/v1/configurations/requests.go index 32bfb1dd41..7b4907887e 100644 --- a/openstack/db/v1/configurations/requests.go +++ b/openstack/db/v1/configurations/requests.go @@ -53,13 +53,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will retrieve the details for a specified configuration group. func Get(client *gophercloud.ServiceClient, configID string) (r GetResult) { - _, r.Err = client.Get(resourceURL(client, configID), &r.Body, nil) + resp, err := client.Get(resourceURL(client, configID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -97,7 +99,8 @@ func Update(client *gophercloud.ServiceClient, configID string, opts UpdateOptsB r.Err = err return } - _, r.Err = client.Patch(resourceURL(client, configID), &b, nil, nil) + resp, err := client.Patch(resourceURL(client, configID), &b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -110,7 +113,8 @@ func Replace(client *gophercloud.ServiceClient, configID string, opts UpdateOpts r.Err = err return } - _, r.Err = client.Put(resourceURL(client, configID), &b, nil, nil) + resp, err := client.Put(resourceURL(client, configID), &b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -118,7 +122,8 @@ func Replace(client *gophercloud.ServiceClient, configID string, opts UpdateOpts // config groups cannot be deleted whilst still attached to running instances - // you must detach and then delete them. func Delete(client *gophercloud.ServiceClient, configID string) (r DeleteResult) { - _, r.Err = client.Delete(resourceURL(client, configID), nil) + resp, err := client.Delete(resourceURL(client, configID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -147,7 +152,8 @@ func ListDatastoreParams(client *gophercloud.ServiceClient, datastoreID, version // need the param's ID first, which can be attained by using the ListDatastoreParams // operation. func GetDatastoreParam(client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) (r ParamResult) { - _, r.Err = client.Get(getDSParamURL(client, datastoreID, versionID, paramID), &r.Body, nil) + resp, err := client.Get(getDSParamURL(client, datastoreID, versionID, paramID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -162,6 +168,7 @@ func ListGlobalParams(client *gophercloud.ServiceClient, versionID string) pagin // GetGlobalParam is similar to GetDatastoreParam but does not require a // DatastoreID. func GetGlobalParam(client *gophercloud.ServiceClient, versionID, paramID string) (r ParamResult) { - _, r.Err = client.Get(getGlobalParamURL(client, versionID, paramID), &r.Body, nil) + resp, err := client.Get(getGlobalParamURL(client, versionID, paramID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/databases/requests.go b/openstack/db/v1/databases/requests.go index ef5394f9c6..908204ac25 100644 --- a/openstack/db/v1/databases/requests.go +++ b/openstack/db/v1/databases/requests.go @@ -68,7 +68,8 @@ func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOpt r.Err = err return } - _, r.Err = client.Post(baseURL(client, instanceID), &b, nil, nil) + resp, err := client.Post(baseURL(client, instanceID), &b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -84,6 +85,7 @@ func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager // Delete will permanently delete the database within a specified instance. // All contained data inside the database will also be permanently deleted. func Delete(client *gophercloud.ServiceClient, instanceID, dbName string) (r DeleteResult) { - _, r.Err = client.Delete(dbURL(client, instanceID, dbName), nil) + resp, err := client.Delete(dbURL(client, instanceID, dbName), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/datastores/requests.go b/openstack/db/v1/datastores/requests.go index 134e309076..bca717d810 100644 --- a/openstack/db/v1/datastores/requests.go +++ b/openstack/db/v1/datastores/requests.go @@ -14,7 +14,8 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // Get will retrieve the details of a specified datastore type. func Get(client *gophercloud.ServiceClient, datastoreID string) (r GetResult) { - _, r.Err = client.Get(resourceURL(client, datastoreID), &r.Body, nil) + resp, err := client.Get(resourceURL(client, datastoreID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -28,6 +29,7 @@ func ListVersions(client *gophercloud.ServiceClient, datastoreID string) paginat // GetVersion will retrieve the details of a specified datastore version. func GetVersion(client *gophercloud.ServiceClient, datastoreID, versionID string) (r GetVersionResult) { - _, r.Err = client.Get(versionURL(client, datastoreID, versionID), &r.Body, nil) + resp, err := client.Get(versionURL(client, datastoreID, versionID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/flavors/requests.go b/openstack/db/v1/flavors/requests.go index 7fac56ae60..7946f14fa8 100644 --- a/openstack/db/v1/flavors/requests.go +++ b/openstack/db/v1/flavors/requests.go @@ -16,6 +16,7 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // Get will retrieve information for a specified hardware flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go index bbd9093182..f5243e70b1 100644 --- a/openstack/db/v1/instances/requests.go +++ b/openstack/db/v1/instances/requests.go @@ -140,7 +140,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -153,20 +154,23 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // Get retrieves the status and information for a specified database instance. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) + resp, err := client.Get(resourceURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete permanently destroys the database instance. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(resourceURL(client, id), nil) + resp, err := client.Delete(resourceURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // EnableRootUser enables the login from any host for the root user and // provides the user with a generated root password. func EnableRootUser(client *gophercloud.ServiceClient, id string) (r EnableRootUserResult) { - _, r.Err = client.Post(userRootURL(client, id), nil, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Post(userRootURL(client, id), nil, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -174,7 +178,8 @@ func EnableRootUser(client *gophercloud.ServiceClient, id string) (r EnableRootU // True if root user is enabled for the specified database instance or False // otherwise. func IsRootEnabled(client *gophercloud.ServiceClient, id string) (r IsRootEnabledResult) { - _, r.Err = client.Get(userRootURL(client, id), &r.Body, nil) + resp, err := client.Get(userRootURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -183,7 +188,8 @@ func IsRootEnabled(client *gophercloud.ServiceClient, id string) (r IsRootEnable // The MySQL service will be unavailable until the instance restarts. func Restart(client *gophercloud.ServiceClient, id string) (r ActionResult) { b := map[string]interface{}{"restart": struct{}{}} - _, r.Err = client.Post(actionURL(client, id), &b, nil, nil) + resp, err := client.Post(actionURL(client, id), &b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -191,7 +197,8 @@ func Restart(client *gophercloud.ServiceClient, id string) (r ActionResult) { // flavorRef is provided. It will also restart the MySQL service. func Resize(client *gophercloud.ServiceClient, id, flavorRef string) (r ActionResult) { b := map[string]interface{}{"resize": map[string]string{"flavorRef": flavorRef}} - _, r.Err = client.Post(actionURL(client, id), &b, nil, nil) + resp, err := client.Post(actionURL(client, id), &b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -200,20 +207,23 @@ func Resize(client *gophercloud.ServiceClient, id, flavorRef string) (r ActionRe // The volume size is in gigabytes (GB) and must be an integer. func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) (r ActionResult) { b := map[string]interface{}{"resize": map[string]interface{}{"volume": map[string]int{"size": size}}} - _, r.Err = client.Post(actionURL(client, id), &b, nil, nil) + resp, err := client.Post(actionURL(client, id), &b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // AttachConfigurationGroup will attach configuration group to the instance func AttachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string, configID string) (r ConfigurationResult) { b := map[string]interface{}{"instance": map[string]interface{}{"configuration": configID}} - _, r.Err = client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DetachConfigurationGroup will dettach configuration group from the instance func DetachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string) (r ConfigurationResult) { b := map[string]interface{}{"instance": map[string]interface{}{}} - _, r.Err = client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/users/requests.go b/openstack/db/v1/users/requests.go index d342de3447..4d107e490b 100644 --- a/openstack/db/v1/users/requests.go +++ b/openstack/db/v1/users/requests.go @@ -71,7 +71,8 @@ func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOpt r.Err = err return } - _, r.Err = client.Post(baseURL(client, instanceID), &b, nil, nil) + resp, err := client.Post(baseURL(client, instanceID), &b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -86,6 +87,7 @@ func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager // Delete will permanently delete a user from a specified database instance. func Delete(client *gophercloud.ServiceClient, instanceID, userName string) (r DeleteResult) { - _, r.Err = client.Delete(userURL(client, instanceID, userName), nil) + resp, err := client.Delete(userURL(client, instanceID, userName), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/dns/v2/recordsets/requests.go b/openstack/dns/v2/recordsets/requests.go index 1d9a77bcf5..58414bc0dd 100644 --- a/openstack/dns/v2/recordsets/requests.go +++ b/openstack/dns/v2/recordsets/requests.go @@ -57,7 +57,8 @@ func ListByZone(client *gophercloud.ServiceClient, zoneID string, opts ListOptsB // Get implements the recordset Get request. func Get(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) { - _, r.Err = client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) + resp, err := client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -103,9 +104,10 @@ func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBui r.Err = err return } - _, r.Err = client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -158,16 +160,18 @@ func Update(client *gophercloud.ServiceClient, zoneID string, rrsetID string, op r.Err = err return } - _, r.Err = client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete removes an existing RecordSet. func Delete(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) { - _, r.Err = client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ + resp, err := client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/dns/v2/zones/requests.go b/openstack/dns/v2/zones/requests.go index 78b08ae4f8..7fa25359ff 100644 --- a/openstack/dns/v2/zones/requests.go +++ b/openstack/dns/v2/zones/requests.go @@ -55,7 +55,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get returns information about a zone, given its ID. func Get(client *gophercloud.ServiceClient, zoneID string) (r GetResult) { - _, r.Err = client.Get(zoneURL(client, zoneID), &r.Body, nil) + resp, err := client.Get(zoneURL(client, zoneID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -110,9 +111,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -158,17 +160,19 @@ func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBui r.Err = err return } - _, r.Err = client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete implements a zone delete request. func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { - _, r.Err = client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ + resp, err := client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ OkCodes: []int{202}, JSONResponse: &r.Body, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v2/extensions/admin/roles/requests.go b/openstack/identity/v2/extensions/admin/roles/requests.go index 50228c9066..2d707902a5 100644 --- a/openstack/identity/v2/extensions/admin/roles/requests.go +++ b/openstack/identity/v2/extensions/admin/roles/requests.go @@ -17,9 +17,10 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // a user. This is confined to the scope of the user's tenant - so the tenant // ID is a required argument. func AddUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { - _, r.Err = client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -27,6 +28,7 @@ func AddUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) // from a user. This is confined to the scope of the user's tenant - so the // tenant ID is a required argument. func DeleteUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { - _, r.Err = client.Delete(userRoleURL(client, tenantID, userID, roleID), nil) + resp, err := client.Delete(userRoleURL(client, tenantID, userID, roleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v2/tenants/requests.go b/openstack/identity/v2/tenants/requests.go index f21a58f10c..f16df38e5e 100644 --- a/openstack/identity/v2/tenants/requests.go +++ b/openstack/identity/v2/tenants/requests.go @@ -60,15 +60,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single tenant by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -103,14 +105,16 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete is the operation responsible for permanently deleting a tenant. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index ab32368cc6..2b64f108cb 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -87,17 +87,19 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r Creat r.Err = err return } - _, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, MoreHeaders: map[string]string{"X-Auth-Token": ""}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get validates and retrieves information for user's token. func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { - _, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v2/users/requests.go b/openstack/identity/v2/users/requests.go index 0081e8a9f2..57c566b577 100644 --- a/openstack/identity/v2/users/requests.go +++ b/openstack/identity/v2/users/requests.go @@ -59,15 +59,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single user, either by ID or Name. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(ResourceURL(client, id), &r.Body, nil) + resp, err := client.Get(ResourceURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -93,15 +95,17 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(ResourceURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ResourceURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete is the operation responsible for permanently deleting a User. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(ResourceURL(client, id), nil) + resp, err := client.Delete(ResourceURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/applicationcredentials/requests.go b/openstack/identity/v3/applicationcredentials/requests.go index 218a925cf0..2f21464645 100644 --- a/openstack/identity/v3/applicationcredentials/requests.go +++ b/openstack/identity/v3/applicationcredentials/requests.go @@ -40,7 +40,8 @@ func List(client *gophercloud.ServiceClient, userID string, opts ListOptsBuilder // Get retrieves details on a single user, by ID. func Get(client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, userID, id), &r.Body, nil) + resp, err := client.Get(getURL(client, userID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -84,15 +85,17 @@ func Create(client *gophercloud.ServiceClient, userID string, opts CreateOptsBui r.Err = err return } - _, r.Err = client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes an application credential. func Delete(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, userID, id), nil) + resp, err := client.Delete(deleteURL(client, userID, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -106,12 +109,14 @@ func ListAccessRules(client *gophercloud.ServiceClient, userID string) paginatio // GetAccessRule retrieves details on a single access rule by ID. func GetAccessRule(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessRuleResult) { - _, r.Err = client.Get(getAccessRuleURL(client, userID, id), &r.Body, nil) + resp, err := client.Get(getAccessRuleURL(client, userID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteAccessRule deletes an access rule. func DeleteAccessRule(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteAccessRuleURL(client, userID, id), nil) + resp, err := client.Delete(deleteAccessRuleURL(client, userID, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/credentials/requests.go b/openstack/identity/v3/credentials/requests.go index bf36a95521..64dd1bde78 100644 --- a/openstack/identity/v3/credentials/requests.go +++ b/openstack/identity/v3/credentials/requests.go @@ -42,7 +42,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details on a single user, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -76,15 +77,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a credential. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -118,8 +121,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(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/domains/requests.go b/openstack/identity/v3/domains/requests.go index 215af0fca1..78847c8794 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -43,7 +43,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details on a single domain, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -77,15 +78,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a domain. func Delete(client *gophercloud.ServiceClient, domainID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, domainID), nil) + resp, err := client.Delete(deleteURL(client, domainID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -119,8 +122,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(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/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index ebcd755a6a..4eba57cd9f 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -42,7 +42,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(listURL(client), &b, &r.Body, nil) + resp, err := client.Post(listURL(client), &b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -125,12 +126,14 @@ func Update(client *gophercloud.ServiceClient, endpointID string, opts UpdateOpt r.Err = err return } - _, r.Err = client.Patch(endpointURL(client, endpointID), &b, &r.Body, nil) + resp, err := client.Patch(endpointURL(client, endpointID), &b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete removes an endpoint from the service catalog. func Delete(client *gophercloud.ServiceClient, endpointID string) (r DeleteResult) { - _, r.Err = client.Delete(endpointURL(client, endpointID), nil) + resp, err := client.Delete(endpointURL(client, endpointID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/extensions/ec2credentials/requests.go b/openstack/identity/v3/extensions/ec2credentials/requests.go index 86fe41cf3d..93c44f48af 100644 --- a/openstack/identity/v3/extensions/ec2credentials/requests.go +++ b/openstack/identity/v3/extensions/ec2credentials/requests.go @@ -15,7 +15,8 @@ func List(client *gophercloud.ServiceClient, userID string) pagination.Pager { // Get retrieves details on a single EC2 credential by ID. func Get(client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, userID, id), &r.Body, nil) + resp, err := client.Get(getURL(client, userID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -37,14 +38,16 @@ func Create(client *gophercloud.ServiceClient, userID string, opts CreateOpts) ( r.Err = err return } - _, r.Err = client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes an EC2 credential. func Delete(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, userID, id), nil) + resp, err := client.Delete(deleteURL(client, userID, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go index f3261b9e0a..bcf5a8fb0f 100644 --- a/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -295,10 +295,7 @@ func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tok MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) - r.Err = err - if resp != nil { - r.Header = resp.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -318,10 +315,7 @@ func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilde MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) - r.Err = err - if resp != nil { - r.Header = resp.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index 7ad86e2413..7b716639c7 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -124,15 +124,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a trust. func Delete(client *gophercloud.ServiceClient, trustID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, trustID), nil) + resp, err := client.Delete(deleteURL(client, trustID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -153,6 +155,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details on a single trust, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(resourceURL(client, id), &r.Body, nil) + resp, err := client.Get(resourceURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index 032b544804..013cf62d29 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -65,7 +65,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details on a single group, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -115,9 +116,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -167,14 +169,16 @@ func Update(client *gophercloud.ServiceClient, groupID string, opts UpdateOptsBu r.Err = err return } - _, r.Err = client.Patch(updateURL(client, groupID), &b, &r.Body, &gophercloud.RequestOpts{ + 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 a group. func Delete(client *gophercloud.ServiceClient, groupID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, groupID), nil) + resp, err := client.Delete(deleteURL(client, groupID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index c9640b2278..a06b931bca 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -116,15 +116,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details on a single policy, by ID. func Get(client *gophercloud.ServiceClient, policyID string) (r GetResult) { - _, r.Err = client.Get(getURL(client, policyID), &r.Body, nil) + resp, err := client.Get(getURL(client, policyID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -182,14 +184,16 @@ func Update(client *gophercloud.ServiceClient, policyID string, opts UpdateOptsB r.Err = err return } - _, r.Err = client.Patch(updateURL(client, policyID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, policyID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a policy. func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, policyID), nil) + resp, err := client.Delete(deleteURL(client, policyID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index e7fde5129c..999da0aeae 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -87,7 +87,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details on a single project, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -133,13 +134,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, nil) + resp, err := client.Post(createURL(client), &b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a project. func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, projectID), nil) + resp, err := client.Delete(deleteURL(client, projectID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -185,8 +188,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(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/regions/requests.go b/openstack/identity/v3/regions/requests.go index b5889ad3ec..d120381ab9 100644 --- a/openstack/identity/v3/regions/requests.go +++ b/openstack/identity/v3/regions/requests.go @@ -40,7 +40,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details on a single region, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -90,9 +91,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -151,14 +153,16 @@ func Update(client *gophercloud.ServiceClient, regionID string, opts UpdateOptsB r.Err = err return } - _, r.Err = client.Patch(updateURL(client, regionID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, regionID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a region. func Delete(client *gophercloud.ServiceClient, regionID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, regionID), nil) + resp, err := client.Delete(deleteURL(client, regionID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 573db9ea20..007a15b9cc 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -66,7 +66,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details on a single role, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -113,9 +114,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -159,15 +161,17 @@ func Update(client *gophercloud.ServiceClient, roleID string, opts UpdateOptsBui r.Err = err return } - _, r.Err = client.Patch(updateURL(client, roleID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, roleID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a role. func Delete(client *gophercloud.ServiceClient, roleID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, roleID), nil) + resp, err := client.Delete(deleteURL(client, roleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -348,9 +352,10 @@ func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) ( actorType = "groups" } - _, r.Err = client.Put(assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ + 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 } @@ -385,8 +390,9 @@ func Unassign(client *gophercloud.ServiceClient, roleID string, opts UnassignOpt actorType = "groups" } - _, r.Err = client.Delete(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ + 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/services/requests.go b/openstack/identity/v3/services/requests.go index 1a8b35232a..6d3c70c51d 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -48,9 +48,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -92,7 +93,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get returns additional information about a service, given its ID. func Get(client *gophercloud.ServiceClient, serviceID string) (r GetResult) { - _, r.Err = client.Get(serviceURL(client, serviceID), &r.Body, nil) + resp, err := client.Get(serviceURL(client, serviceID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -139,9 +141,10 @@ func Update(client *gophercloud.ServiceClient, serviceID string, opts UpdateOpts r.Err = err return } - _, r.Err = client.Patch(updateURL(client, serviceID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, serviceID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -149,6 +152,7 @@ func Update(client *gophercloud.ServiceClient, serviceID string, opts UpdateOpts // It either deletes all associated endpoints, or fails until all endpoints // are deleted. func Delete(client *gophercloud.ServiceClient, serviceID string) (r DeleteResult) { - _, r.Err = client.Delete(serviceURL(client, serviceID), nil) + resp, err := client.Delete(serviceURL(client, serviceID), nil) + _, 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 72c15bf00a..c345cac684 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -130,10 +130,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": ""}, }) - r.Err = err - if resp != nil { - r.Header = resp.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -143,10 +140,7 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 203}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -165,8 +159,9 @@ func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { // Revoke immediately makes specified token invalid. func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { - _, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ + resp, err := c.Delete(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 7a40ec762d..0f1eb117bd 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -1,7 +1,6 @@ package users import ( - "net/http" "net/url" "strings" @@ -95,7 +94,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details on a single user, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -157,9 +157,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -221,9 +222,10 @@ func Update(client *gophercloud.ServiceClient, userID string, opts UpdateOptsBui r.Err = err return } - _, r.Err = client.Patch(updateURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -260,15 +262,17 @@ func ChangePassword(client *gophercloud.ServiceClient, userID string, opts Chang return } - _, r.Err = client.Post(changePasswordURL(client, userID), &b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(changePasswordURL(client, userID), &b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a user. func Delete(client *gophercloud.ServiceClient, userID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, userID), nil) + resp, err := client.Delete(deleteURL(client, userID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -283,34 +287,35 @@ func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pag // AddToGroup adds a user to a group. func AddToGroup(client *gophercloud.ServiceClient, groupID, userID string) (r AddToGroupResult) { url := addToGroupURL(client, groupID, userID) - _, r.Err = client.Put(url, nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // IsMemberOfGroup checks whether a user belongs to a group. func IsMemberOfGroup(client *gophercloud.ServiceClient, groupID, userID string) (r IsMemberOfGroupResult) { url := isMemberOfGroupURL(client, groupID, userID) - var response *http.Response - response, r.Err = client.Head(url, &gophercloud.RequestOpts{ + resp, err := client.Head(url, &gophercloud.RequestOpts{ OkCodes: []int{204, 404}, }) - if r.Err == nil && response != nil { - if (*response).StatusCode == 204 { + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + if r.Err == nil { + if resp.StatusCode == 204 { r.isMember = true } } - return } // RemoveFromGroup removes a user from a group. func RemoveFromGroup(client *gophercloud.ServiceClient, groupID, userID string) (r RemoveFromGroupResult) { url := removeFromGroupURL(client, groupID, userID) - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/imageservice/v2/imagedata/requests.go b/openstack/imageservice/v2/imagedata/requests.go index 545c561f36..087794f7c7 100644 --- a/openstack/imageservice/v2/imagedata/requests.go +++ b/openstack/imageservice/v2/imagedata/requests.go @@ -2,17 +2,17 @@ package imagedata import ( "io" - "net/http" "github.com/gophercloud/gophercloud" ) // Upload uploads an image file. func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { - _, r.Err = client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -20,20 +20,17 @@ func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r Upl // the provided file. // Existing image object must be in the "queued" status. func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { - _, r.Err = client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Download retrieves an image. func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { - var resp *http.Response - resp, r.Err = client.Get(downloadURL(client, id), nil, nil) - if resp != nil { - r.Body = resp.Body - r.Header = resp.Header - } + resp, err := client.Get(downloadURL(client, id), nil, nil) + r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/imageservice/v2/imageimport/requests.go b/openstack/imageservice/v2/imageimport/requests.go index 38920051b3..118d36ea8c 100644 --- a/openstack/imageservice/v2/imageimport/requests.go +++ b/openstack/imageservice/v2/imageimport/requests.go @@ -15,7 +15,8 @@ const ( // Get retrieves Import API information data. func Get(c *gophercloud.ServiceClient) (r GetResult) { - _, r.Err = c.Get(infoURL(c), &r.Body, nil) + resp, err := c.Get(infoURL(c), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -46,8 +47,9 @@ func Create(client *gophercloud.ServiceClient, imageID string, opts CreateOptsBu r.Err = err return } - _, r.Err = client.Post(importURL(client, imageID), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(importURL(client, imageID), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index d0a9eb572a..f0cd5cbde8 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -206,19 +206,22 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return r } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete implements image delete request. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get implements image get request. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -229,10 +232,11 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return r } - _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, MoreHeaders: map[string]string{"Content-Type": "application/openstack-images-v2.1-json-patch"}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/imageservice/v2/members/requests.go b/openstack/imageservice/v2/members/requests.go index b80e54e83c..e7dc42b15c 100644 --- a/openstack/imageservice/v2/members/requests.go +++ b/openstack/imageservice/v2/members/requests.go @@ -25,9 +25,10 @@ import ( */ func Create(client *gophercloud.ServiceClient, id string, member string) (r CreateResult) { b := map[string]interface{}{"member": member} - _, r.Err = client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -40,13 +41,15 @@ func List(client *gophercloud.ServiceClient, id string) pagination.Pager { // Get image member details. func Get(client *gophercloud.ServiceClient, imageID string, memberID string) (r DetailsResult) { - _, r.Err = client.Get(getMemberURL(client, imageID, memberID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Get(getMemberURL(client, imageID, memberID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete membership for given image. Callee should be image owner. func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204}}) + resp, err := client.Delete(deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -75,7 +78,8 @@ func Update(client *gophercloud.ServiceClient, imageID string, memberID string, r.Err = err return } - _, r.Err = client.Put(updateMemberURL(client, imageID, memberID), b, &r.Body, + resp, err := client.Put(updateMemberURL(client, imageID, memberID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/imageservice/v2/tasks/requests.go b/openstack/imageservice/v2/tasks/requests.go index 94fe45d9c5..d0a2be1fa2 100644 --- a/openstack/imageservice/v2/tasks/requests.go +++ b/openstack/imageservice/v2/tasks/requests.go @@ -90,7 +90,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific Imageservice task based on its ID. func Get(c *gophercloud.ServiceClient, taskID string) (r GetResult) { - _, r.Err = c.Get(getURL(c, taskID), &r.Body, nil) + resp, err := c.Get(getURL(c, taskID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -121,8 +122,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/keymanager/v1/acls/requests.go b/openstack/keymanager/v1/acls/requests.go index 5c5daf9430..2eb3b58038 100644 --- a/openstack/keymanager/v1/acls/requests.go +++ b/openstack/keymanager/v1/acls/requests.go @@ -6,13 +6,15 @@ import ( // GetContainerACL retrieves the ACL of a container. func GetContainerACL(client *gophercloud.ServiceClient, containerID string) (r ACLResult) { - _, r.Err = client.Get(containerURL(client, containerID), &r.Body, nil) + resp, err := client.Get(containerURL(client, containerID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetSecretACL retrieves the ACL of a secret. func GetSecretACL(client *gophercloud.ServiceClient, secretID string) (r ACLResult) { - _, r.Err = client.Get(secretURL(client, secretID), &r.Body, nil) + resp, err := client.Get(secretURL(client, secretID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -58,9 +60,10 @@ func SetContainerACL(client *gophercloud.ServiceClient, containerID string, opts return } - _, r.Err = client.Put(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -72,9 +75,10 @@ func SetSecretACL(client *gophercloud.ServiceClient, secretID string, opts SetOp return } - _, r.Err = client.Put(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -86,9 +90,10 @@ func UpdateContainerACL(client *gophercloud.ServiceClient, containerID string, o return } - _, r.Err = client.Patch(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -100,24 +105,27 @@ func UpdateSecretACL(client *gophercloud.ServiceClient, secretID string, opts Se return } - _, r.Err = client.Patch(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteContainerACL will delete an ACL from a conatiner. func DeleteContainerACL(client *gophercloud.ServiceClient, containerID string) (r DeleteResult) { - _, r.Err = client.Delete(containerURL(client, containerID), &gophercloud.RequestOpts{ + resp, err := client.Delete(containerURL(client, containerID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteSecretACL will delete an ACL from a secret. func DeleteSecretACL(client *gophercloud.ServiceClient, secretID string) (r DeleteResult) { - _, r.Err = client.Delete(secretURL(client, secretID), &gophercloud.RequestOpts{ + resp, err := client.Delete(secretURL(client, secretID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/keymanager/v1/containers/requests.go b/openstack/keymanager/v1/containers/requests.go index 189b6c501c..4d55a7aa8c 100644 --- a/openstack/keymanager/v1/containers/requests.go +++ b/openstack/keymanager/v1/containers/requests.go @@ -55,7 +55,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details of a container. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -89,15 +90,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a container. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -166,9 +169,10 @@ func CreateConsumer(client *gophercloud.ServiceClient, containerID string, opts r.Err = err return } - _, r.Err = client.Post(createConsumerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createConsumerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -203,11 +207,12 @@ func DeleteConsumer(client *gophercloud.ServiceClient, containerID string, opts return } - _, r.Err = client.Request("DELETE", url, &gophercloud.RequestOpts{ + resp, err := client.Request("DELETE", url, &gophercloud.RequestOpts{ JSONBody: b, JSONResponse: &r.Body, OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -230,9 +235,10 @@ func CreateSecretRef(client *gophercloud.ServiceClient, containerID string, opts r.Err = err return } - _, r.Err = client.Post(createSecretRefURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createSecretRefURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -246,9 +252,10 @@ func DeleteSecretRef(client *gophercloud.ServiceClient, containerID string, opts return } - _, r.Err = client.Request("DELETE", url, &gophercloud.RequestOpts{ + resp, err := client.Request("DELETE", url, &gophercloud.RequestOpts{ JSONBody: b, OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/keymanager/v1/orders/requests.go b/openstack/keymanager/v1/orders/requests.go index 7a55f9ddb8..063189ef80 100644 --- a/openstack/keymanager/v1/orders/requests.go +++ b/openstack/keymanager/v1/orders/requests.go @@ -53,7 +53,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details of a orders. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -116,14 +117,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a orders. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/keymanager/v1/secrets/requests.go b/openstack/keymanager/v1/secrets/requests.go index 1b1dfc5b82..18f8b25177 100644 --- a/openstack/keymanager/v1/secrets/requests.go +++ b/openstack/keymanager/v1/secrets/requests.go @@ -143,7 +143,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves details of a secrets. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -183,12 +184,7 @@ func GetPayload(client *gophercloud.ServiceClient, id string, opts GetPayloadOpt MoreHeaders: h, OkCodes: []int{200}, }) - - if resp != nil { - r.Header = resp.Header - r.Body = resp.Body - } - r.Err = err + r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -249,15 +245,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a secrets. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -310,22 +308,18 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder b = payload } - resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ - RawBody: strings.NewReader(b), + resp, err := client.Put(url, strings.NewReader(b), nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{204}, }) - r.Err = err - if resp != nil { - r.Header = resp.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadata will list metadata for a given secret. func GetMetadata(client *gophercloud.ServiceClient, secretID string) (r MetadataResult) { - _, r.Err = client.Get(metadataURL(client, secretID), &r.Body, nil) + resp, err := client.Get(metadataURL(client, secretID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -350,15 +344,17 @@ func CreateMetadata(client *gophercloud.ServiceClient, secretID string, opts Cre r.Err = err return } - _, r.Err = client.Put(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadatum will get a single key/value metadata from a secret. func GetMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumResult) { - _, r.Err = client.Get(metadatumURL(client, secretID, key), &r.Body, nil) + resp, err := client.Get(metadatumURL(client, secretID, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -386,9 +382,10 @@ func CreateMetadatum(client *gophercloud.ServiceClient, secretID string, opts Cr r.Err = err return } - _, r.Err = client.Post(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -411,14 +408,16 @@ func UpdateMetadatum(client *gophercloud.ServiceClient, secretID string, opts Up r.Err = err return } - _, r.Err = client.Put(metadatumURL(client, secretID, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(metadatumURL(client, secretID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMetadatum will delete an individual metadatum from a secret. func DeleteMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumDeleteResult) { - _, r.Err = client.Delete(metadatumURL(client, secretID, key), nil) + resp, err := client.Delete(metadatumURL(client, secretID, key), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/keymanager/v1/secrets/testing/fixtures.go b/openstack/keymanager/v1/secrets/testing/fixtures.go index f62fa83608..eb3e8ddafb 100644 --- a/openstack/keymanager/v1/secrets/testing/fixtures.go +++ b/openstack/keymanager/v1/secrets/testing/fixtures.go @@ -259,6 +259,7 @@ func HandleUpdateSecretSuccessfully(t *testing.T) { th.Mux.HandleFunc("/secrets/1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestBody(t, r, `foobar`) w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusNoContent) diff --git a/openstack/loadbalancer/v2/amphorae/requests.go b/openstack/loadbalancer/v2/amphorae/requests.go index 7e5377453f..4b4fd0a90c 100644 --- a/openstack/loadbalancer/v2/amphorae/requests.go +++ b/openstack/loadbalancer/v2/amphorae/requests.go @@ -54,14 +54,16 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a particular amphora based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Failover performs a failover of an amphora. func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { - _, r.Err = c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ + resp, err := c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index fc1c809afa..19f487450f 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -80,7 +80,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -137,13 +138,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a particular l7policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular l7policy based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -208,9 +211,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -254,7 +258,8 @@ func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOp r.Err = err return } - _, r.Err = c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + resp, err := c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -309,13 +314,15 @@ func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOpts // GetRule retrieves a particular L7Policy Rule based on its unique ID. func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { - _, r.Err = c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) + resp, err := c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will remove a Rule from a particular L7Policy. func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { - _, r.Err = c.Delete(ruleResourceURL(c, policyID, ruleID), nil) + resp, err := c.Delete(ruleResourceURL(c, policyID, ruleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -369,8 +376,9 @@ func UpdateRule(c *gophercloud.ServiceClient, policyID string, ruleID string, op r.Err = err return } - _, r.Err = c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 2d2f71f24f..a90d726273 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -154,13 +154,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Listeners based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -235,20 +237,23 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + 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 Listeners based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular Listeners. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { - _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) + resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index f32a7f9853..4c7c288942 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -133,13 +133,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Loadbalancer based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -179,9 +181,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + 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 } @@ -216,26 +219,30 @@ func Delete(c *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r } url += query } - _, r.Err = c.Delete(url, nil) + resp, err := c.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { - _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) + resp, err := c.Get(statusRootURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular LoadBalancer. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { - _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) + resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Failover performs a failover of a load balancer. func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { - _, r.Err = c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ + resp, err := c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index 1a7e8b8f48..aedf672fc1 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -166,13 +166,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Health Monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -235,14 +237,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + 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 Monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 04df4ddb85..c522cb41d1 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -130,13 +130,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,15 +179,17 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -298,13 +302,15 @@ func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMember r.Err = err return } - _, r.Err = c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + resp, err := c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMember retrieves a particular Pool Member based on its unique ID. func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { - _, r.Err = c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) + resp, err := c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -343,9 +349,10 @@ func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, r.Err = err return } - _, r.Err = c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -413,13 +420,15 @@ func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []Batc b := map[string]interface{}{"members": members} - _, r.Err = c.Put(memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := c.Put(memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DisassociateMember will remove and disassociate a Member from a particular // Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { - _, r.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil) + resp, err := c.Delete(memberResourceURL(c, poolID, memberID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index d3da9889df..cfd39d0a31 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -1,8 +1,6 @@ package claims import ( - "net/http" - "github.com/gophercloud/gophercloud" ) @@ -52,10 +50,10 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts url += q } - var resp *http.Response - resp, r.Err = client.Post(url, b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(url, b, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) if r.Err != nil { return } @@ -70,9 +68,10 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts // Get queries the specified claim for the specified queue. func Get(client *gophercloud.ServiceClient, queueName string, claimID string) (r GetResult) { - _, r.Err = client.Get(getURL(client, queueName, claimID), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, queueName, claimID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -108,16 +107,18 @@ func Update(client *gophercloud.ServiceClient, queueName string, claimID string, r.Err = err return r } - _, r.Err = client.Patch(updateURL(client, queueName, claimID), &b, nil, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, queueName, claimID), &b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete a Claim for a specified Queue. func Delete(client *gophercloud.ServiceClient, queueName string, claimID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, queueName, claimID), &gophercloud.RequestOpts{ + resp, err := client.Delete(deleteURL(client, queueName, claimID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index 6aac4ac35c..0ccc24cfab 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -98,9 +98,10 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts return } - _, r.Err = client.Post(createURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -132,9 +133,10 @@ func DeleteMessages(client *gophercloud.ServiceClient, queueName string, opts De } url += query } - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{200, 204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -203,17 +205,19 @@ func GetMessages(client *gophercloud.ServiceClient, queueName string, opts GetMe } url += query } - _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single message, by ID. func Get(client *gophercloud.ServiceClient, queueName string, messageID string) (r GetResult) { - _, r.Err = client.Get(messageURL(client, queueName, messageID), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(messageURL(client, queueName, messageID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -246,8 +250,9 @@ func Delete(client *gophercloud.ServiceClient, queueName string, messageID strin } url += query } - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 6ef53947d7..b150df436b 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -116,9 +116,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create queueName := b["queue_name"].(string) delete(b, "queue_name") - _, r.Err = client.Put(createURL(client, queueName), b, r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(createURL(client, queueName), b, r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -166,34 +167,39 @@ func (opts UpdateOpts) ToMap() (map[string]interface{}, error) { // Update Updates the specified queue. func Update(client *gophercloud.ServiceClient, queueName string, opts UpdateOptsBuilder) (r UpdateResult) { - _, r.Err = client.Patch(updateURL(client, queueName), opts, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(updateURL(client, queueName), opts, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 204}, MoreHeaders: map[string]string{ "Content-Type": "application/openstack-messaging-v2.0-json-patch"}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single queue, by name. func Get(client *gophercloud.ServiceClient, queueName string) (r GetResult) { - _, r.Err = client.Get(getURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified queue. func Delete(client *gophercloud.ServiceClient, queueName string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, queueName), &gophercloud.RequestOpts{ + resp, err := client.Delete(deleteURL(client, queueName), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats returns statistics for the specified queue. func GetStats(client *gophercloud.ServiceClient, queueName string) (r StatResult) { - _, r.Err = client.Get(statURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}}) + resp, err := client.Get(statURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -243,9 +249,10 @@ func Share(client *gophercloud.ServiceClient, queueName string, opts ShareOptsBu r.Err = err return r } - _, r.Err = client.Post(shareURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(shareURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -285,8 +292,9 @@ func Purge(client *gophercloud.ServiceClient, queueName string, opts PurgeOptsBu return r } - _, r.Err = client.Post(purgeURL(client, queueName), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(purgeURL(client, queueName), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 3d5d77f741..730b5e6f66 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -61,13 +61,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific agent based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListDHCPNetworks returns a list of networks scheduled to a specific // dhcp agent func ListDHCPNetworks(c *gophercloud.ServiceClient, id string) (r ListDHCPNetworksResult) { - _, r.Err = c.Get(listDHCPNetworksURL(c, id), &r.Body, nil) + resp, err := c.Get(listDHCPNetworksURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index a08bccbb68..6d49aeaf6c 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -29,53 +29,59 @@ func ReplaceAll(client *gophercloud.ServiceClient, resourceType string, resource r.Err = err return } - _, r.Err = client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // List all tags on a resource func List(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r ListResult) { url := listURL(client, resourceType, resourceID) - _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteAll deletes all tags on a resource func DeleteAll(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r DeleteResult) { url := deleteAllURL(client, resourceType, resourceID) - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Add a tag on a resource func Add(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r AddResult) { url := addURL(client, resourceType, resourceID, tag) - _, r.Err = client.Put(url, nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete a tag on a resource func Delete(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r DeleteResult) { url := deleteURL(client, resourceType, resourceID, tag) - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Confirm if a tag exists on a resource func Confirm(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r ConfirmResult) { url := confirmURL(client, resourceType, resourceID, tag) - _, r.Err = client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go index c710f94a8d..f1c0de54b0 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go @@ -89,13 +89,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -126,14 +128,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go index fcecf6a50d..88e23cb994 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas/policies/requests.go @@ -89,13 +89,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -126,16 +128,18 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -163,17 +167,19 @@ func AddRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder r.Err = err return } - _, r.Err = c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RemoveRule will add a rule to a policy. func RemoveRule(c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { b := map[string]interface{}{"firewall_rule_id": ruleID} - _, r.Err = c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(removeURL(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/fwaas/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go index 17979b637b..4627a92628 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas/rules/requests.go @@ -131,13 +131,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -176,15 +178,17 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall rule based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index caddd9d2d4..7ffd681d96 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -149,13 +149,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -194,14 +196,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall rule based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go index defa8e1b5a..fb28b72e36 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go @@ -60,7 +60,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific address-scope based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -100,9 +101,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -134,14 +136,16 @@ func Update(c *gophercloud.ServiceClient, addressScopeID string, opts UpdateOpts r.Err = err return } - _, r.Err = c.Put(updateURL(c, addressScopeID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, addressScopeID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the address-scope associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, id), nil) + resp, err := c.Delete(deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 0c0db64d8d..2a4ff1aca4 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -116,13 +116,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular floating IP resource based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -167,9 +169,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,6 +180,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r // ensure this is what you want - you can also disassociate the IP from existing // internal ports. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index 0fa5c1a669..9081bc3549 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -53,7 +53,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder, id string) paginat // Get retrieves a particular port forwarding resource based on its unique ID. func Get(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r GetResult) { - _, r.Err = c.Get(singlePortForwardingUrl(c, floatingIpId, pfId), &r.Body, nil) + resp, err := c.Get(singlePortForwardingUrl(c, floatingIpId, pfId), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -87,7 +88,8 @@ func Create(c *gophercloud.ServiceClient, floatingIpId string, opts CreateOptsBu r.Err = err return } - _, r.Err = c.Post(portForwardingUrl(c, floatingIpId), b, &r.Body, nil) + resp, err := c.Post(portForwardingUrl(c, floatingIpId), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -124,14 +126,16 @@ func Update(c *gophercloud.ServiceClient, fipID string, pfID string, opts Update r.Err = err return } - _, r.Err = c.Put(singlePortForwardingUrl(c, fipID, pfID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(singlePortForwardingUrl(c, fipID, pfID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular port forwarding for a given floating ID. func Delete(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r DeleteResult) { - _, r.Err = c.Delete(singlePortForwardingUrl(c, floatingIpId, pfId), nil) + resp, err := c.Delete(singlePortForwardingUrl(c, floatingIpId, pfId), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index cf499f9873..e0b1effa17 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -84,13 +84,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular router based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -126,15 +128,17 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular router based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -182,9 +186,10 @@ func AddInterface(c *gophercloud.ServiceClient, id string, opts AddInterfaceOpts r.Err = err return } - _, r.Err = c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -226,8 +231,9 @@ func RemoveInterface(c *gophercloud.ServiceClient, id string, opts RemoveInterfa r.Err = err return } - _, r.Err = c.Put(removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(removeInterfaceURL(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/lbaas/members/requests.go b/openstack/networking/v2/extensions/lbaas/members/requests.go index 1a31288444..c423a09505 100644 --- a/openstack/networking/v2/extensions/lbaas/members/requests.go +++ b/openstack/networking/v2/extensions/lbaas/members/requests.go @@ -77,13 +77,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool member based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -111,14 +113,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular member based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go index 9ed0c769c9..3d3ff97bcf 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go @@ -146,13 +146,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular health monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -214,14 +216,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + 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 monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go index f5f4e9a0df..e1b73f16d1 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas/pools/requests.go @@ -104,13 +104,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -143,15 +145,17 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -162,7 +166,8 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { // finds it unhealthy. func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { b := map[string]interface{}{"health_monitor": map[string]string{"id": monitorID}} - _, r.Err = c.Post(associateURL(c, poolID), b, &r.Body, nil) + resp, err := c.Post(associateURL(c, poolID), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -170,6 +175,7 @@ func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r // pool. When dissociation is successful, the health monitor will no longer // check for the health of the members of the pool. func DisassociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { - _, r.Err = c.Delete(disassociateURL(c, poolID, monitorID), nil) + resp, err := c.Delete(disassociateURL(c, poolID, monitorID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go index 53b81bfdb9..361fd3adc0 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/requests.go +++ b/openstack/networking/v2/extensions/lbaas/vips/requests.go @@ -114,13 +114,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular virtual IP based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -167,14 +169,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + 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 virtual IP based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go index 9d2b3a0d35..db8c0ea32d 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go @@ -80,7 +80,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -137,13 +138,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a particular l7policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular l7policy based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -208,9 +211,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -254,7 +258,8 @@ func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOp r.Err = err return } - _, r.Err = c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + resp, err := c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -309,13 +314,15 @@ func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOpts // GetRule retrieves a particular L7Policy Rule based on its unique ID. func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { - _, r.Err = c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) + resp, err := c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will remove a Rule from a particular L7Policy. func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { - _, r.Err = c.Delete(ruleResourceURL(c, policyID, ruleID), nil) + resp, err := c.Delete(ruleResourceURL(c, policyID, ruleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -369,8 +376,9 @@ func UpdateRule(c *gophercloud.ServiceClient, policyID string, ruleID string, op r.Err = err return } - _, r.Err = c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index aa4f7d15d7..dacfe31a70 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -136,13 +136,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Listeners based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -199,14 +201,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + 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 Listeners based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index 3aaaa0d0c1..c6992730a5 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -121,13 +121,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Loadbalancer based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -164,16 +166,18 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + 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 LoadBalancer based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -187,18 +191,21 @@ func CascadingDelete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { return } u := fmt.Sprintf("%s?cascade=true", resourceURL(c, id)) - _, r.Err = c.Delete(u, nil) + resp, err := c.Delete(u, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { - _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) + resp, err := c.Get(statusRootURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular LoadBalancer. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { - _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) + resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go index f728f5a823..fa0afc3bf6 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go @@ -179,13 +179,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Health Monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -244,14 +246,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + 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 Monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index f427ae7bf5..6f7bc07318 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -134,13 +134,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -181,15 +183,17 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -297,13 +301,15 @@ func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMember r.Err = err return } - _, r.Err = c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + resp, err := c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMember retrieves a particular Pool Member based on its unique ID. func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { - _, r.Err = c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) + resp, err := c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -342,15 +348,17 @@ func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, r.Err = err return } - _, r.Err = c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DisassociateMember will remove and disassociate a Member from a particular // Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { - _, r.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil) + resp, err := c.Delete(memberResourceURL(c, poolID, memberID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/networkipavailabilities/requests.go b/openstack/networking/v2/extensions/networkipavailabilities/requests.go index c024d7a7b8..ecc7f84dbf 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/requests.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/requests.go @@ -56,6 +56,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific NetworkIPAvailability based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + 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/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index 8e8b905db3..b2c1fbd38b 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -171,7 +171,8 @@ func List(c *gophercloud.ServiceClient, opts PolicyListOptsBuilder) pagination.P // Get retrieves a specific QoS policy based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -214,9 +215,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -254,14 +256,16 @@ func Update(c *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilde r.Err = err return } - _, r.Err = c.Put(updateURL(c, policyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the QoS policy associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, id), nil) + resp, err := c.Delete(deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index a003efa899..a5719d9f8a 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -59,7 +59,8 @@ func ListBandwidthLimitRules(c *gophercloud.ServiceClient, policyID string, opts // GetBandwidthLimitRule retrieves a specific BandwidthLimitRule based on its ID. func GetBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetBandwidthLimitRuleResult) { - _, r.Err = c.Get(getBandwidthLimitRuleURL(c, policyID, ruleID), &r.Body, nil) + resp, err := c.Get(getBandwidthLimitRuleURL(c, policyID, ruleID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -93,9 +94,10 @@ func CreateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID string r.Err = err return } - _, r.Err = client.Post(createBandwidthLimitRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createBandwidthLimitRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -129,15 +131,17 @@ func UpdateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID, ruleI r.Err = err return } - _, r.Err = client.Put(updateBandwidthLimitRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateBandwidthLimitRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts policy and rule ID and deletes the BandwidthLimitRule associated with them. func DeleteBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteBandwidthLimitRuleResult) { - _, r.Err = c.Delete(deleteBandwidthLimitRuleURL(c, policyID, ruleID), nil) + resp, err := c.Delete(deleteBandwidthLimitRuleURL(c, policyID, ruleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -193,7 +197,8 @@ func ListDSCPMarkingRules(c *gophercloud.ServiceClient, policyID string, opts DS // GetDSCPMarkingRule retrieves a specific DSCPMarkingRule based on its ID. func GetDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetDSCPMarkingRuleResult) { - _, r.Err = c.Get(getDSCPMarkingRuleURL(c, policyID, ruleID), &r.Body, nil) + resp, err := c.Get(getDSCPMarkingRuleURL(c, policyID, ruleID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -221,9 +226,10 @@ func CreateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID string, o r.Err = err return } - _, r.Err = client.Post(createDSCPMarkingRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createDSCPMarkingRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -251,15 +257,17 @@ func UpdateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID, ruleID s r.Err = err return } - _, r.Err = client.Put(updateDSCPMarkingRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateDSCPMarkingRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteDSCPMarkingRule accepts policy and rule ID and deletes the DSCPMarkingRule associated with them. func DeleteDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteDSCPMarkingRuleResult) { - _, r.Err = c.Delete(deleteDSCPMarkingRuleURL(c, policyID, ruleID), nil) + resp, err := c.Delete(deleteDSCPMarkingRuleURL(c, policyID, ruleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -316,7 +324,8 @@ func ListMinimumBandwidthRules(c *gophercloud.ServiceClient, policyID string, op // GetMinimumBandwidthRule retrieves a specific MinimumBandwidthRule based on its ID. func GetMinimumBandwidthRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetMinimumBandwidthRuleResult) { - _, r.Err = c.Get(getMinimumBandwidthRuleURL(c, policyID, ruleID), &r.Body, nil) + resp, err := c.Get(getMinimumBandwidthRuleURL(c, policyID, ruleID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -347,9 +356,10 @@ func CreateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID stri r.Err = err return } - _, r.Err = client.Post(createMinimumBandwidthRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createMinimumBandwidthRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -380,14 +390,16 @@ func UpdateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID, rul r.Err = err return } - _, r.Err = client.Put(updateMinimumBandwidthRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateMinimumBandwidthRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMinimumBandwidthRule accepts policy and rule ID and deletes the MinimumBandwidthRule associated with them. func DeleteMinimumBandwidthRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteMinimumBandwidthRuleResult) { - _, r.Err = c.Delete(deleteMinimumBandwidthRuleURL(c, policyID, ruleID), nil) + resp, err := c.Delete(deleteMinimumBandwidthRuleURL(c, policyID, ruleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/requests.go b/openstack/networking/v2/extensions/qos/ruletypes/requests.go index 61121f6b0e..cbccf06ded 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/requests.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/requests.go @@ -14,6 +14,7 @@ func ListRuleTypes(c *gophercloud.ServiceClient) (result pagination.Pager) { // GetRuleType retrieves a specific QoS RuleType based on its name. func GetRuleType(c *gophercloud.ServiceClient, name string) (r GetResult) { - _, r.Err = c.Get(getRuleTypeURL(c, name), &r.Body, nil) + resp, err := c.Get(getRuleTypeURL(c, name), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/quotas/requests.go b/openstack/networking/v2/extensions/quotas/requests.go index 973e46e104..2abaf22b44 100644 --- a/openstack/networking/v2/extensions/quotas/requests.go +++ b/openstack/networking/v2/extensions/quotas/requests.go @@ -4,7 +4,8 @@ import "github.com/gophercloud/gophercloud" // Get returns Networking Quotas for a project. func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { - _, r.Err = client.Get(getURL(client, projectID), &r.Body, nil) + resp, err := client.Get(getURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -57,9 +58,9 @@ func Update(c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuild r.Err = err return } - _, r.Err = c.Put(updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index e388e1a168..723c918580 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -59,7 +59,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific rbac policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -105,13 +106,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + 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 rbac-policy associated with it. func Delete(c *gophercloud.ServiceClient, rbacPolicyID string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, rbacPolicyID), nil) + resp, err := c.Delete(deleteURL(c, rbacPolicyID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -139,8 +142,9 @@ func Update(c *gophercloud.ServiceClient, rbacPolicyID string, opts UpdateOptsBu r.Err = err return } - _, r.Err = c.Put(updateURL(c, rbacPolicyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, rbacPolicyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index b45f8c581c..566a730eaf 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -76,7 +76,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -109,21 +110,24 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index c7741ffcd2..544c24d7f3 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -141,19 +141,22 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group rule based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index c54813b85d..3092f809c5 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -70,7 +70,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific subnetpool based on its ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -141,9 +142,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -212,14 +214,16 @@ func Update(c *gophercloud.ServiceClient, subnetPoolID string, opts UpdateOptsBu r.Err = err return } - _, r.Err = c.Put(updateURL(c, subnetPoolID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, subnetPoolID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the subnetpool associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, id), nil) + resp, err := c.Delete(deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index 447a0d4113..d9037b141d 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -37,13 +37,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul return } - _, r.Err = c.Post(createURL(c), body, &r.Body, nil) + resp, err := c.Post(createURL(c), body, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the trunk associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, id), nil) + resp, err := c.Delete(deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -105,7 +107,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific trunk based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -129,16 +132,18 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(updateURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetSubports(c *gophercloud.ServiceClient, id string) (r GetSubportsResult) { - _, r.Err = c.Get(getSubportsURL(c, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Get(getSubportsURL(c, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -160,9 +165,10 @@ func AddSubports(c *gophercloud.ServiceClient, id string, opts AddSubportsOptsBu r.Err = err return } - _, r.Err = c.Put(addSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(addSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -188,8 +194,9 @@ func RemoveSubports(c *gophercloud.ServiceClient, id string, opts RemoveSubports r.Err = err return } - _, r.Err = c.Put(removeSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(removeSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go index c12d0a8004..e13139d029 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -56,13 +56,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular endpoint group based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -109,7 +111,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Delete will permanently delete a particular endpoint group based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -137,8 +140,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(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/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go index 6b084ff624..dea35bc6e6 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -105,20 +105,23 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IKE policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IKE policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -202,8 +205,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(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/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go index 9496365ca4..fd23548110 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -108,20 +108,23 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IPSec policy based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IPSec policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -204,8 +207,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(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/vpnaas/services/requests.go b/openstack/networking/v2/extensions/vpnaas/services/requests.go index 8d642197e0..981f4bc82a 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/services/requests.go @@ -50,14 +50,16 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular VPN service based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -91,9 +93,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -145,6 +148,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a particular VPN service based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index 15ce54b5fb..4d97b54b74 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -125,21 +125,23 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) - + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IPSec site connection based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IPSec site connection based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -236,8 +238,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(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/networks/requests.go b/openstack/networking/v2/networks/requests.go index 168a284584..7a28ba0d19 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -60,7 +60,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific network based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -99,7 +100,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -130,14 +132,16 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild r.Err = err return } - _, r.Err = c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the network associated with it. func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, networkID), nil) + resp, err := c.Delete(deleteURL(c, networkID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 5b45c2f10a..4dc1600726 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -97,7 +97,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific port based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -136,7 +137,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -171,14 +173,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the port associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, id), nil) + resp, err := c.Delete(deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index c92ab9bb63..94a5b6b1aa 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -69,7 +69,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific subnet based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -160,7 +161,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -219,14 +221,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the subnet associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, id), nil) + resp, err := c.Delete(deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/objectstorage/v1/accounts/requests.go b/openstack/objectstorage/v1/accounts/requests.go index 452a331c74..7c9acf85ff 100644 --- a/openstack/objectstorage/v1/accounts/requests.go +++ b/openstack/objectstorage/v1/accounts/requests.go @@ -39,10 +39,7 @@ func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { MoreHeaders: h, OkCodes: []int{204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -92,9 +89,6 @@ func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) (r UpdateResul MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 4a4be32715..b8f169345d 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -111,11 +111,7 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) - if resp != nil { - r.Header = resp.Header - resp.Body.Close() - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -128,25 +124,22 @@ func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDelete for i, v := range containers { encodedContainers[i] = url.QueryEscape(v) } - resp, err := c.Request("POST", bulkDeleteURL(c), &gophercloud.RequestOpts{ - RawBody: strings.NewReader(strings.Join(encodedContainers, "\n") + "\n"), + b := strings.NewReader(strings.Join(encodedContainers, "\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}, }) - if resp != nil { - r.Header = resp.Header - r.Body = resp.Body - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete is a function that deletes a container. func Delete(c *gophercloud.ServiceClient, containerName string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, url.QueryEscape(containerName)), nil) + resp, err := c.Delete(deleteURL(c, url.QueryEscape(containerName)), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -212,10 +205,7 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -255,9 +245,6 @@ func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder MoreHeaders: h, OkCodes: []int{200, 204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index f438d633ad..e85f83ee25 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -131,11 +131,7 @@ func Download(c *gophercloud.ServiceClient, containerName, objectName string, op MoreHeaders: h, OkCodes: []int{200, 206, 304}, }) - if resp != nil { - r.Header = resp.Header - r.Body = resp.Body - } - r.Err = err + r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -238,14 +234,10 @@ func Create(c *gophercloud.ServiceClient, containerName, objectName string, opts b = tmpB } - resp, err := c.Put(url, nil, nil, &gophercloud.RequestOpts{ - RawBody: b, + resp, err := c.Put(url, b, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) - r.Err = err - if resp != nil { - r.Header = resp.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -295,10 +287,7 @@ func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts C MoreHeaders: h, OkCodes: []int{201}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -331,10 +320,7 @@ func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts url += query } resp, err := c.Delete(url, nil) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -387,10 +373,7 @@ func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts Ge MoreHeaders: h, OkCodes: []int{200, 204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -442,10 +425,7 @@ func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -521,18 +501,14 @@ func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string url.QueryEscape(v)}, "/") } - resp, err := c.Request("POST", bulkDeleteURL(c), &gophercloud.RequestOpts{ - RawBody: strings.NewReader(strings.Join(encodedObjects, "\n") + "\n"), + 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}, }) - if resp != nil { - r.Header = resp.Header - r.Body = resp.Body - } - r.Err = err + _, 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 cd51fdb572..8d950a3139 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -151,6 +151,7 @@ func HandleCreateTextObjectSuccessfully(t *testing.T, content string) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Content-Type", "text/plain") th.TestHeader(t, r, "Accept", "application/json") + th.TestBody(t, r, `Did gyre and gimble in the wabe`) hash := md5.New() io.WriteString(hash, content) @@ -169,6 +170,7 @@ func HandleCreateTextWithCacheControlSuccessfully(t *testing.T, content string) th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Cache-Control", `max-age="3600", public`) th.TestHeader(t, r, "Accept", "application/json") + th.TestBody(t, r, `All mimsy were the borogoves`) hash := md5.New() io.WriteString(hash, content) @@ -187,6 +189,7 @@ func HandleCreateTypelessObjectSuccessfully(t *testing.T, content string) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") + th.TestBody(t, r, `The sky was the color of television, tuned to a dead channel.`) if contentType, present := r.Header["Content-Type"]; present { t.Errorf("Expected Content-Type header to be omitted, but was %#v", contentType) diff --git a/openstack/objectstorage/v1/swauth/requests.go b/openstack/objectstorage/v1/swauth/requests.go index 29bdcbcf76..3d067d8e05 100644 --- a/openstack/objectstorage/v1/swauth/requests.go +++ b/openstack/objectstorage/v1/swauth/requests.go @@ -41,13 +41,7 @@ func Auth(c *gophercloud.ProviderClient, opts AuthOptsBuilder) (r GetAuthResult) MoreHeaders: h, OkCodes: []int{200}, }) - - if resp != nil { - r.Header = resp.Header - } - - r.Err = err - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return r } diff --git a/openstack/orchestration/v1/buildinfo/requests.go b/openstack/orchestration/v1/buildinfo/requests.go index 32f6032d66..d710da3d9d 100644 --- a/openstack/orchestration/v1/buildinfo/requests.go +++ b/openstack/orchestration/v1/buildinfo/requests.go @@ -4,6 +4,7 @@ import "github.com/gophercloud/gophercloud" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient) (r GetResult) { - _, r.Err = c.Get(getURL(c), &r.Body, nil) + resp, err := c.Get(getURL(c), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/resourcetypes/requests.go b/openstack/orchestration/v1/resourcetypes/requests.go index 064f89f124..d350cfc443 100644 --- a/openstack/orchestration/v1/resourcetypes/requests.go +++ b/openstack/orchestration/v1/resourcetypes/requests.go @@ -64,13 +64,15 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) (r ListResult } url += query - _, r.Err = client.Get(url, &r.Body, nil) + resp, err := client.Get(url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetSchema retreives the schema for a given resource type. func GetSchema(client *gophercloud.ServiceClient, resourceType string) (r GetSchemaResult) { - _, r.Err = client.Get(getSchemaURL(client, resourceType), &r.Body, nil) + resp, err := client.Get(getSchemaURL(client, resourceType), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -113,6 +115,7 @@ func GenerateTemplate(client *gophercloud.ServiceClient, resourceType string, op return } url += query - _, r.Err = client.Get(url, &r.Body, nil) + resp, err := client.Get(url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/stackevents/requests.go b/openstack/orchestration/v1/stackevents/requests.go index e6e7f79147..117b52bdcd 100644 --- a/openstack/orchestration/v1/stackevents/requests.go +++ b/openstack/orchestration/v1/stackevents/requests.go @@ -7,7 +7,8 @@ import ( // Find retrieves stack events for the given stack name. func Find(c *gophercloud.ServiceClient, stackName string) (r FindResult) { - _, r.Err = c.Get(findURL(c, stackName), &r.Body, nil) + resp, err := c.Get(findURL(c, stackName), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,6 +178,7 @@ func ListResourceEvents(client *gophercloud.ServiceClient, stackName, stackID, r // Get retreives data for the given stack resource. func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) (r GetResult) { - _, r.Err = c.Get(getURL(c, stackName, stackID, resourceName, eventID), &r.Body, nil) + resp, err := c.Get(getURL(c, stackName, stackID, resourceName, eventID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/stackresources/requests.go b/openstack/orchestration/v1/stackresources/requests.go index 306ada629d..466b7ec718 100644 --- a/openstack/orchestration/v1/stackresources/requests.go +++ b/openstack/orchestration/v1/stackresources/requests.go @@ -7,7 +7,8 @@ import ( // Find retrieves stack resources for the given stack name. func Find(c *gophercloud.ServiceClient, stackName string) (r FindResult) { - _, r.Err = c.Get(findURL(c, stackName), &r.Body, nil) + resp, err := c.Get(findURL(c, stackName), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -47,13 +48,15 @@ func List(client *gophercloud.ServiceClient, stackName, stackID string, opts Lis // Get retreives data for the given stack resource. func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r GetResult) { - _, r.Err = c.Get(getURL(c, stackName, stackID, resourceName), &r.Body, nil) + resp, err := c.Get(getURL(c, stackName, stackID, resourceName), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Metadata retreives the metadata for the given stack resource. func Metadata(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r MetadataResult) { - _, r.Err = c.Get(metadataURL(c, stackName, stackID, resourceName), &r.Body, nil) + resp, err := c.Get(metadataURL(c, stackName, stackID, resourceName), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -66,13 +69,15 @@ func ListTypes(client *gophercloud.ServiceClient) pagination.Pager { // Schema retreives the schema for the given resource type. func Schema(c *gophercloud.ServiceClient, resourceType string) (r SchemaResult) { - _, r.Err = c.Get(schemaURL(c, resourceType), &r.Body, nil) + resp, err := c.Get(schemaURL(c, resourceType), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Template retreives the template representation for the given resource type. func Template(c *gophercloud.ServiceClient, resourceType string) (r TemplateResult) { - _, r.Err = c.Get(templateURL(c, resourceType), &r.Body, nil) + resp, err := c.Get(templateURL(c, resourceType), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -108,6 +113,7 @@ func MarkUnhealthy(c *gophercloud.ServiceClient, stackName, stackID, resourceNam r.Err = err return } - _, r.Err = c.Patch(markUnhealthyURL(c, stackName, stackID, resourceName), b, nil, nil) + resp, err := c.Patch(markUnhealthyURL(c, stackName, stackID, resourceName), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index d7fd4f16fd..bf480f06d8 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -92,7 +92,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -181,7 +182,8 @@ func Adopt(c *gophercloud.ServiceClient, opts AdoptOptsBuilder) (r AdoptResult) r.Err = err return } - _, r.Err = c.Post(adoptURL(c), b, &r.Body, nil) + resp, err := c.Post(adoptURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -293,13 +295,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retreives a stack based on the stack name and stack ID. func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { - _, r.Err = c.Get(getURL(c, stackName, stackID), &r.Body, nil) + resp, err := c.Get(getURL(c, stackName, stackID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Find retrieves a stack based on the stack name or stack ID. func Find(c *gophercloud.ServiceClient, stackIdentity string) (r GetResult) { - _, r.Err = c.Get(findURL(c, stackIdentity), &r.Body, nil) + resp, err := c.Get(findURL(c, stackIdentity), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -404,7 +408,8 @@ func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts Update r.Err = err return } - _, r.Err = c.Put(updateURL(c, stackName, stackID), b, nil, nil) + resp, err := c.Put(updateURL(c, stackName, stackID), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -416,13 +421,15 @@ func UpdatePatch(c *gophercloud.ServiceClient, stackName, stackID string, opts U r.Err = err return } - _, r.Err = c.Patch(updateURL(c, stackName, stackID), b, nil, nil) + resp, err := c.Patch(updateURL(c, stackName, stackID), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a stack based on the stack name and stack ID. func Delete(c *gophercloud.ServiceClient, stackName, stackID string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, stackName, stackID), nil) + resp, err := c.Delete(deleteURL(c, stackName, stackID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -503,18 +510,20 @@ func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) (r PreviewRe r.Err = err return } - _, r.Err = c.Post(previewURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(previewURL(c), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Abandon deletes the stack with the provided stackName and stackID, but leaves its // resources intact, and returns data describing the stack and its resources. func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) (r AbandonResult) { - _, r.Err = c.Delete(abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{ + resp, err := c.Delete(abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{ JSONResponse: &r.Body, OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/stacktemplates/requests.go b/openstack/orchestration/v1/stacktemplates/requests.go index d248c24ff6..58eafffcc4 100644 --- a/openstack/orchestration/v1/stacktemplates/requests.go +++ b/openstack/orchestration/v1/stacktemplates/requests.go @@ -4,7 +4,8 @@ import "github.com/gophercloud/gophercloud" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { - _, r.Err = c.Get(getURL(c, stackName, stackID), &r.Body, nil) + resp, err := c.Get(getURL(c, stackName, stackID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -32,8 +33,9 @@ func Validate(c *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r Validat r.Err = err return } - _, r.Err = c.Post(validateURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(validateURL(c), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 6e16d27676..f0da23f989 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -1,8 +1,6 @@ package resourceproviders import ( - "net/http" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -91,29 +89,27 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } - var result *http.Response - - result, r.Err = client.Post(resourceProvidersListURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(resourceProvidersListURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - if r.Err == nil { - r.Header = result.Header - } - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetUsages(client *gophercloud.ServiceClient, resourceProviderID string) (r GetUsagesResult) { - _, r.Err = client.Get(getResourceProviderUsagesURL(client, resourceProviderID), &r.Body, nil) + resp, err := client.Get(getResourceProviderUsagesURL(client, resourceProviderID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetInventories(client *gophercloud.ServiceClient, resourceProviderID string) (r GetInventoriesResult) { - _, r.Err = client.Get(getResourceProviderInventoriesURL(client, resourceProviderID), &r.Body, nil) + resp, err := client.Get(getResourceProviderInventoriesURL(client, resourceProviderID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetTraits(client *gophercloud.ServiceClient, resourceProviderID string) (r GetTraitsResult) { - _, r.Err = client.Get(getResourceProviderTraitsURL(client, resourceProviderID), &r.Body, nil) + resp, err := client.Get(getResourceProviderTraitsURL(client, resourceProviderID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/apiversions/requests.go b/openstack/sharedfilesystems/apiversions/requests.go index 2e1f6639b5..244a7acba4 100644 --- a/openstack/sharedfilesystems/apiversions/requests.go +++ b/openstack/sharedfilesystems/apiversions/requests.go @@ -14,6 +14,7 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { // Get will get a specific API version, specified by major ID. func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - _, r.Err = client.Get(getURL(client, v), &r.Body, nil) + resp, err := client.Get(getURL(client, v), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/v2/messages/requests.go b/openstack/sharedfilesystems/v2/messages/requests.go index 44e0d94bd1..39c7e82a74 100644 --- a/openstack/sharedfilesystems/v2/messages/requests.go +++ b/openstack/sharedfilesystems/v2/messages/requests.go @@ -7,7 +7,8 @@ import ( // Delete will delete the existing Message with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -67,6 +68,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves the Message with the provided ID. To extract the Message // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + 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/securityservices/requests.go b/openstack/sharedfilesystems/v2/securityservices/requests.go index 8bfec43cf8..636c7b2f85 100644 --- a/openstack/sharedfilesystems/v2/securityservices/requests.go +++ b/openstack/sharedfilesystems/v2/securityservices/requests.go @@ -59,15 +59,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing SecurityService with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -127,7 +129,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves the SecurityService with the provided ID. To extract the SecurityService // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -175,8 +178,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + 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/sharedfilesystems/v2/sharenetworks/requests.go b/openstack/sharedfilesystems/v2/sharenetworks/requests.go index 15e664ec38..f8976b76ea 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/requests.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/requests.go @@ -42,15 +42,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing ShareNetwork with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -120,7 +122,8 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // Get retrieves the ShareNetwork with the provided ID. To extract the ShareNetwork // object from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -160,9 +163,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -193,9 +197,10 @@ func AddSecurityService(client *gophercloud.ServiceClient, id string, opts AddSe r.Err = err return } - _, r.Err = client.Post(addSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(addSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -226,8 +231,9 @@ func RemoveSecurityService(client *gophercloud.ServiceClient, id string, opts Re r.Err = err return } - _, r.Err = client.Post(removeSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(removeSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 692e18604e..68ef32e17d 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -63,9 +63,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -164,27 +165,31 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // Delete will delete an existing Share with the given UUID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + 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) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListExportLocations will list shareID's export locations. // Client must have Microversion set; minimum supported microversion for ListExportLocations is 2.9. func ListExportLocations(client *gophercloud.ServiceClient, id string) (r ListExportLocationsResult) { - _, r.Err = client.Get(listExportLocationsURL(client, id), &r.Body, nil) + resp, err := client.Get(listExportLocationsURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetExportLocation will get shareID's export location by an ID. // Client must have Microversion set; minimum supported microversion for GetExportLocation is 2.9. func GetExportLocation(client *gophercloud.ServiceClient, shareID string, id string) (r GetExportLocationResult) { - _, r.Err = client.Get(getExportLocationURL(client, shareID, id), &r.Body, nil) + resp, err := client.Get(getExportLocationURL(client, shareID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -221,9 +226,10 @@ func GrantAccess(client *gophercloud.ServiceClient, id string, opts GrantAccessO r.Err = err return } - _, r.Err = client.Post(grantAccessURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(grantAccessURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -257,10 +263,10 @@ func RevokeAccess(client *gophercloud.ServiceClient, id string, opts RevokeAcces return } - _, r.Err = client.Post(revokeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(revokeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -269,9 +275,10 @@ func RevokeAccess(client *gophercloud.ServiceClient, id string, opts RevokeAcces // Client must have Microversion set; minimum supported microversion for ListAccessRights is 2.7. func ListAccessRights(client *gophercloud.ServiceClient, id string) (r ListAccessRightsResult) { requestBody := map[string]interface{}{"access_list": nil} - _, r.Err = client.Post(listAccessRightsURL(client, id), requestBody, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(listAccessRightsURL(client, id), requestBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -305,10 +312,10 @@ func Extend(client *gophercloud.ServiceClient, id string, opts ExtendOptsBuilder return } - _, r.Err = client.Post(extendURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(extendURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -342,10 +349,10 @@ func Shrink(client *gophercloud.ServiceClient, id string, opts ShrinkOptsBuilder return } - _, r.Err = client.Post(shrinkURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(shrinkURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -381,23 +388,26 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadata retrieves metadata of the specified share. To extract the retrieved // metadata from the response, call the Extract method on the MetadataResult. func GetMetadata(client *gophercloud.ServiceClient, id string) (r MetadataResult) { - _, r.Err = client.Get(getMetadataURL(client, id), &r.Body, nil) + resp, err := client.Get(getMetadataURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadatum retrieves a single metadata item of the specified share. To extract the retrieved // metadata from the response, call the Extract method on the GetMetadatumResult. func GetMetadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { - _, r.Err = client.Get(getMetadatumURL(client, id, key), &r.Body, nil) + resp, err := client.Get(getMetadatumURL(client, id, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -431,10 +441,10 @@ func SetMetadata(client *gophercloud.ServiceClient, id string, opts SetMetadataO return } - _, r.Err = client.Post(setMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(setMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -468,19 +478,19 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet return } - _, r.Err = client.Post(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMetadatum deletes a single key-value pair from the metadata of the specified share. func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { - _, r.Err = client.Delete(deleteMetadatumURL(client, id, key), &gophercloud.RequestOpts{ + resp, err := client.Delete(deleteMetadatumURL(client, id, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -515,10 +525,10 @@ func Revert(client *gophercloud.ServiceClient, id string, opts RevertOptsBuilder return } - _, r.Err = client.Post(revertURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(revertURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -552,10 +562,10 @@ func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusO return } - _, r.Err = client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -566,10 +576,10 @@ func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteRes b := map[string]interface{}{ "force_delete": nil, } - _, r.Err = client.Post(forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -581,9 +591,9 @@ func Unmanage(client *gophercloud.ServiceClient, id string) (r UnmanageResult) { b := map[string]interface{}{ "unmanage": nil, } - _, r.Err = client.Post(unmanageURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(unmanageURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/v2/sharetypes/requests.go b/openstack/sharedfilesystems/v2/sharetypes/requests.go index c2c2049b9f..11f554b563 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/requests.go +++ b/openstack/sharedfilesystems/v2/sharetypes/requests.go @@ -46,15 +46,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will delete the existing ShareType with the provided ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -95,13 +97,15 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // GetDefault will retrieve the default ShareType. func GetDefault(client *gophercloud.ServiceClient) (r GetDefaultResult) { - _, r.Err = client.Get(getDefaultURL(client), &r.Body, nil) + resp, err := client.Get(getDefaultURL(client), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetExtraSpecs will retrieve the extra specifications for a given ShareType. func GetExtraSpecs(client *gophercloud.ServiceClient, id string) (r GetExtraSpecsResult) { - _, r.Err = client.Get(getExtraSpecsURL(client, id), &r.Body, nil) + resp, err := client.Get(getExtraSpecsURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -132,21 +136,24 @@ func SetExtraSpecs(client *gophercloud.ServiceClient, id string, opts SetExtraSp return } - _, r.Err = client.Post(setExtraSpecsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(setExtraSpecsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // UnsetExtraSpecs will unset an extra specification for an existing ShareType. func UnsetExtraSpecs(client *gophercloud.ServiceClient, id string, key string) (r UnsetExtraSpecsResult) { - _, r.Err = client.Delete(unsetExtraSpecsURL(client, id, key), nil) + resp, err := client.Delete(unsetExtraSpecsURL(client, id, key), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ShowAccess will show access details for an existing ShareType. func ShowAccess(client *gophercloud.ServiceClient, id string) (r ShowAccessResult) { - _, r.Err = client.Get(showAccessURL(client, id), &r.Body, nil) + resp, err := client.Get(showAccessURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -176,9 +183,10 @@ func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsB return } - _, r.Err = client.Post(addAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(addAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -203,8 +211,9 @@ func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAcces return } - _, r.Err = client.Post(removeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(removeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/v2/snapshots/requests.go b/openstack/sharedfilesystems/v2/snapshots/requests.go index 1f2f7d7cc9..1ed6e8aef2 100644 --- a/openstack/sharedfilesystems/v2/snapshots/requests.go +++ b/openstack/sharedfilesystems/v2/snapshots/requests.go @@ -45,9 +45,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -114,13 +115,15 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // Delete will delete an existing Snapshot with the given UUID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will get a single snapshot with given UUID func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -154,8 +157,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + 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/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index c279254923..1084cbcc90 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -66,21 +66,23 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } - _, r.Err = client.Post(createURL(client), b, &r.Body, nil) - + resp, err := client.Post(createURL(client), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cron trigger. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single cron trigger. // Use Extract to convert its result into an CronTrigger. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/workflow/v2/executions/requests.go b/openstack/workflow/v2/executions/requests.go index ea35499413..36d81a2bb4 100644 --- a/openstack/workflow/v2/executions/requests.go +++ b/openstack/workflow/v2/executions/requests.go @@ -56,21 +56,23 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } - _, r.Err = client.Post(createURL(client), b, &r.Body, nil) - + resp, err := client.Post(createURL(client), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single execution. // Use ExtractExecution to convert its result into an Execution. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified execution. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/workflow/v2/workflows/requests.go b/openstack/workflow/v2/workflows/requests.go index d077c7da0b..f0bd9cc5c1 100644 --- a/openstack/workflow/v2/workflows/requests.go +++ b/openstack/workflow/v2/workflows/requests.go @@ -50,27 +50,28 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create b = tmpB } - _, r.Err = client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ - RawBody: b, + resp, err := client.Post(url, b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{ "Content-Type": "text/plain", "Accept": "", // Drop default JSON Accept header }, }) - + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified execution. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single execution. // Use Extract to convert its result into an Workflow. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/service_client.go b/service_client.go index f222f05a66..dd54abe30e 100644 --- a/service_client.go +++ b/service_client.go @@ -152,3 +152,11 @@ func (client *ServiceClient) Request(method, url string, options *RequestOpts) ( } return client.ProviderClient.Request(method, url, options) } + +// ParseResponse is a helper function to parse http.Response to constituents. +func ParseResponse(resp *http.Response, err error) (io.ReadCloser, http.Header, error) { + if resp != nil { + return resp.Body, resp.Header, err + } + return nil, nil, err +} From 353dc16ea5a13a1274d051f7e41c2d3e2eb6058f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 19 Apr 2020 09:10:08 -0600 Subject: [PATCH 1060/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4262813d5..40588e3af9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,15 @@ 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) 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) +* 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) ## 0.10.0 (April 12, 2020) From d7fba7fe5c93fc90f7415f398e7d1f1e1cdc973b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 19 Apr 2020 09:10:32 -0600 Subject: [PATCH 1061/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40588e3af9..926f8da9a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,7 @@ 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) +* 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) ## 0.10.0 (April 12, 2020) From 10cd0e1efc645db3e2636f73f73c661a0ebe3a39 Mon Sep 17 00:00:00 2001 From: SITCNET <9760367+sitcnet@users.noreply.github.com> Date: Mon, 20 Apr 2020 11:01:24 +0900 Subject: [PATCH 1062/2296] fix typo in the servers documentation (#1946) --- openstack/compute/v2/servers/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 01e4df8a8a..db1c01c1f2 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -398,7 +398,7 @@ func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { 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 rtored or the VM instance restarted. + 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. From b88f26317c347bc39d800e54663fd86c2a35d53e Mon Sep 17 00:00:00 2001 From: vtdat Date: Mon, 20 Apr 2020 21:47:54 +0700 Subject: [PATCH 1063/2296] Fix misleading/typo in openstack client doc (#1947) --- openstack/client.go | 2 +- openstack/doc.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/client.go b/openstack/client.go index f470b8c888..fef4bcf8a2 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -69,7 +69,7 @@ Example: ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(ao) - client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ + client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) */ diff --git a/openstack/doc.go b/openstack/doc.go index cedf1f4d3a..af4bd512bf 100644 --- a/openstack/doc.go +++ b/openstack/doc.go @@ -7,7 +7,7 @@ Example of Creating a Service Client ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(ao) - client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ + client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) */ From dadd51700719eacc225bf1f508e1db64c31f2834 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 21 Apr 2020 03:47:57 +0200 Subject: [PATCH 1064/2296] Nove V2: Introduce injectNetworkInfo and resetNetwork server actions support (#1941) --- .../v2/extensions/injectnetworkinfo/doc.go | 14 +++++++++++++ .../extensions/injectnetworkinfo/requests.go | 16 ++++++++++++++ .../extensions/injectnetworkinfo/results.go | 11 ++++++++++ .../injectnetworkinfo/testing/fixtures.go | 18 ++++++++++++++++ .../testing/requests_test.go | 21 +++++++++++++++++++ .../compute/v2/extensions/resetnetwork/doc.go | 14 +++++++++++++ .../v2/extensions/resetnetwork/requests.go | 16 ++++++++++++++ .../v2/extensions/resetnetwork/results.go | 11 ++++++++++ .../resetnetwork/testing/fixtures.go | 18 ++++++++++++++++ .../resetnetwork/testing/requests_test.go | 21 +++++++++++++++++++ 10 files changed, 160 insertions(+) create mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/doc.go create mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/requests.go create mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/results.go create mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go create mode 100644 openstack/compute/v2/extensions/resetnetwork/doc.go create mode 100644 openstack/compute/v2/extensions/resetnetwork/requests.go create mode 100644 openstack/compute/v2/extensions/resetnetwork/results.go create mode 100644 openstack/compute/v2/extensions/resetnetwork/testing/fixtures.go create mode 100644 openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/doc.go b/openstack/compute/v2/extensions/injectnetworkinfo/doc.go new file mode 100644 index 0000000000..2ab8658b26 --- /dev/null +++ b/openstack/compute/v2/extensions/injectnetworkinfo/doc.go @@ -0,0 +1,14 @@ +/* +Package injectnetworkinfo provides functionality to inject the network info into +a server that has been provisioned by the OpenStack Compute service. This action +requires admin privileges and Nova configured with a Xen hypervisor driver. + +Example to Inject a Network Info into a Server + + serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" + err := injectnetworkinfo.InjectNetworkInfo(client, id).ExtractErr() + if err != nil { + panic(err) + } +*/ +package injectnetworkinfo diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/requests.go b/openstack/compute/v2/extensions/injectnetworkinfo/requests.go new file mode 100644 index 0000000000..bbc665f1aa --- /dev/null +++ b/openstack/compute/v2/extensions/injectnetworkinfo/requests.go @@ -0,0 +1,16 @@ +package injectnetworkinfo + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" +) + +// InjectNetworkInfo will inject the network info into a server +func InjectNetworkInfo(client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { + b := map[string]interface{}{ + "injectNetworkInfo": nil, + } + resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/results.go b/openstack/compute/v2/extensions/injectnetworkinfo/results.go new file mode 100644 index 0000000000..44b133f70b --- /dev/null +++ b/openstack/compute/v2/extensions/injectnetworkinfo/results.go @@ -0,0 +1,11 @@ +package injectnetworkinfo + +import ( + "github.com/gophercloud/gophercloud" +) + +// InjectNetworkResult is the response of a InjectNetworkInfo operation. Call +// its ExtractErr method to determine if the request suceeded or failed. +type InjectNetworkResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures.go b/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures.go new file mode 100644 index 0000000000..1951e36daa --- /dev/null +++ b/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures.go @@ -0,0 +1,18 @@ +package testing + +import ( + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func mockInjectNetworkInfoResponse(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, `{"injectNetworkInfo": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go b/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go new file mode 100644 index 0000000000..132b407361 --- /dev/null +++ b/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go @@ -0,0 +1,21 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/injectnetworkinfo" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" + +func TestInjectNetworkInfo(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockInjectNetworkInfoResponse(t, serverID) + + err := injectnetworkinfo.InjectNetworkInfo(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/resetnetwork/doc.go b/openstack/compute/v2/extensions/resetnetwork/doc.go new file mode 100644 index 0000000000..5d89c7ccfc --- /dev/null +++ b/openstack/compute/v2/extensions/resetnetwork/doc.go @@ -0,0 +1,14 @@ +/* +Package resetnetwork provides functionality to reset the network of a server +that has been provisioned by the OpenStack Compute service. This action +requires admin privileges and Nova configured with a Xen hypervisor driver. + +Example to Reset a Network of a Server + + serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" + err := resetnetwork.ResetNetwork(client, id).ExtractErr() + if err != nil { + panic(err) + } +*/ +package resetnetwork diff --git a/openstack/compute/v2/extensions/resetnetwork/requests.go b/openstack/compute/v2/extensions/resetnetwork/requests.go new file mode 100644 index 0000000000..247650d364 --- /dev/null +++ b/openstack/compute/v2/extensions/resetnetwork/requests.go @@ -0,0 +1,16 @@ +package resetnetwork + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" +) + +// ResetNetwork will reset the network of a server +func ResetNetwork(client *gophercloud.ServiceClient, id string) (r ResetResult) { + b := map[string]interface{}{ + "resetNetwork": nil, + } + resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/compute/v2/extensions/resetnetwork/results.go b/openstack/compute/v2/extensions/resetnetwork/results.go new file mode 100644 index 0000000000..638677b063 --- /dev/null +++ b/openstack/compute/v2/extensions/resetnetwork/results.go @@ -0,0 +1,11 @@ +package resetnetwork + +import ( + "github.com/gophercloud/gophercloud" +) + +// ResetResult is the response of a ResetNetwork operation. Call its ExtractErr +// method to determine if the request suceeded or failed. +type ResetResult struct { + gophercloud.ErrResult +} diff --git a/openstack/compute/v2/extensions/resetnetwork/testing/fixtures.go b/openstack/compute/v2/extensions/resetnetwork/testing/fixtures.go new file mode 100644 index 0000000000..7f4ba2e541 --- /dev/null +++ b/openstack/compute/v2/extensions/resetnetwork/testing/fixtures.go @@ -0,0 +1,18 @@ +package testing + +import ( + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func mockResetNetworkResponse(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, `{"resetNetwork": null}`) + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go b/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go new file mode 100644 index 0000000000..c296b3f7bd --- /dev/null +++ b/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go @@ -0,0 +1,21 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/resetnetwork" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" + +func TestResetNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + mockResetNetworkResponse(t, serverID) + + err := resetnetwork.ResetNetwork(client.ServiceClient(), serverID).ExtractErr() + th.AssertNoErr(t, err) +} From f64f79ae14bfec2b1f18b1410f4a2c17a6167d94 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 20 Apr 2020 19:49:21 -0600 Subject: [PATCH 1065/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 926f8da9a6..29e9f1e61a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ IMPROVEMENTS * 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/resetnetworkResetNetwork` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) ## 0.10.0 (April 12, 2020) From 54ba005af87992916c4bb14db54acee43a543fdd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 20 Apr 2020 19:49:34 -0600 Subject: [PATCH 1066/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29e9f1e61a..b3d1423f44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ IMPROVEMENTS * 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/resetnetworkResetNetwork` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) +* Added `compute/v2/extensions/resetnetwork.ResetNetwork` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) ## 0.10.0 (April 12, 2020) From 0e100a803ed00bf747230b91d4a40564c44dea21 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 21 Apr 2020 03:50:14 +0200 Subject: [PATCH 1067/2296] Keystone V3: Introduce trust roles list and get (#1939) --- .../openstack/identity/v3/trusts_test.go | 18 ++++ .../identity/v3/extensions/trusts/requests.go | 26 +++++- .../identity/v3/extensions/trusts/results.go | 54 ++++++++++- .../v3/extensions/trusts/testing/fixtures.go | 89 ++++++++++++++++++- .../trusts/testing/requests_test.go | 52 +++++++++++ .../identity/v3/extensions/trusts/urls.go | 8 ++ provider_client.go | 12 +-- 7 files changed, 248 insertions(+), 11 deletions(-) diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/acceptance/openstack/identity/v3/trusts_test.go index 4632fd8209..dd1cb37d39 100644 --- a/acceptance/openstack/identity/v3/trusts_test.go +++ b/acceptance/openstack/identity/v3/trusts_test.go @@ -100,10 +100,28 @@ func TestTrustCRUD(t *testing.T) { trust, err = FindTrust(t, client) th.AssertNoErr(t, err) + // Get trust p, err := trusts.Get(client, trust.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, p.ExpiresAt.IsZero(), false) th.AssertEquals(t, p.DeletedAt.IsZero(), true) tools.PrintResource(t, p) + + // List trust roles + rolesPages, err := trusts.ListRoles(client, p.ID).AllPages() + th.AssertNoErr(t, err) + allTrustRoles, err := trusts.ExtractRoles(rolesPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, len(allTrustRoles), 1) + th.AssertEquals(t, allTrustRoles[0].ID, memberRoleID) + + // Get trust role + role, err := trusts.GetRole(client, p.ID, memberRoleID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, role.ID, memberRoleID) + + // Check trust role + err = trusts.CheckRole(client, p.ID, memberRoleID).ExtractErr() + th.AssertNoErr(t, err) } diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index 7b716639c7..a8879d8545 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -131,7 +131,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Delete deletes a trust. +// Delete deletes a Trust. func Delete(client *gophercloud.ServiceClient, trustID string) (r DeleteResult) { resp, err := client.Delete(deleteURL(client, trustID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -153,9 +153,31 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// Get retrieves details on a single trust, by ID. +// Get retrieves details on a single Trust, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := client.Get(resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ListRoles lists roles delegated by a Trust. +func ListRoles(client *gophercloud.ServiceClient, id string) pagination.Pager { + url := listRolesURL(client, id) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return RolesPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetRole retrieves details on a single role delegated by a Trust. +func GetRole(client *gophercloud.ServiceClient, id string, roleID string) (r GetRoleResult) { + resp, err := client.Get(getRoleURL(client, id, roleID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CheckRole checks whether a role ID is delegated by a Trust. +func CheckRole(client *gophercloud.ServiceClient, id string, roleID string) (r CheckRoleResult) { + resp, err := client.Head(getRoleURL(client, id, roleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/extensions/trusts/results.go b/openstack/identity/v3/extensions/trusts/results.go index 60629f9b42..945659cdd8 100644 --- a/openstack/identity/v3/extensions/trusts/results.go +++ b/openstack/identity/v3/extensions/trusts/results.go @@ -85,7 +85,7 @@ type Trust struct { RedelegationCount int `json:"redelegation_count,omitempty"` AllowRedelegation bool `json:"allow_redelegation,omitempty"` ProjectID string `json:"project_id,omitempty"` - RemainingUses bool `json:"remaining_uses,omitempty"` + RemainingUses int `json:"remaining_uses,omitempty"` Roles []Role `json:"roles,omitempty"` DeletedAt time.Time `json:"deleted_at"` ExpiresAt time.Time `json:"expires_at"` @@ -101,3 +101,55 @@ type Role struct { type TokenExt struct { Trust Trust `json:"OS-TRUST:trust"` } + +// RolesPage is a single page of Trust roles results. +type RolesPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a a Page contains any results. +func (r RolesPage) IsEmpty() (bool, error) { + accessTokenRoles, err := ExtractRoles(r) + return len(accessTokenRoles) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r RolesPage) 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 +} + +// ExtractRoles returns a slice of Role contained in a single page of results. +func ExtractRoles(r pagination.Page) ([]Role, error) { + var s struct { + Roles []Role `json:"roles"` + } + err := (r.(RolesPage)).ExtractInto(&s) + return s.Roles, err +} + +type GetRoleResult struct { + gophercloud.Result +} + +// Extract interprets any GetRoleResult result as an Role. +func (r GetRoleResult) Extract() (*Role, error) { + var s struct { + Role *Role `json:"role"` + } + err := r.ExtractInto(&s) + return s.Role, err +} + +type CheckRoleResult struct { + gophercloud.ErrResult +} diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures.go b/openstack/identity/v3/extensions/trusts/testing/fixtures.go index c0d52be533..ff6a1e4476 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures.go +++ b/openstack/identity/v3/extensions/trusts/testing/fixtures.go @@ -114,6 +114,51 @@ const ListResponse = ` } ` +const ListTrustRolesResponse = ` +{ + "roles": [ + { + "id": "c1648e", + "links": { + "self": "http://example.com/identity/v3/roles/c1648e" + }, + "name": "manager" + }, + { + "id": "ed7b78", + "links": { + "self": "http://example.com/identity/v3/roles/ed7b78" + }, + "name": "member" + } + ] +} +` + +const GetTrustRoleResponse = ` +{ + "role": { + "id": "c1648e", + "links": { + "self": "http://example.com/identity/v3/roles/c1648e" + }, + "name": "manager" + } +} +` + +var FirstRole = trusts.Role{ + ID: "c1648e", + Name: "manager", +} + +var SecondRole = trusts.Role{ + ID: "ed7b78", + Name: "member", +} + +var ExpectedTrustRolesSlice = []trusts.Role{FirstRole, SecondRole} + // HandleCreateTokenWithTrustID verifies that providing certain AuthOptions and Scope results in an expected JSON structure. func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilder, requestJSON string) { testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { @@ -133,8 +178,8 @@ func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilde "OS-TRUST:trust": { "id": "fe0aef", "impersonation": false, - "redelegated_trust_id": "3ba234", - "redelegation_count": 2, + "redelegated_trust_id": "3ba234", + "redelegation_count": 2, "links": { "self": "http://example.com/identity/v3/trusts/fe0aef" }, @@ -246,3 +291,43 @@ func HandleListTrustsSuccessfully(t *testing.T) { fmt.Fprintf(w, ListResponse) }) } + +// HandleListTrustRolesSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles` on the +// test handler mux that responds with a list trust roles. +func HandleListTrustRolesSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListTrustRolesResponse) + }) +} + +// HandleGetTrustRoleSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles/c1648e` on the +// test handler mux that responds with a trust role details. +func HandleGetTrustRoleSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles/c1648e", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetTrustRoleResponse) + }) +} + +// HandleCheckTrustRoleSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles/c1648e` on the +// test handler mux that responds with a list trust roles. +func HandleCheckTrustRoleSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles/c1648e", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "HEAD") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusOK) + }) +} diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index 98cbd563f8..6b231b4284 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -156,3 +156,55 @@ func TestListTrustsFiltered(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedTrustsSlice, actual) } + +func TestListTrustRoles(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTrustRolesSuccessfully(t) + + count := 0 + err := trusts.ListRoles(client.ServiceClient(), "987fe8").EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := trusts.ExtractRoles(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedTrustRolesSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListTrustRolesAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListTrustRolesSuccessfully(t) + + allPages, err := trusts.ListRoles(client.ServiceClient(), "987fe8").AllPages() + th.AssertNoErr(t, err) + actual, err := trusts.ExtractRoles(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedTrustRolesSlice, actual) +} + +func TestGetTrustRole(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetTrustRoleSuccessfully(t) + + role, err := trusts.GetRole(client.ServiceClient(), "987fe8", "c1648e").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, FirstRole, *role) +} + +func TestCheckTrustRole(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCheckTrustRoleSuccessfully(t) + + err := trusts.CheckRole(client.ServiceClient(), "987fe8", "c1648e").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/identity/v3/extensions/trusts/urls.go b/openstack/identity/v3/extensions/trusts/urls.go index 22b62ab83c..3b1bf46534 100644 --- a/openstack/identity/v3/extensions/trusts/urls.go +++ b/openstack/identity/v3/extensions/trusts/urls.go @@ -23,3 +23,11 @@ func deleteURL(c *gophercloud.ServiceClient, id string) string { func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(resourcePath) } + +func listRolesURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "roles") +} + +func getRoleURL(c *gophercloud.ServiceClient, id, roleID string) string { + return c.ServiceURL(resourcePath, id, "roles", roleID) +} diff --git a/provider_client.go b/provider_client.go index 9490c00f9d..2b4b368f2b 100644 --- a/provider_client.go +++ b/provider_client.go @@ -523,16 +523,16 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts } func defaultOkCodes(method string) []int { - switch { - case method == "GET": + switch method { + case "GET", "HEAD": return []int{200} - case method == "POST": + case "POST": return []int{201, 202} - case method == "PUT": + case "PUT": return []int{201, 202} - case method == "PATCH": + case "PATCH": return []int{200, 202, 204} - case method == "DELETE": + case "DELETE": return []int{202, 204} } From a84883f981ccf9f4998de9111db84bc5130e747a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 20 Apr 2020 19:52:08 -0600 Subject: [PATCH 1068/2296] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3d1423f44..bda74b187e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,13 @@ IMPROVEMENTS * 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) + +BUG FIXES + +* Changed`identity/v3/extensions/trusts.Trust.RemainingUses` from `bool` to `int` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) ## 0.10.0 (April 12, 2020) From e1c764264031a265b768d2471dcc27849a13f833 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 21 Apr 2020 03:53:03 +0200 Subject: [PATCH 1069/2296] Swift: Simplify json unmarshal using ",string" struct tag (#1944) --- .../objectstorage/v1/accounts/results.go | 84 ++--------------- .../objectstorage/v1/containers/results.go | 91 +++---------------- openstack/objectstorage/v1/objects/results.go | 87 ++---------------- 3 files changed, 30 insertions(+), 232 deletions(-) diff --git a/openstack/objectstorage/v1/accounts/results.go b/openstack/objectstorage/v1/accounts/results.go index e25ae99abd..c9b7cb7eb1 100644 --- a/openstack/objectstorage/v1/accounts/results.go +++ b/openstack/objectstorage/v1/accounts/results.go @@ -2,7 +2,6 @@ package accounts import ( "encoding/json" - "strconv" "strings" "time" @@ -17,7 +16,7 @@ type UpdateResult struct { // UpdateHeader represents the headers returned in the response from an Update // request. type UpdateHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` TransID string `json:"X-Trans-Id"` Date time.Time `json:"-"` @@ -27,8 +26,7 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -37,16 +35,6 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { *r = UpdateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return err @@ -62,11 +50,11 @@ func (r UpdateResult) Extract() (*UpdateHeader, error) { // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { - BytesUsed int64 `json:"-"` - QuotaBytes *int64 `json:"-"` - ContainerCount int64 `json:"-"` - ContentLength int64 `json:"-"` - ObjectCount int64 `json:"-"` + BytesUsed int64 `json:"X-Account-Bytes-Used,string"` + QuotaBytes *int64 `json:"X-Account-Meta-Quota-Bytes,string"` + ContainerCount int64 `json:"X-Account-Container-Count,string"` + ContentLength int64 `json:"Content-Length,string"` + ObjectCount int64 `json:"X-Account-Object-Count,string"` ContentType string `json:"Content-Type"` TransID string `json:"X-Trans-Id"` TempURLKey string `json:"X-Account-Meta-Temp-URL-Key"` @@ -78,12 +66,7 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp - BytesUsed string `json:"X-Account-Bytes-Used"` - QuotaBytes string `json:"X-Account-Meta-Quota-Bytes"` - ContentLength string `json:"Content-Length"` - ContainerCount string `json:"X-Account-Container-Count"` - ObjectCount string `json:"X-Account-Object-Count"` - Date string `json:"Date"` + Date string `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -92,57 +75,6 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { *r = GetHeader(s.tmp) - switch s.BytesUsed { - case "": - r.BytesUsed = 0 - default: - r.BytesUsed, err = strconv.ParseInt(s.BytesUsed, 10, 64) - if err != nil { - return err - } - } - - switch s.QuotaBytes { - case "": - r.QuotaBytes = nil - default: - v, err := strconv.ParseInt(s.QuotaBytes, 10, 64) - if err != nil { - return err - } - r.QuotaBytes = &v - } - - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - - switch s.ObjectCount { - case "": - r.ObjectCount = 0 - default: - r.ObjectCount, err = strconv.ParseInt(s.ObjectCount, 10, 64) - if err != nil { - return err - } - } - - switch s.ContainerCount { - case "": - r.ContainerCount = 0 - default: - r.ContainerCount, err = strconv.ParseInt(s.ContainerCount, 10, 64) - if err != nil { - return err - } - } - if s.Date != "" { r.Date, err = time.Parse(time.RFC1123, s.Date) } diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index d2a13b1d28..14e390541f 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -3,7 +3,6 @@ package containers import ( "encoding/json" "fmt" - "strconv" "strings" "time" @@ -92,11 +91,11 @@ func ExtractNames(page pagination.Page) ([]string, error) { // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { AcceptRanges string `json:"Accept-Ranges"` - BytesUsed int64 `json:"-"` - ContentLength int64 `json:"-"` + BytesUsed int64 `json:"X-Container-Bytes-Used,string"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` - ObjectCount int64 `json:"-"` + ObjectCount int64 `json:"X-Container-Object-Count,string"` Read []string `json:"-"` TransID string `json:"X-Trans-Id"` VersionsLocation string `json:"X-Versions-Location"` @@ -111,12 +110,9 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp - BytesUsed string `json:"X-Container-Bytes-Used"` - ContentLength string `json:"Content-Length"` - ObjectCount string `json:"X-Container-Object-Count"` - 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"` } err := json.Unmarshal(b, &s) if err != nil { @@ -125,36 +121,6 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { *r = GetHeader(s.tmp) - switch s.BytesUsed { - case "": - r.BytesUsed = 0 - default: - r.BytesUsed, err = strconv.ParseInt(s.BytesUsed, 10, 64) - if err != nil { - return err - } - } - - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - - switch s.ObjectCount { - case "": - r.ObjectCount = 0 - default: - r.ObjectCount, err = strconv.ParseInt(s.ObjectCount, 10, 64) - if err != nil { - return err - } - } - r.Read = strings.Split(s.Read, ",") r.Write = strings.Split(s.Write, ",") @@ -194,7 +160,7 @@ 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:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -204,8 +170,7 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { type tmp CreateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -214,16 +179,6 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { *r = CreateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return err @@ -246,7 +201,7 @@ func (r CreateResult) Extract() (*CreateHeader, error) { // UpdateHeader represents the headers returned in the response from a Update // request. type UpdateHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -256,8 +211,7 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -266,16 +220,6 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { *r = UpdateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return err @@ -297,7 +241,7 @@ func (r UpdateResult) Extract() (*UpdateHeader, error) { // DeleteHeader represents the headers returned in the response from a Delete // request. type DeleteHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -307,8 +251,7 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { type tmp DeleteHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -317,16 +260,6 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { *r = DeleteHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return err diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index e841bcf9e2..4844b123e2 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -6,7 +6,6 @@ import ( "io" "io/ioutil" "net/url" - "strconv" "strings" "time" @@ -134,7 +133,7 @@ type DownloadHeader struct { AcceptRanges string `json:"Accept-Ranges"` ContentDisposition string `json:"Content-Disposition"` ContentEncoding string `json:"Content-Encoding"` - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` DeleteAt time.Time `json:"-"` @@ -149,7 +148,6 @@ func (r *DownloadHeader) UnmarshalJSON(b []byte) error { type tmp DownloadHeader var s struct { tmp - ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` @@ -162,16 +160,6 @@ func (r *DownloadHeader) UnmarshalJSON(b []byte) error { *r = DownloadHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - switch t := s.StaticLargeObject.(type) { case string: if t == "True" || t == "true" { @@ -224,7 +212,7 @@ func (r *DownloadResult) ExtractContent() ([]byte, error) { type GetHeader struct { ContentDisposition string `json:"Content-Disposition"` ContentEncoding string `json:"Content-Encoding"` - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` DeleteAt time.Time `json:"-"` @@ -239,7 +227,6 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp - ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` @@ -252,16 +239,6 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { *r = GetHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - switch t := s.StaticLargeObject.(type) { case string: if t == "True" || t == "true" { @@ -310,7 +287,7 @@ 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:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` ETag string `json:"Etag"` @@ -322,9 +299,8 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { type tmp CreateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` - LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` + Date gophercloud.JSONRFC1123 `json:"Date"` + LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` } err := json.Unmarshal(b, &s) if err != nil { @@ -333,16 +309,6 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { *r = CreateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) r.LastModified = time.Time(s.LastModified) @@ -368,7 +334,7 @@ func (r CreateResult) Extract() (*CreateHeader, error) { // UpdateHeader represents the headers returned in the response from a // Update request. type UpdateHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -378,8 +344,7 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -388,16 +353,6 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { *r = UpdateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return nil @@ -418,7 +373,7 @@ func (r UpdateResult) Extract() (*UpdateHeader, error) { // DeleteHeader represents the headers returned in the response from a // Delete request. type DeleteHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -428,8 +383,7 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { type tmp DeleteHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -438,16 +392,6 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { *r = DeleteHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return nil @@ -468,7 +412,7 @@ func (r DeleteResult) Extract() (*DeleteHeader, error) { // CopyHeader represents the headers returned in the response from a // Copy request. type CopyHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` CopiedFrom string `json:"X-Copied-From"` CopiedFromLastModified time.Time `json:"-"` @@ -482,7 +426,6 @@ func (r *CopyHeader) UnmarshalJSON(b []byte) error { type tmp CopyHeader var s struct { tmp - ContentLength string `json:"Content-Length"` CopiedFromLastModified gophercloud.JSONRFC1123 `json:"X-Copied-From-Last-Modified"` Date gophercloud.JSONRFC1123 `json:"Date"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` @@ -494,16 +437,6 @@ func (r *CopyHeader) UnmarshalJSON(b []byte) error { *r = CopyHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) r.CopiedFromLastModified = time.Time(s.CopiedFromLastModified) r.LastModified = time.Time(s.LastModified) From bd4044819c094a272941bbca3dd769d41232e922 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 23 Apr 2020 04:21:33 +0200 Subject: [PATCH 1070/2296] Testing: enable missed acceptance tests (#1953) * Testing: enable missed acceptance tests * Check for err before setting a variable * Fix blockstorage apiversions detection * Skip test, when no SFS messages were found --- .../v2/messages/messages_test.go | 4 ++++ openstack/baremetal/noauth/requests.go | 5 ++++- .../baremetalintrospection/noauth/requests.go | 5 ++++- openstack/blockstorage/apiversions/urls.go | 9 +++++---- script/acceptancetest | 16 +++++++++++++++- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go index faf8fc66ec..d3e65603d9 100644 --- a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go @@ -87,6 +87,10 @@ func TestMessageDelete(t *testing.T) { t.Fatalf("Unable to extract messages: %v", err) } + if len(allMessages) == 0 { + t.Skipf("No messages were found") + } + var messageID string for _, listedMessage := range allMessages { if listedMessage.RequestID != options.RequestID { diff --git a/openstack/baremetal/noauth/requests.go b/openstack/baremetal/noauth/requests.go index b5a1b58f0d..f5356ca52d 100644 --- a/openstack/baremetal/noauth/requests.go +++ b/openstack/baremetal/noauth/requests.go @@ -29,8 +29,11 @@ func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophe // "noauth" bare metal service. func NewBareMetalNoAuth(eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) + if err != nil { + return nil, err + } sc.Type = "baremetal" - return sc, err + return sc, nil } diff --git a/openstack/baremetalintrospection/noauth/requests.go b/openstack/baremetalintrospection/noauth/requests.go index a1c0857c96..97816cdf92 100644 --- a/openstack/baremetalintrospection/noauth/requests.go +++ b/openstack/baremetalintrospection/noauth/requests.go @@ -29,8 +29,11 @@ func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophe // "noauth" bare metal introspection service. func NewBareMetalIntrospectionNoAuth(eo EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) + if err != nil { + return nil, err + } sc.Type = "baremetal-inspector" - return sc, err + return sc, nil } diff --git a/openstack/blockstorage/apiversions/urls.go b/openstack/blockstorage/apiversions/urls.go index d3ec8e661d..a6a35d4225 100644 --- a/openstack/blockstorage/apiversions/urls.go +++ b/openstack/blockstorage/apiversions/urls.go @@ -1,13 +1,14 @@ package apiversions import ( - "net/url" + "strings" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" ) func listURL(c *gophercloud.ServiceClient) string { - u, _ := url.Parse(c.ServiceURL("")) - u.Path = "/" - return u.String() + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint } diff --git a/script/acceptancetest b/script/acceptancetest index 7aca654bdb..766fc61a9d 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -10,7 +10,17 @@ failed= # Run the acceptance tests. # Check the error code after each suite, but do not exit early if a suite failed. +# list generated by: +# find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq -c | awk '{print $2}' ACCEPTANCE_TESTS=( +acceptance/openstack + +# IronicEndpoint is required +# acceptance/openstack/baremetal/noauth +# No suitable endpoint could be found in the service catalog +# acceptance/openstack/baremetal/v1 + +acceptance/openstack/blockstorage acceptance/openstack/blockstorage/extensions # snapshots_test.go:16: Unable to create a blockstorage client: CinderEndpoint is required @@ -47,11 +57,12 @@ 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 # Resource not found # acceptance/openstack/networking/v2/extensions/fwaas - +# acceptance/openstack/networking/v2/extensions/fwaas_v2 acceptance/openstack/networking/v2/extensions/layer3 # Unable to create: Resource not found @@ -70,13 +81,16 @@ 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 # Unable to create: Resource not found # acceptance/openstack/networking/v2/extensions/vpnaas acceptance/openstack/objectstorage/v1 acceptance/openstack/orchestration/v1 +acceptance/openstack/placement/v1 acceptance/openstack/sharedfilesystems/v2 +acceptance/openstack/sharedfilesystems/v2/messages # No suitable endpoint could be found in the service catalog # acceptance/openstack/workflow/v2 From 7470098d4298ed7346b44a962ec3ac55513a7f18 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 23 Apr 2020 04:23:00 +0200 Subject: [PATCH 1071/2296] Core: reuse HTTP connection when possible (#1952) --- openstack/imageservice/v2/imagedata/doc.go | 3 + .../imageservice/v2/imagedata/requests.go | 4 +- .../imageservice/v2/imagedata/results.go | 10 +-- .../v2/imagedata/testing/requests_test.go | 2 + openstack/keymanager/v1/secrets/doc.go | 1 + openstack/keymanager/v1/secrets/requests.go | 5 +- openstack/keymanager/v1/secrets/results.go | 1 - openstack/messaging/v2/claims/requests.go | 11 +--- .../messaging/v2/claims/testing/fixtures.go | 12 ++++ .../v2/claims/testing/requests_test.go | 17 +++++ openstack/messaging/v2/messages/requests.go | 9 ++- openstack/objectstorage/v1/objects/doc.go | 4 ++ .../objectstorage/v1/objects/requests.go | 5 +- openstack/objectstorage/v1/objects/results.go | 1 - pagination/http.go | 5 +- provider_client.go | 26 +++++++- testing/provider_client_test.go | 65 ++++++++++++++++++- 17 files changed, 147 insertions(+), 34 deletions(-) diff --git a/openstack/imageservice/v2/imagedata/doc.go b/openstack/imageservice/v2/imagedata/doc.go index 0c12bf2e07..a9d7a58948 100644 --- a/openstack/imageservice/v2/imagedata/doc.go +++ b/openstack/imageservice/v2/imagedata/doc.go @@ -40,6 +40,9 @@ Example to Download Image Data panic(err) } + // close the reader, when reading has finished + defer image.Close() + imageData, err := ioutil.ReadAll(image) if err != nil { panic(err) diff --git a/openstack/imageservice/v2/imagedata/requests.go b/openstack/imageservice/v2/imagedata/requests.go index 087794f7c7..820a0c6993 100644 --- a/openstack/imageservice/v2/imagedata/requests.go +++ b/openstack/imageservice/v2/imagedata/requests.go @@ -30,7 +30,9 @@ func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r Stag // Download retrieves an image. func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { - resp, err := client.Get(downloadURL(client, id), nil, nil) + resp, err := client.Get(downloadURL(client, id), nil, &gophercloud.RequestOpts{ + KeepResponseBody: true, + }) r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/imageservice/v2/imagedata/results.go b/openstack/imageservice/v2/imagedata/results.go index 17a3f0e0f7..d9632f27b2 100644 --- a/openstack/imageservice/v2/imagedata/results.go +++ b/openstack/imageservice/v2/imagedata/results.go @@ -1,7 +1,6 @@ package imagedata import ( - "fmt" "io" "github.com/gophercloud/gophercloud" @@ -23,12 +22,13 @@ type StageResult struct { // method to gain access to the image data. type DownloadResult struct { gophercloud.Result + Body io.ReadCloser } // Extract builds images model from io.Reader -func (r DownloadResult) Extract() (io.Reader, error) { - if r, ok := r.Body.(io.Reader); ok { - return r, nil +func (r DownloadResult) Extract() (io.ReadCloser, error) { + if r.Err != nil { + return nil, r.Err } - return nil, fmt.Errorf("Expected io.Reader but got: %T(%#v)", r.Body, r.Body) + return r.Body, nil } diff --git a/openstack/imageservice/v2/imagedata/testing/requests_test.go b/openstack/imageservice/v2/imagedata/testing/requests_test.go index 7155f61b63..d141cf4cdf 100644 --- a/openstack/imageservice/v2/imagedata/testing/requests_test.go +++ b/openstack/imageservice/v2/imagedata/testing/requests_test.go @@ -94,6 +94,8 @@ func TestDownload(t *testing.T) { rdr, err := imagedata.Download(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea").Extract() th.AssertNoErr(t, err) + defer rdr.Close() + bs, err := ioutil.ReadAll(rdr) th.AssertNoErr(t, err) diff --git a/openstack/keymanager/v1/secrets/doc.go b/openstack/keymanager/v1/secrets/doc.go index 4167e425d2..2b5e299f1c 100644 --- a/openstack/keymanager/v1/secrets/doc.go +++ b/openstack/keymanager/v1/secrets/doc.go @@ -38,6 +38,7 @@ Example to Get a Secret Example to Get a Payload + // if "Extract" method is not called, the HTTP connection will remain consumed payload, err := secrets.GetPayload(client, secretID).Extract() if err != nil { panic(err) diff --git a/openstack/keymanager/v1/secrets/requests.go b/openstack/keymanager/v1/secrets/requests.go index 18f8b25177..35bc21fa6d 100644 --- a/openstack/keymanager/v1/secrets/requests.go +++ b/openstack/keymanager/v1/secrets/requests.go @@ -181,8 +181,9 @@ func GetPayload(client *gophercloud.ServiceClient, id string, opts GetPayloadOpt url := payloadURL(client, id) resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ - MoreHeaders: h, - OkCodes: []int{200}, + MoreHeaders: h, + OkCodes: []int{200}, + KeepResponseBody: true, }) r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/keymanager/v1/secrets/results.go b/openstack/keymanager/v1/secrets/results.go index a3230a8b4f..bd36036f3a 100644 --- a/openstack/keymanager/v1/secrets/results.go +++ b/openstack/keymanager/v1/secrets/results.go @@ -126,7 +126,6 @@ func (r PayloadResult) Extract() ([]byte, error) { if err != nil { return nil, err } - r.Body.Close() return body, nil } diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index cfd39d0a31..17789fbcea 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -50,19 +50,10 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts url += q } - resp, err := client.Post(url, b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(url, b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - if r.Err != nil { - return - } - // If the Claim has no content return an empty CreateResult - if resp.StatusCode == 204 { - r.Body = CreateResult{} - } else { - r.Body = resp.Body - } return } diff --git a/openstack/messaging/v2/claims/testing/fixtures.go b/openstack/messaging/v2/claims/testing/fixtures.go index 95ef1468a6..7efd483640 100644 --- a/openstack/messaging/v2/claims/testing/fixtures.go +++ b/openstack/messaging/v2/claims/testing/fixtures.go @@ -97,6 +97,18 @@ func HandleCreateSuccessfully(t *testing.T) { }) } +// HandleCreateNoContent configures the test server to respond to a Create request with no content. +func HandleCreateNoContent(t *testing.T) { + th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims", QueueName), + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestJSONRequest(t, r, CreateClaimRequest) + + w.WriteHeader(http.StatusNoContent) + }) +} + // HandleGetSuccessfully configures the test server to respond to a Get request. func HandleGetSuccessfully(t *testing.T) { th.Mux.HandleFunc(fmt.Sprintf("/v2/queues/%s/claims/%s", QueueName, ClaimID), diff --git a/openstack/messaging/v2/claims/testing/requests_test.go b/openstack/messaging/v2/claims/testing/requests_test.go index e3ee3d39a0..fea1e6d194 100644 --- a/openstack/messaging/v2/claims/testing/requests_test.go +++ b/openstack/messaging/v2/claims/testing/requests_test.go @@ -24,6 +24,23 @@ func TestCreate(t *testing.T) { th.CheckDeepEquals(t, CreatedClaim, actual) } +func TestCreateNoContent(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateNoContent(t) + + createOpts := claims.CreateOpts{ + TTL: 3600, + Grace: 3600, + Limit: 10, + } + + actual, err := claims.Create(fake.ServiceClient(), QueueName, createOpts).Extract() + th.AssertNoErr(t, err) + var expected []claims.Messages + th.CheckDeepEquals(t, expected, actual) +} + func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index 0ccc24cfab..14ea4dd653 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -168,12 +168,11 @@ func PopMessages(client *gophercloud.ServiceClient, queueName string, opts PopMe } url += query } - result, err := client.Delete(url, &gophercloud.RequestOpts{ - OkCodes: []int{200, 204}, + resp, err := client.Delete(url, &gophercloud.RequestOpts{ + JSONResponse: &r.Body, + OkCodes: []int{200, 204}, }) - r.Body = result.Body - r.Header = result.Header - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/objectstorage/v1/objects/doc.go b/openstack/objectstorage/v1/objects/doc.go index e9b4b8a9f3..7714460aad 100644 --- a/openstack/objectstorage/v1/objects/doc.go +++ b/openstack/objectstorage/v1/objects/doc.go @@ -98,6 +98,10 @@ Example to Download an Object's Data containerName := "my_container" object := objects.Download(objectStorageClient, containerName, objectName, nil) + if object.Err != nil { + panic(object.Err) + } + // if "ExtractContent" method is not called, the HTTP connection will remain consumed content, err := object.ExtractContent() if err != nil { panic(err) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index e85f83ee25..61e4afeff7 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -128,8 +128,9 @@ func Download(c *gophercloud.ServiceClient, containerName, objectName string, op } resp, err := c.Get(url, nil, &gophercloud.RequestOpts{ - MoreHeaders: h, - OkCodes: []int{200, 206, 304}, + MoreHeaders: h, + OkCodes: []int{200, 206, 304}, + KeepResponseBody: true, }) r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 4844b123e2..75367d8349 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -204,7 +204,6 @@ func (r *DownloadResult) ExtractContent() ([]byte, error) { if err != nil { return nil, err } - r.Body.Close() return body, nil } diff --git a/pagination/http.go b/pagination/http.go index 757295c423..df3503159a 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -54,7 +54,8 @@ func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { // Request performs an HTTP request and extracts the http.Response from the result. func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { return client.Get(url, nil, &gophercloud.RequestOpts{ - MoreHeaders: headers, - OkCodes: []int{200, 204, 300}, + MoreHeaders: headers, + OkCodes: []int{200, 204, 300}, + KeepResponseBody: true, }) } diff --git a/provider_client.go b/provider_client.go index 2b4b368f2b..43574402e9 100644 --- a/provider_client.go +++ b/provider_client.go @@ -305,6 +305,9 @@ type RequestOpts struct { // ErrorContext specifies the resource error type to return if an error is encountered. // This lets resources override default error messages based on the response status code. ErrorContext error + // KeepResponseBody specifies whether to keep the HTTP response body. Usually used, when the HTTP + // response body is considered for further use. Valid when JSONResponse is nil. + KeepResponseBody bool } // requestState contains temporary state for a single ProviderClient.Request() call. @@ -346,6 +349,11 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts contentType = &applicationJSON } + // Return an error, when "KeepResponseBody" is true and "JSONResponse" is not nil + if options.KeepResponseBody && options.JSONResponse != nil { + return nil, errors.New("cannot use KeepResponseBody when JSONResponse is not nil") + } + if options.RawBody != nil { body = options.RawBody } @@ -384,9 +392,6 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts req.Header.Set(k, v) } - // Set connection parameter to close the connection immediately when we've got the response - req.Close = true - prereqtok := req.Header.Get("X-Auth-Token") // Issue the request. @@ -514,11 +519,26 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts // Parse the response body as JSON, if requested to do so. if options.JSONResponse != nil { defer resp.Body.Close() + // Don't decode JSON when there is no content + if resp.StatusCode == http.StatusNoContent { + // read till EOF, otherwise the connection will be closed and cannot be reused + _, err = io.Copy(ioutil.Discard, resp.Body) + return resp, err + } if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { return nil, err } } + // Close unused body to allow the HTTP connection to be reused + if !options.KeepResponseBody && options.JSONResponse == nil { + defer resp.Body.Close() + // read till EOF, otherwise the connection will be closed and cannot be reused + if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { + return nil, err + } + } + return resp, nil } diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index de175029c3..0f11295bec 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -4,10 +4,12 @@ import ( "context" "fmt" "io/ioutil" + "net" "net/http" "net/http/httptest" "strings" "sync" + "sync/atomic" "testing" "time" @@ -100,6 +102,7 @@ func TestConcurrentReauth(t *testing.T) { wg := new(sync.WaitGroup) reqopts := new(gophercloud.RequestOpts) + reqopts.KeepResponseBody = true reqopts.MoreHeaders = map[string]string{ "X-Auth-Token": prereauthTok, } @@ -277,6 +280,7 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { wg := new(sync.WaitGroup) reqopts := new(gophercloud.RequestOpts) + reqopts.KeepResponseBody = true reqopts.MoreHeaders = map[string]string{ "X-Auth-Token": prereauthTok, } @@ -372,10 +376,11 @@ func TestRequestWithContext(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) p := &gophercloud.ProviderClient{Context: ctx} - res, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{}) + res, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) th.AssertNoErr(t, err) _, err = ioutil.ReadAll(res.Body) - res.Body.Close() + th.AssertNoErr(t, err) + err = res.Body.Close() th.AssertNoErr(t, err) cancel() @@ -387,3 +392,59 @@ func TestRequestWithContext(t *testing.T) { t.Fatalf("expecting error to contain: %q, got %q", ctx.Err().Error(), err.Error()) } } + +func TestRequestConnectionReuse(t *testing.T) { + ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "OK") + })) + + // an amount of iterations + var iter = 10000 + // connections tracks an amount of connections made + var connections int64 + + ts.Config.ConnState = func(_ net.Conn, s http.ConnState) { + // track an amount of connections + if s == http.StateNew { + atomic.AddInt64(&connections, 1) + } + } + ts.Start() + defer ts.Close() + + p := &gophercloud.ProviderClient{} + for i := 0; i < iter; i++ { + _, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: false}) + th.AssertNoErr(t, err) + } + + th.AssertEquals(t, int64(1), connections) +} + +func TestRequestConnectionClose(t *testing.T) { + ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "OK") + })) + + // an amount of iterations + var iter = 10 + // connections tracks an amount of connections made + var connections int64 + + ts.Config.ConnState = func(_ net.Conn, s http.ConnState) { + // track an amount of connections + if s == http.StateNew { + atomic.AddInt64(&connections, 1) + } + } + ts.Start() + defer ts.Close() + + p := &gophercloud.ProviderClient{} + for i := 0; i < iter; i++ { + _, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) + th.AssertNoErr(t, err) + } + + th.AssertEquals(t, int64(iter), connections) +} From 456b0b69b115e9e2baf3782bd1a843fefbff0479 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 22 Apr 2020 20:23:46 -0600 Subject: [PATCH 1072/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bda74b187e..4be86e56b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ 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 From 6917509ab11a17237b06ad7af64d0f261aaae90c Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 24 Apr 2020 03:35:39 +0200 Subject: [PATCH 1073/2296] Fix time elements handling in application credentials and trusts (#1937) --- .../v3/applicationcredentials_test.go | 19 ++-- .../openstack/identity/v3/trusts_test.go | 4 +- .../v3/applicationcredentials/requests.go | 18 +++- .../testing/fixtures.go | 9 +- .../testing/requests_test.go | 2 +- .../identity/v3/extensions/trusts/requests.go | 1 - .../v3/extensions/trusts/testing/fixtures.go | 97 ++++++++++++++++++- .../trusts/testing/requests_test.go | 27 +++++- 8 files changed, 148 insertions(+), 29 deletions(-) diff --git a/acceptance/openstack/identity/v3/applicationcredentials_test.go b/acceptance/openstack/identity/v3/applicationcredentials_test.go index 3046f7dd7f..d24d0d2356 100644 --- a/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -15,7 +15,6 @@ import ( ) func TestApplicationCredentialsCRD(t *testing.T) { - clients.RequireAdmin(t) clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") clients.SkipRelease(t, "stable/ocata") @@ -82,11 +81,12 @@ func TestApplicationCredentialsCRD(t *testing.T) { tools.PrintResource(t, apRoles) // restricted, limited TTL, with limited roles, autogenerated secret + expiresAt := time.Now().Add(time.Minute).Truncate(time.Millisecond).UTC() createOpts := applicationcredentials.CreateOpts{ Name: "test-ac", Description: "test application credential", Roles: apRoles, - ExpiresAt: "2119-01-01T12:12:12Z", + ExpiresAt: &expiresAt, } applicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() @@ -98,7 +98,7 @@ func TestApplicationCredentialsCRD(t *testing.T) { t.Fatalf("Application credential secret was not generated") } - th.AssertEquals(t, applicationCredential.ExpiresAt.UTC().Format(time.RFC3339), createOpts.ExpiresAt) + th.AssertEquals(t, applicationCredential.ExpiresAt, expiresAt) th.AssertEquals(t, applicationCredential.Name, createOpts.Name) th.AssertEquals(t, applicationCredential.Description, createOpts.Description) th.AssertEquals(t, applicationCredential.Unrestricted, false) @@ -125,7 +125,7 @@ func TestApplicationCredentialsCRD(t *testing.T) { t.Fatalf("Application credential secret should not be returned by a GET request") } - th.AssertEquals(t, getApplicationCredential.ExpiresAt.UTC().Format(time.RFC3339), createOpts.ExpiresAt) + th.AssertEquals(t, getApplicationCredential.ExpiresAt, expiresAt) th.AssertEquals(t, getApplicationCredential.Name, createOpts.Name) th.AssertEquals(t, getApplicationCredential.Description, createOpts.Description) th.AssertEquals(t, getApplicationCredential.Unrestricted, false) @@ -156,12 +156,12 @@ func TestApplicationCredentialsCRD(t *testing.T) { defer applicationcredentials.Delete(client, user.ID, newApplicationCredential.ID) tools.PrintResource(t, newApplicationCredential) - th.AssertEquals(t, newApplicationCredential.ExpiresAt.UTC(), time.Time{}) + th.AssertEquals(t, newApplicationCredential.ExpiresAt, time.Time{}) th.AssertEquals(t, newApplicationCredential.Name, createOpts.Name) th.AssertEquals(t, newApplicationCredential.Description, createOpts.Description) th.AssertEquals(t, newApplicationCredential.Secret, createOpts.Secret) th.AssertEquals(t, newApplicationCredential.Unrestricted, true) - th.AssertEquals(t, newApplicationCredential.ExpiresAt.UTC(), time.Time{}) + th.AssertEquals(t, newApplicationCredential.ExpiresAt, time.Time{}) th.AssertEquals(t, newApplicationCredential.ProjectID, project.ID) checkACroles = rolesToMap(newApplicationCredential.Roles) @@ -226,11 +226,12 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { tools.PrintResource(t, apAccessRules) // restricted, limited TTL, with limited roles, autogenerated secret + expiresAt := time.Now().Add(time.Minute).Truncate(time.Millisecond).UTC() createOpts := applicationcredentials.CreateOpts{ Name: "test-ac", Description: "test application credential", AccessRules: apAccessRules, - ExpiresAt: "2119-01-01T12:12:12Z", + ExpiresAt: &expiresAt, } applicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() @@ -242,7 +243,7 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { t.Fatalf("Application credential secret was not generated") } - th.AssertEquals(t, applicationCredential.ExpiresAt.UTC().Format(time.RFC3339), createOpts.ExpiresAt) + th.AssertEquals(t, applicationCredential.ExpiresAt, expiresAt) th.AssertEquals(t, applicationCredential.Name, createOpts.Name) th.AssertEquals(t, applicationCredential.Description, createOpts.Description) th.AssertEquals(t, applicationCredential.Unrestricted, false) @@ -262,7 +263,7 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { t.Fatalf("Application credential secret should not be returned by a GET request") } - th.AssertEquals(t, getApplicationCredential.ExpiresAt.UTC().Format(time.RFC3339), createOpts.ExpiresAt) + th.AssertEquals(t, getApplicationCredential.ExpiresAt, expiresAt) th.AssertEquals(t, getApplicationCredential.Name, createOpts.Name) th.AssertEquals(t, getApplicationCredential.Description, createOpts.Description) th.AssertEquals(t, getApplicationCredential.Unrestricted, false) diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/acceptance/openstack/identity/v3/trusts_test.go index dd1cb37d39..44a838af62 100644 --- a/acceptance/openstack/identity/v3/trusts_test.go +++ b/acceptance/openstack/identity/v3/trusts_test.go @@ -81,7 +81,7 @@ func TestTrustCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteUser(t, client, trusteeUser.ID) - expiresAt := time.Now().Add(time.Minute) + expiresAt := time.Now().Add(time.Minute).Truncate(time.Second).UTC() // Create a trust. trust, err := CreateTrust(t, client, trusts.CreateOpts{ TrusteeUserID: trusteeUser.ID, @@ -103,7 +103,7 @@ func TestTrustCRUD(t *testing.T) { // Get trust p, err := trusts.Get(client, trust.ID).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, p.ExpiresAt.IsZero(), false) + th.AssertEquals(t, p.ExpiresAt, expiresAt) th.AssertEquals(t, p.DeletedAt.IsZero(), true) tools.PrintResource(t, p) diff --git a/openstack/identity/v3/applicationcredentials/requests.go b/openstack/identity/v3/applicationcredentials/requests.go index 2f21464645..be62af7f78 100644 --- a/openstack/identity/v3/applicationcredentials/requests.go +++ b/openstack/identity/v3/applicationcredentials/requests.go @@ -1,6 +1,8 @@ package applicationcredentials import ( + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -70,12 +72,24 @@ type CreateOpts struct { // A list of access rules objects. AccessRules []AccessRule `json:"access_rules,omitempty"` // The expiration time of the application credential, if one was specified. - ExpiresAt string `json:"expires_at,omitempty"` + ExpiresAt *time.Time `json:"-"` } // ToApplicationCredentialCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToApplicationCredentialCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "application_credential") + parent := "application_credential" + b, err := gophercloud.BuildRequestBody(opts, parent) + if err != nil { + return nil, err + } + + if opts.ExpiresAt != nil { + if v, ok := b[parent].(map[string]interface{}); ok { + v["expires_at"] = opts.ExpiresAt.Format(gophercloud.RFC3339MilliNoZ) + } + } + + return b, nil } // Create creates a new ApplicationCredential. diff --git a/openstack/identity/v3/applicationcredentials/testing/fixtures.go b/openstack/identity/v3/applicationcredentials/testing/fixtures.go index 86dbf4c335..25803415db 100644 --- a/openstack/identity/v3/applicationcredentials/testing/fixtures.go +++ b/openstack/identity/v3/applicationcredentials/testing/fixtures.go @@ -6,7 +6,6 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" @@ -60,7 +59,7 @@ const ListOutput = ` "name": "network_viewer" } ], - "expires_at": "2019-03-12T12:12:12.000000", + "expires_at": "2019-03-12T12:12:12.123456", "unrestricted": true, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", "id": "6b8cc7647da64166a4a3cc0c88ebbabb", @@ -208,7 +207,7 @@ const CreateUnrestrictedRequest = ` "id": "4494bc5bea1a4105ad7fbba6a7eb9ef4" } ], - "expires_at": "2019-03-12T12:12:12.000000", + "expires_at": "2019-03-12T12:12:12.123456", "name": "test2" } } @@ -233,7 +232,7 @@ const CreateUnrestrictedResponse = ` "name": "network_viewer" } ], - "expires_at": "2019-03-12T12:12:12.000000", + "expires_at": "2019-03-12T12:12:12.123456", "secret": "generated_secret", "unrestricted": true, "project_id": "53c2b94f63fb4f43a21b92d119ce549f", @@ -326,7 +325,7 @@ var ApplicationCredentialNoSecretResponse = applicationcredentials.ApplicationCr }, } -var ApplationCredentialExpiresAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2019-03-12T12:12:12.000000") +var ApplationCredentialExpiresAt = time.Date(2019, 3, 12, 12, 12, 12, 123456000, time.UTC) var UnrestrictedApplicationCredential = applicationcredentials.ApplicationCredential{ ID: "6b8cc7647da64166a4a3cc0c88ebbabb", Name: "test2", diff --git a/openstack/identity/v3/applicationcredentials/testing/requests_test.go b/openstack/identity/v3/applicationcredentials/testing/requests_test.go index 00dc548c25..c14ed379f8 100644 --- a/openstack/identity/v3/applicationcredentials/testing/requests_test.go +++ b/openstack/identity/v3/applicationcredentials/testing/requests_test.go @@ -110,7 +110,7 @@ func TestCreateUnrestrictedApplicationCredential(t *testing.T) { applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, applicationcredentials.Role{ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4"}, }, - ExpiresAt: "2019-03-12T12:12:12.000000", + ExpiresAt: &ApplationCredentialExpiresAt, } UnrestrictedApplicationCredentialResponse := UnrestrictedApplicationCredential diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index a8879d8545..0677b7d4df 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -83,7 +83,6 @@ type CreateOpts struct { // ToTrustCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToTrustCreateMap() (map[string]interface{}, error) { parent := "trust" - b, err := gophercloud.BuildRequestBody(opts, parent) if err != nil { return nil, err diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures.go b/openstack/identity/v3/extensions/trusts/testing/fixtures.go index ff6a1e4476..0980cb1034 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures.go +++ b/openstack/identity/v3/extensions/trusts/testing/fixtures.go @@ -15,7 +15,24 @@ import ( const CreateRequest = ` { "trust": { - "expires_at": "2019-12-01T14:00:00.999999Z", + "expires_at": "2019-12-01T14:00:00Z", + "impersonation": false, + "allow_redelegation": true, + "project_id": "9b71012f5a4a4aef9193f1995fe159b2", + "roles": [ + { + "name": "member" + } + ], + "trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf", + "trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3" + } +} +` + +const CreateRequestNoExpire = ` +{ + "trust": { "impersonation": false, "allow_redelegation": true, "project_id": "9b71012f5a4a4aef9193f1995fe159b2", @@ -33,7 +50,30 @@ const CreateRequest = ` const CreateResponse = ` { "trust": { - "expires_at": "2019-12-01T14:00:00.999999Z", + "expires_at": "2019-12-01T14:00:00.000000Z", + "id": "3422b7c113894f5d90665e1a79655e23", + "impersonation": false, + "redelegation_count": 10, + "project_id": "9b71012f5a4a4aef9193f1995fe159b2", + "remaining_uses": null, + "roles": [ + { + "id": "b627fca5-beb0-471a-9857-0e852b719e76", + "links": { + "self": "http://example.com/identity/v3/roles/b627fca5-beb0-471a-9857-0e852b719e76" + }, + "name": "member" + } + ], + "trustee_user_id": "ecb37e88cc86431c99d0332208cb6fbf", + "trustor_user_id": "959ed913a32c4ec88c041c98e61cbbc3" + } +} +` + +const CreateResponseNoExpire = ` +{ + "trust": { "id": "3422b7c113894f5d90665e1a79655e23", "impersonation": false, "redelegation_count": 10, @@ -59,7 +99,7 @@ const GetResponse = ` { "trust": { "id": "987fe8", - "expires_at": "2013-02-27T18:30:59.999999Z", + "expires_at": "2013-02-27T18:30:59.000000Z", "impersonation": true, "links": { "self": "http://example.com/identity/v3/OS-TRUST/trusts/987fe8" @@ -91,7 +131,7 @@ const ListResponse = ` "trusts": [ { "id": "1ff900", - "expires_at": "2013-02-27T18:30:59.999999Z", + "expires_at": "2019-12-01T14:00:00.000000Z", "impersonation": true, "links": { "self": "http://example.com/identity/v3/OS-TRUST/trusts/1ff900" @@ -230,6 +270,20 @@ func HandleCreateTrust(t *testing.T) { }) } +// HandleCreateTrustNoExpire creates an HTTP handler at `/OS-TRUST/trusts` on the +// test handler mux that tests trust creation. +func HandleCreateTrustNoExpire(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "POST") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + testhelper.TestJSONRequest(t, r, CreateRequestNoExpire) + + w.WriteHeader(http.StatusCreated) + _, err := fmt.Fprintf(w, CreateResponseNoExpire) + testhelper.AssertNoErr(t, err) + }) +} + // HandleDeleteUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests user deletion. func HandleDeleteTrust(t *testing.T) { @@ -261,7 +315,7 @@ var FirstTrust = trusts.Trust{ TrusteeUserID: "86c0d5", TrustorUserID: "a0fdfd", ProjectID: "0f1233", - ExpiresAt: time.Date(2013, 02, 27, 18, 30, 59, 999999000, time.UTC), + ExpiresAt: time.Date(2019, 12, 01, 14, 00, 00, 0, time.UTC), DeletedAt: time.Time{}, } @@ -275,6 +329,39 @@ var SecondTrust = trusts.Trust{ DeletedAt: time.Time{}, } +var CreatedTrust = trusts.Trust{ + ID: "3422b7c113894f5d90665e1a79655e23", + Impersonation: false, + TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", + TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", + ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", + ExpiresAt: time.Date(2019, 12, 01, 14, 00, 00, 0, time.UTC), + DeletedAt: time.Time{}, + RedelegationCount: 10, + Roles: []trusts.Role{ + { + ID: "b627fca5-beb0-471a-9857-0e852b719e76", + Name: "member", + }, + }, +} + +var CreatedTrustNoExpire = trusts.Trust{ + ID: "3422b7c113894f5d90665e1a79655e23", + Impersonation: false, + TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", + TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", + ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", + DeletedAt: time.Time{}, + RedelegationCount: 10, + Roles: []trusts.Role{ + { + ID: "b627fca5-beb0-471a-9857-0e852b719e76", + Name: "member", + }, + }, +} + // ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. var ExpectedTrustsSlice = []trusts.Trust{FirstTrust, SecondTrust} diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index 6b231b4284..b2aa1e5ca0 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -73,7 +73,7 @@ func TestCreateTrust(t *testing.T) { defer th.TeardownHTTP() HandleCreateTrust(t) - expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC) + expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 0, time.UTC) result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{ ExpiresAt: &expiresAt, AllowRedelegation: true, @@ -88,9 +88,28 @@ func TestCreateTrust(t *testing.T) { }).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, "3422b7c113894f5d90665e1a79655e23", result.ID) - th.AssertEquals(t, false, result.Impersonation) - th.AssertEquals(t, 10, result.RedelegationCount) + th.AssertDeepEquals(t, CreatedTrust, *result) +} + +func TestCreateTrustNoExpire(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateTrustNoExpire(t) + + result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{ + AllowRedelegation: true, + ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", + Roles: []trusts.Role{ + { + Name: "member", + }, + }, + TrusteeUserID: "ecb37e88cc86431c99d0332208cb6fbf", + TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", + }).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, CreatedTrustNoExpire, *result) } func TestDeleteTrust(t *testing.T) { From 10aa97469c8cd3faf4d93a21dd42ccb45ef5ded6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 23 Apr 2020 19:36:55 -0600 Subject: [PATCH 1074/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4be86e56b6..52199ac32d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ IMPROVEMENTS BUG FIXES * Changed`identity/v3/extensions/trusts.Trust.RemainingUses` from `bool` to `int` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Change `identity/v3/applicationcredentials.CreateOpts.ExpiresAt` from string to time [GH-1937](https://github.com/gophercloud/gophercloud/pull/1937) ## 0.10.0 (April 12, 2020) From 6f8ab5563e549c3922db21d464612d60c6339811 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 23 Apr 2020 19:37:44 -0600 Subject: [PATCH 1075/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52199ac32d..ef7b6627a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ IMPROVEMENTS BUG FIXES * Changed`identity/v3/extensions/trusts.Trust.RemainingUses` from `bool` to `int` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) -* Change `identity/v3/applicationcredentials.CreateOpts.ExpiresAt` from string to time [GH-1937](https://github.com/gophercloud/gophercloud/pull/1937) +* Changed `identity/v3/applicationcredentials.CreateOpts.ExpiresAt` from `string` to `*time.Time` [GH-1937](https://github.com/gophercloud/gophercloud/pull/1937) ## 0.10.0 (April 12, 2020) From 105a9694c07417728e1c8616497aeef7e192d2b0 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 24 Apr 2020 03:39:10 +0200 Subject: [PATCH 1076/2296] Keystone V3: Introduce OAuth1 auth support (#1935) * Keystone V3: Introduce OAuth1 auth support * Code review * Add create interfaces * Extend v3 tokens.AuthOptionsBuilder interface * Properly consume ToTokenV3CreateMap * Typo fix in comment * Fix typos in comments * Do less url conversions * refactor * go vet fixes * Move oauth1 json structs inside the ToTokenV3CreateMap method * Use "client.Post" method * Rebase to master * Close body Co-authored-by: Joe Topjian --- .../openstack/identity/v3/oauth1_test.go | 203 ++++++ auth_options.go | 10 + openstack/client.go | 7 + .../v3/extensions/ec2tokens/requests.go | 9 +- .../identity/v3/extensions/oauth1/doc.go | 123 ++++ .../identity/v3/extensions/oauth1/requests.go | 587 ++++++++++++++++++ .../identity/v3/extensions/oauth1/results.go | 305 +++++++++ .../v3/extensions/oauth1/testing/fixtures.go | 454 ++++++++++++++ .../oauth1/testing/requests_test.go | 270 ++++++++ .../identity/v3/extensions/oauth1/urls.go | 43 ++ openstack/identity/v3/tokens/requests.go | 9 +- testhelper/http_responses.go | 14 + 12 files changed, 2032 insertions(+), 2 deletions(-) create mode 100644 acceptance/openstack/identity/v3/oauth1_test.go create mode 100644 openstack/identity/v3/extensions/oauth1/doc.go create mode 100644 openstack/identity/v3/extensions/oauth1/requests.go create mode 100644 openstack/identity/v3/extensions/oauth1/results.go create mode 100644 openstack/identity/v3/extensions/oauth1/testing/fixtures.go create mode 100644 openstack/identity/v3/extensions/oauth1/testing/requests_test.go create mode 100644 openstack/identity/v3/extensions/oauth1/urls.go diff --git a/acceptance/openstack/identity/v3/oauth1_test.go b/acceptance/openstack/identity/v3/oauth1_test.go new file mode 100644 index 0000000000..b86b33a193 --- /dev/null +++ b/acceptance/openstack/identity/v3/oauth1_test.go @@ -0,0 +1,203 @@ +// +build acceptance + +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" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestOAuth1CRUD(t *testing.T) { + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + ao, err := openstack.AuthOptionsFromEnv() + th.AssertNoErr(t, err) + + authOptions := tokens.AuthOptions{ + Username: ao.Username, + Password: ao.Password, + DomainName: ao.DomainName, + DomainID: ao.DomainID, + // We need a scope to get the token roles list + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, + } + tokenRes := tokens.Create(client, &authOptions) + token, err := tokenRes.Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) + + user, err := tokenRes.ExtractUser() + th.AssertNoErr(t, err) + tools.PrintResource(t, user) + + roles, err := tokenRes.ExtractRoles() + th.AssertNoErr(t, err) + tools.PrintResource(t, roles) + + project, err := tokenRes.ExtractProject() + th.AssertNoErr(t, err) + tools.PrintResource(t, project) + + // Create a consumer + createConsumerOpts := oauth1.CreateConsumerOpts{ + Description: "My test consumer", + } + // NOTE: secret is available only in create response + consumer, err := oauth1.CreateConsumer(client, createConsumerOpts).Extract() + th.AssertNoErr(t, err) + + // Delete a consumer + defer oauth1.DeleteConsumer(client, consumer.ID) + tools.PrintResource(t, consumer) + + th.AssertEquals(t, consumer.Description, createConsumerOpts.Description) + + // Update a consumer + updateConsumerOpts := oauth1.UpdateConsumerOpts{ + Description: "", + } + updatedConsumer, err := oauth1.UpdateConsumer(client, consumer.ID, updateConsumerOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, updatedConsumer) + th.AssertEquals(t, updatedConsumer.ID, consumer.ID) + th.AssertEquals(t, updatedConsumer.Description, updateConsumerOpts.Description) + + // Get a consumer + getConsumer, err := oauth1.GetConsumer(client, consumer.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getConsumer) + th.AssertEquals(t, getConsumer.ID, consumer.ID) + th.AssertEquals(t, getConsumer.Description, updateConsumerOpts.Description) + + // List consumers + consumersPages, err := oauth1.ListConsumers(client).AllPages() + th.AssertNoErr(t, err) + consumers, err := oauth1.ExtractConsumers(consumersPages) + th.AssertNoErr(t, err) + th.AssertEquals(t, consumers[0].ID, updatedConsumer.ID) + th.AssertEquals(t, consumers[0].Description, updatedConsumer.Description) + + // test HMACSHA1 and PLAINTEXT signature methods + for _, method := range []oauth1.SignatureMethod{oauth1.HMACSHA1, oauth1.PLAINTEXT} { + oauth1MethodTest(t, client, consumer, method, user, project, roles, ao.IdentityEndpoint) + } +} + +func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer *oauth1.Consumer, method oauth1.SignatureMethod, user *tokens.User, project *tokens.Project, roles []tokens.Role, identityEndpoint string) { + // Request a token + requestTokenOpts := oauth1.RequestTokenOpts{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthSignatureMethod: method, + RequestedProjectID: project.ID, + } + requestToken, err := oauth1.RequestToken(client, requestTokenOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, requestToken) + + // Authorize token + authorizeTokenOpts := oauth1.AuthorizeTokenOpts{ + Roles: []oauth1.Role{ + // test role by ID + {ID: roles[0].ID}, + }, + } + if len(roles) > 1 { + // test role by name + authorizeTokenOpts.Roles = append(authorizeTokenOpts.Roles, oauth1.Role{Name: roles[1].Name}) + } + authToken, err := oauth1.AuthorizeToken(client, requestToken.OAuthToken, authorizeTokenOpts).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, authToken) + + // Create access token + accessTokenOpts := oauth1.CreateAccessTokenOpts{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthToken: requestToken.OAuthToken, + OAuthTokenSecret: requestToken.OAuthTokenSecret, + OAuthVerifier: authToken.OAuthVerifier, + OAuthSignatureMethod: method, + } + accessToken, err := oauth1.CreateAccessToken(client, accessTokenOpts).Extract() + th.AssertNoErr(t, err) + defer oauth1.RevokeAccessToken(client, user.ID, accessToken.OAuthToken) + tools.PrintResource(t, accessToken) + + // Get access token + getAccessToken, err := oauth1.GetAccessToken(client, user.ID, accessToken.OAuthToken).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getAccessToken) + th.AssertEquals(t, getAccessToken.ID, accessToken.OAuthToken) + th.AssertEquals(t, getAccessToken.ConsumerID, consumer.ID) + th.AssertEquals(t, getAccessToken.AuthorizingUserID, user.ID) + th.AssertEquals(t, getAccessToken.ProjectID, project.ID) + + // List access tokens + accessTokensPages, err := oauth1.ListAccessTokens(client, user.ID).AllPages() + th.AssertNoErr(t, err) + accessTokens, err := oauth1.ExtractAccessTokens(accessTokensPages) + th.AssertNoErr(t, err) + tools.PrintResource(t, accessTokens) + th.AssertDeepEquals(t, accessTokens[0], *getAccessToken) + + // List access token roles + accessTokenRolesPages, err := oauth1.ListAccessTokenRoles(client, user.ID, accessToken.OAuthToken).AllPages() + th.AssertNoErr(t, err) + accessTokenRoles, err := oauth1.ExtractAccessTokenRoles(accessTokenRolesPages) + th.AssertNoErr(t, err) + tools.PrintResource(t, accessTokenRoles) + th.AssertEquals(t, accessTokenRoles[0].ID, roles[0].ID) + if len(accessTokenRoles) > 1 { + th.AssertEquals(t, accessTokenRoles[1].Name, roles[1].Name) + } + + // Get access token role + getAccessTokenRole, err := oauth1.GetAccessTokenRole(client, user.ID, accessToken.OAuthToken, roles[0].ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getAccessTokenRole) + th.AssertDeepEquals(t, *getAccessTokenRole, accessTokenRoles[0]) + + // Test auth using OAuth1 + newClient, err := clients.NewIdentityV3UnauthenticatedClient() + th.AssertNoErr(t, err) + + // Opts to auth using an oauth1 credential + authOptions := &oauth1.AuthOptions{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthToken: accessToken.OAuthToken, + OAuthTokenSecret: accessToken.OAuthTokenSecret, + OAuthSignatureMethod: method, + } + err = openstack.AuthenticateV3(newClient.ProviderClient, authOptions, gophercloud.EndpointOpts{}) + th.AssertNoErr(t, err) + + // Test OAuth1 token extract + var token struct { + tokens.Token + oauth1.TokenExt + } + tokenRes := tokens.Get(newClient, newClient.ProviderClient.TokenID) + err = tokenRes.ExtractInto(&token) + th.AssertNoErr(t, err) + oauth1Roles, err := tokenRes.ExtractRoles() + th.AssertNoErr(t, err) + tools.PrintResource(t, token) + tools.PrintResource(t, oauth1Roles) + th.AssertEquals(t, token.OAuth1.ConsumerID, consumer.ID) + th.AssertEquals(t, token.OAuth1.AccessTokenID, accessToken.OAuthToken) +} diff --git a/auth_options.go b/auth_options.go index f13cdd2f62..4f301305e6 100644 --- a/auth_options.go +++ b/auth_options.go @@ -137,6 +137,8 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { return map[string]interface{}{"auth": authMap}, nil } +// ToTokenV3CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { type domainReq struct { ID *string `json:"id,omitempty"` @@ -401,6 +403,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s return b, nil } +// ToTokenV3ScopeMap builds a scope from AuthOptions and satisfies interface in +// the v3 tokens package. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { // For backwards compatibility. // If AuthOptions.Scope was not set, try to determine it. @@ -502,3 +506,9 @@ func (opts AuthOptions) CanReauth() bool { return opts.AllowReauth } + +// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package. +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { + return nil, nil +} diff --git a/openstack/client.go b/openstack/client.go index fef4bcf8a2..655a9f6b91 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/openstack/utils" ) @@ -229,6 +230,8 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au switch opts.(type) { case *ec2tokens.AuthOptions: result = ec2tokens.Create(v3Client, opts) + case *oauth1.AuthOptions: + result = oauth1.Create(v3Client, opts) default: result = tokens3.Create(v3Client, opts) } @@ -266,6 +269,10 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au o := *ot o.AllowReauth = false tao = &o + case *oauth1.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o default: tao = opts } diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go index bcf5a8fb0f..32ba0e621d 100644 --- a/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -202,11 +202,18 @@ func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders st signature) } -// ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder interface +// ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder +// interface. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { return nil, nil } +// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package. +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { + return nil, nil +} + // CanReauth is a method method to satisfy tokens.AuthOptionsBuilder interface func (opts *AuthOptions) CanReauth() bool { return opts.AllowReauth diff --git a/openstack/identity/v3/extensions/oauth1/doc.go b/openstack/identity/v3/extensions/oauth1/doc.go new file mode 100644 index 0000000000..c5b0831ca1 --- /dev/null +++ b/openstack/identity/v3/extensions/oauth1/doc.go @@ -0,0 +1,123 @@ +/* +Package oauth1 enables management of OpenStack OAuth1 tokens and Authentication. + +Example to Create an OAuth1 Consumer + + createConsumerOpts := oauth1.CreateConsumerOpts{ + Description: "My consumer", + } + consumer, err := oauth1.CreateConsumer(identityClient, createConsumerOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Consumer secret is available only on create response + fmt.Printf("Consumer: %+v\n", consumer) + +Example to Request an unauthorized OAuth1 token + + requestTokenOpts := oauth1.RequestTokenOpts{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthSignatureMethod: oauth1.HMACSHA1, + RequestedProjectID: projectID, + } + requestToken, err := oauth1.RequestToken(identityClient, requestTokenOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Request token secret is available only on request response + fmt.Printf("Request token: %+v\n", requestToken) + +Example to Authorize an unauthorized OAuth1 token + + authorizeTokenOpts := oauth1.AuthorizeTokenOpts{ + Roles: []oauth1.Role{ + {Name: "member"}, + }, + } + authToken, err := oauth1.AuthorizeToken(identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("Verifier ID of the unauthorized Token: %+v\n", authToken.OAuthVerifier) + +Example to Create an OAuth1 Access Token + + accessTokenOpts := oauth1.CreateAccessTokenOpts{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthToken: requestToken.OAuthToken, + OAuthTokenSecret: requestToken.OAuthTokenSecret, + OAuthVerifier: authToken.OAuthVerifier, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + accessToken, err := oauth1.CreateAccessToken(identityClient, accessTokenOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Access token secret is available only on create response + fmt.Printf("OAuth1 Access Token: %+v\n", accessToken) + +Example to List User's OAuth1 Access Tokens + + allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages() + if err != nil { + panic(err) + } + accessTokens, err := oauth1.ExtractAccessTokens(allPages) + if err != nil { + panic(err) + } + + for _, accessToken := range accessTokens { + fmt.Printf("Access Token: %+v\n", accessToken) + } + +Example to Authenticate a client using OAuth1 method + + client, err := openstack.NewClient("http://localhost:5000/v3") + if err != nil { + panic(err) + } + + authOptions := &oauth1.AuthOptions{ + // consumer token, created earlier + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + // access token, created earlier + OAuthToken: accessToken.OAuthToken, + OAuthTokenSecret: accessToken.OAuthTokenSecret, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) + if err != nil { + panic(err) + } + +Example to Create a Token using OAuth1 method + + var oauth1Token struct { + tokens.Token + oauth1.TokenExt + } + + createOpts := &oauth1.AuthOptions{ + // consumer token, created earlier + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + // access token, created earlier + OAuthToken: accessToken.OAuthToken, + OAuthTokenSecret: accessToken.OAuthTokenSecret, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + err := tokens.Create(identityClient, createOpts).ExtractInto(&oauth1Token) + if err != nil { + panic(err) + } + +*/ +package oauth1 diff --git a/openstack/identity/v3/extensions/oauth1/requests.go b/openstack/identity/v3/extensions/oauth1/requests.go new file mode 100644 index 0000000000..028b5a45bd --- /dev/null +++ b/openstack/identity/v3/extensions/oauth1/requests.go @@ -0,0 +1,587 @@ +package oauth1 + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "fmt" + "io/ioutil" + "math/rand" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/pagination" +) + +// Type SignatureMethod is a OAuth1 SignatureMethod type. +type SignatureMethod string + +const ( + // HMACSHA1 is a recommended OAuth1 signature method. + HMACSHA1 SignatureMethod = "HMAC-SHA1" + + // PLAINTEXT signature method is not recommended to be used in + // production environment. + PLAINTEXT SignatureMethod = "PLAINTEXT" + + // OAuth1TokenContentType is a supported content type for an OAuth1 + // token. + OAuth1TokenContentType = "application/x-www-form-urlencoded" +) + +// AuthOptions represents options for authenticating a user using OAuth1 tokens. +type AuthOptions struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthToken is the OAuth1 Request Token. + OAuthToken string `q:"oauth_token" required:"true"` + + // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate + // an OAuth1 request signature. + OAuthTokenSecret string `required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` + + // AllowReauth allows Gophercloud to re-authenticate automatically + // if/when your token expires. + AllowReauth bool +} + +// ToTokenV3HeadersMap builds the headers required for an OAuth1-based create +// request. +func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret} + + method := headerOpts["method"].(string) + u := headerOpts["url"].(string) + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + + authHeader := buildAuthHeader(q.Query(), signature) + + headers := map[string]string{ + "Authorization": authHeader, + "X-Auth-Token": "", + } + + return headers, nil +} + +// ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder +// interface. +func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { + return nil, nil +} + +// CanReauth allows AuthOptions to satisfy the tokens.AuthOptionsBuilder +// interface. +func (opts AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + +// ToTokenV3CreateMap builds a create request body. +func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { + // identityReq defines the "identity" portion of an OAuth1-based authentication + // create request body. + type identityReq struct { + Methods []string `json:"methods"` + OAuth1 struct{} `json:"oauth1"` + } + + // authReq defines the "auth" portion of an OAuth1-based authentication + // create request body. + type authReq struct { + Identity identityReq `json:"identity"` + } + + // oauth1Request defines how an OAuth1-based authentication create + // request body looks. + type oauth1Request struct { + Auth authReq `json:"auth"` + } + + var req oauth1Request + + req.Auth.Identity.Methods = []string{"oauth1"} + return gophercloud.BuildRequestBody(req, "") +} + +// Create authenticates and either generates a new OpenStack token from an +// OAuth1 token. +func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { + b, err := opts.ToTokenV3CreateMap(nil) + if err != nil { + r.Err = err + return + } + + headerOpts := map[string]interface{}{ + "method": "POST", + "url": authURL(client), + } + + h, err := opts.ToTokenV3HeadersMap(headerOpts) + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateConsumerOptsBuilder allows extensions to add additional parameters to +// the CreateConsumer request. +type CreateConsumerOptsBuilder interface { + ToOAuth1CreateConsumerMap() (map[string]interface{}, error) +} + +// CreateConsumerOpts provides options used to create a new Consumer. +type CreateConsumerOpts struct { + // Description is the consumer description. + Description string `json:"description"` +} + +// ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request. +func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "consumer") +} + +// 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.Post(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) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// List enumerates Consumers. +func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page { + return ConsumersPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetConsumer retrieves details on a single Consumer by ID. +func GetConsumer(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 +} + +// UpdateConsumerOpts provides options used to update a consumer. +type UpdateConsumerOpts struct { + // Description is the consumer description. + Description string `json:"description"` +} + +// ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update +// request. +func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "consumer") +} + +// 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.Patch(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RequestTokenOptsBuilder allows extensions to add additional parameters to the +// RequestToken request. +type RequestTokenOptsBuilder interface { + ToOAuth1RequestTokenHeaders(string, string) (map[string]string, error) +} + +// RequestTokenOpts provides options used to get a consumer unauthorized +// request token. +type RequestTokenOpts struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` + + // RequestedProjectID is a Project ID a consumer user requested an + // access to. + RequestedProjectID string `h:"Requested-Project-Id"` +} + +// ToOAuth1RequestTokenHeaders formats a RequestTokenOpts into a map of request +// headers. +func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "oob") + if err != nil { + return nil, err + } + + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret} + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + authHeader := buildAuthHeader(q.Query(), signature) + + h["Authorization"] = authHeader + + return h, nil +} + +// RequestToken requests an unauthorized OAuth1 Token. +func RequestToken(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{ + MoreHeaders: h, + OkCodes: []int{201}, + KeepResponseBody: true, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + if r.Err != nil { + return + } + defer resp.Body.Close() + if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType { + r.Err = fmt.Errorf("unsupported Content-Type: %q", v) + return + } + r.Body, r.Err = ioutil.ReadAll(resp.Body) + return +} + +// AuthorizeTokenOptsBuilder allows extensions to add additional parameters to +// the AuthorizeToken request. +type AuthorizeTokenOptsBuilder interface { + ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) +} + +// AuthorizeTokenOpts provides options used to authorize a request token. +type AuthorizeTokenOpts struct { + Roles []Role `json:"roles"` +} + +// Role is a struct representing a role object in a AuthorizeTokenOpts struct. +type Role struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +// ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token +// request. +func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) { + for _, r := range opts.Roles { + if r == (Role{}) { + return nil, fmt.Errorf("role must not be empty") + } + } + return gophercloud.BuildRequestBody(opts, "") +} + +// AuthorizeToken authorizes an unauthorized consumer token. +func AuthorizeToken(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{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateAccessTokenOptsBuilder allows extensions to add additional parameters +// to the CreateAccessToken request. +type CreateAccessTokenOptsBuilder interface { + ToOAuth1CreateAccessTokenHeaders(string, string) (map[string]string, error) +} + +// CreateAccessTokenOpts provides options used to create an OAuth1 token. +type CreateAccessTokenOpts struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthToken is the OAuth1 Request Token. + OAuthToken string `q:"oauth_token" required:"true"` + + // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate + // an OAuth1 request signature. + OAuthTokenSecret string `required:"true"` + + // OAuthVerifier is the OAuth1 verification code. + OAuthVerifier string `q:"oauth_verifier" required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` +} + +// ToOAuth1CreateAccessTokenHeaders formats a CreateAccessTokenOpts into a map of +// request headers. +func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u string) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret} + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + authHeader := buildAuthHeader(q.Query(), signature) + + headers := map[string]string{ + "Authorization": authHeader, + } + + return headers, nil +} + +// CreateAccessToken creates a new OAuth1 Access Token +func CreateAccessToken(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{ + MoreHeaders: h, + OkCodes: []int{201}, + KeepResponseBody: true, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + if r.Err != nil { + return + } + defer resp.Body.Close() + if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType { + r.Err = fmt.Errorf("unsupported Content-Type: %q", v) + return + } + r.Body, r.Err = ioutil.ReadAll(resp.Body) + return +} + +// GetAccessToken retrieves details on a single OAuth1 access token by an ID. +func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { + resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil) + _, 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) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListAccessTokens enumerates authorized access tokens. +func ListAccessTokens(client *gophercloud.ServiceClient, userID string) pagination.Pager { + url := userAccessTokensURL(client, userID) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessTokensPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// ListAccessTokenRoles enumerates authorized access token roles. +func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id string) pagination.Pager { + url := userAccessTokenRolesURL(client, userID, id) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessTokenRolesPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetAccessTokenRole retrieves details on a single OAuth1 access token role by +// an ID. +func GetAccessTokenRole(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 +} + +// The following are small helper functions used to help build the signature. + +// buildOAuth1QueryString builds a URLEncoded parameters string specific for +// OAuth1-based requests. +func buildOAuth1QueryString(opts interface{}, timestamp *time.Time, callback string) (*url.URL, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return nil, err + } + + query := q.Query() + + if timestamp != nil { + // use provided timestamp + query.Set("oauth_timestamp", strconv.FormatInt(timestamp.Unix(), 10)) + } else { + // use current timestamp + query.Set("oauth_timestamp", strconv.FormatInt(time.Now().UTC().Unix(), 10)) + } + + if query.Get("oauth_nonce") == "" { + // when nonce is not set, generate a random one + query.Set("oauth_nonce", strconv.FormatInt(rand.Int63(), 10)+query.Get("oauth_timestamp")) + } + + if callback != "" { + query.Set("oauth_callback", callback) + } + query.Set("oauth_version", "1.0") + + return &url.URL{RawQuery: query.Encode()}, nil +} + +// buildStringToSign builds a string to be signed. +func buildStringToSign(method string, u string, query url.Values) []byte { + parsedURL, _ := url.Parse(u) + p := parsedURL.Port() + s := parsedURL.Scheme + + // Default scheme port must be stripped + if s == "http" && p == "80" || s == "https" && p == "443" { + parsedURL.Host = strings.TrimSuffix(parsedURL.Host, ":"+p) + } + + // Ensure that URL doesn't contain queries + parsedURL.RawQuery = "" + + v := strings.Join( + []string{method, url.QueryEscape(parsedURL.String()), url.QueryEscape(query.Encode())}, "&") + + return []byte(v) +} + +// signString signs a string using an OAuth1 signature method. +func signString(signatureMethod SignatureMethod, strToSign []byte, signatureKeys []string) string { + var key []byte + for i, k := range signatureKeys { + key = append(key, []byte(url.QueryEscape(k))...) + if i == 0 { + key = append(key, '&') + } + } + + var signedString string + switch signatureMethod { + case PLAINTEXT: + signedString = string(key) + default: + h := hmac.New(sha1.New, key) + h.Write(strToSign) + signedString = base64.StdEncoding.EncodeToString(h.Sum(nil)) + } + + return signedString +} + +// buildAuthHeader generates an OAuth1 Authorization header with a signature +// calculated using an OAuth1 signature method. +func buildAuthHeader(query url.Values, signature string) string { + var authHeader []string + var keys []string + for k := range query { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + for _, v := range query[k] { + authHeader = append(authHeader, fmt.Sprintf("%s=%q", k, url.QueryEscape(v))) + } + } + + authHeader = append(authHeader, fmt.Sprintf("oauth_signature=%q", signature)) + + return "OAuth " + strings.Join(authHeader, ", ") +} diff --git a/openstack/identity/v3/extensions/oauth1/results.go b/openstack/identity/v3/extensions/oauth1/results.go new file mode 100644 index 0000000000..bb109e98e3 --- /dev/null +++ b/openstack/identity/v3/extensions/oauth1/results.go @@ -0,0 +1,305 @@ +package oauth1 + +import ( + "encoding/json" + "net/url" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Consumer represents a delegated authorization request between two +// identities. +type Consumer struct { + ID string `json:"id"` + Secret string `json:"secret"` + Description string `json:"description"` +} + +type consumerResult struct { + gophercloud.Result +} + +// CreateConsumerResult is the response from a Create operation. Call its +// Extract method to interpret it as a Consumer. +type CreateConsumerResult struct { + consumerResult +} + +// UpdateConsumerResult is the response from a Create operation. Call its +// Extract method to interpret it as a Consumer. +type UpdateConsumerResult struct { + consumerResult +} + +// DeleteConsumerResult is the response from a Delete operation. Call its +// ExtractErr to determine if the request succeeded or failed. +type DeleteConsumerResult struct { + gophercloud.ErrResult +} + +// ConsumersPage is a single page of Region results. +type ConsumersPage struct { + pagination.LinkedPageBase +} + +// GetConsumerResult is the response from a Get operation. Call its Extract +// method to interpret it as a Consumer. +type GetConsumerResult struct { + consumerResult +} + +// IsEmpty determines whether or not a page of Consumers contains any results. +func (c ConsumersPage) IsEmpty() (bool, error) { + consumers, err := ExtractConsumers(c) + return len(consumers) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (c ConsumersPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := c.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractConsumers returns a slice of Consumers contained in a single page of +// results. +func ExtractConsumers(r pagination.Page) ([]Consumer, error) { + var s struct { + Consumers []Consumer `json:"consumers"` + } + err := (r.(ConsumersPage)).ExtractInto(&s) + return s.Consumers, err +} + +// Extract interprets any consumer result as a Consumer. +func (c consumerResult) Extract() (*Consumer, error) { + var s struct { + Consumer *Consumer `json:"consumer"` + } + err := c.ExtractInto(&s) + return s.Consumer, err +} + +// Token contains an OAuth1 token. +type Token struct { + // OAuthToken is the key value for the oauth token that the Identity API returns. + OAuthToken string `q:"oauth_token"` + // OAuthTokenSecret is the secret value associated with the OAuth Token. + OAuthTokenSecret string `q:"oauth_token_secret"` + // OAUthExpiresAt is the date and time when an OAuth token expires. + OAUthExpiresAt *time.Time `q:"-"` +} + +// TokenResult is a struct to handle +// "Content-Type: application/x-www-form-urlencoded" response. +type TokenResult struct { + gophercloud.Result + Body []byte +} + +// Extract interprets any OAuth1 token result as a Token. +func (r TokenResult) Extract() (*Token, error) { + if r.Err != nil { + return nil, r.Err + } + + values, err := url.ParseQuery(string(r.Body)) + if err != nil { + return nil, err + } + + token := &Token{ + OAuthToken: values.Get("oauth_token"), + OAuthTokenSecret: values.Get("oauth_token_secret"), + } + + if v := values.Get("oauth_expires_at"); v != "" { + if t, err := time.Parse(gophercloud.RFC3339Milli, v); err != nil { + return nil, err + } else { + token.OAUthExpiresAt = &t + } + } + + return token, nil +} + +// AuthorizedToken contains an OAuth1 authorized token info. +type AuthorizedToken struct { + // OAuthVerifier is the ID of the token verifier. + OAuthVerifier string `json:"oauth_verifier"` +} + +type AuthorizeTokenResult struct { + gophercloud.Result +} + +// Extract interprets AuthorizeTokenResult result as a AuthorizedToken. +func (r AuthorizeTokenResult) Extract() (*AuthorizedToken, error) { + var s struct { + AuthorizedToken *AuthorizedToken `json:"token"` + } + err := r.ExtractInto(&s) + return s.AuthorizedToken, err +} + +// AccessToken represents an AccessToken response as a struct. +type AccessToken struct { + ID string `json:"id"` + ConsumerID string `json:"consumer_id"` + ProjectID string `json:"project_id"` + AuthorizingUserID string `json:"authorizing_user_id"` + ExpiresAt *time.Time `json:"-"` +} + +func (r *AccessToken) UnmarshalJSON(b []byte) error { + type tmp AccessToken + var s struct { + tmp + ExpiresAt *gophercloud.JSONRFC3339Milli `json:"expires_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = AccessToken(s.tmp) + + if s.ExpiresAt != nil { + t := time.Time(*s.ExpiresAt) + r.ExpiresAt = &t + } + + return nil +} + +type GetAccessTokenResult struct { + gophercloud.Result +} + +// Extract interprets any GetAccessTokenResult result as an AccessToken. +func (r GetAccessTokenResult) Extract() (*AccessToken, error) { + var s struct { + AccessToken *AccessToken `json:"access_token"` + } + err := r.ExtractInto(&s) + return s.AccessToken, err +} + +// RevokeAccessTokenResult is the response from a Delete operation. Call its +// ExtractErr to determine if the request succeeded or failed. +type RevokeAccessTokenResult struct { + gophercloud.ErrResult +} + +// AccessTokensPage is a single page of Access Tokens results. +type AccessTokensPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an AccessTokensPage contains any results. +func (r AccessTokensPage) IsEmpty() (bool, error) { + accessTokens, err := ExtractAccessTokens(r) + return len(accessTokens) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r AccessTokensPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractAccessTokens returns a slice of AccessTokens contained in a single +// page of results. +func ExtractAccessTokens(r pagination.Page) ([]AccessToken, error) { + var s struct { + AccessTokens []AccessToken `json:"access_tokens"` + } + err := (r.(AccessTokensPage)).ExtractInto(&s) + return s.AccessTokens, err +} + +// AccessTokenRole represents an Access Token Role struct. +type AccessTokenRole struct { + ID string `json:"id"` + Name string `json:"name"` + DomainID string `json:"domain_id"` +} + +// AccessTokenRolesPage is a single page of Access Token roles results. +type AccessTokenRolesPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an AccessTokensPage contains any results. +func (r AccessTokenRolesPage) IsEmpty() (bool, error) { + accessTokenRoles, err := ExtractAccessTokenRoles(r) + return len(accessTokenRoles) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r AccessTokenRolesPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractAccessTokenRoles returns a slice of AccessTokenRole contained in a +// single page of results. +func ExtractAccessTokenRoles(r pagination.Page) ([]AccessTokenRole, error) { + var s struct { + AccessTokenRoles []AccessTokenRole `json:"roles"` + } + err := (r.(AccessTokenRolesPage)).ExtractInto(&s) + return s.AccessTokenRoles, err +} + +type GetAccessTokenRoleResult struct { + gophercloud.Result +} + +// Extract interprets any GetAccessTokenRoleResult result as an AccessTokenRole. +func (r GetAccessTokenRoleResult) Extract() (*AccessTokenRole, error) { + var s struct { + AccessTokenRole *AccessTokenRole `json:"role"` + } + err := r.ExtractInto(&s) + return s.AccessTokenRole, err +} + +// OAuth1 is an OAuth1 object, returned in OAuth1 token result. +type OAuth1 struct { + AccessTokenID string `json:"access_token_id"` + ConsumerID string `json:"consumer_id"` +} + +// TokenExt represents an extension of the base token result. +type TokenExt struct { + OAuth1 OAuth1 `json:"OS-OAUTH1"` +} diff --git a/openstack/identity/v3/extensions/oauth1/testing/fixtures.go b/openstack/identity/v3/extensions/oauth1/testing/fixtures.go new file mode 100644 index 0000000000..c710ff867b --- /dev/null +++ b/openstack/identity/v3/extensions/oauth1/testing/fixtures.go @@ -0,0 +1,454 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" + tokens "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/testing" + "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const CreateConsumerRequest = ` +{ + "consumer": { + "description": "My consumer" + } +} +` + +const CreateConsumerResponse = ` +{ + "consumer": { + "secret": "secretsecret", + "description": "My consumer", + "id": "7fea2d", + "links": { + "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/7fea2d" + } + } +} +` + +const UpdateConsumerRequest = ` +{ + "consumer": { + "description": "My new consumer" + } +} +` + +const UpdateConsumerResponse = ` +{ + "consumer": { + "description": "My new consumer", + "id": "7fea2d", + "links": { + "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/7fea2d" + } + } +} +` + +// GetConsumerOutput provides a Get result. +const GetConsumerResponse = ` +{ + "consumer": { + "id": "7fea2d", + "description": "My consumer", + "links": { + "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/7fea2d" + } + } +} +` + +// ListConsumersResponse provides a single page of Consumers results. +const ListConsumersResponse = ` +{ + "consumers": [ + { + "description": "My consumer", + "id": "7fea2d", + "links": { + "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/7fea2d" + } + }, + { + "id": "0c2a74", + "links": { + "self": "http://example.com/identity/v3/OS-OAUTH1/consumers/0c2a74" + } + } + ], + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/OS-OAUTH1/consumers" + } +} +` + +const AuthorizeTokenRequest = ` +{ + "roles": [ + { + "id": "a3b29b" + }, + { + "id": "49993e" + } + ] +} +` + +const AuthorizeTokenResponse = ` +{ + "token": { + "oauth_verifier": "8171" + } +} +` + +const GetUserAccessTokenResponse = ` +{ + "access_token": { + "consumer_id": "7fea2d", + "id": "6be26a", + "expires_at": "2013-09-11T06:07:51.501805Z", + "links": { + "roles": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles", + "self": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a" + }, + "project_id": "b9fca3", + "authorizing_user_id": "ce9e07" + } +} +` + +const ListUserAccessTokensResponse = ` +{ + "access_tokens": [ + { + "consumer_id": "7fea2d", + "id": "6be26a", + "expires_at": "2013-09-11T06:07:51.501805Z", + "links": { + "roles": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles", + "self": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a" + }, + "project_id": "b9fca3", + "authorizing_user_id": "ce9e07" + } + ], + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens" + } +} +` + +const ListUserAccessTokenRolesResponse = ` +{ + "roles": [ + { + "id": "5ad150", + "domain_id": "7cf37b", + "links": { + "self": "http://example.com/identity/v3/roles/5ad150" + }, + "name": "admin" + }, + { + "id": "a62eb6", + "domain_id": "7cf37b", + "links": { + "self": "http://example.com/identity/v3/roles/a62eb6" + }, + "name": "member" + } + ], + "links": { + "next": null, + "previous": null, + "self": "http://example.com/identity/v3/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles" + } +} +` + +const ListUserAccessTokenRoleResponse = ` +{ + "role": { + "id": "5ad150", + "domain_id": "7cf37b", + "links": { + "self": "http://example.com/identity/v3/roles/5ad150" + }, + "name": "admin" + } +} +` + +var tokenExpiresAt = time.Date(2013, time.September, 11, 06, 07, 51, 501805000, time.UTC) +var UserAccessToken = oauth1.AccessToken{ + ID: "6be26a", + ConsumerID: "7fea2d", + ProjectID: "b9fca3", + AuthorizingUserID: "ce9e07", + ExpiresAt: &tokenExpiresAt, +} + +var UserAccessTokenRole = oauth1.AccessTokenRole{ + ID: "5ad150", + DomainID: "7cf37b", + Name: "admin", +} + +var UserAccessTokenRoleSecond = oauth1.AccessTokenRole{ + ID: "a62eb6", + DomainID: "7cf37b", + Name: "member", +} + +var ExpectedUserAccessTokensSlice = []oauth1.AccessToken{UserAccessToken} + +var ExpectedUserAccessTokenRolesSlice = []oauth1.AccessTokenRole{UserAccessTokenRole, UserAccessTokenRoleSecond} + +// HandleCreateConsumer creates an HTTP handler at `/OS-OAUTH1/consumers` on the +// test handler mux that tests consumer creation. +func HandleCreateConsumer(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "POST") + testhelper.TestHeader(t, r, "Content-Type", "application/json") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestJSONRequest(t, r, CreateConsumerRequest) + + w.WriteHeader(http.StatusCreated) + _, err := fmt.Fprintf(w, CreateConsumerResponse) + testhelper.AssertNoErr(t, err) + }) +} + +// HandleUpdateConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the +// test handler mux that tests consumer update. +func HandleUpdateConsumer(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "PATCH") + testhelper.TestHeader(t, r, "Content-Type", "application/json") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestJSONRequest(t, r, UpdateConsumerRequest) + + w.WriteHeader(http.StatusOK) + _, err := fmt.Fprintf(w, UpdateConsumerResponse) + testhelper.AssertNoErr(t, err) + }) +} + +// HandleDeleteConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the +// test handler mux that tests consumer deletion. +func HandleDeleteConsumer(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "DELETE") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleGetConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the +// test handler mux that responds with a single consumer. +func HandleGetConsumer(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetConsumerResponse) + }) +} + +var Consumer = oauth1.Consumer{ + ID: "7fea2d", + Description: "My consumer", + Secret: "secretsecret", +} + +var UpdatedConsumer = oauth1.Consumer{ + ID: "7fea2d", + Description: "My new consumer", +} + +var FirstConsumer = oauth1.Consumer{ + ID: "7fea2d", + Description: "My consumer", +} + +var SecondConsumer = oauth1.Consumer{ + ID: "0c2a74", +} + +// ExpectedConsumersSlice is the slice of consumers expected to be returned from ListOutput. +var ExpectedConsumersSlice = []oauth1.Consumer{FirstConsumer, SecondConsumer} + +// HandleListConsumers creates an HTTP handler at `/OS-OAUTH1/consumers` on the +// test handler mux that responds with a list of two consumers. +func HandleListConsumers(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListConsumersResponse) + }) +} + +var Token = oauth1.Token{ + OAuthToken: "29971f", + OAuthTokenSecret: "238eb8", + OAUthExpiresAt: &tokenExpiresAt, +} + +// HandleRequestToken creates an HTTP handler at `/OS-OAUTH1/request_token` on the +// test handler mux that responds with a OAuth1 unauthorized token. +func HandleRequestToken(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-OAUTH1/request_token", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "POST") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + testhelper.TestHeader(t, r, "Authorization", `OAuth oauth_callback="oob", oauth_consumer_key="7fea2d", oauth_nonce="71416001758914252991586795052", oauth_signature_method="HMAC-SHA1", oauth_timestamp="0", oauth_version="1.0", oauth_signature="jCSPVryCYF52Ks0VNNmBmeKSGuw%3D"`) + testhelper.TestHeader(t, r, "Requested-Project-Id", "1df927e8a466498f98788ed73d3c8ab4") + testhelper.TestBody(t, r, "") + + w.Header().Set("Content-Type", oauth1.OAuth1TokenContentType) + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, `oauth_token=29971f&oauth_token_secret=238eb8&oauth_expires_at=2013-09-11T06:07:51.501805Z`) + }) +} + +// HandleAuthorizeToken creates an HTTP handler at `/OS-OAUTH1/authorize/29971f` on the +// test handler mux that tests unauthorized token authorization. +func HandleAuthorizeToken(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-OAUTH1/authorize/29971f", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "PUT") + testhelper.TestHeader(t, r, "Content-Type", "application/json") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestJSONRequest(t, r, AuthorizeTokenRequest) + + w.WriteHeader(http.StatusOK) + _, err := fmt.Fprintf(w, AuthorizeTokenResponse) + testhelper.AssertNoErr(t, err) + }) +} + +var AccessToken = oauth1.Token{ + OAuthToken: "accd36", + OAuthTokenSecret: "aa47da", + OAUthExpiresAt: &tokenExpiresAt, +} + +// HandleCreateAccessToken creates an HTTP handler at `/OS-OAUTH1/access_token` on the +// test handler mux that responds with a OAuth1 access token. +func HandleCreateAccessToken(t *testing.T) { + testhelper.Mux.HandleFunc("/OS-OAUTH1/access_token", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "POST") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + testhelper.TestHeader(t, r, "Authorization", `OAuth oauth_consumer_key="7fea2d", oauth_nonce="66148873158553341551586804894", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1586804894", oauth_token="29971f", oauth_verifier="8171", oauth_version="1.0", oauth_signature="usQ89Y3IYG0IBE7%2Ft8aVsc8XgEk%3D"`) + testhelper.TestBody(t, r, "") + + w.Header().Set("Content-Type", oauth1.OAuth1TokenContentType) + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, `oauth_token=accd36&oauth_token_secret=aa47da&oauth_expires_at=2013-09-11T06:07:51.501805Z`) + }) +} + +// HandleGetAccessToken creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a` on the +// test handler mux that responds with a single access token. +func HandleGetAccessToken(t *testing.T) { + testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetUserAccessTokenResponse) + }) +} + +// HandleRevokeAccessToken creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a` on the +// test handler mux that tests access token deletion. +func HandleRevokeAccessToken(t *testing.T) { + testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "DELETE") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleListAccessTokens creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens` on the +// test handler mux that responds with a slice of access tokens. +func HandleListAccessTokens(t *testing.T) { + testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListUserAccessTokensResponse) + }) +} + +// HandleListAccessTokenRoles creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles` on the +// test handler mux that responds with a slice of access token roles. +func HandleListAccessTokenRoles(t *testing.T) { + testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListUserAccessTokenRolesResponse) + }) +} + +// HandleGetAccessTokenRole creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles/5ad150` on the +// test handler mux that responds with an access token role. +func HandleGetAccessTokenRole(t *testing.T) { + testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles/5ad150", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListUserAccessTokenRoleResponse) + }) +} + +// HandleAuthenticate creates an HTTP handler at `/auth/tokens` on the +// test handler mux that responds with an OpenStack token. +func HandleAuthenticate(t *testing.T) { + testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "POST") + testhelper.TestHeader(t, r, "Content-Type", "application/json") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestHeader(t, r, "Authorization", `OAuth oauth_consumer_key="7fea2d", oauth_nonce="66148873158553341551586804894", oauth_signature_method="HMAC-SHA1", oauth_timestamp="0", oauth_token="accd36", oauth_version="1.0", oauth_signature="JgMHu4e7rXGlqz3A%2FLhHDMvtjp8%3D"`) + testhelper.TestJSONRequest(t, r, `{"auth": {"identity": {"oauth1": {}, "methods": ["oauth1"]}}}`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, tokens.TokenOutput) + }) +} diff --git a/openstack/identity/v3/extensions/oauth1/testing/requests_test.go b/openstack/identity/v3/extensions/oauth1/testing/requests_test.go new file mode 100644 index 0000000000..108318bd26 --- /dev/null +++ b/openstack/identity/v3/extensions/oauth1/testing/requests_test.go @@ -0,0 +1,270 @@ +package testing + +import ( + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreateConsumer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateConsumer(t) + + consumer, err := oauth1.CreateConsumer(client.ServiceClient(), oauth1.CreateConsumerOpts{ + Description: "My consumer", + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, Consumer, *consumer) +} + +func TestUpdateConsumer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateConsumer(t) + + consumer, err := oauth1.UpdateConsumer(client.ServiceClient(), "7fea2d", oauth1.UpdateConsumerOpts{ + Description: "My new consumer", + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, UpdatedConsumer, *consumer) +} + +func TestDeleteConsumer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteConsumer(t) + + err := oauth1.DeleteConsumer(client.ServiceClient(), "7fea2d").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestGetConsumer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetConsumer(t) + + consumer, err := oauth1.GetConsumer(client.ServiceClient(), "7fea2d").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, FirstConsumer, *consumer) +} + +func TestListConsumers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListConsumers(t) + + count := 0 + err := oauth1.ListConsumers(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := oauth1.ExtractConsumers(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedConsumersSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListConsumersAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListConsumers(t) + + allPages, err := oauth1.ListConsumers(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + actual, err := oauth1.ExtractConsumers(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedConsumersSlice, actual) +} + +func TestRequestToken(t *testing.T) { + th.SetupPersistentPortHTTP(t, 33199) + defer th.TeardownHTTP() + HandleRequestToken(t) + + ts := time.Unix(0, 0) + token, err := oauth1.RequestToken(client.ServiceClient(), oauth1.RequestTokenOpts{ + OAuthConsumerKey: Consumer.ID, + OAuthConsumerSecret: Consumer.Secret, + OAuthSignatureMethod: oauth1.HMACSHA1, + OAuthTimestamp: &ts, + OAuthNonce: "71416001758914252991586795052", + RequestedProjectID: "1df927e8a466498f98788ed73d3c8ab4", + }).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, Token, *token) +} + +func TestAuthorizeToken(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAuthorizeToken(t) + + token, err := oauth1.AuthorizeToken(client.ServiceClient(), "29971f", oauth1.AuthorizeTokenOpts{ + Roles: []oauth1.Role{ + { + ID: "a3b29b", + }, + { + ID: "49993e", + }, + }, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "8171", token.OAuthVerifier) +} + +func TestCreateAccessToken(t *testing.T) { + th.SetupPersistentPortHTTP(t, 33199) + defer th.TeardownHTTP() + HandleCreateAccessToken(t) + + ts := time.Unix(1586804894, 0) + token, err := oauth1.CreateAccessToken(client.ServiceClient(), oauth1.CreateAccessTokenOpts{ + OAuthConsumerKey: Consumer.ID, + OAuthConsumerSecret: Consumer.Secret, + OAuthToken: Token.OAuthToken, + OAuthTokenSecret: Token.OAuthTokenSecret, + OAuthVerifier: "8171", + OAuthSignatureMethod: oauth1.HMACSHA1, + OAuthTimestamp: &ts, + OAuthNonce: "66148873158553341551586804894", + }).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, AccessToken, *token) +} + +func TestGetAccessToken(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetAccessToken(t) + + token, err := oauth1.GetAccessToken(client.ServiceClient(), "ce9e07", "6be26a").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, UserAccessToken, *token) +} + +func TestRevokeAccessToken(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleRevokeAccessToken(t) + + err := oauth1.RevokeAccessToken(client.ServiceClient(), "ce9e07", "6be26a").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListAccessTokens(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAccessTokens(t) + + count := 0 + err := oauth1.ListAccessTokens(client.ServiceClient(), "ce9e07").EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := oauth1.ExtractAccessTokens(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedUserAccessTokensSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListAccessTokensAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAccessTokens(t) + + allPages, err := oauth1.ListAccessTokens(client.ServiceClient(), "ce9e07").AllPages() + th.AssertNoErr(t, err) + actual, err := oauth1.ExtractAccessTokens(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedUserAccessTokensSlice, actual) +} + +func TestListAccessTokenRoles(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAccessTokenRoles(t) + + count := 0 + err := oauth1.ListAccessTokenRoles(client.ServiceClient(), "ce9e07", "6be26a").EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := oauth1.ExtractAccessTokenRoles(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedUserAccessTokenRolesSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestListAccessTokenRolesAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAccessTokenRoles(t) + + allPages, err := oauth1.ListAccessTokenRoles(client.ServiceClient(), "ce9e07", "6be26a").AllPages() + th.AssertNoErr(t, err) + actual, err := oauth1.ExtractAccessTokenRoles(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedUserAccessTokenRolesSlice, actual) +} + +func TestGetAccessTokenRole(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetAccessTokenRole(t) + + role, err := oauth1.GetAccessTokenRole(client.ServiceClient(), "ce9e07", "6be26a", "5ad150").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, UserAccessTokenRole, *role) +} + +func TestAuthenticate(t *testing.T) { + th.SetupPersistentPortHTTP(t, 33199) + defer th.TeardownHTTP() + HandleAuthenticate(t) + + expected := &tokens.Token{ + ExpiresAt: time.Date(2017, 6, 3, 2, 19, 49, 0, time.UTC), + } + + ts := time.Unix(0, 0) + options := &oauth1.AuthOptions{ + OAuthConsumerKey: Consumer.ID, + OAuthConsumerSecret: Consumer.Secret, + OAuthToken: AccessToken.OAuthToken, + OAuthTokenSecret: AccessToken.OAuthTokenSecret, + OAuthSignatureMethod: oauth1.HMACSHA1, + OAuthTimestamp: &ts, + OAuthNonce: "66148873158553341551586804894", + } + + actual, err := oauth1.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} diff --git a/openstack/identity/v3/extensions/oauth1/urls.go b/openstack/identity/v3/extensions/oauth1/urls.go new file mode 100644 index 0000000000..9b51d53b31 --- /dev/null +++ b/openstack/identity/v3/extensions/oauth1/urls.go @@ -0,0 +1,43 @@ +package oauth1 + +import "github.com/gophercloud/gophercloud" + +func consumersURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "consumers") +} + +func consumerURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("OS-OAUTH1", "consumers", id) +} + +func requestTokenURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "request_token") +} + +func authorizeTokenURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("OS-OAUTH1", "authorize", id) +} + +func createAccessTokenURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "access_token") +} + +func userAccessTokensURL(c *gophercloud.ServiceClient, userID string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens") +} + +func userAccessTokenURL(c *gophercloud.ServiceClient, userID string, id string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id) +} + +func userAccessTokenRolesURL(c *gophercloud.ServiceClient, userID string, id string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles") +} + +func userAccessTokenRoleURL(c *gophercloud.ServiceClient, userID string, id string, roleID string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles", roleID) +} + +func authURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("auth", "tokens") +} diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index c345cac684..d8c455d160 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -17,6 +17,7 @@ type AuthOptionsBuilder interface { // ToTokenV3CreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) + ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) ToTokenV3ScopeMap() (map[string]interface{}, error) CanReauth() bool } @@ -84,7 +85,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s return gophercloudAuthOpts.ToTokenV3CreateMap(scope) } -// ToTokenV3CreateMap builds a scope request body from AuthOptions. +// ToTokenV3ScopeMap builds a scope request body from AuthOptions. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { scope := gophercloud.AuthScope(opts.Scope) @@ -106,6 +107,12 @@ func (opts *AuthOptions) CanReauth() bool { return opts.AllowReauth } +// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package. +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { + return nil, nil +} + func subjectTokenHeaders(subjectToken string) map[string]string { return map[string]string{ "X-Subject-Token": subjectToken, diff --git a/testhelper/http_responses.go b/testhelper/http_responses.go index e1f1f9ac0e..7482134d9f 100644 --- a/testhelper/http_responses.go +++ b/testhelper/http_responses.go @@ -2,7 +2,9 @@ package testhelper import ( "encoding/json" + "fmt" "io/ioutil" + "net" "net/http" "net/http/httptest" "net/url" @@ -18,6 +20,18 @@ var ( Server *httptest.Server ) +// SetupHTTP prepares the Mux and Server listening specific port. +func SetupPersistentPortHTTP(t *testing.T, port int) { + l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) + if err != nil { + t.Errorf("Failed to listen to 127.0.0.1:%d: %s", port, err) + } + Mux = http.NewServeMux() + Server = httptest.NewUnstartedServer(Mux) + Server.Listener = l + Server.Start() +} + // SetupHTTP prepares the Mux and Server. func SetupHTTP() { Mux = http.NewServeMux() From c3bfe50899e53a0f01ddbe5e2243999414b41649 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 23 Apr 2020 19:42:53 -0600 Subject: [PATCH 1077/2296] Update CHANGELOG.md --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef7b6627a3..e4f82532c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,21 @@ IMPROVEMENTS * Added `identity/v3/extensions/trusts.ListRoles` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) * Added `identity/v3/extensions/trusts.GetRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) * Added `identity/v3/extensions/trusts.CheckRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Added `identity/v3/extensions/oauth1.Create` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.CreateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.DeleteConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListConsumers` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.UpdateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.RequestToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.AuthorizeToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.CreateAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.RevokeAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListAccessTokens` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListAccessTokenRoles` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetAccessTokenRole` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) + BUG FIXES From f307d4993d25713b3a20a1f35011a6be300f83d7 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 26 Apr 2020 03:50:47 +0200 Subject: [PATCH 1078/2296] Networking V2: Update/Delete/ScheduleDHCP/RemoveDHCP on network agents (#1954) --- .../v2/extensions/agents/agents_test.go | 73 ++++++++++++++- .../networking/v2/extensions/agents/doc.go | 93 +++++++++++++++---- .../v2/extensions/agents/requests.go | 78 +++++++++++++++- .../v2/extensions/agents/results.go | 30 ++++++ .../v2/extensions/agents/testing/fixtures.go | 70 ++++++++++++++ .../agents/testing/requests_test.go | 85 +++++++++++++++++ .../networking/v2/extensions/agents/urls.go | 16 ++++ 7 files changed, 422 insertions(+), 23 deletions(-) diff --git a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index 972b784513..6436b27b4e 100644 --- a/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -6,12 +6,15 @@ 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" th "github.com/gophercloud/gophercloud/testhelper" ) -func TestAgentsRead(t *testing.T) { +func TestAgentsRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -21,8 +24,72 @@ func TestAgentsRead(t *testing.T) { allAgents, err := agents.ExtractAgents(allPages) th.AssertNoErr(t, err) + t.Logf("Retrieved Networking V2 agents") + tools.PrintResource(t, allAgents) + + // List DHCP agents + listOpts := &agents.ListOpts{ + AgentType: "DHCP agent", + } + allPages, err = agents.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allAgents, err = agents.ExtractAgents(allPages) + th.AssertNoErr(t, err) + + t.Logf("Retrieved Networking V2 DHCP agents") + tools.PrintResource(t, allAgents) + + // List DHCP agent networks for _, agent := range allAgents { - t.Logf("Retrieved Networking V2 agent: %s", agent.ID) - tools.PrintResource(t, agent) + t.Logf("Retrieving DHCP networks from the agent: %s", agent.ID) + networks, err := agents.ListDHCPNetworks(client, agent.ID).Extract() + th.AssertNoErr(t, err) + for _, network := range networks { + t.Logf("Retrieved %q network, assigned to a %q DHCP agent", network.ID, agent.ID) + } + } + + // Get a single agent + agent, err := agents.Get(client, allAgents[0].ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, agent) + + // Update an agent + description := "updated agent" + updateOpts := &agents.UpdateOpts{ + Description: &description, + } + agent, err = agents.Update(client, allAgents[0].ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, agent.Description, description) + + // Restore original description + agent, err = agents.Update(client, allAgents[0].ID, &agents.UpdateOpts{Description: &allAgents[0].Description}).Extract() + 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) + defer networking.DeleteNetwork(t, client, network.ID) + + opts := &agents.ScheduleDHCPNetworkOpts{ + NetworkID: network.ID, } + err = agents.ScheduleDHCPNetwork(client, allAgents[0].ID, opts).ExtractErr() + th.AssertNoErr(t, err) + + err = agents.RemoveDHCPNetwork(client, allAgents[0].ID, network.ID).ExtractErr() + th.AssertNoErr(t, err) + + // skip this part + t.Skip("Skip DHCP agent deletion") + + // Delete a DHCP agent + err = agents.Delete(client, allAgents[0].ID).ExtractErr() + th.AssertNoErr(t, err) } diff --git a/openstack/networking/v2/extensions/agents/doc.go b/openstack/networking/v2/extensions/agents/doc.go index 7706b730e5..1a6f663a3e 100644 --- a/openstack/networking/v2/extensions/agents/doc.go +++ b/openstack/networking/v2/extensions/agents/doc.go @@ -3,30 +3,85 @@ Package agents provides the ability to retrieve and manage Agents through the Ne Example of Listing Agents - listOpts := agents.ListOpts{ - AgentType: "Open vSwitch agent", - } + listOpts := agents.ListOpts{ + AgentType: "Open vSwitch agent", + } - allPages, err := agents.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } + allPages, err := agents.List(networkClient, listOpts).AllPages() + if err != nil { + panic(err) + } - allAgents, err := agents.ExtractAgents(allPages) - if err != nil { - panic(err) - } + allAgents, err := agents.ExtractAgents(allPages) + if err != nil { + panic(err) + } - for _, agent := range allAgents { - fmt.Printf("%+v\n", agent) - } + for _, agent := range allAgents { + fmt.Printf("%+v\n", agent) + } Example to Get an Agent - agentID = "76af7b1f-d61b-4526-94f7-d2e14e2698df" - agent, err := agents.Get(networkClient, agentID).Extract() - if err != nil { - panic(err) - } + agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" + agent, err := agents.Get(networkClient, agentID).Extract() + if err != nil { + panic(err) + } + +Example to Update an Agent + + adminStateUp := true + description := "agent description" + updateOpts := &agents.UpdateOpts{ + Description: &description, + AdminStateUp: &adminStateUp, + } + agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" + agent, err := agents.Update(networkClient, agentID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete an Agent + + agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" + err := agents.Delete(networkClient, agentID).ExtractErr() + if err != nil { + panic(err) + } + +Example to List Networks hosted by a DHCP Agent + + agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" + networks, err := agents.ListDHCPNetworks(networkClient, agentID).Extract() + if err != nil { + panic(err) + } + + for _, network := range networks { + fmt.Printf("%+v\n", network) + } + +Example to Schedule a network to a DHCP Agent + + agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" + opts := &agents.ScheduleDHCPNetworkOpts{ + NetworkID: "1ae075ca-708b-4e66-b4a7-b7698632f05f", + } + err := agents.ScheduleDHCPNetwork(networkClient, agentID, opts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Remove a network from a DHCP Agent + + agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" + networkID := "1ae075ca-708b-4e66-b4a7-b7698632f05f" + err := agents.RemoveDHCPNetwork(networkClient, agentID, networkID).ExtractErr() + if err != nil { + panic(err) + } + */ package agents diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 730b5e6f66..7c81864145 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -66,10 +66,86 @@ func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { return } +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToAgentUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents the attributes used when updating an existing agent. +type UpdateOpts struct { + Description *string `json:"description,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` +} + +// ToAgentUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToAgentUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "agent") +} + +// Update updates a specific agent based on its ID. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToAgentUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete deletes a specific agent based on its ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(getURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + // ListDHCPNetworks returns a list of networks scheduled to a specific -// dhcp agent +// dhcp agent. func ListDHCPNetworks(c *gophercloud.ServiceClient, id string) (r ListDHCPNetworksResult) { resp, err := c.Get(listDHCPNetworksURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ScheduleDHCPNetworkOptsBuilder allows extensions to add additional parameters +// to the ScheduleDHCPNetwork request. +type ScheduleDHCPNetworkOptsBuilder interface { + ToAgentScheduleDHCPNetworkMap() (map[string]interface{}, error) +} + +// ScheduleDHCPNetworkOpts represents the attributes used when scheduling a +// network to a DHCP agent. +type ScheduleDHCPNetworkOpts struct { + NetworkID string `json:"network_id" required:"true"` +} + +// ToAgentScheduleDHCPNetworkMap builds a request body from ScheduleDHCPNetworkOpts. +func (opts ScheduleDHCPNetworkOpts) ToAgentScheduleDHCPNetworkMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// ScheduleDHCPNetwork schedule a network to a DHCP agent. +func ScheduleDHCPNetwork(c *gophercloud.ServiceClient, id string, opts ScheduleDHCPNetworkOptsBuilder) (r ScheduleDHCPNetworkResult) { + b, err := opts.ToAgentScheduleDHCPNetworkMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(scheduleDHCPNetworkURL(c, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RemoveDHCPNetwork removes a network from a DHCP agent. +func RemoveDHCPNetwork(c *gophercloud.ServiceClient, id string, networkID string) (r RemoveDHCPNetworkResult) { + resp, err := c.Delete(removeDHCPNetworkURL(c, id, networkID), 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 2b3f0194ba..ba9d4b3421 100644 --- a/openstack/networking/v2/extensions/agents/results.go +++ b/openstack/networking/v2/extensions/agents/results.go @@ -28,6 +28,32 @@ type GetResult struct { commonResult } +// UpdateResult represents the result of a get operation. Call its Extract +// method to interpret it as an Agent. +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 +} + +// ScheduleDHCPNetworkResult represents the result of a schedule a network to +// a DHCP agent operation. ExtractErr method to determine if the request +// succeeded or failed. +type ScheduleDHCPNetworkResult struct { + gophercloud.ErrResult +} + +// RemoveDHCPNetworkResult represents the result of a remove a network from a +// DHCP agent operation. ExtractErr method to determine if the request succeeded +// or failed. +type RemoveDHCPNetworkResult struct { + gophercloud.ErrResult +} + // Agent represents a Neutron agent. type Agent struct { // ID is the id of the agent. @@ -42,6 +68,10 @@ type Agent struct { // Alive indicates whether agent is alive or not. Alive bool `json:"alive"` + // ResourcesSynced indicates whether agent is synced or not. + // Not all agent types track resources via Placement. + ResourcesSynced bool `json:"resources_synced"` + // AvailabilityZone is a zone of the agent. AvailabilityZone string `json:"availability_zone"` diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures.go b/openstack/networking/v2/extensions/agents/testing/fixtures.go index bfb47841c1..ea25d00776 100644 --- a/openstack/networking/v2/extensions/agents/testing/fixtures.go +++ b/openstack/networking/v2/extensions/agents/testing/fixtures.go @@ -54,6 +54,40 @@ const AgentsListResult = ` } ` +// AgentUpdateRequest represents raw request to update an Agent. +const AgentUpdateRequest = ` +{ + "agent": { + "description": "My OVS agent for OpenStack", + "admin_state_up": true + } +} +` + +// Agent represents a sample Agent struct. +var Agent = agents.Agent{ + ID: "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", + AdminStateUp: true, + AgentType: "Open vSwitch agent", + Description: "My OVS agent for OpenStack", + Alive: true, + ResourcesSynced: true, + Binary: "neutron-openvswitch-agent", + Configurations: map[string]interface{}{ + "ovs_hybrid_plug": false, + "datapath_type": "system", + "vhostuser_socket_dir": "/var/run/openvswitch", + "log_agent_heartbeats": false, + "l2_population": true, + "enable_distributed_routing": false, + }, + CreatedAt: time.Date(2017, 7, 26, 23, 2, 5, 0, time.UTC), + StartedAt: time.Date(2018, 6, 26, 21, 46, 20, 0, time.UTC), + HeartbeatTimestamp: time.Date(2019, 1, 9, 11, 43, 01, 0, time.UTC), + Host: "compute3", + Topic: "N/A", +} + // Agent1 represents first unmarshalled address scope from the // AgentsListResult. var Agent1 = agents.Agent{ @@ -124,6 +158,35 @@ const AgentsGetResult = ` } ` +// AgentsUpdateResult represents raw response for the Update request. +const AgentsUpdateResult = ` +{ + "agent": { + "binary": "neutron-openvswitch-agent", + "description": "My OVS agent for OpenStack", + "availability_zone": null, + "heartbeat_timestamp": "2019-01-09 11:43:01", + "admin_state_up": true, + "alive": true, + "id": "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", + "topic": "N/A", + "host": "compute3", + "agent_type": "Open vSwitch agent", + "started_at": "2018-06-26 21:46:20", + "created_at": "2017-07-26 23:02:05", + "resources_synced": true, + "configurations": { + "ovs_hybrid_plug": false, + "datapath_type": "system", + "vhostuser_socket_dir": "/var/run/openvswitch", + "log_agent_heartbeats": false, + "l2_population": true, + "enable_distributed_routing": false + } + } +} +` + // AgentDHCPNetworksListResult represents raw response for the ListDHCPNetworks request. const AgentDHCPNetworksListResult = ` { @@ -161,3 +224,10 @@ const AgentDHCPNetworksListResult = ` ] } ` + +// ScheduleDHCPNetworkRequest represents raw request for the ScheduleDHCPNetwork request. +const ScheduleDHCPNetworkRequest = ` +{ + "network_id": "1ae075ca-708b-4e66-b4a7-b7698632f05f" +} +` diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index afbf2c1c87..b4c848ceb9 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -89,6 +89,52 @@ func TestGet(t *testing.T) { }) } +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a", 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, AgentUpdateRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, AgentsUpdateResult) + }) + + iTrue := true + description := "My OVS agent for OpenStack" + updateOpts := &agents.UpdateOpts{ + Description: &description, + AdminStateUp: &iTrue, + } + s, err := agents.Update(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", updateOpts).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, *s, Agent) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a", 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.Delete(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").ExtractErr() + th.AssertNoErr(t, err) +} + func TestListDHCPNetworks(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -120,3 +166,42 @@ func TestListDHCPNetworks(t *testing.T) { th.AssertDeepEquals(t, s[0].Subnets, []string{"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"}) } + +func TestScheduleDHCPNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/dhcp-networks", 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, ScheduleDHCPNetworkRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + }) + + opts := &agents.ScheduleDHCPNetworkOpts{ + NetworkID: "1ae075ca-708b-4e66-b4a7-b7698632f05f", + } + err := agents.ScheduleDHCPNetwork(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", opts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestRemoveDHCPNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/agents/43583cf5-472e-4dc8-af5b-6aed4c94ee3a/dhcp-networks/1ae075ca-708b-4e66-b4a7-b7698632f05f", 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.RemoveDHCPNetwork(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "1ae075ca-708b-4e66-b4a7-b7698632f05f").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/agents/urls.go b/openstack/networking/v2/extensions/agents/urls.go index 1386a848ef..d8582013d6 100644 --- a/openstack/networking/v2/extensions/agents/urls.go +++ b/openstack/networking/v2/extensions/agents/urls.go @@ -21,6 +21,14 @@ func getURL(c *gophercloud.ServiceClient, id string) string { return resourceURL(c, id) } +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + func dhcpNetworksURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, dhcpNetworksResourcePath) } @@ -28,3 +36,11 @@ func dhcpNetworksURL(c *gophercloud.ServiceClient, id string) string { func listDHCPNetworksURL(c *gophercloud.ServiceClient, id string) string { return dhcpNetworksURL(c, id) } + +func scheduleDHCPNetworkURL(c *gophercloud.ServiceClient, id string) string { + return dhcpNetworksURL(c, id) +} + +func removeDHCPNetworkURL(c *gophercloud.ServiceClient, id string, networkID string) string { + return c.ServiceURL(resourcePath, id, dhcpNetworksResourcePath, networkID) +} From d4a6850dc27530c05b0701df1cab8f9ac2a57944 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 25 Apr 2020 19:52:13 -0600 Subject: [PATCH 1079/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4f82532c6..9e25570bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,10 @@ IMPROVEMENTS * 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) BUG FIXES From 336458aa7f54a59c98a5c67743e5779c283a61e1 Mon Sep 17 00:00:00 2001 From: vtdat Date: Tue, 28 Apr 2020 08:55:10 +0700 Subject: [PATCH 1080/2296] Fix misleading/typo in openstack client docs (#1957) * Fix doc using wrong variable * Change variable names to remain consistency across docs --- openstack/blockstorage/extensions/volumetransfers/doc.go | 6 +++--- openstack/blockstorage/v3/attachments/doc.go | 4 ++-- openstack/clustering/v1/nodes/doc.go | 4 ++-- openstack/clustering/v1/policies/doc.go | 4 ++-- openstack/compute/v2/extensions/aggregates/doc.go | 4 ++-- openstack/containerinfra/v1/certificates/doc.go | 4 ++-- openstack/imageservice/v2/imageimport/doc.go | 4 ++-- openstack/networking/v2/extensions/qos/policies/doc.go | 4 ++-- openstack/networking/v2/extensions/trunks/doc.go | 4 ++-- openstack/placement/v1/resourceproviders/doc.go | 4 ++-- openstack/workflow/v2/crontriggers/doc.go | 2 +- openstack/workflow/v2/executions/doc.go | 2 +- openstack/workflow/v2/workflows/doc.go | 2 +- 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/openstack/blockstorage/extensions/volumetransfers/doc.go b/openstack/blockstorage/extensions/volumetransfers/doc.go index 9d842e05b6..fa1ddaa0c7 100644 --- a/openstack/blockstorage/extensions/volumetransfers/doc.go +++ b/openstack/blockstorage/extensions/volumetransfers/doc.go @@ -27,9 +27,9 @@ Example to List all Volume Transfer requests being an OpenStack admin Example to Create a Volume Transfer request createOpts := volumetransfers.CreateOpts{ - VolumeID: "uuid", - Name: "my-volume-transfer", - } + VolumeID: "uuid", + Name: "my-volume-transfer", + } transfer, err := volumetransfers.Create(client, createOpts).Extract() if err != nil { diff --git a/openstack/blockstorage/v3/attachments/doc.go b/openstack/blockstorage/v3/attachments/doc.go index 4b193431c5..dc6156ac74 100644 --- a/openstack/blockstorage/v3/attachments/doc.go +++ b/openstack/blockstorage/v3/attachments/doc.go @@ -28,13 +28,13 @@ Example to List Attachments Example to Create Attachment - opts := &attachments.CreateOpts{ + createOpts := &attachments.CreateOpts{ InstanceiUUID: "uuid", VolumeUUID: "uuid" } client.Microversion = "3.27" - attachment, err := attachments.Create(client, opts).Extract() + attachment, err := attachments.Create(client, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index 2bd644604b..3d7ce0b805 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -4,7 +4,7 @@ the OpenStack Clustering service. Example to Create a Node - opts := nodes.CreateOpts{ + createOpts := nodes.CreateOpts{ ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", Metadata: map[string]interface{}{}, Name: "node-e395be1e-002", @@ -12,7 +12,7 @@ Example to Create a Node Role: "", } - node, err := nodes.Create(serviceClient, opts).Extract() + node, err := nodes.Create(serviceClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index ffe75f6e13..422f5cf9f0 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -25,7 +25,7 @@ Example to List Policies Example to Create a Policy - opts := policies.CreateOpts{ + createOpts := policies.CreateOpts{ Name: "new_policy", Spec: policies.Spec{ Description: "new policy description", @@ -43,7 +43,7 @@ Example to Create a Policy }, } - createdPolicy, err := policies.Create(client, opts).Extract() + createdPolicy, err := policies.Create(client, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index 97f1b033da..fbbf182b57 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -4,12 +4,12 @@ OpenStack cloud. Example of Create Aggregate - opts := aggregates.CreateOpts{ + createOpts := aggregates.CreateOpts{ Name: "name", AvailabilityZone: "london", } - aggregate, err := aggregates.Create(computeClient, opts).Extract() + aggregate, err := aggregates.Create(computeClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/certificates/doc.go b/openstack/containerinfra/v1/certificates/doc.go index b5c39f7e66..86125e9473 100644 --- a/openstack/containerinfra/v1/certificates/doc.go +++ b/openstack/containerinfra/v1/certificates/doc.go @@ -13,12 +13,12 @@ Example to get certificates Example to create certificates - opts := certificates.CreateOpts{ + createOpts := certificates.CreateOpts{ BayUUID: "d564b18a-2890-4152-be3d-e05d784ff727", CSR: "-----BEGIN CERTIFICATE REQUEST-----\nMIIEfzCCAmcCAQAwFDESMBAGA1UEAxMJWW91ciBOYW1lMIICIjANBgkqhkiG9w0B\n-----END CERTIFICATE REQUEST-----\n", } - response, err := certificates.Create(sc, opts).Extract() + response, err := certificates.Create(sc, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/imageservice/v2/imageimport/doc.go b/openstack/imageservice/v2/imageimport/doc.go index 16f8ce32f5..7772445651 100644 --- a/openstack/imageservice/v2/imageimport/doc.go +++ b/openstack/imageservice/v2/imageimport/doc.go @@ -13,13 +13,13 @@ Example to Get an information about the Import API Example to Create a new image import - opts := imageimport.CreateOpts{ + 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, opts).ExtractErr() + err := imageimport.Create(imagesClient, imageID, createOpts).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index 4cd9c2c307..3386812ed0 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -213,13 +213,13 @@ Example to Get a specific QoS policy Example to Create a QoS policy - opts := policies.CreateOpts{ + createOpts := policies.CreateOpts{ Name: "shared-default-policy", Shared: true, IsDefault: true, } - policy, err := policies.Create(networkClient, opts).Extract() + policy, err := policies.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 82496ffd0b..879e00070e 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -14,7 +14,7 @@ Example of a new empty Trunk creation PortID: "a6f0560c-b7a8-401f-bf6e-d0a5c851ae10", } - trunk, err := trunks.Create(networkClient, trunkOpts).Extract() + trunk, err := trunks.Create(networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example of a new Trunk creation with 2 subports }, } - trunk, err := trunks.Create(client, trunkOpts).Extract() + trunk, err := trunks.Create(client, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index d11000d22f..051d404dbd 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -19,12 +19,12 @@ Example to list resource providers Example to create resource providers - opts := resourceproviders.CreateOpts{ + createOpts := resourceproviders.CreateOpts{ Name: "new-rp", UUID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", } - rp, err := resourceproviders.Create(placementClient, opts).Extract() + rp, err := resourceproviders.Create(placementClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go index 36e36d4a61..46164cad56 100644 --- a/openstack/workflow/v2/crontriggers/doc.go +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -48,7 +48,7 @@ Create a cron trigger. This example will start the workflow "echo" each day at 8 "msg": "world", }, } - crontrigger, err := crontriggers.Create(mistralClient, opts).Extract() + crontrigger, err := crontriggers.Create(mistralClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/workflow/v2/executions/doc.go b/openstack/workflow/v2/executions/doc.go index 6e1777bd5a..cb1f4804ba 100644 --- a/openstack/workflow/v2/executions/doc.go +++ b/openstack/workflow/v2/executions/doc.go @@ -46,7 +46,7 @@ Create an execution Description: "this is a description", } - execution, err := executions.Create(mistralClient, opts).Extract() + execution, err := executions.Create(mistralClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/workflow/v2/workflows/doc.go b/openstack/workflow/v2/workflows/doc.go index 4e59fff996..af3c400d5d 100644 --- a/openstack/workflow/v2/workflows/doc.go +++ b/openstack/workflow/v2/workflows/doc.go @@ -56,7 +56,7 @@ Create a workflow Namespace: "some-namespace", } - workflow, err := workflows.Create(mistralClient, opts).Extract() + workflow, err := workflows.Create(mistralClient, createOpts).Extract() if err != nil { panic(err) } From 84b3cb3964512bb0595c07058a6f04feafeea360 Mon Sep 17 00:00:00 2001 From: Johannes Scheuermann Date: Wed, 29 Apr 2020 17:56:59 +0200 Subject: [PATCH 1081/2296] Add extra and option fields to project (#1951) * Add extra and option fields to project * Adjust test cases for new options * Adjust user tests for project changes --- openstack/identity/v3/projects/requests.go | 44 ++++++++++++++++- openstack/identity/v3/projects/results.go | 47 +++++++++++++++++++ .../identity/v3/projects/testing/fixtures.go | 26 +++++++--- .../v3/projects/testing/requests_test.go | 2 + .../identity/v3/users/testing/fixtures.go | 6 +++ 5 files changed, 117 insertions(+), 8 deletions(-) diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 999da0aeae..27092ebb5b 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -120,11 +120,31 @@ type CreateOpts struct { // Tags is a list of tags to associate with the project. Tags []string `json:"tags,omitempty"` + + // Extra is free-form extra key/value pairs to describe the project. + Extra map[string]interface{} `json:"-"` + + // Options are defined options in the API to enable certain features. + Options map[Option]interface{} `json:"options,omitempty"` } // ToProjectCreateMap formats a CreateOpts into a create request. func (opts CreateOpts) ToProjectCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "project") + b, err := gophercloud.BuildRequestBody(opts, "project") + + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["project"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil } // Create creates a new Project. @@ -174,11 +194,31 @@ type UpdateOpts struct { // Tags is a list of tags to associate with the project. Tags *[]string `json:"tags,omitempty"` + + // Extra is free-form extra key/value pairs to describe the project. + Extra map[string]interface{} `json:"-"` + + // Options are defined options in the API to enable certain features. + Options map[Option]interface{} `json:"options,omitempty"` } // ToUpdateCreateMap formats a UpdateOpts into an update request. func (opts UpdateOpts) ToProjectUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "project") + b, err := gophercloud.BuildRequestBody(opts, "project") + + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["project"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil } // Update modifies the attributes of a project. diff --git a/openstack/identity/v3/projects/results.go b/openstack/identity/v3/projects/results.go index f3557dcbdd..a601809101 100644 --- a/openstack/identity/v3/projects/results.go +++ b/openstack/identity/v3/projects/results.go @@ -1,10 +1,21 @@ package projects import ( + "encoding/json" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) +// Option is a specific option defined at the API to enable features +// on a project. +type Option string + +const ( + Immutable Option = "immutable" +) + type projectResult struct { gophercloud.Result } @@ -58,6 +69,42 @@ type Project struct { // Tags is the list of tags associated with the project. Tags []string `json:"tags,omitempty"` + + // Extra is free-form extra key/value pairs to describe the project. + Extra map[string]interface{} `json:"-"` + + // Options are defined options in the API to enable certain features. + Options map[Option]interface{} `json:"options,omitempty"` +} + +func (r *Project) UnmarshalJSON(b []byte) error { + type tmp Project + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Project(s.tmp) + + // Collect other fields and bundle them into Extra + // but only if a field titled "extra" wasn't sent. + if s.Extra != nil { + r.Extra = s.Extra + } else { + var result interface{} + err := json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = internal.RemainingKeys(Project{}, resultMap) + } + } + + return err } // ProjectPage is a single page of Project results. diff --git a/openstack/identity/v3/projects/testing/fixtures.go b/openstack/identity/v3/projects/testing/fixtures.go index f834c1e87a..b774e3fc82 100644 --- a/openstack/identity/v3/projects/testing/fixtures.go +++ b/openstack/identity/v3/projects/testing/fixtures.go @@ -22,7 +22,8 @@ const ListOutput = ` "id": "1234", "name": "Red Team", "parent_id": null, - "tags": ["Red", "Team"] + "tags": ["Red", "Team"], + "test": "old" }, { "is_domain": false, @@ -31,7 +32,10 @@ const ListOutput = ` "enabled": true, "id": "9876", "name": "Blue Team", - "parent_id": null + "parent_id": null, + "options": { + "immutable": true + } } ], "links": { @@ -52,7 +56,8 @@ const GetOutput = ` "id": "1234", "name": "Red Team", "parent_id": null, - "tags": ["Red", "Team"] + "tags": ["Red", "Team"], + "test": "old" } } ` @@ -63,7 +68,8 @@ const CreateRequest = ` "project": { "description": "The team that is red", "name": "Red Team", - "tags": ["Red", "Team"] + "tags": ["Red", "Team"], + "test": "old" } } ` @@ -74,7 +80,8 @@ const UpdateRequest = ` "project": { "description": "The team that is bright red", "name": "Bright Red Team", - "tags": ["Red"] + "tags": ["Red"], + "test": "new" } } ` @@ -90,7 +97,8 @@ const UpdateOutput = ` "id": "1234", "name": "Bright Red Team", "parent_id": null, - "tags": ["Red"] + "tags": ["Red"], + "test": "new" } } ` @@ -105,6 +113,7 @@ var RedTeam = projects.Project{ Name: "Red Team", ParentID: "", Tags: []string{"Red", "Team"}, + Extra: map[string]interface{}{"test": "old"}, } // BlueTeam is a Project fixture. @@ -116,6 +125,10 @@ var BlueTeam = projects.Project{ ID: "9876", Name: "Blue Team", ParentID: "", + Extra: make(map[string]interface{}), + Options: map[projects.Option]interface{}{ + projects.Immutable: true, + }, } // UpdatedRedTeam is a Project Fixture. @@ -128,6 +141,7 @@ var UpdatedRedTeam = projects.Project{ Name: "Bright Red Team", ParentID: "", Tags: []string{"Red"}, + Extra: map[string]interface{}{"test": "new"}, } // ExpectedProjectSlice is the slice of projects expected to be returned from ListOutput. diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 2a854bcfd5..1a78699425 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -80,6 +80,7 @@ func TestCreateProject(t *testing.T) { Name: "Red Team", Description: "The team that is red", Tags: []string{"Red", "Team"}, + Extra: map[string]interface{}{"test": "old"}, } actual, err := projects.Create(client.ServiceClient(), createOpts).Extract() @@ -106,6 +107,7 @@ func TestUpdateProject(t *testing.T) { Name: "Bright Red Team", Description: &description, Tags: &[]string{"Red"}, + Extra: map[string]interface{}{"test": "new"}, } actual, err := projects.Update(client.ServiceClient(), "1234", updateOpts).Extract() diff --git a/openstack/identity/v3/users/testing/fixtures.go b/openstack/identity/v3/users/testing/fixtures.go index d68e1ac2a4..4b20a22e64 100644 --- a/openstack/identity/v3/users/testing/fixtures.go +++ b/openstack/identity/v3/users/testing/fixtures.go @@ -353,6 +353,9 @@ var FirstProject = projects.Project{ ID: "abcde", Name: "project 1", ParentID: "11111", + Extra: map[string]interface{}{ + "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/abcde"}, + }, } var SecondProject = projects.Project{ @@ -362,6 +365,9 @@ var SecondProject = projects.Project{ ID: "bcdef", Name: "project 2", ParentID: "22222", + Extra: map[string]interface{}{ + "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/bcdef"}, + }, } var ExpectedProjectsSlice = []projects.Project{FirstProject, SecondProject} From 822b5cd65fc83eaf2bf5ba859000069e2b838ccb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 29 Apr 2020 09:59:11 -0600 Subject: [PATCH 1082/2296] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e25570bee..026802c168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,13 @@ IMPROVEMENTS * 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) + BUG FIXES From 3f1e8e0098d4dae6a70c19aba4078a1b5ceef725 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 May 2020 12:30:04 -0600 Subject: [PATCH 1083/2296] Core: Fix issue with decoding slices of composed structs (#1964) --- results.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/results.go b/results.go index 94a16bff0b..1b608103b7 100644 --- a/results.go +++ b/results.go @@ -131,6 +131,18 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { // fields of the struct or composed extension struct // at the end of this method. toValue.Set(newSlice) + + // jtopjian: This was put into place to resolve the issue + // described at + // https://github.com/gophercloud/gophercloud/issues/1963 + // + // This probably isn't the best fix, but it appears to + // be resolving the issue, so I'm going to implement it + // for now. + // + // For future readers, this entire case statement could + // use a review. + return nil } } case reflect.Struct: From 4113412d2c6df99654011c97241f2c9475bcffed Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 May 2020 12:31:16 -0600 Subject: [PATCH 1084/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 026802c168..cc9c61eac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,12 +42,11 @@ IMPROVEMENTS * 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) - - 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) From 6cd283dba8128e19e218cda2043e741081515d33 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 15 May 2020 03:57:31 +0200 Subject: [PATCH 1085/2296] Glance V2: Add create action response headers support (#1962) --- openstack/imageservice/v2/images/results.go | 40 ++++++++++++++++++- .../v2/images/testing/fixtures.go | 12 +++--- .../v2/images/testing/requests_test.go | 8 ++++ 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index 676181e1f4..f445cc38fc 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "time" "github.com/gophercloud/gophercloud" @@ -86,13 +87,22 @@ type Image struct { // VirtualSize is the virtual size of the image VirtualSize int64 `json:"virtual_size"` + + // OpenStackImageImportMethods is a slice listing the types of import + // methods available in the cloud. + OpenStackImageImportMethods []string `json:"-"` + // OpenStackImageStoreIDs is a slice listing the store IDs available in + // the cloud. + OpenStackImageStoreIDs []string `json:"-"` } func (r *Image) UnmarshalJSON(b []byte) error { type tmp Image var s struct { tmp - SizeBytes interface{} `json:"size"` + SizeBytes interface{} `json:"size"` + OpenStackImageImportMethods string `json:"openstack-image-import-methods"` + OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` } err := json.Unmarshal(b, &s) if err != nil { @@ -120,9 +130,18 @@ func (r *Image) UnmarshalJSON(b []byte) error { if resultMap, ok := result.(map[string]interface{}); ok { delete(resultMap, "self") delete(resultMap, "size") + delete(resultMap, "openstack-image-import-methods") + delete(resultMap, "openstack-image-store-ids") r.Properties = internal.RemainingKeys(Image{}, resultMap) } + if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageImportMethods), splitFunc); len(v) > 0 { + r.OpenStackImageImportMethods = v + } + if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageStoreIDs), splitFunc); len(v) > 0 { + r.OpenStackImageStoreIDs = v + } + return err } @@ -133,6 +152,20 @@ type commonResult struct { // Extract interprets any commonResult as an Image. func (r commonResult) Extract() (*Image, error) { var s *Image + if v, ok := r.Body.(map[string]interface{}); ok { + for k, h := range r.Header { + if strings.ToLower(k) == "openstack-image-import-methods" { + for _, s := range h { + v["openstack-image-import-methods"] = s + } + } + if strings.ToLower(k) == "openstack-image-store-ids" { + for _, s := range h { + v["openstack-image-store-ids"] = s + } + } + } + } err := r.ExtractInto(&s) return s, err } @@ -200,3 +233,8 @@ func ExtractImages(r pagination.Page) ([]Image, error) { err := (r.(ImagePage)).ExtractInto(&s) return s.Images, err } + +// splitFunc is a helper function used to avoid a slice of empty strings. +func splitFunc(c rune) bool { + return c == ',' +} diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index 357072f5cf..0311699b11 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -174,8 +174,8 @@ func HandleImageCreationSuccessfully(t *testing.T) { ] }`) - w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{ "status": "queued", "name": "Ubuntu 12.10", @@ -220,8 +220,10 @@ func HandleImageCreationSuccessfullyNulls(t *testing.T) { ] }`) + w.Header().Set("Content-Type", "application/json") + w.Header().Set("OpenStack-image-import-methods", "glance-direct,web-download") + w.Header().Set("OpenStack-image-store-ids", "123,456") w.WriteHeader(http.StatusCreated) - w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "architecture": "x86_64", "status": "queued", @@ -254,8 +256,8 @@ func HandleImageGetSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) - w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "status": "active", "name": "cirros-0.3.2-x86_64-disk", @@ -332,8 +334,8 @@ func HandleImageUpdateSuccessfully(t *testing.T) { th.AssertEquals(t, "application/openstack-images-v2.1-json-patch", r.Header.Get("Content-Type")) - w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "name": "Fedora 17", @@ -431,8 +433,8 @@ func HandleImageUpdatePropertiesSuccessfully(t *testing.T) { th.AssertEquals(t, "application/openstack-images-v2.1-json-patch", r.Header.Get("Content-Type")) - w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{ "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "name": "Fedora 17", diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index d5c3f600c1..befe7e9559 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -173,6 +173,14 @@ func TestCreateImageNulls(t *testing.T) { Schema: schema, Properties: properties, SizeBytes: sizeBytes, + OpenStackImageImportMethods: []string{ + "glance-direct", + "web-download", + }, + OpenStackImageStoreIDs: []string{ + "123", + "456", + }, } th.AssertDeepEquals(t, &expectedImage, actualImage) From 2b034ff895fad1bca237ea4143e4510d843f1db7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 14 May 2020 19:58:33 -0600 Subject: [PATCH 1086/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9c61eac8..708e9dc43d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ IMPROVEMENTS * 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 From 0b03630bc64338e55c3a2bd150c4de768dc6818e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 14 May 2020 19:59:05 -0600 Subject: [PATCH 1087/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 708e9dc43d..23af89a3f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.11.0 (Unreleased) +## 0.12.0 (Unreleased) + +## 0.11.0 (May 14, 2020) UPGRADE NOTES From e148d50b5f56ff269d8977b01e9ecd1eaa950698 Mon Sep 17 00:00:00 2001 From: Feilong Wang Date: Tue, 19 May 2020 02:58:26 +1200 Subject: [PATCH 1088/2296] ContainerInfra v1: Fix cluster Get (#1968) This is a regression issue introduced by[1], which may impact the cluster Get. The health_status_reason attribute should be a dict instead of string. [1] https://github.com/gophercloud/gophercloud/pull/1910 --- .../containerinfra/v1/clusters/results.go | 62 +++++++++---------- .../v1/clusters/testing/fixtures.go | 12 ++-- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 7e4d2efa4a..5581b0b749 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -69,37 +69,37 @@ func (r ResizeResult) Extract() (string, error) { } type Cluster struct { - APIAddress string `json:"api_address"` - COEVersion string `json:"coe_version"` - ClusterTemplateID string `json:"cluster_template_id"` - ContainerVersion string `json:"container_version"` - CreateTimeout int `json:"create_timeout"` - CreatedAt time.Time `json:"created_at"` - DiscoveryURL string `json:"discovery_url"` - DockerVolumeSize int `json:"docker_volume_size"` - Faults map[string]string `json:"faults"` - FlavorID string `json:"flavor_id"` - KeyPair string `json:"keypair"` - Labels map[string]string `json:"labels"` - Links []gophercloud.Link `json:"links"` - MasterFlavorID string `json:"master_flavor_id"` - MasterAddresses []string `json:"master_addresses"` - MasterCount int `json:"master_count"` - Name string `json:"name"` - NodeAddresses []string `json:"node_addresses"` - NodeCount int `json:"node_count"` - ProjectID string `json:"project_id"` - StackID string `json:"stack_id"` - Status string `json:"status"` - StatusReason string `json:"status_reason"` - UUID string `json:"uuid"` - UpdatedAt time.Time `json:"updated_at"` - UserID string `json:"user_id"` - FloatingIPEnabled bool `json:"floating_ip_enabled"` - FixedNetwork string `json:"fixed_network"` - FixedSubnet string `json:"fixed_subnet"` - HealthStatus string `json:"health_status"` - HealthStatusReason string `json:"health_status_reason"` + APIAddress string `json:"api_address"` + COEVersion string `json:"coe_version"` + ClusterTemplateID string `json:"cluster_template_id"` + ContainerVersion string `json:"container_version"` + CreateTimeout int `json:"create_timeout"` + CreatedAt time.Time `json:"created_at"` + DiscoveryURL string `json:"discovery_url"` + DockerVolumeSize int `json:"docker_volume_size"` + Faults map[string]string `json:"faults"` + FlavorID string `json:"flavor_id"` + KeyPair string `json:"keypair"` + Labels map[string]string `json:"labels"` + Links []gophercloud.Link `json:"links"` + MasterFlavorID string `json:"master_flavor_id"` + MasterAddresses []string `json:"master_addresses"` + MasterCount int `json:"master_count"` + Name string `json:"name"` + NodeAddresses []string `json:"node_addresses"` + NodeCount int `json:"node_count"` + ProjectID string `json:"project_id"` + StackID string `json:"stack_id"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + UUID string `json:"uuid"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` + FloatingIPEnabled bool `json:"floating_ip_enabled"` + FixedNetwork string `json:"fixed_network"` + FixedSubnet string `json:"fixed_subnet"` + HealthStatus string `json:"health_status"` + HealthStatusReason map[string]interface{} `json:"health_status_reason"` } type ClusterPage struct { diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index c6a30793ae..e8264039ea 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -53,7 +53,7 @@ var ExpectedCluster = clusters.Cluster{ FixedNetwork: "private_network", FixedSubnet: "private_subnet", HealthStatus: "HEALTHY", - HealthStatusReason: "", + HealthStatusReason: map[string]interface{}{"api": "ok"}, } var ExpectedCluster2 = clusters.Cluster{ @@ -88,7 +88,7 @@ var ExpectedCluster2 = clusters.Cluster{ FixedNetwork: "private_network", FixedSubnet: "private_subnet", HealthStatus: "HEALTHY", - HealthStatusReason: "", + HealthStatusReason: map[string]interface{}{"api": "ok"}, } var ExpectedClusterUUID = clusterUUID @@ -155,7 +155,7 @@ var ClusterGetResponse = fmt.Sprintf(` "fixed_network": "private_network", "fixed_subnet": "private_subnet", "health_status": "HEALTHY", - "health_status_reason": "" + "health_status_reason": {"api": "ok"} }`, clusterUUID) var ClusterListResponse = fmt.Sprintf(` @@ -196,7 +196,8 @@ var ClusterListResponse = fmt.Sprintf(` "floating_ip_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet", - "health_status": "HEALTHY" + "health_status": "HEALTHY", + "health_status_reason": {"api": "ok"} }, { "api_address":"https://172.24.4.6:6443", @@ -233,7 +234,8 @@ var ClusterListResponse = fmt.Sprintf(` "floating_ip_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet", - "health_status": "HEALTHY" + "health_status": "HEALTHY", + "health_status_reason": {"api": "ok"} } ] }`, clusterUUID, clusterUUID2) From 2e4dd013d8694a920e6a1797777a7ec6bcdb522b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 May 2020 08:59:28 -0600 Subject: [PATCH 1089/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23af89a3f7..45ed77b04d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.12.0 (Unreleased) +BUG FIXES + +* Changed `containerinfra/v1/clusters.Cluster.HealthStatusReason` from `string` to `map[string]interface{}` [GH-1968](https://github.com/gophercloud/gophercloud/pull/1968) + ## 0.11.0 (May 14, 2020) UPGRADE NOTES From cbab4d1ae11232b1331e1f26caa4088a5c5e6a35 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 18 May 2020 20:31:03 +0200 Subject: [PATCH 1090/2296] Cinder V3: Marshal an empty metadata object in backup import (#1967) --- .../extensions/backups/results.go | 62 ++++++++++--------- .../extensions/backups/testing/fixtures.go | 3 +- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/openstack/blockstorage/extensions/backups/results.go b/openstack/blockstorage/extensions/backups/results.go index bfba515d4a..a575498de2 100644 --- a/openstack/blockstorage/extensions/backups/results.go +++ b/openstack/blockstorage/extensions/backups/results.go @@ -237,35 +237,35 @@ func (r ImportResult) ExtractInto(v interface{}) error { // ImportBackup contains all the information to import a Cinder Backup. type ImportBackup struct { - ID string `json:"id"` - CreatedAt time.Time `json:"-"` - UpdatedAt time.Time `json:"-"` - VolumeID string `json:"volume_id"` - SnapshotID *string `json:"snapshot_id"` - Status *string `json:"status"` - Size *int `json:"size"` - ObjectCount *int `json:"object_count"` - Container *string `json:"container"` - ServiceMetadata *string `json:"service_metadata"` - Service *string `json:"service"` - Host *string `json:"host"` - UserID string `json:"user_id"` - DeletedAt time.Time `json:"-"` - DataTimestamp time.Time `json:"-"` - TempSnapshotID *string `json:"temp_snapshot_id"` - TempVolumeID *string `json:"temp_volume_id"` - RestoreVolumeID *string `json:"restore_volume_id"` - NumDependentBackups *int `json:"num_dependent_backups"` - EncryptionKeyID *string `json:"encryption_key_id"` - ParentID *string `json:"parent_id"` - Deleted bool `json:"deleted"` - DisplayName *string `json:"display_name"` - DisplayDescription *string `json:"display_description"` - DriverInfo interface{} `json:"driver_info"` - FailReason *string `json:"fail_reason"` - ProjectID string `json:"project_id"` - Metadata *map[string]string `json:"metadata"` - AvailabilityZone *string `json:"availability_zone"` + ID string `json:"id"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + VolumeID string `json:"volume_id"` + SnapshotID *string `json:"snapshot_id"` + Status *string `json:"status"` + Size *int `json:"size"` + ObjectCount *int `json:"object_count"` + Container *string `json:"container"` + ServiceMetadata *string `json:"service_metadata"` + Service *string `json:"service"` + Host *string `json:"host"` + UserID string `json:"user_id"` + DeletedAt time.Time `json:"-"` + DataTimestamp time.Time `json:"-"` + TempSnapshotID *string `json:"temp_snapshot_id"` + TempVolumeID *string `json:"temp_volume_id"` + RestoreVolumeID *string `json:"restore_volume_id"` + NumDependentBackups *int `json:"num_dependent_backups"` + EncryptionKeyID *string `json:"encryption_key_id"` + ParentID *string `json:"parent_id"` + Deleted bool `json:"deleted"` + DisplayName *string `json:"display_name"` + DisplayDescription *string `json:"display_description"` + DriverInfo interface{} `json:"driver_info"` + FailReason *string `json:"fail_reason"` + ProjectID string `json:"project_id"` + Metadata map[string]string `json:"metadata"` + AvailabilityZone *string `json:"availability_zone"` } // UnmarshalJSON converts our JSON API response into our backup struct @@ -324,6 +324,10 @@ func (r ImportBackup) MarshalJSON() ([]byte, error) { t.DataTimestamp = &v } + if r.Metadata == nil { + r.Metadata = make(map[string]string) + } + s := tmp{ b(r), t, diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures.go b/openstack/blockstorage/extensions/backups/testing/fixtures.go index 78567e337c..cceef70a19 100644 --- a/openstack/blockstorage/extensions/backups/testing/fixtures.go +++ b/openstack/blockstorage/extensions/backups/testing/fixtures.go @@ -103,7 +103,7 @@ const ExportResponse = ` { "backup-record": { "backup_service": "cinder.backup.drivers.swift.SwiftBackupDriver", - "backup_url": "eyJpZCI6ImQzMjAxOWQzLWJjNmUtNDMxOS05YzFkLTY3MjJmYzEzNmEyMiIsInZvbHVtZV9pZCI6ImNmOWJjNmZhLWM1YmMtNDFmNi1iYzRlLTZlNzZjMGJlYTk1OSIsInNuYXBzaG90X2lkIjpudWxsLCJzdGF0dXMiOiJhdmFpbGFibGUiLCJzaXplIjoxLCJvYmplY3RfY291bnQiOjIsImNvbnRhaW5lciI6Im15LXRlc3QtYmFja3VwIiwic2VydmljZV9tZXRhZGF0YSI6InZvbHVtZV9jZjliYzZmYS1jNWJjLTQxZjYtYmM0ZS02ZTc2YzBiZWE5NTkvMjAyMDAzMTExOTI4NTUvYXpfcmVnaW9uYl9iYWNrdXBfYjg3YmIxZTUtMGQ0ZS00NDVlLWE1NDgtNWFlNzQyNTYyYmFjIiwic2VydmljZSI6ImNpbmRlci5iYWNrdXAuZHJpdmVycy5zd2lmdC5Td2lmdEJhY2t1cERyaXZlciIsImhvc3QiOiJjaW5kZXItYmFja3VwLWhvc3QxIiwidXNlcl9pZCI6IjkzNTE0ZTA0LWEwMjYtNGY2MC04MTc2LTM5NWM4NTk1MDFkZCIsInRlbXBfc25hcHNob3RfaWQiOm51bGwsInRlbXBfdm9sdW1lX2lkIjpudWxsLCJyZXN0b3JlX3ZvbHVtZV9pZCI6bnVsbCwibnVtX2RlcGVuZGVudF9iYWNrdXBzIjpudWxsLCJlbmNyeXB0aW9uX2tleV9pZCI6bnVsbCwicGFyZW50X2lkIjpudWxsLCJkZWxldGVkIjpmYWxzZSwiZGlzcGxheV9uYW1lIjpudWxsLCJkaXNwbGF5X2Rlc2NyaXB0aW9uIjpudWxsLCJkcml2ZXJfaW5mbyI6bnVsbCwiZmFpbF9yZWFzb24iOm51bGwsInByb2plY3RfaWQiOiIxNGYxYzFmNWQxMmI0NzU1Yjk0ZWRlZjc4ZmY4YjMyNSIsIm1ldGFkYXRhIjpudWxsLCJhdmFpbGFiaWxpdHlfem9uZSI6InJlZ2lvbjFiIiwiY3JlYXRlZF9hdCI6IjIwMjAtMDMtMTFUMTk6MjU6MjRaIiwidXBkYXRlZF9hdCI6IjIwMjAtMDMtMTFUMTk6Mjk6MDhaIiwiZGVsZXRlZF9hdCI6bnVsbCwiZGF0YV90aW1lc3RhbXAiOiIyMDIwLTAzLTExVDE5OjI1OjI0WiJ9" + "backup_url": "eyJpZCI6ImQzMjAxOWQzLWJjNmUtNDMxOS05YzFkLTY3MjJmYzEzNmEyMiIsInZvbHVtZV9pZCI6ImNmOWJjNmZhLWM1YmMtNDFmNi1iYzRlLTZlNzZjMGJlYTk1OSIsInNuYXBzaG90X2lkIjpudWxsLCJzdGF0dXMiOiJhdmFpbGFibGUiLCJzaXplIjoxLCJvYmplY3RfY291bnQiOjIsImNvbnRhaW5lciI6Im15LXRlc3QtYmFja3VwIiwic2VydmljZV9tZXRhZGF0YSI6InZvbHVtZV9jZjliYzZmYS1jNWJjLTQxZjYtYmM0ZS02ZTc2YzBiZWE5NTkvMjAyMDAzMTExOTI4NTUvYXpfcmVnaW9uYl9iYWNrdXBfYjg3YmIxZTUtMGQ0ZS00NDVlLWE1NDgtNWFlNzQyNTYyYmFjIiwic2VydmljZSI6ImNpbmRlci5iYWNrdXAuZHJpdmVycy5zd2lmdC5Td2lmdEJhY2t1cERyaXZlciIsImhvc3QiOiJjaW5kZXItYmFja3VwLWhvc3QxIiwidXNlcl9pZCI6IjkzNTE0ZTA0LWEwMjYtNGY2MC04MTc2LTM5NWM4NTk1MDFkZCIsInRlbXBfc25hcHNob3RfaWQiOm51bGwsInRlbXBfdm9sdW1lX2lkIjpudWxsLCJyZXN0b3JlX3ZvbHVtZV9pZCI6bnVsbCwibnVtX2RlcGVuZGVudF9iYWNrdXBzIjpudWxsLCJlbmNyeXB0aW9uX2tleV9pZCI6bnVsbCwicGFyZW50X2lkIjpudWxsLCJkZWxldGVkIjpmYWxzZSwiZGlzcGxheV9uYW1lIjpudWxsLCJkaXNwbGF5X2Rlc2NyaXB0aW9uIjpudWxsLCJkcml2ZXJfaW5mbyI6bnVsbCwiZmFpbF9yZWFzb24iOm51bGwsInByb2plY3RfaWQiOiIxNGYxYzFmNWQxMmI0NzU1Yjk0ZWRlZjc4ZmY4YjMyNSIsIm1ldGFkYXRhIjp7fSwiYXZhaWxhYmlsaXR5X3pvbmUiOiJyZWdpb24xYiIsImNyZWF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI1OjI0WiIsInVwZGF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI5OjA4WiIsImRlbGV0ZWRfYXQiOm51bGwsImRhdGFfdGltZXN0YW1wIjoiMjAyMC0wMy0xMVQxOToyNToyNFoifQ==" } } ` @@ -154,6 +154,7 @@ var ( CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", + Metadata: make(map[string]string), } backupURL, _ = json.Marshal(backupImport) ) From 7aec46f32c1927e3753f1ffcfd1f001ece7bf539 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 May 2020 12:32:26 -0600 Subject: [PATCH 1091/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45ed77b04d..aaf5da2f61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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) ## 0.11.0 (May 14, 2020) From b66178ef47c6b64b67bf4b026b872e5e7646d21d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 29 May 2020 09:01:28 -0600 Subject: [PATCH 1092/2296] Update doc.go --- openstack/identity/v2/tenants/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/identity/v2/tenants/doc.go b/openstack/identity/v2/tenants/doc.go index 45623369e1..348dd20839 100644 --- a/openstack/identity/v2/tenants/doc.go +++ b/openstack/identity/v2/tenants/doc.go @@ -8,7 +8,7 @@ for more information. Example to List Tenants - listOpts := tenants.ListOpts{ + listOpts := &tenants.ListOpts{ Limit: 2, } From 308d7a93e10ab407a875fa4537d0a15036e14d73 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 31 May 2020 23:46:47 +0200 Subject: [PATCH 1093/2296] OAuth1: Variable name typo fix (#1969) --- openstack/identity/v3/extensions/oauth1/results.go | 6 +++--- openstack/identity/v3/extensions/oauth1/testing/fixtures.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openstack/identity/v3/extensions/oauth1/results.go b/openstack/identity/v3/extensions/oauth1/results.go index bb109e98e3..a67f9381d6 100644 --- a/openstack/identity/v3/extensions/oauth1/results.go +++ b/openstack/identity/v3/extensions/oauth1/results.go @@ -96,8 +96,8 @@ type Token struct { OAuthToken string `q:"oauth_token"` // OAuthTokenSecret is the secret value associated with the OAuth Token. OAuthTokenSecret string `q:"oauth_token_secret"` - // OAUthExpiresAt is the date and time when an OAuth token expires. - OAUthExpiresAt *time.Time `q:"-"` + // OAuthExpiresAt is the date and time when an OAuth token expires. + OAuthExpiresAt *time.Time `q:"-"` } // TokenResult is a struct to handle @@ -127,7 +127,7 @@ func (r TokenResult) Extract() (*Token, error) { if t, err := time.Parse(gophercloud.RFC3339Milli, v); err != nil { return nil, err } else { - token.OAUthExpiresAt = &t + token.OAuthExpiresAt = &t } } diff --git a/openstack/identity/v3/extensions/oauth1/testing/fixtures.go b/openstack/identity/v3/extensions/oauth1/testing/fixtures.go index c710ff867b..b04abb5b32 100644 --- a/openstack/identity/v3/extensions/oauth1/testing/fixtures.go +++ b/openstack/identity/v3/extensions/oauth1/testing/fixtures.go @@ -313,7 +313,7 @@ func HandleListConsumers(t *testing.T) { var Token = oauth1.Token{ OAuthToken: "29971f", OAuthTokenSecret: "238eb8", - OAUthExpiresAt: &tokenExpiresAt, + OAuthExpiresAt: &tokenExpiresAt, } // HandleRequestToken creates an HTTP handler at `/OS-OAUTH1/request_token` on the @@ -351,7 +351,7 @@ func HandleAuthorizeToken(t *testing.T) { var AccessToken = oauth1.Token{ OAuthToken: "accd36", OAuthTokenSecret: "aa47da", - OAUthExpiresAt: &tokenExpiresAt, + OAuthExpiresAt: &tokenExpiresAt, } // HandleCreateAccessToken creates an HTTP handler at `/OS-OAUTH1/access_token` on the From b0121d561effe694e54db88cc04b08ceafd72466 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 31 May 2020 15:47:45 -0600 Subject: [PATCH 1094/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaf5da2f61..bf987f9f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ 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) ## 0.11.0 (May 14, 2020) From d139d601bc1a1cc01ff90acd739f0145bd30d3d4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 4 Jun 2020 09:07:17 -0600 Subject: [PATCH 1095/2296] Acc Tests: Use Ubuntu Xenial for older releases (#1975) --- .zuul.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.zuul.yaml b/.zuul.yaml index b6649a3d6b..f3c4015e07 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -36,6 +36,7 @@ parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on rocky branch + nodeset: ubuntu-xenial vars: global_env: OS_BRANCH: stable/rocky @@ -45,6 +46,7 @@ parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on queens branch + nodeset: ubuntu-xenial vars: global_env: OS_BRANCH: stable/queens @@ -54,6 +56,7 @@ parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on pike branch + nodeset: ubuntu-xenial vars: global_env: OS_BRANCH: stable/pike @@ -63,6 +66,7 @@ parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on ocata branch + nodeset: ubuntu-xenial vars: global_env: OS_BRANCH: stable/ocata @@ -72,6 +76,7 @@ parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on newton branch + nodeset: ubuntu-xenial vars: global_env: OS_BRANCH: stable/newton From 91a627117c060452e8478a90ed69232801902159 Mon Sep 17 00:00:00 2001 From: ji chen Date: Mon, 8 Jun 2020 01:00:41 +0800 Subject: [PATCH 1096/2296] switch bootfromvolume from os-volumes_boot to servers (#1973) according to https://github.com/openstack/nova/blob/master/nova/api/openstack/compute/routes.py#L726 os-volumes_boot is identical to servers routes and it will be deprecated later https://docs.openstack.org/api-ref/compute/ os-volumes_boot is not listed at all so switch from os-volumes_boot to servers --- openstack/compute/v2/extensions/bootfromvolume/urls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/compute/v2/extensions/bootfromvolume/urls.go b/openstack/compute/v2/extensions/bootfromvolume/urls.go index dc007eadf8..e74422d0c1 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/urls.go +++ b/openstack/compute/v2/extensions/bootfromvolume/urls.go @@ -3,5 +3,5 @@ package bootfromvolume import "github.com/gophercloud/gophercloud" func createURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("os-volumes_boot") + return c.ServiceURL("servers") } From 88a3ae476afe088e855c99f70d6b315afc01e44a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 7 Jun 2020 11:02:17 -0600 Subject: [PATCH 1097/2296] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf987f9f8c..88227fa17b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ ## 0.12.0 (Unreleased) +UPGRADE NOTES + +The URL used in the `compute/v2/bootfromvolume` package has been changed from `os-volumes_boot` to `servers`. + +IMPROVEMENTS + +* The URL used in the `compute/v2/bootfromvolume` package has been changed from `os-volumes_boot` to `servers` [GH-1973](https://github.com/gophercloud/gophercloud/pull/1973) + BUG FIXES * Changed `containerinfra/v1/clusters.Cluster.HealthStatusReason` from `string` to `map[string]interface{}` [GH-1968](https://github.com/gophercloud/gophercloud/pull/1968) From 58d03a6a3df456f91ce65d5d3842604f35af7a9d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 7 Jun 2020 11:02:31 -0600 Subject: [PATCH 1098/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88227fa17b..428b4ac948 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ UPGRADE NOTES -The URL used in the `compute/v2/bootfromvolume` package has been changed from `os-volumes_boot` to `servers`. +* The URL used in the `compute/v2/bootfromvolume` package has been changed from `os-volumes_boot` to `servers`. IMPROVEMENTS From 326e5f9d72d8a870bbcf7ed5f5a25c4a468995e5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 7 Jun 2020 11:02:58 -0600 Subject: [PATCH 1099/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 428b4ac948..c0960ff554 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,11 @@ UPGRADE NOTES -* The URL used in the `compute/v2/bootfromvolume` package has been changed from `os-volumes_boot` to `servers`. +* 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/bootfromvolume` package has been changed from `os-volumes_boot` to `servers` [GH-1973](https://github.com/gophercloud/gophercloud/pull/1973) +* 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) BUG FIXES From d48e557b9093ff51c3dae00405d891200934ba8c Mon Sep 17 00:00:00 2001 From: Pavel Ivanov Date: Thu, 11 Jun 2020 04:59:49 +0300 Subject: [PATCH 1100/2296] fix: leak of goroutines on re-auth (#1978) * fix: leak of goroutines on re-auth * fix: remove context cancellation support from reauth due to coverage decrease Co-authored-by: Pavel Ivanov --- provider_client.go | 56 +++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/provider_client.go b/provider_client.go index 43574402e9..53b3ecf27f 100644 --- a/provider_client.go +++ b/provider_client.go @@ -94,10 +94,32 @@ type ProviderClient struct { // reauthlock represents a set of attributes used to help in the reauthentication process. type reauthlock struct { sync.RWMutex - // This channel is non-nil during reauthentication. It can be used to ask the - // goroutine doing Reauthenticate() for its result. Look at the implementation - // of Reauthenticate() for details. - ongoing chan<- (chan<- error) + ongoing *reauthFuture +} + +// reauthFuture represents future result of the reauthentication process. +// while done channel is not closed, reauthentication is in progress. +// when done channel is closed, err contains the result of reauthentication. +type reauthFuture struct { + done chan struct{} + err error +} + +func newReauthFuture() *reauthFuture { + return &reauthFuture{ + make(chan struct{}), + nil, + } +} + +func (f *reauthFuture) Set(err error) { + f.err = err + close(f.done) +} + +func (f *reauthFuture) Get() error { + <-f.done + return f.err } // AuthenticatedHeaders returns a map of HTTP headers that are common for all @@ -112,9 +134,7 @@ func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { ongoing := client.reauthmut.ongoing client.reauthmut.Unlock() if ongoing != nil { - responseChannel := make(chan error) - ongoing <- responseChannel - _ = <-responseChannel + _ = ongoing.Get() } } t := client.Token() @@ -237,21 +257,19 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error { return client.ReauthFunc() } - messages := make(chan (chan<- error)) + future := newReauthFuture() // Check if a Reauthenticate is in progress, or start one if not. client.reauthmut.Lock() ongoing := client.reauthmut.ongoing if ongoing == nil { - client.reauthmut.ongoing = messages + client.reauthmut.ongoing = future } client.reauthmut.Unlock() // If Reauthenticate is running elsewhere, wait for its result. if ongoing != nil { - responseChannel := make(chan error) - ongoing <- responseChannel - return <-responseChannel + return ongoing.Get() } // Perform the actual reauthentication. @@ -264,22 +282,10 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error { // Mark Reauthenticate as finished. client.reauthmut.Lock() + client.reauthmut.ongoing.Set(err) client.reauthmut.ongoing = nil client.reauthmut.Unlock() - // Report result to all other interested goroutines. - // - // This happens in a separate goroutine because another goroutine might have - // acquired a copy of `client.reauthmut.ongoing` before we cleared it, but not - // have come around to sending its request. By answering in a goroutine, we - // can have that goroutine linger until all responseChannels have been sent. - // When GC has collected all sendings ends of the channel, our receiving end - // will be closed and the goroutine will end. - go func() { - for responseChannel := range messages { - responseChannel <- err - } - }() return err } From 1f9e326ce889660d92ec175620ba7eb8c8dd7009 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 10 Jun 2020 20:01:45 -0600 Subject: [PATCH 1101/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0960ff554..886668cabd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ 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) ## 0.11.0 (May 14, 2020) From 7495a1acc79a29a25751e8ad89c4944daa92eafe Mon Sep 17 00:00:00 2001 From: ZouYu Date: Wed, 24 Jun 2020 11:32:14 +0800 Subject: [PATCH 1102/2296] Modify PhysicalDisks's type to support physical disks hints (#1982) Signed-off-by: ZouYu --- .../openstack/baremetal/v1/nodes_test.go | 20 ++++ openstack/baremetal/v1/nodes/requests.go | 12 ++- .../v1/nodes/testing/requests_test.go | 92 +++++++++++++++++++ 3 files changed, 119 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/baremetal/v1/nodes_test.go b/acceptance/openstack/baremetal/v1/nodes_test.go index 27afaef806..f34015563d 100644 --- a/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/acceptance/openstack/baremetal/v1/nodes_test.go @@ -81,6 +81,26 @@ func TestNodesRAIDConfig(t *testing.T) { sizeGB := 100 isTrue := true + err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ + LogicalDisks: []nodes.LogicalDisk{ + { + SizeGB: &sizeGB, + IsRootVolume: &isTrue, + RAIDLevel: nodes.RAID5, + Controller: "software", + PhysicalDisks: []interface{}{ + map[string]string{ + "size": "> 100", + }, + map[string]string{ + "size": "> 100", + }, + }, + }, + }, + }).ExtractErr() + th.AssertNoErr(t, err) + err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 233057ad96..f30eb40a23 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -568,7 +568,7 @@ type LogicalDisk struct { Controller string `json:"controller,omitempty"` // A list of physical disks to use as read by the RAID interface. - PhysicalDisks []string `json:"physical_disks,omitempty"` + PhysicalDisks []interface{} `json:"physical_disks,omitempty"` } func (opts RAIDConfigOpts) ToRAIDConfigMap() (map[string]interface{}, error) { @@ -577,10 +577,12 @@ func (opts RAIDConfigOpts) ToRAIDConfigMap() (map[string]interface{}, error) { return nil, err } - for _, v := range body["logical_disks"].([]interface{}) { - if logicalDisk, ok := v.(map[string]interface{}); ok { - if logicalDisk["size_gb"] == nil { - logicalDisk["size_gb"] = "MAX" + if body["logical_disks"] != nil { + for _, v := range body["logical_disks"].([]interface{}) { + if logicalDisk, ok := v.(map[string]interface{}); ok { + if logicalDisk["size_gb"] == nil { + logicalDisk["size_gb"] = "MAX" + } } } } diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 481f37990a..3770916c13 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -430,3 +430,95 @@ func TestSetRAIDConfigMaxSize(t *testing.T) { err := nodes.SetRAIDConfig(c, "1234asdf", config).ExtractErr() th.AssertNoErr(t, err) } + +func TestToRAIDConfigMap(t *testing.T) { + cases := []struct { + name string + opts nodes.RAIDConfigOpts + expected map[string]interface{} + }{ + { + name: "LogicalDisks is empty", + opts: nodes.RAIDConfigOpts{}, + expected: map[string]interface{}{ + "logical_disks": nil, + }, + }, + { + name: "LogicalDisks is nil", + opts: nodes.RAIDConfigOpts{ + LogicalDisks: nil, + }, + expected: map[string]interface{}{ + "logical_disks": nil, + }, + }, + { + name: "PhysicalDisks is []string", + opts: nodes.RAIDConfigOpts{ + LogicalDisks: []nodes.LogicalDisk{ + { + RAIDLevel: "0", + VolumeName: "root", + PhysicalDisks: []interface{}{"6I:1:5", "6I:1:6", "6I:1:7"}, + }, + }, + }, + expected: map[string]interface{}{ + "logical_disks": []map[string]interface{}{ + { + "raid_level": "0", + "size_gb": "MAX", + "volume_name": "root", + "physical_disks": []interface{}{"6I:1:5", "6I:1:6", "6I:1:7"}, + }, + }, + }, + }, + { + name: "PhysicalDisks is []map[string]string", + opts: nodes.RAIDConfigOpts{ + LogicalDisks: []nodes.LogicalDisk{ + { + RAIDLevel: "0", + VolumeName: "root", + Controller: "software", + PhysicalDisks: []interface{}{ + map[string]string{ + "size": "> 100", + }, + map[string]string{ + "size": "> 100", + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "logical_disks": []map[string]interface{}{ + { + "raid_level": "0", + "size_gb": "MAX", + "volume_name": "root", + "controller": "software", + "physical_disks": []interface{}{ + map[string]interface{}{ + "size": "> 100", + }, + map[string]interface{}{ + "size": "> 100", + }, + }, + }, + }, + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, _ := c.opts.ToRAIDConfigMap() + th.CheckDeepEquals(t, c.expected, got) + }) + } +} From 8b8b1535c1c7c8964e229877f9556e668c8990ba Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 23 Jun 2020 21:33:36 -0600 Subject: [PATCH 1103/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 886668cabd..5cb91af777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ UPGRADE NOTES 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) BUG FIXES From 95b3c5763eae81c6e73e5d312f43ce75ddf8f820 Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Wed, 24 Jun 2020 11:21:35 -0300 Subject: [PATCH 1104/2296] Support http_basic auth_strategy for Ironic Inspector (#1986) Ironic Inspector introduced a new auth_type http_basic that can be used in standalone deployments. The code was introduced by [1] This commit adds the support to use the new authentication type. [1] https://review.opendev.org/#/c/729463/ Co-authored-by: Iury --- .../baremetalintrospection/httpbasic/doc.go | 17 +++++++ .../httpbasic/requests.go | 45 +++++++++++++++++++ .../httpbasic/testing/requests_test.go | 34 ++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 openstack/baremetalintrospection/httpbasic/doc.go create mode 100644 openstack/baremetalintrospection/httpbasic/requests.go create mode 100644 openstack/baremetalintrospection/httpbasic/testing/requests_test.go diff --git a/openstack/baremetalintrospection/httpbasic/doc.go b/openstack/baremetalintrospection/httpbasic/doc.go new file mode 100644 index 0000000000..f69369e129 --- /dev/null +++ b/openstack/baremetalintrospection/httpbasic/doc.go @@ -0,0 +1,17 @@ +/* +Package httpbasic provides support for http_basic bare metal introspection endpoints. + +Example of obtaining and using a client: + + client, err := httpbasic.NewBareMetalIntrospectionHTTPBasic(httpbasic.EndpointOpts{ + IronicInspectorEndpoint: "http://localhost:5050/v1/", + IronicInspectorUser: "myUser", + IronicInspectorUserPassword: "myPassword", + }) + if err != nil { + panic(err) + } + + introspection.GetIntrospectionStatus(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") +*/ +package httpbasic diff --git a/openstack/baremetalintrospection/httpbasic/requests.go b/openstack/baremetalintrospection/httpbasic/requests.go new file mode 100644 index 0000000000..0a726aa1f0 --- /dev/null +++ b/openstack/baremetalintrospection/httpbasic/requests.go @@ -0,0 +1,45 @@ +package httpbasic + +import ( + "encoding/base64" + "fmt" + + "github.com/gophercloud/gophercloud" +) + +// EndpointOpts specifies a "http_basic" Ironic Inspector Endpoint. +type EndpointOpts struct { + IronicInspectorEndpoint string + IronicInspectorUser string + IronicInspectorUserPassword string +} + +func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc := new(gophercloud.ServiceClient) + if eo.IronicInspectorEndpoint == "" { + return nil, fmt.Errorf("IronicInspectorEndpoint is required") + } + if eo.IronicInspectorUser == "" || eo.IronicInspectorUserPassword == "" { + return nil, fmt.Errorf("User and Password are required") + } + + token := []byte(eo.IronicInspectorUser + ":" + eo.IronicInspectorUserPassword) + encodedToken := base64.StdEncoding.EncodeToString(token) + sc.MoreHeaders = map[string]string{"Authorization": "Basic " + encodedToken} + sc.Endpoint = gophercloud.NormalizeURL(eo.IronicInspectorEndpoint) + sc.ProviderClient = client + return sc, nil +} + +// NewBareMetalIntrospectionHTTPBasic creates a ServiceClient that may be used to access a +// "http_basic" bare metal introspection service. +func NewBareMetalIntrospectionHTTPBasic(eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) + if err != nil { + return nil, err + } + + sc.Type = "baremetal-inspector" + + return sc, nil +} diff --git a/openstack/baremetalintrospection/httpbasic/testing/requests_test.go b/openstack/baremetalintrospection/httpbasic/testing/requests_test.go new file mode 100644 index 0000000000..8fbe0c328c --- /dev/null +++ b/openstack/baremetalintrospection/httpbasic/testing/requests_test.go @@ -0,0 +1,34 @@ +package testing + +import ( + "encoding/base64" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/httpbasic" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNoAuth(t *testing.T) { + httpClient, err := httpbasic.NewBareMetalIntrospectionHTTPBasic(httpbasic.EndpointOpts{ + IronicInspectorEndpoint: "http://ironic:5050/v1", + IronicInspectorUser: "myUser", + IronicInspectorUserPassword: "myPasswd", + }) + th.AssertNoErr(t, err) + encToken := base64.StdEncoding.EncodeToString([]byte("myUser:myPasswd")) + headerValue := "Basic " + encToken + th.AssertEquals(t, headerValue, httpClient.MoreHeaders["Authorization"]) + + errTest1, err := httpbasic.NewBareMetalIntrospectionHTTPBasic(httpbasic.EndpointOpts{ + IronicInspectorEndpoint: "http://ironic:5050/v1", + }) + _ = errTest1 + th.AssertEquals(t, "User and Password are required", err.Error()) + + errTest2, err := httpbasic.NewBareMetalIntrospectionHTTPBasic(httpbasic.EndpointOpts{ + IronicInspectorUser: "myUser", + IronicInspectorUserPassword: "myPasswd", + }) + _ = errTest2 + th.AssertEquals(t, "IronicInspectorEndpoint is required", err.Error()) +} From 2d963c5b95edf1f09e90d298c64493b5ec2cfdc8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 24 Jun 2020 08:23:10 -0600 Subject: [PATCH 1105/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb91af777..6df4b5ef38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ 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) BUG FIXES From 1f7127ef4405f21f332bd8aecc4ef48b1d822bec Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Wed, 24 Jun 2020 11:29:32 -0300 Subject: [PATCH 1106/2296] Support http_basic auth_strategy for Ironic (#1983) Ironic introduced a new auth_type `http_basic` that can be used in standalone deployments[1]. The `http_basic` This commit adds the support to use the new authentication type. [1] https://docs.openstack.org/ironic/latest/install/standalone.html Co-authored-by: Iury --- acceptance/clients/clients.go | 12 +++ acceptance/clients/conditions.go | 8 ++ .../baremetal/httpbasic/allocations_test.go | 46 +++++++++ .../openstack/baremetal/httpbasic/doc.go | 12 +++ .../baremetal/httpbasic/nodes_test.go | 98 +++++++++++++++++++ .../baremetal/httpbasic/ports_test.go | 75 ++++++++++++++ openstack/baremetal/httpbasic/doc.go | 18 ++++ openstack/baremetal/httpbasic/requests.go | 45 +++++++++ .../httpbasic/testing/requests_test.go | 36 +++++++ 9 files changed, 350 insertions(+) create mode 100644 acceptance/openstack/baremetal/httpbasic/allocations_test.go create mode 100644 acceptance/openstack/baremetal/httpbasic/doc.go create mode 100644 acceptance/openstack/baremetal/httpbasic/nodes_test.go create mode 100644 acceptance/openstack/baremetal/httpbasic/ports_test.go create mode 100644 openstack/baremetal/httpbasic/doc.go create mode 100644 openstack/baremetal/httpbasic/requests.go create mode 100644 openstack/baremetal/httpbasic/testing/requests_test.go diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go index dedc59d7b9..eecec73903 100644 --- a/acceptance/clients/clients.go +++ b/acceptance/clients/clients.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" + baremetalHTTPBasic "github.com/gophercloud/gophercloud/openstack/baremetal/httpbasic" baremetalNoAuth "github.com/gophercloud/gophercloud/openstack/baremetal/noauth" blockstorageNoAuth "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" ) @@ -285,6 +286,17 @@ func NewBareMetalV1NoAuthClient() (*gophercloud.ServiceClient, error) { }) } +// NewBareMetalV1HTTPBasic returns a *ServiceClient for making calls +// to the OpenStack Bare Metal v1 API. An error will be returned +// if authentication or client creation was not possible. +func NewBareMetalV1HTTPBasic() (*gophercloud.ServiceClient, error) { + return baremetalHTTPBasic.NewBareMetalHTTPBasic(baremetalHTTPBasic.EndpointOpts{ + IronicEndpoint: os.Getenv("IRONIC_ENDPOINT"), + IronicUser: os.Getenv("OS_USERNAME"), + IronicUserPassword: os.Getenv("OS_PASSWORD"), + }) +} + // NewBareMetalIntrospectionV1Client returns a *ServiceClient for making calls // to the OpenStack Bare Metal Introspection v1 API. An error will be returned // if authentication or client creation was not possible. diff --git a/acceptance/clients/conditions.go b/acceptance/clients/conditions.go index e0390ff4fa..b98754edde 100644 --- a/acceptance/clients/conditions.go +++ b/acceptance/clients/conditions.go @@ -74,6 +74,14 @@ func RequireNovaNetwork(t *testing.T) { } } +// RequireIronicHTTPBasic will restric a test to be only run in +// environments that have Ironic using http_basic. +func RequireIronicHTTPBasic(t *testing.T) { + if os.Getenv("IRONIC_ENDPOINT") == "" || os.Getenv("OS_USERNAME") == "" || os.Getenv("OS_PASSWORD") == "" { + t.Skip("this test requires Ironic using http_basic, set OS_USERNAME, OS_PASSWORD and IRONIC_ENDPOINT") + } +} + // 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) { diff --git a/acceptance/openstack/baremetal/httpbasic/allocations_test.go b/acceptance/openstack/baremetal/httpbasic/allocations_test.go new file mode 100644 index 0000000000..7af0b5700b --- /dev/null +++ b/acceptance/openstack/baremetal/httpbasic/allocations_test.go @@ -0,0 +1,46 @@ +// +build acceptance baremetal allocations + +package httpbasic + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestAllocationsCreateDestroy(t *testing.T) { + clients.RequireLong(t) + clients.RequireIronicHTTPBasic(t) + + client, err := clients.NewBareMetalV1HTTPBasic() + th.AssertNoErr(t, err) + + client.Microversion = "1.52" + + allocation, err := v1.CreateAllocation(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteAllocation(t, client, allocation) + + found := false + err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + allocationList, err := allocations.ExtractAllocations(page) + if err != nil { + return false, err + } + + for _, a := range allocationList { + if a.UUID == allocation.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + th.AssertEquals(t, found, true) +} diff --git a/acceptance/openstack/baremetal/httpbasic/doc.go b/acceptance/openstack/baremetal/httpbasic/doc.go new file mode 100644 index 0000000000..b0b6e1dc39 --- /dev/null +++ b/acceptance/openstack/baremetal/httpbasic/doc.go @@ -0,0 +1,12 @@ +package httpbasic + +/* +Acceptance tests for Ironic endpoints with auth_strategy=noauth. Specify +IRONIC_ENDPOINT, OS_USERNAME and OS_PASSWORD environment variables. For example: + + IRONIC_ENDPOINT="http://127.0.0.1:6385/v1" + OS_USERNAME="myUser" + OS_PASSWORD="myPassword" + go test ./acceptance/openstack/baremetal/httpbasic/... + +*/ diff --git a/acceptance/openstack/baremetal/httpbasic/nodes_test.go b/acceptance/openstack/baremetal/httpbasic/nodes_test.go new file mode 100644 index 0000000000..56b87a8af7 --- /dev/null +++ b/acceptance/openstack/baremetal/httpbasic/nodes_test.go @@ -0,0 +1,98 @@ +package httpbasic + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/pagination" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestNodesCreateDestroy(t *testing.T) { + clients.RequireLong(t) + clients.RequireIronicHTTPBasic(t) + + client, err := clients.NewBareMetalV1HTTPBasic() + th.AssertNoErr(t, err) + client.Microversion = "1.50" + + node, err := v1.CreateNode(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + + found := false + err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + nodeList, err := nodes.ExtractNodes(page) + if err != nil { + return false, err + } + + for _, n := range nodeList { + if n.UUID == node.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + + th.AssertEquals(t, found, true) +} + +func TestNodesUpdate(t *testing.T) { + clients.RequireLong(t) + clients.RequireIronicHTTPBasic(t) + + client, err := clients.NewBareMetalV1HTTPBasic() + th.AssertNoErr(t, err) + client.Microversion = "1.50" + + node, err := v1.CreateNode(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + + updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ + nodes.UpdateOperation{ + Op: nodes.ReplaceOp, + Path: "/maintenance", + Value: "true", + }, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.Maintenance, true) +} + +func TestNodesRAIDConfig(t *testing.T) { + clients.RequireLong(t) + clients.RequireIronicHTTPBasic(t) + + client, err := clients.NewBareMetalV1HTTPBasic() + th.AssertNoErr(t, err) + client.Microversion = "1.50" + + node, err := v1.CreateNode(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + + sizeGB := 100 + isTrue := true + + err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ + LogicalDisks: []nodes.LogicalDisk{ + { + SizeGB: &sizeGB, + IsRootVolume: &isTrue, + RAIDLevel: nodes.RAID5, + DiskType: nodes.HDD, + NumberOfPhysicalDisks: 5, + }, + }, + }).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/acceptance/openstack/baremetal/httpbasic/ports_test.go b/acceptance/openstack/baremetal/httpbasic/ports_test.go new file mode 100644 index 0000000000..8c54a549bd --- /dev/null +++ b/acceptance/openstack/baremetal/httpbasic/ports_test.go @@ -0,0 +1,75 @@ +// +build acceptance baremetal ports + +package httpbasic + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" + "github.com/gophercloud/gophercloud/pagination" + + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestPortsCreateDestroy(t *testing.T) { + clients.RequireLong(t) + clients.RequireIronicHTTPBasic(t) + + client, err := clients.NewBareMetalV1HTTPBasic() + th.AssertNoErr(t, err) + client.Microversion = "1.53" + + node, err := v1.CreateFakeNode(t, client) + port, err := v1.CreatePort(t, client, node) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + defer v1.DeletePort(t, client, port) + + found := false + err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + portList, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } + + for _, p := range portList { + if p.UUID == port.UUID { + found = true + return true, nil + } + } + + return false, nil + }) + th.AssertNoErr(t, err) + + th.AssertEquals(t, found, true) +} + +func TestPortsUpdate(t *testing.T) { + clients.RequireLong(t) + clients.RequireIronicHTTPBasic(t) + + client, err := clients.NewBareMetalV1HTTPBasic() + th.AssertNoErr(t, err) + client.Microversion = "1.53" + + node, err := v1.CreateFakeNode(t, client) + port, err := v1.CreatePort(t, client, node) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + defer v1.DeletePort(t, client, port) + + updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ + ports.UpdateOperation{ + Op: ports.ReplaceOp, + Path: "/address", + Value: "aa:bb:cc:dd:ee:ff", + }, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, updated.Address, "aa:bb:cc:dd:ee:ff") +} diff --git a/openstack/baremetal/httpbasic/doc.go b/openstack/baremetal/httpbasic/doc.go new file mode 100644 index 0000000000..d36e9f71cd --- /dev/null +++ b/openstack/baremetal/httpbasic/doc.go @@ -0,0 +1,18 @@ +/* +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.Microversion = "1.50" + nodes.ListDetail(client, nodes.listOpts{}) +*/ +package httpbasic diff --git a/openstack/baremetal/httpbasic/requests.go b/openstack/baremetal/httpbasic/requests.go new file mode 100644 index 0000000000..c88c846470 --- /dev/null +++ b/openstack/baremetal/httpbasic/requests.go @@ -0,0 +1,45 @@ +package httpbasic + +import ( + "encoding/base64" + "fmt" + + "github.com/gophercloud/gophercloud" +) + +// EndpointOpts specifies a "http_basic" Ironic Endpoint +type EndpointOpts struct { + IronicEndpoint string + IronicUser string + IronicUserPassword string +} + +func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc := new(gophercloud.ServiceClient) + if eo.IronicEndpoint == "" { + return nil, fmt.Errorf("IronicEndpoint is required") + } + if eo.IronicUser == "" || eo.IronicUserPassword == "" { + return nil, fmt.Errorf("User and Password are required") + } + + token := []byte(eo.IronicUser + ":" + eo.IronicUserPassword) + encodedToken := base64.StdEncoding.EncodeToString(token) + sc.MoreHeaders = map[string]string{"Authorization": "Basic " + encodedToken} + sc.Endpoint = gophercloud.NormalizeURL(eo.IronicEndpoint) + sc.ProviderClient = client + return sc, nil +} + +// NewBareMetalHTTPBasic creates a ServiceClient that may be used to access a +// "http_basic" bare metal service. +func NewBareMetalHTTPBasic(eo EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(&gophercloud.ProviderClient{}, eo) + if err != nil { + return nil, err + } + + sc.Type = "baremetal" + + return sc, nil +} diff --git a/openstack/baremetal/httpbasic/testing/requests_test.go b/openstack/baremetal/httpbasic/testing/requests_test.go new file mode 100644 index 0000000000..c746f242d6 --- /dev/null +++ b/openstack/baremetal/httpbasic/testing/requests_test.go @@ -0,0 +1,36 @@ +package testing + +import ( + "encoding/base64" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/httpbasic" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestHttpBasic(t *testing.T) { + httpClient, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.EndpointOpts{ + IronicEndpoint: "http://ironic:6385/v1", + IronicUser: "myUser", + IronicUserPassword: "myPasswd", + }) + th.AssertNoErr(t, err) + encToken := base64.StdEncoding.EncodeToString([]byte("myUser:myPasswd")) + headerValue := "Basic " + encToken + + th.AssertEquals(t, headerValue, httpClient.MoreHeaders["Authorization"]) + + errTest1, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.EndpointOpts{ + IronicEndpoint: "http://ironic:6385/v1", + }) + _ = errTest1 + th.AssertEquals(t, "User and Password are required", err.Error()) + + errTest2, err := httpbasic.NewBareMetalHTTPBasic(httpbasic.EndpointOpts{ + IronicUser: "myUser", + IronicUserPassword: "myPasswd", + }) + _ = errTest2 + th.AssertEquals(t, "IronicEndpoint is required", err.Error()) + +} From 9d63f4bd54908544c8ee9e04431f4886e4a4c984 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 24 Jun 2020 08:30:24 -0600 Subject: [PATCH 1107/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6df4b5ef38..8d3f6bbfbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ 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) BUG FIXES From f4f6cc9e28463ff34984d79089c5985b5b8df85c Mon Sep 17 00:00:00 2001 From: Feilong Wang Date: Fri, 26 Jun 2020 02:27:38 +1200 Subject: [PATCH 1108/2296] Support `merge_labels` when creating Magnum cluster (#1985) --- openstack/containerinfra/v1/clusters/requests.go | 1 + openstack/containerinfra/v1/clusters/testing/requests_test.go | 1 + 2 files changed, 2 insertions(+) diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 5fc67d76cb..2cd865b8b9 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -26,6 +26,7 @@ type CreateOpts struct { FloatingIPEnabled *bool `json:"floating_ip_enabled,omitempty"` FixedNetwork string `json:"fixed_network,omitempty"` FixedSubnet string `json:"fixed_subnet,omitempty"` + MergeLabels *bool `json:"merge_labels,omitempty"` } // ToClusterCreateMap constructs a request body from CreateOpts. diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index ee3e2f8913..e0ce2ae951 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -33,6 +33,7 @@ func TestCreateCluster(t *testing.T) { FloatingIPEnabled: gophercloud.Enabled, FixedNetwork: "private_network", FixedSubnet: "private_subnet", + MergeLabels: gophercloud.Enabled, } sc := fake.ServiceClient() From d3d59add3e162db697153b13cb394e13111e13d3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 25 Jun 2020 08:28:37 -0600 Subject: [PATCH 1109/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d3f6bbfbf..7a54dbbb21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ IMPROVEMENTS * 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 From 2921f45de19eb28a5b1960c110d6c6761361c1dc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 25 Jun 2020 08:30:07 -0600 Subject: [PATCH 1110/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a54dbbb21..719aa6ecf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.12.0 (Unreleased) +## 0.13.0 (Unlreleased) + +## 0.12.0 (June 25, 2020) UPGRADE NOTES From 0183d6ad325843c27bd9497b9dca1f0259ffadb8 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Wed, 8 Jul 2020 04:50:27 +1200 Subject: [PATCH 1111/2296] make introspection.RootDiskType int64 rather than int (#1634) (#1988) Fixes tests (and functionality I suspect!) on 32-bit systems. --- 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 e0d21f3d8e..e9abe85328 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -238,7 +238,7 @@ type RootDiskType struct { ByPath string `json:"by_path"` Rotational bool `json:"rotational"` Serial string `json:"serial"` - Size int `json:"size"` + Size int64 `json:"size"` Vendor string `json:"vendor"` Wwn string `json:"wwn"` WwnVendorExtension string `json:"wwn_vendor_extension"` From c8c96f74de7f0c91297732d1a1468bc225a68137 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 7 Jul 2020 10:51:57 -0600 Subject: [PATCH 1112/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 719aa6ecf9..bef6ec9022 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ BUG FIXES * 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) From b9450e780a49a84e0a794a75a4c0e01f610869d4 Mon Sep 17 00:00:00 2001 From: Hamza Zafar Date: Tue, 21 Jul 2020 23:25:22 +0200 Subject: [PATCH 1113/2296] support TERMINATED_HTTPS protocol for listeners (#1992) --- openstack/loadbalancer/v2/listeners/requests.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index a90d726273..f839fa9506 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -10,11 +10,12 @@ type Protocol string // Supported attributes for create/update operations. const ( - ProtocolTCP Protocol = "TCP" - ProtocolUDP Protocol = "UDP" - ProtocolPROXY Protocol = "PROXY" - ProtocolHTTP Protocol = "HTTP" - ProtocolHTTPS Protocol = "HTTPS" + ProtocolTCP Protocol = "TCP" + ProtocolUDP Protocol = "UDP" + ProtocolPROXY Protocol = "PROXY" + ProtocolHTTP Protocol = "HTTP" + ProtocolHTTPS Protocol = "HTTPS" + ProtocolTerminatedHTTPS Protocol = "TERMINATED_HTTPS" ) // ListOptsBuilder allows extensions to add additional parameters to the @@ -85,7 +86,7 @@ type CreateOpts struct { // The load balancer on which to provision this listener. LoadbalancerID string `json:"loadbalancer_id" required:"true"` - // The protocol - can either be TCP, HTTP or HTTPS. + // The protocol - can either be TCP, HTTP, HTTPS or TERMINATED_HTTPS. Protocol Protocol `json:"protocol" required:"true"` // The port on which to listen for client traffic. From bd999d0da882fe8c5b0077b7af2dcc019c1ab458 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 21 Jul 2020 15:26:41 -0600 Subject: [PATCH 1114/2296] Update CHANGELOG.md --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bef6ec9022..7137387eeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ -## 0.13.0 (Unlreleased) +## 0.13.0 (Unreleased) + +IMPROVEMENTS + +* Added `ProtocolTerminatedHTTPS` as a valid listener protocol to `loadbalancer/v2/listeners` [GH-1992](https://github.com/gophercloud/gophercloud/pull/1992) ## 0.12.0 (June 25, 2020) From ffb0421108ba77e952c6af78f20748ca99c1186d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 3 Aug 2020 12:43:10 -0600 Subject: [PATCH 1115/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7137387eeb..d4216d07b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -202,7 +202,7 @@ IMPROVEMENTS * Added `MonitorAddress` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) * Added `MonitorPort` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824) * Changed `Impersonation` to a non-required field in `identity/v3/extensions/trusts.CreateOpts` [GH-1818](https://github.com/gophercloud/gophercloud/pull/1818) -* Added `InsertHeaders` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1835] +* Added `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) From c1b8ad23469833bcfbec3b08a0572d0bef4a341c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 4 Aug 2020 20:51:26 -0600 Subject: [PATCH 1116/2296] Acc Tests: Fix Insert Headers Usage (#2000) * Acc Tests: Fix Insert Headers Usage It is no longer possible to use InsertHeaders on TCP-based listeners. Therefore, a new test using the HTTP protocol has been added. * Increasing timeout of acceptance tests * Acc Tests: Various updates and cleanup --- .zuul.yaml | 1 + .../blockstorage/extensions/extensions.go | 6 +- .../blockstorage/noauth/blockstorage.go | 2 +- .../openstack/blockstorage/noauth/pkg.go | 3 - .../openstack/blockstorage/v1/blockstorage.go | 4 +- .../openstack/blockstorage/v2/blockstorage.go | 4 +- .../openstack/blockstorage/v2/volumes_test.go | 5 +- .../openstack/blockstorage/v3/blockstorage.go | 4 +- .../openstack/blockstorage/v3/volumes_test.go | 5 +- acceptance/openstack/dns/v2/dns.go | 4 +- .../openstack/dns/v2/recordsets_test.go | 2 +- .../openstack/identity/v3/oauth1_test.go | 26 +++- .../openstack/loadbalancer/v2/loadbalancer.go | 118 +++++++++++++++--- .../loadbalancer/v2/loadbalancers_test.go | 109 ++++++++++------ .../networking/v2/extensions/fwaas/fwaas.go | 10 +- .../networking/v2/extensions/layer3/layer3.go | 28 ++--- .../v2/extensions/lbaas_v2/lbaas_v2.go | 35 +++--- .../extensions/lbaas_v2/loadbalancers_test.go | 20 +-- .../openstack/networking/v2/networking.go | 14 +-- .../v2/availabilityzones_test.go | 2 + .../v2/securityservices_test.go | 2 + .../v2/sharenetworks_test.go | 2 + .../openstack/sharedfilesystems/v2/shares.go | 8 +- .../sharedfilesystems/v2/shares_test.go | 32 +++-- .../sharedfilesystems/v2/sharetypes_test.go | 2 + .../sharedfilesystems/v2/snapshots.go | 9 +- .../sharedfilesystems/v2/snapshots_test.go | 2 + acceptance/tools/tools.go | 4 +- openstack/loadbalancer/v2/pools/requests.go | 3 +- 29 files changed, 307 insertions(+), 159 deletions(-) delete mode 100644 acceptance/openstack/blockstorage/noauth/pkg.go diff --git a/.zuul.yaml b/.zuul.yaml index f3c4015e07..67c71a6983 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -12,6 +12,7 @@ description: | Run gophercloud acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml + timeout: 18000 # 5 hours nodeset: ubuntu-bionic - job: diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index 2c66b3679c..cd77d6954a 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -204,7 +204,7 @@ func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID stri return nil, err } - err = WaitForBackupStatus(client, backup.ID, "available", 120) + err = WaitForBackupStatus(client, backup.ID, "available") if err != nil { return nil, err } @@ -234,8 +234,8 @@ func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID stri // WaitForBackupStatus will continually poll a backup, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) error { + return tools.WaitFor(func() (bool, error) { current, err := backups.Get(client, id).Extract() if err != nil { return false, err diff --git a/acceptance/openstack/blockstorage/noauth/blockstorage.go b/acceptance/openstack/blockstorage/noauth/blockstorage.go index d453dac94a..ca133d8d5c 100644 --- a/acceptance/openstack/blockstorage/noauth/blockstorage.go +++ b/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -126,7 +126,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s // Volumes can't be deleted until their snapshots have been, // so block up to 120 seconds for the snapshot to delete. - err = gophercloud.WaitFor(120, func() (bool, error) { + err = tools.WaitFor(func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { return true, nil diff --git a/acceptance/openstack/blockstorage/noauth/pkg.go b/acceptance/openstack/blockstorage/noauth/pkg.go deleted file mode 100644 index 5d4f920cbf..0000000000 --- a/acceptance/openstack/blockstorage/noauth/pkg.go +++ /dev/null @@ -1,3 +0,0 @@ -// The noauth package contains acceptance tests for the Openstack Cinder standalone service. - -package noauth diff --git a/acceptance/openstack/blockstorage/v1/blockstorage.go b/acceptance/openstack/blockstorage/v1/blockstorage.go index c2b35333f5..670eaad50d 100644 --- a/acceptance/openstack/blockstorage/v1/blockstorage.go +++ b/acceptance/openstack/blockstorage/v1/blockstorage.go @@ -104,8 +104,8 @@ func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapsho } // Volumes can't be deleted until their snapshots have been, - // so block up to 120 seconds for the snapshot to delete. - err = gophercloud.WaitFor(120, func() (bool, error) { + // so block until the snapshot has been deleted. + err = tools.WaitFor(func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { return true, nil diff --git a/acceptance/openstack/blockstorage/v2/blockstorage.go b/acceptance/openstack/blockstorage/v2/blockstorage.go index b6b93b4c1a..6170d555ba 100644 --- a/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -139,8 +139,8 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s } // Volumes can't be deleted until their snapshots have been, - // so block up to 120 seconds for the snapshot to delete. - err = gophercloud.WaitFor(120, func() (bool, error) { + // so block until the snapshot is deleted. + err = tools.WaitFor(func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { return true, nil diff --git a/acceptance/openstack/blockstorage/v2/volumes_test.go b/acceptance/openstack/blockstorage/v2/volumes_test.go index 39136bbf00..4399a7a790 100644 --- a/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -5,7 +5,6 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" @@ -103,7 +102,7 @@ func TestVolumesCascadeDelete(t *testing.T) { } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { - err := gophercloud.WaitFor(120, func() (bool, error) { + err := tools.WaitFor(func() (bool, error) { _, err := snapshots.Get(client, sid).Extract() if err != nil { return true, nil @@ -114,7 +113,7 @@ func TestVolumesCascadeDelete(t *testing.T) { t.Logf("Successfully deleted snapshot: %s", sid) } - err = gophercloud.WaitFor(120, func() (bool, error) { + err = tools.WaitFor(func() (bool, error) { _, err := volumes.Get(client, vol.ID).Extract() if err != nil { return true, nil diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index 3eccab04af..b574dd3c51 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -119,8 +119,8 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s } // Volumes can't be deleted until their snapshots have been, - // so block up to 120 seconds for the snapshot to delete. - err = gophercloud.WaitFor(120, func() (bool, error) { + // so block until the snapshoth as been deleted. + err = tools.WaitFor(func() (bool, error) { _, err := snapshots.Get(client, snapshot.ID).Extract() if err != nil { return true, nil diff --git a/acceptance/openstack/blockstorage/v3/volumes_test.go b/acceptance/openstack/blockstorage/v3/volumes_test.go index 195e5c6ff9..3164c1b37c 100644 --- a/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -5,7 +5,6 @@ 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/blockstorage/v3/snapshots" @@ -126,7 +125,7 @@ func TestVolumesCascadeDelete(t *testing.T) { } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { - err := gophercloud.WaitFor(120, func() (bool, error) { + err := tools.WaitFor(func() (bool, error) { _, err := snapshots.Get(client, sid).Extract() if err != nil { return true, nil @@ -137,7 +136,7 @@ func TestVolumesCascadeDelete(t *testing.T) { t.Logf("Successfully deleted snapshot: %s", sid) } - err = gophercloud.WaitFor(120, func() (bool, error) { + err = tools.WaitFor(func() (bool, error) { _, err := volumes.Get(client, vol.ID).Extract() if err != nil { return true, nil diff --git a/acceptance/openstack/dns/v2/dns.go b/acceptance/openstack/dns/v2/dns.go index 18cd157fc1..791703e87b 100644 --- a/acceptance/openstack/dns/v2/dns.go +++ b/acceptance/openstack/dns/v2/dns.go @@ -143,7 +143,7 @@ func DeleteZone(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zon // WaitForRecordSetStatus will poll a record set's status until it either matches // the specified status or the status becomes ERROR. func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.RecordSet, status string) error { - return gophercloud.WaitFor(600, func() (bool, error) { + return tools.WaitFor(func() (bool, error) { current, err := recordsets.Get(client, rs.ZoneID, rs.ID).Extract() if err != nil { return false, err @@ -160,7 +160,7 @@ func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.Re // WaitForZoneStatus will poll a zone's status until it either matches // the specified status or the status becomes ERROR. func WaitForZoneStatus(client *gophercloud.ServiceClient, zone *zones.Zone, status string) error { - return gophercloud.WaitFor(600, func() (bool, error) { + return tools.WaitFor(func() (bool, error) { current, err := zones.Get(client, zone.ID).Extract() if err != nil { return false, err diff --git a/acceptance/openstack/dns/v2/recordsets_test.go b/acceptance/openstack/dns/v2/recordsets_test.go index 683384e1e7..885054c0e3 100644 --- a/acceptance/openstack/dns/v2/recordsets_test.go +++ b/acceptance/openstack/dns/v2/recordsets_test.go @@ -1,4 +1,4 @@ -// +build acceptance dns recordsets +// +build acceptance package v2 diff --git a/acceptance/openstack/identity/v3/oauth1_test.go b/acceptance/openstack/identity/v3/oauth1_test.go index b86b33a193..dbc8359888 100644 --- a/acceptance/openstack/identity/v3/oauth1_test.go +++ b/acceptance/openstack/identity/v3/oauth1_test.go @@ -132,15 +132,19 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer OAuthVerifier: authToken.OAuthVerifier, OAuthSignatureMethod: method, } + accessToken, err := oauth1.CreateAccessToken(client, accessTokenOpts).Extract() th.AssertNoErr(t, err) defer oauth1.RevokeAccessToken(client, user.ID, accessToken.OAuthToken) + tools.PrintResource(t, accessToken) // Get access token getAccessToken, err := oauth1.GetAccessToken(client, user.ID, accessToken.OAuthToken).Extract() th.AssertNoErr(t, err) + tools.PrintResource(t, getAccessToken) + th.AssertEquals(t, getAccessToken.ID, accessToken.OAuthToken) th.AssertEquals(t, getAccessToken.ConsumerID, consumer.ID) th.AssertEquals(t, getAccessToken.AuthorizingUserID, user.ID) @@ -149,20 +153,38 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer // List access tokens accessTokensPages, err := oauth1.ListAccessTokens(client, user.ID).AllPages() th.AssertNoErr(t, err) + accessTokens, err := oauth1.ExtractAccessTokens(accessTokensPages) th.AssertNoErr(t, err) + tools.PrintResource(t, accessTokens) th.AssertDeepEquals(t, accessTokens[0], *getAccessToken) // List access token roles accessTokenRolesPages, err := oauth1.ListAccessTokenRoles(client, user.ID, accessToken.OAuthToken).AllPages() th.AssertNoErr(t, err) + accessTokenRoles, err := oauth1.ExtractAccessTokenRoles(accessTokenRolesPages) th.AssertNoErr(t, err) + tools.PrintResource(t, accessTokenRoles) - th.AssertEquals(t, accessTokenRoles[0].ID, roles[0].ID) + + var found bool + for _, atr := range accessTokenRoles { + if atr.ID == roles[0].ID { + found = true + } + } + th.AssertEquals(t, found, true) + if len(accessTokenRoles) > 1 { - th.AssertEquals(t, accessTokenRoles[1].Name, roles[1].Name) + found = false + for _, atr := range accessTokenRoles { + if atr.ID == roles[1].ID { + found = true + } + } + th.AssertEquals(t, found, true) } // Get access token role diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 8a08d5232a..4ab7bc8b82 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -15,9 +15,6 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -const loadbalancerActiveTimeoutSeconds = 600 -const loadbalancerDeleteTimeoutSeconds = 600 - // CreateListener will create a listener for a given load balancer on a random // port with a random name. An error will be returned if the listener could not // be created. @@ -43,7 +40,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal t.Logf("Successfully created listener %s", listenerName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -56,6 +53,50 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal return listener, nil } +// CreateListenerHTTP will create an HTTP-based listener for a given load +// 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) { + listenerName := tools.RandomString("TESTACCT-", 8) + listenerDescription := tools.RandomString("TESTACCT-DESC-", 8) + listenerPort := tools.RandomInt(1, 100) + + t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) + + headers := map[string]string{ + "X-Forwarded-For": "true", + } + + createOpts := listeners.CreateOpts{ + Name: listenerName, + Description: listenerDescription, + LoadbalancerID: lb.ID, + InsertHeaders: headers, + Protocol: listeners.ProtocolHTTP, + ProtocolPort: listenerPort, + } + + listener, err := listeners.Create(client, createOpts).Extract() + if err != nil { + return listener, err + } + + t.Logf("Successfully created listener %s", listenerName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { + return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) + } + + th.AssertEquals(t, listener.Name, listenerName) + th.AssertEquals(t, listener.Description, listenerDescription) + th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID) + th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolHTTP)) + th.AssertEquals(t, listener.ProtocolPort, listenerPort) + th.AssertDeepEquals(t, listener.InsertHeaders, headers) + + return listener, nil +} + // 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) { @@ -82,7 +123,7 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID) t.Logf("Waiting for loadbalancer %s to become active", lbName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return lb, err } @@ -130,7 +171,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan t.Logf("Successfully created member %s", memberName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return member, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -163,7 +204,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala t.Logf("Successfully created monitor: %s", monitorName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -201,7 +242,7 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance t.Logf("Successfully created pool %s", poolName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -214,6 +255,43 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance return pool, nil } +// CreatePoolHTTP will create an HTTP-based pool with a random name with a +// specified listener and loadbalancer. An error will be returned if the pool +// could not be created. +func CreatePoolHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) { + poolName := tools.RandomString("TESTACCT-", 8) + poolDescription := tools.RandomString("TESTACCT-DESC-", 8) + + t.Logf("Attempting to create pool %s", poolName) + + createOpts := pools.CreateOpts{ + Name: poolName, + Description: poolDescription, + Protocol: pools.ProtocolHTTP, + LoadbalancerID: lb.ID, + LBMethod: pools.LBMethodLeastConnections, + } + + pool, err := pools.Create(client, createOpts).Extract() + if err != nil { + return pool, err + } + + t.Logf("Successfully created pool %s", poolName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { + return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) + } + + th.AssertEquals(t, pool.Name, poolName) + th.AssertEquals(t, pool.Description, poolDescription) + th.AssertEquals(t, pool.Protocol, string(pools.ProtocolHTTP)) + th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID) + th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections)) + + return pool, nil +} + // CreateL7Policy will create a l7 policy with a random name with a specified listener // and loadbalancer. An error will be returned if the l7 policy could not be // created. @@ -238,7 +316,7 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l t.Logf("Successfully created l7 policy %s", policyName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -268,7 +346,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri t.Logf("Successfully created l7 rule for policy %s", policyID) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -291,7 +369,7 @@ func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, polic } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -310,7 +388,7 @@ func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyI } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -329,7 +407,7 @@ func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, liste } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -348,7 +426,7 @@ func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -373,7 +451,7 @@ func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID st t.Logf("Waiting for loadbalancer %s to delete", lbID) - if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil { t.Fatalf("Loadbalancer did not delete in time: %s", err) } @@ -396,7 +474,7 @@ func CascadeDeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, t.Logf("Waiting for loadbalancer %s to cascade delete", lbID) - if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil { t.Fatalf("Loadbalancer did not delete in time.") } @@ -415,7 +493,7 @@ func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monito } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -433,7 +511,7 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -441,8 +519,8 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st } // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. -func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error { + return tools.WaitFor(func() (bool, error) { current, err := loadbalancers.Get(client, lbID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 2b63fed177..2984361ecf 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -108,6 +108,62 @@ func TestLoadbalancersListByTags(t *testing.T) { th.AssertEquals(t, 0, len(allLoadbalancers)) } +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") + + netClient, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + lbClient, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + network, err := networking.CreateNetwork(t, netClient) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, netClient, network.ID) + + subnet, err := networking.CreateSubnet(t, netClient, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, netClient, subnet.ID) + + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, nil) + th.AssertNoErr(t, err) + defer DeleteLoadBalancer(t, lbClient, lb.ID) + + // Listener + listener, err := CreateListenerHTTP(t, lbClient, lb) + th.AssertNoErr(t, err) + defer DeleteListener(t, lbClient, lb.ID, listener.ID) + + // L7 policy + policy, err := CreateL7Policy(t, lbClient, listener, lb) + th.AssertNoErr(t, err) + defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) + + // L7 rule + rule, err := CreateL7Rule(t, lbClient, policy.ID, lb) + th.AssertNoErr(t, err) + defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) + + // Pool + pool, err := CreatePoolHTTP(t, lbClient, lb) + th.AssertNoErr(t, err) + defer DeletePool(t, lbClient, lb.ID, pool.ID) + + // Member + 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) + + monitor, err := CreateMonitor(t, lbClient, lb, pool) + th.AssertNoErr(t, err) + defer DeleteMonitor(t, lbClient, lb.ID, monitor.ID) +} + func TestLoadbalancersCRUD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") @@ -142,7 +198,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = loadbalancers.Update(lbClient, lb.ID, updateLoadBalancerOpts).Extract() th.AssertNoErr(t, err) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -168,18 +224,14 @@ func TestLoadbalancersCRUD(t *testing.T) { listenerName := "" listenerDescription := "" - listenerHeaders := map[string]string{ - "X-Forwarded-For": "true", - } updateListenerOpts := listeners.UpdateOpts{ - Name: &listenerName, - Description: &listenerDescription, - InsertHeaders: &listenerHeaders, + Name: &listenerName, + Description: &listenerDescription, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -190,7 +242,6 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertEquals(t, newListener.Name, listenerName) th.AssertEquals(t, newListener.Description, listenerDescription) - th.AssertDeepEquals(t, newListener.InsertHeaders, listenerHeaders) listenerStats, err := listeners.GetStats(lbClient, listener.ID).Extract() th.AssertNoErr(t, err) @@ -209,7 +260,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -241,7 +292,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = l7policies.UpdateRule(lbClient, policy.ID, rule.ID, updateL7ruleOpts).Extract() th.AssertNoErr(t, err) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -264,7 +315,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() th.AssertNoErr(t, err) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -285,7 +336,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -303,16 +354,14 @@ func TestLoadbalancersCRUD(t *testing.T) { defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) - // Update listener's default pool ID and remove headers - listenerHeaders = map[string]string{} + // Update listener's default pool ID. updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &pool.ID, - InsertHeaders: &listenerHeaders, } _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -322,7 +371,6 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newListener) th.AssertEquals(t, newListener.DefaultPoolID, pool.ID) - th.AssertDeepEquals(t, newListener.InsertHeaders, listenerHeaders) // Remove listener's default pool ID emptyPoolID := "" @@ -332,7 +380,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -357,7 +405,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -378,7 +426,7 @@ func TestLoadbalancersCRUD(t *testing.T) { t.Fatalf("Unable to batch update members") } - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -387,15 +435,6 @@ func TestLoadbalancersCRUD(t *testing.T) { tools.PrintResource(t, newMember) - // delete members from the pool - if err = pools.BatchUpdateMembers(lbClient, pool.ID, []pools.BatchUpdateMemberOpts{}).ExtractErr(); err != nil { - t.Fatalf("Unable to delete members") - } - - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - pool, err = pools.Get(lbClient, pool.ID).Extract() th.AssertNoErr(t, err) @@ -417,7 +456,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) - if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -477,7 +516,7 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -497,7 +536,7 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -517,7 +556,7 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -539,7 +578,7 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index 66065bd2c8..f6f1f88388 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -36,7 +36,7 @@ func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID st } t.Logf("Waiting for firewall to become active.") - if err := WaitForFirewallState(client, firewall.ID, "ACTIVE", 60); err != nil { + if err := WaitForFirewallState(client, firewall.ID, "ACTIVE"); err != nil { return firewall, err } @@ -74,7 +74,7 @@ func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, pol } t.Logf("Waiting for firewall to become active.") - if err := WaitForFirewallState(client, firewall.ID, "ACTIVE", 60); err != nil { + if err := WaitForFirewallState(client, firewall.ID, "ACTIVE"); err != nil { return firewall, err } @@ -168,7 +168,7 @@ func DeleteFirewall(t *testing.T, client *gophercloud.ServiceClient, firewallID } t.Logf("Waiting for firewall to delete.") - if err := WaitForFirewallState(client, firewallID, "DELETED", 60); err != nil { + if err := WaitForFirewallState(client, firewallID, "DELETED"); err != nil { t.Logf("Unable to delete firewall: %s", firewallID) } @@ -204,8 +204,8 @@ func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) } // WaitForFirewallState will wait until a firewall reaches a given state. -func WaitForFirewallState(client *gophercloud.ServiceClient, firewallID, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForFirewallState(client *gophercloud.ServiceClient, firewallID, status string) error { + return tools.WaitFor(func() (bool, error) { current, err := firewalls.Get(client, firewallID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { diff --git a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 996df6cfeb..6c1cdca080 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -138,7 +138,7 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou return router, err } - if err := WaitForRouterToCreate(client, router.ID, 60); err != nil { + if err := WaitForRouterToCreate(client, router.ID); err != nil { return router, err } @@ -170,7 +170,7 @@ func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID str return router, err } - if err := WaitForRouterToCreate(client, router.ID, 60); err != nil { + if err := WaitForRouterToCreate(client, router.ID); err != nil { return router, err } @@ -196,7 +196,7 @@ func CreateRouterInterface(t *testing.T, client *gophercloud.ServiceClient, port return iface, err } - if err := WaitForRouterInterfaceToAttach(client, portID, 60); err != nil { + if err := WaitForRouterInterfaceToAttach(client, portID); err != nil { return iface, err } @@ -218,7 +218,7 @@ func CreateRouterInterfaceOnSubnet(t *testing.T, client *gophercloud.ServiceClie return iface, err } - if err := WaitForRouterInterfaceToAttach(client, iface.PortID, 60); err != nil { + if err := WaitForRouterInterfaceToAttach(client, iface.PortID); err != nil { return iface, err } @@ -236,7 +236,7 @@ func DeleteRouter(t *testing.T, client *gophercloud.ServiceClient, routerID stri t.Fatalf("Error deleting router: %v", err) } - if err := WaitForRouterToDelete(client, routerID, 60); err != nil { + if err := WaitForRouterToDelete(client, routerID); err != nil { t.Fatalf("Error waiting for router to delete: %v", err) } @@ -258,7 +258,7 @@ func DeleteRouterInterface(t *testing.T, client *gophercloud.ServiceClient, port t.Fatalf("Failed to detach port %s from router %s", portID, routerID) } - if err := WaitForRouterInterfaceToDetach(client, portID, 60); err != nil { + if err := WaitForRouterInterfaceToDetach(client, portID); err != nil { t.Fatalf("Failed to wait for port %s to detach from router %s", portID, routerID) } @@ -279,8 +279,8 @@ func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingI t.Logf("Deleted floating IP: %s", floatingIPID) } -func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string) error { + return tools.WaitFor(func() (bool, error) { r, err := routers.Get(client, routerID).Extract() if err != nil { return false, err @@ -294,8 +294,8 @@ func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string, s }) } -func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string) error { + return tools.WaitFor(func() (bool, error) { _, err := routers.Get(client, routerID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { @@ -309,8 +309,8 @@ func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string, s }) } -func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInterfaceID string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInterfaceID string) error { + return tools.WaitFor(func() (bool, error) { r, err := ports.Get(client, routerInterfaceID).Extract() if err != nil { return false, err @@ -324,8 +324,8 @@ func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInt }) } -func WaitForRouterInterfaceToDetach(client *gophercloud.ServiceClient, routerInterfaceID string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForRouterInterfaceToDetach(client *gophercloud.ServiceClient, routerInterfaceID string) error { + return tools.WaitFor(func() (bool, error) { r, err := ports.Get(client, routerInterfaceID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { diff --git a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index db84121104..13fddaf803 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -15,9 +15,6 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -const loadbalancerActiveTimeoutSeconds = 600 -const loadbalancerDeleteTimeoutSeconds = 600 - // CreateListener will create a listener for a given load balancer on a random // port with a random name. An error will be returned if the listener could not // be created. @@ -43,7 +40,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal t.Logf("Successfully created listener %s", listenerName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -79,7 +76,7 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID) t.Logf("Waiting for loadbalancer %s to become active", lbName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return lb, err } @@ -123,7 +120,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan t.Logf("Successfully created member %s", memberName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return member, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -155,7 +152,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala t.Logf("Successfully created monitor: %s", monitorName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -189,7 +186,7 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance t.Logf("Successfully created pool %s", poolName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -226,7 +223,7 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l t.Logf("Successfully created l7 policy %s", policyName) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -256,7 +253,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri t.Logf("Successfully created l7 rule for policy %s", policyID) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -279,7 +276,7 @@ func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, polic } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -298,7 +295,7 @@ func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyI } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -317,7 +314,7 @@ func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, liste } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -336,7 +333,7 @@ func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -357,7 +354,7 @@ func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID st t.Logf("Waiting for loadbalancer %s to delete", lbID) - if err := WaitForLoadBalancerState(client, lbID, "DELETED", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil { t.Fatalf("Loadbalancer did not delete in time: %s", err) } @@ -376,7 +373,7 @@ func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monito } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -394,7 +391,7 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st } } - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) } @@ -402,8 +399,8 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st } // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. -func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error { + return tools.WaitFor(func() (bool, error) { current, err := loadbalancers.Get(client, lbID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { 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 49184277de..c17058d597 100644 --- a/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -54,7 +54,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = loadbalancers.Update(client, lb.ID, updateLoadBalancerOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -87,7 +87,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -111,7 +111,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -144,7 +144,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = l7policies.UpdateRule(client, policy.ID, rule.ID, updateL7ruleOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -167,7 +167,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = pools.Update(client, pool.ID, updatePoolOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -188,7 +188,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -213,7 +213,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -232,7 +232,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -257,7 +257,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = pools.UpdateMember(client, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } @@ -282,7 +282,7 @@ func TestLoadbalancersCRUD(t *testing.T) { _, err = monitors.Update(client, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE", loadbalancerActiveTimeoutSeconds); err != nil { + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index 2540684c68..f0c56fcaad 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -96,7 +96,7 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subn return port, err } - if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + if err := WaitForPortToCreate(client, port.ID); err != nil { return port, err } @@ -134,7 +134,7 @@ func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClie return port, err } - if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + if err := WaitForPortToCreate(client, port.ID); err != nil { return port, err } @@ -175,7 +175,7 @@ func CreatePortWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClie return port, err } - if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + if err := WaitForPortToCreate(client, port.ID); err != nil { return port, err } @@ -221,7 +221,7 @@ func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient return nil, err } - if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + if err := WaitForPortToCreate(client, port.ID); err != nil { return nil, err } @@ -256,7 +256,7 @@ func CreatePortWithMultipleFixedIPs(t *testing.T, client *gophercloud.ServiceCli return port, err } - if err := WaitForPortToCreate(client, port.ID, 60); err != nil { + if err := WaitForPortToCreate(client, port.ID); err != nil { return port, err } @@ -514,8 +514,8 @@ func DeleteSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID stri t.Logf("Deleted subnet: %s", subnetID) } -func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string) error { + return tools.WaitFor(func() (bool, error) { p, err := ports.Get(client, portID).Extract() if err != nil { return false, err diff --git a/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go b/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go index 8841160a24..2f2b80886c 100644 --- a/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go @@ -1,3 +1,5 @@ +// +build acceptance + package v2 import ( diff --git a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index 34b8a79e79..af3e537530 100644 --- a/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -1,3 +1,5 @@ +// +build acceptance + package v2 import ( diff --git a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index 16b2bfbc9e..02d2940c55 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -1,3 +1,5 @@ +// +build acceptance + package v2 import ( diff --git a/acceptance/openstack/sharedfilesystems/v2/shares.go b/acceptance/openstack/sharedfilesystems/v2/shares.go index ea4639b13c..f5d4141005 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -35,7 +35,7 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient) (*shares.Share return nil, err } - err = waitForStatus(t, client, share.ID, "available", 600) + 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 +91,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", 600) + 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,8 +129,8 @@ func PrintMessages(t *testing.T, c *gophercloud.ServiceClient, id string) error return nil } -func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string, secs int) error { - err := gophercloud.WaitFor(secs, func() (bool, error) { +func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) error { + err := tools.WaitFor(func() (bool, error) { current, err := shares.Get(c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 8833c25b2f..c2b3376c33 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -1,3 +1,5 @@ +// +build acceptance + package v2 import ( @@ -48,7 +50,7 @@ func TestShareExportLocations(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -239,7 +241,7 @@ func TestExtendAndShrink(t *testing.T) { } // We need to wait till the Extend operation is done - err = waitForStatus(t, client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -253,7 +255,7 @@ func TestExtendAndShrink(t *testing.T) { } // We need to wait till the Shrink operation is done - err = waitForStatus(t, client, share.ID, "available", 300) + err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -341,7 +343,7 @@ func TestRevert(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -350,10 +352,9 @@ func TestRevert(t *testing.T) { if err != nil { t.Fatalf("Unable to create a snapshot: %v", err) } - defer DeleteSnapshot(t, client, snapshot) - err = waitForSnapshotStatus(t, client, snapshot.ID, "available", 120) + err = waitForSnapshotStatus(t, client, snapshot.ID, "available") if err != nil { t.Fatalf("Snapshot status error: %v", err) } @@ -367,11 +368,16 @@ func TestRevert(t *testing.T) { } // We need to wait till the Extend operation is done - err = waitForStatus(t, client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } + err = waitForSnapshotStatus(t, client, snapshot.ID, "available") + if err != nil { + t.Fatalf("Snapshot status error: %v", err) + } + t.Logf("Share %s successfuly reverted", share.ID) } @@ -392,7 +398,7 @@ func TestResetStatus(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -406,7 +412,7 @@ func TestResetStatus(t *testing.T) { } // We need to wait till the Extend operation is done - err = waitForStatus(t, client, share.ID, "error", 120) + err = waitForStatus(t, client, share.ID, "error") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -431,7 +437,7 @@ func TestForceDelete(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -441,7 +447,7 @@ func TestForceDelete(t *testing.T) { t.Fatalf("Unable to force delete a share: %v", err) } - err = waitForStatus(t, client, share.ID, "deleted", 120) + err = waitForStatus(t, client, share.ID, "deleted") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -467,7 +473,7 @@ func TestUnmanage(t *testing.T) { defer DeleteShare(t, client, share) - err = waitForStatus(t, client, share.ID, "available", 120) + err = waitForStatus(t, client, share.ID, "available") if err != nil { t.Fatalf("Share status error: %v", err) } @@ -477,7 +483,7 @@ func TestUnmanage(t *testing.T) { t.Fatalf("Unable to unmanage a share: %v", err) } - err = waitForStatus(t, client, share.ID, "deleted", 120) + err = waitForStatus(t, client, share.ID, "deleted") if err != nil { t.Fatalf("Share status error: %v", err) } diff --git a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index e5cb2ac82a..470d15bbcf 100644 --- a/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -1,3 +1,5 @@ +// +build acceptance + package v2 import ( diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots.go b/acceptance/openstack/sharedfilesystems/v2/snapshots.go index 852e8844c1..62e607d229 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots.go +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" ) @@ -28,7 +29,7 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, shareID str return nil, err } - err = waitForSnapshotStatus(t, client, snapshot.ID, "available", 600) + err = waitForSnapshotStatus(t, client, snapshot.ID, "available") if err != nil { t.Logf("Failed to get %s snapshot status", snapshot.ID) return snapshot, err @@ -56,7 +57,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s t.Errorf("Unable to delete snapshot %s: %v", snapshot.ID, err) } - err = waitForSnapshotStatus(t, client, snapshot.ID, "deleted", 600) + err = waitForSnapshotStatus(t, client, snapshot.ID, "deleted") if err != nil { t.Errorf("Failed to wait for 'deleted' status for %s snapshot: %v", snapshot.ID, err) } else { @@ -64,8 +65,8 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s } } -func waitForSnapshotStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string, secs int) error { - err := gophercloud.WaitFor(secs, func() (bool, error) { +func waitForSnapshotStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) error { + err := tools.WaitFor(func() (bool, error) { current, err := snapshots.Get(c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { diff --git a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index 5f4313b0b7..69d44809f0 100644 --- a/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -1,3 +1,5 @@ +// +build acceptance + package v2 import ( diff --git a/acceptance/tools/tools.go b/acceptance/tools/tools.go index 08e2e86f1d..30b821dd07 100644 --- a/acceptance/tools/tools.go +++ b/acceptance/tools/tools.go @@ -15,7 +15,7 @@ var ErrTimeout = errors.New("Timed out") // WaitFor uses WaitForTimeout to poll a predicate function once per second to // wait for a certain state to arrive, with a default timeout of 300 seconds. func WaitFor(predicate func() (bool, error)) error { - return WaitForTimeout(predicate, 300*time.Second) + return WaitForTimeout(predicate, 600*time.Second) } // WaitForTimeout polls a predicate function once per second to wait for a @@ -23,7 +23,7 @@ func WaitFor(predicate func() (bool, error)) error { func WaitForTimeout(predicate func() (bool, error), timeout time.Duration) error { startTime := time.Now() for time.Since(startTime) < timeout { - time.Sleep(1 * time.Second) + time.Sleep(2 * time.Second) satisfied, err := predicate() if err != nil { diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index c522cb41d1..5a4c7750a3 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -425,8 +425,7 @@ func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []Batc return } -// DisassociateMember will remove and disassociate a Member from a particular -// Pool. +// DeleteMember will remove and disassociate a Member from a particular Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { resp, err := c.Delete(memberResourceURL(c, poolID, memberID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) From e4a74d04c186cb4a04c1b7cb08479e9dd8527f84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Va=C5=A1ko?= Date: Wed, 5 Aug 2020 16:51:16 +0200 Subject: [PATCH 1117/2296] Fix/create temp url escaping (#1994) * Remove container and object escaping from CreateTempURL - https://github.com/gophercloud/gophercloud/issues/1993 - URL MUST NOT be encoded prior to hashing - From Swift docs - Do not URL-encode the path when you generate the HMAC-SHA1 signature Signed-off-by: Ondrej Vasko * Implement unit test for CreateTempURL - This test should generate URL with consistent signature and expiry and then be asserted Signed-off-by: Ondrej Vasko * refactoring: Statically define HTTP port in test Signed-off-by: Ondrej Vasko * Allow stubbing of time on package level for object-storage This gives enough flexibility to stub time and perform unit tests Signed-off-by: Ondrej Vasko * Stub time to static value in test for function CreateTempURL Signed-off-by: Ondrej Vasko * Use complex object name with pseudo hierarchical path Signed-off-by: Ondrej Vasko * Update expected test results in accounts where TempUrlKey secret was added Signed-off-by: Ondrej Vasko * Use different persistent port as in other tests to avoid collisions Signed-off-by: Ondrej Vasko * Reset clock implementation in stubbed clock Signed-off-by: Ondrej Vasko * Add acceptance test for CreateTempURL Signed-off-by: Ondrej Vasko * Close response body manually rather than deferred Signed-off-by: Ondrej Vasko * Return byte array from bytes buffer in comparison Signed-off-by: Ondrej Vasko * Allow testing of CreateTempURL by passing time.Time as opts - This way is less hacky than packaging time Signed-off-by: Ondrej Vasko * Use assert deep equals to compare complex objects such as byte arrays Signed-off-by: Ondrej Vasko --- .../objectstorage/v1/objects_test.go | 21 +++++++++++++ .../v1/accounts/testing/fixtures.go | 1 + .../v1/accounts/testing/requests_test.go | 3 +- .../objectstorage/v1/objects/requests.go | 16 ++++++++-- .../v1/objects/testing/requests_test.go | 30 +++++++++++++++++++ 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index 841e670a31..f3f77d36c1 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -4,6 +4,8 @@ package v1 import ( "bytes" + "io/ioutil" + "net/http" "strings" "testing" @@ -82,6 +84,25 @@ func TestObjects(t *testing.T) { } th.AssertEquals(t, len(ois), len(oNames)) + // Create temporary URL, download its contents and compare with what was originally created. + // Downloading the URL validates it (this cannot be done in unit tests). + objURLs := make([]string, numObjects) + for i := 0; i < numObjects; i++ { + objURLs[i], err = objects.CreateTempURL(client, cName, oNames[i], objects.CreateTempURLOpts{ + Method: http.MethodGet, + TTL: 180, + }) + th.AssertNoErr(t, err) + + resp, err := http.Get(objURLs[i]) + th.AssertNoErr(t, err) + + body, err := ioutil.ReadAll(resp.Body) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, oContents[i].Bytes(), body) + resp.Body.Close() + } + // Copy the contents of one object to another. copyOpts := objects.CopyOpts{ Destination: cName + "/" + oNames[1], diff --git a/openstack/objectstorage/v1/accounts/testing/fixtures.go b/openstack/objectstorage/v1/accounts/testing/fixtures.go index f7254cc3b7..0a2ba8cf11 100644 --- a/openstack/objectstorage/v1/accounts/testing/fixtures.go +++ b/openstack/objectstorage/v1/accounts/testing/fixtures.go @@ -21,6 +21,7 @@ func HandleGetAccountSuccessfully(t *testing.T) { w.Header().Set("X-Account-Bytes-Used", "14") w.Header().Set("X-Account-Meta-Subject", "books") w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 UTC") + w.Header().Set("X-Account-Meta-Temp-URL-Key", "testsecret") w.WriteHeader(http.StatusNoContent) }) diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go index c7e1695052..47cb4e9e1d 100644 --- a/openstack/objectstorage/v1/accounts/testing/requests_test.go +++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go @@ -31,7 +31,7 @@ func TestGetAccount(t *testing.T) { defer th.TeardownHTTP() HandleGetAccountSuccessfully(t) - expectedMetadata := map[string]string{"Subject": "books", "Quota-Bytes": "42"} + expectedMetadata := map[string]string{"Subject": "books", "Quota-Bytes": "42", "Temp-Url-Key": "testsecret"} res := accounts.Get(fake.ServiceClient(), &accounts.GetOpts{}) th.AssertNoErr(t, res.Err) actualMetadata, _ := res.ExtractMetadata() @@ -46,6 +46,7 @@ func TestGetAccount(t *testing.T) { ObjectCount: 5, BytesUsed: 14, Date: time.Date(2014, time.January, 17, 16, 9, 56, 0, time.UTC), + TempURLKey: "testsecret", } actual, err := res.Extract() th.AssertNoErr(t, err) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 61e4afeff7..e5aeecbf57 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -454,6 +454,9 @@ type CreateTempURLOpts struct { // the object path is used in the hash, the object URL needs to be parsed. If // empty, the default OpenStack URL split point will be used ("/v1/"). Split string + + // Timestamp is a timestamp to calculate Temp URL signature. Optional. + Timestamp time.Time } // CreateTempURL is a function for creating a temporary URL for an object. It @@ -463,8 +466,17 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin if opts.Split == "" { opts.Split = "/v1/" } + + // 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 + } + duration := time.Duration(opts.TTL) * time.Second - expiry := time.Now().Add(duration).Unix() + expiry := date.Add(duration).Unix() getHeader, err := containers.Get(c, url.QueryEscape(containerName), nil).Extract() if err != nil { return "", err @@ -479,7 +491,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin tempURLKey = getHeader.TempURLKey } secretKey := []byte(tempURLKey) - url := getURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) + 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/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 97c789efaf..ca39fff05d 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -6,10 +6,13 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "strings" "testing" "time" + accountTesting "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/testing" + containerTesting "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/testing" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -323,3 +326,30 @@ func TestObjectCreateParamsWithSeek(t *testing.T) { _, ok = headers["ETag"] th.AssertEquals(t, true, ok) } + +func TestCreateTempURL(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/" + tempURL, err := objects.CreateTempURL(client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ + Method: http.MethodGet, + TTL: 60, + Timestamp: time.Date(2020, 07, 01, 01, 12, 00, 00, time.UTC), + }) + + sig := "89be454a9c7e2e9f3f50a8441815e0b5801cba5b" + expiry := "1593565980" + expectedURL := fmt.Sprintf("http://127.0.0.1:%v/v1/testContainer/testObject/testFile.txt?temp_url_sig=%v&temp_url_expires=%v", port, sig, expiry) + + th.AssertNoErr(t, err) + th.AssertEquals(t, expectedURL, tempURL) +} From 51f8fa152459ae60d3b348023ad79f850db3a931 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 5 Aug 2020 08:53:17 -0600 Subject: [PATCH 1118/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4216d07b8..de0413d4e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ 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) + +BUG FIXES + +* Fixed URL escaping in `objectstorage/v1/objects.CreateTempURL` [GH-1994](https://github.com/gophercloud/gophercloud/pull/1994) ## 0.12.0 (June 25, 2020) From 597f138587e9b2458ea60a5563979459774a6dfa Mon Sep 17 00:00:00 2001 From: Andrey Kaipov <9457739+andreykaipov@users.noreply.github.com> Date: Fri, 14 Aug 2020 10:22:28 -0400 Subject: [PATCH 1119/2296] Remove unused ServiceClient from servers.CreateOpts (#2004) Co-authored-by: Andrey Kaipov --- openstack/compute/v2/servers/requests.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index db1c01c1f2..4c594e2ef2 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -199,10 +199,6 @@ type CreateOpts struct { // Max specifies Maximum number of servers to launch. Max int `json:"max_count,omitempty"` - // ServiceClient will allow calls to be made to retrieve an image or - // flavor ID by name. - ServiceClient *gophercloud.ServiceClient `json:"-"` - // Tags allows a server to be tagged with single-word metadata. // Requires microversion 2.52 or later. Tags []string `json:"tags,omitempty"` @@ -211,7 +207,6 @@ type CreateOpts struct { // ToServerCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { - opts.ServiceClient = nil b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -447,10 +442,6 @@ type RebuildOpts struct { // Personality [optional] includes files to inject into the server at launch. // Rebuild will base64-encode file contents for you. Personality Personality `json:"personality,omitempty"` - - // ServiceClient will allow calls to be made to retrieve an image or - // flavor ID by name. - ServiceClient *gophercloud.ServiceClient `json:"-"` } // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON From 50e3548609afe5351c1b97a8184d8c70221584c5 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 14 Aug 2020 08:32:53 -0600 Subject: [PATCH 1120/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de0413d4e1..b435fd3aa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ IMPROVEMENTS 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) ## 0.12.0 (June 25, 2020) From 362eb785d6179f9409043690e8e332c2c280aa78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Ga=C5=82uszka?= Date: Fri, 21 Aug 2020 16:37:28 +0200 Subject: [PATCH 1121/2296] chore: Update travis test matrix and YAML package (#2006) --- .travis.yml | 4 ++-- go.mod | 4 +++- go.sum | 9 +-------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 31f80f8dbb..a6eb99c4a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,9 @@ install: - GO111MODULE=off go get github.com/mattn/goveralls - GO111MODULE=off go get golang.org/x/tools/cmd/goimports go: -- "1.11" -- "1.12" - "1.13" +- "1.14" +- "1.15" - "tip" env: global: diff --git a/go.mod b/go.mod index 46e16507dc..cf6aabe31c 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module github.com/gophercloud/gophercloud +go 1.13 + require ( golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect @@ -9,5 +11,5 @@ require ( golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 // indirect golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.2.7 + gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index 9a0b94de1f..a98dc550de 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,10 @@ -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU= golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -17,10 +14,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 77b238f211307ecbb597156656f43aae55b94307 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 25 Aug 2020 15:19:16 -0600 Subject: [PATCH 1122/2296] Updating Zuul jobs (#2009) --- .zuul.yaml | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index 67c71a6983..5da8982915 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -23,6 +23,24 @@ run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml nodeset: ubuntu-bionic +- job: + name: gophercloud-acceptance-test-ussuri + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on ussuri branch + vars: + global_env: + OS_BRANCH: stable/ussuri + +- job: + name: gophercloud-acceptance-test-train + parent: gophercloud-acceptance-test + description: | + Run gophercloud acceptance test on train branch + vars: + global_env: + OS_BRANCH: stable/train + - job: name: gophercloud-acceptance-test-stein parent: gophercloud-acceptance-test @@ -37,7 +55,6 @@ parent: gophercloud-acceptance-test description: | Run gophercloud acceptance test on rocky branch - nodeset: ubuntu-xenial vars: global_env: OS_BRANCH: stable/rocky @@ -52,6 +69,8 @@ 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 @@ -72,6 +91,8 @@ 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 @@ -107,3 +128,9 @@ recheck-stein: jobs: - gophercloud-acceptance-test-stein + recheck-train: + jobs: + - gophercloud-acceptance-test-train + recheck-ussuri: + jobs: + - gophercloud-acceptance-test-ussuri From bb4781e9de45086040ce192d7568837ea98656c1 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 27 Aug 2020 14:11:44 -0500 Subject: [PATCH 1123/2296] schedulerhints: support DifferentCell filter (#2012) The TargetCell filter from cells v1 is supported in the code base but support for the DifferentCell filter was never added. DifferentCell filter is part of cells v1 but for anyone still using cells v1 this is useful. Originally part of OpenStack with https://github.com/openstack/nova/commit/596ee8796a028596e5ea1ad0529aa3f14b6f5934 --- .../v2/extensions/schedulerhints/requests.go | 7 ++++++ .../schedulerhints/testing/requests_test.go | 24 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/openstack/compute/v2/extensions/schedulerhints/requests.go b/openstack/compute/v2/extensions/schedulerhints/requests.go index 963934507f..9a6324cf1d 100644 --- a/openstack/compute/v2/extensions/schedulerhints/requests.go +++ b/openstack/compute/v2/extensions/schedulerhints/requests.go @@ -31,6 +31,9 @@ type SchedulerHints struct { // TargetCell specifies a cell name where the instance will be placed. TargetCell string `json:"target_cell,omitempty"` + // DifferentCell specifies cells names where an instance should not be placed. + DifferentCell []string `json:"different_cell,omitempty"` + // BuildNearHostIP specifies a subnet of compute nodes to host the instance. BuildNearHostIP string @@ -124,6 +127,10 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa sh["target_cell"] = opts.TargetCell } + if len(opts.DifferentCell) > 0 { + sh["different_cell"] = opts.DifferentCell + } + if opts.BuildNearHostIP != "" { if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil { err := gophercloud.ErrInvalidInput{} diff --git a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go index 393c0f73ce..426dce785f 100644 --- a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go +++ b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go @@ -25,8 +25,12 @@ func TestCreateOpts(t *testing.T) { "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, - Query: []interface{}{"=", "$free_ram_mb", "1024"}, - TargetCell: "foobar", + Query: []interface{}{"=", "$free_ram_mb", "1024"}, + TargetCell: "foobar", + DifferentCell: []string{ + "bazbar", + "barbaz", + }, BuildNearHostIP: "192.168.1.1/24", AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } @@ -55,6 +59,10 @@ func TestCreateOpts(t *testing.T) { ], "query": "[\"=\",\"$free_ram_mb\",\"1024\"]", "target_cell": "foobar", + "different_cell": [ + "bazbar", + "barbaz" + ], "build_near_host_ip": "192.168.1.1", "cidr": "/24", "reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" @@ -83,8 +91,12 @@ func TestCreateOptsWithComplexQuery(t *testing.T) { "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, - Query: []interface{}{"and", []string{"=", "$free_ram_mb", "1024"}, []string{"=", "$free_disk_mb", "204800"}}, - TargetCell: "foobar", + Query: []interface{}{"and", []string{"=", "$free_ram_mb", "1024"}, []string{"=", "$free_disk_mb", "204800"}}, + TargetCell: "foobar", + DifferentCell: []string{ + "bazbar", + "barbaz", + }, BuildNearHostIP: "192.168.1.1/24", AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } @@ -113,6 +125,10 @@ func TestCreateOptsWithComplexQuery(t *testing.T) { ], "query": "[\"and\",[\"=\",\"$free_ram_mb\",\"1024\"],[\"=\",\"$free_disk_mb\",\"204800\"]]", "target_cell": "foobar", + "different_cell": [ + "bazbar", + "barbaz" + ], "build_near_host_ip": "192.168.1.1", "cidr": "/24", "reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" From 2dc49a37219ff03033960a6d2597b58fc89cc9a1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 27 Aug 2020 13:12:57 -0600 Subject: [PATCH 1124/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b435fd3aa7..65e4fddb7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ 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) BUG FIXES From bc87166e59666acfaf83b2229fd29ccc0f2ce324 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 5 Sep 2020 13:58:37 -0600 Subject: [PATCH 1125/2296] Acc Tests: Skipping some tests on older OpenStack releases (#2001) * Acc Tests: Skipping some tests on older OpenStack releases * BlockStorage v3: Adding 202 response to Attachemnt create * Acc Tests: Skipping tests for newer releases --- .../blockstorage/v3/volumeattachments_test.go | 4 ++++ .../openstack/compute/v2/quotaset_test.go | 4 ++++ .../openstack/compute/v2/services_test.go | 1 + .../openstack/identity/v3/oauth1_test.go | 4 ++++ .../openstack/identity/v3/projects_test.go | 3 +++ .../openstack/identity/v3/roles_test.go | 3 ++- .../openstack/identity/v3/trusts_test.go | 5 +++++ .../placement/v1/resourceproviders_test.go | 22 +++++++++++++++++++ .../v2/messages/messages_test.go | 12 ++++++++++ .../blockstorage/v3/attachments/requests.go | 2 +- 10 files changed, 58 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index a07f87eeef..0ec40ca09c 100644 --- a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -12,6 +12,10 @@ import ( ) func TestVolumeAttachments(t *testing.T) { + 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/compute/v2/quotaset_test.go b/acceptance/openstack/compute/v2/quotaset_test.go index 80cc6dadd4..9beb785654 100644 --- a/acceptance/openstack/compute/v2/quotaset_test.go +++ b/acceptance/openstack/compute/v2/quotaset_test.go @@ -24,6 +24,8 @@ func TestQuotasetGet(t *testing.T) { 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) @@ -117,6 +119,8 @@ func TestQuotasetUpdateDelete(t *testing.T) { 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) diff --git a/acceptance/openstack/compute/v2/services_test.go b/acceptance/openstack/compute/v2/services_test.go index b565986390..4223332aa4 100644 --- a/acceptance/openstack/compute/v2/services_test.go +++ b/acceptance/openstack/compute/v2/services_test.go @@ -65,6 +65,7 @@ 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/identity/v3/oauth1_test.go b/acceptance/openstack/identity/v3/oauth1_test.go index dbc8359888..17e9268f62 100644 --- a/acceptance/openstack/identity/v3/oauth1_test.go +++ b/acceptance/openstack/identity/v3/oauth1_test.go @@ -15,6 +15,10 @@ 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 1e589bacba..bc1803475f 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -196,6 +196,9 @@ 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 33aa8fa6e3..1177364635 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -112,7 +112,6 @@ func TestRolesFilterList(t *testing.T) { clients.RequireAdmin(t) // For some reason this is not longer working. - // It might be a temporary issue. clients.SkipRelease(t, "master") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") @@ -121,6 +120,8 @@ func TestRolesFilterList(t *testing.T) { 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) diff --git a/acceptance/openstack/identity/v3/trusts_test.go b/acceptance/openstack/identity/v3/trusts_test.go index 44a838af62..6af7cf41dd 100644 --- a/acceptance/openstack/identity/v3/trusts_test.go +++ b/acceptance/openstack/identity/v3/trusts_test.go @@ -17,6 +17,11 @@ 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() diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 5f0daf1a07..52d2137800 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -27,6 +27,11 @@ 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() @@ -47,6 +52,13 @@ 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) client, err := clients.NewPlacementV1Client() @@ -72,6 +84,11 @@ 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() @@ -97,6 +114,11 @@ 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() diff --git a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go index d3e65603d9..be3c4207e4 100644 --- a/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go @@ -12,6 +12,10 @@ 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) @@ -36,6 +40,10 @@ 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) @@ -67,6 +75,10 @@ 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/openstack/blockstorage/v3/attachments/requests.go b/openstack/blockstorage/v3/attachments/requests.go index 4cbd5054ec..3feba700e4 100644 --- a/openstack/blockstorage/v3/attachments/requests.go +++ b/openstack/blockstorage/v3/attachments/requests.go @@ -47,7 +47,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, + OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return From c694535cf934507747754a21112e54cbf9d6aeeb Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 10 Sep 2020 04:25:02 +0200 Subject: [PATCH 1126/2296] Swift: use int64 for DeleteAt and DeleteAfter opts (#2014) --- openstack/objectstorage/v1/objects/requests.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index e5aeecbf57..c11241cc2f 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -153,8 +153,8 @@ type CreateOpts struct { ContentLength int64 `h:"Content-Length"` ContentType string `h:"Content-Type"` CopyFrom string `h:"X-Copy-From"` - DeleteAfter int `h:"X-Delete-After"` - DeleteAt int `h:"X-Delete-At"` + DeleteAfter int64 `h:"X-Delete-After"` + DeleteAt int64 `h:"X-Delete-At"` DetectContentType string `h:"X-Detect-Content-Type"` ETag string `h:"ETag"` IfNoneMatch string `h:"If-None-Match"` @@ -391,8 +391,8 @@ type UpdateOpts struct { ContentDisposition string `h:"Content-Disposition"` ContentEncoding string `h:"Content-Encoding"` ContentType string `h:"Content-Type"` - DeleteAfter int `h:"X-Delete-After"` - DeleteAt int `h:"X-Delete-At"` + DeleteAfter int64 `h:"X-Delete-After"` + DeleteAt int64 `h:"X-Delete-At"` DetectContentType bool `h:"X-Detect-Content-Type"` } From 77dd9868e9081b47f2d5daaf847d121dfbd74305 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 9 Sep 2020 20:26:35 -0600 Subject: [PATCH 1127/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65e4fddb7f..5c1281965b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ 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) From 04912433af6e94dbb1c5ffef812b6fb83701b76a Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Thu, 10 Sep 2020 04:27:44 +0200 Subject: [PATCH 1128/2296] LoadBalancer V2: add quotas package (#2010) * LoadBalancer V2: add quotas package Add openstack/loadbalancer/v2/quotas.Get. Add unit and acceptance tests. Signed-off-by: Schlotter, Christian * openstack/loadbalancer/v2/quotas review fixes * use the not-deprecated loadbalancer and healthmonitor instead of load_balancer and health_monitor * fix header in acceptance/openstack/loadbalancer/v2/quotas_test.go * openstack/loadbalancer/v2/quota handle multiple syntaxes Support the current and deprecated syntaxes: * `loadbalancer` and `load_balancer` * `healthmonitor` and `health_monitor` Signed-off-by: Schlotter, Christian --- .../openstack/loadbalancer/v2/quotas_test.go | 25 +++++ openstack/loadbalancer/v2/quotas/doc.go | 15 +++ openstack/loadbalancer/v2/quotas/requests.go | 10 ++ openstack/loadbalancer/v2/quotas/results.go | 92 +++++++++++++++++++ .../loadbalancer/v2/quotas/testing/doc.go | 2 + .../v2/quotas/testing/fixtures.go | 41 +++++++++ .../v2/quotas/testing/requests_test.go | 49 ++++++++++ openstack/loadbalancer/v2/quotas/urls.go | 13 +++ 8 files changed, 247 insertions(+) create mode 100644 acceptance/openstack/loadbalancer/v2/quotas_test.go create mode 100644 openstack/loadbalancer/v2/quotas/doc.go create mode 100644 openstack/loadbalancer/v2/quotas/requests.go create mode 100644 openstack/loadbalancer/v2/quotas/results.go create mode 100644 openstack/loadbalancer/v2/quotas/testing/doc.go create mode 100644 openstack/loadbalancer/v2/quotas/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/quotas/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/quotas/urls.go diff --git a/acceptance/openstack/loadbalancer/v2/quotas_test.go b/acceptance/openstack/loadbalancer/v2/quotas_test.go new file mode 100644 index 0000000000..a770610213 --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -0,0 +1,25 @@ +// +build acceptance networking loadbalancer quotas + +package v2 + +import ( + "os" + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestQuotasGet(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + quotasInfo, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, quotasInfo) +} diff --git a/openstack/loadbalancer/v2/quotas/doc.go b/openstack/loadbalancer/v2/quotas/doc.go new file mode 100644 index 0000000000..fbfed5c699 --- /dev/null +++ b/openstack/loadbalancer/v2/quotas/doc.go @@ -0,0 +1,15 @@ +/* +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) + } + + fmt.Printf("quotas: %#v\n", quotasInfo) + +*/ +package quotas diff --git a/openstack/loadbalancer/v2/quotas/requests.go b/openstack/loadbalancer/v2/quotas/requests.go new file mode 100644 index 0000000000..99e67a8be2 --- /dev/null +++ b/openstack/loadbalancer/v2/quotas/requests.go @@ -0,0 +1,10 @@ +package quotas + +import "github.com/gophercloud/gophercloud" + +// Get returns load balancer Quotas for a project. +func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(getURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/loadbalancer/v2/quotas/results.go b/openstack/loadbalancer/v2/quotas/results.go new file mode 100644 index 0000000000..5191b42a03 --- /dev/null +++ b/openstack/loadbalancer/v2/quotas/results.go @@ -0,0 +1,92 @@ +package quotas + +import ( + "encoding/json" + + "github.com/gophercloud/gophercloud" +) + +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:"quota"` + } + 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 load balancer quotas for a project. +type Quota struct { + // Loadbalancer represents the number of load balancers. A "-1" value means no limit. + Loadbalancer int `json:"-"` + + // Listener represents the number of listeners. A "-1" value means no limit. + Listener int `json:"listener"` + + // Member represents the number of members. A "-1" value means no limit. + Member int `json:"member"` + + // Poool represents the number of pools. A "-1" value means no limit. + Pool int `json:"pool"` + + // HealthMonitor represents the number of healthmonitors. A "-1" value means no limit. + Healthmonitor int `json:"-"` + + // L7Policy represents the number of l7policies. A "-1" value means no limit. + L7Policy int `json:"l7policy"` + + // L7Rule represents the number of l7rules. A "-1" value means no limit. + L7Rule int `json:"l7rule"` +} + +// UnmarshalJSON provides backwards compatibility to OpenStack APIs which still +// return the deprecated `load_balancer` or `health_monitor` as quota values +// instead of `loadbalancer` and `healthmonitor`. +func (r *Quota) UnmarshalJSON(b []byte) error { + type tmp Quota + + // Support both underscore and non-underscore naming. + var s struct { + tmp + LoadBalancer *int `json:"load_balancer"` + Loadbalancer *int `json:"loadbalancer"` + + HealthMonitor *int `json:"health_monitor"` + Healthmonitor *int `json:"healthmonitor"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *r = Quota(s.tmp) + + if s.LoadBalancer != nil { + r.Loadbalancer = *s.LoadBalancer + } + + if s.Loadbalancer != nil { + r.Loadbalancer = *s.Loadbalancer + } + + if s.HealthMonitor != nil { + r.Healthmonitor = *s.HealthMonitor + } + + if s.Healthmonitor != nil { + r.Healthmonitor = *s.Healthmonitor + } + + return nil +} diff --git a/openstack/loadbalancer/v2/quotas/testing/doc.go b/openstack/loadbalancer/v2/quotas/testing/doc.go new file mode 100644 index 0000000000..404d517542 --- /dev/null +++ b/openstack/loadbalancer/v2/quotas/testing/doc.go @@ -0,0 +1,2 @@ +// quotas unit tests +package testing diff --git a/openstack/loadbalancer/v2/quotas/testing/fixtures.go b/openstack/loadbalancer/v2/quotas/testing/fixtures.go new file mode 100644 index 0000000000..e180557ad2 --- /dev/null +++ b/openstack/loadbalancer/v2/quotas/testing/fixtures.go @@ -0,0 +1,41 @@ +package testing + +import "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" + +const GetResponseRaw_1 = ` +{ + "quota": { + "loadbalancer": 15, + "listener": 30, + "member": -1, + "pool": 15, + "healthmonitor": 30, + "l7policy": 100, + "l7rule": -1 + } +} +` + +const GetResponseRaw_2 = ` +{ + "quota": { + "load_balancer": 15, + "listener": 30, + "member": -1, + "pool": 15, + "health_monitor": 30, + "l7policy": 100, + "l7rule": -1 + } +} +` + +var GetResponse = quotas.Quota{ + Loadbalancer: 15, + Listener: 30, + Member: -1, + Pool: 15, + Healthmonitor: 30, + L7Policy: 100, + L7Rule: -1, +} diff --git a/openstack/loadbalancer/v2/quotas/testing/requests_test.go b/openstack/loadbalancer/v2/quotas/testing/requests_test.go new file mode 100644 index 0000000000..4bf9872ecb --- /dev/null +++ b/openstack/loadbalancer/v2/quotas/testing/requests_test.go @@ -0,0 +1,49 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestGet_1(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/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.Fprintf(w, GetResponseRaw_1) + }) + + q, err := quotas.Get(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &GetResponse) +} + +func TestGet_2(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/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.Fprintf(w, GetResponseRaw_2) + }) + + q, err := quotas.Get(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &GetResponse) +} diff --git a/openstack/loadbalancer/v2/quotas/urls.go b/openstack/loadbalancer/v2/quotas/urls.go new file mode 100644 index 0000000000..74bb1e2132 --- /dev/null +++ b/openstack/loadbalancer/v2/quotas/urls.go @@ -0,0 +1,13 @@ +package quotas + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "quotas" + +func resourceURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID) +} + +func getURL(c *gophercloud.ServiceClient, projectID string) string { + return resourceURL(c, projectID) +} From d0d95a08cd50b4429ad1afdd9276e5f2aafc7665 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 9 Sep 2020 20:28:33 -0600 Subject: [PATCH 1129/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c1281965b..f213a97580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ 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) BUG FIXES From 98ddeee48f72dae95996878e2795e8f865a4c1c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Ga=C5=82uszka?= Date: Fri, 11 Sep 2020 16:45:16 +0200 Subject: [PATCH 1130/2296] feat: update Messaging v2 API to support _enable_encrypt_messages field (#2016) --- openstack/messaging/v2/queues/requests.go | 3 +++ openstack/messaging/v2/queues/results.go | 3 +++ openstack/messaging/v2/queues/testing/fixtures.go | 3 +++ openstack/messaging/v2/queues/testing/requests_test.go | 2 ++ 4 files changed, 11 insertions(+) diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index b150df436b..f30deb74ac 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -85,6 +85,9 @@ type CreateOpts struct { // for any messages posted to the queue. MaxMessagesPostSize int `json:"_max_messages_post_size,omitempty"` + // Does messages should be encrypted + EnableEncryptMessages *bool `json:"_enable_encrypt_messages,omitempty"` + // Extra is free-form extra key/value pairs to describe the queue. Extra map[string]interface{} `json:"-"` } diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 61005da778..5d873042b2 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -88,6 +88,9 @@ type QueueDetails struct { // The max post size of messages defined for the queue. MaxMessagesPostSize int `json:"_max_messages_post_size"` + // Is message encryption enabled + EnableEncryptMessages bool `json:"_enable_encrypt_messages"` + // The flavor defined for the queue. Flavor string `json:"flavor"` } diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index 50e7bd4c93..fb9661f6a4 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -22,6 +22,7 @@ const CreateQueueRequest = ` "_dead_letter_queue": "dead_letter", "_dead_letter_queue_messages_ttl": 3600, "_max_claim_count": 10, + "_enable_encrypt_messages": false, "description": "Queue for unit testing." }` @@ -53,6 +54,7 @@ const ListQueuesResponse1 = ` "_default_message_ttl":3700, "_max_claim_count":10, "_max_messages_post_size":262143, + "_enable_encrypt_messages":true, "description":"Test queue." } } @@ -155,6 +157,7 @@ var FirstQueue = queues.Queue{ DefaultMessageTTL: 3700, MaxClaimCount: 10, MaxMessagesPostSize: 262143, + EnableEncryptMessages: true, Extra: map[string]interface{}{"description": "Test queue."}, }, } diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index c4502cdac8..3eda906749 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -37,6 +37,7 @@ func TestCreate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleCreateSuccessfully(t) + var enableEncrypted *bool = new(bool) createOpts := queues.CreateOpts{ QueueName: QueueName, @@ -46,6 +47,7 @@ func TestCreate(t *testing.T) { DeadLetterQueue: "dead_letter", DeadLetterQueueMessagesTTL: 3600, MaxClaimCount: 10, + EnableEncryptMessages: enableEncrypted, Extra: map[string]interface{}{"description": "Queue for unit testing."}, } From e77d083eb855516dd844bcd458625665a298b79d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 11 Sep 2020 08:46:58 -0600 Subject: [PATCH 1131/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f213a97580..4f6777e3d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ IMPROVEMENTS * 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) BUG FIXES From 9bd60a2a0c955dbfb82ff4b56631bd6a0b1ab5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Ga=C5=82uszka?= Date: Tue, 15 Sep 2020 20:25:59 +0200 Subject: [PATCH 1132/2296] feat: add name and with_count params into listing queues (#2018) --- openstack/messaging/v2/queues/requests.go | 6 ++++++ openstack/messaging/v2/queues/results.go | 12 ++++++++++++ openstack/messaging/v2/queues/testing/fixtures.go | 8 +++++--- .../messaging/v2/queues/testing/requests_test.go | 8 ++++++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index f30deb74ac..4f036640c5 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -21,6 +21,12 @@ type ListOpts struct { // Specifies if showing the detailed information when querying queues Detailed bool `q:"detailed,omitempty"` + + // Specifies if filter the queues by queue’s name when querying queues. + Name bool `q:"name,omitempty"` + + // Specifies if showing the amount of queues when querying them. + WithCount bool `q:"with_count,omitempty"` } // ToQueueListQuery formats a ListOpts into a query string. diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 5d873042b2..d1f24c086a 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -173,6 +173,18 @@ func (r QueuePage) NextPageURL() (string, error) { return nextPageURL(r.URL.String(), next) } +// GetCount value if it request was supplied `WithCount` param +func (r QueuePage) GetCount() (int, error) { + var s struct { + Count int `json:"count"` + } + err := r.ExtractInto(&s) + if err != nil { + return 0, err + } + return s.Count, nil +} + func (r *QueueDetails) UnmarshalJSON(b []byte) error { type tmp QueueDetails var s struct { diff --git a/openstack/messaging/v2/queues/testing/fixtures.go b/openstack/messaging/v2/queues/testing/fixtures.go index fb9661f6a4..67496c90f4 100644 --- a/openstack/messaging/v2/queues/testing/fixtures.go +++ b/openstack/messaging/v2/queues/testing/fixtures.go @@ -64,7 +64,8 @@ const ListQueuesResponse1 = ` "href":"/v2/queues?marker=london", "rel":"next" } - ] + ], + "count": 2 }` // ListQueuesResponse2 is a sample response to a List queues. @@ -90,7 +91,8 @@ const ListQueuesResponse2 = ` "href":"/v2/queues?marker=beijing", "rel":"next" } - ] + ], + "count": 2 }` // UpdateQueueRequest is a sample request to update a queue. @@ -223,7 +225,7 @@ func HandleListSuccessfully(t *testing.T) { next := r.RequestURI switch next { - case "/v2/queues?limit=1": + case "/v2/queues?limit=1&with_count=true": fmt.Fprintf(w, ListQueuesResponse1) case "/v2/queues?marker=london": fmt.Fprint(w, ListQueuesResponse2) diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index 3eda906749..ec5505e801 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -15,17 +15,21 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) listOpts := queues.ListOpts{ - Limit: 1, + Limit: 1, + WithCount: true, } count := 0 err := queues.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { actual, err := queues.ExtractQueues(page) th.AssertNoErr(t, err) + countField, err := page.(queues.QueuePage).GetCount() + + th.AssertNoErr(t, err) + th.AssertEquals(t, countField, 2) th.CheckDeepEquals(t, ExpectedQueueSlice[count], actual) count++ - return true, nil }) th.AssertNoErr(t, err) From 5690aed155c19b686633167a249e7a2c7495951c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 15 Sep 2020 12:26:58 -0600 Subject: [PATCH 1133/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6777e3d8..ea7f8e96cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ IMPROVEMENTS * 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) BUG FIXES From b1ee3a6a3b9297e670c54cafbf247744b9b8026b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 18 Sep 2020 13:20:07 -0600 Subject: [PATCH 1134/2296] Acc Tests: Fix TestOAuth1CRUD ordering issues (#2020) --- .../openstack/identity/v3/oauth1_test.go | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/acceptance/openstack/identity/v3/oauth1_test.go b/acceptance/openstack/identity/v3/oauth1_test.go index 17e9268f62..02a1d8f10f 100644 --- a/acceptance/openstack/identity/v3/oauth1_test.go +++ b/acceptance/openstack/identity/v3/oauth1_test.go @@ -173,18 +173,10 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer tools.PrintResource(t, accessTokenRoles) - var found bool for _, atr := range accessTokenRoles { - if atr.ID == roles[0].ID { - found = true - } - } - th.AssertEquals(t, found, true) - - if len(accessTokenRoles) > 1 { - found = false - for _, atr := range accessTokenRoles { - if atr.ID == roles[1].ID { + var found bool + for _, role := range roles { + if atr.ID == role.ID { found = true } } @@ -195,7 +187,14 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer getAccessTokenRole, err := oauth1.GetAccessTokenRole(client, user.ID, accessToken.OAuthToken, roles[0].ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getAccessTokenRole) - th.AssertDeepEquals(t, *getAccessTokenRole, accessTokenRoles[0]) + + var found bool + for _, atr := range accessTokenRoles { + if atr.ID == getAccessTokenRole.ID { + found = true + } + } + th.AssertEquals(t, found, true) // Test auth using OAuth1 newClient, err := clients.NewIdentityV3UnauthenticatedClient() From 5b1a3dca823e2b80635c2a873d20573476edc10c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 22 Sep 2020 09:24:38 -0600 Subject: [PATCH 1135/2296] Travis: Go mod cleanup (#2028) --- go.mod | 6 +----- go.sum | 18 +++++++++--------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index cf6aabe31c..64e2a0fb48 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,9 @@ module github.com/gophercloud/gophercloud go 1.13 require ( + github.com/kr/pretty v0.2.1 // indirect golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e - golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect - golang.org/x/text v0.3.2 // indirect - golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 // indirect - golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index a98dc550de..311ab0449d 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,19 @@ +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU= golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From ba31f2ca890a3b8bdcd8d684be6fd9e0b8b87a3c Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Tue, 22 Sep 2020 17:25:48 +0200 Subject: [PATCH 1136/2296] LoadBalancer V2: implement quotas.Update (#2023) Signed-off-by: Schlotter, Christian --- .../openstack/loadbalancer/v2/quotas.go | 16 ++++++ .../openstack/loadbalancer/v2/quotas_test.go | 37 ++++++++++++ openstack/loadbalancer/v2/quotas/doc.go | 19 +++++++ openstack/loadbalancer/v2/quotas/requests.go | 55 +++++++++++++++++- openstack/loadbalancer/v2/quotas/results.go | 6 ++ .../v2/quotas/testing/fixtures.go | 38 +++++++++++++ .../v2/quotas/testing/requests_test.go | 57 +++++++++++++++++++ openstack/loadbalancer/v2/quotas/urls.go | 4 ++ 8 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/loadbalancer/v2/quotas.go diff --git a/acceptance/openstack/loadbalancer/v2/quotas.go b/acceptance/openstack/loadbalancer/v2/quotas.go new file mode 100644 index 0000000000..9a0819e46e --- /dev/null +++ b/acceptance/openstack/loadbalancer/v2/quotas.go @@ -0,0 +1,16 @@ +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 a770610213..fa359dd7b7 100644 --- a/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -3,7 +3,9 @@ package v2 import ( + "log" "os" + "reflect" "testing" "github.com/gophercloud/gophercloud/acceptance/clients" @@ -23,3 +25,38 @@ func TestQuotasGet(t *testing.T) { tools.PrintResource(t, quotasInfo) } + +func TestQuotasUpdate(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + originalQuotas, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() + th.AssertNoErr(t, err) + + newQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), quotaUpdateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newQuotas) + + if reflect.DeepEqual(originalQuotas, newQuotas) { + 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{ + Loadbalancer: &originalQuotas.Loadbalancer, + Listener: &originalQuotas.Listener, + Member: &originalQuotas.Member, + Pool: &originalQuotas.Pool, + Healthmonitor: &originalQuotas.Healthmonitor, + L7Policy: &originalQuotas.L7Policy, + L7Rule: &originalQuotas.L7Rule, + }).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, originalQuotas, restoredQuotas) + + tools.PrintResource(t, restoredQuotas) +} diff --git a/openstack/loadbalancer/v2/quotas/doc.go b/openstack/loadbalancer/v2/quotas/doc.go index fbfed5c699..39d54b98f9 100644 --- a/openstack/loadbalancer/v2/quotas/doc.go +++ b/openstack/loadbalancer/v2/quotas/doc.go @@ -11,5 +11,24 @@ Example to Get project quotas fmt.Printf("quotas: %#v\n", quotasInfo) +Example to Update project quotas + + 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) + } + + fmt.Printf("quotas: %#v\n", quotasInfo) */ package quotas diff --git a/openstack/loadbalancer/v2/quotas/requests.go b/openstack/loadbalancer/v2/quotas/requests.go index 99e67a8be2..974cc4b9bb 100644 --- a/openstack/loadbalancer/v2/quotas/requests.go +++ b/openstack/loadbalancer/v2/quotas/requests.go @@ -1,6 +1,8 @@ package quotas -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" +) // Get returns load balancer Quotas for a project. func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { @@ -8,3 +10,54 @@ func Get(client *gophercloud.ServiceClient, projectID 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 { + ToQuotaUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts represents options used to update the load balancer Quotas. +type UpdateOpts struct { + // Loadbalancer represents the number of load balancers. A "-1" value means no limit. + Loadbalancer *int `json:"loadbalancer,omitempty"` + + // Listener represents the number of listeners. A "-1" value means no limit. + Listener *int `json:"listener,omitempty"` + + // Member represents the number of members. A "-1" value means no limit. + Member *int `json:"member,omitempty"` + + // Poool represents the number of pools. A "-1" value means no limit. + Pool *int `json:"pool,omitempty"` + + // HealthMonitor represents the number of healthmonitors. A "-1" value means no limit. + Healthmonitor *int `json:"healthmonitor,omitempty"` + + // L7Policy represents the number of l7policies. A "-1" value means no limit. + L7Policy *int `json:"l7policy,omitempty"` + + // L7Rule represents the number of l7rules. A "-1" value means no limit. + L7Rule *int `json:"l7rule,omitempty"` +} + +// ToQuotaUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "quota") +} + +// Update accepts a UpdateOpts struct and updates an existing load balancer Quotas using the +// values provided. +func Update(c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ + // allow 200 (neutron/lbaasv2) and 202 (octavia) + OkCodes: []int{200, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/loadbalancer/v2/quotas/results.go b/openstack/loadbalancer/v2/quotas/results.go index 5191b42a03..a25b687244 100644 --- a/openstack/loadbalancer/v2/quotas/results.go +++ b/openstack/loadbalancer/v2/quotas/results.go @@ -25,6 +25,12 @@ type GetResult struct { commonResult } +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a Quota. +type UpdateResult struct { + commonResult +} + // Quota contains load balancer quotas for a project. type Quota struct { // Loadbalancer represents the number of load balancers. A "-1" value means no limit. diff --git a/openstack/loadbalancer/v2/quotas/testing/fixtures.go b/openstack/loadbalancer/v2/quotas/testing/fixtures.go index e180557ad2..b9f5a05b6c 100644 --- a/openstack/loadbalancer/v2/quotas/testing/fixtures.go +++ b/openstack/loadbalancer/v2/quotas/testing/fixtures.go @@ -39,3 +39,41 @@ var GetResponse = quotas.Quota{ L7Policy: 100, L7Rule: -1, } + +const UpdateRequestResponseRaw_1 = ` +{ + "quota": { + "loadbalancer": 20, + "listener": 40, + "member": 200, + "pool": 20, + "healthmonitor": -1, + "l7policy": 50, + "l7rule": 100 + } +} +` + +const UpdateRequestResponseRaw_2 = ` +{ + "quota": { + "load_balancer": 20, + "listener": 40, + "member": 200, + "pool": 20, + "health_monitor": -1, + "l7policy": 50, + "l7rule": 100 + } +} +` + +var UpdateResponse = quotas.Quota{ + Loadbalancer: 20, + Listener: 40, + Member: 200, + Pool: 20, + Healthmonitor: -1, + L7Policy: 50, + L7Rule: 100, +} diff --git a/openstack/loadbalancer/v2/quotas/testing/requests_test.go b/openstack/loadbalancer/v2/quotas/testing/requests_test.go index 4bf9872ecb..509b246f79 100644 --- a/openstack/loadbalancer/v2/quotas/testing/requests_test.go +++ b/openstack/loadbalancer/v2/quotas/testing/requests_test.go @@ -5,6 +5,7 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" th "github.com/gophercloud/gophercloud/testhelper" @@ -47,3 +48,59 @@ func TestGet_2(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &GetResponse) } + +func TestUpdate_1(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/quotas/0a73845280574ad389c292f6a74afa76", 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") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, UpdateRequestResponseRaw_1) + }) + + q, err := quotas.Update(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", 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), + }).Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &UpdateResponse) +} + +func TestUpdate_2(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/quotas/0a73845280574ad389c292f6a74afa76", 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") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, UpdateRequestResponseRaw_2) + }) + + q, err := quotas.Update(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", 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), + }).Extract() + + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &UpdateResponse) +} diff --git a/openstack/loadbalancer/v2/quotas/urls.go b/openstack/loadbalancer/v2/quotas/urls.go index 74bb1e2132..cf7dcbde65 100644 --- a/openstack/loadbalancer/v2/quotas/urls.go +++ b/openstack/loadbalancer/v2/quotas/urls.go @@ -11,3 +11,7 @@ func resourceURL(c *gophercloud.ServiceClient, projectID string) string { func getURL(c *gophercloud.ServiceClient, projectID string) string { return resourceURL(c, projectID) } + +func updateURL(c *gophercloud.ServiceClient, projectID string) string { + return resourceURL(c, projectID) +} From c075d0e51ede4e3b12838893ed8e2cb3ab5482ec Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 22 Sep 2020 09:26:25 -0600 Subject: [PATCH 1137/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea7f8e96cf..49c06fc3db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ IMPROVEMENTS * 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) BUG FIXES From 900f5ba8c05b518e75ab5e5f470e960bba107cde Mon Sep 17 00:00:00 2001 From: ITD27M01 Date: Tue, 22 Sep 2020 18:27:35 +0300 Subject: [PATCH 1138/2296] Added availability zones support for Octavia LoadBalancers (#2026) --- openstack/loadbalancer/v2/loadbalancers/requests.go | 5 +++++ openstack/loadbalancer/v2/loadbalancers/results.go | 3 +++ openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go | 3 +++ 3 files changed, 11 insertions(+) diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 4c7c288942..63c832fb01 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -29,6 +29,7 @@ type ListOpts struct { OperatingStatus string `q:"operating_status"` Name string `q:"name"` FlavorID string `q:"flavor_id"` + AvailabilityZone string `q:"availability_zone"` Provider string `q:"provider"` Limit int `q:"limit"` Marker string `q:"marker"` @@ -111,6 +112,10 @@ type CreateOpts struct { // The UUID of a flavor. FlavorID string `json:"flavor_id,omitempty"` + // The name of an Octavia availability zone. + // Requires Octavia API version 2.14 or later. + AvailabilityZone string `json:"availability_zone,omitempty"` + // The name of the provider. Provider string `json:"provider,omitempty"` diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index 14d64a3586..9a385363f2 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -59,6 +59,9 @@ type LoadBalancer struct { // The UUID of a flavor if set. FlavorID string `json:"flavor_id"` + // The name of an Octavia availability zone if set. + AvailabilityZone string `json:"availability_zone"` + // The name of the provider. Provider string `json:"provider"` diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index f93b294db3..3f76f797e2 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -46,6 +46,7 @@ const LoadbalancersListBody = ` "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", + "availability_zone": "db_az", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", @@ -70,6 +71,7 @@ const SingleLoadbalancerBody = ` "vip_address": "10.30.176.48", "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", + "availability_zone": "db_az", "provider": "haproxy", "admin_state_up": true, "provisioning_status": "PENDING_CREATE", @@ -184,6 +186,7 @@ var ( VipAddress: "10.30.176.48", VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", + AvailabilityZone: "db_az", Provider: "haproxy", AdminStateUp: true, ProvisioningStatus: "PENDING_CREATE", From 6da85109d124567f8429a1923904f0816cecda73 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 22 Sep 2020 09:38:50 -0600 Subject: [PATCH 1139/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49c06fc3db..72d4152ebd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ IMPROVEMENTS * 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) BUG FIXES From 321ce8a2474bdaf446e19bc6b109ca2f342b4f60 Mon Sep 17 00:00:00 2001 From: Alexey Myltsev <57535004+alexeymyltsev@users.noreply.github.com> Date: Sun, 27 Sep 2020 17:57:26 +0300 Subject: [PATCH 1140/2296] Add l3agent list for router with HA (#2025) * add l3agent list for router * add resource_version to type * add ListL3Agents to acceptance test * fix TestListL3Agents * remove not needed test from acceptance * fix acceptance test for ListL3Agents * add comment for acceptance test * Converting ListL3Agents to a paged result * Updating ListL3Agents acceptance test * fix TestListL3Agents Co-authored-by: Joe Topjian --- .../v2/extensions/layer3/routers_test.go | 48 ++++++ .../v2/extensions/layer3/routers/doc.go | 18 +++ .../v2/extensions/layer3/routers/requests.go | 7 + .../v2/extensions/layer3/routers/results.go | 96 ++++++++++++ .../layer3/routers/testing/requests_test.go | 137 ++++++++++++++++++ .../v2/extensions/layer3/routers/urls.go | 4 + 6 files changed, 310 insertions(+) diff --git a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 73390757b4..0bbeb9fa11 100644 --- a/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -123,6 +123,7 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { newRouter, err = routers.Update(client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, newRouter.GatewayInfo, routers.GatewayInfo{}) + } func TestLayer3RouterInterface(t *testing.T) { @@ -163,3 +164,50 @@ func TestLayer3RouterInterface(t *testing.T) { _, err = routers.RemoveInterface(client, router.ID, riOpts).Extract() th.AssertNoErr(t, err) } + +func TestLayer3RouterAgents(t *testing.T) { + clients.RequireAdmin(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) + + router, err := CreateRouter(t, client, network.ID) + th.AssertNoErr(t, err) + defer DeleteRouter(t, client, router.ID) + + tools.PrintResource(t, router) + + newName := tools.RandomString("TESTACC-", 8) + newDescription := "" + updateOpts := routers.UpdateOpts{ + Name: newName, + Description: &newDescription, + } + + _, err = routers.Update(client, router.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + _, err = routers.Get(client, router.ID).Extract() + th.AssertNoErr(t, err) + + // Test ListL3Agents for HA or not HA router + l3AgentsPages, err := routers.ListL3Agents(client, router.ID).AllPages() + th.AssertNoErr(t, err) + l3Agents, err := routers.ExtractL3Agents(l3AgentsPages) + th.AssertNoErr(t, err) + + tools.PrintResource(t, l3Agents) + + var found bool + for _, agent := range l3Agents { + if agent.Binary == "neutron-l3-agent" { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/openstack/networking/v2/extensions/layer3/routers/doc.go b/openstack/networking/v2/extensions/layer3/routers/doc.go index 6ede7f5e17..9f0cf98c4b 100644 --- a/openstack/networking/v2/extensions/layer3/routers/doc.go +++ b/openstack/networking/v2/extensions/layer3/routers/doc.go @@ -104,5 +104,23 @@ Example to Remove an Interface from a Router if err != nil { panic(err) } + +Example to List an L3 agents for a Router + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + allPages, err := routers.ListL3Agents(networkClient, routerID).AllPages() + if err != nil { + panic(err) + } + + allL3Agents, err := routers.ExtractL3Agents(allPages) + if err != nil { + panic(err) + } + + for _, agent := range allL3Agents { + fmt.Printf("%+v\n", agent) + } */ package routers diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index e0b1effa17..3855603236 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -237,3 +237,10 @@ func RemoveInterface(c *gophercloud.ServiceClient, id string, opts RemoveInterfa _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ListL3Agents returns a list of l3-agents scheduled for a specific router. +func ListL3Agents(c *gophercloud.ServiceClient, id string) (result pagination.Pager) { + return pagination.NewPager(c, listl3AgentsURL(c, id), func(r pagination.PageResult) pagination.Page { + return ListL3AgentsPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index 857e1947e1..a6c93cfd0a 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -1,6 +1,9 @@ package routers import ( + "encoding/json" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -179,3 +182,96 @@ func (r InterfaceResult) Extract() (*InterfaceInfo, error) { err := r.ExtractInto(&s) return &s, err } + +// L3Agent represents a Neutron agent for routers. +type L3Agent struct { + // ID is the id of the agent. + ID string `json:"id"` + + // AdminStateUp is an administrative state of the agent. + AdminStateUp bool `json:"admin_state_up"` + + // AgentType is a type of the agent. + AgentType string `json:"agent_type"` + + // Alive indicates whether agent is alive or not. + Alive bool `json:"alive"` + + // ResourcesSynced indicates whether agent is synced or not. + // Not all agent types track resources via Placement. + ResourcesSynced bool `json:"resources_synced"` + + // AvailabilityZone is a zone of the agent. + AvailabilityZone string `json:"availability_zone"` + + // Binary is an executable binary of the agent. + Binary string `json:"binary"` + + // Configurations is a configuration specific key/value pairs that are + // determined by the agent binary and type. + Configurations map[string]interface{} `json:"configurations"` + + // CreatedAt is a creation timestamp. + CreatedAt time.Time `json:"-"` + + // StartedAt is a starting timestamp. + StartedAt time.Time `json:"-"` + + // HeartbeatTimestamp is a last heartbeat timestamp. + HeartbeatTimestamp time.Time `json:"-"` + + // Description contains agent description. + Description string `json:"description"` + + // Host is a hostname of the agent system. + Host string `json:"host"` + + // Topic contains name of AMQP topic. + Topic string `json:"topic"` + + // HAState is a ha state of agent(active/standby) for router + HAState string `json:"ha_state"` + + // ResourceVersions is a list agent known objects and version numbers + ResourceVersions map[string]interface{} `json:"resource_versions"` +} + +// UnmarshalJSON helps to convert the timestamps into the time.Time type. +func (r *L3Agent) UnmarshalJSON(b []byte) error { + type tmp L3Agent + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"created_at"` + StartedAt gophercloud.JSONRFC3339ZNoTNoZ `json:"started_at"` + HeartbeatTimestamp gophercloud.JSONRFC3339ZNoTNoZ `json:"heartbeat_timestamp"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = L3Agent(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.StartedAt = time.Time(s.StartedAt) + r.HeartbeatTimestamp = time.Time(s.HeartbeatTimestamp) + + return nil +} + +type ListL3AgentsPage struct { + pagination.SinglePageBase +} + +func (r ListL3AgentsPage) IsEmpty() (bool, error) { + v, err := ExtractL3Agents(r) + return len(v) == 0, err +} + +func ExtractL3Agents(r pagination.Page) ([]L3Agent, error) { + var s struct { + L3Agents []L3Agent `json:"agents"` + } + + err := (r.(ListL3AgentsPage)).ExtractInto(&s) + return s.L3Agents, err +} diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 7bd1c4edf6..c4d386b63b 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "testing" + "time" fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" @@ -478,3 +479,139 @@ func TestRemoveInterface(t *testing.T) { th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID) th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID) } + +func TestListL3Agents(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/routers/fa3a4aaa-c73f-48aa-a603-8c8bf642b7c0/l3-agents", 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, ` +{ + "agents": [ + { + "id": "ddbf087c-e38f-4a73-bcb3-c38f2a719a03", + "agent_type": "L3 agent", + "binary": "neutron-l3-agent", + "topic": "l3_agent", + "host": "os-ctrl-02", + "admin_state_up": true, + "created_at": "2017-07-26 23:15:44", + "started_at": "2018-06-26 21:46:19", + "heartbeat_timestamp": "2019-01-09 10:28:53", + "description": "My L3 agent for OpenStack", + "resources_synced": true, + "availability_zone": "nova", + "alive": true, + "configurations": { + "agent_mode": "legacy", + "ex_gw_ports": 2, + "floating_ips": 2, + "handle_internal_only_routers": true, + "interface_driver": "linuxbridge", + "interfaces": 1, + "log_agent_heartbeats": false, + "routers": 2 + }, + "resource_versions": {}, + "ha_state": "standby" + }, + { + "id": "4541cc6c-87bc-4cee-bad2-36ca78836c91", + "agent_type": "L3 agent", + "binary": "neutron-l3-agent", + "topic": "l3_agent", + "host": "os-ctrl-03", + "admin_state_up": true, + "created_at": "2017-01-22 14:00:50", + "started_at": "2018-11-06 12:09:17", + "heartbeat_timestamp": "2019-01-09 10:28:50", + "description": "My L3 agent for OpenStack", + "resources_synced": true, + "availability_zone": "nova", + "alive": true, + "configurations": { + "agent_mode": "legacy", + "ex_gw_ports": 2, + "floating_ips": 2, + "handle_internal_only_routers": true, + "interface_driver": "linuxbridge", + "interfaces": 1, + "log_agent_heartbeats": false, + "routers": 2 + }, + "resource_versions": {}, + "ha_state": "active" + } + ] +} + `) + }) + + l3AgentsPages, err := routers.ListL3Agents(fake.ServiceClient(), "fa3a4aaa-c73f-48aa-a603-8c8bf642b7c0").AllPages() + th.AssertNoErr(t, err) + actual, err := routers.ExtractL3Agents(l3AgentsPages) + + expected := []routers.L3Agent{ + { + ID: "ddbf087c-e38f-4a73-bcb3-c38f2a719a03", + AdminStateUp: true, + AgentType: "L3 agent", + Description: "My L3 agent for OpenStack", + Alive: true, + ResourcesSynced: true, + Binary: "neutron-l3-agent", + AvailabilityZone: "nova", + Configurations: map[string]interface{}{ + "agent_mode": "legacy", + "ex_gw_ports": float64(2), + "floating_ips": float64(2), + "handle_internal_only_routers": true, + "interface_driver": "linuxbridge", + "interfaces": float64(1), + "log_agent_heartbeats": false, + "routers": float64(2), + }, + CreatedAt: time.Date(2017, 7, 26, 23, 15, 44, 0, time.UTC), + StartedAt: time.Date(2018, 6, 26, 21, 46, 19, 0, time.UTC), + HeartbeatTimestamp: time.Date(2019, 1, 9, 10, 28, 53, 0, time.UTC), + Host: "os-ctrl-02", + Topic: "l3_agent", + HAState: "standby", + ResourceVersions: map[string]interface{}{}, + }, + { + ID: "4541cc6c-87bc-4cee-bad2-36ca78836c91", + AdminStateUp: true, + AgentType: "L3 agent", + Description: "My L3 agent for OpenStack", + Alive: true, + ResourcesSynced: true, + Binary: "neutron-l3-agent", + AvailabilityZone: "nova", + Configurations: map[string]interface{}{ + "agent_mode": "legacy", + "ex_gw_ports": float64(2), + "floating_ips": float64(2), + "handle_internal_only_routers": true, + "interface_driver": "linuxbridge", + "interfaces": float64(1), + "log_agent_heartbeats": false, + "routers": float64(2), + }, + CreatedAt: time.Date(2017, 1, 22, 14, 00, 50, 0, time.UTC), + StartedAt: time.Date(2018, 11, 6, 12, 9, 17, 0, time.UTC), + HeartbeatTimestamp: time.Date(2019, 1, 9, 10, 28, 50, 0, time.UTC), + Host: "os-ctrl-03", + Topic: "l3_agent", + HAState: "active", + ResourceVersions: map[string]interface{}{}, + }, + } + th.CheckDeepEquals(t, expected, actual) +} diff --git a/openstack/networking/v2/extensions/layer3/routers/urls.go b/openstack/networking/v2/extensions/layer3/routers/urls.go index f9e9da3211..7b30f9033c 100644 --- a/openstack/networking/v2/extensions/layer3/routers/urls.go +++ b/openstack/networking/v2/extensions/layer3/routers/urls.go @@ -19,3 +19,7 @@ func addInterfaceURL(c *gophercloud.ServiceClient, id string) string { func removeInterfaceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "remove_router_interface") } + +func listl3AgentsURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "l3-agents") +} From 5583cd7be787bb2e56c6dbb46e139efb95309e1b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 27 Sep 2020 08:58:19 -0600 Subject: [PATCH 1141/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72d4152ebd..9379528776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ IMPROVEMENTS * 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 From c137dd5308818241136f971d6d6a7fe6f295f5d2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 27 Sep 2020 08:59:29 -0600 Subject: [PATCH 1142/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9379528776..3f7d4c4949 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.13.0 (Unreleased) +## 0.14.0 (Unreleased) + +## 0.13.0 (September 27, 2020) IMPROVEMENTS From 2898324d9be6961b4d2d9fb253839d24e6109a0b Mon Sep 17 00:00:00 2001 From: Benjamin Ziehms Date: Tue, 29 Sep 2020 16:31:44 +0200 Subject: [PATCH 1143/2296] Identity: add 'enabled' field on Endpoint (#2030) --- openstack/identity/v3/endpoints/results.go | 3 +++ .../identity/v3/endpoints/testing/requests_test.go | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index 6e54f6b413..61d201f383 100644 --- a/openstack/identity/v3/endpoints/results.go +++ b/openstack/identity/v3/endpoints/results.go @@ -57,6 +57,9 @@ type Endpoint struct { // URL is the url of the Endpoint. URL string `json:"url"` + + // Enabled is whether or not the endpoint is enabled. + Enabled bool `json:"enabled"` } // EndpointPage is a single page of Endpoint results. diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 53d8488896..d244ba5ca3 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -37,7 +37,8 @@ func TestCreateSuccessful(t *testing.T) { "endpoint": { "id": "12", "interface": "public", - "links": { + "enabled": true, + "links": { "self": "https://localhost:5000/v3/endpoints/12" }, "name": "the-endiest-of-points", @@ -61,6 +62,7 @@ func TestCreateSuccessful(t *testing.T) { expected := &endpoints.Endpoint{ ID: "12", Availability: gophercloud.AvailabilityPublic, + Enabled: true, Name: "the-endiest-of-points", Region: "underground", ServiceID: "asdfasdfasdfasdf", @@ -85,6 +87,7 @@ func TestListEndpoints(t *testing.T) { { "id": "12", "interface": "public", + "enabled": true, "links": { "self": "https://localhost:5000/v3/endpoints/12" }, @@ -96,6 +99,7 @@ func TestListEndpoints(t *testing.T) { { "id": "13", "interface": "internal", + "enabled": false, "links": { "self": "https://localhost:5000/v3/endpoints/13" }, @@ -126,6 +130,7 @@ func TestListEndpoints(t *testing.T) { { ID: "12", Availability: gophercloud.AvailabilityPublic, + Enabled: true, Name: "the-endiest-of-points", Region: "underground", ServiceID: "asdfasdfasdfasdf", @@ -134,6 +139,7 @@ func TestListEndpoints(t *testing.T) { { ID: "13", Availability: gophercloud.AvailabilityInternal, + Enabled: false, Name: "shhhh", Region: "underground", ServiceID: "asdfasdfasdfasdf", @@ -167,6 +173,7 @@ func TestUpdateEndpoint(t *testing.T) { "endpoint": { "id": "12", "interface": "public", + "enabled": true, "links": { "self": "https://localhost:5000/v3/endpoints/12" }, @@ -190,6 +197,7 @@ func TestUpdateEndpoint(t *testing.T) { expected := &endpoints.Endpoint{ ID: "12", Availability: gophercloud.AvailabilityPublic, + Enabled: true, Name: "renamed", Region: "somewhere-else", ServiceID: "asdfasdfasdfasdf", From fe61704a6a37a49c16326cd7a04ca8ac7616ae5e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 29 Sep 2020 08:32:34 -0600 Subject: [PATCH 1144/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f7d4c4949..d784c8430a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.14.0 (Unreleased) +IMPROVEMENTS + +* Added `identity/v3/endpoints.Endpoint.Enabled` [GH-2030](https://github.com/gophercloud/gophercloud/pull/2030) + ## 0.13.0 (September 27, 2020) IMPROVEMENTS From f81c533ac6d0fe0b5c883138bc790c02cd36debb Mon Sep 17 00:00:00 2001 From: JK Date: Tue, 29 Sep 2020 16:33:19 +0200 Subject: [PATCH 1145/2296] Support Magnum upgrade API (#2032) API doc: https://docs.openstack.org/api-ref/container-infrastructure-management/?expanded=upgrade-a-cluster-detail --- openstack/containerinfra/v1/clusters/doc.go | 11 ++++++ .../containerinfra/v1/clusters/requests.go | 36 +++++++++++++++++++ .../containerinfra/v1/clusters/results.go | 13 +++++++ .../v1/clusters/testing/fixtures.go | 18 ++++++++++ .../v1/clusters/testing/requests_test.go | 25 +++++++++++++ openstack/containerinfra/v1/clusters/urls.go | 4 +++ 6 files changed, 107 insertions(+) diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index 3fd28d10c6..f0d41f0760 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -89,6 +89,17 @@ Example to Update a Cluster } fmt.Printf("%s\n", clusterUUID) +Example to Upgrade a Cluster + + upgradeOpts := clusters.UpgradeOpts{ + ClusterTemplate: "0562d357-8641-4759-8fed-8173f02c9633", + } + clusterUUID, err := clusters.Upgrade(serviceClient, clusterUUID, upgradeOpts).Extract() + if err != nil { + panic(err) + } + fmt.Printf("%s\n", clusterUUID) + Example to Delete a Cluster clusterUUID := "dc6d336e3fc4c0a951b5698cd1236ee" diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 2cd865b8b9..58524586f0 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -164,6 +164,42 @@ func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuild return } +type UpgradeOpts struct { + ClusterTemplate string `json:"cluster_template" required:"true"` + MaxBatchSize *int `json:"max_batch_size,omitempty"` + NodeGroup string `json:"nodegroup,omitempty"` +} + +// UpgradeOptsBuilder allows extensions to add additional parameters to the +// Upgrade request. +type UpgradeOptsBuilder interface { + ToClustersUpgradeMap() (map[string]interface{}, error) +} + +// ToClustersUpgradeMap constructs a request body from UpgradeOpts. +func (opts UpgradeOpts) ToClustersUpgradeMap() (map[string]interface{}, error) { + if opts.MaxBatchSize == nil { + defaultMaxBatchSize := 1 + opts.MaxBatchSize = &defaultMaxBatchSize + } + return gophercloud.BuildRequestBody(opts, "") +} + +// Upgrade implements cluster upgrade request. +func Upgrade(client *gophercloud.ServiceClient, id string, opts UpgradeOptsBuilder) (r UpgradeResult) { + b, err := opts.ToClustersUpgradeMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(upgradeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + // ResizeOptsBuilder allows extensions to add additional parameters to the // Resize request. type ResizeOptsBuilder interface { diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 5581b0b749..6f712b5c11 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -39,6 +39,11 @@ type UpdateResult struct { commonResult } +// UpgradeResult is the response of a Upgrade operations. +type UpgradeResult struct { + commonResult +} + // ResizeResult is the response of a Resize operations. type ResizeResult struct { commonResult @@ -60,6 +65,14 @@ func (r UpdateResult) Extract() (string, error) { return s.UUID, err } +func (r UpgradeResult) Extract() (string, error) { + var s struct { + UUID string + } + err := r.ExtractInto(&s) + return s.UUID, err +} + func (r ResizeResult) Extract() (string, error) { var s struct { UUID string diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures.go b/openstack/containerinfra/v1/clusters/testing/fixtures.go index e8264039ea..684984333c 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures.go @@ -286,6 +286,24 @@ func HandleUpdateClusterSuccessfully(t *testing.T) { }) } +var UpgradeResponse = fmt.Sprintf(` +{ + "uuid":"%s" +}`, clusterUUID) + +func HandleUpgradeClusterSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/v1/clusters/"+clusterUUID+"/actions/upgrade", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("X-OpenStack-Request-Id", requestUUID) + w.WriteHeader(http.StatusAccepted) + + fmt.Fprint(w, UpgradeResponse) + }) +} + func HandleDeleteClusterSuccessfully(t *testing.T) { th.Mux.HandleFunc("/v1/clusters/"+clusterUUID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index e0ce2ae951..b31d1f9252 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -152,6 +152,31 @@ func TestUpdateCluster(t *testing.T) { th.AssertDeepEquals(t, clusterUUID, actual) } +func TestUpgradeCluster(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleUpgradeClusterSuccessfully(t) + + var opts clusters.UpgradeOptsBuilder + opts = clusters.UpgradeOpts{ + ClusterTemplate: "0562d357-8641-4759-8fed-8173f02c9633", + } + + sc := fake.ServiceClient() + sc.Endpoint = sc.Endpoint + "v1/" + res := clusters.Upgrade(sc, clusterUUID, opts) + th.AssertNoErr(t, res.Err) + + requestID := res.Header.Get("X-OpenStack-Request-Id") + th.AssertEquals(t, requestUUID, requestID) + + actual, err := res.Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, clusterUUID, actual) +} + func TestDeleteCluster(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/containerinfra/v1/clusters/urls.go b/openstack/containerinfra/v1/clusters/urls.go index f0fcf1aeff..bcffd926ea 100644 --- a/openstack/containerinfra/v1/clusters/urls.go +++ b/openstack/containerinfra/v1/clusters/urls.go @@ -38,6 +38,10 @@ func updateURL(client *gophercloud.ServiceClient, id string) string { return idURL(client, id) } +func upgradeURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("clusters", id, "actions/upgrade") +} + func resizeURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("clusters", id, "actions/resize") } From 7598b05e44dfdb04c2206b9a440a6201497e0698 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 29 Sep 2020 08:34:14 -0600 Subject: [PATCH 1146/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d784c8430a..139abfd3c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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) ## 0.13.0 (September 27, 2020) From 92a3dd07cee8c28cfb95d1a4b621c9ea7a469ffd Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Wed, 30 Sep 2020 07:53:57 -0700 Subject: [PATCH 1147/2296] openstack/networking/v2/extensions/layer3/routers/testing: fix dropped test error (#2033) --- .../v2/extensions/layer3/routers/testing/requests_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index c4d386b63b..4ffcf9baea 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -556,6 +556,7 @@ func TestListL3Agents(t *testing.T) { l3AgentsPages, err := routers.ListL3Agents(fake.ServiceClient(), "fa3a4aaa-c73f-48aa-a603-8c8bf642b7c0").AllPages() th.AssertNoErr(t, err) actual, err := routers.ExtractL3Agents(l3AgentsPages) + th.AssertNoErr(t, err) expected := []routers.L3Agent{ { From b56bf177589a08f858956fa9666ea382233be8ab Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 3 Oct 2020 20:17:35 -0600 Subject: [PATCH 1148/2296] Compute v2: Adding apiversions package (#2037) --- openstack/compute/apiversions/doc.go | 30 +++ openstack/compute/apiversions/errors.go | 13 ++ openstack/compute/apiversions/requests.go | 20 ++ openstack/compute/apiversions/results.go | 67 ++++++ .../compute/apiversions/testing/fixtures.go | 198 ++++++++++++++++++ .../apiversions/testing/requests_test.go | 46 ++++ openstack/compute/apiversions/urls.go | 20 ++ 7 files changed, 394 insertions(+) create mode 100644 openstack/compute/apiversions/doc.go create mode 100644 openstack/compute/apiversions/errors.go create mode 100644 openstack/compute/apiversions/requests.go create mode 100644 openstack/compute/apiversions/results.go create mode 100644 openstack/compute/apiversions/testing/fixtures.go create mode 100644 openstack/compute/apiversions/testing/requests_test.go create mode 100644 openstack/compute/apiversions/urls.go diff --git a/openstack/compute/apiversions/doc.go b/openstack/compute/apiversions/doc.go new file mode 100644 index 0000000000..39688e0c2f --- /dev/null +++ b/openstack/compute/apiversions/doc.go @@ -0,0 +1,30 @@ +/* +Package apiversions provides information and interaction with the different +API versions for the Compute service, code-named Nova. + +Example to List API Versions + + allPages, err := apiversions.List(computeClient).AllPages() + if err != nil { + panic(err) + } + + allVersions, err := apiversions.ExtractAPIVersions(allPages) + if err != nil { + panic(err) + } + + for _, version := range allVersions { + fmt.Printf("%+v\n", version) + } + +Example to Get an API Version + + version, err := apiVersions.Get(computeClient, "v2.1").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", version) +*/ +package apiversions diff --git a/openstack/compute/apiversions/errors.go b/openstack/compute/apiversions/errors.go new file mode 100644 index 0000000000..696f81f793 --- /dev/null +++ b/openstack/compute/apiversions/errors.go @@ -0,0 +1,13 @@ +package apiversions + +import ( + "fmt" +) + +// ErrVersionNotFound is the error when the requested API version +// could not be found. +type ErrVersionNotFound struct{} + +func (e ErrVersionNotFound) Error() string { + return fmt.Sprintf("Unable to find requested API version") +} diff --git a/openstack/compute/apiversions/requests.go b/openstack/compute/apiversions/requests.go new file mode 100644 index 0000000000..244a7acba4 --- /dev/null +++ b/openstack/compute/apiversions/requests.go @@ -0,0 +1,20 @@ +package apiversions + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List lists all the API versions available to end-users. +func List(c *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { + return APIVersionPage{pagination.SinglePageBase(r)} + }) +} + +// Get will get a specific API version, specified by major ID. +func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { + resp, err := client.Get(getURL(client, v), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/compute/apiversions/results.go b/openstack/compute/apiversions/results.go new file mode 100644 index 0000000000..1459e7452b --- /dev/null +++ b/openstack/compute/apiversions/results.go @@ -0,0 +1,67 @@ +package apiversions + +import ( + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// APIVersion represents an API version for the Nova service. +type APIVersion struct { + // ID is the unique identifier of the API version. + ID string `json:"id"` + + // MinVersion is the minimum microversion supported. + MinVersion string `json:"min_version"` + + // Status is the API versions status. + Status string `json:"status"` + + // Updated is the date when the API was last updated. + Updated time.Time `json:"updated"` + + // Version is the maximum microversion supported. + Version string `json:"version"` +} + +// APIVersionPage is the page returned by a pager when traversing over a +// collection of API versions. +type APIVersionPage struct { + pagination.SinglePageBase +} + +// IsEmpty checks whether an APIVersionPage struct is empty. +func (r APIVersionPage) IsEmpty() (bool, error) { + is, err := ExtractAPIVersions(r) + return len(is) == 0, err +} + +// ExtractAPIVersions takes a collection page, extracts all of the elements, +// and returns them a slice of APIVersion structs. It is effectively a cast. +func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { + var s struct { + Versions []APIVersion `json:"versions"` + } + err := (r.(APIVersionPage)).ExtractInto(&s) + return s.Versions, err +} + +// GetResult represents the result of a get operation. +type GetResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts an API version resource. +func (r GetResult) Extract() (*APIVersion, error) { + var s struct { + Version *APIVersion `json:"version"` + } + err := r.ExtractInto(&s) + + if s.Version == nil && err == nil { + return nil, ErrVersionNotFound{} + } + + return s.Version, err +} diff --git a/openstack/compute/apiversions/testing/fixtures.go b/openstack/compute/apiversions/testing/fixtures.go new file mode 100644 index 0000000000..46dd650f8c --- /dev/null +++ b/openstack/compute/apiversions/testing/fixtures.go @@ -0,0 +1,198 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/openstack/compute/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const NovaAPIVersionResponse_20 = ` +{ + "version": { + "id": "v2.0", + "status": "SUPPORTED", + "version": "", + "min_version": "", + "updated": "2011-01-21T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "http://10.1.5.216/compute/v2/" + }, + { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=2" + } + ] + } +} +` + +const NovaAPIVersionResponse_21 = ` +{ + "version": { + "id": "v2.1", + "status": "CURRENT", + "version": "2.87", + "min_version": "2.1", + "updated": "2013-07-23T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "http://10.1.5.216/compute/v2.1/" + }, + { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=2.1" + } + ] + } +} + +` + +const NovaAPIInvalidVersionResponse = ` +{ + "choices": [ + { + "id": "v2.0", + "status": "SUPPORTED", + "links": [ + { + "rel": "self", + "href": "http://10.1.5.216/compute/v2/compute/v3" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=2" + } + ] + }, + { + "id": "v2.1", + "status": "CURRENT", + "links": [ + { + "rel": "self", + "href": "http://10.1.5.216/compute/v2.1/compute/v3" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=2.1" + } + ] + } + ] +} +` + +const NovaAllAPIVersionsResponse = ` +{ + "versions": [ + { + "id": "v2.0", + "status": "SUPPORTED", + "version": "", + "min_version": "", + "updated": "2011-01-21T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "http://10.1.5.216/compute/v2/" + } + ] + }, + { + "id": "v2.1", + "status": "CURRENT", + "version": "2.87", + "min_version": "2.1", + "updated": "2013-07-23T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "http://10.1.5.216/compute/v2.1/" + } + ] + } + ] +} +` + +var NovaAPIVersion20Result = apiversions.APIVersion{ + ID: "v2.0", + Status: "SUPPORTED", + Updated: time.Date(2011, 1, 21, 11, 33, 21, 0, time.UTC), +} + +var NovaAPIVersion21Result = apiversions.APIVersion{ + ID: "v2.1", + Status: "CURRENT", + Updated: time.Date(2013, 7, 23, 11, 33, 21, 0, time.UTC), + MinVersion: "2.1", + Version: "2.87", +} + +var NovaAllAPIVersionResults = []apiversions.APIVersion{ + NovaAPIVersion20Result, + NovaAPIVersion21Result, +} + +func MockListResponse(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", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, NovaAllAPIVersionsResponse) + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/v2.1/", 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, NovaAPIVersionResponse_21) + }) +} + +func MockGetMultipleResponses(t *testing.T) { + th.Mux.HandleFunc("/v3/", 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, NovaAPIInvalidVersionResponse) + }) +} diff --git a/openstack/compute/apiversions/testing/requests_test.go b/openstack/compute/apiversions/testing/requests_test.go new file mode 100644 index 0000000000..ebb75a47fe --- /dev/null +++ b/openstack/compute/apiversions/testing/requests_test.go @@ -0,0 +1,46 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/compute/apiversions" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListAPIVersions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + + actual, err := apiversions.ExtractAPIVersions(allVersions) + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, NovaAllAPIVersionResults, actual) +} + +func TestGetAPIVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + actual, err := apiversions.Get(client.ServiceClient(), "v2.1").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, NovaAPIVersion21Result, *actual) +} + +func TestGetMultipleAPIVersion(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetMultipleResponses(t) + + _, err := apiversions.Get(client.ServiceClient(), "v3").Extract() + th.AssertEquals(t, err.Error(), "Unable to find requested API version") +} diff --git a/openstack/compute/apiversions/urls.go b/openstack/compute/apiversions/urls.go new file mode 100644 index 0000000000..41aebdc5f2 --- /dev/null +++ b/openstack/compute/apiversions/urls.go @@ -0,0 +1,20 @@ +package apiversions + +import ( + "strings" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" +) + +func getURL(c *gophercloud.ServiceClient, version string) string { + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" + return endpoint +} + +func listURL(c *gophercloud.ServiceClient) string { + baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) + endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + return endpoint +} From 841705fad1df2f50b23c31ee60f51702063be7c6 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 3 Oct 2020 20:18:33 -0600 Subject: [PATCH 1149/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 139abfd3c5..bb8931aceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ 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) ## 0.13.0 (September 27, 2020) From ca2a158e5344428f0e4c8f4885c0552117c246d0 Mon Sep 17 00:00:00 2001 From: Chris Pickel Date: Mon, 5 Oct 2020 23:53:13 +0900 Subject: [PATCH 1150/2296] Add ip, ip6, and user_id to servers.ListOpts (#2038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As listed in https://docs.openstack.org/api-guide/compute/server_concepts.html#server-query: * ip * ip6 (New in version 2.5) * user_id (New in version 2.83) And the CLI documentation: --ip Regular expression to match IP addresses --ip6 Regular expression to match IPv6 addresses. Note that this option only applies for non-admin users when using ``--os-compute-api-version`` 2.5 or greater. --user Search by user (admin only) (name or ID) Note the difference between the user_id docs. My understanding is that it’s the same as ip6: the option was initially admin-only, and became generally available with API 2.83 (this is what I have documented). For #2035 --- openstack/compute/v2/servers/requests.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 4c594e2ef2..4e6042409e 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -29,6 +29,14 @@ type ListOpts struct { // Flavor is the name of the flavor in URL format. Flavor string `q:"flavor"` + // IP is a regular expression to match the IPv4 address of the server. + IP string `q:"ip"` + + // This requires the client to be set to microversion 2.5 or later, unless + // the user is an admin. + // IP is a regular expression to match the IPv6 address of the server. + IP6 string `q:"ip6"` + // Name of the server as a string; can be queried with regular expressions. // Realize that ?name=bob returns both bob and bobb. If you need to match bob // only, you can use a regular expression matching the syntax of the @@ -55,6 +63,11 @@ type ListOpts struct { // Setting "AllTenants = true" is required. TenantID string `q:"tenant_id"` + // This requires the client to be set to microversion 2.83 or later, unless + // the user is an admin. + // UserID lists servers for a particular user. + UserID string `q:"user_id"` + // This requires the client to be set to microversion 2.26 or later. // Tags filters on specific server tags. All tags must be present for the server. Tags string `q:"tags"` From 0e6f9ce41455b967839ac9ce6ecf61eaa8d9b283 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 5 Oct 2020 08:55:49 -0600 Subject: [PATCH 1151/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb8931aceb..ab6ca40bf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ IMPROVEMENTS * 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) ## 0.13.0 (September 27, 2020) From 18cd3ba2cde35f2a6fb0689ac6edd469b00e9a6b Mon Sep 17 00:00:00 2001 From: Steffen Neubauer Date: Sat, 31 Oct 2020 16:59:26 +0100 Subject: [PATCH 1152/2296] Networking / routers: Allow updating routers, without specifying the static routes (#2043) * Allow updating routers, without specifying the static routes This is helpful when it is necessary to rename a router, but it is not desired by the user to touch the static routes. The OpenStack API just allows to omit the static routes (e.g. verifyable with `openstack router set --name new_name --debug`). This change makes sure that static routes parameter can be omitted with gophercloud as well. Also added a unit test. * Update documentation * Simplify unittest --- .../v2/extensions/layer3/routers/doc.go | 17 +++++- .../v2/extensions/layer3/routers/requests.go | 2 +- .../layer3/routers/testing/requests_test.go | 59 ++++++++++++++++++- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/routers/doc.go b/openstack/networking/v2/extensions/layer3/routers/doc.go index 9f0cf98c4b..fb20e2ef54 100644 --- a/openstack/networking/v2/extensions/layer3/routers/doc.go +++ b/openstack/networking/v2/extensions/layer3/routers/doc.go @@ -48,7 +48,20 @@ Example to Update a Router updateOpts := routers.UpdateOpts{ Name: "new_name", - Routes: routes, + Routes: &routes, + } + + router, err := routers.Update(networkClient, routerID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update just the Router name, keeping everything else as-is + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + updateOpts := routers.UpdateOpts{ + Name: "new_name", } router, err := routers.Update(networkClient, routerID, updateOpts).Extract() @@ -63,7 +76,7 @@ Example to Remove all Routes from a Router routes := []routers.Route{} updateOpts := routers.UpdateOpts{ - Routes: routes, + Routes: &routes, } router, err := routers.Update(networkClient, routerID, updateOpts).Extract() diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index 3855603236..81665acef4 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -109,7 +109,7 @@ type UpdateOpts struct { AdminStateUp *bool `json:"admin_state_up,omitempty"` Distributed *bool `json:"distributed,omitempty"` GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"` - Routes []Route `json:"routes"` + Routes *[]Route `json:"routes,omitempty"` } // ToRouterUpdateMap builds an update body based on UpdateOpts. diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 4ffcf9baea..36126701ec 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -318,7 +318,7 @@ func TestUpdate(t *testing.T) { gwi := routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"} r := []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}} - options := routers.UpdateOpts{Name: "new_name", GatewayInfo: &gwi, Routes: r} + options := routers.UpdateOpts{Name: "new_name", GatewayInfo: &gwi, Routes: &r} n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) @@ -332,6 +332,61 @@ func TestUpdate(t *testing.T) { th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}) } +func TestUpdateWithoutRoutes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/routers/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, ` +{ + "router": { + "name": "new_name" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "router": { + "status": "ACTIVE", + "external_gateway_info": { + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "external_fixed_ips": [ + {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] + }, + "name": "new_name", + "admin_state_up": true, + "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", + "distributed": false, + "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e", + "routes": [ + { + "nexthop": "10.1.0.10", + "destination": "40.0.1.0/24" + } + ] + } +} + `) + }) + + options := routers.UpdateOpts{Name: "new_name"} + + n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Name, "new_name") + th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}) +} + func TestAllRoutesRemoved(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -371,7 +426,7 @@ func TestAllRoutesRemoved(t *testing.T) { }) r := []routers.Route{} - options := routers.UpdateOpts{Routes: r} + options := routers.UpdateOpts{Routes: &r} n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) From 9e602f1dcf3850be72c9379417174b8ac61d6e9c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 31 Oct 2020 10:00:30 -0600 Subject: [PATCH 1153/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6ca40bf8..8872ad2131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ IMPROVEMENTS * 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) +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 From 35a020fa3d93c17c480efa4c2f2aa4b2113d2c95 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 10 Nov 2020 07:09:32 +0200 Subject: [PATCH 1154/2296] DNS v2: Zone Transfer Request/Accept (#2041) * DNS v2: Added zone transfer request feature * Added CRUD operations * Added unittests * DNS v2: Added zone transfer accept feature * Added CRUD operations * Added unittests * Added acceptance tests * Added more tests * Updated acceptance test to use Identity v3 instead of v2 * Removed version from transfer request fields and fixtures * Fixed spacing in docs Co-authored-by: Mike Durnosvystov --- acceptance/openstack/dns/v2/dns.go | 99 ++++++++ acceptance/openstack/dns/v2/transfers_test.go | 100 ++++++++ openstack/dns/v2/transfer/accept/doc.go | 43 ++++ openstack/dns/v2/transfer/accept/requests.go | 88 +++++++ openstack/dns/v2/transfer/accept/results.go | 104 ++++++++ .../transfer/accept/testing/accepts_test.go | 83 +++++++ .../dns/v2/transfer/accept/testing/doc.go | 2 + .../v2/transfer/accept/testing/fixtures.go | 205 ++++++++++++++++ openstack/dns/v2/transfer/accept/urls.go | 17 ++ openstack/dns/v2/transfer/request/doc.go | 42 ++++ openstack/dns/v2/transfer/request/requests.go | 136 ++++++++++ openstack/dns/v2/transfer/request/results.go | 123 ++++++++++ .../dns/v2/transfer/request/testing/doc.go | 2 + .../v2/transfer/request/testing/fixtures.go | 232 ++++++++++++++++++ .../transfer/request/testing/requests_test.go | 113 +++++++++ openstack/dns/v2/transfer/request/urls.go | 21 ++ 16 files changed, 1410 insertions(+) create mode 100644 acceptance/openstack/dns/v2/transfers_test.go create mode 100644 openstack/dns/v2/transfer/accept/doc.go create mode 100644 openstack/dns/v2/transfer/accept/requests.go create mode 100644 openstack/dns/v2/transfer/accept/results.go create mode 100644 openstack/dns/v2/transfer/accept/testing/accepts_test.go create mode 100644 openstack/dns/v2/transfer/accept/testing/doc.go create mode 100644 openstack/dns/v2/transfer/accept/testing/fixtures.go create mode 100644 openstack/dns/v2/transfer/accept/urls.go create mode 100644 openstack/dns/v2/transfer/request/doc.go create mode 100644 openstack/dns/v2/transfer/request/requests.go create mode 100644 openstack/dns/v2/transfer/request/results.go create mode 100644 openstack/dns/v2/transfer/request/testing/doc.go create mode 100644 openstack/dns/v2/transfer/request/testing/fixtures.go create mode 100644 openstack/dns/v2/transfer/request/testing/requests_test.go create mode 100644 openstack/dns/v2/transfer/request/urls.go diff --git a/acceptance/openstack/dns/v2/dns.go b/acceptance/openstack/dns/v2/dns.go index 791703e87b..7fc34b70e0 100644 --- a/acceptance/openstack/dns/v2/dns.go +++ b/acceptance/openstack/dns/v2/dns.go @@ -6,6 +6,8 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -116,6 +118,73 @@ func CreateSecondaryZone(t *testing.T, client *gophercloud.ServiceClient) (*zone return newZone, nil } +// CreateTransferRequest will create a Transfer Request to a spectified Zone. An error will +// be returned if the zone transfer request was unable to be created. +func CreateTransferRequest(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zone, targetProjectID string) (*transferRequests.TransferRequest, error) { + t.Logf("Attempting to create Transfer Request to Zone: %s", zone.Name) + + createOpts := transferRequests.CreateOpts{ + TargetProjectID: targetProjectID, + Description: "Test transfer request", + } + + transferRequest, err := transferRequests.Create(client, zone.ID, createOpts).Extract() + if err != nil { + return transferRequest, err + } + + if err := WaitForTransferRequestStatus(client, transferRequest, "ACTIVE"); err != nil { + return transferRequest, err + } + + newTransferRequest, err := transferRequests.Get(client, transferRequest.ID).Extract() + if err != nil { + return transferRequest, err + } + + t.Logf("Created Transfer Request for Zone: %s", zone.Name) + + th.AssertEquals(t, newTransferRequest.ZoneID, zone.ID) + th.AssertEquals(t, newTransferRequest.ZoneName, zone.Name) + + return newTransferRequest, nil +} + +// CreateTransferAccept will accept a spectified Transfer Request. An error will +// be returned if the zone transfer accept was unable to be created. +func CreateTransferAccept(t *testing.T, client *gophercloud.ServiceClient, zoneTransferRequestID string, key string) (*transferAccepts.TransferAccept, error) { + t.Logf("Attempting to accept specified transfer reqeust: %s", zoneTransferRequestID) + createOpts := transferAccepts.CreateOpts{ + ZoneTransferRequestID: zoneTransferRequestID, + Key: key, + } + transferAccept, err := transferAccepts.Create(client, createOpts).Extract() + if err != nil { + return transferAccept, err + } + if err := WaitForTransferAcceptStatus(client, transferAccept, "COMPLETE"); err != nil { + return transferAccept, err + } + newTransferAccept, err := transferAccepts.Get(client, transferAccept.ID).Extract() + if err != nil { + return transferAccept, err + } + t.Logf("Accepted Transfer Request: %s", zoneTransferRequestID) + th.AssertEquals(t, newTransferAccept.ZoneTransferRequestID, zoneTransferRequestID) + return newTransferAccept, nil +} + +// DeleteTransferRequest will delete a specified zone transfer request. A fatal error will occur if +// the transfer request failed to be deleted. This works best when used as a deferred +// function. +func DeleteTransferRequest(t *testing.T, client *gophercloud.ServiceClient, tr *transferRequests.TransferRequest) { + err := transferRequests.Delete(client, tr.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete zone transfer request %s: %v", tr.ID, err) + } + t.Logf("Deleted zone transfer request: %s", tr.ID) +} + // DeleteRecordSet will delete a specified record set. A fatal error will occur if // the record set failed to be deleted. This works best when used as a deferred // function. @@ -157,6 +226,36 @@ func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.Re }) } +// WaitForTransferRequestStatus will poll a transfer reqeust's status until it either matches +// the specified status or the status becomes ERROR. +func WaitForTransferRequestStatus(client *gophercloud.ServiceClient, tr *transferRequests.TransferRequest, status string) error { + return tools.WaitFor(func() (bool, error) { + current, err := transferRequests.Get(client, tr.ID).Extract() + if err != nil { + return false, err + } + if current.Status == status { + return true, nil + } + return false, nil + }) +} + +// WaitForTransferAcceptStatus will poll a transfer accept's status until it either matches +// the specified status or the status becomes ERROR. +func WaitForTransferAcceptStatus(client *gophercloud.ServiceClient, ta *transferAccepts.TransferAccept, status string) error { + return tools.WaitFor(func() (bool, error) { + current, err := transferAccepts.Get(client, ta.ID).Extract() + if err != nil { + return false, err + } + if current.Status == status { + return true, nil + } + return false, nil + }) +} + // WaitForZoneStatus will poll a zone's status until it either matches // the specified status or the status becomes ERROR. func WaitForZoneStatus(client *gophercloud.ServiceClient, zone *zones.Zone, status string) error { diff --git a/acceptance/openstack/dns/v2/transfers_test.go b/acceptance/openstack/dns/v2/transfers_test.go new file mode 100644 index 0000000000..ef06284f2e --- /dev/null +++ b/acceptance/openstack/dns/v2/transfers_test.go @@ -0,0 +1,100 @@ +// +build acceptance dns transfers + +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" + 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" +) + +func TestTransferRequestCRUD(t *testing.T) { + // Create new Zone + clients.RequireDNS(t) + + client, err := clients.NewDNSV2Client() + th.AssertNoErr(t, err) + + zone, err := CreateZone(t, client) + th.AssertNoErr(t, err) + defer DeleteZone(t, client, zone) + + // Create transfers request to new tenant + transferRequest, err := CreateTransferRequest(t, client, zone, "123") + th.AssertNoErr(t, err) + defer DeleteTransferRequest(t, client, transferRequest) + + allTransferRequestsPages, err := transferRequests.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allTransferRequests, err := transferRequests.ExtractTransferRequests(allTransferRequestsPages) + th.AssertNoErr(t, err) + + var foundRequest bool + for _, tr := range allTransferRequests { + tools.PrintResource(t, &tr) + if transferRequest.ZoneID == tr.ZoneID { + foundRequest = true + } + } + th.AssertEquals(t, foundRequest, true) + + description := "new description" + updateOpts := transferRequests.UpdateOpts{ + Description: description, + } + + newTransferRequest, err := transferRequests.Update(client, transferRequest.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, &newTransferRequest) + th.AssertEquals(t, newTransferRequest.Description, description) +} + +func TestTransferRequestAccept(t *testing.T) { + // Create new project + clients.RequireAdmin(t) + + identityClient, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + project, err := identity.CreateProject(t, identityClient, nil) + th.AssertNoErr(t, err) + defer identity.DeleteProject(t, identityClient, project.ID) + + // Create new Zone + clients.RequireDNS(t) + + client, err := clients.NewDNSV2Client() + th.AssertNoErr(t, err) + + zone, err := CreateZone(t, client) + th.AssertNoErr(t, err) + defer DeleteZone(t, client, zone) + + // Create transfers request to new tenant + transferRequest, err := CreateTransferRequest(t, client, zone, project.ID) + + // Accept Zone Transfer Request + transferAccept, err := CreateTransferAccept(t, client, transferRequest.ID, transferRequest.Key) + + allTransferAcceptsPages, err := transferAccepts.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allTransferAcceptsPages) + th.AssertNoErr(t, err) + + var foundAccept bool + for _, ta := range allTransferAccepts { + tools.PrintResource(t, &ta) + if transferAccept.ZoneID == ta.ZoneID { + foundAccept = true + } + } + th.AssertEquals(t, foundAccept, true) +} diff --git a/openstack/dns/v2/transfer/accept/doc.go b/openstack/dns/v2/transfer/accept/doc.go new file mode 100644 index 0000000000..1d2a6d53c4 --- /dev/null +++ b/openstack/dns/v2/transfer/accept/doc.go @@ -0,0 +1,43 @@ +/* +Package zones provides information and interaction with the zone API +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) + } + + allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allPages) + if err != nil { + panic(err) + } + + 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) + } + +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) + } +*/ +package accept diff --git a/openstack/dns/v2/transfer/accept/requests.go b/openstack/dns/v2/transfer/accept/requests.go new file mode 100644 index 0000000000..4668156c77 --- /dev/null +++ b/openstack/dns/v2/transfer/accept/requests.go @@ -0,0 +1,88 @@ +package accept + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add parameters to the List request. +type ListOptsBuilder interface { + ToTransferAcceptListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the server attributes you want to see returned. +// https://developer.openstack.org/api-ref/dns/ +type ListOpts struct { + Status string `q:"status"` +} + +// ToTransferAcceptListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTransferAcceptListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List implements a transfer accept List request. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := baseURL(client) + if opts != nil { + query, err := opts.ToTransferAcceptListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return TransferAcceptPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get returns information about a transfer accept, given its ID. +func Get(client *gophercloud.ServiceClient, transferAcceptID string) (r GetResult) { + resp, err := client.Get(resourceURL(client, transferAcceptID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateOptsBuilder allows extensions to add additional attributes to the +// Create request. +type CreateOptsBuilder interface { + ToTransferAcceptCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies the attributes used to create a transfer accept. +type CreateOpts struct { + // Key is used as part of the zone transfer accept process. + // This is only shown to the creator, and must be communicated out of band. + Key string `json:"key" required:"true"` + + // ZoneTransferRequestID is ID for this zone transfer request + ZoneTransferRequestID string `json:"zone_transfer_request_id" required:"true"` +} + +// ToTransferAcceptCreateMap formats an CreateOpts structure into a request body. +func (opts CreateOpts) ToTransferAcceptCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +// Create implements a transfer accept create request. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTransferAcceptCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{http.StatusCreated, http.StatusAccepted}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/dns/v2/transfer/accept/results.go b/openstack/dns/v2/transfer/accept/results.go new file mode 100644 index 0000000000..5a820c30a7 --- /dev/null +++ b/openstack/dns/v2/transfer/accept/results.go @@ -0,0 +1,104 @@ +package accept + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract interprets a GetResult, CreateResult as a TransferAccept. +// An error is returned if the original call or the extraction failed. +func (r commonResult) Extract() (*TransferAccept, error) { + var s *TransferAccept + err := r.ExtractInto(&s) + return s, err +} + +// CreateResult is the result of a Create request. Call its Extract method +// to interpret the result as a TransferAccept. +type CreateResult struct { + commonResult +} + +// GetResult is the result of a Get request. Call its Extract method +// to interpret the result as a TransferAccept. +type GetResult struct { + commonResult +} + +// TransferAcceptPage is a single page of TransferAccept results. +type TransferAcceptPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if the page contains no results. +func (r TransferAcceptPage) IsEmpty() (bool, error) { + s, err := ExtractTransferAccepts(r) + return len(s) == 0, err +} + +// ExtractTransferAccepts extracts a slice of TransferAccept from a List result. +func ExtractTransferAccepts(r pagination.Page) ([]TransferAccept, error) { + var s struct { + TransferAccepts []TransferAccept `json:"transfer_accepts"` + } + err := (r.(TransferAcceptPage)).ExtractInto(&s) + return s.TransferAccepts, err +} + +// TransferAccept represents a Zone transfer accept task. +type TransferAccept struct { + // ID for this zone transfer accept. + ID string `json:"id"` + + // Status is current status of the zone transfer request. + Status string `json:"status"` + + // ProjectID identifies the project/tenant owning this resource. + ProjectID string `json:"project_id"` + + // ZoneID is the ID for the zone that was being exported. + ZoneID string `json:"zone_id"` + + // Key is used as part of the zone transfer accept process. + // This is only shown to the creator, and must be communicated out of band. + Key string `json:"key"` + + // ZoneTransferRequestID is ID for this zone transfer request + ZoneTransferRequestID string `json:"zone_transfer_request_id"` + + // CreatedAt is the date when the zone transfer accept was created. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the date when the last change was made to the zone transfer accept. + UpdatedAt time.Time `json:"-"` + + // Links includes HTTP references to the itself, useful for passing along + // to other APIs that might want a server reference. + Links map[string]interface{} `json:"links"` +} + +func (r *TransferAccept) UnmarshalJSON(b []byte) error { + type tmp TransferAccept + 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 = TransferAccept(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} diff --git a/openstack/dns/v2/transfer/accept/testing/accepts_test.go b/openstack/dns/v2/transfer/accept/testing/accepts_test.go new file mode 100644 index 0000000000..49d1269005 --- /dev/null +++ b/openstack/dns/v2/transfer/accept/testing/accepts_test.go @@ -0,0 +1,83 @@ +package testing + +import ( + "testing" + + transferAccepts "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/accept" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + count := 0 + err := transferAccepts.List(client.ServiceClient(), nil).EachPage( + func(page pagination.Page) (bool, error) { + count++ + actual, err := transferAccepts.ExtractTransferAccepts(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedTransferAcceptSlice, actual) + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, count) +} + +func TestListWithOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFilteredListSuccessfully(t) + + listOpts := transferAccepts.ListOpts{ + Status: "ACTIVE", + } + + allPages, err := transferAccepts.List(client.ServiceClient(), listOpts).AllPages() + th.AssertNoErr(t, err) + allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allPages) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, len(allTransferAccepts)) +} + +func TestListAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + allPages, err := transferAccepts.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allPages) + th.AssertNoErr(t, err) + th.CheckEquals(t, 2, len(allTransferAccepts)) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := transferAccepts.Get( + client.ServiceClient(), "92236f39-0fad-4f8f-bf25-fbdf027de34d").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &FirstTransferAccept, actual) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t) + + createOpts := transferAccepts.CreateOpts{ + Key: "M2KA0Y20", + ZoneTransferRequestID: "fc46bb1f-bdf0-4e67-96e0-f8c04f26261c", + } + + actual, err := transferAccepts.Create( + client.ServiceClient(), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &CreatedTransferAccept, actual) +} diff --git a/openstack/dns/v2/transfer/accept/testing/doc.go b/openstack/dns/v2/transfer/accept/testing/doc.go new file mode 100644 index 0000000000..7f49ddec66 --- /dev/null +++ b/openstack/dns/v2/transfer/accept/testing/doc.go @@ -0,0 +1,2 @@ +// transfer requests unit tests +package testing diff --git a/openstack/dns/v2/transfer/accept/testing/fixtures.go b/openstack/dns/v2/transfer/accept/testing/fixtures.go new file mode 100644 index 0000000000..79ffd5d6fe --- /dev/null +++ b/openstack/dns/v2/transfer/accept/testing/fixtures.go @@ -0,0 +1,205 @@ +package testing + +import ( + "fmt" + "net/http" + s "strings" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + transferAccepts "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/accept" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput is a sample response to a List call. +const ListOutput = ` +{ + "transfer_accepts": [ + { + "id": "92236f39-0fad-4f8f-bf25-fbdf027de34d", + "status": "COMPLETE", + "project_id": "9f3cfb08bf52469abe598e127676cd57", + "zone_id": "cd046f4b-f4dc-4e41-b946-1a2d32e1be40", + "key": "M2KA0Y20", + "zone_transfer_request_id": "fc46bb1f-bdf0-4e67-96e0-f8c04f26261c", + "created_at": "2020-10-12T08:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/92236f39-0fad-4f8f-bf25-fbdf027de34d", + "zone": "https://127.0.0.1:9001/v2/zones/cd046f4b-f4dc-4e41-b946-1a2d32e1be40" + } + }, + { + "id": "f785ef12-7ee0-4c30-bd67-a2b9edba0dff", + "status": "ACTIVE", + "project_id": "9f3cfb08bf52469abe598e127676cd57", + "zone_id": "30d67a9a-d6df-4ba7-9b55-fb49e7987f84", + "key": "SDF32HJ1", + "zone_transfer_request_id": "c5d11193-72ea-4d9f-ba04-7f80e99627fa", + "created_at": "2020-10-12T09:38:58.000000", + "updated_at": "2020-10-12T09:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/f785ef12-7ee0-4c30-bd67-a2b9edba0dff", + "zone": "https://127.0.0.1:9001/v2/zones/30d67a9a-d6df-4ba7-9b55-fb49e7987f84" + } + } + ] +} +` + +// FilteredListOutput is a sample response to a List call with Opts. +const FilteredListOutput = ` +{ + "transfer_accepts": [ + { + "id": "f785ef12-7ee0-4c30-bd67-a2b9edba0dff", + "status": "ACTIVE", + "project_id": "9f3cfb08bf52469abe598e127676cd57", + "zone_id": "30d67a9a-d6df-4ba7-9b55-fb49e7987f84", + "key": "SDF32HJ1", + "zone_transfer_request_id": "c5d11193-72ea-4d9f-ba04-7f80e99627fa", + "created_at": "2020-10-12T09:38:58.000000", + "updated_at": "2020-10-12T09:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/f785ef12-7ee0-4c30-bd67-a2b9edba0dff", + "zone": "https://127.0.0.1:9001/v2/zones/30d67a9a-d6df-4ba7-9b55-fb49e7987f84" + } + } + ] +} +` + +// GetOutput is a sample response to a Get call. +const GetOutput = ` +{ + "id": "92236f39-0fad-4f8f-bf25-fbdf027de34d", + "status": "COMPLETE", + "project_id": "9f3cfb08bf52469abe598e127676cd57", + "zone_id": "cd046f4b-f4dc-4e41-b946-1a2d32e1be40", + "key": "M2KA0Y20", + "zone_transfer_request_id": "fc46bb1f-bdf0-4e67-96e0-f8c04f26261c", + "created_at": "2020-10-12T08:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/92236f39-0fad-4f8f-bf25-fbdf027de34d", + "zone": "https://127.0.0.1:9001/v2/zones/cd046f4b-f4dc-4e41-b946-1a2d32e1be40" + } +} +` + +// FirstTransferAccept is the first result in ListOutput +var FirstTransferAcceptCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-10-12T08:38:58.000000") +var FirstTransferAccept = transferAccepts.TransferAccept{ + ID: "92236f39-0fad-4f8f-bf25-fbdf027de34d", + ZoneID: "cd046f4b-f4dc-4e41-b946-1a2d32e1be40", + ProjectID: "9f3cfb08bf52469abe598e127676cd57", + ZoneTransferRequestID: "fc46bb1f-bdf0-4e67-96e0-f8c04f26261c", + Key: "M2KA0Y20", + Status: "COMPLETE", + CreatedAt: FirstTransferAcceptCreatedAt, + Links: map[string]interface{}{ + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/92236f39-0fad-4f8f-bf25-fbdf027de34d", + "zone": "https://127.0.0.1:9001/v2/zones/cd046f4b-f4dc-4e41-b946-1a2d32e1be40", + }, +} + +// SecondTransferRequest is the second result in ListOutput +var SecondTransferAcceptCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-10-12T09:38:58.000000") +var SecondTransferAcceptUpdatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-10-12T09:38:58.000000") +var SecondTransferAccept = transferAccepts.TransferAccept{ + ID: "f785ef12-7ee0-4c30-bd67-a2b9edba0dff", + Status: "ACTIVE", + ProjectID: "9f3cfb08bf52469abe598e127676cd57", + ZoneID: "30d67a9a-d6df-4ba7-9b55-fb49e7987f84", + ZoneTransferRequestID: "c5d11193-72ea-4d9f-ba04-7f80e99627fa", + Key: "SDF32HJ1", + CreatedAt: SecondTransferAcceptCreatedAt, + UpdatedAt: SecondTransferAcceptUpdatedAt, + Links: map[string]interface{}{ + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/f785ef12-7ee0-4c30-bd67-a2b9edba0dff", + "zone": "https://127.0.0.1:9001/v2/zones/30d67a9a-d6df-4ba7-9b55-fb49e7987f84", + }, +} + +// ExpectedTransferAcceptSlice is the slice of results that should be parsed +// from ListOutput, in the expected order. +var ExpectedTransferAcceptSlice = []transferAccepts.TransferAccept{FirstTransferAccept, SecondTransferAccept} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + baseURL := "/zones/tasks/transfer_accepts" + th.Mux.HandleFunc(baseURL, + 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, ListOutput) + }) +} + +// HandleFilteredListSuccessfully configures the test server to respond to a List request with Opts. +func HandleFilteredListSuccessfully(t *testing.T) { + baseURL := "/zones/tasks/transfer_accepts" + th.Mux.HandleFunc(baseURL, + 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, FilteredListOutput) + }) +} + +// HandleGetSuccessfully configures the test server to respond to a List request. +func HandleGetSuccessfully(t *testing.T) { + baseURL := "/zones/tasks/transfer_accepts" + th.Mux.HandleFunc(s.Join([]string{baseURL, FirstTransferAccept.ID}, "/"), + 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, GetOutput) + }) +} + +// CreateTransferAccept is a sample request to create a zone. +const CreateTransferAccept = ` +{ + "key": "M2KA0Y20", + "zone_transfer_request_id": "fc46bb1f-bdf0-4e67-96e0-f8c04f26261c" +} +` + +// CreateTransferAcceptResponse is a sample response to a create request. +const CreateTransferAcceptResponse = ` +{ + "id": "92236f39-0fad-4f8f-bf25-fbdf027de34d", + "zone_transfer_request_id": "fc46bb1f-bdf0-4e67-96e0-f8c04f26261c", + "project_id": "9f3cfb08bf52469abe598e127676cd57", + "key": "M2KA0Y20", + "status": "COMPLETE", + "zone_id": "cd046f4b-f4dc-4e41-b946-1a2d32e1be40", + "created_at": "2020-10-12T08:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/92236f39-0fad-4f8f-bf25-fbdf027de34d", + "zone": "https://127.0.0.1:9001/v2/zones/cd046f4b-f4dc-4e41-b946-1a2d32e1be40" + } +} +` + +// CreatedTransferRequest is the expected created zone transfer request. +var CreatedTransferAccept = FirstTransferAccept + +// HandleTransferRequestCreationSuccessfully configures the test server to respond to a Create request. +func HandleCreateSuccessfully(t *testing.T) { + baseURL := "/zones/tasks/transfer_accepts" + th.Mux.HandleFunc(baseURL, + 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, CreateTransferAccept) + + w.WriteHeader(http.StatusCreated) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, CreateTransferAcceptResponse) + }) +} diff --git a/openstack/dns/v2/transfer/accept/urls.go b/openstack/dns/v2/transfer/accept/urls.go new file mode 100644 index 0000000000..f7e1b69ea5 --- /dev/null +++ b/openstack/dns/v2/transfer/accept/urls.go @@ -0,0 +1,17 @@ +package accept + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "zones" + tasksPath = "tasks" + resourcePath = "transfer_accepts" +) + +func baseURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, tasksPath, resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, transferAcceptID string) string { + return c.ServiceURL(rootPath, tasksPath, resourcePath, transferAcceptID) +} diff --git a/openstack/dns/v2/transfer/request/doc.go b/openstack/dns/v2/transfer/request/doc.go new file mode 100644 index 0000000000..05316687e5 --- /dev/null +++ b/openstack/dns/v2/transfer/request/doc.go @@ -0,0 +1,42 @@ +/* +Package zones provides information and interaction with the zone API +resource for the OpenStack DNS service. + +Example to List Zone Transfer Requests + + allPages, err := transferRequests.List(dnsClient, nil).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) + } + +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) + } + +Example to Delete a Zone Transfer Request + + transferID := "99d10f68-5623-4491-91a0-6daafa32b60e" + err := transferRequests.Delete(dnsClient, transferID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package request diff --git a/openstack/dns/v2/transfer/request/requests.go b/openstack/dns/v2/transfer/request/requests.go new file mode 100644 index 0000000000..c9d3feb83b --- /dev/null +++ b/openstack/dns/v2/transfer/request/requests.go @@ -0,0 +1,136 @@ +package request + +import ( + "net/http" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add parameters to the List request. +type ListOptsBuilder interface { + ToTransferRequestListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the server attributes you want to see returned. +// https://developer.openstack.org/api-ref/dns/ +type ListOpts struct { + Status string `q:"status"` +} + +// ToTransferRequestListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTransferRequestListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List implements a transfer request List request. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := baseURL(client) + if opts != nil { + query, err := opts.ToTransferRequestListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return TransferRequestPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get returns information about a transfer request, given its ID. +func Get(client *gophercloud.ServiceClient, transferRequestID string) (r GetResult) { + resp, err := client.Get(resourceURL(client, transferRequestID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateOptsBuilder allows extensions to add additional attributes to the +// Create request. +type CreateOptsBuilder interface { + ToTransferRequestCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies the attributes used to create a transfer request. +type CreateOpts struct { + // TargetProjectID is ID that the request will be limited to. No other project + // will be allowed to accept this request. + TargetProjectID string `json:"target_project_id,omitempty"` + + // Description of the transfer request. + Description string `json:"description,omitempty"` +} + +// ToTransferRequestCreateMap formats an CreateOpts structure into a request body. +func (opts CreateOpts) ToTransferRequestCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +// Create implements a transfer request create request. +func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTransferRequestCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{http.StatusCreated, http.StatusAccepted}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional attributes to the +// Update request. +type UpdateOptsBuilder interface { + ToTransferRequestUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts specifies the attributes to update a transfer request. +type UpdateOpts struct { + // TargetProjectID is ID that the request will be limited to. No other project + // will be allowed to accept this request. + TargetProjectID string `json:"target_project_id,omitempty"` + + // Description of the transfer request. + Description string `json:"description,omitempty"` +} + +// ToTransferRequestUpdateMap formats an UpdateOpts structure into a request body. +func (opts UpdateOpts) ToTransferRequestUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + return b, nil +} + +// Update implements a transfer request update request. +func Update(client *gophercloud.ServiceClient, transferID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToTransferRequestUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Patch(resourceURL(client, transferID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{http.StatusOK, http.StatusAccepted}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete implements a transfer request delete request. +func Delete(client *gophercloud.ServiceClient, transferID string) (r DeleteResult) { + resp, err := client.Delete(resourceURL(client, transferID), &gophercloud.RequestOpts{ + OkCodes: []int{http.StatusNoContent}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/dns/v2/transfer/request/results.go b/openstack/dns/v2/transfer/request/results.go new file mode 100644 index 0000000000..efb274232f --- /dev/null +++ b/openstack/dns/v2/transfer/request/results.go @@ -0,0 +1,123 @@ +package request + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract interprets a GetResult, CreateResult or UpdateResult as a TransferRequest. +// An error is returned if the original call or the extraction failed. +func (r commonResult) Extract() (*TransferRequest, error) { + var s *TransferRequest + err := r.ExtractInto(&s) + return s, err +} + +// CreateResult is the result of a Create request. Call its Extract method +// to interpret the result as a TransferRequest. +type CreateResult struct { + commonResult +} + +// GetResult is the result of a Get request. Call its Extract method +// to interpret the result as a TransferRequest. +type GetResult struct { + commonResult +} + +// UpdateResult is the result of an Update request. Call its Extract method +// to interpret the result as a TransferRequest. +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 +} + +// TransferRequestPage is a single page of TransferRequest results. +type TransferRequestPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if the page contains no results. +func (r TransferRequestPage) IsEmpty() (bool, error) { + s, err := ExtractTransferRequests(r) + return len(s) == 0, err +} + +// ExtractTransferRequests extracts a slice of TransferRequest from a List result. +func ExtractTransferRequests(r pagination.Page) ([]TransferRequest, error) { + var s struct { + TransferRequests []TransferRequest `json:"transfer_requests"` + } + err := (r.(TransferRequestPage)).ExtractInto(&s) + return s.TransferRequests, err +} + +// TransferRequest represents a Zone transfer request task. +type TransferRequest struct { + // ID uniquely identifies this transfer request zone amongst all other transfer requests, + // including those not accessible to the current tenant. + ID string `json:"id"` + + // ZoneID is the ID for the zone that is being exported. + ZoneID string `json:"zone_id"` + + // Name is the name of the zone that is being exported. + ZoneName string `json:"zone_name"` + + // ProjectID identifies the project/tenant owning this resource. + ProjectID string `json:"project_id"` + + // TargetProjectID identifies the project/tenant to transfer this resource. + TargetProjectID string `json:"target_project_id"` + + // Key is used as part of the zone transfer accept process. + // This is only shown to the creator, and must be communicated out of band. + Key string `json:"key"` + + // Description for the resource. + Description string `json:"description"` + + // Status is the status of the resource. + Status string `json:"status"` + + // CreatedAt is the date when the zone transfer request was created. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the date when the last change was made to the zone transfer request. + UpdatedAt time.Time `json:"-"` + + // Links includes HTTP references to the itself, useful for passing along + // to other APIs that might want a server reference. + Links map[string]interface{} `json:"links"` +} + +func (r *TransferRequest) UnmarshalJSON(b []byte) error { + type tmp TransferRequest + 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 = TransferRequest(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} diff --git a/openstack/dns/v2/transfer/request/testing/doc.go b/openstack/dns/v2/transfer/request/testing/doc.go new file mode 100644 index 0000000000..7f49ddec66 --- /dev/null +++ b/openstack/dns/v2/transfer/request/testing/doc.go @@ -0,0 +1,2 @@ +// transfer requests unit tests +package testing diff --git a/openstack/dns/v2/transfer/request/testing/fixtures.go b/openstack/dns/v2/transfer/request/testing/fixtures.go new file mode 100644 index 0000000000..bf619ed8d9 --- /dev/null +++ b/openstack/dns/v2/transfer/request/testing/fixtures.go @@ -0,0 +1,232 @@ +package testing + +import ( + "fmt" + "net/http" + s "strings" + "testing" + "time" + + "github.com/gophercloud/gophercloud" + transferRequests "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/request" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput is a sample response to a List call. +const ListOutput = ` +{ + "transfer_requests": [ + { + "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", + "zone_id": "a6a8515c-5d80-48c0-955b-fde631b59791", + "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", + "target_project_id": "05d98711-b3a1-4264-a395-f46383671ee6", + "description": "This is a first example zone transfer request.", + "key": "KJSDH23Z", + "status": "ACTIVE", + "zone_name": "example1.org.", + "created_at": "2020-10-12T08:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" + } + }, + { + "id": "34c4561c-9205-4386-9df5-167436f5a222", + "zone_id": "572ba08c-d929-4c70-8e42-03824bb24ca2", + "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", + "target_project_id": "05d98711-b3a1-4264-a395-f46383671ee6", + "description": "This is second example zone transfer request.", + "key": "KSDFJ22H", + "status": "ACTIVE", + "zone_name": "example2.org.", + "created_at": "2020-10-12T09:38:58.000000", + "updated_at": "2020-10-12T10:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/34c4561c-9205-4386-9df5-167436f5a222" + } + } + ], + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests" + } +} +` + +// GetOutput is a sample response to a Get call. +const GetOutput = ` +{ + "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", + "zone_id": "a6a8515c-5d80-48c0-955b-fde631b59791", + "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", + "target_project_id": "05d98711-b3a1-4264-a395-f46383671ee6", + "description": "This is a first example zone transfer request.", + "key": "KJSDH23Z", + "status": "ACTIVE", + "zone_name": "example1.org.", + "created_at": "2020-10-12T08:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" + } +} +` + +// FirstTransferRequest is the first result in ListOutput +var FirstTransferRequestCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-10-12T08:38:58.000000") +var FirstTransferRequest = transferRequests.TransferRequest{ + ID: "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", + ZoneID: "a6a8515c-5d80-48c0-955b-fde631b59791", + ProjectID: "4335d1f0-f793-11e2-b778-0800200c9a66", + TargetProjectID: "05d98711-b3a1-4264-a395-f46383671ee6", + ZoneName: "example1.org.", + Key: "KJSDH23Z", + Description: "This is a first example zone transfer request.", + Status: "ACTIVE", + CreatedAt: FirstTransferRequestCreatedAt, + Links: map[string]interface{}{ + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", + }, +} + +// SecondTransferRequest is the second result in ListOutput +var SecondTransferRequestCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-10-12T09:38:58.000000") +var SecondTransferRequestUpdatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-10-12T10:38:58.000000") +var SecondTransferRequest = transferRequests.TransferRequest{ + ID: "34c4561c-9205-4386-9df5-167436f5a222", + ZoneID: "572ba08c-d929-4c70-8e42-03824bb24ca2", + ProjectID: "4335d1f0-f793-11e2-b778-0800200c9a66", + TargetProjectID: "05d98711-b3a1-4264-a395-f46383671ee6", + ZoneName: "example2.org.", + Key: "KSDFJ22H", + Description: "This is second example zone transfer request.", + Status: "ACTIVE", + CreatedAt: SecondTransferRequestCreatedAt, + UpdatedAt: SecondTransferRequestUpdatedAt, + Links: map[string]interface{}{ + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/34c4561c-9205-4386-9df5-167436f5a222", + }, +} + +// ExpectedTransferRequestsSlice is the slice of results that should be parsed +// from ListOutput, in the expected order. +var ExpectedTransferRequestsSlice = []transferRequests.TransferRequest{FirstTransferRequest, SecondTransferRequest} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + baseURL := "/zones/tasks/transfer_requests" + th.Mux.HandleFunc(baseURL, + 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, ListOutput) + }) +} + +// HandleGetSuccessfully configures the test server to respond to a List request. +func HandleGetSuccessfully(t *testing.T) { + baseURL := "/zones/tasks/transfer_requests" + th.Mux.HandleFunc(s.Join([]string{baseURL, FirstTransferRequest.ID}, "/"), + 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, GetOutput) + }) +} + +// CreateTransferRequest is a sample request to create a zone. +const CreateTransferRequest = ` +{ + "target_project_id": "05d98711-b3a1-4264-a395-f46383671ee6", + "description": "This is a first example zone transfer request." +} +` + +// CreateZoneResponse is a sample response to a create request. +const CreateTransferRequestResponse = ` +{ + "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", + "zone_id": "a6a8515c-5d80-48c0-955b-fde631b59791", + "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", + "target_project_id": "05d98711-b3a1-4264-a395-f46383671ee6", + "description": "This is a first example zone transfer request.", + "key": "KJSDH23Z", + "status": "ACTIVE", + "zone_name": "example1.org.", + "created_at": "2020-10-12T08:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" + } +} +` + +// CreatedTransferRequest is the expected created zone transfer request. +var CreatedTransferRequest = FirstTransferRequest + +// HandleTransferRequestCreationSuccessfully configures the test server to respond to a Create request. +func HandleCreateSuccessfully(t *testing.T) { + createURL := "/zones/a6a8515c-5d80-48c0-955b-fde631b59791/tasks/transfer_requests" + th.Mux.HandleFunc(createURL, + 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, CreateTransferRequest) + + w.WriteHeader(http.StatusCreated) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, CreateTransferRequestResponse) + }) +} + +// UpdateTransferRequest is a sample request to update a zone transfer request. +const UpdateTransferRequest = ` +{ + "description": "Updated Description" +} +` + +// UpdatedTransferRequestResponse is a sample response to update a zone transfer request. +const UpdatedTransferRequestResponse = ` +{ + "id": "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", + "zone_id": "a6a8515c-5d80-48c0-955b-fde631b59791", + "project_id": "4335d1f0-f793-11e2-b778-0800200c9a66", + "target_project_id": "05d98711-b3a1-4264-a395-f46383671ee6", + "description": "Updated Description", + "key": "KJSDH23Z", + "status": "ACTIVE", + "zone_name": "example1.org.", + "created_at": "2020-10-12T08:38:58.000000", + "links": { + "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3" + } +} +` + +// HandleTransferRequestUpdateSuccessfully configures the test server to respond to an Update request. +func HandleUpdateSuccessfully(t *testing.T) { + baseURL := "/zones/tasks/transfer_requests" + th.Mux.HandleFunc(s.Join([]string{baseURL, FirstTransferRequest.ID}, "/"), + 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, UpdateTransferRequest) + + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, UpdatedTransferRequestResponse) + }) +} + +// HandleTransferRequestDeleteSuccessfully configures the test server to respond to an Delete request. +func HandleDeleteSuccessfully(t *testing.T) { + baseURL := "/zones/tasks/transfer_requests" + th.Mux.HandleFunc(s.Join([]string{baseURL, FirstTransferRequest.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/dns/v2/transfer/request/testing/requests_test.go b/openstack/dns/v2/transfer/request/testing/requests_test.go new file mode 100644 index 0000000000..3df033da1b --- /dev/null +++ b/openstack/dns/v2/transfer/request/testing/requests_test.go @@ -0,0 +1,113 @@ +package testing + +import ( + "testing" + + transferRequests "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/request" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + count := 0 + err := transferRequests.List(client.ServiceClient(), nil).EachPage( + func(page pagination.Page) (bool, error) { + count++ + actual, err := transferRequests.ExtractTransferRequests(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedTransferRequestsSlice, actual) + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, count) +} + +func TestListWithOpts(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + listOpts := transferRequests.ListOpts{ + Status: "ACTIVE", + } + + allPages, err := transferRequests.List(client.ServiceClient(), listOpts).AllPages() + th.AssertNoErr(t, err) + allTransferRequests, err := transferRequests.ExtractTransferRequests(allPages) + th.AssertNoErr(t, err) + th.CheckEquals(t, 2, len(allTransferRequests)) +} + +func TestListAllPages(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListSuccessfully(t) + + allPages, err := transferRequests.List(client.ServiceClient(), nil).AllPages() + th.AssertNoErr(t, err) + allTransferRequests, err := transferRequests.ExtractTransferRequests(allPages) + th.AssertNoErr(t, err) + th.CheckEquals(t, 2, len(allTransferRequests)) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := transferRequests.Get( + client.ServiceClient(), "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &FirstTransferRequest, actual) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfully(t) + + createOpts := transferRequests.CreateOpts{ + TargetProjectID: "05d98711-b3a1-4264-a395-f46383671ee6", + Description: "This is a first example zone transfer request.", + } + + actual, err := transferRequests.Create( + client.ServiceClient(), FirstTransferRequest.ZoneID, createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &CreatedTransferRequest, actual) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleUpdateSuccessfully(t) + + var description = "Updated Description" + updateOpts := transferRequests.UpdateOpts{ + Description: description, + } + + UpdatedTransferRequest := CreatedTransferRequest + UpdatedTransferRequest.Description = "Updated Description" + + actual, err := transferRequests.Update( + client.ServiceClient(), UpdatedTransferRequest.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &UpdatedTransferRequest, actual) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + DeletedZone := CreatedTransferRequest + + err := transferRequests.Delete(client.ServiceClient(), DeletedZone.ID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/dns/v2/transfer/request/urls.go b/openstack/dns/v2/transfer/request/urls.go new file mode 100644 index 0000000000..d8fe6ac488 --- /dev/null +++ b/openstack/dns/v2/transfer/request/urls.go @@ -0,0 +1,21 @@ +package request + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "zones" + tasksPath = "tasks" + resourcePath = "transfer_requests" +) + +func baseURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, tasksPath, resourcePath) +} + +func createURL(c *gophercloud.ServiceClient, zoneID string) string { + return c.ServiceURL(rootPath, zoneID, tasksPath, resourcePath) +} + +func resourceURL(c *gophercloud.ServiceClient, transferID string) string { + return c.ServiceURL(rootPath, tasksPath, resourcePath, transferID) +} From 3eecbca1164858df3bb117ef4d8596d111efa8a1 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 9 Nov 2020 22:11:41 -0700 Subject: [PATCH 1155/2296] Update doc.go --- openstack/dns/v2/transfer/accept/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/dns/v2/transfer/accept/doc.go b/openstack/dns/v2/transfer/accept/doc.go index 1d2a6d53c4..44d053875a 100644 --- a/openstack/dns/v2/transfer/accept/doc.go +++ b/openstack/dns/v2/transfer/accept/doc.go @@ -24,7 +24,7 @@ Example to Create a Zone Transfer Accept zoneTransferRequestID := "99d10f68-5623-4491-91a0-6daafa32b60e" key := "JKHGD2F7" createOpts := transferAccepts.CreateOpts{ - ZoneTransferRequestID: zoneTransferRequestID, + ZoneTransferRequestID: zoneTransferRequestID, Key: key, } transferAccept, err := transferAccepts.Create(dnsClient, createOpts).Extract() From 47700480d49a3b029aff8e4ef81903e3a4574177 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 9 Nov 2020 22:14:46 -0700 Subject: [PATCH 1156/2296] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8872ad2131..9c1cf520d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ IMPROVEMENTS * 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) BUG FIXES From 54ad369227a4c6f2be2329a25103d4503f034fcb Mon Sep 17 00:00:00 2001 From: Andrea Fasano <60063538+andfasano@users.noreply.github.com> Date: Tue, 10 Nov 2020 06:15:32 +0100 Subject: [PATCH 1157/2296] Add RescueWait and Unrescuing ProvisionState states (#2052) --- openstack/baremetal/v1/nodes/requests.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index f30eb40a23..7cb86ba5ff 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -43,6 +43,8 @@ const ( RescueFail ProvisionState = "rescue failed" Rescuing ProvisionState = "rescuing" UnrescueFail ProvisionState = "unrescue failed" + RescueWait ProvisionState = "rescue wait" + Unrescuing ProvisionState = "unrescuing" ) // TargetProvisionState is used when setting the provision state for a node. From 9ee271de6ec7a4c82e1f95c90a6a8948b5d49fec Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 9 Nov 2020 22:16:40 -0700 Subject: [PATCH 1158/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c1cf520d3..79b73d28a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ IMPROVEMENTS * 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) BUG FIXES From 86926d0364fd574f2b594f23ccb318337dccedaf Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Nov 2020 20:26:44 -0700 Subject: [PATCH 1159/2296] Docs: Update README.md with note about clientconfig (#2053) --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index ad29041d9b..95539563ac 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,13 @@ 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 From 445d0918980a140dd6e6183ff9721b1a2dce6033 Mon Sep 17 00:00:00 2001 From: Georg Schreiber <1942155+codemanufaktur@users.noreply.github.com> Date: Thu, 12 Nov 2020 04:28:38 +0100 Subject: [PATCH 1160/2296] FWaaSv2 CRUD firewall-groups (#2050) FWaaSv2 Create firewall-groups FWaaSv2 Update & Delete firewall-groups acc tests for fwaasv2 firewall groups --- .../networking/v2/extensions/fwaas/fwaas.go | 2 +- .../v2/extensions/fwaas_v2/fwaas_v2.go | 45 +++ .../v2/extensions/fwaas_v2/groups_test.go | 62 ++++ .../v2/extensions/fwaas_v2/groups/requests.go | 153 +++++++++ .../v2/extensions/fwaas_v2/groups/results.go | 91 +++++ .../extensions/fwaas_v2/groups/testing/doc.go | 2 + .../fwaas_v2/groups/testing/requests_test.go | 310 ++++++++++++++++++ .../v2/extensions/fwaas_v2/groups/urls.go | 16 + 8 files changed, 680 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/groups/requests.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/groups/results.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/groups/testing/doc.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/groups/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/groups/urls.go diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index f6f1f88388..af20a111f1 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -14,7 +14,7 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -// CreateFirewall will create a Firewaill with a random name and a specified +// CreateFirewall will create a Firewall with a random name and a specified // policy ID. An error will be returned if the firewall could not be created. func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*firewalls.Firewall, error) { firewallName := 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 e44427d6af..b9a8ff0f06 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/groups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -65,3 +66,47 @@ func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) t.Logf("Deleted rule: %s", ruleID) } + +// CreateGroup will create a Firewall Group. An error will be returned if the +// firewall group could not be created. +func CreateGroup(t *testing.T, client *gophercloud.ServiceClient) (*groups.Group, error) { + + groupName := tools.RandomString("TESTACC-", 8) + description := tools.RandomString("TESTACC-", 8) + adminStateUp := true + shared := false + + createOpts := groups.CreateOpts{ + Name: groupName, + Description: description, + AdminStateUp: &adminStateUp, + Shared: &shared, + } + + t.Logf("Attempting to create firewall group %s", + groupName) + + group, err := groups.Create(client, createOpts).Extract() + if err != nil { + return group, err + } + + t.Logf("firewall group %s successfully created", groupName) + + th.AssertEquals(t, group.Name, groupName) + return group, nil +} + +// DeleteGroup will delete a group 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 DeleteGroup(t *testing.T, client *gophercloud.ServiceClient, groupId string) { + t.Logf("Attempting to delete firewall group %s", groupId) + + err := groups.Delete(client, groupId).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete firewall group %s: %v", groupId, err) + } + + t.Logf("Deleted firewall group %s", groupId) +} diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go new file mode 100644 index 0000000000..d58562580f --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -0,0 +1,62 @@ +// +build acceptance networking fwaas_v2 + +package fwaas_v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/groups" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestGroupCRUD(t *testing.T) { + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + createdGroup, err := CreateGroup(t, client) + th.AssertNoErr(t, err) + defer DeleteGroup(t, client, createdGroup.ID) + + tools.PrintResource(t, createdGroup) + + groupName := tools.RandomString("TESTACC-", 8) + adminStateUp := false + description := ("Some firewall group description") + updateOpts := groups.UpdateOpts{ + Name: &groupName, + Description: &description, + AdminStateUp: &adminStateUp, + } + + groupUpdated, 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) + + t.Logf("Updated firewall group %s", groupUpdated.ID) + + allPages, err := groups.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allGroups, err := groups.ExtractGroups(allPages) + th.AssertNoErr(t, err) + + t.Logf("Attempting to find firewall group %s\n", createdGroup.ID) + var found bool + for _, group := range allGroups { + if group.ID == createdGroup.ID { + found = true + t.Logf("Found firewall group %s\n", group.ID) + } + } + + th.AssertEquals(t, found, true) +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go new file mode 100644 index 0000000000..fb10d4e8ea --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go @@ -0,0 +1,153 @@ +package groups + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToGroupListQuery() (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 firewall group attributes you want to see returned. SortKey allows you +// to sort by a particular firewall group attribute. SortDir sets the direction, +// and is either `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + TenantID string `q:"tenant_id"` + Name string `q:"name"` + Description string `q:"description"` + IngressFirewallPolicyID string `q:"ingress_firewall_policy_id"` + EgressFirewallPolicyID string `q:"egress_firewall_policy_id"` + AdminStateUp *bool `q:"admin_state_up"` + Ports *[]string `q:"ports"` + Status string `q:"status"` + ID string `q:"id"` + Shared *bool `q:"shared"` + ProjectID string `q:"project_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToGroupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToGroupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// firewall groups. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. +// +// Default group settings return only those firewall groups that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + + if opts != nil { + query, err := opts.ToGroupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return GroupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get retrieves a particular firewall group based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +// CreateOptsBuilder is the interface options structs have to satisfy in order +// to be used in the main Create operation in this package. Since many +// extensions decorate or modify the common logic, it is useful for them to +// satisfy a basic interface in order for them to be used. +type CreateOptsBuilder interface { + ToFirewallGroupCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new firewall group. +type CreateOpts struct { + ID string `json:"id,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + IngressFirewallPolicyID string `json:"ingress_firewall_policy_id,omitempty"` + EgressFirewallPolicyID string `json:"egress_firewall_policy_id,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Ports []string `json:"ports,omitempty"` + Shared *bool `json:"shared,omitempty"` + ProjectID string `json:"project_id,omitempty"` +} + +// ToFirewallGroupCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToFirewallGroupCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "firewall_group") +} + +// Create accepts a CreateOpts struct and uses the values to create a new firewall group +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToFirewallGroupCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} + +// UpdateOptsBuilder is the interface options structs have to satisfy in order +// to be used in the main Update operation in this package. Since many +// extensions decorate or modify the common logic, it is useful for them to +// satisfy a basic interface in order for them to be used. +type UpdateOptsBuilder interface { + ToFirewallGroupUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contains the values used when updating a firewall group. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + IngressFirewallPolicyID *string `json:"ingress_firewall_policy_id,omitempty"` + EgressFirewallPolicyID *string `json:"egress_firewall_policy_id,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Ports []string `json:"ports,omitempty"` + Shared *bool `json:"shared,omitempty"` +} + +// ToFirewallGroupUpdateMap casts a CreateOpts struct to a map. +func (opts UpdateOpts) ToFirewallGroupUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "firewall_group") +} + +// Update allows firewall groups to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToFirewallGroupUpdateMap() + 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) + return +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/results.go b/openstack/networking/v2/extensions/fwaas_v2/groups/results.go new file mode 100644 index 0000000000..c0af2b0a9d --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/results.go @@ -0,0 +1,91 @@ +package groups + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Group is a firewall group. +type Group struct { + ID string `json:"id"` + TenantID string `json:"tenant_id"` + Name string `json:"name"` + Description string `json:"description"` + IngressFirewallPolicyID string `json:"ingress_firewall_policy_id"` + EgressFirewallPolicyID string `json:"egress_firewall_policy_id"` + AdminStateUp bool `json:"admin_state_up"` + Ports []string `json:"ports"` + Status string `json:"status"` + Shared bool `json:"shared"` + ProjectID string `json:"project_id"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a firewall group. +func (r commonResult) Extract() (*Group, error) { + var s struct { + Group *Group `json:"firewall_group"` + } + err := r.ExtractInto(&s) + return s.Group, err +} + +// GroupPage is the page returned by a pager when traversing over a +// collection of firewall groups. +type GroupPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of firewall groups 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 GroupPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"firewall_groups_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a GroupPage struct is empty. +func (r GroupPage) IsEmpty() (bool, error) { + is, err := ExtractGroups(r) + return len(is) == 0, err +} + +// ExtractGroups accepts a Page struct, specifically a GroupPage struct, +// and extracts the elements into a slice of Group structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractGroups(r pagination.Page) ([]Group, error) { + var s struct { + Groups []Group `json:"firewall_groups"` + } + err := (r.(GroupPage)).ExtractInto(&s) + return s.Groups, err +} + +// GetResult represents the result of a get operation. +type GetResult struct { + commonResult +} + +// CreateResult represents the result of a create operation. +type CreateResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. +type UpdateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/testing/doc.go b/openstack/networking/v2/extensions/fwaas_v2/groups/testing/doc.go new file mode 100644 index 0000000000..b5e99dc513 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/testing/doc.go @@ -0,0 +1,2 @@ +// networking_extensions_fwaas_groups_v2 +package testing 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 new file mode 100644 index 0000000000..9909a77c6c --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/testing/requests_test.go @@ -0,0 +1,310 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/groups" + "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/fwaas/firewall_groups", 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, ` +{ + "firewall_groups": [ + { + "id": "3af94f0e-b52d-491a-87d2-704497305948", + "tenant_id": "9f98fc0e5f944cd1b51798b668dc8778", + "name": "test", + "description": "fancy group", + "ingress_firewall_policy_id": "e3f11142-3792-454b-8d3e-91ac1bf127b4", + "egress_firewall_policy_id": null, + "admin_state_up": true, + "ports": [ + "a6af1e56-b12b-4733-8f77-49166afd5719" + ], + "status": "ACTIVE", + "shared": false, + "project_id": "9f98fc0e5f944cd1b51798b668dc8778" + }, + { + "id": "f9fbb80c-eeb4-4f3f-aa50-1032960c08ea", + "tenant_id": "9f98fc0e5f944cd1b51798b668dc8778", + "name": "default", + "description": "Default firewall group", + "ingress_firewall_policy_id": "90e3fcac-3bfb-48f9-8e91-2a78fb352b92", + "egress_firewall_policy_id": "122fb344-3c28-49f0-af00-f7fcbc88330b", + "admin_state_up": true, + "ports": [ + "20da216c-bab3-4cf6-bd6b-8904b133a816", + "4f4c714c-185f-487e-998c-c1a35da3c4f4", + "681b1db4-40ca-4314-b098-d2f43225e7f7", + "82f9d868-6f56-44fb-9684-654dc473bed0", + "a5858b5d-20dc-4bb1-9f95-1d322c8bb81b", + "d25a04a2-447b-4ee1-80d7-b32967dbb643" + ], + "status": "ACTIVE", + "shared": false, + "project_id": "9f98fc0e5f944cd1b51798b668dc8778" + } + ] +} + `) + }) + + count := 0 + + groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := groups.ExtractGroups(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + + expected := []groups.Group{ + { + ID: "3af94f0e-b52d-491a-87d2-704497305948", + TenantID: "9f98fc0e5f944cd1b51798b668dc8778", + Name: "test", + Description: "fancy group", + IngressFirewallPolicyID: "e3f11142-3792-454b-8d3e-91ac1bf127b4", + EgressFirewallPolicyID: "", + AdminStateUp: true, + Ports: []string{ + "a6af1e56-b12b-4733-8f77-49166afd5719", + }, + Status: "ACTIVE", + Shared: false, + ProjectID: "9f98fc0e5f944cd1b51798b668dc8778", + }, + { + ID: "f9fbb80c-eeb4-4f3f-aa50-1032960c08ea", + TenantID: "9f98fc0e5f944cd1b51798b668dc8778", + Name: "default", + Description: "Default firewall group", + IngressFirewallPolicyID: "90e3fcac-3bfb-48f9-8e91-2a78fb352b92", + EgressFirewallPolicyID: "122fb344-3c28-49f0-af00-f7fcbc88330b", + AdminStateUp: true, + Ports: []string{ + "20da216c-bab3-4cf6-bd6b-8904b133a816", + "4f4c714c-185f-487e-998c-c1a35da3c4f4", + "681b1db4-40ca-4314-b098-d2f43225e7f7", + "82f9d868-6f56-44fb-9684-654dc473bed0", + "a5858b5d-20dc-4bb1-9f95-1d322c8bb81b", + "d25a04a2-447b-4ee1-80d7-b32967dbb643", + }, + Status: "ACTIVE", + Shared: false, + ProjectID: "9f98fc0e5f944cd1b51798b668dc8778", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(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, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + 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" + ], + "status": "ACTIVE", + "shared": false, + "project_id": "9f98fc0e5f944cd1b51798b668dc8778" + } +} + `) + }) + + group, err := groups.Get(fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", group.ID) + th.AssertEquals(t, "9f98fc0e5f944cd1b51798b668dc8778", group.TenantID) + th.AssertEquals(t, "test", group.Name) + th.AssertEquals(t, "some information", group.Description) + th.AssertEquals(t, "e3f11142-3792-454b-8d3e-91ac1bf127b4", group.IngressFirewallPolicyID) + th.AssertEquals(t, "", group.EgressFirewallPolicyID) + th.AssertEquals(t, true, group.AdminStateUp) + th.AssertEquals(t, 1, len(group.Ports)) + th.AssertEquals(t, "a6af1e56-b12b-4733-8f77-49166afd5719", group.Ports[0]) + th.AssertEquals(t, "ACTIVE", group.Status) + th.AssertEquals(t, false, group.Shared) + th.AssertEquals(t, "9f98fc0e5f944cd1b51798b668dc8778", group.TenantID) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_groups", 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, ` +{ + "firewall_group": { + "ports": [ + "a6af1e56-b12b-4733-8f77-49166afd5719" + ], + "ingress_firewall_policy_id": "e3f11142-3792-454b-8d3e-91ac1bf127b4", + "name": "test" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "firewall_group": { + "id": "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", + "tenant_id": "9f98fc0e5f944cd1b51798b668dc8778", + "name": "test", + "description": "", + "ingress_firewall_policy_id": "e3f11142-3792-454b-8d3e-91ac1bf127b4", + "egress_firewall_policy_id": null, + "admin_state_up": true, + "ports": [ + "a6af1e56-b12b-4733-8f77-49166afd5719" + ], + "status": "CREATED", + "shared": false, + "project_id": "9f98fc0e5f944cd1b51798b668dc8778" + } +} + `) + }) + + options := groups.CreateOpts{ + Name: "test", + Description: "", + IngressFirewallPolicyID: "e3f11142-3792-454b-8d3e-91ac1bf127b4", + Ports: []string{ + "a6af1e56-b12b-4733-8f77-49166afd5719", + }, + } + + _, err := groups.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) +} + +func TestUpdate(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":{ + "name": "the group", + "ports": [ + "a6af1e56-b12b-4733-8f77-49166afd5719", + "11a58c87-76be-ae7c-a74e-b77fffb88a32" + ], + "description": "Firewall group", + "admin_state_up": false + } +} + `) + + 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" + } +} + `) + }) + + name := "the group" + description := "Firewall group" + adminStateUp := false + options := groups.UpdateOpts{ + Name: &name, + Description: &description, + Ports: []string{ + "a6af1e56-b12b-4733-8f77-49166afd5719", + "11a58c87-76be-ae7c-a74e-b77fffb88a32", + }, + AdminStateUp: &adminStateUp, + } + + _, err := groups.Update(fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", options).Extract() + th.AssertNoErr(t, err) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_groups/4ec89077-d057-4a2b-911f-60a3b47ee304", 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) + }) + + res := groups.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/urls.go b/openstack/networking/v2/extensions/fwaas_v2/groups/urls.go new file mode 100644 index 0000000000..318794369c --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/urls.go @@ -0,0 +1,16 @@ +package groups + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "fwaas" + resourcePath = "firewall_groups" +) + +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 dd9d5881ef77486756ba25b6b603b1dfc315600b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Nov 2020 20:31:09 -0700 Subject: [PATCH 1161/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b73d28a5..21a1117aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ IMPROVEMENTS * 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 From eeff63dbdcab503e85178c5faa8a0acd4735f7f7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Nov 2020 20:31:35 -0700 Subject: [PATCH 1162/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a1117aa2..b38b101688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.14.0 (Unreleased) +## 0.15.0 (Unreleased) + +## 0.14.0 (November 11, 2020) IMPROVEMENTS From 0859f65df2820887da8668f365299cb4b66f215b Mon Sep 17 00:00:00 2001 From: Lars Lehtonen Date: Mon, 16 Nov 2020 15:01:53 -0800 Subject: [PATCH 1163/2296] Prune sharedfilesystems (#2058) * openstack/sharedfilesystems/v2/shares/testing: prune unused deleteMetadatumRequest * openstack/sharedfilesystems/v2/availabilityzones: prune unused commonResult type --- openstack/sharedfilesystems/v2/availabilityzones/results.go | 4 ---- openstack/sharedfilesystems/v2/shares/testing/fixtures.go | 6 ------ 2 files changed, 10 deletions(-) diff --git a/openstack/sharedfilesystems/v2/availabilityzones/results.go b/openstack/sharedfilesystems/v2/availabilityzones/results.go index 83a76c1a83..c685f04ea7 100644 --- a/openstack/sharedfilesystems/v2/availabilityzones/results.go +++ b/openstack/sharedfilesystems/v2/availabilityzones/results.go @@ -21,10 +21,6 @@ type AvailabilityZone struct { UpdatedAt time.Time `json:"-"` } -type commonResult struct { - gophercloud.Result -} - // ListResult contains the response body and error from a List request. type AvailabilityZonePage struct { pagination.SinglePageBase diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 7117da4104..73b3e61345 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -528,12 +528,6 @@ func MockUpdateMetadataResponse(t *testing.T) { }) } -var deleteMetadatumRequest = `{ - "metadata": { - "foo": "bar" - } - }` - // MockDeleteMetadatumResponse creates a mock unset metadata response func MockDeleteMetadatumResponse(t *testing.T, key string) { th.Mux.HandleFunc(shareEndpoint+"/"+shareID+"/metadata/"+key, func(w http.ResponseWriter, r *http.Request) { From 46b2ac9a349568a88a9e8e42dbeeeaf6a841b53d Mon Sep 17 00:00:00 2001 From: Matthias Neugebauer Date: Tue, 24 Nov 2020 00:13:24 +0100 Subject: [PATCH 1164/2296] Add missing options to member types (#2056) This adds Backup, MonitorAddress, MonitorPort and Tags to member types. **`CreateMemberOpts`** [Documentation](https://docs.openstack.org/api-ref/load-balancer/v2/?expanded=create-member-detail#create-member) [Code](https://github.com/openstack/octavia/blob/198980639cbe02820f90783fa04d5239717ed80d/octavia/api/v2/types/member.py#L87) **`UpdateMemberOpts`** [Documentation](https://docs.openstack.org/api-ref/load-balancer/v2/?expanded=update-a-member-detail#update-a-member) [Code](https://github.com/openstack/octavia/blob/198980639cbe02820f90783fa04d5239717ed80d/octavia/api/v2/types/member.py#L100-L104) **`BatchUpdateMemberOpts`** [Documentation](https://docs.openstack.org/api-ref/load-balancer/v2/?expanded=batch-update-members-detail#batch-update-members) [Code](https://github.com/openstack/octavia/blob/198980639cbe02820f90783fa04d5239717ed80d/octavia/api/v2/types/member.py#L79-L87) --- openstack/loadbalancer/v2/pools/requests.go | 40 +++++++++++++++++++-- openstack/loadbalancer/v2/pools/results.go | 4 +++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 5a4c7750a3..055263f41f 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -280,14 +280,20 @@ type CreateMemberOpts struct { // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` - // Is the member a backup? Backup members only receive traffic when all non-backup members are down. + // Is the member a backup? Backup members only receive traffic when all + // non-backup members are down. + // Requires microversion 2.1 or later. Backup *bool `json:"backup,omitempty"` // An alternate IP address used for health monitoring a backend member. - MonitorAddress string `json:"monitor_address,omitempty"` + MonitorAddress *string `json:"monitor_address,omitempty"` // An alternate protocol port used for health monitoring a backend member. MonitorPort *int `json:"monitor_port,omitempty"` + + // A list of simple strings assigned to the resource. + // Requires microversion 2.5 or later. + Tags []string `json:"tags,omitempty"` } // ToMemberCreateMap builds a request body from CreateMemberOpts. @@ -335,6 +341,21 @@ type UpdateMemberOpts struct { // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Is the member a backup? Backup members only receive traffic when all + // non-backup members are down. + // Requires microversion 2.1 or later. + Backup *bool `json:"backup,omitempty"` + + // An alternate IP address used for health monitoring a backend member. + MonitorAddress *string `json:"monitor_address,omitempty"` + + // An alternate protocol port used for health monitoring a backend member. + MonitorPort *int `json:"monitor_port,omitempty"` + + // A list of simple strings assigned to the resource. + // Requires microversion 2.5 or later. + Tags []string `json:"tags,omitempty"` } // ToMemberUpdateMap builds a request body from UpdateMemberOpts. @@ -390,6 +411,21 @@ type BatchUpdateMemberOpts struct { // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Is the member a backup? Backup members only receive traffic when all + // non-backup members are down. + // Requires microversion 2.1 or later. + Backup *bool `json:"backup,omitempty"` + + // An alternate IP address used for health monitoring a backend member. + MonitorAddress *string `json:"monitor_address,omitempty"` + + // An alternate protocol port used for health monitoring a backend member. + MonitorPort *int `json:"monitor_port,omitempty"` + + // A list of simple strings assigned to the resource. + // Requires microversion 2.5 or later. + Tags []string `json:"tags,omitempty"` } // ToBatchMemberUpdateMap builds a request body from BatchUpdateMemberOpts. diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index 1dc0ed90ac..b4883e9986 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -228,6 +228,10 @@ type Member struct { // An alternate protocol port used for health monitoring a backend member. MonitorPort int `json:"monitor_port"` + + // A list of simple strings assigned to the resource. + // Requires microversion 2.5 or later. + Tags []string `json:"tags"` } // MemberPage is the page returned by a pager when traversing over a From f054f4a0c5960a231e4c64a95ce50138de2b7332 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 23 Nov 2020 16:15:26 -0700 Subject: [PATCH 1165/2296] Update requests.go --- openstack/loadbalancer/v2/pools/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 055263f41f..8c3718bec5 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -286,7 +286,7 @@ type CreateMemberOpts struct { Backup *bool `json:"backup,omitempty"` // An alternate IP address used for health monitoring a backend member. - MonitorAddress *string `json:"monitor_address,omitempty"` + MonitorAddress string `json:"monitor_address,omitempty"` // An alternate protocol port used for health monitoring a backend member. MonitorPort *int `json:"monitor_port,omitempty"` From b47fd443c31717ccee45d35afa463c687eb3a0e0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 23 Nov 2020 16:18:48 -0700 Subject: [PATCH 1166/2296] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b38b101688..a5aa42995c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ ## 0.15.0 (Unreleased) +* 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) + + ## 0.14.0 (November 11, 2020) IMPROVEMENTS From fbf58a11d16acf0fcc884c1293810ecfc383a1ee Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 24 Nov 2020 17:51:25 -0500 Subject: [PATCH 1167/2296] Networking v2: Add Get Detailed Quota for project + 'trunk' extension support (#2061) * Networking v2: Add Get Detailed Quota for project * Networking v2: Add Get Detailed Quota for Tenant * Add GetDetail function for the quotas extension in Gophercloud's OpenStack Networking v2 API - Allows a caller to get the list of quota types with in_use, reserved, and limit values for each - Effectively wraps OpenStack Networking v2 API function /v2.0/quotas/{project_id}/details.json - Inspired from the Compute API implementation. For #2060 API reference: https://docs.openstack.org/api-ref/network/v2/index.html?expanded=show-quota-details-for-a-tenant-detail#quotas-extension-quotas Code references: * (extension) https://opendev.org/openstack/neutron/src/branch/master/neutron/extensions/quotasv2_detail.py * (options) https://opendev.org/openstack/neutron/src/branch/master/neutron/conf/quota.py * Add 'trunk' resource in Networking v2 quotas When the 'trunk' extension is enabled, it's possible to set quotas for trunks resources, the same way as other networking resources. 'trunk' originates here: https://opendev.org/openstack/neutron-lib/src/branch/master/neutron_lib/api/definitions/trunk.py#L49 It is registered as a quota resource in neutron here: https://opendev.org/openstack/neutron/src/branch/master/neutron/extensions/trunk.py Closes #2064 --- .../networking/v2/extensions/quotas/doc.go | 11 +++ .../v2/extensions/quotas/requests.go | 10 +++ .../v2/extensions/quotas/results.go | 72 ++++++++++++++++ .../v2/extensions/quotas/testing/fixtures.go | 84 ++++++++++++++++++- .../quotas/testing/requests_test.go | 20 +++++ .../networking/v2/extensions/quotas/urls.go | 9 ++ 6 files changed, 203 insertions(+), 3 deletions(-) diff --git a/openstack/networking/v2/extensions/quotas/doc.go b/openstack/networking/v2/extensions/quotas/doc.go index 563e9c688b..cb39dc9939 100644 --- a/openstack/networking/v2/extensions/quotas/doc.go +++ b/openstack/networking/v2/extensions/quotas/doc.go @@ -11,6 +11,16 @@ Example to Get project quotas 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) + } + + fmt.Printf("quotas: %#v\n", quotasInfo) + Example to Update project quotas projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" @@ -25,6 +35,7 @@ Example to Update project quotas 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 { diff --git a/openstack/networking/v2/extensions/quotas/requests.go b/openstack/networking/v2/extensions/quotas/requests.go index 2abaf22b44..b4b67a96a9 100644 --- a/openstack/networking/v2/extensions/quotas/requests.go +++ b/openstack/networking/v2/extensions/quotas/requests.go @@ -9,6 +9,13 @@ func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { 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 +} + // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { @@ -43,6 +50,9 @@ type UpdateOpts struct { // SubnetPool represents a number of subnet pools. A "-1" value means no limit. SubnetPool *int `json:"subnetpool,omitempty"` + + // Trunk represents a number of trunks. A "-1" value means no limit. + Trunk *int `json:"trunk,omitempty"` } // ToQuotaUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/networking/v2/extensions/quotas/results.go b/openstack/networking/v2/extensions/quotas/results.go index 6219abb017..99f28771b0 100644 --- a/openstack/networking/v2/extensions/quotas/results.go +++ b/openstack/networking/v2/extensions/quotas/results.go @@ -6,6 +6,10 @@ type commonResult struct { gophercloud.Result } +type detailResult 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 { @@ -15,12 +19,27 @@ func (r commonResult) Extract() (*Quota, error) { return s.Quota, err } +// Extract is a function that accepts a result and extracts a QuotaDetailSet resource. +func (r detailResult) Extract() (*QuotaDetailSet, error) { + var s struct { + Quota *QuotaDetailSet `json:"quota"` + } + 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 } +// GetDetailResult represents the detailed result of a get operation. Call its Extract +// method to interpret it as a Quota. +type GetDetailResult struct { + detailResult +} + // UpdateResult represents the result of an update operation. Call its Extract // method to interpret it as a Quota. type UpdateResult struct { @@ -55,4 +74,57 @@ type Quota struct { // SubnetPool represents a number of subnet pools. A "-1" value means no limit. SubnetPool int `json:"subnetpool"` + + // Trunk represents a number of trunks. A "-1" value means no limit. + Trunk int `json:"trunk"` +} + +// QuotaDetailSet represents details of both operational limits of Networking resources for a project +// and the current usage of those resources. +type QuotaDetailSet struct { + // FloatingIP represents a number of floating IPs. A "-1" value means no limit. + FloatingIP QuotaDetail `json:"floatingip"` + + // Network represents a number of networks. A "-1" value means no limit. + Network QuotaDetail `json:"network"` + + // Port represents a number of ports. A "-1" value means no limit. + Port QuotaDetail `json:"port"` + + // RBACPolicy represents a number of RBAC policies. A "-1" value means no limit. + RBACPolicy QuotaDetail `json:"rbac_policy"` + + // Router represents a number of routers. A "-1" value means no limit. + Router QuotaDetail `json:"router"` + + // SecurityGroup represents a number of security groups. A "-1" value means no limit. + SecurityGroup QuotaDetail `json:"security_group"` + + // SecurityGroupRule represents a number of security group rules. A "-1" value means no limit. + SecurityGroupRule QuotaDetail `json:"security_group_rule"` + + // Subnet represents a number of subnets. A "-1" value means no limit. + Subnet QuotaDetail `json:"subnet"` + + // SubnetPool represents a number of subnet pools. A "-1" value means no limit. + SubnetPool QuotaDetail `json:"subnetpool"` + + // Trunk represents a number of trunks. A "-1" value means no limit. + Trunk QuotaDetail `json:"trunk"` +} + +// QuotaDetail is a set of details about a single operational limit that allows +// for control of networking usage. +type QuotaDetail struct { + // Used is the current number of provisioned/allocated resources of the + // given type. + Used int `json:"used"` + + // 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"` } diff --git a/openstack/networking/v2/extensions/quotas/testing/fixtures.go b/openstack/networking/v2/extensions/quotas/testing/fixtures.go index fd8b6181af..b81a1eed17 100644 --- a/openstack/networking/v2/extensions/quotas/testing/fixtures.go +++ b/openstack/networking/v2/extensions/quotas/testing/fixtures.go @@ -1,6 +1,8 @@ package testing -import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" +import ( + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" +) const GetResponseRaw = ` { @@ -13,11 +15,70 @@ const GetResponseRaw = ` "security_group": 35, "security_group_rule": 40, "subnet": 45, - "subnetpool": -1 + "subnetpool": -1, + "trunk": 50 } } ` +// GetDetailedResponseRaw is a sample response to a Get call with the detailed option. +const GetDetailedResponseRaw = ` +{ + "quota" : { + "floatingip" : { + "used": 0, + "limit": 15, + "reserved": 0 + }, + "network" : { + "used": 0, + "limit": 20, + "reserved": 0 + }, + "port" : { + "used": 0, + "limit": 25, + "reserved": 0 + }, + "rbac_policy" : { + "used": 0, + "limit": -1, + "reserved": 0 + }, + "router" : { + "used": 0, + "limit": 30, + "reserved": 0 + }, + "security_group" : { + "used": 0, + "limit": 35, + "reserved": 0 + }, + "security_group_rule" : { + "used": 0, + "limit": 40, + "reserved": 0 + }, + "subnet" : { + "used": 0, + "limit": 45, + "reserved": 0 + }, + "subnetpool" : { + "used": 0, + "limit": -1, + "reserved": 0 + }, + "trunk" : { + "used": 0, + "limit": 50, + "reserved": 0 + } + } +} +` + var GetResponse = quotas.Quota{ FloatingIP: 15, Network: 20, @@ -28,6 +89,21 @@ var GetResponse = quotas.Quota{ SecurityGroupRule: 40, Subnet: 45, SubnetPool: -1, + Trunk: 50, +} + +// GetDetailResponse is the first result in ListOutput. +var GetDetailResponse = quotas.QuotaDetailSet{ + FloatingIP: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: 15}, + Network: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: 20}, + Port: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: 25}, + RBACPolicy: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: -1}, + Router: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: 30}, + SecurityGroup: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: 35}, + SecurityGroupRule: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: 40}, + Subnet: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: 45}, + SubnetPool: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: -1}, + Trunk: quotas.QuotaDetail{Used: 0, Reserved: 0, Limit: 50}, } const UpdateRequestResponseRaw = ` @@ -41,7 +117,8 @@ const UpdateRequestResponseRaw = ` "security_group": 20, "security_group_rule": -1, "subnet": 25, - "subnetpool": 0 + "subnetpool": 0, + "trunk": 5 } } ` @@ -56,4 +133,5 @@ var UpdateResponse = quotas.Quota{ SecurityGroupRule: -1, Subnet: 25, SubnetPool: 0, + Trunk: 5, } diff --git a/openstack/networking/v2/extensions/quotas/testing/requests_test.go b/openstack/networking/v2/extensions/quotas/testing/requests_test.go index 8d7ffad36c..6530ffb969 100644 --- a/openstack/networking/v2/extensions/quotas/testing/requests_test.go +++ b/openstack/networking/v2/extensions/quotas/testing/requests_test.go @@ -30,6 +30,25 @@ func TestGet(t *testing.T) { th.AssertDeepEquals(t, q, &GetResponse) } +func TestGetDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/quotas/0a73845280574ad389c292f6a74afa76/details.json", 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, GetDetailedResponseRaw) + }) + + q, err := quotas.GetDetail(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, q, &GetDetailResponse) +} + func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -54,6 +73,7 @@ func TestUpdate(t *testing.T) { SecurityGroupRule: gophercloud.IntToPointer(-1), Subnet: gophercloud.IntToPointer(25), SubnetPool: gophercloud.IntToPointer(0), + Trunk: gophercloud.IntToPointer(5), }).Extract() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/quotas/urls.go b/openstack/networking/v2/extensions/quotas/urls.go index cf7dcbde65..7bf669439a 100644 --- a/openstack/networking/v2/extensions/quotas/urls.go +++ b/openstack/networking/v2/extensions/quotas/urls.go @@ -3,15 +3,24 @@ package quotas import "github.com/gophercloud/gophercloud" const resourcePath = "quotas" +const resourcePathDetail = "details.json" func resourceURL(c *gophercloud.ServiceClient, projectID string) string { return c.ServiceURL(resourcePath, projectID) } +func resourceDetailURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID, resourcePathDetail) +} + func getURL(c *gophercloud.ServiceClient, projectID string) string { return resourceURL(c, projectID) } +func getDetailURL(c *gophercloud.ServiceClient, projectID string) string { + return resourceDetailURL(c, projectID) +} + func updateURL(c *gophercloud.ServiceClient, projectID string) string { return resourceURL(c, projectID) } From 5c7caf58c4c8ee95850fa75865dd62aad9474349 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 24 Nov 2020 15:53:32 -0700 Subject: [PATCH 1168/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5aa42995c..34b437af71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ * 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) ## 0.14.0 (November 11, 2020) From 11768baa60aba19a227a27a9566303e73e351f62 Mon Sep 17 00:00:00 2001 From: Benjamin Ziehms Date: Wed, 2 Dec 2020 20:35:02 +0100 Subject: [PATCH 1169/2296] Objectstore v1: allow to remove account and object metadata (#2063) --- openstack/objectstorage/v1/accounts/requests.go | 7 +++++++ openstack/objectstorage/v1/accounts/testing/fixtures.go | 1 + .../objectstorage/v1/accounts/testing/requests_test.go | 5 ++++- openstack/objectstorage/v1/objects/requests.go | 6 ++++++ openstack/objectstorage/v1/objects/testing/fixtures.go | 1 + .../objectstorage/v1/objects/testing/requests_test.go | 5 ++++- 6 files changed, 23 insertions(+), 2 deletions(-) diff --git a/openstack/objectstorage/v1/accounts/requests.go b/openstack/objectstorage/v1/accounts/requests.go index 7c9acf85ff..8c0bfaffb7 100644 --- a/openstack/objectstorage/v1/accounts/requests.go +++ b/openstack/objectstorage/v1/accounts/requests.go @@ -53,6 +53,7 @@ type UpdateOptsBuilder interface { // deleting an account's metadata. type UpdateOpts struct { Metadata map[string]string + RemoveMetadata []string ContentType string `h:"Content-Type"` DetectContentType bool `h:"X-Detect-Content-Type"` TempURLKey string `h:"X-Account-Meta-Temp-URL-Key"` @@ -65,9 +66,15 @@ func (opts UpdateOpts) ToAccountUpdateMap() (map[string]string, error) { if err != nil { return nil, err } + for k, v := range opts.Metadata { headers["X-Account-Meta-"+k] = v } + + for _, k := range opts.RemoveMetadata { + headers["X-Remove-Account-Meta-"+k] = "remove" + } + return headers, err } diff --git a/openstack/objectstorage/v1/accounts/testing/fixtures.go b/openstack/objectstorage/v1/accounts/testing/fixtures.go index 0a2ba8cf11..6cc4b731a0 100644 --- a/openstack/objectstorage/v1/accounts/testing/fixtures.go +++ b/openstack/objectstorage/v1/accounts/testing/fixtures.go @@ -51,6 +51,7 @@ func HandleUpdateAccountSuccessfully(t *testing.T) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "X-Account-Meta-Gophercloud-Test", "accounts") + th.TestHeader(t, r, "X-Remove-Account-Meta-Gophercloud-Test-Remove", "remove") w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 UTC") w.WriteHeader(http.StatusNoContent) diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go index 47cb4e9e1d..65405b0fde 100644 --- a/openstack/objectstorage/v1/accounts/testing/requests_test.go +++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go @@ -14,7 +14,10 @@ func TestUpdateAccount(t *testing.T) { defer th.TeardownHTTP() HandleUpdateAccountSuccessfully(t) - options := &accounts.UpdateOpts{Metadata: map[string]string{"gophercloud-test": "accounts"}} + options := &accounts.UpdateOpts{ + Metadata: map[string]string{"gophercloud-test": "accounts"}, + RemoveMetadata: []string{"gophercloud-test-remove"}, + } res := accounts.Update(fake.ServiceClient(), options) th.AssertNoErr(t, res.Err) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index c11241cc2f..24ab79e991 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -388,6 +388,7 @@ type UpdateOptsBuilder interface { // deleting an object's metadata. type UpdateOpts struct { Metadata map[string]string + RemoveMetadata []string ContentDisposition string `h:"Content-Disposition"` ContentEncoding string `h:"Content-Encoding"` ContentType string `h:"Content-Type"` @@ -402,9 +403,14 @@ func (opts UpdateOpts) ToObjectUpdateMap() (map[string]string, error) { if err != nil { return nil, err } + for k, v := range opts.Metadata { h["X-Object-Meta-"+k] = v } + + for _, k := range opts.RemoveMetadata { + h["X-Remove-Object-Meta-"+k] = "remove" + } return h, nil } diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 8d950a3139..9ca126a52b 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -264,6 +264,7 @@ func HandleUpdateObjectSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Object-Meta-Gophercloud-Test", "objects") + th.TestHeader(t, r, "X-Remove-Object-Meta-Gophercloud-Test-Remove", "remove") w.WriteHeader(http.StatusAccepted) }) } diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index ca39fff05d..dadc6bf648 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -238,7 +238,10 @@ func TestUpateObjectMetadata(t *testing.T) { defer th.TeardownHTTP() HandleUpdateObjectSuccessfully(t) - options := &objects.UpdateOpts{Metadata: map[string]string{"Gophercloud-Test": "objects"}} + options := &objects.UpdateOpts{ + Metadata: map[string]string{"Gophercloud-Test": "objects"}, + RemoveMetadata: []string{"Gophercloud-Test-Remove"}, + } res := objects.Update(fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) } From b9712288544b1653a1071786b9d68f4e21e40389 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 2 Dec 2020 12:36:17 -0700 Subject: [PATCH 1170/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34b437af71..2f25981431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ * 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) ## 0.14.0 (November 11, 2020) From 6a2adbc24cfe21da80eac2e4932b65a699dd961b Mon Sep 17 00:00:00 2001 From: Anton Kachurin Date: Tue, 8 Dec 2020 05:36:59 +0300 Subject: [PATCH 1171/2296] Add service catalog listing (#2067) Refers https://docs.openstack.org/api-ref/identity/v3/?expanded=get-service-catalog-detail#get-service-catalog --- .../openstack/identity/v3/catalog_test.go | 25 ++++++ openstack/identity/v3/catalog/requests.go | 14 +++ openstack/identity/v3/catalog/results.go | 26 ++++++ .../v3/catalog/testing/catalog_test.go | 30 +++++++ .../identity/v3/catalog/testing/fixtures.go | 90 +++++++++++++++++++ openstack/identity/v3/catalog/urls.go | 7 ++ 6 files changed, 192 insertions(+) create mode 100644 acceptance/openstack/identity/v3/catalog_test.go create mode 100644 openstack/identity/v3/catalog/requests.go create mode 100644 openstack/identity/v3/catalog/results.go create mode 100644 openstack/identity/v3/catalog/testing/catalog_test.go create mode 100644 openstack/identity/v3/catalog/testing/fixtures.go create mode 100644 openstack/identity/v3/catalog/urls.go diff --git a/acceptance/openstack/identity/v3/catalog_test.go b/acceptance/openstack/identity/v3/catalog_test.go new file mode 100644 index 0000000000..11542c58e6 --- /dev/null +++ b/acceptance/openstack/identity/v3/catalog_test.go @@ -0,0 +1,25 @@ +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/identity/v3/catalog" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestCatalogList(t *testing.T) { + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + allPages, err := catalog.List(client).AllPages() + th.AssertNoErr(t, err) + + allEntities, err := catalog.ExtractServiceCatalog(allPages) + th.AssertNoErr(t, err) + + for _, entity := range allEntities { + tools.PrintResource(t, entity) + } +} diff --git a/openstack/identity/v3/catalog/requests.go b/openstack/identity/v3/catalog/requests.go new file mode 100644 index 0000000000..82773d3a17 --- /dev/null +++ b/openstack/identity/v3/catalog/requests.go @@ -0,0 +1,14 @@ +package catalog + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List enumerates the services available to a specific user. +func List(client *gophercloud.ServiceClient) pagination.Pager { + url := listURL(client) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ServiceCatalogPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/identity/v3/catalog/results.go b/openstack/identity/v3/catalog/results.go new file mode 100644 index 0000000000..c131acd5cf --- /dev/null +++ b/openstack/identity/v3/catalog/results.go @@ -0,0 +1,26 @@ +package catalog + +import ( + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/pagination" +) + +// ServiceCatalogPage is a single page of Service results. +type ServiceCatalogPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if the ServiceCatalogPage contains no results. +func (r ServiceCatalogPage) IsEmpty() (bool, error) { + services, err := ExtractServiceCatalog(r) + return len(services) == 0, err +} + +// ExtractServiceCatalog extracts a slice of Catalog from a Collection acquired from List. +func ExtractServiceCatalog(r pagination.Page) ([]tokens.CatalogEntry, error) { + var s struct { + Entries []tokens.CatalogEntry `json:"catalog"` + } + err := (r.(ServiceCatalogPage)).ExtractInto(&s) + return s.Entries, err +} diff --git a/openstack/identity/v3/catalog/testing/catalog_test.go b/openstack/identity/v3/catalog/testing/catalog_test.go new file mode 100644 index 0000000000..54a08b539e --- /dev/null +++ b/openstack/identity/v3/catalog/testing/catalog_test.go @@ -0,0 +1,30 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/catalog" + "github.com/gophercloud/gophercloud/pagination" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestListCatalog(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListCatalogSuccessfully(t) + + count := 0 + err := catalog.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := catalog.ExtractServiceCatalog(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedCatalogSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} diff --git a/openstack/identity/v3/catalog/testing/fixtures.go b/openstack/identity/v3/catalog/testing/fixtures.go new file mode 100644 index 0000000000..cd46f7cec6 --- /dev/null +++ b/openstack/identity/v3/catalog/testing/fixtures.go @@ -0,0 +1,90 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// ListOutput provides a single page of ServiceCatalog results. +const ListOutput = ` +{ + "catalog": [ + { + "endpoints": [ + { + "id": "39dc322ce86c4111b4f06c2eeae0841b", + "interface": "public", + "region": "RegionOne", + "url": "http://localhost:5000" + }, + { + "id": "ec642f27474842e78bf059f6c48f4e99", + "interface": "internal", + "region": "RegionOne", + "url": "http://localhost:5000" + }, + { + "id": "c609fc430175452290b62a4242e8a7e8", + "interface": "admin", + "region": "RegionOne", + "url": "http://localhost:5000" + } + ], + "id": "4363ae44bdf34a3981fde3b823cb9aa2", + "type": "identity", + "name": "keystone" + } + ], + "links": { + "self": "https://example.com/identity/v3/catalog", + "previous": null, + "next": null + } +} +` + +// ExpectedCatalogSlice is the slice of domains expected to be returned from ListOutput. +var ExpectedCatalogSlice = []tokens.CatalogEntry{{ + ID: "4363ae44bdf34a3981fde3b823cb9aa2", + Name: "keystone", + Type: "identity", + Endpoints: []tokens.Endpoint{ + { + ID: "39dc322ce86c4111b4f06c2eeae0841b", + Interface: "public", + Region: "RegionOne", + URL: "http://localhost:5000", + }, + { + ID: "ec642f27474842e78bf059f6c48f4e99", + Interface: "internal", + Region: "RegionOne", + URL: "http://localhost:5000", + }, + { + ID: "c609fc430175452290b62a4242e8a7e8", + Region: "RegionOne", + Interface: "admin", + URL: "http://localhost:5000", + }, + }, +}} + +// HandleListCatalogSuccessfully creates an HTTP handler at `/domains` on the +// test handler mux that responds with a list of two domains. +func HandleListCatalogSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/auth/catalog", 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/catalog/urls.go b/openstack/identity/v3/catalog/urls.go new file mode 100644 index 0000000000..d3edf6451f --- /dev/null +++ b/openstack/identity/v3/catalog/urls.go @@ -0,0 +1,7 @@ +package catalog + +import "github.com/gophercloud/gophercloud" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("auth", "catalog") +} From 441620ad3de681a8fe168e842e2071d96a822827 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 7 Dec 2020 19:37:48 -0700 Subject: [PATCH 1172/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f25981431..873fcc83e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * 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) ## 0.14.0 (November 11, 2020) From 2f909c712e72c3765762f3ef6b069bcdd960c2f8 Mon Sep 17 00:00:00 2001 From: Georg Schreiber <1942155+codemanufaktur@users.noreply.github.com> Date: Wed, 9 Dec 2020 04:01:10 +0100 Subject: [PATCH 1173/2296] FWaaSv2.0 Policy CRUD (#2057) * FWaaSv2.0 Policy * s/Router/Policy/g as per https://github.com/gophercloud/gophercloud/pull/1789#discussion_r353528203 * Acceptance test for AddRule as per https://github.com/gophercloud/gophercloud/pull/1789#discussion_r353528387 * Acceptance test for RemoveRule as per https://github.com/gophercloud/gophercloud/pull/1789#discussion_r353528397 * fwaas_v2: review fix (https://github.com/gophercloud/gophercloud/pull/1789#discussion_r353527621) from #1789 * openstack/networking/v2/extensions/fwaas_v2/rules/results.go fix firewall_policy_id According https://github.com/openstack/neutron-fwaas/blob/stable/ussuri/neutron_fwaas/db/firewall/v2/firewall_db_v2.py#L609 openstack does return an array here. The docs tell it is a string. * fix goimports * fwaas_v2: testing to remove all rules from firewall policy (https://github.com/gophercloud/gophercloud/pull/1789#discussion_r353527621) * fwaas_v2: extract result & test for policies.AddRule * fwaas_v2: test for RemoveRule * apply suggestions from #2057 Co-authored-by: Colin Fowler Co-authored-by: Colin Fowler Co-authored-by: Schlotter, Christian --- .../v2/extensions/fwaas_v2/fwaas_v2.go | 70 ++++ .../v2/extensions/fwaas_v2/policy_test.go | 78 ++++ .../extensions/fwaas_v2/policies/requests.go | 173 ++++++++ .../extensions/fwaas_v2/policies/results.go | 108 +++++ .../fwaas_v2/policies/testing/doc.go | 2 + .../policies/testing/requests_test.go | 388 ++++++++++++++++++ .../v2/extensions/fwaas_v2/policies/urls.go | 26 ++ .../v2/extensions/fwaas_v2/rules/results.go | 30 +- .../fwaas_v2/rules/testing/requests_test.go | 19 +- 9 files changed, 870 insertions(+), 24 deletions(-) create mode 100644 acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/policies/requests.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/policies/results.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/policies/testing/doc.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/policies/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/fwaas_v2/policies/urls.go 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 b9a8ff0f06..ce3bdbbbb6 100644 --- a/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -8,10 +8,66 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" th "github.com/gophercloud/gophercloud/testhelper" ) +// RemoveRule will remove a rule from the policy. +func RemoveRule(t *testing.T, client *gophercloud.ServiceClient, policyID string, ruleID string) { + t.Logf("Attempting to remove rule %s from policy %s", ruleID, policyID) + + _, err := policies.RemoveRule(client, policyID, ruleID).Extract() + if err != nil { + t.Fatalf("Unable to remove rule %s from policy %s: %v", ruleID, policyID, err) + } +} + +// AddRule will add a rule to to a policy. +func AddRule(t *testing.T, client *gophercloud.ServiceClient, policyID string, ruleID string, beforeRuleID string) { + t.Logf("Attempting to insert rule %s in to policy %s", ruleID, policyID) + + addOpts := policies.InsertRuleOpts{ + ID: ruleID, + InsertBefore: beforeRuleID, + } + + _, err := policies.InsertRule(client, policyID, addOpts).Extract() + if err != nil { + t.Fatalf("Unable to insert rule %s before rule %s in policy %s: %v", ruleID, beforeRuleID, policyID, err) + } +} + +// CreatePolicy will create a Firewall Policy with a random name and given +// rule. An error will be returned if the rule could not be created. +func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string) (*policies.Policy, error) { + policyName := tools.RandomString("TESTACC-", 8) + policyDescription := tools.RandomString("TESTACC-DESC-", 8) + + t.Logf("Attempting to create policy %s", policyName) + + createOpts := policies.CreateOpts{ + Name: policyName, + Description: policyDescription, + FirewallRules: []string{ + ruleID, + }, + } + + policy, err := policies.Create(client, createOpts).Extract() + if err != nil { + return policy, err + } + + t.Logf("Successfully created policy %s", policyName) + + th.AssertEquals(t, policy.Name, policyName) + th.AssertEquals(t, policy.Description, policyDescription) + th.AssertEquals(t, len(policy.Rules), 1) + + return policy, nil +} + // CreateRule will create a Firewall Rule with a random source address and //source port, destination address and port. An error will be returned if // the rule could not be created. @@ -53,6 +109,20 @@ func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, e return rule, nil } +// DeletePolicy will delete a policy 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 DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { + t.Logf("Attempting to delete policy: %s", policyID) + + err := policies.Delete(client, policyID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete policy %s: %v", policyID, err) + } + + t.Logf("Deleted policy: %s", policyID) +} + // DeleteRule will delete a rule with a specified ID. A fatal error will occur // if the delete was not successful. This works best when used as a deferred // function. diff --git a/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go new file mode 100644 index 0000000000..fc3ec1a1da --- /dev/null +++ b/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -0,0 +1,78 @@ +// +build acceptance networking fwaas_v2 + +package fwaas_v2 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/policies" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestPolicyCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create First Rule. This will be used as part of the Policy creation + rule, err := CreateRule(t, client) + th.AssertNoErr(t, err) + defer DeleteRule(t, client, rule.ID) + + tools.PrintResource(t, rule) + + // Create Second rule. This will be injected in to the policy after its creation + ruleToInsert, err := CreateRule(t, client) + th.AssertNoErr(t, err) + defer DeleteRule(t, client, ruleToInsert.ID) + + tools.PrintResource(t, ruleToInsert) + + // Create the Policy using the first rule + policy, err := CreatePolicy(t, client, rule.ID) + th.AssertNoErr(t, err) + defer DeletePolicy(t, client, policy.ID) + + tools.PrintResource(t, policy) + + // Inject the second rule + AddRule(t, client, policy.ID, ruleToInsert.ID, rule.ID) + + // Remove the first rule + RemoveRule(t, client, policy.ID, rule.ID) + + name := "" + description := "" + updateOpts := policies.UpdateOpts{ + Name: &name, + Description: &description, + FirewallRules: &[]string{}, + } + + _, err = policies.Update(client, policy.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + newPolicy, err := policies.Get(client, policy.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newPolicy) + th.AssertEquals(t, newPolicy.Name, name) + th.AssertEquals(t, newPolicy.Description, description) + th.AssertEquals(t, len(newPolicy.Rules), 0) + + allPages, err := policies.List(client, nil).AllPages() + th.AssertNoErr(t, err) + + allPolicies, err := policies.ExtractPolicies(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, policy := range allPolicies { + if policy.ID == newPolicy.ID { + found = true + } + } + + th.AssertEquals(t, found, true) +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go new file mode 100644 index 0000000000..d5a701aa13 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go @@ -0,0 +1,173 @@ +package policies + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToPolicyListQuery() (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 firewall policy attributes you want to see returned. SortKey allows you +// to sort by a particular firewall policy attribute. SortDir sets the direction, +// and is either `asc' or `desc'. Marker and Limit are used for pagination. +type ListOpts struct { + TenantID string `q:"tenant_id"` + Name string `q:"name"` + Description string `q:"description"` + Shared *bool `q:"shared"` + Audited *bool `q:"audited"` + ID string `q:"id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` +} + +// ToPolicyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToPolicyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// firewall policies. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. +// +// Default policy settings return only those firewall policies that are owned by the +// tenant who submits the request, unless an admin user submits the request. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToPolicyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return PolicyPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOptsBuilder is the interface options structs have to satisfy in order +// to be used in the main Create operation in this package. Since many +// extensions decorate or modify the common logic, it is useful for them to +// satisfy a basic interface in order for them to be used. +type CreateOptsBuilder interface { + ToFirewallPolicyCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains all the values needed to create a new firewall policy. +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"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Shared *bool `json:"shared,omitempty"` + Audited *bool `json:"audited,omitempty"` + FirewallRules []string `json:"firewall_rules,omitempty"` +} + +// ToFirewallPolicyCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "firewall_policy") +} + +// Create accepts a CreateOpts struct and uses the values to create a new firewall policy +func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToFirewallPolicyCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + return +} + +// Get retrieves a particular firewall policy based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + return +} + +// UpdateOptsBuilder is the interface options structs have to satisfy in order +// to be used in the main Update operation in this package. Since many +// extensions decorate or modify the common logic, it is useful for them to +// satisfy a basic interface in order for them to be used. +type UpdateOptsBuilder interface { + ToFirewallPolicyUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contains the values used when updating a firewall policy. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Shared *bool `json:"shared,omitempty"` + Audited *bool `json:"audited,omitempty"` + FirewallRules *[]string `json:"firewall_rules,omitempty"` +} + +// ToFirewallPolicyUpdateMap casts a CreateOpts struct to a map. +func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "firewall_policy") +} + +// Update allows firewall policies to be updated. +func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToFirewallPolicyUpdateMap() + 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 policy based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(resourceURL(c, id), nil) + return +} + +type InsertRuleOptsBuilder interface { + ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) +} + +type InsertRuleOpts struct { + ID string `json:"firewall_rule_id" required:"true"` + InsertBefore string `json:"insert_before,omitempty" xor:"InsertAfter"` + InsertAfter string `json:"insert_after,omitempty" xor:"InsertBefore"` +} + +func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +func InsertRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) { + b, err := opts.ToFirewallPolicyInsertRuleMap() + if err != nil { + r.Err = err + return + } + _, r.Err = c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +func RemoveRule(c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { + b := map[string]interface{}{"firewall_rule_id": ruleID} + _, r.Err = c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/results.go b/openstack/networking/v2/extensions/fwaas_v2/policies/results.go new file mode 100644 index 0000000000..a3c212eb6f --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/results.go @@ -0,0 +1,108 @@ +package policies + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Policy is a firewall policy. +type Policy struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + TenantID string `json:"tenant_id"` + Audited bool `json:"audited"` + Shared bool `json:"shared"` + Rules []string `json:"firewall_rules,omitempty"` +} + +type commonResult struct { + gophercloud.Result +} + +type shortResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a firewall policy. +func (r commonResult) Extract() (*Policy, error) { + var s struct { + Policy *Policy `json:"firewall_policy"` + } + err := r.ExtractInto(&s) + return s.Policy, err +} + +// Extract is a function that accepts a shortResult and extracts a firewall policy. +func (r shortResult) Extract() (*Policy, error) { + var policy *Policy + err := r.ExtractInto(&policy) + return policy, err +} + +// PolicyPage is the page returned by a pager when traversing over a +// collection of firewall policies. +type PolicyPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of firewall policies 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 PolicyPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"firewall_policies_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a PolicyPage struct is empty. +func (r PolicyPage) IsEmpty() (bool, error) { + is, err := ExtractPolicies(r) + return len(is) == 0, err +} + +// ExtractPolicies accepts a Page struct, specifically a PolicyPage struct, +// and extracts the elements into a slice of Policy structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractPolicies(r pagination.Page) ([]Policy, error) { + var s struct { + Policies []Policy `json:"firewall_policies"` + } + err := (r.(PolicyPage)).ExtractInto(&s) + return s.Policies, err +} + +// GetResult represents the result of a get operation. +type GetResult struct { + commonResult +} + +// UpdateResult represents the result of an update operation. +type UpdateResult struct { + commonResult +} + +// DeleteResult represents the result of a delete operation. +type DeleteResult struct { + gophercloud.ErrResult +} + +// CreateResult represents the result of a create operation. +type CreateResult struct { + commonResult +} + +// InsertRuleResult represents the result of an InsertRule operation. +type InsertRuleResult struct { + shortResult +} + +// RemoveRuleResult represents the result of a RemoveRule operation. +type RemoveRuleResult struct { + shortResult +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/testing/doc.go b/openstack/networking/v2/extensions/fwaas_v2/policies/testing/doc.go new file mode 100644 index 0000000000..d2707f0c19 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/testing/doc.go @@ -0,0 +1,2 @@ +// networking_extensions_fwaas_policies_v2 +package testing 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 new file mode 100644 index 0000000000..ca0e09263c --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/testing/requests_test.go @@ -0,0 +1,388 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud" + fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/policies" + "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/fwaas/firewall_policies", 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, ` +{ + "firewall_policies": [ + { + "name": "policy1", + "firewall_rules": [ + "75452b36-268e-4e75-aaf4-f0e7ed50bc97", + "c9e77ca0-1bc8-497d-904d-948107873dc6" + ], + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "audited": true, + "shared": false, + "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + "description": "Firewall policy 1" + }, + { + "name": "policy2", + "firewall_rules": [ + "03d2a6ad-633f-431a-8463-4370d06a22c8" + ], + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "audited": false, + "shared": true, + "id": "c854fab5-bdaf-4a86-9359-78de93e5df01", + "description": "Firewall policy 2" + } + ] +} + `) + }) + + count := 0 + + policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := policies.ExtractPolicies(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + + expected := []policies.Policy{ + { + Name: "policy1", + Rules: []string{ + "75452b36-268e-4e75-aaf4-f0e7ed50bc97", + "c9e77ca0-1bc8-497d-904d-948107873dc6", + }, + TenantID: "9145d91459d248b1b02fdaca97c6a75d", + Audited: true, + Shared: false, + ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + Description: "Firewall policy 1", + }, + { + Name: "policy2", + Rules: []string{ + "03d2a6ad-633f-431a-8463-4370d06a22c8", + }, + TenantID: "9145d91459d248b1b02fdaca97c6a75d", + Audited: false, + Shared: true, + ID: "c854fab5-bdaf-4a86-9359-78de93e5df01", + Description: "Firewall policy 2", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_policies", 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, ` +{ + "firewall_policy":{ + "name": "policy", + "firewall_rules": [ + "98a58c87-76be-ae7c-a74e-b77fffb88d95", + "11a58c87-76be-ae7c-a74e-b77fffb88a32" + ], + "description": "Firewall policy", + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "audited": true, + "shared": false + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "firewall_policy":{ + "name": "policy", + "firewall_rules": [ + "98a58c87-76be-ae7c-a74e-b77fffb88d95", + "11a58c87-76be-ae7c-a74e-b77fffb88a32" + ], + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "audited": false, + "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + "description": "Firewall policy" + } +} + `) + }) + + options := policies.CreateOpts{ + TenantID: "9145d91459d248b1b02fdaca97c6a75d", + Name: "policy", + Description: "Firewall policy", + Shared: gophercloud.Disabled, + Audited: gophercloud.Enabled, + FirewallRules: []string{ + "98a58c87-76be-ae7c-a74e-b77fffb88d95", + "11a58c87-76be-ae7c-a74e-b77fffb88a32", + }, + } + + _, err := policies.Create(fake.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) +} + +func TestInsertRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_policies/e3c78ab6-e827-4297-8d68-739063865a8b/insert_rule", 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_rule_id": "7d305689-6cb1-4e75-9f4d-517b9ba792b5", + "insert_before": "3062ed90-1fb0-4c25-af3d-318dff2143ae" +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "audited": false, + "description": "TESTACC-DESC-8P12aLfW", + "firewall_rules": [ + "7d305689-6cb1-4e75-9f4d-517b9ba792b5", + "3062ed90-1fb0-4c25-af3d-318dff2143ae" + ], + "id": "e3c78ab6-e827-4297-8d68-739063865a8b", + "name": "TESTACC-2LnMayeG", + "project_id": "9f98fc0e5f944cd1b51798b668dc8778", + "shared": false, + "tenant_id": "9f98fc0e5f944cd1b51798b668dc8778" +} + `) + }) + + options := policies.InsertRuleOpts{ + ID: "7d305689-6cb1-4e75-9f4d-517b9ba792b5", + InsertBefore: "3062ed90-1fb0-4c25-af3d-318dff2143ae", + } + + policy, err := policies.InsertRule(fake.ServiceClient(), "e3c78ab6-e827-4297-8d68-739063865a8b", options).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "TESTACC-2LnMayeG", policy.Name) + th.AssertEquals(t, 2, len(policy.Rules)) + th.AssertEquals(t, "7d305689-6cb1-4e75-9f4d-517b9ba792b5", policy.Rules[0]) + th.AssertEquals(t, "3062ed90-1fb0-4c25-af3d-318dff2143ae", policy.Rules[1]) + th.AssertEquals(t, "e3c78ab6-e827-4297-8d68-739063865a8b", policy.ID) + th.AssertEquals(t, "TESTACC-DESC-8P12aLfW", policy.Description) + th.AssertEquals(t, "9f98fc0e5f944cd1b51798b668dc8778", policy.TenantID) + +} + +func TestInsertRuleWithInvalidParameters(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + //invalid opts, its not allowed to specify InsertBefore and InsertAfter together + options := policies.InsertRuleOpts{ + ID: "unknown", + InsertBefore: "1", + InsertAfter: "2", + } + + _, err := policies.InsertRule(fake.ServiceClient(), "0", options).Extract() + + // expect to fail with an gophercloud error + th.AssertErr(t, err) + th.AssertEquals(t, "Exactly one of InsertBefore and InsertAfter must be provided", err.Error()) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_policies/f2b08c1e-aa81-4668-8ae1-1401bcb0576c", 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, ` +{ + "firewall_policy":{ + "name": "www", + "firewall_rules": [ + "75452b36-268e-4e75-aaf4-f0e7ed50bc97", + "c9e77ca0-1bc8-497d-904d-948107873dc6", + "03d2a6ad-633f-431a-8463-4370d06a22c8" + ], + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "audited": false, + "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + "description": "Firewall policy web" + } +} + `) + }) + + policy, err := policies.Get(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "www", policy.Name) + th.AssertEquals(t, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", policy.ID) + th.AssertEquals(t, "Firewall policy web", policy.Description) + th.AssertEquals(t, 3, len(policy.Rules)) + th.AssertEquals(t, "75452b36-268e-4e75-aaf4-f0e7ed50bc97", policy.Rules[0]) + 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) +} + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_policies/f2b08c1e-aa81-4668-8ae1-1401bcb0576c", 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_policy":{ + "name": "policy", + "firewall_rules": [ + "98a58c87-76be-ae7c-a74e-b77fffb88d95", + "11a58c87-76be-ae7c-a74e-b77fffb88a32" + ], + "description": "Firewall policy" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "firewall_policy":{ + "name": "policy", + "firewall_rules": [ + "75452b36-268e-4e75-aaf4-f0e7ed50bc97", + "c9e77ca0-1bc8-497d-904d-948107873dc6", + "03d2a6ad-633f-431a-8463-4370d06a22c8" + ], + "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", + "audited": false, + "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", + "description": "Firewall policy" + } +} + `) + }) + + name := "policy" + description := "Firewall policy" + + options := policies.UpdateOpts{ + Name: &name, + Description: &description, + FirewallRules: &[]string{ + "98a58c87-76be-ae7c-a74e-b77fffb88d95", + "11a58c87-76be-ae7c-a74e-b77fffb88a32", + }, + } + + _, err := policies.Update(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract() + th.AssertNoErr(t, err) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_policies/4ec89077-d057-4a2b-911f-60a3b47ee304", 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) + }) + + res := policies.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") + th.AssertNoErr(t, res.Err) +} + +func TestRemoveRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/fwaas/firewall_policies/9fed8075-06ee-463f-83a6-d4118791b02f/remove_rule", 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_rule_id": "9fed8075-06ee-463f-83a6-d4118791b02f" +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` +{ + "audited": false, + "description": "TESTACC-DESC-skno2e52", + "firewall_rules": [ + "3ccc0f2b-4a04-4e7c-bb47-dd1701127a47" + ], + "id": "9fed8075-06ee-463f-83a6-d4118791b02f", + "name": "TESTACC-Qf7pMSkq", + "project_id": "TESTID-era34jkaslk", + "shared": false, + "tenant_id": "TESTID-334sdfassdf" +} + `) + }) + + policy, err := policies.RemoveRule(fake.ServiceClient(), "9fed8075-06ee-463f-83a6-d4118791b02f", "9fed8075-06ee-463f-83a6-d4118791b02f").Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, "9fed8075-06ee-463f-83a6-d4118791b02f", policy.ID) + +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/urls.go b/openstack/networking/v2/extensions/fwaas_v2/policies/urls.go new file mode 100644 index 0000000000..5411379a07 --- /dev/null +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/urls.go @@ -0,0 +1,26 @@ +package policies + +import "github.com/gophercloud/gophercloud" + +const ( + rootPath = "fwaas" + resourcePath = "firewall_policies" + insertPath = "insert_rule" + removePath = "remove_rule" +) + +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) +} + +func insertURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id, insertPath) +} + +func removeURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(rootPath, resourcePath, id, removePath) +} diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go index 6308682522..ab51ef9f47 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go @@ -7,21 +7,21 @@ import ( // Rule represents a firewall rule type Rule struct { - ID string `json:"id"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Protocol string `json:"protocol"` - Action string `json:"action"` - IPVersion int `json:"ip_version,omitempty"` - SourceIPAddress string `json:"source_ip_address,omitempty"` - DestinationIPAddress string `json:"destination_ip_address,omitempty"` - SourcePort string `json:"source_port,omitempty"` - DestinationPort string `json:"destination_port,omitempty"` - Shared bool `json:"shared,omitempty"` - Enabled bool `json:"enabled,omitempty"` - FirewallPolicyID string `json:"firewall_policy_id"` - TenantID string `json:"tenant_id"` - ProjectID string `json:"project_id"` + ID string `json:"id"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Protocol string `json:"protocol"` + Action string `json:"action"` + IPVersion int `json:"ip_version,omitempty"` + SourceIPAddress string `json:"source_ip_address,omitempty"` + DestinationIPAddress string `json:"destination_ip_address,omitempty"` + SourcePort string `json:"source_port,omitempty"` + DestinationPort string `json:"destination_port,omitempty"` + Shared bool `json:"shared,omitempty"` + Enabled bool `json:"enabled,omitempty"` + FirewallPolicyID []string `json:"firewall_policy_id"` + TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` } // RulePage is the page returned by a pager when traversing over a 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 2d509d37ca..7330833654 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 @@ -32,7 +32,7 @@ func TestList(t *testing.T) { "source_port": null, "source_ip_address": null, "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "firewall_policy_id": ["e2a5fb51-698c-4898-87e8-f1eee6b50919"], "destination_port": "22", "id": "f03bd950-6c56-4f5e-a307-45967078f507", "name": "ssh_form_any", @@ -48,7 +48,7 @@ func TestList(t *testing.T) { "source_port": null, "source_ip_address": null, "destination_ip_address": null, - "firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", + "firewall_policy_id": ["98d7fb51-698c-4123-87e8-f1eee6b5ab7e"], "destination_port": null, "id": "ab7bd950-6c56-4f5e-a307-45967078f890", "name": "deny_all_udp", @@ -80,7 +80,7 @@ func TestList(t *testing.T) { SourcePort: "", SourceIPAddress: "", DestinationIPAddress: "192.168.1.0/24", - FirewallPolicyID: "e2a5fb51-698c-4898-87e8-f1eee6b50919", + FirewallPolicyID: []string{"e2a5fb51-698c-4898-87e8-f1eee6b50919"}, DestinationPort: "22", ID: "f03bd950-6c56-4f5e-a307-45967078f507", Name: "ssh_form_any", @@ -96,7 +96,7 @@ func TestList(t *testing.T) { SourcePort: "", SourceIPAddress: "", DestinationIPAddress: "", - FirewallPolicyID: "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", + FirewallPolicyID: []string{"98d7fb51-698c-4123-87e8-f1eee6b5ab7e"}, DestinationPort: "", ID: "ab7bd950-6c56-4f5e-a307-45967078f890", Name: "deny_all_udp", @@ -151,7 +151,7 @@ func TestCreate(t *testing.T) { "source_port": null, "source_ip_address": null, "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "firewall_policy_id": ["e2a5fb51-698c-4898-87e8-f1eee6b50919"], "position": 2, "destination_port": "22", "id": "f03bd950-6c56-4f5e-a307-45967078f507", @@ -213,7 +213,7 @@ func TestCreateAnyProtocol(t *testing.T) { "source_port": null, "source_ip_address": null, "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "firewall_policy_id": ["e2a5fb51-698c-4898-87e8-f1eee6b50919"], "position": 2, "destination_port": null, "id": "f03bd950-6c56-4f5e-a307-45967078f507", @@ -260,7 +260,7 @@ func TestGet(t *testing.T) { "source_port": null, "source_ip_address": null, "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "firewall_policy_id": ["e2a5fb51-698c-4898-87e8-f1eee6b50919"], "position": 2, "destination_port": "22", "id": "f03bd950-6c56-4f5e-a307-45967078f507", @@ -281,7 +281,8 @@ func TestGet(t *testing.T) { th.AssertEquals(t, "tcp", rule.Protocol) th.AssertEquals(t, "ssh rule", rule.Description) th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress) - th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.FirewallPolicyID) + th.AssertEquals(t, 1, len(rule.FirewallPolicyID)) + th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.FirewallPolicyID[0]) th.AssertEquals(t, "22", rule.DestinationPort) th.AssertEquals(t, "f03bd950-6c56-4f5e-a307-45967078f507", rule.ID) th.AssertEquals(t, "ssh_form_any", rule.Name) @@ -324,7 +325,7 @@ func TestUpdate(t *testing.T) { "protocol": "tcp", "description": "ssh rule", "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", + "firewall_policy_id": ["e2a5fb51-698c-4898-87e8-f1eee6b50919"], "position": 2, "destination_port": "22", "id": "f03bd950-6c56-4f5e-a307-45967078f507", From 93f98a54e84c872c6ba5c12d360af1657a69f3c2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 8 Dec 2020 20:02:43 -0700 Subject: [PATCH 1174/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 873fcc83e3..3a9581b46d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ * 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) ## 0.14.0 (November 11, 2020) From 3647be39edaff083dac47a526700d3a5c2964081 Mon Sep 17 00:00:00 2001 From: Mike Fedosin Date: Sat, 12 Dec 2020 04:20:44 +0100 Subject: [PATCH 1175/2296] Cleanup: fix formatting (#2068) Generated by `gofmt -s -w .` --- .../openstack/clustering/v1/nodes_test.go | 12 ++--- .../compute/v2/bootfromvolume_test.go | 22 ++++----- acceptance/openstack/compute/v2/compute.go | 16 +++---- .../networking/v2/extensions/dns/dns.go | 2 +- .../networking/v2/extensions/extensions.go | 2 +- .../extensions/portsbinding/portsbinding.go | 2 +- .../openstack/networking/v2/networking.go | 10 ++-- .../openstack/networking/v2/ports_test.go | 4 +- .../objectstorage/v1/containers_test.go | 4 +- .../v1/introspection/testing/fixtures.go | 10 ++-- .../cdn/v1/flavors/testing/requests_test.go | 8 ++-- .../cdn/v1/services/testing/requests_test.go | 6 +-- .../v1/policies/testing/fixtures.go | 4 +- .../v1/policytypes/testing/fixtures.go | 2 +- .../v2/extensions/secgroups/requests.go | 2 +- .../v2/extensions/testing/delegate_test.go | 2 +- .../v2/flavors/testing/requests_test.go | 4 +- .../compute/v2/servers/testing/fixtures.go | 10 ++-- .../container/v1/capsules/testing/fixtures.go | 12 ++--- .../clustertemplates/testing/requests_test.go | 2 +- .../db/v1/users/testing/requests_test.go | 2 +- .../identity/v2/tokens/testing/fixtures.go | 2 +- .../testing/fixtures.go | 14 +++--- .../testing/requests_test.go | 10 ++-- .../identity/v3/tokens/testing/fixtures.go | 12 ++--- .../v2/apiversions/testing/fixture.go | 8 ++-- .../layer3/routers/testing/requests_test.go | 2 +- .../provider/testing/results_test.go | 4 +- .../v2/ports/testing/requests_test.go | 2 +- .../v1/apiversions/testing/requests_test.go | 2 +- .../v1/resourcetypes/testing/fixtures.go | 40 ++++++++-------- .../v1/resourceproviders/testing/fixtures.go | 6 +-- .../v2/snapshots/testing/request_test.go | 2 +- openstack/testing/endpoint_location_test.go | 48 +++++++++---------- .../v2/crontriggers/testing/requests_test.go | 10 ++-- .../v2/executions/testing/requests_test.go | 8 ++-- .../v2/workflows/testing/requests_test.go | 10 ++-- pagination/linked.go | 2 +- 38 files changed, 160 insertions(+), 160 deletions(-) diff --git a/acceptance/openstack/clustering/v1/nodes_test.go b/acceptance/openstack/clustering/v1/nodes_test.go index 5f65ce3893..58fcbf33b0 100644 --- a/acceptance/openstack/clustering/v1/nodes_test.go +++ b/acceptance/openstack/clustering/v1/nodes_test.go @@ -145,18 +145,18 @@ func TestNodesRecover(t *testing.T) { // TODO: nodes.RebuildRecovery is commented out as of 12/14/2018 the API backend can't perform the action without returning error ops := []nodes.RecoverOpts{ // Microversion < 1.6 legacy support where argument DOES NOT support Check - nodes.RecoverOpts{}, - nodes.RecoverOpts{Operation: nodes.RebootRecovery}, + {}, + {Operation: nodes.RebootRecovery}, // nodes.RecoverOpts{Operation: nodes.RebuildRecovery}, // MicroVersion >= 1.6 that supports Check where Check is true - nodes.RecoverOpts{Check: &checkTrue}, - nodes.RecoverOpts{Operation: nodes.RebootRecovery, Check: &checkTrue}, + {Check: &checkTrue}, + {Operation: nodes.RebootRecovery, Check: &checkTrue}, //nodes.RecoverOpts{Operation: nodes.RebuildRecovery, Check: &checkTrue}, // MicroVersion >= 1.6 that supports Check where Check is false - nodes.RecoverOpts{Check: &checkFalse}, - nodes.RecoverOpts{Operation: nodes.RebootRecovery, Check: &checkFalse}, + {Check: &checkFalse}, + {Operation: nodes.RebootRecovery, Check: &checkFalse}, //nodes.RecoverOpts{Operation: nodes.RebuildRecovery, Check: &checkFalse}, } diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go index ec5d72f757..9128747810 100644 --- a/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -23,7 +23,7 @@ func TestBootFromImage(t *testing.T) { th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ + { BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, @@ -51,7 +51,7 @@ func TestBootFromNewVolume(t *testing.T) { th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ + { DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceImage, @@ -97,7 +97,7 @@ func TestBootFromExistingVolume(t *testing.T) { tools.PrintResource(t, volume) blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ + { DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceVolume, @@ -137,7 +137,7 @@ func TestBootFromMultiEphemeralServer(t *testing.T) { th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ + { BootIndex: 0, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, @@ -145,7 +145,7 @@ func TestBootFromMultiEphemeralServer(t *testing.T) { UUID: choices.ImageID, VolumeSize: 5, }, - bootfromvolume.BlockDevice{ + { BootIndex: -1, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, @@ -153,7 +153,7 @@ func TestBootFromMultiEphemeralServer(t *testing.T) { SourceType: bootfromvolume.SourceBlank, VolumeSize: 1, }, - bootfromvolume.BlockDevice{ + { BootIndex: -1, DestinationType: bootfromvolume.DestinationLocal, DeleteOnTermination: true, @@ -180,14 +180,14 @@ func TestAttachNewVolume(t *testing.T) { th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ + { BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, }, - bootfromvolume.BlockDevice{ + { BootIndex: 1, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, @@ -231,14 +231,14 @@ func TestAttachExistingVolume(t *testing.T) { th.AssertNoErr(t, err) blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ + { BootIndex: 0, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationLocal, SourceType: bootfromvolume.SourceImage, UUID: choices.ImageID, }, - bootfromvolume.BlockDevice{ + { BootIndex: 1, DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, @@ -283,7 +283,7 @@ func TestBootFromNewCustomizedVolume(t *testing.T) { } blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ + { DeleteOnTermination: true, DestinationType: bootfromvolume.DestinationVolume, SourceType: bootfromvolume.SourceImage, diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index d4a9edf735..1ef17dd933 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -151,7 +151,7 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, Name: name, FlavorRef: choices.FlavorID, Networks: []servers.Network{ - servers.Network{UUID: networkID}, + {UUID: networkID}, }, } @@ -320,7 +320,7 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, Networks: []servers.Network{ - servers.Network{UUID: networkID}, + {UUID: networkID}, }, } @@ -456,7 +456,7 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser ImageRef: choices.ImageID, AdminPass: pwd, Networks: []servers.Network{ - servers.Network{UUID: networkID}, + {UUID: networkID}, }, Metadata: map[string]string{ "abc": "def", @@ -516,7 +516,7 @@ func CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) ( ImageRef: choices.ImageID, AdminPass: pwd, Networks: []servers.Network{ - servers.Network{UUID: networkID}, + {UUID: networkID}, }, Metadata: map[string]string{ "abc": "def", @@ -567,7 +567,7 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient FlavorRef: choices.FlavorID, AdminPass: pwd, Networks: []servers.Network{ - servers.Network{UUID: networkID}, + {UUID: networkID}, }, Personality: servers.Personality{ &servers.File{ @@ -610,7 +610,7 @@ func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, netwo ImageRef: choices.ImageID, AdminPass: pwd, Networks: []servers.Network{ - servers.Network{UUID: networkID}, + {UUID: networkID}, }, Metadata: map[string]string{ "abc": "def", @@ -719,7 +719,7 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, ImageRef: choices.ImageID, AdminPass: pwd, Networks: []servers.Network{ - servers.Network{UUID: networkID}, + {UUID: networkID}, }, } @@ -771,7 +771,7 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, Networks: []servers.Network{ - servers.Network{UUID: networkID}, + {UUID: networkID}, }, } diff --git a/acceptance/openstack/networking/v2/extensions/dns/dns.go b/acceptance/openstack/networking/v2/extensions/dns/dns.go index dba9dfed9b..c625d68be9 100644 --- a/acceptance/openstack/networking/v2/extensions/dns/dns.go +++ b/acceptance/openstack/networking/v2/extensions/dns/dns.go @@ -44,7 +44,7 @@ func CreatePortDNS(t *testing.T, client *gophercloud.ServiceClient, networkID, s Name: portName, Description: portDescription, AdminStateUp: &iFalse, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + FixedIPs: []ports.IP{{SubnetID: subnetID}}, } createOpts := dns.PortCreateOptsExt{ diff --git a/acceptance/openstack/networking/v2/extensions/extensions.go b/acceptance/openstack/networking/v2/extensions/extensions.go index a05d0ed78f..fe2999d066 100644 --- a/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/acceptance/openstack/networking/v2/extensions/extensions.go @@ -62,7 +62,7 @@ func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient Name: portName, Description: portDescription, AdminStateUp: &iFalse, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + FixedIPs: []ports.IP{{SubnetID: subnetID}}, SecurityGroups: &[]string{secGroupID}, } diff --git a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index ef3179e6a9..2957176311 100644 --- a/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -30,7 +30,7 @@ func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, network Name: portName, Description: portDescription, AdminStateUp: &iFalse, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + FixedIPs: []ports.IP{{SubnetID: subnetID}}, } createOpts := portsbinding.CreateOptsExt{ diff --git a/acceptance/openstack/networking/v2/networking.go b/acceptance/openstack/networking/v2/networking.go index f0c56fcaad..9362c9b623 100644 --- a/acceptance/openstack/networking/v2/networking.go +++ b/acceptance/openstack/networking/v2/networking.go @@ -88,7 +88,7 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subn Name: portName, Description: portDescription, AdminStateUp: gophercloud.Enabled, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + FixedIPs: []ports.IP{{SubnetID: subnetID}}, } port, err := ports.Create(client, createOpts).Extract() @@ -125,7 +125,7 @@ func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClie NetworkID: networkID, Name: portName, AdminStateUp: &iFalse, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + FixedIPs: []ports.IP{{SubnetID: subnetID}}, SecurityGroups: &[]string{}, } @@ -161,7 +161,7 @@ func CreatePortWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClie NetworkID: networkID, Name: portName, AdminStateUp: gophercloud.Enabled, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + FixedIPs: []ports.IP{{SubnetID: subnetID}}, } iFalse := false @@ -202,7 +202,7 @@ func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient NetworkID: networkID, Name: portName, AdminStateUp: gophercloud.Enabled, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + FixedIPs: []ports.IP{{SubnetID: subnetID}}, } createOpts := extradhcpopts.CreateOptsExt{ @@ -248,7 +248,7 @@ func CreatePortWithMultipleFixedIPs(t *testing.T, client *gophercloud.ServiceCli Name: portName, Description: portDescription, AdminStateUp: gophercloud.Enabled, - FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}, ports.IP{SubnetID: subnetID}}, + FixedIPs: []ports.IP{{SubnetID: subnetID}, {SubnetID: subnetID}}, } port, err := ports.Create(client, createOpts).Extract() diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go index b50edbb0f1..6355e99491 100644 --- a/acceptance/openstack/networking/v2/ports_test.go +++ b/acceptance/openstack/networking/v2/ports_test.go @@ -215,7 +215,7 @@ func TestPortsRemoveAddressPair(t *testing.T) { // Add an address pair to the port updateOpts := ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{ - ports.AddressPair{IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, + {IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, }, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() @@ -259,7 +259,7 @@ func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { // Add an address pair to the port updateOpts := ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{ - ports.AddressPair{IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, + {IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, }, } newPort, err := ports.Update(client, port.ID, updateOpts).Extract() diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index 1f6738b78b..c6f1b6a67c 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -78,7 +78,7 @@ func TestContainers(t *testing.T) { // After the tests are done, delete the metadata that was set. defer func() { temp := []string{} - for k, _ := range metadata { + for k := range metadata { temp = append(temp, k) } res := containers.Update(client, cNames[0], &containers.UpdateOpts{RemoveMetadata: temp}) @@ -91,7 +91,7 @@ func TestContainers(t *testing.T) { cm, err := containers.Get(client, cNames[0], getOpts).ExtractMetadata() th.AssertNoErr(t, err) - for k, _ := range metadata { + for k := range metadata { if _, ok := cm[k]; ok { t.Errorf("Unexpected custom metadata with key: %s", k) } diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index 97070dbffc..bc7bf3a24c 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -390,7 +390,7 @@ var ( Architecture: "x86_64", }, Disks: []introspection.RootDiskType{ - introspection.RootDiskType{ + { Rotational: true, Model: "", Name: "/dev/vda", @@ -399,7 +399,7 @@ var ( }, }, Interfaces: []introspection.InterfaceType{ - introspection.InterfaceType{ + { Vendor: "0x1af4", HasCarrier: true, MACAddress: "52:54:00:47:20:4d", @@ -408,7 +408,7 @@ var ( IPV4Address: "172.24.42.101", LLDP: []introspection.LLDPTLVType{}, }, - introspection.InterfaceType{ + { IPV4Address: "172.24.42.100", MACAddress: "52:54:00:4e:3d:30", Name: "eth0", @@ -416,11 +416,11 @@ var ( HasCarrier: true, Vendor: "0x1af4", LLDP: []introspection.LLDPTLVType{ - introspection.LLDPTLVType{ + { Type: 1, Value: "04112233aabbcc", }, - introspection.LLDPTLVType{ + { Type: 5, Value: "737730312d646973742d31622d623132", }, diff --git a/openstack/cdn/v1/flavors/testing/requests_test.go b/openstack/cdn/v1/flavors/testing/requests_test.go index bc4b1a50e5..d84b600a29 100644 --- a/openstack/cdn/v1/flavors/testing/requests_test.go +++ b/openstack/cdn/v1/flavors/testing/requests_test.go @@ -33,7 +33,7 @@ func TestList(t *testing.T) { { Provider: "Fastly", Links: []gophercloud.Link{ - gophercloud.Link{ + { Href: "http://www.fastly.com", Rel: "provider_url", }, @@ -41,7 +41,7 @@ func TestList(t *testing.T) { }, }, Links: []gophercloud.Link{ - gophercloud.Link{ + { Href: "https://www.poppycdn.io/v1.0/flavors/europe", Rel: "self", }, @@ -69,7 +69,7 @@ func TestGet(t *testing.T) { { Provider: "ChinaCache", Links: []gophercloud.Link{ - gophercloud.Link{ + { Href: "http://www.chinacache.com", Rel: "provider_url", }, @@ -77,7 +77,7 @@ func TestGet(t *testing.T) { }, }, Links: []gophercloud.Link{ - gophercloud.Link{ + { Href: "https://www.poppycdn.io/v1.0/flavors/asia", Rel: "self", }, diff --git a/openstack/cdn/v1/services/testing/requests_test.go b/openstack/cdn/v1/services/testing/requests_test.go index 0abc98ef3d..52854163af 100644 --- a/openstack/cdn/v1/services/testing/requests_test.go +++ b/openstack/cdn/v1/services/testing/requests_test.go @@ -133,15 +133,15 @@ func TestList(t *testing.T) { FlavorID: "europe", Status: "deployed", Links: []gophercloud.Link{ - gophercloud.Link{ + { Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", Rel: "self", }, - gophercloud.Link{ + { Href: "myothersite.com.poppycdn.net", Rel: "access_url", }, - gophercloud.Link{ + { Href: "https://www.poppycdn.io/v1.0/flavors/europe", Rel: "flavor", }, diff --git a/openstack/clustering/v1/policies/testing/fixtures.go b/openstack/clustering/v1/policies/testing/fixtures.go index cf03210184..90a01fd7fe 100644 --- a/openstack/clustering/v1/policies/testing/fixtures.go +++ b/openstack/clustering/v1/policies/testing/fixtures.go @@ -300,8 +300,8 @@ var ExpectedPolicy2 = policies.Policy{ } var ExpectedPolicies = [][]policies.Policy{ - []policies.Policy{ExpectedPolicy1}, - []policies.Policy{ExpectedPolicy2}, + {ExpectedPolicy1}, + {ExpectedPolicy2}, } var ExpectedCreatePolicy = policies.Policy{ diff --git a/openstack/clustering/v1/policytypes/testing/fixtures.go b/openstack/clustering/v1/policytypes/testing/fixtures.go index 2732dd9ebf..c9492b6e75 100644 --- a/openstack/clustering/v1/policytypes/testing/fixtures.go +++ b/openstack/clustering/v1/policytypes/testing/fixtures.go @@ -199,7 +199,7 @@ var ExpectedPolicyTypeDetail = &policytypes.PolicyTypeDetail{ }, }, SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": []policytypes.SupportStatusType{ + "1.0": { { Status: "EXPERIMENTAL", Since: "2017.02", diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go index 9a5070bd91..6f174bb95d 100644 --- a/openstack/compute/v2/extensions/secgroups/requests.go +++ b/openstack/compute/v2/extensions/secgroups/requests.go @@ -171,7 +171,7 @@ func DeleteRule(client *gophercloud.ServiceClient, id string) (r DeleteRuleResul func actionMap(prefix, groupName string) map[string]map[string]string { return map[string]map[string]string{ - prefix + "SecurityGroup": map[string]string{"name": groupName}, + prefix + "SecurityGroup": {"name": groupName}, } } diff --git a/openstack/compute/v2/extensions/testing/delegate_test.go b/openstack/compute/v2/extensions/testing/delegate_test.go index 822093ff47..2d33c86343 100644 --- a/openstack/compute/v2/extensions/testing/delegate_test.go +++ b/openstack/compute/v2/extensions/testing/delegate_test.go @@ -23,7 +23,7 @@ func TestList(t *testing.T) { th.AssertNoErr(t, err) expected := []common.Extension{ - common.Extension{ + { Updated: "2013-01-20T00:00:00-00:00", Name: "Neutron Service Type Management", Links: []interface{}{}, diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 14a9388edb..3dddcd34fe 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -237,7 +237,7 @@ func TestFlavorAccessesList(t *testing.T) { }) expected := []flavors.FlavorAccess{ - flavors.FlavorAccess{ + { FlavorID: "12345678", TenantID: "2f954bcf047c4ee9b09a37d49ae6db54", }, @@ -285,7 +285,7 @@ func TestFlavorAccessAdd(t *testing.T) { }) expected := []flavors.FlavorAccess{ - flavors.FlavorAccess{ + { FlavorID: "12345678", TenantID: "2f954bcf047c4ee9b09a37d49ae6db54", }, diff --git a/openstack/compute/v2/servers/testing/fixtures.go b/openstack/compute/v2/servers/testing/fixtures.go index d81047bc1e..f5dd178fa2 100644 --- a/openstack/compute/v2/servers/testing/fixtures.go +++ b/openstack/compute/v2/servers/testing/fixtures.go @@ -540,7 +540,7 @@ var ( }, }, SecurityGroups: []map[string]interface{}{ - map[string]interface{}{ + { "name": "default", }, }, @@ -599,7 +599,7 @@ var ( Metadata: map[string]string{}, AttachedVolumes: []servers.AttachedVolume{}, SecurityGroups: []map[string]interface{}{ - map[string]interface{}{ + { "name": "default", }, }, @@ -652,7 +652,7 @@ var ( Metadata: map[string]string{}, AttachedVolumes: []servers.AttachedVolume{}, SecurityGroups: []map[string]interface{}{ - map[string]interface{}{ + { "name": "default", }, }, @@ -1119,7 +1119,7 @@ func HandleMetadataUpdateSuccessfully(t *testing.T) { // ListAddressesExpected represents an expected repsonse from a ListAddresses request. var ListAddressesExpected = map[string][]servers.Address{ - "public": []servers.Address{ + "public": { { Version: 4, Address: "50.56.176.35", @@ -1129,7 +1129,7 @@ var ListAddressesExpected = map[string][]servers.Address{ Address: "2001:4800:790e:510:be76:4eff:fe04:84a8", }, }, - "private": []servers.Address{ + "private": { { Version: 4, Address: "10.180.3.155", diff --git a/openstack/container/v1/capsules/testing/fixtures.go b/openstack/container/v1/capsules/testing/fixtures.go index 4d6e785124..8e82f25a42 100644 --- a/openstack/container/v1/capsules/testing/fixtures.go +++ b/openstack/container/v1/capsules/testing/fixtures.go @@ -101,7 +101,7 @@ var ValidJSONTemplateParsed = map[string]interface{}{ "spec": map[string]interface{}{ "restartPolicy": "Always", "containers": []map[string]interface{}{ - map[string]interface{}{ + { "image": "ubuntu", "command": []interface{}{ "/bin/bash", @@ -145,7 +145,7 @@ var ValidYAMLTemplateParsed = map[string]interface{}{ "spec": map[interface{}]interface{}{ "restartPolicy": "Always", "containers": []map[interface{}]interface{}{ - map[interface{}]interface{}{ + { "image": "ubuntu", "command": []interface{}{ "/bin/bash", @@ -497,7 +497,7 @@ var ExpectedContainer1 = capsules.Container{ "Name": "always", }, Addresses: map[string][]capsules.Address{ - "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ + "b1295212-64e1-471d-aa01-25ff46f9818d": { { PreserveOnDelete: false, Addr: "172.24.4.11", @@ -536,7 +536,7 @@ var ExpectedCapsule = capsules.Capsule{ "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", }, Addresses: map[string][]capsules.Address{ - "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ + "b1295212-64e1-471d-aa01-25ff46f9818d": { { PreserveOnDelete: false, Addr: "172.24.4.11", @@ -547,7 +547,7 @@ var ExpectedCapsule = capsules.Capsule{ }, }, VolumesInfo: map[string][]string{ - "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": []string{ + "67618d54-dd55-4f7e-91b3-39ffb3ba7f5f": { "1739e28a-d391-4fd9-93a5-3ba3f29a4c9b", }, }, @@ -584,7 +584,7 @@ var ExpectedCapsuleV132 = capsules.CapsuleV132{ "web": "app", }, Addresses: map[string][]capsules.Address{ - "b1295212-64e1-471d-aa01-25ff46f9818d": []capsules.Address{ + "b1295212-64e1-471d-aa01-25ff46f9818d": { { PreserveOnDelete: false, Addr: "172.24.4.11", diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index cbed6b1365..f78f67ba38 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -87,7 +87,7 @@ func TestListClusterTemplates(t *testing.T) { count++ actual, err := clustertemplates.ExtractClusterTemplates(page) th.AssertNoErr(t, err) - for idx, _ := range actual { + for idx := range actual { actual[idx].CreatedAt = actual[idx].CreatedAt.UTC() } th.AssertDeepEquals(t, ExpectedClusterTemplates, actual) diff --git a/openstack/db/v1/users/testing/requests_test.go b/openstack/db/v1/users/testing/requests_test.go index 952f245eb7..23a2b1d006 100644 --- a/openstack/db/v1/users/testing/requests_test.go +++ b/openstack/db/v1/users/testing/requests_test.go @@ -45,7 +45,7 @@ func TestUserList(t *testing.T) { expectedUsers := []users.User{ { Databases: []db.Database{ - db.Database{Name: "databaseA"}, + {Name: "databaseA"}, }, Name: "dbuser3", }, diff --git a/openstack/identity/v2/tokens/testing/fixtures.go b/openstack/identity/v2/tokens/testing/fixtures.go index 0a8544ba86..1a819d1e5c 100644 --- a/openstack/identity/v2/tokens/testing/fixtures.go +++ b/openstack/identity/v2/tokens/testing/fixtures.go @@ -58,7 +58,7 @@ var ExpectedServiceCatalog = &tokens.ServiceCatalog{ var ExpectedUser = &tokens.User{ ID: "a530fefc3d594c4ba2693a4ecd6be74e", Name: "apiserver", - Roles: []tokens.Role{tokens.Role{Name: "member"}, tokens.Role{Name: "service"}}, + Roles: []tokens.Role{{Name: "member"}, {Name: "service"}}, UserName: "apiserver", } diff --git a/openstack/identity/v3/applicationcredentials/testing/fixtures.go b/openstack/identity/v3/applicationcredentials/testing/fixtures.go index 25803415db..b2a749bbf3 100644 --- a/openstack/identity/v3/applicationcredentials/testing/fixtures.go +++ b/openstack/identity/v3/applicationcredentials/testing/fixtures.go @@ -287,7 +287,7 @@ var ApplicationCredential = applicationcredentials.ApplicationCredential{ Secret: "", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ - applicationcredentials.Role{ + { ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, @@ -314,7 +314,7 @@ var ApplicationCredentialNoSecretResponse = applicationcredentials.ApplicationCr Secret: "generated_secret", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ - applicationcredentials.Role{ + { ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, @@ -334,11 +334,11 @@ var UnrestrictedApplicationCredential = applicationcredentials.ApplicationCreden Secret: "", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ - applicationcredentials.Role{ + { ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, - applicationcredentials.Role{ + { ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", Name: "network_viewer", }, @@ -357,7 +357,7 @@ var FirstApplicationCredential = applicationcredentials.ApplicationCredential{ Secret: "", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ - applicationcredentials.Role{ + { ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, @@ -376,11 +376,11 @@ var SecondApplicationCredential = applicationcredentials.ApplicationCredential{ Secret: "", ProjectID: "53c2b94f63fb4f43a21b92d119ce549f", Roles: []applicationcredentials.Role{ - applicationcredentials.Role{ + { ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer", }, - applicationcredentials.Role{ + { ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", Name: "network_viewer", }, diff --git a/openstack/identity/v3/applicationcredentials/testing/requests_test.go b/openstack/identity/v3/applicationcredentials/testing/requests_test.go index c14ed379f8..31d76b9432 100644 --- a/openstack/identity/v3/applicationcredentials/testing/requests_test.go +++ b/openstack/identity/v3/applicationcredentials/testing/requests_test.go @@ -40,7 +40,7 @@ func TestListApplicationCredentialsAllPages(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedApplicationCredentialsSlice, actual) th.AssertDeepEquals(t, ExpectedApplicationCredentialsSlice[0].Roles, []applicationcredentials.Role{{ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer"}}) - th.AssertDeepEquals(t, ExpectedApplicationCredentialsSlice[1].Roles, []applicationcredentials.Role{applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer"}, applicationcredentials.Role{ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", Name: "network_viewer"}}) + th.AssertDeepEquals(t, ExpectedApplicationCredentialsSlice[1].Roles, []applicationcredentials.Role{{ID: "31f87923ae4a4d119aa0b85dcdbeed13", Name: "compute_viewer"}, {ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4", Name: "network_viewer"}}) } func TestGetApplicationCredential(t *testing.T) { @@ -62,7 +62,7 @@ func TestCreateApplicationCredential(t *testing.T) { Name: "test", Secret: "mysecret", Roles: []applicationcredentials.Role{ - applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, + {ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, }, AccessRules: []applicationcredentials.AccessRule{ { @@ -89,7 +89,7 @@ func TestCreateNoSecretApplicationCredential(t *testing.T) { createOpts := applicationcredentials.CreateOpts{ Name: "test1", Roles: []applicationcredentials.Role{ - applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, + {ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, }, } @@ -107,8 +107,8 @@ func TestCreateUnrestrictedApplicationCredential(t *testing.T) { Name: "test2", Unrestricted: true, Roles: []applicationcredentials.Role{ - applicationcredentials.Role{ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, - applicationcredentials.Role{ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4"}, + {ID: "31f87923ae4a4d119aa0b85dcdbeed13"}, + {ID: "4494bc5bea1a4105ad7fbba6a7eb9ef4"}, }, ExpiresAt: &ApplationCredentialExpiresAt, } diff --git a/openstack/identity/v3/tokens/testing/fixtures.go b/openstack/identity/v3/tokens/testing/fixtures.go index 503e1dd7de..40811fb62b 100644 --- a/openstack/identity/v3/tokens/testing/fixtures.go +++ b/openstack/identity/v3/tokens/testing/fixtures.go @@ -214,21 +214,21 @@ var catalogEntry1 = tokens.CatalogEntry{ Name: "nova", Type: "compute", Endpoints: []tokens.Endpoint{ - tokens.Endpoint{ + { ID: "3eac9e7588eb4eb2a4650cf5e079505f", Region: "RegionOne", RegionID: "RegionOne", Interface: "admin", URL: "http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", }, - tokens.Endpoint{ + { ID: "6b33fabc69c34ea782a3f6282582b59f", Region: "RegionOne", RegionID: "RegionOne", Interface: "internal", URL: "http://127.0.0.1:8774/v2.1/a99e9b4e620e4db09a2dfb6e42a01e66", }, - tokens.Endpoint{ + { ID: "dae63c71bee24070a71f5425e7a916b5", Region: "RegionOne", RegionID: "RegionOne", @@ -242,21 +242,21 @@ var catalogEntry2 = tokens.CatalogEntry{ Name: "keystone", Type: "identity", Endpoints: []tokens.Endpoint{ - tokens.Endpoint{ + { ID: "0539aeff80954a0bb756cec496768d3d", Region: "RegionOne", RegionID: "RegionOne", Interface: "admin", URL: "http://127.0.0.1:35357/v3", }, - tokens.Endpoint{ + { ID: "15bdf2d0853e4c939993d29548b1b56f", Region: "RegionOne", RegionID: "RegionOne", Interface: "public", URL: "http://127.0.0.1:5000/v3", }, - tokens.Endpoint{ + { ID: "3b4423c54ba343c58226bc424cb11c4b", Region: "RegionOne", RegionID: "RegionOne", diff --git a/openstack/loadbalancer/v2/apiversions/testing/fixture.go b/openstack/loadbalancer/v2/apiversions/testing/fixture.go index 4c78c58cbc..a4fcfa536f 100644 --- a/openstack/loadbalancer/v2/apiversions/testing/fixture.go +++ b/openstack/loadbalancer/v2/apiversions/testing/fixture.go @@ -62,19 +62,19 @@ const OctaviaAllAPIVersionsResponse = ` ` var OctaviaAllAPIVersionResults = []apiversions.APIVersion{ - apiversions.APIVersion{ + { ID: "v1", Status: "DEPRECATED", }, - apiversions.APIVersion{ + { ID: "v2.0", Status: "SUPPORTED", }, - apiversions.APIVersion{ + { ID: "v2.1", Status: "SUPPORTED", }, - apiversions.APIVersion{ + { ID: "v2.2", Status: "CURRENT", }, diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 36126701ec..81818e59d7 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -176,7 +176,7 @@ func TestCreate(t *testing.T) { asu := false enableSNAT := false efi := []routers.ExternalFixedIP{ - routers.ExternalFixedIP{ + { SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def", }, } diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go index b242103177..fe5ea98973 100644 --- a/openstack/networking/v2/extensions/provider/testing/results_test.go +++ b/openstack/networking/v2/extensions/provider/testing/results_test.go @@ -168,8 +168,8 @@ func TestCreateWithMultipleProvider(t *testing.T) { iTrue := true segments := []provider.Segment{ - provider.Segment{NetworkType: "vxlan", PhysicalNetwork: "br-ex", SegmentationID: 666}, - provider.Segment{NetworkType: "vxlan", PhysicalNetwork: "br-ex", SegmentationID: 615}, + {NetworkType: "vxlan", PhysicalNetwork: "br-ex", SegmentationID: 666}, + {NetworkType: "vxlan", PhysicalNetwork: "br-ex", SegmentationID: 615}, } networkCreateOpts := networks.CreateOpts{ diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index c611de6629..529540dd25 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -788,7 +788,7 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { func TestPortsListOpts(t *testing.T) { for expected, opts := range map[string]ports.ListOpts{ - newValue("fixed_ips", `ip_address=1.2.3.4,subnet_id=42`): ports.ListOpts{ + newValue("fixed_ips", `ip_address=1.2.3.4,subnet_id=42`): { FixedIPs: []ports.FixedIPOpts{{IPAddress: "1.2.3.4", SubnetID: "42"}}, }, } { diff --git a/openstack/orchestration/v1/apiversions/testing/requests_test.go b/openstack/orchestration/v1/apiversions/testing/requests_test.go index ac59b6c6c2..9705018d80 100644 --- a/openstack/orchestration/v1/apiversions/testing/requests_test.go +++ b/openstack/orchestration/v1/apiversions/testing/requests_test.go @@ -55,7 +55,7 @@ func TestListVersions(t *testing.T) { Status: "CURRENT", ID: "v1.0", Links: []gophercloud.Link{ - gophercloud.Link{ + { Href: "http://23.253.228.211:8000/v1", Rel: "self", }, diff --git a/openstack/orchestration/v1/resourcetypes/testing/fixtures.go b/openstack/orchestration/v1/resourcetypes/testing/fixtures.go index ed1faf1e18..f750ef1b36 100644 --- a/openstack/orchestration/v1/resourcetypes/testing/fixtures.go +++ b/openstack/orchestration/v1/resourcetypes/testing/fixtures.go @@ -20,10 +20,10 @@ const BasicListOutput = ` ` var BasicListExpected = []resourcetypes.ResourceTypeSummary{ - resourcetypes.ResourceTypeSummary{ + { ResourceType: "OS::Nova::Server", }, - resourcetypes.ResourceTypeSummary{ + { ResourceType: "OS::Heat::Stack", }, } @@ -44,11 +44,11 @@ const FullListOutput = ` ` var FullListExpected = []resourcetypes.ResourceTypeSummary{ - resourcetypes.ResourceTypeSummary{ + { ResourceType: "OS::Nova::Server", Description: "A Nova Server", }, - resourcetypes.ResourceTypeSummary{ + { ResourceType: "OS::Heat::Stack", Description: "A Heat Stack", }, @@ -67,7 +67,7 @@ const FilteredListOutput = ` ` var FilteredListExpected = []resourcetypes.ResourceTypeSummary{ - resourcetypes.ResourceTypeSummary{ + { ResourceType: "OS::Heat::Stack", Description: "A Heat Stack", }, @@ -112,65 +112,65 @@ var GetSchemaExpected = resourcetypes.ResourceSchema{ }, }, Attributes: map[string]resourcetypes.AttributeSchema{ - "show": resourcetypes.AttributeSchema{ + "show": { Description: "Detailed information about resource.", Type: resourcetypes.MapProperty, }, - "tags": resourcetypes.AttributeSchema{ + "tags": { Description: "Tags from the server.", Type: resourcetypes.ListProperty, }, - "name": resourcetypes.AttributeSchema{ + "name": { Description: "Name of the server.", Type: resourcetypes.StringProperty, }, }, Properties: map[string]resourcetypes.PropertySchema{ - "name": resourcetypes.PropertySchema{ + "name": { Type: resourcetypes.StringProperty, Description: "Server name.", UpdateAllowed: true, }, - "image": resourcetypes.PropertySchema{ + "image": { Type: resourcetypes.StringProperty, Description: "The ID or name of the image to boot with.", Required: true, Constraints: []resourcetypes.ConstraintSchema{ - resourcetypes.ConstraintSchema{ + { CustomConstraint: &glanceImageConstraint, }, }, }, - "block_device_mapping": resourcetypes.PropertySchema{ + "block_device_mapping": { Type: resourcetypes.ListProperty, Description: "Block device mappings for this server.", Schema: map[string]resourcetypes.PropertySchema{ - "*": resourcetypes.PropertySchema{ + "*": { Type: resourcetypes.MapProperty, Schema: map[string]resourcetypes.PropertySchema{ - "ephemeral_format": resourcetypes.PropertySchema{ + "ephemeral_format": { Type: resourcetypes.StringProperty, Description: "The format of the local ephemeral block device.", Constraints: []resourcetypes.ConstraintSchema{ - resourcetypes.ConstraintSchema{ + { AllowedValues: &[]interface{}{ "ext3", "ext4", "xfs", }, }, }, }, - "ephemeral_size": resourcetypes.PropertySchema{ + "ephemeral_size": { Type: resourcetypes.IntegerProperty, Description: "The size of the local ephemeral block device, in GB.", Constraints: []resourcetypes.ConstraintSchema{ - resourcetypes.ConstraintSchema{ + { Range: &resourcetypes.MinMaxConstraint{ Min: 1, }, }, }, }, - "delete_on_termination": resourcetypes.PropertySchema{ + "delete_on_termination": { Type: resourcetypes.BooleanProperty, Description: "Delete volume on server termination.", Default: true, @@ -180,12 +180,12 @@ var GetSchemaExpected = resourcetypes.ResourceSchema{ }, }, }, - "image_update_policy": resourcetypes.PropertySchema{ + "image_update_policy": { Type: resourcetypes.StringProperty, Description: "Policy on how to apply an image-id update.", Default: "REBUILD", Constraints: []resourcetypes.ConstraintSchema{ - resourcetypes.ConstraintSchema{ + { AllowedValues: &[]interface{}{ "REBUILD", "REPLACE", }, diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures.go index 34256b7693..70d31ec145 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -160,7 +160,7 @@ var ExpectedUsages = resourceproviders.ResourceProviderUsage{ var ExpectedInventories = resourceproviders.ResourceProviderInventories{ ResourceProviderGeneration: 7, Inventories: map[string]resourceproviders.Inventory{ - "DISK_GB": resourceproviders.Inventory{ + "DISK_GB": { AllocationRatio: 1.0, MaxUnit: 35, MinUnit: 1, @@ -168,7 +168,7 @@ var ExpectedInventories = resourceproviders.ResourceProviderInventories{ StepSize: 1, Total: 35, }, - "MEMORY_MB": resourceproviders.Inventory{ + "MEMORY_MB": { AllocationRatio: 1.5, MaxUnit: 5825, MinUnit: 1, @@ -176,7 +176,7 @@ var ExpectedInventories = resourceproviders.ResourceProviderInventories{ StepSize: 1, Total: 5825, }, - "VCPU": resourceproviders.Inventory{ + "VCPU": { AllocationRatio: 16.0, MaxUnit: 4, MinUnit: 1, diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go index eb588e5fd0..e210b4adc9 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go @@ -101,7 +101,7 @@ func TestListDetail(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, []snapshots.Snapshot{ - snapshots.Snapshot{ + { ID: snapshotID, Name: "new_app_snapshot", Description: "", diff --git a/openstack/testing/endpoint_location_test.go b/openstack/testing/endpoint_location_test.go index 8ad36b4378..ce92d3dd06 100644 --- a/openstack/testing/endpoint_location_test.go +++ b/openstack/testing/endpoint_location_test.go @@ -13,45 +13,45 @@ import ( // Service catalog fixtures take too much vertical space! var catalog2 = tokens2.ServiceCatalog{ Entries: []tokens2.CatalogEntry{ - tokens2.CatalogEntry{ + { Type: "same", Name: "same", Endpoints: []tokens2.Endpoint{ - tokens2.Endpoint{ + { Region: "same", PublicURL: "https://public.correct.com/", InternalURL: "https://internal.correct.com/", AdminURL: "https://admin.correct.com/", }, - tokens2.Endpoint{ + { Region: "different", PublicURL: "https://badregion.com/", }, }, }, - tokens2.CatalogEntry{ + { Type: "same", Name: "different", Endpoints: []tokens2.Endpoint{ - tokens2.Endpoint{ + { Region: "same", PublicURL: "https://badname.com/", }, - tokens2.Endpoint{ + { Region: "different", PublicURL: "https://badname.com/+badregion", }, }, }, - tokens2.CatalogEntry{ + { Type: "different", Name: "different", Endpoints: []tokens2.Endpoint{ - tokens2.Endpoint{ + { Region: "same", PublicURL: "https://badtype.com/+badname", }, - tokens2.Endpoint{ + { Region: "different", PublicURL: "https://badtype.com/+badregion+badname", }, @@ -111,29 +111,29 @@ func TestV2EndpointBadAvailability(t *testing.T) { var catalog3 = tokens3.ServiceCatalog{ Entries: []tokens3.CatalogEntry{ - tokens3.CatalogEntry{ + { Type: "same", Name: "same", Endpoints: []tokens3.Endpoint{ - tokens3.Endpoint{ + { ID: "1", Region: "same", Interface: "public", URL: "https://public.correct.com/", }, - tokens3.Endpoint{ + { ID: "2", Region: "same", Interface: "admin", URL: "https://admin.correct.com/", }, - tokens3.Endpoint{ + { ID: "3", Region: "same", Interface: "internal", URL: "https://internal.correct.com/", }, - tokens3.Endpoint{ + { ID: "4", Region: "different", Interface: "public", @@ -141,17 +141,17 @@ var catalog3 = tokens3.ServiceCatalog{ }, }, }, - tokens3.CatalogEntry{ + { Type: "same", Name: "different", Endpoints: []tokens3.Endpoint{ - tokens3.Endpoint{ + { ID: "5", Region: "same", Interface: "public", URL: "https://badname.com/", }, - tokens3.Endpoint{ + { ID: "6", Region: "different", Interface: "public", @@ -159,17 +159,17 @@ var catalog3 = tokens3.ServiceCatalog{ }, }, }, - tokens3.CatalogEntry{ + { Type: "different", Name: "different", Endpoints: []tokens3.Endpoint{ - tokens3.Endpoint{ + { ID: "7", Region: "same", Interface: "public", URL: "https://badtype.com/+badname", }, - tokens3.Endpoint{ + { ID: "8", Region: "different", Interface: "public", @@ -177,23 +177,23 @@ var catalog3 = tokens3.ServiceCatalog{ }, }, }, - tokens3.CatalogEntry{ + { Type: "someother", Name: "someother", Endpoints: []tokens3.Endpoint{ - tokens3.Endpoint{ + { ID: "1", Region: "someother", Interface: "public", URL: "https://public.correct.com/", }, - tokens3.Endpoint{ + { ID: "2", RegionID: "someother", Interface: "admin", URL: "https://admin.correct.com/", }, - tokens3.Endpoint{ + { ID: "3", RegionID: "someother", Interface: "internal", diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index ef911406eb..cf314e5681 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -209,7 +209,7 @@ func TestListCronTriggers(t *testing.T) { firstExecution := time.Date(2018, time.September, 12, 17, 48, 0, 0, time.UTC) expected := []crontriggers.CronTrigger{ - crontriggers.CronTrigger{ + { ID: "0520ffd8-f7f1-4f2e-845b-55d953a1cf46", Name: "crontrigger", Pattern: "0 0 1 1 *", @@ -245,24 +245,24 @@ func TestListCronTriggers(t *testing.T) { func TestToExecutionListQuery(t *testing.T) { for expected, opts := range map[string]*crontriggers.ListOpts{ - newValue("workflow_input", `{"msg":"Hello"}`): &crontriggers.ListOpts{ + newValue("workflow_input", `{"msg":"Hello"}`): { WorkflowInput: map[string]interface{}{ "msg": "Hello", }, }, - newValue("name", `neq:not_name`): &crontriggers.ListOpts{ + newValue("name", `neq:not_name`): { Name: &crontriggers.ListFilter{ Filter: crontriggers.FilterNEQ, Value: "not_name", }, }, - newValue("workflow_name", `eq:workflow`): &crontriggers.ListOpts{ + newValue("workflow_name", `eq:workflow`): { WorkflowName: &crontriggers.ListFilter{ Filter: crontriggers.FilterEQ, Value: "workflow", }, }, - newValue("created_at", `gt:2018-01-01 00:00:00`): &crontriggers.ListOpts{ + newValue("created_at", `gt:2018-01-01 00:00:00`): { CreatedAt: &crontriggers.ListDateFilter{ Filter: crontriggers.FilterGT, Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index dfe278294a..5eae95904e 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -201,7 +201,7 @@ func TestListExecutions(t *testing.T) { } expected := []executions.Execution{ - executions.Execution{ + { ID: "50bb59f1-eb77-4017-a77f-6d575b002667", Description: "description", Input: map[string]interface{}{ @@ -235,18 +235,18 @@ func TestListExecutions(t *testing.T) { func TestToExecutionListQuery(t *testing.T) { for expected, opts := range map[string]*executions.ListOpts{ - newValue("input", `{"msg":"Hello"}`): &executions.ListOpts{ + newValue("input", `{"msg":"Hello"}`): { Input: map[string]interface{}{ "msg": "Hello", }, }, - newValue("description", `neq:not_description`): &executions.ListOpts{ + newValue("description", `neq:not_description`): { Description: &executions.ListFilter{ Filter: executions.FilterNEQ, Value: "not_description", }, }, - newValue("created_at", `gt:2018-01-01 00:00:00`): &executions.ListOpts{ + newValue("created_at", `gt:2018-01-01 00:00:00`): { CreatedAt: &executions.ListDateFilter{ Filter: executions.FilterGT, Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index f0c2755744..dfc6857c42 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -76,7 +76,7 @@ workflow_echo: updated := time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC) expected := []workflows.Workflow{ - workflows.Workflow{ + { ID: "604a3a1e-94e3-4066-a34a-aa56873ef236", Definition: "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", Name: "workflow_echo", @@ -200,7 +200,7 @@ func TestListWorkflows(t *testing.T) { updated := time.Date(2018, time.September, 12, 15, 48, 17, 0, time.UTC) expected := []workflows.Workflow{ - workflows.Workflow{ + { ID: "604a3a1e-94e3-4066-a34a-aa56873ef236", Definition: "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", Name: "workflow_echo", @@ -229,16 +229,16 @@ func TestListWorkflows(t *testing.T) { func TestToWorkflowListQuery(t *testing.T) { for expected, opts := range map[string]*workflows.ListOpts{ - newValue("tags", `tag1,tag2`): &workflows.ListOpts{ + newValue("tags", `tag1,tag2`): { Tags: []string{"tag1", "tag2"}, }, - newValue("name", `neq:invalid_name`): &workflows.ListOpts{ + newValue("name", `neq:invalid_name`): { Name: &workflows.ListFilter{ Filter: workflows.FilterNEQ, Value: "invalid_name", }, }, - newValue("created_at", `gt:2018-01-01 00:00:00`): &workflows.ListOpts{ + newValue("created_at", `gt:2018-01-01 00:00:00`): { CreatedAt: &workflows.ListDateFilter{ Filter: workflows.FilterGT, Value: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC), diff --git a/pagination/linked.go b/pagination/linked.go index 3656fb7f8f..a664e05673 100644 --- a/pagination/linked.go +++ b/pagination/linked.go @@ -40,7 +40,7 @@ func (current LinkedPageBase) NextPageURL() (string, error) { } for { - key, path = path[0], path[1:len(path)] + key, path = path[0], path[1:] value, ok := submap[key] if !ok { From 01e5088438cb957f5ae44ba0286505a67f9674d7 Mon Sep 17 00:00:00 2001 From: clementblaise <32685572+clementblaise@users.noreply.github.com> Date: Thu, 17 Dec 2020 20:09:30 +0100 Subject: [PATCH 1176/2296] add AllProjects LitsOps on ServerGroups (#2070) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add AllProjects LitsOps on ServerGroups * update servergroups TestList params * update servergroups List params on acceptance * update servergroups List params on acceptance Co-authored-by: Clément Blaise --- .../openstack/compute/v2/servergroup_test.go | 4 +-- .../v2/extensions/servergroups/requests.go | 28 +++++++++++++++++-- .../servergroups/testing/requests_test.go | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/compute/v2/servergroup_test.go b/acceptance/openstack/compute/v2/servergroup_test.go index 0429ff9f5e..f8fe03d20e 100644 --- a/acceptance/openstack/compute/v2/servergroup_test.go +++ b/acceptance/openstack/compute/v2/servergroup_test.go @@ -25,7 +25,7 @@ func TestServergroupsCreateDelete(t *testing.T) { tools.PrintResource(t, serverGroup) - allPages, err := servergroups.List(client).AllPages() + allPages, err := servergroups.List(client, &servergroups.ListOpts{}).AllPages() th.AssertNoErr(t, err) allServerGroups, err := servergroups.ExtractServerGroups(allPages) @@ -90,7 +90,7 @@ func TestServergroupsMicroversionCreateDelete(t *testing.T) { tools.PrintResource(t, serverGroup) - allPages, err := servergroups.List(client).AllPages() + allPages, err := servergroups.List(client, &servergroups.ListOpts{}).AllPages() th.AssertNoErr(t, err) allServerGroups, err := servergroups.ExtractServerGroups(allPages) diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go index 7a877718d9..8e7966d567 100644 --- a/openstack/compute/v2/extensions/servergroups/requests.go +++ b/openstack/compute/v2/extensions/servergroups/requests.go @@ -5,10 +5,34 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +type ListOptsBuilder interface { + ToServerListQuery() (string, error) +} + +type ListOpts struct { + // AllProjects is a bool to show all projects. + AllProjects bool `q:"all_projects"` +} + +// ToServerListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToServerListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // List returns a Pager that allows you to iterate over a collection of // ServerGroups. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { +func List(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 ServerGroupPage{pagination.SinglePageBase(r)} }) } diff --git a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go index cada74fa00..e5b7e667cc 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go +++ b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go @@ -15,7 +15,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := servergroups.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := servergroups.List(client.ServiceClient(), &servergroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := servergroups.ExtractServerGroups(page) th.AssertNoErr(t, err) From 6375e7815bfc0c7a24c3b97008b97d37c64228dc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 17 Dec 2020 12:11:54 -0700 Subject: [PATCH 1177/2296] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a9581b46d..733f25c3f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## 0.15.0 (Unreleased) +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) @@ -19,6 +25,7 @@ * 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) ## 0.14.0 (November 11, 2020) From cd654f9a7d6b1ef249009de1892abe285169cdd7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 17 Dec 2020 23:54:48 +0000 Subject: [PATCH 1178/2296] Removing Travis in favour of GitHub Actions With Travis support soon coming to an end, this commit moves unit testing to GitHub Actions. --- .github/workflows/unit.yml | 43 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 25 ---------------------- 2 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/unit.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml new file mode 100644 index 0000000000..cdc0b707fe --- /dev/null +++ b/.github/workflows/unit.yml @@ -0,0 +1,43 @@ +on: [push, pull_request] +name: Unit Testing +jobs: + test: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + go-version: + - "1.15" + + env: + GO111MODULE: "on" + + steps: + - name: Setup Go ${{ matrix.go-version }} + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + + - uses: actions/checkout@v2 + + - name: Setup environment + run: | + GO111MODULE=off go get golang.org/x/crypto/ssh + GO111MODULE=off go get -v -tags 'fixtures acceptance' ./... + GO111MODULE=off go get github.com/wadey/gocovmerge + GO111MODULE=off go get github.com/mattn/goveralls + GO111MODULE=off go get golang.org/x/tools/cmd/goimports + + - name: Run go vet + run: | + go vet ./... + + - name: Run unit tests + run: | + ./script/coverage + ./script/unittest + ./script/format + + - name: Run Goveralls for Coveralls + run: | + $HOME/gopath/bin/goveralls -service=github -coverprofile=cover.out diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a6eb99c4a9..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -language: go -sudo: false -install: -- GO111MODULE=off go get golang.org/x/crypto/ssh -- GO111MODULE=off go get -v -tags 'fixtures acceptance' ./... -- GO111MODULE=off go get github.com/wadey/gocovmerge -- GO111MODULE=off go get github.com/mattn/goveralls -- GO111MODULE=off go get golang.org/x/tools/cmd/goimports -go: -- "1.13" -- "1.14" -- "1.15" -- "tip" -env: - global: - - secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ=" - - GO111MODULE=on -before_script: -- go vet ./... -script: -- ./script/coverage -- ./script/unittest -- ./script/format -after_success: -- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out From fa4789cbfd59ec540053320948154632240dafac Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 18 Dec 2020 16:59:27 +0000 Subject: [PATCH 1179/2296] Remove internal package This commit removes the "internal" package. Golang now raises errors when a package imports another package called "internal". --- internal/pkg.go | 1 - internal/testing/pkg.go | 1 - internal/testing/util_test.go | 42 ------- internal/util.go | 34 ------ openstack/identity/v3/groups/results.go | 3 +- openstack/identity/v3/policies/results.go | 3 +- openstack/identity/v3/projects/results.go | 3 +- openstack/identity/v3/regions/results.go | 3 +- openstack/identity/v3/roles/results.go | 3 +- openstack/identity/v3/services/results.go | 3 +- openstack/identity/v3/users/results.go | 3 +- openstack/imageservice/v2/images/results.go | 3 +- openstack/messaging/v2/queues/results.go | 3 +- testing/util_test.go | 35 ++++++ util.go | 122 ++++++++++++-------- 15 files changed, 119 insertions(+), 143 deletions(-) delete mode 100644 internal/pkg.go delete mode 100644 internal/testing/pkg.go delete mode 100644 internal/testing/util_test.go delete mode 100644 internal/util.go diff --git a/internal/pkg.go b/internal/pkg.go deleted file mode 100644 index 5bf0569ce8..0000000000 --- a/internal/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package internal diff --git a/internal/testing/pkg.go b/internal/testing/pkg.go deleted file mode 100644 index 7603f836a0..0000000000 --- a/internal/testing/pkg.go +++ /dev/null @@ -1 +0,0 @@ -package testing diff --git a/internal/testing/util_test.go b/internal/testing/util_test.go deleted file mode 100644 index 12fd172b60..0000000000 --- a/internal/testing/util_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package testing - -import ( - "reflect" - "testing" - - "github.com/gophercloud/gophercloud/internal" -) - -func TestRemainingKeys(t *testing.T) { - type User struct { - UserID string `json:"user_id"` - Username string `json:"username"` - Location string `json:"-"` - CreatedAt string `json:"-"` - Status string - IsAdmin bool - } - - userResponse := map[string]interface{}{ - "user_id": "abcd1234", - "username": "jdoe", - "location": "Hawaii", - "created_at": "2017-06-08T02:49:03.000000", - "status": "active", - "is_admin": "true", - "custom_field": "foo", - } - - expected := map[string]interface{}{ - "created_at": "2017-06-08T02:49:03.000000", - "is_admin": "true", - "custom_field": "foo", - } - - actual := internal.RemainingKeys(User{}, userResponse) - - isEqual := reflect.DeepEqual(expected, actual) - if !isEqual { - t.Fatalf("expected %s but got %s", expected, actual) - } -} diff --git a/internal/util.go b/internal/util.go deleted file mode 100644 index 8efb283e72..0000000000 --- a/internal/util.go +++ /dev/null @@ -1,34 +0,0 @@ -package internal - -import ( - "reflect" - "strings" -) - -// RemainingKeys will inspect a struct and compare it to a map. Any struct -// field that does not have a JSON tag that matches a key in the map or -// a matching lower-case field in the map will be returned as an extra. -// -// This is useful for determining the extra fields returned in response bodies -// for resources that can contain an arbitrary or dynamic number of fields. -func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]interface{}) { - extras = make(map[string]interface{}) - for k, v := range m { - extras[k] = v - } - - valueOf := reflect.ValueOf(s) - typeOf := reflect.TypeOf(s) - for i := 0; i < valueOf.NumField(); i++ { - field := typeOf.Field(i) - - lowerField := strings.ToLower(field.Name) - delete(extras, lowerField) - - if tagValue := field.Tag.Get("json"); tagValue != "" && tagValue != "-" { - delete(extras, tagValue) - } - } - - return -} diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go index ba7d018d17..f4b0b1a0e7 100644 --- a/openstack/identity/v3/groups/results.go +++ b/openstack/identity/v3/groups/results.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -52,7 +51,7 @@ func (r *Group) UnmarshalJSON(b []byte) error { return err } if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = internal.RemainingKeys(Group{}, resultMap) + r.Extra = gophercloud.RemainingKeys(Group{}, resultMap) } } diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go index 131a9f8103..6ecf6205bb 100644 --- a/openstack/identity/v3/policies/results.go +++ b/openstack/identity/v3/policies/results.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -50,7 +49,7 @@ func (r *Policy) UnmarshalJSON(b []byte) error { return err } if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = internal.RemainingKeys(Policy{}, resultMap) + r.Extra = gophercloud.RemainingKeys(Policy{}, resultMap) } } diff --git a/openstack/identity/v3/projects/results.go b/openstack/identity/v3/projects/results.go index a601809101..c2fe821939 100644 --- a/openstack/identity/v3/projects/results.go +++ b/openstack/identity/v3/projects/results.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -100,7 +99,7 @@ func (r *Project) UnmarshalJSON(b []byte) error { return err } if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = internal.RemainingKeys(Project{}, resultMap) + r.Extra = gophercloud.RemainingKeys(Project{}, resultMap) } } diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go index a60cb34883..6d9050f886 100644 --- a/openstack/identity/v3/regions/results.go +++ b/openstack/identity/v3/regions/results.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -49,7 +48,7 @@ func (r *Region) UnmarshalJSON(b []byte) error { return err } if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = internal.RemainingKeys(Region{}, resultMap) + r.Extra = gophercloud.RemainingKeys(Region{}, resultMap) } } diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index af8fd9e6a7..631c992a3c 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -49,7 +48,7 @@ func (r *Role) UnmarshalJSON(b []byte) error { return err } if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = internal.RemainingKeys(Role{}, resultMap) + r.Extra = gophercloud.RemainingKeys(Role{}, resultMap) } } diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index f3484d7a96..5ac580ba4b 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -87,7 +86,7 @@ func (r *Service) UnmarshalJSON(b []byte) error { return err } if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = internal.RemainingKeys(Service{}, resultMap) + r.Extra = gophercloud.RemainingKeys(Service{}, resultMap) } } diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index e158ac723b..54a969d5ac 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -5,7 +5,6 @@ import ( "time" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -69,7 +68,7 @@ func (r *User) UnmarshalJSON(b []byte) error { } if resultMap, ok := result.(map[string]interface{}); ok { delete(resultMap, "password_expires_at") - r.Extra = internal.RemainingKeys(User{}, resultMap) + r.Extra = gophercloud.RemainingKeys(User{}, resultMap) } } diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index f445cc38fc..9bab7f94e7 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -8,7 +8,6 @@ import ( "time" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -132,7 +131,7 @@ func (r *Image) UnmarshalJSON(b []byte) error { delete(resultMap, "size") delete(resultMap, "openstack-image-import-methods") delete(resultMap, "openstack-image-store-ids") - r.Properties = internal.RemainingKeys(Image{}, resultMap) + r.Properties = gophercloud.RemainingKeys(Image{}, resultMap) } if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageImportMethods), splitFunc); len(v) > 0 { diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index d1f24c086a..92d2fc67c1 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -4,7 +4,6 @@ import ( "encoding/json" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal" "github.com/gophercloud/gophercloud/pagination" ) @@ -208,7 +207,7 @@ func (r *QueueDetails) UnmarshalJSON(b []byte) error { return err } if resultMap, ok := result.(map[string]interface{}); ok { - r.Extra = internal.RemainingKeys(QueueDetails{}, resultMap) + r.Extra = gophercloud.RemainingKeys(QueueDetails{}, resultMap) } } diff --git a/testing/util_test.go b/testing/util_test.go index ae3e448fa9..b0430dffda 100644 --- a/testing/util_test.go +++ b/testing/util_test.go @@ -4,6 +4,7 @@ import ( "errors" "os" "path/filepath" + "reflect" "strings" "testing" "time" @@ -120,3 +121,37 @@ func TestNormalizePathURL(t *testing.T) { th.CheckEquals(t, expected, result) } + +func TestRemainingKeys(t *testing.T) { + type User struct { + UserID string `json:"user_id"` + Username string `json:"username"` + Location string `json:"-"` + CreatedAt string `json:"-"` + Status string + IsAdmin bool + } + + userResponse := map[string]interface{}{ + "user_id": "abcd1234", + "username": "jdoe", + "location": "Hawaii", + "created_at": "2017-06-08T02:49:03.000000", + "status": "active", + "is_admin": "true", + "custom_field": "foo", + } + + expected := map[string]interface{}{ + "created_at": "2017-06-08T02:49:03.000000", + "is_admin": "true", + "custom_field": "foo", + } + + actual := gophercloud.RemainingKeys(User{}, userResponse) + + isEqual := reflect.DeepEqual(expected, actual) + if !isEqual { + t.Fatalf("expected %s but got %s", expected, actual) + } +} diff --git a/util.go b/util.go index 68f9a5d3ec..2740c301e5 100644 --- a/util.go +++ b/util.go @@ -4,10 +4,85 @@ import ( "fmt" "net/url" "path/filepath" + "reflect" "strings" "time" ) +// NormalizePathURL is used to convert rawPath to a fqdn, using basePath as +// a reference in the filesystem, if necessary. basePath is assumed to contain +// either '.' when first used, or the file:// type fqdn of the parent resource. +// e.g. myFavScript.yaml => file://opt/lib/myFavScript.yaml +func NormalizePathURL(basePath, rawPath string) (string, error) { + u, err := url.Parse(rawPath) + if err != nil { + return "", err + } + // if a scheme is defined, it must be a fqdn already + if u.Scheme != "" { + return u.String(), nil + } + // if basePath is a url, then child resources are assumed to be relative to it + bu, err := url.Parse(basePath) + if err != nil { + return "", err + } + var basePathSys, absPathSys string + if bu.Scheme != "" { + basePathSys = filepath.FromSlash(bu.Path) + absPathSys = filepath.Join(basePathSys, rawPath) + bu.Path = filepath.ToSlash(absPathSys) + return bu.String(), nil + } + + absPathSys = filepath.Join(basePath, rawPath) + u.Path = filepath.ToSlash(absPathSys) + if err != nil { + return "", err + } + u.Scheme = "file" + return u.String(), nil +} + +// NormalizeURL is an internal function to be used by provider clients. +// +// It ensures that each endpoint URL has a closing `/`, as expected by +// ServiceClient's methods. +func NormalizeURL(url string) string { + if !strings.HasSuffix(url, "/") { + return url + "/" + } + return url +} + +// RemainingKeys will inspect a struct and compare it to a map. Any struct +// field that does not have a JSON tag that matches a key in the map or +// a matching lower-case field in the map will be returned as an extra. +// +// This is useful for determining the extra fields returned in response bodies +// for resources that can contain an arbitrary or dynamic number of fields. +func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]interface{}) { + extras = make(map[string]interface{}) + for k, v := range m { + extras[k] = v + } + + valueOf := reflect.ValueOf(s) + typeOf := reflect.TypeOf(s) + for i := 0; i < valueOf.NumField(); i++ { + field := typeOf.Field(i) + + lowerField := strings.ToLower(field.Name) + delete(extras, lowerField) + + if tagValue := field.Tag.Get("json"); tagValue != "" && tagValue != "-" { + delete(extras, tagValue) + } + } + + return +} + // WaitFor polls a predicate function, once per second, up to a timeout limit. // This is useful to wait for a resource to transition to a certain state. // To handle situations when the predicate might hang indefinitely, the @@ -53,50 +128,3 @@ func WaitFor(timeout int, predicate func() (bool, error)) error { } } } - -// NormalizeURL is an internal function to be used by provider clients. -// -// It ensures that each endpoint URL has a closing `/`, as expected by -// ServiceClient's methods. -func NormalizeURL(url string) string { - if !strings.HasSuffix(url, "/") { - return url + "/" - } - return url -} - -// NormalizePathURL is used to convert rawPath to a fqdn, using basePath as -// a reference in the filesystem, if necessary. basePath is assumed to contain -// either '.' when first used, or the file:// type fqdn of the parent resource. -// e.g. myFavScript.yaml => file://opt/lib/myFavScript.yaml -func NormalizePathURL(basePath, rawPath string) (string, error) { - u, err := url.Parse(rawPath) - if err != nil { - return "", err - } - // if a scheme is defined, it must be a fqdn already - if u.Scheme != "" { - return u.String(), nil - } - // if basePath is a url, then child resources are assumed to be relative to it - bu, err := url.Parse(basePath) - if err != nil { - return "", err - } - var basePathSys, absPathSys string - if bu.Scheme != "" { - basePathSys = filepath.FromSlash(bu.Path) - absPathSys = filepath.Join(basePathSys, rawPath) - bu.Path = filepath.ToSlash(absPathSys) - return bu.String(), nil - } - - absPathSys = filepath.Join(basePath, rawPath) - u.Path = filepath.ToSlash(absPathSys) - if err != nil { - return "", err - } - u.Scheme = "file" - return u.String(), nil - -} From 6e41dcba540d91fa413fb6e08512567a0e3d9c8a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 18 Dec 2020 18:31:05 +0000 Subject: [PATCH 1180/2296] Fixing goveralls/coveralls --- .github/workflows/unit.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index cdc0b707fe..ee6e18aac8 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -38,6 +38,6 @@ jobs: ./script/unittest ./script/format - - name: Run Goveralls for Coveralls - run: | - $HOME/gopath/bin/goveralls -service=github -coverprofile=cover.out + - uses: shogo82148/actions-goveralls@v1 + with: + path-to-profile: cover.out From 7e3109d5c143f1fb3fd2cfcef11db4a7e2b7fb5b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 18 Dec 2020 17:22:25 -0700 Subject: [PATCH 1181/2296] Changing how testing environment is set up (#2080) --- .github/workflows/unit.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index ee6e18aac8..f61ff79d30 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -22,11 +22,10 @@ jobs: - name: Setup environment run: | - GO111MODULE=off go get golang.org/x/crypto/ssh - GO111MODULE=off go get -v -tags 'fixtures acceptance' ./... - GO111MODULE=off go get github.com/wadey/gocovmerge - GO111MODULE=off go get github.com/mattn/goveralls - GO111MODULE=off go get golang.org/x/tools/cmd/goimports + 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 run: | From 338313306ac8582bff9a1da5b009bb5622f9a52e Mon Sep 17 00:00:00 2001 From: Simon Merrick Date: Mon, 21 Dec 2020 16:23:32 +1300 Subject: [PATCH 1182/2296] Support storage policy on container create (#2075) --- openstack/objectstorage/v1/containers/requests.go | 1 + openstack/objectstorage/v1/containers/testing/fixtures.go | 1 + 2 files changed, 2 insertions(+) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index b8f169345d..119e06997f 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -80,6 +80,7 @@ type CreateOpts 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"` + StoragePolicy string `h:"X-Storage-Policy"` } // ToContainerCreateMap formats a CreateOpts into a map of headers. diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 5fd0218f79..f062a9a9d6 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -107,6 +107,7 @@ func HandleCreateContainerSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "text/html; charset=UTF-8") w.Header().Set("Date", "Wed, 17 Aug 2016 19:25:43 UTC") w.Header().Set("X-Trans-Id", "tx554ed59667a64c61866f1-0058b4ba37") + w.Header().Set("X-Storage-Policy", "multi-region-three-replicas") w.WriteHeader(http.StatusNoContent) }) } From 766a46ff1d6eebab9e763f033f98cb5af93d8b24 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 20 Dec 2020 20:24:18 -0700 Subject: [PATCH 1183/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 733f25c3f6..a8770f25ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ IMPROVEMENTS * 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) ## 0.14.0 (November 11, 2020) From b9ecd5a022841e089aad443ae8d668f0ae8d25ee Mon Sep 17 00:00:00 2001 From: Stepan Date: Mon, 21 Dec 2020 22:03:45 +0200 Subject: [PATCH 1184/2296] Block storage: adding support of update snapshot api call (#2081) * adding all funcs for update snapshot API call; added missing comments; changed receiver name from page to r in the NextPageURL func to be consistent over all funcs * adding unit test for update snapshot; added comments for all fixtures; updated comment in docs for tests * adding acceptance test for update snapshot * adding small log to indicate attempt to update snapshot * use randomly generated names instead of hard-coded in update snapshot acceptance test --- .../blockstorage/v3/snapshots_test.go | 16 +++++ openstack/blockstorage/v3/snapshots/doc.go | 65 +++++++++++++++++-- .../blockstorage/v3/snapshots/requests.go | 37 +++++++++++ .../blockstorage/v3/snapshots/results.go | 11 +++- .../blockstorage/v3/snapshots/testing/doc.go | 2 +- .../v3/snapshots/testing/fixtures.go | 30 +++++++++ .../v3/snapshots/testing/requests_test.go | 15 +++++ openstack/blockstorage/v3/snapshots/urls.go | 4 ++ 8 files changed, 173 insertions(+), 7 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/snapshots_test.go b/acceptance/openstack/blockstorage/v3/snapshots_test.go index 1e45c9e96c..a7b52c44a2 100644 --- a/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -26,6 +27,21 @@ func TestSnapshots(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSnapshot(t, client, snapshot1) + // Update snapshot + updatedSnapshotName := tools.RandomString("ACPTTEST", 16) + updatedSnapshotDescription := tools.RandomString("ACPTTEST", 16) + updateOpts := snapshots.UpdateOpts{ + Name: &updatedSnapshotName, + Description: &updatedSnapshotDescription, + } + t.Logf("Attempting to update snapshot: %s", updatedSnapshotName) + updatedSnapshot, err := snapshots.Update(client, snapshot1.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedSnapshot) + th.AssertEquals(t, updatedSnapshot.Name, updatedSnapshotName) + th.AssertEquals(t, updatedSnapshot.Description, updatedSnapshotDescription) + volume2, err := CreateVolume(t, client) th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume2) diff --git a/openstack/blockstorage/v3/snapshots/doc.go b/openstack/blockstorage/v3/snapshots/doc.go index 198f83077c..76960f4218 100644 --- a/openstack/blockstorage/v3/snapshots/doc.go +++ b/openstack/blockstorage/v3/snapshots/doc.go @@ -1,5 +1,62 @@ -// Package snapshots provides information and interaction with snapshots in the -// OpenStack Block Storage service. A snapshot is a point in time copy of the -// data contained in an external storage volume, and can be controlled -// programmatically. +/* +Package snapshots provides information and interaction with snapshots in the +OpenStack Block Storage service. A snapshot is a point in time copy of the +data contained in an external storage volume, and can be controlled +programmatically. + + +Example to list Snapshots + + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + if err != nil{ + panic(err) + } + snapshots, err := snapshots.ExtractSnapshots(allPages) + if err != nil{ + panic(err) + } + for _,s := range snapshots{ + fmt.Println(s) + } + +Example to get a Snapshot + + snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" + snapshot, err := snapshots.Get(client, snapshotID).Extract() + if err != nil{ + panic(err) + } + fmt.Println(snapshot) + +Example to create a Snapshot + + snapshot, err := snapshots.Create(client, snapshots.CreateOpts{ + Name:"snapshot_001", + VolumeID:"5aa119a8-d25b-45a7-8d1b-88e127885635", + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(snapshot) + +Example to delete a Snapshot + + snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" + err := snapshots.Delete(client, snapshotID).ExtractErr() + if err != nil{ + panic(err) + } + +Example to update a Snapshot + + snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" + snapshot, err = snapshots.Update(client, snapshotID, snapshots.UpdateOpts{ + Name: "snapshot_002", + Description:"description_002", + }).Extract() + if err != nil{ + panic(err) + } + fmt.Println(snapshot) +*/ package snapshots diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 6f5a24341f..7dcfed7946 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -65,6 +65,8 @@ type ListOptsBuilder interface { ToSnapshotListQuery() (string, error) } +// ListOpts holds options for listing Snapshots. It is passed to the snapshots.List +// function. type ListOpts struct { // AllTenants will retrieve snapshots of all tenants/projects. AllTenants bool `q:"all_tenants"` @@ -118,6 +120,41 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToSnapshotUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Snapshot. This object is passed +// to the snapshots.Update function. For more information about the parameters, see +// the Snapshot object. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` +} + +// ToSnapshotUpdateMap assembles a request body based on the contents of an +// UpdateOpts. +func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "snapshot") +} + +// Update will update the Snapshot with provided information. To extract the updated +// Snapshot from the response, call the Extract method on the UpdateResult. +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToSnapshotUpdateMap() + 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 +} + // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMetadataOptsBuilder interface { diff --git a/openstack/blockstorage/v3/snapshots/results.go b/openstack/blockstorage/v3/snapshots/results.go index 1ffae8f61f..3c81a447a6 100644 --- a/openstack/blockstorage/v3/snapshots/results.go +++ b/openstack/blockstorage/v3/snapshots/results.go @@ -53,6 +53,11 @@ type DeleteResult struct { gophercloud.ErrResult } +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + // SnapshotPage is a pagination.Pager that is returned from a call to the List function. type SnapshotPage struct { pagination.LinkedPageBase @@ -84,11 +89,13 @@ func (r SnapshotPage) IsEmpty() (bool, error) { return len(volumes) == 0, err } -func (page SnapshotPage) NextPageURL() (string, error) { +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (r SnapshotPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"snapshots_links"` } - err := page.ExtractInto(&s) + err := r.ExtractInto(&s) if err != nil { return "", err } diff --git a/openstack/blockstorage/v3/snapshots/testing/doc.go b/openstack/blockstorage/v3/snapshots/testing/doc.go index 3de6274b32..89a5d0d3e2 100644 --- a/openstack/blockstorage/v3/snapshots/testing/doc.go +++ b/openstack/blockstorage/v3/snapshots/testing/doc.go @@ -1,2 +1,2 @@ -// snapshots_v3 +// Package testing for snapshots_v3 package testing diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures.go b/openstack/blockstorage/v3/snapshots/testing/fixtures.go index 8255207cc2..3ae3bb86d2 100644 --- a/openstack/blockstorage/v3/snapshots/testing/fixtures.go +++ b/openstack/blockstorage/v3/snapshots/testing/fixtures.go @@ -9,6 +9,7 @@ import ( fake "github.com/gophercloud/gophercloud/testhelper/client" ) +// MockListResponse provides mock responce 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") @@ -58,6 +59,7 @@ func MockListResponse(t *testing.T) { }) } +// MockGetResponse provides mock responce 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") @@ -81,6 +83,7 @@ func MockGetResponse(t *testing.T) { }) } +// MockCreateResponse provides mock responce 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") @@ -116,6 +119,7 @@ func MockCreateResponse(t *testing.T) { }) } +// MockUpdateMetadataResponse provides mock responce 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") @@ -139,6 +143,7 @@ func MockUpdateMetadataResponse(t *testing.T) { }) } +// MockDeleteResponse provides mock responce 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") @@ -146,3 +151,28 @@ func MockDeleteResponse(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +// MockUpdateResponse provides mock responce 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") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` +{ + "snapshot": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "snapshot-002", + "description": "Daily backup 002", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000", + "updated_at": "2017-05-30T03:35:03.000000" + } +} + `) + }) +} diff --git a/openstack/blockstorage/v3/snapshots/testing/requests_test.go b/openstack/blockstorage/v3/snapshots/testing/requests_test.go index 51231e3017..a787928ba9 100644 --- a/openstack/blockstorage/v3/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/requests_test.go @@ -114,3 +114,18 @@ func TestDelete(t *testing.T) { res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } + +func TestUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUpdateResponse(t) + + var name = "snapshot-002" + var description = "Daily backup 002" + options := snapshots.UpdateOpts{Name: &name, Description: &description} + v, err := snapshots.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + th.CheckEquals(t, "snapshot-002", v.Name) + th.CheckEquals(t, "Daily backup 002", v.Description) +} diff --git a/openstack/blockstorage/v3/snapshots/urls.go b/openstack/blockstorage/v3/snapshots/urls.go index 7780437493..d0bcbfb98c 100644 --- a/openstack/blockstorage/v3/snapshots/urls.go +++ b/openstack/blockstorage/v3/snapshots/urls.go @@ -18,6 +18,10 @@ func listURL(c *gophercloud.ServiceClient) string { return createURL(c) } +func updateURL(c *gophercloud.ServiceClient, id string) string { + return deleteURL(c, id) +} + func metadataURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("snapshots", id, "metadata") } From 65865e08042d12fe0f012c1406dd340cfdd6904c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 21 Dec 2020 13:04:36 -0700 Subject: [PATCH 1185/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8770f25ee..636f149ec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ IMPROVEMENTS * 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) ## 0.14.0 (November 11, 2020) From 62d0f4803becf9ac55c1c697529c418f26a0f750 Mon Sep 17 00:00:00 2001 From: Andrew Karpow Date: Sun, 27 Dec 2020 21:26:34 +0100 Subject: [PATCH 1186/2296] loadbalancer/v2 Support fully populated load balancer (#2077) * Creating a Fully Populated Load Balancer * Added unit and acceptance tests for fully populated load balancer For #1740 Introduces fully populated load balancer support Co-authored-by: Martin Weindel Co-authored-by: Anton Khramov * fully-populated lb: Remove unsupported UpdateOpts params, Added doc and constraints * fully-populated lb: Added documentation example and corrected indentation for unittest fixture * fully-populated lb: remove xor constraints for pools.CreateOpts Constraint (Members OR Monitors) NAND (ListenerID XOR LoadbalancerID) is too complicated to be modelled via struct tags Co-authored-by: Martin Weindel Co-authored-by: Anton Khramov --- .../openstack/loadbalancer/v2/loadbalancer.go | 113 ++++++++ .../loadbalancer/v2/loadbalancers_test.go | 33 +++ .../loadbalancer/v2/l7policies/requests.go | 9 +- .../loadbalancer/v2/listeners/requests.go | 18 +- .../loadbalancer/v2/listeners/results.go | 3 + .../loadbalancer/v2/loadbalancers/doc.go | 48 ++++ .../loadbalancer/v2/loadbalancers/requests.go | 16 ++ .../v2/loadbalancers/testing/fixtures.go | 255 ++++++++++++++++++ .../v2/loadbalancers/testing/requests_test.go | 63 +++++ .../loadbalancer/v2/monitors/requests.go | 2 +- openstack/loadbalancer/v2/pools/requests.go | 19 +- 11 files changed, 574 insertions(+), 5 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 4ab7bc8b82..944a2cdb9e 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -141,6 +141,119 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI return lb, nil } +// CreateLoadBalancerFullyPopulated will create a fully populated load balancer with a random name on a given +// subnet. It will contain a listener, l7policy, l7rule, pool, member and health monitor. +// An error will be returned if the loadbalancer could not be created. +func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string) (*loadbalancers.LoadBalancer, error) { + lbName := tools.RandomString("TESTACCT-", 8) + lbDescription := tools.RandomString("TESTACCT-DESC-", 8) + listenerName := tools.RandomString("TESTACCT-", 8) + listenerDescription := tools.RandomString("TESTACCT-DESC-", 8) + listenerPort := tools.RandomInt(1, 100) + policyName := tools.RandomString("TESTACCT-", 8) + policyDescription := tools.RandomString("TESTACCT-DESC-", 8) + poolName := tools.RandomString("TESTACCT-", 8) + poolDescription := tools.RandomString("TESTACCT-DESC-", 8) + memberName := tools.RandomString("TESTACCT-", 8) + memberPort := tools.RandomInt(100, 1000) + memberWeight := tools.RandomInt(1, 10) + + 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) + + createOpts := loadbalancers.CreateOpts{ + Name: lbName, + Description: lbDescription, + VipSubnetID: subnetID, + AdminStateUp: gophercloud.Enabled, + Listeners: []listeners.CreateOpts{{ + Name: listenerName, + Description: listenerDescription, + Protocol: listeners.ProtocolHTTP, + ProtocolPort: listenerPort, + DefaultPool: &pools.CreateOpts{ + Name: poolName, + Description: poolDescription, + Protocol: pools.ProtocolHTTP, + LBMethod: pools.LBMethodLeastConnections, + Members: []pools.BatchUpdateMemberOpts{{ + Name: &memberName, + ProtocolPort: memberPort, + Weight: &memberWeight, + Address: "1.2.3.4", + SubnetID: &subnetID, + }}, + Monitor: &monitors.CreateOpts{ + Delay: 10, + Timeout: 5, + MaxRetries: 5, + MaxRetriesDown: 4, + Type: monitors.TypeHTTP, + }, + }, + L7Policies: []l7policies.CreateOpts{{ + Name: policyName, + Description: policyDescription, + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + Rules: []l7policies.CreateRuleOpts{{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeStartWith, + Value: "/api", + }}, + }}, + }}, + } + if len(tags) > 0 { + createOpts.Tags = tags + } + + lb, err := loadbalancers.Create(client, createOpts).Extract() + if err != nil { + return lb, err + } + + t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID) + t.Logf("Waiting for loadbalancer %s to become active", lbName) + + if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { + return lb, err + } + + t.Logf("LoadBalancer %s is active", lbName) + + th.AssertEquals(t, lb.Name, lbName) + th.AssertEquals(t, lb.Description, lbDescription) + th.AssertEquals(t, lb.VipSubnetID, subnetID) + th.AssertEquals(t, lb.AdminStateUp, true) + + th.AssertEquals(t, len(lb.Listeners), 1) + th.AssertEquals(t, lb.Listeners[0].Name, listenerName) + th.AssertEquals(t, lb.Listeners[0].Description, listenerDescription) + th.AssertEquals(t, lb.Listeners[0].ProtocolPort, listenerPort) + + th.AssertEquals(t, len(lb.Listeners[0].L7Policies), 1) + th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Name, policyName) + th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Description, policyDescription) + th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Description, policyDescription) + th.AssertEquals(t, len(lb.Listeners[0].L7Policies[0].Rules), 1) + + th.AssertEquals(t, len(lb.Pools), 1) + th.AssertEquals(t, lb.Pools[0].Name, poolName) + th.AssertEquals(t, lb.Pools[0].Description, poolDescription) + + th.AssertEquals(t, len(lb.Pools[0].Members), 1) + th.AssertEquals(t, lb.Pools[0].Members[0].Name, memberName) + th.AssertEquals(t, lb.Pools[0].Members[0].ProtocolPort, memberPort) + th.AssertEquals(t, lb.Pools[0].Members[0].Weight, memberWeight) + + if len(tags) > 0 { + th.AssertDeepEquals(t, lb.Tags, tags) + } + + return lb, nil +} + // CreateMember will create a member with a random name, port, address, and // weight. An error will be returned if the member could not be created. func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) { diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 2984361ecf..97e798253c 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -590,3 +590,36 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { th.AssertEquals(t, newMonitor.Delay, newDelay) th.AssertEquals(t, newMonitor.MaxRetriesDown, newMaxRetriesDown) } + +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") + + netClient, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + lbClient, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + network, err := networking.CreateNetwork(t, netClient) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, netClient, network.ID) + + subnet, err := networking.CreateSubnet(t, netClient, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, netClient, subnet.ID) + + tags := []string{"test"} + lb, err := CreateLoadBalancerFullyPopulated(t, lbClient, subnet.ID, tags) + th.AssertNoErr(t, err) + defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) + + newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newLB) +} diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 19f487450f..ea008086a6 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -40,7 +40,7 @@ type CreateOpts struct { Name string `json:"name,omitempty"` // The ID of the listener. - ListenerID string `json:"listener_id" required:"true"` + ListenerID string `json:"listener_id,omitempty"` // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. Action Action `json:"action" required:"true"` @@ -66,6 +66,13 @@ type CreateOpts struct { // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Rules is a slice of CreateRuleOpts which allows a set of rules + // to be created at the same time the policy is created. + // + // This is only possible to use when creating a fully populated + // Loadbalancer. + Rules []CreateRuleOpts `json:"rules,omitempty" xor:"ListenerID"` } // ToL7PolicyCreateMap builds a request body from CreateOpts. diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index f839fa9506..c905ebe35b 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -2,6 +2,8 @@ package listeners import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" "github.com/gophercloud/gophercloud/pagination" ) @@ -84,7 +86,7 @@ type CreateOptsBuilder interface { // CreateOpts represents options for creating a listener. type CreateOpts struct { // The load balancer on which to provision this listener. - LoadbalancerID string `json:"loadbalancer_id" required:"true"` + LoadbalancerID string `json:"loadbalancer_id,omitempty"` // The protocol - can either be TCP, HTTP, HTTPS or TERMINATED_HTTPS. Protocol Protocol `json:"protocol" required:"true"` @@ -102,6 +104,13 @@ type CreateOpts struct { // The ID of the default pool with which the Listener is associated. DefaultPoolID string `json:"default_pool_id,omitempty"` + // DefaultPool an instance of pools.CreateOpts which allows a + // (default) pool to be created at the same time the listener is created. + // + // This is only possible to use when creating a fully populated + // load balancer. + DefaultPool *pools.CreateOpts `json:"default_pool,omitempty" xor:"LoadbalancerID"` + // Human-readable description for the Listener. Description string `json:"description,omitempty"` @@ -118,6 +127,13 @@ type CreateOpts struct { // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + // L7Policies is a slice of l7policies.CreateOpts which allows a set + // of policies to be created at the same time the listener is created. + // + // This is only possible to use when creating a fully populated + // Loadbalancer. + L7Policies []l7policies.CreateOpts `json:"l7policies,omitempty" xor:"LoadbalancerID"` + // Frontend client inactivity timeout in milliseconds TimeoutClientData *int `json:"timeout_client_data,omitempty"` diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 7bf6734ef7..3271c6ae05 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -37,6 +37,9 @@ type Listener struct { // The UUID of default pool. Must have compatible protocol with listener. DefaultPoolID string `json:"default_pool_id"` + // The default pool with which the Listener is associated. + DefaultPool *pools.Pool `json:"default_pool"` + // A list of load balancer IDs. Loadbalancers []LoadBalancerID `json:"loadbalancers"` diff --git a/openstack/loadbalancer/v2/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go index 5c2b42d60a..8587a0d99e 100644 --- a/openstack/loadbalancer/v2/loadbalancers/doc.go +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -39,6 +39,54 @@ Example to Create a Load Balancer panic(err) } +Example to Create a fully populated Load Balancer + + createOpts := loadbalancers.CreateOpts{ + Name: "db_lb", + AdminStateUp: gophercloud.Enabled, + VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + VipAddress: "10.30.176.48", + FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", + Provider: "haproxy", + Tags: []string{"test", "stage"}, + Listeners: []listeners.CreateOpts{{ + Protocol: "HTTP", + ProtocolPort: 8080, + Name: "redirect_listener", + L7Policies: []l7policies.CreateOpts{{ + Name: "redirect-example.com", + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + Rules: []l7policies.CreateRuleOpts{{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images*", + }}, + }}, + DefaultPool: &pools.CreateOpts{ + LBMethod: pools.LBMethodRoundRobin, + Protocol: "HTTP", + Name: "example pool", + Members: []pools.BatchUpdateMemberOpts{{ + Address: "192.0.2.51", + ProtocolPort: 80, + },}, + Monitor: &monitors.CreateOpts{ + Name: "db", + Type: "HTTP", + Delay: 3, + MaxRetries: 2, + Timeout: 1, + }, + }, + }}, + } + + lb, err := loadbalancers.Create(networkClient, createOpts).Extract() + if err != nil { + panic(err) + } + Example to Update a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 63c832fb01..42179ce7e3 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -2,6 +2,8 @@ package loadbalancers import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" "github.com/gophercloud/gophercloud/pagination" ) @@ -119,6 +121,20 @@ type CreateOpts struct { // The name of the provider. Provider string `json:"provider,omitempty"` + // Listeners is a slice of listeners.CreateOpts which allows a set + // of listeners to be created at the same time the Loadbalancer is created. + // + // This is only possible to use when creating a fully populated + // load balancer. + Listeners []listeners.CreateOpts `json:"listeners,omitempty"` + + // Pools is a slice of pools.CreateOpts which allows a set of pools + // to be created at the same time the Loadbalancer is created. + // + // This is only possible to use when creating a fully populated + // load balancer. + Pools []pools.CreateOpts `json:"pools,omitempty"` + // Tags is a set of resource tags. Tags []string `json:"tags,omitempty"` } diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go index 3f76f797e2..e0ddcb72e0 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" @@ -104,6 +105,100 @@ const PostUpdateLoadbalancerBody = ` } ` +// PostFullyPopulatedLoadbalancerBody is the canned response body of a Create request of an fully populated loadbalancer. +const PostFullyPopulatedLoadbalancerBody = ` +{ + "loadbalancer": { + "description": "My favorite load balancer", + "admin_state_up": true, + "project_id": "e3cd678b11784734bc366148aa37580e", + "provisioning_status": "ACTIVE", + "flavor_id": "", + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49", + "listeners": [{ + "l7policies": [{ + "description": "", + "admin_state_up": true, + "rules": [], + "project_id": "e3cd678b11784734bc366148aa37580e", + "listener_id": "95de30ec-67f4-437b-b3f3-22c5d9ef9828", + "redirect_url": "https://www.example.com/", + "action": "REDIRECT_TO_URL", + "position": 1, + "id": "d0553837-f890-4981-b99a-f7cbd6a76577", + "name": "redirect_policy" + }], + "protocol": "HTTP", + "description": "", + "default_tls_container_ref": null, + "admin_state_up": true, + "default_pool_id": "c8cec227-410a-4a5b-af13-ecf38c2b0abb", + "project_id": "e3cd678b11784734bc366148aa37580e", + "default_tls_container_id": null, + "connection_limit": -1, + "sni_container_refs": [], + "protocol_port": 8080, + "id": "95de30ec-67f4-437b-b3f3-22c5d9ef9828", + "name": "redirect_listener" + }], + "vip_address": "203.0.113.50", + "vip_network_id": "d0d217df-3958-4fbf-a3c2-8dad2908c709", + "vip_subnet_id": "d4af86e1-0051-488c-b7a0-527f97490c9a", + "vip_port_id": "b4ca07d1-a31e-43e2-891a-7d14f419f342", + "provider": "octavia", + "pools": [{ + "lb_algorithm": "ROUND_ROBIN", + "protocol": "HTTP", + "description": "", + "admin_state_up": true, + "project_id": "e3cd678b11784734bc366148aa37580e", + "session_persistence": null, + "healthmonitor": { + "name": "", + "admin_state_up": true, + "project_id": "e3cd678b11784734bc366148aa37580e", + "delay": 3, + "expected_codes": "200,201,202", + "max_retries": 2, + "http_method": "GET", + "timeout": 1, + "max_retries_down": 3, + "url_path": "/index.html", + "type": "HTTP", + "id": "a8a2aa3f-d099-4752-8265-e6472f8147f9" + }, + "members": [{ + "name": "", + "weight": 1, + "admin_state_up": true, + "subnet_id": "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + "project_id": "e3cd678b11784734bc366148aa37580e", + "address": "192.0.2.16", + "protocol_port": 80, + "id": "7d19ad6c-d549-453e-a5cd-05382c6be96a" + },{ + "name": "", + "weight": 1, + "admin_state_up": true, + "subnet_id": "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + "project_id": "e3cd678b11784734bc366148aa37580e", + "address": "192.0.2.19", + "protocol_port": 80, + "id": "a167402b-caa6-41d5-b4d4-bde7f2cbfa5e" + }], + "id": "c8cec227-410a-4a5b-af13-ecf38c2b0abb", + "name": "rr_pool" + }], + "id": "607226db-27ef-4d41-ae89-f2a800e9c2db", + "operating_status": "ONLINE", + "name": "best_load_balancer", + "availability_zone": "my_az", + "tags": ["test_tag"] + } +} +` + // GetLoadbalancerStatusesBody is the canned request body of a Get request on loadbalancer's status. const GetLoadbalancerStatusesBody = ` { @@ -210,6 +305,91 @@ var ( OperatingStatus: "OFFLINE", Tags: []string{"test"}, } + FullyPopulatedLoadBalancerDb = loadbalancers.LoadBalancer{ + Description: "My favorite load balancer", + AdminStateUp: true, + ProjectID: "e3cd678b11784734bc366148aa37580e", + UpdatedAt: updatedTime, + CreatedAt: createdTime, + ProvisioningStatus: "ACTIVE", + VipSubnetID: "d4af86e1-0051-488c-b7a0-527f97490c9a", + VipNetworkID: "d0d217df-3958-4fbf-a3c2-8dad2908c709", + VipAddress: "203.0.113.50", + VipPortID: "b4ca07d1-a31e-43e2-891a-7d14f419f342", + AvailabilityZone: "my_az", + ID: "607226db-27ef-4d41-ae89-f2a800e9c2db", + OperatingStatus: "ONLINE", + Name: "best_load_balancer", + FlavorID: "", + Provider: "octavia", + Tags: []string{"test_tag"}, + Listeners: []listeners.Listener{{ + ID: "95de30ec-67f4-437b-b3f3-22c5d9ef9828", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Name: "redirect_listener", + Description: "", + Protocol: "HTTP", + ProtocolPort: 8080, + DefaultPoolID: "c8cec227-410a-4a5b-af13-ecf38c2b0abb", + AdminStateUp: true, + ConnLimit: -1, + SniContainerRefs: []string{}, + L7Policies: []l7policies.L7Policy{{ + ID: "d0553837-f890-4981-b99a-f7cbd6a76577", + Name: "redirect_policy", + ListenerID: "95de30ec-67f4-437b-b3f3-22c5d9ef9828", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Description: "", + Action: "REDIRECT_TO_URL", + Position: 1, + RedirectURL: "https://www.example.com/", + AdminStateUp: true, + Rules: []l7policies.Rule{}, + }}, + }}, + Pools: []pools.Pool{{ + LBMethod: "ROUND_ROBIN", + Protocol: "HTTP", + Description: "", + AdminStateUp: true, + Name: "rr_pool", + ID: "c8cec227-410a-4a5b-af13-ecf38c2b0abb", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Members: []pools.Member{{ + Name: "", + Address: "192.0.2.16", + SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + AdminStateUp: true, + ProtocolPort: 80, + ID: "7d19ad6c-d549-453e-a5cd-05382c6be96a", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Weight: 1, + }, { + Name: "", + Address: "192.0.2.19", + SubnetID: "bbb35f84-35cc-4b2f-84c2-a6a29bba68aa", + AdminStateUp: true, + ProtocolPort: 80, + ID: "a167402b-caa6-41d5-b4d4-bde7f2cbfa5e", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Weight: 1, + }}, + Monitor: monitors.Monitor{ + ID: "a8a2aa3f-d099-4752-8265-e6472f8147f9", + ProjectID: "e3cd678b11784734bc366148aa37580e", + Name: "", + Type: "HTTP", + Timeout: 1, + MaxRetries: 2, + Delay: 3, + MaxRetriesDown: 3, + HTTPMethod: "GET", + URLPath: "/index.html", + ExpectedCodes: "200,201,202", + AdminStateUp: true, + }, + }}, + } LoadbalancerStatusesTree = loadbalancers.StatusTree{ Loadbalancer: &loadbalancers.LoadBalancer{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -270,6 +450,81 @@ func HandleLoadbalancerListSuccessfully(t *testing.T) { }) } +// HandleFullyPopulatedLoadbalancerCreationSuccessfully sets up the test server to respond to a +// fully populated loadbalancer creation request with a given response. +func HandleFullyPopulatedLoadbalancerCreationSuccessfully(t *testing.T, response string) { + th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", 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, `{ + "loadbalancer": { + "admin_state_up": true, + "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", + "listeners": [ + { + "default_pool": { + "healthmonitor": { + "delay": 3, + "expected_codes": "200", + "http_method": "GET", + "max_retries": 2, + "max_retries_down": 3, + "name": "db", + "timeout": 1, + "type": "HTTP", + "url_path": "/index.html" + }, + "lb_algorithm": "ROUND_ROBIN", + "members": [ + { + "address": "192.0.2.51", + "protocol_port": 80 + }, + { + "address": "192.0.2.52", + "protocol_port": 80 + } + ], + "name": "Example pool", + "protocol": "HTTP" + }, + "l7policies": [ + { + "action": "REDIRECT_TO_URL", + "name": "redirect-example.com", + "redirect_url": "http://www.example.com", + "rules": [ + { + "compare_type": "REGEX", + "type": "PATH", + "value": "/images*" + } + ] + } + ], + "name": "redirect_listener", + "protocol": "HTTP", + "protocol_port": 8080 + } + ], + "name": "db_lb", + "provider": "octavia", + "tags": [ + "test", + "stage" + ], + "vip_address": "10.30.176.48", + "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", + "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + // HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request // with a given response. func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 9684bc280f..fbd33c648c 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -3,6 +3,11 @@ package testing import ( "testing" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" @@ -73,6 +78,64 @@ func TestCreateLoadbalancer(t *testing.T) { th.CheckDeepEquals(t, LoadbalancerDb, *actual) } +func TestCreateFullyPopulatedLoadbalancer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFullyPopulatedLoadbalancerCreationSuccessfully(t, PostFullyPopulatedLoadbalancerBody) + + actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{ + Name: "db_lb", + AdminStateUp: gophercloud.Enabled, + VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", + VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", + VipAddress: "10.30.176.48", + FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", + Provider: "octavia", + Tags: []string{"test", "stage"}, + Listeners: []listeners.CreateOpts{{ + Protocol: "HTTP", + ProtocolPort: 8080, + Name: "redirect_listener", + L7Policies: []l7policies.CreateOpts{{ + Name: "redirect-example.com", + Action: l7policies.ActionRedirectToURL, + RedirectURL: "http://www.example.com", + Rules: []l7policies.CreateRuleOpts{{ + RuleType: l7policies.TypePath, + CompareType: l7policies.CompareTypeRegex, + Value: "/images*", + }}, + }}, + DefaultPool: &pools.CreateOpts{ + LBMethod: pools.LBMethodRoundRobin, + Protocol: "HTTP", + Name: "Example pool", + Members: []pools.BatchUpdateMemberOpts{{ + Address: "192.0.2.51", + ProtocolPort: 80, + }, { + Address: "192.0.2.52", + ProtocolPort: 80, + }}, + Monitor: &monitors.CreateOpts{ + Name: "db", + Type: "HTTP", + Delay: 3, + Timeout: 1, + MaxRetries: 2, + MaxRetriesDown: 3, + URLPath: "/index.html", + HTTPMethod: "GET", + ExpectedCodes: "200", + }, + }, + }}, + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, FullyPopulatedLoadBalancerDb, *actual) +} + func TestGetLoadbalancer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index aedf672fc1..cdfc810c9b 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -91,7 +91,7 @@ type CreateOptsBuilder interface { // operation. type CreateOpts struct { // The Pool to Monitor. - PoolID string `json:"pool_id" required:"true"` + PoolID string `json:"pool_id,omitempty"` // The type of probe, which is PING, TCP, HTTP, or HTTPS, that is // sent by the load balancer to verify the member state. diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 8c3718bec5..bf93656b02 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -2,6 +2,7 @@ package pools import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/pagination" ) @@ -92,11 +93,11 @@ type CreateOpts struct { // The Loadbalancer on which the members of the pool will be associated with. // Note: one of LoadbalancerID or ListenerID must be provided. - LoadbalancerID string `json:"loadbalancer_id,omitempty" xor:"ListenerID"` + LoadbalancerID string `json:"loadbalancer_id,omitempty"` // The Listener on which the members of the pool will be associated with. // Note: one of LoadbalancerID or ListenerID must be provided. - ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"` + ListenerID string `json:"listener_id,omitempty"` // ProjectID is the UUID of the project who owns the Pool. // Only administrative users can specify a project UUID other than their own. @@ -115,6 +116,20 @@ type CreateOpts struct { // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Members is a slice of BatchUpdateMemberOpts which allows a set of + // members to be created at the same time the pool is created. + // + // This is only possible to use when creating a fully populated + // Loadbalancer. + Members []BatchUpdateMemberOpts `json:"members,omitempty"` + + // Monitor is an instance of monitors.CreateOpts which allows a monitor + // to be created at the same time the pool is created. + // + // This is only possible to use when creating a fully populated + // Loadbalancer. + Monitor *monitors.CreateOpts `json:"healthmonitor,omitempty"` } // ToPoolCreateMap builds a request body from CreateOpts. From d1b55617c7f03faba4ae595b418a9120ff4a72da Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 27 Dec 2020 13:30:36 -0700 Subject: [PATCH 1187/2296] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 636f149ec2..f9da17c04e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,14 @@ IMPROVEMENTS * 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) From 810f391f1126720acfa83e2c3b1cd73708fd5ef7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 27 Dec 2020 13:31:17 -0700 Subject: [PATCH 1188/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9da17c04e..b436f0076a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.15.0 (Unreleased) +## 0.16.0 (Unreleased) + +## 0.15.0 (December 27, 2020) BREAKING CHANGES From 017c1524158df86cc7648ee9290f389c65d6ea15 Mon Sep 17 00:00:00 2001 From: Stepan Date: Tue, 5 Jan 2021 03:19:01 +0200 Subject: [PATCH 1189/2296] Blockstorage extensions limits (#2084) * adding limits directory with all code and unit tests * adding acceptance test for blockstorage/extensions/limits * update to blockstorage/extensions/limits/docs.go * adding rate struct; updating unit tests; updating acceptance tests * update to blockstorage/limits acceptance tests to make it flexible for the non-default values * clean-up of func names --- .../blockstorage/extensions/limits_test.go | 36 +++++ .../blockstorage/extensions/limits/doc.go | 13 ++ .../extensions/limits/requests.go | 13 ++ .../blockstorage/extensions/limits/results.go | 80 +++++++++++ .../extensions/limits/testing/fixtures.go | 129 ++++++++++++++++++ .../limits/testing/requests_test.go | 19 +++ .../blockstorage/extensions/limits/urls.go | 11 ++ testhelper/convenience.go | 14 ++ testhelper/http_responses.go | 2 +- 9 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 acceptance/openstack/blockstorage/extensions/limits_test.go create mode 100644 openstack/blockstorage/extensions/limits/doc.go create mode 100644 openstack/blockstorage/extensions/limits/requests.go create mode 100644 openstack/blockstorage/extensions/limits/results.go create mode 100644 openstack/blockstorage/extensions/limits/testing/fixtures.go create mode 100644 openstack/blockstorage/extensions/limits/testing/requests_test.go create mode 100644 openstack/blockstorage/extensions/limits/urls.go diff --git a/acceptance/openstack/blockstorage/extensions/limits_test.go b/acceptance/openstack/blockstorage/extensions/limits_test.go new file mode 100644 index 0000000000..c1b3994f9c --- /dev/null +++ b/acceptance/openstack/blockstorage/extensions/limits_test.go @@ -0,0 +1,36 @@ +package extensions + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/acceptance/tools" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestLimits(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + limits, err := limits.Get(client).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, limits) + + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalVolumes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalSnapshots, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalVolumeGigabytes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalBackups, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalBackupGigabytes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalVolumesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalVolumesUsed, limits.Absolute.MaxTotalVolumes) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalGigabytesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalGigabytesUsed, limits.Absolute.MaxTotalVolumeGigabytes) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalSnapshotsUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalSnapshotsUsed, limits.Absolute.MaxTotalSnapshots) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalBackupsUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalBackupsUsed, limits.Absolute.MaxTotalBackups) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalBackupGigabytesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalBackupGigabytesUsed, limits.Absolute.MaxTotalBackupGigabytes) +} diff --git a/openstack/blockstorage/extensions/limits/doc.go b/openstack/blockstorage/extensions/limits/doc.go new file mode 100644 index 0000000000..df2a630bb5 --- /dev/null +++ b/openstack/blockstorage/extensions/limits/doc.go @@ -0,0 +1,13 @@ +/* +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) + } + + fmt.Printf("%+v\n", limits) +*/ +package limits diff --git a/openstack/blockstorage/extensions/limits/requests.go b/openstack/blockstorage/extensions/limits/requests.go new file mode 100644 index 0000000000..21721f1bc8 --- /dev/null +++ b/openstack/blockstorage/extensions/limits/requests.go @@ -0,0 +1,13 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud" +) + +// Get returns the limits about the currently scoped tenant. +func Get(client *gophercloud.ServiceClient) (r GetResult) { + url := getURL(client) + resp, err := client.Get(url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/extensions/limits/results.go b/openstack/blockstorage/extensions/limits/results.go new file mode 100644 index 0000000000..f0a74b7ec4 --- /dev/null +++ b/openstack/blockstorage/extensions/limits/results.go @@ -0,0 +1,80 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud" +) + +// Limits is a struct that contains the response of a limit query. +type Limits struct { + // Absolute contains the limits and usage information. + // An absolute limit value of -1 indicates that the absolute limit for the item is infinite. + Absolute Absolute `json:"absolute"` + // Rate contains rate-limit volume copy bandwidth, used to mitigate slow down of data access from the instances. + Rate []Rate `json:"rate"` +} + +// Absolute is a struct that contains the current resource usage and limits +// of a project. +type Absolute struct { + // MaxTotalVolumes is the maximum number of volumes. + MaxTotalVolumes int `json:"maxTotalVolumes"` + + // MaxTotalSnapshots is the maximum number of snapshots. + MaxTotalSnapshots int `json:"maxTotalSnapshots"` + + // MaxTotalVolumeGigabytes is the maximum total amount of volumes, in gibibytes (GiB). + MaxTotalVolumeGigabytes int `json:"maxTotalVolumeGigabytes"` + + // MaxTotalBackups is the maximum number of backups. + MaxTotalBackups int `json:"maxTotalBackups"` + + // MaxTotalBackupGigabytes is the maximum total amount of backups, in gibibytes (GiB). + MaxTotalBackupGigabytes int `json:"maxTotalBackupGigabytes"` + + // TotalVolumesUsed is the total number of volumes used. + TotalVolumesUsed int `json:"totalVolumesUsed"` + + // TotalGigabytesUsed is the total number of gibibytes (GiB) used. + TotalGigabytesUsed int `json:"totalGigabytesUsed"` + + // TotalSnapshotsUsed the total number of snapshots used. + TotalSnapshotsUsed int `json:"totalSnapshotsUsed"` + + // TotalBackupsUsed is the total number of backups used. + TotalBackupsUsed int `json:"totalBackupsUsed"` + + // TotalBackupGigabytesUsed is the total number of backups gibibytes (GiB) used. + TotalBackupGigabytesUsed int `json:"totalBackupGigabytesUsed"` +} + +// Rate is a struct that contains the +// rate-limit volume copy bandwidth, used to mitigate slow down of data access from the instances. +type Rate struct { + Regex string `json:"regex"` + URI string `json:"uri"` + Limit []Limit `json:"limit"` +} + +// Limit struct contains Limit values for the Rate struct +type Limit struct { + Verb string `json:"verb"` + NextAvailable string `json:"next-available"` + Unit string `json:"unit"` + Value int `json:"value"` + Remaining int `json:"remaining"` +} + +// Extract interprets a limits result as a Limits. +func (r GetResult) Extract() (*Limits, error) { + var s struct { + Limits *Limits `json:"limits"` + } + err := r.ExtractInto(&s) + return s.Limits, err +} + +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as an Absolute. +type GetResult struct { + gophercloud.Result +} diff --git a/openstack/blockstorage/extensions/limits/testing/fixtures.go b/openstack/blockstorage/extensions/limits/testing/fixtures.go new file mode 100644 index 0000000000..2df756eff3 --- /dev/null +++ b/openstack/blockstorage/extensions/limits/testing/fixtures.go @@ -0,0 +1,129 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// GetOutput is a sample response to a Get call. +const GetOutput = ` +{ + "limits": { + "rate": [ + { + "regex": ".*", + "uri": "*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00", + "unit": "MINUTE", + "value": 10, + "remaining": 10 + }, + { + "verb": "POST", + "next-available": "1970-01-01T00:00:00", + "unit": "HOUR", + "value": 5, + "remaining": 5 + } + ] + }, + { + "regex": "changes-since", + "uri": "changes-since*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00", + "unit": "MINUTE", + "value": 5, + "remaining": 5 + } + ] + } + ], + "absolute": { + "maxTotalVolumes": 40, + "maxTotalSnapshots": 40, + "maxTotalVolumeGigabytes": 1000, + "maxTotalBackups": 10, + "maxTotalBackupGigabytes": 1000, + "totalVolumesUsed": 1, + "totalGigabytesUsed": 100, + "totalSnapshotsUsed": 1, + "totalBackupsUsed": 1, + "totalBackupGigabytesUsed": 50 + } + } +} +` + +// LimitsResult is the result of the limits in GetOutput. +var LimitsResult = limits.Limits{ + Rate: []limits.Rate{ + { + Regex: ".*", + URI: "*", + Limit: []limits.Limit{ + { + Verb: "GET", + NextAvailable: "1970-01-01T00:00:00", + Unit: "MINUTE", + Value: 10, + Remaining: 10, + }, + { + Verb: "POST", + NextAvailable: "1970-01-01T00:00:00", + Unit: "HOUR", + Value: 5, + Remaining: 5, + }, + }, + }, + { + Regex: "changes-since", + URI: "changes-since*", + Limit: []limits.Limit{ + { + Verb: "GET", + NextAvailable: "1970-01-01T00:00:00", + Unit: "MINUTE", + Value: 5, + Remaining: 5, + }, + }, + }, + }, + Absolute: limits.Absolute{ + MaxTotalVolumes: 40, + MaxTotalSnapshots: 40, + MaxTotalVolumeGigabytes: 1000, + MaxTotalBackups: 10, + MaxTotalBackupGigabytes: 1000, + TotalVolumesUsed: 1, + TotalGigabytesUsed: 100, + TotalSnapshotsUsed: 1, + TotalBackupsUsed: 1, + TotalBackupGigabytesUsed: 50, + }, +} + +// HandleGetSuccessfully configures the test server to respond to a Get request +// for a limit. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits", 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, GetOutput) + }) +} diff --git a/openstack/blockstorage/extensions/limits/testing/requests_test.go b/openstack/blockstorage/extensions/limits/testing/requests_test.go new file mode 100644 index 0000000000..b1a59673ca --- /dev/null +++ b/openstack/blockstorage/extensions/limits/testing/requests_test.go @@ -0,0 +1,19 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := limits.Get(client.ServiceClient()).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &LimitsResult, actual) +} diff --git a/openstack/blockstorage/extensions/limits/urls.go b/openstack/blockstorage/extensions/limits/urls.go new file mode 100644 index 0000000000..edd97e4e0e --- /dev/null +++ b/openstack/blockstorage/extensions/limits/urls.go @@ -0,0 +1,11 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud" +) + +const resourcePath = "limits" + +func getURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} diff --git a/testhelper/convenience.go b/testhelper/convenience.go index d1dafb3758..3eb34822a7 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -354,3 +354,17 @@ func CheckNoErr(t *testing.T, e error) { 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) { + logFatal(t, fmt.Sprintf("The first value \"%v\" is greater than the second value \"%v\"", v1, v2)) + } +} + +// AssertIntGreaterOrEqual verifies that first value is greater or equal than second values +func AssertIntGreaterOrEqual(t *testing.T, v1 int, v2 int) { + if !(v1 >= v2) { + logFatal(t, fmt.Sprintf("The first value \"%v\" is lesser than the second value \"%v\"", v1, v2)) + } +} diff --git a/testhelper/http_responses.go b/testhelper/http_responses.go index 7482134d9f..cee20f421d 100644 --- a/testhelper/http_responses.go +++ b/testhelper/http_responses.go @@ -20,7 +20,7 @@ var ( Server *httptest.Server ) -// SetupHTTP prepares the Mux and Server listening specific port. +// SetupPersistentPortHTTP prepares the Mux and Server listening specific port. func SetupPersistentPortHTTP(t *testing.T, port int) { l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) if err != nil { From a0104c2ead51c2ea4ffdd111a1f8916cb2776165 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 4 Jan 2021 18:20:02 -0700 Subject: [PATCH 1190/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b436f0076a..a369bbff29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.16.0 (Unreleased) +IMPROVEMENTS + +* Added `blockstorage/extensions/limits.Get` [GH-2084](https://github.com/gophercloud/gophercloud/pull/2084) + ## 0.15.0 (December 27, 2020) BREAKING CHANGES From 77a5e67cebe1cc3c0e1fdb3a02aa38c68a8afe7e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 4 Jan 2021 18:21:16 -0700 Subject: [PATCH 1191/2296] Update doc.go --- openstack/blockstorage/extensions/limits/doc.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openstack/blockstorage/extensions/limits/doc.go b/openstack/blockstorage/extensions/limits/doc.go index df2a630bb5..bb19f80305 100644 --- a/openstack/blockstorage/extensions/limits/doc.go +++ b/openstack/blockstorage/extensions/limits/doc.go @@ -4,10 +4,10 @@ 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) - } + if err != nil { + panic(err) + } - fmt.Printf("%+v\n", limits) + fmt.Printf("%+v\n", limits) */ package limits From fa957f3e1f14997f31f301788af11e247d1facc6 Mon Sep 17 00:00:00 2001 From: Andrew Karpow Date: Tue, 5 Jan 2021 02:23:44 +0100 Subject: [PATCH 1192/2296] Fixes fully-populated loadbalancer creation without default_pool, l7policies or l7rules (#2087) --- openstack/loadbalancer/v2/l7policies/requests.go | 2 +- openstack/loadbalancer/v2/listeners/requests.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index ea008086a6..1969ba0367 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -72,7 +72,7 @@ type CreateOpts struct { // // This is only possible to use when creating a fully populated // Loadbalancer. - Rules []CreateRuleOpts `json:"rules,omitempty" xor:"ListenerID"` + Rules []CreateRuleOpts `json:"rules,omitempty"` } // ToL7PolicyCreateMap builds a request body from CreateOpts. diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index c905ebe35b..07fa26848d 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -109,7 +109,7 @@ type CreateOpts struct { // // This is only possible to use when creating a fully populated // load balancer. - DefaultPool *pools.CreateOpts `json:"default_pool,omitempty" xor:"LoadbalancerID"` + DefaultPool *pools.CreateOpts `json:"default_pool,omitempty"` // Human-readable description for the Listener. Description string `json:"description,omitempty"` @@ -132,7 +132,7 @@ type CreateOpts struct { // // This is only possible to use when creating a fully populated // Loadbalancer. - L7Policies []l7policies.CreateOpts `json:"l7policies,omitempty" xor:"LoadbalancerID"` + L7Policies []l7policies.CreateOpts `json:"l7policies,omitempty"` // Frontend client inactivity timeout in milliseconds TimeoutClientData *int `json:"timeout_client_data,omitempty"` From d9f03aa2f75d9d2ce95bfe8fa3bfa2a7e40cba27 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 4 Jan 2021 18:25:04 -0700 Subject: [PATCH 1193/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a369bbff29..ea223c7b24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.16.0 (Unreleased) +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) + IMPROVEMENTS * Added `blockstorage/extensions/limits.Get` [GH-2084](https://github.com/gophercloud/gophercloud/pull/2084) From 6c454a512fb812614027af1882b5bfb10e9d341c Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Mon, 4 Jan 2021 17:27:53 -0800 Subject: [PATCH 1194/2296] Return actionID for Node Remove API (#2089) * Return actionID for Node Remove API * fix unit test --- acceptance/openstack/clustering/v1/clusters_test.go | 10 ++++------ openstack/clustering/v1/clusters/requests.go | 2 +- .../clustering/v1/clusters/testing/requests_test.go | 5 ++++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/acceptance/openstack/clustering/v1/clusters_test.go b/acceptance/openstack/clustering/v1/clusters_test.go index 8bc02f041e..bad20dfc05 100644 --- a/acceptance/openstack/clustering/v1/clusters_test.go +++ b/acceptance/openstack/clustering/v1/clusters_test.go @@ -340,17 +340,15 @@ func TestClustersRemoveNodeFromCluster(t *testing.T) { tools.PrintResource(t, cluster) opt := clusters.RemoveNodesOpts{Nodes: cluster.Nodes} - res := clusters.RemoveNodes(client, cluster.ID, opt) - err = res.ExtractErr() - th.AssertNoErr(t, err) + actionID, err := clusters.RemoveNodes(client, cluster.ID, opt).Extract() + if err != nil { + t.Fatalf("Unable to remove nodes to cluster: %v", err) + } for _, n := range cluster.Nodes { defer DeleteNode(t, client, n) } - actionID, err := GetActionID(res.Header) - th.AssertNoErr(t, err) - err = WaitForAction(client, actionID) th.AssertNoErr(t, err) diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index d89f86a1ce..4ba7477924 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -497,7 +497,7 @@ type RemoveNodesOpts struct { Nodes []string `json:"nodes" required:"true"` } -func RemoveNodes(client *gophercloud.ServiceClient, clusterID string, opts RemoveNodesOpts) (r DeleteResult) { +func RemoveNodes(client *gophercloud.ServiceClient, clusterID string, opts RemoveNodesOpts) (r ActionResult) { b, err := opts.ToClusterRemoveNodeMap() if err != nil { r.Err = err diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 1de7161a4b..1d723a53ce 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -444,12 +444,15 @@ func TestAddNodes(t *testing.T) { func TestRemoveNodes(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() + HandleRemoveNodesSuccessfully(t) + opts := clusters.RemoveNodesOpts{ Nodes: []string{"node1", "node2", "node3"}, } - err := clusters.RemoveNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).ExtractErr() + result, err := clusters.RemoveNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) + th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") } func TestReplaceNodes(t *testing.T) { From e34a44dc6580d7c0ef1c6af139a061c63c966a22 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 4 Jan 2021 18:28:56 -0700 Subject: [PATCH 1195/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea223c7b24..16b95ab734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ BUG FIXES 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) ## 0.15.0 (December 27, 2020) From 81c7624c6c8ea7218c8660d2d3234cf69bbbaf73 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 7 Jan 2021 07:35:29 +0100 Subject: [PATCH 1196/2296] Bump golang.org/x/crypto (#2092) Even though Gophercloud might now be directly affectred by CVE-2020-29652, removing the vulnerable dependency will: * endure that such a vulnerability is not introduced in Gophercloud in the future; * simplify threat analysis in dependent packages. After this patch, only one occurrence of the vulnerable library will remain; it is imported indirectly by the new, fixed version of the same golang.org/x/crypto. ref.: https://go-review.googlesource.com/c/crypto/+/281632 --- go.mod | 6 ++---- go.sum | 23 +++++++++++------------ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 64e2a0fb48..8c83df2349 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,7 @@ module github.com/gophercloud/gophercloud go 1.13 require ( - github.com/kr/pretty v0.2.1 // indirect - golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e - golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index 311ab0449d..c91f7ee224 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,18 @@ -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU= -golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +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/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +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= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From f5eb9e0dd0e2dde9c1d54309086a274239ce1388 Mon Sep 17 00:00:00 2001 From: Sunghoon Kang Date: Sun, 10 Jan 2021 12:11:22 +0900 Subject: [PATCH 1197/2296] Add available project listing (#2090) Related issue: https://github.com/gophercloud/gophercloud/issues/1719 Refers https://docs.openstack.org/api-ref/identity/v3/#get-available-project-scopes --- .../openstack/identity/v3/projects_test.go | 17 ++++ openstack/identity/v3/projects/requests.go | 8 ++ .../identity/v3/projects/testing/fixtures.go | 79 +++++++++++++++++++ .../v3/projects/testing/requests_test.go | 20 +++++ openstack/identity/v3/projects/urls.go | 4 + 5 files changed, 128 insertions(+) diff --git a/acceptance/openstack/identity/v3/projects_test.go b/acceptance/openstack/identity/v3/projects_test.go index bc1803475f..c50aaec931 100644 --- a/acceptance/openstack/identity/v3/projects_test.go +++ b/acceptance/openstack/identity/v3/projects_test.go @@ -11,6 +11,23 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) +func TestProjectsListAvailable(t *testing.T) { + clients.RequireNonAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + allPages, err := projects.ListAvailable(client).AllPages() + th.AssertNoErr(t, err) + + allProjects, err := projects.ExtractProjects(allPages) + th.AssertNoErr(t, err) + + for _, project := range allProjects { + tools.PrintResource(t, project) + } +} + func TestProjectsList(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 27092ebb5b..3cda901523 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -85,6 +85,14 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } +// ListAvailable enumerates the Projects 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 ProjectPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + // Get retrieves details on a single project, 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/projects/testing/fixtures.go b/openstack/identity/v3/projects/testing/fixtures.go index b774e3fc82..009a2f07ac 100644 --- a/openstack/identity/v3/projects/testing/fixtures.go +++ b/openstack/identity/v3/projects/testing/fixtures.go @@ -10,6 +10,41 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +// ListAvailableOutput provides a single page of available Project results. +const ListAvailableOutput = ` +{ + "projects": [ + { + "description": "my first project", + "domain_id": "11111", + "enabled": true, + "id": "abcde", + "links": { + "self": "http://localhost:5000/identity/v3/projects/abcde" + }, + "name": "project 1", + "parent_id": "11111" + }, + { + "description": "my second project", + "domain_id": "22222", + "enabled": true, + "id": "bcdef", + "links": { + "self": "http://localhost:5000/identity/v3/projects/bcdef" + }, + "name": "project 2", + "parent_id": "22222" + } + ], + "links": { + "next": null, + "previous": null, + "self": "http://localhost:5000/identity/v3/users/foobar/projects" + } +} +` + // ListOutput provides a single page of Project results. const ListOutput = ` { @@ -103,6 +138,32 @@ const UpdateOutput = ` } ` +// FirstProject is a Project fixture. +var FirstProject = projects.Project{ + Description: "my first project", + DomainID: "11111", + Enabled: true, + ID: "abcde", + Name: "project 1", + ParentID: "11111", + Extra: map[string]interface{}{ + "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/abcde"}, + }, +} + +// SecondProject is a Project fixture. +var SecondProject = projects.Project{ + Description: "my second project", + DomainID: "22222", + Enabled: true, + ID: "bcdef", + Name: "project 2", + ParentID: "22222", + Extra: map[string]interface{}{ + "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/bcdef"}, + }, +} + // RedTeam is a Project fixture. var RedTeam = projects.Project{ IsDomain: false, @@ -144,9 +205,27 @@ var UpdatedRedTeam = projects.Project{ Extra: map[string]interface{}{"test": "new"}, } +// ExpectedAvailableProjectsSlice is the slice of projects expected to be returned +// from ListAvailableOutput. +var ExpectedAvailableProjectsSlice = []projects.Project{FirstProject, SecondProject} + // ExpectedProjectSlice is the slice of projects expected to be returned from ListOutput. var ExpectedProjectSlice = []projects.Project{RedTeam, BlueTeam} +// HandleListAvailableProjectsSuccessfully creates an HTTP handler at `/auth/projects` +// on the test handler mux that responds with a list of two tenants. +func HandleListAvailableProjectsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/auth/projects", 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) + }) +} + // HandleListProjectsSuccessfully creates an HTTP handler at `/projects` on the // test handler mux that responds with a list of two tenants. func HandleListProjectsSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 1a78699425..47420ac9f7 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -9,6 +9,26 @@ import ( "github.com/gophercloud/gophercloud/testhelper/client" ) +func TestListAvailableProjects(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListAvailableProjectsSuccessfully(t) + + count := 0 + err := projects.ListAvailable(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + + actual, err := projects.ExtractProjects(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ExpectedAvailableProjectsSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + func TestListProjects(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/projects/urls.go b/openstack/identity/v3/projects/urls.go index e26cf3684c..1b1d34f924 100644 --- a/openstack/identity/v3/projects/urls.go +++ b/openstack/identity/v3/projects/urls.go @@ -2,6 +2,10 @@ package projects import "github.com/gophercloud/gophercloud" +func listAvailableURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("auth", "projects") +} + func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("projects") } From 1875fb816c53e921791b5d9560a50b9a49c31b28 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 9 Jan 2021 20:13:14 -0700 Subject: [PATCH 1198/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16b95ab734..a81b526925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ 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) ## 0.15.0 (December 27, 2020) From 9a8ce00a7e26137c79a23904564b1ab7b109b084 Mon Sep 17 00:00:00 2001 From: rendaw Date: Mon, 18 Jan 2021 06:11:07 +0900 Subject: [PATCH 1199/2296] Backups list detail #2083 (#2085) * Add support for backups list-detail operation * Fixes Co-authored-by: rendaw <> --- .../extensions/backups/requests.go | 49 +++++++++++++++++++ .../extensions/backups/testing/fixtures.go | 44 ++++++++++++++++- .../backups/testing/requests_test.go | 45 ++++++++++++++++- .../blockstorage/extensions/backups/urls.go | 4 ++ 4 files changed, 140 insertions(+), 2 deletions(-) diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/extensions/backups/requests.go index 0524a1a73a..4eb123b9ff 100644 --- a/openstack/blockstorage/extensions/backups/requests.go +++ b/openstack/blockstorage/extensions/backups/requests.go @@ -145,6 +145,55 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } +// ListDetailOptsBuilder allows extensions to add additional parameters to the ListDetail +// request. +type ListDetailOptsBuilder interface { + ToBackupListDetailQuery() (string, error) +} + +type ListDetailOpts struct { + // AllTenants will retrieve backups of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` + + // True to include `count` in the API response, supported from version 3.45 + WithCount bool `q:"with_count"` +} + +// ToBackupListDetailQuery formats a ListDetailOpts into a query string. +func (opts ListDetailOpts) ToBackupListDetailQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDetail returns more detailed information about Backups optionally +// limited by the conditions provided in ListDetailOpts. +func ListDetail(client *gophercloud.ServiceClient, opts ListDetailOptsBuilder) pagination.Pager { + url := listDetailURL(client) + if opts != nil { + query, err := opts.ToBackupListDetailQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return BackupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures.go b/openstack/blockstorage/extensions/backups/testing/fixtures.go index cceef70a19..94cd9b52f1 100644 --- a/openstack/blockstorage/extensions/backups/testing/fixtures.go +++ b/openstack/blockstorage/extensions/backups/testing/fixtures.go @@ -13,6 +13,27 @@ import ( ) const ListResponse = ` +{ + "backups": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "backup-001" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "backup-002" + } + ], + "backups_links": [ + { + "href": "%s/backups?marker=1", + "rel": "next" + } + ] +} +` + +const ListDetailResponse = ` { "backups": [ { @@ -36,7 +57,7 @@ const ListResponse = ` ], "backups_links": [ { - "href": "%s/backups?marker=1", + "href": "%s/backups/detail?marker=1", "rel": "next" } ] @@ -180,6 +201,27 @@ func MockListResponse(t *testing.T) { }) } +func MockListDetailResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/detail", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ListDetailResponse, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"backups": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + func MockGetResponse(t *testing.T) { th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") diff --git a/openstack/blockstorage/extensions/backups/testing/requests_test.go b/openstack/blockstorage/extensions/backups/testing/requests_test.go index cd96af74f1..1adc7a4512 100644 --- a/openstack/blockstorage/extensions/backups/testing/requests_test.go +++ b/openstack/blockstorage/extensions/backups/testing/requests_test.go @@ -19,7 +19,47 @@ func TestList(t *testing.T) { count := 0 - backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := backups.ExtractBackups(page) + if err != nil { + t.Errorf("Failed to extract backups: %v", err) + return false, err + } + + expected := []backups.Backup{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "backup-001", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "backup-002", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + if err != nil { + t.Errorf("EachPage returned error: %s", err) + } + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestListDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListDetailResponse(t) + + count := 0 + + err := backups.ListDetail(client.ServiceClient(), &backups.ListDetailOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := backups.ExtractBackups(page) if err != nil { @@ -52,6 +92,9 @@ func TestList(t *testing.T) { return true, nil }) + if err != nil { + t.Errorf("EachPage returned error: %s", err) + } if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/blockstorage/extensions/backups/urls.go b/openstack/blockstorage/extensions/backups/urls.go index 914f98bdad..e727de727d 100644 --- a/openstack/blockstorage/extensions/backups/urls.go +++ b/openstack/blockstorage/extensions/backups/urls.go @@ -18,6 +18,10 @@ func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("backups") } +func listDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups", "detail") +} + func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("backups", id) } From 0e64d5ec02f928a816e967f46a5d71d1637181dd Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 17 Jan 2021 14:11:51 -0700 Subject: [PATCH 1200/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a81b526925..862eeb4f19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ 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) ## 0.15.0 (December 27, 2020) From a07c15c7dfc9c28091c0a9bbc227d711e57223cf Mon Sep 17 00:00:00 2001 From: Christian Schlotter Date: Sun, 17 Jan 2021 22:26:32 +0100 Subject: [PATCH 1201/2296] openstack/networking/v2/extensions/fwaas_v2/groups/requests.go make UpdateOpts.Ports to pointer to allow unsetting it explicitly (#2073) --- .../v2/extensions/fwaas_v2/groups/requests.go | 14 +++++++------- .../fwaas_v2/groups/testing/requests_test.go | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go index fb10d4e8ea..34df7695e3 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go @@ -119,13 +119,13 @@ type UpdateOptsBuilder interface { // UpdateOpts contains the values used when updating a firewall group. type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - IngressFirewallPolicyID *string `json:"ingress_firewall_policy_id,omitempty"` - EgressFirewallPolicyID *string `json:"egress_firewall_policy_id,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - Ports []string `json:"ports,omitempty"` - Shared *bool `json:"shared,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + IngressFirewallPolicyID *string `json:"ingress_firewall_policy_id,omitempty"` + EgressFirewallPolicyID *string `json:"egress_firewall_policy_id,omitempty"` + AdminStateUp *bool `json:"admin_state_up,omitempty"` + Ports *[]string `json:"ports,omitempty"` + Shared *bool `json:"shared,omitempty"` } // ToFirewallGroupUpdateMap casts a CreateOpts struct to a map. 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 9909a77c6c..4ca7b4aad8 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 @@ -284,7 +284,7 @@ func TestUpdate(t *testing.T) { options := groups.UpdateOpts{ Name: &name, Description: &description, - Ports: []string{ + Ports: &[]string{ "a6af1e56-b12b-4733-8f77-49166afd5719", "11a58c87-76be-ae7c-a74e-b77fffb88a32", }, From f1735f5edeb1bdac6722979091d21d81b3f86df4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 17 Jan 2021 14:27:26 -0700 Subject: [PATCH 1202/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 862eeb4f19..ad4fb3bbc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ IMPROVEMENTS * `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] ## 0.15.0 (December 27, 2020) From 5c7edc889fa356f21e50550fa0d6cdadd236c552 Mon Sep 17 00:00:00 2001 From: Tadas Sutkaitis Date: Tue, 19 Jan 2021 06:54:24 +0200 Subject: [PATCH 1203/2296] Glance: hide images from public list (#2094) * glance: hide image from list * add: Hidden to the ListOpts struct --- openstack/imageservice/v2/images/requests.go | 20 +++++++++++++++++++ openstack/imageservice/v2/images/results.go | 3 +++ .../v2/images/testing/fixtures.go | 8 ++++++++ .../v2/images/testing/requests_test.go | 3 +++ 4 files changed, 34 insertions(+) diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index f0cd5cbde8..7947645eb4 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -41,6 +41,9 @@ type ListOpts struct { // Visibility filters on the visibility of the image. Visibility ImageVisibility `q:"visibility"` + // Hidden filters on the hidden status of the image. + Hidden bool `q:"os_hidden"` + // MemberStatus filters on the member status of the image. MemberStatus ImageMemberStatus `q:"member_status"` @@ -155,6 +158,9 @@ type CreateOpts struct { // Visibility defines who can see/use the image. Visibility *ImageVisibility `json:"visibility,omitempty"` + // Hidden is whether the image is listed in default image list or not. + Hidden *bool `json:"os_hidden,omitempty"` + // Tags is a set of image tags. Tags []string `json:"tags,omitempty"` @@ -283,6 +289,20 @@ func (r UpdateVisibility) ToImagePatchMap() map[string]interface{} { } } +// ReplaceImageHidden represents an updated os_hidden property request. +type ReplaceImageHidden struct { + NewHidden bool +} + +// ToImagePatchMap assembles a request body based on ReplaceImageHidden. +func (r ReplaceImageHidden) ToImagePatchMap() map[string]interface{} { + return map[string]interface{}{ + "op": "replace", + "path": "/os_hidden", + "value": r.NewHidden, + } +} + // ReplaceImageName represents an updated image_name property request. type ReplaceImageName struct { NewName string diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index 9bab7f94e7..d723a466a6 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -53,6 +53,9 @@ type Image struct { // Visibility defines who can see/use the image. Visibility ImageVisibility `json:"visibility"` + // Hidden is whether the image is listed in default image list or not. + Hidden bool `json:"os_hidden"` + // Checksum is the checksum of the data that's associated with the image. Checksum string `json:"checksum"` diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index 0311699b11..5ae66d5e05 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -267,6 +267,7 @@ func HandleImageGetSuccessfully(t *testing.T) { "disk_format": "qcow2", "updated_at": "2014-05-05T17:15:11Z", "visibility": "public", + "os_hidden": false, "self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", "min_disk": 0, "protected": false, @@ -325,6 +326,11 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "path": "/min_ram", "value": 1024 }, + { + "op": "replace", + "path": "/os_hidden", + "value": false + }, { "op": "add", "path": "/empty_value", @@ -341,6 +347,7 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "name": "Fedora 17", "status": "active", "visibility": "public", + "os_hidden": false, "size": 2254249, "checksum": "2cec138d7dae2aa59038ef8c9aec2390", "tags": [ @@ -387,6 +394,7 @@ func HandleImageListByTagsSuccessfully(t *testing.T) { "disk_format": "qcow2", "updated_at": "2014-05-05T17:15:11Z", "visibility": "public", + "os_hidden": false, "self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27", "min_disk": 0, "protected": false, diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index befe7e9559..c4b97677ec 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -225,6 +225,7 @@ func TestGetImage(t *testing.T) { Protected: false, Visibility: images.ImageVisibilityPublic, + Hidden: false, Checksum: checksum, SizeBytes: sizeBytes, @@ -264,6 +265,7 @@ func TestUpdateImage(t *testing.T) { images.ReplaceImageTags{NewTags: []string{"fedora", "beefy"}}, images.ReplaceImageMinDisk{NewMinDisk: 21}, images.ReplaceImageMinRam{NewMinRam: 1024}, + images.ReplaceImageHidden{NewHidden: false}, images.UpdateImageProperty{ Op: images.AddOp, Name: "empty_value", @@ -285,6 +287,7 @@ func TestUpdateImage(t *testing.T) { Name: "Fedora 17", Status: images.ImageStatusActive, Visibility: images.ImageVisibilityPublic, + Hidden: false, SizeBytes: sizebytes, Checksum: checksum, From bb95efe2e1e7225d263125171bed6f10770605ea Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 18 Jan 2021 21:56:17 -0700 Subject: [PATCH 1204/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4fb3bbc1..c573b18e04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ IMPROVEMENTS * 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) ## 0.15.0 (December 27, 2020) From 5f502c57f05aa636535df682b5e9bcd5d1187ac3 Mon Sep 17 00:00:00 2001 From: Simon Merrick Date: Sun, 24 Jan 2021 07:35:05 +1300 Subject: [PATCH 1205/2296] Add support for master_lb_enabled field for clusters (#2102) --- openstack/containerinfra/v1/clusters/doc.go | 4 +++- openstack/containerinfra/v1/clusters/requests.go | 1 + openstack/containerinfra/v1/clusters/testing/requests_test.go | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index f0d41f0760..334afd9db3 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -6,7 +6,8 @@ Example to Create a Cluster masterCount := 1 nodeCount := 1 createTimeout := 30 - opts := clusters.CreateOpts{ + masterLBEnabled := true + createOpts := clusters.CreateOpts{ ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", CreateTimeout: &createTimeout, DiscoveryURL: "", @@ -17,6 +18,7 @@ Example to Create a Cluster MasterFlavorID: "m1.small", Name: "k8s", NodeCount: &nodeCount, + MasterLBEnabled: &masterLBEnabled, } cluster, err := clusters.Create(serviceClient, createOpts).Extract() diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 58524586f0..bd62a85b95 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -24,6 +24,7 @@ type CreateOpts struct { Name string `json:"name"` NodeCount *int `json:"node_count,omitempty"` FloatingIPEnabled *bool `json:"floating_ip_enabled,omitempty"` + MasterLBEnabled *bool `json:"master_lb_enabled,omitempty"` FixedNetwork string `json:"fixed_network,omitempty"` FixedSubnet string `json:"fixed_subnet,omitempty"` MergeLabels *bool `json:"merge_labels,omitempty"` diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index b31d1f9252..680ea550cc 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -19,6 +19,7 @@ func TestCreateCluster(t *testing.T) { masterCount := 1 nodeCount := 1 createTimeout := 30 + masterLBEnabled := true opts := clusters.CreateOpts{ ClusterTemplateID: "0562d357-8641-4759-8fed-8173f02c9633", CreateTimeout: &createTimeout, @@ -28,6 +29,7 @@ func TestCreateCluster(t *testing.T) { Labels: map[string]string{}, MasterCount: &masterCount, MasterFlavorID: "m1.small", + MasterLBEnabled: &masterLBEnabled, Name: "k8s", NodeCount: &nodeCount, FloatingIPEnabled: gophercloud.Enabled, From 2329b0f40ad9d3bff2d3b4493fa534b32734128d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Jan 2021 11:36:05 -0700 Subject: [PATCH 1206/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c573b18e04..2611ec6eeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ IMPROVEMENTS * 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) ## 0.15.0 (December 27, 2020) From 3f19ae7638410d5d9823a29834ca86aabae84275 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 23 Jan 2021 19:58:42 +0100 Subject: [PATCH 1207/2296] Client: introduce the retry backoff (#2097) * Client: introduce the retry backoff support * Bump go version to 1.15 in openlab tests --- .../run.yaml | 3 +- .../gophercloud-acceptance-test/run.yaml | 3 +- .zuul/playbooks/gophercloud-unittest/run.yaml | 3 +- doc.go | 38 ++++ provider_client.go | 31 ++- testing/provider_client_test.go | 185 ++++++++++++++++++ 6 files changed, 259 insertions(+), 4 deletions(-) diff --git a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml index 03210cca84..b6fcfc32c6 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml @@ -1,7 +1,8 @@ - hosts: all become: yes roles: - - config-golang + - role: config-golang + go_version: '1.15' - clone-devstack-gate-to-workspace - role: create-devstack-local-conf enable_services: diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index ac870d0a42..3626dba47e 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -1,7 +1,8 @@ - hosts: all become: yes roles: - - config-golang + - role: config-golang + go_version: '1.15' - clone-devstack-gate-to-workspace - role: create-devstack-local-conf enable_services: diff --git a/.zuul/playbooks/gophercloud-unittest/run.yaml b/.zuul/playbooks/gophercloud-unittest/run.yaml index feefa54f2e..6303b3bed8 100644 --- a/.zuul/playbooks/gophercloud-unittest/run.yaml +++ b/.zuul/playbooks/gophercloud-unittest/run.yaml @@ -1,7 +1,8 @@ - hosts: all become: yes roles: - - config-golang + - role: config-golang + go_version: '1.15' tasks: - name: Run unit tests with gophercloud shell: diff --git a/doc.go b/doc.go index 953ca822a9..83aefc9015 100644 --- a/doc.go +++ b/doc.go @@ -106,5 +106,43 @@ intermediary processing on each page, you can use the AllPages method: This top-level package contains utility functions and data types that are used throughout the provider and service packages. Of particular note for end users are the AuthOptions and EndpointOpts structs. + +An example retry backoff function, which respects the 429 HTTP response code and a "Retry-After" header: + + endpoint := "http://localhost:5000" + provider, err := openstack.NewClient(endpoint) + if err != nil { + panic(err) + } + provider.MaxBackoffRetries = 3 // max three retries + provider.RetryBackoffFunc = func(ctx context.Context, respErr *ErrUnexpectedResponseCode, e error, retries uint) error { + retryAfter := respErr.ResponseHeader.Get("Retry-After") + if retryAfter == "" { + return e + } + + var sleep time.Duration + + // Parse delay seconds or HTTP date + if v, err := strconv.ParseUint(retryAfter, 10, 32); err == nil { + sleep = time.Duration(v) * time.Second + } else if v, err := time.Parse(http.TimeFormat, retryAfter); err != nil { + return e + } else { + sleep = v.UTC().Sub(time.Now().UTC()) + } + + if ctx != nil { + select { + case <-time.After(sleep): + case <-ctx.Done(): + } + } else { + time.Sleep(sleep) + } + + return nil + } + */ package gophercloud diff --git a/provider_client.go b/provider_client.go index 53b3ecf27f..a943a81ce0 100644 --- a/provider_client.go +++ b/provider_client.go @@ -13,7 +13,10 @@ import ( ) // DefaultUserAgent is the default User-Agent string set in the request header. -const DefaultUserAgent = "gophercloud/2.0.0" +const ( + DefaultUserAgent = "gophercloud/2.0.0" + DefaultMaxBackoffRetries = 60 +) // UserAgent represents a User-Agent header. type UserAgent struct { @@ -80,6 +83,12 @@ type ProviderClient struct { // Context is the context passed to the HTTP request. Context context.Context + // Retry backoff func + RetryBackoffFunc func(context.Context, *ErrUnexpectedResponseCode, error, uint) error + + // MaxBackoffRetries set the maximum number of backoffs. When not set, defaults to DefaultMaxBackoffRetries + MaxBackoffRetries uint + // mut is a mutex for the client. It protects read and write access to client attributes such as getting // and setting the TokenID. mut *sync.RWMutex @@ -323,6 +332,8 @@ type requestState struct { // reauthenticate, but keep getting 401 responses with the fresh token, reauthenticating some more // will just get us into an infinite loop. hasReauthenticated bool + // Retry-After backoff counter, increments during each backoff call + retries uint } var applicationJSON = "application/json" @@ -503,6 +514,24 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if error429er, ok := errType.(Err429er); ok { err = error429er.Error429(respErr) } + + maxTries := client.MaxBackoffRetries + if maxTries == 0 { + maxTries = DefaultMaxBackoffRetries + } + + if f := client.RetryBackoffFunc; f != nil && state.retries < maxTries { + var e error + + state.retries = state.retries + 1 + e = f(client.Context, &respErr, err, state.retries) + + if e != nil { + return resp, e + } + + return client.doRequest(method, url, options, state) + } case http.StatusInternalServerError: err = ErrDefault500{respErr} if error500er, ok := errType.(Err500er); ok { diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 0f11295bec..b71da19d53 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "net/http/httptest" + "strconv" "strings" "sync" "sync/atomic" @@ -448,3 +449,187 @@ func TestRequestConnectionClose(t *testing.T) { th.AssertEquals(t, int64(iter), connections) } + +type testRetryFunc func(context.Context, *gophercloud.ErrUnexpectedResponseCode, error, uint) error + +func retryTest(retryCounter *uint, t *testing.T) testRetryFunc { + return func(ctx context.Context, respErr *gophercloud.ErrUnexpectedResponseCode, e error, retries uint) error { + retryAfter := respErr.ResponseHeader.Get("Retry-After") + if retryAfter == "" { + return e + } + + var sleep time.Duration + + // Parse delay seconds or HTTP date + if v, err := strconv.ParseUint(retryAfter, 10, 32); err == nil { + sleep = time.Duration(v) * time.Second + } else if v, err := time.Parse(http.TimeFormat, retryAfter); err != nil { + return e + } else { + sleep = v.UTC().Sub(time.Now().UTC()) + } + + if ctx != nil { + t.Logf("Context sleeping for %d milliseconds", sleep.Milliseconds()) + select { + case <-time.After(sleep): + t.Log("sleep is over") + case <-ctx.Done(): + t.Log("context exceeded") + return e + } + } else { + t.Logf("Sleeping for %d milliseconds", sleep.Milliseconds()) + time.Sleep(sleep) + t.Log("sleep is over") + } + + *retryCounter = *retryCounter + 1 + + return nil + } +} + +func TestRequestRetry(t *testing.T) { + var retryCounter uint + + p := &gophercloud.ProviderClient{} + p.UseTokenLock() + p.SetToken(client.TokenID) + p.MaxBackoffRetries = 3 + + p.RetryBackoffFunc = retryTest(&retryCounter, t) + + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Retry-After", "1") + + //always reply 429 + http.Error(w, "retry later", http.StatusTooManyRequests) + }) + + _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + if err == nil { + t.Fatal("expecting error, got nil") + } + th.AssertEquals(t, retryCounter, p.MaxBackoffRetries) +} + +func TestRequestRetryHTTPDate(t *testing.T) { + var retryCounter uint + + p := &gophercloud.ProviderClient{} + p.UseTokenLock() + p.SetToken(client.TokenID) + p.MaxBackoffRetries = 3 + + p.RetryBackoffFunc = retryTest(&retryCounter, t) + + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Retry-After", time.Now().Add(1*time.Second).UTC().Format(http.TimeFormat)) + + //always reply 429 + http.Error(w, "retry later", http.StatusTooManyRequests) + }) + + _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + if err == nil { + t.Fatal("expecting error, got nil") + } + th.AssertEquals(t, retryCounter, p.MaxBackoffRetries) +} + +func TestRequestRetryError(t *testing.T) { + var retryCounter uint + + p := &gophercloud.ProviderClient{} + p.UseTokenLock() + p.SetToken(client.TokenID) + p.MaxBackoffRetries = 3 + + p.RetryBackoffFunc = retryTest(&retryCounter, t) + + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Retry-After", "foo bar") + + //always reply 429 + http.Error(w, "retry later", http.StatusTooManyRequests) + }) + + _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + if err == nil { + t.Fatal("expecting error, got nil") + } + th.AssertEquals(t, retryCounter, uint(0)) +} + +func TestRequestRetrySuccess(t *testing.T) { + var retryCounter uint + + p := &gophercloud.ProviderClient{} + p.UseTokenLock() + p.SetToken(client.TokenID) + p.MaxBackoffRetries = 3 + + p.RetryBackoffFunc = retryTest(&retryCounter, t) + + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + //always reply 200 + http.Error(w, "retry later", http.StatusOK) + }) + + _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + if err != nil { + t.Fatal(err) + } + th.AssertEquals(t, retryCounter, uint(0)) +} + +func TestRequestRetryContext(t *testing.T) { + var retryCounter uint + + ctx, cancel := context.WithCancel(context.Background()) + go func() { + sleep := 2.5 * 1000 * time.Millisecond + time.Sleep(sleep) + cancel() + }() + + p := &gophercloud.ProviderClient{ + Context: ctx, + } + p.UseTokenLock() + p.SetToken(client.TokenID) + p.MaxBackoffRetries = 3 + + p.RetryBackoffFunc = retryTest(&retryCounter, t) + + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Retry-After", "1") + + //always reply 429 + http.Error(w, "retry later", http.StatusTooManyRequests) + }) + + _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + if err == nil { + t.Fatal("expecting error, got nil") + } + t.Logf("retryCounter: %d, p.MaxBackoffRetries: %d", retryCounter, p.MaxBackoffRetries-1) + th.AssertEquals(t, retryCounter, p.MaxBackoffRetries-1) +} From 7d4ec67e8c6c0b4b608026d33a74a5433185e458 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Jan 2021 12:00:20 -0700 Subject: [PATCH 1208/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2611ec6eeb..b024af632f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ IMPROVEMENTS * 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) ## 0.15.0 (December 27, 2020) From 1e2dcb16f8996d4f40d60ef01c65304527e229b0 Mon Sep 17 00:00:00 2001 From: ooneko Date: Fri, 29 Jan 2021 12:49:22 +0800 Subject: [PATCH 1209/2296] Add RaidLevel "JBOD" (#2103) --- 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 7cb86ba5ff..9439638e5c 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -521,6 +521,7 @@ const ( RAID10 RAIDLevel = "1+0" RAID50 RAIDLevel = "5+0" RAID60 RAIDLevel = "6+0" + JBOD RAIDLevel = "JBOD" ) // DiskType is used to specify the disk type for a logical disk, e.g. hdd or ssd. From 8e76a0a9f51efdafb403a7158803a98a18392d97 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 28 Jan 2021 21:50:39 -0700 Subject: [PATCH 1210/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b024af632f..2cd1ada97c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ IMPROVEMENTS * 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) ## 0.15.0 (December 27, 2020) From 43a99c44178410e14f40eb87df40b0a4d74e5c0c Mon Sep 17 00:00:00 2001 From: toliu Date: Tue, 2 Feb 2021 04:22:04 +0800 Subject: [PATCH 1211/2296] support modified-since for cache (#2108) --- .../objectstorage/v1/objects/requests.go | 6 +++++ .../v1/objects/testing/fixtures.go | 21 +++++++++++++++++- .../v1/objects/testing/requests_test.go | 22 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 24ab79e991..7d6eb4123a 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -106,6 +106,12 @@ func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, er if err != nil { return nil, q.String(), err } + if !opts.IfModifiedSince.IsZero() { + h["If-Modified-Since"] = opts.IfModifiedSince.Format(time.RFC1123) + } + if !opts.IfUnmodifiedSince.IsZero() { + h["If-Unmodified-Since"] = opts.IfUnmodifiedSince.Format(time.RFC1123) + } return h, q.String(), nil } diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index 9ca126a52b..a1dc81263d 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -17,11 +17,30 @@ import ( // responds with a `Download` response. func HandleDownloadObjectSuccessfully(t *testing.T) { th.Mux.HandleFunc("/testContainer/testObject", 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) th.TestHeader(t, r, "Accept", "application/json") - w.Header().Set("Date", "Wed, 10 Nov 2009 23:00:00 UTC") + w.Header().Set("Date", date.Format(time.RFC1123)) w.Header().Set("X-Static-Large-Object", "True") + + unModifiedSince := r.Header.Get("If-Unmodified-Since") + modifiedSince := r.Header.Get("If-Modified-Since") + if unModifiedSince != "" { + ums, _ := time.Parse(time.RFC1123, unModifiedSince) + if ums.Before(date) || ums.Equal(date) { + w.WriteHeader(http.StatusPreconditionFailed) + return + } + } + if modifiedSince != "" { + ms, _ := time.Parse(time.RFC1123, modifiedSince) + if ms.After(date) { + w.WriteHeader(http.StatusNotModified) + return + } + } + w.Header().Set("Last-Modified", date.Format(time.RFC1123)) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "Successful download with Gophercloud") }) diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index dadc6bf648..4495217849 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -50,12 +50,34 @@ func TestDownloadExtraction(t *testing.T) { ContentType: "text/plain; charset=utf-8", Date: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), StaticLargeObject: true, + LastModified: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), } actual, err := response.Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } +func TestDownloadWithLastModified(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDownloadObjectSuccessfully(t) + + options1 := &objects.DownloadOpts{ + IfUnmodifiedSince: time.Date(2009, time.November, 10, 22, 59, 59, 0, time.UTC), + } + response1 := objects.Download(fake.ServiceClient(), "testContainer", "testObject", options1) + _, err1 := response1.Extract() + th.AssertErr(t, err1) + + options2 := &objects.DownloadOpts{ + IfModifiedSince: time.Date(2009, time.November, 10, 23, 0, 1, 0, time.UTC), + } + response2 := objects.Download(fake.ServiceClient(), "testContainer", "testObject", options2) + content, err2 := response2.ExtractContent() + th.AssertNoErr(t, err2) + th.AssertEquals(t, len(content), 0) +} + func TestListObjectInfo(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From fde256284c2e108403a52e9001ab021893fd614b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 1 Feb 2021 13:24:22 -0700 Subject: [PATCH 1212/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd1ada97c..11a56490cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ 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 From 633d73521055b7b9dfa92da51e6982c398683d58 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 2 Feb 2021 04:52:23 +0100 Subject: [PATCH 1213/2296] Core: tiny fixes for retry logic (#2106) * Core: tiny fixes for retry logic * Change the if condition order --- doc.go | 7 ++++--- provider_client.go | 6 ++++-- testing/provider_client_test.go | 12 +++++------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/doc.go b/doc.go index 83aefc9015..e2623b44e2 100644 --- a/doc.go +++ b/doc.go @@ -126,16 +126,17 @@ An example retry backoff function, which respects the 429 HTTP response code and // Parse delay seconds or HTTP date if v, err := strconv.ParseUint(retryAfter, 10, 32); err == nil { sleep = time.Duration(v) * time.Second - } else if v, err := time.Parse(http.TimeFormat, retryAfter); err != nil { - return e + } else if v, err := time.Parse(http.TimeFormat, retryAfter); err == nil { + sleep = time.Until(v) } else { - sleep = v.UTC().Sub(time.Now().UTC()) + return e } if ctx != nil { select { case <-time.After(sleep): case <-ctx.Done(): + return e } } else { time.Sleep(sleep) diff --git a/provider_client.go b/provider_client.go index a943a81ce0..f56c1375fe 100644 --- a/provider_client.go +++ b/provider_client.go @@ -25,6 +25,8 @@ type UserAgent struct { prepend []string } +type RetryFunc func(context.Context, *ErrUnexpectedResponseCode, error, uint) error + // Prepend prepends a user-defined string to the default User-Agent string. Users // may pass in one or more strings to prepend. func (ua *UserAgent) Prepend(s ...string) { @@ -84,7 +86,7 @@ type ProviderClient struct { Context context.Context // Retry backoff func - RetryBackoffFunc func(context.Context, *ErrUnexpectedResponseCode, error, uint) error + RetryBackoffFunc RetryFunc // MaxBackoffRetries set the maximum number of backoffs. When not set, defaults to DefaultMaxBackoffRetries MaxBackoffRetries uint @@ -509,7 +511,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if error409er, ok := errType.(Err409er); ok { err = error409er.Error409(respErr) } - case 429: + case http.StatusTooManyRequests, 498: err = ErrDefault429{respErr} if error429er, ok := errType.(Err429er); ok { err = error429er.Error429(respErr) diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index b71da19d53..a10f8348f3 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -450,9 +450,7 @@ func TestRequestConnectionClose(t *testing.T) { th.AssertEquals(t, int64(iter), connections) } -type testRetryFunc func(context.Context, *gophercloud.ErrUnexpectedResponseCode, error, uint) error - -func retryTest(retryCounter *uint, t *testing.T) testRetryFunc { +func retryTest(retryCounter *uint, t *testing.T) gophercloud.RetryFunc { return func(ctx context.Context, respErr *gophercloud.ErrUnexpectedResponseCode, e error, retries uint) error { retryAfter := respErr.ResponseHeader.Get("Retry-After") if retryAfter == "" { @@ -464,10 +462,10 @@ func retryTest(retryCounter *uint, t *testing.T) testRetryFunc { // Parse delay seconds or HTTP date if v, err := strconv.ParseUint(retryAfter, 10, 32); err == nil { sleep = time.Duration(v) * time.Second - } else if v, err := time.Parse(http.TimeFormat, retryAfter); err != nil { - return e + } else if v, err := time.Parse(http.TimeFormat, retryAfter); err == nil { + sleep = time.Until(v) } else { - sleep = v.UTC().Sub(time.Now().UTC()) + return e } if ctx != nil { @@ -476,7 +474,7 @@ func retryTest(retryCounter *uint, t *testing.T) testRetryFunc { case <-time.After(sleep): t.Log("sleep is over") case <-ctx.Done(): - t.Log("context exceeded") + t.Log(ctx.Err()) return e } } else { From 281f27b22d650a9971be1e7c18b1eb918e4735db Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Fri, 5 Feb 2021 23:00:07 +0100 Subject: [PATCH 1214/2296] Add support for blockstorage volume_type quota (#2109) --- .../blockstorage/v3/quotaset_test.go | 43 ++++++++++++++++++- .../blockstorage/extensions/quotasets/doc.go | 19 ++++++++ .../extensions/quotasets/requests.go | 21 ++++++++- .../extensions/quotasets/results.go | 32 ++++++++++++++ .../extensions/quotasets/testing/fixtures.go | 9 +++- 5 files changed, 120 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/quotaset_test.go b/acceptance/openstack/blockstorage/v3/quotaset_test.go index fe076c7ee7..46b30b2671 100644 --- a/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/acceptance/clients" "github.com/gophercloud/gophercloud/acceptance/tools" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -57,6 +58,9 @@ var UpdateQuotaOpts = quotasets.UpdateOpts{ Backups: gophercloud.IntToPointer(2), BackupGigabytes: gophercloud.IntToPointer(300), Groups: gophercloud.IntToPointer(350), + Extra: map[string]interface{}{ + "volumes_foo": gophercloud.IntToPointer(100), + }, } var UpdatedQuotas = quotasets.QuotaSet{ @@ -69,6 +73,14 @@ var UpdatedQuotas = quotasets.QuotaSet{ Groups: 350, } +var VolumeTypeIsPublic = true +var VolumeTypeCreateOpts = volumetypes.CreateOpts{ + Name: "foo", + IsPublic: &VolumeTypeIsPublic, + Description: "foo", + ExtraSpecs: map[string]string{}, +} + func TestQuotasetUpdate(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") @@ -81,24 +93,53 @@ func TestQuotasetUpdate(t *testing.T) { orig, err := quotasets.Get(client, projectID).Extract() th.AssertNoErr(t, err) + // create volumeType to test volume type quota + volumeType, err := volumetypes.Create(client, VolumeTypeCreateOpts).Extract() + th.AssertNoErr(t, err) + defer func() { restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) + err := volumetypes.Delete(client, volumeType.ID).ExtractErr() + th.AssertNoErr(t, err) + _, err = quotasets.Update(client, projectID, restore).Extract() th.AssertNoErr(t, err) + }() // test Update resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, UpdatedQuotas, *resultQuotas) // We dont know the default quotas, so just check if the quotas are not the // same as before newQuotas, err := quotasets.Get(client, projectID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) + th.AssertEquals(t, resultQuotas.Extra["volumes_foo"], newQuotas.Extra["volumes_foo"]) + + // 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 { + tools.PrintResource(t, k) + switch k { + case + "volumes_foo", + "snapshots_foo", + "gigabytes_foo": + count += 1 + } + } + + th.AssertEquals(t, count, 3) + + // unpopulate resultQuotas.Extra as it is different per cloud and test + // rest of the quotaSet + resultQuotas.Extra = map[string]interface{}(nil) + th.AssertDeepEquals(t, UpdatedQuotas, *resultQuotas) } func TestQuotasetDelete(t *testing.T) { diff --git a/openstack/blockstorage/extensions/quotasets/doc.go b/openstack/blockstorage/extensions/quotasets/doc.go index 109f78f506..0f9c9d4831 100644 --- a/openstack/blockstorage/extensions/quotasets/doc.go +++ b/openstack/blockstorage/extensions/quotasets/doc.go @@ -32,6 +32,25 @@ Example to Update a Quota Set fmt.Printf("%+v\n", quotaset) +Example to Update a Quota set with volume_type quotas + + updateOpts := quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(100), + Extra: map[string]interface{}{ + "gigabytes_foo": gophercloud.IntToPointer(100), + "snapshots_foo": gophercloud.IntToPointer(10), + "volumes_foo": gophercloud.IntToPointer(10), + }, + } + + quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + + Example to Delete a Quota Set err := quotasets.Delete(blockStorageClient, "project-id").ExtractErr() diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go index 50cd4fbaf1..5cf28aee9e 100644 --- a/openstack/blockstorage/extensions/quotasets/requests.go +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -43,7 +43,7 @@ func Update(client *gophercloud.ServiceClient, projectID string, opts UpdateOpts return } -// UpdateOptsBuilder enables extensins to add parameters to the update request. +// UpdateOptsBuilder enables extensions to add parameters to the update request. type UpdateOptsBuilder interface { // Extra specific name to prevent collisions with interfaces for other quotas // (e.g. neutron) @@ -53,7 +53,20 @@ type UpdateOptsBuilder interface { // ToBlockStorageQuotaUpdateMap builds the update options into a serializable // format. func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "quota_set") + b, err := gophercloud.BuildRequestBody(opts, "quota_set") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["quota_set"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil } // Options for Updating the quotas of a Tenant. @@ -87,6 +100,10 @@ type UpdateOpts struct { // Force will update the quotaset even if the quota has already been used // and the reserved quota exceeds the new quota. Force bool `json:"force,omitempty"` + + // Extra is a collection of miscellaneous key/values used to set + // quota per volume_type + Extra map[string]interface{} `json:"-"` } // Resets the quotas for the given tenant to their default values. diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/extensions/quotasets/results.go index e29569e8b4..bc516be5da 100644 --- a/openstack/blockstorage/extensions/quotasets/results.go +++ b/openstack/blockstorage/extensions/quotasets/results.go @@ -1,6 +1,8 @@ package quotasets import ( + "encoding/json" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -34,6 +36,36 @@ type QuotaSet struct { // Groups is the number of groups that are allowed for each project. Groups int `json:"groups,omitempty"` + + // Extra is a collection of miscellaneous key/values used to set + // quota per volume_type + Extra map[string]interface{} `json:"-"` +} + +// UnmarshalJSON is used on QuotaSet to unmarshal extra keys that are +// used for volume_type quota +func (r *QuotaSet) UnmarshalJSON(b []byte) error { + type tmp QuotaSet + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = QuotaSet(s.tmp) + + var result interface{} + err = json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = gophercloud.RemainingKeys(QuotaSet{}, resultMap) + } + + return err } // QuotaUsageSet represents details of both operational limits of block diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go index 38b413e71a..8c9f9318fe 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/fixtures.go +++ b/openstack/blockstorage/extensions/quotasets/testing/fixtures.go @@ -34,6 +34,7 @@ var getExpectedQuotaSet = quotasets.QuotaSet{ Backups: 12, BackupGigabytes: 13, Groups: 14, + Extra: make(map[string]interface{}), } var getUsageExpectedJSONBody = ` @@ -110,6 +111,7 @@ var fullUpdateOpts = quotasets.UpdateOpts{ Backups: gophercloud.IntToPointer(12), BackupGigabytes: gophercloud.IntToPointer(13), Groups: gophercloud.IntToPointer(14), + Extra: make(map[string]interface{}), } var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ @@ -120,6 +122,7 @@ var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ Backups: 12, BackupGigabytes: 13, Groups: 14, + Extra: make(map[string]interface{}), } var partialUpdateExpectedJSONBody = ` @@ -141,9 +144,13 @@ var partialUpdateOpts = quotasets.UpdateOpts{ PerVolumeGigabytes: gophercloud.IntToPointer(0), Backups: gophercloud.IntToPointer(0), BackupGigabytes: gophercloud.IntToPointer(0), + Extra: make(map[string]interface{}), } -var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{Volumes: 200} +var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 200, + Extra: make(map[string]interface{}), +} // HandleSuccessfulRequest configures the test server to respond to an HTTP request. func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { From 18b16b34db5c134749338cfb8d534548830d2630 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 5 Feb 2021 15:01:51 -0700 Subject: [PATCH 1215/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a56490cf..dc9f35de7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ IMPROVEMENTS * 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) ## 0.15.0 (December 27, 2020) From 632e0527535b3675a9016d8b4bc473d7e2553cc2 Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Sat, 13 Feb 2021 22:57:32 +0100 Subject: [PATCH 1216/2296] Add support for os-retype volumeaction (#2113) --- .../blockstorage/extensions/extensions.go | 22 +++++ .../extensions/volumeactions_test.go | 31 +++++++ .../openstack/blockstorage/v3/blockstorage.go | 80 +++++++++++++++++++ .../extensions/volumeactions/doc.go | 12 +++ .../extensions/volumeactions/requests.go | 50 +++++++++++- .../extensions/volumeactions/results.go | 5 ++ .../volumeactions/testing/fixtures.go | 24 ++++++ .../volumeactions/testing/requests_test.go | 15 ++++ 8 files changed, 238 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index cd77d6954a..d9713bf9c1 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -14,6 +14,7 @@ import ( "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" v3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" "github.com/gophercloud/gophercloud/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/testhelper" @@ -291,3 +292,24 @@ func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volume return nil } + +// ChangeVolumeType will extend the size of a volume. +func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume, vt *volumetypes.VolumeType) error { + t.Logf("Attempting to change the type of volume %s from %s to %s", volume.ID, volume.VolumeType, vt.Name) + + changeOpts := volumeactions.ChangeTypeOpts{ + NewType: vt.Name, + MigrationPolicy: volumeactions.MigrationPolicyOnDemand, + } + + err := volumeactions.ChangeType(client, volume.ID, changeOpts).ExtractErr() + if err != nil { + return err + } + + if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + return err + } + + return nil +} diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index adba0748c8..9844410f2f 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -7,6 +7,7 @@ import ( "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/openstack/blockstorage/v2/volumes" @@ -113,6 +114,36 @@ func TestVolumeActionsSetBootable(t *testing.T) { th.AssertNoErr(t, err) } +func TestVolumeActionsChangeType(t *testing.T) { + // clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volumeType1, err := blockstorageV3.CreateVolumeTypeNoExtraSpecs(t, client) + th.AssertNoErr(t, err) + defer blockstorageV3.DeleteVolumeType(t, client, volumeType1) + + volumeType2, err := blockstorageV3.CreateVolumeTypeNoExtraSpecs(t, client) + th.AssertNoErr(t, err) + defer blockstorageV3.DeleteVolumeType(t, client, volumeType2) + + volume, err := blockstorageV3.CreateVolumeWithType(t, client, volumeType1) + th.AssertNoErr(t, err) + defer blockstorageV3.DeleteVolume(t, client, volume) + + tools.PrintResource(t, volume) + + err = ChangeVolumeType(t, client, volume, volumeType2) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(client, volume.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newVolume.VolumeType, volumeType2.Name) + + tools.PrintResource(t, newVolume) +} + // Note(jtopjian): I plan to work on this at some point, but it requires // setting up a server with iscsi utils. /* diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index b574dd3c51..3394cee7f6 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -79,6 +79,42 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, nil } +// CreateVolumeWithType will create a volume of the given volume type +// with a random name and size of 1GB. An error will be returned if +// the volume was unable to be created. +func CreateVolumeWithType(t *testing.T, client *gophercloud.ServiceClient, vt *volumetypes.VolumeType) (*volumes.Volume, error) { + volumeName := tools.RandomString("ACPTTEST", 16) + volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) + t.Logf("Attempting to create volume: %s", volumeName) + + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + Description: volumeDescription, + VolumeType: vt.Name, + } + + volume, err := volumes.Create(client, createOpts).Extract() + if err != nil { + return volume, err + } + + err = volumes.WaitForStatus(client, volume.ID, "available", 60) + if err != nil { + return volume, err + } + + tools.PrintResource(t, volume) + th.AssertEquals(t, volume.Name, volumeName) + th.AssertEquals(t, volume.Description, volumeDescription) + th.AssertEquals(t, volume.Size, 1) + th.AssertEquals(t, volume.VolumeType, vt.Name) + + t.Logf("Successfully created volume: %s", volume.ID) + + return volume, nil +} + // CreateVolumeType will create a volume type with a random name. An // error will be returned if the volume was unable to be created. func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) { @@ -110,6 +146,36 @@ func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumet return vt, nil } +// CreateVolumeTypeNoExtraSpecs will create a volume type with a random name and +// no extra specs. This is required to bypass cinder-scheduler filters and be able +// to create a volume with this volumeType. An error will be returned if the volume +// type was unable to be created. +func CreateVolumeTypeNoExtraSpecs(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{}, + 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) + + t.Logf("Successfully created volume type: %s", vt.ID) + + return vt, nil +} + // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { @@ -145,6 +211,20 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } + // VolumeTypes can't be deleted until their volumes have been, + // so block until the volume is deleted. + err = tools.WaitFor(func() (bool, error) { + _, err := volumes.Get(client, volume.ID).Extract() + if err != nil { + return true, nil + } + + return false, nil + }) + if err != nil { + t.Fatalf("Error waiting for volume to delete: %v", err) + } + t.Logf("Successfully deleted volume: %s", volume.ID) } diff --git a/openstack/blockstorage/extensions/volumeactions/doc.go b/openstack/blockstorage/extensions/volumeactions/doc.go index 0f2dd2cb26..69d803d05c 100644 --- a/openstack/blockstorage/extensions/volumeactions/doc.go +++ b/openstack/blockstorage/extensions/volumeactions/doc.go @@ -93,5 +93,17 @@ Example of Setting a Volume's Bootable status if err != nil { panic(err) } + +Example of Changing Type of a Volume + + changeTypeOpts := volumeactions.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: volumeactions.MigrationPolicyOnDemand, + } + + err = volumeactions.ChangeType(client, volumeID, changeTypeOpts).ExtractErr() + if err != nil { + panic(err) + } */ package volumeactions diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index d84828327e..1c33c1785e 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -54,7 +54,7 @@ func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder return } -// BeginDetach will mark the volume as detaching. +// BeginDetaching will mark the volume as detaching. func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ @@ -343,3 +343,51 @@ func SetBootable(client *gophercloud.ServiceClient, id string, opts BootableOpts _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// MigrationPolicy type represents a migration_policy when changing types. +type MigrationPolicy string + +// Supported attributes for MigrationPolicy attribute for changeType operations. +const ( + MigrationPolicyNever MigrationPolicy = "never" + MigrationPolicyOnDemand MigrationPolicy = "on-demand" +) + +// ChangeTypeOptsBuilder allows extensions to add additional parameters to the +// ChangeType request. +type ChangeTypeOptsBuilder interface { + ToVolumeChangeTypeMap() (map[string]interface{}, error) +} + +// ChangeTypeOpts contains options for changing the type of an existing Volume. +// This object is passed to the volumes.ChangeType function. +type ChangeTypeOpts struct { + // NewType is the name of the new volume type of the volume. + NewType string `json:"new_type" required:"true"` + + // MigrationPolicy specifies if the volume should be migrated when it is + // re-typed. Possible values are "on-demand" or "never". If not specified, + // the default is "never". + MigrationPolicy MigrationPolicy `json:"migration_policy,omitempty"` +} + +// ToVolumeChangeTypeMap assembles a request body based on the contents of an +// ChangeTypeOpts. +func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-retype") +} + +// ChangeType will change the volume type of the volume based on the provided information. +// This operation does not return a response body. +func ChangeType(client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { + b, err := opts.ToVolumeChangeTypeMap() + 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 691ecc8950..c4bd91a7ff 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -209,3 +209,8 @@ func (r UploadImageResult) Extract() (VolumeImage, error) { type ForceDeleteResult struct { gophercloud.ErrResult } + +// ChangeTypeResult contains the response body and error from an ChangeType request. +type ChangeTypeResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go index 4564e71812..0ec9105251 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures.go @@ -326,3 +326,27 @@ func MockSetBootableResponse(t *testing.T) { w.WriteHeader(http.StatusOK) }) } + +func MockChangeTypeResponse(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-retype": + { + "new_type": "ssd", + "migration_policy": "on-demand" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go index f792b8c2d8..2b5bd9ca0a 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go @@ -194,3 +194,18 @@ func TestSetBootable(t *testing.T) { err := volumeactions.SetBootable(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } + +func TestChangeType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockChangeTypeResponse(t) + + options := &volumeactions.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: "on-demand", + } + + err := volumeactions.ChangeType(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} From fec2b51cda7d96c30404a8974086574863f1a78c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 13 Feb 2021 14:58:53 -0700 Subject: [PATCH 1217/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc9f35de7f..a3ac919b02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ IMPROVEMENTS * 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) ## 0.15.0 (December 27, 2020) From 67150d2800cbf4a42623adfd2d0b71b8f523ff2f Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Mon, 22 Feb 2021 22:47:02 +0100 Subject: [PATCH 1218/2296] Baremetal: support deploy_steps for the provisioning API (#2120) Deploy steps work similarly to clean steps but during deployment. For: #2119 --- openstack/baremetal/v1/nodes/requests.go | 26 +++++++++++++++-- .../baremetal/v1/nodes/testing/fixtures.go | 26 +++++++++++++++++ .../v1/nodes/testing/requests_test.go | 29 +++++++++++++++++-- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 9439638e5c..87aaa1c0a9 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -394,15 +394,36 @@ func GetSupportedBootDevices(client *gophercloud.ServiceClient, id string) (r Su return } +// An interface type for a deploy (or clean) step. +type StepInterface string + +const ( + InterfaceBIOS StepInterface = "bios" + InterfaceDeploy StepInterface = "deploy" + InterfaceManagement StepInterface = "management" + InterfacePower StepInterface = "power" + InterfaceRAID StepInterface = "raid" +) + // A cleaning step has required keys ‘interface’ and ‘step’, and optional key ‘args’. If specified, // the value for ‘args’ is a keyword variable argument dictionary that is passed to the cleaning step // method. type CleanStep struct { - Interface string `json:"interface" required:"true"` + Interface StepInterface `json:"interface" required:"true"` Step string `json:"step" required:"true"` Args map[string]interface{} `json:"args,omitempty"` } +// A deploy step has required keys ‘interface’, ‘step’, ’args’ and ’priority’. +// The value for ‘args’ is a keyword variable argument dictionary that is passed to the deploy step +// method. Priority is a numeric priority at which the step is running. +type DeployStep struct { + Interface StepInterface `json:"interface" required:"true"` + Step string `json:"step" required:"true"` + Args map[string]interface{} `json:"args" required:"true"` + Priority int `json:"priority" required:"true"` +} + // ProvisionStateOptsBuilder allows extensions to add additional parameters to the // ChangeProvisionState request. type ProvisionStateOptsBuilder interface { @@ -418,11 +439,12 @@ type ConfigDrive struct { } // ProvisionStateOpts for a request to change a node's provision state. A config drive should be base64-encoded -// gzipped ISO9660 image. +// gzipped ISO9660 image. Deploy steps are supported starting with API 1.69. type ProvisionStateOpts struct { Target TargetProvisionState `json:"target" required:"true"` ConfigDrive interface{} `json:"configdrive,omitempty"` CleanSteps []CleanStep `json:"clean_steps,omitempty"` + DeploySteps []DeployStep `json:"deploy_steps,omitempty"` RescuePassword string `json:"rescue_password,omitempty"` } diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 1717f7a586..858215b28e 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -547,6 +547,23 @@ const NodeProvisionStateActiveBody = ` "configdrive": "http://127.0.0.1/images/test-node-config-drive.iso.gz" } ` + +const NodeProvisionStateActiveBodyWithSteps = ` +{ + "target": "active", + "deploy_steps": [ + { + "interface": "deploy", + "step": "inject_files", + "priority": 50, + "args": { + "files": [] + } + } + ] +} +` + const NodeProvisionStateCleanBody = ` { "target": "clean", @@ -937,6 +954,15 @@ func HandleNodeChangeProvisionStateActive(t *testing.T) { }) } +func HandleNodeChangeProvisionStateActiveWithSteps(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateActiveBodyWithSteps) + w.WriteHeader(http.StatusAccepted) + }) +} + func HandleNodeChangeProvisionStateClean(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/provision", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "PUT") diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 3770916c13..33f5817284 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -254,6 +254,29 @@ func TestNodeChangeProvisionStateActive(t *testing.T) { th.AssertNoErr(t, err) } +func TestNodeChangeProvisionStateActiveWithSteps(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeChangeProvisionStateActiveWithSteps(t) + + c := client.ServiceClient() + err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + Target: nodes.TargetActive, + DeploySteps: []nodes.DeployStep{ + { + Interface: nodes.InterfaceDeploy, + Step: "inject_files", + Priority: 50, + Args: map[string]interface{}{ + "files": []interface{}{}, + }, + }, + }, + }).ExtractErr() + + th.AssertNoErr(t, err) +} + func TestHandleNodeChangeProvisionStateConfigDrive(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -280,7 +303,7 @@ func TestNodeChangeProvisionStateClean(t *testing.T) { Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { - Interface: "deploy", + Interface: nodes.InterfaceDeploy, Step: "upgrade_firmware", Args: map[string]interface{}{ "force": "True", @@ -302,7 +325,7 @@ func TestNodeChangeProvisionStateCleanWithConflict(t *testing.T) { Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { - Interface: "deploy", + Interface: nodes.InterfaceDeploy, Step: "upgrade_firmware", Args: map[string]interface{}{ "force": "True", @@ -341,7 +364,7 @@ func TestCleanStepRequiresStep(t *testing.T) { Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { - Interface: "deploy", + Interface: nodes.InterfaceDeploy, Args: map[string]interface{}{ "force": "True", }, From fe53046609ca80a388df20f02ae91da7ac9efc58 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 22 Feb 2021 14:49:32 -0700 Subject: [PATCH 1219/2296] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3ac919b02..4345d526f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.16.0 (Unreleased) +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) @@ -23,6 +27,8 @@ IMPROVEMENTS * 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) ## 0.15.0 (December 27, 2020) From 78cdb2bf67e3d3445759844a57ecc98b22ed9832 Mon Sep 17 00:00:00 2001 From: Feruzjon Muyassarov Date: Tue, 23 Feb 2021 17:18:32 +0200 Subject: [PATCH 1220/2296] Baremetal: add nodes.automated_clean (#2122) Baremetal: add nodes.automated_clean --- openstack/baremetal/v1/nodes/requests.go | 4 ++++ openstack/baremetal/v1/nodes/results.go | 4 ++++ openstack/baremetal/v1/nodes/testing/fixtures.go | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 87aaa1c0a9..9648abe5c5 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -181,6 +181,10 @@ type CreateOptsBuilder interface { // CreateOpts specifies node creation parameters. type CreateOpts struct { + // The interface to configure automated cleaning for a Node. + // Requires microversion 1.47 or later. + AutomatedClean *bool `json:"automated_clean,omitempty"` + // The boot interface for a Node, e.g. “pxe”. BootInterface string `json:"boot_interface,omitempty"` diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 8d61b8d3bd..70dc9a0447 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -50,6 +50,10 @@ func ExtractNodesInto(r pagination.Page, v interface{}) error { // Node represents a node in the OpenStack Bare Metal API. type Node struct { + // Whether automated cleaning is enabled or disabled on this node. + // Requires microversion 1.47 or later. + AutomatedClean *bool `json:"automated_clean"` + // UUID for the resource. UUID string `json:"uuid"` diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 858215b28e..a849d05ee1 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -74,6 +74,7 @@ const NodeListDetailBody = ` { "nodes": [ { + "automated_clean": null, "bios_interface": "no-bios", "boot_interface": "pxe", "chassis_uuid": null, @@ -178,6 +179,7 @@ const NodeListDetailBody = ` ] }, { + "automated_clean": null, "bios_interface": "no-bios", "boot_interface": "pxe", "chassis_uuid": null, @@ -274,6 +276,7 @@ const NodeListDetailBody = ` ] }, { + "automated_clean": null, "bios_interface": "no-bios", "boot_interface": "pxe", "chassis_uuid": null, @@ -376,6 +379,7 @@ const NodeListDetailBody = ` // SingleNodeBody is the canned body of a Get request on an existing node. const SingleNodeBody = ` { + "automated_clean": null, "bios_interface": "no-bios", "boot_interface": "pxe", "chassis_uuid": null, From 63f60e2c6ea7dd824e839104313477a7c002cf56 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 23 Feb 2021 08:19:24 -0700 Subject: [PATCH 1221/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4345d526f9..8721b7c57d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ IMPROVEMENTS * 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) From bcd179f2c98de61525a99bf9310d698fd7786a17 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 23 Feb 2021 08:20:29 -0700 Subject: [PATCH 1222/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8721b7c57d..f1a1d3e311 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.16.0 (Unreleased) +## 0.17.0 (Unreleased) + +## 0.16.0 (February 23, 2021) UPGRADE NOTES From 3dc4bb870058da3030372f11efbd003fb7fe0856 Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Wed, 10 Mar 2021 01:48:48 +0100 Subject: [PATCH 1223/2296] blockstoragev3: support extra_spec for volumetypes (#2123) Add support for blockstoragev3 volumetype extra_spec operations such as: - create extra specs for volume type - list all extra specs of volume type - get specific extra spec of volume type - update specific extra spec of volume type - delete specific extra spec of volume type --- .../blockstorage/v3/volumetypes_test.go | 54 +++++++++ openstack/blockstorage/v3/volumetypes/doc.go | 59 +++++++++- .../blockstorage/v3/volumetypes/requests.go | 93 +++++++++++++++ .../blockstorage/v3/volumetypes/results.go | 63 ++++++++++- .../v3/volumetypes/testing/fixtures.go | 106 ++++++++++++++++++ .../v3/volumetypes/testing/requests_test.go | 60 ++++++++++ openstack/blockstorage/v3/volumetypes/urls.go | 20 ++++ 7 files changed, 452 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 2afe90c1d1..176e41963c 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -55,3 +55,57 @@ func TestVolumeTypes(t *testing.T) { th.AssertEquals(t, description, newVT.Description) th.AssertEquals(t, isPublic, newVT.IsPublic) } + +func TestVolumeTypesExtraSpecs(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + vt, err := CreateVolumeTypeNoExtraSpecs(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + + createOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu", + "volume_backend_name": "ssd", + } + + createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, vt.ID, createOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, createdExtraSpecs) + + th.AssertEquals(t, len(createdExtraSpecs), 2) + th.AssertEquals(t, createdExtraSpecs["capabilities"], "gpu") + th.AssertEquals(t, createdExtraSpecs["volume_backend_name"], "ssd") + + err = volumetypes.DeleteExtraSpec(client, vt.ID, "volume_backend_name").ExtractErr() + th.AssertNoErr(t, err) + + updateOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu-2", + } + updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, vt.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, updatedExtraSpec) + + th.AssertEquals(t, updatedExtraSpec["capabilities"], "gpu-2") + + allExtraSpecs, err := volumetypes.ListExtraSpecs(client, vt.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, allExtraSpecs) + + th.AssertEquals(t, len(allExtraSpecs), 1) + th.AssertEquals(t, allExtraSpecs["capabilities"], "gpu-2") + + singleSpec, err := volumetypes.GetExtraSpec(client, vt.ID, "capabilities").Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, singleSpec) + + th.AssertEquals(t, singleSpec["capabilities"], "gpu-2") +} diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index 8b2769cb7c..4b5fb687f1 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -58,6 +58,63 @@ Example to update a Volume Type panic(err) } fmt.Println(volumetype) -*/ +Example to Create Extra Specs for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + + createOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu", + } + createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, typeID, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", createdExtraSpecs) + +Example to Get Extra Specs for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + + extraSpecs, err := volumetypes.ListExtraSpecs(client, typeID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", extraSpecs) + +Example to Get specific Extra Spec for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + + extraSpec, err := volumetypes.GetExtraSpec(client, typeID, "capabilities").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", extraSpec) + +Example to Update Extra Specs for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + + updateOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "capabilities-updated", + } + updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, typeID, updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", updatedExtraSpec) + +Example to Delete an Extra Spec for a Volume Type + + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + err := volumetypes.DeleteExtraSpec(client, typeID, "capabilities").ExtractErr() + if err != nil { + panic(err) + } +*/ package volumetypes diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 869d426bf6..48c0a202d3 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -140,3 +140,96 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ListExtraSpecs requests all the extra-specs for the given volume type ID. +func ListExtraSpecs(client *gophercloud.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { + resp, err := client.Get(extraSpecsListURL(client, volumeTypeID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetExtraSpec requests an extra-spec specified by key for the given volume type ID +func GetExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { + resp, err := client.Get(extraSpecsGetURL(client, volumeTypeID, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the +// CreateExtraSpecs requests. +type CreateExtraSpecsOptsBuilder interface { + ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) +} + +// ExtraSpecsOpts is a map that contains key-value pairs. +type ExtraSpecsOpts map[string]string + +// ToVolumeTypeExtraSpecsCreateMap assembles a body for a Create request based on +// the contents of ExtraSpecsOpts. +func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) { + return map[string]interface{}{"extra_specs": opts}, nil +} + +// CreateExtraSpecs will create or update the extra-specs key-value pairs for +// the specified volume type. +func CreateExtraSpecs(client *gophercloud.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { + b, err := opts.ToVolumeTypeExtraSpecsCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(extraSpecsCreateURL(client, volumeTypeID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateExtraSpecOptsBuilder interface { + ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) +} + +// ToVolumeTypeExtraSpecUpdateMap assembles a body for an Update request based on +// the contents of a ExtraSpecOpts. +func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecUpdateMap() (map[string]string, string, error) { + if len(opts) != 1 { + err := gophercloud.ErrInvalidInput{} + err.Argument = "volumetypes.ExtraSpecOpts" + err.Info = "Must have one and only one key-value pair" + return nil, "", err + } + + var key string + for k := range opts { + key = k + } + + return opts, key, nil +} + +// UpdateExtraSpec will updates the value of the specified volume type's extra spec +// for the key in opts. +func UpdateExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { + b, key, err := opts.ToVolumeTypeExtraSpecUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(extraSpecUpdateURL(client, volumeTypeID, key), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteExtraSpec will delete the key-value pair with the given key for the given +// volume type ID. +func DeleteExtraSpec(client *gophercloud.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { + resp, err := client.Delete(extraSpecDeleteURL(client, volumeTypeID, key), &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, 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 2e3169070f..8901bf06a1 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -5,7 +5,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// Volume Type contains all the information associated with an OpenStack Volume Type. +// VolumeType contains all the information associated with an OpenStack Volume Type. type VolumeType struct { // Unique identifier for the volume type. ID string `json:"id"` @@ -68,7 +68,7 @@ func (r commonResult) ExtractInto(v interface{}) error { return r.Result.ExtractIntoStructPtr(v, "volume_type") } -// ExtractVolumesInto similar to ExtractInto but operates on a `list` of volume types +// ExtractVolumeTypesInto similar to ExtractInto but operates on a `list` of volume types func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { return r.(VolumeTypePage).Result.ExtractIntoSlicePtr(v, "volume_types") } @@ -92,3 +92,62 @@ type DeleteResult struct { type UpdateResult struct { commonResult } + +// extraSpecsResult 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 extraSpecsResult struct { + gophercloud.Result +} + +// ListExtraSpecsResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. +type ListExtraSpecsResult struct { + extraSpecsResult +} + +// CreateExtraSpecsResult contains the result of a Create operation. Call its +// Extract method to interpret it as a map[string]interface. +type CreateExtraSpecsResult struct { + extraSpecsResult +} + +// Extract interprets any extraSpecsResult as ExtraSpecs, if possible. +func (r extraSpecsResult) Extract() (map[string]string, error) { + var s struct { + ExtraSpecs map[string]string `json:"extra_specs"` + } + err := r.ExtractInto(&s) + return s.ExtraSpecs, err +} + +// extraSpecResult contains the result of a call for individual a single +// key-value pair. +type extraSpecResult struct { + gophercloud.Result +} + +// GetExtraSpecResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. +type GetExtraSpecResult struct { + extraSpecResult +} + +// UpdateExtraSpecResult contains the result of an Update operation. Call its +// Extract method to interpret it as a map[string]interface. +type UpdateExtraSpecResult struct { + extraSpecResult +} + +// DeleteExtraSpecResult contains the result of a Delete operation. Call its +// ExtractErr method to determine if the call succeeded or failed. +type DeleteExtraSpecResult struct { + gophercloud.ErrResult +} + +// Extract interprets any extraSpecResult as an ExtraSpec, if possible. +func (r extraSpecResult) Extract() (map[string]string, error) { + var s map[string]string + err := r.ExtractInto(&s) + return s, err +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go index 979aee174c..eb617f19e5 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures.go @@ -152,3 +152,109 @@ func MockUpdateResponse(t *testing.T) { }`) }) } + +// ExtraSpecsGetBody provides a GET result of the extra_specs for a volume type +const ExtraSpecsGetBody = ` +{ + "extra_specs" : { + "capabilities": "gpu", + "volume_backend_name": "ssd" + } +} +` + +// GetExtraSpecBody provides a GET result of a particular extra_spec for a volume type +const GetExtraSpecBody = ` +{ + "capabilities": "gpu" +} +` + +// UpdatedExtraSpecBody provides an PUT result of a particular updated extra_spec for a volume type +const UpdatedExtraSpecBody = ` +{ + "capabilities": "gpu-2" +} +` + +// ExtraSpecs is the expected extra_specs returned from GET on a volume type's extra_specs +var ExtraSpecs = map[string]string{ + "capabilities": "gpu", + "volume_backend_name": "ssd", +} + +// ExtraSpec is the expected extra_spec returned from GET on a volume type's extra_specs +var ExtraSpec = map[string]string{ + "capabilities": "gpu", +} + +// UpdatedExtraSpec is the expected extra_spec returned from PUT on a volume type's extra_specs +var UpdatedExtraSpec = map[string]string{ + "capabilities": "gpu-2", +} + +func HandleExtraSpecsListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs", 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().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExtraSpecsGetBody) + }) +} + +func HandleExtraSpecGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs/capabilities", 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().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, GetExtraSpecBody) + }) +} + +func HandleExtraSpecsCreateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs", 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.TestJSONRequest(t, r, `{ + "extra_specs": { + "capabilities": "gpu", + "volume_backend_name": "ssd" + } + }`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ExtraSpecsGetBody) + }) +} + +func HandleExtraSpecUpdateSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs/capabilities", 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, `{ + "capabilities": "gpu-2" + }`) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdatedExtraSpecBody) + }) +} + +func HandleExtraSpecDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/types/1/extra_specs/capabilities", 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) + }) +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index b1435805d9..64bdcba12b 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -117,3 +117,63 @@ func TestUpdate(t *testing.T) { th.CheckEquals(t, "vol-type-002", v.Name) th.CheckEquals(t, true, v.IsPublic) } + +func TestVolumeTypeExtraSpecsList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecsListSuccessfully(t) + + expected := ExtraSpecs + actual, err := volumetypes.ListExtraSpecs(client.ServiceClient(), "1").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestVolumeTypeExtraSpecGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecGetSuccessfully(t) + + expected := ExtraSpec + actual, err := volumetypes.GetExtraSpec(client.ServiceClient(), "1", "capabilities").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestVolumeTypeExtraSpecsCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecsCreateSuccessfully(t) + + createOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu", + "volume_backend_name": "ssd", + } + expected := ExtraSpecs + actual, err := volumetypes.CreateExtraSpecs(client.ServiceClient(), "1", createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestVolumeTypeExtraSpecUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecUpdateSuccessfully(t) + + updateOpts := volumetypes.ExtraSpecsOpts{ + "capabilities": "gpu-2", + } + expected := UpdatedExtraSpec + actual, err := volumetypes.UpdateExtraSpec(client.ServiceClient(), "1", updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestVolumeTypeExtraSpecDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleExtraSpecDeleteSuccessfully(t) + + res := volumetypes.DeleteExtraSpec(client.ServiceClient(), "1", "capabilities") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go index f794af86d1..effd8c5dab 100644 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -21,3 +21,23 @@ func deleteURL(c *gophercloud.ServiceClient, id string) string { func updateURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("types", id) } + +func extraSpecsListURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "extra_specs") +} + +func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("types", id, "extra_specs", key) +} + +func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "extra_specs") +} + +func extraSpecUpdateURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("types", id, "extra_specs", key) +} + +func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("types", id, "extra_specs", key) +} From cd9c207e93f4f76af2c0a06c6d449ab342bfbe56 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 9 Mar 2021 17:50:38 -0700 Subject: [PATCH 1224/2296] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a1d3e311..05fded4a97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ ## 0.17.0 (Unreleased) +IMPROVEMENTS + +* 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) + ## 0.16.0 (February 23, 2021) UPGRADE NOTES From 613955ac6448a28e7d4893421dc785af779aebaa Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 11 Mar 2021 20:38:10 +0100 Subject: [PATCH 1225/2296] Neutron quota: int-as-string workaround (#2126) In some conditions, Neutron will JSON-encode quota values as strings instead of integers. With this patch, the unmarshaling method is overridden to accept integers as strings. See https://bugs.launchpad.net/neutron/+bug/1918565 Fixes #2125 --- .../v2/extensions/quotas/results.go | 45 ++++++++++++++++++- .../v2/extensions/quotas/testing/fixtures.go | 7 ++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/quotas/results.go b/openstack/networking/v2/extensions/quotas/results.go index 99f28771b0..4748ace52c 100644 --- a/openstack/networking/v2/extensions/quotas/results.go +++ b/openstack/networking/v2/extensions/quotas/results.go @@ -1,6 +1,12 @@ package quotas -import "github.com/gophercloud/gophercloud" +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/gophercloud/gophercloud" +) type commonResult struct { gophercloud.Result @@ -128,3 +134,40 @@ type QuotaDetail struct { // allocated/provisioned. This is what "quota" usually refers to. Limit int `json:"limit"` } + +// UnmarshalJSON overrides the default unmarshalling function to accept +// Reserved as a string. +// +// Due to a bug in Neutron, under some conditions Reserved is returned as a +// string. +// +// This method is left for compatibility with unpatched versions of Neutron. +// +// cf. https://bugs.launchpad.net/neutron/+bug/1918565 +func (q *QuotaDetail) UnmarshalJSON(b []byte) error { + type tmp QuotaDetail + var s struct { + tmp + Reserved interface{} `json:"reserved"` + } + + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *q = QuotaDetail(s.tmp) + + switch t := s.Reserved.(type) { + case float64: + q.Reserved = int(t) + case string: + if q.Reserved, err = strconv.Atoi(t); err != nil { + return err + } + default: + return fmt.Errorf("reserved has unexpected type: %T", t) + } + + return nil +} diff --git a/openstack/networking/v2/extensions/quotas/testing/fixtures.go b/openstack/networking/v2/extensions/quotas/testing/fixtures.go index b81a1eed17..de4b579ce5 100644 --- a/openstack/networking/v2/extensions/quotas/testing/fixtures.go +++ b/openstack/networking/v2/extensions/quotas/testing/fixtures.go @@ -22,6 +22,11 @@ const GetResponseRaw = ` ` // GetDetailedResponseRaw is a sample response to a Get call with the detailed option. +// +// One "reserved" property is returned as a string to reflect a buggy behaviour +// of Neutron. +// +// cf. https://bugs.launchpad.net/neutron/+bug/1918565 const GetDetailedResponseRaw = ` { "quota" : { @@ -38,7 +43,7 @@ const GetDetailedResponseRaw = ` "port" : { "used": 0, "limit": 25, - "reserved": 0 + "reserved": "0" }, "rbac_policy" : { "used": 0, From 69f51f2f086c850bf0b0691cb6b94e117af1c75f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 11 Mar 2021 12:40:00 -0700 Subject: [PATCH 1226/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05fded4a97..074ab2585f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ 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) From 61ead6a7a9d6dfb44bcc9fdc23d9ed12ea5516f0 Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Wed, 17 Mar 2021 15:12:36 +0100 Subject: [PATCH 1227/2296] blockstorage/v3/volumetypes Extra_specs omitempty (#2130) --- openstack/blockstorage/v3/volumetypes/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 48c0a202d3..882c9c9dd0 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -22,7 +22,7 @@ type CreateOpts struct { // the ID of the existing volume snapshot IsPublic *bool `json:"os-volume-type-access:is_public,omitempty"` // Extra spec key-value pairs defined by the user. - ExtraSpecs map[string]string `json:"extra_specs"` + ExtraSpecs map[string]string `json:"extra_specs,omitempty"` } // ToVolumeTypeCreateMap assembles a request body based on the contents of a From d26cb14b4e620d51d9cda2382859570db76fd92f Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 26 Mar 2021 08:21:26 +0300 Subject: [PATCH 1228/2296] Identity: Support include_names query arg in /role_assignments (#2133) Keystone may include names into RoleAssignment entity, if provide include_names=true as query arguemnt doc: https://docs.openstack.org/api-ref/identity/v3/index.html?expanded=id627-detail#id627 --- openstack/identity/v3/roles/requests.go | 3 + openstack/identity/v3/roles/results.go | 15 +++-- .../identity/v3/roles/testing/fixtures.go | 60 +++++++++++++++++++ .../v3/roles/testing/requests_test.go | 24 ++++++++ 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 007a15b9cc..61d09c71f7 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -204,6 +204,9 @@ type ListAssignmentsOpts struct { // Effective lists effective assignments at the user, project, and domain // level, allowing for the effects of group membership. Effective *bool `q:"effective"` + + // IncludeNames indicates whether to include names of any returned entities. + IncludeNames *bool `q:"include_names"` } // ToRolesListAssignmentsQuery formats a ListAssignmentsOpts into a query string. diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 631c992a3c..da2c348e77 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -138,7 +138,8 @@ type RoleAssignment struct { // AssignedRole represents a Role in an assignment. type AssignedRole struct { - ID string `json:"id,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // Scope represents a scope in a Role assignment. @@ -149,22 +150,26 @@ type Scope struct { // Domain represents a domain in a role assignment scope. type Domain struct { - ID string `json:"id,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // Project represents a project in a role assignment scope. type Project struct { - ID string `json:"id,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // User represents a user in a role assignment scope. type User struct { - ID string `json:"id,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // Group represents a group in a role assignment scope. type Group struct { - ID string `json:"id,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // RoleAssignmentPage is a single page of RoleAssignments results. diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index 9bcc2c7d07..bcbd8e2522 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -96,6 +96,7 @@ const UpdateOutput = ` } ` +// ListAssignmentOutput provides a result of ListAssignment request. const ListAssignmentOutput = ` { "role_assignments": [ @@ -141,6 +142,38 @@ const ListAssignmentOutput = ` } ` +// ListAssignmentWithNamesOutput provides a result of ListAssignment request with IncludeNames option. +const ListAssignmentWithNamesOutput = ` +{ + "role_assignments": [ + { + "links": { + "assignment": "http://identity:35357/v3/domains/161718/users/313233/roles/123456" + }, + "role": { + "id": "123456", + "name": "include_names_role" + }, + "scope": { + "domain": { + "id": "161718", + "name": "52833" + } + }, + "user": { + "id": "313233", + "name": "example-user-name" + } + } + ], + "links": { + "self": "http://identity:35357/v3/role_assignments?include_names=True", + "previous": null, + "next": null + } +} +` + // ListAssignmentsOnResourceOutput provides a result of ListAssignmentsOnResource request. const ListAssignmentsOnResourceOutput = ` { @@ -337,10 +370,22 @@ var SecondRoleAssignment = roles.RoleAssignment{ Group: roles.Group{}, } +// ThirdRoleAssignment is the third role assignment that has entity names in the List request. +var ThirdRoleAssignment = roles.RoleAssignment{ + Role: roles.AssignedRole{ID: "123456", Name: "include_names_role"}, + Scope: roles.Scope{Domain: roles.Domain{ID: "161718", Name: "52833"}}, + User: roles.User{ID: "313233", Name: "example-user-name"}, + Group: roles.Group{}, +} + // ExpectedRoleAssignmentsSlice is the slice of role assignments expected to be // returned from ListAssignmentOutput. var ExpectedRoleAssignmentsSlice = []roles.RoleAssignment{FirstRoleAssignment, SecondRoleAssignment} +// ExpectedRoleAssignmentsWithNamesSlice is the slice of role assignments expected to be +// returned from ListAssignmentWithNamesOutput. +var ExpectedRoleAssignmentsWithNamesSlice = []roles.RoleAssignment{ThirdRoleAssignment} + // HandleListRoleAssignmentsSuccessfully creates an HTTP handler at `/role_assignments` on the // test handler mux that responds with a list of two role assignments. func HandleListRoleAssignmentsSuccessfully(t *testing.T) { @@ -355,6 +400,21 @@ func HandleListRoleAssignmentsSuccessfully(t *testing.T) { }) } +// HandleListRoleAssignmentsSuccessfully creates an HTTP handler at `/role_assignments` on the +// test handler mux that responds with a list of two role assignments. +func HandleListRoleAssignmentsWithNamesSuccessfully(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_names=true", r.URL.RawQuery) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListAssignmentWithNamesOutput) + }) +} + // 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 c8ac5a9b03..d574d8ba93 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -147,6 +147,30 @@ func TestListAssignmentsSinglePage(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListAssignmentsWithNamesSinglePage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRoleAssignmentsWithNamesSuccessfully(t) + + var includeNames = true + listOpts := roles.ListAssignmentsOpts{ + IncludeNames: &includeNames, + } + + 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, ExpectedRoleAssignmentsWithNamesSlice, 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 ff0fd6c11d7a8e2c0534ac5ab668bf952a906156 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 25 Mar 2021 23:22:11 -0600 Subject: [PATCH 1229/2296] Update requests.go --- openstack/identity/v3/roles/requests.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 61d09c71f7..851dae0a6e 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -206,6 +206,7 @@ type ListAssignmentsOpts struct { Effective *bool `q:"effective"` // IncludeNames indicates whether to include names of any returned entities. + // Requires microversion 3.6 or later. IncludeNames *bool `q:"include_names"` } From 906fef2de40d3ca81274d57c7e6c8b173f2f9032 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 25 Mar 2021 23:24:01 -0600 Subject: [PATCH 1230/2296] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 074ab2585f..7a970b2153 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ IMPROVEMENTS * 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) + ## 0.16.0 (February 23, 2021) From 0fda7e61f69bff87fd64ef207d8b806897163cc3 Mon Sep 17 00:00:00 2001 From: Mike Fedosin Date: Thu, 1 Apr 2021 04:06:00 +0200 Subject: [PATCH 1231/2296] Add availabilityzones extension for blockstorage (#2135) --- .../extensions/availabilityzones/doc.go | 21 ++++++++ .../extensions/availabilityzones/requests.go | 13 +++++ .../extensions/availabilityzones/results.go | 33 ++++++++++++ .../availabilityzones/testing/doc.go | 2 + .../availabilityzones/testing/fixtures.go | 52 +++++++++++++++++++ .../testing/requests_test.go | 25 +++++++++ .../extensions/availabilityzones/urls.go | 7 +++ 7 files changed, 153 insertions(+) create mode 100644 openstack/blockstorage/extensions/availabilityzones/doc.go create mode 100644 openstack/blockstorage/extensions/availabilityzones/requests.go create mode 100644 openstack/blockstorage/extensions/availabilityzones/results.go create mode 100644 openstack/blockstorage/extensions/availabilityzones/testing/doc.go create mode 100644 openstack/blockstorage/extensions/availabilityzones/testing/fixtures.go create mode 100644 openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go create mode 100644 openstack/blockstorage/extensions/availabilityzones/urls.go diff --git a/openstack/blockstorage/extensions/availabilityzones/doc.go b/openstack/blockstorage/extensions/availabilityzones/doc.go new file mode 100644 index 0000000000..0b9a5a6b58 --- /dev/null +++ b/openstack/blockstorage/extensions/availabilityzones/doc.go @@ -0,0 +1,21 @@ +/* +Package availabilityzones provides the ability to get lists of +available volume availability zones. + +Example of Get Availability Zone Information + + allPages, err := availabilityzones.List(volumeClient).AllPages() + if err != nil { + panic(err) + } + + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + panic(err) + } + + for _, zoneInfo := range availabilityZoneInfo { + fmt.Printf("%+v\n", zoneInfo) + } +*/ +package availabilityzones diff --git a/openstack/blockstorage/extensions/availabilityzones/requests.go b/openstack/blockstorage/extensions/availabilityzones/requests.go new file mode 100644 index 0000000000..df10b856eb --- /dev/null +++ b/openstack/blockstorage/extensions/availabilityzones/requests.go @@ -0,0 +1,13 @@ +package availabilityzones + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List will return the existing availability zones. +func List(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { + return AvailabilityZonePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/blockstorage/extensions/availabilityzones/results.go b/openstack/blockstorage/extensions/availabilityzones/results.go new file mode 100644 index 0000000000..0e115411c1 --- /dev/null +++ b/openstack/blockstorage/extensions/availabilityzones/results.go @@ -0,0 +1,33 @@ +package availabilityzones + +import ( + "github.com/gophercloud/gophercloud/pagination" +) + +// ZoneState represents the current state of the availability zone. +type ZoneState struct { + // Returns true if the availability zone is available + Available bool `json:"available"` +} + +// AvailabilityZone contains all the information associated with an OpenStack +// AvailabilityZone. +type AvailabilityZone struct { + // The availability zone name + ZoneName string `json:"zoneName"` + ZoneState ZoneState `json:"zoneState"` +} + +type AvailabilityZonePage struct { + pagination.SinglePageBase +} + +// ExtractAvailabilityZones returns a slice of AvailabilityZones contained in a +// single page of results. +func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) { + var s struct { + AvailabilityZoneInfo []AvailabilityZone `json:"availabilityZoneInfo"` + } + err := (r.(AvailabilityZonePage)).ExtractInto(&s) + return s.AvailabilityZoneInfo, err +} diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/doc.go b/openstack/blockstorage/extensions/availabilityzones/testing/doc.go new file mode 100644 index 0000000000..a4408d7a0d --- /dev/null +++ b/openstack/blockstorage/extensions/availabilityzones/testing/doc.go @@ -0,0 +1,2 @@ +// availabilityzones unittests +package testing diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/fixtures.go b/openstack/blockstorage/extensions/availabilityzones/testing/fixtures.go new file mode 100644 index 0000000000..4b500e4843 --- /dev/null +++ b/openstack/blockstorage/extensions/availabilityzones/testing/fixtures.go @@ -0,0 +1,52 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + az "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +const GetOutput = ` +{ + "availabilityZoneInfo": [ + { + "zoneName": "internal", + "zoneState": { + "available": true + } + }, + { + "zoneName": "nova", + "zoneState": { + "available": true + } + } + ] +}` + +var AZResult = []az.AvailabilityZone{ + { + ZoneName: "internal", + ZoneState: az.ZoneState{Available: true}, + }, + { + ZoneName: "nova", + ZoneState: az.ZoneState{Available: true}, + }, +} + +// HandleGetSuccessfully configures the test server to respond to a Get request +// for availability zone information. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-availability-zone", 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, GetOutput) + }) +} diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go b/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go new file mode 100644 index 0000000000..39f41bf09f --- /dev/null +++ b/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go @@ -0,0 +1,25 @@ +package testing + +import ( + "testing" + + az "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +// Verifies that availability zones can be listed correctly +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetSuccessfully(t) + + allPages, err := az.List(client.ServiceClient()).AllPages() + th.AssertNoErr(t, err) + + actual, err := az.ExtractAvailabilityZones(allPages) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, AZResult, actual) +} diff --git a/openstack/blockstorage/extensions/availabilityzones/urls.go b/openstack/blockstorage/extensions/availabilityzones/urls.go new file mode 100644 index 0000000000..fb4cdcf4e2 --- /dev/null +++ b/openstack/blockstorage/extensions/availabilityzones/urls.go @@ -0,0 +1,7 @@ +package availabilityzones + +import "github.com/gophercloud/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-availability-zone") +} From b1dcc226e3a266c347526d5b4e87de200866c4dc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 31 Mar 2021 20:06:35 -0600 Subject: [PATCH 1232/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a970b2153..54096ef37c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ IMPROVEMENTS * 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) ## 0.16.0 (February 23, 2021) From bad3698077229ebecae1a1291088aa0d4e415535 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 1 Apr 2021 18:02:09 +0200 Subject: [PATCH 1233/2296] */apiversions: Fix documentation (#2136) This is a documentation-only patch. * The **baremetal** apiversions `doc.go` documented a non-existant "AllPages" method of `ListResult`. * The **blockstorage** apiversions `doc.go` called `panic` with multiple arguments. * The **sharedfilesystem** apiversions `doc.go` did not provide an example call. --- openstack/baremetal/apiversions/doc.go | 14 +++++++-- openstack/blockstorage/apiversions/doc.go | 8 ++--- .../sharedfilesystems/apiversions/doc.go | 31 +++++++++++++++++-- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/openstack/baremetal/apiversions/doc.go b/openstack/baremetal/apiversions/doc.go index 93b5adc885..7fbbac0d80 100644 --- a/openstack/baremetal/apiversions/doc.go +++ b/openstack/baremetal/apiversions/doc.go @@ -3,11 +3,21 @@ Package apiversions provides information about the versions supported by a speci Example to list versions - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(baremetalClient).Extract() + if err != nil { + panic("unable to get API versions: " + err.Error()) + } + + for _, version := range allVersions.Versions { + fmt.Printf("%+v\n", version) + } Example to get a specific version - actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() + actual, err := apiversions.Get(baremetalClient).Extract() + if err != nil { + panic("unable to get API version: " + err.Error()) + } */ package apiversions diff --git a/openstack/blockstorage/apiversions/doc.go b/openstack/blockstorage/apiversions/doc.go index 05470516c4..8c38b506bf 100644 --- a/openstack/blockstorage/apiversions/doc.go +++ b/openstack/blockstorage/apiversions/doc.go @@ -6,15 +6,15 @@ Example of Retrieving all API Versions allPages, err := apiversions.List(client).AllPages() if err != nil { - panic("Unable to get API versions: %s", err) + panic("unable to get API versions: " + err.Error()) } allVersions, err := apiversions.ExtractAPIVersions(allPages) if err != nil { - panic("Unable to extract API versions: %s", err) + panic("unable to extract API versions: " + err.Error()) } - for _, version := range versions { + for _, version := range allVersions { fmt.Printf("%+v\n", version) } @@ -23,7 +23,7 @@ Example of Retrieving an API Version version, err := apiversions.Get(client, "v3").Extract() if err != nil { - panic("Unable to get API version: %s", err) + panic("unable to get API version: " + err.Error()) } fmt.Printf("%+v\n", version) diff --git a/openstack/sharedfilesystems/apiversions/doc.go b/openstack/sharedfilesystems/apiversions/doc.go index 841a9c578c..dc0a55ebad 100644 --- a/openstack/sharedfilesystems/apiversions/doc.go +++ b/openstack/sharedfilesystems/apiversions/doc.go @@ -1,3 +1,30 @@ -// Package apiversions provides information and interaction with the different -// API versions for the Shared File System service, code-named Manila. +/* +Package apiversions provides information and interaction with the different +API versions for the Shared File System service, code-named Manila. + +Example to List API Versions + + allPages, err := apiversions.List(client).AllPages() + if err != nil { + panic(err) + } + + allVersions, err := apiversions.ExtractAPIVersions(allPages) + if err != nil { + panic(err) + } + + for _, version := range allVersions { + fmt.Printf("%+v\n", version) + } + +Example to Get an API Version + + version, err := apiVersions.Get(client, "v2.1").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", version) +*/ package apiversions From 0c9d332cd1e5741d6acdd2e1fbe56bf1e6f28e07 Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Thu, 1 Apr 2021 18:08:22 +0200 Subject: [PATCH 1234/2296] Add support for volume type access (#2138) ListAccesses: List accesses of private volume type AddAccess: Add access of project to volume type RemoveAccess: Remove access of project from volume type --- .../openstack/blockstorage/v3/blockstorage.go | 31 ++++++ .../blockstorage/v3/volumetypes_test.go | 55 +++++++++++ openstack/blockstorage/v3/volumetypes/doc.go | 45 +++++++++ .../blockstorage/v3/volumetypes/requests.go | 73 +++++++++++++- .../blockstorage/v3/volumetypes/results.go | 41 ++++++++ .../v3/volumetypes/testing/requests_test.go | 99 +++++++++++++++++++ openstack/blockstorage/v3/volumetypes/urls.go | 8 ++ 7 files changed, 351 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index 3394cee7f6..250b9b5165 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -176,6 +176,37 @@ func CreateVolumeTypeNoExtraSpecs(t *testing.T, client *gophercloud.ServiceClien 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. +func CreatePrivateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) { + name := tools.RandomString("ACPTTEST", 16) + description := "create_from_gophercloud" + isPublic := false + t.Logf("Attempting to create volume type: %s", name) + + createOpts := volumetypes.CreateOpts{ + Name: name, + ExtraSpecs: map[string]string{}, + Description: description, + IsPublic: &isPublic, + } + + vt, err := volumetypes.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, vt) + th.AssertEquals(t, vt.IsPublic, false) + th.AssertEquals(t, vt.Name, name) + th.AssertEquals(t, vt.Description, description) + + t.Logf("Successfully created volume type: %s", vt.ID) + + return vt, nil +} + // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { diff --git a/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 176e41963c..e6e898df40 100644 --- a/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -6,6 +6,7 @@ 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/openstack/blockstorage/v3/volumetypes" th "github.com/gophercloud/gophercloud/testhelper" @@ -109,3 +110,57 @@ func TestVolumeTypesExtraSpecs(t *testing.T) { th.AssertEquals(t, singleSpec["capabilities"], "gpu-2") } + +func TestVolumeTypesAccess(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + identityClient, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + vt, err := CreatePrivateVolumeType(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, vt) + + project, err := identity.CreateProject(t, identityClient, nil) + th.AssertNoErr(t, err) + defer identity.DeleteProject(t, identityClient, project.ID) + + addAccessOpts := volumetypes.AddAccessOpts{ + Project: project.ID, + } + + err = volumetypes.AddAccess(client, vt.ID, addAccessOpts).ExtractErr() + th.AssertNoErr(t, err) + + allPages, err := volumetypes.ListAccesses(client, vt.ID).AllPages() + th.AssertNoErr(t, err) + + accessList, err := volumetypes.ExtractAccesses(allPages) + th.AssertNoErr(t, err) + + tools.PrintResource(t, accessList) + + th.AssertEquals(t, len(accessList), 1) + th.AssertEquals(t, accessList[0].ProjectID, project.ID) + th.AssertEquals(t, accessList[0].VolumeTypeID, vt.ID) + + removeAccessOpts := volumetypes.RemoveAccessOpts{ + Project: project.ID, + } + + err = volumetypes.RemoveAccess(client, vt.ID, removeAccessOpts).ExtractErr() + th.AssertNoErr(t, err) + + allPages, err = volumetypes.ListAccesses(client, vt.ID).AllPages() + th.AssertNoErr(t, err) + + accessList, err = volumetypes.ExtractAccesses(allPages) + th.AssertNoErr(t, err) + + tools.PrintResource(t, accessList) + + th.AssertEquals(t, len(accessList), 0) +} diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index 4b5fb687f1..4e48e9c022 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -116,5 +116,50 @@ Example to Delete an Extra Spec for a Volume Type if err != nil { panic(err) } + +Example to List Volume Type Access + + typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + allPages, err := volumetypes.ListAccesses(client, typeID).AllPages() + if err != nil { + panic(err) + } + + allAccesses, err := volumetypes.ExtractAccesses(allPages) + if err != nil { + panic(err) + } + + for _, access := range allAccesses { + fmt.Printf("%+v", access) + } + +Example to Grant Access to a Volume Type + + typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + accessOpts := volumetypes.AddAccessOpts{ + Project: "15153a0979884b59b0592248ef947921", + } + + err := volumetypes.AddAccess(client, typeID, accessOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Remove/Revoke Access to a Volume Type + + typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + accessOpts := volumetypes.RemoveAccessOpts{ + Project: "15153a0979884b59b0592248ef947921", + } + + err := volumetypes.RemoveAccess(client, typeID, accessOpts).ExtractErr() + if err != nil { + panic(err) + } + */ package volumetypes diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 882c9c9dd0..5b272bf05b 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -120,7 +120,7 @@ type UpdateOpts struct { IsPublic *bool `json:"is_public,omitempty"` } -// ToVolumeUpdateMap assembles a request body based on the contents of an +// ToVolumeTypeUpdateMap assembles a request body based on the contents of an // UpdateOpts. func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "volume_type") @@ -233,3 +233,74 @@ func DeleteExtraSpec(client *gophercloud.ServiceClient, volumeTypeID, key string _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ListAccesses retrieves the tenants which have access to a volume type. +func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager { + url := accessURL(client, id) + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessPage{pagination.SinglePageBase(r)} + }) +} + +// AddAccessOptsBuilder allows extensions to add additional parameters to the +// AddAccess requests. +type AddAccessOptsBuilder interface { + ToVolumeTypeAddAccessMap() (map[string]interface{}, error) +} + +// AddAccessOpts represents options for adding access to a volume type. +type AddAccessOpts struct { + // Project is the project/tenant ID to grant access. + Project string `json:"project"` +} + +// ToVolumeTypeAddAccessMap constructs a request body from AddAccessOpts. +func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "addProjectAccess") +} + +// AddAccess grants a tenant/project access to a volume type. +func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { + b, err := opts.ToVolumeTypeAddAccessMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RemoveAccessOptsBuilder allows extensions to add additional parameters to the +// RemoveAccess requests. +type RemoveAccessOptsBuilder interface { + ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) +} + +// RemoveAccessOpts represents options for removing access to a volume type. +type RemoveAccessOpts struct { + // Project is the project/tenant ID to remove access. + Project string `json:"project"` +} + +// ToVolumeTypeRemoveAccessMap constructs a request body from RemoveAccessOpts. +func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "removeProjectAccess") +} + +// RemoveAccess removes/revokes a tenant/project access to a volume type. +func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { + b, err := opts.ToVolumeTypeRemoveAccessMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, 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 8901bf06a1..72c696ef13 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -151,3 +151,44 @@ func (r extraSpecResult) Extract() (map[string]string, error) { err := r.ExtractInto(&s) return s, err } + +// VolumeTypeAccess represents an ACL of project access to a specific Volume Type. +type VolumeTypeAccess struct { + // VolumeTypeID is the unique ID of the volume type. + VolumeTypeID string `json:"volume_type_id"` + + // ProjectID is the unique ID of the project. + ProjectID string `json:"project_id"` +} + +// AccessPage contains a single page of all VolumeTypeAccess entries for a volume type. +type AccessPage struct { + pagination.SinglePageBase +} + +// IsEmpty indicates whether an AccessPage is empty. +func (page AccessPage) IsEmpty() (bool, error) { + v, err := ExtractAccesses(page) + return len(v) == 0, err +} + +// ExtractAccesses interprets a page of results as a slice of VolumeTypeAccess. +func ExtractAccesses(r pagination.Page) ([]VolumeTypeAccess, error) { + var s struct { + VolumeTypeAccesses []VolumeTypeAccess `json:"volume_type_access"` + } + err := (r.(AccessPage)).ExtractInto(&s) + return s.VolumeTypeAccesses, err +} + +// AddAccessResult is the response from a AddAccess request. Call its +// ExtractErr method to determine if the request succeeded or failed. +type AddAccessResult struct { + gophercloud.ErrResult +} + +// RemoveAccessResult is the response from a RemoveAccess request. Call its +// ExtractErr method to determine if the request succeeded or failed. +type RemoveAccessResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 64bdcba12b..eb6f2e7c0e 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -1,6 +1,9 @@ package testing import ( + "fmt" + "net/http" + "reflect" "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" @@ -177,3 +180,99 @@ func TestVolumeTypeExtraSpecDelete(t *testing.T) { res := volumetypes.DeleteExtraSpec(client.ServiceClient(), "1", "capabilities") th.AssertNoErr(t, res.Err) } + +func TestVolumeTypeListAccesses(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/os-volume-type-access", 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, ` + { + "volume_type_access": [ + { + "project_id": "6f70656e737461636b20342065766572", + "volume_type_id": "a5082c24-2a27-43a4-b48e-fcec1240e36b" + } + ] + } + `) + }) + + expected := []volumetypes.VolumeTypeAccess{ + { + VolumeTypeID: "a5082c24-2a27-43a4-b48e-fcec1240e36b", + ProjectID: "6f70656e737461636b20342065766572", + }, + } + + allPages, err := volumetypes.ListAccesses(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b").AllPages() + th.AssertNoErr(t, err) + + actual, err := volumetypes.ExtractAccesses(allPages) + th.AssertNoErr(t, err) + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } +} + +func TestVolumeTypeAddAccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "addProjectAccess": { + "project": "6f70656e737461636b20342065766572" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) + + addAccessOpts := volumetypes.AddAccessOpts{ + Project: "6f70656e737461636b20342065766572", + } + + err := volumetypes.AddAccess(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", addAccessOpts).ExtractErr() + th.AssertNoErr(t, err) + +} + +func TestVolumeTypeRemoveAccess(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/types/a5082c24-2a27-43a4-b48e-fcec1240e36b/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "removeProjectAccess": { + "project": "6f70656e737461636b20342065766572" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + }) + + removeAccessOpts := volumetypes.RemoveAccessOpts{ + Project: "6f70656e737461636b20342065766572", + } + + err := volumetypes.RemoveAccess(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", removeAccessOpts).ExtractErr() + th.AssertNoErr(t, err) + +} diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go index effd8c5dab..c63ee47e62 100644 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -41,3 +41,11 @@ func extraSpecUpdateURL(client *gophercloud.ServiceClient, id, key string) strin func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string { return client.ServiceURL("types", id, "extra_specs", key) } + +func accessURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "os-volume-type-access") +} + +func accessActionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("types", id, "action") +} From 513734676e6495f6fec60e7aaf1f86f1ce807428 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 1 Apr 2021 10:10:25 -0600 Subject: [PATCH 1235/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54096ef37c..f24306be8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ IMPROVEMENTS * 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) ## 0.16.0 (February 23, 2021) From 05a462de95f34e0b8acaf292c3508af4c1c28058 Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Sat, 10 Apr 2021 05:32:27 +0200 Subject: [PATCH 1236/2296] blockstorage/v3: Add support for create/delete qos (#2140) --- .../openstack/blockstorage/v3/blockstorage.go | 47 ++++++++ .../openstack/blockstorage/v3/qos_test.go | 20 ++++ openstack/blockstorage/v3/qos/doc.go | 36 +++++++ openstack/blockstorage/v3/qos/requests.go | 100 ++++++++++++++++++ openstack/blockstorage/v3/qos/results.go | 41 +++++++ openstack/blockstorage/v3/qos/testing/doc.go | 2 + .../blockstorage/v3/qos/testing/fixtures.go | 62 +++++++++++ .../v3/qos/testing/requests_test.go | 37 +++++++ openstack/blockstorage/v3/qos/urls.go | 11 ++ 9 files changed, 356 insertions(+) create mode 100644 acceptance/openstack/blockstorage/v3/qos_test.go create mode 100644 openstack/blockstorage/v3/qos/doc.go create mode 100644 openstack/blockstorage/v3/qos/requests.go create mode 100644 openstack/blockstorage/v3/qos/results.go create mode 100644 openstack/blockstorage/v3/qos/testing/doc.go create mode 100644 openstack/blockstorage/v3/qos/testing/fixtures.go create mode 100644 openstack/blockstorage/v3/qos/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/qos/urls.go diff --git a/acceptance/openstack/blockstorage/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index 250b9b5165..5e05024a1c 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/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" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" @@ -272,3 +273,49 @@ func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, vt *volum t.Logf("Successfully deleted volume type: %s", vt.ID) } + +// CreateQoS will create a QoS with one spec and a random name. An +// error will be returned if the volume was unable to be created. +func CreateQoS(t *testing.T, client *gophercloud.ServiceClient) (*qos.QoS, error) { + name := tools.RandomString("ACPTTEST", 16) + t.Logf("Attempting to create QoS: %s", name) + + createOpts := qos.CreateOpts{ + Name: name, + Consumer: qos.ConsumerFront, + Specs: map[string]string{ + "read_iops_sec": "20000", + }, + } + + qs, err := qos.Create(client, createOpts).Extract() + if err != nil { + return nil, err + } + + tools.PrintResource(t, qs) + th.AssertEquals(t, qs.Consumer, "front-end") + th.AssertEquals(t, qs.Name, name) + th.AssertDeepEquals(t, qs.Specs, createOpts.Specs) + + t.Logf("Successfully created QoS: %s", qs.ID) + + return qs, nil +} + +// DeleteQoS will delete a QoS. A fatal error will occur if the QoS +// failed to be deleted. This works best when used as a deferred function. +func DeleteQoS(t *testing.T, client *gophercloud.ServiceClient, qs *qos.QoS) { + t.Logf("Attempting to delete QoS: %s", qs.ID) + + deleteOpts := qos.DeleteOpts{ + Force: true, + } + + err := qos.Delete(client, qs.ID, deleteOpts).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete QoS %s: %v", qs.ID, err) + } + + t.Logf("Successfully deleted QoS: %s", qs.ID) +} diff --git a/acceptance/openstack/blockstorage/v3/qos_test.go b/acceptance/openstack/blockstorage/v3/qos_test.go new file mode 100644 index 0000000000..95ce68be48 --- /dev/null +++ b/acceptance/openstack/blockstorage/v3/qos_test.go @@ -0,0 +1,20 @@ +package v3 + +import ( + "testing" + + "github.com/gophercloud/gophercloud/acceptance/clients" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestQoS(t *testing.T) { + clients.SkipRelease(t, "stable/mitaka") + clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + qs, err := CreateQoS(t, client) + th.AssertNoErr(t, err) + defer DeleteQoS(t, client, qs) +} diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go new file mode 100644 index 0000000000..23894b651b --- /dev/null +++ b/openstack/blockstorage/v3/qos/doc.go @@ -0,0 +1,36 @@ +/* +Package qos provides information and interaction with the QoS specifications +for the Openstack Blockstorage service. + +Example to create a QoS specification + + createOpts := qos.CreateOpts{ + Name: "test", + Consumer: qos.ConsumerFront, + Specs: map[string]string{ + "read_iops_sec": "20000", + }, + } + + test, err := qos.Create(client, createOpts).Extract() + if err != nil { + log.Fatal(err) + } + + fmt.Printf("QoS: %+v\n", test) + +Example to delete a QoS specification + + qosID := "d6ae28ce-fcb5-4180-aa62-d260a27e09ae" + + deleteOpts := qos.DeleteOpts{ + Force: false, + } + + err = qos.Delete(client, qosID, deleteOpts).ExtractErr() + if err != nil { + log.Fatal(err) + } + +*/ +package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go new file mode 100644 index 0000000000..af02972f67 --- /dev/null +++ b/openstack/blockstorage/v3/qos/requests.go @@ -0,0 +1,100 @@ +package qos + +import ( + "github.com/gophercloud/gophercloud" +) + +type CreateOptsBuilder interface { + ToQoSCreateMap() (map[string]interface{}, error) +} + +type QoSConsumer string + +const ( + ConsumerFront QoSConsumer = "front-end" + ConsumberBack QoSConsumer = "back-end" + ConsumerBoth QoSConsumer = "both" +) + +// CreateOpts contains options for creating a QoS specification. +// This object is passed to the qos.Create function. +type CreateOpts struct { + // The name of the QoS spec + Name string `json:"name"` + // 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:"-"` +} + +// ToQoSCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToQoSCreateMap() (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 +} + +// Create will create a new QoS based on the values in CreateOpts. To extract +// the QoS object from the response, call the Extract method on the +// CreateResult. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToQoSCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteOptsBuilder allows extensions to add additional parameters to the +// Delete request. +type DeleteOptsBuilder interface { + ToQoSDeleteQuery() (string, error) +} + +// DeleteOpts contains options for deleting a QoS. This object is passed to +// the qos.Delete function. +type DeleteOpts struct { + // Delete a QoS specification even if it is in-use + Force bool `q:"force"` +} + +// ToQoSDeleteQuery formats a DeleteOpts into a query string. +func (opts DeleteOpts) ToQoSDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Delete will delete the existing QoS with the provided ID. +func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { + url := deleteURL(client, id) + if opts != nil { + query, err := opts.ToQoSDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Delete(url, nil) + _, 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 new file mode 100644 index 0000000000..700616427c --- /dev/null +++ b/openstack/blockstorage/v3/qos/results.go @@ -0,0 +1,41 @@ +package qos + +import "github.com/gophercloud/gophercloud" + +// QoS contains all the information associated with an OpenStack QoS specification. +type QoS struct { + // Name is the name of the QoS. + Name string `json:"name"` + // Unique identifier for the QoS. + ID string `json:"id"` + // Consumer of QoS + Consumer string `json:"consumer"` + // Arbitrary key-value pairs defined by the user. + Specs map[string]string `json:"specs"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the QoS object out of the commonResult object. +func (r commonResult) Extract() (*QoS, error) { + var s QoS + err := r.ExtractInto(&s) + return &s, err +} + +// ExtractInto converts our response data into a QoS struct +func (r commonResult) ExtractInto(qos interface{}) error { + return r.Result.ExtractIntoStructPtr(qos, "qos_specs") +} + +// CreateResult contains the response body and error from a Create request. +type CreateResult struct { + commonResult +} + +// DeleteResult contains the response body and error from a Delete request. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/qos/testing/doc.go b/openstack/blockstorage/v3/qos/testing/doc.go new file mode 100644 index 0000000000..0155a0963d --- /dev/null +++ b/openstack/blockstorage/v3/qos/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing for qos_v3 +package testing diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go new file mode 100644 index 0000000000..9cad5ff9a2 --- /dev/null +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -0,0 +1,62 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" + th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/testhelper/client" +) + +var createQoSExpected = 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") + 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, ` +{ + "qos_specs": { + "name": "qos-001", + "consumer": "front-end", + "read_iops_sec": "20000" + } +} + `) + + 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" + } + } +} + `) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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) + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go new file mode 100644 index 0000000000..72e8ac4c7d --- /dev/null +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -0,0 +1,37 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" + th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/testhelper/client" +) + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := qos.CreateOpts{ + Name: "qos-001", + Consumer: qos.ConsumerFront, + Specs: map[string]string{ + "read_iops_sec": "20000", + }, + } + actual, err := qos.Create(client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &createQoSExpected, actual) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := qos.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteOpts{}) + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go new file mode 100644 index 0000000000..3d28c53822 --- /dev/null +++ b/openstack/blockstorage/v3/qos/urls.go @@ -0,0 +1,11 @@ +package qos + +import "github.com/gophercloud/gophercloud" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("qos-specs") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("qos-specs", id) +} From c6cb996dcc7163aec07c663b6510417f37b7ebb7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 9 Apr 2021 21:33:38 -0600 Subject: [PATCH 1237/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f24306be8d..20a62c17e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ IMPROVEMENTS * 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) From ffd74114bb8c919f535c22a22361833478e0db6c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 9 Apr 2021 21:34:30 -0600 Subject: [PATCH 1238/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20a62c17e7..ea4ca7539e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.17.0 (Unreleased) +## 0.18.0 (Unreleased) + +## 0.17.0 (April 9, 2021) IMPROVEMENTS From 315f2335b9b40014645235d08720f43733c60f19 Mon Sep 17 00:00:00 2001 From: yylt <34183906+yylt@users.noreply.github.com> Date: Fri, 16 Apr 2021 13:22:23 +0800 Subject: [PATCH 1239/2296] heat/stack support show hidden option (#2104) Co-authored-by: yang.yang --- openstack/orchestration/v1/stacks/requests.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index bf480f06d8..a17cbd5f98 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -253,6 +253,9 @@ type ListOpts struct { // ShowNested set to `true` to include nested stacks in the list. ShowNested bool `q:"show_nested"` + // ShowHidden set to `true` to include hiddened stacks in the list. + ShowHidden bool `q:"show_hidden"` + // Tags lists stacks that contain one or more simple string tags. Tags string `q:"tags"` From 1f929efd30f55f81960334a623b96d7a65bba4f3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 15 Apr 2021 23:24:19 -0600 Subject: [PATCH 1240/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea4ca7539e..898a3239b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.18.0 (Unreleased) +IMPROVEMENTS + +* Added `orchestration/v1/stacks.ListOpts.ShowHidden` [GH-2104](https://github.com/gophercloud/gophercloud/pull/2104) + ## 0.17.0 (April 9, 2021) IMPROVEMENTS From 6a3f4b1ddeb05348ab48350e0a4d8a9b49cbd73d Mon Sep 17 00:00:00 2001 From: Sergey Putko Date: Fri, 23 Apr 2021 17:58:54 +0300 Subject: [PATCH 1241/2296] Fix doc (#2143) --- .../networking/v2/extensions/networkipavailabilities/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/networking/v2/extensions/networkipavailabilities/doc.go b/openstack/networking/v2/extensions/networkipavailabilities/doc.go index 9109473693..faadaa2227 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/doc.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/doc.go @@ -9,7 +9,7 @@ Example of Listing NetworkIPAvailabilities panic(err) } - allAvailabilities, err := subnetpools.ExtractSubnetPools(allPages) + allAvailabilities, err := networkipavailabilities.ExtractNetworkIPAvailabilities(allPages) if err != nil { panic(err) } From dbea4fcfb05bb26cfbeb896099845cc07a27df4f Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Thu, 29 Apr 2021 19:00:46 +0200 Subject: [PATCH 1242/2296] Add SCTP protocol on octavia listener (#2149) --- openstack/loadbalancer/v2/listeners/requests.go | 14 ++++++++------ openstack/loadbalancer/v2/listeners/results.go | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 07fa26848d..08a1f5823e 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -12,11 +12,13 @@ type Protocol string // Supported attributes for create/update operations. const ( - ProtocolTCP Protocol = "TCP" - ProtocolUDP Protocol = "UDP" - ProtocolPROXY Protocol = "PROXY" - ProtocolHTTP Protocol = "HTTP" - ProtocolHTTPS Protocol = "HTTPS" + ProtocolTCP Protocol = "TCP" + ProtocolUDP Protocol = "UDP" + ProtocolPROXY Protocol = "PROXY" + ProtocolHTTP Protocol = "HTTP" + ProtocolHTTPS Protocol = "HTTPS" + // Protocol SCTP requires octavia microversion 2.23 + ProtocolSCTP Protocol = "SCTP" ProtocolTerminatedHTTPS Protocol = "TERMINATED_HTTPS" ) @@ -88,7 +90,7 @@ type CreateOpts struct { // The load balancer on which to provision this listener. LoadbalancerID string `json:"loadbalancer_id,omitempty"` - // The protocol - can either be TCP, HTTP, HTTPS or TERMINATED_HTTPS. + // The protocol - can either be TCP, SCTP, HTTP, HTTPS or TERMINATED_HTTPS. Protocol Protocol `json:"protocol" required:"true"` // The port on which to listen for client traffic. diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 3271c6ae05..1d5761fb84 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -27,7 +27,7 @@ type Listener struct { // Human-readable description for the Listener. Description string `json:"description"` - // The protocol to loadbalance. A valid value is TCP, HTTP, or HTTPS. + // The protocol to loadbalance. A valid value is TCP, SCTP, HTTP, HTTPS or TERMINATED_HTTPS. Protocol string `json:"protocol"` // The port on which to listen to client traffic that is associated with the From 3e9de207d89a5fe520fc155bf8edd44e91a0fff8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 29 Apr 2021 11:01:40 -0600 Subject: [PATCH 1243/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 898a3239b7..0a62c6e2fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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) ## 0.17.0 (April 9, 2021) From 2e59567c532702dfa24b670f5bcd58ff1d93635c Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Thu, 29 Apr 2021 19:23:53 +0200 Subject: [PATCH 1244/2296] Add support for loadbalancer listener tls versions (#2150) --- .../openstack/loadbalancer/v2/loadbalancer.go | 5 +++++ .../loadbalancer/v2/listeners/requests.go | 17 +++++++++++++++ .../loadbalancer/v2/listeners/results.go | 3 +++ .../v2/listeners/testing/fixtures.go | 21 +++++++++++++------ .../v2/listeners/testing/requests_test.go | 3 +++ 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 944a2cdb9e..244d8d90b9 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -67,6 +67,9 @@ 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"} + createOpts := listeners.CreateOpts{ Name: listenerName, Description: listenerDescription, @@ -74,6 +77,7 @@ func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loa InsertHeaders: headers, Protocol: listeners.ProtocolHTTP, ProtocolPort: listenerPort, + TLSVersions: tlsVersions, } listener, err := listeners.Create(client, createOpts).Extract() @@ -93,6 +97,7 @@ func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loa th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolHTTP)) th.AssertEquals(t, listener.ProtocolPort, listenerPort) th.AssertDeepEquals(t, listener.InsertHeaders, headers) + th.AssertDeepEquals(t, listener.TLSVersions, tlsVersionsExp) return listener, nil } diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 08a1f5823e..c15d65dab0 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -22,6 +22,17 @@ const ( ProtocolTerminatedHTTPS Protocol = "TERMINATED_HTTPS" ) +// Type TLSVersion represents a tls version +type TLSVersion string + +const ( + TLSVersionSSLv3 TLSVersion = "SSLv3" + TLSVersionTLSv1 TLSVersion = "TLSv1" + TLSVersionTLSv1_1 TLSVersion = "TLSv1.1" + TLSVersionTLSv1_2 TLSVersion = "TLSv1.2" + TLSVersionTLSv1_3 TLSVersion = "TLSv1.3" +) + // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { @@ -153,6 +164,9 @@ type CreateOpts struct { // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs []string `json:"allowed_cidrs,omitempty"` + + // A list of TLS protocol versions. Available from microversion 2.17 + TLSVersions []TLSVersion `json:"tls_versions,omitempty"` } // ToListenerCreateMap builds a request body from CreateOpts. @@ -232,6 +246,9 @@ type UpdateOpts struct { // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs *[]string `json:"allowed_cidrs,omitempty"` + + // A list of TLS protocol versions. Available from microversion 2.17 + TLSVersions *[]TLSVersion `json:"tls_versions,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 1d5761fb84..be2a6dcfed 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -83,6 +83,9 @@ type Listener struct { // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs []string `json:"allowed_cidrs"` + + // A list of TLS protocol versions. Available from microversion 2.17 + TLSVersions []string `json:"tls_versions"` } type Stats struct { diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures.go b/openstack/loadbalancer/v2/listeners/testing/fixtures.go index b74f2638b9..3efdf0af31 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures.go @@ -29,7 +29,8 @@ const ListenersListBody = ` "allowed_cidrs": [ "192.0.2.0/24", "198.51.100.0/24" - ] + ], + "tls_versions": ["TLSv1.2", "TLSv1.3"] }, { "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -54,7 +55,8 @@ const ListenersListBody = ` "allowed_cidrs": [ "192.0.2.0/24", "198.51.100.0/24" - ] + ], + "tls_versions": ["TLSv1.2"] } ] } @@ -86,7 +88,8 @@ const SingleListenerBody = ` "allowed_cidrs": [ "192.0.2.0/24", "198.51.100.0/24" - ] + ], + "tls_versions": ["TLSv1.2"] } } ` @@ -114,7 +117,8 @@ const PostUpdateListenerBody = ` "insert_headers": { "X-Forwarded-For": "true", "X-Forwarded-Port": "false" - } + }, + "tls_versions": ["TLSv1.2", "TLSv1.3"] } } ` @@ -146,6 +150,7 @@ var ( DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, + TLSVersions: []string{"TLSv1.2", "TLSv1.3"}, } ListenerDb = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -166,6 +171,7 @@ var ( TimeoutTCPInspect: 0, InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, + TLSVersions: []string{"TLSv1.2"}, } ListenerUpdated = listeners.Listener{ ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", @@ -188,6 +194,7 @@ var ( "X-Forwarded-For": "true", "X-Forwarded-Port": "false", }, + TLSVersions: []string{"TLSv1.2", "TLSv1.3"}, } ListenerStatsTree = listeners.Stats{ ActiveConnections: 0, @@ -239,7 +246,8 @@ func HandleListenerCreationSuccessfully(t *testing.T, response string) { "allowed_cidrs": [ "192.0.2.0/24", "198.51.100.0/24" - ] + ], + "tls_versions": ["TLSv1.2"] } }`) @@ -289,7 +297,8 @@ func HandleListenerUpdateSuccessfully(t *testing.T) { "insert_headers": { "X-Forwarded-For": "true", "X-Forwarded-Port": "false" - } + }, + "tls_versions": ["TLSv1.2", "TLSv1.3"] } }`) diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 1cdb553ab1..21fc0a4bea 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -68,6 +68,7 @@ func TestCreateListener(t *testing.T) { ProtocolPort: 3306, InsertHeaders: map[string]string{"X-Forwarded-For": "true"}, AllowedCIDRs: []string{"192.0.2.0/24", "198.51.100.0/24"}, + TLSVersions: []listeners.TLSVersion{"TLSv1.2"}, }).Extract() th.AssertNoErr(t, err) @@ -134,6 +135,7 @@ func TestUpdateListener(t *testing.T) { "X-Forwarded-For": "true", "X-Forwarded-Port": "false", } + tlsVersions := []listeners.TLSVersion{"TLSv1.2", "TLSv1.3"} actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ Name: &name, ConnLimit: &i1001, @@ -143,6 +145,7 @@ func TestUpdateListener(t *testing.T) { TimeoutMemberConnect: &i181000, TimeoutTCPInspect: &i181000, InsertHeaders: &insertHeaders, + TLSVersions: &tlsVersions, }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From 0fc2c97ff6dae0f127c06db76a4cce21f4f5aa60 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 29 Apr 2021 11:27:43 -0600 Subject: [PATCH 1245/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a62c6e2fa..da1dcfda95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ 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) ## 0.17.0 (April 9, 2021) From d49f498c5057ef587521e6aeeb5f3f440264726d Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Mon, 10 May 2021 23:33:22 -0400 Subject: [PATCH 1246/2296] baremetal: add network_data to Node interface (#2154) This field was added in microversion 1.66 and appears in the Victoria release. Depends-On: https://github.com/theopenlab/openlab-zuul-jobs/pull/1147 --- openstack/baremetal/v1/nodes/requests.go | 3 +++ openstack/baremetal/v1/nodes/results.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 9648abe5c5..4a04f750b5 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -247,6 +247,9 @@ type CreateOpts struct { // A string or UUID of the tenant who owns the baremetal node. Owner string `json:"owner,omitempty"` + + // Static network configuration to use during deployment and cleaning. + NetworkData map[string]interface{} `json:"network_data,omitempty"` } // ToNodeCreateMap assembles a request body based on the contents of a CreateOpts. diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 70dc9a0447..caadd673aa 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -192,6 +192,9 @@ type Node struct { // A string or UUID of the tenant who owns the baremetal node. Owner string `json:"owner"` + + // Static network configuration to use during deployment and cleaning. + NetworkData map[string]interface{} `json:"network_data"` } // NodePage abstracts the raw results of making a List() request against From ec3f761dbd2a3bab5efefac50a7bae7bd7063873 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 10 May 2021 21:34:49 -0600 Subject: [PATCH 1247/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da1dcfda95..51d0aafec7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ IMPROVEMENTS * 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) ## 0.17.0 (April 9, 2021) From 91506b94ba5cdd29198b1dee99fe641b49f8823c Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Thu, 13 May 2021 08:50:03 +0900 Subject: [PATCH 1248/2296] Networking v2: Fix typo in results.go (#2156) Descripton -> Description --- openstack/networking/v2/extensions/security/rules/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index 3bf5501d92..52ac3f7a75 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -17,7 +17,7 @@ type SecGroupRule struct { // instance. An egress rule is applied to traffic leaving the instance. Direction string - // Descripton of the rule + // Description of the rule Description string `json:"description"` // Must be IPv4 or IPv6, and addresses represented in CIDR must match the From 12b4c582fb975a3ad5896a461400549277eda12c Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Thu, 13 May 2021 01:53:18 +0200 Subject: [PATCH 1249/2296] Add SCTP and PROXYV2 protocol for octavia pools (#2158) --- openstack/loadbalancer/v2/pools/requests.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index bf93656b02..0379d0385c 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -71,6 +71,10 @@ const ( ProtocolPROXY Protocol = "PROXY" ProtocolHTTP Protocol = "HTTP" ProtocolHTTPS Protocol = "HTTPS" + // Protocol PROXYV2 requires octavia microversion 2.22 + ProtocolPROXYV2 Protocol = "PROXYV2" + // Protocol SCTP requires octavia microversion 2.23 + ProtocolSCTP Protocol = "SCTP" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -88,7 +92,8 @@ type CreateOpts struct { LBMethod LBMethod `json:"lb_algorithm" required:"true"` // The protocol used by the pool members, you can use either - // ProtocolTCP, ProtocolUDP, ProtocolPROXY, ProtocolHTTP, or ProtocolHTTPS. + // ProtocolTCP, ProtocolUDP, ProtocolPROXY, ProtocolHTTP, ProtocolHTTPS, + // ProtocolSCTP or ProtocolPROXYV2. Protocol Protocol `json:"protocol" required:"true"` // The Loadbalancer on which the members of the pool will be associated with. From 2841b2e97e75d212b8c9002482a6c87f6d937c63 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 12 May 2021 17:54:30 -0600 Subject: [PATCH 1250/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51d0aafec7..4fe5d774c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ IMPROVEMENTS * 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) ## 0.17.0 (April 9, 2021) From cb7437a9f284c9731e784eb14d2546283e333b76 Mon Sep 17 00:00:00 2001 From: Abhinav Anand <6172555+abnvanand@users.noreply.github.com> Date: Tue, 18 May 2021 03:04:38 +0530 Subject: [PATCH 1251/2296] Add resource provider allocations API for placement (#2162) --- .../placement/v1/resourceproviders_test.go | 30 +++++++++ .../placement/v1/resourceproviders/doc.go | 7 ++ .../v1/resourceproviders/requests.go | 6 ++ .../placement/v1/resourceproviders/results.go | 22 +++++++ .../v1/resourceproviders/testing/fixtures.go | 65 +++++++++++++++++++ .../testing/requests_test.go | 11 ++++ .../placement/v1/resourceproviders/urls.go | 4 ++ 7 files changed, 145 insertions(+) diff --git a/acceptance/openstack/placement/v1/resourceproviders_test.go b/acceptance/openstack/placement/v1/resourceproviders_test.go index 52d2137800..0092b10ef1 100644 --- a/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -142,3 +142,33 @@ func TestResourceProviderTraits(t *testing.T) { tools.PrintResource(t, usage) } + +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() + 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() + th.AssertNoErr(t, err) + + // now get the allocations for the newly created resource provider + usage, err := resourceproviders.GetAllocations(client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, usage) +} diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 051d404dbd..1945958438 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -50,5 +50,12 @@ Example to get resource providers traits panic(err) } +Example to get resource providers allocations + + rp, err := resourceproviders.GetAllocations(placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } + */ package resourceproviders diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index f0da23f989..c2c9980838 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -108,6 +108,12 @@ func GetInventories(client *gophercloud.ServiceClient, resourceProviderID string return } +func GetAllocations(client *gophercloud.ServiceClient, resourceProviderID string) (r GetAllocationsResult) { + resp, err := client.Get(getResourceProviderAllocationsURL(client, resourceProviderID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + func GetTraits(client *gophercloud.ServiceClient, resourceProviderID string) (r GetTraitsResult) { resp, err := client.Get(getResourceProviderTraitsURL(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 0562451777..582cb86af7 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -47,11 +47,20 @@ type Inventory struct { Total int `json:"total"` } +type Allocation struct { + Resources map[string]int `json:"resources"` +} + type ResourceProviderInventories struct { ResourceProviderGeneration int `json:"resource_provider_generation"` Inventories map[string]Inventory `json:"inventories"` } +type ResourceProviderAllocations struct { + ResourceProviderGeneration int `json:"resource_provider_generation"` + Allocations map[string]Allocation `json:"allocations"` +} + type ResourceProviderTraits struct { ResourceProviderGeneration int `json:"resource_provider_generation"` Traits []string `json:"traits"` @@ -122,6 +131,19 @@ func (r GetInventoriesResult) Extract() (*ResourceProviderInventories, error) { return &s, err } +// GetAllocationsResult is the response of a Get allocations operations. Call its Extract method +// to interpret it as a ResourceProviderAllocations. +type GetAllocationsResult struct { + gophercloud.Result +} + +// Extract interprets a GetAllocationsResult as a ResourceProviderAllocations. +func (r GetAllocationsResult) Extract() (*ResourceProviderAllocations, error) { + var s ResourceProviderAllocations + err := r.ExtractInto(&s) + return &s, err +} + // GetTraitsResult is the response of a Get traits operations. Call its Extract method // to interpret it as a ResourceProviderTraits. type GetTraitsResult struct { diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures.go b/openstack/placement/v1/resourceproviders/testing/fixtures.go index 70d31ec145..29bf4c68fe 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures.go @@ -105,6 +105,32 @@ const InventoriesBody = ` } ` +const AllocationsBody = ` +{ + "allocations": { + "56785a3f-6f1c-4fec-af0b-0faf075b1fcb": { + "resources": { + "MEMORY_MB": 256, + "VCPU": 1 + } + }, + "9afd5aeb-d6b9-4dea-a588-1e6327a91834": { + "resources": { + "MEMORY_MB": 512, + "VCPU": 2 + } + }, + "9d16a611-e7f9-4ef3-be26-c61ed01ecefb": { + "resources": { + "MEMORY_MB": 1024, + "VCPU": 1 + } + } + }, + "resource_provider_generation": 12 +} +` + const TraitsBody = ` { "resource_provider_generation": 1, @@ -187,6 +213,30 @@ var ExpectedInventories = resourceproviders.ResourceProviderInventories{ }, } +var ExpectedAllocations = resourceproviders.ResourceProviderAllocations{ + ResourceProviderGeneration: 12, + Allocations: map[string]resourceproviders.Allocation{ + "56785a3f-6f1c-4fec-af0b-0faf075b1fcb": { + Resources: map[string]int{ + "MEMORY_MB": 256, + "VCPU": 1, + }, + }, + "9afd5aeb-d6b9-4dea-a588-1e6327a91834": { + Resources: map[string]int{ + "MEMORY_MB": 512, + "VCPU": 2, + }, + }, + "9d16a611-e7f9-4ef3-be26-c61ed01ecefb": { + Resources: map[string]int{ + "MEMORY_MB": 1024, + "VCPU": 1, + }, + }, + }, +} + var ExpectedTraits = resourceproviders.ResourceProviderTraits{ ResourceProviderGeneration: 1, Traits: []string{ @@ -250,6 +300,21 @@ func HandleResourceProviderGetInventories(t *testing.T) { }) } +func HandleResourceProviderGetAllocations(t *testing.T) { + allocationsTestUrl := fmt.Sprintf("/resource_providers/%s/allocations", ResourceProviderTestID) + + th.Mux.HandleFunc(allocationsTestUrl, + 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, AllocationsBody) + }) +} + func HandleResourceProviderGetTraits(t *testing.T) { traitsTestUrl := fmt.Sprintf("/resource_providers/%s/traits", ResourceProviderTestID) diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index bf3837b8b0..896df74812 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -78,6 +78,17 @@ func TestGetResourceProvidersInventories(t *testing.T) { th.AssertDeepEquals(t, ExpectedInventories, *actual) } +func TestGetResourceProvidersAllocations(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleResourceProviderGetAllocations(t) + + actual, err := resourceproviders.GetAllocations(fake.ServiceClient(), ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAllocations, *actual) +} + func TestGetResourceProvidersTraits(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 5f0ef2e347..6cc2a57c96 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -18,6 +18,10 @@ func getResourceProviderInventoriesURL(client *gophercloud.ServiceClient, resour return client.ServiceURL(apiName, resourceProviderID, "inventories") } +func getResourceProviderAllocationsURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID, "allocations") +} + func getResourceProviderTraitsURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "traits") } From 0be823b69be8891e4571548f528284c6995386e2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 17 May 2021 15:35:36 -0600 Subject: [PATCH 1252/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fe5d774c2..c656651ee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ IMPROVEMENTS * 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) ## 0.17.0 (April 9, 2021) From 965c5608e42226c8348eae18a9de019a7fc1d2a1 Mon Sep 17 00:00:00 2001 From: Bob Fournier Date: Thu, 20 May 2021 21:36:50 -0400 Subject: [PATCH 1253/2296] Add missing bios interface fields to Ironic API (#2164) The bios interface fields are in the Ironic API but were missing from the Ironic API documentation and not included in gophercloud. This change adds in the bios interface to the node create, node list, and node driver validation APIs. For reference, these are the Ironic updates to the api-ref documentation: https://review.opendev.org/c/openstack/ironic/+/757353 https://review.opendev.org/c/openstack/ironic/+/791493 https://review.opendev.org/c/openstack/ironic/+/792210 --- openstack/baremetal/v1/nodes/requests.go | 3 +++ openstack/baremetal/v1/nodes/results.go | 4 ++++ openstack/baremetal/v1/nodes/testing/fixtures.go | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 4a04f750b5..a25c0ea2c3 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -185,6 +185,9 @@ type CreateOpts struct { // Requires microversion 1.47 or later. AutomatedClean *bool `json:"automated_clean,omitempty"` + // The BIOS interface for a Node, e.g. “redfish”. + BIOSInterface string `json:"bios_interface,omitempty"` + // The boot interface for a Node, e.g. “pxe”. BootInterface string `json:"boot_interface,omitempty"` diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index caadd673aa..22bc598f3a 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -145,6 +145,9 @@ type Node struct { // For more details, see: https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html ResourceClass string `json:"resource_class"` + // BIOS interface for a Node, e.g. “redfish”. + BIOSInterface string `json:"bios_interface"` + // Boot interface for a Node, e.g. “pxe”. BootInterface string `json:"boot_interface"` @@ -301,6 +304,7 @@ type 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"` Console DriverValidation `json:"console"` Deploy DriverValidation `json:"deploy"` diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index a849d05ee1..18f3588ce7 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -638,6 +638,7 @@ var ( CleanStep: map[string]interface{}{}, DeployStep: map[string]interface{}{}, ResourceClass: "", + BIOSInterface: "no-bios", BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", @@ -656,6 +657,10 @@ var ( } NodeFooValidation = nodes.NodeValidation{ + BIOS: nodes.DriverValidation{ + Result: false, + Reason: "Driver ipmi does not support bios (disabled or not implemented).", + }, Boot: nodes.DriverValidation{ Result: false, Reason: "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", @@ -730,6 +735,7 @@ var ( CleanStep: map[string]interface{}{}, DeployStep: map[string]interface{}{}, ResourceClass: "", + BIOSInterface: "no-bios", BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", @@ -773,6 +779,7 @@ var ( CleanStep: map[string]interface{}{}, DeployStep: map[string]interface{}{}, ResourceClass: "", + BIOSInterface: "no-bios", BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", From e94bc6d0e997f14bb9152e51046668a61bfa9830 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 20 May 2021 19:38:54 -0600 Subject: [PATCH 1254/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c656651ee7..aadbb77d7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ IMPROVEMENTS * 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) ## 0.17.0 (April 9, 2021) From c0932594cee4a474889dbf062cf8f60bcc64f3d5 Mon Sep 17 00:00:00 2001 From: Bob Fournier Date: Wed, 26 May 2021 02:07:54 -0400 Subject: [PATCH 1255/2296] Add support for baremetal bios endpoint (#2171) For #2165. The baremetal bios endpoint has been available in the Ironic API since version 1.40 and is documented in the API reference here - https://docs.openstack.org/api-ref/baremetal/?expanded=#node-bios-nodes. It provides a list of all bios settings by node or a particular setting on a node. The ironic API code is here: https://github.com/openstack/ironic/blob/master/ironic/api/controllers/v1/bios.py#L57 --- openstack/baremetal/v1/nodes/requests.go | 18 +++++ openstack/baremetal/v1/nodes/results.go | 45 ++++++++++++- .../baremetal/v1/nodes/testing/fixtures.go | 67 +++++++++++++++++++ .../v1/nodes/testing/requests_test.go | 22 ++++++ openstack/baremetal/v1/nodes/urls.go | 8 +++ 5 files changed, 159 insertions(+), 1 deletion(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index a25c0ea2c3..33a649cd9b 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -639,3 +639,21 @@ func SetRAIDConfig(client *gophercloud.ServiceClient, id string, raidConfigOptsB _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Get the current BIOS Settings for the given Node. +func ListBIOSSettings(client *gophercloud.ServiceClient, id string) (r ListBIOSSettingsResult) { + resp, err := client.Get(biosListSettingsURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get one BIOS Setting for the given Node. +func GetBIOSSetting(client *gophercloud.ServiceClient, id string, setting string) (r GetBIOSSettingResult) { + resp, err := client.Get(biosGetSettingURL(client, id, setting), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, 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 22bc598f3a..6566b141a0 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -48,6 +48,23 @@ func ExtractNodesInto(r pagination.Page, v interface{}) error { return r.(NodePage).Result.ExtractIntoSlicePtr(v, "nodes") } +// Extract interprets a BIOSSettingsResult as an array of BIOSSetting structs, if possible. +func (r ListBIOSSettingsResult) Extract() ([]BIOSSetting, error) { + var s struct { + Settings []BIOSSetting `json:"bios"` + } + + err := r.ExtractInto(&s) + return s.Settings, err +} + +// Extract interprets a SingleBIOSSettingResult as a BIOSSetting struct, if possible. +func (r GetBIOSSettingResult) Extract() (*BIOSSetting, error) { + var s SingleBIOSSetting + err := r.ExtractInto(&s) + return &s.Setting, err +} + // Node represents a node in the OpenStack Bare Metal API. type Node struct { // Whether automated cleaning is enabled or disabled on this node. @@ -276,7 +293,7 @@ type BootDeviceResult struct { gophercloud.Result } -// BootDeviceResult is the response from a GetBootDevice operation. Call its Extract +// SetBootDeviceResult is the response from a SetBootDevice operation. Call its Extract // method to interpret it as a BootDeviceOpts struct. type SetBootDeviceResult struct { gophercloud.ErrResult @@ -294,6 +311,18 @@ type ChangePowerStateResult struct { gophercloud.ErrResult } +// ListBIOSSettingsResult is the response from a ListBIOSSettings operation. Call its Extract +// method to interpret it as an array of BIOSSetting structs. +type ListBIOSSettingsResult struct { + gophercloud.Result +} + +// GetBIOSSettingResult is the response from a GetBIOSSetting operation. Call its Extract +// method to interpret it as a BIOSSetting struct. +type GetBIOSSettingResult struct { + gophercloud.Result +} + // Each element in the response will contain a “result” variable, which will have a value of “true” or “false”, and // also potentially a reason. A value of nil indicates that the Node’s driver does not support that interface. type DriverValidation struct { @@ -317,6 +346,20 @@ type NodeValidation struct { Storage DriverValidation `json:"storage"` } +// A particular BIOS setting for a node in the OpenStack Bare Metal API. +type BIOSSetting struct { + + // Identifier for the BIOS setting. + Name string `json:"name"` + + // Value of the BIOS setting. + Value string `json:"value"` +} + +type SingleBIOSSetting struct { + Setting BIOSSetting +} + // ChangeStateResult is the response from any state change operation. Call its ExtractErr // method to determine if the call succeeded or failed. type ChangeStateResult struct { diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 18f3588ce7..157d338b61 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -604,6 +604,33 @@ const NodeProvisionStateConfigDriveBody = ` } ` +const NodeBIOSSettingsBody = ` +{ + "bios": [ + { + "name": "Proc1L2Cache", + "value": "10x256 KB" + }, + { + "name": "Proc1NumCores", + "value": "10" + }, + { + "name": "ProcVirtualization", + "value": "Enabled" + } + ] +} +` +const NodeSingleBIOSSettingBody = ` +{ + "Setting": { + "name": "ProcVirtualization", + "value": "Enabled" + } +} +` + var ( NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", @@ -811,6 +838,26 @@ var ( }, }, } + + NodeBIOSSettings = []nodes.BIOSSetting{ + { + Name: "Proc1L2Cache", + Value: "10x256 KB", + }, + { + Name: "Proc1NumCores", + Value: "10", + }, + { + Name: "ProcVirtualization", + Value: "Enabled", + }, + } + + NodeSingleBIOSSetting = nodes.BIOSSetting{ + Name: "ProcVirtualization", + Value: "Enabled", + } ) // HandleNodeListSuccessfully sets up the test server to respond to a server List request. @@ -1068,3 +1115,23 @@ func HandleSetRAIDConfigMaxSize(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) } + +func HandleListBIOSSettingsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/bios", 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, NodeBIOSSettingsBody) + }) +} + +func HandleGetBIOSSettingSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/bios/ProcVirtualization", 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, NodeSingleBIOSSettingBody) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 33f5817284..a912d32660 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -545,3 +545,25 @@ func TestToRAIDConfigMap(t *testing.T) { }) } } + +func TestListBIOSSettings(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListBIOSSettingsSuccessfully(t) + + c := client.ServiceClient() + actual, err := nodes.ListBIOSSettings(c, "1234asdf").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeBIOSSettings, actual) +} + +func TestGetBIOSSetting(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetBIOSSettingSuccessfully(t) + + c := client.ServiceClient() + actual, err := nodes.GetBIOSSetting(c, "1234asdf", "ProcVirtualization").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeSingleBIOSSetting, *actual) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index c7ef550365..44ce58f336 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -57,3 +57,11 @@ func provisionStateURL(client *gophercloud.ServiceClient, id string) string { func raidConfigURL(client *gophercloud.ServiceClient, id string) string { return statesResourceURL(client, id, "raid") } + +func biosListSettingsURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "bios") +} + +func biosGetSettingURL(client *gophercloud.ServiceClient, id string, setting string) string { + return client.ServiceURL("nodes", id, "bios", setting) +} From 34fae6ed30801505fc2f4157ecbafb733a91b4ba Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 26 May 2021 00:08:48 -0600 Subject: [PATCH 1256/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aadbb77d7a..357c0dd5e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ IMPROVEMENTS * 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) ## 0.17.0 (April 9, 2021) From 351ac5eeffc833f48205f436cac8f0864600ffe8 Mon Sep 17 00:00:00 2001 From: Benjamin Ziehms Date: Tue, 1 Jun 2021 17:10:07 +0200 Subject: [PATCH 1257/2296] Fix ErrUnexpectedResponseCode expected OK codes (#2173) When receiving a response from OpenStack HTTP response code is checked against a list of OK codes. This list is either specific to the call being made or generic according to the HTTP verb. In the latter scenario, when a wrong code is received, an error is returned that does not contains the actual list of expected OK codes. It is misleading because you may think the list of accepted codes is not correctly defined. Actual: 'Expected HTTP response code [] when accessing [DELETE http://127.0.0.1:35195], but got 200 instead OK' Expected: 'Expected HTTP response code [202 204] when accessing [DELETE http://127.0.0.1:35195], but got 200 instead OK' --- provider_client.go | 2 +- testing/provider_client_test.go | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/provider_client.go b/provider_client.go index f56c1375fe..7797af61c5 100644 --- a/provider_client.go +++ b/provider_client.go @@ -440,7 +440,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts respErr := ErrUnexpectedResponseCode{ URL: url, Method: method, - Expected: options.OkCodes, + Expected: okc, Actual: resp.StatusCode, Body: body, ResponseHeader: resp.Header, diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index a10f8348f3..f7278b024d 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -631,3 +631,23 @@ func TestRequestRetryContext(t *testing.T) { t.Logf("retryCounter: %d, p.MaxBackoffRetries: %d", retryCounter, p.MaxBackoffRetries-1) th.AssertEquals(t, retryCounter, p.MaxBackoffRetries-1) } + +func TestRequestWrongOkCode(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "OK") + // Returns 200 OK + })) + defer ts.Close() + + p := &gophercloud.ProviderClient{} + + _, err := p.Request("DELETE", ts.URL, &gophercloud.RequestOpts{}) + th.AssertErr(t, err) + if urErr, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { + // DELETE expects a 202 or 204 by default + // Make sure returned error contains the expected OK codes + th.AssertDeepEquals(t, []int{202, 204}, urErr.Expected) + } else { + t.Fatalf("expected error type gophercloud.ErrUnexpectedResponseCode but got %T", err) + } +} From 054cd0b5598a33fdfb5e37fa09e23cadc2390527 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 1 Jun 2021 09:11:21 -0600 Subject: [PATCH 1258/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 357c0dd5e8..906ca7dca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.18.0 (Unreleased) +BUG FIXES + +* Fixed expected OK codes to use default codes [GH-2173](https://github.com/gophercloud/gophercloud/pull/2173) + IMPROVEMENTS * Added `orchestration/v1/stacks.ListOpts.ShowHidden` [GH-2104](https://github.com/gophercloud/gophercloud/pull/2104) From 9abac83404d86f64e4a8e887afbc5e9c8a315945 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 7 Jun 2021 08:25:51 -0600 Subject: [PATCH 1259/2296] Acc Tests: Moving to Ubuntu Focal (#2169) * Acc Tests: Moving to Ubuntu Focal * Acc Tests: Disabling Block Storage v2 * Acc Tests: Disabling broken OpenLab test * Acc Tests: More Block Storage v2 to v3 changes * Acc Tests: don't install tempest for ironic * Acc Tests: Disabling TestVolumeActionsUploadImageDestroy as it is failing in OpenLab --- .zuul.yaml | 4 ++-- .../gophercloud-acceptance-test-ironic/run.yaml | 2 +- .../openstack/blockstorage/apiversions_test.go | 4 ++-- .../blockstorage/extensions/schedulerstats_test.go | 2 +- .../blockstorage/extensions/volumeactions_test.go | 14 ++++++++------ .../blockstorage/v3/volumeattachments_test.go | 2 ++ .../openstack/compute/v2/bootfromvolume_test.go | 4 ++-- .../openstack/compute/v2/volumeattach_test.go | 2 +- script/acceptancetest | 2 +- 9 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 5da8982915..8cba23cb0b 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -13,7 +13,7 @@ Run gophercloud acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml timeout: 18000 # 5 hours - nodeset: ubuntu-bionic + nodeset: ubuntu-focal - job: name: gophercloud-acceptance-test-ironic @@ -21,7 +21,7 @@ description: | Run gophercloud ironic acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml - nodeset: ubuntu-bionic + nodeset: ubuntu-focal - job: name: gophercloud-acceptance-test-ussuri diff --git a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml index b6fcfc32c6..901c29fc95 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml @@ -9,7 +9,7 @@ - 'ironic' - role: install-devstack environment: - 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,tempest' + 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' PROJECTS: 'openstack/ironic-python-agent-builder openstack/ironic' tasks: - name: Run ironic acceptance tests with gophercloud diff --git a/acceptance/openstack/blockstorage/apiversions_test.go b/acceptance/openstack/blockstorage/apiversions_test.go index 1adf5c3e8b..5c94d55928 100644 --- a/acceptance/openstack/blockstorage/apiversions_test.go +++ b/acceptance/openstack/blockstorage/apiversions_test.go @@ -11,7 +11,7 @@ import ( ) func TestAPIVersionsList(t *testing.T) { - client, err := clients.NewBlockStorageV2Client() + client, err := clients.NewBlockStorageV3Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } @@ -32,7 +32,7 @@ func TestAPIVersionsList(t *testing.T) { } func TestAPIVersionsGet(t *testing.T) { - client, err := clients.NewBlockStorageV2Client() + client, err := clients.NewBlockStorageV3Client() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) } diff --git a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go index b13cb0ec6b..5b4c35e2d3 100644 --- a/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -18,7 +18,7 @@ func TestSchedulerStatsList(t *testing.T) { clients.SkipRelease(t, "stable/ocata") clients.SkipRelease(t, "stable/pike") - blockClient, err := clients.NewBlockStorageV2Client() + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) listOpts := schedulerstats.ListOpts{ diff --git a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 9844410f2f..e5cc069ebc 100644 --- a/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -15,7 +15,9 @@ import ( ) func TestVolumeActionsUploadImageDestroy(t *testing.T) { - blockClient, err := clients.NewBlockStorageV2Client() + t.Skip("Currently failing in OpenLab") + + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) computeClient, err := clients.NewComputeV2Client() @@ -35,7 +37,7 @@ func TestVolumeActionsUploadImageDestroy(t *testing.T) { } func TestVolumeActionsAttachCreateDestroy(t *testing.T) { - blockClient, err := clients.NewBlockStorageV2Client() + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) computeClient, err := clients.NewComputeV2Client() @@ -59,7 +61,7 @@ func TestVolumeActionsAttachCreateDestroy(t *testing.T) { } func TestVolumeActionsReserveUnreserve(t *testing.T) { - client, err := clients.NewBlockStorageV2Client() + client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, client) @@ -72,7 +74,7 @@ func TestVolumeActionsReserveUnreserve(t *testing.T) { } func TestVolumeActionsExtendSize(t *testing.T) { - blockClient, err := clients.NewBlockStorageV2Client() + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) @@ -91,7 +93,7 @@ func TestVolumeActionsExtendSize(t *testing.T) { } func TestVolumeActionsImageMetadata(t *testing.T) { - blockClient, err := clients.NewBlockStorageV2Client() + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) @@ -103,7 +105,7 @@ func TestVolumeActionsImageMetadata(t *testing.T) { } func TestVolumeActionsSetBootable(t *testing.T) { - blockClient, err := clients.NewBlockStorageV2Client() + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolume(t, blockClient) diff --git a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index 0ec40ca09c..26921a0525 100644 --- a/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -12,6 +12,8 @@ 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") diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go index 9128747810..f98c561425 100644 --- a/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -88,7 +88,7 @@ func TestBootFromExistingVolume(t *testing.T) { computeClient, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - blockStorageClient, err := clients.NewBlockStorageV2Client() + blockStorageClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) volume, err := blockstorage.CreateVolumeFromImage(t, blockStorageClient) @@ -221,7 +221,7 @@ func TestAttachExistingVolume(t *testing.T) { computeClient, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - blockStorageClient, err := clients.NewBlockStorageV2Client() + blockStorageClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) choices, err := clients.AcceptanceTestChoicesFromEnv() diff --git a/acceptance/openstack/compute/v2/volumeattach_test.go b/acceptance/openstack/compute/v2/volumeattach_test.go index 022df830ca..e8d15786d1 100644 --- a/acceptance/openstack/compute/v2/volumeattach_test.go +++ b/acceptance/openstack/compute/v2/volumeattach_test.go @@ -17,7 +17,7 @@ func TestVolumeAttachAttachment(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - blockClient, err := clients.NewBlockStorageV2Client() + blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) server, err := CreateServer(t, client) diff --git a/script/acceptancetest b/script/acceptancetest index 766fc61a9d..6ad683bef3 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -28,8 +28,8 @@ acceptance/openstack/blockstorage/extensions # snapshots_test.go:21: Unable to retrieve snapshots: Resource not found # acceptance/openstack/blockstorage/v1 +# acceptance/openstack/blockstorage/v2 -acceptance/openstack/blockstorage/v2 acceptance/openstack/blockstorage/v3 # No suitable endpoint could be found in the service catalog. From b7d5b2cdd7ffc13e79d924f61571b0e5f74ec91c Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 8 Jun 2021 13:31:01 -0500 Subject: [PATCH 1260/2296] objects: partially revert e54703519 (#2160) * objects: partially revert e54703519 Since this change you are no longer able to create objects with a / in them to create pseudo-folders. This behavior is relied upon by kOps documented as broken in kubernetes/kops#9933. fixes #2159. * object storage: continuation of partially reverting e547035 Co-authored-by: Joe Topjian --- .../objectstorage/v1/containers_test.go | 2 +- .../objectstorage/v1/objects_test.go | 22 +++++++++---------- .../objectstorage/v1/containers/requests.go | 11 +++++----- .../objectstorage/v1/containers/results.go | 2 +- .../objectstorage/v1/objects/requests.go | 22 ++++++++----------- 5 files changed, 27 insertions(+), 32 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index c6f1b6a67c..1152fd3dc8 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -173,7 +173,7 @@ func TestBulkDeleteContainers(t *testing.T) { // Create a slice of random container names. cNames := make([]string, numContainers) for i := 0; i < numContainers; i++ { - cNames[i] = tools.RandomString("test&happy?-", 8) + cNames[i] = tools.RandomString("gophercloud-test-container-", 8) } // Create numContainers containers. diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index f3f77d36c1..1b61324665 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -290,22 +290,22 @@ func TestObjectsBulkDelete(t *testing.T) { } // Create a random subdirectory name. - cSubdir1 := tools.RandomString("don't worry & be happy?-", 8) - cSubdir2 := tools.RandomString("don't worry & be happy?-", 8) + cSubdir1 := tools.RandomString("test-subdir-", 8) + cSubdir2 := tools.RandomString("test-subdir-", 8) // Make a slice of length numObjects to hold the random object names. oNames1 := make([]string, numObjects) for i := 0; i < len(oNames1); i++ { - oNames1[i] = cSubdir1 + "/" + tools.RandomString("stranger?things-", 8) + oNames1[i] = cSubdir1 + "/" + tools.RandomString("test-object-", 8) } oNames2 := make([]string, numObjects) for i := 0; i < len(oNames2); i++ { - oNames2[i] = cSubdir2 + "/" + tools.RandomString("freddy's coming for you?-", 8) + oNames2[i] = cSubdir2 + "/" + tools.RandomString("test-object-", 8) } // Create a container to hold the test objects. - cName := tools.RandomString("test&happy?-", 8) + cName := tools.RandomString("test-container-", 8) _, err = containers.Create(client, cName, nil).Extract() th.AssertNoErr(t, err) @@ -327,12 +327,6 @@ func TestObjectsBulkDelete(t *testing.T) { th.AssertNoErr(t, res.Err) } - expectedResp := objects.BulkDeleteResponse{ - ResponseStatus: "200 OK", - Errors: [][]string{}, - NumberDeleted: numObjects * 2, - } - oContents2 := make([]*bytes.Buffer, numObjects) for i := 0; i < numObjects; i++ { oContents2[i] = bytes.NewBuffer([]byte(tools.RandomString("", 10))) @@ -344,6 +338,12 @@ func TestObjectsBulkDelete(t *testing.T) { } // Delete the objects after testing. + expectedResp := objects.BulkDeleteResponse{ + ResponseStatus: "200 OK", + Errors: [][]string{}, + NumberDeleted: numObjects * 2, + } + resp, err := objects.BulkDelete(client, cName, append(oNames1, oNames2...)).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, *resp, expectedResp) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 119e06997f..bea0d9575c 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -1,7 +1,6 @@ package containers import ( - "net/url" "strings" "github.com/gophercloud/gophercloud" @@ -108,7 +107,7 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB h[k] = v } } - resp, err := c.Request("PUT", createURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ + resp, err := c.Request("PUT", createURL(c, containerName), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) @@ -123,7 +122,7 @@ func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDelete // https://github.com/openstack/swift/blob/stable/train/swift/common/swob.py#L302 encodedContainers := make([]string, len(containers)) for i, v := range containers { - encodedContainers[i] = url.QueryEscape(v) + encodedContainers[i] = v } b := strings.NewReader(strings.Join(encodedContainers, "\n") + "\n") resp, err := c.Post(bulkDeleteURL(c), b, &r.Body, &gophercloud.RequestOpts{ @@ -139,7 +138,7 @@ 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, url.QueryEscape(containerName)), nil) + resp, err := c.Delete(deleteURL(c, containerName), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -202,7 +201,7 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB h[k] = v } } - resp, err := c.Request("POST", updateURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ + resp, err := c.Request("POST", updateURL(c, containerName), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) @@ -242,7 +241,7 @@ func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder h[k] = v } } - resp, err := c.Head(getURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ + resp, err := c.Head(getURL(c, containerName), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index 14e390541f..e032640d6f 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -72,7 +72,7 @@ func ExtractNames(page pagination.Page) ([]string, error) { names = append(names, container.Name) } return names, nil - case strings.HasPrefix(ct, "text/plain"): + case strings.HasPrefix(ct, "text/plain") || ct == "": names := make([]string, 0, 50) body := string(page.(ContainerPage).Body.([]uint8)) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 7d6eb4123a..e820026a2e 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -8,7 +8,6 @@ import ( "fmt" "io" "io/ioutil" - "net/url" "strings" "time" @@ -54,7 +53,7 @@ func (opts ListOpts) ToObjectListParams() (bool, string, error) { func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager { headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} - url := listURL(c, url.QueryEscape(containerName)) + url := listURL(c, containerName) if opts != nil { full, query, err := opts.ToObjectListParams() if err != nil { @@ -119,7 +118,7 @@ func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, er // To extract just the content, pass the DownloadResult response to the // ExtractContent function. func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) { - url := downloadURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) + url := downloadURL(c, containerName, objectName) h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectDownloadParams() @@ -225,7 +224,7 @@ 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, url.QueryEscape(containerName), url.QueryEscape(objectName)) + url := createURL(c, containerName, objectName) h := make(map[string]string) var b io.Reader if opts != nil { @@ -289,7 +288,7 @@ func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts C h[k] = v } - url := copyURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) + url := copyURL(c, containerName, objectName) resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, @@ -317,7 +316,7 @@ 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, url.QueryEscape(containerName), url.QueryEscape(objectName)) + url := deleteURL(c, containerName, objectName) if opts != nil { query, err := opts.ToObjectDeleteQuery() if err != nil { @@ -362,7 +361,7 @@ 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, url.QueryEscape(containerName), url.QueryEscape(objectName)) + url := getURL(c, containerName, objectName) h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectGetParams() @@ -434,7 +433,7 @@ func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts h[k] = v } } - url := updateURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) + url := updateURL(c, containerName, objectName) resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) @@ -489,7 +488,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin duration := time.Duration(opts.TTL) * time.Second expiry := date.Add(duration).Unix() - getHeader, err := containers.Get(c, url.QueryEscape(containerName), nil).Extract() + getHeader, err := containers.Get(c, containerName, nil).Extract() if err != nil { return "", err } @@ -521,10 +520,7 @@ func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string // https://github.com/openstack/swift/blob/stable/train/swift/common/swob.py#L302 encodedObjects := make([]string, len(objects)) for i, v := range objects { - encodedObjects[i] = strings.Join([]string{ - url.QueryEscape(container), - url.QueryEscape(v)}, - "/") + 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{ From 05c2cc82de67f340d06209fc3b1f5aadac9434c9 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 8 Jun 2021 12:33:18 -0600 Subject: [PATCH 1261/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 906ca7dca9..d3ab856883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,13 @@ ## 0.18.0 (Unreleased) +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. + 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 From 08e135ab594d230d9277ef73b8e0677c5c911e65 Mon Sep 17 00:00:00 2001 From: Bob Fournier Date: Thu, 10 Jun 2021 16:27:08 -0400 Subject: [PATCH 1262/2296] Add support for bios settings options to get additional fields (#2174) For #2166 A recent change to Ironic (https://review.opendev.org/c/openstack/ironic/+/786707) has added support for bios registry fields in the Ironic API. These can be retrieved using the `?detail=True` option. --- openstack/baremetal/v1/nodes/requests.go | 41 ++++- openstack/baremetal/v1/nodes/results.go | 31 ++++ .../baremetal/v1/nodes/testing/fixtures.go | 145 ++++++++++++++++++ .../v1/nodes/testing/requests_test.go | 28 +++- 4 files changed, 242 insertions(+), 3 deletions(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 33a649cd9b..7c9befb161 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -640,9 +640,46 @@ func SetRAIDConfig(client *gophercloud.ServiceClient, id string, raidConfigOptsB return } +// ListBIOSSettingsOptsBuilder allows extensions to add additional parameters to the +// ListBIOSSettings request. +type ListBIOSSettingsOptsBuilder interface { + ToListBIOSSettingsOptsQuery() (string, error) +} + +// ListBIOSSettingsOpts defines query options that can be passed to ListBIOSettings +type ListBIOSSettingsOpts struct { + // Provide additional information for the BIOS Settings + Detail bool `q:"detail"` + + // One or more fields to be returned in the response. + Fields []string `q:"fields"` +} + +// ToListBIOSSettingsOptsQuery formats a ListBIOSSettingsOpts into a query string +func (opts ListBIOSSettingsOpts) ToListBIOSSettingsOptsQuery() (string, error) { + if opts.Detail == true && len(opts.Fields) > 0 { + return "", fmt.Errorf("cannot have both fields and detail options for BIOS settings") + } + + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // Get the current BIOS Settings for the given Node. -func ListBIOSSettings(client *gophercloud.ServiceClient, id string) (r ListBIOSSettingsResult) { - resp, err := client.Get(biosListSettingsURL(client, id), &r.Body, &gophercloud.RequestOpts{ +// To use the opts requires microversion 1.74. +func ListBIOSSettings(client *gophercloud.ServiceClient, id string, opts ListBIOSSettingsOptsBuilder) (r ListBIOSSettingsResult) { + url := biosListSettingsURL(client, id) + if opts != nil { + + query, err := opts.ToListBIOSSettingsOptsQuery() + if err != nil { + r.Err = err + return + } + url += query + } + + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 6566b141a0..1d1e50ffc1 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -354,6 +354,37 @@ type BIOSSetting struct { // Value of the BIOS setting. Value string `json:"value"` + + // The following fields are returned in microversion 1.74 or later + // when using the `details` option + + // The type of setting - Enumeration, String, Integer, or Boolean. + AttributeType string `json:"attribute_type"` + + // The allowable value for an Enumeration type setting. + AllowableValues []string `json:"allowable_values"` + + // The lowest value for an Integer type setting. + LowerBound *int `json:"lower_bound"` + + // The highest value for an Integer type setting. + UpperBound *int `json:"upper_bound"` + + // Minimum length for a String type setting. + MinLength *int `json:"min_length"` + + // Maximum length for a String type setting. + MaxLength *int `json:"max_length"` + + // Whether or not this setting is read only. + ReadOnly *bool `json:"read_only"` + + // Whether or not a reset is required after changing this setting. + ResetRequired *bool `json:"reset_required"` + + // Whether or not this setting's value is unique to this node, e.g. + // a serial number. + Unique *bool `json:"unique"` } type SingleBIOSSetting struct { diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 157d338b61..39499e7bcd 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -622,6 +622,92 @@ const NodeBIOSSettingsBody = ` ] } ` + +const NodeDetailBIOSSettingsBody = ` +{ + "bios": [ + { + "created_at": "2021-05-11T21:33:44+00:00", + "updated_at": null, + "name": "Proc1L2Cache", + "value": "10x256 KB", + "attribute_type": "String", + "allowable_values": [], + "lower_bound": null, + "max_length": 16, + "min_length": 0, + "read_only": true, + "reset_required": null, + "unique": null, + "upper_bound": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d26115bf-1296-4ca8-8c86-6f310d8ec375/bios/Proc1L2Cache", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d26115bf-1296-4ca8-8c86-6f310d8ec375/bios/Proc1L2Cache", + "rel": "bookmark" + } + ] + }, + { + "created_at": "2021-05-11T21:33:44+00:00", + "updated_at": null, + "name": "Proc1NumCores", + "value": "10", + "attribute_type": "Integer", + "allowable_values": [], + "lower_bound": 0, + "max_length": null, + "min_length": null, + "read_only": true, + "reset_required": null, + "unique": null, + "upper_bound": 20, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d26115bf-1296-4ca8-8c86-6f310d8ec375/bios/Proc1NumCores", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d26115bf-1296-4ca8-8c86-6f310d8ec375/bios/Proc1NumCores", + "rel": "bookmark" + } + ] + }, + { + "created_at": "2021-05-11T21:33:44+00:00", + "updated_at": null, + "name": "ProcVirtualization", + "value": "Enabled", + "attribute_type": "Enumeration", + "allowable_values": [ + "Enabled", + "Disabled" + ], + "lower_bound": null, + "max_length": null, + "min_length": null, + "read_only": false, + "reset_required": null, + "unique": null, + "upper_bound": null, + "links": [ + { + "href": "http://ironic.example.com:6385/v1/nodes/d26115bf-1296-4ca8-8c86-6f310d8ec375/bios/ProcVirtualization", + "rel": "self" + }, + { + "href": "http://ironic.example.com:6385/nodes/d26115bf-1296-4ca8-8c86-6f310d8ec375/bios/ProcVirtualization", + "rel": "bookmark" + } + ] + } + ] +} +` + const NodeSingleBIOSSettingBody = ` { "Setting": { @@ -854,6 +940,55 @@ var ( }, } + iTrue = true + iFalse = false + minLength = 0 + maxLength = 16 + lowerBound = 0 + upperBound = 20 + + NodeDetailBIOSSettings = []nodes.BIOSSetting{ + { + Name: "Proc1L2Cache", + Value: "10x256 KB", + AttributeType: "String", + AllowableValues: []string{}, + LowerBound: nil, + UpperBound: nil, + MinLength: &minLength, + MaxLength: &maxLength, + ReadOnly: &iTrue, + ResetRequired: nil, + Unique: nil, + }, + { + Name: "Proc1NumCores", + Value: "10", + AttributeType: "Integer", + AllowableValues: []string{}, + LowerBound: &lowerBound, + UpperBound: &upperBound, + MinLength: nil, + MaxLength: nil, + ReadOnly: &iTrue, + ResetRequired: nil, + Unique: nil, + }, + { + Name: "ProcVirtualization", + Value: "Enabled", + AttributeType: "Enumeration", + AllowableValues: []string{"Enabled", "Disabled"}, + LowerBound: nil, + UpperBound: nil, + MinLength: nil, + MaxLength: nil, + ReadOnly: &iFalse, + ResetRequired: nil, + Unique: nil, + }, + } + NodeSingleBIOSSetting = nodes.BIOSSetting{ Name: "ProcVirtualization", Value: "Enabled", @@ -1126,6 +1261,16 @@ func HandleListBIOSSettingsSuccessfully(t *testing.T) { }) } +func HandleListDetailBIOSSettingsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/bios", 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, NodeDetailBIOSSettingsBody) + }) +} + func HandleGetBIOSSettingSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/bios/ProcVirtualization", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index a912d32660..46e02eb496 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -552,11 +552,26 @@ func TestListBIOSSettings(t *testing.T) { HandleListBIOSSettingsSuccessfully(t) c := client.ServiceClient() - actual, err := nodes.ListBIOSSettings(c, "1234asdf").Extract() + actual, err := nodes.ListBIOSSettings(c, "1234asdf", nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeBIOSSettings, actual) } +func TestListDetailBIOSSettings(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListDetailBIOSSettingsSuccessfully(t) + + opts := nodes.ListBIOSSettingsOpts{ + Detail: true, + } + + c := client.ServiceClient() + actual, err := nodes.ListBIOSSettings(c, "1234asdf", opts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeDetailBIOSSettings, actual) +} + func TestGetBIOSSetting(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -567,3 +582,14 @@ func TestGetBIOSSetting(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeSingleBIOSSetting, *actual) } + +func TestListBIOSSettingsOpts(t *testing.T) { + // Detail cannot take Fields + opts := nodes.ListBIOSSettingsOpts{ + Detail: true, + Fields: []string{"name", "value"}, + } + + _, err := opts.ToListBIOSSettingsOptsQuery() + th.AssertEquals(t, err.Error(), "cannot have both fields and detail options for BIOS settings") +} From e16426402d890d19cd27edb30bebf583e5113013 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 10 Jun 2021 14:30:21 -0600 Subject: [PATCH 1263/2296] Update CHANGELOG.md --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3ab856883..b16b1fabf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ 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) @@ -25,6 +27,16 @@ IMPROVEMENTS * 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) From b99817b08b69f18f1ac066fb41d4b90e4f2af396 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 11 Jun 2021 08:28:51 -0600 Subject: [PATCH 1264/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b16b1fabf3..e5c9c4e169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.18.0 (Unreleased) +## 0.19.0 (Unreleased) + +## 0.18.0 (June 11, 2021) NOTES / BREAKING CHANGES From 1fdbee54ef8759156473a36499454064bb513a47 Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Thu, 17 Jun 2021 05:42:13 +0200 Subject: [PATCH 1265/2296] blockstorage/v3: Add support for list qos (#2167) --- .../openstack/blockstorage/v3/qos_test.go | 34 +++++++++++++- openstack/blockstorage/v3/qos/doc.go | 18 ++++++++ openstack/blockstorage/v3/qos/requests.go | 45 +++++++++++++++++++ openstack/blockstorage/v3/qos/results.go | 38 +++++++++++++++- .../blockstorage/v3/qos/testing/fixtures.go | 45 +++++++++++++++++++ .../v3/qos/testing/requests_test.go | 38 ++++++++++++++++ openstack/blockstorage/v3/qos/urls.go | 4 ++ 7 files changed, 219 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/blockstorage/v3/qos_test.go b/acceptance/openstack/blockstorage/v3/qos_test.go index 95ce68be48..d3883795fb 100644 --- a/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/acceptance/openstack/blockstorage/v3/qos_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/gophercloud/gophercloud/acceptance/clients" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -14,7 +16,35 @@ func TestQoS(t *testing.T) { client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - qs, err := CreateQoS(t, client) + qos1, err := CreateQoS(t, client) th.AssertNoErr(t, err) - defer DeleteQoS(t, client, qs) + defer DeleteQoS(t, client, qos1) + + qos2, err := CreateQoS(t, client) + th.AssertNoErr(t, err) + defer DeleteQoS(t, client, qos2) + + listOpts := qos.ListOpts{ + Limit: 1, + } + + err = qos.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + actual, err := qos.ExtractQoS(page) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + + var found bool + for _, q := range actual { + if q.ID == qos1.ID || q.ID == qos2.ID { + found = true + } + } + + th.AssertEquals(t, found, true) + + return true, nil + }) + + th.AssertNoErr(t, err) + } diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 23894b651b..ba8c8b1935 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -32,5 +32,23 @@ Example to delete a QoS specification log.Fatal(err) } +Example to list QoS specifications + + listOpts := qos.ListOpts{} + + allPages, err := qos.List(client, listOpts).AllPages() + if err != nil { + panic(err) + } + + allQoS, err := qos.ExtractQoS(allPages) + if err != nil { + panic(err) + } + + for _, qos := range allQoS { + fmt.Printf("List: %+v\n", qos) + } + */ package qos diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index af02972f67..f2d05a3bfd 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -2,12 +2,19 @@ package qos import ( "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type CreateOptsBuilder interface { ToQoSCreateMap() (map[string]interface{}, error) } +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToQoSListQuery() (string, error) +} + type QoSConsumer string const ( @@ -98,3 +105,41 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +type ListOpts struct { + // Sort is Comma-separated list of sort keys and optional sort + // directions in the form of < key > [: < direction > ]. A valid + //direction is asc (ascending) or desc (descending). + Sort string `q:"sort"` + + // Marker and Limit control paging. + // Marker instructs List where to start listing from. + Marker string `q:"marker"` + + // Limit instructs List to refrain from sending excessively large lists of + // QoS. + Limit int `q:"limit"` +} + +// ToQoSListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToQoSListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List instructs OpenStack to provide a list of QoS. +// You may provide criteria by which List curtails its results for easier +// processing. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToQoSListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return QoSPage{pagination.LinkedPageBase{PageResult: r}} + }) +} diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 700616427c..7d46775805 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -1,6 +1,9 @@ package qos -import "github.com/gophercloud/gophercloud" +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) // QoS contains all the information associated with an OpenStack QoS specification. type QoS struct { @@ -39,3 +42,36 @@ type CreateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +type QoSPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a QoSPage contains any results. +func (page QoSPage) IsEmpty() (bool, error) { + qos, err := ExtractQoS(page) + return len(qos) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (page QoSPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"qos_specs_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractQoS provides access to the list of qos in a page acquired +// from the List operation. +func ExtractQoS(r pagination.Page) ([]QoS, error) { + var s struct { + QoSs []QoS `json:"qos_specs"` + } + err := (r.(QoSPage)).ExtractInto(&s) + return s.QoSs, err +} diff --git a/openstack/blockstorage/v3/qos/testing/fixtures.go b/openstack/blockstorage/v3/qos/testing/fixtures.go index 9cad5ff9a2..138dc86677 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures.go @@ -60,3 +60,48 @@ func MockDeleteResponse(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/qos-specs", 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") + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ` + { + "qos_specs": [ + { + "consumer": "back-end", + "id": "1", + "name": "foo", + "specs": {} + }, + { + "consumer": "front-end", + "id": "2", + "name": "bar", + "specs" : { + "read_iops_sec" : "20000" + } + } + + ], + "qos_specs_links": [ + { + "href": "%s/qos-specs?marker=2", + "rel": "next" + } + ] + } + `, th.Server.URL) + case "2": + fmt.Fprintf(w, `{ "qos_specs": [] }`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index 72e8ac4c7d..e89d9a913e 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -1,9 +1,11 @@ package testing import ( + "reflect" "testing" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" + "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" ) @@ -35,3 +37,39 @@ func TestDelete(t *testing.T) { res := qos.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteOpts{}) th.AssertNoErr(t, res.Err) } + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + pages := 0 + err := qos.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + pages++ + actual, err := qos.ExtractQoS(page) + if err != nil { + return false, err + } + + expected := []qos.QoS{ + {ID: "1", Consumer: "back-end", Name: "foo", Specs: map[string]string{}}, + {ID: "2", Consumer: "front-end", Name: "bar", Specs: map[string]string{ + "read_iops_sec": "20000", + }, + }, + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected %#v, but was %#v", expected, actual) + } + + return true, nil + }) + if err != nil { + t.Fatal(err) + } + if pages != 1 { + t.Errorf("Expected one page, got %d", pages) + } +} diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index 3d28c53822..09787ec29e 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -6,6 +6,10 @@ func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("qos-specs") } +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("qos-specs") +} + func deleteURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("qos-specs", id) } From 9d4ce8e73608e6ecdd1edb30b6a993c56a9bd9d3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 16 Jun 2021 21:43:09 -0600 Subject: [PATCH 1266/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5c9c4e169..4f44e90196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.19.0 (Unreleased) +IMPROVEMENTS + +* Added `blockstorage/v3/qos.List` [GH-2167](https://github.com/gophercloud/gophercloud/pull/2167) + ## 0.18.0 (June 11, 2021) NOTES / BREAKING CHANGES From 3ef6cff392d3b78b960387ada829ae43e43bc16f Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Tue, 22 Jun 2021 20:20:27 +0200 Subject: [PATCH 1267/2296] Add tag and delete_on_termination for nova volumeattach (#2177) --- acceptance/openstack/compute/v2/compute.go | 7 ++++++- .../openstack/compute/v2/volumeattach_test.go | 1 + .../v2/extensions/volumeattach/requests.go | 8 ++++++++ .../v2/extensions/volumeattach/results.go | 8 ++++++++ .../volumeattach/testing/fixtures.go | 8 ++++++-- .../volumeattach/testing/requests_test.go | 19 +++++++++++++------ 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 1ef17dd933..9cd082c40c 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -802,8 +802,13 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, // CreateVolumeAttachment will attach a volume to a server. An error will be // returned if the volume failed to attach. func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volume *volumes.Volume) (*volumeattach.VolumeAttachment, error) { + tag := tools.RandomString("ACPTTEST", 16) + dot := false + volumeAttachOptions := volumeattach.CreateOpts{ - VolumeID: volume.ID, + VolumeID: volume.ID, + Tag: tag, + DeleteOnTermination: dot, } t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) diff --git a/acceptance/openstack/compute/v2/volumeattach_test.go b/acceptance/openstack/compute/v2/volumeattach_test.go index e8d15786d1..d4b841f233 100644 --- a/acceptance/openstack/compute/v2/volumeattach_test.go +++ b/acceptance/openstack/compute/v2/volumeattach_test.go @@ -28,6 +28,7 @@ func TestVolumeAttachAttachment(t *testing.T) { th.AssertNoErr(t, err) defer bs.DeleteVolume(t, blockClient, volume) + client.Microversion = "2.79" volumeAttachment, err := CreateVolumeAttachment(t, client, blockClient, server, volume) th.AssertNoErr(t, err) defer DeleteVolumeAttachment(t, client, blockClient, server, volumeAttachment) diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go index 3b09cdef2b..8c5a2ba03e 100644 --- a/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/openstack/compute/v2/extensions/volumeattach/requests.go @@ -26,6 +26,14 @@ type CreateOpts struct { // VolumeID is the ID of the volume to attach to the instance. VolumeID string `json:"volumeId" required:"true"` + + // Tag is a device role tag that can be applied to a volume when attaching + // it to the VM. Requires 2.49 microversion + Tag string `json:"tag,omitempty"` + + // DeleteOnTermination specifies whether or not to delete the volume when the server + // is destroyed. Requires 2.79 microversion + DeleteOnTermination bool `json:"delete_on_termination,omitempty"` } // ToVolumeAttachmentCreateMap constructs a request body from CreateOpts. diff --git a/openstack/compute/v2/extensions/volumeattach/results.go b/openstack/compute/v2/extensions/volumeattach/results.go index 56d5034729..dec0e87eb5 100644 --- a/openstack/compute/v2/extensions/volumeattach/results.go +++ b/openstack/compute/v2/extensions/volumeattach/results.go @@ -19,6 +19,14 @@ type VolumeAttachment struct { // ServerID is the ID of the instance that has the volume attached. ServerID string `json:"serverId"` + + // Tag is a device role tag that can be applied to a volume when attaching + // it to the VM. Requires 2.70 microversion + Tag *string `json:"tag"` + + // DeleteOnTermination specifies whether or not to delete the volume when the server + // is destroyed. Requires 2.79 microversion + DeleteOnTermination *bool `json:"delete_on_termination"` } // VolumeAttachmentPage stores a single page all of VolumeAttachment diff --git a/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go b/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go index 4f996106e4..f0158689e2 100644 --- a/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go +++ b/openstack/compute/v2/extensions/volumeattach/testing/fixtures.go @@ -48,7 +48,9 @@ const CreateOutput = ` "device": "/dev/vdc", "id": "a26887c6-c47b-4654-abb5-dfadf7d3f804", "serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", - "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804" + "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804", + "tag": "foo", + "delete_on_termination": true } } ` @@ -86,7 +88,9 @@ func HandleCreateSuccessfully(t *testing.T) { { "volumeAttachment": { "volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804", - "device": "/dev/vdc" + "device": "/dev/vdc", + "tag": "foo", + "delete_on_termination": true } } `) diff --git a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go index 9486f9bb56..60d80ca125 100644 --- a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go +++ b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go @@ -29,12 +29,17 @@ var SecondVolumeAttachment = volumeattach.VolumeAttachment{ // from ListOutput, in the expected order. var ExpectedVolumeAttachmentSlice = []volumeattach.VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment} +var iTag = "foo" +var iTrue = true + //CreatedVolumeAttachment is the parsed result from CreatedOutput. var CreatedVolumeAttachment = volumeattach.VolumeAttachment{ - Device: "/dev/vdc", - ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", - ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", - VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", + Device: "/dev/vdc", + ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", + ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", + VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", + Tag: &iTag, + DeleteOnTermination: &iTrue, } func TestList(t *testing.T) { @@ -67,8 +72,10 @@ func TestCreate(t *testing.T) { serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" actual, err := volumeattach.Create(client.ServiceClient(), serverID, volumeattach.CreateOpts{ - Device: "/dev/vdc", - VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", + Device: "/dev/vdc", + VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", + Tag: iTag, + DeleteOnTermination: iTrue, }).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedVolumeAttachment, actual) From 1bcad6c25756b93ab1f2bd672da6a6ff3574f2be Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 22 Jun 2021 12:22:00 -0600 Subject: [PATCH 1268/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f44e90196..b93a4624fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ 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) ## 0.18.0 (June 11, 2021) From 55be98f0477400ad68bc488f185dd588259dbb78 Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Tue, 29 Jun 2021 02:16:03 +1200 Subject: [PATCH 1269/2296] Trove: Support to return Trove instance addresses (#2179) - API response example for getting trove instance : https://docs.openstack.org/api-ref/database/?expanded=show-database-instance-details-detail#id52 - Trove code for getting instance addresses: https://github.com/openstack/trove/blob/b050996b9f6df738a0f68ac36a5b5f17f8bb2bc2/trove/instance/models.py#L170 Closes: #2178 --- openstack/db/v1/instances/results.go | 15 +++- openstack/db/v1/instances/testing/fixtures.go | 77 ++++++++++++++++++- .../db/v1/instances/testing/requests_test.go | 2 +- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index e47a740ce9..3ac7b02480 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -39,6 +39,15 @@ type Fault struct { Details string } +// Address represents the IP address and its type to connect with the instance. +type Address struct { + // The address type, e.g public + Type string + + // The actual IP address + Address string +} + func (r *Fault) UnmarshalJSON(b []byte) error { type tmp Fault var s struct { @@ -76,7 +85,8 @@ type Instance struct { Hostname string // The IP addresses associated with the database instance - // Is empty if the instance has a hostname + // Is empty if the instance has a hostname. + // Deprecated in favor of Addresses. IP []string // Indicates the unique identifier for the instance resource. @@ -99,6 +109,9 @@ type Instance struct { // Indicates how the instance stores data. Datastore datastores.DatastorePartial + + // The instance addresses + Addresses []Address } func (r *Instance) UnmarshalJSON(b []byte) error { diff --git a/openstack/db/v1/instances/testing/fixtures.go b/openstack/db/v1/instances/testing/fixtures.go index 3555a008ae..c7e019e271 100644 --- a/openstack/db/v1/instances/testing/fixtures.go +++ b/openstack/db/v1/instances/testing/fixtures.go @@ -53,6 +53,54 @@ var instance = ` } ` +var instanceGet = ` +{ + "created": "` + timestamp + `", + "datastore": { + "type": "mysql", + "version": "5.6" + }, + "flavor": { + "id": "1", + "links": [ + { + "href": "https://openstack.example.com/v1.0/1234/flavors/1", + "rel": "self" + }, + { + "href": "https://openstack.example.com/v1.0/1234/flavors/1", + "rel": "bookmark" + } + ] + }, + "links": [ + { + "href": "https://openstack.example.com/v1.0/1234/instances/1", + "rel": "self" + } + ], + "id": "{instanceID}", + "name": "test", + "status": "ACTIVE", + "operating_status": "HEALTHY", + "updated": "` + timestamp + `", + "volume": { + "size": 1, + "used": 0.12 + }, + "addresses": [ + { + "address": "10.1.0.62", + "type": "private" + }, + { + "address": "172.24.5.114", + "type": "public" + } + ] +} +` + var createReq = ` { "instance": { @@ -149,7 +197,7 @@ var ( createResp = fmt.Sprintf(`{"instance": %s}`, instance) createWithFaultResp = fmt.Sprintf(`{"instance": %s}`, instanceWithFault) listInstancesResp = fmt.Sprintf(`{"instances":[%s]}`, instance) - getInstanceResp = createResp + getInstanceResp = fmt.Sprintf(`{"instance": %s}`, instanceGet) enableUserResp = `{"user":{"name":"root","password":"secretsecret"}}` isUserEnabledResp = `{"rootEnabled":true}` ) @@ -178,6 +226,33 @@ var expectedInstance = instances.Instance{ }, } +var expectedGetInstance = instances.Instance{ + Created: timeVal, + Updated: timeVal, + Flavor: instances.Flavor{ + ID: "1", + Links: []gophercloud.Link{ + {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "self"}, + {Href: "https://openstack.example.com/v1.0/1234/flavors/1", Rel: "bookmark"}, + }, + }, + ID: instanceID, + Links: []gophercloud.Link{ + {Href: "https://openstack.example.com/v1.0/1234/instances/1", Rel: "self"}, + }, + Name: "test", + Status: "ACTIVE", + Volume: instances.Volume{Size: 1, Used: 0.12}, + Datastore: datastores.DatastorePartial{ + Type: "mysql", + Version: "5.6", + }, + Addresses: []instances.Address{ + {Type: "private", Address: "10.1.0.62"}, + {Type: "public", Address: "172.24.5.114"}, + }, +} + var expectedInstanceWithFault = instances.Instance{ Created: timeVal, Updated: timeVal, diff --git a/openstack/db/v1/instances/testing/requests_test.go b/openstack/db/v1/instances/testing/requests_test.go index 6e6c4f127e..815793cbb6 100644 --- a/openstack/db/v1/instances/testing/requests_test.go +++ b/openstack/db/v1/instances/testing/requests_test.go @@ -101,7 +101,7 @@ func TestGetInstance(t *testing.T) { instance, err := instances.Get(fake.ServiceClient(), instanceID).Extract() th.AssertNoErr(t, err) - th.AssertDeepEquals(t, &expectedInstance, instance) + th.AssertDeepEquals(t, &expectedGetInstance, instance) } func TestDeleteInstance(t *testing.T) { From c9471fccb2bb90d41832195b1134ac1c6f50f2bb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 28 Jun 2021 08:17:03 -0600 Subject: [PATCH 1270/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b93a4624fa..c41e0a4b29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ IMPROVEMENTS * 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) ## 0.18.0 (June 11, 2021) From 733e655d1975a9572dd753e126e845e81e24216c Mon Sep 17 00:00:00 2001 From: TonyZhang Date: Thu, 1 Jul 2021 21:54:24 +0800 Subject: [PATCH 1271/2296] nova: support query hosts with availability-zone (#2098) * support query hosts with availability-zone * support query hosts with availability-zone Co-authored-by: zhangzetao --- openstack/compute/v2/servers/requests.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 4e6042409e..6da3da2e3a 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -83,6 +83,9 @@ type ListOpts struct { // This requires the client to be set to microversion 2.26 or later. // NotTagsAny filters on specific server tags. At least one of the tags must be absent for the server. NotTagsAny string `q:"not-tags-any"` + + // Display servers based on their availability zone (Admin only until microversion 2.82). + AvailabilityZone string `q:"availability_zone"` } // ToServerListQuery formats a ListOpts into a query string. From a6f6d5a2ebe01a8ecdbebc09b2539a2ad2329dfa Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 1 Jul 2021 07:55:13 -0600 Subject: [PATCH 1272/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c41e0a4b29..b6ef0a1a4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ IMPROVEMENTS * 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) ## 0.18.0 (June 11, 2021) From 8c51993c555b2259b8fed78b9f2c4619572d654d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 2 Jul 2021 07:32:41 -0600 Subject: [PATCH 1273/2296] Compute v2: Add the user id to the get create and delete keypair (#2186) * Add the user id to the get create and delete keypair * Fix the build * Code review changes * Compute v2: Refactoring keypairs and UserID * Compute v2: Removing WithOpts functions * Compute v2: Adding ListOpts to keypairs.List * Compute v2: Updating inline doc for keypairs.CreateOpts.UserID Co-authored-by: Bernardo Pastorelli <13519917+randomswdev@users.noreply.github.com> --- acceptance/openstack/compute/v2/compute.go | 2 +- .../openstack/compute/v2/keypairs_test.go | 70 +++++++++++- .../compute/v2/extensions/keypairs/doc.go | 53 ++++++++- .../v2/extensions/keypairs/requests.go | 102 ++++++++++++++++-- .../extensions/keypairs/testing/fixtures.go | 90 +++++++++++++++- .../keypairs/testing/requests_test.go | 46 +++++++- 6 files changed, 341 insertions(+), 22 deletions(-) diff --git a/acceptance/openstack/compute/v2/compute.go b/acceptance/openstack/compute/v2/compute.go index 9cd082c40c..4ee2c5fde8 100644 --- a/acceptance/openstack/compute/v2/compute.go +++ b/acceptance/openstack/compute/v2/compute.go @@ -875,7 +875,7 @@ func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingI // the keypair failed to be deleted. This works best when used as a deferred // function. func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) { - err := keypairs.Delete(client, keyPair.Name).ExtractErr() + err := keypairs.Delete(client, keyPair.Name, nil).ExtractErr() if err != nil { t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err) } diff --git a/acceptance/openstack/compute/v2/keypairs_test.go b/acceptance/openstack/compute/v2/keypairs_test.go index d2df89aad2..723bae2fe5 100644 --- a/acceptance/openstack/compute/v2/keypairs_test.go +++ b/acceptance/openstack/compute/v2/keypairs_test.go @@ -6,6 +6,7 @@ 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/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" @@ -15,7 +16,7 @@ import ( const keyName = "gophercloud_test_key_pair" -func TestKeypairsParse(t *testing.T) { +func TestKeyPairsParse(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") @@ -35,7 +36,7 @@ func TestKeypairsParse(t *testing.T) { tools.PrintResource(t, keyPair) } -func TestKeypairsCreateDelete(t *testing.T) { +func TestKeyPairsCreateDelete(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) @@ -45,7 +46,7 @@ func TestKeypairsCreateDelete(t *testing.T) { tools.PrintResource(t, keyPair) - allPages, err := keypairs.List(client).AllPages() + allPages, err := keypairs.List(client, nil).AllPages() th.AssertNoErr(t, err) allKeys, err := keypairs.ExtractKeyPairs(allPages) @@ -63,7 +64,7 @@ func TestKeypairsCreateDelete(t *testing.T) { th.AssertEquals(t, found, true) } -func TestKeypairsImportPublicKey(t *testing.T) { +func TestKeyPairsImportPublicKey(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) @@ -77,7 +78,7 @@ func TestKeypairsImportPublicKey(t *testing.T) { tools.PrintResource(t, keyPair) } -func TestKeypairsServerCreateWithKey(t *testing.T) { +func TestKeyPairsServerCreateWithKey(t *testing.T) { clients.RequireLong(t) client, err := clients.NewComputeV2Client() @@ -99,3 +100,62 @@ func TestKeypairsServerCreateWithKey(t *testing.T) { th.AssertEquals(t, server.KeyName, keyPair.Name) } + +func TestKeyPairsCreateDeleteByID(t *testing.T) { + clients.RequireAdmin(t) + + identityClient, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + computeClient.Microversion = "2.10" + + user, err := identity.CreateUser(t, identityClient, nil) + th.AssertNoErr(t, err) + defer identity.DeleteUser(t, identityClient, user.ID) + + keyPairName := tools.RandomString("keypair_", 5) + createOpts := keypairs.CreateOpts{ + Name: keyPairName, + UserID: user.ID, + } + + keyPair, err := keypairs.Create(computeClient, createOpts).Extract() + th.AssertNoErr(t, err) + + getOpts := keypairs.GetOpts{ + UserID: user.ID, + } + + newKeyPair, err := keypairs.Get(computeClient, keyPair.Name, getOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, keyPair.Name, newKeyPair.Name) + + listOpts := keypairs.ListOpts{ + UserID: user.ID, + } + + allPages, err := keypairs.List(computeClient, listOpts).AllPages() + th.AssertNoErr(t, err) + + allKeys, err := keypairs.ExtractKeyPairs(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, kp := range allKeys { + if kp.Name == keyPair.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + + deleteOpts := keypairs.DeleteOpts{ + UserID: user.ID, + } + + err = keypairs.Delete(computeClient, keyPair.Name, deleteOpts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go index 24c4607722..15173bd76b 100644 --- a/openstack/compute/v2/extensions/keypairs/doc.go +++ b/openstack/compute/v2/extensions/keypairs/doc.go @@ -4,7 +4,29 @@ servers with a specified key pair. Example to List Key Pairs - allPages, err := keypairs.List(computeClient).AllPages() + allPages, err := keypairs.List(computeClient, nil).AllPages() + if err != nil { + panic(err) + } + + allKeyPairs, err := keypairs.ExtractKeyPairs(allPages) + if err != nil { + panic(err) + } + + for _, kp := range allKeyPairs { + fmt.Printf("%+v\n", kp) + } + +Example to List Key Pairs using microversion 2.10 or greater + + client.Microversion = "2.10" + + listOpts := keypairs.ListOpts{ + UserID: "user-id", + } + + allPages, err := keypairs.List(computeClient, listOpts).AllPages() if err != nil { panic(err) } @@ -45,7 +67,20 @@ Example to Import a Key Pair Example to Delete a Key Pair - err := keypairs.Delete(computeClient, "keypair-name").ExtractErr() + err := keypairs.Delete(computeClient, "keypair-name", nil).ExtractErr() + if err != nil { + panic(err) + } + +Example to Delete a Key Pair owned by a certain user using microversion 2.10 or greater + + client.Microversion = "2.10" + + deleteOpts := keypairs.DeleteOpts{ + UserID: "user-id", + } + + err := keypairs.Delete(client, "keypair-name", deleteOpts).ExtractErr() if err != nil { panic(err) } @@ -67,5 +102,19 @@ Example to Create a Server With a Key Pair if err != nil { panic(err) } + +Example to Get a Key Pair owned by a certain user using microversion 2.10 or greater + + client.Microversion = "2.10" + + getOpts := keypairs.GetOpts{ + UserID: "user-id", + } + + keypair, err := keypairs.Get(computeClient, "keypair-name", getOpts).Extract() + if err != nil { + panic(err) + } + */ package keypairs diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go index 39056fdc70..9b0357390d 100644 --- a/openstack/compute/v2/extensions/keypairs/requests.go +++ b/openstack/compute/v2/extensions/keypairs/requests.go @@ -31,9 +31,36 @@ func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { return base, nil } +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToKeyPairListQuery() (string, error) +} + +// ListOpts enables listing KeyPairs based on specific attributes. +type ListOpts struct { + // UserID is the user ID that owns the key pair. + // This requires microversion 2.10 or higher. + UserID string `q:"user_id"` +} + +// ToKeyPairListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToKeyPairListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // List returns a Pager that allows you to iterate over a collection of KeyPairs. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + if opts != nil { + query, err := opts.ToKeyPairListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return KeyPairPage{pagination.SinglePageBase(r)} }) } @@ -49,6 +76,11 @@ type CreateOpts struct { // Name is a friendly name to refer to this KeyPair in other services. Name string `json:"name" required:"true"` + // UserID [optional] is the user_id for a keypair. + // This allows administrative users to upload keys for other users than themselves. + // This requires microversion 2.10 or higher. + UserID string `json:"user_id,omitempty"` + // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. // If provided, this key will be imported and no new key will be created. PublicKey string `json:"public_key,omitempty"` @@ -74,16 +106,74 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } +// GetOptsBuilder allows extensions to add additional parameters to the +// Get request. +type GetOptsBuilder interface { + ToKeyPairGetQuery() (string, error) +} + +// GetOpts enables retrieving KeyPairs based on specific attributes. +type GetOpts struct { + // UserID is the user ID that owns the key pair. + // This requires microversion 2.10 or higher. + UserID string `q:"user_id"` +} + +// ToKeyPairGetQuery formats a GetOpts into a query string. +func (opts GetOpts) ToKeyPairGetQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // Get returns public data about a previously uploaded KeyPair. -func Get(client *gophercloud.ServiceClient, name string) (r GetResult) { - resp, err := client.Get(getURL(client, name), &r.Body, nil) +func Get(client *gophercloud.ServiceClient, name string, opts GetOptsBuilder) (r GetResult) { + url := getURL(client, name) + if opts != nil { + query, err := opts.ToKeyPairGetQuery() + if err != nil { + r.Err = err + return + } + url += query + } + + resp, err := client.Get(url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// DeleteOptsBuilder allows extensions to add additional parameters to the +// Delete request. +type DeleteOptsBuilder interface { + ToKeyPairDeleteQuery() (string, error) +} + +// DeleteOpts enables deleting KeyPairs based on specific attributes. +type DeleteOpts struct { + // UserID is the user ID of the user that owns the key pair. + // This requires microversion 2.10 or higher. + UserID string `q:"user_id"` +} + +// ToKeyPairDeleteQuery formats a DeleteOpts into a query string. +func (opts DeleteOpts) ToKeyPairDeleteQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // Delete requests the deletion of a previous stored KeyPair from the server. -func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, name), nil) +func Delete(client *gophercloud.ServiceClient, name string, opts DeleteOptsBuilder) (r DeleteResult) { + url := deleteURL(client, name) + if opts != nil { + query, err := opts.ToKeyPairDeleteQuery() + if err != nil { + r.Err = err + return + } + url += query + } + + resp, err := client.Delete(url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/keypairs/testing/fixtures.go b/openstack/compute/v2/extensions/keypairs/testing/fixtures.go index dc716d8db4..ae62051e6c 100644 --- a/openstack/compute/v2/extensions/keypairs/testing/fixtures.go +++ b/openstack/compute/v2/extensions/keypairs/testing/fixtures.go @@ -18,14 +18,16 @@ const ListOutput = ` "keypair": { "fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", "name": "firstkey", - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n" + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", + "user_id": "fake" } }, { "keypair": { "fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", "name": "secondkey", - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n" + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", + "user_id": "fake" } } ] @@ -38,7 +40,20 @@ const GetOutput = ` "keypair": { "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", "name": "firstkey", - "fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a" + "fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", + "user_id": "fake" + } +} +` + +// GetOutputOtherUser is a sample response to a Get call for another user. +const GetOutputOtherUser = ` +{ + "keypair": { + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", + "name": "firstkey", + "fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", + "user_id": "fake2" } } ` @@ -56,6 +71,19 @@ const CreateOutput = ` } ` +// CreateOutput is a sample response to a Create call. +const CreateOutputOtherUser = ` +{ + "keypair": { + "fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", + "name": "createdkey", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n", + "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", + "user_id": "fake2" + } +} +` + // ImportOutput is a sample response to a Create call that provides its own public key. const ImportOutput = ` { @@ -73,6 +101,15 @@ var FirstKeyPair = keypairs.KeyPair{ Name: "firstkey", Fingerprint: "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", + UserID: "fake", +} + +// FirstKeyPair is the first result in ListOutput. +var FirstKeyPairOtherUser = keypairs.KeyPair{ + Name: "firstkey", + Fingerprint: "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", + PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", + UserID: "fake2", } // SecondKeyPair is the second result in ListOutput. @@ -80,6 +117,7 @@ var SecondKeyPair = keypairs.KeyPair{ Name: "secondkey", Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", + UserID: "fake", } // ExpectedKeyPairSlice is the slice of results that should be parsed from ListOutput, in the expected @@ -95,6 +133,15 @@ var CreatedKeyPair = keypairs.KeyPair{ UserID: "fake", } +// CreatedKeyPairOtherUser is the parsed result from CreatedOutput. +var CreatedKeyPairOtherUser = keypairs.KeyPair{ + Name: "createdkey", + Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", + PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", + PrivateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n", + UserID: "fake2", +} + // ImportedKeyPair is the parsed result from ImportOutput. var ImportedKeyPair = keypairs.KeyPair{ Name: "importedkey", @@ -120,8 +167,15 @@ func HandleGetSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + if r.URL.Query().Get("user_id") == "fake2" { + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, GetOutputOtherUser) + + } else { + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, GetOutput) + + } }) } @@ -138,6 +192,19 @@ func HandleCreateSuccessfully(t *testing.T) { }) } +// HandleCreateSuccessfullyOtherUser configures the test server to respond to a Create request for a new +// keypair called "createdkey" for another user, different than the current one. +func HandleCreateSuccessfullyOtherUser(t *testing.T) { + th.Mux.HandleFunc("/os-keypairs", 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, `{ "keypair": { "name": "createdkey", "user_id": "fake2" } }`) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, CreateOutputOtherUser) + }) +} + // HandleImportSuccessfully configures the test server to respond to an Import request for an // existing keypair called "importedkey". func HandleImportSuccessfully(t *testing.T) { @@ -164,6 +231,19 @@ func HandleDeleteSuccessfully(t *testing.T) { th.Mux.HandleFunc("/os-keypairs/deletedkey", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "DELETE") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.AssertEquals(t, r.Form.Get("user_id"), "") + + w.WriteHeader(http.StatusAccepted) + }) +} + +// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a +// keypair called "deletedkey" for another user. +func HandleDeleteSuccessfullyOtherUser(t *testing.T) { + th.Mux.HandleFunc("/os-keypairs/deletedkey", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestFormValues(t, r, map[string]string{"user_id": "fake2"}) w.WriteHeader(http.StatusAccepted) }) diff --git a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go b/openstack/compute/v2/extensions/keypairs/testing/requests_test.go index 1e05e6687e..74303dc2b4 100644 --- a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go +++ b/openstack/compute/v2/extensions/keypairs/testing/requests_test.go @@ -15,7 +15,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := keypairs.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := keypairs.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := keypairs.ExtractKeyPairs(page) th.AssertNoErr(t, err) @@ -39,6 +39,19 @@ func TestCreate(t *testing.T) { th.CheckDeepEquals(t, &CreatedKeyPair, actual) } +func TestCreateOtherUser(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSuccessfullyOtherUser(t) + + actual, err := keypairs.Create(client.ServiceClient(), keypairs.CreateOpts{ + Name: "createdkey", + UserID: "fake2", + }).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &CreatedKeyPairOtherUser, actual) +} + func TestImport(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -57,16 +70,43 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := keypairs.Get(client.ServiceClient(), "firstkey").Extract() + actual, err := keypairs.Get(client.ServiceClient(), "firstkey", nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstKeyPair, actual) } +func TestGetOtherUser(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + getOpts := keypairs.GetOpts{ + UserID: "fake2", + } + + actual, err := keypairs.Get(client.ServiceClient(), "firstkey", getOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &FirstKeyPairOtherUser, actual) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := keypairs.Delete(client.ServiceClient(), "deletedkey").ExtractErr() + err := keypairs.Delete(client.ServiceClient(), "deletedkey", nil).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDeleteOtherUser(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfullyOtherUser(t) + + deleteOpts := keypairs.DeleteOpts{ + UserID: "fake2", + } + + err := keypairs.Delete(client.ServiceClient(), "deletedkey", deleteOpts).ExtractErr() th.AssertNoErr(t, err) } From 6dceb43bcc6bd0df2ef2c454cd0a4bc45afe5959 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 2 Jul 2021 07:36:55 -0600 Subject: [PATCH 1274/2296] Update CHANGELOG.md --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6ef0a1a4a..8afb306f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## 0.19.0 (Unreleased) +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) + IMPROVEMENTS * Added `blockstorage/v3/qos.List` [GH-2167](https://github.com/gophercloud/gophercloud/pull/2167) @@ -9,6 +15,9 @@ IMPROVEMENTS * 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) ## 0.18.0 (June 11, 2021) From 1f08945af63ef941fdfeb769e54a01c68aba5196 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 5 Jul 2021 09:01:35 -0600 Subject: [PATCH 1275/2296] Object Storage v1: Adding Timestamp to GetHeader results (#2185) * Object Storage v1: Adding Timestamp to GetHeader results * Object Storage v1: Fixing unit test. Changing Timestamp parsing method --- acceptance/openstack/objectstorage/v1/containers_test.go | 5 +++++ openstack/objectstorage/v1/containers/results.go | 4 +++- .../objectstorage/v1/containers/testing/requests_test.go | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index 1152fd3dc8..9c61dfe24a 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -110,6 +110,11 @@ func TestContainers(t *testing.T) { t.Errorf("Expected custom metadata with key: %s", k) } } + + // Retrieve a container's timestamp + cHeaders, err := containers.Get(client, cNames[0], getOpts).Extract() + th.AssertNoErr(t, err) + t.Logf("Container: Name [%s] Timestamp: [%f]\n", cNames[0], cHeaders.Timestamp) } func TestListAllContainers(t *testing.T) { diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index e032640d6f..e6e6a0487b 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -28,7 +28,7 @@ type ContainerPage struct { pagination.MarkerPageBase } -//IsEmpty returns true if a ListResult contains no container names. +// IsEmpty returns true if a ListResult contains no container names. func (r ContainerPage) IsEmpty() (bool, error) { names, err := ExtractNames(r) return len(names) == 0, err @@ -104,6 +104,7 @@ type GetHeader struct { StoragePolicy string `json:"X-Storage-Policy"` 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"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { @@ -114,6 +115,7 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { Read string `json:"X-Container-Read"` Date gophercloud.JSONRFC1123 `json:"Date"` } + err := json.Unmarshal(b, &s) if err != nil { return err diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index f18965fdf0..4d9263d646 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -157,6 +157,7 @@ func TestGetContainer(t *testing.T) { TransID: "tx554ed59667a64c61866f1-0057b4ba37", Write: []string{"test2", "user4"}, StoragePolicy: "test_policy", + Timestamp: 1471298837.95721, } actual, err := res.Extract() th.AssertNoErr(t, err) From 932f0916e113975e4d34bcd1d2dce9c01aa96570 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 5 Jul 2021 09:02:46 -0600 Subject: [PATCH 1276/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8afb306f5d..fd30f63220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ IMPROVEMENTS * 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) ## 0.18.0 (June 11, 2021) From 69ecde88334f47b0fc5797116ed17eb286ec4a06 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 10 Jul 2021 00:12:58 -0600 Subject: [PATCH 1277/2296] Compute v2: Implement Hypervisor request options (#2187) * Implement Hypervisor request options Signed-off-by: Kien Nguyen * Compute v2: Acceptance tests for hypervisors.List * Compute v2: Changing type of hypervisors.Hypervisor.Servers This commit changes hypervisors.Hypervisor.Servers from []*Server to *[]Server to better conform with microversioned result fields. * Compute v2: Doc updates for hypervisors extension Co-authored-by: Kien Nguyen --- .../openstack/compute/v2/aggregates_test.go | 2 +- .../openstack/compute/v2/hypervisors_test.go | 33 +++- .../compute/v2/extensions/hypervisors/doc.go | 51 ++++-- .../v2/extensions/hypervisors/requests.go | 47 +++++- .../v2/extensions/hypervisors/results.go | 10 ++ .../hypervisors/testing/fixtures.go | 155 ++++++++++++++++++ .../hypervisors/testing/requests_test.go | 24 ++- 7 files changed, 301 insertions(+), 21 deletions(-) diff --git a/acceptance/openstack/compute/v2/aggregates_test.go b/acceptance/openstack/compute/v2/aggregates_test.go index 352adb38e3..543d958992 100644 --- a/acceptance/openstack/compute/v2/aggregates_test.go +++ b/acceptance/openstack/compute/v2/aggregates_test.go @@ -132,7 +132,7 @@ func TestAggregatesSetRemoveMetadata(t *testing.T) { } func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (*hypervisors.Hypervisor, error) { - allPages, err := hypervisors.List(client).AllPages() + allPages, err := hypervisors.List(client, nil).AllPages() th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) diff --git a/acceptance/openstack/compute/v2/hypervisors_test.go b/acceptance/openstack/compute/v2/hypervisors_test.go index 0b57cc5758..1a4e53ebd6 100644 --- a/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/acceptance/openstack/compute/v2/hypervisors_test.go @@ -19,7 +19,7 @@ func TestHypervisorsList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := hypervisors.List(client).AllPages() + allPages, err := hypervisors.List(client, nil).AllPages() th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) @@ -80,8 +80,37 @@ func TestHypervisorsGetUptime(t *testing.T) { th.AssertEquals(t, hypervisorID, hypervisor.ID) } +func TestHypervisorListQuery(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + client.Microversion = "2.53" + + server, err := CreateMicroversionServer(t, client) + th.AssertNoErr(t, err) + defer DeleteServer(t, client, server) + + iTrue := true + listOpts := hypervisors.ListOpts{ + WithServers: &iTrue, + } + + allPages, err := hypervisors.List(client, listOpts).AllPages() + th.AssertNoErr(t, err) + + allHypervisors, err := hypervisors.ExtractHypervisors(allPages) + th.AssertNoErr(t, err) + + hypervisor := allHypervisors[0] + if len(*hypervisor.Servers) < 1 { + t.Fatalf("hypervisor.Servers length should be >= 1") + } +} + func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { - allPages, err := hypervisors.List(client).AllPages() + allPages, err := hypervisors.List(client, nil).AllPages() th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index 8f7d773e74..cb50014e87 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -12,19 +12,44 @@ Example of Show Hypervisor Details fmt.Printf("%+v\n", hypervisor) -Example of Show Hypervisor Details with Compute API microversion greater than 2.53 +Example of Show Hypervisor Details when using Compute API microversion greater than 2.53 - hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" - hypervisor, err := hypervisors.Get(computeClient, hypervisorID).Extract() - if err != nil { - panic(err) - } + computeClient.Microversion = "2.53" + + hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" + hypervisor, err := hypervisors.Get(computeClient, hypervisorID).Extract() + if err != nil { + panic(err) + } fmt.Printf("%+v\n", hypervisor) Example of Retrieving Details of All Hypervisors - allPages, err := hypervisors.List(computeClient).AllPages() + allPages, err := hypervisors.List(computeClient, nil).AllPages() + if err != nil { + panic(err) + } + + allHypervisors, err := hypervisors.ExtractHypervisors(allPages) + if err != nil { + panic(err) + } + + for _, hypervisor := range allHypervisors { + fmt.Printf("%+v\n", hypervisor) + } + +Example of Retrieving Details of All Hypervisors when using Compute API microversion 2.33 or greater. + + computeClient.Microversion = "2.53" + + iTrue := true + listOpts := hypervisors.ListOpts{ + WithServers: &true, + } + + allPages, err := hypervisors.List(computeClient, listOpts).AllPages() if err != nil { panic(err) } @@ -59,11 +84,13 @@ Example of Show Hypervisor Uptime Example of Show Hypervisor Uptime with Compute API microversion greater than 2.53 - hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" - hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() - if err != nil { - panic(err) - } + computeClient.Microversion = "2.53" + + hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" + hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() + if err != nil { + panic(err) + } fmt.Printf("%+v\n", hypervisorUptime) */ diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/extensions/hypervisors/requests.go index a7047d98da..eac01cd468 100644 --- a/openstack/compute/v2/extensions/hypervisors/requests.go +++ b/openstack/compute/v2/extensions/hypervisors/requests.go @@ -5,9 +5,52 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToHypervisorListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the server attributes you want to see returned. Marker and Limit are used +// for pagination. +type ListOpts struct { + // Limit is an integer value for the limit of values to return. + // This requires microversion 2.33 or later. + Limit *int `q:"limit"` + + // Marker is the ID of the last-seen item as a UUID. + // This requires microversion 2.53 or later. + Marker *string `q:"marker"` + + // HypervisorHostnamePattern is the hypervisor hostname or a portion of it. + // This requires microversion 2.53 or later + HypervisorHostnamePattern *string `q:"hypervisor_hostname_pattern"` + + // WithServers is a bool to include all servers which belong to each hypervisor + // This requires microversion 2.53 or later + WithServers *bool `q:"with_servers"` +} + +// ToHypervisorListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToHypervisorListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // List makes a request against the API to list hypervisors. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, hypervisorsListDetailURL(client), func(r pagination.PageResult) pagination.Page { +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := hypervisorsListDetailURL(client) + if opts != nil { + query, err := opts.ToHypervisorListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return HypervisorPage{pagination.SinglePageBase(r)} }) } diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index 5dab0f3d37..6c172b4cac 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -62,6 +62,12 @@ func (r *Service) UnmarshalJSON(b []byte) error { return nil } +// Server represents an instance running on the hypervisor +type Server struct { + Name string `json:"name"` + UUID string `json:"uuid"` +} + // Hypervisor represents a hypervisor in the OpenStack cloud. type Hypervisor struct { // A structure that contains cpu information like arch, model, vendor, @@ -123,6 +129,10 @@ type Hypervisor struct { // Service is the service this hypervisor represents. Service Service `json:"service"` + // Servers is a list of Server object. + // The requires microversion 2.53 or later. + Servers *[]Server `json:"servers"` + // VCPUs is the total number of vcpus on the hypervisor. VCPUs int `json:"vcpus"` diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go index 34e61f5b78..cef3d0ece7 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/fixtures.go @@ -157,6 +157,98 @@ const HypervisorListBody = ` ] }` +// HypervisorListWithParametersBody represents a raw hypervisor list result with Pike+ release. +const HypervisorListWithParametersBody = ` +{ + "hypervisors": [ + { + "cpu_info": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "features": [ + "pge", + "clflush" + ], + "topology": { + "cores": 1, + "threads": 1, + "sockets": 4 + } + }, + "current_workload": 0, + "status": "enabled", + "state": "up", + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 2002000, + "id": "c48f6247-abe4-4a24-824e-ea39e108874f", + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "service": { + "host": "e6a37ee802d74863ab8b91ade8f12a67", + "id": "9c2566e7-7a54-4777-a1ae-c2662f0c407c", + "disabled_reason": null + }, + "servers": [ + { + "name": "instance-00000001", + "uuid": "c42acc8d-eab3-4e4d-9d90-01b0791328f4" + }, + { + "name": "instance-00000002", + "uuid": "8aaf2941-b774-41fc-921b-20c4757cc359" + } + ], + "vcpus": 1, + "vcpus_used": 0 + }, + { + "cpu_info": "{\"arch\": \"x86_64\", \"model\": \"Nehalem\", \"vendor\": \"Intel\", \"features\": [\"pge\", \"clflush\"], \"topology\": {\"cores\": 1, \"threads\": 1, \"sockets\": 4}}", + "current_workload": 0, + "status": "enabled", + "state": "up", + "disk_available_least": 0, + "host_ip": "1.1.1.1", + "free_disk_gb": 1028, + "free_ram_mb": 7680, + "hypervisor_hostname": "fake-mini", + "hypervisor_type": "fake", + "hypervisor_version": 2.002e+06, + "id": "c48f6247-abe4-4a24-824e-ea39e108874f", + "local_gb": 1028, + "local_gb_used": 0, + "memory_mb": 8192, + "memory_mb_used": 512, + "running_vms": 0, + "servers": [ + { + "name": "instance-00000001", + "uuid": "c42acc8d-eab3-4e4d-9d90-01b0791328f4" + }, + { + "name": "instance-00000002", + "uuid": "8aaf2941-b774-41fc-921b-20c4757cc359" + } + ], + "service": { + "host": "e6a37ee802d74863ab8b91ade8f12a67", + "id": "9c2566e7-7a54-4777-a1ae-c2662f0c407c", + "disabled_reason": null + }, + "vcpus": 1, + "vcpus_used": 0 + } + ] +}` + const HypervisorsStatisticsBody = ` { "hypervisor_statistics": { @@ -349,6 +441,56 @@ var ( VCPUsUsed: 0, } + HypervisorFakeWithParameters = hypervisors.Hypervisor{ + CPUInfo: hypervisors.CPUInfo{ + Arch: "x86_64", + Model: "Nehalem", + Vendor: "Intel", + Features: []string{ + "pge", + "clflush", + }, + Topology: hypervisors.Topology{ + Cores: 1, + Threads: 1, + Sockets: 4, + }, + }, + CurrentWorkload: 0, + Status: "enabled", + State: "up", + DiskAvailableLeast: 0, + HostIP: "1.1.1.1", + FreeDiskGB: 1028, + FreeRamMB: 7680, + HypervisorHostname: "fake-mini", + HypervisorType: "fake", + HypervisorVersion: 2002000, + ID: "c48f6247-abe4-4a24-824e-ea39e108874f", + LocalGB: 1028, + LocalGBUsed: 0, + MemoryMB: 8192, + MemoryMBUsed: 512, + RunningVMs: 0, + Service: hypervisors.Service{ + Host: "e6a37ee802d74863ab8b91ade8f12a67", + ID: "9c2566e7-7a54-4777-a1ae-c2662f0c407c", + DisabledReason: "", + }, + Servers: &[]hypervisors.Server{ + { + Name: "instance-00000001", + UUID: "c42acc8d-eab3-4e4d-9d90-01b0791328f4", + }, + { + Name: "instance-00000002", + UUID: "8aaf2941-b774-41fc-921b-20c4757cc359", + }, + }, + VCPUs: 1, + VCPUsUsed: 0, + } + HypervisorEmptyCPUInfo = hypervisors.Hypervisor{ CurrentWorkload: 0, Status: "enabled", @@ -429,6 +571,19 @@ func HandleHypervisorListSuccessfully(t *testing.T) { }) } +func HandleHypervisorListWithParametersSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "GET") + testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + testhelper.TestFormValues(t, r, map[string]string{ + "with_servers": "true", + }) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, HypervisorListWithParametersBody) + }) +} + func HandleHypervisorGetSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go index 21428d60a3..9cc27f607e 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go @@ -15,7 +15,8 @@ func TestListHypervisorsPre253(t *testing.T) { HandleHypervisorListPre253Successfully(t) pages := 0 - err := hypervisors.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := hypervisors.List(client.ServiceClient(), + hypervisors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := hypervisors.ExtractHypervisors(page) @@ -44,7 +45,7 @@ func TestListAllHypervisorsPre253(t *testing.T) { defer testhelper.TeardownHTTP() HandleHypervisorListPre253Successfully(t) - allPages, err := hypervisors.List(client.ServiceClient()).AllPages() + allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{}).AllPages() testhelper.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) testhelper.AssertNoErr(t, err) @@ -58,7 +59,8 @@ func TestListHypervisors(t *testing.T) { HandleHypervisorListSuccessfully(t) pages := 0 - err := hypervisors.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := hypervisors.List(client.ServiceClient(), + hypervisors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { pages++ actual, err := hypervisors.ExtractHypervisors(page) @@ -87,7 +89,7 @@ func TestListAllHypervisors(t *testing.T) { defer testhelper.TeardownHTTP() HandleHypervisorListSuccessfully(t) - allPages, err := hypervisors.List(client.ServiceClient()).AllPages() + allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{}).AllPages() testhelper.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) testhelper.AssertNoErr(t, err) @@ -95,6 +97,20 @@ func TestListAllHypervisors(t *testing.T) { testhelper.CheckDeepEquals(t, HypervisorFake, actual[1]) } +func TestListAllHypervisorsWithParameters(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleHypervisorListWithParametersSuccessfully(t) + + with_servers := true + allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{WithServers: &with_servers}).AllPages() + testhelper.AssertNoErr(t, err) + actual, err := hypervisors.ExtractHypervisors(allPages) + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, HypervisorFakeWithParameters, actual[0]) + testhelper.CheckDeepEquals(t, HypervisorFakeWithParameters, actual[1]) +} + func TestHypervisorsStatistics(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() From 782f2f7b3795b05d6fedb59e5979691567f4368e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 10 Jul 2021 00:18:12 -0600 Subject: [PATCH 1278/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd30f63220..4dc498a675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ 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 @@ -19,6 +20,7 @@ IMPROVEMENTS * 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) ## 0.18.0 (June 11, 2021) From 5a7ac6556e891d9dc387da1ed15311b1ab60023e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 10 Jul 2021 09:21:48 -0600 Subject: [PATCH 1279/2296] Acc Tests: Enable Shared Filesystem access tests (#1803) --- acceptance/openstack/sharedfilesystems/v2/shares_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/acceptance/openstack/sharedfilesystems/v2/shares_test.go index c2b3376c33..972c5fbd12 100644 --- a/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -150,7 +150,6 @@ func TestShareListDetail(t *testing.T) { } func TestGrantAndRevokeAccess(t *testing.T) { - t.Skip("Currently failing in OpenLab") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") @@ -180,7 +179,6 @@ func TestGrantAndRevokeAccess(t *testing.T) { } func TestListAccessRights(t *testing.T) { - t.Skip("Currently failing in OpenLab") clients.SkipRelease(t, "stable/mitaka") clients.SkipRelease(t, "stable/newton") From 9f639c280922faa6664c12ccb3da366a55e75137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Va=C5=A1ek?= Date: Thu, 15 Jul 2021 04:26:45 +0200 Subject: [PATCH 1280/2296] sharedfilesystems: Add CreateShareFromSnapshotSupport field to Share struct (#2191) * sharedfilesystems: Add CreateShareFromSnapshotSupport field to Share struct * updated unit tests --- .../sharedfilesystems/v2/shares/results.go | 2 + .../v2/shares/testing/fixtures.go | 4 ++ .../v2/shares/testing/request_test.go | 54 ++++++++++--------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 741f4a0d64..d2e7470bbb 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -69,6 +69,8 @@ type Share struct { // Used for filtering backends which either support or do not support share snapshots SnapshotSupport bool `json:"snapshot_support"` SourceCgsnapshotMemberID string `json:"source_cgsnapshot_member_id"` + // Used for filtering backends which either support or do not support creating shares from snapshots + CreateShareFromSnapshotSupport bool `json:"create_share_from_snapshot_support"` // Timestamp when the share was created CreatedAt time.Time `json:"-"` // Timestamp when the share was updated diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go index 73b3e61345..19c3aa15f5 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures.go @@ -53,6 +53,7 @@ var createResponse = `{ "replication_type": null, "task_state": null, "snapshot_support": true, + "create_share_from_snapshot_support": true, "consistency_group_id": "9397c191-8427-4661-a2e8-b23820dc01d4", "source_cgsnapshot_member_id": null, "volume_type": "default", @@ -133,6 +134,7 @@ var updateResponse = ` "task_state": null, "is_public": false, "snapshot_support": true, + "create_share_from_snapshot_support": true, "name": "my_new_test_share", "created_at": "2015-09-18T10:25:24.000000", "share_proto": "NFS", @@ -188,6 +190,7 @@ var getResponse = `{ "task_state": null, "is_public": true, "snapshot_support": true, + "create_share_from_snapshot_support": true, "name": "my_test_share", "created_at": "2015-09-18T10:25:24.000000", "share_proto": "NFS", @@ -241,6 +244,7 @@ var listDetailResponse = `{ "task_state": null, "is_public": true, "snapshot_support": true, + "create_share_from_snapshot_support": true, "name": "my_test_share", "created_at": "2015-09-18T10:25:24.000000", "share_proto": "NFS", diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index 91efd4fbda..b7880c6238 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -79,19 +79,20 @@ func TestGet(t *testing.T) { "project": "my_app", "aim": "doc", }, - Status: "available", - Description: "My custom share London", - Host: "manila2@generic1#GENERIC1", - HasReplicas: false, - ReplicationType: "", - TaskState: "", - SnapshotSupport: true, - Name: "my_test_share", - CreatedAt: time.Date(2015, time.September, 18, 10, 25, 24, 0, time.UTC), - ShareProto: "NFS", - VolumeType: "default", - SourceCgsnapshotMemberID: "", - IsPublic: true, + Status: "available", + Description: "My custom share London", + Host: "manila2@generic1#GENERIC1", + HasReplicas: false, + ReplicationType: "", + TaskState: "", + SnapshotSupport: true, + CreateShareFromSnapshotSupport: true, + Name: "my_test_share", + CreatedAt: time.Date(2015, time.September, 18, 10, 25, 24, 0, time.UTC), + ShareProto: "NFS", + VolumeType: "default", + SourceCgsnapshotMemberID: "", + IsPublic: true, Links: []map[string]string{ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", @@ -134,19 +135,20 @@ func TestListDetail(t *testing.T) { "project": "my_app", "aim": "doc", }, - Status: "available", - Description: "My custom share London", - Host: "manila2@generic1#GENERIC1", - HasReplicas: false, - ReplicationType: "", - TaskState: "", - SnapshotSupport: true, - Name: "my_test_share", - CreatedAt: time.Date(2015, time.September, 18, 10, 25, 24, 0, time.UTC), - ShareProto: "NFS", - VolumeType: "default", - SourceCgsnapshotMemberID: "", - IsPublic: true, + Status: "available", + Description: "My custom share London", + Host: "manila2@generic1#GENERIC1", + HasReplicas: false, + ReplicationType: "", + TaskState: "", + SnapshotSupport: true, + CreateShareFromSnapshotSupport: true, + Name: "my_test_share", + CreatedAt: time.Date(2015, time.September, 18, 10, 25, 24, 0, time.UTC), + ShareProto: "NFS", + VolumeType: "default", + SourceCgsnapshotMemberID: "", + IsPublic: true, Links: []map[string]string{ { "href": "http://172.18.198.54:8786/v2/16e1ab15c35a457e9c2b2aa189f544e1/shares/011d21e2-fbc3-4e4a-9993-9ea223f73264", From 004d8c86cb4b5422bf27f9181c5620bdfa608cd3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 14 Jul 2021 20:27:50 -0600 Subject: [PATCH 1281/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dc498a675..a664e6f29b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ IMPROVEMENTS * 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) ## 0.18.0 (June 11, 2021) From d36ceaf56aa196b261430f278e5b217f1436f0bd Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Thu, 15 Jul 2021 17:40:29 +0000 Subject: [PATCH 1282/2296] add `tag` attribute to servers.Network (#2193) Closes #2192. --- openstack/compute/v2/servers/requests.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 6da3da2e3a..72ec69e503 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -128,6 +128,13 @@ type Network struct { // FixedIP specifies a fixed IPv4 address to be used on this network. FixedIP string + + // Tag may contain an optional device role tag for the server's virtual + // network interface. This can be used to identify network interfaces when + // multiple networks are connected to one server. + // + // Requires microversion 2.32 through 2.36 or 2.42 or later. + Tag string } // Personality is an array of files that are injected into the server at launch. @@ -261,6 +268,9 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { if net.FixedIP != "" { networks[i]["fixed_ip"] = net.FixedIP } + if net.Tag != "" { + networks[i]["tag"] = net.Tag + } } b["networks"] = networks } From 2509091608e7b377980b0d24a3b60e3cc82f251f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 15 Jul 2021 11:42:39 -0600 Subject: [PATCH 1283/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a664e6f29b..1ab6ca180d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ IMPROVEMENTS * 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) From 4dfac38ca86110b4da2f443bf2ed70b861f7171c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 22 Jul 2021 08:43:54 -0600 Subject: [PATCH 1284/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ab6ca180d..abfd567820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.19.0 (Unreleased) +## 0.20.0 (Unreleased) + +## 0.19.0 (July 22, 2021) NOTES / BREAKING CHANGES From 96bec7c4377d72b11ca232a900727d3d7e791c5e Mon Sep 17 00:00:00 2001 From: andrewbaxter Date: Tue, 3 Aug 2021 00:12:33 +0900 Subject: [PATCH 1285/2296] Add general request retry mechanism (#2194) * Add general request retry mechanism * Changes per comments * Also trigger retry for network issues * Handle retries from more potential failure locations * Revert retry on connection reuse Co-authored-by: andrewbaxter <> --- provider_client.go | 47 +++++++++++++++++++++++-- testing/provider_client_test.go | 61 ++++++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/provider_client.go b/provider_client.go index 7797af61c5..916e59ae4c 100644 --- a/provider_client.go +++ b/provider_client.go @@ -25,7 +25,13 @@ type UserAgent struct { prepend []string } -type RetryFunc func(context.Context, *ErrUnexpectedResponseCode, error, uint) error +type RetryBackoffFunc func(context.Context, *ErrUnexpectedResponseCode, error, uint) error + +// RetryFunc is a catch-all function for retrying failed API requests. +// If it returns nil, the request will be retried. If it returns an error, +// the request method will exit with that error. failCount is the number of +// times the request has failed (starting at 1). +type RetryFunc func(context context.Context, method, url string, options *RequestOpts, err error, failCount uint) error // Prepend prepends a user-defined string to the default User-Agent string. Users // may pass in one or more strings to prepend. @@ -85,12 +91,16 @@ type ProviderClient struct { // Context is the context passed to the HTTP request. Context context.Context - // Retry backoff func - RetryBackoffFunc RetryFunc + // Retry backoff func is called when rate limited. + RetryBackoffFunc RetryBackoffFunc // MaxBackoffRetries set the maximum number of backoffs. When not set, defaults to DefaultMaxBackoffRetries MaxBackoffRetries uint + // A general failed request handler method - this is always called in the end if a request failed. Leave as nil + // to abort when an error is encountered. + RetryFunc RetryFunc + // mut is a mutex for the client. It protects read and write access to client attributes such as getting // and setting the TokenID. mut *sync.RWMutex @@ -416,6 +426,16 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts // Issue the request. resp, err := client.HTTPClient.Do(req) if err != nil { + if client.RetryFunc != nil { + var e error + state.retries = state.retries + 1 + e = client.RetryFunc(client.Context, method, url, options, err, state.retries) + if e != nil { + return nil, e + } + + return client.doRequest(method, url, options, state) + } return nil, err } @@ -550,6 +570,17 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts err = respErr } + if err != nil && client.RetryFunc != nil { + var e error + state.retries = state.retries + 1 + e = client.RetryFunc(client.Context, method, url, options, err, state.retries) + if e != nil { + return resp, e + } + + return client.doRequest(method, url, options, state) + } + return resp, err } @@ -563,6 +594,16 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts return resp, err } if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { + if client.RetryFunc != nil { + var e error + state.retries = state.retries + 1 + e = client.RetryFunc(client.Context, method, url, options, err, state.retries) + if e != nil { + return resp, e + } + + return client.doRequest(method, url, options, state) + } return nil, err } } diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index f7278b024d..2456a71102 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -450,7 +450,7 @@ func TestRequestConnectionClose(t *testing.T) { th.AssertEquals(t, int64(iter), connections) } -func retryTest(retryCounter *uint, t *testing.T) gophercloud.RetryFunc { +func retryTest(retryCounter *uint, t *testing.T) gophercloud.RetryBackoffFunc { return func(ctx context.Context, respErr *gophercloud.ErrUnexpectedResponseCode, e error, retries uint) error { retryAfter := respErr.ResponseHeader.Get("Retry-After") if retryAfter == "" { @@ -632,6 +632,65 @@ func TestRequestRetryContext(t *testing.T) { th.AssertEquals(t, retryCounter, p.MaxBackoffRetries-1) } +func TestRequestGeneralRetry(t *testing.T) { + p := &gophercloud.ProviderClient{} + p.UseTokenLock() + p.SetToken(client.TokenID) + p.RetryFunc = func(context context.Context, method, url string, options *gophercloud.RequestOpts, err error, failCount uint) error { + if failCount >= 5 { + return err + } + return nil + } + + th.SetupHTTP() + defer th.TeardownHTTP() + + count := 0 + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + if count < 3 { + http.Error(w, "bad gateway", http.StatusBadGateway) + count += 1 + } else { + fmt.Fprintln(w, "OK") + } + }) + + _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + if err != nil { + t.Fatal("expecting nil, got err") + } + th.AssertEquals(t, 3, count) +} + +func TestRequestGeneralRetryAbort(t *testing.T) { + p := &gophercloud.ProviderClient{} + p.UseTokenLock() + p.SetToken(client.TokenID) + p.RetryFunc = func(context context.Context, method, url string, options *gophercloud.RequestOpts, err error, failCount uint) error { + return err + } + + th.SetupHTTP() + defer th.TeardownHTTP() + + count := 0 + th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { + if count < 3 { + http.Error(w, "bad gateway", http.StatusBadGateway) + count += 1 + } else { + fmt.Fprintln(w, "OK") + } + }) + + _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + if err == nil { + t.Fatal("expecting err, got nil") + } + th.AssertEquals(t, 1, count) +} + func TestRequestWrongOkCode(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "OK") From cc9268f4d4dd68d3d2d7a295110aaee5f6cd4d0b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Aug 2021 09:14:57 -0600 Subject: [PATCH 1286/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index abfd567820..78b8aca2af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.20.0 (Unreleased) +IMPROVEMENTS + +* Added `RetryFunc` to enable custom retry functions. + ## 0.19.0 (July 22, 2021) NOTES / BREAKING CHANGES From 1e1b375a4a542de72fe2a505c0f70a312430ddc8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 2 Aug 2021 09:17:58 -0600 Subject: [PATCH 1287/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b8aca2af..9f1ec3834d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ IMPROVEMENTS -* Added `RetryFunc` to enable custom retry functions. +* Added `RetryFunc` to enable custom retry functions. [GH-2194](https://github.com/gophercloud/gophercloud/pull/2194) ## 0.19.0 (July 22, 2021) From ad8066cf841fa16a5497835d6df808bf7aadcbe4 Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Tue, 10 Aug 2021 16:10:09 +0200 Subject: [PATCH 1288/2296] Subscriptions Vendor Passthru support (#2201) This commit adds support for the following vendor passthru methods: - create_subscription - delete_subscription - get_subscription - get_all_subscriptions This methods are only supported by redfish implementations in ironic that are using the vendor passthru. --- openstack/baremetal/v1/nodes/doc.go | 62 +++++ openstack/baremetal/v1/nodes/requests.go | 140 ++++++++++ openstack/baremetal/v1/nodes/results.go | 105 ++++++++ .../baremetal/v1/nodes/testing/fixtures.go | 242 ++++++++++++++++++ .../v1/nodes/testing/requests_test.go | 95 +++++++ openstack/baremetal/v1/nodes/urls.go | 8 + 6 files changed, 652 insertions(+) diff --git a/openstack/baremetal/v1/nodes/doc.go b/openstack/baremetal/v1/nodes/doc.go index 37f60b5f3e..68c553ff32 100644 --- a/openstack/baremetal/v1/nodes/doc.go +++ b/openstack/baremetal/v1/nodes/doc.go @@ -126,5 +126,67 @@ Example to get boot device for a node if err != nil { panic(err) } + +Example to list all vendor passthru methods + + methods, err := nodes.GetVendorPassthruMethods(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + if err != nil { + panic(err) + } + +Example to list all subscriptions + + method := nodes.CallVendorPassthruOpts{ + Method: "get_all_subscriptions", + } + allSubscriptions, err := nodes.GetAllSubscriptions(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method).Extract() + if err != nil { + panic(err) + } + +Example to get a subscription + + method := nodes.CallVendorPassthruOpts{ + Method: "get_subscription", + } + subscriptionOpt := nodes.GetSubscriptionOpts{ + Id: "subscription id", + } + + subscription, err := nodes.GetSubscription(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionOpt).Extract() + if err != nil { + panic(err) + } + +Example to delete a subscription + + method := nodes.CallVendorPassthruOpts{ + Method: "delete_subscription", + } + subscriptionDeleteOpt := nodes.DeleteSubscriptionOpts{ + Id: "subscription id", + } + + err := nodes.DeleteSubscription(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionDeleteOpt).ExtractErr() + if err != nil { + panic(err) + } + +Example to create a subscription + + method := nodes.CallVendorPassthruOpts{ + Method: "create_subscription", + } + subscriptionCreateOpt := nodes.CreateSubscriptionOpts{ + Destination: "https://subscription_destination_url" + Context: "MyContext", + Protocol: "Redfish", + EventTypes: ["Alert"], + } + + newSubscription, err := nodes.CreateSubscription(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionCreateOpt).Extract() + if err != nil { + panic(err) + } */ package nodes diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 7c9befb161..e0f23486a5 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -694,3 +694,143 @@ func GetBIOSSetting(client *gophercloud.ServiceClient, id string, setting string _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// CallVendorPassthruOpts defines query options that can be passed to any VendorPassthruCall +type CallVendorPassthruOpts struct { + Method string `q:"method"` +} + +// ToGetSubscriptionMap assembles a query based on the contents of a CallVendorPassthruOpts +func ToGetAllSubscriptionMap(opts CallVendorPassthruOpts) (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Get all vendor_passthru methods available for the given Node. +func GetVendorPassthruMethods(client *gophercloud.ServiceClient, id string) (r VendorPassthruMethodsResult) { + resp, err := client.Get(vendorPassthruMethodsURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get all subscriptions available for the given Node. +func GetAllSubscriptions(client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts) (r GetAllSubscriptionsVendorPassthruResult) { + query, err := ToGetAllSubscriptionMap(method) + if err != nil { + r.Err = err + return + } + url := vendorPassthruCallURL(client, id) + query + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// The desired subscription id on the baremetal node. +type GetSubscriptionOpts struct { + Id string `json:"id"` +} + +// ToGetSubscriptionMap assembles a query based on the contents of CallVendorPassthruOpts and a request body based on the contents of a GetSubscriptionOpts +func ToGetSubscriptionMap(method CallVendorPassthruOpts, opts GetSubscriptionOpts) (string, map[string]interface{}, error) { + q, err := gophercloud.BuildQueryString(method) + if err != nil { + return q.String(), nil, err + } + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return q.String(), nil, err + } + + return q.String(), body, nil +} + +// Get a subscription on the given Node. +func GetSubscription(client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts GetSubscriptionOpts) (r SubscriptionVendorPassthruResult) { + query, reqBody, err := ToGetSubscriptionMap(method, subscriptionOpts) + if err != nil { + r.Err = err + return + } + url := vendorPassthruCallURL(client, id) + query + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + JSONBody: reqBody, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// The desired subscription to be deleted from the baremetal node. +type DeleteSubscriptionOpts struct { + Id string `json:"id"` +} + +// ToDeleteSubscriptionMap assembles a query based on the contents of CallVendorPassthruOpts and a request body based on the contents of a DeleteSubscriptionOpts +func ToDeleteSubscriptionMap(method CallVendorPassthruOpts, opts DeleteSubscriptionOpts) (string, map[string]interface{}, error) { + q, err := gophercloud.BuildQueryString(method) + if err != nil { + return q.String(), nil, err + } + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return q.String(), nil, err + } + return q.String(), body, nil +} + +// Delete a subscription on the given node. +func DeleteSubscription(client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts DeleteSubscriptionOpts) (r DeleteSubscriptionVendorPassthruResult) { + query, reqBody, err := ToDeleteSubscriptionMap(method, subscriptionOpts) + if err != nil { + r.Err = err + return + } + url := vendorPassthruCallURL(client, id) + query + resp, err := client.Delete(url, &gophercloud.RequestOpts{ + JSONBody: reqBody, + OkCodes: []int{200, 202, 204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return r +} + +// The desired subscription to be created from the baremetal node. +type CreateSubscriptionOpts struct { + Destination string `json:"Destination"` + EventTypes []string `json:"EventTypes,omitempty"` + Context string `json:"Context,omitempty"` + Protocol string `json:"Protocol,omitempty"` +} + +// ToCreateSubscriptionMap assembles a query based on the contents of CallVendorPassthruOpts and a request body based on the contents of a CreateSubscriptionOpts +func ToCreateSubscriptionMap(method CallVendorPassthruOpts, opts CreateSubscriptionOpts) (string, map[string]interface{}, error) { + q, err := gophercloud.BuildQueryString(method) + if err != nil { + return q.String(), nil, err + } + body, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return q.String(), nil, err + } + return q.String(), body, nil +} + +// Creates a subscription on the given node. +func CreateSubscription(client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts CreateSubscriptionOpts) (r SubscriptionVendorPassthruResult) { + query, reqBody, err := ToCreateSubscriptionMap(method, subscriptionOpts) + if err != nil { + r.Err = err + return + } + url := vendorPassthruCallURL(client, id) + query + resp, err := client.Post(url, reqBody, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return r +} diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 1d1e50ffc1..dd02b532e2 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -65,6 +65,25 @@ func (r GetBIOSSettingResult) Extract() (*BIOSSetting, error) { return &s.Setting, err } +// Extract interprets a VendorPassthruMethod as +func (r VendorPassthruMethodsResult) Extract() (*VendorPassthruMethods, error) { + var s VendorPassthruMethods + err := r.ExtractInto(&s) + return &s, err +} + +func (r GetAllSubscriptionsVendorPassthruResult) Extract() (*GetAllSubscriptionsVendorPassthru, error) { + var s GetAllSubscriptionsVendorPassthru + err := r.ExtractInto(&s) + return &s, err +} + +func (r SubscriptionVendorPassthruResult) Extract() (*SubscriptionVendorPassthru, error) { + var s SubscriptionVendorPassthru + err := r.ExtractInto(&s) + return &s, err +} + // Node represents a node in the OpenStack Bare Metal API. type Node struct { // Whether automated cleaning is enabled or disabled on this node. @@ -323,6 +342,30 @@ type GetBIOSSettingResult struct { gophercloud.Result } +// VendorPassthruMethodsResult is the response from a GetVendorPassthruMethods operation. Call its Extract +// method to interpret it as an array of allowed vendor methods. +type VendorPassthruMethodsResult struct { + gophercloud.Result +} + +// GetAllSubscriptionsVendorPassthruResult is the response from GetAllSubscriptions operation. Call its +// Extract method to interpret it as a GetAllSubscriptionsVendorPassthru struct. +type GetAllSubscriptionsVendorPassthruResult struct { + gophercloud.Result +} + +// SubscriptionVendorPassthruResult is the response from GetSubscription and CreateSubscription operation. Call its Extract +// method to interpret it as a SubscriptionVendorPassthru struct. +type SubscriptionVendorPassthruResult struct { + gophercloud.Result +} + +// DeleteSubscriptionVendorPassthruResult is the response from DeleteSubscription operation. Call its +// ExtractErr method to determine if the call succeeded of failed. +type DeleteSubscriptionVendorPassthruResult struct { + gophercloud.ErrResult +} + // Each element in the response will contain a “result” variable, which will have a value of “true” or “false”, and // also potentially a reason. A value of nil indicates that the Node’s driver does not support that interface. type DriverValidation struct { @@ -396,3 +439,65 @@ type SingleBIOSSetting struct { type ChangeStateResult struct { gophercloud.ErrResult } + +type VendorPassthruMethods struct { + CreateSubscription CreateSubscriptionMethod `json:"create_subscription,omitempty"` + DeleteSubscription DeleteSubscriptionMethod `json:"delete_subscription,omitempty"` + GetSubscription GetSubscriptionMethod `json:"get_subscription,omitempty"` + GetAllSubscriptions GetAllSubscriptionsMethod `json:"get_all_subscriptions,omitempty"` +} + +// Below you can find all vendor passthru methods structs + +type CreateSubscriptionMethod struct { + HTTPMethods []string `json:"http_methods"` + Async bool `json:"async"` + Description string `json:"description"` + Attach bool `json:"attach"` + RequireExclusiveLock bool `json:"require_exclusive_lock"` +} + +type DeleteSubscriptionMethod struct { + HTTPMethods []string `json:"http_methods"` + Async bool `json:"async"` + Description string `json:"description"` + Attach bool `json:"attach"` + RequireExclusiveLock bool `json:"require_exclusive_lock"` +} + +type GetSubscriptionMethod struct { + HTTPMethods []string `json:"http_methods"` + Async bool `json:"async"` + Description string `json:"description"` + Attach bool `json:"attach"` + RequireExclusiveLock bool `json:"require_exclusive_lock"` +} + +type GetAllSubscriptionsMethod struct { + HTTPMethods []string `json:"http_methods"` + Async bool `json:"async"` + Description string `json:"description"` + Attach bool `json:"attach"` + RequireExclusiveLock bool `json:"require_exclusive_lock"` +} + +// A List of subscriptions from a node in the OpenStack Bare Metal API. +type GetAllSubscriptionsVendorPassthru struct { + Context string `json:"@odata.context"` + Etag string `json:"@odata.etag"` + Id string `json:"@odata.id"` + Type string `json:"@odata.type"` + Description string `json:"Description"` + Name string `json:"Name"` + Members []map[string]string `json:"Members"` + MembersCount int `json:"Members@odata.count"` +} + +// A Subscription from a node in the OpenStack Bare Metal API. +type SubscriptionVendorPassthru struct { + Id string `json:"Id"` + Context string `json:"Context"` + Destination string `json:"Destination"` + EventTypes []string `json:"EventTypes"` + Protocol string `json:"Protocol"` +} diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 39499e7bcd..415af5d593 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -717,6 +717,94 @@ const NodeSingleBIOSSettingBody = ` } ` +const NodeVendorPassthruMethodsBody = ` +{ + "create_subscription": { + "http_methods": [ + "POST" + ], + "async": false, + "description": "", + "attach": false, + "require_exclusive_lock": true + }, + "delete_subscription": { + "http_methods": [ + "DELETE" + ], + "async": false, + "description": "", + "attach": false, + "require_exclusive_lock": true + }, + "get_subscription": { + "http_methods": [ + "GET" + ], + "async": false, + "description": "", + "attach": false, + "require_exclusive_lock": true + }, + "get_all_subscriptions": { + "http_methods": [ + "GET" + ], + "async": false, + "description": "", + "attach": false, + "require_exclusive_lock": true + } +} +` + +const NodeGetAllSubscriptionsVnedorPassthruBody = ` +{ + "@odata.context": "/redfish/v1/$metadata#EventDestinationCollection.EventDestinationCollection", + "@odata.id": "/redfish/v1/EventService/Subscriptions", + "@odata.type": "#EventDestinationCollection.EventDestinationCollection", + "Description": "List of Event subscriptions", + "Members": [ + { + "@odata.id": "/redfish/v1/EventService/Subscriptions/62dbd1b6-f637-11eb-b551-4cd98f20754c" + } + ], + "Members@odata.count": 1, + "Name": "Event Subscriptions Collection" +} + +` + +const NodeGetSubscriptionVendorPassthruBody = ` +{ + "Context": "Ironic", + "Destination": "https://192.168.0.1/EventReceiver.php", + "EventTypes": ["Alert"], + "Id": "62dbd1b6-f637-11eb-b551-4cd98f20754c", + "Protocol": "Redfish" +} +` + +const NodeCreateSubscriptionVendorPassthruAllParametersBody = ` +{ + "Context": "gophercloud", + "Destination": "https://someurl", + "EventTypes": ["Alert"], + "Id": "eaa43e2-018a-424e-990a-cbf47c62ef80", + "Protocol": "Redfish" +} +` + +const NodeCreateSubscriptionVendorPassthruRequiredParametersBody = ` +{ + "Context": "", + "Destination": "https://somedestinationurl", + "EventTypes": ["Alert"], + "Id": "344a3e2-978a-444e-990a-cbf47c62ef88", + "Protocol": "Redfish" +} +` + var ( NodeFoo = nodes.Node{ UUID: "d2630783-6ec8-4836-b556-ab427c4b581e", @@ -993,6 +1081,72 @@ var ( Name: "ProcVirtualization", Value: "Enabled", } + + NodeVendorPassthruMethods = nodes.VendorPassthruMethods{ + CreateSubscription: nodes.CreateSubscriptionMethod{ + HTTPMethods: []string{"POST"}, + Async: false, + Description: "", + Attach: false, + RequireExclusiveLock: true, + }, + DeleteSubscription: nodes.DeleteSubscriptionMethod{ + HTTPMethods: []string{"DELETE"}, + Async: false, + Description: "", + Attach: false, + RequireExclusiveLock: true, + }, + GetSubscription: nodes.GetSubscriptionMethod{ + HTTPMethods: []string{"GET"}, + Async: false, + Description: "", + Attach: false, + RequireExclusiveLock: true, + }, + GetAllSubscriptions: nodes.GetAllSubscriptionsMethod{ + HTTPMethods: []string{"GET"}, + Async: false, + Description: "", + Attach: false, + RequireExclusiveLock: true, + }, + } + + NodeGetAllSubscriptions = nodes.GetAllSubscriptionsVendorPassthru{ + Context: "/redfish/v1/$metadata#EventDestinationCollection.EventDestinationCollection", + Etag: "", + Id: "/redfish/v1/EventService/Subscriptions", + Type: "#EventDestinationCollection.EventDestinationCollection", + Description: "List of Event subscriptions", + Name: "Event Subscriptions Collection", + Members: []map[string]string{{"@odata.id": "/redfish/v1/EventService/Subscriptions/62dbd1b6-f637-11eb-b551-4cd98f20754c"}}, + MembersCount: 1, + } + + NodeGetSubscription = nodes.SubscriptionVendorPassthru{ + Id: "62dbd1b6-f637-11eb-b551-4cd98f20754c", + Context: "Ironic", + Destination: "https://192.168.0.1/EventReceiver.php", + EventTypes: []string{"Alert"}, + Protocol: "Redfish", + } + + NodeCreateSubscriptionRequiredParameters = nodes.SubscriptionVendorPassthru{ + Id: "344a3e2-978a-444e-990a-cbf47c62ef88", + Context: "", + Destination: "https://somedestinationurl", + EventTypes: []string{"Alert"}, + Protocol: "Redfish", + } + + NodeCreateSubscriptionAllParameters = nodes.SubscriptionVendorPassthru{ + Id: "eaa43e2-018a-424e-990a-cbf47c62ef80", + Context: "gophercloud", + Destination: "https://someurl", + EventTypes: []string{"Alert"}, + Protocol: "Redfish", + } ) // HandleNodeListSuccessfully sets up the test server to respond to a server List request. @@ -1280,3 +1434,91 @@ func HandleGetBIOSSettingSuccessfully(t *testing.T) { fmt.Fprintf(w, NodeSingleBIOSSettingBody) }) } + +func HandleGetVendorPassthruMethodsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/vendor_passthru/methods", 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, NodeVendorPassthruMethodsBody) + }) +} + +func HandleGetAllSubscriptionsVendorPassthruSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/vendor_passthru", 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") + th.TestFormValues(t, r, map[string]string{"method": "get_all_subscriptions"}) + + fmt.Fprintf(w, NodeGetAllSubscriptionsVnedorPassthruBody) + }) +} + +func HandleGetSubscriptionVendorPassthruSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/vendor_passthru", 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") + th.TestFormValues(t, r, map[string]string{"method": "get_subscription"}) + th.TestJSONRequest(t, r, ` + { + "id" : "62dbd1b6-f637-11eb-b551-4cd98f20754c" + } + `) + + fmt.Fprintf(w, NodeGetSubscriptionVendorPassthruBody) + }) +} + +func HandleCreateSubscriptionVendorPassthruAllParametersSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/vendor_passthru", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestFormValues(t, r, map[string]string{"method": "create_subscription"}) + th.TestJSONRequest(t, r, ` + { + "Context": "gophercloud", + "EventTypes": ["Alert"], + "Protocol": "Redfish", + "Destination" : "https://someurl" + } + `) + + fmt.Fprintf(w, NodeCreateSubscriptionVendorPassthruAllParametersBody) + }) +} + +func HandleCreateSubscriptionVendorPassthruRequiredParametersSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/vendor_passthru", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestFormValues(t, r, map[string]string{"method": "create_subscription"}) + th.TestJSONRequest(t, r, ` + { + "Destination" : "https://somedestinationurl" + } + `) + + fmt.Fprintf(w, NodeCreateSubscriptionVendorPassthruRequiredParametersBody) + }) +} + +func HandleDeleteSubscriptionVendorPassthruSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/vendor_passthru", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestFormValues(t, r, map[string]string{"method": "delete_subscription"}) + th.TestJSONRequest(t, r, ` + { + "id" : "344a3e2-978a-444e-990a-cbf47c62ef88" + } + `) + + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 46e02eb496..d3000eee09 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -593,3 +593,98 @@ func TestListBIOSSettingsOpts(t *testing.T) { _, err := opts.ToListBIOSSettingsOptsQuery() th.AssertEquals(t, err.Error(), "cannot have both fields and detail options for BIOS settings") } + +func TestGetVendorPassthruMethods(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetVendorPassthruMethodsSuccessfully(t) + + c := client.ServiceClient() + actual, err := nodes.GetVendorPassthruMethods(c, "1234asdf").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeVendorPassthruMethods, *actual) +} + +func TestGetAllSubscriptions(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetAllSubscriptionsVendorPassthruSuccessfully(t) + + c := client.ServiceClient() + method := nodes.CallVendorPassthruOpts{ + Method: "get_all_subscriptions", + } + actual, err := nodes.GetAllSubscriptions(c, "1234asdf", method).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeGetAllSubscriptions, *actual) +} + +func TestGetSubscription(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSubscriptionVendorPassthruSuccessfully(t) + + c := client.ServiceClient() + method := nodes.CallVendorPassthruOpts{ + Method: "get_subscription", + } + subscriptionOpt := nodes.GetSubscriptionOpts{ + Id: "62dbd1b6-f637-11eb-b551-4cd98f20754c", + } + actual, err := nodes.GetSubscription(c, "1234asdf", method, subscriptionOpt).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeGetSubscription, *actual) +} + +func TestCreateSubscriptionAllParameters(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSubscriptionVendorPassthruAllParametersSuccessfully(t) + + c := client.ServiceClient() + method := nodes.CallVendorPassthruOpts{ + Method: "create_subscription", + } + createOpt := nodes.CreateSubscriptionOpts{ + Destination: "https://someurl", + Context: "gophercloud", + Protocol: "Redfish", + EventTypes: []string{"Alert"}, + } + actual, err := nodes.CreateSubscription(c, "1234asdf", method, createOpt).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeCreateSubscriptionAllParameters, *actual) +} + +func TestCreateSubscriptionWithRequiredParameters(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateSubscriptionVendorPassthruRequiredParametersSuccessfully(t) + + c := client.ServiceClient() + method := nodes.CallVendorPassthruOpts{ + Method: "create_subscription", + } + createOpt := nodes.CreateSubscriptionOpts{ + Destination: "https://somedestinationurl", + } + actual, err := nodes.CreateSubscription(c, "1234asdf", method, createOpt).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeCreateSubscriptionRequiredParameters, *actual) +} + +func TestDeleteSubscription(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSubscriptionVendorPassthruSuccessfully(t) + + c := client.ServiceClient() + method := nodes.CallVendorPassthruOpts{ + Method: "delete_subscription", + } + deleteOpt := nodes.DeleteSubscriptionOpts{ + Id: "344a3e2-978a-444e-990a-cbf47c62ef88", + } + err := nodes.DeleteSubscription(c, "1234asdf", method, deleteOpt).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index 44ce58f336..9e67714dd1 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -65,3 +65,11 @@ func biosListSettingsURL(client *gophercloud.ServiceClient, id string) string { func biosGetSettingURL(client *gophercloud.ServiceClient, id string, setting string) string { return client.ServiceURL("nodes", id, "bios", setting) } + +func vendorPassthruMethodsURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "vendor_passthru", "methods") +} + +func vendorPassthruCallURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "vendor_passthru") +} From b124e6472d2f038d18b5835540c8d6fd7df3b4c8 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Aug 2021 08:11:54 -0600 Subject: [PATCH 1289/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f1ec3834d..cedf755e64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ 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) From a8903d09e5e482d7347872734c4d98eb3c16749f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Aug 2021 16:07:10 -0600 Subject: [PATCH 1290/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cedf755e64..b28cfe686c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.20.0 (Unreleased) +## 0.21.0 (Unreleased) + +## 0.20.0 (August 10, 2021) IMPROVEMENTS From e2a8e99d9e28971d96e9c3a3671a26c4b0dd47de Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 23 Aug 2021 08:54:16 -0600 Subject: [PATCH 1291/2296] Core: Updating unit tests for retrybackoff (#2200) --- testing/provider_client_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 2456a71102..27210df6ae 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -450,7 +450,7 @@ func TestRequestConnectionClose(t *testing.T) { th.AssertEquals(t, int64(iter), connections) } -func retryTest(retryCounter *uint, t *testing.T) gophercloud.RetryBackoffFunc { +func retryBackoffTest(retryCounter *uint, t *testing.T) gophercloud.RetryBackoffFunc { return func(ctx context.Context, respErr *gophercloud.ErrUnexpectedResponseCode, e error, retries uint) error { retryAfter := respErr.ResponseHeader.Get("Retry-After") if retryAfter == "" { @@ -497,7 +497,7 @@ func TestRequestRetry(t *testing.T) { p.SetToken(client.TokenID) p.MaxBackoffRetries = 3 - p.RetryBackoffFunc = retryTest(&retryCounter, t) + p.RetryBackoffFunc = retryBackoffTest(&retryCounter, t) th.SetupHTTP() defer th.TeardownHTTP() @@ -524,7 +524,7 @@ func TestRequestRetryHTTPDate(t *testing.T) { p.SetToken(client.TokenID) p.MaxBackoffRetries = 3 - p.RetryBackoffFunc = retryTest(&retryCounter, t) + p.RetryBackoffFunc = retryBackoffTest(&retryCounter, t) th.SetupHTTP() defer th.TeardownHTTP() @@ -551,7 +551,7 @@ func TestRequestRetryError(t *testing.T) { p.SetToken(client.TokenID) p.MaxBackoffRetries = 3 - p.RetryBackoffFunc = retryTest(&retryCounter, t) + p.RetryBackoffFunc = retryBackoffTest(&retryCounter, t) th.SetupHTTP() defer th.TeardownHTTP() @@ -578,7 +578,7 @@ func TestRequestRetrySuccess(t *testing.T) { p.SetToken(client.TokenID) p.MaxBackoffRetries = 3 - p.RetryBackoffFunc = retryTest(&retryCounter, t) + p.RetryBackoffFunc = retryBackoffTest(&retryCounter, t) th.SetupHTTP() defer th.TeardownHTTP() @@ -612,7 +612,7 @@ func TestRequestRetryContext(t *testing.T) { p.SetToken(client.TokenID) p.MaxBackoffRetries = 3 - p.RetryBackoffFunc = retryTest(&retryCounter, t) + p.RetryBackoffFunc = retryBackoffTest(&retryCounter, t) th.SetupHTTP() defer th.TeardownHTTP() From 3b820fa5fdb1f19ebbc2525305ba1c955591029c Mon Sep 17 00:00:00 2001 From: Matej Pevec Date: Fri, 27 Aug 2021 07:04:43 +0200 Subject: [PATCH 1292/2296] Fix typo alloted to allotted (#2207) --- openstack/compute/v2/extensions/quotasets/requests.go | 4 ++-- openstack/compute/v2/extensions/quotasets/results.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go index 6d3af50939..bb99a1085d 100644 --- a/openstack/compute/v2/extensions/quotasets/requests.go +++ b/openstack/compute/v2/extensions/quotasets/requests.go @@ -42,10 +42,10 @@ func Delete(client *gophercloud.ServiceClient, tenantID string) (r DeleteResult) // 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 { - // FixedIPs is number of fixed ips alloted this quota_set. + // FixedIPs is number of fixed ips allotted this quota_set. FixedIPs *int `json:"fixed_ips,omitempty"` - // FloatingIPs is number of floating ips alloted this quota_set. + // FloatingIPs is number of floating ips allotted this quota_set. FloatingIPs *int `json:"floating_ips,omitempty"` // InjectedFileContentBytes is content bytes allowed for each injected file. diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go index e38868a637..d8df81a63a 100644 --- a/openstack/compute/v2/extensions/quotasets/results.go +++ b/openstack/compute/v2/extensions/quotasets/results.go @@ -11,10 +11,10 @@ type QuotaSet struct { // ID is tenant associated with this QuotaSet. ID string `json:"id"` - // FixedIPs is number of fixed ips alloted this QuotaSet. + // FixedIPs is number of fixed ips allotted this QuotaSet. FixedIPs int `json:"fixed_ips"` - // FloatingIPs is number of floating ips alloted this QuotaSet. + // FloatingIPs is number of floating ips allotted this QuotaSet. FloatingIPs int `json:"floating_ips"` // InjectedFileContentBytes is the allowed bytes for each injected file. @@ -61,10 +61,10 @@ type QuotaDetailSet struct { // ID is the tenant ID associated with this QuotaDetailSet. ID string `json:"id"` - // FixedIPs is number of fixed ips alloted this QuotaDetailSet. + // FixedIPs is number of fixed ips allotted this QuotaDetailSet. FixedIPs QuotaDetail `json:"fixed_ips"` - // FloatingIPs is number of floating ips alloted this QuotaDetailSet. + // FloatingIPs is number of floating ips allotted this QuotaDetailSet. FloatingIPs QuotaDetail `json:"floating_ips"` // InjectedFileContentBytes is the allowed bytes for each injected file. From bdb54d44c848e6d5693a6934dccf325703ff96d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristi=C3=A1n=20Le=C5=A1ko?= Date: Tue, 14 Sep 2021 15:18:50 +0200 Subject: [PATCH 1293/2296] Add block volume extension for os-vol-host-attr (#2212) The os-vol-host-attr is returned by OpenStack API; let's add an extension to allow using this value. --- .../blockstorage/extensions/volumehost/doc.go | 26 +++++++++++++++++++ .../extensions/volumehost/results.go | 7 +++++ .../v3/volumes/testing/fixtures.go | 3 +-- .../v3/volumes/testing/requests_test.go | 4 +++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 openstack/blockstorage/extensions/volumehost/doc.go create mode 100644 openstack/blockstorage/extensions/volumehost/results.go diff --git a/openstack/blockstorage/extensions/volumehost/doc.go b/openstack/blockstorage/extensions/volumehost/doc.go new file mode 100644 index 0000000000..6a651a7c5a --- /dev/null +++ b/openstack/blockstorage/extensions/volumehost/doc.go @@ -0,0 +1,26 @@ +/* +Package volumehost provides the ability to extend a volume result with +information about the Openstack host holding the volume. Example: + + type VolumeWithHost struct { + volumes.Volume + volumehost.VolumeHostExt + } + + var allVolumes []VolumeWithHost + + allPages, err := volumes.List(client, nil).AllPages() + if err != nil { + panic("Unable to retrieve volumes: %s", err) + } + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + if err != nil { + panic("Unable to extract volumes: %s", err) + } + + for _, volume := range allVolumes { + fmt.Println(volume.Host) + } +*/ +package volumehost diff --git a/openstack/blockstorage/extensions/volumehost/results.go b/openstack/blockstorage/extensions/volumehost/results.go new file mode 100644 index 0000000000..5434f37723 --- /dev/null +++ b/openstack/blockstorage/extensions/volumehost/results.go @@ -0,0 +1,7 @@ +package volumehost + +// VolumeHostExt is an extension to the base Volume object +type VolumeHostExt struct { + // Host is the identifier of the host holding the volume. + Host string `json:"os-vol-host-attr:host"` +} diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures.go b/openstack/blockstorage/v3/volumes/testing/fixtures.go index d67835e5dc..339cb30c9c 100644 --- a/openstack/blockstorage/v3/volumes/testing/fixtures.go +++ b/openstack/blockstorage/v3/volumes/testing/fixtures.go @@ -38,7 +38,7 @@ func MockListResponse(t *testing.T) { "replication_status": "disabled", "os-volume-replication:extended_status": null, "encrypted": false, - "os-vol-host-attr:host": null, + "os-vol-host-attr:host": "host-001", "availability_zone": "nova", "attachments": [{ "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", @@ -123,7 +123,6 @@ func MockGetResponse(t *testing.T) { "replication_status": "disabled", "os-volume-replication:extended_status": null, "encrypted": false, - "os-vol-host-attr:host": null, "availability_zone": "nova", "attachments": [{ "server_id": "83ec2e3b-4321-422b-8706-a84185f52a0a", diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index dcc587f437..a4ecdae232 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumehost" "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/pagination" @@ -103,6 +104,7 @@ func TestListAllWithExtensions(t *testing.T) { type VolumeWithExt struct { volumes.Volume volumetenants.VolumeTenantExt + volumehost.VolumeHostExt } allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() @@ -112,6 +114,8 @@ func TestListAllWithExtensions(t *testing.T) { err = volumes.ExtractVolumesInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(actual)) + th.AssertEquals(t, "host-001", actual[0].Host) + th.AssertEquals(t, "", actual[1].Host) th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", actual[0].TenantID) } From b3f1e137c998823dd9afc1dba3bb924acc10f794 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 14 Sep 2021 07:21:29 -0600 Subject: [PATCH 1294/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b28cfe686c..09cac952c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.21.0 (Unreleased) +IMPROVEMENTS + +* Added `blockstorage/extensions/volumehost` [GH-2212](https://github.com/gophercloud/gophercloud/pull/2212) + ## 0.20.0 (August 10, 2021) IMPROVEMENTS From eed3c2965c50fa85efb96689ff6368b71102ee5c Mon Sep 17 00:00:00 2001 From: Lingxian Kong Date: Wed, 15 Sep 2021 01:28:30 +1200 Subject: [PATCH 1295/2296] Octavia: Support listener tags (#2214) For: #2213 --- openstack/loadbalancer/v2/listeners/doc.go | 3 +++ openstack/loadbalancer/v2/listeners/requests.go | 6 ++++++ openstack/loadbalancer/v2/listeners/results.go | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/openstack/loadbalancer/v2/listeners/doc.go b/openstack/loadbalancer/v2/listeners/doc.go index eeea6eea82..c99bbc7026 100644 --- a/openstack/loadbalancer/v2/listeners/doc.go +++ b/openstack/loadbalancer/v2/listeners/doc.go @@ -31,6 +31,7 @@ Example to Create a Listener AdminStateUp: gophercloud.Enabled, DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", ProtocolPort: 3306, + Tags: []string{"test", "stage"}, } listener, err := listeners.Create(networkClient, createOpts).Extract() @@ -44,10 +45,12 @@ Example to Update a Listener i1001 := 1001 i181000 := 181000 + newTags := []string{"prod"} updateOpts := listeners.UpdateOpts{ ConnLimit: &i1001, TimeoutClientData: &i181000, TimeoutMemberData: &i181000, + Tags: &newTags, } listener, err := listeners.Update(networkClient, listenerID, updateOpts).Extract() diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index c15d65dab0..54e968ce51 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -167,6 +167,9 @@ type CreateOpts struct { // A list of TLS protocol versions. Available from microversion 2.17 TLSVersions []TLSVersion `json:"tls_versions,omitempty"` + + // Tags is a set of resource tags. New in version 2.5 + Tags []string `json:"tags,omitempty"` } // ToListenerCreateMap builds a request body from CreateOpts. @@ -249,6 +252,9 @@ type UpdateOpts struct { // A list of TLS protocol versions. Available from microversion 2.17 TLSVersions *[]TLSVersion `json:"tls_versions,omitempty"` + + // Tags is a set of resource tags. New in version 2.5 + Tags *[]string `json:"tags,omitempty"` } // ToListenerUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index be2a6dcfed..b446beb695 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -86,6 +86,10 @@ type Listener struct { // 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"` } type Stats struct { From 86c42d4e5d685d99e15b975f8957b0b34b3c18dc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 14 Sep 2021 07:29:46 -0600 Subject: [PATCH 1296/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09cac952c9..902d02cfad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ 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) From 9cf6777318713a51fbdb1238c19d1213712fd8b4 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 14 Sep 2021 16:35:26 -0600 Subject: [PATCH 1297/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 902d02cfad..2ca0265abb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.21.0 (Unreleased) +## 0.22.0 (Unreleased) + +## 0.21.0 (September 14, 2021) IMPROVEMENTS From 23a9f87cdb07fe1b0a45f1f7fec5b2fc671d831d Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 17 Sep 2021 17:11:49 +0200 Subject: [PATCH 1298/2296] compute: Add ServerGroup property to Server (#2217) Add to the Server type the list of server groups the server is part of, as exposed in microversions 1.71 and above as "server_groups". The new `ServerGroups` property is a pointer to slice (`*[]string`), as mandated by the Gophercloud ["New Response Fields" convention][1]. Nova code implementing the feature: * https://github.com/openstack/nova/blob/d2a5fe5621d6ff1ae8ba5087049e0c4347592cf6/nova/api/openstack/compute/servers.py#L454-L455 * https://github.com/openstack/nova/blob/52cae8801de9229d6cfb7871c2073b83b3e41b81/nova/api/openstack/compute/views/servers.py#L420-L423 [1]: https://github.com/gophercloud/gophercloud/blob/master/docs/MICROVERSIONS.md#new-response-fields --- openstack/compute/v2/servers/results.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index b3028be5d5..8cfb8958e8 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -221,6 +221,12 @@ type Server struct { // Tags is a slice/list of string tags in a server. // The requires microversion 2.26 or later. Tags *[]string `json:"tags"` + + // ServerGroups is a slice of strings containing the UUIDs of the + // server groups to which the server belongs. Currently this can + // contain at most one entry. + // New in microversion 2.71 + ServerGroups *[]string `json:"server_groups"` } type AttachedVolume struct { From f77850b607e9d6f7c015aebfe4d5943860c125b0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 17 Sep 2021 09:12:30 -0600 Subject: [PATCH 1299/2296] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ca0265abb..f796946cc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## 0.22.0 (Unreleased) +* Added `compute/v2/servers.Server.ServerGroups` [GH-2217](https://github.com/gophercloud/gophercloud/pull/2217) + ## 0.21.0 (September 14, 2021) IMPROVEMENTS From 29581a2fc7076bae722d4b681f9cfaa467aa3e09 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 20 Sep 2021 19:14:46 +0200 Subject: [PATCH 1300/2296] Swift V1: fix setting empty headers (#2218) --- .../objectstorage/v1/containers_test.go | 39 +++++++++++++-- .../objectstorage/v1/objects_test.go | 48 ++++++++++++++++--- .../v3/tokens/testing/requests_test.go | 4 +- .../objectstorage/v1/accounts/requests.go | 8 ++-- .../v1/accounts/testing/fixtures.go | 3 ++ .../v1/accounts/testing/requests_test.go | 6 ++- .../objectstorage/v1/containers/requests.go | 24 +++++----- .../v1/containers/testing/fixtures.go | 5 ++ .../v1/containers/testing/requests_test.go | 10 +++- .../objectstorage/v1/objects/requests.go | 12 ++--- .../v1/objects/testing/fixtures.go | 6 +++ .../v1/objects/testing/requests_test.go | 11 ++++- params.go | 3 ++ provider_client.go | 6 +-- testhelper/http_responses.go | 17 ++++++- 15 files changed, 155 insertions(+), 47 deletions(-) diff --git a/acceptance/openstack/objectstorage/v1/containers_test.go b/acceptance/openstack/objectstorage/v1/containers_test.go index 9c61dfe24a..ea12218b27 100644 --- a/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/acceptance/openstack/objectstorage/v1/containers_test.go @@ -72,8 +72,20 @@ func TestContainers(t *testing.T) { metadata := map[string]string{ "Gophercloud-Test": "containers", } - - updateres := containers.Update(client, cNames[0], &containers.UpdateOpts{Metadata: metadata}) + read := ".r:*,.rlistings" + write := "*:*" + iTrue := true + empty := "" + opts := &containers.UpdateOpts{ + Metadata: metadata, + ContainerRead: &read, + ContainerWrite: &write, + DetectContentType: new(bool), + ContainerSyncTo: &empty, + ContainerSyncKey: &empty, + } + + updateres := containers.Update(client, cNames[0], opts) th.AssertNoErr(t, updateres.Err) // After the tests are done, delete the metadata that was set. defer func() { @@ -81,7 +93,14 @@ func TestContainers(t *testing.T) { for k := range metadata { temp = append(temp, k) } - res := containers.Update(client, cNames[0], &containers.UpdateOpts{RemoveMetadata: temp}) + empty := "" + opts = &containers.UpdateOpts{ + RemoveMetadata: temp, + ContainerRead: &empty, + ContainerWrite: &empty, + DetectContentType: &iTrue, + } + res := containers.Update(client, cNames[0], opts) th.AssertNoErr(t, res.Err) // confirm the metadata was removed @@ -89,13 +108,18 @@ func TestContainers(t *testing.T) { Newest: true, } - cm, err := containers.Get(client, cNames[0], getOpts).ExtractMetadata() + resp := containers.Get(client, cNames[0], getOpts) + cm, err := resp.ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { if _, ok := cm[k]; ok { t.Errorf("Unexpected custom metadata with key: %s", k) } } + container, err := resp.Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, empty, strings.Join(container.Read, ",")) + th.AssertEquals(t, empty, strings.Join(container.Write, ",")) }() // Retrieve a container's metadata. @@ -103,13 +127,18 @@ func TestContainers(t *testing.T) { Newest: true, } - cm, err := containers.Get(client, cNames[0], getOpts).ExtractMetadata() + resp := containers.Get(client, cNames[0], getOpts) + cm, err := resp.ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { if cm[k] != metadata[strings.Title(k)] { t.Errorf("Expected custom metadata with key: %s", k) } } + container, err := resp.Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, read, strings.Join(container.Read, ",")) + th.AssertEquals(t, write, strings.Join(container.Write, ",")) // Retrieve a container's timestamp cHeaders, err := containers.Get(client, cNames[0], getOpts).Extract() diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index 1b61324665..8683b442bd 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -134,27 +134,58 @@ func TestObjects(t *testing.T) { "Gophercloud-Test": "objects", } - updateOpts := objects.UpdateOpts{ - Metadata: metadata, + disposition := "inline" + cType := "text/plain" + updateOpts := &objects.UpdateOpts{ + Metadata: metadata, + ContentDisposition: &disposition, + ContentType: &cType, } updateres := objects.Update(client, cName, oNames[0], updateOpts) th.AssertNoErr(t, updateres.Err) // Delete the object's metadata after testing. defer func() { - tempMap := make(map[string]string) + temp := make([]string, len(metadata)) + i := 0 for k := range metadata { - tempMap[k] = "" + temp[i] = k + i++ } - res := objects.Update(client, cName, oNames[0], &objects.UpdateOpts{Metadata: tempMap}) + empty := "" + cType := "application/octet-stream" + iTrue := true + updateOpts = &objects.UpdateOpts{ + RemoveMetadata: temp, + ContentDisposition: &empty, + ContentType: &cType, + DetectContentType: &iTrue, + } + res := objects.Update(client, cName, oNames[0], updateOpts) th.AssertNoErr(t, res.Err) + + // Retrieve an object's metadata. + getOpts := objects.GetOpts{ + Newest: true, + } + resp := objects.Get(client, cName, oNames[0], getOpts) + om, err := resp.ExtractMetadata() + th.AssertNoErr(t, err) + if len(om) > 0 { + t.Errorf("Expected custom metadata to be empty, found: %v", metadata) + } + object, err := resp.Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, empty, object.ContentDisposition) + th.AssertEquals(t, cType, object.ContentType) }() // Retrieve an object's metadata. getOpts := objects.GetOpts{ Newest: true, } - om, err := objects.Get(client, cName, oNames[0], getOpts).ExtractMetadata() + resp := objects.Get(client, cName, oNames[0], getOpts) + om, err := resp.ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { if om[k] != metadata[strings.Title(k)] { @@ -162,6 +193,11 @@ func TestObjects(t *testing.T) { return } } + + object, err := resp.Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, disposition, object.ContentDisposition) + th.AssertEquals(t, cType, object.ContentType) } func TestObjectsListSubdir(t *testing.T) { diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index e8583a99c7..cfa087b984 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -556,7 +556,7 @@ func TestGetRequest(t *testing.T) { testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Content-Type", "") + testhelper.TestHeaderUnset(t, r, "Content-Type") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef") testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345") @@ -588,7 +588,7 @@ func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) go testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, expectedMethod) - testhelper.TestHeader(t, r, "Content-Type", "") + testhelper.TestHeaderUnset(t, r, "Content-Type") testhelper.TestHeader(t, r, "Accept", "application/json") testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef") testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345") diff --git a/openstack/objectstorage/v1/accounts/requests.go b/openstack/objectstorage/v1/accounts/requests.go index 8c0bfaffb7..54b067205a 100644 --- a/openstack/objectstorage/v1/accounts/requests.go +++ b/openstack/objectstorage/v1/accounts/requests.go @@ -54,10 +54,10 @@ type UpdateOptsBuilder interface { type UpdateOpts struct { Metadata map[string]string RemoveMetadata []string - ContentType string `h:"Content-Type"` - DetectContentType bool `h:"X-Detect-Content-Type"` - TempURLKey string `h:"X-Account-Meta-Temp-URL-Key"` - TempURLKey2 string `h:"X-Account-Meta-Temp-URL-Key-2"` + ContentType *string `h:"Content-Type"` + DetectContentType *bool `h:"X-Detect-Content-Type"` + TempURLKey string `h:"X-Account-Meta-Temp-URL-Key"` + TempURLKey2 string `h:"X-Account-Meta-Temp-URL-Key-2"` } // ToAccountUpdateMap formats an UpdateOpts into a map[string]string of headers. diff --git a/openstack/objectstorage/v1/accounts/testing/fixtures.go b/openstack/objectstorage/v1/accounts/testing/fixtures.go index 6cc4b731a0..f9bab9cf5d 100644 --- a/openstack/objectstorage/v1/accounts/testing/fixtures.go +++ b/openstack/objectstorage/v1/accounts/testing/fixtures.go @@ -52,6 +52,9 @@ func HandleUpdateAccountSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "X-Account-Meta-Gophercloud-Test", "accounts") th.TestHeader(t, r, "X-Remove-Account-Meta-Gophercloud-Test-Remove", "remove") + th.TestHeader(t, r, "Content-Type", "") + th.TestHeader(t, r, "X-Detect-Content-Type", "false") + th.TestHeaderUnset(t, r, "X-Account-Meta-Temp-URL-Key") w.Header().Set("Date", "Fri, 17 Jan 2014 16:09:56 UTC") w.WriteHeader(http.StatusNoContent) diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go index 65405b0fde..8ed3c74de6 100644 --- a/openstack/objectstorage/v1/accounts/testing/requests_test.go +++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go @@ -15,8 +15,10 @@ func TestUpdateAccount(t *testing.T) { HandleUpdateAccountSuccessfully(t) options := &accounts.UpdateOpts{ - Metadata: map[string]string{"gophercloud-test": "accounts"}, - RemoveMetadata: []string{"gophercloud-test-remove"}, + Metadata: map[string]string{"gophercloud-test": "accounts"}, + RemoveMetadata: []string{"gophercloud-test-remove"}, + ContentType: new(string), + DetectContentType: new(bool), } res := accounts.Update(fake.ServiceClient(), options) th.AssertNoErr(t, res.Err) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index bea0d9575c..e5dfa4ebcc 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -154,18 +154,18 @@ type UpdateOptsBuilder interface { type UpdateOpts struct { Metadata map[string]string RemoveMetadata []string - ContainerRead string `h:"X-Container-Read"` - ContainerSyncTo string `h:"X-Container-Sync-To"` - ContainerSyncKey string `h:"X-Container-Sync-Key"` - ContainerWrite string `h:"X-Container-Write"` - ContentType string `h:"Content-Type"` - DetectContentType bool `h:"X-Detect-Content-Type"` - RemoveVersionsLocation string `h:"X-Remove-Versions-Location"` - VersionsLocation string `h:"X-Versions-Location"` - RemoveHistoryLocation string `h:"X-Remove-History-Location"` - 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"` + ContainerRead *string `h:"X-Container-Read"` + ContainerSyncTo *string `h:"X-Container-Sync-To"` + ContainerSyncKey *string `h:"X-Container-Sync-Key"` + ContainerWrite *string `h:"X-Container-Write"` + ContentType *string `h:"Content-Type"` + DetectContentType *bool `h:"X-Detect-Content-Type"` + RemoveVersionsLocation string `h:"X-Remove-Versions-Location"` + VersionsLocation string `h:"X-Versions-Location"` + RemoveHistoryLocation string `h:"X-Remove-History-Location"` + 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"` } // ToContainerUpdateMap formats a UpdateOpts into a map of headers. diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index f062a9a9d6..042e39d2fa 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -159,6 +159,11 @@ func HandleUpdateContainerSuccessfully(t *testing.T) { 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") 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 4d9263d646..dcfd1de753 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -130,7 +130,15 @@ func TestUpdateContainer(t *testing.T) { defer th.TeardownHTTP() HandleUpdateContainerSuccessfully(t) - options := &containers.UpdateOpts{Metadata: map[string]string{"foo": "bar"}} + 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(), "testContainer", options) th.AssertNoErr(t, res.Err) } diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index e820026a2e..63f40e9abd 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -394,12 +394,12 @@ type UpdateOptsBuilder interface { type UpdateOpts struct { Metadata map[string]string RemoveMetadata []string - ContentDisposition string `h:"Content-Disposition"` - ContentEncoding string `h:"Content-Encoding"` - ContentType string `h:"Content-Type"` - DeleteAfter int64 `h:"X-Delete-After"` - DeleteAt int64 `h:"X-Delete-At"` - DetectContentType bool `h:"X-Detect-Content-Type"` + ContentDisposition *string `h:"Content-Disposition"` + ContentEncoding *string `h:"Content-Encoding"` + ContentType *string `h:"Content-Type"` + DeleteAfter *int64 `h:"X-Delete-After"` + DeleteAt *int64 `h:"X-Delete-At"` + DetectContentType *bool `h:"X-Detect-Content-Type"` } // ToObjectUpdateMap formats a UpdateOpts into a map of headers. diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go index a1dc81263d..21da11450c 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures.go @@ -284,6 +284,12 @@ func HandleUpdateObjectSuccessfully(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") th.TestHeader(t, r, "X-Object-Meta-Gophercloud-Test", "objects") th.TestHeader(t, r, "X-Remove-Object-Meta-Gophercloud-Test-Remove", "remove") + th.TestHeader(t, r, "Content-Disposition", "") + th.TestHeader(t, r, "Content-Encoding", "") + th.TestHeader(t, r, "Content-Type", "") + th.TestHeaderUnset(t, r, "X-Delete-After") + th.TestHeader(t, r, "X-Delete-At", "0") + th.TestHeader(t, r, "X-Detect-Content-Type", "false") w.WriteHeader(http.StatusAccepted) }) } diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 4495217849..3b7156d0be 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -260,9 +260,16 @@ func TestUpateObjectMetadata(t *testing.T) { defer th.TeardownHTTP() HandleUpdateObjectSuccessfully(t) + s := new(string) + i := new(int64) options := &objects.UpdateOpts{ - Metadata: map[string]string{"Gophercloud-Test": "objects"}, - RemoveMetadata: []string{"Gophercloud-Test-Remove"}, + Metadata: map[string]string{"Gophercloud-Test": "objects"}, + RemoveMetadata: []string{"Gophercloud-Test-Remove"}, + ContentDisposition: s, + ContentEncoding: s, + ContentType: s, + DeleteAt: i, + DetectContentType: new(bool), } res := objects.Update(fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) diff --git a/params.go b/params.go index 219c020a24..6282894d3a 100644 --- a/params.go +++ b/params.go @@ -445,6 +445,9 @@ func BuildHeaders(opts interface{}) (map[string]string, error) { // if the field is set, add it to the slice of query pieces if !isZero(v) { + if v.Kind() == reflect.Ptr { + v = v.Elem() + } switch v.Kind() { case reflect.String: optsMap[tags[0]] = v.String() diff --git a/provider_client.go b/provider_client.go index 916e59ae4c..faa421fdd4 100644 --- a/provider_client.go +++ b/provider_client.go @@ -408,11 +408,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if options.MoreHeaders != nil { for k, v := range options.MoreHeaders { - if v != "" { - req.Header.Set(k, v) - } else { - req.Header.Del(k) - } + req.Header.Set(k, v) } } diff --git a/testhelper/http_responses.go b/testhelper/http_responses.go index cee20f421d..2f20957fe9 100644 --- a/testhelper/http_responses.go +++ b/testhelper/http_responses.go @@ -70,8 +70,21 @@ func TestMethod(t *testing.T, r *http.Request, expected string) { // TestHeader checks that the header on the http.Request matches the expected value. func TestHeader(t *testing.T, r *http.Request, header string, expected string) { - if actual := r.Header.Get(header); expected != actual { - t.Errorf("Header %s = %s, expected %s", header, actual, expected) + if len(r.Header.Values(header)) == 0 { + t.Errorf("Header %s not found, expected %q", header, expected) + return + } + for _, actual := range r.Header.Values(header) { + if expected != actual { + t.Errorf("Header %s = %q, expected %q", header, actual, expected) + } + } +} + +// TestHeaderUnset checks that the header on the http.Request doesn't exist. +func TestHeaderUnset(t *testing.T, r *http.Request, header string) { + if len(r.Header.Values(header)) > 0 { + t.Errorf("Header %s is not expected", header) } } From 94f0a20116d479db31a92860f61cdd9a02a25cd0 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 20 Sep 2021 11:21:16 -0600 Subject: [PATCH 1301/2296] Update CHANGELOG.md --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f796946cc0..565476ad84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ ## 0.22.0 (Unreleased) +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) ## 0.21.0 (September 14, 2021) From 7749e117cb252704a97dc39b3dbb7a423e0aceef Mon Sep 17 00:00:00 2001 From: Dusty Mabe Date: Wed, 29 Sep 2021 22:45:24 -0400 Subject: [PATCH 1302/2296] Image Service v2: Add Updating image.Protected field (#2221) Now we should be able to do something like: ``` updateOpts := images.UpdateOpts{ images.ReplaceImageProtected{ NewProtected: false, }, } image, err = images.Update(client, imageID, updateOpts).Extract() ``` Fixes https://github.com/gophercloud/gophercloud/issues/2220 --- .../openstack/imageservice/v2/images_test.go | 9 +++++++++ openstack/imageservice/v2/images/requests.go | 14 ++++++++++++++ .../imageservice/v2/images/testing/fixtures.go | 6 ++++++ .../v2/images/testing/requests_test.go | 2 ++ 4 files changed, 31 insertions(+) diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/acceptance/openstack/imageservice/v2/images_test.go index 01aedf3b02..ad22bde826 100644 --- a/acceptance/openstack/imageservice/v2/images_test.go +++ b/acceptance/openstack/imageservice/v2/images_test.go @@ -154,6 +154,7 @@ func TestImagesUpdate(t *testing.T) { images.ReplaceImageName{NewName: image.Name + "foo"}, images.ReplaceImageTags{NewTags: newTags}, images.ReplaceImageMinDisk{NewMinDisk: 21}, + images.ReplaceImageProtected{NewProtected: true}, images.UpdateImageProperty{ Op: images.AddOp, Name: "hw_disk_bus", @@ -172,6 +173,7 @@ func TestImagesUpdate(t *testing.T) { tools.PrintResource(t, newImage.Properties) th.AssertEquals(t, newImage.Name, image.Name+"foo") + th.AssertEquals(t, newImage.Protected, true) sort.Strings(newTags) sort.Strings(newImage.Tags) @@ -184,4 +186,11 @@ func TestImagesUpdate(t *testing.T) { if _, ok := newImage.Properties["architecture"]; ok { t.Fatal("architecture property still exists") } + + // Now change image protection back to false or delete will fail + updateOpts = images.UpdateOpts{ + images.ReplaceImageProtected{NewProtected: false}, + } + _, err = images.Update(client, image.ID, updateOpts).Extract() + th.AssertNoErr(t, err) } diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 7947645eb4..2ab609cbca 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -373,6 +373,20 @@ func (r ReplaceImageMinRam) ToImagePatchMap() map[string]interface{} { } } +// ReplaceImageProtected represents an updated protected property request. +type ReplaceImageProtected struct { + NewProtected bool +} + +// ToImagePatchMap assembles a request body based on ReplaceImageProtected +func (r ReplaceImageProtected) ToImagePatchMap() map[string]interface{} { + return map[string]interface{}{ + "op": "replace", + "path": "/protected", + "value": r.NewProtected, + } +} + // UpdateOp represents a valid update operation. type UpdateOp string diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go index 5ae66d5e05..6627cf1182 100644 --- a/openstack/imageservice/v2/images/testing/fixtures.go +++ b/openstack/imageservice/v2/images/testing/fixtures.go @@ -331,6 +331,11 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "path": "/os_hidden", "value": false }, + { + "op": "replace", + "path": "/protected", + "value": true + }, { "op": "add", "path": "/empty_value", @@ -348,6 +353,7 @@ func HandleImageUpdateSuccessfully(t *testing.T) { "status": "active", "visibility": "public", "os_hidden": false, + "protected": true, "size": 2254249, "checksum": "2cec138d7dae2aa59038ef8c9aec2390", "tags": [ diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index c4b97677ec..e349777642 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -266,6 +266,7 @@ func TestUpdateImage(t *testing.T) { images.ReplaceImageMinDisk{NewMinDisk: 21}, images.ReplaceImageMinRam{NewMinRam: 1024}, images.ReplaceImageHidden{NewHidden: false}, + images.ReplaceImageProtected{NewProtected: true}, images.UpdateImageProperty{ Op: images.AddOp, Name: "empty_value", @@ -288,6 +289,7 @@ func TestUpdateImage(t *testing.T) { Status: images.ImageStatusActive, Visibility: images.ImageVisibilityPublic, Hidden: false, + Protected: true, SizeBytes: sizebytes, Checksum: checksum, From 81b5e5ca30b0c3a0b7c9de6dc637e1e437a9dde3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 29 Sep 2021 20:46:31 -0600 Subject: [PATCH 1303/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 565476ad84..e5c9928328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ BREAKING CHANGES * `objectstorage/v1/objects.UpdateOpts.DeleteAt` * `objectstorage/v1/objects.UpdateOpts.DetectContentType` +IMPROVEMENTS + +* Added `imageservice/v2/images.ReplaceImageProtected` to allow the `protected` field to be updated [GH-2221](https://github.com/gophercloud/gophercloud/pull/2221) + 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) From a7e4d0e3721b3b07284b7b3a31e3ac4ff1d96601 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 29 Sep 2021 20:47:24 -0600 Subject: [PATCH 1304/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5c9928328..76141a591b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,10 +18,6 @@ BREAKING CHANGES * `objectstorage/v1/objects.UpdateOpts.DeleteAt` * `objectstorage/v1/objects.UpdateOpts.DetectContentType` -IMPROVEMENTS - -* Added `imageservice/v2/images.ReplaceImageProtected` to allow the `protected` field to be updated [GH-2221](https://github.com/gophercloud/gophercloud/pull/2221) - 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) @@ -29,6 +25,7 @@ BUG FIXES 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) ## 0.21.0 (September 14, 2021) From 0b6da3700da658f5bd0dac564b210e5f038a0dab Mon Sep 17 00:00:00 2001 From: schirevko Date: Fri, 1 Oct 2021 17:25:26 +0300 Subject: [PATCH 1305/2296] Expose the real error message of NotFound error (#2223) This change exposes the error message of NotFound error from openstack API, this is helpful to find out the reason of request failure. --- errors.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/errors.go b/errors.go index 77cabf6a92..1f504ea834 100644 --- a/errors.go +++ b/errors.go @@ -179,7 +179,11 @@ func (e ErrDefault403) Error() string { return e.choseErrString() } func (e ErrDefault404) Error() string { - return "Resource not found" + e.DefaultErrString = fmt.Sprintf( + "Resource not found: [%s %s], error message: %s", + e.Method, e.URL, e.Body, + ) + return e.choseErrString() } func (e ErrDefault405) Error() string { return "Method not allowed" From 2e4a09a149efdd97abae96d19866253a92963fe7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 1 Oct 2021 08:26:19 -0600 Subject: [PATCH 1306/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76141a591b..0b6578675d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ 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) ## 0.21.0 (September 14, 2021) From f581ce6eb375e26af0691e98d6357e13716510e9 Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Tue, 5 Oct 2021 22:53:40 +0200 Subject: [PATCH 1307/2296] Support HttpHeaders in create_subscription (#2224) This commit adds support for HttpHeaders in the create_subscription. It was added during the Xena release of Ironic in [1] after the initial support for create subscriptions [2] We don't need to modify the response of get_subscription because redfish doesn't return the value of the HttpHeaders for the enduser. [1] https://review.opendev.org/c/openstack/ironic/+/806859 [2] https://review.opendev.org/c/openstack/ironic/+/801064 --- openstack/baremetal/v1/nodes/doc.go | 1 + openstack/baremetal/v1/nodes/requests.go | 9 +++++---- openstack/baremetal/v1/nodes/testing/fixtures.go | 4 +++- openstack/baremetal/v1/nodes/testing/requests_test.go | 1 + 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/openstack/baremetal/v1/nodes/doc.go b/openstack/baremetal/v1/nodes/doc.go index 68c553ff32..6ee2aff5e4 100644 --- a/openstack/baremetal/v1/nodes/doc.go +++ b/openstack/baremetal/v1/nodes/doc.go @@ -182,6 +182,7 @@ Example to create a subscription Context: "MyContext", Protocol: "Redfish", EventTypes: ["Alert"], + HttpHeaders: [{"Key1":"Value1"}, {"Key2":"Value2"}], } newSubscription, err := nodes.CreateSubscription(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionCreateOpt).Extract() diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index e0f23486a5..ed3fe3439f 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -801,10 +801,11 @@ func DeleteSubscription(client *gophercloud.ServiceClient, id string, method Cal // The desired subscription to be created from the baremetal node. type CreateSubscriptionOpts struct { - Destination string `json:"Destination"` - EventTypes []string `json:"EventTypes,omitempty"` - Context string `json:"Context,omitempty"` - Protocol string `json:"Protocol,omitempty"` + Destination string `json:"Destination"` + EventTypes []string `json:"EventTypes,omitempty"` + HttpHeaders []map[string]string `json:"HttpHeaders,omitempty"` + Context string `json:"Context,omitempty"` + Protocol string `json:"Protocol,omitempty"` } // ToCreateSubscriptionMap assembles a query based on the contents of CallVendorPassthruOpts and a request body based on the contents of a CreateSubscriptionOpts diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index 415af5d593..409fbe6342 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -790,6 +790,7 @@ const NodeCreateSubscriptionVendorPassthruAllParametersBody = ` "Context": "gophercloud", "Destination": "https://someurl", "EventTypes": ["Alert"], + "HttpHeaders": [{"Context-Type":"application/json"}], "Id": "eaa43e2-018a-424e-990a-cbf47c62ef80", "Protocol": "Redfish" } @@ -1481,7 +1482,8 @@ func HandleCreateSubscriptionVendorPassthruAllParametersSuccessfully(t *testing. th.TestJSONRequest(t, r, ` { "Context": "gophercloud", - "EventTypes": ["Alert"], + "EventTypes": ["Alert"], + "HttpHeaders": [{"Content-Type":"application/json"}], "Protocol": "Redfish", "Destination" : "https://someurl" } diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index d3000eee09..ac6ded0dd7 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -650,6 +650,7 @@ func TestCreateSubscriptionAllParameters(t *testing.T) { Context: "gophercloud", Protocol: "Redfish", EventTypes: []string{"Alert"}, + HttpHeaders: []map[string]string{{"Content-Type": "application/json"}}, } actual, err := nodes.CreateSubscription(c, "1234asdf", method, createOpt).Extract() th.AssertNoErr(t, err) From 0798d97c18c9112c2898ef7cf3656ace1542e92e Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 5 Oct 2021 14:57:39 -0600 Subject: [PATCH 1308/2296] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b6578675d..75080abf99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ 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) From 3c0eaeb6f2432a6ad265d1324c596235bc059d08 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 7 Oct 2021 09:26:06 -0600 Subject: [PATCH 1309/2296] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75080abf99..c32412a679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.22.0 (Unreleased) +## 0.23.0 (Unreleased) + +## 0.22.0 (October 7, 2021) BREAKING CHANGES From 8225638305271226707ce08235dfc6d5fe54fca4 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 9 Oct 2021 11:29:32 +0200 Subject: [PATCH 1310/2296] nova: add support for keypair type --- openstack/compute/v2/extensions/keypairs/requests.go | 4 ++++ openstack/compute/v2/extensions/keypairs/results.go | 3 +++ 2 files changed, 7 insertions(+) diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go index 9b0357390d..aba11fb435 100644 --- a/openstack/compute/v2/extensions/keypairs/requests.go +++ b/openstack/compute/v2/extensions/keypairs/requests.go @@ -81,6 +81,10 @@ type CreateOpts struct { // This requires microversion 2.10 or higher. UserID string `json:"user_id,omitempty"` + // The type of the keypair. Allowed values are ssh or x509 + // This requires microversion 2.2 or higher. + Type string `json:"type,omitempty"` + // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. // If provided, this key will be imported and no new key will be created. PublicKey string `json:"public_key,omitempty"` diff --git a/openstack/compute/v2/extensions/keypairs/results.go b/openstack/compute/v2/extensions/keypairs/results.go index 2d71034b10..6e8db69954 100644 --- a/openstack/compute/v2/extensions/keypairs/results.go +++ b/openstack/compute/v2/extensions/keypairs/results.go @@ -27,6 +27,9 @@ type KeyPair struct { // UserID is the user who owns this KeyPair. UserID string `json:"user_id"` + + // The type of the keypair + Type string `json:"type"` } // KeyPairPage stores a single page of all KeyPair results from a List call. From db8771c3d9b874f76d3bc7632db3108d9f5f9281 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 12 Oct 2021 13:06:38 -0400 Subject: [PATCH 1311/2296] Add ListBGPSpeakers (continued) (#2229) * Add ListBGPSpeakers * Update the code per Peer Review 1. Change BGPSpeakerPage to SinglePageBase 2. Change the return type of ListBGPSpeakers to ListBGPSpeakersResult 3. Implement the Extract method of ListBGPSpeakersResult in agents 4. misc: delete unused stuff for now and change the wording of doc.go * Rename bgp/speaker to bgp/speakers * Rename ListBGPSpeakerResult.Extract to ExtractBGPSpeakers * Change the type of ListBGPSpeakersResult to SinglePageBase * Fix doc * Delete an empty line for gofmt Co-authored-by: Jonathan Huang --- .../networking/v2/extensions/agents/doc.go | 15 ++++ .../v2/extensions/agents/requests.go | 9 +++ .../v2/extensions/agents/results.go | 21 ++++++ .../v2/extensions/agents/testing/fixtures.go | 24 +++++++ .../agents/testing/requests_test.go | 36 ++++++++++ .../networking/v2/extensions/agents/urls.go | 6 ++ .../v2/extensions/bgp/speakers/results.go | 69 +++++++++++++++++++ 7 files changed, 180 insertions(+) create mode 100644 openstack/networking/v2/extensions/bgp/speakers/results.go diff --git a/openstack/networking/v2/extensions/agents/doc.go b/openstack/networking/v2/extensions/agents/doc.go index 1a6f663a3e..e20b58c797 100644 --- a/openstack/networking/v2/extensions/agents/doc.go +++ b/openstack/networking/v2/extensions/agents/doc.go @@ -83,5 +83,20 @@ Example to Remove a network from a DHCP Agent panic(err) } +Example to List BGP speakers by dragent + + pages, err := agents.ListBGPSpeakers(c, agentID).AllPages() + if err != nil { + log.Panicf("%v", err) + } + allSpeakers, err := agents.ExtractBGPSpeakers(pages) + if err != nil { + log.Panicf("%v", err) + } + for _, s := range allSpeakers { + log.Printf("%v", s) + } + */ + package agents diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 7c81864145..0aff95af0e 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -149,3 +149,12 @@ func RemoveDHCPNetwork(c *gophercloud.ServiceClient, id string, networkID string _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ListBGPSpeakers list the BGP Speakers hosted by a specific dragent +// GET /v2.0/agents/{agent-id}/bgp-drinstances +func ListBGPSpeakers(c *gophercloud.ServiceClient, agentID string) pagination.Pager { + url := listBGPSpeakersURL(c, agentID) + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return ListBGPSpeakersResult{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/networking/v2/extensions/agents/results.go b/openstack/networking/v2/extensions/agents/results.go index ba9d4b3421..4e84da2ef6 100644 --- a/openstack/networking/v2/extensions/agents/results.go +++ b/openstack/networking/v2/extensions/agents/results.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/pagination" ) @@ -173,3 +174,23 @@ func (r ListDHCPNetworksResult) Extract() ([]networks.Network, error) { err := r.ExtractInto(&s) return s.Networks, err } + +// ListBGPSpeakersResult is the respone of agents/{id}/bgp-speakers +type ListBGPSpeakersResult struct { + pagination.SinglePageBase +} + +func (r ListBGPSpeakersResult) IsEmpty() (bool, error) { + speakers, err := ExtractBGPSpeakers(r) + return 0 == len(speakers), err +} + +// ExtractBGPSpeakers inteprets the ListBGPSpeakersResult into an array of BGP speakers +func ExtractBGPSpeakers(r pagination.Page) ([]speakers.BGPSpeaker, error) { + var s struct { + Speakers []speakers.BGPSpeaker `json:"bgp_speakers"` + } + + err := (r.(ListBGPSpeakersResult)).ExtractInto(&s) + return s.Speakers, err +} diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures.go b/openstack/networking/v2/extensions/agents/testing/fixtures.go index ea25d00776..9ab0c91a62 100644 --- a/openstack/networking/v2/extensions/agents/testing/fixtures.go +++ b/openstack/networking/v2/extensions/agents/testing/fixtures.go @@ -231,3 +231,27 @@ const ScheduleDHCPNetworkRequest = ` "network_id": "1ae075ca-708b-4e66-b4a7-b7698632f05f" } ` + +const ListBGPSpeakersResult = ` +{ + "bgp_speakers": [ + { + "peers": [ + "cc4e1b15-e8b1-415e-b39a-3b087ed567b4", + "4022d79f-835e-4271-b5d1-d90dce5662df" + ], + "project_id": "89f56d77-fee7-4b2f-8b1e-583717a93690", + "name": "gophercloud-testing-speaker", + "tenant_id": "5c372f0b-051e-485c-a82c-9dd732e7df83", + "local_as": 12345, + "advertise_tenant_networks": true, + "networks": [ + "932d70b1-db21-4542-b520-d5e73ddee407" + ], + "ip_version": 4, + "advertise_floating_ip_host_routes": true, + "id": "cab00464-284d-4251-9798-2b27db7b1668" + } + ] +} +` diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index b4c848ceb9..323e750c4a 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -205,3 +205,39 @@ func TestRemoveDHCPNetwork(t *testing.T) { err := agents.RemoveDHCPNetwork(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "1ae075ca-708b-4e66-b4a7-b7698632f05f").ExtractErr() th.AssertNoErr(t, err) } + +func TestListBGPSpeakers(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + agentID := "30d76012-46de-4215-aaa1-a1630d01d891" + + th.Mux.HandleFunc("/v2.0/agents/"+agentID+"/bgp-drinstances", + 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, ListBGPSpeakersResult) + }) + + count := 0 + agents.ListBGPSpeakers(fake.ServiceClient(), agentID).EachPage( + func(page pagination.Page) (bool, error) { + count++ + actual, err := agents.ExtractBGPSpeakers(page) + + th.AssertNoErr(t, err) + th.AssertEquals(t, len(actual), 1) + th.AssertEquals(t, actual[0].ID, "cab00464-284d-4251-9798-2b27db7b1668") + th.AssertEquals(t, actual[0].Name, "gophercloud-testing-speaker") + th.AssertEquals(t, actual[0].LocalAS, 12345) + th.AssertEquals(t, actual[0].IPVersion, 4) + 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 d8582013d6..e357d0a08c 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 bgpSpeakersResourcePath = "bgp-drinstances" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id) @@ -44,3 +45,8 @@ func scheduleDHCPNetworkURL(c *gophercloud.ServiceClient, id string) string { func removeDHCPNetworkURL(c *gophercloud.ServiceClient, id string, networkID string) string { return c.ServiceURL(resourcePath, id, dhcpNetworksResourcePath, networkID) } + +// return /v2.0/agents/{agent-id}/bgp-drinstances +func listBGPSpeakersURL(c *gophercloud.ServiceClient, agentID string) string { + return c.ServiceURL(resourcePath, agentID, bgpSpeakersResourcePath) +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/results.go b/openstack/networking/v2/extensions/bgp/speakers/results.go new file mode 100644 index 0000000000..322728a70a --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/speakers/results.go @@ -0,0 +1,69 @@ +package speakers + +import ( + // "encoding/json" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// BGPSpeaker BGP Speaker +type BGPSpeaker struct { + // UUID for the bgp speaker + ID string `json:"id"` + + // Human-readable name for the bgp speaker. Might not be unique. + Name string `json:"name"` + + // TenantID is the project owner of the bgp speaker. + TenantID string `json:"tenant_id"` + + // ProjectID is the project owner of the bgp speaker. + ProjectID string `json:"project_id"` + + // If the speaker would advertise floating ip host routes + AdvertiseFloatingIPHostRoutes bool `json:"advertise_floating_ip_host_routes"` + + // If the speaker would advertise tenant networks + AdvertiseTenantNetworks bool `json:"advertise_tenant_networks"` + + // IP version + IPVersion int `json:"ip_version"` + + // Local Autonomous System + LocalAS int `json:"local_as"` + + // The uuid of the Networks configured with this speaker + Networks []string `json:"networks"` + + // The uuid of the BGP Peer Configured with this speaker + Peers []string `json:"peers"` +} + +// BGPSpeakerPage is the page returned by a pager when traversing over a +// collection of bgp speakers. +type BGPSpeakerPage struct { + pagination.SinglePageBase +} + +// IsEmpty checks whether a BGPSpeakerPage struct is empty. +func (r BGPSpeakerPage) IsEmpty() (bool, error) { + is, err := ExtractBGPSpeakers(r) + return len(is) == 0, err +} + +// ExtractBGPSpeakers accepts a Page struct, specifically a BGPSpeakerPage struct, +// and extracts the elements into a slice of BGPSpeaker structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractBGPSpeakers(r pagination.Page) ([]BGPSpeaker, error) { + var s []BGPSpeaker + err := ExtractBGPSpeakersInto(r, &s) + return s, err +} + +func ExtractBGPSpeakersInto(r pagination.Page, v interface{}) error { + return r.(BGPSpeakerPage).Result.ExtractIntoSlicePtr(v, "bgp_speakers") +} From e13988ee6098719e280f4f3b3120fc59a8db7df2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 12 Oct 2021 11:08:58 -0600 Subject: [PATCH 1312/2296] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c32412a679..9320d4a608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.23.0 (Unreleased) +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) + ## 0.22.0 (October 7, 2021) BREAKING CHANGES From f993da3d81034ffd10cce933967df760c351a906 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 14 Oct 2021 05:11:11 +0200 Subject: [PATCH 1313/2296] identity v3: extend role assignments values with domain info (#2235) * identity v3: extend role assignments values with domain info * Fixing acceptance tests Co-authored-by: Joe Topjian --- .../openstack/identity/v3/roles_test.go | 24 +++++++++++++++++ openstack/identity/v3/roles/results.go | 15 ++++++----- .../identity/v3/roles/testing/fixtures.go | 27 ++++++++++++++----- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/acceptance/openstack/identity/v3/roles_test.go b/acceptance/openstack/identity/v3/roles_test.go index 1177364635..799204f6f9 100644 --- a/acceptance/openstack/identity/v3/roles_test.go +++ b/acceptance/openstack/identity/v3/roles_test.go @@ -484,10 +484,12 @@ func TestRolesAssignToUserOnProject(t *testing.T) { ProjectID: project.ID, }) + iTrue := true lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeProjectID: project.ID, UserID: user.ID, + IncludeNames: &iTrue, } allPages, err := roles.ListAssignments(client, lao).AllPages() @@ -504,6 +506,10 @@ func TestRolesAssignToUserOnProject(t *testing.T) { if roleAssignment.Role.ID == role.ID { found = true } + + if roleAssignment.User.Domain.ID == "" || roleAssignment.Scope.Project.Domain.ID == "" { + found = false + } } th.AssertEquals(t, found, true) @@ -551,10 +557,12 @@ func TestRolesAssignToUserOnDomain(t *testing.T) { DomainID: domain.ID, }) + iTrue := true lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeDomainID: domain.ID, UserID: user.ID, + IncludeNames: &iTrue, } allPages, err := roles.ListAssignments(client, lao).AllPages() @@ -571,6 +579,10 @@ func TestRolesAssignToUserOnDomain(t *testing.T) { if roleAssignment.Role.ID == role.ID { found = true } + + if roleAssignment.User.Domain.ID == "" { + found = false + } } th.AssertEquals(t, found, true) @@ -621,10 +633,12 @@ func TestRolesAssignToGroupOnDomain(t *testing.T) { DomainID: domain.ID, }) + iTrue := true lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeDomainID: domain.ID, GroupID: group.ID, + IncludeNames: &iTrue, } allPages, err := roles.ListAssignments(client, lao).AllPages() @@ -641,6 +655,10 @@ func TestRolesAssignToGroupOnDomain(t *testing.T) { if roleAssignment.Role.ID == role.ID { found = true } + + if roleAssignment.Group.Domain.ID == "" { + found = false + } } th.AssertEquals(t, found, true) @@ -688,10 +706,12 @@ func TestRolesAssignToGroupOnProject(t *testing.T) { ProjectID: project.ID, }) + iTrue := true lao := roles.ListAssignmentsOpts{ RoleID: role.ID, ScopeProjectID: project.ID, GroupID: group.ID, + IncludeNames: &iTrue, } allPages, err := roles.ListAssignments(client, lao).AllPages() @@ -708,6 +728,10 @@ func TestRolesAssignToGroupOnProject(t *testing.T) { if roleAssignment.Role.ID == role.ID { found = true } + + if roleAssignment.Scope.Project.Domain.ID == "" || roleAssignment.Group.Domain.ID == "" { + found = false + } } th.AssertEquals(t, found, true) diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index da2c348e77..1ae032a1e3 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -156,20 +156,23 @@ type Domain struct { // Project represents a project in a role assignment scope. type Project struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` + Domain Domain `json:"domain,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // User represents a user in a role assignment scope. type User struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` + Domain Domain `json:"domain,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // Group represents a group in a role assignment scope. type Group struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` + Domain Domain `json:"domain,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // RoleAssignmentPage is a single page of RoleAssignments results. diff --git a/openstack/identity/v3/roles/testing/fixtures.go b/openstack/identity/v3/roles/testing/fixtures.go index bcbd8e2522..33d8f02e2a 100644 --- a/openstack/identity/v3/roles/testing/fixtures.go +++ b/openstack/identity/v3/roles/testing/fixtures.go @@ -113,6 +113,9 @@ const ListAssignmentOutput = ` } }, "user": { + "domain": { + "id": "161718" + }, "id": "313233" } }, @@ -126,10 +129,16 @@ const ListAssignmentOutput = ` }, "scope": { "project": { + "domain": { + "id": "161718" + }, "id": "456789" } }, "user": { + "domain": { + "id": "161718" + }, "id": "313233" } } @@ -152,17 +161,21 @@ const ListAssignmentWithNamesOutput = ` }, "role": { "id": "123456", - "name": "include_names_role" + "name": "include_names_role" }, "scope": { "domain": { "id": "161718", - "name": "52833" + "name": "52833" } }, "user": { + "domain": { + "id": "161718", + "name": "52833" + }, "id": "313233", - "name": "example-user-name" + "name": "example-user-name" } } ], @@ -358,15 +371,15 @@ func HandleUnassignSuccessfully(t *testing.T) { var FirstRoleAssignment = roles.RoleAssignment{ Role: roles.AssignedRole{ID: "123456"}, Scope: roles.Scope{Domain: roles.Domain{ID: "161718"}}, - User: roles.User{ID: "313233"}, + User: roles.User{Domain: roles.Domain{ID: "161718"}, ID: "313233"}, Group: roles.Group{}, } // SecondRoleAssignemnt is the second role assignemnt in the List request. var SecondRoleAssignment = roles.RoleAssignment{ Role: roles.AssignedRole{ID: "123456"}, - Scope: roles.Scope{Project: roles.Project{ID: "456789"}}, - User: roles.User{ID: "313233"}, + Scope: roles.Scope{Project: roles.Project{Domain: roles.Domain{ID: "161718"}, ID: "456789"}}, + User: roles.User{Domain: roles.Domain{ID: "161718"}, ID: "313233"}, Group: roles.Group{}, } @@ -374,7 +387,7 @@ var SecondRoleAssignment = roles.RoleAssignment{ var ThirdRoleAssignment = roles.RoleAssignment{ Role: roles.AssignedRole{ID: "123456", Name: "include_names_role"}, Scope: roles.Scope{Domain: roles.Domain{ID: "161718", Name: "52833"}}, - User: roles.User{ID: "313233", Name: "example-user-name"}, + User: roles.User{Domain: roles.Domain{ID: "161718", Name: "52833"}, ID: "313233", Name: "example-user-name"}, Group: roles.Group{}, } From dc5a91fe03b4bb6e7f40bc10e3cda26a933c66c7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 13 Oct 2021 21:12:20 -0600 Subject: [PATCH 1314/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9320d4a608..f9b67252ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ 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) ## 0.22.0 (October 7, 2021) From 5e2daf8a980fbc0ff2dde40d5b6ad6270e1513f0 Mon Sep 17 00:00:00 2001 From: Parasyris Nikolaos Date: Mon, 18 Oct 2021 02:15:51 +0200 Subject: [PATCH 1315/2296] Octavia: Add tags on pools (#2237) Add support for tags on octavia pools. For #2236 --- openstack/loadbalancer/v2/pools/doc.go | 3 +++ openstack/loadbalancer/v2/pools/requests.go | 6 ++++++ openstack/loadbalancer/v2/pools/results.go | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/openstack/loadbalancer/v2/pools/doc.go b/openstack/loadbalancer/v2/pools/doc.go index d26de312cb..5657cd8733 100644 --- a/openstack/loadbalancer/v2/pools/doc.go +++ b/openstack/loadbalancer/v2/pools/doc.go @@ -28,6 +28,7 @@ Example to Create a Pool LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", + Tags: []string{"test"}, LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", } @@ -40,8 +41,10 @@ Example to Update a Pool poolID := "d67d56a6-4a86-4688-a282-f46444705c64" + newTags := []string{"prod"} updateOpts := pools.UpdateOpts{ Name: "new-name", + Tags: &newTags, } pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 0379d0385c..087e95cab2 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -135,6 +135,9 @@ type CreateOpts struct { // This is only possible to use when creating a fully populated // Loadbalancer. Monitor *monitors.CreateOpts `json:"healthmonitor,omitempty"` + + // Tags is a set of resource tags. New in version 2.5 + Tags []string `json:"tags,omitempty"` } // ToPoolCreateMap builds a request body from CreateOpts. @@ -185,6 +188,9 @@ type UpdateOpts struct { // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Tags is a set of resource tags. New in version 2.5 + Tags *[]string `json:"tags,omitempty"` } // ToPoolUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index b4883e9986..f5b8829fd1 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -102,6 +102,10 @@ type Pool struct { // The operating status of the pool. OperatingStatus string `json:"operating_status"` + + // 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"` } // PoolPage is the page returned by a pager when traversing over a From 05e8ab1214898f4b7aa98cf141b65e6279448835 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 17 Oct 2021 18:17:37 -0600 Subject: [PATCH 1316/2296] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9b67252ab..36640448ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ IMPROVEMENTS * 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 `openstack/loadbalancer/v2/pools.CreateOpts.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) +* Added `openstack/loadbalancer/v2/pools.UpdateOpts.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) +* Added `openstack/loadbalancer/v2/pools.Pool.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) ## 0.22.0 (October 7, 2021) From 44fac004e12ae3eea162aa36c2094ce163c01c81 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sat, 23 Oct 2021 11:24:50 -0400 Subject: [PATCH 1317/2296] Add List and Get method for bgp/speakers (#2238) * Add bgp/speakers/List and Get method * Fix for go fmt Co-authored-by: Jonathan Huang --- .../v2/extensions/bgp/speakers/doc.go | 34 ++++++++++ .../v2/extensions/bgp/speakers/requests.go | 21 +++++++ .../v2/extensions/bgp/speakers/results.go | 20 +++++- .../v2/extensions/bgp/speakers/testing/doc.go | 2 + .../bgp/speakers/testing/fixture.go | 63 +++++++++++++++++++ .../bgp/speakers/testing/requests_test.go | 60 ++++++++++++++++++ .../v2/extensions/bgp/speakers/urls.go | 25 ++++++++ 7 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 openstack/networking/v2/extensions/bgp/speakers/doc.go create mode 100644 openstack/networking/v2/extensions/bgp/speakers/requests.go create mode 100644 openstack/networking/v2/extensions/bgp/speakers/testing/doc.go create mode 100644 openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go create mode 100644 openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/bgp/speakers/urls.go diff --git a/openstack/networking/v2/extensions/bgp/speakers/doc.go b/openstack/networking/v2/extensions/bgp/speakers/doc.go new file mode 100644 index 0000000000..23f21e35a9 --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/speakers/doc.go @@ -0,0 +1,34 @@ +package speakers + +/* +Package speakers contains the functionality for working with Neutron bgp speakers. + + +1. List BGP Speakers, e.g. GET /bgp-speakers + +Example: + + pages, err := speakers.List(c).AllPages() + if err != nil { + log.Panic(err) + } + allSpeakers, err := speakers.ExtractBGPSpeakers(pages) + if err != nil { + log.Panic(err) + } + + for _, speaker := range allSpeakers { + log.Printf("%+v", speaker) + } + + +2. Get BGP speakers, e.g. GET /bgp-speakers/{id} + +Example: + + speaker, err := speakers.Get(c, id).Extract() + if err != nil { + log.Panic(nil) + } + log.Printf("%+v", *speaker) +*/ diff --git a/openstack/networking/v2/extensions/bgp/speakers/requests.go b/openstack/networking/v2/extensions/bgp/speakers/requests.go new file mode 100644 index 0000000000..58947b3e8b --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/speakers/requests.go @@ -0,0 +1,21 @@ +package speakers + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List the bgp speakers +func List(c *gophercloud.ServiceClient) pagination.Pager { + url := listURL(c) + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return BGPSpeakerPage{pagination.SinglePageBase(r)} + }) +} + +// Get retrieve the specific bgp speaker 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/speakers/results.go b/openstack/networking/v2/extensions/bgp/speakers/results.go index 322728a70a..1bd5f68f59 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/results.go +++ b/openstack/networking/v2/extensions/bgp/speakers/results.go @@ -1,15 +1,27 @@ package speakers import ( - // "encoding/json" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) +const jroot = "bgp_speaker" + type commonResult struct { gophercloud.Result } +// Extract is a function that accepts a result and extracts a bgp speaker resource. +func (r commonResult) Extract() (*BGPSpeaker, error) { + var s BGPSpeaker + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, jroot) +} + // BGPSpeaker BGP Speaker type BGPSpeaker struct { // UUID for the bgp speaker @@ -67,3 +79,9 @@ func ExtractBGPSpeakers(r pagination.Page) ([]BGPSpeaker, error) { func ExtractBGPSpeakersInto(r pagination.Page, v interface{}) error { return r.(BGPSpeakerPage).Result.ExtractIntoSlicePtr(v, "bgp_speakers") } + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a BGPSpeaker. +type GetResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/doc.go b/openstack/networking/v2/extensions/bgp/speakers/testing/doc.go new file mode 100644 index 0000000000..b2d8da9878 --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing for bgp speakers +package testing diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go new file mode 100644 index 0000000000..137682e9bf --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go @@ -0,0 +1,63 @@ +package testing + +import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" + +const ListBGPSpeakerResult = ` +{ + "bgp_speakers": [ + { + "peers": [ + "afacc0e8-6b66-44e4-be53-a1ef16033ceb", + "acd7c4a1-e243-4fe5-80f9-eba8f143ac1d" + ], + "advertise_floating_ip_host_routes": true, + "name": "gophercloud-testing-speaker", + "tenant_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "local_as": 56789, + "id": "ab01ade1-ae62-43c9-8a1f-3c24225b96d8", + "ip_version": 4, + "project_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "networks": [ + "acdc6339-7d2d-411f-82bb-e6cc3ad9eb9f" + ], + "advertise_tenant_networks": true + } + ] +} +` + +var BGPSpeaker1 = speakers.BGPSpeaker{ + ID: "ab01ade1-ae62-43c9-8a1f-3c24225b96d8", + Name: "gophercloud-testing-speaker", + TenantID: "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + ProjectID: "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + AdvertiseFloatingIPHostRoutes: true, + AdvertiseTenantNetworks: true, + IPVersion: 4, + LocalAS: 56789, + Networks: []string{"acdc6339-7d2d-411f-82bb-e6cc3ad9eb9f"}, + Peers: []string{"afacc0e8-6b66-44e4-be53-a1ef16033ceb", + "acd7c4a1-e243-4fe5-80f9-eba8f143ac1d"}, +} + +const GetBGPSpeakerResult = ` +{ + "bgp_speaker": { + "peers": [ + "afacc0e8-6b66-44e4-be53-a1ef16033ceb", + "acd7c4a1-e243-4fe5-80f9-eba8f143ac1d" + ], + "project_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "name": "gophercloud-testing-speaker", + "tenant_id": "7fa3f96b-17ee-4d1b-8fbf-fe889bb1f1d0", + "local_as": 56789, + "advertise_tenant_networks": true, + "networks": [ + "acdc6339-7d2d-411f-82bb-e6cc3ad9eb9f" + ], + "ip_version": 4, + "advertise_floating_ip_host_routes": true, + "id": "ab01ade1-ae62-43c9-8a1f-3c24225b96d8" + } +} +` diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go new file mode 100644 index 0000000000..4f257302c7 --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/speakers/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/speakers" + "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-speakers", + 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, ListBGPSpeakerResult) + }) + count := 0 + + speakers.List(fake.ServiceClient()).EachPage( + func(page pagination.Page) (bool, error) { + count++ + actual, err := speakers.ExtractBGPSpeakers(page) + + if err != nil { + t.Errorf("Failed to extract BGP speakers: %v", err) + return false, nil + } + expected := []speakers.BGPSpeaker{BGPSpeaker1} + th.CheckDeepEquals(t, expected, actual) + return true, nil + }) +} + +func TestGet(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, "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) + }) + + s, err := speakers.Get(fake.ServiceClient(), bgpSpeakerID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, *s, BGPSpeaker1) +} diff --git a/openstack/networking/v2/extensions/bgp/speakers/urls.go b/openstack/networking/v2/extensions/bgp/speakers/urls.go new file mode 100644 index 0000000000..06b636fdb8 --- /dev/null +++ b/openstack/networking/v2/extensions/bgp/speakers/urls.go @@ -0,0 +1,25 @@ +package speakers + +import "github.com/gophercloud/gophercloud" + +const urlBase = "bgp-speakers" + +// return /v2.0/bgp-speakers/{bgp-speaker-id} +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(urlBase, id) +} + +// return /v2.0/bgp-speakers +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(urlBase) +} + +// return /v2.0/bgp-speakers/{bgp-speaker-id} +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +// return /v2.0/bgp-speakers +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} From 754fd24f7576a953fd40d51135b27568aa228f70 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 23 Oct 2021 09:25:49 -0600 Subject: [PATCH 1318/2296] Update CHANGELOG.md --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36640448ef..3034df9fce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,11 @@ IMPROVEMENTS * 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 `openstack/loadbalancer/v2/pools.CreateOpts.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) -* Added `openstack/loadbalancer/v2/pools.UpdateOpts.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) -* Added `openstack/loadbalancer/v2/pools.Pool.Tags` [GH-2237](https://github.com/gophercloud/gophercloud/pull/2237) +* 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) ## 0.22.0 (October 7, 2021) From ecbce8932548dd33e3fd6a63e9f1f6f224537868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 25 Oct 2021 19:30:01 +0200 Subject: [PATCH 1319/2296] Fix openstack/common/extension doc examples (#2243) * Docs: Fix calls to pagination.Pager.AllPages() The doc example for `openstack/common/extension` would otherwise fail compilation with: extensions.List(networkClient).Allpages undefined (type pagination.Pager has no field or method Allpages, but does have AllPages) * Fix formatting issue with doc examples Some doc examples were wrongly calling `fmt.Println()` with a formatting specifier instead of `fmt.Printf()`. This caused the examples to print the literal string `%+v` with a new line before printing the argument using its default format. This clears the following compilation warning: Println call has possible formatting directive %+v Got all files with `ag "Println.+%"`. --- openstack/clustering/v1/actions/doc.go | 2 +- openstack/clustering/v1/events/doc.go | 2 +- openstack/clustering/v1/profiletypes/doc.go | 2 +- openstack/common/extensions/doc.go | 8 ++++---- openstack/networking/v2/extensions/external/doc.go | 2 +- .../networking/v2/extensions/portsecurity/doc.go | 12 ++++++------ .../networking/v2/extensions/vlantransparent/doc.go | 8 ++++---- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/openstack/clustering/v1/actions/doc.go b/openstack/clustering/v1/actions/doc.go index 2312a512f3..8e1f0619b1 100644 --- a/openstack/clustering/v1/actions/doc.go +++ b/openstack/clustering/v1/actions/doc.go @@ -15,7 +15,7 @@ Example to List Actions } for _, actionInfo := range actionInfos { - fmt.Println("%+v\n", actionInfo) + fmt.Printf("%+v\n", actionInfo) } return true, nil }) diff --git a/openstack/clustering/v1/events/doc.go b/openstack/clustering/v1/events/doc.go index 05db559001..eb58d68bc6 100644 --- a/openstack/clustering/v1/events/doc.go +++ b/openstack/clustering/v1/events/doc.go @@ -15,7 +15,7 @@ Example to List Events } for _, eventInfo := range eventInfos { - fmt.Println("%+v\n", eventInfo) + fmt.Printf("%+v\n", eventInfo) } return true, nil }) diff --git a/openstack/clustering/v1/profiletypes/doc.go b/openstack/clustering/v1/profiletypes/doc.go index 39877ace88..64f1fc99bc 100644 --- a/openstack/clustering/v1/profiletypes/doc.go +++ b/openstack/clustering/v1/profiletypes/doc.go @@ -11,7 +11,7 @@ Example to List ProfileType } for _, profileType := range profileTypes { - fmt.Println("%+v\n", profileType) + fmt.Printf("%+v\n", profileType) } return true, nil }) diff --git a/openstack/common/extensions/doc.go b/openstack/common/extensions/doc.go index 5510f26539..b3d3436698 100644 --- a/openstack/common/extensions/doc.go +++ b/openstack/common/extensions/doc.go @@ -26,11 +26,11 @@ Example of Retrieving Compute Extensions Region: os.Getenv("OS_REGION_NAME"), }) - allPages, err := extensions.List(computeClient).Allpages() + allPages, err := extensions.List(computeClient).AllPages() allExtensions, err := extensions.ExtractExtensions(allPages) for _, extension := range allExtensions{ - fmt.Println("%+v\n", extension) + fmt.Printf("%+v\n", extension) } @@ -42,11 +42,11 @@ Example of Retrieving Network Extensions Region: os.Getenv("OS_REGION_NAME"), }) - allPages, err := extensions.List(networkClient).Allpages() + allPages, err := extensions.List(networkClient).AllPages() allExtensions, err := extensions.ExtractExtensions(allPages) for _, extension := range allExtensions{ - fmt.Println("%+v\n", extension) + fmt.Printf("%+v\n", extension) } */ package extensions diff --git a/openstack/networking/v2/extensions/external/doc.go b/openstack/networking/v2/extensions/external/doc.go index eda010cb0c..bec2c0f5f9 100644 --- a/openstack/networking/v2/extensions/external/doc.go +++ b/openstack/networking/v2/extensions/external/doc.go @@ -29,7 +29,7 @@ Example to List Networks with External Information } for _, network := range allNetworks { - fmt.Println("%+v\n", network) + fmt.Printf("%+v\n", network) } Example to Create a Network with External Information diff --git a/openstack/networking/v2/extensions/portsecurity/doc.go b/openstack/networking/v2/extensions/portsecurity/doc.go index 2b9a391681..f87820e0e2 100644 --- a/openstack/networking/v2/extensions/portsecurity/doc.go +++ b/openstack/networking/v2/extensions/portsecurity/doc.go @@ -26,7 +26,7 @@ Example to List Networks with Port Security Information } for _, network := range allNetworks { - fmt.Println("%+v\n", network) + fmt.Printf("%+v\n", network) } Example to Create a Network without Port Security @@ -51,7 +51,7 @@ Example to Create a Network without Port Security panic(err) } - fmt.Println("%+v\n", networkWithPortSecurityExt) + fmt.Printf("%+v\n", networkWithPortSecurityExt) Example to Disable Port Security on an Existing Network @@ -73,7 +73,7 @@ Example to Disable Port Security on an Existing Network panic(err) } - fmt.Println("%+v\n", networkWithPortSecurityExt) + fmt.Printf("%+v\n", networkWithPortSecurityExt) Example to Get a Port with Port Security Information @@ -89,7 +89,7 @@ Example to Get a Port with Port Security Information panic(err) } - fmt.Println("%+v\n", portWithPortSecurityExtensions) + fmt.Printf("%+v\n", portWithPortSecurityExtensions) Example to Create a Port Without Port Security @@ -117,7 +117,7 @@ Example to Create a Port Without Port Security panic(err) } - fmt.Println("%+v\n", portWithPortSecurityExtensions) + fmt.Printf("%+v\n", portWithPortSecurityExtensions) Example to Disable Port Security on an Existing Port @@ -140,6 +140,6 @@ Example to Disable Port Security on an Existing Port panic(err) } - fmt.Println("%+v\n", portWithPortSecurityExtensions) + fmt.Printf("%+v\n", portWithPortSecurityExtensions) */ package portsecurity diff --git a/openstack/networking/v2/extensions/vlantransparent/doc.go b/openstack/networking/v2/extensions/vlantransparent/doc.go index a309a9667b..ae3216f412 100644 --- a/openstack/networking/v2/extensions/vlantransparent/doc.go +++ b/openstack/networking/v2/extensions/vlantransparent/doc.go @@ -29,7 +29,7 @@ Example of Listing Networks with the vlan-transparent extension } for _, network := range allNetworks { - fmt.Println("%+v\n", network) + fmt.Printf("%+v\n", network) } Example of Getting a Network with the vlan-transparent extension @@ -44,7 +44,7 @@ Example of Getting a Network with the vlan-transparent extension panic(err) } - fmt.Println("%+v\n", network) + fmt.Printf("%+v\n", network) Example of Creating Network with the vlan-transparent extension @@ -68,7 +68,7 @@ Example of Creating Network with the vlan-transparent extension panic(err) } - fmt.Println("%+v\n", network) + fmt.Printf("%+v\n", network) Example of Updating Network with the vlan-transparent extension @@ -92,6 +92,6 @@ Example of Updating Network with the vlan-transparent extension panic(err) } - fmt.Println("%+v\n", network) + fmt.Printf("%+v\n", network) */ package vlantransparent From f9603bb70e90047ab38b8829357f9311274a1122 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 27 Oct 2021 15:52:52 +0200 Subject: [PATCH 1320/2296] README: Remove reference to godep For the better or for the worse, the Go ecosystem has now migrated to using modules. --- README.md | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 95539563ac..b429f09051 100644 --- a/README.md +++ b/README.md @@ -11,30 +11,18 @@ Gophercloud is an OpenStack Go SDK. ## How to install -Before installing, you need to ensure that your [GOPATH environment variable](https://golang.org/doc/code.html#GOPATH) -is pointing to an appropriate directory where you want to install Gophercloud: +Reference a Gophercloud package in your code: -```bash -mkdir $HOME/go -export GOPATH=$HOME/go +```Go +import "github.com/gophercloud/gophercloud" ``` -To protect yourself against changes in your dependencies, we highly recommend choosing a -[dependency management solution](https://github.com/golang/go/wiki/PackageManagementTools) for -your projects, such as [godep](https://github.com/tools/godep). Once this is set up, you can install -Gophercloud as a dependency like so: +Then update your `go.mod`: -```bash -go get github.com/gophercloud/gophercloud - -# Edit your code to import relevant packages from "github.com/gophercloud/gophercloud" - -godep save ./... +```shell +go mod tidy ``` -This will install all the source files you need into a `Godeps/_workspace` directory, which is -referenceable from your own source files when you use the `godep go` command. - ## Getting started ### Credentials From 13e56d06cc72b97b41bcd7d86ff98caa314beed6 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 29 Oct 2021 11:20:15 +0200 Subject: [PATCH 1321/2296] go.mod: Remove unused packages This is the result of running `go mod tidy`. --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index c91f7ee224..82f69051a1 100644 --- a/go.sum +++ b/go.sum @@ -3,11 +3,9 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHR 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 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 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/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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= From 63d3ac0a7381c6e1ea086a55f8b05491db1cb258 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 3 Nov 2021 15:26:19 -0400 Subject: [PATCH 1322/2296] Create dependabot.yaml Get our dependencies automatically updates by Dependabot. It's free and easy to setup, let's just use it. --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..d921d0ffdb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 From 082da4177ef66cff870a929d2f599de2b3a11a1e Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 4 Nov 2021 17:20:45 +0100 Subject: [PATCH 1323/2296] core: keep keystone reauth error on failed reauth --- errors.go | 3 ++- provider_client.go | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/errors.go b/errors.go index 1f504ea834..79271d3bec 100644 --- a/errors.go +++ b/errors.go @@ -277,10 +277,11 @@ func (e ErrTimeOut) Error() string { type ErrUnableToReauthenticate struct { BaseError ErrOriginal error + ErrReauth error } func (e ErrUnableToReauthenticate) Error() string { - e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s", e.ErrOriginal) + e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s: %s", e.ErrOriginal, e.ErrReauth) return e.choseErrString() } diff --git a/provider_client.go b/provider_client.go index faa421fdd4..207ab88af8 100644 --- a/provider_client.go +++ b/provider_client.go @@ -475,6 +475,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if err != nil { e := &ErrUnableToReauthenticate{} e.ErrOriginal = respErr + e.ErrReauth = err return nil, e } if options.RawBody != nil { From 102e0d98787f19ccc5ea035eda0b32e97f06fd7d Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 5 Nov 2021 09:12:19 +0100 Subject: [PATCH 1324/2296] octavia: add new poolmonitor types --- openstack/loadbalancer/v2/monitors/requests.go | 11 +++++++---- openstack/loadbalancer/v2/monitors/results.go | 5 ++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index cdfc810c9b..bef74197d3 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -71,10 +71,13 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Constants that represent approved monitoring types. const ( - TypePING = "PING" - TypeTCP = "TCP" - TypeHTTP = "HTTP" - TypeHTTPS = "HTTPS" + TypePING = "PING" + TypeTCP = "TCP" + TypeHTTP = "HTTP" + TypeHTTPS = "HTTPS" + TypeTLSHELLO = "TLS-HELLO" + TypeUDPConnect = "UDP-CONNECT" + TypeSCTP = "SCTP" ) var ( diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index dec9395192..27d61fd466 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -18,6 +18,9 @@ type PoolID struct { // TCP: used to connect to the members using TCP. // HTTP: used to send an HTTP request to the member. // HTTPS: used to send a secure HTTP request to the member. +// TLS-HELLO: used to send TLS-HELLO request to the member. +// UDP-CONNECT: used to send UDP-CONNECT request to the member. +// SCTP: used to send SCTP request to the member. // // When a pool has several monitors associated with it, each member of the pool // is monitored by all these monitors. If any monitor declares the member as @@ -35,7 +38,7 @@ type Monitor struct { ProjectID string `json:"project_id"` // The type of probe sent by the load balancer to verify the member state, - // which is PING, TCP, HTTP, or HTTPS. + // which is PING, TCP, HTTP, HTTPS, TLS-HELLO, UDP-CONNECT or SCTP. Type string `json:"type"` // The time, in seconds, between sending probes to members. From 7d7a213f06dc105ff79eb04765114da4e45b5a7a Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 1 Nov 2021 15:35:32 -0400 Subject: [PATCH 1325/2296] Move acceptance tests to a generic script This patch will avoid the duplication of gophercloud-acceptance script by adding job variants, where we set variables of what we want to deploy and it'll use the same workflow/scripts. This removes the specific playbook for running Ironic tests and creates these variables: * `acceptance_tests`: List of acceptance tests (which can be discovered via: ``` find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq -c | awk '{print $2}' ``` * `devstack_services`: List of devstack services to activate. * `devstack_override_enabled_services`: List of services to activate in Devstack, specific to the framework itself. * `devstack_projects`: List of projects that will have to be cloned by devstack to be installed. This moves both gophercloud-acceptance-test and gophercloud-acceptance-test-ironic to use this new layout; providing the same configuration as before. This will reduce maintenance cost and simplify when we want to add other variants (will come later). --- .zuul.yaml | 52 +++++++++- .../run.yaml | 27 ------ .../gophercloud-acceptance-test/run.yaml | 16 ++-- script/acceptancetest | 94 ++----------------- script/acceptancetest-ironic | 29 ------ 5 files changed, 65 insertions(+), 153 deletions(-) delete mode 100644 .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml delete mode 100755 script/acceptancetest-ironic diff --git a/.zuul.yaml b/.zuul.yaml index 8cba23cb0b..c4398ea40c 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -14,14 +14,64 @@ run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml timeout: 18000 # 5 hours nodeset: ubuntu-focal + 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: name: gophercloud-acceptance-test-ironic parent: golang-test description: | Run gophercloud ironic acceptance test on master branch - run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml + run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml nodeset: ubuntu-focal + 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-ussuri diff --git a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml deleted file mode 100644 index 901c29fc95..0000000000 --- a/.zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml +++ /dev/null @@ -1,27 +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: - - 'ironic' - - role: install-devstack - environment: - 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' - PROJECTS: 'openstack/ironic-python-agent-builder openstack/ironic' - tasks: - - name: Run ironic acceptance tests with gophercloud - shell: - cmd: | - set -e - set -o pipefail - set -x - echo $(export |grep OS_BRANCH) - - go get ./... || true - ./script/acceptancetest-ironic -v 2>&1 | tee $TEST_RESULTS_TXT - executable: /bin/bash - chdir: '{{ zuul.project.src_dir }}' - environment: '{{ global_env }}' diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index 3626dba47e..e4cf4686eb 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -5,22 +5,20 @@ go_version: '1.15' - clone-devstack-gate-to-workspace - role: create-devstack-local-conf - enable_services: - - 'manila' - - 'designate' - - 'zun' - - 'octavia' - - 'neutron-ext' - - install-devstack + enable_services: "{{ devstack_services | default(omit) }}" + - role: install-devstack + environment: + OVERRIDE_ENABLED_SERVICES: "{{ devstack_override_enabled_services | default('') }}" + PROJECTS: "{{ devstack_projects | default('') }}" tasks: - - name: Run acceptance tests with gophercloud + - name: Run ironic acceptance tests with gophercloud shell: cmd: | set -e set -o pipefail set -x echo $(export |grep OS_BRANCH) - export OS_DEBUG=1 + export ACCEPTANCE_TESTS="{{ acceptance_tests|default('') }}" go get ./... || true ./script/acceptancetest -v 2>&1 | tee $TEST_RESULTS_TXT executable: /bin/bash diff --git a/script/acceptancetest b/script/acceptancetest index 6ad683bef3..e782ff20bc 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -7,97 +7,17 @@ source `dirname $0`/stackenv timeout="60m" failed= -# Run the acceptance tests. -# Check the error code after each suite, but do not exit early if a suite failed. -# list generated by: -# find acceptance/openstack -name '*_test.go' -exec dirname {} \; | sort -n | uniq -c | awk '{print $2}' -ACCEPTANCE_TESTS=( -acceptance/openstack - -# IronicEndpoint is required -# acceptance/openstack/baremetal/noauth -# No suitable endpoint could be found in the service catalog -# acceptance/openstack/baremetal/v1 - -acceptance/openstack/blockstorage -acceptance/openstack/blockstorage/extensions - -# snapshots_test.go:16: Unable to create a blockstorage client: CinderEndpoint is required -# acceptance/openstack/blockstorage/noauth - -# snapshots_test.go:21: Unable to retrieve snapshots: Resource not found -# acceptance/openstack/blockstorage/v1 -# acceptance/openstack/blockstorage/v2 - -acceptance/openstack/blockstorage/v3 - -# No suitable endpoint could be found in the service catalog. -# acceptance/openstack/clustering/v1 - -acceptance/openstack/compute/v2 - -# No suitable endpoint could be found in the service catalog. -# acceptance/openstack/containerinfra/v1 - -acceptance/openstack/container/v1 - -# Unable to create a DB client: No suitable endpoint could be found in the service catalog. -# acceptance/openstack/db/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 - -# No suitable endpoint could be found in the service catalog. -# acceptance/openstack/messaging/v2 - -acceptance/openstack/networking/v2 -acceptance/openstack/networking/v2/extensions -acceptance/openstack/networking/v2/extensions/agents -acceptance/openstack/networking/v2/extensions/dns - -# Resource not found -# acceptance/openstack/networking/v2/extensions/fwaas -# acceptance/openstack/networking/v2/extensions/fwaas_v2 -acceptance/openstack/networking/v2/extensions/layer3 - -# Unable to create: Resource not found -# acceptance/openstack/networking/v2/extensions/lbaas - -# Resource not found -# acceptance/openstack/networking/v2/extensions/lbaas_v2 - -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 - -# Unable to create: Resource not found -# acceptance/openstack/networking/v2/extensions/vpnaas - -acceptance/openstack/objectstorage/v1 -acceptance/openstack/orchestration/v1 -acceptance/openstack/placement/v1 -acceptance/openstack/sharedfilesystems/v2 -acceptance/openstack/sharedfilesystems/v2/messages +if [[ -z $ACCEPTANCE_TESTS ]]; then + echo "No acceptance tests to run" + exit 0 +fi -# No suitable endpoint could be found in the service catalog -# acceptance/openstack/workflow/v2 -) +TESTS=($(python <<< "print(' '.join($ACCEPTANCE_TESTS))")) -for acceptance_test in ${ACCEPTANCE_TESTS[@]}; do +for acceptance_test in "${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 failed=1 fi diff --git a/script/acceptancetest-ironic b/script/acceptancetest-ironic deleted file mode 100755 index aa1ce2bd33..0000000000 --- a/script/acceptancetest-ironic +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# -set -x - -source `dirname $0`/stackenv - -timeout="60m" -failed= - -# Run the Ironic acceptance tests. -# Check the error code after each suite, but do not exit early if a suite failed. - -ACCEPTANCE_TESTS=( -acceptance/openstack/baremetal/v1 -) - -for acceptance_test in ${ACCEPTANCE_TESTS[@]}; do - go test -v -timeout $timeout -tags "fixtures acceptance" ./${acceptance_test} - if [[ $? != 0 ]]; then - failed=1 - fi -done - -# If any of the test suites failed, exit 1 -if [[ -n $failed ]]; then - exit 1 -fi - -exit 0 From e903c7cacb2f62fe129147f3b53c9736f347c235 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 1 Nov 2021 16:38:24 -0400 Subject: [PATCH 1326/2296] zuul: split the jobs to spread the load * `gophercloud-acceptance-test` becomes `gophercloud-acceptance-test-compute` which will test Nova, Zun, Keystone, Barbican, Heat and Placement. * `gophercloud-acceptance-test-networking` which will test Neutron, Designate and Octavia. * `gophercloud-acceptance-test-storage` which will test Cinder, Glance, Swift and Manila. This will hopefully helps to reduce the timeouts when running everyone within a single instance and spread the load across 3 jobs instead of 1. --- .zuul.yaml | 56 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index c4398ea40c..35b7e4eccf 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -7,10 +7,10 @@ nodeset: ubuntu-xenial-ut - job: - name: gophercloud-acceptance-test + name: gophercloud-acceptance-test-compute parent: golang-test description: | - Run gophercloud acceptance test on master branch + Run gophercloud compute acceptance test on master branch run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml timeout: 18000 # 5 hours nodeset: ubuntu-focal @@ -19,16 +19,29 @@ # 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/orchestration/v1 + - acceptance/openstack/placement/v1 + devstack_services: + - zun + +- job: + name: gophercloud-acceptance-test-networking + parent: golang-test + description: | + Run gophercloud networking acceptance test on master branch + run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml + timeout: 18000 # 5 hours + nodeset: ubuntu-focal + 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/dns/v2 - acceptance/openstack/loadbalancer/v2 - acceptance/openstack/networking/v2 - acceptance/openstack/networking/v2/extensions @@ -46,17 +59,30 @@ - acceptance/openstack/networking/v2/extensions/subnetpools - acceptance/openstack/networking/v2/extensions/trunks - acceptance/openstack/networking/v2/extensions/vlantransparent + devstack_services: + - designate + - neutron-ext + - octavia + +- job: + name: gophercloud-acceptance-test-storage + parent: golang-test + description: | + Run gophercloud storage acceptance test on master branch + run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml + timeout: 18000 # 5 hours + nodeset: ubuntu-focal + vars: + acceptance_tests: + - acceptance/openstack/blockstorage + - acceptance/openstack/blockstorage/extensions + - acceptance/openstack/blockstorage/v3 + - acceptance/openstack/imageservice/v2 - 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: name: gophercloud-acceptance-test-ironic @@ -158,7 +184,9 @@ check: jobs: - gophercloud-unittest - - gophercloud-acceptance-test + - gophercloud-acceptance-test-compute + - gophercloud-acceptance-test-networking + - gophercloud-acceptance-test-storage - gophercloud-acceptance-test-ironic recheck-newton: jobs: From 4c303f733867f5e89c28f312bc8667f8780aa5a9 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 1 Nov 2021 16:56:39 -0400 Subject: [PATCH 1327/2296] zuul: optimize how jobs are triggered * Run `gophercloud-acceptance-test-compute` all the times, this is our "reference" job for OpenStack; testing the minimal set of features. Also from now, we won't run this job if documentation is the only change in a PR. * Run the networking & storage & ironic variants only when it's needed, to avoid wasting CI resources. --- .zuul.yaml | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 35b7e4eccf..ddbbac3f23 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -7,13 +7,24 @@ nodeset: ubuntu-xenial-ut - job: - name: gophercloud-acceptance-test-compute + name: gophercloud-acceptance-test-base parent: golang-test - description: | - Run gophercloud compute acceptance test on master branch 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}' @@ -31,12 +42,13 @@ - job: name: gophercloud-acceptance-test-networking - parent: golang-test + parent: gophercloud-acceptance-test-base description: | Run gophercloud networking acceptance test on master branch - run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml - timeout: 18000 # 5 hours - nodeset: ubuntu-focal + files: + - ^.*dns.*$ + - ^.*loadbalancer.*$ + - ^.*networking.*$ 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}' @@ -66,12 +78,14 @@ - job: name: gophercloud-acceptance-test-storage - parent: golang-test + parent: gophercloud-acceptance-test-base description: | Run gophercloud storage acceptance test on master branch - run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml - timeout: 18000 # 5 hours - nodeset: ubuntu-focal + files: + - ^.*blockstorage.*$ + - ^.*imageservice.*$ + - ^.*objectstorage.*$ + - ^.*sharedfilesystems.*$ vars: acceptance_tests: - acceptance/openstack/blockstorage @@ -86,11 +100,11 @@ - job: name: gophercloud-acceptance-test-ironic - parent: golang-test + parent: gophercloud-acceptance-test-base description: | Run gophercloud ironic acceptance test on master branch - run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml - nodeset: ubuntu-focal + files: + - ^.*baremetal.*$ vars: devstack_services: - ironic From f963a58a38ae7309c5e3316a897827d2ea513273 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 1 Nov 2021 20:24:52 -0400 Subject: [PATCH 1328/2296] zuul: add variants for stable jobs Add the new variants for stable OpenStack platforms. Note: we had to keep the original job, as "legacy", so we maintain the same testing matrix. --- .zuul.yaml | 324 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 304 insertions(+), 20 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index ddbbac3f23..327def1d1d 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -114,26 +114,294 @@ - acceptance/openstack/baremetal/v1 - job: - name: gophercloud-acceptance-test-ussuri - parent: gophercloud-acceptance-test + name: gophercloud-acceptance-test-compute-ussuri + parent: gophercloud-acceptance-test-compute description: | - Run gophercloud acceptance test on ussuri branch + Run gophercloud compute acceptance test on ussuri branch vars: global_env: OS_BRANCH: stable/ussuri - job: - name: gophercloud-acceptance-test-train - parent: gophercloud-acceptance-test + name: gophercloud-acceptance-test-networking-ussuri + parent: gophercloud-acceptance-test-networking description: | - Run gophercloud acceptance test on train branch + 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 + 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 + description: | + Run gophercloud compute acceptance test on train branch vars: global_env: OS_BRANCH: stable/train +- job: + name: gophercloud-acceptance-test-networking-train + parent: gophercloud-acceptance-test-networking + 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 + 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 + description: | + Run gophercloud compute acceptance test on stein branch + vars: + global_env: + OS_BRANCH: stable/stein + +- job: + name: gophercloud-acceptance-test-networking-stein + parent: gophercloud-acceptance-test-networking + 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 + 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 + description: | + Run gophercloud compute acceptance test on rocky branch + vars: + global_env: + OS_BRANCH: stable/rocky + +- job: + name: gophercloud-acceptance-test-networking-rocky + parent: gophercloud-acceptance-test-networking + 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 + 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 + description: | + Run gophercloud compute acceptance test on queens branch + nodeset: ubuntu-xenial + vars: + 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 + 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: + 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-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 + 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 + description: | + Run gophercloud compute acceptance test on ocata branch + nodeset: ubuntu-xenial + vars: + 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 + 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: + 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 + +- 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 + 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. +# TODO(emilien) update the childs to use the new variants. +- 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: + # 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: name: gophercloud-acceptance-test-stein - parent: gophercloud-acceptance-test + parent: gophercloud-acceptance-test-legacy description: | Run gophercloud acceptance test on stein branch vars: @@ -142,7 +410,7 @@ - job: name: gophercloud-acceptance-test-rocky - parent: gophercloud-acceptance-test + parent: gophercloud-acceptance-test-legacy description: | Run gophercloud acceptance test on rocky branch vars: @@ -151,7 +419,7 @@ - job: name: gophercloud-acceptance-test-queens - parent: gophercloud-acceptance-test + parent: gophercloud-acceptance-test-legacy description: | Run gophercloud acceptance test on queens branch nodeset: ubuntu-xenial @@ -163,7 +431,7 @@ # not building correctly. This might be a temporary issue. - job: name: gophercloud-acceptance-test-pike - parent: gophercloud-acceptance-test + parent: gophercloud-acceptance-test-legacy description: | Run gophercloud acceptance test on pike branch nodeset: ubuntu-xenial @@ -173,7 +441,7 @@ - job: name: gophercloud-acceptance-test-ocata - parent: gophercloud-acceptance-test + parent: gophercloud-acceptance-test-legacy description: | Run gophercloud acceptance test on ocata branch nodeset: ubuntu-xenial @@ -185,7 +453,7 @@ # not building correctly. This might be a temporary issue. - job: name: gophercloud-acceptance-test-newton - parent: gophercloud-acceptance-test + parent: gophercloud-acceptance-test-legacy description: | Run gophercloud acceptance test on newton branch nodeset: ubuntu-xenial @@ -204,25 +472,41 @@ - gophercloud-acceptance-test-ironic recheck-newton: jobs: - - gophercloud-acceptance-test-newton + - gophercloud-acceptance-test-compute-newton + - gophercloud-acceptance-test-networking-newton + - gophercloud-acceptance-test-storage-newton recheck-ocata: jobs: - - gophercloud-acceptance-test-ocata + - gophercloud-acceptance-test-compute-ocata + - gophercloud-acceptance-test-networking-ocata + - gophercloud-acceptance-test-storage-ocata recheck-pike: jobs: - - gophercloud-acceptance-test-pike + - gophercloud-acceptance-test-compute-pike + - gophercloud-acceptance-test-networking-pike + - gophercloud-acceptance-test-storage-pike recheck-queens: jobs: - - gophercloud-acceptance-test-queens + - gophercloud-acceptance-test-compute-queens + - gophercloud-acceptance-test-networking-queens + - gophercloud-acceptance-test-storage-queens recheck-rocky: jobs: - - gophercloud-acceptance-test-rocky + - gophercloud-acceptance-test-compute-rocky + - gophercloud-acceptance-test-networking-rocky + - gophercloud-acceptance-test-storage-rocky recheck-stein: jobs: - - gophercloud-acceptance-test-stein + - gophercloud-acceptance-test-compute-stein + - gophercloud-acceptance-test-networking-stein + - gophercloud-acceptance-test-storage-stein recheck-train: jobs: - - gophercloud-acceptance-test-train + - gophercloud-acceptance-test-compute-train + - gophercloud-acceptance-test-networking-train + - gophercloud-acceptance-test-storage-train recheck-ussuri: jobs: - - gophercloud-acceptance-test-ussuri + - gophercloud-acceptance-test-compute-ussuri + - gophercloud-acceptance-test-networking-ussuri + - gophercloud-acceptance-test-storage-ussuri From 1e9074f691f872fb3a29881f32f6bddc1eb95063 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 1 Nov 2021 21:11:37 -0400 Subject: [PATCH 1329/2296] zuul/stable: fix Ubuntu versions Fix the Ubuntu versions for the stable OpenStack jobs. Note: for each version, we had a look at: https://github.com/openstack/devstack/blob/stable//stack.sh --- .zuul.yaml | 18 ++++++++++++++++++ .../gophercloud-acceptance-test/run.yaml | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index 327def1d1d..24167fab33 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -38,6 +38,8 @@ - acceptance/openstack/orchestration/v1 - acceptance/openstack/placement/v1 devstack_services: + - barbican + - heat - zun - job: @@ -75,6 +77,8 @@ - designate - neutron-ext - octavia + zuul_copy_output: + '/var/log/dib-build': logs - job: name: gophercloud-acceptance-test-storage @@ -116,6 +120,7 @@ - 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: @@ -125,6 +130,7 @@ - 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: @@ -134,6 +140,7 @@ - job: name: gophercloud-acceptance-test-storage-ussuri parent: gophercloud-acceptance-test-storage + nodeset: ubuntu-bionic description: | Run gophercloud storage test on ussuri branch vars: @@ -143,6 +150,7 @@ - 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: @@ -152,6 +160,7 @@ - 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: @@ -161,6 +170,7 @@ - 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: @@ -170,6 +180,7 @@ - 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: @@ -179,6 +190,7 @@ - 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: @@ -188,6 +200,7 @@ - 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: @@ -197,6 +210,7 @@ - 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: @@ -206,6 +220,7 @@ - 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: @@ -215,6 +230,7 @@ - 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: @@ -402,6 +418,7 @@ - job: name: gophercloud-acceptance-test-stein parent: gophercloud-acceptance-test-legacy + nodeset: ubuntu-xenial description: | Run gophercloud acceptance test on stein branch vars: @@ -411,6 +428,7 @@ - job: name: gophercloud-acceptance-test-rocky parent: gophercloud-acceptance-test-legacy + nodeset: ubuntu-xenial description: | Run gophercloud acceptance test on rocky branch vars: diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index e4cf4686eb..e980085e8c 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -11,7 +11,7 @@ OVERRIDE_ENABLED_SERVICES: "{{ devstack_override_enabled_services | default('') }}" PROJECTS: "{{ devstack_projects | default('') }}" tasks: - - name: Run ironic acceptance tests with gophercloud + - name: Run acceptance tests with gophercloud shell: cmd: | set -e From bd07bd5cebe901c37ebdb01424a73822d5888eb6 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 2 Nov 2021 12:12:49 -0400 Subject: [PATCH 1330/2296] Pre-fetch the Octavia Amphora image This will avoid issues that we currently have with DIB, until we figure it out correctly. To figure it out correctly, we need to rework the roles that we run for Devstack job to properly collect the logs. This will be done in the future but not in this first effort. For now, we'll download the 500MB+ image from RDO servers, which should be faster and more reliable than building the image locally. --- .zuul.yaml | 5 +++-- .zuul/playbooks/gophercloud-acceptance-test/run.yaml | 2 ++ roles/prefetch-amphora/README.md | 6 ++++++ roles/prefetch-amphora/defaults/main.yaml | 3 +++ roles/prefetch-amphora/tasks/main.yaml | 11 +++++++++++ 5 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 roles/prefetch-amphora/README.md create mode 100644 roles/prefetch-amphora/defaults/main.yaml create mode 100644 roles/prefetch-amphora/tasks/main.yaml diff --git a/.zuul.yaml b/.zuul.yaml index 24167fab33..1de1c406f2 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -52,6 +52,9 @@ - ^.*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: @@ -77,8 +80,6 @@ - designate - neutron-ext - octavia - zuul_copy_output: - '/var/log/dib-build': logs - job: name: gophercloud-acceptance-test-storage diff --git a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml index e980085e8c..844e2a8c68 100644 --- a/.zuul/playbooks/gophercloud-acceptance-test/run.yaml +++ b/.zuul/playbooks/gophercloud-acceptance-test/run.yaml @@ -6,6 +6,8 @@ - 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('') }}" diff --git a/roles/prefetch-amphora/README.md b/roles/prefetch-amphora/README.md new file mode 100644 index 0000000000..956c3f3bac --- /dev/null +++ b/roles/prefetch-amphora/README.md @@ -0,0 +1,6 @@ +Pre-fetch the Amphora image from an URL into a specific directory. + +## Roles variables + +* `amphora_url`: URL of the Amphora image to download +* `amphora_dir`: Directory where the image will be downloaded diff --git a/roles/prefetch-amphora/defaults/main.yaml b/roles/prefetch-amphora/defaults/main.yaml new file mode 100644 index 0000000000..6fc53b2ec1 --- /dev/null +++ b/roles/prefetch-amphora/defaults/main.yaml @@ -0,0 +1,3 @@ +amphora_url: https://images.rdoproject.org/octavia/master/amphora-x64-haproxy-centos.qcow2 +amphora_dir: /opt/octavia-amphora +amphora_file_name: amphora-x64-haproxy.qcow2 diff --git a/roles/prefetch-amphora/tasks/main.yaml b/roles/prefetch-amphora/tasks/main.yaml new file mode 100644 index 0000000000..d5694a9fa9 --- /dev/null +++ b/roles/prefetch-amphora/tasks/main.yaml @@ -0,0 +1,11 @@ +- name: Create a directory for the Octavia Amphora image + file: + path: "{{ amphora_dir }}" + state: directory + mode: 0755 + +- name: Download the image into the directory + get_url: + url: "{{ amphora_url }}" + dest: "{{ amphora_dir }}/{{ amphora_file_name }}" + mode: 0755 From 4672a29e38592ba025452a5049bad1eeb5691e87 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 5 Nov 2021 08:53:41 -0400 Subject: [PATCH 1331/2296] Skip some acceptance tests which fail in OpenLab We'll re-enable them later, but for now we need to move on and bring the acceptance jobs in a good shape. The failing tests are related to heat stacks, neutron trunk & loadbalancers. --- acceptance/openstack/loadbalancer/v2/loadbalancers_test.go | 5 +++++ .../openstack/networking/v2/extensions/trunks/trunks_test.go | 4 ++++ 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 | 2 ++ 6 files changed, 14 insertions(+) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 97e798253c..d1808f6539 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -45,6 +45,7 @@ func TestLoadbalancersListByTags(t *testing.T) { 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) @@ -115,6 +116,7 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { 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) @@ -171,6 +173,7 @@ func TestLoadbalancersCRUD(t *testing.T) { 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) @@ -477,6 +480,7 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { 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) @@ -598,6 +602,7 @@ func TestLoadbalancersFullyPopulatedCRUD(t *testing.T) { 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/networking/v2/extensions/trunks/trunks_test.go b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index df969b6d8c..672508cc50 100644 --- a/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -16,6 +16,7 @@ import ( func TestTrunkCRUD(t *testing.T) { clients.SkipRelease(t, "stable/mitaka") + t.Skip("Currently failing in OpenLab") client, err := clients.NewNetworkV2Client() if err != nil { @@ -102,6 +103,7 @@ 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 { @@ -125,6 +127,7 @@ 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 { @@ -211,6 +214,7 @@ 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 { diff --git a/acceptance/openstack/orchestration/v1/stackevents_test.go b/acceptance/openstack/orchestration/v1/stackevents_test.go index 21d0c7d26d..577160e03e 100644 --- a/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -12,6 +12,7 @@ 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 5d1bf676e9..91ff630ec9 100644 --- a/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -13,6 +13,7 @@ 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 ac21fed28d..559b365d4c 100644 --- a/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/acceptance/openstack/orchestration/v1/stacks_test.go @@ -13,6 +13,7 @@ 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 42797ef8b4..98314825a2 100644 --- a/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -13,6 +13,7 @@ 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) @@ -43,6 +44,7 @@ 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 1ac7d1cfff7e24a87d217aed21c5f756399dcf2f Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 8 Nov 2021 08:42:04 -0500 Subject: [PATCH 1332/2296] Remove in-tree role for prefetch amphora The role is now in openlab zuul repo. --- roles/prefetch-amphora/README.md | 6 ------ roles/prefetch-amphora/defaults/main.yaml | 3 --- roles/prefetch-amphora/tasks/main.yaml | 11 ----------- 3 files changed, 20 deletions(-) delete mode 100644 roles/prefetch-amphora/README.md delete mode 100644 roles/prefetch-amphora/defaults/main.yaml delete mode 100644 roles/prefetch-amphora/tasks/main.yaml diff --git a/roles/prefetch-amphora/README.md b/roles/prefetch-amphora/README.md deleted file mode 100644 index 956c3f3bac..0000000000 --- a/roles/prefetch-amphora/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Pre-fetch the Amphora image from an URL into a specific directory. - -## Roles variables - -* `amphora_url`: URL of the Amphora image to download -* `amphora_dir`: Directory where the image will be downloaded diff --git a/roles/prefetch-amphora/defaults/main.yaml b/roles/prefetch-amphora/defaults/main.yaml deleted file mode 100644 index 6fc53b2ec1..0000000000 --- a/roles/prefetch-amphora/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ -amphora_url: https://images.rdoproject.org/octavia/master/amphora-x64-haproxy-centos.qcow2 -amphora_dir: /opt/octavia-amphora -amphora_file_name: amphora-x64-haproxy.qcow2 diff --git a/roles/prefetch-amphora/tasks/main.yaml b/roles/prefetch-amphora/tasks/main.yaml deleted file mode 100644 index d5694a9fa9..0000000000 --- a/roles/prefetch-amphora/tasks/main.yaml +++ /dev/null @@ -1,11 +0,0 @@ -- name: Create a directory for the Octavia Amphora image - file: - path: "{{ amphora_dir }}" - state: directory - mode: 0755 - -- name: Download the image into the directory - get_url: - url: "{{ amphora_url }}" - dest: "{{ amphora_dir }}/{{ amphora_file_name }}" - mode: 0755 From c8b070bc36e1fd7b28236c75ef58a090e4f57341 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 9 Nov 2021 19:21:36 -0500 Subject: [PATCH 1333/2296] CHANGELOG: update with recent PRs --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3034df9fce..ad4b6a17b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ IMPROVEMENTS * 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) From a8e6452c0415ee6e2d61170c5edb3ba5f543e73a Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 9 Nov 2021 21:05:23 -0500 Subject: [PATCH 1334/2296] Add security checks with CodeQL CodeQL is the analysis engine used by developers to automate security checks, and by security researchers to perform variant analysis. This patch will add a new check that will analyze the code and look for security issues. --- .github/workflows/codeql-analysis.yml | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000..5ab08fab9e --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,39 @@ +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '18 8 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 2aa241fafd017c4685009f4b605cfb680a01384c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 10 Nov 2021 10:34:45 +0100 Subject: [PATCH 1335/2296] Use keyword to automatically close related issue There are multiple keywords to close related issue when merging a pull request [1], and `For` wasn't one of them. [1] https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword --- .github/PULL_REQUEST_TEMPLATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index ea3abc8f88..6978cc2c53 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -4,7 +4,7 @@ Prior to starting a PR, please make sure you have read our Prior to a PR being reviewed, there needs to be a Github issue that the PR addresses. Replace the brackets and text below with that issue number. -For #[PUT ISSUE NUMBER HERE] +Fixes #[PUT ISSUE NUMBER HERE] Links to the line numbers/files in the OpenStack source code that support the code in this PR: From a84b682073cb58fd27aaad92150518e2803a4ae4 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 10 Nov 2021 14:17:48 -0500 Subject: [PATCH 1336/2296] Add coverage files into .gitignore We don't need them commited in git anyway. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dd91ed2055..d7a5e5293a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ **/*.swp .idea .vscode +testing_*.coverprofile From 9fed4e9f83f06519016ef92548f56dbc4ab69c52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Nov 2021 14:23:00 +0000 Subject: [PATCH 1337/2296] Bump gopkg.in/yaml.v2 from 2.3.0 to 2.4.0 Bumps [gopkg.in/yaml.v2](https://github.com/go-yaml/yaml) from 2.3.0 to 2.4.0. - [Release notes](https://github.com/go-yaml/yaml/releases) - [Commits](https://github.com/go-yaml/yaml/compare/v2.3.0...v2.4.0) --- updated-dependencies: - dependency-name: gopkg.in/yaml.v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8c83df2349..94cd805878 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,5 @@ go 1.13 require ( golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect - gopkg.in/yaml.v2 v2.3.0 + gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 82f69051a1..552ef90a11 100644 --- a/go.sum +++ b/go.sum @@ -12,5 +12,5 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= From a02416ed931a3a10747439c86e16d0e77877da7d Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 10 Nov 2021 13:56:55 -0500 Subject: [PATCH 1338/2296] 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 31accb60e8855f9e062ffac10fd629a7fff3ac93 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 12 Nov 2021 11:14:03 -0500 Subject: [PATCH 1339/2296] Prepare CHANGELOG for 0.23.0 release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4b6a17b9..8a798c68d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.23.0 (Unreleased) +## 0.23.0 (November 12, 2021) IMPROVEMENTS From 4063b39a6ac022604b7c8efc64391de503d984fc Mon Sep 17 00:00:00 2001 From: nikParasyr Date: Thu, 25 Nov 2021 20:38:00 +0100 Subject: [PATCH 1340/2296] 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 Date: Thu, 25 Nov 2021 21:40:53 +0100 Subject: [PATCH 1341/2296] 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 Date: Fri, 26 Nov 2021 10:36:44 +0100 Subject: [PATCH 1342/2296] 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 Date: Sat, 27 Nov 2021 19:56:32 +0100 Subject: [PATCH 1343/2296] 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 Date: Sat, 27 Nov 2021 19:58:33 +0100 Subject: [PATCH 1344/2296] 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 Date: Sat, 27 Nov 2021 20:00:23 +0100 Subject: [PATCH 1345/2296] 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 Date: Sat, 27 Nov 2021 20:21:09 +0100 Subject: [PATCH 1346/2296] 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 Date: Tue, 7 Dec 2021 13:53:36 +0100 Subject: [PATCH 1347/2296] 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 Date: Wed, 8 Dec 2021 13:57:48 +0100 Subject: [PATCH 1348/2296] 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 Date: Fri, 10 Dec 2021 15:28:16 +0100 Subject: [PATCH 1349/2296] 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 Date: Fri, 10 Dec 2021 15:45:11 +0100 Subject: [PATCH 1350/2296] 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 Date: Fri, 10 Dec 2021 15:46:24 +0100 Subject: [PATCH 1351/2296] 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 Date: Fri, 10 Dec 2021 15:46:55 +0100 Subject: [PATCH 1352/2296] 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 Date: Fri, 10 Dec 2021 15:55:08 +0100 Subject: [PATCH 1353/2296] 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 Date: Fri, 10 Dec 2021 15:07:34 +0100 Subject: [PATCH 1354/2296] 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 Date: Fri, 10 Dec 2021 16:09:43 +0100 Subject: [PATCH 1355/2296] 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 Date: Fri, 10 Dec 2021 15:16:06 -0500 Subject: [PATCH 1356/2296] 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 Date: Thu, 30 Dec 2021 15:37:18 +0000 Subject: [PATCH 1357/2296] 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 Date: Fri, 7 Jan 2022 16:34:25 +0200 Subject: [PATCH 1358/2296] 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 Date: Thu, 6 Jan 2022 13:59:07 -0500 Subject: [PATCH 1359/2296] [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 Date: Sat, 8 Jan 2022 12:08:44 +0200 Subject: [PATCH 1360/2296] 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 Date: Fri, 7 Jan 2022 21:22:53 -0500 Subject: [PATCH 1361/2296] 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 Date: Tue, 11 Jan 2022 10:30:11 +0000 Subject: [PATCH 1362/2296] 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 Date: Tue, 11 Jan 2022 17:04:09 -0500 Subject: [PATCH 1363/2296] 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 Date: Sun, 9 Jan 2022 09:25:43 -0500 Subject: [PATCH 1364/2296] [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 Date: Thu, 6 Jan 2022 14:17:06 -0500 Subject: [PATCH 1365/2296] [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 Date: Tue, 11 Jan 2022 10:44:39 -0500 Subject: [PATCH 1366/2296] [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 Date: Fri, 7 Jan 2022 20:34:33 -0500 Subject: [PATCH 1367/2296] [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?= Date: Fri, 19 Nov 2021 16:52:53 +0100 Subject: [PATCH 1368/2296] 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?= Date: Fri, 19 Nov 2021 17:56:59 +0100 Subject: [PATCH 1369/2296] 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?= Date: Fri, 19 Nov 2021 17:59:32 +0100 Subject: [PATCH 1370/2296] 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?= Date: Fri, 19 Nov 2021 18:07:26 +0100 Subject: [PATCH 1371/2296] 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 Date: Fri, 14 Jan 2022 09:44:10 -0500 Subject: [PATCH 1372/2296] 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 Date: Mon, 17 Jan 2022 16:24:25 -0500 Subject: [PATCH 1373/2296] [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 Date: Tue, 18 Jan 2022 14:48:47 +0100 Subject: [PATCH 1374/2296] 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 Date: Fri, 21 Jan 2022 10:11:50 -0500 Subject: [PATCH 1375/2296] [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?= Date: Tue, 25 Jan 2022 11:07:41 +0100 Subject: [PATCH 1376/2296] [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?= Date: Tue, 25 Jan 2022 17:41:45 +0100 Subject: [PATCH 1377/2296] 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 Date: Tue, 25 Jan 2022 12:40:01 -0500 Subject: [PATCH 1378/2296] 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 Date: Fri, 21 Jan 2022 10:20:23 -0500 Subject: [PATCH 1379/2296] [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 Date: Tue, 11 Jan 2022 16:35:46 -0500 Subject: [PATCH 1380/2296] [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?= Date: Wed, 26 Jan 2022 09:25:31 +0100 Subject: [PATCH 1381/2296] 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 Date: Fri, 28 Jan 2022 08:23:52 -0500 Subject: [PATCH 1382/2296] [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 Date: Fri, 28 Jan 2022 08:47:32 -0500 Subject: [PATCH 1383/2296] 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 Date: Mon, 10 Jan 2022 16:40:34 +0800 Subject: [PATCH 1384/2296] 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 Date: Sat, 18 Dec 2021 23:30:04 +0800 Subject: [PATCH 1385/2296] 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 Date: Tue, 8 Feb 2022 10:54:17 +0800 Subject: [PATCH 1386/2296] 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?= Date: Wed, 9 Feb 2022 12:28:58 +0100 Subject: [PATCH 1387/2296] 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?= Date: Wed, 9 Feb 2022 13:33:57 +0100 Subject: [PATCH 1388/2296] 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?= Date: Tue, 15 Feb 2022 14:59:25 +0100 Subject: [PATCH 1389/2296] 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 Date: Tue, 9 Nov 2021 21:12:21 -0500 Subject: [PATCH 1390/2296] 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 Date: Mon, 14 Feb 2022 11:27:41 -0500 Subject: [PATCH 1391/2296] 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?= Date: Tue, 15 Feb 2022 16:24:43 +0100 Subject: [PATCH 1392/2296] 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 Date: Thu, 24 Feb 2022 19:32:37 +0100 Subject: [PATCH 1393/2296] 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 Date: Fri, 25 Feb 2022 10:55:07 -0500 Subject: [PATCH 1394/2296] 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?= Date: Fri, 25 Feb 2022 22:59:53 +0100 Subject: [PATCH 1395/2296] 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?= Date: Sat, 26 Feb 2022 11:56:55 +0100 Subject: [PATCH 1396/2296] [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?= Date: Sat, 26 Feb 2022 18:34:17 +0100 Subject: [PATCH 1397/2296] 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?= Date: Tue, 15 Feb 2022 16:27:05 +0100 Subject: [PATCH 1398/2296] 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 Date: Wed, 23 Feb 2022 12:51:01 +0100 Subject: [PATCH 1399/2296] 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 Date: Wed, 23 Feb 2022 14:20:04 +0100 Subject: [PATCH 1400/2296] 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 Date: Wed, 23 Feb 2022 15:43:16 +0100 Subject: [PATCH 1401/2296] 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 Date: Wed, 23 Feb 2022 17:27:09 +0100 Subject: [PATCH 1402/2296] 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?= Date: Fri, 18 Mar 2022 15:39:11 +0100 Subject: [PATCH 1403/2296] [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?= Date: Fri, 18 Mar 2022 15:17:31 +0100 Subject: [PATCH 1404/2296] [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?= Date: Fri, 18 Mar 2022 15:19:53 +0100 Subject: [PATCH 1405/2296] 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?= Date: Fri, 18 Mar 2022 17:17:08 +0100 Subject: [PATCH 1406/2296] 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?= Date: Fri, 18 Mar 2022 17:21:00 +0100 Subject: [PATCH 1407/2296] 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?= Date: Fri, 18 Mar 2022 17:25:11 +0100 Subject: [PATCH 1408/2296] [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?= Date: Sun, 20 Mar 2022 16:23:17 +0100 Subject: [PATCH 1409/2296] 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?= Date: Sun, 20 Mar 2022 16:27:03 +0100 Subject: [PATCH 1410/2296] 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?= Date: Fri, 11 Feb 2022 14:47:45 +0100 Subject: [PATCH 1411/2296] 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 Date: Tue, 5 Apr 2022 09:42:27 -0400 Subject: [PATCH 1412/2296] 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 Date: Tue, 5 Apr 2022 09:23:34 -0400 Subject: [PATCH 1413/2296] 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?= Date: Fri, 8 Apr 2022 11:20:00 +0200 Subject: [PATCH 1414/2296] 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 Date: Sat, 23 Oct 2021 22:26:56 -0400 Subject: [PATCH 1415/2296] 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 Date: Tue, 5 Apr 2022 09:26:46 -0400 Subject: [PATCH 1416/2296] 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 Date: Wed, 20 Apr 2022 16:42:06 +0200 Subject: [PATCH 1417/2296] 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 1418/2296] 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 1419/2296] 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 Date: Tue, 3 May 2022 23:14:23 -0400 Subject: [PATCH 1420/2296] 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 1421/2296] 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] --- .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 1422/2296] 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] --- .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 1423/2296] 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] --- .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 1424/2296] 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] --- .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?= Date: Fri, 11 Feb 2022 14:47:09 +0100 Subject: [PATCH 1425/2296] 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?= Date: Thu, 24 Feb 2022 17:13:34 +0100 Subject: [PATCH 1426/2296] 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?= Date: Thu, 31 Mar 2022 16:00:23 +0200 Subject: [PATCH 1427/2296] 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 Date: Fri, 6 May 2022 21:26:54 -0400 Subject: [PATCH 1428/2296] 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?= Date: Fri, 8 Apr 2022 14:27:09 +0200 Subject: [PATCH 1429/2296] [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?= Date: Fri, 8 Apr 2022 16:20:12 +0200 Subject: [PATCH 1430/2296] 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?= Date: Thu, 30 Dec 2021 19:11:51 +0100 Subject: [PATCH 1431/2296] 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?= Date: Thu, 30 Dec 2021 19:14:05 +0100 Subject: [PATCH 1432/2296] 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?= Date: Sat, 7 May 2022 16:19:55 +0200 Subject: [PATCH 1433/2296] 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?= Date: Sun, 8 May 2022 20:14:27 +0200 Subject: [PATCH 1434/2296] 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?= Date: Sun, 8 May 2022 20:27:39 +0200 Subject: [PATCH 1435/2296] 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?= Date: Sun, 8 May 2022 20:27:54 +0200 Subject: [PATCH 1436/2296] 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 Date: Mon, 9 May 2022 17:01:24 -0400 Subject: [PATCH 1437/2296] 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?= Date: Tue, 10 May 2022 10:31:00 +0200 Subject: [PATCH 1438/2296] 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?= Date: Tue, 10 May 2022 10:31:44 +0200 Subject: [PATCH 1439/2296] 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 Date: Thu, 7 Apr 2022 17:55:27 +0200 Subject: [PATCH 1440/2296] containerinfra: add missing merge_labels in create nodegroup request Signed-off-by: Arthur Outhenin-Chalandre --- 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 Date: Fri, 8 Apr 2022 10:17:31 +0200 Subject: [PATCH 1441/2296] containerinfra: add missing labels fields in result Signed-off-by: Arthur Outhenin-Chalandre --- 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 Date: Fri, 20 May 2022 09:43:48 -0400 Subject: [PATCH 1442/2296] 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 Date: Sat, 21 May 2022 18:26:59 +0200 Subject: [PATCH 1443/2296] 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 Co-authored-by: Emilien Macchi --- 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?= Date: Sun, 22 May 2022 15:01:22 +0200 Subject: [PATCH 1444/2296] 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?= Date: Sun, 22 May 2022 22:38:20 +0200 Subject: [PATCH 1445/2296] 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 Date: Tue, 24 May 2022 10:53:12 -0400 Subject: [PATCH 1446/2296] 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 Date: Tue, 24 May 2022 13:59:27 -0400 Subject: [PATCH 1447/2296] 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?= Date: Wed, 25 May 2022 08:49:42 +0200 Subject: [PATCH 1448/2296] 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 Date: Tue, 24 May 2022 17:00:47 +0300 Subject: [PATCH 1449/2296] 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?= Date: Fri, 27 May 2022 16:56:23 +0200 Subject: [PATCH 1450/2296] 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?= Date: Fri, 27 May 2022 17:11:50 +0200 Subject: [PATCH 1451/2296] 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?= Date: Fri, 27 May 2022 17:13:11 +0200 Subject: [PATCH 1452/2296] 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?= Date: Fri, 27 May 2022 17:59:42 +0200 Subject: [PATCH 1453/2296] 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 Date: Mon, 30 May 2022 11:05:12 -0400 Subject: [PATCH 1454/2296] 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 Date: Mon, 27 Jun 2022 11:23:21 +0200 Subject: [PATCH 1455/2296] 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 1456/2296] 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: 吴典秋 --- 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?= Date: Mon, 18 Jul 2022 11:13:26 +0900 Subject: [PATCH 1457/2296] 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 Date: Thu, 14 Jul 2022 19:57:59 +0200 Subject: [PATCH 1458/2296] 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 --- .../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 Date: Tue, 2 Aug 2022 07:41:44 +0100 Subject: [PATCH 1459/2296] 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 --- 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?= Date: Mon, 22 Aug 2022 09:41:33 +0200 Subject: [PATCH 1460/2296] 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?= Date: Mon, 22 Aug 2022 13:30:46 +0200 Subject: [PATCH 1461/2296] 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 '' | base64 -D | openssl rsautl -decrypt -inkey +// +// echo '' | base64 -D | openssl rsautl -decrypt -inkey 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 "" | base64 -D | openssl rsautl -decrypt -inkey +// +// echo "" | base64 -D | openssl rsautl -decrypt -inkey 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 Date: Wed, 24 Aug 2022 13:54:02 +0100 Subject: [PATCH 1462/2296] Add CreatedAt to ports.Port Signed-off-by: Matt Vinall --- 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 Date: Thu, 25 Aug 2022 14:02:16 +0200 Subject: [PATCH 1463/2296] 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 Date: Mon, 29 Aug 2022 14:32:10 +0200 Subject: [PATCH 1464/2296] 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é --- 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 Date: Thu, 1 Sep 2022 08:14:51 +0200 Subject: [PATCH 1465/2296] 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 Date: Thu, 1 Sep 2022 09:25:03 +0200 Subject: [PATCH 1466/2296] 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 Date: Tue, 30 Aug 2022 17:22:16 +0200 Subject: [PATCH 1467/2296] 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 Date: Tue, 6 Sep 2022 11:33:04 +0200 Subject: [PATCH 1468/2296] 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 Date: Tue, 6 Sep 2022 19:56:08 +0200 Subject: [PATCH 1469/2296] 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 Date: Wed, 7 Sep 2022 16:34:41 +0200 Subject: [PATCH 1470/2296] 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 Date: Wed, 1 Jun 2022 10:14:24 -0400 Subject: [PATCH 1471/2296] 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 Date: Wed, 7 Sep 2022 16:30:40 -0400 Subject: [PATCH 1472/2296] 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 Date: Thu, 8 Sep 2022 09:03:32 -0400 Subject: [PATCH 1473/2296] 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 Date: Thu, 8 Sep 2022 11:22:01 -0400 Subject: [PATCH 1474/2296] 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?= Date: Mon, 12 Sep 2022 09:28:04 +0200 Subject: [PATCH 1475/2296] 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 Date: Mon, 12 Sep 2022 14:59:42 +0200 Subject: [PATCH 1476/2296] 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 Date: Sat, 10 Sep 2022 15:35:23 +0430 Subject: [PATCH 1477/2296] 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 Date: Thu, 22 Sep 2022 13:42:57 -0700 Subject: [PATCH 1478/2296] 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 Date: Thu, 22 Sep 2022 15:50:30 -0700 Subject: [PATCH 1479/2296] 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 Date: Wed, 21 Sep 2022 10:05:43 +0200 Subject: [PATCH 1480/2296] 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 Date: Fri, 30 Sep 2022 10:28:52 +0200 Subject: [PATCH 1481/2296] 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 Date: Tue, 6 Sep 2022 16:41:51 +0200 Subject: [PATCH 1482/2296] 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 Date: Tue, 4 Oct 2022 10:18:53 +0200 Subject: [PATCH 1483/2296] 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 Date: Thu, 8 Sep 2022 11:01:51 +0200 Subject: [PATCH 1484/2296] 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 Date: Tue, 4 Oct 2022 18:14:27 +0200 Subject: [PATCH 1485/2296] 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 Date: Wed, 5 Oct 2022 10:16:33 +0200 Subject: [PATCH 1486/2296] 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 Date: Wed, 12 Oct 2022 17:46:48 +0200 Subject: [PATCH 1487/2296] 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 Date: Wed, 12 Oct 2022 17:51:25 +0200 Subject: [PATCH 1488/2296] 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 1489/2296] 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] --- .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 Date: Thu, 13 Oct 2022 15:25:27 +0200 Subject: [PATCH 1490/2296] 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 706597e94d5316815a4dc1159f1bc42bf69b0dc0 Mon Sep 17 00:00:00 2001 From: nikParasyr Date: Thu, 13 Oct 2022 19:38:37 +0200 Subject: [PATCH 1491/2296] Add tags for loadbalancer l7policy and l7rule Tags are supported for octavia l7policy and l7rules since version 2.5. Add support for Create and Update. [Docs](https://docs.openstack.org/api-ref/load-balancer/v2/?expanded=create-an-l7-policy-detail,update-a-l7-policy-detail,create-an-l7-rule-detail,update-a-l7-rule-detail#create-an-l7-policy) --- .../openstack/loadbalancer/v2/loadbalancer.go | 8 ++++++-- .../openstack/loadbalancer/v2/loadbalancers_test.go | 13 +++++++++++-- openstack/loadbalancer/v2/l7policies/requests.go | 12 ++++++++++++ openstack/loadbalancer/v2/l7policies/results.go | 8 ++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index f8d44a8b17..94fa5307ea 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -419,7 +419,7 @@ func CreatePoolHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal // CreateL7Policy will create a l7 policy with a random name with a specified listener // and loadbalancer. An error will be returned if the l7 policy could not be // created. -func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) { +func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer, tags []string) (*l7policies.L7Policy, error) { policyName := tools.RandomString("TESTACCT-", 8) policyDescription := tools.RandomString("TESTACCT-DESC-", 8) @@ -431,6 +431,7 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l ListenerID: listener.ID, Action: l7policies.ActionRedirectToURL, RedirectURL: "http://www.example.com", + Tags: tags, } policy, err := l7policies.Create(client, createOpts).Extract() @@ -449,18 +450,20 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l th.AssertEquals(t, policy.ListenerID, listener.ID) th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL)) th.AssertEquals(t, policy.RedirectURL, "http://www.example.com") + th.AssertDeepEquals(t, policy.Tags, tags) return policy, nil } // CreateL7Rule creates a l7 rule for specified l7 policy. -func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer) (*l7policies.Rule, error) { +func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer, tags []string) (*l7policies.Rule, error) { t.Logf("Attempting to create l7 rule for policy %s", policyID) createOpts := l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeStartWith, Value: "/api", + Tags: tags, } rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract() @@ -477,6 +480,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath)) th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith)) th.AssertEquals(t, rule.Value, "/api") + th.AssertDeepEquals(t, rule.Tags, tags) return rule, nil } diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index c21663ef35..096157ff0a 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -120,13 +120,16 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { defer DeleteListener(t, lbClient, lb.ID, listener.ID) // L7 policy - policy, err := CreateL7Policy(t, lbClient, listener, lb) + tags := []string{"test"} + policy, err := CreateL7Policy(t, lbClient, listener, lb, tags) th.AssertNoErr(t, err) defer DeleteL7Policy(t, lbClient, lb.ID, policy.ID) + tags = []string{"test", "test1"} newDescription := "" updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, + Tags: &tags, } _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) @@ -141,9 +144,11 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Description, newDescription) + th.AssertDeepEquals(t, newPolicy.Tags, tags) // L7 rule - rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb) + tags = []string{"test"} + rule, err := CreateL7Rule(t, lbClient, newPolicy.ID, lb, tags) th.AssertNoErr(t, err) defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) @@ -155,10 +160,12 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { tools.PrintResource(t, rule) } + tags = []string{"test", "test1"} updateL7ruleOpts := l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", + Tags: &tags, } _, err = l7policies.UpdateRule(lbClient, policy.ID, rule.ID, updateL7ruleOpts).Extract() th.AssertNoErr(t, err) @@ -172,6 +179,8 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { tools.PrintResource(t, newRule) + th.AssertDeepEquals(t, newRule.Tags, tags) + // Pool pool, err := CreatePoolHTTP(t, lbClient, lb) th.AssertNoErr(t, err) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 2b84bcad14..86e20c0faa 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -83,6 +83,9 @@ type CreateOpts struct { // This is only possible to use when creating a fully populated // Loadbalancer. Rules []CreateRuleOpts `json:"rules,omitempty"` + + // Tags is a set of resource tags. Requires version 2.5. + Tags []string `json:"tags,omitempty"` } // ToL7PolicyCreateMap builds a request body from CreateOpts. @@ -208,6 +211,9 @@ type UpdateOpts struct { // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Tags is a set of resource tags. Requires version 2.5. + Tags *[]string `json:"tags,omitempty"` } // ToL7PolicyUpdateMap builds a request body from UpdateOpts. @@ -278,6 +284,9 @@ type CreateRuleOpts struct { // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Tags is a set of resource tags. Requires version 2.5. + Tags []string `json:"tags,omitempty"` } // ToRuleCreateMap builds a request body from CreateRuleOpts. @@ -387,6 +396,9 @@ type UpdateRuleOpts struct { // The administrative state of the Loadbalancer. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Tags is a set of resource tags. Requires version 2.5. + Tags *[]string `json:"tags,omitempty"` } // ToRuleUpdateMap builds a request body from UpdateRuleOpts. diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index cf236d742c..6285858165 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -58,6 +58,10 @@ type L7Policy struct { // Rules are List of associated L7 rule IDs. Rules []Rule `json:"rules"` + + // Tags is a list of resource tags. Tags are arbitrarily defined strings + // attached to the resource. + Tags []string `json:"tags"` } // Rule represents layer 7 load balancing rule. @@ -94,6 +98,10 @@ type Rule struct { // The operating status of the L7 policy. OperatingStatus string `json:"operating_status"` + + // Tags is a list of resource tags. Tags are arbitrarily defined strings + // attached to the resource. + Tags []string `json:"tags"` } type commonResult struct { 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 1492/2296] 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] --- .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 Date: Mon, 31 Oct 2022 20:55:52 +1300 Subject: [PATCH 1493/2296] 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 Date: Tue, 1 Nov 2022 12:20:27 +1300 Subject: [PATCH 1494/2296] 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 Date: Tue, 1 Nov 2022 20:59:35 +1300 Subject: [PATCH 1495/2296] 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 Date: Tue, 1 Nov 2022 23:23:16 +1300 Subject: [PATCH 1496/2296] 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 Date: Tue, 1 Nov 2022 23:35:54 +1300 Subject: [PATCH 1497/2296] 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 1498/2296] 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] --- .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 Date: Mon, 7 Nov 2022 11:17:43 +0100 Subject: [PATCH 1499/2296] 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 Date: Mon, 7 Nov 2022 15:55:53 +0100 Subject: [PATCH 1500/2296] 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 Date: Fri, 11 Nov 2022 15:18:53 +1300 Subject: [PATCH 1501/2296] 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 Date: Sat, 12 Nov 2022 09:14:55 +1300 Subject: [PATCH 1502/2296] 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é --- .../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 Date: Wed, 16 Nov 2022 11:11:07 +0300 Subject: [PATCH 1503/2296] 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 Date: Thu, 20 Oct 2022 14:57:07 +0200 Subject: [PATCH 1504/2296] 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 Date: Wed, 23 Nov 2022 12:50:29 +0100 Subject: [PATCH 1505/2296] 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?= Date: Wed, 21 Sep 2022 10:18:24 +0200 Subject: [PATCH 1506/2296] 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?= Date: Wed, 21 Sep 2022 22:35:34 +0200 Subject: [PATCH 1507/2296] 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?= Date: Wed, 21 Sep 2022 23:01:08 +0200 Subject: [PATCH 1508/2296] 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 Date: Wed, 23 Nov 2022 16:36:38 +0100 Subject: [PATCH 1509/2296] 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 Date: Mon, 12 Sep 2022 10:50:17 +0200 Subject: [PATCH 1510/2296] 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 Date: Wed, 7 Dec 2022 14:26:50 +0100 Subject: [PATCH 1511/2296] 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 Date: Wed, 14 Dec 2022 15:06:53 +0100 Subject: [PATCH 1512/2296] 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?= Date: Wed, 14 Dec 2022 13:05:24 +0100 Subject: [PATCH 1513/2296] [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?= Date: Wed, 14 Dec 2022 13:42:45 +0100 Subject: [PATCH 1514/2296] [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?= Date: Wed, 14 Dec 2022 13:44:30 +0100 Subject: [PATCH 1515/2296] [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 Date: Thu, 22 Dec 2022 01:54:24 +0100 Subject: [PATCH 1516/2296] 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 Date: Thu, 22 Dec 2022 13:08:40 +0100 Subject: [PATCH 1517/2296] 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 Date: Thu, 22 Dec 2022 14:19:01 +0100 Subject: [PATCH 1518/2296] 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 Date: Thu, 22 Dec 2022 15:35:46 +0100 Subject: [PATCH 1519/2296] 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 Date: Tue, 27 Dec 2022 16:11:47 +0100 Subject: [PATCH 1520/2296] 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 Date: Tue, 27 Dec 2022 16:26:59 +0100 Subject: [PATCH 1521/2296] 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 1522/2296] 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] --- .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 Date: Mon, 23 Jan 2023 15:36:58 -0500 Subject: [PATCH 1523/2296] 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 - - - -OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases. - -### VEXXHOST - - - -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 Date: Wed, 18 Jan 2023 14:40:17 +0200 Subject: [PATCH 1524/2296] 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 Date: Fri, 27 Jan 2023 13:28:39 +0100 Subject: [PATCH 1525/2296] 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 Date: Tue, 29 Nov 2022 09:10:17 -0500 Subject: [PATCH 1526/2296] 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 Date: Wed, 25 Jan 2023 10:33:07 +0100 Subject: [PATCH 1527/2296] 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 Date: Wed, 25 Jan 2023 10:34:29 +0100 Subject: [PATCH 1528/2296] 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?= Date: Fri, 27 Jan 2023 18:26:57 +0100 Subject: [PATCH 1529/2296] 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 Date: Tue, 27 Dec 2022 16:26:15 +0100 Subject: [PATCH 1530/2296] 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 Date: Thu, 2 Feb 2023 16:25:15 +0100 Subject: [PATCH 1531/2296] 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?= Date: Thu, 2 Feb 2023 18:00:45 +0100 Subject: [PATCH 1532/2296] 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 Date: Wed, 25 Jan 2023 12:24:52 +0100 Subject: [PATCH 1533/2296] 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 Date: Sat, 4 Feb 2023 14:35:02 +0100 Subject: [PATCH 1534/2296] 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 Date: Mon, 6 Feb 2023 15:04:02 +0100 Subject: [PATCH 1535/2296] 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 Date: Wed, 8 Feb 2023 14:41:15 +0100 Subject: [PATCH 1536/2296] 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 Date: Tue, 22 Nov 2022 17:20:10 +0100 Subject: [PATCH 1537/2296] 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 81fb82d5493a2d85a71b6d1c3db4f3d5433ba3d4 Mon Sep 17 00:00:00 2001 From: Daniel Failing Date: Thu, 16 Feb 2023 09:57:44 +0100 Subject: [PATCH 1538/2296] loadbalancer: Use CreateMemberOpts instead of BatchUpdateMemberOpts in PoolCreateOpts In order to support members without any subnet, the struct for the pool members need to be CreateMemberOpts. --- openstack/loadbalancer/v2/pools/requests.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 69e6a2a763..e86d1ef245 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -123,12 +123,12 @@ type CreateOpts struct { // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` - // Members is a slice of BatchUpdateMemberOpts which allows a set of + // Members is a slice of CreateMemberOpts which allows a set of // members to be created at the same time the pool is created. // // This is only possible to use when creating a fully populated // Loadbalancer. - Members []BatchUpdateMemberOpts `json:"members,omitempty"` + Members []CreateMemberOpts `json:"members,omitempty"` // Monitor is an instance of monitors.CreateOpts which allows a monitor // to be created at the same time the pool is created. From 806d4afbd8dfddc9eefb768ece8763c58b00d38a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 16 Feb 2023 11:16:20 +0000 Subject: [PATCH 1539/2296] 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 --- 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 Date: Fri, 17 Feb 2023 09:30:22 +0200 Subject: [PATCH 1540/2296] 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 Date: Mon, 20 Feb 2023 10:10:52 +0000 Subject: [PATCH 1541/2296] README: Remove unnecessary import Signed-off-by: Stephen Finucane --- 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 Date: Thu, 23 Feb 2023 17:15:52 +0100 Subject: [PATCH 1542/2296] 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 Date: Fri, 24 Feb 2023 22:13:45 +0100 Subject: [PATCH 1543/2296] 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?= Date: Fri, 24 Feb 2023 08:47:56 +0100 Subject: [PATCH 1544/2296] 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?= Date: Fri, 24 Feb 2023 08:50:33 +0100 Subject: [PATCH 1545/2296] 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?= Date: Fri, 24 Feb 2023 17:01:40 +0100 Subject: [PATCH 1546/2296] 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 Date: Wed, 7 Jul 2021 12:18:06 +0200 Subject: [PATCH 1547/2296] 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?= Date: Tue, 28 Feb 2023 15:37:05 +0100 Subject: [PATCH 1548/2296] 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?= Date: Tue, 28 Feb 2023 18:20:27 +0100 Subject: [PATCH 1549/2296] 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?= Date: Tue, 28 Feb 2023 18:21:46 +0100 Subject: [PATCH 1550/2296] 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 1551/2296] 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 1552/2296] 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 70b8539c907b45d3b1139dd4ad0d7dafb956aea0 Mon Sep 17 00:00:00 2001 From: Daniel Failing Date: Thu, 2 Mar 2023 09:20:02 +0100 Subject: [PATCH 1553/2296] Fix tests --- acceptance/openstack/loadbalancer/v2/loadbalancer.go | 2 +- .../loadbalancer/v2/loadbalancers/testing/requests_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 8d5efb011d..7777292b1c 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -195,7 +195,7 @@ func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceC Description: poolDescription, Protocol: pools.ProtocolHTTP, LBMethod: pools.LBMethodLeastConnections, - Members: []pools.BatchUpdateMemberOpts{{ + Members: []pools.CreateMemberOpts{{ Name: &memberName, ProtocolPort: memberPort, Weight: &memberWeight, diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index fbd33c648c..34656f50ff 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -110,7 +110,7 @@ func TestCreateFullyPopulatedLoadbalancer(t *testing.T) { LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", - Members: []pools.BatchUpdateMemberOpts{{ + Members: []pools.CreateMemberOpts{{ Address: "192.0.2.51", ProtocolPort: 80, }, { From 207de4adf1e8750b838717c9fe1b2d71b464a710 Mon Sep 17 00:00:00 2001 From: Daniel Failing Date: Thu, 2 Mar 2023 09:55:51 +0100 Subject: [PATCH 1554/2296] fix tests 2 --- acceptance/openstack/loadbalancer/v2/loadbalancer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 7777292b1c..9b8e6668db 100644 --- a/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -196,11 +196,11 @@ func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceC Protocol: pools.ProtocolHTTP, LBMethod: pools.LBMethodLeastConnections, Members: []pools.CreateMemberOpts{{ - Name: &memberName, + Name: memberName, ProtocolPort: memberPort, Weight: &memberWeight, Address: "1.2.3.4", - SubnetID: &subnetID, + SubnetID: subnetID, }}, Monitor: &monitors.CreateOpts{ Delay: 10, From 680a8dffd45be47f06b1a96784e2339b989c3406 Mon Sep 17 00:00:00 2001 From: gxxxh Date: Fri, 17 Feb 2023 10:15:45 +0800 Subject: [PATCH 1555/2296] add extra_specs to flavor --- .../openstack/compute/v2/flavors_test.go | 31 +++++++++++++++++++ openstack/compute/v2/flavors/results.go | 6 ++++ .../v2/flavors/testing/requests_test.go | 14 +++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/acceptance/openstack/compute/v2/flavors_test.go b/acceptance/openstack/compute/v2/flavors_test.go index 9e3ec1db45..4b289b9b77 100644 --- a/acceptance/openstack/compute/v2/flavors_test.go +++ b/acceptance/openstack/compute/v2/flavors_test.go @@ -77,6 +77,37 @@ func TestFlavorsGet(t *testing.T) { th.AssertEquals(t, flavor.ID, choices.FlavorID) } +func TestFlavorExtraSpecsGet(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + // Microversion 2.61 is required to add extra_specs to flavor + client.Microversion = "2.61" + + flavor, err := CreatePrivateFlavor(t, client) + th.AssertNoErr(t, err) + defer DeleteFlavor(t, client, flavor) + + createOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_policy": "CPU-POLICY", + "hw:cpu_thread_policy": "CPU-THREAD-POLICY", + } + createdExtraSpecs, err := flavors.CreateExtraSpecs(client, flavor.ID, createOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, createdExtraSpecs) + + flavor, err = flavors.Get(client, flavor.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, flavor) + th.AssertEquals(t, len(flavor.ExtraSpecs), 2) + th.AssertEquals(t, flavor.ExtraSpecs["hw:cpu_policy"], "CPU-POLICY") + th.AssertEquals(t, flavor.ExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY") +} + func TestFlavorsCreateDelete(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 0234402ed2..003984ef6f 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -80,6 +80,12 @@ type Flavor struct { // 65535 characters in length. Only printable characters are allowed. // New in version 2.55 Description string `json:"description"` + + // Properties is a dictionary of the flavor’s extra-specs key-and-value + // pairs. This will only be included if the user is allowed by policy to + // index flavor extra_specs + // New in version 2.61 + ExtraSpecs map[string]string `json:"extra_specs"` } func (r *Flavor) UnmarshalJSON(b []byte) error { diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 05b7fbb57a..356cdd6cc9 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -39,7 +39,11 @@ func TestListFlavors(t *testing.T) { "swap":"", "os-flavor-access:is_public": true, "OS-FLV-EXT-DATA:ephemeral": 10, - "description": "foo" + "description": "foo", + "extra_specs": + { + "foo": "bar" + } }, { "id": "2", @@ -88,7 +92,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, Description: "foo"}, + {ID: "1", Name: "m1.tiny", VCPUs: 1, Disk: 1, RAM: 9216000, Swap: 0, IsPublic: true, Ephemeral: 10, Description: "foo", ExtraSpecs: map[string]string{"foo": "bar"}}, {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}, } @@ -126,7 +130,10 @@ func TestGetFlavor(t *testing.T) { "vcpus": 1, "rxtx_factor": 1, "swap": "", - "description": "foo" + "description": "foo", + "extra_specs": { + "foo": "bar" + } } } `) @@ -146,6 +153,7 @@ func TestGetFlavor(t *testing.T) { RxTxFactor: 1, Swap: 0, Description: "foo", + ExtraSpecs: map[string]string{"foo": "bar"}, } if !reflect.DeepEqual(expected, actual) { t.Errorf("Expected %#v, but was %#v", expected, actual) From a8ce133cdf4879929357cc75bb41cbabc203acc9 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 5 Mar 2023 13:44:42 +0100 Subject: [PATCH 1556/2296] [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 Date: Thu, 26 Jan 2023 10:46:24 +0100 Subject: [PATCH 1557/2296] [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 1558/2296] 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 Date: Fri, 10 Mar 2023 16:34:28 +0100 Subject: [PATCH 1559/2296] 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 1560/2296] 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] --- .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 Date: Tue, 14 Mar 2023 11:38:32 +0100 Subject: [PATCH 1561/2296] 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 1562/2296] 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] --- .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?= Date: Sat, 4 Mar 2023 12:45:50 +0100 Subject: [PATCH 1563/2296] 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?= Date: Sat, 4 Mar 2023 12:48:49 +0100 Subject: [PATCH 1564/2296] 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?= Date: Tue, 14 Mar 2023 11:39:17 +0100 Subject: [PATCH 1565/2296] 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?= Date: Tue, 7 Mar 2023 15:44:34 +0100 Subject: [PATCH 1566/2296] 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 Date: Thu, 29 Dec 2022 16:59:10 +0100 Subject: [PATCH 1567/2296] 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 Date: Wed, 22 Mar 2023 18:34:09 +0100 Subject: [PATCH 1568/2296] 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 Date: Wed, 22 Mar 2023 18:35:01 +0100 Subject: [PATCH 1569/2296] 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 Date: Tue, 28 Mar 2023 11:55:58 +0200 Subject: [PATCH 1570/2296] 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 Date: Tue, 28 Mar 2023 13:59:03 +0200 Subject: [PATCH 1571/2296] 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 Date: Thu, 30 Mar 2023 11:50:04 +0200 Subject: [PATCH 1572/2296] [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 1573/2296] 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] --- .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 Date: Wed, 22 Mar 2023 18:41:25 +0100 Subject: [PATCH 1574/2296] 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?= Date: Sun, 9 Apr 2023 21:47:35 +0200 Subject: [PATCH 1575/2296] 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 Date: Fri, 28 Apr 2023 15:33:13 +0200 Subject: [PATCH 1576/2296] 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 Date: Fri, 28 Apr 2023 16:09:59 +0200 Subject: [PATCH 1577/2296] 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 Date: Fri, 28 Apr 2023 16:37:50 +0200 Subject: [PATCH 1578/2296] 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 Date: Sun, 14 May 2023 15:31:50 +0200 Subject: [PATCH 1579/2296] 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 Date: Thu, 29 Dec 2022 17:09:47 +0100 Subject: [PATCH 1580/2296] 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?= Date: Wed, 17 May 2023 12:52:06 +0200 Subject: [PATCH 1581/2296] 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?= Date: Wed, 17 May 2023 10:54:39 +0200 Subject: [PATCH 1582/2296] 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?= Date: Wed, 17 May 2023 11:09:32 +0200 Subject: [PATCH 1583/2296] 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 Date: Wed, 10 May 2023 00:12:51 +0000 Subject: [PATCH 1584/2296] 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 Date: Thu, 29 Dec 2022 17:24:25 +0100 Subject: [PATCH 1585/2296] 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 Date: Wed, 17 May 2023 09:46:28 +0200 Subject: [PATCH 1586/2296] 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 Date: Fri, 19 May 2023 17:46:58 +0300 Subject: [PATCH 1587/2296] 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 Date: Fri, 19 May 2023 17:28:39 +0100 Subject: [PATCH 1588/2296] 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 Date: Tue, 23 May 2023 09:13:09 +0000 Subject: [PATCH 1589/2296] 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 Date: Wed, 24 May 2023 08:39:49 +0100 Subject: [PATCH 1590/2296] 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 Date: Wed, 24 May 2023 15:32:08 +0200 Subject: [PATCH 1591/2296] [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?= Date: Thu, 25 May 2023 10:02:10 +0200 Subject: [PATCH 1592/2296] 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?= Date: Thu, 25 May 2023 10:03:06 +0200 Subject: [PATCH 1593/2296] 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 Date: Thu, 25 May 2023 15:17:48 +0200 Subject: [PATCH 1594/2296] 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 Date: Tue, 30 May 2023 14:22:26 +0300 Subject: [PATCH 1595/2296] 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 Date: Tue, 30 May 2023 14:43:52 +0300 Subject: [PATCH 1596/2296] 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 Date: Tue, 30 May 2023 14:54:51 +0300 Subject: [PATCH 1597/2296] 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 Date: Tue, 30 May 2023 16:47:37 +0300 Subject: [PATCH 1598/2296] 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 Date: Fri, 2 Jun 2023 14:37:27 +0200 Subject: [PATCH 1599/2296] 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 Date: Tue, 6 Jun 2023 13:51:21 +0200 Subject: [PATCH 1600/2296] 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é --- 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 Date: Wed, 7 Jun 2023 18:48:02 +0300 Subject: [PATCH 1601/2296] 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 Date: Thu, 8 Jun 2023 11:47:50 +0300 Subject: [PATCH 1602/2296] 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 Date: Thu, 8 Jun 2023 14:48:37 +0300 Subject: [PATCH 1603/2296] 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 1604/2296] 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] --- .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 Date: Fri, 26 May 2023 11:51:23 +0200 Subject: [PATCH 1605/2296] [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 Date: Wed, 14 Jun 2023 22:18:37 +0200 Subject: [PATCH 1606/2296] 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 Date: Wed, 14 Jun 2023 23:22:53 +0200 Subject: [PATCH 1607/2296] [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 Date: Tue, 20 Jun 2023 15:18:21 +0200 Subject: [PATCH 1608/2296] 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 48aa6d531e91ce5c653f514fa6db2c012d431ec8 Mon Sep 17 00:00:00 2001 From: georgeb Date: Wed, 7 Jun 2023 17:12:16 +0200 Subject: [PATCH 1609/2296] 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 980eb8a342ee21e01ec1cb28f4352ced0c18c9c5 Mon Sep 17 00:00:00 2001 From: georgeb Date: Mon, 12 Jun 2023 11:32:41 +0200 Subject: [PATCH 1610/2296] 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 ea71744690516fe42b6dba075265587990fdfa09 Mon Sep 17 00:00:00 2001 From: georgeb Date: Tue, 20 Jun 2023 17:29:13 +0200 Subject: [PATCH 1611/2296] 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 b98471812a77380b2bb25a28b04bb1be9fc2e72e Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 28 Jun 2023 11:12:55 +0200 Subject: [PATCH 1612/2296] Instrument backporting to v1 The stable branch is now `v1`. Non-breaking changes will be backported by applying a `backport-v1` label. --- .github/backport-labels.yaml | 3 ++ .github/workflows/backport_v1.yaml | 31 +++++++++++++++++++ ...{semver-labels.yaml => ensure-labels.yaml} | 12 ++++++- README.md | 8 ++--- 4 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 .github/backport-labels.yaml create mode 100644 .github/workflows/backport_v1.yaml rename .github/workflows/{semver-labels.yaml => ensure-labels.yaml} (53%) diff --git a/.github/backport-labels.yaml b/.github/backport-labels.yaml new file mode 100644 index 0000000000..cf4e8818f0 --- /dev/null +++ b/.github/backport-labels.yaml @@ -0,0 +1,3 @@ +- name: backport-v1 + description: This PR will be backported to v1 + color: '30ABB9' diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml new file mode 100644 index 0000000000..0e3e967ec8 --- /dev/null +++ b/.github/workflows/backport_v1.yaml @@ -0,0 +1,31 @@ +name: Pull Request backporting + +on: + pull_request_target: + types: + - closed + - labeled + +jobs: + backporting: + name: "Backporting" + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + && contains(github.event.pull_request.labels.*.name, 'backport-v1') + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport-v1') + ) + ) + runs-on: ubuntu-latest + steps: + - name: Backporting + uses: pierreprinetti/backport@v1 + with: + target-branch: v1 + pull-request: ${{ github.event.pull_request.url }} + auth: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/semver-labels.yaml b/.github/workflows/ensure-labels.yaml similarity index 53% rename from .github/workflows/semver-labels.yaml rename to .github/workflows/ensure-labels.yaml index ccaf44522b..534167cb7b 100644 --- a/.github/workflows/semver-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -4,7 +4,8 @@ on: branches: - master paths: - - .github/semver-labels.yaml + - .github/ensure-labels.yaml + - .github/backport-labels.yaml - .github/workflows/semver-labels.yaml jobs: semver: @@ -16,3 +17,12 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: manifest: .github/semver-labels.yaml + backport: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: micnncim/action-label-syncer@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + manifest: .github/backport-labels.yaml diff --git a/README.md b/README.md index 89b08156fe..7a2abbee1b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ # Gophercloud: an OpenStack SDK for Go -[![Build Status](https://travis-ci.org/gophercloud/gophercloud.svg?branch=master)](https://travis-ci.org/gophercloud/gophercloud) [![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master) -Gophercloud is an OpenStack Go SDK. +[Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud) -## Useful links +Gophercloud is a Go SDK for OpenStack. -* [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud) -* [Effective Go](https://golang.org/doc/effective_go.html) +This is the development branch of Gophercloud; stable releases are cut from the branch `v1`. ## How to install From d8128d396eb63adbf7e68b05bc717d3f461d1025 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 29 Jun 2023 14:56:39 +0200 Subject: [PATCH 1613/2296] chore: Fix labels The PR https://github.com/gophercloud/gophercloud/pull/2656 caused all labels to be removed, because each run of the action eliminates all labels that aren't listed. This PR reintroduces our semver labels and the `good first issue` label in the repository. --- .github/labels.yaml | 15 +++++++++++++++ .github/semver-labels.yaml | 9 --------- .github/workflows/ensure-labels.yaml | 20 +++++--------------- 3 files changed, 20 insertions(+), 24 deletions(-) create mode 100644 .github/labels.yaml delete mode 100644 .github/semver-labels.yaml diff --git a/.github/labels.yaml b/.github/labels.yaml new file mode 100644 index 0000000000..82dbcbad31 --- /dev/null +++ b/.github/labels.yaml @@ -0,0 +1,15 @@ +- color: '9E1957' + description: Breaking change + name: semver:major +- color: 'FBCA04' + description: Backwards-compatible change + name: semver:minor +- color: '6E7624' + description: No API change + name: semver:patch +- color: '30ABB9' + description: This PR will be backported to v1 + name: backport-v1 +- color: 'BCF611' + description: A good issue for first-time contributors + name: good first issue 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/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index 534167cb7b..a6aa52aea8 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -1,14 +1,13 @@ -name: Ensure labels +name: Apply labels in .github/labels.yaml on: push: branches: - master paths: - - .github/ensure-labels.yaml - - .github/backport-labels.yaml - - .github/workflows/semver-labels.yaml + - .github/labels.yaml + - .github/workflows/ensure-labels.yaml jobs: - semver: + ensure: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -16,13 +15,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - manifest: .github/semver-labels.yaml - backport: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: micnncim/action-label-syncer@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - manifest: .github/backport-labels.yaml + manifest: .github/labels.yaml From 7cf90c21b37c49b600d401d11033a81db1a37b34 Mon Sep 17 00:00:00 2001 From: nikParasyr Date: Thu, 29 Jun 2023 22:42:30 +0200 Subject: [PATCH 1614/2296] 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 86bd2445575c6d882a3ffdfd9933c6996d771bb1 Mon Sep 17 00:00:00 2001 From: nikParasyr Date: Thu, 29 Jun 2023 23:12:45 +0200 Subject: [PATCH 1615/2296] 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 cf6531ed80e71416a63a42c747b8163c37aab31d Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 28 Jun 2023 15:31:34 -0400 Subject: [PATCH 1616/2296] 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é [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": " 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"], " 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 9a407d60a7f0f51463429fa5890b73ed86a4cb16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 6 Jul 2023 10:25:24 +0900 Subject: [PATCH 1617/2296] [CI] Drop branch filters for 'Ensure Labels' and 'CodeQL' jobs These jobs should run on all branches, so drop the branch filter that were in place. For the CodeQL job, it seems like the github action syntax does not allow for a simplified `on` syntax, while at the same time using a `on.schedule`, so drop the periodic that we weren't paying attention to anyway. We have enough signal with just the PR jobs. --- .github/workflows/codeql-analysis.yml | 9 +-------- .github/workflows/ensure-labels.yaml | 2 -- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index db762de4d7..38cf2bfb08 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,13 +1,6 @@ name: "CodeQL" -on: - push: - branches: [ master ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master ] - schedule: - - cron: '18 8 * * 6' +on: [push, pull_request] jobs: analyze: diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index a6aa52aea8..5f86a6c766 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -1,8 +1,6 @@ name: Apply labels in .github/labels.yaml on: push: - branches: - - master paths: - .github/labels.yaml - .github/workflows/ensure-labels.yaml From 9a62fc3a65728bc698dac21046ffa11ce0164e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 7 Jul 2023 13:22:19 +0900 Subject: [PATCH 1618/2296] Restore branch filter for ensure-labels workflow This workflow should only run on the main branch. The labels are repo-wide and shouldn't be controlled by multiple branches, as there's a risk it removes non-listed labels if the label declaration files differ between branches. Partially reverts https://github.com/gophercloud/gophercloud/pull/2674/. --- .github/workflows/ensure-labels.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index 5f86a6c766..a6aa52aea8 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -1,6 +1,8 @@ name: Apply labels in .github/labels.yaml on: push: + branches: + - master paths: - .github/labels.yaml - .github/workflows/ensure-labels.yaml From f81f168a10f136e0d2cf37b756783380d85715c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 7 Jul 2023 13:27:45 +0900 Subject: [PATCH 1619/2296] Remove unused backport-labels.yaml file This removes a file that is no longer used and should have been cleaned up in https://github.com/gophercloud/gophercloud/pull/2661. --- .github/backport-labels.yaml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .github/backport-labels.yaml diff --git a/.github/backport-labels.yaml b/.github/backport-labels.yaml deleted file mode 100644 index cf4e8818f0..0000000000 --- a/.github/backport-labels.yaml +++ /dev/null @@ -1,3 +0,0 @@ -- name: backport-v1 - description: This PR will be backported to v1 - color: '30ABB9' From 44eb3a191fc6e244936a6efe33830cd1c0ea1ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 10 Jul 2023 10:30:58 +0900 Subject: [PATCH 1620/2296] Advertise slack channel We have a shiny new #gophercloud channel on kubernetes slack. Let's advertise it. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7a2abbee1b..8826bc8318 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ Gophercloud is a Go SDK for OpenStack. +Join us on kubernetes slack, on [#gophercloud](https://kubernetes.slack.com/archives/C05G4NJ6P6X). Visit [slack.k8s.io](https://slack.k8s.io) for an invitation. + This is the development branch of Gophercloud; stable releases are cut from the branch `v1`. ## How to install From 1804ad9c5aa8246d05cc08416a3ed7c67436b757 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 12 Jul 2023 10:16:18 +0200 Subject: [PATCH 1621/2296] Use git-backporting Using a fork is no longer necessary, as watermarks have been removed from the code. This version bump brings: * merge commits * the default git merge strategy --- .github/workflows/backport_v1.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 0e3e967ec8..5114815bf9 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -24,8 +24,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Backporting - uses: pierreprinetti/backport@v1 + uses: kiegroup/git-backporting@265955dda77a8191fd1f64517fec20e8d5f8c5b4 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} auth: ${{ secrets.GITHUB_TOKEN }} + no-squash: true + strategy-option: find-renames From 7e5019e81fa096a10e2a3fb86a7ddfb5e59252ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 09:33:28 +0000 Subject: [PATCH 1622/2296] Bump kiegroup/git-backporting Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 265955dda77a8191fd1f64517fec20e8d5f8c5b4 to e29dae5073d5b026781931f9be00fc19d0453acb. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/265955dda77a8191fd1f64517fec20e8d5f8c5b4...e29dae5073d5b026781931f9be00fc19d0453acb) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 5114815bf9..04cfcf61e8 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Backporting - uses: kiegroup/git-backporting@265955dda77a8191fd1f64517fec20e8d5f8c5b4 + uses: kiegroup/git-backporting@e29dae5073d5b026781931f9be00fc19d0453acb with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} From 3f96d65fd60ca874e64f1b46f2aa69fe51bf1dcc Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 13 Jul 2023 13:06:34 +0200 Subject: [PATCH 1623/2296] labels: Add Dependabot labels They are fine. --- .github/labels.yaml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/labels.yaml b/.github/labels.yaml index 82dbcbad31..f38c346352 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -1,3 +1,15 @@ +- color: '30ABB9' + description: This PR will be backported to v1 + name: backport-v1 +- color: '0366d6' + description: Pull requests that update a dependency file + name: dependencies +- color: '000000' + description: Pull requests that update GitHub Actions code + name: github_actions +- color: 'BCF611' + description: A good issue for first-time contributors + name: good first issue - color: '9E1957' description: Breaking change name: semver:major @@ -7,9 +19,3 @@ - color: '6E7624' description: No API change name: semver:patch -- color: '30ABB9' - description: This PR will be backported to v1 - name: backport-v1 -- color: 'BCF611' - description: A good issue for first-time contributors - name: good first issue From 514b0d882695e202842b7226b524a4da475f1f16 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 12 Jul 2023 20:40:46 +0200 Subject: [PATCH 1624/2296] [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 223eac3b0d3d583d4be8e2b491c76891667a96ba Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 17 Jul 2023 18:03:00 +0200 Subject: [PATCH 1625/2296] [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 1d0246662607027819e6c1c416940bccd71ed870 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 14 Jun 2023 11:04:39 +0200 Subject: [PATCH 1626/2296] Require Go v1.20 In order to upgrade golang.org/x/crypto, and to get support to the latest language features, upgrade to a newer version of the language and its toolchain. --- .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 +- .../workflows/functional-sharedfilesystems.yaml | 2 +- .github/workflows/unit.yml | 7 +++---- go.mod | 6 ++++-- go.sum | 16 +++++----------- 21 files changed, 30 insertions(+), 35 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 0ae2f53a29..bcd20ae5d7 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -76,7 +76,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 727fb9d1e0..2b3cfde178 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -45,7 +45,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 07624e7cb1..0ce1dfece9 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -44,7 +44,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 110920487b..ec1a7a96d3 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -45,7 +45,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 188064e4dc..fcff786845 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -43,7 +43,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 4f1aca8db3..3baddab8fe 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -62,7 +62,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index ec9b829d96..aff6aa7a52 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -57,7 +57,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 4c0b4463ee..a676338fd1 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -40,7 +40,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 9cf1a44b8b..7bc1bae450 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 7b1c3fefd9..4484012cde 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index cd270e7416..8a38805af4 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -44,7 +44,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 843f3645c5..72b585ac2c 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -45,7 +45,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index b727eafbce..b84f97027f 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -44,7 +44,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 3f69e5abbe..d10bd89004 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -63,7 +63,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index f43f6cad8a..2bdcdec14e 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -48,7 +48,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 12dbe82a63..2def92f86b 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -44,7 +44,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 57d5990b0a..806a1cae87 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index b72c33e75c..d6332d28a2 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -57,7 +57,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v4 with: - go-version: '^1.15' + go-version: '^1.20' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 9a49c86d26..9d5272d781 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: go-version: - - "1.14" + - "1.20" - "1" env: @@ -33,9 +33,8 @@ jobs: 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@v0.8.0 + go install github.com/wadey/gocovmerge@master + go install golang.org/x/tools/cmd/goimports@latest - name: Run go vet run: | diff --git a/go.mod b/go.mod index 0c7f0517e6..3823c2337d 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,10 @@ module github.com/gophercloud/gophercloud -go 1.14 +go 1.20 require ( - golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 + golang.org/x/crypto v0.10.0 gopkg.in/yaml.v2 v2.4.0 ) + +require golang.org/x/sys v0.9.0 // indirect diff --git a/go.sum b/go.sum index 0d5a1cde5a..f561644d94 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,8 @@ -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= -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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= 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 cce9095445a51fcf86c2afcc67ef579e6bd5e3fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jul 2023 01:44:34 +0000 Subject: [PATCH 1627/2296] Bump golang.org/x/crypto Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.0.0-20220829220503-c86fa9a7ed90 to 0.11.0. - [Commits](https://github.com/golang/crypto/commits/v0.11.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 3823c2337d..6c8e4ea02f 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud go 1.20 require ( - golang.org/x/crypto v0.10.0 + golang.org/x/crypto v0.11.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.9.0 // indirect +require golang.org/x/sys v0.10.0 // indirect diff --git a/go.sum b/go.sum index f561644d94..2da03ab1ce 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= 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 7653bb5dc6dacd3b6f771c377ad644d25a4d2ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 30 Jun 2023 14:23:30 +0900 Subject: [PATCH 1628/2296] Cinder: Remove multiatttach request parameter The multiattach request parameter is no longer part of the API [1]. Starting with microversion 3.50 (Queens), you need to use a volume type with multiattach capability instead. Depends on https://github.com/gophercloud/gophercloud/pull/2658. [1] https://github.com/openstack/cinder/commit/d4535c77493a7b362091b962f42f2613dea65dbe --- openstack/blockstorage/v3/volumes/requests.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index f6063c5954..70ddb71dbc 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -41,8 +41,6 @@ type CreateOpts struct { BackupID string `json:"backup_id,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` - // Multiattach denotes if the volume is multi-attach capable. - Multiattach bool `json:"multiattach,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a From 00b86ec9b0f77c5d2bea202d780d23e5274b4960 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 21 Jul 2023 15:56:07 +0200 Subject: [PATCH 1629/2296] [cinder]: add reset and force delete actions to backups --- .../blockstorage/extensions/backups_test.go | 41 ++++++++++++++++ .../blockstorage/extensions/extensions.go | 22 +++++++++ .../openstack/blockstorage/v3/blockstorage.go | 2 + .../extensions/backups/requests.go | 49 +++++++++++++++++++ .../extensions/backups/results.go | 10 ++++ .../extensions/backups/testing/fixtures.go | 38 ++++++++++++++ .../backups/testing/requests_test.go | 23 +++++++++ .../blockstorage/extensions/backups/urls.go | 8 +++ .../blockstorage/v3/snapshots/requests.go | 2 +- 9 files changed, 194 insertions(+), 1 deletion(-) diff --git a/acceptance/openstack/blockstorage/extensions/backups_test.go b/acceptance/openstack/blockstorage/extensions/backups_test.go index 67f86c8eb6..b1bef16939 100644 --- a/acceptance/openstack/blockstorage/extensions/backups_test.go +++ b/acceptance/openstack/blockstorage/extensions/backups_test.go @@ -40,3 +40,44 @@ func TestBackupsCRUD(t *testing.T) { th.AssertEquals(t, found, true) } + +func TestBackupsResetStatus(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + backup, err := CreateBackup(t, blockClient, volume.ID) + th.AssertNoErr(t, err) + defer DeleteBackup(t, blockClient, backup.ID) + + err = ResetBackupStatus(t, blockClient, backup, "error") + th.AssertNoErr(t, err) + + err = ResetBackupStatus(t, blockClient, backup, "available") + th.AssertNoErr(t, err) +} + +func TestBackupsForceDelete(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := blockstorage.CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer blockstorage.DeleteVolume(t, blockClient, volume) + + backup, err := CreateBackup(t, blockClient, volume.ID) + th.AssertNoErr(t, err) + defer DeleteBackup(t, blockClient, backup.ID) + + err = WaitForBackupStatus(blockClient, backup.ID, "available") + th.AssertNoErr(t, err) + + err = backups.ForceDelete(blockClient, backup.ID).ExtractErr() + th.AssertNoErr(t, err) + + err = WaitForBackupStatus(blockClient, backup.ID, "deleted") + th.AssertNoErr(t, err) +} diff --git a/acceptance/openstack/blockstorage/extensions/extensions.go b/acceptance/openstack/blockstorage/extensions/extensions.go index dfa357fdd3..78bd4f5334 100644 --- a/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/acceptance/openstack/blockstorage/extensions/extensions.go @@ -227,6 +227,10 @@ func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID stri // could not be deleted. This works best when used as a deferred function. func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { if err := backups.Delete(client, backupID).ExtractErr(); err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + t.Logf("Backup %s is already deleted", backupID) + return + } t.Fatalf("Unable to delete backup %s: %s", backupID, err) } @@ -239,6 +243,9 @@ func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) e return tools.WaitFor(func() (bool, error) { current, err := backups.Get(client, id).Extract() if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok && status == "deleted" { + return true, nil + } return false, err } @@ -333,6 +340,21 @@ func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume * return nil } +// ResetBackupStatus will reset the status of a backup. +func ResetBackupStatus(t *testing.T, client *gophercloud.ServiceClient, backup *backups.Backup, status string) error { + t.Logf("Attempting to reset the status of backup %s from %s to %s", backup.ID, backup.Status, status) + + resetOpts := backups.ResetStatusOpts{ + Status: status, + } + err := backups.ResetStatus(client, backup.ID, resetOpts).ExtractErr() + if err != nil { + return err + } + + return WaitForBackupStatus(client, backup.ID, status) +} + // 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/v3/blockstorage.go b/acceptance/openstack/blockstorage/v3/blockstorage.go index ad6518b7de..8b829dfa72 100644 --- a/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -259,6 +259,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s err := snapshots.Delete(client, snapshot.ID).ExtractErr() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { + t.Logf("Snapshot %s is already deleted", snapshot.ID) return } t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) @@ -289,6 +290,7 @@ 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 { + t.Logf("Volume %s is already deleted", volume.ID) return } t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/extensions/backups/requests.go index 4eb123b9ff..5fb83227fe 100644 --- a/openstack/blockstorage/extensions/backups/requests.go +++ b/openstack/blockstorage/extensions/backups/requests.go @@ -301,3 +301,52 @@ func Import(client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ResetStatusOptsBuilder allows extensions to add additional parameters to the +// ResetStatus request. +type ResetStatusOptsBuilder interface { + ToBackupResetStatusMap() (map[string]interface{}, error) +} + +// ResetStatusOpts contains options for resetting a Backup status. +// For more information about these parameters, please, refer to the Block Storage API V3, +// Backup Actions, ResetStatus backup documentation. +type ResetStatusOpts struct { + // Status is a backup status to reset to. + Status string `json:"status"` +} + +// ToBackupResetStatusMap assembles a request body based on the contents of a +// ResetStatusOpts. +func (opts ResetStatusOpts) ToBackupResetStatusMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-reset_status") +} + +// ResetStatus will reset the existing backup 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.ToBackupResetStatusMap() + 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 backup 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/extensions/backups/results.go b/openstack/blockstorage/extensions/backups/results.go index 520a1ff486..4797373d76 100644 --- a/openstack/blockstorage/extensions/backups/results.go +++ b/openstack/blockstorage/extensions/backups/results.go @@ -339,3 +339,13 @@ func (r ImportBackup) MarshalJSON() ([]byte, error) { return json.Marshal(s) } + +// ResetStatusResult contains the response error from a ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} + +// ForceDeleteResult contains the response error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures.go b/openstack/blockstorage/extensions/backups/testing/fixtures.go index 94cd9b52f1..bb764b11f5 100644 --- a/openstack/blockstorage/extensions/backups/testing/fixtures.go +++ b/openstack/blockstorage/extensions/backups/testing/fixtures.go @@ -150,6 +150,20 @@ const ImportResponse = ` } ` +const ResetRequest = ` +{ + "os-reset_status": { + "status": "error" + } +} +` + +const ForceDeleteRequest = ` +{ + "os-force_delete": {} +} +` + var ( status = "available" availabilityZone = "region1b" @@ -298,3 +312,27 @@ func MockImportResponse(t *testing.T) { fmt.Fprintf(w, ImportResponse) }) } + +// MockResetStatusResponse provides mock response for reset backup status API call +func MockResetStatusResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/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, ResetRequest) + + w.WriteHeader(http.StatusAccepted) + }) +} + +// MockForceDeleteResponse provides mock response for force delete backup API call +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/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, ForceDeleteRequest) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/blockstorage/extensions/backups/testing/requests_test.go b/openstack/blockstorage/extensions/backups/testing/requests_test.go index 1adc7a4512..fe71aca125 100644 --- a/openstack/blockstorage/extensions/backups/testing/requests_test.go +++ b/openstack/blockstorage/extensions/backups/testing/requests_test.go @@ -187,3 +187,26 @@ func TestImport(t *testing.T) { th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } + +func TestResetStatus(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResetStatusResponse(t) + + opts := &backups.ResetStatusOpts{ + Status: "error", + } + res := backups.ResetStatus(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 := backups.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/extensions/backups/urls.go b/openstack/blockstorage/extensions/backups/urls.go index e727de727d..caebce82f9 100644 --- a/openstack/blockstorage/extensions/backups/urls.go +++ b/openstack/blockstorage/extensions/backups/urls.go @@ -37,3 +37,11 @@ func exportURL(c *gophercloud.ServiceClient, id string) string { func importURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("backups", "import_record") } + +func resetStatusURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "action") +} + +func forceDeleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "action") +} diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 9bbec339ec..c5b5082413 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -248,7 +248,7 @@ func (opts UpdateStatusOpts) ToSnapshotUpdateStatusMap() (map[string]interface{} return gophercloud.BuildRequestBody(opts, "os-update_snapshot_status") } -// UpdateStatus will reset the existing snapshot status. UpdateStatusResult contains only the error. +// UpdateStatus will update 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() From 18cb6a18a220b38bcfc3ce157f82ce9a36536aef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jul 2023 09:32:30 +0000 Subject: [PATCH 1630/2296] Bump kiegroup/git-backporting from 4.2.0 to 4.3.0 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/e29dae5073d5b026781931f9be00fc19d0453acb...c19a56a9ad85adf9c74df7b416ec47257f1f8e91) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 04cfcf61e8..c8de227ed1 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Backporting - uses: kiegroup/git-backporting@e29dae5073d5b026781931f9be00fc19d0453acb + uses: kiegroup/git-backporting@c19a56a9ad85adf9c74df7b416ec47257f1f8e91 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} From 92b47198a9915bc0aad025fbba53ed0ed85e3d4e Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 1 Aug 2023 10:08:50 -0400 Subject: [PATCH 1631/2296] backport-v1: use PAT token when creating the PR We need to use a Personal Access Token (PAT) when creating the backport pull request because we expect GitHub Actions workflows to be triggered when the pull request is opened. --- .github/workflows/backport_v1.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index c8de227ed1..0cf7ff93c5 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -28,6 +28,9 @@ jobs: with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} - auth: ${{ secrets.GITHUB_TOKEN }} + # We need to use a Personal Access Token (PAT) when creating the pull request because we + # expect GitHub Actions workflows to be triggered when the pull request is opened. + # BACKPORT_PR_PAT is a secret that contains that token which will expire on August 1st 2024. + auth: ${{ secrets.BACKPORT_PR_PAT }} no-squash: true strategy-option: find-renames From 235ddb2f17e9912dd9ba92edef3cec229592cd9b Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 1 Aug 2023 11:21:54 -0400 Subject: [PATCH 1632/2296] 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 8826bc8318..76f6ccc651 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,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 1562de7281b670be1edb950f0718be6838760b24 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 1 Aug 2023 17:09:04 -0400 Subject: [PATCH 1633/2296] backport: use github app instead of PAT --- .github/workflows/backport_v1.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 0cf7ff93c5..578cbfb5b0 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -23,14 +23,18 @@ jobs: ) runs-on: ubuntu-latest steps: + - name: Generate a token from the gophercloud-backport-bot github-app + id: generate_token + uses: tibdex/github-app-token@v1.8.0 + with: + app_id: ${{ secrets.BACKPORT_APP_ID }} + private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} + - name: Backporting uses: kiegroup/git-backporting@c19a56a9ad85adf9c74df7b416ec47257f1f8e91 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} - # We need to use a Personal Access Token (PAT) when creating the pull request because we - # expect GitHub Actions workflows to be triggered when the pull request is opened. - # BACKPORT_PR_PAT is a secret that contains that token which will expire on August 1st 2024. - auth: ${{ secrets.BACKPORT_PR_PAT }} + auth: ${{ steps.generate_token.outputs.token }} no-squash: true strategy-option: find-renames From 699db20f9623e6948f3c301aaac0df14b0cd7644 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 2 Dec 2022 12:41:12 +0100 Subject: [PATCH 1634/2296] 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 6222d15534b511f1dc1f041dc3ef50bc82f418a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 09:35:57 +0000 Subject: [PATCH 1635/2296] Bump golang.org/x/crypto from 0.11.0 to 0.12.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.11.0 to 0.12.0. - [Commits](https://github.com/golang/crypto/compare/v0.11.0...v0.12.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 6c8e4ea02f..74a362d33f 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud go 1.20 require ( - golang.org/x/crypto v0.11.0 + golang.org/x/crypto v0.12.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.10.0 // indirect +require golang.org/x/sys v0.11.0 // indirect diff --git a/go.sum b/go.sum index 2da03ab1ce..694bbecaf7 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= 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 b3e4b647427b912467a4c6fe86d1973a563c55a5 Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Mon, 7 Aug 2023 14:01:40 -0700 Subject: [PATCH 1636/2296] 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 8e4c755d865d5e9e52390304c5ae2260611a6a8b Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Mon, 7 Aug 2023 14:01:40 -0700 Subject: [PATCH 1637/2296] 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 6ff19a737fceadf537cd1e8174778d6c0483857e Mon Sep 17 00:00:00 2001 From: Duc Truong Date: Tue, 8 Aug 2023 12:04:08 -0700 Subject: [PATCH 1638/2296] 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 011e45a09c345e79a0194d48a4347dd42fbb9be2 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 23 Feb 2023 18:13:14 +0100 Subject: [PATCH 1639/2296] Migrate baremetal inventory to a common location With the introduction of the Ironic inventory API, these types are now shared between Ironic and Inspector. --- .../baremetal/inventory/testing/fixtures.go | 148 ++++++++++++++++ .../baremetal/inventory/testing/types_test.go | 40 +++++ openstack/baremetal/inventory/types.go | 108 ++++++++++++ .../v1/introspection/results.go | 134 ++------------- .../v1/introspection/testing/fixtures.go | 159 ++---------------- .../v1/introspection/testing/results_test.go | 21 --- 6 files changed, 322 insertions(+), 288 deletions(-) create mode 100644 openstack/baremetal/inventory/testing/fixtures.go create mode 100644 openstack/baremetal/inventory/testing/types_test.go create mode 100644 openstack/baremetal/inventory/types.go diff --git a/openstack/baremetal/inventory/testing/fixtures.go b/openstack/baremetal/inventory/testing/fixtures.go new file mode 100644 index 0000000000..c91857823c --- /dev/null +++ b/openstack/baremetal/inventory/testing/fixtures.go @@ -0,0 +1,148 @@ +package testing + +import "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" + +const InventorySample = `{ + "bmc_address": "192.167.2.134", + "boot": { + "current_boot_mode": "bios", + "pxe_interface": "52:54:00:4e:3d:30" + }, + "cpu": { + "architecture": "x86_64", + "count": 2, + "flags": [ + "fpu", + "mmx", + "fxsr", + "sse", + "sse2" + ], + "frequency": "2100.084" + }, + "disks": [ + { + "hctl": null, + "model": "", + "name": "/dev/vda", + "rotational": true, + "serial": null, + "size": 13958643712, + "vendor": "0x1af4", + "wwn": null, + "wwn_vendor_extension": null, + "wwn_with_extension": null + } + ], + "hostname": "myawesomehost", + "interfaces": [ + { + "client_id": null, + "has_carrier": true, + "ipv4_address": "172.24.42.101", + "lldp": [], + "mac_address": "52:54:00:47:20:4d", + "name": "eth1", + "product": "0x0001", + "vendor": "0x1af4" + }, + { + "client_id": null, + "has_carrier": true, + "ipv4_address": "172.24.42.100", + "lldp": [ + [ + 1, + "04112233aabbcc" + ], + [ + 5, + "737730312d646973742d31622d623132" + ] + ], + "mac_address": "52:54:00:4e:3d:30", + "name": "eth0", + "product": "0x0001", + "vendor": "0x1af4", + "speed_mbps": 1000 + } + ], + "memory": { + "physical_mb": 2048, + "total": 2105864192 + }, + "system_vendor": { + "manufacturer": "Bochs", + "product_name": "Bochs", + "serial_number": "Not Specified", + "firmware": { + "version": "1.2.3.4" + } + } +}` + +var Inventory = inventory.InventoryType{ + SystemVendor: inventory.SystemVendorType{ + Manufacturer: "Bochs", + ProductName: "Bochs", + SerialNumber: "Not Specified", + Firmware: inventory.SystemFirmwareType{ + Version: "1.2.3.4", + }, + }, + BmcAddress: "192.167.2.134", + Boot: inventory.BootInfoType{ + CurrentBootMode: "bios", + PXEInterface: "52:54:00:4e:3d:30", + }, + CPU: inventory.CPUType{ + Count: 2, + Flags: []string{"fpu", "mmx", "fxsr", "sse", "sse2"}, + Frequency: "2100.084", + Architecture: "x86_64", + }, + Disks: []inventory.RootDiskType{ + { + Rotational: true, + Model: "", + Name: "/dev/vda", + Size: 13958643712, + Vendor: "0x1af4", + }, + }, + Interfaces: []inventory.InterfaceType{ + { + Vendor: "0x1af4", + HasCarrier: true, + MACAddress: "52:54:00:47:20:4d", + Name: "eth1", + Product: "0x0001", + IPV4Address: "172.24.42.101", + LLDP: []inventory.LLDPTLVType{}, + }, + { + IPV4Address: "172.24.42.100", + MACAddress: "52:54:00:4e:3d:30", + Name: "eth0", + Product: "0x0001", + HasCarrier: true, + Vendor: "0x1af4", + LLDP: []inventory.LLDPTLVType{ + { + Type: 1, + Value: "04112233aabbcc", + }, + { + Type: 5, + Value: "737730312d646973742d31622d623132", + }, + }, + SpeedMbps: 1000, + }, + }, + Memory: inventory.MemoryType{ + PhysicalMb: 2048.0, + Total: 2.105864192e+09, + }, + Hostname: "myawesomehost", +} diff --git a/openstack/baremetal/inventory/testing/types_test.go b/openstack/baremetal/inventory/testing/types_test.go new file mode 100644 index 0000000000..4de6859b39 --- /dev/null +++ b/openstack/baremetal/inventory/testing/types_test.go @@ -0,0 +1,40 @@ +package testing + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestIntrospectionNUMA(t *testing.T) { + var output inventory.InventoryType + err := json.Unmarshal([]byte(InventorySample), &output) + if err != nil { + t.Fatalf("Failed to unmarshal inventory: %s", err) + } + + th.CheckDeepEquals(t, Inventory, output) +} + +func TestLLDPTLVErrors(t *testing.T) { + badInputs := []string{ + "[1]", + "[1, 2]", + "[\"foo\", \"bar\"]", + } + + for _, input := range badInputs { + var output inventory.LLDPTLVType + err := json.Unmarshal([]byte(input), &output) + if err == nil { + t.Fatalf("No JSON parse error for invalid LLDP TLV %s", input) + } + + if !strings.Contains(err.Error(), "LLDP TLV") { + t.Fatalf("Unexpected JSON parse error \"%s\" for invalid LLDP TLV %s", err, input) + } + } +} diff --git a/openstack/baremetal/inventory/types.go b/openstack/baremetal/inventory/types.go new file mode 100644 index 0000000000..74cfe88c80 --- /dev/null +++ b/openstack/baremetal/inventory/types.go @@ -0,0 +1,108 @@ +package inventory + +import ( + "encoding/json" + "fmt" +) + +type BootInfoType struct { + CurrentBootMode string `json:"current_boot_mode"` + PXEInterface string `json:"pxe_interface"` +} + +type CPUType struct { + Architecture string `json:"architecture"` + Count int `json:"count"` + Flags []string `json:"flags"` + Frequency string `json:"frequency"` + ModelName string `json:"model_name"` +} + +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"` + // 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 LLDPTLVType struct { + Type int + Value string +} + +type MemoryType struct { + PhysicalMb int `json:"physical_mb"` + Total int `json:"total"` +} + +type RootDiskType struct { + Hctl string `json:"hctl"` + Model string `json:"model"` + Name string `json:"name"` + ByPath string `json:"by_path"` + Rotational bool `json:"rotational"` + Serial string `json:"serial"` + Size int64 `json:"size"` + Vendor string `json:"vendor"` + Wwn string `json:"wwn"` + WwnVendorExtension string `json:"wwn_vendor_extension"` + WwnWithExtension string `json:"wwn_with_extension"` +} + +type SystemFirmwareType struct { + Version string `json:"version"` + BuildDate string `json:"build_date"` + Vendor string `json:"vendor"` +} + +type SystemVendorType struct { + Manufacturer string `json:"manufacturer"` + ProductName string `json:"product_name"` + SerialNumber string `json:"serial_number"` + Firmware SystemFirmwareType `json:"firmware"` +} + +type InventoryType struct { + BmcAddress string `json:"bmc_address"` + Boot BootInfoType `json:"boot"` + CPU CPUType `json:"cpu"` + Disks []RootDiskType `json:"disks"` + Interfaces []InterfaceType `json:"interfaces"` + Memory MemoryType `json:"memory"` + SystemVendor SystemVendorType `json:"system_vendor"` + Hostname string `json:"hostname"` +} + +// UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure +func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { + var list []interface{} + if err := json.Unmarshal(data, &list); err != nil { + return err + } + + if len(list) != 2 { + return fmt.Errorf("Invalid LLDP TLV key-value pair") + } + + fieldtype, ok := list[0].(float64) + if !ok { + return fmt.Errorf("LLDP TLV key is not number") + } + + value, ok := list[1].(string) + if !ok { + return fmt.Errorf("LLDP TLV value is not string") + } + + r.Type = int(fieldtype) + r.Value = value + return nil +} diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 2c197fa07f..ac61674e3b 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -2,10 +2,10 @@ package introspection import ( "encoding/json" - "fmt" "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" "github.com/gophercloud/gophercloud/pagination" ) @@ -162,21 +162,21 @@ type AbortResult struct { // This structure has been provided for basic compatibility but it // will need extensions type Data struct { - AllInterfaces map[string]BaseInterfaceType `json:"all_interfaces"` - BootInterface string `json:"boot_interface"` - CPUArch string `json:"cpu_arch"` - CPUs int `json:"cpus"` - Error string `json:"error"` - Interfaces map[string]BaseInterfaceType `json:"interfaces"` - Inventory InventoryType `json:"inventory"` - IPMIAddress string `json:"ipmi_address"` - LocalGB int `json:"local_gb"` - MACs []string `json:"macs"` - MemoryMB int `json:"memory_mb"` - RootDisk RootDiskType `json:"root_disk"` - Extra ExtraHardwareDataType `json:"extra"` - NUMATopology NUMATopology `json:"numa_topology"` - RawLLDP map[string][]LLDPTLVType `json:"lldp_raw"` + AllInterfaces map[string]BaseInterfaceType `json:"all_interfaces"` + BootInterface string `json:"boot_interface"` + CPUArch string `json:"cpu_arch"` + CPUs int `json:"cpus"` + Error string `json:"error"` + Interfaces map[string]BaseInterfaceType `json:"interfaces"` + Inventory inventory.InventoryType `json:"inventory"` + IPMIAddress string `json:"ipmi_address"` + LocalGB int `json:"local_gb"` + MACs []string `json:"macs"` + MemoryMB int `json:"memory_mb"` + RootDisk inventory.RootDiskType `json:"root_disk"` + Extra ExtraHardwareDataType `json:"extra"` + NUMATopology NUMATopology `json:"numa_topology"` + RawLLDP map[string][]inventory.LLDPTLVType `json:"lldp_raw"` } // Sub Types defined under Data and deeper in the structure @@ -189,82 +189,6 @@ type BaseInterfaceType struct { LLDPProcessed map[string]interface{} `json:"lldp_processed"` } -type BootInfoType struct { - CurrentBootMode string `json:"current_boot_mode"` - PXEInterface string `json:"pxe_interface"` -} - -type CPUType struct { - Architecture string `json:"architecture"` - Count int `json:"count"` - Flags []string `json:"flags"` - Frequency string `json:"frequency"` - ModelName string `json:"model_name"` -} - -type LLDPTLVType struct { - Type int - Value string -} - -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"` - // 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 { - BmcAddress string `json:"bmc_address"` - Boot BootInfoType `json:"boot"` - CPU CPUType `json:"cpu"` - Disks []RootDiskType `json:"disks"` - Interfaces []InterfaceType `json:"interfaces"` - Memory MemoryType `json:"memory"` - SystemVendor SystemVendorType `json:"system_vendor"` - Hostname string `json:"hostname"` -} - -type MemoryType struct { - PhysicalMb int `json:"physical_mb"` - Total int `json:"total"` -} - -type RootDiskType struct { - Hctl string `json:"hctl"` - Model string `json:"model"` - Name string `json:"name"` - ByPath string `json:"by_path"` - Rotational bool `json:"rotational"` - Serial string `json:"serial"` - Size int64 `json:"size"` - Vendor string `json:"vendor"` - Wwn string `json:"wwn"` - WwnVendorExtension string `json:"wwn_vendor_extension"` - WwnWithExtension string `json:"wwn_with_extension"` -} - -type SystemFirmwareType struct { - Version string `json:"version"` - BuildDate string `json:"build_date"` - Vendor string `json:"vendor"` -} - -type SystemVendorType struct { - Manufacturer string `json:"manufacturer"` - ProductName string `json:"product_name"` - SerialNumber string `json:"serial_number"` - Firmware SystemFirmwareType `json:"firmware"` -} - type ExtraHardwareData map[string]interface{} type ExtraHardwareDataSection map[string]ExtraHardwareData @@ -301,32 +225,6 @@ type NUMARAM struct { SizeKB int `json:"size_kb"` } -// UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure -func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { - var list []interface{} - if err := json.Unmarshal(data, &list); err != nil { - return err - } - - if len(list) != 2 { - return fmt.Errorf("Invalid LLDP TLV key-value pair") - } - - fieldtype, ok := list[0].(float64) - if !ok { - return fmt.Errorf("LLDP TLV key is not number") - } - - value, ok := list[1].(string) - if !ok { - return fmt.Errorf("LLDP TLV value is not string") - } - - r.Type = int(fieldtype) - r.Value = value - return nil -} - // Extract interprets any IntrospectionDataResult as IntrospectionData, if possible. func (r DataResult) Extract() (*Data, error) { var s Data diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index 142b3a7469..9b0c467abe 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -7,6 +7,8 @@ import ( "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" + inventorytesting "github.com/gophercloud/gophercloud/openstack/baremetal/inventory/testing" "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" @@ -67,7 +69,7 @@ const IntrospectionStatus = ` ` // IntrospectionDataJSONSample contains sample data reported by the introspection process. -const IntrospectionDataJSONSample = ` +var IntrospectionDataJSONSample = fmt.Sprintf(` { "all_interfaces": { "eth0": { @@ -99,84 +101,7 @@ const IntrospectionDataJSONSample = ` "pxe": true } }, - "inventory": { - "bmc_address": "192.167.2.134", - "boot": { - "current_boot_mode": "bios", - "pxe_interface": "52:54:00:4e:3d:30" - }, - "cpu": { - "architecture": "x86_64", - "count": 2, - "flags": [ - "fpu", - "mmx", - "fxsr", - "sse", - "sse2" - ], - "frequency": "2100.084" - }, - "disks": [ - { - "hctl": null, - "model": "", - "name": "/dev/vda", - "rotational": true, - "serial": null, - "size": 13958643712, - "vendor": "0x1af4", - "wwn": null, - "wwn_vendor_extension": null, - "wwn_with_extension": null - } - ], - "hostname": "myawesomehost", - "interfaces": [ - { - "client_id": null, - "has_carrier": true, - "ipv4_address": "172.24.42.101", - "lldp": [], - "mac_address": "52:54:00:47:20:4d", - "name": "eth1", - "product": "0x0001", - "vendor": "0x1af4" - }, - { - "client_id": null, - "has_carrier": true, - "ipv4_address": "172.24.42.100", - "lldp": [ - [ - 1, - "04112233aabbcc" - ], - [ - 5, - "737730312d646973742d31622d623132" - ] - ], - "mac_address": "52:54:00:4e:3d:30", - "name": "eth0", - "product": "0x0001", - "vendor": "0x1af4", - "speed_mbps": 1000 - } - ], - "memory": { - "physical_mb": 2048, - "total": 2105864192 - }, - "system_vendor": { - "manufacturer": "Bochs", - "product_name": "Bochs", - "serial_number": "Not Specified", - "firmware": { - "version": "1.2.3.4" - } - } - }, + "inventory": %s, "ipmi_address": "192.167.2.134", "lldp_raw": { "eth0": [ @@ -208,7 +133,7 @@ const IntrospectionDataJSONSample = ` "wwn_with_extension": null } } -` +`, inventorytesting.InventorySample) // IntrospectionExtraHardwareJSONSample contains extra hardware sample data // reported by the introspection process. @@ -370,7 +295,7 @@ var ( IntrospectionDataRes = introspection.Data{ CPUArch: "x86_64", MACs: []string{"52:54:00:4e:3d:30"}, - RootDisk: introspection.RootDiskType{ + RootDisk: inventory.RootDiskType{ Rotational: true, Model: "", Name: "/dev/vda", @@ -388,73 +313,9 @@ var ( BootInterface: "52:54:00:4e:3d:30", MemoryMB: 2048, IPMIAddress: "192.167.2.134", - Inventory: introspection.InventoryType{ - SystemVendor: introspection.SystemVendorType{ - Manufacturer: "Bochs", - ProductName: "Bochs", - SerialNumber: "Not Specified", - Firmware: introspection.SystemFirmwareType{ - Version: "1.2.3.4", - }, - }, - BmcAddress: "192.167.2.134", - Boot: introspection.BootInfoType{ - CurrentBootMode: "bios", - PXEInterface: "52:54:00:4e:3d:30", - }, - CPU: introspection.CPUType{ - Count: 2, - Flags: []string{"fpu", "mmx", "fxsr", "sse", "sse2"}, - Frequency: "2100.084", - Architecture: "x86_64", - }, - Disks: []introspection.RootDiskType{ - { - Rotational: true, - Model: "", - Name: "/dev/vda", - Size: 13958643712, - Vendor: "0x1af4", - }, - }, - Interfaces: []introspection.InterfaceType{ - { - Vendor: "0x1af4", - HasCarrier: true, - MACAddress: "52:54:00:47:20:4d", - Name: "eth1", - Product: "0x0001", - IPV4Address: "172.24.42.101", - LLDP: []introspection.LLDPTLVType{}, - }, - { - IPV4Address: "172.24.42.100", - MACAddress: "52:54:00:4e:3d:30", - Name: "eth0", - Product: "0x0001", - HasCarrier: true, - Vendor: "0x1af4", - LLDP: []introspection.LLDPTLVType{ - { - Type: 1, - Value: "04112233aabbcc", - }, - { - Type: 5, - Value: "737730312d646973742d31622d623132", - }, - }, - SpeedMbps: 1000, - }, - }, - Memory: introspection.MemoryType{ - PhysicalMb: 2048.0, - Total: 2.105864192e+09, - }, - Hostname: "myawesomehost", - }, - Error: "", - LocalGB: 12, + Inventory: inventorytesting.Inventory, + Error: "", + LocalGB: 12, AllInterfaces: map[string]introspection.BaseInterfaceType{ "eth1": { IP: "172.24.42.101", @@ -471,7 +332,7 @@ var ( }, }, }, - RawLLDP: map[string][]introspection.LLDPTLVType{ + RawLLDP: map[string][]inventory.LLDPTLVType{ "eth0": { { Type: 1, diff --git a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go index 259015277b..78194ea8e1 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go @@ -2,33 +2,12 @@ package testing import ( "encoding/json" - "strings" "testing" "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" th "github.com/gophercloud/gophercloud/testhelper" ) -func TestLLDPTLVErrors(t *testing.T) { - badInputs := []string{ - "[1]", - "[1, 2]", - "[\"foo\", \"bar\"]", - } - - for _, input := range badInputs { - var output introspection.LLDPTLVType - err := json.Unmarshal([]byte(input), &output) - if err == nil { - t.Fatalf("No JSON parse error for invalid LLDP TLV %s", input) - } - - if !strings.Contains(err.Error(), "LLDP TLV") { - t.Fatalf("Unexpected JSON parse error \"%s\" for invalid LLDP TLV %s", err, input) - } - } -} - func TestExtraHardware(t *testing.T) { var output introspection.ExtraHardwareDataType err := json.Unmarshal([]byte(IntrospectionExtraHardwareJSONSample), &output) From e44eb43e312afa7f8d00efe252ad7b9aa7913e2c Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Wed, 9 Aug 2023 17:16:12 +0200 Subject: [PATCH 1640/2296] baremetal: drop the deprecated LLDP field --- .../baremetal/inventory/testing/fixtures.go | 24 +------------------ openstack/baremetal/inventory/types.go | 12 ++++------ 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/openstack/baremetal/inventory/testing/fixtures.go b/openstack/baremetal/inventory/testing/fixtures.go index c91857823c..3f0a9beacd 100644 --- a/openstack/baremetal/inventory/testing/fixtures.go +++ b/openstack/baremetal/inventory/testing/fixtures.go @@ -40,7 +40,6 @@ const InventorySample = `{ "client_id": null, "has_carrier": true, "ipv4_address": "172.24.42.101", - "lldp": [], "mac_address": "52:54:00:47:20:4d", "name": "eth1", "product": "0x0001", @@ -50,16 +49,6 @@ const InventorySample = `{ "client_id": null, "has_carrier": true, "ipv4_address": "172.24.42.100", - "lldp": [ - [ - 1, - "04112233aabbcc" - ], - [ - 5, - "737730312d646973742d31622d623132" - ] - ], "mac_address": "52:54:00:4e:3d:30", "name": "eth0", "product": "0x0001", @@ -118,7 +107,6 @@ var Inventory = inventory.InventoryType{ Name: "eth1", Product: "0x0001", IPV4Address: "172.24.42.101", - LLDP: []inventory.LLDPTLVType{}, }, { IPV4Address: "172.24.42.100", @@ -127,17 +115,7 @@ var Inventory = inventory.InventoryType{ Product: "0x0001", HasCarrier: true, Vendor: "0x1af4", - LLDP: []inventory.LLDPTLVType{ - { - Type: 1, - Value: "04112233aabbcc", - }, - { - Type: 5, - Value: "737730312d646973742d31622d623132", - }, - }, - SpeedMbps: 1000, + SpeedMbps: 1000, }, }, Memory: inventory.MemoryType{ diff --git a/openstack/baremetal/inventory/types.go b/openstack/baremetal/inventory/types.go index 74cfe88c80..cb8825116c 100644 --- a/openstack/baremetal/inventory/types.go +++ b/openstack/baremetal/inventory/types.go @@ -24,13 +24,11 @@ type InterfaceType struct { 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"` + MACAddress string `json:"mac_address"` + Name string `json:"name"` + Product string `json:"product"` + SpeedMbps int `json:"speed_mbps"` + Vendor string `json:"vendor"` } type LLDPTLVType struct { From d4f6ef79ea24f39eb6399f1621ee923465d84a42 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Fri, 11 Aug 2023 16:43:18 +0200 Subject: [PATCH 1641/2296] baremetal: introduce Node Inventory API The plugin-specific part of the API gets a more sophisticated handling than it was the case for Inspector: a new opaque type PluginData is used for handling different formats. The Ironic-native plugin data format will be introduced later after some more preparation work is done. Part of #2612 --- openstack/baremetal/v1/nodes/requests.go | 9 ++++ openstack/baremetal/v1/nodes/results.go | 43 +++++++++++++++++++ .../baremetal/v1/nodes/testing/fixtures.go | 29 +++++++++++++ .../v1/nodes/testing/requests_test.go | 19 ++++++++ openstack/baremetal/v1/nodes/urls.go | 4 ++ 5 files changed, 104 insertions(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 2515bf1587..76b8b73b92 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -880,3 +880,12 @@ func UnsetMaintenance(client *gophercloud.ServiceClient, id string) (r SetMainte _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// GetInventory return stored data from successful inspection. +func GetInventory(client *gophercloud.ServiceClient, id string) (r InventoryResult) { + resp, err := client.Get(inventoryURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, 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 3756789f87..b47ead2ddb 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -1,9 +1,12 @@ package nodes import ( + "encoding/json" "time" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" + "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" "github.com/gophercloud/gophercloud/pagination" ) @@ -528,3 +531,43 @@ type SubscriptionVendorPassthru struct { type SetMaintenanceResult struct { gophercloud.ErrResult } + +// PluginData is an abstraction around plugin-specific data from inspection. +// The format of PluginData is different between ironic-inspector and the native in-band inspection in Ironic. +// We may need an opaque structure that can be extracted in two (or more) ways. +type PluginData struct { + // Raw JSON data. + json.RawMessage +} + +// Interpret plugin data as a free-form mapping. +func (pd PluginData) AsMap() (result map[string]interface{}, err error) { + err = json.Unmarshal(pd.RawMessage, &result) + return +} + +// Interpret plugin data as coming from ironic-inspector. +func (pd PluginData) AsInspectorData() (result introspection.Data, err error) { + err = json.Unmarshal(pd.RawMessage, &result) + return +} + +// InventoryData is the full node inventory. +type InventoryData struct { + // Formally specified bare metal node inventory. + Inventory inventory.InventoryType `json:"inventory"` + // Data from inspection plugins. + PluginData PluginData `json:"plugin_data"` +} + +// InventoryResult is the response from a GetInventory operation. +type InventoryResult struct { + gophercloud.Result +} + +// Extract interprets a InventoryResult as a InventoryData struct, if possible. +func (r InventoryResult) Extract() (*InventoryData, error) { + var data InventoryData + err := r.ExtractInto(&data) + return &data, err +} diff --git a/openstack/baremetal/v1/nodes/testing/fixtures.go b/openstack/baremetal/v1/nodes/testing/fixtures.go index ea9d390279..b9cb3fbc1c 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures.go @@ -6,6 +6,7 @@ import ( "testing" "time" + inventorytest "github.com/gophercloud/gophercloud/openstack/baremetal/inventory/testing" "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" th "github.com/gophercloud/gophercloud/testhelper" "github.com/gophercloud/gophercloud/testhelper/client" @@ -813,6 +814,20 @@ const NodeSetMaintenanceBody = ` } ` +var NodeInventoryBody = fmt.Sprintf(` +{ + "inventory": %s, + "plugin_data":{ + "macs":[ + "52:54:00:90:35:d6" + ], + "local_gb":10, + "cpu_arch":"x86_64", + "memory_mb":2048 + } +} +`, inventorytest.InventorySample) + 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") @@ -1173,6 +1188,10 @@ var ( EventTypes: []string{"Alert"}, Protocol: "Redfish", } + + NodeInventoryData = nodes.InventoryData{ + Inventory: inventorytest.Inventory, + } ) // HandleNodeListSuccessfully sets up the test server to respond to a server List request. @@ -1568,3 +1587,13 @@ func HandleUnsetNodeMaintenanceSuccessfully(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) } + +// HandleGetInventorySuccessfully sets up the test server to respond to a get inventory request for a node +func HandleGetInventorySuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/inventory", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, NodeInventoryBody) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index ba9e8f3ceb..bb0b6ff432 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -711,3 +711,22 @@ func TestUnsetMaintenance(t *testing.T) { err := nodes.UnsetMaintenance(c, "1234asdf").ExtractErr() th.AssertNoErr(t, err) } + +func TestGetInventory(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetInventorySuccessfully(t) + + c := client.ServiceClient() + actual, err := nodes.GetInventory(c, "1234asdf").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeInventoryData.Inventory, actual.Inventory) + + pluginData, err := actual.PluginData.AsMap() + th.AssertNoErr(t, err) + th.AssertEquals(t, "x86_64", pluginData["cpu_arch"].(string)) + + compatData, err := actual.PluginData.AsInspectorData() + th.AssertNoErr(t, err) + th.AssertEquals(t, "x86_64", compatData.CPUArch) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index ab82db932e..f2f002c333 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -77,3 +77,7 @@ func vendorPassthruCallURL(client *gophercloud.ServiceClient, id string) string func maintenanceURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "maintenance") } + +func inventoryURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "inventory") +} From 2ebec68308d1d8e6f9550100e616121c1e2c3f3a Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Mon, 14 Aug 2023 15:57:12 +0200 Subject: [PATCH 1642/2296] baremetal: finish moving common inventory bits This change concerns plugin-specific structures that are shared between ironic-inspector and the ironic's native inspection. Names have been shortened for consistency and clarity. Part of #2612 --- openstack/baremetal/inventory/plugindata.go | 73 ++++++ .../baremetal/inventory/testing/fixtures.go | 226 ++++++++++++++++++ .../inventory/testing/plugindata_test.go | 30 +++ .../baremetal/inventory/testing/types_test.go | 2 +- openstack/baremetal/inventory/types.go | 36 --- .../v1/introspection/results.go | 40 +--- .../v1/introspection/testing/fixtures.go | 24 +- .../v1/introspection/testing/results_test.go | 20 -- 8 files changed, 344 insertions(+), 107 deletions(-) create mode 100644 openstack/baremetal/inventory/plugindata.go create mode 100644 openstack/baremetal/inventory/testing/plugindata_test.go diff --git a/openstack/baremetal/inventory/plugindata.go b/openstack/baremetal/inventory/plugindata.go new file mode 100644 index 0000000000..2f4c5260b7 --- /dev/null +++ b/openstack/baremetal/inventory/plugindata.go @@ -0,0 +1,73 @@ +package inventory + +import ( + "encoding/json" + "fmt" +) + +type ExtraDataItem map[string]interface{} + +type ExtraDataSection map[string]ExtraDataItem + +type ExtraDataType struct { + CPU ExtraDataSection `json:"cpu"` + Disk ExtraDataSection `json:"disk"` + Firmware ExtraDataSection `json:"firmware"` + IPMI ExtraDataSection `json:"ipmi"` + Memory ExtraDataSection `json:"memory"` + Network ExtraDataSection `json:"network"` + System ExtraDataSection `json:"system"` +} + +type NUMATopology struct { + CPUs []NUMACPU `json:"cpus"` + NICs []NUMANIC `json:"nics"` + RAM []NUMARAM `json:"ram"` +} + +type NUMACPU struct { + CPU int `json:"cpu"` + NUMANode int `json:"numa_node"` + ThreadSiblings []int `json:"thread_siblings"` +} + +type NUMANIC struct { + Name string `json:"name"` + NUMANode int `json:"numa_node"` +} + +type NUMARAM struct { + NUMANode int `json:"numa_node"` + SizeKB int `json:"size_kb"` +} + +type LLDPTLVType struct { + Type int + Value string +} + +// UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure +func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { + var list []interface{} + if err := json.Unmarshal(data, &list); err != nil { + return err + } + + if len(list) != 2 { + return fmt.Errorf("Invalid LLDP TLV key-value pair") + } + + fieldtype, ok := list[0].(float64) + if !ok { + return fmt.Errorf("LLDP TLV key is not number") + } + + value, ok := list[1].(string) + if !ok { + return fmt.Errorf("LLDP TLV value is not string") + } + + r.Type = int(fieldtype) + r.Value = value + return nil +} diff --git a/openstack/baremetal/inventory/testing/fixtures.go b/openstack/baremetal/inventory/testing/fixtures.go index 3f0a9beacd..aeabf052cd 100644 --- a/openstack/baremetal/inventory/testing/fixtures.go +++ b/openstack/baremetal/inventory/testing/fixtures.go @@ -70,6 +70,126 @@ const InventorySample = `{ } }` +// ExtraDataJSONSample contains extra hardware sample data reported by the inspection process. +const ExtraDataJSONSample = ` +{ + "cpu": { + "logical": { + "number": 16 + }, + "physical": { + "clock": 2105032704, + "cores": 8, + "flags": "lm fpu fpu_exception wp vme de" + } + }, + "disk": { + "sda": { + "rotational": 1, + "vendor": "TEST" + } + }, + "firmware": { + "bios": { + "date": "01/01/1970", + "vendor": "test" + } + }, + "ipmi": { + "Fan1A RPM": { + "unit": "RPM", + "value": 3120 + }, + "Fan1B RPM": { + "unit": "RPM", + "value": 2280 + } + }, + "memory": { + "bank0": { + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" + }, + "bank1": { + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)" + } + }, + "network": { + "em1": { + "Autonegotiate": "on", + "loopback": "off [fixed]" + }, + "p2p1": { + "Autonegotiate": "on", + "loopback": "off [fixed]" + } + }, + "system": { + "ipmi": { + "channel": 1 + }, + "kernel": { + "arch": "x86_64", + "version": "3.10.0" + }, + "motherboard": { + "vendor": "Test" + }, + "product": { + "name": "test", + "vendor": "Test" + } + } +} +` + +// NUMADataJSONSample contains NUMA sample data reported by the inspection process. +const NUMADataJSONSample = ` +{ + "numa_topology": { + "cpus": [ + { + "cpu": 6, + "numa_node": 1, + "thread_siblings": [ + 3, + 27 + ] + }, + { + "cpu": 10, + "numa_node": 0, + "thread_siblings": [ + 20, + 44 + ] + } + ], + "nics": [ + { + "name": "p2p1", + "numa_node": 0 + }, + { + "name": "p2p2", + "numa_node": 1 + } + ], + "ram": [ + { + "numa_node": 0, + "size_kb": 99289532 + }, + { + "numa_node": 1, + "size_kb": 100663296 + } + ] + } +} +` + var Inventory = inventory.InventoryType{ SystemVendor: inventory.SystemVendorType{ Manufacturer: "Bochs", @@ -124,3 +244,109 @@ var Inventory = inventory.InventoryType{ }, Hostname: "myawesomehost", } + +var ExtraData = inventory.ExtraDataType{ + CPU: inventory.ExtraDataSection{ + "logical": map[string]interface{}{ + "number": float64(16), + }, + "physical": map[string]interface{}{ + "clock": float64(2105032704), + "cores": float64(8), + "flags": "lm fpu fpu_exception wp vme de", + }, + }, + Disk: inventory.ExtraDataSection{ + "sda": map[string]interface{}{ + "rotational": float64(1), + "vendor": "TEST", + }, + }, + Firmware: inventory.ExtraDataSection{ + "bios": map[string]interface{}{ + "date": "01/01/1970", + "vendor": "test", + }, + }, + IPMI: inventory.ExtraDataSection{ + "Fan1A RPM": map[string]interface{}{ + "unit": "RPM", + "value": float64(3120), + }, + "Fan1B RPM": map[string]interface{}{ + "unit": "RPM", + "value": float64(2280), + }, + }, + Memory: inventory.ExtraDataSection{ + "bank0": map[string]interface{}{ + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", + }, + "bank1": map[string]interface{}{ + "clock": 1600000000.0, + "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", + }, + }, + Network: inventory.ExtraDataSection{ + "em1": map[string]interface{}{ + "Autonegotiate": "on", + "loopback": "off [fixed]", + }, + "p2p1": map[string]interface{}{ + "Autonegotiate": "on", + "loopback": "off [fixed]", + }, + }, + System: inventory.ExtraDataSection{ + "ipmi": map[string]interface{}{ + "channel": float64(1), + }, + "kernel": map[string]interface{}{ + "arch": "x86_64", + "version": "3.10.0", + }, + "motherboard": map[string]interface{}{ + "vendor": "Test", + }, + "product": map[string]interface{}{ + "name": "test", + "vendor": "Test", + }, + }, +} + +var NUMATopology = inventory.NUMATopology{ + CPUs: []inventory.NUMACPU{ + { + CPU: 6, + NUMANode: 1, + ThreadSiblings: []int{3, 27}, + }, + { + CPU: 10, + NUMANode: 0, + ThreadSiblings: []int{20, 44}, + }, + }, + NICs: []inventory.NUMANIC{ + { + Name: "p2p1", + NUMANode: 0, + }, + { + Name: "p2p2", + NUMANode: 1, + }, + }, + RAM: []inventory.NUMARAM{ + { + NUMANode: 0, + SizeKB: 99289532, + }, + { + NUMANode: 1, + SizeKB: 100663296, + }, + }, +} diff --git a/openstack/baremetal/inventory/testing/plugindata_test.go b/openstack/baremetal/inventory/testing/plugindata_test.go new file mode 100644 index 0000000000..09af178320 --- /dev/null +++ b/openstack/baremetal/inventory/testing/plugindata_test.go @@ -0,0 +1,30 @@ +package testing + +import ( + "encoding/json" + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" + "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" + th "github.com/gophercloud/gophercloud/testhelper" +) + +func TestExtraHardware(t *testing.T) { + var output inventory.ExtraDataType + err := json.Unmarshal([]byte(ExtraDataJSONSample), &output) + if err != nil { + t.Fatalf("Failed to unmarshal ExtraHardware data: %s", err) + } + + th.CheckDeepEquals(t, ExtraData, output) +} + +func TestIntrospectionNUMA(t *testing.T) { + var output introspection.Data + err := json.Unmarshal([]byte(NUMADataJSONSample), &output) + if err != nil { + t.Fatalf("Failed to unmarshal NUMA Data: %s", err) + } + + th.CheckDeepEquals(t, NUMATopology, output.NUMATopology) +} diff --git a/openstack/baremetal/inventory/testing/types_test.go b/openstack/baremetal/inventory/testing/types_test.go index 4de6859b39..f8a7572581 100644 --- a/openstack/baremetal/inventory/testing/types_test.go +++ b/openstack/baremetal/inventory/testing/types_test.go @@ -9,7 +9,7 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestIntrospectionNUMA(t *testing.T) { +func TestInventory(t *testing.T) { var output inventory.InventoryType err := json.Unmarshal([]byte(InventorySample), &output) if err != nil { diff --git a/openstack/baremetal/inventory/types.go b/openstack/baremetal/inventory/types.go index cb8825116c..468f32a74f 100644 --- a/openstack/baremetal/inventory/types.go +++ b/openstack/baremetal/inventory/types.go @@ -1,10 +1,5 @@ package inventory -import ( - "encoding/json" - "fmt" -) - type BootInfoType struct { CurrentBootMode string `json:"current_boot_mode"` PXEInterface string `json:"pxe_interface"` @@ -31,11 +26,6 @@ type InterfaceType struct { Vendor string `json:"vendor"` } -type LLDPTLVType struct { - Type int - Value string -} - type MemoryType struct { PhysicalMb int `json:"physical_mb"` Total int `json:"total"` @@ -78,29 +68,3 @@ type InventoryType struct { SystemVendor SystemVendorType `json:"system_vendor"` Hostname string `json:"hostname"` } - -// UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure -func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { - var list []interface{} - if err := json.Unmarshal(data, &list); err != nil { - return err - } - - if len(list) != 2 { - return fmt.Errorf("Invalid LLDP TLV key-value pair") - } - - fieldtype, ok := list[0].(float64) - if !ok { - return fmt.Errorf("LLDP TLV key is not number") - } - - value, ok := list[1].(string) - if !ok { - return fmt.Errorf("LLDP TLV value is not string") - } - - r.Type = int(fieldtype) - r.Value = value - return nil -} diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index ac61674e3b..2643dff9ea 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -174,8 +174,8 @@ type Data struct { MACs []string `json:"macs"` MemoryMB int `json:"memory_mb"` RootDisk inventory.RootDiskType `json:"root_disk"` - Extra ExtraHardwareDataType `json:"extra"` - NUMATopology NUMATopology `json:"numa_topology"` + Extra inventory.ExtraDataType `json:"extra"` + NUMATopology inventory.NUMATopology `json:"numa_topology"` RawLLDP map[string][]inventory.LLDPTLVType `json:"lldp_raw"` } @@ -189,42 +189,6 @@ type BaseInterfaceType struct { LLDPProcessed map[string]interface{} `json:"lldp_processed"` } -type ExtraHardwareData map[string]interface{} - -type ExtraHardwareDataSection map[string]ExtraHardwareData - -type ExtraHardwareDataType struct { - CPU ExtraHardwareDataSection `json:"cpu"` - Disk ExtraHardwareDataSection `json:"disk"` - Firmware ExtraHardwareDataSection `json:"firmware"` - IPMI ExtraHardwareDataSection `json:"ipmi"` - Memory ExtraHardwareDataSection `json:"memory"` - Network ExtraHardwareDataSection `json:"network"` - System ExtraHardwareDataSection `json:"system"` -} - -type NUMATopology struct { - CPUs []NUMACPU `json:"cpus"` - NICs []NUMANIC `json:"nics"` - RAM []NUMARAM `json:"ram"` -} - -type NUMACPU struct { - CPU int `json:"cpu"` - NUMANode int `json:"numa_node"` - ThreadSiblings []int `json:"thread_siblings"` -} - -type NUMANIC struct { - Name string `json:"name"` - NUMANode int `json:"numa_node"` -} - -type NUMARAM struct { - NUMANode int `json:"numa_node"` - SizeKB int `json:"size_kb"` -} - // Extract interprets any IntrospectionDataResult as IntrospectionData, if possible. func (r DataResult) Extract() (*Data, error) { var s Data diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index 9b0c467abe..d60cb374cf 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -346,8 +346,8 @@ var ( }, } - IntrospectionExtraHardware = introspection.ExtraHardwareDataType{ - CPU: introspection.ExtraHardwareDataSection{ + IntrospectionExtraHardware = inventory.ExtraDataType{ + CPU: inventory.ExtraDataSection{ "logical": map[string]interface{}{ "number": float64(16), }, @@ -357,19 +357,19 @@ var ( "flags": "lm fpu fpu_exception wp vme de", }, }, - Disk: introspection.ExtraHardwareDataSection{ + Disk: inventory.ExtraDataSection{ "sda": map[string]interface{}{ "rotational": float64(1), "vendor": "TEST", }, }, - Firmware: introspection.ExtraHardwareDataSection{ + Firmware: inventory.ExtraDataSection{ "bios": map[string]interface{}{ "date": "01/01/1970", "vendor": "test", }, }, - IPMI: introspection.ExtraHardwareDataSection{ + IPMI: inventory.ExtraDataSection{ "Fan1A RPM": map[string]interface{}{ "unit": "RPM", "value": float64(3120), @@ -379,7 +379,7 @@ var ( "value": float64(2280), }, }, - Memory: introspection.ExtraHardwareDataSection{ + Memory: inventory.ExtraDataSection{ "bank0": map[string]interface{}{ "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", @@ -389,7 +389,7 @@ var ( "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", }, }, - Network: introspection.ExtraHardwareDataSection{ + Network: inventory.ExtraDataSection{ "em1": map[string]interface{}{ "Autonegotiate": "on", "loopback": "off [fixed]", @@ -399,7 +399,7 @@ var ( "loopback": "off [fixed]", }, }, - System: introspection.ExtraHardwareDataSection{ + System: inventory.ExtraDataSection{ "ipmi": map[string]interface{}{ "channel": float64(1), }, @@ -417,8 +417,8 @@ var ( }, } - IntrospectionNUMA = introspection.NUMATopology{ - CPUs: []introspection.NUMACPU{ + IntrospectionNUMA = inventory.NUMATopology{ + CPUs: []inventory.NUMACPU{ { CPU: 6, NUMANode: 1, @@ -430,7 +430,7 @@ var ( ThreadSiblings: []int{20, 44}, }, }, - NICs: []introspection.NUMANIC{ + NICs: []inventory.NUMANIC{ { Name: "p2p1", NUMANode: 0, @@ -440,7 +440,7 @@ var ( NUMANode: 1, }, }, - RAM: []introspection.NUMARAM{ + RAM: []inventory.NUMARAM{ { NUMANode: 0, SizeKB: 99289532, diff --git a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go index 78194ea8e1..df59fa2d94 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go @@ -8,26 +8,6 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestExtraHardware(t *testing.T) { - var output introspection.ExtraHardwareDataType - err := json.Unmarshal([]byte(IntrospectionExtraHardwareJSONSample), &output) - if err != nil { - t.Fatalf("Failed to unmarshal ExtraHardware data: %s", err) - } - - th.CheckDeepEquals(t, IntrospectionExtraHardware, output) -} - -func TestIntrospectionNUMA(t *testing.T) { - var output introspection.Data - err := json.Unmarshal([]byte(IntrospectionNUMADataJSONSample), &output) - if err != nil { - t.Fatalf("Failed to unmarshal NUMA Data: %s", err) - } - - th.CheckDeepEquals(t, IntrospectionNUMA, output.NUMATopology) -} - func TestHostnameInInventory(t *testing.T) { var output introspection.Data err := json.Unmarshal([]byte(IntrospectionDataJSONSample), &output) From 7b019a7dd279c475111e3568bba22b6102548839 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 09:23:40 +0000 Subject: [PATCH 1643/2296] Bump kiegroup/git-backporting from 4.3.0 to 4.4.0 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.3.0 to 4.4.0. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/c19a56a9ad85adf9c74df7b416ec47257f1f8e91...4313be48e73b299a20b3c8290fd1ea8704e8dd5e) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 578cbfb5b0..ec240c57ee 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -31,7 +31,7 @@ jobs: private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} - name: Backporting - uses: kiegroup/git-backporting@c19a56a9ad85adf9c74df7b416ec47257f1f8e91 + uses: kiegroup/git-backporting@4313be48e73b299a20b3c8290fd1ea8704e8dd5e with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} From e767be1201e8bc611534fb55233da0ca680e41db Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 21 Aug 2023 13:16:21 -0400 Subject: [PATCH 1644/2296] 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 a934f0107b0649d6fee1e04be7ad4585080ee2e8 Mon Sep 17 00:00:00 2001 From: Maxime Delord Date: Fri, 7 Oct 2022 13:17:03 +0000 Subject: [PATCH 1645/2296] Fix BareMetalV1 version --- openstack/client.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openstack/client.go b/openstack/client.go index 81c907c35b..87cd90f38e 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(strings.TrimSuffix(sc.Endpoint, "/"), "v1") { + sc.ResourceBase = sc.Endpoint + "v1/" + } + return sc, err } // NewBareMetalIntrospectionV1 creates a ServiceClient that may be used with the v1 From 00da6f70afacb50378ce7cc0d6413359aa98899a Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 18 Nov 2022 09:50:53 -0500 Subject: [PATCH 1646/2296] 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. --- .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 86e726d1a1072a2ebd10ce692578efac3bdb2643 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 1647/2296] 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] --- .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 8a032b26f39817e01dd42d1e3b3de49685872bd2 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 1648/2296] 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] --- .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 a5fbf1cee6..a6bdfb2530 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 8d7fc3c717d0566dcb64a0eb4f0c5c7f07a98b7b Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 29 Aug 2023 10:23:31 -0400 Subject: [PATCH 1649/2296] 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 dd968c9a31e3b0b9b9ccec28215191605a3527eb Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 30 Aug 2023 11:53:15 +0200 Subject: [PATCH 1650/2296] all: Replace deprecated ioutil All functions in `ioutil` have been moved to either `io` or `os`. --- acceptance/clients/http.go | 5 ++--- acceptance/openstack/objectstorage/v1/objects_test.go | 6 +++--- openstack/identity/v3/extensions/oauth1/requests.go | 6 +++--- openstack/imageservice/v2/imagedata/doc.go | 2 +- openstack/imageservice/v2/imagedata/testing/fixtures.go | 6 +++--- .../imageservice/v2/imagedata/testing/requests_test.go | 3 +-- openstack/keymanager/v1/secrets/results.go | 3 +-- openstack/objectstorage/v1/objects/requests.go | 3 +-- openstack/objectstorage/v1/objects/results.go | 3 +-- .../objectstorage/v1/objects/testing/requests_test.go | 5 ++--- openstack/orchestration/v1/stacks/doc.go | 6 +++--- openstack/orchestration/v1/stacks/utils.go | 4 ++-- openstack/orchestration/v1/stacktemplates/doc.go | 2 +- pagination/http.go | 4 ++-- provider_client.go | 7 +++---- testhelper/http_responses.go | 6 +++--- testing/provider_client_test.go | 8 ++++---- 17 files changed, 36 insertions(+), 43 deletions(-) diff --git a/acceptance/clients/http.go b/acceptance/clients/http.go index c8d266f89d..1af2bea6b8 100644 --- a/acceptance/clients/http.go +++ b/acceptance/clients/http.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "log" "net/http" "sort" @@ -75,7 +74,7 @@ func (lrt *LogRoundTripper) logRequest(original io.ReadCloser, contentType strin log.Printf("[DEBUG] OpenStack Request Body: %s", debugInfo) } - return ioutil.NopCloser(strings.NewReader(bs.String())), nil + return io.NopCloser(strings.NewReader(bs.String())), nil } // logResponse will log the HTTP Response details. @@ -92,7 +91,7 @@ func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, contentType stri if debugInfo != "" { log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo) } - return ioutil.NopCloser(strings.NewReader(bs.String())), nil + return io.NopCloser(strings.NewReader(bs.String())), nil } log.Printf("[DEBUG] Not logging because OpenStack response body isn't JSON") diff --git a/acceptance/openstack/objectstorage/v1/objects_test.go b/acceptance/openstack/objectstorage/v1/objects_test.go index 0569428f3d..67fde0004e 100644 --- a/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/acceptance/openstack/objectstorage/v1/objects_test.go @@ -5,7 +5,7 @@ package v1 import ( "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -106,7 +106,7 @@ func TestObjects(t *testing.T) { th.AssertNoErr(t, fmt.Errorf("unexpected response code: %d", resp.StatusCode)) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) th.AssertNoErr(t, err) th.AssertDeepEquals(t, oContents[i], string(body)) resp.Body.Close() @@ -127,7 +127,7 @@ func TestObjects(t *testing.T) { th.AssertNoErr(t, fmt.Errorf("unexpected response code: %d", resp.StatusCode)) } - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) th.AssertNoErr(t, err) th.AssertDeepEquals(t, oContents[i], string(body)) resp.Body.Close() diff --git a/openstack/identity/v3/extensions/oauth1/requests.go b/openstack/identity/v3/extensions/oauth1/requests.go index 028b5a45bd..445803af52 100644 --- a/openstack/identity/v3/extensions/oauth1/requests.go +++ b/openstack/identity/v3/extensions/oauth1/requests.go @@ -5,7 +5,7 @@ import ( "crypto/sha1" "encoding/base64" "fmt" - "io/ioutil" + "io" "math/rand" "net/url" "sort" @@ -319,7 +319,7 @@ func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilde r.Err = fmt.Errorf("unsupported Content-Type: %q", v) return } - r.Body, r.Err = ioutil.ReadAll(resp.Body) + r.Body, r.Err = io.ReadAll(resp.Body) return } @@ -447,7 +447,7 @@ func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessToken r.Err = fmt.Errorf("unsupported Content-Type: %q", v) return } - r.Body, r.Err = ioutil.ReadAll(resp.Body) + r.Body, r.Err = io.ReadAll(resp.Body) return } diff --git a/openstack/imageservice/v2/imagedata/doc.go b/openstack/imageservice/v2/imagedata/doc.go index 20a5108396..bdb6583400 100644 --- a/openstack/imageservice/v2/imagedata/doc.go +++ b/openstack/imageservice/v2/imagedata/doc.go @@ -43,7 +43,7 @@ Example to Download Image Data // close the reader, when reading has finished defer image.Close() - imageData, err := ioutil.ReadAll(image) + imageData, err := io.ReadAll(image) if err != nil { panic(err) } diff --git a/openstack/imageservice/v2/imagedata/testing/fixtures.go b/openstack/imageservice/v2/imagedata/testing/fixtures.go index 64c44bdf6f..408b146912 100644 --- a/openstack/imageservice/v2/imagedata/testing/fixtures.go +++ b/openstack/imageservice/v2/imagedata/testing/fixtures.go @@ -1,7 +1,7 @@ package testing import ( - "io/ioutil" + "io" "net/http" "testing" @@ -15,7 +15,7 @@ func HandlePutImageDataSuccessfully(t *testing.T) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) if err != nil { t.Errorf("Unable to read request body: %v", err) } @@ -32,7 +32,7 @@ func HandleStageImageDataSuccessfully(t *testing.T) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) if err != nil { t.Errorf("Unable to read request body: %v", err) } diff --git a/openstack/imageservice/v2/imagedata/testing/requests_test.go b/openstack/imageservice/v2/imagedata/testing/requests_test.go index d141cf4cdf..af06256571 100644 --- a/openstack/imageservice/v2/imagedata/testing/requests_test.go +++ b/openstack/imageservice/v2/imagedata/testing/requests_test.go @@ -3,7 +3,6 @@ package testing import ( "fmt" "io" - "io/ioutil" "testing" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata" @@ -96,7 +95,7 @@ func TestDownload(t *testing.T) { defer rdr.Close() - bs, err := ioutil.ReadAll(rdr) + bs, err := io.ReadAll(rdr) th.AssertNoErr(t, err) th.AssertByteArrayEquals(t, []byte{34, 87, 0, 23, 23, 23, 56, 255, 254, 0}, bs) diff --git a/openstack/keymanager/v1/secrets/results.go b/openstack/keymanager/v1/secrets/results.go index f76b977520..83814ef875 100644 --- a/openstack/keymanager/v1/secrets/results.go +++ b/openstack/keymanager/v1/secrets/results.go @@ -3,7 +3,6 @@ package secrets import ( "encoding/json" "io" - "io/ioutil" "time" "github.com/gophercloud/gophercloud" @@ -122,7 +121,7 @@ func (r PayloadResult) Extract() ([]byte, error) { return nil, r.Err } defer r.Body.Close() - body, err := ioutil.ReadAll(r.Body) + body, err := io.ReadAll(r.Body) if err != nil { return nil, err } diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index c03a27fce5..ffad4624da 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -10,7 +10,6 @@ import ( "fmt" "hash" "io" - "io/ioutil" "strings" "time" @@ -232,7 +231,7 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str // file content into memory first. readSeeker, isReadSeeker := opts.Content.(io.ReadSeeker) if !isReadSeeker { - data, err := ioutil.ReadAll(opts.Content) + data, err := io.ReadAll(opts.Content) if err != nil { return nil, nil, "", err } diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 6d8d5d304c..5eccbde4ac 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/url" "strings" "time" @@ -212,7 +211,7 @@ func (r *DownloadResult) ExtractContent() ([]byte, error) { return nil, r.Err } defer r.Body.Close() - body, err := ioutil.ReadAll(r.Body) + body, err := io.ReadAll(r.Body) if err != nil { return nil, err } diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 11e2235f8a..9161d2956e 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -5,7 +5,6 @@ import ( "crypto/md5" "fmt" "io" - "io/ioutil" "net/http" "strings" "testing" @@ -464,7 +463,7 @@ func TestObjectCreateParamsWithoutSeek(t *testing.T) { _, ok := reader.(io.ReadSeeker) th.AssertEquals(t, true, ok) - c, err := ioutil.ReadAll(reader) + c, err := io.ReadAll(reader) th.AssertNoErr(t, err) th.AssertEquals(t, content, string(c)) @@ -483,7 +482,7 @@ func TestObjectCreateParamsWithSeek(t *testing.T) { _, ok := reader.(io.ReadSeeker) th.AssertEquals(t, ok, true) - c, err := ioutil.ReadAll(reader) + c, err := io.ReadAll(reader) th.AssertNoErr(t, err) th.AssertEquals(t, content, string(c)) diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index 78bfa8d7b9..6f49bd716c 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -42,7 +42,7 @@ Example to Create an Stack // Create Template t := make(map[string]interface{}) - f, err := ioutil.ReadFile("template.yaml") + f, err := os.ReadFile("template.yaml") if err != nil { panic(err) } @@ -57,7 +57,7 @@ Example to Create an Stack } // Create Environment if needed t_env := make(map[string]interface{}) - f_env, err := ioutil.ReadFile("env.yaml") + f_env, err := os.ReadFile("env.yaml") if err != nil { panic(err) } @@ -155,7 +155,7 @@ raw_template value. Example to Update a Stack Using the Update (PUT) Method t := make(map[string]interface{}) - f, err := ioutil.ReadFile("template.yaml") + f, err := os.ReadFile("template.yaml") if err != nil { panic(err) } diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go index bf0831376d..470c08f1c5 100644 --- a/openstack/orchestration/v1/stacks/utils.go +++ b/openstack/orchestration/v1/stacks/utils.go @@ -3,7 +3,7 @@ package stacks import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "path/filepath" "reflect" @@ -77,7 +77,7 @@ func (t *TE) Fetch() error { return err } defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return err } diff --git a/openstack/orchestration/v1/stacktemplates/doc.go b/openstack/orchestration/v1/stacktemplates/doc.go index 76d6475e71..c08ea07da5 100644 --- a/openstack/orchestration/v1/stacktemplates/doc.go +++ b/openstack/orchestration/v1/stacktemplates/doc.go @@ -18,7 +18,7 @@ Example to get stack template Example to validate stack template - f2, err := ioutil.ReadFile("template.err.yaml") + f2, err := os.ReadFile("template.err.yaml") if err != nil { panic(err) } diff --git a/pagination/http.go b/pagination/http.go index 7845cda13b..18b1ff438c 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -2,7 +2,7 @@ package pagination import ( "encoding/json" - "io/ioutil" + "io" "net/http" "net/url" "strings" @@ -22,7 +22,7 @@ func PageResultFrom(resp *http.Response) (PageResult, error) { var parsedBody interface{} defer resp.Body.Close() - rawBody, err := ioutil.ReadAll(resp.Body) + rawBody, err := io.ReadAll(resp.Body) if err != nil { return PageResult{}, err } diff --git a/provider_client.go b/provider_client.go index 6cfb14fd7e..9238ecbb73 100644 --- a/provider_client.go +++ b/provider_client.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "io" - "io/ioutil" "net/http" "strings" "sync" @@ -458,7 +457,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts } if !ok { - body, _ := ioutil.ReadAll(resp.Body) + body, _ := io.ReadAll(resp.Body) resp.Body.Close() respErr := ErrUnexpectedResponseCode{ URL: url, @@ -604,7 +603,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts // Don't decode JSON when there is no content if resp.StatusCode == http.StatusNoContent { // read till EOF, otherwise the connection will be closed and cannot be reused - _, err = io.Copy(ioutil.Discard, resp.Body) + _, err = io.Copy(io.Discard, resp.Body) return resp, err } if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { @@ -626,7 +625,7 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts if !options.KeepResponseBody && options.JSONResponse == nil { defer resp.Body.Close() // read till EOF, otherwise the connection will be closed and cannot be reused - if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { + if _, err := io.Copy(io.Discard, resp.Body); err != nil { return nil, err } } diff --git a/testhelper/http_responses.go b/testhelper/http_responses.go index 2f20957fe9..366625d849 100644 --- a/testhelper/http_responses.go +++ b/testhelper/http_responses.go @@ -3,7 +3,7 @@ package testhelper import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net" "net/http" "net/http/httptest" @@ -90,7 +90,7 @@ func TestHeaderUnset(t *testing.T, r *http.Request, header string) { // TestBody verifies that the request body matches an expected body. func TestBody(t *testing.T, r *http.Request, expected string) { - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) if err != nil { t.Errorf("Unable to read body: %v", err) } @@ -103,7 +103,7 @@ func TestBody(t *testing.T, r *http.Request, expected string) { // TestJSONRequest verifies that the JSON payload of a request matches an expected structure, without asserting things about // whitespace or ordering. func TestJSONRequest(t *testing.T, r *http.Request, expected string) { - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) if err != nil { t.Errorf("Unable to read request body: %v", err) } diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index f460bedae7..05a0c50f64 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -3,7 +3,7 @@ package testing import ( "context" "fmt" - "io/ioutil" + "io" "net" "net/http" "net/http/httptest" @@ -123,7 +123,7 @@ func TestConcurrentReauth(t *testing.T) { return } defer resp.Body.Close() - actual, err := ioutil.ReadAll(resp.Body) + actual, err := io.ReadAll(resp.Body) if err != nil { t.Errorf("error reading response body: %s", err) return @@ -304,7 +304,7 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { return } defer resp.Body.Close() - actual, err := ioutil.ReadAll(resp.Body) + actual, err := io.ReadAll(resp.Body) if err != nil { t.Errorf("error reading response body: %s", err) return @@ -379,7 +379,7 @@ func TestRequestWithContext(t *testing.T) { res, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) th.AssertNoErr(t, err) - _, err = ioutil.ReadAll(res.Body) + _, err = io.ReadAll(res.Body) th.AssertNoErr(t, err) err = res.Body.Close() th.AssertNoErr(t, err) From 7229f298813a3b022404a062a0f893d618f6c29e Mon Sep 17 00:00:00 2001 From: David Verbeiren Date: Tue, 29 Aug 2023 17:43:00 +0200 Subject: [PATCH 1651/2296] orchestration: test for template composition Add a test that uses a template which itself uses another template, whith templates in different directories so we test the fact that template references are supposed to be relative to the template in which a child template is used (without this, templates wouldn't be composable). Signed-off-by: David Verbeiren --- .../orchestration/v1/stacks/template_test.go | 93 ++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go index 62419e5d72..96a061e579 100644 --- a/openstack/orchestration/v1/stacks/template_test.go +++ b/openstack/orchestration/v1/stacks/template_test.go @@ -143,7 +143,7 @@ resources: }, }, } - te.Parse() + th.AssertNoErr(t, te.Parse()) th.AssertDeepEquals(t, expectedParsed, te.Parsed) } @@ -195,6 +195,95 @@ resources: }, }, } - te.Parse() + th.AssertNoErr(t, te.Parse()) + th.AssertDeepEquals(t, expectedParsed, te.Parsed) +} + +func TestGetFileContentsComposeRelativePath(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + baseurl, err := getBasePath() + th.AssertNoErr(t, err) + + novaPath := strings.Join([]string{"templates", "my_nova.yaml"}, "/") + novaURL := strings.Join([]string{baseurl, novaPath}, "/") + novaURLParse, err := url.Parse(novaURL) + th.AssertNoErr(t, err) + myNovaContent := `heat_template_version: 2014-10-16 +parameters: + flavor: + type: string + description: Flavor for the server to be created + default: 4353 + hidden: true +resources: + test_server: + type: "OS::Nova::Server" + properties: + name: test-server + flavor: 2 GB General Purpose v1 + image: Debian 7 (Wheezy) (PVHVM) + networks: + - {uuid: 11111111-1111-1111-1111-111111111111}` + th.Mux.HandleFunc(novaURLParse.Path, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, myNovaContent) + }) + + subStacksPath := strings.Join([]string{"substacks", "my_substack.yaml"}, "/") + subStackURL := strings.Join([]string{baseurl, subStacksPath}, "/") + subStackURLParsed, err := url.Parse(subStackURL) + th.AssertNoErr(t, err) + mySubStackContentFmt := `heat_template_version: 2015-04-30 +resources: + my_server: + type: %s + my_backend: + type: "OS::Nova::Server" + properties: + name: test-backend + flavor: 4 GB General Purpose v1 + image: Debian 7 (Wheezy) (PVHVM) + networks: + - {uuid: 11111111-1111-1111-1111-111111111111}` + th.Mux.HandleFunc(subStackURLParsed.Path, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, mySubStackContentFmt, "../templates/my_nova.yaml") + }) + + client := fakeClient{BaseClient: getHTTPClient()} + te := new(Template) + te.Bin = []byte(`heat_template_version: 2015-04-30 +resources: + my_stack: + type: substacks/my_substack.yaml`) + te.client = client + + err = te.Parse() + th.AssertNoErr(t, err) + err = te.getFileContents(te.Parsed, ignoreIfTemplate, true) + th.AssertNoErr(t, err) + + expectedFiles := map[string]string{ + "templates/my_nova.yaml": myNovaContent, + "substacks/my_substack.yaml": fmt.Sprintf(mySubStackContentFmt, novaURLParse), + } + th.AssertEquals(t, expectedFiles[novaPath], te.Files[novaURL]) + th.AssertEquals(t, expectedFiles[subStacksPath], te.Files[subStackURL]) + + te.fixFileRefs() + expectedParsed := map[string]interface{}{ + "heat_template_version": "2015-04-30", + "resources": map[string]interface{}{ + "my_stack": map[string]interface{}{ + "type": subStackURL, + }, + }, + } + th.AssertNoErr(t, te.Parse()) th.AssertDeepEquals(t, expectedParsed, te.Parsed) } From 8ec15c23bcb26736b02345c04acb332e1d47dd79 Mon Sep 17 00:00:00 2001 From: David Verbeiren Date: Tue, 29 Aug 2023 12:53:41 +0200 Subject: [PATCH 1652/2296] orchestration: support stack template composition In order for composable stack templates to work, the following changes are needed: * a child template URL must be interpreted as relative to its parent template, not to the base URL * the child templates and 'get_file' of child templates must also be included in the stack create request * the references within child templates (to other templates or files) must be resolved so the correct URLs are provided, matching the files provided in the OS stack create request, just like is done for the top-level template. Signed-off-by: David Verbeiren --- openstack/orchestration/v1/stacks/template.go | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index 90f205a1f2..128f4f2369 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -2,6 +2,7 @@ package stacks import ( "fmt" + "path/filepath" "reflect" "strings" @@ -67,7 +68,8 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool return err } } else if !ignoreIf(k, value) { - // at this point, the k, v pair has a reference to an external template. + // at this point, the k, v pair has a reference to an external template + // or file (for 'get_file' function). // The assumption of heatclient is that value v is a reference // to a file in the users environment @@ -76,15 +78,15 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool // initialize child template - // get the base location of the child template - baseURL, err := gophercloud.NormalizePathURL(t.baseURL, value) - if err != nil { - return err + // get the base location of the child template. Child path is relative + // to its parent location so that templates can be composed + if t.URL != "" { + childTemplate.baseURL = filepath.Dir(t.URL) } - childTemplate.baseURL = baseURL + childTemplate.URL = value childTemplate.client = t.client - // fetch the contents of the child template + // fetch the contents of the child template or file if err := childTemplate.Fetch(); err != nil { return err } @@ -98,14 +100,20 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil { return err } + childTemplate.fixFileRefs() } } } + // update parent template with current child templates' content. // At this point, the child template has been parsed recursively. t.fileMaps[value] = childTemplate.URL t.Files[childTemplate.URL] = string(childTemplate.Bin) + // Also add child templates' own children (templates or get_file)! + for k, v := range childTemplate.Files { + t.Files[k] = v + } } } return nil From 3e0071fcd67b3fb055c7634e1191a704dae8a56e Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 30 Aug 2023 09:13:38 -0400 Subject: [PATCH 1653/2296] 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`. --- .github/labels.yaml | 3 +++ .github/workflows/semver-auto.yaml | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/labels.yaml b/.github/labels.yaml index f38c346352..8a908009e2 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -19,3 +19,6 @@ - color: '6E7624' description: No API change name: semver:patch +- color: 'EC0101' + description: Unable to figure out the semver type + name: semver:unknown 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 9e9f560bd9c8534412f1ac6f0585f172a7f6a3f9 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 1 Sep 2023 10:31:11 -0400 Subject: [PATCH 1654/2296] CI: Fix a typo in semver-auto A caracter is missing for the rebase action in git ref. --- .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 8534afd5fbdd37c6822f582fc89737caeaa1aad9 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 1 Sep 2023 11:08:36 -0400 Subject: [PATCH 1655/2296] 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. --- .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 510ded837d5ce25911d57dc5a68cdf302e127c6a Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 1 Sep 2023 11:18:31 -0400 Subject: [PATCH 1656/2296] 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. --- .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 e1c770189c..ae86b7ce9b 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -12,9 +12,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 d43efef34d48ae51f13fdf199ba8e2f225d95752 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:51:48 +0000 Subject: [PATCH 1657/2296] build(deps): bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [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/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/ensure-labels.yaml | 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 +- .github/workflows/gomod.yml | 2 +- .github/workflows/reauth-retests.yaml | 2 +- .github/workflows/semver-auto.yaml | 2 +- .github/workflows/unit.yml | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 38cf2bfb08..496e65efcc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index a6aa52aea8..4528512c0f 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -10,7 +10,7 @@ jobs: ensure: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: micnncim/action-label-syncer@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index bcd20ae5d7..e6efa7c387 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 2b3cfde178..2f67d17e85 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -36,7 +36,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with defaults and run basic acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 0ce1dfece9..9de345406f 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Cinder and run blockstorage acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index ec1a7a96d3..8ee0bc0273 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Senlin and run clustering acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index fcff786845..bb651389dd 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Nova and run compute acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 3baddab8fe..aa81c5b575 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -45,7 +45,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index aff6aa7a52..643e3bd519 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -46,7 +46,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index a676338fd1..f40ca188b4 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -24,7 +24,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with enabled FWaaS_v2 and run networking acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 7bc1bae450..755c5ec8a8 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Keystone and run identity acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 4484012cde..4717105fa2 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Glance and run imageservice acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 8a38805af4..5a15920cca 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Barbican and run keymanager acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 72b585ac2c..d2554f24e1 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Octavia and run loadbalancer acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index b84f97027f..8f78f47e42 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Zaqar and run messaging acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index d10bd89004..956610a101 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -51,7 +51,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 2bdcdec14e..bd31da8a5b 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Swift and run objectstorage acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 2def92f86b..84226ab92d 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Heat and run orchestration acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 806a1cae87..6df79ffa5b 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Placement and run placement acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index d6332d28a2..b5cf9e9d10 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -33,7 +33,7 @@ jobs: name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests steps: - name: Checkout Gophercloud - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml index 929bad77e9..8a17b797b3 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@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: '1' diff --git a/.github/workflows/reauth-retests.yaml b/.github/workflows/reauth-retests.yaml index ae1aa17994..0e18ca15ea 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@v3 + - uses: actions/checkout@v4 - name: Run reauth retests run: | diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index 0cd6080bc5..da49504026 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -16,7 +16,7 @@ jobs: semver:unknown github_token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 9d5272d781..24b69909f1 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@v3 + - uses: actions/checkout@v4 - name: Setup environment run: | From 255951df74eb4c73d55a2e1ed9a024de7aceb91e Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 1 Sep 2023 10:21:20 -0400 Subject: [PATCH 1658/2296] workflow/backport_v1: use verified getsentry/action-github-app-token --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index ec240c57ee..978eac9487 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -25,7 +25,7 @@ jobs: steps: - name: Generate a token from the gophercloud-backport-bot github-app id: generate_token - uses: tibdex/github-app-token@v1.8.0 + uses: getsentry/action-github-app-token@97c9e23528286821f97fba885c1b1123284b29cc with: app_id: ${{ secrets.BACKPORT_APP_ID }} private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} From 13358932e257c194233e0e4865b3860b985203f9 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 5 Sep 2023 10:53:02 -0400 Subject: [PATCH 1659/2296] 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. --- .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 4abe682d402735186fed3f084d7289655f59824b Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 5 Sep 2023 22:06:24 -0400 Subject: [PATCH 1660/2296] ci/functional: add `antelope` release in the matrix --- .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 e6efa7c387..742973e3dd 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: "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 2f67d17e85..6ee3eef505 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: "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 9de345406f..0451154f98 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: "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 8ee0bc0273..79b5039bef 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -14,6 +14,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 bb651389dd..4c1bfb829f 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: "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 aa81c5b575..37303a08e7 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -16,6 +16,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 643e3bd519..74dfec1c63 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -17,6 +17,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 755c5ec8a8..b9790bc1bb 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: "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 4717105fa2..3ae37e9fff 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: "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 5a15920cca..175f8c7782 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: "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 d2554f24e1..d06393e12c 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: "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 8f78f47e42..f506e3209c 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -14,6 +14,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 956610a101..ef8aa0a526 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -17,6 +17,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 bd31da8a5b..2489a92718 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: "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 84226ab92d..0847f1efb0 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: "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 6df79ffa5b..5fc02acbad 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: "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 b5cf9e9d10..f4e72bbca1 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: "antelope" + openstack_version: "stable/2023.1" + ubuntu_version: "22.04" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" From e2a497e0624bf79d844ea5e3c6717fbb2982891c Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 5 Sep 2023 22:12:47 -0400 Subject: [PATCH 1661/2296] ci/functional: reduce LOC --- .../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 37303a08e7..e7d055af92 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -14,36 +14,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: @@ -54,13 +42,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 74dfec1c63..9afae284f1 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -15,36 +15,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: @@ -55,7 +43,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 ef8aa0a526..ce38236556 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -14,42 +14,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: @@ -60,8 +42,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 9a8b0de0519b455afaf88ee38ee66f5babf8e3cf Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 5 Sep 2023 16:11:35 -0400 Subject: [PATCH 1662/2296] 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. --- .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 742973e3dd..785168da6d 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -12,7 +12,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 6ee3eef505..0f04e0dbb1 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -15,7 +15,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 0451154f98..a3f4090f4e 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -12,7 +12,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 4c1bfb829f..dc1c6ec782 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -12,7 +12,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 e7d055af92..e7a17052f5 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -13,7 +13,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 9afae284f1..63530f52f3 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -14,7 +14,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 b9790bc1bb..40970dc57a 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -12,7 +12,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 3ae37e9fff..67bd7571f6 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -12,7 +12,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 175f8c7782..cecc607dbf 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -12,7 +12,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 d06393e12c..c7c62057c4 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -12,7 +12,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 ce38236556..20ff562878 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -13,7 +13,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 2489a92718..9ad53a06c3 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -12,7 +12,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 0847f1efb0..13dae3e424 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -12,7 +12,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 5fc02acbad..0e1ec8d126 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -12,7 +12,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 f4e72bbca1..af281166c9 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -12,7 +12,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 cc01bec92712f8be54f2d91876196b3ccbafa308 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 09:06:24 +0000 Subject: [PATCH 1663/2296] build(deps): bump golang.org/x/crypto from 0.12.0 to 0.13.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.12.0 to 0.13.0. - [Commits](https://github.com/golang/crypto/compare/v0.12.0...v0.13.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 74a362d33f..845e935ee2 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud go 1.20 require ( - golang.org/x/crypto v0.12.0 + golang.org/x/crypto v0.13.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.11.0 // indirect +require golang.org/x/sys v0.12.0 // indirect diff --git a/go.sum b/go.sum index 694bbecaf7..0b221dbc09 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= 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 f4ed08af2924655f97c7f4eeca5269ea5fa1a0fc Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Thu, 7 Sep 2023 09:17:33 -0400 Subject: [PATCH 1664/2296] 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. --- .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 a3f4090f4e..8437b7beb2 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -42,7 +42,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 dc1c6ec782..aca2dabefe 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -42,7 +42,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 12ff5ebd8f619dfb58968234568fb0ff8d14ea71 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Thu, 7 Sep 2023 14:22:28 -0400 Subject: [PATCH 1665/2296] 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). --- .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 5b21ec0c48..353cbc1f21 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 440873f734970a2a19669f19af999977df1e91ae Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Thu, 7 Sep 2023 15:23:01 -0400 Subject: [PATCH 1666/2296] 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 --- .../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 142078ce8d52f139b64255ffe2a36b11fbd71e9d Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 8 Sep 2023 12:26:13 +0200 Subject: [PATCH 1667/2296] 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 83814ef875..15c58ad98a 100644 --- a/openstack/keymanager/v1/secrets/results.go +++ b/openstack/keymanager/v1/secrets/results.go @@ -92,14 +92,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 } @@ -111,11 +111,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 @@ -214,7 +214,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. @@ -223,7 +223,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 2999b21e92d1033d01ccc10e5f9b06458dacb450 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 8 Sep 2023 09:15:40 -0400 Subject: [PATCH 1668/2296] Fix semver-auto In my previous commit, I didn't read the output, fixing now. --- .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 353cbc1f21..d23fff01d8 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 69989d7ae5f2182e4dcefe785c898b4a6db22fe4 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 5 Sep 2023 21:49:54 -0400 Subject: [PATCH 1669/2296] CI: automatically reject backward incompatible backports If a backport is labeled as `semver-unknown` or `semver-major`, we consider it unsafe to backport until someone has verified otherwise and applied `semver-patch` or `semver-minor`. We create a commit with an error to make it visible if that happens. --- .github/workflows/backport_v1.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 978eac9487..c487c62cbf 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -31,6 +31,9 @@ jobs: private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} - name: Backporting + if: > + contains(github.event.pull_request.labels.*.name, 'semver-patch') + || contains(github.event.pull_request.labels.*.name, 'semver-minor') uses: kiegroup/git-backporting@4313be48e73b299a20b3c8290fd1ea8704e8dd5e with: target-branch: v1 @@ -38,3 +41,13 @@ jobs: auth: ${{ steps.generate_token.outputs.token }} no-squash: true strategy-option: find-renames + + - name: Report an error if backport unsupported labels + if: > + contains(github.event.pull_request.labels.*.name, 'semver-major') + || contains(github.event.pull_request.labels.*.name, 'semver-unknown') + uses: thollander/actions-comment-pull-request@d61db783da9abefc3437960d0cce08552c7c004f + with: + message: | + Labels `semver-major` or `semver-unknown` can not trigger backports. + The PR has to be labeled `semver-patch` or `semver-minor`. From 3c22f397ce422ad1443251191864252dcd268e39 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 20 Sep 2023 09:24:20 -0400 Subject: [PATCH 1670/2296] backport: include label name changes in events Extend the condition in case of a PR gets new labels after it has merged, the steps will be executed and we'll check whether a backport is accepted or not. --- .github/workflows/backport_v1.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index c487c62cbf..7fa357d8ba 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -34,6 +34,8 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver-patch') || contains(github.event.pull_request.labels.*.name, 'semver-minor') + || contains(github.event.label.name, 'semver-patch') + || contains(github.event.label.name, 'semver-minor') uses: kiegroup/git-backporting@4313be48e73b299a20b3c8290fd1ea8704e8dd5e with: target-branch: v1 @@ -46,6 +48,8 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver-major') || contains(github.event.pull_request.labels.*.name, 'semver-unknown') + || contains(github.event.label.name, 'semver-major') + || contains(github.event.label.name, 'semver-unknown') uses: thollander/actions-comment-pull-request@d61db783da9abefc3437960d0cce08552c7c004f with: message: | From 0e644e48ef6d1c4ce759755884073147e15a1957 Mon Sep 17 00:00:00 2001 From: Matt Pryor Date: Thu, 14 Sep 2023 13:59:06 +0100 Subject: [PATCH 1671/2296] Add tag field to compute block_device_v2 --- 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 f218ca0823da721823870bb0c8cdb26302924326 Mon Sep 17 00:00:00 2001 From: Matt Pryor Date: Thu, 14 Sep 2023 15:19:35 +0100 Subject: [PATCH 1672/2296] Add requests test for new field --- .../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 93e5d0fec756becea90038b5fd3b9354528690e5 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 20 Sep 2023 13:37:15 -0400 Subject: [PATCH 1673/2296] 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. --- 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 4fd4c99bff16eca3cdbeae1ec01af260e77aac3f Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 20 Sep 2023 11:06:50 -0400 Subject: [PATCH 1674/2296] Add acceptance tests for tag field to compute block_device_v2 --- 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 8a3a10bcd755da20fa5ff232e123e1e69d32b169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 7 Sep 2023 15:06:57 +0200 Subject: [PATCH 1675/2296] 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 20c5c312fd..9eb6eb75b3 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 5819dc0c7b6c72a3d6cddb1584e8f28ad42a2cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 7 Sep 2023 18:07:51 +0200 Subject: [PATCH 1676/2296] 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 227a0916c20110546e3468c02683da8925534cc1 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 22 Sep 2023 09:22:18 -0400 Subject: [PATCH 1677/2296] backport: fix label names --- .github/workflows/backport_v1.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 7fa357d8ba..5b46c9cf29 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -32,10 +32,10 @@ jobs: - name: Backporting if: > - contains(github.event.pull_request.labels.*.name, 'semver-patch') - || contains(github.event.pull_request.labels.*.name, 'semver-minor') - || contains(github.event.label.name, 'semver-patch') - || contains(github.event.label.name, 'semver-minor') + contains(github.event.pull_request.labels.*.name, 'semver:patch') + || contains(github.event.pull_request.labels.*.name, 'semver:minor') + || contains(github.event.label.name, 'semver:patch') + || contains(github.event.label.name, 'semver:minor') uses: kiegroup/git-backporting@4313be48e73b299a20b3c8290fd1ea8704e8dd5e with: target-branch: v1 @@ -46,10 +46,10 @@ jobs: - name: Report an error if backport unsupported labels if: > - contains(github.event.pull_request.labels.*.name, 'semver-major') - || contains(github.event.pull_request.labels.*.name, 'semver-unknown') - || contains(github.event.label.name, 'semver-major') - || contains(github.event.label.name, 'semver-unknown') + contains(github.event.pull_request.labels.*.name, 'semver:major') + || contains(github.event.pull_request.labels.*.name, 'semver:unknown') + || contains(github.event.label.name, 'semver:major') + || contains(github.event.label.name, 'semver:unknown') uses: thollander/actions-comment-pull-request@d61db783da9abefc3437960d0cce08552c7c004f with: message: | From 6f318813932f2cca065f9d10f3c2e549edb6f27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 22 Sep 2023 15:46:35 +0200 Subject: [PATCH 1678/2296] 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 (92%) 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 0f04e0dbb1..311cb8a8e4 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -53,7 +53,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 92% rename from acceptance/openstack/blockstorage/extensions/backups_test.go rename to internal/acceptance/openstack/blockstorage/extensions/backups_test.go index b1bef16939..9492fbfb8a 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 78bd4f5334..849d3d1c7d 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 8b829dfa72..9eae06ef24 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 4b289b9b77..f2dc2cda88 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 9eb6eb75b3..74bd7d1c09 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 f5515d420a..bcdcb22a0f 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 67fde0004e..4187636f6e 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 7bec78e7a8cbe26bf89a0f0a2b2494cbd544a902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 22 Sep 2023 16:15:25 +0200 Subject: [PATCH 1679/2296] 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 72156e23a82fe9e17b89c06743594a4fd9c93ab4 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 21 Sep 2023 13:31:15 +0200 Subject: [PATCH 1680/2296] baremetal: support ironic native PluginData Add logic to detect which PluginData is provided for the node. --- openstack/baremetal/inventory/plugindata.go | 41 +++++ .../baremetal/inventory/testing/fixtures.go | 156 +++++++++++++++++- .../inventory/testing/plugindata_test.go | 14 +- openstack/baremetal/v1/nodes/results.go | 37 ++++- .../v1/nodes/testing/results_test.go | 75 +++++++++ 5 files changed, 319 insertions(+), 4 deletions(-) create mode 100644 openstack/baremetal/v1/nodes/testing/results_test.go diff --git a/openstack/baremetal/inventory/plugindata.go b/openstack/baremetal/inventory/plugindata.go index 2f4c5260b7..ebb9a840ba 100644 --- a/openstack/baremetal/inventory/plugindata.go +++ b/openstack/baremetal/inventory/plugindata.go @@ -71,3 +71,44 @@ func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { r.Value = value return nil } + +type HardwareManager struct { + Name string `json:"name"` + Version string `json:"version"` +} + +type ConfigurationType struct { + // Collectors is a list of enabled collectors - ramdisk-side inspection + // plugins that populated the plugin data. + Collectors []string `json:"collectors"` + // Managers is a list of hardware managers - ramdisk-side plugins that + // implement all actions, such as writing images or collecting + // inventory. + Managers []HardwareManager `json:"managers"` +} + +type ProcessedInterfaceType struct { + InterfaceType + // Whether PXE was enabled on this interface during inspection + PXEEnabled bool `json:"pxe_enabled"` + // TODO(dtantsur): add LLDPProcessed once it's actually implemented +} + +// StandardPluginData represents the plugin data as collected and processes +// by a standard ramdisk and a standard Ironic deployment. +// The format and contents of the stored data depends on the ramdisk used +// and plugins enabled both in the ramdisk and in inspector itself. +// This structure has been provided for basic compatibility but it +// will need extensions. +type StandardPluginData struct { + AllInterfaces map[string]ProcessedInterfaceType `json:"all_interfaces"` + BootInterface string `json:"boot_interface"` + Configuration ConfigurationType `json:"configuration"` + Error string `json:"error"` + Extra ExtraDataType `json:"extra"` + MACs []string `json:"macs"` + NUMATopology NUMATopology `json:"numa_topology"` + RawLLDP map[string][]LLDPTLVType `json:"lldp_raw"` + RootDisk RootDiskType `json:"root_disk"` + ValidInterfaces map[string]ProcessedInterfaceType `json:"valid_interfaces"` +} diff --git a/openstack/baremetal/inventory/testing/fixtures.go b/openstack/baremetal/inventory/testing/fixtures.go index aeabf052cd..a50c39d9ea 100644 --- a/openstack/baremetal/inventory/testing/fixtures.go +++ b/openstack/baremetal/inventory/testing/fixtures.go @@ -1,6 +1,10 @@ package testing -import "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" +import ( + "fmt" + + "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" +) const InventorySample = `{ "bmc_address": "192.167.2.134", @@ -190,6 +194,84 @@ const NUMADataJSONSample = ` } ` +var StandardPluginDataSample = fmt.Sprintf(` +{ + "all_interfaces": { + "eth0": { + "client_id": null, + "has_carrier": true, + "ipv4_address": "172.24.42.101", + "mac_address": "52:54:00:47:20:4d", + "name": "eth1", + "product": "0x0001", + "vendor": "0x1af4", + "pxe_enabled": true + }, + "eth1": { + "client_id": null, + "has_carrier": true, + "ipv4_address": "172.24.42.100", + "mac_address": "52:54:00:4e:3d:30", + "name": "eth0", + "product": "0x0001", + "vendor": "0x1af4", + "speed_mbps": 1000, + "pxe_enabled": false + } + }, + "boot_interface": "52:54:00:4e:3d:30", + "configuration": { + "collectors": ["default", "logs"], + "managers": [ + { + "name": "generic_hardware_manager", + "version": "1.1" + } + ] + }, + "error": null, + "extra": %s, + "valid_interfaces": { + "eth0": { + "client_id": null, + "has_carrier": true, + "ipv4_address": "172.24.42.101", + "mac_address": "52:54:00:47:20:4d", + "name": "eth1", + "product": "0x0001", + "vendor": "0x1af4", + "pxe_enabled": true + } + }, + "lldp_raw": { + "eth0": [ + [ + 1, + "04112233aabbcc" + ], + [ + 5, + "737730312d646973742d31622d623132" + ] + ] + }, + "macs": [ + "52:54:00:4e:3d:30" + ], + "root_disk": { + "hctl": null, + "model": "", + "name": "/dev/vda", + "rotational": true, + "serial": null, + "size": 13958643712, + "vendor": "0x1af4", + "wwn": null, + "wwn_vendor_extension": null, + "wwn_with_extension": null + } +}`, ExtraDataJSONSample) + var Inventory = inventory.InventoryType{ SystemVendor: inventory.SystemVendorType{ Manufacturer: "Bochs", @@ -350,3 +432,75 @@ var NUMATopology = inventory.NUMATopology{ }, }, } + +var StandardPluginData = inventory.StandardPluginData{ + AllInterfaces: map[string]inventory.ProcessedInterfaceType{ + "eth0": { + InterfaceType: inventory.InterfaceType{ + Vendor: "0x1af4", + HasCarrier: true, + MACAddress: "52:54:00:47:20:4d", + Name: "eth1", + Product: "0x0001", + IPV4Address: "172.24.42.101", + }, + PXEEnabled: true, + }, + "eth1": { + InterfaceType: inventory.InterfaceType{ + IPV4Address: "172.24.42.100", + MACAddress: "52:54:00:4e:3d:30", + Name: "eth0", + Product: "0x0001", + HasCarrier: true, + Vendor: "0x1af4", + SpeedMbps: 1000, + }, + }, + }, + BootInterface: "52:54:00:4e:3d:30", + Configuration: inventory.ConfigurationType{ + Collectors: []string{"default", "logs"}, + Managers: []inventory.HardwareManager{ + { + Name: "generic_hardware_manager", + Version: "1.1", + }, + }, + }, + Error: "", + Extra: ExtraData, + MACs: []string{"52:54:00:4e:3d:30"}, + RawLLDP: map[string][]inventory.LLDPTLVType{ + "eth0": { + { + Type: 1, + Value: "04112233aabbcc", + }, + { + Type: 5, + Value: "737730312d646973742d31622d623132", + }, + }, + }, + RootDisk: inventory.RootDiskType{ + Rotational: true, + Model: "", + Name: "/dev/vda", + Size: 13958643712, + Vendor: "0x1af4", + }, + ValidInterfaces: map[string]inventory.ProcessedInterfaceType{ + "eth0": { + InterfaceType: inventory.InterfaceType{ + Vendor: "0x1af4", + HasCarrier: true, + MACAddress: "52:54:00:47:20:4d", + Name: "eth1", + Product: "0x0001", + IPV4Address: "172.24.42.101", + }, + PXEEnabled: true, + }, + }, +} diff --git a/openstack/baremetal/inventory/testing/plugindata_test.go b/openstack/baremetal/inventory/testing/plugindata_test.go index 09af178320..eb1479502c 100644 --- a/openstack/baremetal/inventory/testing/plugindata_test.go +++ b/openstack/baremetal/inventory/testing/plugindata_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" - "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" th "github.com/gophercloud/gophercloud/testhelper" ) @@ -20,7 +19,7 @@ func TestExtraHardware(t *testing.T) { } func TestIntrospectionNUMA(t *testing.T) { - var output introspection.Data + var output inventory.StandardPluginData err := json.Unmarshal([]byte(NUMADataJSONSample), &output) if err != nil { t.Fatalf("Failed to unmarshal NUMA Data: %s", err) @@ -28,3 +27,14 @@ func TestIntrospectionNUMA(t *testing.T) { th.CheckDeepEquals(t, NUMATopology, output.NUMATopology) } + +func TestStandardPluginData(t *testing.T) { + var output inventory.StandardPluginData + + err := json.Unmarshal([]byte(StandardPluginDataSample), &output) + if err != nil { + t.Fatalf("Failed to unmarshal plugin data: %s", err) + } + + th.CheckDeepEquals(t, StandardPluginData, output) +} diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index b47ead2ddb..9f9007fe3e 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -2,6 +2,7 @@ package nodes import ( "encoding/json" + "fmt" "time" "github.com/gophercloud/gophercloud" @@ -546,12 +547,46 @@ func (pd PluginData) AsMap() (result map[string]interface{}, err error) { return } -// Interpret plugin data as coming from ironic-inspector. +// AsStandardData interprets plugin data as coming from ironic native inspection. +func (pd PluginData) AsStandardData() (result inventory.StandardPluginData, err error) { + err = json.Unmarshal(pd.RawMessage, &result) + return +} + +// AsInspectorData interprets plugin data as coming from ironic-inspector. func (pd PluginData) AsInspectorData() (result introspection.Data, err error) { err = json.Unmarshal(pd.RawMessage, &result) return } +// GuessFormat tries to guess which format the data is in. Unless there is +// an error while parsing, one result will be valid, the other - nil. +// Unknown (but still parseable) format defaults to standard. +func (pd PluginData) GuessFormat() (*inventory.StandardPluginData, *introspection.Data, error) { + // Ironic and Inspector formats are compatible, don't expect an error in either case + ironic, err := pd.AsStandardData() + if err != nil { + return nil, nil, err + } + + // The valid_interfaces field only exists in the Ironic data (it's called just interfaces in Inspector) + if len(ironic.ValidInterfaces) > 0 { + return &ironic, nil, nil + } + + inspector, err := pd.AsInspectorData() + if err != nil { + return nil, nil, fmt.Errorf("cannot interpret PluginData as coming from inspector on conversion: %w", err) + } + + // If the format does not match anything (but still parses), assume a heavily customized deployment + if len(inspector.Interfaces) == 0 { + return &ironic, nil, nil + } + + return nil, &inspector, nil +} + // InventoryData is the full node inventory. type InventoryData struct { // Formally specified bare metal node inventory. diff --git a/openstack/baremetal/v1/nodes/testing/results_test.go b/openstack/baremetal/v1/nodes/testing/results_test.go new file mode 100644 index 0000000000..79617d4a71 --- /dev/null +++ b/openstack/baremetal/v1/nodes/testing/results_test.go @@ -0,0 +1,75 @@ +package testing + +import ( + "testing" + + "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" + invtest "github.com/gophercloud/gophercloud/openstack/baremetal/inventory/testing" + "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" + insptest "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection/testing" + "github.com/gophercloud/gophercloud/testhelper" +) + +func TestStandardPluginData(t *testing.T) { + var pluginData nodes.PluginData + err := pluginData.RawMessage.UnmarshalJSON([]byte(invtest.StandardPluginDataSample)) + testhelper.AssertNoErr(t, err) + + parsedData, err := pluginData.AsStandardData() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, invtest.StandardPluginData, parsedData) + + irData, inspData, err := pluginData.GuessFormat() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, invtest.StandardPluginData, *irData) + testhelper.CheckEquals(t, (*introspection.Data)(nil), inspData) +} + +func TestInspectorPluginData(t *testing.T) { + var pluginData nodes.PluginData + err := pluginData.RawMessage.UnmarshalJSON([]byte(insptest.IntrospectionDataJSONSample)) + testhelper.AssertNoErr(t, err) + + parsedData, err := pluginData.AsInspectorData() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, insptest.IntrospectionDataRes, parsedData) + + irData, inspData, err := pluginData.GuessFormat() + testhelper.AssertNoErr(t, err) + testhelper.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) + testhelper.CheckDeepEquals(t, insptest.IntrospectionDataRes, *inspData) +} + +func TestGuessFormatUnknownDefaultsToIronic(t *testing.T) { + var pluginData nodes.PluginData + err := pluginData.RawMessage.UnmarshalJSON([]byte("{}")) + testhelper.AssertNoErr(t, err) + + irData, inspData, err := pluginData.GuessFormat() + testhelper.CheckDeepEquals(t, inventory.StandardPluginData{}, *irData) + testhelper.CheckEquals(t, (*introspection.Data)(nil), inspData) + testhelper.AssertNoErr(t, err) +} + +func TestGuessFormatErrors(t *testing.T) { + var pluginData nodes.PluginData + err := pluginData.RawMessage.UnmarshalJSON([]byte("\"banana\"")) + testhelper.AssertNoErr(t, err) + + irData, inspData, err := pluginData.GuessFormat() + testhelper.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) + testhelper.CheckEquals(t, (*introspection.Data)(nil), inspData) + testhelper.AssertErr(t, err) + + failsInspectorConversion := `{ + "interfaces": "banana" + }` + err = pluginData.RawMessage.UnmarshalJSON([]byte(failsInspectorConversion)) + testhelper.AssertNoErr(t, err) + + irData, inspData, err = pluginData.GuessFormat() + testhelper.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) + testhelper.CheckEquals(t, (*introspection.Data)(nil), inspData) + testhelper.AssertErr(t, err) +} From 65b80e58cb7f7ce6800f41b528de9cc08c2e4f1d Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 4 Oct 2023 11:10:56 -0400 Subject: [PATCH 1681/2296] 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 24b69909f1..1d0bc5b299 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: @@ -46,18 +46,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 a5456195d17774bd94323e51e8a91699fb096293 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 09:40:57 +0000 Subject: [PATCH 1682/2296] build(deps): bump golang.org/x/crypto from 0.13.0 to 0.14.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.13.0 to 0.14.0. - [Commits](https://github.com/golang/crypto/compare/v0.13.0...v0.14.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 845e935ee2..2e0519a75a 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud go 1.20 require ( - golang.org/x/crypto v0.13.0 + golang.org/x/crypto v0.14.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.12.0 // indirect +require golang.org/x/sys v0.13.0 // indirect diff --git a/go.sum b/go.sum index 0b221dbc09..77548a5981 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= 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 0f4a9ffacccf0c09fc8c495b3bc7079c9de2823a Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Fri, 6 Oct 2023 16:27:07 +0200 Subject: [PATCH 1683/2296] 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 529f0a2208516856ca722d9487228fbf76d376e1 Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Fri, 6 Oct 2023 12:12:20 -0400 Subject: [PATCH 1684/2296] 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 785168da6d..e19420776f 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -14,6 +14,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 311cb8a8e4..37d08bf3cb 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -17,6 +17,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 8437b7beb2..d8287ab520 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -14,6 +14,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 79b5039bef..d817c8c06f 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -14,6 +14,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 aca2dabefe..8add827786 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -14,6 +14,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 e7a17052f5..f5ef2cdd9b 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -14,6 +14,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 63530f52f3..37a0f31c8d 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -15,6 +15,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 f40ca188b4..1f6acd189e 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -14,6 +14,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 40970dc57a..950c17c3d9 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -14,6 +14,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 67bd7571f6..4f561eb212 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -14,6 +14,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 cecc607dbf..18d4ec5cec 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -14,6 +14,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 c7c62057c4..7d0b9d4f65 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -14,6 +14,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 f506e3209c..a92fe0f80b 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -14,6 +14,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 20ff562878..7e8c99a5d7 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -14,6 +14,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 9ad53a06c3..350168de26 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -14,6 +14,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 13dae3e424..479b627a75 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -14,6 +14,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 0e1ec8d126..1e22261d49 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -14,6 +14,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 af281166c9..8ec350f161 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -14,6 +14,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 6a7ae90356dfed2d148e2f9afdc1b5f7582e40ac Mon Sep 17 00:00:00 2001 From: Matthew Booth Date: Thu, 12 Oct 2023 09:51:23 +0100 Subject: [PATCH 1685/2296] Add acceptance tests for list ports --- .../openstack/networking/v2/ports_test.go | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index 19151c2fe3..eae54ee183 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -4,6 +4,7 @@ package v2 import ( + "fmt" "strings" "testing" @@ -70,6 +71,92 @@ func TestPortsCRUD(t *testing.T) { } th.AssertEquals(t, found, true) + + ipAddress := port.FixedIPs[0].IPAddress + t.Logf("Port has IP address: %s", ipAddress) + + // List ports by fixed IP + // All of the following listOpts should return the port + for _, tt := range []struct { + name string + opts ports.ListOpts + expectedPorts int + }{ + { + name: "Port ID", + opts: ports.ListOpts{ + ID: port.ID, + }, + expectedPorts: 1, + }, + { + name: "Network ID", + opts: ports.ListOpts{ + NetworkID: port.NetworkID, + }, + expectedPorts: 2, // Will also return DHCP port + }, + { + name: "Subnet ID", + opts: ports.ListOpts{ + FixedIPs: []ports.FixedIPOpts{ + {SubnetID: subnet.ID}, + }, + }, + expectedPorts: 1, + }, + { + name: "IP Address", + opts: ports.ListOpts{ + FixedIPs: []ports.FixedIPOpts{ + {IPAddress: ipAddress}, + }, + }, + expectedPorts: 1, + }, + { + name: "Subnet ID and IP Address", + opts: ports.ListOpts{ + FixedIPs: []ports.FixedIPOpts{ + {SubnetID: subnet.ID, IPAddress: ipAddress}, + }, + }, + expectedPorts: 1, + }, + } { + t.Run(fmt.Sprintf("List ports by %s", tt.name), func(t *testing.T) { + allPages, err := ports.List(client, tt.opts).AllPages() + th.AssertNoErr(t, err) + + allPorts, err := ports.ExtractPorts(allPages) + th.AssertNoErr(t, err) + + logPorts := func() { + for _, port := range allPorts { + tools.PrintResource(t, port) + } + } + + if len(allPorts) != tt.expectedPorts { + if len(allPorts) == 0 { + t.Fatalf("Port not found") + } + if len(allPorts) > 1 { + logPorts() + t.Fatalf("Expected %d port but got %d", tt.expectedPorts, len(allPorts)) + } + } + func() { + for _, port := range allPorts { + if port.ID == newPort.ID { + return + } + } + logPorts() + t.Fatalf("Returned ports did not contain expected port") + }() + }) + } } func TestPortsRemoveSecurityGroups(t *testing.T) { From 3076b79d074fe4c715433e303101d07a64b7df14 Mon Sep 17 00:00:00 2001 From: Matthew Booth Date: Thu, 12 Oct 2023 12:02:40 +0100 Subject: [PATCH 1686/2296] Fix port list with multiple fixedip parameters --- openstack/networking/v2/ports/requests.go | 9 +++--- .../v2/ports/testing/requests_test.go | 31 +++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 805f0e5b99..70c396abc8 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -3,7 +3,6 @@ package ports import ( "fmt" "net/url" - "strings" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" @@ -50,7 +49,7 @@ type FixedIPOpts struct { SubnetID string } -func (f FixedIPOpts) String() string { +func (f FixedIPOpts) toParams() []string { var res []string if f.IPAddress != "" { res = append(res, fmt.Sprintf("ip_address=%s", f.IPAddress)) @@ -61,7 +60,7 @@ func (f FixedIPOpts) String() string { if f.SubnetID != "" { res = append(res, fmt.Sprintf("subnet_id=%s", f.SubnetID)) } - return strings.Join(res, ",") + return res } // ToPortListQuery formats a ListOpts into a query string. @@ -69,7 +68,9 @@ func (opts ListOpts) ToPortListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) params := q.Query() for _, fixedIP := range opts.FixedIPs { - params.Add("fixed_ips", fixedIP.String()) + for _, fixedIPParam := range fixedIP.toParams() { + params.Add("fixed_ips", fixedIPParam) + } } q = &url.URL{RawQuery: params.Encode()} return q.String(), err diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 5113f005fd..eae8efadf5 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -1001,17 +1001,30 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { } func TestPortsListOpts(t *testing.T) { - for expected, opts := range map[string]ports.ListOpts{ - newValue("fixed_ips", `ip_address=1.2.3.4,subnet_id=42`): { - FixedIPs: []ports.FixedIPOpts{{IPAddress: "1.2.3.4", SubnetID: "42"}}, + for _, tt := range []struct { + listOpts ports.ListOpts + params []struct{ key, value string } + }{ + { + listOpts: ports.ListOpts{ + FixedIPs: []ports.FixedIPOpts{{IPAddress: "1.2.3.4", SubnetID: "42"}}, + }, + params: []struct { + key string + value string + }{ + {"fixed_ips", "ip_address=1.2.3.4"}, + {"fixed_ips", "subnet_id=42"}, + }, }, } { - actual, _ := opts.ToPortListQuery() + v := url.Values{} + for _, param := range tt.params { + v.Add(param.key, param.value) + } + expected := "?" + v.Encode() + + actual, _ := tt.listOpts.ToPortListQuery() th.AssertEquals(t, expected, actual) } } -func newValue(param, value string) string { - v := url.Values{} - v.Add(param, value) - return "?" + v.Encode() -} From 4dc5596bc63a769c55a68efd2ead57a1ac5a3f29 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 14 Dec 2021 15:09:40 -0500 Subject: [PATCH 1687/2296] Remove code for CDN (poppy) OpenStack CDN (Poppy) isn't maintained anymore, last commit was 5 years ago: https://opendev.org/x/poppy/commits/branch/master This patch removes all the code that called CDN v1 API, which is not tested, and the upstream project doesn't seem to be alive anymore. This effort is made for helping to maintain Gophercloud and clean the things we don't need/use anymore. --- openstack/cdn/v1/base/doc.go | 4 - openstack/cdn/v1/base/requests.go | 21 - openstack/cdn/v1/base/results.go | 23 -- openstack/cdn/v1/base/testing/doc.go | 2 - openstack/cdn/v1/base/testing/fixtures.go | 53 --- .../cdn/v1/base/testing/requests_test.go | 46 --- openstack/cdn/v1/base/urls.go | 11 - openstack/cdn/v1/flavors/doc.go | 6 - openstack/cdn/v1/flavors/requests.go | 20 - openstack/cdn/v1/flavors/results.go | 64 --- openstack/cdn/v1/flavors/testing/doc.go | 2 - openstack/cdn/v1/flavors/testing/fixtures.go | 82 ---- .../cdn/v1/flavors/testing/requests_test.go | 90 ----- openstack/cdn/v1/flavors/urls.go | 11 - openstack/cdn/v1/serviceassets/doc.go | 7 - openstack/cdn/v1/serviceassets/requests.go | 52 --- openstack/cdn/v1/serviceassets/results.go | 8 - openstack/cdn/v1/serviceassets/testing/doc.go | 2 - .../cdn/v1/serviceassets/testing/fixtures.go | 19 - .../v1/serviceassets/testing/requests_test.go | 19 - openstack/cdn/v1/serviceassets/urls.go | 7 - openstack/cdn/v1/services/doc.go | 7 - openstack/cdn/v1/services/errors.go | 7 - openstack/cdn/v1/services/requests.go | 285 -------------- openstack/cdn/v1/services/results.go | 308 --------------- openstack/cdn/v1/services/testing/doc.go | 2 - openstack/cdn/v1/services/testing/fixtures.go | 372 ------------------ .../cdn/v1/services/testing/requests_test.go | 359 ----------------- openstack/cdn/v1/services/urls.go | 23 -- openstack/client.go | 6 - 30 files changed, 1918 deletions(-) delete mode 100644 openstack/cdn/v1/base/doc.go delete mode 100644 openstack/cdn/v1/base/requests.go delete mode 100644 openstack/cdn/v1/base/results.go delete mode 100644 openstack/cdn/v1/base/testing/doc.go delete mode 100644 openstack/cdn/v1/base/testing/fixtures.go delete mode 100644 openstack/cdn/v1/base/testing/requests_test.go delete mode 100644 openstack/cdn/v1/base/urls.go delete mode 100644 openstack/cdn/v1/flavors/doc.go delete mode 100644 openstack/cdn/v1/flavors/requests.go delete mode 100644 openstack/cdn/v1/flavors/results.go delete mode 100644 openstack/cdn/v1/flavors/testing/doc.go delete mode 100644 openstack/cdn/v1/flavors/testing/fixtures.go delete mode 100644 openstack/cdn/v1/flavors/testing/requests_test.go delete mode 100644 openstack/cdn/v1/flavors/urls.go delete mode 100644 openstack/cdn/v1/serviceassets/doc.go delete mode 100644 openstack/cdn/v1/serviceassets/requests.go delete mode 100644 openstack/cdn/v1/serviceassets/results.go delete mode 100644 openstack/cdn/v1/serviceassets/testing/doc.go delete mode 100644 openstack/cdn/v1/serviceassets/testing/fixtures.go delete mode 100644 openstack/cdn/v1/serviceassets/testing/requests_test.go delete mode 100644 openstack/cdn/v1/serviceassets/urls.go delete mode 100644 openstack/cdn/v1/services/doc.go delete mode 100644 openstack/cdn/v1/services/errors.go delete mode 100644 openstack/cdn/v1/services/requests.go delete mode 100644 openstack/cdn/v1/services/results.go delete mode 100644 openstack/cdn/v1/services/testing/doc.go delete mode 100644 openstack/cdn/v1/services/testing/fixtures.go delete mode 100644 openstack/cdn/v1/services/testing/requests_test.go delete mode 100644 openstack/cdn/v1/services/urls.go diff --git a/openstack/cdn/v1/base/doc.go b/openstack/cdn/v1/base/doc.go deleted file mode 100644 index f78d4f7355..0000000000 --- a/openstack/cdn/v1/base/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package base provides information and interaction with the base API -// resource in the OpenStack CDN service. This API resource allows for -// retrieving the Home Document and pinging the root URL. -package base diff --git a/openstack/cdn/v1/base/requests.go b/openstack/cdn/v1/base/requests.go deleted file mode 100644 index 1bb41e462c..0000000000 --- a/openstack/cdn/v1/base/requests.go +++ /dev/null @@ -1,21 +0,0 @@ -package base - -import "github.com/gophercloud/gophercloud" - -// Get retrieves the home document, allowing the user to discover the -// entire API. -func Get(c *gophercloud.ServiceClient) (r GetResult) { - resp, err := c.Get(getURL(c), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Ping retrieves a ping to the server. -func Ping(c *gophercloud.ServiceClient) (r PingResult) { - resp, err := c.Get(pingURL(c), nil, &gophercloud.RequestOpts{ - OkCodes: []int{204}, - MoreHeaders: map[string]string{"Accept": ""}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/cdn/v1/base/results.go b/openstack/cdn/v1/base/results.go deleted file mode 100644 index 2dfde7dca3..0000000000 --- a/openstack/cdn/v1/base/results.go +++ /dev/null @@ -1,23 +0,0 @@ -package base - -import "github.com/gophercloud/gophercloud" - -// HomeDocument is a resource that contains all the resources for the CDN API. -type HomeDocument map[string]interface{} - -// GetResult represents the result of a Get operation. -type GetResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a home document resource. -func (r GetResult) Extract() (*HomeDocument, error) { - var s HomeDocument - err := r.ExtractInto(&s) - return &s, err -} - -// PingResult represents the result of a Ping operation. -type PingResult struct { - gophercloud.ErrResult -} diff --git a/openstack/cdn/v1/base/testing/doc.go b/openstack/cdn/v1/base/testing/doc.go deleted file mode 100644 index 891c69a215..0000000000 --- a/openstack/cdn/v1/base/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// cdn_base_v1 -package testing diff --git a/openstack/cdn/v1/base/testing/fixtures.go b/openstack/cdn/v1/base/testing/fixtures.go deleted file mode 100644 index f1f4ac0047..0000000000 --- a/openstack/cdn/v1/base/testing/fixtures.go +++ /dev/null @@ -1,53 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -// HandleGetSuccessfully creates an HTTP handler at `/` on the test handler mux -// that responds with a `Get` response. -func HandleGetSuccessfully(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", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "resources": { - "rel/cdn": { - "href-template": "services{?marker,limit}", - "href-vars": { - "marker": "param/marker", - "limit": "param/limit" - }, - "hints": { - "allow": [ - "GET" - ], - "formats": { - "application/json": {} - } - } - } - } - } - `) - - }) -} - -// HandlePingSuccessfully creates an HTTP handler at `/ping` on the test handler -// mux that responds with a `Ping` response. -func HandlePingSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusNoContent) - }) -} diff --git a/openstack/cdn/v1/base/testing/requests_test.go b/openstack/cdn/v1/base/testing/requests_test.go deleted file mode 100644 index 9c9517e3a0..0000000000 --- a/openstack/cdn/v1/base/testing/requests_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/openstack/cdn/v1/base" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestGetHomeDocument(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleGetSuccessfully(t) - - actual, err := base.Get(fake.ServiceClient()).Extract() - th.CheckNoErr(t, err) - - expected := base.HomeDocument{ - "resources": map[string]interface{}{ - "rel/cdn": map[string]interface{}{ - "href-template": "services{?marker,limit}", - "href-vars": map[string]interface{}{ - "marker": "param/marker", - "limit": "param/limit", - }, - "hints": map[string]interface{}{ - "allow": []interface{}{"GET"}, - "formats": map[string]interface{}{ - "application/json": map[string]interface{}{}, - }, - }, - }, - }, - } - th.CheckDeepEquals(t, expected, *actual) -} - -func TestPing(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandlePingSuccessfully(t) - - err := base.Ping(fake.ServiceClient()).ExtractErr() - th.CheckNoErr(t, err) -} diff --git a/openstack/cdn/v1/base/urls.go b/openstack/cdn/v1/base/urls.go deleted file mode 100644 index 07d892ba93..0000000000 --- a/openstack/cdn/v1/base/urls.go +++ /dev/null @@ -1,11 +0,0 @@ -package base - -import "github.com/gophercloud/gophercloud" - -func getURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL() -} - -func pingURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("ping") -} diff --git a/openstack/cdn/v1/flavors/doc.go b/openstack/cdn/v1/flavors/doc.go deleted file mode 100644 index d4066985cb..0000000000 --- a/openstack/cdn/v1/flavors/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package flavors provides information and interaction with the flavors API -// resource in the OpenStack CDN service. This API resource allows for -// listing flavors and retrieving a specific flavor. -// -// A flavor is a mapping configuration to a CDN provider. -package flavors diff --git a/openstack/cdn/v1/flavors/requests.go b/openstack/cdn/v1/flavors/requests.go deleted file mode 100644 index dd16443b18..0000000000 --- a/openstack/cdn/v1/flavors/requests.go +++ /dev/null @@ -1,20 +0,0 @@ -package flavors - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// List returns a single page of CDN flavors. -func List(c *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { - return FlavorPage{pagination.SinglePageBase(r)} - }) -} - -// Get retrieves a specific flavor based on its unique ID. -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/cdn/v1/flavors/results.go b/openstack/cdn/v1/flavors/results.go deleted file mode 100644 index f7222c75cf..0000000000 --- a/openstack/cdn/v1/flavors/results.go +++ /dev/null @@ -1,64 +0,0 @@ -package flavors - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// Provider represents a provider for a particular flavor. -type Provider struct { - // Specifies the name of the provider. The name must not exceed 64 bytes in - // length and is limited to unicode, digits, underscores, and hyphens. - Provider string `json:"provider"` - // Specifies a list with an href where rel is provider_url. - Links []gophercloud.Link `json:"links"` -} - -// Flavor represents a mapping configuration to a CDN provider. -type Flavor struct { - // Specifies the name of the flavor. The name must not exceed 64 bytes in - // length and is limited to unicode, digits, underscores, and hyphens. - ID string `json:"id"` - // Specifies the list of providers mapped to this flavor. - Providers []Provider `json:"providers"` - // Specifies the self-navigating JSON document paths. - Links []gophercloud.Link `json:"links"` -} - -// FlavorPage is the page returned by a pager when traversing over a -// collection of CDN flavors. -type FlavorPage struct { - pagination.SinglePageBase -} - -// 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 -} - -// ExtractFlavors extracts and returns Flavors. It is used while iterating over -// a flavors.List call. -func ExtractFlavors(r pagination.Page) ([]Flavor, error) { - var s struct { - Flavors []Flavor `json:"flavors"` - } - err := (r.(FlavorPage)).ExtractInto(&s) - return s.Flavors, err -} - -// GetResult represents the result of a get operation. -type GetResult struct { - gophercloud.Result -} - -// Extract is a function that extracts a flavor from a GetResult. -func (r GetResult) Extract() (*Flavor, error) { - var s *Flavor - err := r.ExtractInto(&s) - return s, err -} diff --git a/openstack/cdn/v1/flavors/testing/doc.go b/openstack/cdn/v1/flavors/testing/doc.go deleted file mode 100644 index 567b67e237..0000000000 --- a/openstack/cdn/v1/flavors/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// cdn_flavors_v1 -package testing diff --git a/openstack/cdn/v1/flavors/testing/fixtures.go b/openstack/cdn/v1/flavors/testing/fixtures.go deleted file mode 100644 index ed97247e2e..0000000000 --- a/openstack/cdn/v1/flavors/testing/fixtures.go +++ /dev/null @@ -1,82 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -// HandleListCDNFlavorsSuccessfully creates an HTTP handler at `/flavors` on the test handler mux -// that responds with a `List` response. -func HandleListCDNFlavorsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "flavors": [ - { - "id": "europe", - "providers": [ - { - "provider": "Fastly", - "links": [ - { - "href": "http://www.fastly.com", - "rel": "provider_url" - } - ] - } - ], - "links": [ - { - "href": "https://www.poppycdn.io/v1.0/flavors/europe", - "rel": "self" - } - ] - } - ] - } - `) - }) -} - -// HandleGetCDNFlavorSuccessfully creates an HTTP handler at `/flavors/{id}` on the test handler mux -// that responds with a `Get` response. -func HandleGetCDNFlavorSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/flavors/asia", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "id" : "asia", - "providers" : [ - { - "provider" : "ChinaCache", - "links": [ - { - "href": "http://www.chinacache.com", - "rel": "provider_url" - } - ] - } - ], - "links": [ - { - "href": "https://www.poppycdn.io/v1.0/flavors/asia", - "rel": "self" - } - ] - } - `) - }) -} diff --git a/openstack/cdn/v1/flavors/testing/requests_test.go b/openstack/cdn/v1/flavors/testing/requests_test.go deleted file mode 100644 index d84b600a29..0000000000 --- a/openstack/cdn/v1/flavors/testing/requests_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/cdn/v1/flavors" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListCDNFlavorsSuccessfully(t) - - count := 0 - - err := flavors.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := flavors.ExtractFlavors(page) - if err != nil { - t.Errorf("Failed to extract flavors: %v", err) - return false, err - } - - expected := []flavors.Flavor{ - { - ID: "europe", - Providers: []flavors.Provider{ - { - Provider: "Fastly", - Links: []gophercloud.Link{ - { - Href: "http://www.fastly.com", - Rel: "provider_url", - }, - }, - }, - }, - Links: []gophercloud.Link{ - { - Href: "https://www.poppycdn.io/v1.0/flavors/europe", - Rel: "self", - }, - }, - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - th.CheckEquals(t, 1, count) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetCDNFlavorSuccessfully(t) - - expected := &flavors.Flavor{ - ID: "asia", - Providers: []flavors.Provider{ - { - Provider: "ChinaCache", - Links: []gophercloud.Link{ - { - Href: "http://www.chinacache.com", - Rel: "provider_url", - }, - }, - }, - }, - Links: []gophercloud.Link{ - { - Href: "https://www.poppycdn.io/v1.0/flavors/asia", - Rel: "self", - }, - }, - } - - actual, err := flavors.Get(fake.ServiceClient(), "asia").Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, actual) -} diff --git a/openstack/cdn/v1/flavors/urls.go b/openstack/cdn/v1/flavors/urls.go deleted file mode 100644 index a8540a2aed..0000000000 --- a/openstack/cdn/v1/flavors/urls.go +++ /dev/null @@ -1,11 +0,0 @@ -package flavors - -import "github.com/gophercloud/gophercloud" - -func listURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("flavors") -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("flavors", id) -} diff --git a/openstack/cdn/v1/serviceassets/doc.go b/openstack/cdn/v1/serviceassets/doc.go deleted file mode 100644 index ceecaa5a5e..0000000000 --- a/openstack/cdn/v1/serviceassets/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Package serviceassets provides information and interaction with the -// serviceassets API resource in the OpenStack CDN service. This API resource -// allows for deleting cached assets. -// -// A service distributes assets across the network. Service assets let you -// interrogate properties about these assets and perform certain actions on them. -package serviceassets diff --git a/openstack/cdn/v1/serviceassets/requests.go b/openstack/cdn/v1/serviceassets/requests.go deleted file mode 100644 index 36200ca37b..0000000000 --- a/openstack/cdn/v1/serviceassets/requests.go +++ /dev/null @@ -1,52 +0,0 @@ -package serviceassets - -import ( - "strings" - - "github.com/gophercloud/gophercloud" -) - -// DeleteOptsBuilder allows extensions to add additional parameters to the Delete -// request. -type DeleteOptsBuilder interface { - ToCDNAssetDeleteParams() (string, error) -} - -// DeleteOpts is a structure that holds options for deleting CDN service assets. -type DeleteOpts struct { - // If all is set to true, specifies that the delete occurs against all of the - // assets for the service. - All bool `q:"all"` - // Specifies the relative URL of the asset to be deleted. - URL string `q:"url"` -} - -// ToCDNAssetDeleteParams formats a DeleteOpts into a query string. -func (opts DeleteOpts) ToCDNAssetDeleteParams() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// Delete accepts a unique service ID or URL and deletes the CDN service asset associated with -// it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and -// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" -// are valid options for idOrURL. -func Delete(c *gophercloud.ServiceClient, idOrURL string, opts DeleteOptsBuilder) (r DeleteResult) { - var url string - if strings.Contains(idOrURL, "/") { - url = idOrURL - } else { - url = deleteURL(c, idOrURL) - } - if opts != nil { - q, err := opts.ToCDNAssetDeleteParams() - if err != nil { - r.Err = err - return - } - url += q - } - resp, err := c.Delete(url, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/cdn/v1/serviceassets/results.go b/openstack/cdn/v1/serviceassets/results.go deleted file mode 100644 index b6114c6893..0000000000 --- a/openstack/cdn/v1/serviceassets/results.go +++ /dev/null @@ -1,8 +0,0 @@ -package serviceassets - -import "github.com/gophercloud/gophercloud" - -// DeleteResult represents the result of a Delete operation. -type DeleteResult struct { - gophercloud.ErrResult -} diff --git a/openstack/cdn/v1/serviceassets/testing/doc.go b/openstack/cdn/v1/serviceassets/testing/doc.go deleted file mode 100644 index 1adb681a28..0000000000 --- a/openstack/cdn/v1/serviceassets/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// cdn_serviceassets_v1 -package testing diff --git a/openstack/cdn/v1/serviceassets/testing/fixtures.go b/openstack/cdn/v1/serviceassets/testing/fixtures.go deleted file mode 100644 index 3172d30fd1..0000000000 --- a/openstack/cdn/v1/serviceassets/testing/fixtures.go +++ /dev/null @@ -1,19 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -// HandleDeleteCDNAssetSuccessfully creates an HTTP handler at `/services/{id}/assets` on the test handler mux -// that responds with a `Delete` response. -func HandleDeleteCDNAssetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets", 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) - }) -} diff --git a/openstack/cdn/v1/serviceassets/testing/requests_test.go b/openstack/cdn/v1/serviceassets/testing/requests_test.go deleted file mode 100644 index ff2073bd86..0000000000 --- a/openstack/cdn/v1/serviceassets/testing/requests_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/openstack/cdn/v1/serviceassets" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleDeleteCDNAssetSuccessfully(t) - - err := serviceassets.Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", nil).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/cdn/v1/serviceassets/urls.go b/openstack/cdn/v1/serviceassets/urls.go deleted file mode 100644 index ce1741826a..0000000000 --- a/openstack/cdn/v1/serviceassets/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package serviceassets - -import "github.com/gophercloud/gophercloud" - -func deleteURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("services", id, "assets") -} diff --git a/openstack/cdn/v1/services/doc.go b/openstack/cdn/v1/services/doc.go deleted file mode 100644 index 41f7c60dae..0000000000 --- a/openstack/cdn/v1/services/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Package services provides information and interaction with the services API -// resource in the OpenStack CDN service. This API resource allows for -// listing, creating, updating, retrieving, and deleting services. -// -// A service represents an application that has its content cached to the edge -// nodes. -package services diff --git a/openstack/cdn/v1/services/errors.go b/openstack/cdn/v1/services/errors.go deleted file mode 100644 index 359584c2a6..0000000000 --- a/openstack/cdn/v1/services/errors.go +++ /dev/null @@ -1,7 +0,0 @@ -package services - -import "fmt" - -func no(str string) error { - return fmt.Errorf("Required parameter %s not provided", str) -} diff --git a/openstack/cdn/v1/services/requests.go b/openstack/cdn/v1/services/requests.go deleted file mode 100644 index 1ec4810811..0000000000 --- a/openstack/cdn/v1/services/requests.go +++ /dev/null @@ -1,285 +0,0 @@ -package services - -import ( - "fmt" - "strings" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToCDNServiceListQuery() (string, error) -} - -// ListOpts allows the filtering and sorting of paginated collections through -// the API. Marker and Limit are used for pagination. -type ListOpts struct { - Marker string `q:"marker"` - Limit int `q:"limit"` -} - -// ToCDNServiceListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToCDNServiceListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns a Pager which allows you to iterate over a collection of -// CDN services. It accepts a ListOpts struct, which allows for pagination via -// marker and limit. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(c) - if opts != nil { - query, err := opts.ToCDNServiceListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - p := ServicePage{pagination.MarkerPageBase{PageResult: r}} - p.MarkerPageBase.Owner = p - return p - }) -} - -// CreateOptsBuilder is the interface options structs have to satisfy in order -// to be used in the main Create operation in this package. Since many -// extensions decorate or modify the common logic, it is useful for them to -// satisfy a basic interface in order for them to be used. -type CreateOptsBuilder interface { - ToCDNServiceCreateMap() (map[string]interface{}, error) -} - -// CreateOpts is the common options struct used in this package's Create -// operation. -type CreateOpts struct { - // Specifies the name of the service. The minimum length for name is - // 3. The maximum length is 256. - Name string `json:"name" required:"true"` - // Specifies a list of domains used by users to access their website. - Domains []Domain `json:"domains" required:"true"` - // Specifies a list of origin domains or IP addresses where the - // original assets are stored. - Origins []Origin `json:"origins" required:"true"` - // Specifies the CDN provider flavor ID to use. For a list of - // flavors, see the operation to list the available flavors. The minimum - // length for flavor_id is 1. The maximum length is 256. - FlavorID string `json:"flavor_id" required:"true"` - // Specifies the TTL rules for the assets under this service. Supports wildcards for fine-grained control. - Caching []CacheRule `json:"caching,omitempty"` - // Specifies the restrictions that define who can access assets (content from the CDN cache). - Restrictions []Restriction `json:"restrictions,omitempty"` -} - -// ToCDNServiceCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToCDNServiceCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "") -} - -// Create accepts a CreateOpts struct and creates a new CDN service using the -// values provided. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToCDNServiceCreateMap() - if err != nil { - r.Err = err - return r - } - resp, err := c.Post(createURL(c), &b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a specific service based on its URL or its unique ID. For -// example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and -// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" -// are valid options for idOrURL. -func Get(c *gophercloud.ServiceClient, idOrURL string) (r GetResult) { - var url string - if strings.Contains(idOrURL, "/") { - url = idOrURL - } else { - url = getURL(c, idOrURL) - } - resp, err := c.Get(url, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Path is a JSON pointer location that indicates which service parameter is being added, replaced, -// or removed. -type Path struct { - baseElement string -} - -func (p Path) renderRoot() string { - return "/" + p.baseElement -} - -func (p Path) renderDash() string { - return fmt.Sprintf("/%s/-", p.baseElement) -} - -func (p Path) renderIndex(index int64) string { - return fmt.Sprintf("/%s/%d", p.baseElement, index) -} - -var ( - // PathDomains indicates that an update operation is to be performed on a Domain. - PathDomains = Path{baseElement: "domains"} - - // PathOrigins indicates that an update operation is to be performed on an Origin. - PathOrigins = Path{baseElement: "origins"} - - // PathCaching indicates that an update operation is to be performed on a CacheRule. - PathCaching = Path{baseElement: "caching"} -) - -type value interface { - toPatchValue() interface{} - appropriatePath() Path - renderRootOr(func(p Path) string) string -} - -// Patch represents a single update to an existing Service. Multiple updates to a service can be -// submitted at the same time. -type Patch interface { - ToCDNServiceUpdateMap() map[string]interface{} -} - -// Insertion is a Patch that requests the addition of a value (Domain, Origin, or CacheRule) to -// a Service at a fixed index. Use an Append instead to append the new value to the end of its -// collection. Pass it to the Update function as part of the Patch slice. -type Insertion struct { - Index int64 - Value value -} - -// ToCDNServiceUpdateMap converts an Insertion into a request body fragment suitable for the -// Update call. -func (opts Insertion) ToCDNServiceUpdateMap() map[string]interface{} { - return map[string]interface{}{ - "op": "add", - "path": opts.Value.renderRootOr(func(p Path) string { return p.renderIndex(opts.Index) }), - "value": opts.Value.toPatchValue(), - } -} - -// Append is a Patch that requests the addition of a value (Domain, Origin, or CacheRule) to a -// Service at the end of its respective collection. Use an Insertion instead to insert the value -// at a fixed index within the collection. Pass this to the Update function as part of its -// Patch slice. -type Append struct { - Value value -} - -// ToCDNServiceUpdateMap converts an Append into a request body fragment suitable for the -// Update call. -func (a Append) ToCDNServiceUpdateMap() map[string]interface{} { - return map[string]interface{}{ - "op": "add", - "path": a.Value.renderRootOr(func(p Path) string { return p.renderDash() }), - "value": a.Value.toPatchValue(), - } -} - -// Replacement is a Patch that alters a specific service parameter (Domain, Origin, or CacheRule) -// in-place by index. Pass it to the Update function as part of the Patch slice. -type Replacement struct { - Value value - Index int64 -} - -// ToCDNServiceUpdateMap converts a Replacement into a request body fragment suitable for the -// Update call. -func (r Replacement) ToCDNServiceUpdateMap() map[string]interface{} { - return map[string]interface{}{ - "op": "replace", - "path": r.Value.renderRootOr(func(p Path) string { return p.renderIndex(r.Index) }), - "value": r.Value.toPatchValue(), - } -} - -// NameReplacement specifically updates the Service name. Pass it to the Update function as part -// of the Patch slice. -type NameReplacement struct { - NewName string -} - -// ToCDNServiceUpdateMap converts a NameReplacement into a request body fragment suitable for the -// Update call. -func (r NameReplacement) ToCDNServiceUpdateMap() map[string]interface{} { - return map[string]interface{}{ - "op": "replace", - "path": "/name", - "value": r.NewName, - } -} - -// Removal is a Patch that requests the removal of a service parameter (Domain, Origin, or -// CacheRule) by index. Pass it to the Update function as part of the Patch slice. -type Removal struct { - Path Path - Index int64 - All bool -} - -// ToCDNServiceUpdateMap converts a Removal into a request body fragment suitable for the -// Update call. -func (opts Removal) ToCDNServiceUpdateMap() map[string]interface{} { - b := map[string]interface{}{"op": "remove"} - if opts.All { - b["path"] = opts.Path.renderRoot() - } else { - b["path"] = opts.Path.renderIndex(opts.Index) - } - return b -} - -// UpdateOpts is a slice of Patches used to update a CDN service -type UpdateOpts []Patch - -// Update accepts a slice of Patch operations (Insertion, Append, Replacement or Removal) and -// updates an existing CDN service using the values provided. idOrURL can be either the service's -// URL or its ID. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and -// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" -// are valid options for idOrURL. -func Update(c *gophercloud.ServiceClient, idOrURL string, opts UpdateOpts) (r UpdateResult) { - var url string - if strings.Contains(idOrURL, "/") { - url = idOrURL - } else { - url = updateURL(c, idOrURL) - } - - b := make([]map[string]interface{}, len(opts)) - for i, patch := range opts { - b[i] = patch.ToCDNServiceUpdateMap() - } - - resp, err := c.Request("PATCH", url, &gophercloud.RequestOpts{ - JSONBody: &b, - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete accepts a service's ID or its URL and deletes the CDN service -// associated with it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and -// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" -// are valid options for idOrURL. -func Delete(c *gophercloud.ServiceClient, idOrURL string) (r DeleteResult) { - var url string - if strings.Contains(idOrURL, "/") { - url = idOrURL - } else { - url = deleteURL(c, idOrURL) - } - resp, err := c.Delete(url, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/cdn/v1/services/results.go b/openstack/cdn/v1/services/results.go deleted file mode 100644 index fd77f192f8..0000000000 --- a/openstack/cdn/v1/services/results.go +++ /dev/null @@ -1,308 +0,0 @@ -package services - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// Domain represents a domain used by users to access their website. -type Domain struct { - // Specifies the domain used to access the assets on their website, for which - // a CNAME is given to the CDN provider. - Domain string `json:"domain" required:"true"` - // Specifies the protocol used to access the assets on this domain. Only "http" - // or "https" are currently allowed. The default is "http". - Protocol string `json:"protocol,omitempty"` -} - -func (d Domain) toPatchValue() interface{} { - r := make(map[string]interface{}) - r["domain"] = d.Domain - if d.Protocol != "" { - r["protocol"] = d.Protocol - } - return r -} - -func (d Domain) appropriatePath() Path { - return PathDomains -} - -func (d Domain) renderRootOr(render func(p Path) string) string { - return render(d.appropriatePath()) -} - -// DomainList provides a useful way to perform bulk operations in a single Patch. -type DomainList []Domain - -func (list DomainList) toPatchValue() interface{} { - r := make([]interface{}, len(list)) - for i, domain := range list { - r[i] = domain.toPatchValue() - } - return r -} - -func (list DomainList) appropriatePath() Path { - return PathDomains -} - -func (list DomainList) renderRootOr(_ func(p Path) string) string { - return list.appropriatePath().renderRoot() -} - -// OriginRule represents a rule that defines when an origin should be accessed. -type OriginRule struct { - // Specifies the name of this rule. - Name string `json:"name" required:"true"` - // Specifies the request URL this rule should match for this origin to be used. Regex is supported. - RequestURL string `json:"request_url" required:"true"` -} - -// Origin specifies a list of origin domains or IP addresses where the original assets are stored. -type Origin struct { - // Specifies the URL or IP address to pull origin content from. - Origin string `json:"origin" required:"true"` - // Specifies the port used to access the origin. The default is port 80. - Port int `json:"port,omitempty"` - // Specifies whether or not to use HTTPS to access the origin. The default - // is false. - SSL bool `json:"ssl"` - // Specifies a collection of rules that define the conditions when this origin - // should be accessed. If there is more than one origin, the rules parameter is required. - Rules []OriginRule `json:"rules,omitempty"` -} - -func (o Origin) toPatchValue() interface{} { - r := make(map[string]interface{}) - r["origin"] = o.Origin - r["port"] = o.Port - r["ssl"] = o.SSL - if len(o.Rules) > 0 { - r["rules"] = make([]map[string]interface{}, len(o.Rules)) - for index, rule := range o.Rules { - submap := r["rules"].([]map[string]interface{})[index] - submap["name"] = rule.Name - submap["request_url"] = rule.RequestURL - } - } - return r -} - -func (o Origin) appropriatePath() Path { - return PathOrigins -} - -func (o Origin) renderRootOr(render func(p Path) string) string { - return render(o.appropriatePath()) -} - -// OriginList provides a useful way to perform bulk operations in a single Patch. -type OriginList []Origin - -func (list OriginList) toPatchValue() interface{} { - r := make([]interface{}, len(list)) - for i, origin := range list { - r[i] = origin.toPatchValue() - } - return r -} - -func (list OriginList) appropriatePath() Path { - return PathOrigins -} - -func (list OriginList) renderRootOr(_ func(p Path) string) string { - return list.appropriatePath().renderRoot() -} - -// TTLRule specifies a rule that determines if a TTL should be applied to an asset. -type TTLRule struct { - // Specifies the name of this rule. - Name string `json:"name" required:"true"` - // Specifies the request URL this rule should match for this TTL to be used. Regex is supported. - RequestURL string `json:"request_url" required:"true"` -} - -// CacheRule specifies the TTL rules for the assets under this service. -type CacheRule struct { - // Specifies the name of this caching rule. Note: 'default' is a reserved name used for the default TTL setting. - Name string `json:"name" required:"true"` - // Specifies the TTL to apply. - TTL int `json:"ttl,omitempty"` - // Specifies a collection of rules that determine if this TTL should be applied to an asset. - Rules []TTLRule `json:"rules,omitempty"` -} - -func (c CacheRule) toPatchValue() interface{} { - r := make(map[string]interface{}) - r["name"] = c.Name - r["ttl"] = c.TTL - r["rules"] = make([]map[string]interface{}, len(c.Rules)) - for index, rule := range c.Rules { - submap := r["rules"].([]map[string]interface{})[index] - submap["name"] = rule.Name - submap["request_url"] = rule.RequestURL - } - return r -} - -func (c CacheRule) appropriatePath() Path { - return PathCaching -} - -func (c CacheRule) renderRootOr(render func(p Path) string) string { - return render(c.appropriatePath()) -} - -// CacheRuleList provides a useful way to perform bulk operations in a single Patch. -type CacheRuleList []CacheRule - -func (list CacheRuleList) toPatchValue() interface{} { - r := make([]interface{}, len(list)) - for i, rule := range list { - r[i] = rule.toPatchValue() - } - return r -} - -func (list CacheRuleList) appropriatePath() Path { - return PathCaching -} - -func (list CacheRuleList) renderRootOr(_ func(p Path) string) string { - return list.appropriatePath().renderRoot() -} - -// RestrictionRule specifies a rule that determines if this restriction should be applied to an asset. -type RestrictionRule struct { - // Specifies the name of this rule. - Name string `json:"name" required:"true"` - // Specifies the http host that requests must come from. - Referrer string `json:"referrer,omitempty"` -} - -// Restriction specifies a restriction that defines who can access assets (content from the CDN cache). -type Restriction struct { - // Specifies the name of this restriction. - Name string `json:"name" required:"true"` - // Specifies a collection of rules that determine if this TTL should be applied to an asset. - Rules []RestrictionRule `json:"rules,omitempty"` -} - -// Error specifies an error that occurred during the previous service action. -type Error struct { - // Specifies an error message detailing why there is an error. - Message string `json:"message"` -} - -// Service represents a CDN service resource. -type Service struct { - // Specifies the service ID that represents distributed content. The value is - // a UUID, such as 96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0, that is generated by the server. - ID string `json:"id"` - // Specifies the name of the service. - Name string `json:"name"` - // Specifies a list of domains used by users to access their website. - Domains []Domain `json:"domains"` - // Specifies a list of origin domains or IP addresses where the original assets are stored. - Origins []Origin `json:"origins"` - // Specifies the TTL rules for the assets under this service. Supports wildcards for fine grained control. - Caching []CacheRule `json:"caching"` - // Specifies the restrictions that define who can access assets (content from the CDN cache). - Restrictions []Restriction `json:"restrictions"` - // Specifies the CDN provider flavor ID to use. For a list of flavors, see the operation to list the available flavors. - FlavorID string `json:"flavor_id"` - // Specifies the current status of the service. - Status string `json:"status"` - // Specifies the list of errors that occurred during the previous service action. - Errors []Error `json:"errors"` - // Specifies the self-navigating JSON document paths. - Links []gophercloud.Link `json:"links"` -} - -// ServicePage is the page returned by a pager when traversing over a -// collection of CDN services. -type ServicePage struct { - pagination.MarkerPageBase -} - -// 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 -} - -// LastMarker returns the last service in a ListResult. -func (r ServicePage) LastMarker() (string, error) { - services, err := ExtractServices(r) - if err != nil { - return "", err - } - if len(services) == 0 { - return "", nil - } - return (services[len(services)-1]).ID, nil -} - -// ExtractServices is a function that takes a ListResult and returns the services' information. -func ExtractServices(r pagination.Page) ([]Service, error) { - var s struct { - Services []Service `json:"services"` - } - err := (r.(ServicePage)).ExtractInto(&s) - return s.Services, err -} - -// CreateResult represents the result of a Create operation. -type CreateResult struct { - gophercloud.Result -} - -// Extract is a method that extracts the location of a newly created service. -func (r CreateResult) Extract() (string, error) { - if r.Err != nil { - return "", r.Err - } - if l, ok := r.Header["Location"]; ok && len(l) > 0 { - return l[0], nil - } - return "", nil -} - -// GetResult represents the result of a get operation. -type GetResult struct { - gophercloud.Result -} - -// Extract is a function that extracts a service from a GetResult. -func (r GetResult) Extract() (*Service, error) { - var s Service - err := r.ExtractInto(&s) - return &s, err -} - -// UpdateResult represents the result of a Update operation. -type UpdateResult struct { - gophercloud.Result -} - -// Extract is a method that extracts the location of an updated service. -func (r UpdateResult) Extract() (string, error) { - if r.Err != nil { - return "", r.Err - } - if l, ok := r.Header["Location"]; ok && len(l) > 0 { - return l[0], nil - } - return "", nil -} - -// DeleteResult represents the result of a Delete operation. -type DeleteResult struct { - gophercloud.ErrResult -} diff --git a/openstack/cdn/v1/services/testing/doc.go b/openstack/cdn/v1/services/testing/doc.go deleted file mode 100644 index c72e391afe..0000000000 --- a/openstack/cdn/v1/services/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// cdn_services_v1 -package testing diff --git a/openstack/cdn/v1/services/testing/fixtures.go b/openstack/cdn/v1/services/testing/fixtures.go deleted file mode 100644 index d4093e0515..0000000000 --- a/openstack/cdn/v1/services/testing/fixtures.go +++ /dev/null @@ -1,372 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -// HandleListCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux -// that responds with a `List` response. -func HandleListCDNServiceSuccessfully(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", fake.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ` - { - "links": [ - { - "rel": "next", - "href": "https://www.poppycdn.io/v1.0/services?marker=96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0&limit=20" - } - ], - "services": [ - { - "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - "name": "mywebsite.com", - "domains": [ - { - "domain": "www.mywebsite.com" - } - ], - "origins": [ - { - "origin": "mywebsite.com", - "port": 80, - "ssl": false - } - ], - "caching": [ - { - "name": "default", - "ttl": 3600 - }, - { - "name": "home", - "ttl": 17200, - "rules": [ - { - "name": "index", - "request_url": "/index.htm" - } - ] - }, - { - "name": "images", - "ttl": 12800, - "rules": [ - { - "name": "images", - "request_url": "*.png" - } - ] - } - ], - "restrictions": [ - { - "name": "website only", - "rules": [ - { - "name": "mywebsite.com", - "referrer": "www.mywebsite.com" - } - ] - } - ], - "flavor_id": "asia", - "status": "deployed", - "errors" : [], - "links": [ - { - "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - "rel": "self" - }, - { - "href": "mywebsite.com.cdn123.poppycdn.net", - "rel": "access_url" - }, - { - "href": "https://www.poppycdn.io/v1.0/flavors/asia", - "rel": "flavor" - } - ] - }, - { - "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", - "name": "myothersite.com", - "domains": [ - { - "domain": "www.myothersite.com" - } - ], - "origins": [ - { - "origin": "44.33.22.11", - "port": 80, - "ssl": false - }, - { - "origin": "77.66.55.44", - "port": 80, - "ssl": false, - "rules": [ - { - "name": "videos", - "request_url": "^/videos/*.m3u" - } - ] - } - ], - "caching": [ - { - "name": "default", - "ttl": 3600 - } - ], - "restrictions": [ - {} - ], - "flavor_id": "europe", - "status": "deployed", - "links": [ - { - "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", - "rel": "self" - }, - { - "href": "myothersite.com.poppycdn.net", - "rel": "access_url" - }, - { - "href": "https://www.poppycdn.io/v1.0/flavors/europe", - "rel": "flavor" - } - ] - } - ] - } - `) - case "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1": - fmt.Fprintf(w, `{ - "services": [] - }`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -// HandleCreateCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux -// that responds with a `Create` response. -func HandleCreateCDNServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestJSONRequest(t, r, ` - { - "name": "mywebsite.com", - "domains": [ - { - "domain": "www.mywebsite.com" - }, - { - "domain": "blog.mywebsite.com" - } - ], - "origins": [ - { - "origin": "mywebsite.com", - "port": 80, - "ssl": false - } - ], - "restrictions": [ - { - "name": "website only", - "rules": [ - { - "name": "mywebsite.com", - "referrer": "www.mywebsite.com" - } - ] - } - ], - "caching": [ - { - "name": "default", - "ttl": 3600 - } - ], - - "flavor_id": "cdn" - } - `) - w.Header().Add("Location", "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0") - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleGetCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux -// that responds with a `Get` response. -func HandleGetCDNServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - "name": "mywebsite.com", - "domains": [ - { - "domain": "www.mywebsite.com", - "protocol": "http" - } - ], - "origins": [ - { - "origin": "mywebsite.com", - "port": 80, - "ssl": false - } - ], - "caching": [ - { - "name": "default", - "ttl": 3600 - }, - { - "name": "home", - "ttl": 17200, - "rules": [ - { - "name": "index", - "request_url": "/index.htm" - } - ] - }, - { - "name": "images", - "ttl": 12800, - "rules": [ - { - "name": "images", - "request_url": "*.png" - } - ] - } - ], - "restrictions": [ - { - "name": "website only", - "rules": [ - { - "name": "mywebsite.com", - "referrer": "www.mywebsite.com" - } - ] - } - ], - "flavor_id": "cdn", - "status": "deployed", - "errors" : [], - "links": [ - { - "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - "rel": "self" - }, - { - "href": "blog.mywebsite.com.cdn1.raxcdn.com", - "rel": "access_url" - }, - { - "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn", - "rel": "flavor" - } - ] - } - `) - }) -} - -// HandleUpdateCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux -// that responds with a `Update` response. -func HandleUpdateCDNServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestJSONRequest(t, r, ` - [ - { - "op": "add", - "path": "/domains/-", - "value": {"domain": "appended.mocksite4.com"} - }, - { - "op": "add", - "path": "/domains/4", - "value": {"domain": "inserted.mocksite4.com"} - }, - { - "op": "add", - "path": "/domains", - "value": [ - {"domain": "bulkadded1.mocksite4.com"}, - {"domain": "bulkadded2.mocksite4.com"} - ] - }, - { - "op": "replace", - "path": "/origins/2", - "value": {"origin": "44.33.22.11", "port": 80, "ssl": false} - }, - { - "op": "replace", - "path": "/origins", - "value": [ - {"origin": "44.33.22.11", "port": 80, "ssl": false}, - {"origin": "55.44.33.22", "port": 443, "ssl": true} - ] - }, - { - "op": "remove", - "path": "/caching/8" - }, - { - "op": "remove", - "path": "/caching" - }, - { - "op": "replace", - "path": "/name", - "value": "differentServiceName" - } - ] - `) - w.Header().Add("Location", "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0") - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleDeleteCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux -// that responds with a `Delete` response. -func HandleDeleteCDNServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", 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) - }) -} diff --git a/openstack/cdn/v1/services/testing/requests_test.go b/openstack/cdn/v1/services/testing/requests_test.go deleted file mode 100644 index 52854163af..0000000000 --- a/openstack/cdn/v1/services/testing/requests_test.go +++ /dev/null @@ -1,359 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/cdn/v1/services" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListCDNServiceSuccessfully(t) - - count := 0 - - err := services.List(fake.ServiceClient(), &services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := services.ExtractServices(page) - if err != nil { - t.Errorf("Failed to extract services: %v", err) - return false, err - } - - expected := []services.Service{ - { - ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - Name: "mywebsite.com", - Domains: []services.Domain{ - { - Domain: "www.mywebsite.com", - }, - }, - Origins: []services.Origin{ - { - Origin: "mywebsite.com", - Port: 80, - SSL: false, - }, - }, - Caching: []services.CacheRule{ - { - Name: "default", - TTL: 3600, - }, - { - Name: "home", - TTL: 17200, - Rules: []services.TTLRule{ - { - Name: "index", - RequestURL: "/index.htm", - }, - }, - }, - { - Name: "images", - TTL: 12800, - Rules: []services.TTLRule{ - { - Name: "images", - RequestURL: "*.png", - }, - }, - }, - }, - Restrictions: []services.Restriction{ - { - Name: "website only", - Rules: []services.RestrictionRule{ - { - Name: "mywebsite.com", - Referrer: "www.mywebsite.com", - }, - }, - }, - }, - FlavorID: "asia", - Status: "deployed", - Errors: []services.Error{}, - Links: []gophercloud.Link{ - { - Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - Rel: "self", - }, - { - Href: "mywebsite.com.cdn123.poppycdn.net", - Rel: "access_url", - }, - { - Href: "https://www.poppycdn.io/v1.0/flavors/asia", - Rel: "flavor", - }, - }, - }, - { - ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", - Name: "myothersite.com", - Domains: []services.Domain{ - { - Domain: "www.myothersite.com", - }, - }, - Origins: []services.Origin{ - { - Origin: "44.33.22.11", - Port: 80, - SSL: false, - }, - { - Origin: "77.66.55.44", - Port: 80, - SSL: false, - Rules: []services.OriginRule{ - { - Name: "videos", - RequestURL: "^/videos/*.m3u", - }, - }, - }, - }, - Caching: []services.CacheRule{ - { - Name: "default", - TTL: 3600, - }, - }, - Restrictions: []services.Restriction{}, - FlavorID: "europe", - Status: "deployed", - Links: []gophercloud.Link{ - { - Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", - Rel: "self", - }, - { - Href: "myothersite.com.poppycdn.net", - Rel: "access_url", - }, - { - Href: "https://www.poppycdn.io/v1.0/flavors/europe", - Rel: "flavor", - }, - }, - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCreateCDNServiceSuccessfully(t) - - createOpts := services.CreateOpts{ - Name: "mywebsite.com", - Domains: []services.Domain{ - { - Domain: "www.mywebsite.com", - }, - { - Domain: "blog.mywebsite.com", - }, - }, - Origins: []services.Origin{ - { - Origin: "mywebsite.com", - Port: 80, - SSL: false, - }, - }, - Restrictions: []services.Restriction{ - { - Name: "website only", - Rules: []services.RestrictionRule{ - { - Name: "mywebsite.com", - Referrer: "www.mywebsite.com", - }, - }, - }, - }, - Caching: []services.CacheRule{ - { - Name: "default", - TTL: 3600, - }, - }, - FlavorID: "cdn", - } - - expected := "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" - actual, err := services.Create(fake.ServiceClient(), createOpts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, expected, actual) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetCDNServiceSuccessfully(t) - - expected := &services.Service{ - ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - Name: "mywebsite.com", - Domains: []services.Domain{ - { - Domain: "www.mywebsite.com", - Protocol: "http", - }, - }, - Origins: []services.Origin{ - { - Origin: "mywebsite.com", - Port: 80, - SSL: false, - }, - }, - Caching: []services.CacheRule{ - { - Name: "default", - TTL: 3600, - }, - { - Name: "home", - TTL: 17200, - Rules: []services.TTLRule{ - { - Name: "index", - RequestURL: "/index.htm", - }, - }, - }, - { - Name: "images", - TTL: 12800, - Rules: []services.TTLRule{ - { - Name: "images", - RequestURL: "*.png", - }, - }, - }, - }, - Restrictions: []services.Restriction{ - { - Name: "website only", - Rules: []services.RestrictionRule{ - { - Name: "mywebsite.com", - Referrer: "www.mywebsite.com", - }, - }, - }, - }, - FlavorID: "cdn", - Status: "deployed", - Errors: []services.Error{}, - Links: []gophercloud.Link{ - { - Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - Rel: "self", - }, - { - Href: "blog.mywebsite.com.cdn1.raxcdn.com", - Rel: "access_url", - }, - { - Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn", - Rel: "flavor", - }, - }, - } - - actual, err := services.Get(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, actual) -} - -func TestSuccessfulUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleUpdateCDNServiceSuccessfully(t) - - expected := "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" - ops := services.UpdateOpts{ - // Append a single Domain - services.Append{Value: services.Domain{Domain: "appended.mocksite4.com"}}, - // Insert a single Domain - services.Insertion{ - Index: 4, - Value: services.Domain{Domain: "inserted.mocksite4.com"}, - }, - // Bulk addition - services.Append{ - Value: services.DomainList{ - {Domain: "bulkadded1.mocksite4.com"}, - {Domain: "bulkadded2.mocksite4.com"}, - }, - }, - // Replace a single Origin - services.Replacement{ - Index: 2, - Value: services.Origin{Origin: "44.33.22.11", Port: 80, SSL: false}, - }, - // Bulk replace Origins - services.Replacement{ - Index: 0, // Ignored - Value: services.OriginList{ - {Origin: "44.33.22.11", Port: 80, SSL: false}, - {Origin: "55.44.33.22", Port: 443, SSL: true}, - }, - }, - // Remove a single CacheRule - services.Removal{ - Index: 8, - Path: services.PathCaching, - }, - // Bulk removal - services.Removal{ - All: true, - Path: services.PathCaching, - }, - // Service name replacement - services.NameReplacement{ - NewName: "differentServiceName", - }, - } - - actual, err := services.Update(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", ops).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, expected, actual) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleDeleteCDNServiceSuccessfully(t) - - err := services.Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/cdn/v1/services/urls.go b/openstack/cdn/v1/services/urls.go deleted file mode 100644 index 5bb3ca9d92..0000000000 --- a/openstack/cdn/v1/services/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package services - -import "github.com/gophercloud/gophercloud" - -func listURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("services") -} - -func createURL(c *gophercloud.ServiceClient) string { - return listURL(c) -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("services", id) -} - -func updateURL(c *gophercloud.ServiceClient, id string) string { - return getURL(c, id) -} - -func deleteURL(c *gophercloud.ServiceClient, id string) string { - return getURL(c, id) -} diff --git a/openstack/client.go b/openstack/client.go index 87cd90f38e..8786189edf 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -418,12 +418,6 @@ func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.En return initClientOpts(client, eo, "sharev2") } -// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1 -// CDN service. -func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "cdn") -} - // NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 // orchestration service. func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { From 7f1d07519ffc99fdc74e1dafc92d56be52624989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 13 Oct 2023 08:45:18 +0200 Subject: [PATCH 1688/2296] 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/baremetalintrospection/v1/introspection/testing" "github.com/gophercloud/gophercloud/openstack/baremetal/inventory/testing" "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. --- .../.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 .../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 187 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/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/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 2d3aee0a77bec0ded23829ab5f001ed29b15d735 Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Wed, 11 Oct 2023 18:06:04 -0400 Subject: [PATCH 1689/2296] Fix baremetal jobs on Ubuntu 20.04 Add workaround to grub-efi-amd64-signed package See [1][2] [1] https://askubuntu.com/questions/1276111/error-upgrading-grub-efi-amd64-signed-special-device-old-ssd-does-not-exist [2] https://forum.hestiacp.com/t/ubuntu-20-04-installation-error/4323 --- .github/workflows/functional-baremetal.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index e19420776f..a6cd1c9ab4 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -40,6 +40,10 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v4 + - name: Workaround for grub-efi-amd64-signed + run: sudo apt update && sudo apt -y upgrade + shell: bash + if: ${{ matrix.ubuntu_version == "20.04" }} - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: From 8962bc80c422cf4c8c5650a47ae21ea5f566fd8c Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Fri, 13 Oct 2023 10:57:18 -0300 Subject: [PATCH 1690/2296] Revert "Fix baremetal jobs on Ubuntu 20.04" --- .github/workflows/functional-baremetal.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index a6cd1c9ab4..e19420776f 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -40,10 +40,6 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v4 - - name: Workaround for grub-efi-amd64-signed - run: sudo apt update && sudo apt -y upgrade - shell: bash - if: ${{ matrix.ubuntu_version == "20.04" }} - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: From b9a2337f792da82e945f08bd60ac2c77fc996551 Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Fri, 13 Oct 2023 10:02:13 -0400 Subject: [PATCH 1691/2296] Add workaround for baremetal jobs on ubuntu 20.04 This commit adds a workaround to baremetal jobs running on ubuntu 20.04 since they are failling when installing grub-efi-amd64-signed --- .github/workflows/functional-baremetal.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index e19420776f..18de39ec85 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -40,6 +40,14 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v4 + - name: Workaround for grub-efi-amd64-signed + run: sudo rm /var/cache/debconf/config.dat + shell: bash + if: matrix.ubuntu_version == '20.04' + - name: Ensure update and upgrade + run: sudo apt update && sudo apt -y upgrade + shell: bash + if: matrix.ubuntu_version == '20.04' - name: Deploy devstack uses: EmilienM/devstack-action@v0.11 with: From 8b13ac932d1d9e591f8fe36f8f13d9d13e5dd626 Mon Sep 17 00:00:00 2001 From: Iury Gregory Melo Ferreira Date: Fri, 6 Oct 2023 08:52:21 -0400 Subject: [PATCH 1692/2296] Support Firmware Interface * Driver updated to support - default_firmware_interface and enabled_firmware_interfaces * Node updated to support - firmware_interface field can be specified - List the Firmware Components requires API 1.86 Acceptance tests added - show that the node has the field FirmwareInterface and that returns no Firmware Components since is ipmi. --- .../baremetal/httpbasic/nodes_test.go | 20 ++++++ .../openstack/baremetal/v1/nodes_test.go | 19 +++++ openstack/baremetal/v1/drivers/results.go | 7 ++ .../v1/drivers/testing/fixtures_test.go | 6 ++ openstack/baremetal/v1/nodes/requests.go | 13 ++++ openstack/baremetal/v1/nodes/results.go | 36 ++++++++++ .../v1/nodes/testing/fixtures_test.go | 71 +++++++++++++++++++ .../v1/nodes/testing/requests_test.go | 12 ++++ openstack/baremetal/v1/nodes/urls.go | 4 ++ 9 files changed, 188 insertions(+) diff --git a/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go index 993a3ca227..5acaac6ffd 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go @@ -97,3 +97,23 @@ func TestNodesRAIDConfig(t *testing.T) { }).ExtractErr() th.AssertNoErr(t, err) } + +func TestNodesFirmwareInterface(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/2023.2") + clients.RequireLong(t) + clients.RequireIronicHTTPBasic(t) + + client, err := clients.NewBareMetalV1HTTPBasic() + th.AssertNoErr(t, err) + client.Microversion = "1.86" + + node, err := v1.CreateNode(t, client) + th.AssertNoErr(t, err) + defer v1.DeleteNode(t, client, node) + + th.AssertEquals(t, node.FirmwareInterface, "no-firmware") + + nodeFirmwareCmps, err := nodes.ListFirmware(client, node.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, nodeFirmwareCmps, []nodes.FirmwareComponent{}) +} diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index af79bfd5d7..75d04f1aff 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -148,3 +148,22 @@ func TestNodesRAIDConfig(t *testing.T) { }).ExtractErr() th.AssertNoErr(t, err) } + +func TestNodesFirmwareInterface(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/2023.2") + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.86" + + node, err := CreateNode(t, client) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node) + + th.AssertEquals(t, node.FirmwareInterface, "no-firmware") + + nodeFirmwareCmps, err := nodes.ListFirmware(client, node.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, nodeFirmwareCmps, []nodes.FirmwareComponent{}) +} diff --git a/openstack/baremetal/v1/drivers/results.go b/openstack/baremetal/v1/drivers/results.go index 8ef42459d1..ee1fff37bd 100644 --- a/openstack/baremetal/v1/drivers/results.go +++ b/openstack/baremetal/v1/drivers/results.go @@ -51,6 +51,10 @@ type Driver struct { // if no deploy interface is specified for the node. DefaultDeployInterface string `json:"default_deploy_interface"` + // The default firmware interface used for a node with a dynamic driver, + // if no firmware interface is specified for the node. + DefaultFirmwareInterface string `json:"default_firmware_interface"` + // The default inspection interface used for a node with a dynamic driver, // if no inspection interface is specified for the node. DefaultInspectInterface string `json:"default_inspect_interface"` @@ -95,6 +99,9 @@ type Driver struct { // The enabled deploy interfaces for this driver. EnabledDeployInterfaces []string `json:"enabled_deploy_interfaces"` + // The enabled firmware interfaces for this driver. + EnabledFirmwareInterfaces []string `json:"enabled_firmware_interfaces"` + // The enabled inspection interfaces for this driver. EnabledInspectInterfaces []string `json:"enabled_inspect_interfaces"` diff --git a/openstack/baremetal/v1/drivers/testing/fixtures_test.go b/openstack/baremetal/v1/drivers/testing/fixtures_test.go index a785b905c1..e3e79ef97d 100644 --- a/openstack/baremetal/v1/drivers/testing/fixtures_test.go +++ b/openstack/baremetal/v1/drivers/testing/fixtures_test.go @@ -104,6 +104,7 @@ const SingleDriverDetails = ` "default_boot_interface": "pxe", "default_console_interface": "no-console", "default_deploy_interface": "iscsi", + "default_firmware_interface": "no-firmware", "default_inspect_interface": "no-inspect", "default_management_interface": "ipmitool", "default_network_interface": "flat", @@ -125,6 +126,9 @@ const SingleDriverDetails = ` "iscsi", "direct" ], + "enabled_firmware_interfaces": [ + "no-firmware" + ], "enabled_inspect_interfaces": [ "no-inspect" ], @@ -281,6 +285,7 @@ var ( DefaultBootInterface: "pxe", DefaultConsoleInterface: "no-console", DefaultDeployInterface: "iscsi", + DefaultFirmwareInterface: "no-firmware", DefaultInspectInterface: "no-inspect", DefaultManagementInterface: "ipmitool", DefaultNetworkInterface: "flat", @@ -293,6 +298,7 @@ var ( EnabledBootInterfaces: []string{"pxe"}, EnabledConsoleInterface: []string{"no-console"}, EnabledDeployInterfaces: []string{"iscsi", "direct"}, + EnabledFirmwareInterfaces: []string{"no-firmware"}, EnabledInspectInterfaces: []string{"no-inspect"}, EnabledManagementInterfaces: []string{"ipmitool"}, EnabledNetworkInterfaces: []string{"flat", "noop"}, diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 76b8b73b92..d12a42023d 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -211,6 +211,9 @@ type CreateOpts struct { // A set of one or more arbitrary metadata key and value pairs. Extra map[string]interface{} `json:"extra,omitempty"` + // The firmware interface for a node, e.g. "redfish" + FirmwareInterface string `json:"firmware_interface,omitempty"` + // The interface used for node inspection, e.g. “no-inspect”. InspectInterface string `json:"inspect_interface,omitempty"` @@ -411,6 +414,7 @@ type StepInterface string const ( InterfaceBIOS StepInterface = "bios" InterfaceDeploy StepInterface = "deploy" + InterfaceFirmware StepInterface = "firmware" InterfaceManagement StepInterface = "management" InterfacePower StepInterface = "power" InterfaceRAID StepInterface = "raid" @@ -889,3 +893,12 @@ func GetInventory(client *gophercloud.ServiceClient, id string) (r InventoryResu _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// ListFirmware return the list of Firmware components for the given Node. +func ListFirmware(client *gophercloud.ServiceClient, id string) (r ListFirmwareResult) { + resp, err := client.Get(firmwareListURL(client, id), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, 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 9f9007fe3e..622d8b694b 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -199,6 +199,9 @@ type Node struct { // Deploy interface for a node, e.g. “iscsi”. DeployInterface string `json:"deploy_interface"` + // Firmware interface for a node, e.g. “redfish”. + FirmwareInterface string `json:"firmware_interface"` + // Interface used for node inspection, e.g. “no-inspect”. InspectInterface string `json:"inspect_interface"` @@ -405,6 +408,7 @@ type NodeValidation struct { Boot DriverValidation `json:"boot"` Console DriverValidation `json:"console"` Deploy DriverValidation `json:"deploy"` + Firmware DriverValidation `json:"firmware"` Inspect DriverValidation `json:"inspect"` Management DriverValidation `json:"management"` Network DriverValidation `json:"network"` @@ -606,3 +610,35 @@ func (r InventoryResult) Extract() (*InventoryData, error) { err := r.ExtractInto(&data) return &data, err } + +// ListFirmwareResult is the response from a ListFirmware operation. Call its Extract method +// to interpret it as an array of FirmwareComponent structs. +type ListFirmwareResult struct { + gophercloud.Result +} + +// A particular Firmware Component for a node +type FirmwareComponent struct { + // 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"` + // The Component name + Component string `json:"component"` + // The initial version of the firmware component. + InitialVersion string `json:"initial_version"` + // The current version of the firmware component. + CurrentVersion string `json:"current_version"` + // The last firmware version updated for the component. + LastVersionFlashed string `json:"last_version_flashed,omitempty"` +} + +// Extract interprets a ListFirmwareResult as an array of FirmwareComponent structs, if possible. +func (r ListFirmwareResult) Extract() ([]FirmwareComponent, error) { + var s struct { + Components []FirmwareComponent `json:"firmware"` + } + + err := r.ExtractInto(&s) + return s.Components, err +} diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index b9cb3fbc1c..ce7b877b3e 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -100,6 +100,7 @@ const NodeListDetailBody = ` "driver_internal_info": {}, "extra": {}, "fault": null, + "firmware_interface": "no-firmware", "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, @@ -197,6 +198,7 @@ const NodeListDetailBody = ` "driver_internal_info": {}, "extra": {}, "fault": null, + "firmware_interface": "no-firmware", "inspect_interface": "no-inspect", "inspection_finished_at": "2023-02-02T14:45:59.705249Z", "inspection_started_at": "2023-02-02T14:35:59.682403Z", @@ -294,6 +296,7 @@ const NodeListDetailBody = ` "driver_internal_info": {}, "extra": {}, "fault": null, + "firmware_interface": "no-firmware", "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, @@ -404,6 +407,7 @@ const SingleNodeBody = ` "driver_internal_info": {}, "extra": {}, "fault": null, + "firmware_interface": "no-firmware", "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, @@ -504,6 +508,10 @@ const NodeValidationBody = ` "reason": "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", "result": false }, + "firmware": { + "reason": "Driver ipmi does not support firmware (disabled or not implemented).", + "result": false + }, "inspect": { "reason": "Driver ipmi does not support inspect (disabled or not implemented).", "result": false @@ -828,6 +836,29 @@ var NodeInventoryBody = fmt.Sprintf(` } `, inventorytest.InventorySample) +const NodeFirmwareListBody = ` +{ + "firmware": [ + { + "created_at": "2023-10-03T18:30:00+00:00", + "updated_at": null, + "component": "bios", + "initial_version": "U30 v2.36 (07/16/2020)", + "current_version": "U30 v2.36 (07/16/2020)", + "last_version_flashed": null + }, + { + "created_at": "2023-10-03T18:30:00+00:00", + "updated_at": "2023-10-03T18:45:54+00:00", + "component": "bmc", + "initial_version": "iLO 5 v2.78", + "current_version": "iLO 5 v2.81", + "last_version_flashed": "iLO 5 v2.81" + } + ] +} +` + 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") @@ -872,6 +903,7 @@ var ( BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", + FirmwareInterface: "no-firmware", InspectInterface: "no-inspect", ManagementInterface: "ipmitool", NetworkInterface: "flat", @@ -906,6 +938,10 @@ var ( Result: false, Reason: "Cannot validate image information for node a62b8495-52e2-407b-b3cb-62775d04c2b8 because one or more parameters are missing from its instance_info and insufficent information is present to boot from a remote volume. Missing are: ['ramdisk', 'kernel', 'image_source']", }, + Firmware: nodes.DriverValidation{ + Result: false, + Reason: "Driver ipmi does not support firmware (disabled or not implemented).", + }, Inspect: nodes.DriverValidation{ Result: false, Reason: "Driver ipmi does not support inspect (disabled or not implemented).", @@ -975,6 +1011,7 @@ var ( BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", + FirmwareInterface: "no-firmware", InspectInterface: "no-inspect", ManagementInterface: "ipmitool", NetworkInterface: "flat", @@ -1023,6 +1060,7 @@ var ( BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", + FirmwareInterface: "no-firmware", InspectInterface: "no-inspect", ManagementInterface: "ipmitool", NetworkInterface: "flat", @@ -1192,6 +1230,28 @@ var ( NodeInventoryData = nodes.InventoryData{ Inventory: inventorytest.Inventory, } + + createdAtFirmware, _ = time.Parse(time.RFC3339, "2023-10-03T18:30:00+00:00") + updatedAtFirmware, _ = time.Parse(time.RFC3339, "2023-10-03T18:45:54+00:00") + lastVersion = "iLO 5 v2.81" + NodeFirmwareList = []nodes.FirmwareComponent{ + { + CreatedAt: createdAtFirmware, + UpdatedAt: nil, + Component: "bios", + InitialVersion: "U30 v2.36 (07/16/2020)", + CurrentVersion: "U30 v2.36 (07/16/2020)", + LastVersionFlashed: "", + }, + { + CreatedAt: createdAtFirmware, + UpdatedAt: &updatedAtFirmware, + Component: "bmc", + InitialVersion: "iLO 5 v2.78", + CurrentVersion: "iLO 5 v2.81", + LastVersionFlashed: lastVersion, + }, + } ) // HandleNodeListSuccessfully sets up the test server to respond to a server List request. @@ -1244,6 +1304,7 @@ func HandleNodeCreationSuccessfully(t *testing.T, response string) { "ipmi_port": "6230", "ipmi_username": "admin" }, + "firmware_interface": "no-firmware", "name": "foo" }`) @@ -1597,3 +1658,13 @@ func HandleGetInventorySuccessfully(t *testing.T) { fmt.Fprintf(w, NodeInventoryBody) }) } + +// HandleListFirmware +func HandleListFirmwareSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/firmware", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, NodeFirmwareListBody) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index bb0b6ff432..50397dd666 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -104,6 +104,7 @@ func TestCreateNode(t *testing.T) { "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, + FirmwareInterface: "no-firmware", }).Extract() th.AssertNoErr(t, err) @@ -730,3 +731,14 @@ func TestGetInventory(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, "x86_64", compatData.CPUArch) } + +func TestListFirmware(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListFirmwareSuccessfully(t) + + c := client.ServiceClient() + actual, err := nodes.ListFirmware(c, "1234asdf").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, NodeFirmwareList, actual) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index f2f002c333..1e509dd088 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -81,3 +81,7 @@ func maintenanceURL(client *gophercloud.ServiceClient, id string) string { func inventoryURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "inventory") } + +func firmwareListURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "firmware") +} From 019424e5ec54f1a2fb02ee25862203a77cc76688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 20 Oct 2023 14:27:29 +0200 Subject: [PATCH 1693/2296] 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 d356f52e16b160e1fbaeece7904779a76501827d Mon Sep 17 00:00:00 2001 From: David Verbeiren Date: Tue, 17 Oct 2023 12:13:21 +0200 Subject: [PATCH 1694/2296] orchestration: fix child template baseURL Applying filepath.Dir() to a URL used to give the expected result in go1.18. But with more recent versions, the library function cleans the provided path and removes any duplicate path separator. As a result, filepath.Dir() applied to "file:///some_abs_path/here" will give "file:/some_abs_path" instead of "file:///some_abs_path". This commit ensures filepath.Dir() is only applied to the Path element of the URL by first Parse()ing the URL. Signed-off-by: David Verbeiren --- openstack/orchestration/v1/stacks/template.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index 128f4f2369..8fc56035cc 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -2,6 +2,7 @@ package stacks import ( "fmt" + "net/url" "path/filepath" "reflect" "strings" @@ -81,7 +82,13 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool // get the base location of the child template. Child path is relative // to its parent location so that templates can be composed if t.URL != "" { - childTemplate.baseURL = filepath.Dir(t.URL) + // Preserve all elements of the URL but take the directory part of the path + u, err := url.Parse(t.URL) + if err != nil { + return err + } + u.Path = filepath.Dir(u.Path) + childTemplate.baseURL = u.String() } childTemplate.URL = value childTemplate.client = t.client From 504e0c1a6b218a6f738a5593e401b433c14a8f5c Mon Sep 17 00:00:00 2001 From: David Verbeiren Date: Tue, 17 Oct 2023 14:56:24 +0200 Subject: [PATCH 1695/2296] orchestration: getFileContents() into smaller funcs Refactor getFileContents() into smaller functions to make it easier to modify in a follow-up commit that will replace fixFileRefs() by something more appropriate for replacing child-template paths. Signed-off-by: David Verbeiren --- openstack/orchestration/v1/stacks/template.go | 143 ++++++++++-------- 1 file changed, 84 insertions(+), 59 deletions(-) diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index 8fc56035cc..430cfeedfa 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -40,6 +40,88 @@ func (t *Template) Validate() error { return ErrInvalidTemplateFormatVersion{Version: invalid} } +func (t *Template) makeChildTemplate(childURL string, ignoreIf igFunc, recurse bool) (*Template, error) { + // create a new child template + childTemplate := new(Template) + + // initialize child template + + // get the base location of the child template. Child path is relative + // to its parent location so that templates can be composed + if t.URL != "" { + // Preserve all elements of the URL but take the directory part of the path + u, err := url.Parse(t.URL) + if err != nil { + return nil, err + } + u.Path = filepath.Dir(u.Path) + childTemplate.baseURL = u.String() + } + childTemplate.URL = childURL + childTemplate.client = t.client + + // fetch the contents of the child template or file + if err := childTemplate.Fetch(); err != nil { + return nil, err + } + + // process child template recursively if required. This is + // required if the child template itself contains references to + // other templates + if recurse { + if err := childTemplate.Parse(); err == nil { + if err := childTemplate.Validate(); err == nil { + if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil { + return nil, err + } + childTemplate.fixFileRefs() + } + } + } + + return childTemplate, nil +} + +// Applies the transformation for getFileContents() to just one element of a map. +// In case the element requires transforming, the function returns its new value. +func (t *Template) mapElemFileContents(k interface{}, v interface{}, ignoreIf igFunc, recurse bool) error { + key, ok := k.(string) + if !ok { + return fmt.Errorf("can't convert map key to string: %v", k) + } + + value, ok := v.(string) + if !ok { + // if the value is not a string, recursively parse that value + if err := t.getFileContents(v, ignoreIf, recurse); err != nil { + return err + } + } else if !ignoreIf(key, value) { + // at this point, the k, v pair has a reference to an external template + // or file (for 'get_file' function). + // The assumption of heatclient is that value v is a reference + // to a file in the users environment, so we have to the path + + // create a new child template with the referenced contents + childTemplate, err := t.makeChildTemplate(value, ignoreIf, recurse) + if err != nil { + return err + } + + // update parent template with current child templates' content. + // At this point, the child template has been parsed recursively. + t.fileMaps[value] = childTemplate.URL + t.Files[childTemplate.URL] = string(childTemplate.Bin) + + // Also add child templates' own children (templates or get_file)! + for k, v := range childTemplate.Files { + t.Files[k] = v + } + } + + return nil +} + // GetFileContents recursively parses a template to search for urls. These urls // are assumed to point to other templates (known in OpenStack Heat as child // templates). The contents of these urls are fetched and stored in the `Files` @@ -62,65 +144,8 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool return err } for k, v := range teMap { - value, ok := v.(string) - if !ok { - // if the value is not a string, recursively parse that value - if err := t.getFileContents(v, ignoreIf, recurse); err != nil { - return err - } - } else if !ignoreIf(k, value) { - // at this point, the k, v pair has a reference to an external template - // or file (for 'get_file' function). - // The assumption of heatclient is that value v is a reference - // to a file in the users environment - - // create a new child template - childTemplate := new(Template) - - // initialize child template - - // get the base location of the child template. Child path is relative - // to its parent location so that templates can be composed - if t.URL != "" { - // Preserve all elements of the URL but take the directory part of the path - u, err := url.Parse(t.URL) - if err != nil { - return err - } - u.Path = filepath.Dir(u.Path) - childTemplate.baseURL = u.String() - } - childTemplate.URL = value - childTemplate.client = t.client - - // fetch the contents of the child template or file - if err := childTemplate.Fetch(); err != nil { - return err - } - - // process child template recursively if required. This is - // required if the child template itself contains references to - // other templates - if recurse { - if err := childTemplate.Parse(); err == nil { - if err := childTemplate.Validate(); err == nil { - if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil { - return err - } - childTemplate.fixFileRefs() - } - } - } - - // update parent template with current child templates' content. - // At this point, the child template has been parsed recursively. - t.fileMaps[value] = childTemplate.URL - t.Files[childTemplate.URL] = string(childTemplate.Bin) - - // Also add child templates' own children (templates or get_file)! - for k, v := range childTemplate.Files { - t.Files[k] = v - } + if err := t.mapElemFileContents(k, v, ignoreIf, recurse); err != nil { + return err } } return nil From 219b96ea8436f7bf7ba3cfbdbb22abc0a2985708 Mon Sep 17 00:00:00 2001 From: David Verbeiren Date: Tue, 17 Oct 2023 15:41:53 +0200 Subject: [PATCH 1696/2296] orchestration: stack template tests cleanup Simple cleanup of the stack template tests: * Move long duplicated template into a constant * Move function invocations into their AssertNoErr() when the resulting line is not too long (reduces number of lines for the tests, making their flow and intent more visible) Signed-off-by: David Verbeiren --- .../orchestration/v1/stacks/template_test.go | 76 +++++++------------ 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go index 96a061e579..c5d334c3e6 100644 --- a/openstack/orchestration/v1/stacks/template_test.go +++ b/openstack/orchestration/v1/stacks/template_test.go @@ -13,17 +13,15 @@ import ( func TestTemplateValidation(t *testing.T) { templateJSON := new(Template) templateJSON.Bin = []byte(ValidJSONTemplate) - err := templateJSON.Validate() - th.AssertNoErr(t, err) + th.AssertNoErr(t, templateJSON.Validate()) templateYAML := new(Template) templateYAML.Bin = []byte(ValidYAMLTemplate) - err = templateYAML.Validate() - th.AssertNoErr(t, err) + th.AssertNoErr(t, templateYAML.Validate()) templateInvalid := new(Template) templateInvalid.Bin = []byte(InvalidTemplateNoVersion) - if err = templateInvalid.Validate(); err == nil { + if err := templateInvalid.Validate(); err == nil { t.Error("Template validation did not catch invalid template") } } @@ -31,19 +29,17 @@ func TestTemplateValidation(t *testing.T) { func TestTemplateParsing(t *testing.T) { templateJSON := new(Template) templateJSON.Bin = []byte(ValidJSONTemplate) - err := templateJSON.Parse() - th.AssertNoErr(t, err) + th.AssertNoErr(t, templateJSON.Parse()) th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateJSON.Parsed) templateYAML := new(Template) templateYAML.Bin = []byte(ValidJSONTemplate) - err = templateYAML.Parse() - th.AssertNoErr(t, err) + th.AssertNoErr(t, templateYAML.Parse()) th.AssertDeepEquals(t, ValidJSONTemplateParsed, templateYAML.Parsed) templateInvalid := new(Template) templateInvalid.Bin = []byte("Keep Austin Weird") - err = templateInvalid.Parse() + err := templateInvalid.Parse() if err == nil { t.Error("Template parsing did not catch invalid template") } @@ -73,15 +69,7 @@ func TestIgnoreIfTemplate(t *testing.T) { } } -func TestGetFileContentsWithType(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - baseurl, err := getBasePath() - th.AssertNoErr(t, err) - fakeURL := strings.Join([]string{baseurl, "my_nova.yaml"}, "/") - urlparsed, err := url.Parse(fakeURL) - th.AssertNoErr(t, err) - myNovaContent := `heat_template_version: 2014-10-16 +const myNovaContent = `heat_template_version: 2014-10-16 parameters: flavor: type: string @@ -97,11 +85,20 @@ resources: image: Debian 7 (Wheezy) (PVHVM) networks: - {uuid: 11111111-1111-1111-1111-111111111111}` + +func TestGetFileContentsWithType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + baseurl, err := getBasePath() + th.AssertNoErr(t, err) + fakeURL := strings.Join([]string{baseurl, "my_nova.yaml"}, "/") + urlparsed, err := url.Parse(fakeURL) + th.AssertNoErr(t, err) th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, myNovaContent) + fmt.Fprint(w, myNovaContent) }) client := fakeClient{BaseClient: getHTTPClient()} @@ -112,10 +109,9 @@ resources: type: my_nova.yaml`) te.client = client - err = te.Parse() - th.AssertNoErr(t, err) - err = te.getFileContents(te.Parsed, ignoreIfTemplate, true) - th.AssertNoErr(t, err) + th.AssertNoErr(t, te.Parse()) + th.AssertNoErr(t, te.getFileContents(te.Parsed, ignoreIfTemplate, true)) + expectedFiles := map[string]string{ "my_nova.yaml": `heat_template_version: 2014-10-16 parameters: @@ -160,7 +156,7 @@ func TestGetFileContentsWithFile(t *testing.T) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, somefile) + fmt.Fprint(w, somefile) }) client := fakeClient{BaseClient: getHTTPClient()} @@ -173,10 +169,8 @@ resources: value: {get_file: somefile }`) te.client = client - err = te.Parse() - th.AssertNoErr(t, err) - err = te.getFileContents(te.Parsed, ignoreIfTemplate, true) - th.AssertNoErr(t, err) + th.AssertNoErr(t, te.Parse()) + th.AssertNoErr(t, te.getFileContents(te.Parsed, ignoreIfTemplate, true)) expectedFiles := map[string]string{ "somefile": "Welcome!", } @@ -209,27 +203,11 @@ func TestGetFileContentsComposeRelativePath(t *testing.T) { novaURL := strings.Join([]string{baseurl, novaPath}, "/") novaURLParse, err := url.Parse(novaURL) th.AssertNoErr(t, err) - myNovaContent := `heat_template_version: 2014-10-16 -parameters: - flavor: - type: string - description: Flavor for the server to be created - default: 4353 - hidden: true -resources: - test_server: - type: "OS::Nova::Server" - properties: - name: test-server - flavor: 2 GB General Purpose v1 - image: Debian 7 (Wheezy) (PVHVM) - networks: - - {uuid: 11111111-1111-1111-1111-111111111111}` th.Mux.HandleFunc(novaURLParse.Path, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, myNovaContent) + fmt.Fprint(w, myNovaContent) }) subStacksPath := strings.Join([]string{"substacks", "my_substack.yaml"}, "/") @@ -263,10 +241,8 @@ resources: type: substacks/my_substack.yaml`) te.client = client - err = te.Parse() - th.AssertNoErr(t, err) - err = te.getFileContents(te.Parsed, ignoreIfTemplate, true) - th.AssertNoErr(t, err) + th.AssertNoErr(t, te.Parse()) + th.AssertNoErr(t, te.getFileContents(te.Parsed, ignoreIfTemplate, true)) expectedFiles := map[string]string{ "templates/my_nova.yaml": myNovaContent, From c96d1c895fc2f83c24461655e8f515c90d8631fd Mon Sep 17 00:00:00 2001 From: David Verbeiren Date: Tue, 17 Oct 2023 16:17:53 +0200 Subject: [PATCH 1697/2296] orchestration: reduce duplication in staks tests Reduce duplication and make tests shorter by consolidating all Mux.HandleFunc(...) invocations into a serveFile() convenience function. Signed-off-by: David Verbeiren --- .../v1/stacks/environment_test.go | 29 ++--------- .../orchestration/v1/stacks/template_test.go | 49 ++++--------------- testhelper/http_responses.go | 16 ++++++ 3 files changed, 28 insertions(+), 66 deletions(-) diff --git a/openstack/orchestration/v1/stacks/environment_test.go b/openstack/orchestration/v1/stacks/environment_test.go index 6fcc230d43..4737b0bd25 100644 --- a/openstack/orchestration/v1/stacks/environment_test.go +++ b/openstack/orchestration/v1/stacks/environment_test.go @@ -1,10 +1,6 @@ package stacks import ( - "fmt" - "net/http" - "net/url" - "strings" "testing" th "github.com/gophercloud/gophercloud/testhelper" @@ -132,28 +128,9 @@ service_db: baseurl, err := getBasePath() th.AssertNoErr(t, err) - fakeEnvURL := strings.Join([]string{baseurl, "my_env.yaml"}, "/") - urlparsed, err := url.Parse(fakeEnvURL) - th.AssertNoErr(t, err) - // handler for my_env.yaml - th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, environmentContent) - }) - - fakeDBURL := strings.Join([]string{baseurl, "my_db.yaml"}, "/") - urlparsed, err = url.Parse(fakeDBURL) - th.AssertNoErr(t, err) - - // handler for my_db.yaml - th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, dbContent) - }) + // Serve "my_env.yaml" and "my_db.yaml" + fakeEnvURL := th.ServeFile(t, baseurl, "my_env.yaml", "application/json", environmentContent) + fakeDBURL := th.ServeFile(t, baseurl, "my_db.yaml", "application/json", dbContent) client := fakeClient{BaseClient: getHTTPClient()} env := new(Environment) diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go index c5d334c3e6..0a218cfe9c 100644 --- a/openstack/orchestration/v1/stacks/template_test.go +++ b/openstack/orchestration/v1/stacks/template_test.go @@ -2,8 +2,6 @@ package stacks import ( "fmt" - "net/http" - "net/url" "strings" "testing" @@ -91,15 +89,8 @@ func TestGetFileContentsWithType(t *testing.T) { defer th.TeardownHTTP() baseurl, err := getBasePath() th.AssertNoErr(t, err) - fakeURL := strings.Join([]string{baseurl, "my_nova.yaml"}, "/") - urlparsed, err := url.Parse(fakeURL) - th.AssertNoErr(t, err) - th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, myNovaContent) - }) + + fakeURL := th.ServeFile(t, baseurl, "my_nova.yaml", "application/json", myNovaContent) client := fakeClient{BaseClient: getHTTPClient()} te := new(Template) @@ -148,16 +139,9 @@ func TestGetFileContentsWithFile(t *testing.T) { defer th.TeardownHTTP() baseurl, err := getBasePath() th.AssertNoErr(t, err) - fakeURL := strings.Join([]string{baseurl, "somefile"}, "/") - urlparsed, err := url.Parse(fakeURL) - th.AssertNoErr(t, err) + somefile := `Welcome!` - th.Mux.HandleFunc(urlparsed.Path, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, somefile) - }) + fakeURL := th.ServeFile(t, baseurl, "somefile", "text/plain", somefile) client := fakeClient{BaseClient: getHTTPClient()} te := new(Template) @@ -200,20 +184,8 @@ func TestGetFileContentsComposeRelativePath(t *testing.T) { th.AssertNoErr(t, err) novaPath := strings.Join([]string{"templates", "my_nova.yaml"}, "/") - novaURL := strings.Join([]string{baseurl, novaPath}, "/") - novaURLParse, err := url.Parse(novaURL) - th.AssertNoErr(t, err) - th.Mux.HandleFunc(novaURLParse.Path, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, myNovaContent) - }) + novaURL := th.ServeFile(t, baseurl, novaPath, "application/json", myNovaContent) - subStacksPath := strings.Join([]string{"substacks", "my_substack.yaml"}, "/") - subStackURL := strings.Join([]string{baseurl, subStacksPath}, "/") - subStackURLParsed, err := url.Parse(subStackURL) - th.AssertNoErr(t, err) mySubStackContentFmt := `heat_template_version: 2015-04-30 resources: my_server: @@ -226,12 +198,9 @@ resources: image: Debian 7 (Wheezy) (PVHVM) networks: - {uuid: 11111111-1111-1111-1111-111111111111}` - th.Mux.HandleFunc(subStackURLParsed.Path, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, mySubStackContentFmt, "../templates/my_nova.yaml") - }) + subStacksPath := strings.Join([]string{"substacks", "my_substack.yaml"}, "/") + subStackURL := th.ServeFile(t, baseurl, subStacksPath, "application/json", + fmt.Sprintf(mySubStackContentFmt, "../templates/my_nova.yaml")) client := fakeClient{BaseClient: getHTTPClient()} te := new(Template) @@ -246,7 +215,7 @@ resources: expectedFiles := map[string]string{ "templates/my_nova.yaml": myNovaContent, - "substacks/my_substack.yaml": fmt.Sprintf(mySubStackContentFmt, novaURLParse), + "substacks/my_substack.yaml": fmt.Sprintf(mySubStackContentFmt, novaURL), } th.AssertEquals(t, expectedFiles[novaPath], te.Files[novaURL]) th.AssertEquals(t, expectedFiles[subStacksPath], te.Files[subStackURL]) diff --git a/testhelper/http_responses.go b/testhelper/http_responses.go index 366625d849..6f8f2db4b1 100644 --- a/testhelper/http_responses.go +++ b/testhelper/http_responses.go @@ -9,6 +9,7 @@ import ( "net/http/httptest" "net/url" "reflect" + "strings" "testing" ) @@ -48,6 +49,21 @@ func Endpoint() string { return Server.URL + "/" } +// Serves a static content at baseURL/relPath +func ServeFile(t *testing.T, baseURL, relPath, contentType, content string) string { + rawURL := strings.Join([]string{baseURL, relPath}, "/") + parsedURL, err := url.Parse(rawURL) + AssertNoErr(t, err) + Mux.HandleFunc(parsedURL.Path, func(w http.ResponseWriter, r *http.Request) { + TestMethod(t, r, "GET") + w.Header().Set("Content-Type", contentType) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, content) + }) + + return rawURL +} + // TestFormValues ensures that all the URL parameters given to the http.Request are the same as values. func TestFormValues(t *testing.T, r *http.Request, values map[string]string) { want := url.Values{} From a7cb532d67787521b5568894a242b4d045eb58a5 Mon Sep 17 00:00:00 2001 From: David Verbeiren Date: Tue, 17 Oct 2023 16:00:35 +0200 Subject: [PATCH 1698/2296] orchestration: Fix relative to abs file refs fixing Existing fixFileRefs() does a simple string replacement regardless of context. This is not appropriate as the same keyord could appear outside of the valid template/file reference cases ('type: ...' and 'get_file: ...'). Example: init-cmd: type: OS::Heat::CloudConfig properties: cloud_config: write_files: - path: /etc/somefile content: { get_file: somefile } Here "somefile" in "{ get_file: somefile }" should be replaced by the absolute path to somefile, but the "path: /etc/somefile" part should not be modified as that path refers here to the target destination in a completely different context (in this example, a cloud-init target path on a to-be-created VM). Signed-off-by: David Verbeiren --- openstack/orchestration/v1/stacks/requests.go | 4 - openstack/orchestration/v1/stacks/template.go | 62 +++++++--- .../orchestration/v1/stacks/template_test.go | 108 ++++++++++++------ 3 files changed, 117 insertions(+), 57 deletions(-) diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index 0bd16543b9..edd362cc24 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -52,7 +52,6 @@ func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) { if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { return nil, err } - opts.TemplateOpts.fixFileRefs() b["template"] = string(opts.TemplateOpts.Bin) files := make(map[string]string) @@ -146,7 +145,6 @@ func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) { if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { return nil, err } - opts.TemplateOpts.fixFileRefs() b["template"] = string(opts.TemplateOpts.Bin) files := make(map[string]string) @@ -371,7 +369,6 @@ func toStackUpdateMap(opts UpdateOpts) (map[string]interface{}, error) { if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { return nil, err } - opts.TemplateOpts.fixFileRefs() b["template"] = string(opts.TemplateOpts.Bin) for k, v := range opts.TemplateOpts.Files { @@ -479,7 +476,6 @@ func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) { if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil { return nil, err } - opts.TemplateOpts.fixFileRefs() b["template"] = string(opts.TemplateOpts.Bin) files := make(map[string]string) diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index 430cfeedfa..d2ddbb62fa 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/gophercloud/gophercloud" + yaml "gopkg.in/yaml.v2" ) // Template is a structure that represents OpenStack Heat templates @@ -74,7 +75,6 @@ func (t *Template) makeChildTemplate(childURL string, ignoreIf igFunc, recurse b if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil { return nil, err } - childTemplate.fixFileRefs() } } } @@ -84,17 +84,17 @@ func (t *Template) makeChildTemplate(childURL string, ignoreIf igFunc, recurse b // Applies the transformation for getFileContents() to just one element of a map. // In case the element requires transforming, the function returns its new value. -func (t *Template) mapElemFileContents(k interface{}, v interface{}, ignoreIf igFunc, recurse bool) error { +func (t *Template) mapElemFileContents(k interface{}, v interface{}, ignoreIf igFunc, recurse bool) (interface{}, error) { key, ok := k.(string) if !ok { - return fmt.Errorf("can't convert map key to string: %v", k) + return nil, fmt.Errorf("can't convert map key to string: %v", k) } value, ok := v.(string) if !ok { // if the value is not a string, recursively parse that value if err := t.getFileContents(v, ignoreIf, recurse); err != nil { - return err + return nil, err } } else if !ignoreIf(key, value) { // at this point, the k, v pair has a reference to an external template @@ -105,7 +105,7 @@ func (t *Template) mapElemFileContents(k interface{}, v interface{}, ignoreIf ig // create a new child template with the referenced contents childTemplate, err := t.makeChildTemplate(value, ignoreIf, recurse) if err != nil { - return err + return nil, err } // update parent template with current child templates' content. @@ -117,9 +117,11 @@ func (t *Template) mapElemFileContents(k interface{}, v interface{}, ignoreIf ig for k, v := range childTemplate.Files { t.Files[k] = v } + + return childTemplate.URL, nil } - return nil + return nil, nil } // GetFileContents recursively parses a template to search for urls. These urls @@ -136,33 +138,55 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool if t.fileMaps == nil { t.fileMaps = make(map[string]string) } - switch te.(type) { - // if te is a map - case map[string]interface{}, map[interface{}]interface{}: - teMap, err := toStringKeys(te) - if err != nil { - return err + + updated := false + + switch teTyped := (te).(type) { + // if te is a map[string], go check all elements for URLs to replace + case map[string]interface{}: + for k, v := range teTyped { + newVal, err := t.mapElemFileContents(k, v, ignoreIf, recurse) + if err != nil { + return err + } else if newVal != nil { + teTyped[k] = newVal + updated = true + } } - for k, v := range teMap { - if err := t.mapElemFileContents(k, v, ignoreIf, recurse); err != nil { + // same if te is a map[non-string] (can't group with above case because we + // can't range over and update 'te' without knowing its key type) + case map[interface{}]interface{}: + for k, v := range teTyped { + newVal, err := t.mapElemFileContents(k, v, ignoreIf, recurse) + if err != nil { return err + } else if newVal != nil { + teTyped[k] = newVal + updated = true } } - return nil // if te is a slice, call the function on each element of the slice. case []interface{}: - teSlice := te.([]interface{}) - for i := range teSlice { - if err := t.getFileContents(teSlice[i], ignoreIf, recurse); err != nil { + for i := range teTyped { + if err := t.getFileContents(teTyped[i], ignoreIf, recurse); err != nil { return err } } - // if te is anything else, return + // if te is anything else, there is nothing to do. case string, bool, float64, nil, int: return nil default: return gophercloud.ErrUnexpectedType{Actual: fmt.Sprintf("%v", reflect.TypeOf(te))} } + + // In case some element was updated, we have to regenerate the string representation + if updated { + var err error + t.Bin, err = yaml.Marshal(&t.Parsed) + if err != nil { + return fmt.Errorf("failed to marshal updated data: %w", err) + } + } return nil } diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go index 0a218cfe9c..7fc273b192 100644 --- a/openstack/orchestration/v1/stacks/template_test.go +++ b/openstack/orchestration/v1/stacks/template_test.go @@ -1,7 +1,6 @@ package stacks import ( - "fmt" "strings" "testing" @@ -84,6 +83,33 @@ resources: networks: - {uuid: 11111111-1111-1111-1111-111111111111}` +var myNovaExpected = map[string]interface{}{ + "heat_template_version": "2014-10-16", + "parameters": map[string]interface{}{ + "flavor": map[string]interface{}{ + "type": "string", + "description": "Flavor for the server to be created", + "default": 4353, + "hidden": true, + }, + }, + "resources": map[string]interface{}{ + "test_server": map[string]interface{}{ + "type": "OS::Nova::Server", + "properties": map[string]interface{}{ + "name": "test-server", + "flavor": "2 GB General Purpose v1", + "image": "Debian 7 (Wheezy) (PVHVM)", + "networks": []interface{}{ + map[string]interface{}{ + "uuid": "11111111-1111-1111-1111-111111111111", + }, + }, + }, + }, + }, +} + func TestGetFileContentsWithType(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -103,25 +129,7 @@ resources: th.AssertNoErr(t, te.Parse()) th.AssertNoErr(t, te.getFileContents(te.Parsed, ignoreIfTemplate, true)) - expectedFiles := map[string]string{ - "my_nova.yaml": `heat_template_version: 2014-10-16 -parameters: - flavor: - type: string - description: Flavor for the server to be created - default: 4353 - hidden: true -resources: - test_server: - type: "OS::Nova::Server" - properties: - name: test-server - flavor: 2 GB General Purpose v1 - image: Debian 7 (Wheezy) (PVHVM) - networks: - - {uuid: 11111111-1111-1111-1111-111111111111}`} - th.AssertEquals(t, expectedFiles["my_nova.yaml"], te.Files[fakeURL]) - te.fixFileRefs() + // Now check template and referenced file expectedParsed := map[string]interface{}{ "heat_template_version": "2015-04-30", "resources": map[string]interface{}{ @@ -132,6 +140,11 @@ resources: } th.AssertNoErr(t, te.Parse()) th.AssertDeepEquals(t, expectedParsed, te.Parsed) + + novaTe := new(Template) + novaTe.Bin = []byte(te.Files[fakeURL]) + th.AssertNoErr(t, novaTe.Parse()) + th.AssertDeepEquals(t, myNovaExpected, novaTe.Parsed) } func TestGetFileContentsWithFile(t *testing.T) { @@ -145,12 +158,16 @@ func TestGetFileContentsWithFile(t *testing.T) { client := fakeClient{BaseClient: getHTTPClient()} te := new(Template) + // Note: We include the path that should be replaced also as a not-to-be-replaced + // keyword ("path: somefile" below) to validate that no updates happen outside of + // the real local URLs (child templates (type:) or included files (get_file:)). te.Bin = []byte(`heat_template_version: 2015-04-30 resources: test_resource: type: OS::Heat::TestResource properties: - value: {get_file: somefile }`) + path: somefile + value: { get_file: somefile }`) te.client = client th.AssertNoErr(t, te.Parse()) @@ -159,13 +176,13 @@ resources: "somefile": "Welcome!", } th.AssertEquals(t, expectedFiles["somefile"], te.Files[fakeURL]) - te.fixFileRefs() expectedParsed := map[string]interface{}{ "heat_template_version": "2015-04-30", "resources": map[string]interface{}{ "test_resource": map[string]interface{}{ "type": "OS::Heat::TestResource", "properties": map[string]interface{}{ + "path": "somefile", "value": map[string]interface{}{ "get_file": fakeURL, }, @@ -186,10 +203,10 @@ func TestGetFileContentsComposeRelativePath(t *testing.T) { novaPath := strings.Join([]string{"templates", "my_nova.yaml"}, "/") novaURL := th.ServeFile(t, baseurl, novaPath, "application/json", myNovaContent) - mySubStackContentFmt := `heat_template_version: 2015-04-30 + mySubStackContent := `heat_template_version: 2015-04-30 resources: my_server: - type: %s + type: ../templates/my_nova.yaml my_backend: type: "OS::Nova::Server" properties: @@ -198,9 +215,29 @@ resources: image: Debian 7 (Wheezy) (PVHVM) networks: - {uuid: 11111111-1111-1111-1111-111111111111}` + mySubstrackExpected := map[string]interface{}{ + "heat_template_version": "2015-04-30", + "resources": map[string]interface{}{ + "my_server": map[string]interface{}{ + "type": novaURL, + }, + "my_backend": map[string]interface{}{ + "type": "OS::Nova::Server", + "properties": map[string]interface{}{ + "name": "test-backend", + "flavor": "4 GB General Purpose v1", + "image": "Debian 7 (Wheezy) (PVHVM)", + "networks": []interface{}{ + map[string]interface{}{ + "uuid": "11111111-1111-1111-1111-111111111111", + }, + }, + }, + }, + }, + } subStacksPath := strings.Join([]string{"substacks", "my_substack.yaml"}, "/") - subStackURL := th.ServeFile(t, baseurl, subStacksPath, "application/json", - fmt.Sprintf(mySubStackContentFmt, "../templates/my_nova.yaml")) + subStackURL := th.ServeFile(t, baseurl, subStacksPath, "application/json", mySubStackContent) client := fakeClient{BaseClient: getHTTPClient()} te := new(Template) @@ -213,14 +250,6 @@ resources: th.AssertNoErr(t, te.Parse()) th.AssertNoErr(t, te.getFileContents(te.Parsed, ignoreIfTemplate, true)) - expectedFiles := map[string]string{ - "templates/my_nova.yaml": myNovaContent, - "substacks/my_substack.yaml": fmt.Sprintf(mySubStackContentFmt, novaURL), - } - th.AssertEquals(t, expectedFiles[novaPath], te.Files[novaURL]) - th.AssertEquals(t, expectedFiles[subStacksPath], te.Files[subStackURL]) - - te.fixFileRefs() expectedParsed := map[string]interface{}{ "heat_template_version": "2015-04-30", "resources": map[string]interface{}{ @@ -231,4 +260,15 @@ resources: } th.AssertNoErr(t, te.Parse()) th.AssertDeepEquals(t, expectedParsed, te.Parsed) + + expectedFiles := map[string]interface{}{ + novaURL: myNovaExpected, + subStackURL: mySubstrackExpected, + } + for path, expected := range expectedFiles { + checkTe := new(Template) + checkTe.Bin = []byte(te.Files[path]) + th.AssertNoErr(t, checkTe.Parse()) + th.AssertDeepEquals(t, expected, checkTe.Parsed) + } } From 257b069b7722ee0918ec7688deb44effbec5de72 Mon Sep 17 00:00:00 2001 From: David Verbeiren Date: Fri, 20 Oct 2023 17:21:44 +0200 Subject: [PATCH 1699/2296] orchestration: remove fixFileRefs() also for env An earlier commit removed the usage of fixFileRefs() for templates, by having the getFileContents() do the replacement directly on the parsed data structure as it goes through it and gathers referenced files. This commit does the same for environment files that also ultimately use the now improved getFileContents() function. As the fixFileRefs() was not used anywhere else, it has been removed, together with its test. Signed-off-by: David Verbeiren --- .../orchestration/v1/stacks/environment.go | 17 ++++++++++++++++- .../orchestration/v1/stacks/environment_test.go | 3 +-- openstack/orchestration/v1/stacks/requests.go | 4 ---- openstack/orchestration/v1/stacks/utils.go | 14 -------------- openstack/orchestration/v1/stacks/utils_test.go | 11 ----------- 5 files changed, 17 insertions(+), 32 deletions(-) diff --git a/openstack/orchestration/v1/stacks/environment.go b/openstack/orchestration/v1/stacks/environment.go index 86989186fa..f7bc155e06 100644 --- a/openstack/orchestration/v1/stacks/environment.go +++ b/openstack/orchestration/v1/stacks/environment.go @@ -1,6 +1,11 @@ package stacks -import "strings" +import ( + "fmt" + "strings" + + yaml "gopkg.in/yaml.v2" +) // Environment is a structure that represents stack environments type Environment struct { @@ -108,6 +113,16 @@ func (e *Environment) getRRFileContents(ignoreIf igFunc) error { // if the resource registry contained any URL's, store them. This can // then be passed as parameter to api calls to Heat api. e.Files = tempTemplate.Files + + // In case some element was updated, regenerate the string representation + if len(e.Files) > 0 { + var err error + e.Bin, err = yaml.Marshal(&e.Parsed) + if err != nil { + return fmt.Errorf("failed to marshal updated environment: %w", err) + } + } + return nil default: return nil diff --git a/openstack/orchestration/v1/stacks/environment_test.go b/openstack/orchestration/v1/stacks/environment_test.go index 4737b0bd25..9a69d833cb 100644 --- a/openstack/orchestration/v1/stacks/environment_test.go +++ b/openstack/orchestration/v1/stacks/environment_test.go @@ -152,7 +152,6 @@ service_db: "my_env.yaml": fakeEnvURL, "my_db.yaml": fakeDBURL, } - env.fixFileRefs() expectedParsed := map[string]interface{}{ "resource_registry": map[string]interface{}{ @@ -164,6 +163,6 @@ service_db: }, }, } - env.Parse() + th.AssertNoErr(t, env.Parse()) th.AssertDeepEquals(t, expectedParsed, env.Parsed) } diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index edd362cc24..41ca7063c0 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -66,7 +66,6 @@ func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) { if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil { return nil, err } - opts.EnvironmentOpts.fixFileRefs() for k, v := range opts.EnvironmentOpts.Files { files[k] = v } @@ -159,7 +158,6 @@ func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) { if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil { return nil, err } - opts.EnvironmentOpts.fixFileRefs() for k, v := range opts.EnvironmentOpts.Files { files[k] = v } @@ -383,7 +381,6 @@ func toStackUpdateMap(opts UpdateOpts) (map[string]interface{}, error) { if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil { return nil, err } - opts.EnvironmentOpts.fixFileRefs() for k, v := range opts.EnvironmentOpts.Files { files[k] = v } @@ -490,7 +487,6 @@ func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) { if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil { return nil, err } - opts.EnvironmentOpts.fixFileRefs() for k, v := range opts.EnvironmentOpts.Files { files[k] = v } diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go index 470c08f1c5..21a83f7e79 100644 --- a/openstack/orchestration/v1/stacks/utils.go +++ b/openstack/orchestration/v1/stacks/utils.go @@ -7,7 +7,6 @@ import ( "net/http" "path/filepath" "reflect" - "strings" "github.com/gophercloud/gophercloud" yaml "gopkg.in/yaml.v2" @@ -143,16 +142,3 @@ func toStringKeys(m interface{}) (map[string]interface{}, error) { return nil, gophercloud.ErrUnexpectedType{Expected: "map[string]interface{}/map[interface{}]interface{}", Actual: fmt.Sprintf("%v", reflect.TypeOf(m))} } } - -// fix the reference to files by replacing relative URL's by absolute -// URL's -func (t *TE) fixFileRefs() { - tStr := string(t.Bin) - if t.fileMaps == nil { - return - } - for k, v := range t.fileMaps { - tStr = strings.Replace(tStr, k, v, -1) - } - t.Bin = []byte(tStr) -} diff --git a/openstack/orchestration/v1/stacks/utils_test.go b/openstack/orchestration/v1/stacks/utils_test.go index b64e4dcef5..adc97d14aa 100644 --- a/openstack/orchestration/v1/stacks/utils_test.go +++ b/openstack/orchestration/v1/stacks/utils_test.go @@ -10,17 +10,6 @@ import ( th "github.com/gophercloud/gophercloud/testhelper" ) -func TestTEFixFileRefs(t *testing.T) { - te := TE{ - Bin: []byte(`string_to_replace: my fair lady`), - fileMaps: map[string]string{ - "string_to_replace": "london bridge is falling down", - }, - } - te.fixFileRefs() - th.AssertEquals(t, string(te.Bin), `london bridge is falling down: my fair lady`) -} - func TestToStringKeys(t *testing.T) { var test1 interface{} = map[interface{}]interface{}{ "Adam": "Smith", From bfad08478d787aadef6d2ddf04abe08a1f4a12b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 09:50:23 +0000 Subject: [PATCH 1700/2296] build(deps): bump thollander/actions-comment-pull-request Bumps [thollander/actions-comment-pull-request](https://github.com/thollander/actions-comment-pull-request) from 2.4.2 to 2.4.3. - [Release notes](https://github.com/thollander/actions-comment-pull-request/releases) - [Commits](https://github.com/thollander/actions-comment-pull-request/compare/d61db783da9abefc3437960d0cce08552c7c004f...1d3973dc4b8e1399c0620d3f2b1aa5e795465308) --- updated-dependencies: - dependency-name: thollander/actions-comment-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 5b46c9cf29..4e35d42bb2 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -50,7 +50,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:unknown') || contains(github.event.label.name, 'semver:major') || contains(github.event.label.name, 'semver:unknown') - uses: thollander/actions-comment-pull-request@d61db783da9abefc3437960d0cce08552c7c004f + uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 with: message: | Labels `semver-major` or `semver-unknown` can not trigger backports. From ad897c81094774e3e0220d4bc1b7b86b3972be5e Mon Sep 17 00:00:00 2001 From: Lennart Jern Date: Wed, 27 Sep 2023 15:01:57 +0300 Subject: [PATCH 1701/2296] Add microversion utilities This adds GetSupportedMicroversions, MicroversionSupported, RequireMicroversion and ParseMicroversion utility functions for working with microversions. The doc string for ChooseVersion is also updated to clarify how it works. --- openstack/utils/choose_version.go | 130 +++++++- .../utils/testing/choose_version_test.go | 305 ++++++++++++++++++ 2 files changed, 432 insertions(+), 3 deletions(-) diff --git a/openstack/utils/choose_version.go b/openstack/utils/choose_version.go index 27da19f91a..ed34197d2c 100644 --- a/openstack/utils/choose_version.go +++ b/openstack/utils/choose_version.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "strconv" "strings" "github.com/gophercloud/gophercloud" @@ -20,9 +21,12 @@ var goodStatus = map[string]bool{ "stable": true, } -// ChooseVersion queries the base endpoint of an API to choose the most recent non-experimental alternative from a service's -// published versions. -// It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint. +// ChooseVersion queries the base endpoint of an API to choose the identity service version. +// It will pick a version among the recognized, taking into account the priority and avoiding +// experimental alternatives from the published versions. However, if the client specifies a full +// endpoint that is among the recognized versions, it will be used regardless of priority. +// It returns the highest-Priority Version, OR exact match with client endpoint, +// among the alternatives that are provided, as well as its corresponding endpoint. func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) { type linkResp struct { Href string `json:"href"` @@ -109,3 +113,123 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* return highest, endpoint, nil } + +type SupportedMicroversions struct { + MaxMajor int + MaxMinor int + MinMajor int + MinMinor int +} + +// GetSupportedMicroversions returns the minimum and maximum microversion that is supported by the ServiceClient Endpoint. +func GetSupportedMicroversions(client *gophercloud.ServiceClient) (SupportedMicroversions, error) { + type valueResp struct { + ID string `json:"id"` + Status string `json:"status"` + Version string `json:"version"` + MinVersion string `json:"min_version"` + } + + type response struct { + Version valueResp `json:"version"` + Versions []valueResp `json:"versions"` + } + var minVersion, maxVersion string + var supportedMicroversions SupportedMicroversions + var resp response + _, err := client.Get(client.Endpoint, &resp, &gophercloud.RequestOpts{ + OkCodes: []int{200, 300}, + }) + + if err != nil { + return supportedMicroversions, err + } + + if len(resp.Versions) > 0 { + // We are dealing with an unversioned endpoint + // We only handle the case when there is exactly one, and assume it is the correct one + if len(resp.Versions) > 1 { + return supportedMicroversions, fmt.Errorf("unversioned endpoint with multiple alternatives not supported") + } + minVersion = resp.Versions[0].MinVersion + maxVersion = resp.Versions[0].Version + } else { + minVersion = resp.Version.MinVersion + maxVersion = resp.Version.Version + } + + // Return early if the endpoint does not support microversions + if minVersion == "" && maxVersion == "" { + return supportedMicroversions, fmt.Errorf("microversions not supported by ServiceClient Endpoint") + } + + supportedMicroversions.MinMajor, supportedMicroversions.MinMinor, err = ParseMicroversion(minVersion) + if err != nil { + return supportedMicroversions, err + } + + supportedMicroversions.MaxMajor, supportedMicroversions.MaxMinor, err = ParseMicroversion(maxVersion) + if err != nil { + return supportedMicroversions, err + } + + return supportedMicroversions, nil +} + +// RequireMicroversion checks that the required microversion is supported and +// returns a ServiceClient with the microversion set. +func RequireMicroversion(client gophercloud.ServiceClient, required string) (gophercloud.ServiceClient, error) { + supportedMicroversions, err := GetSupportedMicroversions(&client) + if err != nil { + return client, fmt.Errorf("unable to determine supported microversions: %w", err) + } + supported, err := supportedMicroversions.IsSupported(required) + if err != nil { + return client, err + } + if !supported { + return client, fmt.Errorf("microversion %s not supported. Supported versions: %v", required, supportedMicroversions) + } + client.Microversion = required + return client, nil +} + +// IsSupported checks if a microversion falls in the supported interval. +// It returns true if the version is within the interval and false otherwise. +func (supported SupportedMicroversions) IsSupported(version string) (bool, error) { + // Parse the version X.Y into X and Y integers that are easier to compare. + vMajor, vMinor, err := ParseMicroversion(version) + if err != nil { + return false, err + } + + // Check that the major version number is supported. + if (vMajor < supported.MinMajor) || (vMajor > supported.MaxMajor) { + return false, nil + } + + // Check that the minor version number is supported + if (vMinor <= supported.MaxMinor) && (vMinor >= supported.MinMinor) { + return true, nil + } + + return false, nil +} + +// ParseMicroversion parses the version major.minor into separate integers major and minor. +// For example, "2.53" becomes 2 and 53. +func ParseMicroversion(version string) (major int, minor int, err error) { + parts := strings.Split(version, ".") + if len(parts) != 2 { + return 0, 0, fmt.Errorf("invalid microversion format: %q", version) + } + major, err = strconv.Atoi(parts[0]) + if err != nil { + return 0, 0, err + } + minor, err = strconv.Atoi(parts[1]) + if err != nil { + return 0, 0, err + } + return major, minor, nil +} diff --git a/openstack/utils/testing/choose_version_test.go b/openstack/utils/testing/choose_version_test.go index 9c0119cb28..cf09562aec 100644 --- a/openstack/utils/testing/choose_version_test.go +++ b/openstack/utils/testing/choose_version_test.go @@ -3,6 +3,7 @@ package testing import ( "fmt" "net/http" + "strings" "testing" "github.com/gophercloud/gophercloud" @@ -35,6 +36,150 @@ func setupVersionHandler() { } `, testhelper.Server.URL, testhelper.Server.URL) }) + // Compute v2.1 API + testhelper.Mux.HandleFunc("/compute/v2.1/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, ` + { + "version": { + "id": "v2.1", + "status": "CURRENT", + "version": "2.90", + "min_version": "2.1", + "updated": "2013-07-23T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "%s/compute/v2.1/" + }, + { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=2.1" + } + ] + } + } + `, testhelper.Server.URL) + }) + // Compute v2 API + testhelper.Mux.HandleFunc("/compute/v2/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, ` + { + "version": { + "id": "v2.0", + "status": "SUPPORTED", + "version": "", + "min_version": "", + "updated": "2011-01-21T11:33:21Z", + "links": [ + { + "rel": "self", + "href": "%s/compute/v2/" + }, + { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/" + } + ], + "media-types": [ + { + "base": "application/json", + "type": "application/vnd.openstack.compute+json;version=2" + } + ] + } + } + `, testhelper.Server.URL) + }) + // Ironic API + testhelper.Mux.HandleFunc("/ironic/v1/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, ` + { + "name": "OpenStack Ironic API", + "description": "Ironic is an OpenStack project which enables the provision and management of baremetal machines.", + "default_version": { + "id": "v1", + "links": [ + { + "href": "%s/ironic/v1/", + "rel": "self" + } + ], + "status": "CURRENT", + "min_version": "1.1", + "version": "1.87" + }, + "versions": [ + { + "id": "v1", + "links": [ + { + "href": "%s/ironic/v1/", + "rel": "self" + } + ], + "status": "CURRENT", + "min_version": "1.1", + "version": "1.87" + } + ] + } + `, testhelper.Server.URL, testhelper.Server.URL) + }) + // Ironic multi-version + testhelper.Mux.HandleFunc("/ironic/v1.2/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, ` + { + "name": "OpenStack Ironic API", + "description": "Ironic is an OpenStack project which enables the provision and management of baremetal machines.", + "default_version": { + "id": "v1", + "links": [ + { + "href": "%s/ironic/v1/", + "rel": "self" + } + ], + "status": "CURRENT", + "min_version": "1.1", + "version": "1.87" + }, + "versions": [ + { + "id": "v1", + "links": [ + { + "href": "%s/ironic/v1/", + "rel": "self" + } + ], + "status": "CURRENT", + "min_version": "1.1", + "version": "1.87" + }, + { + "id": "v1.2", + "links": [ + { + "href": "%s/ironic/v1/", + "rel": "self" + } + ], + "status": "CURRENT", + "min_version": "1.2", + "version": "1.90" + } + ] + } + `, testhelper.Server.URL, testhelper.Server.URL, testhelper.Server.URL) + }) } func TestChooseVersion(t *testing.T) { @@ -117,3 +262,163 @@ func TestChooseVersionFromSuffix(t *testing.T) { t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint) } } + +type getSupportedServiceMicroversions struct { + Endpoint string + ExpectedMax string + ExpectedMin string + ExpectedErr bool +} + +func TestGetSupportedVersions(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + setupVersionHandler() + + tests := []getSupportedServiceMicroversions{ + { + // v2 does not support microversions and returns error + Endpoint: testhelper.Endpoint() + "compute/v2/", + ExpectedMax: "", + ExpectedMin: "", + ExpectedErr: true, + }, + { + Endpoint: testhelper.Endpoint() + "compute/v2.1/", + ExpectedMax: "2.90", + ExpectedMin: "2.1", + ExpectedErr: false, + }, + { + Endpoint: testhelper.Endpoint() + "ironic/v1/", + ExpectedMax: "1.87", + ExpectedMin: "1.1", + ExpectedErr: false, + }, + { + // This endpoint returns multiple versions, which is not supported + Endpoint: testhelper.Endpoint() + "ironic/v1.2/", + ExpectedMax: "not-relevant", + ExpectedMin: "not-relevant", + ExpectedErr: true, + }, + } + + for _, test := range tests { + c := &gophercloud.ProviderClient{ + IdentityBase: testhelper.Endpoint(), + IdentityEndpoint: testhelper.Endpoint() + "v2.0/", + } + + client := &gophercloud.ServiceClient{ + ProviderClient: c, + Endpoint: test.Endpoint, + } + + supported, err := utils.GetSupportedMicroversions(client) + + if test.ExpectedErr { + if err == nil { + t.Error("Expected error but got none!") + } + // Check for reasonable error message + if !strings.Contains(err.Error(), "not supported") { + t.Error("Expected error to contain 'not supported' but it did not!") + } + // No point parsing and comparing versions after error, so continue to next test case + continue + } else { + if err != nil { + t.Errorf("Expected no error but got %s", err.Error()) + } + } + + min := fmt.Sprintf("%d.%d", supported.MinMajor, supported.MinMinor) + max := fmt.Sprintf("%d.%d", supported.MaxMajor, supported.MaxMinor) + + if (min != test.ExpectedMin) || (max != test.ExpectedMax) { + t.Errorf("Expected min=%s and max=%s but got min=%s and max=%s", test.ExpectedMin, test.ExpectedMax, min, max) + } + } +} + +type microversionSupported struct { + Version string + MinVersion string + MaxVersion string + Supported bool + Error bool +} + +func TestMicroversionSupported(t *testing.T) { + tests := []microversionSupported{ + { + // Checking min version + Version: "2.1", + MinVersion: "2.1", + MaxVersion: "2.90", + Supported: true, + Error: false, + }, + { + // Checking max version + Version: "2.90", + MinVersion: "2.1", + MaxVersion: "2.90", + Supported: true, + Error: false, + }, + { + // Checking too high version + Version: "2.95", + MinVersion: "2.1", + MaxVersion: "2.90", + Supported: false, + Error: false, + }, + { + // Checking too low version + Version: "2.1", + MinVersion: "2.53", + MaxVersion: "2.90", + Supported: false, + Error: false, + }, + { + // Invalid version + Version: "2.1.53", + MinVersion: "2.53", + MaxVersion: "2.90", + Supported: false, + Error: true, + }, + } + + for _, test := range tests { + var err error + var supportedVersions utils.SupportedMicroversions + supportedVersions.MaxMajor, supportedVersions.MaxMinor, err = utils.ParseMicroversion(test.MaxVersion) + if err != nil { + t.Error("Error parsing MaxVersion!") + } + supportedVersions.MinMajor, supportedVersions.MinMinor, err = utils.ParseMicroversion(test.MinVersion) + if err != nil { + t.Error("Error parsing MinVersion!") + } + + supported, err := supportedVersions.IsSupported(test.Version) + if test.Error { + if err == nil { + t.Error("Expected error but got none!") + } + } else { + if err != nil { + t.Errorf("Expected no error but got %s", err.Error()) + } + } + if test.Supported != supported { + t.Errorf("Expected supported=%t to be %t, when version=%s, min=%s and max=%s", + supported, test.Supported, test.Version, test.MinVersion, test.MaxVersion) + } + } +} From 902ed6ee32d3e76b494d7d52a15f462f55f780e2 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 24 Oct 2023 11:31:43 -0400 Subject: [PATCH 1702/2296] 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 9d0243f86e4686dd5fb05a2707b5ef75dd09b665 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 23 Oct 2023 17:27:12 +0200 Subject: [PATCH 1703/2296] objects: Escape names in Gophercloud With this change, container and object names passed to the Gophercloud functions in `objectstorage/v1/containers` and `objectstorage/v1/objects` are now URL-escaped. If you were escaping them prior to passing them to Gophercloud, you should now remove escaping and pass the intended name to Gophercloud directly. **This is a breaking change.** API CHANGES: * Before this change, container and object names were sent as-is to Swift. With this change, **Gophercloud now escapes container and object names in all `objects` and `containers` functions**. If you were previously escaping names ( with, for example: `url.PathEscape` or `url.QueryEscape`), then you should REMOVE that and pass the intended names to Gophercloud directly. * The **`containers.ListOpts#Full` and `objects.ListOpts#Full` properties are REMOVED** from the Gophercloud API. The reason for that is: plaintext listing is unfixably wrong and won't handle special characters reliably (i.e. `\n`). * Empty container names, container names containing a slash (`/`), and empty object names are now rejected in Gophercloud before any call to Swift. * `containers.ErrInvalidContainerName` is now `v1.ErrInvalidContainerName` * New name validation errors: * `v1.ErrEmptyContainerName` * `v1.ErrEmptyObjectName` * In `objects.Copy`: the `destination` field (e.g. `objects.CopyOpts#Destination`) must be in the form `/container/object`: the function will reject a destination path if it doesn't start with a slash (`/`). CODE CHANGES: * `containers.List` and `objects.List` are now always setting `accept: application/json`. There is no plaintext listing any more, for the reason detailed above. * `containers` and `objects` acceptance tests are now run with nasty Unicode code points (including European characters, a space, "\n", backslash, slash where appropriate, emojis, emojis with modifiers, Chinese traditional characters), instead of only using nice ASCII characters. * Container and object names are now systematically validated before being sent to Swift. INCIDENTAL FIX: * it is now possible to successfuly call `objects.CreateTempURL` with a container name or object name containing the string `/v1/`. --- CHANGELOG.md | 18 ++++ .../objectstorage/v1/containers_test.go | 14 +-- .../objectstorage/v1/objects_test.go | 44 +++++---- .../objectstorage/v1/versioning_test.go | 2 - internal/acceptance/tools/tools.go | 38 +++++--- .../objectstorage/v1/containers/errors.go | 13 --- .../objectstorage/v1/containers/requests.go | 46 ++++----- .../v1/containers/testing/fixtures.go | 26 +----- .../v1/containers/testing/requests_test.go | 36 +++---- openstack/objectstorage/v1/containers/urls.go | 27 +----- openstack/objectstorage/v1/errors.go | 54 +++++++++++ .../objectstorage/v1/objects/requests.go | 93 ++++++++++++------- .../v1/objects/testing/fixtures_test.go | 30 +----- .../v1/objects/testing/requests_test.go | 83 +++++++++++------ openstack/objectstorage/v1/objects/urls.go | 23 ++++- 15 files changed, 311 insertions(+), 236 deletions(-) delete mode 100644 openstack/objectstorage/v1/containers/errors.go create mode 100644 openstack/objectstorage/v1/errors.go diff --git a/CHANGELOG.md b/CHANGELOG.md index e19d5af517..42dec99649 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## v2 (unreleased) + +BREAKING CHANGES: + +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Gophercloud now escapes container and object names in all `objects` and `containers` functions. If you were previously escaping names (with, for example: `url.PathEscape` or `url.QueryEscape`), then you should REMOVE that and pass the intended names to Gophercloud directly. +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) The `containers.ListOpts#Full` and `objects.ListOpts#Full` properties are REMOVED from the Gophercloud API. The reason for that is: plaintext listing is unfixably wrong and won't handle special characters reliably (i.e. `\n`). +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Empty container names, container names containing a slash (`/`), and empty object names are now rejected in Gophercloud before any call to Swift. +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) In `objectstorage`: `containers.ErrInvalidContainerName` is now `v1.ErrInvalidContainerName`. +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) New name validation errors in `objectstorage`: + * `v1.ErrEmptyContainerName` + * `v1.ErrEmptyObjectName` +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) In `objects.Copy`: the `destination` field (e.g. `objects.CopyOpts#Destination`) must be in the form `/container/object`: the function will reject a destination path if it doesn't start with a slash (`/`). + +New features and improvements: + +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Bugfix: it is now possible to successfuly call `objects.CreateTempURL` with a container name or object name containing the string `/v1/`. + + ## v1.5.0 (2023-06-21) New features and improvements: diff --git a/internal/acceptance/openstack/objectstorage/v1/containers_test.go b/internal/acceptance/openstack/objectstorage/v1/containers_test.go index 314b8019e7..48a51e634b 100644 --- a/internal/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/containers_test.go @@ -26,7 +26,7 @@ func TestContainers(t *testing.T) { // Create a slice of random container names. cNames := make([]string, numContainers) for i := 0; i < numContainers; i++ { - cNames[i] = tools.RandomString("gophercloud-test-container-", 8) + cNames[i] = "gophercloud-test-container-" + tools.RandomFunnyStringNoSlash(8) } // Create numContainers containers. @@ -44,7 +44,7 @@ func TestContainers(t *testing.T) { // List the numContainer names that were just created. To just list those, // the 'prefix' parameter is used. - err = containers.List(client, &containers.ListOpts{Full: true, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { + err = containers.List(client, &containers.ListOpts{Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { containerList, err := containers.ExtractInfo(page) th.AssertNoErr(t, err) @@ -58,7 +58,7 @@ func TestContainers(t *testing.T) { th.AssertNoErr(t, err) // List the info for the numContainer containers that were created. - err = containers.List(client, &containers.ListOpts{Full: false, Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { + err = containers.List(client, &containers.ListOpts{Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { containerList, err := containers.ExtractNames(page) th.AssertNoErr(t, err) for _, n := range containerList { @@ -158,7 +158,7 @@ func TestListAllContainers(t *testing.T) { // Create a slice of random container names. cNames := make([]string, numContainers) for i := 0; i < numContainers; i++ { - cNames[i] = tools.RandomString("gophercloud-test-container-", 8) + cNames[i] = "gophercloud-test-container-" + tools.RandomFunnyStringNoSlash(8) } // Create numContainers containers. @@ -176,7 +176,7 @@ func TestListAllContainers(t *testing.T) { // List all the numContainer names that were just created. To just list those, // the 'prefix' parameter is used. - allPages, err := containers.List(client, &containers.ListOpts{Full: true, Limit: 5, Prefix: "gophercloud-test-container-"}).AllPages() + allPages, err := containers.List(client, &containers.ListOpts{Limit: 5, Prefix: "gophercloud-test-container-"}).AllPages() th.AssertNoErr(t, err) containerInfoList, err := containers.ExtractInfo(allPages) th.AssertNoErr(t, err) @@ -187,7 +187,7 @@ func TestListAllContainers(t *testing.T) { th.AssertEquals(t, numContainers, len(containerInfoList)) // List the info for all the numContainer containers that were created. - allPages, err = containers.List(client, &containers.ListOpts{Full: false, Limit: 2, Prefix: "gophercloud-test-container-"}).AllPages() + allPages, err = containers.List(client, &containers.ListOpts{Limit: 2, Prefix: "gophercloud-test-container-"}).AllPages() th.AssertNoErr(t, err) containerNamesList, err := containers.ExtractNames(allPages) th.AssertNoErr(t, err) @@ -208,7 +208,7 @@ func TestBulkDeleteContainers(t *testing.T) { // Create a slice of random container names. cNames := make([]string, numContainers) for i := 0; i < numContainers; i++ { - cNames[i] = tools.RandomString("gophercloud-test-container-", 8) + cNames[i] = "gophercloud-test-container-" + tools.RandomFunnyStringNoSlash(8) } // Create numContainers containers. diff --git a/internal/acceptance/openstack/objectstorage/v1/objects_test.go b/internal/acceptance/openstack/objectstorage/v1/objects_test.go index 4187636f6e..44039ae719 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,12 +30,13 @@ 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++ { - oNames[i] = tools.RandomString("test-object-", 8) + for i := 0; i < len(oNames)-1; i++ { + oNames[i] = "test-object-" + tools.RandomFunnyString(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) + cName := "test-container-" + tools.RandomFunnyStringNoSlash(8) opts := containers.CreateOpts{ TempURLKey: "super-secret", } @@ -51,7 +53,7 @@ func TestObjects(t *testing.T) { // Create a slice of buffers to hold the test object content. oContents := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents[i] = tools.RandomString("", 10) + oContents[i] = tools.RandomFunnyString(10) createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents[i]), } @@ -68,7 +70,6 @@ func TestObjects(t *testing.T) { // List all created objects listOpts := objects.ListOpts{ - Full: true, Prefix: "test-object-", } @@ -135,7 +136,7 @@ func TestObjects(t *testing.T) { // Copy the contents of one object to another. copyOpts := objects.CopyOpts{ - Destination: cName + "/" + oNames[1], + Destination: "/" + cName + "/" + oNames[1], } copyres := objects.Copy(client, cName, oNames[0], copyOpts) th.AssertNoErr(t, copyres.Err) @@ -237,22 +238,22 @@ func TestObjectsListSubdir(t *testing.T) { } // Create a random subdirectory name. - cSubdir1 := tools.RandomString("test-subdir-", 8) - cSubdir2 := tools.RandomString("test-subdir-", 8) + cSubdir1 := "test-subdir-" + tools.RandomFunnyStringNoSlash(8) + cSubdir2 := "test-subdir-" + tools.RandomFunnyStringNoSlash(8) // Make a slice of length numObjects to hold the random object names. oNames1 := make([]string, numObjects) for i := 0; i < len(oNames1); i++ { - oNames1[i] = cSubdir1 + "/" + tools.RandomString("test-object-", 8) + oNames1[i] = cSubdir1 + "/test-object-" + tools.RandomFunnyString(8) } oNames2 := make([]string, numObjects) for i := 0; i < len(oNames2); i++ { - oNames2[i] = cSubdir2 + "/" + tools.RandomString("test-object-", 8) + oNames2[i] = cSubdir2 + "/test-object-" + tools.RandomFunnyString(8) } // Create a container to hold the test objects. - cName := tools.RandomString("test-container-", 8) + cName := "test-container-" + tools.RandomFunnyStringNoSlash(8) _, err = containers.Create(client, cName, nil).Extract() th.AssertNoErr(t, err) @@ -266,7 +267,7 @@ func TestObjectsListSubdir(t *testing.T) { // Create a slice of buffers to hold the test object content. oContents1 := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents1[i] = tools.RandomString("", 10) + oContents1[i] = tools.RandomFunnyString(10) createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents1[i]), } @@ -284,7 +285,7 @@ func TestObjectsListSubdir(t *testing.T) { oContents2 := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents2[i] = tools.RandomString("", 10) + oContents2[i] = tools.RandomFunnyString(10) createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents2[i]), } @@ -301,7 +302,6 @@ func TestObjectsListSubdir(t *testing.T) { }() listOpts := objects.ListOpts{ - Full: true, Delimiter: "/", } @@ -330,7 +330,6 @@ func TestObjectsListSubdir(t *testing.T) { } listOpts = objects.ListOpts{ - Full: true, Delimiter: "/", Prefix: cSubdir2, } @@ -356,22 +355,22 @@ func TestObjectsBulkDelete(t *testing.T) { } // Create a random subdirectory name. - cSubdir1 := tools.RandomString("test-subdir-", 8) - cSubdir2 := tools.RandomString("test-subdir-", 8) + cSubdir1 := "test-subdir-" + tools.RandomFunnyString(8) + cSubdir2 := "test-subdir-" + tools.RandomFunnyString(8) // Make a slice of length numObjects to hold the random object names. oNames1 := make([]string, numObjects) for i := 0; i < len(oNames1); i++ { - oNames1[i] = cSubdir1 + "/" + tools.RandomString("test-object-", 8) + oNames1[i] = cSubdir1 + "/test-object-" + tools.RandomFunnyString(8) } oNames2 := make([]string, numObjects) for i := 0; i < len(oNames2); i++ { - oNames2[i] = cSubdir2 + "/" + tools.RandomString("test-object-", 8) + oNames2[i] = cSubdir2 + "/test-object-" + tools.RandomFunnyString(8) } // Create a container to hold the test objects. - cName := tools.RandomString("test-container-", 8) + cName := "test-container-" + tools.RandomFunnyStringNoSlash(8) _, err = containers.Create(client, cName, nil).Extract() th.AssertNoErr(t, err) @@ -385,7 +384,7 @@ func TestObjectsBulkDelete(t *testing.T) { // Create a slice of buffers to hold the test object content. oContents1 := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents1[i] = tools.RandomString("", 10) + oContents1[i] = tools.RandomFunnyString(10) createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents1[i]), } @@ -395,7 +394,7 @@ func TestObjectsBulkDelete(t *testing.T) { oContents2 := make([]string, numObjects) for i := 0; i < numObjects; i++ { - oContents2[i] = tools.RandomString("", 10) + oContents2[i] = tools.RandomFunnyString(10) createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents2[i]), } @@ -416,7 +415,6 @@ func TestObjectsBulkDelete(t *testing.T) { // Verify deletion listOpts := objects.ListOpts{ - Full: true, Delimiter: "/", } diff --git a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go index 2c90f09ec9..d992aee026 100644 --- a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go @@ -108,7 +108,6 @@ func TestObjectsVersioning(t *testing.T) { // List created objects listOpts := objects.ListOpts{ - Full: true, Prefix: "test-object-", } @@ -131,7 +130,6 @@ func TestObjectsVersioning(t *testing.T) { // List all created objects listOpts = objects.ListOpts{ - Full: true, Prefix: "test-object-", Versions: true, } diff --git a/internal/acceptance/tools/tools.go b/internal/acceptance/tools/tools.go index 30b821dd07..7a5f2f7690 100644 --- a/internal/acceptance/tools/tools.go +++ b/internal/acceptance/tools/tools.go @@ -1,10 +1,10 @@ package tools import ( - "crypto/rand" "encoding/json" "errors" - mrand "math/rand" + "math/rand" + "strings" "testing" "time" ) @@ -47,22 +47,36 @@ func MakeNewPassword(oldPass string) string { // RandomString generates a string of given length, but random content. // All content will be within the ASCII graphic character set. -// (Implementation from Even Shaw's contribution on -// http://stackoverflow.com/questions/12771930/what-is-the-fastest-way-to-generate-a-long-random-string-in-go). func RandomString(prefix string, n int) string { - const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - var bytes = make([]byte, n) - rand.Read(bytes) - for i, b := range bytes { - bytes[i] = alphanum[b%byte(len(alphanum))] + charset := []rune("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") + return prefix + randomString(charset, n) +} + +// RandomFunnyString returns a random string of the given length filled with +// funny Unicode code points. +func RandomFunnyString(length int) string { + charset := []rune("012abc \n\t🤖👾👩🏾‍🚀+.,;:*`~|\"'/\\]êà²×c師☷") + return randomString(charset, length) +} + +// RandomFunnyStringNoSlash returns a random string of the given length filled with +// funny Unicode code points, but no forward slash. +func RandomFunnyStringNoSlash(length int) string { + charset := []rune("012abc \n\t🤖👾👩🏾‍🚀+.,;:*`~|\"'\\]êà²×c師☷") + return randomString(charset, length) +} + +func randomString(charset []rune, length int) string { + var s strings.Builder + for i := 0; i < length; i++ { + s.WriteRune(charset[rand.Intn(len(charset))]) } - return prefix + string(bytes) + return s.String() } // RandomInt will return a random integer between a specified range. func RandomInt(min, max int) int { - mrand.Seed(time.Now().Unix()) - return mrand.Intn(max-min) + min + return rand.Intn(max-min) + min } // Elide returns the first bit of its input string with a suffix of "..." if it's longer than diff --git a/openstack/objectstorage/v1/containers/errors.go b/openstack/objectstorage/v1/containers/errors.go deleted file mode 100644 index 2b99a516df..0000000000 --- a/openstack/objectstorage/v1/containers/errors.go +++ /dev/null @@ -1,13 +0,0 @@ -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 0957702447..434d977749 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -1,21 +1,27 @@ package containers import ( - "strings" + "bytes" + "net/url" "github.com/gophercloud/gophercloud" + v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { - ToContainerListParams() (bool, string, error) + ToContainerListParams() (string, error) } // ListOpts is a structure that holds options for listing containers. type ListOpts struct { - Full bool + // Full has been removed from the Gophercloud API. Gophercloud will now + // always request the "full" (json) listing, because simplified listing + // (plaintext) returns false results when names contain end-of-line + // characters. + Limit int `q:"limit"` Marker string `q:"marker"` EndMarker string `q:"end_marker"` @@ -24,30 +30,25 @@ type ListOpts struct { Delimiter string `q:"delimiter"` } -// ToContainerListParams formats a ListOpts into a query string and boolean -// representing whether to list complete information for each container. -func (opts ListOpts) ToContainerListParams() (bool, string, error) { +// ToContainerListParams formats a ListOpts into a query string. +func (opts ListOpts) ToContainerListParams() (string, error) { q, err := gophercloud.BuildQueryString(opts) - return opts.Full, q.String(), err + return q.String(), err } // List is a function that retrieves containers associated with the account as // well as account metadata. It returns a pager which can be iterated with the // EachPage function. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} + headers := map[string]string{"Accept": "application/json", "Content-Type": "application/json"} url := listURL(c) if opts != nil { - full, query, err := opts.ToContainerListParams() + query, err := opts.ToContainerListParams() if err != nil { return pagination.Pager{Err: err} } url += query - - if full { - headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"} - } } pager := pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { @@ -123,15 +124,18 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB // BulkDelete is a function that bulk deletes containers. func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDeleteResult) { - // urlencode container 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 - encodedContainers := make([]string, len(containers)) - for i, v := range containers { - encodedContainers[i] = v + var body bytes.Buffer + + for i := range containers { + if err := v1.CheckContainerName(containers[i]); err != nil { + r.Err = err + return + } + body.WriteString(url.PathEscape(containers[i])) + body.WriteRune('\n') } - b := strings.NewReader(strings.Join(encodedContainers, "\n") + "\n") - resp, err := c.Post(bulkDeleteURL(c), b, &r.Body, &gophercloud.RequestOpts{ + + resp, err := c.Post(bulkDeleteURL(c), &body, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{ "Accept": "application/json", "Content-Type": "text/plain", diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index aa38b68da7..41e37ef1a7 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -82,30 +82,6 @@ func HandleListContainerInfoSuccessfully(t *testing.T) { }) } -// HandleListContainerNamesSuccessfully creates an HTTP handler at `/` on the test handler mux that -// responds with a `ListNames` response when only container names are requested. -func HandleListContainerNamesSuccessfully(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.Header().Set("Content-Type", "text/plain") - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, "janeausten\nmarktwain\n") - case "janeausten": - fmt.Fprintf(w, "marktwain\n") - case "marktwain": - fmt.Fprintf(w, ``) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - // 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. @@ -113,7 +89,7 @@ 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") + th.TestHeader(t, r, "Accept", "application/json") 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 cc40e2151e..f31718aaad 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/pagination" th "github.com/gophercloud/gophercloud/testhelper" @@ -18,18 +19,17 @@ func TestContainerNames(t *testing.T) { for _, tc := range [...]struct { name string containerName string + expectedError error }{ { "rejects_a_slash", "one/two", + v1.ErrInvalidContainerName{}, }, { - "rejects_an_escaped_slash", - "one%2Ftwo", - }, - { - "rejects_an_escaped_slash_lowercase", - "one%2ftwo", + "rejects_an_empty_string", + "", + v1.ErrEmptyContainerName{}, }, } { t.Run(tc.name, func(t *testing.T) { @@ -39,7 +39,7 @@ func TestContainerNames(t *testing.T) { HandleCreateContainerSuccessfully(t) _, err := containers.Create(fake.ServiceClient(), tc.containerName, nil).Extract() - th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, err, &tc.expectedError) }) t.Run("delete", func(t *testing.T) { th.SetupHTTP() @@ -47,7 +47,7 @@ func TestContainerNames(t *testing.T) { HandleDeleteContainerSuccessfully(t, WithPath("/")) res := containers.Delete(fake.ServiceClient(), tc.containerName) - th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("update", func(t *testing.T) { th.SetupHTTP() @@ -64,7 +64,7 @@ func TestContainerNames(t *testing.T) { ContentType: &contentType, } res := containers.Update(fake.ServiceClient(), tc.containerName, options) - th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("get", func(t *testing.T) { th.SetupHTTP() @@ -73,10 +73,10 @@ func TestContainerNames(t *testing.T) { res := containers.Get(fake.ServiceClient(), tc.containerName, nil) _, err := res.ExtractMetadata() - th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, err, &tc.expectedError) _, err = res.Extract() - th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, err, &tc.expectedError) }) }) } @@ -88,7 +88,7 @@ func TestListContainerInfo(t *testing.T) { HandleListContainerInfoSuccessfully(t) count := 0 - err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) { + err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractInfo(page) th.AssertNoErr(t, err) @@ -106,7 +106,7 @@ func TestListAllContainerInfo(t *testing.T) { defer th.TeardownHTTP() HandleListContainerInfoSuccessfully(t) - allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: true}).AllPages() + allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := containers.ExtractInfo(allPages) th.AssertNoErr(t, err) @@ -116,10 +116,10 @@ func TestListAllContainerInfo(t *testing.T) { func TestListContainerNames(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - HandleListContainerNamesSuccessfully(t) + HandleListContainerInfoSuccessfully(t) count := 0 - err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) { + err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractNames(page) if err != nil { @@ -138,9 +138,9 @@ func TestListContainerNames(t *testing.T) { func TestListAllContainerNames(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - HandleListContainerNamesSuccessfully(t) + HandleListContainerInfoSuccessfully(t) - allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: false}).AllPages() + allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := containers.ExtractNames(allPages) th.AssertNoErr(t, err) @@ -152,7 +152,7 @@ func TestListZeroContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleListZeroContainerNames204(t) - allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{Full: false}).AllPages() + allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages() th.AssertNoErr(t, err) actual, err := containers.ExtractNames(allPages) th.AssertNoErr(t, err) diff --git a/openstack/objectstorage/v1/containers/urls.go b/openstack/objectstorage/v1/containers/urls.go index 0f11a97b7f..bc8d7274ff 100644 --- a/openstack/objectstorage/v1/containers/urls.go +++ b/openstack/objectstorage/v1/containers/urls.go @@ -1,40 +1,21 @@ package containers import ( - "fmt" - "strings" + "net/url" "github.com/gophercloud/gophercloud" + v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" ) -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, error) { - if err := CheckContainerName(container); err != nil { + if err := v1.CheckContainerName(container); err != nil { return "", err } - return c.ServiceURL(container), nil + return c.ServiceURL(url.PathEscape(container)), nil } func getURL(c *gophercloud.ServiceClient, container string) (string, error) { diff --git a/openstack/objectstorage/v1/errors.go b/openstack/objectstorage/v1/errors.go new file mode 100644 index 0000000000..a4c5c57c88 --- /dev/null +++ b/openstack/objectstorage/v1/errors.go @@ -0,0 +1,54 @@ +package v1 + +import ( + "fmt" + "strings" + + "github.com/gophercloud/gophercloud" +) + +func CheckContainerName(s string) error { + if len(s) < 1 { + return ErrEmptyContainerName{} + } + if strings.ContainsRune(s, '/') { + return ErrInvalidContainerName{name: s} + } + return nil +} + +func CheckObjectName(s string) error { + if s == "" { + return ErrEmptyObjectName{} + } + return nil +} + +// ErrInvalidContainerName signals a container name containing an illegal +// character. +type ErrInvalidContainerName struct { + name string + gophercloud.BaseError +} + +func (e ErrInvalidContainerName) Error() string { + return fmt.Sprintf("invalid name %q: a container name cannot contain a slash (/) character", e.name) +} + +// ErrEmptyContainerName signals an empty container name. +type ErrEmptyContainerName struct { + gophercloud.BaseError +} + +func (e ErrEmptyContainerName) Error() string { + return "a container name must not be empty" +} + +// ErrEmptyObjectName signals an empty container name. +type ErrEmptyObjectName struct { + gophercloud.BaseError +} + +func (e ErrEmptyObjectName) Error() string { + return "an object name must not be empty" +} diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index ffad4624da..1a2af4e6cf 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -10,10 +10,12 @@ import ( "fmt" "hash" "io" + "net/url" "strings" "time" "github.com/gophercloud/gophercloud" + v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" "github.com/gophercloud/gophercloud/pagination" @@ -41,16 +43,16 @@ func (e ErrTempURLDigestNotValid) Error() string { // ListOptsBuilder allows extensions to add additional parameters to the List // request. type ListOptsBuilder interface { - ToObjectListParams() (bool, string, error) + ToObjectListParams() (string, error) } // ListOpts is a structure that holds parameters for listing objects. type ListOpts struct { - // Full is a true/false value that represents the amount of object information - // returned. If Full is set to true, then the content-type, number of bytes, - // hash date last modified, and name are returned. If set to false or not set, - // then only the object names are returned. - Full bool + // Full has been removed from the Gophercloud API. Gophercloud will now + // always request the "full" (json) listing, because simplified listing + // (plaintext) returns false results when names contain end-of-line + // characters. + Limit int `q:"limit"` Marker string `q:"marker"` EndMarker string `q:"end_marker"` @@ -61,11 +63,10 @@ type ListOpts struct { Versions bool `q:"versions"` } -// ToObjectListParams formats a ListOpts into a query string and boolean -// representing whether to list complete information for each object. -func (opts ListOpts) ToObjectListParams() (bool, string, error) { +// ToObjectListParams formats a ListOpts into a query string. +func (opts ListOpts) ToObjectListParams() (string, error) { q, err := gophercloud.BuildQueryString(opts) - return opts.Full, q.String(), err + return q.String(), err } // List is a function that retrieves all objects in a container. It also returns @@ -78,17 +79,13 @@ func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuild return pagination.Pager{Err: err} } - headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} + headers := map[string]string{"Accept": "application/json", "Content-Type": "application/json"} if opts != nil { - full, query, err := opts.ToObjectListParams() + query, err := opts.ToObjectListParams() if err != nil { return pagination.Pager{Err: err} } url += query - - if full { - headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"} - } } pager := pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { @@ -301,8 +298,12 @@ type CopyOpts struct { ContentDisposition string `h:"Content-Disposition"` ContentEncoding string `h:"Content-Encoding"` ContentType string `h:"Content-Type"` - Destination string `h:"Destination" required:"true"` - ObjectVersionID string `q:"version-id"` + + // Destination is where the object should be copied to, in the form: + // `/container/object`. + Destination string `h:"Destination" required:"true"` + + ObjectVersionID string `q:"version-id"` } // ToObjectCopyMap formats a CopyOpts into a map of headers. @@ -328,12 +329,6 @@ func (opts CopyOpts) ToObjectCopyQuery() (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 { @@ -341,9 +336,34 @@ func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts C return } for k, v := range headers { + if strings.ToLower(k) == "destination" { + // URL-encode the container name and the object name + // separately before joining them around the `/` slash + // separator. Note that the destination path is also + // expected to start with a slash. + segments := strings.SplitN(v, "/", 3) + if l := len(segments); l != 3 { + r.Err = fmt.Errorf("the destination field is expected to contain at least two slash / characters: the initial one, and the separator between the container name and the object name") + return + } + if segments[0] != "" { + r.Err = fmt.Errorf("the destination field is expected to start with a slash") + return + } + for i := range segments { + segments[i] = url.PathEscape(segments[i]) + } + v = strings.Join(segments, "/") + } h[k] = v } + url, err := copyURL(c, containerName, objectName) + if err != nil { + r.Err = err + return + } + if opts, ok := opts.(CopyOptsQueryBuilder); ok { query, err := opts.ToObjectCopyQuery() if err != nil { @@ -574,6 +594,10 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin if err != nil { return "", err } + urlToBeSigned, err := tempURL(c, containerName, objectName) + if err != nil { + return "", err + } if opts.Split == "" { opts.Split = "/v1/" @@ -611,8 +635,10 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin } secretKey := []byte(tempURLKey) - splitPath := strings.Split(url, opts.Split) - baseURL, objectPath := splitPath[0], splitPath[1] + _, objectPath, splitFound := strings.Cut(urlToBeSigned, opts.Split) + if !splitFound { + return "", fmt.Errorf("URL prefix %q not found", opts.Split) + } objectPath = opts.Split + objectPath body := fmt.Sprintf("%s\n%d\n%s", opts.Method, expiry, objectPath) var hash hash.Hash @@ -628,7 +654,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin } 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 + return fmt.Sprintf("%s?temp_url_sig=%s&temp_url_expires=%d", url, hexsum, expiry), nil } // BulkDelete is a function that bulk deletes objects. @@ -638,21 +664,22 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin // * 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 { + if err := v1.CheckContainerName(container); err != nil { r.Err = err return } + encodedContainer := url.PathEscape(container) + var body bytes.Buffer for i := range objects { - if objects[i] == "" { - r.Err = fmt.Errorf("object names must not be the empty string") + if err := v1.CheckObjectName(objects[i]); err != nil { + r.Err = err return } - body.WriteString(container) + body.WriteString(encodedContainer) body.WriteRune('/') - body.WriteString(objects[i]) + body.WriteString(url.PathEscape(objects[i])) body.WriteRune('\n') } diff --git a/openstack/objectstorage/v1/objects/testing/fixtures_test.go b/openstack/objectstorage/v1/objects/testing/fixtures_test.go index d21dc74b6b..c4bc2d27ed 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures_test.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures_test.go @@ -94,7 +94,7 @@ var ExpectedListSubdir = []objects.Object{ // ExpectedListNames is the result expected from a call to `List` when just // object names are requested. -var ExpectedListNames = []string{"hello", "goodbye"} +var ExpectedListNames = []string{"goodbye", "hello"} // HandleListObjectsInfoSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that // responds with a `List` response when full info is requested. @@ -166,28 +166,6 @@ func HandleListSubdirSuccessfully(t *testing.T) { }) } -// HandleListObjectNamesSuccessfully creates an HTTP handler at `/testContainer` on the test handler mux that -// responds with a `List` response when only object names are requested. -func HandleListObjectNamesSuccessfully(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.Header().Set("Content-Type", "text/plain") - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, "hello\ngoodbye\n") - case "goodbye": - fmt.Fprintf(w, "") - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - // 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. @@ -195,7 +173,7 @@ 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") + th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusNoContent) }) @@ -271,12 +249,12 @@ func HandleCreateTypelessObjectSuccessfully(t *testing.T, content string) { // HandleCopyObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that // responds with a `Copy` response. -func HandleCopyObjectSuccessfully(t *testing.T) { +func HandleCopyObjectSuccessfully(t *testing.T, destination string) { 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.TestHeader(t, r, "Destination", destination) w.WriteHeader(http.StatusCreated) }) } diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 9161d2956e..d8e6c11101 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -10,8 +10,8 @@ import ( "testing" "time" + v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" 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" @@ -23,18 +23,17 @@ func TestContainerNames(t *testing.T) { for _, tc := range [...]struct { name string containerName string + expectedError error }{ { "rejects_a_slash", "one/two", + v1.ErrInvalidContainerName{}, }, { - "rejects_an_escaped_slash", - "one%2Ftwo", - }, - { - "rejects_an_escaped_slash_lowercase", - "one%2ftwo", + "rejects_an_empty_string", + "", + v1.ErrEmptyContainerName{}, }, } { t.Run(tc.name, func(t *testing.T) { @@ -44,7 +43,7 @@ func TestContainerNames(t *testing.T) { HandleListObjectsInfoSuccessfully(t, WithPath("/")) _, err := objects.List(fake.ServiceClient(), tc.containerName, nil).AllPages() - th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, err, &tc.expectedError) }) t.Run("download", func(t *testing.T) { th.SetupHTTP() @@ -52,7 +51,7 @@ func TestContainerNames(t *testing.T) { HandleDownloadObjectSuccessfully(t, WithPath("/")) _, err := objects.Download(fake.ServiceClient(), tc.containerName, "testObject", nil).Extract() - th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, err, &tc.expectedError) }) t.Run("create", func(t *testing.T) { th.SetupHTTP() @@ -64,7 +63,7 @@ func TestContainerNames(t *testing.T) { ContentType: "text/plain", Content: strings.NewReader(content), }) - th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("delete", func(t *testing.T) { th.SetupHTTP() @@ -72,7 +71,7 @@ func TestContainerNames(t *testing.T) { HandleDeleteObjectSuccessfully(t, WithPath("/")) res := objects.Delete(fake.ServiceClient(), tc.containerName, "testObject", nil) - th.CheckErr(t, res.Err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("get", func(t *testing.T) { th.SetupHTTP() @@ -80,7 +79,7 @@ func TestContainerNames(t *testing.T) { HandleGetObjectSuccessfully(t, WithPath("/")) _, err := objects.Get(fake.ServiceClient(), tc.containerName, "testObject", nil).ExtractMetadata() - th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, err, &tc.expectedError) }) t.Run("update", func(t *testing.T) { th.SetupHTTP() @@ -90,7 +89,7 @@ func TestContainerNames(t *testing.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{}) + th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("createTempURL", func(t *testing.T) { port := 33200 @@ -111,7 +110,15 @@ func TestContainerNames(t *testing.T) { Timestamp: time.Date(2020, 07, 01, 01, 12, 00, 00, time.UTC), }) - th.CheckErr(t, err, &containers.ErrInvalidContainerName{}) + th.CheckErr(t, err, &tc.expectedError) + }) + t.Run("bulk-delete", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleBulkDeleteSuccessfully(t) + + res := objects.BulkDelete(fake.ServiceClient(), tc.containerName, []string{"testObject"}) + th.CheckErr(t, res.Err, &tc.expectedError) }) }) } @@ -182,7 +189,7 @@ func TestListObjectInfo(t *testing.T) { HandleListObjectsInfoSuccessfully(t) count := 0 - options := &objects.ListOpts{Full: true} + options := &objects.ListOpts{} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractInfo(page) @@ -202,7 +209,7 @@ func TestListObjectSubdir(t *testing.T) { HandleListSubdirSuccessfully(t) count := 0 - options := &objects.ListOpts{Full: true, Prefix: "", Delimiter: "/"} + options := &objects.ListOpts{Prefix: "", Delimiter: "/"} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractInfo(page) @@ -219,11 +226,11 @@ func TestListObjectSubdir(t *testing.T) { func TestListObjectNames(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - HandleListObjectNamesSuccessfully(t) + HandleListObjectsInfoSuccessfully(t) // Check without delimiter. count := 0 - options := &objects.ListOpts{Full: false} + options := &objects.ListOpts{} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) @@ -241,7 +248,7 @@ func TestListObjectNames(t *testing.T) { // Check with delimiter. count = 0 - options = &objects.ListOpts{Full: false, Delimiter: "/"} + options = &objects.ListOpts{Delimiter: "/"} err = objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) @@ -264,7 +271,7 @@ func TestListZeroObjectNames204(t *testing.T) { HandleListZeroObjectNames204(t) count := 0 - options := &objects.ListOpts{Full: false} + options := &objects.ListOpts{} err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) @@ -341,13 +348,33 @@ func TestErrorIsRaisedForChecksumMismatch(t *testing.T) { */ func TestCopyObject(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleCopyObjectSuccessfully(t) - - options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject"} - res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) - th.AssertNoErr(t, res.Err) + t.Run("simple", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCopyObjectSuccessfully(t, "/newTestContainer/newTestObject") + + options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject"} + res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) + th.AssertNoErr(t, res.Err) + }) + t.Run("slash", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCopyObjectSuccessfully(t, "/newTestContainer/path%2Fto%2FnewTestObject") + + options := &objects.CopyOpts{Destination: "/newTestContainer/path/to/newTestObject"} + res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) + th.AssertNoErr(t, res.Err) + }) + t.Run("emojis", func(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCopyObjectSuccessfully(t, "/newTestContainer/new%F0%9F%98%8ATest%2C%3B%22O%28bject%21_%E7%AF%84") + + options := &objects.CopyOpts{Destination: "/newTestContainer/new😊Test,;\"O(bject!_範"} + res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) + th.AssertNoErr(t, res.Err) + }) } func TestCopyObjectVersion(t *testing.T) { @@ -512,7 +539,7 @@ func TestCreateTempURL(t *testing.T) { sig := "89be454a9c7e2e9f3f50a8441815e0b5801cba5b" expiry := "1593565980" - expectedURL := fmt.Sprintf("http://127.0.0.1:%v/v1/testContainer/testObject/testFile.txt?temp_url_sig=%v&temp_url_expires=%v", port, sig, expiry) + expectedURL := fmt.Sprintf("http://127.0.0.1:%v/v1/testContainer/testObject%%2FtestFile.txt?temp_url_sig=%v&temp_url_expires=%v", port, sig, expiry) th.AssertNoErr(t, err) th.AssertEquals(t, expectedURL, tempURL) diff --git a/openstack/objectstorage/v1/objects/urls.go b/openstack/objectstorage/v1/objects/urls.go index 172a197bfd..c398091e71 100644 --- a/openstack/objectstorage/v1/objects/urls.go +++ b/openstack/objectstorage/v1/objects/urls.go @@ -1,22 +1,35 @@ package objects import ( + "net/url" + "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" + v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" ) +// tempURL returns an unescaped virtual path to generate the HMAC signature. +// Names must not be URL-encoded in this case. +// +// See: https://docs.openstack.org/swift/latest/api/temporary_url_middleware.html#hmac-signature-for-temporary-urls +func tempURL(c *gophercloud.ServiceClient, container, object string) (string, error) { + return c.ServiceURL(container, object), nil +} + func listURL(c *gophercloud.ServiceClient, container string) (string, error) { - if err := containers.CheckContainerName(container); err != nil { + if err := v1.CheckContainerName(container); err != nil { return "", err } - return c.ServiceURL(container), nil + return c.ServiceURL(url.PathEscape(container)), nil } func copyURL(c *gophercloud.ServiceClient, container, object string) (string, error) { - if err := containers.CheckContainerName(container); err != nil { + if err := v1.CheckContainerName(container); err != nil { return "", err } - return c.ServiceURL(container, object), nil + if err := v1.CheckObjectName(object); err != nil { + return "", err + } + return c.ServiceURL(url.PathEscape(container), url.PathEscape(object)), nil } func createURL(c *gophercloud.ServiceClient, container, object string) (string, error) { From af41ccdfa38c65b1a353b415470510a74571cbf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dulko?= Date: Mon, 30 Oct 2023 18:56:54 +0100 Subject: [PATCH 1704/2296] Octavia: Add tags to resources missing them Seems like a bunch of resources are missing the tags fiels that Octavia API reference specified. This commit adds them. --- .../loadbalancer/v2/listeners/requests.go | 35 ++++++++------- .../loadbalancer/v2/monitors/requests.go | 45 +++++++++++-------- openstack/loadbalancer/v2/monitors/results.go | 4 ++ .../v2/monitors/testing/fixtures_test.go | 15 +++++-- .../v2/monitors/testing/requests_test.go | 1 + openstack/loadbalancer/v2/pools/requests.go | 23 +++++----- 6 files changed, 72 insertions(+), 51 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index a0a06f6448..e71696030b 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -47,23 +47,24 @@ type ListOptsBuilder interface { // sort by a particular listener 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"` - AdminStateUp *bool `q:"admin_state_up"` - ProjectID string `q:"project_id"` - LoadbalancerID string `q:"loadbalancer_id"` - DefaultPoolID string `q:"default_pool_id"` - Protocol string `q:"protocol"` - ProtocolPort int `q:"protocol_port"` - ConnectionLimit int `q:"connection_limit"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - TimeoutClientData *int `q:"timeout_client_data"` - TimeoutMemberData *int `q:"timeout_member_data"` - TimeoutMemberConnect *int `q:"timeout_member_connect"` - TimeoutTCPInspect *int `q:"timeout_tcp_inspect"` + ID string `q:"id"` + Name string `q:"name"` + AdminStateUp *bool `q:"admin_state_up"` + ProjectID string `q:"project_id"` + LoadbalancerID string `q:"loadbalancer_id"` + DefaultPoolID string `q:"default_pool_id"` + Protocol string `q:"protocol"` + ProtocolPort int `q:"protocol_port"` + ConnectionLimit int `q:"connection_limit"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + TimeoutClientData *int `q:"timeout_client_data"` + TimeoutMemberData *int `q:"timeout_member_data"` + TimeoutMemberConnect *int `q:"timeout_member_connect"` + TimeoutTCPInspect *int `q:"timeout_tcp_inspect"` + Tags []string `q:"tags"` } // ToListenerListQuery formats a ListOpts into a query string. diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index 7d7466a081..6051c70978 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -19,25 +19,26 @@ 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"` + 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"` + Tags []string `q:"tags"` } // ToMonitorListQuery formats a ListOpts into a query string. @@ -141,6 +142,9 @@ type CreateOpts struct { // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Tags is a set of resource tags. New in version 2.5 + Tags []string `json:"tags,omitempty"` } // ToMonitorCreateMap builds a request body from CreateOpts. @@ -224,6 +228,9 @@ type UpdateOpts struct { // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` + + // Tags is a set of resource tags. New in version 2.5 + Tags []string `json:"tags,omitempty"` } // ToMonitorUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index 502581feba..f1060b2462 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -84,6 +84,10 @@ type Monitor struct { // The operating status of the monitor. OperatingStatus string `json:"operating_status"` + + // 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"` } // MonitorPage is the page returned by a pager when traversing over a diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go index c6ae88494d..d097e49084 100644 --- a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go @@ -24,7 +24,8 @@ const HealthmonitorsListBody = ` "timeout":1, "type":"PING", "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}], - "id":"466c8345-28d8-4f84-a246-e04380b0461d" + "id":"466c8345-28d8-4f84-a246-e04380b0461d", + "tags":[] }, { "admin_state_up":true, @@ -39,7 +40,8 @@ const HealthmonitorsListBody = ` "url_path":"/", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], - "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" + "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7", + "tags":["foobar"] } ] } @@ -61,7 +63,8 @@ const SingleHealthmonitorBody = ` "url_path":"/", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], - "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" + "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7", + "tags":[] } } ` @@ -82,7 +85,8 @@ const PostUpdateHealthmonitorBody = ` "url_path":"/another_check", "type":"HTTP", "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], - "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" + "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7", + "tags":[] } } ` @@ -99,6 +103,7 @@ var ( Type: "PING", ID: "466c8345-28d8-4f84-a246-e04380b0461d", Pools: []monitors.PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}}, + Tags: []string{}, } HealthmonitorDb = monitors.Monitor{ AdminStateUp: true, @@ -114,6 +119,7 @@ var ( HTTPMethod: "GET", ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, + Tags: []string{}, } HealthmonitorUpdated = monitors.Monitor{ AdminStateUp: true, @@ -129,6 +135,7 @@ var ( HTTPMethod: "GET", ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, + Tags: []string{}, } ) diff --git a/openstack/loadbalancer/v2/monitors/testing/requests_test.go b/openstack/loadbalancer/v2/monitors/testing/requests_test.go index 5b4bc6732b..9a3b82d647 100644 --- a/openstack/loadbalancer/v2/monitors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -66,6 +66,7 @@ func TestCreateHealthmonitor(t *testing.T) { Timeout: 10, MaxRetries: 5, MaxRetriesDown: 4, + Tags: []string{}, URLPath: "/check", ExpectedCodes: "200-299", }).Extract() diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index e86d1ef245..08c47a44d3 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -18,17 +18,18 @@ type ListOptsBuilder interface { // sort by a particular Pool attribute. SortDir sets the direction, and is // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - LBMethod string `q:"lb_algorithm"` - Protocol string `q:"protocol"` - ProjectID string `q:"project_id"` - AdminStateUp *bool `q:"admin_state_up"` - Name string `q:"name"` - ID string `q:"id"` - LoadbalancerID string `q:"loadbalancer_id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` + LBMethod string `q:"lb_algorithm"` + Protocol string `q:"protocol"` + ProjectID string `q:"project_id"` + AdminStateUp *bool `q:"admin_state_up"` + Name string `q:"name"` + ID string `q:"id"` + LoadbalancerID string `q:"loadbalancer_id"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tags []string `q:"tags"` } // ToPoolListQuery formats a ListOpts into a query string. From 2c7c40d2a5a824494d26a53a0fc976deae0b0ac5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 09:56:06 +0000 Subject: [PATCH 1705/2296] build(deps): bump golang.org/x/crypto from 0.14.0 to 0.15.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.15.0. - [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.15.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 2e0519a75a..ca3746a2a8 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud go 1.20 require ( - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.15.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.13.0 // indirect +require golang.org/x/sys v0.14.0 // indirect diff --git a/go.sum b/go.sum index 77548a5981..480bbbe96f 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= 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 c1a68b6d2f69c01d26acd780666ffdf76d0f9fb6 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Tue, 14 Nov 2023 16:43:33 +0100 Subject: [PATCH 1706/2296] baremetal: implemented ParsedLLDP in the standard PluginData Closes: #2833 --- openstack/baremetal/inventory/plugindata.go | 4 +++- openstack/baremetal/inventory/testing/fixtures.go | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/openstack/baremetal/inventory/plugindata.go b/openstack/baremetal/inventory/plugindata.go index ebb9a840ba..c19f77610e 100644 --- a/openstack/baremetal/inventory/plugindata.go +++ b/openstack/baremetal/inventory/plugindata.go @@ -87,11 +87,12 @@ type ConfigurationType struct { Managers []HardwareManager `json:"managers"` } +type ParsedLLDP = map[string]interface{} + type ProcessedInterfaceType struct { InterfaceType // Whether PXE was enabled on this interface during inspection PXEEnabled bool `json:"pxe_enabled"` - // TODO(dtantsur): add LLDPProcessed once it's actually implemented } // StandardPluginData represents the plugin data as collected and processes @@ -108,6 +109,7 @@ type StandardPluginData struct { Extra ExtraDataType `json:"extra"` MACs []string `json:"macs"` NUMATopology NUMATopology `json:"numa_topology"` + ParsedLLDP map[string]ParsedLLDP `json:"parsed_lldp"` RawLLDP map[string][]LLDPTLVType `json:"lldp_raw"` RootDisk RootDiskType `json:"root_disk"` ValidInterfaces map[string]ProcessedInterfaceType `json:"valid_interfaces"` diff --git a/openstack/baremetal/inventory/testing/fixtures.go b/openstack/baremetal/inventory/testing/fixtures.go index a50c39d9ea..d2f93176cc 100644 --- a/openstack/baremetal/inventory/testing/fixtures.go +++ b/openstack/baremetal/inventory/testing/fixtures.go @@ -258,6 +258,12 @@ var StandardPluginDataSample = fmt.Sprintf(` "macs": [ "52:54:00:4e:3d:30" ], + "parsed_lldp": { + "eth0": { + "switch_chassis_id": "11:22:33:aa:bb:cc", + "switch_system_name": "sw01-dist-1b-b12" + } + }, "root_disk": { "hctl": null, "model": "", @@ -471,6 +477,12 @@ var StandardPluginData = inventory.StandardPluginData{ Error: "", Extra: ExtraData, MACs: []string{"52:54:00:4e:3d:30"}, + ParsedLLDP: map[string]inventory.ParsedLLDP{ + "eth0": map[string]interface{}{ + "switch_chassis_id": "11:22:33:aa:bb:cc", + "switch_system_name": "sw01-dist-1b-b12", + }, + }, RawLLDP: map[string][]inventory.LLDPTLVType{ "eth0": { { From 5259ac4fe054749685880e04e9988b5e516d641b Mon Sep 17 00:00:00 2001 From: Pierre-Yves Jourel Date: Tue, 28 Feb 2023 02:40:23 -0500 Subject: [PATCH 1707/2296] 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 522d29fda056ac1cbbd15404ea13b6d544917380 Mon Sep 17 00:00:00 2001 From: guoguangwu Date: Thu, 23 Nov 2023 13:22:44 +0800 Subject: [PATCH 1708/2296] chore: use String() instead of fmt.Sprintf Signed-off-by: guoguangwu --- openstack/blockstorage/apiversions/errors.go | 2 +- openstack/compute/apiversions/errors.go | 6 +----- openstack/container/v1/capsules/errors.go | 4 +--- openstack/containerinfra/apiversions/errors.go | 2 +- openstack/orchestration/v1/stacks/errors.go | 6 +++--- openstack/sharedfilesystems/apiversions/errors.go | 2 +- testhelper/convenience.go | 2 +- 7 files changed, 9 insertions(+), 15 deletions(-) diff --git a/openstack/blockstorage/apiversions/errors.go b/openstack/blockstorage/apiversions/errors.go index 8f0f7628de..03dd82cb61 100644 --- a/openstack/blockstorage/apiversions/errors.go +++ b/openstack/blockstorage/apiversions/errors.go @@ -9,7 +9,7 @@ import ( type ErrVersionNotFound struct{} func (e ErrVersionNotFound) Error() string { - return fmt.Sprintf("Unable to find requested API version") + return "Unable to find requested API version" } // ErrMultipleVersionsFound is the error when a request for an API diff --git a/openstack/compute/apiversions/errors.go b/openstack/compute/apiversions/errors.go index 696f81f793..387b68fd71 100644 --- a/openstack/compute/apiversions/errors.go +++ b/openstack/compute/apiversions/errors.go @@ -1,13 +1,9 @@ package apiversions -import ( - "fmt" -) - // ErrVersionNotFound is the error when the requested API version // could not be found. type ErrVersionNotFound struct{} func (e ErrVersionNotFound) Error() string { - return fmt.Sprintf("Unable to find requested API version") + return "Unable to find requested API version" } diff --git a/openstack/container/v1/capsules/errors.go b/openstack/container/v1/capsules/errors.go index 6542e66ec8..a5f602dc83 100644 --- a/openstack/container/v1/capsules/errors.go +++ b/openstack/container/v1/capsules/errors.go @@ -1,8 +1,6 @@ package capsules import ( - "fmt" - "github.com/gophercloud/gophercloud" ) @@ -11,5 +9,5 @@ type ErrInvalidDataFormat struct { } func (e ErrInvalidDataFormat) Error() string { - return fmt.Sprintf("Data in neither json nor yaml format.") + return "Data in neither json nor yaml format." } diff --git a/openstack/containerinfra/apiversions/errors.go b/openstack/containerinfra/apiversions/errors.go index 8f0f7628de..03dd82cb61 100644 --- a/openstack/containerinfra/apiversions/errors.go +++ b/openstack/containerinfra/apiversions/errors.go @@ -9,7 +9,7 @@ import ( type ErrVersionNotFound struct{} func (e ErrVersionNotFound) Error() string { - return fmt.Sprintf("Unable to find requested API version") + return "Unable to find requested API version" } // ErrMultipleVersionsFound is the error when a request for an API diff --git a/openstack/orchestration/v1/stacks/errors.go b/openstack/orchestration/v1/stacks/errors.go index a6febe0408..2d51736707 100644 --- a/openstack/orchestration/v1/stacks/errors.go +++ b/openstack/orchestration/v1/stacks/errors.go @@ -20,7 +20,7 @@ type ErrInvalidDataFormat struct { } func (e ErrInvalidDataFormat) Error() string { - return fmt.Sprintf("Data in neither json nor yaml format.") + return "Data in neither json nor yaml format." } type ErrInvalidTemplateFormatVersion struct { @@ -29,7 +29,7 @@ type ErrInvalidTemplateFormatVersion struct { } func (e ErrInvalidTemplateFormatVersion) Error() string { - return fmt.Sprintf("Template format version not found.") + return "Template format version not found." } type ErrTemplateRequired struct { @@ -37,5 +37,5 @@ type ErrTemplateRequired struct { } func (e ErrTemplateRequired) Error() string { - return fmt.Sprintf("Template required for this function.") + return "Template required for this function." } diff --git a/openstack/sharedfilesystems/apiversions/errors.go b/openstack/sharedfilesystems/apiversions/errors.go index 8f0f7628de..03dd82cb61 100644 --- a/openstack/sharedfilesystems/apiversions/errors.go +++ b/openstack/sharedfilesystems/apiversions/errors.go @@ -9,7 +9,7 @@ import ( type ErrVersionNotFound struct{} func (e ErrVersionNotFound) Error() string { - return fmt.Sprintf("Unable to find requested API version") + return "Unable to find requested API version" } // ErrMultipleVersionsFound is the error when a request for an API diff --git a/testhelper/convenience.go b/testhelper/convenience.go index 2ae247052b..a767684d2d 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -345,7 +345,7 @@ func AssertNoErr(t *testing.T, e error) { // nil func AssertErr(t *testing.T, e error) { if e == nil { - logFatal(t, fmt.Sprintf("expected error, got nil")) + logFatal(t, "expected error, got nil") } } From 7ef0ae6fa2673accf18bf153b3394486805dffe4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 09:46:41 +0000 Subject: [PATCH 1709/2296] build(deps): bump golang.org/x/crypto from 0.15.0 to 0.16.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.15.0 to 0.16.0. - [Commits](https://github.com/golang/crypto/compare/v0.15.0...v0.16.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index ca3746a2a8..f397daec0f 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud go 1.20 require ( - golang.org/x/crypto v0.15.0 + golang.org/x/crypto v0.16.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.14.0 // indirect +require golang.org/x/sys v0.15.0 // indirect diff --git a/go.sum b/go.sum index 480bbbe96f..a405649bac 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= 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 711b0426d3558c3a572a87a73eae12a016614842 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:01:02 +0000 Subject: [PATCH 1710/2296] build(deps): bump kiegroup/git-backporting from 4.4.0 to 4.4.1 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.4.0 to 4.4.1. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/4313be48e73b299a20b3c8290fd1ea8704e8dd5e...aac73bf7c5fd0e94086d421b6b9646d77efdad76) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 4e35d42bb2..0e93f95392 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -36,7 +36,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@4313be48e73b299a20b3c8290fd1ea8704e8dd5e + uses: kiegroup/git-backporting@aac73bf7c5fd0e94086d421b6b9646d77efdad76 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} From ac6e5ab6a0b7718b38d80e5163be12e7c08ba7f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 10:01:37 +0000 Subject: [PATCH 1711/2296] build(deps): bump actions/setup-go from 4 to 5 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .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 +- .github/workflows/gomod.yml | 2 +- .github/workflows/reauth-retests.yaml | 2 +- .github/workflows/semver-auto.yaml | 2 +- .github/workflows/unit.yml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 18de39ec85..8fead8ca83 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -88,7 +88,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@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 37d08bf3cb..d4fd5561af 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -49,7 +49,7 @@ jobs: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy' - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index d8287ab520..16dbc5b548 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -48,7 +48,7 @@ jobs: CINDER_ISCSI_HELPER=lioadm enabled_services: 's-account,s-container,s-object,s-proxy,c-bak' - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index d817c8c06f..6f766195dd 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -49,7 +49,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@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 8add827786..44a7c9219a 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -47,7 +47,7 @@ jobs: conf_overrides: | CINDER_ISCSI_HELPER=lioadm - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index f5ef2cdd9b..542fe2afa4 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -54,7 +54,7 @@ jobs: MAGNUMCLIENT_BRANCH=${{ matrix.openstack_version }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 37a0f31c8d..88554b8b21 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -49,7 +49,7 @@ jobs: 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 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 1f6acd189e..e66e656c74 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -41,7 +41,7 @@ jobs: Q_TUNNEL_TYPES=vxlan,gre 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 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 950c17c3d9..edfac2b809 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -45,7 +45,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index 4f561eb212..fde871827e 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -45,7 +45,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 18d4ec5cec..05c003bf4b 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -48,7 +48,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@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 7d0b9d4f65..3cdc0a936c 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -49,7 +49,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@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index a92fe0f80b..02cf1cf075 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -48,7 +48,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@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 7e8c99a5d7..6983abdf64 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -50,7 +50,7 @@ jobs: Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords 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 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 350168de26..da2b798580 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -52,7 +52,7 @@ jobs: allow_object_versioning = true enabled_services: 's-account,s-container,s-object,s-proxy' - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 479b627a75..76000ed30b 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -48,7 +48,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@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 1e22261d49..f8518db70c 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -45,7 +45,7 @@ jobs: with: branch: ${{ matrix.openstack_version }} - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 8ec350f161..114aa2d009 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -61,7 +61,7 @@ jobs: MANILA_CONFIGURE_DEFAULT_TYPES=True MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE=false - name: Checkout go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '^1.20' - name: Run Gophercloud acceptance tests diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml index 8a17b797b3..43dc5c242b 100644 --- a/.github/workflows/gomod.yml +++ b/.github/workflows/gomod.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 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 0e18ca15ea..983102a048 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@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index d23fff01d8..fa140c3889 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -37,7 +37,7 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} labels: semver:unknown - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: '1' diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 1d0bc5b299..c3cbd434e3 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@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} From 4c78de1f2b2f289cbebe5684e7dca0ef9221e7eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:50:18 +0000 Subject: [PATCH 1712/2296] build(deps): bump kiegroup/git-backporting from 4.4.1 to 4.5.0 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.4.1 to 4.5.0. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/aac73bf7c5fd0e94086d421b6b9646d77efdad76...204ebd4376d7501696d52aec8fea2a2f6e09328f) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 0e93f95392..53c0c66ee8 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -36,7 +36,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@aac73bf7c5fd0e94086d421b6b9646d77efdad76 + uses: kiegroup/git-backporting@204ebd4376d7501696d52aec8fea2a2f6e09328f with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} From 69926992fdd766df6dd7fbdb92c3081c63b549b0 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 1713/2296] 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] --- .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 496e65efcc..9e1b8711dd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -21,12 +21,12 @@ jobs: uses: actions/checkout@v4 - 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 c433929ab93c88b98f7fead9701c64f3751da49f 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 1714/2296] 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] --- .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 8fead8ca83..9b47f5101c 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -102,7 +102,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 d4fd5561af..d5cd352a8f 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -63,7 +63,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 16dbc5b548..eb93e88b7b 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.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-blockstorage-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 6f766195dd..ef830cbc4b 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -63,7 +63,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 44a7c9219a..c6781ab165 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.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-compute-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 542fe2afa4..8f5f73e40e 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -68,7 +68,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 88554b8b21..67859fb870 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -63,7 +63,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 e66e656c74..27b07b2cbc 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -55,7 +55,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 edfac2b809..6acece36db 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.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-identity-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-imageservice.yaml index fde871827e..4b7dd255ae 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.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-imageservice-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 05c003bf4b..a52ce79865 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.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-keymanager-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 3cdc0a936c..0e515d49a9 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -63,7 +63,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 02cf1cf075..574971a5e7 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.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-messaging-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 6983abdf64..fca72f43e7 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.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-networking-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index da2b798580..91c6b616d4 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -66,7 +66,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 76000ed30b..03be9d9b63 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.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-orchestration-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index f8518db70c..fe83089044 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.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-placement-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 114aa2d009..afebf7b3a8 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -75,7 +75,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 80de6dda601a9c72cef6b262dce2cd7849648b7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 00:02:55 +0000 Subject: [PATCH 1715/2296] build(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.16.0 to 0.17.0. - [Commits](https://github.com/golang/crypto/compare/v0.16.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f397daec0f..43bb96f1d8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gophercloud/gophercloud go 1.20 require ( - golang.org/x/crypto v0.16.0 + golang.org/x/crypto v0.17.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index a405649bac..2e1c942142 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= From 9379f861fe6521a99166a7dd6bad954648693cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 1 Jan 2024 14:07:01 +0100 Subject: [PATCH 1716/2296] 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 ada7a8b7e98e5fc7b77d572200a5f5b490829339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 1 Jan 2024 14:48:11 +0100 Subject: [PATCH 1717/2296] 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 26b668e12c2dc613f6028398722adaef576631bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 1 Jan 2024 14:38:16 +0100 Subject: [PATCH 1718/2296] 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 542fe2afa4..69f8882bba 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -14,27 +14,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: @@ -45,13 +69,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@v5 From aea2739f24a08defec402f5b5fa2afb2c597d514 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 09:34:55 +0000 Subject: [PATCH 1719/2296] build(deps): bump golang.org/x/crypto from 0.17.0 to 0.18.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.17.0 to 0.18.0. - [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.18.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 43bb96f1d8..685ca96676 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud go 1.20 require ( - golang.org/x/crypto v0.17.0 + golang.org/x/crypto v0.18.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.15.0 // indirect +require golang.org/x/sys v0.16.0 // indirect diff --git a/go.sum b/go.sum index 2e1c942142..de48846989 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= 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 05b545790fe779b6d3a993ce3a3f65aab719f303 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 16 Jan 2024 10:33:38 +0100 Subject: [PATCH 1720/2296] Bump go_apidiff --- .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 fa140c3889..d3217956f5 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -46,7 +46,7 @@ jobs: # 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 + uses: joelanford/go-apidiff@v002aa613b261e8d1547b516fb71793280f05bb78 # 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. From d131ce6b192a45fb3bbfa64d4a685b4cea3e4e89 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 16 Jan 2024 08:26:03 -0500 Subject: [PATCH 1721/2296] Fix go-apidiff commit ID --- .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 d3217956f5..c042be134a 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -46,7 +46,7 @@ jobs: # 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@v002aa613b261e8d1547b516fb71793280f05bb78 + uses: joelanford/go-apidiff@002aa613b261e8d1547b516fb71793280f05bb78 # 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. From 995c232824cc1ef5f09ff9341a9323a55ff94e53 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche Date: Wed, 19 Jul 2023 10:18:27 +0000 Subject: [PATCH 1722/2296] 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..52669925ee 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..acf3a319dd 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 e0ddcb72e0..cffe5e3488 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"}] } } ` @@ -287,7 +289,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", @@ -540,7 +547,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 34656f50ff..50ede06d3e 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 fc018d2bcd547a993aa1e4cce5472b38b380b0e6 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche Date: Wed, 19 Jul 2023 14:03:34 +0000 Subject: [PATCH 1723/2296] loadbalancer additional_vips acceptance tests --- .../openstack/loadbalancer/v2/loadbalancer.go | 11 ++++++----- .../loadbalancer/v2/loadbalancers_test.go | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 74bd7d1c09..5cc35dd2b0 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -110,17 +110,18 @@ 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, policyID string) (*loadbalancers.LoadBalancer, error) { +func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string, policyID string, additionalVips []loadbalancers.AdditionalVip) (*loadbalancers.LoadBalancer, error) { lbName := tools.RandomString("TESTACCT-", 8) lbDescription := tools.RandomString("TESTACCT-DESC-", 8) t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) createOpts := loadbalancers.CreateOpts{ - Name: lbName, - Description: lbDescription, - VipSubnetID: subnetID, - AdminStateUp: gophercloud.Enabled, + Name: lbName, + Description: lbDescription, + VipSubnetID: subnetID, + AdminStateUp: gophercloud.Enabled, + AdditionalVips: additionalVips, } if len(tags) > 0 { createOpts.Tags = tags diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index bcdcb22a0f..19b678c7c4 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -51,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, "", nil) th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, lbClient, lb.ID) @@ -111,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, "", nil) th.AssertNoErr(t, err) defer DeleteLoadBalancer(t, lbClient, lb.ID) @@ -266,14 +266,25 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) + additionalSubnet, err := networking.CreateSubnet(t, netClient, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, netClient, additionalSubnet.ID) + + additionalSubnetPort, err := networking.CreatePort(t, netClient, network.ID, additionalSubnet.ID) + th.AssertNoErr(t, err) + defer networking.DeletePort(t, netClient, additionalSubnetPort.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, policy1.ID) + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags, policy1.ID, []loadbalancers.AdditionalVip{{SubnetID: additionalSubnet.ID, IPAddress: additionalSubnetPort.FixedIPs[0].IPAddress}}) th.AssertNoErr(t, err) th.AssertEquals(t, lb.VipQosPolicyID, policy1.ID) + th.AssertEquals(t, 1, len(lb.AdditionalVips)) + th.AssertEquals(t, additionalSubnetPort.FixedIPs[0].IPAddress, lb.AdditionalVips[0].IPAddress) + th.AssertEquals(t, additionalSubnetPort.FixedIPs[0].SubnetID, lb.AdditionalVips[0].SubnetID) defer DeleteLoadBalancer(t, lbClient, lb.ID) lbDescription := "" @@ -472,7 +483,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, "", nil) th.AssertNoErr(t, err) defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) From b25d38a58eadee87ce686505aa42e010b2444bc3 Mon Sep 17 00:00:00 2001 From: Ludovic Lamarche Date: Fri, 25 Aug 2023 09:14:00 +0000 Subject: [PATCH 1724/2296] fix acceptance test using specific subnet cidr --- .../openstack/loadbalancer/v2/loadbalancers_test.go | 4 ++-- .../acceptance/openstack/networking/v2/networking.go | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 19b678c7c4..c262b7d026 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -262,11 +262,11 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, netClient, network.ID) - subnet, err := networking.CreateSubnet(t, netClient, network.ID) + subnet, err := networking.CreateSubnetWithCIDR(t, netClient, network.ID, "192.168.1.0/24", "192.168.1.1") th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, subnet.ID) - additionalSubnet, err := networking.CreateSubnet(t, netClient, network.ID) + additionalSubnet, err := networking.CreateSubnetWithCIDR(t, netClient, network.ID, "192.168.2.0/24", "192.168.2.1") th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, additionalSubnet.ID) diff --git a/internal/acceptance/openstack/networking/v2/networking.go b/internal/acceptance/openstack/networking/v2/networking.go index 6777b39dcb..666e1b549a 100644 --- a/internal/acceptance/openstack/networking/v2/networking.go +++ b/internal/acceptance/openstack/networking/v2/networking.go @@ -280,11 +280,17 @@ func CreatePortWithMultipleFixedIPs(t *testing.T, client *gophercloud.ServiceCli // CreateSubnet will create a subnet on the specified Network ID. An error // will be returned if the subnet could not be created. func CreateSubnet(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) + return CreateSubnetWithCIDR(t, client, networkID, subnetCIDR, subnetGateway) +} + +// CreateSubnetWithCIDR will create a subnet on the specified Network ID and CIDR. An error +// will be returned if the subnet could not be created. +func CreateSubnetWithCIDR(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetCIDR, subnetGateway string) (*subnets.Subnet, error) { + subnetName := tools.RandomString("TESTACC-", 8) + subnetDescription := tools.RandomString("TESTACC-DESC-", 8) createOpts := subnets.CreateOpts{ NetworkID: networkID, CIDR: subnetCIDR, From eeda2a27a071adea43218807336435475d0197aa Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 16 Jan 2024 15:08:47 -0500 Subject: [PATCH 1725/2296] gofumpt & fix e2e The additional VIPs should not be created before the load balancer, Octavia already takes care of creating the port for us. Also, this feature is only available in Zed and beyond. --- .../loadbalancer/v2/loadbalancers_test.go | 46 +++++++++++++------ .../openstack/networking/v2/networking.go | 2 +- .../v2/loadbalancers/testing/fixtures_test.go | 9 ++-- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index c262b7d026..9d33c74576 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -245,15 +245,11 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { defer DeleteMonitor(t, lbClient, lb.ID, monitor.ID) } -func TestLoadbalancersCRUD(t *testing.T) { - netClient, err := clients.NewNetworkV2Client() - th.AssertNoErr(t, err) +func TestLoadBalancerWithAdditionalVips(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/zed") - // 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) + netClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - defer policies.DeleteQoSPolicy(t, netClient, policy2.ID) lbClient, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) @@ -270,21 +266,45 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeleteSubnet(t, netClient, additionalSubnet.ID) - additionalSubnetPort, err := networking.CreatePort(t, netClient, network.ID, additionalSubnet.ID) + tags := []string{"test"} + // Octavia takes care of creating the port for the loadbalancer + additionalSubnetIP := "192.168.2.207" + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags, "", []loadbalancers.AdditionalVip{{SubnetID: additionalSubnet.ID, IPAddress: additionalSubnetIP}}) + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(lb.AdditionalVips)) + th.AssertEquals(t, additionalSubnetIP, lb.AdditionalVips[0].IPAddress) + defer DeleteLoadBalancer(t, lbClient, lb.ID) +} + +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) + + network, err := networking.CreateNetwork(t, netClient) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, netClient, network.ID) + + subnet, err := networking.CreateSubnetWithCIDR(t, netClient, network.ID, "192.168.1.0/24", "192.168.1.1") th.AssertNoErr(t, err) - defer networking.DeletePort(t, netClient, additionalSubnetPort.ID) + 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, policy1.ID, []loadbalancers.AdditionalVip{{SubnetID: additionalSubnet.ID, IPAddress: additionalSubnetPort.FixedIPs[0].IPAddress}}) + lb, err := CreateLoadBalancer(t, lbClient, subnet.ID, tags, policy1.ID, nil) th.AssertNoErr(t, err) th.AssertEquals(t, lb.VipQosPolicyID, policy1.ID) - th.AssertEquals(t, 1, len(lb.AdditionalVips)) - th.AssertEquals(t, additionalSubnetPort.FixedIPs[0].IPAddress, lb.AdditionalVips[0].IPAddress) - th.AssertEquals(t, additionalSubnetPort.FixedIPs[0].SubnetID, lb.AdditionalVips[0].SubnetID) defer DeleteLoadBalancer(t, lbClient, lb.ID) lbDescription := "" diff --git a/internal/acceptance/openstack/networking/v2/networking.go b/internal/acceptance/openstack/networking/v2/networking.go index 666e1b549a..2d3ce57e27 100644 --- a/internal/acceptance/openstack/networking/v2/networking.go +++ b/internal/acceptance/openstack/networking/v2/networking.go @@ -394,7 +394,7 @@ func CreateSubnetWithDefaultGateway(t *testing.T, client *gophercloud.ServiceCli // specified Network ID. An error will be returned if the subnet could not be // created. func CreateSubnetWithNoGateway(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) { - var noGateway = "" + noGateway := "" subnetName := tools.RandomString("TESTACC-", 8) subnetOctet := tools.RandomInt(1, 250) subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet) diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go index cffe5e3488..948ed4ef61 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go @@ -251,8 +251,10 @@ const GetLoadbalancerStatsBody = ` } ` -var createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") -var updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") +var ( + createdTime, _ = time.Parse(time.RFC3339, "2019-06-30T04:15:37Z") + updatedTime, _ = time.Parse(time.RFC3339, "2019-06-30T05:18:49Z") +) var ( LoadbalancerWeb = loadbalancers.LoadBalancer{ @@ -294,7 +296,8 @@ var ( 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", From 571d3f585aa2baa39408ec51e9274940d00e8596 Mon Sep 17 00:00:00 2001 From: Nobuhiro MIKI Date: Fri, 19 Jan 2024 07:29:18 +0000 Subject: [PATCH 1726/2296] Fix panic in ExtractIntoStructPtr and ExtractIntoSlicePtr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passing nil as an argument to 'ExtractIntoStructPtr' or 'ExtractIntoSlicePtr' causes "panic: runtime error: invalid memory address or nil pointer dereference". This is an invalid argument and should be corrected to return an error. For reference, standard package functions such as 'json.Unmarshal' return an error instead of panic. Signed-off-by: Nobuhiro MIKI Co-authored-by: pýrus --- results.go | 18 ++++++++++++++++++ testing/results_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/results.go b/results.go index b3ee9d5682..34ae29b073 100644 --- a/results.go +++ b/results.go @@ -184,10 +184,19 @@ func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { return r.Err } + if to == nil { + return fmt.Errorf("Expected pointer, got %T", to) + } + t := reflect.TypeOf(to) if k := t.Kind(); k != reflect.Ptr { return fmt.Errorf("Expected pointer, got %v", k) } + + if reflect.ValueOf(to).IsNil() { + return fmt.Errorf("Expected pointer, got %T", to) + } + switch t.Elem().Kind() { case reflect.Struct: return r.extractIntoPtr(to, label) @@ -210,10 +219,19 @@ func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error { return r.Err } + if to == nil { + return fmt.Errorf("Expected pointer, got %T", to) + } + t := reflect.TypeOf(to) if k := t.Kind(); k != reflect.Ptr { return fmt.Errorf("Expected pointer, got %v", k) } + + if reflect.ValueOf(to).IsNil() { + return fmt.Errorf("Expected pointer, got %T", to) + } + switch t.Elem().Kind() { case reflect.Slice: return r.extractIntoPtr(to, label) diff --git a/testing/results_test.go b/testing/results_test.go index ddcb1322a4..4905139545 100644 --- a/testing/results_test.go +++ b/testing/results_test.go @@ -113,6 +113,40 @@ func TestUnmarshalAnonymousStructs(t *testing.T) { th.AssertEquals(t, "Canada unmarshalled", actual.Location) } +func TestUnmarshalNilStruct(t *testing.T) { + var x *TestPerson + var y TestPerson + + err1 := gophercloud.Result{}.ExtractIntoStructPtr(&x, "") + err2 := gophercloud.Result{}.ExtractIntoStructPtr(nil, "") + err3 := gophercloud.Result{}.ExtractIntoStructPtr(y, "") + err4 := gophercloud.Result{}.ExtractIntoStructPtr(&y, "") + err5 := gophercloud.Result{}.ExtractIntoStructPtr(x, "") + + th.AssertErr(t, err1) + th.AssertErr(t, err2) + th.AssertErr(t, err3) + th.AssertNoErr(t, err4) + th.AssertErr(t, err5) +} + +func TestUnmarshalNilSlice(t *testing.T) { + var x *[]TestPerson + var y []TestPerson + + err1 := gophercloud.Result{}.ExtractIntoSlicePtr(&x, "") + err2 := gophercloud.Result{}.ExtractIntoSlicePtr(nil, "") + err3 := gophercloud.Result{}.ExtractIntoSlicePtr(y, "") + err4 := gophercloud.Result{}.ExtractIntoSlicePtr(&y, "") + err5 := gophercloud.Result{}.ExtractIntoSlicePtr(x, "") + + th.AssertErr(t, err1) + th.AssertErr(t, err2) + th.AssertErr(t, err3) + th.AssertNoErr(t, err4) + th.AssertErr(t, err5) +} + // TestUnmarshalSliceofAnonymousStructs tests if UnmarshalJSON is called on each // of the anonymous structs contained in an overarching struct slice. func TestUnmarshalSliceOfAnonymousStructs(t *testing.T) { From 74052e2594a7c4da0db3b8ea2ec419e38f5b2d95 Mon Sep 17 00:00:00 2001 From: Eugene Zuev Date: Thu, 18 Jan 2024 19:24:11 +0000 Subject: [PATCH 1727/2296] 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 34a7c1392494c1f3b8218fd86b40b7c7ae44ba02 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Jourel Date: Tue, 28 Feb 2023 14:44:00 -0500 Subject: [PATCH 1728/2296] 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 74bd7d1c09..f940ff1d16 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" @@ -680,3 +682,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 ab9124625e81a7a38bc46b5e4e2b06199315bdc3 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 25 Jan 2024 13:32:29 +0100 Subject: [PATCH 1729/2296] Set the user agent to an unreleased version Building from the development branch should not give a user agent that matches a release. --- provider_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider_client.go b/provider_client.go index 9238ecbb73..8daf611c98 100644 --- a/provider_client.go +++ b/provider_client.go @@ -13,7 +13,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v1.5.0" + DefaultUserAgent = "gophercloud/v2.0.0-unreleased" DefaultMaxBackoffRetries = 60 ) From f29f4a573438dbe381df13738671d0a61994b764 Mon Sep 17 00:00:00 2001 From: Alban PRATS Date: Thu, 25 Jan 2024 09:24:33 -0500 Subject: [PATCH 1730/2296] Adding missing QoS field for router --- openstack/networking/v2/extensions/layer3/routers/results.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index 0c93918991..1c93eab506 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -14,6 +14,7 @@ type GatewayInfo struct { NetworkID string `json:"network_id,omitempty"` EnableSNAT *bool `json:"enable_snat,omitempty"` ExternalFixedIPs []ExternalFixedIP `json:"external_fixed_ips,omitempty"` + QoSPolicyID string `json:"qos_policy_id,omitempty"` } // ExternalFixedIP is the IP address and subnet ID of the external gateway of a From f7533c95474de0ae96f4b8c8061ef790070f6899 Mon Sep 17 00:00:00 2001 From: Alban PRATS Date: Thu, 25 Jan 2024 10:03:44 -0500 Subject: [PATCH 1731/2296] Added tests for gateway qos_policy --- .../layer3/routers/testing/requests_test.go | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 81818e59d7..4f2de23b53 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -53,7 +53,8 @@ func TestList(t *testing.T) { "external_fixed_ips": [ {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, {"ip_address": "198.51.100.33", "subnet_id": "1d699529-bdfd-43f8-bcaa-bff00c547af2"} - ] + ], + "qos_policy_id": "6601bae5-f15a-4687-8be9-ddec9a2f8a8b" }, "name": "gateway", "admin_state_up": true, @@ -103,6 +104,7 @@ func TestList(t *testing.T) { {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, {IPAddress: "198.51.100.33", SubnetID: "1d699529-bdfd-43f8-bcaa-bff00c547af2"}, }, + QoSPolicyID: "6601bae5-f15a-4687-8be9-ddec9a2f8a8b", }, AdminStateUp: true, Distributed: false, @@ -141,7 +143,8 @@ func TestCreate(t *testing.T) { "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b", "external_fixed_ips": [ {"subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} - ] + ], + "qos_policy_id": "6601bae5-f15a-4687-8be9-ddec9a2f8a8b" }, "availability_zone_hints": ["zone1", "zone2"] } @@ -160,7 +163,8 @@ func TestCreate(t *testing.T) { "enable_snat": false, "external_fixed_ips": [ {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} - ] + ], + "qos_policy_id": "6601bae5-f15a-4687-8be9-ddec9a2f8a8b" }, "name": "foo_router", "admin_state_up": false, @@ -175,6 +179,7 @@ func TestCreate(t *testing.T) { asu := false enableSNAT := false + qosID := "6601bae5-f15a-4687-8be9-ddec9a2f8a8b" efi := []routers.ExternalFixedIP{ { SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def", @@ -184,6 +189,7 @@ func TestCreate(t *testing.T) { NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", EnableSNAT: &enableSNAT, ExternalFixedIPs: efi, + QoSPolicyID: qosID, } options := routers.CreateOpts{ Name: "foo_router", @@ -224,7 +230,8 @@ func TestGet(t *testing.T) { "network_id": "85d76829-6415-48ff-9c63-5c5ca8c61ac6", "external_fixed_ips": [ {"ip_address": "198.51.100.33", "subnet_id": "1d699529-bdfd-43f8-bcaa-bff00c547af2"} - ] + ], + "qos_policy_id": "6601bae5-f15a-4687-8be9-ddec9a2f8a8b" }, "routes": [ { @@ -252,6 +259,7 @@ func TestGet(t *testing.T) { ExternalFixedIPs: []routers.ExternalFixedIP{ {IPAddress: "198.51.100.33", SubnetID: "1d699529-bdfd-43f8-bcaa-bff00c547af2"}, }, + QoSPolicyID: "6601bae5-f15a-4687-8be9-ddec9a2f8a8b", }) th.AssertEquals(t, n.Name, "router1") th.AssertEquals(t, n.AdminStateUp, true) @@ -275,7 +283,8 @@ func TestUpdate(t *testing.T) { "router": { "name": "new_name", "external_gateway_info": { - "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b" + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "qos_policy_id": "01ba32e5-f15a-4687-8be9-ddec92a2f8a8" }, "routes": [ { @@ -298,7 +307,8 @@ func TestUpdate(t *testing.T) { "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", "external_fixed_ips": [ {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} - ] + ], + "qos_policy_id": "01ba32e5-f15a-4687-8be9-ddec92a2f8a8" }, "name": "new_name", "admin_state_up": true, @@ -316,7 +326,10 @@ func TestUpdate(t *testing.T) { `) }) - gwi := routers.GatewayInfo{NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b"} + gwi := routers.GatewayInfo{ + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + QoSPolicyID: "01ba32e5-f15a-4687-8be9-ddec92a2f8a8", + } r := []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}} options := routers.UpdateOpts{Name: "new_name", GatewayInfo: &gwi, Routes: &r} From 086cd1bfa067ae2903ad5018acedf685f9850c32 Mon Sep 17 00:00:00 2001 From: nikParasyr Date: Sun, 28 Jan 2024 11:20:32 +0100 Subject: [PATCH 1732/2296] db/v1/instance: Add configuration to createOpts Add configuration group id to createOpts and update unit tests accordingly. --- openstack/db/v1/instances/requests.go | 6 ++++++ openstack/db/v1/instances/testing/fixtures_test.go | 1 + openstack/db/v1/instances/testing/requests_test.go | 2 ++ 3 files changed, 9 insertions(+) diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go index 26638157ca..f7a09d2055 100644 --- a/openstack/db/v1/instances/requests.go +++ b/openstack/db/v1/instances/requests.go @@ -49,6 +49,8 @@ func (opts NetworkOpts) ToMap() (map[string]interface{}, error) { type CreateOpts struct { // The availability zone of the instance. AvailabilityZone string `json:"availability_zone,omitempty"` + // ID of the configuration group that you want to attach to the instance. + Configuration string `json:"configuration,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 @@ -93,6 +95,10 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { instance["availability_zone"] = opts.AvailabilityZone } + if opts.Configuration != "" { + instance["configuration"] = opts.Configuration + } + 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 7abd42e3e7..b18b574671 100644 --- a/openstack/db/v1/instances/testing/fixtures_test.go +++ b/openstack/db/v1/instances/testing/fixtures_test.go @@ -105,6 +105,7 @@ var createReq = ` { "instance": { "availability_zone": "us-east1", + "configuration": "4a78b397-c355-4127-be45-56230b2ab74e", "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 62d615d7a9..652d442131 100644 --- a/openstack/db/v1/instances/testing/requests_test.go +++ b/openstack/db/v1/instances/testing/requests_test.go @@ -18,6 +18,7 @@ func TestCreate(t *testing.T) { opts := instances.CreateOpts{ AvailabilityZone: "us-east1", + Configuration: "4a78b397-c355-4127-be45-56230b2ab74e", Name: "json_rack_instance", FlavorRef: "1", Databases: db.BatchCreateOpts{ @@ -50,6 +51,7 @@ func TestCreateWithFault(t *testing.T) { opts := instances.CreateOpts{ AvailabilityZone: "us-east1", + Configuration: "4a78b397-c355-4127-be45-56230b2ab74e", Name: "json_rack_instance", FlavorRef: "1", Databases: db.BatchCreateOpts{ From 11729a871443ebbfb7152e19b281ad4c1c556462 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 09:18:16 +0000 Subject: [PATCH 1733/2296] build(deps): bump getsentry/action-github-app-token from 2.0.0 to 3.0.0 Bumps [getsentry/action-github-app-token](https://github.com/getsentry/action-github-app-token) from 2.0.0 to 3.0.0. - [Release notes](https://github.com/getsentry/action-github-app-token/releases) - [Commits](https://github.com/getsentry/action-github-app-token/compare/97c9e23528286821f97fba885c1b1123284b29cc...d4b5da6c5e37703f8c3b3e43abb5705b46e159cc) --- updated-dependencies: - dependency-name: getsentry/action-github-app-token dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 53c0c66ee8..2a7f97ca6f 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -25,7 +25,7 @@ jobs: steps: - name: Generate a token from the gophercloud-backport-bot github-app id: generate_token - uses: getsentry/action-github-app-token@97c9e23528286821f97fba885c1b1123284b29cc + uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc with: app_id: ${{ secrets.BACKPORT_APP_ID }} private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} From b49ce376d8a2757b8ac938dc195e6150bac6c3cd Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 31 Jan 2024 13:03:58 +0100 Subject: [PATCH 1734/2296] Context-aware methods to ProviderClient and ServiceClient Provide new methods to leverage per-call `context.Context`. --- .github/workflows/codeql-analysis.yml | 5 + .github/workflows/unit.yml | 2 +- go.mod | 2 +- go.sum | 1 + internal/ctxt/merge.go | 52 ++++++++++ internal/ctxt/merge_test.go | 133 ++++++++++++++++++++++++++ provider_client.go | 45 +++++---- service_client.go | 76 +++++++++++---- 8 files changed, 277 insertions(+), 39 deletions(-) create mode 100644 internal/ctxt/merge.go create mode 100644 internal/ctxt/merge_test.go diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9e1b8711dd..f0e26e7713 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -17,6 +17,11 @@ jobs: language: [ 'go' ] steps: + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index c3cbd434e3..6f40060af1 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: go-version: - - "1.20" + - "1.21" - "1" env: diff --git a/go.mod b/go.mod index 685ca96676..b61904cace 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gophercloud/gophercloud -go 1.20 +go 1.21.6 require ( golang.org/x/crypto v0.18.0 diff --git a/go.sum b/go.sum index de48846989..678f5e89cb 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 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= diff --git a/internal/ctxt/merge.go b/internal/ctxt/merge.go new file mode 100644 index 0000000000..5575596a4e --- /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 := context.WithCancelCause(ctx1) + stop := context.AfterFunc(ctx2, func() { + cancel(context.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 any) any { + 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 8daf611c98..bd6a62040e 100644 --- a/provider_client.go +++ b/provider_client.go @@ -9,6 +9,8 @@ import ( "net/http" "strings" "sync" + + "github.com/gophercloud/gophercloud/internal/ctxt" ) // DefaultUserAgent is the default User-Agent string set in the request header. @@ -87,7 +89,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. @@ -351,15 +355,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 @@ -388,14 +397,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 @@ -431,12 +442,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 } @@ -490,7 +501,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: @@ -555,7 +566,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} @@ -591,7 +602,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 @@ -615,7 +626,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 30b0cb7de3998ba50e465fa541874193bceab710 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 2 Feb 2024 18:50:56 +0100 Subject: [PATCH 1735/2296] 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 18b1ff438c..4e5f5d5c62 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -1,6 +1,7 @@ package pagination import ( + "context" "encoding/json" "io" "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 40e166402c59426c2663d33f98395ac2d1936101 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 2 Feb 2024 16:49:22 +0100 Subject: [PATCH 1736/2296] 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 445803af52..d4e045ac63 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 c9b54ab18c92410804dc62280a0dd6a6d5d71bca Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 2 Feb 2024 21:00:22 +0100 Subject: [PATCH 1737/2296] 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 8786189edf..75722835cf 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 d1f318bcd899bd86bace3c6ffa54cad3ce6ad2bb Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 6 Nov 2023 16:16:16 +0100 Subject: [PATCH 1738/2296] test: Restructure actions gofmt and go vet should now be enough for linting. --- .github/workflows/go-fmt.yaml | 17 ++++++++ .github/workflows/{gomod.yml => go-mod.yaml} | 5 ++- .github/workflows/go-vet.yaml | 17 ++++++++ .../{reauth-retests.yaml => unit-reauth.yaml} | 5 +-- .github/workflows/unit.yml | 20 ++------- script/format | 41 ------------------- script/unittest | 7 ---- 7 files changed, 44 insertions(+), 68 deletions(-) create mode 100644 .github/workflows/go-fmt.yaml rename .github/workflows/{gomod.yml => go-mod.yaml} (68%) create mode 100644 .github/workflows/go-vet.yaml rename .github/workflows/{reauth-retests.yaml => unit-reauth.yaml} (85%) delete mode 100755 script/format delete mode 100755 script/unittest diff --git a/.github/workflows/go-fmt.yaml b/.github/workflows/go-fmt.yaml new file mode 100644 index 0000000000..39ed0b48ac --- /dev/null +++ b/.github/workflows/go-fmt.yaml @@ -0,0 +1,17 @@ +on: [push, pull_request] +name: go fmt +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1' + + - run: | + test -z "$(gofmt -e -d . | tee /dev/stderr)" diff --git a/.github/workflows/gomod.yml b/.github/workflows/go-mod.yaml similarity index 68% rename from .github/workflows/gomod.yml rename to .github/workflows/go-mod.yaml index 43dc5c242b..6bcb26018a 100644 --- a/.github/workflows/gomod.yml +++ b/.github/workflows/go-mod.yaml @@ -6,9 +6,12 @@ permissions: jobs: test: runs-on: ubuntu-latest + steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1' - - run: if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi + + - run: | + if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi diff --git a/.github/workflows/go-vet.yaml b/.github/workflows/go-vet.yaml new file mode 100644 index 0000000000..3312438fca --- /dev/null +++ b/.github/workflows/go-vet.yaml @@ -0,0 +1,17 @@ +on: [push, pull_request] +name: go vet +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1' + + - run: | + go vet ./... diff --git a/.github/workflows/reauth-retests.yaml b/.github/workflows/unit-reauth.yaml similarity index 85% rename from .github/workflows/reauth-retests.yaml rename to .github/workflows/unit-reauth.yaml index 983102a048..d4cf28cd48 100644 --- a/.github/workflows/reauth-retests.yaml +++ b/.github/workflows/unit-reauth.yaml @@ -7,10 +7,9 @@ jobs: test: runs-on: ubuntu-latest strategy: - fail-fast: false matrix: go-version: - - "1" + - '1' steps: - name: Setup Go ${{ matrix.go-version }} @@ -22,4 +21,4 @@ jobs: - name: Run reauth retests run: | - ./script/unittest + go test -v -race -count=5 ./testing diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 6f40060af1..c583015db6 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -1,5 +1,5 @@ on: [push, pull_request] -name: Unit Testing +name: Unit tests permissions: contents: read @@ -13,32 +13,20 @@ jobs: fail-fast: false matrix: go-version: - - "1.21" - - "1" - - env: - GO111MODULE: "on" + - '1.21' + - '1' steps: + - uses: actions/checkout@v4 - name: Setup Go ${{ matrix.go-version }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - - uses: actions/checkout@v4 - name: Setup environment 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 - - - name: Run go vet - run: | - go vet ./... - name: Run unit tests run: | diff --git a/script/format b/script/format deleted file mode 100755 index 05645a8252..0000000000 --- a/script/format +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -goimports="goimports" - -find_files() { - find . -not \( \ - \( \ - -wholename './output' \ - -o -wholename './_output' \ - -o -wholename './_gopath' \ - -o -wholename './release' \ - -o -wholename './target' \ - -o -wholename '*/third_party/*' \ - -o -wholename '*/vendor/*' \ - \) -prune \ - \) -name '*.go' -} - -ignore_files=( - "./openstack/compute/v2/extensions/quotasets/testing/fixtures.go" - "./openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go" -) - -bad_files=$(find_files | xargs ${goimports} -l) - -final_files=() -for bad_file in $bad_files; do - found= - for ignore_file in "${ignore_files[@]}"; do - [[ "${bad_file}" == "${ignore_file}" ]] && { found=1; break; } - done - [[ -n $found ]] || final_files+=("$bad_file") -done - -if [[ "${#final_files[@]}" -gt 0 ]]; then - diff=$(echo "${final_files[@]}" | xargs ${goimports} -d -e 2>&1) - if [[ -n "${diff}" ]]; then - echo "${diff}" - exit 1 - fi -fi diff --git a/script/unittest b/script/unittest deleted file mode 100755 index 609f74cd0c..0000000000 --- a/script/unittest +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# -# Run the unit tests. - -# Do extra rounds of testing to help identify reauth concurrency issues. -# All other packages are tested in the `coverage` tests. -go test -v -race -count=5 ./testing From 833d504a415f5688c45e3c041a36babbc2f718d4 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 5 Feb 2024 10:05:45 -0500 Subject: [PATCH 1739/2296] Revert "test: Restructure actions" --- .github/workflows/go-fmt.yaml | 17 -------- .github/workflows/go-vet.yaml | 17 -------- .github/workflows/{go-mod.yaml => gomod.yml} | 5 +-- .../{unit-reauth.yaml => reauth-retests.yaml} | 5 ++- .github/workflows/unit.yml | 20 +++++++-- script/format | 41 +++++++++++++++++++ script/unittest | 7 ++++ 7 files changed, 68 insertions(+), 44 deletions(-) delete mode 100644 .github/workflows/go-fmt.yaml delete mode 100644 .github/workflows/go-vet.yaml rename .github/workflows/{go-mod.yaml => gomod.yml} (68%) rename .github/workflows/{unit-reauth.yaml => reauth-retests.yaml} (85%) create mode 100755 script/format create mode 100755 script/unittest diff --git a/.github/workflows/go-fmt.yaml b/.github/workflows/go-fmt.yaml deleted file mode 100644 index 39ed0b48ac..0000000000 --- a/.github/workflows/go-fmt.yaml +++ /dev/null @@ -1,17 +0,0 @@ -on: [push, pull_request] -name: go fmt -permissions: - contents: read - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: '1' - - - run: | - test -z "$(gofmt -e -d . | tee /dev/stderr)" diff --git a/.github/workflows/go-vet.yaml b/.github/workflows/go-vet.yaml deleted file mode 100644 index 3312438fca..0000000000 --- a/.github/workflows/go-vet.yaml +++ /dev/null @@ -1,17 +0,0 @@ -on: [push, pull_request] -name: go vet -permissions: - contents: read - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: '1' - - - run: | - go vet ./... diff --git a/.github/workflows/go-mod.yaml b/.github/workflows/gomod.yml similarity index 68% rename from .github/workflows/go-mod.yaml rename to .github/workflows/gomod.yml index 6bcb26018a..43dc5c242b 100644 --- a/.github/workflows/go-mod.yaml +++ b/.github/workflows/gomod.yml @@ -6,12 +6,9 @@ permissions: jobs: test: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1' - - - run: | - if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi + - run: if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi diff --git a/.github/workflows/unit-reauth.yaml b/.github/workflows/reauth-retests.yaml similarity index 85% rename from .github/workflows/unit-reauth.yaml rename to .github/workflows/reauth-retests.yaml index d4cf28cd48..983102a048 100644 --- a/.github/workflows/unit-reauth.yaml +++ b/.github/workflows/reauth-retests.yaml @@ -7,9 +7,10 @@ jobs: test: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: go-version: - - '1' + - "1" steps: - name: Setup Go ${{ matrix.go-version }} @@ -21,4 +22,4 @@ jobs: - name: Run reauth retests run: | - go test -v -race -count=5 ./testing + ./script/unittest diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index c583015db6..6f40060af1 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -1,5 +1,5 @@ on: [push, pull_request] -name: Unit tests +name: Unit Testing permissions: contents: read @@ -13,20 +13,32 @@ jobs: fail-fast: false matrix: go-version: - - '1.21' - - '1' + - "1.21" + - "1" + + env: + GO111MODULE: "on" steps: - - uses: actions/checkout@v4 - name: Setup Go ${{ matrix.go-version }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} + - uses: actions/checkout@v4 - name: Setup environment 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 + + - name: Run go vet + run: | + go vet ./... - name: Run unit tests run: | diff --git a/script/format b/script/format new file mode 100755 index 0000000000..05645a8252 --- /dev/null +++ b/script/format @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +goimports="goimports" + +find_files() { + find . -not \( \ + \( \ + -wholename './output' \ + -o -wholename './_output' \ + -o -wholename './_gopath' \ + -o -wholename './release' \ + -o -wholename './target' \ + -o -wholename '*/third_party/*' \ + -o -wholename '*/vendor/*' \ + \) -prune \ + \) -name '*.go' +} + +ignore_files=( + "./openstack/compute/v2/extensions/quotasets/testing/fixtures.go" + "./openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go" +) + +bad_files=$(find_files | xargs ${goimports} -l) + +final_files=() +for bad_file in $bad_files; do + found= + for ignore_file in "${ignore_files[@]}"; do + [[ "${bad_file}" == "${ignore_file}" ]] && { found=1; break; } + done + [[ -n $found ]] || final_files+=("$bad_file") +done + +if [[ "${#final_files[@]}" -gt 0 ]]; then + diff=$(echo "${final_files[@]}" | xargs ${goimports} -d -e 2>&1) + if [[ -n "${diff}" ]]; then + echo "${diff}" + exit 1 + fi +fi diff --git a/script/unittest b/script/unittest new file mode 100755 index 0000000000..609f74cd0c --- /dev/null +++ b/script/unittest @@ -0,0 +1,7 @@ +#!/bin/bash +# +# Run the unit tests. + +# Do extra rounds of testing to help identify reauth concurrency issues. +# All other packages are tested in the `coverage` tests. +go test -v -race -count=5 ./testing From 63e3079141405af29f16db011c73f58756a6621f Mon Sep 17 00:00:00 2001 From: Lennart Jern Date: Fri, 2 Feb 2024 14:48:45 +0200 Subject: [PATCH 1740/2296] Fix value_specs implementation The current implementation does not do anything useful. The key-pairs must be extracted and added directly in the body instead of under "value_specs". This commit fixes the implementation and also adds validation of the value_specs based on what is done in Heat. There are some banned keys that are not allowed in value_specs, and values are not allowed to overwrite existing fields either. Signed-off-by: Lennart Jern --- openstack/networking/v2/ports/requests.go | 39 ++++++++++++- openstack/networking/v2/ports/results.go | 3 - .../networking/v2/ports/testing/fixtures.go | 14 +---- .../v2/ports/testing/requests_test.go | 58 +++++++++++++++++-- 4 files changed, 91 insertions(+), 23 deletions(-) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 70c396abc8..95eef81a2c 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -3,6 +3,7 @@ package ports import ( "fmt" "net/url" + "slices" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" @@ -130,7 +131,37 @@ type CreateOpts struct { // ToPortCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "port") + body, err := gophercloud.BuildRequestBody(opts, "port") + if err != nil { + return nil, err + } + + return AddValueSpecs(body) +} + +// AddValueSpecs expands the 'value_specs' object and removes 'value_specs' +// from the request body. It will return error if the value specs would overwrite +// an existing field or contains forbidden keys. +func AddValueSpecs(body map[string]interface{}) (map[string]interface{}, error) { + // Banned the same as in heat. See https://github.com/openstack/heat/blob/dd7319e373b88812cb18897f742b5196a07227ea/heat/engine/resources/openstack/neutron/neutron.py#L59 + bannedKeys := []string{"shared", "tenant_id"} + port := body["port"].(map[string]interface{}) + + if port["value_specs"] != nil { + for k, v := range port["value_specs"].(map[string]interface{}) { + if slices.Contains(bannedKeys, k) { + return nil, fmt.Errorf("forbidden key in value_specs: %s", k) + } + if _, ok := port[k]; ok { + return nil, fmt.Errorf("value_specs would overwrite key: %s", k) + } + port[k] = v + } + delete(port, "value_specs") + } + body["port"] = port + + return body, nil } // Create accepts a CreateOpts struct and creates a new network using the values @@ -173,7 +204,11 @@ type UpdateOpts struct { // ToPortUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "port") + body, err := gophercloud.BuildRequestBody(opts, "port") + if err != nil { + return nil, err + } + return AddValueSpecs(body) } // Update accepts a UpdateOpts struct and updates an existing port using the diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index a39133fc06..dc9b75b43c 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -114,9 +114,6 @@ type Port struct { // 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"` - // 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 a54cf39622..b4c03fd618 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -299,9 +299,7 @@ const CreateValueSpecRequest = ` "mac_address": "fa:16:3e:c9:cb:f0" } ], - "value_specs": { - "key": "value" - } + "test": "value" } } ` @@ -332,9 +330,6 @@ const CreateValueSpecResponse = ` "mac_address": "fa:16:3e:c9:cb:f0" } ], - "value_specs": { - "key": "value" - }, "device_id": "" } } @@ -544,9 +539,7 @@ const UpdatePropagateUplinkStatusResponse = ` const UpdateValueSpecsRequest = ` { "port": { - "value_specs": { - "key": "value" - } + "test": "update" } } ` @@ -577,9 +570,6 @@ const UpdateValueSpecsResponse = ` "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" ], - "value_specs": { - "key": "value" - }, "device_id": "" } } diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index eae8efadf5..ab54a55fb9 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -389,7 +389,7 @@ func TestCreateWithValueSpecs(t *testing.T) { {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, ValueSpecs: &map[string]string{ - "key": "value", + "test": "value", }, } n, err := ports.Create(fake.ServiceClient(), options).Extract() @@ -410,7 +410,55 @@ func TestCreateWithValueSpecs(t *testing.T) { 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 TestCreateWithInvalidValueSpecs(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{ + // This is a forbidden key + "shared": "value", + }, + } + + // We expect an error here since we used a fobidden key in the value specs. + _, err := ports.Create(fake.ServiceClient(), options).Extract() + th.AssertErr(t, err) + + options.ValueSpecs = &map[string]string{ + // Try to overwrite an existing field + "name": "overwrite", + } + + // We expect an error here since the value specs would overwrite an existing field. + _, err = ports.Create(fake.ServiceClient(), options).Extract() + th.AssertErr(t, err) } func TestRequiredCreateOpts(t *testing.T) { @@ -598,14 +646,12 @@ func TestUpdateValueSpecs(t *testing.T) { options := ports.UpdateOpts{ ValueSpecs: &map[string]string{ - "key": "value", + "test": "update", }, } - s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + _, 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) { From d7e07ae051260d0c638362ecb616a6827a93d9a1 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 29 Jan 2024 10:38:23 +0100 Subject: [PATCH 1741/2296] 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 | 87 ++++++++ openstack/config/clouds/types.go | 203 ++++++++++++++++++ openstack/config/provider_client.go | 70 +++++++ 6 files changed, 883 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..ed9bd08288 --- /dev/null +++ b/openstack/config/clouds/tls.go @@ -0,0 +1,87 @@ +package clouds + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "fmt" + "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 := os.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 := os.ReadFile(clientCertPath) + if err != nil { + return nil, fmt.Errorf("failed to read the client cert file: %w", err) + } + + clientKey, err := os.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 3eaa6be06666d48c3f1d9931b1a914ed7cd2acbe Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 6 Feb 2024 10:19:09 +0100 Subject: [PATCH 1742/2296] Import the changelog from v1 --- CHANGELOG.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42dec99649..c96be9111c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,74 @@ New features and improvements: * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Bugfix: it is now possible to successfuly call `objects.CreateTempURL` with a container name or object name containing the string `/v1/`. +## 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: + +* [GH-2800](https://github.com/gophercloud/gophercloud/pull/2800) [v1] Fix options initialization in ServiceClient.Request (fixes #2798) +* [GH-2823](https://github.com/gophercloud/gophercloud/pull/2823) [v1] Add more godoc to GuestFormat +* [GH-2826](https://github.com/gophercloud/gophercloud/pull/2826) Allow objects.CreateTempURL with names containing /v1/ + +CI changes: + +* [GH-2802](https://github.com/gophercloud/gophercloud/pull/2802) [v1] Add job for bobcat stable/2023.2 +* [GH-2819](https://github.com/gophercloud/gophercloud/pull/2819) [v1] Test files alongside code +* [GH-2814](https://github.com/gophercloud/gophercloud/pull/2814) Make fixtures part of tests +* [GH-2796](https://github.com/gophercloud/gophercloud/pull/2796) [v1] ci/unit: switch to coverallsapp/github-action +* [GH-2840](https://github.com/gophercloud/gophercloud/pull/2840) unit tests: Fix the installation of tools + +## v1.7.0 (2023-09-22) + +New features and improvements: + +* [GH-2782](https://github.com/gophercloud/gophercloud/pull/2782) [v1] (manual clean backport) Add tag field to compute block_device_v2 + +CI changes: + +* [GH-2760](https://github.com/gophercloud/gophercloud/pull/2760) [v1 backports] semver auto labels +* [GH-2775](https://github.com/gophercloud/gophercloud/pull/2775) [v1] Fix typos in comments +* [GH-2783](https://github.com/gophercloud/gophercloud/pull/2783) [v1] (clean manual backport) ci/functional: fix ubuntu version & add antelope +* [GH-2785](https://github.com/gophercloud/gophercloud/pull/2785) [v1] Acceptance: Handle numerical version names in version comparison helpers +* [GH-2787](https://github.com/gophercloud/gophercloud/pull/2787) backport-v1: fixes to semver label +* [GH-2788](https://github.com/gophercloud/gophercloud/pull/2788) [v1] Make acceptance tests internal + + +## v1.6.0 (2023-08-30) + +New features and improvements: + +* [GH-2712](https://github.com/gophercloud/gophercloud/pull/2712) [v1] README: minor change to test backport workflow +* [GH-2713](https://github.com/gophercloud/gophercloud/pull/2713) [v1] tests: run MultiAttach with a capable Cinder Type +* [GH-2714](https://github.com/gophercloud/gophercloud/pull/2714) [v1] Add CRUD support for encryption in volume v3 types +* [GH-2715](https://github.com/gophercloud/gophercloud/pull/2715) [v1] Add projectID to fwaas_v2 policy CreateOpts and ListOpts +* [GH-2716](https://github.com/gophercloud/gophercloud/pull/2716) [v1] Add projectID to fwaas_v2 CreateOpts +* [GH-2717](https://github.com/gophercloud/gophercloud/pull/2717) [v1] [manila]: add reset and force delete actions to a snapshot +* [GH-2718](https://github.com/gophercloud/gophercloud/pull/2718) [v1] [cinder]: add reset and force delete actions to volumes and snapshots +* [GH-2721](https://github.com/gophercloud/gophercloud/pull/2721) [v1] orchestration: Explicit error in optionsmap creation +* [GH-2723](https://github.com/gophercloud/gophercloud/pull/2723) [v1] Add conductor API to Baremetal V1 +* [GH-2729](https://github.com/gophercloud/gophercloud/pull/2729) [v1] networking/v2/ports: allow list filter by security group + +CI changes: + +* [GH-2675](https://github.com/gophercloud/gophercloud/pull/2675) [v1][CI] Drop periodic jobs from stable branch +* [GH-2683](https://github.com/gophercloud/gophercloud/pull/2683) [v1] CI tweaks + + ## v1.5.0 (2023-06-21) New features and improvements: From 166ec0dce5778cc6f2906d13820451b51b6cdcad Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 6 Feb 2024 10:19:25 +0100 Subject: [PATCH 1743/2296] Bump the module version Rename the module to "github.com/gophercloud/gophercloud/v2" in `go.mod` and in all internal imports. --- auth_result.go | 6 +-- .../.template/requests.go | 4 +- .../contributor-tutorial/.template/results.go | 4 +- .../.template/testing/fixtures_test.go | 6 +-- .../.template/testing/requests_test.go | 8 ++-- docs/contributor-tutorial/.template/urls.go | 2 +- go.mod | 2 +- internal/acceptance/clients/clients.go | 10 ++-- .../clients/testing/conditions_test.go | 2 +- .../baremetal/httpbasic/allocations_test.go | 10 ++-- .../baremetal/httpbasic/nodes_test.go | 10 ++-- .../baremetal/httpbasic/ports_test.go | 10 ++-- .../baremetal/noauth/allocations_test.go | 10 ++-- .../openstack/baremetal/noauth/nodes_test.go | 10 ++-- .../openstack/baremetal/noauth/ports_test.go | 10 ++-- .../baremetal/v1/allocations_test.go | 8 ++-- .../openstack/baremetal/v1/baremetal.go | 10 ++-- .../openstack/baremetal/v1/conductors_test.go | 10 ++-- .../openstack/baremetal/v1/nodes_test.go | 8 ++-- .../openstack/baremetal/v1/ports_test.go | 8 ++-- .../blockstorage/apiversions_test.go | 6 +-- .../blockstorage/extensions/backups_test.go | 8 ++-- .../blockstorage/extensions/extensions.go | 20 ++++---- .../blockstorage/extensions/limits_test.go | 8 ++-- .../extensions/schedulerhints_test.go | 10 ++-- .../extensions/schedulerstats_test.go | 8 ++-- .../blockstorage/extensions/services_test.go | 8 ++-- .../extensions/volumeactions_test.go | 14 +++--- .../extensions/volumetenants_test.go | 10 ++-- .../blockstorage/noauth/blockstorage.go | 10 ++-- .../blockstorage/noauth/snapshots_test.go | 6 +-- .../blockstorage/noauth/volumes_test.go | 6 +-- .../openstack/blockstorage/v1/blockstorage.go | 10 ++-- .../blockstorage/v1/snapshots_test.go | 6 +-- .../openstack/blockstorage/v1/volumes_test.go | 8 ++-- .../blockstorage/v1/volumetypes_test.go | 6 +-- .../openstack/blockstorage/v2/blockstorage.go | 12 ++--- .../blockstorage/v2/snapshots_test.go | 8 ++-- .../openstack/blockstorage/v2/volumes_test.go | 12 ++--- .../openstack/blockstorage/v3/blockstorage.go | 14 +++--- .../openstack/blockstorage/v3/qos_test.go | 10 ++-- .../blockstorage/v3/quotaset_test.go | 12 ++--- .../blockstorage/v3/snapshots_test.go | 12 ++--- .../blockstorage/v3/volumeattachments.go | 8 ++-- .../blockstorage/v3/volumeattachments_test.go | 8 ++-- .../openstack/blockstorage/v3/volumes_test.go | 12 ++--- .../blockstorage/v3/volumetypes_test.go | 10 ++-- internal/acceptance/openstack/client_test.go | 16 +++---- .../openstack/clustering/v1/actions_test.go | 8 ++-- .../openstack/clustering/v1/clustering.go | 20 ++++---- .../openstack/clustering/v1/clusters_test.go | 10 ++-- .../openstack/clustering/v1/events_test.go | 8 ++-- .../openstack/clustering/v1/nodes_test.go | 8 ++-- .../openstack/clustering/v1/policies_test.go | 8 ++-- .../clustering/v1/policytypes_test.go | 8 ++-- .../openstack/clustering/v1/profiles_test.go | 8 ++-- .../clustering/v1/profiletypes_test.go | 8 ++-- .../openstack/clustering/v1/receivers_test.go | 8 ++-- .../clustering/v1/webhooktrigger_test.go | 8 ++-- internal/acceptance/openstack/common.go | 2 +- .../openstack/compute/v2/aggregates_test.go | 12 ++--- .../compute/v2/attachinterfaces_test.go | 8 ++-- .../compute/v2/availabilityzones_test.go | 8 ++-- .../compute/v2/bootfromvolume_test.go | 12 ++--- .../openstack/compute/v2/compute.go | 46 +++++++++---------- .../openstack/compute/v2/defsecrules_test.go | 8 ++-- .../openstack/compute/v2/diagnostics_test.go | 8 ++-- .../openstack/compute/v2/extension_test.go | 8 ++-- .../openstack/compute/v2/flavors_test.go | 10 ++-- .../openstack/compute/v2/floatingip_test.go | 10 ++-- .../openstack/compute/v2/hypervisors_test.go | 10 ++-- .../openstack/compute/v2/images_test.go | 8 ++-- .../compute/v2/instance_actions_test.go | 10 ++-- .../openstack/compute/v2/keypairs_test.go | 12 ++--- .../openstack/compute/v2/limits_test.go | 8 ++-- .../openstack/compute/v2/migrate_test.go | 6 +-- .../openstack/compute/v2/network_test.go | 8 ++-- .../openstack/compute/v2/quotaset_test.go | 12 ++--- .../compute/v2/remoteconsoles_test.go | 6 +-- .../compute/v2/rescueunrescue_test.go | 4 +- .../openstack/compute/v2/secgroup_test.go | 10 ++-- .../openstack/compute/v2/servergroup_test.go | 10 ++-- .../openstack/compute/v2/servers_test.go | 30 ++++++------ .../openstack/compute/v2/services_test.go | 8 ++-- .../compute/v2/tenantnetworks_test.go | 8 ++-- .../openstack/compute/v2/usage_test.go | 10 ++-- .../openstack/compute/v2/volumeattach_test.go | 8 ++-- .../openstack/container/v1/capsules.go | 6 +-- .../openstack/container/v1/capsules_test.go | 8 ++-- .../containerinfra/v1/certificates_test.go | 6 +-- .../containerinfra/v1/clusters_test.go | 8 ++-- .../v1/clustertemplates_test.go | 8 ++-- .../containerinfra/v1/containerinfra.go | 16 +++---- .../containerinfra/v1/nodegroups_test.go | 10 ++-- .../containerinfra/v1/quotas_test.go | 6 +-- .../openstack/db/v1/configurations_test.go | 8 ++-- .../openstack/db/v1/databases_test.go | 6 +-- internal/acceptance/openstack/db/v1/db.go | 12 ++--- .../openstack/db/v1/flavors_test.go | 6 +-- .../openstack/db/v1/instances_test.go | 6 +-- .../acceptance/openstack/db/v1/users_test.go | 6 +-- internal/acceptance/openstack/dns/v2/dns.go | 14 +++--- .../openstack/dns/v2/recordsets_test.go | 10 ++-- .../openstack/dns/v2/transfers_test.go | 12 ++--- .../acceptance/openstack/dns/v2/zones_test.go | 8 ++-- .../openstack/identity/v2/extension_test.go | 8 ++-- .../openstack/identity/v2/identity.go | 12 ++--- .../openstack/identity/v2/role_test.go | 10 ++-- .../openstack/identity/v2/tenant_test.go | 8 ++-- .../openstack/identity/v2/token_test.go | 10 ++-- .../openstack/identity/v2/user_test.go | 8 ++-- .../v3/applicationcredentials_test.go | 12 ++--- .../openstack/identity/v3/catalog_test.go | 8 ++-- .../openstack/identity/v3/credentials_test.go | 14 +++--- .../openstack/identity/v3/domains_test.go | 8 ++-- .../identity/v3/ec2credentials_test.go | 12 ++--- .../openstack/identity/v3/endpoint_test.go | 12 ++--- .../openstack/identity/v3/federation_test.go | 10 ++-- .../openstack/identity/v3/groups_test.go | 8 ++-- .../openstack/identity/v3/identity.go | 22 ++++----- .../openstack/identity/v3/limits_test.go | 12 ++--- .../openstack/identity/v3/oauth1_test.go | 14 +++--- .../openstack/identity/v3/osinherit_test.go | 14 +++--- .../openstack/identity/v3/policies_test.go | 8 ++-- .../identity/v3/projectendpoint_test.go | 10 ++-- .../openstack/identity/v3/projects_test.go | 8 ++-- .../openstack/identity/v3/reauth_test.go | 10 ++-- .../openstack/identity/v3/regions_test.go | 8 ++-- .../identity/v3/registeredlimits_test.go | 10 ++-- .../openstack/identity/v3/roles_test.go | 14 +++--- .../openstack/identity/v3/service_test.go | 8 ++-- .../openstack/identity/v3/token_test.go | 10 ++-- .../openstack/identity/v3/trusts_test.go | 16 +++---- .../openstack/identity/v3/users_test.go | 12 ++--- .../imageservice/v2/imagedata_test.go | 6 +-- .../imageservice/v2/imageimport_test.go | 6 +-- .../openstack/imageservice/v2/images_test.go | 10 ++-- .../openstack/imageservice/v2/imageservice.go | 14 +++--- .../openstack/imageservice/v2/tasks_test.go | 10 ++-- .../openstack/keymanager/v1/acls_test.go | 8 ++-- .../keymanager/v1/containers_test.go | 10 ++-- .../openstack/keymanager/v1/keymanager.go | 12 ++--- .../openstack/keymanager/v1/orders_test.go | 12 ++--- .../openstack/keymanager/v1/secrets_test.go | 8 ++-- .../loadbalancer/v2/amphorae_test.go | 6 +-- .../loadbalancer/v2/flavorprofiles_test.go | 8 ++-- .../openstack/loadbalancer/v2/flavors_test.go | 8 ++-- .../loadbalancer/v2/l7policies_test.go | 6 +-- .../loadbalancer/v2/listeners_test.go | 6 +-- .../openstack/loadbalancer/v2/loadbalancer.go | 22 ++++----- .../loadbalancer/v2/loadbalancers_test.go | 20 ++++---- .../loadbalancer/v2/monitors_test.go | 6 +-- .../openstack/loadbalancer/v2/pools_test.go | 6 +-- .../loadbalancer/v2/providers_test.go | 6 +-- .../openstack/loadbalancer/v2/quotas_test.go | 10 ++-- .../openstack/messaging/v2/claims_test.go | 6 +-- .../openstack/messaging/v2/message_test.go | 10 ++-- .../openstack/messaging/v2/messaging.go | 12 ++--- .../openstack/messaging/v2/queue_test.go | 8 ++-- .../networking/v2/apiversion_test.go | 6 +-- .../openstack/networking/v2/extension_test.go | 6 +-- .../v2/extensions/agents/agents_test.go | 14 +++--- .../v2/extensions/attributestags_test.go | 14 +++--- .../v2/extensions/bgp/peers/bgppeers_test.go | 8 ++-- .../v2/extensions/bgp/peers/peers.go | 8 ++-- .../bgp/speakers/bgpspeakers_test.go | 14 +++--- .../v2/extensions/bgp/speakers/speakers.go | 8 ++-- .../networking/v2/extensions/dns/dns.go | 14 +++--- .../networking/v2/extensions/dns/dns_test.go | 22 ++++----- .../networking/v2/extensions/extensions.go | 16 +++---- .../v2/extensions/fwaas/firewall_test.go | 12 ++--- .../networking/v2/extensions/fwaas/fwaas.go | 14 +++--- .../v2/extensions/fwaas/policy_test.go | 8 ++-- .../v2/extensions/fwaas/rule_test.go | 8 ++-- .../v2/extensions/fwaas_v2/fwaas_v2.go | 12 ++--- .../v2/extensions/fwaas_v2/groups_test.go | 8 ++-- .../v2/extensions/fwaas_v2/policy_test.go | 8 ++-- .../v2/extensions/fwaas_v2/rule_test.go | 8 ++-- .../extensions/layer3/addressscopes_test.go | 8 ++-- .../v2/extensions/layer3/extraroutes_test.go | 12 ++--- .../v2/extensions/layer3/floatingips_test.go | 12 ++--- .../extensions/layer3/l3_scheduling_test.go | 14 +++--- .../networking/v2/extensions/layer3/layer3.go | 18 ++++---- .../extensions/layer3/portforwardings_test.go | 12 ++--- .../v2/extensions/layer3/routers_test.go | 10 ++-- .../networking/v2/extensions/lbaas/lbaas.go | 12 ++--- .../v2/extensions/lbaas/members_test.go | 10 ++-- .../v2/extensions/lbaas/monitors_test.go | 6 +-- .../v2/extensions/lbaas/pools_test.go | 8 ++-- .../v2/extensions/lbaas/vips_test.go | 8 ++-- .../v2/extensions/lbaas_v2/l7policies_test.go | 6 +-- .../v2/extensions/lbaas_v2/lbaas_v2.go | 16 +++---- .../v2/extensions/lbaas_v2/listeners_test.go | 6 +-- .../extensions/lbaas_v2/loadbalancers_test.go | 18 ++++---- .../v2/extensions/lbaas_v2/monitors_test.go | 6 +-- .../v2/extensions/lbaas_v2/pools_test.go | 6 +-- .../networking/v2/extensions/mtu/mtu.go | 10 ++-- .../networking/v2/extensions/mtu/mtu_test.go | 16 +++---- .../networkipavailabilities_test.go | 8 ++-- .../extensions/portsbinding/portsbinding.go | 10 ++-- .../portsbinding/portsbinding_test.go | 12 ++--- .../networking/v2/extensions/provider_test.go | 10 ++-- .../v2/extensions/qos/policies/policies.go | 8 ++-- .../extensions/qos/policies/policies_test.go | 10 ++-- .../v2/extensions/qos/rules/rules.go | 6 +-- .../v2/extensions/qos/rules/rules_test.go | 14 +++--- .../qos/ruletypes/ruletypes_test.go | 8 ++-- .../networking/v2/extensions/quotas/quotas.go | 4 +- .../v2/extensions/quotas/quotas_test.go | 8 ++-- .../extensions/rbacpolicies/rbacpolicies.go | 6 +-- .../rbacpolicies/rbacpolicies_test.go | 12 ++--- .../networking/v2/extensions/security_test.go | 10 ++-- .../v2/extensions/subnetpools/subnetpools.go | 8 ++-- .../subnetpools/subnetpools_test.go | 8 ++-- .../extensions/trunk_details/trunks_test.go | 16 +++---- .../networking/v2/extensions/trunks/trunks.go | 6 +-- .../v2/extensions/trunks/trunks_test.go | 14 +++--- .../vlantransparent/vlantransparent.go | 10 ++-- .../vlantransparent/vlantransparent_test.go | 10 ++-- .../v2/extensions/vpnaas/group_test.go | 8 ++-- .../v2/extensions/vpnaas/ikepolicy_test.go | 8 ++-- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 8 ++-- .../v2/extensions/vpnaas/service_test.go | 10 ++-- .../extensions/vpnaas/siteconnection_test.go | 14 +++--- .../networking/v2/extensions/vpnaas/vpnaas.go | 16 +++---- .../openstack/networking/v2/networking.go | 16 +++---- .../openstack/networking/v2/networks_test.go | 12 ++--- .../openstack/networking/v2/ports_test.go | 14 +++--- .../openstack/networking/v2/subnets_test.go | 10 ++-- .../objectstorage/v1/accounts_test.go | 6 +-- .../objectstorage/v1/containers_test.go | 10 ++-- .../objectstorage/v1/objects_test.go | 10 ++-- .../objectstorage/v1/versioning_test.go | 10 ++-- .../orchestration/v1/buildinfo_test.go | 6 +-- .../orchestration/v1/orchestration.go | 8 ++-- .../orchestration/v1/stackevents_test.go | 6 +-- .../orchestration/v1/stackresources_test.go | 8 ++-- .../openstack/orchestration/v1/stacks_test.go | 8 ++-- .../orchestration/v1/stacktemplates_test.go | 8 ++-- .../openstack/placement/v1/placement.go | 8 ++-- .../placement/v1/resourceproviders_test.go | 8 ++-- .../v2/availabilityzones_test.go | 4 +- .../sharedfilesystems/v2/messages/messages.go | 4 +- .../v2/messages/messages_test.go | 6 +-- .../sharedfilesystems/v2/replicas.go | 8 ++-- .../sharedfilesystems/v2/replicas_test.go | 8 ++-- .../v2/schedulerstats_test.go | 8 ++-- .../sharedfilesystems/v2/securityservices.go | 6 +-- .../v2/securityservices_test.go | 6 +-- .../sharedfilesystems/v2/services_test.go | 8 ++-- .../sharedfilesystems/v2/shareaccessrules.go | 8 ++-- .../v2/shareaccessrules_test.go | 6 +-- .../sharedfilesystems/v2/sharenetworks.go | 8 ++-- .../v2/sharenetworks_test.go | 10 ++-- .../openstack/sharedfilesystems/v2/shares.go | 8 ++-- .../sharedfilesystems/v2/shares_test.go | 8 ++-- .../sharedfilesystems/v2/sharetransfers.go | 6 +-- .../v2/sharetransfers_test.go | 8 ++-- .../sharedfilesystems/v2/sharetypes.go | 6 +-- .../sharedfilesystems/v2/sharetypes_test.go | 6 +-- .../sharedfilesystems/v2/snapshots.go | 6 +-- .../sharedfilesystems/v2/snapshots_test.go | 8 ++-- .../openstack/workflow/v2/crontrigger.go | 10 ++-- .../workflow/v2/crontriggers_test.go | 8 ++-- .../openstack/workflow/v2/execution.go | 10 ++-- .../openstack/workflow/v2/executions_test.go | 8 ++-- .../openstack/workflow/v2/workflow.go | 8 ++-- .../openstack/workflow/v2/workflows_test.go | 8 ++-- internal/ctxt/merge_test.go | 2 +- openstack/auth_env.go | 2 +- openstack/baremetal/apiversions/requests.go | 2 +- openstack/baremetal/apiversions/results.go | 2 +- .../apiversions/testing/fixtures_test.go | 6 +-- .../apiversions/testing/requests_test.go | 6 +-- openstack/baremetal/apiversions/urls.go | 2 +- openstack/baremetal/httpbasic/requests.go | 2 +- .../httpbasic/testing/requests_test.go | 4 +- .../baremetal/inventory/testing/fixtures.go | 2 +- .../inventory/testing/plugindata_test.go | 4 +- .../baremetal/inventory/testing/types_test.go | 4 +- openstack/baremetal/noauth/requests.go | 2 +- .../baremetal/noauth/testing/requests_test.go | 4 +- .../baremetal/v1/allocations/requests.go | 4 +- openstack/baremetal/v1/allocations/results.go | 4 +- .../v1/allocations/testing/fixtures_test.go | 6 +-- .../v1/allocations/testing/requests_test.go | 8 ++-- openstack/baremetal/v1/allocations/urls.go | 2 +- openstack/baremetal/v1/conductors/requests.go | 4 +- openstack/baremetal/v1/conductors/results.go | 4 +- .../v1/conductors/testing/fixtures_test.go | 6 +-- .../v1/conductors/testing/requests_test.go | 8 ++-- openstack/baremetal/v1/conductors/urls.go | 2 +- openstack/baremetal/v1/drivers/requests.go | 4 +- openstack/baremetal/v1/drivers/results.go | 4 +- .../v1/drivers/testing/fixtures_test.go | 6 +-- .../v1/drivers/testing/requests_test.go | 8 ++-- openstack/baremetal/v1/drivers/urls.go | 2 +- openstack/baremetal/v1/nodes/requests.go | 4 +- openstack/baremetal/v1/nodes/results.go | 8 ++-- .../v1/nodes/testing/fixtures_test.go | 8 ++-- .../v1/nodes/testing/requests_test.go | 10 ++-- .../v1/nodes/testing/results_test.go | 12 ++--- openstack/baremetal/v1/nodes/urls.go | 2 +- openstack/baremetal/v1/ports/requests.go | 4 +- openstack/baremetal/v1/ports/results.go | 4 +- .../v1/ports/testing/fixtures_test.go | 6 +-- .../v1/ports/testing/requests_test.go | 8 ++-- openstack/baremetal/v1/ports/urls.go | 2 +- .../httpbasic/requests.go | 2 +- .../httpbasic/testing/requests_test.go | 4 +- .../baremetalintrospection/noauth/requests.go | 2 +- .../noauth/testing/requests_test.go | 4 +- .../v1/introspection/requests.go | 4 +- .../v1/introspection/results.go | 6 +-- .../v1/introspection/testing/fixtures.go | 12 ++--- .../v1/introspection/testing/requests_test.go | 8 ++-- .../v1/introspection/testing/results_test.go | 4 +- .../v1/introspection/urls.go | 2 +- .../blockstorage/apiversions/requests.go | 4 +- openstack/blockstorage/apiversions/results.go | 2 +- .../apiversions/testing/fixtures_test.go | 4 +- .../apiversions/testing/requests_test.go | 6 +-- openstack/blockstorage/apiversions/urls.go | 4 +- .../extensions/availabilityzones/requests.go | 4 +- .../extensions/availabilityzones/results.go | 2 +- .../testing/fixtures_test.go | 6 +-- .../testing/requests_test.go | 6 +-- .../extensions/availabilityzones/urls.go | 2 +- .../extensions/backups/requests.go | 4 +- .../extensions/backups/results.go | 4 +- .../backups/testing/fixtures_test.go | 6 +-- .../backups/testing/requests_test.go | 8 ++-- .../blockstorage/extensions/backups/urls.go | 2 +- .../extensions/limits/requests.go | 2 +- .../blockstorage/extensions/limits/results.go | 2 +- .../limits/testing/fixtures_test.go | 6 +-- .../limits/testing/requests_test.go | 6 +-- .../blockstorage/extensions/limits/urls.go | 2 +- .../extensions/quotasets/requests.go | 2 +- .../extensions/quotasets/results.go | 4 +- .../quotasets/testing/fixtures_test.go | 8 ++-- .../quotasets/testing/requests_test.go | 6 +-- .../blockstorage/extensions/quotasets/urls.go | 2 +- .../extensions/schedulerhints/requests.go | 2 +- .../schedulerhints/testing/requests_test.go | 6 +-- .../extensions/schedulerstats/requests.go | 4 +- .../extensions/schedulerstats/results.go | 2 +- .../schedulerstats/testing/fixtures_test.go | 6 +-- .../schedulerstats/testing/requests_test.go | 8 ++-- .../extensions/schedulerstats/urls.go | 2 +- .../extensions/services/requests.go | 4 +- .../extensions/services/results.go | 4 +- .../services/testing/fixtures_test.go | 6 +-- .../services/testing/requests_test.go | 8 ++-- .../blockstorage/extensions/services/urls.go | 2 +- .../extensions/volumeactions/requests.go | 2 +- .../extensions/volumeactions/results.go | 2 +- .../volumeactions/testing/fixtures_test.go | 4 +- .../volumeactions/testing/requests_test.go | 8 ++-- .../extensions/volumeactions/urls.go | 2 +- .../extensions/volumetransfers/requests.go | 4 +- .../extensions/volumetransfers/results.go | 4 +- .../volumetransfers/testing/fixtures_test.go | 8 ++-- .../volumetransfers/testing/requests_test.go | 8 ++-- .../extensions/volumetransfers/urls.go | 2 +- openstack/blockstorage/noauth/requests.go | 2 +- .../noauth/testing/requests_test.go | 6 +-- .../blockstorage/v1/apiversions/requests.go | 4 +- .../blockstorage/v1/apiversions/results.go | 4 +- .../v1/apiversions/testing/fixtures_test.go | 4 +- .../v1/apiversions/testing/requests_test.go | 8 ++-- openstack/blockstorage/v1/apiversions/urls.go | 4 +- .../blockstorage/v1/snapshots/requests.go | 4 +- .../blockstorage/v1/snapshots/results.go | 4 +- .../v1/snapshots/testing/fixtures_test.go | 4 +- .../v1/snapshots/testing/requests_test.go | 8 ++-- openstack/blockstorage/v1/snapshots/urls.go | 2 +- openstack/blockstorage/v1/snapshots/util.go | 2 +- openstack/blockstorage/v1/volumes/requests.go | 4 +- openstack/blockstorage/v1/volumes/results.go | 4 +- .../v1/volumes/testing/fixtures_test.go | 4 +- .../v1/volumes/testing/requests_test.go | 8 ++-- openstack/blockstorage/v1/volumes/urls.go | 2 +- openstack/blockstorage/v1/volumes/util.go | 2 +- .../blockstorage/v1/volumetypes/requests.go | 4 +- .../blockstorage/v1/volumetypes/results.go | 4 +- .../v1/volumetypes/testing/fixtures_test.go | 4 +- .../v1/volumetypes/testing/requests_test.go | 8 ++-- openstack/blockstorage/v1/volumetypes/urls.go | 2 +- .../blockstorage/v2/snapshots/requests.go | 4 +- .../blockstorage/v2/snapshots/results.go | 4 +- .../v2/snapshots/testing/fixtures_test.go | 4 +- .../v2/snapshots/testing/requests_test.go | 8 ++-- openstack/blockstorage/v2/snapshots/urls.go | 2 +- openstack/blockstorage/v2/snapshots/util.go | 2 +- openstack/blockstorage/v2/volumes/requests.go | 4 +- openstack/blockstorage/v2/volumes/results.go | 4 +- .../v2/volumes/testing/fixtures_test.go | 4 +- .../v2/volumes/testing/requests_test.go | 10 ++-- openstack/blockstorage/v2/volumes/urls.go | 2 +- openstack/blockstorage/v2/volumes/util.go | 2 +- .../blockstorage/v3/attachments/requests.go | 4 +- .../blockstorage/v3/attachments/results.go | 4 +- .../v3/attachments/testing/fixtures_test.go | 8 ++-- .../v3/attachments/testing/requests_test.go | 6 +-- openstack/blockstorage/v3/attachments/urls.go | 2 +- openstack/blockstorage/v3/attachments/util.go | 2 +- openstack/blockstorage/v3/qos/requests.go | 4 +- openstack/blockstorage/v3/qos/results.go | 4 +- .../v3/qos/testing/fixtures_test.go | 6 +-- .../v3/qos/testing/requests_test.go | 8 ++-- openstack/blockstorage/v3/qos/urls.go | 2 +- .../blockstorage/v3/snapshots/requests.go | 4 +- .../blockstorage/v3/snapshots/results.go | 4 +- .../v3/snapshots/testing/fixtures_test.go | 4 +- .../v3/snapshots/testing/requests_test.go | 8 ++-- openstack/blockstorage/v3/snapshots/urls.go | 2 +- openstack/blockstorage/v3/snapshots/util.go | 2 +- openstack/blockstorage/v3/volumes/requests.go | 4 +- openstack/blockstorage/v3/volumes/results.go | 4 +- .../v3/volumes/testing/fixtures_test.go | 4 +- .../v3/volumes/testing/requests_test.go | 12 ++--- openstack/blockstorage/v3/volumes/urls.go | 2 +- openstack/blockstorage/v3/volumes/util.go | 2 +- .../blockstorage/v3/volumetypes/requests.go | 4 +- .../blockstorage/v3/volumetypes/results.go | 4 +- .../v3/volumetypes/testing/fixtures_test.go | 4 +- .../v3/volumetypes/testing/requests_test.go | 8 ++-- openstack/blockstorage/v3/volumetypes/urls.go | 2 +- .../cdn/v1/base/testing/fixtures_test.go | 4 +- .../cdn/v1/flavors/testing/fixtures_test.go | 4 +- .../v1/serviceassets/testing/fixtures_test.go | 4 +- .../cdn/v1/services/testing/fixtures_test.go | 4 +- openstack/client.go | 12 ++--- openstack/clustering/v1/actions/requests.go | 4 +- openstack/clustering/v1/actions/results.go | 4 +- .../v1/actions/testing/fixtures_test.go | 6 +-- .../v1/actions/testing/requests_test.go | 8 ++-- openstack/clustering/v1/actions/urls.go | 2 +- openstack/clustering/v1/clusters/requests.go | 4 +- openstack/clustering/v1/clusters/results.go | 4 +- .../v1/clusters/testing/fixtures_test.go | 6 +-- .../v1/clusters/testing/requests_test.go | 8 ++-- openstack/clustering/v1/clusters/urls.go | 2 +- openstack/clustering/v1/events/requests.go | 4 +- openstack/clustering/v1/events/results.go | 4 +- .../v1/events/testing/fixtures_test.go | 6 +-- .../v1/events/testing/requests_test.go | 8 ++-- openstack/clustering/v1/events/urls.go | 2 +- openstack/clustering/v1/nodes/requests.go | 4 +- openstack/clustering/v1/nodes/results.go | 4 +- .../v1/nodes/testing/fixtures_test.go | 6 +-- .../v1/nodes/testing/requests_test.go | 8 ++-- openstack/clustering/v1/nodes/urls.go | 2 +- openstack/clustering/v1/policies/requests.go | 4 +- openstack/clustering/v1/policies/results.go | 4 +- .../v1/policies/testing/fixtures_test.go | 6 +-- .../v1/policies/testing/requests_test.go | 8 ++-- openstack/clustering/v1/policies/urls.go | 2 +- .../clustering/v1/policytypes/requests.go | 4 +- .../clustering/v1/policytypes/results.go | 4 +- .../v1/policytypes/testing/fixtures_test.go | 6 +-- .../v1/policytypes/testing/requests_test.go | 8 ++-- openstack/clustering/v1/policytypes/urls.go | 2 +- openstack/clustering/v1/profiles/requests.go | 4 +- openstack/clustering/v1/profiles/results.go | 4 +- .../v1/profiles/testing/fixtures_test.go | 6 +-- .../v1/profiles/testing/requests_test.go | 8 ++-- openstack/clustering/v1/profiles/urls.go | 2 +- .../clustering/v1/profiletypes/requests.go | 4 +- .../clustering/v1/profiletypes/results.go | 4 +- .../v1/profiletypes/testing/fixtures_test.go | 6 +-- .../v1/profiletypes/testing/requests_test.go | 10 ++-- openstack/clustering/v1/profiletypes/urls.go | 2 +- openstack/clustering/v1/receivers/requests.go | 4 +- openstack/clustering/v1/receivers/results.go | 4 +- .../v1/receivers/testing/fixtures_test.go | 6 +-- .../v1/receivers/testing/requests_test.go | 8 ++-- openstack/clustering/v1/receivers/urls.go | 2 +- openstack/clustering/v1/webhooks/requests.go | 2 +- openstack/clustering/v1/webhooks/results.go | 2 +- .../v1/webhooks/testing/requests_test.go | 6 +-- openstack/clustering/v1/webhooks/urls.go | 2 +- openstack/common/extensions/requests.go | 4 +- openstack/common/extensions/results.go | 4 +- .../common/extensions/testing/fixtures.go | 6 +-- .../extensions/testing/requests_test.go | 8 ++-- openstack/common/extensions/urls.go | 2 +- openstack/compute/apiversions/requests.go | 4 +- openstack/compute/apiversions/results.go | 4 +- .../apiversions/testing/fixtures_test.go | 6 +-- .../apiversions/testing/requests_test.go | 6 +-- openstack/compute/apiversions/urls.go | 4 +- .../v2/extensions/aggregates/requests.go | 4 +- .../v2/extensions/aggregates/results.go | 4 +- .../aggregates/testing/fixtures_test.go | 6 +-- .../aggregates/testing/requests_test.go | 8 ++-- .../compute/v2/extensions/aggregates/urls.go | 2 +- .../extensions/attachinterfaces/requests.go | 4 +- .../v2/extensions/attachinterfaces/results.go | 4 +- .../attachinterfaces/testing/fixtures_test.go | 6 +-- .../attachinterfaces/testing/requests_test.go | 8 ++-- .../v2/extensions/attachinterfaces/urls.go | 2 +- .../extensions/availabilityzones/requests.go | 4 +- .../extensions/availabilityzones/results.go | 4 +- .../testing/fixtures_test.go | 6 +-- .../testing/requests_test.go | 6 +-- .../v2/extensions/availabilityzones/urls.go | 2 +- .../v2/extensions/bootfromvolume/requests.go | 4 +- .../v2/extensions/bootfromvolume/results.go | 2 +- .../bootfromvolume/testing/fixtures_test.go | 4 +- .../bootfromvolume/testing/requests_test.go | 2 +- .../v2/extensions/bootfromvolume/urls.go | 2 +- .../v2/extensions/defsecrules/requests.go | 4 +- .../v2/extensions/defsecrules/results.go | 6 +-- .../defsecrules/testing/fixtures_test.go | 4 +- .../defsecrules/testing/requests_test.go | 10 ++-- .../compute/v2/extensions/defsecrules/urls.go | 2 +- openstack/compute/v2/extensions/delegate.go | 6 +-- .../v2/extensions/diagnostics/requests.go | 2 +- .../v2/extensions/diagnostics/results.go | 2 +- .../diagnostics/testing/fixtures_test.go | 4 +- .../diagnostics/testing/requests_test.go | 6 +-- .../compute/v2/extensions/diagnostics/urls.go | 2 +- .../v2/extensions/diskconfig/requests.go | 4 +- .../diskconfig/testing/requests_test.go | 6 +-- .../v2/extensions/evacuate/requests.go | 4 +- .../compute/v2/extensions/evacuate/results.go | 2 +- .../evacuate/testing/fixtures_test.go | 4 +- .../evacuate/testing/requests_test.go | 6 +-- .../testing/requests_test.go | 8 ++-- .../v2/extensions/floatingips/requests.go | 4 +- .../v2/extensions/floatingips/results.go | 4 +- .../floatingips/testing/fixtures_test.go | 6 +-- .../floatingips/testing/requests_test.go | 8 ++-- .../compute/v2/extensions/floatingips/urls.go | 2 +- .../v2/extensions/hypervisors/requests.go | 4 +- .../v2/extensions/hypervisors/results.go | 4 +- .../hypervisors/testing/fixtures_test.go | 6 +-- .../hypervisors/testing/requests_test.go | 8 ++-- .../compute/v2/extensions/hypervisors/urls.go | 2 +- .../extensions/injectnetworkinfo/requests.go | 4 +- .../extensions/injectnetworkinfo/results.go | 2 +- .../testing/fixtures_test.go | 4 +- .../testing/requests_test.go | 6 +-- .../v2/extensions/instanceactions/request.go | 4 +- .../v2/extensions/instanceactions/results.go | 4 +- .../instanceactions/testing/fixtures_test.go | 6 +-- .../instanceactions/testing/request_test.go | 8 ++-- .../v2/extensions/instanceactions/urls.go | 2 +- .../v2/extensions/keypairs/requests.go | 6 +-- .../compute/v2/extensions/keypairs/results.go | 4 +- .../keypairs/testing/fixtures_test.go | 6 +-- .../keypairs/testing/requests_test.go | 8 ++-- .../compute/v2/extensions/keypairs/urls.go | 2 +- .../compute/v2/extensions/limits/requests.go | 2 +- .../compute/v2/extensions/limits/results.go | 2 +- .../limits/testing/fixtures_test.go | 6 +-- .../limits/testing/requests_test.go | 6 +-- .../compute/v2/extensions/limits/urls.go | 2 +- .../v2/extensions/lockunlock/requests.go | 4 +- .../v2/extensions/lockunlock/results.go | 2 +- .../lockunlock/testing/fixtures_test.go | 4 +- .../lockunlock/testing/request_test.go | 6 +-- .../compute/v2/extensions/migrate/requests.go | 4 +- .../compute/v2/extensions/migrate/results.go | 2 +- .../migrate/testing/fixtures_test.go | 4 +- .../migrate/testing/requests_test.go | 6 +-- .../v2/extensions/networks/requests.go | 4 +- .../compute/v2/extensions/networks/results.go | 4 +- .../networks/testing/fixtures_test.go | 8 ++-- .../networks/testing/requests_test.go | 8 ++-- .../compute/v2/extensions/networks/urls.go | 2 +- .../v2/extensions/pauseunpause/requests.go | 4 +- .../v2/extensions/pauseunpause/results.go | 2 +- .../pauseunpause/testing/fixtures_test.go | 4 +- .../pauseunpause/testing/requests_test.go | 6 +-- .../v2/extensions/quotasets/requests.go | 2 +- .../v2/extensions/quotasets/results.go | 4 +- .../quotasets/testing/fixtures_test.go | 8 ++-- .../quotasets/testing/requests_test.go | 8 ++-- .../compute/v2/extensions/quotasets/urls.go | 2 +- .../v2/extensions/remoteconsoles/requests.go | 2 +- .../v2/extensions/remoteconsoles/results.go | 2 +- .../remoteconsoles/testing/requests_test.go | 6 +-- .../v2/extensions/remoteconsoles/urls.go | 2 +- .../v2/extensions/rescueunrescue/requests.go | 4 +- .../v2/extensions/rescueunrescue/results.go | 2 +- .../rescueunrescue/testing/requests_test.go | 6 +-- .../v2/extensions/resetnetwork/requests.go | 4 +- .../v2/extensions/resetnetwork/results.go | 2 +- .../resetnetwork/testing/fixtures_test.go | 4 +- .../resetnetwork/testing/requests_test.go | 6 +-- .../v2/extensions/resetstate/requests.go | 4 +- .../v2/extensions/resetstate/results.go | 2 +- .../resetstate/testing/fixtures_test.go | 4 +- .../resetstate/testing/requests_test.go | 6 +-- .../v2/extensions/schedulerhints/requests.go | 4 +- .../schedulerhints/testing/requests_test.go | 6 +-- .../v2/extensions/secgroups/requests.go | 4 +- .../v2/extensions/secgroups/results.go | 4 +- .../secgroups/testing/fixtures_test.go | 4 +- .../secgroups/testing/requests_test.go | 8 ++-- .../compute/v2/extensions/secgroups/urls.go | 2 +- .../v2/extensions/servergroups/requests.go | 4 +- .../v2/extensions/servergroups/results.go | 4 +- .../servergroups/testing/fixtures_test.go | 6 +-- .../servergroups/testing/requests_test.go | 8 ++-- .../v2/extensions/servergroups/urls.go | 2 +- .../v2/extensions/serverusage/results.go | 2 +- .../serverusage/testing/requests_test.go | 8 ++-- .../v2/extensions/services/requests.go | 4 +- .../compute/v2/extensions/services/results.go | 4 +- .../services/testing/fixtures_test.go | 6 +-- .../services/testing/requests_test.go | 8 ++-- .../compute/v2/extensions/services/urls.go | 2 +- .../v2/extensions/shelveunshelve/requests.go | 4 +- .../v2/extensions/shelveunshelve/results.go | 2 +- .../shelveunshelve/testing/fixtures_test.go | 4 +- .../shelveunshelve/testing/requests_test.go | 6 +-- .../v2/extensions/startstop/requests.go | 4 +- .../v2/extensions/startstop/results.go | 2 +- .../startstop/testing/fixtures_test.go | 4 +- .../startstop/testing/requests_test.go | 6 +-- .../v2/extensions/suspendresume/requests.go | 4 +- .../v2/extensions/suspendresume/results.go | 2 +- .../suspendresume/testing/fixtures_test.go | 4 +- .../suspendresume/testing/requests_test.go | 6 +-- .../compute/v2/extensions/tags/requests.go | 2 +- .../compute/v2/extensions/tags/results.go | 2 +- .../extensions/tags/testing/requests_test.go | 6 +-- openstack/compute/v2/extensions/tags/urls.go | 2 +- .../v2/extensions/tenantnetworks/requests.go | 4 +- .../v2/extensions/tenantnetworks/results.go | 4 +- .../tenantnetworks/testing/fixtures_test.go | 6 +-- .../tenantnetworks/testing/requests_test.go | 8 ++-- .../v2/extensions/tenantnetworks/urls.go | 2 +- .../v2/extensions/testing/delegate_test.go | 10 ++-- .../v2/extensions/testing/fixtures_test.go | 4 +- openstack/compute/v2/extensions/urls.go | 2 +- .../compute/v2/extensions/usage/requests.go | 4 +- .../compute/v2/extensions/usage/results.go | 4 +- .../extensions/usage/testing/fixtures_test.go | 6 +-- .../extensions/usage/testing/requests_test.go | 8 ++-- openstack/compute/v2/extensions/usage/urls.go | 2 +- .../v2/extensions/volumeattach/requests.go | 4 +- .../v2/extensions/volumeattach/results.go | 4 +- .../volumeattach/testing/fixtures_test.go | 4 +- .../volumeattach/testing/requests_test.go | 8 ++-- .../v2/extensions/volumeattach/urls.go | 2 +- openstack/compute/v2/flavors/requests.go | 4 +- openstack/compute/v2/flavors/results.go | 4 +- .../v2/flavors/testing/fixtures_test.go | 4 +- .../v2/flavors/testing/requests_test.go | 8 ++-- openstack/compute/v2/flavors/urls.go | 2 +- openstack/compute/v2/images/requests.go | 4 +- openstack/compute/v2/images/results.go | 4 +- .../v2/images/testing/requests_test.go | 8 ++-- openstack/compute/v2/images/urls.go | 2 +- openstack/compute/v2/servers/errors.go | 2 +- openstack/compute/v2/servers/requests.go | 4 +- openstack/compute/v2/servers/results.go | 4 +- .../v2/servers/testing/fixtures_test.go | 8 ++-- .../v2/servers/testing/requests_test.go | 14 +++--- .../v2/servers/testing/results_test.go | 8 ++-- openstack/compute/v2/servers/urls.go | 2 +- openstack/compute/v2/servers/util.go | 2 +- openstack/config/clouds/clouds.go | 2 +- openstack/config/clouds/clouds_test.go | 2 +- openstack/config/clouds/options.go | 2 +- openstack/config/provider_client.go | 4 +- openstack/container/v1/capsules/errors.go | 2 +- .../container/v1/capsules/microversions.go | 4 +- openstack/container/v1/capsules/requests.go | 4 +- openstack/container/v1/capsules/results.go | 4 +- .../v1/capsules/testing/fixtures_test.go | 6 +-- .../v1/capsules/testing/requests_test.go | 10 ++-- .../v1/capsules/testing/template_test.go | 4 +- openstack/container/v1/capsules/urls.go | 2 +- .../containerinfra/apiversions/requests.go | 4 +- .../containerinfra/apiversions/results.go | 4 +- .../apiversions/testing/fixtures_test.go | 6 +-- .../apiversions/testing/requests_test.go | 6 +-- openstack/containerinfra/apiversions/urls.go | 4 +- .../v1/certificates/requests.go | 2 +- .../containerinfra/v1/certificates/results.go | 2 +- .../v1/certificates/testing/fixtures_test.go | 8 ++-- .../v1/certificates/testing/requests_test.go | 6 +-- .../containerinfra/v1/certificates/urls.go | 2 +- .../containerinfra/v1/clusters/requests.go | 4 +- .../containerinfra/v1/clusters/results.go | 4 +- .../v1/clusters/testing/fixtures_test.go | 8 ++-- .../v1/clusters/testing/requests_test.go | 10 ++-- openstack/containerinfra/v1/clusters/urls.go | 2 +- .../v1/clustertemplates/requests.go | 4 +- .../v1/clustertemplates/results.go | 4 +- .../clustertemplates/testing/fixtures_test.go | 8 ++-- .../clustertemplates/testing/requests_test.go | 8 ++-- .../v1/clustertemplates/urls.go | 2 +- .../containerinfra/v1/nodegroups/requests.go | 4 +- .../containerinfra/v1/nodegroups/results.go | 4 +- .../v1/nodegroups/testing/fixtures_test.go | 8 ++-- .../v1/nodegroups/testing/requests_test.go | 8 ++-- .../containerinfra/v1/nodegroups/urls.go | 2 +- .../containerinfra/v1/quotas/requests.go | 2 +- openstack/containerinfra/v1/quotas/results.go | 2 +- .../v1/quotas/testing/fixtures_test.go | 4 +- .../v1/quotas/testing/requests_test.go | 6 +-- openstack/containerinfra/v1/quotas/urls.go | 2 +- openstack/db/v1/configurations/requests.go | 6 +-- openstack/db/v1/configurations/results.go | 4 +- .../configurations/testing/fixtures_test.go | 4 +- .../configurations/testing/requests_test.go | 12 ++--- openstack/db/v1/configurations/urls.go | 2 +- openstack/db/v1/databases/requests.go | 4 +- openstack/db/v1/databases/results.go | 4 +- .../db/v1/databases/testing/fixtures_test.go | 2 +- .../db/v1/databases/testing/requests_test.go | 8 ++-- openstack/db/v1/databases/urls.go | 2 +- openstack/db/v1/datastores/requests.go | 4 +- openstack/db/v1/datastores/results.go | 4 +- .../db/v1/datastores/testing/fixtures_test.go | 4 +- .../db/v1/datastores/testing/requests_test.go | 10 ++-- openstack/db/v1/datastores/urls.go | 2 +- openstack/db/v1/flavors/requests.go | 4 +- openstack/db/v1/flavors/results.go | 4 +- .../db/v1/flavors/testing/fixtures_test.go | 2 +- .../db/v1/flavors/testing/requests_test.go | 10 ++-- openstack/db/v1/flavors/urls.go | 2 +- openstack/db/v1/instances/requests.go | 8 ++-- openstack/db/v1/instances/results.go | 8 ++-- .../db/v1/instances/testing/fixtures_test.go | 8 ++-- .../db/v1/instances/testing/requests_test.go | 12 ++--- openstack/db/v1/instances/urls.go | 2 +- openstack/db/v1/users/requests.go | 6 +-- openstack/db/v1/users/results.go | 6 +-- .../db/v1/users/testing/fixtures_test.go | 2 +- .../db/v1/users/testing/requests_test.go | 10 ++-- openstack/db/v1/users/urls.go | 2 +- openstack/dns/v2/recordsets/requests.go | 4 +- openstack/dns/v2/recordsets/results.go | 4 +- .../v2/recordsets/testing/fixtures_test.go | 8 ++-- .../v2/recordsets/testing/requests_test.go | 8 ++-- openstack/dns/v2/recordsets/urls.go | 2 +- openstack/dns/v2/transfer/accept/requests.go | 4 +- openstack/dns/v2/transfer/accept/results.go | 4 +- .../transfer/accept/testing/accepts_test.go | 8 ++-- .../transfer/accept/testing/fixtures_test.go | 8 ++-- openstack/dns/v2/transfer/accept/urls.go | 2 +- openstack/dns/v2/transfer/request/requests.go | 4 +- openstack/dns/v2/transfer/request/results.go | 4 +- .../transfer/request/testing/fixtures_test.go | 8 ++-- .../transfer/request/testing/requests_test.go | 8 ++-- openstack/dns/v2/transfer/request/urls.go | 2 +- openstack/dns/v2/zones/requests.go | 4 +- openstack/dns/v2/zones/results.go | 4 +- .../dns/v2/zones/testing/fixtures_test.go | 8 ++-- .../dns/v2/zones/testing/requests_test.go | 8 ++-- openstack/dns/v2/zones/urls.go | 2 +- openstack/endpoint_location.go | 6 +-- openstack/errors.go | 2 +- .../v2/extensions/admin/roles/requests.go | 4 +- .../v2/extensions/admin/roles/results.go | 4 +- .../admin/roles/testing/fixtures_test.go | 4 +- .../admin/roles/testing/requests_test.go | 8 ++-- .../v2/extensions/admin/roles/urls.go | 2 +- openstack/identity/v2/extensions/delegate.go | 6 +-- .../v2/extensions/testing/delegate_test.go | 10 ++-- .../v2/extensions/testing/fixtures_test.go | 4 +- openstack/identity/v2/tenants/requests.go | 4 +- openstack/identity/v2/tenants/results.go | 4 +- .../v2/tenants/testing/fixtures_test.go | 6 +-- .../v2/tenants/testing/requests_test.go | 10 ++-- openstack/identity/v2/tenants/urls.go | 2 +- openstack/identity/v2/tokens/requests.go | 2 +- openstack/identity/v2/tokens/results.go | 4 +- .../v2/tokens/testing/fixtures_test.go | 8 ++-- .../v2/tokens/testing/requests_test.go | 8 ++-- openstack/identity/v2/tokens/urls.go | 2 +- openstack/identity/v2/users/requests.go | 4 +- openstack/identity/v2/users/results.go | 4 +- .../v2/users/testing/fixtures_test.go | 4 +- .../v2/users/testing/requests_test.go | 10 ++-- openstack/identity/v2/users/urls.go | 2 +- .../v3/applicationcredentials/requests.go | 4 +- .../v3/applicationcredentials/results.go | 4 +- .../testing/fixtures_test.go | 6 +-- .../testing/requests_test.go | 8 ++-- .../v3/applicationcredentials/urls.go | 2 +- openstack/identity/v3/catalog/requests.go | 4 +- openstack/identity/v3/catalog/results.go | 4 +- .../v3/catalog/testing/catalog_test.go | 8 ++-- .../v3/catalog/testing/fixtures_test.go | 6 +-- openstack/identity/v3/catalog/urls.go | 2 +- openstack/identity/v3/credentials/requests.go | 4 +- openstack/identity/v3/credentials/results.go | 4 +- .../v3/credentials/testing/fixtures_test.go | 6 +-- .../v3/credentials/testing/requests_test.go | 8 ++-- openstack/identity/v3/credentials/urls.go | 2 +- openstack/identity/v3/domains/requests.go | 4 +- openstack/identity/v3/domains/results.go | 4 +- .../v3/domains/testing/fixtures_test.go | 6 +-- .../v3/domains/testing/requests_test.go | 8 ++-- openstack/identity/v3/domains/urls.go | 2 +- openstack/identity/v3/endpoints/requests.go | 4 +- openstack/identity/v3/endpoints/results.go | 4 +- .../v3/endpoints/testing/requests_test.go | 10 ++-- openstack/identity/v3/endpoints/urls.go | 2 +- .../v3/extensions/ec2credentials/requests.go | 4 +- .../v3/extensions/ec2credentials/results.go | 4 +- .../ec2credentials/testing/fixtures_test.go | 6 +-- .../ec2credentials/testing/requests_test.go | 8 ++-- .../v3/extensions/ec2credentials/urls.go | 2 +- .../v3/extensions/ec2tokens/requests.go | 4 +- .../ec2tokens/testing/requests_test.go | 10 ++-- .../identity/v3/extensions/ec2tokens/urls.go | 2 +- .../v3/extensions/federation/requests.go | 4 +- .../v3/extensions/federation/results.go | 4 +- .../federation/testing/fixtures_test.go | 6 +-- .../federation/testing/requests_test.go | 8 ++-- .../identity/v3/extensions/federation/urls.go | 2 +- .../identity/v3/extensions/oauth1/requests.go | 6 +-- .../identity/v3/extensions/oauth1/results.go | 4 +- .../oauth1/testing/fixtures_test.go | 8 ++-- .../oauth1/testing/requests_test.go | 10 ++-- .../identity/v3/extensions/oauth1/urls.go | 2 +- .../extensions/projectendpoints/requests.go | 4 +- .../v3/extensions/projectendpoints/results.go | 4 +- .../projectendpoints/testing/requests_test.go | 10 ++-- .../v3/extensions/projectendpoints/urls.go | 2 +- .../identity/v3/extensions/trusts/requests.go | 6 +-- .../identity/v3/extensions/trusts/results.go | 4 +- .../trusts/testing/fixtures_test.go | 8 ++-- .../trusts/testing/requests_test.go | 10 ++-- .../identity/v3/extensions/trusts/urls.go | 2 +- openstack/identity/v3/groups/requests.go | 4 +- openstack/identity/v3/groups/results.go | 4 +- .../v3/groups/testing/fixtures_test.go | 6 +-- .../v3/groups/testing/requests_test.go | 8 ++-- openstack/identity/v3/groups/urls.go | 2 +- openstack/identity/v3/limits/requests.go | 4 +- openstack/identity/v3/limits/results.go | 4 +- .../v3/limits/testing/fixtures_test.go | 6 +-- .../v3/limits/testing/requests_test.go | 8 ++-- openstack/identity/v3/limits/urls.go | 2 +- openstack/identity/v3/osinherit/requests.go | 2 +- openstack/identity/v3/osinherit/results.go | 2 +- .../v3/osinherit/testing/fixtures_test.go | 4 +- .../v3/osinherit/testing/requests_test.go | 6 +-- openstack/identity/v3/osinherit/urls.go | 2 +- openstack/identity/v3/policies/requests.go | 4 +- openstack/identity/v3/policies/results.go | 4 +- .../v3/policies/testing/fixtures_test.go | 6 +-- .../v3/policies/testing/requests_test.go | 8 ++-- openstack/identity/v3/policies/urls.go | 2 +- openstack/identity/v3/projects/requests.go | 4 +- openstack/identity/v3/projects/results.go | 4 +- .../v3/projects/testing/fixtures_test.go | 6 +-- .../v3/projects/testing/requests_test.go | 8 ++-- openstack/identity/v3/projects/urls.go | 2 +- openstack/identity/v3/regions/requests.go | 4 +- openstack/identity/v3/regions/results.go | 4 +- .../v3/regions/testing/fixtures_test.go | 6 +-- .../v3/regions/testing/requests_test.go | 8 ++-- openstack/identity/v3/regions/urls.go | 2 +- .../identity/v3/registeredlimits/requests.go | 4 +- .../identity/v3/registeredlimits/results.go | 4 +- .../registeredlimits/testing/fixtures_test.go | 6 +-- .../registeredlimits/testing/requests_test.go | 8 ++-- .../identity/v3/registeredlimits/urls.go | 2 +- openstack/identity/v3/roles/requests.go | 4 +- openstack/identity/v3/roles/results.go | 4 +- .../v3/roles/testing/fixtures_test.go | 6 +-- .../v3/roles/testing/requests_test.go | 8 ++-- openstack/identity/v3/roles/urls.go | 2 +- openstack/identity/v3/services/requests.go | 4 +- openstack/identity/v3/services/results.go | 4 +- .../v3/services/testing/fixtures_test.go | 6 +-- .../v3/services/testing/requests_test.go | 8 ++-- openstack/identity/v3/services/urls.go | 2 +- openstack/identity/v3/tokens/requests.go | 2 +- openstack/identity/v3/tokens/results.go | 2 +- .../identity/v3/tokens/testing/fixtures.go | 6 +-- .../v3/tokens/testing/requests_test.go | 6 +-- .../v3/tokens/testing/results_test.go | 2 +- openstack/identity/v3/tokens/urls.go | 2 +- openstack/identity/v3/users/requests.go | 8 ++-- openstack/identity/v3/users/results.go | 4 +- .../v3/users/testing/fixtures_test.go | 12 ++--- .../v3/users/testing/requests_test.go | 12 ++--- openstack/identity/v3/users/urls.go | 2 +- .../imageservice/v2/imagedata/requests.go | 2 +- .../imageservice/v2/imagedata/results.go | 2 +- .../v2/imagedata/testing/fixtures_test.go | 4 +- .../v2/imagedata/testing/requests_test.go | 6 +-- openstack/imageservice/v2/imagedata/urls.go | 2 +- .../imageservice/v2/imageimport/requests.go | 2 +- .../imageservice/v2/imageimport/results.go | 2 +- .../v2/imageimport/testing/requests_test.go | 6 +-- openstack/imageservice/v2/imageimport/urls.go | 2 +- openstack/imageservice/v2/images/requests.go | 4 +- openstack/imageservice/v2/images/results.go | 4 +- .../v2/images/testing/fixtures_test.go | 4 +- .../v2/images/testing/requests_test.go | 8 ++-- openstack/imageservice/v2/images/urls.go | 4 +- openstack/imageservice/v2/members/requests.go | 4 +- openstack/imageservice/v2/members/results.go | 4 +- .../v2/members/testing/fixtures_test.go | 4 +- .../v2/members/testing/requests_test.go | 8 ++-- openstack/imageservice/v2/members/urls.go | 2 +- openstack/imageservice/v2/tasks/requests.go | 4 +- openstack/imageservice/v2/tasks/results.go | 4 +- .../v2/tasks/testing/fixtures_test.go | 2 +- .../v2/tasks/testing/requests_test.go | 8 ++-- openstack/imageservice/v2/tasks/urls.go | 4 +- openstack/keymanager/v1/acls/requests.go | 2 +- openstack/keymanager/v1/acls/results.go | 2 +- .../v1/acls/testing/fixtures_test.go | 6 +-- .../v1/acls/testing/requests_test.go | 6 +-- openstack/keymanager/v1/acls/urls.go | 2 +- .../keymanager/v1/containers/requests.go | 4 +- openstack/keymanager/v1/containers/results.go | 4 +- .../v1/containers/testing/fixtures_test.go | 6 +-- .../v1/containers/testing/requests_test.go | 8 ++-- openstack/keymanager/v1/containers/urls.go | 2 +- openstack/keymanager/v1/orders/requests.go | 4 +- openstack/keymanager/v1/orders/results.go | 4 +- .../v1/orders/testing/fixtures_test.go | 6 +-- .../v1/orders/testing/requests_test.go | 8 ++-- openstack/keymanager/v1/orders/urls.go | 2 +- openstack/keymanager/v1/secrets/requests.go | 4 +- openstack/keymanager/v1/secrets/results.go | 4 +- .../v1/secrets/testing/fixtures_test.go | 6 +-- .../v1/secrets/testing/requests_test.go | 8 ++-- openstack/keymanager/v1/secrets/urls.go | 2 +- .../loadbalancer/v2/amphorae/requests.go | 4 +- openstack/loadbalancer/v2/amphorae/results.go | 4 +- .../v2/amphorae/testing/fixtures_test.go | 6 +-- .../v2/amphorae/testing/requests_test.go | 8 ++-- openstack/loadbalancer/v2/amphorae/urls.go | 2 +- .../loadbalancer/v2/apiversions/requests.go | 4 +- .../loadbalancer/v2/apiversions/results.go | 2 +- .../v2/apiversions/testing/fixture.go | 6 +-- .../v2/apiversions/testing/requests_test.go | 6 +-- openstack/loadbalancer/v2/apiversions/urls.go | 4 +- .../v2/flavorprofiles/requests.go | 4 +- .../loadbalancer/v2/flavorprofiles/results.go | 4 +- .../v2/flavorprofiles/testing/fixtures.go | 6 +-- .../flavorprofiles/testing/requests_test.go | 8 ++-- .../loadbalancer/v2/flavorprofiles/urls.go | 2 +- openstack/loadbalancer/v2/flavors/requests.go | 4 +- openstack/loadbalancer/v2/flavors/results.go | 4 +- .../v2/flavors/testing/fixtures.go | 6 +-- .../v2/flavors/testing/requests_test.go | 8 ++-- openstack/loadbalancer/v2/flavors/urls.go | 2 +- .../loadbalancer/v2/l7policies/requests.go | 4 +- .../loadbalancer/v2/l7policies/results.go | 4 +- .../v2/l7policies/testing/fixtures_test.go | 6 +-- .../v2/l7policies/testing/requests_test.go | 8 ++-- openstack/loadbalancer/v2/l7policies/urls.go | 2 +- .../loadbalancer/v2/listeners/requests.go | 8 ++-- .../loadbalancer/v2/listeners/results.go | 8 ++-- .../v2/listeners/testing/fixtures_test.go | 6 +-- .../v2/listeners/testing/requests_test.go | 10 ++-- openstack/loadbalancer/v2/listeners/urls.go | 2 +- .../loadbalancer/v2/loadbalancers/requests.go | 8 ++-- .../loadbalancer/v2/loadbalancers/results.go | 8 ++-- .../v2/loadbalancers/testing/fixtures_test.go | 14 +++--- .../v2/loadbalancers/testing/requests_test.go | 20 ++++---- .../loadbalancer/v2/loadbalancers/urls.go | 2 +- .../loadbalancer/v2/monitors/requests.go | 4 +- openstack/loadbalancer/v2/monitors/results.go | 4 +- .../v2/monitors/testing/fixtures_test.go | 6 +-- .../v2/monitors/testing/requests_test.go | 8 ++-- openstack/loadbalancer/v2/monitors/urls.go | 2 +- openstack/loadbalancer/v2/pools/requests.go | 6 +-- openstack/loadbalancer/v2/pools/results.go | 6 +-- .../v2/pools/testing/fixtures_test.go | 6 +-- .../v2/pools/testing/requests_test.go | 8 ++-- openstack/loadbalancer/v2/pools/urls.go | 2 +- .../loadbalancer/v2/providers/requests.go | 4 +- .../loadbalancer/v2/providers/results.go | 4 +- .../v2/providers/testing/fixtures_test.go | 6 +-- .../v2/providers/testing/requests_test.go | 8 ++-- openstack/loadbalancer/v2/providers/urls.go | 2 +- openstack/loadbalancer/v2/quotas/requests.go | 2 +- openstack/loadbalancer/v2/quotas/results.go | 2 +- .../v2/quotas/testing/fixtures_test.go | 2 +- .../v2/quotas/testing/requests_test.go | 8 ++-- openstack/loadbalancer/v2/quotas/urls.go | 2 +- .../loadbalancer/v2/testhelper/client.go | 4 +- openstack/messaging/v2/claims/requests.go | 2 +- openstack/messaging/v2/claims/results.go | 2 +- .../v2/claims/testing/fixtures_test.go | 6 +-- .../v2/claims/testing/requests_test.go | 6 +-- openstack/messaging/v2/claims/urls.go | 2 +- openstack/messaging/v2/messages/requests.go | 4 +- openstack/messaging/v2/messages/results.go | 4 +- .../v2/messages/testing/fixtures_test.go | 6 +-- .../v2/messages/testing/requests_test.go | 8 ++-- openstack/messaging/v2/messages/urls.go | 2 +- openstack/messaging/v2/queues/requests.go | 4 +- openstack/messaging/v2/queues/results.go | 4 +- .../v2/queues/testing/fixtures_test.go | 6 +-- .../v2/queues/testing/requests_test.go | 8 ++-- openstack/messaging/v2/queues/urls.go | 2 +- .../networking/v2/apiversions/requests.go | 4 +- .../networking/v2/apiversions/results.go | 2 +- .../v2/apiversions/testing/requests_test.go | 8 ++-- openstack/networking/v2/apiversions/urls.go | 4 +- .../networking/v2/common/common_tests.go | 4 +- .../v2/extensions/agents/requests.go | 4 +- .../v2/extensions/agents/results.go | 10 ++-- .../agents/testing/fixtures_test.go | 2 +- .../agents/testing/requests_test.go | 10 ++-- .../networking/v2/extensions/agents/urls.go | 2 +- .../v2/extensions/attributestags/requests.go | 2 +- .../v2/extensions/attributestags/results.go | 2 +- .../attributestags/testing/requests_test.go | 6 +-- .../v2/extensions/attributestags/urls.go | 2 +- .../v2/extensions/bgp/peers/requests.go | 4 +- .../v2/extensions/bgp/peers/results.go | 4 +- .../extensions/bgp/peers/testing/fixture.go | 2 +- .../bgp/peers/testing/requests_test.go | 8 ++-- .../v2/extensions/bgp/peers/urls.go | 2 +- .../v2/extensions/bgp/speakers/requests.go | 4 +- .../v2/extensions/bgp/speakers/results.go | 4 +- .../bgp/speakers/testing/fixture.go | 2 +- .../bgp/speakers/testing/requests_test.go | 8 ++-- .../v2/extensions/bgp/speakers/urls.go | 2 +- .../networking/v2/extensions/delegate.go | 6 +-- .../networking/v2/extensions/dns/requests.go | 8 ++-- .../extensions/dns/testing/fixtures_test.go | 10 ++-- .../extensions/dns/testing/requests_test.go | 12 ++--- .../v2/extensions/external/requests.go | 4 +- .../external/testing/requests_test.go | 6 +-- .../external/testing/results_test.go | 10 ++-- .../v2/extensions/extradhcpopts/requests.go | 4 +- .../v2/extensions/fwaas/firewalls/requests.go | 4 +- .../v2/extensions/fwaas/firewalls/results.go | 4 +- .../fwaas/firewalls/testing/requests_test.go | 12 ++--- .../v2/extensions/fwaas/firewalls/urls.go | 2 +- .../v2/extensions/fwaas/policies/requests.go | 4 +- .../v2/extensions/fwaas/policies/results.go | 4 +- .../fwaas/policies/testing/requests_test.go | 10 ++-- .../v2/extensions/fwaas/policies/urls.go | 2 +- .../fwaas/routerinsertion/requests.go | 2 +- .../routerinsertion/testing/requests_test.go | 10 ++-- .../v2/extensions/fwaas/rules/requests.go | 4 +- .../v2/extensions/fwaas/rules/results.go | 4 +- .../fwaas/rules/testing/requests_test.go | 10 ++-- .../v2/extensions/fwaas/rules/urls.go | 2 +- .../v2/extensions/fwaas_v2/groups/requests.go | 4 +- .../v2/extensions/fwaas_v2/groups/results.go | 4 +- .../fwaas_v2/groups/testing/requests_test.go | 8 ++-- .../v2/extensions/fwaas_v2/groups/urls.go | 2 +- .../extensions/fwaas_v2/policies/requests.go | 4 +- .../extensions/fwaas_v2/policies/results.go | 4 +- .../policies/testing/requests_test.go | 10 ++-- .../v2/extensions/fwaas_v2/policies/urls.go | 2 +- .../v2/extensions/fwaas_v2/rules/requests.go | 4 +- .../v2/extensions/fwaas_v2/rules/results.go | 4 +- .../fwaas_v2/rules/testing/requests_test.go | 10 ++-- .../v2/extensions/fwaas_v2/rules/urls.go | 2 +- .../layer3/addressscopes/requests.go | 4 +- .../layer3/addressscopes/results.go | 4 +- .../addressscopes/testing/fixtures_test.go | 2 +- .../addressscopes/testing/requests_test.go | 8 ++-- .../extensions/layer3/addressscopes/urls.go | 2 +- .../extensions/layer3/extraroutes/requests.go | 4 +- .../extensions/layer3/extraroutes/results.go | 4 +- .../extraroutes/testing/requests_test.go | 8 ++-- .../v2/extensions/layer3/extraroutes/urls.go | 2 +- .../extensions/layer3/floatingips/requests.go | 4 +- .../extensions/layer3/floatingips/results.go | 4 +- .../floatingips/testing/requests_test.go | 8 ++-- .../v2/extensions/layer3/floatingips/urls.go | 2 +- .../layer3/portforwarding/requests.go | 4 +- .../layer3/portforwarding/results.go | 4 +- .../portforwarding/testing/requests_test.go | 8 ++-- .../extensions/layer3/portforwarding/urls.go | 2 +- .../v2/extensions/layer3/routers/requests.go | 4 +- .../v2/extensions/layer3/routers/results.go | 4 +- .../layer3/routers/testing/requests_test.go | 8 ++-- .../v2/extensions/layer3/routers/urls.go | 2 +- .../v2/extensions/lbaas/members/requests.go | 4 +- .../v2/extensions/lbaas/members/results.go | 4 +- .../lbaas/members/testing/requests_test.go | 10 ++-- .../v2/extensions/lbaas/members/urls.go | 2 +- .../v2/extensions/lbaas/monitors/requests.go | 4 +- .../v2/extensions/lbaas/monitors/results.go | 4 +- .../lbaas/monitors/testing/requests_test.go | 10 ++-- .../v2/extensions/lbaas/monitors/urls.go | 2 +- .../v2/extensions/lbaas/pools/requests.go | 4 +- .../v2/extensions/lbaas/pools/results.go | 4 +- .../lbaas/pools/testing/requests_test.go | 8 ++-- .../v2/extensions/lbaas/pools/urls.go | 2 +- .../v2/extensions/lbaas/vips/requests.go | 4 +- .../v2/extensions/lbaas/vips/results.go | 4 +- .../lbaas/vips/testing/requests_test.go | 10 ++-- .../v2/extensions/lbaas/vips/urls.go | 2 +- .../lbaas_v2/l7policies/requests.go | 4 +- .../extensions/lbaas_v2/l7policies/results.go | 4 +- .../l7policies/testing/fixtures_test.go | 6 +-- .../l7policies/testing/requests_test.go | 8 ++-- .../v2/extensions/lbaas_v2/l7policies/urls.go | 2 +- .../extensions/lbaas_v2/listeners/requests.go | 4 +- .../extensions/lbaas_v2/listeners/results.go | 8 ++-- .../listeners/testing/fixtures_test.go | 6 +-- .../listeners/testing/requests_test.go | 10 ++-- .../v2/extensions/lbaas_v2/listeners/urls.go | 2 +- .../lbaas_v2/loadbalancers/requests.go | 4 +- .../lbaas_v2/loadbalancers/results.go | 8 ++-- .../loadbalancers/testing/fixtures_test.go | 12 ++--- .../loadbalancers/testing/requests_test.go | 10 ++-- .../extensions/lbaas_v2/loadbalancers/urls.go | 2 +- .../extensions/lbaas_v2/monitors/requests.go | 4 +- .../extensions/lbaas_v2/monitors/results.go | 4 +- .../monitors/testing/fixtures_test.go | 6 +-- .../monitors/testing/requests_test.go | 8 ++-- .../v2/extensions/lbaas_v2/monitors/urls.go | 2 +- .../v2/extensions/lbaas_v2/pools/requests.go | 4 +- .../v2/extensions/lbaas_v2/pools/results.go | 6 +-- .../lbaas_v2/pools/testing/fixtures_test.go | 6 +-- .../lbaas_v2/pools/testing/requests_test.go | 8 ++-- .../v2/extensions/lbaas_v2/pools/urls.go | 2 +- .../networking/v2/extensions/mtu/requests.go | 4 +- .../extensions/mtu/testing/requests_test.go | 6 +-- .../v2/extensions/mtu/testing/results_test.go | 10 ++-- .../networkipavailabilities/requests.go | 4 +- .../networkipavailabilities/results.go | 4 +- .../testing/fixtures_test.go | 4 +- .../testing/requests_test.go | 10 ++-- .../networkipavailabilities/urls.go | 2 +- .../v2/extensions/portsbinding/requests.go | 2 +- .../portsbinding/testing/fixtures_test.go | 6 +-- .../portsbinding/testing/requests_test.go | 8 ++-- .../v2/extensions/portsecurity/requests.go | 4 +- .../v2/extensions/provider/requests.go | 2 +- .../provider/testing/results_test.go | 12 ++--- .../v2/extensions/qos/policies/requests.go | 8 ++-- .../v2/extensions/qos/policies/results.go | 4 +- .../qos/policies/testing/fixtures_test.go | 2 +- .../qos/policies/testing/requests_test.go | 12 ++--- .../v2/extensions/qos/policies/urls.go | 2 +- .../v2/extensions/qos/rules/requests.go | 4 +- .../v2/extensions/qos/rules/results.go | 4 +- .../qos/rules/testing/requests_test.go | 8 ++-- .../v2/extensions/qos/rules/urls.go | 2 +- .../v2/extensions/qos/ruletypes/requests.go | 4 +- .../v2/extensions/qos/ruletypes/results.go | 4 +- .../qos/ruletypes/testing/requests_test.go | 6 +-- .../v2/extensions/qos/ruletypes/urls.go | 2 +- .../v2/extensions/quotas/requests.go | 2 +- .../v2/extensions/quotas/results.go | 2 +- .../quotas/testing/fixtures_test.go | 2 +- .../quotas/testing/requests_test.go | 8 ++-- .../networking/v2/extensions/quotas/urls.go | 2 +- .../v2/extensions/rbacpolicies/requests.go | 4 +- .../v2/extensions/rbacpolicies/results.go | 4 +- .../rbacpolicies/testing/fixtures_test.go | 2 +- .../rbacpolicies/testing/requests_test.go | 8 ++-- .../v2/extensions/rbacpolicies/urls.go | 2 +- .../v2/extensions/security/groups/requests.go | 4 +- .../v2/extensions/security/groups/results.go | 6 +-- .../security/groups/testing/fixtures_test.go | 4 +- .../security/groups/testing/requests_test.go | 8 ++-- .../v2/extensions/security/groups/urls.go | 2 +- .../v2/extensions/security/rules/requests.go | 4 +- .../v2/extensions/security/rules/results.go | 4 +- .../security/rules/testing/requests_test.go | 8 ++-- .../v2/extensions/security/rules/urls.go | 2 +- .../v2/extensions/subnetpools/requests.go | 4 +- .../v2/extensions/subnetpools/results.go | 4 +- .../subnetpools/testing/fixtures_test.go | 2 +- .../subnetpools/testing/requests_test.go | 8 ++-- .../v2/extensions/subnetpools/urls.go | 2 +- .../v2/extensions/testing/delegate_test.go | 10 ++-- .../v2/extensions/trunk_details/results.go | 2 +- .../trunk_details/testing/requests_test.go | 8 ++-- .../v2/extensions/trunks/requests.go | 4 +- .../v2/extensions/trunks/results.go | 4 +- .../trunks/testing/fixtures_test.go | 2 +- .../trunks/testing/requests_test.go | 8 ++-- .../networking/v2/extensions/trunks/urls.go | 2 +- .../v2/extensions/vlantransparent/requests.go | 4 +- .../vlantransparent/testing/requests_test.go | 8 ++-- .../vpnaas/endpointgroups/requests.go | 4 +- .../vpnaas/endpointgroups/results.go | 4 +- .../endpointgroups/testing/requests_test.go | 8 ++-- .../extensions/vpnaas/endpointgroups/urls.go | 2 +- .../extensions/vpnaas/ikepolicies/requests.go | 4 +- .../extensions/vpnaas/ikepolicies/results.go | 4 +- .../ikepolicies/testing/requests_test.go | 8 ++-- .../v2/extensions/vpnaas/ikepolicies/urls.go | 2 +- .../vpnaas/ipsecpolicies/requests.go | 4 +- .../vpnaas/ipsecpolicies/results.go | 4 +- .../ipsecpolicies/testing/requests_test.go | 8 ++-- .../extensions/vpnaas/ipsecpolicies/urls.go | 2 +- .../v2/extensions/vpnaas/services/requests.go | 4 +- .../v2/extensions/vpnaas/services/results.go | 4 +- .../vpnaas/services/testing/requests_test.go | 10 ++-- .../v2/extensions/vpnaas/services/urls.go | 2 +- .../vpnaas/siteconnections/requests.go | 4 +- .../vpnaas/siteconnections/results.go | 4 +- .../siteconnections/testing/requests_test.go | 10 ++-- .../extensions/vpnaas/siteconnections/urls.go | 2 +- openstack/networking/v2/networks/requests.go | 4 +- openstack/networking/v2/networks/results.go | 4 +- .../v2/networks/testing/fixtures.go | 2 +- .../v2/networks/testing/requests_test.go | 10 ++-- openstack/networking/v2/networks/urls.go | 2 +- openstack/networking/v2/ports/requests.go | 4 +- openstack/networking/v2/ports/results.go | 4 +- .../v2/ports/testing/requests_test.go | 12 ++--- openstack/networking/v2/ports/urls.go | 2 +- openstack/networking/v2/subnets/requests.go | 4 +- openstack/networking/v2/subnets/results.go | 4 +- .../v2/subnets/testing/fixtures_test.go | 2 +- .../v2/subnets/testing/requests_test.go | 8 ++-- .../v2/subnets/testing/results_test.go | 6 +-- openstack/networking/v2/subnets/urls.go | 2 +- .../objectstorage/v1/accounts/requests.go | 2 +- .../objectstorage/v1/accounts/results.go | 2 +- .../v1/accounts/testing/fixtures.go | 4 +- .../v1/accounts/testing/requests_test.go | 6 +-- openstack/objectstorage/v1/accounts/urls.go | 2 +- .../objectstorage/v1/containers/requests.go | 6 +-- .../objectstorage/v1/containers/results.go | 4 +- .../v1/containers/testing/fixtures.go | 6 +-- .../v1/containers/testing/requests_test.go | 10 ++-- openstack/objectstorage/v1/containers/urls.go | 4 +- openstack/objectstorage/v1/errors.go | 2 +- openstack/objectstorage/v1/objects/errors.go | 2 +- .../objectstorage/v1/objects/requests.go | 10 ++-- openstack/objectstorage/v1/objects/results.go | 4 +- .../v1/objects/testing/fixtures_test.go | 6 +-- .../v1/objects/testing/requests_test.go | 14 +++--- openstack/objectstorage/v1/objects/urls.go | 4 +- openstack/objectstorage/v1/swauth/requests.go | 2 +- openstack/objectstorage/v1/swauth/results.go | 2 +- .../v1/swauth/testing/fixtures_test.go | 4 +- .../v1/swauth/testing/requests_test.go | 6 +-- openstack/objectstorage/v1/swauth/urls.go | 2 +- .../orchestration/v1/apiversions/requests.go | 4 +- .../orchestration/v1/apiversions/results.go | 4 +- .../v1/apiversions/testing/requests_test.go | 10 ++-- .../orchestration/v1/apiversions/urls.go | 4 +- .../orchestration/v1/buildinfo/requests.go | 2 +- .../orchestration/v1/buildinfo/results.go | 2 +- .../v1/buildinfo/testing/fixtures_test.go | 6 +-- .../v1/buildinfo/testing/requests_test.go | 6 +-- openstack/orchestration/v1/buildinfo/urls.go | 2 +- .../v1/resourcetypes/requests.go | 2 +- .../orchestration/v1/resourcetypes/results.go | 2 +- .../v1/resourcetypes/testing/fixtures_test.go | 6 +-- .../v1/resourcetypes/testing/requests_test.go | 6 +-- .../orchestration/v1/resourcetypes/urls.go | 2 +- .../orchestration/v1/stackevents/requests.go | 4 +- .../orchestration/v1/stackevents/results.go | 4 +- .../v1/stackevents/testing/fixtures_test.go | 8 ++-- .../v1/stackevents/testing/requests_test.go | 8 ++-- .../orchestration/v1/stackevents/urls.go | 2 +- .../v1/stackresources/requests.go | 4 +- .../v1/stackresources/results.go | 4 +- .../stackresources/testing/fixtures_test.go | 8 ++-- .../stackresources/testing/requests_test.go | 8 ++-- .../orchestration/v1/stackresources/urls.go | 2 +- openstack/orchestration/v1/stacks/doc.go | 6 +-- .../v1/stacks/environment_test.go | 2 +- openstack/orchestration/v1/stacks/errors.go | 2 +- openstack/orchestration/v1/stacks/requests.go | 4 +- openstack/orchestration/v1/stacks/results.go | 4 +- openstack/orchestration/v1/stacks/template.go | 2 +- .../orchestration/v1/stacks/template_test.go | 2 +- .../v1/stacks/testing/fixtures_test.go | 8 ++-- .../v1/stacks/testing/requests_test.go | 10 ++-- openstack/orchestration/v1/stacks/urls.go | 2 +- openstack/orchestration/v1/stacks/utils.go | 2 +- .../orchestration/v1/stacks/utils_test.go | 2 +- .../v1/stacktemplates/requests.go | 2 +- .../v1/stacktemplates/results.go | 2 +- .../stacktemplates/testing/fixtures_test.go | 6 +-- .../stacktemplates/testing/requests_test.go | 6 +-- .../orchestration/v1/stacktemplates/urls.go | 2 +- .../v1/resourceproviders/requests.go | 4 +- .../placement/v1/resourceproviders/results.go | 4 +- .../testing/fixtures_test.go | 6 +-- .../testing/requests_test.go | 8 ++-- .../placement/v1/resourceproviders/urls.go | 2 +- .../sharedfilesystems/apiversions/requests.go | 4 +- .../sharedfilesystems/apiversions/results.go | 4 +- .../apiversions/testing/fixtures_test.go | 6 +-- .../apiversions/testing/requests_test.go | 6 +-- .../sharedfilesystems/apiversions/urls.go | 4 +- .../v2/availabilityzones/requests.go | 4 +- .../v2/availabilityzones/results.go | 4 +- .../testing/fixtures_test.go | 4 +- .../testing/requests_test.go | 6 +-- .../v2/availabilityzones/urls.go | 2 +- .../sharedfilesystems/v2/errors/errors.go | 2 +- .../v2/errors/testing/fixtures_test.go | 4 +- .../v2/errors/testing/request_test.go | 8 ++-- .../sharedfilesystems/v2/messages/requests.go | 4 +- .../sharedfilesystems/v2/messages/results.go | 4 +- .../v2/messages/testing/fixtures_test.go | 4 +- .../v2/messages/testing/requests_test.go | 6 +-- .../sharedfilesystems/v2/messages/urls.go | 2 +- .../sharedfilesystems/v2/replicas/requests.go | 4 +- .../sharedfilesystems/v2/replicas/results.go | 4 +- .../v2/replicas/testing/fixtures_test.go | 4 +- .../v2/replicas/testing/request_test.go | 8 ++-- .../sharedfilesystems/v2/replicas/urls.go | 2 +- .../v2/schedulerstats/requests.go | 4 +- .../v2/schedulerstats/results.go | 2 +- .../schedulerstats/testing/fixtures_test.go | 6 +-- .../schedulerstats/testing/requests_test.go | 8 ++-- .../v2/schedulerstats/urls.go | 2 +- .../v2/securityservices/requests.go | 4 +- .../v2/securityservices/results.go | 4 +- .../securityservices/testing/fixtures_test.go | 4 +- .../securityservices/testing/requests_test.go | 8 ++-- .../v2/securityservices/urls.go | 2 +- .../sharedfilesystems/v2/services/requests.go | 4 +- .../sharedfilesystems/v2/services/results.go | 4 +- .../v2/services/testing/fixtures_test.go | 6 +-- .../v2/services/testing/requests_test.go | 8 ++-- .../sharedfilesystems/v2/services/urls.go | 2 +- .../v2/shareaccessrules/requests.go | 2 +- .../v2/shareaccessrules/results.go | 2 +- .../shareaccessrules/testing/fixtures_test.go | 4 +- .../shareaccessrules/testing/requests_test.go | 8 ++-- .../v2/shareaccessrules/urls.go | 2 +- .../v2/sharenetworks/requests.go | 4 +- .../v2/sharenetworks/results.go | 4 +- .../v2/sharenetworks/testing/fixtures_test.go | 4 +- .../v2/sharenetworks/testing/requests_test.go | 8 ++-- .../v2/sharenetworks/urls.go | 2 +- .../sharedfilesystems/v2/shares/requests.go | 4 +- .../sharedfilesystems/v2/shares/results.go | 4 +- .../v2/shares/testing/fixtures_test.go | 4 +- .../v2/shares/testing/request_test.go | 6 +-- openstack/sharedfilesystems/v2/shares/urls.go | 2 +- .../v2/sharetransfers/requests.go | 4 +- .../v2/sharetransfers/results.go | 4 +- .../sharetransfers/testing/fixtures_test.go | 8 ++-- .../sharetransfers/testing/requests_test.go | 8 ++-- .../v2/sharetransfers/urls.go | 2 +- .../v2/sharetypes/requests.go | 4 +- .../v2/sharetypes/results.go | 4 +- .../v2/sharetypes/testing/fixtures_test.go | 4 +- .../v2/sharetypes/testing/requests_test.go | 8 ++-- .../sharedfilesystems/v2/sharetypes/urls.go | 2 +- .../v2/snapshots/requests.go | 4 +- .../sharedfilesystems/v2/snapshots/results.go | 4 +- .../v2/snapshots/testing/fixtures_test.go | 4 +- .../v2/snapshots/testing/request_test.go | 6 +-- .../sharedfilesystems/v2/snapshots/urls.go | 2 +- openstack/testing/client_test.go | 6 +-- openstack/testing/endpoint_location_test.go | 10 ++-- openstack/utils/choose_version.go | 2 +- openstack/utils/testing/base_endpoint_test.go | 4 +- .../utils/testing/choose_version_test.go | 6 +-- .../workflow/v2/crontriggers/requests.go | 4 +- openstack/workflow/v2/crontriggers/results.go | 4 +- .../v2/crontriggers/testing/requests_test.go | 8 ++-- openstack/workflow/v2/crontriggers/urls.go | 2 +- openstack/workflow/v2/executions/requests.go | 4 +- openstack/workflow/v2/executions/results.go | 4 +- .../v2/executions/testing/requests_test.go | 8 ++-- openstack/workflow/v2/executions/urls.go | 2 +- openstack/workflow/v2/workflows/requests.go | 4 +- openstack/workflow/v2/workflows/results.go | 4 +- .../v2/workflows/testing/requests_test.go | 8 ++-- openstack/workflow/v2/workflows/urls.go | 2 +- pagination/http.go | 2 +- pagination/linked.go | 2 +- pagination/marker.go | 2 +- pagination/pager.go | 2 +- pagination/single.go | 2 +- pagination/testing/linked_test.go | 4 +- pagination/testing/marker_test.go | 4 +- pagination/testing/pagination_test.go | 4 +- pagination/testing/single_test.go | 4 +- provider_client.go | 2 +- testhelper/client/fake.go | 4 +- testhelper/fixture/helper.go | 4 +- testing/endpoint_search_test.go | 4 +- testing/errors_test.go | 4 +- testing/params_test.go | 4 +- testing/provider_client_test.go | 6 +-- testing/results_test.go | 4 +- testing/service_client_test.go | 4 +- testing/util_test.go | 4 +- 1394 files changed, 3904 insertions(+), 3904 deletions(-) diff --git a/auth_result.go b/auth_result.go index 2e4699b978..9a49cce8e5 100644 --- a/auth_result.go +++ b/auth_result.go @@ -12,9 +12,9 @@ The following types satisfy this interface: Usage example: import ( - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2" + tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" ) func GetAuthenticatedUserID(providerClient *gophercloud.ProviderClient) (string, error) { diff --git a/docs/contributor-tutorial/.template/requests.go b/docs/contributor-tutorial/.template/requests.go index 2431393d60..31d1737e8d 100644 --- a/docs/contributor-tutorial/.template/requests.go +++ b/docs/contributor-tutorial/.template/requests.go @@ -1,8 +1,8 @@ package RESOURCE import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/docs/contributor-tutorial/.template/results.go b/docs/contributor-tutorial/.template/results.go index 0ff0c10500..2434b641ee 100644 --- a/docs/contributor-tutorial/.template/results.go +++ b/docs/contributor-tutorial/.template/results.go @@ -1,8 +1,8 @@ package RESOURCE import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // RESOURCE represents... diff --git a/docs/contributor-tutorial/.template/testing/fixtures_test.go b/docs/contributor-tutorial/.template/testing/fixtures_test.go index 66e2a202a2..45664875c3 100644 --- a/docs/contributor-tutorial/.template/testing/fixtures_test.go +++ b/docs/contributor-tutorial/.template/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/service/vN/resources" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/service/vN/resources" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListResult provides a single page of RESOURCE results. diff --git a/docs/contributor-tutorial/.template/testing/requests_test.go b/docs/contributor-tutorial/.template/testing/requests_test.go index 22f97a2269..990c29146e 100644 --- a/docs/contributor-tutorial/.template/testing/requests_test.go +++ b/docs/contributor-tutorial/.template/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/service/vN/resources" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/service/vN/resources" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListResources(t *testing.T) { diff --git a/docs/contributor-tutorial/.template/urls.go b/docs/contributor-tutorial/.template/urls.go index 603b8124c9..46d88bcd26 100644 --- a/docs/contributor-tutorial/.template/urls.go +++ b/docs/contributor-tutorial/.template/urls.go @@ -1,6 +1,6 @@ package RESOURCE -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("resource") diff --git a/go.mod b/go.mod index b61904cace..1c0e9e8aac 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/gophercloud/gophercloud +module github.com/gophercloud/gophercloud/v2 go 1.21.6 diff --git a/internal/acceptance/clients/clients.go b/internal/acceptance/clients/clients.go index 3770f0060f..ea395f2b2a 100644 --- a/internal/acceptance/clients/clients.go +++ b/internal/acceptance/clients/clients.go @@ -9,11 +9,11 @@ import ( "os" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - baremetalHTTPBasic "github.com/gophercloud/gophercloud/openstack/baremetal/httpbasic" - baremetalNoAuth "github.com/gophercloud/gophercloud/openstack/baremetal/noauth" - blockstorageNoAuth "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" + baremetalHTTPBasic "github.com/gophercloud/gophercloud/v2/openstack/baremetal/httpbasic" + baremetalNoAuth "github.com/gophercloud/gophercloud/v2/openstack/baremetal/noauth" + blockstorageNoAuth "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/noauth" ) // AcceptanceTestChoices contains image and flavor selections for use by the acceptance tests. diff --git a/internal/acceptance/clients/testing/conditions_test.go b/internal/acceptance/clients/testing/conditions_test.go index 3fbe11260e..afa8beb6b8 100644 --- a/internal/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/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" ) func TestIsCurrentAbove(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go b/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go index 89acfb6a03..ea32644e7b 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go @@ -6,11 +6,11 @@ package httpbasic import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAllocationsCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go index 5acaac6ffd..68ff249c3a 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go @@ -3,12 +3,12 @@ package httpbasic import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNodesCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go index f69ba2744a..ae12b6d9c6 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go @@ -6,12 +6,12 @@ package httpbasic import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/ports" + "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPortsCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/noauth/allocations_test.go b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go index fe02a1fdb6..7ddf1fa4fd 100644 --- a/internal/acceptance/openstack/baremetal/noauth/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go @@ -6,11 +6,11 @@ package noauth import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAllocationsCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/noauth/nodes_test.go b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go index 3d2f36c6f7..f64548e080 100644 --- a/internal/acceptance/openstack/baremetal/noauth/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go @@ -3,12 +3,12 @@ package noauth import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNodesCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/noauth/ports_test.go b/internal/acceptance/openstack/baremetal/noauth/ports_test.go index bb59ec6ad4..ba4c6f8e57 100644 --- a/internal/acceptance/openstack/baremetal/noauth/ports_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/ports_test.go @@ -6,12 +6,12 @@ package noauth import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v1 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/baremetal/v1" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/ports" + "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPortsCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/v1/allocations_test.go b/internal/acceptance/openstack/baremetal/v1/allocations_test.go index c902f4219d..eafc5a6204 100644 --- a/internal/acceptance/openstack/baremetal/v1/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/v1/allocations_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAllocationsCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/v1/baremetal.go b/internal/acceptance/openstack/baremetal/v1/baremetal.go index 5849957fb8..5257d27ded 100644 --- a/internal/acceptance/openstack/baremetal/v1/baremetal.go +++ b/internal/acceptance/openstack/baremetal/v1/baremetal.go @@ -3,11 +3,11 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/ports" ) // CreateNode creates a basic node with a randomly generated name. diff --git a/internal/acceptance/openstack/baremetal/v1/conductors_test.go b/internal/acceptance/openstack/baremetal/v1/conductors_test.go index 4dc294c4b6..0b5e8d46b3 100644 --- a/internal/acceptance/openstack/baremetal/v1/conductors_test.go +++ b/internal/acceptance/openstack/baremetal/v1/conductors_test.go @@ -6,12 +6,12 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/conductors" + "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestConductorsListAndGet(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index 75d04f1aff..98fe95fed1 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -6,11 +6,11 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNodesCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/baremetal/v1/ports_test.go b/internal/acceptance/openstack/baremetal/v1/ports_test.go index 069a31028d..068c28d764 100644 --- a/internal/acceptance/openstack/baremetal/v1/ports_test.go +++ b/internal/acceptance/openstack/baremetal/v1/ports_test.go @@ -6,11 +6,11 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/ports" + "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPortsCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/apiversions_test.go b/internal/acceptance/openstack/blockstorage/apiversions_test.go index b9ff57b83f..3cf2ac717f 100644 --- a/internal/acceptance/openstack/blockstorage/apiversions_test.go +++ b/internal/acceptance/openstack/blockstorage/apiversions_test.go @@ -6,9 +6,9 @@ package blockstorage import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/apiversions" ) func TestAPIVersionsList(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/extensions/backups_test.go b/internal/acceptance/openstack/blockstorage/extensions/backups_test.go index 9492fbfb8a..fd9f32e1db 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/backups_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/backups_test.go @@ -6,11 +6,11 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" - blockstorage "github.com/gophercloud/gophercloud/internal/acceptance/openstack/blockstorage/v3" - th "github.com/gophercloud/gophercloud/testhelper" + blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v3" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestBackupsCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/extensions/extensions.go b/internal/acceptance/openstack/blockstorage/extensions/extensions.go index 849d3d1c7d..c606004f9f 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/internal/acceptance/openstack/blockstorage/extensions/extensions.go @@ -8,16 +8,16 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "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" - v3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" - "github.com/gophercloud/gophercloud/openstack/compute/v2/images" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumeactions" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + v3 "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be diff --git a/internal/acceptance/openstack/blockstorage/extensions/limits_test.go b/internal/acceptance/openstack/blockstorage/extensions/limits_test.go index 4ea356f657..efdea5e95e 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/limits_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/limits_test.go @@ -3,10 +3,10 @@ package extensions import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLimits(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go index 9864428324..6b08ea39dd 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -6,11 +6,11 @@ package extensions import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerhints" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSchedulerHints(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go index f30c4c6120..bc3124314b 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -6,10 +6,10 @@ package extensions import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerstats" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSchedulerStatsList(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/extensions/services_test.go b/internal/acceptance/openstack/blockstorage/extensions/services_test.go index 9f32132a54..c9c95aa387 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/services_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/services_test.go @@ -6,10 +6,10 @@ package extensions import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServicesList(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 673e968297..1427704583 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -6,13 +6,13 @@ package extensions import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v2" + blockstorageV3 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v3" + compute "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/compute/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumeActionsUploadImageDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go index 2571588537..82a7e1c238 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go @@ -6,11 +6,11 @@ package extensions import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v3" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumeTenants(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go index 6f5580714f..4caa5d5fd3 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -6,11 +6,11 @@ package noauth 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/blockstorage/v3/snapshots" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" ) // CreateVolume will create a volume with a random name and size of 1GB. An diff --git a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go index a12d1fa66b..3d1176cf49 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -6,9 +6,9 @@ package noauth import ( "testing" - "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/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" ) func TestSnapshotsList(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go index 5a66210a33..959a0b0286 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -6,9 +6,9 @@ package noauth import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" ) func TestVolumesList(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v1/blockstorage.go b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go index e380f80caf..67d6172363 100644 --- a/internal/acceptance/openstack/blockstorage/v1/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go @@ -6,11 +6,11 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/snapshots" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumetypes" ) // CreateSnapshot will create a volume snapshot based off of a given volume and diff --git a/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go index 8b02b35486..2e3d8f38b8 100644 --- a/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/snapshots" ) func TestSnapshotsList(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v1/volumes_test.go b/internal/acceptance/openstack/blockstorage/v1/volumes_test.go index d22701955c..fba2ddbf0f 100644 --- a/internal/acceptance/openstack/blockstorage/v1/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/volumes_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumesList(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go index f21fe5eae1..08399ac6b6 100644 --- a/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumetypes" ) func TestVolumeTypesList(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go index e3027abce9..c35c0fe605 100644 --- a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -6,12 +6,12 @@ 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/blockstorage/v2/snapshots" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/snapshots" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateSnapshot will create a snapshot of the specified volume. diff --git a/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go index ec8a2775d8..975d492ea1 100644 --- a/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/snapshots" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSnapshots(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go index 3ca33c1b6c..1674dcc40c 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -6,12 +6,12 @@ package v2 import ( "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumeactions" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/snapshots" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumesCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 9eae06ef24..55d21c5644 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -6,13 +6,13 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/qos" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateSnapshot will create a snapshot of the specified volume. diff --git a/internal/acceptance/openstack/blockstorage/v3/qos_test.go b/internal/acceptance/openstack/blockstorage/v3/qos_test.go index 85aa5b1908..3fca65ae35 100644 --- a/internal/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/qos_test.go @@ -3,11 +3,11 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/qos" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestQoS(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go index 1106f160da..e5e7cbd6a4 100644 --- a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -7,12 +7,12 @@ import ( "os" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestQuotasetGet(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go index 51cdd1f461..c16014ba1a 100644 --- a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -7,12 +7,12 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSnapshots(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go index 87eb0d92bd..134962654b 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go @@ -4,10 +4,10 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" - v3 "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/attachments" + v3 "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" ) // CreateVolumeAttachment will attach a volume to an instance. An error will be diff --git a/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index 5d2b939dc0..fd45869e77 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -6,10 +6,10 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + compute "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/compute/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumeAttachments(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go index 3bf405e3d5..9605c3a2a0 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -6,12 +6,12 @@ package v3 import ( "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumes(t *testing.T) { diff --git a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 84b5c0fd39..96a6b909a8 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -6,11 +6,11 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + identity "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumeTypes(t *testing.T) { diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index 366dd8b664..d28ce7041b 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -8,14 +8,14 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/credentials" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAuthenticatedClient(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/actions_test.go b/internal/acceptance/openstack/clustering/v1/actions_test.go index 9ec67640f4..c33ec1bd60 100644 --- a/internal/acceptance/openstack/clustering/v1/actions_test.go +++ b/internal/acceptance/openstack/clustering/v1/actions_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestActionsList(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/clustering.go b/internal/acceptance/openstack/clustering/v1/clustering.go index d256bf2790..326ce8f80d 100644 --- a/internal/acceptance/openstack/clustering/v1/clustering.go +++ b/internal/acceptance/openstack/clustering/v1/clustering.go @@ -6,16 +6,16 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/clusters" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) var TestPolicySpec = policies.Spec{ diff --git a/internal/acceptance/openstack/clustering/v1/clusters_test.go b/internal/acceptance/openstack/clustering/v1/clusters_test.go index 9d37267ae2..6b5a339a48 100644 --- a/internal/acceptance/openstack/clustering/v1/clusters_test.go +++ b/internal/acceptance/openstack/clustering/v1/clusters_test.go @@ -8,11 +8,11 @@ import ( "strings" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/clusters" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestClustersCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/events_test.go b/internal/acceptance/openstack/clustering/v1/events_test.go index cb849ad880..dcc7c5638e 100644 --- a/internal/acceptance/openstack/clustering/v1/events_test.go +++ b/internal/acceptance/openstack/clustering/v1/events_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/events" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestEventsList(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/nodes_test.go b/internal/acceptance/openstack/clustering/v1/nodes_test.go index f35c3d4a46..38a5b8af7e 100644 --- a/internal/acceptance/openstack/clustering/v1/nodes_test.go +++ b/internal/acceptance/openstack/clustering/v1/nodes_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNodesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/policies_test.go b/internal/acceptance/openstack/clustering/v1/policies_test.go index 9502b9a1af..97011c3e60 100644 --- a/internal/acceptance/openstack/clustering/v1/policies_test.go +++ b/internal/acceptance/openstack/clustering/v1/policies_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPoliciesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/policytypes_test.go b/internal/acceptance/openstack/clustering/v1/policytypes_test.go index fe930b9c57..c25a89dca5 100644 --- a/internal/acceptance/openstack/clustering/v1/policytypes_test.go +++ b/internal/acceptance/openstack/clustering/v1/policytypes_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policytypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPolicyTypeList(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/profiles_test.go b/internal/acceptance/openstack/clustering/v1/profiles_test.go index 0c6143df57..35bca43431 100644 --- a/internal/acceptance/openstack/clustering/v1/profiles_test.go +++ b/internal/acceptance/openstack/clustering/v1/profiles_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestProfilesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/profiletypes_test.go b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go index 2fe7c0301c..e10af5a96f 100644 --- a/internal/acceptance/openstack/clustering/v1/profiletypes_test.go +++ b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiletypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestProfileTypesList(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/receivers_test.go b/internal/acceptance/openstack/clustering/v1/receivers_test.go index 6c7149a489..1dea38fee5 100644 --- a/internal/acceptance/openstack/clustering/v1/receivers_test.go +++ b/internal/acceptance/openstack/clustering/v1/receivers_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestReceiversCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go index c481208711..371bc8349f 100644 --- a/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/webhooks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestClusteringWebhookTrigger(t *testing.T) { diff --git a/internal/acceptance/openstack/common.go b/internal/acceptance/openstack/common.go index ba78cb635d..28234a4703 100644 --- a/internal/acceptance/openstack/common.go +++ b/internal/acceptance/openstack/common.go @@ -5,7 +5,7 @@ package openstack import ( "testing" - "github.com/gophercloud/gophercloud/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" ) // PrintExtension prints an extension and all of its attributes. diff --git a/internal/acceptance/openstack/compute/v2/aggregates_test.go b/internal/acceptance/openstack/compute/v2/aggregates_test.go index 84e05dec52..6e6aa53ae0 100644 --- a/internal/acceptance/openstack/compute/v2/aggregates_test.go +++ b/internal/acceptance/openstack/compute/v2/aggregates_test.go @@ -8,12 +8,12 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAggregatesList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go index 3887634f1e..98f36f4bc6 100644 --- a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go +++ b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAttachDetachInterface(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go index 9d125589e6..09bf4efa2e 100644 --- a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAvailabilityZonesList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go index 2a054568ec..9901a24fee 100644 --- a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -6,12 +6,12 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/bootfromvolume" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/volumeattach" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestBootFromImage(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index f3d102916e..b5f9cb381b 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -8,29 +8,29 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" - dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/remoteconsoles" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" - "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - neutron "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/bootfromvolume" + dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/defsecrules" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/remoteconsoles" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/rescueunrescue" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/schedulerhints" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/volumeattach" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + neutron "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" "golang.org/x/crypto/ssh" ) diff --git a/internal/acceptance/openstack/compute/v2/defsecrules_test.go b/internal/acceptance/openstack/compute/v2/defsecrules_test.go index b2e5accad0..4fa4e3d4a1 100644 --- a/internal/acceptance/openstack/compute/v2/defsecrules_test.go +++ b/internal/acceptance/openstack/compute/v2/defsecrules_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/defsecrules" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestDefSecRulesList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/diagnostics_test.go b/internal/acceptance/openstack/compute/v2/diagnostics_test.go index 0424bb27a3..473b6fda61 100644 --- a/internal/acceptance/openstack/compute/v2/diagnostics_test.go +++ b/internal/acceptance/openstack/compute/v2/diagnostics_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diagnostics" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestDiagnostics(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/extension_test.go b/internal/acceptance/openstack/compute/v2/extension_test.go index 96b321a0c7..f51ec75910 100644 --- a/internal/acceptance/openstack/compute/v2/extension_test.go +++ b/internal/acceptance/openstack/compute/v2/extension_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestExtensionsList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/flavors_test.go b/internal/acceptance/openstack/compute/v2/flavors_test.go index f2dc2cda88..0643dfd64d 100644 --- a/internal/acceptance/openstack/compute/v2/flavors_test.go +++ b/internal/acceptance/openstack/compute/v2/flavors_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + identity "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestFlavorsList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/floatingip_test.go b/internal/acceptance/openstack/compute/v2/floatingip_test.go index 6e2ecb0399..a09476f72a 100644 --- a/internal/acceptance/openstack/compute/v2/floatingip_test.go +++ b/internal/acceptance/openstack/compute/v2/floatingip_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestFloatingIPsCreateDelete(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/hypervisors_test.go b/internal/acceptance/openstack/compute/v2/hypervisors_test.go index 4992a62814..0d870ebb72 100644 --- a/internal/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/internal/acceptance/openstack/compute/v2/hypervisors_test.go @@ -7,11 +7,11 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestHypervisorsList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/images_test.go b/internal/acceptance/openstack/compute/v2/images_test.go index 48dae1d974..e6f1bc51f7 100644 --- a/internal/acceptance/openstack/compute/v2/images_test.go +++ b/internal/acceptance/openstack/compute/v2/images_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestImagesList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/instance_actions_test.go b/internal/acceptance/openstack/compute/v2/instance_actions_test.go index 9928c2ceb8..25918e0ba3 100644 --- a/internal/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/internal/acceptance/openstack/compute/v2/instance_actions_test.go @@ -7,11 +7,11 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/instanceactions" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestInstanceActions(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/keypairs_test.go b/internal/acceptance/openstack/compute/v2/keypairs_test.go index 21e37bd802..4565fc5b2a 100644 --- a/internal/acceptance/openstack/compute/v2/keypairs_test.go +++ b/internal/acceptance/openstack/compute/v2/keypairs_test.go @@ -6,12 +6,12 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + identity "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" "golang.org/x/crypto/ssh" ) diff --git a/internal/acceptance/openstack/compute/v2/limits_test.go b/internal/acceptance/openstack/compute/v2/limits_test.go index c355ea0181..f960c16472 100644 --- a/internal/acceptance/openstack/compute/v2/limits_test.go +++ b/internal/acceptance/openstack/compute/v2/limits_test.go @@ -7,10 +7,10 @@ import ( "strings" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLimits(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/migrate_test.go b/internal/acceptance/openstack/compute/v2/migrate_test.go index 8edc44b980..bebbe9af06 100644 --- a/internal/acceptance/openstack/compute/v2/migrate_test.go +++ b/internal/acceptance/openstack/compute/v2/migrate_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/migrate" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestMigrate(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/network_test.go b/internal/acceptance/openstack/compute/v2/network_test.go index 0850c040b0..5ae1db18ba 100644 --- a/internal/acceptance/openstack/compute/v2/network_test.go +++ b/internal/acceptance/openstack/compute/v2/network_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNetworksList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/quotaset_test.go b/internal/acceptance/openstack/compute/v2/quotaset_test.go index 72868e1d25..01e7216e55 100644 --- a/internal/acceptance/openstack/compute/v2/quotaset_test.go +++ b/internal/acceptance/openstack/compute/v2/quotaset_test.go @@ -8,12 +8,12 @@ import ( "os" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestQuotasetGet(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go b/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go index f6fa7c8edb..55068d2ed2 100644 --- a/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go +++ b/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRemoteConsoleCreate(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go b/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go index 2d660a0b4f..8492197387 100644 --- a/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go +++ b/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServerRescueUnrescue(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/secgroup_test.go b/internal/acceptance/openstack/compute/v2/secgroup_test.go index aaa2595443..14eb40b026 100644 --- a/internal/acceptance/openstack/compute/v2/secgroup_test.go +++ b/internal/acceptance/openstack/compute/v2/secgroup_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSecGroupsList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/servergroup_test.go b/internal/acceptance/openstack/compute/v2/servergroup_test.go index 2ee3a1494c..63c1d56844 100644 --- a/internal/acceptance/openstack/compute/v2/servergroup_test.go +++ b/internal/acceptance/openstack/compute/v2/servergroup_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServergroupsCreateDelete(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 0f32a4ea17..08e182db14 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -7,21 +7,21 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedstatus" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/serverusage" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tags" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networks "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedserverattributes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedstatus" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/serverusage" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/suspendresume" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tags" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServersCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/services_test.go b/internal/acceptance/openstack/compute/v2/services_test.go index fbd8b760cf..3945f2029e 100644 --- a/internal/acceptance/openstack/compute/v2/services_test.go +++ b/internal/acceptance/openstack/compute/v2/services_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServicesList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go index 3700d5f38c..70f1a7a0f8 100644 --- a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go +++ b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTenantNetworksList(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/usage_test.go b/internal/acceptance/openstack/compute/v2/usage_test.go index 4ad35aa599..7d33e88ebb 100644 --- a/internal/acceptance/openstack/compute/v2/usage_test.go +++ b/internal/acceptance/openstack/compute/v2/usage_test.go @@ -8,11 +8,11 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/usage" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestUsageSingleTenant(t *testing.T) { diff --git a/internal/acceptance/openstack/compute/v2/volumeattach_test.go b/internal/acceptance/openstack/compute/v2/volumeattach_test.go index 11f5328e1f..53488cd5d1 100644 --- a/internal/acceptance/openstack/compute/v2/volumeattach_test.go +++ b/internal/acceptance/openstack/compute/v2/volumeattach_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + bs "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumeAttachAttachment(t *testing.T) { diff --git a/internal/acceptance/openstack/container/v1/capsules.go b/internal/acceptance/openstack/container/v1/capsules.go index 7b4cdcd095..a1819963da 100644 --- a/internal/acceptance/openstack/container/v1/capsules.go +++ b/internal/acceptance/openstack/container/v1/capsules.go @@ -3,9 +3,9 @@ package v1 import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/container/v1/capsules" ) // WaitForCapsuleStatus will poll a capsule's status until it either matches diff --git a/internal/acceptance/openstack/container/v1/capsules_test.go b/internal/acceptance/openstack/container/v1/capsules_test.go index 4ebda2bbfd..dfc6cd6c69 100644 --- a/internal/acceptance/openstack/container/v1/capsules_test.go +++ b/internal/acceptance/openstack/container/v1/capsules_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/container/v1/capsules" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCapsuleBase(t *testing.T) { diff --git a/internal/acceptance/openstack/containerinfra/v1/certificates_test.go b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go index b1d1911cb6..1cbd2528f6 100644 --- a/internal/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/certificates" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCertificatesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go index 1e2fbb6184..6a273f1081 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clusters" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestClustersCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index 67843c1c4c..68b6379e2f 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clustertemplates" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestClusterTemplatesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go index 28127195f0..2b461a2c5c 100644 --- a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -7,14 +7,14 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + idv3 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clusters" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clustertemplates" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/quotas" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateClusterTemplateCOE will create a random cluster template for the specified orchestration engine. diff --git a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go index acbde956a5..665451e58f 100644 --- a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -8,11 +8,11 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/nodegroups" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNodeGroupsCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/containerinfra/v1/quotas_test.go b/internal/acceptance/openstack/containerinfra/v1/quotas_test.go index 64169babca..17618b2e66 100644 --- a/internal/acceptance/openstack/containerinfra/v1/quotas_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/quotas_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestQuotasCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/db/v1/configurations_test.go b/internal/acceptance/openstack/db/v1/configurations_test.go index ae108aace3..05b47401d9 100644 --- a/internal/acceptance/openstack/db/v1/configurations_test.go +++ b/internal/acceptance/openstack/db/v1/configurations_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/configurations" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestConfigurationsCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/db/v1/databases_test.go b/internal/acceptance/openstack/db/v1/databases_test.go index f8ca7d2250..27690ae7b4 100644 --- a/internal/acceptance/openstack/db/v1/databases_test.go +++ b/internal/acceptance/openstack/db/v1/databases_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "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/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" ) // Because it takes so long to create an instance, diff --git a/internal/acceptance/openstack/db/v1/db.go b/internal/acceptance/openstack/db/v1/db.go index 1a1e39758e..3a9e5c84e5 100644 --- a/internal/acceptance/openstack/db/v1/db.go +++ b/internal/acceptance/openstack/db/v1/db.go @@ -6,12 +6,12 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/instances" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/users" ) // CreateDatabase will create a database with a randomly generated name. diff --git a/internal/acceptance/openstack/db/v1/flavors_test.go b/internal/acceptance/openstack/db/v1/flavors_test.go index 2183e60f7a..ea61629463 100644 --- a/internal/acceptance/openstack/db/v1/flavors_test.go +++ b/internal/acceptance/openstack/db/v1/flavors_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/db/v1/flavors" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/flavors" ) func TestFlavorsList(t *testing.T) { diff --git a/internal/acceptance/openstack/db/v1/instances_test.go b/internal/acceptance/openstack/db/v1/instances_test.go index 16a5d2d6df..9ee652f63a 100644 --- a/internal/acceptance/openstack/db/v1/instances_test.go +++ b/internal/acceptance/openstack/db/v1/instances_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/db/v1/instances" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/instances" ) // Because it takes so long to create an instance, diff --git a/internal/acceptance/openstack/db/v1/users_test.go b/internal/acceptance/openstack/db/v1/users_test.go index c9417c06e8..1ddce5b203 100644 --- a/internal/acceptance/openstack/db/v1/users_test.go +++ b/internal/acceptance/openstack/db/v1/users_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/db/v1/users" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/users" ) // Because it takes so long to create an instance, diff --git a/internal/acceptance/openstack/dns/v2/dns.go b/internal/acceptance/openstack/dns/v2/dns.go index d61588895f..9e083676ed 100644 --- a/internal/acceptance/openstack/dns/v2/dns.go +++ b/internal/acceptance/openstack/dns/v2/dns.go @@ -3,13 +3,13 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/recordsets" + transferAccepts "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/accept" + transferRequests "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/request" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/zones" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateRecordSet will create a RecordSet with a random name. An error will diff --git a/internal/acceptance/openstack/dns/v2/recordsets_test.go b/internal/acceptance/openstack/dns/v2/recordsets_test.go index 38aadcd6e8..e5a705e3c7 100644 --- a/internal/acceptance/openstack/dns/v2/recordsets_test.go +++ b/internal/acceptance/openstack/dns/v2/recordsets_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/recordsets" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRecordSetsListByZone(t *testing.T) { diff --git a/internal/acceptance/openstack/dns/v2/transfers_test.go b/internal/acceptance/openstack/dns/v2/transfers_test.go index 7cbcf4aa72..2282cf64b0 100644 --- a/internal/acceptance/openstack/dns/v2/transfers_test.go +++ b/internal/acceptance/openstack/dns/v2/transfers_test.go @@ -6,12 +6,12 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + identity "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/identity/v3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + transferAccepts "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/accept" + transferRequests "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/request" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTransferRequestCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/dns/v2/zones_test.go b/internal/acceptance/openstack/dns/v2/zones_test.go index b8edc55b10..5806fb7c3c 100644 --- a/internal/acceptance/openstack/dns/v2/zones_test.go +++ b/internal/acceptance/openstack/dns/v2/zones_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/zones" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestZonesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v2/extension_test.go b/internal/acceptance/openstack/identity/v2/extension_test.go index ed4202c372..7169e51e04 100644 --- a/internal/acceptance/openstack/identity/v2/extension_test.go +++ b/internal/acceptance/openstack/identity/v2/extension_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestExtensionsList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v2/identity.go b/internal/acceptance/openstack/identity/v2/identity.go index 6666e2e950..3500d06776 100644 --- a/internal/acceptance/openstack/identity/v2/identity.go +++ b/internal/acceptance/openstack/identity/v2/identity.go @@ -5,12 +5,12 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions/admin/roles" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/users" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // AddUserRole will grant a role to a user in a tenant. An error will be diff --git a/internal/acceptance/openstack/identity/v2/role_test.go b/internal/acceptance/openstack/identity/v2/role_test.go index 573209f4b8..bc47672b20 100644 --- a/internal/acceptance/openstack/identity/v2/role_test.go +++ b/internal/acceptance/openstack/identity/v2/role_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions/admin/roles" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/users" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRolesAddToUser(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v2/tenant_test.go b/internal/acceptance/openstack/identity/v2/tenant_test.go index 0b108f6733..697755b2ac 100644 --- a/internal/acceptance/openstack/identity/v2/tenant_test.go +++ b/internal/acceptance/openstack/identity/v2/tenant_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTenantsList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v2/token_test.go b/internal/acceptance/openstack/identity/v2/token_test.go index 085a3a5066..c858546588 100644 --- a/internal/acceptance/openstack/identity/v2/token_test.go +++ b/internal/acceptance/openstack/identity/v2/token_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTokenAuthenticate(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v2/user_test.go b/internal/acceptance/openstack/identity/v2/user_test.go index d014fc9698..549b6611e3 100644 --- a/internal/acceptance/openstack/identity/v2/user_test.go +++ b/internal/acceptance/openstack/identity/v2/user_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/users" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestUsersList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go index e2c5d6580d..1ed0a3beeb 100644 --- a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -7,12 +7,12 @@ import ( "testing" "time" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/applicationcredentials" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestApplicationCredentialsCRD(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/catalog_test.go b/internal/acceptance/openstack/identity/v3/catalog_test.go index c366b40793..c263bad8a4 100644 --- a/internal/acceptance/openstack/identity/v3/catalog_test.go +++ b/internal/acceptance/openstack/identity/v3/catalog_test.go @@ -3,10 +3,10 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/catalog" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCatalogList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/credentials_test.go b/internal/acceptance/openstack/identity/v3/credentials_test.go index a0a6d675e6..5a0fc9f1a0 100644 --- a/internal/acceptance/openstack/identity/v3/credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/credentials_test.go @@ -6,13 +6,13 @@ package v3 import ( "testing" - "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" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/credentials" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCredentialsCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/domains_test.go b/internal/acceptance/openstack/identity/v3/domains_test.go index 0a096ea7d9..ee09dcad3e 100644 --- a/internal/acceptance/openstack/identity/v3/domains_test.go +++ b/internal/acceptance/openstack/identity/v3/domains_test.go @@ -6,10 +6,10 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestDomainsListAvailable(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go index 36977de3e7..f28e487035 100644 --- a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -6,12 +6,12 @@ package v3 import ( "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2credentials" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestEC2CredentialsCRD(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go index f0dbd37737..c32c62cba2 100644 --- a/internal/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -7,12 +7,12 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/endpoints" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestEndpointsList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/federation_test.go b/internal/acceptance/openstack/identity/v3/federation_test.go index eb84beda31..19aa7d169a 100644 --- a/internal/acceptance/openstack/identity/v3/federation_test.go +++ b/internal/acceptance/openstack/identity/v3/federation_test.go @@ -6,11 +6,11 @@ package v3 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/identity/v3/extensions/federation" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/federation" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListMappings(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/groups_test.go b/internal/acceptance/openstack/identity/v3/groups_test.go index ee16600be4..30bec02620 100644 --- a/internal/acceptance/openstack/identity/v3/groups_test.go +++ b/internal/acceptance/openstack/identity/v3/groups_test.go @@ -6,10 +6,10 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGroupCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/identity.go b/internal/acceptance/openstack/identity/v3/identity.go index c4c5282280..78b1f87664 100644 --- a/internal/acceptance/openstack/identity/v3/identity.go +++ b/internal/acceptance/openstack/identity/v3/identity.go @@ -3,17 +3,17 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" - "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" - "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" - "github.com/gophercloud/gophercloud/openstack/identity/v3/services" - "github.com/gophercloud/gophercloud/openstack/identity/v3/users" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/regions" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/users" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateProject will create a project with a random name. diff --git a/internal/acceptance/openstack/identity/v3/limits_test.go b/internal/acceptance/openstack/identity/v3/limits_test.go index af36e3b420..7dfa9c22aa 100644 --- a/internal/acceptance/openstack/identity/v3/limits_test.go +++ b/internal/acceptance/openstack/identity/v3/limits_test.go @@ -7,12 +7,12 @@ import ( "os" "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/limits" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/registeredlimits" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGetEnforcementModel(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index 7270518478..efac3d778c 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -6,13 +6,13 @@ package v3 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" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/oauth1" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestOAuth1CRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/osinherit_test.go b/internal/acceptance/openstack/identity/v3/osinherit_test.go index b75171e08b..bed18b8f74 100644 --- a/internal/acceptance/openstack/identity/v3/osinherit_test.go +++ b/internal/acceptance/openstack/identity/v3/osinherit_test.go @@ -6,13 +6,13 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/osinherit" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestInheritRolesAssignToUserOnProject(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/policies_test.go b/internal/acceptance/openstack/identity/v3/policies_test.go index 39d43e08a9..5a62a8269b 100644 --- a/internal/acceptance/openstack/identity/v3/policies_test.go +++ b/internal/acceptance/openstack/identity/v3/policies_test.go @@ -6,10 +6,10 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPoliciesList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go index af4542d553..1de418da8a 100644 --- a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go @@ -6,11 +6,11 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/endpoints" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/projectendpoints" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestProjectEndpoints(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/projects_test.go b/internal/acceptance/openstack/identity/v3/projects_test.go index 08265e1595..cf1d535451 100644 --- a/internal/acceptance/openstack/identity/v3/projects_test.go +++ b/internal/acceptance/openstack/identity/v3/projects_test.go @@ -6,10 +6,10 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestProjectsListAvailable(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/reauth_test.go b/internal/acceptance/openstack/identity/v3/reauth_test.go index 0c1b74991d..ea8a13f72b 100644 --- a/internal/acceptance/openstack/identity/v3/reauth_test.go +++ b/internal/acceptance/openstack/identity/v3/reauth_test.go @@ -6,12 +6,12 @@ package v3 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack" + th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" ) func TestReauthAuthResultDeadlock(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/regions_test.go b/internal/acceptance/openstack/identity/v3/regions_test.go index 3b1173555d..711629bf39 100644 --- a/internal/acceptance/openstack/identity/v3/regions_test.go +++ b/internal/acceptance/openstack/identity/v3/regions_test.go @@ -6,10 +6,10 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/regions" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRegionsList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go index fa3f910559..b14a0fcd79 100644 --- a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go +++ b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go @@ -7,11 +7,11 @@ import ( "os" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/registeredlimits" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRegisteredLimitsCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index cd8f6c5523..1bf4fe868c 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -6,13 +6,13 @@ package v3 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/identity/v3/domains" - "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" - "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRolesList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/service_test.go b/internal/acceptance/openstack/identity/v3/service_test.go index 38eee8a4b9..43e9351428 100644 --- a/internal/acceptance/openstack/identity/v3/service_test.go +++ b/internal/acceptance/openstack/identity/v3/service_test.go @@ -6,10 +6,10 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServicesList(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/token_test.go b/internal/acceptance/openstack/identity/v3/token_test.go index e2d052d4d2..db480d2a4f 100644 --- a/internal/acceptance/openstack/identity/v3/token_test.go +++ b/internal/acceptance/openstack/identity/v3/token_test.go @@ -6,11 +6,11 @@ package v3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTokensGet(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/trusts_test.go b/internal/acceptance/openstack/identity/v3/trusts_test.go index 63b5c1afb7..749b4a8679 100644 --- a/internal/acceptance/openstack/identity/v3/trusts_test.go +++ b/internal/acceptance/openstack/identity/v3/trusts_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" - "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" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/openstack/identity/v3/users" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/users" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTrustCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/identity/v3/users_test.go b/internal/acceptance/openstack/identity/v3/users_test.go index 9379a20930..02f9740dec 100644 --- a/internal/acceptance/openstack/identity/v3/users_test.go +++ b/internal/acceptance/openstack/identity/v3/users_test.go @@ -6,12 +6,12 @@ package v3 import ( "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/users" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestUsersList(t *testing.T) { diff --git a/internal/acceptance/openstack/imageservice/v2/imagedata_test.go b/internal/acceptance/openstack/imageservice/v2/imagedata_test.go index d5c1c6f260..e6d43a1259 100644 --- a/internal/acceptance/openstack/imageservice/v2/imagedata_test.go +++ b/internal/acceptance/openstack/imageservice/v2/imagedata_test.go @@ -3,9 +3,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestImageStage(t *testing.T) { diff --git a/internal/acceptance/openstack/imageservice/v2/imageimport_test.go b/internal/acceptance/openstack/imageservice/v2/imageimport_test.go index 4d032dbfb3..a2b26f6832 100644 --- a/internal/acceptance/openstack/imageservice/v2/imageimport_test.go +++ b/internal/acceptance/openstack/imageservice/v2/imageimport_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGetImportInfo(t *testing.T) { diff --git a/internal/acceptance/openstack/imageservice/v2/images_test.go b/internal/acceptance/openstack/imageservice/v2/images_test.go index a904471c5d..d3c57407fd 100644 --- a/internal/acceptance/openstack/imageservice/v2/images_test.go +++ b/internal/acceptance/openstack/imageservice/v2/images_test.go @@ -8,11 +8,11 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/images" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestImagesListEachPage(t *testing.T) { diff --git a/internal/acceptance/openstack/imageservice/v2/imageservice.go b/internal/acceptance/openstack/imageservice/v2/imageservice.go index f093fe3123..37c21321bd 100644 --- a/internal/acceptance/openstack/imageservice/v2/imageservice.go +++ b/internal/acceptance/openstack/imageservice/v2/imageservice.go @@ -8,13 +8,13 @@ import ( "os" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/imagedata" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/imageimport" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/images" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/tasks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateEmptyImage will create an image, but with no actual image data. diff --git a/internal/acceptance/openstack/imageservice/v2/tasks_test.go b/internal/acceptance/openstack/imageservice/v2/tasks_test.go index e751118797..c53b37f094 100644 --- a/internal/acceptance/openstack/imageservice/v2/tasks_test.go +++ b/internal/acceptance/openstack/imageservice/v2/tasks_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTasksListEachPage(t *testing.T) { diff --git a/internal/acceptance/openstack/keymanager/v1/acls_test.go b/internal/acceptance/openstack/keymanager/v1/acls_test.go index 5638443078..5ba18a707b 100644 --- a/internal/acceptance/openstack/keymanager/v1/acls_test.go +++ b/internal/acceptance/openstack/keymanager/v1/acls_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/acls" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestACLCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/keymanager/v1/containers_test.go b/internal/acceptance/openstack/keymanager/v1/containers_test.go index cf7e162f8f..7b6395e9f6 100644 --- a/internal/acceptance/openstack/keymanager/v1/containers_test.go +++ b/internal/acceptance/openstack/keymanager/v1/containers_test.go @@ -6,11 +6,11 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/containers" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/secrets" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGenericContainersCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/keymanager/v1/keymanager.go b/internal/acceptance/openstack/keymanager/v1/keymanager.go index c0e9dd2728..a0bf19db66 100644 --- a/internal/acceptance/openstack/keymanager/v1/keymanager.go +++ b/internal/acceptance/openstack/keymanager/v1/keymanager.go @@ -14,12 +14,12 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/containers" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/orders" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/secrets" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateAsymmetric Order will create a random asymmetric order. diff --git a/internal/acceptance/openstack/keymanager/v1/orders_test.go b/internal/acceptance/openstack/keymanager/v1/orders_test.go index f300f8fa7e..29dc94efcd 100644 --- a/internal/acceptance/openstack/keymanager/v1/orders_test.go +++ b/internal/acceptance/openstack/keymanager/v1/orders_test.go @@ -6,12 +6,12 @@ package v1 import ( "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/containers" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/orders" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/secrets" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestOrdersCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/keymanager/v1/secrets_test.go b/internal/acceptance/openstack/keymanager/v1/secrets_test.go index 59c989bb2e..99355b6ad6 100644 --- a/internal/acceptance/openstack/keymanager/v1/secrets_test.go +++ b/internal/acceptance/openstack/keymanager/v1/secrets_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/secrets" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSecretsCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go b/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go index b2ec529e3f..4fa8cff9c3 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go @@ -6,9 +6,9 @@ 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/amphorae" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/amphorae" ) func TestAmphoraeList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go index bde7dbec77..a3c934cc94 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go @@ -6,11 +6,11 @@ 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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestFlavorProfilesList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go index f45b4c20a6..4597e107ce 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go @@ -6,10 +6,10 @@ 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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestFlavorsList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go index 66c6422d6e..ac61897e43 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -6,9 +6,9 @@ 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/l7policies" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" ) func TestL7PoliciesList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go b/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go index a760224ee8..bbabc35059 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go @@ -6,9 +6,9 @@ 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/listeners" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" ) func TestListenersList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index e4ca76c8d2..3be3c10f8b 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -5,17 +5,17 @@ import ( "strings" "testing" - "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" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/loadbalancers" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateListener will create a listener for a given load balancer on a random diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 9d33c74576..3c16c52783 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -6,16 +6,16 @@ package v2 import ( "testing" - "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" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/loadbalancers" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLoadbalancersList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go b/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go index 483b0cef51..352b7e7cca 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go @@ -6,9 +6,9 @@ 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/monitors" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" ) func TestMonitorsList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/pools_test.go b/internal/acceptance/openstack/loadbalancer/v2/pools_test.go index 9ae691f9e8..62469b63de 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/pools_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/pools_test.go @@ -6,9 +6,9 @@ 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/pools" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" ) func TestPoolsList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/providers_test.go b/internal/acceptance/openstack/loadbalancer/v2/providers_test.go index f71cb191c9..6326aa9ff6 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/providers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/providers_test.go @@ -6,9 +6,9 @@ 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/providers" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/providers" ) func TestProvidersList(t *testing.T) { diff --git a/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go index e88dca5b44..30e53974a7 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -9,11 +9,11 @@ import ( "reflect" "testing" - "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/quotas" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/quotas" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestQuotasGet(t *testing.T) { diff --git a/internal/acceptance/openstack/messaging/v2/claims_test.go b/internal/acceptance/openstack/messaging/v2/claims_test.go index d08ed61b2a..0c2f5aeaa5 100644 --- a/internal/acceptance/openstack/messaging/v2/claims_test.go +++ b/internal/acceptance/openstack/messaging/v2/claims_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/claims" ) func TestCRUDClaim(t *testing.T) { diff --git a/internal/acceptance/openstack/messaging/v2/message_test.go b/internal/acceptance/openstack/messaging/v2/message_test.go index bef7c4b94a..f3b67f9098 100644 --- a/internal/acceptance/openstack/messaging/v2/message_test.go +++ b/internal/acceptance/openstack/messaging/v2/message_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/messages" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListMessages(t *testing.T) { diff --git a/internal/acceptance/openstack/messaging/v2/messaging.go b/internal/acceptance/openstack/messaging/v2/messaging.go index 00513b7e24..afb7dbb147 100644 --- a/internal/acceptance/openstack/messaging/v2/messaging.go +++ b/internal/acceptance/openstack/messaging/v2/messaging.go @@ -4,12 +4,12 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/claims" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/messages" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/queues" + "github.com/gophercloud/gophercloud/v2/pagination" ) func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error) { diff --git a/internal/acceptance/openstack/messaging/v2/queue_test.go b/internal/acceptance/openstack/messaging/v2/queue_test.go index bdbc5885ec..478573f936 100644 --- a/internal/acceptance/openstack/messaging/v2/queue_test.go +++ b/internal/acceptance/openstack/messaging/v2/queue_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/queues" + "github.com/gophercloud/gophercloud/v2/pagination" ) func TestCRUDQueues(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/apiversion_test.go b/internal/acceptance/openstack/networking/v2/apiversion_test.go index ab89b438fd..ce28c3c606 100644 --- a/internal/acceptance/openstack/networking/v2/apiversion_test.go +++ b/internal/acceptance/openstack/networking/v2/apiversion_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/apiversions" ) func TestAPIVersionsList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extension_test.go b/internal/acceptance/openstack/networking/v2/extension_test.go index 06c6c632c1..d6d0212812 100644 --- a/internal/acceptance/openstack/networking/v2/extension_test.go +++ b/internal/acceptance/openstack/networking/v2/extension_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "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/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" ) func TestExtensionsList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index b9186e3bde..878a85e2d5 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -7,13 +7,13 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + spk "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/agents" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/speakers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAgentsRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go index f9843281a9..f9890fba93 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -8,13 +8,13 @@ import ( "sort" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/attributestags" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func createNetworkWithTags(t *testing.T, client *gophercloud.ServiceClient, tags []string) (network *networks.Network) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go index 28e790b901..caf750a954 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go @@ -3,10 +3,10 @@ package peers import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/peers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestBGPPeerCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go index b92a488edf..5dc21c3ebb 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go @@ -3,10 +3,10 @@ package peers import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/peers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func CreateBGPPeer(t *testing.T, client *gophercloud.ServiceClient) (*peers.BGPPeer, error) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go index 2f45251cf7..bd51df0cc2 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go @@ -3,13 +3,13 @@ package speakers import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + ap "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/bgp/peers" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/peers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/speakers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestBGPSpeakerCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go index 9a04eae2a9..4bb3750eb1 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go @@ -4,10 +4,10 @@ import ( "strconv" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/speakers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func CreateBGPSpeaker(t *testing.T, client *gophercloud.ServiceClient) (*speakers.BGPSpeaker, error) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go index 72fc944ecf..29ebcdf71e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go @@ -3,13 +3,13 @@ package dns import ( "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/dns" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // PortWithDNSExt represents a port with the DNS fields diff --git a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index 654112fe74..134b951b17 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -7,17 +7,17 @@ import ( "os" "testing" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/dns" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestDNSPortCRUDL(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/extensions.go b/internal/acceptance/openstack/networking/v2/extensions/extensions.go index 83f64bd6ad..9554888f99 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/internal/acceptance/openstack/networking/v2/extensions/extensions.go @@ -3,14 +3,14 @@ package extensions import ( "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/external" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateExternalNetwork will create an external network. An error will be diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go index 2a1079c8ed..74b7392813 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go @@ -6,12 +6,12 @@ package fwaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + layer3 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/routerinsertion" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestFirewallCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index 4169e84288..7c0a3abea4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -5,13 +5,13 @@ import ( "strconv" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/policies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/routerinsertion" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/rules" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateFirewall will create a Firewall with a random name and a specified diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go index 1a474e83cc..04b016ec2c 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go @@ -6,10 +6,10 @@ package fwaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPolicyCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go index 59a4ad19f5..6d0b693ee1 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go @@ -6,10 +6,10 @@ package fwaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/rules" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRuleCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go index d99106bf35..580d64cb9a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -5,12 +5,12 @@ import ( "strconv" "testing" - "github.com/gophercloud/gophercloud" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/groups" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/policies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/rules" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // RemoveRule will remove a rule from the policy. diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index c09f3ab130..0480252a5a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -6,10 +6,10 @@ package fwaas_v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/groups" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGroupCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go index 2a375f2584..b058d79ffc 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -6,10 +6,10 @@ package fwaas_v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPolicyCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go index c6eb6d2332..2e854eea56 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -8,10 +8,10 @@ import ( "strconv" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/rules" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRuleCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go index 6b4384422d..885e2b4995 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go @@ -6,10 +6,10 @@ package layer3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/addressscopes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAddressScopesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go index ccf1e545be..ef1eea42ac 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go @@ -8,12 +8,12 @@ import ( "net" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/extraroutes" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLayer3ExtraRoutesAddRemove(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index 93dec689fa..f4b0fdfb22 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -6,12 +6,12 @@ package layer3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLayer3FloatingIPsCreateDelete(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index 34d91ca29c..62c41ff30a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -6,13 +6,13 @@ package layer3 import ( "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/agents" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLayer3RouterScheduling(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 8e4f5e8590..c7ab126607 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -3,17 +3,17 @@ package layer3 import ( "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/addressscopes" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/portforwarding" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateFloatingIP creates a floating IP on a given network and port. An error diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index f5ae90b252..a857adedb8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -3,12 +3,12 @@ package layer3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/portforwarding" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLayer3PortForwardingsCreateDelete(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 91e5af616a..c832b0ac7e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -6,11 +6,11 @@ package layer3 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLayer3RouterCreateDelete(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go index 90b54ef253..b68ab01f77 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go @@ -4,12 +4,12 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/members" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/monitors" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/pools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/vips" ) // CreateMember will create a load balancer member in a specified pool on a diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go index 93cdfe4984..6f1caf5169 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go @@ -6,11 +6,11 @@ package lbaas import ( "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/members" ) func TestMembersList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go index e871065e6c..280e9bfbea 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go @@ -6,9 +6,9 @@ package lbaas import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/monitors" ) func TestMonitorsList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go index 4019b08ba3..30a7c43505 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go @@ -6,10 +6,10 @@ package lbaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/pools" ) func TestPoolsList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go index 3ae32d445e..b07d6d7a59 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go @@ -6,10 +6,10 @@ package lbaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/vips" ) func TestVIPsList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go index 4aa1fa9aea..9ca2b83155 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go @@ -6,9 +6,9 @@ package lbaas_v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" ) func TestL7PoliciesList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index 897d66c568..f2d0d27573 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -5,14 +5,14 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateListener will create a listener for a given load balancer on a random diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go index 2feb6d9ace..902bc1b41c 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go @@ -6,9 +6,9 @@ package lbaas_v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" ) func TestListenersList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go index 11ee1f8ce0..a65b392ca9 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -6,15 +6,15 @@ package lbaas_v2 import ( "testing" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestLoadbalancersList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go index 2450bfd687..ede7794a4a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go @@ -6,9 +6,9 @@ package lbaas_v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" ) func TestMonitorsList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go index efc2978cbd..ef8c8f0427 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go @@ -6,9 +6,9 @@ package lbaas_v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" ) func TestPoolsList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go index a02053f724..3ae7daa2b3 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go @@ -3,11 +3,11 @@ package mtu import ( "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/mtu" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) type NetworkMTU struct { diff --git a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 7b10c99434..76d26c8ee3 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -6,14 +6,14 @@ package mtu import ( "testing" - "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" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/mtu" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestMTUNetworkCRUDL(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go b/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go index e0bcebc1f7..f5237e5c6d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go @@ -6,10 +6,10 @@ package networkipavailabilities import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/networkipavailabilities" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNetworkIPAvailabilityList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index 607cf1cd9c..0f48dbcdb0 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -3,11 +3,11 @@ package portsbinding import ( "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsbinding" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // PortWithBindingExt represents a port with the binding fields 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 e4e851fe28..f3f9563b7f 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -6,12 +6,12 @@ package portsbinding import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsbinding" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPortsbindingCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/provider_test.go b/internal/acceptance/openstack/networking/v2/extensions/provider_test.go index 6b21141b9a..7abe50fc26 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/provider_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/provider_test.go @@ -6,11 +6,11 @@ package extensions import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNetworksProviderCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go index 91d4a398bc..854a70816a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go @@ -3,10 +3,10 @@ package policies import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateQoSPolicy will create a QoS policy. An error will be returned if the diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go index 4bda08b2d0..e994f2a567 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -6,11 +6,11 @@ package policies import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPoliciesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go index eb760670d2..78fec261ad 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go @@ -3,9 +3,9 @@ package rules import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/rules" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateBandwidthLimitRule will create a QoS BandwidthLimitRule associated with the provided QoS policy. diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go index 8c88e31b0f..c8e9f496dd 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -3,13 +3,13 @@ package rules import ( "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + accpolicies "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/rules" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestBandwidthLimitRulesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go index df89bff06e..505fec6fa4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -3,10 +3,10 @@ package ruletypes import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/ruletypes" ) func TestRuleTypes(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas.go b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas.go index 8b1a95a5c0..ea66d85a59 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas.go @@ -1,8 +1,8 @@ package quotas import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/quotas" ) var updateOpts = quotas.UpdateOpts{ diff --git a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go index 5749c90433..878bbedb47 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go @@ -9,10 +9,10 @@ import ( "reflect" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/quotas" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestQuotasGet(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go index b6e80d2420..6acd09547a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go +++ b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go @@ -3,9 +3,9 @@ package rbacpolicies import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/rbacpolicies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateRBACPolicy will create a rbac-policy. An error will be returned if the diff --git a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index 8015610a67..5ea473fe2d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -6,12 +6,12 @@ package rbacpolicies import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + projects "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/identity/v3" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/rbacpolicies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestRBACPolicyCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/security_test.go b/internal/acceptance/openstack/networking/v2/extensions/security_test.go index aaf772e7d5..6393761a85 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/security_test.go @@ -6,11 +6,11 @@ package extensions import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go index a7370cc3d5..7f2332b325 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go +++ b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go @@ -3,10 +3,10 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/subnetpools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateSubnetPool will create a subnetpool. An error will be returned if the diff --git a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index 12301d2b07..3f434a1ed8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/subnetpools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSubnetPoolsCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go index d2d5855540..e6726ad7f1 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -6,14 +6,14 @@ package trunk_details import ( "testing" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + v2Trunks "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunk_details" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) type portWithTrunkDetails struct { diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go index 215120dae8..f35940631c 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go @@ -3,9 +3,9 @@ package trunks import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" ) func CreateTrunk(t *testing.T, client *gophercloud.ServiceClient, parentPortID string, subportIDs ...string) (trunk *trunks.Trunk, err error) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index a41209c95c..5e2a7eeec2 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -7,13 +7,13 @@ import ( "sort" "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/attributestags" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTrunkCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go index e061ea3813..603a790a89 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go @@ -3,11 +3,11 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vlantransparent" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // VLANTransparentNetwork represents OpenStack V2 Networking Network with the diff --git a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go index c919866309..892ecd8dd7 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networkingv2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVLANTransparentCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index b65c356e81..fd5b8e5e48 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -6,10 +6,10 @@ package vpnaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/endpointgroups" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGroupList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index f203c2f42e..14d2032e1b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -6,10 +6,10 @@ package vpnaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/ikepolicies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestIKEPolicyList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index d36e1bff48..d5deb7f738 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -6,10 +6,10 @@ package vpnaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestIPSecPolicyList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index 17de845fa9..92cff63b6c 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -6,11 +6,11 @@ package vpnaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + layer3 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServiceList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index 9dab42c48c..5a4b8358e2 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -6,13 +6,13 @@ package vpnaas import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networks "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + layer3 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/siteconnections" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestConnectionList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index a165aa328a..c6db6ac425 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -3,14 +3,14 @@ package vpnaas import ( "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/endpointgroups" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/ikepolicies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/services" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/siteconnections" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateService will create a Service with a random name and a specified router ID diff --git a/internal/acceptance/openstack/networking/v2/networking.go b/internal/acceptance/openstack/networking/v2/networking.go index 2d3ce57e27..8501b70fb0 100644 --- a/internal/acceptance/openstack/networking/v2/networking.go +++ b/internal/acceptance/openstack/networking/v2/networking.go @@ -4,14 +4,14 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/extradhcpopts" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsecurity" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // PortWithExtraDHCPOpts represents a port with extra DHCP options configuration. diff --git a/internal/acceptance/openstack/networking/v2/networks_test.go b/internal/acceptance/openstack/networking/v2/networks_test.go index 1208e058c8..a1e70c3b08 100644 --- a/internal/acceptance/openstack/networking/v2/networks_test.go +++ b/internal/acceptance/openstack/networking/v2/networks_test.go @@ -7,12 +7,12 @@ import ( "strings" "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/external" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsecurity" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNetworksExternalList(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index eae54ee183..1cf7cecf4a 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -8,13 +8,13 @@ import ( "strings" "testing" - "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" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + extensions "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/extradhcpopts" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsecurity" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPortsCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/networking/v2/subnets_test.go b/internal/acceptance/openstack/networking/v2/subnets_test.go index e7f3b71ebb..bed5ae1382 100644 --- a/internal/acceptance/openstack/networking/v2/subnets_test.go +++ b/internal/acceptance/openstack/networking/v2/subnets_test.go @@ -8,11 +8,11 @@ import ( "strings" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + subnetpools "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/subnetpools" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSubnetCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/objectstorage/v1/accounts_test.go b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go index b6792ce9f5..36107ee9b7 100644 --- a/internal/acceptance/openstack/objectstorage/v1/accounts_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/accounts" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAccounts(t *testing.T) { diff --git a/internal/acceptance/openstack/objectstorage/v1/containers_test.go b/internal/acceptance/openstack/objectstorage/v1/containers_test.go index 48a51e634b..82000968e7 100644 --- a/internal/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/containers_test.go @@ -7,11 +7,11 @@ import ( "strings" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/containers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // numContainers is the number of containers to create for testing. diff --git a/internal/acceptance/openstack/objectstorage/v1/objects_test.go b/internal/acceptance/openstack/objectstorage/v1/objects_test.go index 44039ae719..450c81cd6a 100644 --- a/internal/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/objects_test.go @@ -11,11 +11,11 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/containers" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/objects" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // numObjects is the number of objects to create for testing. diff --git a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go index d992aee026..f817097ad2 100644 --- a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go @@ -7,11 +7,11 @@ import ( "strings" "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/containers" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/objects" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestObjectsVersioning(t *testing.T) { diff --git a/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go index 0bbea7be74..98c6e59fad 100644 --- a/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/buildinfo" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestBuildInfo(t *testing.T) { diff --git a/internal/acceptance/openstack/orchestration/v1/orchestration.go b/internal/acceptance/openstack/orchestration/v1/orchestration.go index 48c155505d..fde59c6aa0 100644 --- a/internal/acceptance/openstack/orchestration/v1/orchestration.go +++ b/internal/acceptance/openstack/orchestration/v1/orchestration.go @@ -4,10 +4,10 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) const basicTemplateResourceName = "secgroup_1" diff --git a/internal/acceptance/openstack/orchestration/v1/stackevents_test.go b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go index a5de54486d..0bd5d379cc 100644 --- a/internal/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -6,9 +6,9 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stackevents" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestStackEvents(t *testing.T) { diff --git a/internal/acceptance/openstack/orchestration/v1/stackresources_test.go b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go index 2033419a1c..9e62a486b0 100644 --- a/internal/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stackresources" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestStackResources(t *testing.T) { diff --git a/internal/acceptance/openstack/orchestration/v1/stacks_test.go b/internal/acceptance/openstack/orchestration/v1/stacks_test.go index 02e4d57299..f32245008c 100644 --- a/internal/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacks_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestStacksCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go index 738d3fda44..fa6aa931cb 100644 --- a/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -6,10 +6,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacktemplates" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestStackTemplatesCRUD(t *testing.T) { diff --git a/internal/acceptance/openstack/placement/v1/placement.go b/internal/acceptance/openstack/placement/v1/placement.go index 3fc3cf9397..344ab97320 100644 --- a/internal/acceptance/openstack/placement/v1/placement.go +++ b/internal/acceptance/openstack/placement/v1/placement.go @@ -3,10 +3,10 @@ package v1 import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func CreateResourceProvider(t *testing.T, client *gophercloud.ServiceClient) (*resourceproviders.ResourceProvider, error) { diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index 52c1140ff3..11413b7921 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -3,10 +3,10 @@ package v1 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestResourceProviderList(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go index acb85406a0..aec11d0af3 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go @@ -6,8 +6,8 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/availabilityzones" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/availabilityzones" ) func TestAvailabilityZonesList(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go index 3bc24e960d..65d7ca934a 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go @@ -3,8 +3,8 @@ package messages import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/messages" ) // DeleteMessage will delete a message. An error will occur if diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go index e3e92b0da0..d15209a007 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go @@ -3,9 +3,9 @@ package messages import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/messages" ) const requestID = "req-6f52cd8b-25a1-42cf-b497-7babf70f55f4" diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go index 3c08152250..5969ac3dd2 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go @@ -5,11 +5,11 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/replicas" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/replicas" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" ) // CreateReplica will create a replica from shareID. An error will be returned diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go index 1b54d79b60..dca0c273ef 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/replicas" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // 2.56 is required for a /v2/replicas/XXX URL support diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go index 2d51348cab..498bd450fa 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/schedulerstats" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSchedulerStatsList(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go index 47b93afb98..b5cff53af6 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go @@ -3,9 +3,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/securityservices" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/securityservices" ) // CreateSecurityService will create a security service with a random name. An diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index e578c52e3e..64aa957145 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/securityservices" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/securityservices" ) func TestSecurityServiceCreateDelete(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go index a0745ab795..0ccfe426b7 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServicesList(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go index 2659cc9834..8be4821cf9 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go @@ -4,10 +4,10 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shareaccessrules" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shareaccessrules" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" ) func ShareAccessRuleGet(t *testing.T, client *gophercloud.ServiceClient, accessID string) (*shareaccessrules.ShareAccess, error) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go index d5a279a923..c6a9626974 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestShareAccessRulesGet(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go index 3a269df8b5..4250ef5d91 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go @@ -3,10 +3,10 @@ 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/sharenetworks" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharenetworks" ) // CreateShareNetwork will create a share network with a random name. An diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index 2b75e52300..db683be42b 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharenetworks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestShareNetworkCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go index cce4f7feb1..fcb4b5d09d 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -5,11 +5,11 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/messages" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" ) // CreateShare will create a share with a name, and a size of 1Gb. An diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go index e05e1b090c..173ee30810 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestShareCreate(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go index e7678cf455..dcff28e2f9 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go @@ -4,9 +4,9 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetransfers" ) func CreateTransferRequest(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, name string) (*sharetransfers.Transfer, error) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go index 09a188682c..6853f23f50 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go @@ -6,11 +6,11 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetransfers" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetransfers" ) // minimal microversion for the share transfers diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go index 791f34a08b..54c48934dd 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go @@ -3,9 +3,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetypes" ) // CreateShareType will create a share type with a random name. An diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index bfaa8c2eda..466d830c8e 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -6,9 +6,9 @@ package v2 import ( "testing" - "github.com/gophercloud/gophercloud/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetypes" ) func TestShareTypeCreateDestroy(t *testing.T) { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go index 641b6440df..51a2daec55 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go @@ -5,9 +5,9 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/snapshots" ) // CreateSnapshot will create a snapshot from the share ID with a name. An error will diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index cb5a7f9b33..a84d652178 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -6,10 +6,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/snapshots" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // 2.7 is required for a /v2/snapshots/XXX/action URL support diff --git a/internal/acceptance/openstack/workflow/v2/crontrigger.go b/internal/acceptance/openstack/workflow/v2/crontrigger.go index cdd5045133..01a80bfd5c 100644 --- a/internal/acceptance/openstack/workflow/v2/crontrigger.go +++ b/internal/acceptance/openstack/workflow/v2/crontrigger.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/crontriggers" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/workflows" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateCronTrigger creates a cron trigger for the given workflow. diff --git a/internal/acceptance/openstack/workflow/v2/crontriggers_test.go b/internal/acceptance/openstack/workflow/v2/crontriggers_test.go index 97c0703771..d76f5902e8 100644 --- a/internal/acceptance/openstack/workflow/v2/crontriggers_test.go +++ b/internal/acceptance/openstack/workflow/v2/crontriggers_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/crontriggers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCronTriggersCreateGetDelete(t *testing.T) { diff --git a/internal/acceptance/openstack/workflow/v2/execution.go b/internal/acceptance/openstack/workflow/v2/execution.go index 359275e1da..a3d5168149 100644 --- a/internal/acceptance/openstack/workflow/v2/execution.go +++ b/internal/acceptance/openstack/workflow/v2/execution.go @@ -4,11 +4,11 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/executions" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/workflows" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateExecution creates an execution for the given workflow. diff --git a/internal/acceptance/openstack/workflow/v2/executions_test.go b/internal/acceptance/openstack/workflow/v2/executions_test.go index 098f3dadb2..708484ad66 100644 --- a/internal/acceptance/openstack/workflow/v2/executions_test.go +++ b/internal/acceptance/openstack/workflow/v2/executions_test.go @@ -3,10 +3,10 @@ package v2 import ( "testing" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/executions" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestExecutionsCreate(t *testing.T) { diff --git a/internal/acceptance/openstack/workflow/v2/workflow.go b/internal/acceptance/openstack/workflow/v2/workflow.go index b81b69902e..4fa16d5e43 100644 --- a/internal/acceptance/openstack/workflow/v2/workflow.go +++ b/internal/acceptance/openstack/workflow/v2/workflow.go @@ -5,10 +5,10 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/workflows" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // GetEchoWorkflowDefinition returns a simple workflow definition that does nothing except a simple "echo" command. diff --git a/internal/acceptance/openstack/workflow/v2/workflows_test.go b/internal/acceptance/openstack/workflow/v2/workflows_test.go index 163a52b40b..9ce86b4225 100644 --- a/internal/acceptance/openstack/workflow/v2/workflows_test.go +++ b/internal/acceptance/openstack/workflow/v2/workflows_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "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" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/workflows" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestWorkflowsCreateGetDelete(t *testing.T) { diff --git a/internal/ctxt/merge_test.go b/internal/ctxt/merge_test.go index c111ae7a09..a179bd661b 100644 --- a/internal/ctxt/merge_test.go +++ b/internal/ctxt/merge_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/internal/ctxt" + "github.com/gophercloud/gophercloud/v2/internal/ctxt" ) func TestMerge(t *testing.T) { diff --git a/openstack/auth_env.go b/openstack/auth_env.go index 7c6d06f0c3..fbabed0a4a 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -3,7 +3,7 @@ package openstack import ( "os" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) var nilOptions = gophercloud.AuthOptions{} diff --git a/openstack/baremetal/apiversions/requests.go b/openstack/baremetal/apiversions/requests.go index e9a610095a..cf096413a1 100644 --- a/openstack/baremetal/apiversions/requests.go +++ b/openstack/baremetal/apiversions/requests.go @@ -1,7 +1,7 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // List lists all the API versions available to end users. diff --git a/openstack/baremetal/apiversions/results.go b/openstack/baremetal/apiversions/results.go index 982758000d..30dad1b403 100644 --- a/openstack/baremetal/apiversions/results.go +++ b/openstack/baremetal/apiversions/results.go @@ -1,7 +1,7 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // APIVersions represents the result from getting a list of all versions available diff --git a/openstack/baremetal/apiversions/testing/fixtures_test.go b/openstack/baremetal/apiversions/testing/fixtures_test.go index 1b9b201717..5e169d36fc 100644 --- a/openstack/baremetal/apiversions/testing/fixtures_test.go +++ b/openstack/baremetal/apiversions/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const IronicAPIAllVersionResponse = ` diff --git a/openstack/baremetal/apiversions/testing/requests_test.go b/openstack/baremetal/apiversions/testing/requests_test.go index 5b2b3a6505..df2ec60f76 100644 --- a/openstack/baremetal/apiversions/testing/requests_test.go +++ b/openstack/baremetal/apiversions/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAPIVersions(t *testing.T) { diff --git a/openstack/baremetal/apiversions/urls.go b/openstack/baremetal/apiversions/urls.go index 5c101310ac..5a373b45cb 100644 --- a/openstack/baremetal/apiversions/urls.go +++ b/openstack/baremetal/apiversions/urls.go @@ -1,7 +1,7 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) func getURL(c *gophercloud.ServiceClient, version string) string { diff --git a/openstack/baremetal/httpbasic/requests.go b/openstack/baremetal/httpbasic/requests.go index c88c846470..3b13dd4637 100644 --- a/openstack/baremetal/httpbasic/requests.go +++ b/openstack/baremetal/httpbasic/requests.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // EndpointOpts specifies a "http_basic" Ironic Endpoint diff --git a/openstack/baremetal/httpbasic/testing/requests_test.go b/openstack/baremetal/httpbasic/testing/requests_test.go index c746f242d6..d8c9faf5ba 100644 --- a/openstack/baremetal/httpbasic/testing/requests_test.go +++ b/openstack/baremetal/httpbasic/testing/requests_test.go @@ -4,8 +4,8 @@ import ( "encoding/base64" "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/httpbasic" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/httpbasic" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestHttpBasic(t *testing.T) { diff --git a/openstack/baremetal/inventory/testing/fixtures.go b/openstack/baremetal/inventory/testing/fixtures.go index d2f93176cc..07acd9ada0 100644 --- a/openstack/baremetal/inventory/testing/fixtures.go +++ b/openstack/baremetal/inventory/testing/fixtures.go @@ -3,7 +3,7 @@ package testing import ( "fmt" - "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory" ) const InventorySample = `{ diff --git a/openstack/baremetal/inventory/testing/plugindata_test.go b/openstack/baremetal/inventory/testing/plugindata_test.go index eb1479502c..034ed05076 100644 --- a/openstack/baremetal/inventory/testing/plugindata_test.go +++ b/openstack/baremetal/inventory/testing/plugindata_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestExtraHardware(t *testing.T) { diff --git a/openstack/baremetal/inventory/testing/types_test.go b/openstack/baremetal/inventory/testing/types_test.go index f8a7572581..6142a8cb24 100644 --- a/openstack/baremetal/inventory/testing/types_test.go +++ b/openstack/baremetal/inventory/testing/types_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestInventory(t *testing.T) { diff --git a/openstack/baremetal/noauth/requests.go b/openstack/baremetal/noauth/requests.go index f5356ca52d..7ee3d479a4 100644 --- a/openstack/baremetal/noauth/requests.go +++ b/openstack/baremetal/noauth/requests.go @@ -3,7 +3,7 @@ package noauth import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // EndpointOpts specifies a "noauth" Ironic Endpoint. diff --git a/openstack/baremetal/noauth/testing/requests_test.go b/openstack/baremetal/noauth/testing/requests_test.go index 4d107b9ffe..702ea68758 100644 --- a/openstack/baremetal/noauth/testing/requests_test.go +++ b/openstack/baremetal/noauth/testing/requests_test.go @@ -3,8 +3,8 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/noauth" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/noauth" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNoAuth(t *testing.T) { diff --git a/openstack/baremetal/v1/allocations/requests.go b/openstack/baremetal/v1/allocations/requests.go index dfb04ba942..851c70d589 100644 --- a/openstack/baremetal/v1/allocations/requests.go +++ b/openstack/baremetal/v1/allocations/requests.go @@ -1,8 +1,8 @@ package allocations import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/baremetal/v1/allocations/results.go b/openstack/baremetal/v1/allocations/results.go index 6614ea1587..61fa2d88c3 100644 --- a/openstack/baremetal/v1/allocations/results.go +++ b/openstack/baremetal/v1/allocations/results.go @@ -3,8 +3,8 @@ package allocations import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type Allocation struct { diff --git a/openstack/baremetal/v1/allocations/testing/fixtures_test.go b/openstack/baremetal/v1/allocations/testing/fixtures_test.go index 340a7c5c17..a2b843e5b8 100644 --- a/openstack/baremetal/v1/allocations/testing/fixtures_test.go +++ b/openstack/baremetal/v1/allocations/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/allocations" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const AllocationListBody = ` diff --git a/openstack/baremetal/v1/allocations/testing/requests_test.go b/openstack/baremetal/v1/allocations/testing/requests_test.go index 6d7b228d08..9c04477be5 100644 --- a/openstack/baremetal/v1/allocations/testing/requests_test.go +++ b/openstack/baremetal/v1/allocations/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/allocations" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/allocations" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAllocations(t *testing.T) { diff --git a/openstack/baremetal/v1/allocations/urls.go b/openstack/baremetal/v1/allocations/urls.go index 7163bbe334..b6ea962ef7 100644 --- a/openstack/baremetal/v1/allocations/urls.go +++ b/openstack/baremetal/v1/allocations/urls.go @@ -1,6 +1,6 @@ package allocations -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("allocations") diff --git a/openstack/baremetal/v1/conductors/requests.go b/openstack/baremetal/v1/conductors/requests.go index f5bc63d63e..997585a8c2 100644 --- a/openstack/baremetal/v1/conductors/requests.go +++ b/openstack/baremetal/v1/conductors/requests.go @@ -3,8 +3,8 @@ package conductors import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/baremetal/v1/conductors/results.go b/openstack/baremetal/v1/conductors/results.go index 9dd4e963c3..831a5d99d9 100644 --- a/openstack/baremetal/v1/conductors/results.go +++ b/openstack/baremetal/v1/conductors/results.go @@ -3,8 +3,8 @@ package conductors import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type conductorResult struct { diff --git a/openstack/baremetal/v1/conductors/testing/fixtures_test.go b/openstack/baremetal/v1/conductors/testing/fixtures_test.go index 02e671aa28..e0b0c374e2 100644 --- a/openstack/baremetal/v1/conductors/testing/fixtures_test.go +++ b/openstack/baremetal/v1/conductors/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/conductors" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/conductors" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ConductorListBody contains the canned body of a conductor.List response, without detail. diff --git a/openstack/baremetal/v1/conductors/testing/requests_test.go b/openstack/baremetal/v1/conductors/testing/requests_test.go index b05495a5fd..8221a10919 100644 --- a/openstack/baremetal/v1/conductors/testing/requests_test.go +++ b/openstack/baremetal/v1/conductors/testing/requests_test.go @@ -3,10 +3,10 @@ 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" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/conductors" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListConductors(t *testing.T) { diff --git a/openstack/baremetal/v1/conductors/urls.go b/openstack/baremetal/v1/conductors/urls.go index a52e1e5ca5..ef29f52ab2 100644 --- a/openstack/baremetal/v1/conductors/urls.go +++ b/openstack/baremetal/v1/conductors/urls.go @@ -1,6 +1,6 @@ package conductors -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("conductors") diff --git a/openstack/baremetal/v1/drivers/requests.go b/openstack/baremetal/v1/drivers/requests.go index 988f64e635..989408534f 100644 --- a/openstack/baremetal/v1/drivers/requests.go +++ b/openstack/baremetal/v1/drivers/requests.go @@ -1,8 +1,8 @@ package drivers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListDriversOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/baremetal/v1/drivers/results.go b/openstack/baremetal/v1/drivers/results.go index ee1fff37bd..611257e854 100644 --- a/openstack/baremetal/v1/drivers/results.go +++ b/openstack/baremetal/v1/drivers/results.go @@ -1,8 +1,8 @@ package drivers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type driverResult struct { diff --git a/openstack/baremetal/v1/drivers/testing/fixtures_test.go b/openstack/baremetal/v1/drivers/testing/fixtures_test.go index e3e79ef97d..d29325d408 100644 --- a/openstack/baremetal/v1/drivers/testing/fixtures_test.go +++ b/openstack/baremetal/v1/drivers/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/drivers" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/drivers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListDriversBody contains the canned body of a drivers.ListDrivers response, without details. diff --git a/openstack/baremetal/v1/drivers/testing/requests_test.go b/openstack/baremetal/v1/drivers/testing/requests_test.go index 28e5365183..c53af5addc 100644 --- a/openstack/baremetal/v1/drivers/testing/requests_test.go +++ b/openstack/baremetal/v1/drivers/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/drivers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/drivers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListDrivers(t *testing.T) { diff --git a/openstack/baremetal/v1/drivers/urls.go b/openstack/baremetal/v1/drivers/urls.go index d5ddba7d89..bb85ea6f8d 100644 --- a/openstack/baremetal/v1/drivers/urls.go +++ b/openstack/baremetal/v1/drivers/urls.go @@ -1,6 +1,6 @@ package drivers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func driversURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("drivers") diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index d12a42023d..158eb25668 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -3,8 +3,8 @@ package nodes import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 622d8b694b..a7e02cba6a 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -5,10 +5,10 @@ import ( "fmt" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" - "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory" + "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection" + "github.com/gophercloud/gophercloud/v2/pagination" ) type nodeResult struct { diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index ce7b877b3e..301d75eed7 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - inventorytest "github.com/gophercloud/gophercloud/openstack/baremetal/inventory/testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + inventorytest "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory/testing" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // NodeListBody contains the canned body of a nodes.List response, without detail. diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 50397dd666..6fca0e87a3 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListDetailNodes(t *testing.T) { diff --git a/openstack/baremetal/v1/nodes/testing/results_test.go b/openstack/baremetal/v1/nodes/testing/results_test.go index 79617d4a71..7c683ebecb 100644 --- a/openstack/baremetal/v1/nodes/testing/results_test.go +++ b/openstack/baremetal/v1/nodes/testing/results_test.go @@ -3,12 +3,12 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" - invtest "github.com/gophercloud/gophercloud/openstack/baremetal/inventory/testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes" - "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" - insptest "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection/testing" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory" + invtest "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory/testing" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" + "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection" + insptest "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection/testing" + "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestStandardPluginData(t *testing.T) { diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index 1e509dd088..34390633c9 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -1,6 +1,6 @@ package nodes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("nodes") diff --git a/openstack/baremetal/v1/ports/requests.go b/openstack/baremetal/v1/ports/requests.go index ed191649d6..3812b0d3b1 100644 --- a/openstack/baremetal/v1/ports/requests.go +++ b/openstack/baremetal/v1/ports/requests.go @@ -3,8 +3,8 @@ package ports import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/baremetal/v1/ports/results.go b/openstack/baremetal/v1/ports/results.go index 226bd7ada4..d6c6ae22d7 100644 --- a/openstack/baremetal/v1/ports/results.go +++ b/openstack/baremetal/v1/ports/results.go @@ -3,8 +3,8 @@ package ports import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type portResult struct { diff --git a/openstack/baremetal/v1/ports/testing/fixtures_test.go b/openstack/baremetal/v1/ports/testing/fixtures_test.go index dafeed845b..2f8c041fc0 100644 --- a/openstack/baremetal/v1/ports/testing/fixtures_test.go +++ b/openstack/baremetal/v1/ports/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // PortListBody contains the canned body of a ports.List response, without detail. diff --git a/openstack/baremetal/v1/ports/testing/requests_test.go b/openstack/baremetal/v1/ports/testing/requests_test.go index cf9519b53d..6c04c8567e 100644 --- a/openstack/baremetal/v1/ports/testing/requests_test.go +++ b/openstack/baremetal/v1/ports/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/ports" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListDetailPorts(t *testing.T) { diff --git a/openstack/baremetal/v1/ports/urls.go b/openstack/baremetal/v1/ports/urls.go index 436c954150..51750d890a 100644 --- a/openstack/baremetal/v1/ports/urls.go +++ b/openstack/baremetal/v1/ports/urls.go @@ -1,6 +1,6 @@ package ports -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("ports") diff --git a/openstack/baremetalintrospection/httpbasic/requests.go b/openstack/baremetalintrospection/httpbasic/requests.go index 587eb39570..473e92c372 100644 --- a/openstack/baremetalintrospection/httpbasic/requests.go +++ b/openstack/baremetalintrospection/httpbasic/requests.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // EndpointOpts specifies a "http_basic" Ironic Inspector Endpoint. diff --git a/openstack/baremetalintrospection/httpbasic/testing/requests_test.go b/openstack/baremetalintrospection/httpbasic/testing/requests_test.go index 8fbe0c328c..df091e0cc9 100644 --- a/openstack/baremetalintrospection/httpbasic/testing/requests_test.go +++ b/openstack/baremetalintrospection/httpbasic/testing/requests_test.go @@ -4,8 +4,8 @@ import ( "encoding/base64" "testing" - "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/httpbasic" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/httpbasic" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNoAuth(t *testing.T) { diff --git a/openstack/baremetalintrospection/noauth/requests.go b/openstack/baremetalintrospection/noauth/requests.go index a528e1030c..7acd0abe75 100644 --- a/openstack/baremetalintrospection/noauth/requests.go +++ b/openstack/baremetalintrospection/noauth/requests.go @@ -3,7 +3,7 @@ package noauth import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // EndpointOpts specifies a "noauth" Ironic Inspector Endpoint. diff --git a/openstack/baremetalintrospection/noauth/testing/requests_test.go b/openstack/baremetalintrospection/noauth/testing/requests_test.go index 33f3d7e4ed..4743f4073b 100644 --- a/openstack/baremetalintrospection/noauth/testing/requests_test.go +++ b/openstack/baremetalintrospection/noauth/testing/requests_test.go @@ -3,8 +3,8 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/noauth" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/noauth" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNoAuth(t *testing.T) { diff --git a/openstack/baremetalintrospection/v1/introspection/requests.go b/openstack/baremetalintrospection/v1/introspection/requests.go index d44b4b8ad6..e7e1b39f08 100644 --- a/openstack/baremetalintrospection/v1/introspection/requests.go +++ b/openstack/baremetalintrospection/v1/introspection/requests.go @@ -1,8 +1,8 @@ package introspection import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListIntrospectionsOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 2643dff9ea..872a8dc30e 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -4,9 +4,9 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory" + "github.com/gophercloud/gophercloud/v2/pagination" ) type introspectionResult struct { diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index d60cb374cf..ae8c384028 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -6,12 +6,12 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/baremetal/inventory" - inventorytesting "github.com/gophercloud/gophercloud/openstack/baremetal/inventory/testing" - "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory" + inventorytesting "github.com/gophercloud/gophercloud/v2/openstack/baremetal/inventory/testing" + "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // IntrospectionListBody contains the canned body of a introspection.IntrospectionList response. diff --git a/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go b/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go index d27ee30a51..c67af04998 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListIntrospections(t *testing.T) { diff --git a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go index df59fa2d94..da75e49190 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/results_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/results_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/gophercloud/gophercloud/openstack/baremetalintrospection/v1/introspection" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestHostnameInInventory(t *testing.T) { diff --git a/openstack/baremetalintrospection/v1/introspection/urls.go b/openstack/baremetalintrospection/v1/introspection/urls.go index e480613749..a3230f4cd1 100644 --- a/openstack/baremetalintrospection/v1/introspection/urls.go +++ b/openstack/baremetalintrospection/v1/introspection/urls.go @@ -1,6 +1,6 @@ package introspection -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listIntrospectionsURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("introspection") diff --git a/openstack/blockstorage/apiversions/requests.go b/openstack/blockstorage/apiversions/requests.go index d2d3851d1f..5ae4756793 100644 --- a/openstack/blockstorage/apiversions/requests.go +++ b/openstack/blockstorage/apiversions/requests.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List lists all the Cinder API versions available to end-users. diff --git a/openstack/blockstorage/apiversions/results.go b/openstack/blockstorage/apiversions/results.go index 941dca1813..515bd11b61 100644 --- a/openstack/blockstorage/apiversions/results.go +++ b/openstack/blockstorage/apiversions/results.go @@ -3,7 +3,7 @@ package apiversions import ( "time" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/pagination" ) // APIVersion represents an API version for Cinder. diff --git a/openstack/blockstorage/apiversions/testing/fixtures_test.go b/openstack/blockstorage/apiversions/testing/fixtures_test.go index 3be1ee66b4..4a368e9da9 100644 --- a/openstack/blockstorage/apiversions/testing/fixtures_test.go +++ b/openstack/blockstorage/apiversions/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const APIListResponse = ` diff --git a/openstack/blockstorage/apiversions/testing/requests_test.go b/openstack/blockstorage/apiversions/testing/requests_test.go index ca9688243a..24bd28ca9f 100644 --- a/openstack/blockstorage/apiversions/testing/requests_test.go +++ b/openstack/blockstorage/apiversions/testing/requests_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListVersions(t *testing.T) { diff --git a/openstack/blockstorage/apiversions/urls.go b/openstack/blockstorage/apiversions/urls.go index a6a35d4225..deaf717651 100644 --- a/openstack/blockstorage/apiversions/urls.go +++ b/openstack/blockstorage/apiversions/urls.go @@ -3,8 +3,8 @@ package apiversions import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) func listURL(c *gophercloud.ServiceClient) string { diff --git a/openstack/blockstorage/extensions/availabilityzones/requests.go b/openstack/blockstorage/extensions/availabilityzones/requests.go index df10b856eb..15f9c228b2 100644 --- a/openstack/blockstorage/extensions/availabilityzones/requests.go +++ b/openstack/blockstorage/extensions/availabilityzones/requests.go @@ -1,8 +1,8 @@ package availabilityzones import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List will return the existing availability zones. diff --git a/openstack/blockstorage/extensions/availabilityzones/results.go b/openstack/blockstorage/extensions/availabilityzones/results.go index 0e115411c1..1e80451a36 100644 --- a/openstack/blockstorage/extensions/availabilityzones/results.go +++ b/openstack/blockstorage/extensions/availabilityzones/results.go @@ -1,7 +1,7 @@ package availabilityzones import ( - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ZoneState represents the current state of the availability zone. diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go b/openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go index 4b500e4843..2d7239347d 100644 --- a/openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go +++ b/openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - az "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/availabilityzones" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const GetOutput = ` diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go b/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go index 39f41bf09f..c722827ea5 100644 --- a/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go +++ b/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - az "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/availabilityzones" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // Verifies that availability zones can be listed correctly diff --git a/openstack/blockstorage/extensions/availabilityzones/urls.go b/openstack/blockstorage/extensions/availabilityzones/urls.go index fb4cdcf4e2..f78b7c8f97 100644 --- a/openstack/blockstorage/extensions/availabilityzones/urls.go +++ b/openstack/blockstorage/extensions/availabilityzones/urls.go @@ -1,6 +1,6 @@ package availabilityzones -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-availability-zone") diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/extensions/backups/requests.go index 5fb83227fe..2e201d7431 100644 --- a/openstack/blockstorage/extensions/backups/requests.go +++ b/openstack/blockstorage/extensions/backups/requests.go @@ -1,8 +1,8 @@ package backups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/extensions/backups/results.go b/openstack/blockstorage/extensions/backups/results.go index 4797373d76..79c4901c09 100644 --- a/openstack/blockstorage/extensions/backups/results.go +++ b/openstack/blockstorage/extensions/backups/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Backup contains all the information associated with a Cinder Backup. diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures_test.go b/openstack/blockstorage/extensions/backups/testing/fixtures_test.go index bb764b11f5..77cec05ed7 100644 --- a/openstack/blockstorage/extensions/backups/testing/fixtures_test.go +++ b/openstack/blockstorage/extensions/backups/testing/fixtures_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ListResponse = ` diff --git a/openstack/blockstorage/extensions/backups/testing/requests_test.go b/openstack/blockstorage/extensions/backups/testing/requests_test.go index fe71aca125..6fc405cb45 100644 --- a/openstack/blockstorage/extensions/backups/testing/requests_test.go +++ b/openstack/blockstorage/extensions/backups/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/backups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/blockstorage/extensions/backups/urls.go b/openstack/blockstorage/extensions/backups/urls.go index caebce82f9..9a96bb56bb 100644 --- a/openstack/blockstorage/extensions/backups/urls.go +++ b/openstack/blockstorage/extensions/backups/urls.go @@ -1,6 +1,6 @@ package backups -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("backups") diff --git a/openstack/blockstorage/extensions/limits/requests.go b/openstack/blockstorage/extensions/limits/requests.go index 21721f1bc8..0fe84b4e91 100644 --- a/openstack/blockstorage/extensions/limits/requests.go +++ b/openstack/blockstorage/extensions/limits/requests.go @@ -1,7 +1,7 @@ package limits import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Get returns the limits about the currently scoped tenant. diff --git a/openstack/blockstorage/extensions/limits/results.go b/openstack/blockstorage/extensions/limits/results.go index f0a74b7ec4..961f0ea696 100644 --- a/openstack/blockstorage/extensions/limits/results.go +++ b/openstack/blockstorage/extensions/limits/results.go @@ -1,7 +1,7 @@ package limits import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Limits is a struct that contains the response of a limit query. diff --git a/openstack/blockstorage/extensions/limits/testing/fixtures_test.go b/openstack/blockstorage/extensions/limits/testing/fixtures_test.go index 2df756eff3..ddb88797a1 100644 --- a/openstack/blockstorage/extensions/limits/testing/fixtures_test.go +++ b/openstack/blockstorage/extensions/limits/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // GetOutput is a sample response to a Get call. diff --git a/openstack/blockstorage/extensions/limits/testing/requests_test.go b/openstack/blockstorage/extensions/limits/testing/requests_test.go index b1a59673ca..1740048a77 100644 --- a/openstack/blockstorage/extensions/limits/testing/requests_test.go +++ b/openstack/blockstorage/extensions/limits/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/limits" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGet(t *testing.T) { diff --git a/openstack/blockstorage/extensions/limits/urls.go b/openstack/blockstorage/extensions/limits/urls.go index edd97e4e0e..ac5b0f2333 100644 --- a/openstack/blockstorage/extensions/limits/urls.go +++ b/openstack/blockstorage/extensions/limits/urls.go @@ -1,7 +1,7 @@ package limits import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) const resourcePath = "limits" diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go index 5cf28aee9e..be1b9734eb 100644 --- a/openstack/blockstorage/extensions/quotasets/requests.go +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -3,7 +3,7 @@ package quotasets import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Get returns public data about a previously created QuotaSet. diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/extensions/quotasets/results.go index 3a0a3e9293..0ee49821ce 100644 --- a/openstack/blockstorage/extensions/quotasets/results.go +++ b/openstack/blockstorage/extensions/quotasets/results.go @@ -3,8 +3,8 @@ package quotasets import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // QuotaSet is a set of operational limits that allow for control of block diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go b/openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go index 8c9f9318fe..51acf12d0e 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go +++ b/openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/quotasets" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const FirstTenantID = "555544443333222211110000ffffeeee" diff --git a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go index 922b8d336e..86bac291f3 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go +++ b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go @@ -4,9 +4,9 @@ import ( "errors" "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/quotasets" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/quotasets" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGet(t *testing.T) { diff --git a/openstack/blockstorage/extensions/quotasets/urls.go b/openstack/blockstorage/extensions/quotasets/urls.go index 7d8e5ceb7a..c3cc4b0c71 100644 --- a/openstack/blockstorage/extensions/quotasets/urls.go +++ b/openstack/blockstorage/extensions/quotasets/urls.go @@ -1,6 +1,6 @@ package quotasets -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-quota-sets" diff --git a/openstack/blockstorage/extensions/schedulerhints/requests.go b/openstack/blockstorage/extensions/schedulerhints/requests.go index 05b722da41..b01fd0b27f 100644 --- a/openstack/blockstorage/extensions/schedulerhints/requests.go +++ b/openstack/blockstorage/extensions/schedulerhints/requests.go @@ -3,7 +3,7 @@ package schedulerhints import ( "regexp" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // SchedulerHints represents a set of scheduling hints that are passed to the diff --git a/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go b/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go index 2ba27c7ef7..6759d41fd0 100644 --- a/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go +++ b/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerhints" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerhints" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreateOpts(t *testing.T) { diff --git a/openstack/blockstorage/extensions/schedulerstats/requests.go b/openstack/blockstorage/extensions/schedulerstats/requests.go index 7b374dcd86..629b42124d 100644 --- a/openstack/blockstorage/extensions/schedulerstats/requests.go +++ b/openstack/blockstorage/extensions/schedulerstats/requests.go @@ -1,8 +1,8 @@ package schedulerstats import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/extensions/schedulerstats/results.go b/openstack/blockstorage/extensions/schedulerstats/results.go index 9bf19a9fba..2170c483ed 100644 --- a/openstack/blockstorage/extensions/schedulerstats/results.go +++ b/openstack/blockstorage/extensions/schedulerstats/results.go @@ -5,7 +5,7 @@ import ( "math" "strconv" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Capabilities represents the information of an individual StoragePool. diff --git a/openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go index 3af0718a42..27c7d30a4f 100644 --- a/openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go +++ b/openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerstats" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const StoragePoolsListBody = ` diff --git a/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go b/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go index 8a4ef5180d..521cad03e2 100644 --- a/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go +++ b/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/schedulerstats" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerstats" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListStoragePoolsDetail(t *testing.T) { diff --git a/openstack/blockstorage/extensions/schedulerstats/urls.go b/openstack/blockstorage/extensions/schedulerstats/urls.go index c0ddb3695a..0ed58a490b 100644 --- a/openstack/blockstorage/extensions/schedulerstats/urls.go +++ b/openstack/blockstorage/extensions/schedulerstats/urls.go @@ -1,6 +1,6 @@ package schedulerstats -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func storagePoolsListURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("scheduler-stats", "get_pools") diff --git a/openstack/blockstorage/extensions/services/requests.go b/openstack/blockstorage/extensions/services/requests.go index 0edcfc9d7e..fea0927da0 100644 --- a/openstack/blockstorage/extensions/services/requests.go +++ b/openstack/blockstorage/extensions/services/requests.go @@ -1,8 +1,8 @@ package services import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the List diff --git a/openstack/blockstorage/extensions/services/results.go b/openstack/blockstorage/extensions/services/results.go index bf0160423d..1e2d10f624 100644 --- a/openstack/blockstorage/extensions/services/results.go +++ b/openstack/blockstorage/extensions/services/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Service represents a Blockstorage service in the OpenStack cloud. diff --git a/openstack/blockstorage/extensions/services/testing/fixtures_test.go b/openstack/blockstorage/extensions/services/testing/fixtures_test.go index 9d14723c12..94bd3da2b7 100644 --- a/openstack/blockstorage/extensions/services/testing/fixtures_test.go +++ b/openstack/blockstorage/extensions/services/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ServiceListBody is sample response to the List call diff --git a/openstack/blockstorage/extensions/services/testing/requests_test.go b/openstack/blockstorage/extensions/services/testing/requests_test.go index 4178c23699..c23b148080 100644 --- a/openstack/blockstorage/extensions/services/testing/requests_test.go +++ b/openstack/blockstorage/extensions/services/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/services" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/services" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListServices(t *testing.T) { diff --git a/openstack/blockstorage/extensions/services/urls.go b/openstack/blockstorage/extensions/services/urls.go index 61d794007e..e46d27ae6a 100644 --- a/openstack/blockstorage/extensions/services/urls.go +++ b/openstack/blockstorage/extensions/services/urls.go @@ -1,6 +1,6 @@ package services -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-services") diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index 03fb724a9f..34e400ec33 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -1,7 +1,7 @@ package volumeactions import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // AttachOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go index 34f64e18e8..b4fd0ea3ae 100644 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ b/openstack/blockstorage/extensions/volumeactions/results.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // AttachResult contains the response body and error from an Attach request. diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go index eef61d477a..3ba84cc0b8 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockAttachResponse(t *testing.T) { diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go index bb5d02e926..db3fd78d06 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumeactions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestAttach(t *testing.T) { diff --git a/openstack/blockstorage/extensions/volumeactions/urls.go b/openstack/blockstorage/extensions/volumeactions/urls.go index 20486ed719..dea10c5f5b 100644 --- a/openstack/blockstorage/extensions/volumeactions/urls.go +++ b/openstack/blockstorage/extensions/volumeactions/urls.go @@ -1,6 +1,6 @@ package volumeactions -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func actionURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("volumes", id, "action") diff --git a/openstack/blockstorage/extensions/volumetransfers/requests.go b/openstack/blockstorage/extensions/volumetransfers/requests.go index d32ac70f81..5686c58c70 100644 --- a/openstack/blockstorage/extensions/volumetransfers/requests.go +++ b/openstack/blockstorage/extensions/volumetransfers/requests.go @@ -1,8 +1,8 @@ package volumetransfers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOpts contains options for a Volume transfer. diff --git a/openstack/blockstorage/extensions/volumetransfers/results.go b/openstack/blockstorage/extensions/volumetransfers/results.go index 19b65c44cf..2eee0d2829 100644 --- a/openstack/blockstorage/extensions/volumetransfers/results.go +++ b/openstack/blockstorage/extensions/volumetransfers/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Transfer represents a Volume Transfer record diff --git a/openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go b/openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go index 9714323f9d..c094efd132 100644 --- a/openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go +++ b/openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetransfers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ListOutput = ` diff --git a/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go b/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go index 85dc359620..0e2e440df7 100644 --- a/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetransfers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetransfers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateTransfer(t *testing.T) { diff --git a/openstack/blockstorage/extensions/volumetransfers/urls.go b/openstack/blockstorage/extensions/volumetransfers/urls.go index cf06dc4940..91c5e749bf 100644 --- a/openstack/blockstorage/extensions/volumetransfers/urls.go +++ b/openstack/blockstorage/extensions/volumetransfers/urls.go @@ -1,6 +1,6 @@ package volumetransfers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func transferURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-volume-transfer") diff --git a/openstack/blockstorage/noauth/requests.go b/openstack/blockstorage/noauth/requests.go index 147fd8e609..87f2d9b7f9 100644 --- a/openstack/blockstorage/noauth/requests.go +++ b/openstack/blockstorage/noauth/requests.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // EndpointOpts specifies a "noauth" Cinder Endpoint. diff --git a/openstack/blockstorage/noauth/testing/requests_test.go b/openstack/blockstorage/noauth/testing/requests_test.go index 72f933a083..ee8a353efd 100644 --- a/openstack/blockstorage/noauth/testing/requests_test.go +++ b/openstack/blockstorage/noauth/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/noauth" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/noauth" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestNoAuth(t *testing.T) { diff --git a/openstack/blockstorage/v1/apiversions/requests.go b/openstack/blockstorage/v1/apiversions/requests.go index 18a5f28640..d60f8ab235 100644 --- a/openstack/blockstorage/v1/apiversions/requests.go +++ b/openstack/blockstorage/v1/apiversions/requests.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List lists all the Cinder API versions available to end-users. diff --git a/openstack/blockstorage/v1/apiversions/results.go b/openstack/blockstorage/v1/apiversions/results.go index 1e176d25b3..359231af13 100644 --- a/openstack/blockstorage/v1/apiversions/results.go +++ b/openstack/blockstorage/v1/apiversions/results.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // APIVersion represents an API version for Cinder. diff --git a/openstack/blockstorage/v1/apiversions/testing/fixtures_test.go b/openstack/blockstorage/v1/apiversions/testing/fixtures_test.go index 885fdf659a..02959c48f7 100644 --- a/openstack/blockstorage/v1/apiversions/testing/fixtures_test.go +++ b/openstack/blockstorage/v1/apiversions/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/blockstorage/v1/apiversions/testing/requests_test.go b/openstack/blockstorage/v1/apiversions/testing/requests_test.go index 31034970cf..a7bcc38cea 100644 --- a/openstack/blockstorage/v1/apiversions/testing/requests_test.go +++ b/openstack/blockstorage/v1/apiversions/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/apiversions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListVersions(t *testing.T) { diff --git a/openstack/blockstorage/v1/apiversions/urls.go b/openstack/blockstorage/v1/apiversions/urls.go index 41aebdc5f2..47f8116620 100644 --- a/openstack/blockstorage/v1/apiversions/urls.go +++ b/openstack/blockstorage/v1/apiversions/urls.go @@ -3,8 +3,8 @@ package apiversions import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go index 76da1ed631..adfa9d0693 100644 --- a/openstack/blockstorage/v1/snapshots/requests.go +++ b/openstack/blockstorage/v1/snapshots/requests.go @@ -1,8 +1,8 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v1/snapshots/results.go b/openstack/blockstorage/v1/snapshots/results.go index ca2445c805..dede83eef4 100644 --- a/openstack/blockstorage/v1/snapshots/results.go +++ b/openstack/blockstorage/v1/snapshots/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Snapshot contains all the information associated with an OpenStack Snapshot. diff --git a/openstack/blockstorage/v1/snapshots/testing/fixtures_test.go b/openstack/blockstorage/v1/snapshots/testing/fixtures_test.go index 21be6f90a0..f148d1e5c6 100644 --- a/openstack/blockstorage/v1/snapshots/testing/fixtures_test.go +++ b/openstack/blockstorage/v1/snapshots/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/blockstorage/v1/snapshots/testing/requests_test.go b/openstack/blockstorage/v1/snapshots/testing/requests_test.go index f4056b5b9f..3088bf002e 100644 --- a/openstack/blockstorage/v1/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v1/snapshots/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/snapshots" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/snapshots" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/blockstorage/v1/snapshots/urls.go b/openstack/blockstorage/v1/snapshots/urls.go index 7780437493..7cecf6de63 100644 --- a/openstack/blockstorage/v1/snapshots/urls.go +++ b/openstack/blockstorage/v1/snapshots/urls.go @@ -1,6 +1,6 @@ package snapshots -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots") diff --git a/openstack/blockstorage/v1/snapshots/util.go b/openstack/blockstorage/v1/snapshots/util.go index 40fbb827b8..e74ff98f31 100644 --- a/openstack/blockstorage/v1/snapshots/util.go +++ b/openstack/blockstorage/v1/snapshots/util.go @@ -1,7 +1,7 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go index f89886b767..f0dd9e5baf 100644 --- a/openstack/blockstorage/v1/volumes/requests.go +++ b/openstack/blockstorage/v1/volumes/requests.go @@ -1,8 +1,8 @@ package volumes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v1/volumes/results.go b/openstack/blockstorage/v1/volumes/results.go index a72028479b..13238eefd9 100644 --- a/openstack/blockstorage/v1/volumes/results.go +++ b/openstack/blockstorage/v1/volumes/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Volume contains all the information associated with an OpenStack Volume. diff --git a/openstack/blockstorage/v1/volumes/testing/fixtures_test.go b/openstack/blockstorage/v1/volumes/testing/fixtures_test.go index 306901b048..c149f4a7b3 100644 --- a/openstack/blockstorage/v1/volumes/testing/fixtures_test.go +++ b/openstack/blockstorage/v1/volumes/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/blockstorage/v1/volumes/testing/requests_test.go b/openstack/blockstorage/v1/volumes/testing/requests_test.go index 0ba830e038..9dc434f008 100644 --- a/openstack/blockstorage/v1/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v1/volumes/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/blockstorage/v1/volumes/urls.go b/openstack/blockstorage/v1/volumes/urls.go index 8a00f97e98..41f0cf75a1 100644 --- a/openstack/blockstorage/v1/volumes/urls.go +++ b/openstack/blockstorage/v1/volumes/urls.go @@ -1,6 +1,6 @@ package volumes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes") diff --git a/openstack/blockstorage/v1/volumes/util.go b/openstack/blockstorage/v1/volumes/util.go index e86c1b4b4e..f596575e52 100644 --- a/openstack/blockstorage/v1/volumes/util.go +++ b/openstack/blockstorage/v1/volumes/util.go @@ -1,7 +1,7 @@ package volumes import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular diff --git a/openstack/blockstorage/v1/volumetypes/requests.go b/openstack/blockstorage/v1/volumetypes/requests.go index 55489e6923..4343819607 100644 --- a/openstack/blockstorage/v1/volumetypes/requests.go +++ b/openstack/blockstorage/v1/volumetypes/requests.go @@ -1,8 +1,8 @@ package volumetypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v1/volumetypes/results.go b/openstack/blockstorage/v1/volumetypes/results.go index 66cffe3e1f..301f95ca2c 100644 --- a/openstack/blockstorage/v1/volumetypes/results.go +++ b/openstack/blockstorage/v1/volumetypes/results.go @@ -1,8 +1,8 @@ package volumetypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // VolumeType contains all information associated with an OpenStack Volume Type. diff --git a/openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go b/openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go index 0e2715a14f..93490ed24f 100644 --- a/openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go +++ b/openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/blockstorage/v1/volumetypes/testing/requests_test.go b/openstack/blockstorage/v1/volumetypes/testing/requests_test.go index 42446151b3..d43b6b7baa 100644 --- a/openstack/blockstorage/v1/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v1/volumetypes/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumetypes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumetypes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/blockstorage/v1/volumetypes/urls.go b/openstack/blockstorage/v1/volumetypes/urls.go index 822c7dd891..99f9ec0dc2 100644 --- a/openstack/blockstorage/v1/volumetypes/urls.go +++ b/openstack/blockstorage/v1/volumetypes/urls.go @@ -1,6 +1,6 @@ package volumetypes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types") diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go index c0e12139df..e6985fb296 100644 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ b/openstack/blockstorage/v2/snapshots/requests.go @@ -1,8 +1,8 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v2/snapshots/results.go b/openstack/blockstorage/v2/snapshots/results.go index 5450fc2876..7412ff3b3a 100644 --- a/openstack/blockstorage/v2/snapshots/results.go +++ b/openstack/blockstorage/v2/snapshots/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Snapshot contains all the information associated with a Cinder Snapshot. diff --git a/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go b/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go index 9638fa5007..2c00c8a05f 100644 --- a/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/blockstorage/v2/snapshots/testing/requests_test.go b/openstack/blockstorage/v2/snapshots/testing/requests_test.go index 1c44e52c7e..6faa40c699 100644 --- a/openstack/blockstorage/v2/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v2/snapshots/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/snapshots" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/snapshots" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/blockstorage/v2/snapshots/urls.go b/openstack/blockstorage/v2/snapshots/urls.go index 7780437493..7cecf6de63 100644 --- a/openstack/blockstorage/v2/snapshots/urls.go +++ b/openstack/blockstorage/v2/snapshots/urls.go @@ -1,6 +1,6 @@ package snapshots -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots") diff --git a/openstack/blockstorage/v2/snapshots/util.go b/openstack/blockstorage/v2/snapshots/util.go index 40fbb827b8..e74ff98f31 100644 --- a/openstack/blockstorage/v2/snapshots/util.go +++ b/openstack/blockstorage/v2/snapshots/util.go @@ -1,7 +1,7 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index e524d8210d..7551b465f6 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -1,8 +1,8 @@ package volumes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/blockstorage/v2/volumes/results.go index 4dd5d6c077..a8a48af7b3 100644 --- a/openstack/blockstorage/v2/volumes/results.go +++ b/openstack/blockstorage/v2/volumes/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type Attachment struct { diff --git a/openstack/blockstorage/v2/volumes/testing/fixtures_test.go b/openstack/blockstorage/v2/volumes/testing/fixtures_test.go index 44d2ca383c..369f1e7e1a 100644 --- a/openstack/blockstorage/v2/volumes/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/volumes/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 349f746a33..77dd3cadf4 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListWithExtensions(t *testing.T) { diff --git a/openstack/blockstorage/v2/volumes/urls.go b/openstack/blockstorage/v2/volumes/urls.go index 170724905a..b8704306c9 100644 --- a/openstack/blockstorage/v2/volumes/urls.go +++ b/openstack/blockstorage/v2/volumes/urls.go @@ -1,6 +1,6 @@ package volumes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes") diff --git a/openstack/blockstorage/v2/volumes/util.go b/openstack/blockstorage/v2/volumes/util.go index e86c1b4b4e..f596575e52 100644 --- a/openstack/blockstorage/v2/volumes/util.go +++ b/openstack/blockstorage/v2/volumes/util.go @@ -1,7 +1,7 @@ package volumes import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular diff --git a/openstack/blockstorage/v3/attachments/requests.go b/openstack/blockstorage/v3/attachments/requests.go index b6032a3b0d..eb5db3711a 100644 --- a/openstack/blockstorage/v3/attachments/requests.go +++ b/openstack/blockstorage/v3/attachments/requests.go @@ -1,8 +1,8 @@ package attachments import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v3/attachments/results.go b/openstack/blockstorage/v3/attachments/results.go index 791368ae78..04dfa54031 100644 --- a/openstack/blockstorage/v3/attachments/results.go +++ b/openstack/blockstorage/v3/attachments/results.go @@ -6,8 +6,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Attachment contains all the information associated with an OpenStack diff --git a/openstack/blockstorage/v3/attachments/testing/fixtures_test.go b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go index b19245e57a..a42caf8638 100644 --- a/openstack/blockstorage/v3/attachments/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/attachments" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) var ( diff --git a/openstack/blockstorage/v3/attachments/testing/requests_test.go b/openstack/blockstorage/v3/attachments/testing/requests_test.go index 2c9793100c..09bc916ab3 100644 --- a/openstack/blockstorage/v3/attachments/testing/requests_test.go +++ b/openstack/blockstorage/v3/attachments/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/attachments" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/attachments" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAll(t *testing.T) { diff --git a/openstack/blockstorage/v3/attachments/urls.go b/openstack/blockstorage/v3/attachments/urls.go index a774d7772f..df8fb5f7cd 100644 --- a/openstack/blockstorage/v3/attachments/urls.go +++ b/openstack/blockstorage/v3/attachments/urls.go @@ -1,6 +1,6 @@ package attachments -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("attachments") diff --git a/openstack/blockstorage/v3/attachments/util.go b/openstack/blockstorage/v3/attachments/util.go index dca1b888ec..b235ac0430 100644 --- a/openstack/blockstorage/v3/attachments/util.go +++ b/openstack/blockstorage/v3/attachments/util.go @@ -1,7 +1,7 @@ package attachments import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index 8169f82190..e9a02e2dda 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -1,8 +1,8 @@ package qos import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type CreateOptsBuilder interface { diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index d5f4254d2b..9efa30cc2e 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -1,8 +1,8 @@ package qos import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // QoS contains all the information associated with an OpenStack QoS specification. diff --git a/openstack/blockstorage/v3/qos/testing/fixtures_test.go b/openstack/blockstorage/v3/qos/testing/fixtures_test.go index d3bf00d69f..af3d02e01c 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/qos" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) var createQoSExpected = qos.QoS{ diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index e452fa2eee..3243b718a2 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "reflect" "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/qos" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/blockstorage/v3/qos/urls.go b/openstack/blockstorage/v3/qos/urls.go index e0e4a0eec8..956c276e78 100644 --- a/openstack/blockstorage/v3/qos/urls.go +++ b/openstack/blockstorage/v3/qos/urls.go @@ -1,6 +1,6 @@ package qos -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("qos-specs", id) diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index c5b5082413..6b8357bfe6 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -1,8 +1,8 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v3/snapshots/results.go b/openstack/blockstorage/v3/snapshots/results.go index 23c7a506d8..e3413f2811 100644 --- a/openstack/blockstorage/v3/snapshots/results.go +++ b/openstack/blockstorage/v3/snapshots/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Snapshot contains all the information associated with a Cinder Snapshot. diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go b/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go index be48c50048..eb4443b3af 100644 --- a/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // MockListResponse provides mock response for list snapshot API call diff --git a/openstack/blockstorage/v3/snapshots/testing/requests_test.go b/openstack/blockstorage/v3/snapshots/testing/requests_test.go index 862b25e010..1cc4e17a73 100644 --- a/openstack/blockstorage/v3/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/blockstorage/v3/snapshots/urls.go b/openstack/blockstorage/v3/snapshots/urls.go index 605b3cf5ec..b68cee9ccf 100644 --- a/openstack/blockstorage/v3/snapshots/urls.go +++ b/openstack/blockstorage/v3/snapshots/urls.go @@ -1,6 +1,6 @@ package snapshots -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots") diff --git a/openstack/blockstorage/v3/snapshots/util.go b/openstack/blockstorage/v3/snapshots/util.go index 40fbb827b8..e74ff98f31 100644 --- a/openstack/blockstorage/v3/snapshots/util.go +++ b/openstack/blockstorage/v3/snapshots/util.go @@ -1,7 +1,7 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 70ddb71dbc..a989039548 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -1,8 +1,8 @@ package volumes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index df97e66947..096a7bebe0 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Attachment represents a Volume Attachment record diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go index 339cb30c9c..c203ffc18d 100644 --- a/openstack/blockstorage/v3/volumes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index a4ecdae232..bb11d09ce1 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -4,12 +4,12 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumehost" - "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumetenants" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumehost" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListWithExtensions(t *testing.T) { diff --git a/openstack/blockstorage/v3/volumes/urls.go b/openstack/blockstorage/v3/volumes/urls.go index 170724905a..b8704306c9 100644 --- a/openstack/blockstorage/v3/volumes/urls.go +++ b/openstack/blockstorage/v3/volumes/urls.go @@ -1,6 +1,6 @@ package volumes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("volumes") diff --git a/openstack/blockstorage/v3/volumes/util.go b/openstack/blockstorage/v3/volumes/util.go index e86c1b4b4e..f596575e52 100644 --- a/openstack/blockstorage/v3/volumes/util.go +++ b/openstack/blockstorage/v3/volumes/util.go @@ -1,7 +1,7 @@ package volumes import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index e06f7a4638..8a9bd20a32 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -1,8 +1,8 @@ package volumetypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go index 4d1d1cf2df..93e6ffdd7a 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -1,8 +1,8 @@ package volumetypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // VolumeType contains all the information associated with an OpenStack Volume Type. diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go index a8cb0345cc..433a6d47a7 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 033158df55..846817208b 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "reflect" "testing" - "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAll(t *testing.T) { diff --git a/openstack/blockstorage/v3/volumetypes/urls.go b/openstack/blockstorage/v3/volumetypes/urls.go index c65478e684..8f0934bc4e 100644 --- a/openstack/blockstorage/v3/volumetypes/urls.go +++ b/openstack/blockstorage/v3/volumetypes/urls.go @@ -1,6 +1,6 @@ package volumetypes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types") diff --git a/openstack/cdn/v1/base/testing/fixtures_test.go b/openstack/cdn/v1/base/testing/fixtures_test.go index f1f4ac0047..137c7d4625 100644 --- a/openstack/cdn/v1/base/testing/fixtures_test.go +++ b/openstack/cdn/v1/base/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HandleGetSuccessfully creates an HTTP handler at `/` on the test handler mux diff --git a/openstack/cdn/v1/flavors/testing/fixtures_test.go b/openstack/cdn/v1/flavors/testing/fixtures_test.go index ed97247e2e..3c9b8b5e09 100644 --- a/openstack/cdn/v1/flavors/testing/fixtures_test.go +++ b/openstack/cdn/v1/flavors/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HandleListCDNFlavorsSuccessfully creates an HTTP handler at `/flavors` on the test handler mux diff --git a/openstack/cdn/v1/serviceassets/testing/fixtures_test.go b/openstack/cdn/v1/serviceassets/testing/fixtures_test.go index 3172d30fd1..daf3088264 100644 --- a/openstack/cdn/v1/serviceassets/testing/fixtures_test.go +++ b/openstack/cdn/v1/serviceassets/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HandleDeleteCDNAssetSuccessfully creates an HTTP handler at `/services/{id}/assets` on the test handler mux diff --git a/openstack/cdn/v1/services/testing/fixtures_test.go b/openstack/cdn/v1/services/testing/fixtures_test.go index d4093e0515..278f9080f8 100644 --- a/openstack/cdn/v1/services/testing/fixtures_test.go +++ b/openstack/cdn/v1/services/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HandleListCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux diff --git a/openstack/client.go b/openstack/client.go index 75722835cf..2aceb4e929 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -6,12 +6,12 @@ import ( "reflect" "strings" - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/oauth1" + tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) const ( diff --git a/openstack/clustering/v1/actions/requests.go b/openstack/clustering/v1/actions/requests.go index a9d5b9e046..efe3e3efd2 100644 --- a/openstack/clustering/v1/actions/requests.go +++ b/openstack/clustering/v1/actions/requests.go @@ -1,8 +1,8 @@ package actions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/clustering/v1/actions/results.go b/openstack/clustering/v1/actions/results.go index c73371bd56..59c1b8aa6a 100644 --- a/openstack/clustering/v1/actions/results.go +++ b/openstack/clustering/v1/actions/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Action represents a detailed Action. diff --git a/openstack/clustering/v1/actions/testing/fixtures_test.go b/openstack/clustering/v1/actions/testing/fixtures_test.go index 4709b7c516..8f178963cb 100644 --- a/openstack/clustering/v1/actions/testing/fixtures_test.go +++ b/openstack/clustering/v1/actions/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ListResponse = ` diff --git a/openstack/clustering/v1/actions/testing/requests_test.go b/openstack/clustering/v1/actions/testing/requests_test.go index 6a4e5f1592..bd75a24109 100644 --- a/openstack/clustering/v1/actions/testing/requests_test.go +++ b/openstack/clustering/v1/actions/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/actions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListActions(t *testing.T) { diff --git a/openstack/clustering/v1/actions/urls.go b/openstack/clustering/v1/actions/urls.go index 3288cc805e..25a739c41e 100644 --- a/openstack/clustering/v1/actions/urls.go +++ b/openstack/clustering/v1/actions/urls.go @@ -1,6 +1,6 @@ package actions -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" var apiVersion = "v1" var apiName = "actions" diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index 4ba7477924..bc5c479419 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -3,8 +3,8 @@ package clusters import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // AdjustmentType represents valid values for resizing a cluster. diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go index 23bc8ca1ae..f8a1b312fb 100644 --- a/openstack/clustering/v1/clusters/results.go +++ b/openstack/clustering/v1/clusters/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Cluster represents an OpenStack Clustering cluster. diff --git a/openstack/clustering/v1/clusters/testing/fixtures_test.go b/openstack/clustering/v1/clusters/testing/fixtures_test.go index 3d47671ef1..fe7847df1e 100644 --- a/openstack/clustering/v1/clusters/testing/fixtures_test.go +++ b/openstack/clustering/v1/clusters/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/clusters" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ClusterResponse = ` diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index 1d723a53ce..bc48af2f6d 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/clusters" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/clusters" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateCluster(t *testing.T) { diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go index ea1fa0d52c..44d05578a3 100644 --- a/openstack/clustering/v1/clusters/urls.go +++ b/openstack/clustering/v1/clusters/urls.go @@ -1,6 +1,6 @@ package clusters -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" var apiVersion = "v1" var apiName = "clusters" diff --git a/openstack/clustering/v1/events/requests.go b/openstack/clustering/v1/events/requests.go index 0db8d38863..720c20e2b7 100644 --- a/openstack/clustering/v1/events/requests.go +++ b/openstack/clustering/v1/events/requests.go @@ -1,8 +1,8 @@ package events import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/clustering/v1/events/results.go b/openstack/clustering/v1/events/results.go index abec9db307..c130fbf69b 100644 --- a/openstack/clustering/v1/events/results.go +++ b/openstack/clustering/v1/events/results.go @@ -3,8 +3,8 @@ package events import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Event represents a detailed Event. diff --git a/openstack/clustering/v1/events/testing/fixtures_test.go b/openstack/clustering/v1/events/testing/fixtures_test.go index 6e6b8fd33f..adfc75c07e 100644 --- a/openstack/clustering/v1/events/testing/fixtures_test.go +++ b/openstack/clustering/v1/events/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/events" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ListResponse = ` diff --git a/openstack/clustering/v1/events/testing/requests_test.go b/openstack/clustering/v1/events/testing/requests_test.go index e06d28f205..86bf6eed07 100644 --- a/openstack/clustering/v1/events/testing/requests_test.go +++ b/openstack/clustering/v1/events/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/events" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/events" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListEvents(t *testing.T) { diff --git a/openstack/clustering/v1/events/urls.go b/openstack/clustering/v1/events/urls.go index 65d9c1a8f4..a3807e1b55 100644 --- a/openstack/clustering/v1/events/urls.go +++ b/openstack/clustering/v1/events/urls.go @@ -1,6 +1,6 @@ package events -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" var apiVersion = "v1" var apiName = "events" diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index b4e9c0e9f0..f12d43e671 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -1,8 +1,8 @@ package nodes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go index fdee5d7258..196594ad6f 100644 --- a/openstack/clustering/v1/nodes/results.go +++ b/openstack/clustering/v1/nodes/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Node represents an OpenStack clustering node. diff --git a/openstack/clustering/v1/nodes/testing/fixtures_test.go b/openstack/clustering/v1/nodes/testing/fixtures_test.go index 1f972276f6..5f6a838aaf 100644 --- a/openstack/clustering/v1/nodes/testing/fixtures_test.go +++ b/openstack/clustering/v1/nodes/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const CreateResponse = `{ diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index 154bfb4729..f534485cae 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/nodes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateNode(t *testing.T) { diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go index 1224dcb066..05f695f573 100644 --- a/openstack/clustering/v1/nodes/urls.go +++ b/openstack/clustering/v1/nodes/urls.go @@ -1,6 +1,6 @@ package nodes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" var apiVersion = "v1" var apiName = "nodes" diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index 903a5534d6..77e799d092 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -1,8 +1,8 @@ package policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go index f8553b28ef..3942a2cb3c 100644 --- a/openstack/clustering/v1/policies/results.go +++ b/openstack/clustering/v1/policies/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Policy represents a clustering policy in the Openstack cloud. diff --git a/openstack/clustering/v1/policies/testing/fixtures_test.go b/openstack/clustering/v1/policies/testing/fixtures_test.go index 90a01fd7fe..9d1273737c 100644 --- a/openstack/clustering/v1/policies/testing/fixtures_test.go +++ b/openstack/clustering/v1/policies/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const PolicyListBody1 = ` diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go index 7bf3a527ee..e50d82f5cf 100644 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/policies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListPolicies(t *testing.T) { diff --git a/openstack/clustering/v1/policies/urls.go b/openstack/clustering/v1/policies/urls.go index d558ab0dcc..94b76f8617 100644 --- a/openstack/clustering/v1/policies/urls.go +++ b/openstack/clustering/v1/policies/urls.go @@ -1,6 +1,6 @@ package policies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( apiVersion = "v1" diff --git a/openstack/clustering/v1/policytypes/requests.go b/openstack/clustering/v1/policytypes/requests.go index ef8f84b39a..a51e6c7d6a 100644 --- a/openstack/clustering/v1/policytypes/requests.go +++ b/openstack/clustering/v1/policytypes/requests.go @@ -1,8 +1,8 @@ package policytypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List makes a request against the API to list policy types. diff --git a/openstack/clustering/v1/policytypes/results.go b/openstack/clustering/v1/policytypes/results.go index ce6fa75dfd..1045f2bb17 100644 --- a/openstack/clustering/v1/policytypes/results.go +++ b/openstack/clustering/v1/policytypes/results.go @@ -1,8 +1,8 @@ package policytypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // PolicyType represents a clustering policy type in the Openstack cloud. diff --git a/openstack/clustering/v1/policytypes/testing/fixtures_test.go b/openstack/clustering/v1/policytypes/testing/fixtures_test.go index c9492b6e75..05b15985b7 100644 --- a/openstack/clustering/v1/policytypes/testing/fixtures_test.go +++ b/openstack/clustering/v1/policytypes/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policytypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const FakePolicyTypetoGet = "fake-policytype" diff --git a/openstack/clustering/v1/policytypes/testing/requests_test.go b/openstack/clustering/v1/policytypes/testing/requests_test.go index 87e42fab1a..7d825e551f 100644 --- a/openstack/clustering/v1/policytypes/testing/requests_test.go +++ b/openstack/clustering/v1/policytypes/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/policytypes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policytypes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListPolicyTypes(t *testing.T) { diff --git a/openstack/clustering/v1/policytypes/urls.go b/openstack/clustering/v1/policytypes/urls.go index b291a95c70..4d1c420b8b 100644 --- a/openstack/clustering/v1/policytypes/urls.go +++ b/openstack/clustering/v1/policytypes/urls.go @@ -1,6 +1,6 @@ package policytypes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( apiVersion = "v1" diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index 759c0114bb..bb4b596c9f 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -1,8 +1,8 @@ package profiles import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go index a699aeb202..6cdd21f75f 100644 --- a/openstack/clustering/v1/profiles/results.go +++ b/openstack/clustering/v1/profiles/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Profile represent a detailed profile. diff --git a/openstack/clustering/v1/profiles/testing/fixtures_test.go b/openstack/clustering/v1/profiles/testing/fixtures_test.go index 590f69ed46..cd038c6a64 100644 --- a/openstack/clustering/v1/profiles/testing/fixtures_test.go +++ b/openstack/clustering/v1/profiles/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const CreateResponse = ` diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go index 8ddde9233c..b820bc3252 100644 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiles" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateProfile(t *testing.T) { diff --git a/openstack/clustering/v1/profiles/urls.go b/openstack/clustering/v1/profiles/urls.go index 5a3d56aa52..3cc1f2102e 100644 --- a/openstack/clustering/v1/profiles/urls.go +++ b/openstack/clustering/v1/profiles/urls.go @@ -1,6 +1,6 @@ package profiles -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" var apiVersion = "v1" var apiName = "profiles" diff --git a/openstack/clustering/v1/profiletypes/requests.go b/openstack/clustering/v1/profiletypes/requests.go index 2b944032ce..22db019ccc 100644 --- a/openstack/clustering/v1/profiletypes/requests.go +++ b/openstack/clustering/v1/profiletypes/requests.go @@ -1,8 +1,8 @@ package profiletypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { diff --git a/openstack/clustering/v1/profiletypes/results.go b/openstack/clustering/v1/profiletypes/results.go index b4b1c0bd52..fe0fd2d8b0 100644 --- a/openstack/clustering/v1/profiletypes/results.go +++ b/openstack/clustering/v1/profiletypes/results.go @@ -1,8 +1,8 @@ package profiletypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // commonResult is the response of a base result. diff --git a/openstack/clustering/v1/profiletypes/testing/fixtures_test.go b/openstack/clustering/v1/profiletypes/testing/fixtures_test.go index 64a112fff6..df18955840 100644 --- a/openstack/clustering/v1/profiletypes/testing/fixtures_test.go +++ b/openstack/clustering/v1/profiletypes/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/profiletypes" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiletypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ProfileTypeRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" diff --git a/openstack/clustering/v1/profiletypes/testing/requests_test.go b/openstack/clustering/v1/profiletypes/testing/requests_test.go index 3bac5ab349..8e7fa19e68 100644 --- a/openstack/clustering/v1/profiletypes/testing/requests_test.go +++ b/openstack/clustering/v1/profiletypes/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "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" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiletypes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListProfileTypes(t *testing.T) { diff --git a/openstack/clustering/v1/profiletypes/urls.go b/openstack/clustering/v1/profiletypes/urls.go index cec8bfe46a..9577fc3843 100644 --- a/openstack/clustering/v1/profiletypes/urls.go +++ b/openstack/clustering/v1/profiletypes/urls.go @@ -1,6 +1,6 @@ package profiletypes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( apiVersion = "v1" diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index 32f7366094..785c2663cf 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -1,8 +1,8 @@ package receivers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ReceiverType represents a valid type of receiver diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go index 4b3b064f98..3c6cb1590e 100644 --- a/openstack/clustering/v1/receivers/results.go +++ b/openstack/clustering/v1/receivers/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Receiver represent a detailed receiver diff --git a/openstack/clustering/v1/receivers/testing/fixtures_test.go b/openstack/clustering/v1/receivers/testing/fixtures_test.go index 8be67870bb..f69eee146e 100644 --- a/openstack/clustering/v1/receivers/testing/fixtures_test.go +++ b/openstack/clustering/v1/receivers/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const CreateResponse = ` diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go index d04ac24abf..9e3f261c9a 100644 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/receivers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateReceiver(t *testing.T) { diff --git a/openstack/clustering/v1/receivers/urls.go b/openstack/clustering/v1/receivers/urls.go index 62ad621b26..7b7021d619 100644 --- a/openstack/clustering/v1/receivers/urls.go +++ b/openstack/clustering/v1/receivers/urls.go @@ -1,6 +1,6 @@ package receivers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" var apiVersion = "v1" var apiName = "receivers" diff --git a/openstack/clustering/v1/webhooks/requests.go b/openstack/clustering/v1/webhooks/requests.go index 12371366f9..36048df535 100644 --- a/openstack/clustering/v1/webhooks/requests.go +++ b/openstack/clustering/v1/webhooks/requests.go @@ -4,7 +4,7 @@ import ( "fmt" "net/url" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // TriggerOpts represents options used for triggering an action diff --git a/openstack/clustering/v1/webhooks/results.go b/openstack/clustering/v1/webhooks/results.go index ccb06086a2..47f3579970 100644 --- a/openstack/clustering/v1/webhooks/results.go +++ b/openstack/clustering/v1/webhooks/results.go @@ -1,7 +1,7 @@ package webhooks import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type commonResult struct { diff --git a/openstack/clustering/v1/webhooks/testing/requests_test.go b/openstack/clustering/v1/webhooks/testing/requests_test.go index 4a519fdf6a..24bf532ec1 100644 --- a/openstack/clustering/v1/webhooks/testing/requests_test.go +++ b/openstack/clustering/v1/webhooks/testing/requests_test.go @@ -6,9 +6,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/clustering/v1/webhooks" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/webhooks" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestWebhookTrigger(t *testing.T) { diff --git a/openstack/clustering/v1/webhooks/urls.go b/openstack/clustering/v1/webhooks/urls.go index 563cf81122..52c2a7deb8 100644 --- a/openstack/clustering/v1/webhooks/urls.go +++ b/openstack/clustering/v1/webhooks/urls.go @@ -1,6 +1,6 @@ package webhooks -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func triggerURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("v1", "webhooks", id, "trigger") diff --git a/openstack/common/extensions/requests.go b/openstack/common/extensions/requests.go index bb301c7a12..69c84dde99 100755 --- a/openstack/common/extensions/requests.go +++ b/openstack/common/extensions/requests.go @@ -1,8 +1,8 @@ package extensions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Get retrieves information for a specific extension using its alias. diff --git a/openstack/common/extensions/results.go b/openstack/common/extensions/results.go index 609d132e0a..b845091383 100644 --- a/openstack/common/extensions/results.go +++ b/openstack/common/extensions/results.go @@ -1,8 +1,8 @@ package extensions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // GetResult temporarily stores the result of a Get call. diff --git a/openstack/common/extensions/testing/fixtures.go b/openstack/common/extensions/testing/fixtures.go index a986c950a2..9b8c21e4da 100644 --- a/openstack/common/extensions/testing/fixtures.go +++ b/openstack/common/extensions/testing/fixtures.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/common/extensions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of Extension results. diff --git a/openstack/common/extensions/testing/requests_test.go b/openstack/common/extensions/testing/requests_test.go index fbaedfa0be..7f35678758 100644 --- a/openstack/common/extensions/testing/requests_test.go +++ b/openstack/common/extensions/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/common/extensions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/common/extensions/urls.go b/openstack/common/extensions/urls.go index eaf38b2d19..8eb5f53725 100644 --- a/openstack/common/extensions/urls.go +++ b/openstack/common/extensions/urls.go @@ -1,6 +1,6 @@ package extensions -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // ExtensionURL generates the URL for an extension resource by name. func ExtensionURL(c *gophercloud.ServiceClient, name string) string { diff --git a/openstack/compute/apiversions/requests.go b/openstack/compute/apiversions/requests.go index 244a7acba4..a82c605c10 100644 --- a/openstack/compute/apiversions/requests.go +++ b/openstack/compute/apiversions/requests.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List lists all the API versions available to end-users. diff --git a/openstack/compute/apiversions/results.go b/openstack/compute/apiversions/results.go index 3718172463..67bee23f53 100644 --- a/openstack/compute/apiversions/results.go +++ b/openstack/compute/apiversions/results.go @@ -3,8 +3,8 @@ package apiversions import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // APIVersion represents an API version for the Nova service. diff --git a/openstack/compute/apiversions/testing/fixtures_test.go b/openstack/compute/apiversions/testing/fixtures_test.go index 46dd650f8c..c7f4b1a977 100644 --- a/openstack/compute/apiversions/testing/fixtures_test.go +++ b/openstack/compute/apiversions/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/compute/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const NovaAPIVersionResponse_20 = ` diff --git a/openstack/compute/apiversions/testing/requests_test.go b/openstack/compute/apiversions/testing/requests_test.go index ebb75a47fe..86d0deb0a0 100644 --- a/openstack/compute/apiversions/testing/requests_test.go +++ b/openstack/compute/apiversions/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAPIVersions(t *testing.T) { diff --git a/openstack/compute/apiversions/urls.go b/openstack/compute/apiversions/urls.go index 41aebdc5f2..47f8116620 100644 --- a/openstack/compute/apiversions/urls.go +++ b/openstack/compute/apiversions/urls.go @@ -3,8 +3,8 @@ package apiversions import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index 5268ee3ad0..14779e2c05 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -3,8 +3,8 @@ package aggregates import ( "strconv" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List makes a request against the API to list aggregates. diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/extensions/aggregates/results.go index 96a7b740c5..0d4794acff 100644 --- a/openstack/compute/v2/extensions/aggregates/results.go +++ b/openstack/compute/v2/extensions/aggregates/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Aggregate represents a host aggregate in the OpenStack cloud. diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go b/openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go index 9ae71d2230..8bd4387bc5 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go @@ -7,9 +7,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // AggregateListBody is sample response to the List call diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index bfd18614cc..4bf950eb10 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAggregates(t *testing.T) { diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/extensions/aggregates/urls.go index bb30c7fc90..ab33213cc6 100644 --- a/openstack/compute/v2/extensions/aggregates/urls.go +++ b/openstack/compute/v2/extensions/aggregates/urls.go @@ -1,6 +1,6 @@ package aggregates -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func aggregatesListURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-aggregates") diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go index 9748ce4ac1..59c07a30cc 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/requests.go +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -1,8 +1,8 @@ package attachinterfaces import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List makes a request against the nova API to list the server's interfaces. diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/extensions/attachinterfaces/results.go index e713c34e9b..6895be69d1 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/results.go +++ b/openstack/compute/v2/extensions/attachinterfaces/results.go @@ -1,8 +1,8 @@ package attachinterfaces import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type attachInterfaceResult struct { diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go index e701a93fc6..43a3c971ff 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListInterfacesExpected represents an expected repsonse from a ListInterfaces request. diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go index 1ffbd61e75..78cfcf6e01 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListInterface(t *testing.T) { diff --git a/openstack/compute/v2/extensions/attachinterfaces/urls.go b/openstack/compute/v2/extensions/attachinterfaces/urls.go index 50292e8b5a..98f8271d2a 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/urls.go +++ b/openstack/compute/v2/extensions/attachinterfaces/urls.go @@ -1,6 +1,6 @@ package attachinterfaces -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listInterfaceURL(client *gophercloud.ServiceClient, serverID string) string { return client.ServiceURL("servers", serverID, "os-interface") diff --git a/openstack/compute/v2/extensions/availabilityzones/requests.go b/openstack/compute/v2/extensions/availabilityzones/requests.go index f9a2e86e03..319e61978a 100644 --- a/openstack/compute/v2/extensions/availabilityzones/requests.go +++ b/openstack/compute/v2/extensions/availabilityzones/requests.go @@ -1,8 +1,8 @@ package availabilityzones import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List will return the existing availability zones. diff --git a/openstack/compute/v2/extensions/availabilityzones/results.go b/openstack/compute/v2/extensions/availabilityzones/results.go index d48a0ea858..ce7f039b77 100644 --- a/openstack/compute/v2/extensions/availabilityzones/results.go +++ b/openstack/compute/v2/extensions/availabilityzones/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ServerAvailabilityZoneExt is an extension to the base Server object. diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go b/openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go index 9cc6d46379..95c90aa5d6 100644 --- a/openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - az "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + az "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const GetOutput = ` diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go b/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go index 8996d366d0..41c618f063 100644 --- a/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go +++ b/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - az "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + az "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // Verifies that availability zones can be listed correctly diff --git a/openstack/compute/v2/extensions/availabilityzones/urls.go b/openstack/compute/v2/extensions/availabilityzones/urls.go index 9d99ec74b7..88c748275d 100644 --- a/openstack/compute/v2/extensions/availabilityzones/urls.go +++ b/openstack/compute/v2/extensions/availabilityzones/urls.go @@ -1,6 +1,6 @@ package availabilityzones -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-availability-zone") diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go index 05f45aeceb..c702ccd803 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -1,8 +1,8 @@ package bootfromvolume import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" ) type ( diff --git a/openstack/compute/v2/extensions/bootfromvolume/results.go b/openstack/compute/v2/extensions/bootfromvolume/results.go index ba1eafabcd..8a36abbb6d 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/results.go +++ b/openstack/compute/v2/extensions/bootfromvolume/results.go @@ -1,7 +1,7 @@ package bootfromvolume import ( - os "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + os "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" ) // CreateResult temporarily contains the response from a Create call. diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go index 39b09c7fe7..5c2ff60d38 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go @@ -1,8 +1,8 @@ package testing import ( - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/bootfromvolume" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" ) var BaseCreateOpts = servers.CreateOpts{ diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go index 22d11cc823..b5fa104773 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go +++ b/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go @@ -3,7 +3,7 @@ package testing import ( "testing" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestBootFromNewVolume(t *testing.T) { diff --git a/openstack/compute/v2/extensions/bootfromvolume/urls.go b/openstack/compute/v2/extensions/bootfromvolume/urls.go index e74422d0c1..b6aa7d652b 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/urls.go +++ b/openstack/compute/v2/extensions/bootfromvolume/urls.go @@ -1,6 +1,6 @@ package bootfromvolume -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("servers") diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/extensions/defsecrules/requests.go index 3a9503249b..2fdb577bf5 100644 --- a/openstack/compute/v2/extensions/defsecrules/requests.go +++ b/openstack/compute/v2/extensions/defsecrules/requests.go @@ -3,8 +3,8 @@ package defsecrules import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List will return a collection of default rules. diff --git a/openstack/compute/v2/extensions/defsecrules/results.go b/openstack/compute/v2/extensions/defsecrules/results.go index 36035acc2a..f28457f243 100644 --- a/openstack/compute/v2/extensions/defsecrules/results.go +++ b/openstack/compute/v2/extensions/defsecrules/results.go @@ -3,9 +3,9 @@ package defsecrules import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/pagination" ) // DefaultRule represents a rule belonging to the "default" security group. diff --git a/openstack/compute/v2/extensions/defsecrules/testing/fixtures_test.go b/openstack/compute/v2/extensions/defsecrules/testing/fixtures_test.go index e4a62d4ecc..4a8547c9a0 100644 --- a/openstack/compute/v2/extensions/defsecrules/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/defsecrules/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const rootPath = "/os-security-group-default-rules" diff --git a/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go b/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go index 1f2fb8686a..c2678b046a 100644 --- a/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go +++ b/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/defsecrules" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ruleID = "{ruleID}" diff --git a/openstack/compute/v2/extensions/defsecrules/urls.go b/openstack/compute/v2/extensions/defsecrules/urls.go index e5fbf82454..2cba51d3fc 100644 --- a/openstack/compute/v2/extensions/defsecrules/urls.go +++ b/openstack/compute/v2/extensions/defsecrules/urls.go @@ -1,6 +1,6 @@ package defsecrules -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const rulepath = "os-security-group-default-rules" diff --git a/openstack/compute/v2/extensions/delegate.go b/openstack/compute/v2/extensions/delegate.go index 00e7c3becf..84fe2963fc 100644 --- a/openstack/compute/v2/extensions/delegate.go +++ b/openstack/compute/v2/extensions/delegate.go @@ -1,9 +1,9 @@ package extensions import ( - "github.com/gophercloud/gophercloud" - common "github.com/gophercloud/gophercloud/openstack/common/extensions" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ExtractExtensions interprets a Page as a slice of Extensions. diff --git a/openstack/compute/v2/extensions/diagnostics/requests.go b/openstack/compute/v2/extensions/diagnostics/requests.go index 0f322761e1..d3523f6070 100644 --- a/openstack/compute/v2/extensions/diagnostics/requests.go +++ b/openstack/compute/v2/extensions/diagnostics/requests.go @@ -1,7 +1,7 @@ package diagnostics import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Diagnostics diff --git a/openstack/compute/v2/extensions/diagnostics/results.go b/openstack/compute/v2/extensions/diagnostics/results.go index 00ee3f316f..b8cac8b285 100644 --- a/openstack/compute/v2/extensions/diagnostics/results.go +++ b/openstack/compute/v2/extensions/diagnostics/results.go @@ -1,7 +1,7 @@ package diagnostics import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type serverDiagnosticsResult struct { diff --git a/openstack/compute/v2/extensions/diagnostics/testing/fixtures_test.go b/openstack/compute/v2/extensions/diagnostics/testing/fixtures_test.go index e36b8a403f..924716c827 100644 --- a/openstack/compute/v2/extensions/diagnostics/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/diagnostics/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HandleDiagnosticGetSuccessfully sets up the test server to respond to a diagnostic Get request. diff --git a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go b/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go index 9d8f3a3dc7..053dedfa55 100644 --- a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go +++ b/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diagnostics" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diagnostics" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGetDiagnostics(t *testing.T) { diff --git a/openstack/compute/v2/extensions/diagnostics/urls.go b/openstack/compute/v2/extensions/diagnostics/urls.go index 72ae68de45..4a3e70d7ca 100644 --- a/openstack/compute/v2/extensions/diagnostics/urls.go +++ b/openstack/compute/v2/extensions/diagnostics/urls.go @@ -1,6 +1,6 @@ package diagnostics -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // serverDiagnosticsURL returns the diagnostics url for a nova instance/server func serverDiagnosticsURL(client *gophercloud.ServiceClient, id string) string { diff --git a/openstack/compute/v2/extensions/diskconfig/requests.go b/openstack/compute/v2/extensions/diskconfig/requests.go index cc04aed6f3..5638f477f4 100644 --- a/openstack/compute/v2/extensions/diskconfig/requests.go +++ b/openstack/compute/v2/extensions/diskconfig/requests.go @@ -1,8 +1,8 @@ package diskconfig import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" ) // DiskConfig represents one of the two possible settings for the DiskConfig diff --git a/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go b/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go index 5b64931824..b21b1cf48e 100644 --- a/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go +++ b/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diskconfig" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diskconfig" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreateOpts(t *testing.T) { diff --git a/openstack/compute/v2/extensions/evacuate/requests.go b/openstack/compute/v2/extensions/evacuate/requests.go index b1d3726ab8..66ed220017 100644 --- a/openstack/compute/v2/extensions/evacuate/requests.go +++ b/openstack/compute/v2/extensions/evacuate/requests.go @@ -1,8 +1,8 @@ package evacuate import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // EvacuateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/compute/v2/extensions/evacuate/results.go b/openstack/compute/v2/extensions/evacuate/results.go index 54eacfc580..803d9d4772 100644 --- a/openstack/compute/v2/extensions/evacuate/results.go +++ b/openstack/compute/v2/extensions/evacuate/results.go @@ -1,7 +1,7 @@ package evacuate import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // EvacuateResult is the response from an Evacuate operation. diff --git a/openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go b/openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go index e078d1019d..c306643ce0 100644 --- a/openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockEvacuateResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/evacuate/testing/requests_test.go b/openstack/compute/v2/extensions/evacuate/testing/requests_test.go index aec03114b8..07f08ffa12 100644 --- a/openstack/compute/v2/extensions/evacuate/testing/requests_test.go +++ b/openstack/compute/v2/extensions/evacuate/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/evacuate" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/evacuate" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestEvacuate(t *testing.T) { diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go index a9d5d24b3b..b9ed64df94 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedserverattributes" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedserverattributes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestServerWithUsageExt(t *testing.T) { diff --git a/openstack/compute/v2/extensions/floatingips/requests.go b/openstack/compute/v2/extensions/floatingips/requests.go index 0d8104d24b..c3acec3e50 100644 --- a/openstack/compute/v2/extensions/floatingips/requests.go +++ b/openstack/compute/v2/extensions/floatingips/requests.go @@ -1,8 +1,8 @@ package floatingips import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List returns a Pager that allows you to iterate over a collection of FloatingIPs. diff --git a/openstack/compute/v2/extensions/floatingips/results.go b/openstack/compute/v2/extensions/floatingips/results.go index 4009b8336b..7cefbd048e 100644 --- a/openstack/compute/v2/extensions/floatingips/results.go +++ b/openstack/compute/v2/extensions/floatingips/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "strconv" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // A FloatingIP is an IP that can be associated with a server. diff --git a/openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go b/openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go index 6866e265de..101464ac33 100644 --- a/openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput is a sample response to a List call. diff --git a/openstack/compute/v2/extensions/floatingips/testing/requests_test.go b/openstack/compute/v2/extensions/floatingips/testing/requests_test.go index 2356671e02..6b4c4fa84d 100644 --- a/openstack/compute/v2/extensions/floatingips/testing/requests_test.go +++ b/openstack/compute/v2/extensions/floatingips/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/compute/v2/extensions/floatingips/urls.go b/openstack/compute/v2/extensions/floatingips/urls.go index 4768e5a897..853110aa7e 100644 --- a/openstack/compute/v2/extensions/floatingips/urls.go +++ b/openstack/compute/v2/extensions/floatingips/urls.go @@ -1,6 +1,6 @@ package floatingips -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-floating-ips" diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/extensions/hypervisors/requests.go index eac01cd468..d8446166b9 100644 --- a/openstack/compute/v2/extensions/hypervisors/requests.go +++ b/openstack/compute/v2/extensions/hypervisors/requests.go @@ -1,8 +1,8 @@ package hypervisors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/extensions/hypervisors/results.go index 26113f348e..7413dde45d 100644 --- a/openstack/compute/v2/extensions/hypervisors/results.go +++ b/openstack/compute/v2/extensions/hypervisors/results.go @@ -5,8 +5,8 @@ import ( "fmt" "strconv" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Topology represents a CPU Topology. diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go b/openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go index cef3d0ece7..1052efa9bc 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HypervisorListBodyPre253 represents a raw hypervisor list from the Compute diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go index 9cc27f607e..8c1296c0c1 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/hypervisors" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListHypervisorsPre253(t *testing.T) { diff --git a/openstack/compute/v2/extensions/hypervisors/urls.go b/openstack/compute/v2/extensions/hypervisors/urls.go index 4c18ed43c4..812302ddc1 100644 --- a/openstack/compute/v2/extensions/hypervisors/urls.go +++ b/openstack/compute/v2/extensions/hypervisors/urls.go @@ -1,6 +1,6 @@ package hypervisors -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func hypervisorsListDetailURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-hypervisors", "detail") diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/requests.go b/openstack/compute/v2/extensions/injectnetworkinfo/requests.go index bbc665f1aa..91645d01fb 100644 --- a/openstack/compute/v2/extensions/injectnetworkinfo/requests.go +++ b/openstack/compute/v2/extensions/injectnetworkinfo/requests.go @@ -1,8 +1,8 @@ package injectnetworkinfo import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // InjectNetworkInfo will inject the network info into a server diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/results.go b/openstack/compute/v2/extensions/injectnetworkinfo/results.go index 44b133f70b..339fe2ab7b 100644 --- a/openstack/compute/v2/extensions/injectnetworkinfo/results.go +++ b/openstack/compute/v2/extensions/injectnetworkinfo/results.go @@ -1,7 +1,7 @@ package injectnetworkinfo import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // InjectNetworkResult is the response of a InjectNetworkInfo operation. Call diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go b/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go index 1951e36daa..13a9148396 100644 --- a/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockInjectNetworkInfoResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go b/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go index 132b407361..8e5df9fde3 100644 --- a/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go +++ b/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/injectnetworkinfo" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/injectnetworkinfo" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" diff --git a/openstack/compute/v2/extensions/instanceactions/request.go b/openstack/compute/v2/extensions/instanceactions/request.go index 9367f96900..d6f69f8488 100644 --- a/openstack/compute/v2/extensions/instanceactions/request.go +++ b/openstack/compute/v2/extensions/instanceactions/request.go @@ -4,8 +4,8 @@ import ( "net/url" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/compute/v2/extensions/instanceactions/results.go b/openstack/compute/v2/extensions/instanceactions/results.go index 351d4b1eeb..9f0bcc6045 100644 --- a/openstack/compute/v2/extensions/instanceactions/results.go +++ b/openstack/compute/v2/extensions/instanceactions/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // InstanceAction represents an instance action. diff --git a/openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go b/openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go index ced79e74a1..8e593d5873 100644 --- a/openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/instanceactions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListExpected represents an expected repsonse from a List request. diff --git a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go b/openstack/compute/v2/extensions/instanceactions/testing/request_test.go index 77b7e69412..74e8f6b88e 100644 --- a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go +++ b/openstack/compute/v2/extensions/instanceactions/testing/request_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/instanceactions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/instanceactions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/compute/v2/extensions/instanceactions/urls.go b/openstack/compute/v2/extensions/instanceactions/urls.go index 4239ade7fd..baf4e23a93 100644 --- a/openstack/compute/v2/extensions/instanceactions/urls.go +++ b/openstack/compute/v2/extensions/instanceactions/urls.go @@ -1,6 +1,6 @@ package instanceactions -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "os-instance-actions") diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go index aba11fb435..2435147548 100644 --- a/openstack/compute/v2/extensions/keypairs/requests.go +++ b/openstack/compute/v2/extensions/keypairs/requests.go @@ -1,9 +1,9 @@ package keypairs import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsExt adds a KeyPair option to the base CreateOpts. diff --git a/openstack/compute/v2/extensions/keypairs/results.go b/openstack/compute/v2/extensions/keypairs/results.go index 0ac05c361d..0c91fee693 100644 --- a/openstack/compute/v2/extensions/keypairs/results.go +++ b/openstack/compute/v2/extensions/keypairs/results.go @@ -1,8 +1,8 @@ package keypairs import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // KeyPair is an SSH key known to the OpenStack Cloud that is available to be diff --git a/openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go b/openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go index ae62051e6c..3c4c33eec9 100644 --- a/openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput is a sample response to a List call. diff --git a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go b/openstack/compute/v2/extensions/keypairs/testing/requests_test.go index 74303dc2b4..c368bfa94b 100644 --- a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go +++ b/openstack/compute/v2/extensions/keypairs/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/compute/v2/extensions/keypairs/urls.go b/openstack/compute/v2/extensions/keypairs/urls.go index fec38f3679..f5be5f5e3b 100644 --- a/openstack/compute/v2/extensions/keypairs/urls.go +++ b/openstack/compute/v2/extensions/keypairs/urls.go @@ -1,6 +1,6 @@ package keypairs -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-keypairs" diff --git a/openstack/compute/v2/extensions/limits/requests.go b/openstack/compute/v2/extensions/limits/requests.go index 5193ce8465..946e80d5d7 100644 --- a/openstack/compute/v2/extensions/limits/requests.go +++ b/openstack/compute/v2/extensions/limits/requests.go @@ -1,7 +1,7 @@ package limits import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // GetOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/compute/v2/extensions/limits/results.go b/openstack/compute/v2/extensions/limits/results.go index 8d0564bd2f..850f8ce2d8 100644 --- a/openstack/compute/v2/extensions/limits/results.go +++ b/openstack/compute/v2/extensions/limits/results.go @@ -1,7 +1,7 @@ package limits import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Limits is a struct that contains the response of a limit query. diff --git a/openstack/compute/v2/extensions/limits/testing/fixtures_test.go b/openstack/compute/v2/extensions/limits/testing/fixtures_test.go index d4e52f7783..6b8abd991f 100644 --- a/openstack/compute/v2/extensions/limits/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/limits/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/limits" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // GetOutput is a sample response to a Get call. diff --git a/openstack/compute/v2/extensions/limits/testing/requests_test.go b/openstack/compute/v2/extensions/limits/testing/requests_test.go index 9c8456c9dd..d4196c0087 100644 --- a/openstack/compute/v2/extensions/limits/testing/requests_test.go +++ b/openstack/compute/v2/extensions/limits/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/limits" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGet(t *testing.T) { diff --git a/openstack/compute/v2/extensions/limits/urls.go b/openstack/compute/v2/extensions/limits/urls.go index edd97e4e0e..ac5b0f2333 100644 --- a/openstack/compute/v2/extensions/limits/urls.go +++ b/openstack/compute/v2/extensions/limits/urls.go @@ -1,7 +1,7 @@ package limits import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) const resourcePath = "limits" diff --git a/openstack/compute/v2/extensions/lockunlock/requests.go b/openstack/compute/v2/extensions/lockunlock/requests.go index df466bfcbf..1d7c070994 100644 --- a/openstack/compute/v2/extensions/lockunlock/requests.go +++ b/openstack/compute/v2/extensions/lockunlock/requests.go @@ -1,8 +1,8 @@ package lockunlock import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Lock is the operation responsible for locking a Compute server. diff --git a/openstack/compute/v2/extensions/lockunlock/results.go b/openstack/compute/v2/extensions/lockunlock/results.go index 282bc8a0eb..3f4abc77d0 100644 --- a/openstack/compute/v2/extensions/lockunlock/results.go +++ b/openstack/compute/v2/extensions/lockunlock/results.go @@ -1,7 +1,7 @@ package lockunlock import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // LockResult and UnlockResult are the responses from a Lock and Unlock diff --git a/openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go b/openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go index ec79b75321..db713c4963 100644 --- a/openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockStartServerResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/lockunlock/testing/request_test.go b/openstack/compute/v2/extensions/lockunlock/testing/request_test.go index cb2906d27b..8e8ef4996e 100644 --- a/openstack/compute/v2/extensions/lockunlock/testing/request_test.go +++ b/openstack/compute/v2/extensions/lockunlock/testing/request_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/lockunlock" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "{serverId}" diff --git a/openstack/compute/v2/extensions/migrate/requests.go b/openstack/compute/v2/extensions/migrate/requests.go index 81969e39a4..429f50425b 100644 --- a/openstack/compute/v2/extensions/migrate/requests.go +++ b/openstack/compute/v2/extensions/migrate/requests.go @@ -1,8 +1,8 @@ package migrate import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Migrate will initiate a migration of the instance to another host. diff --git a/openstack/compute/v2/extensions/migrate/results.go b/openstack/compute/v2/extensions/migrate/results.go index ebccf56ac3..3648fc1f48 100644 --- a/openstack/compute/v2/extensions/migrate/results.go +++ b/openstack/compute/v2/extensions/migrate/results.go @@ -1,7 +1,7 @@ package migrate import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // MigrateResult is the response from a Migrate operation. Call its ExtractErr diff --git a/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go b/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go index 1d2f5902c2..cbdacedac6 100644 --- a/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockMigrateResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/migrate/testing/requests_test.go b/openstack/compute/v2/extensions/migrate/testing/requests_test.go index b6906b7839..2d21cd73a5 100644 --- a/openstack/compute/v2/extensions/migrate/testing/requests_test.go +++ b/openstack/compute/v2/extensions/migrate/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/migrate" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/migrate" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" diff --git a/openstack/compute/v2/extensions/networks/requests.go b/openstack/compute/v2/extensions/networks/requests.go index 1a3bc0da79..b1309d49ab 100644 --- a/openstack/compute/v2/extensions/networks/requests.go +++ b/openstack/compute/v2/extensions/networks/requests.go @@ -1,8 +1,8 @@ package networks import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List returns a Pager that allows you to iterate over a collection of Network. diff --git a/openstack/compute/v2/extensions/networks/results.go b/openstack/compute/v2/extensions/networks/results.go index b6c4158f43..7f87d595e7 100644 --- a/openstack/compute/v2/extensions/networks/results.go +++ b/openstack/compute/v2/extensions/networks/results.go @@ -1,8 +1,8 @@ package networks import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // A Network represents a network in an OpenStack cloud. diff --git a/openstack/compute/v2/extensions/networks/testing/fixtures_test.go b/openstack/compute/v2/extensions/networks/testing/fixtures_test.go index e2fa49b48a..782efe1fed 100644 --- a/openstack/compute/v2/extensions/networks/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/networks/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput is a sample response to a List call. diff --git a/openstack/compute/v2/extensions/networks/testing/requests_test.go b/openstack/compute/v2/extensions/networks/testing/requests_test.go index 36b5463e42..080de3b284 100644 --- a/openstack/compute/v2/extensions/networks/testing/requests_test.go +++ b/openstack/compute/v2/extensions/networks/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/compute/v2/extensions/networks/urls.go b/openstack/compute/v2/extensions/networks/urls.go index 491bde6f62..f041841b1d 100644 --- a/openstack/compute/v2/extensions/networks/urls.go +++ b/openstack/compute/v2/extensions/networks/urls.go @@ -1,6 +1,6 @@ package networks -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-networks" diff --git a/openstack/compute/v2/extensions/pauseunpause/requests.go b/openstack/compute/v2/extensions/pauseunpause/requests.go index db3032cfff..10edf4d649 100644 --- a/openstack/compute/v2/extensions/pauseunpause/requests.go +++ b/openstack/compute/v2/extensions/pauseunpause/requests.go @@ -1,8 +1,8 @@ package pauseunpause import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Pause is the operation responsible for pausing a Compute server. diff --git a/openstack/compute/v2/extensions/pauseunpause/results.go b/openstack/compute/v2/extensions/pauseunpause/results.go index 3cb91d981a..4a71d2b2fb 100644 --- a/openstack/compute/v2/extensions/pauseunpause/results.go +++ b/openstack/compute/v2/extensions/pauseunpause/results.go @@ -1,6 +1,6 @@ package pauseunpause -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // PauseResult is the response from a Pause operation. Call its ExtractErr // method to determine if the request succeeded or failed. diff --git a/openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go b/openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go index 3723bb3360..645bf2ea6b 100644 --- a/openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockPauseServerResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go b/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go index 0433e8c482..6fbf5d41df 100644 --- a/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go +++ b/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/pauseunpause" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "{serverId}" diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go index bb99a1085d..ade73d8e86 100644 --- a/openstack/compute/v2/extensions/quotasets/requests.go +++ b/openstack/compute/v2/extensions/quotasets/requests.go @@ -1,7 +1,7 @@ package quotasets import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Get returns public data about a previously created QuotaSet. diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/extensions/quotasets/results.go index 07fb49c127..b72d76e249 100644 --- a/openstack/compute/v2/extensions/quotasets/results.go +++ b/openstack/compute/v2/extensions/quotasets/results.go @@ -1,8 +1,8 @@ package quotasets import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // QuotaSet is a set of operational limits that allow for control of compute diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go b/openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go index 03bc83cc80..7d34f43f71 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/quotasets" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // GetOutput is a sample response to a Get call. diff --git a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go index ccac51b364..e9ec586454 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go +++ b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "errors" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/quotasets" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGet(t *testing.T) { diff --git a/openstack/compute/v2/extensions/quotasets/urls.go b/openstack/compute/v2/extensions/quotasets/urls.go index 37e50215b5..4e0a2a213f 100644 --- a/openstack/compute/v2/extensions/quotasets/urls.go +++ b/openstack/compute/v2/extensions/quotasets/urls.go @@ -1,6 +1,6 @@ package quotasets -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-quota-sets" diff --git a/openstack/compute/v2/extensions/remoteconsoles/requests.go b/openstack/compute/v2/extensions/remoteconsoles/requests.go index 7620b56957..88dc6dbc8a 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/requests.go +++ b/openstack/compute/v2/extensions/remoteconsoles/requests.go @@ -1,7 +1,7 @@ package remoteconsoles import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ConsoleProtocol represents valid remote console protocol. diff --git a/openstack/compute/v2/extensions/remoteconsoles/results.go b/openstack/compute/v2/extensions/remoteconsoles/results.go index 684c637086..f01226eeac 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/results.go +++ b/openstack/compute/v2/extensions/remoteconsoles/results.go @@ -1,6 +1,6 @@ package remoteconsoles -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" type commonResult struct { gophercloud.Result diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go b/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go index f0b2c8c4c9..ed53447636 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go +++ b/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/remoteconsoles" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/remoteconsoles" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/compute/v2/extensions/remoteconsoles/urls.go b/openstack/compute/v2/extensions/remoteconsoles/urls.go index b4d7ec2bbf..34611f96fb 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/urls.go +++ b/openstack/compute/v2/extensions/remoteconsoles/urls.go @@ -1,6 +1,6 @@ package remoteconsoles -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "servers" diff --git a/openstack/compute/v2/extensions/rescueunrescue/requests.go b/openstack/compute/v2/extensions/rescueunrescue/requests.go index d6169e0d2c..82ac651662 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/requests.go +++ b/openstack/compute/v2/extensions/rescueunrescue/requests.go @@ -1,8 +1,8 @@ package rescueunrescue import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // RescueOptsBuilder is an interface that allows extensions to override the diff --git a/openstack/compute/v2/extensions/rescueunrescue/results.go b/openstack/compute/v2/extensions/rescueunrescue/results.go index 1966b15c46..12073ba677 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/results.go +++ b/openstack/compute/v2/extensions/rescueunrescue/results.go @@ -1,6 +1,6 @@ package rescueunrescue -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" type commonResult struct { gophercloud.Result diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go b/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go index fda9023e2f..852018eb4b 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go +++ b/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/rescueunrescue" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestRescue(t *testing.T) { diff --git a/openstack/compute/v2/extensions/resetnetwork/requests.go b/openstack/compute/v2/extensions/resetnetwork/requests.go index 247650d364..222807eca3 100644 --- a/openstack/compute/v2/extensions/resetnetwork/requests.go +++ b/openstack/compute/v2/extensions/resetnetwork/requests.go @@ -1,8 +1,8 @@ package resetnetwork import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // ResetNetwork will reset the network of a server diff --git a/openstack/compute/v2/extensions/resetnetwork/results.go b/openstack/compute/v2/extensions/resetnetwork/results.go index 638677b063..a9522d5440 100644 --- a/openstack/compute/v2/extensions/resetnetwork/results.go +++ b/openstack/compute/v2/extensions/resetnetwork/results.go @@ -1,7 +1,7 @@ package resetnetwork import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ResetResult is the response of a ResetNetwork operation. Call its ExtractErr diff --git a/openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go b/openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go index 7f4ba2e541..feb48a25ad 100644 --- a/openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockResetNetworkResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go b/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go index c296b3f7bd..1ae35531bb 100644 --- a/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go +++ b/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/resetnetwork" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/resetnetwork" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" diff --git a/openstack/compute/v2/extensions/resetstate/requests.go b/openstack/compute/v2/extensions/resetstate/requests.go index cf0650df8c..6b49d32ec2 100644 --- a/openstack/compute/v2/extensions/resetstate/requests.go +++ b/openstack/compute/v2/extensions/resetstate/requests.go @@ -1,8 +1,8 @@ package resetstate import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // ServerState refers to the states usable in ResetState Action diff --git a/openstack/compute/v2/extensions/resetstate/results.go b/openstack/compute/v2/extensions/resetstate/results.go index ddeb3519a1..5f3c63db69 100644 --- a/openstack/compute/v2/extensions/resetstate/results.go +++ b/openstack/compute/v2/extensions/resetstate/results.go @@ -1,7 +1,7 @@ package resetstate import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ResetResult is the response of a ResetState operation. Call its ExtractErr diff --git a/openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go b/openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go index 857a8b2127..7384908c23 100644 --- a/openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockResetStateResponse(t *testing.T, id string, state string) { diff --git a/openstack/compute/v2/extensions/resetstate/testing/requests_test.go b/openstack/compute/v2/extensions/resetstate/testing/requests_test.go index 491a7ee1fb..a3f35ca19b 100644 --- a/openstack/compute/v2/extensions/resetstate/testing/requests_test.go +++ b/openstack/compute/v2/extensions/resetstate/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/resetstate" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/resetstate" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" diff --git a/openstack/compute/v2/extensions/schedulerhints/requests.go b/openstack/compute/v2/extensions/schedulerhints/requests.go index 9a6324cf1d..0535d32556 100644 --- a/openstack/compute/v2/extensions/schedulerhints/requests.go +++ b/openstack/compute/v2/extensions/schedulerhints/requests.go @@ -6,8 +6,8 @@ import ( "regexp" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" ) // SchedulerHints represents a set of scheduling hints that are passed to the diff --git a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go index 426dce785f..b25a7579d6 100644 --- a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go +++ b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/schedulerhints" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreateOpts(t *testing.T) { diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go index 6f174bb95d..b5df71f756 100644 --- a/openstack/compute/v2/extensions/secgroups/requests.go +++ b/openstack/compute/v2/extensions/secgroups/requests.go @@ -1,8 +1,8 @@ package secgroups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager { diff --git a/openstack/compute/v2/extensions/secgroups/results.go b/openstack/compute/v2/extensions/secgroups/results.go index 40c4e94e75..30ab4fdb9c 100644 --- a/openstack/compute/v2/extensions/secgroups/results.go +++ b/openstack/compute/v2/extensions/secgroups/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "strconv" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SecurityGroup represents a security group. diff --git a/openstack/compute/v2/extensions/secgroups/testing/fixtures_test.go b/openstack/compute/v2/extensions/secgroups/testing/fixtures_test.go index 27bd56f364..0b35cc71d8 100644 --- a/openstack/compute/v2/extensions/secgroups/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/secgroups/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const rootPath = "/os-security-groups" diff --git a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go b/openstack/compute/v2/extensions/secgroups/testing/requests_test.go index 01ab9f7356..0f7d2cedea 100644 --- a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go +++ b/openstack/compute/v2/extensions/secgroups/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ( diff --git a/openstack/compute/v2/extensions/secgroups/urls.go b/openstack/compute/v2/extensions/secgroups/urls.go index d99746cae9..18fbdd5969 100644 --- a/openstack/compute/v2/extensions/secgroups/urls.go +++ b/openstack/compute/v2/extensions/secgroups/urls.go @@ -1,6 +1,6 @@ package secgroups -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( secgrouppath = "os-security-groups" diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go index 9769e48deb..eac23c2ad2 100644 --- a/openstack/compute/v2/extensions/servergroups/requests.go +++ b/openstack/compute/v2/extensions/servergroups/requests.go @@ -1,8 +1,8 @@ package servergroups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type ListOptsBuilder interface { diff --git a/openstack/compute/v2/extensions/servergroups/results.go b/openstack/compute/v2/extensions/servergroups/results.go index 03eb48d27a..053fc6ea0c 100644 --- a/openstack/compute/v2/extensions/servergroups/results.go +++ b/openstack/compute/v2/extensions/servergroups/results.go @@ -1,8 +1,8 @@ package servergroups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // A ServerGroup creates a policy for instance placement in the cloud. diff --git a/openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go b/openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go index fc4a4c7bd7..05839c2e50 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput is a sample response to a List call. diff --git a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go index e5b7e667cc..ec80002fc6 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go +++ b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/compute/v2/extensions/servergroups/urls.go b/openstack/compute/v2/extensions/servergroups/urls.go index 9a1f99b199..bca4f320ba 100644 --- a/openstack/compute/v2/extensions/servergroups/urls.go +++ b/openstack/compute/v2/extensions/servergroups/urls.go @@ -1,6 +1,6 @@ package servergroups -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-server-groups" diff --git a/openstack/compute/v2/extensions/serverusage/results.go b/openstack/compute/v2/extensions/serverusage/results.go index a80abb6bc9..fa637f2940 100644 --- a/openstack/compute/v2/extensions/serverusage/results.go +++ b/openstack/compute/v2/extensions/serverusage/results.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // UsageExt represents OS-SRV-USG server response fields. diff --git a/openstack/compute/v2/extensions/serverusage/testing/requests_test.go b/openstack/compute/v2/extensions/serverusage/testing/requests_test.go index b05dee5786..a7afc30d16 100644 --- a/openstack/compute/v2/extensions/serverusage/testing/requests_test.go +++ b/openstack/compute/v2/extensions/serverusage/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/serverusage" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/serverusage" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestServerWithUsageExt(t *testing.T) { diff --git a/openstack/compute/v2/extensions/services/requests.go b/openstack/compute/v2/extensions/services/requests.go index 841fac40ac..d7a8dccc7c 100644 --- a/openstack/compute/v2/extensions/services/requests.go +++ b/openstack/compute/v2/extensions/services/requests.go @@ -1,8 +1,8 @@ package services import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/compute/v2/extensions/services/results.go b/openstack/compute/v2/extensions/services/results.go index 2185b86ec8..769acb8103 100644 --- a/openstack/compute/v2/extensions/services/results.go +++ b/openstack/compute/v2/extensions/services/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Service represents a Compute service in the OpenStack cloud. diff --git a/openstack/compute/v2/extensions/services/testing/fixtures_test.go b/openstack/compute/v2/extensions/services/testing/fixtures_test.go index 8811ba9b3b..dc9661ce27 100644 --- a/openstack/compute/v2/extensions/services/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/services/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ServiceListBodyPre253 represents a raw service list from the Compute API diff --git a/openstack/compute/v2/extensions/services/testing/requests_test.go b/openstack/compute/v2/extensions/services/testing/requests_test.go index cd47353a05..f3a34dd919 100644 --- a/openstack/compute/v2/extensions/services/testing/requests_test.go +++ b/openstack/compute/v2/extensions/services/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/services" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/services" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListServicesPre253(t *testing.T) { diff --git a/openstack/compute/v2/extensions/services/urls.go b/openstack/compute/v2/extensions/services/urls.go index 49690efb25..0699e0176c 100644 --- a/openstack/compute/v2/extensions/services/urls.go +++ b/openstack/compute/v2/extensions/services/urls.go @@ -1,6 +1,6 @@ package services -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-services") diff --git a/openstack/compute/v2/extensions/shelveunshelve/requests.go b/openstack/compute/v2/extensions/shelveunshelve/requests.go index afcc85c473..438debb9e8 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/requests.go +++ b/openstack/compute/v2/extensions/shelveunshelve/requests.go @@ -1,8 +1,8 @@ package shelveunshelve import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Shelve is the operation responsible for shelving a Compute server. diff --git a/openstack/compute/v2/extensions/shelveunshelve/results.go b/openstack/compute/v2/extensions/shelveunshelve/results.go index 7d2cefffe2..553b60d0a7 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/results.go +++ b/openstack/compute/v2/extensions/shelveunshelve/results.go @@ -1,6 +1,6 @@ package shelveunshelve -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // ShelveResult is the response from a Shelve operation. Call its ExtractErr // method to determine if the request succeeded or failed. diff --git a/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go b/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go index 0f13cee671..fbe958d31e 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockShelveServerResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go b/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go index e2a2f8a1e7..45f1b27035 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go +++ b/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/shelveunshelve" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/shelveunshelve" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "{serverId}" diff --git a/openstack/compute/v2/extensions/startstop/requests.go b/openstack/compute/v2/extensions/startstop/requests.go index 9ece54b205..b30c8f5738 100644 --- a/openstack/compute/v2/extensions/startstop/requests.go +++ b/openstack/compute/v2/extensions/startstop/requests.go @@ -1,8 +1,8 @@ package startstop import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Start is the operation responsible for starting a Compute server. diff --git a/openstack/compute/v2/extensions/startstop/results.go b/openstack/compute/v2/extensions/startstop/results.go index 8349689332..5c5f94005a 100644 --- a/openstack/compute/v2/extensions/startstop/results.go +++ b/openstack/compute/v2/extensions/startstop/results.go @@ -1,6 +1,6 @@ package startstop -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // StartResult is the response from a Start operation. Call its ExtractErr // method to determine if the request succeeded or failed. diff --git a/openstack/compute/v2/extensions/startstop/testing/fixtures_test.go b/openstack/compute/v2/extensions/startstop/testing/fixtures_test.go index 1086b0e341..6d98be4bc6 100644 --- a/openstack/compute/v2/extensions/startstop/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/startstop/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockStartServerResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/startstop/testing/requests_test.go b/openstack/compute/v2/extensions/startstop/testing/requests_test.go index be45bf5c71..1d1a66f75b 100644 --- a/openstack/compute/v2/extensions/startstop/testing/requests_test.go +++ b/openstack/compute/v2/extensions/startstop/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/startstop" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "{serverId}" diff --git a/openstack/compute/v2/extensions/suspendresume/requests.go b/openstack/compute/v2/extensions/suspendresume/requests.go index f5ebda5d8b..8959d4416b 100644 --- a/openstack/compute/v2/extensions/suspendresume/requests.go +++ b/openstack/compute/v2/extensions/suspendresume/requests.go @@ -1,8 +1,8 @@ package suspendresume import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Suspend is the operation responsible for suspending a Compute server. diff --git a/openstack/compute/v2/extensions/suspendresume/results.go b/openstack/compute/v2/extensions/suspendresume/results.go index 988d83e4aa..5207bf89f8 100644 --- a/openstack/compute/v2/extensions/suspendresume/results.go +++ b/openstack/compute/v2/extensions/suspendresume/results.go @@ -1,6 +1,6 @@ package suspendresume -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // SuspendResult is the response from a Suspend operation. Call its // ExtractErr method to determine if the request succeeded or failed. diff --git a/openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go b/openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go index 5c6405deeb..11e6b55e4d 100644 --- a/openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func mockSuspendServerResponse(t *testing.T, id string) { diff --git a/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go b/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go index 7c8e4ec6b1..1ae7c80662 100644 --- a/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go +++ b/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/suspendresume" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/suspendresume" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const serverID = "{serverId}" diff --git a/openstack/compute/v2/extensions/tags/requests.go b/openstack/compute/v2/extensions/tags/requests.go index 0ccad4c3a9..bdea66b4c6 100644 --- a/openstack/compute/v2/extensions/tags/requests.go +++ b/openstack/compute/v2/extensions/tags/requests.go @@ -1,6 +1,6 @@ package tags -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // List all tags on a server. func List(client *gophercloud.ServiceClient, serverID string) (r ListResult) { diff --git a/openstack/compute/v2/extensions/tags/results.go b/openstack/compute/v2/extensions/tags/results.go index c73b7a6cb0..a3158504a6 100644 --- a/openstack/compute/v2/extensions/tags/results.go +++ b/openstack/compute/v2/extensions/tags/results.go @@ -1,6 +1,6 @@ package tags -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" type commonResult struct { gophercloud.Result diff --git a/openstack/compute/v2/extensions/tags/testing/requests_test.go b/openstack/compute/v2/extensions/tags/testing/requests_test.go index bc0eb8ec5e..8cb219f175 100644 --- a/openstack/compute/v2/extensions/tags/testing/requests_test.go +++ b/openstack/compute/v2/extensions/tags/testing/requests_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tags" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tags" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/compute/v2/extensions/tags/urls.go b/openstack/compute/v2/extensions/tags/urls.go index 64296887a5..1eb4597943 100644 --- a/openstack/compute/v2/extensions/tags/urls.go +++ b/openstack/compute/v2/extensions/tags/urls.go @@ -1,6 +1,6 @@ package tags -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootResourcePath = "servers" diff --git a/openstack/compute/v2/extensions/tenantnetworks/requests.go b/openstack/compute/v2/extensions/tenantnetworks/requests.go index 80c7b55920..79574a31e4 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/requests.go +++ b/openstack/compute/v2/extensions/tenantnetworks/requests.go @@ -1,8 +1,8 @@ package tenantnetworks import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List returns a Pager that allows you to iterate over a collection of Networks. diff --git a/openstack/compute/v2/extensions/tenantnetworks/results.go b/openstack/compute/v2/extensions/tenantnetworks/results.go index 96414fc587..386072ea74 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/results.go +++ b/openstack/compute/v2/extensions/tenantnetworks/results.go @@ -1,8 +1,8 @@ package tenantnetworks import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // A Network represents a network that a server communicates on. diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go b/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go index ae679b46e4..1fd6827e5a 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput is a sample response to a List call. diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go b/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go index 703c8468b7..a5427e930b 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go +++ b/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/compute/v2/extensions/tenantnetworks/urls.go b/openstack/compute/v2/extensions/tenantnetworks/urls.go index 683041ded3..0409256d5f 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/urls.go +++ b/openstack/compute/v2/extensions/tenantnetworks/urls.go @@ -1,6 +1,6 @@ package tenantnetworks -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-tenant-networks" diff --git a/openstack/compute/v2/extensions/testing/delegate_test.go b/openstack/compute/v2/extensions/testing/delegate_test.go index 2d33c86343..b776a66e6e 100644 --- a/openstack/compute/v2/extensions/testing/delegate_test.go +++ b/openstack/compute/v2/extensions/testing/delegate_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - common "github.com/gophercloud/gophercloud/openstack/common/extensions" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/compute/v2/extensions/testing/fixtures_test.go b/openstack/compute/v2/extensions/testing/fixtures_test.go index 2a3fb69094..6827dab20c 100644 --- a/openstack/compute/v2/extensions/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func HandleListExtensionsSuccessfully(t *testing.T) { diff --git a/openstack/compute/v2/extensions/urls.go b/openstack/compute/v2/extensions/urls.go index 9d40e49507..d73d9502b3 100644 --- a/openstack/compute/v2/extensions/urls.go +++ b/openstack/compute/v2/extensions/urls.go @@ -1,6 +1,6 @@ package extensions -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func ActionURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "action") diff --git a/openstack/compute/v2/extensions/usage/requests.go b/openstack/compute/v2/extensions/usage/requests.go index eb36f59b7e..ad6c533e16 100644 --- a/openstack/compute/v2/extensions/usage/requests.go +++ b/openstack/compute/v2/extensions/usage/requests.go @@ -4,8 +4,8 @@ import ( "net/url" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SingleTenantOpts are options for fetching usage of a single tenant. diff --git a/openstack/compute/v2/extensions/usage/results.go b/openstack/compute/v2/extensions/usage/results.go index 8c36dde8f2..81ed36053e 100644 --- a/openstack/compute/v2/extensions/usage/results.go +++ b/openstack/compute/v2/extensions/usage/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // TenantUsage is a set of usage information about a tenant over the sampling window diff --git a/openstack/compute/v2/extensions/usage/testing/fixtures_test.go b/openstack/compute/v2/extensions/usage/testing/fixtures_test.go index 3e37947a4e..dbd1dabdb3 100644 --- a/openstack/compute/v2/extensions/usage/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/usage/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/usage" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const FirstTenantID = "aabbccddeeff112233445566" diff --git a/openstack/compute/v2/extensions/usage/testing/requests_test.go b/openstack/compute/v2/extensions/usage/testing/requests_test.go index 1b4f27694c..464b650507 100644 --- a/openstack/compute/v2/extensions/usage/testing/requests_test.go +++ b/openstack/compute/v2/extensions/usage/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/usage" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/usage" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGetTenant(t *testing.T) { diff --git a/openstack/compute/v2/extensions/usage/urls.go b/openstack/compute/v2/extensions/usage/urls.go index 5063610701..109b04b8f9 100644 --- a/openstack/compute/v2/extensions/usage/urls.go +++ b/openstack/compute/v2/extensions/usage/urls.go @@ -1,6 +1,6 @@ package usage -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-simple-tenant-usage" diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go index 8c5a2ba03e..09d5eb529f 100644 --- a/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/openstack/compute/v2/extensions/volumeattach/requests.go @@ -1,8 +1,8 @@ package volumeattach import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List returns a Pager that allows you to iterate over a collection of diff --git a/openstack/compute/v2/extensions/volumeattach/results.go b/openstack/compute/v2/extensions/volumeattach/results.go index e5f565a35c..3b817750c1 100644 --- a/openstack/compute/v2/extensions/volumeattach/results.go +++ b/openstack/compute/v2/extensions/volumeattach/results.go @@ -1,8 +1,8 @@ package volumeattach import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // VolumeAttachment contains attachment information between a volume diff --git a/openstack/compute/v2/extensions/volumeattach/testing/fixtures_test.go b/openstack/compute/v2/extensions/volumeattach/testing/fixtures_test.go index f0158689e2..c5f0435a96 100644 --- a/openstack/compute/v2/extensions/volumeattach/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/volumeattach/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput is a sample response to a List call. diff --git a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go index 1b578eb0a6..79e5913e68 100644 --- a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go +++ b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/volumeattach" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // FirstVolumeAttachment is the first result in ListOutput. diff --git a/openstack/compute/v2/extensions/volumeattach/urls.go b/openstack/compute/v2/extensions/volumeattach/urls.go index 083f8dc455..9a274294d9 100644 --- a/openstack/compute/v2/extensions/volumeattach/urls.go +++ b/openstack/compute/v2/extensions/volumeattach/urls.go @@ -1,6 +1,6 @@ package volumeattach -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-volume_attachments" diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 3887cdfdca..8694977be9 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -1,8 +1,8 @@ package flavors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index aeaa64f016..5ca0132301 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "strconv" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/compute/v2/flavors/testing/fixtures_test.go b/openstack/compute/v2/flavors/testing/fixtures_test.go index 445f769b2b..94ac8c8a59 100644 --- a/openstack/compute/v2/flavors/testing/fixtures_test.go +++ b/openstack/compute/v2/flavors/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ExtraSpecsGetBody provides a GET result of the extra_specs for a flavor diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 356cdd6cc9..7608832c13 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "reflect" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const tokenID = "blerb" diff --git a/openstack/compute/v2/flavors/urls.go b/openstack/compute/v2/flavors/urls.go index 65bbb65401..27ca0571f1 100644 --- a/openstack/compute/v2/flavors/urls.go +++ b/openstack/compute/v2/flavors/urls.go @@ -1,7 +1,7 @@ package flavors import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) func getURL(client *gophercloud.ServiceClient, id string) string { diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go index c51de8dc3a..6cfbdb43ed 100644 --- a/openstack/compute/v2/images/requests.go +++ b/openstack/compute/v2/images/requests.go @@ -1,8 +1,8 @@ package images import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/compute/v2/images/results.go b/openstack/compute/v2/images/results.go index ca30befeb9..33ba105c39 100644 --- a/openstack/compute/v2/images/results.go +++ b/openstack/compute/v2/images/results.go @@ -1,8 +1,8 @@ package images import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // GetResult is the response from a Get operation. Call its Extract method to diff --git a/openstack/compute/v2/images/testing/requests_test.go b/openstack/compute/v2/images/testing/requests_test.go index 1de030352e..3cf546fa4c 100644 --- a/openstack/compute/v2/images/testing/requests_test.go +++ b/openstack/compute/v2/images/testing/requests_test.go @@ -7,10 +7,10 @@ import ( "reflect" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/images" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListImages(t *testing.T) { diff --git a/openstack/compute/v2/images/urls.go b/openstack/compute/v2/images/urls.go index 57787fb725..2f351b307f 100644 --- a/openstack/compute/v2/images/urls.go +++ b/openstack/compute/v2/images/urls.go @@ -1,6 +1,6 @@ package images -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listDetailURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("images", "detail") diff --git a/openstack/compute/v2/servers/errors.go b/openstack/compute/v2/servers/errors.go index c9f0e3c20b..a6eda38f45 100644 --- a/openstack/compute/v2/servers/errors.go +++ b/openstack/compute/v2/servers/errors.go @@ -3,7 +3,7 @@ package servers import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ErrNeitherImageIDNorImageNameProvided is the error when neither the image diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index d6a903aab9..5155f5dd40 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -5,8 +5,8 @@ import ( "encoding/json" "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 2c22a3c4d1..26b3463baa 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -9,8 +9,8 @@ import ( "path" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type serverResult struct { diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index c1761df912..3f628efe9e 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ServerListBody contains the canned body of a servers.List response. diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 5e16202cb4..745bf13685 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/diskconfig" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/extendedstatus" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diskconfig" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedstatus" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListServers(t *testing.T) { diff --git a/openstack/compute/v2/servers/testing/results_test.go b/openstack/compute/v2/servers/testing/results_test.go index cfa40f665c..f66fdb6827 100644 --- a/openstack/compute/v2/servers/testing/results_test.go +++ b/openstack/compute/v2/servers/testing/results_test.go @@ -6,10 +6,10 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" "golang.org/x/crypto/ssh" ) diff --git a/openstack/compute/v2/servers/urls.go b/openstack/compute/v2/servers/urls.go index e892e8d925..36c8c90a16 100644 --- a/openstack/compute/v2/servers/urls.go +++ b/openstack/compute/v2/servers/urls.go @@ -1,6 +1,6 @@ package servers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("servers") diff --git a/openstack/compute/v2/servers/util.go b/openstack/compute/v2/servers/util.go index cadef05450..aa08b5934e 100644 --- a/openstack/compute/v2/servers/util.go +++ b/openstack/compute/v2/servers/util.go @@ -1,6 +1,6 @@ package servers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // WaitForStatus will continually poll a server until it successfully // transitions to a specified status. It will do this for at most the number diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index b04416e68d..06e02ed9ce 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -28,7 +28,7 @@ import ( "path" "reflect" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" "gopkg.in/yaml.v2" ) diff --git a/openstack/config/clouds/clouds_test.go b/openstack/config/clouds/clouds_test.go index e25bae0122..2e13924cee 100644 --- a/openstack/config/clouds/clouds_test.go +++ b/openstack/config/clouds/clouds_test.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/gophercloud/gophercloud/openstack/config/clouds" + "github.com/gophercloud/gophercloud/v2/openstack/config/clouds" ) func ExampleWithCloudName() { diff --git a/openstack/config/clouds/options.go b/openstack/config/clouds/options.go index 30bf6a2ef3..97198c9371 100644 --- a/openstack/config/clouds/options.go +++ b/openstack/config/clouds/options.go @@ -3,7 +3,7 @@ package clouds import ( "io" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type cloudOpts struct { diff --git a/openstack/config/provider_client.go b/openstack/config/provider_client.go index a26b3511bd..842c75adc6 100644 --- a/openstack/config/provider_client.go +++ b/openstack/config/provider_client.go @@ -5,8 +5,8 @@ import ( "crypto/tls" "net/http" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" ) type options struct { diff --git a/openstack/container/v1/capsules/errors.go b/openstack/container/v1/capsules/errors.go index a5f602dc83..9a472c9bba 100644 --- a/openstack/container/v1/capsules/errors.go +++ b/openstack/container/v1/capsules/errors.go @@ -1,7 +1,7 @@ package capsules import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type ErrInvalidDataFormat struct { diff --git a/openstack/container/v1/capsules/microversions.go b/openstack/container/v1/capsules/microversions.go index 7eb648ce0f..6cdc04512c 100644 --- a/openstack/container/v1/capsules/microversions.go +++ b/openstack/container/v1/capsules/microversions.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ExtractV132 is a function that accepts a result and extracts a capsule resource. diff --git a/openstack/container/v1/capsules/requests.go b/openstack/container/v1/capsules/requests.go index 10b14f5757..74f17559ae 100644 --- a/openstack/container/v1/capsules/requests.go +++ b/openstack/container/v1/capsules/requests.go @@ -1,8 +1,8 @@ package capsules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder is the interface options structs have to satisfy in order diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index 9e99c2f7c8..0c51b6e5b5 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/container/v1/capsules/testing/fixtures_test.go b/openstack/container/v1/capsules/testing/fixtures_test.go index 8e82f25a42..6e434f8027 100644 --- a/openstack/container/v1/capsules/testing/fixtures_test.go +++ b/openstack/container/v1/capsules/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/container/v1/capsules" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ValidJSONTemplate is a valid OpenStack Capsule template in JSON format diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 2d5e7c8ea0..1f4baa49e0 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/container/v1/capsules" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGetCapsule_OldTime(t *testing.T) { diff --git a/openstack/container/v1/capsules/testing/template_test.go b/openstack/container/v1/capsules/testing/template_test.go index 62d0de8f5d..aef55f5530 100644 --- a/openstack/container/v1/capsules/testing/template_test.go +++ b/openstack/container/v1/capsules/testing/template_test.go @@ -3,8 +3,8 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/container/v1/capsules" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/container/v1/capsules" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTemplateParsing(t *testing.T) { diff --git a/openstack/container/v1/capsules/urls.go b/openstack/container/v1/capsules/urls.go index 575fb2a712..d05af67f30 100644 --- a/openstack/container/v1/capsules/urls.go +++ b/openstack/container/v1/capsules/urls.go @@ -1,6 +1,6 @@ package capsules -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("capsules", id) diff --git a/openstack/containerinfra/apiversions/requests.go b/openstack/containerinfra/apiversions/requests.go index 244a7acba4..a82c605c10 100644 --- a/openstack/containerinfra/apiversions/requests.go +++ b/openstack/containerinfra/apiversions/requests.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List lists all the API versions available to end-users. diff --git a/openstack/containerinfra/apiversions/results.go b/openstack/containerinfra/apiversions/results.go index 01a9795d45..7541c509fa 100644 --- a/openstack/containerinfra/apiversions/results.go +++ b/openstack/containerinfra/apiversions/results.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // APIVersion represents an API version for the Container Infra service. diff --git a/openstack/containerinfra/apiversions/testing/fixtures_test.go b/openstack/containerinfra/apiversions/testing/fixtures_test.go index b5a13ea486..e95058151d 100644 --- a/openstack/containerinfra/apiversions/testing/fixtures_test.go +++ b/openstack/containerinfra/apiversions/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/containerinfra/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const MagnumAPIVersionResponse = ` diff --git a/openstack/containerinfra/apiversions/testing/requests_test.go b/openstack/containerinfra/apiversions/testing/requests_test.go index cd21ad7792..6800739f9b 100644 --- a/openstack/containerinfra/apiversions/testing/requests_test.go +++ b/openstack/containerinfra/apiversions/testing/requests_test.go @@ -4,9 +4,9 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud/openstack/containerinfra/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAPIVersions(t *testing.T) { diff --git a/openstack/containerinfra/apiversions/urls.go b/openstack/containerinfra/apiversions/urls.go index 41aebdc5f2..47f8116620 100644 --- a/openstack/containerinfra/apiversions/urls.go +++ b/openstack/containerinfra/apiversions/urls.go @@ -3,8 +3,8 @@ package apiversions import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { diff --git a/openstack/containerinfra/v1/certificates/requests.go b/openstack/containerinfra/v1/certificates/requests.go index 4e02f96183..0a286989aa 100644 --- a/openstack/containerinfra/v1/certificates/requests.go +++ b/openstack/containerinfra/v1/certificates/requests.go @@ -1,7 +1,7 @@ package certificates import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // CreateOptsBuilder allows extensions to add additional parameters diff --git a/openstack/containerinfra/v1/certificates/results.go b/openstack/containerinfra/v1/certificates/results.go index 654e585fd4..a285e2167b 100644 --- a/openstack/containerinfra/v1/certificates/results.go +++ b/openstack/containerinfra/v1/certificates/results.go @@ -1,7 +1,7 @@ package certificates import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type commonResult struct { diff --git a/openstack/containerinfra/v1/certificates/testing/fixtures_test.go b/openstack/containerinfra/v1/certificates/testing/fixtures_test.go index 0634b7e25d..3f88c34242 100644 --- a/openstack/containerinfra/v1/certificates/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/certificates/testing/fixtures_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/certificates" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const CertificateResponse = ` diff --git a/openstack/containerinfra/v1/certificates/testing/requests_test.go b/openstack/containerinfra/v1/certificates/testing/requests_test.go index 7ad95e9cc9..b93861da15 100644 --- a/openstack/containerinfra/v1/certificates/testing/requests_test.go +++ b/openstack/containerinfra/v1/certificates/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/certificates" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/certificates" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGetCertificates(t *testing.T) { diff --git a/openstack/containerinfra/v1/certificates/urls.go b/openstack/containerinfra/v1/certificates/urls.go index 87c831d88e..86e0a1e3da 100644 --- a/openstack/containerinfra/v1/certificates/urls.go +++ b/openstack/containerinfra/v1/certificates/urls.go @@ -1,7 +1,7 @@ package certificates import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) var apiName = "certificates" diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index bd62a85b95..1860f1016f 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -1,8 +1,8 @@ package clusters import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder Builder. diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index abfb771850..5f176c91a7 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -3,8 +3,8 @@ package clusters import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures_test.go b/openstack/containerinfra/v1/clusters/testing/fixtures_test.go index 684984333c..c50b3495fd 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clusters" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const clusterUUID = "746e779a-751a-456b-a3e9-c883d734946f" diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index 680ea550cc..f1dc6d2dfd 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clusters" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clusters" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateCluster(t *testing.T) { diff --git a/openstack/containerinfra/v1/clusters/urls.go b/openstack/containerinfra/v1/clusters/urls.go index bcffd926ea..9f3556307b 100644 --- a/openstack/containerinfra/v1/clusters/urls.go +++ b/openstack/containerinfra/v1/clusters/urls.go @@ -1,7 +1,7 @@ package clusters import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) var apiName = "clusters" diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index 90f57558c8..141f07d53a 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -1,8 +1,8 @@ package clustertemplates import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder Builder. diff --git a/openstack/containerinfra/v1/clustertemplates/results.go b/openstack/containerinfra/v1/clustertemplates/results.go index 552ad2ee44..945fdf95fe 100644 --- a/openstack/containerinfra/v1/clustertemplates/results.go +++ b/openstack/containerinfra/v1/clustertemplates/results.go @@ -3,8 +3,8 @@ package clustertemplates import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/containerinfra/v1/clustertemplates/testing/fixtures_test.go b/openstack/containerinfra/v1/clustertemplates/testing/fixtures_test.go index 4051010d4f..f269d99cb3 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clustertemplates" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ClusterTemplateResponse = ` diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index 772d128e5b..50adcd53db 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/clustertemplates" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clustertemplates" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateClusterTemplate(t *testing.T) { diff --git a/openstack/containerinfra/v1/clustertemplates/urls.go b/openstack/containerinfra/v1/clustertemplates/urls.go index f90fb85108..add3ee9762 100644 --- a/openstack/containerinfra/v1/clustertemplates/urls.go +++ b/openstack/containerinfra/v1/clustertemplates/urls.go @@ -1,7 +1,7 @@ package clustertemplates import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) var apiName = "clustertemplates" diff --git a/openstack/containerinfra/v1/nodegroups/requests.go b/openstack/containerinfra/v1/nodegroups/requests.go index fa82e1265d..0c9955f123 100644 --- a/openstack/containerinfra/v1/nodegroups/requests.go +++ b/openstack/containerinfra/v1/nodegroups/requests.go @@ -1,8 +1,8 @@ package nodegroups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Get makes a request to the Magnum API to retrieve a node group diff --git a/openstack/containerinfra/v1/nodegroups/results.go b/openstack/containerinfra/v1/nodegroups/results.go index 97efacd058..c94defe618 100644 --- a/openstack/containerinfra/v1/nodegroups/results.go +++ b/openstack/containerinfra/v1/nodegroups/results.go @@ -3,8 +3,8 @@ package nodegroups import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go b/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go index 0cb13f7ab7..ae4c4f00bd 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/nodegroups" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ( diff --git a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go index d4d1a20aaf..d6c92c8e2e 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go +++ b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/nodegroups" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/nodegroups" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // TestGetNodeGroupSuccess gets a node group successfully. diff --git a/openstack/containerinfra/v1/nodegroups/urls.go b/openstack/containerinfra/v1/nodegroups/urls.go index a7dfa1e774..85ad4d0cc1 100644 --- a/openstack/containerinfra/v1/nodegroups/urls.go +++ b/openstack/containerinfra/v1/nodegroups/urls.go @@ -1,7 +1,7 @@ package nodegroups import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) func getURL(c *gophercloud.ServiceClient, clusterID, nodeGroupID string) string { diff --git a/openstack/containerinfra/v1/quotas/requests.go b/openstack/containerinfra/v1/quotas/requests.go index f2158c52a4..b3ecfb6f07 100644 --- a/openstack/containerinfra/v1/quotas/requests.go +++ b/openstack/containerinfra/v1/quotas/requests.go @@ -1,7 +1,7 @@ package quotas import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // CreateOptsBuilder Builder. diff --git a/openstack/containerinfra/v1/quotas/results.go b/openstack/containerinfra/v1/quotas/results.go index 0fc4f97d78..ab9e67912a 100644 --- a/openstack/containerinfra/v1/quotas/results.go +++ b/openstack/containerinfra/v1/quotas/results.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type commonResult struct { diff --git a/openstack/containerinfra/v1/quotas/testing/fixtures_test.go b/openstack/containerinfra/v1/quotas/testing/fixtures_test.go index 8e02448dad..2aad3abb66 100644 --- a/openstack/containerinfra/v1/quotas/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/quotas/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const projectID = "aa5436ab58144c768ca4e9d2e9f5c3b2" diff --git a/openstack/containerinfra/v1/quotas/testing/requests_test.go b/openstack/containerinfra/v1/quotas/testing/requests_test.go index 80e19a62c7..1e64eac2fe 100644 --- a/openstack/containerinfra/v1/quotas/testing/requests_test.go +++ b/openstack/containerinfra/v1/quotas/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/containerinfra/v1/quotas" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/quotas" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateQuota(t *testing.T) { diff --git a/openstack/containerinfra/v1/quotas/urls.go b/openstack/containerinfra/v1/quotas/urls.go index 332b3f3c4b..3819e2de51 100644 --- a/openstack/containerinfra/v1/quotas/urls.go +++ b/openstack/containerinfra/v1/quotas/urls.go @@ -1,7 +1,7 @@ package quotas import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) var apiName = "quotas" diff --git a/openstack/db/v1/configurations/requests.go b/openstack/db/v1/configurations/requests.go index 7b4907887e..94dedc271a 100644 --- a/openstack/db/v1/configurations/requests.go +++ b/openstack/db/v1/configurations/requests.go @@ -1,9 +1,9 @@ package configurations import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/db/v1/instances" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/instances" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List will list all of the available configurations. diff --git a/openstack/db/v1/configurations/results.go b/openstack/db/v1/configurations/results.go index 92006e9170..2c6588f6df 100644 --- a/openstack/db/v1/configurations/results.go +++ b/openstack/db/v1/configurations/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Config represents a configuration group API resource. diff --git a/openstack/db/v1/configurations/testing/fixtures_test.go b/openstack/db/v1/configurations/testing/fixtures_test.go index 3d515ea2b1..12e6819d4c 100644 --- a/openstack/db/v1/configurations/testing/fixtures_test.go +++ b/openstack/db/v1/configurations/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "fmt" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/configurations" ) var ( diff --git a/openstack/db/v1/configurations/testing/requests_test.go b/openstack/db/v1/configurations/testing/requests_test.go index 643f363426..e7a9ba6229 100644 --- a/openstack/db/v1/configurations/testing/requests_test.go +++ b/openstack/db/v1/configurations/testing/requests_test.go @@ -3,12 +3,12 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/db/v1/configurations" - "github.com/gophercloud/gophercloud/openstack/db/v1/instances" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" - "github.com/gophercloud/gophercloud/testhelper/fixture" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/configurations" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/instances" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" + "github.com/gophercloud/gophercloud/v2/testhelper/fixture" ) var ( diff --git a/openstack/db/v1/configurations/urls.go b/openstack/db/v1/configurations/urls.go index 0a69253a72..ac78a7aeff 100644 --- a/openstack/db/v1/configurations/urls.go +++ b/openstack/db/v1/configurations/urls.go @@ -1,6 +1,6 @@ package configurations -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func baseURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("configurations") diff --git a/openstack/db/v1/databases/requests.go b/openstack/db/v1/databases/requests.go index 908204ac25..92450538c4 100644 --- a/openstack/db/v1/databases/requests.go +++ b/openstack/db/v1/databases/requests.go @@ -1,8 +1,8 @@ package databases import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder builds create options diff --git a/openstack/db/v1/databases/results.go b/openstack/db/v1/databases/results.go index 22c7e2dfe9..7ca9da7f8b 100644 --- a/openstack/db/v1/databases/results.go +++ b/openstack/db/v1/databases/results.go @@ -1,8 +1,8 @@ package databases import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Database represents a Database API resource. diff --git a/openstack/db/v1/databases/testing/fixtures_test.go b/openstack/db/v1/databases/testing/fixtures_test.go index 02b9ecc2a3..b7362c0864 100644 --- a/openstack/db/v1/databases/testing/fixtures_test.go +++ b/openstack/db/v1/databases/testing/fixtures_test.go @@ -3,7 +3,7 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/testhelper/fixture" + "github.com/gophercloud/gophercloud/v2/testhelper/fixture" ) var ( diff --git a/openstack/db/v1/databases/testing/requests_test.go b/openstack/db/v1/databases/testing/requests_test.go index a470ffa899..f6a9798405 100644 --- a/openstack/db/v1/databases/testing/requests_test.go +++ b/openstack/db/v1/databases/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/db/v1/databases" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/db/v1/databases/urls.go b/openstack/db/v1/databases/urls.go index aba42c9c87..9d503240e0 100644 --- a/openstack/db/v1/databases/urls.go +++ b/openstack/db/v1/databases/urls.go @@ -1,6 +1,6 @@ package databases -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func baseURL(c *gophercloud.ServiceClient, instanceID string) string { return c.ServiceURL("instances", instanceID, "databases") diff --git a/openstack/db/v1/datastores/requests.go b/openstack/db/v1/datastores/requests.go index bca717d810..c0dc9f1b4c 100644 --- a/openstack/db/v1/datastores/requests.go +++ b/openstack/db/v1/datastores/requests.go @@ -1,8 +1,8 @@ package datastores import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List will list all available datastore types that instances can use. diff --git a/openstack/db/v1/datastores/results.go b/openstack/db/v1/datastores/results.go index a3b8be8951..60e342c73c 100644 --- a/openstack/db/v1/datastores/results.go +++ b/openstack/db/v1/datastores/results.go @@ -1,8 +1,8 @@ package datastores import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Version represents a version API resource. Multiple versions belong to a Datastore. diff --git a/openstack/db/v1/datastores/testing/fixtures_test.go b/openstack/db/v1/datastores/testing/fixtures_test.go index 3b82646342..c95a79a076 100644 --- a/openstack/db/v1/datastores/testing/fixtures_test.go +++ b/openstack/db/v1/datastores/testing/fixtures_test.go @@ -3,8 +3,8 @@ package testing import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/db/v1/datastores" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/datastores" ) const version1JSON = ` diff --git a/openstack/db/v1/datastores/testing/requests_test.go b/openstack/db/v1/datastores/testing/requests_test.go index b505726d3f..e0d55d946c 100644 --- a/openstack/db/v1/datastores/testing/requests_test.go +++ b/openstack/db/v1/datastores/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/db/v1/datastores" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" - "github.com/gophercloud/gophercloud/testhelper/fixture" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/datastores" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" + "github.com/gophercloud/gophercloud/v2/testhelper/fixture" ) func TestList(t *testing.T) { diff --git a/openstack/db/v1/datastores/urls.go b/openstack/db/v1/datastores/urls.go index 06d1b3daec..e2b8b22302 100644 --- a/openstack/db/v1/datastores/urls.go +++ b/openstack/db/v1/datastores/urls.go @@ -1,6 +1,6 @@ package datastores -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func baseURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("datastores") diff --git a/openstack/db/v1/flavors/requests.go b/openstack/db/v1/flavors/requests.go index 7946f14fa8..1f9bab8af3 100644 --- a/openstack/db/v1/flavors/requests.go +++ b/openstack/db/v1/flavors/requests.go @@ -1,8 +1,8 @@ package flavors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List will list all available hardware flavors that an instance can use. The diff --git a/openstack/db/v1/flavors/results.go b/openstack/db/v1/flavors/results.go index 716d756ad4..b3a6d4e304 100644 --- a/openstack/db/v1/flavors/results.go +++ b/openstack/db/v1/flavors/results.go @@ -1,8 +1,8 @@ package flavors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // GetResult temporarily holds the response from a Get call. diff --git a/openstack/db/v1/flavors/testing/fixtures_test.go b/openstack/db/v1/flavors/testing/fixtures_test.go index 9c323b80c7..048b23592e 100644 --- a/openstack/db/v1/flavors/testing/fixtures_test.go +++ b/openstack/db/v1/flavors/testing/fixtures_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud/testhelper/fixture" + "github.com/gophercloud/gophercloud/v2/testhelper/fixture" ) const flavor = ` diff --git a/openstack/db/v1/flavors/testing/requests_test.go b/openstack/db/v1/flavors/testing/requests_test.go index e8b580aef6..cf95072695 100644 --- a/openstack/db/v1/flavors/testing/requests_test.go +++ b/openstack/db/v1/flavors/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/db/v1/flavors" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/flavors" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListFlavors(t *testing.T) { diff --git a/openstack/db/v1/flavors/urls.go b/openstack/db/v1/flavors/urls.go index a24301b186..cc326c5112 100644 --- a/openstack/db/v1/flavors/urls.go +++ b/openstack/db/v1/flavors/urls.go @@ -1,6 +1,6 @@ package flavors -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func getURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("flavors", id) diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go index 26638157ca..c8eb604997 100644 --- a/openstack/db/v1/instances/requests.go +++ b/openstack/db/v1/instances/requests.go @@ -1,10 +1,10 @@ package instances import ( - "github.com/gophercloud/gophercloud" - db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" - "github.com/gophercloud/gophercloud/openstack/db/v1/users" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/users" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder is the top-level interface for create options. diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index b387208ace..e66a86abca 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -4,10 +4,10 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/db/v1/datastores" - "github.com/gophercloud/gophercloud/openstack/db/v1/users" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/datastores" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/users" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Volume represents information about an attached volume for a database instance. diff --git a/openstack/db/v1/instances/testing/fixtures_test.go b/openstack/db/v1/instances/testing/fixtures_test.go index 7abd42e3e7..2bafa618f1 100644 --- a/openstack/db/v1/instances/testing/fixtures_test.go +++ b/openstack/db/v1/instances/testing/fixtures_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/db/v1/datastores" - "github.com/gophercloud/gophercloud/openstack/db/v1/instances" - "github.com/gophercloud/gophercloud/testhelper/fixture" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/datastores" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/instances" + "github.com/gophercloud/gophercloud/v2/testhelper/fixture" ) var ( diff --git a/openstack/db/v1/instances/testing/requests_test.go b/openstack/db/v1/instances/testing/requests_test.go index 62d615d7a9..a9ca418e93 100644 --- a/openstack/db/v1/instances/testing/requests_test.go +++ b/openstack/db/v1/instances/testing/requests_test.go @@ -3,12 +3,12 @@ package testing import ( "testing" - db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" - "github.com/gophercloud/gophercloud/openstack/db/v1/instances" - "github.com/gophercloud/gophercloud/openstack/db/v1/users" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/instances" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/users" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/db/v1/instances/urls.go b/openstack/db/v1/instances/urls.go index 76d1ca56d8..8abb070053 100644 --- a/openstack/db/v1/instances/urls.go +++ b/openstack/db/v1/instances/urls.go @@ -1,6 +1,6 @@ package instances -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func baseURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("instances") diff --git a/openstack/db/v1/users/requests.go b/openstack/db/v1/users/requests.go index 4d107e490b..094403fce4 100644 --- a/openstack/db/v1/users/requests.go +++ b/openstack/db/v1/users/requests.go @@ -1,9 +1,9 @@ package users import ( - "github.com/gophercloud/gophercloud" - db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder is the top-level interface for creating JSON maps. diff --git a/openstack/db/v1/users/results.go b/openstack/db/v1/users/results.go index 21c1f3ddd1..650ddc076f 100644 --- a/openstack/db/v1/users/results.go +++ b/openstack/db/v1/users/results.go @@ -1,9 +1,9 @@ package users import ( - "github.com/gophercloud/gophercloud" - db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" + "github.com/gophercloud/gophercloud/v2/pagination" ) // User represents a database user diff --git a/openstack/db/v1/users/testing/fixtures_test.go b/openstack/db/v1/users/testing/fixtures_test.go index f49f46f93c..961e4f6ecd 100644 --- a/openstack/db/v1/users/testing/fixtures_test.go +++ b/openstack/db/v1/users/testing/fixtures_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud/testhelper/fixture" + "github.com/gophercloud/gophercloud/v2/testhelper/fixture" ) const user1 = ` diff --git a/openstack/db/v1/users/testing/requests_test.go b/openstack/db/v1/users/testing/requests_test.go index 23a2b1d006..2a15f811a9 100644 --- a/openstack/db/v1/users/testing/requests_test.go +++ b/openstack/db/v1/users/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - db "github.com/gophercloud/gophercloud/openstack/db/v1/databases" - "github.com/gophercloud/gophercloud/openstack/db/v1/users" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" + "github.com/gophercloud/gophercloud/v2/openstack/db/v1/users" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/db/v1/users/urls.go b/openstack/db/v1/users/urls.go index 8c36a39b30..942b3ec4ef 100644 --- a/openstack/db/v1/users/urls.go +++ b/openstack/db/v1/users/urls.go @@ -1,6 +1,6 @@ package users -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func baseURL(c *gophercloud.ServiceClient, instanceID string) string { return c.ServiceURL("instances", instanceID, "users") diff --git a/openstack/dns/v2/recordsets/requests.go b/openstack/dns/v2/recordsets/requests.go index 58414bc0dd..36534bfa45 100644 --- a/openstack/dns/v2/recordsets/requests.go +++ b/openstack/dns/v2/recordsets/requests.go @@ -1,8 +1,8 @@ package recordsets import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/dns/v2/recordsets/results.go b/openstack/dns/v2/recordsets/results.go index b8c92ccff9..c0593e1de9 100644 --- a/openstack/dns/v2/recordsets/results.go +++ b/openstack/dns/v2/recordsets/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/dns/v2/recordsets/testing/fixtures_test.go b/openstack/dns/v2/recordsets/testing/fixtures_test.go index 24ca89c518..4b0ba245c1 100644 --- a/openstack/dns/v2/recordsets/testing/fixtures_test.go +++ b/openstack/dns/v2/recordsets/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/recordsets" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListByZoneOutput is a sample response to a ListByZone call. diff --git a/openstack/dns/v2/recordsets/testing/requests_test.go b/openstack/dns/v2/recordsets/testing/requests_test.go index 07f8cebabf..6624e242b7 100644 --- a/openstack/dns/v2/recordsets/testing/requests_test.go +++ b/openstack/dns/v2/recordsets/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "encoding/json" "testing" - "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/recordsets" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListByZone(t *testing.T) { diff --git a/openstack/dns/v2/recordsets/urls.go b/openstack/dns/v2/recordsets/urls.go index 5ec18d1bb7..26d9384aa0 100644 --- a/openstack/dns/v2/recordsets/urls.go +++ b/openstack/dns/v2/recordsets/urls.go @@ -1,6 +1,6 @@ package recordsets -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func baseURL(c *gophercloud.ServiceClient, zoneID string) string { return c.ServiceURL("zones", zoneID, "recordsets") diff --git a/openstack/dns/v2/transfer/accept/requests.go b/openstack/dns/v2/transfer/accept/requests.go index 4668156c77..dc98e6d10e 100644 --- a/openstack/dns/v2/transfer/accept/requests.go +++ b/openstack/dns/v2/transfer/accept/requests.go @@ -3,8 +3,8 @@ package accept import ( "net/http" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add parameters to the List request. diff --git a/openstack/dns/v2/transfer/accept/results.go b/openstack/dns/v2/transfer/accept/results.go index 85135694b5..7c54e73503 100644 --- a/openstack/dns/v2/transfer/accept/results.go +++ b/openstack/dns/v2/transfer/accept/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/dns/v2/transfer/accept/testing/accepts_test.go b/openstack/dns/v2/transfer/accept/testing/accepts_test.go index 49d1269005..49e5e75380 100644 --- a/openstack/dns/v2/transfer/accept/testing/accepts_test.go +++ b/openstack/dns/v2/transfer/accept/testing/accepts_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - transferAccepts "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/accept" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + transferAccepts "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/accept" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/dns/v2/transfer/accept/testing/fixtures_test.go b/openstack/dns/v2/transfer/accept/testing/fixtures_test.go index 79ffd5d6fe..e52b08576f 100644 --- a/openstack/dns/v2/transfer/accept/testing/fixtures_test.go +++ b/openstack/dns/v2/transfer/accept/testing/fixtures_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - transferAccepts "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/accept" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + transferAccepts "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/accept" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput is a sample response to a List call. diff --git a/openstack/dns/v2/transfer/accept/urls.go b/openstack/dns/v2/transfer/accept/urls.go index f7e1b69ea5..faba55a823 100644 --- a/openstack/dns/v2/transfer/accept/urls.go +++ b/openstack/dns/v2/transfer/accept/urls.go @@ -1,6 +1,6 @@ package accept -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "zones" diff --git a/openstack/dns/v2/transfer/request/requests.go b/openstack/dns/v2/transfer/request/requests.go index c9d3feb83b..46eaf9473b 100644 --- a/openstack/dns/v2/transfer/request/requests.go +++ b/openstack/dns/v2/transfer/request/requests.go @@ -3,8 +3,8 @@ package request import ( "net/http" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add parameters to the List request. diff --git a/openstack/dns/v2/transfer/request/results.go b/openstack/dns/v2/transfer/request/results.go index edbf808985..e56ffc7ef4 100644 --- a/openstack/dns/v2/transfer/request/results.go +++ b/openstack/dns/v2/transfer/request/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/dns/v2/transfer/request/testing/fixtures_test.go b/openstack/dns/v2/transfer/request/testing/fixtures_test.go index bf619ed8d9..a0460bf643 100644 --- a/openstack/dns/v2/transfer/request/testing/fixtures_test.go +++ b/openstack/dns/v2/transfer/request/testing/fixtures_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - transferRequests "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/request" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + transferRequests "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/request" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput is a sample response to a List call. diff --git a/openstack/dns/v2/transfer/request/testing/requests_test.go b/openstack/dns/v2/transfer/request/testing/requests_test.go index 3df033da1b..c7ebe5832d 100644 --- a/openstack/dns/v2/transfer/request/testing/requests_test.go +++ b/openstack/dns/v2/transfer/request/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - transferRequests "github.com/gophercloud/gophercloud/openstack/dns/v2/transfer/request" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + transferRequests "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/request" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/dns/v2/transfer/request/urls.go b/openstack/dns/v2/transfer/request/urls.go index d8fe6ac488..5b34e540de 100644 --- a/openstack/dns/v2/transfer/request/urls.go +++ b/openstack/dns/v2/transfer/request/urls.go @@ -1,6 +1,6 @@ package request -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "zones" diff --git a/openstack/dns/v2/zones/requests.go b/openstack/dns/v2/zones/requests.go index 7fa25359ff..08da3d80a3 100644 --- a/openstack/dns/v2/zones/requests.go +++ b/openstack/dns/v2/zones/requests.go @@ -1,8 +1,8 @@ package zones import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add parameters to the List request. diff --git a/openstack/dns/v2/zones/results.go b/openstack/dns/v2/zones/results.go index c84c722c08..7321880aca 100644 --- a/openstack/dns/v2/zones/results.go +++ b/openstack/dns/v2/zones/results.go @@ -5,8 +5,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/dns/v2/zones/testing/fixtures_test.go b/openstack/dns/v2/zones/testing/fixtures_test.go index 55e401306d..047b78080b 100644 --- a/openstack/dns/v2/zones/testing/fixtures_test.go +++ b/openstack/dns/v2/zones/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/zones" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // List Output is a sample response to a List call. diff --git a/openstack/dns/v2/zones/testing/requests_test.go b/openstack/dns/v2/zones/testing/requests_test.go index 0c9857cbda..2283c1b955 100644 --- a/openstack/dns/v2/zones/testing/requests_test.go +++ b/openstack/dns/v2/zones/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/zones" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/dns/v2/zones/urls.go b/openstack/dns/v2/zones/urls.go index 9bef705809..d157b30ef3 100644 --- a/openstack/dns/v2/zones/urls.go +++ b/openstack/dns/v2/zones/urls.go @@ -1,6 +1,6 @@ package zones -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func baseURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("zones") diff --git a/openstack/endpoint_location.go b/openstack/endpoint_location.go index 509700790e..2cdbd3e7f7 100644 --- a/openstack/endpoint_location.go +++ b/openstack/endpoint_location.go @@ -1,9 +1,9 @@ package openstack import ( - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2" + tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" ) /* diff --git a/openstack/errors.go b/openstack/errors.go index cba6ae5f00..f5273483ec 100644 --- a/openstack/errors.go +++ b/openstack/errors.go @@ -3,7 +3,7 @@ package openstack import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ErrEndpointNotFound is the error when no suitable endpoint can be found diff --git a/openstack/identity/v2/extensions/admin/roles/requests.go b/openstack/identity/v2/extensions/admin/roles/requests.go index 2d707902a5..abaed4ba80 100644 --- a/openstack/identity/v2/extensions/admin/roles/requests.go +++ b/openstack/identity/v2/extensions/admin/roles/requests.go @@ -1,8 +1,8 @@ package roles import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List is the operation responsible for listing all available global roles diff --git a/openstack/identity/v2/extensions/admin/roles/results.go b/openstack/identity/v2/extensions/admin/roles/results.go index 6794d5f697..a51780bc6d 100644 --- a/openstack/identity/v2/extensions/admin/roles/results.go +++ b/openstack/identity/v2/extensions/admin/roles/results.go @@ -1,8 +1,8 @@ package roles import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Role represents an API role resource. diff --git a/openstack/identity/v2/extensions/admin/roles/testing/fixtures_test.go b/openstack/identity/v2/extensions/admin/roles/testing/fixtures_test.go index 498c1611d0..0b5af6e541 100644 --- a/openstack/identity/v2/extensions/admin/roles/testing/fixtures_test.go +++ b/openstack/identity/v2/extensions/admin/roles/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListRoleResponse(t *testing.T) { diff --git a/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go b/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go index 8cf539557c..12703aa216 100644 --- a/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go +++ b/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions/admin/roles" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions/admin/roles" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestRole(t *testing.T) { diff --git a/openstack/identity/v2/extensions/admin/roles/urls.go b/openstack/identity/v2/extensions/admin/roles/urls.go index e4661e8bf1..f662ed87ac 100644 --- a/openstack/identity/v2/extensions/admin/roles/urls.go +++ b/openstack/identity/v2/extensions/admin/roles/urls.go @@ -1,6 +1,6 @@ package roles -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( ExtPath = "OS-KSADM" diff --git a/openstack/identity/v2/extensions/delegate.go b/openstack/identity/v2/extensions/delegate.go index cf6cc816da..870ee9929b 100644 --- a/openstack/identity/v2/extensions/delegate.go +++ b/openstack/identity/v2/extensions/delegate.go @@ -1,9 +1,9 @@ package extensions import ( - "github.com/gophercloud/gophercloud" - common "github.com/gophercloud/gophercloud/openstack/common/extensions" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ExtensionPage is a single page of Extension results. diff --git a/openstack/identity/v2/extensions/testing/delegate_test.go b/openstack/identity/v2/extensions/testing/delegate_test.go index e7869d8d87..51be73f115 100644 --- a/openstack/identity/v2/extensions/testing/delegate_test.go +++ b/openstack/identity/v2/extensions/testing/delegate_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - common "github.com/gophercloud/gophercloud/openstack/common/extensions/testing" - "github.com/gophercloud/gophercloud/openstack/identity/v2/extensions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions/testing" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/identity/v2/extensions/testing/fixtures_test.go b/openstack/identity/v2/extensions/testing/fixtures_test.go index 60afb747b6..3733eba312 100644 --- a/openstack/identity/v2/extensions/testing/fixtures_test.go +++ b/openstack/identity/v2/extensions/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single Extension result. It differs from the delegated implementation diff --git a/openstack/identity/v2/tenants/requests.go b/openstack/identity/v2/tenants/requests.go index f16df38e5e..f6de7839d4 100644 --- a/openstack/identity/v2/tenants/requests.go +++ b/openstack/identity/v2/tenants/requests.go @@ -1,8 +1,8 @@ package tenants import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts filters the Tenants that are returned by the List call. diff --git a/openstack/identity/v2/tenants/results.go b/openstack/identity/v2/tenants/results.go index 2daff98403..2569ffec05 100644 --- a/openstack/identity/v2/tenants/results.go +++ b/openstack/identity/v2/tenants/results.go @@ -1,8 +1,8 @@ package tenants import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Tenant is a grouping of users in the identity service. diff --git a/openstack/identity/v2/tenants/testing/fixtures_test.go b/openstack/identity/v2/tenants/testing/fixtures_test.go index 9a314704fa..7c50152faa 100644 --- a/openstack/identity/v2/tenants/testing/fixtures_test.go +++ b/openstack/identity/v2/tenants/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of Tenant results. diff --git a/openstack/identity/v2/tenants/testing/requests_test.go b/openstack/identity/v2/tenants/testing/requests_test.go index 585bdcf2e9..1d75a09a2e 100644 --- a/openstack/identity/v2/tenants/testing/requests_test.go +++ b/openstack/identity/v2/tenants/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListTenants(t *testing.T) { diff --git a/openstack/identity/v2/tenants/urls.go b/openstack/identity/v2/tenants/urls.go index 0f02669079..4c2aaf3843 100644 --- a/openstack/identity/v2/tenants/urls.go +++ b/openstack/identity/v2/tenants/urls.go @@ -1,6 +1,6 @@ package tenants -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("tenants") diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 67c04fced2..9e7b7512e1 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -3,7 +3,7 @@ package tokens import ( "context" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // PasswordCredentialsV2 represents the required options to authenticate diff --git a/openstack/identity/v2/tokens/results.go b/openstack/identity/v2/tokens/results.go index ee5da37f46..516371ee85 100644 --- a/openstack/identity/v2/tokens/results.go +++ b/openstack/identity/v2/tokens/results.go @@ -3,8 +3,8 @@ package tokens import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants" ) // Token provides only the most basic information related to an authentication diff --git a/openstack/identity/v2/tokens/testing/fixtures_test.go b/openstack/identity/v2/tokens/testing/fixtures_test.go index 1a819d1e5c..8eb7d6f7ea 100644 --- a/openstack/identity/v2/tokens/testing/fixtures_test.go +++ b/openstack/identity/v2/tokens/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - th "github.com/gophercloud/gophercloud/testhelper" - thclient "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" + thclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ExpectedToken is the token that should be parsed from TokenCreationResponse. diff --git a/openstack/identity/v2/tokens/testing/requests_test.go b/openstack/identity/v2/tokens/testing/requests_test.go index b687a929e0..ecf53870c0 100644 --- a/openstack/identity/v2/tokens/testing/requests_test.go +++ b/openstack/identity/v2/tokens/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func tokenPost(t *testing.T, options gophercloud.AuthOptions, requestJSON string) tokens.CreateResult { diff --git a/openstack/identity/v2/tokens/urls.go b/openstack/identity/v2/tokens/urls.go index ee0a28f200..845cdb58b2 100644 --- a/openstack/identity/v2/tokens/urls.go +++ b/openstack/identity/v2/tokens/urls.go @@ -1,6 +1,6 @@ package tokens -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // CreateURL generates the URL used to create new Tokens. func CreateURL(client *gophercloud.ServiceClient) string { diff --git a/openstack/identity/v2/users/requests.go b/openstack/identity/v2/users/requests.go index 57c566b577..7bb60a7c62 100644 --- a/openstack/identity/v2/users/requests.go +++ b/openstack/identity/v2/users/requests.go @@ -1,8 +1,8 @@ package users import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List lists the existing users. diff --git a/openstack/identity/v2/users/results.go b/openstack/identity/v2/users/results.go index 4ed1d6ebaf..1188a752cf 100644 --- a/openstack/identity/v2/users/results.go +++ b/openstack/identity/v2/users/results.go @@ -1,8 +1,8 @@ package users import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // User represents a user resource that exists on the API. diff --git a/openstack/identity/v2/users/testing/fixtures_test.go b/openstack/identity/v2/users/testing/fixtures_test.go index 8626da2af6..f38f05dba2 100644 --- a/openstack/identity/v2/users/testing/fixtures_test.go +++ b/openstack/identity/v2/users/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListUserResponse(t *testing.T) { diff --git a/openstack/identity/v2/users/testing/requests_test.go b/openstack/identity/v2/users/testing/requests_test.go index 3cb047e2da..53060a776f 100644 --- a/openstack/identity/v2/users/testing/requests_test.go +++ b/openstack/identity/v2/users/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v2/users" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/users" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/identity/v2/users/urls.go b/openstack/identity/v2/users/urls.go index 89f66f2799..fb4fb0f0b1 100644 --- a/openstack/identity/v2/users/urls.go +++ b/openstack/identity/v2/users/urls.go @@ -1,6 +1,6 @@ package users -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( tenantPath = "tenants" diff --git a/openstack/identity/v3/applicationcredentials/requests.go b/openstack/identity/v3/applicationcredentials/requests.go index be62af7f78..5b503092e4 100644 --- a/openstack/identity/v3/applicationcredentials/requests.go +++ b/openstack/identity/v3/applicationcredentials/requests.go @@ -3,8 +3,8 @@ package applicationcredentials import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/applicationcredentials/results.go b/openstack/identity/v3/applicationcredentials/results.go index 8ed389d134..d74e218d4b 100644 --- a/openstack/identity/v3/applicationcredentials/results.go +++ b/openstack/identity/v3/applicationcredentials/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type Role struct { diff --git a/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go b/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go index b2a749bbf3..6ddb513810 100644 --- a/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go +++ b/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/applicationcredentials" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const userID = "2844b2a08be147a08ef58317d6471f1f" diff --git a/openstack/identity/v3/applicationcredentials/testing/requests_test.go b/openstack/identity/v3/applicationcredentials/testing/requests_test.go index 31d76b9432..8a305f028e 100644 --- a/openstack/identity/v3/applicationcredentials/testing/requests_test.go +++ b/openstack/identity/v3/applicationcredentials/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/applicationcredentials" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/applicationcredentials" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListApplicationCredentials(t *testing.T) { diff --git a/openstack/identity/v3/applicationcredentials/urls.go b/openstack/identity/v3/applicationcredentials/urls.go index f809385f25..21bd4f2872 100644 --- a/openstack/identity/v3/applicationcredentials/urls.go +++ b/openstack/identity/v3/applicationcredentials/urls.go @@ -1,6 +1,6 @@ package applicationcredentials -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "application_credentials") diff --git a/openstack/identity/v3/catalog/requests.go b/openstack/identity/v3/catalog/requests.go index 82773d3a17..0d5fd1337c 100644 --- a/openstack/identity/v3/catalog/requests.go +++ b/openstack/identity/v3/catalog/requests.go @@ -1,8 +1,8 @@ package catalog import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List enumerates the services available to a specific user. diff --git a/openstack/identity/v3/catalog/results.go b/openstack/identity/v3/catalog/results.go index 99dd24c92e..037e75dc1b 100644 --- a/openstack/identity/v3/catalog/results.go +++ b/openstack/identity/v3/catalog/results.go @@ -1,8 +1,8 @@ package catalog import ( - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ServiceCatalogPage is a single page of Service results. diff --git a/openstack/identity/v3/catalog/testing/catalog_test.go b/openstack/identity/v3/catalog/testing/catalog_test.go index 54a08b539e..0753fdb2c1 100644 --- a/openstack/identity/v3/catalog/testing/catalog_test.go +++ b/openstack/identity/v3/catalog/testing/catalog_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/catalog" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/catalog" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListCatalog(t *testing.T) { diff --git a/openstack/identity/v3/catalog/testing/fixtures_test.go b/openstack/identity/v3/catalog/testing/fixtures_test.go index cd46f7cec6..541fcac4af 100644 --- a/openstack/identity/v3/catalog/testing/fixtures_test.go +++ b/openstack/identity/v3/catalog/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of ServiceCatalog results. diff --git a/openstack/identity/v3/catalog/urls.go b/openstack/identity/v3/catalog/urls.go index d3edf6451f..f8a18abcda 100644 --- a/openstack/identity/v3/catalog/urls.go +++ b/openstack/identity/v3/catalog/urls.go @@ -1,6 +1,6 @@ package catalog -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("auth", "catalog") diff --git a/openstack/identity/v3/credentials/requests.go b/openstack/identity/v3/credentials/requests.go index 64dd1bde78..c5b9fcb803 100644 --- a/openstack/identity/v3/credentials/requests.go +++ b/openstack/identity/v3/credentials/requests.go @@ -1,8 +1,8 @@ package credentials import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/credentials/results.go b/openstack/identity/v3/credentials/results.go index 312ccf10d7..0a876744d1 100644 --- a/openstack/identity/v3/credentials/results.go +++ b/openstack/identity/v3/credentials/results.go @@ -1,8 +1,8 @@ package credentials import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Credential represents the Credential object diff --git a/openstack/identity/v3/credentials/testing/fixtures_test.go b/openstack/identity/v3/credentials/testing/fixtures_test.go index b765538c0e..eaa9d4610e 100644 --- a/openstack/identity/v3/credentials/testing/fixtures_test.go +++ b/openstack/identity/v3/credentials/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/credentials" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const userID = "bb5476fd12884539b41d5a88f838d773" diff --git a/openstack/identity/v3/credentials/testing/requests_test.go b/openstack/identity/v3/credentials/testing/requests_test.go index f901db5e8a..2e7f5995b0 100644 --- a/openstack/identity/v3/credentials/testing/requests_test.go +++ b/openstack/identity/v3/credentials/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/credentials" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/credentials" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListCredentials(t *testing.T) { diff --git a/openstack/identity/v3/credentials/urls.go b/openstack/identity/v3/credentials/urls.go index 5ec51d1e34..045fa8bf62 100644 --- a/openstack/identity/v3/credentials/urls.go +++ b/openstack/identity/v3/credentials/urls.go @@ -1,6 +1,6 @@ package credentials -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("credentials") diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index bf911d05c7..377a241d6c 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -1,8 +1,8 @@ package domains import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/domains/results.go b/openstack/identity/v3/domains/results.go index 0d927c6db9..58b6ecd424 100644 --- a/openstack/identity/v3/domains/results.go +++ b/openstack/identity/v3/domains/results.go @@ -1,8 +1,8 @@ package domains import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // A Domain is a collection of projects, users, and roles. diff --git a/openstack/identity/v3/domains/testing/fixtures_test.go b/openstack/identity/v3/domains/testing/fixtures_test.go index db328831b2..562d44f819 100644 --- a/openstack/identity/v3/domains/testing/fixtures_test.go +++ b/openstack/identity/v3/domains/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListAvailableOutput provides a single page of available domain results. diff --git a/openstack/identity/v3/domains/testing/requests_test.go b/openstack/identity/v3/domains/testing/requests_test.go index 0f8d6fc7cf..d47b1a178e 100644 --- a/openstack/identity/v3/domains/testing/requests_test.go +++ b/openstack/identity/v3/domains/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/domains" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAvailableDomains(t *testing.T) { diff --git a/openstack/identity/v3/domains/urls.go b/openstack/identity/v3/domains/urls.go index 902532cc39..4e69b29103 100644 --- a/openstack/identity/v3/domains/urls.go +++ b/openstack/identity/v3/domains/urls.go @@ -1,6 +1,6 @@ package domains -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listAvailableURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("auth", "domains") diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 4eba57cd9f..d34de44b27 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -1,8 +1,8 @@ package endpoints import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type CreateOptsBuilder interface { diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index 886e1b4987..9efec30af2 100644 --- a/openstack/identity/v3/endpoints/results.go +++ b/openstack/identity/v3/endpoints/results.go @@ -1,8 +1,8 @@ package endpoints import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index d244ba5ca3..8b01cb4a7c 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/endpoints" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/endpoints" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateSuccessful(t *testing.T) { diff --git a/openstack/identity/v3/endpoints/urls.go b/openstack/identity/v3/endpoints/urls.go index 80cf57eb35..c39761c3db 100644 --- a/openstack/identity/v3/endpoints/urls.go +++ b/openstack/identity/v3/endpoints/urls.go @@ -1,6 +1,6 @@ package endpoints -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("endpoints") diff --git a/openstack/identity/v3/extensions/ec2credentials/requests.go b/openstack/identity/v3/extensions/ec2credentials/requests.go index 93c44f48af..15990e6e8d 100644 --- a/openstack/identity/v3/extensions/ec2credentials/requests.go +++ b/openstack/identity/v3/extensions/ec2credentials/requests.go @@ -1,8 +1,8 @@ package ec2credentials import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List enumerates the Credentials to which the current token has access. diff --git a/openstack/identity/v3/extensions/ec2credentials/results.go b/openstack/identity/v3/extensions/ec2credentials/results.go index bf2d643ecc..6815362169 100644 --- a/openstack/identity/v3/extensions/ec2credentials/results.go +++ b/openstack/identity/v3/extensions/ec2credentials/results.go @@ -1,8 +1,8 @@ package ec2credentials import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Credential represents the application credential object diff --git a/openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go b/openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go index 49aea0a331..c497b6e4b7 100644 --- a/openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go +++ b/openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2credentials" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const userID = "2844b2a08be147a08ef58317d6471f1f" diff --git a/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go b/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go index 3ad394ef8a..0a47fe3a30 100644 --- a/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go +++ b/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2credentials" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2credentials" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListEC2Credentials(t *testing.T) { diff --git a/openstack/identity/v3/extensions/ec2credentials/urls.go b/openstack/identity/v3/extensions/ec2credentials/urls.go index 11f69d32e5..1efe17db2d 100644 --- a/openstack/identity/v3/extensions/ec2credentials/urls.go +++ b/openstack/identity/v3/extensions/ec2credentials/urls.go @@ -1,6 +1,6 @@ package ec2credentials -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient, userID string) string { return client.ServiceURL("users", userID, "credentials", "OS-EC2") diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go index b5b5350c54..86bcde1bd1 100644 --- a/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -13,8 +13,8 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" ) const ( diff --git a/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go b/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go index b4e1e0c657..d5ee9d4a0e 100644 --- a/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go +++ b/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go @@ -7,11 +7,11 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - tokens_testing "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/testing" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + tokens_testing "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/testing" + "github.com/gophercloud/gophercloud/v2/testhelper" ) // authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. diff --git a/openstack/identity/v3/extensions/ec2tokens/urls.go b/openstack/identity/v3/extensions/ec2tokens/urls.go index 84b33b282e..91add91eb0 100644 --- a/openstack/identity/v3/extensions/ec2tokens/urls.go +++ b/openstack/identity/v3/extensions/ec2tokens/urls.go @@ -1,6 +1,6 @@ package ec2tokens -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func ec2tokensURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("ec2tokens") diff --git a/openstack/identity/v3/extensions/federation/requests.go b/openstack/identity/v3/extensions/federation/requests.go index 7563164975..6d3b512dd4 100644 --- a/openstack/identity/v3/extensions/federation/requests.go +++ b/openstack/identity/v3/extensions/federation/requests.go @@ -1,8 +1,8 @@ package federation import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListMappings enumerates the mappings. diff --git a/openstack/identity/v3/extensions/federation/results.go b/openstack/identity/v3/extensions/federation/results.go index f92cdf23c9..f6a0b045f7 100644 --- a/openstack/identity/v3/extensions/federation/results.go +++ b/openstack/identity/v3/extensions/federation/results.go @@ -1,8 +1,8 @@ package federation import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type UserType string diff --git a/openstack/identity/v3/extensions/federation/testing/fixtures_test.go b/openstack/identity/v3/extensions/federation/testing/fixtures_test.go index 41b85ce56b..8a1fd42dce 100644 --- a/openstack/identity/v3/extensions/federation/testing/fixtures_test.go +++ b/openstack/identity/v3/extensions/federation/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/federation" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/federation" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ListOutput = ` diff --git a/openstack/identity/v3/extensions/federation/testing/requests_test.go b/openstack/identity/v3/extensions/federation/testing/requests_test.go index 0b55877673..836cf78939 100644 --- a/openstack/identity/v3/extensions/federation/testing/requests_test.go +++ b/openstack/identity/v3/extensions/federation/testing/requests_test.go @@ -3,10 +3,10 @@ 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" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/federation" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListMappings(t *testing.T) { diff --git a/openstack/identity/v3/extensions/federation/urls.go b/openstack/identity/v3/extensions/federation/urls.go index 07a08ae5eb..23b6ec84f5 100644 --- a/openstack/identity/v3/extensions/federation/urls.go +++ b/openstack/identity/v3/extensions/federation/urls.go @@ -1,6 +1,6 @@ package federation -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "OS-FEDERATION" diff --git a/openstack/identity/v3/extensions/oauth1/requests.go b/openstack/identity/v3/extensions/oauth1/requests.go index d4e045ac63..571f3465e9 100644 --- a/openstack/identity/v3/extensions/oauth1/requests.go +++ b/openstack/identity/v3/extensions/oauth1/requests.go @@ -14,9 +14,9 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Type SignatureMethod is a OAuth1 SignatureMethod type. diff --git a/openstack/identity/v3/extensions/oauth1/results.go b/openstack/identity/v3/extensions/oauth1/results.go index 2a37061627..2ed75bd74a 100644 --- a/openstack/identity/v3/extensions/oauth1/results.go +++ b/openstack/identity/v3/extensions/oauth1/results.go @@ -5,8 +5,8 @@ import ( "net/url" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Consumer represents a delegated authorization request between two diff --git a/openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go b/openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go index b04abb5b32..18dfe05934 100644 --- a/openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go +++ b/openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" - tokens "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/testing" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/oauth1" + tokens "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/testing" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const CreateConsumerRequest = ` diff --git a/openstack/identity/v3/extensions/oauth1/testing/requests_test.go b/openstack/identity/v3/extensions/oauth1/testing/requests_test.go index 108318bd26..99c5d82dfd 100644 --- a/openstack/identity/v3/extensions/oauth1/testing/requests_test.go +++ b/openstack/identity/v3/extensions/oauth1/testing/requests_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/oauth1" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateConsumer(t *testing.T) { diff --git a/openstack/identity/v3/extensions/oauth1/urls.go b/openstack/identity/v3/extensions/oauth1/urls.go index 9b51d53b31..c8dc02e5da 100644 --- a/openstack/identity/v3/extensions/oauth1/urls.go +++ b/openstack/identity/v3/extensions/oauth1/urls.go @@ -1,6 +1,6 @@ package oauth1 -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func consumersURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("OS-OAUTH1", "consumers") diff --git a/openstack/identity/v3/extensions/projectendpoints/requests.go b/openstack/identity/v3/extensions/projectendpoints/requests.go index 771dd8f522..58350b7b02 100644 --- a/openstack/identity/v3/extensions/projectendpoints/requests.go +++ b/openstack/identity/v3/extensions/projectendpoints/requests.go @@ -1,8 +1,8 @@ package projectendpoints import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type CreateOptsBuilder interface { diff --git a/openstack/identity/v3/extensions/projectendpoints/results.go b/openstack/identity/v3/extensions/projectendpoints/results.go index d3c54656aa..54ea988f5a 100644 --- a/openstack/identity/v3/extensions/projectendpoints/results.go +++ b/openstack/identity/v3/extensions/projectendpoints/results.go @@ -1,8 +1,8 @@ package projectendpoints import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateResult is the response from a Create operation. Call its Extract diff --git a/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go b/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go index 1b88765eff..cb451fd01d 100644 --- a/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go +++ b/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/projectendpoints" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateSuccessful(t *testing.T) { diff --git a/openstack/identity/v3/extensions/projectendpoints/urls.go b/openstack/identity/v3/extensions/projectendpoints/urls.go index d72f3f5198..57b081ece7 100644 --- a/openstack/identity/v3/extensions/projectendpoints/urls.go +++ b/openstack/identity/v3/extensions/projectendpoints/urls.go @@ -1,6 +1,6 @@ package projectendpoints -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient, projectID string) string { return client.ServiceURL("OS-EP-FILTER", "projects", projectID, "endpoints") diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index 0677b7d4df..76d4605513 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -3,9 +3,9 @@ package trusts import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/pagination" ) // AuthOptsExt extends the base Identity v3 tokens AuthOpts with a TrustID. diff --git a/openstack/identity/v3/extensions/trusts/results.go b/openstack/identity/v3/extensions/trusts/results.go index d1c73d59f1..b09c617d97 100644 --- a/openstack/identity/v3/extensions/trusts/results.go +++ b/openstack/identity/v3/extensions/trusts/results.go @@ -3,8 +3,8 @@ package trusts import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type trustResult struct { diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go b/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go index 0980cb1034..6de29c1598 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const CreateRequest = ` diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index b2aa1e5ca0..adb7239f7f 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateUserIDPasswordTrustID(t *testing.T) { diff --git a/openstack/identity/v3/extensions/trusts/urls.go b/openstack/identity/v3/extensions/trusts/urls.go index 3b1bf46534..17eb035d48 100644 --- a/openstack/identity/v3/extensions/trusts/urls.go +++ b/openstack/identity/v3/extensions/trusts/urls.go @@ -1,6 +1,6 @@ package trusts -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "OS-TRUST/trusts" diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index 013cf62d29..4ba5adecbe 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -4,8 +4,8 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go index 02bd53a928..64d9a581ba 100644 --- a/openstack/identity/v3/groups/results.go +++ b/openstack/identity/v3/groups/results.go @@ -3,8 +3,8 @@ package groups import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Group helps manage related users. diff --git a/openstack/identity/v3/groups/testing/fixtures_test.go b/openstack/identity/v3/groups/testing/fixtures_test.go index 58f3503785..d20c4c9619 100644 --- a/openstack/identity/v3/groups/testing/fixtures_test.go +++ b/openstack/identity/v3/groups/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of Group results. diff --git a/openstack/identity/v3/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go index 380b9779ad..9656ca8dd2 100644 --- a/openstack/identity/v3/groups/testing/requests_test.go +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListGroups(t *testing.T) { diff --git a/openstack/identity/v3/groups/urls.go b/openstack/identity/v3/groups/urls.go index e7d1e53b27..3c16bcad5a 100644 --- a/openstack/identity/v3/groups/urls.go +++ b/openstack/identity/v3/groups/urls.go @@ -1,6 +1,6 @@ package groups -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("groups") diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go index 5540167b9d..9908a99a5f 100644 --- a/openstack/identity/v3/limits/requests.go +++ b/openstack/identity/v3/limits/requests.go @@ -1,8 +1,8 @@ package limits import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Get retrieves details on a single limit, by ID. diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go index b811a9deb6..d508f25a46 100644 --- a/openstack/identity/v3/limits/results.go +++ b/openstack/identity/v3/limits/results.go @@ -1,8 +1,8 @@ package limits import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // A model describing the configured enforcement model used by the deployment. diff --git a/openstack/identity/v3/limits/testing/fixtures_test.go b/openstack/identity/v3/limits/testing/fixtures_test.go index 0b66f8bc76..b491bc4d76 100644 --- a/openstack/identity/v3/limits/testing/fixtures_test.go +++ b/openstack/identity/v3/limits/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/limits" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const GetEnforcementModelOutput = ` diff --git a/openstack/identity/v3/limits/testing/requests_test.go b/openstack/identity/v3/limits/testing/requests_test.go index ac98bfcc58..743acd3cbb 100644 --- a/openstack/identity/v3/limits/testing/requests_test.go +++ b/openstack/identity/v3/limits/testing/requests_test.go @@ -3,10 +3,10 @@ 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" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/limits" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGetEnforcementModel(t *testing.T) { diff --git a/openstack/identity/v3/limits/urls.go b/openstack/identity/v3/limits/urls.go index ce8ca245e5..3d3ecbe733 100644 --- a/openstack/identity/v3/limits/urls.go +++ b/openstack/identity/v3/limits/urls.go @@ -1,6 +1,6 @@ package limits -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "limits" diff --git a/openstack/identity/v3/osinherit/requests.go b/openstack/identity/v3/osinherit/requests.go index fe261660d8..f527651b4c 100644 --- a/openstack/identity/v3/osinherit/requests.go +++ b/openstack/identity/v3/osinherit/requests.go @@ -1,6 +1,6 @@ package osinherit -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // AssignOpts provides options to assign an inherited role type AssignOpts struct { diff --git a/openstack/identity/v3/osinherit/results.go b/openstack/identity/v3/osinherit/results.go index ffbc83dfea..7fe51c580f 100644 --- a/openstack/identity/v3/osinherit/results.go +++ b/openstack/identity/v3/osinherit/results.go @@ -1,6 +1,6 @@ package osinherit -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // AssignmentResult represents the result of an assign operation. // Call ExtractErr method to determine if the request succeeded or failed. diff --git a/openstack/identity/v3/osinherit/testing/fixtures_test.go b/openstack/identity/v3/osinherit/testing/fixtures_test.go index 49180035bc..e0c33646f6 100644 --- a/openstack/identity/v3/osinherit/testing/fixtures_test.go +++ b/openstack/identity/v3/osinherit/testing/fixtures_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func HandleAssignSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/osinherit/testing/requests_test.go b/openstack/identity/v3/osinherit/testing/requests_test.go index 0c542ba41b..8f8aa01db5 100644 --- a/openstack/identity/v3/osinherit/testing/requests_test.go +++ b/openstack/identity/v3/osinherit/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/osinherit" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/osinherit" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestAssign(t *testing.T) { diff --git a/openstack/identity/v3/osinherit/urls.go b/openstack/identity/v3/osinherit/urls.go index 358dc33ac2..5042e7ea9e 100644 --- a/openstack/identity/v3/osinherit/urls.go +++ b/openstack/identity/v3/osinherit/urls.go @@ -1,6 +1,6 @@ package osinherit -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( inheritPath = "OS-INHERIT" diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index a06b931bca..8f96692891 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -4,8 +4,8 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) const policyTypeMaxLength = 255 diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go index fd15f0eacd..3d298e26c7 100644 --- a/openstack/identity/v3/policies/results.go +++ b/openstack/identity/v3/policies/results.go @@ -3,8 +3,8 @@ package policies import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Policy is an arbitrarily serialized policy engine rule diff --git a/openstack/identity/v3/policies/testing/fixtures_test.go b/openstack/identity/v3/policies/testing/fixtures_test.go index 24bca7e175..ed29f45221 100644 --- a/openstack/identity/v3/policies/testing/fixtures_test.go +++ b/openstack/identity/v3/policies/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of Policy results. diff --git a/openstack/identity/v3/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go index 6348fd57f1..5cefb7775b 100644 --- a/openstack/identity/v3/policies/testing/requests_test.go +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "fmt" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/policies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/policies" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListPolicies(t *testing.T) { diff --git a/openstack/identity/v3/policies/urls.go b/openstack/identity/v3/policies/urls.go index df0d183b43..2c3f290eaa 100644 --- a/openstack/identity/v3/policies/urls.go +++ b/openstack/identity/v3/policies/urls.go @@ -1,6 +1,6 @@ package policies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const policyPath = "policies" diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 3cda901523..0b81fd548d 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -4,8 +4,8 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/projects/results.go b/openstack/identity/v3/projects/results.go index 8025c8593a..23f482f14a 100644 --- a/openstack/identity/v3/projects/results.go +++ b/openstack/identity/v3/projects/results.go @@ -3,8 +3,8 @@ package projects import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Option is a specific option defined at the API to enable features diff --git a/openstack/identity/v3/projects/testing/fixtures_test.go b/openstack/identity/v3/projects/testing/fixtures_test.go index 009a2f07ac..f78d2660ef 100644 --- a/openstack/identity/v3/projects/testing/fixtures_test.go +++ b/openstack/identity/v3/projects/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListAvailableOutput provides a single page of available Project results. diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 47420ac9f7..a45eb24a37 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAvailableProjects(t *testing.T) { diff --git a/openstack/identity/v3/projects/urls.go b/openstack/identity/v3/projects/urls.go index 1b1d34f924..fc22c02dc5 100644 --- a/openstack/identity/v3/projects/urls.go +++ b/openstack/identity/v3/projects/urls.go @@ -1,6 +1,6 @@ package projects -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listAvailableURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("auth", "projects") diff --git a/openstack/identity/v3/regions/requests.go b/openstack/identity/v3/regions/requests.go index d120381ab9..2cc3dd252e 100644 --- a/openstack/identity/v3/regions/requests.go +++ b/openstack/identity/v3/regions/requests.go @@ -1,8 +1,8 @@ package regions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go index f72ccac09d..751c7ce5fc 100644 --- a/openstack/identity/v3/regions/results.go +++ b/openstack/identity/v3/regions/results.go @@ -3,8 +3,8 @@ package regions import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Region helps manage related users. diff --git a/openstack/identity/v3/regions/testing/fixtures_test.go b/openstack/identity/v3/regions/testing/fixtures_test.go index dee57c52af..665a336de0 100644 --- a/openstack/identity/v3/regions/testing/fixtures_test.go +++ b/openstack/identity/v3/regions/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/regions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of Region results. diff --git a/openstack/identity/v3/regions/testing/requests_test.go b/openstack/identity/v3/regions/testing/requests_test.go index 7210121c3b..e89450d42c 100644 --- a/openstack/identity/v3/regions/testing/requests_test.go +++ b/openstack/identity/v3/regions/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/regions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/regions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListRegions(t *testing.T) { diff --git a/openstack/identity/v3/regions/urls.go b/openstack/identity/v3/regions/urls.go index 150ecc8358..c36ffeddd4 100644 --- a/openstack/identity/v3/regions/urls.go +++ b/openstack/identity/v3/regions/urls.go @@ -1,6 +1,6 @@ package regions -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("regions") diff --git a/openstack/identity/v3/registeredlimits/requests.go b/openstack/identity/v3/registeredlimits/requests.go index 8f6a0fdd30..8785611e10 100644 --- a/openstack/identity/v3/registeredlimits/requests.go +++ b/openstack/identity/v3/registeredlimits/requests.go @@ -1,8 +1,8 @@ package registeredlimits import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/registeredlimits/results.go b/openstack/identity/v3/registeredlimits/results.go index c10707154c..80d742cedd 100644 --- a/openstack/identity/v3/registeredlimits/results.go +++ b/openstack/identity/v3/registeredlimits/results.go @@ -1,8 +1,8 @@ package registeredlimits import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // A model describing the configured enforcement model used by the deployment. diff --git a/openstack/identity/v3/registeredlimits/testing/fixtures_test.go b/openstack/identity/v3/registeredlimits/testing/fixtures_test.go index 4e92bb763e..1e865ceb4a 100644 --- a/openstack/identity/v3/registeredlimits/testing/fixtures_test.go +++ b/openstack/identity/v3/registeredlimits/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/registeredlimits" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/registeredlimits" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of List results. diff --git a/openstack/identity/v3/registeredlimits/testing/requests_test.go b/openstack/identity/v3/registeredlimits/testing/requests_test.go index b4476176ee..1a93ee09e9 100644 --- a/openstack/identity/v3/registeredlimits/testing/requests_test.go +++ b/openstack/identity/v3/registeredlimits/testing/requests_test.go @@ -3,10 +3,10 @@ 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" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/registeredlimits" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListRegisteredLimits(t *testing.T) { diff --git a/openstack/identity/v3/registeredlimits/urls.go b/openstack/identity/v3/registeredlimits/urls.go index c08949bcc8..3b1123150b 100644 --- a/openstack/identity/v3/registeredlimits/urls.go +++ b/openstack/identity/v3/registeredlimits/urls.go @@ -1,6 +1,6 @@ package registeredlimits -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "registered_limits" diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 2ee925f671..e8248a0e1d 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -4,8 +4,8 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 9fde609461..e6948bb7fd 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -3,8 +3,8 @@ package roles import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Role grants permissions to a user. diff --git a/openstack/identity/v3/roles/testing/fixtures_test.go b/openstack/identity/v3/roles/testing/fixtures_test.go index 65809d1c50..f64e7cbec4 100644 --- a/openstack/identity/v3/roles/testing/fixtures_test.go +++ b/openstack/identity/v3/roles/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of Role results. diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 229cfcf7d1..0bbf10d5b2 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/roles" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListRoles(t *testing.T) { diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index 2b82011424..8bc46ac312 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -1,6 +1,6 @@ package roles -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rolePath = "roles" diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index 6d3c70c51d..4a32f5987c 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -1,8 +1,8 @@ package services import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index 98bb4449bb..1426644897 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -3,8 +3,8 @@ package services import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type serviceResult struct { diff --git a/openstack/identity/v3/services/testing/fixtures_test.go b/openstack/identity/v3/services/testing/fixtures_test.go index fd1d632425..339cbcdbe5 100644 --- a/openstack/identity/v3/services/testing/fixtures_test.go +++ b/openstack/identity/v3/services/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/services" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of Service results. diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index 2a8a89dd24..4f42a7b10c 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/services" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateSuccessful(t *testing.T) { diff --git a/openstack/identity/v3/services/urls.go b/openstack/identity/v3/services/urls.go index caa625a208..b130e6e1a6 100644 --- a/openstack/identity/v3/services/urls.go +++ b/openstack/identity/v3/services/urls.go @@ -1,6 +1,6 @@ package services -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("services") diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index d5b2fe83fd..e250a7582e 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -3,7 +3,7 @@ package tokens import ( "context" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Scope allows a created token to be limited to a specific domain or project. diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index f1e17e9f75..ea4900e910 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -3,7 +3,7 @@ package tokens import ( "time" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Endpoint represents a single API endpoint offered by a service. diff --git a/openstack/identity/v3/tokens/testing/fixtures.go b/openstack/identity/v3/tokens/testing/fixtures.go index 40811fb62b..0b4ccafa26 100644 --- a/openstack/identity/v3/tokens/testing/fixtures.go +++ b/openstack/identity/v3/tokens/testing/fixtures.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/testhelper" ) const testTokenID = "130f6c17-420e-4a0b-97b0-0c9cf2a05f30" diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index cfa087b984..e6fe6c4086 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/testhelper" ) // authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. diff --git a/openstack/identity/v3/tokens/testing/results_test.go b/openstack/identity/v3/tokens/testing/results_test.go index c889818878..e142ac9c57 100644 --- a/openstack/identity/v3/tokens/testing/results_test.go +++ b/openstack/identity/v3/tokens/testing/results_test.go @@ -3,7 +3,7 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestExtractToken(t *testing.T) { diff --git a/openstack/identity/v3/tokens/urls.go b/openstack/identity/v3/tokens/urls.go index 2f864a31c8..2218c107fb 100644 --- a/openstack/identity/v3/tokens/urls.go +++ b/openstack/identity/v3/tokens/urls.go @@ -1,6 +1,6 @@ package tokens -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func tokenURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("auth", "tokens") diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 0f1eb117bd..644faab82f 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -4,10 +4,10 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" - "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Option is a specific option defined at the API to enable features diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 83de3c8bac..8abd6965b3 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // User represents a User in the OpenStack Identity Service. diff --git a/openstack/identity/v3/users/testing/fixtures_test.go b/openstack/identity/v3/users/testing/fixtures_test.go index 4b20a22e64..621c0a67cf 100644 --- a/openstack/identity/v3/users/testing/fixtures_test.go +++ b/openstack/identity/v3/users/testing/fixtures_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" - "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" - "github.com/gophercloud/gophercloud/openstack/identity/v3/users" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/users" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListOutput provides a single page of User results. diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index 3eb1b46f5b..20f568f66a 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -3,12 +3,12 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/identity/v3/groups" - "github.com/gophercloud/gophercloud/openstack/identity/v3/projects" - "github.com/gophercloud/gophercloud/openstack/identity/v3/users" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/users" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListUsers(t *testing.T) { diff --git a/openstack/identity/v3/users/urls.go b/openstack/identity/v3/users/urls.go index 3caa8bbb6c..4625650779 100644 --- a/openstack/identity/v3/users/urls.go +++ b/openstack/identity/v3/users/urls.go @@ -1,6 +1,6 @@ package users -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("users") diff --git a/openstack/imageservice/v2/imagedata/requests.go b/openstack/imageservice/v2/imagedata/requests.go index 820a0c6993..48992e1a70 100644 --- a/openstack/imageservice/v2/imagedata/requests.go +++ b/openstack/imageservice/v2/imagedata/requests.go @@ -3,7 +3,7 @@ package imagedata import ( "io" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Upload uploads an image file. diff --git a/openstack/imageservice/v2/imagedata/results.go b/openstack/imageservice/v2/imagedata/results.go index d9632f27b2..27f41b7df5 100644 --- a/openstack/imageservice/v2/imagedata/results.go +++ b/openstack/imageservice/v2/imagedata/results.go @@ -3,7 +3,7 @@ package imagedata import ( "io" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // UploadResult is the result of an upload image operation. Call its ExtractErr diff --git a/openstack/imageservice/v2/imagedata/testing/fixtures_test.go b/openstack/imageservice/v2/imagedata/testing/fixtures_test.go index 408b146912..fa3b4af781 100644 --- a/openstack/imageservice/v2/imagedata/testing/fixtures_test.go +++ b/openstack/imageservice/v2/imagedata/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HandlePutImageDataSuccessfully setup diff --git a/openstack/imageservice/v2/imagedata/testing/requests_test.go b/openstack/imageservice/v2/imagedata/testing/requests_test.go index af06256571..03f199906e 100644 --- a/openstack/imageservice/v2/imagedata/testing/requests_test.go +++ b/openstack/imageservice/v2/imagedata/testing/requests_test.go @@ -5,9 +5,9 @@ import ( "io" "testing" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/imagedata" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestUpload(t *testing.T) { diff --git a/openstack/imageservice/v2/imagedata/urls.go b/openstack/imageservice/v2/imagedata/urls.go index d9615ba7fb..21b7cceb32 100644 --- a/openstack/imageservice/v2/imagedata/urls.go +++ b/openstack/imageservice/v2/imagedata/urls.go @@ -1,6 +1,6 @@ package imagedata -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "images" diff --git a/openstack/imageservice/v2/imageimport/requests.go b/openstack/imageservice/v2/imageimport/requests.go index 118d36ea8c..4468b06ff8 100644 --- a/openstack/imageservice/v2/imageimport/requests.go +++ b/openstack/imageservice/v2/imageimport/requests.go @@ -1,6 +1,6 @@ package imageimport -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // ImportMethod represents valid Import API method. type ImportMethod string diff --git a/openstack/imageservice/v2/imageimport/results.go b/openstack/imageservice/v2/imageimport/results.go index 2158c20da6..d5f1dbab85 100644 --- a/openstack/imageservice/v2/imageimport/results.go +++ b/openstack/imageservice/v2/imageimport/results.go @@ -1,6 +1,6 @@ package imageimport -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" type commonResult struct { gophercloud.Result diff --git a/openstack/imageservice/v2/imageimport/testing/requests_test.go b/openstack/imageservice/v2/imageimport/testing/requests_test.go index d0c98df698..1e1d2660a3 100644 --- a/openstack/imageservice/v2/imageimport/testing/requests_test.go +++ b/openstack/imageservice/v2/imageimport/testing/requests_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/imageimport" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGet(t *testing.T) { diff --git a/openstack/imageservice/v2/imageimport/urls.go b/openstack/imageservice/v2/imageimport/urls.go index 20310eb093..797b005498 100644 --- a/openstack/imageservice/v2/imageimport/urls.go +++ b/openstack/imageservice/v2/imageimport/urls.go @@ -1,6 +1,6 @@ package imageimport -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "images" diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 2ab609cbca..21348a1918 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -5,8 +5,8 @@ import ( "net/url" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/imageservice/v2/images/results.go b/openstack/imageservice/v2/images/results.go index 96fd91a2ca..96644a9aeb 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/imageservice/v2/images/results.go @@ -7,8 +7,8 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Image represents an image found in the OpenStack Image service. diff --git a/openstack/imageservice/v2/images/testing/fixtures_test.go b/openstack/imageservice/v2/images/testing/fixtures_test.go index 6627cf1182..0c9a7b0318 100644 --- a/openstack/imageservice/v2/images/testing/fixtures_test.go +++ b/openstack/imageservice/v2/images/testing/fixtures_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) type imageEntry struct { diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index e349777642..a4c70d47c4 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/images" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListImage(t *testing.T) { diff --git a/openstack/imageservice/v2/images/urls.go b/openstack/imageservice/v2/images/urls.go index 1780c3c6ca..c3007a6129 100644 --- a/openstack/imageservice/v2/images/urls.go +++ b/openstack/imageservice/v2/images/urls.go @@ -4,8 +4,8 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) // `listURL` is a pure function. `listURL(c)` is a URL for which a GET diff --git a/openstack/imageservice/v2/members/requests.go b/openstack/imageservice/v2/members/requests.go index 8d1c29e780..d202dafe89 100644 --- a/openstack/imageservice/v2/members/requests.go +++ b/openstack/imageservice/v2/members/requests.go @@ -1,8 +1,8 @@ package members import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) /* diff --git a/openstack/imageservice/v2/members/results.go b/openstack/imageservice/v2/members/results.go index 8996635b6d..44e53568d6 100644 --- a/openstack/imageservice/v2/members/results.go +++ b/openstack/imageservice/v2/members/results.go @@ -3,8 +3,8 @@ package members import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Member represents a member of an Image. diff --git a/openstack/imageservice/v2/members/testing/fixtures_test.go b/openstack/imageservice/v2/members/testing/fixtures_test.go index c08fc5ebaa..4100b186f4 100644 --- a/openstack/imageservice/v2/members/testing/fixtures_test.go +++ b/openstack/imageservice/v2/members/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HandleCreateImageMemberSuccessfully setup diff --git a/openstack/imageservice/v2/members/testing/requests_test.go b/openstack/imageservice/v2/members/testing/requests_test.go index 04624c9937..e84a732306 100644 --- a/openstack/imageservice/v2/members/testing/requests_test.go +++ b/openstack/imageservice/v2/members/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/members" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/members" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const createdAtString = "2013-09-20T19:22:19Z" diff --git a/openstack/imageservice/v2/members/urls.go b/openstack/imageservice/v2/members/urls.go index 0898364e7d..8ea3cc0e30 100644 --- a/openstack/imageservice/v2/members/urls.go +++ b/openstack/imageservice/v2/members/urls.go @@ -1,6 +1,6 @@ package members -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func imageMembersURL(c *gophercloud.ServiceClient, imageID string) string { return c.ServiceURL("images", imageID, "members") diff --git a/openstack/imageservice/v2/tasks/requests.go b/openstack/imageservice/v2/tasks/requests.go index d0a2be1fa2..ebeb10d0f6 100644 --- a/openstack/imageservice/v2/tasks/requests.go +++ b/openstack/imageservice/v2/tasks/requests.go @@ -1,8 +1,8 @@ package tasks import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // TaskStatus represents valid task status. diff --git a/openstack/imageservice/v2/tasks/results.go b/openstack/imageservice/v2/tasks/results.go index 04df85928a..d6471ea8af 100644 --- a/openstack/imageservice/v2/tasks/results.go +++ b/openstack/imageservice/v2/tasks/results.go @@ -3,8 +3,8 @@ package tasks import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/imageservice/v2/tasks/testing/fixtures_test.go b/openstack/imageservice/v2/tasks/testing/fixtures_test.go index 6a1a9d3e93..fc42383c92 100644 --- a/openstack/imageservice/v2/tasks/testing/fixtures_test.go +++ b/openstack/imageservice/v2/tasks/testing/fixtures_test.go @@ -3,7 +3,7 @@ package testing import ( "time" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/tasks" ) // TasksListResult represents raw server response from a server to a list call. diff --git a/openstack/imageservice/v2/tasks/testing/requests_test.go b/openstack/imageservice/v2/tasks/testing/requests_test.go index bc9af17aca..95c29c1242 100644 --- a/openstack/imageservice/v2/tasks/testing/requests_test.go +++ b/openstack/imageservice/v2/tasks/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/tasks" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fakeclient "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/imageservice/v2/tasks/urls.go b/openstack/imageservice/v2/tasks/urls.go index 8133f38356..aa79479396 100644 --- a/openstack/imageservice/v2/tasks/urls.go +++ b/openstack/imageservice/v2/tasks/urls.go @@ -4,8 +4,8 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) const resourcePath = "tasks" diff --git a/openstack/keymanager/v1/acls/requests.go b/openstack/keymanager/v1/acls/requests.go index 2eb3b58038..82a682a475 100644 --- a/openstack/keymanager/v1/acls/requests.go +++ b/openstack/keymanager/v1/acls/requests.go @@ -1,7 +1,7 @@ package acls import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // GetContainerACL retrieves the ACL of a container. diff --git a/openstack/keymanager/v1/acls/results.go b/openstack/keymanager/v1/acls/results.go index 959742da95..d9ad1a1bb8 100644 --- a/openstack/keymanager/v1/acls/results.go +++ b/openstack/keymanager/v1/acls/results.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ACL represents an ACL on a resource. diff --git a/openstack/keymanager/v1/acls/testing/fixtures_test.go b/openstack/keymanager/v1/acls/testing/fixtures_test.go index 7ae5b5971c..7db2b37b4f 100644 --- a/openstack/keymanager/v1/acls/testing/fixtures_test.go +++ b/openstack/keymanager/v1/acls/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/acls" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const GetResponse = ` diff --git a/openstack/keymanager/v1/acls/testing/requests_test.go b/openstack/keymanager/v1/acls/testing/requests_test.go index febac2b38d..f7b46e0c81 100644 --- a/openstack/keymanager/v1/acls/testing/requests_test.go +++ b/openstack/keymanager/v1/acls/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/keymanager/v1/acls" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/acls" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGetSecretACL(t *testing.T) { diff --git a/openstack/keymanager/v1/acls/urls.go b/openstack/keymanager/v1/acls/urls.go index 5cb4439a8c..0b7e8c4620 100644 --- a/openstack/keymanager/v1/acls/urls.go +++ b/openstack/keymanager/v1/acls/urls.go @@ -1,6 +1,6 @@ package acls -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func containerURL(client *gophercloud.ServiceClient, containerID string) string { return client.ServiceURL("containers", containerID, "acl") diff --git a/openstack/keymanager/v1/containers/requests.go b/openstack/keymanager/v1/containers/requests.go index 4d55a7aa8c..48bdfad5a9 100644 --- a/openstack/keymanager/v1/containers/requests.go +++ b/openstack/keymanager/v1/containers/requests.go @@ -1,8 +1,8 @@ package containers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ContainerType represents the valid types of containers. diff --git a/openstack/keymanager/v1/containers/results.go b/openstack/keymanager/v1/containers/results.go index c89a960958..2ea8c3e258 100644 --- a/openstack/keymanager/v1/containers/results.go +++ b/openstack/keymanager/v1/containers/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Container represents a container in the key manager service. diff --git a/openstack/keymanager/v1/containers/testing/fixtures_test.go b/openstack/keymanager/v1/containers/testing/fixtures_test.go index 3c73e38b1c..4b3997f786 100644 --- a/openstack/keymanager/v1/containers/testing/fixtures_test.go +++ b/openstack/keymanager/v1/containers/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/containers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListResponse provides a single page of container results. diff --git a/openstack/keymanager/v1/containers/testing/requests_test.go b/openstack/keymanager/v1/containers/testing/requests_test.go index 2f405e3c55..e46f2e9691 100644 --- a/openstack/keymanager/v1/containers/testing/requests_test.go +++ b/openstack/keymanager/v1/containers/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/containers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListContainers(t *testing.T) { diff --git a/openstack/keymanager/v1/containers/urls.go b/openstack/keymanager/v1/containers/urls.go index 8077e459ca..1b95b33408 100644 --- a/openstack/keymanager/v1/containers/urls.go +++ b/openstack/keymanager/v1/containers/urls.go @@ -1,6 +1,6 @@ package containers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("containers") diff --git a/openstack/keymanager/v1/orders/requests.go b/openstack/keymanager/v1/orders/requests.go index 063189ef80..9aaea4901c 100644 --- a/openstack/keymanager/v1/orders/requests.go +++ b/openstack/keymanager/v1/orders/requests.go @@ -3,8 +3,8 @@ package orders import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // OrderType represents the valid types of orders. diff --git a/openstack/keymanager/v1/orders/results.go b/openstack/keymanager/v1/orders/results.go index fa67c12dda..3cd3a2ec11 100644 --- a/openstack/keymanager/v1/orders/results.go +++ b/openstack/keymanager/v1/orders/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Order represents an order in the key manager service. diff --git a/openstack/keymanager/v1/orders/testing/fixtures_test.go b/openstack/keymanager/v1/orders/testing/fixtures_test.go index bdab5427b0..636fe7eb3f 100644 --- a/openstack/keymanager/v1/orders/testing/fixtures_test.go +++ b/openstack/keymanager/v1/orders/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/orders" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListResponse provides a single page of RESOURCE results. diff --git a/openstack/keymanager/v1/orders/testing/requests_test.go b/openstack/keymanager/v1/orders/testing/requests_test.go index 5c9fcb3133..21f7c726be 100644 --- a/openstack/keymanager/v1/orders/testing/requests_test.go +++ b/openstack/keymanager/v1/orders/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/orders" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListOrders(t *testing.T) { diff --git a/openstack/keymanager/v1/orders/urls.go b/openstack/keymanager/v1/orders/urls.go index 36890e533a..98e7e94436 100644 --- a/openstack/keymanager/v1/orders/urls.go +++ b/openstack/keymanager/v1/orders/urls.go @@ -1,6 +1,6 @@ package orders -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("orders") diff --git a/openstack/keymanager/v1/secrets/requests.go b/openstack/keymanager/v1/secrets/requests.go index 35bc21fa6d..55315e8b19 100644 --- a/openstack/keymanager/v1/secrets/requests.go +++ b/openstack/keymanager/v1/secrets/requests.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // DateFilter represents a valid filter to use for filtering diff --git a/openstack/keymanager/v1/secrets/results.go b/openstack/keymanager/v1/secrets/results.go index 15c58ad98a..82facc0dee 100644 --- a/openstack/keymanager/v1/secrets/results.go +++ b/openstack/keymanager/v1/secrets/results.go @@ -5,8 +5,8 @@ import ( "io" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Secret represents a secret stored in the key manager service. diff --git a/openstack/keymanager/v1/secrets/testing/fixtures_test.go b/openstack/keymanager/v1/secrets/testing/fixtures_test.go index eb3e8ddafb..ff13102354 100644 --- a/openstack/keymanager/v1/secrets/testing/fixtures_test.go +++ b/openstack/keymanager/v1/secrets/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/secrets" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListResponse provides a single page of RESOURCE results. diff --git a/openstack/keymanager/v1/secrets/testing/requests_test.go b/openstack/keymanager/v1/secrets/testing/requests_test.go index 0b486f800d..98383e0846 100644 --- a/openstack/keymanager/v1/secrets/testing/requests_test.go +++ b/openstack/keymanager/v1/secrets/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/secrets" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListSecrets(t *testing.T) { diff --git a/openstack/keymanager/v1/secrets/urls.go b/openstack/keymanager/v1/secrets/urls.go index ebd636a66e..fb076f9d37 100644 --- a/openstack/keymanager/v1/secrets/urls.go +++ b/openstack/keymanager/v1/secrets/urls.go @@ -1,6 +1,6 @@ package secrets -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("secrets") diff --git a/openstack/loadbalancer/v2/amphorae/requests.go b/openstack/loadbalancer/v2/amphorae/requests.go index 4b4fd0a90c..3e94d5ba8f 100644 --- a/openstack/loadbalancer/v2/amphorae/requests.go +++ b/openstack/loadbalancer/v2/amphorae/requests.go @@ -1,8 +1,8 @@ package amphorae import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/loadbalancer/v2/amphorae/results.go b/openstack/loadbalancer/v2/amphorae/results.go index edbddc63b4..113fe4a9bf 100644 --- a/openstack/loadbalancer/v2/amphorae/results.go +++ b/openstack/loadbalancer/v2/amphorae/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Amphora is virtual machine, container, dedicated hardware, appliance or device that actually performs the task of diff --git a/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go b/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go index 60d13c35ea..4beea47591 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/amphorae" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // AmphoraeListBody contains the canned body of a amphora list response. diff --git a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go index 5ade025c72..cfa303e643 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go +++ b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/amphorae" - fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/amphorae" + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListAmphorae(t *testing.T) { diff --git a/openstack/loadbalancer/v2/amphorae/urls.go b/openstack/loadbalancer/v2/amphorae/urls.go index bab8dfdbe2..1d579f74d0 100644 --- a/openstack/loadbalancer/v2/amphorae/urls.go +++ b/openstack/loadbalancer/v2/amphorae/urls.go @@ -1,6 +1,6 @@ package amphorae -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "octavia" diff --git a/openstack/loadbalancer/v2/apiversions/requests.go b/openstack/loadbalancer/v2/apiversions/requests.go index 18c88de077..1a08dc4880 100644 --- a/openstack/loadbalancer/v2/apiversions/requests.go +++ b/openstack/loadbalancer/v2/apiversions/requests.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List lists all the load balancer API versions available to end-users. diff --git a/openstack/loadbalancer/v2/apiversions/results.go b/openstack/loadbalancer/v2/apiversions/results.go index dce4aa5d14..4d3d1ae222 100644 --- a/openstack/loadbalancer/v2/apiversions/results.go +++ b/openstack/loadbalancer/v2/apiversions/results.go @@ -1,6 +1,6 @@ package apiversions -import "github.com/gophercloud/gophercloud/pagination" +import "github.com/gophercloud/gophercloud/v2/pagination" // APIVersion represents an API version for load balancer. It contains // the status of the API, and its unique ID. diff --git a/openstack/loadbalancer/v2/apiversions/testing/fixture.go b/openstack/loadbalancer/v2/apiversions/testing/fixture.go index a4fcfa536f..d21507dbab 100644 --- a/openstack/loadbalancer/v2/apiversions/testing/fixture.go +++ b/openstack/loadbalancer/v2/apiversions/testing/fixture.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const OctaviaAllAPIVersionsResponse = ` diff --git a/openstack/loadbalancer/v2/apiversions/testing/requests_test.go b/openstack/loadbalancer/v2/apiversions/testing/requests_test.go index 016c64da72..fbca8f9c51 100644 --- a/openstack/loadbalancer/v2/apiversions/testing/requests_test.go +++ b/openstack/loadbalancer/v2/apiversions/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListVersions(t *testing.T) { diff --git a/openstack/loadbalancer/v2/apiversions/urls.go b/openstack/loadbalancer/v2/apiversions/urls.go index a6a35d4225..deaf717651 100644 --- a/openstack/loadbalancer/v2/apiversions/urls.go +++ b/openstack/loadbalancer/v2/apiversions/urls.go @@ -3,8 +3,8 @@ package apiversions import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) func listURL(c *gophercloud.ServiceClient) string { diff --git a/openstack/loadbalancer/v2/flavorprofiles/requests.go b/openstack/loadbalancer/v2/flavorprofiles/requests.go index 886fb5450d..52ed895e73 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/requests.go +++ b/openstack/loadbalancer/v2/flavorprofiles/requests.go @@ -1,8 +1,8 @@ package flavorprofiles import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/loadbalancer/v2/flavorprofiles/results.go b/openstack/loadbalancer/v2/flavorprofiles/results.go index a4d7c11c19..1dd6b07ff8 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/results.go +++ b/openstack/loadbalancer/v2/flavorprofiles/results.go @@ -1,8 +1,8 @@ package flavorprofiles import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // FlavorProfile provide metadata such as provider, toplogy and instance flavor. diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go b/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go index 35c341c576..2ed07d52e7 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavorprofiles" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const FlavorProfilesListBody = ` diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go b/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go index 1b30e2ff07..d977387f7e 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavorprofiles" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" + "github.com/gophercloud/gophercloud/v2/pagination" - fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListFlavorProfiles(t *testing.T) { diff --git a/openstack/loadbalancer/v2/flavorprofiles/urls.go b/openstack/loadbalancer/v2/flavorprofiles/urls.go index 6125d77923..6a165b2aa2 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/urls.go +++ b/openstack/loadbalancer/v2/flavorprofiles/urls.go @@ -1,6 +1,6 @@ package flavorprofiles -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go index 0b9509c320..8b5e5e0aeb 100644 --- a/openstack/loadbalancer/v2/flavors/requests.go +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -1,8 +1,8 @@ package flavors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/loadbalancer/v2/flavors/results.go b/openstack/loadbalancer/v2/flavors/results.go index 21c517154d..105a6cfec4 100644 --- a/openstack/loadbalancer/v2/flavors/results.go +++ b/openstack/loadbalancer/v2/flavors/results.go @@ -1,8 +1,8 @@ package flavors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Flavor provide specs for the creation of a load balancer. diff --git a/openstack/loadbalancer/v2/flavors/testing/fixtures.go b/openstack/loadbalancer/v2/flavors/testing/fixtures.go index 42e2fc96a0..dac0e584be 100644 --- a/openstack/loadbalancer/v2/flavors/testing/fixtures.go +++ b/openstack/loadbalancer/v2/flavors/testing/fixtures.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavors" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const FlavorsListBody = ` diff --git a/openstack/loadbalancer/v2/flavors/testing/requests_test.go b/openstack/loadbalancer/v2/flavors/testing/requests_test.go index b04f3056f8..907cd8d696 100644 --- a/openstack/loadbalancer/v2/flavors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavors/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/flavors" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" + "github.com/gophercloud/gophercloud/v2/pagination" - fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListFlavors(t *testing.T) { diff --git a/openstack/loadbalancer/v2/flavors/urls.go b/openstack/loadbalancer/v2/flavors/urls.go index 5d9a84b1ff..1bfdfef877 100644 --- a/openstack/loadbalancer/v2/flavors/urls.go +++ b/openstack/loadbalancer/v2/flavors/urls.go @@ -1,6 +1,6 @@ package flavors -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index 86e20c0faa..d32335a2ae 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -1,8 +1,8 @@ package l7policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/loadbalancer/v2/l7policies/results.go b/openstack/loadbalancer/v2/l7policies/results.go index cd62d5d4b6..214d59d121 100644 --- a/openstack/loadbalancer/v2/l7policies/results.go +++ b/openstack/loadbalancer/v2/l7policies/results.go @@ -1,8 +1,8 @@ package l7policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // L7Policy is a collection of L7 rules associated with a Listener, and which diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go index fea5bad570..a2569f8489 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // SingleL7PolicyBody is the canned body of a Get request on an existing l7policy. diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index 76a10c55f2..9d2a64a779 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreateL7Policy(t *testing.T) { diff --git a/openstack/loadbalancer/v2/l7policies/urls.go b/openstack/loadbalancer/v2/l7policies/urls.go index ecb607a8e8..57126a8811 100644 --- a/openstack/loadbalancer/v2/l7policies/urls.go +++ b/openstack/loadbalancer/v2/l7policies/urls.go @@ -1,6 +1,6 @@ package l7policies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index e71696030b..669b3ed07b 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -1,10 +1,10 @@ package listeners import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Type Protocol represents a listener protocol. diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 234b6cb062..562a7618dd 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -1,10 +1,10 @@ package listeners import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + "github.com/gophercloud/gophercloud/v2/pagination" ) type LoadBalancerID struct { diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go b/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go index 3efdf0af31..305a9c87e3 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListenersListBody contains the canned body of a listeners list response. diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 21fc0a4bea..600d9178d6 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" - fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListListeners(t *testing.T) { diff --git a/openstack/loadbalancer/v2/listeners/urls.go b/openstack/loadbalancer/v2/listeners/urls.go index e9e3bccd3e..77157c726c 100644 --- a/openstack/loadbalancer/v2/listeners/urls.go +++ b/openstack/loadbalancer/v2/listeners/urls.go @@ -1,6 +1,6 @@ package listeners -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 52669925ee..577f1c29b7 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -1,10 +1,10 @@ package loadbalancers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/loadbalancer/v2/loadbalancers/results.go b/openstack/loadbalancer/v2/loadbalancers/results.go index acf3a319dd..67e5c749df 100644 --- a/openstack/loadbalancer/v2/loadbalancers/results.go +++ b/openstack/loadbalancer/v2/loadbalancers/results.go @@ -4,10 +4,10 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + "github.com/gophercloud/gophercloud/v2/pagination" ) // LoadBalancer is the primary load balancing configuration object that diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go index 948ed4ef61..e3604da29a 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/loadbalancers" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // LoadbalancersListBody contains the canned body of a loadbalancer list response. diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 50ede06d3e..0a9f2c6149 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -3,16 +3,16 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" - fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/loadbalancers" + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListLoadbalancers(t *testing.T) { diff --git a/openstack/loadbalancer/v2/loadbalancers/urls.go b/openstack/loadbalancer/v2/loadbalancers/urls.go index 7b184e35f6..221bc84e3d 100644 --- a/openstack/loadbalancer/v2/loadbalancers/urls.go +++ b/openstack/loadbalancer/v2/loadbalancers/urls.go @@ -1,6 +1,6 @@ package loadbalancers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index 6051c70978..4d3dec2434 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -3,8 +3,8 @@ package monitors import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index f1060b2462..3a29da9f40 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -1,8 +1,8 @@ package monitors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type PoolID struct { diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go index d097e49084..379dc8c6ee 100644 --- a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HealthmonitorsListBody contains the canned body of a healthmonitor list response. diff --git a/openstack/loadbalancer/v2/monitors/testing/requests_test.go b/openstack/loadbalancer/v2/monitors/testing/requests_test.go index 9a3b82d647..bd8da6aa46 100644 --- a/openstack/loadbalancer/v2/monitors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" - fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListHealthmonitors(t *testing.T) { diff --git a/openstack/loadbalancer/v2/monitors/urls.go b/openstack/loadbalancer/v2/monitors/urls.go index a222e52a93..d5723a305f 100644 --- a/openstack/loadbalancer/v2/monitors/urls.go +++ b/openstack/loadbalancer/v2/monitors/urls.go @@ -1,6 +1,6 @@ package monitors -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 08c47a44d3..494bc9e6f7 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -1,9 +1,9 @@ package pools import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index 44379b6f38..164582315c 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -4,9 +4,9 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SessionPersistence represents the session persistence feature of the load diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures_test.go b/openstack/loadbalancer/v2/pools/testing/fixtures_test.go index 3dd296ac87..0544ae9eab 100644 --- a/openstack/loadbalancer/v2/pools/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/pools/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // PoolsListBody contains the canned body of a pool list response. diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go index 2b88cb2cfd..35a1ab2658 100644 --- a/openstack/loadbalancer/v2/pools/testing/requests_test.go +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" - fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListPools(t *testing.T) { diff --git a/openstack/loadbalancer/v2/pools/urls.go b/openstack/loadbalancer/v2/pools/urls.go index e7443c4f19..a362f1b957 100644 --- a/openstack/loadbalancer/v2/pools/urls.go +++ b/openstack/loadbalancer/v2/pools/urls.go @@ -1,6 +1,6 @@ package pools -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/loadbalancer/v2/providers/requests.go b/openstack/loadbalancer/v2/providers/requests.go index f386276483..8b8e07c960 100644 --- a/openstack/loadbalancer/v2/providers/requests.go +++ b/openstack/loadbalancer/v2/providers/requests.go @@ -1,8 +1,8 @@ package providers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/loadbalancer/v2/providers/results.go b/openstack/loadbalancer/v2/providers/results.go index 509f815fed..ec374faee6 100644 --- a/openstack/loadbalancer/v2/providers/results.go +++ b/openstack/loadbalancer/v2/providers/results.go @@ -1,8 +1,8 @@ package providers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Provider is the Octavia driver that implements the load balancing mechanism diff --git a/openstack/loadbalancer/v2/providers/testing/fixtures_test.go b/openstack/loadbalancer/v2/providers/testing/fixtures_test.go index 95753a45b9..d9bc1be49f 100644 --- a/openstack/loadbalancer/v2/providers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/providers/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/providers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ProvidersListBody contains the canned body of a provider list response. diff --git a/openstack/loadbalancer/v2/providers/testing/requests_test.go b/openstack/loadbalancer/v2/providers/testing/requests_test.go index b8a879eba0..a831f828b2 100644 --- a/openstack/loadbalancer/v2/providers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/providers/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers" - fake "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/testhelper" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/providers" + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListProviders(t *testing.T) { diff --git a/openstack/loadbalancer/v2/providers/urls.go b/openstack/loadbalancer/v2/providers/urls.go index 00d1c8880c..bd74a8425d 100644 --- a/openstack/loadbalancer/v2/providers/urls.go +++ b/openstack/loadbalancer/v2/providers/urls.go @@ -1,6 +1,6 @@ package providers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/loadbalancer/v2/quotas/requests.go b/openstack/loadbalancer/v2/quotas/requests.go index 974cc4b9bb..01696d1055 100644 --- a/openstack/loadbalancer/v2/quotas/requests.go +++ b/openstack/loadbalancer/v2/quotas/requests.go @@ -1,7 +1,7 @@ package quotas import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Get returns load balancer Quotas for a project. diff --git a/openstack/loadbalancer/v2/quotas/results.go b/openstack/loadbalancer/v2/quotas/results.go index a25b687244..e1ef385982 100644 --- a/openstack/loadbalancer/v2/quotas/results.go +++ b/openstack/loadbalancer/v2/quotas/results.go @@ -3,7 +3,7 @@ package quotas import ( "encoding/json" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type commonResult struct { diff --git a/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go b/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go index b9f5a05b6c..e5185b2012 100644 --- a/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go @@ -1,6 +1,6 @@ package testing -import "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" +import "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/quotas" const GetResponseRaw_1 = ` { diff --git a/openstack/loadbalancer/v2/quotas/testing/requests_test.go b/openstack/loadbalancer/v2/quotas/testing/requests_test.go index 509b246f79..fe2b44bb3f 100644 --- a/openstack/loadbalancer/v2/quotas/testing/requests_test.go +++ b/openstack/loadbalancer/v2/quotas/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/quotas" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/quotas" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGet_1(t *testing.T) { diff --git a/openstack/loadbalancer/v2/quotas/urls.go b/openstack/loadbalancer/v2/quotas/urls.go index cf7dcbde65..0365d9c8ad 100644 --- a/openstack/loadbalancer/v2/quotas/urls.go +++ b/openstack/loadbalancer/v2/quotas/urls.go @@ -1,6 +1,6 @@ package quotas -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "quotas" diff --git a/openstack/loadbalancer/v2/testhelper/client.go b/openstack/loadbalancer/v2/testhelper/client.go index 7e1d917280..f32313a105 100644 --- a/openstack/loadbalancer/v2/testhelper/client.go +++ b/openstack/loadbalancer/v2/testhelper/client.go @@ -1,8 +1,8 @@ package common import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const TokenID = client.TokenID diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index 17789fbcea..91f019adfa 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -1,7 +1,7 @@ package claims import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // CreateOptsBuilder Builder. diff --git a/openstack/messaging/v2/claims/results.go b/openstack/messaging/v2/claims/results.go index ec43e582c0..cf76641bb5 100644 --- a/openstack/messaging/v2/claims/results.go +++ b/openstack/messaging/v2/claims/results.go @@ -1,6 +1,6 @@ package claims -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func (r CreateResult) Extract() ([]Messages, error) { var s struct { diff --git a/openstack/messaging/v2/claims/testing/fixtures_test.go b/openstack/messaging/v2/claims/testing/fixtures_test.go index 7efd483640..25d5060c02 100644 --- a/openstack/messaging/v2/claims/testing/fixtures_test.go +++ b/openstack/messaging/v2/claims/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/claims" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // QueueName is the name of the queue diff --git a/openstack/messaging/v2/claims/testing/requests_test.go b/openstack/messaging/v2/claims/testing/requests_test.go index fea1e6d194..7a271c61f9 100644 --- a/openstack/messaging/v2/claims/testing/requests_test.go +++ b/openstack/messaging/v2/claims/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/messaging/v2/claims" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/claims" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/messaging/v2/claims/urls.go b/openstack/messaging/v2/claims/urls.go index fae8fb4f45..484494a329 100644 --- a/openstack/messaging/v2/claims/urls.go +++ b/openstack/messaging/v2/claims/urls.go @@ -1,6 +1,6 @@ package claims -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( apiVersion = "v2" diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index 14ea4dd653..9286e96934 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -1,8 +1,8 @@ package messages import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go index ed1208b3f3..76bd91a2f5 100644 --- a/openstack/messaging/v2/messages/results.go +++ b/openstack/messaging/v2/messages/results.go @@ -1,8 +1,8 @@ package messages import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // commonResult is the response of a base result. diff --git a/openstack/messaging/v2/messages/testing/fixtures_test.go b/openstack/messaging/v2/messages/testing/fixtures_test.go index 000f887ffa..94aa07e574 100644 --- a/openstack/messaging/v2/messages/testing/fixtures_test.go +++ b/openstack/messaging/v2/messages/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/messages" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // QueueName is the name of the queue diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go index eb839262b9..e415dbafe3 100644 --- a/openstack/messaging/v2/messages/testing/requests_test.go +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/messaging/v2/messages" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/messages" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/messaging/v2/messages/urls.go b/openstack/messaging/v2/messages/urls.go index a00dd5620d..b5ba4235b2 100644 --- a/openstack/messaging/v2/messages/urls.go +++ b/openstack/messaging/v2/messages/urls.go @@ -3,7 +3,7 @@ package messages import ( "net/url" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) const ( diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 4f036640c5..9bc02f8327 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -1,8 +1,8 @@ package queues import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 45a9083957..53f7cd9f60 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -3,8 +3,8 @@ package queues import ( "encoding/json" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // commonResult is the response of a base result. diff --git a/openstack/messaging/v2/queues/testing/fixtures_test.go b/openstack/messaging/v2/queues/testing/fixtures_test.go index 67496c90f4..fa329117ab 100644 --- a/openstack/messaging/v2/queues/testing/fixtures_test.go +++ b/openstack/messaging/v2/queues/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/queues" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // QueueName is the name of the queue diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index ec5505e801..6597e8a953 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/messaging/v2/queues" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/queues" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestList(t *testing.T) { diff --git a/openstack/messaging/v2/queues/urls.go b/openstack/messaging/v2/queues/urls.go index 6b5a0e325a..9a89bfceab 100644 --- a/openstack/messaging/v2/queues/urls.go +++ b/openstack/messaging/v2/queues/urls.go @@ -3,7 +3,7 @@ package queues import ( "net/url" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) const ( diff --git a/openstack/networking/v2/apiversions/requests.go b/openstack/networking/v2/apiversions/requests.go index 0d027be8b6..00f2e16c2b 100644 --- a/openstack/networking/v2/apiversions/requests.go +++ b/openstack/networking/v2/apiversions/requests.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListVersions lists all the Neutron API versions available to end-users. diff --git a/openstack/networking/v2/apiversions/results.go b/openstack/networking/v2/apiversions/results.go index ad9d092fa0..89f30152b2 100644 --- a/openstack/networking/v2/apiversions/results.go +++ b/openstack/networking/v2/apiversions/results.go @@ -1,7 +1,7 @@ package apiversions import ( - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/pagination" ) // APIVersion represents an API version for Neutron. It contains the status of diff --git a/openstack/networking/v2/apiversions/testing/requests_test.go b/openstack/networking/v2/apiversions/testing/requests_test.go index 5a66a2a09a..4f94f974d9 100644 --- a/openstack/networking/v2/apiversions/testing/requests_test.go +++ b/openstack/networking/v2/apiversions/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/apiversions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/apiversions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListVersions(t *testing.T) { diff --git a/openstack/networking/v2/apiversions/urls.go b/openstack/networking/v2/apiversions/urls.go index 41aebdc5f2..47f8116620 100644 --- a/openstack/networking/v2/apiversions/urls.go +++ b/openstack/networking/v2/apiversions/urls.go @@ -3,8 +3,8 @@ package apiversions import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { diff --git a/openstack/networking/v2/common/common_tests.go b/openstack/networking/v2/common/common_tests.go index 7e1d917280..f32313a105 100644 --- a/openstack/networking/v2/common/common_tests.go +++ b/openstack/networking/v2/common/common_tests.go @@ -1,8 +1,8 @@ package common import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const TokenID = client.TokenID diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 5a3c4c35c3..d3852c8a72 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -1,8 +1,8 @@ package agents import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/agents/results.go b/openstack/networking/v2/extensions/agents/results.go index 01af98c4e2..6b5c32b563 100644 --- a/openstack/networking/v2/extensions/agents/results.go +++ b/openstack/networking/v2/extensions/agents/results.go @@ -4,11 +4,11 @@ import ( "encoding/json" "time" - "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/speakers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures_test.go b/openstack/networking/v2/extensions/agents/testing/fixtures_test.go index d40bc6ecdb..74711a8dc6 100644 --- a/openstack/networking/v2/extensions/agents/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/agents/testing/fixtures_test.go @@ -3,7 +3,7 @@ package testing import ( "time" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/agents" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/agents" ) // AgentsListResult represents raw response for the List request. diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index d1afbc5c98..806910660b 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - 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" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/agents" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/agents/urls.go b/openstack/networking/v2/extensions/agents/urls.go index 3ee3e02dcd..6d4b243aff 100644 --- a/openstack/networking/v2/extensions/agents/urls.go +++ b/openstack/networking/v2/extensions/agents/urls.go @@ -1,6 +1,6 @@ package agents -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "agents" const dhcpNetworksResourcePath = "dhcp-networks" diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index 6d49aeaf6c..c083dc9170 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -1,7 +1,7 @@ package attributestags import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ReplaceAllOptsBuilder allows extensions to add additional parameters to diff --git a/openstack/networking/v2/extensions/attributestags/results.go b/openstack/networking/v2/extensions/attributestags/results.go index cea8045beb..1dba4cc7ab 100644 --- a/openstack/networking/v2/extensions/attributestags/results.go +++ b/openstack/networking/v2/extensions/attributestags/results.go @@ -1,7 +1,7 @@ package attributestags import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type tagResult struct { diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go index 4a005faf76..48fc55b5f6 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/attributestags" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestReplaceAll(t *testing.T) { diff --git a/openstack/networking/v2/extensions/attributestags/urls.go b/openstack/networking/v2/extensions/attributestags/urls.go index 973e00c47c..94eb2b41d5 100644 --- a/openstack/networking/v2/extensions/attributestags/urls.go +++ b/openstack/networking/v2/extensions/attributestags/urls.go @@ -1,6 +1,6 @@ package attributestags -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( tagsPath = "tags" diff --git a/openstack/networking/v2/extensions/bgp/peers/requests.go b/openstack/networking/v2/extensions/bgp/peers/requests.go index bedeb457af..ef2b5e0b83 100644 --- a/openstack/networking/v2/extensions/bgp/peers/requests.go +++ b/openstack/networking/v2/extensions/bgp/peers/requests.go @@ -1,8 +1,8 @@ package peers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List the bgp peers diff --git a/openstack/networking/v2/extensions/bgp/peers/results.go b/openstack/networking/v2/extensions/bgp/peers/results.go index c20ed238ff..a3a1b5fd34 100644 --- a/openstack/networking/v2/extensions/bgp/peers/results.go +++ b/openstack/networking/v2/extensions/bgp/peers/results.go @@ -1,8 +1,8 @@ package peers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) const jroot = "bgp_peer" diff --git a/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go index 67cd77fbf5..5025780dae 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/fixture.go @@ -1,6 +1,6 @@ package testing -import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/peers" +import "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/peers" const ListBGPPeersResult = ` { 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 83c207ad95..7e14e5c65e 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "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" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/peers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/bgp/peers/urls.go b/openstack/networking/v2/extensions/bgp/peers/urls.go index e63ea79f24..804ba52f38 100644 --- a/openstack/networking/v2/extensions/bgp/peers/urls.go +++ b/openstack/networking/v2/extensions/bgp/peers/urls.go @@ -1,6 +1,6 @@ package peers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const urlBase = "bgp-peers" diff --git a/openstack/networking/v2/extensions/bgp/speakers/requests.go b/openstack/networking/v2/extensions/bgp/speakers/requests.go index 778cf01119..ca86ec90d9 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/requests.go +++ b/openstack/networking/v2/extensions/bgp/speakers/requests.go @@ -1,8 +1,8 @@ package speakers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List the bgp speakers diff --git a/openstack/networking/v2/extensions/bgp/speakers/results.go b/openstack/networking/v2/extensions/bgp/speakers/results.go index 3f8377f399..d7353da85c 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/results.go +++ b/openstack/networking/v2/extensions/bgp/speakers/results.go @@ -1,8 +1,8 @@ package speakers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) const jroot = "bgp_speaker" diff --git a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go index 044291778f..de346783c3 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/fixture.go @@ -1,6 +1,6 @@ package testing -import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" +import "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/speakers" const ListBGPSpeakerResult = ` { 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 bf604a9fab..71b22edd56 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/bgp/speakers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgp/speakers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/bgp/speakers/urls.go b/openstack/networking/v2/extensions/bgp/speakers/urls.go index 42c99e0e5a..c04041c343 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/urls.go +++ b/openstack/networking/v2/extensions/bgp/speakers/urls.go @@ -1,6 +1,6 @@ package speakers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const urlBase = "bgp-speakers" diff --git a/openstack/networking/v2/extensions/delegate.go b/openstack/networking/v2/extensions/delegate.go index 0c43689bb8..9dbb521073 100644 --- a/openstack/networking/v2/extensions/delegate.go +++ b/openstack/networking/v2/extensions/delegate.go @@ -1,9 +1,9 @@ package extensions import ( - "github.com/gophercloud/gophercloud" - common "github.com/gophercloud/gophercloud/openstack/common/extensions" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Extension is a single OpenStack extension. diff --git a/openstack/networking/v2/extensions/dns/requests.go b/openstack/networking/v2/extensions/dns/requests.go index b7bd62d2c8..b93a5a353a 100644 --- a/openstack/networking/v2/extensions/dns/requests.go +++ b/openstack/networking/v2/extensions/dns/requests.go @@ -3,10 +3,10 @@ package dns import ( "net/url" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" ) // PortListOptsExt adds the DNS options to the base port ListOpts. diff --git a/openstack/networking/v2/extensions/dns/testing/fixtures_test.go b/openstack/networking/v2/extensions/dns/testing/fixtures_test.go index d5fd33324b..340c90083c 100644 --- a/openstack/networking/v2/extensions/dns/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/dns/testing/fixtures_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - floatingiptest "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/testing" - networktest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" - porttest "github.com/gophercloud/gophercloud/openstack/networking/v2/ports/testing" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + floatingiptest "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips/testing" + networktest "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/testing" + porttest "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/testing" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) const NetworkCreateRequest = ` diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go index 931022c3ec..191d8ad038 100644 --- a/openstack/networking/v2/extensions/dns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/dns/testing/requests_test.go @@ -4,12 +4,12 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "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" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/dns" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) type PortDNS struct { diff --git a/openstack/networking/v2/extensions/external/requests.go b/openstack/networking/v2/extensions/external/requests.go index ced5efed8d..8b53828a55 100644 --- a/openstack/networking/v2/extensions/external/requests.go +++ b/openstack/networking/v2/extensions/external/requests.go @@ -4,8 +4,8 @@ import ( "net/url" "strconv" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" ) // ListOptsExt adds the external network options to the base ListOpts. diff --git a/openstack/networking/v2/extensions/external/testing/requests_test.go b/openstack/networking/v2/extensions/external/testing/requests_test.go index 29236b8928..aa60ee4424 100644 --- a/openstack/networking/v2/extensions/external/testing/requests_test.go +++ b/openstack/networking/v2/extensions/external/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/external" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListExternal(t *testing.T) { diff --git a/openstack/networking/v2/extensions/external/testing/results_test.go b/openstack/networking/v2/extensions/external/testing/results_test.go index a64d23c3c4..5cdf8a50cf 100644 --- a/openstack/networking/v2/extensions/external/testing/results_test.go +++ b/openstack/networking/v2/extensions/external/testing/results_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/external" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + nettest "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/testing" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/extradhcpopts/requests.go b/openstack/networking/v2/extensions/extradhcpopts/requests.go index f3eb9bc450..5df95ff3eb 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/requests.go +++ b/openstack/networking/v2/extensions/extradhcpopts/requests.go @@ -1,8 +1,8 @@ package extradhcpopts import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" ) // CreateOptsExt adds extra DHCP options to the base ports.CreateOpts. diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go index f1c0de54b0..1c3419b5f2 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go @@ -1,8 +1,8 @@ package firewalls import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/results.go b/openstack/networking/v2/extensions/fwaas/firewalls/results.go index edefdd6fe8..27fec509d6 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/results.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/results.go @@ -1,8 +1,8 @@ package firewalls import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Firewall is an OpenStack firewall. diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go index f7fae9fdc5..ced5e46b23 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go @@ -5,12 +5,12 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/routerinsertion" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/urls.go b/openstack/networking/v2/extensions/fwaas/firewalls/urls.go index 807ea1ab65..d7f479355a 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/urls.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/urls.go @@ -1,6 +1,6 @@ package firewalls -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "fw" diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go index 88e23cb994..0af024d3c8 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas/policies/requests.go @@ -1,8 +1,8 @@ package policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/fwaas/policies/results.go b/openstack/networking/v2/extensions/fwaas/policies/results.go index 6796f0ceb8..2e60d37396 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/results.go +++ b/openstack/networking/v2/extensions/fwaas/policies/results.go @@ -1,8 +1,8 @@ package policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Policy is a firewall policy. diff --git a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go index ed7070cc25..c40d2f458a 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/policies" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/fwaas/policies/urls.go b/openstack/networking/v2/extensions/fwaas/policies/urls.go index c252b79dd0..2a8e354ae4 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/urls.go +++ b/openstack/networking/v2/extensions/fwaas/policies/urls.go @@ -1,6 +1,6 @@ package policies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "fw" diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go index b1f6d76e38..0ac9abf7e9 100644 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go +++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go @@ -1,7 +1,7 @@ package routerinsertion import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" ) // CreateOptsExt adds the RouterIDs option to the base CreateOpts. diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go index 0eff5fcd49..42a60b7003 100644 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "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" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/routerinsertion" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/fwaas/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go index 4627a92628..8daa4f13f9 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas/rules/requests.go @@ -1,8 +1,8 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type ( diff --git a/openstack/networking/v2/extensions/fwaas/rules/results.go b/openstack/networking/v2/extensions/fwaas/rules/results.go index f7b44a05fc..8f31d65556 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas/rules/results.go @@ -1,8 +1,8 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Rule represents a firewall rule. diff --git a/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go index 2fedfa8ac7..ce2cc97d15 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/rules" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/fwaas/rules/urls.go b/openstack/networking/v2/extensions/fwaas/rules/urls.go index 79654be73e..96212de7c5 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/urls.go +++ b/openstack/networking/v2/extensions/fwaas/rules/urls.go @@ -1,6 +1,6 @@ package rules -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "fw" diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go index a795ccf2ab..580dfab6ae 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go @@ -1,8 +1,8 @@ package groups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/results.go b/openstack/networking/v2/extensions/fwaas_v2/groups/results.go index 793a5d4328..622986c2e5 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/results.go @@ -1,8 +1,8 @@ package groups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Group is a firewall group. 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 b4ddaa091f..da719d1a47 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 @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/groups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/groups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/urls.go b/openstack/networking/v2/extensions/fwaas_v2/groups/urls.go index 318794369c..20fe79190a 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/urls.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/urls.go @@ -1,6 +1,6 @@ package groups -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "fwaas" diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go index 8f036ca93c..abb468971c 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go @@ -1,8 +1,8 @@ package policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/results.go b/openstack/networking/v2/extensions/fwaas_v2/policies/results.go index 8d7411c3de..88731d0788 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/results.go @@ -1,8 +1,8 @@ package policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Policy is a firewall policy. 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 e0251c770a..203de039fe 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 @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/policies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/policies" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/urls.go b/openstack/networking/v2/extensions/fwaas_v2/policies/urls.go index 5411379a07..1fdafbbe67 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/urls.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/urls.go @@ -1,6 +1,6 @@ package policies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "fwaas" diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index d80e682796..206c1744cd 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -1,8 +1,8 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type ( diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go index 1d085b1459..601b9dd2ce 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/results.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/results.go @@ -1,8 +1,8 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Rule represents a firewall rule 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 8ca0d70e28..cc28e83489 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 @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas_v2/rules" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/rules" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go b/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go index 926f368fb3..593b7740d3 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/urls.go @@ -1,6 +1,6 @@ package rules -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "fwaas" diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go index fb28b72e36..cf7221b859 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go @@ -1,8 +1,8 @@ package addressscopes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/results.go b/openstack/networking/v2/extensions/layer3/addressscopes/results.go index 8a94aaaef5..8664edd6ea 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/results.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/results.go @@ -1,8 +1,8 @@ package addressscopes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures_test.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures_test.go index 42b896d3c3..120e51956d 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/fixtures_test.go @@ -1,6 +1,6 @@ package testing -import "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" +import "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/addressscopes" // AddressScopesListResult represents raw response for the List request. const AddressScopesListResult = ` diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go index caad3a2f3f..f1d0484f7e 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/addressscopes" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/addressscopes" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/urls.go b/openstack/networking/v2/extensions/layer3/addressscopes/urls.go index 9fe7e01a03..3fff7a7229 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/urls.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/urls.go @@ -1,6 +1,6 @@ package addressscopes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "address-scopes" diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/requests.go b/openstack/networking/v2/extensions/layer3/extraroutes/requests.go index 6049f9c48e..501955b031 100644 --- a/openstack/networking/v2/extensions/layer3/extraroutes/requests.go +++ b/openstack/networking/v2/extensions/layer3/extraroutes/requests.go @@ -1,8 +1,8 @@ package extraroutes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" ) // OptsBuilder allows extensions to add additional parameters to the Add or diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/results.go b/openstack/networking/v2/extensions/layer3/extraroutes/results.go index 522aa8e85f..cfe8517dbe 100644 --- a/openstack/networking/v2/extensions/layer3/extraroutes/results.go +++ b/openstack/networking/v2/extensions/layer3/extraroutes/results.go @@ -1,8 +1,8 @@ package extraroutes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" ) // Extract is a function that accepts a result and extracts a router. diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go index 070e8f29b2..fefe1a2c00 100644 --- a/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "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" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/extraroutes" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAddExtraRoutes(t *testing.T) { diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/urls.go b/openstack/networking/v2/extensions/layer3/extraroutes/urls.go index ac91a20c25..7f43aa3b4c 100644 --- a/openstack/networking/v2/extensions/layer3/extraroutes/urls.go +++ b/openstack/networking/v2/extensions/layer3/extraroutes/urls.go @@ -1,6 +1,6 @@ package extraroutes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "routers" diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 2a4ff1aca4..0fbbcad239 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -1,8 +1,8 @@ package floatingips import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index c4c019bca4..4524ceb161 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // FloatingIP represents a floating IP resource. A floating IP is an external diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index 26ff84c720..60fb2ad88f 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/layer3/floatingips/urls.go b/openstack/networking/v2/extensions/layer3/floatingips/urls.go index 1318a184ca..4352321a30 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/urls.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/urls.go @@ -1,6 +1,6 @@ package floatingips -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "floatingips" diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index 9081bc3549..af919a586e 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -1,8 +1,8 @@ package portforwarding import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type ListOptsBuilder interface { diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go index 70bd097336..0824471d57 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/results.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -1,8 +1,8 @@ package portforwarding import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type PortForwarding struct { diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go index a2b509b0d3..57b3ff0b30 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/portforwarding" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/portforwarding" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestPortForwardingList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/urls.go b/openstack/networking/v2/extensions/layer3/portforwarding/urls.go index 13eb158016..59f0b6e7ad 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/urls.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/urls.go @@ -1,6 +1,6 @@ package portforwarding -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "floatingips" const portForwardingPath = "port_forwardings" diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index 81665acef4..8a39ef7593 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -1,8 +1,8 @@ package routers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index 1c93eab506..9bf8a02b6e 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // GatewayInfo represents the information of an external gateway for any diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 4f2de23b53..1e1b7a630e 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/layer3/routers/urls.go b/openstack/networking/v2/extensions/layer3/routers/urls.go index 7b30f9033c..87815aa6ec 100644 --- a/openstack/networking/v2/extensions/layer3/routers/urls.go +++ b/openstack/networking/v2/extensions/layer3/routers/urls.go @@ -1,6 +1,6 @@ package routers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "routers" diff --git a/openstack/networking/v2/extensions/lbaas/members/requests.go b/openstack/networking/v2/extensions/lbaas/members/requests.go index c423a09505..9b102ed745 100644 --- a/openstack/networking/v2/extensions/lbaas/members/requests.go +++ b/openstack/networking/v2/extensions/lbaas/members/requests.go @@ -1,8 +1,8 @@ package members import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through diff --git a/openstack/networking/v2/extensions/lbaas/members/results.go b/openstack/networking/v2/extensions/lbaas/members/results.go index c23308def7..05eff303ab 100644 --- a/openstack/networking/v2/extensions/lbaas/members/results.go +++ b/openstack/networking/v2/extensions/lbaas/members/results.go @@ -1,8 +1,8 @@ package members import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Member represents the application running on a backend server. diff --git a/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go index 3e4f1d43f6..0acb5b44bb 100644 --- a/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/members" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas/members/urls.go b/openstack/networking/v2/extensions/lbaas/members/urls.go index e2248f81f4..0136bd430e 100644 --- a/openstack/networking/v2/extensions/lbaas/members/urls.go +++ b/openstack/networking/v2/extensions/lbaas/members/urls.go @@ -1,6 +1,6 @@ package members -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lb" diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go index 1cc92e60b8..47fc567b9b 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go @@ -3,8 +3,8 @@ package monitors import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go index cf0dd9a75c..2b55dc7c67 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/results.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/results.go @@ -1,8 +1,8 @@ package monitors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Monitor represents a load balancer health monitor. A health monitor is used diff --git a/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go index f7360743b0..c07c9fcce3 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/monitors" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas/monitors/urls.go b/openstack/networking/v2/extensions/lbaas/monitors/urls.go index e9d90fcc56..35310dd08d 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/urls.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/urls.go @@ -1,6 +1,6 @@ package monitors -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lb" diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go index e1b73f16d1..9c9cedb32c 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas/pools/requests.go @@ -1,8 +1,8 @@ package pools import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go index d94c6b0b5a..cdd7667a19 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas/pools/results.go @@ -1,8 +1,8 @@ package pools import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Pool represents a logical set of devices, such as web servers, that you diff --git a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go index dce13dd745..814f5c86de 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/pools" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas/pools/urls.go b/openstack/networking/v2/extensions/lbaas/pools/urls.go index fe3601bbec..fd64c69a21 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/urls.go +++ b/openstack/networking/v2/extensions/lbaas/pools/urls.go @@ -1,6 +1,6 @@ package pools -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lb" diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go index 361fd3adc0..0307aa6999 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/requests.go +++ b/openstack/networking/v2/extensions/lbaas/vips/requests.go @@ -1,8 +1,8 @@ package vips import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go index a6a5bb5836..c9d078e2b7 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/results.go +++ b/openstack/networking/v2/extensions/lbaas/vips/results.go @@ -1,8 +1,8 @@ package vips import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SessionPersistence represents the session persistence feature of the load diff --git a/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go index 7f9b6ddb78..5543faf901 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/vips" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas/vips/urls.go b/openstack/networking/v2/extensions/lbaas/vips/urls.go index 584a1cf680..436a9a38dc 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/urls.go +++ b/openstack/networking/v2/extensions/lbaas/vips/urls.go @@ -1,6 +1,6 @@ package vips -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lb" diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go index db8c0ea32d..022169cdb4 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go @@ -1,8 +1,8 @@ package l7policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go index 5123d85caf..886af9b4e1 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go @@ -1,8 +1,8 @@ package l7policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // L7Policy is a collection of L7 rules associated with a Listener, and which diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go index 87c6e66d22..4eb4ca22a5 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // SingleL7PolicyBody is the canned body of a Get request on an existing l7policy. diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go index f8e67f4b80..55453efc8b 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreateL7Policy(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go index ecb607a8e8..57126a8811 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go @@ -1,6 +1,6 @@ package l7policies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index dacfe31a70..b234f10475 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -1,8 +1,8 @@ package listeners import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Type Protocol represents a listener protocol. diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go index 6bf13cf68d..4434a9db24 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go @@ -1,10 +1,10 @@ package listeners import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" + "github.com/gophercloud/gophercloud/v2/pagination" ) type LoadBalancerID struct { diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go index 5a5f050324..31cfbf2eb1 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ListenersListBody contains the canned body of a listeners list response. diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go index 80f56709d6..27049cf0e9 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListListeners(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go index 02fb1eb39e..18c7775b29 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go @@ -1,6 +1,6 @@ package listeners -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index c6992730a5..b522267156 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -3,8 +3,8 @@ package loadbalancers import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go index a9027fb7fa..5301d1207c 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go @@ -1,10 +1,10 @@ package loadbalancers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" + "github.com/gophercloud/gophercloud/v2/pagination" ) // LoadBalancer is the primary load balancing configuration object that diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go index cebb4eb085..03ecfcc941 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go @@ -5,12 +5,12 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // LoadbalancersListBody contains the canned body of a loadbalancer list response. diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go index ab0561c71b..e274fcb4d1 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListLoadbalancers(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go index 2d2a99b779..e30c799767 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go @@ -1,6 +1,6 @@ package loadbalancers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go index 5fc513b184..1c48bb9742 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go @@ -3,8 +3,8 @@ package monitors import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go index ee96038f3c..b03c811c83 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go @@ -1,8 +1,8 @@ package monitors import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type PoolID struct { diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go index 6d3eb01ee4..2a3c3b0c5c 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HealthmonitorsListBody contains the canned body of a healthmonitor list response. diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go index 09389c840a..162f61f92d 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListHealthmonitors(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go index a222e52a93..d5723a305f 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go @@ -1,6 +1,6 @@ package monitors -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index 6f7bc07318..fa4dbebb67 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -1,8 +1,8 @@ package pools import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go index c747f93a71..7ef9698f79 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go @@ -1,9 +1,9 @@ package pools import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SessionPersistence represents the session persistence feature of the load diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go index df9d1fd05c..5efc3e0145 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // PoolsListBody contains the canned body of a pool list response. diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go index 14802fd4c4..3801efe271 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListPools(t *testing.T) { diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go index e7443c4f19..a362f1b957 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go @@ -1,6 +1,6 @@ package pools -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "lbaas" diff --git a/openstack/networking/v2/extensions/mtu/requests.go b/openstack/networking/v2/extensions/mtu/requests.go index bffb7f5ef7..9495076da4 100644 --- a/openstack/networking/v2/extensions/mtu/requests.go +++ b/openstack/networking/v2/extensions/mtu/requests.go @@ -4,8 +4,8 @@ import ( "fmt" "net/url" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" ) // ListOptsExt adds an MTU option to the base ListOpts. diff --git a/openstack/networking/v2/extensions/mtu/testing/requests_test.go b/openstack/networking/v2/extensions/mtu/testing/requests_test.go index 1b69ce41ee..ac0f4ec107 100644 --- a/openstack/networking/v2/extensions/mtu/testing/requests_test.go +++ b/openstack/networking/v2/extensions/mtu/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/mtu" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListExternal(t *testing.T) { diff --git a/openstack/networking/v2/extensions/mtu/testing/results_test.go b/openstack/networking/v2/extensions/mtu/testing/results_test.go index ff5f9ae35b..6b94a70cb7 100644 --- a/openstack/networking/v2/extensions/mtu/testing/results_test.go +++ b/openstack/networking/v2/extensions/mtu/testing/results_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/mtu" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/mtu" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + nettest "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/testing" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) type NetworkMTU struct { diff --git a/openstack/networking/v2/extensions/networkipavailabilities/requests.go b/openstack/networking/v2/extensions/networkipavailabilities/requests.go index ecc7f84dbf..9039895a4a 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/requests.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/requests.go @@ -1,8 +1,8 @@ package networkipavailabilities import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/networkipavailabilities/results.go b/openstack/networking/v2/extensions/networkipavailabilities/results.go index c3bd7e1cdb..b37617f9d1 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/results.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "math/big" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go index 6a88f39bbe..58900db23c 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go @@ -1,8 +1,8 @@ package testing import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/networkipavailabilities" ) // NetworkIPAvailabilityListResult represents raw server response from a server to a list call. diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go index 1b352e2a18..87b71b55c0 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/networkipavailabilities" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/networkipavailabilities" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/networkipavailabilities/urls.go b/openstack/networking/v2/extensions/networkipavailabilities/urls.go index 5e3228ff3a..2c8ef9cae4 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/urls.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/urls.go @@ -1,6 +1,6 @@ package networkipavailabilities -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "network-ip-availabilities" diff --git a/openstack/networking/v2/extensions/portsbinding/requests.go b/openstack/networking/v2/extensions/portsbinding/requests.go index e38000904e..fc9233d51e 100644 --- a/openstack/networking/v2/extensions/portsbinding/requests.go +++ b/openstack/networking/v2/extensions/portsbinding/requests.go @@ -1,7 +1,7 @@ package portsbinding import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" ) // CreateOptsExt adds port binding options to the base ports.CreateOpts. diff --git a/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go b/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go index 03fe353910..494fe49091 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - porttest "github.com/gophercloud/gophercloud/openstack/networking/v2/ports/testing" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + porttest "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports/testing" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func HandleListSuccessfully(t *testing.T) { diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go index 666f406ca4..1c3d9df67c 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsbinding" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsbinding" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/portsecurity/requests.go b/openstack/networking/v2/extensions/portsecurity/requests.go index c80f47cf61..1eb670c67b 100644 --- a/openstack/networking/v2/extensions/portsecurity/requests.go +++ b/openstack/networking/v2/extensions/portsecurity/requests.go @@ -1,8 +1,8 @@ package portsecurity import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" ) // PortCreateOptsExt adds port security options to the base ports.CreateOpts. diff --git a/openstack/networking/v2/extensions/provider/requests.go b/openstack/networking/v2/extensions/provider/requests.go index d4f3c60a30..0ed1735dc5 100644 --- a/openstack/networking/v2/extensions/provider/requests.go +++ b/openstack/networking/v2/extensions/provider/requests.go @@ -1,7 +1,7 @@ package provider import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" ) // CreateOptsExt adds a Segments option to the base Network CreateOpts. diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go index 16fe0848b8..771f497577 100644 --- a/openstack/networking/v2/extensions/provider/testing/results_test.go +++ b/openstack/networking/v2/extensions/provider/testing/results_test.go @@ -5,12 +5,12 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/provider" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - nettest "github.com/gophercloud/gophercloud/openstack/networking/v2/networks/testing" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/provider" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + nettest "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks/testing" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index b2c1fbd38b..dd4b97e59b 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -1,10 +1,10 @@ package policies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2/pagination" ) // PortCreateOptsExt adds QoS options to the base ports.CreateOpts. diff --git a/openstack/networking/v2/extensions/qos/policies/results.go b/openstack/networking/v2/extensions/qos/policies/results.go index 356f116a00..e1d22775a9 100644 --- a/openstack/networking/v2/extensions/qos/policies/results.go +++ b/openstack/networking/v2/extensions/qos/policies/results.go @@ -3,8 +3,8 @@ package policies import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // QoSPolicyExt represents additional resource attributes available with the QoS extension. diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go index f77ba74136..58aa19e554 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go @@ -3,7 +3,7 @@ package testing import ( "time" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/policies" ) const GetPortResponse = ` diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index cc7c2d9004..3d94557748 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -6,12 +6,12 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/policies" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/policies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGetPort(t *testing.T) { diff --git a/openstack/networking/v2/extensions/qos/policies/urls.go b/openstack/networking/v2/extensions/qos/policies/urls.go index 31ceff9cd5..014207d197 100644 --- a/openstack/networking/v2/extensions/qos/policies/urls.go +++ b/openstack/networking/v2/extensions/qos/policies/urls.go @@ -1,6 +1,6 @@ package policies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "qos/policies" diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index a5719d9f8a..58c4648e7f 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -1,8 +1,8 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index 7a8a08588a..39617b7e76 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -1,8 +1,8 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index d295bb197c..f98ac61cfb 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/rules" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/rules" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestListBandwidthLimitRule(t *testing.T) { diff --git a/openstack/networking/v2/extensions/qos/rules/urls.go b/openstack/networking/v2/extensions/qos/rules/urls.go index 6db2bce8a9..4e2547f154 100644 --- a/openstack/networking/v2/extensions/qos/rules/urls.go +++ b/openstack/networking/v2/extensions/qos/rules/urls.go @@ -1,6 +1,6 @@ package rules -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "qos/policies" diff --git a/openstack/networking/v2/extensions/qos/ruletypes/requests.go b/openstack/networking/v2/extensions/qos/ruletypes/requests.go index cbccf06ded..f10b2b886a 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/requests.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/requests.go @@ -1,8 +1,8 @@ package ruletypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListRuleTypes returns the list of rule types from the server diff --git a/openstack/networking/v2/extensions/qos/ruletypes/results.go b/openstack/networking/v2/extensions/qos/ruletypes/results.go index c237f347c1..558295fdf2 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/results.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/results.go @@ -1,8 +1,8 @@ package ruletypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go index b27a9a93b6..dbc4813adb 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/qos/ruletypes" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/ruletypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListRuleTypes(t *testing.T) { diff --git a/openstack/networking/v2/extensions/qos/ruletypes/urls.go b/openstack/networking/v2/extensions/qos/ruletypes/urls.go index a6f23ac744..f0f7115ebc 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/urls.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/urls.go @@ -1,6 +1,6 @@ package ruletypes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listRuleTypesURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("qos", "rule-types") diff --git a/openstack/networking/v2/extensions/quotas/requests.go b/openstack/networking/v2/extensions/quotas/requests.go index b4b67a96a9..e42bca16a9 100644 --- a/openstack/networking/v2/extensions/quotas/requests.go +++ b/openstack/networking/v2/extensions/quotas/requests.go @@ -1,6 +1,6 @@ package quotas -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // Get returns Networking Quotas for a project. func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { diff --git a/openstack/networking/v2/extensions/quotas/results.go b/openstack/networking/v2/extensions/quotas/results.go index 4748ace52c..f430c8bb2a 100644 --- a/openstack/networking/v2/extensions/quotas/results.go +++ b/openstack/networking/v2/extensions/quotas/results.go @@ -5,7 +5,7 @@ import ( "fmt" "strconv" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type commonResult struct { diff --git a/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go b/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go index de4b579ce5..a4a7703c89 100644 --- a/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go @@ -1,7 +1,7 @@ package testing import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/quotas" ) const GetResponseRaw = ` diff --git a/openstack/networking/v2/extensions/quotas/testing/requests_test.go b/openstack/networking/v2/extensions/quotas/testing/requests_test.go index 6530ffb969..487f2fa343 100644 --- a/openstack/networking/v2/extensions/quotas/testing/requests_test.go +++ b/openstack/networking/v2/extensions/quotas/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/quotas" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/quotas" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestGet(t *testing.T) { diff --git a/openstack/networking/v2/extensions/quotas/urls.go b/openstack/networking/v2/extensions/quotas/urls.go index 7bf669439a..94cbe23880 100644 --- a/openstack/networking/v2/extensions/quotas/urls.go +++ b/openstack/networking/v2/extensions/quotas/urls.go @@ -1,6 +1,6 @@ package quotas -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "quotas" const resourcePathDetail = "details.json" diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index 723c918580..b58ffd6b14 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -1,8 +1,8 @@ package rbacpolicies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go index 53ef46c590..4fdebd61de 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/results.go +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -1,8 +1,8 @@ package rbacpolicies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures_test.go index f63fa2b89f..2fba0cde2e 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/fixtures_test.go @@ -1,7 +1,7 @@ package testing import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/rbacpolicies" ) const ListResponse = ` diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go index 8aad843459..2e15e2184c 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/rbacpolicies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/rbacpolicies" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/rbacpolicies/urls.go b/openstack/networking/v2/extensions/rbacpolicies/urls.go index 8beeed9a5f..a8cdff598b 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/urls.go +++ b/openstack/networking/v2/extensions/rbacpolicies/urls.go @@ -1,6 +1,6 @@ package rbacpolicies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("rbac-policies", id) diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index 566a730eaf..b196beca07 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -1,8 +1,8 @@ package groups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go index 2027037de2..6f1a4e1620 100644 --- a/openstack/networking/v2/extensions/security/groups/results.go +++ b/openstack/networking/v2/extensions/security/groups/results.go @@ -4,9 +4,9 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SecGroup represents a container for security group rules. diff --git a/openstack/networking/v2/extensions/security/groups/testing/fixtures_test.go b/openstack/networking/v2/extensions/security/groups/testing/fixtures_test.go index 0f91859b99..998c3ddd19 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/security/groups/testing/fixtures_test.go @@ -3,8 +3,8 @@ package testing import ( "time" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules" ) const SecurityGroupListResponse = ` diff --git a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go index 306490f114..b18e11f111 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/security/groups/urls.go b/openstack/networking/v2/extensions/security/groups/urls.go index 104cbcc558..8c445868a6 100644 --- a/openstack/networking/v2/extensions/security/groups/urls.go +++ b/openstack/networking/v2/extensions/security/groups/urls.go @@ -1,6 +1,6 @@ package groups -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const rootPath = "security-groups" diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 364f7f5c98..24147bb772 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -1,8 +1,8 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOpts allows the filtering and sorting of paginated collections through diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index 1e65f0627c..cfdb27fa17 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -1,8 +1,8 @@ package rules import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SecGroupRule represents a rule to dictate the behaviour of incoming or diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 8d0edabb81..a5779d931d 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/security/rules/urls.go b/openstack/networking/v2/extensions/security/rules/urls.go index a5ede0e89b..98c9ea7c27 100644 --- a/openstack/networking/v2/extensions/security/rules/urls.go +++ b/openstack/networking/v2/extensions/security/rules/urls.go @@ -1,6 +1,6 @@ package rules -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const rootPath = "security-group-rules" diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index 3092f809c5..bb1bf2cd56 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -1,8 +1,8 @@ package subnetpools import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index 3fb879dfa5..977d3d605a 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/extensions/subnetpools/testing/fixtures_test.go b/openstack/networking/v2/extensions/subnetpools/testing/fixtures_test.go index e6389d410f..634eb6af74 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/fixtures_test.go @@ -3,7 +3,7 @@ package testing import ( "time" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/subnetpools" ) const SubnetPoolsListResult = ` diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index 72df6af960..526e5965c9 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/subnetpools" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/subnetpools" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/subnetpools/urls.go b/openstack/networking/v2/extensions/subnetpools/urls.go index a05062c96d..21f328b1eb 100644 --- a/openstack/networking/v2/extensions/subnetpools/urls.go +++ b/openstack/networking/v2/extensions/subnetpools/urls.go @@ -1,6 +1,6 @@ package subnetpools -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "subnetpools" diff --git a/openstack/networking/v2/extensions/testing/delegate_test.go b/openstack/networking/v2/extensions/testing/delegate_test.go index 20a85f95b2..82f44adad2 100644 --- a/openstack/networking/v2/extensions/testing/delegate_test.go +++ b/openstack/networking/v2/extensions/testing/delegate_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - common "github.com/gophercloud/gophercloud/openstack/common/extensions" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/trunk_details/results.go b/openstack/networking/v2/extensions/trunk_details/results.go index 41142f58d2..41e28c68c8 100644 --- a/openstack/networking/v2/extensions/trunk_details/results.go +++ b/openstack/networking/v2/extensions/trunk_details/results.go @@ -1,7 +1,7 @@ package trunk_details import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" ) // TrunkDetailsExt represents additional trunking information returned in a diff --git a/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go b/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go index ae19dd721e..43532ae4e7 100644 --- a/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "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" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunk_details" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestServerWithUsageExt(t *testing.T) { diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index d9037b141d..3a4c942454 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -1,8 +1,8 @@ package trunks import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/trunks/results.go b/openstack/networking/v2/extensions/trunks/results.go index 6b66276032..20edcf9a88 100644 --- a/openstack/networking/v2/extensions/trunks/results.go +++ b/openstack/networking/v2/extensions/trunks/results.go @@ -3,8 +3,8 @@ package trunks import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type Subport struct { diff --git a/openstack/networking/v2/extensions/trunks/testing/fixtures_test.go b/openstack/networking/v2/extensions/trunks/testing/fixtures_test.go index d8d7b2aa53..e83b6e9083 100644 --- a/openstack/networking/v2/extensions/trunks/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/fixtures_test.go @@ -3,7 +3,7 @@ package testing import ( "time" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" ) const CreateRequest = ` diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index 8f2b377852..c99dc53807 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunks" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/trunks/urls.go b/openstack/networking/v2/extensions/trunks/urls.go index ac7dff0961..477b85d7a6 100644 --- a/openstack/networking/v2/extensions/trunks/urls.go +++ b/openstack/networking/v2/extensions/trunks/urls.go @@ -1,6 +1,6 @@ package trunks -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const resourcePath = "trunks" diff --git a/openstack/networking/v2/extensions/vlantransparent/requests.go b/openstack/networking/v2/extensions/vlantransparent/requests.go index 65504cf3ea..cd82324dc9 100644 --- a/openstack/networking/v2/extensions/vlantransparent/requests.go +++ b/openstack/networking/v2/extensions/vlantransparent/requests.go @@ -4,8 +4,8 @@ import ( "net/url" "strconv" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" ) // ListOptsExt adds the vlan-transparent network options to the base ListOpts. diff --git a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go index 0632a6c9e9..2f4d934f51 100644 --- a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vlantransparent" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vlantransparent" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go index e13139d029..440958ce8b 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -1,8 +1,8 @@ package endpointgroups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type EndpointType string diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go index e8cfd2510b..2fa97f5c8a 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/results.go @@ -1,8 +1,8 @@ package endpointgroups import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // EndpointGroup is an endpoint group. diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go index 7feac37f78..f86e65cf97 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/endpointgroups" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/endpointgroups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/urls.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/urls.go index 9e83563ce1..2aa696a239 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/urls.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/urls.go @@ -1,6 +1,6 @@ package endpointgroups -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "vpn" diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go index dea35bc6e6..3191ddf432 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -1,8 +1,8 @@ package ikepolicies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type AuthAlgorithm string diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go index e5d33a4db1..551d0af7a2 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/results.go @@ -1,8 +1,8 @@ package ikepolicies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Policy is an IKE Policy diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index b3ec548da7..1927ab2556 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ikepolicies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/ikepolicies" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/urls.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/urls.go index a364a881e6..99e468489b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/urls.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/urls.go @@ -1,6 +1,6 @@ package ikepolicies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "vpn" diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go index fd23548110..2c1836a49f 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -1,8 +1,8 @@ package ipsecpolicies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type TransformProtocol string diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go index 1268ec61b6..6170a2a9a7 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/results.go @@ -1,8 +1,8 @@ package ipsecpolicies import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Policy is an IPSec Policy diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go index 702bd38355..7e50dd2f5a 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/ipsecpolicies" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/urls.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/urls.go index 8781cc4499..2f9c7cf2af 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/urls.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/urls.go @@ -1,6 +1,6 @@ package ipsecpolicies -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "vpn" diff --git a/openstack/networking/v2/extensions/vpnaas/services/requests.go b/openstack/networking/v2/extensions/vpnaas/services/requests.go index 981f4bc82a..74bf415db9 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/services/requests.go @@ -1,8 +1,8 @@ package services import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/vpnaas/services/results.go b/openstack/networking/v2/extensions/vpnaas/services/results.go index c3aa9698c1..1af204b157 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/results.go +++ b/openstack/networking/v2/extensions/vpnaas/services/results.go @@ -1,8 +1,8 @@ package services import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Service is a VPN Service diff --git a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go index ca7adf327c..f1269f5294 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/services" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/services" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/vpnaas/services/urls.go b/openstack/networking/v2/extensions/vpnaas/services/urls.go index fe8b343fe3..6cf0ffe87a 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/urls.go +++ b/openstack/networking/v2/extensions/vpnaas/services/urls.go @@ -1,6 +1,6 @@ package services -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "vpn" diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index 4d97b54b74..70142f981e 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -1,8 +1,8 @@ package siteconnections import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go index d0fb6403b2..59840e8df9 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/results.go @@ -1,8 +1,8 @@ package siteconnections import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type DPD struct { diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go index 3db27364df..47c5e8cf34 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/vpnaas/siteconnections" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/vpnaas/siteconnections" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/urls.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/urls.go index 5c8ee9a364..6e52df7041 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/urls.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/urls.go @@ -1,6 +1,6 @@ package siteconnections -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( rootPath = "vpn" diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 00c2eae77d..d54b681bd7 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -3,8 +3,8 @@ package networks import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index 2a020a13f0..01dcbaedb7 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/networks/testing/fixtures.go b/openstack/networking/v2/networks/testing/fixtures.go index 7a8b69a129..3d4412c5be 100644 --- a/openstack/networking/v2/networks/testing/fixtures.go +++ b/openstack/networking/v2/networks/testing/fixtures.go @@ -3,7 +3,7 @@ package testing import ( "time" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" ) const ListResponse = ` diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index 151ffdd82f..eca6341298 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity" - "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsecurity" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/networks/urls.go b/openstack/networking/v2/networks/urls.go index 4a8fb1dc7d..14c352e395 100644 --- a/openstack/networking/v2/networks/urls.go +++ b/openstack/networking/v2/networks/urls.go @@ -1,6 +1,6 @@ package networks -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("networks", id) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 95eef81a2c..1d021f2ae1 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -5,8 +5,8 @@ import ( "net/url" "slices" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index dc9b75b43c..c9bb2446db 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index ab54a55fb9..e93391cf77 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -7,12 +7,12 @@ import ( "testing" "time" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "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" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/extradhcpopts" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsecurity" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/ports/urls.go b/openstack/networking/v2/ports/urls.go index 600d6f2fd9..e52f44f654 100644 --- a/openstack/networking/v2/ports/urls.go +++ b/openstack/networking/v2/ports/urls.go @@ -1,6 +1,6 @@ package ports -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("ports", id) diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 2e87907587..951a734a7a 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -3,8 +3,8 @@ package subnets import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index cd09b6f6e6..a105c75015 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -1,8 +1,8 @@ package subnets import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/networking/v2/subnets/testing/fixtures_test.go b/openstack/networking/v2/subnets/testing/fixtures_test.go index af8512e549..e6079975ec 100644 --- a/openstack/networking/v2/subnets/testing/fixtures_test.go +++ b/openstack/networking/v2/subnets/testing/fixtures_test.go @@ -1,7 +1,7 @@ package testing import ( - "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" ) const SubnetListResult = ` diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index 7e82d5855d..4bf688fd75 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestList(t *testing.T) { diff --git a/openstack/networking/v2/subnets/testing/results_test.go b/openstack/networking/v2/subnets/testing/results_test.go index a227ccde99..e3caf5fa2f 100644 --- a/openstack/networking/v2/subnets/testing/results_test.go +++ b/openstack/networking/v2/subnets/testing/results_test.go @@ -4,9 +4,9 @@ import ( "encoding/json" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestHostRoute(t *testing.T) { diff --git a/openstack/networking/v2/subnets/urls.go b/openstack/networking/v2/subnets/urls.go index 7a4f2f7dd4..e4ad4dd0ef 100644 --- a/openstack/networking/v2/subnets/urls.go +++ b/openstack/networking/v2/subnets/urls.go @@ -1,6 +1,6 @@ package subnets -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func resourceURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL("subnets", id) diff --git a/openstack/objectstorage/v1/accounts/requests.go b/openstack/objectstorage/v1/accounts/requests.go index 54b067205a..1dcd8d0cd2 100644 --- a/openstack/objectstorage/v1/accounts/requests.go +++ b/openstack/objectstorage/v1/accounts/requests.go @@ -1,6 +1,6 @@ package accounts -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // GetOptsBuilder allows extensions to add additional headers to the Get // request. diff --git a/openstack/objectstorage/v1/accounts/results.go b/openstack/objectstorage/v1/accounts/results.go index c9b7cb7eb1..39175517d3 100644 --- a/openstack/objectstorage/v1/accounts/results.go +++ b/openstack/objectstorage/v1/accounts/results.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // UpdateResult is returned from a call to the Update function. diff --git a/openstack/objectstorage/v1/accounts/testing/fixtures.go b/openstack/objectstorage/v1/accounts/testing/fixtures.go index f9bab9cf5d..42284933e8 100644 --- a/openstack/objectstorage/v1/accounts/testing/fixtures.go +++ b/openstack/objectstorage/v1/accounts/testing/fixtures.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // HandleGetAccountSuccessfully creates an HTTP handler at `/` on the test handler mux that diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go index 8ed3c74de6..ad50a7cba1 100644 --- a/openstack/objectstorage/v1/accounts/testing/requests_test.go +++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/accounts" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestUpdateAccount(t *testing.T) { diff --git a/openstack/objectstorage/v1/accounts/urls.go b/openstack/objectstorage/v1/accounts/urls.go index 71540b1daf..ffa5f5f125 100644 --- a/openstack/objectstorage/v1/accounts/urls.go +++ b/openstack/objectstorage/v1/accounts/urls.go @@ -1,6 +1,6 @@ package accounts -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func getURL(c *gophercloud.ServiceClient) string { return c.Endpoint diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index 434d977749..e2e7a21d7f 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -4,9 +4,9 @@ import ( "bytes" "net/url" - "github.com/gophercloud/gophercloud" - v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + v1 "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the List diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index 73de4c0135..8e71c15063 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -7,8 +7,8 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Container represents a container resource. diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 41e37ef1a7..7b791f38df 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/containers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) type handlerOptions struct { diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index f31718aaad..890a6cf8de 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" - v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + v1 "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/containers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) var ( diff --git a/openstack/objectstorage/v1/containers/urls.go b/openstack/objectstorage/v1/containers/urls.go index bc8d7274ff..2117b3628f 100644 --- a/openstack/objectstorage/v1/containers/urls.go +++ b/openstack/objectstorage/v1/containers/urls.go @@ -3,8 +3,8 @@ package containers import ( "net/url" - "github.com/gophercloud/gophercloud" - v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" + "github.com/gophercloud/gophercloud/v2" + v1 "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1" ) func listURL(c *gophercloud.ServiceClient) string { diff --git a/openstack/objectstorage/v1/errors.go b/openstack/objectstorage/v1/errors.go index a4c5c57c88..ba5cb0ab94 100644 --- a/openstack/objectstorage/v1/errors.go +++ b/openstack/objectstorage/v1/errors.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) func CheckContainerName(s string) error { diff --git a/openstack/objectstorage/v1/objects/errors.go b/openstack/objectstorage/v1/objects/errors.go index 5c4ae44d31..f48c01fbdb 100644 --- a/openstack/objectstorage/v1/objects/errors.go +++ b/openstack/objectstorage/v1/objects/errors.go @@ -1,6 +1,6 @@ package objects -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // ErrWrongChecksum is the error when the checksum generated for an object // doesn't match the ETAG header. diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 1a2af4e6cf..22c476de55 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -14,11 +14,11 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + v1 "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/accounts" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/containers" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ErrTempURLKeyNotFound is an error indicating that the Temp URL key was diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 5eccbde4ac..15bb57c748 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Object is a structure that holds information related to a storage object. diff --git a/openstack/objectstorage/v1/objects/testing/fixtures_test.go b/openstack/objectstorage/v1/objects/testing/fixtures_test.go index c4bc2d27ed..5c4127a256 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures_test.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures_test.go @@ -8,9 +8,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/objects" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) type handlerOptions struct { diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index d8e6c11101..c11c7d8df6 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -10,13 +10,13 @@ import ( "testing" "time" - v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" - accountTesting "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/testing" - containerTesting "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/testing" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + v1 "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1" + accountTesting "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/accounts/testing" + containerTesting "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/containers/testing" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/objects" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestContainerNames(t *testing.T) { diff --git a/openstack/objectstorage/v1/objects/urls.go b/openstack/objectstorage/v1/objects/urls.go index c398091e71..4758a04f04 100644 --- a/openstack/objectstorage/v1/objects/urls.go +++ b/openstack/objectstorage/v1/objects/urls.go @@ -3,8 +3,8 @@ package objects import ( "net/url" - "github.com/gophercloud/gophercloud" - v1 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1" + "github.com/gophercloud/gophercloud/v2" + v1 "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1" ) // tempURL returns an unescaped virtual path to generate the HMAC signature. diff --git a/openstack/objectstorage/v1/swauth/requests.go b/openstack/objectstorage/v1/swauth/requests.go index 3d067d8e05..c8a6914aa2 100644 --- a/openstack/objectstorage/v1/swauth/requests.go +++ b/openstack/objectstorage/v1/swauth/requests.go @@ -1,6 +1,6 @@ package swauth -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // AuthOptsBuilder describes struct types that can be accepted by the Auth call. type AuthOptsBuilder interface { diff --git a/openstack/objectstorage/v1/swauth/results.go b/openstack/objectstorage/v1/swauth/results.go index f442f47255..ac3ff31d67 100644 --- a/openstack/objectstorage/v1/swauth/results.go +++ b/openstack/objectstorage/v1/swauth/results.go @@ -1,7 +1,7 @@ package swauth import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // GetAuthResult contains the response from the Auth request. Call its Extract diff --git a/openstack/objectstorage/v1/swauth/testing/fixtures_test.go b/openstack/objectstorage/v1/swauth/testing/fixtures_test.go index 79858f5c87..b8e0b33f24 100644 --- a/openstack/objectstorage/v1/swauth/testing/fixtures_test.go +++ b/openstack/objectstorage/v1/swauth/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/swauth" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/swauth" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // AuthResult is the expected result of AuthOutput diff --git a/openstack/objectstorage/v1/swauth/testing/requests_test.go b/openstack/objectstorage/v1/swauth/testing/requests_test.go index 0850aeff45..d5fde5c6c5 100644 --- a/openstack/objectstorage/v1/swauth/testing/requests_test.go +++ b/openstack/objectstorage/v1/swauth/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/swauth" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/objectstorage/v1/swauth" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestAuth(t *testing.T) { diff --git a/openstack/objectstorage/v1/swauth/urls.go b/openstack/objectstorage/v1/swauth/urls.go index a30cabd60e..5608dc6ffd 100644 --- a/openstack/objectstorage/v1/swauth/urls.go +++ b/openstack/objectstorage/v1/swauth/urls.go @@ -1,6 +1,6 @@ package swauth -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func getURL(c *gophercloud.ProviderClient) string { return c.IdentityBase + "auth/v1.0" diff --git a/openstack/orchestration/v1/apiversions/requests.go b/openstack/orchestration/v1/apiversions/requests.go index f0cd34e2a0..fcb124e27f 100644 --- a/openstack/orchestration/v1/apiversions/requests.go +++ b/openstack/orchestration/v1/apiversions/requests.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListVersions lists all the Neutron API versions available to end-users diff --git a/openstack/orchestration/v1/apiversions/results.go b/openstack/orchestration/v1/apiversions/results.go index a14f7ebaab..2c5a828295 100644 --- a/openstack/orchestration/v1/apiversions/results.go +++ b/openstack/orchestration/v1/apiversions/results.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // APIVersion represents an API version for Neutron. It contains the status of diff --git a/openstack/orchestration/v1/apiversions/testing/requests_test.go b/openstack/orchestration/v1/apiversions/testing/requests_test.go index 9705018d80..ed966c0b3b 100644 --- a/openstack/orchestration/v1/apiversions/testing/requests_test.go +++ b/openstack/orchestration/v1/apiversions/testing/requests_test.go @@ -5,11 +5,11 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/apiversions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/apiversions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListVersions(t *testing.T) { diff --git a/openstack/orchestration/v1/apiversions/urls.go b/openstack/orchestration/v1/apiversions/urls.go index a6a35d4225..deaf717651 100644 --- a/openstack/orchestration/v1/apiversions/urls.go +++ b/openstack/orchestration/v1/apiversions/urls.go @@ -3,8 +3,8 @@ package apiversions import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) func listURL(c *gophercloud.ServiceClient) string { diff --git a/openstack/orchestration/v1/buildinfo/requests.go b/openstack/orchestration/v1/buildinfo/requests.go index d710da3d9d..4706b93090 100644 --- a/openstack/orchestration/v1/buildinfo/requests.go +++ b/openstack/orchestration/v1/buildinfo/requests.go @@ -1,6 +1,6 @@ package buildinfo -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient) (r GetResult) { diff --git a/openstack/orchestration/v1/buildinfo/results.go b/openstack/orchestration/v1/buildinfo/results.go index c3d2cdbeff..70cb576502 100644 --- a/openstack/orchestration/v1/buildinfo/results.go +++ b/openstack/orchestration/v1/buildinfo/results.go @@ -1,7 +1,7 @@ package buildinfo import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Revision represents the API/Engine revision of a Heat deployment. diff --git a/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go b/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go index c240d5f581..72527032d8 100644 --- a/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go +++ b/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/buildinfo" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // GetExpected represents the expected object from a Get request. diff --git a/openstack/orchestration/v1/buildinfo/testing/requests_test.go b/openstack/orchestration/v1/buildinfo/testing/requests_test.go index bd2e164af0..c9cc0df4a0 100644 --- a/openstack/orchestration/v1/buildinfo/testing/requests_test.go +++ b/openstack/orchestration/v1/buildinfo/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/buildinfo" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/buildinfo" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGetTemplate(t *testing.T) { diff --git a/openstack/orchestration/v1/buildinfo/urls.go b/openstack/orchestration/v1/buildinfo/urls.go index 28a2128df8..9f8103f83d 100644 --- a/openstack/orchestration/v1/buildinfo/urls.go +++ b/openstack/orchestration/v1/buildinfo/urls.go @@ -1,6 +1,6 @@ package buildinfo -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func getURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("build_info") diff --git a/openstack/orchestration/v1/resourcetypes/requests.go b/openstack/orchestration/v1/resourcetypes/requests.go index d350cfc443..87c7498c16 100644 --- a/openstack/orchestration/v1/resourcetypes/requests.go +++ b/openstack/orchestration/v1/resourcetypes/requests.go @@ -1,7 +1,7 @@ package resourcetypes import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // SupportStatus is a type for specifying by which support status to filter the diff --git a/openstack/orchestration/v1/resourcetypes/results.go b/openstack/orchestration/v1/resourcetypes/results.go index f5b13a481d..56a2d3dbad 100644 --- a/openstack/orchestration/v1/resourcetypes/results.go +++ b/openstack/orchestration/v1/resourcetypes/results.go @@ -1,7 +1,7 @@ package resourcetypes import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ResourceTypeSummary contains the result of listing an available resource diff --git a/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go b/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go index f750ef1b36..50db64b3f4 100644 --- a/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go +++ b/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/resourcetypes" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/resourcetypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const BasicListOutput = ` diff --git a/openstack/orchestration/v1/resourcetypes/testing/requests_test.go b/openstack/orchestration/v1/resourcetypes/testing/requests_test.go index a6753ff38d..8f0f3540f0 100644 --- a/openstack/orchestration/v1/resourcetypes/testing/requests_test.go +++ b/openstack/orchestration/v1/resourcetypes/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/resourcetypes" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/resourcetypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestBasicListResourceTypes(t *testing.T) { diff --git a/openstack/orchestration/v1/resourcetypes/urls.go b/openstack/orchestration/v1/resourcetypes/urls.go index 0c4bd075ac..790aa6eced 100644 --- a/openstack/orchestration/v1/resourcetypes/urls.go +++ b/openstack/orchestration/v1/resourcetypes/urls.go @@ -1,6 +1,6 @@ package resourcetypes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( resTypesPath = "resource_types" diff --git a/openstack/orchestration/v1/stackevents/requests.go b/openstack/orchestration/v1/stackevents/requests.go index 117b52bdcd..c6d21c185e 100644 --- a/openstack/orchestration/v1/stackevents/requests.go +++ b/openstack/orchestration/v1/stackevents/requests.go @@ -1,8 +1,8 @@ package stackevents import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Find retrieves stack events for the given stack name. diff --git a/openstack/orchestration/v1/stackevents/results.go b/openstack/orchestration/v1/stackevents/results.go index afe81b3771..45673b06bd 100644 --- a/openstack/orchestration/v1/stackevents/results.go +++ b/openstack/orchestration/v1/stackevents/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Event represents a stack event. diff --git a/openstack/orchestration/v1/stackevents/testing/fixtures_test.go b/openstack/orchestration/v1/stackevents/testing/fixtures_test.go index 01a64583c1..28dd6cc01e 100644 --- a/openstack/orchestration/v1/stackevents/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stackevents/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stackevents" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) var Timestamp1, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z") diff --git a/openstack/orchestration/v1/stackevents/testing/requests_test.go b/openstack/orchestration/v1/stackevents/testing/requests_test.go index 6dabfd23ef..c3706e0991 100644 --- a/openstack/orchestration/v1/stackevents/testing/requests_test.go +++ b/openstack/orchestration/v1/stackevents/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackevents" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stackevents" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestFindEvents(t *testing.T) { diff --git a/openstack/orchestration/v1/stackevents/urls.go b/openstack/orchestration/v1/stackevents/urls.go index 6b6b330894..f88465c2dd 100644 --- a/openstack/orchestration/v1/stackevents/urls.go +++ b/openstack/orchestration/v1/stackevents/urls.go @@ -1,6 +1,6 @@ package stackevents -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func findURL(c *gophercloud.ServiceClient, stackName string) string { return c.ServiceURL("stacks", stackName, "events") diff --git a/openstack/orchestration/v1/stackresources/requests.go b/openstack/orchestration/v1/stackresources/requests.go index 466b7ec718..21260fffad 100644 --- a/openstack/orchestration/v1/stackresources/requests.go +++ b/openstack/orchestration/v1/stackresources/requests.go @@ -1,8 +1,8 @@ package stackresources import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Find retrieves stack resources for the given stack name. diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go index 8de3fba1a0..48e1f3e649 100644 --- a/openstack/orchestration/v1/stackresources/results.go +++ b/openstack/orchestration/v1/stackresources/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Resource represents a stack resource. diff --git a/openstack/orchestration/v1/stackresources/testing/fixtures_test.go b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go index 9382736441..63b79569b0 100644 --- a/openstack/orchestration/v1/stackresources/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stackresources" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) var Create_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:57:17Z") diff --git a/openstack/orchestration/v1/stackresources/testing/requests_test.go b/openstack/orchestration/v1/stackresources/testing/requests_test.go index 55ca4a0be4..60624d2823 100644 --- a/openstack/orchestration/v1/stackresources/testing/requests_test.go +++ b/openstack/orchestration/v1/stackresources/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "sort" "testing" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stackresources" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stackresources" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestFindResources(t *testing.T) { diff --git a/openstack/orchestration/v1/stackresources/urls.go b/openstack/orchestration/v1/stackresources/urls.go index 6b332f17e8..2372cdfdb0 100644 --- a/openstack/orchestration/v1/stackresources/urls.go +++ b/openstack/orchestration/v1/stackresources/urls.go @@ -1,6 +1,6 @@ package stackresources -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func findURL(c *gophercloud.ServiceClient, stackName string) string { return c.ServiceURL("stacks", stackName, "resources") diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index 6f49bd716c..e0fec79a32 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -12,9 +12,9 @@ of the application framework or component. import ( "fmt" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacks" ) diff --git a/openstack/orchestration/v1/stacks/environment_test.go b/openstack/orchestration/v1/stacks/environment_test.go index 9a69d833cb..21e9f716ae 100644 --- a/openstack/orchestration/v1/stacks/environment_test.go +++ b/openstack/orchestration/v1/stacks/environment_test.go @@ -3,7 +3,7 @@ package stacks import ( "testing" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestEnvironmentValidation(t *testing.T) { diff --git a/openstack/orchestration/v1/stacks/errors.go b/openstack/orchestration/v1/stacks/errors.go index 2d51736707..a1108cbf03 100644 --- a/openstack/orchestration/v1/stacks/errors.go +++ b/openstack/orchestration/v1/stacks/errors.go @@ -3,7 +3,7 @@ package stacks import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type ErrInvalidEnvironment struct { diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index 41ca7063c0..af7f260642 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder is the interface options structs have to satisfy in order diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go index a741164b6f..b501d960a5 100644 --- a/openstack/orchestration/v1/stacks/results.go +++ b/openstack/orchestration/v1/stacks/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreatedStack represents the object extracted from a Create operation. diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index d2ddbb62fa..bb71ac9dfa 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -7,7 +7,7 @@ import ( "reflect" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" yaml "gopkg.in/yaml.v2" ) diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go index 7fc273b192..593f464580 100644 --- a/openstack/orchestration/v1/stacks/template_test.go +++ b/openstack/orchestration/v1/stacks/template_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTemplateValidation(t *testing.T) { diff --git a/openstack/orchestration/v1/stacks/testing/fixtures_test.go b/openstack/orchestration/v1/stacks/testing/fixtures_test.go index a69bfa3724..d473a50e2f 100644 --- a/openstack/orchestration/v1/stacks/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stacks/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacks" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) var Create_time, _ = time.Parse(time.RFC3339, "2018-06-26T07:58:17Z") diff --git a/openstack/orchestration/v1/stacks/testing/requests_test.go b/openstack/orchestration/v1/stacks/testing/requests_test.go index 9f0932a4d2..675ce103c0 100644 --- a/openstack/orchestration/v1/stacks/testing/requests_test.go +++ b/openstack/orchestration/v1/stacks/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacks" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateStack(t *testing.T) { diff --git a/openstack/orchestration/v1/stacks/urls.go b/openstack/orchestration/v1/stacks/urls.go index b909caac86..0d7b09dd6d 100644 --- a/openstack/orchestration/v1/stacks/urls.go +++ b/openstack/orchestration/v1/stacks/urls.go @@ -1,6 +1,6 @@ package stacks -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("stacks") diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go index 21a83f7e79..bb836c7ac1 100644 --- a/openstack/orchestration/v1/stacks/utils.go +++ b/openstack/orchestration/v1/stacks/utils.go @@ -8,7 +8,7 @@ import ( "path/filepath" "reflect" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" yaml "gopkg.in/yaml.v2" ) diff --git a/openstack/orchestration/v1/stacks/utils_test.go b/openstack/orchestration/v1/stacks/utils_test.go index adc97d14aa..3f7250b0fc 100644 --- a/openstack/orchestration/v1/stacks/utils_test.go +++ b/openstack/orchestration/v1/stacks/utils_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - th "github.com/gophercloud/gophercloud/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestToStringKeys(t *testing.T) { diff --git a/openstack/orchestration/v1/stacktemplates/requests.go b/openstack/orchestration/v1/stacktemplates/requests.go index 58eafffcc4..e29de588ad 100644 --- a/openstack/orchestration/v1/stacktemplates/requests.go +++ b/openstack/orchestration/v1/stacktemplates/requests.go @@ -1,6 +1,6 @@ package stacktemplates -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { diff --git a/openstack/orchestration/v1/stacktemplates/results.go b/openstack/orchestration/v1/stacktemplates/results.go index bca959b9c1..56b24ff196 100644 --- a/openstack/orchestration/v1/stacktemplates/results.go +++ b/openstack/orchestration/v1/stacktemplates/results.go @@ -3,7 +3,7 @@ package stacktemplates import ( "encoding/json" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // GetResult represents the result of a Get operation. diff --git a/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go b/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go index 23ec579172..cfef51adab 100644 --- a/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacktemplates" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // GetExpected represents the expected object from a Get request. diff --git a/openstack/orchestration/v1/stacktemplates/testing/requests_test.go b/openstack/orchestration/v1/stacktemplates/testing/requests_test.go index 442bcb7a7f..785ef6605e 100644 --- a/openstack/orchestration/v1/stacktemplates/testing/requests_test.go +++ b/openstack/orchestration/v1/stacktemplates/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/orchestration/v1/stacktemplates" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacktemplates" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGetTemplate(t *testing.T) { diff --git a/openstack/orchestration/v1/stacktemplates/urls.go b/openstack/orchestration/v1/stacktemplates/urls.go index aed6b4b9de..2b188f3427 100644 --- a/openstack/orchestration/v1/stacktemplates/urls.go +++ b/openstack/orchestration/v1/stacktemplates/urls.go @@ -1,6 +1,6 @@ package stacktemplates -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func getURL(c *gophercloud.ServiceClient, stackName, stackID string) string { return c.ServiceURL("stacks", stackName, stackID, "template") diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 6c638cd58b..156ec2abb3 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -1,8 +1,8 @@ package resourceproviders import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index 225f18ef8c..19b71103e1 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -1,8 +1,8 @@ package resourceproviders import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type ResourceProviderLinks struct { diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index cdb8404c3a..e663cab280 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -5,10 +5,10 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ResourceProviderTestID = "99c09379-6e52-4ef8-9a95-b9ce6f68452e" diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 741aeee934..be6554bc85 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/placement/v1/resourceproviders" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListResourceProviders(t *testing.T) { diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 5bcd53e80e..037149b684 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -1,6 +1,6 @@ package resourceproviders -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" const ( apiName = "resource_providers" diff --git a/openstack/sharedfilesystems/apiversions/requests.go b/openstack/sharedfilesystems/apiversions/requests.go index 244a7acba4..a82c605c10 100644 --- a/openstack/sharedfilesystems/apiversions/requests.go +++ b/openstack/sharedfilesystems/apiversions/requests.go @@ -1,8 +1,8 @@ package apiversions import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List lists all the API versions available to end-users. diff --git a/openstack/sharedfilesystems/apiversions/results.go b/openstack/sharedfilesystems/apiversions/results.go index 0c915b8f52..39ddcdc3eb 100644 --- a/openstack/sharedfilesystems/apiversions/results.go +++ b/openstack/sharedfilesystems/apiversions/results.go @@ -3,8 +3,8 @@ package apiversions import ( "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // APIVersion represents an API version for the Shared File System service. diff --git a/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go b/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go index 9707d62af2..949434480f 100644 --- a/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ManilaAPIVersionResponse = ` diff --git a/openstack/sharedfilesystems/apiversions/testing/requests_test.go b/openstack/sharedfilesystems/apiversions/testing/requests_test.go index 8b5501fd8e..7a7eef7e5f 100644 --- a/openstack/sharedfilesystems/apiversions/testing/requests_test.go +++ b/openstack/sharedfilesystems/apiversions/testing/requests_test.go @@ -3,9 +3,9 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/apiversions" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/apiversions" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListAPIVersions(t *testing.T) { diff --git a/openstack/sharedfilesystems/apiversions/urls.go b/openstack/sharedfilesystems/apiversions/urls.go index 41aebdc5f2..47f8116620 100644 --- a/openstack/sharedfilesystems/apiversions/urls.go +++ b/openstack/sharedfilesystems/apiversions/urls.go @@ -3,8 +3,8 @@ package apiversions import ( "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" ) func getURL(c *gophercloud.ServiceClient, version string) string { diff --git a/openstack/sharedfilesystems/v2/availabilityzones/requests.go b/openstack/sharedfilesystems/v2/availabilityzones/requests.go index df10b856eb..15f9c228b2 100644 --- a/openstack/sharedfilesystems/v2/availabilityzones/requests.go +++ b/openstack/sharedfilesystems/v2/availabilityzones/requests.go @@ -1,8 +1,8 @@ package availabilityzones import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // List will return the existing availability zones. diff --git a/openstack/sharedfilesystems/v2/availabilityzones/results.go b/openstack/sharedfilesystems/v2/availabilityzones/results.go index c685f04ea7..f756755aea 100644 --- a/openstack/sharedfilesystems/v2/availabilityzones/results.go +++ b/openstack/sharedfilesystems/v2/availabilityzones/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // AvailabilityZone contains all the information associated with an OpenStack diff --git a/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go index e5db8cda66..d6cd25830f 100644 --- a/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockListResponse(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/availabilityzones/testing/requests_test.go b/openstack/sharedfilesystems/v2/availabilityzones/testing/requests_test.go index 76c8574fc5..dbe00da14b 100644 --- a/openstack/sharedfilesystems/v2/availabilityzones/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/availabilityzones/testing/requests_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/availabilityzones" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/availabilityzones" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // Verifies that availability zones can be listed correctly diff --git a/openstack/sharedfilesystems/v2/availabilityzones/urls.go b/openstack/sharedfilesystems/v2/availabilityzones/urls.go index fb4cdcf4e2..f78b7c8f97 100644 --- a/openstack/sharedfilesystems/v2/availabilityzones/urls.go +++ b/openstack/sharedfilesystems/v2/availabilityzones/urls.go @@ -1,6 +1,6 @@ package availabilityzones -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("os-availability-zone") diff --git a/openstack/sharedfilesystems/v2/errors/errors.go b/openstack/sharedfilesystems/v2/errors/errors.go index cd88a8ac1f..1e56210b18 100644 --- a/openstack/sharedfilesystems/v2/errors/errors.go +++ b/openstack/sharedfilesystems/v2/errors/errors.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) type ManilaError struct { diff --git a/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go index dc7a394fb4..5a6f7f013c 100644 --- a/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const shareEndpoint = "/shares" diff --git a/openstack/sharedfilesystems/v2/errors/testing/request_test.go b/openstack/sharedfilesystems/v2/errors/testing/request_test.go index 4d590d5e7d..3c0f5f2500 100644 --- a/openstack/sharedfilesystems/v2/errors/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/errors/testing/request_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/errors" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/errors" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/messages/requests.go b/openstack/sharedfilesystems/v2/messages/requests.go index 39c7e82a74..53be2c2b45 100644 --- a/openstack/sharedfilesystems/v2/messages/requests.go +++ b/openstack/sharedfilesystems/v2/messages/requests.go @@ -1,8 +1,8 @@ package messages import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Delete will delete the existing Message with the provided ID. diff --git a/openstack/sharedfilesystems/v2/messages/results.go b/openstack/sharedfilesystems/v2/messages/results.go index 8507454bfa..23fe6b3e65 100644 --- a/openstack/sharedfilesystems/v2/messages/results.go +++ b/openstack/sharedfilesystems/v2/messages/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Message contains all the information associated with an OpenStack diff --git a/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go index da1c98adb9..111b6dbf97 100644 --- a/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockDeleteResponse(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/messages/testing/requests_test.go b/openstack/sharedfilesystems/v2/messages/testing/requests_test.go index c92297c6c5..257b18c939 100644 --- a/openstack/sharedfilesystems/v2/messages/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/messages/testing/requests_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/messages" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/messages" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // Verifies that message deletion works diff --git a/openstack/sharedfilesystems/v2/messages/urls.go b/openstack/sharedfilesystems/v2/messages/urls.go index 7c2c54a87f..7f9f70d0ea 100644 --- a/openstack/sharedfilesystems/v2/messages/urls.go +++ b/openstack/sharedfilesystems/v2/messages/urls.go @@ -1,6 +1,6 @@ package messages -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("messages") diff --git a/openstack/sharedfilesystems/v2/replicas/requests.go b/openstack/sharedfilesystems/v2/replicas/requests.go index 6810f2e5c7..061f7a1dc5 100644 --- a/openstack/sharedfilesystems/v2/replicas/requests.go +++ b/openstack/sharedfilesystems/v2/replicas/requests.go @@ -1,8 +1,8 @@ package replicas import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/sharedfilesystems/v2/replicas/results.go b/openstack/sharedfilesystems/v2/replicas/results.go index bf53de3d0b..764139f0c6 100644 --- a/openstack/sharedfilesystems/v2/replicas/results.go +++ b/openstack/sharedfilesystems/v2/replicas/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) const ( diff --git a/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go index edfd5698e1..e2470ab177 100644 --- a/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ( diff --git a/openstack/sharedfilesystems/v2/replicas/testing/request_test.go b/openstack/sharedfilesystems/v2/replicas/testing/request_test.go index df070061cc..b3717af3a7 100644 --- a/openstack/sharedfilesystems/v2/replicas/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/replicas/testing/request_test.go @@ -4,10 +4,10 @@ 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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/replicas" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func getClient(microVersion string) *gophercloud.ServiceClient { diff --git a/openstack/sharedfilesystems/v2/replicas/urls.go b/openstack/sharedfilesystems/v2/replicas/urls.go index 99fa60416d..efb64476ec 100644 --- a/openstack/sharedfilesystems/v2/replicas/urls.go +++ b/openstack/sharedfilesystems/v2/replicas/urls.go @@ -1,6 +1,6 @@ package replicas -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("share-replicas") diff --git a/openstack/sharedfilesystems/v2/schedulerstats/requests.go b/openstack/sharedfilesystems/v2/schedulerstats/requests.go index c325c58e45..3190246e2d 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/requests.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/requests.go @@ -1,8 +1,8 @@ package schedulerstats import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/sharedfilesystems/v2/schedulerstats/results.go b/openstack/sharedfilesystems/v2/schedulerstats/results.go index 671a314abb..fd25924851 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/results.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/results.go @@ -4,7 +4,7 @@ import ( "encoding/json" "math" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Capabilities represents the information of an individual Pool. diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go index 244e068b94..cc1d664d88 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/schedulerstats" - "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/schedulerstats" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const PoolsListBody = ` diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go index cd1df62cce..ce3b7d43af 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go @@ -3,10 +3,10 @@ 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" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/schedulerstats" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListPoolsDetail(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/schedulerstats/urls.go b/openstack/sharedfilesystems/v2/schedulerstats/urls.go index 1c907ffd1d..2017a0f72a 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/urls.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/urls.go @@ -1,6 +1,6 @@ package schedulerstats -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func poolsListURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("scheduler-stats", "pools") diff --git a/openstack/sharedfilesystems/v2/securityservices/requests.go b/openstack/sharedfilesystems/v2/securityservices/requests.go index 636c7b2f85..8a781144be 100644 --- a/openstack/sharedfilesystems/v2/securityservices/requests.go +++ b/openstack/sharedfilesystems/v2/securityservices/requests.go @@ -1,8 +1,8 @@ package securityservices import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type SecurityServiceType string diff --git a/openstack/sharedfilesystems/v2/securityservices/results.go b/openstack/sharedfilesystems/v2/securityservices/results.go index 0f510f1570..da007d62a0 100644 --- a/openstack/sharedfilesystems/v2/securityservices/results.go +++ b/openstack/sharedfilesystems/v2/securityservices/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // SecurityService contains all the information associated with an OpenStack diff --git a/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go index 528c854537..c0b7645770 100644 --- a/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockCreateResponse(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go b/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go index 1dcd6353e1..2c854ac42a 100644 --- a/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/securityservices" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/securityservices" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // Verifies that a security service can be created correctly diff --git a/openstack/sharedfilesystems/v2/securityservices/urls.go b/openstack/sharedfilesystems/v2/securityservices/urls.go index c19b1f1062..fe4585ebda 100644 --- a/openstack/sharedfilesystems/v2/securityservices/urls.go +++ b/openstack/sharedfilesystems/v2/securityservices/urls.go @@ -1,6 +1,6 @@ package securityservices -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("security-services") diff --git a/openstack/sharedfilesystems/v2/services/requests.go b/openstack/sharedfilesystems/v2/services/requests.go index 908e5a65c6..47e2370d06 100644 --- a/openstack/sharedfilesystems/v2/services/requests.go +++ b/openstack/sharedfilesystems/v2/services/requests.go @@ -1,8 +1,8 @@ package services import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the List diff --git a/openstack/sharedfilesystems/v2/services/results.go b/openstack/sharedfilesystems/v2/services/results.go index 03ecfda897..ae5b9513ae 100644 --- a/openstack/sharedfilesystems/v2/services/results.go +++ b/openstack/sharedfilesystems/v2/services/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // Service represents a Shared File System service in the OpenStack cloud. diff --git a/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go index e36692d3f9..67f7b94c78 100644 --- a/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/services" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // ServiceListBody is sample response to the List call diff --git a/openstack/sharedfilesystems/v2/services/testing/requests_test.go b/openstack/sharedfilesystems/v2/services/testing/requests_test.go index 1513379afa..530c0b7760 100644 --- a/openstack/sharedfilesystems/v2/services/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/services/testing/requests_test.go @@ -3,10 +3,10 @@ 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" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/services" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListServices(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/services/urls.go b/openstack/sharedfilesystems/v2/services/urls.go index 0e4acc11f5..8571ebc09d 100644 --- a/openstack/sharedfilesystems/v2/services/urls.go +++ b/openstack/sharedfilesystems/v2/services/urls.go @@ -1,6 +1,6 @@ package services -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func listURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("services") diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/requests.go b/openstack/sharedfilesystems/v2/shareaccessrules/requests.go index 8f96899c30..2d48827837 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/requests.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/requests.go @@ -1,7 +1,7 @@ package shareaccessrules import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Get retrieves details about a share access rule. diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/results.go b/openstack/sharedfilesystems/v2/shareaccessrules/results.go index 4c8a18de60..fa63c05e68 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/results.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/results.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // ShareAccess contains information associated with an OpenStack share access rule. diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go index d4b96f2e47..d82014357d 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ( diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go index cbbfc634e5..4bfd639f27 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go @@ -6,10 +6,10 @@ import ( "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" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shareaccessrules" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestGet(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/urls.go b/openstack/sharedfilesystems/v2/shareaccessrules/urls.go index 2ff1337840..cd660d9950 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/urls.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/urls.go @@ -3,7 +3,7 @@ package shareaccessrules import ( "fmt" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) const shareAccessRulesEndpoint = "share-access-rules" diff --git a/openstack/sharedfilesystems/v2/sharenetworks/requests.go b/openstack/sharedfilesystems/v2/sharenetworks/requests.go index f8976b76ea..66a5b38523 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/requests.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/requests.go @@ -1,8 +1,8 @@ package sharenetworks import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/sharedfilesystems/v2/sharenetworks/results.go b/openstack/sharedfilesystems/v2/sharenetworks/results.go index 76c35f78d6..5d3537c007 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/results.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ShareNetwork contains all the information associated with an OpenStack diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go index ee5567a636..9ce5d2b928 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func createReq(name, description, network, subnetwork string) string { diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go index 61beefca21..c951ad7341 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharenetworks" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharenetworks" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // Verifies that a share network can be created correctly diff --git a/openstack/sharedfilesystems/v2/sharenetworks/urls.go b/openstack/sharedfilesystems/v2/sharenetworks/urls.go index 667bd2a526..13f2132ee3 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/urls.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/urls.go @@ -1,6 +1,6 @@ package sharenetworks -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("share-networks") diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 68ef32e17d..c5b38628d5 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -1,8 +1,8 @@ package shares import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index 3204481a8f..d3fe9d8437 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) const ( diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go index 19c3aa15f5..f6370a1f85 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ( diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index b7880c6238..abe2105df2 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/shares" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/shares/urls.go b/openstack/sharedfilesystems/v2/shares/urls.go index f5c347cbda..36623852be 100644 --- a/openstack/sharedfilesystems/v2/shares/urls.go +++ b/openstack/sharedfilesystems/v2/shares/urls.go @@ -1,6 +1,6 @@ package shares -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("shares") diff --git a/openstack/sharedfilesystems/v2/sharetransfers/requests.go b/openstack/sharedfilesystems/v2/sharetransfers/requests.go index 40ef8e7dbb..fc5de44d51 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/requests.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/requests.go @@ -1,8 +1,8 @@ package sharetransfers import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/sharedfilesystems/v2/sharetransfers/results.go b/openstack/sharedfilesystems/v2/sharetransfers/results.go index bc91e3a165..02b8ec71b0 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/results.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) const ( diff --git a/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go index a492d4597f..980292f38f 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go @@ -6,10 +6,10 @@ import ( "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" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetransfers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ListOutput = ` diff --git a/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go index 75ed482c45..bd2e454b46 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go @@ -3,10 +3,10 @@ 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" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetransfers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateTransfer(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/sharetransfers/urls.go b/openstack/sharedfilesystems/v2/sharetransfers/urls.go index 1513f38cc9..4d48f93c83 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/urls.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/urls.go @@ -1,6 +1,6 @@ package sharetransfers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func transferURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("share-transfers") diff --git a/openstack/sharedfilesystems/v2/sharetypes/requests.go b/openstack/sharedfilesystems/v2/sharetypes/requests.go index 11f554b563..93c6c26177 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/requests.go +++ b/openstack/sharedfilesystems/v2/sharetypes/requests.go @@ -1,8 +1,8 @@ package sharetypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/sharedfilesystems/v2/sharetypes/results.go b/openstack/sharedfilesystems/v2/sharetypes/results.go index ce5fbccb03..0ac2cf0bbd 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/results.go +++ b/openstack/sharedfilesystems/v2/sharetypes/results.go @@ -1,8 +1,8 @@ package sharetypes import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // ShareType contains all the information associated with an OpenStack diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go index 7ba85ed189..767d42807a 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func MockCreateResponse(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go index 85a9e5a974..9329e0694c 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go @@ -3,10 +3,10 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/sharetypes" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetypes" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // Verifies that a share type can be created correctly diff --git a/openstack/sharedfilesystems/v2/sharetypes/urls.go b/openstack/sharedfilesystems/v2/sharetypes/urls.go index 42779b1f97..35afec8f5c 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/urls.go +++ b/openstack/sharedfilesystems/v2/sharetypes/urls.go @@ -1,6 +1,6 @@ package sharetypes -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("types") diff --git a/openstack/sharedfilesystems/v2/snapshots/requests.go b/openstack/sharedfilesystems/v2/snapshots/requests.go index bbdde5eac1..efaccff78a 100644 --- a/openstack/sharedfilesystems/v2/snapshots/requests.go +++ b/openstack/sharedfilesystems/v2/snapshots/requests.go @@ -1,8 +1,8 @@ package snapshots import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/sharedfilesystems/v2/snapshots/results.go b/openstack/sharedfilesystems/v2/snapshots/results.go index 44337b17a7..9cd1b65236 100644 --- a/openstack/sharedfilesystems/v2/snapshots/results.go +++ b/openstack/sharedfilesystems/v2/snapshots/results.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) const ( diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go index fb677918dd..75432bdb9f 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) const ( diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go index 52f9c33a23..2d0518e35e 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/sharedfilesystems/v2/snapshots" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/snapshots" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreate(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/snapshots/urls.go b/openstack/sharedfilesystems/v2/snapshots/urls.go index 138d97f350..447de44580 100644 --- a/openstack/sharedfilesystems/v2/snapshots/urls.go +++ b/openstack/sharedfilesystems/v2/snapshots/urls.go @@ -1,6 +1,6 @@ package snapshots -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("snapshots") diff --git a/openstack/testing/client_test.go b/openstack/testing/client_test.go index 6f94e44e0d..9efa0ac182 100644 --- a/openstack/testing/client_test.go +++ b/openstack/testing/client_test.go @@ -5,9 +5,9 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) const ID = "0123456789" diff --git a/openstack/testing/endpoint_location_test.go b/openstack/testing/endpoint_location_test.go index ce92d3dd06..8194d31e41 100644 --- a/openstack/testing/endpoint_location_test.go +++ b/openstack/testing/endpoint_location_test.go @@ -3,11 +3,11 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" + tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // Service catalog fixtures take too much vertical space! diff --git a/openstack/utils/choose_version.go b/openstack/utils/choose_version.go index ed34197d2c..d3c27e241b 100644 --- a/openstack/utils/choose_version.go +++ b/openstack/utils/choose_version.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // Version is a supported API version, corresponding to a vN package within the appropriate service. diff --git a/openstack/utils/testing/base_endpoint_test.go b/openstack/utils/testing/base_endpoint_test.go index 944536b8bf..d361b054ae 100644 --- a/openstack/utils/testing/base_endpoint_test.go +++ b/openstack/utils/testing/base_endpoint_test.go @@ -3,8 +3,8 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/openstack/utils" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/openstack/utils" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) type endpointTestCases struct { diff --git a/openstack/utils/testing/choose_version_test.go b/openstack/utils/testing/choose_version_test.go index cf09562aec..53e76a6523 100644 --- a/openstack/utils/testing/choose_version_test.go +++ b/openstack/utils/testing/choose_version_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/utils" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" + "github.com/gophercloud/gophercloud/v2/testhelper" ) func setupVersionHandler() { diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index 1084cbcc90..851c8c9b35 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -7,8 +7,8 @@ import ( "reflect" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extension to add additional parameters to the Create request. diff --git a/openstack/workflow/v2/crontriggers/results.go b/openstack/workflow/v2/crontriggers/results.go index 2e548f4836..41476c5ddd 100644 --- a/openstack/workflow/v2/crontriggers/results.go +++ b/openstack/workflow/v2/crontriggers/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index cf314e5681..b4f092823b 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -8,10 +8,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/workflow/v2/crontriggers" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/crontriggers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateCronTrigger(t *testing.T) { diff --git a/openstack/workflow/v2/crontriggers/urls.go b/openstack/workflow/v2/crontriggers/urls.go index 853dbe30c4..a49359b1f6 100644 --- a/openstack/workflow/v2/crontriggers/urls.go +++ b/openstack/workflow/v2/crontriggers/urls.go @@ -1,6 +1,6 @@ package crontriggers -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("cron_triggers") diff --git a/openstack/workflow/v2/executions/requests.go b/openstack/workflow/v2/executions/requests.go index 36d81a2bb4..d9e3e7ac19 100644 --- a/openstack/workflow/v2/executions/requests.go +++ b/openstack/workflow/v2/executions/requests.go @@ -7,8 +7,8 @@ import ( "reflect" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extension to add additional parameters to the Create request. diff --git a/openstack/workflow/v2/executions/results.go b/openstack/workflow/v2/executions/results.go index b0e88f1255..68abd2f31c 100644 --- a/openstack/workflow/v2/executions/results.go +++ b/openstack/workflow/v2/executions/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type commonResult struct { diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index 5eae95904e..841739d730 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -8,10 +8,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/workflow/v2/executions" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/executions" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateExecution(t *testing.T) { diff --git a/openstack/workflow/v2/executions/urls.go b/openstack/workflow/v2/executions/urls.go index d593e9d28f..dd763723aa 100644 --- a/openstack/workflow/v2/executions/urls.go +++ b/openstack/workflow/v2/executions/urls.go @@ -1,6 +1,6 @@ package executions -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("executions") diff --git a/openstack/workflow/v2/workflows/requests.go b/openstack/workflow/v2/workflows/requests.go index f0bd9cc5c1..4849ba5fb0 100644 --- a/openstack/workflow/v2/workflows/requests.go +++ b/openstack/workflow/v2/workflows/requests.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateOptsBuilder allows extension to add additional parameters to the Create request. diff --git a/openstack/workflow/v2/workflows/results.go b/openstack/workflow/v2/workflows/results.go index d2063ae5cd..c0c9cf1d27 100644 --- a/openstack/workflow/v2/workflows/results.go +++ b/openstack/workflow/v2/workflows/results.go @@ -4,8 +4,8 @@ import ( "encoding/json" "time" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // CreateResult is the response of a Post operations. Call its Extract method to interpret it as a list of Workflows. diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index dfc6857c42..f6091c483e 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -9,10 +9,10 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/openstack/workflow/v2/workflows" - "github.com/gophercloud/gophercloud/pagination" - th "github.com/gophercloud/gophercloud/testhelper" - fake "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2/openstack/workflow/v2/workflows" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestCreateWorkflow(t *testing.T) { diff --git a/openstack/workflow/v2/workflows/urls.go b/openstack/workflow/v2/workflows/urls.go index 6c3f80d4b8..8243c991b4 100644 --- a/openstack/workflow/v2/workflows/urls.go +++ b/openstack/workflow/v2/workflows/urls.go @@ -1,7 +1,7 @@ package workflows import ( - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) func createURL(client *gophercloud.ServiceClient) string { diff --git a/pagination/http.go b/pagination/http.go index 4e5f5d5c62..7781df4528 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -8,7 +8,7 @@ import ( "net/url" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // PageResult stores the HTTP response that returned the current page of results. diff --git a/pagination/linked.go b/pagination/linked.go index a664e05673..a5d8727886 100644 --- a/pagination/linked.go +++ b/pagination/linked.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result. diff --git a/pagination/marker.go b/pagination/marker.go index 52e53bae85..228bbc1612 100644 --- a/pagination/marker.go +++ b/pagination/marker.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager. diff --git a/pagination/pager.go b/pagination/pager.go index 8bc6680804..107279bb57 100644 --- a/pagination/pager.go +++ b/pagination/pager.go @@ -8,7 +8,7 @@ import ( "reflect" "strings" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) var ( diff --git a/pagination/single.go b/pagination/single.go index 4251d6491e..ba1176ec9a 100644 --- a/pagination/single.go +++ b/pagination/single.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/v2" ) // SinglePageBase may be embedded in a Page that contains all of the results from an operation at once. diff --git a/pagination/testing/linked_test.go b/pagination/testing/linked_test.go index 3533e445a3..93e5278b47 100644 --- a/pagination/testing/linked_test.go +++ b/pagination/testing/linked_test.go @@ -6,8 +6,8 @@ import ( "reflect" "testing" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" ) // LinkedPager sample and test cases. diff --git a/pagination/testing/marker_test.go b/pagination/testing/marker_test.go index 7b1a6daf4a..2ca5035ecc 100644 --- a/pagination/testing/marker_test.go +++ b/pagination/testing/marker_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" ) // MarkerPager sample and test cases. diff --git a/pagination/testing/pagination_test.go b/pagination/testing/pagination_test.go index 170dca45ca..b730676d4e 100644 --- a/pagination/testing/pagination_test.go +++ b/pagination/testing/pagination_test.go @@ -1,8 +1,8 @@ package testing import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/testhelper" ) func createClient() *gophercloud.ServiceClient { diff --git a/pagination/testing/single_test.go b/pagination/testing/single_test.go index 8d95e948bf..094ed85270 100644 --- a/pagination/testing/single_test.go +++ b/pagination/testing/single_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/pagination" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" ) // SinglePage sample and test cases. diff --git a/provider_client.go b/provider_client.go index bd6a62040e..2ee827c11b 100644 --- a/provider_client.go +++ b/provider_client.go @@ -10,7 +10,7 @@ import ( "strings" "sync" - "github.com/gophercloud/gophercloud/internal/ctxt" + "github.com/gophercloud/gophercloud/v2/internal/ctxt" ) // DefaultUserAgent is the default User-Agent string set in the request header. diff --git a/testhelper/client/fake.go b/testhelper/client/fake.go index 3d81cc97b9..6e25f759d7 100644 --- a/testhelper/client/fake.go +++ b/testhelper/client/fake.go @@ -1,8 +1,8 @@ package client import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/testhelper" ) // Fake token to use. diff --git a/testhelper/fixture/helper.go b/testhelper/fixture/helper.go index fe98c86f99..a0f3b5c7b2 100644 --- a/testhelper/fixture/helper.go +++ b/testhelper/fixture/helper.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func SetupHandler(t *testing.T, url, method, requestBody, responseBody string, status int) { diff --git a/testing/endpoint_search_test.go b/testing/endpoint_search_test.go index 22476cbb14..278955ecdf 100644 --- a/testing/endpoint_search_test.go +++ b/testing/endpoint_search_test.go @@ -3,8 +3,8 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestApplyDefaultsToEndpointOpts(t *testing.T) { diff --git a/testing/errors_test.go b/testing/errors_test.go index 2663c6bc77..d9e6156adc 100644 --- a/testing/errors_test.go +++ b/testing/errors_test.go @@ -4,8 +4,8 @@ import ( "errors" "testing" - "github.com/gophercloud/gophercloud" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func returnsUnexpectedResp(code int) gophercloud.ErrUnexpectedResponseCode { diff --git a/testing/params_test.go b/testing/params_test.go index 9d0de5d30f..1303cf6fb9 100644 --- a/testing/params_test.go +++ b/testing/params_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestMaybeString(t *testing.T) { diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 05a0c50f64..fd15af6058 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -14,9 +14,9 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - th "github.com/gophercloud/gophercloud/testhelper" - "github.com/gophercloud/gophercloud/testhelper/client" + "github.com/gophercloud/gophercloud/v2" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestAuthenticatedHeaders(t *testing.T) { diff --git a/testing/results_test.go b/testing/results_test.go index ddcb1322a4..8bfcc03683 100644 --- a/testing/results_test.go +++ b/testing/results_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/gophercloud/gophercloud" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) var singleResponse = ` diff --git a/testing/service_client_test.go b/testing/service_client_test.go index 034fdc1d93..63524e92d5 100644 --- a/testing/service_client_test.go +++ b/testing/service_client_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestServiceURL(t *testing.T) { diff --git a/testing/util_test.go b/testing/util_test.go index b0430dffda..34a88e0b3d 100644 --- a/testing/util_test.go +++ b/testing/util_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud" - th "github.com/gophercloud/gophercloud/testhelper" + "github.com/gophercloud/gophercloud/v2" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestWaitFor(t *testing.T) { From 1d22e6f0758751af2f16f47bf6322f037dfb7ee8 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 6 Feb 2024 13:52:51 +0100 Subject: [PATCH 1744/2296] Prepare v2.0.0-beta.1 Update the version string in the User Agent and update the changelog with an edited output of the command: ```shell jq -r '.[] | select(del(select(.labels[] | select(.name=="backport-v1"))) != null) | "* [GH-\(.number)](\(.url)) \(.title)"' prs.json ``` --- CHANGELOG.md | 28 +++++++++++++++++++++++++--- provider_client.go | 2 +- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c96be9111c..20321357a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ -## v2 (unreleased) +## v2.0.0-beta.1 BREAKING CHANGES: +* **The minimum required Go version is now v1.21.6.** * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Gophercloud now escapes container and object names in all `objects` and `containers` functions. If you were previously escaping names (with, for example: `url.PathEscape` or `url.QueryEscape`), then you should REMOVE that and pass the intended names to Gophercloud directly. * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) The `containers.ListOpts#Full` and `objects.ListOpts#Full` properties are REMOVED from the Gophercloud API. The reason for that is: plaintext listing is unfixably wrong and won't handle special characters reliably (i.e. `\n`). * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Empty container names, container names containing a slash (`/`), and empty object names are now rejected in Gophercloud before any call to Swift. @@ -10,11 +11,32 @@ BREAKING CHANGES: * `v1.ErrEmptyContainerName` * `v1.ErrEmptyObjectName` * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) In `objects.Copy`: the `destination` field (e.g. `objects.CopyOpts#Destination`) must be in the form `/container/object`: the function will reject a destination path if it doesn't start with a slash (`/`). +* [GH-2560](https://github.com/gophercloud/gophercloud/pull/2560) loadbalancer: Use CreateMemberOpts instead of BatchUpdateMemberOpts in PoolCreateOpts +* [GH-2886](https://github.com/gophercloud/gophercloud/pull/2886) ports: Fix value_specs implementation New features and improvements: -* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Bugfix: it is now possible to successfuly call `objects.CreateTempURL` with a container name or object name containing the string `/v1/`. - +* [GH-2486](https://github.com/gophercloud/gophercloud/pull/2486) Fix BareMetalV1 version +* [GH-2492](https://github.com/gophercloud/gophercloud/pull/2492) Add tags for loadbalancer l7policy and l7rule +* [GH-2560](https://github.com/gophercloud/gophercloud/pull/2560) loadbalancer: Use CreateMemberOpts instead of BatchUpdateMemberOpts in PoolCreateOpts +* [GH-2561](https://github.com/gophercloud/gophercloud/pull/2561) compute: add ext_specs to flavor +* [GH-2613](https://github.com/gophercloud/gophercloud/pull/2613) Migrate baremetal inventory to a common location +* [GH-2665](https://github.com/gophercloud/gophercloud/pull/2665) Cinder: Remove multiatttach request parameter +* [GH-2724](https://github.com/gophercloud/gophercloud/pull/2724) baremetal: introduce Node Inventory API +* [GH-2725](https://github.com/gophercloud/gophercloud/pull/2725) baremetal: finish moving common inventory bits +* [GH-2736](https://github.com/gophercloud/gophercloud/pull/2736) Composable templates +* [GH-2781](https://github.com/gophercloud/gophercloud/pull/2781) baremetal: support ironic native PluginData +* [GH-2791](https://github.com/gophercloud/gophercloud/pull/2791) Add microversion utilities +* [GH-2806](https://github.com/gophercloud/gophercloud/pull/2806) Fix list ports with multiple fixedip parameters +* [GH-2809](https://github.com/gophercloud/gophercloud/pull/2809) Remove code for CDN (poppy) +* [GH-2812](https://github.com/gophercloud/gophercloud/pull/2812) Revert "Fix baremetal jobs on Ubuntu 20.04" +* [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) objects: Escape names in Gophercloud +* [GH-2828](https://github.com/gophercloud/gophercloud/pull/2828) Octavia: Add tags to resources missing them +* [GH-2834](https://github.com/gophercloud/gophercloud/pull/2834) baremetal: implemented ParsedLLDP in the standard PluginData +* [GH-2866](https://github.com/gophercloud/gophercloud/pull/2866) loadbalancer additional_vips by snigle +* [GH-2881](https://github.com/gophercloud/gophercloud/pull/2881) Adding missing QoS field for router +* [GH-2883](https://github.com/gophercloud/gophercloud/pull/2883) Context-aware methods to ProviderClient and ServiceClient +* [GH-2892](https://github.com/gophercloud/gophercloud/pull/2892) Authenticate with a clouds.yaml ## v1.9.0 (2024-02-02) diff --git a/provider_client.go b/provider_client.go index 2ee827c11b..7640431991 100644 --- a/provider_client.go +++ b/provider_client.go @@ -15,7 +15,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v2.0.0-unreleased" + DefaultUserAgent = "gophercloud/v2.0.0-beta.1" DefaultMaxBackoffRetries = 60 ) From 3d2271aa57ceba8c783f41c24d6efadd601e8462 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 09:44:10 +0000 Subject: [PATCH 1745/2296] build(deps): bump golang.org/x/crypto from 0.18.0 to 0.19.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.18.0 to 0.19.0. - [Commits](https://github.com/golang/crypto/compare/v0.18.0...v0.19.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index b61904cace..1868dd7671 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud go 1.21.6 require ( - golang.org/x/crypto v0.18.0 + golang.org/x/crypto v0.19.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.16.0 // indirect +require golang.org/x/sys v0.17.0 // indirect diff --git a/go.sum b/go.sum index 678f5e89cb..6d66f158ca 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 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 2cac7f436944f78f42be2131e145d4780aab3da3 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 9 Feb 2024 10:41:24 +0100 Subject: [PATCH 1746/2296] Set the user agent to `v2-unreleased` The change to `v2.0.0-beta.1` should have happened in the v2 branch. --- provider_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider_client.go b/provider_client.go index 7640431991..6a97f89b51 100644 --- a/provider_client.go +++ b/provider_client.go @@ -15,7 +15,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v2.0.0-beta.1" + DefaultUserAgent = "gophercloud/v2-unreleased" DefaultMaxBackoffRetries = 60 ) From 3fc4b11f7e5e268b2830199ab684bcff7cb01d97 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 9 Feb 2024 11:17:58 +0100 Subject: [PATCH 1747/2296] CHANGELOG: Plus one to the breaking changes Co-Authored-By: Lennart Jern --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20321357a2..a0e59fddb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ BREAKING CHANGES: * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) In `objects.Copy`: the `destination` field (e.g. `objects.CopyOpts#Destination`) must be in the form `/container/object`: the function will reject a destination path if it doesn't start with a slash (`/`). * [GH-2560](https://github.com/gophercloud/gophercloud/pull/2560) loadbalancer: Use CreateMemberOpts instead of BatchUpdateMemberOpts in PoolCreateOpts * [GH-2886](https://github.com/gophercloud/gophercloud/pull/2886) ports: Fix value_specs implementation +* [GH-2665](https://github.com/gophercloud/gophercloud/pull/2665) Cinder: Remove multiatttach request parameter New features and improvements: @@ -21,7 +22,6 @@ New features and improvements: * [GH-2560](https://github.com/gophercloud/gophercloud/pull/2560) loadbalancer: Use CreateMemberOpts instead of BatchUpdateMemberOpts in PoolCreateOpts * [GH-2561](https://github.com/gophercloud/gophercloud/pull/2561) compute: add ext_specs to flavor * [GH-2613](https://github.com/gophercloud/gophercloud/pull/2613) Migrate baremetal inventory to a common location -* [GH-2665](https://github.com/gophercloud/gophercloud/pull/2665) Cinder: Remove multiatttach request parameter * [GH-2724](https://github.com/gophercloud/gophercloud/pull/2724) baremetal: introduce Node Inventory API * [GH-2725](https://github.com/gophercloud/gophercloud/pull/2725) baremetal: finish moving common inventory bits * [GH-2736](https://github.com/gophercloud/gophercloud/pull/2736) Composable templates From 6747962ca315fb5071083362b84e00d34f8d0c6a Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 12 Feb 2024 12:54:38 +0100 Subject: [PATCH 1748/2296] 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 9b47f5101c..8753d091f2 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -49,7 +49,7 @@ jobs: shell: bash if: matrix.ubuntu_version == '20.04' - 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 d5cd352a8f..11048088fb 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -44,7 +44,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 eb93e88b7b..1ee43e742c 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 ef830cbc4b..3ed60f16cb 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 c6781ab165..de667ed055 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 16f9a7b1d5..17be7db52a 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -65,7 +65,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 67859fb870..4e789e088c 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -42,7 +42,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 27b07b2cbc..f2b18fc97d 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 6acece36db..d0966abecb 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 4b7dd255ae..252e4b571c 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-imageservice.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 a52ce79865..abcb66392a 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 0e515d49a9..5a16b1fb4a 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 574971a5e7..84d155440b 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 fca72f43e7..db8fda07f3 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 91c6b616d4..d4f3c8320b 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 03be9d9b63..3424aa1374 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 fe83089044..3cc2d74597 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - 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 afebf7b3a8..c7a107864b 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@v0.11 + uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: branch: ${{ matrix.openstack_version }} conf_overrides: | From ef5fd4a5ed429549c26789a79555c1c719c11446 Mon Sep 17 00:00:00 2001 From: Vladimir Ermakov Date: Tue, 13 Feb 2024 11:59:09 +0100 Subject: [PATCH 1749/2296] 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 --- 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 06e02ed9ce..c8f25561d2 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 5d62d9c949a4a2692708fa70eba16a41feb1856c Mon Sep 17 00:00:00 2001 From: Vladimir Ermakov Date: Tue, 13 Feb 2024 12:11:32 +0100 Subject: [PATCH 1750/2296] clouds: export ParseOption to allow conditionally compose With* opts Fix #2913 Signed-off-by: Vladimir Ermakov --- 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 c8f25561d2..c0ab5f6098 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 97198c9371..863aaba459 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 1111ded486c4cfc6b029ac26df85ef5c76884e30 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 13 Feb 2024 13:54:48 +0100 Subject: [PATCH 1751/2296] chore: Add a workflow to backport to v2 --- .github/labels.yaml | 3 +++ .github/workflows/backport_v2.yaml | 40 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 .github/workflows/backport_v2.yaml diff --git a/.github/labels.yaml b/.github/labels.yaml index 8a908009e2..8debbd689f 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -1,6 +1,9 @@ - color: '30ABB9' description: This PR will be backported to v1 name: backport-v1 +- color: '30ABB9' + description: This PR will be backported to v2 + name: backport-v2 - color: '0366d6' description: Pull requests that update a dependency file name: dependencies diff --git a/.github/workflows/backport_v2.yaml b/.github/workflows/backport_v2.yaml new file mode 100644 index 0000000000..43b079c990 --- /dev/null +++ b/.github/workflows/backport_v2.yaml @@ -0,0 +1,40 @@ +name: Pull Request backporting + +on: + pull_request_target: + types: + - closed + - labeled + +jobs: + backporting: + name: "Backporting" + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + && contains(github.event.pull_request.labels.*.name, 'backport-v2') + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport-v2') + ) + ) + runs-on: ubuntu-latest + steps: + - name: Generate a token from the gophercloud-backport-bot github-app + id: generate_token + uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc + with: + app_id: ${{ secrets.BACKPORT_APP_ID }} + private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} + + - name: Backporting + uses: kiegroup/git-backporting@204ebd4376d7501696d52aec8fea2a2f6e09328f + with: + target-branch: v2 + pull-request: ${{ github.event.pull_request.url }} + auth: ${{ steps.generate_token.outputs.token }} + no-squash: true + strategy-option: find-renames From b350e18e699ecba1f24860cdad730a5f05db3ed0 Mon Sep 17 00:00:00 2001 From: Youngjun Date: Fri, 16 Feb 2024 00:06:00 +0900 Subject: [PATCH 1752/2296] simplify conditional expressions - opts.Detail, a bool type, itself represents a true or false value, so there is no need to compare it again with the comparison operator (==). Signed-off-by: Youngjun --- openstack/baremetal/v1/conductors/requests.go | 2 +- openstack/baremetal/v1/nodes/requests.go | 2 +- openstack/compute/v2/extensions/usage/requests.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/baremetal/v1/conductors/requests.go b/openstack/baremetal/v1/conductors/requests.go index 997585a8c2..96b1441e76 100644 --- a/openstack/baremetal/v1/conductors/requests.go +++ b/openstack/baremetal/v1/conductors/requests.go @@ -39,7 +39,7 @@ type ListOpts struct { // ToConductorListQuery formats a ListOpts into a query string. func (opts ListOpts) ToConductorListQuery() (string, error) { - if opts.Detail == true && len(opts.Fields) > 0 { + if opts.Detail && len(opts.Fields) > 0 { return "", fmt.Errorf("cannot have both fields and detail options for conductors") } diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 158eb25668..443e36e872 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -662,7 +662,7 @@ type ListBIOSSettingsOpts struct { // ToListBIOSSettingsOptsQuery formats a ListBIOSSettingsOpts into a query string func (opts ListBIOSSettingsOpts) ToListBIOSSettingsOptsQuery() (string, error) { - if opts.Detail == true && len(opts.Fields) > 0 { + if opts.Detail && len(opts.Fields) > 0 { return "", fmt.Errorf("cannot have both fields and detail options for BIOS settings") } diff --git a/openstack/compute/v2/extensions/usage/requests.go b/openstack/compute/v2/extensions/usage/requests.go index ad6c533e16..3ed23b6d56 100644 --- a/openstack/compute/v2/extensions/usage/requests.go +++ b/openstack/compute/v2/extensions/usage/requests.go @@ -110,7 +110,7 @@ func (opts AllTenantsOpts) ToUsageAllTenantsQuery() (string, error) { params.Add("end", opts.End.Format(gophercloud.RFC3339MilliNoZ)) } - if opts.Detailed == true { + if opts.Detailed { params.Add("detailed", "1") } From 5e4b623e85f0d5d4a74014dff83fd581f57d0d18 Mon Sep 17 00:00:00 2001 From: Youngjun Date: Fri, 16 Feb 2024 00:20:19 +0900 Subject: [PATCH 1753/2296] replace deprecated library - replace math/rand -> crypto/rand Signed-off-by: Youngjun --- openstack/identity/v3/extensions/ec2tokens/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go index 86bcde1bd1..befb4c4fab 100644 --- a/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -3,11 +3,11 @@ package ec2tokens import ( "context" "crypto/hmac" + "crypto/rand" "crypto/sha1" "crypto/sha256" "encoding/hex" "fmt" - "math/rand" "net/url" "sort" "strings" From 09ac067aa9e738cc2d7a99dae06247a4fd7a8db1 Mon Sep 17 00:00:00 2001 From: Youngjun Date: Fri, 16 Feb 2024 11:17:17 +0900 Subject: [PATCH 1754/2296] chage coding style Signed-off-by: Youngjun --- openstack/config/clouds/clouds.go | 6 ++---- params.go | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index c0ab5f6098..f0a897975b 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -251,10 +251,8 @@ func mergeInterfaces(overridingInterface, inferiorInterface interface{}) interfa if !ok { return overriding } - for i := range list { - overriding = append(overriding, list[i]) - } - return overriding + + return append(overriding, list...) case nil: // mergeClouds(nil, map[string]interface{...}) -> map[string]interface{...} v, ok := inferiorInterface.(map[string]interface{}) diff --git a/params.go b/params.go index 17b200cd23..9e7bb5bde2 100644 --- a/params.go +++ b/params.go @@ -282,10 +282,7 @@ func isZero(v reflect.Value) bool { return z case reflect.Struct: if v.Type() == reflect.TypeOf(t) { - if v.Interface().(time.Time).IsZero() { - return true - } - return false + return v.Interface().(time.Time).IsZero() } z := true for i := 0; i < v.NumField(); i++ { From 66a938ba4f621b7bb2093a83aaa7ca47687ac0fb Mon Sep 17 00:00:00 2001 From: Youngjun Date: Fri, 16 Feb 2024 20:16:02 +0900 Subject: [PATCH 1755/2296] apply go fmt for lint Signed-off-by: Youngjun --- 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 f0a897975b..84e533a960 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -251,7 +251,7 @@ func mergeInterfaces(overridingInterface, inferiorInterface interface{}) interfa if !ok { return overriding } - + return append(overriding, list...) case nil: // mergeClouds(nil, map[string]interface{...}) -> map[string]interface{...} From c1733f7e17a356b497e5bdd576f3bea16bd37d80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 09:54:38 +0000 Subject: [PATCH 1756/2296] build(deps): bump thollander/actions-comment-pull-request Bumps [thollander/actions-comment-pull-request](https://github.com/thollander/actions-comment-pull-request) from 2.4.3 to 2.5.0. - [Release notes](https://github.com/thollander/actions-comment-pull-request/releases) - [Commits](https://github.com/thollander/actions-comment-pull-request/compare/1d3973dc4b8e1399c0620d3f2b1aa5e795465308...fabd468d3a1a0b97feee5f6b9e499eab0dd903f6) --- updated-dependencies: - dependency-name: thollander/actions-comment-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 2a7f97ca6f..17b8401495 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -50,7 +50,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:unknown') || contains(github.event.label.name, 'semver:major') || contains(github.event.label.name, 'semver:unknown') - uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 with: message: | Labels `semver-major` or `semver-unknown` can not trigger backports. From ecb891eb9824bc08b765f0e5c412e2fd5f8ba9d0 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 20 Feb 2024 13:49:02 +0100 Subject: [PATCH 1757/2296] Fix AllowReauth reauthentication Due to an error in implementing the addition of `context.Context`, the reauth function catched the context passed when generating the ProviderClient, which could be long canceled when the reauthentication takes place. This is a breaking change: this patch changes the signature of the reauthentication function in the ProviderClient to accept a context. --- internal/acceptance/openstack/client_test.go | 3 ++- openstack/client.go | 4 ++-- provider_client.go | 8 +++++--- testing/provider_client_test.go | 8 ++++---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index d28ce7041b..74961f380a 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -4,6 +4,7 @@ package openstack import ( + "context" "os" "testing" "time" @@ -140,7 +141,7 @@ func TestReauth(t *testing.T) { time.Sleep(1 * time.Second) t.Logf("Attempting to reauthenticate") - err = provider.ReauthFunc() + err = provider.ReauthFunc(context.TODO()) if err != nil { t.Fatalf("Unable to reauthenticate: %v", err) } diff --git a/openstack/client.go b/openstack/client.go index 2aceb4e929..ad5026089d 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -170,7 +170,7 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tac.SetTokenAndAuthResult(nil) tao := options tao.AllowReauth = false - client.ReauthFunc = func() error { + client.ReauthFunc = func(ctx context.Context) error { err := v2auth(ctx, &tac, endpoint, tao, eo) if err != nil { return err @@ -293,7 +293,7 @@ func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st default: tao = opts } - client.ReauthFunc = func() error { + client.ReauthFunc = func(ctx context.Context) error { err := v3auth(ctx, &tac, endpoint, tao, eo) if err != nil { return err diff --git a/provider_client.go b/provider_client.go index 6a97f89b51..c4cc1a3a35 100644 --- a/provider_client.go +++ b/provider_client.go @@ -83,7 +83,7 @@ type ProviderClient struct { // ReauthFunc is the function used to re-authenticate the user if the request // fails with a 401 HTTP response code. This a needed because there may be multiple // authentication functions for different Identity service versions. - ReauthFunc func() error + ReauthFunc func(context.Context) error // Throwaway determines whether if this client is a throw-away client. It's a copy of user's provider client // with the token and reauth func zeroed. Such client can be used to perform reauthorization. @@ -273,12 +273,14 @@ func (client *ProviderClient) SetThrowaway(v bool) { // reauthenticated in the meantime. If no previous token is known, an empty // string should be passed instead to force unconditional reauthentication. func (client *ProviderClient) Reauthenticate(previousToken string) error { + ctx := context.TODO() + if client.ReauthFunc == nil { return nil } if client.reauthmut == nil { - return client.ReauthFunc() + return client.ReauthFunc(ctx) } future := newReauthFuture() @@ -299,7 +301,7 @@ func (client *ProviderClient) Reauthenticate(previousToken string) error { // Perform the actual reauthentication. var err error if previousToken == "" || client.TokenID == previousToken { - err = client.ReauthFunc() + err = client.ReauthFunc(ctx) } else { err = nil } diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index fd15af6058..b502dc36b6 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -66,7 +66,7 @@ func TestConcurrentReauth(t *testing.T) { p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(prereauthTok) - p.ReauthFunc = func() error { + p.ReauthFunc = func(_ context.Context) error { p.SetThrowaway(true) time.Sleep(1 * time.Second) p.AuthenticatedHeaders() @@ -154,7 +154,7 @@ func TestReauthEndLoop(t *testing.T) { p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(client.TokenID) - p.ReauthFunc = func() error { + p.ReauthFunc = func(_ context.Context) error { info.mut.Lock() defer info.mut.Unlock() @@ -237,7 +237,7 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(prereauthTok) - p.ReauthFunc = func() error { + p.ReauthFunc = func(_ context.Context) error { info.mut.RLock() if info.numreauths == 0 { info.mut.RUnlock() @@ -331,7 +331,7 @@ func TestRequestReauthsAtMostOnce(t *testing.T) { p := new(gophercloud.ProviderClient) p.UseTokenLock() p.SetToken(client.TokenID) - p.ReauthFunc = func() error { + p.ReauthFunc = func(_ context.Context) error { reauthCounterMutex.Lock() reauthCounter++ reauthCounterMutex.Unlock() From eaadf19ab8a04e0957457eaa8dead6ddafe504cf Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 21 Feb 2024 12:09:03 +0100 Subject: [PATCH 1758/2296] provider client: Reauthenticate with context Change `(ProviderClient).Reauthenticate` to accept a `context.Context` as its first argument, and use it to perform the reauth call. --- provider_client.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/provider_client.go b/provider_client.go index c4cc1a3a35..f3bd65b309 100644 --- a/provider_client.go +++ b/provider_client.go @@ -272,9 +272,7 @@ func (client *ProviderClient) SetThrowaway(v bool) { // this case, the reauthentication can be skipped if another thread has already // reauthenticated in the meantime. If no previous token is known, an empty // string should be passed instead to force unconditional reauthentication. -func (client *ProviderClient) Reauthenticate(previousToken string) error { - ctx := context.TODO() - +func (client *ProviderClient) Reauthenticate(ctx context.Context, previousToken string) error { if client.ReauthFunc == nil { return nil } @@ -490,7 +488,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, } case http.StatusUnauthorized: if client.ReauthFunc != nil && !state.hasReauthenticated { - err = client.Reauthenticate(prereqtok) + err = client.Reauthenticate(ctx, prereqtok) if err != nil { e := &ErrUnableToReauthenticate{} e.ErrOriginal = respErr From 1b7c1ceb72b30b5dada9993e8113a67fa95ab419 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 21 Feb 2024 16:33:13 +0100 Subject: [PATCH 1759/2296] Make Gophercloud context-aware With this change, all functions and methods that trigger an HTTP call now accept an instance of `context.Context` as their first argument. This breaking change enables cancellation and tracing to be used across all packages of the library. Co-Authored-By: Stefan Majewsky Co-Authored-By: Pierre Prinetti --- internal/acceptance/clients/clients.go | 49 +++---- .../baremetal/httpbasic/allocations_test.go | 3 +- .../baremetal/httpbasic/nodes_test.go | 9 +- .../baremetal/httpbasic/ports_test.go | 5 +- .../baremetal/noauth/allocations_test.go | 3 +- .../openstack/baremetal/noauth/nodes_test.go | 7 +- .../openstack/baremetal/noauth/ports_test.go | 5 +- .../baremetal/v1/allocations_test.go | 3 +- .../openstack/baremetal/v1/baremetal.go | 15 ++- .../openstack/baremetal/v1/conductors_test.go | 5 +- .../openstack/baremetal/v1/nodes_test.go | 19 +-- .../openstack/baremetal/v1/ports_test.go | 5 +- .../blockstorage/apiversions_test.go | 5 +- .../blockstorage/extensions/backups_test.go | 5 +- .../blockstorage/extensions/extensions.go | 57 +++++---- .../blockstorage/extensions/limits_test.go | 3 +- .../extensions/schedulerhints_test.go | 13 +- .../extensions/schedulerstats_test.go | 3 +- .../blockstorage/extensions/services_test.go | 3 +- .../extensions/volumeactions_test.go | 15 ++- .../extensions/volumetenants_test.go | 5 +- .../blockstorage/noauth/blockstorage.go | 19 +-- .../blockstorage/noauth/snapshots_test.go | 5 +- .../blockstorage/noauth/volumes_test.go | 5 +- .../openstack/blockstorage/v1/blockstorage.go | 19 +-- .../blockstorage/v1/snapshots_test.go | 5 +- .../openstack/blockstorage/v1/volumes_test.go | 7 +- .../blockstorage/v1/volumetypes_test.go | 3 +- .../openstack/blockstorage/v2/blockstorage.go | 21 +-- .../blockstorage/v2/snapshots_test.go | 5 +- .../openstack/blockstorage/v2/volumes_test.go | 19 +-- .../openstack/blockstorage/v3/blockstorage.go | 41 +++--- .../openstack/blockstorage/v3/qos_test.go | 21 +-- .../blockstorage/v3/quotaset_test.go | 29 +++-- .../blockstorage/v3/snapshots_test.go | 25 ++-- .../blockstorage/v3/volumeattachments.go | 17 +-- .../blockstorage/v3/volumeattachments_test.go | 3 +- .../openstack/blockstorage/v3/volumes_test.go | 17 +-- .../blockstorage/v3/volumetypes_test.go | 33 ++--- internal/acceptance/openstack/client_test.go | 16 +-- .../openstack/clustering/v1/actions_test.go | 3 +- .../openstack/clustering/v1/clustering.go | 29 +++-- .../openstack/clustering/v1/clusters_test.go | 69 +++++----- .../openstack/clustering/v1/events_test.go | 3 +- .../openstack/clustering/v1/nodes_test.go | 19 +-- .../openstack/clustering/v1/policies_test.go | 9 +- .../clustering/v1/policytypes_test.go | 9 +- .../openstack/clustering/v1/profiles_test.go | 7 +- .../clustering/v1/profiletypes_test.go | 5 +- .../openstack/clustering/v1/receivers_test.go | 7 +- .../clustering/v1/webhooktrigger_test.go | 5 +- .../openstack/compute/v2/aggregates_test.go | 15 ++- .../compute/v2/attachinterfaces_test.go | 3 +- .../compute/v2/availabilityzones_test.go | 5 +- .../compute/v2/bootfromvolume_test.go | 9 +- .../openstack/compute/v2/compute.go | 113 ++++++++-------- .../openstack/compute/v2/defsecrules_test.go | 5 +- .../openstack/compute/v2/diagnostics_test.go | 3 +- .../openstack/compute/v2/extension_test.go | 5 +- .../openstack/compute/v2/flavors_test.go | 29 +++-- .../openstack/compute/v2/floatingip_test.go | 11 +- .../openstack/compute/v2/hypervisors_test.go | 13 +- .../openstack/compute/v2/images_test.go | 5 +- .../compute/v2/instance_actions_test.go | 13 +- .../openstack/compute/v2/keypairs_test.go | 13 +- .../openstack/compute/v2/limits_test.go | 5 +- .../openstack/compute/v2/migrate_test.go | 5 +- .../openstack/compute/v2/network_test.go | 5 +- .../openstack/compute/v2/quotaset_test.go | 17 +-- .../openstack/compute/v2/secgroup_test.go | 15 ++- .../openstack/compute/v2/servergroup_test.go | 13 +- .../openstack/compute/v2/servers_test.go | 93 +++++++------- .../openstack/compute/v2/services_test.go | 15 ++- .../compute/v2/tenantnetworks_test.go | 5 +- .../openstack/compute/v2/usage_test.go | 5 +- .../openstack/container/v1/capsules.go | 3 +- .../openstack/container/v1/capsules_test.go | 5 +- .../containerinfra/v1/certificates_test.go | 7 +- .../containerinfra/v1/clusters_test.go | 9 +- .../v1/clustertemplates_test.go | 7 +- .../containerinfra/v1/containerinfra.go | 15 ++- .../containerinfra/v1/nodegroups_test.go | 21 +-- .../openstack/db/v1/databases_test.go | 3 +- internal/acceptance/openstack/db/v1/db.go | 17 +-- .../openstack/db/v1/flavors_test.go | 5 +- .../openstack/db/v1/instances_test.go | 3 +- .../acceptance/openstack/db/v1/users_test.go | 3 +- internal/acceptance/openstack/dns/v2/dns.go | 35 ++--- .../openstack/dns/v2/recordsets_test.go | 3 +- .../openstack/dns/v2/transfers_test.go | 5 +- .../acceptance/openstack/dns/v2/zones_test.go | 3 +- .../openstack/identity/v2/extension_test.go | 5 +- .../openstack/identity/v2/identity.go | 19 +-- .../openstack/identity/v2/role_test.go | 5 +- .../openstack/identity/v2/tenant_test.go | 7 +- .../openstack/identity/v2/token_test.go | 7 +- .../openstack/identity/v2/user_test.go | 3 +- .../v3/applicationcredentials_test.go | 39 +++--- .../openstack/identity/v3/catalog_test.go | 3 +- .../openstack/identity/v3/credentials_test.go | 27 ++-- .../openstack/identity/v3/domains_test.go | 9 +- .../identity/v3/ec2credentials_test.go | 11 +- .../openstack/identity/v3/endpoint_test.go | 7 +- .../openstack/identity/v3/federation_test.go | 13 +- .../openstack/identity/v3/groups_test.go | 11 +- .../openstack/identity/v3/identity.go | 39 +++--- .../openstack/identity/v3/limits_test.go | 23 ++-- .../openstack/identity/v3/oauth1_test.go | 33 ++--- .../openstack/identity/v3/osinherit_test.go | 25 ++-- .../openstack/identity/v3/policies_test.go | 17 +-- .../identity/v3/projectendpoint_test.go | 9 +- .../openstack/identity/v3/projects_test.go | 33 ++--- .../openstack/identity/v3/reauth_test.go | 5 +- .../openstack/identity/v3/regions_test.go | 9 +- .../identity/v3/registeredlimits_test.go | 15 ++- .../openstack/identity/v3/roles_test.go | 49 +++---- .../openstack/identity/v3/service_test.go | 5 +- .../openstack/identity/v3/token_test.go | 11 +- .../openstack/identity/v3/trusts_test.go | 17 +-- .../openstack/identity/v3/users_test.go | 33 ++--- .../openstack/imageservice/v2/images_test.go | 15 ++- .../openstack/imageservice/v2/imageservice.go | 17 +-- .../openstack/imageservice/v2/tasks_test.go | 5 +- .../openstack/keymanager/v1/acls_test.go | 25 ++-- .../keymanager/v1/containers_test.go | 21 +-- .../openstack/keymanager/v1/keymanager.go | 63 ++++----- .../openstack/keymanager/v1/orders_test.go | 9 +- .../openstack/keymanager/v1/secrets_test.go | 35 ++--- .../loadbalancer/v2/amphorae_test.go | 3 +- .../loadbalancer/v2/flavorprofiles_test.go | 5 +- .../openstack/loadbalancer/v2/flavors_test.go | 5 +- .../loadbalancer/v2/l7policies_test.go | 3 +- .../loadbalancer/v2/listeners_test.go | 3 +- .../openstack/loadbalancer/v2/loadbalancer.go | 47 +++---- .../loadbalancer/v2/loadbalancers_test.go | 83 ++++++------ .../loadbalancer/v2/monitors_test.go | 3 +- .../openstack/loadbalancer/v2/pools_test.go | 3 +- .../loadbalancer/v2/providers_test.go | 3 +- .../openstack/loadbalancer/v2/quotas_test.go | 9 +- .../openstack/messaging/v2/claims_test.go | 3 +- .../openstack/messaging/v2/message_test.go | 21 +-- .../openstack/messaging/v2/messaging.go | 19 +-- .../openstack/messaging/v2/queue_test.go | 11 +- .../networking/v2/apiversion_test.go | 5 +- .../openstack/networking/v2/extension_test.go | 5 +- .../v2/extensions/agents/agents_test.go | 41 +++--- .../v2/extensions/attributestags_test.go | 21 +-- .../v2/extensions/bgp/peers/bgppeers_test.go | 11 +- .../v2/extensions/bgp/peers/peers.go | 3 +- .../bgp/speakers/bgpspeakers_test.go | 25 ++-- .../v2/extensions/bgp/speakers/speakers.go | 3 +- .../networking/v2/extensions/dns/dns.go | 7 +- .../networking/v2/extensions/dns/dns_test.go | 25 ++-- .../networking/v2/extensions/extensions.go | 13 +- .../v2/extensions/fwaas/firewall_test.go | 15 ++- .../networking/v2/extensions/fwaas/fwaas.go | 17 +-- .../v2/extensions/fwaas/policy_test.go | 7 +- .../v2/extensions/fwaas/rule_test.go | 7 +- .../v2/extensions/fwaas_v2/fwaas_v2.go | 17 +-- .../v2/extensions/fwaas_v2/groups_test.go | 3 +- .../v2/extensions/fwaas_v2/policy_test.go | 7 +- .../v2/extensions/fwaas_v2/rule_test.go | 7 +- .../extensions/layer3/addressscopes_test.go | 7 +- .../v2/extensions/layer3/extraroutes_test.go | 13 +- .../v2/extensions/layer3/floatingips_test.go | 23 ++-- .../extensions/layer3/l3_scheduling_test.go | 13 +- .../networking/v2/extensions/layer3/layer3.go | 35 ++--- .../extensions/layer3/portforwardings_test.go | 11 +- .../v2/extensions/layer3/routers_test.go | 23 ++-- .../networking/v2/extensions/lbaas/lbaas.go | 17 +-- .../v2/extensions/lbaas/members_test.go | 7 +- .../v2/extensions/lbaas/monitors_test.go | 7 +- .../v2/extensions/lbaas/pools_test.go | 11 +- .../v2/extensions/lbaas/vips_test.go | 7 +- .../v2/extensions/lbaas_v2/l7policies_test.go | 3 +- .../v2/extensions/lbaas_v2/lbaas_v2.go | 31 ++--- .../v2/extensions/lbaas_v2/listeners_test.go | 3 +- .../extensions/lbaas_v2/loadbalancers_test.go | 47 +++---- .../v2/extensions/lbaas_v2/monitors_test.go | 3 +- .../v2/extensions/lbaas_v2/pools_test.go | 3 +- .../networking/v2/extensions/mtu/mtu.go | 3 +- .../networking/v2/extensions/mtu/mtu_test.go | 15 ++- .../networkipavailabilities_test.go | 3 +- .../extensions/portsbinding/portsbinding.go | 3 +- .../portsbinding/portsbinding_test.go | 5 +- .../networking/v2/extensions/provider_test.go | 3 +- .../v2/extensions/qos/policies/policies.go | 5 +- .../extensions/qos/policies/policies_test.go | 9 +- .../v2/extensions/qos/rules/rules.go | 7 +- .../v2/extensions/qos/rules/rules_test.go | 31 ++--- .../qos/ruletypes/ruletypes_test.go | 7 +- .../v2/extensions/quotas/quotas_test.go | 9 +- .../extensions/rbacpolicies/rbacpolicies.go | 5 +- .../rbacpolicies/rbacpolicies_test.go | 7 +- .../networking/v2/extensions/security_test.go | 5 +- .../v2/extensions/subnetpools/subnetpools.go | 5 +- .../subnetpools/subnetpools_test.go | 7 +- .../extensions/trunk_details/trunks_test.go | 7 +- .../networking/v2/extensions/trunks/trunks.go | 5 +- .../v2/extensions/trunks/trunks_test.go | 35 ++--- .../vlantransparent/vlantransparent.go | 7 +- .../vlantransparent/vlantransparent_test.go | 3 +- .../v2/extensions/vpnaas/group_test.go | 7 +- .../v2/extensions/vpnaas/ikepolicy_test.go | 7 +- .../v2/extensions/vpnaas/ipsecpolicy_test.go | 7 +- .../v2/extensions/vpnaas/service_test.go | 5 +- .../extensions/vpnaas/siteconnection_test.go | 9 +- .../networking/v2/extensions/vpnaas/vpnaas.go | 25 ++-- .../openstack/networking/v2/networking.go | 49 +++---- .../openstack/networking/v2/networks_test.go | 23 ++-- .../openstack/networking/v2/ports_test.go | 37 +++--- .../openstack/networking/v2/subnets_test.go | 29 +++-- .../objectstorage/v1/accounts_test.go | 7 +- .../objectstorage/v1/containers_test.go | 33 ++--- .../objectstorage/v1/objects_test.go | 57 +++++---- .../objectstorage/v1/versioning_test.go | 27 ++-- .../orchestration/v1/buildinfo_test.go | 3 +- .../orchestration/v1/orchestration.go | 13 +- .../orchestration/v1/stackevents_test.go | 5 +- .../orchestration/v1/stackresources_test.go | 11 +- .../openstack/orchestration/v1/stacks_test.go | 5 +- .../orchestration/v1/stacktemplates_test.go | 7 +- .../openstack/placement/v1/placement.go | 7 +- .../placement/v1/resourceproviders_test.go | 15 ++- .../v2/availabilityzones_test.go | 3 +- .../sharedfilesystems/v2/messages/messages.go | 3 +- .../v2/messages/messages_test.go | 9 +- .../sharedfilesystems/v2/replicas.go | 11 +- .../sharedfilesystems/v2/replicas_test.go | 25 ++-- .../v2/schedulerstats_test.go | 3 +- .../sharedfilesystems/v2/securityservices.go | 5 +- .../v2/securityservices_test.go | 11 +- .../sharedfilesystems/v2/services_test.go | 3 +- .../sharedfilesystems/v2/shareaccessrules.go | 5 +- .../sharedfilesystems/v2/sharenetworks.go | 5 +- .../v2/sharenetworks_test.go | 19 +-- .../openstack/sharedfilesystems/v2/shares.go | 21 +-- .../sharedfilesystems/v2/shares_test.go | 33 ++--- .../sharedfilesystems/v2/sharetransfers.go | 7 +- .../v2/sharetransfers_test.go | 5 +- .../sharedfilesystems/v2/sharetypes.go | 5 +- .../sharedfilesystems/v2/sharetypes_test.go | 21 +-- .../sharedfilesystems/v2/snapshots.go | 9 +- .../sharedfilesystems/v2/snapshots_test.go | 13 +- .../openstack/workflow/v2/crontrigger.go | 9 +- .../openstack/workflow/v2/execution.go | 9 +- .../openstack/workflow/v2/workflow.go | 9 +- openstack/baremetal/apiversions/requests.go | 10 +- .../apiversions/testing/requests_test.go | 5 +- .../baremetal/v1/allocations/requests.go | 14 +- .../v1/allocations/testing/requests_test.go | 9 +- openstack/baremetal/v1/conductors/requests.go | 5 +- .../v1/conductors/testing/requests_test.go | 7 +- openstack/baremetal/v1/drivers/requests.go | 14 +- .../v1/drivers/testing/requests_test.go | 9 +- openstack/baremetal/v1/nodes/requests.go | 93 +++++++------- .../v1/nodes/testing/requests_test.go | 75 +++++------ openstack/baremetal/v1/ports/requests.go | 17 +-- .../v1/ports/testing/requests_test.go | 13 +- .../v1/introspection/requests.go | 22 ++-- .../v1/introspection/testing/requests_test.go | 13 +- .../apiversions/testing/requests_test.go | 9 +- .../testing/requests_test.go | 3 +- .../extensions/backups/requests.go | 38 +++--- .../backups/testing/requests_test.go | 21 +-- .../extensions/limits/requests.go | 6 +- .../limits/testing/requests_test.go | 3 +- .../extensions/quotasets/requests.go | 21 +-- .../quotasets/testing/requests_test.go | 13 +- .../schedulerstats/testing/requests_test.go | 3 +- .../services/testing/requests_test.go | 3 +- .../extensions/volumeactions/requests.go | 62 ++++----- .../volumeactions/testing/requests_test.go | 31 ++--- .../extensions/volumetransfers/requests.go | 18 +-- .../volumetransfers/testing/requests_test.go | 13 +- .../blockstorage/v1/apiversions/requests.go | 6 +- .../v1/apiversions/testing/requests_test.go | 5 +- .../blockstorage/v1/snapshots/requests.go | 18 +-- .../v1/snapshots/testing/requests_test.go | 11 +- openstack/blockstorage/v1/snapshots/util.go | 6 +- openstack/blockstorage/v1/volumes/requests.go | 18 +-- .../v1/volumes/testing/requests_test.go | 13 +- openstack/blockstorage/v1/volumes/util.go | 6 +- .../blockstorage/v1/volumetypes/requests.go | 14 +- .../v1/volumetypes/testing/requests_test.go | 9 +- .../blockstorage/v2/snapshots/requests.go | 18 +-- .../v2/snapshots/testing/requests_test.go | 11 +- openstack/blockstorage/v2/snapshots/util.go | 6 +- openstack/blockstorage/v2/volumes/requests.go | 18 +-- .../v2/volumes/testing/requests_test.go | 19 +-- openstack/blockstorage/v2/volumes/util.go | 6 +- .../blockstorage/v3/attachments/requests.go | 22 ++-- .../v3/attachments/testing/requests_test.go | 15 ++- openstack/blockstorage/v3/attachments/util.go | 6 +- openstack/blockstorage/v3/qos/requests.go | 34 ++--- .../v3/qos/testing/requests_test.go | 21 +-- .../blockstorage/v3/snapshots/requests.go | 34 ++--- .../v3/snapshots/testing/requests_test.go | 19 +-- openstack/blockstorage/v3/snapshots/util.go | 6 +- openstack/blockstorage/v3/volumes/requests.go | 18 +-- .../v3/volumes/testing/requests_test.go | 21 +-- openstack/blockstorage/v3/volumes/util.go | 6 +- .../blockstorage/v3/volumetypes/requests.go | 66 +++++----- .../v3/volumetypes/testing/requests_test.go | 37 +++--- openstack/client.go | 52 +++----- openstack/clustering/v1/actions/requests.go | 6 +- .../v1/actions/testing/requests_test.go | 5 +- openstack/clustering/v1/clusters/requests.go | 77 +++++------ .../v1/clusters/testing/requests_test.go | 51 ++++---- openstack/clustering/v1/events/requests.go | 6 +- .../v1/events/testing/requests_test.go | 5 +- openstack/clustering/v1/nodes/requests.go | 30 +++-- .../v1/nodes/testing/requests_test.go | 17 +-- openstack/clustering/v1/policies/requests.go | 22 ++-- .../v1/policies/testing/requests_test.go | 17 +-- .../clustering/v1/policytypes/requests.go | 6 +- .../v1/policytypes/testing/requests_test.go | 5 +- openstack/clustering/v1/profiles/requests.go | 22 ++-- .../v1/profiles/testing/requests_test.go | 13 +- .../clustering/v1/profiletypes/requests.go | 6 +- .../v1/profiletypes/testing/requests_test.go | 9 +- openstack/clustering/v1/receivers/requests.go | 22 ++-- .../v1/receivers/testing/requests_test.go | 13 +- openstack/clustering/v1/webhooks/requests.go | 5 +- .../v1/webhooks/testing/requests_test.go | 9 +- openstack/common/extensions/requests.go | 6 +- .../extensions/testing/requests_test.go | 5 +- openstack/compute/apiversions/requests.go | 6 +- .../apiversions/testing/requests_test.go | 7 +- .../v2/extensions/aggregates/requests.go | 29 +++-- .../aggregates/testing/requests_test.go | 17 +-- .../extensions/attachinterfaces/requests.go | 14 +- .../attachinterfaces/testing/requests_test.go | 11 +- .../testing/requests_test.go | 5 +- .../v2/extensions/bootfromvolume/requests.go | 6 +- .../v2/extensions/defsecrules/requests.go | 13 +- .../defsecrules/testing/requests_test.go | 11 +- openstack/compute/v2/extensions/delegate.go | 6 +- .../v2/extensions/diagnostics/requests.go | 6 +- .../diagnostics/testing/requests_test.go | 3 +- .../v2/extensions/evacuate/requests.go | 6 +- .../evacuate/testing/requests_test.go | 9 +- .../testing/requests_test.go | 3 +- .../v2/extensions/floatingips/requests.go | 22 ++-- .../floatingips/testing/requests_test.go | 17 +-- .../v2/extensions/hypervisors/requests.go | 14 +- .../hypervisors/testing/requests_test.go | 19 +-- .../extensions/injectnetworkinfo/requests.go | 6 +- .../testing/requests_test.go | 3 +- .../v2/extensions/instanceactions/request.go | 5 +- .../instanceactions/testing/request_test.go | 5 +- .../v2/extensions/keypairs/requests.go | 14 +- .../keypairs/testing/requests_test.go | 17 +-- .../compute/v2/extensions/limits/requests.go | 6 +- .../limits/testing/requests_test.go | 3 +- .../v2/extensions/lockunlock/requests.go | 10 +- .../lockunlock/testing/request_test.go | 5 +- .../compute/v2/extensions/migrate/requests.go | 10 +- .../migrate/testing/requests_test.go | 5 +- .../v2/extensions/networks/requests.go | 6 +- .../networks/testing/requests_test.go | 5 +- .../v2/extensions/pauseunpause/requests.go | 10 +- .../pauseunpause/testing/requests_test.go | 5 +- .../v2/extensions/quotasets/requests.go | 18 +-- .../quotasets/testing/requests_test.go | 13 +- .../v2/extensions/remoteconsoles/requests.go | 6 +- .../remoteconsoles/testing/requests_test.go | 3 +- .../v2/extensions/rescueunrescue/requests.go | 10 +- .../rescueunrescue/testing/requests_test.go | 5 +- .../v2/extensions/resetnetwork/requests.go | 6 +- .../resetnetwork/testing/requests_test.go | 3 +- .../v2/extensions/resetstate/requests.go | 6 +- .../resetstate/testing/requests_test.go | 3 +- .../v2/extensions/secgroups/requests.go | 34 ++--- .../secgroups/testing/requests_test.go | 27 ++-- .../v2/extensions/servergroups/requests.go | 14 +- .../servergroups/testing/requests_test.go | 13 +- .../serverusage/testing/requests_test.go | 3 +- .../v2/extensions/services/requests.go | 10 +- .../services/testing/requests_test.go | 9 +- .../v2/extensions/shelveunshelve/requests.go | 14 +- .../shelveunshelve/testing/requests_test.go | 9 +- .../v2/extensions/startstop/requests.go | 10 +- .../startstop/testing/requests_test.go | 5 +- .../v2/extensions/suspendresume/requests.go | 10 +- .../suspendresume/testing/requests_test.go | 5 +- .../compute/v2/extensions/tags/requests.go | 30 +++-- .../extensions/tags/testing/requests_test.go | 17 +-- .../v2/extensions/tenantnetworks/requests.go | 6 +- .../tenantnetworks/testing/requests_test.go | 5 +- .../v2/extensions/testing/delegate_test.go | 5 +- .../extensions/usage/testing/requests_test.go | 5 +- .../v2/extensions/volumeattach/requests.go | 14 +- .../volumeattach/testing/requests_test.go | 9 +- openstack/compute/v2/flavors/requests.go | 46 +++---- .../v2/flavors/testing/requests_test.go | 27 ++-- openstack/compute/v2/images/requests.go | 10 +- .../v2/images/testing/requests_test.go | 7 +- openstack/compute/v2/servers/requests.go | 81 ++++++------ .../v2/servers/testing/requests_test.go | 71 +++++----- .../v2/servers/testing/results_test.go | 3 +- openstack/compute/v2/servers/util.go | 10 +- openstack/config/provider_client.go | 2 +- openstack/container/v1/capsules/requests.go | 14 +- .../v1/capsules/testing/requests_test.go | 13 +- .../containerinfra/apiversions/requests.go | 6 +- .../apiversions/testing/requests_test.go | 5 +- .../v1/certificates/requests.go | 14 +- .../v1/certificates/testing/requests_test.go | 7 +- .../containerinfra/v1/clusters/requests.go | 26 ++-- .../v1/clusters/testing/requests_test.go | 17 +-- .../v1/clustertemplates/requests.go | 18 +-- .../clustertemplates/testing/requests_test.go | 17 +-- .../containerinfra/v1/nodegroups/requests.go | 18 +-- .../v1/nodegroups/testing/requests_test.go | 37 +++--- .../containerinfra/v1/quotas/requests.go | 6 +- .../v1/quotas/testing/requests_test.go | 3 +- openstack/db/v1/configurations/requests.go | 30 +++-- .../configurations/testing/requests_test.go | 23 ++-- openstack/db/v1/databases/requests.go | 10 +- .../db/v1/databases/testing/requests_test.go | 7 +- openstack/db/v1/datastores/requests.go | 10 +- .../db/v1/datastores/testing/requests_test.go | 9 +- openstack/db/v1/flavors/requests.go | 6 +- .../db/v1/flavors/testing/requests_test.go | 5 +- openstack/db/v1/instances/requests.go | 42 +++--- .../db/v1/instances/testing/requests_test.go | 25 ++-- openstack/db/v1/users/requests.go | 10 +- .../db/v1/users/testing/requests_test.go | 7 +- openstack/dns/v2/recordsets/requests.go | 18 +-- .../v2/recordsets/testing/requests_test.go | 15 ++- openstack/dns/v2/transfer/accept/requests.go | 9 +- .../transfer/accept/testing/accepts_test.go | 12 +- openstack/dns/v2/transfer/request/requests.go | 17 +-- .../transfer/request/testing/requests_test.go | 16 ++- openstack/dns/v2/zones/requests.go | 18 +-- .../dns/v2/zones/testing/requests_test.go | 13 +- .../v2/extensions/admin/roles/requests.go | 10 +- .../admin/roles/testing/requests_test.go | 7 +- openstack/identity/v2/extensions/delegate.go | 6 +- .../v2/extensions/testing/delegate_test.go | 5 +- openstack/identity/v2/tenants/requests.go | 18 +-- .../v2/tenants/testing/requests_test.go | 11 +- openstack/identity/v2/tokens/requests.go | 22 +--- .../v2/tokens/testing/requests_test.go | 7 +- openstack/identity/v2/users/requests.go | 18 +-- .../v2/users/testing/requests_test.go | 13 +- .../v3/applicationcredentials/requests.go | 21 +-- .../testing/requests_test.go | 21 +-- .../v3/catalog/testing/catalog_test.go | 3 +- openstack/identity/v3/credentials/requests.go | 18 +-- .../v3/credentials/testing/requests_test.go | 13 +- openstack/identity/v3/domains/requests.go | 18 +-- .../v3/domains/testing/requests_test.go | 15 ++- openstack/identity/v3/endpoints/requests.go | 14 +- .../v3/endpoints/testing/requests_test.go | 9 +- .../v3/extensions/ec2credentials/requests.go | 14 +- .../ec2credentials/testing/requests_test.go | 11 +- .../v3/extensions/ec2tokens/requests.go | 25 +--- .../ec2tokens/testing/requests_test.go | 3 +- .../v3/extensions/federation/requests.go | 18 +-- .../federation/testing/requests_test.go | 13 +- .../identity/v3/extensions/oauth1/requests.go | 121 +++++------------- .../oauth1/testing/requests_test.go | 35 ++--- .../extensions/projectendpoints/requests.go | 10 +- .../projectendpoints/testing/requests_test.go | 7 +- .../identity/v3/extensions/trusts/requests.go | 21 +-- .../trusts/testing/requests_test.go | 25 ++-- openstack/identity/v3/groups/requests.go | 17 +-- .../v3/groups/testing/requests_test.go | 13 +- openstack/identity/v3/limits/requests.go | 22 ++-- .../v3/limits/testing/requests_test.go | 15 ++- openstack/identity/v3/osinherit/requests.go | 18 ++- .../v3/osinherit/testing/requests_test.go | 37 +++--- openstack/identity/v3/policies/requests.go | 17 +-- .../v3/policies/testing/requests_test.go | 15 ++- openstack/identity/v3/projects/requests.go | 17 +-- .../v3/projects/testing/requests_test.go | 13 +- openstack/identity/v3/regions/requests.go | 18 +-- .../v3/regions/testing/requests_test.go | 13 +- .../identity/v3/registeredlimits/requests.go | 18 +-- .../registeredlimits/testing/requests_test.go | 13 +- openstack/identity/v3/roles/requests.go | 25 ++-- .../v3/roles/testing/requests_test.go | 43 ++++--- openstack/identity/v3/services/requests.go | 18 +-- .../v3/services/testing/requests_test.go | 13 +- openstack/identity/v3/tokens/requests.go | 44 ++----- .../v3/tokens/testing/requests_test.go | 21 +-- openstack/identity/v3/users/requests.go | 33 ++--- .../v3/users/testing/requests_test.go | 29 +++-- .../imageservice/v2/imagedata/requests.go | 13 +- .../v2/imagedata/testing/requests_test.go | 5 +- .../imageservice/v2/imageimport/requests.go | 14 +- .../v2/imageimport/testing/requests_test.go | 5 +- openstack/imageservice/v2/images/requests.go | 17 +-- .../v2/images/testing/requests_test.go | 19 +-- openstack/imageservice/v2/members/requests.go | 18 +-- .../v2/members/testing/requests_test.go | 13 +- openstack/imageservice/v2/tasks/requests.go | 10 +- .../v2/tasks/testing/requests_test.go | 7 +- openstack/keymanager/v1/acls/requests.go | 34 ++--- .../v1/acls/testing/requests_test.go | 17 +-- .../keymanager/v1/containers/requests.go | 30 +++-- .../v1/containers/testing/requests_test.go | 19 +-- openstack/keymanager/v1/orders/requests.go | 13 +- .../v1/orders/testing/requests_test.go | 11 +- openstack/keymanager/v1/secrets/requests.go | 45 +++---- .../v1/secrets/testing/requests_test.go | 27 ++-- .../loadbalancer/v2/amphorae/requests.go | 10 +- .../v2/amphorae/testing/requests_test.go | 9 +- .../v2/apiversions/testing/requests_test.go | 3 +- .../v2/flavorprofiles/requests.go | 18 +-- .../flavorprofiles/testing/requests_test.go | 15 ++- openstack/loadbalancer/v2/flavors/requests.go | 18 +-- .../v2/flavors/testing/requests_test.go | 15 ++- .../loadbalancer/v2/l7policies/requests.go | 34 ++--- .../v2/l7policies/testing/requests_test.go | 45 +++---- .../loadbalancer/v2/listeners/requests.go | 22 ++-- .../v2/listeners/testing/requests_test.go | 25 ++-- .../loadbalancer/v2/loadbalancers/requests.go | 30 +++-- .../v2/loadbalancers/testing/requests_test.go | 23 ++-- .../loadbalancer/v2/monitors/requests.go | 17 +-- .../v2/monitors/testing/requests_test.go | 21 +-- openstack/loadbalancer/v2/pools/requests.go | 38 +++--- .../v2/pools/testing/requests_test.go | 51 ++++---- .../v2/providers/testing/requests_test.go | 5 +- openstack/loadbalancer/v2/quotas/requests.go | 10 +- .../v2/quotas/testing/requests_test.go | 9 +- openstack/messaging/v2/claims/requests.go | 18 +-- .../v2/claims/testing/requests_test.go | 11 +- openstack/messaging/v2/messages/requests.go | 26 ++-- .../v2/messages/testing/requests_test.go | 15 ++- openstack/messaging/v2/queues/requests.go | 30 +++-- .../v2/queues/testing/requests_test.go | 17 +-- .../v2/apiversions/testing/requests_test.go | 9 +- .../v2/extensions/agents/requests.go | 46 +++---- .../agents/testing/requests_test.go | 31 +++-- .../v2/extensions/attributestags/requests.go | 26 ++-- .../attributestags/testing/requests_test.go | 15 ++- .../v2/extensions/bgp/peers/requests.go | 18 +-- .../bgp/peers/testing/requests_test.go | 12 +- .../v2/extensions/bgp/speakers/requests.go | 34 ++--- .../bgp/speakers/testing/requests_test.go | 23 ++-- .../networking/v2/extensions/delegate.go | 6 +- .../extensions/dns/testing/requests_test.go | 21 +-- .../external/testing/results_test.go | 9 +- .../v2/extensions/fwaas/firewalls/requests.go | 18 +-- .../fwaas/firewalls/testing/requests_test.go | 15 ++- .../v2/extensions/fwaas/policies/requests.go | 26 ++-- .../fwaas/policies/testing/requests_test.go | 11 +- .../routerinsertion/testing/requests_test.go | 9 +- .../v2/extensions/fwaas/rules/requests.go | 18 +-- .../fwaas/rules/testing/requests_test.go | 13 +- .../v2/extensions/fwaas_v2/groups/requests.go | 26 ++-- .../fwaas_v2/groups/testing/requests_test.go | 15 ++- .../extensions/fwaas_v2/policies/requests.go | 26 ++-- .../policies/testing/requests_test.go | 17 +-- .../v2/extensions/fwaas_v2/rules/requests.go | 18 +-- .../fwaas_v2/rules/testing/requests_test.go | 13 +- .../layer3/addressscopes/requests.go | 18 +-- .../addressscopes/testing/requests_test.go | 11 +- .../extensions/layer3/extraroutes/requests.go | 10 +- .../extraroutes/testing/requests_test.go | 5 +- .../extensions/layer3/floatingips/requests.go | 18 +-- .../floatingips/testing/requests_test.go | 23 ++-- .../layer3/portforwarding/requests.go | 18 +-- .../portforwarding/testing/requests_test.go | 11 +- .../v2/extensions/layer3/routers/requests.go | 26 ++-- .../layer3/routers/testing/requests_test.go | 25 ++-- .../v2/extensions/lbaas/members/requests.go | 18 +-- .../lbaas/members/testing/requests_test.go | 11 +- .../v2/extensions/lbaas/monitors/requests.go | 17 +-- .../lbaas/monitors/testing/requests_test.go | 19 +-- .../v2/extensions/lbaas/pools/requests.go | 26 ++-- .../lbaas/pools/testing/requests_test.go | 15 ++- .../v2/extensions/lbaas/vips/requests.go | 18 +-- .../lbaas/vips/testing/requests_test.go | 21 +-- .../lbaas_v2/l7policies/requests.go | 34 ++--- .../l7policies/testing/requests_test.go | 45 +++---- .../extensions/lbaas_v2/listeners/requests.go | 18 +-- .../listeners/testing/requests_test.go | 23 ++-- .../lbaas_v2/loadbalancers/requests.go | 29 +++-- .../loadbalancers/testing/requests_test.go | 29 +++-- .../extensions/lbaas_v2/monitors/requests.go | 17 +-- .../monitors/testing/requests_test.go | 21 +-- .../v2/extensions/lbaas_v2/pools/requests.go | 34 ++--- .../lbaas_v2/pools/testing/requests_test.go | 41 +++--- .../v2/extensions/mtu/testing/results_test.go | 9 +- .../networkipavailabilities/requests.go | 6 +- .../testing/requests_test.go | 5 +- .../portsbinding/testing/requests_test.go | 11 +- .../provider/testing/results_test.go | 11 +- .../v2/extensions/qos/policies/requests.go | 18 +-- .../qos/policies/testing/requests_test.go | 27 ++-- .../v2/extensions/qos/rules/requests.go | 50 ++++---- .../qos/rules/testing/requests_test.go | 31 ++--- .../v2/extensions/qos/ruletypes/requests.go | 6 +- .../qos/ruletypes/testing/requests_test.go | 5 +- .../v2/extensions/quotas/requests.go | 18 ++- .../quotas/testing/requests_test.go | 7 +- .../v2/extensions/rbacpolicies/requests.go | 18 +-- .../rbacpolicies/testing/requests_test.go | 13 +- .../v2/extensions/security/groups/requests.go | 18 +-- .../security/groups/testing/requests_test.go | 11 +- .../v2/extensions/security/rules/requests.go | 14 +- .../security/rules/testing/requests_test.go | 17 +-- .../v2/extensions/subnetpools/requests.go | 18 +-- .../subnetpools/testing/requests_test.go | 11 +- .../v2/extensions/testing/delegate_test.go | 5 +- .../trunk_details/testing/requests_test.go | 3 +- .../v2/extensions/trunks/requests.go | 30 +++-- .../trunks/testing/requests_test.go | 21 +-- .../vlantransparent/testing/requests_test.go | 9 +- .../vpnaas/endpointgroups/requests.go | 18 +-- .../endpointgroups/testing/requests_test.go | 11 +- .../extensions/vpnaas/ikepolicies/requests.go | 18 +-- .../ikepolicies/testing/requests_test.go | 11 +- .../vpnaas/ipsecpolicies/requests.go | 18 +-- .../ipsecpolicies/testing/requests_test.go | 11 +- .../v2/extensions/vpnaas/services/requests.go | 18 +-- .../vpnaas/services/testing/requests_test.go | 11 +- .../vpnaas/siteconnections/requests.go | 18 +-- .../siteconnections/testing/requests_test.go | 11 +- openstack/networking/v2/networks/requests.go | 17 +-- .../v2/networks/testing/requests_test.go | 25 ++-- openstack/networking/v2/ports/requests.go | 17 +-- .../v2/ports/testing/requests_test.go | 55 ++++---- openstack/networking/v2/subnets/requests.go | 17 +-- .../v2/subnets/testing/requests_test.go | 41 +++--- .../objectstorage/v1/accounts/requests.go | 14 +- .../v1/accounts/testing/requests_test.go | 7 +- .../objectstorage/v1/containers/requests.go | 21 +-- .../v1/containers/testing/requests_test.go | 33 ++--- .../objectstorage/v1/objects/requests.go | 35 ++--- .../v1/objects/testing/requests_test.go | 65 +++++----- openstack/objectstorage/v1/swauth/requests.go | 14 +- .../v1/swauth/testing/requests_test.go | 3 +- .../v1/apiversions/testing/requests_test.go | 5 +- .../orchestration/v1/buildinfo/requests.go | 10 +- .../v1/buildinfo/testing/requests_test.go | 3 +- .../v1/resourcetypes/requests.go | 14 +- .../v1/resourcetypes/testing/requests_test.go | 11 +- .../orchestration/v1/stackevents/requests.go | 10 +- .../v1/stackevents/testing/requests_test.go | 9 +- .../v1/stackresources/requests.go | 26 ++-- .../stackresources/testing/requests_test.go | 17 +-- openstack/orchestration/v1/stacks/requests.go | 37 +++--- .../v1/stacks/testing/requests_test.go | 25 ++-- .../v1/stacktemplates/requests.go | 14 +- .../stacktemplates/testing/requests_test.go | 5 +- .../v1/resourceproviders/requests.go | 34 ++--- .../testing/requests_test.go | 19 +-- .../sharedfilesystems/apiversions/requests.go | 6 +- .../apiversions/testing/requests_test.go | 9 +- .../testing/requests_test.go | 3 +- .../v2/errors/testing/request_test.go | 3 +- .../sharedfilesystems/v2/messages/requests.go | 10 +- .../v2/messages/testing/requests_test.go | 9 +- .../sharedfilesystems/v2/replicas/requests.go | 42 +++--- .../v2/replicas/testing/request_test.go | 25 ++-- .../schedulerstats/testing/requests_test.go | 5 +- .../v2/securityservices/requests.go | 18 +-- .../securityservices/testing/requests_test.go | 15 ++- .../v2/services/testing/requests_test.go | 3 +- .../v2/shareaccessrules/requests.go | 10 +- .../shareaccessrules/testing/requests_test.go | 3 +- .../v2/sharenetworks/requests.go | 26 ++-- .../v2/sharenetworks/testing/requests_test.go | 19 +-- .../sharedfilesystems/v2/shares/requests.go | 82 ++++++------ .../v2/shares/testing/request_test.go | 43 ++++--- .../v2/sharetransfers/requests.go | 18 +-- .../sharetransfers/testing/requests_test.go | 15 ++- .../v2/sharetypes/requests.go | 38 +++--- .../v2/sharetypes/testing/requests_test.go | 25 ++-- .../v2/snapshots/requests.go | 26 ++-- .../v2/snapshots/testing/request_test.go | 15 ++- openstack/testing/client_test.go | 9 +- openstack/utils/choose_version.go | 13 +- .../utils/testing/choose_version_test.go | 9 +- .../workflow/v2/crontriggers/requests.go | 13 +- .../v2/crontriggers/testing/requests_test.go | 9 +- openstack/workflow/v2/executions/requests.go | 13 +- .../v2/executions/testing/requests_test.go | 9 +- openstack/workflow/v2/workflows/requests.go | 13 +- .../v2/workflows/testing/requests_test.go | 9 +- pagination/http.go | 11 +- pagination/pager.go | 32 ++--- pagination/testing/linked_test.go | 5 +- pagination/testing/marker_test.go | 5 +- pagination/testing/single_test.go | 5 +- provider_client.go | 9 +- service_client.go | 75 +++-------- testing/provider_client_test.go | 32 ++--- testing/service_client_test.go | 3 +- 694 files changed, 5954 insertions(+), 5250 deletions(-) diff --git a/internal/acceptance/clients/clients.go b/internal/acceptance/clients/clients.go index ea395f2b2a..05f45fe56d 100644 --- a/internal/acceptance/clients/clients.go +++ b/internal/acceptance/clients/clients.go @@ -4,6 +4,7 @@ package clients import ( + "context" "fmt" "net/http" "os" @@ -143,7 +144,7 @@ func NewBlockStorageV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -164,7 +165,7 @@ func NewBlockStorageV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -185,7 +186,7 @@ func NewBlockStorageV3Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -244,7 +245,7 @@ func NewComputeV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -265,7 +266,7 @@ func NewBareMetalV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -306,7 +307,7 @@ func NewBareMetalIntrospectionV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -327,7 +328,7 @@ func NewDBV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -348,7 +349,7 @@ func NewDNSV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -369,7 +370,7 @@ func NewIdentityV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -390,7 +391,7 @@ func NewIdentityV2AdminClient() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -431,7 +432,7 @@ func NewIdentityV3Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -471,7 +472,7 @@ func NewImageServiceV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -492,7 +493,7 @@ func NewNetworkV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -513,7 +514,7 @@ func NewObjectStorageV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -534,7 +535,7 @@ func NewSharedFileSystemV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -555,7 +556,7 @@ func NewLoadBalancerV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -576,7 +577,7 @@ func NewClusteringV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -597,7 +598,7 @@ func NewMessagingV2Client(clientID string) (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -618,7 +619,7 @@ func NewContainerV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -639,7 +640,7 @@ func NewKeyManagerV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -674,7 +675,7 @@ func NewContainerInfraV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -695,7 +696,7 @@ func NewWorkflowV2Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -716,7 +717,7 @@ func NewOrchestrationV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } @@ -737,7 +738,7 @@ func NewPlacementV1Client() (*gophercloud.ServiceClient, error) { return nil, err } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { return nil, err } diff --git a/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go b/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go index ea32644e7b..2a677452c6 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go @@ -4,6 +4,7 @@ package httpbasic import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -27,7 +28,7 @@ func TestAllocationsCreateDestroy(t *testing.T) { defer v1.DeleteAllocation(t, client, allocation) found := false - err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = allocations.List(client, allocations.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allocationList, err := allocations.ExtractAllocations(page) if err != nil { return false, err diff --git a/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go index 68ff249c3a..ca204eeecf 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go @@ -1,6 +1,7 @@ package httpbasic import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -24,7 +25,7 @@ func TestNodesCreateDestroy(t *testing.T) { defer v1.DeleteNode(t, client, node) found := false - err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = nodes.List(client, nodes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err @@ -56,7 +57,7 @@ func TestNodesUpdate(t *testing.T) { th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) - updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ + updated, err := nodes.Update(context.TODO(), client, node.UUID, nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/maintenance", @@ -84,7 +85,7 @@ func TestNodesRAIDConfig(t *testing.T) { sizeGB := 100 isTrue := true - err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ + err = nodes.SetRAIDConfig(context.TODO(), client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, @@ -113,7 +114,7 @@ func TestNodesFirmwareInterface(t *testing.T) { th.AssertEquals(t, node.FirmwareInterface, "no-firmware") - nodeFirmwareCmps, err := nodes.ListFirmware(client, node.UUID).Extract() + nodeFirmwareCmps, err := nodes.ListFirmware(context.TODO(), client, node.UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, nodeFirmwareCmps, []nodes.FirmwareComponent{}) } diff --git a/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go index ae12b6d9c6..618b4b8cba 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go @@ -4,6 +4,7 @@ package httpbasic import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -29,7 +30,7 @@ func TestPortsCreateDestroy(t *testing.T) { defer v1.DeletePort(t, client, port) found := false - err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = ports.List(client, ports.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err @@ -63,7 +64,7 @@ func TestPortsUpdate(t *testing.T) { defer v1.DeleteNode(t, client, node) defer v1.DeletePort(t, client, port) - updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ + updated, err := ports.Update(context.TODO(), client, port.UUID, ports.UpdateOpts{ ports.UpdateOperation{ Op: ports.ReplaceOp, Path: "/address", diff --git a/internal/acceptance/openstack/baremetal/noauth/allocations_test.go b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go index 7ddf1fa4fd..11d792d1c4 100644 --- a/internal/acceptance/openstack/baremetal/noauth/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go @@ -4,6 +4,7 @@ package noauth import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -26,7 +27,7 @@ func TestAllocationsCreateDestroy(t *testing.T) { defer v1.DeleteAllocation(t, client, allocation) found := false - err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = allocations.List(client, allocations.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allocationList, err := allocations.ExtractAllocations(page) if err != nil { return false, err diff --git a/internal/acceptance/openstack/baremetal/noauth/nodes_test.go b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go index f64548e080..773dcdd9f6 100644 --- a/internal/acceptance/openstack/baremetal/noauth/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go @@ -1,6 +1,7 @@ package noauth import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -23,7 +24,7 @@ func TestNodesCreateDestroy(t *testing.T) { defer v1.DeleteNode(t, client, node) found := false - err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = nodes.List(client, nodes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err @@ -54,7 +55,7 @@ func TestNodesUpdate(t *testing.T) { th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) - updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ + updated, err := nodes.Update(context.TODO(), client, node.UUID, nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/maintenance", @@ -81,7 +82,7 @@ func TestNodesRAIDConfig(t *testing.T) { sizeGB := 100 isTrue := true - err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ + err = nodes.SetRAIDConfig(context.TODO(), client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, diff --git a/internal/acceptance/openstack/baremetal/noauth/ports_test.go b/internal/acceptance/openstack/baremetal/noauth/ports_test.go index ba4c6f8e57..4215002a97 100644 --- a/internal/acceptance/openstack/baremetal/noauth/ports_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/ports_test.go @@ -4,6 +4,7 @@ package noauth import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -28,7 +29,7 @@ func TestPortsCreateDestroy(t *testing.T) { defer v1.DeletePort(t, client, port) found := false - err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = ports.List(client, ports.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err @@ -61,7 +62,7 @@ func TestPortsUpdate(t *testing.T) { defer v1.DeleteNode(t, client, node) defer v1.DeletePort(t, client, port) - updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ + updated, err := ports.Update(context.TODO(), client, port.UUID, ports.UpdateOpts{ ports.UpdateOperation{ Op: ports.ReplaceOp, Path: "/address", diff --git a/internal/acceptance/openstack/baremetal/v1/allocations_test.go b/internal/acceptance/openstack/baremetal/v1/allocations_test.go index eafc5a6204..88747fdbcf 100644 --- a/internal/acceptance/openstack/baremetal/v1/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/v1/allocations_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -25,7 +26,7 @@ func TestAllocationsCreateDestroy(t *testing.T) { defer DeleteAllocation(t, client, allocation) found := false - err = allocations.List(client, allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = allocations.List(client, allocations.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allocationList, err := allocations.ExtractAllocations(page) if err != nil { return false, err diff --git a/internal/acceptance/openstack/baremetal/v1/baremetal.go b/internal/acceptance/openstack/baremetal/v1/baremetal.go index 5257d27ded..4bb1a19631 100644 --- a/internal/acceptance/openstack/baremetal/v1/baremetal.go +++ b/internal/acceptance/openstack/baremetal/v1/baremetal.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -15,7 +16,7 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, e name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bare metal node: %s", name) - node, err := nodes.Create(client, nodes.CreateOpts{ + node, err := nodes.Create(context.TODO(), client, nodes.CreateOpts{ Name: name, Driver: "ipmi", BootInterface: "ipxe", @@ -35,7 +36,7 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, e // DeleteNode deletes a bare metal node via its UUID. func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Node) { - err := nodes.Delete(client, node.UUID).ExtractErr() + err := nodes.Delete(context.TODO(), client, node.UUID).ExtractErr() if err != nil { t.Fatalf("Unable to delete node %s: %s", node.UUID, err) } @@ -48,7 +49,7 @@ func CreateAllocation(t *testing.T, client *gophercloud.ServiceClient) (*allocat name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bare metal allocation: %s", name) - allocation, err := allocations.Create(client, allocations.CreateOpts{ + allocation, err := allocations.Create(context.TODO(), client, allocations.CreateOpts{ Name: name, ResourceClass: "baremetal", }).Extract() @@ -58,7 +59,7 @@ func CreateAllocation(t *testing.T, client *gophercloud.ServiceClient) (*allocat // DeleteAllocation deletes a bare metal allocation via its UUID. func DeleteAllocation(t *testing.T, client *gophercloud.ServiceClient, allocation *allocations.Allocation) { - err := allocations.Delete(client, allocation.UUID).ExtractErr() + err := allocations.Delete(context.TODO(), client, allocation.UUID).ExtractErr() if err != nil { t.Fatalf("Unable to delete allocation %s: %s", allocation.UUID, err) } @@ -71,7 +72,7 @@ func CreateFakeNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Nod name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bare metal node: %s", name) - node, err := nodes.Create(client, nodes.CreateOpts{ + node, err := nodes.Create(context.TODO(), client, nodes.CreateOpts{ Name: name, Driver: "fake-hardware", BootInterface: "fake", @@ -94,7 +95,7 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Nod t.Logf("Attempting to create Port for Node: %s with Address: %s", node.UUID, mac) iTrue := true - port, err := ports.Create(client, ports.CreateOpts{ + port, err := ports.Create(context.TODO(), client, ports.CreateOpts{ NodeUUID: node.UUID, Address: mac, PXEEnabled: &iTrue, @@ -105,7 +106,7 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Nod // DeletePort - deletes a port via its UUID func DeletePort(t *testing.T, client *gophercloud.ServiceClient, port *ports.Port) { - err := ports.Delete(client, port.UUID).ExtractErr() + err := ports.Delete(context.TODO(), client, port.UUID).ExtractErr() if err != nil { t.Fatalf("Unable to delete port %s: %s", port.UUID, err) } diff --git a/internal/acceptance/openstack/baremetal/v1/conductors_test.go b/internal/acceptance/openstack/baremetal/v1/conductors_test.go index 0b5e8d46b3..f63b7eec46 100644 --- a/internal/acceptance/openstack/baremetal/v1/conductors_test.go +++ b/internal/acceptance/openstack/baremetal/v1/conductors_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -21,7 +22,7 @@ func TestConductorsListAndGet(t *testing.T) { th.AssertNoErr(t, err) client.Microversion = "1.49" - err = conductors.List(client, conductors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = conductors.List(client, conductors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { conductorList, err := conductors.ExtractConductors(page) if err != nil { return false, err @@ -30,7 +31,7 @@ func TestConductorsListAndGet(t *testing.T) { tools.PrintResource(t, conductorList) if len(conductorList) > 0 { - conductor, err := conductors.Get(client, conductorList[0].Hostname).Extract() + conductor, err := conductors.Get(context.TODO(), client, conductorList[0].Hostname).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, conductor) diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index 98fe95fed1..e51501eadf 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -25,7 +26,7 @@ func TestNodesCreateDestroy(t *testing.T) { defer DeleteNode(t, client, node) found := false - err = nodes.List(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = nodes.List(client, nodes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err @@ -56,7 +57,7 @@ func TestNodesUpdate(t *testing.T) { th.AssertNoErr(t, err) defer DeleteNode(t, client, node) - updated, err := nodes.Update(client, node.UUID, nodes.UpdateOpts{ + updated, err := nodes.Update(context.TODO(), client, node.UUID, nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/maintenance", @@ -79,21 +80,21 @@ func TestNodesMaintenance(t *testing.T) { th.AssertNoErr(t, err) defer DeleteNode(t, client, node) - err = nodes.SetMaintenance(client, node.UUID, nodes.MaintenanceOpts{ + err = nodes.SetMaintenance(context.TODO(), client, node.UUID, nodes.MaintenanceOpts{ Reason: "I'm tired", }).ExtractErr() th.AssertNoErr(t, err) - updated, err := nodes.Get(client, node.UUID).Extract() + updated, err := nodes.Get(context.TODO(), 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() + err = nodes.UnsetMaintenance(context.TODO(), client, node.UUID).ExtractErr() th.AssertNoErr(t, err) - updated, err = nodes.Get(client, node.UUID).Extract() + updated, err = nodes.Get(context.TODO(), client, node.UUID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.Maintenance, false) @@ -115,7 +116,7 @@ func TestNodesRAIDConfig(t *testing.T) { sizeGB := 100 isTrue := true - err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ + err = nodes.SetRAIDConfig(context.TODO(), client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, @@ -135,7 +136,7 @@ func TestNodesRAIDConfig(t *testing.T) { }).ExtractErr() th.AssertNoErr(t, err) - err = nodes.SetRAIDConfig(client, node.UUID, nodes.RAIDConfigOpts{ + err = nodes.SetRAIDConfig(context.TODO(), client, node.UUID, nodes.RAIDConfigOpts{ LogicalDisks: []nodes.LogicalDisk{ { SizeGB: &sizeGB, @@ -163,7 +164,7 @@ func TestNodesFirmwareInterface(t *testing.T) { th.AssertEquals(t, node.FirmwareInterface, "no-firmware") - nodeFirmwareCmps, err := nodes.ListFirmware(client, node.UUID).Extract() + nodeFirmwareCmps, err := nodes.ListFirmware(context.TODO(), client, node.UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, nodeFirmwareCmps, []nodes.FirmwareComponent{}) } diff --git a/internal/acceptance/openstack/baremetal/v1/ports_test.go b/internal/acceptance/openstack/baremetal/v1/ports_test.go index 068c28d764..6ee31a7ad0 100644 --- a/internal/acceptance/openstack/baremetal/v1/ports_test.go +++ b/internal/acceptance/openstack/baremetal/v1/ports_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -28,7 +29,7 @@ func TestPortsCreateDestroy(t *testing.T) { defer DeletePort(t, client, port) found := false - err = ports.List(client, ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = ports.List(client, ports.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err @@ -62,7 +63,7 @@ func TestPortsUpdate(t *testing.T) { th.AssertNoErr(t, err) defer DeletePort(t, client, port) - updated, err := ports.Update(client, port.UUID, ports.UpdateOpts{ + updated, err := ports.Update(context.TODO(), client, port.UUID, ports.UpdateOpts{ ports.UpdateOperation{ Op: ports.ReplaceOp, Path: "/address", diff --git a/internal/acceptance/openstack/blockstorage/apiversions_test.go b/internal/acceptance/openstack/blockstorage/apiversions_test.go index 3cf2ac717f..1b86ee5490 100644 --- a/internal/acceptance/openstack/blockstorage/apiversions_test.go +++ b/internal/acceptance/openstack/blockstorage/apiversions_test.go @@ -4,6 +4,7 @@ package blockstorage import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestAPIVersionsList(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := apiversions.List(client).AllPages() + allPages, err := apiversions.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve API versions: %v", err) } @@ -38,7 +39,7 @@ func TestAPIVersionsGet(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := apiversions.List(client).AllPages() + allPages, err := apiversions.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve API versions: %v", err) } diff --git a/internal/acceptance/openstack/blockstorage/extensions/backups_test.go b/internal/acceptance/openstack/blockstorage/extensions/backups_test.go index fd9f32e1db..e90a68482a 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/backups_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/backups_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -25,7 +26,7 @@ func TestBackupsCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteBackup(t, blockClient, backup.ID) - allPages, err := backups.List(blockClient, nil).AllPages() + allPages, err := backups.List(blockClient, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allBackups, err := backups.ExtractBackups(allPages) @@ -75,7 +76,7 @@ func TestBackupsForceDelete(t *testing.T) { err = WaitForBackupStatus(blockClient, backup.ID, "available") th.AssertNoErr(t, err) - err = backups.ForceDelete(blockClient, backup.ID).ExtractErr() + err = backups.ForceDelete(context.TODO(), blockClient, backup.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForBackupStatus(blockClient, backup.ID, "deleted") diff --git a/internal/acceptance/openstack/blockstorage/extensions/extensions.go b/internal/acceptance/openstack/blockstorage/extensions/extensions.go index c606004f9f..bc46f09f3f 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/internal/acceptance/openstack/blockstorage/extensions/extensions.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "fmt" "strings" "testing" @@ -33,14 +34,14 @@ func CreateUploadImage(t *testing.T, client *gophercloud.ServiceClient, volume * Force: true, } - volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() + volumeImage, err := volumeactions.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() if err != nil { return volumeImage, err } t.Logf("Uploading volume %s as volume-backed image %s", volume.ID, imageName) - if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { return volumeImage, err } @@ -59,7 +60,7 @@ func DeleteUploadedImage(t *testing.T, client *gophercloud.ServiceClient, imageI t.Logf("Removing image %s", imageID) - err := images.Delete(client, imageID).ExtractErr() + err := images.Delete(context.TODO(), client, imageID).ExtractErr() if err != nil { return err } @@ -82,11 +83,11 @@ func CreateVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) - if err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr(); err != nil { + if err := volumeactions.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr(); err != nil { return err } - if err := volumes.WaitForStatus(client, volume.ID, "in-use", 60); err != nil { + if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "in-use", 60); err != nil { return err } @@ -104,7 +105,7 @@ func CreateVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume t.Logf("Attempting to reserve volume %s", volume.ID) - if err := volumeactions.Reserve(client, volume.ID).ExtractErr(); err != nil { + if err := volumeactions.Reserve(context.TODO(), client, volume.ID).ExtractErr(); err != nil { return err } @@ -123,11 +124,11 @@ func DeleteVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume AttachmentID: volume.Attachments[0].AttachmentID, } - if err := volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr(); err != nil { + if err := volumeactions.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr(); err != nil { t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } - if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) } @@ -144,7 +145,7 @@ func DeleteVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume t.Logf("Attempting to unreserve volume %s", volume.ID) - if err := volumeactions.Unreserve(client, volume.ID).ExtractErr(); err != nil { + if err := volumeactions.Unreserve(context.TODO(), client, volume.ID).ExtractErr(); err != nil { t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) } @@ -159,12 +160,12 @@ func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *v NewSize: 2, } - err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() + err := volumeactions.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() if err != nil { return err } - if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { return err } @@ -181,7 +182,7 @@ func SetImageMetadata(t *testing.T, client *gophercloud.ServiceClient, volume *v }, } - err := volumeactions.SetImageMetadata(client, volume.ID, imageMetadataOpts).ExtractErr() + err := volumeactions.SetImageMetadata(context.TODO(), client, volume.ID, imageMetadataOpts).ExtractErr() if err != nil { return err } @@ -200,7 +201,7 @@ func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID stri Name: backupName, } - backup, err := backups.Create(client, createOpts).Extract() + backup, err := backups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -210,7 +211,7 @@ func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID stri return nil, err } - backup, err = backups.Get(client, backup.ID).Extract() + backup, err = backups.Get(context.TODO(), client, backup.ID).Extract() if err != nil { return nil, err } @@ -226,7 +227,7 @@ func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID stri // DeleteBackup will delete a backup. A fatal error will occur if the backup // could not be deleted. This works best when used as a deferred function. func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { - if err := backups.Delete(client, backupID).ExtractErr(); err != nil { + if err := backups.Delete(context.TODO(), client, backupID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { t.Logf("Backup %s is already deleted", backupID) return @@ -241,7 +242,7 @@ func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID stri // status. It will do this for the amount of seconds defined. func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := backups.Get(client, id).Extract() + current, err := backups.Get(context.TODO(), client, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "deleted" { return true, nil @@ -265,12 +266,12 @@ func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volume Bootable: true, } - err := volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() + err := volumeactions.SetBootable(context.TODO(), client, volume.ID, bootableOpts).ExtractErr() if err != nil { return err } - vol, err := v3.Get(client, volume.ID).Extract() + vol, err := v3.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return err } @@ -283,12 +284,12 @@ func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volume Bootable: false, } - err = volumeactions.SetBootable(client, volume.ID, bootableOpts).ExtractErr() + err = volumeactions.SetBootable(context.TODO(), client, volume.ID, bootableOpts).ExtractErr() if err != nil { return err } - vol, err = v3.Get(client, volume.ID).Extract() + vol, err = v3.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return err } @@ -309,12 +310,12 @@ func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *v MigrationPolicy: volumeactions.MigrationPolicyOnDemand, } - err := volumeactions.ChangeType(client, volume.ID, changeOpts).ExtractErr() + err := volumeactions.ChangeType(context.TODO(), client, volume.ID, changeOpts).ExtractErr() if err != nil { return err } - if err := volumes.WaitForStatus(client, volume.ID, "available", 60); err != nil { + if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { return err } @@ -328,12 +329,12 @@ func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume * resetOpts := volumeactions.ResetStatusOpts{ Status: status, } - err := volumeactions.ResetStatus(client, volume.ID, resetOpts).ExtractErr() + err := volumeactions.ResetStatus(context.TODO(), client, volume.ID, resetOpts).ExtractErr() if err != nil { return err } - if err := volumes.WaitForStatus(client, volume.ID, status, 60); err != nil { + if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, status, 60); err != nil { return err } @@ -347,7 +348,7 @@ func ResetBackupStatus(t *testing.T, client *gophercloud.ServiceClient, backup * resetOpts := backups.ResetStatusOpts{ Status: status, } - err := backups.ResetStatus(client, backup.ID, resetOpts).ExtractErr() + err := backups.ResetStatus(context.TODO(), client, backup.ID, resetOpts).ExtractErr() if err != nil { return err } @@ -364,17 +365,17 @@ func ReImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Vo ReImageReserved: false, } - err := volumeactions.ReImage(client, volume.ID, reimageOpts).ExtractErr() + err := volumeactions.ReImage(context.TODO(), client, volume.ID, reimageOpts).ExtractErr() if err != nil { return err } - err = volumes.WaitForStatus(client, volume.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) if err != nil { return err } - vol, err := v3.Get(client, volume.ID).Extract() + vol, err := v3.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return err } diff --git a/internal/acceptance/openstack/blockstorage/extensions/limits_test.go b/internal/acceptance/openstack/blockstorage/extensions/limits_test.go index efdea5e95e..24bcee1096 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/limits_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/limits_test.go @@ -1,6 +1,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -13,7 +14,7 @@ func TestLimits(t *testing.T) { client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - limits, err := limits.Get(client).Extract() + limits, err := limits.Get(context.TODO(), client).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, limits) diff --git a/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go index 6b08ea39dd..3b77886402 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -25,12 +26,12 @@ func TestSchedulerHints(t *testing.T) { Name: volumeName, } - volume1, err := volumes.Create(client, createOpts).Extract() + volume1, err := volumes.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) - err = volumes.WaitForStatus(client, volume1.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume1.ID, "available", 60) th.AssertNoErr(t, err) - defer volumes.Delete(client, volume1.ID, volumes.DeleteOpts{}) + defer volumes.Delete(context.TODO(), client, volume1.ID, volumes.DeleteOpts{}) volumeName = tools.RandomString("ACPTTEST", 16) base := volumes.CreateOpts{ @@ -49,12 +50,12 @@ func TestSchedulerHints(t *testing.T) { SchedulerHints: schedulerHints, } - volume2, err := volumes.Create(client, createOptsWithHints).Extract() + volume2, err := volumes.Create(context.TODO(), client, createOptsWithHints).Extract() th.AssertNoErr(t, err) - err = volumes.WaitForStatus(client, volume2.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume2.ID, "available", 60) th.AssertNoErr(t, err) - err = volumes.Delete(client, volume2.ID, volumes.DeleteOpts{}).ExtractErr() + err = volumes.Delete(context.TODO(), client, volume2.ID, volumes.DeleteOpts{}).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go index bc3124314b..5760970cac 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestSchedulerStatsList(t *testing.T) { Detail: true, } - allPages, err := schedulerstats.List(blockClient, listOpts).AllPages() + allPages, err := schedulerstats.List(blockClient, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allStats, err := schedulerstats.ExtractStoragePools(allPages) diff --git a/internal/acceptance/openstack/blockstorage/extensions/services_test.go b/internal/acceptance/openstack/blockstorage/extensions/services_test.go index c9c95aa387..92ae10a016 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/services_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/services_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestServicesList(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - allPages, err := services.List(blockClient, services.ListOpts{}).AllPages() + allPages, err := services.List(blockClient, services.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) diff --git a/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 1427704583..2768c90132 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -53,7 +54,7 @@ func TestVolumeActionsAttachCreateDestroy(t *testing.T) { err = CreateVolumeAttach(t, blockClient, volume, server) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() th.AssertNoErr(t, err) DeleteVolumeAttach(t, blockClient, newVolume) @@ -85,7 +86,7 @@ func TestVolumeActionsExtendSize(t *testing.T) { err = ExtendVolumeSize(t, blockClient, volume) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newVolume) @@ -138,7 +139,7 @@ func TestVolumeActionsChangeType(t *testing.T) { err = ChangeVolumeType(t, client, volume, volumeType2) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(client, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newVolume.VolumeType, volumeType2.Name) @@ -190,22 +191,22 @@ func TestVolumeConns(t *testing.T) { th.AssertNoErr(t, err) t.Logf("Creating volume") - cv, err := volumes.Create(client, &volumes.CreateOpts{ + cv, err := volumes.Create(context.TODO(), client, &volumes.CreateOpts{ Size: 1, Name: "blockv2-volume", }).Extract() th.AssertNoErr(t, err) defer func() { - err = volumes.WaitForStatus(client, cv.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, cv.ID, "available", 60) th.AssertNoErr(t, err) t.Logf("Deleting volume") - err = volumes.Delete(client, cv.ID, volumes.DeleteOpts{}).ExtractErr() + err = volumes.Delete(context.TODO(), client, cv.ID, volumes.DeleteOpts{}).ExtractErr() th.AssertNoErr(t, err) }() - err = volumes.WaitForStatus(client, cv.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, cv.ID, "available", 60) th.AssertNoErr(t, err) connOpts := &volumeactions.ConnectorOpts{ diff --git a/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go index 82a7e1c238..70a2681176 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -27,7 +28,7 @@ func TestVolumeTenants(t *testing.T) { listOpts := volumes.ListOpts{ Name: "I SHOULD NOT EXIST", } - allPages, err := volumes.List(client, listOpts).AllPages() + allPages, err := volumes.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) err = volumes.ExtractVolumesInto(allPages, &allVolumes) @@ -38,7 +39,7 @@ func TestVolumeTenants(t *testing.T) { th.AssertNoErr(t, err) defer blockstorage.DeleteVolume(t, client, volume1) - allPages, err = volumes.List(client, nil).AllPages() + allPages, err = volumes.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) err = volumes.ExtractVolumesInto(allPages, &allVolumes) diff --git a/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go index 4caa5d5fd3..78ee6833dc 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -4,6 +4,7 @@ package noauth import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -28,12 +29,12 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol Name: volumeName, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return volume, err } - err = volumes.WaitForStatus(client, volume.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) if err != nil { return volume, err } @@ -62,12 +63,12 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo ImageID: choices.ImageID, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return volume, err } - err = volumes.WaitForStatus(client, volume.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) if err != nil { return volume, err } @@ -78,7 +79,7 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo // DeleteVolume will delete a volume. A fatal error will occur if the volume // failed to be deleted. This works best when used as a deferred function. func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { - err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() + err := volumes.Delete(context.TODO(), client, volume.ID, volumes.DeleteOpts{}).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } @@ -103,12 +104,12 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol Description: snapshotDescription, } - snapshot, err := snapshots.Create(client, createOpts).Extract() + snapshot, err := snapshots.Create(context.TODO(), client, createOpts).Extract() if err != nil { return snapshot, err } - err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + err = snapshots.WaitForStatus(context.TODO(), client, snapshot.ID, "available", 60) if err != nil { return snapshot, err } @@ -119,7 +120,7 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { - err := snapshots.Delete(client, snapshot.ID).ExtractErr() + err := snapshots.Delete(context.TODO(), client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) } @@ -127,7 +128,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s // Volumes can't be deleted until their snapshots have been, // so block up to 120 seconds for the snapshot to delete. err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, snapshot.ID).Extract() + _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go index 3d1176cf49..ecce2d8e92 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -4,6 +4,7 @@ package noauth import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestSnapshotsList(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve snapshots: %v", err) } @@ -50,7 +51,7 @@ func TestSnapshotsCreateDelete(t *testing.T) { } defer DeleteSnapshot(t, client, snapshot) - newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + newSnapshot, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { t.Errorf("Unable to retrieve snapshot: %v", err) } diff --git a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go index 959a0b0286..e0356b0650 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -4,6 +4,7 @@ package noauth import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestVolumesList(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() + allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve volumes: %v", err) } @@ -44,7 +45,7 @@ func TestVolumesCreateDestroy(t *testing.T) { } defer DeleteVolume(t, client, volume) - newVolume, err := volumes.Get(client, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { t.Errorf("Unable to retrieve volume: %v", err) } diff --git a/internal/acceptance/openstack/blockstorage/v1/blockstorage.go b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go index 67d6172363..98f60b5d70 100644 --- a/internal/acceptance/openstack/blockstorage/v1/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -29,12 +30,12 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol VolumeID: volume.ID, } - snapshot, err := snapshots.Create(client, createOpts).Extract() + snapshot, err := snapshots.Create(context.TODO(), client, createOpts).Extract() if err != nil { return snapshot, err } - err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + err = snapshots.WaitForStatus(context.TODO(), client, snapshot.ID, "available", 60) if err != nil { return snapshot, err } @@ -59,12 +60,12 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol Description: volumeDescription, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return volume, err } - err = volumes.WaitForStatus(client, volume.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) if err != nil { return volume, err } @@ -86,7 +87,7 @@ func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumet }, } - volumeType, err := volumetypes.Create(client, createOpts).Extract() + volumeType, err := volumetypes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return volumeType, err } @@ -98,7 +99,7 @@ func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumet // snapshot failed to be deleted. This works best when used as a deferred // function. func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { - err := snapshots.Delete(client, snapshot.ID).ExtractErr() + err := snapshots.Delete(context.TODO(), client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete snapshot %s: %v", snapshot.ID, err) } @@ -106,7 +107,7 @@ func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapsho // Volumes can't be deleted until their snapshots have been, // so block until the snapshot has been deleted. err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, snapshot.ID).Extract() + _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { return true, nil } @@ -123,7 +124,7 @@ func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapsho // DeleteVolume will delete a volume. A fatal error will occur if the volume // failed to be deleted. This works best when used as a deferred function. func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { - err := volumes.Delete(client, volume.ID).ExtractErr() + err := volumes.Delete(context.TODO(), client, volume.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } @@ -135,7 +136,7 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum // volume type failed to be deleted. This works best when used as a deferred // function. func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, volumeType *volumetypes.VolumeType) { - err := volumetypes.Delete(client, volumeType.ID).ExtractErr() + err := volumetypes.Delete(context.TODO(), client, volumeType.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume type %s: %v", volumeType.ID, err) } diff --git a/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go index 2e3d8f38b8..06981d9869 100644 --- a/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestSnapshotsList(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve snapshots: %v", err) } @@ -52,7 +53,7 @@ func TestSnapshotsCreateDelete(t *testing.T) { } defer DeleteSnapshotshot(t, client, snapshot) - newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + newSnapshot, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { t.Errorf("Unable to retrieve snapshot: %v", err) } diff --git a/internal/acceptance/openstack/blockstorage/v1/volumes_test.go b/internal/acceptance/openstack/blockstorage/v1/volumes_test.go index fba2ddbf0f..ad222542e1 100644 --- a/internal/acceptance/openstack/blockstorage/v1/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/volumes_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestVolumesList(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() + allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve volumes: %v", err) } @@ -47,7 +48,7 @@ func TestVolumesCreateDestroy(t *testing.T) { } defer DeleteVolume(t, client, volume) - newVolume, err := volumes.Get(client, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { t.Errorf("Unable to retrieve volume: %v", err) } @@ -63,7 +64,7 @@ func TestVolumesCreateDestroy(t *testing.T) { Name: &updatedVolumeName, Description: &updatedVolumeDescription, } - updatedVolume, err := volumes.Update(client, volume.ID, updateOpts).Extract() + updatedVolume, err := volumes.Update(context.TODO(), client, volume.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedVolume) diff --git a/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go index 08399ac6b6..e1edcf79bc 100644 --- a/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestVolumeTypesList(t *testing.T) { t.Fatalf("Unable to create a blockstorage client: %v", err) } - allPages, err := volumetypes.List(client).AllPages() + allPages, err := volumetypes.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve volume types: %v", err) } diff --git a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go index c35c0fe605..aa5677f1bd 100644 --- a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -27,12 +28,12 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol Description: snapshotDescription, } - snapshot, err := snapshots.Create(client, createOpts).Extract() + snapshot, err := snapshots.Create(context.TODO(), client, createOpts).Extract() if err != nil { return snapshot, err } - err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + err = snapshots.WaitForStatus(context.TODO(), client, snapshot.ID, "available", 60) if err != nil { return snapshot, err } @@ -55,12 +56,12 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol Description: volumeDescription, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return volume, err } - err = volumes.WaitForStatus(client, volume.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) if err != nil { return volume, err } @@ -92,17 +93,17 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo ImageID: choices.ImageID, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return volume, err } - err = volumes.WaitForStatus(client, volume.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) if err != nil { return volume, err } - newVolume, err := volumes.Get(client, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return nil, err } @@ -120,7 +121,7 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { t.Logf("Attempting to delete volume: %s", volume.ID) - err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() + err := volumes.Delete(context.TODO(), client, volume.ID, volumes.DeleteOpts{}).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) } @@ -133,7 +134,7 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { t.Logf("Attempting to delete snapshot: %s", snapshot.ID) - err := snapshots.Delete(client, snapshot.ID).ExtractErr() + err := snapshots.Delete(context.TODO(), client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err) } @@ -141,7 +142,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s // Volumes can't be deleted until their snapshots have been, // so block until the snapshot is deleted. err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, snapshot.ID).Extract() + _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go index 975d492ea1..0ccdea2eae 100644 --- a/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -27,10 +28,10 @@ func TestSnapshots(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSnapshot(t, client, snapshot) - newSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + newSnapshot, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() th.AssertNoErr(t, err) - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allSnapshots, err := snapshots.ExtractSnapshots(allPages) diff --git a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go index 1674dcc40c..b45be9a7f3 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -25,7 +26,7 @@ func TestVolumesCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) defer DeleteVolume(t, client, volume) - newVolume, err := volumes.Get(client, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() th.AssertNoErr(t, err) // Update volume @@ -35,14 +36,14 @@ func TestVolumesCreateDestroy(t *testing.T) { Name: &updatedVolumeName, Description: &updatedVolumeDescription, } - updatedVolume, err := volumes.Update(client, volume.ID, updateOpts).Extract() + updatedVolume, err := volumes.Update(context.TODO(), client, volume.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedVolume) th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) - allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages() + allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allVolumes, err := volumes.ExtractVolumes(allPages) @@ -69,12 +70,12 @@ func TestVolumesCreateForceDestroy(t *testing.T) { volume, err := CreateVolume(t, client) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(client, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newVolume) - err = volumeactions.ForceDelete(client, newVolume.ID).ExtractErr() + err = volumeactions.ForceDelete(context.TODO(), client, newVolume.ID).ExtractErr() th.AssertNoErr(t, err) } @@ -88,7 +89,7 @@ func TestVolumesCascadeDelete(t *testing.T) { vol, err := CreateVolume(t, client) th.AssertNoErr(t, err) - err = volumes.WaitForStatus(client, vol.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, vol.ID, "available", 60) th.AssertNoErr(t, err) snapshot1, err := CreateSnapshot(t, client, vol) @@ -100,14 +101,14 @@ func TestVolumesCascadeDelete(t *testing.T) { t.Logf("Attempting to delete volume: %s", vol.ID) deleteOpts := volumes.DeleteOpts{Cascade: true} - err = volumes.Delete(client, vol.ID, deleteOpts).ExtractErr() + err = volumes.Delete(context.TODO(), client, vol.ID, deleteOpts).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", vol.ID, err) } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { err := tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, sid).Extract() + _, err := snapshots.Get(context.TODO(), client, sid).Extract() if err != nil { return true, nil } @@ -118,7 +119,7 @@ func TestVolumesCascadeDelete(t *testing.T) { } err = tools.WaitFor(func() (bool, error) { - _, err := volumes.Get(client, vol.ID).Extract() + _, err := volumes.Get(context.TODO(), client, vol.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 55d21c5644..75686e05a1 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -28,17 +29,17 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol Description: snapshotDescription, } - snapshot, err := snapshots.Create(client, createOpts).Extract() + snapshot, err := snapshots.Create(context.TODO(), client, createOpts).Extract() if err != nil { return snapshot, err } - err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60) + err = snapshots.WaitForStatus(context.TODO(), client, snapshot.ID, "available", 60) if err != nil { return snapshot, err } - snapshot, err = snapshots.Get(client, snapshot.ID).Extract() + snapshot, err = snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { return snapshot, err } @@ -65,17 +66,17 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol Description: volumeDescription, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return volume, err } - err = volumes.WaitForStatus(client, volume.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) if err != nil { return volume, err } - volume, err = volumes.Get(client, volume.ID).Extract() + volume, err = volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return volume, err } @@ -105,17 +106,17 @@ func CreateVolumeWithType(t *testing.T, client *gophercloud.ServiceClient, vt *v VolumeType: vt.Name, } - volume, err := volumes.Create(client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return volume, err } - err = volumes.WaitForStatus(client, volume.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) if err != nil { return volume, err } - volume, err = volumes.Get(client, volume.ID).Extract() + volume, err = volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return volume, err } @@ -144,7 +145,7 @@ func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumet Description: description, } - vt, err := volumetypes.Create(client, createOpts).Extract() + vt, err := volumetypes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -177,7 +178,7 @@ func CreateVolumeTypeNoExtraSpecs(t *testing.T, client *gophercloud.ServiceClien Description: description, } - vt, err := volumetypes.Create(client, createOpts).Extract() + vt, err := volumetypes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -206,7 +207,7 @@ func CreateVolumeTypeMultiAttach(t *testing.T, client *gophercloud.ServiceClient Description: description, } - vt, err := volumetypes.Create(client, createOpts).Extract() + vt, err := volumetypes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -238,7 +239,7 @@ func CreatePrivateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (* IsPublic: &isPublic, } - vt, err := volumetypes.Create(client, createOpts).Extract() + vt, err := volumetypes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -256,7 +257,7 @@ func CreatePrivateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (* // DeleteSnapshot will delete a snapshot. A fatal error will occur if the // snapshot failed to be deleted. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { - err := snapshots.Delete(client, snapshot.ID).ExtractErr() + err := snapshots.Delete(context.TODO(), client, snapshot.ID).ExtractErr() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { t.Logf("Snapshot %s is already deleted", snapshot.ID) @@ -268,7 +269,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s // Volumes can't be deleted until their snapshots have been, // so block until the snapshoth as been deleted. err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, snapshot.ID).Extract() + _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { return true, nil } @@ -287,7 +288,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { t.Logf("Attempting to delete volume: %s", volume.ID) - err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr() + err := volumes.Delete(context.TODO(), client, volume.ID, volumes.DeleteOpts{}).ExtractErr() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { t.Logf("Volume %s is already deleted", volume.ID) @@ -299,7 +300,7 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum // VolumeTypes can't be deleted until their volumes have been, // so block until the volume is deleted. err = tools.WaitFor(func() (bool, error) { - _, err := volumes.Get(client, volume.ID).Extract() + _, err := volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return true, nil } @@ -319,7 +320,7 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, vt *volumetypes.VolumeType) { t.Logf("Attempting to delete volume type: %s", vt.ID) - err := volumetypes.Delete(client, vt.ID).ExtractErr() + err := volumetypes.Delete(context.TODO(), client, vt.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume type %s: %v", vt.ID, err) } @@ -341,7 +342,7 @@ func CreateQoS(t *testing.T, client *gophercloud.ServiceClient) (*qos.QoS, error }, } - qs, err := qos.Create(client, createOpts).Extract() + qs, err := qos.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -365,7 +366,7 @@ func DeleteQoS(t *testing.T, client *gophercloud.ServiceClient, qs *qos.QoS) { Force: true, } - err := qos.Delete(client, qs.ID, deleteOpts).ExtractErr() + err := qos.Delete(context.TODO(), client, qs.ID, deleteOpts).ExtractErr() if err != nil { t.Fatalf("Unable to delete QoS %s: %v", qs.ID, err) } diff --git a/internal/acceptance/openstack/blockstorage/v3/qos_test.go b/internal/acceptance/openstack/blockstorage/v3/qos_test.go index 3fca65ae35..2fc694792d 100644 --- a/internal/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/qos_test.go @@ -1,6 +1,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -24,11 +25,11 @@ func TestQoS(t *testing.T) { th.AssertNoErr(t, err) defer DeleteQoS(t, client, qos2) - getQoS2, err := qos.Get(client, qos2.ID).Extract() + getQoS2, err := qos.Get(context.TODO(), client, qos2.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, qos2, getQoS2) - err = qos.DeleteKeys(client, qos2.ID, qos.DeleteKeysOpts{"read_iops_sec"}).ExtractErr() + err = qos.DeleteKeys(context.TODO(), client, qos2.ID, qos.DeleteKeysOpts{"read_iops_sec"}).ExtractErr() th.AssertNoErr(t, err) updateOpts := qos.UpdateOpts{ @@ -45,7 +46,7 @@ func TestQoS(t *testing.T) { "write_iops_sec": "40000", } - updatedQosSpecs, err := qos.Update(client, qos2.ID, updateOpts).Extract() + updatedQosSpecs, err := qos.Update(context.TODO(), client, qos2.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, updatedQosSpecs, expectedQosSpecs) @@ -53,7 +54,7 @@ func TestQoS(t *testing.T) { Limit: 1, } - err = qos.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + err = qos.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := qos.ExtractQoS(page) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(actual)) @@ -92,10 +93,10 @@ func TestQoSAssociations(t *testing.T) { VolumeTypeID: vt.ID, } - err = qos.Associate(client, qos1.ID, associateOpts).ExtractErr() + err = qos.Associate(context.TODO(), client, qos1.ID, associateOpts).ExtractErr() th.AssertNoErr(t, err) - allQosAssociations, err := qos.ListAssociations(client, qos1.ID).AllPages() + allQosAssociations, err := qos.ListAssociations(client, qos1.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allAssociations, err := qos.ExtractAssociations(allQosAssociations) @@ -108,10 +109,10 @@ func TestQoSAssociations(t *testing.T) { VolumeTypeID: vt.ID, } - err = qos.Disassociate(client, qos1.ID, disassociateOpts).ExtractErr() + err = qos.Disassociate(context.TODO(), client, qos1.ID, disassociateOpts).ExtractErr() th.AssertNoErr(t, err) - allQosAssociations, err = qos.ListAssociations(client, qos1.ID).AllPages() + allQosAssociations, err = qos.ListAssociations(client, qos1.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allAssociations, err = qos.ExtractAssociations(allQosAssociations) @@ -119,9 +120,9 @@ func TestQoSAssociations(t *testing.T) { tools.PrintResource(t, allAssociations) th.AssertEquals(t, 0, len(allAssociations)) - err = qos.Associate(client, qos1.ID, associateOpts).ExtractErr() + err = qos.Associate(context.TODO(), client, qos1.ID, associateOpts).ExtractErr() th.AssertNoErr(t, err) - err = qos.DisassociateAll(client, qos1.ID).ExtractErr() + err = qos.DisassociateAll(context.TODO(), client, qos1.ID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go index e5e7cbd6a4..4a546516ff 100644 --- a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "os" "testing" @@ -20,7 +21,7 @@ func TestQuotasetGet(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSet, err := quotasets.Get(client, projectID).Extract() + quotaSet, err := quotasets.Get(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -31,7 +32,7 @@ func TestQuotasetGetDefaults(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSet, err := quotasets.GetDefaults(client, projectID).Extract() + quotaSet, err := quotasets.GetDefaults(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -42,7 +43,7 @@ func TestQuotasetGetUsage(t *testing.T) { client, projectID := getClientAndProject(t) - quotaSetUsage, err := quotasets.GetUsage(client, projectID).Extract() + quotaSetUsage, err := quotasets.GetUsage(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSetUsage) @@ -85,32 +86,32 @@ func TestQuotasetUpdate(t *testing.T) { client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets.Get(client, projectID).Extract() + orig, err := quotasets.Get(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) // create volumeType to test volume type quota - volumeType, err := volumetypes.Create(client, VolumeTypeCreateOpts).Extract() + volumeType, err := volumetypes.Create(context.TODO(), client, VolumeTypeCreateOpts).Extract() th.AssertNoErr(t, err) defer func() { restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) - err := volumetypes.Delete(client, volumeType.ID).ExtractErr() + err := volumetypes.Delete(context.TODO(), client, volumeType.ID).ExtractErr() th.AssertNoErr(t, err) - _, err = quotasets.Update(client, projectID, restore).Extract() + _, err = quotasets.Update(context.TODO(), client, projectID, restore).Extract() th.AssertNoErr(t, err) }() // test Update - resultQuotas, err := quotasets.Update(client, projectID, UpdateQuotaOpts).Extract() + resultQuotas, err := quotasets.Update(context.TODO(), client, projectID, UpdateQuotaOpts).Extract() th.AssertNoErr(t, err) // We dont know the default quotas, so just check if the quotas are not the // same as before - newQuotas, err := quotasets.Get(client, projectID).Extract() + newQuotas, err := quotasets.Get(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, resultQuotas.Volumes, newQuotas.Volumes) th.AssertEquals(t, resultQuotas.Extra["volumes_foo"], newQuotas.Extra["volumes_foo"]) @@ -143,26 +144,26 @@ func TestQuotasetDelete(t *testing.T) { client, projectID := getClientAndProject(t) // save original quotas - orig, err := quotasets.Get(client, projectID).Extract() + orig, err := quotasets.Get(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) defer func() { restore := quotasets.UpdateOpts{} FillUpdateOptsFromQuotaSet(*orig, &restore) - _, err = quotasets.Update(client, projectID, restore).Extract() + _, err = quotasets.Update(context.TODO(), client, projectID, restore).Extract() th.AssertNoErr(t, err) }() // Obtain environment default quotaset values to validate deletion. - defaultQuotaSet, err := quotasets.GetDefaults(client, projectID).Extract() + defaultQuotaSet, err := quotasets.GetDefaults(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) // Test Delete - err = quotasets.Delete(client, projectID).ExtractErr() + err = quotasets.Delete(context.TODO(), client, projectID).ExtractErr() th.AssertNoErr(t, err) - newQuotas, err := quotasets.Get(client, projectID).Extract() + newQuotas, err := quotasets.Get(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newQuotas.Volumes, defaultQuotaSet.Volumes) diff --git a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go index c16014ba1a..4526f83746 100644 --- a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "fmt" "testing" @@ -37,7 +38,7 @@ func TestSnapshots(t *testing.T) { Description: &updatedSnapshotDescription, } t.Logf("Attempting to update snapshot: %s", updatedSnapshotName) - updatedSnapshot, err := snapshots.Update(client, snapshot1.ID, updateOpts).Extract() + updatedSnapshot, err := snapshots.Update(context.TODO(), client, snapshot1.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedSnapshot) @@ -56,7 +57,7 @@ func TestSnapshots(t *testing.T) { Limit: 1, } - err = snapshots.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + err = snapshots.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := snapshots.ExtractSnapshots(page) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(actual)) @@ -95,10 +96,10 @@ func TestSnapshotsResetStatus(t *testing.T) { Status: "error", } t.Logf("Attempting to reset snapshot status to %s", resetOpts.Status) - err = snapshots.ResetStatus(client, snapshot1.ID, resetOpts).ExtractErr() + err = snapshots.ResetStatus(context.TODO(), client, snapshot1.ID, resetOpts).ExtractErr() th.AssertNoErr(t, err) - snapshot, err := snapshots.Get(client, snapshot1.ID).Extract() + snapshot, err := snapshots.Get(context.TODO(), client, snapshot1.ID).Extract() th.AssertNoErr(t, err) if snapshot.Status != resetOpts.Status { @@ -110,10 +111,10 @@ func TestSnapshotsResetStatus(t *testing.T) { Status: "available", } t.Logf("Attempting to reset snapshot status to %s", resetOpts.Status) - err = snapshots.ResetStatus(client, snapshot1.ID, resetOpts).ExtractErr() + err = snapshots.ResetStatus(context.TODO(), client, snapshot1.ID, resetOpts).ExtractErr() th.AssertNoErr(t, err) - snapshot, err = snapshots.Get(client, snapshot1.ID).Extract() + snapshot, err = snapshots.Get(context.TODO(), client, snapshot1.ID).Extract() th.AssertNoErr(t, err) if snapshot.Status != resetOpts.Status { @@ -140,10 +141,10 @@ func TestSnapshotsUpdateStatus(t *testing.T) { Status: "creating", } t.Logf("Attempting to update snapshot status to %s", resetOpts.Status) - err = snapshots.ResetStatus(client, snapshot1.ID, resetOpts).ExtractErr() + err = snapshots.ResetStatus(context.TODO(), client, snapshot1.ID, resetOpts).ExtractErr() th.AssertNoErr(t, err) - snapshot, err := snapshots.Get(client, snapshot1.ID).Extract() + snapshot, err := snapshots.Get(context.TODO(), client, snapshot1.ID).Extract() th.AssertNoErr(t, err) if snapshot.Status != resetOpts.Status { @@ -155,10 +156,10 @@ func TestSnapshotsUpdateStatus(t *testing.T) { Status: "available", } t.Logf("Attempting to update snapshot status to %s", updateOpts.Status) - err = snapshots.UpdateStatus(client, snapshot1.ID, updateOpts).ExtractErr() + err = snapshots.UpdateStatus(context.TODO(), client, snapshot1.ID, updateOpts).ExtractErr() th.AssertNoErr(t, err) - snapshot, err = snapshots.Get(client, snapshot1.ID).Extract() + snapshot, err = snapshots.Get(context.TODO(), client, snapshot1.ID).Extract() th.AssertNoErr(t, err) if snapshot.Status != updateOpts.Status { @@ -182,11 +183,11 @@ func TestSnapshotsForceDelete(t *testing.T) { // Force delete snapshot t.Logf("Attempting to force delete %s snapshot", snapshot.ID) - err = snapshots.ForceDelete(client, snapshot.ID).ExtractErr() + err = snapshots.ForceDelete(context.TODO(), client, snapshot.ID).ExtractErr() th.AssertNoErr(t, err) err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, snapshot.ID).Extract() + _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil diff --git a/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go index 134962654b..536830efd4 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go @@ -1,6 +1,7 @@ package v3 import ( + "context" "fmt" "testing" @@ -26,7 +27,7 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, vol var err error var attachment *attachments.Attachment - if attachment, err = attachments.Create(client, attachOpts).Extract(); err != nil { + if attachment, err = attachments.Create(context.TODO(), client, attachOpts).Extract(); err != nil { return err } @@ -35,19 +36,19 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, vol defer func() { client.Microversion = mv }() - if err = attachments.Complete(client, attachment.ID).ExtractErr(); err != nil { + if err = attachments.Complete(context.TODO(), client, attachment.ID).ExtractErr(); err != nil { return err } - if err = attachments.WaitForStatus(client, attachment.ID, "attached", 60); err != nil { - e := attachments.Delete(client, attachment.ID).ExtractErr() + if err = attachments.WaitForStatus(context.TODO(), client, attachment.ID, "attached", 60); err != nil { + e := attachments.Delete(context.TODO(), client, attachment.ID).ExtractErr() if e != nil { t.Logf("Failed to delete %q attachment: %s", attachment.ID, err) } return err } - attachment, err = attachments.Get(client, attachment.ID).Extract() + attachment, err = attachments.Get(context.TODO(), client, attachment.ID).Extract() if err != nil { return err } @@ -56,7 +57,7 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, vol VolumeID: volume.ID, InstanceID: server.ID, } - allPages, err := attachments.List(client, listOpts).AllPages() + allPages, err := attachments.List(client, listOpts).AllPages(context.TODO()) if err != nil { return err } @@ -80,11 +81,11 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, vol func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume) { t.Logf("Attepting to detach volume volume: %s", volume.ID) - if err := attachments.Delete(client, volume.Attachments[0].AttachmentID).ExtractErr(); err != nil { + if err := attachments.Delete(context.TODO(), client, volume.Attachments[0].AttachmentID).ExtractErr(); err != nil { t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } - if err := v3.WaitForStatus(client, volume.ID, "available", 60); err != nil { + if err := v3.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) } diff --git a/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index fd45869e77..2cf187d44d 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -33,7 +34,7 @@ func TestVolumeAttachments(t *testing.T) { err = CreateVolumeAttachment(t, blockClient, volume, server) th.AssertNoErr(t, err) - newVolume, err := volumes.Get(blockClient, volume.ID).Extract() + newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() th.AssertNoErr(t, err) DeleteVolumeAttachment(t, blockClient, newVolume) diff --git a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go index 9605c3a2a0..b8609fd540 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -35,7 +36,7 @@ func TestVolumes(t *testing.T) { Name: &updatedVolumeName, Description: &updatedVolumeDescription, } - updatedVolume, err := volumes.Update(client, volume1.ID, updateOpts).Extract() + updatedVolume, err := volumes.Update(context.TODO(), client, volume1.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedVolume) @@ -46,7 +47,7 @@ func TestVolumes(t *testing.T) { Limit: 1, } - err = volumes.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + err = volumes.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := volumes.ExtractVolumes(page) th.AssertNoErr(t, err) th.AssertEquals(t, 1, len(actual)) @@ -86,11 +87,11 @@ func TestVolumesMultiAttach(t *testing.T) { VolumeType: vt.ID, } - vol, err := volumes.Create(client, volOpts).Extract() + vol, err := volumes.Create(context.TODO(), client, volOpts).Extract() th.AssertNoErr(t, err) defer DeleteVolume(t, client, vol) - err = volumes.WaitForStatus(client, vol.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, vol.ID, "available", 60) th.AssertNoErr(t, err) th.AssertEquals(t, vol.Multiattach, true) @@ -105,7 +106,7 @@ func TestVolumesCascadeDelete(t *testing.T) { vol, err := CreateVolume(t, client) th.AssertNoErr(t, err) - err = volumes.WaitForStatus(client, vol.ID, "available", 60) + err = volumes.WaitForStatus(context.TODO(), client, vol.ID, "available", 60) th.AssertNoErr(t, err) snapshot1, err := CreateSnapshot(t, client, vol) @@ -117,14 +118,14 @@ func TestVolumesCascadeDelete(t *testing.T) { t.Logf("Attempting to delete volume: %s", vol.ID) deleteOpts := volumes.DeleteOpts{Cascade: true} - err = volumes.Delete(client, vol.ID, deleteOpts).ExtractErr() + err = volumes.Delete(context.TODO(), client, vol.ID, deleteOpts).ExtractErr() if err != nil { t.Fatalf("Unable to delete volume %s: %v", vol.ID, err) } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { err := tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(client, sid).Extract() + _, err := snapshots.Get(context.TODO(), client, sid).Extract() if err != nil { return true, nil } @@ -135,7 +136,7 @@ func TestVolumesCascadeDelete(t *testing.T) { } err = tools.WaitFor(func() (bool, error) { - _, err := volumes.Get(client, vol.ID).Extract() + _, err := volumes.Get(context.TODO(), client, vol.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 96a6b909a8..da8dc95463 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -23,7 +24,7 @@ func TestVolumeTypes(t *testing.T) { th.AssertNoErr(t, err) defer DeleteVolumeType(t, client, vt) - allPages, err := volumetypes.List(client, nil).AllPages() + allPages, err := volumetypes.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) @@ -48,7 +49,7 @@ func TestVolumeTypes(t *testing.T) { IsPublic: &isPublic, } - newVT, err := volumetypes.Update(client, vt.ID, updateOpts).Extract() + newVT, err := volumetypes.Update(context.TODO(), client, vt.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newVT) @@ -72,7 +73,7 @@ func TestVolumeTypesExtraSpecs(t *testing.T) { "volume_backend_name": "ssd", } - createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, vt.ID, createOpts).Extract() + createdExtraSpecs, err := volumetypes.CreateExtraSpecs(context.TODO(), client, vt.ID, createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, createdExtraSpecs) @@ -81,20 +82,20 @@ func TestVolumeTypesExtraSpecs(t *testing.T) { th.AssertEquals(t, createdExtraSpecs["capabilities"], "gpu") th.AssertEquals(t, createdExtraSpecs["volume_backend_name"], "ssd") - err = volumetypes.DeleteExtraSpec(client, vt.ID, "volume_backend_name").ExtractErr() + err = volumetypes.DeleteExtraSpec(context.TODO(), client, vt.ID, "volume_backend_name").ExtractErr() th.AssertNoErr(t, err) updateOpts := volumetypes.ExtraSpecsOpts{ "capabilities": "gpu-2", } - updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, vt.ID, updateOpts).Extract() + updatedExtraSpec, err := volumetypes.UpdateExtraSpec(context.TODO(), client, vt.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedExtraSpec) th.AssertEquals(t, updatedExtraSpec["capabilities"], "gpu-2") - allExtraSpecs, err := volumetypes.ListExtraSpecs(client, vt.ID).Extract() + allExtraSpecs, err := volumetypes.ListExtraSpecs(context.TODO(), client, vt.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, allExtraSpecs) @@ -102,7 +103,7 @@ func TestVolumeTypesExtraSpecs(t *testing.T) { th.AssertEquals(t, len(allExtraSpecs), 1) th.AssertEquals(t, allExtraSpecs["capabilities"], "gpu-2") - singleSpec, err := volumetypes.GetExtraSpec(client, vt.ID, "capabilities").Extract() + singleSpec, err := volumetypes.GetExtraSpec(context.TODO(), client, vt.ID, "capabilities").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, singleSpec) @@ -131,10 +132,10 @@ func TestVolumeTypesAccess(t *testing.T) { Project: project.ID, } - err = volumetypes.AddAccess(client, vt.ID, addAccessOpts).ExtractErr() + err = volumetypes.AddAccess(context.TODO(), client, vt.ID, addAccessOpts).ExtractErr() th.AssertNoErr(t, err) - allPages, err := volumetypes.ListAccesses(client, vt.ID).AllPages() + allPages, err := volumetypes.ListAccesses(client, vt.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) accessList, err := volumetypes.ExtractAccesses(allPages) @@ -150,10 +151,10 @@ func TestVolumeTypesAccess(t *testing.T) { Project: project.ID, } - err = volumetypes.RemoveAccess(client, vt.ID, removeAccessOpts).ExtractErr() + err = volumetypes.RemoveAccess(context.TODO(), client, vt.ID, removeAccessOpts).ExtractErr() th.AssertNoErr(t, err) - allPages, err = volumetypes.ListAccesses(client, vt.ID).AllPages() + allPages, err = volumetypes.ListAccesses(client, vt.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) accessList, err = volumetypes.ExtractAccesses(allPages) @@ -181,16 +182,16 @@ func TestEncryptionVolumeTypes(t *testing.T) { Cipher: "aes-xts-plain64", } - eVT, err := volumetypes.CreateEncryption(client, vt.ID, createEncryptionOpts).Extract() + eVT, err := volumetypes.CreateEncryption(context.TODO(), client, vt.ID, createEncryptionOpts).Extract() th.AssertNoErr(t, err) - defer volumetypes.DeleteEncryption(client, eVT.VolumeTypeID, eVT.EncryptionID) + defer volumetypes.DeleteEncryption(context.TODO(), client, eVT.VolumeTypeID, eVT.EncryptionID) - geVT, err := volumetypes.GetEncryption(client, vt.ID).Extract() + geVT, err := volumetypes.GetEncryption(context.TODO(), client, vt.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, geVT) key := "cipher" - gesVT, err := volumetypes.GetEncryptionSpec(client, vt.ID, key).Extract() + gesVT, err := volumetypes.GetEncryptionSpec(context.TODO(), client, vt.ID, key).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, gesVT) @@ -198,7 +199,7 @@ func TestEncryptionVolumeTypes(t *testing.T) { ControlLocation: "back-end", } - newEVT, err := volumetypes.UpdateEncryption(client, vt.ID, eVT.EncryptionID, updateEncryptionOpts).Extract() + newEVT, err := volumetypes.UpdateEncryption(context.TODO(), client, vt.ID, eVT.EncryptionID, updateEncryptionOpts).Extract() tools.PrintResource(t, newEVT) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index 74961f380a..f44df3cd42 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -26,7 +26,7 @@ func TestAuthenticatedClient(t *testing.T) { t.Fatalf("Unable to acquire credentials: %v", err) } - client, err := openstack.AuthenticatedClient(ao) + client, err := openstack.AuthenticatedClient(context.TODO(), ao) if err != nil { t.Fatalf("Unable to authenticate: %v", err) } @@ -68,15 +68,15 @@ func TestEC2AuthMethod(t *testing.T) { DomainName: ao.DomainName, }, } - token, err := tokens.Create(client, &authOptions).Extract() + token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - project, err := tokens.Get(client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) @@ -88,11 +88,11 @@ func TestEC2AuthMethod(t *testing.T) { } // Create a credential - credential, err := credentials.Create(client, createOpts).Extract() + credential, err := credentials.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) // Delete a credential - defer credentials.Delete(client, credential.ID) + defer credentials.Delete(context.TODO(), client, credential.ID) tools.PrintResource(t, credential) newClient, err := clients.NewIdentityV3UnauthenticatedClient() @@ -104,7 +104,7 @@ func TestEC2AuthMethod(t *testing.T) { Secret: "secretKey", } - err = openstack.AuthenticateV3(newClient.ProviderClient, ec2AuthOptions, gophercloud.EndpointOpts{}) + err = openstack.AuthenticateV3(context.TODO(), newClient.ProviderClient, ec2AuthOptions, gophercloud.EndpointOpts{}) th.AssertNoErr(t, err) tools.PrintResource(t, newClient.TokenID) @@ -124,7 +124,7 @@ func TestReauth(t *testing.T) { t.Fatalf("Unable to create provider: %v", err) } - err = openstack.Authenticate(provider, ao) + err = openstack.Authenticate(context.TODO(), provider, ao) if err != nil { t.Fatalf("Unable to authenticate: %v", err) } diff --git a/internal/acceptance/openstack/clustering/v1/actions_test.go b/internal/acceptance/openstack/clustering/v1/actions_test.go index c33ec1bd60..9a4aa10708 100644 --- a/internal/acceptance/openstack/clustering/v1/actions_test.go +++ b/internal/acceptance/openstack/clustering/v1/actions_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -20,7 +21,7 @@ func TestActionsList(t *testing.T) { Limit: 200, } - allPages, err := actions.List(client, opts).AllPages() + allPages, err := actions.List(client, opts).AllPages(context.TODO()) th.AssertNoErr(t, err) allActions, err := actions.ExtractActions(allPages) diff --git a/internal/acceptance/openstack/clustering/v1/clustering.go b/internal/acceptance/openstack/clustering/v1/clustering.go index 326ce8f80d..5a296ec08b 100644 --- a/internal/acceptance/openstack/clustering/v1/clustering.go +++ b/internal/acceptance/openstack/clustering/v1/clustering.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "fmt" "net/http" "strings" @@ -55,7 +56,7 @@ func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, profileID st Config: map[string]interface{}{}, } - res := clusters.Create(client, createOpts) + res := clusters.Create(context.TODO(), client, createOpts) if res.Err != nil { return nil, res.Err } @@ -112,7 +113,7 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, prof Role: "", } - res := nodes.Create(client, createOpts) + res := nodes.Create(context.TODO(), client, createOpts) if res.Err != nil { return nil, res.Err } @@ -143,7 +144,7 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, prof t.Logf("Successfully created node: %s", node.ID) - node, err = nodes.Get(client, node.ID).Extract() + node, err = nodes.Get(context.TODO(), client, node.ID).Extract() if err != nil { return nil, err } @@ -169,7 +170,7 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies.Po Spec: TestPolicySpec, } - res := policies.Create(client, createOpts) + res := policies.Create(context.TODO(), client, createOpts) if res.Err != nil { return nil, res.Err } @@ -226,7 +227,7 @@ func CreateProfile(t *testing.T, client *gophercloud.ServiceClient) (*profiles.P }, } - res := profiles.Create(client, createOpts) + res := profiles.Create(context.TODO(), client, createOpts) if res.Err != nil { return nil, res.Err } @@ -266,7 +267,7 @@ func CreateWebhookReceiver(t *testing.T, client *gophercloud.ServiceClient, clus Action: "CLUSTER_SCALE_OUT", } - res := receivers.Create(client, createOpts) + res := receivers.Create(context.TODO(), client, createOpts) if res.Err != nil { return nil, res.Err } @@ -299,7 +300,7 @@ func CreateMessageReceiver(t *testing.T, client *gophercloud.ServiceClient, clus Type: receivers.MessageReceiver, } - res := receivers.Create(client, createOpts) + res := receivers.Create(context.TODO(), client, createOpts) if res.Err != nil { return nil, res.Err } @@ -325,7 +326,7 @@ func CreateMessageReceiver(t *testing.T, client *gophercloud.ServiceClient, clus func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete cluster: %s", id) - res := clusters.Delete(client, id) + res := clusters.Delete(context.TODO(), client, id) if res.Err != nil { t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) } @@ -350,7 +351,7 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete node: %s", id) - res := nodes.Delete(client, id) + res := nodes.Delete(context.TODO(), client, id) if res.Err != nil { t.Fatalf("Error deleting node %s: %s:", id, res.Err) } @@ -376,7 +377,7 @@ func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, id string) { func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete policy: %s", id) - err := policies.Delete(client, id).ExtractErr() + err := policies.Delete(context.TODO(), client, id).ExtractErr() if err != nil { t.Fatalf("Error deleting policy %s: %s:", id, err) } @@ -391,7 +392,7 @@ func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, id string) { func DeleteProfile(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete profile: %s", id) - err := profiles.Delete(client, id).ExtractErr() + err := profiles.Delete(context.TODO(), client, id).ExtractErr() if err != nil { t.Fatalf("Error deleting profile %s: %s:", id, err) } @@ -406,7 +407,7 @@ func DeleteProfile(t *testing.T, client *gophercloud.ServiceClient, id string) { func DeleteReceiver(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete Receiver: %s", id) - res := receivers.Delete(client, id) + res := receivers.Delete(context.TODO(), client, id) if res.Err != nil { t.Fatalf("Error deleting receiver %s: %s:", id, res.Err) } @@ -431,7 +432,7 @@ func GetActionID(headers http.Header) (string, error) { func WaitForAction(client *gophercloud.ServiceClient, actionID string) error { return tools.WaitFor(func() (bool, error) { - action, err := actions.Get(client, actionID).Extract() + action, err := actions.Get(context.TODO(), client, actionID).Extract() if err != nil { return false, err } @@ -450,7 +451,7 @@ func WaitForAction(client *gophercloud.ServiceClient, actionID string) error { func WaitForNodeStatus(client *gophercloud.ServiceClient, id string, status string) error { return tools.WaitFor(func() (bool, error) { - latest, err := nodes.Get(client, id).Extract() + latest, err := nodes.Get(context.TODO(), client, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETED" { return true, nil diff --git a/internal/acceptance/openstack/clustering/v1/clusters_test.go b/internal/acceptance/openstack/clustering/v1/clusters_test.go index 6b5a339a48..53cdaa5aa9 100644 --- a/internal/acceptance/openstack/clustering/v1/clusters_test.go +++ b/internal/acceptance/openstack/clustering/v1/clusters_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "sort" "strings" "testing" @@ -28,7 +29,7 @@ func TestClustersCRUD(t *testing.T) { defer DeleteCluster(t, client, cluster.ID) // Test clusters list - allPages, err := clusters.List(client, nil).AllPages() + allPages, err := clusters.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allClusters, err := clusters.ExtractClusters(allPages) @@ -48,7 +49,7 @@ func TestClustersCRUD(t *testing.T) { Name: cluster.Name + "-UPDATED", } - res := clusters.Update(client, cluster.ID, updateOpts) + res := clusters.Update(context.TODO(), client, cluster.ID, updateOpts) th.AssertNoErr(t, res.Err) actionID, err := GetActionID(res.Header) @@ -57,14 +58,14 @@ func TestClustersCRUD(t *testing.T) { err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - newCluster, err := clusters.Get(client, cluster.ID).Extract() + newCluster, err := clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.Name, cluster.Name+"-UPDATED") tools.PrintResource(t, newCluster) // Test cluster health - actionID, err = clusters.Check(client, cluster.ID).Extract() + actionID, err = clusters.Check(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) @@ -90,13 +91,13 @@ func TestClustersResize(t *testing.T) { Strict: &iTrue, } - actionID, err := clusters.Resize(client, cluster.ID, resizeOpts).Extract() + actionID, err := clusters.Resize(context.TODO(), client, cluster.ID, resizeOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - newCluster, err := clusters.Get(client, cluster.ID).Extract() + newCluster, err := clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.DesiredCapacity, 2) @@ -119,13 +120,13 @@ func TestClustersScale(t *testing.T) { scaleOutOpts := clusters.ScaleOutOpts{ Count: 1, } - actionID, err := clusters.ScaleOut(client, cluster.ID, scaleOutOpts).Extract() + actionID, err := clusters.ScaleOut(context.TODO(), client, cluster.ID, scaleOutOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - newCluster, err := clusters.Get(client, cluster.ID).Extract() + newCluster, err := clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.DesiredCapacity, 2) @@ -135,13 +136,13 @@ func TestClustersScale(t *testing.T) { Count: &count, } - actionID, err = clusters.ScaleIn(client, cluster.ID, scaleInOpts).Extract() + actionID, err = clusters.ScaleIn(context.TODO(), client, cluster.ID, scaleInOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - newCluster, err = clusters.Get(client, cluster.ID).Extract() + newCluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.DesiredCapacity, 0) @@ -171,7 +172,7 @@ func TestClustersPolicies(t *testing.T) { Enabled: &iTrue, } - actionID, err := clusters.AttachPolicy(client, cluster.ID, attachPolicyOpts).Extract() + actionID, err := clusters.AttachPolicy(context.TODO(), client, cluster.ID, attachPolicyOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) @@ -179,7 +180,7 @@ func TestClustersPolicies(t *testing.T) { // List all policies in the cluster to see if the policy was // successfully attached. - allPages, err := clusters.ListPolicies(client, cluster.ID, nil).AllPages() + allPages, err := clusters.ListPolicies(client, cluster.ID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := clusters.ExtractClusterPolicies(allPages) @@ -202,13 +203,13 @@ func TestClustersPolicies(t *testing.T) { Enabled: &iFalse, } - actionID, err = clusters.UpdatePolicy(client, cluster.ID, updatePolicyOpts).Extract() + actionID, err = clusters.UpdatePolicy(context.TODO(), client, cluster.ID, updatePolicyOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - clusterPolicy, err := clusters.GetPolicy(client, cluster.ID, policy.ID).Extract() + clusterPolicy, err := clusters.GetPolicy(context.TODO(), client, cluster.ID, policy.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, clusterPolicy.Enabled, false) @@ -217,7 +218,7 @@ func TestClustersPolicies(t *testing.T) { PolicyID: policy.ID, } - actionID, err = clusters.DetachPolicy(client, cluster.ID, detachPolicyOpts).Extract() + actionID, err = clusters.DetachPolicy(context.TODO(), client, cluster.ID, detachPolicyOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) @@ -225,7 +226,7 @@ func TestClustersPolicies(t *testing.T) { // List all policies in the cluster to see if the policy was // successfully detached. - allPages, err = clusters.ListPolicies(client, cluster.ID, nil).AllPages() + allPages, err = clusters.ListPolicies(client, cluster.ID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err = clusters.ExtractClusterPolicies(allPages) @@ -258,13 +259,13 @@ func TestClustersRecovery(t *testing.T) { Operation: clusters.RebuildRecovery, } - actionID, err := clusters.Recover(client, cluster.ID, recoverOpts).Extract() + actionID, err := clusters.Recover(context.TODO(), client, cluster.ID, recoverOpts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - newCluster, err := clusters.Get(client, cluster.ID).Extract() + newCluster, err := clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newCluster) @@ -292,7 +293,7 @@ func TestClustersAddNode(t *testing.T) { // Even tho deleting the cluster will delete the nodes but only if added into cluster successfully. defer DeleteNode(t, client, node2.ID) - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) nodeIDs := []string{node1.ID, node2.ID} @@ -302,7 +303,7 @@ func TestClustersAddNode(t *testing.T) { addNodesOpts := clusters.AddNodesOpts{ Nodes: nodeNames, } - actionID, err := clusters.AddNodes(client, cluster.ID, addNodesOpts).Extract() + actionID, err := clusters.AddNodes(context.TODO(), client, cluster.ID, addNodesOpts).Extract() if err != nil { t.Fatalf("Unable to add nodes to cluster: %v", err) } @@ -310,7 +311,7 @@ func TestClustersAddNode(t *testing.T) { err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) sort.Strings(nodeIDs) @@ -336,12 +337,12 @@ func TestClustersRemoveNodeFromCluster(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, cluster) opt := clusters.RemoveNodesOpts{Nodes: cluster.Nodes} - actionID, err := clusters.RemoveNodes(client, cluster.ID, opt).Extract() + actionID, err := clusters.RemoveNodes(context.TODO(), client, cluster.ID, opt).Extract() if err != nil { t.Fatalf("Unable to remove nodes to cluster: %v", err) } @@ -353,7 +354,7 @@ func TestClustersRemoveNodeFromCluster(t *testing.T) { err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(cluster.Nodes)) @@ -378,7 +379,7 @@ func TestClustersReplaceNode(t *testing.T) { th.AssertNoErr(t, err) defer DeleteNode(t, client, node1.ID) - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, len(cluster.Nodes) > 0) for _, n := range cluster.Nodes { @@ -387,12 +388,12 @@ func TestClustersReplaceNode(t *testing.T) { nodeIDToBeReplaced := cluster.Nodes[0] opts := clusters.ReplaceNodesOpts{Nodes: map[string]string{nodeIDToBeReplaced: node1.ID}} - actionID, err := clusters.ReplaceNodes(client, cluster.ID, opts).Extract() + actionID, err := clusters.ReplaceNodes(context.TODO(), client, cluster.ID, opts).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) clusterNodes := strings.Join(cluster.Nodes, ",") @@ -414,14 +415,14 @@ func TestClustersCollectAttributes(t *testing.T) { th.AssertNoErr(t, err) defer DeleteCluster(t, client, cluster.ID) - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, len(cluster.Nodes) > 0) _, err = CreateNode(t, client, cluster.ID, profile.ID) th.AssertNoErr(t, err) - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, len(cluster.Nodes) > 0) @@ -432,7 +433,7 @@ func TestClustersCollectAttributes(t *testing.T) { opts := clusters.CollectOpts{ Path: "status", } - attrs, err := clusters.Collect(client, cluster.ID, opts).Extract() + attrs, err := clusters.Collect(context.TODO(), client, cluster.ID, opts).Extract() th.AssertNoErr(t, err) for _, attr := range attrs { th.AssertEquals(t, attr.Value, "ACTIVE") @@ -441,7 +442,7 @@ func TestClustersCollectAttributes(t *testing.T) { opts = clusters.CollectOpts{ Path: "data.placement.zone", } - attrs, err = clusters.Collect(client, cluster.ID, opts).Extract() + attrs, err = clusters.Collect(context.TODO(), client, cluster.ID, opts).Extract() th.AssertNoErr(t, err) for _, attr := range attrs { th.AssertEquals(t, attr.Value, "nova") @@ -490,20 +491,20 @@ func TestClustersOps(t *testing.T) { for _, op := range ops { opName := string(op.Operation) t.Logf("Attempting to perform '%s' on cluster: %s", opName, cluster.ID) - actionID, res := clusters.Ops(client, cluster.ID, op).Extract() + actionID, res := clusters.Ops(context.TODO(), client, cluster.ID, op).Extract() th.AssertNoErr(t, res) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - action, err := actions.Get(client, actionID).Extract() + action, err := actions.Get(context.TODO(), client, actionID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "SUCCEEDED", action.Status) t.Logf("Successfully performed '%s' on cluster: %s", opName, cluster.ID) } - cluster, err = clusters.Get(client, cluster.ID).Extract() + cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, cluster) } diff --git a/internal/acceptance/openstack/clustering/v1/events_test.go b/internal/acceptance/openstack/clustering/v1/events_test.go index dcc7c5638e..98676da5d9 100644 --- a/internal/acceptance/openstack/clustering/v1/events_test.go +++ b/internal/acceptance/openstack/clustering/v1/events_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -20,7 +21,7 @@ func TestEventsList(t *testing.T) { Limit: 200, } - allPages, err := events.List(client, opts).AllPages() + allPages, err := events.List(client, opts).AllPages(context.TODO()) th.AssertNoErr(t, err) allEvents, err := events.ExtractEvents(allPages) diff --git a/internal/acceptance/openstack/clustering/v1/nodes_test.go b/internal/acceptance/openstack/clustering/v1/nodes_test.go index 38a5b8af7e..13534256a8 100644 --- a/internal/acceptance/openstack/clustering/v1/nodes_test.go +++ b/internal/acceptance/openstack/clustering/v1/nodes_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -29,7 +30,7 @@ func TestNodesCRUD(t *testing.T) { defer DeleteNode(t, client, node.ID) // Test nodes list - allPages, err := nodes.List(client, nil).AllPages() + allPages, err := nodes.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allNodes, err := nodes.ExtractNodes(allPages) @@ -53,7 +54,7 @@ func TestNodesCRUD(t *testing.T) { }, } - res := nodes.Update(client, node.ID, updateOpts) + res := nodes.Update(context.TODO(), client, node.ID, updateOpts) th.AssertNoErr(t, res.Err) actionID, err := GetActionID(res.Header) @@ -62,7 +63,7 @@ func TestNodesCRUD(t *testing.T) { err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - node, err = nodes.Get(client, node.ID).Extract() + node, err = nodes.Get(context.TODO(), client, node.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, node) @@ -110,13 +111,13 @@ func TestNodesOps(t *testing.T) { for _, op := range ops { opName := string(op.Operation) t.Logf("Attempting to perform '%s' on node: %s", opName, node.ID) - actionID, res := nodes.Ops(client, node.ID, op).Extract() + actionID, res := nodes.Ops(context.TODO(), client, node.ID, op).Extract() th.AssertNoErr(t, res) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - node, err = nodes.Get(client, node.ID).Extract() + node, err = nodes.Get(context.TODO(), client, node.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "Operation '"+opName+"' succeeded", node.StatusReason) t.Logf("Successfully performed '%s' on node: %s", opName, node.ID) @@ -168,7 +169,7 @@ func TestNodesRecover(t *testing.T) { t.Logf("Attempting to recover by using '%s' on node: %s", recoverOpt.Operation, node.ID) } - actionID, err := nodes.Recover(client, node.ID, recoverOpt).Extract() + actionID, err := nodes.Recover(context.TODO(), client, node.ID, recoverOpt).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) @@ -179,7 +180,7 @@ func TestNodesRecover(t *testing.T) { t.Logf("Successfully recovered by using '%s' on node: %s", recoverOpt.Operation, node.ID) } - node, err := nodes.Get(client, node.ID).Extract() + node, err := nodes.Get(context.TODO(), client, node.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, node) } @@ -203,13 +204,13 @@ func TestNodeCheck(t *testing.T) { t.Logf("Attempting to check on node: %s", node.ID) - actionID, err := nodes.Check(client, node.ID).Extract() + actionID, err := nodes.Check(context.TODO(), client, node.ID).Extract() th.AssertNoErr(t, err) err = WaitForAction(client, actionID) th.AssertNoErr(t, err) - node, err = nodes.Get(client, node.ID).Extract() + node, err = nodes.Get(context.TODO(), client, node.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "Check: Node is ACTIVE.", node.StatusReason) tools.PrintResource(t, node) diff --git a/internal/acceptance/openstack/clustering/v1/policies_test.go b/internal/acceptance/openstack/clustering/v1/policies_test.go index 97011c3e60..a763dd6b6f 100644 --- a/internal/acceptance/openstack/clustering/v1/policies_test.go +++ b/internal/acceptance/openstack/clustering/v1/policies_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestPoliciesCRUD(t *testing.T) { defer DeletePolicy(t, client, policy.ID) // Test listing policies - allPages, err := policies.List(client, nil).AllPages() + allPages, err := policies.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) @@ -38,7 +39,7 @@ func TestPoliciesCRUD(t *testing.T) { th.AssertEquals(t, found, true) // Test Get policy - getPolicy, err := policies.Get(client, policy.ID).Extract() + getPolicy, err := policies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getPolicy) @@ -48,7 +49,7 @@ func TestPoliciesCRUD(t *testing.T) { } t.Logf("Attempting to update policy: %s", policy.ID) - updatePolicy, err := policies.Update(client, policy.ID, updateOpts).Extract() + updatePolicy, err := policies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatePolicy) @@ -60,7 +61,7 @@ func TestPoliciesCRUD(t *testing.T) { Spec: TestPolicySpec, } - validatePolicy, err := policies.Validate(client, validateOpts).Extract() + validatePolicy, err := policies.Validate(context.TODO(), client, validateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, validatePolicy) diff --git a/internal/acceptance/openstack/clustering/v1/policytypes_test.go b/internal/acceptance/openstack/clustering/v1/policytypes_test.go index c25a89dca5..4170928e35 100644 --- a/internal/acceptance/openstack/clustering/v1/policytypes_test.go +++ b/internal/acceptance/openstack/clustering/v1/policytypes_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestPolicyTypeList(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) - allPages, err := policytypes.List(client).AllPages() + allPages, err := policytypes.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicyTypes, err := policytypes.ExtractPolicyTypes(allPages) @@ -32,7 +33,7 @@ func TestPolicyTypeList_v_1_5(t *testing.T) { th.AssertNoErr(t, err) client.Microversion = "1.5" - allPages, err := policytypes.List(client).AllPages() + allPages, err := policytypes.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicyTypes, err := policytypes.ExtractPolicyTypes(allPages) @@ -47,7 +48,7 @@ func TestPolicyTypeGet(t *testing.T) { client, err := clients.NewClusteringV1Client() th.AssertNoErr(t, err) - policyType, err := policytypes.Get(client, "senlin.policy.batch-1.0").Extract() + policyType, err := policytypes.Get(context.TODO(), client, "senlin.policy.batch-1.0").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, policyType) @@ -58,7 +59,7 @@ func TestPolicyTypeGet_v_1_5(t *testing.T) { th.AssertNoErr(t, err) client.Microversion = "1.5" - policyType, err := policytypes.Get(client, "senlin.policy.batch-1.0").Extract() + policyType, err := policytypes.Get(context.TODO(), client, "senlin.policy.batch-1.0").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, policyType) diff --git a/internal/acceptance/openstack/clustering/v1/profiles_test.go b/internal/acceptance/openstack/clustering/v1/profiles_test.go index 35bca43431..550c889449 100644 --- a/internal/acceptance/openstack/clustering/v1/profiles_test.go +++ b/internal/acceptance/openstack/clustering/v1/profiles_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -21,7 +22,7 @@ func TestProfilesCRUD(t *testing.T) { defer DeleteProfile(t, client, profile.ID) // Test listing profiles - allPages, err := profiles.List(client, nil).AllPages() + allPages, err := profiles.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allProfiles, err := profiles.ExtractProfiles(allPages) @@ -41,7 +42,7 @@ func TestProfilesCRUD(t *testing.T) { Name: profile.Name + "-UPDATED", } - newProfile, err := profiles.Update(client, profile.ID, updateOpts).Extract() + newProfile, err := profiles.Update(context.TODO(), client, profile.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newProfile.Name, profile.Name+"-UPDATED") @@ -61,7 +62,7 @@ func TestProfileValidate(t *testing.T) { opts := profiles.ValidateOpts{ Spec: profile.Spec, } - validatedProfile, err := profiles.Validate(client, opts).Extract() + validatedProfile, err := profiles.Validate(context.TODO(), client, opts).Extract() th.AssertNoErr(t, err) // Do not validate the following fields for AssertDeepEquals() because the actual fields are either missing or hardcoded. diff --git a/internal/acceptance/openstack/clustering/v1/profiletypes_test.go b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go index e10af5a96f..43ba35ffa0 100644 --- a/internal/acceptance/openstack/clustering/v1/profiletypes_test.go +++ b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestProfileTypesList(t *testing.T) { client.Microversion = "1.5" - allPages, err := profiletypes.List(client).AllPages() + allPages, err := profiletypes.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allProfileTypes, err := profiletypes.ExtractProfileTypes(allPages) @@ -35,7 +36,7 @@ func TestProfileTypesOpsList(t *testing.T) { client.Microversion = "1.5" profileTypeName := "os.nova.server-1.0" - allPages, err := profiletypes.ListOps(client, profileTypeName).AllPages() + allPages, err := profiletypes.ListOps(client, profileTypeName).AllPages(context.TODO()) th.AssertNoErr(t, err) ops, err := profiletypes.ExtractOps(allPages) diff --git a/internal/acceptance/openstack/clustering/v1/receivers_test.go b/internal/acceptance/openstack/clustering/v1/receivers_test.go index 1dea38fee5..2376e83d32 100644 --- a/internal/acceptance/openstack/clustering/v1/receivers_test.go +++ b/internal/acceptance/openstack/clustering/v1/receivers_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -29,7 +30,7 @@ func TestReceiversCRUD(t *testing.T) { defer DeleteReceiver(t, client, receiver.ID) // Test listing receivers - allPages, err := receivers.List(client, nil).AllPages() + allPages, err := receivers.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allReceivers, err := receivers.ExtractReceivers(allPages) @@ -50,7 +51,7 @@ func TestReceiversCRUD(t *testing.T) { Name: newName, } - receiver, err = receivers.Update(client, receiver.ID, updateOpts).Extract() + receiver, err = receivers.Update(context.TODO(), client, receiver.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, receiver) @@ -77,7 +78,7 @@ func TestReceiversNotify(t *testing.T) { defer DeleteReceiver(t, client, receiver.ID) t.Logf("Created Mesage Receiver Name:[%s] Message Receiver ID:[%s]", receiver.Name, receiver.ID) - requestID, err := receivers.Notify(client, receiver.ID).Extract() + requestID, err := receivers.Notify(context.TODO(), client, receiver.ID).Extract() th.AssertNoErr(t, err) t.Logf("Receiver Notify Service Request ID: %s", requestID) } diff --git a/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go index 371bc8349f..8f34bf717c 100644 --- a/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -37,7 +38,7 @@ func TestClusteringWebhookTrigger(t *testing.T) { defer DeleteReceiver(t, client, receiver.ID) // trigger webhook - actionID, err := webhooks.Trigger(client, receiver.ID, opts).Extract() + actionID, err := webhooks.Trigger(context.TODO(), client, receiver.ID, opts).Extract() if err != nil { t.Fatalf("Unable to extract webhooks trigger: %v", err) } else { @@ -54,7 +55,7 @@ func TestClusteringWebhookTrigger(t *testing.T) { ClusterID: cluster.ID, } - allPages, err := nodes.List(client, nodelistopts).AllPages() + allPages, err := nodes.List(client, nodelistopts).AllPages(context.TODO()) th.AssertNoErr(t, err) allNodes, err := nodes.ExtractNodes(allPages) diff --git a/internal/acceptance/openstack/compute/v2/aggregates_test.go b/internal/acceptance/openstack/compute/v2/aggregates_test.go index 6e6aa53ae0..ed460944ea 100644 --- a/internal/acceptance/openstack/compute/v2/aggregates_test.go +++ b/internal/acceptance/openstack/compute/v2/aggregates_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "fmt" "strings" "testing" @@ -22,7 +23,7 @@ func TestAggregatesList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := aggregates.List(client).AllPages() + allPages, err := aggregates.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allAggregates, err := aggregates.ExtractAggregates(allPages) @@ -51,7 +52,7 @@ func TestAggregatesCRUD(t *testing.T) { AvailabilityZone: "new_azone", } - updatedAggregate, err := aggregates.Update(client, aggregate.ID, updateOpts).Extract() + updatedAggregate, err := aggregates.Update(context.TODO(), client, aggregate.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregate) @@ -77,7 +78,7 @@ func TestAggregatesAddRemoveHost(t *testing.T) { Host: hostToAdd, } - aggregateWithNewHost, err := aggregates.AddHost(client, aggregate.ID, addHostOpts).Extract() + aggregateWithNewHost, err := aggregates.AddHost(context.TODO(), client, aggregate.ID, addHostOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithNewHost) @@ -88,7 +89,7 @@ func TestAggregatesAddRemoveHost(t *testing.T) { Host: hostToAdd, } - aggregateWithRemovedHost, err := aggregates.RemoveHost(client, aggregate.ID, removeHostOpts).Extract() + aggregateWithRemovedHost, err := aggregates.RemoveHost(context.TODO(), client, aggregate.ID, removeHostOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithRemovedHost) @@ -110,7 +111,7 @@ func TestAggregatesSetRemoveMetadata(t *testing.T) { Metadata: map[string]interface{}{"key": "value"}, } - aggregateWithMetadata, err := aggregates.SetMetadata(client, aggregate.ID, opts).Extract() + aggregateWithMetadata, err := aggregates.SetMetadata(context.TODO(), client, aggregate.ID, opts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithMetadata) @@ -123,7 +124,7 @@ func TestAggregatesSetRemoveMetadata(t *testing.T) { Metadata: map[string]interface{}{"key": nil}, } - aggregateWithRemovedKey, err := aggregates.SetMetadata(client, aggregate.ID, optsToRemove).Extract() + aggregateWithRemovedKey, err := aggregates.SetMetadata(context.TODO(), client, aggregate.ID, optsToRemove).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aggregateWithRemovedKey) @@ -134,7 +135,7 @@ func TestAggregatesSetRemoveMetadata(t *testing.T) { } func getHypervisor(t *testing.T, client *gophercloud.ServiceClient) (string, error) { - allPages, err := hypervisors.List(client, nil).AllPages() + allPages, err := hypervisors.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) diff --git a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go index 98f36f4bc6..9d46ec0ac9 100644 --- a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go +++ b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -31,7 +32,7 @@ func TestAttachDetachInterface(t *testing.T) { tools.PrintResource(t, iface) - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) var found bool diff --git a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go index 09bf4efa2e..54e82168ed 100644 --- a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestAvailabilityZonesList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := availabilityzones.List(client).AllPages() + allPages, err := availabilityzones.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) @@ -40,7 +41,7 @@ func TestAvailabilityZonesListDetail(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := availabilityzones.ListDetail(client).AllPages() + allPages, err := availabilityzones.ListDetail(client).AllPages(context.TODO()) th.AssertNoErr(t, err) availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) diff --git a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go index 9901a24fee..45faadc132 100644 --- a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -71,7 +72,7 @@ func TestBootFromNewVolume(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - attachPages, err := volumeattach.List(client, server.ID).AllPages() + attachPages, err := volumeattach.List(client, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) @@ -118,7 +119,7 @@ func TestBootFromExistingVolume(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, computeClient, server) - attachPages, err := volumeattach.List(computeClient, server.ID).AllPages() + attachPages, err := volumeattach.List(computeClient, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) @@ -209,7 +210,7 @@ func TestAttachNewVolume(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - attachPages, err := volumeattach.List(client, server.ID).AllPages() + attachPages, err := volumeattach.List(client, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) @@ -260,7 +261,7 @@ func TestAttachExistingVolume(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, computeClient, server) - attachPages, err := volumeattach.List(computeClient, server.ID).AllPages() + attachPages, err := volumeattach.List(computeClient, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) attachments, err := volumeattach.ExtractVolumeAttachments(attachPages) diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index b5f9cb381b..8210b4acf2 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -3,6 +3,7 @@ package v2 import ( + "context" "crypto/rand" "crypto/rsa" "fmt" @@ -43,7 +44,7 @@ func AssociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floati } t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID) - err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr() + err := floatingips.AssociateInstance(context.TODO(), client, server.ID, associateOpts).ExtractErr() if err != nil { return err } @@ -61,7 +62,7 @@ func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceCli } t.Logf("Attempting to associate floating IP %s to fixed IP %s on instance %s", floatingIP.IP, fixedIP, server.ID) - err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr() + err := floatingips.AssociateInstance(context.TODO(), client, server.ID, associateOpts).ExtractErr() if err != nil { return err } @@ -88,7 +89,7 @@ func AttachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID s NetworkID: networkID, } - iface, err := attachinterfaces.Create(client, serverID, createOpts).Extract() + iface, err := attachinterfaces.Create(context.TODO(), client, serverID, createOpts).Extract() if err != nil { return nil, err } @@ -110,14 +111,14 @@ func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregat AvailabilityZone: availabilityZone, } - aggregate, err := aggregates.Create(client, createOpts).Extract() + aggregate, err := aggregates.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } t.Logf("Successfully created aggregate %d", aggregate.ID) - aggregate, err = aggregates.Get(client, aggregate.ID).Extract() + aggregate, err = aggregates.Get(context.TODO(), client, aggregate.ID).Extract() if err != nil { return nil, err } @@ -159,7 +160,7 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, serverCreateOpts.ImageRef = blockDevices[0].UUID } - server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ + server, err = bootfromvolume.Create(context.TODO(), client, bootfromvolume.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, BlockDevice: blockDevices, }).Extract() @@ -172,7 +173,7 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, return server, err } - newServer, err := servers.Get(client, server.ID).Extract() + newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() if err != nil { return nil, err } @@ -193,7 +194,7 @@ func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.Def CIDR: "0.0.0.0/0", } - defaultRule, err := dsr.Create(client, createOpts).Extract() + defaultRule, err := dsr.Create(context.TODO(), client, createOpts).Extract() if err != nil { return *defaultRule, err } @@ -223,7 +224,7 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Fla Description: flavorDescription, } - flavor, err := flavors.Create(client, createOpts).Extract() + flavor, err := flavors.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -251,7 +252,7 @@ func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatin createOpts := floatingips.CreateOpts{ Pool: choices.FloatingIPPoolName, } - floatingIP, err := floatingips.Create(client, createOpts).Extract() + floatingIP, err := floatingips.Create(context.TODO(), client, createOpts).Extract() if err != nil { return floatingIP, err } @@ -287,7 +288,7 @@ func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.K createOpts := keypairs.CreateOpts{ Name: keyPairName, } - keyPair, err := keypairs.Create(client, createOpts).Extract() + keyPair, err := keypairs.Create(context.TODO(), client, createOpts).Extract() if err != nil { return keyPair, err } @@ -329,7 +330,7 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, }, } - server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ + server, err = bootfromvolume.Create(context.TODO(), client, bootfromvolume.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, BlockDevice: blockDevices, }).Extract() @@ -342,7 +343,7 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, return server, err } - newServer, err := servers.Get(client, server.ID).Extract() + newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() if err != nil { return server, err } @@ -368,7 +369,7 @@ func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flav IsPublic: &isPublic, } - flavor, err := flavors.Create(client, createOpts).Extract() + flavor, err := flavors.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -394,7 +395,7 @@ func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*secg Description: "something", } - securityGroup, err := secgroups.Create(client, createOpts).Extract() + securityGroup, err := secgroups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -420,7 +421,7 @@ func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, se CIDR: "0.0.0.0/0", } - rule, err := secgroups.CreateRule(client, createOpts).Extract() + rule, err := secgroups.CreateRule(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -455,7 +456,7 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser pwd := tools.MakeNewPassword("") - server, err := servers.Create(client, servers.CreateOpts{ + server, err := servers.Create(context.TODO(), client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, @@ -481,7 +482,7 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser return nil, err } - newServer, err := servers.Get(client, server.ID).Extract() + newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() if err != nil { return nil, err } @@ -515,7 +516,7 @@ func CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) ( pwd := tools.MakeNewPassword("") - server, err := servers.Create(client, servers.CreateOpts{ + server, err := servers.Create(context.TODO(), client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, @@ -535,7 +536,7 @@ func CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) ( return nil, err } - newServer, err := servers.Get(client, server.ID).Extract() + newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() if err != nil { return nil, err } @@ -567,7 +568,7 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient pwd := tools.MakeNewPassword("") - server, err := servers.Create(client, servers.CreateOpts{ + server, err := servers.Create(context.TODO(), client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, AdminPass: pwd, @@ -609,7 +610,7 @@ func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, netwo pwd := tools.MakeNewPassword("") - server, err := servers.Create(client, servers.CreateOpts{ + server, err := servers.Create(context.TODO(), client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, @@ -636,7 +637,7 @@ func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, netwo return nil, err } - res := servers.Get(client, server.ID) + res := servers.Get(context.TODO(), client, server.ID) if res.Err != nil { return nil, res.Err } @@ -656,7 +657,7 @@ func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy s t.Logf("Attempting to create server group %s", name) - sg, err := servergroups.Create(client, &servergroups.CreateOpts{ + sg, err := servergroups.Create(context.TODO(), client, &servergroups.CreateOpts{ Name: name, Policies: []string{policy}, }).Extract() @@ -681,7 +682,7 @@ func CreateServerGroupMicroversion(t *testing.T, client *gophercloud.ServiceClie t.Logf("Attempting to create %s server group with max server per host = %d: %s", policy, maxServerPerHost, name) - sg, err := servergroups.Create(client, &servergroups.CreateOpts{ + sg, err := servergroups.Create(context.TODO(), client, &servergroups.CreateOpts{ Name: name, Policy: policy, Rules: &servergroups.Rules{ @@ -734,7 +735,7 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, Group: serverGroup.ID, }, } - server, err := servers.Create(client, schedulerHintsOpts).Extract() + server, err := servers.Create(context.TODO(), client, schedulerHintsOpts).Extract() if err != nil { return nil, err } @@ -743,7 +744,7 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, return nil, err } - newServer, err := servers.Get(client, server.ID).Extract() + newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() if err != nil { return nil, err } @@ -780,7 +781,7 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, }, } - server, err := servers.Create(client, keypairs.CreateOptsExt{ + server, err := servers.Create(context.TODO(), client, keypairs.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, KeyName: keyPairName, }).Extract() @@ -792,7 +793,7 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, return nil, err } - newServer, err := servers.Get(client, server.ID).Extract() + newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() if err != nil { return nil, err } @@ -817,12 +818,12 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blo } t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) - volumeAttachment, err := volumeattach.Create(client, server.ID, volumeAttachOptions).Extract() + volumeAttachment, err := volumeattach.Create(context.TODO(), client, server.ID, volumeAttachOptions).Extract() if err != nil { return volumeAttachment, err } - if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil { + if err := volumes.WaitForStatus(context.TODO(), blockClient, volume.ID, "in-use", 60); err != nil { return volumeAttachment, err } @@ -833,7 +834,7 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blo // the aggregate deleting is failed. This works best when using it as a // deferred function. func DeleteAggregate(t *testing.T, client *gophercloud.ServiceClient, aggregate *aggregates.Aggregate) { - err := aggregates.Delete(client, aggregate.ID).ExtractErr() + err := aggregates.Delete(context.TODO(), client, aggregate.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete aggregate %d", aggregate.ID) } @@ -845,7 +846,7 @@ func DeleteAggregate(t *testing.T, client *gophercloud.ServiceClient, aggregate // A fatal error will occur if the rule failed to delete. This works best when // using it as a deferred function. func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) { - err := dsr.Delete(client, defaultRule.ID).ExtractErr() + err := dsr.Delete(context.TODO(), client, defaultRule.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err) } @@ -856,7 +857,7 @@ func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultR // DeleteFlavor will delete a flavor. A fatal error will occur if the flavor // could not be deleted. This works best when using it as a deferred function. func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) { - err := flavors.Delete(client, flavor.ID).ExtractErr() + err := flavors.Delete(context.TODO(), client, flavor.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete flavor %s", flavor.ID) } @@ -868,7 +869,7 @@ func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavo // the floating IP failed to de-allocate. This works best when using it as a // deferred function. func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) { - err := floatingips.Delete(client, floatingIP.ID).ExtractErr() + err := floatingips.Delete(context.TODO(), client, floatingIP.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err) } @@ -880,7 +881,7 @@ func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingI // the keypair failed to be deleted. This works best when used as a deferred // function. func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) { - err := keypairs.Delete(client, keyPair.Name, nil).ExtractErr() + err := keypairs.Delete(context.TODO(), client, keyPair.Name, nil).ExtractErr() if err != nil { t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err) } @@ -891,7 +892,7 @@ func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *key // DeleteSecurityGroup will delete a security group. A fatal error will occur // if the group failed to be deleted. This works best as a deferred function. func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) { - err := secgroups.Delete(client, securityGroupID).ExtractErr() + err := secgroups.Delete(context.TODO(), client, securityGroupID).ExtractErr() if err != nil { t.Fatalf("Unable to delete security group %s: %s", securityGroupID, err) } @@ -903,7 +904,7 @@ func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securi // will occur if the rule failed to be deleted. This works best when used // as a deferred function. func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { - err := secgroups.DeleteRule(client, ruleID).ExtractErr() + err := secgroups.DeleteRule(context.TODO(), client, ruleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete rule: %v", err) } @@ -915,7 +916,7 @@ func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ru // A fatal error will occur if the instance failed to be destroyed. This works // best when using it as a deferred function. func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) { - err := servers.Delete(client, server.ID).ExtractErr() + err := servers.Delete(context.TODO(), client, server.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete server %s: %s", server.ID, err) } @@ -937,7 +938,7 @@ func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *serve // the server group failed to be deleted. This works best when used as a // deferred function. func DeleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) { - err := servergroups.Delete(client, serverGroup.ID).ExtractErr() + err := servergroups.Delete(context.TODO(), client, serverGroup.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete server group %s: %v", serverGroup.ID, err) } @@ -950,12 +951,12 @@ func DeleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGr // as a deferred function. func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) { - err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr() + err := volumeattach.Delete(context.TODO(), client, server.ID, volumeAttachment.VolumeID).ExtractErr() if err != nil { t.Fatalf("Unable to detach volume: %v", err) } - if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil { + if err := volumes.WaitForStatus(context.TODO(), blockClient, volumeAttachment.ID, "available", 60); err != nil { t.Fatalf("Unable to wait for volume: %v", err) } t.Logf("Deleted volume: %s", volumeAttachment.VolumeID) @@ -967,7 +968,7 @@ func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blo func DetachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID, portID string) { t.Logf("Attempting to detach interface %s from server %s", portID, serverID) - err := attachinterfaces.Delete(client, serverID, portID).ExtractErr() + err := attachinterfaces.Delete(context.TODO(), client, serverID, portID).ExtractErr() if err != nil { t.Fatalf("Unable to detach interface %s from server %s", portID, serverID) } @@ -983,7 +984,7 @@ func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, flo FloatingIP: floatingIP.IP, } - err := floatingips.DisassociateInstance(client, server.ID, disassociateOpts).ExtractErr() + err := floatingips.DisassociateInstance(context.TODO(), client, server.ID, disassociateOpts).ExtractErr() if err != nil { t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err) } @@ -995,7 +996,7 @@ func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, flo // UUID using the os-networks API extension. An error will be returned if the // network could not be retrieved. func GetNetworkIDFromOSNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { - allPages, err := networks.List(client).AllPages() + allPages, err := networks.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list networks: %v", err) } @@ -1022,7 +1023,7 @@ func GetNetworkIDFromOSNetworks(t *testing.T, client *gophercloud.ServiceClient, // network name using the os-tenant-networks API extension. An error will be // returned if the network could not be retrieved. func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { - allPages, err := tenantnetworks.List(client).AllPages() + allPages, err := tenantnetworks.List(client).AllPages(context.TODO()) if err != nil { return "", err } @@ -1045,7 +1046,7 @@ func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceCli // name using either the os-tenant-networks API extension or Neutron API. // An error will be returned if the network could not be retrieved. func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { - allPages, err := tenantnetworks.List(client).AllPages() + allPages, err := tenantnetworks.List(client).AllPages(context.TODO()) if err == nil { allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) if err != nil { @@ -1062,7 +1063,7 @@ func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, n networkClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages2, err := neutron.List(networkClient, nil).AllPages() + allPages2, err := neutron.List(networkClient, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allNetworks, err := neutron.ExtractNetworks(allPages2) @@ -1087,7 +1088,7 @@ func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey Name: keyPairName, PublicKey: publicKey, } - keyPair, err := keypairs.Create(client, createOpts).Extract() + keyPair, err := keypairs.Create(context.TODO(), client, createOpts).Extract() if err != nil { return keyPair, err } @@ -1112,7 +1113,7 @@ func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *serve opts := &servers.ResizeOpts{ FlavorRef: choices.FlavorIDResize, } - if res := servers.Resize(client, server.ID, opts); res.Err != nil { + if res := servers.Resize(context.TODO(), client, server.ID, opts); res.Err != nil { return res.Err } @@ -1127,7 +1128,7 @@ func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *serve // the specified status or the status becomes ERROR. func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error { return tools.WaitFor(func() (bool, error) { - latest, err := servers.Get(client, server.ID).Extract() + latest, err := servers.Get(context.TODO(), client, server.ID).Extract() if err != nil { return false, err } @@ -1166,7 +1167,7 @@ func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOp // RescueServer will place the specified server into rescue mode. func RescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { t.Logf("Attempting to put server %s into rescue mode", server.ID) - _, err := rescueunrescue.Rescue(client, server.ID, rescueunrescue.RescueOpts{}).Extract() + _, err := rescueunrescue.Rescue(context.TODO(), client, server.ID, rescueunrescue.RescueOpts{}).Extract() if err != nil { return err } @@ -1181,7 +1182,7 @@ func RescueServer(t *testing.T, client *gophercloud.ServiceClient, server *serve // UnrescueServer will return server from rescue mode. func UnrescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { t.Logf("Attempting to return server %s from rescue mode", server.ID) - if err := rescueunrescue.Unrescue(client, server.ID).ExtractErr(); err != nil { + if err := rescueunrescue.Unrescue(context.TODO(), client, server.ID).ExtractErr(); err != nil { return err } @@ -1200,7 +1201,7 @@ func CreateRemoteConsole(t *testing.T, client *gophercloud.ServiceClient, server } t.Logf("Attempting to create a %s console for the server %s", createOpts.Type, serverID) - remoteConsole, err := remoteconsoles.Create(client, serverID, createOpts).Extract() + remoteConsole, err := remoteconsoles.Create(context.TODO(), client, serverID, createOpts).Extract() if err != nil { return nil, err } @@ -1225,7 +1226,7 @@ func CreateServerNoNetwork(t *testing.T, client *gophercloud.ServiceClient) (*se pwd := tools.MakeNewPassword("") - server, err := servers.Create(client, servers.CreateOpts{ + server, err := servers.Create(context.TODO(), client, servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, @@ -1249,7 +1250,7 @@ func CreateServerNoNetwork(t *testing.T, client *gophercloud.ServiceClient) (*se return nil, err } - newServer, err := servers.Get(client, server.ID).Extract() + newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() if err != nil { return nil, err } diff --git a/internal/acceptance/openstack/compute/v2/defsecrules_test.go b/internal/acceptance/openstack/compute/v2/defsecrules_test.go index 4fa4e3d4a1..c457be6920 100644 --- a/internal/acceptance/openstack/compute/v2/defsecrules_test.go +++ b/internal/acceptance/openstack/compute/v2/defsecrules_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestDefSecRulesList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := dsr.List(client).AllPages() + allPages, err := dsr.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allDefaultRules, err := dsr.ExtractDefaultRules(allPages) @@ -55,7 +56,7 @@ func TestDefSecRulesGet(t *testing.T) { th.AssertNoErr(t, err) defer DeleteDefaultRule(t, client, defaultRule) - newDefaultRule, err := dsr.Get(client, defaultRule.ID).Extract() + newDefaultRule, err := dsr.Get(context.TODO(), client, defaultRule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newDefaultRule) diff --git a/internal/acceptance/openstack/compute/v2/diagnostics_test.go b/internal/acceptance/openstack/compute/v2/diagnostics_test.go index 473b6fda61..2de23bfb62 100644 --- a/internal/acceptance/openstack/compute/v2/diagnostics_test.go +++ b/internal/acceptance/openstack/compute/v2/diagnostics_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestDiagnostics(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - diag, err := diagnostics.Get(client, server.ID).Extract() + diag, err := diagnostics.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, diag) diff --git a/internal/acceptance/openstack/compute/v2/extension_test.go b/internal/acceptance/openstack/compute/v2/extension_test.go index f51ec75910..3ed26bcbce 100644 --- a/internal/acceptance/openstack/compute/v2/extension_test.go +++ b/internal/acceptance/openstack/compute/v2/extension_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestExtensionsList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := extensions.List(client).AllPages() + allPages, err := extensions.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allExtensions, err := extensions.ExtractExtensions(allPages) @@ -38,7 +39,7 @@ func TestExtensionsGet(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "os-admin-actions").Extract() + extension, err := extensions.Get(context.TODO(), client, "os-admin-actions").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, extension) diff --git a/internal/acceptance/openstack/compute/v2/flavors_test.go b/internal/acceptance/openstack/compute/v2/flavors_test.go index 0643dfd64d..31ed4fb6de 100644 --- a/internal/acceptance/openstack/compute/v2/flavors_test.go +++ b/internal/acceptance/openstack/compute/v2/flavors_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -20,7 +21,7 @@ func TestFlavorsList(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - allPages, err := flavors.ListDetail(client, nil).AllPages() + allPages, err := flavors.ListDetail(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allFlavors, err := flavors.ExtractFlavors(allPages) @@ -50,7 +51,7 @@ func TestFlavorsAccessTypeList(t *testing.T) { for flavorTypeName, flavorAccessType := range flavorAccessTypes { t.Logf("** %s flavors: **", flavorTypeName) - allPages, err := flavors.ListDetail(client, flavors.ListOpts{AccessType: flavorAccessType}).AllPages() + allPages, err := flavors.ListDetail(client, flavors.ListOpts{AccessType: flavorAccessType}).AllPages(context.TODO()) th.AssertNoErr(t, err) allFlavors, err := flavors.ExtractFlavors(allPages) @@ -69,7 +70,7 @@ func TestFlavorsGet(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - flavor, err := flavors.Get(client, choices.FlavorID).Extract() + flavor, err := flavors.Get(context.TODO(), client, choices.FlavorID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, flavor) @@ -94,12 +95,12 @@ func TestFlavorExtraSpecsGet(t *testing.T) { "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } - createdExtraSpecs, err := flavors.CreateExtraSpecs(client, flavor.ID, createOpts).Extract() + createdExtraSpecs, err := flavors.CreateExtraSpecs(context.TODO(), client, flavor.ID, createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, createdExtraSpecs) - flavor, err = flavors.Get(client, flavor.ID).Extract() + flavor, err = flavors.Get(context.TODO(), client, flavor.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, flavor) @@ -138,7 +139,7 @@ func TestFlavorsCreateUpdateDelete(t *testing.T) { Description: newFlavorDescription, } - flavor, err = flavors.Update(client, flavor.ID, updateOpts).Extract() + flavor, err = flavors.Update(context.TODO(), client, flavor.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, flavor.Description, newFlavorDescription) @@ -155,7 +156,7 @@ func TestFlavorsAccessesList(t *testing.T) { th.AssertNoErr(t, err) defer DeleteFlavor(t, client, flavor) - allPages, err := flavors.ListAccesses(client, flavor.ID).AllPages() + allPages, err := flavors.ListAccesses(client, flavor.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allAccesses, err := flavors.ExtractAccesses(allPages) @@ -185,7 +186,7 @@ func TestFlavorsAccessCRUD(t *testing.T) { Tenant: project.ID, } - accessList, err := flavors.AddAccess(client, flavor.ID, addAccessOpts).Extract() + accessList, err := flavors.AddAccess(context.TODO(), client, flavor.ID, addAccessOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, len(accessList), 1) @@ -200,7 +201,7 @@ func TestFlavorsAccessCRUD(t *testing.T) { Tenant: project.ID, } - accessList, err = flavors.RemoveAccess(client, flavor.ID, removeAccessOpts).Extract() + accessList, err = flavors.RemoveAccess(context.TODO(), client, flavor.ID, removeAccessOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, len(accessList), 0) @@ -220,7 +221,7 @@ func TestFlavorsExtraSpecsCRUD(t *testing.T) { "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } - createdExtraSpecs, err := flavors.CreateExtraSpecs(client, flavor.ID, createOpts).Extract() + createdExtraSpecs, err := flavors.CreateExtraSpecs(context.TODO(), client, flavor.ID, createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, createdExtraSpecs) @@ -229,18 +230,18 @@ func TestFlavorsExtraSpecsCRUD(t *testing.T) { th.AssertEquals(t, createdExtraSpecs["hw:cpu_policy"], "CPU-POLICY") th.AssertEquals(t, createdExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY") - err = flavors.DeleteExtraSpec(client, flavor.ID, "hw:cpu_policy").ExtractErr() + err = flavors.DeleteExtraSpec(context.TODO(), client, flavor.ID, "hw:cpu_policy").ExtractErr() th.AssertNoErr(t, err) updateOpts := flavors.ExtraSpecsOpts{ "hw:cpu_thread_policy": "CPU-THREAD-POLICY-BETTER", } - updatedExtraSpec, err := flavors.UpdateExtraSpec(client, flavor.ID, updateOpts).Extract() + updatedExtraSpec, err := flavors.UpdateExtraSpec(context.TODO(), client, flavor.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedExtraSpec) - allExtraSpecs, err := flavors.ListExtraSpecs(client, flavor.ID).Extract() + allExtraSpecs, err := flavors.ListExtraSpecs(context.TODO(), client, flavor.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, allExtraSpecs) @@ -248,7 +249,7 @@ func TestFlavorsExtraSpecsCRUD(t *testing.T) { th.AssertEquals(t, len(allExtraSpecs), 1) th.AssertEquals(t, allExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY-BETTER") - spec, err := flavors.GetExtraSpec(client, flavor.ID, "hw:cpu_thread_policy").Extract() + spec, err := flavors.GetExtraSpec(context.TODO(), client, flavor.ID, "hw:cpu_thread_policy").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, spec) diff --git a/internal/acceptance/openstack/compute/v2/floatingip_test.go b/internal/acceptance/openstack/compute/v2/floatingip_test.go index a09476f72a..0445e31066 100644 --- a/internal/acceptance/openstack/compute/v2/floatingip_test.go +++ b/internal/acceptance/openstack/compute/v2/floatingip_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -23,7 +24,7 @@ func TestFloatingIPsCreateDelete(t *testing.T) { tools.PrintResource(t, floatingIP) - allPages, err := floatingips.List(client).AllPages() + allPages, err := floatingips.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages) @@ -40,7 +41,7 @@ func TestFloatingIPsCreateDelete(t *testing.T) { th.AssertEquals(t, found, true) - fip, err := floatingips.Get(client, floatingIP.ID).Extract() + fip, err := floatingips.Get(context.TODO(), client, floatingIP.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, floatingIP.ID, fip.ID) @@ -66,7 +67,7 @@ func TestFloatingIPsAssociate(t *testing.T) { th.AssertNoErr(t, err) defer DisassociateFloatingIP(t, client, floatingIP, server) - newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract() + newFloatingIP, err := floatingips.Get(context.TODO(), client, floatingIP.ID).Extract() th.AssertNoErr(t, err) t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP) @@ -89,7 +90,7 @@ func TestFloatingIPsFixedIPAssociate(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - newServer, err := servers.Get(client, server.ID).Extract() + newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) floatingIP, err := CreateFloatingIP(t, client) @@ -112,7 +113,7 @@ func TestFloatingIPsFixedIPAssociate(t *testing.T) { th.AssertNoErr(t, err) defer DisassociateFloatingIP(t, client, floatingIP, newServer) - newFloatingIP, err := floatingips.Get(client, floatingIP.ID).Extract() + newFloatingIP, err := floatingips.Get(context.TODO(), client, floatingIP.ID).Extract() th.AssertNoErr(t, err) t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP) diff --git a/internal/acceptance/openstack/compute/v2/hypervisors_test.go b/internal/acceptance/openstack/compute/v2/hypervisors_test.go index 0d870ebb72..44b7c6a3ac 100644 --- a/internal/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/internal/acceptance/openstack/compute/v2/hypervisors_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "fmt" "testing" @@ -20,7 +21,7 @@ func TestHypervisorsList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := hypervisors.List(client, nil).AllPages() + allPages, err := hypervisors.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) @@ -40,7 +41,7 @@ func TestHypervisorsGet(t *testing.T) { hypervisorID, err := getHypervisorID(t, client) th.AssertNoErr(t, err) - hypervisor, err := hypervisors.Get(client, hypervisorID).Extract() + hypervisor, err := hypervisors.Get(context.TODO(), client, hypervisorID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, hypervisor) @@ -54,7 +55,7 @@ func TestHypervisorsGetStatistics(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - hypervisorsStats, err := hypervisors.GetStatistics(client).Extract() + hypervisorsStats, err := hypervisors.GetStatistics(context.TODO(), client).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, hypervisorsStats) @@ -73,7 +74,7 @@ func TestHypervisorsGetUptime(t *testing.T) { hypervisorID, err := getHypervisorID(t, client) th.AssertNoErr(t, err) - hypervisor, err := hypervisors.GetUptime(client, hypervisorID).Extract() + hypervisor, err := hypervisors.GetUptime(context.TODO(), client, hypervisorID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, hypervisor) @@ -98,7 +99,7 @@ func TestHypervisorListQuery(t *testing.T) { WithServers: &iTrue, } - allPages, err := hypervisors.List(client, listOpts).AllPages() + allPages, err := hypervisors.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) @@ -111,7 +112,7 @@ func TestHypervisorListQuery(t *testing.T) { } func getHypervisorID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { - allPages, err := hypervisors.List(client, nil).AllPages() + allPages, err := hypervisors.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allHypervisors, err := hypervisors.ExtractHypervisors(allPages) diff --git a/internal/acceptance/openstack/compute/v2/images_test.go b/internal/acceptance/openstack/compute/v2/images_test.go index e6f1bc51f7..eae54cb3e9 100644 --- a/internal/acceptance/openstack/compute/v2/images_test.go +++ b/internal/acceptance/openstack/compute/v2/images_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestImagesList(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - allPages, err := images.ListDetail(client, nil).AllPages() + allPages, err := images.ListDetail(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) @@ -44,7 +45,7 @@ func TestImagesGet(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - image, err := images.Get(client, choices.ImageID).Extract() + image, err := images.Get(context.TODO(), client, choices.ImageID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, image) diff --git a/internal/acceptance/openstack/compute/v2/instance_actions_test.go b/internal/acceptance/openstack/compute/v2/instance_actions_test.go index 25918e0ba3..7d1a7a1abe 100644 --- a/internal/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/internal/acceptance/openstack/compute/v2/instance_actions_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "time" @@ -22,7 +23,7 @@ func TestInstanceActions(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - allPages, err := instanceactions.List(client, server.ID, nil).AllPages() + allPages, err := instanceactions.List(client, server.ID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allActions, err := instanceactions.ExtractInstanceActions(allPages) th.AssertNoErr(t, err) @@ -30,7 +31,7 @@ func TestInstanceActions(t *testing.T) { var found bool for _, action := range allActions { - action, err := instanceactions.Get(client, server.ID, action.RequestID).Extract() + action, err := instanceactions.Get(context.TODO(), client, server.ID, action.RequestID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, action) @@ -59,7 +60,7 @@ func TestInstanceActionsMicroversions(t *testing.T) { Type: servers.HardReboot, } - err = servers.Reboot(client, server.ID, rebootOpts).ExtractErr() + err = servers.Reboot(context.TODO(), client, server.ID, rebootOpts).ExtractErr() if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } @@ -69,7 +70,7 @@ func TestInstanceActionsMicroversions(t *testing.T) { ChangesSince: &now, } - allPages, err := instanceactions.List(client, server.ID, listOpts).AllPages() + allPages, err := instanceactions.List(client, server.ID, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allActions, err := instanceactions.ExtractInstanceActions(allPages) @@ -78,7 +79,7 @@ func TestInstanceActionsMicroversions(t *testing.T) { var found bool for _, action := range allActions { - action, err := instanceactions.Get(client, server.ID, action.RequestID).Extract() + action, err := instanceactions.Get(context.TODO(), client, server.ID, action.RequestID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, action) @@ -94,7 +95,7 @@ func TestInstanceActionsMicroversions(t *testing.T) { ChangesBefore: &now, } - allPages, err = instanceactions.List(client, server.ID, listOpts).AllPages() + allPages, err = instanceactions.List(client, server.ID, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allActions, err = instanceactions.ExtractInstanceActions(allPages) diff --git a/internal/acceptance/openstack/compute/v2/keypairs_test.go b/internal/acceptance/openstack/compute/v2/keypairs_test.go index 4565fc5b2a..ac4e2207c8 100644 --- a/internal/acceptance/openstack/compute/v2/keypairs_test.go +++ b/internal/acceptance/openstack/compute/v2/keypairs_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -44,7 +45,7 @@ func TestKeyPairsCreateDelete(t *testing.T) { tools.PrintResource(t, keyPair) - allPages, err := keypairs.List(client, nil).AllPages() + allPages, err := keypairs.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allKeys, err := keypairs.ExtractKeyPairs(allPages) @@ -93,7 +94,7 @@ func TestKeyPairsServerCreateWithKey(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, server.KeyName, keyPair.Name) @@ -120,14 +121,14 @@ func TestKeyPairsCreateDeleteByID(t *testing.T) { UserID: user.ID, } - keyPair, err := keypairs.Create(computeClient, createOpts).Extract() + keyPair, err := keypairs.Create(context.TODO(), computeClient, createOpts).Extract() th.AssertNoErr(t, err) getOpts := keypairs.GetOpts{ UserID: user.ID, } - newKeyPair, err := keypairs.Get(computeClient, keyPair.Name, getOpts).Extract() + newKeyPair, err := keypairs.Get(context.TODO(), computeClient, keyPair.Name, getOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, keyPair.Name, newKeyPair.Name) @@ -135,7 +136,7 @@ func TestKeyPairsCreateDeleteByID(t *testing.T) { UserID: user.ID, } - allPages, err := keypairs.List(computeClient, listOpts).AllPages() + allPages, err := keypairs.List(computeClient, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allKeys, err := keypairs.ExtractKeyPairs(allPages) @@ -154,6 +155,6 @@ func TestKeyPairsCreateDeleteByID(t *testing.T) { UserID: user.ID, } - err = keypairs.Delete(computeClient, keyPair.Name, deleteOpts).ExtractErr() + err = keypairs.Delete(context.TODO(), computeClient, keyPair.Name, deleteOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/compute/v2/limits_test.go b/internal/acceptance/openstack/compute/v2/limits_test.go index f960c16472..a8257a288d 100644 --- a/internal/acceptance/openstack/compute/v2/limits_test.go +++ b/internal/acceptance/openstack/compute/v2/limits_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "strings" "testing" @@ -17,7 +18,7 @@ func TestLimits(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - limits, err := limits.Get(client, nil).Extract() + limits, err := limits.Get(context.TODO(), client, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, limits) @@ -42,7 +43,7 @@ func TestLimitsForTenant(t *testing.T) { TenantID: tenantID, } - limits, err := limits.Get(client, getOpts).Extract() + limits, err := limits.Get(context.TODO(), client, getOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, limits) diff --git a/internal/acceptance/openstack/compute/v2/migrate_test.go b/internal/acceptance/openstack/compute/v2/migrate_test.go index bebbe9af06..0e320739d1 100644 --- a/internal/acceptance/openstack/compute/v2/migrate_test.go +++ b/internal/acceptance/openstack/compute/v2/migrate_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -24,7 +25,7 @@ func TestMigrate(t *testing.T) { t.Logf("Attempting to migrate server %s", server.ID) - err = migrate.Migrate(client, server.ID).ExtractErr() + err = migrate.Migrate(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) } @@ -50,6 +51,6 @@ func TestLiveMigrate(t *testing.T) { DiskOverCommit: &diskOverCommit, } - err = migrate.LiveMigrate(client, server.ID, liveMigrateOpts).ExtractErr() + err = migrate.LiveMigrate(context.TODO(), client, server.ID, liveMigrateOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/compute/v2/network_test.go b/internal/acceptance/openstack/compute/v2/network_test.go index 5ae1db18ba..c32459fd66 100644 --- a/internal/acceptance/openstack/compute/v2/network_test.go +++ b/internal/acceptance/openstack/compute/v2/network_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestNetworksList(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - allPages, err := networks.List(client).AllPages() + allPages, err := networks.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allNetworks, err := networks.ExtractNetworks(allPages) @@ -47,7 +48,7 @@ func TestNetworksGet(t *testing.T) { networkID, err := GetNetworkIDFromOSNetworks(t, client, choices.NetworkName) th.AssertNoErr(t, err) - network, err := networks.Get(client, networkID).Extract() + network, err := networks.Get(context.TODO(), client, networkID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, network) diff --git a/internal/acceptance/openstack/compute/v2/quotaset_test.go b/internal/acceptance/openstack/compute/v2/quotaset_test.go index 01e7216e55..dfd55af976 100644 --- a/internal/acceptance/openstack/compute/v2/quotaset_test.go +++ b/internal/acceptance/openstack/compute/v2/quotaset_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "fmt" "os" "testing" @@ -26,7 +27,7 @@ func TestQuotasetGet(t *testing.T) { projectID, err := getProjectID(t, identityClient) th.AssertNoErr(t, err) - quotaSet, err := quotasets.Get(client, projectID).Extract() + quotaSet, err := quotasets.Get(context.TODO(), client, projectID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotaSet) @@ -35,7 +36,7 @@ func TestQuotasetGet(t *testing.T) { } func getProjectID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { - allPages, err := projects.ListAvailable(client).AllPages() + allPages, err := projects.ListAvailable(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) @@ -49,7 +50,7 @@ func getProjectID(t *testing.T, client *gophercloud.ServiceClient) (string, erro } func getProjectIDByName(t *testing.T, client *gophercloud.ServiceClient, name string) (string, error) { - allPages, err := projects.List(client, nil).AllPages() + allPages, err := projects.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) @@ -113,20 +114,20 @@ func TestQuotasetUpdateDelete(t *testing.T) { th.AssertNoErr(t, err) // save original quotas - orig, err := quotasets.Get(client, projectid).Extract() + orig, err := quotasets.Get(context.TODO(), client, projectid).Extract() th.AssertNoErr(t, err) // Test Update - res, err := quotasets.Update(client, projectid, UpdateQuotaOpts).Extract() + res, err := quotasets.Update(context.TODO(), client, projectid, UpdateQuotaOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, UpdatedQuotas, *res) // Test Delete - _, err = quotasets.Delete(client, projectid).Extract() + _, err = quotasets.Delete(context.TODO(), 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, projectid).Extract() + newres, err := quotasets.Get(context.TODO(), client, projectid).Extract() th.AssertNoErr(t, err) if newres.RAM == res.RAM { t.Fatalf("Failed to update quotas") @@ -136,7 +137,7 @@ func TestQuotasetUpdateDelete(t *testing.T) { FillUpdateOptsFromQuotaSet(*orig, &restore) // restore original quotas - res, err = quotasets.Update(client, projectid, restore).Extract() + res, err = quotasets.Update(context.TODO(), client, projectid, restore).Extract() th.AssertNoErr(t, err) orig.ID = "" diff --git a/internal/acceptance/openstack/compute/v2/secgroup_test.go b/internal/acceptance/openstack/compute/v2/secgroup_test.go index 14eb40b026..130397c4ef 100644 --- a/internal/acceptance/openstack/compute/v2/secgroup_test.go +++ b/internal/acceptance/openstack/compute/v2/secgroup_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestSecGroupsList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := secgroups.List(client).AllPages() + allPages, err := secgroups.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allSecGroups, err := secgroups.ExtractSecurityGroups(allPages) @@ -51,7 +52,7 @@ func TestSecGroupsCRUD(t *testing.T) { Name: newName, Description: &description, } - updatedSecurityGroup, err := secgroups.Update(client, securityGroup.ID, updateOpts).Extract() + updatedSecurityGroup, err := secgroups.Update(context.TODO(), client, securityGroup.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedSecurityGroup) @@ -78,7 +79,7 @@ func TestSecGroupsRuleCreate(t *testing.T) { tools.PrintResource(t, rule) - newSecurityGroup, err := secgroups.Get(client, securityGroup.ID).Extract() + newSecurityGroup, err := secgroups.Get(context.TODO(), client, securityGroup.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSecurityGroup) @@ -105,10 +106,10 @@ func TestSecGroupsAddGroupToServer(t *testing.T) { defer DeleteSecurityGroupRule(t, client, rule.ID) t.Logf("Adding group %s to server %s", securityGroup.ID, server.ID) - err = secgroups.AddServer(client, server.ID, securityGroup.Name).ExtractErr() + err = secgroups.AddServer(context.TODO(), client, server.ID, securityGroup.Name).ExtractErr() th.AssertNoErr(t, err) - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, server) @@ -123,10 +124,10 @@ func TestSecGroupsAddGroupToServer(t *testing.T) { th.AssertEquals(t, found, true) t.Logf("Removing group %s from server %s", securityGroup.ID, server.ID) - err = secgroups.RemoveServer(client, server.ID, securityGroup.Name).ExtractErr() + err = secgroups.RemoveServer(context.TODO(), client, server.ID, securityGroup.Name).ExtractErr() th.AssertNoErr(t, err) - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) found = false diff --git a/internal/acceptance/openstack/compute/v2/servergroup_test.go b/internal/acceptance/openstack/compute/v2/servergroup_test.go index 63c1d56844..5421150024 100644 --- a/internal/acceptance/openstack/compute/v2/servergroup_test.go +++ b/internal/acceptance/openstack/compute/v2/servergroup_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -21,12 +22,12 @@ func TestServergroupsCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServerGroup(t, client, serverGroup) - serverGroup, err = servergroups.Get(client, serverGroup.ID).Extract() + serverGroup, err = servergroups.Get(context.TODO(), client, serverGroup.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, serverGroup) - allPages, err := servergroups.List(client, &servergroups.ListOpts{}).AllPages() + allPages, err := servergroups.List(client, &servergroups.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allServerGroups, err := servergroups.ExtractServerGroups(allPages) @@ -58,14 +59,14 @@ func TestServergroupsAffinityPolicy(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, firstServer) - firstServer, err = servers.Get(client, firstServer.ID).Extract() + firstServer, err = servers.Get(context.TODO(), client, firstServer.ID).Extract() th.AssertNoErr(t, err) secondServer, err := CreateServerInServerGroup(t, client, serverGroup) th.AssertNoErr(t, err) defer DeleteServer(t, client, secondServer) - secondServer, err = servers.Get(client, secondServer.ID).Extract() + secondServer, err = servers.Get(context.TODO(), client, secondServer.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, firstServer.HostID, secondServer.HostID) @@ -80,12 +81,12 @@ func TestServergroupsMicroversionCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServerGroup(t, client, serverGroup) - serverGroup, err = servergroups.Get(client, serverGroup.ID).Extract() + serverGroup, err = servergroups.Get(context.TODO(), client, serverGroup.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, serverGroup) - allPages, err := servergroups.List(client, &servergroups.ListOpts{}).AllPages() + allPages, err := servergroups.List(client, &servergroups.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allServerGroups, err := servergroups.ExtractServerGroups(allPages) diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 08e182db14..ef58fe08c9 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "strings" "testing" @@ -37,7 +38,7 @@ func TestServersCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - allPages, err := servers.List(client, servers.ListOpts{}).AllPages() + allPages, err := servers.List(client, servers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allServers, err := servers.ExtractServers(allPages) @@ -54,7 +55,7 @@ func TestServersCreateDestroy(t *testing.T) { th.AssertEquals(t, found, true) - allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages() + allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allAddresses, err := servers.ExtractAddresses(allAddressPages) @@ -64,7 +65,7 @@ func TestServersCreateDestroy(t *testing.T) { t.Logf("Addresses on %s: %+v", network, address) } - allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages() + allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allInterfaces, err := attachinterfaces.ExtractInterfaces(allInterfacePages) @@ -74,7 +75,7 @@ func TestServersCreateDestroy(t *testing.T) { t.Logf("Interfaces: %+v", iface) } - allNetworkAddressPages, err := servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages() + allNetworkAddressPages, err := servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages(context.TODO()) th.AssertNoErr(t, err) allNetworkAddresses, err := servers.ExtractNetworkAddresses(allNetworkAddressPages) @@ -103,7 +104,7 @@ func TestServersWithExtensionsCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - err = servers.Get(client, server.ID).ExtractInto(&extendedServer) + err = servers.Get(context.TODO(), client, server.ID).ExtractInto(&extendedServer) th.AssertNoErr(t, err) tools.PrintResource(t, extendedServer) @@ -150,13 +151,13 @@ func TestServersUpdate(t *testing.T) { Name: alternateName, } - updated, err := servers.Update(client, server.ID, updateOpts).Extract() + updated, err := servers.Update(context.TODO(), client, server.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.ID, server.ID) err = tools.WaitFor(func() (bool, error) { - latest, err := servers.Get(client, updated.ID).Extract() + latest, err := servers.Get(context.TODO(), client, updated.ID).Extract() if err != nil { return false, err } @@ -177,14 +178,14 @@ func TestServersMetadata(t *testing.T) { tools.PrintResource(t, server) - metadata, err := servers.UpdateMetadata(client, server.ID, servers.MetadataOpts{ + metadata, err := servers.UpdateMetadata(context.TODO(), client, server.ID, servers.MetadataOpts{ "foo": "bar", "this": "that", }).Extract() th.AssertNoErr(t, err) t.Logf("UpdateMetadata result: %+v\n", metadata) - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, server) @@ -196,10 +197,10 @@ func TestServersMetadata(t *testing.T) { } th.AssertDeepEquals(t, expectedMetadata, server.Metadata) - err = servers.DeleteMetadatum(client, server.ID, "foo").ExtractErr() + err = servers.DeleteMetadatum(context.TODO(), client, server.ID, "foo").ExtractErr() th.AssertNoErr(t, err) - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, server) @@ -210,13 +211,13 @@ func TestServersMetadata(t *testing.T) { } th.AssertDeepEquals(t, expectedMetadata, server.Metadata) - metadata, err = servers.CreateMetadatum(client, server.ID, servers.MetadatumOpts{ + metadata, err = servers.CreateMetadatum(context.TODO(), client, server.ID, servers.MetadatumOpts{ "foo": "baz", }).Extract() th.AssertNoErr(t, err) t.Logf("CreateMetadatum result: %+v\n", metadata) - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, server) @@ -228,18 +229,18 @@ func TestServersMetadata(t *testing.T) { } th.AssertDeepEquals(t, expectedMetadata, server.Metadata) - metadata, err = servers.Metadatum(client, server.ID, "foo").Extract() + metadata, err = servers.Metadatum(context.TODO(), client, server.ID, "foo").Extract() th.AssertNoErr(t, err) t.Logf("Metadatum result: %+v\n", metadata) th.AssertEquals(t, "baz", metadata["foo"]) - metadata, err = servers.Metadata(client, server.ID).Extract() + metadata, err = servers.Metadata(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) t.Logf("Metadata result: %+v\n", metadata) th.AssertDeepEquals(t, expectedMetadata, metadata) - metadata, err = servers.ResetMetadata(client, server.ID, servers.MetadataOpts{}).Extract() + metadata, err = servers.ResetMetadata(context.TODO(), client, server.ID, servers.MetadataOpts{}).Extract() th.AssertNoErr(t, err) t.Logf("ResetMetadata result: %+v\n", metadata) th.AssertDeepEquals(t, map[string]string{}, metadata) @@ -257,7 +258,7 @@ func TestServersActionChangeAdminPassword(t *testing.T) { defer DeleteServer(t, client, server) randomPassword := tools.MakeNewPassword(server.AdminPass) - res := servers.ChangeAdminPassword(client, server.ID, randomPassword) + res := servers.ChangeAdminPassword(context.TODO(), client, server.ID, randomPassword) th.AssertNoErr(t, res.Err) if err = WaitForComputeStatus(client, server, "PASSWORD"); err != nil { @@ -284,7 +285,7 @@ func TestServersActionReboot(t *testing.T) { } t.Logf("Attempting reboot of server %s", server.ID) - res := servers.Reboot(client, server.ID, rebootOpts) + res := servers.Reboot(context.TODO(), client, server.ID, rebootOpts) th.AssertNoErr(t, res.Err) if err = WaitForComputeStatus(client, server, "REBOOT"); err != nil { @@ -317,7 +318,7 @@ func TestServersActionRebuild(t *testing.T) { ImageRef: choices.ImageID, } - rebuilt, err := servers.Rebuild(client, server.ID, rebuildOpts).Extract() + rebuilt, err := servers.Rebuild(context.TODO(), client, server.ID, rebuildOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, rebuilt.ID, server.ID) @@ -348,7 +349,7 @@ func TestServersActionResizeConfirm(t *testing.T) { ResizeServer(t, client, server) t.Logf("Attempting to confirm resize for server %s", server.ID) - if res := servers.ConfirmResize(client, server.ID); res.Err != nil { + if res := servers.ConfirmResize(context.TODO(), client, server.ID); res.Err != nil { t.Fatal(res.Err) } @@ -356,7 +357,7 @@ func TestServersActionResizeConfirm(t *testing.T) { t.Fatal(err) } - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, server.Flavor["id"], choices.FlavorIDResize) @@ -379,7 +380,7 @@ func TestServersActionResizeRevert(t *testing.T) { ResizeServer(t, client, server) t.Logf("Attempting to revert resize for server %s", server.ID) - if res := servers.RevertResize(client, server.ID); res.Err != nil { + if res := servers.RevertResize(context.TODO(), client, server.ID); res.Err != nil { t.Fatal(res.Err) } @@ -387,7 +388,7 @@ func TestServersActionResizeRevert(t *testing.T) { t.Fatal(err) } - server, err = servers.Get(client, server.ID).Extract() + server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, server.Flavor["id"], choices.FlavorID) @@ -404,13 +405,13 @@ func TestServersActionPause(t *testing.T) { defer DeleteServer(t, client, server) t.Logf("Attempting to pause server %s", server.ID) - err = pauseunpause.Pause(client, server.ID).ExtractErr() + err = pauseunpause.Pause(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "PAUSED") th.AssertNoErr(t, err) - err = pauseunpause.Unpause(client, server.ID).ExtractErr() + err = pauseunpause.Unpause(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") @@ -428,13 +429,13 @@ func TestServersActionSuspend(t *testing.T) { defer DeleteServer(t, client, server) t.Logf("Attempting to suspend server %s", server.ID) - err = suspendresume.Suspend(client, server.ID).ExtractErr() + err = suspendresume.Suspend(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "SUSPENDED") th.AssertNoErr(t, err) - err = suspendresume.Resume(client, server.ID).ExtractErr() + err = suspendresume.Resume(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") @@ -453,15 +454,15 @@ func TestServersActionLock(t *testing.T) { defer DeleteServer(t, client, server) t.Logf("Attempting to Lock server %s", server.ID) - err = lockunlock.Lock(client, server.ID).ExtractErr() + err = lockunlock.Lock(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) t.Logf("Attempting to delete locked server %s", server.ID) - err = servers.Delete(client, server.ID).ExtractErr() + err = servers.Delete(context.TODO(), client, server.ID).ExtractErr() th.AssertEquals(t, err != nil, true) t.Logf("Attempting to unlock server %s", server.ID) - err = lockunlock.Unlock(client, server.ID).ExtractErr() + err = lockunlock.Unlock(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") @@ -481,7 +482,7 @@ func TestServersConsoleOutput(t *testing.T) { outputOpts := &servers.ShowConsoleOutputOpts{ Length: 4, } - output, err := servers.ShowConsoleOutput(client, server.ID, outputOpts).Extract() + output, err := servers.ShowConsoleOutput(context.TODO(), client, server.ID, outputOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, output) @@ -512,49 +513,49 @@ func TestServersTags(t *testing.T) { client.Microversion = "2.26" // Check server tags in body. - serverWithTags, err := servers.Get(client, server.ID).Extract() + serverWithTags, err := servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag1", "tag2"}, *serverWithTags.Tags) // Check all tags. - allTags, err := tags.List(client, server.ID).Extract() + allTags, err := tags.List(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag1", "tag2"}, allTags) // Check single tag. - exists, err := tags.Check(client, server.ID, "tag2").Extract() + exists, err := tags.Check(context.TODO(), client, server.ID, "tag2").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) // Add new tag. - newTags, err := tags.ReplaceAll(client, server.ID, tags.ReplaceAllOpts{Tags: []string{"tag3", "tag4"}}).Extract() + newTags, err := tags.ReplaceAll(context.TODO(), client, server.ID, tags.ReplaceAllOpts{Tags: []string{"tag3", "tag4"}}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag3", "tag4"}, newTags) // Add new single tag. - err = tags.Add(client, server.ID, "tag5").ExtractErr() + err = tags.Add(context.TODO(), client, server.ID, "tag5").ExtractErr() th.AssertNoErr(t, err) // Check current tags. - newAllTags, err := tags.List(client, server.ID).Extract() + newAllTags, err := tags.List(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, []string{"tag3", "tag4", "tag5"}, newAllTags) // Remove single tag. - err = tags.Delete(client, server.ID, "tag4").ExtractErr() + err = tags.Delete(context.TODO(), client, server.ID, "tag4").ExtractErr() th.AssertNoErr(t, err) // Check that tag doesn't exist anymore. - exists, err = tags.Check(client, server.ID, "tag4").Extract() + exists, err = tags.Check(context.TODO(), client, server.ID, "tag4").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, exists) // Remove all tags. - err = tags.DeleteAll(client, server.ID).ExtractErr() + err = tags.DeleteAll(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) // Check that there are no more tags. - currentTags, err := tags.List(client, server.ID).Extract() + currentTags, err := tags.List(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(currentTags)) } @@ -577,7 +578,7 @@ func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { } var serverWithAttributesExt serverAttributesExt - err = servers.Get(client, server.ID).ExtractInto(&serverWithAttributesExt) + err = servers.Get(context.TODO(), client, server.ID).ExtractInto(&serverWithAttributesExt) th.AssertNoErr(t, err) t.Logf("Server With Extended Attributes: %#v", serverWithAttributesExt) @@ -606,7 +607,7 @@ func TestServerNoNetworkCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - allPages, err := servers.List(client, servers.ListOpts{}).AllPages() + allPages, err := servers.List(client, servers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allServers, err := servers.ExtractServers(allPages) @@ -623,7 +624,7 @@ func TestServerNoNetworkCreateDestroy(t *testing.T) { th.AssertEquals(t, found, true) - allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages() + allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allAddresses, err := servers.ExtractAddresses(allAddressPages) @@ -633,7 +634,7 @@ func TestServerNoNetworkCreateDestroy(t *testing.T) { t.Logf("Addresses on %s: %+v", network, address) } - allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages() + allInterfacePages, err := attachinterfaces.List(client, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allInterfaces, err := attachinterfaces.ExtractInterfaces(allInterfacePages) @@ -643,7 +644,7 @@ func TestServerNoNetworkCreateDestroy(t *testing.T) { t.Logf("Interfaces: %+v", iface) } - _, err = servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages() + _, err = servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName).AllPages(context.TODO()) if err == nil { t.Fatalf("Instance must not be a member of specified network") } diff --git a/internal/acceptance/openstack/compute/v2/services_test.go b/internal/acceptance/openstack/compute/v2/services_test.go index 3945f2029e..91f67e1792 100644 --- a/internal/acceptance/openstack/compute/v2/services_test.go +++ b/internal/acceptance/openstack/compute/v2/services_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestServicesList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := services.List(client, nil).AllPages() + allPages, err := services.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) @@ -46,7 +47,7 @@ func TestServicesListWithOpts(t *testing.T) { Binary: "nova-scheduler", } - allPages, err := services.List(client, opts).AllPages() + allPages, err := services.List(client, opts).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) @@ -76,7 +77,7 @@ func TestServicesUpdate(t *testing.T) { } client.Microversion = "2.53" - allPages, err := services.List(client, listOpts).AllPages() + allPages, err := services.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) @@ -87,14 +88,14 @@ func TestServicesUpdate(t *testing.T) { opts := services.UpdateOpts{ Status: services.ServiceDisabled, } - updated, err := services.Update(client, service.ID, opts).Extract() + updated, err := services.Update(context.TODO(), client, service.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.ID, service.ID) } // verify all services are disabled - allPages, err = services.List(client, listOpts).AllPages() + allPages, err = services.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err = services.ExtractServices(allPages) @@ -105,7 +106,7 @@ func TestServicesUpdate(t *testing.T) { } // reenable all services - allPages, err = services.List(client, listOpts).AllPages() + allPages, err = services.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err = services.ExtractServices(allPages) @@ -115,7 +116,7 @@ func TestServicesUpdate(t *testing.T) { opts := services.UpdateOpts{ Status: services.ServiceEnabled, } - updated, err := services.Update(client, service.ID, opts).Extract() + updated, err := services.Update(context.TODO(), client, service.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, updated.ID, service.ID) diff --git a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go index 70f1a7a0f8..61347506d0 100644 --- a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go +++ b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestTenantNetworksList(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) - allPages, err := tenantnetworks.List(client).AllPages() + allPages, err := tenantnetworks.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) @@ -47,7 +48,7 @@ func TestTenantNetworksGet(t *testing.T) { networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) th.AssertNoErr(t, err) - network, err := tenantnetworks.Get(client, networkID).Extract() + network, err := tenantnetworks.Get(context.TODO(), client, networkID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, network) diff --git a/internal/acceptance/openstack/compute/v2/usage_test.go b/internal/acceptance/openstack/compute/v2/usage_test.go index 7d33e88ebb..65c85f87f8 100644 --- a/internal/acceptance/openstack/compute/v2/usage_test.go +++ b/internal/acceptance/openstack/compute/v2/usage_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "strings" "testing" "time" @@ -38,7 +39,7 @@ func TestUsageSingleTenant(t *testing.T) { End: &end, } - err = usage.SingleTenant(client, tenantID, opts).EachPage(func(page pagination.Page) (bool, error) { + err = usage.SingleTenant(client, tenantID, opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { tenantUsage, err := usage.ExtractSingleTenant(page) th.AssertNoErr(t, err) @@ -73,7 +74,7 @@ func TestUsageAllTenants(t *testing.T) { End: &end, } - err = usage.AllTenants(client, opts).EachPage(func(page pagination.Page) (bool, error) { + err = usage.AllTenants(client, opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allUsage, err := usage.ExtractAllTenants(page) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/container/v1/capsules.go b/internal/acceptance/openstack/container/v1/capsules.go index a1819963da..f166400b5d 100644 --- a/internal/acceptance/openstack/container/v1/capsules.go +++ b/internal/acceptance/openstack/container/v1/capsules.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -12,7 +13,7 @@ import ( // the specified status or the status becomes Failed. func WaitForCapsuleStatus(client *gophercloud.ServiceClient, uuid, status string) error { return tools.WaitFor(func() (bool, error) { - v, err := capsules.Get(client, uuid).Extract() + v, err := capsules.Get(context.TODO(), client, uuid).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/container/v1/capsules_test.go b/internal/acceptance/openstack/container/v1/capsules_test.go index dfc6cd6c69..e61e3a36ae 100644 --- a/internal/acceptance/openstack/container/v1/capsules_test.go +++ b/internal/acceptance/openstack/container/v1/capsules_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -39,7 +40,7 @@ func TestCapsuleBase(t *testing.T) { th.AssertNoErr(t, err) pager := capsules.List(client, nil) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { v, err := capsules.ExtractCapsules(page) th.AssertNoErr(t, err) allCapsules := v.([]capsules.Capsule) @@ -93,7 +94,7 @@ func TestCapsuleV132(t *testing.T) { th.AssertNoErr(t, err) pager := capsules.List(client, nil) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allCapsules, err := capsules.ExtractCapsulesV132(page) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/containerinfra/v1/certificates_test.go b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go index 1cbd2528f6..3e26eea692 100644 --- a/internal/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -35,14 +36,14 @@ func TestCertificatesCRUD(t *testing.T) { "-----END CERTIFICATE REQUEST-----", } - createResponse, err := certificates.Create(client, opts).Extract() + createResponse, err := certificates.Create(context.TODO(), client, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, opts.CSR, createResponse.CSR) - certificate, err := certificates.Get(client, clusterUUID).Extract() + certificate, err := certificates.Get(context.TODO(), client, clusterUUID).Extract() th.AssertNoErr(t, err) t.Log(certificate.PEM) - err = certificates.Update(client, clusterUUID).ExtractErr() + err = certificates.Update(context.TODO(), client, clusterUUID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go index 6a273f1081..47b63f1288 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "time" @@ -27,7 +28,7 @@ func TestClustersCRUD(t *testing.T) { tools.PrintResource(t, clusterID) defer DeleteCluster(t, client, clusterID) - allPages, err := clusters.List(client, nil).AllPages() + allPages, err := clusters.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allClusters, err := clusters.ExtractClusters(allPages) @@ -47,7 +48,7 @@ func TestClustersCRUD(t *testing.T) { Value: 2, }, } - updateResult := clusters.Update(client, clusterID, updateOpts) + updateResult := clusters.Update(context.TODO(), client, clusterID, updateOpts) th.AssertNoErr(t, updateResult.Err) if len(updateResult.Header["X-Openstack-Request-Id"]) > 0 { @@ -60,11 +61,11 @@ func TestClustersCRUD(t *testing.T) { err = WaitForCluster(client, clusterID, "UPDATE_COMPLETE", time.Second*300) th.AssertNoErr(t, err) - newCluster, err := clusters.Get(client, clusterID).Extract() + newCluster, err := clusters.Get(context.TODO(), client, clusterID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.UUID, clusterID) - allPagesDetail, err := clusters.ListDetail(client, nil).AllPages() + allPagesDetail, err := clusters.ListDetail(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allClustersDetail, err := clusters.ExtractClusters(allPagesDetail) diff --git a/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index 68b6379e2f..91e72ce569 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -23,7 +24,7 @@ func TestClusterTemplatesCRUD(t *testing.T) { defer DeleteClusterTemplate(t, client, clusterTemplate.UUID) // Test clusters list - allPages, err := clustertemplates.List(client, nil).AllPages() + allPages, err := clustertemplates.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allClusterTemplates, err := clustertemplates.ExtractClusterTemplates(allPages) @@ -38,7 +39,7 @@ func TestClusterTemplatesCRUD(t *testing.T) { th.AssertEquals(t, found, true) - template, err := clustertemplates.Get(client, clusterTemplate.UUID).Extract() + template, err := clustertemplates.Get(context.TODO(), client, clusterTemplate.UUID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, clusterTemplate.UUID, template.UUID) @@ -61,7 +62,7 @@ func TestClusterTemplatesCRUD(t *testing.T) { }, } - updateClusterTemplate, err := clustertemplates.Update(client, clusterTemplate.UUID, updateOpts).Extract() + updateClusterTemplate, err := clustertemplates.Update(context.TODO(), client, clusterTemplate.UUID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, updateClusterTemplate.MasterLBEnabled) th.AssertEquals(t, false, updateClusterTemplate.RegistryEnabled) diff --git a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go index 2b461a2c5c..8b36a47263 100644 --- a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "fmt" "math" "strings" @@ -45,7 +46,7 @@ func CreateClusterTemplateCOE(t *testing.T, client *gophercloud.ServiceClient, c ServerType: "vm", } - res := clustertemplates.Create(client, createOpts) + res := clustertemplates.Create(context.TODO(), client, createOpts) if res.Err != nil { return nil, res.Err } @@ -89,7 +90,7 @@ func CreateKubernetesClusterTemplate(t *testing.T, client *gophercloud.ServiceCl func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete cluster-template: %s", id) - err := clustertemplates.Delete(client, id).ExtractErr() + err := clustertemplates.Delete(context.TODO(), client, id).ExtractErr() if err != nil { t.Fatalf("Error deleting cluster-template %s: %s:", id, err) } @@ -126,7 +127,7 @@ func CreateClusterTimeout(t *testing.T, client *gophercloud.ServiceClient, clust NodeCount: &nodeCount, } - createResult := clusters.Create(client, createOpts) + createResult := clusters.Create(context.TODO(), client, createOpts) th.AssertNoErr(t, createResult.Err) if len(createResult.Header["X-Openstack-Request-Id"]) > 0 { t.Logf("Cluster Create Request ID: %s", createResult.Header["X-Openstack-Request-Id"][0]) @@ -162,8 +163,8 @@ func CreateKubernetesCluster(t *testing.T, client *gophercloud.ServiceClient, cl func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete cluster: %s", id) - r := clusters.Delete(client, id) - err := clusters.Delete(client, id).ExtractErr() + r := clusters.Delete(context.TODO(), client, id) + err := clusters.Delete(context.TODO(), client, id).ExtractErr() deleteRequestID := "" idKey := "X-Openstack-Request-Id" if len(r.Header[idKey]) > 0 { @@ -185,7 +186,7 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status string, timeout time.Duration) error { return tools.WaitForTimeout(func() (bool, error) { - cluster, err := clusters.Get(client, clusterID).Extract() + cluster, err := clusters.Get(context.TODO(), client, clusterID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETE_COMPLETE" { return true, nil @@ -225,7 +226,7 @@ func CreateQuota(t *testing.T, client *gophercloud.ServiceClient) (*quotas.Quota HardLimit: 10, } - res := quotas.Create(client, createOpts) + res := quotas.Create(context.TODO(), client, createOpts) if res.Err != nil { return nil, res.Err } diff --git a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go index 665451e58f..9bb5ea15d0 100644 --- a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "fmt" "testing" "time" @@ -49,7 +50,7 @@ func TestNodeGroupsCRUD(t *testing.T) { // Wait for the node group to finish creating err = tools.WaitForTimeout(func() (bool, error) { - ng, err := nodegroups.Get(client, clusterID, nodeGroupID).Extract() + ng, err := nodegroups.Get(context.TODO(), client, clusterID, nodeGroupID).Extract() if err != nil { return false, fmt.Errorf("error waiting for node group to create: %v", err) } @@ -62,7 +63,7 @@ func TestNodeGroupsCRUD(t *testing.T) { } func testNodeGroupsList(t *testing.T, client *gophercloud.ServiceClient, clusterID string) { - allPages, err := nodegroups.List(client, clusterID, nil).AllPages() + allPages, err := nodegroups.List(client, clusterID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allNodeGroups, err := nodegroups.ExtractNodeGroups(allPages) @@ -76,7 +77,7 @@ func testNodeGroupGet(t *testing.T, client *gophercloud.ServiceClient, clusterID listOpts := nodegroups.ListOpts{ Role: "worker", } - allPages, err := nodegroups.List(client, clusterID, listOpts).AllPages() + allPages, err := nodegroups.List(client, clusterID, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allNodeGroups, err := nodegroups.ExtractNodeGroups(allPages) @@ -87,7 +88,7 @@ func testNodeGroupGet(t *testing.T, client *gophercloud.ServiceClient, clusterID ngID := allNodeGroups[0].UUID - ng, err := nodegroups.Get(client, clusterID, ngID).Extract() + ng, err := nodegroups.Get(context.TODO(), client, clusterID, ngID).Extract() th.AssertNoErr(t, err) // Should have got the same node group as from the list @@ -105,7 +106,7 @@ func testNodeGroupCreate(t *testing.T, client *gophercloud.ServiceClient, cluste NodeCount: &two, } - ng, err := nodegroups.Create(client, clusterID, createOpts).Extract() + ng, err := nodegroups.Create(context.TODO(), client, clusterID, createOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, name, ng.Name) @@ -123,7 +124,7 @@ func testNodeGroupUpdate(t *testing.T, client *gophercloud.ServiceClient, cluste Value: 2, }, } - ng, err := nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() + ng, err := nodegroups.Update(context.TODO(), client, clusterID, nodeGroupID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 2, ng.MinNodeCount) @@ -134,7 +135,7 @@ func testNodeGroupUpdate(t *testing.T, client *gophercloud.ServiceClient, cluste Value: 5, }, } - ng, err = nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() + ng, err = nodegroups.Update(context.TODO(), client, clusterID, nodeGroupID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, ng.MaxNodeCount == nil) th.AssertEquals(t, 5, *ng.MaxNodeCount) @@ -151,7 +152,7 @@ func testNodeGroupUpdate(t *testing.T, client *gophercloud.ServiceClient, cluste Value: 3, }, } - ng, err = nodegroups.Update(client, clusterID, nodeGroupID, updateOpts).Extract() + ng, err = nodegroups.Update(context.TODO(), client, clusterID, nodeGroupID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, ng.MaxNodeCount == nil) th.AssertEquals(t, 1, ng.MinNodeCount) @@ -159,12 +160,12 @@ func testNodeGroupUpdate(t *testing.T, client *gophercloud.ServiceClient, cluste } func testNodeGroupDelete(t *testing.T, client *gophercloud.ServiceClient, clusterID, nodeGroupID string) { - err := nodegroups.Delete(client, clusterID, nodeGroupID).ExtractErr() + err := nodegroups.Delete(context.TODO(), client, clusterID, nodeGroupID).ExtractErr() th.AssertNoErr(t, err) // Wait for the node group to be deleted err = tools.WaitFor(func() (bool, error) { - _, err := nodegroups.Get(client, clusterID, nodeGroupID).Extract() + _, err := nodegroups.Get(context.TODO(), client, clusterID, nodeGroupID).Extract() if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil } diff --git a/internal/acceptance/openstack/db/v1/databases_test.go b/internal/acceptance/openstack/db/v1/databases_test.go index 27690ae7b4..76cba92389 100644 --- a/internal/acceptance/openstack/db/v1/databases_test.go +++ b/internal/acceptance/openstack/db/v1/databases_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -37,7 +38,7 @@ func TestDatabases(t *testing.T) { } // List all databases. - allPages, err := databases.List(client, instance.ID).AllPages() + allPages, err := databases.List(client, instance.ID).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list databases: %v", err) } diff --git a/internal/acceptance/openstack/db/v1/db.go b/internal/acceptance/openstack/db/v1/db.go index 3a9e5c84e5..865b3fada0 100644 --- a/internal/acceptance/openstack/db/v1/db.go +++ b/internal/acceptance/openstack/db/v1/db.go @@ -3,6 +3,7 @@ package v1 import ( + "context" "fmt" "testing" @@ -26,7 +27,7 @@ func CreateDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID }, } - return databases.Create(client, instanceID, createOpts).ExtractErr() + return databases.Create(context.TODO(), client, instanceID, createOpts).ExtractErr() } // CreateInstance will create an instance with a randomly generated name. @@ -57,7 +58,7 @@ func CreateInstance(t *testing.T, client *gophercloud.ServiceClient) (*instances }, } - instance, err := instances.Create(client, createOpts).Extract() + instance, err := instances.Create(context.TODO(), client, createOpts).Extract() if err != nil { return instance, err } @@ -66,7 +67,7 @@ func CreateInstance(t *testing.T, client *gophercloud.ServiceClient) (*instances return instance, err } - return instances.Get(client, instance.ID).Extract() + return instances.Get(context.TODO(), client, instance.ID).Extract() } // CreateUser will create a user with a randomly generated name. @@ -83,14 +84,14 @@ func CreateUser(t *testing.T, client *gophercloud.ServiceClient, instanceID stri }, } - return users.Create(client, instanceID, createOpts).ExtractErr() + return users.Create(context.TODO(), client, instanceID, createOpts).ExtractErr() } // DeleteDatabase deletes a database. A fatal error will occur if the database // failed to delete. This works best when used as a deferred function. func DeleteDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID, name string) { t.Logf("Attempting to delete database: %s", name) - err := databases.Delete(client, instanceID, name).ExtractErr() + err := databases.Delete(context.TODO(), client, instanceID, name).ExtractErr() if err != nil { t.Fatalf("Unable to delete database %s: %s", name, err) } @@ -102,7 +103,7 @@ func DeleteDatabase(t *testing.T, client *gophercloud.ServiceClient, instanceID, // failed to delete. This works best when used as a deferred function. func DeleteInstance(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete instance: %s", id) - err := instances.Delete(client, id).ExtractErr() + err := instances.Delete(context.TODO(), client, id).ExtractErr() if err != nil { t.Fatalf("Unable to delete instance %s: %s", id, err) } @@ -114,7 +115,7 @@ func DeleteInstance(t *testing.T, client *gophercloud.ServiceClient, id string) // failed to delete. This works best when used as a deferred function. func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, instanceID, name string) { t.Logf("Attempting to delete user: %s", name) - err := users.Delete(client, instanceID, name).ExtractErr() + err := users.Delete(context.TODO(), client, instanceID, name).ExtractErr() if err != nil { t.Fatalf("Unable to delete users %s: %s", name, err) } @@ -127,7 +128,7 @@ func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, instanceID, nam func WaitForInstanceStatus( client *gophercloud.ServiceClient, instance *instances.Instance, status string) error { return tools.WaitFor(func() (bool, error) { - latest, err := instances.Get(client, instance.ID).Extract() + latest, err := instances.Get(context.TODO(), client, instance.ID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/db/v1/flavors_test.go b/internal/acceptance/openstack/db/v1/flavors_test.go index ea61629463..7268d5ebd4 100644 --- a/internal/acceptance/openstack/db/v1/flavors_test.go +++ b/internal/acceptance/openstack/db/v1/flavors_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestFlavorsList(t *testing.T) { t.Fatalf("Unable to create a DB client: %v", err) } - allPages, err := flavors.List(client).AllPages() + allPages, err := flavors.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve flavors: %v", err) } @@ -38,7 +39,7 @@ func TestFlavorsGet(t *testing.T) { t.Fatalf("Unable to create a DB client: %v", err) } - allPages, err := flavors.List(client).AllPages() + allPages, err := flavors.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve flavors: %v", err) } diff --git a/internal/acceptance/openstack/db/v1/instances_test.go b/internal/acceptance/openstack/db/v1/instances_test.go index 9ee652f63a..3098b23241 100644 --- a/internal/acceptance/openstack/db/v1/instances_test.go +++ b/internal/acceptance/openstack/db/v1/instances_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -32,7 +33,7 @@ func TestInstances(t *testing.T) { tools.PrintResource(t, &instance) // List all instances. - allPages, err := instances.List(client).AllPages() + allPages, err := instances.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list instances: %v", err) } diff --git a/internal/acceptance/openstack/db/v1/users_test.go b/internal/acceptance/openstack/db/v1/users_test.go index 1ddce5b203..4530d63180 100644 --- a/internal/acceptance/openstack/db/v1/users_test.go +++ b/internal/acceptance/openstack/db/v1/users_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -37,7 +38,7 @@ func TestUsers(t *testing.T) { } // List all users. - allPages, err := users.List(client, instance.ID).AllPages() + allPages, err := users.List(client, instance.ID).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list users: %v", err) } diff --git a/internal/acceptance/openstack/dns/v2/dns.go b/internal/acceptance/openstack/dns/v2/dns.go index 9e083676ed..5e34637faf 100644 --- a/internal/acceptance/openstack/dns/v2/dns.go +++ b/internal/acceptance/openstack/dns/v2/dns.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -25,7 +26,7 @@ func CreateRecordSet(t *testing.T, client *gophercloud.ServiceClient, zone *zone Records: []string{"10.1.0.2"}, } - rs, err := recordsets.Create(client, zone.ID, createOpts).Extract() + rs, err := recordsets.Create(context.TODO(), client, zone.ID, createOpts).Extract() if err != nil { return rs, err } @@ -34,7 +35,7 @@ func CreateRecordSet(t *testing.T, client *gophercloud.ServiceClient, zone *zone return rs, err } - newRS, err := recordsets.Get(client, rs.ZoneID, rs.ID).Extract() + newRS, err := recordsets.Get(context.TODO(), client, rs.ZoneID, rs.ID).Extract() if err != nil { return newRS, err } @@ -60,7 +61,7 @@ func CreateZone(t *testing.T, client *gophercloud.ServiceClient) (*zones.Zone, e Description: "Test zone", } - zone, err := zones.Create(client, createOpts).Extract() + zone, err := zones.Create(context.TODO(), client, createOpts).Extract() if err != nil { return zone, err } @@ -69,7 +70,7 @@ func CreateZone(t *testing.T, client *gophercloud.ServiceClient) (*zones.Zone, e return zone, err } - newZone, err := zones.Get(client, zone.ID).Extract() + newZone, err := zones.Get(context.TODO(), client, zone.ID).Extract() if err != nil { return zone, err } @@ -96,7 +97,7 @@ func CreateSecondaryZone(t *testing.T, client *gophercloud.ServiceClient) (*zone Masters: []string{"10.0.0.1"}, } - zone, err := zones.Create(client, createOpts).Extract() + zone, err := zones.Create(context.TODO(), client, createOpts).Extract() if err != nil { return zone, err } @@ -105,7 +106,7 @@ func CreateSecondaryZone(t *testing.T, client *gophercloud.ServiceClient) (*zone return zone, err } - newZone, err := zones.Get(client, zone.ID).Extract() + newZone, err := zones.Get(context.TODO(), client, zone.ID).Extract() if err != nil { return zone, err } @@ -128,7 +129,7 @@ func CreateTransferRequest(t *testing.T, client *gophercloud.ServiceClient, zone Description: "Test transfer request", } - transferRequest, err := transferRequests.Create(client, zone.ID, createOpts).Extract() + transferRequest, err := transferRequests.Create(context.TODO(), client, zone.ID, createOpts).Extract() if err != nil { return transferRequest, err } @@ -137,7 +138,7 @@ func CreateTransferRequest(t *testing.T, client *gophercloud.ServiceClient, zone return transferRequest, err } - newTransferRequest, err := transferRequests.Get(client, transferRequest.ID).Extract() + newTransferRequest, err := transferRequests.Get(context.TODO(), client, transferRequest.ID).Extract() if err != nil { return transferRequest, err } @@ -158,14 +159,14 @@ func CreateTransferAccept(t *testing.T, client *gophercloud.ServiceClient, zoneT ZoneTransferRequestID: zoneTransferRequestID, Key: key, } - transferAccept, err := transferAccepts.Create(client, createOpts).Extract() + transferAccept, err := transferAccepts.Create(context.TODO(), client, createOpts).Extract() if err != nil { return transferAccept, err } if err := WaitForTransferAcceptStatus(client, transferAccept, "COMPLETE"); err != nil { return transferAccept, err } - newTransferAccept, err := transferAccepts.Get(client, transferAccept.ID).Extract() + newTransferAccept, err := transferAccepts.Get(context.TODO(), client, transferAccept.ID).Extract() if err != nil { return transferAccept, err } @@ -178,7 +179,7 @@ func CreateTransferAccept(t *testing.T, client *gophercloud.ServiceClient, zoneT // the transfer request failed to be deleted. This works best when used as a deferred // function. func DeleteTransferRequest(t *testing.T, client *gophercloud.ServiceClient, tr *transferRequests.TransferRequest) { - err := transferRequests.Delete(client, tr.ID).ExtractErr() + err := transferRequests.Delete(context.TODO(), client, tr.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete zone transfer request %s: %v", tr.ID, err) } @@ -189,7 +190,7 @@ func DeleteTransferRequest(t *testing.T, client *gophercloud.ServiceClient, tr * // the record set failed to be deleted. This works best when used as a deferred // function. func DeleteRecordSet(t *testing.T, client *gophercloud.ServiceClient, rs *recordsets.RecordSet) { - err := recordsets.Delete(client, rs.ZoneID, rs.ID).ExtractErr() + err := recordsets.Delete(context.TODO(), client, rs.ZoneID, rs.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete record set %s: %v", rs.ID, err) } @@ -201,7 +202,7 @@ func DeleteRecordSet(t *testing.T, client *gophercloud.ServiceClient, rs *record // the zone failed to be deleted. This works best when used as a deferred // function. func DeleteZone(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zone) { - _, err := zones.Delete(client, zone.ID).Extract() + _, err := zones.Delete(context.TODO(), client, zone.ID).Extract() if err != nil { t.Fatalf("Unable to delete zone %s: %v", zone.ID, err) } @@ -213,7 +214,7 @@ func DeleteZone(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zon // the specified status or the status becomes ERROR. func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.RecordSet, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := recordsets.Get(client, rs.ZoneID, rs.ID).Extract() + current, err := recordsets.Get(context.TODO(), client, rs.ZoneID, rs.ID).Extract() if err != nil { return false, err } @@ -230,7 +231,7 @@ func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.Re // the specified status or the status becomes ERROR. func WaitForTransferRequestStatus(client *gophercloud.ServiceClient, tr *transferRequests.TransferRequest, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := transferRequests.Get(client, tr.ID).Extract() + current, err := transferRequests.Get(context.TODO(), client, tr.ID).Extract() if err != nil { return false, err } @@ -245,7 +246,7 @@ func WaitForTransferRequestStatus(client *gophercloud.ServiceClient, tr *transfe // the specified status or the status becomes ERROR. func WaitForTransferAcceptStatus(client *gophercloud.ServiceClient, ta *transferAccepts.TransferAccept, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := transferAccepts.Get(client, ta.ID).Extract() + current, err := transferAccepts.Get(context.TODO(), client, ta.ID).Extract() if err != nil { return false, err } @@ -260,7 +261,7 @@ func WaitForTransferAcceptStatus(client *gophercloud.ServiceClient, ta *transfer // the specified status or the status becomes ERROR. func WaitForZoneStatus(client *gophercloud.ServiceClient, zone *zones.Zone, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := zones.Get(client, zone.ID).Extract() + current, err := zones.Get(context.TODO(), client, zone.ID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/dns/v2/recordsets_test.go b/internal/acceptance/openstack/dns/v2/recordsets_test.go index e5a705e3c7..e97719f3c3 100644 --- a/internal/acceptance/openstack/dns/v2/recordsets_test.go +++ b/internal/acceptance/openstack/dns/v2/recordsets_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -21,7 +22,7 @@ func TestRecordSetsListByZone(t *testing.T) { th.AssertNoErr(t, err) defer DeleteZone(t, client, zone) - allPages, err := recordsets.ListByZone(client, zone.ID, nil).AllPages() + allPages, err := recordsets.ListByZone(client, zone.ID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allRecordSets, err := recordsets.ExtractRecordSets(allPages) diff --git a/internal/acceptance/openstack/dns/v2/transfers_test.go b/internal/acceptance/openstack/dns/v2/transfers_test.go index 2282cf64b0..0930355aa6 100644 --- a/internal/acceptance/openstack/dns/v2/transfers_test.go +++ b/internal/acceptance/openstack/dns/v2/transfers_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -28,7 +29,7 @@ func TestTransferRequestCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteTransferRequest(t, client, transferRequest) - allTransferRequestsPages, err := transferRequests.List(client, nil).AllPages() + allTransferRequestsPages, err := transferRequests.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allTransferRequests, err := transferRequests.ExtractTransferRequests(allTransferRequestsPages) @@ -78,7 +79,7 @@ func TestTransferRequestAccept(t *testing.T) { // Accept Zone Transfer Request transferAccept, err := CreateTransferAccept(t, client, transferRequest.ID, transferRequest.Key) - allTransferAcceptsPages, err := transferAccepts.List(client, nil).AllPages() + allTransferAcceptsPages, err := transferAccepts.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allTransferAcceptsPages) diff --git a/internal/acceptance/openstack/dns/v2/zones_test.go b/internal/acceptance/openstack/dns/v2/zones_test.go index 5806fb7c3c..c8b1d09864 100644 --- a/internal/acceptance/openstack/dns/v2/zones_test.go +++ b/internal/acceptance/openstack/dns/v2/zones_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestZonesCRUD(t *testing.T) { tools.PrintResource(t, &zone) - allPages, err := zones.List(client, nil).AllPages() + allPages, err := zones.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allZones, err := zones.ExtractZones(allPages) diff --git a/internal/acceptance/openstack/identity/v2/extension_test.go b/internal/acceptance/openstack/identity/v2/extension_test.go index 7169e51e04..1b3538a12b 100644 --- a/internal/acceptance/openstack/identity/v2/extension_test.go +++ b/internal/acceptance/openstack/identity/v2/extension_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestExtensionsList(t *testing.T) { client, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) - allPages, err := extensions.List(client).AllPages() + allPages, err := extensions.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allExtensions, err := extensions.ExtractExtensions(allPages) @@ -43,7 +44,7 @@ func TestExtensionsGet(t *testing.T) { client, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "OS-KSCRUD").Extract() + extension, err := extensions.Get(context.TODO(), client, "OS-KSCRUD").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, extension) diff --git a/internal/acceptance/openstack/identity/v2/identity.go b/internal/acceptance/openstack/identity/v2/identity.go index 3500d06776..89e946fc3f 100644 --- a/internal/acceptance/openstack/identity/v2/identity.go +++ b/internal/acceptance/openstack/identity/v2/identity.go @@ -3,6 +3,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -18,7 +19,7 @@ import ( func AddUserRole(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants.Tenant, user *users.User, role *roles.Role) error { t.Logf("Attempting to grant user %s role %s in tenant %s", user.ID, role.ID, tenant.ID) - err := roles.AddUser(client, tenant.ID, user.ID, role.ID).ExtractErr() + err := roles.AddUser(context.TODO(), client, tenant.ID, user.ID, role.ID).ExtractErr() if err != nil { return err } @@ -47,7 +48,7 @@ func CreateTenant(t *testing.T, client *gophercloud.ServiceClient, c *tenants.Cr createOpts.Name = name createOpts.Description = description - tenant, err := tenants.Create(client, createOpts).Extract() + tenant, err := tenants.Create(context.TODO(), client, createOpts).Extract() if err != nil { return tenant, err } @@ -74,7 +75,7 @@ func CreateUser(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants Email: userEmail, } - user, err := users.Create(client, createOpts).Extract() + user, err := users.Create(context.TODO(), client, createOpts).Extract() if err != nil { return user, err } @@ -88,7 +89,7 @@ func CreateUser(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants // the tenant ID failed to be deleted. This works best when using it as // a deferred function. func DeleteTenant(t *testing.T, client *gophercloud.ServiceClient, tenantID string) { - err := tenants.Delete(client, tenantID).ExtractErr() + err := tenants.Delete(context.TODO(), client, tenantID).ExtractErr() if err != nil { t.Fatalf("Unable to delete tenant %s: %v", tenantID, err) } @@ -101,7 +102,7 @@ func DeleteTenant(t *testing.T, client *gophercloud.ServiceClient, tenantID stri func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, user *users.User) { t.Logf("Attempting to delete user: %s", user.Name) - result := users.Delete(client, user.ID) + result := users.Delete(context.TODO(), client, user.ID) if result.Err != nil { t.Fatalf("Unable to delete user") } @@ -115,7 +116,7 @@ func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, user *users.Use func DeleteUserRole(t *testing.T, client *gophercloud.ServiceClient, tenant *tenants.Tenant, user *users.User, role *roles.Role) { t.Logf("Attempting to remove role %s from user %s in tenant %s", role.ID, user.ID, tenant.ID) - err := roles.DeleteUser(client, tenant.ID, user.ID, role.ID).ExtractErr() + err := roles.DeleteUser(context.TODO(), client, tenant.ID, user.ID, role.ID).ExtractErr() if err != nil { t.Fatalf("Unable to remove role") } @@ -129,7 +130,7 @@ func DeleteUserRole(t *testing.T, client *gophercloud.ServiceClient, tenant *ten func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, error) { var role *roles.Role - allPages, err := roles.List(client).AllPages() + allPages, err := roles.List(client).AllPages(context.TODO()) if err != nil { return role, err } @@ -153,7 +154,7 @@ func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, err func FindTenant(t *testing.T, client *gophercloud.ServiceClient) (*tenants.Tenant, error) { var tenant *tenants.Tenant - allPages, err := tenants.List(client, nil).AllPages() + allPages, err := tenants.List(client, nil).AllPages(context.TODO()) if err != nil { return tenant, err } @@ -184,7 +185,7 @@ func UpdateUser(t *testing.T, client *gophercloud.ServiceClient, user *users.Use Email: userEmail, } - newUser, err := users.Update(client, user.ID, updateOpts).Extract() + newUser, err := users.Update(context.TODO(), client, user.ID, updateOpts).Extract() if err != nil { return newUser, err } diff --git a/internal/acceptance/openstack/identity/v2/role_test.go b/internal/acceptance/openstack/identity/v2/role_test.go index bc47672b20..667c3addff 100644 --- a/internal/acceptance/openstack/identity/v2/role_test.go +++ b/internal/acceptance/openstack/identity/v2/role_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -34,7 +35,7 @@ func TestRolesAddToUser(t *testing.T) { th.AssertNoErr(t, err) defer DeleteUserRole(t, client, tenant, user, role) - allPages, err := users.ListRoles(client, tenant.ID, user.ID).AllPages() + allPages, err := users.ListRoles(client, tenant.ID, user.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := users.ExtractRoles(allPages) @@ -59,7 +60,7 @@ func TestRolesList(t *testing.T) { client, err := clients.NewIdentityV2AdminClient() th.AssertNoErr(t, err) - allPages, err := roles.List(client).AllPages() + allPages, err := roles.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) diff --git a/internal/acceptance/openstack/identity/v2/tenant_test.go b/internal/acceptance/openstack/identity/v2/tenant_test.go index 697755b2ac..b8f35474d0 100644 --- a/internal/acceptance/openstack/identity/v2/tenant_test.go +++ b/internal/acceptance/openstack/identity/v2/tenant_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestTenantsList(t *testing.T) { client, err := clients.NewIdentityV2Client() th.AssertNoErr(t, err) - allPages, err := tenants.List(client, nil).AllPages() + allPages, err := tenants.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allTenants, err := tenants.ExtractTenants(allPages) @@ -48,7 +49,7 @@ func TestTenantsCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteTenant(t, client, tenant.ID) - tenant, err = tenants.Get(client, tenant.ID).Extract() + tenant, err = tenants.Get(context.TODO(), client, tenant.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, tenant) @@ -58,7 +59,7 @@ func TestTenantsCRUD(t *testing.T) { Description: &description, } - newTenant, err := tenants.Update(client, tenant.ID, updateOpts).Extract() + newTenant, err := tenants.Update(context.TODO(), client, tenant.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newTenant) diff --git a/internal/acceptance/openstack/identity/v2/token_test.go b/internal/acceptance/openstack/identity/v2/token_test.go index c858546588..9b3234d33c 100644 --- a/internal/acceptance/openstack/identity/v2/token_test.go +++ b/internal/acceptance/openstack/identity/v2/token_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -23,7 +24,7 @@ func TestTokenAuthenticate(t *testing.T) { authOptions, err := openstack.AuthOptionsFromEnv() th.AssertNoErr(t, err) - result := tokens.Create(client, authOptions) + result := tokens.Create(context.TODO(), client, authOptions) token, err := result.ExtractToken() th.AssertNoErr(t, err) @@ -47,13 +48,13 @@ func TestTokenValidate(t *testing.T) { authOptions, err := openstack.AuthOptionsFromEnv() th.AssertNoErr(t, err) - result := tokens.Create(client, authOptions) + result := tokens.Create(context.TODO(), client, authOptions) token, err := result.ExtractToken() th.AssertNoErr(t, err) tools.PrintResource(t, token) - getResult := tokens.Get(client, token.ID) + getResult := tokens.Get(context.TODO(), client, token.ID) user, err := getResult.ExtractUser() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/identity/v2/user_test.go b/internal/acceptance/openstack/identity/v2/user_test.go index 549b6611e3..1fd8c2bed4 100644 --- a/internal/acceptance/openstack/identity/v2/user_test.go +++ b/internal/acceptance/openstack/identity/v2/user_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestUsersList(t *testing.T) { client, err := clients.NewIdentityV2AdminClient() th.AssertNoErr(t, err) - allPages, err := users.List(client).AllPages() + allPages, err := users.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) diff --git a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go index 1ed0a3beeb..0f1f754529 100644 --- a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "time" @@ -46,19 +47,19 @@ func TestApplicationCredentialsCRD(t *testing.T) { }, } - token, err := tokens.Create(client, &authOptions).Extract() + token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - roles, err := tokens.Get(client, token.ID).ExtractRoles() + roles, err := tokens.Get(context.TODO(), client, token.ID).ExtractRoles() th.AssertNoErr(t, err) tools.PrintResource(t, roles) - project, err := tokens.Get(client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) @@ -85,9 +86,9 @@ func TestApplicationCredentialsCRD(t *testing.T) { ExpiresAt: &expiresAt, } - applicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() + applicationCredential, err := applicationcredentials.Create(context.TODO(), client, user.ID, createOpts).Extract() th.AssertNoErr(t, err) - defer applicationcredentials.Delete(client, user.ID, applicationCredential.ID) + defer applicationcredentials.Delete(context.TODO(), client, user.ID, applicationCredential.ID) tools.PrintResource(t, applicationCredential) if applicationCredential.Secret == "" { @@ -113,7 +114,7 @@ func TestApplicationCredentialsCRD(t *testing.T) { } // Get an application credential - getApplicationCredential, err := applicationcredentials.Get(client, user.ID, applicationCredential.ID).Extract() + getApplicationCredential, err := applicationcredentials.Get(context.TODO(), client, user.ID, applicationCredential.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getApplicationCredential) @@ -147,9 +148,9 @@ func TestApplicationCredentialsCRD(t *testing.T) { Secret: "myprecious", } - newApplicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() + newApplicationCredential, err := applicationcredentials.Create(context.TODO(), client, user.ID, createOpts).Extract() th.AssertNoErr(t, err) - defer applicationcredentials.Delete(client, user.ID, newApplicationCredential.ID) + defer applicationcredentials.Delete(context.TODO(), client, user.ID, newApplicationCredential.ID) tools.PrintResource(t, newApplicationCredential) th.AssertEquals(t, newApplicationCredential.ExpiresAt, time.Time{}) @@ -190,11 +191,11 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { }, } - token, err := tokens.Create(client, &authOptions).Extract() + token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) @@ -223,9 +224,9 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { ExpiresAt: &expiresAt, } - applicationCredential, err := applicationcredentials.Create(client, user.ID, createOpts).Extract() + applicationCredential, err := applicationcredentials.Create(context.TODO(), client, user.ID, createOpts).Extract() th.AssertNoErr(t, err) - defer applicationcredentials.Delete(client, user.ID, applicationCredential.ID) + defer applicationcredentials.Delete(context.TODO(), client, user.ID, applicationCredential.ID) tools.PrintResource(t, applicationCredential) if applicationCredential.Secret == "" { @@ -244,7 +245,7 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { } // Get an application credential - getApplicationCredential, err := applicationcredentials.Get(client, user.ID, applicationCredential.ID).Extract() + getApplicationCredential, err := applicationcredentials.Get(context.TODO(), client, user.ID, applicationCredential.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getApplicationCredential) @@ -264,7 +265,7 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { } // test list - allPages, err := applicationcredentials.ListAccessRules(client, user.ID).AllPages() + allPages, err := applicationcredentials.ListAccessRules(client, user.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := applicationcredentials.ExtractAccessRules(allPages) th.AssertNoErr(t, err) @@ -272,21 +273,21 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { // test individual get for i, rule := range actual { - getRule, err := applicationcredentials.GetAccessRule(client, user.ID, rule.ID).Extract() + getRule, err := applicationcredentials.GetAccessRule(context.TODO(), client, user.ID, rule.ID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, actual[i], *getRule) } - res := applicationcredentials.Delete(client, user.ID, applicationCredential.ID) + res := applicationcredentials.Delete(context.TODO(), client, user.ID, applicationCredential.ID) th.AssertNoErr(t, res.Err) // test delete for _, rule := range actual { - res := applicationcredentials.DeleteAccessRule(client, user.ID, rule.ID) + res := applicationcredentials.DeleteAccessRule(context.TODO(), client, user.ID, rule.ID) th.AssertNoErr(t, res.Err) } - allPages, err = applicationcredentials.ListAccessRules(client, user.ID).AllPages() + allPages, err = applicationcredentials.ListAccessRules(client, user.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err = applicationcredentials.ExtractAccessRules(allPages) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/identity/v3/catalog_test.go b/internal/acceptance/openstack/identity/v3/catalog_test.go index c263bad8a4..6d15b1054b 100644 --- a/internal/acceptance/openstack/identity/v3/catalog_test.go +++ b/internal/acceptance/openstack/identity/v3/catalog_test.go @@ -1,6 +1,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -13,7 +14,7 @@ func TestCatalogList(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := catalog.List(client).AllPages() + allPages, err := catalog.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allEntities, err := catalog.ExtractServiceCatalog(allPages) diff --git a/internal/acceptance/openstack/identity/v3/credentials_test.go b/internal/acceptance/openstack/identity/v3/credentials_test.go index 5a0fc9f1a0..00bc2052e8 100644 --- a/internal/acceptance/openstack/identity/v3/credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/credentials_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -35,15 +36,15 @@ func TestCredentialsCRUD(t *testing.T) { DomainName: ao.DomainName, }, } - token, err := tokens.Create(client, &authOptions).Extract() + token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - project, err := tokens.Get(client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) @@ -55,11 +56,11 @@ func TestCredentialsCRUD(t *testing.T) { } // Create a credential - credential, err := credentials.Create(client, createOpts).Extract() + credential, err := credentials.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) // Delete a credential - defer credentials.Delete(client, credential.ID) + defer credentials.Delete(context.TODO(), client, credential.ID) tools.PrintResource(t, credential) th.AssertEquals(t, credential.Blob, createOpts.Blob) @@ -68,7 +69,7 @@ func TestCredentialsCRUD(t *testing.T) { th.AssertEquals(t, credential.ProjectID, createOpts.ProjectID) // Get a credential - getCredential, err := credentials.Get(client, credential.ID).Extract() + getCredential, err := credentials.Get(context.TODO(), client, credential.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getCredential) @@ -85,7 +86,7 @@ func TestCredentialsCRUD(t *testing.T) { } // Update a credential - updateCredential, err := credentials.Update(client, credential.ID, updateOpts).Extract() + updateCredential, err := credentials.Update(context.TODO(), client, credential.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updateCredential) @@ -112,15 +113,15 @@ func TestCredentialsValidateS3(t *testing.T) { DomainName: ao.DomainName, }, } - token, err := tokens.Create(client, &authOptions).Extract() + token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - project, err := tokens.Get(client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) @@ -132,11 +133,11 @@ func TestCredentialsValidateS3(t *testing.T) { } // Create a credential - credential, err := credentials.Create(client, createOpts).Extract() + credential, err := credentials.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) // Delete a credential - defer credentials.Delete(client, credential.ID) + defer credentials.Delete(context.TODO(), client, credential.ID) tools.PrintResource(t, credential) th.AssertEquals(t, credential.Blob, createOpts.Blob) @@ -152,7 +153,7 @@ func TestCredentialsValidateS3(t *testing.T) { } // Validate a credential - token, err = ec2tokens.ValidateS3Token(client, &opts).Extract() + token, err = ec2tokens.ValidateS3Token(context.TODO(), client, &opts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) } diff --git a/internal/acceptance/openstack/identity/v3/domains_test.go b/internal/acceptance/openstack/identity/v3/domains_test.go index ee09dcad3e..c0864fffe1 100644 --- a/internal/acceptance/openstack/identity/v3/domains_test.go +++ b/internal/acceptance/openstack/identity/v3/domains_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestDomainsListAvailable(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := domains.ListAvailable(client).AllPages() + allPages, err := domains.ListAvailable(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allDomains, err := domains.ExtractDomains(allPages) @@ -40,7 +41,7 @@ func TestDomainsList(t *testing.T) { Enabled: &iTrue, } - allPages, err := domains.List(client, listOpts).AllPages() + allPages, err := domains.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allDomains, err := domains.ExtractDomains(allPages) @@ -64,7 +65,7 @@ func TestDomainsGet(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - p, err := domains.Get(client, "default").Extract() + p, err := domains.Get(context.TODO(), client, "default").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) @@ -100,7 +101,7 @@ func TestDomainsCRUD(t *testing.T) { Enabled: &iFalse, } - newDomain, err := domains.Update(client, domain.ID, updateOpts).Extract() + newDomain, err := domains.Update(context.TODO(), client, domain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newDomain) diff --git a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go index f28e487035..69c3877374 100644 --- a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -35,7 +36,7 @@ func TestEC2CredentialsCRD(t *testing.T) { }, } - res := tokens.Create(client, &authOptions) + res := tokens.Create(context.TODO(), client, &authOptions) th.AssertNoErr(t, res.Err) token, err := res.Extract() th.AssertNoErr(t, err) @@ -53,9 +54,9 @@ func TestEC2CredentialsCRD(t *testing.T) { TenantID: project.ID, } - ec2credential, err := ec2credentials.Create(client, user.ID, createOpts).Extract() + ec2credential, err := ec2credentials.Create(context.TODO(), client, user.ID, createOpts).Extract() th.AssertNoErr(t, err) - defer ec2credentials.Delete(client, user.ID, ec2credential.Access) + defer ec2credentials.Delete(context.TODO(), client, user.ID, ec2credential.Access) tools.PrintResource(t, ec2credential) access := ec2credential.Access @@ -72,7 +73,7 @@ func TestEC2CredentialsCRD(t *testing.T) { th.AssertEquals(t, ec2credential.TenantID, project.ID) // Get an ec2 credential - getEC2Credential, err := ec2credentials.Get(client, user.ID, ec2credential.Access).Extract() + getEC2Credential, err := ec2credentials.Get(context.TODO(), client, user.ID, ec2credential.Access).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getEC2Credential) @@ -81,7 +82,7 @@ func TestEC2CredentialsCRD(t *testing.T) { th.AssertEquals(t, getEC2Credential.Access, access) th.AssertEquals(t, getEC2Credential.Secret, secret) - allPages, err := ec2credentials.List(client, user.ID).AllPages() + allPages, err := ec2credentials.List(client, user.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) credentials, err := ec2credentials.ExtractCredentials(allPages) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go index c32c62cba2..7e1e37b5d0 100644 --- a/internal/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "strings" "testing" @@ -21,7 +22,7 @@ func TestEndpointsList(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := endpoints.List(client, nil).AllPages() + allPages, err := endpoints.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allEndpoints, err := endpoints.ExtractEndpoints(allPages) @@ -50,7 +51,7 @@ func TestEndpointsNavigateCatalog(t *testing.T) { ServiceType: "compute", } - allPages, err := services.List(client, serviceListOpts).AllPages() + allPages, err := services.List(client, serviceListOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) @@ -67,7 +68,7 @@ func TestEndpointsNavigateCatalog(t *testing.T) { ServiceID: computeService.ID, } - allPages, err = endpoints.List(client, endpointListOpts).AllPages() + allPages, err = endpoints.List(client, endpointListOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allEndpoints, err := endpoints.ExtractEndpoints(allPages) diff --git a/internal/acceptance/openstack/identity/v3/federation_test.go b/internal/acceptance/openstack/identity/v3/federation_test.go index 19aa7d169a..cc964c20c0 100644 --- a/internal/acceptance/openstack/identity/v3/federation_test.go +++ b/internal/acceptance/openstack/identity/v3/federation_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -17,7 +18,7 @@ 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(context.TODO()) th.AssertNoErr(t, err) mappings, err := federation.ExtractMappings(allPages) @@ -65,12 +66,12 @@ func TestMappingsCRUD(t *testing.T) { }, } - createdMapping, err := federation.CreateMapping(client, mappingName, createOpts).Extract() + createdMapping, err := federation.CreateMapping(context.TODO(), client, mappingName, createOpts).Extract() th.AssertNoErr(t, err) 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() + mapping, err := federation.GetMapping(context.TODO(), 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]) @@ -106,15 +107,15 @@ func TestMappingsCRUD(t *testing.T) { }, } - updatedMapping, err := federation.UpdateMapping(client, mappingName, updateOpts).Extract() + updatedMapping, err := federation.UpdateMapping(context.TODO(), 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]) - err = federation.DeleteMapping(client, mappingName).ExtractErr() + err = federation.DeleteMapping(context.TODO(), client, mappingName).ExtractErr() th.AssertNoErr(t, err) - resp := federation.GetMapping(client, mappingName) + resp := federation.GetMapping(context.TODO(), client, mappingName) th.AssertErr(t, resp.Err) _, ok := resp.Err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, ok) diff --git a/internal/acceptance/openstack/identity/v3/groups_test.go b/internal/acceptance/openstack/identity/v3/groups_test.go index 30bec02620..3fc21a946c 100644 --- a/internal/acceptance/openstack/identity/v3/groups_test.go +++ b/internal/acceptance/openstack/identity/v3/groups_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -48,7 +49,7 @@ func TestGroupCRUD(t *testing.T) { }, } - newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() + newGroup, err := groups.Update(context.TODO(), client, group.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) @@ -62,7 +63,7 @@ func TestGroupCRUD(t *testing.T) { } // List all Groups in default domain - allPages, err := groups.List(client, listOpts).AllPages() + allPages, err := groups.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allGroups, err := groups.ExtractGroups(allPages) @@ -89,7 +90,7 @@ func TestGroupCRUD(t *testing.T) { "name__contains": "TEST", } - allPages, err = groups.List(client, listOpts).AllPages() + allPages, err = groups.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allGroups, err = groups.ExtractGroups(allPages) @@ -111,7 +112,7 @@ func TestGroupCRUD(t *testing.T) { "name__contains": "foo", } - allPages, err = groups.List(client, listOpts).AllPages() + allPages, err = groups.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allGroups, err = groups.ExtractGroups(allPages) @@ -130,7 +131,7 @@ func TestGroupCRUD(t *testing.T) { th.AssertEquals(t, found, false) // Get the recently created group by ID - p, err := groups.Get(client, group.ID).Extract() + p, err := groups.Get(context.TODO(), client, group.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) diff --git a/internal/acceptance/openstack/identity/v3/identity.go b/internal/acceptance/openstack/identity/v3/identity.go index 78b1f87664..bd5d2470ef 100644 --- a/internal/acceptance/openstack/identity/v3/identity.go +++ b/internal/acceptance/openstack/identity/v3/identity.go @@ -1,6 +1,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -35,7 +36,7 @@ func CreateProject(t *testing.T, client *gophercloud.ServiceClient, c *projects. createOpts.Name = name createOpts.Description = description - project, err := projects.Create(client, createOpts).Extract() + project, err := projects.Create(context.TODO(), client, createOpts).Extract() if err != nil { return project, err } @@ -65,7 +66,7 @@ func CreateUser(t *testing.T, client *gophercloud.ServiceClient, c *users.Create createOpts.Name = name - user, err := users.Create(client, createOpts).Extract() + user, err := users.Create(context.TODO(), client, createOpts).Extract() if err != nil { return user, err } @@ -94,7 +95,7 @@ func CreateGroup(t *testing.T, client *gophercloud.ServiceClient, c *groups.Crea createOpts.Name = name - group, err := groups.Create(client, createOpts).Extract() + group, err := groups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return group, err } @@ -123,7 +124,7 @@ func CreateDomain(t *testing.T, client *gophercloud.ServiceClient, c *domains.Cr createOpts.Name = name - domain, err := domains.Create(client, createOpts).Extract() + domain, err := domains.Create(context.TODO(), client, createOpts).Extract() if err != nil { return domain, err } @@ -157,7 +158,7 @@ func CreateRole(t *testing.T, client *gophercloud.ServiceClient, c *roles.Create } createOpts.Name = name - role, err := roles.Create(client, createOpts).Extract() + role, err := roles.Create(context.TODO(), client, createOpts).Extract() if err != nil { return role, err } @@ -186,7 +187,7 @@ func CreateRegion(t *testing.T, client *gophercloud.ServiceClient, c *regions.Cr createOpts.ID = id - region, err := regions.Create(client, createOpts).Extract() + region, err := regions.Create(context.TODO(), client, createOpts).Extract() if err != nil { return region, err } @@ -215,7 +216,7 @@ func CreateService(t *testing.T, client *gophercloud.ServiceClient, c *services. createOpts.Extra["name"] = name - service, err := services.Create(client, createOpts).Extract() + service, err := services.Create(context.TODO(), client, createOpts).Extract() if err != nil { return service, err } @@ -231,7 +232,7 @@ func CreateService(t *testing.T, client *gophercloud.ServiceClient, c *services. // the project ID failed to be deleted. This works best when using it as // a deferred function. func DeleteProject(t *testing.T, client *gophercloud.ServiceClient, projectID string) { - err := projects.Delete(client, projectID).ExtractErr() + err := projects.Delete(context.TODO(), client, projectID).ExtractErr() if err != nil { t.Fatalf("Unable to delete project %s: %v", projectID, err) } @@ -243,7 +244,7 @@ func DeleteProject(t *testing.T, client *gophercloud.ServiceClient, projectID st // the user failed to be deleted. This works best when using it as // a deferred function. func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) { - err := users.Delete(client, userID).ExtractErr() + err := users.Delete(context.TODO(), client, userID).ExtractErr() if err != nil { t.Fatalf("Unable to delete user with ID %s: %v", userID, err) } @@ -255,7 +256,7 @@ func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) // the group failed to be deleted. This works best when using it as // a deferred function. func DeleteGroup(t *testing.T, client *gophercloud.ServiceClient, groupID string) { - err := groups.Delete(client, groupID).ExtractErr() + err := groups.Delete(context.TODO(), client, groupID).ExtractErr() if err != nil { t.Fatalf("Unable to delete group %s: %v", groupID, err) } @@ -267,7 +268,7 @@ func DeleteGroup(t *testing.T, client *gophercloud.ServiceClient, groupID string // the project ID failed to be deleted. This works best when using it as // a deferred function. func DeleteDomain(t *testing.T, client *gophercloud.ServiceClient, domainID string) { - err := domains.Delete(client, domainID).ExtractErr() + err := domains.Delete(context.TODO(), client, domainID).ExtractErr() if err != nil { t.Fatalf("Unable to delete domain %s: %v", domainID, err) } @@ -279,7 +280,7 @@ func DeleteDomain(t *testing.T, client *gophercloud.ServiceClient, domainID stri // the role failed to be deleted. This works best when using it as // a deferred function. func DeleteRole(t *testing.T, client *gophercloud.ServiceClient, roleID string) { - err := roles.Delete(client, roleID).ExtractErr() + err := roles.Delete(context.TODO(), client, roleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete role %s: %v", roleID, err) } @@ -291,7 +292,7 @@ func DeleteRole(t *testing.T, client *gophercloud.ServiceClient, roleID string) // the region failed to be deleted. This works best when using it as // a deferred function. func DeleteRegion(t *testing.T, client *gophercloud.ServiceClient, regionID string) { - err := regions.Delete(client, regionID).ExtractErr() + err := regions.Delete(context.TODO(), client, regionID).ExtractErr() if err != nil { t.Fatalf("Unable to delete region %s: %v", regionID, err) } @@ -303,7 +304,7 @@ func DeleteRegion(t *testing.T, client *gophercloud.ServiceClient, regionID stri // the service failed to be deleted. This works best when using it as // a deferred function. func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID string) { - err := services.Delete(client, serviceID).ExtractErr() + err := services.Delete(context.TODO(), client, serviceID).ExtractErr() if err != nil { t.Fatalf("Unable to delete service %s: %v", serviceID, err) } @@ -315,7 +316,7 @@ func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID st // A fatal error will occur if it fails to delete the assignment. // This works best when using it as a deferred function. func UnassignRole(t *testing.T, client *gophercloud.ServiceClient, roleID string, opts *roles.UnassignOpts) { - err := roles.Unassign(client, roleID, *opts).ExtractErr() + err := roles.Unassign(context.TODO(), client, roleID, *opts).ExtractErr() if err != nil { t.Fatalf("Unable to unassign a role %v on context %+v: %v", roleID, *opts, err) } @@ -329,7 +330,7 @@ func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, err t.Log("Attempting to find a role") var role *roles.Role - allPages, err := roles.List(client, nil).AllPages() + allPages, err := roles.List(client, nil).AllPages(context.TODO()) if err != nil { return nil, err } @@ -352,7 +353,7 @@ func FindRole(t *testing.T, client *gophercloud.ServiceClient) (*roles.Role, err // CreateTrust will create a trust with the provided options. // An error will be returned if the trust was unable to be created. func CreateTrust(t *testing.T, client *gophercloud.ServiceClient, createOpts trusts.CreateOpts) (*trusts.Trust, error) { - trust, err := trusts.Create(client, createOpts).Extract() + trust, err := trusts.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -366,7 +367,7 @@ func CreateTrust(t *testing.T, client *gophercloud.ServiceClient, createOpts tru // the trust failed to be deleted. This works best when using it as // a deferred function. func DeleteTrust(t *testing.T, client *gophercloud.ServiceClient, trustID string) { - err := trusts.Delete(client, trustID).ExtractErr() + err := trusts.Delete(context.TODO(), client, trustID).ExtractErr() if err != nil { t.Fatalf("Unable to delete trust %s: %v", trustID, err) } @@ -381,7 +382,7 @@ func FindTrust(t *testing.T, client *gophercloud.ServiceClient) (*trusts.Trust, t.Log("Attempting to find a trust") var trust *trusts.Trust - allPages, err := trusts.List(client, nil).AllPages() + allPages, err := trusts.List(client, nil).AllPages(context.TODO()) if err != nil { return nil, err } diff --git a/internal/acceptance/openstack/identity/v3/limits_test.go b/internal/acceptance/openstack/identity/v3/limits_test.go index 7dfa9c22aa..8b2f42f642 100644 --- a/internal/acceptance/openstack/identity/v3/limits_test.go +++ b/internal/acceptance/openstack/identity/v3/limits_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "os" "testing" @@ -21,7 +22,7 @@ func TestGetEnforcementModel(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - model, err := limits.GetEnforcementModel(client).Extract() + model, err := limits.GetEnforcementModel(context.TODO(), client).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, model) @@ -35,7 +36,7 @@ func TestLimitsList(t *testing.T) { listOpts := limits.ListOpts{} - allPages, err := limits.List(client, listOpts).AllPages() + allPages, err := limits.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) _, err = limits.ExtractLimits(allPages) @@ -56,7 +57,7 @@ func TestLimitsCRUD(t *testing.T) { th.AssertNoErr(t, err) // Get the service to register the limit against. - allPages, err := services.List(client, nil).AllPages() + allPages, err := services.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) svList, err := services.ExtractServices(allPages) @@ -82,7 +83,7 @@ func TestLimitsCRUD(t *testing.T) { }, } - createdRegisteredLimits, err := registeredlimits.BatchCreate(client, createRegisteredLimitsOpts).Extract() + createdRegisteredLimits, err := registeredlimits.BatchCreate(context.TODO(), client, createRegisteredLimitsOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, createdRegisteredLimits[0]) th.AssertIntGreaterOrEqual(t, 1, len(createdRegisteredLimits)) @@ -102,7 +103,7 @@ func TestLimitsCRUD(t *testing.T) { }, } - createdLimits, err := limits.BatchCreate(client, createOpts).Extract() + createdLimits, err := limits.BatchCreate(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) th.AssertIntGreaterOrEqual(t, 1, len(createdLimits)) th.AssertEquals(t, limitDescription, createdLimits[0].Description) @@ -113,7 +114,7 @@ func TestLimitsCRUD(t *testing.T) { limitID := createdLimits[0].ID - limit, err := limits.Get(client, limitID).Extract() + limit, err := limits.Get(context.TODO(), client, limitID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, createdLimits[0], *limit) @@ -124,23 +125,23 @@ func TestLimitsCRUD(t *testing.T) { ResourceLimit: &newResourceLimit, } - updatedLimit, err := limits.Update(client, limitID, updateOpts).Extract() + updatedLimit, err := limits.Update(context.TODO(), client, limitID, updateOpts).Extract() th.AssertNoErr(t, err) 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() + del_err := registeredlimits.Delete(context.TODO(), client, createdRegisteredLimits[0].ID).ExtractErr() th.AssertErr(t, del_err) // Delete project specific limit - err = limits.Delete(client, limitID).ExtractErr() + err = limits.Delete(context.TODO(), client, limitID).ExtractErr() th.AssertNoErr(t, err) - _, err = limits.Get(client, limitID).Extract() + _, err = limits.Get(context.TODO(), client, limitID).Extract() th.AssertErr(t, err) // Delete registered limit - err = registeredlimits.Delete(client, createdRegisteredLimits[0].ID).ExtractErr() + err = registeredlimits.Delete(context.TODO(), client, createdRegisteredLimits[0].ID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index efac3d778c..a88ac39cc0 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -35,7 +36,7 @@ func TestOAuth1CRUD(t *testing.T) { DomainName: ao.DomainName, }, } - tokenRes := tokens.Create(client, &authOptions) + tokenRes := tokens.Create(context.TODO(), client, &authOptions) token, err := tokenRes.Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) @@ -57,11 +58,11 @@ func TestOAuth1CRUD(t *testing.T) { Description: "My test consumer", } // NOTE: secret is available only in create response - consumer, err := oauth1.CreateConsumer(client, createConsumerOpts).Extract() + consumer, err := oauth1.CreateConsumer(context.TODO(), client, createConsumerOpts).Extract() th.AssertNoErr(t, err) // Delete a consumer - defer oauth1.DeleteConsumer(client, consumer.ID) + defer oauth1.DeleteConsumer(context.TODO(), client, consumer.ID) tools.PrintResource(t, consumer) th.AssertEquals(t, consumer.Description, createConsumerOpts.Description) @@ -70,21 +71,21 @@ func TestOAuth1CRUD(t *testing.T) { updateConsumerOpts := oauth1.UpdateConsumerOpts{ Description: "", } - updatedConsumer, err := oauth1.UpdateConsumer(client, consumer.ID, updateConsumerOpts).Extract() + updatedConsumer, err := oauth1.UpdateConsumer(context.TODO(), client, consumer.ID, updateConsumerOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedConsumer) th.AssertEquals(t, updatedConsumer.ID, consumer.ID) th.AssertEquals(t, updatedConsumer.Description, updateConsumerOpts.Description) // Get a consumer - getConsumer, err := oauth1.GetConsumer(client, consumer.ID).Extract() + getConsumer, err := oauth1.GetConsumer(context.TODO(), client, consumer.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getConsumer) th.AssertEquals(t, getConsumer.ID, consumer.ID) th.AssertEquals(t, getConsumer.Description, updateConsumerOpts.Description) // List consumers - consumersPages, err := oauth1.ListConsumers(client).AllPages() + consumersPages, err := oauth1.ListConsumers(client).AllPages(context.TODO()) th.AssertNoErr(t, err) consumers, err := oauth1.ExtractConsumers(consumersPages) th.AssertNoErr(t, err) @@ -105,7 +106,7 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer OAuthSignatureMethod: method, RequestedProjectID: project.ID, } - requestToken, err := oauth1.RequestToken(client, requestTokenOpts).Extract() + requestToken, err := oauth1.RequestToken(context.TODO(), client, requestTokenOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, requestToken) @@ -120,7 +121,7 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer // test role by name authorizeTokenOpts.Roles = append(authorizeTokenOpts.Roles, oauth1.Role{Name: roles[1].Name}) } - authToken, err := oauth1.AuthorizeToken(client, requestToken.OAuthToken, authorizeTokenOpts).Extract() + authToken, err := oauth1.AuthorizeToken(context.TODO(), client, requestToken.OAuthToken, authorizeTokenOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, authToken) @@ -134,14 +135,14 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer OAuthSignatureMethod: method, } - accessToken, err := oauth1.CreateAccessToken(client, accessTokenOpts).Extract() + accessToken, err := oauth1.CreateAccessToken(context.TODO(), client, accessTokenOpts).Extract() th.AssertNoErr(t, err) - defer oauth1.RevokeAccessToken(client, user.ID, accessToken.OAuthToken) + defer oauth1.RevokeAccessToken(context.TODO(), client, user.ID, accessToken.OAuthToken) tools.PrintResource(t, accessToken) // Get access token - getAccessToken, err := oauth1.GetAccessToken(client, user.ID, accessToken.OAuthToken).Extract() + getAccessToken, err := oauth1.GetAccessToken(context.TODO(), client, user.ID, accessToken.OAuthToken).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getAccessToken) @@ -152,7 +153,7 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer th.AssertEquals(t, getAccessToken.ProjectID, project.ID) // List access tokens - accessTokensPages, err := oauth1.ListAccessTokens(client, user.ID).AllPages() + accessTokensPages, err := oauth1.ListAccessTokens(client, user.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) accessTokens, err := oauth1.ExtractAccessTokens(accessTokensPages) @@ -162,7 +163,7 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer th.AssertDeepEquals(t, accessTokens[0], *getAccessToken) // List access token roles - accessTokenRolesPages, err := oauth1.ListAccessTokenRoles(client, user.ID, accessToken.OAuthToken).AllPages() + accessTokenRolesPages, err := oauth1.ListAccessTokenRoles(client, user.ID, accessToken.OAuthToken).AllPages(context.TODO()) th.AssertNoErr(t, err) accessTokenRoles, err := oauth1.ExtractAccessTokenRoles(accessTokenRolesPages) @@ -181,7 +182,7 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer } // Get access token role - getAccessTokenRole, err := oauth1.GetAccessTokenRole(client, user.ID, accessToken.OAuthToken, roles[0].ID).Extract() + getAccessTokenRole, err := oauth1.GetAccessTokenRole(context.TODO(), client, user.ID, accessToken.OAuthToken, roles[0].ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, getAccessTokenRole) @@ -205,7 +206,7 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer OAuthTokenSecret: accessToken.OAuthTokenSecret, OAuthSignatureMethod: method, } - err = openstack.AuthenticateV3(newClient.ProviderClient, authOptions, gophercloud.EndpointOpts{}) + err = openstack.AuthenticateV3(context.TODO(), newClient.ProviderClient, authOptions, gophercloud.EndpointOpts{}) th.AssertNoErr(t, err) // Test OAuth1 token extract @@ -213,7 +214,7 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer tokens.Token oauth1.TokenExt } - tokenRes := tokens.Get(newClient, newClient.ProviderClient.TokenID) + tokenRes := tokens.Get(context.TODO(), newClient, newClient.ProviderClient.TokenID) err = tokenRes.ExtractInto(&token) th.AssertNoErr(t, err) oauth1Roles, err := tokenRes.ExtractRoles() diff --git a/internal/acceptance/openstack/identity/v3/osinherit_test.go b/internal/acceptance/openstack/identity/v3/osinherit_test.go index bed18b8f74..9d0e6d004f 100644 --- a/internal/acceptance/openstack/identity/v3/osinherit_test.go +++ b/internal/acceptance/openstack/identity/v3/osinherit_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -43,7 +44,7 @@ func TestInheritRolesAssignToUserOnProject(t *testing.T) { UserID: user.ID, ProjectID: project.ID, } - err = osinherit.Assign(client, role.ID, assignOpts).ExtractErr() + err = osinherit.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a project %s", @@ -53,7 +54,7 @@ func TestInheritRolesAssignToUserOnProject(t *testing.T) { UserID: user.ID, ProjectID: project.ID, } - err = osinherit.Validate(client, role.ID, validateOpts).ExtractErr() + err = osinherit.Validate(context.TODO(), client, role.ID, validateOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully validated inherited role %s to a user %s on a project %s", @@ -63,7 +64,7 @@ func TestInheritRolesAssignToUserOnProject(t *testing.T) { UserID: user.ID, ProjectID: project.ID, } - err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() + err = osinherit.Unassign(context.TODO(), client, role.ID, unassignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully unassigned inherited role %s to a user %s on a project %s", @@ -102,7 +103,7 @@ func TestInheritRolesAssignToUserOnDomain(t *testing.T) { DomainID: domain.ID, } - err = osinherit.Assign(client, role.ID, assignOpts).ExtractErr() + err = osinherit.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a domain %s", @@ -113,7 +114,7 @@ func TestInheritRolesAssignToUserOnDomain(t *testing.T) { DomainID: domain.ID, } - err = osinherit.Validate(client, role.ID, validateOpts).ExtractErr() + err = osinherit.Validate(context.TODO(), client, role.ID, validateOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully validated inherited role %s to a user %s on a domain %s", @@ -124,7 +125,7 @@ func TestInheritRolesAssignToUserOnDomain(t *testing.T) { DomainID: domain.ID, } - err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() + err = osinherit.Unassign(context.TODO(), client, role.ID, unassignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully unassigned inherited role %s to a user %s on a domain %s", @@ -166,7 +167,7 @@ func TestInheritRolesAssignToGroupOnDomain(t *testing.T) { DomainID: domain.ID, } - err = osinherit.Assign(client, role.ID, assignOpts).ExtractErr() + err = osinherit.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a domain %s", @@ -177,7 +178,7 @@ func TestInheritRolesAssignToGroupOnDomain(t *testing.T) { DomainID: domain.ID, } - err = osinherit.Validate(client, role.ID, validateOpts).ExtractErr() + err = osinherit.Validate(context.TODO(), client, role.ID, validateOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully validated inherited role %s to a group %s on a domain %s", @@ -188,7 +189,7 @@ func TestInheritRolesAssignToGroupOnDomain(t *testing.T) { DomainID: domain.ID, } - err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() + err = osinherit.Unassign(context.TODO(), client, role.ID, unassignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully unassigned inherited role %s to a group %s on a domain %s", @@ -227,7 +228,7 @@ func TestInheritRolesAssignToGroupOnProject(t *testing.T) { GroupID: group.ID, ProjectID: project.ID, } - err = osinherit.Assign(client, role.ID, assignOpts).ExtractErr() + err = osinherit.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a project %s", @@ -237,7 +238,7 @@ func TestInheritRolesAssignToGroupOnProject(t *testing.T) { GroupID: group.ID, ProjectID: project.ID, } - err = osinherit.Validate(client, role.ID, validateOpts).ExtractErr() + err = osinherit.Validate(context.TODO(), client, role.ID, validateOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully validated inherited role %s to a group %s on a project %s", @@ -247,7 +248,7 @@ func TestInheritRolesAssignToGroupOnProject(t *testing.T) { GroupID: group.ID, ProjectID: project.ID, } - err = osinherit.Unassign(client, role.ID, unassignOpts).ExtractErr() + err = osinherit.Unassign(context.TODO(), client, role.ID, unassignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully unassigned inherited role %s to a group %s on a project %s", diff --git a/internal/acceptance/openstack/identity/v3/policies_test.go b/internal/acceptance/openstack/identity/v3/policies_test.go index 5a62a8269b..eb21878370 100644 --- a/internal/acceptance/openstack/identity/v3/policies_test.go +++ b/internal/acceptance/openstack/identity/v3/policies_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestPoliciesList(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := policies.List(client, policies.ListOpts{}).AllPages() + allPages, err := policies.List(client, policies.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) @@ -43,7 +44,7 @@ func TestPoliciesCRUD(t *testing.T) { }, } - policy, err := policies.Create(client, &createOpts).Extract() + policy, err := policies.Create(context.TODO(), client, &createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, policy) @@ -55,7 +56,7 @@ func TestPoliciesCRUD(t *testing.T) { var listOpts policies.ListOpts - allPages, err := policies.List(client, listOpts).AllPages() + allPages, err := policies.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) @@ -77,7 +78,7 @@ func TestPoliciesCRUD(t *testing.T) { "type__contains": "json", } - allPages, err = policies.List(client, listOpts).AllPages() + allPages, err = policies.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err = policies.ExtractPolicies(allPages) @@ -99,7 +100,7 @@ func TestPoliciesCRUD(t *testing.T) { "type__contains": "foobar", } - allPages, err = policies.List(client, listOpts).AllPages() + allPages, err = policies.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err = policies.ExtractPolicies(allPages) @@ -117,7 +118,7 @@ func TestPoliciesCRUD(t *testing.T) { th.AssertEquals(t, false, found) - gotPolicy, err := policies.Get(client, policy.ID).Extract() + gotPolicy, err := policies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, policy, gotPolicy) @@ -129,7 +130,7 @@ func TestPoliciesCRUD(t *testing.T) { }, } - updatedPolicy, err := policies.Update(client, policy.ID, updateOpts).Extract() + updatedPolicy, err := policies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedPolicy) @@ -139,6 +140,6 @@ func TestPoliciesCRUD(t *testing.T) { th.AssertEquals(t, updatedPolicy.Blob, string(updateOpts.Blob)) th.AssertEquals(t, updatedPolicy.Extra["description"], updateOpts.Extra["description"]) - err = policies.Delete(client, policy.ID).ExtractErr() + err = policies.Delete(context.TODO(), client, policy.ID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go index 1de418da8a..1da742cc8b 100644 --- a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -27,7 +28,7 @@ func TestProjectEndpoints(t *testing.T) { tools.PrintResource(t, project) // Get an endpoint - allEndpointsPages, err := endpoints.List(client, nil).AllPages() + allEndpointsPages, err := endpoints.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allEndpoints, err := endpoints.ExtractEndpoints(allEndpointsPages) @@ -36,11 +37,11 @@ func TestProjectEndpoints(t *testing.T) { endpoint := allEndpoints[0] // Attach endpoint - err = projectendpoints.Create(client, project.ID, endpoint.ID).Err + err = projectendpoints.Create(context.TODO(), client, project.ID, endpoint.ID).Err th.AssertNoErr(t, err) // List endpoints - allProjectEndpointsPages, err := projectendpoints.List(client, project.ID).AllPages() + allProjectEndpointsPages, err := projectendpoints.List(client, project.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjectEndpoints, err := projectendpoints.ExtractEndpoints(allProjectEndpointsPages) @@ -50,7 +51,7 @@ func TestProjectEndpoints(t *testing.T) { tools.PrintResource(t, allProjectEndpoints[0]) // Detach endpoint - err = projectendpoints.Delete(client, project.ID, endpoint.ID).Err + err = projectendpoints.Delete(context.TODO(), client, project.ID, endpoint.ID).Err th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/identity/v3/projects_test.go b/internal/acceptance/openstack/identity/v3/projects_test.go index cf1d535451..95fff1aebd 100644 --- a/internal/acceptance/openstack/identity/v3/projects_test.go +++ b/internal/acceptance/openstack/identity/v3/projects_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestProjectsListAvailable(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := projects.ListAvailable(client).AllPages() + allPages, err := projects.ListAvailable(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) @@ -40,7 +41,7 @@ func TestProjectsList(t *testing.T) { Enabled: &iTrue, } - allPages, err := projects.List(client, listOpts).AllPages() + allPages, err := projects.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) @@ -61,7 +62,7 @@ func TestProjectsList(t *testing.T) { "name__contains": "dmi", } - allPages, err = projects.List(client, listOpts).AllPages() + allPages, err = projects.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) @@ -82,7 +83,7 @@ func TestProjectsList(t *testing.T) { "name__contains": "foo", } - allPages, err = projects.List(client, listOpts).AllPages() + allPages, err = projects.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) @@ -106,14 +107,14 @@ func TestProjectsGet(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := projects.List(client, nil).AllPages() + allPages, err := projects.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) th.AssertNoErr(t, err) project := allProjects[0] - p, err := projects.Get(client, project.ID).Extract() + p, err := projects.Get(context.TODO(), client, project.ID).Extract() if err != nil { t.Fatalf("Unable to get project: %v", err) } @@ -142,7 +143,7 @@ func TestProjectsCRUD(t *testing.T) { Enabled: &iFalse, } - updatedProject, err := projects.Update(client, project.ID, updateOpts).Extract() + updatedProject, err := projects.Update(context.TODO(), client, project.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) @@ -184,7 +185,7 @@ func TestProjectsDomain(t *testing.T) { Enabled: &iFalse, } - _, err = projects.Update(client, projectDomain.ID, updateOpts).Extract() + _, err = projects.Update(context.TODO(), client, projectDomain.ID, updateOpts).Extract() th.AssertNoErr(t, err) } @@ -232,7 +233,7 @@ func TestProjectsTags(t *testing.T) { Tags: "Tag1,Tag2", } - allPages, err := projects.List(client, listOpts).AllPages() + allPages, err := projects.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allPages) @@ -254,7 +255,7 @@ func TestProjectsTags(t *testing.T) { Tags: "Tag1,Tag2,Tag3", } - allPages, err = projects.List(client, listOpts).AllPages() + allPages, err = projects.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) @@ -267,7 +268,7 @@ func TestProjectsTags(t *testing.T) { TagsAny: "Tag1,Tag2,Tag3", } - allPages, err = projects.List(client, listOpts).AllPages() + allPages, err = projects.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) @@ -289,7 +290,7 @@ func TestProjectsTags(t *testing.T) { NotTagsAny: "Tag1", } - allPages, err = projects.List(client, listOpts).AllPages() + allPages, err = projects.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) @@ -311,7 +312,7 @@ func TestProjectsTags(t *testing.T) { NotTags: "Tag1,Tag2,Tag3", } - allPages, err = projects.List(client, listOpts).AllPages() + allPages, err = projects.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err = projects.ExtractProjects(allPages) @@ -333,7 +334,7 @@ func TestProjectsTags(t *testing.T) { Tags: &[]string{"Tag1"}, } - updatedProject, err := projects.Update(client, projectMain.ID, updateOpts).Extract() + updatedProject, err := projects.Update(context.TODO(), client, projectMain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) @@ -346,7 +347,7 @@ func TestProjectsTags(t *testing.T) { Description: &description, } - updatedProject, err = projects.Update(client, projectMain.ID, updateOpts).Extract() + updatedProject, err = projects.Update(context.TODO(), client, projectMain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) @@ -358,7 +359,7 @@ func TestProjectsTags(t *testing.T) { Tags: &[]string{}, } - updatedProject, err = projects.Update(client, projectMain.ID, updateOpts).Extract() + updatedProject, err = projects.Update(context.TODO(), client, projectMain.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedProject) diff --git a/internal/acceptance/openstack/identity/v3/reauth_test.go b/internal/acceptance/openstack/identity/v3/reauth_test.go index ea8a13f72b..4b1611ea3d 100644 --- a/internal/acceptance/openstack/identity/v3/reauth_test.go +++ b/internal/acceptance/openstack/identity/v3/reauth_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,13 +23,13 @@ func TestReauthAuthResultDeadlock(t *testing.T) { ao.AllowReauth = true - provider, err := openstack.AuthenticatedClient(ao) + provider, err := openstack.AuthenticatedClient(context.TODO(), ao) th.AssertNoErr(t, err) provider.SetToken("this is not a valid token") client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) - pages, err := projects.List(client, nil).AllPages() + pages, err := projects.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) _, err = projects.ExtractProjects(pages) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/identity/v3/regions_test.go b/internal/acceptance/openstack/identity/v3/regions_test.go index 711629bf39..62d27de7ba 100644 --- a/internal/acceptance/openstack/identity/v3/regions_test.go +++ b/internal/acceptance/openstack/identity/v3/regions_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestRegionsList(t *testing.T) { ParentRegionID: "RegionOne", } - allPages, err := regions.List(client, listOpts).AllPages() + allPages, err := regions.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRegions, err := regions.ExtractRegions(allPages) @@ -39,14 +40,14 @@ func TestRegionsGet(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := regions.List(client, nil).AllPages() + allPages, err := regions.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allRegions, err := regions.ExtractRegions(allPages) th.AssertNoErr(t, err) region := allRegions[0] - p, err := regions.Get(client, region.ID).Extract() + p, err := regions.Get(context.TODO(), client, region.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) @@ -90,7 +91,7 @@ func TestRegionsCRUD(t *testing.T) { */ } - newRegion, err := regions.Update(client, region.ID, updateOpts).Extract() + newRegion, err := regions.Update(context.TODO(), client, region.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRegion) diff --git a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go index b14a0fcd79..d94efe7b9f 100644 --- a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go +++ b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "os" "testing" @@ -25,7 +26,7 @@ func TestRegisteredLimitsCRUD(t *testing.T) { th.AssertNoErr(t, err) // Get glance service to register the limit - allServicePages, err := services.List(client, nil).AllPages() + allServicePages, err := services.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) svList, err := services.ExtractServices(allServicePages) @@ -51,7 +52,7 @@ func TestRegisteredLimitsCRUD(t *testing.T) { }, } - createdRegisteredLimits, err := registeredlimits.BatchCreate(client, createOpts).Extract() + createdRegisteredLimits, err := registeredlimits.BatchCreate(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, createdRegisteredLimits[0]) th.AssertIntGreaterOrEqual(t, 1, len(createdRegisteredLimits)) @@ -63,14 +64,14 @@ func TestRegisteredLimitsCRUD(t *testing.T) { // List the registered limits listOpts := registeredlimits.ListOpts{} - allPages, err := registeredlimits.List(client, listOpts).AllPages() + allPages, err := registeredlimits.List(client, listOpts).AllPages(context.TODO()) 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() + registered_limit, err := registeredlimits.Get(context.TODO(), client, createdRegisteredLimits[0].ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, registered_limit) @@ -85,7 +86,7 @@ func TestRegisteredLimitsCRUD(t *testing.T) { ResourceName: updatedResourceName, } - updated_registered_limit, err := registeredlimits.Update(client, createdRegisteredLimits[0].ID, updatedOpts).Extract() + updated_registered_limit, err := registeredlimits.Update(context.TODO(), client, createdRegisteredLimits[0].ID, updatedOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updated_registered_limit) @@ -94,9 +95,9 @@ func TestRegisteredLimitsCRUD(t *testing.T) { th.AssertEquals(t, updated_registered_limit.ResourceName, updatedResourceName) // Delete the registered limit - del_err := registeredlimits.Delete(client, createdRegisteredLimits[0].ID).ExtractErr() + del_err := registeredlimits.Delete(context.TODO(), client, createdRegisteredLimits[0].ID).ExtractErr() th.AssertNoErr(t, del_err) - _, err = registeredlimits.Get(client, createdRegisteredLimits[0].ID).Extract() + _, err = registeredlimits.Get(context.TODO(), client, createdRegisteredLimits[0].ID).Extract() th.AssertErr(t, err) } diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index 1bf4fe868c..9b8508b422 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -25,7 +26,7 @@ func TestRolesList(t *testing.T) { DomainID: "default", } - allPages, err := roles.List(client, listOpts).AllPages() + allPages, err := roles.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) @@ -45,7 +46,7 @@ func TestRolesGet(t *testing.T) { role, err := FindRole(t, client) th.AssertNoErr(t, err) - p, err := roles.Get(client, role.ID).Extract() + p, err := roles.Get(context.TODO(), client, role.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) @@ -73,7 +74,7 @@ func TestRolesCRUD(t *testing.T) { tools.PrintResource(t, role.Extra) listOpts := roles.ListOpts{} - allPages, err := roles.List(client, listOpts).AllPages() + allPages, err := roles.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) @@ -97,7 +98,7 @@ func TestRolesCRUD(t *testing.T) { }, } - newRole, err := roles.Update(client, role.ID, updateOpts).Extract() + newRole, err := roles.Update(context.TODO(), client, role.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRole) @@ -129,7 +130,7 @@ func TestRolesFilterList(t *testing.T) { "name__contains": "test", } - allPages, err := roles.List(client, listOpts).AllPages() + allPages, err := roles.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) @@ -151,7 +152,7 @@ func TestRolesFilterList(t *testing.T) { "name__contains": "reader", } - allPages, err = roles.List(client, listOpts).AllPages() + allPages, err = roles.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err = roles.ExtractRoles(allPages) @@ -199,7 +200,7 @@ func TestRoleListAssignmentIncludeNamesAndSubtree(t *testing.T) { UserID: user.ID, ProjectID: project.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a project %s", @@ -217,7 +218,7 @@ func TestRoleListAssignmentIncludeNamesAndSubtree(t *testing.T) { IncludeSubtree: &iTrue, IncludeNames: &iTrue, } - allPages, err := roles.ListAssignments(client, listAssignmentsOpts).AllPages() + allPages, err := roles.ListAssignments(client, listAssignmentsOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoleAssignments(allPages) @@ -266,7 +267,7 @@ func TestRoleListAssignmentForUserOnProject(t *testing.T) { UserID: user.ID, ProjectID: project.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a project %s", @@ -281,7 +282,7 @@ func TestRoleListAssignmentForUserOnProject(t *testing.T) { UserID: user.ID, ProjectID: project.ID, } - allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() + allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) @@ -331,7 +332,7 @@ func TestRoleListAssignmentForUserOnDomain(t *testing.T) { DomainID: domain.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a domain %s", @@ -346,7 +347,7 @@ func TestRoleListAssignmentForUserOnDomain(t *testing.T) { UserID: user.ID, DomainID: domain.ID, } - allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() + allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) @@ -396,7 +397,7 @@ func TestRoleListAssignmentForGroupOnProject(t *testing.T) { GroupID: group.ID, ProjectID: project.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a project %s", @@ -411,7 +412,7 @@ func TestRoleListAssignmentForGroupOnProject(t *testing.T) { GroupID: group.ID, ProjectID: project.ID, } - allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() + allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) @@ -464,7 +465,7 @@ func TestRoleListAssignmentForGroupOnDomain(t *testing.T) { DomainID: domain.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a domain %s", @@ -479,7 +480,7 @@ func TestRoleListAssignmentForGroupOnDomain(t *testing.T) { GroupID: group.ID, DomainID: domain.ID, } - allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages() + allPages, err := roles.ListAssignmentsOnResource(client, listAssignmentsOnResourceOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) @@ -526,7 +527,7 @@ func TestRolesAssignToUserOnProject(t *testing.T) { UserID: user.ID, ProjectID: project.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a project %s", @@ -545,7 +546,7 @@ func TestRolesAssignToUserOnProject(t *testing.T) { IncludeNames: &iTrue, } - allPages, err := roles.ListAssignments(client, lao).AllPages() + allPages, err := roles.ListAssignments(client, lao).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) @@ -599,7 +600,7 @@ func TestRolesAssignToUserOnDomain(t *testing.T) { DomainID: domain.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a user %s on a domain %s", @@ -618,7 +619,7 @@ func TestRolesAssignToUserOnDomain(t *testing.T) { IncludeNames: &iTrue, } - allPages, err := roles.ListAssignments(client, lao).AllPages() + allPages, err := roles.ListAssignments(client, lao).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) @@ -675,7 +676,7 @@ func TestRolesAssignToGroupOnDomain(t *testing.T) { DomainID: domain.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a domain %s", @@ -694,7 +695,7 @@ func TestRolesAssignToGroupOnDomain(t *testing.T) { IncludeNames: &iTrue, } - allPages, err := roles.ListAssignments(client, lao).AllPages() + allPages, err := roles.ListAssignments(client, lao).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) @@ -748,7 +749,7 @@ func TestRolesAssignToGroupOnProject(t *testing.T) { GroupID: group.ID, ProjectID: project.ID, } - err = roles.Assign(client, role.ID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, role.ID, assignOpts).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully assigned a role %s to a group %s on a project %s", @@ -767,7 +768,7 @@ func TestRolesAssignToGroupOnProject(t *testing.T) { IncludeNames: &iTrue, } - allPages, err := roles.ListAssignments(client, lao).AllPages() + allPages, err := roles.ListAssignments(client, lao).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoleAssignments, err := roles.ExtractRoleAssignments(allPages) diff --git a/internal/acceptance/openstack/identity/v3/service_test.go b/internal/acceptance/openstack/identity/v3/service_test.go index 43e9351428..be249c18fd 100644 --- a/internal/acceptance/openstack/identity/v3/service_test.go +++ b/internal/acceptance/openstack/identity/v3/service_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestServicesList(t *testing.T) { ServiceType: "identity", } - allPages, err := services.List(client, listOpts).AllPages() + allPages, err := services.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) @@ -69,7 +70,7 @@ func TestServicesCRUD(t *testing.T) { }, } - newService, err := services.Update(client, service.ID, updateOpts).Extract() + newService, err := services.Update(context.TODO(), client, service.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newService) diff --git a/internal/acceptance/openstack/identity/v3/token_test.go b/internal/acceptance/openstack/identity/v3/token_test.go index db480d2a4f..cd5aeb6d30 100644 --- a/internal/acceptance/openstack/identity/v3/token_test.go +++ b/internal/acceptance/openstack/identity/v3/token_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -28,23 +29,23 @@ func TestTokensGet(t *testing.T) { DomainName: "default", } - token, err := tokens.Create(client, &authOptions).Extract() + token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, token) - catalog, err := tokens.Get(client, token.ID).ExtractServiceCatalog() + catalog, err := tokens.Get(context.TODO(), client, token.ID).ExtractServiceCatalog() th.AssertNoErr(t, err) tools.PrintResource(t, catalog) - user, err := tokens.Get(client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - roles, err := tokens.Get(client, token.ID).ExtractRoles() + roles, err := tokens.Get(context.TODO(), client, token.ID).ExtractRoles() th.AssertNoErr(t, err) tools.PrintResource(t, roles) - project, err := tokens.Get(client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) } diff --git a/internal/acceptance/openstack/identity/v3/trusts_test.go b/internal/acceptance/openstack/identity/v3/trusts_test.go index 749b4a8679..0d848496ce 100644 --- a/internal/acceptance/openstack/identity/v3/trusts_test.go +++ b/internal/acceptance/openstack/identity/v3/trusts_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "time" @@ -34,15 +35,15 @@ func TestTrustCRUD(t *testing.T) { DomainID: ao.DomainID, } - token, err := tokens.Create(client, &authOptions).Extract() + token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() th.AssertNoErr(t, err) - adminUser, err := tokens.Get(client, token.ID).ExtractUser() + adminUser, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() th.AssertNoErr(t, err) // Get the admin and member role IDs. adminRoleID := "" memberRoleID := "" - allPages, err := roles.List(client, nil).AllPages() + allPages, err := roles.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allRoles, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) @@ -70,7 +71,7 @@ func TestTrustCRUD(t *testing.T) { ProjectID: trusteeProject.ID, } - err = roles.Assign(client, adminRoleID, assignOpts).ExtractErr() + err = roles.Assign(context.TODO(), client, adminRoleID, assignOpts).ExtractErr() th.AssertNoErr(t, err) // Create a user as the trustee. @@ -102,7 +103,7 @@ func TestTrustCRUD(t *testing.T) { th.AssertNoErr(t, err) // Get trust - p, err := trusts.Get(client, trust.ID).Extract() + p, err := trusts.Get(context.TODO(), client, trust.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, p.ExpiresAt, expiresAt) th.AssertEquals(t, p.DeletedAt.IsZero(), true) @@ -110,7 +111,7 @@ func TestTrustCRUD(t *testing.T) { tools.PrintResource(t, p) // List trust roles - rolesPages, err := trusts.ListRoles(client, p.ID).AllPages() + rolesPages, err := trusts.ListRoles(client, p.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allTrustRoles, err := trusts.ExtractRoles(rolesPages) th.AssertNoErr(t, err) @@ -118,11 +119,11 @@ func TestTrustCRUD(t *testing.T) { th.AssertEquals(t, allTrustRoles[0].ID, memberRoleID) // Get trust role - role, err := trusts.GetRole(client, p.ID, memberRoleID).Extract() + role, err := trusts.GetRole(context.TODO(), client, p.ID, memberRoleID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, role.ID, memberRoleID) // Check trust role - err = trusts.CheckRole(client, p.ID, memberRoleID).ExtractErr() + err = trusts.CheckRole(context.TODO(), client, p.ID, memberRoleID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/identity/v3/users_test.go b/internal/acceptance/openstack/identity/v3/users_test.go index 02f9740dec..ac67b98c72 100644 --- a/internal/acceptance/openstack/identity/v3/users_test.go +++ b/internal/acceptance/openstack/identity/v3/users_test.go @@ -4,6 +4,7 @@ package v3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -25,7 +26,7 @@ func TestUsersList(t *testing.T) { Enabled: &iTrue, } - allPages, err := users.List(client, listOpts).AllPages() + allPages, err := users.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) @@ -47,7 +48,7 @@ func TestUsersList(t *testing.T) { "name__contains": "dmi", } - allPages, err = users.List(client, listOpts).AllPages() + allPages, err = users.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allUsers, err = users.ExtractUsers(allPages) @@ -69,7 +70,7 @@ func TestUsersList(t *testing.T) { "name__contains": "foo", } - allPages, err = users.List(client, listOpts).AllPages() + allPages, err = users.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allUsers, err = users.ExtractUsers(allPages) @@ -94,14 +95,14 @@ func TestUsersGet(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allPages, err := users.List(client, nil).AllPages() + allPages, err := users.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allPages) th.AssertNoErr(t, err) user := allUsers[0] - p, err := users.Get(client, user.ID).Extract() + p, err := users.Get(context.TODO(), client, user.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, p) @@ -163,7 +164,7 @@ func TestUserCRUD(t *testing.T) { }, } - newUser, err := users.Update(client, user.ID, updateOpts).Extract() + newUser, err := users.Update(context.TODO(), client, user.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newUser) @@ -197,7 +198,7 @@ func TestUserChangePassword(t *testing.T) { OriginalPassword: "secretsecret", Password: "new_secretsecret", } - err = users.ChangePassword(client, user.ID, changePasswordOpts).ExtractErr() + err = users.ChangePassword(context.TODO(), client, user.ID, changePasswordOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -232,10 +233,10 @@ func TestUsersGroups(t *testing.T) { tools.PrintResource(t, group) tools.PrintResource(t, group.Extra) - err = users.AddToGroup(client, group.ID, user.ID).ExtractErr() + err = users.AddToGroup(context.TODO(), client, group.ID, user.ID).ExtractErr() th.AssertNoErr(t, err) - allGroupPages, err := users.ListGroups(client, user.ID).AllPages() + allGroupPages, err := users.ListGroups(client, user.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allGroups, err := groups.ExtractGroups(allGroupPages) @@ -254,7 +255,7 @@ func TestUsersGroups(t *testing.T) { th.AssertEquals(t, found, true) found = false - allUserPages, err := users.ListInGroup(client, group.ID, nil).AllPages() + allUserPages, err := users.ListInGroup(client, group.ID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allUserPages) @@ -271,7 +272,7 @@ func TestUsersGroups(t *testing.T) { th.AssertEquals(t, found, true) - ok, err := users.IsMemberOfGroup(client, group.ID, user.ID).Extract() + ok, err := users.IsMemberOfGroup(context.TODO(), client, group.ID, user.ID).Extract() if err != nil { t.Fatalf("Unable to check whether user belongs to group: %v", err) } @@ -279,10 +280,10 @@ func TestUsersGroups(t *testing.T) { t.Fatalf("User %s is expected to be a member of group %s", user.ID, group.ID) } - err = users.RemoveFromGroup(client, group.ID, user.ID).ExtractErr() + err = users.RemoveFromGroup(context.TODO(), client, group.ID, user.ID).ExtractErr() th.AssertNoErr(t, err) - allGroupPages, err = users.ListGroups(client, user.ID).AllPages() + allGroupPages, err = users.ListGroups(client, user.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allGroups, err = groups.ExtractGroups(allGroupPages) @@ -301,7 +302,7 @@ func TestUsersGroups(t *testing.T) { th.AssertEquals(t, found, false) found = false - allUserPages, err = users.ListInGroup(client, group.ID, nil).AllPages() + allUserPages, err = users.ListInGroup(client, group.ID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allUsers, err = users.ExtractUsers(allUserPages) @@ -326,7 +327,7 @@ func TestUsersListProjects(t *testing.T) { client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) - allUserPages, err := users.List(client, nil).AllPages() + allUserPages, err := users.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allUsers, err := users.ExtractUsers(allUserPages) @@ -334,7 +335,7 @@ func TestUsersListProjects(t *testing.T) { user := allUsers[0] - allProjectPages, err := users.ListProjects(client, user.ID).AllPages() + allProjectPages, err := users.ListProjects(client, user.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allProjects, err := projects.ExtractProjects(allProjectPages) diff --git a/internal/acceptance/openstack/imageservice/v2/images_test.go b/internal/acceptance/openstack/imageservice/v2/images_test.go index d3c57407fd..1013d66d3d 100644 --- a/internal/acceptance/openstack/imageservice/v2/images_test.go +++ b/internal/acceptance/openstack/imageservice/v2/images_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "sort" "testing" "time" @@ -24,7 +25,7 @@ func TestImagesListEachPage(t *testing.T) { } pager := images.List(client, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { images, err := images.ExtractImages(page) if err != nil { t.Fatalf("Unable to extract images: %v", err) @@ -49,7 +50,7 @@ func TestImagesListAllPages(t *testing.T) { listOpts := images.ListOpts{} - allPages, err := images.List(client, listOpts).AllPages() + allPages, err := images.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) @@ -81,7 +82,7 @@ func TestImagesListByDate(t *testing.T) { }, } - allPages, err := images.List(client, listOpts).AllPages() + allPages, err := images.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) @@ -105,7 +106,7 @@ func TestImagesListByDate(t *testing.T) { }, } - allPages, err = images.List(client, listOpts).AllPages() + allPages, err = images.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allImages, err = images.ExtractImages(allPages) @@ -130,7 +131,7 @@ func TestImagesFilter(t *testing.T) { DiskFormat: "qcow2", } - allPages, err := images.List(client, listOpts).AllPages() + allPages, err := images.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allImages, err := images.ExtractImages(allPages) @@ -167,7 +168,7 @@ func TestImagesUpdate(t *testing.T) { }, } - newImage, err := images.Update(client, image.ID, updateOpts).Extract() + newImage, err := images.Update(context.TODO(), client, image.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newImage) @@ -192,6 +193,6 @@ func TestImagesUpdate(t *testing.T) { updateOpts = images.UpdateOpts{ images.ReplaceImageProtected{NewProtected: false}, } - _, err = images.Update(client, image.ID, updateOpts).Extract() + _, err = images.Update(context.TODO(), client, image.ID, updateOpts).Extract() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/imageservice/v2/imageservice.go b/internal/acceptance/openstack/imageservice/v2/imageservice.go index 37c21321bd..b44526e26e 100644 --- a/internal/acceptance/openstack/imageservice/v2/imageservice.go +++ b/internal/acceptance/openstack/imageservice/v2/imageservice.go @@ -3,6 +3,7 @@ package v2 import ( + "context" "io" "net/http" "os" @@ -41,12 +42,12 @@ func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images. Tags: []string{"foo", "bar", "baz"}, } - image, err := images.Create(client, createOpts).Extract() + image, err := images.Create(context.TODO(), client, createOpts).Extract() if err != nil { return image, err } - newImage, err := images.Get(client, image.ID).Extract() + newImage, err := images.Get(context.TODO(), client, image.ID).Extract() if err != nil { return image, err } @@ -62,7 +63,7 @@ func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images. // A fatal error will occur if the image failed to delete. This works best when // used as a deferred function. func DeleteImage(t *testing.T, client *gophercloud.ServiceClient, image *images.Image) { - err := images.Delete(client, image.ID).ExtractErr() + err := images.Delete(context.TODO(), client, image.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete image %s: %v", image.ID, err) } @@ -88,12 +89,12 @@ func CreateTask(t *testing.T, client *gophercloud.ServiceClient, imageURL string "import_from": imageURL, }, } - task, err := tasks.Create(client, opts).Extract() + task, err := tasks.Create(context.TODO(), client, opts).Extract() if err != nil { return nil, err } - newTask, err := tasks.Get(client, task.ID).Extract() + newTask, err := tasks.Get(context.TODO(), client, task.ID).Extract() if err != nil { return nil, err } @@ -104,7 +105,7 @@ func CreateTask(t *testing.T, client *gophercloud.ServiceClient, imageURL string // GetImportInfo will retrieve Import API information. func GetImportInfo(t *testing.T, client *gophercloud.ServiceClient) (*imageimport.ImportInfo, error) { t.Log("Attempting to get the Imageservice Import API information") - importInfo, err := imageimport.Get(client).Extract() + importInfo, err := imageimport.Get(context.TODO(), client).Extract() if err != nil { return nil, err } @@ -120,7 +121,7 @@ func StageImage(t *testing.T, client *gophercloud.ServiceClient, filepath, image } defer imageData.Close() - return imagedata.Stage(client, imageID, imageData).ExtractErr() + return imagedata.Stage(context.TODO(), client, imageID, imageData).ExtractErr() } // DownloadImageFileFromURL will download an image from the specified URL and @@ -166,5 +167,5 @@ func ImportImage(t *testing.T, client *gophercloud.ServiceClient, imageID string } t.Logf("Attempting to import image data for %s from %s", imageID, importOpts.URI) - return imageimport.Create(client, imageID, importOpts).ExtractErr() + return imageimport.Create(context.TODO(), client, imageID, importOpts).ExtractErr() } diff --git a/internal/acceptance/openstack/imageservice/v2/tasks_test.go b/internal/acceptance/openstack/imageservice/v2/tasks_test.go index c53b37f094..1a722f19bc 100644 --- a/internal/acceptance/openstack/imageservice/v2/tasks_test.go +++ b/internal/acceptance/openstack/imageservice/v2/tasks_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestTasksListEachPage(t *testing.T) { } pager := tasks.List(client, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { tasks, err := tasks.ExtractTasks(page) th.AssertNoErr(t, err) @@ -40,7 +41,7 @@ func TestTasksListAllPages(t *testing.T) { listOpts := tasks.ListOpts{} - allPages, err := tasks.List(client, listOpts).AllPages() + allPages, err := tasks.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allTasks, err := tasks.ExtractTasks(allPages) diff --git a/internal/acceptance/openstack/keymanager/v1/acls_test.go b/internal/acceptance/openstack/keymanager/v1/acls_test.go index 5ba18a707b..e8b0b6db28 100644 --- a/internal/acceptance/openstack/keymanager/v1/acls_test.go +++ b/internal/acceptance/openstack/keymanager/v1/acls_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -34,18 +35,18 @@ func TestACLCRUD(t *testing.T) { }, } - aclRef, err := acls.SetSecretACL(client, secretID, setOpts).Extract() + aclRef, err := acls.SetSecretACL(context.TODO(), client, secretID, setOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aclRef) defer func() { - err := acls.DeleteSecretACL(client, secretID).ExtractErr() + err := acls.DeleteSecretACL(context.TODO(), client, secretID).ExtractErr() th.AssertNoErr(t, err) - acl, err := acls.GetSecretACL(client, secretID).Extract() + acl, err := acls.GetSecretACL(context.TODO(), client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) }() - acl, err := acls.GetSecretACL(client, secretID).Extract() + acl, err := acls.GetSecretACL(context.TODO(), client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) tools.PrintResource(t, (*acl)["read"].Created) @@ -60,11 +61,11 @@ func TestACLCRUD(t *testing.T) { }, } - aclRef, err = acls.UpdateSecretACL(client, secretID, updateOpts).Extract() + aclRef, err = acls.UpdateSecretACL(context.TODO(), client, secretID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aclRef) - acl, err = acls.GetSecretACL(client, secretID).Extract() + acl, err = acls.GetSecretACL(context.TODO(), client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) tools.PrintResource(t, (*acl)["read"].Created) @@ -77,29 +78,29 @@ func TestACLCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteContainer(t, client, containerID) - aclRef, err = acls.SetContainerACL(client, containerID, setOpts).Extract() + aclRef, err = acls.SetContainerACL(context.TODO(), client, containerID, setOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aclRef) defer func() { - err := acls.DeleteContainerACL(client, containerID).ExtractErr() + err := acls.DeleteContainerACL(context.TODO(), client, containerID).ExtractErr() th.AssertNoErr(t, err) - acl, err := acls.GetContainerACL(client, containerID).Extract() + acl, err := acls.GetContainerACL(context.TODO(), client, containerID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) }() - acl, err = acls.GetContainerACL(client, containerID).Extract() + acl, err = acls.GetContainerACL(context.TODO(), client, containerID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) tools.PrintResource(t, (*acl)["read"].Created) th.AssertEquals(t, len((*acl)["read"].Users), 1) th.AssertEquals(t, (*acl)["read"].ProjectAccess, false) - aclRef, err = acls.UpdateContainerACL(client, containerID, updateOpts).Extract() + aclRef, err = acls.UpdateContainerACL(context.TODO(), client, containerID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, aclRef) - acl, err = acls.GetContainerACL(client, containerID).Extract() + acl, err = acls.GetContainerACL(context.TODO(), client, containerID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, acl) tools.PrintResource(t, (*acl)["read"].Created) diff --git a/internal/acceptance/openstack/keymanager/v1/containers_test.go b/internal/acceptance/openstack/keymanager/v1/containers_test.go index 7b6395e9f6..50795a2678 100644 --- a/internal/acceptance/openstack/keymanager/v1/containers_test.go +++ b/internal/acceptance/openstack/keymanager/v1/containers_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -40,7 +41,7 @@ func TestGenericContainersCRUD(t *testing.T) { err = ReplaceGenericContainerSecretRef(t, client, container, secret, secret1) th.AssertNoErr(t, err) - allPages, err := containers.List(client, nil).AllPages() + allPages, err := containers.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allContainers, err := containers.ExtractContainers(allPages) @@ -70,7 +71,7 @@ func TestCertificateContainer(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err := secrets.GetPayload(client, secretID, nil).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Private Payload: %s", string(payload)) @@ -80,7 +81,7 @@ func TestCertificateContainer(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err = secrets.GetPayload(client, secretID, nil).Extract() + payload, err = secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Certificate Payload: %s", string(payload)) @@ -90,7 +91,7 @@ func TestCertificateContainer(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err = secrets.GetPayload(client, secretID, nil).Extract() + payload, err = secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Passphrase Payload: %s", string(payload)) @@ -114,7 +115,7 @@ func TestRSAContainer(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err := secrets.GetPayload(client, secretID, nil).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Private Payload: %s", string(payload)) @@ -124,7 +125,7 @@ func TestRSAContainer(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err = secrets.GetPayload(client, secretID, nil).Extract() + payload, err = secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Public Payload: %s", string(payload)) @@ -134,7 +135,7 @@ func TestRSAContainer(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err = secrets.GetPayload(client, secretID, nil).Extract() + payload, err = secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) t.Logf("Passphrase Payload: %s", string(payload)) @@ -167,7 +168,7 @@ func TestContainerConsumersCRUD(t *testing.T) { URL: "http://example.com", } - container, err = containers.CreateConsumer(client, containerID, consumerCreateOpts).Extract() + container, err = containers.CreateConsumer(context.TODO(), client, containerID, consumerCreateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, container.Consumers) th.AssertEquals(t, len(container.Consumers), 1) @@ -177,12 +178,12 @@ func TestContainerConsumersCRUD(t *testing.T) { URL: "http://example.com", } - container, err := containers.DeleteConsumer(client, containerID, deleteOpts).Extract() + container, err := containers.DeleteConsumer(context.TODO(), client, containerID, deleteOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, len(container.Consumers), 0) }() - allPages, err := containers.ListConsumers(client, containerID, nil).AllPages() + allPages, err := containers.ListConsumers(client, containerID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allConsumers, err := containers.ExtractConsumers(allPages) diff --git a/internal/acceptance/openstack/keymanager/v1/keymanager.go b/internal/acceptance/openstack/keymanager/v1/keymanager.go index a0bf19db66..cb84db8cd1 100644 --- a/internal/acceptance/openstack/keymanager/v1/keymanager.go +++ b/internal/acceptance/openstack/keymanager/v1/keymanager.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -40,7 +41,7 @@ func CreateAsymmetricOrder(t *testing.T, client *gophercloud.ServiceClient) (*or }, } - order, err := orders.Create(client, createOpts).Extract() + order, err := orders.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -53,7 +54,7 @@ func CreateAsymmetricOrder(t *testing.T, client *gophercloud.ServiceClient) (*or err = WaitForOrder(client, orderID) th.AssertNoErr(t, err) - order, err = orders.Get(client, orderID).Extract() + order, err = orders.Get(context.TODO(), client, orderID).Extract() if err != nil { return nil, err } @@ -93,7 +94,7 @@ func CreateCertificateContainer(t *testing.T, client *gophercloud.ServiceClient, }, } - container, err := containers.Create(client, createOpts).Extract() + container, err := containers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -105,7 +106,7 @@ func CreateCertificateContainer(t *testing.T, client *gophercloud.ServiceClient, return nil, err } - container, err = containers.Get(client, containerID).Extract() + container, err = containers.Get(context.TODO(), client, containerID).Extract() if err != nil { return nil, err } @@ -136,7 +137,7 @@ func CreateKeyOrder(t *testing.T, client *gophercloud.ServiceClient) (*orders.Or }, } - order, err := orders.Create(client, createOpts).Extract() + order, err := orders.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -146,7 +147,7 @@ func CreateKeyOrder(t *testing.T, client *gophercloud.ServiceClient) (*orders.Or return nil, err } - order, err = orders.Get(client, orderID).Extract() + order, err = orders.Get(context.TODO(), client, orderID).Extract() if err != nil { return nil, err } @@ -186,7 +187,7 @@ func CreateRSAContainer(t *testing.T, client *gophercloud.ServiceClient, passphr }, } - container, err := containers.Create(client, createOpts).Extract() + container, err := containers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -198,7 +199,7 @@ func CreateRSAContainer(t *testing.T, client *gophercloud.ServiceClient, passphr return nil, err } - container, err = containers.Get(client, containerID).Extract() + container, err = containers.Get(context.TODO(), client, containerID).Extract() if err != nil { return nil, err } @@ -228,7 +229,7 @@ func CreateCertificateSecret(t *testing.T, client *gophercloud.ServiceClient, ce Algorithm: "rsa", } - secret, err := secrets.Create(client, createOpts).Extract() + secret, err := secrets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -240,7 +241,7 @@ func CreateCertificateSecret(t *testing.T, client *gophercloud.ServiceClient, ce return nil, err } - secret, err = secrets.Get(client, secretID).Extract() + secret, err = secrets.Get(context.TODO(), client, secretID).Extract() if err != nil { return nil, err } @@ -268,7 +269,7 @@ func CreateEmptySecret(t *testing.T, client *gophercloud.ServiceClient) (*secret SecretType: secrets.OpaqueSecret, } - secret, err := secrets.Create(client, createOpts).Extract() + secret, err := secrets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -280,7 +281,7 @@ func CreateEmptySecret(t *testing.T, client *gophercloud.ServiceClient) (*secret return nil, err } - secret, err = secrets.Get(client, secretID).Extract() + secret, err = secrets.Get(context.TODO(), client, secretID).Extract() if err != nil { return nil, err } @@ -312,7 +313,7 @@ func CreateGenericContainer(t *testing.T, client *gophercloud.ServiceClient, sec }, } - container, err := containers.Create(client, createOpts).Extract() + container, err := containers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -324,7 +325,7 @@ func CreateGenericContainer(t *testing.T, client *gophercloud.ServiceClient, sec return nil, err } - container, err = containers.Get(client, containerID).Extract() + container, err = containers.Get(context.TODO(), client, containerID).Extract() if err != nil { return nil, err } @@ -348,7 +349,7 @@ func ReplaceGenericContainerSecretRef(t *testing.T, client *gophercloud.ServiceC t.Logf("Attempting to remove an old secret reference %s", secretOld.SecretRef) - res1 := containers.DeleteSecretRef(client, containerID, containers.SecretRef{Name: secretOld.Name, SecretRef: secretOld.SecretRef}) + res1 := containers.DeleteSecretRef(context.TODO(), client, containerID, containers.SecretRef{Name: secretOld.Name, SecretRef: secretOld.SecretRef}) if res1.Err != nil { return res1.Err } @@ -358,7 +359,7 @@ func ReplaceGenericContainerSecretRef(t *testing.T, client *gophercloud.ServiceC t.Logf("Attempting to remove a new secret reference %s", secretNew.SecretRef) newRef := containers.SecretRef{Name: secretNew.Name, SecretRef: secretNew.SecretRef} - res2 := containers.CreateSecretRef(client, containerID, newRef) + res2 := containers.CreateSecretRef(context.TODO(), client, containerID, newRef) if res2.Err != nil { return res2.Err } @@ -371,7 +372,7 @@ func ReplaceGenericContainerSecretRef(t *testing.T, client *gophercloud.ServiceC t.Logf("Successfully created new secret reference: %s", secretNew.SecretRef) - updatedContainer, err := containers.Get(client, containerID).Extract() + updatedContainer, err := containers.Get(context.TODO(), client, containerID).Extract() if err != nil { return err } @@ -402,7 +403,7 @@ func CreatePassphraseSecret(t *testing.T, client *gophercloud.ServiceClient, pas SecretType: secrets.PassphraseSecret, } - secret, err := secrets.Create(client, createOpts).Extract() + secret, err := secrets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -414,7 +415,7 @@ func CreatePassphraseSecret(t *testing.T, client *gophercloud.ServiceClient, pas return nil, err } - secret, err = secrets.Get(client, secretID).Extract() + secret, err = secrets.Get(context.TODO(), client, secretID).Extract() if err != nil { return nil, err } @@ -444,7 +445,7 @@ func CreatePublicSecret(t *testing.T, client *gophercloud.ServiceClient, pub []b Algorithm: "rsa", } - secret, err := secrets.Create(client, createOpts).Extract() + secret, err := secrets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -456,7 +457,7 @@ func CreatePublicSecret(t *testing.T, client *gophercloud.ServiceClient, pub []b return nil, err } - secret, err = secrets.Get(client, secretID).Extract() + secret, err = secrets.Get(context.TODO(), client, secretID).Extract() if err != nil { return nil, err } @@ -486,7 +487,7 @@ func CreatePrivateSecret(t *testing.T, client *gophercloud.ServiceClient, priv [ Algorithm: "rsa", } - secret, err := secrets.Create(client, createOpts).Extract() + secret, err := secrets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -498,7 +499,7 @@ func CreatePrivateSecret(t *testing.T, client *gophercloud.ServiceClient, priv [ return nil, err } - secret, err = secrets.Get(client, secretID).Extract() + secret, err = secrets.Get(context.TODO(), client, secretID).Extract() if err != nil { return nil, err } @@ -530,7 +531,7 @@ func CreateSecretWithPayload(t *testing.T, client *gophercloud.ServiceClient, pa Expiration: &expiration, } - secret, err := secrets.Create(client, createOpts).Extract() + secret, err := secrets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -542,7 +543,7 @@ func CreateSecretWithPayload(t *testing.T, client *gophercloud.ServiceClient, pa return nil, err } - secret, err = secrets.Get(client, secretID).Extract() + secret, err = secrets.Get(context.TODO(), client, secretID).Extract() if err != nil { return nil, err } @@ -576,7 +577,7 @@ func CreateSymmetricSecret(t *testing.T, client *gophercloud.ServiceClient) (*se Mode: "cbc", } - secret, err := secrets.Create(client, createOpts).Extract() + secret, err := secrets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -588,7 +589,7 @@ func CreateSymmetricSecret(t *testing.T, client *gophercloud.ServiceClient) (*se return nil, err } - secret, err = secrets.Get(client, secretID).Extract() + secret, err = secrets.Get(context.TODO(), client, secretID).Extract() if err != nil { return nil, err } @@ -607,7 +608,7 @@ func CreateSymmetricSecret(t *testing.T, client *gophercloud.ServiceClient) (*se func DeleteContainer(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete container %s", id) - err := containers.Delete(client, id).ExtractErr() + err := containers.Delete(context.TODO(), client, id).ExtractErr() if err != nil { t.Fatalf("Could not delete container: %s", err) } @@ -621,7 +622,7 @@ func DeleteContainer(t *testing.T, client *gophercloud.ServiceClient, id string) func DeleteOrder(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete order %s", id) - err := orders.Delete(client, id).ExtractErr() + err := orders.Delete(context.TODO(), client, id).ExtractErr() if err != nil { t.Fatalf("Could not delete order: %s", err) } @@ -634,7 +635,7 @@ func DeleteOrder(t *testing.T, client *gophercloud.ServiceClient, id string) { func DeleteSecret(t *testing.T, client *gophercloud.ServiceClient, id string) { t.Logf("Attempting to delete secret %s", id) - err := secrets.Delete(client, id).ExtractErr() + err := secrets.Delete(context.TODO(), client, id).ExtractErr() if err != nil { t.Fatalf("Could not delete secret: %s", err) } @@ -742,7 +743,7 @@ func CreateRSAKeyPair(t *testing.T, passphrase string) ([]byte, []byte, error) { func WaitForOrder(client *gophercloud.ServiceClient, orderID string) error { return tools.WaitFor(func() (bool, error) { - order, err := orders.Get(client, orderID).Extract() + order, err := orders.Get(context.TODO(), client, orderID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/keymanager/v1/orders_test.go b/internal/acceptance/openstack/keymanager/v1/orders_test.go index 29dc94efcd..73fd4ea851 100644 --- a/internal/acceptance/openstack/keymanager/v1/orders_test.go +++ b/internal/acceptance/openstack/keymanager/v1/orders_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -32,11 +33,11 @@ func TestOrdersCRUD(t *testing.T) { payloadOpts := secrets.GetPayloadOpts{ PayloadContentType: "application/octet-stream", } - payload, err := secrets.GetPayload(client, secretID, payloadOpts).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, payloadOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, payload) - allPages, err := orders.List(client, nil).AllPages() + allPages, err := orders.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allOrders, err := orders.ExtractOrders(allPages) @@ -67,7 +68,7 @@ func TestOrdersAsymmetric(t *testing.T) { containerID, err := ParseID(order.ContainerRef) th.AssertNoErr(t, err) - container, err := containers.Get(client, containerID).Extract() + container, err := containers.Get(context.TODO(), client, containerID).Extract() th.AssertNoErr(t, err) for _, v := range container.SecretRefs { @@ -78,7 +79,7 @@ func TestOrdersAsymmetric(t *testing.T) { PayloadContentType: "application/octet-stream", } - payload, err := secrets.GetPayload(client, secretID, payloadOpts).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, payloadOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } diff --git a/internal/acceptance/openstack/keymanager/v1/secrets_test.go b/internal/acceptance/openstack/keymanager/v1/secrets_test.go index 99355b6ad6..a6133dc320 100644 --- a/internal/acceptance/openstack/keymanager/v1/secrets_test.go +++ b/internal/acceptance/openstack/keymanager/v1/secrets_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "time" @@ -25,7 +26,7 @@ func TestSecretsCRUD(t *testing.T) { defer DeleteSecret(t, client, secretID) // Test payload retrieval - actual, err := secrets.GetPayload(client, secretID, nil).Extract() + actual, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, payload, string(actual)) @@ -39,7 +40,7 @@ func TestSecretsCRUD(t *testing.T) { CreatedQuery: createdQuery, } - allPages, err := secrets.List(client, listOpts).AllPages() + allPages, err := secrets.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allSecrets, err := secrets.ExtractSecrets(allPages) @@ -71,11 +72,11 @@ func TestSecretsDelayedPayload(t *testing.T) { Payload: payload, } - err = secrets.Update(client, secretID, updateOpts).ExtractErr() + err = secrets.Update(context.TODO(), client, secretID, updateOpts).ExtractErr() th.AssertNoErr(t, err) // Test payload retrieval - actual, err := secrets.GetPayload(client, secretID, nil).Extract() + actual, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, payload, string(actual)) } @@ -97,12 +98,12 @@ func TestSecretsMetadataCRUD(t *testing.T) { "something": "something else", } - ref, err := secrets.CreateMetadata(client, secretID, createOpts).Extract() + ref, err := secrets.CreateMetadata(context.TODO(), client, secretID, createOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ref["metadata_ref"], secret.SecretRef+"/metadata") // Get the metadata - metadata, err := secrets.GetMetadata(client, secretID).Extract() + metadata, err := secrets.GetMetadata(context.TODO(), client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) th.AssertEquals(t, metadata["foo"], "bar") @@ -114,10 +115,10 @@ func TestSecretsMetadataCRUD(t *testing.T) { Value: "baz", } - err = secrets.CreateMetadatum(client, secretID, metadatumOpts).ExtractErr() + err = secrets.CreateMetadatum(context.TODO(), client, secretID, metadatumOpts).ExtractErr() th.AssertNoErr(t, err) - metadata, err = secrets.GetMetadata(client, secretID).Extract() + metadata, err = secrets.GetMetadata(context.TODO(), client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) th.AssertEquals(t, len(metadata), 3) @@ -129,13 +130,13 @@ func TestSecretsMetadataCRUD(t *testing.T) { metadatumOpts.Key = "foo" metadatumOpts.Value = "foo" - metadatum, err := secrets.UpdateMetadatum(client, secretID, metadatumOpts).Extract() + metadatum, err := secrets.UpdateMetadatum(context.TODO(), client, secretID, metadatumOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadatum) th.AssertDeepEquals(t, metadatum.Key, "foo") th.AssertDeepEquals(t, metadatum.Value, "foo") - metadata, err = secrets.GetMetadata(client, secretID).Extract() + metadata, err = secrets.GetMetadata(context.TODO(), client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) th.AssertEquals(t, len(metadata), 3) @@ -144,10 +145,10 @@ func TestSecretsMetadataCRUD(t *testing.T) { th.AssertEquals(t, metadata["bar"], "baz") // Delete a metadatum - err = secrets.DeleteMetadatum(client, secretID, "foo").ExtractErr() + err = secrets.DeleteMetadatum(context.TODO(), client, secretID, "foo").ExtractErr() th.AssertNoErr(t, err) - metadata, err = secrets.GetMetadata(client, secretID).Extract() + metadata, err = secrets.GetMetadata(context.TODO(), client, secretID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) th.AssertEquals(t, len(metadata), 2) @@ -165,7 +166,7 @@ func TestSymmetricSecret(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err := secrets.GetPayload(client, secretID, nil).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } @@ -184,7 +185,7 @@ func TestCertificateSecret(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err := secrets.GetPayload(client, secretID, nil).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } @@ -203,7 +204,7 @@ func TestPrivateSecret(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err := secrets.GetPayload(client, secretID, nil).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } @@ -221,7 +222,7 @@ func TestPublicSecret(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err := secrets.GetPayload(client, secretID, nil).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } @@ -237,7 +238,7 @@ func TestPassphraseSecret(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecret(t, client, secretID) - payload, err := secrets.GetPayload(client, secretID, nil).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID, nil).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, string(payload)) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go b/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go index 4fa8cff9c3..9fa07addc1 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestAmphoraeList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := amphorae.List(client, nil).AllPages() + allPages, err := amphorae.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list amphorae: %v", err) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go index a3c934cc94..bdd930d3c0 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestFlavorProfilesList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) - allPages, err := flavorprofiles.List(client, nil).AllPages() + allPages, err := flavorprofiles.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allFlavorProfiles, err := flavorprofiles.ExtractFlavorProfiles(allPages) @@ -44,7 +45,7 @@ func TestFlavorProfilesCRUD(t *testing.T) { Name: tools.RandomString("TESTACCTUP-", 8), } - flavorProfileUpdated, err := flavorprofiles.Update(lbClient, flavorProfile.ID, flavorProfileUpdateOpts).Extract() + flavorProfileUpdated, err := flavorprofiles.Update(context.TODO(), lbClient, flavorProfile.ID, flavorProfileUpdateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, flavorProfileUpdateOpts.Name, flavorProfileUpdated.Name) diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go index 4597e107ce..7681809841 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestFlavorsList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := flavors.List(client, nil).AllPages() + allPages, err := flavors.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list flavors: %v", err) } @@ -57,7 +58,7 @@ func TestFlavorsCRUD(t *testing.T) { Name: tools.RandomString("TESTACCTUP-", 8), } - flavorUpdated, err := flavors.Update(lbClient, flavor.ID, flavorUpdateOpts).Extract() + flavorUpdated, err := flavors.Update(context.TODO(), lbClient, flavor.ID, flavorUpdateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, flavorUpdateOpts.Name, flavorUpdated.Name) diff --git a/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go index ac61897e43..30f26b3e14 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestL7PoliciesList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := l7policies.List(client, nil).AllPages() + allPages, err := l7policies.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list l7policies: %v", err) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go b/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go index bbabc35059..a172695638 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestListenersList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := listeners.List(client, nil).AllPages() + allPages, err := listeners.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list listeners: %v", err) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 3be3c10f8b..a214f4626c 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "strings" "testing" @@ -36,7 +37,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal ProtocolPort: listenerPort, } - listener, err := listeners.Create(client, createOpts).Extract() + listener, err := listeners.Create(context.TODO(), client, createOpts).Extract() if err != nil { return listener, err } @@ -88,7 +89,7 @@ func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loa TLSVersions: tlsVersions, } - listener, err := listeners.Create(client, createOpts).Extract() + listener, err := listeners.Create(context.TODO(), client, createOpts).Extract() if err != nil { return listener, err } @@ -133,7 +134,7 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI createOpts.VipQosPolicyID = policyID } - lb, err := loadbalancers.Create(client, createOpts).Extract() + lb, err := loadbalancers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return lb, err } @@ -230,7 +231,7 @@ func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceC createOpts.Tags = tags } - lb, err := loadbalancers.Create(client, createOpts).Extract() + lb, err := loadbalancers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return lb, err } @@ -299,7 +300,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan t.Logf("Member create opts: %#v", createOpts) - member, err := pools.CreateMember(client, pool.ID, createOpts).Extract() + member, err := pools.CreateMember(context.TODO(), client, pool.ID, createOpts).Extract() if err != nil { return member, err } @@ -332,7 +333,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala Type: monitors.TypePING, } - monitor, err := monitors.Create(client, createOpts).Extract() + monitor, err := monitors.Create(context.TODO(), client, createOpts).Extract() if err != nil { return monitor, err } @@ -370,7 +371,7 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance LBMethod: pools.LBMethodLeastConnections, } - pool, err := pools.Create(client, createOpts).Extract() + pool, err := pools.Create(context.TODO(), client, createOpts).Extract() if err != nil { return pool, err } @@ -407,7 +408,7 @@ func CreatePoolHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal LBMethod: pools.LBMethodLeastConnections, } - pool, err := pools.Create(client, createOpts).Extract() + pool, err := pools.Create(context.TODO(), client, createOpts).Extract() if err != nil { return pool, err } @@ -445,7 +446,7 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l Tags: tags, } - policy, err := l7policies.Create(client, createOpts).Extract() + policy, err := l7policies.Create(context.TODO(), client, createOpts).Extract() if err != nil { return policy, err } @@ -477,7 +478,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri Tags: tags, } - rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract() + rule, err := l7policies.CreateRule(context.TODO(), client, policyID, createOpts).Extract() if err != nil { return rule, err } @@ -502,7 +503,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) { t.Logf("Attempting to delete l7 policy %s", policyID) - if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil { + if err := l7policies.Delete(context.TODO(), client, policyID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete l7 policy: %v", err) } @@ -521,7 +522,7 @@ func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, polic func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) { t.Logf("Attempting to delete l7 rule %s", ruleID) - if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil { + if err := l7policies.DeleteRule(context.TODO(), client, policyID, ruleID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete l7 rule: %v", err) } @@ -540,7 +541,7 @@ func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyI func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) { t.Logf("Attempting to delete listener %s", listenerID) - if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil { + if err := listeners.Delete(context.TODO(), client, listenerID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete listener: %v", err) } @@ -559,7 +560,7 @@ func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, liste func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) { t.Logf("Attempting to delete member %s", memberID) - if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil { + if err := pools.DeleteMember(context.TODO(), client, poolID, memberID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete member: %s", memberID) } @@ -582,7 +583,7 @@ func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID st Cascade: false, } - if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil { + if err := loadbalancers.Delete(context.TODO(), client, lbID, deleteOpts).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete loadbalancer: %v", err) } @@ -607,7 +608,7 @@ func CascadeDeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, Cascade: true, } - if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil { + if err := loadbalancers.Delete(context.TODO(), client, lbID, deleteOpts).ExtractErr(); err != nil { t.Fatalf("Unable to cascade delete loadbalancer: %v", err) } @@ -626,7 +627,7 @@ func CascadeDeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) { t.Logf("Attempting to delete monitor %s", monitorID) - if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { + if err := monitors.Delete(context.TODO(), client, monitorID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete monitor: %v", err) } @@ -644,7 +645,7 @@ func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monito func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) { t.Logf("Attempting to delete pool %s", poolID) - if err := pools.Delete(client, poolID).ExtractErr(); err != nil { + if err := pools.Delete(context.TODO(), client, poolID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete pool: %v", err) } @@ -660,7 +661,7 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := loadbalancers.Get(client, lbID).Extract() + current, err := loadbalancers.Get(context.TODO(), client, lbID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { @@ -695,7 +696,7 @@ func CreateFlavorProfile(t *testing.T, client *gophercloud.ServiceClient) (*flav FlavorData: flavorProfileData, } - flavorProfile, err := flavorprofiles.Create(client, createOpts).Extract() + flavorProfile, err := flavorprofiles.Create(context.TODO(), client, createOpts).Extract() if err != nil { return flavorProfile, err } @@ -710,7 +711,7 @@ func CreateFlavorProfile(t *testing.T, client *gophercloud.ServiceClient) (*flav } func DeleteFlavorProfile(t *testing.T, client *gophercloud.ServiceClient, flavorProfile *flavorprofiles.FlavorProfile) { - err := flavorprofiles.Delete(client, flavorProfile.ID).ExtractErr() + err := flavorprofiles.Delete(context.TODO(), client, flavorProfile.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete flavorprofile: %v", err) } @@ -729,7 +730,7 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient, flavorProfile Enabled: true, } - flavor, err := flavors.Create(client, createOpts).Extract() + flavor, err := flavors.Create(context.TODO(), client, createOpts).Extract() if err != nil { return flavor, err } @@ -745,7 +746,7 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient, flavorProfile } func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) { - err := flavors.Delete(client, flavor.ID).ExtractErr() + err := flavors.Delete(context.TODO(), client, flavor.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete flavor: %v", err) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 3c16c52783..4334adbb14 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestLoadbalancersList(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) - allPages, err := loadbalancers.List(client, nil).AllPages() + allPages, err := loadbalancers.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) @@ -59,7 +60,7 @@ func TestLoadbalancersListByTags(t *testing.T) { listOpts := loadbalancers.ListOpts{ Tags: tags, } - allPages, err := loadbalancers.List(lbClient, listOpts).AllPages() + allPages, err := loadbalancers.List(lbClient, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) @@ -69,7 +70,7 @@ func TestLoadbalancersListByTags(t *testing.T) { listOpts = loadbalancers.ListOpts{ TagsNot: tags, } - allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() + allPages, err = loadbalancers.List(lbClient, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) @@ -79,7 +80,7 @@ func TestLoadbalancersListByTags(t *testing.T) { listOpts = loadbalancers.ListOpts{ TagsAny: tags, } - allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() + allPages, err = loadbalancers.List(lbClient, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) @@ -89,7 +90,7 @@ func TestLoadbalancersListByTags(t *testing.T) { listOpts = loadbalancers.ListOpts{ TagsNotAny: tags, } - allPages, err = loadbalancers.List(lbClient, listOpts).AllPages() + allPages, err = loadbalancers.List(lbClient, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allLoadbalancers, err = loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) @@ -132,14 +133,14 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { Description: &newDescription, Tags: &tags, } - _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() + _, err = l7policies.Update(context.TODO(), 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() + newPolicy, err := l7policies.Get(context.TODO(), lbClient, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) @@ -153,7 +154,7 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteL7Rule(t, lbClient, lb.ID, policy.ID, rule.ID) - allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages() + allPages, err := l7policies.ListRules(lbClient, policy.ID, l7policies.ListRulesOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allRules, err := l7policies.ExtractRules(allPages) th.AssertNoErr(t, err) @@ -168,14 +169,14 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { Value: "/images/special*", Tags: &tags, } - _, err = l7policies.UpdateRule(lbClient, policy.ID, rule.ID, updateL7ruleOpts).Extract() + _, err = l7policies.UpdateRule(context.TODO(), 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() + newRule, err := l7policies.GetRule(context.TODO(), lbClient, newPolicy.ID, rule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) @@ -193,14 +194,14 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { Name: &poolName, Description: &poolDescription, } - _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() + _, err = pools.Update(context.TODO(), 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() + newPool, err := pools.Get(context.TODO(), lbClient, pool.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPool) @@ -214,14 +215,14 @@ func TestLoadbalancerHTTPCRUD(t *testing.T) { RedirectPoolID: &newPool.ID, RedirectURL: &newRedirectURL, } - _, err = l7policies.Update(lbClient, policy.ID, updateL7policyOpts).Extract() + _, err = l7policies.Update(context.TODO(), 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() + newPolicy, err = l7policies.Get(context.TODO(), lbClient, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) @@ -312,14 +313,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Description: &lbDescription, VipQosPolicyID: &policy2.ID, } - _, err = loadbalancers.Update(lbClient, lb.ID, updateLoadBalancerOpts).Extract() + _, err = loadbalancers.Update(context.TODO(), lbClient, lb.ID, updateLoadBalancerOpts).Extract() th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() + newLB, err := loadbalancers.Get(context.TODO(), lbClient, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newLB) @@ -327,7 +328,7 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertEquals(t, newLB.Description, lbDescription) th.AssertEquals(t, newLB.VipQosPolicyID, policy2.ID) - lbStats, err := loadbalancers.GetStats(lbClient, lb.ID).Extract() + lbStats, err := loadbalancers.GetStats(context.TODO(), lbClient, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, lbStats) @@ -346,14 +347,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Name: &listenerName, Description: &listenerDescription, } - _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + _, err = listeners.Update(context.TODO(), lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newListener, err := listeners.Get(lbClient, listener.ID).Extract() + newListener, err := listeners.Get(context.TODO(), lbClient, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) @@ -361,7 +362,7 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertEquals(t, newListener.Name, listenerName) th.AssertEquals(t, newListener.Description, listenerDescription) - listenerStats, err := listeners.GetStats(lbClient, listener.ID).Extract() + listenerStats, err := listeners.GetStats(context.TODO(), lbClient, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, listenerStats) @@ -375,14 +376,14 @@ func TestLoadbalancersCRUD(t *testing.T) { updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &pool.ID, } - _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + _, err = listeners.Update(context.TODO(), lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newListener, err = listeners.Get(lbClient, listener.ID).Extract() + newListener, err = listeners.Get(context.TODO(), lbClient, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) @@ -394,14 +395,14 @@ func TestLoadbalancersCRUD(t *testing.T) { updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &emptyPoolID, } - _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + _, err = listeners.Update(context.TODO(), lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newListener, err = listeners.Get(lbClient, listener.ID).Extract() + newListener, err = listeners.Get(context.TODO(), lbClient, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) @@ -419,14 +420,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Name: &memberName, Weight: &newWeight, } - _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() + _, err = pools.UpdateMember(context.TODO(), lbClient, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() + newMember, err := pools.GetMember(context.TODO(), lbClient, pool.ID, member.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newMember) @@ -439,7 +440,7 @@ func TestLoadbalancersCRUD(t *testing.T) { Weight: &newWeight, } batchMembers := []pools.BatchUpdateMemberOpts{memberOpts} - if err = pools.BatchUpdateMembers(lbClient, pool.ID, batchMembers).ExtractErr(); err != nil { + if err = pools.BatchUpdateMembers(context.TODO(), lbClient, pool.ID, batchMembers).ExtractErr(); err != nil { t.Fatalf("Unable to batch update members") } @@ -447,12 +448,12 @@ func TestLoadbalancersCRUD(t *testing.T) { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newMember, err = pools.GetMember(lbClient, pool.ID, member.ID).Extract() + newMember, err = pools.GetMember(context.TODO(), lbClient, pool.ID, member.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newMember) - pool, err = pools.Get(lbClient, pool.ID).Extract() + pool, err = pools.Get(context.TODO(), lbClient, pool.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, pool) @@ -470,14 +471,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Delay: newDelay, MaxRetriesDown: newMaxRetriesDown, } - _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() + _, err = monitors.Update(context.TODO(), lbClient, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) if err = WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() + newMonitor, err := monitors.Get(context.TODO(), lbClient, monitor.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) @@ -507,7 +508,7 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { th.AssertNoErr(t, err) defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) - newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() + newLB, err := loadbalancers.Get(context.TODO(), lbClient, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newLB) @@ -523,14 +524,14 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { updateListenerOpts := listeners.UpdateOpts{ Description: &listenerDescription, } - _, err = listeners.Update(lbClient, listener.ID, updateListenerOpts).Extract() + _, err = listeners.Update(context.TODO(), lbClient, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newListener, err := listeners.Get(lbClient, listener.ID).Extract() + newListener, err := listeners.Get(context.TODO(), lbClient, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) @@ -543,14 +544,14 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { updatePoolOpts := pools.UpdateOpts{ Description: &poolDescription, } - _, err = pools.Update(lbClient, pool.ID, updatePoolOpts).Extract() + _, err = pools.Update(context.TODO(), 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() + newPool, err := pools.Get(context.TODO(), lbClient, pool.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPool) @@ -563,14 +564,14 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { updateMemberOpts := pools.UpdateMemberOpts{ Weight: &newWeight, } - _, err = pools.UpdateMember(lbClient, pool.ID, member.ID, updateMemberOpts).Extract() + _, err = pools.UpdateMember(context.TODO(), lbClient, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newMember, err := pools.GetMember(lbClient, pool.ID, member.ID).Extract() + newMember, err := pools.GetMember(context.TODO(), lbClient, pool.ID, member.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newMember) @@ -585,14 +586,14 @@ func TestLoadbalancersCascadeCRUD(t *testing.T) { Delay: newDelay, MaxRetriesDown: newMaxRetriesDown, } - _, err = monitors.Update(lbClient, monitor.ID, updateMonitorOpts).Extract() + _, err = monitors.Update(context.TODO(), lbClient, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(lbClient, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newMonitor, err := monitors.Get(lbClient, monitor.ID).Extract() + newMonitor, err := monitors.Get(context.TODO(), lbClient, monitor.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) @@ -621,7 +622,7 @@ func TestLoadbalancersFullyPopulatedCRUD(t *testing.T) { th.AssertNoErr(t, err) defer CascadeDeleteLoadBalancer(t, lbClient, lb.ID) - newLB, err := loadbalancers.Get(lbClient, lb.ID).Extract() + newLB, err := loadbalancers.Get(context.TODO(), lbClient, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newLB) diff --git a/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go b/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go index 352b7e7cca..63e36c35ea 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestMonitorsList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := monitors.List(client, nil).AllPages() + allPages, err := monitors.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list monitors: %v", err) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/pools_test.go b/internal/acceptance/openstack/loadbalancer/v2/pools_test.go index 62469b63de..7ac60041c5 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/pools_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/pools_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestPoolsList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := pools.List(client, nil).AllPages() + allPages, err := pools.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list pools: %v", err) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/providers_test.go b/internal/acceptance/openstack/loadbalancer/v2/providers_test.go index 6326aa9ff6..987864ee36 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/providers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/providers_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestProvidersList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := providers.List(client, nil).AllPages() + allPages, err := providers.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list providers: %v", err) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go index 30e53974a7..2f722c51aa 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "log" "os" "reflect" @@ -22,7 +23,7 @@ func TestQuotasGet(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) - quotasInfo, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() + quotasInfo, err := quotas.Get(context.TODO(), client, os.Getenv("OS_PROJECT_NAME")).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotasInfo) @@ -34,7 +35,7 @@ func TestQuotasUpdate(t *testing.T) { client, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) - originalQuotas, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() + originalQuotas, err := quotas.Get(context.TODO(), client, os.Getenv("OS_PROJECT_NAME")).Extract() th.AssertNoErr(t, err) var quotaUpdateOpts = quotas.UpdateOpts{ @@ -50,7 +51,7 @@ func TestQuotasUpdate(t *testing.T) { quotaUpdateOpts.L7Rule = gophercloud.IntToPointer(105) } - newQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), quotaUpdateOpts).Extract() + newQuotas, err := quotas.Update(context.TODO(), client, os.Getenv("OS_PROJECT_NAME"), quotaUpdateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newQuotas) @@ -73,7 +74,7 @@ func TestQuotasUpdate(t *testing.T) { } // Restore original quotas. - restoredQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), restoredQuotaUpdate).Extract() + restoredQuotas, err := quotas.Update(context.TODO(), client, os.Getenv("OS_PROJECT_NAME"), restoredQuotaUpdate).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, originalQuotas, restoredQuotas) diff --git a/internal/acceptance/openstack/messaging/v2/claims_test.go b/internal/acceptance/openstack/messaging/v2/claims_test.go index 0c2f5aeaa5..54f32f19f0 100644 --- a/internal/acceptance/openstack/messaging/v2/claims_test.go +++ b/internal/acceptance/openstack/messaging/v2/claims_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -45,7 +46,7 @@ func TestCRUDClaim(t *testing.T) { for _, claimID := range claimIDs { t.Logf("Attempting to update claim: %s", claimID) - updateErr := claims.Update(client, createdQueueName, claimID, updateOpts).ExtractErr() + updateErr := claims.Update(context.TODO(), client, createdQueueName, claimID, updateOpts).ExtractErr() if updateErr != nil { t.Fatalf("Unable to update claim %s: %v", claimID, err) diff --git a/internal/acceptance/openstack/messaging/v2/message_test.go b/internal/acceptance/openstack/messaging/v2/message_test.go index f3b67f9098..88ebb416d9 100644 --- a/internal/acceptance/openstack/messaging/v2/message_test.go +++ b/internal/acceptance/openstack/messaging/v2/message_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -38,7 +39,7 @@ func TestListMessages(t *testing.T) { listOpts := messages.ListOpts{} pager := messages.List(client, createdQueueName, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) @@ -91,7 +92,7 @@ func TestGetMessages(t *testing.T) { var messageIDs []string pager := messages.List(client, createdQueueName, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) @@ -108,7 +109,7 @@ func TestGetMessages(t *testing.T) { IDs: messageIDs, } t.Logf("Attempting to get messages from queue %s with ids: %v", createdQueueName, messageIDs) - messagesList, err := messages.GetMessages(client, createdQueueName, getMessageOpts).Extract() + messagesList, err := messages.GetMessages(context.TODO(), client, createdQueueName, getMessageOpts).Extract() if err != nil { t.Fatalf("Unable to get messages from queue: %s", createdQueueName) } @@ -138,7 +139,7 @@ func TestGetMessage(t *testing.T) { var messageIDs []string pager := messages.List(client, createdQueueName, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) @@ -153,7 +154,7 @@ func TestGetMessage(t *testing.T) { for _, messageID := range messageIDs { t.Logf("Attempting to get message from queue %s: %s", createdQueueName, messageID) - message, getErr := messages.Get(client, createdQueueName, messageID).Extract() + message, getErr := messages.Get(context.TODO(), client, createdQueueName, messageID).Extract() if getErr != nil { t.Fatalf("Unable to get message from queue %s: %s", createdQueueName, messageID) } @@ -185,7 +186,7 @@ func TestDeleteMessagesIDs(t *testing.T) { var messageIDs []string pager := messages.List(client, createdQueueName, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) @@ -204,7 +205,7 @@ func TestDeleteMessagesIDs(t *testing.T) { } t.Logf("Attempting to delete messages: %v", messageIDs) - deleteErr := messages.DeleteMessages(client, createdQueueName, deleteOpts).ExtractErr() + deleteErr := messages.DeleteMessages(context.TODO(), client, createdQueueName, deleteOpts).ExtractErr() if deleteErr != nil { t.Fatalf("Unable to delete messages: %v", deleteErr) } @@ -247,7 +248,7 @@ func TestDeleteMessagesPop(t *testing.T) { } t.Logf("Attempting to Pop last %v messages.", popNumber) - popMessages, deleteErr := messages.PopMessages(client, createdQueueName, PopOpts).Extract() + popMessages, deleteErr := messages.PopMessages(context.TODO(), client, createdQueueName, PopOpts).Extract() if deleteErr != nil { t.Fatalf("Unable to Pop messages: %v", deleteErr) } @@ -282,7 +283,7 @@ func TestDeleteMessage(t *testing.T) { var messageIDs []string pager := messages.List(client, createdQueueName, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allMessages, err := messages.ExtractMessages(page) if err != nil { t.Fatalf("Unable to extract messages: %v", err) @@ -298,7 +299,7 @@ func TestDeleteMessage(t *testing.T) { for _, messageID := range messageIDs { t.Logf("Attempting to delete message from queue %s: %s", createdQueueName, messageID) deleteOpts := messages.DeleteOpts{} - deleteErr := messages.Delete(client, createdQueueName, messageID, deleteOpts).ExtractErr() + deleteErr := messages.Delete(context.TODO(), client, createdQueueName, messageID, deleteOpts).ExtractErr() if deleteErr != nil { t.Fatalf("Unable to delete message from queue %s: %s", createdQueueName, messageID) } else { diff --git a/internal/acceptance/openstack/messaging/v2/messaging.go b/internal/acceptance/openstack/messaging/v2/messaging.go index afb7dbb147..5f0f1231bb 100644 --- a/internal/acceptance/openstack/messaging/v2/messaging.go +++ b/internal/acceptance/openstack/messaging/v2/messaging.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "strings" "testing" @@ -26,7 +27,7 @@ func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error Extra: map[string]interface{}{"description": "Test Queue for Gophercloud acceptance tests."}, } - createErr := queues.Create(client, createOpts).ExtractErr() + createErr := queues.Create(context.TODO(), client, createOpts).ExtractErr() if createErr != nil { t.Fatalf("Unable to create Queue: %v", createErr) } @@ -39,7 +40,7 @@ func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error func DeleteQueue(t *testing.T, client *gophercloud.ServiceClient, queueName string) { t.Logf("Attempting to delete Queue: %s", queueName) - err := queues.Delete(client, queueName).ExtractErr() + err := queues.Delete(context.TODO(), client, queueName).ExtractErr() if err != nil { t.Fatalf("Unable to delete Queue %s: %v", queueName, err) } @@ -49,7 +50,7 @@ func DeleteQueue(t *testing.T, client *gophercloud.ServiceClient, queueName stri func GetQueue(t *testing.T, client *gophercloud.ServiceClient, queueName string) (queues.QueueDetails, error) { t.Logf("Attempting to get Queue: %s", queueName) - queue, err := queues.Get(client, queueName).Extract() + queue, err := queues.Get(context.TODO(), client, queueName).Extract() if err != nil { t.Fatalf("Unable to get Queue %s: %v", queueName, err) } @@ -64,7 +65,7 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient, queueName stri Methods: []queues.ShareMethod{queues.MethodPost}, } - share, err := queues.Share(client, queueName, shareOpts).Extract() + share, err := queues.Share(context.TODO(), client, queueName, shareOpts).Extract() return share, err } @@ -78,7 +79,7 @@ func CreateMessage(t *testing.T, client *gophercloud.ServiceClient, queueName st }, } - resource, err := messages.Create(client, queueName, createOpts).Extract() + resource, err := messages.Create(context.TODO(), client, queueName, createOpts).Extract() if err != nil { t.Fatalf("Unable to add message to queue %s: %v", queueName, err) } else { @@ -95,7 +96,7 @@ func ListMessages(t *testing.T, client *gophercloud.ServiceClient, queueName str t.Logf("Attempting to list messages on queue: %s", queueName) pager := messages.List(client, queueName, listOpts) - err := pager.EachPage(func(page pagination.Page) (bool, error) { + err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allMessages, listErr = messages.ExtractMessages(page) if listErr != nil { t.Fatalf("Unable to extract messages: %v", listErr) @@ -114,7 +115,7 @@ func CreateClaim(t *testing.T, client *gophercloud.ServiceClient, queueName stri createOpts := claims.CreateOpts{} t.Logf("Attempting to create claim on queue: %s", queueName) - claimedMessages, err := claims.Create(client, queueName, createOpts).Extract() + claimedMessages, err := claims.Create(context.TODO(), client, queueName, createOpts).Extract() tools.PrintResource(t, claimedMessages) if err != nil { t.Fatalf("Unable to create claim: %v", err) @@ -125,7 +126,7 @@ func CreateClaim(t *testing.T, client *gophercloud.ServiceClient, queueName stri func GetClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string, claimID string) (*claims.Claim, error) { t.Logf("Attempting to get claim: %s", claimID) - claim, err := claims.Get(client, queueName, claimID).Extract() + claim, err := claims.Get(context.TODO(), client, queueName, claimID).Extract() if err != nil { t.Fatalf("Unable to get claim: %s", claimID) } @@ -135,7 +136,7 @@ func GetClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string, func DeleteClaim(t *testing.T, client *gophercloud.ServiceClient, queueName string, claimID string) error { t.Logf("Attempting to delete claim: %s", claimID) - err := claims.Delete(client, queueName, claimID).ExtractErr() + err := claims.Delete(context.TODO(), client, queueName, claimID).ExtractErr() if err != nil { t.Fatalf("Unable to delete claim: %s", claimID) } diff --git a/internal/acceptance/openstack/messaging/v2/queue_test.go b/internal/acceptance/openstack/messaging/v2/queue_test.go index 478573f936..b8eaaa0cf6 100644 --- a/internal/acceptance/openstack/messaging/v2/queue_test.go +++ b/internal/acceptance/openstack/messaging/v2/queue_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -23,7 +24,7 @@ func TestCRUDQueues(t *testing.T) { createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) - createdQueue, err := queues.Get(client, createdQueueName).Extract() + createdQueue, err := queues.Get(context.TODO(), client, createdQueueName).Extract() tools.PrintResource(t, createdQueue) tools.PrintResource(t, createdQueue.Extra) @@ -42,7 +43,7 @@ func TestCRUDQueues(t *testing.T) { } t.Logf("Attempting to update Queue: %s", createdQueueName) - updateResult, updateErr := queues.Update(client, createdQueueName, updateOpts).Extract() + updateResult, updateErr := queues.Update(context.TODO(), client, createdQueueName, updateOpts).Extract() if updateErr != nil { t.Fatalf("Unable to update Queue %s: %v", createdQueueName, updateErr) } @@ -74,7 +75,7 @@ func TestListQueues(t *testing.T) { } pager := queues.List(client, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allQueues, err := queues.ExtractQueues(page) if err != nil { t.Fatalf("Unable to extract Queues: %v", err) @@ -99,7 +100,7 @@ func TestStatQueue(t *testing.T) { createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) - queueStats, err := queues.GetStats(client, createdQueueName).Extract() + queueStats, err := queues.GetStats(context.TODO(), client, createdQueueName).Extract() if err != nil { t.Fatalf("Unable to stat queue: %v", err) } @@ -148,7 +149,7 @@ func TestPurge(t *testing.T) { } t.Logf("Attempting to purge queue: %s", queueName) - purgeErr := queues.Purge(client, queueName, purgeOpts).ExtractErr() + purgeErr := queues.Purge(context.TODO(), client, queueName, purgeOpts).ExtractErr() if purgeErr != nil { t.Fatalf("Unable to purge queue %s: %v", queueName, purgeErr) } diff --git a/internal/acceptance/openstack/networking/v2/apiversion_test.go b/internal/acceptance/openstack/networking/v2/apiversion_test.go index ce28c3c606..6237210ea9 100644 --- a/internal/acceptance/openstack/networking/v2/apiversion_test.go +++ b/internal/acceptance/openstack/networking/v2/apiversion_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestAPIVersionsList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - allPages, err := apiversions.ListVersions(client).AllPages() + allPages, err := apiversions.ListVersions(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list api versions: %v", err) } @@ -38,7 +39,7 @@ func TestAPIResourcesList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - allPages, err := apiversions.ListVersionResources(client, "v2.0").AllPages() + allPages, err := apiversions.ListVersionResources(client, "v2.0").AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list api version resources: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extension_test.go b/internal/acceptance/openstack/networking/v2/extension_test.go index d6d0212812..dd8f563135 100644 --- a/internal/acceptance/openstack/networking/v2/extension_test.go +++ b/internal/acceptance/openstack/networking/v2/extension_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestExtensionsList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - allPages, err := extensions.List(client).AllPages() + allPages, err := extensions.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list extensions: %v", err) } @@ -38,7 +39,7 @@ func TestExtensionGet(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(client, "router").Extract() + extension, err := extensions.Get(context.TODO(), client, "router").Extract() if err != nil { t.Fatalf("Unable to get extension port-security: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index 878a85e2d5..c68facb7ac 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -4,6 +4,7 @@ package agents import ( + "context" "testing" "time" @@ -23,7 +24,7 @@ func TestAgentsRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := agents.List(client, agents.ListOpts{}).AllPages() + allPages, err := agents.List(client, agents.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allAgents, err := agents.ExtractAgents(allPages) @@ -36,7 +37,7 @@ func TestAgentsRUD(t *testing.T) { listOpts := &agents.ListOpts{ AgentType: "DHCP agent", } - allPages, err = agents.List(client, listOpts).AllPages() + allPages, err = agents.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allAgents, err = agents.ExtractAgents(allPages) @@ -48,7 +49,7 @@ func TestAgentsRUD(t *testing.T) { // List DHCP agent networks for _, agent := range allAgents { t.Logf("Retrieving DHCP networks from the agent: %s", agent.ID) - networks, err := agents.ListDHCPNetworks(client, agent.ID).Extract() + networks, err := agents.ListDHCPNetworks(context.TODO(), client, agent.ID).Extract() th.AssertNoErr(t, err) for _, network := range networks { t.Logf("Retrieved %q network, assigned to a %q DHCP agent", network.ID, agent.ID) @@ -56,7 +57,7 @@ func TestAgentsRUD(t *testing.T) { } // Get a single agent - agent, err := agents.Get(client, allAgents[0].ID).Extract() + agent, err := agents.Get(context.TODO(), client, allAgents[0].ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, agent) @@ -65,12 +66,12 @@ func TestAgentsRUD(t *testing.T) { updateOpts := &agents.UpdateOpts{ Description: &description, } - agent, err = agents.Update(client, allAgents[0].ID, updateOpts).Extract() + agent, err = agents.Update(context.TODO(), client, allAgents[0].ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, agent.Description, description) // Restore original description - agent, err = agents.Update(client, allAgents[0].ID, &agents.UpdateOpts{Description: &allAgents[0].Description}).Extract() + agent, err = agents.Update(context.TODO(), client, allAgents[0].ID, &agents.UpdateOpts{Description: &allAgents[0].Description}).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, agent.Description, allAgents[0].Description) @@ -82,17 +83,17 @@ func TestAgentsRUD(t *testing.T) { opts := &agents.ScheduleDHCPNetworkOpts{ NetworkID: network.ID, } - err = agents.ScheduleDHCPNetwork(client, allAgents[0].ID, opts).ExtractErr() + err = agents.ScheduleDHCPNetwork(context.TODO(), client, allAgents[0].ID, opts).ExtractErr() th.AssertNoErr(t, err) - err = agents.RemoveDHCPNetwork(client, allAgents[0].ID, network.ID).ExtractErr() + err = agents.RemoveDHCPNetwork(context.TODO(), client, allAgents[0].ID, network.ID).ExtractErr() th.AssertNoErr(t, err) // skip this part t.Skip("Skip DHCP agent deletion") // Delete a DHCP agent - err = agents.Delete(client, allAgents[0].ID).ExtractErr() + err = agents.Delete(context.TODO(), client, allAgents[0].ID).ExtractErr() th.AssertNoErr(t, err) } @@ -107,7 +108,7 @@ func TestBGPAgentRUD(t *testing.T) { listOpts := &agents.ListOpts{ AgentType: "BGP Dynamic Routing Agent", } - allPages, err := agents.List(client, listOpts).AllPages() + allPages, err := agents.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allAgents, err := agents.ExtractAgents(allPages) @@ -119,7 +120,7 @@ func TestBGPAgentRUD(t *testing.T) { // Create a BGP Speaker bgpSpeaker, err := spk.CreateBGPSpeaker(t, client) th.AssertNoErr(t, err) - pages, err := agents.ListDRAgentHostingBGPSpeakers(client, bgpSpeaker.ID).AllPages() + pages, err := agents.ListDRAgentHostingBGPSpeakers(client, bgpSpeaker.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) bgpAgents, err := agents.ExtractAgents(pages) th.AssertNoErr(t, err) @@ -131,7 +132,7 @@ func TestBGPAgentRUD(t *testing.T) { 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() + bgpAgent, err := agents.Get(context.TODO(), client, agt.ID).Extract() th.AssertNoErr(t, err) numOfSpeakers := int(bgpAgent.Configurations["bgp_speakers"].(float64)) flag = flag && (numOfSpeakers == 1) @@ -141,12 +142,12 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) // Remove the BGP Speaker from the first agent - err = agents.RemoveBGPSpeaker(client, bgpAgents[0].ID, bgpSpeaker.ID).ExtractErr() + err = agents.RemoveBGPSpeaker(context.TODO(), 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) err = tools.WaitForTimeout( func() (bool, error) { - bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract() + bgpAgent, err := agents.Get(context.TODO(), client, bgpAgents[0].ID).Extract() th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) @@ -156,25 +157,25 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) // Remove all BGP Speakers from the agent - pages, err = agents.ListBGPSpeakers(client, bgpAgents[0].ID).AllPages() + pages, err = agents.ListBGPSpeakers(client, bgpAgents[0].ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allSpeakers, err := agents.ExtractBGPSpeakers(pages) th.AssertNoErr(t, err) for _, speaker := range allSpeakers { - th.AssertNoErr(t, agents.RemoveBGPSpeaker(client, bgpAgents[0].ID, speaker.ID).ExtractErr()) + th.AssertNoErr(t, agents.RemoveBGPSpeaker(context.TODO(), client, bgpAgents[0].ID, speaker.ID).ExtractErr()) } // Schedule a BGP Speaker to an agent opts := agents.ScheduleBGPSpeakerOpts{ SpeakerID: bgpSpeaker.ID, } - err = agents.ScheduleBGPSpeaker(client, bgpAgents[0].ID, opts).ExtractErr() + err = agents.ScheduleBGPSpeaker(context.TODO(), client, bgpAgents[0].ID, opts).ExtractErr() th.AssertNoErr(t, err) 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() + bgpAgent, err := agents.Get(context.TODO(), client, bgpAgents[0].ID).Extract() th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) @@ -184,12 +185,12 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) // Delete the BGP Speaker - speakers.Delete(client, bgpSpeaker.ID).ExtractErr() + speakers.Delete(context.TODO(), client, bgpSpeaker.ID).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully deleted the BGP Speaker, %s", bgpSpeaker.ID) err = tools.WaitForTimeout( func() (bool, error) { - bgpAgent, err := agents.Get(client, bgpAgents[0].ID).Extract() + bgpAgent, err := agents.Get(context.TODO(), client, bgpAgents[0].ID).Extract() th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) diff --git a/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go index f9890fba93..8f029269b0 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "fmt" "sort" "testing" @@ -26,13 +27,13 @@ func createNetworkWithTags(t *testing.T, client *gophercloud.ServiceClient, tags // docs say list of tags, but it's a set e.g no duplicates Tags: tags, } - rtags, err := attributestags.ReplaceAll(client, "networks", network.ID, tagReplaceAllOpts).Extract() + rtags, err := attributestags.ReplaceAll(context.TODO(), client, "networks", network.ID, tagReplaceAllOpts).Extract() th.AssertNoErr(t, err) sort.Strings(rtags) // Ensure ordering, older OpenStack versions aren't sorted... th.AssertDeepEquals(t, rtags, tags) // Verify the tags are also set in the object Get response - gnetwork, err := networks.Get(client, network.ID).Extract() + gnetwork, err := networks.Get(context.TODO(), client, network.ID).Extract() th.AssertNoErr(t, err) rtags = gnetwork.Tags sort.Strings(rtags) @@ -49,36 +50,36 @@ func TestTags(t *testing.T) { defer networking.DeleteNetwork(t, client, network.ID) // Add a tag - err = attributestags.Add(client, "networks", network.ID, "d").ExtractErr() + err = attributestags.Add(context.TODO(), client, "networks", network.ID, "d").ExtractErr() th.AssertNoErr(t, err) // Delete a tag - err = attributestags.Delete(client, "networks", network.ID, "a").ExtractErr() + err = attributestags.Delete(context.TODO(), client, "networks", network.ID, "a").ExtractErr() th.AssertNoErr(t, err) // Verify expected tags are set in the List response - tags, err := attributestags.List(client, "networks", network.ID).Extract() + tags, err := attributestags.List(context.TODO(), client, "networks", network.ID).Extract() th.AssertNoErr(t, err) sort.Strings(tags) th.AssertDeepEquals(t, []string{"b", "c", "d"}, tags) // Confirm tags exist/don't exist - exists, err := attributestags.Confirm(client, "networks", network.ID, "d").Extract() + exists, err := attributestags.Confirm(context.TODO(), client, "networks", network.ID, "d").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) - noexists, err := attributestags.Confirm(client, "networks", network.ID, "a").Extract() + noexists, err := attributestags.Confirm(context.TODO(), client, "networks", network.ID, "a").Extract() th.AssertEquals(t, false, noexists) // Delete all tags - err = attributestags.DeleteAll(client, "networks", network.ID).ExtractErr() + err = attributestags.DeleteAll(context.TODO(), client, "networks", network.ID).ExtractErr() th.AssertNoErr(t, err) - tags, err = attributestags.List(client, "networks", network.ID).Extract() + tags, err = attributestags.List(context.TODO(), client, "networks", network.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(tags)) } func listNetworkWithTagOpts(t *testing.T, client *gophercloud.ServiceClient, listOpts networks.ListOpts) (ids []string) { - allPages, err := networks.List(client, listOpts).AllPages() + allPages, err := networks.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allNetworks, err := networks.ExtractNetworks(allPages) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go index caf750a954..5cd0959765 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go @@ -1,6 +1,7 @@ package peers import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -20,7 +21,7 @@ func TestBGPPeerCRUD(t *testing.T) { th.AssertNoErr(t, err) // Get a BGP Peer - bgpPeerGot, err := peers.Get(client, bgpPeerCreated.ID).Extract() + bgpPeerGot, err := peers.Get(context.TODO(), client, bgpPeerCreated.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, bgpPeerCreated.ID, bgpPeerGot.ID) th.AssertEquals(t, bgpPeerCreated.Name, bgpPeerGot.Name) @@ -31,13 +32,13 @@ func TestBGPPeerCRUD(t *testing.T) { Name: newBGPPeerName, Password: tools.MakeNewPassword(""), } - bgpPeerUpdated, err := peers.Update(client, bgpPeerGot.ID, updateBGPOpts).Extract() + bgpPeerUpdated, err := peers.Update(context.TODO(), 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() + allPages, err := peers.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allPeers, err := peers.ExtractBGPPeers(allPages) th.AssertNoErr(t, err) @@ -48,10 +49,10 @@ func TestBGPPeerCRUD(t *testing.T) { // Delete a BGP Peer t.Logf("Attempting to delete BGP Peer: %s", bgpPeerUpdated.Name) - err = peers.Delete(client, bgpPeerGot.ID).ExtractErr() + err = peers.Delete(context.TODO(), client, bgpPeerGot.ID).ExtractErr() th.AssertNoErr(t, err) - bgpPeerGot, err = peers.Get(client, bgpPeerGot.ID).Extract() + bgpPeerGot, err = peers.Get(context.TODO(), client, bgpPeerGot.ID).Extract() th.AssertErr(t, err) t.Logf("BGP Peer %s deleted", bgpPeerUpdated.Name) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go index 5dc21c3ebb..198673498b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/peers.go @@ -1,6 +1,7 @@ package peers import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -18,7 +19,7 @@ func CreateBGPPeer(t *testing.T, client *gophercloud.ServiceClient) (*peers.BGPP opts.PeerIP = "192.168.0.1" t.Logf("Attempting to create BGP Peer: %s", opts.Name) - bgpPeer, err := peers.Create(client, opts).Extract() + bgpPeer, err := peers.Create(context.TODO(), client, opts).Extract() if err != nil { return bgpPeer, err } diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go index bd51df0cc2..ed0a381b15 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go @@ -1,6 +1,7 @@ package speakers import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -26,7 +27,7 @@ func TestBGPSpeakerCRUD(t *testing.T) { th.AssertNoErr(t, err) // List BGP Speakers - allPages, err := speakers.List(client).AllPages() + allPages, err := speakers.List(client).AllPages(context.TODO()) th.AssertNoErr(t, err) allSpeakers, err := speakers.ExtractBGPSpeakers(allPages) th.AssertNoErr(t, err) @@ -46,37 +47,37 @@ func TestBGPSpeakerCRUD(t *testing.T) { AdvertiseTenantNetworks: false, AdvertiseFloatingIPHostRoutes: true, } - speakerUpdated, err := speakers.Update(client, bgpSpeaker.ID, opts).Extract() + speakerUpdated, err := speakers.Update(context.TODO(), 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) // Get a BGP Speaker - bgpSpeakerGot, err := speakers.Get(client, bgpSpeaker.ID).Extract() + bgpSpeakerGot, err := speakers.Get(context.TODO(), 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() + _, err = speakers.AddBGPPeer(context.TODO(), client, bgpSpeaker.ID, addBGPPeerOpts).Extract() th.AssertNoErr(t, err) - speakerGot, err := speakers.Get(client, bgpSpeaker.ID).Extract() + speakerGot, err := speakers.Get(context.TODO(), 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() + err = speakers.RemoveBGPPeer(context.TODO(), client, bgpSpeaker.ID, removeBGPPeerOpts).ExtractErr() th.AssertNoErr(t, err) - speakerGot, err = speakers.Get(client, bgpSpeaker.ID).Extract() + speakerGot, err = speakers.Get(context.TODO(), 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) // GetAdvertisedRoutes - pages, err := speakers.GetAdvertisedRoutes(client, bgpSpeaker.ID).AllPages() + pages, err := speakers.GetAdvertisedRoutes(client, bgpSpeaker.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) routes, err := speakers.ExtractAdvertisedRoutes(pages) th.AssertNoErr(t, err) @@ -85,24 +86,24 @@ func TestBGPSpeakerCRUD(t *testing.T) { // AddGatewayNetwork optsAddGatewayNetwork := speakers.AddGatewayNetworkOpts{NetworkID: network.ID} - r, err := speakers.AddGatewayNetwork(client, bgpSpeaker.ID, optsAddGatewayNetwork).Extract() + r, err := speakers.AddGatewayNetwork(context.TODO(), 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() + err = speakers.RemoveGatewayNetwork(context.TODO(), 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() + err = peers.Delete(context.TODO(), 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() + err = speakers.Delete(context.TODO(), client, bgpSpeaker.ID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go index 4bb3750eb1..d9e620144d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/speakers.go @@ -1,6 +1,7 @@ package speakers import ( + "context" "strconv" "testing" @@ -21,7 +22,7 @@ func CreateBGPSpeaker(t *testing.T, client *gophercloud.ServiceClient) (*speaker } t.Logf("Attempting to create BGP Speaker: %s", opts.Name) - bgpSpeaker, err := speakers.Create(client, opts).Extract() + bgpSpeaker, err := speakers.Create(context.TODO(), client, opts).Extract() if err != nil { return bgpSpeaker, err } diff --git a/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go index 29ebcdf71e..7c1c513d12 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns.go @@ -1,6 +1,7 @@ package dns import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -54,7 +55,7 @@ func CreatePortDNS(t *testing.T, client *gophercloud.ServiceClient, networkID, s var port PortWithDNSExt - err := ports.Create(client, createOpts).ExtractInto(&port) + err := ports.Create(context.TODO(), client, createOpts).ExtractInto(&port) if err != nil { return &port, err } @@ -87,7 +88,7 @@ func CreateFloatingIPDNS(t *testing.T, client *gophercloud.ServiceClient, networ } var floatingIP FloatingIPWithDNSExt - err := floatingips.Create(client, createOpts).ExtractInto(&floatingIP) + err := floatingips.Create(context.TODO(), client, createOpts).ExtractInto(&floatingIP) if err != nil { return &floatingIP, err } @@ -121,7 +122,7 @@ func CreateNetworkDNS(t *testing.T, client *gophercloud.ServiceClient, dnsDomain var network NetworkWithDNSExt - err := networks.Create(client, createOpts).ExtractInto(&network) + err := networks.Create(context.TODO(), client, createOpts).ExtractInto(&network) if err != nil { return &network, err } diff --git a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index 134b951b17..bdd9e9d7d8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -4,6 +4,7 @@ package dns import ( + "context" "os" "testing" @@ -26,7 +27,7 @@ func TestDNSPortCRUDL(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "dns-integration").Extract() + extension, err := extensions.Get(context.TODO(), client, "dns-integration").Extract() if err != nil { t.Skip("This test requires dns-integration Neutron extension") } @@ -60,7 +61,7 @@ func TestDNSPortCRUDL(t *testing.T) { } var listedPorts []PortWithDNSExt i := 0 - err = ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + err = ports.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { i++ err := ports.ExtractPortsInto(page, &listedPorts) if err != nil { @@ -84,7 +85,7 @@ func TestDNSPortCRUDL(t *testing.T) { DNSName: "foo", } i = 0 - err = ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + err = ports.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { i++ err := ports.ExtractPortsInto(page, &listedPorts) if err != nil { @@ -105,7 +106,7 @@ func TestDNSPortCRUDL(t *testing.T) { // Get port var getPort PortWithDNSExt - err = ports.Get(client, port.ID).ExtractInto(&getPort) + err = ports.Get(context.TODO(), client, port.ID).ExtractInto(&getPort) th.AssertNoErr(t, err) tools.PrintResource(t, getPort) @@ -126,7 +127,7 @@ func TestDNSPortCRUDL(t *testing.T) { } var newPort PortWithDNSExt - err = ports.Update(client, port.ID, updateOpts).ExtractInto(&newPort) + err = ports.Update(context.TODO(), client, port.ID, updateOpts).ExtractInto(&newPort) th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -136,7 +137,7 @@ func TestDNSPortCRUDL(t *testing.T) { // Get updated port var getNewPort PortWithDNSExt - err = ports.Get(client, port.ID).ExtractInto(&getNewPort) + err = ports.Get(context.TODO(), client, port.ID).ExtractInto(&getNewPort) th.AssertNoErr(t, err) tools.PrintResource(t, getNewPort) @@ -153,7 +154,7 @@ func TestDNSFloatingIPCRDL(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "dns-integration").Extract() + extension, err := extensions.Get(context.TODO(), client, "dns-integration").Extract() if err != nil { t.Skip("This test requires dns-integration Neutron extension") } @@ -202,7 +203,7 @@ func TestDNSFloatingIPCRDL(t *testing.T) { // Get floating IP var getFip FloatingIPWithDNSExt - err = floatingips.Get(client, fip.ID).ExtractInto(&getFip) + err = floatingips.Get(context.TODO(), client, fip.ID).ExtractInto(&getFip) th.AssertNoErr(t, err) tools.PrintResource(t, getFip) @@ -215,7 +216,7 @@ func TestDNSNetwork(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "dns-integration").Extract() + extension, err := extensions.Get(context.TODO(), client, "dns-integration").Extract() if err != nil { t.Skip("This test requires dns-integration Neutron extension") } @@ -229,7 +230,7 @@ func TestDNSNetwork(t *testing.T) { // Get network var getNetwork NetworkWithDNSExt - err = networks.Get(client, network.ID).ExtractInto(&getNetwork) + err = networks.Get(context.TODO(), client, network.ID).ExtractInto(&getNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, getNetwork) @@ -250,7 +251,7 @@ func TestDNSNetwork(t *testing.T) { } var newNetwork NetworkWithDNSExt - err = networks.Update(client, network.ID, updateOpts).ExtractInto(&newNetwork) + err = networks.Update(context.TODO(), client, network.ID, updateOpts).ExtractInto(&newNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) @@ -260,7 +261,7 @@ func TestDNSNetwork(t *testing.T) { // Get updated network var getNewNetwork NetworkWithDNSExt - err = networks.Get(client, network.ID).ExtractInto(&getNewNetwork) + err = networks.Get(context.TODO(), client, network.ID).ExtractInto(&getNewNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, getNewNetwork) diff --git a/internal/acceptance/openstack/networking/v2/extensions/extensions.go b/internal/acceptance/openstack/networking/v2/extensions/extensions.go index 9554888f99..4de1401f4d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/internal/acceptance/openstack/networking/v2/extensions/extensions.go @@ -1,6 +1,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -35,7 +36,7 @@ func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*ne External: &isExternal, } - network, err := networks.Create(client, createOpts).Extract() + network, err := networks.Create(context.TODO(), client, createOpts).Extract() if err != nil { return network, err } @@ -66,7 +67,7 @@ func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient SecurityGroups: &[]string{secGroupID}, } - port, err := ports.Create(client, createOpts).Extract() + port, err := ports.Create(context.TODO(), client, createOpts).Extract() if err != nil { return port, err } @@ -93,7 +94,7 @@ func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*grou Description: secGroupDescription, } - secGroup, err := groups.Create(client, createOpts).Extract() + secGroup, err := groups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return secGroup, err } @@ -126,7 +127,7 @@ func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, se Protocol: rules.ProtocolTCP, } - rule, err := rules.Create(client, createOpts).Extract() + rule, err := rules.Create(context.TODO(), client, createOpts).Extract() if err != nil { return rule, err } @@ -145,7 +146,7 @@ func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, se func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) { t.Logf("Attempting to delete security group: %s", secGroupID) - err := groups.Delete(client, secGroupID).ExtractErr() + err := groups.Delete(context.TODO(), client, secGroupID).ExtractErr() if err != nil { t.Fatalf("Unable to delete security group: %v", err) } @@ -157,7 +158,7 @@ func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, secGro func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { t.Logf("Attempting to delete security group rule: %s", ruleID) - err := rules.Delete(client, ruleID).ExtractErr() + err := rules.Delete(context.TODO(), client, ruleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete security group rule: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go index 74b7392813..69706ddef3 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go @@ -4,6 +4,7 @@ package fwaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -49,10 +50,10 @@ func TestFirewallCRUD(t *testing.T) { PolicyID: policy.ID, } - _, err = firewalls.Update(client, firewall.ID, fwUpdateOpts).Extract() + _, err = firewalls.Update(context.TODO(), client, firewall.ID, fwUpdateOpts).Extract() th.AssertNoErr(t, err) - newFirewall, err := firewalls.Get(client, firewall.ID).Extract() + newFirewall, err := firewalls.Get(context.TODO(), client, firewall.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) @@ -60,7 +61,7 @@ func TestFirewallCRUD(t *testing.T) { th.AssertEquals(t, newFirewall.Description, fwDescription) th.AssertEquals(t, newFirewall.PolicyID, policy.ID) - allPages, err := firewalls.List(client, nil).AllPages() + allPages, err := firewalls.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allFirewalls, err := firewalls.ExtractFirewalls(allPages) @@ -118,10 +119,10 @@ func TestFirewallCRUDRouter(t *testing.T) { []string{router2.ID}, } - _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() + _, err = firewalls.Update(context.TODO(), client, firewall.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newFirewall, err := firewalls.Get(client, firewall.ID).Extract() + newFirewall, err := firewalls.Get(context.TODO(), client, firewall.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) @@ -165,10 +166,10 @@ func TestFirewallCRUDRemoveRouter(t *testing.T) { []string{}, } - _, err = firewalls.Update(client, firewall.ID, updateOpts).Extract() + _, err = firewalls.Update(context.TODO(), client, firewall.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newFirewall, err := firewalls.Get(client, firewall.ID).Extract() + newFirewall, err := firewalls.Get(context.TODO(), client, firewall.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFirewall) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index 7c0a3abea4..616eef4288 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -1,6 +1,7 @@ package fwaas import ( + "context" "fmt" "strconv" "testing" @@ -30,7 +31,7 @@ func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID st AdminStateUp: &iTrue, } - firewall, err := firewalls.Create(client, createOpts).Extract() + firewall, err := firewalls.Create(context.TODO(), client, createOpts).Extract() if err != nil { return firewall, err } @@ -68,7 +69,7 @@ func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, pol RouterIDs: []string{routerID}, } - firewall, err := firewalls.Create(client, createOpts).Extract() + firewall, err := firewalls.Create(context.TODO(), client, createOpts).Extract() if err != nil { return firewall, err } @@ -102,7 +103,7 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string }, } - policy, err := policies.Create(client, createOpts).Extract() + policy, err := policies.Create(context.TODO(), client, createOpts).Extract() if err != nil { return policy, err } @@ -139,7 +140,7 @@ func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, e DestinationPort: destinationPort, } - rule, err := rules.Create(client, createOpts).Extract() + rule, err := rules.Create(context.TODO(), client, createOpts).Extract() if err != nil { return rule, err } @@ -162,7 +163,7 @@ func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, e func DeleteFirewall(t *testing.T, client *gophercloud.ServiceClient, firewallID string) { t.Logf("Attempting to delete firewall: %s", firewallID) - err := firewalls.Delete(client, firewallID).ExtractErr() + err := firewalls.Delete(context.TODO(), client, firewallID).ExtractErr() if err != nil { t.Fatalf("Unable to delete firewall %s: %v", firewallID, err) } @@ -181,7 +182,7 @@ func DeleteFirewall(t *testing.T, client *gophercloud.ServiceClient, firewallID func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete policy: %s", policyID) - err := policies.Delete(client, policyID).ExtractErr() + err := policies.Delete(context.TODO(), client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete policy %s: %v", policyID, err) } @@ -195,7 +196,7 @@ func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID stri func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { t.Logf("Attempting to delete rule: %s", ruleID) - err := rules.Delete(client, ruleID).ExtractErr() + err := rules.Delete(context.TODO(), client, ruleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete rule %s: %v", ruleID, err) } @@ -206,7 +207,7 @@ func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) // WaitForFirewallState will wait until a firewall reaches a given state. func WaitForFirewallState(client *gophercloud.ServiceClient, firewallID, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := firewalls.Get(client, firewallID).Extract() + current, err := firewalls.Get(context.TODO(), client, firewallID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go index 04b016ec2c..06375f01db 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go @@ -4,6 +4,7 @@ package fwaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -36,17 +37,17 @@ func TestPolicyCRUD(t *testing.T) { Description: &description, } - _, err = policies.Update(client, policy.ID, updateOpts).Extract() + _, err = policies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newPolicy, err := policies.Get(client, policy.ID).Extract() + newPolicy, err := policies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Name, name) th.AssertEquals(t, newPolicy.Description, description) - allPages, err := policies.List(client, nil).AllPages() + allPages, err := policies.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go index 6d0b693ee1..15254dc35a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go @@ -4,6 +4,7 @@ package fwaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -28,15 +29,15 @@ func TestRuleCRUD(t *testing.T) { Description: &ruleDescription, } - _, err = rules.Update(client, rule.ID, updateOpts).Extract() + _, err = rules.Update(context.TODO(), client, rule.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newRule, err := rules.Get(client, rule.ID).Extract() + newRule, err := rules.Get(context.TODO(), client, rule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) - allPages, err := rules.List(client, nil).AllPages() + allPages, err := rules.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allRules, err := rules.ExtractRules(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go index 580d64cb9a..805b2265be 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/fwaas_v2.go @@ -1,6 +1,7 @@ package fwaas_v2 import ( + "context" "fmt" "strconv" "testing" @@ -17,7 +18,7 @@ import ( func RemoveRule(t *testing.T, client *gophercloud.ServiceClient, policyID string, ruleID string) { t.Logf("Attempting to remove rule %s from policy %s", ruleID, policyID) - _, err := policies.RemoveRule(client, policyID, ruleID).Extract() + _, err := policies.RemoveRule(context.TODO(), client, policyID, ruleID).Extract() if err != nil { t.Fatalf("Unable to remove rule %s from policy %s: %v", ruleID, policyID, err) } @@ -32,7 +33,7 @@ func AddRule(t *testing.T, client *gophercloud.ServiceClient, policyID string, r InsertBefore: beforeRuleID, } - _, err := policies.InsertRule(client, policyID, addOpts).Extract() + _, err := policies.InsertRule(context.TODO(), client, policyID, addOpts).Extract() if err != nil { t.Fatalf("Unable to insert rule %s before rule %s in policy %s: %v", ruleID, beforeRuleID, policyID, err) } @@ -54,7 +55,7 @@ func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string }, } - policy, err := policies.Create(client, createOpts).Extract() + policy, err := policies.Create(context.TODO(), client, createOpts).Extract() if err != nil { return policy, err } @@ -93,7 +94,7 @@ func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, e DestinationPort: destinationPort, } - rule, err := rules.Create(client, createOpts).Extract() + rule, err := rules.Create(context.TODO(), client, createOpts).Extract() if err != nil { return rule, err } @@ -117,7 +118,7 @@ func CreateRule(t *testing.T, client *gophercloud.ServiceClient) (*rules.Rule, e func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete policy: %s", policyID) - err := policies.Delete(client, policyID).ExtractErr() + err := policies.Delete(context.TODO(), client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete policy %s: %v", policyID, err) } @@ -131,7 +132,7 @@ func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID stri func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { t.Logf("Attempting to delete rule: %s", ruleID) - err := rules.Delete(client, ruleID).ExtractErr() + err := rules.Delete(context.TODO(), client, ruleID).ExtractErr() if err != nil { t.Fatalf("Unable to delete rule %s: %v", ruleID, err) } @@ -158,7 +159,7 @@ func CreateGroup(t *testing.T, client *gophercloud.ServiceClient) (*groups.Group t.Logf("Attempting to create firewall group %s", groupName) - group, err := groups.Create(client, createOpts).Extract() + group, err := groups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return group, err } @@ -175,7 +176,7 @@ func CreateGroup(t *testing.T, client *gophercloud.ServiceClient) (*groups.Group func DeleteGroup(t *testing.T, client *gophercloud.ServiceClient, groupId string) { t.Logf("Attempting to delete firewall group %s", groupId) - err := groups.Delete(client, groupId).ExtractErr() + err := groups.Delete(context.TODO(), client, groupId).ExtractErr() if err != nil { t.Fatalf("Unable to delete firewall group %s: %v", groupId, err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index 0480252a5a..a948a0da5d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -4,6 +4,7 @@ package fwaas_v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -83,7 +84,7 @@ func TestGroupCRUD(t *testing.T) { t.Logf("Egress policy removed from firewall group %s", updatedGroup.ID) - allPages, err := groups.List(client, nil).AllPages() + allPages, err := groups.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allGroups, err := groups.ExtractGroups(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go index b058d79ffc..5c683a65b6 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -4,6 +4,7 @@ package fwaas_v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -55,10 +56,10 @@ func TestPolicyCRUD(t *testing.T) { FirewallRules: &[]string{}, } - _, err = policies.Update(client, policy.ID, updateOpts).Extract() + _, err = policies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newPolicy, err := policies.Get(client, policy.ID).Extract() + newPolicy, err := policies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) @@ -66,7 +67,7 @@ func TestPolicyCRUD(t *testing.T) { th.AssertEquals(t, newPolicy.Description, description) th.AssertEquals(t, len(newPolicy.Rules), 0) - allPages, err := policies.List(client, nil).AllPages() + allPages, err := policies.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go index 2e854eea56..fc1ea944e0 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -4,6 +4,7 @@ package fwaas_v2 import ( + "context" "fmt" "strconv" "testing" @@ -38,18 +39,18 @@ func TestRuleCRUD(t *testing.T) { SourcePort: &ruleSourcePort, } - ruleUpdated, err := rules.Update(client, rule.ID, updateOpts).Extract() + ruleUpdated, err := rules.Update(context.TODO(), 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() + newRule, err := rules.Get(context.TODO(), client, rule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) - allPages, err := rules.List(client, nil).AllPages() + allPages, err := rules.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allRules, err := rules.ExtractRules(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go index 885e2b4995..194bcfd336 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go @@ -4,6 +4,7 @@ package layer3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -28,16 +29,16 @@ func TestAddressScopesCRUD(t *testing.T) { Name: &newName, } - _, err = addressscopes.Update(client, addressScope.ID, updateOpts).Extract() + _, err = addressscopes.Update(context.TODO(), client, addressScope.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newAddressScope, err := addressscopes.Get(client, addressScope.ID).Extract() + newAddressScope, err := addressscopes.Get(context.TODO(), client, addressScope.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newAddressScope) th.AssertEquals(t, newAddressScope.Name, newName) - allPages, err := addressscopes.List(client, nil).AllPages() + allPages, err := addressscopes.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allAddressScopes, err := addressscopes.ExtractAddressScopes(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go index ef1eea42ac..5a8469ddc3 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go @@ -4,6 +4,7 @@ package layer3 import ( + "context" "fmt" "net" "testing" @@ -43,7 +44,7 @@ func TestLayer3ExtraRoutesAddRemove(t *testing.T) { aiOpts := routers.AddInterfaceOpts{ SubnetID: subnet.ID, } - iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract() + iface, err := routers.AddInterface(context.TODO(), client, router.ID, aiOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, iface) @@ -53,7 +54,7 @@ func TestLayer3ExtraRoutesAddRemove(t *testing.T) { riOpts := routers.RemoveInterfaceOpts{ SubnetID: subnet.ID, } - _, err = routers.RemoveInterface(client, router.ID, riOpts).Extract() + _, err = routers.RemoveInterface(context.TODO(), client, router.ID, riOpts).Extract() th.AssertNoErr(t, err) }() @@ -63,7 +64,7 @@ func TestLayer3ExtraRoutesAddRemove(t *testing.T) { opts := routers.UpdateOpts{ Routes: &routes, } - _, err = routers.Update(client, router.ID, opts).Extract() + _, err = routers.Update(context.TODO(), client, router.ID, opts).Extract() th.AssertNoErr(t, err) }() @@ -80,7 +81,7 @@ func TestLayer3ExtraRoutesAddRemove(t *testing.T) { updateOpts := routers.UpdateOpts{ Routes: &routes, } - _, err = routers.Update(client, router.ID, updateOpts).Extract() + _, err = routers.Update(context.TODO(), client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) newRoutes := []routers.Route{ @@ -97,12 +98,12 @@ func TestLayer3ExtraRoutesAddRemove(t *testing.T) { Routes: &newRoutes, } // add new routes - rt, err := extraroutes.Add(client, router.ID, opts).Extract() + rt, err := extraroutes.Add(context.TODO(), 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() + rt, err = extraroutes.Remove(context.TODO(), client, router.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, routes, rt.Routes) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index f4b0fdfb22..f258bec4ea 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -4,6 +4,7 @@ package layer3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -25,12 +26,12 @@ func TestLayer3FloatingIPsCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) - newFip, err := floatingips.Get(client, fip.ID).Extract() + newFip, err := floatingips.Get(context.TODO(), client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) - allPages, err := floatingips.List(client, floatingips.ListOpts{}).AllPages() + allPages, err := floatingips.List(client, floatingips.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allFIPs, err := floatingips.ExtractFloatingIPs(allPages) @@ -81,7 +82,7 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) - newFip, err := floatingips.Get(client, fip.ID).Extract() + newFip, err := floatingips.Get(context.TODO(), client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) @@ -91,10 +92,10 @@ func TestLayer3FloatingIPsExternalCreateDelete(t *testing.T) { PortID: new(string), } - _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() + _, err = floatingips.Update(context.TODO(), client, fip.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newFip, err = floatingips.Get(client, fip.ID).Extract() + newFip, err = floatingips.Get(context.TODO(), client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) @@ -141,7 +142,7 @@ func TestLayer3FloatingIPsWithFixedIPsExternalCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) - newFip, err := floatingips.Get(client, fip.ID).Extract() + newFip, err := floatingips.Get(context.TODO(), client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) @@ -152,10 +153,10 @@ func TestLayer3FloatingIPsWithFixedIPsExternalCreateDelete(t *testing.T) { FixedIP: fixedIPs[1], } - _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() + _, err = floatingips.Update(context.TODO(), client, fip.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newFip, err = floatingips.Get(client, fip.ID).Extract() + newFip, err = floatingips.Get(context.TODO(), client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) @@ -167,7 +168,7 @@ func TestLayer3FloatingIPsWithFixedIPsExternalCreateDelete(t *testing.T) { PortID: new(string), } - _, err = floatingips.Update(client, fip.ID, updateOpts).Extract() + _, err = floatingips.Update(context.TODO(), client, fip.ID, updateOpts).Extract() th.AssertNoErr(t, err) } @@ -185,7 +186,7 @@ func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { IPVersion: 4, } - subnetPages, err := subnets.List(client, listOpts).AllPages() + subnetPages, err := subnets.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allSubnets, err := subnets.ExtractSubnets(subnetPages) @@ -196,7 +197,7 @@ func TestLayer3FloatingIPsCreateDeleteBySubnetID(t *testing.T) { SubnetID: allSubnets[0].ID, } - fip, err := floatingips.Create(client, createOpts).Extract() + fip, err := floatingips.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, fip) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index 62c41ff30a..57818c160d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -4,6 +4,7 @@ package layer3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestLayer3RouterScheduling(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - _, err = extensions.Get(client, "l3_agent_scheduler").Extract() + _, err = extensions.Get(context.TODO(), client, "l3_agent_scheduler").Extract() if err != nil { t.Skip("Extension l3_agent_scheduler not present") } @@ -43,7 +44,7 @@ func TestLayer3RouterScheduling(t *testing.T) { defer DeleteRouterInterface(t, client, routerInterface.PortID, router.ID) // List hosting agent - allPages, err := routers.ListL3Agents(client, router.ID).AllPages() + allPages, err := routers.ListL3Agents(client, router.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) hostingAgents, err := routers.ExtractL3Agents(allPages) th.AssertNoErr(t, err) @@ -52,7 +53,7 @@ func TestLayer3RouterScheduling(t *testing.T) { 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() + err = agents.RemoveL3Router(context.TODO(), client, hostingAgent.ID, router.ID).ExtractErr() th.AssertNoErr(t, err) containsRouterFunc := func(rs []routers.Router, routerID string) bool { @@ -65,17 +66,17 @@ func TestLayer3RouterScheduling(t *testing.T) { } // List routers on hosting agent - routersOnHostingAgent, err := agents.ListL3Routers(client, hostingAgent.ID).Extract() + routersOnHostingAgent, err := agents.ListL3Routers(context.TODO(), 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() + err = agents.ScheduleL3Router(context.TODO(), 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() + routersOnHostingAgent, err = agents.ListL3Routers(context.TODO(), 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) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index c7ab126607..54f93a3e2f 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -1,6 +1,7 @@ package layer3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/addressscopes" @@ -28,7 +29,7 @@ func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, networkID PortID: portID, } - floatingIP, err := floatingips.Create(client, createOpts).Extract() + floatingIP, err := floatingips.Create(context.TODO(), client, createOpts).Extract() if err != nil { return floatingIP, err } @@ -53,7 +54,7 @@ func CreateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient FixedIP: fixedIP, } - floatingIP, err := floatingips.Create(client, createOpts).Extract() + floatingIP, err := floatingips.Create(context.TODO(), client, createOpts).Extract() if err != nil { return floatingIP, err } @@ -81,7 +82,7 @@ func CreatePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID InternalPortID: portID, } - pf, err := portforwarding.Create(client, fipID, createOpts).Extract() + pf, err := portforwarding.Create(context.TODO(), client, fipID, createOpts).Extract() if err != nil { return pf, err } @@ -98,7 +99,7 @@ func CreatePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID func DeletePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID string, pfID string) { t.Logf("Attempting to delete the port forwarding with ID %s for floating IP with ID %s", pfID, fipID) - err := portforwarding.Delete(client, fipID, pfID).ExtractErr() + err := portforwarding.Delete(context.TODO(), client, fipID, pfID).ExtractErr() if err != nil { t.Fatalf("Failed to delete Port forwarding with ID %s for floating IP with ID %s", pfID, fipID) } @@ -133,7 +134,7 @@ func CreateExternalRouter(t *testing.T, client *gophercloud.ServiceClient) (*rou GatewayInfo: &gatewayInfo, } - router, err = routers.Create(client, createOpts).Extract() + router, err = routers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return router, err } @@ -165,7 +166,7 @@ func CreateRouter(t *testing.T, client *gophercloud.ServiceClient, networkID str AdminStateUp: &adminStateUp, } - router, err := routers.Create(client, createOpts).Extract() + router, err := routers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return router, err } @@ -191,7 +192,7 @@ func CreateRouterInterface(t *testing.T, client *gophercloud.ServiceClient, port PortID: portID, } - iface, err := routers.AddInterface(client, routerID, aiOpts).Extract() + iface, err := routers.AddInterface(context.TODO(), client, routerID, aiOpts).Extract() if err != nil { return iface, err } @@ -213,7 +214,7 @@ func CreateRouterInterfaceOnSubnet(t *testing.T, client *gophercloud.ServiceClie SubnetID: subnetID, } - iface, err := routers.AddInterface(client, routerID, aiOpts).Extract() + iface, err := routers.AddInterface(context.TODO(), client, routerID, aiOpts).Extract() if err != nil { return iface, err } @@ -231,7 +232,7 @@ func CreateRouterInterfaceOnSubnet(t *testing.T, client *gophercloud.ServiceClie func DeleteRouter(t *testing.T, client *gophercloud.ServiceClient, routerID string) { t.Logf("Attempting to delete router: %s", routerID) - err := routers.Delete(client, routerID).ExtractErr() + err := routers.Delete(context.TODO(), client, routerID).ExtractErr() if err != nil { t.Fatalf("Error deleting router: %v", err) } @@ -253,7 +254,7 @@ func DeleteRouterInterface(t *testing.T, client *gophercloud.ServiceClient, port PortID: portID, } - _, err := routers.RemoveInterface(client, routerID, riOpts).Extract() + _, err := routers.RemoveInterface(context.TODO(), client, routerID, riOpts).Extract() if err != nil { t.Fatalf("Failed to detach port %s from router %s", portID, routerID) } @@ -271,7 +272,7 @@ func DeleteRouterInterface(t *testing.T, client *gophercloud.ServiceClient, port func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIPID string) { t.Logf("Attempting to delete floating IP: %s", floatingIPID) - err := floatingips.Delete(client, floatingIPID).ExtractErr() + err := floatingips.Delete(context.TODO(), client, floatingIPID).ExtractErr() if err != nil { t.Fatalf("Failed to delete floating IP: %v", err) } @@ -281,7 +282,7 @@ func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingI func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string) error { return tools.WaitFor(func() (bool, error) { - r, err := routers.Get(client, routerID).Extract() + r, err := routers.Get(context.TODO(), client, routerID).Extract() if err != nil { return false, err } @@ -296,7 +297,7 @@ func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string) e func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string) error { return tools.WaitFor(func() (bool, error) { - _, err := routers.Get(client, routerID).Extract() + _, err := routers.Get(context.TODO(), client, routerID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil @@ -311,7 +312,7 @@ func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string) e func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInterfaceID string) error { return tools.WaitFor(func() (bool, error) { - r, err := ports.Get(client, routerInterfaceID).Extract() + r, err := ports.Get(context.TODO(), client, routerInterfaceID).Extract() if err != nil { return false, err } @@ -326,7 +327,7 @@ func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInt func WaitForRouterInterfaceToDetach(client *gophercloud.ServiceClient, routerInterfaceID string) error { return tools.WaitFor(func() (bool, error) { - r, err := ports.Get(client, routerInterfaceID).Extract() + r, err := ports.Get(context.TODO(), client, routerInterfaceID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil @@ -360,7 +361,7 @@ func CreateAddressScope(t *testing.T, client *gophercloud.ServiceClient) (*addre t.Logf("Attempting to create an address-scope: %s", addressScopeName) - addressScope, err := addressscopes.Create(client, createOpts).Extract() + addressScope, err := addressscopes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -378,7 +379,7 @@ func CreateAddressScope(t *testing.T, client *gophercloud.ServiceClient) (*addre func DeleteAddressScope(t *testing.T, client *gophercloud.ServiceClient, addressScopeID string) { t.Logf("Attempting to delete the address-scope: %s", addressScopeID) - err := addressscopes.Delete(client, addressScopeID).ExtractErr() + err := addressscopes.Delete(context.TODO(), client, addressScopeID).ExtractErr() if err != nil { t.Fatalf("Unable to delete address-scope %s: %v", addressScopeID, err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index a857adedb8..216a997d5b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -1,6 +1,7 @@ package layer3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -46,7 +47,7 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteFloatingIP(t, client, fip.ID) - newFip, err := floatingips.Get(client, fip.ID).Extract() + newFip, err := floatingips.Get(context.TODO(), client, fip.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newFip) @@ -56,7 +57,7 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { defer DeletePortForwarding(t, client, fip.ID, pf.ID) tools.PrintResource(t, pf) - newPf, err := portforwarding.Get(client, fip.ID, pf.ID).Extract() + newPf, err := portforwarding.Get(context.TODO(), client, fip.ID, pf.ID).Extract() th.AssertNoErr(t, err) updateOpts := portforwarding.UpdateOpts{ @@ -65,13 +66,13 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { ExternalPort: 678, } - _, err = portforwarding.Update(client, fip.ID, newPf.ID, updateOpts).Extract() + _, err = portforwarding.Update(context.TODO(), client, fip.ID, newPf.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newPf, err = portforwarding.Get(client, fip.ID, pf.ID).Extract() + newPf, err = portforwarding.Get(context.TODO(), client, fip.ID, pf.ID).Extract() th.AssertNoErr(t, err) - allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fip.ID).AllPages() + allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fip.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) allPFs, err := portforwarding.ExtractPortForwardings(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index c832b0ac7e..e94ab3488e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -4,6 +4,7 @@ package layer3 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -34,10 +35,10 @@ func TestLayer3RouterCreateDelete(t *testing.T) { Description: &newDescription, } - _, err = routers.Update(client, router.ID, updateOpts).Extract() + _, err = routers.Update(context.TODO(), client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newRouter, err := routers.Get(client, router.ID).Extract() + newRouter, err := routers.Get(context.TODO(), client, router.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRouter) @@ -45,7 +46,7 @@ func TestLayer3RouterCreateDelete(t *testing.T) { th.AssertEquals(t, newRouter.Description, newDescription) listOpts := routers.ListOpts{} - allPages, err := routers.List(client, listOpts).AllPages() + allPages, err := routers.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allRouters, err := routers.ExtractRouters(allPages) @@ -104,10 +105,10 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { GatewayInfo: &gatewayInfo, } - _, err = routers.Update(client, router.ID, updateOpts).Extract() + _, err = routers.Update(context.TODO(), client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newRouter, err := routers.Get(client, router.ID).Extract() + newRouter, err := routers.Get(context.TODO(), client, router.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRouter) @@ -121,7 +122,7 @@ func TestLayer3ExternalRouterCreateDelete(t *testing.T) { GatewayInfo: &routers.GatewayInfo{}, } - newRouter, err = routers.Update(client, router.ID, updateOpts).Extract() + newRouter, err = routers.Update(context.TODO(), client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, newRouter.GatewayInfo, routers.GatewayInfo{}) @@ -152,7 +153,7 @@ func TestLayer3RouterInterface(t *testing.T) { SubnetID: subnet.ID, } - iface, err := routers.AddInterface(client, router.ID, aiOpts).Extract() + iface, err := routers.AddInterface(context.TODO(), client, router.ID, aiOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, router) @@ -162,7 +163,7 @@ func TestLayer3RouterInterface(t *testing.T) { SubnetID: subnet.ID, } - _, err = routers.RemoveInterface(client, router.ID, riOpts).Extract() + _, err = routers.RemoveInterface(context.TODO(), client, router.ID, riOpts).Extract() th.AssertNoErr(t, err) } @@ -190,14 +191,14 @@ func TestLayer3RouterAgents(t *testing.T) { Description: &newDescription, } - _, err = routers.Update(client, router.ID, updateOpts).Extract() + _, err = routers.Update(context.TODO(), client, router.ID, updateOpts).Extract() th.AssertNoErr(t, err) - _, err = routers.Get(client, router.ID).Extract() + _, err = routers.Get(context.TODO(), client, router.ID).Extract() th.AssertNoErr(t, err) // Test ListL3Agents for HA or not HA router - l3AgentsPages, err := routers.ListL3Agents(client, router.ID).AllPages() + l3AgentsPages, err := routers.ListL3Agents(client, router.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) l3Agents, err := routers.ExtractL3Agents(l3AgentsPages) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go index b68ab01f77..50d07baada 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go @@ -1,6 +1,7 @@ package lbaas import ( + "context" "fmt" "testing" @@ -25,7 +26,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, poolID string Address: fmt.Sprintf("192.168.1.%d", address), } - member, err := members.Create(client, createOpts).Extract() + member, err := members.Create(context.TODO(), client, createOpts).Extract() if err != nil { return member, err } @@ -48,7 +49,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient) (*monitors.M AdminStateUp: gophercloud.Enabled, } - monitor, err := monitors.Create(client, createOpts).Extract() + monitor, err := monitors.Create(context.TODO(), client, createOpts).Extract() if err != nil { return monitor, err } @@ -72,7 +73,7 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, subnetID string LBMethod: pools.LBMethodRoundRobin, } - pool, err := pools.Create(client, createOpts).Extract() + pool, err := pools.Create(context.TODO(), client, createOpts).Extract() if err != nil { return pool, err } @@ -99,7 +100,7 @@ func CreateVIP(t *testing.T, client *gophercloud.ServiceClient, subnetID, poolID ProtocolPort: vipPort, } - vip, err := vips.Create(client, createOpts).Extract() + vip, err := vips.Create(context.TODO(), client, createOpts).Extract() if err != nil { return vip, err } @@ -115,7 +116,7 @@ func CreateVIP(t *testing.T, client *gophercloud.ServiceClient, subnetID, poolID func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, memberID string) { t.Logf("Attempting to delete member %s", memberID) - if err := members.Delete(client, memberID).ExtractErr(); err != nil { + if err := members.Delete(context.TODO(), client, memberID).ExtractErr(); err != nil { t.Fatalf("Unable to delete member: %v", err) } @@ -128,7 +129,7 @@ func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, memberID stri func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, monitorID string) { t.Logf("Attempting to delete monitor %s", monitorID) - if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { + if err := monitors.Delete(context.TODO(), client, monitorID).ExtractErr(); err != nil { t.Fatalf("Unable to delete monitor: %v", err) } @@ -140,7 +141,7 @@ func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, monitorID st func DeletePool(t *testing.T, client *gophercloud.ServiceClient, poolID string) { t.Logf("Attempting to delete pool %s", poolID) - if err := pools.Delete(client, poolID).ExtractErr(); err != nil { + if err := pools.Delete(context.TODO(), client, poolID).ExtractErr(); err != nil { t.Fatalf("Unable to delete pool: %v", err) } @@ -152,7 +153,7 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, poolID string) func DeleteVIP(t *testing.T, client *gophercloud.ServiceClient, vipID string) { t.Logf("Attempting to delete vip %s", vipID) - if err := vips.Delete(client, vipID).ExtractErr(); err != nil { + if err := vips.Delete(context.TODO(), client, vipID).ExtractErr(); err != nil { t.Fatalf("Unable to delete vip: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go index 6f1caf5169..f4f677d5e8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go @@ -4,6 +4,7 @@ package lbaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -20,7 +21,7 @@ func TestMembersList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - allPages, err := members.List(client, members.ListOpts{}).AllPages() + allPages, err := members.List(client, members.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list members: %v", err) } @@ -72,12 +73,12 @@ func TestMembersCRUD(t *testing.T) { AdminStateUp: gophercloud.Enabled, } - _, err = members.Update(client, member.ID, updateOpts).Extract() + _, err = members.Update(context.TODO(), client, member.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update member: %v", err) } - newMember, err := members.Get(client, member.ID).Extract() + newMember, err := members.Get(context.TODO(), client, member.ID).Extract() if err != nil { t.Fatalf("Unable to get member: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go index 280e9bfbea..8adb4fc1bb 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go @@ -4,6 +4,7 @@ package lbaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestMonitorsList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - allPages, err := monitors.List(client, monitors.ListOpts{}).AllPages() + allPages, err := monitors.List(client, monitors.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list monitors: %v", err) } @@ -52,12 +53,12 @@ func TestMonitorsCRUD(t *testing.T) { Delay: 999, } - _, err = monitors.Update(client, monitor.ID, updateOpts).Extract() + _, err = monitors.Update(context.TODO(), client, monitor.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update monitor: %v", err) } - newMonitor, err := monitors.Get(client, monitor.ID).Extract() + newMonitor, err := monitors.Get(context.TODO(), client, monitor.ID).Extract() if err != nil { t.Fatalf("Unable to get monitor: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go index 30a7c43505..61fecc36a2 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go @@ -4,6 +4,7 @@ package lbaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestPoolsList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - allPages, err := pools.List(client, pools.ListOpts{}).AllPages() + allPages, err := pools.List(client, pools.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list pools: %v", err) } @@ -65,12 +66,12 @@ func TestPoolsCRUD(t *testing.T) { LBMethod: pools.LBMethodLeastConnections, } - _, err = pools.Update(client, pool.ID, updateOpts).Extract() + _, err = pools.Update(context.TODO(), client, pool.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update pool: %v", err) } - newPool, err := pools.Get(client, pool.ID).Extract() + newPool, err := pools.Get(context.TODO(), client, pool.ID).Extract() if err != nil { t.Fatalf("Unable to get pool: %v", err) } @@ -110,12 +111,12 @@ func TestPoolsMonitors(t *testing.T) { defer DeleteMonitor(t, client, monitor.ID) t.Logf("Associating monitor %s with pool %s", monitor.ID, pool.ID) - if res := pools.AssociateMonitor(client, pool.ID, monitor.ID); res.Err != nil { + if res := pools.AssociateMonitor(context.TODO(), client, pool.ID, monitor.ID); res.Err != nil { t.Fatalf("Unable to associate monitor to pool") } t.Logf("Disassociating monitor %s with pool %s", monitor.ID, pool.ID) - if res := pools.DisassociateMonitor(client, pool.ID, monitor.ID); res.Err != nil { + if res := pools.DisassociateMonitor(context.TODO(), client, pool.ID, monitor.ID); res.Err != nil { t.Fatalf("Unable to disassociate monitor from pool") } diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go index b07d6d7a59..05f1840a4c 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go @@ -4,6 +4,7 @@ package lbaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestVIPsList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - allPages, err := vips.List(client, vips.ListOpts{}).AllPages() + allPages, err := vips.List(client, vips.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list vips: %v", err) } @@ -72,12 +73,12 @@ func TestVIPsCRUD(t *testing.T) { ConnLimit: &connLimit, } - _, err = vips.Update(client, vip.ID, updateOpts).Extract() + _, err = vips.Update(context.TODO(), client, vip.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update vip: %v", err) } - newVIP, err := vips.Get(client, vip.ID).Extract() + newVIP, err := vips.Get(context.TODO(), client, vip.ID).Extract() if err != nil { t.Fatalf("Unable to get vip: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go index 9ca2b83155..f279cdbb3d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go @@ -4,6 +4,7 @@ package lbaas_v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestL7PoliciesList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := l7policies.List(client, nil).AllPages() + allPages, err := l7policies.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list l7policies: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index f2d0d27573..0e3c6a5003 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -1,6 +1,7 @@ package lbaas_v2 import ( + "context" "fmt" "strings" "testing" @@ -33,7 +34,7 @@ func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbal ProtocolPort: listenerPort, } - listener, err := listeners.Create(client, createOpts).Extract() + listener, err := listeners.Create(context.TODO(), client, createOpts).Extract() if err != nil { return listener, err } @@ -68,7 +69,7 @@ func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetI AdminStateUp: gophercloud.Enabled, } - lb, err := loadbalancers.Create(client, createOpts).Extract() + lb, err := loadbalancers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return lb, err } @@ -113,7 +114,7 @@ func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalan t.Logf("Member create opts: %#v", createOpts) - member, err := pools.CreateMember(client, pool.ID, createOpts).Extract() + member, err := pools.CreateMember(context.TODO(), client, pool.ID, createOpts).Extract() if err != nil { return member, err } @@ -145,7 +146,7 @@ func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbala Type: monitors.TypePING, } - monitor, err := monitors.Create(client, createOpts).Extract() + monitor, err := monitors.Create(context.TODO(), client, createOpts).Extract() if err != nil { return monitor, err } @@ -179,7 +180,7 @@ func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalance LBMethod: pools.LBMethodLeastConnections, } - pool, err := pools.Create(client, createOpts).Extract() + pool, err := pools.Create(context.TODO(), client, createOpts).Extract() if err != nil { return pool, err } @@ -216,7 +217,7 @@ func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *l RedirectURL: "http://www.example.com", } - policy, err := l7policies.Create(client, createOpts).Extract() + policy, err := l7policies.Create(context.TODO(), client, createOpts).Extract() if err != nil { return policy, err } @@ -246,7 +247,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri Value: "/api", } - rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract() + rule, err := l7policies.CreateRule(context.TODO(), client, policyID, createOpts).Extract() if err != nil { return rule, err } @@ -270,7 +271,7 @@ func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID stri func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) { t.Logf("Attempting to delete l7 policy %s", policyID) - if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil { + if err := l7policies.Delete(context.TODO(), client, policyID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete l7 policy: %v", err) } @@ -289,7 +290,7 @@ func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, polic func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) { t.Logf("Attempting to delete l7 rule %s", ruleID) - if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil { + if err := l7policies.DeleteRule(context.TODO(), client, policyID, ruleID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete l7 rule: %v", err) } @@ -308,7 +309,7 @@ func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyI func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) { t.Logf("Attempting to delete listener %s", listenerID) - if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil { + if err := listeners.Delete(context.TODO(), client, listenerID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete listener: %v", err) } @@ -327,7 +328,7 @@ func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, liste func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) { t.Logf("Attempting to delete member %s", memberID) - if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil { + if err := pools.DeleteMember(context.TODO(), client, poolID, memberID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete member: %s", memberID) } @@ -346,7 +347,7 @@ func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) { t.Logf("Attempting to delete loadbalancer %s", lbID) - if err := loadbalancers.Delete(client, lbID).ExtractErr(); err != nil { + if err := loadbalancers.Delete(context.TODO(), client, lbID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete loadbalancer: %v", err) } @@ -367,7 +368,7 @@ func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID st func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) { t.Logf("Attempting to delete monitor %s", monitorID) - if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil { + if err := monitors.Delete(context.TODO(), client, monitorID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete monitor: %v", err) } @@ -385,7 +386,7 @@ func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monito func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) { t.Logf("Attempting to delete pool %s", poolID) - if err := pools.Delete(client, poolID).ExtractErr(); err != nil { + if err := pools.Delete(context.TODO(), client, poolID).ExtractErr(); err != nil { if _, ok := err.(gophercloud.ErrDefault404); !ok { t.Fatalf("Unable to delete pool: %v", err) } @@ -401,7 +402,7 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error { return tools.WaitFor(func() (bool, error) { - current, err := loadbalancers.Get(client, lbID).Extract() + current, err := loadbalancers.Get(context.TODO(), client, lbID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go index 902bc1b41c..5d1b6fb1f2 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go @@ -4,6 +4,7 @@ package lbaas_v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestListenersList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := listeners.List(client, nil).AllPages() + allPages, err := listeners.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list listeners: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go index a65b392ca9..45e2bd4647 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go @@ -4,6 +4,7 @@ package lbaas_v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestLoadbalancersList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := loadbalancers.List(client, nil).AllPages() + allPages, err := loadbalancers.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) @@ -54,21 +55,21 @@ func TestLoadbalancersCRUD(t *testing.T) { updateLoadBalancerOpts := loadbalancers.UpdateOpts{ Description: &lbDescription, } - _, err = loadbalancers.Update(client, lb.ID, updateLoadBalancerOpts).Extract() + _, err = loadbalancers.Update(context.TODO(), client, lb.ID, updateLoadBalancerOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newLB, err := loadbalancers.Get(client, lb.ID).Extract() + newLB, err := loadbalancers.Get(context.TODO(), client, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newLB) th.AssertEquals(t, newLB.Description, lbDescription) - lbStats, err := loadbalancers.GetStats(client, lb.ID).Extract() + lbStats, err := loadbalancers.GetStats(context.TODO(), client, lb.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, lbStats) @@ -87,14 +88,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Name: &listenerName, Description: &listenerDescription, } - _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() + _, err = listeners.Update(context.TODO(), client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newListener, err := listeners.Get(client, listener.ID).Extract() + newListener, err := listeners.Get(context.TODO(), client, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) @@ -111,14 +112,14 @@ func TestLoadbalancersCRUD(t *testing.T) { updateL7policyOpts := l7policies.UpdateOpts{ Description: &newDescription, } - _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() + _, err = l7policies.Update(context.TODO(), client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newPolicy, err := l7policies.Get(client, policy.ID).Extract() + newPolicy, err := l7policies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) @@ -130,7 +131,7 @@ func TestLoadbalancersCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteL7Rule(t, client, lb.ID, policy.ID, rule.ID) - allPages, err := l7policies.ListRules(client, policy.ID, l7policies.ListRulesOpts{}).AllPages() + allPages, err := l7policies.ListRules(client, policy.ID, l7policies.ListRulesOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allRules, err := l7policies.ExtractRules(allPages) th.AssertNoErr(t, err) @@ -144,14 +145,14 @@ func TestLoadbalancersCRUD(t *testing.T) { CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", } - _, err = l7policies.UpdateRule(client, policy.ID, rule.ID, updateL7ruleOpts).Extract() + _, err = l7policies.UpdateRule(context.TODO(), client, policy.ID, rule.ID, updateL7ruleOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newRule, err := l7policies.GetRule(client, newPolicy.ID, rule.ID).Extract() + newRule, err := l7policies.GetRule(context.TODO(), client, newPolicy.ID, rule.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) @@ -167,14 +168,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Name: &poolName, Description: &poolDescription, } - _, err = pools.Update(client, pool.ID, updatePoolOpts).Extract() + _, err = pools.Update(context.TODO(), client, pool.ID, updatePoolOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newPool, err := pools.Get(client, pool.ID).Extract() + newPool, err := pools.Get(context.TODO(), client, pool.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPool) @@ -188,14 +189,14 @@ func TestLoadbalancersCRUD(t *testing.T) { RedirectPoolID: &newPool.ID, RedirectURL: &newRedirectURL, } - _, err = l7policies.Update(client, policy.ID, updateL7policyOpts).Extract() + _, err = l7policies.Update(context.TODO(), client, policy.ID, updateL7policyOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newPolicy, err = l7policies.Get(client, policy.ID).Extract() + newPolicy, err = l7policies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) @@ -213,14 +214,14 @@ func TestLoadbalancersCRUD(t *testing.T) { updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &pool.ID, } - _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() + _, err = listeners.Update(context.TODO(), client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newListener, err = listeners.Get(client, listener.ID).Extract() + newListener, err = listeners.Get(context.TODO(), client, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) @@ -232,14 +233,14 @@ func TestLoadbalancersCRUD(t *testing.T) { updateListenerOpts = listeners.UpdateOpts{ DefaultPoolID: &emptyPoolID, } - _, err = listeners.Update(client, listener.ID, updateListenerOpts).Extract() + _, err = listeners.Update(context.TODO(), client, listener.ID, updateListenerOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newListener, err = listeners.Get(client, listener.ID).Extract() + newListener, err = listeners.Get(context.TODO(), client, listener.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newListener) @@ -257,14 +258,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Name: &memberName, Weight: &newWeight, } - _, err = pools.UpdateMember(client, pool.ID, member.ID, updateMemberOpts).Extract() + _, err = pools.UpdateMember(context.TODO(), client, pool.ID, member.ID, updateMemberOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newMember, err := pools.GetMember(client, pool.ID, member.ID).Extract() + newMember, err := pools.GetMember(context.TODO(), client, pool.ID, member.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newMember) @@ -282,14 +283,14 @@ func TestLoadbalancersCRUD(t *testing.T) { Name: &monName, Delay: newDelay, } - _, err = monitors.Update(client, monitor.ID, updateMonitorOpts).Extract() + _, err = monitors.Update(context.TODO(), client, monitor.ID, updateMonitorOpts).Extract() th.AssertNoErr(t, err) if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { t.Fatalf("Timed out waiting for loadbalancer to become active") } - newMonitor, err := monitors.Get(client, monitor.ID).Extract() + newMonitor, err := monitors.Get(context.TODO(), client, monitor.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newMonitor) diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go index ede7794a4a..3523f487e5 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go @@ -4,6 +4,7 @@ package lbaas_v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestMonitorsList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := monitors.List(client, nil).AllPages() + allPages, err := monitors.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list monitors: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go index ef8c8f0427..7dfc1ef3f9 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go @@ -4,6 +4,7 @@ package lbaas_v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestPoolsList(t *testing.T) { t.Fatalf("Unable to create a loadbalancer client: %v", err) } - allPages, err := pools.List(client, nil).AllPages() + allPages, err := pools.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list pools: %v", err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go index 3ae7daa2b3..1747ad2181 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu.go @@ -1,6 +1,7 @@ package mtu import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -41,7 +42,7 @@ func CreateNetworkWithMTU(t *testing.T, client *gophercloud.ServiceClient, netwo var network NetworkMTU - err := networks.Create(client, createOpts).ExtractInto(&network) + err := networks.Create(context.TODO(), client, createOpts).ExtractInto(&network) if err != nil { return &network, err } diff --git a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 76d26c8ee3..6e07ab2be9 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -4,6 +4,7 @@ package mtu import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,13 +23,13 @@ func TestMTUNetworkCRUDL(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "net-mtu").Extract() + extension, err := extensions.Get(context.TODO(), client, "net-mtu").Extract() if err != nil { t.Skip("This test requires net-mtu Neutron extension") } tools.PrintResource(t, extension) - mtuWritable, _ := extensions.Get(client, "net-mtu-writable").Extract() + mtuWritable, _ := extensions.Get(context.TODO(), client, "net-mtu-writable").Extract() tools.PrintResource(t, mtuWritable) // Create Network @@ -51,7 +52,7 @@ func TestMTUNetworkCRUDL(t *testing.T) { } var listedNetworks []NetworkMTU i := 0 - err = networks.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + err = networks.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { i++ err := networks.ExtractNetworksInto(page, &listedNetworks) if err != nil { @@ -75,7 +76,7 @@ func TestMTUNetworkCRUDL(t *testing.T) { MTU: 1, } i = 0 - err = networks.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + err = networks.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { i++ err := networks.ExtractNetworksInto(page, &listedNetworks) if err != nil { @@ -96,7 +97,7 @@ func TestMTUNetworkCRUDL(t *testing.T) { // Get network var getNetwork NetworkMTU - err = networks.Get(client, network.ID).ExtractInto(&getNetwork) + err = networks.Get(context.TODO(), client, network.ID).ExtractInto(&getNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, getNetwork) @@ -116,7 +117,7 @@ func TestMTUNetworkCRUDL(t *testing.T) { } var newNetwork NetworkMTU - err = networks.Update(client, network.ID, updateOpts).ExtractInto(&newNetwork) + err = networks.Update(context.TODO(), client, network.ID, updateOpts).ExtractInto(&newNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) @@ -125,7 +126,7 @@ func TestMTUNetworkCRUDL(t *testing.T) { // Get updated network var getNewNetwork NetworkMTU - err = networks.Get(client, network.ID).ExtractInto(&getNewNetwork) + err = networks.Get(context.TODO(), client, network.ID).ExtractInto(&getNewNetwork) th.AssertNoErr(t, err) tools.PrintResource(t, getNewNetwork) diff --git a/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go b/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go index f5237e5c6d..73e46a379e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go @@ -4,6 +4,7 @@ package networkipavailabilities import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestNetworkIPAvailabilityList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := networkipavailabilities.List(client, nil).AllPages() + allPages, err := networkipavailabilities.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allAvailabilities, err := networkipavailabilities.ExtractNetworkIPAvailabilities(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index 0f48dbcdb0..bbeaa17818 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -1,6 +1,7 @@ package portsbinding import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -41,7 +42,7 @@ func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, network var s PortWithBindingExt - err := ports.Create(client, createOpts).ExtractInto(&s) + err := ports.Create(context.TODO(), client, createOpts).ExtractInto(&s) if err != nil { return s, err } 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 f3f9563b7f..c1e328277b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -4,6 +4,7 @@ package portsbinding import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -64,11 +65,11 @@ func TestPortsbindingCRUD(t *testing.T) { var newPort PortWithBindingExt - _, err = ports.Update(client, port.ID, finalUpdateOpts).Extract() + _, err = ports.Update(context.TODO(), client, port.ID, finalUpdateOpts).Extract() th.AssertNoErr(t, err) // Read the updated port - err = ports.Get(client, port.ID).ExtractInto(&newPort) + err = ports.Get(context.TODO(), client, port.ID).ExtractInto(&newPort) th.AssertNoErr(t, err) tools.PrintResource(t, newPort) diff --git a/internal/acceptance/openstack/networking/v2/extensions/provider_test.go b/internal/acceptance/openstack/networking/v2/extensions/provider_test.go index 7abe50fc26..e4046a38b7 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/provider_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/provider_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestNetworksProviderCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeleteNetwork(t, client, network.ID) - getResult := networks.Get(client, network.ID) + getResult := networks.Get(context.TODO(), client, network.ID) newNetwork, err := getResult.Extract() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go index 854a70816a..520d7a33e7 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies.go @@ -1,6 +1,7 @@ package policies import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -22,7 +23,7 @@ func CreateQoSPolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies t.Logf("Attempting to create a QoS policy: %s", policyName) - policy, err := policies.Create(client, createOpts).Extract() + policy, err := policies.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -40,7 +41,7 @@ func CreateQoSPolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies func DeleteQoSPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete the QoS policy: %s", policyID) - err := policies.Delete(client, policyID).ExtractErr() + err := policies.Delete(context.TODO(), client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete QoS policy %s: %v", policyID, err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go index e994f2a567..cbc20c4b6f 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -4,6 +4,7 @@ package policies import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestPoliciesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "qos").Extract() + extension, err := extensions.Get(context.TODO(), client, "qos").Extract() if err != nil { t.Skip("This test requires qos Neutron extension") } @@ -37,17 +38,17 @@ func TestPoliciesCRUD(t *testing.T) { Description: &newDescription, } - _, err = policies.Update(client, policy.ID, updateOpts).Extract() + _, err = policies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newPolicy, err := policies.Get(client, policy.ID).Extract() + newPolicy, err := policies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) th.AssertEquals(t, newPolicy.Name, newName) th.AssertEquals(t, newPolicy.Description, newDescription) - allPages, err := policies.List(client, nil).AllPages() + allPages, err := policies.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := policies.ExtractPolicies(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go index 78fec261ad..7502386df6 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules.go @@ -1,6 +1,7 @@ package rules import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -21,7 +22,7 @@ func CreateBandwidthLimitRule(t *testing.T, client *gophercloud.ServiceClient, p t.Logf("Attempting to create a QoS bandwidth limit rule with max_kbps: %d, max_burst_kbps: %d", maxKBps, maxBurstKBps) - rule, err := rules.CreateBandwidthLimitRule(client, policyID, createOpts).ExtractBandwidthLimitRule() + rule, err := rules.CreateBandwidthLimitRule(context.TODO(), client, policyID, createOpts).ExtractBandwidthLimitRule() if err != nil { return nil, err } @@ -45,7 +46,7 @@ func CreateDSCPMarkingRule(t *testing.T, client *gophercloud.ServiceClient, poli t.Logf("Attempting to create a QoS DSCP marking rule with dscp_mark: %d", dscpMark) - rule, err := rules.CreateDSCPMarkingRule(client, policyID, createOpts).ExtractDSCPMarkingRule() + rule, err := rules.CreateDSCPMarkingRule(context.TODO(), client, policyID, createOpts).ExtractDSCPMarkingRule() if err != nil { return nil, err } @@ -68,7 +69,7 @@ func CreateMinimumBandwidthRule(t *testing.T, client *gophercloud.ServiceClient, t.Logf("Attempting to create a QoS minimum bandwidth rule with min_kbps: %d", minKBps) - rule, err := rules.CreateMinimumBandwidthRule(client, policyID, createOpts).ExtractMinimumBandwidthRule() + rule, err := rules.CreateMinimumBandwidthRule(context.TODO(), client, policyID, createOpts).ExtractMinimumBandwidthRule() if err != nil { return nil, err } diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go index c8e9f496dd..82e15899b3 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -1,6 +1,7 @@ package rules import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestBandwidthLimitRulesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "qos").Extract() + extension, err := extensions.Get(context.TODO(), client, "qos").Extract() if err != nil { t.Skip("This test requires qos Neutron extension") } @@ -25,27 +26,27 @@ func TestBandwidthLimitRulesCRUD(t *testing.T) { // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) - defer policies.Delete(client, policy.ID) + defer policies.Delete(context.TODO(), client, policy.ID) tools.PrintResource(t, policy) // Create a QoS policy rule. rule, err := CreateBandwidthLimitRule(t, client, policy.ID) th.AssertNoErr(t, err) - defer rules.DeleteBandwidthLimitRule(client, policy.ID, rule.ID) + defer rules.DeleteBandwidthLimitRule(context.TODO(), client, policy.ID, rule.ID) // Update the QoS policy rule. newMaxBurstKBps := 0 updateOpts := rules.UpdateBandwidthLimitRuleOpts{ MaxBurstKBps: &newMaxBurstKBps, } - newRule, err := rules.UpdateBandwidthLimitRule(client, policy.ID, rule.ID, updateOpts).ExtractBandwidthLimitRule() + newRule, err := rules.UpdateBandwidthLimitRule(context.TODO(), client, policy.ID, rule.ID, updateOpts).ExtractBandwidthLimitRule() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) th.AssertEquals(t, newRule.MaxBurstKBps, 0) - allPages, err := rules.ListBandwidthLimitRules(client, policy.ID, rules.BandwidthLimitRulesListOpts{}).AllPages() + allPages, err := rules.ListBandwidthLimitRules(client, policy.ID, rules.BandwidthLimitRulesListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allRules, err := rules.ExtractBandwidthLimitRules(allPages) @@ -65,7 +66,7 @@ func TestDSCPMarkingRulesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "qos").Extract() + extension, err := extensions.Get(context.TODO(), client, "qos").Extract() if err != nil { t.Skip("This test requires qos Neutron extension") } @@ -74,27 +75,27 @@ func TestDSCPMarkingRulesCRUD(t *testing.T) { // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) - defer policies.Delete(client, policy.ID) + defer policies.Delete(context.TODO(), client, policy.ID) tools.PrintResource(t, policy) // Create a QoS policy rule. rule, err := CreateDSCPMarkingRule(t, client, policy.ID) th.AssertNoErr(t, err) - defer rules.DeleteDSCPMarkingRule(client, policy.ID, rule.ID) + defer rules.DeleteDSCPMarkingRule(context.TODO(), client, policy.ID, rule.ID) // Update the QoS policy rule. dscpMark := 20 updateOpts := rules.UpdateDSCPMarkingRuleOpts{ DSCPMark: &dscpMark, } - newRule, err := rules.UpdateDSCPMarkingRule(client, policy.ID, rule.ID, updateOpts).ExtractDSCPMarkingRule() + newRule, err := rules.UpdateDSCPMarkingRule(context.TODO(), client, policy.ID, rule.ID, updateOpts).ExtractDSCPMarkingRule() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) th.AssertEquals(t, newRule.DSCPMark, 20) - allPages, err := rules.ListDSCPMarkingRules(client, policy.ID, rules.DSCPMarkingRulesListOpts{}).AllPages() + allPages, err := rules.ListDSCPMarkingRules(client, policy.ID, rules.DSCPMarkingRulesListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allRules, err := rules.ExtractDSCPMarkingRules(allPages) @@ -114,7 +115,7 @@ func TestMinimumBandwidthRulesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "qos").Extract() + extension, err := extensions.Get(context.TODO(), client, "qos").Extract() if err != nil { t.Skip("This test requires qos Neutron extension") } @@ -123,27 +124,27 @@ func TestMinimumBandwidthRulesCRUD(t *testing.T) { // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) th.AssertNoErr(t, err) - defer policies.Delete(client, policy.ID) + defer policies.Delete(context.TODO(), client, policy.ID) tools.PrintResource(t, policy) // Create a QoS policy rule. rule, err := CreateMinimumBandwidthRule(t, client, policy.ID) th.AssertNoErr(t, err) - defer rules.DeleteMinimumBandwidthRule(client, policy.ID, rule.ID) + defer rules.DeleteMinimumBandwidthRule(context.TODO(), client, policy.ID, rule.ID) // Update the QoS policy rule. minKBps := 500 updateOpts := rules.UpdateMinimumBandwidthRuleOpts{ MinKBps: &minKBps, } - newRule, err := rules.UpdateMinimumBandwidthRule(client, policy.ID, rule.ID, updateOpts).ExtractMinimumBandwidthRule() + newRule, err := rules.UpdateMinimumBandwidthRule(context.TODO(), client, policy.ID, rule.ID, updateOpts).ExtractMinimumBandwidthRule() th.AssertNoErr(t, err) tools.PrintResource(t, newRule) th.AssertEquals(t, newRule.MinKBps, 500) - allPages, err := rules.ListMinimumBandwidthRules(client, policy.ID, rules.MinimumBandwidthRulesListOpts{}).AllPages() + allPages, err := rules.ListMinimumBandwidthRules(client, policy.ID, rules.MinimumBandwidthRulesListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allRules, err := rules.ExtractMinimumBandwidthRules(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go index 505fec6fa4..223f3ea7ac 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -1,6 +1,7 @@ package ruletypes import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,13 +17,13 @@ func TestRuleTypes(t *testing.T) { return } - extension, err := extensions.Get(client, "qos").Extract() + extension, err := extensions.Get(context.TODO(), client, "qos").Extract() if err != nil { t.Skip("This test requires qos Neutron extension") } tools.PrintResource(t, extension) - page, err := ruletypes.ListRuleTypes(client).AllPages() + page, err := ruletypes.ListRuleTypes(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Failed to list rule types pages: %v", err) return @@ -39,7 +40,7 @@ func TestRuleTypes(t *testing.T) { if len(ruleTypes) > 0 { t.Logf("Trying to get rule type: %s", ruleTypes[0].Type) - ruleType, err := ruletypes.GetRuleType(client, ruleTypes[0].Type).Extract() + ruleType, err := ruletypes.GetRuleType(context.TODO(), client, ruleTypes[0].Type).Extract() if err != nil { t.Fatalf("Failed to get rule type %s: %s", ruleTypes[0].Type, err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go index 878bbedb47..cda108f213 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go @@ -4,6 +4,7 @@ package quotas import ( + "context" "log" "os" "reflect" @@ -21,7 +22,7 @@ func TestQuotasGet(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - quotasInfo, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() + quotasInfo, err := quotas.Get(context.TODO(), client, os.Getenv("OS_PROJECT_NAME")).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, quotasInfo) @@ -33,10 +34,10 @@ func TestQuotasUpdate(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - originalQuotas, err := quotas.Get(client, os.Getenv("OS_PROJECT_NAME")).Extract() + originalQuotas, err := quotas.Get(context.TODO(), client, os.Getenv("OS_PROJECT_NAME")).Extract() th.AssertNoErr(t, err) - newQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), updateOpts).Extract() + newQuotas, err := quotas.Update(context.TODO(), client, os.Getenv("OS_PROJECT_NAME"), updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newQuotas) @@ -46,7 +47,7 @@ func TestQuotasUpdate(t *testing.T) { } // Restore original quotas. - restoredQuotas, err := quotas.Update(client, os.Getenv("OS_PROJECT_NAME"), quotas.UpdateOpts{ + restoredQuotas, err := quotas.Update(context.TODO(), client, os.Getenv("OS_PROJECT_NAME"), quotas.UpdateOpts{ FloatingIP: &originalQuotas.FloatingIP, Network: &originalQuotas.Network, Port: &originalQuotas.Port, diff --git a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go index 6acd09547a..864e8fecb7 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go +++ b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies.go @@ -1,6 +1,7 @@ package rbacpolicies import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -20,7 +21,7 @@ func CreateRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, tenantID, t.Logf("Trying to create rbac_policy") - rbacPolicy, err := rbacpolicies.Create(client, createOpts).Extract() + rbacPolicy, err := rbacpolicies.Create(context.TODO(), client, createOpts).Extract() if err != nil { return rbacPolicy, err } @@ -38,7 +39,7 @@ func CreateRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, tenantID, func DeleteRBACPolicy(t *testing.T, client *gophercloud.ServiceClient, rbacPolicyID string) { t.Logf("Trying to delete rbac_policy: %s", rbacPolicyID) - err := rbacpolicies.Delete(client, rbacPolicyID).ExtractErr() + err := rbacpolicies.Delete(context.TODO(), client, rbacPolicyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete rbac_policy %s: %v", rbacPolicyID, err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index 5ea473fe2d..bd749f2294 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -4,6 +4,7 @@ package rbacpolicies import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -56,12 +57,12 @@ func TestRBACPolicyCRUD(t *testing.T) { TargetTenant: project2.ID, } - _, err = rbacpolicies.Update(client, rbacPolicy.ID, updateOpts).Extract() + _, err = rbacpolicies.Update(context.TODO(), client, rbacPolicy.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Get the rbac-policy by ID t.Logf("Get rbac_policy by ID") - newrbacPolicy, err := rbacpolicies.Get(client, rbacPolicy.ID).Extract() + newrbacPolicy, err := rbacpolicies.Get(context.TODO(), client, rbacPolicy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newrbacPolicy) @@ -79,7 +80,7 @@ func TestRBACPolicyList(t *testing.T) { var allRBACPolicies []rbacPolicy - allPages, err := rbacpolicies.List(client, nil).AllPages() + allPages, err := rbacpolicies.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) err = rbacpolicies.ExtractRBACPolicesInto(allPages, &allRBACPolicies) diff --git a/internal/acceptance/openstack/networking/v2/extensions/security_test.go b/internal/acceptance/openstack/networking/v2/extensions/security_test.go index 6393761a85..b5b424a634 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/security_test.go @@ -4,6 +4,7 @@ package extensions import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -34,7 +35,7 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { Description: &description, } - newGroup, err := groups.Update(client, group.ID, updateOpts).Extract() + newGroup, err := groups.Update(context.TODO(), client, group.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) @@ -42,7 +43,7 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { th.AssertEquals(t, newGroup.Description, description) listOpts := groups.ListOpts{} - allPages, err := groups.List(client, listOpts).AllPages() + allPages, err := groups.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allGroups, err := groups.ExtractGroups(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go index 7f2332b325..3594e94ad4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go +++ b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -26,7 +27,7 @@ func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetp t.Logf("Attempting to create a subnetpool: %s", subnetPoolName) - subnetPool, err := subnetpools.Create(client, createOpts).Extract() + subnetPool, err := subnetpools.Create(context.TODO(), client, createOpts).Extract() if err != nil { return nil, err } @@ -44,7 +45,7 @@ func CreateSubnetPool(t *testing.T, client *gophercloud.ServiceClient) (*subnetp func DeleteSubnetPool(t *testing.T, client *gophercloud.ServiceClient, subnetPoolID string) { t.Logf("Attempting to delete the subnetpool: %s", subnetPoolID) - err := subnetpools.Delete(client, subnetPoolID).ExtractErr() + err := subnetpools.Delete(context.TODO(), client, subnetPoolID).ExtractErr() if err != nil { t.Fatalf("Unable to delete subnetpool %s: %v", subnetPoolID, err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index 3f434a1ed8..2e714d8ab0 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -30,17 +31,17 @@ func TestSubnetPoolsCRUD(t *testing.T) { Description: &newDescription, } - _, err = subnetpools.Update(client, subnetPool.ID, updateOpts).Extract() + _, err = subnetpools.Update(context.TODO(), client, subnetPool.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newSubnetPool, err := subnetpools.Get(client, subnetPool.ID).Extract() + newSubnetPool, err := subnetpools.Get(context.TODO(), client, subnetPool.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSubnetPool) th.AssertEquals(t, newSubnetPool.Name, newName) th.AssertEquals(t, newSubnetPool.Description, newDescription) - allPages, err := subnetpools.List(client, nil).AllPages() + allPages, err := subnetpools.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allSubnetPools, err := subnetpools.ExtractSubnetPools(allPages) diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go index e6726ad7f1..42cfb737da 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -4,6 +4,7 @@ package trunk_details import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -27,7 +28,7 @@ func TestListPortWithSubports(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - _, err = extensions.Get(client, "trunk-details").Extract() + _, err = extensions.Get(context.TODO(), client, "trunk-details").Extract() if err != nil { t.Skip("This test requires trunk-details Neutron extension") } @@ -72,7 +73,7 @@ func TestListPortWithSubports(t *testing.T) { defer v2Trunks.DeleteTrunk(t, client, trunk.ID) // Test LIST ports with trunk details - allPages, err := ports.List(client, ports.ListOpts{ID: parentPort.ID}).AllPages() + allPages, err := ports.List(client, ports.ListOpts{ID: parentPort.ID}).AllPages(context.TODO()) th.AssertNoErr(t, err) var allPorts []portWithTrunkDetails @@ -103,7 +104,7 @@ func TestListPortWithSubports(t *testing.T) { }, port.TrunkDetails.SubPorts[1].Subport) // Test GET port with trunk details - err = ports.Get(client, parentPort.ID).ExtractInto(&port) + err = ports.Get(context.TODO(), 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{ diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go index f35940631c..29f18dab62 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks.go @@ -1,6 +1,7 @@ package trunks import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -28,7 +29,7 @@ func CreateTrunk(t *testing.T, client *gophercloud.ServiceClient, parentPortID s } t.Logf("Attempting to create trunk: %s", opts.Name) - trunk, err = trunks.Create(client, opts).Extract() + trunk, err = trunks.Create(context.TODO(), client, opts).Extract() if err == nil { t.Logf("Successfully created trunk") } @@ -37,7 +38,7 @@ func CreateTrunk(t *testing.T, client *gophercloud.ServiceClient, parentPortID s func DeleteTrunk(t *testing.T, client *gophercloud.ServiceClient, trunkID string) { t.Logf("Attempting to delete trunk: %s", trunkID) - err := trunks.Delete(client, trunkID).ExtractErr() + err := trunks.Delete(context.TODO(), client, trunkID).ExtractErr() if err != nil { t.Fatalf("Unable to delete trunk %s: %v", trunkID, err) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 5e2a7eeec2..8bd5a265d4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -4,6 +4,7 @@ package trunks import ( + "context" "sort" "testing" @@ -22,7 +23,7 @@ func TestTrunkCRUD(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(client, "trunk").Extract() + extension, err := extensions.Get(context.TODO(), client, "trunk").Extract() if err != nil { t.Skip("This test requires trunk Neutron extension") } @@ -67,7 +68,7 @@ func TestTrunkCRUD(t *testing.T) { } defer DeleteTrunk(t, client, trunk.ID) - _, err = trunks.Get(client, trunk.ID).Extract() + _, err = trunks.Get(context.TODO(), client, trunk.ID).Extract() if err != nil { t.Fatalf("Unable to get trunk: %v", err) } @@ -79,7 +80,7 @@ func TestTrunkCRUD(t *testing.T) { Name: &name, Description: &description, } - updatedTrunk, err := trunks.Update(client, trunk.ID, updateOpts).Extract() + updatedTrunk, err := trunks.Update(context.TODO(), client, trunk.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update trunk: %v", err) } @@ -96,7 +97,7 @@ func TestTrunkCRUD(t *testing.T) { th.AssertDeepEquals(t, updatedTrunk.Description, description) // Get subports - subports, err := trunks.GetSubports(client, trunk.ID).Extract() + subports, err := trunks.GetSubports(context.TODO(), client, trunk.ID).Extract() if err != nil { t.Fatalf("Unable to get subports from the Trunk: %v", err) } @@ -112,13 +113,13 @@ func TestTrunkList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(client, "trunk").Extract() + extension, err := extensions.Get(context.TODO(), 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() + allPages, err := trunks.List(client, nil).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list trunks: %v", err) } @@ -139,7 +140,7 @@ func TestTrunkSubportOperation(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(client, "trunk").Extract() + extension, err := extensions.Get(context.TODO(), client, "trunk").Extract() if err != nil { t.Skip("This test requires trunk Neutron extension") } @@ -199,7 +200,7 @@ func TestTrunkSubportOperation(t *testing.T) { }, }, } - updatedTrunk, err := trunks.AddSubports(client, trunk.ID, addSubportsOpts).Extract() + updatedTrunk, err := trunks.AddSubports(context.TODO(), client, trunk.ID, addSubportsOpts).Extract() if err != nil { t.Fatalf("Unable to add subports to the Trunk: %v", err) } @@ -214,7 +215,7 @@ func TestTrunkSubportOperation(t *testing.T) { {PortID: subport2.ID}, }, } - updatedAgainTrunk, err := trunks.RemoveSubports(client, trunk.ID, subRemoveOpts).Extract() + updatedAgainTrunk, err := trunks.RemoveSubports(context.TODO(), client, trunk.ID, subRemoveOpts).Extract() if err != nil { t.Fatalf("Unable to remove subports from the Trunk: %v", err) } @@ -227,7 +228,7 @@ func TestTrunkTags(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(client, "trunk").Extract() + extension, err := extensions.Get(context.TODO(), client, "trunk").Extract() if err != nil { t.Skip("This test requires trunk Neutron extension") } @@ -276,12 +277,12 @@ func TestTrunkTags(t *testing.T) { // docs say list of tags, but it's a set e.g no duplicates Tags: []string{"a", "b", "c"}, } - tags, err := attributestags.ReplaceAll(client, "trunks", trunk.ID, tagReplaceAllOpts).Extract() + tags, err := attributestags.ReplaceAll(context.TODO(), client, "trunks", trunk.ID, tagReplaceAllOpts).Extract() if err != nil { t.Fatalf("Unable to set trunk tags: %v", err) } - gtrunk, err := trunks.Get(client, trunk.ID).Extract() + gtrunk, err := trunks.Get(context.TODO(), client, trunk.ID).Extract() if err != nil { t.Fatalf("Unable to get trunk: %v", err) } @@ -290,23 +291,23 @@ func TestTrunkTags(t *testing.T) { th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) // Add a tag - err = attributestags.Add(client, "trunks", trunk.ID, "d").ExtractErr() + err = attributestags.Add(context.TODO(), client, "trunks", trunk.ID, "d").ExtractErr() th.AssertNoErr(t, err) // Delete a tag - err = attributestags.Delete(client, "trunks", trunk.ID, "a").ExtractErr() + err = attributestags.Delete(context.TODO(), client, "trunks", trunk.ID, "a").ExtractErr() th.AssertNoErr(t, err) // Verify expected tags are set in the List response - tags, err = attributestags.List(client, "trunks", trunk.ID).Extract() + tags, err = attributestags.List(context.TODO(), client, "trunks", trunk.ID).Extract() th.AssertNoErr(t, err) sort.Strings(tags) th.AssertDeepEquals(t, []string{"b", "c", "d"}, tags) // Delete all tags - err = attributestags.DeleteAll(client, "trunks", trunk.ID).ExtractErr() + err = attributestags.DeleteAll(context.TODO(), client, "trunks", trunk.ID).ExtractErr() th.AssertNoErr(t, err) - tags, err = attributestags.List(client, "trunks", trunk.ID).Extract() + tags, err = attributestags.List(context.TODO(), client, "trunks", trunk.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(tags)) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go index 603a790a89..87aa2d4e8d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -31,7 +32,7 @@ func ListVLANTransparentNetworks(t *testing.T, client *gophercloud.ServiceClient t.Log("Attempting to list VLAN-transparent networks") - allPages, err := networks.List(client, listOpts).AllPages() + allPages, err := networks.List(client, listOpts).AllPages(context.TODO()) if err != nil { return nil, err } @@ -63,7 +64,7 @@ func CreateVLANTransparentNetwork(t *testing.T, client *gophercloud.ServiceClien t.Logf("Attempting to create a VLAN-transparent network: %s", networkName) var network VLANTransparentNetwork - err := networks.Create(client, createOpts).ExtractInto(&network) + err := networks.Create(context.TODO(), client, createOpts).ExtractInto(&network) if err != nil { return nil, err } @@ -93,7 +94,7 @@ func UpdateVLANTransparentNetwork(t *testing.T, client *gophercloud.ServiceClien t.Logf("Attempting to update a VLAN-transparent network: %s", networkID) var network VLANTransparentNetwork - err := networks.Update(client, networkID, updateOpts).ExtractInto(&network) + err := networks.Update(context.TODO(), client, networkID, updateOpts).ExtractInto(&network) if err != nil { return nil, err } diff --git a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go index 892ecd8dd7..c21c9c69de 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestVLANTransparentCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(client, "vlan-transparent").Extract() + extension, err := extensions.Get(context.TODO(), client, "vlan-transparent").Extract() if err != nil { t.Skip("This test requires vlan-transparent Neutron extension") } diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index fd5b8e5e48..bfb16ef898 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -4,6 +4,7 @@ package vpnaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestGroupList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := endpointgroups.List(client, nil).AllPages() + allPages, err := endpointgroups.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allGroups, err := endpointgroups.ExtractEndpointGroups(allPages) @@ -36,7 +37,7 @@ func TestGroupCRUD(t *testing.T) { defer DeleteEndpointGroup(t, client, group.ID) tools.PrintResource(t, group) - newGroup, err := endpointgroups.Get(client, group.ID).Extract() + newGroup, err := endpointgroups.Get(context.TODO(), client, group.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newGroup) @@ -46,7 +47,7 @@ func TestGroupCRUD(t *testing.T) { Name: &updatedName, Description: &updatedDescription, } - updatedGroup, err := endpointgroups.Update(client, group.ID, updateOpts).Extract() + updatedGroup, err := endpointgroups.Update(context.TODO(), client, group.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedGroup) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index 14d2032e1b..d8a233b8c4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -4,6 +4,7 @@ package vpnaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestIKEPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := ikepolicies.List(client, nil).AllPages() + allPages, err := ikepolicies.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := ikepolicies.ExtractPolicies(allPages) @@ -37,7 +38,7 @@ func TestIKEPolicyCRUD(t *testing.T) { tools.PrintResource(t, policy) - newPolicy, err := ikepolicies.Get(client, policy.ID).Extract() + newPolicy, err := ikepolicies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) @@ -50,7 +51,7 @@ func TestIKEPolicyCRUD(t *testing.T) { Value: 7000, }, } - updatedPolicy, err := ikepolicies.Update(client, policy.ID, updateOpts).Extract() + updatedPolicy, err := ikepolicies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, updatedPolicy) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index d5deb7f738..fbed901888 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -4,6 +4,7 @@ package vpnaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestIPSecPolicyList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := ipsecpolicies.List(client, nil).AllPages() + allPages, err := ipsecpolicies.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicies, err := ipsecpolicies.ExtractPolicies(allPages) @@ -41,11 +42,11 @@ func TestIPSecPolicyCRUD(t *testing.T) { Description: &updatedDescription, } - policy, err = ipsecpolicies.Update(client, policy.ID, updateOpts).Extract() + policy, err = ipsecpolicies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, policy) - newPolicy, err := ipsecpolicies.Get(client, policy.ID).Extract() + newPolicy, err := ipsecpolicies.Get(context.TODO(), client, policy.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPolicy) } diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index 92cff63b6c..872e5e8a3e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -4,6 +4,7 @@ package vpnaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestServiceList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := services.List(client, nil).AllPages() + allPages, err := services.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) @@ -41,7 +42,7 @@ func TestServiceCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteService(t, client, service.ID) - newService, err := services.Get(client, service.ID).Extract() + newService, err := services.Get(context.TODO(), client, service.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, service) diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index 5a4b8358e2..c201483295 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -4,6 +4,7 @@ package vpnaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestConnectionList(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - allPages, err := siteconnections.List(client, nil).AllPages() + allPages, err := siteconnections.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allConnections, err := siteconnections.ExtractConnections(allPages) @@ -54,13 +55,13 @@ func TestConnectionCRUD(t *testing.T) { SubnetID: subnet.ID, } - _, err = routers.AddInterface(client, router.ID, aiOpts).Extract() + _, err = routers.AddInterface(context.TODO(), client, router.ID, aiOpts).Extract() th.AssertNoErr(t, err) defer func() { riOpts := routers.RemoveInterfaceOpts{ SubnetID: subnet.ID, } - routers.RemoveInterface(client, router.ID, riOpts) + routers.RemoveInterface(context.TODO(), client, router.ID, riOpts) }() // Create all needed resources for the connection @@ -88,7 +89,7 @@ func TestConnectionCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSiteConnection(t, client, conn.ID) - newConnection, err := siteconnections.Get(client, conn.ID).Extract() + newConnection, err := siteconnections.Get(context.TODO(), client, conn.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, conn) diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go index c6db6ac425..07b9e38839 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/vpnaas.go @@ -1,6 +1,7 @@ package vpnaas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -26,7 +27,7 @@ func CreateService(t *testing.T, client *gophercloud.ServiceClient, routerID str AdminStateUp: &iTrue, RouterID: routerID, } - service, err := services.Create(client, createOpts).Extract() + service, err := services.Create(context.TODO(), client, createOpts).Extract() if err != nil { return service, err } @@ -44,7 +45,7 @@ func CreateService(t *testing.T, client *gophercloud.ServiceClient, routerID str func DeleteService(t *testing.T, client *gophercloud.ServiceClient, serviceID string) { t.Logf("Attempting to delete service: %s", serviceID) - err := services.Delete(client, serviceID).ExtractErr() + err := services.Delete(context.TODO(), client, serviceID).ExtractErr() if err != nil { t.Fatalf("Unable to delete service %s: %v", serviceID, err) } @@ -63,7 +64,7 @@ func CreateIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ipsecp Name: policyName, } - policy, err := ipsecpolicies.Create(client, createOpts).Extract() + policy, err := ipsecpolicies.Create(context.TODO(), client, createOpts).Extract() if err != nil { return policy, err } @@ -88,7 +89,7 @@ func CreateIKEPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ikepolic PFS: ikepolicies.PFSGroup5, } - policy, err := ikepolicies.Create(client, createOpts).Extract() + policy, err := ikepolicies.Create(context.TODO(), client, createOpts).Extract() if err != nil { return policy, err } @@ -106,7 +107,7 @@ func CreateIKEPolicy(t *testing.T, client *gophercloud.ServiceClient) (*ikepolic func DeleteIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete IPSec policy: %s", policyID) - err := ipsecpolicies.Delete(client, policyID).ExtractErr() + err := ipsecpolicies.Delete(context.TODO(), client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete IPSec policy %s: %v", policyID, err) } @@ -120,7 +121,7 @@ func DeleteIPSecPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID func DeleteIKEPolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { t.Logf("Attempting to delete policy: %s", policyID) - err := ikepolicies.Delete(client, policyID).ExtractErr() + err := ikepolicies.Delete(context.TODO(), client, policyID).ExtractErr() if err != nil { t.Fatalf("Unable to delete IKE policy %s: %v", policyID, err) } @@ -143,7 +144,7 @@ func CreateEndpointGroup(t *testing.T, client *gophercloud.ServiceClient) (*endp "10.3.0.0/24", }, } - group, err := endpointgroups.Create(client, createOpts).Extract() + group, err := endpointgroups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return group, err } @@ -169,7 +170,7 @@ func CreateEndpointGroupWithCIDR(t *testing.T, client *gophercloud.ServiceClient cidr, }, } - group, err := endpointgroups.Create(client, createOpts).Extract() + group, err := endpointgroups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return group, err } @@ -188,7 +189,7 @@ func CreateEndpointGroupWithCIDR(t *testing.T, client *gophercloud.ServiceClient func DeleteEndpointGroup(t *testing.T, client *gophercloud.ServiceClient, epGroupID string) { t.Logf("Attempting to delete endpoint group: %s", epGroupID) - err := endpointgroups.Delete(client, epGroupID).ExtractErr() + err := endpointgroups.Delete(context.TODO(), client, epGroupID).ExtractErr() if err != nil { t.Fatalf("Unable to delete endpoint group %s: %v", epGroupID, err) } @@ -211,7 +212,7 @@ func CreateEndpointGroupWithSubnet(t *testing.T, client *gophercloud.ServiceClie subnetID, }, } - group, err := endpointgroups.Create(client, createOpts).Extract() + group, err := endpointgroups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return group, err } @@ -245,7 +246,7 @@ func CreateSiteConnection(t *testing.T, client *gophercloud.ServiceClient, ikepo PeerID: "172.24.4.233", MTU: 1500, } - connection, err := siteconnections.Create(client, createOpts).Extract() + connection, err := siteconnections.Create(context.TODO(), client, createOpts).Extract() if err != nil { return connection, err } @@ -263,7 +264,7 @@ func CreateSiteConnection(t *testing.T, client *gophercloud.ServiceClient, ikepo func DeleteSiteConnection(t *testing.T, client *gophercloud.ServiceClient, siteConnectionID string) { t.Logf("Attempting to delete site connection: %s", siteConnectionID) - err := siteconnections.Delete(client, siteConnectionID).ExtractErr() + err := siteconnections.Delete(context.TODO(), client, siteConnectionID).ExtractErr() if err != nil { t.Fatalf("Unable to delete site connection %s: %v", siteConnectionID, err) } diff --git a/internal/acceptance/openstack/networking/v2/networking.go b/internal/acceptance/openstack/networking/v2/networking.go index 8501b70fb0..34057cbeb8 100644 --- a/internal/acceptance/openstack/networking/v2/networking.go +++ b/internal/acceptance/openstack/networking/v2/networking.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "testing" @@ -33,7 +34,7 @@ func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.N t.Logf("Attempting to create network: %s", networkName) - network, err := networks.Create(client, createOpts).Extract() + network, err := networks.Create(context.TODO(), client, createOpts).Extract() if err != nil { return network, err } @@ -63,7 +64,7 @@ func CreateNetworkWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceC t.Logf("Attempting to create network: %s", networkName) - network, err := networks.Create(client, createOpts).Extract() + network, err := networks.Create(context.TODO(), client, createOpts).Extract() if err != nil { return network, err } @@ -91,7 +92,7 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subn FixedIPs: []ports.IP{{SubnetID: subnetID}}, } - port, err := ports.Create(client, createOpts).Extract() + port, err := ports.Create(context.TODO(), client, createOpts).Extract() if err != nil { return port, err } @@ -100,7 +101,7 @@ func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subn return port, err } - newPort, err := ports.Get(client, port.ID).Extract() + newPort, err := ports.Get(context.TODO(), client, port.ID).Extract() if err != nil { return newPort, err } @@ -129,7 +130,7 @@ func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClie SecurityGroups: &[]string{}, } - port, err := ports.Create(client, createOpts).Extract() + port, err := ports.Create(context.TODO(), client, createOpts).Extract() if err != nil { return port, err } @@ -138,7 +139,7 @@ func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClie return port, err } - newPort, err := ports.Get(client, port.ID).Extract() + newPort, err := ports.Get(context.TODO(), client, port.ID).Extract() if err != nil { return newPort, err } @@ -170,7 +171,7 @@ func CreatePortWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClie PortSecurityEnabled: &iFalse, } - port, err := ports.Create(client, createOpts).Extract() + port, err := ports.Create(context.TODO(), client, createOpts).Extract() if err != nil { return port, err } @@ -179,7 +180,7 @@ func CreatePortWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClie return port, err } - newPort, err := ports.Get(client, port.ID).Extract() + newPort, err := ports.Get(context.TODO(), client, port.ID).Extract() if err != nil { return newPort, err } @@ -216,7 +217,7 @@ func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient } port := &PortWithExtraDHCPOpts{} - err := ports.Create(client, createOpts).ExtractInto(port) + err := ports.Create(context.TODO(), client, createOpts).ExtractInto(port) if err != nil { return nil, err } @@ -225,7 +226,7 @@ func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient return nil, err } - err = ports.Get(client, port.ID).ExtractInto(port) + err = ports.Get(context.TODO(), client, port.ID).ExtractInto(port) if err != nil { return port, err } @@ -251,7 +252,7 @@ func CreatePortWithMultipleFixedIPs(t *testing.T, client *gophercloud.ServiceCli FixedIPs: []ports.IP{{SubnetID: subnetID}, {SubnetID: subnetID}}, } - port, err := ports.Create(client, createOpts).Extract() + port, err := ports.Create(context.TODO(), client, createOpts).Extract() if err != nil { return port, err } @@ -260,7 +261,7 @@ func CreatePortWithMultipleFixedIPs(t *testing.T, client *gophercloud.ServiceCli return port, err } - newPort, err := ports.Get(client, port.ID).Extract() + newPort, err := ports.Get(context.TODO(), client, port.ID).Extract() if err != nil { return newPort, err } @@ -303,7 +304,7 @@ func CreateSubnetWithCIDR(t *testing.T, client *gophercloud.ServiceClient, netwo t.Logf("Attempting to create subnet: %s", subnetName) - subnet, err := subnets.Create(client, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return subnet, err } @@ -341,7 +342,7 @@ func CreateSubnetWithServiceTypes(t *testing.T, client *gophercloud.ServiceClien t.Logf("Attempting to create subnet: %s", subnetName) - subnet, err := subnets.Create(client, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return subnet, err } @@ -376,7 +377,7 @@ func CreateSubnetWithDefaultGateway(t *testing.T, client *gophercloud.ServiceCli t.Logf("Attempting to create subnet: %s", subnetName) - subnet, err := subnets.Create(client, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return subnet, err } @@ -417,7 +418,7 @@ func CreateSubnetWithNoGateway(t *testing.T, client *gophercloud.ServiceClient, t.Logf("Attempting to create subnet: %s", subnetName) - subnet, err := subnets.Create(client, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return subnet, err } @@ -448,7 +449,7 @@ func CreateSubnetWithSubnetPool(t *testing.T, client *gophercloud.ServiceClient, t.Logf("Attempting to create subnet: %s", subnetName) - subnet, err := subnets.Create(client, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return subnet, err } @@ -476,7 +477,7 @@ func CreateSubnetWithSubnetPoolNoCIDR(t *testing.T, client *gophercloud.ServiceC t.Logf("Attempting to create subnet: %s", subnetName) - subnet, err := subnets.Create(client, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return subnet, err } @@ -505,7 +506,7 @@ func CreateSubnetWithSubnetPoolPrefixlen(t *testing.T, client *gophercloud.Servi t.Logf("Attempting to create subnet: %s", subnetName) - subnet, err := subnets.Create(client, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract() if err != nil { return subnet, err } @@ -523,7 +524,7 @@ func CreateSubnetWithSubnetPoolPrefixlen(t *testing.T, client *gophercloud.Servi func DeleteNetwork(t *testing.T, client *gophercloud.ServiceClient, networkID string) { t.Logf("Attempting to delete network: %s", networkID) - err := networks.Delete(client, networkID).ExtractErr() + err := networks.Delete(context.TODO(), client, networkID).ExtractErr() if err != nil { t.Fatalf("Unable to delete network %s: %v", networkID, err) } @@ -537,7 +538,7 @@ func DeleteNetwork(t *testing.T, client *gophercloud.ServiceClient, networkID st func DeletePort(t *testing.T, client *gophercloud.ServiceClient, portID string) { t.Logf("Attempting to delete port: %s", portID) - err := ports.Delete(client, portID).ExtractErr() + err := ports.Delete(context.TODO(), client, portID).ExtractErr() if err != nil { t.Fatalf("Unable to delete port %s: %v", portID, err) } @@ -551,7 +552,7 @@ func DeletePort(t *testing.T, client *gophercloud.ServiceClient, portID string) func DeleteSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID string) { t.Logf("Attempting to delete subnet: %s", subnetID) - err := subnets.Delete(client, subnetID).ExtractErr() + err := subnets.Delete(context.TODO(), client, subnetID).ExtractErr() if err != nil { t.Fatalf("Unable to delete subnet %s: %v", subnetID, err) } @@ -561,7 +562,7 @@ func DeleteSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID stri func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string) error { return tools.WaitFor(func() (bool, error) { - p, err := ports.Get(client, portID).Extract() + p, err := ports.Get(context.TODO(), client, portID).Extract() if err != nil { return false, err } @@ -585,7 +586,7 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) Name: name, } - pages, err := networks.List(client, listOpts).AllPages() + pages, err := networks.List(client, listOpts).AllPages(context.TODO()) if err != nil { return "", err } diff --git a/internal/acceptance/openstack/networking/v2/networks_test.go b/internal/acceptance/openstack/networking/v2/networks_test.go index a1e70c3b08..b9b7b10352 100644 --- a/internal/acceptance/openstack/networking/v2/networks_test.go +++ b/internal/acceptance/openstack/networking/v2/networks_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "strings" "testing" @@ -38,7 +39,7 @@ func TestNetworksExternalList(t *testing.T) { External: &iTrue, } - allPages, err := networks.List(client, listOpts).AllPages() + allPages, err := networks.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &allNetworks) @@ -62,7 +63,7 @@ func TestNetworksExternalList(t *testing.T) { External: &iFalse, } - allPages, err = networks.List(client, listOpts).AllPages() + allPages, err = networks.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) v, err := networks.ExtractNetworks(allPages) @@ -87,10 +88,10 @@ func TestNetworksCRUD(t *testing.T) { Description: &newDescription, } - _, err = networks.Update(client, network.ID, updateOpts).Extract() + _, err = networks.Update(context.TODO(), client, network.ID, updateOpts).Extract() th.AssertNoErr(t, err) - newNetwork, err := networks.Get(client, network.ID).Extract() + newNetwork, err := networks.Get(context.TODO(), client, network.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newNetwork) @@ -104,7 +105,7 @@ func TestNetworksCRUD(t *testing.T) { var allNetworks []networkWithExt - allPages, err := networks.List(client, nil).AllPages() + allPages, err := networks.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &allNetworks) @@ -136,7 +137,7 @@ func TestNetworksPortSecurityCRUD(t *testing.T) { portsecurity.PortSecurityExt } - err = networks.Get(client, network.ID).ExtractInto(&networkWithExtensions) + err = networks.Get(context.TODO(), client, network.ID).ExtractInto(&networkWithExtensions) th.AssertNoErr(t, err) tools.PrintResource(t, networkWithExtensions) @@ -148,7 +149,7 @@ func TestNetworksPortSecurityCRUD(t *testing.T) { PortSecurityEnabled: &iTrue, } - err = networks.Update(client, network.ID, updateOpts).ExtractInto(&networkWithExtensions) + err = networks.Update(context.TODO(), client, network.ID, updateOpts).ExtractInto(&networkWithExtensions) th.AssertNoErr(t, err) tools.PrintResource(t, networkWithExtensions) @@ -176,7 +177,7 @@ func TestNetworksRevision(t *testing.T) { Name: &newName, Description: &newDescription, } - network, err = networks.Update(client, network.ID, updateOpts).Extract() + network, err = networks.Update(context.TODO(), client, network.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, network) @@ -188,14 +189,14 @@ func TestNetworksRevision(t *testing.T) { Description: &newDescription, RevisionNumber: &oldRevisionNumber, } - _, err = networks.Update(client, network.ID, updateOpts).Extract() + _, err = networks.Update(context.TODO(), 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() + network, err = networks.Get(context.TODO(), client, network.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, network) @@ -207,7 +208,7 @@ func TestNetworksRevision(t *testing.T) { Description: &newDescription, RevisionNumber: &network.RevisionNumber, } - network, err = networks.Update(client, network.ID, updateOpts).Extract() + network, err = networks.Update(context.TODO(), client, network.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, network) diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index 1cf7cecf4a..0c7352ec34 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "fmt" "strings" "testing" @@ -49,7 +50,7 @@ func TestPortsCRUD(t *testing.T) { Name: &newPortName, Description: &newPortDescription, } - newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -57,7 +58,7 @@ func TestPortsCRUD(t *testing.T) { th.AssertEquals(t, newPort.Name, newPortName) th.AssertEquals(t, newPort.Description, newPortDescription) - allPages, err := ports.List(client, nil).AllPages() + allPages, err := ports.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPorts, err := ports.ExtractPorts(allPages) @@ -125,7 +126,7 @@ func TestPortsCRUD(t *testing.T) { }, } { t.Run(fmt.Sprintf("List ports by %s", tt.name), func(t *testing.T) { - allPages, err := ports.List(client, tt.opts).AllPages() + allPages, err := ports.List(client, tt.opts).AllPages(context.TODO()) th.AssertNoErr(t, err) allPorts, err := ports.ExtractPorts(allPages) @@ -189,14 +190,14 @@ func TestPortsRemoveSecurityGroups(t *testing.T) { updateOpts := ports.UpdateOpts{ SecurityGroups: &[]string{group.ID}, } - newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Remove the group updateOpts = ports.UpdateOpts{ SecurityGroups: &[]string{}, } - newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -236,7 +237,7 @@ func TestPortsDontAlterSecurityGroups(t *testing.T) { updateOpts := ports.UpdateOpts{ SecurityGroups: &[]string{group.ID}, } - newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Update the port again @@ -244,7 +245,7 @@ func TestPortsDontAlterSecurityGroups(t *testing.T) { updateOpts = ports.UpdateOpts{ Name: &name, } - newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -307,14 +308,14 @@ func TestPortsRemoveAddressPair(t *testing.T) { {IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, }, } - newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Remove the address pair updateOpts = ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{}, } - newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -351,7 +352,7 @@ func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { {IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, }, } - newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -361,7 +362,7 @@ func TestPortsDontUpdateAllowedAddressPairs(t *testing.T) { updateOpts = ports.UpdateOpts{ Name: &name, } - newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -395,7 +396,7 @@ func TestPortsPortSecurityCRUD(t *testing.T) { portsecurity.PortSecurityExt } - err = ports.Get(client, port.ID).ExtractInto(&portWithExt) + err = ports.Get(context.TODO(), client, port.ID).ExtractInto(&portWithExt) th.AssertNoErr(t, err) tools.PrintResource(t, portWithExt) @@ -407,7 +408,7 @@ func TestPortsPortSecurityCRUD(t *testing.T) { PortSecurityEnabled: &iTrue, } - err = ports.Update(client, port.ID, updateOpts).ExtractInto(&portWithExt) + err = ports.Update(context.TODO(), client, port.ID, updateOpts).ExtractInto(&portWithExt) th.AssertNoErr(t, err) tools.PrintResource(t, portWithExt) @@ -458,7 +459,7 @@ func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { } newPort := &PortWithExtraDHCPOpts{} - err = ports.Update(client, port.ID, updateOpts).ExtractInto(newPort) + err = ports.Update(context.TODO(), client, port.ID, updateOpts).ExtractInto(newPort) th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -493,7 +494,7 @@ func TestPortsRevision(t *testing.T) { }, RevisionNumber: &port.RevisionNumber, } - newPort, err := ports.Update(client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -503,14 +504,14 @@ func TestPortsRevision(t *testing.T) { AllowedAddressPairs: &[]ports.AddressPair{}, RevisionNumber: &port.RevisionNumber, } - newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + newPort, err = ports.Update(context.TODO(), 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() + newPort, err = ports.Get(context.TODO(), client, port.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -519,7 +520,7 @@ func TestPortsRevision(t *testing.T) { updateOpts = ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{}, } - newPort, err = ports.Update(client, port.ID, updateOpts).Extract() + newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) diff --git a/internal/acceptance/openstack/networking/v2/subnets_test.go b/internal/acceptance/openstack/networking/v2/subnets_test.go index bed5ae1382..66740c8222 100644 --- a/internal/acceptance/openstack/networking/v2/subnets_test.go +++ b/internal/acceptance/openstack/networking/v2/subnets_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "fmt" "strings" "testing" @@ -38,18 +39,18 @@ func TestSubnetCRUD(t *testing.T) { Name: &newSubnetName, Description: &newSubnetDescription, } - _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + _, err = subnets.Update(context.TODO(), client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Get subnet - newSubnet, err := subnets.Get(client, subnet.ID).Extract() + newSubnet, err := subnets.Get(context.TODO(), client, subnet.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSubnet) th.AssertEquals(t, newSubnet.Name, newSubnetName) th.AssertEquals(t, newSubnet.Description, newSubnetDescription) - allPages, err := subnets.List(client, nil).AllPages() + allPages, err := subnets.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allSubnets, err := subnets.ExtractSubnets(allPages) @@ -86,7 +87,7 @@ func TestSubnetsServiceType(t *testing.T) { ServiceTypes: &serviceTypes, } - newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract() + newSubnet, err := subnets.Update(context.TODO(), client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, newSubnet.ServiceTypes, serviceTypes) @@ -117,7 +118,7 @@ func TestSubnetsDefaultGateway(t *testing.T) { GatewayIP: &noGateway, } - newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract() + newSubnet, err := subnets.Update(context.TODO(), client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) if newSubnet.GatewayIP != "" { @@ -151,7 +152,7 @@ func TestSubnetsNoGateway(t *testing.T) { GatewayIP: &newGateway, } - newSubnet, err := subnets.Update(client, subnet.ID, updateOpts).Extract() + newSubnet, err := subnets.Update(context.TODO(), client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) if newSubnet.GatewayIP == "" { @@ -267,11 +268,11 @@ func TestSubnetDNSNameservers(t *testing.T) { updateOpts := subnets.UpdateOpts{ DNSNameservers: &dnsNameservers, } - _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + _, err = subnets.Update(context.TODO(), client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Get subnet - newSubnet, err := subnets.Get(client, subnet.ID).Extract() + newSubnet, err := subnets.Get(context.TODO(), client, subnet.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSubnet) @@ -282,11 +283,11 @@ func TestSubnetDNSNameservers(t *testing.T) { updateOpts = subnets.UpdateOpts{ DNSNameservers: &dnsNameservers, } - _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + _, err = subnets.Update(context.TODO(), client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Get subnet - newSubnet, err = subnets.Get(client, subnet.ID).Extract() + newSubnet, err = subnets.Get(context.TODO(), client, subnet.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newSubnet) @@ -320,7 +321,7 @@ func TestSubnetsRevision(t *testing.T) { Name: &newSubnetName, Description: &newSubnetDescription, } - subnet, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + subnet, err = subnets.Update(context.TODO(), client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, subnet) @@ -332,14 +333,14 @@ func TestSubnetsRevision(t *testing.T) { Description: &newSubnetDescription, RevisionNumber: &oldRevisionNumber, } - _, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + _, err = subnets.Update(context.TODO(), 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() + subnet, err = subnets.Get(context.TODO(), client, subnet.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, subnet) @@ -351,7 +352,7 @@ func TestSubnetsRevision(t *testing.T) { Description: &newSubnetDescription, RevisionNumber: &subnet.RevisionNumber, } - subnet, err = subnets.Update(client, subnet.ID, updateOpts).Extract() + subnet, err = subnets.Update(context.TODO(), client, subnet.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, subnet) diff --git a/internal/acceptance/openstack/objectstorage/v1/accounts_test.go b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go index 36107ee9b7..79f64b7e9a 100644 --- a/internal/acceptance/openstack/objectstorage/v1/accounts_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "strings" "testing" @@ -22,7 +23,7 @@ func TestAccounts(t *testing.T) { metadata := map[string]string{ "Gophercloud-Test": "accounts", } - updateres := accounts.Update(client, accounts.UpdateOpts{Metadata: metadata}) + updateres := accounts.Update(context.TODO(), client, accounts.UpdateOpts{Metadata: metadata}) t.Logf("Update Account Response: %+v\n", updateres) updateHeaders, err := updateres.Extract() th.AssertNoErr(t, err) @@ -34,12 +35,12 @@ func TestAccounts(t *testing.T) { for k := range metadata { tempMap[k] = "" } - updateres = accounts.Update(client, accounts.UpdateOpts{Metadata: tempMap}) + updateres = accounts.Update(context.TODO(), client, accounts.UpdateOpts{Metadata: tempMap}) th.AssertNoErr(t, updateres.Err) }() // Extract the custom metadata from the 'Get' response. - res := accounts.Get(client, nil) + res := accounts.Get(context.TODO(), client, nil) h, err := res.Extract() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/objectstorage/v1/containers_test.go b/internal/acceptance/openstack/objectstorage/v1/containers_test.go index 82000968e7..8f1eb00578 100644 --- a/internal/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/containers_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "strings" "testing" @@ -31,20 +32,20 @@ func TestContainers(t *testing.T) { // Create numContainers containers. for i := 0; i < len(cNames); i++ { - res := containers.Create(client, cNames[i], nil) + res := containers.Create(context.TODO(), client, cNames[i], nil) th.AssertNoErr(t, res.Err) } // Delete the numContainers containers after function completion. defer func() { for i := 0; i < len(cNames); i++ { - res := containers.Delete(client, cNames[i]) + res := containers.Delete(context.TODO(), client, cNames[i]) th.AssertNoErr(t, res.Err) } }() // List the numContainer names that were just created. To just list those, // the 'prefix' parameter is used. - err = containers.List(client, &containers.ListOpts{Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { + err = containers.List(client, &containers.ListOpts{Prefix: "gophercloud-test-container-"}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { containerList, err := containers.ExtractInfo(page) th.AssertNoErr(t, err) @@ -58,7 +59,7 @@ func TestContainers(t *testing.T) { th.AssertNoErr(t, err) // List the info for the numContainer containers that were created. - err = containers.List(client, &containers.ListOpts{Prefix: "gophercloud-test-container-"}).EachPage(func(page pagination.Page) (bool, error) { + err = containers.List(client, &containers.ListOpts{Prefix: "gophercloud-test-container-"}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { containerList, err := containers.ExtractNames(page) th.AssertNoErr(t, err) for _, n := range containerList { @@ -86,7 +87,7 @@ func TestContainers(t *testing.T) { ContainerSyncKey: &empty, } - updateres := containers.Update(client, cNames[0], opts) + updateres := containers.Update(context.TODO(), client, cNames[0], opts) th.AssertNoErr(t, updateres.Err) // After the tests are done, delete the metadata that was set. defer func() { @@ -101,7 +102,7 @@ func TestContainers(t *testing.T) { ContainerWrite: &empty, DetectContentType: &iTrue, } - res := containers.Update(client, cNames[0], opts) + res := containers.Update(context.TODO(), client, cNames[0], opts) th.AssertNoErr(t, res.Err) // confirm the metadata was removed @@ -109,7 +110,7 @@ func TestContainers(t *testing.T) { Newest: true, } - resp := containers.Get(client, cNames[0], getOpts) + resp := containers.Get(context.TODO(), client, cNames[0], getOpts) cm, err := resp.ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { @@ -128,7 +129,7 @@ func TestContainers(t *testing.T) { Newest: true, } - resp := containers.Get(client, cNames[0], getOpts) + resp := containers.Get(context.TODO(), client, cNames[0], getOpts) cm, err := resp.ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { @@ -142,7 +143,7 @@ func TestContainers(t *testing.T) { th.AssertEquals(t, write, strings.Join(container.Write, ",")) // Retrieve a container's timestamp - cHeaders, err := containers.Get(client, cNames[0], getOpts).Extract() + cHeaders, err := containers.Get(context.TODO(), client, cNames[0], getOpts).Extract() th.AssertNoErr(t, err) t.Logf("Container: Name [%s] Timestamp: [%f]\n", cNames[0], cHeaders.Timestamp) } @@ -163,20 +164,20 @@ func TestListAllContainers(t *testing.T) { // Create numContainers containers. for i := 0; i < len(cNames); i++ { - res := containers.Create(client, cNames[i], nil) + res := containers.Create(context.TODO(), client, cNames[i], nil) th.AssertNoErr(t, res.Err) } // Delete the numContainers containers after function completion. defer func() { for i := 0; i < len(cNames); i++ { - res := containers.Delete(client, cNames[i]) + res := containers.Delete(context.TODO(), client, cNames[i]) th.AssertNoErr(t, res.Err) } }() // List all the numContainer names that were just created. To just list those, // the 'prefix' parameter is used. - allPages, err := containers.List(client, &containers.ListOpts{Limit: 5, Prefix: "gophercloud-test-container-"}).AllPages() + allPages, err := containers.List(client, &containers.ListOpts{Limit: 5, Prefix: "gophercloud-test-container-"}).AllPages(context.TODO()) th.AssertNoErr(t, err) containerInfoList, err := containers.ExtractInfo(allPages) th.AssertNoErr(t, err) @@ -187,7 +188,7 @@ func TestListAllContainers(t *testing.T) { th.AssertEquals(t, numContainers, len(containerInfoList)) // List the info for all the numContainer containers that were created. - allPages, err = containers.List(client, &containers.ListOpts{Limit: 2, Prefix: "gophercloud-test-container-"}).AllPages() + allPages, err = containers.List(client, &containers.ListOpts{Limit: 2, Prefix: "gophercloud-test-container-"}).AllPages(context.TODO()) th.AssertNoErr(t, err) containerNamesList, err := containers.ExtractNames(allPages) th.AssertNoErr(t, err) @@ -213,7 +214,7 @@ func TestBulkDeleteContainers(t *testing.T) { // Create numContainers containers. for i := 0; i < len(cNames); i++ { - res := containers.Create(client, cNames[i], nil) + res := containers.Create(context.TODO(), client, cNames[i], nil) th.AssertNoErr(t, res.Err) } @@ -223,13 +224,13 @@ func TestBulkDeleteContainers(t *testing.T) { NumberDeleted: numContainers, } - resp, err := containers.BulkDelete(client, cNames).Extract() + resp, err := containers.BulkDelete(context.TODO(), client, cNames).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, *resp) th.AssertDeepEquals(t, *resp, expectedResp) for _, c := range cNames { - _, err = containers.Get(client, c, nil).Extract() + _, err = containers.Get(context.TODO(), client, c, nil).Extract() th.AssertErr(t, err) } } diff --git a/internal/acceptance/openstack/objectstorage/v1/objects_test.go b/internal/acceptance/openstack/objectstorage/v1/objects_test.go index 450c81cd6a..60a5444ce8 100644 --- a/internal/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/objects_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "fmt" "io" "net/http" @@ -40,13 +41,13 @@ func TestObjects(t *testing.T) { opts := containers.CreateOpts{ TempURLKey: "super-secret", } - header, err := containers.Create(client, cName, opts).Extract() + header, err := containers.Create(context.TODO(), client, cName, opts).Extract() th.AssertNoErr(t, err) t.Logf("Create object headers: %+v\n", header) // Defer deletion of the container until after testing. defer func() { - res := containers.Delete(client, cName) + res := containers.Delete(context.TODO(), client, cName) th.AssertNoErr(t, res.Err) }() @@ -57,13 +58,13 @@ func TestObjects(t *testing.T) { createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents[i]), } - res := objects.Create(client, cName, oNames[i], createOpts) + res := objects.Create(context.TODO(), client, cName, oNames[i], createOpts) th.AssertNoErr(t, res.Err) } // Delete the objects after testing. defer func() { for i := 0; i < numObjects; i++ { - res := objects.Delete(client, cName, oNames[i], nil) + res := objects.Delete(context.TODO(), client, cName, oNames[i], nil) th.AssertNoErr(t, res.Err) } }() @@ -73,7 +74,7 @@ func TestObjects(t *testing.T) { Prefix: "test-object-", } - allPages, err := objects.List(client, cName, listOpts).AllPages() + allPages, err := objects.List(client, cName, listOpts).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list objects: %v", err) } @@ -94,7 +95,7 @@ func TestObjects(t *testing.T) { // Downloading the URL validates it (this cannot be done in unit tests). objURLs := make([]string, numObjects) for i := 0; i < numObjects; i++ { - objURLs[i], err = objects.CreateTempURL(client, cName, oNames[i], objects.CreateTempURLOpts{ + objURLs[i], err = objects.CreateTempURL(context.TODO(), client, cName, oNames[i], objects.CreateTempURLOpts{ Method: http.MethodGet, TTL: 180, }) @@ -113,7 +114,7 @@ func TestObjects(t *testing.T) { 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{ + objURLs[i], err = objects.CreateTempURL(context.TODO(), client, cName, oNames[i], objects.CreateTempURLOpts{ Method: http.MethodGet, Timestamp: time.Now().UTC().Add(180 * time.Second), Digest: "sha256", @@ -138,11 +139,11 @@ func TestObjects(t *testing.T) { copyOpts := objects.CopyOpts{ Destination: "/" + cName + "/" + oNames[1], } - copyres := objects.Copy(client, cName, oNames[0], copyOpts) + copyres := objects.Copy(context.TODO(), client, cName, oNames[0], copyOpts) th.AssertNoErr(t, copyres.Err) // Download one of the objects that was created above. - downloadres := objects.Download(client, cName, oNames[0], nil) + downloadres := objects.Download(context.TODO(), client, cName, oNames[0], nil) th.AssertNoErr(t, downloadres.Err) o1Content, err := downloadres.ExtractContent() @@ -152,7 +153,7 @@ func TestObjects(t *testing.T) { downloadOpts := objects.DownloadOpts{ Newest: true, } - downloadres = objects.Download(client, cName, oNames[1], downloadOpts) + downloadres = objects.Download(context.TODO(), client, cName, oNames[1], downloadOpts) th.AssertNoErr(t, downloadres.Err) o2Content, err := downloadres.ExtractContent() th.AssertNoErr(t, err) @@ -172,7 +173,7 @@ func TestObjects(t *testing.T) { ContentDisposition: &disposition, ContentType: &cType, } - updateres := objects.Update(client, cName, oNames[0], updateOpts) + updateres := objects.Update(context.TODO(), client, cName, oNames[0], updateOpts) th.AssertNoErr(t, updateres.Err) // Delete the object's metadata after testing. @@ -192,14 +193,14 @@ func TestObjects(t *testing.T) { ContentType: &cType, DetectContentType: &iTrue, } - res := objects.Update(client, cName, oNames[0], updateOpts) + res := objects.Update(context.TODO(), client, cName, oNames[0], updateOpts) th.AssertNoErr(t, res.Err) // Retrieve an object's metadata. getOpts := objects.GetOpts{ Newest: true, } - resp := objects.Get(client, cName, oNames[0], getOpts) + resp := objects.Get(context.TODO(), client, cName, oNames[0], getOpts) om, err := resp.ExtractMetadata() th.AssertNoErr(t, err) if len(om) > 0 { @@ -215,7 +216,7 @@ func TestObjects(t *testing.T) { getOpts := objects.GetOpts{ Newest: true, } - resp := objects.Get(client, cName, oNames[0], getOpts) + resp := objects.Get(context.TODO(), client, cName, oNames[0], getOpts) om, err := resp.ExtractMetadata() th.AssertNoErr(t, err) for k := range metadata { @@ -254,13 +255,13 @@ func TestObjectsListSubdir(t *testing.T) { // Create a container to hold the test objects. cName := "test-container-" + tools.RandomFunnyStringNoSlash(8) - _, err = containers.Create(client, cName, nil).Extract() + _, err = containers.Create(context.TODO(), client, cName, nil).Extract() th.AssertNoErr(t, err) // Defer deletion of the container until after testing. defer func() { t.Logf("Deleting container %s", cName) - res := containers.Delete(client, cName) + res := containers.Delete(context.TODO(), client, cName) th.AssertNoErr(t, res.Err) }() @@ -271,14 +272,14 @@ func TestObjectsListSubdir(t *testing.T) { createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents1[i]), } - res := objects.Create(client, cName, oNames1[i], createOpts) + res := objects.Create(context.TODO(), client, cName, oNames1[i], createOpts) th.AssertNoErr(t, res.Err) } // Delete the objects after testing. defer func() { for i := 0; i < numObjects; i++ { t.Logf("Deleting object %s", oNames1[i]) - res := objects.Delete(client, cName, oNames1[i], nil) + res := objects.Delete(context.TODO(), client, cName, oNames1[i], nil) th.AssertNoErr(t, res.Err) } }() @@ -289,14 +290,14 @@ func TestObjectsListSubdir(t *testing.T) { createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents2[i]), } - res := objects.Create(client, cName, oNames2[i], createOpts) + res := objects.Create(context.TODO(), client, cName, oNames2[i], createOpts) th.AssertNoErr(t, res.Err) } // Delete the objects after testing. defer func() { for i := 0; i < numObjects; i++ { t.Logf("Deleting object %s", oNames2[i]) - res := objects.Delete(client, cName, oNames2[i], nil) + res := objects.Delete(context.TODO(), client, cName, oNames2[i], nil) th.AssertNoErr(t, res.Err) } }() @@ -305,7 +306,7 @@ func TestObjectsListSubdir(t *testing.T) { Delimiter: "/", } - allPages, err := objects.List(client, cName, listOpts).AllPages() + allPages, err := objects.List(client, cName, listOpts).AllPages(context.TODO()) if err != nil { t.Fatal(err) } @@ -334,7 +335,7 @@ func TestObjectsListSubdir(t *testing.T) { Prefix: cSubdir2, } - allPages, err = objects.List(client, cName, listOpts).AllPages() + allPages, err = objects.List(client, cName, listOpts).AllPages(context.TODO()) if err != nil { t.Fatal(err) } @@ -371,13 +372,13 @@ func TestObjectsBulkDelete(t *testing.T) { // Create a container to hold the test objects. cName := "test-container-" + tools.RandomFunnyStringNoSlash(8) - _, err = containers.Create(client, cName, nil).Extract() + _, err = containers.Create(context.TODO(), client, cName, nil).Extract() th.AssertNoErr(t, err) // Defer deletion of the container until after testing. defer func() { t.Logf("Deleting container %s", cName) - res := containers.Delete(client, cName) + res := containers.Delete(context.TODO(), client, cName) th.AssertNoErr(t, res.Err) }() @@ -388,7 +389,7 @@ func TestObjectsBulkDelete(t *testing.T) { createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents1[i]), } - res := objects.Create(client, cName, oNames1[i], createOpts) + res := objects.Create(context.TODO(), client, cName, oNames1[i], createOpts) th.AssertNoErr(t, res.Err) } @@ -398,7 +399,7 @@ func TestObjectsBulkDelete(t *testing.T) { createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents2[i]), } - res := objects.Create(client, cName, oNames2[i], createOpts) + res := objects.Create(context.TODO(), client, cName, oNames2[i], createOpts) th.AssertNoErr(t, res.Err) } @@ -409,7 +410,7 @@ func TestObjectsBulkDelete(t *testing.T) { NumberDeleted: numObjects * 2, } - resp, err := objects.BulkDelete(client, cName, append(oNames1, oNames2...)).Extract() + resp, err := objects.BulkDelete(context.TODO(), client, cName, append(oNames1, oNames2...)).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, *resp, expectedResp) @@ -418,7 +419,7 @@ func TestObjectsBulkDelete(t *testing.T) { Delimiter: "/", } - allPages, err := objects.List(client, cName, listOpts).AllPages() + allPages, err := objects.List(client, cName, listOpts).AllPages(context.TODO()) if err != nil { t.Fatal(err) } diff --git a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go index f817097ad2..d38629e04d 100644 --- a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "strings" "testing" @@ -33,18 +34,18 @@ func TestObjectsVersioning(t *testing.T) { opts := containers.CreateOpts{ VersionsEnabled: true, } - header, err := containers.Create(client, cName, opts).Extract() + header, err := containers.Create(context.TODO(), 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) + res := containers.Delete(context.TODO(), client, cName) th.AssertNoErr(t, res.Err) }() // ensure versioning is enabled - get, err := containers.Get(client, cName, nil).Extract() + get, err := containers.Get(context.TODO(), client, cName, nil).Extract() th.AssertNoErr(t, err) t.Logf("Get container headers: %+v\n", get) th.AssertEquals(t, true, get.VersionsEnabled) @@ -57,7 +58,7 @@ func TestObjectsVersioning(t *testing.T) { createOpts := objects.CreateOpts{ Content: strings.NewReader(oContents[i]), } - obj, err := objects.Create(client, cName, oNames[i], createOpts).Extract() + obj, err := objects.Create(context.TODO(), client, cName, oNames[i], createOpts).Extract() th.AssertNoErr(t, err) oContentVersionIDs[i] = obj.ObjectVersionID } @@ -67,7 +68,7 @@ func TestObjectsVersioning(t *testing.T) { createOpts := objects.CreateOpts{ Content: strings.NewReader(oNewContents[i]), } - _, err := objects.Create(client, cName, oNames[i], createOpts).Extract() + _, err := objects.Create(context.TODO(), client, cName, oNames[i], createOpts).Extract() th.AssertNoErr(t, err) } // Delete the objects after testing two times. @@ -76,13 +77,13 @@ func TestObjectsVersioning(t *testing.T) { opts := containers.UpdateOpts{ VersionsEnabled: new(bool), } - header, err := containers.Update(client, cName, opts).Extract() + header, err := containers.Update(context.TODO(), 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() + get, err := containers.Get(context.TODO(), client, cName, nil).Extract() th.AssertNoErr(t, err) t.Logf("Get container headers: %+v\n", get) th.AssertEquals(t, false, get.VersionsEnabled) @@ -93,7 +94,7 @@ func TestObjectsVersioning(t *testing.T) { opts := objects.DeleteOpts{ ObjectVersionID: oContentVersionIDs[i], } - obj, err := objects.Delete(client, cName, oNames[i], opts).Extract() + obj, err := objects.Delete(context.TODO(), client, cName, oNames[i], opts).Extract() th.AssertNoErr(t, err) currentVersionIDs[i] = obj.ObjectCurrentVersionID } @@ -101,7 +102,7 @@ func TestObjectsVersioning(t *testing.T) { opts := objects.DeleteOpts{ ObjectVersionID: currentVersionIDs[i], } - res := objects.Delete(client, cName, oNames[i], opts) + res := objects.Delete(context.TODO(), client, cName, oNames[i], opts) th.AssertNoErr(t, res.Err) } }() @@ -111,7 +112,7 @@ func TestObjectsVersioning(t *testing.T) { Prefix: "test-object-", } - allPages, err := objects.List(client, cName, listOpts).AllPages() + allPages, err := objects.List(client, cName, listOpts).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list objects: %v", err) } @@ -134,7 +135,7 @@ func TestObjectsVersioning(t *testing.T) { Versions: true, } - allPages, err = objects.List(client, cName, listOpts).AllPages() + allPages, err = objects.List(client, cName, listOpts).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list objects: %v", err) } @@ -164,7 +165,7 @@ func TestObjectsVersioning(t *testing.T) { } // Download one of the objects that was created above. - downloadres := objects.Download(client, cName, oNames[0], nil) + downloadres := objects.Download(context.TODO(), client, cName, oNames[0], nil) th.AssertNoErr(t, downloadres.Err) o1Content, err := downloadres.ExtractContent() @@ -177,7 +178,7 @@ func TestObjectsVersioning(t *testing.T) { downloadOpts := objects.DownloadOpts{ Newest: true, } - downloadres = objects.Download(client, cName, oNames[1], downloadOpts) + downloadres = objects.Download(context.TODO(), client, cName, oNames[1], downloadOpts) th.AssertNoErr(t, downloadres.Err) o2Content, err := downloadres.ExtractContent() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go index 98c6e59fad..4a11af0d24 100644 --- a/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -15,7 +16,7 @@ func TestBuildInfo(t *testing.T) { client, err := clients.NewOrchestrationV1Client() th.AssertNoErr(t, err) - bi, err := buildinfo.Get(client).Extract() + bi, err := buildinfo.Get(context.TODO(), client).Extract() th.AssertNoErr(t, err) t.Logf("retrieved build info: %+v\n", bi) } diff --git a/internal/acceptance/openstack/orchestration/v1/orchestration.go b/internal/acceptance/openstack/orchestration/v1/orchestration.go index fde59c6aa0..f7448732d7 100644 --- a/internal/acceptance/openstack/orchestration/v1/orchestration.go +++ b/internal/acceptance/openstack/orchestration/v1/orchestration.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "fmt" "testing" @@ -69,14 +70,14 @@ func CreateStack(t *testing.T, client *gophercloud.ServiceClient) (*stacks.Retri DisableRollback: gophercloud.Disabled, } - stack, err := stacks.Create(client, createOpts).Extract() + stack, err := stacks.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) if err := WaitForStackStatus(client, stackName, stack.ID, "CREATE_COMPLETE"); err != nil { return nil, err } - newStack, err := stacks.Get(client, stackName, stack.ID).Extract() + newStack, err := stacks.Get(context.TODO(), client, stackName, stack.ID).Extract() return newStack, err } @@ -86,7 +87,7 @@ func CreateStack(t *testing.T, client *gophercloud.ServiceClient) (*stacks.Retri func DeleteStack(t *testing.T, client *gophercloud.ServiceClient, stackName, stackID string) { t.Logf("Attempting to delete stack %s (%s)", stackName, stackID) - err := stacks.Delete(client, stackName, stackID).ExtractErr() + err := stacks.Delete(context.TODO(), client, stackName, stackID).ExtractErr() if err != nil { t.Fatalf("Failed to delete stack %s: %s", stackID, err) } @@ -97,7 +98,7 @@ func DeleteStack(t *testing.T, client *gophercloud.ServiceClient, stackName, sta // WaitForStackStatus will wait until a stack has reached a certain status. func WaitForStackStatus(client *gophercloud.ServiceClient, stackName, stackID, status string) error { return tools.WaitFor(func() (bool, error) { - latest, err := stacks.Get(client, stackName, stackID).Extract() + latest, err := stacks.Get(context.TODO(), client, stackName, stackID).Extract() if err != nil { return false, err } @@ -135,13 +136,13 @@ resources: DisableRollback: gophercloud.Disabled, } - stack, err := stacks.Create(client, createOpts).Extract() + stack, err := stacks.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) if err := WaitForStackStatus(client, stackName, stack.ID, "CREATE_COMPLETE"); err != nil { return nil, err } - newStack, err := stacks.Get(client, stackName, stack.ID).Extract() + newStack, err := stacks.Get(context.TODO(), client, stackName, stack.ID).Extract() return newStack, err } diff --git a/internal/acceptance/openstack/orchestration/v1/stackevents_test.go b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go index 0bd5d379cc..bfdd0079a8 100644 --- a/internal/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -19,7 +20,7 @@ func TestStackEvents(t *testing.T) { th.AssertNoErr(t, err) defer DeleteStack(t, client, stack.Name, stack.ID) - allPages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages() + allPages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allEvents, err := stackevents.ExtractEvents(allPages) th.AssertNoErr(t, err) @@ -28,7 +29,7 @@ func TestStackEvents(t *testing.T) { /* allPages is currently broke - allPages, err = stackevents.ListResourceEvents(client, stack.Name, stack.ID, basicTemplateResourceName, nil).AllPages() + allPages, err = stackevents.ListResourceEvents(client, stack.Name, stack.ID, basicTemplateResourceName, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allEvents, err = stackevents.ExtractEvents(allPages) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/orchestration/v1/stackresources_test.go b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go index 9e62a486b0..05930db255 100644 --- a/internal/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -20,11 +21,11 @@ func TestStackResources(t *testing.T) { th.AssertNoErr(t, err) defer DeleteStack(t, client, stack.Name, stack.ID) - resource, err := stackresources.Get(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() + resource, err := stackresources.Get(context.TODO(), client, stack.Name, stack.ID, basicTemplateResourceName).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, resource) - metadata, err := stackresources.Metadata(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() + metadata, err := stackresources.Metadata(context.TODO(), client, stack.Name, stack.ID, basicTemplateResourceName).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, metadata) @@ -33,15 +34,15 @@ func TestStackResources(t *testing.T) { ResourceStatusReason: "Wrong security policy is detected.", } - err = stackresources.MarkUnhealthy(client, stack.Name, stack.ID, basicTemplateResourceName, markUnhealthyOpts).ExtractErr() + err = stackresources.MarkUnhealthy(context.TODO(), client, stack.Name, stack.ID, basicTemplateResourceName, markUnhealthyOpts).ExtractErr() th.AssertNoErr(t, err) - unhealthyResource, err := stackresources.Get(client, stack.Name, stack.ID, basicTemplateResourceName).Extract() + unhealthyResource, err := stackresources.Get(context.TODO(), client, stack.Name, stack.ID, basicTemplateResourceName).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "CHECK_FAILED", unhealthyResource.Status) tools.PrintResource(t, unhealthyResource) - allPages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages() + allPages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allResources, err := stackresources.ExtractResources(allPages) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/orchestration/v1/stacks_test.go b/internal/acceptance/openstack/orchestration/v1/stacks_test.go index f32245008c..a7dbc90928 100644 --- a/internal/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacks_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -30,14 +31,14 @@ func TestStacksCRUD(t *testing.T) { Timeout: 20, } - err = stacks.Update(client, createdStack.Name, createdStack.ID, updateOpts).ExtractErr() + err = stacks.Update(context.TODO(), client, createdStack.Name, createdStack.ID, updateOpts).ExtractErr() th.AssertNoErr(t, err) err = WaitForStackStatus(client, createdStack.Name, createdStack.ID, "UPDATE_COMPLETE") th.AssertNoErr(t, err) var found bool - allPages, err := stacks.List(client, nil).AllPages() + allPages, err := stacks.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allStacks, err := stacks.ExtractStacks(allPages) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go index fa6aa931cb..696fc9e530 100644 --- a/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -20,7 +21,7 @@ func TestStackTemplatesCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteStack(t, client, stack.Name, stack.ID) - tmpl, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() + tmpl, err := stacktemplates.Get(context.TODO(), client, stack.Name, stack.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, tmpl) } @@ -33,7 +34,7 @@ func TestStackTemplatesValidate(t *testing.T) { Template: validateTemplate, } - validatedTemplate, err := stacktemplates.Validate(client, validateOpts).Extract() + validatedTemplate, err := stacktemplates.Validate(context.TODO(), client, validateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, validatedTemplate) } @@ -46,7 +47,7 @@ func TestStackTemplateWithFile(t *testing.T) { th.AssertNoErr(t, err) defer DeleteStack(t, client, stack.Name, stack.ID) - tmpl, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() + tmpl, err := stacktemplates.Get(context.TODO(), client, stack.Name, stack.ID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, tmpl) } diff --git a/internal/acceptance/openstack/placement/v1/placement.go b/internal/acceptance/openstack/placement/v1/placement.go index 344ab97320..4149d5ff8b 100644 --- a/internal/acceptance/openstack/placement/v1/placement.go +++ b/internal/acceptance/openstack/placement/v1/placement.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -18,7 +19,7 @@ func CreateResourceProvider(t *testing.T, client *gophercloud.ServiceClient) (*r } client.Microversion = "1.20" - resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + resourceProvider, err := resourceproviders.Create(context.TODO(), client, createOpts).Extract() if err != nil { return resourceProvider, err } @@ -41,7 +42,7 @@ func CreateResourceProviderWithParent(t *testing.T, client *gophercloud.ServiceC } client.Microversion = "1.20" - resourceProvider, err := resourceproviders.Create(client, createOpts).Extract() + resourceProvider, err := resourceproviders.Create(context.TODO(), client, createOpts).Extract() if err != nil { return resourceProvider, err } @@ -61,7 +62,7 @@ func CreateResourceProviderWithParent(t *testing.T, client *gophercloud.ServiceC func DeleteResourceProvider(t *testing.T, client *gophercloud.ServiceClient, resourceProviderID string) { t.Logf("Attempting to delete resourceProvider: %s", resourceProviderID) - err := resourceproviders.Delete(client, resourceProviderID).ExtractErr() + err := resourceproviders.Delete(context.TODO(), client, resourceProviderID).ExtractErr() if err != nil { t.Fatalf("Unable to delete resourceProvider %s: %v", resourceProviderID, err) } diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index 11413b7921..b0b1bd5f3d 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -15,7 +16,7 @@ func TestResourceProviderList(t *testing.T) { client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) - allPages, err := resourceproviders.List(client, resourceproviders.ListOpts{}).AllPages() + allPages, err := resourceproviders.List(client, resourceproviders.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) allResourceProviders, err := resourceproviders.ExtractResourceProviders(allPages) @@ -49,11 +50,11 @@ func TestResourceProvider(t *testing.T) { updateOpts := resourceproviders.UpdateOpts{ Name: &newName, } - resourceProviderUpdate, err := resourceproviders.Update(client, resourceProvider2.UUID, updateOpts).Extract() + resourceProviderUpdate, err := resourceproviders.Update(context.TODO(), client, resourceProvider2.UUID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newName, resourceProviderUpdate.Name) - resourceProviderGet, err := resourceproviders.Get(client, resourceProvider2.UUID).Extract() + resourceProviderGet, err := resourceproviders.Get(context.TODO(), client, resourceProvider2.UUID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newName, resourceProviderGet.Name) @@ -73,7 +74,7 @@ func TestResourceProviderUsages(t *testing.T) { defer DeleteResourceProvider(t, client, resourceProvider.UUID) // now get the usages for the newly created resource provider - usage, err := resourceproviders.GetUsages(client, resourceProvider.UUID).Extract() + usage, err := resourceproviders.GetUsages(context.TODO(), client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, usage) @@ -91,7 +92,7 @@ func TestResourceProviderInventories(t *testing.T) { defer DeleteResourceProvider(t, client, resourceProvider.UUID) // now get the inventories for the newly created resource provider - usage, err := resourceproviders.GetInventories(client, resourceProvider.UUID).Extract() + usage, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, usage) @@ -109,7 +110,7 @@ func TestResourceProviderTraits(t *testing.T) { defer DeleteResourceProvider(t, client, resourceProvider.UUID) // now get the traits for the newly created resource provider - usage, err := resourceproviders.GetTraits(client, resourceProvider.UUID).Extract() + usage, err := resourceproviders.GetTraits(context.TODO(), client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, usage) @@ -127,7 +128,7 @@ func TestResourceProviderAllocations(t *testing.T) { defer DeleteResourceProvider(t, client, resourceProvider.UUID) // now get the allocations for the newly created resource provider - usage, err := resourceproviders.GetAllocations(client, resourceProvider.UUID).Extract() + usage, err := resourceproviders.GetAllocations(context.TODO(), client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, usage) diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go index aec11d0af3..75441f6e14 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -16,7 +17,7 @@ func TestAvailabilityZonesList(t *testing.T) { t.Fatalf("Unable to create shared file system client: %v", err) } - allPages, err := availabilityzones.List(client).AllPages() + allPages, err := availabilityzones.List(client).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list availability zones: %v", err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go index 65d7ca934a..1c50770e75 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go @@ -1,6 +1,7 @@ package messages import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -10,7 +11,7 @@ import ( // DeleteMessage will delete a message. An error will occur if // the message was unable to be deleted. func DeleteMessage(t *testing.T, client *gophercloud.ServiceClient, message *messages.Message) { - err := messages.Delete(client, message.ID).ExtractErr() + err := messages.Delete(context.TODO(), client, message.ID).ExtractErr() if err != nil { t.Fatalf("Failed to delete message %s: %v", message.ID, err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go index d15209a007..4b1d53e99d 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go @@ -1,6 +1,7 @@ package messages import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -18,7 +19,7 @@ func TestMessageList(t *testing.T) { } client.Microversion = minimumManilaMessagesMicroVersion - allPages, err := messages.List(client, messages.ListOpts{}).AllPages() + allPages, err := messages.List(client, messages.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve messages: %v", err) } @@ -46,7 +47,7 @@ func TestMessageListFiltering(t *testing.T) { RequestID: requestID, } - allPages, err := messages.List(client, options).AllPages() + allPages, err := messages.List(client, options).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve messages: %v", err) } @@ -77,7 +78,7 @@ func TestMessageDelete(t *testing.T) { RequestID: requestID, } - allPages, err := messages.List(client, options).AllPages() + allPages, err := messages.List(client, options).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve messages: %v", err) } @@ -100,7 +101,7 @@ func TestMessageDelete(t *testing.T) { messageID = listedMessage.ID } - message, err := messages.Get(client, messageID).Extract() + message, err := messages.Get(context.TODO(), client, messageID).Extract() if err != nil { t.Fatalf("Unable to retrieve the message: %v", err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go index 5969ac3dd2..98de841523 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "strings" "testing" @@ -20,7 +21,7 @@ func CreateReplica(t *testing.T, client *gophercloud.ServiceClient, share *share AvailabilityZone: share.AvailabilityZone, } - replica, err := replicas.Create(client, createOpts).Extract() + replica, err := replicas.Create(context.TODO(), client, createOpts).Extract() if err != nil { t.Logf("Failed to create replica") return nil, err @@ -39,7 +40,7 @@ func CreateReplica(t *testing.T, client *gophercloud.ServiceClient, share *share // 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() + err := replicas.Delete(context.TODO(), client, replica.ID).ExtractErr() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return @@ -61,7 +62,7 @@ func ListShareReplicas(t *testing.T, client *gophercloud.ServiceClient, shareID opts := replicas.ListOpts{ ShareID: shareID, } - pages, err := replicas.List(client, opts).AllPages() + pages, err := replicas.List(client, opts).AllPages(context.TODO()) if err != nil { t.Errorf("Unable to list %q share replicas: %v", shareID, err) } @@ -75,7 +76,7 @@ func waitForReplicaStatus(t *testing.T, c *gophercloud.ServiceClient, id, status err := tools.WaitFor(func() (bool, error) { var err error - current, err = replicas.Get(c, id).Extract() + current, err = replicas.Get(context.TODO(), c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { @@ -115,7 +116,7 @@ func waitForReplicaState(t *testing.T, c *gophercloud.ServiceClient, id, state s err := tools.WaitFor(func() (bool, error) { var err error - current, err = replicas.Get(c, id).Extract() + current, err = replicas.Get(context.TODO(), c, id).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go index dca0c273ef..847512581f 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "time" @@ -40,7 +41,7 @@ func TestReplicaCreate(t *testing.T) { defer DeleteReplica(t, client, replica) - created, err := replicas.Get(client, replica.ID).Extract() + created, err := replicas.Get(context.TODO(), client, replica.ID).Extract() if err != nil { t.Errorf("Unable to retrieve replica: %v", err) } @@ -77,14 +78,14 @@ func TestReplicaPromote(t *testing.T) { defer DeleteReplica(t, client, replica) - created, err := replicas.Get(client, replica.ID).Extract() + created, err := replicas.Get(context.TODO(), 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() + err = replicas.Resync(context.TODO(), client, created.ID).ExtractErr() th.AssertNoErr(t, err) _, err = waitForReplicaState(t, client, created.ID, "in_sync") if err != nil { @@ -92,7 +93,7 @@ func TestReplicaPromote(t *testing.T) { } // promote new replica - err = replicas.Promote(client, created.ID, &replicas.PromoteOpts{}).ExtractErr() + err = replicas.Promote(context.TODO(), client, created.ID, &replicas.PromoteOpts{}).ExtractErr() th.AssertNoErr(t, err) _, err = waitForReplicaState(t, client, created.ID, "active") @@ -115,13 +116,13 @@ func TestReplicaPromote(t *testing.T) { t.Errorf("Unable to get old replica") } // sync old replica - err = replicas.Resync(client, oldReplicaID).ExtractErr() + err = replicas.Resync(context.TODO(), 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() + err = replicas.Promote(context.TODO(), client, oldReplicaID, &replicas.PromoteOpts{}).ExtractErr() th.AssertNoErr(t, err) _, err = waitForReplicaState(t, client, oldReplicaID, "active") @@ -154,7 +155,7 @@ func TestReplicaExportLocations(t *testing.T) { 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() + exportLocations, err := replicas.ListExportLocations(context.TODO(), client, replica.ID).Extract() if err != nil { t.Errorf("Unable to list replica export locations: %v", err) } @@ -163,7 +164,7 @@ func TestReplicaExportLocations(t *testing.T) { opts := replicas.ListOpts{ ShareID: share.ID, } - pages, err := replicas.List(client, opts).AllPages() + pages, err := replicas.List(client, opts).AllPages(context.TODO()) th.AssertNoErr(t, err) allReplicas, err := replicas.ExtractReplicas(pages) @@ -180,13 +181,13 @@ func TestReplicaExportLocations(t *testing.T) { t.Errorf("Unable to get active replica") } - exportLocations, err = replicas.ListExportLocations(client, activeReplicaID).Extract() + exportLocations, err = replicas.ListExportLocations(context.TODO(), 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() + exportLocation, err := replicas.GetExportLocation(context.TODO(), client, activeReplicaID, exportLocations[0].ID).Extract() if err != nil { t.Errorf("Unable to get replica export location: %v", err) } @@ -256,7 +257,7 @@ func TestReplicaResetStatus(t *testing.T) { resetStatusOpts := &replicas.ResetStatusOpts{ Status: "error", } - err = replicas.ResetStatus(client, replica.ID, resetStatusOpts).ExtractErr() + err = replicas.ResetStatus(context.TODO(), client, replica.ID, resetStatusOpts).ExtractErr() if err != nil { t.Fatalf("Unable to reset a replica status: %v", err) } @@ -295,7 +296,7 @@ func TestReplicaForceDelete(t *testing.T) { defer DeleteReplica(t, client, replica) - err = replicas.ForceDelete(client, replica.ID).ExtractErr() + err = replicas.ForceDelete(context.TODO(), client, replica.ID).ExtractErr() if err != nil { t.Fatalf("Unable to force delete a replica: %v", err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go index 498bd450fa..672578a642 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestSchedulerStatsList(t *testing.T) { th.AssertNoErr(t, err) client.Microversion = "2.23" - allPages, err := schedulerstats.List(client, nil).AllPages() + allPages, err := schedulerstats.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allPools, err := schedulerstats.ExtractPools(allPages) diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go index b5cff53af6..413095a6e2 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -25,7 +26,7 @@ func CreateSecurityService(t *testing.T, client *gophercloud.ServiceClient) (*se Type: "kerberos", } - securityService, err := securityservices.Create(client, createOpts).Extract() + securityService, err := securityservices.Create(context.TODO(), client, createOpts).Extract() if err != nil { return securityService, err } @@ -36,7 +37,7 @@ func CreateSecurityService(t *testing.T, client *gophercloud.ServiceClient) (*se // DeleteSecurityService will delete a security service. An error will occur if // the security service was unable to be deleted. func DeleteSecurityService(t *testing.T, client *gophercloud.ServiceClient, securityService *securityservices.SecurityService) { - err := securityservices.Delete(client, securityService.ID).ExtractErr() + err := securityservices.Delete(context.TODO(), client, securityService.ID).ExtractErr() if err != nil { t.Fatalf("Failed to delete security service %s: %v", securityService.ID, err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index 64aa957145..32860ea6f2 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -22,7 +23,7 @@ func TestSecurityServiceCreateDelete(t *testing.T) { t.Fatalf("Unable to create security service: %v", err) } - newSecurityService, err := securityservices.Get(client, securityService.ID).Extract() + newSecurityService, err := securityservices.Get(context.TODO(), client, securityService.ID).Extract() if err != nil { t.Errorf("Unable to retrieve the security service: %v", err) } @@ -46,7 +47,7 @@ func TestSecurityServiceList(t *testing.T) { t.Fatalf("Unable to create a shared file system client: %v", err) } - allPages, err := securityservices.List(client, securityservices.ListOpts{}).AllPages() + allPages, err := securityservices.List(client, securityservices.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve security services: %v", err) } @@ -85,7 +86,7 @@ func TestSecurityServiceListFiltering(t *testing.T) { Name: securityService.Name, } - allPages, err := securityservices.List(client, options).AllPages() + allPages, err := securityservices.List(client, options).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve security services: %v", err) } @@ -124,12 +125,12 @@ func TestSecurityServiceUpdate(t *testing.T) { Type: "ldap", } - _, err = securityservices.Update(client, securityService.ID, options).Extract() + _, err = securityservices.Update(context.TODO(), client, securityService.ID, options).Extract() if err != nil { t.Errorf("Unable to update the security service: %v", err) } - newSecurityService, err := securityservices.Get(client, securityService.ID).Extract() + newSecurityService, err := securityservices.Get(context.TODO(), client, securityService.ID).Extract() if err != nil { t.Errorf("Unable to retrieve the security service: %v", err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go index 0ccfe426b7..65d0acdbf7 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -17,7 +18,7 @@ func TestServicesList(t *testing.T) { th.AssertNoErr(t, err) client.Microversion = "2.7" - allPages, err := services.List(client, nil).AllPages() + allPages, err := services.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allServices, err := services.ExtractServices(allPages) diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go index 8be4821cf9..3fdc55cc7a 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "testing" @@ -11,7 +12,7 @@ import ( ) func ShareAccessRuleGet(t *testing.T, client *gophercloud.ServiceClient, accessID string) (*shareaccessrules.ShareAccess, error) { - accessRule, err := shareaccessrules.Get(client, accessID).Extract() + accessRule, err := shareaccessrules.Get(context.TODO(), client, accessID).Extract() if err != nil { t.Logf("Failed to get share access rule %s: %v", accessID, err) return nil, err @@ -63,7 +64,7 @@ func WaitForShareAccessRule(t *testing.T, client *gophercloud.ServiceClient, acc } func ShareAccessRuleList(t *testing.T, client *gophercloud.ServiceClient, shareID string) ([]shareaccessrules.ShareAccess, error) { - accessRules, err := shareaccessrules.List(client, shareID).Extract() + accessRules, err := shareaccessrules.List(context.TODO(), client, shareID).Extract() if err != nil { t.Logf("Failed to list share access rules for share %s: %v", shareID, err) return nil, err diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go index 4250ef5d91..86d074058c 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -31,7 +32,7 @@ func CreateShareNetwork(t *testing.T, client *gophercloud.ServiceClient) (*share Description: "This is a shared network", } - shareNetwork, err := sharenetworks.Create(client, createOpts).Extract() + shareNetwork, err := sharenetworks.Create(context.TODO(), client, createOpts).Extract() if err != nil { return shareNetwork, err } @@ -42,7 +43,7 @@ func CreateShareNetwork(t *testing.T, client *gophercloud.ServiceClient) (*share // DeleteShareNetwork will delete a share network. An error will occur if // the share network was unable to be deleted. func DeleteShareNetwork(t *testing.T, client *gophercloud.ServiceClient, shareNetworkID string) { - err := sharenetworks.Delete(client, shareNetworkID).ExtractErr() + err := sharenetworks.Delete(context.TODO(), client, shareNetworkID).ExtractErr() if err != nil { t.Fatalf("Failed to delete share network %s: %v", shareNetworkID, err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index db683be42b..f212702b5e 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -24,7 +25,7 @@ func TestShareNetworkCreateDestroy(t *testing.T) { t.Fatalf("Unable to create share network: %v", err) } - newShareNetwork, err := sharenetworks.Get(client, shareNetwork.ID).Extract() + newShareNetwork, err := sharenetworks.Get(context.TODO(), client, shareNetwork.ID).Extract() if err != nil { t.Errorf("Unable to retrieve shareNetwork: %v", err) } @@ -55,7 +56,7 @@ func TestShareNetworkUpdate(t *testing.T) { t.Fatalf("Unable to create share network: %v", err) } - expectedShareNetwork, err := sharenetworks.Get(client, shareNetwork.ID).Extract() + expectedShareNetwork, err := sharenetworks.Get(context.TODO(), client, shareNetwork.ID).Extract() if err != nil { t.Errorf("Unable to retrieve shareNetwork: %v", err) } @@ -70,12 +71,12 @@ func TestShareNetworkUpdate(t *testing.T) { expectedShareNetwork.Name = name expectedShareNetwork.Description = description - _, err = sharenetworks.Update(client, shareNetwork.ID, options).Extract() + _, err = sharenetworks.Update(context.TODO(), client, shareNetwork.ID, options).Extract() if err != nil { t.Errorf("Unable to update shareNetwork: %v", err) } - updatedShareNetwork, err := sharenetworks.Get(client, shareNetwork.ID).Extract() + updatedShareNetwork, err := sharenetworks.Get(context.TODO(), client, shareNetwork.ID).Extract() if err != nil { t.Errorf("Unable to retrieve shareNetwork: %v", err) } @@ -96,7 +97,7 @@ func TestShareNetworkListDetail(t *testing.T) { t.Fatalf("Unable to create a shared file system client: %v", err) } - allPages, err := sharenetworks.ListDetail(client, sharenetworks.ListOpts{}).AllPages() + allPages, err := sharenetworks.ListDetail(client, sharenetworks.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve share networks: %v", err) } @@ -135,7 +136,7 @@ func TestShareNetworkListFiltering(t *testing.T) { Name: shareNetwork.Name, } - allPages, err := sharenetworks.ListDetail(client, options).AllPages() + allPages, err := sharenetworks.ListDetail(client, options).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve share networks: %v", err) } @@ -173,7 +174,7 @@ func TestShareNetworkListPagination(t *testing.T) { count := 0 - err = sharenetworks.ListDetail(client, sharenetworks.ListOpts{Offset: 0, Limit: 1}).EachPage(func(page pagination.Page) (bool, error) { + err = sharenetworks.ListDetail(client, sharenetworks.ListOpts{Offset: 0, Limit: 1}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ _, err := sharenetworks.ExtractShareNetworks(page) if err != nil { @@ -215,7 +216,7 @@ func TestShareNetworkAddRemoveSecurityService(t *testing.T) { SecurityServiceID: securityService.ID, } - _, err = sharenetworks.AddSecurityService(client, shareNetwork.ID, options).Extract() + _, err = sharenetworks.AddSecurityService(context.TODO(), client, shareNetwork.ID, options).Extract() if err != nil { t.Errorf("Unable to add security service: %v", err) } @@ -224,7 +225,7 @@ func TestShareNetworkAddRemoveSecurityService(t *testing.T) { SecurityServiceID: securityService.ID, } - _, err = sharenetworks.RemoveSecurityService(client, shareNetwork.ID, removeOptions).Extract() + _, err = sharenetworks.RemoveSecurityService(context.TODO(), client, shareNetwork.ID, removeOptions).Extract() if err != nil { t.Errorf("Unable to remove security service: %v", err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go index fcb4b5d09d..9f80e1c36b 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "strings" "testing" @@ -33,7 +34,7 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient, optShareType . IsPublic: &iTrue, } - share, err := shares.Create(client, createOpts).Extract() + share, err := shares.Create(context.TODO(), client, createOpts).Extract() if err != nil { t.Logf("Failed to create share") return nil, err @@ -52,7 +53,7 @@ func CreateShare(t *testing.T, client *gophercloud.ServiceClient, optShareType . // ListShares lists all shares that belong to this tenant's project. // An error will be returned if the shares could not be listed.. func ListShares(t *testing.T, client *gophercloud.ServiceClient) ([]shares.Share, error) { - r, err := shares.ListDetail(client, &shares.ListOpts{}).AllPages() + r, err := shares.ListDetail(client, &shares.ListOpts{}).AllPages(context.TODO()) if err != nil { return nil, err } @@ -63,7 +64,7 @@ func ListShares(t *testing.T, client *gophercloud.ServiceClient) ([]shares.Share // GrantAccess will grant access to an existing share. A fatal error will occur if // this operation fails. func GrantAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) (*shares.AccessRight, error) { - return shares.GrantAccess(client, share.ID, shares.GrantAccessOpts{ + return shares.GrantAccess(context.TODO(), client, share.ID, shares.GrantAccessOpts{ AccessType: "ip", AccessTo: "0.0.0.0/32", AccessLevel: "ro", @@ -73,7 +74,7 @@ func GrantAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares. // RevokeAccess will revoke an exisiting access of a share. A fatal error will occur // if this operation fails. func RevokeAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, accessRight *shares.AccessRight) error { - return shares.RevokeAccess(client, share.ID, shares.RevokeAccessOpts{ + return shares.RevokeAccess(context.TODO(), client, share.ID, shares.RevokeAccessOpts{ AccessID: accessRight.ID, }).ExtractErr() } @@ -81,13 +82,13 @@ func RevokeAccess(t *testing.T, client *gophercloud.ServiceClient, share *shares // GetAccessRightsSlice will retrieve all access rules assigned to a share. // A fatal error will occur if this operation fails. func GetAccessRightsSlice(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) ([]shares.AccessRight, error) { - return shares.ListAccessRights(client, share.ID).Extract() + return shares.ListAccessRights(context.TODO(), client, share.ID).Extract() } // DeleteShare will delete a share. A fatal error will occur if the share // failed to be deleted. This works best when used as a deferred function. func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) { - err := shares.Delete(client, share.ID).ExtractErr() + err := shares.Delete(context.TODO(), client, share.ID).ExtractErr() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return @@ -105,18 +106,18 @@ func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares. // ExtendShare extends the capacity of an existing share func ExtendShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, newSize int) error { - return shares.Extend(client, share.ID, &shares.ExtendOpts{NewSize: newSize}).ExtractErr() + return shares.Extend(context.TODO(), client, share.ID, &shares.ExtendOpts{NewSize: newSize}).ExtractErr() } // ShrinkShare shrinks the capacity of an existing share func ShrinkShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share, newSize int) error { - return shares.Shrink(client, share.ID, &shares.ShrinkOpts{NewSize: newSize}).ExtractErr() + return shares.Shrink(context.TODO(), client, share.ID, &shares.ShrinkOpts{NewSize: newSize}).ExtractErr() } func PrintMessages(t *testing.T, c *gophercloud.ServiceClient, id string) error { c.Microversion = "2.37" - allPages, err := messages.List(c, messages.ListOpts{ResourceID: id}).AllPages() + allPages, err := messages.List(c, messages.ListOpts{ResourceID: id}).AllPages(context.TODO()) if err != nil { return fmt.Errorf("Unable to retrieve messages: %v", err) } @@ -139,7 +140,7 @@ func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string err := tools.WaitFor(func() (bool, error) { var err error - current, err = shares.Get(c, id).Extract() + current, err = shares.Get(context.TODO(), c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 173ee30810..a7f05516b9 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -25,7 +26,7 @@ func TestShareCreate(t *testing.T) { defer DeleteShare(t, client, share) - created, err := shares.Get(client, share.ID).Extract() + created, err := shares.Get(context.TODO(), client, share.ID).Extract() if err != nil { t.Errorf("Unable to retrieve share: %v", err) } @@ -52,13 +53,13 @@ func TestShareExportLocations(t *testing.T) { client.Microversion = "2.9" - exportLocations, err := shares.ListExportLocations(client, share.ID).Extract() + exportLocations, err := shares.ListExportLocations(context.TODO(), client, share.ID).Extract() if err != nil { t.Errorf("Unable to list share export locations: %v", err) } tools.PrintResource(t, exportLocations) - exportLocation, err := shares.GetExportLocation(client, share.ID, exportLocations[0].ID).Extract() + exportLocation, err := shares.GetExportLocation(context.TODO(), client, share.ID, exportLocations[0].ID).Extract() if err != nil { t.Errorf("Unable to get share export location: %v", err) } @@ -79,7 +80,7 @@ func TestShareUpdate(t *testing.T) { defer DeleteShare(t, client, share) - expectedShare, err := shares.Get(client, share.ID).Extract() + expectedShare, err := shares.Get(context.TODO(), client, share.ID).Extract() if err != nil { t.Errorf("Unable to retrieve share: %v", err) } @@ -97,12 +98,12 @@ func TestShareUpdate(t *testing.T) { expectedShare.Description = description expectedShare.IsPublic = iFalse - _, err = shares.Update(client, share.ID, options).Extract() + _, err = shares.Update(context.TODO(), client, share.ID, options).Extract() if err != nil { t.Errorf("Unable to update share: %v", err) } - updatedShare, err := shares.Get(client, share.ID).Extract() + updatedShare, err := shares.Get(context.TODO(), client, share.ID).Extract() if err != nil { t.Errorf("Unable to retrieve share: %v", err) } @@ -268,30 +269,30 @@ func TestShareMetadata(t *testing.T) { } } - metadata, err := shares.SetMetadata(client, share.ID, shares.SetMetadataOpts{Metadata: map[string]string{k: v1}}).Extract() + metadata, err := shares.SetMetadata(context.TODO(), client, share.ID, shares.SetMetadataOpts{Metadata: map[string]string{k: v1}}).Extract() if err != nil { t.Fatalf("Unable to set share metadata: %v", err) } checkMetadataEq(metadata, v1) - metadata, err = shares.UpdateMetadata(client, share.ID, shares.UpdateMetadataOpts{Metadata: map[string]string{k: v2}}).Extract() + metadata, err = shares.UpdateMetadata(context.TODO(), client, share.ID, shares.UpdateMetadataOpts{Metadata: map[string]string{k: v2}}).Extract() if err != nil { t.Fatalf("Unable to update share metadata: %v", err) } checkMetadataEq(metadata, v2) - metadata, err = shares.GetMetadatum(client, share.ID, k).Extract() + metadata, err = shares.GetMetadatum(context.TODO(), client, share.ID, k).Extract() if err != nil { t.Fatalf("Unable to get share metadatum: %v", err) } checkMetadataEq(metadata, v2) - err = shares.DeleteMetadatum(client, share.ID, k).ExtractErr() + err = shares.DeleteMetadatum(context.TODO(), client, share.ID, k).ExtractErr() if err != nil { t.Fatalf("Unable to delete share metadatum: %v", err) } - metadata, err = shares.GetMetadata(client, share.ID).Extract() + metadata, err = shares.GetMetadata(context.TODO(), client, share.ID).Extract() if err != nil { t.Fatalf("Unable to get share metadata: %v", err) } @@ -334,7 +335,7 @@ func TestRevert(t *testing.T) { revertOpts := &shares.RevertOpts{ SnapshotID: snapshot.ID, } - err = shares.Revert(client, share.ID, revertOpts).ExtractErr() + err = shares.Revert(context.TODO(), client, share.ID, revertOpts).ExtractErr() if err != nil { t.Fatalf("Unable to revert a snapshot: %v", err) } @@ -396,7 +397,7 @@ func TestShareRestoreFromSnapshot(t *testing.T) { SnapshotID: snapshot.ID, IsPublic: &iTrue, } - restored, err := shares.Create(client, createOpts).Extract() + restored, err := shares.Create(context.TODO(), client, createOpts).Extract() if err != nil { t.Fatalf("Unable to create a share from a snapshot: %v", err) } @@ -442,7 +443,7 @@ func TestResetStatus(t *testing.T) { resetStatusOpts := &shares.ResetStatusOpts{ Status: "error", } - err = shares.ResetStatus(client, share.ID, resetStatusOpts).ExtractErr() + err = shares.ResetStatus(context.TODO(), client, share.ID, resetStatusOpts).ExtractErr() if err != nil { t.Fatalf("Unable to reset a share status: %v", err) } @@ -475,7 +476,7 @@ func TestForceDelete(t *testing.T) { t.Fatalf("Share status error: %v", err) } - err = shares.ForceDelete(client, share.ID).ExtractErr() + err = shares.ForceDelete(context.TODO(), client, share.ID).ExtractErr() if err != nil { t.Fatalf("Unable to force delete a share: %v", err) } @@ -509,7 +510,7 @@ func TestUnmanage(t *testing.T) { t.Fatalf("Share status error: %v", err) } - err = shares.Unmanage(client, share.ID).ExtractErr() + err = shares.Unmanage(context.TODO(), client, share.ID).ExtractErr() if err != nil { t.Fatalf("Unable to unmanage a share: %v", err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go index dcff28e2f9..6b95497452 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "testing" @@ -14,7 +15,7 @@ func CreateTransferRequest(t *testing.T, client *gophercloud.ServiceClient, shar ShareID: share.ID, Name: name, } - transfer, err := sharetransfers.Create(client, opts).Extract() + transfer, err := sharetransfers.Create(context.TODO(), client, opts).Extract() if err != nil { return nil, fmt.Errorf("failed to create a share transfer request: %s", err) } @@ -27,7 +28,7 @@ func AcceptTransfer(t *testing.T, client *gophercloud.ServiceClient, transferReq AuthKey: transferRequest.AuthKey, ClearAccessRules: true, } - err := sharetransfers.Accept(client, transferRequest.ID, opts).ExtractErr() + err := sharetransfers.Accept(context.TODO(), client, transferRequest.ID, opts).ExtractErr() if err != nil { return fmt.Errorf("failed to accept a share transfer request: %s", err) } @@ -36,7 +37,7 @@ func AcceptTransfer(t *testing.T, client *gophercloud.ServiceClient, transferReq } func DeleteTransferRequest(t *testing.T, client *gophercloud.ServiceClient, transfer *sharetransfers.Transfer) { - err := sharetransfers.Delete(client, transfer.ID).ExtractErr() + err := sharetransfers.Delete(context.TODO(), client, transfer.ID).ExtractErr() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go index 6853f23f50..2d77d32a8c 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -39,7 +40,7 @@ func TestTransferRequestCRUD(t *testing.T) { defer DeleteTransferRequest(t, client, transferRequest) // list transfer requests - allTransferRequestsPages, err := sharetransfers.ListDetail(client, nil).AllPages() + allTransferRequestsPages, err := sharetransfers.ListDetail(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allTransferRequests, err := sharetransfers.ExtractTransfers(allTransferRequestsPages) @@ -56,7 +57,7 @@ func TestTransferRequestCRUD(t *testing.T) { th.AssertEquals(t, foundRequest, true) // checking get - tr, err := sharetransfers.Get(client, transferRequest.ID).Extract() + tr, err := sharetransfers.Get(context.TODO(), client, transferRequest.ID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, transferRequest.ID == tr.ID, true) diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go index 54c48934dd..69e0a4ea3c 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -28,7 +29,7 @@ func CreateShareType(t *testing.T, client *gophercloud.ServiceClient) (*sharetyp ExtraSpecs: extraSpecsOps, } - shareType, err := sharetypes.Create(client, createOpts).Extract() + shareType, err := sharetypes.Create(context.TODO(), client, createOpts).Extract() if err != nil { return shareType, err } @@ -39,7 +40,7 @@ func CreateShareType(t *testing.T, client *gophercloud.ServiceClient) (*sharetyp // DeleteShareType will delete a share type. An error will occur if // the share type was unable to be deleted. func DeleteShareType(t *testing.T, client *gophercloud.ServiceClient, shareType *sharetypes.ShareType) { - err := sharetypes.Delete(client, shareType.ID).ExtractErr() + err := sharetypes.Delete(context.TODO(), client, shareType.ID).ExtractErr() if err != nil { t.Fatalf("Failed to delete share type %s: %v", shareType.ID, err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index 466d830c8e..d51da2ef3e 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -33,7 +34,7 @@ func TestShareTypeList(t *testing.T) { t.Fatalf("Unable to create a shared file system client: %v", err) } - allPages, err := sharetypes.List(client, sharetypes.ListOpts{}).AllPages() + allPages, err := sharetypes.List(client, sharetypes.ListOpts{}).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to retrieve share types: %v", err) } @@ -54,7 +55,7 @@ func TestShareTypeGetDefault(t *testing.T) { t.Fatalf("Unable to create a shared file system client: %v", err) } - shareType, err := sharetypes.GetDefault(client).Extract() + shareType, err := sharetypes.GetDefault(context.TODO(), client).Extract() if err != nil { t.Fatalf("Unable to retrieve the default share type: %v", err) } @@ -77,12 +78,12 @@ func TestShareTypeExtraSpecs(t *testing.T) { ExtraSpecs: map[string]interface{}{"my_new_key": "my_value"}, } - _, err = sharetypes.SetExtraSpecs(client, shareType.ID, options).Extract() + _, err = sharetypes.SetExtraSpecs(context.TODO(), client, shareType.ID, options).Extract() if err != nil { t.Fatalf("Unable to set extra specs for Share type: %s", shareType.Name) } - extraSpecs, err := sharetypes.GetExtraSpecs(client, shareType.ID).Extract() + extraSpecs, err := sharetypes.GetExtraSpecs(context.TODO(), client, shareType.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve share type: %s", shareType.Name) } @@ -95,12 +96,12 @@ func TestShareTypeExtraSpecs(t *testing.T) { t.Fatal("my_new_key was expected to be equal to my_value") } - err = sharetypes.UnsetExtraSpecs(client, shareType.ID, "my_new_key").ExtractErr() + err = sharetypes.UnsetExtraSpecs(context.TODO(), client, shareType.ID, "my_new_key").ExtractErr() if err != nil { t.Fatalf("Unable to unset extra specs for Share type: %s", shareType.Name) } - extraSpecs, err = sharetypes.GetExtraSpecs(client, shareType.ID).Extract() + extraSpecs, err = sharetypes.GetExtraSpecs(context.TODO(), client, shareType.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve share type: %s", shareType.Name) } @@ -129,12 +130,12 @@ func TestShareTypeAccess(t *testing.T) { Project: "9e3a5a44e0134445867776ef53a37605", } - err = sharetypes.AddAccess(client, shareType.ID, options).ExtractErr() + err = sharetypes.AddAccess(context.TODO(), client, shareType.ID, options).ExtractErr() if err != nil { t.Fatalf("Unable to add a new access to a share type: %v", err) } - access, err := sharetypes.ShowAccess(client, shareType.ID).Extract() + access, err := sharetypes.ShowAccess(context.TODO(), client, shareType.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve the access details for a share type: %v", err) } @@ -145,12 +146,12 @@ func TestShareTypeAccess(t *testing.T) { t.Fatal("Share type access is not the same than expected") } - err = sharetypes.RemoveAccess(client, shareType.ID, options).ExtractErr() + err = sharetypes.RemoveAccess(context.TODO(), client, shareType.ID, options).ExtractErr() if err != nil { t.Fatalf("Unable to remove an access from a share type: %v", err) } - access, err = sharetypes.ShowAccess(client, shareType.ID).Extract() + access, err = sharetypes.ShowAccess(context.TODO(), client, shareType.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve the access details for a share type: %v", err) } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go index 51a2daec55..67eebd7231 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "strings" "testing" @@ -23,7 +24,7 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, shareID str Description: "My Test Description", } - snapshot, err := snapshots.Create(client, createOpts).Extract() + snapshot, err := snapshots.Create(context.TODO(), client, createOpts).Extract() if err != nil { t.Logf("Failed to create snapshot") return nil, err @@ -41,7 +42,7 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, shareID str // ListSnapshots lists all snapshots that belong to this tenant's project. // An error will be returned if the snapshots could not be listed.. func ListSnapshots(t *testing.T, client *gophercloud.ServiceClient) ([]snapshots.Snapshot, error) { - r, err := snapshots.ListDetail(client, &snapshots.ListOpts{}).AllPages() + r, err := snapshots.ListDetail(client, &snapshots.ListOpts{}).AllPages(context.TODO()) if err != nil { return nil, err } @@ -52,7 +53,7 @@ func ListSnapshots(t *testing.T, client *gophercloud.ServiceClient) ([]snapshots // DeleteSnapshot will delete a snapshot. A fatal error will occur if the snapshot // failed to be deleted. This works best when used as a deferred function. func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { - err := snapshots.Delete(client, snapshot.ID).ExtractErr() + err := snapshots.Delete(context.TODO(), client, snapshot.ID).ExtractErr() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return @@ -70,7 +71,7 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s func waitForSnapshotStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) error { err := tools.WaitFor(func() (bool, error) { - current, err := snapshots.Get(c, id).Extract() + current, err := snapshots.Get(context.TODO(), c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index a84d652178..e457d3c704 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -4,6 +4,7 @@ package v2 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -36,7 +37,7 @@ func TestSnapshotCreate(t *testing.T) { defer DeleteSnapshot(t, client, snapshot) - created, err := snapshots.Get(client, snapshot.ID).Extract() + created, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { t.Fatalf("Unable to retrieve a snapshot: %v", err) } @@ -64,7 +65,7 @@ func TestSnapshotUpdate(t *testing.T) { defer DeleteSnapshot(t, client, snapshot) - expectedSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + expectedSnapshot, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { t.Errorf("Unable to retrieve snapshot: %v", err) } @@ -79,12 +80,12 @@ func TestSnapshotUpdate(t *testing.T) { expectedSnapshot.Name = name expectedSnapshot.Description = description - _, err = snapshots.Update(client, snapshot.ID, options).Extract() + _, err = snapshots.Update(context.TODO(), client, snapshot.ID, options).Extract() if err != nil { t.Errorf("Unable to update snapshot: %v", err) } - updatedSnapshot, err := snapshots.Get(client, snapshot.ID).Extract() + updatedSnapshot, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() if err != nil { t.Errorf("Unable to retrieve snapshot: %v", err) } @@ -148,7 +149,7 @@ func TestSnapshotResetStatus(t *testing.T) { resetStatusOpts := &snapshots.ResetStatusOpts{ Status: "error", } - err = snapshots.ResetStatus(client, snapshot.ID, resetStatusOpts).ExtractErr() + err = snapshots.ResetStatus(context.TODO(), client, snapshot.ID, resetStatusOpts).ExtractErr() if err != nil { t.Fatalf("Unable to reset a snapshot status: %v", err) } @@ -182,7 +183,7 @@ func TestSnapshotForceDelete(t *testing.T) { defer DeleteSnapshot(t, client, snapshot) - err = snapshots.ForceDelete(client, snapshot.ID).ExtractErr() + err = snapshots.ForceDelete(context.TODO(), client, snapshot.ID).ExtractErr() if err != nil { t.Fatalf("Unable to force delete a snapshot: %v", err) } diff --git a/internal/acceptance/openstack/workflow/v2/crontrigger.go b/internal/acceptance/openstack/workflow/v2/crontrigger.go index 01a80bfd5c..821129c318 100644 --- a/internal/acceptance/openstack/workflow/v2/crontrigger.go +++ b/internal/acceptance/openstack/workflow/v2/crontrigger.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "testing" "time" @@ -26,7 +27,7 @@ func CreateCronTrigger(t *testing.T, client *gophercloud.ServiceClient, workflow }, FirstExecutionTime: &firstExecution, } - crontrigger, err := crontriggers.Create(client, createOpts).Extract() + crontrigger, err := crontriggers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return crontrigger, err } @@ -37,7 +38,7 @@ func CreateCronTrigger(t *testing.T, client *gophercloud.ServiceClient, workflow // DeleteCronTrigger deletes a cron trigger. func DeleteCronTrigger(t *testing.T, client *gophercloud.ServiceClient, crontrigger *crontriggers.CronTrigger) { - err := crontriggers.Delete(client, crontrigger.ID).ExtractErr() + err := crontriggers.Delete(context.TODO(), client, crontrigger.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete cron trigger %s: %v", crontrigger.Name, err) } @@ -47,7 +48,7 @@ func DeleteCronTrigger(t *testing.T, client *gophercloud.ServiceClient, crontrig // GetCronTrigger gets a cron trigger. func GetCronTrigger(t *testing.T, client *gophercloud.ServiceClient, id string) (*crontriggers.CronTrigger, error) { - crontrigger, err := crontriggers.Get(client, id).Extract() + crontrigger, err := crontriggers.Get(context.TODO(), client, id).Extract() if err != nil { t.Fatalf("Unable to get cron trigger %s: %v", id, err) } @@ -57,7 +58,7 @@ func GetCronTrigger(t *testing.T, client *gophercloud.ServiceClient, id string) // ListCronTriggers lists cron triggers. func ListCronTriggers(t *testing.T, client *gophercloud.ServiceClient, opts crontriggers.ListOptsBuilder) ([]crontriggers.CronTrigger, error) { - allPages, err := crontriggers.List(client, opts).AllPages() + allPages, err := crontriggers.List(client, opts).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list cron triggers: %v", err) } diff --git a/internal/acceptance/openstack/workflow/v2/execution.go b/internal/acceptance/openstack/workflow/v2/execution.go index a3d5168149..398a278477 100644 --- a/internal/acceptance/openstack/workflow/v2/execution.go +++ b/internal/acceptance/openstack/workflow/v2/execution.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "testing" @@ -25,7 +26,7 @@ func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow * "msg": "Hello World!", }, } - execution, err := executions.Create(client, createOpts).Extract() + execution, err := executions.Create(context.TODO(), client, createOpts).Extract() if err != nil { return execution, err } @@ -36,7 +37,7 @@ func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow * t.Logf("Wait for execution status SUCCESS: %s", executionDescription) th.AssertNoErr(t, tools.WaitFor(func() (bool, error) { - latest, err := executions.Get(client, execution.ID).Extract() + latest, err := executions.Get(context.TODO(), client, execution.ID).Extract() if err != nil { return false, err } @@ -59,7 +60,7 @@ func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow * // DeleteExecution deletes an execution. func DeleteExecution(t *testing.T, client *gophercloud.ServiceClient, execution *executions.Execution) { - err := executions.Delete(client, execution.ID).ExtractErr() + err := executions.Delete(context.TODO(), client, execution.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete executions %s: %v", execution.Description, err) } @@ -68,7 +69,7 @@ func DeleteExecution(t *testing.T, client *gophercloud.ServiceClient, execution // ListExecutions lists the executions. func ListExecutions(t *testing.T, client *gophercloud.ServiceClient, opts executions.ListOptsBuilder) ([]executions.Execution, error) { - allPages, err := executions.List(client, opts).AllPages() + allPages, err := executions.List(client, opts).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list executions: %v", err) } diff --git a/internal/acceptance/openstack/workflow/v2/workflow.go b/internal/acceptance/openstack/workflow/v2/workflow.go index 4fa16d5e43..ea67739eac 100644 --- a/internal/acceptance/openstack/workflow/v2/workflow.go +++ b/internal/acceptance/openstack/workflow/v2/workflow.go @@ -1,6 +1,7 @@ package v2 import ( + "context" "fmt" "strings" "testing" @@ -45,7 +46,7 @@ func CreateWorkflow(t *testing.T, client *gophercloud.ServiceClient) (*workflows Scope: "private", Definition: strings.NewReader(definition), } - workflowList, err := workflows.Create(client, opts).Extract() + workflowList, err := workflows.Create(context.TODO(), client, opts).Extract() if err != nil { return nil, err } @@ -62,7 +63,7 @@ func CreateWorkflow(t *testing.T, client *gophercloud.ServiceClient) (*workflows // DeleteWorkflow deletes the given workflow. func DeleteWorkflow(t *testing.T, client *gophercloud.ServiceClient, workflow *workflows.Workflow) { - err := workflows.Delete(client, workflow.ID).ExtractErr() + err := workflows.Delete(context.TODO(), client, workflow.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete workflows %s: %v", workflow.Name, err) } @@ -72,7 +73,7 @@ func DeleteWorkflow(t *testing.T, client *gophercloud.ServiceClient, workflow *w // GetWorkflow gets a workflow. func GetWorkflow(t *testing.T, client *gophercloud.ServiceClient, id string) (*workflows.Workflow, error) { - workflow, err := workflows.Get(client, id).Extract() + workflow, err := workflows.Get(context.TODO(), client, id).Extract() if err != nil { t.Fatalf("Unable to get workflow %s: %v", id, err) } @@ -82,7 +83,7 @@ func GetWorkflow(t *testing.T, client *gophercloud.ServiceClient, id string) (*w // ListWorkflows lists the workflows. func ListWorkflows(t *testing.T, client *gophercloud.ServiceClient, opts workflows.ListOptsBuilder) ([]workflows.Workflow, error) { - allPages, err := workflows.List(client, opts).AllPages() + allPages, err := workflows.List(client, opts).AllPages(context.TODO()) if err != nil { t.Fatalf("Unable to list workflows: %v", err) } diff --git a/openstack/baremetal/apiversions/requests.go b/openstack/baremetal/apiversions/requests.go index cf096413a1..0ab8fd2c67 100644 --- a/openstack/baremetal/apiversions/requests.go +++ b/openstack/baremetal/apiversions/requests.go @@ -1,19 +1,21 @@ package apiversions import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // List lists all the API versions available to end users. -func List(client *gophercloud.ServiceClient) (r ListResult) { - resp, err := client.Get(listURL(client), &r.Body, nil) +func List(ctx context.Context, client *gophercloud.ServiceClient) (r ListResult) { + resp, err := client.Get(ctx, listURL(client), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will get a specific API version, specified by major ID. -func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - resp, err := client.Get(getURL(client, v), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, v string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetal/apiversions/testing/requests_test.go b/openstack/baremetal/apiversions/testing/requests_test.go index df2ec60f76..27d9fa5e22 100644 --- a/openstack/baremetal/apiversions/testing/requests_test.go +++ b/openstack/baremetal/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/baremetal/apiversions" @@ -14,7 +15,7 @@ func TestListAPIVersions(t *testing.T) { MockListResponse(t) - actual, err := apiversions.List(client.ServiceClient()).Extract() + actual, err := apiversions.List(context.TODO(), client.ServiceClient()).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, IronicAllAPIVersionResults, *actual) @@ -26,7 +27,7 @@ func TestGetAPIVersion(t *testing.T) { MockGetResponse(t) - actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() + actual, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v1").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, IronicAPIVersion1Result, *actual) diff --git a/openstack/baremetal/v1/allocations/requests.go b/openstack/baremetal/v1/allocations/requests.go index 851c70d589..4357f8c46c 100644 --- a/openstack/baremetal/v1/allocations/requests.go +++ b/openstack/baremetal/v1/allocations/requests.go @@ -1,6 +1,8 @@ package allocations import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -43,14 +45,14 @@ func (opts CreateOpts) ToAllocationCreateMap() (map[string]interface{}, error) { } // Create requests a node to be created -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToAllocationCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(ctx, createURL(client), reqBody, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -118,8 +120,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get requests the details of an allocation by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -127,8 +129,8 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { } // Delete requests the deletion of an allocation -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetal/v1/allocations/testing/requests_test.go b/openstack/baremetal/v1/allocations/testing/requests_test.go index 9c04477be5..2eec2aa51b 100644 --- a/openstack/baremetal/v1/allocations/testing/requests_test.go +++ b/openstack/baremetal/v1/allocations/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/allocations" @@ -15,7 +16,7 @@ func TestListAllocations(t *testing.T) { HandleAllocationListSuccessfully(t) pages := 0 - err := allocations.List(client.ServiceClient(), allocations.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := allocations.List(client.ServiceClient(), allocations.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := allocations.ExtractAllocations(page) @@ -44,7 +45,7 @@ func TestCreateAllocation(t *testing.T) { defer th.TeardownHTTP() HandleAllocationCreationSuccessfully(t, SingleAllocationBody) - actual, err := allocations.Create(client.ServiceClient(), allocations.CreateOpts{ + actual, err := allocations.Create(context.TODO(), client.ServiceClient(), allocations.CreateOpts{ Name: "allocation-1", ResourceClass: "baremetal", CandidateNodes: []string{"344a3e2-978a-444e-990a-cbf47c62ef88"}, @@ -60,7 +61,7 @@ func TestDeleteAllocation(t *testing.T) { defer th.TeardownHTTP() HandleAllocationDeletionSuccessfully(t) - res := allocations.Delete(client.ServiceClient(), "344a3e2-978a-444e-990a-cbf47c62ef88") + res := allocations.Delete(context.TODO(), client.ServiceClient(), "344a3e2-978a-444e-990a-cbf47c62ef88") th.AssertNoErr(t, res.Err) } @@ -70,7 +71,7 @@ func TestGetAllocation(t *testing.T) { HandleAllocationGetSuccessfully(t) c := client.ServiceClient() - actual, err := allocations.Get(c, "344a3e2-978a-444e-990a-cbf47c62ef88").Extract() + actual, err := allocations.Get(context.TODO(), c, "344a3e2-978a-444e-990a-cbf47c62ef88").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } diff --git a/openstack/baremetal/v1/conductors/requests.go b/openstack/baremetal/v1/conductors/requests.go index 96b1441e76..068ab215be 100644 --- a/openstack/baremetal/v1/conductors/requests.go +++ b/openstack/baremetal/v1/conductors/requests.go @@ -1,6 +1,7 @@ package conductors import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -63,8 +64,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // 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{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, name string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, name), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/baremetal/v1/conductors/testing/requests_test.go b/openstack/baremetal/v1/conductors/testing/requests_test.go index 8221a10919..7ddacf7f90 100644 --- a/openstack/baremetal/v1/conductors/testing/requests_test.go +++ b/openstack/baremetal/v1/conductors/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/conductors" @@ -15,7 +16,7 @@ func TestListConductors(t *testing.T) { HandleConductorListSuccessfully(t) pages := 0 - err := conductors.List(client.ServiceClient(), conductors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := conductors.List(client.ServiceClient(), conductors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := conductors.ExtractConductors(page) @@ -45,7 +46,7 @@ func TestListDetailConductors(t *testing.T) { HandleConductorListDetailSuccessfully(t) pages := 0 - err := conductors.List(client.ServiceClient(), conductors.ListOpts{Detail: true}).EachPage(func(page pagination.Page) (bool, error) { + err := conductors.List(client.ServiceClient(), conductors.ListOpts{Detail: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := conductors.ExtractConductors(page) @@ -97,7 +98,7 @@ func TestGetConductor(t *testing.T) { HandleConductorGetSuccessfully(t) c := client.ServiceClient() - actual, err := conductors.Get(c, "1234asdf").Extract() + actual, err := conductors.Get(context.TODO(), c, "1234asdf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } diff --git a/openstack/baremetal/v1/drivers/requests.go b/openstack/baremetal/v1/drivers/requests.go index 989408534f..f23629309c 100644 --- a/openstack/baremetal/v1/drivers/requests.go +++ b/openstack/baremetal/v1/drivers/requests.go @@ -1,6 +1,8 @@ package drivers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -42,8 +44,8 @@ func ListDrivers(client *gophercloud.ServiceClient, opts ListDriversOptsBuilder) } // GetDriverDetails Shows details for a driver -func GetDriverDetails(client *gophercloud.ServiceClient, driverName string) (r GetDriverResult) { - resp, err := client.Get(driverDetailsURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ +func GetDriverDetails(ctx context.Context, client *gophercloud.ServiceClient, driverName string) (r GetDriverResult) { + resp, err := client.Get(ctx, driverDetailsURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -53,8 +55,8 @@ func GetDriverDetails(client *gophercloud.ServiceClient, driverName string) (r G // GetDriverProperties Shows the required and optional parameters that // driverName expects to be supplied in the driver_info field for every // Node it manages -func GetDriverProperties(client *gophercloud.ServiceClient, driverName string) (r GetPropertiesResult) { - resp, err := client.Get(driverPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ +func GetDriverProperties(ctx context.Context, client *gophercloud.ServiceClient, driverName string) (r GetPropertiesResult) { + resp, err := client.Get(ctx, driverPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -64,8 +66,8 @@ func GetDriverProperties(client *gophercloud.ServiceClient, driverName string) ( // GetDriverDiskProperties Show the required and optional parameters that // driverName expects to be supplied in the node’s raid_config field, if a // RAID configuration change is requested. -func GetDriverDiskProperties(client *gophercloud.ServiceClient, driverName string) (r GetDiskPropertiesResult) { - resp, err := client.Get(driverDiskPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ +func GetDriverDiskProperties(ctx context.Context, client *gophercloud.ServiceClient, driverName string) (r GetDiskPropertiesResult) { + resp, err := client.Get(ctx, driverDiskPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/baremetal/v1/drivers/testing/requests_test.go b/openstack/baremetal/v1/drivers/testing/requests_test.go index c53af5addc..f99e3198c0 100644 --- a/openstack/baremetal/v1/drivers/testing/requests_test.go +++ b/openstack/baremetal/v1/drivers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/drivers" @@ -15,7 +16,7 @@ func TestListDrivers(t *testing.T) { HandleListDriversSuccessfully(t) pages := 0 - err := drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := drivers.ExtractDrivers(page) @@ -47,7 +48,7 @@ func TestGetDriverDetails(t *testing.T) { HandleGetDriverDetailsSuccessfully(t) c := client.ServiceClient() - actual, err := drivers.GetDriverDetails(c, "ipmi").Extract() + actual, err := drivers.GetDriverDetails(context.TODO(), c, "ipmi").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -61,7 +62,7 @@ func TestGetDriverProperties(t *testing.T) { HandleGetDriverPropertiesSuccessfully(t) c := client.ServiceClient() - actual, err := drivers.GetDriverProperties(c, "agent_ipmitool").Extract() + actual, err := drivers.GetDriverProperties(context.TODO(), c, "agent_ipmitool").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -75,7 +76,7 @@ func TestGetDriverDiskProperties(t *testing.T) { HandleGetDriverDiskPropertiesSuccessfully(t) c := client.ServiceClient() - actual, err := drivers.GetDriverDiskProperties(c, "agent_ipmitool").Extract() + actual, err := drivers.GetDriverDiskProperties(context.TODO(), c, "agent_ipmitool").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 443e36e872..567aa6be71 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -1,6 +1,7 @@ package nodes import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -166,8 +167,8 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } // Get requests details on a single node, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -270,14 +271,14 @@ func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { } // Create requests a node to be created -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToNodeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(ctx, createURL(client), reqBody, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -308,7 +309,7 @@ func (opts UpdateOperation) ToNodeUpdateMap() (map[string]interface{}, error) { } // Update requests that a node be updated -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { body := make([]map[string]interface{}, len(opts)) for i, patch := range opts { result, err := patch.ToNodeUpdateMap() @@ -319,7 +320,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up body[i] = result } - resp, err := client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -327,16 +328,16 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up } // Delete requests that a node be removed -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Request that Ironic validate 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. -func Validate(client *gophercloud.ServiceClient, id string) (r ValidateResult) { - resp, err := client.Get(validateURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Validate(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ValidateResult) { + resp, err := client.Get(ctx, validateURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -345,8 +346,8 @@ func Validate(client *gophercloud.ServiceClient, id string) (r ValidateResult) { // Inject NMI (Non-Masking Interrupts) for the given Node. This feature can be used for hardware diagnostics, and // actual support depends on a driver. -func InjectNMI(client *gophercloud.ServiceClient, id string) (r InjectNMIResult) { - resp, err := client.Put(injectNMIURL(client, id), map[string]string{}, nil, &gophercloud.RequestOpts{ +func InjectNMI(ctx context.Context, client *gophercloud.ServiceClient, id string) (r InjectNMIResult) { + resp, err := client.Put(ctx, injectNMIURL(client, id), map[string]string{}, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -376,14 +377,14 @@ func (opts BootDeviceOpts) ToBootDeviceMap() (map[string]interface{}, error) { // Set the boot device for the given Node, and set it persistently or for one-time boot. The exact behaviour // of this depends on the hardware driver. -func SetBootDevice(client *gophercloud.ServiceClient, id string, bootDevice BootDeviceOptsBuilder) (r SetBootDeviceResult) { +func SetBootDevice(ctx context.Context, client *gophercloud.ServiceClient, id string, bootDevice BootDeviceOptsBuilder) (r SetBootDeviceResult) { reqBody, err := bootDevice.ToBootDeviceMap() if err != nil { r.Err = err return } - resp, err := client.Put(bootDeviceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, bootDeviceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -391,8 +392,8 @@ func SetBootDevice(client *gophercloud.ServiceClient, id string, bootDevice Boot } // Get the current boot device for the given Node. -func GetBootDevice(client *gophercloud.ServiceClient, id string) (r BootDeviceResult) { - resp, err := client.Get(bootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func GetBootDevice(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BootDeviceResult) { + resp, err := client.Get(ctx, bootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -400,8 +401,8 @@ func GetBootDevice(client *gophercloud.ServiceClient, id string) (r BootDeviceRe } // Retrieve the acceptable set of supported boot devices for a specific Node. -func GetSupportedBootDevices(client *gophercloud.ServiceClient, id string) (r SupportedBootDeviceResult) { - resp, err := client.Get(supportedBootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func GetSupportedBootDevices(ctx context.Context, client *gophercloud.ServiceClient, id string) (r SupportedBootDeviceResult) { + resp, err := client.Get(ctx, supportedBootDeviceURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -475,14 +476,14 @@ func (opts ProvisionStateOpts) ToProvisionStateMap() (map[string]interface{}, er // Request a change to the Node’s provision state. Acceptable target states depend on the Node’s current provision // state. More detailed documentation of the Ironic State Machine is available in the developer docs. -func ChangeProvisionState(client *gophercloud.ServiceClient, id string, opts ProvisionStateOptsBuilder) (r ChangeStateResult) { +func ChangeProvisionState(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ProvisionStateOptsBuilder) (r ChangeStateResult) { reqBody, err := opts.ToProvisionStateMap() if err != nil { r.Err = err return } - resp, err := client.Put(provisionStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, provisionStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -522,14 +523,14 @@ func (opts PowerStateOpts) ToPowerStateMap() (map[string]interface{}, error) { } // Request to change a Node's power state. -func ChangePowerState(client *gophercloud.ServiceClient, id string, opts PowerStateOptsBuilder) (r ChangePowerStateResult) { +func ChangePowerState(ctx context.Context, client *gophercloud.ServiceClient, id string, opts PowerStateOptsBuilder) (r ChangePowerStateResult) { reqBody, err := opts.ToPowerStateMap() if err != nil { r.Err = err return } - resp, err := client.Put(powerStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, powerStateURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -631,14 +632,14 @@ func (opts RAIDConfigOpts) ToRAIDConfigMap() (map[string]interface{}, error) { } // Request to change a Node's RAID config. -func SetRAIDConfig(client *gophercloud.ServiceClient, id string, raidConfigOptsBuilder RAIDConfigOptsBuilder) (r ChangeStateResult) { +func SetRAIDConfig(ctx context.Context, client *gophercloud.ServiceClient, id string, raidConfigOptsBuilder RAIDConfigOptsBuilder) (r ChangeStateResult) { reqBody, err := raidConfigOptsBuilder.ToRAIDConfigMap() if err != nil { r.Err = err return } - resp, err := client.Put(raidConfigURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, raidConfigURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -672,7 +673,7 @@ func (opts ListBIOSSettingsOpts) ToListBIOSSettingsOptsQuery() (string, error) { // Get the current BIOS Settings for the given Node. // To use the opts requires microversion 1.74. -func ListBIOSSettings(client *gophercloud.ServiceClient, id string, opts ListBIOSSettingsOptsBuilder) (r ListBIOSSettingsResult) { +func ListBIOSSettings(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ListBIOSSettingsOptsBuilder) (r ListBIOSSettingsResult) { url := biosListSettingsURL(client, id) if opts != nil { @@ -684,7 +685,7 @@ func ListBIOSSettings(client *gophercloud.ServiceClient, id string, opts ListBIO url += query } - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -692,8 +693,8 @@ func ListBIOSSettings(client *gophercloud.ServiceClient, id string, opts ListBIO } // Get one BIOS Setting for the given Node. -func GetBIOSSetting(client *gophercloud.ServiceClient, id string, setting string) (r GetBIOSSettingResult) { - resp, err := client.Get(biosGetSettingURL(client, id, setting), &r.Body, &gophercloud.RequestOpts{ +func GetBIOSSetting(ctx context.Context, client *gophercloud.ServiceClient, id string, setting string) (r GetBIOSSettingResult) { + resp, err := client.Get(ctx, biosGetSettingURL(client, id, setting), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -712,8 +713,8 @@ func ToGetAllSubscriptionMap(opts CallVendorPassthruOpts) (string, error) { } // Get all vendor_passthru methods available for the given Node. -func GetVendorPassthruMethods(client *gophercloud.ServiceClient, id string) (r VendorPassthruMethodsResult) { - resp, err := client.Get(vendorPassthruMethodsURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func GetVendorPassthruMethods(ctx context.Context, client *gophercloud.ServiceClient, id string) (r VendorPassthruMethodsResult) { + resp, err := client.Get(ctx, vendorPassthruMethodsURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -721,14 +722,14 @@ func GetVendorPassthruMethods(client *gophercloud.ServiceClient, id string) (r V } // Get all subscriptions available for the given Node. -func GetAllSubscriptions(client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts) (r GetAllSubscriptionsVendorPassthruResult) { +func GetAllSubscriptions(ctx context.Context, client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts) (r GetAllSubscriptionsVendorPassthruResult) { query, err := ToGetAllSubscriptionMap(method) if err != nil { r.Err = err return } url := vendorPassthruCallURL(client, id) + query - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -755,14 +756,14 @@ func ToGetSubscriptionMap(method CallVendorPassthruOpts, opts GetSubscriptionOpt } // Get a subscription on the given Node. -func GetSubscription(client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts GetSubscriptionOpts) (r SubscriptionVendorPassthruResult) { +func GetSubscription(ctx context.Context, client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts GetSubscriptionOpts) (r SubscriptionVendorPassthruResult) { query, reqBody, err := ToGetSubscriptionMap(method, subscriptionOpts) if err != nil { r.Err = err return } url := vendorPassthruCallURL(client, id) + query - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ JSONBody: reqBody, OkCodes: []int{200}, }) @@ -789,14 +790,14 @@ func ToDeleteSubscriptionMap(method CallVendorPassthruOpts, opts DeleteSubscript } // Delete a subscription on the given node. -func DeleteSubscription(client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts DeleteSubscriptionOpts) (r DeleteSubscriptionVendorPassthruResult) { +func DeleteSubscription(ctx context.Context, client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts DeleteSubscriptionOpts) (r DeleteSubscriptionVendorPassthruResult) { query, reqBody, err := ToDeleteSubscriptionMap(method, subscriptionOpts) if err != nil { r.Err = err return } url := vendorPassthruCallURL(client, id) + query - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ JSONBody: reqBody, OkCodes: []int{200, 202, 204}, }) @@ -827,14 +828,14 @@ func ToCreateSubscriptionMap(method CallVendorPassthruOpts, opts CreateSubscript } // Creates a subscription on the given node. -func CreateSubscription(client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts CreateSubscriptionOpts) (r SubscriptionVendorPassthruResult) { +func CreateSubscription(ctx context.Context, client *gophercloud.ServiceClient, id string, method CallVendorPassthruOpts, subscriptionOpts CreateSubscriptionOpts) (r SubscriptionVendorPassthruResult) { query, reqBody, err := ToCreateSubscriptionMap(method, subscriptionOpts) if err != nil { r.Err = err return } url := vendorPassthruCallURL(client, id) + query - resp, err := client.Post(url, reqBody, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, url, reqBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -862,14 +863,14 @@ func (opts MaintenanceOpts) ToMaintenanceMap() (map[string]interface{}, error) { } // Request to set the Node's maintenance mode. -func SetMaintenance(client *gophercloud.ServiceClient, id string, opts MaintenanceOptsBuilder) (r SetMaintenanceResult) { +func SetMaintenance(ctx context.Context, 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{ + resp, err := client.Put(ctx, maintenanceURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -877,8 +878,8 @@ func SetMaintenance(client *gophercloud.ServiceClient, id string, opts Maintenan } // 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{ +func UnsetMaintenance(ctx context.Context, client *gophercloud.ServiceClient, id string) (r SetMaintenanceResult) { + resp, err := client.Delete(ctx, maintenanceURL(client, id), &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -886,8 +887,8 @@ func UnsetMaintenance(client *gophercloud.ServiceClient, id string) (r SetMainte } // GetInventory return stored data from successful inspection. -func GetInventory(client *gophercloud.ServiceClient, id string) (r InventoryResult) { - resp, err := client.Get(inventoryURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func GetInventory(ctx context.Context, client *gophercloud.ServiceClient, id string) (r InventoryResult) { + resp, err := client.Get(ctx, inventoryURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -895,8 +896,8 @@ func GetInventory(client *gophercloud.ServiceClient, id string) (r InventoryResu } // ListFirmware return the list of Firmware components for the given Node. -func ListFirmware(client *gophercloud.ServiceClient, id string) (r ListFirmwareResult) { - resp, err := client.Get(firmwareListURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func ListFirmware(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ListFirmwareResult) { + resp, err := client.Get(ctx, firmwareListURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 6fca0e87a3..c0f1a28360 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -16,7 +17,7 @@ func TestListDetailNodes(t *testing.T) { HandleNodeListDetailSuccessfully(t) pages := 0 - err := nodes.ListDetail(client.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := nodes.ListDetail(client.ServiceClient(), nodes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := nodes.ExtractNodes(page) @@ -47,7 +48,7 @@ func TestListNodes(t *testing.T) { HandleNodeListSuccessfully(t) pages := 0 - err := nodes.List(client.ServiceClient(), nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := nodes.List(client.ServiceClient(), nodes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := nodes.ExtractNodes(page) @@ -92,7 +93,7 @@ func TestCreateNode(t *testing.T) { defer th.TeardownHTTP() HandleNodeCreationSuccessfully(t, SingleNodeBody) - actual, err := nodes.Create(client.ServiceClient(), nodes.CreateOpts{ + actual, err := nodes.Create(context.TODO(), client.ServiceClient(), nodes.CreateOpts{ Name: "foo", Driver: "ipmi", BootInterface: "pxe", @@ -116,7 +117,7 @@ func TestDeleteNode(t *testing.T) { defer th.TeardownHTTP() HandleNodeDeletionSuccessfully(t) - res := nodes.Delete(client.ServiceClient(), "asdfasdfasdf") + res := nodes.Delete(context.TODO(), client.ServiceClient(), "asdfasdfasdf") th.AssertNoErr(t, res.Err) } @@ -126,7 +127,7 @@ func TestGetNode(t *testing.T) { HandleNodeGetSuccessfully(t) c := client.ServiceClient() - actual, err := nodes.Get(c, "1234asdf").Extract() + actual, err := nodes.Get(context.TODO(), c, "1234asdf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -140,7 +141,7 @@ func TestUpdateNode(t *testing.T) { HandleNodeUpdateSuccessfully(t, SingleNodeBody) c := client.ServiceClient() - actual, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ + actual, err := nodes.Update(context.TODO(), c, "1234asdf", nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/properties", @@ -158,7 +159,7 @@ func TestUpdateNode(t *testing.T) { func TestUpdateRequiredOp(t *testing.T) { c := client.ServiceClient() - _, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ + _, err := nodes.Update(context.TODO(), c, "1234asdf", nodes.UpdateOpts{ nodes.UpdateOperation{ Path: "/driver", Value: "new-driver", @@ -173,7 +174,7 @@ func TestUpdateRequiredOp(t *testing.T) { func TestUpdateRequiredPath(t *testing.T) { c := client.ServiceClient() - _, err := nodes.Update(c, "1234asdf", nodes.UpdateOpts{ + _, err := nodes.Update(context.TODO(), c, "1234asdf", nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Value: "new-driver", @@ -191,7 +192,7 @@ func TestValidateNode(t *testing.T) { HandleNodeValidateSuccessfully(t) c := client.ServiceClient() - actual, err := nodes.Validate(c, "1234asdf").Extract() + actual, err := nodes.Validate(context.TODO(), c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeFooValidation, *actual) } @@ -202,7 +203,7 @@ func TestInjectNMI(t *testing.T) { HandleInjectNMISuccessfully(t) c := client.ServiceClient() - err := nodes.InjectNMI(c, "1234asdf").ExtractErr() + err := nodes.InjectNMI(context.TODO(), c, "1234asdf").ExtractErr() th.AssertNoErr(t, err) } @@ -212,7 +213,7 @@ func TestSetBootDevice(t *testing.T) { HandleSetBootDeviceSuccessfully(t) c := client.ServiceClient() - err := nodes.SetBootDevice(c, "1234asdf", nodes.BootDeviceOpts{ + err := nodes.SetBootDevice(context.TODO(), c, "1234asdf", nodes.BootDeviceOpts{ BootDevice: "pxe", Persistent: false, }).ExtractErr() @@ -225,7 +226,7 @@ func TestGetBootDevice(t *testing.T) { HandleGetBootDeviceSuccessfully(t) c := client.ServiceClient() - bootDevice, err := nodes.GetBootDevice(c, "1234asdf").Extract() + bootDevice, err := nodes.GetBootDevice(context.TODO(), c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeBootDevice, *bootDevice) } @@ -236,7 +237,7 @@ func TestGetSupportedBootDevices(t *testing.T) { HandleGetSupportedBootDeviceSuccessfully(t) c := client.ServiceClient() - bootDevices, err := nodes.GetSupportedBootDevices(c, "1234asdf").Extract() + bootDevices, err := nodes.GetSupportedBootDevices(context.TODO(), c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeSupportedBootDevice, bootDevices) } @@ -247,7 +248,7 @@ func TestNodeChangeProvisionStateActive(t *testing.T) { HandleNodeChangeProvisionStateActive(t) c := client.ServiceClient() - err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + err := nodes.ChangeProvisionState(context.TODO(), c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetActive, ConfigDrive: "http://127.0.0.1/images/test-node-config-drive.iso.gz", }).ExtractErr() @@ -261,7 +262,7 @@ func TestNodeChangeProvisionStateActiveWithSteps(t *testing.T) { HandleNodeChangeProvisionStateActiveWithSteps(t) c := client.ServiceClient() - err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + err := nodes.ChangeProvisionState(context.TODO(), c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetActive, DeploySteps: []nodes.DeployStep{ { @@ -286,7 +287,7 @@ func TestHandleNodeChangeProvisionStateConfigDrive(t *testing.T) { c := client.ServiceClient() - err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + err := nodes.ChangeProvisionState(context.TODO(), c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetActive, ConfigDrive: ConfigDriveMap, }).ExtractErr() @@ -300,7 +301,7 @@ func TestNodeChangeProvisionStateClean(t *testing.T) { HandleNodeChangeProvisionStateClean(t) c := client.ServiceClient() - err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + err := nodes.ChangeProvisionState(context.TODO(), c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { @@ -322,7 +323,7 @@ func TestNodeChangeProvisionStateCleanWithConflict(t *testing.T) { HandleNodeChangeProvisionStateCleanWithConflict(t) c := client.ServiceClient() - err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + err := nodes.ChangeProvisionState(context.TODO(), c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { @@ -342,7 +343,7 @@ func TestNodeChangeProvisionStateCleanWithConflict(t *testing.T) { func TestCleanStepRequiresInterface(t *testing.T) { c := client.ServiceClient() - err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + err := nodes.ChangeProvisionState(context.TODO(), c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { @@ -361,7 +362,7 @@ func TestCleanStepRequiresInterface(t *testing.T) { func TestCleanStepRequiresStep(t *testing.T) { c := client.ServiceClient() - err := nodes.ChangeProvisionState(c, "1234asdf", nodes.ProvisionStateOpts{ + err := nodes.ChangeProvisionState(context.TODO(), c, "1234asdf", nodes.ProvisionStateOpts{ Target: nodes.TargetClean, CleanSteps: []nodes.CleanStep{ { @@ -389,7 +390,7 @@ func TestChangePowerState(t *testing.T) { } c := client.ServiceClient() - err := nodes.ChangePowerState(c, "1234asdf", opts).ExtractErr() + err := nodes.ChangePowerState(context.TODO(), c, "1234asdf", opts).ExtractErr() th.AssertNoErr(t, err) } @@ -404,7 +405,7 @@ func TestChangePowerStateWithConflict(t *testing.T) { } c := client.ServiceClient() - err := nodes.ChangePowerState(c, "1234asdf", opts).ExtractErr() + err := nodes.ChangePowerState(context.TODO(), c, "1234asdf", opts).ExtractErr() if _, ok := err.(gophercloud.ErrDefault409); !ok { t.Fatal("ErrDefault409 was expected to occur") } @@ -429,7 +430,7 @@ func TestSetRAIDConfig(t *testing.T) { } c := client.ServiceClient() - err := nodes.SetRAIDConfig(c, "1234asdf", config).ExtractErr() + err := nodes.SetRAIDConfig(context.TODO(), c, "1234asdf", config).ExtractErr() th.AssertNoErr(t, err) } @@ -451,7 +452,7 @@ func TestSetRAIDConfigMaxSize(t *testing.T) { } c := client.ServiceClient() - err := nodes.SetRAIDConfig(c, "1234asdf", config).ExtractErr() + err := nodes.SetRAIDConfig(context.TODO(), c, "1234asdf", config).ExtractErr() th.AssertNoErr(t, err) } @@ -553,7 +554,7 @@ func TestListBIOSSettings(t *testing.T) { HandleListBIOSSettingsSuccessfully(t) c := client.ServiceClient() - actual, err := nodes.ListBIOSSettings(c, "1234asdf", nil).Extract() + actual, err := nodes.ListBIOSSettings(context.TODO(), c, "1234asdf", nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeBIOSSettings, actual) } @@ -568,7 +569,7 @@ func TestListDetailBIOSSettings(t *testing.T) { } c := client.ServiceClient() - actual, err := nodes.ListBIOSSettings(c, "1234asdf", opts).Extract() + actual, err := nodes.ListBIOSSettings(context.TODO(), c, "1234asdf", opts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeDetailBIOSSettings, actual) } @@ -579,7 +580,7 @@ func TestGetBIOSSetting(t *testing.T) { HandleGetBIOSSettingSuccessfully(t) c := client.ServiceClient() - actual, err := nodes.GetBIOSSetting(c, "1234asdf", "ProcVirtualization").Extract() + actual, err := nodes.GetBIOSSetting(context.TODO(), c, "1234asdf", "ProcVirtualization").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeSingleBIOSSetting, *actual) } @@ -601,7 +602,7 @@ func TestGetVendorPassthruMethods(t *testing.T) { HandleGetVendorPassthruMethodsSuccessfully(t) c := client.ServiceClient() - actual, err := nodes.GetVendorPassthruMethods(c, "1234asdf").Extract() + actual, err := nodes.GetVendorPassthruMethods(context.TODO(), c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeVendorPassthruMethods, *actual) } @@ -615,7 +616,7 @@ func TestGetAllSubscriptions(t *testing.T) { method := nodes.CallVendorPassthruOpts{ Method: "get_all_subscriptions", } - actual, err := nodes.GetAllSubscriptions(c, "1234asdf", method).Extract() + actual, err := nodes.GetAllSubscriptions(context.TODO(), c, "1234asdf", method).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeGetAllSubscriptions, *actual) } @@ -632,7 +633,7 @@ func TestGetSubscription(t *testing.T) { subscriptionOpt := nodes.GetSubscriptionOpts{ Id: "62dbd1b6-f637-11eb-b551-4cd98f20754c", } - actual, err := nodes.GetSubscription(c, "1234asdf", method, subscriptionOpt).Extract() + actual, err := nodes.GetSubscription(context.TODO(), c, "1234asdf", method, subscriptionOpt).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeGetSubscription, *actual) } @@ -653,7 +654,7 @@ func TestCreateSubscriptionAllParameters(t *testing.T) { EventTypes: []string{"Alert"}, HttpHeaders: []map[string]string{{"Content-Type": "application/json"}}, } - actual, err := nodes.CreateSubscription(c, "1234asdf", method, createOpt).Extract() + actual, err := nodes.CreateSubscription(context.TODO(), c, "1234asdf", method, createOpt).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeCreateSubscriptionAllParameters, *actual) } @@ -670,7 +671,7 @@ func TestCreateSubscriptionWithRequiredParameters(t *testing.T) { createOpt := nodes.CreateSubscriptionOpts{ Destination: "https://somedestinationurl", } - actual, err := nodes.CreateSubscription(c, "1234asdf", method, createOpt).Extract() + actual, err := nodes.CreateSubscription(context.TODO(), c, "1234asdf", method, createOpt).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeCreateSubscriptionRequiredParameters, *actual) } @@ -687,7 +688,7 @@ func TestDeleteSubscription(t *testing.T) { deleteOpt := nodes.DeleteSubscriptionOpts{ Id: "344a3e2-978a-444e-990a-cbf47c62ef88", } - err := nodes.DeleteSubscription(c, "1234asdf", method, deleteOpt).ExtractErr() + err := nodes.DeleteSubscription(context.TODO(), c, "1234asdf", method, deleteOpt).ExtractErr() th.AssertNoErr(t, err) } @@ -697,7 +698,7 @@ func TestSetMaintenance(t *testing.T) { HandleSetNodeMaintenanceSuccessfully(t) c := client.ServiceClient() - err := nodes.SetMaintenance(c, "1234asdf", nodes.MaintenanceOpts{ + err := nodes.SetMaintenance(context.TODO(), c, "1234asdf", nodes.MaintenanceOpts{ Reason: "I'm tired", }).ExtractErr() th.AssertNoErr(t, err) @@ -709,7 +710,7 @@ func TestUnsetMaintenance(t *testing.T) { HandleUnsetNodeMaintenanceSuccessfully(t) c := client.ServiceClient() - err := nodes.UnsetMaintenance(c, "1234asdf").ExtractErr() + err := nodes.UnsetMaintenance(context.TODO(), c, "1234asdf").ExtractErr() th.AssertNoErr(t, err) } @@ -719,7 +720,7 @@ func TestGetInventory(t *testing.T) { HandleGetInventorySuccessfully(t) c := client.ServiceClient() - actual, err := nodes.GetInventory(c, "1234asdf").Extract() + actual, err := nodes.GetInventory(context.TODO(), c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeInventoryData.Inventory, actual.Inventory) @@ -738,7 +739,7 @@ func TestListFirmware(t *testing.T) { HandleListFirmwareSuccessfully(t) c := client.ServiceClient() - actual, err := nodes.ListFirmware(c, "1234asdf").Extract() + actual, err := nodes.ListFirmware(context.TODO(), c, "1234asdf").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeFirmwareList, actual) } diff --git a/openstack/baremetal/v1/ports/requests.go b/openstack/baremetal/v1/ports/requests.go index 3812b0d3b1..2c4e515191 100644 --- a/openstack/baremetal/v1/ports/requests.go +++ b/openstack/baremetal/v1/ports/requests.go @@ -1,6 +1,7 @@ package ports import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -98,8 +99,8 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } // Get - requests the details off a port, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -155,14 +156,14 @@ func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { } // Create - requests the creation of a port -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToPortCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(ctx, createURL(client), reqBody, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -198,13 +199,13 @@ func (opts UpdateOperation) ToPortUpdateMap() map[string]interface{} { } // Update - requests the update of a port -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { body := make([]map[string]interface{}, len(opts)) for i, patch := range opts { body[i] = patch.ToPortUpdateMap() } - resp, err := client.Patch(updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -212,8 +213,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up } // Delete - requests the deletion of a port -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/baremetal/v1/ports/testing/requests_test.go b/openstack/baremetal/v1/ports/testing/requests_test.go index 6c04c8567e..95073b0a71 100644 --- a/openstack/baremetal/v1/ports/testing/requests_test.go +++ b/openstack/baremetal/v1/ports/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/ports" @@ -15,7 +16,7 @@ func TestListDetailPorts(t *testing.T) { HandlePortListDetailSuccessfully(t) pages := 0 - err := ports.ListDetail(client.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := ports.ListDetail(client.ServiceClient(), ports.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := ports.ExtractPorts(page) @@ -45,7 +46,7 @@ func TestListPorts(t *testing.T) { HandlePortListSuccessfully(t) pages := 0 - err := ports.List(client.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := ports.List(client.ServiceClient(), ports.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := ports.ExtractPorts(page) @@ -90,7 +91,7 @@ func TestCreatePort(t *testing.T) { HandlePortCreationSuccessfully(t, SinglePortBody) iTrue := true - actual, err := ports.Create(client.ServiceClient(), ports.CreateOpts{ + actual, err := ports.Create(context.TODO(), client.ServiceClient(), ports.CreateOpts{ NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", Address: "52:54:00:4d:87:e6", PXEEnabled: &iTrue, @@ -105,7 +106,7 @@ func TestDeletePort(t *testing.T) { defer th.TeardownHTTP() HandlePortDeletionSuccessfully(t) - res := ports.Delete(client.ServiceClient(), "3abe3f36-9708-4e9f-b07e-0f898061d3a7") + res := ports.Delete(context.TODO(), client.ServiceClient(), "3abe3f36-9708-4e9f-b07e-0f898061d3a7") th.AssertNoErr(t, res.Err) } @@ -115,7 +116,7 @@ func TestGetPort(t *testing.T) { HandlePortGetSuccessfully(t) c := client.ServiceClient() - actual, err := ports.Get(c, "f2845e11-dbd4-4728-a8c0-30d19f48924a").Extract() + actual, err := ports.Get(context.TODO(), c, "f2845e11-dbd4-4728-a8c0-30d19f48924a").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -129,7 +130,7 @@ func TestUpdatePort(t *testing.T) { HandlePortUpdateSuccessfully(t, SinglePortBody) c := client.ServiceClient() - actual, err := ports.Update(c, "f2845e11-dbd4-4728-a8c0-30d19f48924a", ports.UpdateOpts{ + actual, err := ports.Update(context.TODO(), c, "f2845e11-dbd4-4728-a8c0-30d19f48924a", ports.UpdateOpts{ ports.UpdateOperation{ Op: ports.ReplaceOp, Path: "/address", diff --git a/openstack/baremetalintrospection/v1/introspection/requests.go b/openstack/baremetalintrospection/v1/introspection/requests.go index e7e1b39f08..25939baab4 100644 --- a/openstack/baremetalintrospection/v1/introspection/requests.go +++ b/openstack/baremetalintrospection/v1/introspection/requests.go @@ -1,6 +1,8 @@ package introspection import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -47,8 +49,8 @@ func ListIntrospections(client *gophercloud.ServiceClient, opts ListIntrospectio // GetIntrospectionStatus makes a request against the Inspector API to get the // status of a single introspection. -func GetIntrospectionStatus(client *gophercloud.ServiceClient, nodeID string) (r GetIntrospectionStatusResult) { - resp, err := client.Get(introspectionURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ +func GetIntrospectionStatus(ctx context.Context, client *gophercloud.ServiceClient, nodeID string) (r GetIntrospectionStatusResult) { + resp, err := client.Get(ctx, introspectionURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -75,14 +77,14 @@ func (opts StartOpts) ToStartIntrospectionQuery() (string, error) { // StartIntrospection initiate hardware introspection for node NodeID . // All power management configuration for this node needs to be done prior to calling the endpoint. -func StartIntrospection(client *gophercloud.ServiceClient, nodeID string, opts StartOptsBuilder) (r StartResult) { +func StartIntrospection(ctx context.Context, client *gophercloud.ServiceClient, nodeID string, opts StartOptsBuilder) (r StartResult) { _, err := opts.ToStartIntrospectionQuery() if err != nil { r.Err = err return } - resp, err := client.Post(introspectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, introspectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -90,8 +92,8 @@ func StartIntrospection(client *gophercloud.ServiceClient, nodeID string, opts S } // AbortIntrospection abort running introspection. -func AbortIntrospection(client *gophercloud.ServiceClient, nodeID string) (r AbortResult) { - resp, err := client.Post(abortIntrospectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ +func AbortIntrospection(ctx context.Context, client *gophercloud.ServiceClient, nodeID string) (r AbortResult) { + resp, err := client.Post(ctx, abortIntrospectionURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -99,8 +101,8 @@ func AbortIntrospection(client *gophercloud.ServiceClient, nodeID string) (r Abo } // GetIntrospectionData return stored data from successful introspection. -func GetIntrospectionData(client *gophercloud.ServiceClient, nodeID string) (r DataResult) { - resp, err := client.Get(introspectionDataURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ +func GetIntrospectionData(ctx context.Context, client *gophercloud.ServiceClient, nodeID string) (r DataResult) { + resp, err := client.Get(ctx, introspectionDataURL(client, nodeID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -109,8 +111,8 @@ func GetIntrospectionData(client *gophercloud.ServiceClient, nodeID string) (r D // ReApplyIntrospection triggers introspection on stored unprocessed data. // No data is allowed to be sent along with the request. -func ReApplyIntrospection(client *gophercloud.ServiceClient, nodeID string) (r ApplyDataResult) { - resp, err := client.Post(introspectionUnprocessedDataURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ +func ReApplyIntrospection(ctx context.Context, client *gophercloud.ServiceClient, nodeID string) (r ApplyDataResult) { + resp, err := client.Post(ctx, introspectionUnprocessedDataURL(client, nodeID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go b/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go index c67af04998..b36318ce76 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection" @@ -15,7 +16,7 @@ func TestListIntrospections(t *testing.T) { HandleListIntrospectionsSuccessfully(t) pages := 0 - err := introspection.ListIntrospections(client.ServiceClient(), introspection.ListIntrospectionsOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := introspection.ListIntrospections(client.ServiceClient(), introspection.ListIntrospectionsOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := introspection.ExtractIntrospections(page) @@ -45,7 +46,7 @@ func TestGetIntrospectionStatus(t *testing.T) { HandleGetIntrospectionStatusSuccessfully(t) c := client.ServiceClient() - actual, err := introspection.GetIntrospectionStatus(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").Extract() + actual, err := introspection.GetIntrospectionStatus(context.TODO(), c, "c244557e-899f-46fa-a1ff-5b2c6718616b").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -59,7 +60,7 @@ func TestStartIntrospection(t *testing.T) { HandleStartIntrospectionSuccessfully(t) c := client.ServiceClient() - err := introspection.StartIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b", introspection.StartOpts{}).ExtractErr() + err := introspection.StartIntrospection(context.TODO(), c, "c244557e-899f-46fa-a1ff-5b2c6718616b", introspection.StartOpts{}).ExtractErr() th.AssertNoErr(t, err) } @@ -69,7 +70,7 @@ func TestAbortIntrospection(t *testing.T) { HandleAbortIntrospectionSuccessfully(t) c := client.ServiceClient() - err := introspection.AbortIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").ExtractErr() + err := introspection.AbortIntrospection(context.TODO(), c, "c244557e-899f-46fa-a1ff-5b2c6718616b").ExtractErr() th.AssertNoErr(t, err) } @@ -79,7 +80,7 @@ func TestGetIntrospectionData(t *testing.T) { HandleGetIntrospectionDataSuccessfully(t) c := client.ServiceClient() - actual, err := introspection.GetIntrospectionData(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").Extract() + actual, err := introspection.GetIntrospectionData(context.TODO(), c, "c244557e-899f-46fa-a1ff-5b2c6718616b").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -93,6 +94,6 @@ func TestReApplyIntrospection(t *testing.T) { HandleReApplyIntrospectionSuccessfully(t) c := client.ServiceClient() - err := introspection.ReApplyIntrospection(c, "c244557e-899f-46fa-a1ff-5b2c6718616b").ExtractErr() + err := introspection.ReApplyIntrospection(context.TODO(), c, "c244557e-899f-46fa-a1ff-5b2c6718616b").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/blockstorage/apiversions/testing/requests_test.go b/openstack/blockstorage/apiversions/testing/requests_test.go index 24bd28ca9f..1cfcb72e29 100644 --- a/openstack/blockstorage/apiversions/testing/requests_test.go +++ b/openstack/blockstorage/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -15,7 +16,7 @@ func TestListVersions(t *testing.T) { MockListResponse(t) - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersions(allVersions) th.AssertNoErr(t, err) @@ -49,7 +50,7 @@ func TestListOldVersions(t *testing.T) { MockListOldResponse(t) - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersions(allVersions) th.AssertNoErr(t, err) @@ -76,7 +77,7 @@ func TestGetVersion(t *testing.T) { MockListResponse(t) - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersion(allVersions, "v3.0") th.AssertNoErr(t, err) @@ -100,7 +101,7 @@ func TestGetOldVersion(t *testing.T) { MockListOldResponse(t) - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersion(allVersions, "v2.0") th.AssertNoErr(t, err) diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go b/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go index c722827ea5..07f74d6aa0 100644 --- a/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go +++ b/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/availabilityzones" @@ -15,7 +16,7 @@ func TestList(t *testing.T) { HandleGetSuccessfully(t) - allPages, err := az.List(client.ServiceClient()).AllPages() + allPages, err := az.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := az.ExtractAvailabilityZones(allPages) diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/extensions/backups/requests.go index 2e201d7431..57f5f55ee3 100644 --- a/openstack/blockstorage/extensions/backups/requests.go +++ b/openstack/blockstorage/extensions/backups/requests.go @@ -1,6 +1,8 @@ package backups import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -55,13 +57,13 @@ func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { // Create will create a new Backup based on the values in CreateOpts. To // extract the Backup object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToBackupCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -69,16 +71,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing Backup with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Backup with the provided ID. To extract the Backup // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -223,13 +225,13 @@ func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { // the updated Backup from the response, call the Extract method on the // UpdateResult. // Requires microversion 3.9 or later. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToBackupUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -255,13 +257,13 @@ func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { // RestoreFromBackup will restore a Backup to a volume based on the values in // RestoreOpts. To extract the Restore object from the response, call the // Extract method on the RestoreResult. -func RestoreFromBackup(client *gophercloud.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { +func RestoreFromBackup(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { b, err := opts.ToRestoreMap() if err != nil { r.Err = err return } - resp, err := client.Post(restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -270,8 +272,8 @@ func RestoreFromBackup(client *gophercloud.ServiceClient, id string, opts Restor // Export will export a Backup information. To extract the Backup export record // object from the response, call the Extract method on the ExportResult. -func Export(client *gophercloud.ServiceClient, id string) (r ExportResult) { - resp, err := client.Get(exportURL(client, id), &r.Body, nil) +func Export(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ExportResult) { + resp, err := client.Get(ctx, exportURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -289,13 +291,13 @@ func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { // Import will import a Backup data to a backup based on the values in // ImportOpts. To extract the Backup object from the response, call the // Extract method on the ImportResult. -func Import(client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) { +func Import(ctx context.Context, client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) { b, err := opts.ToBackupImportMap() if err != nil { r.Err = err return } - resp, err := client.Post(importURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, importURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -324,14 +326,14 @@ func (opts ResetStatusOpts) ToBackupResetStatusMap() (map[string]interface{}, er // ResetStatus will reset the existing backup 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) { +func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { b, err := opts.ToBackupResetStatusMap() if err != nil { r.Err = err return } - resp, err := client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -340,11 +342,11 @@ func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusO // ForceDelete will delete the existing backup 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) { +func ForceDelete(ctx context.Context, 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{ + resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/extensions/backups/testing/requests_test.go b/openstack/blockstorage/extensions/backups/testing/requests_test.go index 6fc405cb45..667715c3cc 100644 --- a/openstack/blockstorage/extensions/backups/testing/requests_test.go +++ b/openstack/blockstorage/extensions/backups/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "encoding/json" "testing" "time" @@ -19,7 +20,7 @@ func TestList(t *testing.T) { count := 0 - err := backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := backups.ExtractBackups(page) if err != nil { @@ -59,7 +60,7 @@ func TestListDetail(t *testing.T) { count := 0 - err := backups.ListDetail(client.ServiceClient(), &backups.ListDetailOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := backups.ListDetail(client.ServiceClient(), &backups.ListDetailOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := backups.ExtractBackups(page) if err != nil { @@ -107,7 +108,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - v, err := backups.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + v, err := backups.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "backup-001") @@ -121,7 +122,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := backups.CreateOpts{VolumeID: "1234", Name: "backup-001"} - n, err := backups.Create(client.ServiceClient(), options).Extract() + n, err := backups.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") @@ -136,7 +137,7 @@ func TestRestore(t *testing.T) { MockRestoreResponse(t) options := backups.RestoreOpts{VolumeID: "1234", Name: "vol-001"} - n, err := backups.RestoreFromBackup(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + n, err := backups.RestoreFromBackup(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") @@ -150,7 +151,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := backups.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := backups.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } @@ -160,7 +161,7 @@ func TestExport(t *testing.T) { MockExportResponse(t) - n, err := backups.Export(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + n, err := backups.Export(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.BackupService, "cinder.backup.drivers.swift.SwiftBackupDriver") @@ -182,7 +183,7 @@ func TestImport(t *testing.T) { BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", BackupURL: backupURL, } - n, err := backups.Import(client.ServiceClient(), options).Extract() + n, err := backups.Import(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") @@ -197,7 +198,7 @@ func TestResetStatus(t *testing.T) { opts := &backups.ResetStatusOpts{ Status: "error", } - res := backups.ResetStatus(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", opts) + res := backups.ResetStatus(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", opts) th.AssertNoErr(t, res.Err) } @@ -207,6 +208,6 @@ func TestForceDelete(t *testing.T) { MockForceDeleteResponse(t) - res := backups.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := backups.ForceDelete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } diff --git a/openstack/blockstorage/extensions/limits/requests.go b/openstack/blockstorage/extensions/limits/requests.go index 0fe84b4e91..8ae37cdd99 100644 --- a/openstack/blockstorage/extensions/limits/requests.go +++ b/openstack/blockstorage/extensions/limits/requests.go @@ -1,13 +1,15 @@ package limits import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // Get returns the limits about the currently scoped tenant. -func Get(client *gophercloud.ServiceClient) (r GetResult) { +func Get(ctx context.Context, client *gophercloud.ServiceClient) (r GetResult) { url := getURL(client) - resp, err := client.Get(url, &r.Body, nil) + resp, err := client.Get(ctx, url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/extensions/limits/testing/requests_test.go b/openstack/blockstorage/extensions/limits/testing/requests_test.go index 1740048a77..a097c35bcf 100644 --- a/openstack/blockstorage/extensions/limits/testing/requests_test.go +++ b/openstack/blockstorage/extensions/limits/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/limits" @@ -13,7 +14,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := limits.Get(client.ServiceClient()).Extract() + actual, err := limits.Get(context.TODO(), client.ServiceClient()).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &LimitsResult, actual) } diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/extensions/quotasets/requests.go index be1b9734eb..21fa7c8f97 100644 --- a/openstack/blockstorage/extensions/quotasets/requests.go +++ b/openstack/blockstorage/extensions/quotasets/requests.go @@ -1,42 +1,43 @@ package quotasets import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" ) // Get returns public data about a previously created QuotaSet. -func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { - resp, err := client.Get(getURL(client, projectID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, projectID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetDefaults returns public data about the project's default block storage quotas. -func GetDefaults(client *gophercloud.ServiceClient, projectID string) (r GetResult) { - resp, err := client.Get(getDefaultsURL(client, projectID), &r.Body, nil) +func GetDefaults(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(ctx, getDefaultsURL(client, projectID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetUsage returns detailed public data about a previously created QuotaSet. -func GetUsage(client *gophercloud.ServiceClient, projectID string) (r GetUsageResult) { +func GetUsage(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetUsageResult) { u := fmt.Sprintf("%s?usage=true", getURL(client, projectID)) - resp, err := client.Get(u, &r.Body, nil) + resp, err := client.Get(ctx, u, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Updates the quotas for the given projectID and returns the new QuotaSet. -func Update(client *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToBlockStorageQuotaUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -107,8 +108,8 @@ type UpdateOpts struct { } // Resets the quotas for the given tenant to their default values. -func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { - resp, err := client.Delete(updateURL(client, projectID), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { + resp, err := client.Delete(ctx, updateURL(client, projectID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go index 86bac291f3..a6a6f40ce1 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go +++ b/openstack/blockstorage/extensions/quotasets/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "errors" "testing" @@ -15,7 +16,7 @@ func TestGet(t *testing.T) { uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() + actual, err := quotasets.Get(context.TODO(), client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &getExpectedQuotaSet, actual) } @@ -26,7 +27,7 @@ func TestGetUsage(t *testing.T) { uriQueryParms := map[string]string{"usage": "true"} HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getUsageExpectedJSONBody, uriQueryParms) - actual, err := quotasets.GetUsage(client.ServiceClient(), FirstTenantID).Extract() + actual, err := quotasets.GetUsage(context.TODO(), client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, getUsageExpectedQuotaSet, actual) } @@ -37,7 +38,7 @@ func TestFullUpdate(t *testing.T) { uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, fullUpdateExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, fullUpdateOpts).Extract() + actual, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, fullUpdateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &fullUpdateExpectedQuotaSet, actual) } @@ -48,7 +49,7 @@ func TestPartialUpdate(t *testing.T) { uriQueryParms := map[string]string{} HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, partialUpdateExpectedJSONBody, uriQueryParms) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, partialUpdateOpts).Extract() + actual, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, partialUpdateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &partiualUpdateExpectedQuotaSet, actual) } @@ -64,7 +65,7 @@ func TestErrorInToBlockStorageQuotaUpdateMap(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, "", nil) - _, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() + _, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, opts).Extract() if err == nil { t.Fatal("Error handling failed") } @@ -75,6 +76,6 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := quotasets.Delete(client.ServiceClient(), FirstTenantID).ExtractErr() + err := quotasets.Delete(context.TODO(), client.ServiceClient(), FirstTenantID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go b/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go index 521cad03e2..2895a46b89 100644 --- a/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go +++ b/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerstats" @@ -15,7 +16,7 @@ func TestListStoragePoolsDetail(t *testing.T) { HandleStoragePoolsListSuccessfully(t) pages := 0 - err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{Detail: true}).EachPage(func(page pagination.Page) (bool, error) { + err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{Detail: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := schedulerstats.ExtractStoragePools(page) diff --git a/openstack/blockstorage/extensions/services/testing/requests_test.go b/openstack/blockstorage/extensions/services/testing/requests_test.go index c23b148080..407aa0723b 100644 --- a/openstack/blockstorage/extensions/services/testing/requests_test.go +++ b/openstack/blockstorage/extensions/services/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/services" @@ -15,7 +16,7 @@ func TestListServices(t *testing.T) { HandleListSuccessfully(t) pages := 0 - err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := services.ExtractServices(page) diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go index 34e400ec33..a435e6c754 100644 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ b/openstack/blockstorage/extensions/volumeactions/requests.go @@ -1,6 +1,8 @@ package volumeactions import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) @@ -41,13 +43,13 @@ func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { } // Attach will attach a volume based on the values in AttachOpts. -func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { +func Attach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { b, err := opts.ToVolumeAttachMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -55,9 +57,9 @@ func Attach(client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder } // BeginDetaching will mark the volume as detaching. -func BeginDetaching(client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { +func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -83,13 +85,13 @@ func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { } // Detach will detach a volume based on volume ID. -func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { +func Detach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { b, err := opts.ToVolumeDetachMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -97,9 +99,9 @@ func Detach(client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder } // Reserve will reserve a volume based on volume ID. -func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { +func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ReserveResult) { b := map[string]interface{}{"os-reserve": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -107,9 +109,9 @@ func Reserve(client *gophercloud.ServiceClient, id string) (r ReserveResult) { } // Unreserve will unreserve a volume based on volume ID. -func Unreserve(client *gophercloud.ServiceClient, id string) (r UnreserveResult) { +func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnreserveResult) { b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -144,13 +146,13 @@ func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[stri } // InitializeConnection initializes an iSCSI connection by volume ID. -func InitializeConnection(client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { +func InitializeConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { b, err := opts.ToVolumeInitializeConnectionMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -183,13 +185,13 @@ func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string } // TerminateConnection terminates an iSCSI connection by volume ID. -func TerminateConnection(client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { +func TerminateConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { b, err := opts.ToVolumeTerminateConnectionMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -217,13 +219,13 @@ func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, erro // ExtendSize will extend the size of the volume based on the provided information. // This operation does not return a response body. -func ExtendSize(client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { +func ExtendSize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { b, err := opts.ToVolumeExtendSizeMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -266,13 +268,13 @@ func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, er } // UploadImage will upload an image based on the values in UploadImageOptsBuilder. -func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { +func UploadImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { b, err := opts.ToVolumeUploadImageMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -280,8 +282,8 @@ func UploadImage(client *gophercloud.ServiceClient, id string, opts UploadImageO } // ForceDelete will delete the volume regardless of state. -func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - resp, err := client.Post(actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) +func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -305,13 +307,13 @@ func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, erro } // SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. -func SetImageMetadata(client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { +func SetImageMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { b, err := opts.ToImageMetadataMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -331,13 +333,13 @@ func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { } // SetBootable will set bootable status on a volume based on the values in BootableOpts -func SetBootable(client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { +func SetBootable(ctx context.Context, client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { b, err := opts.ToBootableMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -379,13 +381,13 @@ func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, erro // ChangeType will change the volume type of the volume based on the provided information. // This operation does not return a response body. -func ChangeType(client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { +func ChangeType(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { b, err := opts.ToVolumeChangeTypeMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -406,13 +408,13 @@ func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { } // ReImage will re-image a volume based on the values in ReImageOpts -func ReImage(client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { +func ReImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { b, err := opts.ToReImageMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -445,14 +447,14 @@ func (opts ResetStatusOpts) ToResetStatusMap() (map[string]interface{}, error) { // 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) { +func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { b, err := opts.ToResetStatusMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go index db3fd78d06..ac32ee2236 100644 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -21,7 +22,7 @@ func TestAttach(t *testing.T) { Mode: "rw", InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", } - err := volumeactions.Attach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + err := volumeactions.Attach(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -31,7 +32,7 @@ func TestBeginDetaching(t *testing.T) { MockBeginDetachingResponse(t) - err := volumeactions.BeginDetaching(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + err := volumeactions.BeginDetaching(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } @@ -41,7 +42,7 @@ func TestDetach(t *testing.T) { MockDetachResponse(t) - err := volumeactions.Detach(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumeactions.DetachOpts{}).ExtractErr() + err := volumeactions.Detach(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumeactions.DetachOpts{}).ExtractErr() th.AssertNoErr(t, err) } @@ -56,7 +57,7 @@ func TestUploadImage(t *testing.T) { Force: true, } - actual, err := volumeactions.UploadImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() + actual, err := volumeactions.UploadImage(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() th.AssertNoErr(t, err) expected := volumeactions.VolumeImage{ @@ -91,7 +92,7 @@ func TestReserve(t *testing.T) { MockReserveResponse(t) - err := volumeactions.Reserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + err := volumeactions.Reserve(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } @@ -101,7 +102,7 @@ func TestUnreserve(t *testing.T) { MockUnreserveResponse(t) - err := volumeactions.Unreserve(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + err := volumeactions.Unreserve(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() th.AssertNoErr(t, err) } @@ -119,7 +120,7 @@ func TestInitializeConnection(t *testing.T) { Platform: "x86_64", OSType: "linux2", } - _, err := volumeactions.InitializeConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() + _, err := volumeactions.InitializeConnection(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() th.AssertNoErr(t, err) } @@ -137,7 +138,7 @@ func TestTerminateConnection(t *testing.T) { Platform: "x86_64", OSType: "linux2", } - err := volumeactions.TerminateConnection(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + err := volumeactions.TerminateConnection(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -151,7 +152,7 @@ func TestExtendSize(t *testing.T) { NewSize: 3, } - err := volumeactions.ExtendSize(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + err := volumeactions.ExtendSize(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -161,7 +162,7 @@ func TestForceDelete(t *testing.T) { MockForceDeleteResponse(t) - res := volumeactions.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := volumeactions.ForceDelete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } @@ -177,7 +178,7 @@ func TestSetImageMetadata(t *testing.T) { }, } - err := volumeactions.SetImageMetadata(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + err := volumeactions.SetImageMetadata(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -191,7 +192,7 @@ func TestSetBootable(t *testing.T) { Bootable: true, } - err := volumeactions.SetBootable(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + err := volumeactions.SetBootable(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -206,7 +207,7 @@ func TestReImage(t *testing.T) { ReImageReserved: false, } - err := volumeactions.ReImage(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + err := volumeactions.ReImage(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -221,7 +222,7 @@ func TestChangeType(t *testing.T) { MigrationPolicy: "on-demand", } - err := volumeactions.ChangeType(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + err := volumeactions.ChangeType(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } @@ -237,6 +238,6 @@ func TestResetStatus(t *testing.T) { MigrationStatus: "migrating", } - err := volumeactions.ResetStatus(client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + err := volumeactions.ResetStatus(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/blockstorage/extensions/volumetransfers/requests.go b/openstack/blockstorage/extensions/volumetransfers/requests.go index 5686c58c70..8cdd7d152e 100644 --- a/openstack/blockstorage/extensions/volumetransfers/requests.go +++ b/openstack/blockstorage/extensions/volumetransfers/requests.go @@ -1,6 +1,8 @@ package volumetransfers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -21,13 +23,13 @@ func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { } // Create will create a volume tranfer request based on the values in CreateOpts. -func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { b, err := opts.ToCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -47,13 +49,13 @@ func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { } // Accept will accept a volume tranfer request based on the values in AcceptOpts. -func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { +func Accept(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { b, err := opts.ToAcceptMap() if err != nil { r.Err = err return } - resp, err := client.Post(acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -61,8 +63,8 @@ func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r Cr } // Delete deletes a volume transfer. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -117,8 +119,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go b/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go index 0e2e440df7..d5fc321473 100644 --- a/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go +++ b/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetransfers" @@ -14,7 +15,7 @@ func TestCreateTransfer(t *testing.T) { defer th.TeardownHTTP() HandleCreateTransfer(t) - actual, err := volumetransfers.Create(client.ServiceClient(), TransferRequest).Extract() + actual, err := volumetransfers.Create(context.TODO(), client.ServiceClient(), TransferRequest).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, TransferResponse, *actual) } @@ -24,7 +25,7 @@ func TestAcceptTransfer(t *testing.T) { defer th.TeardownHTTP() HandleAcceptTransfer(t) - actual, err := volumetransfers.Accept(client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() + actual, err := volumetransfers.Accept(context.TODO(), client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, AcceptResponse, *actual) } @@ -34,7 +35,7 @@ func TestDeleteTransfer(t *testing.T) { defer th.TeardownHTTP() HandleDeleteTransfer(t) - err := volumetransfers.Delete(client.ServiceClient(), TransferResponse.ID).ExtractErr() + err := volumetransfers.Delete(context.TODO(), client.ServiceClient(), TransferResponse.ID).ExtractErr() th.AssertNoErr(t, err) } @@ -47,7 +48,7 @@ func TestListTransfers(t *testing.T) { expectedResponse[0].AuthKey = "" count := 0 - err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { + err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := volumetransfers.ExtractTransfers(page) @@ -69,7 +70,7 @@ func TestListTransfersAllPages(t *testing.T) { expectedResponse := TransferListResponse expectedResponse[0].AuthKey = "" - allPages, err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).AllPages() + allPages, err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := volumetransfers.ExtractTransfers(allPages) th.AssertNoErr(t, err) @@ -84,7 +85,7 @@ func TestGetTransfer(t *testing.T) { expectedResponse := TransferResponse expectedResponse.AuthKey = "" - actual, err := volumetransfers.Get(client.ServiceClient(), TransferResponse.ID).Extract() + actual, err := volumetransfers.Get(context.TODO(), client.ServiceClient(), TransferResponse.ID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expectedResponse, *actual) } diff --git a/openstack/blockstorage/v1/apiversions/requests.go b/openstack/blockstorage/v1/apiversions/requests.go index d60f8ab235..c376b3307d 100644 --- a/openstack/blockstorage/v1/apiversions/requests.go +++ b/openstack/blockstorage/v1/apiversions/requests.go @@ -1,6 +1,8 @@ package apiversions import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -14,8 +16,8 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { // Get will retrieve the volume type with the provided ID. To extract the volume // type from the result, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - resp, err := client.Get(getURL(client, v), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, v string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/v1/apiversions/testing/requests_test.go b/openstack/blockstorage/v1/apiversions/testing/requests_test.go index a7bcc38cea..5af2ce0f4f 100644 --- a/openstack/blockstorage/v1/apiversions/testing/requests_test.go +++ b/openstack/blockstorage/v1/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/apiversions" @@ -17,7 +18,7 @@ func TestListVersions(t *testing.T) { count := 0 - apiversions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + apiversions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractAPIVersions(page) th.AssertNoErr(t, err) @@ -49,7 +50,7 @@ func TestAPIInfo(t *testing.T) { MockGetResponse(t) - actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() + actual, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v1").Extract() th.AssertNoErr(t, err) expected := apiversions.APIVersion{ diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go index adfa9d0693..73f16310a2 100644 --- a/openstack/blockstorage/v1/snapshots/requests.go +++ b/openstack/blockstorage/v1/snapshots/requests.go @@ -1,6 +1,8 @@ package snapshots import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -31,13 +33,13 @@ func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { // Create will create a new Snapshot based on the values in CreateOpts. To // extract the Snapshot object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSnapshotCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -45,16 +47,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing Snapshot with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -117,13 +119,13 @@ func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interfa // UpdateMetadata will update the Snapshot with provided information. To // extract the updated Snapshot from the response, call the ExtractMetadata // method on the UpdateMetadataResult. -func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { +func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToSnapshotUpdateMetadataMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v1/snapshots/testing/requests_test.go b/openstack/blockstorage/v1/snapshots/testing/requests_test.go index 3088bf002e..3f24e41238 100644 --- a/openstack/blockstorage/v1/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v1/snapshots/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -18,7 +19,7 @@ func TestList(t *testing.T) { count := 0 - snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := snapshots.ExtractSnapshots(page) if err != nil { @@ -63,7 +64,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + v, err := snapshots.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "snapshot-001") @@ -77,7 +78,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} - n, err := snapshots.Create(client.ServiceClient(), options).Extract() + n, err := snapshots.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") @@ -99,7 +100,7 @@ func TestUpdateMetadata(t *testing.T) { }, } - actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() + actual, err := snapshots.UpdateMetadata(context.TODO(), client.ServiceClient(), "123", options).ExtractMetadata() th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, expected) @@ -111,6 +112,6 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := snapshots.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } diff --git a/openstack/blockstorage/v1/snapshots/util.go b/openstack/blockstorage/v1/snapshots/util.go index e74ff98f31..a484fc3186 100644 --- a/openstack/blockstorage/v1/snapshots/util.go +++ b/openstack/blockstorage/v1/snapshots/util.go @@ -1,14 +1,16 @@ package snapshots import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go index f0dd9e5baf..8ac1b36745 100644 --- a/openstack/blockstorage/v1/volumes/requests.go +++ b/openstack/blockstorage/v1/volumes/requests.go @@ -1,6 +1,8 @@ package volumes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -35,13 +37,13 @@ func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -49,16 +51,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing Volume with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -126,13 +128,13 @@ func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v1/volumes/testing/requests_test.go b/openstack/blockstorage/v1/volumes/testing/requests_test.go index 9dc434f008..323949dfc2 100644 --- a/openstack/blockstorage/v1/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v1/volumes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -18,7 +19,7 @@ func TestList(t *testing.T) { count := 0 - volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := volumes.ExtractVolumes(page) if err != nil { @@ -53,7 +54,7 @@ func TestListAll(t *testing.T) { MockListResponse(t) - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := volumes.ExtractVolumes(allPages) th.AssertNoErr(t, err) @@ -79,7 +80,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - actual, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + actual, err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) expected := &volumes.Volume{ @@ -122,7 +123,7 @@ func TestCreate(t *testing.T) { Size: 75, AvailabilityZone: "us-east1", } - n, err := volumes.Create(client.ServiceClient(), options).Extract() + n, err := volumes.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Size, 4) @@ -135,7 +136,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := volumes.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } @@ -147,7 +148,7 @@ func TestUpdate(t *testing.T) { var name = "vol-002" options := volumes.UpdateOpts{Name: &name} - v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + v, err := volumes.Update(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) } diff --git a/openstack/blockstorage/v1/volumes/util.go b/openstack/blockstorage/v1/volumes/util.go index f596575e52..d6a76e6c80 100644 --- a/openstack/blockstorage/v1/volumes/util.go +++ b/openstack/blockstorage/v1/volumes/util.go @@ -1,14 +1,16 @@ package volumes import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/openstack/blockstorage/v1/volumetypes/requests.go b/openstack/blockstorage/v1/volumetypes/requests.go index 4343819607..292c36103c 100644 --- a/openstack/blockstorage/v1/volumetypes/requests.go +++ b/openstack/blockstorage/v1/volumetypes/requests.go @@ -1,6 +1,8 @@ package volumetypes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -26,13 +28,13 @@ func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { // Create will create a new volume. To extract the created volume type object, // call the Extract method on the CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeTypeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -40,16 +42,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the volume type with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will retrieve the volume type with the provided ID. To extract the volume // type from the result, 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/blockstorage/v1/volumetypes/testing/requests_test.go b/openstack/blockstorage/v1/volumetypes/testing/requests_test.go index d43b6b7baa..004bea24f3 100644 --- a/openstack/blockstorage/v1/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v1/volumetypes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -19,7 +20,7 @@ func TestList(t *testing.T) { count := 0 - volumetypes.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + volumetypes.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := volumetypes.ExtractVolumeTypes(page) if err != nil { @@ -58,7 +59,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - vt, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + vt, err := volumetypes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, vt.ExtraSpecs, map[string]interface{}{"serverNumber": "2"}) @@ -97,7 +98,7 @@ func TestCreate(t *testing.T) { }) options := &volumetypes.CreateOpts{Name: "vol-type-001"} - n, err := volumetypes.Create(client.ServiceClient(), options).Extract() + n, err := volumetypes.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "vol-type-001") @@ -114,6 +115,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) - err := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractErr() + err := volumetypes.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go index e6985fb296..ccf39043f0 100644 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ b/openstack/blockstorage/v2/snapshots/requests.go @@ -1,6 +1,8 @@ package snapshots import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -31,13 +33,13 @@ func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { // Create will create a new Snapshot based on the values in CreateOpts. To // extract the Snapshot object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSnapshotCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -45,16 +47,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing Snapshot with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -129,13 +131,13 @@ func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interfa // UpdateMetadata will update the Snapshot with provided information. To // extract the updated Snapshot from the response, call the ExtractMetadata // method on the UpdateMetadataResult. -func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { +func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToSnapshotUpdateMetadataMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v2/snapshots/testing/requests_test.go b/openstack/blockstorage/v2/snapshots/testing/requests_test.go index 6faa40c699..43a3fbd131 100644 --- a/openstack/blockstorage/v2/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v2/snapshots/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -18,7 +19,7 @@ func TestList(t *testing.T) { count := 0 - snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := snapshots.ExtractSnapshots(page) if err != nil { @@ -63,7 +64,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + v, err := snapshots.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "snapshot-001") @@ -77,7 +78,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} - n, err := snapshots.Create(client.ServiceClient(), options).Extract() + n, err := snapshots.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") @@ -99,7 +100,7 @@ func TestUpdateMetadata(t *testing.T) { }, } - actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() + actual, err := snapshots.UpdateMetadata(context.TODO(), client.ServiceClient(), "123", options).ExtractMetadata() th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, expected) @@ -111,6 +112,6 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := snapshots.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } diff --git a/openstack/blockstorage/v2/snapshots/util.go b/openstack/blockstorage/v2/snapshots/util.go index e74ff98f31..a484fc3186 100644 --- a/openstack/blockstorage/v2/snapshots/util.go +++ b/openstack/blockstorage/v2/snapshots/util.go @@ -1,14 +1,16 @@ package snapshots import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index 7551b465f6..7f2d5f6a55 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -1,6 +1,8 @@ package volumes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -49,13 +51,13 @@ func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -82,7 +84,7 @@ func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { } // Delete will delete the existing Volume with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, id) if opts != nil { query, err := opts.ToVolumeDeleteQuery() @@ -92,15 +94,15 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder } url += query } - resp, err := client.Delete(url, nil) + resp, err := client.Delete(ctx, url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -189,13 +191,13 @@ func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 77dd3cadf4..9324f67e40 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -19,7 +20,7 @@ func TestListWithExtensions(t *testing.T) { count := 0 - volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := volumes.ExtractVolumes(page) if err != nil { @@ -105,7 +106,7 @@ func TestListAllWithExtensions(t *testing.T) { volumetenants.VolumeTenantExt } - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) var actual []VolumeWithExt @@ -121,7 +122,7 @@ func TestListAll(t *testing.T) { MockListResponse(t) - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := volumes.ExtractVolumes(allPages) th.AssertNoErr(t, err) @@ -193,7 +194,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + v, err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "vol-001") @@ -207,7 +208,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} - n, err := volumes.Create(client.ServiceClient(), options).Extract() + n, err := volumes.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Size, 75) @@ -220,7 +221,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", volumes.DeleteOpts{}) + res := volumes.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", volumes.DeleteOpts{}) th.AssertNoErr(t, res.Err) } @@ -232,7 +233,7 @@ func TestUpdate(t *testing.T) { var name = "vol-002" options := volumes.UpdateOpts{Name: &name} - v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + v, err := volumes.Update(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) } @@ -247,11 +248,11 @@ func TestGetWithExtensions(t *testing.T) { volumes.Volume volumetenants.VolumeTenantExt } - err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) - err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) + err = volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } diff --git a/openstack/blockstorage/v2/volumes/util.go b/openstack/blockstorage/v2/volumes/util.go index f596575e52..d6a76e6c80 100644 --- a/openstack/blockstorage/v2/volumes/util.go +++ b/openstack/blockstorage/v2/volumes/util.go @@ -1,14 +1,16 @@ package volumes import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/openstack/blockstorage/v3/attachments/requests.go b/openstack/blockstorage/v3/attachments/requests.go index eb5db3711a..0d8cb7d716 100644 --- a/openstack/blockstorage/v3/attachments/requests.go +++ b/openstack/blockstorage/v3/attachments/requests.go @@ -1,6 +1,8 @@ package attachments import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -40,13 +42,13 @@ func (opts CreateOpts) ToAttachmentCreateMap() (map[string]interface{}, error) { // Create will create a new Attachment based on the values in CreateOpts. To // extract the Attachment object from the response, call the Extract method on // the CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToAttachmentCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -54,8 +56,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing Attachment with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -64,8 +66,8 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { // Get retrieves the Attachment with the provided ID. To extract the Attachment // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -153,13 +155,13 @@ func (opts UpdateOpts) ToAttachmentUpdateMap() (map[string]interface{}, error) { // Update will update the Attachment with provided information. To extract the // updated Attachment from the response, call the Extract method on the // UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToAttachmentUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -168,11 +170,11 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder // Complete will complete an attachment for a cinder volume. // Available starting in the 3.44 microversion. -func Complete(client *gophercloud.ServiceClient, id string) (r CompleteResult) { +func Complete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r CompleteResult) { b := map[string]interface{}{ "os-complete": nil, } - resp, err := client.Post(completeURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, completeURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v3/attachments/testing/requests_test.go b/openstack/blockstorage/v3/attachments/testing/requests_test.go index 09bc916ab3..9c7e399add 100644 --- a/openstack/blockstorage/v3/attachments/testing/requests_test.go +++ b/openstack/blockstorage/v3/attachments/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/attachments" @@ -14,7 +15,7 @@ func TestListAll(t *testing.T) { MockListResponse(t) - allPages, err := attachments.List(client.ServiceClient(), &attachments.ListOpts{}).AllPages() + allPages, err := attachments.List(client.ServiceClient(), &attachments.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := attachments.ExtractAttachments(allPages) th.AssertNoErr(t, err) @@ -31,7 +32,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - attachment, err := attachments.Get(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").Extract() + attachment, err := attachments.Get(context.TODO(), client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedAttachment, attachment) @@ -57,7 +58,7 @@ func TestCreate(t *testing.T) { }, VolumeUUID: "289da7f8-6440-407c-9fb4-7db01ec49164", } - attachment, err := attachments.Create(client.ServiceClient(), options).Extract() + attachment, err := attachments.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedAttachment, attachment) @@ -69,7 +70,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := attachments.Delete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a") + res := attachments.Delete(context.TODO(), client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a") th.AssertNoErr(t, res.Err) } @@ -91,7 +92,7 @@ func TestUpdate(t *testing.T) { "mode": "rw", }, } - attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() + attachment, err := attachments.Update(context.TODO(), client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedAttachment, attachment) } @@ -103,7 +104,7 @@ func TestUpdateEmpty(t *testing.T) { MockUpdateEmptyResponse(t) options := attachments.UpdateOpts{} - attachment, err := attachments.Update(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() + attachment, err := attachments.Update(context.TODO(), client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedAttachment, attachment) } @@ -114,6 +115,6 @@ func TestComplete(t *testing.T) { MockCompleteResponse(t) - err := attachments.Complete(client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").ExtractErr() + err := attachments.Complete(context.TODO(), client.ServiceClient(), "05551600-a936-4d4a-ba42-79a037c1-c91a").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/blockstorage/v3/attachments/util.go b/openstack/blockstorage/v3/attachments/util.go index b235ac0430..12409d626d 100644 --- a/openstack/blockstorage/v3/attachments/util.go +++ b/openstack/blockstorage/v3/attachments/util.go @@ -1,14 +1,16 @@ package attachments import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index e9a02e2dda..7ffacd3a6f 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -1,6 +1,8 @@ package qos import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -58,13 +60,13 @@ func (opts CreateOpts) ToQoSCreateMap() (map[string]interface{}, error) { // Create will create a new QoS based on the values in CreateOpts. To extract // the QoS object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToQoSCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -91,7 +93,7 @@ func (opts DeleteOpts) ToQoSDeleteQuery() (string, error) { } // Delete will delete the existing QoS with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, id) if opts != nil { query, err := opts.ToQoSDeleteQuery() @@ -101,7 +103,7 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder } url += query } - resp, err := client.Delete(url, nil) + resp, err := client.Delete(ctx, url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -146,8 +148,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // 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{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -197,13 +199,13 @@ func (opts UpdateOpts) ToQoSUpdateMap() (map[string]interface{}, error) { // 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) { +func Update(ctx context.Context, 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{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -226,13 +228,13 @@ func (opts DeleteKeysOpts) ToDeleteKeysCreateMap() (map[string]interface{}, erro } // DeleteKeys will delete the keys/specs from the specified QoS -func DeleteKeys(client *gophercloud.ServiceClient, qosID string, opts DeleteKeysOptsBuilder) (r DeleteResult) { +func DeleteKeys(ctx context.Context, 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{ + resp, err := client.Put(ctx, deleteKeysURL(client, qosID), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -258,7 +260,7 @@ func (opts AssociateOpts) ToQosAssociateQuery() (string, error) { } // Associate will associate a qos with a volute type -func Associate(client *gophercloud.ServiceClient, qosID string, opts AssociateOptsBuilder) (r AssociateResult) { +func Associate(ctx context.Context, client *gophercloud.ServiceClient, qosID string, opts AssociateOptsBuilder) (r AssociateResult) { url := associateURL(client, qosID) query, err := opts.ToQosAssociateQuery() if err != nil { @@ -267,7 +269,7 @@ func Associate(client *gophercloud.ServiceClient, qosID string, opts AssociateOp } url += query - resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -293,7 +295,7 @@ func (opts DisassociateOpts) ToQosDisassociateQuery() (string, error) { } // Disassociate will disassociate a qos from a volute type -func Disassociate(client *gophercloud.ServiceClient, qosID string, opts DisassociateOptsBuilder) (r DisassociateResult) { +func Disassociate(ctx context.Context, client *gophercloud.ServiceClient, qosID string, opts DisassociateOptsBuilder) (r DisassociateResult) { url := disassociateURL(client, qosID) query, err := opts.ToQosDisassociateQuery() if err != nil { @@ -302,7 +304,7 @@ func Disassociate(client *gophercloud.ServiceClient, qosID string, opts Disassoc } url += query - resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -310,8 +312,8 @@ func Disassociate(client *gophercloud.ServiceClient, qosID string, opts Disassoc } // 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{ +func DisassociateAll(ctx context.Context, client *gophercloud.ServiceClient, qosID string) (r DisassociateAllResult) { + resp, err := client.Get(ctx, disassociateAllURL(client, qosID), nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v3/qos/testing/requests_test.go b/openstack/blockstorage/v3/qos/testing/requests_test.go index 3243b718a2..be88fbd302 100644 --- a/openstack/blockstorage/v3/qos/testing/requests_test.go +++ b/openstack/blockstorage/v3/qos/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "reflect" "testing" @@ -23,7 +24,7 @@ func TestCreate(t *testing.T) { "read_iops_sec": "20000", }, } - actual, err := qos.Create(client.ServiceClient(), options).Extract() + actual, err := qos.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &createQoSExpected, actual) } @@ -34,7 +35,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := qos.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteOpts{}) + res := qos.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteOpts{}) th.AssertNoErr(t, res.Err) } @@ -45,7 +46,7 @@ func TestList(t *testing.T) { MockListResponse(t) pages := 0 - err := qos.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := qos.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := qos.ExtractQoS(page) if err != nil { @@ -80,7 +81,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - actual, err := qos.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + actual, err := qos.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &getQoSExpected, actual) } @@ -99,7 +100,7 @@ func TestUpdate(t *testing.T) { } expected := UpdateQos - actual, err := qos.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", updateOpts).Extract() + actual, err := qos.Update(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -110,7 +111,7 @@ func TestDeleteKeys(t *testing.T) { MockDeleteKeysResponse(t) - res := qos.DeleteKeys(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteKeysOpts{"read_iops_sec"}) + res := qos.DeleteKeys(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", qos.DeleteKeysOpts{"read_iops_sec"}) th.AssertNoErr(t, res.Err) } @@ -124,7 +125,7 @@ func TestAssociate(t *testing.T) { VolumeTypeID: "b596be6a-0ce9-43fa-804a-5c5e181ede76", } - res := qos.Associate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", associateOpts) + res := qos.Associate(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", associateOpts) th.AssertNoErr(t, res.Err) } @@ -138,7 +139,7 @@ func TestDisssociate(t *testing.T) { VolumeTypeID: "b596be6a-0ce9-43fa-804a-5c5e181ede76", } - res := qos.Disassociate(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", disassociateOpts) + res := qos.Disassociate(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", disassociateOpts) th.AssertNoErr(t, res.Err) } @@ -148,7 +149,7 @@ func TestDissasociateAll(t *testing.T) { MockDisassociateAllResponse(t) - res := qos.DisassociateAll(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := qos.DisassociateAll(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } @@ -166,7 +167,7 @@ func TestQosAssociationsList(t *testing.T) { }, } - allPages, err := qos.ListAssociations(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").AllPages() + allPages, err := qos.ListAssociations(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := qos.ExtractAssociations(allPages) diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 6b8357bfe6..cec3be5437 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -1,6 +1,8 @@ package snapshots import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -31,13 +33,13 @@ func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { // Create will create a new Snapshot based on the values in CreateOpts. To // extract the Snapshot object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSnapshotCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -45,16 +47,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing Snapshot with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Snapshot with the provided ID. To extract the Snapshot // object from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -142,13 +144,13 @@ func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { // Update will update the Snapshot with provided information. To extract the updated // Snapshot from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSnapshotUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -177,13 +179,13 @@ func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interfa // UpdateMetadata will update the Snapshot with provided information. To // extract the updated Snapshot from the response, call the ExtractMetadata // method on the UpdateMetadataResult. -func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { +func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToSnapshotUpdateMetadataMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -212,14 +214,14 @@ func (opts ResetStatusOpts) ToSnapshotResetStatusMap() (map[string]interface{}, // 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) { +func ResetStatus(ctx context.Context, 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{ + resp, err := client.Post(ctx, resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -250,14 +252,14 @@ func (opts UpdateStatusOpts) ToSnapshotUpdateStatusMap() (map[string]interface{} // UpdateStatus will update 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) { +func UpdateStatus(ctx context.Context, 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{ + resp, err := client.Post(ctx, resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -266,11 +268,11 @@ func UpdateStatus(client *gophercloud.ServiceClient, id string, opts UpdateStatu // 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) { +func ForceDelete(ctx context.Context, 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{ + resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v3/snapshots/testing/requests_test.go b/openstack/blockstorage/v3/snapshots/testing/requests_test.go index 1cc4e17a73..ddca839153 100644 --- a/openstack/blockstorage/v3/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -18,7 +19,7 @@ func TestList(t *testing.T) { count := 0 - snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := snapshots.ExtractSnapshots(page) if err != nil { @@ -63,7 +64,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - v, err := snapshots.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + v, err := snapshots.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "snapshot-001") @@ -77,7 +78,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} - n, err := snapshots.Create(client.ServiceClient(), options).Extract() + n, err := snapshots.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.VolumeID, "1234") @@ -99,7 +100,7 @@ func TestUpdateMetadata(t *testing.T) { }, } - actual, err := snapshots.UpdateMetadata(client.ServiceClient(), "123", options).ExtractMetadata() + actual, err := snapshots.UpdateMetadata(context.TODO(), client.ServiceClient(), "123", options).ExtractMetadata() th.AssertNoErr(t, err) th.AssertDeepEquals(t, actual, expected) @@ -111,7 +112,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := snapshots.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := snapshots.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } @@ -124,7 +125,7 @@ func TestUpdate(t *testing.T) { var name = "snapshot-002" var description = "Daily backup 002" options := snapshots.UpdateOpts{Name: &name, Description: &description} - v, err := snapshots.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + v, err := snapshots.Update(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "snapshot-002", v.Name) th.CheckEquals(t, "Daily backup 002", v.Description) @@ -139,7 +140,7 @@ func TestResetStatus(t *testing.T) { opts := &snapshots.ResetStatusOpts{ Status: "error", } - res := snapshots.ResetStatus(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", opts) + res := snapshots.ResetStatus(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", opts) th.AssertNoErr(t, res.Err) } @@ -153,7 +154,7 @@ func TestUpdateStatus(t *testing.T) { Status: "error", Progress: "80%", } - res := snapshots.UpdateStatus(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", opts) + res := snapshots.UpdateStatus(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", opts) th.AssertNoErr(t, res.Err) } @@ -163,6 +164,6 @@ func TestForceDelete(t *testing.T) { MockForceDeleteResponse(t) - res := snapshots.ForceDelete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := snapshots.ForceDelete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } diff --git a/openstack/blockstorage/v3/snapshots/util.go b/openstack/blockstorage/v3/snapshots/util.go index e74ff98f31..a484fc3186 100644 --- a/openstack/blockstorage/v3/snapshots/util.go +++ b/openstack/blockstorage/v3/snapshots/util.go @@ -1,14 +1,16 @@ package snapshots import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index a989039548..39c12be630 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -1,6 +1,8 @@ package volumes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -52,13 +54,13 @@ func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -85,7 +87,7 @@ func (opts DeleteOpts) ToVolumeDeleteQuery() (string, error) { } // Delete will delete the existing Volume with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, id) if opts != nil { query, err := opts.ToVolumeDeleteQuery() @@ -95,15 +97,15 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder } url += query } - resp, err := client.Delete(url, nil) + resp, err := client.Delete(ctx, url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -192,13 +194,13 @@ func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { // Update will update the Volume with provided information. To extract the updated // Volume from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index bb11d09ce1..beb934d09e 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -20,7 +21,7 @@ func TestListWithExtensions(t *testing.T) { count := 0 - volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := volumes.ExtractVolumes(page) if err != nil { @@ -107,7 +108,7 @@ func TestListAllWithExtensions(t *testing.T) { volumehost.VolumeHostExt } - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) var actual []VolumeWithExt @@ -125,7 +126,7 @@ func TestListAll(t *testing.T) { MockListResponse(t) - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages() + allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := volumes.ExtractVolumes(allPages) th.AssertNoErr(t, err) @@ -197,7 +198,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - v, err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + v, err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "vol-001") @@ -211,7 +212,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} - n, err := volumes.Create(client.ServiceClient(), options).Extract() + n, err := volumes.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Size, 75) @@ -224,7 +225,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := volumes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", volumes.DeleteOpts{}) + res := volumes.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", volumes.DeleteOpts{}) th.AssertNoErr(t, res.Err) } @@ -236,7 +237,7 @@ func TestUpdate(t *testing.T) { var name = "vol-002" options := volumes.UpdateOpts{Name: &name} - v, err := volumes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + v, err := volumes.Update(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-002", v.Name) } @@ -251,12 +252,12 @@ func TestGetWithExtensions(t *testing.T) { volumes.Volume volumetenants.VolumeTenantExt } - err := volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) th.AssertEquals(t, "centos", s.Volume.VolumeImageMetadata["image_name"]) - err = volumes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) + err = volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } @@ -273,7 +274,7 @@ func TestCreateFromBackup(t *testing.T) { BackupID: "20c792f0-bb03-434f-b653-06ef238e337e", } - v, err := volumes.Create(client.ServiceClient(), options).Extract() + v, err := volumes.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Size, 30) diff --git a/openstack/blockstorage/v3/volumes/util.go b/openstack/blockstorage/v3/volumes/util.go index f596575e52..d6a76e6c80 100644 --- a/openstack/blockstorage/v3/volumes/util.go +++ b/openstack/blockstorage/v3/volumes/util.go @@ -1,14 +1,16 @@ package volumes import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // WaitForStatus will continually poll the resource, checking for a particular // status. It will do this for the amount of seconds defined. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 8a9bd20a32..7b5a750af8 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -1,6 +1,8 @@ package volumetypes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -34,13 +36,13 @@ func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { // Create will create a new Volume Type based on the values in CreateOpts. To extract // the Volume Type object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeTypeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -48,16 +50,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing Volume Type with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume Type with the provided ID. To extract the Volume Type 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -128,13 +130,13 @@ func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { // Update will update the Volume Type with provided information. To extract the updated // Volume Type from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVolumeTypeUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -142,15 +144,15 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // ListExtraSpecs requests all the extra-specs for the given volume type ID. -func ListExtraSpecs(client *gophercloud.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { - resp, err := client.Get(extraSpecsListURL(client, volumeTypeID), &r.Body, nil) +func ListExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, volumeTypeID string) (r ListExtraSpecsResult) { + resp, err := client.Get(ctx, extraSpecsListURL(client, volumeTypeID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetExtraSpec requests an extra-spec specified by key for the given volume type ID -func GetExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { - resp, err := client.Get(extraSpecsGetURL(client, volumeTypeID, key), &r.Body, nil) +func GetExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, volumeTypeID string, key string) (r GetExtraSpecResult) { + resp, err := client.Get(ctx, extraSpecsGetURL(client, volumeTypeID, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -172,13 +174,13 @@ func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]interfa // CreateExtraSpecs will create or update the extra-specs key-value pairs for // the specified volume type. -func CreateExtraSpecs(client *gophercloud.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { +func CreateExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, volumeTypeID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { b, err := opts.ToVolumeTypeExtraSpecsCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(extraSpecsCreateURL(client, volumeTypeID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, extraSpecsCreateURL(client, volumeTypeID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -211,13 +213,13 @@ func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecUpdateMap() (map[string]string, // UpdateExtraSpec will updates the value of the specified volume type's extra spec // for the key in opts. -func UpdateExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { +func UpdateExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, volumeTypeID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { b, key, err := opts.ToVolumeTypeExtraSpecUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(extraSpecUpdateURL(client, volumeTypeID, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, extraSpecUpdateURL(client, volumeTypeID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -226,8 +228,8 @@ func UpdateExtraSpec(client *gophercloud.ServiceClient, volumeTypeID string, opt // DeleteExtraSpec will delete the key-value pair with the given key for the given // volume type ID. -func DeleteExtraSpec(client *gophercloud.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { - resp, err := client.Delete(extraSpecDeleteURL(client, volumeTypeID, key), &gophercloud.RequestOpts{ +func DeleteExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, volumeTypeID, key string) (r DeleteExtraSpecResult) { + resp, err := client.Delete(ctx, extraSpecDeleteURL(client, volumeTypeID, key), &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -261,13 +263,13 @@ func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]interface{}, er } // AddAccess grants a tenant/project access to a volume type. -func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { +func AddAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { b, err := opts.ToVolumeTypeAddAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -292,13 +294,13 @@ func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]interface } // RemoveAccess removes/revokes a tenant/project access to a volume type. -func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { +func RemoveAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { b, err := opts.ToVolumeTypeRemoveAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, accessActionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -334,13 +336,13 @@ func (opts CreateEncryptionOpts) ToEncryptionCreateMap() (map[string]interface{} // 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) { +func CreateEncryption(ctx context.Context, 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{ + resp, err := client.Post(ctx, createEncryptionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -348,22 +350,22 @@ func CreateEncryption(client *gophercloud.ServiceClient, id string, opts CreateE } // 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) +func DeleteEncryption(ctx context.Context, client *gophercloud.ServiceClient, id, encryptionID string) (r DeleteEncryptionResult) { + resp, err := client.Delete(ctx, 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) +func GetEncryption(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetEncryptionResult) { + resp, err := client.Get(ctx, 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) +func GetEncryptionSpec(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r GetEncryptionSpecResult) { + resp, err := client.Get(ctx, getEncryptionSpecURL(client, id, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -397,13 +399,13 @@ 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(ctx context.Context, 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{ + resp, err := client.Put(ctx, updateEncryptionURL(client, id, encryptionID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 846817208b..56f22632f9 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "reflect" @@ -18,7 +19,7 @@ func TestListAll(t *testing.T) { MockListResponse(t) pages := 0 - err := volumetypes.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := volumetypes.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := volumetypes.ExtractVolumeTypes(page) if err != nil { @@ -56,7 +57,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - v, err := volumetypes.Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + v, err := volumetypes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Name, "vol-type-001") @@ -81,7 +82,7 @@ func TestCreate(t *testing.T) { ExtraSpecs: map[string]string{"capabilities": "gpu"}, } - n, err := volumetypes.Create(client.ServiceClient(), options).Extract() + n, err := volumetypes.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "test_type") @@ -98,7 +99,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := volumetypes.Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + res := volumetypes.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertNoErr(t, res.Err) } @@ -115,7 +116,7 @@ func TestUpdate(t *testing.T) { IsPublic: &isPublic, } - v, err := volumetypes.Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + v, err := volumetypes.Update(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() th.AssertNoErr(t, err) th.CheckEquals(t, "vol-type-002", v.Name) th.CheckEquals(t, true, v.IsPublic) @@ -127,7 +128,7 @@ func TestVolumeTypeExtraSpecsList(t *testing.T) { HandleExtraSpecsListSuccessfully(t) expected := ExtraSpecs - actual, err := volumetypes.ListExtraSpecs(client.ServiceClient(), "1").Extract() + actual, err := volumetypes.ListExtraSpecs(context.TODO(), client.ServiceClient(), "1").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -138,7 +139,7 @@ func TestVolumeTypeExtraSpecGet(t *testing.T) { HandleExtraSpecGetSuccessfully(t) expected := ExtraSpec - actual, err := volumetypes.GetExtraSpec(client.ServiceClient(), "1", "capabilities").Extract() + actual, err := volumetypes.GetExtraSpec(context.TODO(), client.ServiceClient(), "1", "capabilities").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -153,7 +154,7 @@ func TestVolumeTypeExtraSpecsCreate(t *testing.T) { "volume_backend_name": "ssd", } expected := ExtraSpecs - actual, err := volumetypes.CreateExtraSpecs(client.ServiceClient(), "1", createOpts).Extract() + actual, err := volumetypes.CreateExtraSpecs(context.TODO(), client.ServiceClient(), "1", createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -167,7 +168,7 @@ func TestVolumeTypeExtraSpecUpdate(t *testing.T) { "capabilities": "gpu-2", } expected := UpdatedExtraSpec - actual, err := volumetypes.UpdateExtraSpec(client.ServiceClient(), "1", updateOpts).Extract() + actual, err := volumetypes.UpdateExtraSpec(context.TODO(), client.ServiceClient(), "1", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -177,7 +178,7 @@ func TestVolumeTypeExtraSpecDelete(t *testing.T) { defer th.TeardownHTTP() HandleExtraSpecDeleteSuccessfully(t) - res := volumetypes.DeleteExtraSpec(client.ServiceClient(), "1", "capabilities") + res := volumetypes.DeleteExtraSpec(context.TODO(), client.ServiceClient(), "1", "capabilities") th.AssertNoErr(t, res.Err) } @@ -208,7 +209,7 @@ func TestVolumeTypeListAccesses(t *testing.T) { }, } - allPages, err := volumetypes.ListAccesses(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b").AllPages() + allPages, err := volumetypes.ListAccesses(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := volumetypes.ExtractAccesses(allPages) @@ -243,7 +244,7 @@ func TestVolumeTypeAddAccess(t *testing.T) { Project: "6f70656e737461636b20342065766572", } - err := volumetypes.AddAccess(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", addAccessOpts).ExtractErr() + err := volumetypes.AddAccess(context.TODO(), client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", addAccessOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -272,7 +273,7 @@ func TestVolumeTypeRemoveAccess(t *testing.T) { Project: "6f70656e737461636b20342065766572", } - err := volumetypes.RemoveAccess(client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", removeAccessOpts).ExtractErr() + err := volumetypes.RemoveAccess(context.TODO(), client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", removeAccessOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -290,7 +291,7 @@ func TestCreateEncryption(t *testing.T) { Cipher: "aes-xts-plain64", } id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" - n, err := volumetypes.CreateEncryption(client.ServiceClient(), id, options).Extract() + n, err := volumetypes.CreateEncryption(context.TODO(), client.ServiceClient(), id, options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "a5082c24-2a27-43a4-b48e-fcec1240e36b", n.VolumeTypeID) @@ -307,7 +308,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(context.TODO(), client.ServiceClient(), "a5082c24-2a27-43a4-b48e-fcec1240e36b", "81e069c6-7394-4856-8df7-3b237ca61f74") th.AssertNoErr(t, res.Err) } @@ -325,7 +326,7 @@ func TestUpdateEncryption(t *testing.T) { } id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" encryptionID := "81e069c6-7394-4856-8df7-3b237ca61f74" - n, err := volumetypes.UpdateEncryption(client.ServiceClient(), id, encryptionID, options).Extract() + n, err := volumetypes.UpdateEncryption(context.TODO(), client.ServiceClient(), id, encryptionID, options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "front-end", n.ControlLocation) @@ -340,7 +341,7 @@ func TestGetEncryption(t *testing.T) { MockEncryptionGetResponse(t) id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" - n, err := volumetypes.GetEncryption(client.ServiceClient(), id).Extract() + n, err := volumetypes.GetEncryption(context.TODO(), client.ServiceClient(), id).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "a5082c24-2a27-43a4-b48e-fcec1240e36b", n.VolumeTypeID) @@ -361,7 +362,7 @@ func TestGetEncryptionSpec(t *testing.T) { MockEncryptionGetSpecResponse(t) id := "a5082c24-2a27-43a4-b48e-fcec1240e36b" - n, err := volumetypes.GetEncryptionSpec(client.ServiceClient(), id, "cipher").Extract() + n, err := volumetypes.GetEncryptionSpec(context.TODO(), client.ServiceClient(), id, "cipher").Extract() th.AssertNoErr(t, err) key := "cipher" diff --git a/openstack/client.go b/openstack/client.go index ad5026089d..ca3007be07 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -53,7 +53,7 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { return p, nil } -// AuthenticatedClientWithContext logs in to an OpenStack cloud found at the identity endpoint +// 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. // @@ -67,37 +67,32 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { // Example: // // ao, err := openstack.AuthOptionsFromEnv() -// provider, err := openstack.AuthenticatedClientWithContext(ctx, ao) +// provider, err := openstack.AuthenticatedClient(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) { +func AuthenticatedClient(ctx context.Context, options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { client, err := NewClient(options.IdentityEndpoint) if err != nil { return nil, err } - err = AuthenticateWithContext(ctx, client, options) + err = Authenticate(ctx, 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 +// Authenticate 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 { +func Authenticate(ctx context.Context, client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { versions := []*utils.Version{ {ID: v2, Priority: 20, Suffix: "/v2.0/"}, {ID: v3, Priority: 30, Suffix: "/v3/"}, } - chosen, endpoint, err := utils.ChooseVersion(client, versions) + chosen, endpoint, err := utils.ChooseVersion(ctx, client, versions) if err != nil { return err } @@ -113,21 +108,11 @@ func AuthenticateWithContext(ctx context.Context, client *gophercloud.ProviderCl } } -// 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 { +// AuthenticateV2 explicitly authenticates against the identity v2 endpoint. +func AuthenticateV2(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 AuthenticateV2WithContext(context.Background(), client, options, eo) -} - 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 { @@ -148,7 +133,7 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st TokenID: options.TokenID, } - result := tokens2.CreateWithContext(ctx, v2Client, v2Opts) + result := tokens2.Create(ctx, v2Client, v2Opts) err = client.SetTokenAndAuthResult(result) if err != nil { @@ -186,16 +171,11 @@ 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 { +// AuthenticateV3 explicitly authenticates against the identity v3 service. +func AuthenticateV3(ctx context.Context, client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { return v3auth(ctx, client, "", options, eo) } -// AuthenticateV3 is a compatibility wrapper around AuthenticateV3WithContext -func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { - return AuthenticateV3WithContext(context.Background(), client, options, eo) -} - func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { // Override the generated service endpoint with the one returned by the version endpoint. v3Client, err := NewIdentityV3(client, eo) @@ -228,7 +208,7 @@ func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st } v3Client.SetToken(tokenID) - result := tokens3.Get(v3Client, tokenID) + result := tokens3.Get(ctx, v3Client, tokenID) if result.Err != nil { return result.Err } @@ -246,11 +226,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(ctx, v3Client, opts) case *oauth1.AuthOptions: - result = oauth1.CreateWithContext(ctx, v3Client, opts) + result = oauth1.Create(ctx, v3Client, opts) default: - result = tokens3.CreateWithContext(ctx, v3Client, opts) + result = tokens3.Create(ctx, v3Client, opts) } err = client.SetTokenAndAuthResult(result) diff --git a/openstack/clustering/v1/actions/requests.go b/openstack/clustering/v1/actions/requests.go index efe3e3efd2..7165f63978 100644 --- a/openstack/clustering/v1/actions/requests.go +++ b/openstack/clustering/v1/actions/requests.go @@ -1,6 +1,8 @@ package actions import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -45,8 +47,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details of a single action. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/actions/testing/requests_test.go b/openstack/clustering/v1/actions/testing/requests_test.go index bd75a24109..0fa6ca0e07 100644 --- a/openstack/clustering/v1/actions/testing/requests_test.go +++ b/openstack/clustering/v1/actions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" @@ -16,7 +17,7 @@ func TestListActions(t *testing.T) { HandleListSuccessfully(t) pageCount := 0 - err := actions.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := actions.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pageCount++ actual, err := actions.ExtractActions(page) th.AssertNoErr(t, err) @@ -38,7 +39,7 @@ func TestGetAction(t *testing.T) { HandleGetSuccessfully(t, ExpectedAction1.ID) - actual, err := actions.Get(fake.ServiceClient(), ExpectedAction1.ID).Extract() + actual, err := actions.Get(context.TODO(), fake.ServiceClient(), ExpectedAction1.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedAction1, *actual) } diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go index bc5c479419..56ac847345 100644 --- a/openstack/clustering/v1/clusters/requests.go +++ b/openstack/clustering/v1/clusters/requests.go @@ -1,6 +1,7 @@ package clusters import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -49,13 +50,13 @@ func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new cluster. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToClusterCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -63,8 +64,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get retrieves details of a single cluster. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -136,13 +137,13 @@ func (opts UpdateOpts) ToClusterUpdateMap() (map[string]interface{}, error) { } // Update will update an existing cluster. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToClusterUpdateMap() if err != nil { r.Err = err return r } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -150,8 +151,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Delete deletes the specified cluster ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -192,13 +193,13 @@ func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "resize") } -func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { +func Resize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { b, err := opts.ToClusterResizeMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -222,13 +223,13 @@ func (opts ScaleInOpts) ToClusterScaleInMap() (map[string]interface{}, error) { } // ScaleIn will reduce the capacity of a cluster. -func ScaleIn(client *gophercloud.ServiceClient, id string, opts ScaleInOptsBuilder) (r ActionResult) { +func ScaleIn(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ScaleInOptsBuilder) (r ActionResult) { b, err := opts.ToClusterScaleInMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -252,13 +253,13 @@ func (opts ScaleOutOpts) ToClusterScaleOutMap() (map[string]interface{}, error) } // ScaleOut will increase the capacity of a cluster. -func ScaleOut(client *gophercloud.ServiceClient, id string, opts ScaleOutOptsBuilder) (r ActionResult) { +func ScaleOut(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ScaleOutOptsBuilder) (r ActionResult) { b, err := opts.ToClusterScaleOutMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -283,13 +284,13 @@ func (opts AttachPolicyOpts) ToClusterAttachPolicyMap() (map[string]interface{}, } // Attach Policy will attach a policy to a cluster. -func AttachPolicy(client *gophercloud.ServiceClient, id string, opts AttachPolicyOptsBuilder) (r ActionResult) { +func AttachPolicy(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachPolicyOptsBuilder) (r ActionResult) { b, err := opts.ToClusterAttachPolicyMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -314,13 +315,13 @@ func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap() (map[string]interface{}, } // UpdatePolicy will update a cluster's policy. -func UpdatePolicy(client *gophercloud.ServiceClient, id string, opts UpdatePolicyOptsBuilder) (r ActionResult) { +func UpdatePolicy(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdatePolicyOptsBuilder) (r ActionResult) { b, err := opts.ToClusterUpdatePolicyMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -344,13 +345,13 @@ func (opts DetachPolicyOpts) ToClusterDetachPolicyMap() (map[string]interface{}, } // DetachPolicy will detach a policy from a cluster. -func DetachPolicy(client *gophercloud.ServiceClient, id string, opts DetachPolicyOptsBuilder) (r ActionResult) { +func DetachPolicy(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachPolicyOptsBuilder) (r ActionResult) { b, err := opts.ToClusterDetachPolicyMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -394,8 +395,8 @@ func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts List } // GetPolicy retrieves details of a cluster policy. -func GetPolicy(client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetPolicyResult) { - resp, err := client.Get(getPolicyURL(client, clusterID, policyID), &r.Body, nil) +func GetPolicy(ctx context.Context, client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetPolicyResult) { + resp, err := client.Get(ctx, getPolicyURL(client, clusterID, policyID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -419,13 +420,13 @@ func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { } // Recover implements cluster recover request. -func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOptsBuilder) (r ActionResult) { +func Recover(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RecoverOptsBuilder) (r ActionResult) { b, err := opts.ToClusterRecoverMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -433,12 +434,12 @@ func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOptsBuild } // Check will perform a health check on a cluster. -func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { +func Check(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { b := map[string]interface{}{ "check": map[string]interface{}{}, } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -454,14 +455,14 @@ type CompleteLifecycleOpts struct { LifecycleActionTokenID string `json:"lifecycle_action_token" required:"true"` } -func CompleteLifecycle(client *gophercloud.ServiceClient, id string, opts CompleteLifecycleOpts) (r ActionResult) { +func CompleteLifecycle(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CompleteLifecycleOpts) (r ActionResult) { b, err := opts.ToClusterCompleteLifecycleMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -476,13 +477,13 @@ type AddNodesOpts struct { Nodes []string `json:"nodes" required:"true"` } -func AddNodes(client *gophercloud.ServiceClient, id string, opts AddNodesOpts) (r ActionResult) { +func AddNodes(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AddNodesOpts) (r ActionResult) { b, err := opts.ToClusterAddNodeMap() if err != nil { r.Err = err return } - resp, err := client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -497,13 +498,13 @@ type RemoveNodesOpts struct { Nodes []string `json:"nodes" required:"true"` } -func RemoveNodes(client *gophercloud.ServiceClient, clusterID string, opts RemoveNodesOpts) (r ActionResult) { +func RemoveNodes(ctx context.Context, client *gophercloud.ServiceClient, clusterID string, opts RemoveNodesOpts) (r ActionResult) { b, err := opts.ToClusterRemoveNodeMap() if err != nil { r.Err = err return } - resp, err := client.Post(nodeURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, nodeURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -518,13 +519,13 @@ type ReplaceNodesOpts struct { Nodes map[string]string `json:"nodes" required:"true"` } -func ReplaceNodes(client *gophercloud.ServiceClient, id string, opts ReplaceNodesOpts) (r ActionResult) { +func ReplaceNodes(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ReplaceNodesOpts) (r ActionResult) { b, err := opts.ToClusterReplaceNodeMap() if err != nil { r.Err = err return } - resp, err := client.Post(nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -545,13 +546,13 @@ func (opts CollectOpts) ToClusterCollectMap() (string, error) { } // Collect instructs OpenStack to aggregate attribute values across a cluster -func Collect(client *gophercloud.ServiceClient, id string, opts CollectOptsBuilder) (r CollectResult) { +func Collect(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CollectOptsBuilder) (r CollectResult) { query, err := opts.ToClusterCollectMap() if err != nil { r.Err = err return } - resp, err := client.Get(collectURL(client, id, query), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, collectURL(client, id, query), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -610,13 +611,13 @@ type OperationOpts struct { Params OperationParams `json:"params,omitempty"` } -func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { +func Ops(ctx context.Context, client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { b, err := opts.ToClusterOperationMap() if err != nil { r.Err = err return } - resp, err := client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go index bc48af2f6d..a21dff9051 100644 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ b/openstack/clustering/v1/clusters/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "strings" "testing" @@ -28,7 +29,7 @@ func TestCreateCluster(t *testing.T) { Config: map[string]interface{}{}, } - res := clusters.Create(fake.ServiceClient(), opts) + res := clusters.Create(context.TODO(), fake.ServiceClient(), opts) th.AssertNoErr(t, res.Err) location := res.Header.Get("Location") @@ -61,7 +62,7 @@ func TestCreateClusterEmptyTime(t *testing.T) { Config: map[string]interface{}{}, } - actual, err := clusters.Create(fake.ServiceClient(), opts).Extract() + actual, err := clusters.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } @@ -92,7 +93,7 @@ func TestCreateClusterMetadata(t *testing.T) { Config: map[string]interface{}{}, } - actual, err := clusters.Create(fake.ServiceClient(), opts).Extract() + actual, err := clusters.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster_Metadata, *actual) } @@ -103,7 +104,7 @@ func TestGetCluster(t *testing.T) { HandleGetClusterSuccessfully(t) - actual, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + actual, err := clusters.Get(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster, *actual) } @@ -114,7 +115,7 @@ func TestGetClusterEmptyTime(t *testing.T) { HandleGetClusterEmptyTimeSuccessfully(t) - actual, err := clusters.Get(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + actual, err := clusters.Get(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } @@ -127,7 +128,7 @@ func TestListClusters(t *testing.T) { count := 0 - clusters.List(fake.ServiceClient(), clusters.ListOpts{GlobalProject: new(bool)}).EachPage(func(page pagination.Page) (bool, error) { + clusters.List(fake.ServiceClient(), clusters.ListOpts{GlobalProject: new(bool)}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := clusters.ExtractClusters(page) th.AssertNoErr(t, err) @@ -152,7 +153,7 @@ func TestUpdateCluster(t *testing.T) { ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", } - actual, err := clusters.Update(fake.ServiceClient(), ExpectedCluster.ID, updateOpts).Extract() + actual, err := clusters.Update(context.TODO(), fake.ServiceClient(), ExpectedCluster.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster, *actual) } @@ -168,7 +169,7 @@ func TestUpdateClusterEmptyTime(t *testing.T) { ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", } - actual, err := clusters.Update(fake.ServiceClient(), ExpectedCluster_EmptyTime.ID, updateOpts).Extract() + actual, err := clusters.Update(context.TODO(), fake.ServiceClient(), ExpectedCluster_EmptyTime.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) } @@ -179,7 +180,7 @@ func TestDeleteCluster(t *testing.T) { HandleDeleteClusterSuccessfully(t) - err := clusters.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").ExtractErr() + err := clusters.Delete(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").ExtractErr() th.AssertNoErr(t, err) } @@ -201,7 +202,7 @@ func TestResizeCluster(t *testing.T) { Strict: &strict, } - actionID, err := clusters.Resize(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + actionID, err := clusters.Resize(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -285,7 +286,7 @@ func TestClusterScaleIn(t *testing.T) { scaleOpts := clusters.ScaleInOpts{ Count: &count, } - actionID, err := clusters.ScaleIn(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOpts).Extract() + actionID, err := clusters.ScaleIn(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -297,7 +298,7 @@ func TestListClusterPolicies(t *testing.T) { HandleListPoliciesSuccessfully(t) pageCount := 0 - err := clusters.ListPolicies(fake.ServiceClient(), ExpectedClusterPolicy.ClusterID, clusters.ListPoliciesOpts{Name: "Test"}).EachPage(func(page pagination.Page) (bool, error) { + err := clusters.ListPolicies(fake.ServiceClient(), ExpectedClusterPolicy.ClusterID, clusters.ListPoliciesOpts{Name: "Test"}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pageCount++ actual, err := clusters.ExtractClusterPolicies(page) th.AssertNoErr(t, err) @@ -316,7 +317,7 @@ func TestGetClusterPolicies(t *testing.T) { HandleGetPolicySuccessfully(t) - actual, err := clusters.GetPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", "714fe676-a08f-4196-b7af-61d52eeded15").Extract() + actual, err := clusters.GetPolicy(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", "714fe676-a08f-4196-b7af-61d52eeded15").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedClusterPolicy, *actual) } @@ -332,7 +333,7 @@ func TestClusterRecover(t *testing.T) { Check: new(bool), CheckCapacity: new(bool), } - actionID, err := clusters.Recover(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() + actionID, err := clusters.Recover(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -348,7 +349,7 @@ func TestAttachPolicy(t *testing.T) { PolicyID: "policy1", Enabled: &enabled, } - actionID, err := clusters.AttachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + actionID, err := clusters.AttachPolicy(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -362,7 +363,7 @@ func TestDetachPolicy(t *testing.T) { opts := clusters.DetachPolicyOpts{ PolicyID: "policy1", } - actionID, err := clusters.DetachPolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + actionID, err := clusters.DetachPolicy(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -378,7 +379,7 @@ func TestUpdatePolicy(t *testing.T) { PolicyID: "policy1", Enabled: &enabled, } - actionID, err := clusters.UpdatePolicy(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + actionID, err := clusters.UpdatePolicy(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -392,7 +393,7 @@ func TestClusterScaleOut(t *testing.T) { scaleOutOpts := clusters.ScaleOutOpts{ Count: 5, } - actionID, err := clusters.ScaleOut(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOutOpts).Extract() + actionID, err := clusters.ScaleOut(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOutOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -403,7 +404,7 @@ func TestClusterCheck(t *testing.T) { HandleCheckSuccessfully(t) - actionID, err := clusters.Check(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() + actionID, err := clusters.Check(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -418,7 +419,7 @@ func TestLifecycle(t *testing.T) { LifecycleActionTokenID: "976528c6-dcf6-4d8d-9f4c-588f4e675f29", } - res := clusters.CompleteLifecycle(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", opts) + res := clusters.CompleteLifecycle(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", opts) location := res.Header.Get("Location") th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/2a0ff107-e789-4660-a122-3816c43af703", location) @@ -436,7 +437,7 @@ func TestAddNodes(t *testing.T) { opts := clusters.AddNodesOpts{ Nodes: []string{"node1", "node2", "node3"}, } - result, err := clusters.AddNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + result, err := clusters.AddNodes(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") } @@ -450,7 +451,7 @@ func TestRemoveNodes(t *testing.T) { opts := clusters.RemoveNodesOpts{ Nodes: []string{"node1", "node2", "node3"}, } - result, err := clusters.RemoveNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + result, err := clusters.RemoveNodes(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") } @@ -462,7 +463,7 @@ func TestReplaceNodes(t *testing.T) { opts := clusters.ReplaceNodesOpts{ Nodes: map[string]string{"node-1234": "node-5678"}, } - actionID, err := clusters.ReplaceNodes(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + actionID, err := clusters.ReplaceNodes(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") } @@ -474,7 +475,7 @@ func TestClusterCollect(t *testing.T) { opts := clusters.CollectOpts{ Path: "foo.bar", } - attributes, err := clusters.Collect(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() + attributes, err := clusters.Collect(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCollectAttributes, attributes) } @@ -490,7 +491,7 @@ func TestOperation(t *testing.T) { Filters: clusters.OperationFilters{"role": "slave"}, Params: clusters.OperationParams{"type": "soft"}, } - actual, err := clusters.Ops(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", clusterOpts).Extract() + actual, err := clusters.Ops(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", clusterOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, OperationExpectedActionID, actual) } diff --git a/openstack/clustering/v1/events/requests.go b/openstack/clustering/v1/events/requests.go index 720c20e2b7..164a34efae 100644 --- a/openstack/clustering/v1/events/requests.go +++ b/openstack/clustering/v1/events/requests.go @@ -1,6 +1,8 @@ package events import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -47,8 +49,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details of a single event. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/clustering/v1/events/testing/requests_test.go b/openstack/clustering/v1/events/testing/requests_test.go index 86bf6eed07..592aaa81ea 100644 --- a/openstack/clustering/v1/events/testing/requests_test.go +++ b/openstack/clustering/v1/events/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/events" @@ -17,7 +18,7 @@ func TestListEvents(t *testing.T) { HandleListSuccessfully(t) pageCount := 0 - err := events.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := events.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pageCount++ actual, err := events.ExtractEvents(page) th.AssertNoErr(t, err) @@ -39,7 +40,7 @@ func TestGetEvent(t *testing.T) { HandleGetSuccessfully(t, ExpectedEvent1.ID) - actual, err := events.Get(fake.ServiceClient(), ExpectedEvent1.ID).Extract() + actual, err := events.Get(context.TODO(), fake.ServiceClient(), ExpectedEvent1.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedEvent1, *actual) } diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go index f12d43e671..b8c8c28130 100644 --- a/openstack/clustering/v1/nodes/requests.go +++ b/openstack/clustering/v1/nodes/requests.go @@ -1,6 +1,8 @@ package nodes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -26,13 +28,13 @@ func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new node. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToNodeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -59,13 +61,13 @@ func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) { } // Update requests the update of a node. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToNodeUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -112,15 +114,15 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Delete deletes the specified node. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get makes a request against senlin to get a details of a node type -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -170,13 +172,13 @@ type OperationOpts struct { Params OperationParams `json:"params,omitempty"` } -func Ops(client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { +func Ops(ctx context.Context, client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { b, err := opts.ToNodeOperationMap() if err != nil { r.Err = err return } - resp, err := client.Post(opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -202,24 +204,24 @@ type RecoverOpts struct { Check *bool `json:"check,omitempty"` } -func Recover(client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r ActionResult) { +func Recover(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r ActionResult) { b, err := opts.ToNodeRecoverMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -func Check(client *gophercloud.ServiceClient, id string) (r ActionResult) { +func Check(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { b := map[string]interface{}{ "check": map[string]interface{}{}, } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go index f534485cae..c969eb76e7 100644 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ b/openstack/clustering/v1/nodes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "strings" "testing" @@ -32,7 +33,7 @@ func TestCreateNode(t *testing.T) { Role: "", } - res := nodes.Create(fake.ServiceClient(), createOpts) + res := nodes.Create(context.TODO(), fake.ServiceClient(), createOpts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-Openstack-Request-Id") @@ -57,7 +58,7 @@ func TestListNodes(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := nodes.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := nodes.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := nodes.ExtractNodes(page) th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedList, actual) @@ -75,7 +76,7 @@ func TestDeleteNode(t *testing.T) { HandleDeleteSuccessfully(t) - deleteResult := nodes.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") + deleteResult := nodes.Delete(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } @@ -85,7 +86,7 @@ func TestGetNode(t *testing.T) { HandleGetSuccessfully(t) - actual, err := nodes.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() + actual, err := nodes.Get(context.TODO(), fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedGet, *actual) } @@ -99,7 +100,7 @@ func TestUpdateNode(t *testing.T) { nodeOpts := nodes.UpdateOpts{ Name: "node-e395be1e-002", } - actual, err := nodes.Update(fake.ServiceClient(), ExpectedUpdate.ID, nodeOpts).Extract() + actual, err := nodes.Update(context.TODO(), fake.ServiceClient(), ExpectedUpdate.ID, nodeOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdate, *actual) } @@ -113,7 +114,7 @@ func TestOpsNode(t *testing.T) { nodeOpts := nodes.OperationOpts{ Operation: nodes.PauseOperation, } - actual, err := nodes.Ops(fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", nodeOpts).Extract() + actual, err := nodes.Ops(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", nodeOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, OperationExpectedActionID, actual) } @@ -127,7 +128,7 @@ func TestNodeRecover(t *testing.T) { Operation: nodes.RebuildRecovery, Check: new(bool), } - actionID, err := nodes.Recover(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() + actionID, err := nodes.Recover(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } @@ -138,7 +139,7 @@ func TestNodeCheck(t *testing.T) { HandleCheckSuccessfully(t) - actionID, err := nodes.Check(fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() + actionID, err := nodes.Check(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedActionID, actionID) } diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go index 77e799d092..5196bd71f1 100644 --- a/openstack/clustering/v1/policies/requests.go +++ b/openstack/clustering/v1/policies/requests.go @@ -1,6 +1,8 @@ package policies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -82,13 +84,13 @@ func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { } // Create makes a request against the API to create a policy -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -96,8 +98,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete makes a request against the API to delete a policy. -func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { - resp, err := client.Delete(policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { + resp, err := client.Delete(ctx, policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -121,13 +123,13 @@ func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { } // Update updates a specified policy. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -151,13 +153,13 @@ func (opts ValidateOpts) ToPolicyValidateMap() (map[string]interface{}, error) { } // Validate policy will validate a specified policy. -func Validate(client *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) { +func Validate(ctx context.Context, client *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) { b, err := opts.ToPolicyValidateMap() if err != nil { r.Err = err return } - resp, err := client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -165,9 +167,9 @@ func Validate(client *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r Va } // Get makes a request against the API to get details for a policy. -func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { +func Get(ctx context.Context, client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { url := policyGetURL(client, policyTypeName) - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go index e50d82f5cf..ffedb87983 100644 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ b/openstack/clustering/v1/policies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" @@ -20,7 +21,7 @@ func TestListPolicies(t *testing.T) { } count := 0 - err := policies.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { + err := policies.List(fake.ServiceClient(), listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := policies.ExtractPolicies(page) if err != nil { t.Errorf("Failed to extract policies: %v", err) @@ -53,7 +54,7 @@ func TestCreatePolicy(t *testing.T) { Spec: ExpectedCreatePolicy.Spec, } - actual, err := policies.Create(fake.ServiceClient(), opts).Extract() + actual, err := policies.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -65,7 +66,7 @@ func TestDeletePolicy(t *testing.T) { HandlePolicyDelete(t) - res := policies.Delete(fake.ServiceClient(), PolicyIDtoDelete) + res := policies.Delete(context.TODO(), fake.ServiceClient(), PolicyIDtoDelete) th.AssertNoErr(t, res.ExtractErr()) requestID := res.Header["X-Openstack-Request-Id"][0] @@ -84,7 +85,7 @@ func TestUpdatePolicy(t *testing.T) { Name: ExpectedUpdatePolicy.Name, } - actual, err := policies.Update(fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() + actual, err := policies.Update(context.TODO(), fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -100,7 +101,7 @@ func TestBadUpdatePolicy(t *testing.T) { Name: ExpectedUpdatePolicy.Name, } - _, err := policies.Update(fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() + _, err := policies.Update(context.TODO(), fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() th.AssertEquals(t, false, err == nil) } @@ -116,7 +117,7 @@ func TestValidatePolicy(t *testing.T) { Spec: ExpectedValidatePolicy.Spec, } - actual, err := policies.Validate(fake.ServiceClient(), opts).Extract() + actual, err := policies.Validate(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) } @@ -131,7 +132,7 @@ func TestBadValidatePolicy(t *testing.T) { Spec: ExpectedValidatePolicy.Spec, } - _, err := policies.Validate(fake.ServiceClient(), opts).Extract() + _, err := policies.Validate(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertEquals(t, false, err == nil) } @@ -141,7 +142,7 @@ func TestGetPolicy(t *testing.T) { HandlePolicyGet(t) - actual, err := policies.Get(fake.ServiceClient(), PolicyIDtoGet).Extract() + actual, err := policies.Get(context.TODO(), fake.ServiceClient(), PolicyIDtoGet).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedGetPolicy, *actual) diff --git a/openstack/clustering/v1/policytypes/requests.go b/openstack/clustering/v1/policytypes/requests.go index a51e6c7d6a..2da89848fa 100644 --- a/openstack/clustering/v1/policytypes/requests.go +++ b/openstack/clustering/v1/policytypes/requests.go @@ -1,6 +1,8 @@ package policytypes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -15,10 +17,10 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { } // Get makes a request against the API to get details for a policy type. -func Get(client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { +func Get(ctx context.Context, client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { url := policyTypeGetURL(client, policyTypeName) - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/clustering/v1/policytypes/testing/requests_test.go b/openstack/clustering/v1/policytypes/testing/requests_test.go index 7d825e551f..827616df7a 100644 --- a/openstack/clustering/v1/policytypes/testing/requests_test.go +++ b/openstack/clustering/v1/policytypes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policytypes" @@ -16,7 +17,7 @@ func TestListPolicyTypes(t *testing.T) { HandlePolicyTypeList(t) count := 0 - err := policytypes.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := policytypes.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := policytypes.ExtractPolicyTypes(page) @@ -42,7 +43,7 @@ func TestGetPolicyType(t *testing.T) { HandlePolicyTypeGet(t) - actual, err := policytypes.Get(fake.ServiceClient(), FakePolicyTypetoGet).Extract() + actual, err := policytypes.Get(context.TODO(), fake.ServiceClient(), FakePolicyTypetoGet).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedPolicyTypeDetail, actual) diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go index bb4b596c9f..7673135aee 100644 --- a/openstack/clustering/v1/profiles/requests.go +++ b/openstack/clustering/v1/profiles/requests.go @@ -1,6 +1,8 @@ package profiles import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -24,13 +26,13 @@ func (opts CreateOpts) ToProfileCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new profile on the server. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToProfileCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -38,8 +40,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get retrieves detail of a single profile. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -102,13 +104,13 @@ func (opts UpdateOpts) ToProfileUpdateMap() (map[string]interface{}, error) { } // Update updates a profile. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToProfileUpdateMap() if err != nil { r.Err = err return r } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -116,8 +118,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Delete deletes the specified profile via profile id. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -139,13 +141,13 @@ func (opts ValidateOpts) ToProfileValidateMap() (map[string]interface{}, error) } // Validate profile. -func Validate(client *gophercloud.ServiceClient, opts ValidateOpts) (r ValidateResult) { +func Validate(ctx context.Context, client *gophercloud.ServiceClient, opts ValidateOpts) (r ValidateResult) { b, err := opts.ToProfileValidateMap() if err != nil { r.Err = err return } - resp, err := client.Post(validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go index b820bc3252..bb4cd41568 100644 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ b/openstack/clustering/v1/profiles/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" @@ -36,7 +37,7 @@ func TestCreateProfile(t *testing.T) { }, } - profile, err := profiles.Create(fake.ServiceClient(), createOpts).Extract() + profile, err := profiles.Create(context.TODO(), fake.ServiceClient(), createOpts).Extract() if err != nil { t.Errorf("Failed to extract profile: %v", err) } @@ -50,7 +51,7 @@ func TestGetProfile(t *testing.T) { HandleGetSuccessfully(t) - actual, err := profiles.Get(fake.ServiceClient(), ExpectedGet.ID).Extract() + actual, err := profiles.Get(context.TODO(), fake.ServiceClient(), ExpectedGet.ID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedGet, *actual) } @@ -67,7 +68,7 @@ func TestListProfiles(t *testing.T) { } count := 0 - err := profiles.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { + err := profiles.List(fake.ServiceClient(), listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := profiles.ExtractProfiles(page) th.AssertNoErr(t, err) @@ -93,7 +94,7 @@ func TestUpdateProfile(t *testing.T) { Name: "pserver", } - actual, err := profiles.Update(fake.ServiceClient(), ExpectedUpdate.ID, updateOpts).Extract() + actual, err := profiles.Update(context.TODO(), fake.ServiceClient(), ExpectedUpdate.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdate, *actual) } @@ -104,7 +105,7 @@ func TestDeleteProfile(t *testing.T) { HandleDeleteSuccessfully(t) - deleteResult := profiles.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") + deleteResult := profiles.Delete(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } @@ -134,7 +135,7 @@ func TestValidateProfile(t *testing.T) { client.Microversion = "1.2" client.Type = "clustering" - profile, err := profiles.Validate(client, validateOpts).Extract() + profile, err := profiles.Validate(context.TODO(), client, validateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedValidate, *profile) } diff --git a/openstack/clustering/v1/profiletypes/requests.go b/openstack/clustering/v1/profiletypes/requests.go index 22db019ccc..91124bc6de 100644 --- a/openstack/clustering/v1/profiletypes/requests.go +++ b/openstack/clustering/v1/profiletypes/requests.go @@ -1,12 +1,14 @@ package profiletypes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/clustering/v1/profiletypes/testing/requests_test.go b/openstack/clustering/v1/profiletypes/testing/requests_test.go index 8e7fa19e68..9dc1c55385 100644 --- a/openstack/clustering/v1/profiletypes/testing/requests_test.go +++ b/openstack/clustering/v1/profiletypes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -17,7 +18,7 @@ func TestListProfileTypes(t *testing.T) { HandleList1Successfully(t) pageCount := 0 - err := profiletypes.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := profiletypes.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pageCount++ actual, err := profiletypes.ExtractProfileTypes(page) th.AssertNoErr(t, err) @@ -39,7 +40,7 @@ func TestGetProfileType10(t *testing.T) { HandleGet1Successfully(t, ExpectedProfileType1.Name) - actual, err := profiletypes.Get(fake.ServiceClient(), ExpectedProfileType1.Name).Extract() + actual, err := profiletypes.Get(context.TODO(), fake.ServiceClient(), ExpectedProfileType1.Name).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedProfileType1, *actual) } @@ -50,7 +51,7 @@ func TestGetProfileType15(t *testing.T) { HandleGet15Successfully(t, ExpectedProfileType15.Name) - actual, err := profiletypes.Get(fake.ServiceClient(), ExpectedProfileType15.Name).Extract() + actual, err := profiletypes.Get(context.TODO(), fake.ServiceClient(), ExpectedProfileType15.Name).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedProfileType15, *actual) } @@ -61,7 +62,7 @@ func TestListProfileTypesOps(t *testing.T) { HandleListOpsSuccessfully(t) - allPages, err := profiletypes.ListOps(fake.ServiceClient(), ProfileTypeName).AllPages() + allPages, err := profiletypes.ListOps(fake.ServiceClient(), ProfileTypeName).AllPages(context.TODO()) th.AssertNoErr(t, err) allPolicyTypes, err := profiletypes.ExtractOps(allPages) diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go index 785c2663cf..99c2cc1e2c 100644 --- a/openstack/clustering/v1/receivers/requests.go +++ b/openstack/clustering/v1/receivers/requests.go @@ -1,6 +1,8 @@ package receivers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -35,13 +37,13 @@ func (opts CreateOpts) ToReceiverCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new receiver. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToReceiverCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -67,13 +69,13 @@ func (opts UpdateOpts) ToReceiverUpdateMap() (map[string]interface{}, error) { } // Update requests the update of a receiver. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToReceiverUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -81,8 +83,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Get retrieves details of a single receiver. Use Extract to convert its result into a Receiver. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -129,15 +131,15 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Delete deletes the specified receiver ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Notify Notifies message type receiver -func Notify(client *gophercloud.ServiceClient, id string) (r NotifyResult) { - resp, err := client.Post(notifyURL(client, id), nil, nil, &gophercloud.RequestOpts{ +func Notify(ctx context.Context, client *gophercloud.ServiceClient, id string) (r NotifyResult) { + resp, err := client.Post(ctx, notifyURL(client, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go index 9e3f261c9a..842f88ed27 100644 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ b/openstack/clustering/v1/receivers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" @@ -24,7 +25,7 @@ func TestCreateReceiver(t *testing.T) { Params: map[string]interface{}{}, } - actual, err := receivers.Create(fake.ServiceClient(), opts).Extract() + actual, err := receivers.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedReceiver, *actual) } @@ -35,7 +36,7 @@ func TestGetReceivers(t *testing.T) { HandleGetSuccessfully(t) - actual, err := receivers.Get(fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() + actual, err := receivers.Get(context.TODO(), fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedReceiver, *actual) } @@ -53,7 +54,7 @@ func TestUpdateReceiver(t *testing.T) { "count": "2", }, } - actual, err := receivers.Update(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() + actual, err := receivers.Update(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdateReceiver, *actual) } @@ -70,7 +71,7 @@ func TestListReceivers(t *testing.T) { } count := 0 - receivers.List(fake.ServiceClient(), opts).EachPage(func(page pagination.Page) (bool, error) { + receivers.List(fake.ServiceClient(), opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := receivers.ExtractReceivers(page) th.AssertNoErr(t, err) @@ -89,7 +90,7 @@ func TestDeleteReceiver(t *testing.T) { HandleDeleteSuccessfully(t) - deleteResult := receivers.Delete(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") + deleteResult := receivers.Delete(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, deleteResult.ExtractErr()) } @@ -99,7 +100,7 @@ func TestNotifyReceivers(t *testing.T) { HandleNotifySuccessfully(t) - requestID, err := receivers.Notify(fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").Extract() + requestID, err := receivers.Notify(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ExpectedNotifyRequestID, requestID) } diff --git a/openstack/clustering/v1/webhooks/requests.go b/openstack/clustering/v1/webhooks/requests.go index 36048df535..05bbc475b9 100644 --- a/openstack/clustering/v1/webhooks/requests.go +++ b/openstack/clustering/v1/webhooks/requests.go @@ -1,6 +1,7 @@ package webhooks import ( + "context" "fmt" "net/url" @@ -32,7 +33,7 @@ func (opts TriggerOpts) ToWebhookTriggerQuery() (string, error) { } // Trigger an action represented by a webhook. -func Trigger(client *gophercloud.ServiceClient, id string, opts TriggerOptsBuilder) (r TriggerResult) { +func Trigger(ctx context.Context, client *gophercloud.ServiceClient, id string, opts TriggerOptsBuilder) (r TriggerResult) { url := triggerURL(client, id) if opts != nil { query, err := opts.ToWebhookTriggerQuery() @@ -46,7 +47,7 @@ func Trigger(client *gophercloud.ServiceClient, id string, opts TriggerOptsBuild return } - resp, err := client.Post(url, nil, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, url, nil, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/clustering/v1/webhooks/testing/requests_test.go b/openstack/clustering/v1/webhooks/testing/requests_test.go index 24bf532ec1..48033e421b 100644 --- a/openstack/clustering/v1/webhooks/testing/requests_test.go +++ b/openstack/clustering/v1/webhooks/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "encoding/json" "fmt" "net/http" @@ -35,7 +36,7 @@ func TestWebhookTrigger(t *testing.T) { "bar": "baz", }, } - result, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() + result, err := webhooks.Trigger(context.TODO(), fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, result, "290c44fa-c60f-4d75-a0eb-87433ba982a3") } @@ -80,7 +81,7 @@ func TestWebhooksInvalidAction(t *testing.T) { "bar": "baz", }, } - _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() + _, err := webhooks.Trigger(context.TODO(), fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() isValid := err.(*json.UnmarshalTypeError) == nil th.AssertEquals(t, false, isValid) } @@ -103,7 +104,7 @@ func TestWebhookTriggerInvalidEmptyOpt(t *testing.T) { }`) }) - _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{}).Extract() + _, err := webhooks.Trigger(context.TODO(), fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{}).Extract() if err == nil { t.Errorf("Expected error without V param") } @@ -127,7 +128,7 @@ func TestWebhookTriggerInvalidNilOpt(t *testing.T) { }`) }) - _, err := webhooks.Trigger(fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", nil).Extract() + _, err := webhooks.Trigger(context.TODO(), fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", nil).Extract() if err == nil { t.Errorf("Expected error with nil param") diff --git a/openstack/common/extensions/requests.go b/openstack/common/extensions/requests.go index 69c84dde99..40e18f3574 100755 --- a/openstack/common/extensions/requests.go +++ b/openstack/common/extensions/requests.go @@ -1,13 +1,15 @@ package extensions import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) // Get retrieves information for a specific extension using its alias. -func Get(c *gophercloud.ServiceClient, alias string) (r GetResult) { - resp, err := c.Get(ExtensionURL(c, alias), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, alias string) (r GetResult) { + resp, err := c.Get(ctx, ExtensionURL(c, alias), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/common/extensions/testing/requests_test.go b/openstack/common/extensions/testing/requests_test.go index 7f35678758..5a0c49ce78 100644 --- a/openstack/common/extensions/testing/requests_test.go +++ b/openstack/common/extensions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" @@ -16,7 +17,7 @@ func TestList(t *testing.T) { count := 0 - extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + extensions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) th.AssertNoErr(t, err) @@ -33,7 +34,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetExtensionSuccessfully(t) - actual, err := extensions.Get(client.ServiceClient(), "agent").Extract() + actual, err := extensions.Get(context.TODO(), client.ServiceClient(), "agent").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SingleExtension, actual) } diff --git a/openstack/compute/apiversions/requests.go b/openstack/compute/apiversions/requests.go index a82c605c10..02dccd30d1 100644 --- a/openstack/compute/apiversions/requests.go +++ b/openstack/compute/apiversions/requests.go @@ -1,6 +1,8 @@ package apiversions import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -13,8 +15,8 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { } // Get will get a specific API version, specified by major ID. -func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - resp, err := client.Get(getURL(client, v), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, v string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/apiversions/testing/requests_test.go b/openstack/compute/apiversions/testing/requests_test.go index 86d0deb0a0..737976b750 100644 --- a/openstack/compute/apiversions/testing/requests_test.go +++ b/openstack/compute/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/apiversions" @@ -14,7 +15,7 @@ func TestListAPIVersions(t *testing.T) { MockListResponse(t) - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersions(allVersions) @@ -29,7 +30,7 @@ func TestGetAPIVersion(t *testing.T) { MockGetResponse(t) - actual, err := apiversions.Get(client.ServiceClient(), "v2.1").Extract() + actual, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v2.1").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, NovaAPIVersion21Result, *actual) @@ -41,6 +42,6 @@ func TestGetMultipleAPIVersion(t *testing.T) { MockGetMultipleResponses(t) - _, err := apiversions.Get(client.ServiceClient(), "v3").Extract() + _, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v3").Extract() th.AssertEquals(t, err.Error(), "Unable to find requested API version") } diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/extensions/aggregates/requests.go index 14779e2c05..9324c4f88a 100644 --- a/openstack/compute/v2/extensions/aggregates/requests.go +++ b/openstack/compute/v2/extensions/aggregates/requests.go @@ -1,6 +1,7 @@ package aggregates import ( + "context" "strconv" "github.com/gophercloud/gophercloud/v2" @@ -30,13 +31,13 @@ func (opts CreateOpts) ToAggregatesCreateMap() (map[string]interface{}, error) { } // Create makes a request against the API to create an aggregate. -func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { b, err := opts.ToAggregatesCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(aggregatesCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, aggregatesCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -44,9 +45,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) } // Delete makes a request against the API to delete an aggregate. -func Delete(client *gophercloud.ServiceClient, aggregateID int) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, aggregateID int) (r DeleteResult) { v := strconv.Itoa(aggregateID) - resp, err := client.Delete(aggregatesDeleteURL(client, v), &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, aggregatesDeleteURL(client, v), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -54,9 +55,9 @@ func Delete(client *gophercloud.ServiceClient, aggregateID int) (r DeleteResult) } // Get makes a request against the API to get details for a specific aggregate. -func Get(client *gophercloud.ServiceClient, aggregateID int) (r GetResult) { +func Get(ctx context.Context, client *gophercloud.ServiceClient, aggregateID int) (r GetResult) { v := strconv.Itoa(aggregateID) - resp, err := client.Get(aggregatesGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, aggregatesGetURL(client, v), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -79,7 +80,7 @@ func (opts UpdateOpts) ToAggregatesUpdateMap() (map[string]interface{}, error) { } // Update makes a request against the API to update a specific aggregate. -func Update(client *gophercloud.ServiceClient, aggregateID int, opts UpdateOpts) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, aggregateID int, opts UpdateOpts) (r UpdateResult) { v := strconv.Itoa(aggregateID) b, err := opts.ToAggregatesUpdateMap() @@ -87,7 +88,7 @@ func Update(client *gophercloud.ServiceClient, aggregateID int, opts UpdateOpts) r.Err = err return } - resp, err := client.Put(aggregatesUpdateURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, aggregatesUpdateURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -104,7 +105,7 @@ func (opts AddHostOpts) ToAggregatesAddHostMap() (map[string]interface{}, error) } // AddHost makes a request against the API to add host to a specific aggregate. -func AddHost(client *gophercloud.ServiceClient, aggregateID int, opts AddHostOpts) (r ActionResult) { +func AddHost(ctx context.Context, client *gophercloud.ServiceClient, aggregateID int, opts AddHostOpts) (r ActionResult) { v := strconv.Itoa(aggregateID) b, err := opts.ToAggregatesAddHostMap() @@ -112,7 +113,7 @@ func AddHost(client *gophercloud.ServiceClient, aggregateID int, opts AddHostOpt r.Err = err return } - resp, err := client.Post(aggregatesAddHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, aggregatesAddHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -129,7 +130,7 @@ func (opts RemoveHostOpts) ToAggregatesRemoveHostMap() (map[string]interface{}, } // RemoveHost makes a request against the API to remove host from a specific aggregate. -func RemoveHost(client *gophercloud.ServiceClient, aggregateID int, opts RemoveHostOpts) (r ActionResult) { +func RemoveHost(ctx context.Context, client *gophercloud.ServiceClient, aggregateID int, opts RemoveHostOpts) (r ActionResult) { v := strconv.Itoa(aggregateID) b, err := opts.ToAggregatesRemoveHostMap() @@ -137,7 +138,7 @@ func RemoveHost(client *gophercloud.ServiceClient, aggregateID int, opts RemoveH r.Err = err return } - resp, err := client.Post(aggregatesRemoveHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, aggregatesRemoveHostURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -153,7 +154,7 @@ func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]interface{}, error) { } // SetMetadata makes a request against the API to set metadata to a specific aggregate. -func SetMetadata(client *gophercloud.ServiceClient, aggregateID int, opts SetMetadataOpts) (r ActionResult) { +func SetMetadata(ctx context.Context, client *gophercloud.ServiceClient, aggregateID int, opts SetMetadataOpts) (r ActionResult) { v := strconv.Itoa(aggregateID) b, err := opts.ToSetMetadataMap() @@ -161,7 +162,7 @@ func SetMetadata(client *gophercloud.ServiceClient, aggregateID int, opts SetMet r.Err = err return } - resp, err := client.Post(aggregatesSetMetadataURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, aggregatesSetMetadataURL(client, v), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go index 4bf950eb10..f0a5c3afc7 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/extensions/aggregates/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" @@ -15,7 +16,7 @@ func TestListAggregates(t *testing.T) { HandleListSuccessfully(t) pages := 0 - err := aggregates.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := aggregates.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := aggregates.ExtractAggregates(page) @@ -51,7 +52,7 @@ func TestCreateAggregates(t *testing.T) { AvailabilityZone: "london", } - actual, err := aggregates.Create(client.ServiceClient(), opts).Extract() + actual, err := aggregates.Create(context.TODO(), client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -62,7 +63,7 @@ func TestDeleteAggregates(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := aggregates.Delete(client.ServiceClient(), AggregateIDtoDelete).ExtractErr() + err := aggregates.Delete(context.TODO(), client.ServiceClient(), AggregateIDtoDelete).ExtractErr() th.AssertNoErr(t, err) } @@ -73,7 +74,7 @@ func TestGetAggregates(t *testing.T) { expected := SecondFakeAggregate - actual, err := aggregates.Get(client.ServiceClient(), AggregateIDtoGet).Extract() + actual, err := aggregates.Get(context.TODO(), client.ServiceClient(), AggregateIDtoGet).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -91,7 +92,7 @@ func TestUpdateAggregate(t *testing.T) { AvailabilityZone: "nova2", } - actual, err := aggregates.Update(client.ServiceClient(), expected.ID, opts).Extract() + actual, err := aggregates.Update(context.TODO(), client.ServiceClient(), expected.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -108,7 +109,7 @@ func TestAddHostAggregate(t *testing.T) { Host: "cmp1", } - actual, err := aggregates.AddHost(client.ServiceClient(), expected.ID, opts).Extract() + actual, err := aggregates.AddHost(context.TODO(), client.ServiceClient(), expected.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -125,7 +126,7 @@ func TestRemoveHostAggregate(t *testing.T) { Host: "cmp1", } - actual, err := aggregates.RemoveHost(client.ServiceClient(), expected.ID, opts).Extract() + actual, err := aggregates.RemoveHost(context.TODO(), client.ServiceClient(), expected.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -142,7 +143,7 @@ func TestSetMetadataAggregate(t *testing.T) { Metadata: map[string]interface{}{"key": "value"}, } - actual, err := aggregates.SetMetadata(client.ServiceClient(), expected.ID, opts).Extract() + actual, err := aggregates.SetMetadata(context.TODO(), client.ServiceClient(), expected.ID, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/extensions/attachinterfaces/requests.go index 59c07a30cc..6aa1754f80 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/requests.go +++ b/openstack/compute/v2/extensions/attachinterfaces/requests.go @@ -1,6 +1,8 @@ package attachinterfaces import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -13,8 +15,8 @@ func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { } // Get requests details on a single interface attachment by the server and port IDs. -func Get(client *gophercloud.ServiceClient, serverID, portID string) (r GetResult) { - resp, err := client.Get(getInterfaceURL(client, serverID, portID), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, serverID, portID string) (r GetResult) { + resp, err := client.Get(ctx, getInterfaceURL(client, serverID, portID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -53,13 +55,13 @@ func (opts CreateOpts) ToAttachInterfacesCreateMap() (map[string]interface{}, er } // Create requests the creation of a new interface attachment on the server. -func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToAttachInterfacesCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createInterfaceURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createInterfaceURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -68,8 +70,8 @@ func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsB // Delete makes a request against the nova API to detach a single interface from the server. // It needs server and port IDs to make a such request. -func Delete(client *gophercloud.ServiceClient, serverID, portID string) (r DeleteResult) { - resp, err := client.Delete(deleteInterfaceURL(client, serverID, portID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, serverID, portID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteInterfaceURL(client, serverID, portID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go index 78cfcf6e01..28fa9efb3f 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go +++ b/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" @@ -16,7 +17,7 @@ func TestListInterface(t *testing.T) { expected := ListInterfacesExpected pages := 0 - err := attachinterfaces.List(client.ServiceClient(), "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f").EachPage(func(page pagination.Page) (bool, error) { + err := attachinterfaces.List(client.ServiceClient(), "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := attachinterfaces.ExtractInterfaces(page) @@ -38,7 +39,7 @@ func TestListInterfacesAllPages(t *testing.T) { defer th.TeardownHTTP() HandleInterfaceListSuccessfully(t) - allPages, err := attachinterfaces.List(client.ServiceClient(), "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f").AllPages() + allPages, err := attachinterfaces.List(client.ServiceClient(), "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f").AllPages(context.TODO()) th.AssertNoErr(t, err) _, err = attachinterfaces.ExtractInterfaces(allPages) th.AssertNoErr(t, err) @@ -54,7 +55,7 @@ func TestGetInterface(t *testing.T) { serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" interfaceID := "0dde1598-b374-474e-986f-5b8dd1df1d4e" - actual, err := attachinterfaces.Get(client.ServiceClient(), serverID, interfaceID).Extract() + actual, err := attachinterfaces.Get(context.TODO(), client.ServiceClient(), serverID, interfaceID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, actual) } @@ -69,7 +70,7 @@ func TestCreateInterface(t *testing.T) { serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" networkID := "8a5fe506-7e9f-4091-899b-96336909d93c" - actual, err := attachinterfaces.Create(client.ServiceClient(), serverID, attachinterfaces.CreateOpts{ + actual, err := attachinterfaces.Create(context.TODO(), client.ServiceClient(), serverID, attachinterfaces.CreateOpts{ NetworkID: networkID, }).Extract() th.AssertNoErr(t, err) @@ -84,6 +85,6 @@ func TestDeleteInterface(t *testing.T) { serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" portID := "0dde1598-b374-474e-986f-5b8dd1df1d4e" - err := attachinterfaces.Delete(client.ServiceClient(), serverID, portID).ExtractErr() + err := attachinterfaces.Delete(context.TODO(), client.ServiceClient(), serverID, portID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go b/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go index 41c618f063..ff441f7bb5 100644 --- a/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go +++ b/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" az "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" @@ -15,7 +16,7 @@ func TestList(t *testing.T) { HandleGetSuccessfully(t) - allPages, err := az.List(client.ServiceClient()).AllPages() + allPages, err := az.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := az.ExtractAvailabilityZones(allPages) @@ -31,7 +32,7 @@ func TestListDetail(t *testing.T) { HandleGetDetailSuccessfully(t) - allPages, err := az.ListDetail(client.ServiceClient()).AllPages() + allPages, err := az.ListDetail(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := az.ExtractAvailabilityZones(allPages) diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go index c702ccd803..6d4d75116c 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -1,6 +1,8 @@ package bootfromvolume import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" ) @@ -128,13 +130,13 @@ func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a server from the given block device mapping. -func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) (r servers.CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) (r servers.CreateResult) { b, err := opts.ToServerCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/extensions/defsecrules/requests.go index 2fdb577bf5..21c29fb351 100644 --- a/openstack/compute/v2/extensions/defsecrules/requests.go +++ b/openstack/compute/v2/extensions/defsecrules/requests.go @@ -1,6 +1,7 @@ package defsecrules import ( + "context" "strings" "github.com/gophercloud/gophercloud/v2" @@ -50,13 +51,13 @@ func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { } // Create is the operation responsible for creating a new default rule. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -64,15 +65,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get will return details for a particular default rule. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(resourceURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a rule the project's default security group. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(resourceURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, resourceURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go b/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go index c2678b046a..9b92c8cc83 100644 --- a/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go +++ b/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/defsecrules" @@ -20,7 +21,7 @@ func TestList(t *testing.T) { count := 0 - err := defsecrules.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := defsecrules.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := defsecrules.ExtractDefaultRules(page) th.AssertNoErr(t, err) @@ -57,7 +58,7 @@ func TestCreate(t *testing.T) { CIDR: "10.10.12.0/24", } - group, err := defsecrules.Create(client.ServiceClient(), opts).Extract() + group, err := defsecrules.Create(context.TODO(), client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &defsecrules.DefaultRule{ @@ -83,7 +84,7 @@ func TestCreateICMPZero(t *testing.T) { CIDR: "10.10.12.0/24", } - group, err := defsecrules.Create(client.ServiceClient(), opts).Extract() + group, err := defsecrules.Create(context.TODO(), client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &defsecrules.DefaultRule{ @@ -102,7 +103,7 @@ func TestGet(t *testing.T) { mockGetRuleResponse(t, ruleID) - group, err := defsecrules.Get(client.ServiceClient(), ruleID).Extract() + group, err := defsecrules.Get(context.TODO(), client.ServiceClient(), ruleID).Extract() th.AssertNoErr(t, err) expected := &defsecrules.DefaultRule{ @@ -122,6 +123,6 @@ func TestDelete(t *testing.T) { mockDeleteRuleResponse(t, ruleID) - err := defsecrules.Delete(client.ServiceClient(), ruleID).ExtractErr() + err := defsecrules.Delete(context.TODO(), client.ServiceClient(), ruleID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/delegate.go b/openstack/compute/v2/extensions/delegate.go index 84fe2963fc..8ba5ee4a07 100644 --- a/openstack/compute/v2/extensions/delegate.go +++ b/openstack/compute/v2/extensions/delegate.go @@ -1,6 +1,8 @@ package extensions import ( + "context" + "github.com/gophercloud/gophercloud/v2" common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/pagination" @@ -12,8 +14,8 @@ func ExtractExtensions(page pagination.Page) ([]common.Extension, error) { } // Get retrieves information for a specific extension using its alias. -func Get(c *gophercloud.ServiceClient, alias string) common.GetResult { - return common.Get(c, alias) +func Get(ctx context.Context, c *gophercloud.ServiceClient, alias string) common.GetResult { + return common.Get(ctx, c, alias) } // List returns a Pager which allows you to iterate over the full collection of extensions. diff --git a/openstack/compute/v2/extensions/diagnostics/requests.go b/openstack/compute/v2/extensions/diagnostics/requests.go index d3523f6070..54808a3607 100644 --- a/openstack/compute/v2/extensions/diagnostics/requests.go +++ b/openstack/compute/v2/extensions/diagnostics/requests.go @@ -1,12 +1,14 @@ package diagnostics import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // Diagnostics -func Get(client *gophercloud.ServiceClient, serverId string) (r serverDiagnosticsResult) { - resp, err := client.Get(serverDiagnosticsURL(client, serverId), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, serverId string) (r serverDiagnosticsResult) { + resp, err := client.Get(ctx, serverDiagnosticsURL(client, serverId), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go b/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go index 053dedfa55..05f3d8ca57 100644 --- a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go +++ b/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diagnostics" @@ -16,7 +17,7 @@ func TestGetDiagnostics(t *testing.T) { expected := map[string]interface{}{"cpu0_time": float64(173), "memory": float64(524288)} - res, err := diagnostics.Get(client.ServiceClient(), "1234asdf").Extract() + res, err := diagnostics.Get(context.TODO(), client.ServiceClient(), "1234asdf").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, res) diff --git a/openstack/compute/v2/extensions/evacuate/requests.go b/openstack/compute/v2/extensions/evacuate/requests.go index 66ed220017..52b650c864 100644 --- a/openstack/compute/v2/extensions/evacuate/requests.go +++ b/openstack/compute/v2/extensions/evacuate/requests.go @@ -1,6 +1,8 @@ package evacuate import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) @@ -29,13 +31,13 @@ func (opts EvacuateOpts) ToEvacuateMap() (map[string]interface{}, error) { } // Evacuate will Evacuate a failed instance to another host. -func Evacuate(client *gophercloud.ServiceClient, id string, opts EvacuateOptsBuilder) (r EvacuateResult) { +func Evacuate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts EvacuateOptsBuilder) (r EvacuateResult) { b, err := opts.ToEvacuateMap() if err != nil { r.Err = err return } - resp, err := client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/extensions/evacuate/testing/requests_test.go b/openstack/compute/v2/extensions/evacuate/testing/requests_test.go index 07f08ffa12..6a066fc7a7 100644 --- a/openstack/compute/v2/extensions/evacuate/testing/requests_test.go +++ b/openstack/compute/v2/extensions/evacuate/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/evacuate" @@ -15,7 +16,7 @@ func TestEvacuate(t *testing.T) { mockEvacuateResponse(t, serverID) - _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{ + _, err := evacuate.Evacuate(context.TODO(), client.ServiceClient(), serverID, evacuate.EvacuateOpts{ Host: "derp", AdminPass: "MySecretPass", OnSharedStorage: false, @@ -30,7 +31,7 @@ func TestEvacuateWithHost(t *testing.T) { mockEvacuateResponseWithHost(t, serverID) - _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{ + _, err := evacuate.Evacuate(context.TODO(), client.ServiceClient(), serverID, evacuate.EvacuateOpts{ Host: "derp", }).ExtractAdminPass() th.AssertNoErr(t, err) @@ -43,7 +44,7 @@ func TestEvacuateWithNoOpts(t *testing.T) { mockEvacuateResponseWithNoOpts(t, serverID) - _, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() + _, err := evacuate.Evacuate(context.TODO(), client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() th.AssertNoErr(t, err) } @@ -54,7 +55,7 @@ func TestEvacuateAdminpassResponse(t *testing.T) { mockEvacuateAdminpassResponse(t, serverID) - actual, err := evacuate.Evacuate(client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() + actual, err := evacuate.Evacuate(context.TODO(), client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() th.CheckEquals(t, "MySecretPass", actual) th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go index b9ed64df94..fb2752b505 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -30,7 +31,7 @@ func TestServerWithUsageExt(t *testing.T) { var serverWithAttributesExt serverAttributesExt // Extract basic fields. - err := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) + err := servers.Get(context.TODO(), fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) th.AssertNoErr(t, err) th.AssertEquals(t, serverWithAttributesExt.Host, "compute01") diff --git a/openstack/compute/v2/extensions/floatingips/requests.go b/openstack/compute/v2/extensions/floatingips/requests.go index c3acec3e50..32306304ba 100644 --- a/openstack/compute/v2/extensions/floatingips/requests.go +++ b/openstack/compute/v2/extensions/floatingips/requests.go @@ -1,6 +1,8 @@ package floatingips import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -30,13 +32,13 @@ func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new Floating IP. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFloatingIPCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -44,15 +46,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get returns data about a previously created Floating IP. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous allocated Floating IP. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -78,13 +80,13 @@ func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, er } // AssociateInstance pairs an allocated Floating IP with a server. -func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) { +func AssociateInstance(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) { b, err := opts.ToFloatingIPAssociateMap() if err != nil { r.Err = err return } - resp, err := client.Post(associateURL(client, serverID), b, nil, nil) + resp, err := client.Post(ctx, associateURL(client, serverID), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -107,13 +109,13 @@ func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface } // DisassociateInstance decouples an allocated Floating IP from an instance -func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) { +func DisassociateInstance(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) { b, err := opts.ToFloatingIPDisassociateMap() if err != nil { r.Err = err return } - resp, err := client.Post(disassociateURL(client, serverID), b, nil, nil) + resp, err := client.Post(ctx, disassociateURL(client, serverID), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/floatingips/testing/requests_test.go b/openstack/compute/v2/extensions/floatingips/testing/requests_test.go index 6b4c4fa84d..757f5b6e91 100644 --- a/openstack/compute/v2/extensions/floatingips/testing/requests_test.go +++ b/openstack/compute/v2/extensions/floatingips/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" @@ -15,7 +16,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := floatingips.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := floatingips.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := floatingips.ExtractFloatingIPs(page) th.AssertNoErr(t, err) @@ -32,7 +33,7 @@ func TestCreate(t *testing.T) { defer th.TeardownHTTP() HandleCreateSuccessfully(t) - actual, err := floatingips.Create(client.ServiceClient(), floatingips.CreateOpts{ + actual, err := floatingips.Create(context.TODO(), client.ServiceClient(), floatingips.CreateOpts{ Pool: "nova", }).Extract() th.AssertNoErr(t, err) @@ -44,7 +45,7 @@ func TestCreateWithNumericID(t *testing.T) { defer th.TeardownHTTP() HandleCreateWithNumericIDSuccessfully(t) - actual, err := floatingips.Create(client.ServiceClient(), floatingips.CreateOpts{ + actual, err := floatingips.Create(context.TODO(), client.ServiceClient(), floatingips.CreateOpts{ Pool: "nova", }).Extract() th.AssertNoErr(t, err) @@ -56,7 +57,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := floatingips.Get(client.ServiceClient(), "2").Extract() + actual, err := floatingips.Get(context.TODO(), client.ServiceClient(), "2").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &SecondFloatingIP, actual) } @@ -66,7 +67,7 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := floatingips.Delete(client.ServiceClient(), "1").ExtractErr() + err := floatingips.Delete(context.TODO(), client.ServiceClient(), "1").ExtractErr() th.AssertNoErr(t, err) } @@ -79,7 +80,7 @@ func TestAssociate(t *testing.T) { FloatingIP: "10.10.10.2", } - err := floatingips.AssociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr() + err := floatingips.AssociateInstance(context.TODO(), client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -93,7 +94,7 @@ func TestAssociateFixed(t *testing.T) { FixedIP: "166.78.185.201", } - err := floatingips.AssociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr() + err := floatingips.AssociateInstance(context.TODO(), client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -106,6 +107,6 @@ func TestDisassociateInstance(t *testing.T) { FloatingIP: "10.10.10.2", } - err := floatingips.DisassociateInstance(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", disassociateOpts).ExtractErr() + err := floatingips.DisassociateInstance(context.TODO(), client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", disassociateOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/extensions/hypervisors/requests.go index d8446166b9..37ecbee17a 100644 --- a/openstack/compute/v2/extensions/hypervisors/requests.go +++ b/openstack/compute/v2/extensions/hypervisors/requests.go @@ -1,6 +1,8 @@ package hypervisors import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -56,8 +58,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Statistics makes a request against the API to get hypervisors statistics. -func GetStatistics(client *gophercloud.ServiceClient) (r StatisticsResult) { - resp, err := client.Get(hypervisorsStatisticsURL(client), &r.Body, &gophercloud.RequestOpts{ +func GetStatistics(ctx context.Context, client *gophercloud.ServiceClient) (r StatisticsResult) { + resp, err := client.Get(ctx, hypervisorsStatisticsURL(client), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -65,8 +67,8 @@ func GetStatistics(client *gophercloud.ServiceClient) (r StatisticsResult) { } // Get makes a request against the API to get details for specific hypervisor. -func Get(client *gophercloud.ServiceClient, hypervisorID string) (r HypervisorResult) { - resp, err := client.Get(hypervisorsGetURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, hypervisorID string) (r HypervisorResult) { + resp, err := client.Get(ctx, hypervisorsGetURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -74,8 +76,8 @@ func Get(client *gophercloud.ServiceClient, hypervisorID string) (r HypervisorRe } // GetUptime makes a request against the API to get uptime for specific hypervisor. -func GetUptime(client *gophercloud.ServiceClient, hypervisorID string) (r UptimeResult) { - resp, err := client.Get(hypervisorsUptimeURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ +func GetUptime(ctx context.Context, client *gophercloud.ServiceClient, hypervisorID string) (r UptimeResult) { + resp, err := client.Get(ctx, hypervisorsUptimeURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go index 8c1296c0c1..6774bc3032 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" @@ -16,7 +17,7 @@ func TestListHypervisorsPre253(t *testing.T) { pages := 0 err := hypervisors.List(client.ServiceClient(), - hypervisors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + hypervisors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := hypervisors.ExtractHypervisors(page) @@ -45,7 +46,7 @@ func TestListAllHypervisorsPre253(t *testing.T) { defer testhelper.TeardownHTTP() HandleHypervisorListPre253Successfully(t) - allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{}).AllPages() + allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{}).AllPages(context.TODO()) testhelper.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) testhelper.AssertNoErr(t, err) @@ -60,7 +61,7 @@ func TestListHypervisors(t *testing.T) { pages := 0 err := hypervisors.List(client.ServiceClient(), - hypervisors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + hypervisors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := hypervisors.ExtractHypervisors(page) @@ -89,7 +90,7 @@ func TestListAllHypervisors(t *testing.T) { defer testhelper.TeardownHTTP() HandleHypervisorListSuccessfully(t) - allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{}).AllPages() + allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{}).AllPages(context.TODO()) testhelper.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) testhelper.AssertNoErr(t, err) @@ -103,7 +104,7 @@ func TestListAllHypervisorsWithParameters(t *testing.T) { HandleHypervisorListWithParametersSuccessfully(t) with_servers := true - allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{WithServers: &with_servers}).AllPages() + allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{WithServers: &with_servers}).AllPages(context.TODO()) testhelper.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) testhelper.AssertNoErr(t, err) @@ -118,7 +119,7 @@ func TestHypervisorsStatistics(t *testing.T) { expected := HypervisorsStatisticsExpected - actual, err := hypervisors.GetStatistics(client.ServiceClient()).Extract() + actual, err := hypervisors.GetStatistics(context.TODO(), client.ServiceClient()).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } @@ -130,7 +131,7 @@ func TestGetHypervisor(t *testing.T) { expected := HypervisorFake - actual, err := hypervisors.Get(client.ServiceClient(), expected.ID).Extract() + actual, err := hypervisors.Get(context.TODO(), client.ServiceClient(), expected.ID).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } @@ -142,7 +143,7 @@ func TestGetHypervisorEmptyCPUInfo(t *testing.T) { expected := HypervisorEmptyCPUInfo - actual, err := hypervisors.Get(client.ServiceClient(), expected.ID).Extract() + actual, err := hypervisors.Get(context.TODO(), client.ServiceClient(), expected.ID).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } @@ -154,7 +155,7 @@ func TestHypervisorsUptime(t *testing.T) { expected := HypervisorUptimeExpected - actual, err := hypervisors.GetUptime(client.ServiceClient(), HypervisorFake.ID).Extract() + actual, err := hypervisors.GetUptime(context.TODO(), client.ServiceClient(), HypervisorFake.ID).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, &expected, actual) } diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/requests.go b/openstack/compute/v2/extensions/injectnetworkinfo/requests.go index 91645d01fb..91d51be8fc 100644 --- a/openstack/compute/v2/extensions/injectnetworkinfo/requests.go +++ b/openstack/compute/v2/extensions/injectnetworkinfo/requests.go @@ -1,16 +1,18 @@ package injectnetworkinfo import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // InjectNetworkInfo will inject the network info into a server -func InjectNetworkInfo(client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { +func InjectNetworkInfo(ctx context.Context, client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { b := map[string]interface{}{ "injectNetworkInfo": nil, } - resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) + resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go b/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go index 8e5df9fde3..819f2aa0ec 100644 --- a/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go +++ b/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/injectnetworkinfo" @@ -16,6 +17,6 @@ func TestInjectNetworkInfo(t *testing.T) { mockInjectNetworkInfoResponse(t, serverID) - err := injectnetworkinfo.InjectNetworkInfo(client.ServiceClient(), serverID).ExtractErr() + err := injectnetworkinfo.InjectNetworkInfo(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/instanceactions/request.go b/openstack/compute/v2/extensions/instanceactions/request.go index d6f69f8488..d34090fe3a 100644 --- a/openstack/compute/v2/extensions/instanceactions/request.go +++ b/openstack/compute/v2/extensions/instanceactions/request.go @@ -1,6 +1,7 @@ package instanceactions import ( + "context" "net/url" "time" @@ -71,8 +72,8 @@ func List(client *gophercloud.ServiceClient, id string, opts ListOptsBuilder) pa } // Get makes a request against the API to get a server action. -func Get(client *gophercloud.ServiceClient, serverID, requestID string) (r InstanceActionResult) { - resp, err := client.Get(instanceActionsURL(client, serverID, requestID), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, serverID, requestID string) (r InstanceActionResult) { + resp, err := client.Get(ctx, instanceActionsURL(client, serverID, requestID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go b/openstack/compute/v2/extensions/instanceactions/testing/request_test.go index 74e8f6b88e..142d8d3014 100644 --- a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go +++ b/openstack/compute/v2/extensions/instanceactions/testing/request_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/instanceactions" @@ -16,7 +17,7 @@ func TestList(t *testing.T) { expected := ListExpected pages := 0 - err := instanceactions.List(client.ServiceClient(), "asdfasdfasdf", nil).EachPage(func(page pagination.Page) (bool, error) { + err := instanceactions.List(client.ServiceClient(), "asdfasdfasdf", nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := instanceactions.ExtractInstanceActions(page) @@ -39,7 +40,7 @@ func TestGet(t *testing.T) { HandleInstanceActionGetSuccessfully(t) client := client.ServiceClient() - actual, err := instanceactions.Get(client, "asdfasdfasdf", "okzeorkmkfs").Extract() + actual, err := instanceactions.Get(context.TODO(), client, "asdfasdfasdf", "okzeorkmkfs").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/extensions/keypairs/requests.go index 2435147548..c632f424aa 100644 --- a/openstack/compute/v2/extensions/keypairs/requests.go +++ b/openstack/compute/v2/extensions/keypairs/requests.go @@ -1,6 +1,8 @@ package keypairs import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/pagination" @@ -97,13 +99,13 @@ func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { // Create requests the creation of a new KeyPair on the server, or to import a // pre-existing keypair. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToKeyPairCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -130,7 +132,7 @@ func (opts GetOpts) ToKeyPairGetQuery() (string, error) { } // Get returns public data about a previously uploaded KeyPair. -func Get(client *gophercloud.ServiceClient, name string, opts GetOptsBuilder) (r GetResult) { +func Get(ctx context.Context, client *gophercloud.ServiceClient, name string, opts GetOptsBuilder) (r GetResult) { url := getURL(client, name) if opts != nil { query, err := opts.ToKeyPairGetQuery() @@ -141,7 +143,7 @@ func Get(client *gophercloud.ServiceClient, name string, opts GetOptsBuilder) (r url += query } - resp, err := client.Get(url, &r.Body, nil) + resp, err := client.Get(ctx, url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -166,7 +168,7 @@ func (opts DeleteOpts) ToKeyPairDeleteQuery() (string, error) { } // Delete requests the deletion of a previous stored KeyPair from the server. -func Delete(client *gophercloud.ServiceClient, name string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, name string, opts DeleteOptsBuilder) (r DeleteResult) { url := deleteURL(client, name) if opts != nil { query, err := opts.ToKeyPairDeleteQuery() @@ -177,7 +179,7 @@ func Delete(client *gophercloud.ServiceClient, name string, opts DeleteOptsBuild url += query } - resp, err := client.Delete(url, nil) + resp, err := client.Delete(ctx, url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go b/openstack/compute/v2/extensions/keypairs/testing/requests_test.go index c368bfa94b..c6c4dc0045 100644 --- a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go +++ b/openstack/compute/v2/extensions/keypairs/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" @@ -15,7 +16,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := keypairs.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := keypairs.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := keypairs.ExtractKeyPairs(page) th.AssertNoErr(t, err) @@ -32,7 +33,7 @@ func TestCreate(t *testing.T) { defer th.TeardownHTTP() HandleCreateSuccessfully(t) - actual, err := keypairs.Create(client.ServiceClient(), keypairs.CreateOpts{ + actual, err := keypairs.Create(context.TODO(), client.ServiceClient(), keypairs.CreateOpts{ Name: "createdkey", }).Extract() th.AssertNoErr(t, err) @@ -44,7 +45,7 @@ func TestCreateOtherUser(t *testing.T) { defer th.TeardownHTTP() HandleCreateSuccessfullyOtherUser(t) - actual, err := keypairs.Create(client.ServiceClient(), keypairs.CreateOpts{ + actual, err := keypairs.Create(context.TODO(), client.ServiceClient(), keypairs.CreateOpts{ Name: "createdkey", UserID: "fake2", }).Extract() @@ -57,7 +58,7 @@ func TestImport(t *testing.T) { defer th.TeardownHTTP() HandleImportSuccessfully(t) - actual, err := keypairs.Create(client.ServiceClient(), keypairs.CreateOpts{ + actual, err := keypairs.Create(context.TODO(), client.ServiceClient(), keypairs.CreateOpts{ Name: "importedkey", PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova", }).Extract() @@ -70,7 +71,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := keypairs.Get(client.ServiceClient(), "firstkey", nil).Extract() + actual, err := keypairs.Get(context.TODO(), client.ServiceClient(), "firstkey", nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstKeyPair, actual) } @@ -84,7 +85,7 @@ func TestGetOtherUser(t *testing.T) { UserID: "fake2", } - actual, err := keypairs.Get(client.ServiceClient(), "firstkey", getOpts).Extract() + actual, err := keypairs.Get(context.TODO(), client.ServiceClient(), "firstkey", getOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstKeyPairOtherUser, actual) } @@ -94,7 +95,7 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := keypairs.Delete(client.ServiceClient(), "deletedkey", nil).ExtractErr() + err := keypairs.Delete(context.TODO(), client.ServiceClient(), "deletedkey", nil).ExtractErr() th.AssertNoErr(t, err) } @@ -107,6 +108,6 @@ func TestDeleteOtherUser(t *testing.T) { UserID: "fake2", } - err := keypairs.Delete(client.ServiceClient(), "deletedkey", deleteOpts).ExtractErr() + err := keypairs.Delete(context.TODO(), client.ServiceClient(), "deletedkey", deleteOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/limits/requests.go b/openstack/compute/v2/extensions/limits/requests.go index 946e80d5d7..67223016f4 100644 --- a/openstack/compute/v2/extensions/limits/requests.go +++ b/openstack/compute/v2/extensions/limits/requests.go @@ -1,6 +1,8 @@ package limits import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) @@ -23,7 +25,7 @@ func (opts GetOpts) ToLimitsQuery() (string, error) { } // Get returns the limits about the currently scoped tenant. -func Get(client *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { +func Get(ctx context.Context, client *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { url := getURL(client) if opts != nil { query, err := opts.ToLimitsQuery() @@ -34,7 +36,7 @@ func Get(client *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { url += query } - resp, err := client.Get(url, &r.Body, nil) + resp, err := client.Get(ctx, url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/limits/testing/requests_test.go b/openstack/compute/v2/extensions/limits/testing/requests_test.go index d4196c0087..33dbbdc5ce 100644 --- a/openstack/compute/v2/extensions/limits/testing/requests_test.go +++ b/openstack/compute/v2/extensions/limits/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/limits" @@ -17,7 +18,7 @@ func TestGet(t *testing.T) { TenantID: TenantID, } - actual, err := limits.Get(client.ServiceClient(), getOpts).Extract() + actual, err := limits.Get(context.TODO(), client.ServiceClient(), getOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &LimitsResult, actual) } diff --git a/openstack/compute/v2/extensions/lockunlock/requests.go b/openstack/compute/v2/extensions/lockunlock/requests.go index 1d7c070994..dd88824303 100644 --- a/openstack/compute/v2/extensions/lockunlock/requests.go +++ b/openstack/compute/v2/extensions/lockunlock/requests.go @@ -1,20 +1,22 @@ package lockunlock import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Lock is the operation responsible for locking a Compute server. -func Lock(client *gophercloud.ServiceClient, id string) (r LockResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) +func Lock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r LockResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unlock is the operation responsible for unlocking a Compute server. -func Unlock(client *gophercloud.ServiceClient, id string) (r UnlockResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) +func Unlock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnlockResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/lockunlock/testing/request_test.go b/openstack/compute/v2/extensions/lockunlock/testing/request_test.go index 8e8ef4996e..7d1a982d8a 100644 --- a/openstack/compute/v2/extensions/lockunlock/testing/request_test.go +++ b/openstack/compute/v2/extensions/lockunlock/testing/request_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" @@ -16,7 +17,7 @@ func TestLock(t *testing.T) { mockStartServerResponse(t, serverID) - err := lockunlock.Lock(client.ServiceClient(), serverID).ExtractErr() + err := lockunlock.Lock(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } @@ -26,6 +27,6 @@ func TestUnlock(t *testing.T) { mockStopServerResponse(t, serverID) - err := lockunlock.Unlock(client.ServiceClient(), serverID).ExtractErr() + err := lockunlock.Unlock(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/migrate/requests.go b/openstack/compute/v2/extensions/migrate/requests.go index 429f50425b..d28bd179c4 100644 --- a/openstack/compute/v2/extensions/migrate/requests.go +++ b/openstack/compute/v2/extensions/migrate/requests.go @@ -1,13 +1,15 @@ package migrate import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Migrate will initiate a migration of the instance to another host. -func Migrate(client *gophercloud.ServiceClient, id string) (r MigrateResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) +func Migrate(ctx context.Context, client *gophercloud.ServiceClient, id string) (r MigrateResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -41,13 +43,13 @@ func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]interface{}, error) { } // LiveMigrate will initiate a live-migration (without rebooting) of the instance to another host. -func LiveMigrate(client *gophercloud.ServiceClient, id string, opts LiveMigrateOptsBuilder) (r MigrateResult) { +func LiveMigrate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts LiveMigrateOptsBuilder) (r MigrateResult) { b, err := opts.ToLiveMigrateMap() if err != nil { r.Err = err return } - resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) + resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/migrate/testing/requests_test.go b/openstack/compute/v2/extensions/migrate/testing/requests_test.go index 2d21cd73a5..c57687f001 100644 --- a/openstack/compute/v2/extensions/migrate/testing/requests_test.go +++ b/openstack/compute/v2/extensions/migrate/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/migrate" @@ -16,7 +17,7 @@ func TestMigrate(t *testing.T) { mockMigrateResponse(t, serverID) - err := migrate.Migrate(client.ServiceClient(), serverID).ExtractErr() + err := migrate.Migrate(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } @@ -36,6 +37,6 @@ func TestLiveMigrate(t *testing.T) { DiskOverCommit: &diskOverCommit, } - err := migrate.LiveMigrate(client.ServiceClient(), serverID, migrationOpts).ExtractErr() + err := migrate.LiveMigrate(context.TODO(), client.ServiceClient(), serverID, migrationOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/networks/requests.go b/openstack/compute/v2/extensions/networks/requests.go index b1309d49ab..3957a6faf2 100644 --- a/openstack/compute/v2/extensions/networks/requests.go +++ b/openstack/compute/v2/extensions/networks/requests.go @@ -1,6 +1,8 @@ package networks import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -13,8 +15,8 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { } // Get returns data about a previously created Network. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/networks/testing/requests_test.go b/openstack/compute/v2/extensions/networks/testing/requests_test.go index 080de3b284..2fbc9503bf 100644 --- a/openstack/compute/v2/extensions/networks/testing/requests_test.go +++ b/openstack/compute/v2/extensions/networks/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" @@ -15,7 +16,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := networks.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := networks.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := networks.ExtractNetworks(page) th.AssertNoErr(t, err) @@ -32,7 +33,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := networks.Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() + actual, err := networks.Get(context.TODO(), client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &SecondNetwork, actual) } diff --git a/openstack/compute/v2/extensions/pauseunpause/requests.go b/openstack/compute/v2/extensions/pauseunpause/requests.go index 10edf4d649..8e17792f46 100644 --- a/openstack/compute/v2/extensions/pauseunpause/requests.go +++ b/openstack/compute/v2/extensions/pauseunpause/requests.go @@ -1,20 +1,22 @@ package pauseunpause import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Pause is the operation responsible for pausing a Compute server. -func Pause(client *gophercloud.ServiceClient, id string) (r PauseResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) +func Pause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r PauseResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unpause is the operation responsible for unpausing a Compute server. -func Unpause(client *gophercloud.ServiceClient, id string) (r UnpauseResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) +func Unpause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnpauseResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go b/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go index 6fbf5d41df..61185bb730 100644 --- a/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go +++ b/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" @@ -16,7 +17,7 @@ func TestPause(t *testing.T) { mockPauseServerResponse(t, serverID) - err := pauseunpause.Pause(client.ServiceClient(), serverID).ExtractErr() + err := pauseunpause.Pause(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } @@ -26,6 +27,6 @@ func TestUnpause(t *testing.T) { mockUnpauseServerResponse(t, serverID) - err := pauseunpause.Unpause(client.ServiceClient(), serverID).ExtractErr() + err := pauseunpause.Unpause(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/extensions/quotasets/requests.go index ade73d8e86..a7ff87c4d2 100644 --- a/openstack/compute/v2/extensions/quotasets/requests.go +++ b/openstack/compute/v2/extensions/quotasets/requests.go @@ -1,39 +1,41 @@ package quotasets import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // Get returns public 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, tenantID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, tenantID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetDetail returns detailed public data about a previously created QuotaSet. -func GetDetail(client *gophercloud.ServiceClient, tenantID string) (r GetDetailResult) { - resp, err := client.Get(getDetailURL(client, tenantID), &r.Body, nil) +func GetDetail(ctx context.Context, client *gophercloud.ServiceClient, tenantID string) (r GetDetailResult) { + resp, err := client.Get(ctx, getDetailURL(client, tenantID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Updates the quotas for the given tenantID and returns the new QuotaSet. -func Update(client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, tenantID string, opts UpdateOptsBuilder) (r UpdateResult) { reqBody, err := opts.ToComputeQuotaUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, tenantID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Put(ctx, updateURL(client, tenantID), reqBody, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Resets the quotas for the given tenant to their default values. -func Delete(client *gophercloud.ServiceClient, tenantID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, tenantID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, tenantID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, tenantID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go index e9ec586454..75f1364b74 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go +++ b/openstack/compute/v2/extensions/quotasets/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "errors" "testing" @@ -14,7 +15,7 @@ func TestGet(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := quotasets.Get(client.ServiceClient(), FirstTenantID).Extract() + actual, err := quotasets.Get(context.TODO(), client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstQuotaSet, actual) } @@ -23,7 +24,7 @@ func TestGetDetail(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleGetDetailSuccessfully(t) - actual, err := quotasets.GetDetail(client.ServiceClient(), FirstTenantID).Extract() + actual, err := quotasets.GetDetail(context.TODO(), client.ServiceClient(), FirstTenantID).Extract() th.CheckDeepEquals(t, FirstQuotaDetailsSet, actual) th.AssertNoErr(t, err) } @@ -32,7 +33,7 @@ func TestUpdate(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePutSuccessfully(t) - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, UpdatedQuotaSet).Extract() + actual, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, UpdatedQuotaSet).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstQuotaSet, actual) } @@ -42,7 +43,7 @@ func TestPartialUpdate(t *testing.T) { defer th.TeardownHTTP() HandlePartialPutSuccessfully(t) opts := quotasets.UpdateOpts{Cores: gophercloud.IntToPointer(200), Force: true} - actual, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() + actual, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, opts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstQuotaSet, actual) } @@ -51,7 +52,7 @@ func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - _, err := quotasets.Delete(client.ServiceClient(), FirstTenantID).Extract() + _, err := quotasets.Delete(context.TODO(), client.ServiceClient(), FirstTenantID).Extract() th.AssertNoErr(t, err) } @@ -66,7 +67,7 @@ func TestErrorInToComputeQuotaUpdateMap(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandlePutSuccessfully(t) - _, err := quotasets.Update(client.ServiceClient(), FirstTenantID, opts).Extract() + _, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, opts).Extract() if err == nil { t.Fatal("Error handling failed") } diff --git a/openstack/compute/v2/extensions/remoteconsoles/requests.go b/openstack/compute/v2/extensions/remoteconsoles/requests.go index 88dc6dbc8a..7b26b9e42d 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/requests.go +++ b/openstack/compute/v2/extensions/remoteconsoles/requests.go @@ -1,6 +1,8 @@ package remoteconsoles import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) @@ -69,14 +71,14 @@ func (opts CreateOpts) ToRemoteConsoleCreateMap() (map[string]interface{}, error } // Create requests the creation of a new remote console on the specified server. -func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToRemoteConsoleCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client, serverID), reqBody, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client, serverID), reqBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go b/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go index ed53447636..04b7546dd7 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go +++ b/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -31,7 +32,7 @@ func TestCreate(t *testing.T) { Protocol: remoteconsoles.ConsoleProtocolVNC, Type: remoteconsoles.ConsoleTypeNoVNC, } - s, err := remoteconsoles.Create(fake.ServiceClient(), "b16ba811-199d-4ffd-8839-ba96c1185a67", opts).Extract() + s, err := remoteconsoles.Create(context.TODO(), fake.ServiceClient(), "b16ba811-199d-4ffd-8839-ba96c1185a67", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Protocol, string(remoteconsoles.ConsoleProtocolVNC)) diff --git a/openstack/compute/v2/extensions/rescueunrescue/requests.go b/openstack/compute/v2/extensions/rescueunrescue/requests.go index 82ac651662..26614dbc47 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/requests.go +++ b/openstack/compute/v2/extensions/rescueunrescue/requests.go @@ -1,6 +1,8 @@ package rescueunrescue import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) @@ -32,13 +34,13 @@ func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { } // Rescue instructs the provider to place the server into RESCUE mode. -func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { +func Rescue(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { b, err := opts.ToServerRescueMap() if err != nil { r.Err = err return } - resp, err := client.Post(extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -46,8 +48,8 @@ func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder } // Unrescue instructs the provider to return the server from RESCUE mode. -func Unrescue(client *gophercloud.ServiceClient, id string) (r UnrescueResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) +func Unrescue(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnrescueResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go b/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go index 852018eb4b..a69982ffcd 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go +++ b/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -23,7 +24,7 @@ func TestRescue(t *testing.T) { fmt.Fprintf(w, RescueResult) }) - s, err := rescueunrescue.Rescue(fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488", rescueunrescue.RescueOpts{ + s, err := rescueunrescue.Rescue(context.TODO(), fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488", rescueunrescue.RescueOpts{ AdminPass: "aUPtawPzE9NU", RescueImageRef: "115e5c5b-72f0-4a0a-9067-60706545248c", }).Extract() @@ -44,6 +45,6 @@ func TestUnrescue(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) - err := rescueunrescue.Unrescue(fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488").ExtractErr() + err := rescueunrescue.Unrescue(context.TODO(), fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/resetnetwork/requests.go b/openstack/compute/v2/extensions/resetnetwork/requests.go index 222807eca3..55092f32f1 100644 --- a/openstack/compute/v2/extensions/resetnetwork/requests.go +++ b/openstack/compute/v2/extensions/resetnetwork/requests.go @@ -1,16 +1,18 @@ package resetnetwork import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // ResetNetwork will reset the network of a server -func ResetNetwork(client *gophercloud.ServiceClient, id string) (r ResetResult) { +func ResetNetwork(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResetResult) { b := map[string]interface{}{ "resetNetwork": nil, } - resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) + resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go b/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go index 1ae35531bb..29979eab32 100644 --- a/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go +++ b/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/resetnetwork" @@ -16,6 +17,6 @@ func TestResetNetwork(t *testing.T) { mockResetNetworkResponse(t, serverID) - err := resetnetwork.ResetNetwork(client.ServiceClient(), serverID).ExtractErr() + err := resetnetwork.ResetNetwork(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/resetstate/requests.go b/openstack/compute/v2/extensions/resetstate/requests.go index 6b49d32ec2..2a63f26f70 100644 --- a/openstack/compute/v2/extensions/resetstate/requests.go +++ b/openstack/compute/v2/extensions/resetstate/requests.go @@ -1,6 +1,8 @@ package resetstate import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) @@ -17,9 +19,9 @@ const ( ) // ResetState will reset the state of a server -func ResetState(client *gophercloud.ServiceClient, id string, state ServerState) (r ResetResult) { +func ResetState(ctx context.Context, client *gophercloud.ServiceClient, id string, state ServerState) (r ResetResult) { stateMap := map[string]interface{}{"state": state} - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/resetstate/testing/requests_test.go b/openstack/compute/v2/extensions/resetstate/testing/requests_test.go index a3f35ca19b..7ae25169df 100644 --- a/openstack/compute/v2/extensions/resetstate/testing/requests_test.go +++ b/openstack/compute/v2/extensions/resetstate/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/resetstate" @@ -16,6 +17,6 @@ func TestResetState(t *testing.T) { mockResetStateResponse(t, serverID, "active") - err := resetstate.ResetState(client.ServiceClient(), serverID, "active").ExtractErr() + err := resetstate.ResetState(context.TODO(), client.ServiceClient(), serverID, "active").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/extensions/secgroups/requests.go index b5df71f756..5c9102e1a0 100644 --- a/openstack/compute/v2/extensions/secgroups/requests.go +++ b/openstack/compute/v2/extensions/secgroups/requests.go @@ -1,6 +1,8 @@ package secgroups import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -43,13 +45,13 @@ func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { } // Create will create a new security group. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecGroupCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -77,13 +79,13 @@ func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { // Update will modify the mutable properties of a security group, notably its // name and description. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSecGroupUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -91,15 +93,15 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Get will return details for a particular security group. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(resourceURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a security group from the project. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(resourceURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, resourceURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -149,13 +151,13 @@ func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { // CreateRule will add a new rule to an existing security group (whose ID is // specified in CreateRuleOpts). You have the option of controlling inbound // traffic from either an IP range (CIDR) or from another security group. -func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) (r CreateRuleResult) { +func CreateRule(ctx context.Context, client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) (r CreateRuleResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -163,8 +165,8 @@ func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) ( } // DeleteRule will permanently delete a rule from a security group. -func DeleteRule(client *gophercloud.ServiceClient, id string) (r DeleteRuleResult) { - resp, err := client.Delete(resourceRuleURL(client, id), nil) +func DeleteRule(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteRuleResult) { + resp, err := client.Delete(ctx, resourceRuleURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,15 +179,15 @@ func actionMap(prefix, groupName string) map[string]map[string]string { // AddServer will associate a server and a security group, enforcing the // rules of the group on the server. -func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r AddServerResult) { - resp, err := client.Post(serverActionURL(client, serverID), actionMap("add", groupName), nil, nil) +func AddServer(ctx context.Context, client *gophercloud.ServiceClient, serverID, groupName string) (r AddServerResult) { + resp, err := client.Post(ctx, serverActionURL(client, serverID), actionMap("add", groupName), nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RemoveServer will disassociate a server from a security group. -func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r RemoveServerResult) { - resp, err := client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), nil, nil) +func RemoveServer(ctx context.Context, client *gophercloud.ServiceClient, serverID, groupName string) (r RemoveServerResult) { + resp, err := client.Post(ctx, serverActionURL(client, serverID), actionMap("remove", groupName), nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go b/openstack/compute/v2/extensions/secgroups/testing/requests_test.go index 0f7d2cedea..73dc1f1fe7 100644 --- a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go +++ b/openstack/compute/v2/extensions/secgroups/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" @@ -23,7 +24,7 @@ func TestList(t *testing.T) { count := 0 - err := secgroups.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := secgroups.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := secgroups.ExtractSecurityGroups(page) if err != nil { @@ -58,7 +59,7 @@ func TestListByServer(t *testing.T) { count := 0 - err := secgroups.ListByServer(client.ServiceClient(), serverID).EachPage(func(page pagination.Page) (bool, error) { + err := secgroups.ListByServer(client.ServiceClient(), serverID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := secgroups.ExtractSecurityGroups(page) if err != nil { @@ -96,7 +97,7 @@ func TestCreate(t *testing.T) { Description: "something", } - group, err := secgroups.Create(client.ServiceClient(), opts).Extract() + group, err := secgroups.Create(context.TODO(), client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ @@ -121,7 +122,7 @@ func TestUpdate(t *testing.T) { Description: &description, } - group, err := secgroups.Update(client.ServiceClient(), groupID, opts).Extract() + group, err := secgroups.Update(context.TODO(), client.ServiceClient(), groupID, opts).Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ @@ -140,7 +141,7 @@ func TestGet(t *testing.T) { mockGetGroupsResponse(t, groupID) - group, err := secgroups.Get(client.ServiceClient(), groupID).Extract() + group, err := secgroups.Get(context.TODO(), client.ServiceClient(), groupID).Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ @@ -172,7 +173,7 @@ func TestGetNumericID(t *testing.T) { mockGetNumericIDGroupResponse(t, numericGroupID) - group, err := secgroups.Get(client.ServiceClient(), "12345").Extract() + group, err := secgroups.Get(context.TODO(), client.ServiceClient(), "12345").Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ID: "12345"} @@ -187,7 +188,7 @@ func TestGetNumericRuleID(t *testing.T) { mockGetNumericIDGroupRuleResponse(t, numericGroupID) - group, err := secgroups.Get(client.ServiceClient(), "12345").Extract() + group, err := secgroups.Get(context.TODO(), client.ServiceClient(), "12345").Extract() th.AssertNoErr(t, err) expected := &secgroups.SecurityGroup{ @@ -208,7 +209,7 @@ func TestDelete(t *testing.T) { mockDeleteGroupResponse(t, groupID) - err := secgroups.Delete(client.ServiceClient(), groupID).ExtractErr() + err := secgroups.Delete(context.TODO(), client.ServiceClient(), groupID).ExtractErr() th.AssertNoErr(t, err) } @@ -226,7 +227,7 @@ func TestAddRule(t *testing.T) { CIDR: "0.0.0.0/0", } - rule, err := secgroups.CreateRule(client.ServiceClient(), opts).Extract() + rule, err := secgroups.CreateRule(context.TODO(), client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &secgroups.Rule{ @@ -256,7 +257,7 @@ func TestAddRuleICMPZero(t *testing.T) { CIDR: "0.0.0.0/0", } - rule, err := secgroups.CreateRule(client.ServiceClient(), opts).Extract() + rule, err := secgroups.CreateRule(context.TODO(), client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := &secgroups.Rule{ @@ -278,7 +279,7 @@ func TestDeleteRule(t *testing.T) { mockDeleteRuleResponse(t, ruleID) - err := secgroups.DeleteRule(client.ServiceClient(), ruleID).ExtractErr() + err := secgroups.DeleteRule(context.TODO(), client.ServiceClient(), ruleID).ExtractErr() th.AssertNoErr(t, err) } @@ -288,7 +289,7 @@ func TestAddServer(t *testing.T) { mockAddServerToGroupResponse(t, serverID) - err := secgroups.AddServer(client.ServiceClient(), serverID, "test").ExtractErr() + err := secgroups.AddServer(context.TODO(), client.ServiceClient(), serverID, "test").ExtractErr() th.AssertNoErr(t, err) } @@ -298,6 +299,6 @@ func TestRemoveServer(t *testing.T) { mockRemoveServerFromGroupResponse(t, serverID) - err := secgroups.RemoveServer(client.ServiceClient(), serverID, "test").ExtractErr() + err := secgroups.RemoveServer(context.TODO(), client.ServiceClient(), serverID, "test").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/extensions/servergroups/requests.go index eac23c2ad2..2fd10d4d34 100644 --- a/openstack/compute/v2/extensions/servergroups/requests.go +++ b/openstack/compute/v2/extensions/servergroups/requests.go @@ -1,6 +1,8 @@ package servergroups import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -72,13 +74,13 @@ func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) } // Create requests the creation of a new Server Group. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToServerGroupCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -86,15 +88,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get returns data about a previously created ServerGroup. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previously allocated ServerGroup. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go index ec80002fc6..3557f81079 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go +++ b/openstack/compute/v2/extensions/servergroups/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" @@ -15,7 +16,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := servergroups.List(client.ServiceClient(), &servergroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := servergroups.List(client.ServiceClient(), &servergroups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := servergroups.ExtractServerGroups(page) th.AssertNoErr(t, err) @@ -32,7 +33,7 @@ func TestCreate(t *testing.T) { defer th.TeardownHTTP() HandleCreateSuccessfully(t) - actual, err := servergroups.Create(client.ServiceClient(), servergroups.CreateOpts{ + actual, err := servergroups.Create(context.TODO(), client.ServiceClient(), servergroups.CreateOpts{ Name: "test", Policies: []string{"anti-affinity"}, }).Extract() @@ -52,7 +53,7 @@ func TestCreateMicroversion(t *testing.T) { CreatedServerGroup.Policy = &policy CreatedServerGroup.Rules = &rules - result := servergroups.Create(client.ServiceClient(), servergroups.CreateOpts{ + result := servergroups.Create(context.TODO(), client.ServiceClient(), servergroups.CreateOpts{ Name: "test", Policies: []string{"anti-affinity"}, Policy: policy, @@ -69,7 +70,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := servergroups.Get(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0").Extract() + actual, err := servergroups.Get(context.TODO(), client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstServerGroup, actual) } @@ -86,7 +87,7 @@ func TestGetMicroversion(t *testing.T) { FirstServerGroup.Policy = &policy FirstServerGroup.Rules = &rules - result := servergroups.Get(client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0") + result := servergroups.Get(context.TODO(), client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0") // Extract basic fields. actual, err := result.Extract() @@ -99,6 +100,6 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := servergroups.Delete(client.ServiceClient(), "616fb98f-46ca-475e-917e-2563e5a8cd19").ExtractErr() + err := servergroups.Delete(context.TODO(), client.ServiceClient(), "616fb98f-46ca-475e-917e-2563e5a8cd19").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/serverusage/testing/requests_test.go b/openstack/compute/v2/extensions/serverusage/testing/requests_test.go index a7afc30d16..c81b4553f0 100644 --- a/openstack/compute/v2/extensions/serverusage/testing/requests_test.go +++ b/openstack/compute/v2/extensions/serverusage/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -29,7 +30,7 @@ func TestServerWithUsageExt(t *testing.T) { serverusage.UsageExt } var serverWithUsageExt serverUsageExt - err := servers.Get(fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) + err := servers.Get(context.TODO(), fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) th.AssertNoErr(t, err) th.AssertEquals(t, serverWithUsageExt.LaunchedAt, time.Date(2018, 07, 27, 9, 15, 55, 0, time.UTC)) diff --git a/openstack/compute/v2/extensions/services/requests.go b/openstack/compute/v2/extensions/services/requests.go index d7a8dccc7c..f979dd5dee 100644 --- a/openstack/compute/v2/extensions/services/requests.go +++ b/openstack/compute/v2/extensions/services/requests.go @@ -1,6 +1,8 @@ package services import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -68,13 +70,13 @@ func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { } // Update requests that various attributes of the indicated service be changed. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToServiceUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -82,8 +84,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r Up } // 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{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, updateURL(client, id), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/extensions/services/testing/requests_test.go b/openstack/compute/v2/extensions/services/testing/requests_test.go index f3a34dd919..3bbcb478c2 100644 --- a/openstack/compute/v2/extensions/services/testing/requests_test.go +++ b/openstack/compute/v2/extensions/services/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/services" @@ -15,7 +16,7 @@ func TestListServicesPre253(t *testing.T) { HandleListPre253Successfully(t) pages := 0 - err := services.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := services.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := services.ExtractServices(page) @@ -51,7 +52,7 @@ func TestListServices(t *testing.T) { Binary: "fake-binary", Host: "host123", } - err := services.List(client.ServiceClient(), opts).EachPage(func(page pagination.Page) (bool, error) { + err := services.List(client.ServiceClient(), opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := services.ExtractServices(page) @@ -83,7 +84,7 @@ func TestUpdateService(t *testing.T) { HandleUpdateSuccessfully(t) client := client.ServiceClient() - actual, err := services.Update(client, "fake-service-id", services.UpdateOpts{Status: services.ServiceDisabled}).Extract() + actual, err := services.Update(context.TODO(), client, "fake-service-id", services.UpdateOpts{Status: services.ServiceDisabled}).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } @@ -97,7 +98,7 @@ func TestDeleteService(t *testing.T) { HandleDeleteSuccessfully(t) client := client.ServiceClient() - res := services.Delete(client, "fake-service-id") + res := services.Delete(context.TODO(), client, "fake-service-id") testhelper.AssertNoErr(t, res.Err) } diff --git a/openstack/compute/v2/extensions/shelveunshelve/requests.go b/openstack/compute/v2/extensions/shelveunshelve/requests.go index 438debb9e8..2f5551c0b4 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/requests.go +++ b/openstack/compute/v2/extensions/shelveunshelve/requests.go @@ -1,20 +1,22 @@ package shelveunshelve import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Shelve is the operation responsible for shelving a Compute server. -func Shelve(client *gophercloud.ServiceClient, id string) (r ShelveResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) +func Shelve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ShelveOffload is the operation responsible for Shelve-Offload a Compute server. -func ShelveOffload(client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) +func ShelveOffload(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -48,13 +50,13 @@ func (opts UnshelveOpts) ToUnshelveMap() (map[string]interface{}, error) { } // Unshelve is the operation responsible for unshelve a Compute server. -func Unshelve(client *gophercloud.ServiceClient, id string, opts UnshelveOptsBuilder) (r UnshelveResult) { +func Unshelve(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UnshelveOptsBuilder) (r UnshelveResult) { b, err := opts.ToUnshelveMap() if err != nil { r.Err = err return } - resp, err := client.Post(extensions.ActionURL(client, id), b, nil, nil) + resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go b/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go index 45f1b27035..60f9304d1f 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go +++ b/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/shelveunshelve" @@ -17,7 +18,7 @@ func TestShelve(t *testing.T) { mockShelveServerResponse(t, serverID) - err := shelveunshelve.Shelve(client.ServiceClient(), serverID).ExtractErr() + err := shelveunshelve.Shelve(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } @@ -27,7 +28,7 @@ func TestShelveOffload(t *testing.T) { mockShelveOffloadServerResponse(t, serverID) - err := shelveunshelve.ShelveOffload(client.ServiceClient(), serverID).ExtractErr() + err := shelveunshelve.ShelveOffload(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } @@ -39,7 +40,7 @@ func TestUnshelveNoAvailabilityZone(t *testing.T) { mockUnshelveServerResponseNoAvailabilityZone(t, serverID) - err := shelveunshelve.Unshelve(client.ServiceClient(), serverID, unshelveOpts).ExtractErr() + err := shelveunshelve.Unshelve(context.TODO(), client.ServiceClient(), serverID, unshelveOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -53,6 +54,6 @@ func TestUnshelveWithAvailabilityZone(t *testing.T) { mockUnshelveServerResponseWithAvailabilityZone(t, serverID, availabilityZone) - err := shelveunshelve.Unshelve(client.ServiceClient(), serverID, unshelveOpts).ExtractErr() + err := shelveunshelve.Unshelve(context.TODO(), client.ServiceClient(), serverID, unshelveOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/startstop/requests.go b/openstack/compute/v2/extensions/startstop/requests.go index b30c8f5738..9b430ef7d7 100644 --- a/openstack/compute/v2/extensions/startstop/requests.go +++ b/openstack/compute/v2/extensions/startstop/requests.go @@ -1,20 +1,22 @@ package startstop import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Start is the operation responsible for starting a Compute server. -func Start(client *gophercloud.ServiceClient, id string) (r StartResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) +func Start(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StartResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Stop is the operation responsible for stopping a Compute server. -func Stop(client *gophercloud.ServiceClient, id string) (r StopResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) +func Stop(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StopResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/startstop/testing/requests_test.go b/openstack/compute/v2/extensions/startstop/testing/requests_test.go index 1d1a66f75b..fc65e76ed2 100644 --- a/openstack/compute/v2/extensions/startstop/testing/requests_test.go +++ b/openstack/compute/v2/extensions/startstop/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/startstop" @@ -16,7 +17,7 @@ func TestStart(t *testing.T) { mockStartServerResponse(t, serverID) - err := startstop.Start(client.ServiceClient(), serverID).ExtractErr() + err := startstop.Start(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } @@ -26,6 +27,6 @@ func TestStop(t *testing.T) { mockStopServerResponse(t, serverID) - err := startstop.Stop(client.ServiceClient(), serverID).ExtractErr() + err := startstop.Stop(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/suspendresume/requests.go b/openstack/compute/v2/extensions/suspendresume/requests.go index 8959d4416b..0ffd5f14fc 100644 --- a/openstack/compute/v2/extensions/suspendresume/requests.go +++ b/openstack/compute/v2/extensions/suspendresume/requests.go @@ -1,20 +1,22 @@ package suspendresume import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" ) // Suspend is the operation responsible for suspending a Compute server. -func Suspend(client *gophercloud.ServiceClient, id string) (r SuspendResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) +func Suspend(ctx context.Context, client *gophercloud.ServiceClient, id string) (r SuspendResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Resume is the operation responsible for resuming a Compute server. -func Resume(client *gophercloud.ServiceClient, id string) (r UnsuspendResult) { - resp, err := client.Post(extensions.ActionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) +func Resume(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnsuspendResult) { + resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go b/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go index 1ae7c80662..27f13aa592 100644 --- a/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go +++ b/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/suspendresume" @@ -16,7 +17,7 @@ func TestSuspend(t *testing.T) { mockSuspendServerResponse(t, serverID) - err := suspendresume.Suspend(client.ServiceClient(), serverID).ExtractErr() + err := suspendresume.Suspend(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } @@ -26,6 +27,6 @@ func TestResume(t *testing.T) { mockResumeServerResponse(t, serverID) - err := suspendresume.Resume(client.ServiceClient(), serverID).ExtractErr() + err := suspendresume.Resume(context.TODO(), client.ServiceClient(), serverID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/tags/requests.go b/openstack/compute/v2/extensions/tags/requests.go index bdea66b4c6..759b579d3d 100644 --- a/openstack/compute/v2/extensions/tags/requests.go +++ b/openstack/compute/v2/extensions/tags/requests.go @@ -1,11 +1,15 @@ package tags -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // List all tags on a server. -func List(client *gophercloud.ServiceClient, serverID string) (r ListResult) { +func List(ctx context.Context, client *gophercloud.ServiceClient, serverID string) (r ListResult) { url := listURL(client, serverID) - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -13,9 +17,9 @@ func List(client *gophercloud.ServiceClient, serverID string) (r ListResult) { } // Check if a tag exists on a server. -func Check(client *gophercloud.ServiceClient, serverID, tag string) (r CheckResult) { +func Check(ctx context.Context, client *gophercloud.ServiceClient, serverID, tag string) (r CheckResult) { url := checkURL(client, serverID, tag) - resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -38,14 +42,14 @@ func (opts ReplaceAllOpts) ToTagsReplaceAllMap() (map[string]interface{}, error) } // ReplaceAll replaces all Tags on a server. -func ReplaceAll(client *gophercloud.ServiceClient, serverID string, opts ReplaceAllOptsBuilder) (r ReplaceAllResult) { +func ReplaceAll(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts ReplaceAllOptsBuilder) (r ReplaceAllResult) { b, err := opts.ToTagsReplaceAllMap() url := replaceAllURL(client, serverID) if err != nil { r.Err = err return } - resp, err := client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, url, &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -53,9 +57,9 @@ func ReplaceAll(client *gophercloud.ServiceClient, serverID string, opts Replace } // Add adds a new Tag on a server. -func Add(client *gophercloud.ServiceClient, serverID, tag string) (r AddResult) { +func Add(ctx context.Context, client *gophercloud.ServiceClient, serverID, tag string) (r AddResult) { url := addURL(client, serverID, tag) - resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -63,9 +67,9 @@ func Add(client *gophercloud.ServiceClient, serverID, tag string) (r AddResult) } // Delete removes a tag from a server. -func Delete(client *gophercloud.ServiceClient, serverID, tag string) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, serverID, tag string) (r DeleteResult) { url := deleteURL(client, serverID, tag) - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -73,9 +77,9 @@ func Delete(client *gophercloud.ServiceClient, serverID, tag string) (r DeleteRe } // DeleteAll removes all tag from a server. -func DeleteAll(client *gophercloud.ServiceClient, serverID string) (r DeleteResult) { +func DeleteAll(ctx context.Context, client *gophercloud.ServiceClient, serverID string) (r DeleteResult) { url := deleteAllURL(client, serverID) - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/extensions/tags/testing/requests_test.go b/openstack/compute/v2/extensions/tags/testing/requests_test.go index 8cb219f175..f106b44769 100644 --- a/openstack/compute/v2/extensions/tags/testing/requests_test.go +++ b/openstack/compute/v2/extensions/tags/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -26,7 +27,7 @@ func TestList(t *testing.T) { }) expected := []string{"foo", "bar", "baz"} - actual, err := tags.List(client.ServiceClient(), "uuid1").Extract() + actual, err := tags.List(context.TODO(), client.ServiceClient(), "uuid1").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) @@ -44,7 +45,7 @@ func TestCheckOk(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - exists, err := tags.Check(client.ServiceClient(), "uuid1", "foo").Extract() + exists, err := tags.Check(context.TODO(), client.ServiceClient(), "uuid1", "foo").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) } @@ -61,7 +62,7 @@ func TestCheckFail(t *testing.T) { w.WriteHeader(http.StatusNotFound) }) - exists, err := tags.Check(client.ServiceClient(), "uuid1", "bar").Extract() + exists, err := tags.Check(context.TODO(), client.ServiceClient(), "uuid1", "bar").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, false, exists) } @@ -82,7 +83,7 @@ func TestReplaceAll(t *testing.T) { }) expected := []string{"tag1", "tag2", "tag3"} - actual, err := tags.ReplaceAll(client.ServiceClient(), "uuid1", tags.ReplaceAllOpts{Tags: []string{"tag1", "tag2", "tag3"}}).Extract() + actual, err := tags.ReplaceAll(context.TODO(), client.ServiceClient(), "uuid1", tags.ReplaceAllOpts{Tags: []string{"tag1", "tag2", "tag3"}}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) @@ -100,7 +101,7 @@ func TestAddCreated(t *testing.T) { w.WriteHeader(http.StatusCreated) }) - err := tags.Add(client.ServiceClient(), "uuid1", "foo").ExtractErr() + err := tags.Add(context.TODO(), client.ServiceClient(), "uuid1", "foo").ExtractErr() th.AssertNoErr(t, err) } @@ -116,7 +117,7 @@ func TestAddExists(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := tags.Add(client.ServiceClient(), "uuid1", "foo").ExtractErr() + err := tags.Add(context.TODO(), client.ServiceClient(), "uuid1", "foo").ExtractErr() th.AssertNoErr(t, err) } @@ -132,7 +133,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := tags.Delete(client.ServiceClient(), "uuid1", "foo").ExtractErr() + err := tags.Delete(context.TODO(), client.ServiceClient(), "uuid1", "foo").ExtractErr() th.AssertNoErr(t, err) } @@ -148,6 +149,6 @@ func TestDeleteAll(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := tags.DeleteAll(client.ServiceClient(), "uuid1").ExtractErr() + err := tags.DeleteAll(context.TODO(), client.ServiceClient(), "uuid1").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/extensions/tenantnetworks/requests.go b/openstack/compute/v2/extensions/tenantnetworks/requests.go index 79574a31e4..bbc825a227 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/requests.go +++ b/openstack/compute/v2/extensions/tenantnetworks/requests.go @@ -1,6 +1,8 @@ package tenantnetworks import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -13,8 +15,8 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { } // Get returns data about a previously created Network. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go b/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go index a5427e930b..092c391aca 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go +++ b/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" @@ -15,7 +16,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := tenantnetworks.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := tenantnetworks.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := tenantnetworks.ExtractNetworks(page) th.AssertNoErr(t, err) @@ -32,7 +33,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := tenantnetworks.Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() + actual, err := tenantnetworks.Get(context.TODO(), client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &SecondNetwork, actual) } diff --git a/openstack/compute/v2/extensions/testing/delegate_test.go b/openstack/compute/v2/extensions/testing/delegate_test.go index b776a66e6e..c0897a16c7 100644 --- a/openstack/compute/v2/extensions/testing/delegate_test.go +++ b/openstack/compute/v2/extensions/testing/delegate_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" @@ -17,7 +18,7 @@ func TestList(t *testing.T) { HandleListExtensionsSuccessfully(t) count := 0 - extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + extensions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) th.AssertNoErr(t, err) @@ -45,7 +46,7 @@ func TestGet(t *testing.T) { HandleGetExtensionsSuccessfully(t) - ext, err := extensions.Get(client.ServiceClient(), "agent").Extract() + ext, err := extensions.Get(context.TODO(), client.ServiceClient(), "agent").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00") diff --git a/openstack/compute/v2/extensions/usage/testing/requests_test.go b/openstack/compute/v2/extensions/usage/testing/requests_test.go index 464b650507..2aeab1199c 100644 --- a/openstack/compute/v2/extensions/usage/testing/requests_test.go +++ b/openstack/compute/v2/extensions/usage/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/usage" @@ -15,7 +16,7 @@ func TestGetTenant(t *testing.T) { HandleGetSingleTenantSuccessfully(t) count := 0 - err := usage.SingleTenant(client.ServiceClient(), FirstTenantID, nil).EachPage(func(page pagination.Page) (bool, error) { + err := usage.SingleTenant(client.ServiceClient(), FirstTenantID, nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := usage.ExtractSingleTenant(page) @@ -38,7 +39,7 @@ func TestAllTenants(t *testing.T) { } count := 0 - err := usage.AllTenants(client.ServiceClient(), getOpts).EachPage(func(page pagination.Page) (bool, error) { + err := usage.AllTenants(client.ServiceClient(), getOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := usage.ExtractAllTenants(page) diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/extensions/volumeattach/requests.go index 09d5eb529f..12b70aae68 100644 --- a/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/openstack/compute/v2/extensions/volumeattach/requests.go @@ -1,6 +1,8 @@ package volumeattach import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -42,13 +44,13 @@ func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, er } // Create requests the creation of a new volume attachment on the server. -func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeAttachmentCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -56,16 +58,16 @@ func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsB } // Get returns public data about a previously created VolumeAttachment. -func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) { - resp, err := client.Get(getURL(client, serverID, attachmentID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, serverID, attachmentID), &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(ctx context.Context, client *gophercloud.ServiceClient, serverID, attachmentID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, serverID, attachmentID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go index 79e5913e68..1d0884a073 100644 --- a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go +++ b/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/volumeattach" @@ -51,7 +52,7 @@ func TestList(t *testing.T) { serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" count := 0 - err := volumeattach.List(client.ServiceClient(), serverID).EachPage(func(page pagination.Page) (bool, error) { + err := volumeattach.List(client.ServiceClient(), serverID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := volumeattach.ExtractVolumeAttachments(page) th.AssertNoErr(t, err) @@ -71,7 +72,7 @@ func TestCreate(t *testing.T) { serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" - actual, err := volumeattach.Create(client.ServiceClient(), serverID, volumeattach.CreateOpts{ + actual, err := volumeattach.Create(context.TODO(), client.ServiceClient(), serverID, volumeattach.CreateOpts{ Device: "/dev/vdc", VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", Tag: iTag, @@ -90,7 +91,7 @@ func TestGet(t *testing.T) { aID := "a26887c6-c47b-4654-abb5-dfadf7d3f804" serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" - actual, err := volumeattach.Get(client.ServiceClient(), serverID, aID).Extract() + actual, err := volumeattach.Get(context.TODO(), client.ServiceClient(), serverID, aID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &SecondVolumeAttachment, actual) } @@ -104,6 +105,6 @@ func TestDelete(t *testing.T) { aID := "a26887c6-c47b-4654-abb5-dfadf7d3f804" serverID := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" - err := volumeattach.Delete(client.ServiceClient(), serverID, aID).ExtractErr() + err := volumeattach.Delete(context.TODO(), client.ServiceClient(), serverID, aID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 8694977be9..26bc4f71a8 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -1,6 +1,8 @@ package flavors import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -141,13 +143,13 @@ func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new flavor. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFlavorCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -172,13 +174,13 @@ func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { } // Update requests the update of a new flavor. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFlavorUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -187,15 +189,15 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder // Get retrieves details of a single flavor. Use Extract to convert its // result into a Flavor. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified flavor ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -227,13 +229,13 @@ func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]interface{}, error) } // AddAccess grants a tenant/project access to a flavor. -func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { +func AddAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { b, err := opts.ToFlavorAddAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -258,13 +260,13 @@ func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]interface{}, } // RemoveAccess removes/revokes a tenant/project access to a flavor. -func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { +func RemoveAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { b, err := opts.ToFlavorRemoveAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -272,14 +274,14 @@ func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAcces } // ExtraSpecs requests all the extra-specs for the given flavor ID. -func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { - resp, err := client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) +func ListExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { + resp, err := client.Get(ctx, extraSpecsListURL(client, flavorID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { - resp, err := client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) +func GetExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { + resp, err := client.Get(ctx, extraSpecsGetURL(client, flavorID, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -301,13 +303,13 @@ func (opts ExtraSpecsOpts) ToFlavorExtraSpecsCreateMap() (map[string]interface{} // CreateExtraSpecs will create or update the extra-specs key-value pairs for // the specified Flavor. -func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { +func CreateExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { b, err := opts.ToFlavorExtraSpecsCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -340,13 +342,13 @@ func (opts ExtraSpecsOpts) ToFlavorExtraSpecUpdateMap() (map[string]string, stri // UpdateExtraSpec will updates the value of the specified flavor's extra spec // for the key in opts. -func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { +func UpdateExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { b, key, err := opts.ToFlavorExtraSpecUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -355,8 +357,8 @@ func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts Up // DeleteExtraSpec will delete the key-value pair with the given key for the given // flavor ID. -func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { - resp, err := client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ +func DeleteExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { + resp, err := client.Delete(ctx, extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 7608832c13..fa328680eb 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "reflect" @@ -83,7 +84,7 @@ func TestListFlavors(t *testing.T) { pages := 0 // Get public and private flavors - err := flavors.ListDetail(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := flavors.ListDetail(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := flavors.ExtractFlavors(page) @@ -139,7 +140,7 @@ func TestGetFlavor(t *testing.T) { `) }) - actual, err := flavors.Get(fake.ServiceClient(), "12345").Extract() + actual, err := flavors.Get(context.TODO(), fake.ServiceClient(), "12345").Extract() if err != nil { t.Fatalf("Unable to get flavor: %v", err) } @@ -195,7 +196,7 @@ func TestCreateFlavor(t *testing.T) { RxTxFactor: 1.0, Description: "foo", } - actual, err := flavors.Create(fake.ServiceClient(), opts).Extract() + actual, err := flavors.Create(context.TODO(), fake.ServiceClient(), opts).Extract() if err != nil { t.Fatalf("Unable to create flavor: %v", err) } @@ -243,7 +244,7 @@ func TestUpdateFlavor(t *testing.T) { opts := &flavors.UpdateOpts{ Description: "foo", } - actual, err := flavors.Update(fake.ServiceClient(), "12345678", opts).Extract() + actual, err := flavors.Update(context.TODO(), fake.ServiceClient(), "12345678", opts).Extract() if err != nil { t.Fatalf("Unable to update flavor: %v", err) } @@ -275,7 +276,7 @@ func TestDeleteFlavor(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) - res := flavors.Delete(fake.ServiceClient(), "12345678") + res := flavors.Delete(context.TODO(), fake.ServiceClient(), "12345678") th.AssertNoErr(t, res.Err) } @@ -306,7 +307,7 @@ func TestFlavorAccessesList(t *testing.T) { }, } - allPages, err := flavors.ListAccesses(fake.ServiceClient(), "12345678").AllPages() + allPages, err := flavors.ListAccesses(fake.ServiceClient(), "12345678").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := flavors.ExtractAccesses(allPages) @@ -358,7 +359,7 @@ func TestFlavorAccessAdd(t *testing.T) { Tenant: "2f954bcf047c4ee9b09a37d49ae6db54", } - actual, err := flavors.AddAccess(fake.ServiceClient(), "12345678", addAccessOpts).Extract() + actual, err := flavors.AddAccess(context.TODO(), fake.ServiceClient(), "12345678", addAccessOpts).Extract() th.AssertNoErr(t, err) if !reflect.DeepEqual(expected, actual) { @@ -396,7 +397,7 @@ func TestFlavorAccessRemove(t *testing.T) { Tenant: "2f954bcf047c4ee9b09a37d49ae6db54", } - actual, err := flavors.RemoveAccess(fake.ServiceClient(), "12345678", removeAccessOpts).Extract() + actual, err := flavors.RemoveAccess(context.TODO(), fake.ServiceClient(), "12345678", removeAccessOpts).Extract() th.AssertNoErr(t, err) if !reflect.DeepEqual(expected, actual) { @@ -410,7 +411,7 @@ func TestFlavorExtraSpecsList(t *testing.T) { HandleExtraSpecsListSuccessfully(t) expected := ExtraSpecs - actual, err := flavors.ListExtraSpecs(fake.ServiceClient(), "1").Extract() + actual, err := flavors.ListExtraSpecs(context.TODO(), fake.ServiceClient(), "1").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -421,7 +422,7 @@ func TestFlavorExtraSpecGet(t *testing.T) { HandleExtraSpecGetSuccessfully(t) expected := ExtraSpec - actual, err := flavors.GetExtraSpec(fake.ServiceClient(), "1", "hw:cpu_policy").Extract() + actual, err := flavors.GetExtraSpec(context.TODO(), fake.ServiceClient(), "1", "hw:cpu_policy").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -436,7 +437,7 @@ func TestFlavorExtraSpecsCreate(t *testing.T) { "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } expected := ExtraSpecs - actual, err := flavors.CreateExtraSpecs(fake.ServiceClient(), "1", createOpts).Extract() + actual, err := flavors.CreateExtraSpecs(context.TODO(), fake.ServiceClient(), "1", createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -450,7 +451,7 @@ func TestFlavorExtraSpecUpdate(t *testing.T) { "hw:cpu_policy": "CPU-POLICY-2", } expected := UpdatedExtraSpec - actual, err := flavors.UpdateExtraSpec(fake.ServiceClient(), "1", updateOpts).Extract() + actual, err := flavors.UpdateExtraSpec(context.TODO(), fake.ServiceClient(), "1", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } @@ -460,6 +461,6 @@ func TestFlavorExtraSpecDelete(t *testing.T) { defer th.TeardownHTTP() HandleExtraSpecDeleteSuccessfully(t) - res := flavors.DeleteExtraSpec(fake.ServiceClient(), "1", "hw:cpu_policy") + res := flavors.DeleteExtraSpec(context.TODO(), fake.ServiceClient(), "1", "hw:cpu_policy") th.AssertNoErr(t, res.Err) } diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go index 6cfbdb43ed..ac93d6de61 100644 --- a/openstack/compute/v2/images/requests.go +++ b/openstack/compute/v2/images/requests.go @@ -1,6 +1,8 @@ package images import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -58,15 +60,15 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } // Get returns data about a specific image by its ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified image ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/images/testing/requests_test.go b/openstack/compute/v2/images/testing/requests_test.go index 3cf546fa4c..91bc4da6cd 100644 --- a/openstack/compute/v2/images/testing/requests_test.go +++ b/openstack/compute/v2/images/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "encoding/json" "fmt" "net/http" @@ -72,7 +73,7 @@ func TestListImages(t *testing.T) { pages := 0 options := &images.ListOpts{Limit: 2} - err := images.ListDetail(fake.ServiceClient(), options).EachPage(func(page pagination.Page) (bool, error) { + err := images.ListDetail(fake.ServiceClient(), options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := images.ExtractImages(page) @@ -162,7 +163,7 @@ func TestGetImage(t *testing.T) { `) }) - actual, err := images.Get(fake.ServiceClient(), "12345678").Extract() + actual, err := images.Get(context.TODO(), fake.ServiceClient(), "12345678").Extract() if err != nil { t.Fatalf("Unexpected error from Get: %v", err) } @@ -220,6 +221,6 @@ func TestDeleteImage(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := images.Delete(fake.ServiceClient(), "12345678") + res := images.Delete(context.TODO(), fake.ServiceClient(), "12345678") th.AssertNoErr(t, res.Err) } diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 5155f5dd40..df2df35ce0 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -1,6 +1,7 @@ package servers import ( + "context" "encoding/base64" "encoding/json" "fmt" @@ -309,35 +310,35 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { } // Create requests a server to be provisioned to the user in the current tenant. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToServerCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(listURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(ctx, listURL(client), reqBody, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests that a server previously provisioned be removed from your // account. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ForceDelete forces the deletion of a server. -func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) +func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single server, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -371,13 +372,13 @@ func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) { } // Update requests that various attributes of the indicated server be changed. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToServerUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -386,13 +387,13 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder // ChangeAdminPassword alters the administrator or root password for a specified // server. -func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { +func ChangeAdminPassword(ctx context.Context, client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { b := map[string]interface{}{ "changePassword": map[string]string{ "adminPass": newPassword, }, } - resp, err := client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -441,13 +442,13 @@ 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) { +func Reboot(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) { b, err := opts.ToServerRebootMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -497,13 +498,13 @@ func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { // Rebuild will reprovision the server according to the configuration options // provided in the RebuildOpts struct. -func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) { +func Rebuild(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) { b, err := opts.ToServerRebuildMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, nil) + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -536,21 +537,21 @@ func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { // While in this state, you can explore the use of the new server's // configuration. If you like it, call ConfirmResize() to commit the resize // permanently. Otherwise, call RevertResize() to restore the old configuration. -func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { +func Resize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { b, err := opts.ToServerResizeMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ConfirmResize confirms a previous resize operation on a server. // See Resize() for more details. -func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ +func ConfirmResize(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 202, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -559,8 +560,8 @@ func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult // RevertResize cancels a previous resize operation on a server. // See Resize() for more details. -func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) +func RevertResize(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -591,13 +592,13 @@ func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) { // Note: Using this operation will erase any already-existing metadata and // create the new metadata provided. To keep any already-existing metadata, // use the UpdateMetadatas or UpdateMetadata function. -func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { +func ResetMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { b, err := opts.ToMetadataResetMap() if err != nil { r.Err = err return } - resp, err := client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -605,8 +606,8 @@ func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetad } // Metadata requests all the metadata for the given server ID. -func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { - resp, err := client.Get(metadataURL(client, id), &r.Body, nil) +func Metadata(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { + resp, err := client.Get(ctx, metadataURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -620,13 +621,13 @@ type UpdateMetadataOptsBuilder interface { // UpdateMetadata updates (or creates) all the metadata specified by opts for // the given server ID. This operation does not affect already-existing metadata // that is not specified by opts. -func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { +func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToMetadataUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -661,13 +662,13 @@ func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string // CreateMetadatum will create or update the key-value pair with the given key // for the given server ID. -func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { +func CreateMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { b, key, err := opts.ToMetadatumCreateMap() if err != nil { r.Err = err return } - resp, err := client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -676,16 +677,16 @@ func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts Metadatu // Metadatum requests the key-value pair with the given key for the given // server ID. -func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { - resp, err := client.Get(metadatumURL(client, id, key), &r.Body, nil) +func Metadatum(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { + resp, err := client.Get(ctx, metadatumURL(client, id, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMetadatum will delete the key-value pair with the given key for the // given server ID. -func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { - resp, err := client.Delete(metadatumURL(client, id, key), nil) +func DeleteMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { + resp, err := client.Delete(ctx, metadatumURL(client, id, key), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -730,13 +731,13 @@ func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, er // CreateImage makes a request against the nova API to schedule an image to be // created of the server -func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { +func CreateImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { b, err := opts.ToServerCreateImageMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -745,8 +746,8 @@ func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageO // GetPassword makes a request against the nova API to get the encrypted // administrative password. -func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { - resp, err := client.Get(passwordURL(client, serverId), &r.Body, nil) +func GetPassword(ctx context.Context, client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { + resp, err := client.Get(ctx, passwordURL(client, serverId), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -770,13 +771,13 @@ func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]int } // ShowConsoleOutput makes a request against the nova API to get console log from the server -func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) { +func ShowConsoleOutput(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) { b, err := opts.ToServerShowConsoleOutputMap() if err != nil { r.Err = err return } - resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 745bf13685..19868ca3bd 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "encoding/base64" "encoding/json" "net/http" @@ -21,7 +22,7 @@ func TestListServers(t *testing.T) { HandleServerListSuccessfully(t) pages := 0 - err := servers.List(client.ServiceClient(), servers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := servers.List(client.ServiceClient(), servers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := servers.ExtractServers(page) @@ -51,7 +52,7 @@ func TestListAllServers(t *testing.T) { defer th.TeardownHTTP() HandleServerListSimpleSuccessfully(t) - allPages, err := servers.ListSimple(client.ServiceClient(), servers.ListOpts{}).AllPages() + allPages, err := servers.ListSimple(client.ServiceClient(), servers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := servers.ExtractServers(allPages) th.AssertNoErr(t, err) @@ -71,7 +72,7 @@ func TestListAllServersWithExtensions(t *testing.T) { diskconfig.ServerDiskConfigExt } - allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages() + allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) var actual []ServerWithExt @@ -90,7 +91,7 @@ func TestCreateServer(t *testing.T) { defer th.TeardownHTTP() HandleServerCreationSuccessfully(t, SingleServerBody) - actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ + actual, err := servers.Create(context.TODO(), client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", @@ -105,7 +106,7 @@ func TestCreateServerNoNetwork(t *testing.T) { defer th.TeardownHTTP() HandleServerNoNetworkCreationSuccessfully(t, SingleServerBody) - actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ + actual, err := servers.Create(context.TODO(), client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", @@ -121,7 +122,7 @@ func TestCreateServers(t *testing.T) { defer th.TeardownHTTP() HandleServersCreationSuccessfully(t, SingleServerBody) - actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ + actual, err := servers.Create(context.TODO(), client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", @@ -138,7 +139,7 @@ func TestCreateServerWithCustomField(t *testing.T) { defer th.TeardownHTTP() HandleServerCreationWithCustomFieldSuccessfully(t, SingleServerBody) - actual, err := servers.Create(client.ServiceClient(), CreateOptsWithCustomField{ + actual, err := servers.Create(context.TODO(), client.ServiceClient(), CreateOptsWithCustomField{ CreateOpts: servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", @@ -156,7 +157,7 @@ func TestCreateServerWithMetadata(t *testing.T) { defer th.TeardownHTTP() HandleServerCreationWithMetadata(t, SingleServerBody) - actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ + actual, err := servers.Create(context.TODO(), client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", @@ -174,7 +175,7 @@ func TestCreateServerWithUserdataString(t *testing.T) { defer th.TeardownHTTP() HandleServerCreationWithUserdata(t, SingleServerBody) - actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ + actual, err := servers.Create(context.TODO(), client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", @@ -192,7 +193,7 @@ func TestCreateServerWithUserdataEncoded(t *testing.T) { encoded := base64.StdEncoding.EncodeToString([]byte("userdata string")) - actual, err := servers.Create(client.ServiceClient(), servers.CreateOpts{ + actual, err := servers.Create(context.TODO(), client.ServiceClient(), servers.CreateOpts{ Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", @@ -208,7 +209,7 @@ func TestDeleteServer(t *testing.T) { defer th.TeardownHTTP() HandleServerDeletionSuccessfully(t) - res := servers.Delete(client.ServiceClient(), "asdfasdfasdf") + res := servers.Delete(context.TODO(), client.ServiceClient(), "asdfasdfasdf") th.AssertNoErr(t, res.Err) } @@ -217,7 +218,7 @@ func TestForceDeleteServer(t *testing.T) { defer th.TeardownHTTP() HandleServerForceDeletionSuccessfully(t) - res := servers.ForceDelete(client.ServiceClient(), "asdfasdfasdf") + res := servers.ForceDelete(context.TODO(), client.ServiceClient(), "asdfasdfasdf") th.AssertNoErr(t, res.Err) } @@ -227,7 +228,7 @@ func TestGetServer(t *testing.T) { HandleServerGetSuccessfully(t) client := client.ServiceClient() - actual, err := servers.Get(client, "1234asdf").Extract() + actual, err := servers.Get(context.TODO(), client, "1234asdf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -241,7 +242,7 @@ func TestGetFaultyServer(t *testing.T) { HandleServerGetFaultSuccessfully(t) client := client.ServiceClient() - actual, err := servers.Get(client, "1234asdf").Extract() + actual, err := servers.Get(context.TODO(), client, "1234asdf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -263,7 +264,7 @@ func TestGetServerWithExtensions(t *testing.T) { diskconfig.ServerDiskConfigExt } - err := servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(&s) + err := servers.Get(context.TODO(), client.ServiceClient(), "1234asdf").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "nova", s.AvailabilityZone) th.AssertEquals(t, "RUNNING", s.PowerState.String()) @@ -271,7 +272,7 @@ func TestGetServerWithExtensions(t *testing.T) { th.AssertEquals(t, "active", s.VmState) th.AssertEquals(t, diskconfig.Manual, s.DiskConfig) - err = servers.Get(client.ServiceClient(), "1234asdf").ExtractInto(s) + err = servers.Get(context.TODO(), client.ServiceClient(), "1234asdf").ExtractInto(s) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } @@ -283,7 +284,7 @@ func TestUpdateServer(t *testing.T) { HandleServerUpdateSuccessfully(t) client := client.ServiceClient() - actual, err := servers.Update(client, "1234asdf", servers.UpdateOpts{Name: "new-name"}).Extract() + actual, err := servers.Update(context.TODO(), client, "1234asdf", servers.UpdateOpts{Name: "new-name"}).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } @@ -296,7 +297,7 @@ func TestChangeServerAdminPassword(t *testing.T) { defer th.TeardownHTTP() HandleAdminPasswordChangeSuccessfully(t) - res := servers.ChangeAdminPassword(client.ServiceClient(), "1234asdf", "new-password") + res := servers.ChangeAdminPassword(context.TODO(), client.ServiceClient(), "1234asdf", "new-password") th.AssertNoErr(t, res.Err) } @@ -308,7 +309,7 @@ func TestShowConsoleOutput(t *testing.T) { outputOpts := &servers.ShowConsoleOutputOpts{ Length: 50, } - actual, err := servers.ShowConsoleOutput(client.ServiceClient(), "1234asdf", outputOpts).Extract() + actual, err := servers.ShowConsoleOutput(context.TODO(), client.ServiceClient(), "1234asdf", outputOpts).Extract() th.AssertNoErr(t, err) th.AssertByteArrayEquals(t, []byte(ConsoleOutput), []byte(actual)) @@ -319,7 +320,7 @@ func TestGetPassword(t *testing.T) { defer th.TeardownHTTP() HandlePasswordGetSuccessfully(t) - res := servers.GetPassword(client.ServiceClient(), "1234asdf") + res := servers.GetPassword(context.TODO(), client.ServiceClient(), "1234asdf") th.AssertNoErr(t, res.Err) } @@ -328,7 +329,7 @@ func TestRebootServer(t *testing.T) { defer th.TeardownHTTP() HandleRebootSuccessfully(t) - res := servers.Reboot(client.ServiceClient(), "1234asdf", servers.RebootOpts{ + res := servers.Reboot(context.TODO(), client.ServiceClient(), "1234asdf", servers.RebootOpts{ Type: servers.SoftReboot, }) th.AssertNoErr(t, res.Err) @@ -346,7 +347,7 @@ func TestRebuildServer(t *testing.T) { AccessIPv4: "1.2.3.4", } - actual, err := servers.Rebuild(client.ServiceClient(), "1234asdf", opts).Extract() + actual, err := servers.Rebuild(context.TODO(), client.ServiceClient(), "1234asdf", opts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -364,7 +365,7 @@ func TestResizeServer(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) - res := servers.Resize(client.ServiceClient(), "1234asdf", servers.ResizeOpts{FlavorRef: "2"}) + res := servers.Resize(context.TODO(), client.ServiceClient(), "1234asdf", servers.ResizeOpts{FlavorRef: "2"}) th.AssertNoErr(t, res.Err) } @@ -380,7 +381,7 @@ func TestConfirmResize(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := servers.ConfirmResize(client.ServiceClient(), "1234asdf") + res := servers.ConfirmResize(context.TODO(), client.ServiceClient(), "1234asdf") th.AssertNoErr(t, res.Err) } @@ -396,7 +397,7 @@ func TestRevertResize(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) - res := servers.RevertResize(client.ServiceClient(), "1234asdf") + res := servers.RevertResize(context.TODO(), client.ServiceClient(), "1234asdf") th.AssertNoErr(t, res.Err) } @@ -407,7 +408,7 @@ func TestGetMetadatum(t *testing.T) { HandleMetadatumGetSuccessfully(t) expected := map[string]string{"foo": "bar"} - actual, err := servers.Metadatum(client.ServiceClient(), "1234asdf", "foo").Extract() + actual, err := servers.Metadatum(context.TODO(), client.ServiceClient(), "1234asdf", "foo").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } @@ -419,7 +420,7 @@ func TestCreateMetadatum(t *testing.T) { HandleMetadatumCreateSuccessfully(t) expected := map[string]string{"foo": "bar"} - actual, err := servers.CreateMetadatum(client.ServiceClient(), "1234asdf", servers.MetadatumOpts{"foo": "bar"}).Extract() + actual, err := servers.CreateMetadatum(context.TODO(), client.ServiceClient(), "1234asdf", servers.MetadatumOpts{"foo": "bar"}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } @@ -430,7 +431,7 @@ func TestDeleteMetadatum(t *testing.T) { HandleMetadatumDeleteSuccessfully(t) - err := servers.DeleteMetadatum(client.ServiceClient(), "1234asdf", "foo").ExtractErr() + err := servers.DeleteMetadatum(context.TODO(), client.ServiceClient(), "1234asdf", "foo").ExtractErr() th.AssertNoErr(t, err) } @@ -441,7 +442,7 @@ func TestGetMetadata(t *testing.T) { HandleMetadataGetSuccessfully(t) expected := map[string]string{"foo": "bar", "this": "that"} - actual, err := servers.Metadata(client.ServiceClient(), "1234asdf").Extract() + actual, err := servers.Metadata(context.TODO(), client.ServiceClient(), "1234asdf").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } @@ -453,7 +454,7 @@ func TestResetMetadata(t *testing.T) { HandleMetadataResetSuccessfully(t) expected := map[string]string{"foo": "bar", "this": "that"} - actual, err := servers.ResetMetadata(client.ServiceClient(), "1234asdf", servers.MetadataOpts{ + actual, err := servers.ResetMetadata(context.TODO(), client.ServiceClient(), "1234asdf", servers.MetadataOpts{ "foo": "bar", "this": "that", }).Extract() @@ -468,7 +469,7 @@ func TestUpdateMetadata(t *testing.T) { HandleMetadataUpdateSuccessfully(t) expected := map[string]string{"foo": "baz", "this": "those"} - actual, err := servers.UpdateMetadata(client.ServiceClient(), "1234asdf", servers.MetadataOpts{ + actual, err := servers.UpdateMetadata(context.TODO(), client.ServiceClient(), "1234asdf", servers.MetadataOpts{ "foo": "baz", "this": "those", }).Extract() @@ -483,7 +484,7 @@ func TestListAddresses(t *testing.T) { expected := ListAddressesExpected pages := 0 - err := servers.ListAddresses(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) { + err := servers.ListAddresses(client.ServiceClient(), "asdfasdfasdf").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := servers.ExtractAddresses(page) @@ -507,7 +508,7 @@ func TestListAddressesByNetwork(t *testing.T) { expected := ListNetworkAddressesExpected pages := 0 - err := servers.ListAddressesByNetwork(client.ServiceClient(), "asdfasdfasdf", "public").EachPage(func(page pagination.Page) (bool, error) { + err := servers.ListAddressesByNetwork(client.ServiceClient(), "asdfasdfasdf", "public").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := servers.ExtractNetworkAddresses(page) @@ -529,7 +530,7 @@ func TestCreateServerImage(t *testing.T) { defer th.TeardownHTTP() HandleCreateServerImageSuccessfully(t) - _, err := servers.CreateImage(client.ServiceClient(), "serverimage", servers.CreateImageOpts{Name: "test"}).ExtractImageID() + _, err := servers.CreateImage(context.TODO(), client.ServiceClient(), "serverimage", servers.CreateImageOpts{Name: "test"}).ExtractImageID() th.AssertNoErr(t, err) } @@ -586,7 +587,7 @@ func TestCreateServerWithTags(t *testing.T) { FlavorRef: "1", Tags: tags, } - res := servers.Create(c, createOpts) + res := servers.Create(context.TODO(), c, createOpts) th.AssertNoErr(t, res.Err) actualServer, err := res.Extract() th.AssertNoErr(t, err) diff --git a/openstack/compute/v2/servers/testing/results_test.go b/openstack/compute/v2/servers/testing/results_test.go index f66fdb6827..517d9e4868 100644 --- a/openstack/compute/v2/servers/testing/results_test.go +++ b/openstack/compute/v2/servers/testing/results_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "crypto/rsa" "encoding/json" "fmt" @@ -105,7 +106,7 @@ func TestListAddressesAllPages(t *testing.T) { defer th.TeardownHTTP() HandleAddressListSuccessfully(t) - allPages, err := servers.ListAddresses(client.ServiceClient(), "asdfasdfasdf").AllPages() + allPages, err := servers.ListAddresses(client.ServiceClient(), "asdfasdfasdf").AllPages(context.TODO()) th.AssertNoErr(t, err) _, err = servers.ExtractAddresses(allPages) th.AssertNoErr(t, err) diff --git a/openstack/compute/v2/servers/util.go b/openstack/compute/v2/servers/util.go index aa08b5934e..37537d178f 100644 --- a/openstack/compute/v2/servers/util.go +++ b/openstack/compute/v2/servers/util.go @@ -1,13 +1,17 @@ package servers -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // WaitForStatus will continually poll a server until it successfully // transitions to a specified status. It will do this for at most the number // of seconds specified. -func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { - current, err := Get(c, id).Extract() + current, err := Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/openstack/config/provider_client.go b/openstack/config/provider_client.go index 842c75adc6..c163dbe51e 100644 --- a/openstack/config/provider_client.go +++ b/openstack/config/provider_client.go @@ -62,7 +62,7 @@ func NewProviderClient(ctx context.Context, authOptions gophercloud.AuthOptions, } client.HTTPClient = options.httpClient - err = openstack.AuthenticateWithContext(ctx, client, authOptions) + err = openstack.Authenticate(ctx, client, authOptions) if err != nil { return nil, err } diff --git a/openstack/container/v1/capsules/requests.go b/openstack/container/v1/capsules/requests.go index 74f17559ae..7d572d4f30 100644 --- a/openstack/container/v1/capsules/requests.go +++ b/openstack/container/v1/capsules/requests.go @@ -1,6 +1,8 @@ package capsules import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -20,8 +22,8 @@ type ListOptsBuilder interface { } // Get requests details on a single capsule, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -53,13 +55,13 @@ func (opts CreateOpts) ToCapsuleCreateMap() (map[string]interface{}, error) { } // Create implements create capsule request. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCapsuleCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -98,8 +100,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Delete implements Capsule delete request. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/container/v1/capsules/testing/requests_test.go b/openstack/container/v1/capsules/testing/requests_test.go index 1f4baa49e0..2925f93ecc 100644 --- a/openstack/container/v1/capsules/testing/requests_test.go +++ b/openstack/container/v1/capsules/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -27,7 +28,7 @@ func TestGetCapsule_OldTime(t *testing.T) { ExpectedCapsule.Containers[0].UpdatedAt = updatedAt ExpectedCapsule.Containers[0].StartedAt = startedAt - actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() + actualCapsule, err := capsules.Get(context.TODO(), fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) @@ -49,7 +50,7 @@ func TestGetCapsule_NewTime(t *testing.T) { ExpectedCapsule.Containers[0].UpdatedAt = updatedAt ExpectedCapsule.Containers[0].StartedAt = startedAt - actualCapsule, err := capsules.Get(fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() + actualCapsule, err := capsules.Get(context.TODO(), fakeclient.ServiceClient(), ExpectedCapsule.UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) @@ -66,7 +67,7 @@ func TestCreateCapsule(t *testing.T) { createOpts := capsules.CreateOpts{ TemplateOpts: template, } - actualCapsule, err := capsules.Create(fakeclient.ServiceClient(), createOpts).Extract() + actualCapsule, err := capsules.Create(context.TODO(), fakeclient.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExpectedCapsule, actualCapsule) @@ -91,7 +92,7 @@ func TestListCapsule(t *testing.T) { count := 0 results := capsules.List(fakeclient.ServiceClient(), nil) - err := results.EachPage(func(page pagination.Page) (bool, error) { + err := results.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := capsules.ExtractCapsules(page) if err != nil { @@ -129,7 +130,7 @@ func TestListCapsuleV132(t *testing.T) { count := 0 results := capsules.List(fakeclient.ServiceClient(), nil) - err := results.EachPage(func(page pagination.Page) (bool, error) { + err := results.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := capsules.ExtractCapsules(page) if err != nil { @@ -154,6 +155,6 @@ func TestDelete(t *testing.T) { HandleCapsuleDeleteSuccessfully(t) - res := capsules.Delete(fakeclient.ServiceClient(), "963a239d-3946-452b-be5a-055eab65a421") + res := capsules.Delete(context.TODO(), fakeclient.ServiceClient(), "963a239d-3946-452b-be5a-055eab65a421") th.AssertNoErr(t, res.Err) } diff --git a/openstack/containerinfra/apiversions/requests.go b/openstack/containerinfra/apiversions/requests.go index a82c605c10..02dccd30d1 100644 --- a/openstack/containerinfra/apiversions/requests.go +++ b/openstack/containerinfra/apiversions/requests.go @@ -1,6 +1,8 @@ package apiversions import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -13,8 +15,8 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { } // Get will get a specific API version, specified by major ID. -func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - resp, err := client.Get(getURL(client, v), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, v string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/containerinfra/apiversions/testing/requests_test.go b/openstack/containerinfra/apiversions/testing/requests_test.go index 6800739f9b..175be19c74 100644 --- a/openstack/containerinfra/apiversions/testing/requests_test.go +++ b/openstack/containerinfra/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "testing" @@ -15,7 +16,7 @@ func TestListAPIVersions(t *testing.T) { MockListResponse(t) - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersions(allVersions) @@ -30,7 +31,7 @@ func TestGetAPIVersion(t *testing.T) { MockGetResponse(t) - actual, err := apiversions.Get(client.ServiceClient(), "v1").Extract() + actual, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v1").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, MagnumAPIVersion1Result, *actual) diff --git a/openstack/containerinfra/v1/certificates/requests.go b/openstack/containerinfra/v1/certificates/requests.go index 0a286989aa..b98b0005b8 100644 --- a/openstack/containerinfra/v1/certificates/requests.go +++ b/openstack/containerinfra/v1/certificates/requests.go @@ -1,6 +1,8 @@ package certificates import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) @@ -23,9 +25,9 @@ func (opts CreateOpts) ToCertificateCreateMap() (map[string]interface{}, error) } // Get makes a request against the API to get details for a certificate. -func Get(client *gophercloud.ServiceClient, clusterID string) (r GetResult) { +func Get(ctx context.Context, client *gophercloud.ServiceClient, clusterID string) (r GetResult) { url := getURL(client, clusterID) - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -33,13 +35,13 @@ func Get(client *gophercloud.ServiceClient, clusterID string) (r GetResult) { } // Create requests the creation of a new certificate. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCertificateCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -47,8 +49,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Update will rotate the CA certificate for a cluster -func Update(client *gophercloud.ServiceClient, clusterID string) (r UpdateResult) { - resp, err := client.Patch(updateURL(client, clusterID), nil, &r.Body, &gophercloud.RequestOpts{ +func Update(ctx context.Context, client *gophercloud.ServiceClient, clusterID string) (r UpdateResult) { + resp, err := client.Patch(ctx, updateURL(client, clusterID), nil, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/containerinfra/v1/certificates/testing/requests_test.go b/openstack/containerinfra/v1/certificates/testing/requests_test.go index b93861da15..25a287e401 100644 --- a/openstack/containerinfra/v1/certificates/testing/requests_test.go +++ b/openstack/containerinfra/v1/certificates/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/certificates" @@ -17,7 +18,7 @@ func TestGetCertificates(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - actual, err := certificates.Get(sc, "d564b18a-2890-4152-be3d-e05d784ff72").Extract() + actual, err := certificates.Get(context.TODO(), sc, "d564b18a-2890-4152-be3d-e05d784ff72").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCertificate, *actual) } @@ -36,7 +37,7 @@ func TestCreateCertificates(t *testing.T) { CSR: "FAKE_CERTIFICATE_CSR", } - actual, err := certificates.Create(sc, opts).Extract() + actual, err := certificates.Create(context.TODO(), sc, opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreateCertificateResponse, *actual) } @@ -50,6 +51,6 @@ func TestUpdateCertificates(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - err := certificates.Update(sc, "d564b18a-2890-4152-be3d-e05d784ff72").ExtractErr() + err := certificates.Update(context.TODO(), sc, "d564b18a-2890-4152-be3d-e05d784ff72").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 1860f1016f..9ed06cc4f2 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -1,6 +1,8 @@ package clusters import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -36,13 +38,13 @@ func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new cluster. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToClusterCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -50,15 +52,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get retrieves a specific clusters based on its unique ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cluster ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -148,7 +150,7 @@ func (opts UpdateOpts) ToClustersUpdateMap() (map[string]interface{}, error) { } // Update implements cluster updated request. -func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { var o []map[string]interface{} for _, opt := range opts { b, err := opt.ToClustersUpdateMap() @@ -158,7 +160,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuild } o = append(o, b) } - resp, err := client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -187,14 +189,14 @@ func (opts UpgradeOpts) ToClustersUpgradeMap() (map[string]interface{}, error) { } // Upgrade implements cluster upgrade request. -func Upgrade(client *gophercloud.ServiceClient, id string, opts UpgradeOptsBuilder) (r UpgradeResult) { +func Upgrade(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpgradeOptsBuilder) (r UpgradeResult) { b, err := opts.ToClustersUpgradeMap() if err != nil { r.Err = err return } - resp, err := client.Post(upgradeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, upgradeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -220,13 +222,13 @@ func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { } // Resize an existing cluster node count. -func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ResizeResult) { +func Resize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ResizeResult) { b, err := opts.ToClusterResizeMap() if err != nil { r.Err = err return } - resp, err := client.Post(resizeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, resizeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index f1dc6d2dfd..fef218410d 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -40,7 +41,7 @@ func TestCreateCluster(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - res := clusters.Create(sc, opts) + res := clusters.Create(context.TODO(), sc, opts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") @@ -60,7 +61,7 @@ func TestGetCluster(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - actual, err := clusters.Get(sc, "746e779a-751a-456b-a3e9-c883d734946f").Extract() + actual, err := clusters.Get(context.TODO(), sc, "746e779a-751a-456b-a3e9-c883d734946f").Extract() th.AssertNoErr(t, err) actual.CreatedAt = actual.CreatedAt.UTC() actual.UpdatedAt = actual.UpdatedAt.UTC() @@ -76,7 +77,7 @@ func TestListClusters(t *testing.T) { count := 0 sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - clusters.List(sc, clusters.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + clusters.List(sc, clusters.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := clusters.ExtractClusters(page) th.AssertNoErr(t, err) @@ -103,7 +104,7 @@ func TestListDetailClusters(t *testing.T) { count := 0 sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - clusters.ListDetail(sc, clusters.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + clusters.ListDetail(sc, clusters.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := clusters.ExtractClusters(page) th.AssertNoErr(t, err) @@ -142,7 +143,7 @@ func TestUpdateCluster(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - res := clusters.Update(sc, clusterUUID, updateOpts) + res := clusters.Update(context.TODO(), sc, clusterUUID, updateOpts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") @@ -167,7 +168,7 @@ func TestUpgradeCluster(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - res := clusters.Upgrade(sc, clusterUUID, opts) + res := clusters.Upgrade(context.TODO(), sc, clusterUUID, opts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") @@ -187,7 +188,7 @@ func TestDeleteCluster(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - r := clusters.Delete(sc, clusterUUID) + r := clusters.Delete(context.TODO(), sc, clusterUUID) err := r.ExtractErr() th.AssertNoErr(t, err) @@ -220,7 +221,7 @@ func TestResizeCluster(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - res := clusters.Resize(sc, clusterUUID, opts) + res := clusters.Resize(context.TODO(), sc, clusterUUID, opts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index 141f07d53a..6d88e6f948 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -1,6 +1,8 @@ package clustertemplates import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -47,13 +49,13 @@ func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new cluster. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToClusterCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -61,8 +63,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes the specified cluster ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -108,8 +110,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves a specific cluster-template based on its unique ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -146,7 +148,7 @@ func (opts UpdateOpts) ToClusterTemplateUpdateMap() (map[string]interface{}, err } // Update implements cluster updated request. -func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { var o []map[string]interface{} for _, opt := range opts { b, err := opt.ToClusterTemplateUpdateMap() @@ -156,7 +158,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuild } o = append(o, b) } - resp, err := client.Patch(updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), o, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index 50adcd53db..3e9cb717a9 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/clustertemplates" @@ -47,7 +48,7 @@ func TestCreateClusterTemplate(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - res := clustertemplates.Create(sc, opts) + res := clustertemplates.Create(context.TODO(), sc, opts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") @@ -68,7 +69,7 @@ func TestDeleteClusterTemplate(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - res := clustertemplates.Delete(sc, "6dc6d336e3fc4c0a951b5698cd1236ee") + res := clustertemplates.Delete(context.TODO(), sc, "6dc6d336e3fc4c0a951b5698cd1236ee") th.AssertNoErr(t, res.Err) requestID := res.Header["X-Openstack-Request-Id"][0] th.AssertEquals(t, "req-781e9bdc-4163-46eb-91c9-786c53188bbb", requestID) @@ -84,7 +85,7 @@ func TestListClusterTemplates(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - clustertemplates.List(sc, clustertemplates.ListOpts{Limit: 2}).EachPage(func(page pagination.Page) (bool, error) { + clustertemplates.List(sc, clustertemplates.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := clustertemplates.ExtractClusterTemplates(page) th.AssertNoErr(t, err) @@ -109,7 +110,7 @@ func TestGetClusterTemplate(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - actual, err := clustertemplates.Get(sc, "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + actual, err := clustertemplates.Get(context.TODO(), sc, "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertNoErr(t, err) actual.CreatedAt = actual.CreatedAt.UTC() th.AssertDeepEquals(t, ExpectedClusterTemplate, *actual) @@ -123,7 +124,7 @@ func TestGetClusterTemplateEmptyTime(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - actual, err := clustertemplates.Get(sc, "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() + actual, err := clustertemplates.Get(context.TODO(), sc, "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() th.AssertNoErr(t, err) actual.CreatedAt = actual.CreatedAt.UTC() th.AssertDeepEquals(t, ExpectedClusterTemplate_EmptyTime, *actual) @@ -150,7 +151,7 @@ func TestUpdateClusterTemplate(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - res := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts) + res := clustertemplates.Update(context.TODO(), sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts) th.AssertNoErr(t, res.Err) actual, err := res.Extract() @@ -180,7 +181,7 @@ func TestUpdateClusterTemplateEmptyTime(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - actual, err := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts).Extract() + actual, err := clustertemplates.Update(context.TODO(), sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdateClusterTemplate_EmptyTime, *actual) } @@ -208,6 +209,6 @@ func TestUpdateClusterTemplateInvalidUpdate(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - _, err := clustertemplates.Update(sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts).Extract() + _, err := clustertemplates.Update(context.TODO(), sc, "7d85f602-a948-4a30-afd4-e84f47471c15", updateOpts).Extract() th.AssertEquals(t, true, err != nil) } diff --git a/openstack/containerinfra/v1/nodegroups/requests.go b/openstack/containerinfra/v1/nodegroups/requests.go index 0c9955f123..91686bff80 100644 --- a/openstack/containerinfra/v1/nodegroups/requests.go +++ b/openstack/containerinfra/v1/nodegroups/requests.go @@ -1,6 +1,8 @@ package nodegroups import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -9,8 +11,8 @@ import ( // with the given ID/name belonging to the given cluster. // Use the Extract method of the returned GetResult to extract the // node group from the result. -func Get(client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r GetResult) { - resp, err := client.Get(getURL(client, clusterID, nodeGroupID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) +func Get(ctx context.Context, client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, clusterID, nodeGroupID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -99,13 +101,13 @@ func (opts CreateOpts) ToNodeGroupCreateMap() (map[string]interface{}, error) { // for the the given cluster. // Use the Extract method of the returned CreateResult to extract the // returned node group. -func Create(client *gophercloud.ServiceClient, clusterID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, clusterID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToNodeGroupCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := client.Post(ctx, createURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -141,7 +143,7 @@ func (opts UpdateOpts) ToResourceUpdateMap() (map[string]interface{}, error) { // one UpdateOpts can be passed at a time. // Use the Extract method of the returned UpdateResult to extract the // updated node group from the result. -func Update(client *gophercloud.ServiceClient, clusterID string, nodeGroupID string, opts []UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, clusterID string, nodeGroupID string, opts []UpdateOptsBuilder) (r UpdateResult) { var o []map[string]interface{} for _, opt := range opts { b, err := opt.ToResourceUpdateMap() @@ -151,14 +153,14 @@ func Update(client *gophercloud.ServiceClient, clusterID string, nodeGroupID str } o = append(o, b) } - resp, err := client.Patch(updateURL(client, clusterID, nodeGroupID), o, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := client.Patch(ctx, updateURL(client, clusterID, nodeGroupID), o, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete makes a request to the Magnum API to delete a node group. -func Delete(client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, clusterID, nodeGroupID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, clusterID, nodeGroupID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, clusterID, nodeGroupID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go index d6c92c8e2e..bf4a4f8d6d 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go +++ b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -19,7 +20,7 @@ func TestGetNodeGroupSuccess(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - ng, err := nodegroups.Get(sc, clusterUUID, nodeGroup1UUID).Extract() + ng, err := nodegroups.Get(context.TODO(), sc, clusterUUID, nodeGroup1UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedNodeGroup1, *ng) @@ -35,7 +36,7 @@ func TestGetNodeGroupNotFound(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - _, err := nodegroups.Get(sc, clusterUUID, badNodeGroupUUID).Extract() + _, err := nodegroups.Get(context.TODO(), sc, clusterUUID, badNodeGroupUUID).Extract() th.AssertEquals(t, true, err != nil) _, isNotFound := err.(gophercloud.ErrDefault404) @@ -53,7 +54,7 @@ func TestGetNodeGroupClusterNotFound(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - _, err := nodegroups.Get(sc, badClusterUUID, badNodeGroupUUID).Extract() + _, err := nodegroups.Get(context.TODO(), sc, badClusterUUID, badNodeGroupUUID).Extract() th.AssertEquals(t, true, err != nil) _, isNotFound := err.(gophercloud.ErrDefault404) @@ -70,7 +71,7 @@ func TestListNodeGroupsSuccess(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - ngPages, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages() + ngPages, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) ngs, err := nodegroups.ExtractNodeGroups(ngPages) @@ -93,7 +94,7 @@ func TestListNodeGroupsLimitSuccess(t *testing.T) { sc.Endpoint = sc.Endpoint + "v1/" listOpts := nodegroups.ListOpts{Limit: 1} - ngPages, err := nodegroups.List(sc, clusterUUID, listOpts).AllPages() + ngPages, err := nodegroups.List(sc, clusterUUID, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) ngs, err := nodegroups.ExtractNodeGroups(ngPages) @@ -114,7 +115,7 @@ func TestListNodeGroupsClusterNotFound(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - _, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages() + _, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages(context.TODO()) th.AssertEquals(t, true, err != nil) _, isNotFound := err.(gophercloud.ErrDefault404) @@ -136,7 +137,7 @@ func TestCreateNodeGroupSuccess(t *testing.T) { MergeLabels: gophercloud.Enabled, } - ng, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() + ng, err := nodegroups.Create(context.TODO(), sc, clusterUUID, createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedCreatedNodeGroup, *ng) } @@ -156,7 +157,7 @@ func TestCreateNodeGroupDuplicate(t *testing.T) { Name: "default-worker", } - _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() + _, err := nodegroups.Create(context.TODO(), sc, clusterUUID, createOpts).Extract() th.AssertEquals(t, true, err != nil) _, isNotAccepted := err.(gophercloud.ErrDefault409) th.AssertEquals(t, true, isNotAccepted) @@ -178,7 +179,7 @@ func TestCreateNodeGroupMaster(t *testing.T) { Role: "master", } - _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() + _, err := nodegroups.Create(context.TODO(), sc, clusterUUID, createOpts).Extract() th.AssertEquals(t, true, err != nil) _, isBadRequest := err.(gophercloud.ErrDefault400) th.AssertEquals(t, true, isBadRequest) @@ -202,7 +203,7 @@ func TestCreateNodeGroupBadSizes(t *testing.T) { MaxNodeCount: &maxNodes, } - _, err := nodegroups.Create(sc, clusterUUID, createOpts).Extract() + _, err := nodegroups.Create(context.TODO(), sc, clusterUUID, createOpts).Extract() th.AssertEquals(t, true, err != nil) _, isNotAccepted := err.(gophercloud.ErrDefault409) th.AssertEquals(t, true, isNotAccepted) @@ -226,7 +227,7 @@ func TestUpdateNodeGroupSuccess(t *testing.T) { }, } - ng, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() + ng, err := nodegroups.Update(context.TODO(), sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expectedUpdatedNodeGroup, *ng) } @@ -250,7 +251,7 @@ func TestUpdateNodeGroupInternal(t *testing.T) { }, } - _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() + _, err := nodegroups.Update(context.TODO(), sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() th.AssertEquals(t, true, err != nil) _, isBadRequest := err.(gophercloud.ErrDefault400) th.AssertEquals(t, true, isBadRequest) @@ -275,7 +276,7 @@ func TestUpdateNodeGroupBadField(t *testing.T) { }, } - _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() + _, err := nodegroups.Update(context.TODO(), sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() th.AssertEquals(t, true, err != nil) _, isBadRequest := err.(gophercloud.ErrDefault400) th.AssertEquals(t, true, isBadRequest) @@ -300,7 +301,7 @@ func TestUpdateNodeGroupBadMin(t *testing.T) { }, } - _, err := nodegroups.Update(sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() + _, err := nodegroups.Update(context.TODO(), sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() th.AssertEquals(t, true, err != nil) _, isNotAccepted := err.(gophercloud.ErrDefault409) th.AssertEquals(t, true, isNotAccepted) @@ -316,7 +317,7 @@ func TestDeleteNodeGroupSuccess(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - err := nodegroups.Delete(sc, clusterUUID, nodeGroup2UUID).ExtractErr() + err := nodegroups.Delete(context.TODO(), sc, clusterUUID, nodeGroup2UUID).ExtractErr() th.AssertNoErr(t, err) } @@ -330,7 +331,7 @@ func TestDeleteNodeGroupNotFound(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - err := nodegroups.Delete(sc, clusterUUID, badNodeGroupUUID).ExtractErr() + err := nodegroups.Delete(context.TODO(), sc, clusterUUID, badNodeGroupUUID).ExtractErr() _, isNotFound := err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, isNotFound) } @@ -345,7 +346,7 @@ func TestDeleteNodeGroupClusterNotFound(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - err := nodegroups.Delete(sc, badClusterUUID, badNodeGroupUUID).ExtractErr() + err := nodegroups.Delete(context.TODO(), sc, badClusterUUID, badNodeGroupUUID).ExtractErr() _, isNotFound := err.(gophercloud.ErrDefault404) th.AssertEquals(t, true, isNotFound) } @@ -360,7 +361,7 @@ func TestDeleteNodeGroupDefault(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - err := nodegroups.Delete(sc, clusterUUID, nodeGroup2UUID).ExtractErr() + err := nodegroups.Delete(context.TODO(), sc, clusterUUID, nodeGroup2UUID).ExtractErr() _, isBadRequest := err.(gophercloud.ErrDefault400) th.AssertEquals(t, true, isBadRequest) } diff --git a/openstack/containerinfra/v1/quotas/requests.go b/openstack/containerinfra/v1/quotas/requests.go index b3ecfb6f07..274aaaa775 100644 --- a/openstack/containerinfra/v1/quotas/requests.go +++ b/openstack/containerinfra/v1/quotas/requests.go @@ -1,6 +1,8 @@ package quotas import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) @@ -22,13 +24,13 @@ func (opts CreateOpts) ToQuotaCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new quota. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToQuotaCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/containerinfra/v1/quotas/testing/requests_test.go b/openstack/containerinfra/v1/quotas/testing/requests_test.go index 1e64eac2fe..75dde3000f 100644 --- a/openstack/containerinfra/v1/quotas/testing/requests_test.go +++ b/openstack/containerinfra/v1/quotas/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/containerinfra/v1/quotas" @@ -23,7 +24,7 @@ func TestCreateQuota(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - res := quotas.Create(sc, opts) + res := quotas.Create(context.TODO(), sc, opts) th.AssertNoErr(t, res.Err) requestID := res.Header.Get("X-OpenStack-Request-Id") diff --git a/openstack/db/v1/configurations/requests.go b/openstack/db/v1/configurations/requests.go index 94dedc271a..18066ffe30 100644 --- a/openstack/db/v1/configurations/requests.go +++ b/openstack/db/v1/configurations/requests.go @@ -1,6 +1,8 @@ package configurations import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/db/v1/instances" "github.com/gophercloud/gophercloud/v2/pagination" @@ -47,20 +49,20 @@ func (opts CreateOpts) ToConfigCreateMap() (map[string]interface{}, error) { } // Create will create a new configuration group. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToConfigCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Post(ctx, baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will retrieve the details for a specified configuration group. -func Get(client *gophercloud.ServiceClient, configID string) (r GetResult) { - resp, err := client.Get(resourceURL(client, configID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, configID string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, configID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -93,13 +95,13 @@ func (opts UpdateOpts) ToConfigUpdateMap() (map[string]interface{}, error) { // Update will modify an existing configuration group by performing a merge // between new and existing values. If the key already exists, the new value // will overwrite. All other keys will remain unaffected. -func Update(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToConfigUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(resourceURL(client, configID), &b, nil, nil) + resp, err := client.Patch(ctx, resourceURL(client, configID), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -107,13 +109,13 @@ func Update(client *gophercloud.ServiceClient, configID string, opts UpdateOptsB // Replace will modify an existing configuration group by overwriting the // entire parameter group with the new values provided. Any existing keys not // included in UpdateOptsBuilder will be deleted. -func Replace(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) (r ReplaceResult) { +func Replace(ctx context.Context, client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) (r ReplaceResult) { b, err := opts.ToConfigUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(resourceURL(client, configID), &b, nil, nil) + resp, err := client.Put(ctx, resourceURL(client, configID), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -121,8 +123,8 @@ func Replace(client *gophercloud.ServiceClient, configID string, opts UpdateOpts // Delete will permanently delete a configuration group. Please note that // config groups cannot be deleted whilst still attached to running instances - // you must detach and then delete them. -func Delete(client *gophercloud.ServiceClient, configID string) (r DeleteResult) { - resp, err := client.Delete(resourceURL(client, configID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, configID string) (r DeleteResult) { + resp, err := client.Delete(ctx, resourceURL(client, configID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -151,8 +153,8 @@ func ListDatastoreParams(client *gophercloud.ServiceClient, datastoreID, version // "innodb_file_per_table" configuration param for MySQL datastores. You will // need the param's ID first, which can be attained by using the ListDatastoreParams // operation. -func GetDatastoreParam(client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) (r ParamResult) { - resp, err := client.Get(getDSParamURL(client, datastoreID, versionID, paramID), &r.Body, nil) +func GetDatastoreParam(ctx context.Context, client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) (r ParamResult) { + resp, err := client.Get(ctx, getDSParamURL(client, datastoreID, versionID, paramID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -167,8 +169,8 @@ func ListGlobalParams(client *gophercloud.ServiceClient, versionID string) pagin // GetGlobalParam is similar to GetDatastoreParam but does not require a // DatastoreID. -func GetGlobalParam(client *gophercloud.ServiceClient, versionID, paramID string) (r ParamResult) { - resp, err := client.Get(getGlobalParamURL(client, versionID, paramID), &r.Body, nil) +func GetGlobalParam(ctx context.Context, client *gophercloud.ServiceClient, versionID, paramID string) (r ParamResult) { + resp, err := client.Get(ctx, getGlobalParamURL(client, versionID, paramID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/configurations/testing/requests_test.go b/openstack/db/v1/configurations/testing/requests_test.go index e7a9ba6229..4694e0b73d 100644 --- a/openstack/db/v1/configurations/testing/requests_test.go +++ b/openstack/db/v1/configurations/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/db/v1/configurations" @@ -31,7 +32,7 @@ func TestList(t *testing.T) { fixture.SetupHandler(t, _baseURL, "GET", "", ListConfigsJSON, 200) count := 0 - err := configurations.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := configurations.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := configurations.ExtractConfigs(page) th.AssertNoErr(t, err) @@ -51,7 +52,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() fixture.SetupHandler(t, resURL, "GET", "", GetConfigJSON, 200) - config, err := configurations.Get(fake.ServiceClient(), configID).Extract() + config, err := configurations.Get(context.TODO(), fake.ServiceClient(), configID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExampleConfig, config) } @@ -74,7 +75,7 @@ func TestCreate(t *testing.T) { }, } - config, err := configurations.Create(fake.ServiceClient(), opts).Extract() + config, err := configurations.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExampleConfigWithValues, config) } @@ -90,7 +91,7 @@ func TestUpdate(t *testing.T) { }, } - err := configurations.Update(fake.ServiceClient(), configID, opts).ExtractErr() + err := configurations.Update(context.TODO(), fake.ServiceClient(), configID, opts).ExtractErr() th.AssertNoErr(t, err) } @@ -105,7 +106,7 @@ func TestReplace(t *testing.T) { }, } - err := configurations.Replace(fake.ServiceClient(), configID, opts).ExtractErr() + err := configurations.Replace(context.TODO(), fake.ServiceClient(), configID, opts).ExtractErr() th.AssertNoErr(t, err) } @@ -114,7 +115,7 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() fixture.SetupHandler(t, resURL, "DELETE", "", "", 202) - err := configurations.Delete(fake.ServiceClient(), configID).ExtractErr() + err := configurations.Delete(context.TODO(), fake.ServiceClient(), configID).ExtractErr() th.AssertNoErr(t, err) } @@ -129,7 +130,7 @@ func TestListInstances(t *testing.T) { } pages := 0 - err := configurations.ListInstances(fake.ServiceClient(), configID).EachPage(func(page pagination.Page) (bool, error) { + err := configurations.ListInstances(fake.ServiceClient(), configID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := instances.ExtractInstances(page) @@ -152,7 +153,7 @@ func TestListDSParams(t *testing.T) { fixture.SetupHandler(t, dsParamListURL, "GET", "", ListParamsJSON, 200) pages := 0 - err := configurations.ListDatastoreParams(fake.ServiceClient(), dsID, versionID).EachPage(func(page pagination.Page) (bool, error) { + err := configurations.ListDatastoreParams(fake.ServiceClient(), dsID, versionID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := configurations.ExtractParams(page) @@ -181,7 +182,7 @@ func TestGetDSParam(t *testing.T) { defer th.TeardownHTTP() fixture.SetupHandler(t, dsParamGetURL, "GET", "", GetParamJSON, 200) - param, err := configurations.GetDatastoreParam(fake.ServiceClient(), dsID, versionID, paramID).Extract() + param, err := configurations.GetDatastoreParam(context.TODO(), fake.ServiceClient(), dsID, versionID, paramID).Extract() th.AssertNoErr(t, err) expected := &configurations.Param{ @@ -197,7 +198,7 @@ func TestListGlobalParams(t *testing.T) { fixture.SetupHandler(t, globalParamListURL, "GET", "", ListParamsJSON, 200) pages := 0 - err := configurations.ListGlobalParams(fake.ServiceClient(), versionID).EachPage(func(page pagination.Page) (bool, error) { + err := configurations.ListGlobalParams(fake.ServiceClient(), versionID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := configurations.ExtractParams(page) @@ -226,7 +227,7 @@ func TestGetGlobalParam(t *testing.T) { defer th.TeardownHTTP() fixture.SetupHandler(t, globalParamGetURL, "GET", "", GetParamJSON, 200) - param, err := configurations.GetGlobalParam(fake.ServiceClient(), versionID, paramID).Extract() + param, err := configurations.GetGlobalParam(context.TODO(), fake.ServiceClient(), versionID, paramID).Extract() th.AssertNoErr(t, err) expected := &configurations.Param{ diff --git a/openstack/db/v1/databases/requests.go b/openstack/db/v1/databases/requests.go index 92450538c4..bea255e48c 100644 --- a/openstack/db/v1/databases/requests.go +++ b/openstack/db/v1/databases/requests.go @@ -1,6 +1,8 @@ package databases import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -62,13 +64,13 @@ func (opts BatchCreateOpts) ToDBCreateMap() (map[string]interface{}, error) { // Create will create a new database within the specified instance. If the // specified instance does not exist, a 404 error will be returned. -func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToDBCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(baseURL(client, instanceID), &b, nil, nil) + resp, err := client.Post(ctx, baseURL(client, instanceID), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -84,8 +86,8 @@ func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager // Delete will permanently delete the database within a specified instance. // All contained data inside the database will also be permanently deleted. -func Delete(client *gophercloud.ServiceClient, instanceID, dbName string) (r DeleteResult) { - resp, err := client.Delete(dbURL(client, instanceID, dbName), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, instanceID, dbName string) (r DeleteResult) { + resp, err := client.Delete(ctx, dbURL(client, instanceID, dbName), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/databases/testing/requests_test.go b/openstack/db/v1/databases/testing/requests_test.go index f6a9798405..d509a7f1a9 100644 --- a/openstack/db/v1/databases/testing/requests_test.go +++ b/openstack/db/v1/databases/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" @@ -19,7 +20,7 @@ func TestCreate(t *testing.T) { databases.CreateOpts{Name: "sampledb"}, } - res := databases.Create(fake.ServiceClient(), instanceID, opts) + res := databases.Create(context.TODO(), fake.ServiceClient(), instanceID, opts) th.AssertNoErr(t, res.Err) } @@ -37,7 +38,7 @@ func TestList(t *testing.T) { } pages := 0 - err := databases.List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) { + err := databases.List(fake.ServiceClient(), instanceID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := databases.ExtractDBs(page) @@ -62,6 +63,6 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() HandleDelete(t) - err := databases.Delete(fake.ServiceClient(), instanceID, "{dbName}").ExtractErr() + err := databases.Delete(context.TODO(), fake.ServiceClient(), instanceID, "{dbName}").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/db/v1/datastores/requests.go b/openstack/db/v1/datastores/requests.go index c0dc9f1b4c..16bc5bde36 100644 --- a/openstack/db/v1/datastores/requests.go +++ b/openstack/db/v1/datastores/requests.go @@ -1,6 +1,8 @@ package datastores import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -13,8 +15,8 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { } // Get will retrieve the details of a specified datastore type. -func Get(client *gophercloud.ServiceClient, datastoreID string) (r GetResult) { - resp, err := client.Get(resourceURL(client, datastoreID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, datastoreID string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, datastoreID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -28,8 +30,8 @@ func ListVersions(client *gophercloud.ServiceClient, datastoreID string) paginat } // GetVersion will retrieve the details of a specified datastore version. -func GetVersion(client *gophercloud.ServiceClient, datastoreID, versionID string) (r GetVersionResult) { - resp, err := client.Get(versionURL(client, datastoreID, versionID), &r.Body, nil) +func GetVersion(ctx context.Context, client *gophercloud.ServiceClient, datastoreID, versionID string) (r GetVersionResult) { + resp, err := client.Get(ctx, versionURL(client, datastoreID, versionID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/datastores/testing/requests_test.go b/openstack/db/v1/datastores/testing/requests_test.go index e0d55d946c..47e3dbfae4 100644 --- a/openstack/db/v1/datastores/testing/requests_test.go +++ b/openstack/db/v1/datastores/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/db/v1/datastores" @@ -17,7 +18,7 @@ func TestList(t *testing.T) { pages := 0 - err := datastores.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := datastores.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := datastores.ExtractDatastores(page) @@ -39,7 +40,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() fixture.SetupHandler(t, "/datastores/{dsID}", "GET", "", GetDSResp, 200) - ds, err := datastores.Get(fake.ServiceClient(), "{dsID}").Extract() + ds, err := datastores.Get(context.TODO(), fake.ServiceClient(), "{dsID}").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExampleDatastore, ds) } @@ -51,7 +52,7 @@ func TestListVersions(t *testing.T) { pages := 0 - err := datastores.ListVersions(fake.ServiceClient(), "{dsID}").EachPage(func(page pagination.Page) (bool, error) { + err := datastores.ListVersions(fake.ServiceClient(), "{dsID}").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := datastores.ExtractVersions(page) @@ -73,7 +74,7 @@ func TestGetVersion(t *testing.T) { defer th.TeardownHTTP() fixture.SetupHandler(t, "/datastores/{dsID}/versions/{versionID}", "GET", "", GetVersionResp, 200) - ds, err := datastores.GetVersion(fake.ServiceClient(), "{dsID}", "{versionID}").Extract() + ds, err := datastores.GetVersion(context.TODO(), fake.ServiceClient(), "{dsID}", "{versionID}").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &ExampleVersion1, ds) } diff --git a/openstack/db/v1/flavors/requests.go b/openstack/db/v1/flavors/requests.go index 1f9bab8af3..740fe60aa8 100644 --- a/openstack/db/v1/flavors/requests.go +++ b/openstack/db/v1/flavors/requests.go @@ -1,6 +1,8 @@ package flavors import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -15,8 +17,8 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { } // Get will retrieve information for a specified hardware flavor. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/flavors/testing/requests_test.go b/openstack/db/v1/flavors/testing/requests_test.go index cf95072695..c4a419627a 100644 --- a/openstack/db/v1/flavors/testing/requests_test.go +++ b/openstack/db/v1/flavors/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -16,7 +17,7 @@ func TestListFlavors(t *testing.T) { HandleList(t) pages := 0 - err := flavors.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := flavors.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := flavors.ExtractFlavors(page) @@ -90,7 +91,7 @@ func TestGetFlavor(t *testing.T) { defer th.TeardownHTTP() HandleGet(t) - actual, err := flavors.Get(fake.ServiceClient(), flavorID).Extract() + actual, err := flavors.Get(context.TODO(), fake.ServiceClient(), flavorID).Extract() th.AssertNoErr(t, err) expected := &flavors.Flavor{ diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go index 7699f3fe20..cc8953bec3 100644 --- a/openstack/db/v1/instances/requests.go +++ b/openstack/db/v1/instances/requests.go @@ -1,6 +1,8 @@ package instances import ( + "context" + "github.com/gophercloud/gophercloud/v2" db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/v2/openstack/db/v1/users" @@ -157,13 +159,13 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { // Although this call only allows the creation of 1 instance per request, you // can create an instance with multiple databases and users. The default // binding for a MySQL instance is port 3306. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToInstanceCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + resp, err := client.Post(ctx, baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -176,23 +178,23 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { } // Get retrieves the status and information for a specified database instance. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(resourceURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete permanently destroys the database instance. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(resourceURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, resourceURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // EnableRootUser enables the login from any host for the root user and // provides the user with a generated root password. -func EnableRootUser(client *gophercloud.ServiceClient, id string) (r EnableRootUserResult) { - resp, err := client.Post(userRootURL(client, id), nil, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) +func EnableRootUser(ctx context.Context, client *gophercloud.ServiceClient, id string) (r EnableRootUserResult) { + resp, err := client.Post(ctx, userRootURL(client, id), nil, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -200,8 +202,8 @@ func EnableRootUser(client *gophercloud.ServiceClient, id string) (r EnableRootU // IsRootEnabled checks an instance to see if root access is enabled. It returns // True if root user is enabled for the specified database instance or False // otherwise. -func IsRootEnabled(client *gophercloud.ServiceClient, id string) (r IsRootEnabledResult) { - resp, err := client.Get(userRootURL(client, id), &r.Body, nil) +func IsRootEnabled(ctx context.Context, client *gophercloud.ServiceClient, id string) (r IsRootEnabledResult) { + resp, err := client.Get(ctx, userRootURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -209,18 +211,18 @@ func IsRootEnabled(client *gophercloud.ServiceClient, id string) (r IsRootEnable // Restart will restart only the MySQL Instance. Restarting MySQL will // erase any dynamic configuration settings that you have made within MySQL. // The MySQL service will be unavailable until the instance restarts. -func Restart(client *gophercloud.ServiceClient, id string) (r ActionResult) { +func Restart(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { b := map[string]interface{}{"restart": struct{}{}} - resp, err := client.Post(actionURL(client, id), &b, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Resize changes the memory size of the instance, assuming a valid // flavorRef is provided. It will also restart the MySQL service. -func Resize(client *gophercloud.ServiceClient, id, flavorRef string) (r ActionResult) { +func Resize(ctx context.Context, client *gophercloud.ServiceClient, id, flavorRef string) (r ActionResult) { b := map[string]interface{}{"resize": map[string]string{"flavorRef": flavorRef}} - resp, err := client.Post(actionURL(client, id), &b, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -228,25 +230,25 @@ func Resize(client *gophercloud.ServiceClient, id, flavorRef string) (r ActionRe // ResizeVolume will resize the attached volume for an instance. It supports // only increasing the volume size and does not support decreasing the size. // The volume size is in gigabytes (GB) and must be an integer. -func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) (r ActionResult) { +func ResizeVolume(ctx context.Context, client *gophercloud.ServiceClient, id string, size int) (r ActionResult) { b := map[string]interface{}{"resize": map[string]interface{}{"volume": map[string]int{"size": size}}} - resp, err := client.Post(actionURL(client, id), &b, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // AttachConfigurationGroup will attach configuration group to the instance -func AttachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string, configID string) (r ConfigurationResult) { +func AttachConfigurationGroup(ctx context.Context, client *gophercloud.ServiceClient, instanceID string, configID string) (r ConfigurationResult) { b := map[string]interface{}{"instance": map[string]interface{}{"configuration": configID}} - resp, err := client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := client.Put(ctx, resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DetachConfigurationGroup will dettach configuration group from the instance -func DetachConfigurationGroup(client *gophercloud.ServiceClient, instanceID string) (r ConfigurationResult) { +func DetachConfigurationGroup(ctx context.Context, client *gophercloud.ServiceClient, instanceID string) (r ConfigurationResult) { b := map[string]interface{}{"instance": map[string]interface{}{}} - resp, err := client.Put(resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := client.Put(ctx, resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/instances/testing/requests_test.go b/openstack/db/v1/instances/testing/requests_test.go index c4d3fa1898..1e08a796ac 100644 --- a/openstack/db/v1/instances/testing/requests_test.go +++ b/openstack/db/v1/instances/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" @@ -38,7 +39,7 @@ func TestCreate(t *testing.T) { VolumeType: "ssd", } - instance, err := instances.Create(fake.ServiceClient(), opts).Extract() + instance, err := instances.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expectedInstance, instance) @@ -71,7 +72,7 @@ func TestCreateWithFault(t *testing.T) { VolumeType: "ssd", } - instance, err := instances.Create(fake.ServiceClient(), opts).Extract() + instance, err := instances.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expectedInstanceWithFault, instance) @@ -83,7 +84,7 @@ func TestInstanceList(t *testing.T) { HandleList(t) pages := 0 - err := instances.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := instances.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := instances.ExtractInstances(page) @@ -104,7 +105,7 @@ func TestGetInstance(t *testing.T) { defer th.TeardownHTTP() HandleGet(t) - instance, err := instances.Get(fake.ServiceClient(), instanceID).Extract() + instance, err := instances.Get(context.TODO(), fake.ServiceClient(), instanceID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expectedGetInstance, instance) @@ -115,7 +116,7 @@ func TestDeleteInstance(t *testing.T) { defer th.TeardownHTTP() HandleDelete(t) - res := instances.Delete(fake.ServiceClient(), instanceID) + res := instances.Delete(context.TODO(), fake.ServiceClient(), instanceID) th.AssertNoErr(t, res.Err) } @@ -125,7 +126,7 @@ func TestEnableRootUser(t *testing.T) { HandleEnableRoot(t) expected := &users.User{Name: "root", Password: "secretsecret"} - user, err := instances.EnableRootUser(fake.ServiceClient(), instanceID).Extract() + user, err := instances.EnableRootUser(context.TODO(), fake.ServiceClient(), instanceID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, user) @@ -136,7 +137,7 @@ func TestIsRootEnabled(t *testing.T) { defer th.TeardownHTTP() HandleIsRootEnabled(t) - isEnabled, err := instances.IsRootEnabled(fake.ServiceClient(), instanceID).Extract() + isEnabled, err := instances.IsRootEnabled(context.TODO(), fake.ServiceClient(), instanceID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, isEnabled) @@ -147,7 +148,7 @@ func TestRestart(t *testing.T) { defer th.TeardownHTTP() HandleRestart(t) - res := instances.Restart(fake.ServiceClient(), instanceID) + res := instances.Restart(context.TODO(), fake.ServiceClient(), instanceID) th.AssertNoErr(t, res.Err) } @@ -156,7 +157,7 @@ func TestResize(t *testing.T) { defer th.TeardownHTTP() HandleResize(t) - res := instances.Resize(fake.ServiceClient(), instanceID, "2") + res := instances.Resize(context.TODO(), fake.ServiceClient(), instanceID, "2") th.AssertNoErr(t, res.Err) } @@ -165,7 +166,7 @@ func TestResizeVolume(t *testing.T) { defer th.TeardownHTTP() HandleResizeVol(t) - res := instances.ResizeVolume(fake.ServiceClient(), instanceID, 4) + res := instances.ResizeVolume(context.TODO(), fake.ServiceClient(), instanceID, 4) th.AssertNoErr(t, res.Err) } @@ -174,7 +175,7 @@ func TestAttachConfigurationGroup(t *testing.T) { defer th.TeardownHTTP() HandleAttachConfigurationGroup(t) - res := instances.AttachConfigurationGroup(fake.ServiceClient(), instanceID, configGroupID) + res := instances.AttachConfigurationGroup(context.TODO(), fake.ServiceClient(), instanceID, configGroupID) th.AssertNoErr(t, res.Err) } @@ -183,6 +184,6 @@ func TestDetachConfigurationGroup(t *testing.T) { defer th.TeardownHTTP() HandleDetachConfigurationGroup(t) - res := instances.DetachConfigurationGroup(fake.ServiceClient(), instanceID) + res := instances.DetachConfigurationGroup(context.TODO(), fake.ServiceClient(), instanceID) th.AssertNoErr(t, res.Err) } diff --git a/openstack/db/v1/users/requests.go b/openstack/db/v1/users/requests.go index 094403fce4..d83bdc3c57 100644 --- a/openstack/db/v1/users/requests.go +++ b/openstack/db/v1/users/requests.go @@ -1,6 +1,8 @@ package users import ( + "context" + "github.com/gophercloud/gophercloud/v2" db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" "github.com/gophercloud/gophercloud/v2/pagination" @@ -65,13 +67,13 @@ func (opts BatchCreateOpts) ToUserCreateMap() (map[string]interface{}, error) { // instance based on the configuration defined in CreateOpts. If databases are // assigned for a particular user, the user will be granted all privileges // for those specified databases. "root" is a reserved name and cannot be used. -func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToUserCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(baseURL(client, instanceID), &b, nil, nil) + resp, err := client.Post(ctx, baseURL(client, instanceID), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -86,8 +88,8 @@ func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager } // Delete will permanently delete a user from a specified database instance. -func Delete(client *gophercloud.ServiceClient, instanceID, userName string) (r DeleteResult) { - resp, err := client.Delete(userURL(client, instanceID, userName), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, instanceID, userName string) (r DeleteResult) { + resp, err := client.Delete(ctx, userURL(client, instanceID, userName), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/db/v1/users/testing/requests_test.go b/openstack/db/v1/users/testing/requests_test.go index 2a15f811a9..40713f5446 100644 --- a/openstack/db/v1/users/testing/requests_test.go +++ b/openstack/db/v1/users/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" db "github.com/gophercloud/gophercloud/v2/openstack/db/v1/databases" @@ -33,7 +34,7 @@ func TestCreate(t *testing.T) { }, } - res := users.Create(fake.ServiceClient(), instanceID, opts) + res := users.Create(context.TODO(), fake.ServiceClient(), instanceID, opts) th.AssertNoErr(t, res.Err) } @@ -59,7 +60,7 @@ func TestUserList(t *testing.T) { } pages := 0 - err := users.List(fake.ServiceClient(), instanceID).EachPage(func(page pagination.Page) (bool, error) { + err := users.List(fake.ServiceClient(), instanceID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := users.ExtractUsers(page) @@ -80,6 +81,6 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() HandleDelete(t) - res := users.Delete(fake.ServiceClient(), instanceID, "{userName}") + res := users.Delete(context.TODO(), fake.ServiceClient(), instanceID, "{userName}") th.AssertNoErr(t, res.Err) } diff --git a/openstack/dns/v2/recordsets/requests.go b/openstack/dns/v2/recordsets/requests.go index 36534bfa45..a89bc8134a 100644 --- a/openstack/dns/v2/recordsets/requests.go +++ b/openstack/dns/v2/recordsets/requests.go @@ -1,6 +1,8 @@ package recordsets import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -56,8 +58,8 @@ func ListByZone(client *gophercloud.ServiceClient, zoneID string, opts ListOptsB } // Get implements the recordset Get request. -func Get(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) { - resp, err := client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) { + resp, err := client.Get(ctx, rrsetURL(client, zoneID, rrsetID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -98,13 +100,13 @@ func (opts CreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) { } // Create creates a recordset in a given zone. -func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRecordSetCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -154,13 +156,13 @@ func (opts UpdateOpts) ToRecordSetUpdateMap() (map[string]interface{}, error) { } // Update updates a recordset in a given zone -func Update(client *gophercloud.ServiceClient, zoneID string, rrsetID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, rrsetID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRecordSetUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -168,8 +170,8 @@ func Update(client *gophercloud.ServiceClient, zoneID string, rrsetID string, op } // Delete removes an existing RecordSet. -func Delete(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) { - resp, err := client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) { + resp, err := client.Delete(ctx, rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/dns/v2/recordsets/testing/requests_test.go b/openstack/dns/v2/recordsets/testing/requests_test.go index 6624e242b7..05dde60ed3 100644 --- a/openstack/dns/v2/recordsets/testing/requests_test.go +++ b/openstack/dns/v2/recordsets/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "encoding/json" "testing" @@ -16,7 +17,7 @@ func TestListByZone(t *testing.T) { HandleListByZoneSuccessfully(t) count := 0 - err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", nil).EachPage(func(page pagination.Page) (bool, error) { + err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := recordsets.ExtractRecordSets(page) th.AssertNoErr(t, err) @@ -38,7 +39,7 @@ func TestListByZoneLimited(t *testing.T) { Limit: 1, Marker: "f7b10e9b-0cae-4a91-b162-562bc6096648", } - err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", listOpts).EachPage(func(page pagination.Page) (bool, error) { + err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := recordsets.ExtractRecordSets(page) th.AssertNoErr(t, err) @@ -55,7 +56,7 @@ func TestListByZoneAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListByZoneSuccessfully(t) - allPages, err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", nil).AllPages() + allPages, err := recordsets.ListByZone(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allRecordSets, err := recordsets.ExtractRecordSets(allPages) th.AssertNoErr(t, err) @@ -67,7 +68,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := recordsets.Get(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", "f7b10e9b-0cae-4a91-b162-562bc6096648").Extract() + actual, err := recordsets.Get(context.TODO(), client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", "f7b10e9b-0cae-4a91-b162-562bc6096648").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstRecordSet, actual) } @@ -99,7 +100,7 @@ func TestCreate(t *testing.T) { Records: []string{"10.1.0.2"}, } - actual, err := recordsets.Create(client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", createOpts).Extract() + actual, err := recordsets.Create(context.TODO(), client.ServiceClient(), "2150b1bf-dee2-4221-9d85-11f7886fb15f", createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedRecordSet, actual) } @@ -124,7 +125,7 @@ func TestUpdate(t *testing.T) { UpdatedRecordSet.Records = []string{"10.1.0.2", "10.1.0.3"} UpdatedRecordSet.Version = 2 - actual, err := recordsets.Update(client.ServiceClient(), UpdatedRecordSet.ZoneID, UpdatedRecordSet.ID, updateOpts).Extract() + actual, err := recordsets.Update(context.TODO(), client.ServiceClient(), UpdatedRecordSet.ZoneID, UpdatedRecordSet.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &UpdatedRecordSet, actual) } @@ -141,7 +142,7 @@ func TestDelete(t *testing.T) { DeletedRecordSet.Records = []string{"10.1.0.2", "10.1.0.3"} DeletedRecordSet.Version = 2 - err := recordsets.Delete(client.ServiceClient(), DeletedRecordSet.ZoneID, DeletedRecordSet.ID).ExtractErr() + err := recordsets.Delete(context.TODO(), client.ServiceClient(), DeletedRecordSet.ZoneID, DeletedRecordSet.ID).ExtractErr() th.AssertNoErr(t, err) //th.CheckDeepEquals(t, &DeletedZone, actual) } diff --git a/openstack/dns/v2/transfer/accept/requests.go b/openstack/dns/v2/transfer/accept/requests.go index dc98e6d10e..ff4112dc29 100644 --- a/openstack/dns/v2/transfer/accept/requests.go +++ b/openstack/dns/v2/transfer/accept/requests.go @@ -1,6 +1,7 @@ package accept import ( + "context" "net/http" "github.com/gophercloud/gophercloud/v2" @@ -42,8 +43,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get returns information about a transfer accept, given its ID. -func Get(client *gophercloud.ServiceClient, transferAcceptID string) (r GetResult) { - resp, err := client.Get(resourceURL(client, transferAcceptID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, transferAcceptID string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, transferAcceptID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -74,13 +75,13 @@ func (opts CreateOpts) ToTransferAcceptCreateMap() (map[string]interface{}, erro } // Create implements a transfer accept create request. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTransferAcceptCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{http.StatusCreated, http.StatusAccepted}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/dns/v2/transfer/accept/testing/accepts_test.go b/openstack/dns/v2/transfer/accept/testing/accepts_test.go index 49e5e75380..db868bd779 100644 --- a/openstack/dns/v2/transfer/accept/testing/accepts_test.go +++ b/openstack/dns/v2/transfer/accept/testing/accepts_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" transferAccepts "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/accept" @@ -16,7 +17,8 @@ func TestList(t *testing.T) { count := 0 err := transferAccepts.List(client.ServiceClient(), nil).EachPage( - func(page pagination.Page) (bool, error) { + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := transferAccepts.ExtractTransferAccepts(page) th.AssertNoErr(t, err) @@ -36,7 +38,7 @@ func TestListWithOpts(t *testing.T) { Status: "ACTIVE", } - allPages, err := transferAccepts.List(client.ServiceClient(), listOpts).AllPages() + allPages, err := transferAccepts.List(client.ServiceClient(), listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allPages) th.AssertNoErr(t, err) @@ -48,7 +50,7 @@ func TestListAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListSuccessfully(t) - allPages, err := transferAccepts.List(client.ServiceClient(), nil).AllPages() + allPages, err := transferAccepts.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allTransferAccepts, err := transferAccepts.ExtractTransferAccepts(allPages) th.AssertNoErr(t, err) @@ -61,7 +63,7 @@ func TestGet(t *testing.T) { HandleGetSuccessfully(t) actual, err := transferAccepts.Get( - client.ServiceClient(), "92236f39-0fad-4f8f-bf25-fbdf027de34d").Extract() + context.TODO(), client.ServiceClient(), "92236f39-0fad-4f8f-bf25-fbdf027de34d").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstTransferAccept, actual) } @@ -77,7 +79,7 @@ func TestCreate(t *testing.T) { } actual, err := transferAccepts.Create( - client.ServiceClient(), createOpts).Extract() + context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedTransferAccept, actual) } diff --git a/openstack/dns/v2/transfer/request/requests.go b/openstack/dns/v2/transfer/request/requests.go index 46eaf9473b..55c4823591 100644 --- a/openstack/dns/v2/transfer/request/requests.go +++ b/openstack/dns/v2/transfer/request/requests.go @@ -1,6 +1,7 @@ package request import ( + "context" "net/http" "github.com/gophercloud/gophercloud/v2" @@ -42,8 +43,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get returns information about a transfer request, given its ID. -func Get(client *gophercloud.ServiceClient, transferRequestID string) (r GetResult) { - resp, err := client.Get(resourceURL(client, transferRequestID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, transferRequestID string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, transferRequestID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -74,13 +75,13 @@ func (opts CreateOpts) ToTransferRequestCreateMap() (map[string]interface{}, err } // Create implements a transfer request create request. -func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTransferRequestCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{http.StatusCreated, http.StatusAccepted}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -113,13 +114,13 @@ func (opts UpdateOpts) ToTransferRequestUpdateMap() (map[string]interface{}, err } // Update implements a transfer request update request. -func Update(client *gophercloud.ServiceClient, transferID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, transferID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToTransferRequestUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(resourceURL(client, transferID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, resourceURL(client, transferID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{http.StatusOK, http.StatusAccepted}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -127,8 +128,8 @@ func Update(client *gophercloud.ServiceClient, transferID string, opts UpdateOpt } // Delete implements a transfer request delete request. -func Delete(client *gophercloud.ServiceClient, transferID string) (r DeleteResult) { - resp, err := client.Delete(resourceURL(client, transferID), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, transferID string) (r DeleteResult) { + resp, err := client.Delete(ctx, resourceURL(client, transferID), &gophercloud.RequestOpts{ OkCodes: []int{http.StatusNoContent}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/dns/v2/transfer/request/testing/requests_test.go b/openstack/dns/v2/transfer/request/testing/requests_test.go index c7ebe5832d..daad13b664 100644 --- a/openstack/dns/v2/transfer/request/testing/requests_test.go +++ b/openstack/dns/v2/transfer/request/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" transferRequests "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/request" @@ -16,7 +17,8 @@ func TestList(t *testing.T) { count := 0 err := transferRequests.List(client.ServiceClient(), nil).EachPage( - func(page pagination.Page) (bool, error) { + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := transferRequests.ExtractTransferRequests(page) th.AssertNoErr(t, err) @@ -36,7 +38,7 @@ func TestListWithOpts(t *testing.T) { Status: "ACTIVE", } - allPages, err := transferRequests.List(client.ServiceClient(), listOpts).AllPages() + allPages, err := transferRequests.List(client.ServiceClient(), listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allTransferRequests, err := transferRequests.ExtractTransferRequests(allPages) th.AssertNoErr(t, err) @@ -48,7 +50,7 @@ func TestListAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListSuccessfully(t) - allPages, err := transferRequests.List(client.ServiceClient(), nil).AllPages() + allPages, err := transferRequests.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allTransferRequests, err := transferRequests.ExtractTransferRequests(allPages) th.AssertNoErr(t, err) @@ -61,7 +63,7 @@ func TestGet(t *testing.T) { HandleGetSuccessfully(t) actual, err := transferRequests.Get( - client.ServiceClient(), "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3").Extract() + context.TODO(), client.ServiceClient(), "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstTransferRequest, actual) } @@ -77,7 +79,7 @@ func TestCreate(t *testing.T) { } actual, err := transferRequests.Create( - client.ServiceClient(), FirstTransferRequest.ZoneID, createOpts).Extract() + context.TODO(), client.ServiceClient(), FirstTransferRequest.ZoneID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedTransferRequest, actual) } @@ -96,7 +98,7 @@ func TestUpdate(t *testing.T) { UpdatedTransferRequest.Description = "Updated Description" actual, err := transferRequests.Update( - client.ServiceClient(), UpdatedTransferRequest.ID, updateOpts).Extract() + context.TODO(), client.ServiceClient(), UpdatedTransferRequest.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &UpdatedTransferRequest, actual) } @@ -108,6 +110,6 @@ func TestDelete(t *testing.T) { DeletedZone := CreatedTransferRequest - err := transferRequests.Delete(client.ServiceClient(), DeletedZone.ID).ExtractErr() + err := transferRequests.Delete(context.TODO(), client.ServiceClient(), DeletedZone.ID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/dns/v2/zones/requests.go b/openstack/dns/v2/zones/requests.go index 08da3d80a3..aa6844f600 100644 --- a/openstack/dns/v2/zones/requests.go +++ b/openstack/dns/v2/zones/requests.go @@ -1,6 +1,8 @@ package zones import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -54,8 +56,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get returns information about a zone, given its ID. -func Get(client *gophercloud.ServiceClient, zoneID string) (r GetResult) { - resp, err := client.Get(zoneURL(client, zoneID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, zoneID string) (r GetResult) { + resp, err := client.Get(ctx, zoneURL(client, zoneID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -105,13 +107,13 @@ func (opts CreateOpts) ToZoneCreateMap() (map[string]interface{}, error) { } // Create implements a zone create request. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToZoneCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -154,13 +156,13 @@ func (opts UpdateOpts) ToZoneUpdateMap() (map[string]interface{}, error) { } // Update implements a zone update request. -func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToZoneUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -168,8 +170,8 @@ func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBui } // Delete implements a zone delete request. -func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { - resp, err := client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { + resp, err := client.Delete(ctx, zoneURL(client, zoneID), &gophercloud.RequestOpts{ OkCodes: []int{202}, JSONResponse: &r.Body, }) diff --git a/openstack/dns/v2/zones/testing/requests_test.go b/openstack/dns/v2/zones/testing/requests_test.go index 2283c1b955..0b64e81ecf 100644 --- a/openstack/dns/v2/zones/testing/requests_test.go +++ b/openstack/dns/v2/zones/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/zones" @@ -15,7 +16,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t) count := 0 - err := zones.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := zones.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := zones.ExtractZones(page) th.AssertNoErr(t, err) @@ -32,7 +33,7 @@ func TestListAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListSuccessfully(t) - allPages, err := zones.List(client.ServiceClient(), nil).AllPages() + allPages, err := zones.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) allZones, err := zones.ExtractZones(allPages) th.AssertNoErr(t, err) @@ -44,7 +45,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := zones.Get(client.ServiceClient(), "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3").Extract() + actual, err := zones.Get(context.TODO(), client.ServiceClient(), "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstZone, actual) } @@ -62,7 +63,7 @@ func TestCreate(t *testing.T) { Description: "This is an example zone.", } - actual, err := zones.Create(client.ServiceClient(), createOpts).Extract() + actual, err := zones.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &CreatedZone, actual) } @@ -84,7 +85,7 @@ func TestUpdate(t *testing.T) { UpdatedZone.TTL = 600 UpdatedZone.Description = "Updated Description" - actual, err := zones.Update(client.ServiceClient(), UpdatedZone.ID, updateOpts).Extract() + actual, err := zones.Update(context.TODO(), client.ServiceClient(), UpdatedZone.ID, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &UpdatedZone, actual) } @@ -100,7 +101,7 @@ func TestDelete(t *testing.T) { DeletedZone.TTL = 600 DeletedZone.Description = "Updated Description" - actual, err := zones.Delete(client.ServiceClient(), DeletedZone.ID).Extract() + actual, err := zones.Delete(context.TODO(), client.ServiceClient(), DeletedZone.ID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &DeletedZone, actual) } diff --git a/openstack/identity/v2/extensions/admin/roles/requests.go b/openstack/identity/v2/extensions/admin/roles/requests.go index abaed4ba80..c5c1140c16 100644 --- a/openstack/identity/v2/extensions/admin/roles/requests.go +++ b/openstack/identity/v2/extensions/admin/roles/requests.go @@ -1,6 +1,8 @@ package roles import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -16,8 +18,8 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // AddUser is the operation responsible for assigning a particular role to // a user. This is confined to the scope of the user's tenant - so the tenant // ID is a required argument. -func AddUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { - resp, err := client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, &gophercloud.RequestOpts{ +func AddUser(ctx context.Context, client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { + resp, err := client.Put(ctx, userRoleURL(client, tenantID, userID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -27,8 +29,8 @@ func AddUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) // DeleteUser is the operation responsible for deleting a particular role // from a user. This is confined to the scope of the user's tenant - so the // tenant ID is a required argument. -func DeleteUser(client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { - resp, err := client.Delete(userRoleURL(client, tenantID, userID, roleID), nil) +func DeleteUser(ctx context.Context, client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { + resp, err := client.Delete(ctx, userRoleURL(client, tenantID, userID, roleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go b/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go index 12703aa216..ce14915671 100644 --- a/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go +++ b/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions/admin/roles" @@ -17,7 +18,7 @@ func TestRole(t *testing.T) { count := 0 - err := roles.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := roles.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) if err != nil { @@ -48,7 +49,7 @@ func TestAddUser(t *testing.T) { MockAddUserRoleResponse(t) - err := roles.AddUser(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr() + err := roles.AddUser(context.TODO(), client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr() th.AssertNoErr(t, err) } @@ -59,7 +60,7 @@ func TestDeleteUser(t *testing.T) { MockDeleteUserRoleResponse(t) - err := roles.DeleteUser(client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr() + err := roles.DeleteUser(context.TODO(), client.ServiceClient(), "{tenant_id}", "{user_id}", "{role_id}").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/identity/v2/extensions/delegate.go b/openstack/identity/v2/extensions/delegate.go index 870ee9929b..e918cf6a4d 100644 --- a/openstack/identity/v2/extensions/delegate.go +++ b/openstack/identity/v2/extensions/delegate.go @@ -1,6 +1,8 @@ package extensions import ( + "context" + "github.com/gophercloud/gophercloud/v2" common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/pagination" @@ -31,8 +33,8 @@ func ExtractExtensions(page pagination.Page) ([]common.Extension, error) { } // Get retrieves information for a specific extension using its alias. -func Get(c *gophercloud.ServiceClient, alias string) common.GetResult { - return common.Get(c, alias) +func Get(ctx context.Context, c *gophercloud.ServiceClient, alias string) common.GetResult { + return common.Get(ctx, c, alias) } // List returns a Pager which allows you to iterate over the full collection of extensions. diff --git a/openstack/identity/v2/extensions/testing/delegate_test.go b/openstack/identity/v2/extensions/testing/delegate_test.go index 51be73f115..53c10ab169 100644 --- a/openstack/identity/v2/extensions/testing/delegate_test.go +++ b/openstack/identity/v2/extensions/testing/delegate_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions/testing" @@ -16,7 +17,7 @@ func TestList(t *testing.T) { HandleListExtensionsSuccessfully(t) count := 0 - err := extensions.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := extensions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) th.AssertNoErr(t, err) @@ -33,7 +34,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() common.HandleGetExtensionSuccessfully(t) - actual, err := extensions.Get(client.ServiceClient(), "agent").Extract() + actual, err := extensions.Get(context.TODO(), client.ServiceClient(), "agent").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, common.SingleExtension, actual) } diff --git a/openstack/identity/v2/tenants/requests.go b/openstack/identity/v2/tenants/requests.go index f6de7839d4..4a986b890a 100644 --- a/openstack/identity/v2/tenants/requests.go +++ b/openstack/identity/v2/tenants/requests.go @@ -1,6 +1,8 @@ package tenants import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -54,13 +56,13 @@ func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) { } // Create is the operation responsible for creating new tenant. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTenantCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -68,8 +70,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get requests details on a single tenant by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -99,13 +101,13 @@ func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) { } // Update is the operation responsible for updating exist tenants by their TenantID. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToTenantUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -113,8 +115,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Delete is the operation responsible for permanently deleting a tenant. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v2/tenants/testing/requests_test.go b/openstack/identity/v2/tenants/testing/requests_test.go index 1d75a09a2e..1e3e05ce3c 100644 --- a/openstack/identity/v2/tenants/testing/requests_test.go +++ b/openstack/identity/v2/tenants/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -16,7 +17,7 @@ func TestListTenants(t *testing.T) { HandleListTenantsSuccessfully(t) count := 0 - err := tenants.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := tenants.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := tenants.ExtractTenants(page) @@ -42,7 +43,7 @@ func TestCreateTenant(t *testing.T) { Enabled: gophercloud.Enabled, } - tenant, err := tenants.Create(client.ServiceClient(), opts).Extract() + tenant, err := tenants.Create(context.TODO(), client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) @@ -62,7 +63,7 @@ func TestDeleteTenant(t *testing.T) { mockDeleteTenantResponse(t) - err := tenants.Delete(client.ServiceClient(), "2466f69cd4714d89a548a68ed97ffcd4").ExtractErr() + err := tenants.Delete(context.TODO(), client.ServiceClient(), "2466f69cd4714d89a548a68ed97ffcd4").ExtractErr() th.AssertNoErr(t, err) } @@ -80,7 +81,7 @@ func TestUpdateTenant(t *testing.T) { Enabled: gophercloud.Enabled, } - tenant, err := tenants.Update(client.ServiceClient(), id, opts).Extract() + tenant, err := tenants.Update(context.TODO(), client.ServiceClient(), id, opts).Extract() th.AssertNoErr(t, err) @@ -100,7 +101,7 @@ func TestGetTenant(t *testing.T) { mockGetTenantResponse(t) - tenant, err := tenants.Get(client.ServiceClient(), "5c62ef576dc7444cbb73b1fe84b97648").Extract() + tenant, err := tenants.Get(context.TODO(), client.ServiceClient(), "5c62ef576dc7444cbb73b1fe84b97648").Extract() th.AssertNoErr(t, err) expected := &tenants.Tenant{ diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 9e7b7512e1..03b602ca42 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -81,17 +81,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(ctx context.Context, 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(ctx, CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, OmitHeaders: []string{"X-Auth-Token"}, }) @@ -99,21 +99,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) { - resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ +// Get validates and retrieves information for user's token. +func Get(ctx context.Context, client *gophercloud.ServiceClient, token string) (r GetResult) { + resp, err := client.Get(ctx, GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// 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/v2/tokens/testing/requests_test.go b/openstack/identity/v2/tokens/testing/requests_test.go index ecf53870c0..a688369d65 100644 --- a/openstack/identity/v2/tokens/testing/requests_test.go +++ b/openstack/identity/v2/tokens/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -14,7 +15,7 @@ func tokenPost(t *testing.T, options gophercloud.AuthOptions, requestJSON string defer th.TeardownHTTP() HandleTokenPost(t, requestJSON) - return tokens.Create(client.ServiceClient(), options) + return tokens.Create(context.TODO(), client.ServiceClient(), options) } func tokenPostErr(t *testing.T, options gophercloud.AuthOptions, expectedErr error) { @@ -22,7 +23,7 @@ func tokenPostErr(t *testing.T, options gophercloud.AuthOptions, expectedErr err defer th.TeardownHTTP() HandleTokenPost(t, "") - actualErr := tokens.Create(client.ServiceClient(), options).Err + actualErr := tokens.Create(context.TODO(), client.ServiceClient(), options).Err th.CheckDeepEquals(t, expectedErr, actualErr) } @@ -96,7 +97,7 @@ func tokenGet(t *testing.T, tokenId string) tokens.GetResult { th.SetupHTTP() defer th.TeardownHTTP() HandleTokenGet(t, tokenId) - return tokens.Get(client.ServiceClient(), tokenId) + return tokens.Get(context.TODO(), client.ServiceClient(), tokenId) } func TestGetWithToken(t *testing.T) { diff --git a/openstack/identity/v2/users/requests.go b/openstack/identity/v2/users/requests.go index 7bb60a7c62..4f47a7eed9 100644 --- a/openstack/identity/v2/users/requests.go +++ b/openstack/identity/v2/users/requests.go @@ -1,6 +1,8 @@ package users import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -53,13 +55,13 @@ func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) { } // Create is the operation responsible for creating new users. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToUserCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -67,8 +69,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get requests details on a single user, either by ID or Name. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ResourceURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, ResourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -89,13 +91,13 @@ func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) { } // Update is the operation responsible for updating exist users by their ID. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToUserUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(ResourceURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, ResourceURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -103,8 +105,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Delete is the operation responsible for permanently deleting a User. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ResourceURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, ResourceURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v2/users/testing/requests_test.go b/openstack/identity/v2/users/testing/requests_test.go index 53060a776f..c66207d0c3 100644 --- a/openstack/identity/v2/users/testing/requests_test.go +++ b/openstack/identity/v2/users/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -18,7 +19,7 @@ func TestList(t *testing.T) { count := 0 - err := users.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := users.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := users.ExtractUsers(page) th.AssertNoErr(t, err) @@ -61,7 +62,7 @@ func TestCreateUser(t *testing.T) { Email: "new_user@foo.com", } - user, err := users.Create(client.ServiceClient(), opts).Extract() + user, err := users.Create(context.TODO(), client.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) @@ -82,7 +83,7 @@ func TestGetUser(t *testing.T) { mockGetUserResponse(t) - user, err := users.Get(client.ServiceClient(), "new_user").Extract() + user, err := users.Get(context.TODO(), client.ServiceClient(), "new_user").Extract() th.AssertNoErr(t, err) expected := &users.User{ @@ -109,7 +110,7 @@ func TestUpdateUser(t *testing.T) { Email: "new_email@foo.com", } - user, err := users.Update(client.ServiceClient(), id, opts).Extract() + user, err := users.Update(context.TODO(), client.ServiceClient(), id, opts).Extract() th.AssertNoErr(t, err) @@ -130,7 +131,7 @@ func TestDeleteUser(t *testing.T) { mockDeleteUserResponse(t) - res := users.Delete(client.ServiceClient(), "c39e3de9be2d4c779f1dfd6abacc176d") + res := users.Delete(context.TODO(), client.ServiceClient(), "c39e3de9be2d4c779f1dfd6abacc176d") th.AssertNoErr(t, res.Err) } @@ -143,7 +144,7 @@ func TestListingUserRoles(t *testing.T) { tenantID := "1d8b6120dcc640fda4fc9194ffc80273" userID := "c39e3de9be2d4c779f1dfd6abacc176d" - err := users.ListRoles(client.ServiceClient(), tenantID, userID).EachPage(func(page pagination.Page) (bool, error) { + err := users.ListRoles(client.ServiceClient(), tenantID, userID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := users.ExtractRoles(page) th.AssertNoErr(t, err) diff --git a/openstack/identity/v3/applicationcredentials/requests.go b/openstack/identity/v3/applicationcredentials/requests.go index 5b503092e4..bb8d3b4d3b 100644 --- a/openstack/identity/v3/applicationcredentials/requests.go +++ b/openstack/identity/v3/applicationcredentials/requests.go @@ -1,6 +1,7 @@ package applicationcredentials import ( + "context" "time" "github.com/gophercloud/gophercloud/v2" @@ -41,8 +42,8 @@ func List(client *gophercloud.ServiceClient, userID string, opts ListOptsBuilder } // Get retrieves details on a single user, by ID. -func Get(client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { - resp, err := client.Get(getURL(client, userID, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -93,13 +94,13 @@ func (opts CreateOpts) ToApplicationCredentialCreateMap() (map[string]interface{ } // Create creates a new ApplicationCredential. -func Create(client *gophercloud.ServiceClient, userID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, userID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToApplicationCredentialCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -107,8 +108,8 @@ func Create(client *gophercloud.ServiceClient, userID string, opts CreateOptsBui } // Delete deletes an application credential. -func Delete(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, userID, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -122,15 +123,15 @@ func ListAccessRules(client *gophercloud.ServiceClient, userID string) paginatio } // GetAccessRule retrieves details on a single access rule by ID. -func GetAccessRule(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessRuleResult) { - resp, err := client.Get(getAccessRuleURL(client, userID, id), &r.Body, nil) +func GetAccessRule(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r GetAccessRuleResult) { + resp, err := client.Get(ctx, getAccessRuleURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteAccessRule deletes an access rule. -func DeleteAccessRule(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { - resp, err := client.Delete(deleteAccessRuleURL(client, userID, id), nil) +func DeleteAccessRule(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteAccessRuleURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/applicationcredentials/testing/requests_test.go b/openstack/identity/v3/applicationcredentials/testing/requests_test.go index 8a305f028e..11d863609f 100644 --- a/openstack/identity/v3/applicationcredentials/testing/requests_test.go +++ b/openstack/identity/v3/applicationcredentials/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/applicationcredentials" @@ -15,7 +16,7 @@ func TestListApplicationCredentials(t *testing.T) { HandleListApplicationCredentialsSuccessfully(t) count := 0 - err := applicationcredentials.List(client.ServiceClient(), userID, nil).EachPage(func(page pagination.Page) (bool, error) { + err := applicationcredentials.List(client.ServiceClient(), userID, nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := applicationcredentials.ExtractApplicationCredentials(page) @@ -34,7 +35,7 @@ func TestListApplicationCredentialsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListApplicationCredentialsSuccessfully(t) - allPages, err := applicationcredentials.List(client.ServiceClient(), userID, nil).AllPages() + allPages, err := applicationcredentials.List(client.ServiceClient(), userID, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := applicationcredentials.ExtractApplicationCredentials(allPages) th.AssertNoErr(t, err) @@ -48,7 +49,7 @@ func TestGetApplicationCredential(t *testing.T) { defer th.TeardownHTTP() HandleGetApplicationCredentialSuccessfully(t) - actual, err := applicationcredentials.Get(client.ServiceClient(), userID, applicationCredentialID).Extract() + actual, err := applicationcredentials.Get(context.TODO(), client.ServiceClient(), userID, applicationCredentialID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ApplicationCredential, *actual) } @@ -76,7 +77,7 @@ func TestCreateApplicationCredential(t *testing.T) { ApplicationCredentialResponse := ApplicationCredential ApplicationCredentialResponse.Secret = "mysecret" - actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() + actual, err := applicationcredentials.Create(context.TODO(), client.ServiceClient(), userID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ApplicationCredentialResponse, *actual) } @@ -93,7 +94,7 @@ func TestCreateNoSecretApplicationCredential(t *testing.T) { }, } - actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() + actual, err := applicationcredentials.Create(context.TODO(), client.ServiceClient(), userID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ApplicationCredentialNoSecretResponse, *actual) } @@ -116,7 +117,7 @@ func TestCreateUnrestrictedApplicationCredential(t *testing.T) { UnrestrictedApplicationCredentialResponse := UnrestrictedApplicationCredential UnrestrictedApplicationCredentialResponse.Secret = "generated_secret" - actual, err := applicationcredentials.Create(client.ServiceClient(), userID, createOpts).Extract() + actual, err := applicationcredentials.Create(context.TODO(), client.ServiceClient(), userID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, UnrestrictedApplicationCredentialResponse, *actual) } @@ -126,7 +127,7 @@ func TestDeleteApplicationCredential(t *testing.T) { defer th.TeardownHTTP() HandleDeleteApplicationCredentialSuccessfully(t) - res := applicationcredentials.Delete(client.ServiceClient(), userID, applicationCredentialID) + res := applicationcredentials.Delete(context.TODO(), client.ServiceClient(), userID, applicationCredentialID) th.AssertNoErr(t, res.Err) } @@ -136,7 +137,7 @@ func TestListAccessRules(t *testing.T) { HandleListAccessRulesSuccessfully(t) count := 0 - err := applicationcredentials.ListAccessRules(client.ServiceClient(), userID).EachPage(func(page pagination.Page) (bool, error) { + err := applicationcredentials.ListAccessRules(client.ServiceClient(), userID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := applicationcredentials.ExtractAccessRules(page) @@ -155,7 +156,7 @@ func TestGetAccessRule(t *testing.T) { defer th.TeardownHTTP() HandleGetAccessRuleSuccessfully(t) - actual, err := applicationcredentials.GetAccessRule(client.ServiceClient(), userID, accessRuleID).Extract() + actual, err := applicationcredentials.GetAccessRule(context.TODO(), client.ServiceClient(), userID, accessRuleID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, AccessRule, *actual) } @@ -165,6 +166,6 @@ func TestDeleteAccessRule(t *testing.T) { defer th.TeardownHTTP() HandleDeleteAccessRuleSuccessfully(t) - res := applicationcredentials.DeleteAccessRule(client.ServiceClient(), userID, accessRuleID) + res := applicationcredentials.DeleteAccessRule(context.TODO(), client.ServiceClient(), userID, accessRuleID) th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/catalog/testing/catalog_test.go b/openstack/identity/v3/catalog/testing/catalog_test.go index 0753fdb2c1..4b055081f9 100644 --- a/openstack/identity/v3/catalog/testing/catalog_test.go +++ b/openstack/identity/v3/catalog/testing/catalog_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/catalog" @@ -15,7 +16,7 @@ func TestListCatalog(t *testing.T) { HandleListCatalogSuccessfully(t) count := 0 - err := catalog.List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := catalog.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := catalog.ExtractServiceCatalog(page) diff --git a/openstack/identity/v3/credentials/requests.go b/openstack/identity/v3/credentials/requests.go index c5b9fcb803..978748d156 100644 --- a/openstack/identity/v3/credentials/requests.go +++ b/openstack/identity/v3/credentials/requests.go @@ -1,6 +1,8 @@ package credentials import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -41,8 +43,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details on a single user, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -71,13 +73,13 @@ func (opts CreateOpts) ToCredentialCreateMap() (map[string]interface{}, error) { } // Create creates a new Credential. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCredentialCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -85,8 +87,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes a credential. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -115,13 +117,13 @@ func (opts UpdateOpts) ToCredentialsUpdateMap() (map[string]interface{}, error) } // Update modifies the attributes of a Credential. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToCredentialsUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/identity/v3/credentials/testing/requests_test.go b/openstack/identity/v3/credentials/testing/requests_test.go index 2e7f5995b0..7b90817c59 100644 --- a/openstack/identity/v3/credentials/testing/requests_test.go +++ b/openstack/identity/v3/credentials/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/credentials" @@ -15,7 +16,7 @@ func TestListCredentials(t *testing.T) { HandleListCredentialsSuccessfully(t) count := 0 - err := credentials.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := credentials.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := credentials.ExtractCredentials(page) @@ -34,7 +35,7 @@ func TestListCredentialsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListCredentialsSuccessfully(t) - allPages, err := credentials.List(client.ServiceClient(), nil).AllPages() + allPages, err := credentials.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := credentials.ExtractCredentials(allPages) th.AssertNoErr(t, err) @@ -48,7 +49,7 @@ func TestGetCredential(t *testing.T) { defer th.TeardownHTTP() HandleGetCredentialSuccessfully(t) - actual, err := credentials.Get(client.ServiceClient(), credentialID).Extract() + actual, err := credentials.Get(context.TODO(), client.ServiceClient(), credentialID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, Credential, *actual) } @@ -67,7 +68,7 @@ func TestCreateCredential(t *testing.T) { CredentialResponse := Credential - actual, err := credentials.Create(client.ServiceClient(), createOpts).Extract() + actual, err := credentials.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, CredentialResponse, *actual) } @@ -77,7 +78,7 @@ func TestDeleteCredential(t *testing.T) { defer th.TeardownHTTP() HandleDeleteCredentialSuccessfully(t) - res := credentials.Delete(client.ServiceClient(), credentialID) + res := credentials.Delete(context.TODO(), client.ServiceClient(), credentialID) th.AssertNoErr(t, res.Err) } @@ -93,7 +94,7 @@ func TestUpdateCredential(t *testing.T) { Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", } - actual, err := credentials.Update(client.ServiceClient(), "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", updateOpts).Extract() + actual, err := credentials.Update(context.TODO(), client.ServiceClient(), "2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondCredentialUpdated, *actual) } diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index 377a241d6c..723c511c76 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -1,6 +1,8 @@ package domains import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -50,8 +52,8 @@ func ListAvailable(client *gophercloud.ServiceClient) pagination.Pager { } // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -80,13 +82,13 @@ func (opts CreateOpts) ToDomainCreateMap() (map[string]interface{}, error) { } // Create creates a new Domain. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToDomainCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -94,8 +96,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes a domain. -func Delete(client *gophercloud.ServiceClient, domainID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, domainID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, domainID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, domainID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -124,13 +126,13 @@ func (opts UpdateOpts) ToDomainUpdateMap() (map[string]interface{}, error) { } // Update modifies the attributes of a domain. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToDomainUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/identity/v3/domains/testing/requests_test.go b/openstack/identity/v3/domains/testing/requests_test.go index d47b1a178e..1e0f5ef8f4 100644 --- a/openstack/identity/v3/domains/testing/requests_test.go +++ b/openstack/identity/v3/domains/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" @@ -15,7 +16,7 @@ func TestListAvailableDomains(t *testing.T) { HandleListAvailableDomainsSuccessfully(t) count := 0 - err := domains.ListAvailable(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := domains.ListAvailable(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := domains.ExtractDomains(page) @@ -35,7 +36,7 @@ func TestListDomains(t *testing.T) { HandleListDomainsSuccessfully(t) count := 0 - err := domains.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := domains.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := domains.ExtractDomains(page) @@ -54,7 +55,7 @@ func TestListDomainsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListDomainsSuccessfully(t) - allPages, err := domains.List(client.ServiceClient(), nil).AllPages() + allPages, err := domains.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := domains.ExtractDomains(allPages) th.AssertNoErr(t, err) @@ -66,7 +67,7 @@ func TestGetDomain(t *testing.T) { defer th.TeardownHTTP() HandleGetDomainSuccessfully(t) - actual, err := domains.Get(client.ServiceClient(), "9fe1d3").Extract() + actual, err := domains.Get(context.TODO(), client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomain, *actual) } @@ -80,7 +81,7 @@ func TestCreateDomain(t *testing.T) { Name: "domain two", } - actual, err := domains.Create(client.ServiceClient(), createOpts).Extract() + actual, err := domains.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomain, *actual) } @@ -90,7 +91,7 @@ func TestDeleteDomain(t *testing.T) { defer th.TeardownHTTP() HandleDeleteDomainSuccessfully(t) - res := domains.Delete(client.ServiceClient(), "9fe1d3") + res := domains.Delete(context.TODO(), client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } @@ -104,7 +105,7 @@ func TestUpdateDomain(t *testing.T) { Description: &description, } - actual, err := domains.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + actual, err := domains.Update(context.TODO(), client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondDomainUpdated, *actual) } diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index d34de44b27..7cb347f787 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -1,6 +1,8 @@ package endpoints import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -36,13 +38,13 @@ func (opts CreateOpts) ToEndpointCreateMap() (map[string]interface{}, error) { } // Create inserts a new Endpoint into the service catalog. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToEndpointCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(listURL(client), &b, &r.Body, nil) + resp, err := client.Post(ctx, listURL(client), &b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -120,20 +122,20 @@ func (opts UpdateOpts) ToEndpointUpdateMap() (map[string]interface{}, error) { } // Update changes an existing endpoint with new data. -func Update(client *gophercloud.ServiceClient, endpointID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, endpointID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToEndpointUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(endpointURL(client, endpointID), &b, &r.Body, nil) + resp, err := client.Patch(ctx, endpointURL(client, endpointID), &b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete removes an endpoint from the service catalog. -func Delete(client *gophercloud.ServiceClient, endpointID string) (r DeleteResult) { - resp, err := client.Delete(endpointURL(client, endpointID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, endpointID string) (r DeleteResult) { + resp, err := client.Delete(ctx, endpointURL(client, endpointID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 8b01cb4a7c..457a6c08ed 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -50,7 +51,7 @@ func TestCreateSuccessful(t *testing.T) { `) }) - actual, err := endpoints.Create(client.ServiceClient(), endpoints.CreateOpts{ + actual, err := endpoints.Create(context.TODO(), client.ServiceClient(), endpoints.CreateOpts{ Availability: gophercloud.AvailabilityPublic, Name: "the-endiest-of-points", Region: "underground", @@ -118,7 +119,7 @@ func TestListEndpoints(t *testing.T) { }) count := 0 - endpoints.List(client.ServiceClient(), endpoints.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + endpoints.List(client.ServiceClient(), endpoints.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := endpoints.ExtractEndpoints(page) if err != nil { @@ -186,7 +187,7 @@ func TestUpdateEndpoint(t *testing.T) { `) }) - actual, err := endpoints.Update(client.ServiceClient(), "12", endpoints.UpdateOpts{ + actual, err := endpoints.Update(context.TODO(), client.ServiceClient(), "12", endpoints.UpdateOpts{ Name: "renamed", Region: "somewhere-else", }).Extract() @@ -217,6 +218,6 @@ func TestDeleteEndpoint(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := endpoints.Delete(client.ServiceClient(), "34") + res := endpoints.Delete(context.TODO(), client.ServiceClient(), "34") th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/extensions/ec2credentials/requests.go b/openstack/identity/v3/extensions/ec2credentials/requests.go index 15990e6e8d..cf33069172 100644 --- a/openstack/identity/v3/extensions/ec2credentials/requests.go +++ b/openstack/identity/v3/extensions/ec2credentials/requests.go @@ -1,6 +1,8 @@ package ec2credentials import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -14,8 +16,8 @@ func List(client *gophercloud.ServiceClient, userID string) pagination.Pager { } // Get retrieves details on a single EC2 credential by ID. -func Get(client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { - resp, err := client.Get(getURL(client, userID, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -32,13 +34,13 @@ func (opts CreateOpts) ToCredentialCreateMap() (map[string]interface{}, error) { } // Create creates a new EC2 Credential. -func Create(client *gophercloud.ServiceClient, userID string, opts CreateOpts) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, userID string, opts CreateOpts) (r CreateResult) { b, err := opts.ToCredentialCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -46,8 +48,8 @@ func Create(client *gophercloud.ServiceClient, userID string, opts CreateOpts) ( } // Delete deletes an EC2 credential. -func Delete(client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, userID, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go b/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go index 0a47fe3a30..a5e35c8ed6 100644 --- a/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go +++ b/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2credentials" @@ -15,7 +16,7 @@ func TestListEC2Credentials(t *testing.T) { HandleListEC2CredentialsSuccessfully(t) count := 0 - err := ec2credentials.List(client.ServiceClient(), userID).EachPage(func(page pagination.Page) (bool, error) { + err := ec2credentials.List(client.ServiceClient(), userID).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := ec2credentials.ExtractCredentials(page) @@ -34,7 +35,7 @@ func TestListEC2CredentialsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListEC2CredentialsSuccessfully(t) - allPages, err := ec2credentials.List(client.ServiceClient(), userID).AllPages() + allPages, err := ec2credentials.List(client.ServiceClient(), userID).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := ec2credentials.ExtractCredentials(allPages) th.AssertNoErr(t, err) @@ -46,7 +47,7 @@ func TestGetEC2Credential(t *testing.T) { defer th.TeardownHTTP() HandleGetEC2CredentialSuccessfully(t) - actual, err := ec2credentials.Get(client.ServiceClient(), userID, credentialID).Extract() + actual, err := ec2credentials.Get(context.TODO(), client.ServiceClient(), userID, credentialID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, EC2Credential, *actual) } @@ -60,7 +61,7 @@ func TestCreateEC2Credential(t *testing.T) { TenantID: "6238dee2fec940a6bf31e49e9faf995a", } - actual, err := ec2credentials.Create(client.ServiceClient(), userID, createOpts).Extract() + actual, err := ec2credentials.Create(context.TODO(), client.ServiceClient(), userID, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, EC2Credential, *actual) } @@ -70,6 +71,6 @@ func TestDeleteEC2Credential(t *testing.T) { defer th.TeardownHTTP() HandleDeleteEC2CredentialSuccessfully(t) - res := ec2credentials.Delete(client.ServiceClient(), userID, credentialID) + res := ec2credentials.Delete(context.TODO(), client.ServiceClient(), userID, credentialID) th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/extensions/ec2tokens/requests.go index befb4c4fab..bd1c1293f5 100644 --- a/openstack/identity/v3/extensions/ec2tokens/requests.go +++ b/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -288,8 +288,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(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -299,7 +299,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(ctx, ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) @@ -307,15 +307,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(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -325,7 +319,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(ctx, s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"X-Auth-Token": ""}, OkCodes: []int{200}, }) @@ -333,11 +327,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/ec2tokens/testing/requests_test.go b/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go index d5ee9d4a0e..4c502d7628 100644 --- a/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go +++ b/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "encoding/hex" "fmt" "net/http" @@ -38,7 +39,7 @@ func authTokenPost(t *testing.T, options ec2tokens.AuthOptions, requestJSON stri ExpiresAt: time.Date(2017, 6, 3, 2, 19, 49, 0, time.UTC), } - actual, err := ec2tokens.Create(&client, &options).Extract() + actual, err := ec2tokens.Create(context.TODO(), &client, &options).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, expected, actual) } diff --git a/openstack/identity/v3/extensions/federation/requests.go b/openstack/identity/v3/extensions/federation/requests.go index 6d3b512dd4..e80383b28a 100644 --- a/openstack/identity/v3/extensions/federation/requests.go +++ b/openstack/identity/v3/extensions/federation/requests.go @@ -1,6 +1,8 @@ package federation import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -30,13 +32,13 @@ func (opts CreateMappingOpts) ToMappingCreateMap() (map[string]interface{}, erro } // CreateMapping creates a new Mapping. -func CreateMapping(client *gophercloud.ServiceClient, mappingID string, opts CreateMappingOptsBuilder) (r CreateMappingResult) { +func CreateMapping(ctx context.Context, 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{ + resp, err := client.Put(ctx, mappingsResourceURL(client, mappingID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -44,8 +46,8 @@ func CreateMapping(client *gophercloud.ServiceClient, mappingID string, opts Cre } // 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) +func GetMapping(ctx context.Context, client *gophercloud.ServiceClient, mappingID string) (r GetMappingResult) { + resp, err := client.Get(ctx, mappingsResourceURL(client, mappingID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -68,13 +70,13 @@ func (opts UpdateMappingOpts) ToMappingUpdateMap() (map[string]interface{}, erro } // UpdateMapping updates an existing mapping. -func UpdateMapping(client *gophercloud.ServiceClient, mappingID string, opts UpdateMappingOptsBuilder) (r UpdateMappingResult) { +func UpdateMapping(ctx context.Context, 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{ + resp, err := client.Patch(ctx, mappingsResourceURL(client, mappingID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -82,8 +84,8 @@ func UpdateMapping(client *gophercloud.ServiceClient, mappingID string, opts Upd } // DeleteMapping deletes a mapping. -func DeleteMapping(client *gophercloud.ServiceClient, mappingID string) (r DeleteMappingResult) { - resp, err := client.Delete(mappingsResourceURL(client, mappingID), nil) +func DeleteMapping(ctx context.Context, client *gophercloud.ServiceClient, mappingID string) (r DeleteMappingResult) { + resp, err := client.Delete(ctx, mappingsResourceURL(client, mappingID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/extensions/federation/testing/requests_test.go b/openstack/identity/v3/extensions/federation/testing/requests_test.go index 836cf78939..fe62d8e658 100644 --- a/openstack/identity/v3/extensions/federation/testing/requests_test.go +++ b/openstack/identity/v3/extensions/federation/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/federation" @@ -15,7 +16,7 @@ func TestListMappings(t *testing.T) { HandleListMappingsSuccessfully(t) count := 0 - err := federation.ListMappings(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := federation.ListMappings(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := federation.ExtractMappings(page) @@ -34,7 +35,7 @@ func TestListMappingsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListMappingsSuccessfully(t) - allPages, err := federation.ListMappings(client.ServiceClient()).AllPages() + allPages, err := federation.ListMappings(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := federation.ExtractMappings(allPages) th.AssertNoErr(t, err) @@ -77,7 +78,7 @@ func TestCreateMappings(t *testing.T) { }, } - actual, err := federation.CreateMapping(client.ServiceClient(), "ACME", createOpts).Extract() + actual, err := federation.CreateMapping(context.TODO(), client.ServiceClient(), "ACME", createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, MappingACME, *actual) } @@ -87,7 +88,7 @@ func TestGetMapping(t *testing.T) { defer th.TeardownHTTP() HandleGetMappingSuccessfully(t) - actual, err := federation.GetMapping(client.ServiceClient(), "ACME").Extract() + actual, err := federation.GetMapping(context.TODO(), client.ServiceClient(), "ACME").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, MappingACME, *actual) } @@ -128,7 +129,7 @@ func TestUpdateMapping(t *testing.T) { }, } - actual, err := federation.UpdateMapping(client.ServiceClient(), "ACME", updateOpts).Extract() + actual, err := federation.UpdateMapping(context.TODO(), client.ServiceClient(), "ACME", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, MappingUpdated, *actual) } @@ -138,6 +139,6 @@ func TestDeleteMapping(t *testing.T) { defer th.TeardownHTTP() HandleDeleteMappingSuccessfully(t) - res := federation.DeleteMapping(client.ServiceClient(), "ACME") + res := federation.DeleteMapping(context.TODO(), client.ServiceClient(), "ACME") th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/extensions/oauth1/requests.go b/openstack/identity/v3/extensions/oauth1/requests.go index 571f3465e9..43cdc59e7d 100644 --- a/openstack/identity/v3/extensions/oauth1/requests.go +++ b/openstack/identity/v3/extensions/oauth1/requests.go @@ -134,9 +134,9 @@ func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]i return gophercloud.BuildRequestBody(req, "") } -// CreateWithContext authenticates and either generates a new OpenStack token +// Create 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) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { b, err := opts.ToTokenV3CreateMap(nil) if err != nil { r.Err = err @@ -154,7 +154,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(ctx, authURL(client), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) @@ -162,11 +162,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 +179,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) { +// CreateConsumer creates a new Consumer. +func CreateConsumer(ctx context.Context, client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { b, err := opts.ToOAuth1CreateConsumerMap() if err != nil { r.Err = err return } - resp, err := client.PostWithContext(ctx, consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// 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) +// DeleteConsumer deletes a Consumer. +func DeleteConsumer(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { + resp, err := client.Delete(ctx, consumerURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// 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 +207,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(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { + resp, err := client.Get(ctx, consumerURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// 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 +226,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(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.PatchWithContext(ctx, consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 +298,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(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.PostWithContext(ctx, requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, @@ -349,11 +324,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 +352,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(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.PutWithContext(ctx, authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 +426,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(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.PostWithContext(ctx, createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, KeepResponseBody: true, @@ -487,35 +452,20 @@ 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) +// GetAccessToken retrieves details on a single OAuth1 access token by an ID. +func GetAccessToken(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { + resp, err := client.Get(ctx, userAccessTokenURL(client, userID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// 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) +// RevokeAccessToken revokes an OAuth1 access token. +func RevokeAccessToken(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { + resp, err := client.Delete(ctx, userAccessTokenURL(client, userID, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// 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) @@ -532,19 +482,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(ctx context.Context, client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { + resp, err := client.Get(ctx, userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// 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/extensions/oauth1/testing/requests_test.go b/openstack/identity/v3/extensions/oauth1/testing/requests_test.go index 99c5d82dfd..4c78cc2461 100644 --- a/openstack/identity/v3/extensions/oauth1/testing/requests_test.go +++ b/openstack/identity/v3/extensions/oauth1/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -16,7 +17,7 @@ func TestCreateConsumer(t *testing.T) { defer th.TeardownHTTP() HandleCreateConsumer(t) - consumer, err := oauth1.CreateConsumer(client.ServiceClient(), oauth1.CreateConsumerOpts{ + consumer, err := oauth1.CreateConsumer(context.TODO(), client.ServiceClient(), oauth1.CreateConsumerOpts{ Description: "My consumer", }).Extract() th.AssertNoErr(t, err) @@ -29,7 +30,7 @@ func TestUpdateConsumer(t *testing.T) { defer th.TeardownHTTP() HandleUpdateConsumer(t) - consumer, err := oauth1.UpdateConsumer(client.ServiceClient(), "7fea2d", oauth1.UpdateConsumerOpts{ + consumer, err := oauth1.UpdateConsumer(context.TODO(), client.ServiceClient(), "7fea2d", oauth1.UpdateConsumerOpts{ Description: "My new consumer", }).Extract() th.AssertNoErr(t, err) @@ -42,7 +43,7 @@ func TestDeleteConsumer(t *testing.T) { defer th.TeardownHTTP() HandleDeleteConsumer(t) - err := oauth1.DeleteConsumer(client.ServiceClient(), "7fea2d").ExtractErr() + err := oauth1.DeleteConsumer(context.TODO(), client.ServiceClient(), "7fea2d").ExtractErr() th.AssertNoErr(t, err) } @@ -51,7 +52,7 @@ func TestGetConsumer(t *testing.T) { defer th.TeardownHTTP() HandleGetConsumer(t) - consumer, err := oauth1.GetConsumer(client.ServiceClient(), "7fea2d").Extract() + consumer, err := oauth1.GetConsumer(context.TODO(), client.ServiceClient(), "7fea2d").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, FirstConsumer, *consumer) @@ -63,7 +64,7 @@ func TestListConsumers(t *testing.T) { HandleListConsumers(t) count := 0 - err := oauth1.ListConsumers(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := oauth1.ListConsumers(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := oauth1.ExtractConsumers(page) @@ -82,7 +83,7 @@ func TestListConsumersAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListConsumers(t) - allPages, err := oauth1.ListConsumers(client.ServiceClient()).AllPages() + allPages, err := oauth1.ListConsumers(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := oauth1.ExtractConsumers(allPages) th.AssertNoErr(t, err) @@ -95,7 +96,7 @@ func TestRequestToken(t *testing.T) { HandleRequestToken(t) ts := time.Unix(0, 0) - token, err := oauth1.RequestToken(client.ServiceClient(), oauth1.RequestTokenOpts{ + token, err := oauth1.RequestToken(context.TODO(), client.ServiceClient(), oauth1.RequestTokenOpts{ OAuthConsumerKey: Consumer.ID, OAuthConsumerSecret: Consumer.Secret, OAuthSignatureMethod: oauth1.HMACSHA1, @@ -113,7 +114,7 @@ func TestAuthorizeToken(t *testing.T) { defer th.TeardownHTTP() HandleAuthorizeToken(t) - token, err := oauth1.AuthorizeToken(client.ServiceClient(), "29971f", oauth1.AuthorizeTokenOpts{ + token, err := oauth1.AuthorizeToken(context.TODO(), client.ServiceClient(), "29971f", oauth1.AuthorizeTokenOpts{ Roles: []oauth1.Role{ { ID: "a3b29b", @@ -134,7 +135,7 @@ func TestCreateAccessToken(t *testing.T) { HandleCreateAccessToken(t) ts := time.Unix(1586804894, 0) - token, err := oauth1.CreateAccessToken(client.ServiceClient(), oauth1.CreateAccessTokenOpts{ + token, err := oauth1.CreateAccessToken(context.TODO(), client.ServiceClient(), oauth1.CreateAccessTokenOpts{ OAuthConsumerKey: Consumer.ID, OAuthConsumerSecret: Consumer.Secret, OAuthToken: Token.OAuthToken, @@ -154,7 +155,7 @@ func TestGetAccessToken(t *testing.T) { defer th.TeardownHTTP() HandleGetAccessToken(t) - token, err := oauth1.GetAccessToken(client.ServiceClient(), "ce9e07", "6be26a").Extract() + token, err := oauth1.GetAccessToken(context.TODO(), client.ServiceClient(), "ce9e07", "6be26a").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, UserAccessToken, *token) @@ -165,7 +166,7 @@ func TestRevokeAccessToken(t *testing.T) { defer th.TeardownHTTP() HandleRevokeAccessToken(t) - err := oauth1.RevokeAccessToken(client.ServiceClient(), "ce9e07", "6be26a").ExtractErr() + err := oauth1.RevokeAccessToken(context.TODO(), client.ServiceClient(), "ce9e07", "6be26a").ExtractErr() th.AssertNoErr(t, err) } @@ -175,7 +176,7 @@ func TestListAccessTokens(t *testing.T) { HandleListAccessTokens(t) count := 0 - err := oauth1.ListAccessTokens(client.ServiceClient(), "ce9e07").EachPage(func(page pagination.Page) (bool, error) { + err := oauth1.ListAccessTokens(client.ServiceClient(), "ce9e07").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := oauth1.ExtractAccessTokens(page) @@ -194,7 +195,7 @@ func TestListAccessTokensAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListAccessTokens(t) - allPages, err := oauth1.ListAccessTokens(client.ServiceClient(), "ce9e07").AllPages() + allPages, err := oauth1.ListAccessTokens(client.ServiceClient(), "ce9e07").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := oauth1.ExtractAccessTokens(allPages) th.AssertNoErr(t, err) @@ -207,7 +208,7 @@ func TestListAccessTokenRoles(t *testing.T) { HandleListAccessTokenRoles(t) count := 0 - err := oauth1.ListAccessTokenRoles(client.ServiceClient(), "ce9e07", "6be26a").EachPage(func(page pagination.Page) (bool, error) { + err := oauth1.ListAccessTokenRoles(client.ServiceClient(), "ce9e07", "6be26a").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := oauth1.ExtractAccessTokenRoles(page) @@ -226,7 +227,7 @@ func TestListAccessTokenRolesAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListAccessTokenRoles(t) - allPages, err := oauth1.ListAccessTokenRoles(client.ServiceClient(), "ce9e07", "6be26a").AllPages() + allPages, err := oauth1.ListAccessTokenRoles(client.ServiceClient(), "ce9e07", "6be26a").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := oauth1.ExtractAccessTokenRoles(allPages) th.AssertNoErr(t, err) @@ -238,7 +239,7 @@ func TestGetAccessTokenRole(t *testing.T) { defer th.TeardownHTTP() HandleGetAccessTokenRole(t) - role, err := oauth1.GetAccessTokenRole(client.ServiceClient(), "ce9e07", "6be26a", "5ad150").Extract() + role, err := oauth1.GetAccessTokenRole(context.TODO(), client.ServiceClient(), "ce9e07", "6be26a", "5ad150").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, UserAccessTokenRole, *role) @@ -264,7 +265,7 @@ func TestAuthenticate(t *testing.T) { OAuthNonce: "66148873158553341551586804894", } - actual, err := oauth1.Create(client.ServiceClient(), options).Extract() + actual, err := oauth1.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) } diff --git a/openstack/identity/v3/extensions/projectendpoints/requests.go b/openstack/identity/v3/extensions/projectendpoints/requests.go index 58350b7b02..83bda977e4 100644 --- a/openstack/identity/v3/extensions/projectendpoints/requests.go +++ b/openstack/identity/v3/extensions/projectendpoints/requests.go @@ -1,6 +1,8 @@ package projectendpoints import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -10,8 +12,8 @@ type CreateOptsBuilder interface { } // 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}}) +func Create(ctx context.Context, client *gophercloud.ServiceClient, projectID, endpointID string) (r CreateResult) { + resp, err := client.Put(ctx, createURL(client, projectID, endpointID), nil, nil, &gophercloud.RequestOpts{OkCodes: []int{204}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -26,8 +28,8 @@ func List(client *gophercloud.ServiceClient, projectID string) pagination.Pager } // 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}}) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, projectID string, endpointID string) (r DeleteResult) { + resp, err := client.Delete(ctx, 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/testing/requests_test.go b/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go index cb451fd01d..9833e01643 100644 --- a/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go +++ b/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -23,7 +24,7 @@ func TestCreateSuccessful(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := projectendpoints.Create(client.ServiceClient(), "project-id", "endpoint-id").Err + err := projectendpoints.Create(context.TODO(), client.ServiceClient(), "project-id", "endpoint-id").Err th.AssertNoErr(t, err) } @@ -70,7 +71,7 @@ func TestListEndpoints(t *testing.T) { }) count := 0 - projectendpoints.List(client.ServiceClient(), "project-id").EachPage(func(page pagination.Page) (bool, error) { + projectendpoints.List(client.ServiceClient(), "project-id").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := projectendpoints.ExtractEndpoints(page) if err != nil { @@ -111,6 +112,6 @@ func TestDeleteEndpoint(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := projectendpoints.Delete(client.ServiceClient(), "project-id", "endpoint-id") + res := projectendpoints.Delete(context.TODO(), client.ServiceClient(), "project-id", "endpoint-id") th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index 76d4605513..e8d6b6441f 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -1,6 +1,7 @@ package trusts import ( + "context" "time" "github.com/gophercloud/gophercloud/v2" @@ -117,13 +118,13 @@ func (opts ListOpts) ToTrustListQuery() (string, error) { } // Create creates a new Trust. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTrustCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -131,8 +132,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes a Trust. -func Delete(client *gophercloud.ServiceClient, trustID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, trustID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, trustID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, trustID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -153,8 +154,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details on a single Trust, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(resourceURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -168,15 +169,15 @@ func ListRoles(client *gophercloud.ServiceClient, id string) pagination.Pager { } // GetRole retrieves details on a single role delegated by a Trust. -func GetRole(client *gophercloud.ServiceClient, id string, roleID string) (r GetRoleResult) { - resp, err := client.Get(getRoleURL(client, id, roleID), &r.Body, nil) +func GetRole(ctx context.Context, client *gophercloud.ServiceClient, id string, roleID string) (r GetRoleResult) { + resp, err := client.Get(ctx, getRoleURL(client, id, roleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // CheckRole checks whether a role ID is delegated by a Trust. -func CheckRole(client *gophercloud.ServiceClient, id string, roleID string) (r CheckRoleResult) { - resp, err := client.Head(getRoleURL(client, id, roleID), nil) +func CheckRole(ctx context.Context, client *gophercloud.ServiceClient, id string, roleID string) (r CheckRoleResult) { + resp, err := client.Head(ctx, getRoleURL(client, id, roleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index adb7239f7f..d63184ebc2 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -44,7 +45,7 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) { tokens.Token trusts.TokenExt } - err := tokens.Create(client.ServiceClient(), ao).ExtractInto(&actual) + err := tokens.Create(context.TODO(), client.ServiceClient(), ao).ExtractInto(&actual) if err != nil { t.Errorf("Create returned an error: %v", err) } @@ -74,7 +75,7 @@ func TestCreateTrust(t *testing.T) { HandleCreateTrust(t) expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 0, time.UTC) - result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{ + result, err := trusts.Create(context.TODO(), client.ServiceClient(), trusts.CreateOpts{ ExpiresAt: &expiresAt, AllowRedelegation: true, ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", @@ -96,7 +97,7 @@ func TestCreateTrustNoExpire(t *testing.T) { defer th.TeardownHTTP() HandleCreateTrustNoExpire(t) - result, err := trusts.Create(client.ServiceClient(), trusts.CreateOpts{ + result, err := trusts.Create(context.TODO(), client.ServiceClient(), trusts.CreateOpts{ AllowRedelegation: true, ProjectID: "9b71012f5a4a4aef9193f1995fe159b2", Roles: []trusts.Role{ @@ -117,7 +118,7 @@ func TestDeleteTrust(t *testing.T) { defer th.TeardownHTTP() HandleDeleteTrust(t) - res := trusts.Delete(client.ServiceClient(), "3422b7c113894f5d90665e1a79655e23") + res := trusts.Delete(context.TODO(), client.ServiceClient(), "3422b7c113894f5d90665e1a79655e23") th.AssertNoErr(t, res.Err) } @@ -126,7 +127,7 @@ func TestGetTrust(t *testing.T) { defer th.TeardownHTTP() HandleGetTrustSuccessfully(t) - res := trusts.Get(client.ServiceClient(), "987fe8") + res := trusts.Get(context.TODO(), client.ServiceClient(), "987fe8") th.AssertNoErr(t, res.Err) } @@ -136,7 +137,7 @@ func TestListTrusts(t *testing.T) { HandleListTrustsSuccessfully(t) count := 0 - err := trusts.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := trusts.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := trusts.ExtractTrusts(page) @@ -155,7 +156,7 @@ func TestListTrustsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListTrustsSuccessfully(t) - allPages, err := trusts.List(client.ServiceClient(), nil).AllPages() + allPages, err := trusts.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := trusts.ExtractTrusts(allPages) th.AssertNoErr(t, err) @@ -169,7 +170,7 @@ func TestListTrustsFiltered(t *testing.T) { trustsListOpts := trusts.ListOpts{ TrustorUserID: "86c0d5", } - allPages, err := trusts.List(client.ServiceClient(), trustsListOpts).AllPages() + allPages, err := trusts.List(client.ServiceClient(), trustsListOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := trusts.ExtractTrusts(allPages) th.AssertNoErr(t, err) @@ -182,7 +183,7 @@ func TestListTrustRoles(t *testing.T) { HandleListTrustRolesSuccessfully(t) count := 0 - err := trusts.ListRoles(client.ServiceClient(), "987fe8").EachPage(func(page pagination.Page) (bool, error) { + err := trusts.ListRoles(client.ServiceClient(), "987fe8").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := trusts.ExtractRoles(page) @@ -201,7 +202,7 @@ func TestListTrustRolesAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListTrustRolesSuccessfully(t) - allPages, err := trusts.ListRoles(client.ServiceClient(), "987fe8").AllPages() + allPages, err := trusts.ListRoles(client.ServiceClient(), "987fe8").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := trusts.ExtractRoles(allPages) th.AssertNoErr(t, err) @@ -213,7 +214,7 @@ func TestGetTrustRole(t *testing.T) { defer th.TeardownHTTP() HandleGetTrustRoleSuccessfully(t) - role, err := trusts.GetRole(client.ServiceClient(), "987fe8", "c1648e").Extract() + role, err := trusts.GetRole(context.TODO(), client.ServiceClient(), "987fe8", "c1648e").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, FirstRole, *role) @@ -224,6 +225,6 @@ func TestCheckTrustRole(t *testing.T) { defer th.TeardownHTTP() HandleCheckTrustRoleSuccessfully(t) - err := trusts.CheckRole(client.ServiceClient(), "987fe8", "c1648e").ExtractErr() + err := trusts.CheckRole(context.TODO(), client.ServiceClient(), "987fe8", "c1648e").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index 4ba5adecbe..528244f1ba 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -1,6 +1,7 @@ package groups import ( + "context" "net/url" "strings" @@ -64,8 +65,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details on a single group, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -110,13 +111,13 @@ func (opts CreateOpts) ToGroupCreateMap() (map[string]interface{}, error) { } // Create creates a new Group. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, 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{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -163,13 +164,13 @@ func (opts UpdateOpts) ToGroupUpdateMap() (map[string]interface{}, error) { } // Update updates an existing Group. -func Update(client *gophercloud.ServiceClient, groupID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, 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{ + resp, err := client.Patch(ctx, updateURL(client, groupID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -177,8 +178,8 @@ func Update(client *gophercloud.ServiceClient, groupID string, opts UpdateOptsBu } // Delete deletes a group. -func Delete(client *gophercloud.ServiceClient, groupID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, groupID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, groupID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, groupID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go index 9656ca8dd2..47035d3aa8 100644 --- a/openstack/identity/v3/groups/testing/requests_test.go +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" @@ -15,7 +16,7 @@ func TestListGroups(t *testing.T) { HandleListGroupsSuccessfully(t) count := 0 - err := groups.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := groups.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := groups.ExtractGroups(page) @@ -34,7 +35,7 @@ func TestListGroupsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListGroupsSuccessfully(t) - allPages, err := groups.List(client.ServiceClient(), nil).AllPages() + allPages, err := groups.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := groups.ExtractGroups(allPages) th.AssertNoErr(t, err) @@ -80,7 +81,7 @@ func TestGetGroup(t *testing.T) { defer th.TeardownHTTP() HandleGetGroupSuccessfully(t) - actual, err := groups.Get(client.ServiceClient(), "9fe1d3").Extract() + actual, err := groups.Get(context.TODO(), client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondGroup, *actual) @@ -101,7 +102,7 @@ func TestCreateGroup(t *testing.T) { }, } - actual, err := groups.Create(client.ServiceClient(), createOpts).Extract() + actual, err := groups.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondGroup, *actual) } @@ -119,7 +120,7 @@ func TestUpdateGroup(t *testing.T) { }, } - actual, err := groups.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + actual, err := groups.Update(context.TODO(), client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondGroupUpdated, *actual) } @@ -129,6 +130,6 @@ func TestDeleteGroup(t *testing.T) { defer th.TeardownHTTP() HandleDeleteGroupSuccessfully(t) - res := groups.Delete(client.ServiceClient(), "9fe1d3") + res := groups.Delete(context.TODO(), client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go index 9908a99a5f..babf82f707 100644 --- a/openstack/identity/v3/limits/requests.go +++ b/openstack/identity/v3/limits/requests.go @@ -1,13 +1,15 @@ package limits import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/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) +func GetEnforcementModel(ctx context.Context, client *gophercloud.ServiceClient) (r EnforcementModelResult) { + resp, err := client.Get(ctx, enforcementModelURL(client), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -107,13 +109,13 @@ func (opts CreateOpts) ToMap() (map[string]interface{}, error) { } // BatchCreate creates new Limits. -func BatchCreate(client *gophercloud.ServiceClient, opts BatchCreateOptsBuilder) (r CreateResult) { +func BatchCreate(ctx context.Context, 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{ + resp, err := client.Post(ctx, rootURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -121,8 +123,8 @@ func BatchCreate(client *gophercloud.ServiceClient, opts BatchCreateOptsBuilder) } // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, limitID string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, limitID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -148,13 +150,13 @@ func (opts UpdateOpts) ToLimitUpdateMap() (map[string]interface{}, error) { } // Update modifies the attributes of a limit. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLimitUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -162,8 +164,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Delete deletes a limit. -func Delete(client *gophercloud.ServiceClient, limitID string) (r DeleteResult) { - resp, err := client.Delete(resourceURL(client, limitID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, limitID string) (r DeleteResult) { + resp, err := client.Delete(ctx, resourceURL(client, limitID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/limits/testing/requests_test.go b/openstack/identity/v3/limits/testing/requests_test.go index 743acd3cbb..15cf4dbb8e 100644 --- a/openstack/identity/v3/limits/testing/requests_test.go +++ b/openstack/identity/v3/limits/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/limits" @@ -14,7 +15,7 @@ func TestGetEnforcementModel(t *testing.T) { defer th.TeardownHTTP() HandleGetEnforcementModelSuccessfully(t) - actual, err := limits.GetEnforcementModel(client.ServiceClient()).Extract() + actual, err := limits.GetEnforcementModel(context.TODO(), client.ServiceClient()).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, Model, *actual) } @@ -25,7 +26,7 @@ func TestListLimits(t *testing.T) { HandleListLimitsSuccessfully(t) count := 0 - err := limits.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := limits.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := limits.ExtractLimits(page) @@ -44,7 +45,7 @@ func TestListLimitsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListLimitsSuccessfully(t) - allPages, err := limits.List(client.ServiceClient(), nil).AllPages() + allPages, err := limits.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := limits.ExtractLimits(allPages) th.AssertNoErr(t, err) @@ -73,7 +74,7 @@ func TestCreateLimits(t *testing.T) { }, } - actual, err := limits.BatchCreate(client.ServiceClient(), createOpts).Extract() + actual, err := limits.BatchCreate(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedLimitsSlice, actual) } @@ -83,7 +84,7 @@ func TestGetLimit(t *testing.T) { defer th.TeardownHTTP() HandleGetLimitSuccessfully(t) - actual, err := limits.Get(client.ServiceClient(), "25a04c7a065c430590881c646cdcdd58").Extract() + actual, err := limits.Get(context.TODO(), client.ServiceClient(), "25a04c7a065c430590881c646cdcdd58").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, FirstLimit, *actual) } @@ -100,7 +101,7 @@ func TestUpdateLimit(t *testing.T) { ResourceLimit: &resourceLimit, } - actual, err := limits.Update(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05", updateOpts).Extract() + actual, err := limits.Update(context.TODO(), client.ServiceClient(), "3229b3849f584faea483d6851f7aab05", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondLimitUpdated, *actual) } @@ -110,6 +111,6 @@ func TestDeleteLimit(t *testing.T) { defer th.TeardownHTTP() HandleDeleteLimitSuccessfully(t) - err := limits.Delete(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05").ExtractErr() + err := limits.Delete(context.TODO(), client.ServiceClient(), "3229b3849f584faea483d6851f7aab05").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/identity/v3/osinherit/requests.go b/openstack/identity/v3/osinherit/requests.go index f527651b4c..bcb667e531 100644 --- a/openstack/identity/v3/osinherit/requests.go +++ b/openstack/identity/v3/osinherit/requests.go @@ -1,6 +1,10 @@ package osinherit -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // AssignOpts provides options to assign an inherited role type AssignOpts struct { @@ -61,7 +65,7 @@ type UnassignOpts struct { // 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) { +func Assign(ctx context.Context, client *gophercloud.ServiceClient, roleID string, opts AssignOpts) (r AssignmentResult) { // Check xor conditions _, err := gophercloud.BuildRequestBody(opts, "") if err != nil { @@ -90,7 +94,7 @@ func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) ( actorType = "groups" } - resp, err := client.Put(assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -99,7 +103,7 @@ func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) ( // 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) { +func Validate(ctx context.Context, client *gophercloud.ServiceClient, roleID string, opts ValidateOpts) (r ValidateResult) { // Check xor conditions _, err := gophercloud.BuildRequestBody(opts, "") if err != nil { @@ -128,7 +132,7 @@ func Validate(client *gophercloud.ServiceClient, roleID string, opts ValidateOpt actorType = "groups" } - resp, err := client.Head(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ + resp, err := client.Head(ctx, assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -137,7 +141,7 @@ func Validate(client *gophercloud.ServiceClient, roleID string, opts ValidateOpt // 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) { +func Unassign(ctx context.Context, client *gophercloud.ServiceClient, roleID string, opts UnassignOpts) (r UnassignmentResult) { // Check xor conditions _, err := gophercloud.BuildRequestBody(opts, "") if err != nil { @@ -166,7 +170,7 @@ func Unassign(client *gophercloud.ServiceClient, roleID string, opts UnassignOpt actorType = "groups" } - resp, err := client.Delete(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/identity/v3/osinherit/testing/requests_test.go b/openstack/identity/v3/osinherit/testing/requests_test.go index 8f8aa01db5..7901a689c7 100644 --- a/openstack/identity/v3/osinherit/testing/requests_test.go +++ b/openstack/identity/v3/osinherit/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/osinherit" @@ -13,37 +14,37 @@ func TestAssign(t *testing.T) { defer th.TeardownHTTP() HandleAssignSuccessfully(t) - err := osinherit.Assign(client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ + err := osinherit.Assign(context.TODO(), 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{ + err = osinherit.Assign(context.TODO(), 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{ + err = osinherit.Assign(context.TODO(), 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{ + err = osinherit.Assign(context.TODO(), client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = osinherit.Assign(client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ + err = osinherit.Assign(context.TODO(), 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{ + err = osinherit.Assign(context.TODO(), client.ServiceClient(), "{role_id}", osinherit.AssignOpts{ ProjectID: "{project_id}", DomainID: "{domain_id}", }).ExtractErr() @@ -55,37 +56,37 @@ func TestValidate(t *testing.T) { defer th.TeardownHTTP() HandleValidateSuccessfully(t) - err := osinherit.Validate(client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ + err := osinherit.Validate(context.TODO(), 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{ + err = osinherit.Validate(context.TODO(), 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{ + err = osinherit.Validate(context.TODO(), 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{ + err = osinherit.Validate(context.TODO(), client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = osinherit.Validate(client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ + err = osinherit.Validate(context.TODO(), 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{ + err = osinherit.Validate(context.TODO(), client.ServiceClient(), "{role_id}", osinherit.ValidateOpts{ ProjectID: "{project_id}", DomainID: "{domain_id}", }).ExtractErr() @@ -97,37 +98,37 @@ func TestUnassign(t *testing.T) { defer th.TeardownHTTP() HandleUnassignSuccessfully(t) - err := osinherit.Unassign(client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ + err := osinherit.Unassign(context.TODO(), 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{ + err = osinherit.Unassign(context.TODO(), 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{ + err = osinherit.Unassign(context.TODO(), 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{ + err = osinherit.Unassign(context.TODO(), client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = osinherit.Unassign(client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ + err = osinherit.Unassign(context.TODO(), 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{ + err = osinherit.Unassign(context.TODO(), client.ServiceClient(), "{role_id}", osinherit.UnassignOpts{ ProjectID: "{project_id}", DomainID: "{domain_id}", }).ExtractErr() diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index 8f96692891..3b8340e7ec 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -1,6 +1,7 @@ package policies import ( + "context" "net/url" "strings" @@ -110,13 +111,13 @@ func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { } // Create creates a new Policy. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -124,8 +125,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Get retrieves details on a single policy, by ID. -func Get(client *gophercloud.ServiceClient, policyID string) (r GetResult) { - resp, err := client.Get(getURL(client, policyID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, policyID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, policyID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -178,13 +179,13 @@ func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { } // Update updates an existing Role. -func Update(client *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, policyID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, policyID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -192,8 +193,8 @@ func Update(client *gophercloud.ServiceClient, policyID string, opts UpdateOptsB } // Delete deletes a policy. -func Delete(client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, policyID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, policyID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go index 5cefb7775b..81709d4adf 100644 --- a/openstack/identity/v3/policies/testing/requests_test.go +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "testing" @@ -16,7 +17,7 @@ func TestListPolicies(t *testing.T) { HandleListPoliciesSuccessfully(t) count := 0 - err := policies.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := policies.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := policies.ExtractPolicies(page) @@ -35,7 +36,7 @@ func TestListPoliciesAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListPoliciesSuccessfully(t) - allPages, err := policies.List(client.ServiceClient(), nil).AllPages() + allPages, err := policies.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) @@ -50,7 +51,7 @@ func TestListPoliciesWithFilter(t *testing.T) { listOpts := policies.ListOpts{ Type: "application/json", } - allPages, err := policies.List(client.ServiceClient(), listOpts).AllPages() + allPages, err := policies.List(client.ServiceClient(), listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := policies.ExtractPolicies(allPages) th.AssertNoErr(t, err) @@ -102,7 +103,7 @@ func TestCreatePolicy(t *testing.T) { }, } - actual, err := policies.Create(client.ServiceClient(), createOpts).Extract() + actual, err := policies.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondPolicy, *actual) } @@ -156,7 +157,7 @@ func TestGetPolicy(t *testing.T) { HandleGetPolicySuccessfully(t) id := "b49884da9d31494ea02aff38d4b4e701" - actual, err := policies.Get(client.ServiceClient(), id).Extract() + actual, err := policies.Get(context.TODO(), client.ServiceClient(), id).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondPolicy, *actual) @@ -174,7 +175,7 @@ func TestUpdatePolicy(t *testing.T) { } id := "b49884da9d31494ea02aff38d4b4e701" - actual, err := policies.Update(client.ServiceClient(), id, updateOpts).Extract() + actual, err := policies.Update(context.TODO(), client.ServiceClient(), id, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondPolicyUpdated, *actual) } @@ -224,6 +225,6 @@ func TestDeletePolicy(t *testing.T) { defer th.TeardownHTTP() HandleDeletePolicySuccessfully(t) - res := policies.Delete(client.ServiceClient(), "9fe1d3") + res := policies.Delete(context.TODO(), client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 0b81fd548d..2d9d5eaa39 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -1,6 +1,7 @@ package projects import ( + "context" "net/url" "strings" @@ -94,8 +95,8 @@ func ListAvailable(client *gophercloud.ServiceClient) pagination.Pager { } // Get retrieves details on a single project, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -156,20 +157,20 @@ func (opts CreateOpts) ToProjectCreateMap() (map[string]interface{}, error) { } // Create creates a new Project. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToProjectCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, nil) + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a project. -func Delete(client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, projectID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, projectID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -230,13 +231,13 @@ func (opts UpdateOpts) ToProjectUpdateMap() (map[string]interface{}, error) { } // Update modifies the attributes of a project. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToProjectUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index a45eb24a37..3ca4898123 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" @@ -15,7 +16,7 @@ func TestListAvailableProjects(t *testing.T) { HandleListAvailableProjectsSuccessfully(t) count := 0 - err := projects.ListAvailable(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := projects.ListAvailable(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := projects.ExtractProjects(page) @@ -35,7 +36,7 @@ func TestListProjects(t *testing.T) { HandleListProjectsSuccessfully(t) count := 0 - err := projects.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := projects.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := projects.ExtractProjects(page) @@ -86,7 +87,7 @@ func TestGetProject(t *testing.T) { defer th.TeardownHTTP() HandleGetProjectSuccessfully(t) - actual, err := projects.Get(client.ServiceClient(), "1234").Extract() + actual, err := projects.Get(context.TODO(), client.ServiceClient(), "1234").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, RedTeam, *actual) } @@ -103,7 +104,7 @@ func TestCreateProject(t *testing.T) { Extra: map[string]interface{}{"test": "old"}, } - actual, err := projects.Create(client.ServiceClient(), createOpts).Extract() + actual, err := projects.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, RedTeam, *actual) } @@ -113,7 +114,7 @@ func TestDeleteProject(t *testing.T) { defer th.TeardownHTTP() HandleDeleteProjectSuccessfully(t) - res := projects.Delete(client.ServiceClient(), "1234") + res := projects.Delete(context.TODO(), client.ServiceClient(), "1234") th.AssertNoErr(t, res.Err) } @@ -130,7 +131,7 @@ func TestUpdateProject(t *testing.T) { Extra: map[string]interface{}{"test": "new"}, } - actual, err := projects.Update(client.ServiceClient(), "1234", updateOpts).Extract() + actual, err := projects.Update(context.TODO(), client.ServiceClient(), "1234", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, UpdatedRedTeam, *actual) } diff --git a/openstack/identity/v3/regions/requests.go b/openstack/identity/v3/regions/requests.go index 2cc3dd252e..fd8b5374bc 100644 --- a/openstack/identity/v3/regions/requests.go +++ b/openstack/identity/v3/regions/requests.go @@ -1,6 +1,8 @@ package regions import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -39,8 +41,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details on a single region, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -85,13 +87,13 @@ func (opts CreateOpts) ToRegionCreateMap() (map[string]interface{}, error) { } // Create creates a new Region. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRegionCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -147,13 +149,13 @@ func (opts UpdateOpts) ToRegionUpdateMap() (map[string]interface{}, error) { } // Update updates an existing Region. -func Update(client *gophercloud.ServiceClient, regionID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, regionID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRegionUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, regionID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, regionID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -161,8 +163,8 @@ func Update(client *gophercloud.ServiceClient, regionID string, opts UpdateOptsB } // Delete deletes a region. -func Delete(client *gophercloud.ServiceClient, regionID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, regionID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, regionID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, regionID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/regions/testing/requests_test.go b/openstack/identity/v3/regions/testing/requests_test.go index e89450d42c..8860b8721a 100644 --- a/openstack/identity/v3/regions/testing/requests_test.go +++ b/openstack/identity/v3/regions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/regions" @@ -15,7 +16,7 @@ func TestListRegions(t *testing.T) { HandleListRegionsSuccessfully(t) count := 0 - err := regions.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := regions.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := regions.ExtractRegions(page) @@ -34,7 +35,7 @@ func TestListRegionsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListRegionsSuccessfully(t) - allPages, err := regions.List(client.ServiceClient(), nil).AllPages() + allPages, err := regions.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := regions.ExtractRegions(allPages) th.AssertNoErr(t, err) @@ -47,7 +48,7 @@ func TestGetRegion(t *testing.T) { defer th.TeardownHTTP() HandleGetRegionSuccessfully(t) - actual, err := regions.Get(client.ServiceClient(), "RegionOne-West").Extract() + actual, err := regions.Get(context.TODO(), client.ServiceClient(), "RegionOne-West").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegion, *actual) @@ -67,7 +68,7 @@ func TestCreateRegion(t *testing.T) { ParentRegionID: "RegionOne", } - actual, err := regions.Create(client.ServiceClient(), createOpts).Extract() + actual, err := regions.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegion, *actual) } @@ -91,7 +92,7 @@ func TestUpdateRegion(t *testing.T) { */ } - actual, err := regions.Update(client.ServiceClient(), "RegionOne-West", updateOpts).Extract() + actual, err := regions.Update(context.TODO(), client.ServiceClient(), "RegionOne-West", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegionUpdated, *actual) } @@ -101,6 +102,6 @@ func TestDeleteRegion(t *testing.T) { defer th.TeardownHTTP() HandleDeleteRegionSuccessfully(t) - res := regions.Delete(client.ServiceClient(), "RegionOne-West") + res := regions.Delete(context.TODO(), client.ServiceClient(), "RegionOne-West") th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/registeredlimits/requests.go b/openstack/identity/v3/registeredlimits/requests.go index 8785611e10..232b32e2a5 100644 --- a/openstack/identity/v3/registeredlimits/requests.go +++ b/openstack/identity/v3/registeredlimits/requests.go @@ -1,6 +1,8 @@ package registeredlimits import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -88,13 +90,13 @@ func (opts CreateOpts) ToMap() (map[string]interface{}, error) { } // BatchCreate creates new Limits. -func BatchCreate(client *gophercloud.ServiceClient, opts BatchCreateOptsBuilder) (r CreateResult) { +func BatchCreate(ctx context.Context, 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{ + resp, err := client.Post(ctx, rootURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -102,8 +104,8 @@ func BatchCreate(client *gophercloud.ServiceClient, opts BatchCreateOptsBuilder) } // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, registeredLimitID string) (r GetResult) { + resp, err := client.Get(ctx, resourceURL(client, registeredLimitID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -139,13 +141,13 @@ func (opts UpdateOpts) ToRegisteredLimitUpdateMap() (map[string]interface{}, err } // Update modifies the attributes of a registered limit. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRegisteredLimitUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -153,8 +155,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // Delete deletes a registered_limit. -func Delete(client *gophercloud.ServiceClient, registeredLimitID string) (r DeleteResult) { - resp, err := client.Delete(resourceURL(client, registeredLimitID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, registeredLimitID string) (r DeleteResult) { + resp, err := client.Delete(ctx, resourceURL(client, registeredLimitID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/registeredlimits/testing/requests_test.go b/openstack/identity/v3/registeredlimits/testing/requests_test.go index 1a93ee09e9..ad2a30cb3f 100644 --- a/openstack/identity/v3/registeredlimits/testing/requests_test.go +++ b/openstack/identity/v3/registeredlimits/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/registeredlimits" @@ -15,7 +16,7 @@ func TestListRegisteredLimits(t *testing.T) { HandleListRegisteredLimitsSuccessfully(t) count := 0 - err := registeredlimits.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := registeredlimits.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := registeredlimits.ExtractRegisteredLimits(page) @@ -34,7 +35,7 @@ func TestListRegisteredLimitsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListRegisteredLimitsSuccessfully(t) - allPages, err := registeredlimits.List(client.ServiceClient(), nil).AllPages() + allPages, err := registeredlimits.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := registeredlimits.ExtractRegisteredLimits(allPages) th.AssertNoErr(t, err) @@ -62,7 +63,7 @@ func TestCreateRegisteredLimits(t *testing.T) { }, } - actual, err := registeredlimits.BatchCreate(client.ServiceClient(), createOpts).Extract() + actual, err := registeredlimits.BatchCreate(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRegisteredLimitsSlice, actual) } @@ -72,7 +73,7 @@ func TestGetRegisteredLimit(t *testing.T) { defer th.TeardownHTTP() HandleGetRegisteredLimitSuccessfully(t) - actual, err := registeredlimits.Get(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05").Extract() + actual, err := registeredlimits.Get(context.TODO(), client.ServiceClient(), "3229b3849f584faea483d6851f7aab05").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRegisteredLimit, *actual) } @@ -82,7 +83,7 @@ func TestDeleteRegisteredLimit(t *testing.T) { defer th.TeardownHTTP() HandleDeleteRegisteredLimitSuccessfully(t) - res := registeredlimits.Delete(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05") + res := registeredlimits.Delete(context.TODO(), client.ServiceClient(), "3229b3849f584faea483d6851f7aab05") th.AssertNoErr(t, res.Err) } @@ -98,7 +99,7 @@ func TestUpdateRegisteredLimit(t *testing.T) { DefaultLimit: &defaultLimit, } - actual, err := registeredlimits.Update(client.ServiceClient(), "3229b3849f584faea483d6851f7aab05", updateOpts).Extract() + actual, err := registeredlimits.Update(context.TODO(), client.ServiceClient(), "3229b3849f584faea483d6851f7aab05", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, UpdatedSecondRegisteredLimit, *actual) } diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index e8248a0e1d..ac6d1bfea4 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -1,6 +1,7 @@ package roles import ( + "context" "net/url" "strings" @@ -65,8 +66,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details on a single role, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -108,13 +109,13 @@ func (opts CreateOpts) ToRoleCreateMap() (map[string]interface{}, error) { } // Create creates a new Role. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRoleCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -155,13 +156,13 @@ func (opts UpdateOpts) ToRoleUpdateMap() (map[string]interface{}, error) { } // Update updates an existing Role. -func Update(client *gophercloud.ServiceClient, roleID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, roleID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRoleUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, roleID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, roleID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -169,8 +170,8 @@ func Update(client *gophercloud.ServiceClient, roleID string, opts UpdateOptsBui } // Delete deletes a role. -func Delete(client *gophercloud.ServiceClient, roleID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, roleID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, roleID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, roleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -332,7 +333,7 @@ func ListAssignmentsOnResource(client *gophercloud.ServiceClient, opts ListAssig // Assign is the operation responsible for assigning a role // to a user/group on a project/domain. -func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) (r AssignmentResult) { +func Assign(ctx context.Context, client *gophercloud.ServiceClient, roleID string, opts AssignOpts) (r AssignmentResult) { // Check xor conditions _, err := gophercloud.BuildRequestBody(opts, "") if err != nil { @@ -361,7 +362,7 @@ func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) ( actorType = "groups" } - resp, err := client.Put(assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, assignURL(client, targetType, targetID, actorType, actorID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -370,7 +371,7 @@ func Assign(client *gophercloud.ServiceClient, roleID string, opts AssignOpts) ( // Unassign is the operation responsible for unassigning a role // from a user/group on a project/domain. -func Unassign(client *gophercloud.ServiceClient, roleID string, opts UnassignOpts) (r UnassignmentResult) { +func Unassign(ctx context.Context, client *gophercloud.ServiceClient, roleID string, opts UnassignOpts) (r UnassignmentResult) { // Check xor conditions _, err := gophercloud.BuildRequestBody(opts, "") if err != nil { @@ -399,7 +400,7 @@ func Unassign(client *gophercloud.ServiceClient, roleID string, opts UnassignOpt actorType = "groups" } - resp, err := client.Delete(assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, assignURL(client, targetType, targetID, actorType, actorID, roleID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 0bbf10d5b2..ce41285bb7 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" @@ -15,7 +16,7 @@ func TestListRoles(t *testing.T) { HandleListRolesSuccessfully(t) count := 0 - err := roles.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := roles.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) @@ -34,7 +35,7 @@ func TestListRolesAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListRolesSuccessfully(t) - allPages, err := roles.List(client.ServiceClient(), nil).AllPages() + allPages, err := roles.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) @@ -79,7 +80,7 @@ func TestGetRole(t *testing.T) { defer th.TeardownHTTP() HandleGetRoleSuccessfully(t) - actual, err := roles.Get(client.ServiceClient(), "9fe1d3").Extract() + actual, err := roles.Get(context.TODO(), client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRole, *actual) @@ -98,7 +99,7 @@ func TestCreateRole(t *testing.T) { }, } - actual, err := roles.Create(client.ServiceClient(), createOpts).Extract() + actual, err := roles.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRole, *actual) } @@ -114,7 +115,7 @@ func TestUpdateRole(t *testing.T) { }, } - actual, err := roles.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + actual, err := roles.Update(context.TODO(), client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondRoleUpdated, *actual) } @@ -124,7 +125,7 @@ func TestDeleteRole(t *testing.T) { defer th.TeardownHTTP() HandleDeleteRoleSuccessfully(t) - res := roles.Delete(client.ServiceClient(), "9fe1d3") + res := roles.Delete(context.TODO(), client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } @@ -134,7 +135,7 @@ func TestListAssignmentsSinglePage(t *testing.T) { HandleListRoleAssignmentsSuccessfully(t) count := 0 - err := roles.ListAssignments(client.ServiceClient(), roles.ListAssignmentsOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := roles.ListAssignments(client.ServiceClient(), roles.ListAssignmentsOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoleAssignments(page) th.AssertNoErr(t, err) @@ -158,7 +159,7 @@ func TestListAssignmentsWithNamesSinglePage(t *testing.T) { } count := 0 - err := roles.ListAssignments(client.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { + err := roles.ListAssignments(client.ServiceClient(), listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoleAssignments(page) th.AssertNoErr(t, err) @@ -182,7 +183,7 @@ func TestListAssignmentsWithSubtreeSinglePage(t *testing.T) { } count := 0 - err := roles.ListAssignments(client.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { + err := roles.ListAssignments(client.ServiceClient(), listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoleAssignments(page) th.AssertNoErr(t, err) @@ -204,7 +205,7 @@ func TestListAssignmentsOnResource_ProjectsUsers(t *testing.T) { err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ UserID: "{user_id}", ProjectID: "{project_id}", - }).EachPage(func(page pagination.Page) (bool, error) { + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) @@ -226,7 +227,7 @@ func TestListAssignmentsOnResource_DomainsUsers(t *testing.T) { err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ UserID: "{user_id}", DomainID: "{domain_id}", - }).EachPage(func(page pagination.Page) (bool, error) { + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) @@ -248,7 +249,7 @@ func TestListAssignmentsOnResource_ProjectsGroups(t *testing.T) { err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ GroupID: "{group_id}", ProjectID: "{project_id}", - }).EachPage(func(page pagination.Page) (bool, error) { + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) @@ -270,7 +271,7 @@ func TestListAssignmentsOnResource_DomainsGroups(t *testing.T) { err := roles.ListAssignmentsOnResource(client.ServiceClient(), roles.ListAssignmentsOnResourceOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", - }).EachPage(func(page pagination.Page) (bool, error) { + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := roles.ExtractRoles(page) @@ -288,25 +289,25 @@ func TestAssign(t *testing.T) { defer th.TeardownHTTP() HandleAssignSuccessfully(t) - err := roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ + err := roles.Assign(context.TODO(), client.ServiceClient(), "{role_id}", roles.AssignOpts{ UserID: "{user_id}", ProjectID: "{project_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ + err = roles.Assign(context.TODO(), client.ServiceClient(), "{role_id}", roles.AssignOpts{ UserID: "{user_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ + err = roles.Assign(context.TODO(), client.ServiceClient(), "{role_id}", roles.AssignOpts{ GroupID: "{group_id}", ProjectID: "{project_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = roles.Assign(client.ServiceClient(), "{role_id}", roles.AssignOpts{ + err = roles.Assign(context.TODO(), client.ServiceClient(), "{role_id}", roles.AssignOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).ExtractErr() @@ -318,25 +319,25 @@ func TestUnassign(t *testing.T) { defer th.TeardownHTTP() HandleUnassignSuccessfully(t) - err := roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ + err := roles.Unassign(context.TODO(), client.ServiceClient(), "{role_id}", roles.UnassignOpts{ UserID: "{user_id}", ProjectID: "{project_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ + err = roles.Unassign(context.TODO(), client.ServiceClient(), "{role_id}", roles.UnassignOpts{ UserID: "{user_id}", DomainID: "{domain_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ + err = roles.Unassign(context.TODO(), client.ServiceClient(), "{role_id}", roles.UnassignOpts{ GroupID: "{group_id}", ProjectID: "{project_id}", }).ExtractErr() th.AssertNoErr(t, err) - err = roles.Unassign(client.ServiceClient(), "{role_id}", roles.UnassignOpts{ + err = roles.Unassign(context.TODO(), client.ServiceClient(), "{role_id}", roles.UnassignOpts{ GroupID: "{group_id}", DomainID: "{domain_id}", }).ExtractErr() diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index 4a32f5987c..f01f683525 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -1,6 +1,8 @@ package services import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -42,13 +44,13 @@ func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) { } // Create adds a new service of the requested type to the catalog. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToServiceCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -92,8 +94,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get returns additional information about a service, given its ID. -func Get(client *gophercloud.ServiceClient, serviceID string) (r GetResult) { - resp, err := client.Get(serviceURL(client, serviceID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, serviceID string) (r GetResult) { + resp, err := client.Get(ctx, serviceURL(client, serviceID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -135,13 +137,13 @@ func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { } // Update updates an existing Service. -func Update(client *gophercloud.ServiceClient, serviceID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, serviceID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToServiceUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, serviceID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, serviceID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -151,8 +153,8 @@ func Update(client *gophercloud.ServiceClient, serviceID string, opts UpdateOpts // Delete removes an existing service. // It either deletes all associated endpoints, or fails until all endpoints // are deleted. -func Delete(client *gophercloud.ServiceClient, serviceID string) (r DeleteResult) { - resp, err := client.Delete(serviceURL(client, serviceID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, serviceID string) (r DeleteResult) { + resp, err := client.Delete(ctx, serviceURL(client, serviceID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index 4f42a7b10c..a7afd5a9a9 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "net/http" "testing" @@ -24,7 +25,7 @@ func TestCreateSuccessful(t *testing.T) { }, } - actual, err := services.Create(client.ServiceClient(), createOpts).Extract() + actual, err := services.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondService, *actual) } @@ -35,7 +36,7 @@ func TestListServices(t *testing.T) { HandleListServicesSuccessfully(t) count := 0 - err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := services.ExtractServices(page) @@ -54,7 +55,7 @@ func TestListServicesAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListServicesSuccessfully(t) - allPages, err := services.List(client.ServiceClient(), nil).AllPages() + allPages, err := services.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) @@ -68,7 +69,7 @@ func TestGetSuccessful(t *testing.T) { defer th.TeardownHTTP() HandleGetServiceSuccessfully(t) - actual, err := services.Get(client.ServiceClient(), "9876").Extract() + actual, err := services.Get(context.TODO(), client.ServiceClient(), "9876").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondService, *actual) @@ -86,7 +87,7 @@ func TestUpdateSuccessful(t *testing.T) { "description": "Service Two Updated", }, } - actual, err := services.Update(client.ServiceClient(), "9876", updateOpts).Extract() + actual, err := services.Update(context.TODO(), client.ServiceClient(), "9876", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondServiceUpdated, *actual) th.AssertEquals(t, SecondServiceUpdated.Extra["description"], "Service Two Updated") @@ -102,6 +103,6 @@ func TestDeleteSuccessful(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := services.Delete(client.ServiceClient(), "12345") + res := services.Delete(context.TODO(), client.ServiceClient(), "12345") th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index e250a7582e..4a54ed1d3f 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -123,9 +123,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(ctx context.Context, c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { scope, err := opts.ToTokenV3ScopeMap() if err != nil { r.Err = err @@ -138,21 +138,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(ctx, 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(ctx context.Context, c *gophercloud.ServiceClient, token string) (r GetResult) { + resp, err := c.Get(ctx, tokenURL(c), &r.Body, &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 203}, }) @@ -160,14 +155,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(ctx context.Context, c *gophercloud.ServiceClient, token string) (bool, error) { + resp, err := c.Head(ctx, tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 204, 404}, }) @@ -178,21 +168,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(ctx context.Context, c *gophercloud.ServiceClient, token string) (r RevokeResult) { + resp, err := c.Delete(ctx, tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(token), }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// Revoke is a compatibility wrapper around RevokeWithContext -func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { - return RevokeWithContext(context.Background(), c, token) -} diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index e6fe6c4086..240adf2950 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -42,7 +43,7 @@ func authTokenPost(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope expected := &tokens.Token{ ExpiresAt: time.Date(2014, 10, 2, 13, 45, 0, 0, time.UTC), } - actual, err := tokens.Create(&client, &options).Extract() + actual, err := tokens.Create(context.TODO(), &client, &options).Extract() testhelper.AssertNoErr(t, err) testhelper.CheckDeepEquals(t, expected, actual) } @@ -63,7 +64,7 @@ func authTokenPostErr(t *testing.T, options tokens.AuthOptions, scope *tokens.Sc options.Scope = *scope } - _, err := tokens.Create(&client, &options).Extract() + _, err := tokens.Create(context.TODO(), &client, &options).Extract() if err == nil { t.Errorf("Create did NOT return an error") } @@ -425,7 +426,7 @@ func TestCreateExtractsTokenFromResponse(t *testing.T) { }) options := tokens.AuthOptions{UserID: "me", Password: "shhh"} - token, err := tokens.Create(&client, &options).Extract() + token, err := tokens.Create(context.TODO(), &client, &options).Extract() if err != nil { t.Fatalf("Create returned an error: %v", err) } @@ -567,7 +568,7 @@ func TestGetRequest(t *testing.T) { `) }) - token, err := tokens.Get(&client, "abcdef12345").Extract() + token, err := tokens.Get(context.TODO(), &client, "abcdef12345").Extract() if err != nil { t.Errorf("Info returned an error: %v", err) } @@ -604,7 +605,7 @@ func TestValidateRequestSuccessful(t *testing.T) { defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent) - ok, err := tokens.Validate(&client, "abcdef12345") + ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345") if err != nil { t.Errorf("Unexpected error from Validate: %v", err) } @@ -619,7 +620,7 @@ func TestValidateRequestFailure(t *testing.T) { defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound) - ok, err := tokens.Validate(&client, "abcdef12345") + ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345") if err != nil { t.Errorf("Unexpected error from Validate: %v", err) } @@ -634,7 +635,7 @@ func TestValidateRequestError(t *testing.T) { defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusMethodNotAllowed) - _, err := tokens.Validate(&client, "abcdef12345") + _, err := tokens.Validate(context.TODO(), &client, "abcdef12345") if err == nil { t.Errorf("Missing expected error from Validate") } @@ -645,7 +646,7 @@ func TestRevokeRequestSuccessful(t *testing.T) { defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent) - res := tokens.Revoke(&client, "abcdef12345") + res := tokens.Revoke(context.TODO(), &client, "abcdef12345") testhelper.AssertNoErr(t, res.Err) } @@ -654,7 +655,7 @@ func TestRevokeRequestError(t *testing.T) { defer testhelper.TeardownHTTP() client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound) - res := tokens.Revoke(&client, "abcdef12345") + res := tokens.Revoke(context.TODO(), &client, "abcdef12345") if res.Err == nil { t.Errorf("Missing expected error from Revoke") } @@ -675,6 +676,6 @@ func TestNoTokenInResponse(t *testing.T) { }) options := tokens.AuthOptions{UserID: "me", Password: "squirrel!"} - _, err := tokens.Create(&client, &options).Extract() + _, err := tokens.Create(context.TODO(), &client, &options).Extract() testhelper.AssertNoErr(t, err) } diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 644faab82f..0987c6f92a 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -1,6 +1,7 @@ package users import ( + "context" "net/url" "strings" @@ -93,8 +94,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details on a single user, by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -151,13 +152,13 @@ func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) { } // Create creates a new User. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToUserCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -216,13 +217,13 @@ func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) { } // Update updates an existing User. -func Update(client *gophercloud.ServiceClient, userID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, userID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToUserUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, userID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -255,14 +256,14 @@ func (opts ChangePasswordOpts) ToUserChangePasswordMap() (map[string]interface{} } // ChangePassword changes password for a user. -func ChangePassword(client *gophercloud.ServiceClient, userID string, opts ChangePasswordOptsBuilder) (r ChangePasswordResult) { +func ChangePassword(ctx context.Context, client *gophercloud.ServiceClient, userID string, opts ChangePasswordOptsBuilder) (r ChangePasswordResult) { b, err := opts.ToUserChangePasswordMap() if err != nil { r.Err = err return } - resp, err := client.Post(changePasswordURL(client, userID), &b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, changePasswordURL(client, userID), &b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -270,8 +271,8 @@ func ChangePassword(client *gophercloud.ServiceClient, userID string, opts Chang } // Delete deletes a user. -func Delete(client *gophercloud.ServiceClient, userID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, userID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, userID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, userID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -285,9 +286,9 @@ func ListGroups(client *gophercloud.ServiceClient, userID string) pagination.Pag } // AddToGroup adds a user to a group. -func AddToGroup(client *gophercloud.ServiceClient, groupID, userID string) (r AddToGroupResult) { +func AddToGroup(ctx context.Context, client *gophercloud.ServiceClient, groupID, userID string) (r AddToGroupResult) { url := addToGroupURL(client, groupID, userID) - resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -295,9 +296,9 @@ func AddToGroup(client *gophercloud.ServiceClient, groupID, userID string) (r Ad } // IsMemberOfGroup checks whether a user belongs to a group. -func IsMemberOfGroup(client *gophercloud.ServiceClient, groupID, userID string) (r IsMemberOfGroupResult) { +func IsMemberOfGroup(ctx context.Context, client *gophercloud.ServiceClient, groupID, userID string) (r IsMemberOfGroupResult) { url := isMemberOfGroupURL(client, groupID, userID) - resp, err := client.Head(url, &gophercloud.RequestOpts{ + resp, err := client.Head(ctx, url, &gophercloud.RequestOpts{ OkCodes: []int{204, 404}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -310,9 +311,9 @@ func IsMemberOfGroup(client *gophercloud.ServiceClient, groupID, userID string) } // RemoveFromGroup removes a user from a group. -func RemoveFromGroup(client *gophercloud.ServiceClient, groupID, userID string) (r RemoveFromGroupResult) { +func RemoveFromGroup(ctx context.Context, client *gophercloud.ServiceClient, groupID, userID string) (r RemoveFromGroupResult) { url := removeFromGroupURL(client, groupID, userID) - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index 20f568f66a..c3abfe9f49 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" @@ -17,7 +18,7 @@ func TestListUsers(t *testing.T) { HandleListUsersSuccessfully(t) count := 0 - err := users.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := users.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := users.ExtractUsers(page) @@ -36,7 +37,7 @@ func TestListUsersAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListUsersSuccessfully(t) - allPages, err := users.List(client.ServiceClient(), nil).AllPages() + allPages, err := users.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := users.ExtractUsers(allPages) th.AssertNoErr(t, err) @@ -82,7 +83,7 @@ func TestGetUser(t *testing.T) { defer th.TeardownHTTP() HandleGetUserSuccessfully(t) - actual, err := users.Get(client.ServiceClient(), "9fe1d3").Extract() + actual, err := users.Get(context.TODO(), client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondUser, *actual) th.AssertEquals(t, SecondUser.Extra["email"], "jsmith@example.com") @@ -112,7 +113,7 @@ func TestCreateUser(t *testing.T) { }, } - actual, err := users.Create(client.ServiceClient(), createOpts).Extract() + actual, err := users.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondUser, *actual) } @@ -134,7 +135,7 @@ func TestCreateNoOptionsUser(t *testing.T) { }, } - actual, err := users.Create(client.ServiceClient(), createOpts).Extract() + actual, err := users.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondUserNoOptions, *actual) } @@ -155,7 +156,7 @@ func TestUpdateUser(t *testing.T) { }, } - actual, err := users.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + actual, err := users.Update(context.TODO(), client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondUserUpdated, *actual) } @@ -170,7 +171,7 @@ func TestChangeUserPassword(t *testing.T) { Password: "new_secretsecret", } - res := users.ChangePassword(client.ServiceClient(), "9fe1d3", changePasswordOpts) + res := users.ChangePassword(context.TODO(), client.ServiceClient(), "9fe1d3", changePasswordOpts) th.AssertNoErr(t, res.Err) } @@ -179,7 +180,7 @@ func TestDeleteUser(t *testing.T) { defer th.TeardownHTTP() HandleDeleteUserSuccessfully(t) - res := users.Delete(client.ServiceClient(), "9fe1d3") + res := users.Delete(context.TODO(), client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } @@ -187,7 +188,7 @@ func TestListUserGroups(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListUserGroupsSuccessfully(t) - allPages, err := users.ListGroups(client.ServiceClient(), "9fe1d3").AllPages() + allPages, err := users.ListGroups(client.ServiceClient(), "9fe1d3").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := groups.ExtractGroups(allPages) th.AssertNoErr(t, err) @@ -198,7 +199,7 @@ func TestAddToGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleAddToGroupSuccessfully(t) - res := users.AddToGroup(client.ServiceClient(), "ea167b", "9fe1d3") + res := users.AddToGroup(context.TODO(), client.ServiceClient(), "ea167b", "9fe1d3") th.AssertNoErr(t, res.Err) } @@ -206,7 +207,7 @@ func TestIsMemberOfGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleIsMemberOfGroupSuccessfully(t) - ok, err := users.IsMemberOfGroup(client.ServiceClient(), "ea167b", "9fe1d3").Extract() + ok, err := users.IsMemberOfGroup(context.TODO(), client.ServiceClient(), "ea167b", "9fe1d3").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, ok) } @@ -215,7 +216,7 @@ func TestRemoveFromGroup(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleRemoveFromGroupSuccessfully(t) - res := users.RemoveFromGroup(client.ServiceClient(), "ea167b", "9fe1d3") + res := users.RemoveFromGroup(context.TODO(), client.ServiceClient(), "ea167b", "9fe1d3") th.AssertNoErr(t, res.Err) } @@ -223,7 +224,7 @@ func TestListUserProjects(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() HandleListUserProjectsSuccessfully(t) - allPages, err := users.ListProjects(client.ServiceClient(), "9fe1d3").AllPages() + allPages, err := users.ListProjects(client.ServiceClient(), "9fe1d3").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := projects.ExtractProjects(allPages) th.AssertNoErr(t, err) @@ -240,7 +241,7 @@ func TestListInGroup(t *testing.T) { Enabled: &iTrue, } - allPages, err := users.ListInGroup(client.ServiceClient(), "ea167b", listOpts).AllPages() + allPages, err := users.ListInGroup(client.ServiceClient(), "ea167b", listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := users.ExtractUsers(allPages) th.AssertNoErr(t, err) diff --git a/openstack/imageservice/v2/imagedata/requests.go b/openstack/imageservice/v2/imagedata/requests.go index 48992e1a70..1e56bc2264 100644 --- a/openstack/imageservice/v2/imagedata/requests.go +++ b/openstack/imageservice/v2/imagedata/requests.go @@ -1,14 +1,15 @@ package imagedata import ( + "context" "io" "github.com/gophercloud/gophercloud/v2" ) // Upload uploads an image file. -func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { - resp, err := client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ +func Upload(ctx context.Context, client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { + resp, err := client.Put(ctx, uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) @@ -19,8 +20,8 @@ func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r Upl // Stage performs PUT call on the existing image object in the Imageservice with // the provided file. // Existing image object must be in the "queued" status. -func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { - resp, err := client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{ +func Stage(ctx context.Context, client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { + resp, err := client.Put(ctx, stageURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, OkCodes: []int{204}, }) @@ -29,8 +30,8 @@ func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r Stag } // Download retrieves an image. -func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { - resp, err := client.Get(downloadURL(client, id), nil, &gophercloud.RequestOpts{ +func Download(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DownloadResult) { + resp, err := client.Get(ctx, downloadURL(client, id), nil, &gophercloud.RequestOpts{ KeepResponseBody: true, }) r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/imageservice/v2/imagedata/testing/requests_test.go b/openstack/imageservice/v2/imagedata/testing/requests_test.go index 03f199906e..a36d0a929c 100644 --- a/openstack/imageservice/v2/imagedata/testing/requests_test.go +++ b/openstack/imageservice/v2/imagedata/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "io" "testing" @@ -17,6 +18,7 @@ func TestUpload(t *testing.T) { HandlePutImageDataSuccessfully(t) err := imagedata.Upload( + context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", readSeekerOfBytes([]byte{5, 3, 7, 24})).ExtractErr() @@ -31,6 +33,7 @@ func TestStage(t *testing.T) { HandleStageImageDataSuccessfully(t) err := imagedata.Stage( + context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", readSeekerOfBytes([]byte{5, 3, 7, 24})).ExtractErr() @@ -90,7 +93,7 @@ func TestDownload(t *testing.T) { HandleGetImageDataSuccessfully(t) - rdr, err := imagedata.Download(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea").Extract() + rdr, err := imagedata.Download(context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea").Extract() th.AssertNoErr(t, err) defer rdr.Close() diff --git a/openstack/imageservice/v2/imageimport/requests.go b/openstack/imageservice/v2/imageimport/requests.go index 4468b06ff8..d78376bafe 100644 --- a/openstack/imageservice/v2/imageimport/requests.go +++ b/openstack/imageservice/v2/imageimport/requests.go @@ -1,6 +1,10 @@ package imageimport -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // ImportMethod represents valid Import API method. type ImportMethod string @@ -14,8 +18,8 @@ const ( ) // Get retrieves Import API information data. -func Get(c *gophercloud.ServiceClient) (r GetResult) { - resp, err := c.Get(infoURL(c), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient) (r GetResult) { + resp, err := c.Get(ctx, infoURL(c), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -41,13 +45,13 @@ func (opts CreateOpts) ToImportCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new image import on the server. -func Create(client *gophercloud.ServiceClient, imageID string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, imageID string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToImportCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(importURL(client, imageID), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, importURL(client, imageID), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/imageservice/v2/imageimport/testing/requests_test.go b/openstack/imageservice/v2/imageimport/testing/requests_test.go index 1e1d2660a3..3de8cc8054 100644 --- a/openstack/imageservice/v2/imageimport/testing/requests_test.go +++ b/openstack/imageservice/v2/imageimport/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -29,7 +30,7 @@ func TestGet(t *testing.T) { string(imageimport.WebDownloadMethod), } - s, err := imageimport.Get(fakeclient.ServiceClient()).Extract() + s, err := imageimport.Get(context.TODO(), fakeclient.ServiceClient()).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.ImportMethods.Description, "Import methods available.") @@ -55,6 +56,6 @@ func TestCreate(t *testing.T) { Name: imageimport.WebDownloadMethod, URI: "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", } - err := imageimport.Create(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", opts).ExtractErr() + err := imageimport.Create(context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", opts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go index 21348a1918..9c52569286 100644 --- a/openstack/imageservice/v2/images/requests.go +++ b/openstack/imageservice/v2/images/requests.go @@ -1,6 +1,7 @@ package images import ( + "context" "fmt" "net/url" "time" @@ -206,39 +207,39 @@ func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) { } // Create implements create image request. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToImageCreateMap() if err != nil { r.Err = err return r } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete implements image delete request. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get implements image get request. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Update implements image updated request. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToImageUpdateMap() if err != nil { r.Err = err return r } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, MoreHeaders: map[string]string{"Content-Type": "application/openstack-images-v2.1-json-patch"}, }) diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go index a4c70d47c4..a4aaf0a9ef 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/imageservice/v2/images/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -21,7 +22,7 @@ func TestListImage(t *testing.T) { pager := images.List(fakeclient.ServiceClient(), images.ListOpts{Limit: 1}) t.Logf("Pager state %v", pager) count, pages := 0, 0 - err := pager.EachPage(func(page pagination.Page) (bool, error) { + err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ t.Logf("Page %v", page) images, err := images.ExtractImages(page) @@ -49,7 +50,7 @@ func TestAllPagesImage(t *testing.T) { HandleImageListSuccessfully(t) - pages, err := images.List(fakeclient.ServiceClient(), nil).AllPages() + pages, err := images.List(fakeclient.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) images, err := images.ExtractImages(pages) th.AssertNoErr(t, err) @@ -65,7 +66,7 @@ func TestCreateImage(t *testing.T) { id := "e7db3b45-8db7-47ad-8109-3fb55c2c24fd" name := "Ubuntu 12.10" - actualImage, err := images.Create(fakeclient.ServiceClient(), images.CreateOpts{ + actualImage, err := images.Create(context.TODO(), fakeclient.ServiceClient(), images.CreateOpts{ ID: id, Name: name, Properties: map[string]string{ @@ -126,7 +127,7 @@ func TestCreateImageNulls(t *testing.T) { id := "e7db3b45-8db7-47ad-8109-3fb55c2c24fd" name := "Ubuntu 12.10" - actualImage, err := images.Create(fakeclient.ServiceClient(), images.CreateOpts{ + actualImage, err := images.Create(context.TODO(), fakeclient.ServiceClient(), images.CreateOpts{ ID: id, Name: name, Tags: []string{"ubuntu", "quantal"}, @@ -192,7 +193,7 @@ func TestGetImage(t *testing.T) { HandleImageGetSuccessfully(t) - actualImage, err := images.Get(fakeclient.ServiceClient(), "1bea47ed-f6a9-463b-b423-14b9cca9ad27").Extract() + actualImage, err := images.Get(context.TODO(), fakeclient.ServiceClient(), "1bea47ed-f6a9-463b-b423-14b9cca9ad27").Extract() th.AssertNoErr(t, err) @@ -250,7 +251,7 @@ func TestDeleteImage(t *testing.T) { HandleImageDeleteSuccessfully(t) - result := images.Delete(fakeclient.ServiceClient(), "1bea47ed-f6a9-463b-b423-14b9cca9ad27") + result := images.Delete(context.TODO(), fakeclient.ServiceClient(), "1bea47ed-f6a9-463b-b423-14b9cca9ad27") th.AssertNoErr(t, result.Err) } @@ -260,7 +261,7 @@ func TestUpdateImage(t *testing.T) { HandleImageUpdateSuccessfully(t) - actualImage, err := images.Update(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", images.UpdateOpts{ + actualImage, err := images.Update(context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", images.UpdateOpts{ images.ReplaceImageName{NewName: "Fedora 17"}, images.ReplaceImageTags{NewTags: []string{"fedora", "beefy"}}, images.ReplaceImageMinDisk{NewMinDisk: 21}, @@ -355,7 +356,7 @@ func TestImageListByTags(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, expectedQueryString, actualQueryString) - pages, err := images.List(fakeclient.ServiceClient(), listOpts).AllPages() + pages, err := images.List(fakeclient.ServiceClient(), listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) allImages, err := images.ExtractImages(pages) th.AssertNoErr(t, err) @@ -414,7 +415,7 @@ func TestUpdateImageProperties(t *testing.T) { HandleImageUpdatePropertiesSuccessfully(t) - actualImage, err := images.Update(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", images.UpdateOpts{ + actualImage, err := images.Update(context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", images.UpdateOpts{ images.UpdateImageProperty{ Op: images.AddOp, Name: "hw_disk_bus", diff --git a/openstack/imageservice/v2/members/requests.go b/openstack/imageservice/v2/members/requests.go index d202dafe89..12717e6a8f 100644 --- a/openstack/imageservice/v2/members/requests.go +++ b/openstack/imageservice/v2/members/requests.go @@ -1,6 +1,8 @@ package members import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -23,9 +25,9 @@ pending through API calls. 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) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, id string, member string) (r CreateResult) { b := map[string]interface{}{"member": member} - resp, err := client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -40,15 +42,15 @@ func List(client *gophercloud.ServiceClient, id string) pagination.Pager { } // Get image member details. -func Get(client *gophercloud.ServiceClient, imageID string, memberID string) (r DetailsResult) { - resp, err := client.Get(getMemberURL(client, imageID, memberID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) +func Get(ctx context.Context, client *gophercloud.ServiceClient, imageID string, memberID string) (r DetailsResult) { + resp, err := client.Get(ctx, getMemberURL(client, imageID, memberID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete membership for given image. Callee should be image owner. -func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) (r DeleteResult) { - resp, err := client.Delete(deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204}}) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, imageID string, memberID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -72,13 +74,13 @@ func (opts UpdateOpts) ToImageMemberUpdateMap() (map[string]interface{}, error) } // Update function updates member. -func Update(client *gophercloud.ServiceClient, imageID string, memberID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, imageID string, memberID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToImageMemberUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateMemberURL(client, imageID, memberID), b, &r.Body, + resp, err := client.Put(ctx, updateMemberURL(client, imageID, memberID), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/imageservice/v2/members/testing/requests_test.go b/openstack/imageservice/v2/members/testing/requests_test.go index e84a732306..04717bfeea 100644 --- a/openstack/imageservice/v2/members/testing/requests_test.go +++ b/openstack/imageservice/v2/members/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -18,7 +19,7 @@ func TestCreateMemberSuccessfully(t *testing.T) { defer th.TeardownHTTP() HandleCreateImageMemberSuccessfully(t) - im, err := members.Create(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + im, err := members.Create(context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "8989447062e04a818baf9e073fd04fa7").Extract() th.AssertNoErr(t, err) @@ -48,7 +49,7 @@ func TestMemberListSuccessfully(t *testing.T) { pager := members.List(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea") t.Logf("Pager state %v", pager) count, pages := 0, 0 - err := pager.EachPage(func(page pagination.Page) (bool, error) { + err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ t.Logf("Page %v", page) members, err := members.ExtractMembers(page) @@ -78,7 +79,7 @@ func TestMemberListEmpty(t *testing.T) { pager := members.List(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea") t.Logf("Pager state %v", pager) count, pages := 0, 0 - err := pager.EachPage(func(page pagination.Page) (bool, error) { + err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ t.Logf("Page %v", page) members, err := members.ExtractMembers(page) @@ -104,7 +105,7 @@ func TestShowMemberDetails(t *testing.T) { defer th.TeardownHTTP() HandleImageMemberDetails(t) - md, err := members.Get(fakeclient.ServiceClient(), + md, err := members.Get(context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "8989447062e04a818baf9e073fd04fa7").Extract() @@ -135,7 +136,7 @@ func TestDeleteMember(t *testing.T) { counter := HandleImageMemberDeleteSuccessfully(t) - result := members.Delete(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + result := members.Delete(context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "8989447062e04a818baf9e073fd04fa7") th.AssertEquals(t, 1, counter.Counter) th.AssertNoErr(t, result.Err) @@ -146,7 +147,7 @@ func TestMemberUpdateSuccessfully(t *testing.T) { defer th.TeardownHTTP() counter := HandleImageMemberUpdate(t) - im, err := members.Update(fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", + im, err := members.Update(context.TODO(), fakeclient.ServiceClient(), "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "8989447062e04a818baf9e073fd04fa7", members.UpdateOpts{ Status: "accepted", diff --git a/openstack/imageservice/v2/tasks/requests.go b/openstack/imageservice/v2/tasks/requests.go index ebeb10d0f6..fa6972b529 100644 --- a/openstack/imageservice/v2/tasks/requests.go +++ b/openstack/imageservice/v2/tasks/requests.go @@ -1,6 +1,8 @@ package tasks import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -89,8 +91,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific Imageservice task based on its ID. -func Get(c *gophercloud.ServiceClient, taskID string) (r GetResult) { - resp, err := c.Get(getURL(c, taskID), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, taskID string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, taskID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -116,13 +118,13 @@ func (opts CreateOpts) ToTaskCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new Imageservice task on the server. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTaskCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/imageservice/v2/tasks/testing/requests_test.go b/openstack/imageservice/v2/tasks/testing/requests_test.go index 95c29c1242..e6c8781d61 100644 --- a/openstack/imageservice/v2/tasks/testing/requests_test.go +++ b/openstack/imageservice/v2/tasks/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -28,7 +29,7 @@ func TestList(t *testing.T) { count := 0 - tasks.List(fakeclient.ServiceClient(), tasks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + tasks.List(fakeclient.ServiceClient(), tasks.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := tasks.ExtractTasks(page) if err != nil { @@ -65,7 +66,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, TasksGetResult) }) - s, err := tasks.Get(fakeclient.ServiceClient(), "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() + s, err := tasks.Get(context.TODO(), fakeclient.ServiceClient(), "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, string(tasks.TaskStatusPending)) @@ -114,7 +115,7 @@ func TestCreate(t *testing.T) { "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img", }, } - s, err := tasks.Create(fakeclient.ServiceClient(), opts).Extract() + s, err := tasks.Create(context.TODO(), fakeclient.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, string(tasks.TaskStatusPending)) diff --git a/openstack/keymanager/v1/acls/requests.go b/openstack/keymanager/v1/acls/requests.go index 82a682a475..62899d4b22 100644 --- a/openstack/keymanager/v1/acls/requests.go +++ b/openstack/keymanager/v1/acls/requests.go @@ -1,19 +1,21 @@ package acls import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // GetContainerACL retrieves the ACL of a container. -func GetContainerACL(client *gophercloud.ServiceClient, containerID string) (r ACLResult) { - resp, err := client.Get(containerURL(client, containerID), &r.Body, nil) +func GetContainerACL(ctx context.Context, client *gophercloud.ServiceClient, containerID string) (r ACLResult) { + resp, err := client.Get(ctx, containerURL(client, containerID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetSecretACL retrieves the ACL of a secret. -func GetSecretACL(client *gophercloud.ServiceClient, secretID string) (r ACLResult) { - resp, err := client.Get(secretURL(client, secretID), &r.Body, nil) +func GetSecretACL(ctx context.Context, client *gophercloud.ServiceClient, secretID string) (r ACLResult) { + resp, err := client.Get(ctx, secretURL(client, secretID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -53,14 +55,14 @@ func (opts SetOpts) ToACLSetMap() (map[string]interface{}, error) { } // SetContainerACL will set an ACL on a container. -func SetContainerACL(client *gophercloud.ServiceClient, containerID string, opts SetOptsBuilder) (r ACLRefResult) { +func SetContainerACL(ctx context.Context, client *gophercloud.ServiceClient, containerID string, opts SetOptsBuilder) (r ACLRefResult) { b, err := opts.ToACLSetMap() if err != nil { r.Err = err return } - resp, err := client.Put(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -68,14 +70,14 @@ func SetContainerACL(client *gophercloud.ServiceClient, containerID string, opts } // SetSecretACL will set an ACL on a secret. -func SetSecretACL(client *gophercloud.ServiceClient, secretID string, opts SetOptsBuilder) (r ACLRefResult) { +func SetSecretACL(ctx context.Context, client *gophercloud.ServiceClient, secretID string, opts SetOptsBuilder) (r ACLRefResult) { b, err := opts.ToACLSetMap() if err != nil { r.Err = err return } - resp, err := client.Put(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -83,14 +85,14 @@ func SetSecretACL(client *gophercloud.ServiceClient, secretID string, opts SetOp } // UpdateContainerACL will update an ACL on a container. -func UpdateContainerACL(client *gophercloud.ServiceClient, containerID string, opts SetOptsBuilder) (r ACLRefResult) { +func UpdateContainerACL(ctx context.Context, client *gophercloud.ServiceClient, containerID string, opts SetOptsBuilder) (r ACLRefResult) { b, err := opts.ToACLSetMap() if err != nil { r.Err = err return } - resp, err := client.Patch(containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, containerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -98,14 +100,14 @@ func UpdateContainerACL(client *gophercloud.ServiceClient, containerID string, o } // UpdateSecretACL will update an ACL on a secret. -func UpdateSecretACL(client *gophercloud.ServiceClient, secretID string, opts SetOptsBuilder) (r ACLRefResult) { +func UpdateSecretACL(ctx context.Context, client *gophercloud.ServiceClient, secretID string, opts SetOptsBuilder) (r ACLRefResult) { b, err := opts.ToACLSetMap() if err != nil { r.Err = err return } - resp, err := client.Patch(secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, secretURL(client, secretID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -113,8 +115,8 @@ func UpdateSecretACL(client *gophercloud.ServiceClient, secretID string, opts Se } // DeleteContainerACL will delete an ACL from a conatiner. -func DeleteContainerACL(client *gophercloud.ServiceClient, containerID string) (r DeleteResult) { - resp, err := client.Delete(containerURL(client, containerID), &gophercloud.RequestOpts{ +func DeleteContainerACL(ctx context.Context, client *gophercloud.ServiceClient, containerID string) (r DeleteResult) { + resp, err := client.Delete(ctx, containerURL(client, containerID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -122,8 +124,8 @@ func DeleteContainerACL(client *gophercloud.ServiceClient, containerID string) ( } // DeleteSecretACL will delete an ACL from a secret. -func DeleteSecretACL(client *gophercloud.ServiceClient, secretID string) (r DeleteResult) { - resp, err := client.Delete(secretURL(client, secretID), &gophercloud.RequestOpts{ +func DeleteSecretACL(ctx context.Context, client *gophercloud.ServiceClient, secretID string) (r DeleteResult) { + resp, err := client.Delete(ctx, secretURL(client, secretID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/keymanager/v1/acls/testing/requests_test.go b/openstack/keymanager/v1/acls/testing/requests_test.go index f7b46e0c81..3367381520 100644 --- a/openstack/keymanager/v1/acls/testing/requests_test.go +++ b/openstack/keymanager/v1/acls/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/acls" @@ -13,7 +14,7 @@ func TestGetSecretACL(t *testing.T) { defer th.TeardownHTTP() HandleGetSecretACLSuccessfully(t) - actual, err := acls.GetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() + actual, err := acls.GetSecretACL(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedACL, *actual) } @@ -23,7 +24,7 @@ func TestGetContainerACL(t *testing.T) { defer th.TeardownHTTP() HandleGetContainerACLSuccessfully(t) - actual, err := acls.GetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() + actual, err := acls.GetContainerACL(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedACL, *actual) } @@ -43,7 +44,7 @@ func TestSetSecretACL(t *testing.T) { }, } - actual, err := acls.SetSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() + actual, err := acls.SetSecretACL(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedSecretACLRef, *actual) } @@ -63,7 +64,7 @@ func TestSetContainerACL(t *testing.T) { }, } - actual, err := acls.SetContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() + actual, err := acls.SetContainerACL(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", setOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedContainerACLRef, *actual) } @@ -73,7 +74,7 @@ func TestDeleteSecretACL(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSecretACLSuccessfully(t) - res := acls.DeleteSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") + res := acls.DeleteSecretACL(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") th.AssertNoErr(t, res.Err) } @@ -82,7 +83,7 @@ func TestDeleteContainerACL(t *testing.T) { defer th.TeardownHTTP() HandleDeleteContainerACLSuccessfully(t) - res := acls.DeleteContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") + res := acls.DeleteContainerACL(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") th.AssertNoErr(t, res.Err) } @@ -99,7 +100,7 @@ func TestUpdateSecretACL(t *testing.T) { }, } - actual, err := acls.UpdateSecretACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() + actual, err := acls.UpdateSecretACL(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedSecretACLRef, *actual) } @@ -117,7 +118,7 @@ func TestUpdateContainerACL(t *testing.T) { }, } - actual, err := acls.UpdateContainerACL(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() + actual, err := acls.UpdateContainerACL(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedContainerACLRef, *actual) } diff --git a/openstack/keymanager/v1/containers/requests.go b/openstack/keymanager/v1/containers/requests.go index 48bdfad5a9..cfab2e7ea5 100644 --- a/openstack/keymanager/v1/containers/requests.go +++ b/openstack/keymanager/v1/containers/requests.go @@ -1,6 +1,8 @@ package containers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -54,8 +56,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details of a container. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -84,13 +86,13 @@ func (opts CreateOpts) ToContainerCreateMap() (map[string]interface{}, error) { } // Create creates a new container. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToContainerCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -98,8 +100,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes a container. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -163,13 +165,13 @@ func (opts CreateConsumerOpts) ToContainerConsumerCreateMap() (map[string]interf } // CreateConsumer creates a new consumer. -func CreateConsumer(client *gophercloud.ServiceClient, containerID string, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { +func CreateConsumer(ctx context.Context, client *gophercloud.ServiceClient, containerID string, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { b, err := opts.ToContainerConsumerCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createConsumerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createConsumerURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -198,7 +200,7 @@ func (opts DeleteConsumerOpts) ToContainerConsumerDeleteMap() (map[string]interf } // DeleteConsumer deletes a consumer. -func DeleteConsumer(client *gophercloud.ServiceClient, containerID string, opts DeleteConsumerOptsBuilder) (r DeleteConsumerResult) { +func DeleteConsumer(ctx context.Context, client *gophercloud.ServiceClient, containerID string, opts DeleteConsumerOptsBuilder) (r DeleteConsumerResult) { url := deleteConsumerURL(client, containerID) b, err := opts.ToContainerConsumerDeleteMap() @@ -207,7 +209,7 @@ func DeleteConsumer(client *gophercloud.ServiceClient, containerID string, opts return } - resp, err := client.Request("DELETE", url, &gophercloud.RequestOpts{ + resp, err := client.Request(ctx, "DELETE", url, &gophercloud.RequestOpts{ JSONBody: b, JSONResponse: &r.Body, OkCodes: []int{200}, @@ -229,13 +231,13 @@ func (opts SecretRef) ToContainerSecretRefMap() (map[string]interface{}, error) } // CreateSecret creates a new consumer. -func CreateSecretRef(client *gophercloud.ServiceClient, containerID string, opts SecretRefBuilder) (r CreateSecretRefResult) { +func CreateSecretRef(ctx context.Context, client *gophercloud.ServiceClient, containerID string, opts SecretRefBuilder) (r CreateSecretRefResult) { b, err := opts.ToContainerSecretRefMap() if err != nil { r.Err = err return } - resp, err := client.Post(createSecretRefURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createSecretRefURL(client, containerID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -243,7 +245,7 @@ func CreateSecretRef(client *gophercloud.ServiceClient, containerID string, opts } // DeleteSecret deletes a consumer. -func DeleteSecretRef(client *gophercloud.ServiceClient, containerID string, opts SecretRefBuilder) (r DeleteSecretRefResult) { +func DeleteSecretRef(ctx context.Context, client *gophercloud.ServiceClient, containerID string, opts SecretRefBuilder) (r DeleteSecretRefResult) { url := deleteSecretRefURL(client, containerID) b, err := opts.ToContainerSecretRefMap() @@ -252,7 +254,7 @@ func DeleteSecretRef(client *gophercloud.ServiceClient, containerID string, opts return } - resp, err := client.Request("DELETE", url, &gophercloud.RequestOpts{ + resp, err := client.Request(ctx, "DELETE", url, &gophercloud.RequestOpts{ JSONBody: b, OkCodes: []int{204}, }) diff --git a/openstack/keymanager/v1/containers/testing/requests_test.go b/openstack/keymanager/v1/containers/testing/requests_test.go index e46f2e9691..44ed27b217 100644 --- a/openstack/keymanager/v1/containers/testing/requests_test.go +++ b/openstack/keymanager/v1/containers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/containers" @@ -15,7 +16,7 @@ func TestListContainers(t *testing.T) { HandleListContainersSuccessfully(t) count := 0 - err := containers.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := containers.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractContainers(page) @@ -34,7 +35,7 @@ func TestListContainersAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListContainersSuccessfully(t) - allPages, err := containers.List(client.ServiceClient(), nil).AllPages() + allPages, err := containers.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := containers.ExtractContainers(allPages) th.AssertNoErr(t, err) @@ -46,7 +47,7 @@ func TestGetContainer(t *testing.T) { defer th.TeardownHTTP() HandleGetContainerSuccessfully(t) - actual, err := containers.Get(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0").Extract() + actual, err := containers.Get(context.TODO(), client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FirstContainer, *actual) } @@ -67,7 +68,7 @@ func TestCreateContainer(t *testing.T) { }, } - actual, err := containers.Create(client.ServiceClient(), createOpts).Extract() + actual, err := containers.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FirstContainer, *actual) } @@ -77,7 +78,7 @@ func TestDeleteContainer(t *testing.T) { defer th.TeardownHTTP() HandleDeleteContainerSuccessfully(t) - res := containers.Delete(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0") + res := containers.Delete(context.TODO(), client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0") th.AssertNoErr(t, res.Err) } @@ -87,7 +88,7 @@ func TestListConsumers(t *testing.T) { HandleListConsumersSuccessfully(t) count := 0 - err := containers.ListConsumers(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", nil).EachPage(func(page pagination.Page) (bool, error) { + err := containers.ListConsumers(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractConsumers(page) @@ -106,7 +107,7 @@ func TestListConsumersAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListConsumersSuccessfully(t) - allPages, err := containers.ListConsumers(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", nil).AllPages() + allPages, err := containers.ListConsumers(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := containers.ExtractConsumers(allPages) th.AssertNoErr(t, err) @@ -123,7 +124,7 @@ func TestCreateConsumer(t *testing.T) { URL: "http://example.com", } - actual, err := containers.CreateConsumer(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", createOpts).Extract() + actual, err := containers.CreateConsumer(context.TODO(), client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreatedConsumer, *actual) } @@ -138,7 +139,7 @@ func TestDeleteConsumer(t *testing.T) { URL: "http://example.com", } - actual, err := containers.DeleteConsumer(client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", deleteOpts).Extract() + actual, err := containers.DeleteConsumer(context.TODO(), client.ServiceClient(), "dfdb88f3-4ddb-4525-9da6-066453caa9b0", deleteOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FirstContainer, *actual) } diff --git a/openstack/keymanager/v1/orders/requests.go b/openstack/keymanager/v1/orders/requests.go index 9aaea4901c..30c0ffe787 100644 --- a/openstack/keymanager/v1/orders/requests.go +++ b/openstack/keymanager/v1/orders/requests.go @@ -1,6 +1,7 @@ package orders import ( + "context" "time" "github.com/gophercloud/gophercloud/v2" @@ -52,8 +53,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details of a orders. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -111,13 +112,13 @@ func (opts CreateOpts) ToOrderCreateMap() (map[string]interface{}, error) { } // Create creates a new orders. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToOrderCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -125,8 +126,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes a orders. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/keymanager/v1/orders/testing/requests_test.go b/openstack/keymanager/v1/orders/testing/requests_test.go index 21f7c726be..df2b10d9c1 100644 --- a/openstack/keymanager/v1/orders/testing/requests_test.go +++ b/openstack/keymanager/v1/orders/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/keymanager/v1/orders" @@ -15,7 +16,7 @@ func TestListOrders(t *testing.T) { HandleListOrdersSuccessfully(t) count := 0 - err := orders.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := orders.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := orders.ExtractOrders(page) @@ -34,7 +35,7 @@ func TestListOrdersAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListOrdersSuccessfully(t) - allPages, err := orders.List(client.ServiceClient(), nil).AllPages() + allPages, err := orders.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := orders.ExtractOrders(allPages) th.AssertNoErr(t, err) @@ -46,7 +47,7 @@ func TestGetOrder(t *testing.T) { defer th.TeardownHTTP() HandleGetOrderSuccessfully(t) - actual, err := orders.Get(client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7").Extract() + actual, err := orders.Get(context.TODO(), client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondOrder, *actual) } @@ -66,7 +67,7 @@ func TestCreateOrder(t *testing.T) { }, } - actual, err := orders.Create(client.ServiceClient(), createOpts).Extract() + actual, err := orders.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondOrder, *actual) } @@ -76,6 +77,6 @@ func TestDeleteOrder(t *testing.T) { defer th.TeardownHTTP() HandleDeleteOrderSuccessfully(t) - res := orders.Delete(client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7") + res := orders.Delete(context.TODO(), client.ServiceClient(), "46f73695-82bb-447a-bf96-6635f0fb0ce7") th.AssertNoErr(t, res.Err) } diff --git a/openstack/keymanager/v1/secrets/requests.go b/openstack/keymanager/v1/secrets/requests.go index 55315e8b19..f836d28f70 100644 --- a/openstack/keymanager/v1/secrets/requests.go +++ b/openstack/keymanager/v1/secrets/requests.go @@ -1,6 +1,7 @@ package secrets import ( + "context" "fmt" "net/url" "strings" @@ -142,8 +143,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details of a secrets. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -165,7 +166,7 @@ func (opts GetPayloadOpts) ToSecretPayloadGetParams() (map[string]string, error) } // GetPayload retrieves the payload of a secret. -func GetPayload(client *gophercloud.ServiceClient, id string, opts GetPayloadOptsBuilder) (r PayloadResult) { +func GetPayload(ctx context.Context, client *gophercloud.ServiceClient, id string, opts GetPayloadOptsBuilder) (r PayloadResult) { h := map[string]string{"Accept": "text/plain"} if opts != nil { @@ -180,7 +181,7 @@ func GetPayload(client *gophercloud.ServiceClient, id string, opts GetPayloadOpt } url := payloadURL(client, id) - resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200}, KeepResponseBody: true, @@ -240,13 +241,13 @@ func (opts CreateOpts) ToSecretCreateMap() (map[string]interface{}, error) { } // Create creates a new secrets. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecretCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -254,8 +255,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes a secrets. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -290,7 +291,7 @@ func (opts UpdateOpts) ToSecretUpdateRequest() (string, map[string]string, error } // Update modifies the attributes of a secrets. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { url := updateURL(client, id) h := make(map[string]string) var b string @@ -309,7 +310,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder b = payload } - resp, err := client.Put(url, strings.NewReader(b), nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, url, strings.NewReader(b), nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{204}, }) @@ -318,8 +319,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder } // GetMetadata will list metadata for a given secret. -func GetMetadata(client *gophercloud.ServiceClient, secretID string) (r MetadataResult) { - resp, err := client.Get(metadataURL(client, secretID), &r.Body, nil) +func GetMetadata(ctx context.Context, client *gophercloud.ServiceClient, secretID string) (r MetadataResult) { + resp, err := client.Get(ctx, metadataURL(client, secretID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -339,13 +340,13 @@ func (opts MetadataOpts) ToMetadataCreateMap() (map[string]interface{}, error) { } // CreateMetadata will set metadata for a given secret. -func CreateMetadata(client *gophercloud.ServiceClient, secretID string, opts CreateMetadataOptsBuilder) (r MetadataCreateResult) { +func CreateMetadata(ctx context.Context, client *gophercloud.ServiceClient, secretID string, opts CreateMetadataOptsBuilder) (r MetadataCreateResult) { b, err := opts.ToMetadataCreateMap() if err != nil { r.Err = err return } - resp, err := client.Put(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -353,8 +354,8 @@ func CreateMetadata(client *gophercloud.ServiceClient, secretID string, opts Cre } // GetMetadatum will get a single key/value metadata from a secret. -func GetMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumResult) { - resp, err := client.Get(metadatumURL(client, secretID, key), &r.Body, nil) +func GetMetadatum(ctx context.Context, client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumResult) { + resp, err := client.Get(ctx, metadatumURL(client, secretID, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -377,13 +378,13 @@ func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, error) } // CreateMetadatum will add a single key/value metadata to a secret. -func CreateMetadatum(client *gophercloud.ServiceClient, secretID string, opts CreateMetadatumOptsBuilder) (r MetadatumCreateResult) { +func CreateMetadatum(ctx context.Context, client *gophercloud.ServiceClient, secretID string, opts CreateMetadatumOptsBuilder) (r MetadatumCreateResult) { b, err := opts.ToMetadatumCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, metadataURL(client, secretID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -403,13 +404,13 @@ func (opts MetadatumOpts) ToMetadatumUpdateMap() (map[string]interface{}, string } // UpdateMetadatum will update a single key/value metadata to a secret. -func UpdateMetadatum(client *gophercloud.ServiceClient, secretID string, opts UpdateMetadatumOptsBuilder) (r MetadatumResult) { +func UpdateMetadatum(ctx context.Context, client *gophercloud.ServiceClient, secretID string, opts UpdateMetadatumOptsBuilder) (r MetadatumResult) { b, key, err := opts.ToMetadatumUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(metadatumURL(client, secretID, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, metadatumURL(client, secretID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -417,8 +418,8 @@ func UpdateMetadatum(client *gophercloud.ServiceClient, secretID string, opts Up } // DeleteMetadatum will delete an individual metadatum from a secret. -func DeleteMetadatum(client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumDeleteResult) { - resp, err := client.Delete(metadatumURL(client, secretID, key), nil) +func DeleteMetadatum(ctx context.Context, client *gophercloud.ServiceClient, secretID string, key string) (r MetadatumDeleteResult) { + resp, err := client.Delete(ctx, metadatumURL(client, secretID, key), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/keymanager/v1/secrets/testing/requests_test.go b/openstack/keymanager/v1/secrets/testing/requests_test.go index 98383e0846..0458354bc5 100644 --- a/openstack/keymanager/v1/secrets/testing/requests_test.go +++ b/openstack/keymanager/v1/secrets/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -16,7 +17,7 @@ func TestListSecrets(t *testing.T) { HandleListSecretsSuccessfully(t) count := 0 - err := secrets.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := secrets.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := secrets.ExtractSecrets(page) @@ -35,7 +36,7 @@ func TestListSecretsAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListSecretsSuccessfully(t) - allPages, err := secrets.List(client.ServiceClient(), nil).AllPages() + allPages, err := secrets.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := secrets.ExtractSecrets(allPages) th.AssertNoErr(t, err) @@ -47,7 +48,7 @@ func TestGetSecret(t *testing.T) { defer th.TeardownHTTP() HandleGetSecretSuccessfully(t) - actual, err := secrets.Get(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() + actual, err := secrets.Get(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, FirstSecret, *actual) } @@ -69,7 +70,7 @@ func TestCreateSecret(t *testing.T) { Expiration: &expiration, } - actual, err := secrets.Create(client.ServiceClient(), createOpts).Extract() + actual, err := secrets.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreateResult, *actual) } @@ -79,7 +80,7 @@ func TestDeleteSecret(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSecretSuccessfully(t) - res := secrets.Delete(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") + res := secrets.Delete(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c") th.AssertNoErr(t, res.Err) } @@ -92,7 +93,7 @@ func TestUpdateSecret(t *testing.T) { Payload: "foobar", } - err := secrets.Update(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).ExtractErr() + err := secrets.Update(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -101,7 +102,7 @@ func TestGetPayloadSecret(t *testing.T) { defer th.TeardownHTTP() HandleGetPayloadSuccessfully(t) - res := secrets.GetPayload(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", nil) + res := secrets.GetPayload(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", nil) th.AssertNoErr(t, res.Err) payload, err := res.Extract() th.AssertNoErr(t, err) @@ -113,7 +114,7 @@ func TestGetMetadataSuccessfully(t *testing.T) { defer th.TeardownHTTP() HandleGetMetadataSuccessfully(t) - actual, err := secrets.GetMetadata(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() + actual, err := secrets.GetMetadata(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedMetadata, actual) } @@ -128,7 +129,7 @@ func TestCreateMetadataSuccessfully(t *testing.T) { "something": "something else", } - actual, err := secrets.CreateMetadata(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", createOpts).Extract() + actual, err := secrets.CreateMetadata(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedCreateMetadataResult, actual) } @@ -138,7 +139,7 @@ func TestGetMetadatumSuccessfully(t *testing.T) { defer th.TeardownHTTP() HandleGetMetadatumSuccessfully(t) - actual, err := secrets.GetMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "foo").Extract() + actual, err := secrets.GetMetadatum(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "foo").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedMetadatum, *actual) } @@ -153,7 +154,7 @@ func TestCreateMetadatumSuccessfully(t *testing.T) { Value: "bar", } - err := secrets.CreateMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", createOpts).ExtractErr() + err := secrets.CreateMetadatum(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", createOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -167,7 +168,7 @@ func TestUpdateMetadatumSuccessfully(t *testing.T) { Value: "bar", } - actual, err := secrets.UpdateMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() + actual, err := secrets.UpdateMetadatum(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedMetadatum, *actual) } @@ -177,6 +178,6 @@ func TestDeleteMetadatumSuccessfully(t *testing.T) { defer th.TeardownHTTP() HandleDeleteMetadatumSuccessfully(t) - err := secrets.DeleteMetadatum(client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "foo").ExtractErr() + err := secrets.DeleteMetadatum(context.TODO(), client.ServiceClient(), "1b8068c4-3bb6-4be6-8f1e-da0d1ea0b67c", "foo").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/loadbalancer/v2/amphorae/requests.go b/openstack/loadbalancer/v2/amphorae/requests.go index 3e94d5ba8f..c0059c2b38 100644 --- a/openstack/loadbalancer/v2/amphorae/requests.go +++ b/openstack/loadbalancer/v2/amphorae/requests.go @@ -1,6 +1,8 @@ package amphorae import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -53,15 +55,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a particular amphora based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Failover performs a failover of an amphora. -func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { - resp, err := c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ +func Failover(ctx context.Context, c *gophercloud.ServiceClient, id string) (r FailoverResult) { + resp, err := c.Put(ctx, failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go index cfa303e643..bc5feed401 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/requests_test.go +++ b/openstack/loadbalancer/v2/amphorae/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/amphorae" @@ -15,7 +16,7 @@ func TestListAmphorae(t *testing.T) { HandleAmphoraListSuccessfully(t) pages := 0 - err := amphorae.List(fake.ServiceClient(), amphorae.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := amphorae.List(fake.ServiceClient(), amphorae.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := amphorae.ExtractAmphorae(page) @@ -42,7 +43,7 @@ func TestListAllAmphorae(t *testing.T) { defer th.TeardownHTTP() HandleAmphoraListSuccessfully(t) - allPages, err := amphorae.List(fake.ServiceClient(), amphorae.ListOpts{}).AllPages() + allPages, err := amphorae.List(fake.ServiceClient(), amphorae.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := amphorae.ExtractAmphorae(allPages) th.AssertNoErr(t, err) @@ -56,7 +57,7 @@ func TestGetAmphora(t *testing.T) { HandleAmphoraGetSuccessfully(t) client := fake.ServiceClient() - actual, err := amphorae.Get(client, "45f40289-0551-483a-b089-47214bc2a8a4").Extract() + actual, err := amphorae.Get(context.TODO(), client, "45f40289-0551-483a-b089-47214bc2a8a4").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -69,6 +70,6 @@ func TestFailoverAmphora(t *testing.T) { defer th.TeardownHTTP() HandleAmphoraFailoverSuccessfully(t) - res := amphorae.Failover(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") + res := amphorae.Failover(context.TODO(), fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") th.AssertNoErr(t, res.Err) } diff --git a/openstack/loadbalancer/v2/apiversions/testing/requests_test.go b/openstack/loadbalancer/v2/apiversions/testing/requests_test.go index fbca8f9c51..c0e3aa181c 100644 --- a/openstack/loadbalancer/v2/apiversions/testing/requests_test.go +++ b/openstack/loadbalancer/v2/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/apiversions" @@ -14,7 +15,7 @@ func TestListVersions(t *testing.T) { MockListResponse(t) - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersions(allVersions) diff --git a/openstack/loadbalancer/v2/flavorprofiles/requests.go b/openstack/loadbalancer/v2/flavorprofiles/requests.go index 52ed895e73..cb9f848392 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/requests.go +++ b/openstack/loadbalancer/v2/flavorprofiles/requests.go @@ -1,6 +1,8 @@ package flavorprofiles import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -66,20 +68,20 @@ func (opts CreateOpts) ToFlavorProfileCreateMap() (map[string]interface{}, error // 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) { +func Create(ctx context.Context, 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) + resp, err := c.Post(ctx, 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) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -115,13 +117,13 @@ func (opts UpdateOpts) ToFlavorProfileUpdateMap() (map[string]interface{}, error // Update is an operation which modifies the attributes of the specified // FlavorProfile. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(ctx context.Context, 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{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -130,8 +132,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR // 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) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go b/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go index d977387f7e..aebb2c9408 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" @@ -16,7 +17,7 @@ func TestListFlavorProfiles(t *testing.T) { HandleFlavorProfileListSuccessfully(t) pages := 0 - err := flavorprofiles.List(fake.ServiceClient(), flavorprofiles.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := flavorprofiles.List(fake.ServiceClient(), flavorprofiles.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := flavorprofiles.ExtractFlavorProfiles(page) @@ -45,7 +46,7 @@ func TestListAllFlavorProfiles(t *testing.T) { defer th.TeardownHTTP() HandleFlavorProfileListSuccessfully(t) - allPages, err := flavorprofiles.List(fake.ServiceClient(), flavorprofiles.ListOpts{}).AllPages() + allPages, err := flavorprofiles.List(fake.ServiceClient(), flavorprofiles.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := flavorprofiles.ExtractFlavorProfiles(allPages) th.AssertNoErr(t, err) @@ -58,7 +59,7 @@ func TestCreateFlavorProfile(t *testing.T) { defer th.TeardownHTTP() HandleFlavorProfileCreationSuccessfully(t, SingleFlavorProfileBody) - actual, err := flavorprofiles.Create(fake.ServiceClient(), flavorprofiles.CreateOpts{ + actual, err := flavorprofiles.Create(context.TODO(), fake.ServiceClient(), flavorprofiles.CreateOpts{ Name: "amphora-test", ProviderName: "amphora", FlavorData: "{\"loadbalancer_topology\": \"ACTIVE_STANDBY\"}", @@ -69,7 +70,7 @@ func TestCreateFlavorProfile(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := flavorprofiles.Create(fake.ServiceClient(), flavorprofiles.CreateOpts{}) + res := flavorprofiles.Create(context.TODO(), fake.ServiceClient(), flavorprofiles.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -81,7 +82,7 @@ func TestGetFlavorProfiles(t *testing.T) { HandleFlavorProfileGetSuccessfully(t) client := fake.ServiceClient() - actual, err := flavorprofiles.Get(client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272").Extract() + actual, err := flavorprofiles.Get(context.TODO(), client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -94,7 +95,7 @@ func TestDeleteFlavorProfile(t *testing.T) { defer th.TeardownHTTP() HandleFlavorProfileDeletionSuccessfully(t) - res := flavorprofiles.Delete(fake.ServiceClient(), "dcd65be5-f117-4260-ab3d-b32cc5bd1272") + res := flavorprofiles.Delete(context.TODO(), fake.ServiceClient(), "dcd65be5-f117-4260-ab3d-b32cc5bd1272") th.AssertNoErr(t, res.Err) } @@ -104,7 +105,7 @@ func TestUpdateFlavorProfile(t *testing.T) { HandleFlavorProfileUpdateSuccessfully(t) client := fake.ServiceClient() - actual, err := flavorprofiles.Update(client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272", flavorprofiles.UpdateOpts{ + actual, err := flavorprofiles.Update(context.TODO(), client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272", flavorprofiles.UpdateOpts{ Name: "amphora-test-updated", ProviderName: "amphora", FlavorData: "{\"loadbalancer_topology\": \"SINGLE\"}", diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go index 8b5e5e0aeb..833d16873f 100644 --- a/openstack/loadbalancer/v2/flavors/requests.go +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -1,6 +1,8 @@ package flavors import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -70,20 +72,20 @@ func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { // 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) { +func Create(ctx context.Context, 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) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // 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) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -119,13 +121,13 @@ func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { // Update is an operation which modifies the attributes of the specified // Flavor. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(ctx context.Context, 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{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -134,8 +136,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR // 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) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/flavors/testing/requests_test.go b/openstack/loadbalancer/v2/flavors/testing/requests_test.go index 907cd8d696..14a74dbe45 100644 --- a/openstack/loadbalancer/v2/flavors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavors/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" @@ -16,7 +17,7 @@ func TestListFlavors(t *testing.T) { HandleFlavorListSuccessfully(t) pages := 0 - err := flavors.List(fake.ServiceClient(), flavors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := flavors.List(fake.ServiceClient(), flavors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := flavors.ExtractFlavors(page) @@ -45,7 +46,7 @@ func TestListAllFlavors(t *testing.T) { defer th.TeardownHTTP() HandleFlavorListSuccessfully(t) - allPages, err := flavors.List(fake.ServiceClient(), flavors.ListOpts{}).AllPages() + allPages, err := flavors.List(fake.ServiceClient(), flavors.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := flavors.ExtractFlavors(allPages) th.AssertNoErr(t, err) @@ -58,7 +59,7 @@ func TestCreateFlavor(t *testing.T) { defer th.TeardownHTTP() HandleFlavorCreationSuccessfully(t, SingleFlavorBody) - actual, err := flavors.Create(fake.ServiceClient(), flavors.CreateOpts{ + actual, err := flavors.Create(context.TODO(), fake.ServiceClient(), flavors.CreateOpts{ Name: "Basic", Description: "A basic standalone Octavia load balancer.", Enabled: true, @@ -70,7 +71,7 @@ func TestCreateFlavor(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := flavors.Create(fake.ServiceClient(), flavors.CreateOpts{}) + res := flavors.Create(context.TODO(), fake.ServiceClient(), flavors.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -82,7 +83,7 @@ func TestGetFlavor(t *testing.T) { HandleFlavorGetSuccessfully(t) client := fake.ServiceClient() - actual, err := flavors.Get(client, "5548c807-e6e8-43d7-9ea4-b38d34dd74a0").Extract() + actual, err := flavors.Get(context.TODO(), client, "5548c807-e6e8-43d7-9ea4-b38d34dd74a0").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -95,7 +96,7 @@ func TestDeleteFlavor(t *testing.T) { defer th.TeardownHTTP() HandleFlavorDeletionSuccessfully(t) - res := flavors.Delete(fake.ServiceClient(), "5548c807-e6e8-43d7-9ea4-b38d34dd74a0") + res := flavors.Delete(context.TODO(), fake.ServiceClient(), "5548c807-e6e8-43d7-9ea4-b38d34dd74a0") th.AssertNoErr(t, res.Err) } @@ -105,7 +106,7 @@ func TestUpdateFlavor(t *testing.T) { HandleFlavorUpdateSuccessfully(t) client := fake.ServiceClient() - actual, err := flavors.Update(client, "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", flavors.UpdateOpts{ + actual, err := flavors.Update(context.TODO(), client, "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", flavors.UpdateOpts{ Name: "Basic v2", Description: "Rename flavor", Enabled: true, diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index d32335a2ae..fb624010a3 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -1,6 +1,8 @@ package l7policies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -94,13 +96,13 @@ func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]interface{}, error) { } // Create accepts a CreateOpts struct and uses the values to create a new l7policy. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToL7PolicyCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -157,15 +159,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a particular l7policy based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular l7policy based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -245,13 +247,13 @@ func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { } // Update allows l7policy to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToL7PolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -295,13 +297,13 @@ func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { } // CreateRule will create and associate a Rule with a particular L7Policy. -func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) { +func CreateRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + resp, err := c.Post(ctx, ruleRootURL(c, policyID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -356,15 +358,15 @@ func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOpts } // GetRule retrieves a particular L7Policy Rule based on its unique ID. -func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { - resp, err := c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) +func GetRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { + resp, err := c.Get(ctx, ruleResourceURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will remove a Rule from a particular L7Policy. -func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { - resp, err := c.Delete(ruleResourceURL(c, policyID, ruleID), nil) +func DeleteRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { + resp, err := c.Delete(ctx, ruleResourceURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -416,13 +418,13 @@ func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { } // UpdateRule allows Rule to be updated. -func UpdateRule(c *gophercloud.ServiceClient, policyID string, ruleID string, opts UpdateRuleOptsBuilder) (r UpdateRuleResult) { +func UpdateRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string, opts UpdateRuleOptsBuilder) (r UpdateRuleResult) { b, err := opts.ToRuleUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go index 9d2a64a779..328215982d 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/requests_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" @@ -14,7 +15,7 @@ func TestCreateL7Policy(t *testing.T) { defer th.TeardownHTTP() HandleL7PolicyCreationSuccessfully(t, SingleL7PolicyBody) - actual, err := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ + actual, err := l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{ Name: "redirect-example.com", ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: l7policies.ActionRedirectToURL, @@ -27,13 +28,13 @@ func TestCreateL7Policy(t *testing.T) { func TestRequiredL7PolicyCreateOpts(t *testing.T) { // no param specified. - res := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{}) + res := l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } // Action is invalid. - res = l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ + res = l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{ ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: l7policies.Action("invalid"), }) @@ -48,7 +49,7 @@ func TestListL7Policies(t *testing.T) { HandleL7PolicyListSuccessfully(t) pages := 0 - err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := l7policies.ExtractL7Policies(page) @@ -77,7 +78,7 @@ func TestListAllL7Policies(t *testing.T) { defer th.TeardownHTTP() HandleL7PolicyListSuccessfully(t) - allPages, err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).AllPages() + allPages, err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := l7policies.ExtractL7Policies(allPages) th.AssertNoErr(t, err) @@ -91,7 +92,7 @@ func TestGetL7Policy(t *testing.T) { HandleL7PolicyGetSuccessfully(t) client := fake.ServiceClient() - actual, err := l7policies.Get(client, "8a1412f0-4c32-4257-8b07-af4770b604fd").Extract() + actual, err := l7policies.Get(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -104,7 +105,7 @@ func TestDeleteL7Policy(t *testing.T) { defer th.TeardownHTTP() HandleL7PolicyDeletionSuccessfully(t) - res := l7policies.Delete(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") + res := l7policies.Delete(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") th.AssertNoErr(t, res.Err) } @@ -116,7 +117,7 @@ func TestUpdateL7Policy(t *testing.T) { client := fake.ServiceClient() newName := "NewL7PolicyName" redirectURL := "http://www.new-example.com" - actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", + actual, err := l7policies.Update(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, Action: l7policies.ActionRedirectToURL, @@ -137,7 +138,7 @@ func TestUpdateL7PolicyNullRedirectURL(t *testing.T) { client := fake.ServiceClient() newName := "NewL7PolicyName" redirectURL := "" - actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", + actual, err := l7policies.Update(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, RedirectURL: &redirectURL, @@ -153,7 +154,7 @@ func TestUpdateL7PolicyWithInvalidOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - res := l7policies.Update(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ + res := l7policies.Update(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Action: l7policies.Action("invalid"), }) if res.Err == nil { @@ -166,7 +167,7 @@ func TestCreateRule(t *testing.T) { defer th.TeardownHTTP() HandleRuleCreationSuccessfully(t, SingleRuleBody) - actual, err := l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + actual, err := l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images*", @@ -180,17 +181,17 @@ func TestRequiredRuleCreateOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - res := l7policies.CreateRule(fake.ServiceClient(), "", l7policies.CreateRuleOpts{}) + res := l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "", l7policies.CreateRuleOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } - res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.RuleType("invalid"), CompareType: l7policies.CompareTypeRegex, Value: "/images*", @@ -198,7 +199,7 @@ func TestRequiredRuleCreateOpts(t *testing.T) { if res.Err == nil { t.Fatalf("Expected error, but got none") } - res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareType("invalid"), Value: "/images*", @@ -214,7 +215,7 @@ func TestListRules(t *testing.T) { HandleRuleListSuccessfully(t) pages := 0 - err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := l7policies.ExtractRules(page) @@ -243,7 +244,7 @@ func TestListAllRules(t *testing.T) { defer th.TeardownHTTP() HandleRuleListSuccessfully(t) - allPages, err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).AllPages() + allPages, err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := l7policies.ExtractRules(allPages) @@ -258,7 +259,7 @@ func TestGetRule(t *testing.T) { HandleRuleGetSuccessfully(t) client := fake.ServiceClient() - actual, err := l7policies.GetRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c").Extract() + actual, err := l7policies.GetRule(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -271,7 +272,7 @@ func TestDeleteRule(t *testing.T) { defer th.TeardownHTTP() HandleRuleDeletionSuccessfully(t) - res := l7policies.DeleteRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") + res := l7policies.DeleteRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") th.AssertNoErr(t, res.Err) } @@ -283,7 +284,7 @@ func TestUpdateRule(t *testing.T) { client := fake.ServiceClient() invert := false key := "" - actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ + actual, err := l7policies.UpdateRule(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", @@ -301,14 +302,14 @@ func TestUpdateRuleWithInvalidOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - res := l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ + res := l7policies.UpdateRule(context.TODO(), fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ RuleType: l7policies.RuleType("invalid"), }) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ + res = l7policies.UpdateRule(context.TODO(), fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ CompareType: l7policies.CompareType("invalid"), }) if res.Err == nil { diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 669b3ed07b..7d845d1034 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -1,6 +1,8 @@ package listeners import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" @@ -187,20 +189,20 @@ func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { // // Users with an admin role can create Listeners on behalf of other projects by // specifying a ProjectID attribute different than their own. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToListenerCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Listeners based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -276,13 +278,13 @@ func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { // Update is an operation which modifies the attributes of the specified // Listener. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToListenerUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -290,15 +292,15 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR } // Delete will permanently delete a particular Listeners based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular Listeners. -func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { - resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) +func GetStats(ctx context.Context, c *gophercloud.ServiceClient, id string) (r StatsResult) { + resp, err := c.Get(ctx, statisticsRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/listeners/testing/requests_test.go b/openstack/loadbalancer/v2/listeners/testing/requests_test.go index 600d9178d6..220119a43a 100644 --- a/openstack/loadbalancer/v2/listeners/testing/requests_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -16,7 +17,7 @@ func TestListListeners(t *testing.T) { HandleListenerListSuccessfully(t) pages := 0 - err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := listeners.ExtractListeners(page) @@ -45,7 +46,7 @@ func TestListAllListeners(t *testing.T) { defer th.TeardownHTTP() HandleListenerListSuccessfully(t) - allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages() + allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := listeners.ExtractListeners(allPages) th.AssertNoErr(t, err) @@ -58,7 +59,7 @@ func TestCreateListener(t *testing.T) { defer th.TeardownHTTP() HandleListenerCreationSuccessfully(t, SingleListenerBody) - actual, err := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{ + actual, err := listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{ Protocol: "TCP", Name: "db", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", @@ -76,23 +77,23 @@ func TestCreateListener(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{}) + res := listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo"}) + res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar"}) + res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar", Protocol: "bar"}) + res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar", Protocol: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar", Protocol: "bar", ProtocolPort: 80}) + res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", ProjectID: "bar", Protocol: "bar", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -104,7 +105,7 @@ func TestGetListener(t *testing.T) { HandleListenerGetSuccessfully(t) client := fake.ServiceClient() - actual, err := listeners.Get(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() + actual, err := listeners.Get(context.TODO(), client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -117,7 +118,7 @@ func TestDeleteListener(t *testing.T) { defer th.TeardownHTTP() HandleListenerDeletionSuccessfully(t) - res := listeners.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") + res := listeners.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } @@ -136,7 +137,7 @@ func TestUpdateListener(t *testing.T) { "X-Forwarded-Port": "false", } tlsVersions := []listeners.TLSVersion{"TLSv1.2", "TLSv1.3"} - actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ + actual, err := listeners.Update(context.TODO(), client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ Name: &name, ConnLimit: &i1001, DefaultPoolID: &defaultPoolID, @@ -160,7 +161,7 @@ func TestGetListenerStatsTree(t *testing.T) { HandleListenerGetStatsTree(t) client := fake.ServiceClient() - actual, err := listeners.GetStats(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() + actual, err := listeners.GetStats(context.TODO(), client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 577f1c29b7..9b0b32ac6b 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -1,6 +1,8 @@ package loadbalancers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" @@ -155,20 +157,20 @@ func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) // configuration defined in the CreateOpts struct. Once the request is // validated and progress has started on the provisioning process, a // CreateResult will be returned. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLoadBalancerCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Loadbalancer based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -206,13 +208,13 @@ func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) // Update is an operation which modifies the attributes of the specified // LoadBalancer. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { b, err := opts.ToLoadBalancerUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -240,7 +242,7 @@ func (opts DeleteOpts) ToLoadBalancerDeleteQuery() (string, error) { // Delete will permanently delete a particular LoadBalancer based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r DeleteResult) { url := resourceURL(c, id) if opts != nil { query, err := opts.ToLoadBalancerDeleteQuery() @@ -250,28 +252,28 @@ func Delete(c *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r } url += query } - resp, err := c.Delete(url, nil) + resp, err := c.Delete(ctx, url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStatuses will return the status of a particular LoadBalancer. -func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { - resp, err := c.Get(statusRootURL(c, id), &r.Body, nil) +func GetStatuses(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { + resp, err := c.Get(ctx, statusRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular LoadBalancer. -func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { - resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) +func GetStats(ctx context.Context, c *gophercloud.ServiceClient, id string) (r StatsResult) { + resp, err := c.Get(ctx, statisticsRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Failover performs a failover of a load balancer. -func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { - resp, err := c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ +func Failover(ctx context.Context, c *gophercloud.ServiceClient, id string) (r FailoverResult) { + resp, err := c.Put(ctx, failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go index 0a9f2c6149..04fe8549d3 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" @@ -21,7 +22,7 @@ func TestListLoadbalancers(t *testing.T) { HandleLoadbalancerListSuccessfully(t) pages := 0 - err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := loadbalancers.ExtractLoadBalancers(page) @@ -50,7 +51,7 @@ func TestListAllLoadbalancers(t *testing.T) { defer th.TeardownHTTP() HandleLoadbalancerListSuccessfully(t) - allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages() + allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) @@ -63,7 +64,7 @@ func TestCreateLoadbalancer(t *testing.T) { defer th.TeardownHTTP() HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody) - actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{ + actual, err := loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{ Name: "db_lb", AdminStateUp: gophercloud.Enabled, VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", @@ -89,7 +90,7 @@ func TestCreateFullyPopulatedLoadbalancer(t *testing.T) { defer th.TeardownHTTP() HandleFullyPopulatedLoadbalancerCreationSuccessfully(t, PostFullyPopulatedLoadbalancerBody) - actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{ + actual, err := loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{ Name: "db_lb", AdminStateUp: gophercloud.Enabled, VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", @@ -148,7 +149,7 @@ func TestGetLoadbalancer(t *testing.T) { HandleLoadbalancerGetSuccessfully(t) client := fake.ServiceClient() - actual, err := loadbalancers.Get(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + actual, err := loadbalancers.Get(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -162,7 +163,7 @@ func TestGetLoadbalancerStatusesTree(t *testing.T) { HandleLoadbalancerGetStatusesTree(t) client := fake.ServiceClient() - actual, err := loadbalancers.GetStatuses(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + actual, err := loadbalancers.GetStatuses(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -175,7 +176,7 @@ func TestDeleteLoadbalancer(t *testing.T) { defer th.TeardownHTTP() HandleLoadbalancerDeletionSuccessfully(t) - res := loadbalancers.Delete(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", nil) + res := loadbalancers.Delete(context.TODO(), fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", nil) th.AssertNoErr(t, res.Err) } @@ -187,7 +188,7 @@ func TestUpdateLoadbalancer(t *testing.T) { client := fake.ServiceClient() name := "NewLoadbalancerName" tags := []string{"test"} - actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ + actual, err := loadbalancers.Update(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ Name: &name, Tags: &tags, }).Extract() @@ -212,7 +213,7 @@ func TestCascadingDeleteLoadbalancer(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, query, "?cascade=true") - err = loadbalancers.Delete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", deleteOpts).ExtractErr() + err = loadbalancers.Delete(context.TODO(), sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", deleteOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -222,7 +223,7 @@ func TestGetLoadbalancerStatsTree(t *testing.T) { HandleLoadbalancerGetStatsTree(t) client := fake.ServiceClient() - actual, err := loadbalancers.GetStats(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + actual, err := loadbalancers.GetStats(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -235,6 +236,6 @@ func TestFailoverLoadbalancer(t *testing.T) { defer th.TeardownHTTP() HandleLoadbalancerFailoverSuccessfully(t) - res := loadbalancers.Failover(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") + res := loadbalancers.Failover(context.TODO(), fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") th.AssertNoErr(t, res.Err) } diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index 4d3dec2434..f7c3db49b3 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -1,6 +1,7 @@ package monitors import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -167,20 +168,20 @@ 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"} */ -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToMonitorCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Health Monitor based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -240,14 +241,14 @@ func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) { // Update is an operation which modifies the attributes of the specified // Monitor. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToMonitorUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -255,8 +256,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular Monitor based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/monitors/testing/requests_test.go b/openstack/loadbalancer/v2/monitors/testing/requests_test.go index bd8da6aa46..0ca13f360e 100644 --- a/openstack/loadbalancer/v2/monitors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" @@ -15,7 +16,7 @@ func TestListHealthmonitors(t *testing.T) { HandleHealthmonitorListSuccessfully(t) pages := 0 - err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := monitors.ExtractMonitors(page) @@ -44,7 +45,7 @@ func TestListAllHealthmonitors(t *testing.T) { defer th.TeardownHTTP() HandleHealthmonitorListSuccessfully(t) - allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages() + allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := monitors.ExtractMonitors(allPages) th.AssertNoErr(t, err) @@ -57,7 +58,7 @@ func TestCreateHealthmonitor(t *testing.T) { defer th.TeardownHTTP() HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody) - actual, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + actual, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", Name: "db", PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", @@ -76,11 +77,11 @@ func TestCreateHealthmonitor(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{}) + res := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) + res = monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -92,7 +93,7 @@ func TestGetHealthmonitor(t *testing.T) { HandleHealthmonitorGetSuccessfully(t) client := fake.ServiceClient() - actual, err := monitors.Get(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract() + actual, err := monitors.Get(context.TODO(), client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -105,7 +106,7 @@ func TestDeleteHealthmonitor(t *testing.T) { defer th.TeardownHTTP() HandleHealthmonitorDeletionSuccessfully(t) - res := monitors.Delete(fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7") + res := monitors.Delete(context.TODO(), fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7") th.AssertNoErr(t, res.Err) } @@ -116,7 +117,7 @@ func TestUpdateHealthmonitor(t *testing.T) { client := fake.ServiceClient() name := "NewHealthmonitorName" - actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ + actual, err := monitors.Update(context.TODO(), client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ Name: &name, Delay: 3, Timeout: 20, @@ -133,7 +134,7 @@ func TestUpdateHealthmonitor(t *testing.T) { } func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { - _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + _, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", PoolID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d", Delay: 1, @@ -147,7 +148,7 @@ func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { t.Fatalf("Expected error, got none") } - _, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ + _, err = monitors.Update(context.TODO(), fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ Delay: 1, Timeout: 10, }).Extract() diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 494bc9e6f7..d60568a06e 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -1,6 +1,8 @@ package pools import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/monitors" "github.com/gophercloud/gophercloud/v2/pagination" @@ -149,20 +151,20 @@ func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and uses the values to create a new // load balancer pool. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPoolCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -204,13 +206,13 @@ func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) { } // Update allows pools to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPoolUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -218,8 +220,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular pool based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -333,20 +335,20 @@ func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) } // CreateMember will create and associate a Member with a particular Pool. -func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMemberOptsBuilder) (r CreateMemberResult) { +func CreateMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, opts CreateMemberOptsBuilder) (r CreateMemberResult) { b, err := opts.ToMemberCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + resp, err := c.Post(ctx, memberRootURL(c, poolID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMember retrieves a particular Pool Member based on its unique ID. -func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { - resp, err := c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) +func GetMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { + resp, err := c.Get(ctx, memberResourceURL(c, poolID, memberID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -395,13 +397,13 @@ func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) } // Update allows Member to be updated. -func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) { +func UpdateMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) { b, err := opts.ToMemberUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -474,7 +476,7 @@ func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]interface } // BatchUpdateMembers updates the pool members in batch -func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []BatchUpdateMemberOpts) (r UpdateMembersResult) { +func BatchUpdateMembers(ctx context.Context, c *gophercloud.ServiceClient, poolID string, opts []BatchUpdateMemberOpts) (r UpdateMembersResult) { members := []map[string]interface{}{} for _, opt := range opts { b, err := opt.ToBatchMemberUpdateMap() @@ -487,14 +489,14 @@ func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []Batc b := map[string]interface{}{"members": members} - resp, err := c.Put(memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := c.Put(ctx, memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteMember will remove and disassociate a Member from a particular Pool. -func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { - resp, err := c.Delete(memberResourceURL(c, poolID, memberID), nil) +func DeleteMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { + resp, err := c.Delete(ctx, memberResourceURL(c, poolID, memberID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/loadbalancer/v2/pools/testing/requests_test.go b/openstack/loadbalancer/v2/pools/testing/requests_test.go index 35a1ab2658..6e3126329a 100644 --- a/openstack/loadbalancer/v2/pools/testing/requests_test.go +++ b/openstack/loadbalancer/v2/pools/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/pools" @@ -15,7 +16,7 @@ func TestListPools(t *testing.T) { HandlePoolListSuccessfully(t) pages := 0 - err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := pools.ExtractPools(page) @@ -44,7 +45,7 @@ func TestListAllPools(t *testing.T) { defer th.TeardownHTTP() HandlePoolListSuccessfully(t) - allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages() + allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := pools.ExtractPools(allPages) th.AssertNoErr(t, err) @@ -57,7 +58,7 @@ func TestCreatePool(t *testing.T) { defer th.TeardownHTTP() HandlePoolCreationSuccessfully(t, SinglePoolBody) - actual, err := pools.Create(fake.ServiceClient(), pools.CreateOpts{ + actual, err := pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", @@ -75,7 +76,7 @@ func TestGetPool(t *testing.T) { HandlePoolGetSuccessfully(t) client := fake.ServiceClient() - actual, err := pools.Get(client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract() + actual, err := pools.Get(context.TODO(), client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -88,7 +89,7 @@ func TestDeletePool(t *testing.T) { defer th.TeardownHTTP() HandlePoolDeletionSuccessfully(t) - res := pools.Delete(fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab") + res := pools.Delete(context.TODO(), fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab") th.AssertNoErr(t, res.Err) } @@ -99,7 +100,7 @@ func TestUpdatePool(t *testing.T) { client := fake.ServiceClient() name := "NewPoolName" - actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ + actual, err := pools.Update(context.TODO(), client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ Name: &name, LBMethod: pools.LBMethodLeastConnections, }).Extract() @@ -111,11 +112,11 @@ func TestUpdatePool(t *testing.T) { } func TestRequiredPoolCreateOpts(t *testing.T) { - res := pools.Create(fake.ServiceClient(), pools.CreateOpts{}) + res := pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethod("invalid"), Protocol: pools.ProtocolHTTPS, LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", @@ -124,7 +125,7 @@ func TestRequiredPoolCreateOpts(t *testing.T) { t.Fatalf("Expected error, but got none") } - res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: pools.Protocol("invalid"), LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", @@ -133,7 +134,7 @@ func TestRequiredPoolCreateOpts(t *testing.T) { t.Fatalf("Expected error, but got none") } - res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: pools.ProtocolHTTPS, }) @@ -148,7 +149,7 @@ func TestListMembers(t *testing.T) { HandleMemberListSuccessfully(t) pages := 0 - err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := pools.ExtractMembers(page) @@ -177,7 +178,7 @@ func TestListAllMembers(t *testing.T) { defer th.TeardownHTTP() HandleMemberListSuccessfully(t) - allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages() + allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := pools.ExtractMembers(allPages) th.AssertNoErr(t, err) @@ -191,7 +192,7 @@ func TestCreateMember(t *testing.T) { HandleMemberCreationSuccessfully(t, SingleMemberBody) weight := 10 - actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ + actual, err := pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", ProjectID: "2ffc6e22aae24e4795f87155d24c896f", @@ -205,19 +206,19 @@ func TestCreateMember(t *testing.T) { } func TestRequiredMemberCreateOpts(t *testing.T) { - res := pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{}) + res := pools.CreateMember(context.TODO(), fake.ServiceClient(), "", pools.CreateMemberOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80}) + res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, but got none") } - res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80}) + res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, but got none") } - res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"}) + res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"}) if res.Err == nil { t.Fatalf("Expected error, but got none") } @@ -229,7 +230,7 @@ func TestGetMember(t *testing.T) { HandleMemberGetSuccessfully(t) client := fake.ServiceClient() - actual, err := pools.GetMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract() + actual, err := pools.GetMember(context.TODO(), client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -242,7 +243,7 @@ func TestDeleteMember(t *testing.T) { defer th.TeardownHTTP() HandleMemberDeletionSuccessfully(t) - res := pools.DeleteMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf") + res := pools.DeleteMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf") th.AssertNoErr(t, res.Err) } @@ -254,7 +255,7 @@ func TestUpdateMember(t *testing.T) { weight := 4 client := fake.ServiceClient() name := "newMemberName" - actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ + actual, err := pools.UpdateMember(context.TODO(), client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ Name: &name, Weight: &weight, }).Extract() @@ -292,7 +293,7 @@ func TestBatchUpdateMembers(t *testing.T) { } members := []pools.BatchUpdateMemberOpts{member1, member2} - res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", members) + res := pools.BatchUpdateMembers(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", members) th.AssertNoErr(t, res.Err) } @@ -301,7 +302,7 @@ func TestEmptyBatchUpdateMembers(t *testing.T) { defer th.TeardownHTTP() HandleEmptyMembersUpdateSuccessfully(t) - res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{}) + res := pools.BatchUpdateMembers(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{}) th.AssertNoErr(t, res.Err) } @@ -310,7 +311,7 @@ func TestRequiredBatchUpdateMemberOpts(t *testing.T) { defer th.TeardownHTTP() name := "web-server-1" - res := pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ + res := pools.BatchUpdateMembers(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { Name: &name, }, @@ -319,7 +320,7 @@ func TestRequiredBatchUpdateMemberOpts(t *testing.T) { t.Fatalf("Expected error, but got none") } - res = pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ + res = pools.BatchUpdateMembers(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { Address: "192.0.2.17", Name: &name, @@ -329,7 +330,7 @@ func TestRequiredBatchUpdateMemberOpts(t *testing.T) { t.Fatalf("Expected error, but got none") } - res = pools.BatchUpdateMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ + res = pools.BatchUpdateMembers(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", []pools.BatchUpdateMemberOpts{ { ProtocolPort: 80, Name: &name, diff --git a/openstack/loadbalancer/v2/providers/testing/requests_test.go b/openstack/loadbalancer/v2/providers/testing/requests_test.go index a831f828b2..c5a5278aae 100644 --- a/openstack/loadbalancer/v2/providers/testing/requests_test.go +++ b/openstack/loadbalancer/v2/providers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/providers" @@ -15,7 +16,7 @@ func TestListProviders(t *testing.T) { HandleProviderListSuccessfully(t) pages := 0 - err := providers.List(fake.ServiceClient(), providers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := providers.List(fake.ServiceClient(), providers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := providers.ExtractProviders(page) @@ -44,7 +45,7 @@ func TestListAllProviders(t *testing.T) { defer th.TeardownHTTP() HandleProviderListSuccessfully(t) - allPages, err := providers.List(fake.ServiceClient(), providers.ListOpts{}).AllPages() + allPages, err := providers.List(fake.ServiceClient(), providers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := providers.ExtractProviders(allPages) th.AssertNoErr(t, err) diff --git a/openstack/loadbalancer/v2/quotas/requests.go b/openstack/loadbalancer/v2/quotas/requests.go index 01696d1055..1169785be7 100644 --- a/openstack/loadbalancer/v2/quotas/requests.go +++ b/openstack/loadbalancer/v2/quotas/requests.go @@ -1,12 +1,14 @@ package quotas import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // Get returns load balancer Quotas for a project. -func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { - resp, err := client.Get(getURL(client, projectID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, projectID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -48,13 +50,13 @@ func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]interface{}, error) { // Update accepts a UpdateOpts struct and updates an existing load balancer Quotas using the // values provided. -func Update(c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToQuotaUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ // allow 200 (neutron/lbaasv2) and 202 (octavia) OkCodes: []int{200, 202}, }) diff --git a/openstack/loadbalancer/v2/quotas/testing/requests_test.go b/openstack/loadbalancer/v2/quotas/testing/requests_test.go index fe2b44bb3f..64c819aecf 100644 --- a/openstack/loadbalancer/v2/quotas/testing/requests_test.go +++ b/openstack/loadbalancer/v2/quotas/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -25,7 +26,7 @@ func TestGet_1(t *testing.T) { fmt.Fprintf(w, GetResponseRaw_1) }) - q, err := quotas.Get(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + q, err := quotas.Get(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &GetResponse) } @@ -44,7 +45,7 @@ func TestGet_2(t *testing.T) { fmt.Fprintf(w, GetResponseRaw_2) }) - q, err := quotas.Get(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + q, err := quotas.Get(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &GetResponse) } @@ -63,7 +64,7 @@ func TestUpdate_1(t *testing.T) { fmt.Fprintf(w, UpdateRequestResponseRaw_1) }) - q, err := quotas.Update(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ + q, err := quotas.Update(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ Loadbalancer: gophercloud.IntToPointer(20), Listener: gophercloud.IntToPointer(40), Member: gophercloud.IntToPointer(200), @@ -91,7 +92,7 @@ func TestUpdate_2(t *testing.T) { fmt.Fprintf(w, UpdateRequestResponseRaw_2) }) - q, err := quotas.Update(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ + q, err := quotas.Update(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ Loadbalancer: gophercloud.IntToPointer(20), Listener: gophercloud.IntToPointer(40), Member: gophercloud.IntToPointer(200), diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index 91f019adfa..c216b1b7fd 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -1,6 +1,8 @@ package claims import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) @@ -38,7 +40,7 @@ func (opts CreateOpts) ToClaimCreateRequest() (map[string]interface{}, string, e } // Create creates a Claim that claims messages on a specified queue. -func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, queueName string, opts CreateOptsBuilder) (r CreateResult) { b, q, err := opts.ToClaimCreateRequest() if err != nil { r.Err = err @@ -50,7 +52,7 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts url += q } - resp, err := client.Post(url, b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, url, b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -58,8 +60,8 @@ func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOpts } // Get queries the specified claim for the specified queue. -func Get(client *gophercloud.ServiceClient, queueName string, claimID string) (r GetResult) { - resp, err := client.Get(getURL(client, queueName, claimID), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, queueName string, claimID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, queueName, claimID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -92,13 +94,13 @@ func (opts UpdateOpts) ToClaimUpdateMap() (map[string]interface{}, error) { } // Update will update the options for a specified claim. -func Update(client *gophercloud.ServiceClient, queueName string, claimID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, queueName string, claimID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToClaimUpdateMap() if err != nil { r.Err = err return r } - resp, err := client.Patch(updateURL(client, queueName, claimID), &b, nil, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, queueName, claimID), &b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -106,8 +108,8 @@ func Update(client *gophercloud.ServiceClient, queueName string, claimID string, } // Delete will delete a Claim for a specified Queue. -func Delete(client *gophercloud.ServiceClient, queueName string, claimID string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, queueName, claimID), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, queueName string, claimID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, queueName, claimID), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/messaging/v2/claims/testing/requests_test.go b/openstack/messaging/v2/claims/testing/requests_test.go index 7a271c61f9..8040796601 100644 --- a/openstack/messaging/v2/claims/testing/requests_test.go +++ b/openstack/messaging/v2/claims/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/claims" @@ -19,7 +20,7 @@ func TestCreate(t *testing.T) { Limit: 10, } - actual, err := claims.Create(fake.ServiceClient(), QueueName, createOpts).Extract() + actual, err := claims.Create(context.TODO(), fake.ServiceClient(), QueueName, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, CreatedClaim, actual) } @@ -35,7 +36,7 @@ func TestCreateNoContent(t *testing.T) { Limit: 10, } - actual, err := claims.Create(fake.ServiceClient(), QueueName, createOpts).Extract() + actual, err := claims.Create(context.TODO(), fake.ServiceClient(), QueueName, createOpts).Extract() th.AssertNoErr(t, err) var expected []claims.Messages th.CheckDeepEquals(t, expected, actual) @@ -46,7 +47,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := claims.Get(fake.ServiceClient(), QueueName, ClaimID).Extract() + actual, err := claims.Get(context.TODO(), fake.ServiceClient(), QueueName, ClaimID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &FirstClaim, actual) } @@ -61,7 +62,7 @@ func TestUpdate(t *testing.T) { TTL: 1200, } - err := claims.Update(fake.ServiceClient(), QueueName, ClaimID, updateOpts).ExtractErr() + err := claims.Update(context.TODO(), fake.ServiceClient(), QueueName, ClaimID, updateOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -70,6 +71,6 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := claims.Delete(fake.ServiceClient(), QueueName, ClaimID).ExtractErr() + err := claims.Delete(context.TODO(), fake.ServiceClient(), QueueName, ClaimID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index 9286e96934..221cb0b3ac 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -1,6 +1,8 @@ package messages import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -91,14 +93,14 @@ func (opts CreateOpts) ToMap() (map[string]interface{}, error) { } // Create creates a message on a specific queue based of off queue name. -func Create(client *gophercloud.ServiceClient, queueName string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, queueName string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToMessageCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -123,7 +125,7 @@ func (opts DeleteMessagesOpts) ToMessagesDeleteQuery() (string, error) { } // DeleteMessages deletes multiple messages based off of ID. -func DeleteMessages(client *gophercloud.ServiceClient, queueName string, opts DeleteMessagesOptsBuilder) (r DeleteResult) { +func DeleteMessages(ctx context.Context, client *gophercloud.ServiceClient, queueName string, opts DeleteMessagesOptsBuilder) (r DeleteResult) { url := deleteURL(client, queueName) if opts != nil { query, err := opts.ToMessagesDeleteQuery() @@ -133,7 +135,7 @@ func DeleteMessages(client *gophercloud.ServiceClient, queueName string, opts De } url += query } - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ OkCodes: []int{200, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -158,7 +160,7 @@ func (opts PopMessagesOpts) ToMessagesPopQuery() (string, error) { } // PopMessages deletes and returns multiple messages based off of number of messages. -func PopMessages(client *gophercloud.ServiceClient, queueName string, opts PopMessagesOptsBuilder) (r PopResult) { +func PopMessages(ctx context.Context, client *gophercloud.ServiceClient, queueName string, opts PopMessagesOptsBuilder) (r PopResult) { url := deleteURL(client, queueName) if opts != nil { query, err := opts.ToMessagesPopQuery() @@ -168,7 +170,7 @@ func PopMessages(client *gophercloud.ServiceClient, queueName string, opts PopMe } url += query } - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ JSONResponse: &r.Body, OkCodes: []int{200, 204}, }) @@ -194,7 +196,7 @@ func (opts GetMessagesOpts) ToGetMessagesListQuery() (string, error) { } // GetMessages requests details on a multiple messages, by IDs. -func GetMessages(client *gophercloud.ServiceClient, queueName string, opts GetMessagesOptsBuilder) (r GetMessagesResult) { +func GetMessages(ctx context.Context, client *gophercloud.ServiceClient, queueName string, opts GetMessagesOptsBuilder) (r GetMessagesResult) { url := getURL(client, queueName) if opts != nil { query, err := opts.ToGetMessagesListQuery() @@ -204,7 +206,7 @@ func GetMessages(client *gophercloud.ServiceClient, queueName string, opts GetMe } url += query } - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -212,8 +214,8 @@ func GetMessages(client *gophercloud.ServiceClient, queueName string, opts GetMe } // Get requests details on a single message, by ID. -func Get(client *gophercloud.ServiceClient, queueName string, messageID string) (r GetResult) { - resp, err := client.Get(messageURL(client, queueName, messageID), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, queueName string, messageID string) (r GetResult) { + resp, err := client.Get(ctx, messageURL(client, queueName, messageID), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -239,7 +241,7 @@ func (opts DeleteOpts) ToMessageDeleteQuery() (string, error) { } // Delete deletes a specific message from the queue. -func Delete(client *gophercloud.ServiceClient, queueName string, messageID string, opts DeleteOptsBuilder) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, queueName string, messageID string, opts DeleteOptsBuilder) (r DeleteResult) { url := DeleteMessageURL(client, queueName, messageID) if opts != nil { query, err := opts.ToMessageDeleteQuery() @@ -249,7 +251,7 @@ func Delete(client *gophercloud.ServiceClient, queueName string, messageID strin } url += query } - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go index e415dbafe3..0bcc73c7c7 100644 --- a/openstack/messaging/v2/messages/testing/requests_test.go +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/messages" @@ -19,7 +20,7 @@ func TestList(t *testing.T) { } count := 0 - err := messages.List(fake.ServiceClient(), QueueName, listOpts).EachPage(func(page pagination.Page) (bool, error) { + err := messages.List(fake.ServiceClient(), QueueName, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := messages.ExtractMessages(page) th.AssertNoErr(t, err) @@ -56,7 +57,7 @@ func TestCreate(t *testing.T) { }, } - actual, err := messages.Create(fake.ServiceClient(), QueueName, createOpts).Extract() + actual, err := messages.Create(context.TODO(), fake.ServiceClient(), QueueName, createOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedResources, actual) } @@ -70,7 +71,7 @@ func TestGetMessages(t *testing.T) { IDs: []string{"9988776655"}, } - actual, err := messages.GetMessages(fake.ServiceClient(), QueueName, getMessagesOpts).Extract() + actual, err := messages.GetMessages(context.TODO(), fake.ServiceClient(), QueueName, getMessagesOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedMessagesSet, actual) } @@ -80,7 +81,7 @@ func TestGetMessage(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := messages.Get(fake.ServiceClient(), QueueName, MessageID).Extract() + actual, err := messages.Get(context.TODO(), fake.ServiceClient(), QueueName, MessageID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, FirstMessage, actual) } @@ -94,7 +95,7 @@ func TestDeleteMessages(t *testing.T) { IDs: []string{"9988776655"}, } - err := messages.DeleteMessages(fake.ServiceClient(), QueueName, deleteMessagesOpts).ExtractErr() + err := messages.DeleteMessages(context.TODO(), fake.ServiceClient(), QueueName, deleteMessagesOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -107,7 +108,7 @@ func TestPopMessages(t *testing.T) { Pop: 1, } - actual, err := messages.PopMessages(fake.ServiceClient(), QueueName, popMessagesOpts).Extract() + actual, err := messages.PopMessages(context.TODO(), fake.ServiceClient(), QueueName, popMessagesOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedPopMessage, actual) } @@ -121,6 +122,6 @@ func TestDeleteMessage(t *testing.T) { ClaimID: "12345", } - err := messages.Delete(fake.ServiceClient(), QueueName, MessageID, deleteOpts).ExtractErr() + err := messages.Delete(context.TODO(), fake.ServiceClient(), QueueName, MessageID, deleteOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index 9bc02f8327..c82e43e56b 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -1,6 +1,8 @@ package queues import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -115,7 +117,7 @@ func (opts CreateOpts) ToQueueCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new queue. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToQueueCreateMap() if err != nil { r.Err = err @@ -125,7 +127,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create queueName := b["queue_name"].(string) delete(b, "queue_name") - resp, err := client.Put(createURL(client, queueName), b, r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, createURL(client, queueName), b, r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -175,8 +177,8 @@ func (opts UpdateOpts) ToMap() (map[string]interface{}, error) { } // Update Updates the specified queue. -func Update(client *gophercloud.ServiceClient, queueName string, opts UpdateOptsBuilder) (r UpdateResult) { - resp, err := client.Patch(updateURL(client, queueName), opts, &r.Body, &gophercloud.RequestOpts{ +func Update(ctx context.Context, client *gophercloud.ServiceClient, queueName string, opts UpdateOptsBuilder) (r UpdateResult) { + resp, err := client.Patch(ctx, updateURL(client, queueName), opts, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 204}, MoreHeaders: map[string]string{ "Content-Type": "application/openstack-messaging-v2.0-json-patch"}, @@ -186,8 +188,8 @@ func Update(client *gophercloud.ServiceClient, queueName string, opts UpdateOpts } // Get requests details on a single queue, by name. -func Get(client *gophercloud.ServiceClient, queueName string) (r GetResult) { - resp, err := client.Get(getURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ +func Get(ctx context.Context, client *gophercloud.ServiceClient, queueName string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -195,8 +197,8 @@ func Get(client *gophercloud.ServiceClient, queueName string) (r GetResult) { } // Delete deletes the specified queue. -func Delete(client *gophercloud.ServiceClient, queueName string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, queueName), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, queueName string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, queueName), &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -204,8 +206,8 @@ func Delete(client *gophercloud.ServiceClient, queueName string) (r DeleteResult } // GetStats returns statistics for the specified queue. -func GetStats(client *gophercloud.ServiceClient, queueName string) (r StatResult) { - resp, err := client.Get(statURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ +func GetStats(ctx context.Context, client *gophercloud.ServiceClient, queueName string) (r StatResult) { + resp, err := client.Get(ctx, statURL(client, queueName), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -252,13 +254,13 @@ func (opts ShareOpts) ToQueueShareMap() (map[string]interface{}, error) { } // Share creates a pre-signed URL for a given queue. -func Share(client *gophercloud.ServiceClient, queueName string, opts ShareOptsBuilder) (r ShareResult) { +func Share(ctx context.Context, client *gophercloud.ServiceClient, queueName string, opts ShareOptsBuilder) (r ShareResult) { b, err := opts.ToQueueShareMap() if err != nil { r.Err = err return r } - resp, err := client.Post(shareURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, shareURL(client, queueName), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -294,14 +296,14 @@ func (opts PurgeOpts) ToQueuePurgeMap() (map[string]interface{}, error) { } // Purge purges particular resource of the queue. -func Purge(client *gophercloud.ServiceClient, queueName string, opts PurgeOptsBuilder) (r PurgeResult) { +func Purge(ctx context.Context, client *gophercloud.ServiceClient, queueName string, opts PurgeOptsBuilder) (r PurgeResult) { b, err := opts.ToQueuePurgeMap() if err != nil { r.Err = err return r } - resp, err := client.Post(purgeURL(client, queueName), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, purgeURL(client, queueName), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index 6597e8a953..3acd118eb3 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/queues" @@ -20,7 +21,7 @@ func TestList(t *testing.T) { } count := 0 - err := queues.List(fake.ServiceClient(), listOpts).EachPage(func(page pagination.Page) (bool, error) { + err := queues.List(fake.ServiceClient(), listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := queues.ExtractQueues(page) th.AssertNoErr(t, err) countField, err := page.(queues.QueuePage).GetCount() @@ -55,7 +56,7 @@ func TestCreate(t *testing.T) { Extra: map[string]interface{}{"description": "Queue for unit testing."}, } - err := queues.Create(fake.ServiceClient(), createOpts).ExtractErr() + err := queues.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -75,7 +76,7 @@ func TestUpdate(t *testing.T) { Extra: map[string]interface{}{"description": "Update queue description"}, } - actual, err := queues.Update(fake.ServiceClient(), QueueName, updateOpts).Extract() + actual, err := queues.Update(context.TODO(), fake.ServiceClient(), QueueName, updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, updatedQueueResult, actual) } @@ -85,7 +86,7 @@ func TestGet(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t) - actual, err := queues.Get(fake.ServiceClient(), QueueName).Extract() + actual, err := queues.Get(context.TODO(), fake.ServiceClient(), QueueName).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, QueueDetails, actual) } @@ -95,7 +96,7 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := queues.Delete(fake.ServiceClient(), QueueName).ExtractErr() + err := queues.Delete(context.TODO(), fake.ServiceClient(), QueueName).ExtractErr() th.AssertNoErr(t, err) } @@ -104,7 +105,7 @@ func TestGetStat(t *testing.T) { defer th.TeardownHTTP() HandleGetStatsSuccessfully(t) - actual, err := queues.GetStats(fake.ServiceClient(), QueueName).Extract() + actual, err := queues.GetStats(context.TODO(), fake.ServiceClient(), QueueName).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedStats, actual) } @@ -120,7 +121,7 @@ func TestShare(t *testing.T) { Expires: "2016-09-01T00:00:00", } - actual, err := queues.Share(fake.ServiceClient(), QueueName, shareOpts).Extract() + actual, err := queues.Share(context.TODO(), fake.ServiceClient(), QueueName, shareOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedShare, actual) } @@ -134,6 +135,6 @@ func TestPurge(t *testing.T) { ResourceTypes: []queues.PurgeResource{queues.ResourceMessages, queues.ResourceSubscriptions}, } - err := queues.Purge(fake.ServiceClient(), QueueName, purgeOpts).ExtractErr() + err := queues.Purge(context.TODO(), fake.ServiceClient(), QueueName, purgeOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/networking/v2/apiversions/testing/requests_test.go b/openstack/networking/v2/apiversions/testing/requests_test.go index 4f94f974d9..7f6f482883 100644 --- a/openstack/networking/v2/apiversions/testing/requests_test.go +++ b/openstack/networking/v2/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -41,7 +42,7 @@ func TestListVersions(t *testing.T) { count := 0 - apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractAPIVersions(page) if err != nil { @@ -74,7 +75,7 @@ func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) { w.WriteHeader(http.StatusOK) }) - apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { if _, err := apiversions.ExtractAPIVersions(page); err == nil { t.Fatalf("Expected error, got nil") } @@ -133,7 +134,7 @@ func TestAPIInfo(t *testing.T) { count := 0 - apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) { + apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractVersionResources(page) if err != nil { @@ -174,7 +175,7 @@ func TestNonJSONCannotBeExtractedIntoAPIVersionResources(t *testing.T) { w.WriteHeader(http.StatusOK) }) - apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(func(page pagination.Page) (bool, error) { + apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { if _, err := apiversions.ExtractVersionResources(page); err == nil { t.Fatalf("Expected error, got nil") } diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index d3852c8a72..7b78647736 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -1,6 +1,8 @@ package agents import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -60,8 +62,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific agent based on its ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -84,13 +86,13 @@ func (opts UpdateOpts) ToAgentUpdateMap() (map[string]interface{}, error) { } // Update updates a specific agent based on its ID. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToAgentUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -98,16 +100,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete deletes a specific agent based on its ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(getURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, getURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListDHCPNetworks returns a list of networks scheduled to a specific // dhcp agent. -func ListDHCPNetworks(c *gophercloud.ServiceClient, id string) (r ListDHCPNetworksResult) { - resp, err := c.Get(listDHCPNetworksURL(c, id), &r.Body, nil) +func ListDHCPNetworks(ctx context.Context, c *gophercloud.ServiceClient, id string) (r ListDHCPNetworksResult) { + resp, err := c.Get(ctx, listDHCPNetworksURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -130,13 +132,13 @@ func (opts ScheduleDHCPNetworkOpts) ToAgentScheduleDHCPNetworkMap() (map[string] } // ScheduleDHCPNetwork schedule a network to a DHCP agent. -func ScheduleDHCPNetwork(c *gophercloud.ServiceClient, id string, opts ScheduleDHCPNetworkOptsBuilder) (r ScheduleDHCPNetworkResult) { +func ScheduleDHCPNetwork(ctx context.Context, c *gophercloud.ServiceClient, id string, opts ScheduleDHCPNetworkOptsBuilder) (r ScheduleDHCPNetworkResult) { b, err := opts.ToAgentScheduleDHCPNetworkMap() if err != nil { r.Err = err return } - resp, err := c.Post(scheduleDHCPNetworkURL(c, id), b, nil, &gophercloud.RequestOpts{ + resp, err := c.Post(ctx, scheduleDHCPNetworkURL(c, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -144,8 +146,8 @@ func ScheduleDHCPNetwork(c *gophercloud.ServiceClient, id string, opts ScheduleD } // RemoveDHCPNetwork removes a network from a DHCP agent. -func RemoveDHCPNetwork(c *gophercloud.ServiceClient, id string, networkID string) (r RemoveDHCPNetworkResult) { - resp, err := c.Delete(removeDHCPNetworkURL(c, id, networkID), nil) +func RemoveDHCPNetwork(ctx context.Context, c *gophercloud.ServiceClient, id string, networkID string) (r RemoveDHCPNetworkResult) { + resp, err := c.Delete(ctx, removeDHCPNetworkURL(c, id, networkID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -176,13 +178,13 @@ func (opts ScheduleBGPSpeakerOpts) ToAgentScheduleBGPSpeakerMap() (map[string]in // 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) { +func ScheduleBGPSpeaker(ctx context.Context, 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{ + resp, err := c.Post(ctx, scheduleBGPSpeakersURL(c, agentID), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -191,8 +193,8 @@ func ScheduleBGPSpeaker(c *gophercloud.ServiceClient, agentID string, opts Sched // 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) +func RemoveBGPSpeaker(ctx context.Context, c *gophercloud.ServiceClient, agentID string, speakerID string) (r RemoveBGPSpeakerResult) { + resp, err := c.Delete(ctx, removeBGPSpeakersURL(c, agentID, speakerID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -208,8 +210,8 @@ func ListDRAgentHostingBGPSpeakers(c *gophercloud.ServiceClient, bgpSpeakerID st // 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) +func ListL3Routers(ctx context.Context, c *gophercloud.ServiceClient, id string) (r ListL3RoutersResult) { + resp, err := c.Get(ctx, listL3RoutersURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -232,13 +234,13 @@ func (opts ScheduleL3RouterOpts) ToAgentScheduleL3RouterMap() (map[string]interf } // ScheduleL3Router schedule a router to a L3 agent. -func ScheduleL3Router(c *gophercloud.ServiceClient, id string, opts ScheduleL3RouterOptsBuilder) (r ScheduleL3RouterResult) { +func ScheduleL3Router(ctx context.Context, 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{ + resp, err := c.Post(ctx, scheduleL3RouterURL(c, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -246,8 +248,8 @@ func ScheduleL3Router(c *gophercloud.ServiceClient, id string, opts ScheduleL3Ro } // 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) +func RemoveL3Router(ctx context.Context, c *gophercloud.ServiceClient, id string, routerID string) (r RemoveL3RouterResult) { + resp, err := c.Delete(ctx, removeL3RouterURL(c, id, routerID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index 806910660b..2b2a9b9b8f 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -29,7 +30,7 @@ func TestList(t *testing.T) { count := 0 - agents.List(fake.ServiceClient(), agents.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + agents.List(fake.ServiceClient(), agents.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := agents.ExtractAgents(page) @@ -67,7 +68,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, AgentsGetResult) }) - s, err := agents.Get(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() + s, err := agents.Get(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.ID, "43583cf5-472e-4dc8-af5b-6aed4c94ee3a") @@ -113,7 +114,7 @@ func TestUpdate(t *testing.T) { Description: &description, AdminStateUp: &iTrue, } - s, err := agents.Update(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", updateOpts).Extract() + s, err := agents.Update(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, *s, Agent) @@ -132,7 +133,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := agents.Delete(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").ExtractErr() + err := agents.Delete(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").ExtractErr() th.AssertNoErr(t, err) } @@ -150,7 +151,7 @@ func TestListDHCPNetworks(t *testing.T) { fmt.Fprintf(w, AgentDHCPNetworksListResult) }) - s, err := agents.ListDHCPNetworks(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() + s, err := agents.ListDHCPNetworks(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() th.AssertNoErr(t, err) var nilSlice []string @@ -186,7 +187,7 @@ func TestScheduleDHCPNetwork(t *testing.T) { opts := &agents.ScheduleDHCPNetworkOpts{ NetworkID: "1ae075ca-708b-4e66-b4a7-b7698632f05f", } - err := agents.ScheduleDHCPNetwork(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", opts).ExtractErr() + err := agents.ScheduleDHCPNetwork(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", opts).ExtractErr() th.AssertNoErr(t, err) } @@ -203,7 +204,7 @@ func TestRemoveDHCPNetwork(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := agents.RemoveDHCPNetwork(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "1ae075ca-708b-4e66-b4a7-b7698632f05f").ExtractErr() + err := agents.RemoveDHCPNetwork(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "1ae075ca-708b-4e66-b4a7-b7698632f05f").ExtractErr() th.AssertNoErr(t, err) } @@ -226,7 +227,8 @@ func TestListBGPSpeakers(t *testing.T) { count := 0 agents.ListBGPSpeakers(fake.ServiceClient(), agentID).EachPage( - func(page pagination.Page) (bool, error) { + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := agents.ExtractBGPSpeakers(page) @@ -264,7 +266,7 @@ func TestScheduleBGPSpeaker(t *testing.T) { var opts agents.ScheduleBGPSpeakerOpts opts.SpeakerID = speakerID - err := agents.ScheduleBGPSpeaker(fake.ServiceClient(), agentID, opts).ExtractErr() + err := agents.ScheduleBGPSpeaker(context.TODO(), fake.ServiceClient(), agentID, opts).ExtractErr() th.AssertNoErr(t, err) } @@ -285,7 +287,7 @@ func TestRemoveBGPSpeaker(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := agents.RemoveBGPSpeaker(fake.ServiceClient(), agentID, speakerID).ExtractErr() + err := agents.RemoveBGPSpeaker(context.TODO(), fake.ServiceClient(), agentID, speakerID).ExtractErr() th.AssertNoErr(t, err) } @@ -306,7 +308,8 @@ func TestListDRAgentHostingBGPSpeakers(t *testing.T) { count := 0 agents.ListDRAgentHostingBGPSpeakers(fake.ServiceClient(), speakerID).EachPage( - func(page pagination.Page) (bool, error) { + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := agents.ExtractAgents(page) @@ -339,7 +342,7 @@ func TestListL3Routers(t *testing.T) { fmt.Fprintf(w, AgentL3RoutersListResult) }) - s, err := agents.ListL3Routers(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() + s, err := agents.ListL3Routers(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() th.AssertNoErr(t, err) routes := []routers.Route{ @@ -401,7 +404,7 @@ func TestScheduleL3Router(t *testing.T) { opts := &agents.ScheduleL3RouterOpts{ RouterID: "43e66290-79a4-415d-9eb9-7ff7919839e1", } - err := agents.ScheduleL3Router(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", opts).ExtractErr() + err := agents.ScheduleL3Router(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", opts).ExtractErr() th.AssertNoErr(t, err) } @@ -418,6 +421,6 @@ func TestRemoveL3Router(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := agents.RemoveL3Router(fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "43e66290-79a4-415d-9eb9-7ff7919839e1").ExtractErr() + err := agents.RemoveL3Router(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a", "43e66290-79a4-415d-9eb9-7ff7919839e1").ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index c083dc9170..5a58783d8a 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -1,6 +1,8 @@ package attributestags import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) @@ -22,14 +24,14 @@ func (opts ReplaceAllOpts) ToAttributeTagsReplaceAllMap() (map[string]interface{ } // ReplaceAll updates all tags on a resource, replacing any existing tags -func ReplaceAll(client *gophercloud.ServiceClient, resourceType string, resourceID string, opts ReplaceAllOptsBuilder) (r ReplaceAllResult) { +func ReplaceAll(ctx context.Context, client *gophercloud.ServiceClient, resourceType string, resourceID string, opts ReplaceAllOptsBuilder) (r ReplaceAllResult) { b, err := opts.ToAttributeTagsReplaceAllMap() url := replaceURL(client, resourceType, resourceID) if err != nil { r.Err = err return } - resp, err := client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, url, &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -37,9 +39,9 @@ func ReplaceAll(client *gophercloud.ServiceClient, resourceType string, resource } // List all tags on a resource -func List(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r ListResult) { +func List(ctx context.Context, client *gophercloud.ServiceClient, resourceType string, resourceID string) (r ListResult) { url := listURL(client, resourceType, resourceID) - resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -47,9 +49,9 @@ func List(client *gophercloud.ServiceClient, resourceType string, resourceID str } // DeleteAll deletes all tags on a resource -func DeleteAll(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r DeleteResult) { +func DeleteAll(ctx context.Context, client *gophercloud.ServiceClient, resourceType string, resourceID string) (r DeleteResult) { url := deleteAllURL(client, resourceType, resourceID) - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -57,9 +59,9 @@ func DeleteAll(client *gophercloud.ServiceClient, resourceType string, resourceI } // Add a tag on a resource -func Add(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r AddResult) { +func Add(ctx context.Context, client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r AddResult) { url := addURL(client, resourceType, resourceID, tag) - resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -67,9 +69,9 @@ func Add(client *gophercloud.ServiceClient, resourceType string, resourceID stri } // Delete a tag on a resource -func Delete(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r DeleteResult) { +func Delete(ctx context.Context, client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r DeleteResult) { url := deleteURL(client, resourceType, resourceID, tag) - resp, err := client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -77,9 +79,9 @@ func Delete(client *gophercloud.ServiceClient, resourceType string, resourceID s } // Confirm if a tag exists on a resource -func Confirm(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r ConfirmResult) { +func Confirm(ctx context.Context, client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r ConfirmResult) { url := confirmURL(client, resourceType, resourceID, tag) - resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(ctx, url, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go index 48fc55b5f6..e9dc3498fb 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -30,7 +31,7 @@ func TestReplaceAll(t *testing.T) { opts := attributestags.ReplaceAllOpts{ Tags: []string{"abc", "xyz"}, } - res, err := attributestags.ReplaceAll(fake.ServiceClient(), "networks", "fakeid", opts).Extract() + res, err := attributestags.ReplaceAll(context.TODO(), fake.ServiceClient(), "networks", "fakeid", opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, res, []string{"abc", "xyz"}) @@ -50,7 +51,7 @@ func TestList(t *testing.T) { fmt.Fprintf(w, attributestagsListResult) }) - res, err := attributestags.List(fake.ServiceClient(), "networks", "fakeid").Extract() + res, err := attributestags.List(context.TODO(), fake.ServiceClient(), "networks", "fakeid").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, res, []string{"abc", "xyz"}) @@ -68,7 +69,7 @@ func TestDeleteAll(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := attributestags.DeleteAll(fake.ServiceClient(), "networks", "fakeid").ExtractErr() + err := attributestags.DeleteAll(context.TODO(), fake.ServiceClient(), "networks", "fakeid").ExtractErr() th.AssertNoErr(t, err) } @@ -84,7 +85,7 @@ func TestAdd(t *testing.T) { w.WriteHeader(http.StatusCreated) }) - err := attributestags.Add(fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() + err := attributestags.Add(context.TODO(), fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() th.AssertNoErr(t, err) } @@ -100,7 +101,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := attributestags.Delete(fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() + err := attributestags.Delete(context.TODO(), fake.ServiceClient(), "networks", "fakeid", "atag").ExtractErr() th.AssertNoErr(t, err) } @@ -116,7 +117,7 @@ func TestConfirmTrue(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - exists, err := attributestags.Confirm(fake.ServiceClient(), "networks", "fakeid", "atag").Extract() + exists, err := attributestags.Confirm(context.TODO(), fake.ServiceClient(), "networks", "fakeid", "atag").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) } @@ -133,6 +134,6 @@ func TestConfirmFalse(t *testing.T) { w.WriteHeader(http.StatusNotFound) }) - exists, _ := attributestags.Confirm(fake.ServiceClient(), "networks", "fakeid", "atag").Extract() + exists, _ := attributestags.Confirm(context.TODO(), fake.ServiceClient(), "networks", "fakeid", "atag").Extract() th.AssertEquals(t, false, exists) } diff --git a/openstack/networking/v2/extensions/bgp/peers/requests.go b/openstack/networking/v2/extensions/bgp/peers/requests.go index ef2b5e0b83..0bf5844c76 100644 --- a/openstack/networking/v2/extensions/bgp/peers/requests.go +++ b/openstack/networking/v2/extensions/bgp/peers/requests.go @@ -1,6 +1,8 @@ package peers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -14,8 +16,8 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { } // 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) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -41,20 +43,20 @@ func (opts CreateOpts) ToPeerCreateMap() (map[string]interface{}, error) { } // Create a BGP Peer -func Create(c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { +func Create(ctx context.Context, 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) + resp, err := c.Post(ctx, 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) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, bgpPeerID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, bgpPeerID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -77,13 +79,13 @@ func (opts UpdateOpts) ToPeerUpdateMap() (map[string]interface{}, error) { } // 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) { +func Update(ctx context.Context, 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{ + resp, err := c.Put(ctx, updateURL(c, bgpPeerID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 7e14e5c65e..e07f40db15 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -27,7 +28,8 @@ func TestList(t *testing.T) { count := 0 peers.List(fake.ServiceClient()).EachPage( - func(page pagination.Page) (bool, error) { + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := peers.ExtractBGPPeers(page) @@ -54,7 +56,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, GetBGPPeerResult) }) - s, err := peers.Get(fake.ServiceClient(), bgpPeerID).Extract() + s, err := peers.Get(context.TODO(), fake.ServiceClient(), bgpPeerID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, *s, BGPPeer1) } @@ -81,7 +83,7 @@ func TestCreate(t *testing.T) { opts.Name = "gophercloud-testing-bgp-peer" opts.PeerIP = "192.168.0.1" - r, err := peers.Create(fake.ServiceClient(), opts).Extract() + r, err := peers.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, r.AuthType, opts.AuthType) th.AssertEquals(t, r.RemoteAS, opts.RemoteAS) @@ -102,7 +104,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := peers.Delete(fake.ServiceClient(), bgpPeerID).ExtractErr() + err := peers.Delete(context.TODO(), fake.ServiceClient(), bgpPeerID).ExtractErr() th.AssertNoErr(t, err) } @@ -128,7 +130,7 @@ func TestUpdate(t *testing.T) { opts.Name = "test-rename-bgp-peer" opts.Password = "superStrong" - r, err := peers.Update(fake.ServiceClient(), bgpPeerID, opts).Extract() + r, err := peers.Update(context.TODO(), fake.ServiceClient(), bgpPeerID, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, r.Name, opts.Name) } diff --git a/openstack/networking/v2/extensions/bgp/speakers/requests.go b/openstack/networking/v2/extensions/bgp/speakers/requests.go index ca86ec90d9..ff3c88e7dd 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/requests.go +++ b/openstack/networking/v2/extensions/bgp/speakers/requests.go @@ -1,6 +1,8 @@ package speakers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -14,8 +16,8 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { } // Get retrieve the specific bgp speaker by its uuid -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -41,20 +43,20 @@ func (opts CreateOpts) ToSpeakerCreateMap() (map[string]interface{}, error) { } // Create accepts a CreateOpts and create a BGP Speaker. -func Create(c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { +func Create(ctx context.Context, 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) + resp, err := c.Post(ctx, 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) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, speakerID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, speakerID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -78,13 +80,13 @@ type UpdateOptsBuilder interface { } // Update accepts a UpdateOpts and update the BGP Speaker. -func Update(c *gophercloud.ServiceClient, speakerID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, 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{ + resp, err := c.Put(ctx, updateURL(c, speakerID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -107,13 +109,13 @@ func (opts AddBGPPeerOpts) ToBGPSpeakerAddBGPPeerMap() (map[string]interface{}, } // 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) { +func AddBGPPeer(ctx context.Context, 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{ + resp, err := c.Put(ctx, addBGPPeerURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -134,13 +136,13 @@ func (opts RemoveBGPPeerOpts) ToBGPSpeakerRemoveBGPPeerMap() (map[string]interfa } // 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) { +func RemoveBGPPeer(ctx context.Context, 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{ + resp, err := c.Put(ctx, removeBGPPeerURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -172,13 +174,13 @@ func (opts AddGatewayNetworkOpts) ToBGPSpeakerAddGatewayNetworkMap() (map[string } // 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) { +func AddGatewayNetwork(ctx context.Context, 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{ + resp, err := c.Put(ctx, addGatewayNetworkURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -199,13 +201,13 @@ func (opts RemoveGatewayNetworkOpts) ToBGPSpeakerRemoveGatewayNetworkMap() (map[ } // 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) { +func RemoveGatewayNetwork(ctx context.Context, 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{ + resp, err := c.Put(ctx, removeGatewayNetworkURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 71b22edd56..911c328d2f 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "io" "net/http" @@ -28,7 +29,8 @@ func TestList(t *testing.T) { count := 0 speakers.List(fake.ServiceClient()).EachPage( - func(page pagination.Page) (bool, error) { + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := speakers.ExtractBGPSpeakers(page) @@ -55,7 +57,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, GetBGPSpeakerResult) }) - s, err := speakers.Get(fake.ServiceClient(), bgpSpeakerID).Extract() + s, err := speakers.Get(context.TODO(), fake.ServiceClient(), bgpSpeakerID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, *s, BGPSpeaker1) } @@ -83,7 +85,7 @@ func TestCreate(t *testing.T) { LocalAS: "2000", Networks: []string{}, } - r, err := speakers.Create(fake.ServiceClient(), opts).Extract() + r, err := speakers.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, r.Name, opts.Name) th.AssertEquals(t, r.LocalAS, 2000) @@ -107,7 +109,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - err := speakers.Delete(fake.ServiceClient(), bgpSpeakerID).ExtractErr() + err := speakers.Delete(context.TODO(), fake.ServiceClient(), bgpSpeakerID).ExtractErr() th.AssertNoErr(t, err) } @@ -144,7 +146,7 @@ func TestUpdate(t *testing.T) { AdvertiseFloatingIPHostRoutes: true, } - r, err := speakers.Update(fake.ServiceClient(), bgpSpeakerID, opts).Extract() + r, err := speakers.Update(context.TODO(), fake.ServiceClient(), bgpSpeakerID, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, r.Name, opts.Name) th.AssertEquals(t, r.AdvertiseTenantNetworks, opts.AdvertiseTenantNetworks) @@ -170,7 +172,7 @@ func TestAddBGPPeer(t *testing.T) { }) opts := speakers.AddBGPPeerOpts{BGPPeerID: bgpPeerID} - r, err := speakers.AddBGPPeer(fake.ServiceClient(), bgpSpeakerID, opts).Extract() + r, err := speakers.AddBGPPeer(context.TODO(), fake.ServiceClient(), bgpSpeakerID, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, bgpPeerID, r.BGPPeerID) } @@ -191,7 +193,7 @@ func TestRemoveBGPPeer(t *testing.T) { }) opts := speakers.RemoveBGPPeerOpts{BGPPeerID: bgpPeerID} - err := speakers.RemoveBGPPeer(fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() + err := speakers.RemoveBGPPeer(context.TODO(), fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() th.AssertEquals(t, err, io.EOF) } @@ -210,7 +212,8 @@ func TestGetAdvertisedRoutes(t *testing.T) { count := 0 speakers.GetAdvertisedRoutes(fake.ServiceClient(), bgpSpeakerID).EachPage( - func(page pagination.Page) (bool, error) { + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := speakers.ExtractAdvertisedRoutes(page) @@ -249,7 +252,7 @@ func TestAddGatewayNetwork(t *testing.T) { }) opts := speakers.AddGatewayNetworkOpts{NetworkID: networkID} - r, err := speakers.AddGatewayNetwork(fake.ServiceClient(), bgpSpeakerID, opts).Extract() + r, err := speakers.AddGatewayNetwork(context.TODO(), fake.ServiceClient(), bgpSpeakerID, opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, r.NetworkID, networkID) } @@ -273,6 +276,6 @@ func TestRemoveGatewayNetwork(t *testing.T) { }) opts := speakers.RemoveGatewayNetworkOpts{NetworkID: networkID} - err := speakers.RemoveGatewayNetwork(fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() + err := speakers.RemoveGatewayNetwork(context.TODO(), fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() th.AssertEquals(t, err, io.EOF) } diff --git a/openstack/networking/v2/extensions/delegate.go b/openstack/networking/v2/extensions/delegate.go index 9dbb521073..870403a35b 100644 --- a/openstack/networking/v2/extensions/delegate.go +++ b/openstack/networking/v2/extensions/delegate.go @@ -1,6 +1,8 @@ package extensions import ( + "context" + "github.com/gophercloud/gophercloud/v2" common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/pagination" @@ -30,8 +32,8 @@ func ExtractExtensions(page pagination.Page) ([]Extension, error) { } // Get retrieves information for a specific extension using its alias. -func Get(c *gophercloud.ServiceClient, alias string) GetResult { - return GetResult{common.Get(c, alias)} +func Get(ctx context.Context, c *gophercloud.ServiceClient, alias string) GetResult { + return GetResult{common.Get(ctx, c, alias)} } // List returns a Pager which allows you to iterate over the full collection of extensions. diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go index 191d8ad038..0f6c99c1a2 100644 --- a/openstack/networking/v2/extensions/dns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/dns/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -79,7 +80,7 @@ func TestPortList(t *testing.T) { DNSName: "test-port", } - allPages, err := ports.List(fake.ServiceClient(), listOptsBuilder).AllPages() + allPages, err := ports.List(fake.ServiceClient(), listOptsBuilder).AllPages(context.TODO()) th.AssertNoErr(t, err) err = ports.ExtractPortsInto(allPages, &actual) @@ -96,7 +97,7 @@ func TestPortGet(t *testing.T) { var s PortDNS - err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) + err := ports.Get(context.TODO(), fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "ACTIVE") @@ -147,7 +148,7 @@ func TestPortCreate(t *testing.T) { DNSName: "test-port", } - err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s) + err := ports.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "DOWN") @@ -174,7 +175,7 @@ func TestPortCreate(t *testing.T) { } func TestPortRequiredCreateOpts(t *testing.T) { - res := ports.Create(fake.ServiceClient(), dns.PortCreateOptsExt{CreateOptsBuilder: ports.CreateOpts{}}) + res := ports.Create(context.TODO(), fake.ServiceClient(), dns.PortCreateOptsExt{CreateOptsBuilder: ports.CreateOpts{}}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -203,7 +204,7 @@ func TestPortUpdate(t *testing.T) { DNSName: &dnsName, } - err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) + err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") @@ -228,7 +229,7 @@ func TestFloatingIPGet(t *testing.T) { FloatingIPHandleGet(t) var actual FloatingIPDNS - err := floatingips.Get(fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").ExtractInto(&actual) + err := floatingips.Get(context.TODO(), fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").ExtractInto(&actual) th.AssertNoErr(t, err) expected := FloatingIPDNS{ @@ -271,7 +272,7 @@ func TestFloatingIPCreate(t *testing.T) { DNSDomain: "local.", } - err := floatingips.Create(fake.ServiceClient(), options).ExtractInto(&actual) + err := floatingips.Create(context.TODO(), fake.ServiceClient(), options).ExtractInto(&actual) th.AssertNoErr(t, err) expected := FloatingIPDNS{ @@ -304,7 +305,7 @@ func TestNetworkGet(t *testing.T) { var actual NetworkDNS - err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&actual) + err := networks.Get(context.TODO(), fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&actual) th.AssertNoErr(t, err) expected := NetworkDNS{ @@ -342,7 +343,7 @@ func TestNetworkCreate(t *testing.T) { DNSDomain: "local.", } - err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&actual) + err := networks.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractInto(&actual) th.AssertNoErr(t, err) expected := NetworkDNS{ @@ -380,7 +381,7 @@ func TestNetworkUpdate(t *testing.T) { DNSDomain: new(string), } - err := networks.Update(fake.ServiceClient(), "db193ab3-96e3-4cb3-8fc5-05f4296d0324", updateOpts).ExtractInto(&actual) + err := networks.Update(context.TODO(), fake.ServiceClient(), "db193ab3-96e3-4cb3-8fc5-05f4296d0324", updateOpts).ExtractInto(&actual) th.AssertNoErr(t, err) expected := NetworkDNS{ diff --git a/openstack/networking/v2/extensions/external/testing/results_test.go b/openstack/networking/v2/extensions/external/testing/results_test.go index 5cdf8a50cf..12062e53d8 100644 --- a/openstack/networking/v2/extensions/external/testing/results_test.go +++ b/openstack/networking/v2/extensions/external/testing/results_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -32,7 +33,7 @@ func TestList(t *testing.T) { } var actual []NetworkWithExternalExt - allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() + allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &actual) @@ -61,7 +62,7 @@ func TestGet(t *testing.T) { external.NetworkExternalExt } - err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + err := networks.Get(context.TODO(), fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", s.ID) @@ -97,7 +98,7 @@ func TestCreate(t *testing.T) { External: &iFalse, } - _, err := networks.Create(fake.ServiceClient(), externalCreateOpts).Extract() + _, err := networks.Create(context.TODO(), fake.ServiceClient(), externalCreateOpts).Extract() th.AssertNoErr(t, err) th.AssertNoErr(t, err) @@ -134,6 +135,6 @@ func TestUpdate(t *testing.T) { External: &iFalse, } - _, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", externalUpdateOpts).Extract() + _, err := networks.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", externalUpdateOpts).Extract() th.AssertNoErr(t, err) } diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go index 1c3419b5f2..28b3ea725e 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go @@ -1,6 +1,8 @@ package firewalls import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -83,20 +85,20 @@ func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) { } // Create accepts a CreateOpts struct and uses the values to create a new firewall. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFirewallCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -122,13 +124,13 @@ func (opts UpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) { } // Update allows firewalls to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFirewallUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -136,8 +138,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular firewall based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go index ced5e46b23..66b60c9b60 100644 --- a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -52,7 +53,7 @@ func TestList(t *testing.T) { count := 0 - firewalls.List(fake.ServiceClient(), firewalls.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + firewalls.List(fake.ServiceClient(), firewalls.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := firewalls.ExtractFirewalls(page) if err != nil { @@ -134,7 +135,7 @@ func TestListWithExtensions(t *testing.T) { routerinsertion.FirewallExt } - allPages, err := firewalls.List(fake.ServiceClient(), nil).AllPages() + allPages, err := firewalls.List(fake.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) var actual []FirewallsWithExt @@ -190,7 +191,7 @@ func TestCreate(t *testing.T) { AdminStateUp: gophercloud.Enabled, PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } - _, err := firewalls.Create(fake.ServiceClient(), options).Extract() + _, err := firewalls.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -220,7 +221,7 @@ func TestGet(t *testing.T) { `) }) - fw, err := firewalls.Get(fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").Extract() + fw, err := firewalls.Get(context.TODO(), fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "ACTIVE", fw.Status) @@ -264,7 +265,7 @@ func TestGetWithExtensions(t *testing.T) { routerinsertion.FirewallExt } - err := firewalls.Get(fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").ExtractInto(&fw) + err := firewalls.Get(context.TODO(), fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").ExtractInto(&fw) th.AssertNoErr(t, err) th.AssertEquals(t, "ACTIVE", fw.Status) @@ -324,7 +325,7 @@ func TestUpdate(t *testing.T) { PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", } - _, err := firewalls.Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", options).Extract() + _, err := firewalls.Update(context.TODO(), fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", options).Extract() th.AssertNoErr(t, err) } @@ -338,6 +339,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := firewalls.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") + res := firewalls.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go index 0af024d3c8..8146e142de 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas/policies/requests.go @@ -1,6 +1,8 @@ package policies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -83,20 +85,20 @@ func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, erro // Create accepts a CreateOpts struct and uses the values to create a new // firewall policy. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFirewallPolicyCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall policy based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -122,13 +124,13 @@ func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]interface{}, erro } // Update allows firewall policies to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFirewallPolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -137,8 +139,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r // Delete will permanently delete a particular firewall policy based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -161,13 +163,13 @@ func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface } // AddRule will add a rule to a policy. -func AddRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) { +func AddRule(ctx context.Context, c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) { b, err := opts.ToFirewallPolicyInsertRuleMap() if err != nil { r.Err = err return } - resp, err := c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -175,9 +177,9 @@ func AddRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder } // RemoveRule will add a rule to a policy. -func RemoveRule(c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { +func RemoveRule(ctx context.Context, c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { b := map[string]interface{}{"firewall_rule_id": ruleID} - resp, err := c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go index c40d2f458a..b0bf291cad 100644 --- a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -56,7 +57,7 @@ func TestList(t *testing.T) { count := 0 - policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := policies.ExtractPolicies(page) if err != nil { @@ -157,7 +158,7 @@ func TestCreate(t *testing.T) { }, } - _, err := policies.Create(fake.ServiceClient(), options).Extract() + _, err := policies.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -190,7 +191,7 @@ func TestGet(t *testing.T) { `) }) - policy, err := policies.Get(fake.ServiceClient(), "bcab5315-64f6-4ea3-8e58-981cc37c6f61").Extract() + policy, err := policies.Get(context.TODO(), fake.ServiceClient(), "bcab5315-64f6-4ea3-8e58-981cc37c6f61").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "www", policy.Name) @@ -257,7 +258,7 @@ func TestUpdate(t *testing.T) { }, } - _, err := policies.Update(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract() + _, err := policies.Update(context.TODO(), fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract() th.AssertNoErr(t, err) } @@ -271,6 +272,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := policies.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") + res := policies.Delete(context.TODO(), fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go index 42a60b7003..9afee9ed5d 100644 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -65,7 +66,7 @@ func TestCreate(t *testing.T) { RouterIDs: []string{"8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"}, } - _, err := firewalls.Create(fake.ServiceClient(), createOpts).Extract() + _, err := firewalls.Create(context.TODO(), fake.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) } @@ -120,7 +121,7 @@ func TestCreateWithNoRouters(t *testing.T) { RouterIDs: []string{}, } - _, err := firewalls.Create(fake.ServiceClient(), createOpts).Extract() + _, err := firewalls.Create(context.TODO(), fake.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) } @@ -178,7 +179,7 @@ func TestUpdate(t *testing.T) { RouterIDs: []string{"8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"}, } - _, err := firewalls.Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract() + _, err := firewalls.Update(context.TODO(), fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract() th.AssertNoErr(t, err) } @@ -234,6 +235,6 @@ func TestUpdateWithNoRouters(t *testing.T) { RouterIDs: []string{}, } - _, err := firewalls.Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract() + _, err := firewalls.Update(context.TODO(), fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract() th.AssertNoErr(t, err) } diff --git a/openstack/networking/v2/extensions/fwaas/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go index 8daa4f13f9..fd6e1c459f 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas/rules/requests.go @@ -1,6 +1,8 @@ package rules import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -125,20 +127,20 @@ func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and uses the values to create a new // firewall rule. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall rule based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -172,13 +174,13 @@ func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) { } // Update allows firewall policies to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRuleUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -187,8 +189,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r // Delete will permanently delete a particular firewall rule based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go index ce2cc97d15..114483ebbe 100644 --- a/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -67,7 +68,7 @@ func TestList(t *testing.T) { count := 0 - rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractRules(page) if err != nil { @@ -181,7 +182,7 @@ func TestCreate(t *testing.T) { Action: "allow", } - _, err := rules.Create(fake.ServiceClient(), options).Extract() + _, err := rules.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -242,7 +243,7 @@ func TestCreateAnyProtocol(t *testing.T) { Action: "allow", } - _, err := rules.Create(fake.ServiceClient(), options).Extract() + _, err := rules.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -280,7 +281,7 @@ func TestGet(t *testing.T) { `) }) - rule, err := rules.Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract() + rule, err := rules.Get(context.TODO(), fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "tcp", rule.Protocol) @@ -362,7 +363,7 @@ func TestUpdate(t *testing.T) { Enabled: gophercloud.Disabled, } - _, err := rules.Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract() + _, err := rules.Update(context.TODO(), fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract() th.AssertNoErr(t, err) } @@ -376,6 +377,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := rules.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") + res := rules.Delete(context.TODO(), fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go index 580dfab6ae..20f6b8aab8 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go @@ -1,6 +1,8 @@ package groups import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -66,8 +68,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a particular firewall group based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(ctx, resourceURL(c, id), &r.Body, nil) return } @@ -99,13 +101,13 @@ func (opts CreateOpts) ToFirewallGroupCreateMap() (map[string]interface{}, error } // Create accepts a CreateOpts struct and uses the values to create a new firewall group -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFirewallGroupCreateMap() if err != nil { r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + _, r.Err = c.Post(ctx, rootURL(c), b, &r.Body, nil) return } @@ -134,13 +136,13 @@ func (opts UpdateOpts) ToFirewallGroupUpdateMap() (map[string]interface{}, error } // Update allows firewall groups to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFirewallGroupUpdateMap() if err != nil { r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return @@ -157,13 +159,13 @@ type RemoveIngressPolicyOpts struct { IngressFirewallPolicyID *string `json:"ingress_firewall_policy_id"` } -func RemoveIngressPolicy(c *gophercloud.ServiceClient, id string) (r UpdateResult) { +func RemoveIngressPolicy(ctx context.Context, 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{ + _, r.Err = c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return @@ -173,20 +175,20 @@ type RemoveEgressPolicyOpts struct { EgressFirewallPolicyID *string `json:"egress_firewall_policy_id"` } -func RemoveEgressPolicy(c *gophercloud.ServiceClient, id string) (r UpdateResult) { +func RemoveEgressPolicy(ctx context.Context, 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{ + _, r.Err = c.Put(ctx, 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) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(ctx, resourceURL(c, id), nil) return } 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 da719d1a47..6d40203746 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 @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -67,7 +68,7 @@ func TestList(t *testing.T) { count := 0 - groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := groups.ExtractGroups(page) if err != nil { @@ -155,7 +156,7 @@ func TestGet(t *testing.T) { `) }) - group, err := groups.Get(fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428").Extract() + group, err := groups.Get(context.TODO(), fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", group.ID) @@ -228,7 +229,7 @@ func TestCreate(t *testing.T) { }, } - _, err := groups.Create(fake.ServiceClient(), options).Extract() + _, err := groups.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -293,7 +294,7 @@ func TestUpdate(t *testing.T) { AdminStateUp: &adminStateUp, } - _, err := groups.Update(fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", options).Extract() + _, err := groups.Update(context.TODO(), fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", options).Extract() th.AssertNoErr(t, err) } @@ -339,7 +340,7 @@ func TestRemoveIngressPolicy(t *testing.T) { `) }) - removeIngressPolicy, err := groups.RemoveIngressPolicy(fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428").Extract() + removeIngressPolicy, err := groups.RemoveIngressPolicy(context.TODO(), 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") @@ -387,7 +388,7 @@ func TestRemoveEgressPolicy(t *testing.T) { `) }) - removeEgressPolicy, err := groups.RemoveEgressPolicy(fake.ServiceClient(), "6bfb0f10-07f7-4a40-b534-bad4b4ca3428").Extract() + removeEgressPolicy, err := groups.RemoveEgressPolicy(context.TODO(), 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, "") @@ -403,6 +404,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := groups.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") + res := groups.Delete(context.TODO(), fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go index abb468971c..e908249b00 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go @@ -1,6 +1,8 @@ package policies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -83,19 +85,19 @@ func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, erro } // Create accepts a CreateOpts struct and uses the values to create a new firewall policy -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFirewallPolicyCreateMap() if err != nil { r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + _, r.Err = c.Post(ctx, rootURL(c), b, &r.Body, nil) return } // Get retrieves a particular firewall policy based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = c.Get(ctx, resourceURL(c, id), &r.Body, nil) return } @@ -122,21 +124,21 @@ func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]interface{}, erro } // Update allows firewall policies to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFirewallPolicyUpdateMap() if err != nil { r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } // Delete will permanently delete a particular firewall policy based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = c.Delete(ctx, resourceURL(c, id), nil) return } @@ -154,21 +156,21 @@ func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface return gophercloud.BuildRequestBody(opts, "") } -func InsertRule(c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) { +func InsertRule(ctx context.Context, c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) { b, err := opts.ToFirewallPolicyInsertRuleMap() if err != nil { r.Err = err return } - _, r.Err = c.Put(insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = c.Put(ctx, insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return } -func RemoveRule(c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { +func RemoveRule(ctx context.Context, c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { b := map[string]interface{}{"firewall_rule_id": ruleID} - _, r.Err = c.Put(removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + _, r.Err = c.Put(ctx, removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) return 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 203de039fe..af03dccaeb 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 @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -58,7 +59,7 @@ func TestList(t *testing.T) { count := 0 - policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := policies.ExtractPolicies(page) if err != nil { @@ -164,7 +165,7 @@ func TestCreate(t *testing.T) { }, } - _, err := policies.Create(fake.ServiceClient(), options).Extract() + _, err := policies.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -209,7 +210,7 @@ func TestInsertRule(t *testing.T) { InsertBefore: "3062ed90-1fb0-4c25-af3d-318dff2143ae", } - policy, err := policies.InsertRule(fake.ServiceClient(), "e3c78ab6-e827-4297-8d68-739063865a8b", options).Extract() + policy, err := policies.InsertRule(context.TODO(), fake.ServiceClient(), "e3c78ab6-e827-4297-8d68-739063865a8b", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "TESTACC-2LnMayeG", policy.Name) th.AssertEquals(t, 2, len(policy.Rules)) @@ -232,7 +233,7 @@ func TestInsertRuleWithInvalidParameters(t *testing.T) { InsertAfter: "2", } - _, err := policies.InsertRule(fake.ServiceClient(), "0", options).Extract() + _, err := policies.InsertRule(context.TODO(), fake.ServiceClient(), "0", options).Extract() // expect to fail with an gophercloud error th.AssertErr(t, err) @@ -269,7 +270,7 @@ func TestGet(t *testing.T) { `) }) - policy, err := policies.Get(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + policy, err := policies.Get(context.TODO(), fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "www", policy.Name) @@ -339,7 +340,7 @@ func TestUpdate(t *testing.T) { }, } - _, err := policies.Update(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract() + _, err := policies.Update(context.TODO(), fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract() th.AssertNoErr(t, err) } @@ -353,7 +354,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := policies.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") + res := policies.Delete(context.TODO(), fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } @@ -391,7 +392,7 @@ func TestRemoveRule(t *testing.T) { `) }) - policy, err := policies.RemoveRule(fake.ServiceClient(), "9fed8075-06ee-463f-83a6-d4118791b02f", "9fed8075-06ee-463f-83a6-d4118791b02f").Extract() + policy, err := policies.RemoveRule(context.TODO(), fake.ServiceClient(), "9fed8075-06ee-463f-83a6-d4118791b02f", "9fed8075-06ee-463f-83a6-d4118791b02f").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "9fed8075-06ee-463f-83a6-d4118791b02f", policy.ID) diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index 206c1744cd..cd47062252 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -1,6 +1,8 @@ package rules import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -144,20 +146,20 @@ func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { } // Create accepts a CreateOpts struct and uses the values to create a new firewall rule -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall rule based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -191,13 +193,13 @@ func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) { } // Update allows firewall policies to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRuleUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -205,8 +207,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular firewall rule based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas_v2/rules/testing/requests_test.go index cc28e83489..5f40b5844b 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 @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -67,7 +68,7 @@ func TestList(t *testing.T) { count := 0 - rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractRules(page) if err != nil { @@ -183,7 +184,7 @@ func TestCreate(t *testing.T) { Action: "allow", } - _, err := rules.Create(fake.ServiceClient(), options).Extract() + _, err := rules.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -247,7 +248,7 @@ func TestCreateAnyProtocol(t *testing.T) { Action: "allow", } - _, err := rules.Create(fake.ServiceClient(), options).Extract() + _, err := rules.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -286,7 +287,7 @@ func TestGet(t *testing.T) { `) }) - rule, err := rules.Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract() + rule, err := rules.Get(context.TODO(), fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "tcp", rule.Protocol) @@ -370,7 +371,7 @@ func TestUpdate(t *testing.T) { Enabled: gophercloud.Disabled, } - _, err := rules.Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract() + _, err := rules.Update(context.TODO(), fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract() th.AssertNoErr(t, err) } @@ -384,6 +385,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := rules.Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") + res := rules.Delete(context.TODO(), fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go index cf7221b859..918d42cf12 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go @@ -1,6 +1,8 @@ package addressscopes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -59,8 +61,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific address-scope based on its ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -95,13 +97,13 @@ func (opts CreateOpts) ToAddressScopeCreateMap() (map[string]interface{}, error) } // Create requests the creation of a new address-scope on the server. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToAddressScopeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -130,13 +132,13 @@ func (opts UpdateOpts) ToAddressScopeUpdateMap() (map[string]interface{}, error) // Update accepts a UpdateOpts struct and updates an existing address-scope // using the values provided. -func Update(c *gophercloud.ServiceClient, addressScopeID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, addressScopeID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToAddressScopeUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, addressScopeID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, addressScopeID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -144,8 +146,8 @@ func Update(c *gophercloud.ServiceClient, addressScopeID string, opts UpdateOpts } // Delete accepts a unique ID and deletes the address-scope associated with it. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go index f1d0484f7e..05e5a85174 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -27,7 +28,7 @@ func TestList(t *testing.T) { count := 0 - addressscopes.List(fake.ServiceClient(), addressscopes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + addressscopes.List(fake.ServiceClient(), addressscopes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := addressscopes.ExtractAddressScopes(page) if err != nil { @@ -64,7 +65,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, AddressScopesGetResult) }) - s, err := addressscopes.Get(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e").Extract() + s, err := addressscopes.Get(context.TODO(), fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.ID, "9cc35860-522a-4d35-974d-51d4b011801e") @@ -97,7 +98,7 @@ func TestCreate(t *testing.T) { Shared: true, Name: "test0", } - s, err := addressscopes.Create(fake.ServiceClient(), opts).Extract() + s, err := addressscopes.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "test0") @@ -131,7 +132,7 @@ func TestUpdate(t *testing.T) { Name: &newName, Shared: &shared, } - s, err := addressscopes.Update(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e", updateOpts).Extract() + s, err := addressscopes.Update(context.TODO(), fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "test1") @@ -148,6 +149,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := addressscopes.Delete(fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e") + res := addressscopes.Delete(context.TODO(), fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/requests.go b/openstack/networking/v2/extensions/layer3/extraroutes/requests.go index 501955b031..8e7229047b 100644 --- a/openstack/networking/v2/extensions/layer3/extraroutes/requests.go +++ b/openstack/networking/v2/extensions/layer3/extraroutes/requests.go @@ -1,6 +1,8 @@ package extraroutes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" ) @@ -23,13 +25,13 @@ func (opts Opts) ToExtraRoutesUpdateMap() (map[string]interface{}, error) { } // 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) { +func Add(ctx context.Context, 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{ + resp, err := c.Put(ctx, addExtraRoutesURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -37,13 +39,13 @@ func Add(c *gophercloud.ServiceClient, id string, opts OptsBuilder) (r AddResult } // 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) { +func Remove(ctx context.Context, 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{ + resp, err := c.Put(ctx, removeExtraRoutesURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go index fefe1a2c00..66d08c8898 100644 --- a/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -62,7 +63,7 @@ func TestAddExtraRoutes(t *testing.T) { } options := extraroutes.Opts{Routes: &r} - n, err := extraroutes.Add(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + n, err := extraroutes.Add(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, n.Routes, []routers.Route{ @@ -134,7 +135,7 @@ func TestRemoveExtraRoutes(t *testing.T) { } options := extraroutes.Opts{Routes: &r} - n, err := extraroutes.Remove(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + n, err := extraroutes.Remove(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, n.Routes, []routers.Route{ diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 0fbbcad239..d14dcfb6bd 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -1,6 +1,8 @@ package floatingips import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -110,20 +112,20 @@ func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { // operation will fail and return a 400 error code. If the PortID and FixedIP // are already associated with another resource, the operation will fail and // returns a 409 error code. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFloatingIPCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular floating IP resource based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -163,13 +165,13 @@ func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) { // "update" a floating IP is to associate it with a new internal port, or // disassociated it from all ports. See UpdateOpts for instructions of how to // do this. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToFloatingIPUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -179,8 +181,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r // Delete will permanently delete a particular floating IP resource. Please // ensure this is what you want - you can also disassociate the IP from existing // internal ports. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index 60fb2ad88f..5ac2828806 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -28,7 +29,7 @@ func TestList(t *testing.T) { count := 0 - err := floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := floatingips.ExtractFloatingIPs(page) if err != nil { @@ -90,19 +91,19 @@ func TestInvalidNextPageURLs(t *testing.T) { fmt.Fprintf(w, `{"floatingips": [{}], "floatingips_links": {}}`) }) - floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { floatingips.ExtractFloatingIPs(page) return true, nil }) } func TestRequiredFieldsForCreate(t *testing.T) { - res1 := floatingips.Create(fake.ServiceClient(), floatingips.CreateOpts{FloatingNetworkID: ""}) + res1 := floatingips.Create(context.TODO(), fake.ServiceClient(), floatingips.CreateOpts{FloatingNetworkID: ""}) if res1.Err == nil { t.Fatalf("Expected error, got none") } - res2 := floatingips.Create(fake.ServiceClient(), floatingips.CreateOpts{FloatingNetworkID: "foo", PortID: ""}) + res2 := floatingips.Create(context.TODO(), fake.ServiceClient(), floatingips.CreateOpts{FloatingNetworkID: "foo", PortID: ""}) if res2.Err == nil { t.Fatalf("Expected error, got none") } @@ -151,7 +152,7 @@ func TestCreate(t *testing.T) { PortID: "ce705c24-c1ef-408a-bda3-7bbd946164ab", } - ip, err := floatingips.Create(fake.ServiceClient(), options).Extract() + ip, err := floatingips.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) @@ -204,7 +205,7 @@ func TestCreateEmptyPort(t *testing.T) { FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57", } - ip, err := floatingips.Create(fake.ServiceClient(), options).Extract() + ip, err := floatingips.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) @@ -260,7 +261,7 @@ func TestCreateWithSubnetID(t *testing.T) { SubnetID: "37adf01c-24db-467a-b845-7ab1e8216c01", } - ip, err := floatingips.Create(fake.ServiceClient(), options).Extract() + ip, err := floatingips.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID) @@ -302,7 +303,7 @@ func TestGet(t *testing.T) { `) }) - ip, err := floatingips.Get(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7").Extract() + ip, err := floatingips.Get(context.TODO(), fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "90f742b1-6d17-487b-ba95-71881dbc0b64", ip.FloatingNetworkID) @@ -353,7 +354,7 @@ func TestAssociate(t *testing.T) { }) portID := "423abc8d-2991-4a55-ba98-2aaea84cc72e" - ip, err := floatingips.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: &portID}).Extract() + ip, err := floatingips.Update(context.TODO(), fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: &portID}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, portID, ip.PortID) @@ -394,7 +395,7 @@ func TestDisassociate(t *testing.T) { `) }) - ip, err := floatingips.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: new(string)}).Extract() + ip, err := floatingips.Update(context.TODO(), fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", floatingips.UpdateOpts{PortID: new(string)}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, "", ip.FixedIP) @@ -411,6 +412,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := floatingips.Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7") + res := floatingips.Delete(context.TODO(), fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index af919a586e..95a0a37283 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -1,6 +1,8 @@ package portforwarding import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -52,8 +54,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder, id string) paginat } // Get retrieves a particular port forwarding resource based on its unique ID. -func Get(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r GetResult) { - resp, err := c.Get(singlePortForwardingUrl(c, floatingIpId, pfId), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r GetResult) { + resp, err := c.Get(ctx, singlePortForwardingUrl(c, floatingIpId, pfId), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -82,13 +84,13 @@ func (opts CreateOpts) ToPortForwardingCreateMap() (map[string]interface{}, erro // Create accepts a CreateOpts struct and uses the values provided to create a // new port forwarding for an existing floating IP. -func Create(c *gophercloud.ServiceClient, floatingIpId string, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, floatingIpId string, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPortForwardingCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(portForwardingUrl(c, floatingIpId), b, &r.Body, nil) + resp, err := c.Post(ctx, portForwardingUrl(c, floatingIpId), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -120,13 +122,13 @@ type UpdateOptsBuilder interface { } // Update allows port forwarding resources to be updated. -func Update(c *gophercloud.ServiceClient, fipID string, pfID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, fipID string, pfID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPortForwardingUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(singlePortForwardingUrl(c, fipID, pfID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, singlePortForwardingUrl(c, fipID, pfID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -134,8 +136,8 @@ func Update(c *gophercloud.ServiceClient, fipID string, pfID string, opts Update } // Delete will permanently delete a particular port forwarding for a given floating ID. -func Delete(c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r DeleteResult) { - resp, err := c.Delete(singlePortForwardingUrl(c, floatingIpId, pfId), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, floatingIpId string, pfId string) (r DeleteResult) { + resp, err := c.Delete(ctx, singlePortForwardingUrl(c, floatingIpId, pfId), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go index 57b3ff0b30..a2d58ec4e8 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -27,7 +28,7 @@ func TestPortForwardingList(t *testing.T) { count := 0 - portforwarding.List(fake.ServiceClient(), portforwarding.ListOpts{}, "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").EachPage(func(page pagination.Page) (bool, error) { + portforwarding.List(fake.ServiceClient(), portforwarding.ListOpts{}, "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := portforwarding.ExtractPortForwardings(page) if err != nil { @@ -110,7 +111,7 @@ func TestCreate(t *testing.T) { InternalPortID: "1238be08-a2a8-4b8d-addf-fb5e2250e480", } - pf, err := portforwarding.Create(fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", options).Extract() + pf, err := portforwarding.Create(context.TODO(), fake.ServiceClient(), "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "725ade3c-9760-4880-8080-8fc2dbab9acc", pf.ID) @@ -146,7 +147,7 @@ func TestGet(t *testing.T) { `) }) - pf, err := portforwarding.Get(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc").Extract() + pf, err := portforwarding.Get(context.TODO(), fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "tcp", pf.Protocol) @@ -167,7 +168,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := portforwarding.Delete(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc") + res := portforwarding.Delete(context.TODO(), fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc") th.AssertNoErr(t, res.Err) } @@ -219,7 +220,7 @@ func TestUpdate(t *testing.T) { ExternalPort: updatedExternalPort, } - actual, err := portforwarding.Update(fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc", options).Extract() + actual, err := portforwarding.Update(context.TODO(), fake.ServiceClient(), "2f245a7b-796b-4f26-9cf9-9e82d248fda7", "725ade3c-9760-4880-8080-8fc2dbab9acc", options).Extract() th.AssertNoErr(t, err) expected := portforwarding.PortForwarding{ Protocol: "udp", diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index 8a39ef7593..67061533ea 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -1,6 +1,8 @@ package routers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -78,20 +80,20 @@ func (opts CreateOpts) ToRouterCreateMap() (map[string]interface{}, error) { // GatewayInfo struct. The external gateway for the router must be plugged into // an external network (it is external if its `router:external' field is set to // true). -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRouterCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular router based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -122,13 +124,13 @@ func (opts UpdateOpts) ToRouterUpdateMap() (map[string]interface{}, error) { // external gateway for a router, see Create. This operation does not enable // the update of router interfaces. To do this, use the AddInterface and // RemoveInterface functions. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRouterUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -136,8 +138,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular router based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -180,13 +182,13 @@ func (opts AddInterfaceOpts) ToRouterAddInterfaceMap() (map[string]interface{}, // identifier of a new port created by this operation. After the operation // completes, the device ID of the port is set to the router ID, and the // device owner attribute is set to `network:router_interface'. -func AddInterface(c *gophercloud.ServiceClient, id string, opts AddInterfaceOptsBuilder) (r InterfaceResult) { +func AddInterface(ctx context.Context, c *gophercloud.ServiceClient, id string, opts AddInterfaceOptsBuilder) (r InterfaceResult) { b, err := opts.ToRouterAddInterfaceMap() if err != nil { r.Err = err return } - resp, err := c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -225,13 +227,13 @@ func (opts RemoveInterfaceOpts) ToRouterRemoveInterfaceMap() (map[string]interfa // visible to you, the operation will fail and a 404 Not Found error will be // returned. After this operation completes, the port connecting the router // with the subnet is removed from the subnet for the network. -func RemoveInterface(c *gophercloud.ServiceClient, id string, opts RemoveInterfaceOptsBuilder) (r InterfaceResult) { +func RemoveInterface(ctx context.Context, c *gophercloud.ServiceClient, id string, opts RemoveInterfaceOptsBuilder) (r InterfaceResult) { b, err := opts.ToRouterRemoveInterfaceMap() if err != nil { r.Err = err return } - resp, err := c.Put(removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 1e1b7a630e..fbb07c71ae 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -69,7 +70,7 @@ func TestList(t *testing.T) { count := 0 - routers.List(fake.ServiceClient(), routers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + routers.List(fake.ServiceClient(), routers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := routers.ExtractRouters(page) if err != nil { @@ -197,7 +198,7 @@ func TestCreate(t *testing.T) { GatewayInfo: &gwi, AvailabilityZoneHints: []string{"zone1", "zone2"}, } - r, err := routers.Create(fake.ServiceClient(), options).Extract() + r, err := routers.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) gwi.ExternalFixedIPs = []routers.ExternalFixedIP{{ @@ -250,7 +251,7 @@ func TestGet(t *testing.T) { `) }) - n, err := routers.Get(fake.ServiceClient(), "a07eea83-7710-4860-931b-5fe220fae533").Extract() + n, err := routers.Get(context.TODO(), fake.ServiceClient(), "a07eea83-7710-4860-931b-5fe220fae533").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") @@ -333,7 +334,7 @@ func TestUpdate(t *testing.T) { r := []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}} options := routers.UpdateOpts{Name: "new_name", GatewayInfo: &gwi, Routes: &r} - n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + n, err := routers.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) gwi.ExternalFixedIPs = []routers.ExternalFixedIP{ @@ -393,7 +394,7 @@ func TestUpdateWithoutRoutes(t *testing.T) { options := routers.UpdateOpts{Name: "new_name"} - n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + n, err := routers.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "new_name") @@ -441,7 +442,7 @@ func TestAllRoutesRemoved(t *testing.T) { r := []routers.Route{} options := routers.UpdateOpts{Routes: &r} - n, err := routers.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + n, err := routers.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, n.Routes, []routers.Route{}) @@ -457,7 +458,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := routers.Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c") + res := routers.Delete(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c") th.AssertNoErr(t, res.Err) } @@ -490,7 +491,7 @@ func TestAddInterface(t *testing.T) { }) opts := routers.AddInterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"} - res, err := routers.AddInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() + res, err := routers.AddInterface(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID) @@ -500,11 +501,11 @@ func TestAddInterface(t *testing.T) { } func TestAddInterfaceRequiredOpts(t *testing.T) { - _, err := routers.AddInterface(fake.ServiceClient(), "foo", routers.AddInterfaceOpts{}).Extract() + _, err := routers.AddInterface(context.TODO(), fake.ServiceClient(), "foo", routers.AddInterfaceOpts{}).Extract() if err == nil { t.Fatalf("Expected error, got none") } - _, err = routers.AddInterface(fake.ServiceClient(), "foo", routers.AddInterfaceOpts{SubnetID: "bar", PortID: "baz"}).Extract() + _, err = routers.AddInterface(context.TODO(), fake.ServiceClient(), "foo", routers.AddInterfaceOpts{SubnetID: "bar", PortID: "baz"}).Extract() if err == nil { t.Fatalf("Expected error, got none") } @@ -539,7 +540,7 @@ func TestRemoveInterface(t *testing.T) { }) opts := routers.RemoveInterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"} - res, err := routers.RemoveInterface(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() + res, err := routers.RemoveInterface(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID) @@ -621,7 +622,7 @@ func TestListL3Agents(t *testing.T) { `) }) - l3AgentsPages, err := routers.ListL3Agents(fake.ServiceClient(), "fa3a4aaa-c73f-48aa-a603-8c8bf642b7c0").AllPages() + l3AgentsPages, err := routers.ListL3Agents(fake.ServiceClient(), "fa3a4aaa-c73f-48aa-a603-8c8bf642b7c0").AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := routers.ExtractL3Agents(l3AgentsPages) th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/lbaas/members/requests.go b/openstack/networking/v2/extensions/lbaas/members/requests.go index 9b102ed745..b5ad4b5c5c 100644 --- a/openstack/networking/v2/extensions/lbaas/members/requests.go +++ b/openstack/networking/v2/extensions/lbaas/members/requests.go @@ -1,6 +1,8 @@ package members import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -71,20 +73,20 @@ func (opts CreateOpts) ToLBMemberCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and uses the values to create a new // load balancer pool member. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLBMemberCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool member based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -107,13 +109,13 @@ func (opts UpdateOpts) ToLBMemberUpdateMap() (map[string]interface{}, error) { } // Update allows members to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLBMemberUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -121,8 +123,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular member based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go index 0acb5b44bb..5af53d8fa8 100644 --- a/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -53,7 +54,7 @@ func TestList(t *testing.T) { count := 0 - members.List(fake.ServiceClient(), members.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + members.List(fake.ServiceClient(), members.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := members.ExtractMembers(page) if err != nil { @@ -138,7 +139,7 @@ func TestCreate(t *testing.T) { ProtocolPort: 8080, PoolID: "foo", } - _, err := members.Create(fake.ServiceClient(), options).Extract() + _, err := members.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -168,7 +169,7 @@ func TestGet(t *testing.T) { `) }) - m, err := members.Get(fake.ServiceClient(), "975592ca-e308-48ad-8298-731935ee9f45").Extract() + m, err := members.Get(context.TODO(), fake.ServiceClient(), "975592ca-e308-48ad-8298-731935ee9f45").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "975592ca-e308-48ad-8298-731935ee9f45", m.ID) @@ -219,7 +220,7 @@ func TestUpdate(t *testing.T) { options := members.UpdateOpts{AdminStateUp: gophercloud.Disabled} - _, err := members.Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() + _, err := members.Update(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() th.AssertNoErr(t, err) } @@ -233,6 +234,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := members.Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853") + res := members.Delete(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go index 47fc567b9b..fa23b85db6 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go @@ -1,6 +1,7 @@ package monitors import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -140,20 +141,20 @@ func (opts CreateOpts) ToLBMonitorCreateMap() (map[string]interface{}, error) { // CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, // // HttpMethod: "HEAD", ExpectedCodes: "200"} -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLBMonitorCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular health monitor based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -210,13 +211,13 @@ func (opts UpdateOpts) ToLBMonitorUpdateMap() (map[string]interface{}, error) { // Update is an operation which modifies the attributes of the specified // monitor. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLBMonitorUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -224,8 +225,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular monitor based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go index c07c9fcce3..8537df8b02 100644 --- a/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -54,7 +55,7 @@ func TestList(t *testing.T) { count := 0 - monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := monitors.ExtractMonitors(page) if err != nil { @@ -97,7 +98,7 @@ func TestList(t *testing.T) { } func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { - _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + _, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", Delay: 1, Timeout: 10, @@ -110,7 +111,7 @@ func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { t.Fatalf("Expected error, got none") } - _, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ + _, err = monitors.Update(context.TODO(), fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ Delay: 1, Timeout: 10, }).Extract() @@ -165,7 +166,7 @@ func TestCreate(t *testing.T) { `) }) - _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + _, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", TenantID: "453105b9-1754-413f-aab1-55f1af620750", Delay: 20, @@ -179,11 +180,11 @@ func TestCreate(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{}) + res := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) + res = monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -219,7 +220,7 @@ func TestGet(t *testing.T) { `) }) - hm, err := monitors.Get(fake.ServiceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract() + hm, err := monitors.Get(context.TODO(), fake.ServiceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "f3eeab00-8367-4524-b662-55e64d4cacb5", hm.ID) @@ -283,7 +284,7 @@ func TestUpdate(t *testing.T) { `) }) - _, err := monitors.Update(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", monitors.UpdateOpts{ + _, err := monitors.Update(context.TODO(), fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", monitors.UpdateOpts{ Delay: 30, Timeout: 20, MaxRetries: 10, @@ -305,6 +306,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := monitors.Delete(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7") + res := monitors.Delete(context.TODO(), fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go index 9c9cedb32c..ec02374f70 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas/pools/requests.go @@ -1,6 +1,8 @@ package pools import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -98,20 +100,20 @@ func (opts CreateOpts) ToLBPoolCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOptsBuilder and uses the values to create a new // load balancer pool. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLBPoolCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -139,13 +141,13 @@ func (opts UpdateOpts) ToLBPoolUpdateMap() (map[string]interface{}, error) { } // Update allows pools to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLBPoolUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -153,8 +155,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular pool based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -164,9 +166,9 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { // pool and will deactivate these members if they are deemed unhealthy. A // member can be deactivated (status set to INACTIVE) if any of health monitors // finds it unhealthy. -func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { +func AssociateMonitor(ctx context.Context, c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { b := map[string]interface{}{"health_monitor": map[string]string{"id": monitorID}} - resp, err := c.Post(associateURL(c, poolID), b, &r.Body, nil) + resp, err := c.Post(ctx, associateURL(c, poolID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -174,8 +176,8 @@ func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r // DisassociateMonitor will disassociate a health monitor with a particular // pool. When dissociation is successful, the health monitor will no longer // check for the health of the members of the pool. -func DisassociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { - resp, err := c.Delete(disassociateURL(c, poolID, monitorID), nil) +func DisassociateMonitor(ctx context.Context, c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { + resp, err := c.Delete(ctx, disassociateURL(c, poolID, monitorID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go index 814f5c86de..db2dcf90ee 100644 --- a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -55,7 +56,7 @@ func TestList(t *testing.T) { count := 0 - pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := pools.ExtractPools(page) if err != nil { @@ -153,7 +154,7 @@ func TestCreate(t *testing.T) { TenantID: "2ffc6e22aae24e4795f87155d24c896f", Provider: "haproxy", } - p, err := pools.Create(fake.ServiceClient(), options).Extract() + p, err := pools.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "PENDING_CREATE", p.Status) @@ -201,7 +202,7 @@ func TestGet(t *testing.T) { `) }) - n, err := pools.Get(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853").Extract() + n, err := pools.Get(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.ID, "332abe93-f488-41ba-870b-2ac66be7f853") @@ -255,7 +256,7 @@ func TestUpdate(t *testing.T) { var name = "SuperPool" options := pools.UpdateOpts{Name: &name, LBMethod: pools.LBMethodLeastConnections} - n, err := pools.Update(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() + n, err := pools.Update(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "SuperPool", n.Name) @@ -272,7 +273,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := pools.Delete(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853") + res := pools.Delete(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853") th.AssertNoErr(t, res.Err) } @@ -298,7 +299,7 @@ func TestAssociateHealthMonitor(t *testing.T) { fmt.Fprintf(w, `{}`) }) - _, err := pools.AssociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract() + _, err := pools.AssociateMonitor(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract() th.AssertNoErr(t, err) } @@ -312,6 +313,6 @@ func TestDisassociateHealthMonitor(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := pools.DisassociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181") + res := pools.DisassociateMonitor(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go index 0307aa6999..40937645b5 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/requests.go +++ b/openstack/networking/v2/extensions/lbaas/vips/requests.go @@ -1,6 +1,8 @@ package vips import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -108,20 +110,20 @@ func (opts CreateOpts) ToVIPCreateMap() (map[string]interface{}, error) { // // Users with an admin role can create VIPs on behalf of other tenants by // specifying a TenantID attribute different than their own. -func Create(c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { b, err := opts.ToVIPCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular virtual IP based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -163,13 +165,13 @@ func (opts UpdateOpts) ToVIPUpdateMap() (map[string]interface{}, error) { } // Update is an operation which modifies the attributes of the specified VIP. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToVIPUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -177,8 +179,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular virtual IP based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go index 5543faf901..ae0c86e87b 100644 --- a/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -63,7 +64,7 @@ func TestList(t *testing.T) { count := 0 - vips.List(fake.ServiceClient(), vips.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + vips.List(fake.ServiceClient(), vips.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := vips.ExtractVIPs(page) if err != nil { @@ -173,7 +174,7 @@ func TestCreate(t *testing.T) { Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, } - r, err := vips.Create(fake.ServiceClient(), opts).Extract() + r, err := vips.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "PENDING_CREATE", r.Status) @@ -192,23 +193,23 @@ func TestCreate(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := vips.Create(fake.ServiceClient(), vips.CreateOpts{}) + res := vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo"}) + res = vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar"}) + res = vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar"}) + res = vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = vips.Create(fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar", ProtocolPort: 80}) + res = vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -250,7 +251,7 @@ func TestGet(t *testing.T) { `) }) - vip, err := vips.Get(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() + vip, err := vips.Get(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "ACTIVE", vip.Status) @@ -308,7 +309,7 @@ func TestUpdate(t *testing.T) { ConnLimit: &i1000, Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, } - vip, err := vips.Update(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304", options).Extract() + vip, err := vips.Update(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "PENDING_UPDATE", vip.Status) @@ -325,6 +326,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := vips.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") + res := vips.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go index 022169cdb4..c7ac1b1e98 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go @@ -1,6 +1,8 @@ package l7policies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -74,13 +76,13 @@ func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]interface{}, error) { } // Create accepts a CreateOpts struct and uses the values to create a new l7policy. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToL7PolicyCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -137,15 +139,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a particular l7policy based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular l7policy based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -205,13 +207,13 @@ func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { } // Update allows l7policy to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToL7PolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -252,13 +254,13 @@ func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { } // CreateRule will create and associate a Rule with a particular L7Policy. -func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) { +func CreateRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) { b, err := opts.ToRuleCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + resp, err := c.Post(ctx, ruleRootURL(c, policyID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -313,15 +315,15 @@ func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOpts } // GetRule retrieves a particular L7Policy Rule based on its unique ID. -func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { - resp, err := c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) +func GetRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { + resp, err := c.Get(ctx, ruleResourceURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will remove a Rule from a particular L7Policy. -func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { - resp, err := c.Delete(ruleResourceURL(c, policyID, ruleID), nil) +func DeleteRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { + resp, err := c.Delete(ctx, ruleResourceURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -370,13 +372,13 @@ func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { } // UpdateRule allows Rule to be updated. -func UpdateRule(c *gophercloud.ServiceClient, policyID string, ruleID string, opts UpdateRuleOptsBuilder) (r UpdateRuleResult) { +func UpdateRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string, opts UpdateRuleOptsBuilder) (r UpdateRuleResult) { b, err := opts.ToRuleUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go index 55453efc8b..89a476c3e9 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" @@ -14,7 +15,7 @@ func TestCreateL7Policy(t *testing.T) { defer th.TeardownHTTP() HandleL7PolicyCreationSuccessfully(t, SingleL7PolicyBody) - actual, err := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ + actual, err := l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{ Name: "redirect-example.com", ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: l7policies.ActionRedirectToURL, @@ -27,13 +28,13 @@ func TestCreateL7Policy(t *testing.T) { func TestRequiredL7PolicyCreateOpts(t *testing.T) { // no param specified. - res := l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{}) + res := l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } // Action is invalid. - res = l7policies.Create(fake.ServiceClient(), l7policies.CreateOpts{ + res = l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{ ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", Action: l7policies.Action("invalid"), }) @@ -48,7 +49,7 @@ func TestListL7Policies(t *testing.T) { HandleL7PolicyListSuccessfully(t) pages := 0 - err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := l7policies.ExtractL7Policies(page) @@ -77,7 +78,7 @@ func TestListAllL7Policies(t *testing.T) { defer th.TeardownHTTP() HandleL7PolicyListSuccessfully(t) - allPages, err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).AllPages() + allPages, err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := l7policies.ExtractL7Policies(allPages) th.AssertNoErr(t, err) @@ -91,7 +92,7 @@ func TestGetL7Policy(t *testing.T) { HandleL7PolicyGetSuccessfully(t) client := fake.ServiceClient() - actual, err := l7policies.Get(client, "8a1412f0-4c32-4257-8b07-af4770b604fd").Extract() + actual, err := l7policies.Get(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -104,7 +105,7 @@ func TestDeleteL7Policy(t *testing.T) { defer th.TeardownHTTP() HandleL7PolicyDeletionSuccessfully(t) - res := l7policies.Delete(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") + res := l7policies.Delete(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") th.AssertNoErr(t, res.Err) } @@ -116,7 +117,7 @@ func TestUpdateL7Policy(t *testing.T) { client := fake.ServiceClient() newName := "NewL7PolicyName" redirectURL := "http://www.new-example.com" - actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", + actual, err := l7policies.Update(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, Action: l7policies.ActionRedirectToURL, @@ -137,7 +138,7 @@ func TestUpdateL7PolicyNullRedirectURL(t *testing.T) { client := fake.ServiceClient() newName := "NewL7PolicyName" redirectURL := "" - actual, err := l7policies.Update(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", + actual, err := l7policies.Update(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Name: &newName, RedirectURL: &redirectURL, @@ -153,7 +154,7 @@ func TestUpdateL7PolicyWithInvalidOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - res := l7policies.Update(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ + res := l7policies.Update(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ Action: l7policies.Action("invalid"), }) if res.Err == nil { @@ -166,7 +167,7 @@ func TestCreateRule(t *testing.T) { defer th.TeardownHTTP() HandleRuleCreationSuccessfully(t, SingleRuleBody) - actual, err := l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + actual, err := l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images*", @@ -180,17 +181,17 @@ func TestRequiredRuleCreateOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - res := l7policies.CreateRule(fake.ServiceClient(), "", l7policies.CreateRuleOpts{}) + res := l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "", l7policies.CreateRuleOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, }) if res.Err == nil { t.Fatalf("Expected error, but got none") } - res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.RuleType("invalid"), CompareType: l7policies.CompareTypeRegex, Value: "/images*", @@ -198,7 +199,7 @@ func TestRequiredRuleCreateOpts(t *testing.T) { if res.Err == nil { t.Fatalf("Expected error, but got none") } - res = l7policies.CreateRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ + res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareType("invalid"), Value: "/images*", @@ -214,7 +215,7 @@ func TestListRules(t *testing.T) { HandleRuleListSuccessfully(t) pages := 0 - err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := l7policies.ExtractRules(page) @@ -243,7 +244,7 @@ func TestListAllRules(t *testing.T) { defer th.TeardownHTTP() HandleRuleListSuccessfully(t) - allPages, err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).AllPages() + allPages, err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := l7policies.ExtractRules(allPages) @@ -258,7 +259,7 @@ func TestGetRule(t *testing.T) { HandleRuleGetSuccessfully(t) client := fake.ServiceClient() - actual, err := l7policies.GetRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c").Extract() + actual, err := l7policies.GetRule(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -271,7 +272,7 @@ func TestDeleteRule(t *testing.T) { defer th.TeardownHTTP() HandleRuleDeletionSuccessfully(t) - res := l7policies.DeleteRule(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") + res := l7policies.DeleteRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") th.AssertNoErr(t, res.Err) } @@ -283,7 +284,7 @@ func TestUpdateRule(t *testing.T) { client := fake.ServiceClient() invert := false key := "" - actual, err := l7policies.UpdateRule(client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ + actual, err := l7policies.UpdateRule(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ RuleType: l7policies.TypePath, CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", @@ -301,14 +302,14 @@ func TestUpdateRuleWithInvalidOpts(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() - res := l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ + res := l7policies.UpdateRule(context.TODO(), fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ RuleType: l7policies.RuleType("invalid"), }) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = l7policies.UpdateRule(fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ + res = l7policies.UpdateRule(context.TODO(), fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ CompareType: l7policies.CompareType("invalid"), }) if res.Err == nil { diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go index b234f10475..c914b4adde 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go @@ -1,6 +1,8 @@ package listeners import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -130,20 +132,20 @@ func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { // // Users with an admin role can create Listeners on behalf of other tenants by // specifying a TenantID attribute different than their own. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToListenerCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Listeners based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -195,13 +197,13 @@ func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { // Update is an operation which modifies the attributes of the specified // Listener. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToListenerUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -209,8 +211,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular Listeners based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go index 27049cf0e9..364bcd393e 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -16,7 +17,7 @@ func TestListListeners(t *testing.T) { HandleListenerListSuccessfully(t) pages := 0 - err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := listeners.ExtractListeners(page) @@ -45,7 +46,7 @@ func TestListAllListeners(t *testing.T) { defer th.TeardownHTTP() HandleListenerListSuccessfully(t) - allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages() + allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := listeners.ExtractListeners(allPages) th.AssertNoErr(t, err) @@ -58,7 +59,7 @@ func TestCreateListener(t *testing.T) { defer th.TeardownHTTP() HandleListenerCreationSuccessfully(t, SingleListenerBody) - actual, err := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{ + actual, err := listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{ Protocol: "TCP", Name: "db", LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", @@ -73,23 +74,23 @@ func TestCreateListener(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := listeners.Create(fake.ServiceClient(), listeners.CreateOpts{}) + res := listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo"}) + res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar"}) + res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar"}) + res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = listeners.Create(fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar", ProtocolPort: 80}) + res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -101,7 +102,7 @@ func TestGetListener(t *testing.T) { HandleListenerGetSuccessfully(t) client := fake.ServiceClient() - actual, err := listeners.Get(client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() + actual, err := listeners.Get(context.TODO(), client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -114,7 +115,7 @@ func TestDeleteListener(t *testing.T) { defer th.TeardownHTTP() HandleListenerDeletionSuccessfully(t) - res := listeners.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") + res := listeners.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } @@ -127,7 +128,7 @@ func TestUpdateListener(t *testing.T) { i1001 := 1001 name := "NewListenerName" defaultPoolID := "" - actual, err := listeners.Update(client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ + actual, err := listeners.Update(context.TODO(), client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ Name: &name, ConnLimit: &i1001, DefaultPoolID: &defaultPoolID, diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go index b522267156..b2848d3790 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go @@ -1,6 +1,7 @@ package loadbalancers import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -115,20 +116,20 @@ func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) // configuration defined in the CreateOpts struct. Once the request is // validated and progress has started on the provisioning process, a // CreateResult will be returned. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToLoadBalancerCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Loadbalancer based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -160,13 +161,13 @@ func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) // Update is an operation which modifies the attributes of the specified // LoadBalancer. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToLoadBalancerUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -175,8 +176,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r // Delete will permanently delete a particular LoadBalancer based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -185,27 +186,27 @@ func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { // children (listener, monitor, etc). // NOTE: This function will only work with Octavia load balancers; Neutron does not // support this. -func CascadingDelete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { +func CascadingDelete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { if c.Type != "load-balancer" { r.Err = fmt.Errorf("error prior to running cascade delete: only Octavia LBs supported") return } u := fmt.Sprintf("%s?cascade=true", resourceURL(c, id)) - resp, err := c.Delete(u, nil) + resp, err := c.Delete(ctx, u, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStatuses will return the status of a particular LoadBalancer. -func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { - resp, err := c.Get(statusRootURL(c, id), &r.Body, nil) +func GetStatuses(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { + resp, err := c.Get(ctx, statusRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular LoadBalancer. -func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { - resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) +func GetStats(ctx context.Context, c *gophercloud.ServiceClient, id string) (r StatsResult) { + resp, err := c.Get(ctx, statisticsRootURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go index e274fcb4d1..47803c7ff9 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -16,7 +17,7 @@ func TestListLoadbalancers(t *testing.T) { HandleLoadbalancerListSuccessfully(t) pages := 0 - err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := loadbalancers.ExtractLoadBalancers(page) @@ -45,7 +46,7 @@ func TestListAllLoadbalancers(t *testing.T) { defer th.TeardownHTTP() HandleLoadbalancerListSuccessfully(t) - allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages() + allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := loadbalancers.ExtractLoadBalancers(allPages) th.AssertNoErr(t, err) @@ -58,7 +59,7 @@ func TestCreateLoadbalancer(t *testing.T) { defer th.TeardownHTTP() HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody) - actual, err := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{ + actual, err := loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{ Name: "db_lb", AdminStateUp: gophercloud.Enabled, VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", @@ -72,19 +73,19 @@ func TestCreateLoadbalancer(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{}) + res := loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo"}) + res = loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar"}) + res = loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = loadbalancers.Create(fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"}) + res = loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -96,7 +97,7 @@ func TestGetLoadbalancer(t *testing.T) { HandleLoadbalancerGetSuccessfully(t) client := fake.ServiceClient() - actual, err := loadbalancers.Get(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + actual, err := loadbalancers.Get(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -110,7 +111,7 @@ func TestGetLoadbalancerStatusesTree(t *testing.T) { HandleLoadbalancerGetStatusesTree(t) client := fake.ServiceClient() - actual, err := loadbalancers.GetStatuses(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + actual, err := loadbalancers.GetStatuses(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -123,7 +124,7 @@ func TestDeleteLoadbalancer(t *testing.T) { defer th.TeardownHTTP() HandleLoadbalancerDeletionSuccessfully(t) - res := loadbalancers.Delete(fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") + res := loadbalancers.Delete(context.TODO(), fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") th.AssertNoErr(t, res.Err) } @@ -134,7 +135,7 @@ func TestUpdateLoadbalancer(t *testing.T) { client := fake.ServiceClient() name := "NewLoadbalancerName" - actual, err := loadbalancers.Update(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ + actual, err := loadbalancers.Update(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ Name: &name, }).Extract() if err != nil { @@ -151,13 +152,13 @@ func TestCascadingDeleteLoadbalancer(t *testing.T) { sc := fake.ServiceClient() sc.Type = "network" - err := loadbalancers.CascadingDelete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() + err := loadbalancers.CascadingDelete(context.TODO(), sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() if err == nil { t.Fatalf("expected error running CascadingDelete with Neutron service client but didn't get one") } sc.Type = "load-balancer" - err = loadbalancers.CascadingDelete(sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() + err = loadbalancers.CascadingDelete(context.TODO(), sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() th.AssertNoErr(t, err) } @@ -167,7 +168,7 @@ func TestGetLoadbalancerStatsTree(t *testing.T) { HandleLoadbalancerGetStatsTree(t) client := fake.ServiceClient() - actual, err := loadbalancers.GetStats(client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() + actual, err := loadbalancers.GetStats(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go index 1c48bb9742..91b49e514b 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go @@ -1,6 +1,7 @@ package monitors import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -173,20 +174,20 @@ 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"} */ -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToMonitorCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Health Monitor based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -239,14 +240,14 @@ func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) { // Update is an operation which modifies the attributes of the specified // Monitor. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToMonitorUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -254,8 +255,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular Monitor based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go index 162f61f92d..dfaaf67188 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" @@ -15,7 +16,7 @@ func TestListHealthmonitors(t *testing.T) { HandleHealthmonitorListSuccessfully(t) pages := 0 - err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := monitors.ExtractMonitors(page) @@ -44,7 +45,7 @@ func TestListAllHealthmonitors(t *testing.T) { defer th.TeardownHTTP() HandleHealthmonitorListSuccessfully(t) - allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages() + allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := monitors.ExtractMonitors(allPages) th.AssertNoErr(t, err) @@ -57,7 +58,7 @@ func TestCreateHealthmonitor(t *testing.T) { defer th.TeardownHTTP() HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody) - actual, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + actual, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", Name: "db", PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", @@ -74,11 +75,11 @@ func TestCreateHealthmonitor(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{}) + res := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = monitors.Create(fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) + res = monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -90,7 +91,7 @@ func TestGetHealthmonitor(t *testing.T) { HandleHealthmonitorGetSuccessfully(t) client := fake.ServiceClient() - actual, err := monitors.Get(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract() + actual, err := monitors.Get(context.TODO(), client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -103,7 +104,7 @@ func TestDeleteHealthmonitor(t *testing.T) { defer th.TeardownHTTP() HandleHealthmonitorDeletionSuccessfully(t) - res := monitors.Delete(fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7") + res := monitors.Delete(context.TODO(), fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7") th.AssertNoErr(t, res.Err) } @@ -114,7 +115,7 @@ func TestUpdateHealthmonitor(t *testing.T) { client := fake.ServiceClient() name := "NewHealthmonitorName" - actual, err := monitors.Update(client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ + actual, err := monitors.Update(context.TODO(), client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ Name: &name, Delay: 3, Timeout: 20, @@ -130,7 +131,7 @@ func TestUpdateHealthmonitor(t *testing.T) { } func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { - _, err := monitors.Create(fake.ServiceClient(), monitors.CreateOpts{ + _, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ Type: "HTTP", PoolID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d", Delay: 1, @@ -144,7 +145,7 @@ func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { t.Fatalf("Expected error, got none") } - _, err = monitors.Update(fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ + _, err = monitors.Update(context.TODO(), fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ Delay: 1, Timeout: 10, }).Extract() diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go index fa4dbebb67..a5badad272 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go @@ -1,6 +1,8 @@ package pools import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -128,20 +130,20 @@ func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and uses the values to create a new // load balancer pool. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPoolCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,13 +179,13 @@ func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) { } // Update allows pools to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPoolUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -191,8 +193,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete will permanently delete a particular pool based on its unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -295,20 +297,20 @@ func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) } // CreateMember will create and associate a Member with a particular Pool. -func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMemberOpts) (r CreateMemberResult) { +func CreateMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, opts CreateMemberOpts) (r CreateMemberResult) { b, err := opts.ToMemberCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + resp, err := c.Post(ctx, memberRootURL(c, poolID), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMember retrieves a particular Pool Member based on its unique ID. -func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { - resp, err := c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) +func GetMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { + resp, err := c.Get(ctx, memberResourceURL(c, poolID, memberID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -342,13 +344,13 @@ func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) } // Update allows Member to be updated. -func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) { +func UpdateMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) { b, err := opts.ToMemberUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -357,8 +359,8 @@ func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, // DisassociateMember will remove and disassociate a Member from a particular // Pool. -func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { - resp, err := c.Delete(memberResourceURL(c, poolID, memberID), nil) +func DeleteMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { + resp, err := c.Delete(ctx, memberResourceURL(c, poolID, memberID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go index 3801efe271..87c882bc6f 100644 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" @@ -15,7 +16,7 @@ func TestListPools(t *testing.T) { HandlePoolListSuccessfully(t) pages := 0 - err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := pools.ExtractPools(page) @@ -44,7 +45,7 @@ func TestListAllPools(t *testing.T) { defer th.TeardownHTTP() HandlePoolListSuccessfully(t) - allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages() + allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := pools.ExtractPools(allPages) th.AssertNoErr(t, err) @@ -57,7 +58,7 @@ func TestCreatePool(t *testing.T) { defer th.TeardownHTTP() HandlePoolCreationSuccessfully(t, SinglePoolBody) - actual, err := pools.Create(fake.ServiceClient(), pools.CreateOpts{ + actual, err := pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: "HTTP", Name: "Example pool", @@ -75,7 +76,7 @@ func TestGetPool(t *testing.T) { HandlePoolGetSuccessfully(t) client := fake.ServiceClient() - actual, err := pools.Get(client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract() + actual, err := pools.Get(context.TODO(), client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -88,7 +89,7 @@ func TestDeletePool(t *testing.T) { defer th.TeardownHTTP() HandlePoolDeletionSuccessfully(t) - res := pools.Delete(fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab") + res := pools.Delete(context.TODO(), fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab") th.AssertNoErr(t, res.Err) } @@ -99,7 +100,7 @@ func TestUpdatePool(t *testing.T) { client := fake.ServiceClient() name := "NewPoolName" - actual, err := pools.Update(client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ + actual, err := pools.Update(context.TODO(), client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ Name: &name, LBMethod: pools.LBMethodLeastConnections, }).Extract() @@ -111,11 +112,11 @@ func TestUpdatePool(t *testing.T) { } func TestRequiredPoolCreateOpts(t *testing.T) { - res := pools.Create(fake.ServiceClient(), pools.CreateOpts{}) + res := pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethod("invalid"), Protocol: pools.ProtocolHTTPS, LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", @@ -124,7 +125,7 @@ func TestRequiredPoolCreateOpts(t *testing.T) { t.Fatalf("Expected error, but got none") } - res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: pools.Protocol("invalid"), LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", @@ -133,7 +134,7 @@ func TestRequiredPoolCreateOpts(t *testing.T) { t.Fatalf("Expected error, but got none") } - res = pools.Create(fake.ServiceClient(), pools.CreateOpts{ + res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ LBMethod: pools.LBMethodRoundRobin, Protocol: pools.ProtocolHTTPS, }) @@ -148,7 +149,7 @@ func TestListMembers(t *testing.T) { HandleMemberListSuccessfully(t) pages := 0 - err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := pools.ExtractMembers(page) @@ -177,7 +178,7 @@ func TestListAllMembers(t *testing.T) { defer th.TeardownHTTP() HandleMemberListSuccessfully(t) - allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages() + allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := pools.ExtractMembers(allPages) th.AssertNoErr(t, err) @@ -191,7 +192,7 @@ func TestCreateMember(t *testing.T) { HandleMemberCreationSuccessfully(t, SingleMemberBody) weight := 10 - actual, err := pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ + actual, err := pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ Name: "db", SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", TenantID: "2ffc6e22aae24e4795f87155d24c896f", @@ -205,19 +206,19 @@ func TestCreateMember(t *testing.T) { } func TestRequiredMemberCreateOpts(t *testing.T) { - res := pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{}) + res := pools.CreateMember(context.TODO(), fake.ServiceClient(), "", pools.CreateMemberOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = pools.CreateMember(fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80}) + res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, but got none") } - res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80}) + res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80}) if res.Err == nil { t.Fatalf("Expected error, but got none") } - res = pools.CreateMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"}) + res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"}) if res.Err == nil { t.Fatalf("Expected error, but got none") } @@ -229,7 +230,7 @@ func TestGetMember(t *testing.T) { HandleMemberGetSuccessfully(t) client := fake.ServiceClient() - actual, err := pools.GetMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract() + actual, err := pools.GetMember(context.TODO(), client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract() if err != nil { t.Fatalf("Unexpected Get error: %v", err) } @@ -242,7 +243,7 @@ func TestDeleteMember(t *testing.T) { defer th.TeardownHTTP() HandleMemberDeletionSuccessfully(t) - res := pools.DeleteMember(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf") + res := pools.DeleteMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf") th.AssertNoErr(t, res.Err) } @@ -254,7 +255,7 @@ func TestUpdateMember(t *testing.T) { weight := 4 client := fake.ServiceClient() name := "newMemberName" - actual, err := pools.UpdateMember(client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ + actual, err := pools.UpdateMember(context.TODO(), client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ Name: &name, Weight: &weight, }).Extract() diff --git a/openstack/networking/v2/extensions/mtu/testing/results_test.go b/openstack/networking/v2/extensions/mtu/testing/results_test.go index 6b94a70cb7..273bdb5166 100644 --- a/openstack/networking/v2/extensions/mtu/testing/results_test.go +++ b/openstack/networking/v2/extensions/mtu/testing/results_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -37,7 +38,7 @@ func TestList(t *testing.T) { } var actual []NetworkWithMTUExt - allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() + allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &actual) @@ -63,7 +64,7 @@ func TestGet(t *testing.T) { var s NetworkMTU - err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + err := networks.Get(context.TODO(), fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", s.ID) @@ -100,7 +101,7 @@ func TestCreate(t *testing.T) { var s NetworkMTU - err := networks.Create(fake.ServiceClient(), mtuCreateOpts).ExtractInto(&s) + err := networks.Create(context.TODO(), fake.ServiceClient(), mtuCreateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) @@ -141,7 +142,7 @@ func TestUpdate(t *testing.T) { var s NetworkMTU - err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", mtuUpdateOpts).ExtractInto(&s) + err := networks.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", mtuUpdateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "4e8e5957-649f-477b-9e5b-f1f75b21c03c", s.ID) diff --git a/openstack/networking/v2/extensions/networkipavailabilities/requests.go b/openstack/networking/v2/extensions/networkipavailabilities/requests.go index 9039895a4a..a42836c093 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/requests.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/requests.go @@ -1,6 +1,8 @@ package networkipavailabilities import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -55,8 +57,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific NetworkIPAvailability based on its ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go index 87b71b55c0..86166b5e33 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -28,7 +29,7 @@ func TestList(t *testing.T) { count := 0 - err := networkipavailabilities.List(fake.ServiceClient(), networkipavailabilities.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := networkipavailabilities.List(fake.ServiceClient(), networkipavailabilities.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := networkipavailabilities.ExtractNetworkIPAvailabilities(page) if err != nil { @@ -67,7 +68,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, NetworkIPAvailabilityGetResult) }) - s, err := networkipavailabilities.Get(fake.ServiceClient(), "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() + s, err := networkipavailabilities.Get(context.TODO(), fake.ServiceClient(), "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.NetworkID, "cf11ab78-2302-49fa-870f-851a08c7afb8") diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go index 1c3d9df67c..3331e784d5 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -51,7 +52,7 @@ func TestList(t *testing.T) { }, } - allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages() + allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) err = ports.ExtractPortsInto(allPages, &actual) @@ -71,7 +72,7 @@ func TestGet(t *testing.T) { portsbinding.PortsBindingExt } - err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) + err := ports.Get(context.TODO(), fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "ACTIVE") @@ -122,7 +123,7 @@ func TestCreate(t *testing.T) { VNICType: "normal", } - err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s) + err := ports.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "DOWN") @@ -142,7 +143,7 @@ func TestCreate(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := ports.Create(fake.ServiceClient(), portsbinding.CreateOptsExt{CreateOptsBuilder: ports.CreateOpts{}}) + res := ports.Create(context.TODO(), fake.ServiceClient(), portsbinding.CreateOptsExt{CreateOptsBuilder: ports.CreateOpts{}}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -175,7 +176,7 @@ func TestUpdate(t *testing.T) { VNICType: "normal", } - err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) + err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go index 771f497577..4e21481de1 100644 --- a/openstack/networking/v2/extensions/provider/testing/results_test.go +++ b/openstack/networking/v2/extensions/provider/testing/results_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -33,7 +34,7 @@ func TestList(t *testing.T) { } var actual []NetworkWithExt - allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() + allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &actual) @@ -67,7 +68,7 @@ func TestGet(t *testing.T) { provider.NetworkProviderExt } - err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + err := networks.Get(context.TODO(), fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "d32019d3-bc6e-4319-9c1d-6722fc136a22", s.ID) @@ -99,7 +100,7 @@ func TestCreate(t *testing.T) { } options := networks.CreateOpts{Name: "private", AdminStateUp: gophercloud.Enabled} - err := networks.Create(fake.ServiceClient(), options).ExtractInto(&s) + err := networks.Create(context.TODO(), fake.ServiceClient(), options).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) @@ -184,7 +185,7 @@ func TestCreateWithMultipleProvider(t *testing.T) { Segments: segments, } - _, err := networks.Create(fake.ServiceClient(), providerCreateOpts).Extract() + _, err := networks.Create(context.TODO(), fake.ServiceClient(), providerCreateOpts).Extract() th.AssertNoErr(t, err) } @@ -234,7 +235,7 @@ func TestUpdate(t *testing.T) { provider.NetworkProviderExt } - err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", providerUpdateOpts).ExtractInto(&s) + err := networks.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", providerUpdateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "4e8e5957-649f-477b-9e5b-f1f75b21c03c", s.ID) diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index dd4b97e59b..23a2df70a3 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -1,6 +1,8 @@ package policies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" @@ -170,8 +172,8 @@ func List(c *gophercloud.ServiceClient, opts PolicyListOptsBuilder) pagination.P } // Get retrieves a specific QoS policy based on its ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -209,13 +211,13 @@ func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new QoS policy on the server. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -250,13 +252,13 @@ func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { // Update accepts a UpdateOpts struct and updates an existing policy using the // values provided. -func Update(c *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, policyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -264,8 +266,8 @@ func Update(c *gophercloud.ServiceClient, policyID string, opts UpdateOptsBuilde } // Delete accepts a unique ID and deletes the QoS policy associated with it. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index 3d94557748..c5c1eab261 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -33,7 +34,7 @@ func TestGetPort(t *testing.T) { ports.Port policies.QoSPolicyExt } - err := ports.Get(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d").ExtractInto(&p) + err := ports.Get(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d").ExtractInto(&p) th.AssertNoErr(t, err) th.AssertEquals(t, p.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") @@ -69,7 +70,7 @@ func TestCreatePort(t *testing.T) { CreateOptsBuilder: portCreateOpts, QoSPolicyID: "591e0597-39a6-4665-8149-2111d8de9a08", } - err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&p) + err := ports.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractInto(&p) th.AssertNoErr(t, err) th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") @@ -107,7 +108,7 @@ func TestUpdatePortWithPolicy(t *testing.T) { UpdateOptsBuilder: portUpdateOpts, QoSPolicyID: &policyID, } - err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&p) + err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&p) th.AssertNoErr(t, err) th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") @@ -145,7 +146,7 @@ func TestUpdatePortWithoutPolicy(t *testing.T) { UpdateOptsBuilder: portUpdateOpts, QoSPolicyID: &policyID, } - err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&p) + err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&p) th.AssertNoErr(t, err) th.AssertEquals(t, p.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") @@ -173,7 +174,7 @@ func TestGetNetwork(t *testing.T) { networks.Network policies.QoSPolicyExt } - err := networks.Get(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d").ExtractInto(&n) + err := networks.Get(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d").ExtractInto(&n) th.AssertNoErr(t, err) th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") @@ -209,7 +210,7 @@ func TestCreateNetwork(t *testing.T) { CreateOptsBuilder: networkCreateOpts, QoSPolicyID: "591e0597-39a6-4665-8149-2111d8de9a08", } - err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&n) + err := networks.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractInto(&n) th.AssertNoErr(t, err) th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") @@ -249,7 +250,7 @@ func TestUpdateNetworkWithPolicy(t *testing.T) { UpdateOptsBuilder: networkUpdateOpts, QoSPolicyID: &policyID, } - err := networks.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&n) + err := networks.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&n) th.AssertNoErr(t, err) th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") @@ -287,7 +288,7 @@ func TestUpdateNetworkWithoutPolicy(t *testing.T) { UpdateOptsBuilder: networkUpdateOpts, QoSPolicyID: &policyID, } - err := networks.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&n) + err := networks.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&n) th.AssertNoErr(t, err) th.AssertEquals(t, n.TenantID, "4fd44f30292945e481c7b8a0c8908869") @@ -311,7 +312,7 @@ func TestListPolicies(t *testing.T) { count := 0 - err := policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := policies.ExtractPolicies(page) if err != nil { @@ -349,7 +350,7 @@ func TestGetPolicy(t *testing.T) { fmt.Fprintf(w, GetPolicyResponse) }) - p, err := policies.Get(fake.ServiceClient(), "30a57f4a-336b-4382-8275-d708babd2241").Extract() + p, err := policies.Get(context.TODO(), fake.ServiceClient(), "30a57f4a-336b-4382-8275-d708babd2241").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "bw-limiter", p.Name) @@ -395,7 +396,7 @@ func TestCreatePolicy(t *testing.T) { IsDefault: true, Description: "use-me", } - p, err := policies.Create(fake.ServiceClient(), opts).Extract() + p, err := policies.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "shared-default-policy", p.Name) @@ -434,7 +435,7 @@ func TestUpdatePolicy(t *testing.T) { Shared: &shared, Description: &description, } - p, err := policies.Update(fake.ServiceClient(), "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", opts).Extract() + p, err := policies.Update(context.TODO(), fake.ServiceClient(), "d6ae28ce-fcb5-4180-aa62-d260a27e09ae", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "new-name", p.Name) @@ -459,6 +460,6 @@ func TestDeletePolicy(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := policies.Delete(fake.ServiceClient(), "d6ae28ce-fcb5-4180-aa62-d260a27e09ae") + res := policies.Delete(context.TODO(), fake.ServiceClient(), "d6ae28ce-fcb5-4180-aa62-d260a27e09ae") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index 58c4648e7f..a8b11521b2 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -1,6 +1,8 @@ package rules import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -58,8 +60,8 @@ func ListBandwidthLimitRules(c *gophercloud.ServiceClient, policyID string, opts } // GetBandwidthLimitRule retrieves a specific BandwidthLimitRule based on its ID. -func GetBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetBandwidthLimitRuleResult) { - resp, err := c.Get(getBandwidthLimitRuleURL(c, policyID, ruleID), &r.Body, nil) +func GetBandwidthLimitRule(ctx context.Context, c *gophercloud.ServiceClient, policyID, ruleID string) (r GetBandwidthLimitRuleResult) { + resp, err := c.Get(ctx, getBandwidthLimitRuleURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -88,13 +90,13 @@ func (opts CreateBandwidthLimitRuleOpts) ToBandwidthLimitRuleCreateMap() (map[st } // CreateBandwidthLimitRule requests the creation of a new BandwidthLimitRule on the server. -func CreateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID string, opts CreateBandwidthLimitRuleOptsBuilder) (r CreateBandwidthLimitRuleResult) { +func CreateBandwidthLimitRule(ctx context.Context, client *gophercloud.ServiceClient, policyID string, opts CreateBandwidthLimitRuleOptsBuilder) (r CreateBandwidthLimitRuleResult) { b, err := opts.ToBandwidthLimitRuleCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createBandwidthLimitRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createBandwidthLimitRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -125,13 +127,13 @@ func (opts UpdateBandwidthLimitRuleOpts) ToBandwidthLimitRuleUpdateMap() (map[st } // UpdateBandwidthLimitRule requests the creation of a new BandwidthLimitRule on the server. -func UpdateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateBandwidthLimitRuleOptsBuilder) (r UpdateBandwidthLimitRuleResult) { +func UpdateBandwidthLimitRule(ctx context.Context, client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateBandwidthLimitRuleOptsBuilder) (r UpdateBandwidthLimitRuleResult) { b, err := opts.ToBandwidthLimitRuleUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateBandwidthLimitRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateBandwidthLimitRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -139,8 +141,8 @@ func UpdateBandwidthLimitRule(client *gophercloud.ServiceClient, policyID, ruleI } // Delete accepts policy and rule ID and deletes the BandwidthLimitRule associated with them. -func DeleteBandwidthLimitRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteBandwidthLimitRuleResult) { - resp, err := c.Delete(deleteBandwidthLimitRuleURL(c, policyID, ruleID), nil) +func DeleteBandwidthLimitRule(ctx context.Context, c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteBandwidthLimitRuleResult) { + resp, err := c.Delete(ctx, deleteBandwidthLimitRuleURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -196,8 +198,8 @@ func ListDSCPMarkingRules(c *gophercloud.ServiceClient, policyID string, opts DS } // GetDSCPMarkingRule retrieves a specific DSCPMarkingRule based on its ID. -func GetDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetDSCPMarkingRuleResult) { - resp, err := c.Get(getDSCPMarkingRuleURL(c, policyID, ruleID), &r.Body, nil) +func GetDSCPMarkingRule(ctx context.Context, c *gophercloud.ServiceClient, policyID, ruleID string) (r GetDSCPMarkingRuleResult) { + resp, err := c.Get(ctx, getDSCPMarkingRuleURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -220,13 +222,13 @@ func (opts CreateDSCPMarkingRuleOpts) ToDSCPMarkingRuleCreateMap() (map[string]i } // CreateDSCPMarkingRule requests the creation of a new DSCPMarkingRule on the server. -func CreateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID string, opts CreateDSCPMarkingRuleOptsBuilder) (r CreateDSCPMarkingRuleResult) { +func CreateDSCPMarkingRule(ctx context.Context, client *gophercloud.ServiceClient, policyID string, opts CreateDSCPMarkingRuleOptsBuilder) (r CreateDSCPMarkingRuleResult) { b, err := opts.ToDSCPMarkingRuleCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createDSCPMarkingRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createDSCPMarkingRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -251,13 +253,13 @@ func (opts UpdateDSCPMarkingRuleOpts) ToDSCPMarkingRuleUpdateMap() (map[string]i } // UpdateDSCPMarkingRule requests the creation of a new DSCPMarkingRule on the server. -func UpdateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateDSCPMarkingRuleOptsBuilder) (r UpdateDSCPMarkingRuleResult) { +func UpdateDSCPMarkingRule(ctx context.Context, client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateDSCPMarkingRuleOptsBuilder) (r UpdateDSCPMarkingRuleResult) { b, err := opts.ToDSCPMarkingRuleUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateDSCPMarkingRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateDSCPMarkingRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -265,8 +267,8 @@ func UpdateDSCPMarkingRule(client *gophercloud.ServiceClient, policyID, ruleID s } // DeleteDSCPMarkingRule accepts policy and rule ID and deletes the DSCPMarkingRule associated with them. -func DeleteDSCPMarkingRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteDSCPMarkingRuleResult) { - resp, err := c.Delete(deleteDSCPMarkingRuleURL(c, policyID, ruleID), nil) +func DeleteDSCPMarkingRule(ctx context.Context, c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteDSCPMarkingRuleResult) { + resp, err := c.Delete(ctx, deleteDSCPMarkingRuleURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -323,8 +325,8 @@ func ListMinimumBandwidthRules(c *gophercloud.ServiceClient, policyID string, op } // GetMinimumBandwidthRule retrieves a specific MinimumBandwidthRule based on its ID. -func GetMinimumBandwidthRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r GetMinimumBandwidthRuleResult) { - resp, err := c.Get(getMinimumBandwidthRuleURL(c, policyID, ruleID), &r.Body, nil) +func GetMinimumBandwidthRule(ctx context.Context, c *gophercloud.ServiceClient, policyID, ruleID string) (r GetMinimumBandwidthRuleResult) { + resp, err := c.Get(ctx, getMinimumBandwidthRuleURL(c, policyID, ruleID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -350,13 +352,13 @@ func (opts CreateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleCreateMap() (ma } // CreateMinimumBandwidthRule requests the creation of a new MinimumBandwidthRule on the server. -func CreateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID string, opts CreateMinimumBandwidthRuleOptsBuilder) (r CreateMinimumBandwidthRuleResult) { +func CreateMinimumBandwidthRule(ctx context.Context, client *gophercloud.ServiceClient, policyID string, opts CreateMinimumBandwidthRuleOptsBuilder) (r CreateMinimumBandwidthRuleResult) { b, err := opts.ToMinimumBandwidthRuleCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createMinimumBandwidthRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createMinimumBandwidthRuleURL(client, policyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -384,13 +386,13 @@ func (opts UpdateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleUpdateMap() (ma } // UpdateMinimumBandwidthRule requests the creation of a new MinimumBandwidthRule on the server. -func UpdateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateMinimumBandwidthRuleOptsBuilder) (r UpdateMinimumBandwidthRuleResult) { +func UpdateMinimumBandwidthRule(ctx context.Context, client *gophercloud.ServiceClient, policyID, ruleID string, opts UpdateMinimumBandwidthRuleOptsBuilder) (r UpdateMinimumBandwidthRuleResult) { b, err := opts.ToMinimumBandwidthRuleUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateMinimumBandwidthRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateMinimumBandwidthRuleURL(client, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -398,8 +400,8 @@ func UpdateMinimumBandwidthRule(client *gophercloud.ServiceClient, policyID, rul } // DeleteMinimumBandwidthRule accepts policy and rule ID and deletes the MinimumBandwidthRule associated with them. -func DeleteMinimumBandwidthRule(c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteMinimumBandwidthRuleResult) { - resp, err := c.Delete(deleteMinimumBandwidthRuleURL(c, policyID, ruleID), nil) +func DeleteMinimumBandwidthRule(ctx context.Context, c *gophercloud.ServiceClient, policyID, ruleID string) (r DeleteMinimumBandwidthRuleResult) { + resp, err := c.Delete(ctx, deleteMinimumBandwidthRuleURL(c, policyID, ruleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index f98ac61cfb..4ee49605ea 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -31,7 +32,7 @@ func TestListBandwidthLimitRule(t *testing.T) { fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", rules.BandwidthLimitRulesListOpts{}, - ).EachPage(func(page pagination.Page) (bool, error) { + ).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractBandwidthLimitRules(page) if err != nil { @@ -73,7 +74,7 @@ func TestGetBandwidthLimitRule(t *testing.T) { fmt.Fprintf(w, BandwidthLimitRulesGetResult) }) - r, err := rules.GetBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractBandwidthLimitRule() + r, err := rules.GetBandwidthLimitRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractBandwidthLimitRule() th.AssertNoErr(t, err) th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") @@ -103,7 +104,7 @@ func TestCreateBandwidthLimitRule(t *testing.T) { MaxKBps: 2000, MaxBurstKBps: 200, } - r, err := rules.CreateBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractBandwidthLimitRule() + r, err := rules.CreateBandwidthLimitRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractBandwidthLimitRule() th.AssertNoErr(t, err) th.AssertEquals(t, 200, r.MaxBurstKBps) @@ -133,7 +134,7 @@ func TestUpdateBandwidthLimitRule(t *testing.T) { MaxKBps: &maxKBps, MaxBurstKBps: &maxBurstKBps, } - r, err := rules.UpdateBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractBandwidthLimitRule() + r, err := rules.UpdateBandwidthLimitRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractBandwidthLimitRule() th.AssertNoErr(t, err) th.AssertEquals(t, 0, r.MaxBurstKBps) @@ -150,7 +151,7 @@ func TestDeleteBandwidthLimitRule(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := rules.DeleteBandwidthLimitRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") + res := rules.DeleteBandwidthLimitRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") th.AssertNoErr(t, res.Err) } @@ -174,7 +175,7 @@ func TestListDSCPMarkingRule(t *testing.T) { fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", rules.DSCPMarkingRulesListOpts{}, - ).EachPage(func(page pagination.Page) (bool, error) { + ).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractDSCPMarkingRules(page) if err != nil { @@ -214,7 +215,7 @@ func TestGetDSCPMarkingRule(t *testing.T) { fmt.Fprintf(w, DSCPMarkingRuleGetResult) }) - r, err := rules.GetDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractDSCPMarkingRule() + r, err := rules.GetDSCPMarkingRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractDSCPMarkingRule() th.AssertNoErr(t, err) th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") @@ -241,7 +242,7 @@ func TestCreateDSCPMarkingRule(t *testing.T) { opts := rules.CreateDSCPMarkingRuleOpts{ DSCPMark: 20, } - r, err := rules.CreateDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractDSCPMarkingRule() + r, err := rules.CreateDSCPMarkingRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractDSCPMarkingRule() th.AssertNoErr(t, err) th.AssertEquals(t, "30a57f4a-336b-4382-8275-d708babd2241", r.ID) @@ -269,7 +270,7 @@ func TestUpdateDSCPMarkingRule(t *testing.T) { opts := rules.UpdateDSCPMarkingRuleOpts{ DSCPMark: &dscpMark, } - r, err := rules.UpdateDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractDSCPMarkingRule() + r, err := rules.UpdateDSCPMarkingRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractDSCPMarkingRule() th.AssertNoErr(t, err) th.AssertEquals(t, "30a57f4a-336b-4382-8275-d708babd2241", r.ID) @@ -286,7 +287,7 @@ func TestDeleteDSCPMarkingRule(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := rules.DeleteDSCPMarkingRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") + res := rules.DeleteDSCPMarkingRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") th.AssertNoErr(t, res.Err) } @@ -310,7 +311,7 @@ func TestListMinimumBandwidthRule(t *testing.T) { fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", rules.MinimumBandwidthRulesListOpts{}, - ).EachPage(func(page pagination.Page) (bool, error) { + ).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractMinimumBandwidthRules(page) if err != nil { @@ -351,7 +352,7 @@ func TestGetMinimumBandwidthRule(t *testing.T) { fmt.Fprintf(w, MinimumBandwidthRulesGetResult) }) - r, err := rules.GetMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractMinimumBandwidthRule() + r, err := rules.GetMinimumBandwidthRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractMinimumBandwidthRule() th.AssertNoErr(t, err) th.AssertEquals(t, r.ID, "30a57f4a-336b-4382-8275-d708babd2241") @@ -379,7 +380,7 @@ func TestCreateMinimumBandwidthRule(t *testing.T) { opts := rules.CreateMinimumBandwidthRuleOpts{ MinKBps: 2000, } - r, err := rules.CreateMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractMinimumBandwidthRule() + r, err := rules.CreateMinimumBandwidthRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", opts).ExtractMinimumBandwidthRule() th.AssertNoErr(t, err) th.AssertEquals(t, 2000, r.MinKBps) @@ -406,7 +407,7 @@ func TestUpdateMinimumBandwidthRule(t *testing.T) { opts := rules.UpdateMinimumBandwidthRuleOpts{ MinKBps: &minKBps, } - r, err := rules.UpdateMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractMinimumBandwidthRule() + r, err := rules.UpdateMinimumBandwidthRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241", opts).ExtractMinimumBandwidthRule() th.AssertNoErr(t, err) th.AssertEquals(t, 500, r.MinKBps) @@ -422,6 +423,6 @@ func TestDeleteMinimumBandwidthRule(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := rules.DeleteMinimumBandwidthRule(fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") + res := rules.DeleteMinimumBandwidthRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/requests.go b/openstack/networking/v2/extensions/qos/ruletypes/requests.go index f10b2b886a..69699bc985 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/requests.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/requests.go @@ -1,6 +1,8 @@ package ruletypes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -13,8 +15,8 @@ func ListRuleTypes(c *gophercloud.ServiceClient) (result pagination.Pager) { } // GetRuleType retrieves a specific QoS RuleType based on its name. -func GetRuleType(c *gophercloud.ServiceClient, name string) (r GetResult) { - resp, err := c.Get(getRuleTypeURL(c, name), &r.Body, nil) +func GetRuleType(ctx context.Context, c *gophercloud.ServiceClient, name string) (r GetResult) { + resp, err := c.Get(ctx, getRuleTypeURL(c, name), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go index dbc4813adb..74eeb49aa4 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -24,7 +25,7 @@ func TestListRuleTypes(t *testing.T) { fmt.Fprint(w, ListRuleTypesResponse) }) - page, err := ruletypes.ListRuleTypes(fake.ServiceClient()).AllPages() + page, err := ruletypes.ListRuleTypes(fake.ServiceClient()).AllPages(context.TODO()) if err != nil { t.Errorf("Failed to list rule types pages: %v", err) return @@ -55,7 +56,7 @@ func TestGetRuleType(t *testing.T) { th.AssertNoErr(t, err) }) - r, err := ruletypes.GetRuleType(fake.ServiceClient(), "bandwidth_limit").Extract() + r, err := ruletypes.GetRuleType(context.TODO(), fake.ServiceClient(), "bandwidth_limit").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "bandwidth_limit", r.Type) diff --git a/openstack/networking/v2/extensions/quotas/requests.go b/openstack/networking/v2/extensions/quotas/requests.go index e42bca16a9..9b9e0f4a22 100644 --- a/openstack/networking/v2/extensions/quotas/requests.go +++ b/openstack/networking/v2/extensions/quotas/requests.go @@ -1,17 +1,21 @@ package quotas -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // Get returns Networking Quotas for a project. -func Get(client *gophercloud.ServiceClient, projectID string) (r GetResult) { - resp, err := client.Get(getURL(client, projectID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, projectID), &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) +func GetDetail(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetDetailResult) { + resp, err := client.Get(ctx, getDetailURL(client, projectID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -62,13 +66,13 @@ func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]interface{}, error) { // Update accepts a UpdateOpts struct and updates an existing Networking Quotas using the // values provided. -func Update(c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToQuotaUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, projectID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/quotas/testing/requests_test.go b/openstack/networking/v2/extensions/quotas/testing/requests_test.go index 487f2fa343..7ed3100242 100644 --- a/openstack/networking/v2/extensions/quotas/testing/requests_test.go +++ b/openstack/networking/v2/extensions/quotas/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -25,7 +26,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, GetResponseRaw) }) - q, err := quotas.Get(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + q, err := quotas.Get(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &GetResponse) } @@ -44,7 +45,7 @@ func TestGetDetail(t *testing.T) { fmt.Fprintf(w, GetDetailedResponseRaw) }) - q, err := quotas.GetDetail(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() + q, err := quotas.GetDetail(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &GetDetailResponse) } @@ -63,7 +64,7 @@ func TestUpdate(t *testing.T) { fmt.Fprintf(w, UpdateRequestResponseRaw) }) - q, err := quotas.Update(fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ + q, err := quotas.Update(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ FloatingIP: gophercloud.IntToPointer(0), Network: gophercloud.IntToPointer(-1), Port: gophercloud.IntToPointer(5), diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index b58ffd6b14..a2ac10da6e 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -1,6 +1,8 @@ package rbacpolicies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -58,8 +60,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific rbac policy based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -100,20 +102,20 @@ func (opts CreateOpts) ToRBACPolicyCreateMap() (map[string]interface{}, error) { // // The tenant ID that is contained in the URI is the tenant that creates the // rbac-policy. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToRBACPolicyCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the rbac-policy associated with it. -func Delete(c *gophercloud.ServiceClient, rbacPolicyID string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, rbacPolicyID), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, rbacPolicyID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, rbacPolicyID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -136,13 +138,13 @@ func (opts UpdateOpts) ToRBACPolicyUpdateMap() (map[string]interface{}, error) { // Update accepts a UpdateOpts struct and updates an existing rbac-policy using the // values provided. -func Update(c *gophercloud.ServiceClient, rbacPolicyID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, rbacPolicyID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToRBACPolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, rbacPolicyID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, rbacPolicyID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go index 2e15e2184c..227ef33912 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -33,7 +34,7 @@ func TestCreate(t *testing.T) { TargetTenant: "6e547a3bcfe44702889fdeff3c3520c3", ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc", } - rbacResult, err := rbacpolicies.Create(fake.ServiceClient(), options).Extract() + rbacResult, err := rbacpolicies.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &rbacPolicy1, rbacResult) @@ -53,7 +54,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, GetResponse) }) - n, err := rbacpolicies.Get(fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c").Extract() + n, err := rbacpolicies.Get(context.TODO(), fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &rbacPolicy1, n) } @@ -75,7 +76,7 @@ func TestList(t *testing.T) { client := fake.ServiceClient() count := 0 - rbacpolicies.List(client, rbacpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + rbacpolicies.List(client, rbacpolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rbacpolicies.ExtractRBACPolicies(page) if err != nil { @@ -115,7 +116,7 @@ func TestListWithAllPages(t *testing.T) { var allRBACpolicies []newRBACPolicy - allPages, err := rbacpolicies.List(client, rbacpolicies.ListOpts{}).AllPages() + allPages, err := rbacpolicies.List(client, rbacpolicies.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) err = rbacpolicies.ExtractRBACPolicesInto(allPages, &allRBACpolicies) @@ -139,7 +140,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := rbacpolicies.Delete(fake.ServiceClient(), "71d55b18-d2f8-4c76-a5e6-e0a3dd114361").ExtractErr() + res := rbacpolicies.Delete(context.TODO(), fake.ServiceClient(), "71d55b18-d2f8-4c76-a5e6-e0a3dd114361").ExtractErr() th.AssertNoErr(t, res) } @@ -161,7 +162,7 @@ func TestUpdate(t *testing.T) { }) options := rbacpolicies.UpdateOpts{TargetTenant: "9d766060b6354c9e8e2da44cab0e8f38"} - rbacResult, err := rbacpolicies.Update(fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c", options).Extract() + rbacResult, err := rbacpolicies.Update(context.TODO(), fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, rbacResult.TargetTenant, "9d766060b6354c9e8e2da44cab0e8f38") diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index b196beca07..caddeeaa83 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -1,6 +1,8 @@ package groups import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -70,13 +72,13 @@ func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { // Create is an operation which provisions a new security group with default // security group rules for the IPv4 and IPv6 ether types. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecGroupCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -103,14 +105,14 @@ func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { } // Update is an operation which updates an existing security group. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSecGroupUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -118,16 +120,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Get retrieves a particular security group based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go index b18e11f111..30c3ab6359 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -28,7 +29,7 @@ func TestList(t *testing.T) { count := 0 - err := groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := groups.ExtractGroups(page) if err != nil { @@ -67,7 +68,7 @@ func TestCreate(t *testing.T) { }) opts := groups.CreateOpts{Name: "new-webservers", Description: "security group for webservers"} - _, err := groups.Create(fake.ServiceClient(), opts).Extract() + _, err := groups.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) } @@ -90,7 +91,7 @@ func TestUpdate(t *testing.T) { }) opts := groups.UpdateOpts{Name: "newer-webservers"} - sg, err := groups.Update(fake.ServiceClient(), "2076db17-a522-4506-91de-c6dd8e837028", opts).Extract() + sg, err := groups.Update(context.TODO(), fake.ServiceClient(), "2076db17-a522-4506-91de-c6dd8e837028", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "newer-webservers", sg.Name) @@ -114,7 +115,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, SecurityGroupGetResponse) }) - sg, err := groups.Get(fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract() + sg, err := groups.Get(context.TODO(), fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "default", sg.Description) @@ -136,6 +137,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := groups.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") + res := groups.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 24147bb772..72372afeb1 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -1,6 +1,8 @@ package rules import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -137,28 +139,28 @@ func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) // Create is an operation which adds a new security group rule and associates it // with an existing security group (whose ID is specified in CreateOpts). -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecGroupRuleCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group rule based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group rule based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index a5779d931d..30a5d15565 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -56,7 +57,7 @@ func TestList(t *testing.T) { count := 0 - rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractRules(page) if err != nil { @@ -158,24 +159,24 @@ func TestCreate(t *testing.T) { RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", } - _, err := rules.Create(fake.ServiceClient(), opts).Extract() + _, err := rules.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) } func TestRequiredCreateOpts(t *testing.T) { - res := rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress}) + res := rules.Create(context.TODO(), fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4}) + res = rules.Create(context.TODO(), fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4}) + res = rules.Create(context.TODO(), fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = rules.Create(fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4, SecGroupID: "something", Protocol: "foo"}) + res = rules.Create(context.TODO(), fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress, EtherType: rules.EtherType4, SecGroupID: "something", Protocol: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -210,7 +211,7 @@ func TestGet(t *testing.T) { `) }) - sr, err := rules.Get(fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract() + sr, err := rules.Get(context.TODO(), fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "egress", sr.Direction) @@ -235,6 +236,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := rules.Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") + res := rules.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index bb1bf2cd56..4656e0eda3 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -1,6 +1,8 @@ package subnetpools import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -69,8 +71,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific subnetpool based on its ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -136,13 +138,13 @@ func (opts CreateOpts) ToSubnetPoolCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new subnetpool on the server. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSubnetPoolCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -208,13 +210,13 @@ func (opts UpdateOpts) ToSubnetPoolUpdateMap() (map[string]interface{}, error) { // Update accepts a UpdateOpts struct and updates an existing subnetpool using the // values provided. -func Update(c *gophercloud.ServiceClient, subnetPoolID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, subnetPoolID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSubnetPoolUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, subnetPoolID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, subnetPoolID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -222,8 +224,8 @@ func Update(c *gophercloud.ServiceClient, subnetPoolID string, opts UpdateOptsBu } // Delete accepts a unique ID and deletes the subnetpool associated with it. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index 526e5965c9..50f479b9d1 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -28,7 +29,7 @@ func TestList(t *testing.T) { count := 0 - err := subnetpools.List(fake.ServiceClient(), subnetpools.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := subnetpools.List(fake.ServiceClient(), subnetpools.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := subnetpools.ExtractSubnetPools(page) if err != nil { @@ -68,7 +69,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, SubnetPoolGetResult) }) - s, err := subnetpools.Get(fake.ServiceClient(), "0a738452-8057-4ad3-89c2-92f6a74afa76").Extract() + s, err := subnetpools.Get(context.TODO(), fake.ServiceClient(), "0a738452-8057-4ad3-89c2-92f6a74afa76").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.ID, "0a738452-8057-4ad3-89c2-92f6a74afa76") @@ -119,7 +120,7 @@ func TestCreate(t *testing.T) { AddressScopeID: "3d4e2e2a-552b-42ad-a16d-820bbf3edaf3", Description: "ipv4 prefixes", } - s, err := subnetpools.Create(fake.ServiceClient(), opts).Extract() + s, err := subnetpools.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_ipv4_pool") @@ -163,7 +164,7 @@ func TestUpdate(t *testing.T) { DefaultQuota: &nullInt, Description: &nullString, } - n, err := subnetpools.Update(fake.ServiceClient(), "099546ca-788d-41e5-a76d-17d8cd282d3e", updateOpts).Extract() + n, err := subnetpools.Update(context.TODO(), fake.ServiceClient(), "099546ca-788d-41e5-a76d-17d8cd282d3e", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "new_subnetpool_name") @@ -189,6 +190,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := subnetpools.Delete(fake.ServiceClient(), "099546ca-788d-41e5-a76d-17d8cd282d3e") + res := subnetpools.Delete(context.TODO(), fake.ServiceClient(), "099546ca-788d-41e5-a76d-17d8cd282d3e") th.AssertNoErr(t, res.Err) } diff --git a/openstack/networking/v2/extensions/testing/delegate_test.go b/openstack/networking/v2/extensions/testing/delegate_test.go index 82f44adad2..c8f847873d 100644 --- a/openstack/networking/v2/extensions/testing/delegate_test.go +++ b/openstack/networking/v2/extensions/testing/delegate_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -40,7 +41,7 @@ func TestList(t *testing.T) { count := 0 - extensions.List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + extensions.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) if err != nil { @@ -95,7 +96,7 @@ func TestGet(t *testing.T) { `) }) - ext, err := extensions.Get(fake.ServiceClient(), "agent").Extract() + ext, err := extensions.Get(context.TODO(), fake.ServiceClient(), "agent").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, ext.Updated, "2013-02-03T10:00:00-00:00") diff --git a/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go b/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go index 43532ae4e7..b99e711b1b 100644 --- a/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunk_details/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -31,7 +32,7 @@ func TestServerWithUsageExt(t *testing.T) { } // Extract basic fields. - err := ports.Get(fake.ServiceClient(), portIDFixture).ExtractInto(&portExt) + err := ports.Get(context.TODO(), fake.ServiceClient(), portIDFixture).ExtractInto(&portExt) th.AssertNoErr(t, err) th.AssertEquals(t, portExt.TrunkDetails.TrunkID, "f170c831-8c55-4ceb-ad13-75eab4a121e5") diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index 3a4c942454..251e317743 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -1,6 +1,8 @@ package trunks import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -30,21 +32,21 @@ func (opts CreateOpts) ToTrunkCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "trunk") } -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { body, err := opts.ToTrunkCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(createURL(c), body, &r.Body, nil) + resp, err := c.Post(ctx, createURL(c), body, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the trunk associated with it. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -106,8 +108,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific trunk based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -126,21 +128,21 @@ func (opts UpdateOpts) ToTrunkUpdateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "trunk") } -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { body, err := opts.ToTrunkUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -func GetSubports(c *gophercloud.ServiceClient, id string) (r GetSubportsResult) { - resp, err := c.Get(getSubportsURL(c, id), &r.Body, &gophercloud.RequestOpts{ +func GetSubports(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetSubportsResult) { + resp, err := c.Get(ctx, getSubportsURL(c, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -159,13 +161,13 @@ func (opts AddSubportsOpts) ToTrunkAddSubportsMap() (map[string]interface{}, err return gophercloud.BuildRequestBody(opts, "") } -func AddSubports(c *gophercloud.ServiceClient, id string, opts AddSubportsOptsBuilder) (r UpdateSubportsResult) { +func AddSubports(ctx context.Context, c *gophercloud.ServiceClient, id string, opts AddSubportsOptsBuilder) (r UpdateSubportsResult) { body, err := opts.ToTrunkAddSubportsMap() if err != nil { r.Err = err return } - resp, err := c.Put(addSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, addSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -188,13 +190,13 @@ func (opts RemoveSubportsOpts) ToTrunkRemoveSubportsMap() (map[string]interface{ return gophercloud.BuildRequestBody(opts, "") } -func RemoveSubports(c *gophercloud.ServiceClient, id string, opts RemoveSubportsOptsBuilder) (r UpdateSubportsResult) { +func RemoveSubports(ctx context.Context, c *gophercloud.ServiceClient, id string, opts RemoveSubportsOptsBuilder) (r UpdateSubportsResult) { body, err := opts.ToTrunkRemoveSubportsMap() if err != nil { r.Err = err return } - resp, err := c.Put(removeSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, removeSubportsURL(c, id), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index c99dc53807..e4d0818ff6 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -45,12 +46,12 @@ func TestCreate(t *testing.T) { }, }, } - _, err := trunks.Create(fake.ServiceClient(), options).Extract() + _, err := trunks.Create(context.TODO(), fake.ServiceClient(), options).Extract() if err == nil { t.Fatalf("Failed to detect missing parent PortID field") } options.PortID = "c373d2fa-3d3b-4492-924c-aff54dea19b6" - n, err := trunks.Create(fake.ServiceClient(), options).Extract() + n, err := trunks.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") @@ -82,7 +83,7 @@ func TestCreateNoSubports(t *testing.T) { AdminStateUp: &iTrue, PortID: "c373d2fa-3d3b-4492-924c-aff54dea19b6", } - n, err := trunks.Create(fake.ServiceClient(), options).Extract() + n, err := trunks.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") @@ -99,7 +100,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := trunks.Delete(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c") + res := trunks.Delete(context.TODO(), fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c") th.AssertNoErr(t, res.Err) } @@ -120,7 +121,7 @@ func TestList(t *testing.T) { client := fake.ServiceClient() count := 0 - trunks.List(client, trunks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + trunks.List(client, trunks.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := trunks.ExtractTrunks(page) if err != nil { @@ -154,7 +155,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, GetResponse) }) - n, err := trunks.Get(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() + n, err := trunks.Get(context.TODO(), fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() th.AssertNoErr(t, err) expectedTrunks, err := ExpectedTrunkSlice() th.AssertNoErr(t, err) @@ -186,7 +187,7 @@ func TestUpdate(t *testing.T) { AdminStateUp: &iFalse, Description: &description, } - n, err := trunks.Update(fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c", options).Extract() + n, err := trunks.Update(context.TODO(), fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, name) @@ -210,7 +211,7 @@ func TestGetSubports(t *testing.T) { client := fake.ServiceClient() - subports, err := trunks.GetSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() + subports, err := trunks.GetSubports(context.TODO(), client, "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedSubports, subports) } @@ -266,7 +267,7 @@ func TestAddSubports(t *testing.T) { Subports: ExpectedSubports, } - trunk, err := trunks.AddSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c", opts).Extract() + trunk, err := trunks.AddSubports(context.TODO(), client, "f6a9718c-5a64-43e3-944f-4deccad8e78c", opts).Extract() th.AssertNoErr(t, err) expectedTrunk, err := ExpectedSubportsAddedTrunk() th.AssertNoErr(t, err) @@ -296,7 +297,7 @@ func TestRemoveSubports(t *testing.T) { {PortID: "4c8b2bff-9824-4d4c-9b60-b3f6621b2bab"}, }, } - trunk, err := trunks.RemoveSubports(client, "f6a9718c-5a64-43e3-944f-4deccad8e78c", opts).Extract() + trunk, err := trunks.RemoveSubports(context.TODO(), client, "f6a9718c-5a64-43e3-944f-4deccad8e78c", opts).Extract() th.AssertNoErr(t, err) expectedTrunk, err := ExpectedSubportsRemovedTrunk() diff --git a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go index 2f4d934f51..d298894de7 100644 --- a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -31,7 +32,7 @@ func TestList(t *testing.T) { } var actual []networkVLANTransparentExt - allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages() + allPages, err := networks.List(fake.ServiceClient(), networks.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &actual) @@ -66,7 +67,7 @@ func TestGet(t *testing.T) { vlantransparent.TransparentExt } - err := networks.Get(fake.ServiceClient(), "db193ab3-96e3-4cb3-8fc5-05f4296d0324").ExtractInto(&s) + err := networks.Get(context.TODO(), fake.ServiceClient(), "db193ab3-96e3-4cb3-8fc5-05f4296d0324").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) @@ -111,7 +112,7 @@ func TestCreate(t *testing.T) { vlantransparent.TransparentExt } - err := networks.Create(fake.ServiceClient(), vlanTransparentCreateOpts).ExtractInto(&s) + err := networks.Create(context.TODO(), fake.ServiceClient(), vlanTransparentCreateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) @@ -158,7 +159,7 @@ func TestUpdate(t *testing.T) { vlantransparent.TransparentExt } - err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", vlanTransparentUpdateOpts).ExtractInto(&s) + err := networks.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", vlanTransparentUpdateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "db193ab3-96e3-4cb3-8fc5-05f4296d0324", s.ID) diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go index 440958ce8b..74272beeee 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -1,6 +1,8 @@ package endpointgroups import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -50,20 +52,20 @@ func (opts CreateOpts) ToEndpointGroupCreateMap() (map[string]interface{}, error // Create accepts a CreateOpts struct and uses the values to create a new // endpoint group. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToEndpointGroupCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular endpoint group based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -110,8 +112,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Delete will permanently delete a particular endpoint group based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -134,13 +136,13 @@ func (opts UpdateOpts) ToEndpointGroupUpdateMap() (map[string]interface{}, error } // Update allows endpoint groups to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToEndpointGroupUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go index f86e65cf97..fd27de2add 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -61,7 +62,7 @@ func TestCreate(t *testing.T) { "10.3.0.0/24", }, } - actual, err := endpointgroups.Create(fake.ServiceClient(), options).Extract() + actual, err := endpointgroups.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expected := endpointgroups.EndpointGroup{ Name: "peers", @@ -107,7 +108,7 @@ func TestGet(t *testing.T) { `) }) - actual, err := endpointgroups.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + actual, err := endpointgroups.Get(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expected := endpointgroups.EndpointGroup{ Name: "peers", @@ -156,7 +157,7 @@ func TestList(t *testing.T) { count := 0 - endpointgroups.List(fake.ServiceClient(), endpointgroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + endpointgroups.List(fake.ServiceClient(), endpointgroups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := endpointgroups.ExtractEndpointGroups(page) if err != nil { @@ -197,7 +198,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := endpointgroups.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + res := endpointgroups.Delete(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } @@ -247,7 +248,7 @@ func TestUpdate(t *testing.T) { Description: &updatedDescription, } - actual, err := endpointgroups.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + actual, err := endpointgroups.Update(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expected := endpointgroups.EndpointGroup{ Name: "updatedname", diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go index 3191ddf432..03f76aedb9 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -1,6 +1,8 @@ package ikepolicies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -99,28 +101,28 @@ func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and uses the values to create a new // IKE policy -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IKE policy based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IKE policy based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -199,13 +201,13 @@ func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { } // Update allows IKE policies to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index 1927ab2556..993c5752cb 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -63,7 +64,7 @@ func TestCreate(t *testing.T) { IKEVersion: ikepolicies.IKEVersionv2, } - actual, err := ikepolicies.Create(fake.ServiceClient(), options).Extract() + actual, err := ikepolicies.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expectedLifetime := ikepolicies.Lifetime{ Units: "seconds", @@ -118,7 +119,7 @@ func TestGet(t *testing.T) { `) }) - actual, err := ikepolicies.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + actual, err := ikepolicies.Get(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expectedLifetime := ikepolicies.Lifetime{ Units: "seconds", @@ -150,7 +151,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := ikepolicies.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + res := ikepolicies.Delete(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } @@ -190,7 +191,7 @@ func TestList(t *testing.T) { count := 0 - ikepolicies.List(fake.ServiceClient(), ikepolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + ikepolicies.List(fake.ServiceClient(), ikepolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := ikepolicies.ExtractPolicies(page) if err != nil { @@ -283,7 +284,7 @@ func TestUpdate(t *testing.T) { }, } - actual, err := ikepolicies.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + actual, err := ikepolicies.Update(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expectedLifetime := ikepolicies.Lifetime{ Units: "seconds", diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go index 2c1836a49f..2bb155885b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -1,6 +1,8 @@ package ipsecpolicies import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -102,28 +104,28 @@ func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and uses the values to create a new // IPSec policy -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPolicyCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IPSec policy based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IPSec policy based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -201,13 +203,13 @@ func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { } // Update allows IPSec policies to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPolicyUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go index 7e50dd2f5a..f8ae98a27b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -77,7 +78,7 @@ func TestCreate(t *testing.T) { Lifetime: &lifetime, Description: "", } - actual, err := ipsecpolicies.Create(fake.ServiceClient(), options).Extract() + actual, err := ipsecpolicies.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expectedLifetime := ipsecpolicies.Lifetime{ Units: "seconds", @@ -132,7 +133,7 @@ func TestGet(t *testing.T) { `) }) - actual, err := ipsecpolicies.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + actual, err := ipsecpolicies.Get(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expectedLifetime := ipsecpolicies.Lifetime{ Units: "seconds", @@ -164,7 +165,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := ipsecpolicies.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + res := ipsecpolicies.Delete(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } @@ -205,7 +206,7 @@ func TestList(t *testing.T) { count := 0 - ipsecpolicies.List(fake.ServiceClient(), ipsecpolicies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + ipsecpolicies.List(fake.ServiceClient(), ipsecpolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := ipsecpolicies.ExtractPolicies(page) if err != nil { @@ -298,7 +299,7 @@ func TestUpdate(t *testing.T) { }, } - actual, err := ipsecpolicies.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + actual, err := ipsecpolicies.Update(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expectedLifetime := ipsecpolicies.Lifetime{ Units: "seconds", diff --git a/openstack/networking/v2/extensions/vpnaas/services/requests.go b/openstack/networking/v2/extensions/vpnaas/services/requests.go index 74bf415db9..2a2b85b431 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/services/requests.go @@ -1,6 +1,8 @@ package services import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -44,21 +46,21 @@ func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and uses the values to create a new // VPN service. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToServiceCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular VPN service based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -87,13 +89,13 @@ func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { } // Update allows VPN services to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToServiceUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -147,8 +149,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a particular VPN service based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go index f1269f5294..e697a5312a 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -61,7 +62,7 @@ func TestCreate(t *testing.T) { AdminStateUp: gophercloud.Enabled, RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", } - actual, err := services.Create(fake.ServiceClient(), options).Extract() + actual, err := services.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expected := services.Service{ RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", @@ -110,7 +111,7 @@ func TestList(t *testing.T) { count := 0 - services.List(fake.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + services.List(fake.ServiceClient(), services.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := services.ExtractServices(page) if err != nil { @@ -169,7 +170,7 @@ func TestGet(t *testing.T) { `) }) - actual, err := services.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + actual, err := services.Get(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expected := services.Service{ Status: "PENDING_CREATE", @@ -194,7 +195,7 @@ func TestDelete(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusNoContent) }) - res := services.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + res := services.Delete(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } @@ -247,7 +248,7 @@ func TestUpdate(t *testing.T) { AdminStateUp: gophercloud.Disabled, } - actual, err := services.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + actual, err := services.Update(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expected := services.Service{ RouterID: "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index 70142f981e..bbe709467d 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -1,6 +1,8 @@ package siteconnections import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -119,28 +121,28 @@ func (opts CreateOpts) ToConnectionCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and uses the values to create a new // IPSec site connection. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToConnectionCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular IPSec site connection based on its // unique ID. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(resourceURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular IPSec site connection based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(resourceURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -232,13 +234,13 @@ func (opts UpdateOpts) ToConnectionUpdateMap() (map[string]interface{}, error) { } // Update allows IPSec site connections to be updated. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToConnectionUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go index 47c5e8cf34..034ff0ac77 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -91,7 +92,7 @@ func TestCreate(t *testing.T) { PeerAddress: "172.24.4.233", PeerID: "172.24.4.233", } - actual, err := siteconnections.Create(fake.ServiceClient(), options).Extract() + actual, err := siteconnections.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) expectedDPD := siteconnections.DPD{ Action: "hold", @@ -134,7 +135,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := siteconnections.Delete(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") + res := siteconnections.Delete(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828") th.AssertNoErr(t, res.Err) } @@ -182,7 +183,7 @@ func TestGet(t *testing.T) { `) }) - actual, err := siteconnections.Get(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() + actual, err := siteconnections.Get(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828").Extract() th.AssertNoErr(t, err) expectedDPD := siteconnections.DPD{ Action: "hold", @@ -262,7 +263,7 @@ func TestList(t *testing.T) { count := 0 - siteconnections.List(fake.ServiceClient(), siteconnections.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + siteconnections.List(fake.ServiceClient(), siteconnections.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := siteconnections.ExtractConnections(page) if err != nil { @@ -377,7 +378,7 @@ func TestUpdate(t *testing.T) { PSK: "updatedsecret", } - actual, err := siteconnections.Update(fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() + actual, err := siteconnections.Update(context.TODO(), fake.ServiceClient(), "5c561d9d-eaea-45f6-ae3e-08d1a7080828", options).Extract() th.AssertNoErr(t, err) expectedDPD := siteconnections.DPD{ diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index d54b681bd7..1fcbaa9e9a 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -1,6 +1,7 @@ package networks import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -61,8 +62,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific network based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -96,13 +97,13 @@ func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { // The tenant ID that is contained in the URI is the tenant that creates the // network. An admin user, however, has the option of specifying another tenant // ID in the CreateOpts struct. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToNetworkCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -133,7 +134,7 @@ func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) { // Update accepts a UpdateOpts struct and updates an existing network using the // values provided. For more information, see the Create function. -func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToNetworkUpdateMap() if err != nil { r.Err = err @@ -149,7 +150,7 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild h[k] = fmt.Sprintf("revision_number=%s", h[k]) } } - resp, err := c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 201}, }) @@ -158,8 +159,8 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild } // Delete accepts a unique ID and deletes the network associated with it. -func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, networkID), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, networkID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index eca6341298..c28dd2ed0f 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -30,7 +31,7 @@ func TestList(t *testing.T) { client := fake.ServiceClient() count := 0 - err := networks.List(client, networks.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := networks.List(client, networks.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := networks.ExtractNetworks(page) if err != nil { @@ -73,7 +74,7 @@ func TestListWithExtensions(t *testing.T) { var allNetworks []networkWithExt - allPages, err := networks.List(client, networks.ListOpts{}).AllPages() + allPages, err := networks.List(client, networks.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) err = networks.ExtractNetworksInto(allPages, &allNetworks) @@ -103,7 +104,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, GetResponse) }) - n, err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + n, err := networks.Get(context.TODO(), fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &Network1, n) th.AssertEquals(t, n.CreatedAt.Format(time.RFC3339), "2019-06-30T04:15:37Z") @@ -129,7 +130,7 @@ func TestGetWithExtensions(t *testing.T) { portsecurity.PortSecurityExt } - err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&networkWithExtensions) + err := networks.Get(context.TODO(), fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&networkWithExtensions) th.AssertNoErr(t, err) th.AssertEquals(t, networkWithExtensions.Status, "ACTIVE") @@ -154,7 +155,7 @@ func TestCreate(t *testing.T) { iTrue := true options := networks.CreateOpts{Name: "private", AdminStateUp: &iTrue} - n, err := networks.Create(fake.ServiceClient(), options).Extract() + n, err := networks.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") @@ -186,7 +187,7 @@ func TestCreateWithOptionalFields(t *testing.T) { TenantID: "12345", AvailabilityZoneHints: []string{"zone1", "zone2"}, } - _, err := networks.Create(fake.ServiceClient(), options).Extract() + _, err := networks.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) } @@ -210,7 +211,7 @@ func TestUpdate(t *testing.T) { iTrue, iFalse := true, false name := "new_network_name" options := networks.UpdateOpts{Name: &name, AdminStateUp: &iFalse, Shared: &iTrue} - n, err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + n, err := networks.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "new_network_name") @@ -256,12 +257,12 @@ func TestUpdateRevision(t *testing.T) { 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() + _, err := networks.Update(context.TODO(), 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() + _, err = networks.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03d", options).Extract() th.AssertNoErr(t, err) } @@ -275,7 +276,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := networks.Delete(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c") + res := networks.Delete(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c") th.AssertNoErr(t, res.Err) } @@ -308,7 +309,7 @@ func TestCreatePortSecurity(t *testing.T) { PortSecurityEnabled: &iFalse, } - err := networks.Create(fake.ServiceClient(), createOpts).ExtractInto(&networkWithExtensions) + err := networks.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractInto(&networkWithExtensions) th.AssertNoErr(t, err) th.AssertEquals(t, networkWithExtensions.Status, "ACTIVE") @@ -344,7 +345,7 @@ func TestUpdatePortSecurity(t *testing.T) { PortSecurityEnabled: &iFalse, } - err := networks.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", updateOpts).ExtractInto(&networkWithExtensions) + err := networks.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", updateOpts).ExtractInto(&networkWithExtensions) th.AssertNoErr(t, err) th.AssertEquals(t, networkWithExtensions.Name, "private") diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 1d021f2ae1..750548af06 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -1,6 +1,7 @@ package ports import ( + "context" "fmt" "net/url" "slices" @@ -99,8 +100,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific port based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -166,13 +167,13 @@ func AddValueSpecs(body map[string]interface{}) (map[string]interface{}, error) // Create accepts a CreateOpts struct and creates a new network using the values // provided. You must remember to provide a NetworkID value. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToPortCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -213,7 +214,7 @@ func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) { // Update accepts a UpdateOpts struct and updates an existing port using the // values provided. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToPortUpdateMap() if err != nil { r.Err = err @@ -229,7 +230,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r h[k] = fmt.Sprintf("revision_number=%s", h[k]) } } - resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 201}, }) @@ -238,8 +239,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete accepts a unique ID and deletes the port associated with it. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index e93391cf77..7c3569b70b 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "net/url" @@ -31,7 +32,7 @@ func TestList(t *testing.T) { count := 0 - err := ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := ports.ExtractPorts(page) if err != nil { @@ -95,7 +96,7 @@ func TestListWithExtensions(t *testing.T) { var allPorts []portWithExt - allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages() + allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) err = ports.ExtractPortsInto(allPages, &allPorts) @@ -119,7 +120,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, GetResponse) }) - n, err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract() + n, err := ports.Get(context.TODO(), fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "ACTIVE") @@ -159,7 +160,7 @@ func TestGetWithExtensions(t *testing.T) { portsecurity.PortSecurityExt } - err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&portWithExtensions) + err := ports.Get(context.TODO(), fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&portWithExtensions) th.AssertNoErr(t, err) th.AssertEquals(t, portWithExtensions.Status, "ACTIVE") @@ -196,7 +197,7 @@ func TestCreate(t *testing.T) { {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } - n, err := ports.Create(fake.ServiceClient(), options).Extract() + n, err := ports.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "DOWN") @@ -245,7 +246,7 @@ func TestCreateOmitSecurityGroups(t *testing.T) { {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } - n, err := ports.Create(fake.ServiceClient(), options).Extract() + n, err := ports.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "DOWN") @@ -295,7 +296,7 @@ func TestCreateWithNoSecurityGroup(t *testing.T) { {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } - n, err := ports.Create(fake.ServiceClient(), options).Extract() + n, err := ports.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "DOWN") @@ -342,7 +343,7 @@ func TestCreateWithPropagateUplinkStatus(t *testing.T) { }, PropagateUplinkStatus: &propagateUplinkStatus, } - n, err := ports.Create(fake.ServiceClient(), options).Extract() + n, err := ports.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "DOWN") @@ -392,7 +393,7 @@ func TestCreateWithValueSpecs(t *testing.T) { "test": "value", }, } - n, err := ports.Create(fake.ServiceClient(), options).Extract() + n, err := ports.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Status, "DOWN") @@ -448,7 +449,7 @@ func TestCreateWithInvalidValueSpecs(t *testing.T) { } // We expect an error here since we used a fobidden key in the value specs. - _, err := ports.Create(fake.ServiceClient(), options).Extract() + _, err := ports.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertErr(t, err) options.ValueSpecs = &map[string]string{ @@ -457,12 +458,12 @@ func TestCreateWithInvalidValueSpecs(t *testing.T) { } // We expect an error here since the value specs would overwrite an existing field. - _, err = ports.Create(fake.ServiceClient(), options).Extract() + _, err = ports.Create(context.TODO(), fake.ServiceClient(), options).Extract() th.AssertErr(t, err) } func TestRequiredCreateOpts(t *testing.T) { - res := ports.Create(fake.ServiceClient(), ports.CreateOpts{}) + res := ports.Create(context.TODO(), fake.ServiceClient(), ports.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -509,7 +510,7 @@ func TestCreatePortSecurity(t *testing.T) { PortSecurityEnabled: &iFalse, } - err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&portWithExt) + err := ports.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractInto(&portWithExt) th.AssertNoErr(t, err) th.AssertEquals(t, portWithExt.Status, "DOWN") @@ -545,7 +546,7 @@ func TestUpdate(t *testing.T) { }, } - s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + s, err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") @@ -586,7 +587,7 @@ func TestUpdateOmitSecurityGroups(t *testing.T) { }, } - s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + s, err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") @@ -621,7 +622,7 @@ func TestUpdatePropagateUplinkStatus(t *testing.T) { PropagateUplinkStatus: &propagateUplinkStatus, } - s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + s, err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s.PropagateUplinkStatus, propagateUplinkStatus) @@ -650,7 +651,7 @@ func TestUpdateValueSpecs(t *testing.T) { }, } - _, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + _, err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) } @@ -683,7 +684,7 @@ func TestUpdatePortSecurity(t *testing.T) { PortSecurityEnabled: &iFalse, } - err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithExt) + err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithExt) th.AssertNoErr(t, err) th.AssertEquals(t, portWithExt.Status, "DOWN") @@ -733,12 +734,12 @@ func TestUpdateRevision(t *testing.T) { {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, } - _, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + _, err := ports.Update(context.TODO(), 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() + _, err = ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0e", options).Extract() th.AssertNoErr(t, err) } @@ -771,7 +772,7 @@ func TestRemoveSecurityGroups(t *testing.T) { }, } - s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + s, err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") @@ -811,7 +812,7 @@ func TestRemoveAllowedAddressPairs(t *testing.T) { AllowedAddressPairs: &[]ports.AddressPair{}, } - s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + s, err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") @@ -848,7 +849,7 @@ func TestDontUpdateAllowedAddressPairs(t *testing.T) { SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"}, } - s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() + s, err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "new_port_name") @@ -871,7 +872,7 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := ports.Delete(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d") + res := ports.Delete(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d") th.AssertNoErr(t, res.Err) } @@ -894,7 +895,7 @@ func TestGetWithExtraDHCPOpts(t *testing.T) { extradhcpopts.ExtraDHCPOptsExt } - err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) + err := ports.Get(context.TODO(), fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "ACTIVE") @@ -960,7 +961,7 @@ func TestCreateWithExtraDHCPOpts(t *testing.T) { extradhcpopts.ExtraDHCPOptsExt } - err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s) + err := ports.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "DOWN") @@ -1025,7 +1026,7 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { extradhcpopts.ExtraDHCPOptsExt } - err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) + err := ports.Update(context.TODO(), fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, s.Status, "DOWN") diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 951a734a7a..89ca3ce8bf 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -1,6 +1,7 @@ package subnets import ( + "context" "fmt" "github.com/gophercloud/gophercloud/v2" @@ -70,8 +71,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retrieves a specific subnet based on its unique ID. -func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(getURL(c, id), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -160,13 +161,13 @@ func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and creates a new subnet using the values // provided. You must remember to provide a valid NetworkID, CIDR and IP // version. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSubnetCreateMap() if err != nil { r.Err = err return } - resp, err := c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -228,7 +229,7 @@ func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) { // Update accepts a UpdateOpts struct and updates an existing subnet using the // values provided. -func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSubnetUpdateMap() if err != nil { r.Err = err @@ -245,7 +246,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } } - resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 201}, }) @@ -254,8 +255,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r } // Delete accepts a unique ID and deletes the subnet associated with it. -func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, id), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index 4bf688fd75..dfa3c5bf33 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -27,7 +28,7 @@ func TestList(t *testing.T) { count := 0 - subnets.List(fake.ServiceClient(), subnets.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + subnets.List(fake.ServiceClient(), subnets.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := subnets.ExtractSubnets(page) if err != nil { @@ -66,7 +67,7 @@ func TestGet(t *testing.T) { fmt.Fprintf(w, SubnetGetResult) }) - s, err := subnets.Get(fake.ServiceClient(), "54d6f61d-db07-451c-9ab3-b9609b6b6f0b").Extract() + s, err := subnets.Get(context.TODO(), fake.ServiceClient(), "54d6f61d-db07-451c-9ab3-b9609b6b6f0b").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_subnet") @@ -124,7 +125,7 @@ func TestCreate(t *testing.T) { }, SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", } - s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + s, err := subnets.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") @@ -178,7 +179,7 @@ func TestCreateNoGateway(t *testing.T) { }, DNSNameservers: []string{}, } - s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + s, err := subnets.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") @@ -227,7 +228,7 @@ func TestCreateDefaultGateway(t *testing.T) { }, DNSNameservers: []string{}, } - s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + s, err := subnets.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") @@ -273,7 +274,7 @@ func TestCreateIPv6RaAddressMode(t *testing.T) { IPv6AddressMode: "slaac", IPv6RAMode: "slaac", } - s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + s, err := subnets.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") @@ -314,7 +315,7 @@ func TestCreateWithNoCIDR(t *testing.T) { }, SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", } - s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + s, err := subnets.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") @@ -363,7 +364,7 @@ func TestCreateWithPrefixlen(t *testing.T) { SubnetPoolID: "b80340c7-9960-4f67-a99c-02501656284b", Prefixlen: 12, } - s, err := subnets.Create(fake.ServiceClient(), opts).Extract() + s, err := subnets.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") @@ -386,17 +387,17 @@ func TestCreateWithPrefixlen(t *testing.T) { } func TestRequiredCreateOpts(t *testing.T) { - res := subnets.Create(fake.ServiceClient(), subnets.CreateOpts{}) + res := subnets.Create(context.TODO(), fake.ServiceClient(), subnets.CreateOpts{}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = subnets.Create(fake.ServiceClient(), subnets.CreateOpts{NetworkID: "foo"}) + res = subnets.Create(context.TODO(), fake.ServiceClient(), subnets.CreateOpts{NetworkID: "foo"}) if res.Err == nil { t.Fatalf("Expected error, got none") } - res = subnets.Create(fake.ServiceClient(), subnets.CreateOpts{NetworkID: "foo", CIDR: "bar", IPVersion: 40}) + res = subnets.Create(context.TODO(), fake.ServiceClient(), subnets.CreateOpts{NetworkID: "foo", CIDR: "bar", IPVersion: 40}) if res.Err == nil { t.Fatalf("Expected error, got none") } @@ -428,7 +429,7 @@ func TestUpdate(t *testing.T) { {NextHop: "bar"}, }, } - s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + s, err := subnets.Update(context.TODO(), fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") @@ -458,7 +459,7 @@ func TestUpdateGateway(t *testing.T) { Name: &name, GatewayIP: &gatewayIP, } - s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + s, err := subnets.Update(context.TODO(), fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") @@ -489,7 +490,7 @@ func TestUpdateRemoveGateway(t *testing.T) { Name: &name, GatewayIP: &noGateway, } - s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + s, err := subnets.Update(context.TODO(), fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") @@ -526,7 +527,7 @@ func TestUpdateHostRoutes(t *testing.T) { Name: &name, HostRoutes: &HostRoutes, } - s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + s, err := subnets.Update(context.TODO(), fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") @@ -555,7 +556,7 @@ func TestUpdateRemoveHostRoutes(t *testing.T) { opts := subnets.UpdateOpts{ HostRoutes: &noHostRoutes, } - s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + s, err := subnets.Update(context.TODO(), fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") @@ -590,7 +591,7 @@ func TestUpdateAllocationPool(t *testing.T) { }, }, } - s, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + s, err := subnets.Update(context.TODO(), fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "my_new_subnet") @@ -644,12 +645,12 @@ func TestUpdateRevision(t *testing.T) { {NextHop: "bar"}, }, } - _, err := subnets.Update(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b", opts).Extract() + _, err := subnets.Update(context.TODO(), 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() + _, err = subnets.Update(context.TODO(), fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1c", opts).Extract() th.AssertNoErr(t, err) } @@ -663,6 +664,6 @@ func TestDelete(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - res := subnets.Delete(fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b") + res := subnets.Delete(context.TODO(), fake.ServiceClient(), "08eae331-0402-425a-923c-34f7cfe39c1b") th.AssertNoErr(t, res.Err) } diff --git a/openstack/objectstorage/v1/accounts/requests.go b/openstack/objectstorage/v1/accounts/requests.go index 1dcd8d0cd2..be946db4ac 100644 --- a/openstack/objectstorage/v1/accounts/requests.go +++ b/openstack/objectstorage/v1/accounts/requests.go @@ -1,6 +1,10 @@ package accounts -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // GetOptsBuilder allows extensions to add additional headers to the Get // request. @@ -23,7 +27,7 @@ func (opts GetOpts) ToAccountGetMap() (map[string]string, error) { // custom metadata, call the ExtractMetadata method on the GetResult. To extract // all the headers that are returned (including the metadata), call the // Extract method on the GetResult. -func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { +func Get(ctx context.Context, c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToAccountGetMap() @@ -35,7 +39,7 @@ func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { h[k] = v } } - resp, err := c.Head(getURL(c), &gophercloud.RequestOpts{ + resp, err := c.Head(ctx, getURL(c), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{204}, }) @@ -80,7 +84,7 @@ func (opts UpdateOpts) ToAccountUpdateMap() (map[string]string, error) { // Update is a function that creates, updates, or deletes an account's metadata. // To extract the headers returned, call the Extract method on the UpdateResult. -func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, opts UpdateOptsBuilder) (r UpdateResult) { h := make(map[string]string) if opts != nil { headers, err := opts.ToAccountUpdateMap() @@ -92,7 +96,7 @@ func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) (r UpdateResul h[k] = v } } - resp, err := c.Request("POST", updateURL(c), &gophercloud.RequestOpts{ + resp, err := c.Request(ctx, "POST", updateURL(c), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) diff --git a/openstack/objectstorage/v1/accounts/testing/requests_test.go b/openstack/objectstorage/v1/accounts/testing/requests_test.go index ad50a7cba1..6ed29c1ae5 100644 --- a/openstack/objectstorage/v1/accounts/testing/requests_test.go +++ b/openstack/objectstorage/v1/accounts/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -20,7 +21,7 @@ func TestUpdateAccount(t *testing.T) { ContentType: new(string), DetectContentType: new(bool), } - res := accounts.Update(fake.ServiceClient(), options) + res := accounts.Update(context.TODO(), fake.ServiceClient(), options) th.AssertNoErr(t, res.Err) expected := &accounts.UpdateHeader{ @@ -37,7 +38,7 @@ func TestGetAccount(t *testing.T) { HandleGetAccountSuccessfully(t) expectedMetadata := map[string]string{"Subject": "books", "Quota-Bytes": "42", "Temp-Url-Key": "testsecret"} - res := accounts.Get(fake.ServiceClient(), &accounts.GetOpts{}) + res := accounts.Get(context.TODO(), fake.ServiceClient(), &accounts.GetOpts{}) th.AssertNoErr(t, res.Err) actualMetadata, _ := res.ExtractMetadata() th.CheckDeepEquals(t, expectedMetadata, actualMetadata) @@ -64,7 +65,7 @@ func TestGetAccountNoQuota(t *testing.T) { HandleGetAccountNoQuotaSuccessfully(t) expectedMetadata := map[string]string{"Subject": "books"} - res := accounts.Get(fake.ServiceClient(), &accounts.GetOpts{}) + res := accounts.Get(context.TODO(), fake.ServiceClient(), &accounts.GetOpts{}) th.AssertNoErr(t, res.Err) actualMetadata, _ := res.ExtractMetadata() th.CheckDeepEquals(t, expectedMetadata, actualMetadata) diff --git a/openstack/objectstorage/v1/containers/requests.go b/openstack/objectstorage/v1/containers/requests.go index e2e7a21d7f..f4714d6ff6 100644 --- a/openstack/objectstorage/v1/containers/requests.go +++ b/openstack/objectstorage/v1/containers/requests.go @@ -2,6 +2,7 @@ package containers import ( "bytes" + "context" "net/url" "github.com/gophercloud/gophercloud/v2" @@ -97,7 +98,7 @@ 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) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, containerName string, opts CreateOptsBuilder) (r CreateResult) { url, err := createURL(c, containerName) if err != nil { r.Err = err @@ -114,7 +115,7 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB h[k] = v } } - resp, err := c.Request("PUT", url, &gophercloud.RequestOpts{ + resp, err := c.Request(ctx, "PUT", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) @@ -123,7 +124,7 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB } // BulkDelete is a function that bulk deletes containers. -func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDeleteResult) { +func BulkDelete(ctx context.Context, c *gophercloud.ServiceClient, containers []string) (r BulkDeleteResult) { var body bytes.Buffer for i := range containers { @@ -135,7 +136,7 @@ func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDelete body.WriteRune('\n') } - resp, err := c.Post(bulkDeleteURL(c), &body, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(ctx, bulkDeleteURL(c), &body, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{ "Accept": "application/json", "Content-Type": "text/plain", @@ -147,13 +148,13 @@ 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) { +func Delete(ctx context.Context, c *gophercloud.ServiceClient, containerName string) (r DeleteResult) { url, err := deleteURL(c, containerName) if err != nil { r.Err = err return } - resp, err := c.Delete(url, nil) + resp, err := c.Delete(ctx, url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -204,7 +205,7 @@ 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) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, containerName string, opts UpdateOptsBuilder) (r UpdateResult) { url, err := updateURL(c, containerName) if err != nil { r.Err = err @@ -222,7 +223,7 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB h[k] = v } } - resp, err := c.Request("POST", url, &gophercloud.RequestOpts{ + resp, err := c.Request(ctx, "POST", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) @@ -249,7 +250,7 @@ func (opts GetOpts) ToContainerGetMap() (map[string]string, error) { // Get is a function that retrieves the metadata of a container. To extract just // the custom metadata, pass the GetResult response to the ExtractMetadata // function. -func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder) (r GetResult) { +func Get(ctx context.Context, c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder) (r GetResult) { url, err := getURL(c, containerName) if err != nil { r.Err = err @@ -267,7 +268,7 @@ func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder h[k] = v } } - resp, err := c.Head(url, &gophercloud.RequestOpts{ + resp, err := c.Head(ctx, url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index 890a6cf8de..70a331410f 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -38,7 +39,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleCreateContainerSuccessfully(t) - _, err := containers.Create(fake.ServiceClient(), tc.containerName, nil).Extract() + _, err := containers.Create(context.TODO(), fake.ServiceClient(), tc.containerName, nil).Extract() th.CheckErr(t, err, &tc.expectedError) }) t.Run("delete", func(t *testing.T) { @@ -46,7 +47,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleDeleteContainerSuccessfully(t, WithPath("/")) - res := containers.Delete(fake.ServiceClient(), tc.containerName) + res := containers.Delete(context.TODO(), fake.ServiceClient(), tc.containerName) th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("update", func(t *testing.T) { @@ -63,7 +64,7 @@ func TestContainerNames(t *testing.T) { ContainerSyncKey: new(string), ContentType: &contentType, } - res := containers.Update(fake.ServiceClient(), tc.containerName, options) + res := containers.Update(context.TODO(), fake.ServiceClient(), tc.containerName, options) th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("get", func(t *testing.T) { @@ -71,7 +72,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleGetContainerSuccessfully(t, WithPath("/")) - res := containers.Get(fake.ServiceClient(), tc.containerName, nil) + res := containers.Get(context.TODO(), fake.ServiceClient(), tc.containerName, nil) _, err := res.ExtractMetadata() th.CheckErr(t, err, &tc.expectedError) @@ -88,7 +89,7 @@ func TestListContainerInfo(t *testing.T) { HandleListContainerInfoSuccessfully(t) count := 0 - err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractInfo(page) th.AssertNoErr(t, err) @@ -106,7 +107,7 @@ func TestListAllContainerInfo(t *testing.T) { defer th.TeardownHTTP() HandleListContainerInfoSuccessfully(t) - allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages() + allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := containers.ExtractInfo(allPages) th.AssertNoErr(t, err) @@ -119,7 +120,7 @@ func TestListContainerNames(t *testing.T) { HandleListContainerInfoSuccessfully(t) count := 0 - err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := containers.ExtractNames(page) if err != nil { @@ -140,7 +141,7 @@ func TestListAllContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleListContainerInfoSuccessfully(t) - allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages() + allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := containers.ExtractNames(allPages) th.AssertNoErr(t, err) @@ -152,7 +153,7 @@ func TestListZeroContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleListZeroContainerNames204(t) - allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages() + allPages, err := containers.List(fake.ServiceClient(), &containers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := containers.ExtractNames(allPages) th.AssertNoErr(t, err) @@ -165,7 +166,7 @@ func TestCreateContainer(t *testing.T) { HandleCreateContainerSuccessfully(t) options := containers.CreateOpts{ContentType: "application/json", Metadata: map[string]string{"foo": "bar"}} - res := containers.Create(fake.ServiceClient(), "testContainer", options) + res := containers.Create(context.TODO(), fake.ServiceClient(), "testContainer", options) th.CheckEquals(t, "bar", res.Header["X-Container-Meta-Foo"][0]) expected := &containers.CreateHeader{ @@ -184,7 +185,7 @@ func TestDeleteContainer(t *testing.T) { defer th.TeardownHTTP() HandleDeleteContainerSuccessfully(t) - res := containers.Delete(fake.ServiceClient(), "testContainer") + res := containers.Delete(context.TODO(), fake.ServiceClient(), "testContainer") th.AssertNoErr(t, res.Err) } @@ -200,7 +201,7 @@ func TestBulkDelete(t *testing.T) { Errors: [][]string{}, } - resp, err := containers.BulkDelete(fake.ServiceClient(), []string{"testContainer1", "testContainer2"}).Extract() + resp, err := containers.BulkDelete(context.TODO(), fake.ServiceClient(), []string{"testContainer1", "testContainer2"}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, *resp) } @@ -219,7 +220,7 @@ func TestUpdateContainer(t *testing.T) { ContainerSyncKey: new(string), ContentType: &contentType, } - res := containers.Update(fake.ServiceClient(), "testContainer", options) + res := containers.Update(context.TODO(), fake.ServiceClient(), "testContainer", options) th.AssertNoErr(t, res.Err) } @@ -231,7 +232,7 @@ func TestGetContainer(t *testing.T) { getOpts := containers.GetOpts{ Newest: true, } - res := containers.Get(fake.ServiceClient(), "testContainer", getOpts) + res := containers.Get(context.TODO(), fake.ServiceClient(), "testContainer", getOpts) _, err := res.ExtractMetadata() th.AssertNoErr(t, err) @@ -268,7 +269,7 @@ func TestUpdateContainerVersioningOff(t *testing.T) { ContentType: &contentType, VersionsEnabled: new(bool), } - _, err := containers.Update(fake.ServiceClient(), "testVersioning", options).Extract() + _, err := containers.Update(context.TODO(), fake.ServiceClient(), "testVersioning", options).Extract() th.AssertNoErr(t, err) } @@ -288,6 +289,6 @@ func TestUpdateContainerVersioningOn(t *testing.T) { ContentType: &contentType, VersionsEnabled: &iTrue, } - _, err := containers.Update(fake.ServiceClient(), "testVersioning", options).Extract() + _, err := containers.Update(context.TODO(), 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 22c476de55..44a46d76d0 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -2,6 +2,7 @@ package objects import ( "bytes" + "context" "crypto/hmac" "crypto/md5" "crypto/sha1" @@ -140,7 +141,7 @@ 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, call the DownloadResult method ExtractContent, // after checking DownloadResult's Err field. -func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) { +func Download(ctx context.Context, c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) { url, err := downloadURL(c, containerName, objectName) if err != nil { r.Err = err @@ -160,7 +161,7 @@ func Download(c *gophercloud.ServiceClient, containerName, objectName string, op url += query } - resp, err := c.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := c.Get(ctx, url, nil, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 206, 304}, KeepResponseBody: true, @@ -251,7 +252,7 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str // object. If the returned response's ETag header fails to match the local // 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) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, containerName, objectName string, opts CreateOptsBuilder) (r CreateResult) { url, err := createURL(c, containerName, objectName) if err != nil { r.Err = err @@ -272,7 +273,7 @@ func Create(c *gophercloud.ServiceClient, containerName, objectName string, opts b = tmpB } - resp, err := c.Put(url, b, nil, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, url, b, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -328,7 +329,7 @@ func (opts CopyOpts) ToObjectCopyQuery() (string, error) { } // Copy is a function that copies one object to another. -func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) (r CopyResult) { +func Copy(ctx context.Context, c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) (r CopyResult) { h := make(map[string]string) headers, err := opts.ToObjectCopyMap() if err != nil { @@ -373,7 +374,7 @@ func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts C url += query } - resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{ + resp, err := c.Request(ctx, "COPY", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) @@ -400,7 +401,7 @@ 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) { +func Delete(ctx context.Context, c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) (r DeleteResult) { url, err := deleteURL(c, containerName, objectName) if err != nil { r.Err = err @@ -414,7 +415,7 @@ func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts } url += query } - resp, err := c.Delete(url, nil) + resp, err := c.Delete(ctx, url, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -450,7 +451,7 @@ func (opts GetOpts) ToObjectGetParams() (map[string]string, string, error) { // Get is a function that retrieves the metadata of an object. To extract just // the custom metadata, pass the GetResult response to the ExtractMetadata // function. -func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) (r GetResult) { +func Get(ctx context.Context, c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) (r GetResult) { url, err := getURL(c, containerName, objectName) if err != nil { r.Err = err @@ -469,7 +470,7 @@ func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts Ge url += query } - resp, err := c.Head(url, &gophercloud.RequestOpts{ + resp, err := c.Head(ctx, url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) @@ -514,7 +515,7 @@ 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) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) (r UpdateResult) { url, err := updateURL(c, containerName, objectName) if err != nil { r.Err = err @@ -532,7 +533,7 @@ func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts h[k] = v } } - resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{ + resp, err := c.Post(ctx, url, nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -589,7 +590,7 @@ type CreateTempURLOpts struct { // CreateTempURL is a function for creating a temporary URL for an object. It // 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) { +func CreateTempURL(ctx context.Context, c *gophercloud.ServiceClient, containerName, objectName string, opts CreateTempURLOpts) (string, error) { url, err := getURL(c, containerName, objectName) if err != nil { return "", err @@ -616,14 +617,14 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin tempURLKey := opts.TempURLKey if tempURLKey == "" { // fallback to a container TempURL key - getHeader, err := containers.Get(c, containerName, nil).Extract() + getHeader, err := containers.Get(ctx, 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() + getHeader, err := accounts.Get(ctx, c, nil).Extract() if err != nil { return "", err } @@ -663,7 +664,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin // 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) { +func BulkDelete(ctx context.Context, c *gophercloud.ServiceClient, container string, objects []string) (r BulkDeleteResult) { if err := v1.CheckContainerName(container); err != nil { r.Err = err return @@ -683,7 +684,7 @@ func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string body.WriteRune('\n') } - resp, err := c.Post(bulkDeleteURL(c), &body, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(ctx, bulkDeleteURL(c), &body, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{ "Accept": "application/json", "Content-Type": "text/plain", diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index c11c7d8df6..bb9b5d5ac5 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -2,6 +2,7 @@ package testing import ( "bytes" + "context" "crypto/md5" "fmt" "io" @@ -42,7 +43,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleListObjectsInfoSuccessfully(t, WithPath("/")) - _, err := objects.List(fake.ServiceClient(), tc.containerName, nil).AllPages() + _, err := objects.List(fake.ServiceClient(), tc.containerName, nil).AllPages(context.TODO()) th.CheckErr(t, err, &tc.expectedError) }) t.Run("download", func(t *testing.T) { @@ -50,7 +51,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleDownloadObjectSuccessfully(t, WithPath("/")) - _, err := objects.Download(fake.ServiceClient(), tc.containerName, "testObject", nil).Extract() + _, err := objects.Download(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", nil).Extract() th.CheckErr(t, err, &tc.expectedError) }) t.Run("create", func(t *testing.T) { @@ -59,7 +60,7 @@ func TestContainerNames(t *testing.T) { content := "Ceci n'est pas une pipe" HandleCreateTextObjectSuccessfully(t, content, WithPath("/")) - res := objects.Create(fake.ServiceClient(), tc.containerName, "testObject", &objects.CreateOpts{ + res := objects.Create(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", &objects.CreateOpts{ ContentType: "text/plain", Content: strings.NewReader(content), }) @@ -70,7 +71,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleDeleteObjectSuccessfully(t, WithPath("/")) - res := objects.Delete(fake.ServiceClient(), tc.containerName, "testObject", nil) + res := objects.Delete(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", nil) th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("get", func(t *testing.T) { @@ -78,7 +79,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleGetObjectSuccessfully(t, WithPath("/")) - _, err := objects.Get(fake.ServiceClient(), tc.containerName, "testObject", nil).ExtractMetadata() + _, err := objects.Get(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", nil).ExtractMetadata() th.CheckErr(t, err, &tc.expectedError) }) t.Run("update", func(t *testing.T) { @@ -86,7 +87,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleUpdateObjectSuccessfully(t) - res := objects.Update(fake.ServiceClient(), tc.containerName, "testObject", &objects.UpdateOpts{ + res := objects.Update(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", &objects.UpdateOpts{ Metadata: map[string]string{"Gophercloud-Test": "objects"}, }) th.CheckErr(t, res.Err, &tc.expectedError) @@ -104,7 +105,7 @@ func TestContainerNames(t *testing.T) { // 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{ + _, err := objects.CreateTempURL(context.TODO(), 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), @@ -117,7 +118,7 @@ func TestContainerNames(t *testing.T) { defer th.TeardownHTTP() HandleBulkDeleteSuccessfully(t) - res := objects.BulkDelete(fake.ServiceClient(), tc.containerName, []string{"testObject"}) + res := objects.BulkDelete(context.TODO(), fake.ServiceClient(), tc.containerName, []string{"testObject"}) th.CheckErr(t, res.Err, &tc.expectedError) }) }) @@ -129,7 +130,7 @@ func TestDownloadReader(t *testing.T) { defer th.TeardownHTTP() HandleDownloadObjectSuccessfully(t) - response := objects.Download(fake.ServiceClient(), "testContainer", "testObject", nil) + response := objects.Download(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", nil) defer response.Body.Close() // Check reader @@ -143,7 +144,7 @@ func TestDownloadExtraction(t *testing.T) { defer th.TeardownHTTP() HandleDownloadObjectSuccessfully(t) - response := objects.Download(fake.ServiceClient(), "testContainer", "testObject", nil) + response := objects.Download(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", nil) // Check []byte extraction bytes, err := response.ExtractContent() @@ -170,14 +171,14 @@ func TestDownloadWithLastModified(t *testing.T) { options1 := &objects.DownloadOpts{ IfUnmodifiedSince: time.Date(2009, time.November, 10, 22, 59, 59, 0, time.UTC), } - response1 := objects.Download(fake.ServiceClient(), "testContainer", "testObject", options1) + response1 := objects.Download(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options1) _, err1 := response1.Extract() th.AssertErr(t, err1) options2 := &objects.DownloadOpts{ IfModifiedSince: time.Date(2009, time.November, 10, 23, 0, 1, 0, time.UTC), } - response2 := objects.Download(fake.ServiceClient(), "testContainer", "testObject", options2) + response2 := objects.Download(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options2) content, err2 := response2.ExtractContent() th.AssertNoErr(t, err2) th.AssertEquals(t, 0, len(content)) @@ -190,7 +191,7 @@ func TestListObjectInfo(t *testing.T) { count := 0 options := &objects.ListOpts{} - err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { + err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractInfo(page) th.AssertNoErr(t, err) @@ -210,7 +211,7 @@ func TestListObjectSubdir(t *testing.T) { count := 0 options := &objects.ListOpts{Prefix: "", Delimiter: "/"} - err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { + err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractInfo(page) th.AssertNoErr(t, err) @@ -231,7 +232,7 @@ func TestListObjectNames(t *testing.T) { // Check without delimiter. count := 0 options := &objects.ListOpts{} - err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { + err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) if err != nil { @@ -249,7 +250,7 @@ func TestListObjectNames(t *testing.T) { // Check with delimiter. count = 0 options = &objects.ListOpts{Delimiter: "/"} - err = objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { + err = objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) if err != nil { @@ -272,7 +273,7 @@ func TestListZeroObjectNames204(t *testing.T) { count := 0 options := &objects.ListOpts{} - err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { + err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := objects.ExtractNames(page) if err != nil { @@ -297,7 +298,7 @@ func TestCreateObject(t *testing.T) { HandleCreateTextObjectSuccessfully(t, content) options := &objects.CreateOpts{ContentType: "text/plain", Content: strings.NewReader(content)} - res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", options) + res := objects.Create(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) } @@ -313,7 +314,7 @@ func TestCreateObjectWithCacheControl(t *testing.T) { CacheControl: `max-age="3600", public`, Content: strings.NewReader(content), } - res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", options) + res := objects.Create(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) } @@ -325,7 +326,7 @@ func TestCreateObjectWithoutContentType(t *testing.T) { HandleCreateTypelessObjectSuccessfully(t, content) - res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", &objects.CreateOpts{Content: strings.NewReader(content)}) + res := objects.Create(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", &objects.CreateOpts{Content: strings.NewReader(content)}) th.AssertNoErr(t, res.Err) } @@ -340,7 +341,7 @@ func TestErrorIsRaisedForChecksumMismatch(t *testing.T) { }) content := strings.NewReader("The sky was the color of television, tuned to a dead channel.") - res := Create(fake.ServiceClient(), "testContainer", "testObject", &CreateOpts{Content: content}) + res := Create(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", &CreateOpts{Content: content}) err := fmt.Errorf("Local checksum does not match API ETag header") th.AssertDeepEquals(t, err, res.Err) @@ -354,7 +355,7 @@ func TestCopyObject(t *testing.T) { HandleCopyObjectSuccessfully(t, "/newTestContainer/newTestObject") options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject"} - res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) + res := objects.Copy(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) }) t.Run("slash", func(t *testing.T) { @@ -363,7 +364,7 @@ func TestCopyObject(t *testing.T) { HandleCopyObjectSuccessfully(t, "/newTestContainer/path%2Fto%2FnewTestObject") options := &objects.CopyOpts{Destination: "/newTestContainer/path/to/newTestObject"} - res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) + res := objects.Copy(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) }) t.Run("emojis", func(t *testing.T) { @@ -372,7 +373,7 @@ func TestCopyObject(t *testing.T) { HandleCopyObjectSuccessfully(t, "/newTestContainer/new%F0%9F%98%8ATest%2C%3B%22O%28bject%21_%E7%AF%84") options := &objects.CopyOpts{Destination: "/newTestContainer/new😊Test,;\"O(bject!_範"} - res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) + res := objects.Copy(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) }) } @@ -383,7 +384,7 @@ func TestCopyObjectVersion(t *testing.T) { HandleCopyObjectVersionSuccessfully(t) options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject", ObjectVersionID: "123456788"} - res, err := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options).Extract() + res, err := objects.Copy(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, "123456789", res.ObjectVersionID) } @@ -393,7 +394,7 @@ func TestDeleteObject(t *testing.T) { defer th.TeardownHTTP() HandleDeleteObjectSuccessfully(t) - res := objects.Delete(fake.ServiceClient(), "testContainer", "testObject", nil) + res := objects.Delete(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", nil) th.AssertNoErr(t, res.Err) } @@ -409,7 +410,7 @@ func TestBulkDelete(t *testing.T) { Errors: [][]string{}, } - resp, err := objects.BulkDelete(fake.ServiceClient(), "testContainer", []string{"testObject1", "testObject2"}).Extract() + resp, err := objects.BulkDelete(context.TODO(), fake.ServiceClient(), "testContainer", []string{"testObject1", "testObject2"}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, *resp) } @@ -430,7 +431,7 @@ func TestUpateObjectMetadata(t *testing.T) { DeleteAt: i, DetectContentType: new(bool), } - res := objects.Update(fake.ServiceClient(), "testContainer", "testObject", options) + res := objects.Update(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) th.AssertNoErr(t, res.Err) } @@ -440,14 +441,14 @@ func TestGetObject(t *testing.T) { HandleGetObjectSuccessfully(t) expected := map[string]string{"Gophercloud-Test": "objects"} - actual, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata() + actual, err := objects.Get(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, actual) getOpts := objects.GetOpts{ Newest: true, } - actualHeaders, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", getOpts).Extract() + actualHeaders, err := objects.Get(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", getOpts).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, actualHeaders.StaticLargeObject) } @@ -531,7 +532,7 @@ func TestCreateTempURL(t *testing.T) { // Append v1/ to client endpoint URL to be compliant with tempURL generator client.Endpoint = client.Endpoint + "v1/" - tempURL, err := objects.CreateTempURL(client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ + tempURL, err := objects.CreateTempURL(context.TODO(), client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ Method: http.MethodGet, TTL: 60, Timestamp: time.Date(2020, 07, 01, 01, 12, 00, 00, time.UTC), @@ -545,7 +546,7 @@ func TestCreateTempURL(t *testing.T) { th.AssertEquals(t, expectedURL, tempURL) // Test TTL=0, but different timestamp - tempURL, err = objects.CreateTempURL(client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ + tempURL, err = objects.CreateTempURL(context.TODO(), client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ Method: http.MethodGet, Timestamp: time.Date(2020, 07, 01, 01, 13, 00, 00, time.UTC), }) diff --git a/openstack/objectstorage/v1/swauth/requests.go b/openstack/objectstorage/v1/swauth/requests.go index c8a6914aa2..b8c0befe3d 100644 --- a/openstack/objectstorage/v1/swauth/requests.go +++ b/openstack/objectstorage/v1/swauth/requests.go @@ -1,6 +1,10 @@ package swauth -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // AuthOptsBuilder describes struct types that can be accepted by the Auth call. type AuthOptsBuilder interface { @@ -22,7 +26,7 @@ func (opts AuthOpts) ToAuthOptsMap() (map[string]string, error) { } // Auth performs an authentication request for a Swauth-based user. -func Auth(c *gophercloud.ProviderClient, opts AuthOptsBuilder) (r GetAuthResult) { +func Auth(ctx context.Context, c *gophercloud.ProviderClient, opts AuthOptsBuilder) (r GetAuthResult) { h := make(map[string]string) if opts != nil { @@ -37,7 +41,7 @@ func Auth(c *gophercloud.ProviderClient, opts AuthOptsBuilder) (r GetAuthResult) } } - resp, err := c.Request("GET", getURL(c), &gophercloud.RequestOpts{ + resp, err := c.Request(ctx, "GET", getURL(c), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200}, }) @@ -47,8 +51,8 @@ func Auth(c *gophercloud.ProviderClient, opts AuthOptsBuilder) (r GetAuthResult) // NewObjectStorageV1 creates a Swauth-authenticated *gophercloud.ServiceClient // client that can issue ObjectStorage-based API calls. -func NewObjectStorageV1(pc *gophercloud.ProviderClient, authOpts AuthOpts) (*gophercloud.ServiceClient, error) { - auth, err := Auth(pc, authOpts).Extract() +func NewObjectStorageV1(ctx context.Context, pc *gophercloud.ProviderClient, authOpts AuthOpts) (*gophercloud.ServiceClient, error) { + auth, err := Auth(ctx, pc, authOpts).Extract() if err != nil { return nil, err } diff --git a/openstack/objectstorage/v1/swauth/testing/requests_test.go b/openstack/objectstorage/v1/swauth/testing/requests_test.go index d5fde5c6c5..cd095d0c17 100644 --- a/openstack/objectstorage/v1/swauth/testing/requests_test.go +++ b/openstack/objectstorage/v1/swauth/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack" @@ -21,7 +22,7 @@ func TestAuth(t *testing.T) { providerClient, err := openstack.NewClient(th.Endpoint()) th.AssertNoErr(t, err) - swiftClient, err := swauth.NewObjectStorageV1(providerClient, authOpts) + swiftClient, err := swauth.NewObjectStorageV1(context.TODO(), providerClient, authOpts) th.AssertNoErr(t, err) th.AssertEquals(t, AuthResult.Token, swiftClient.TokenID) } diff --git a/openstack/orchestration/v1/apiversions/testing/requests_test.go b/openstack/orchestration/v1/apiversions/testing/requests_test.go index ed966c0b3b..163469fec1 100644 --- a/openstack/orchestration/v1/apiversions/testing/requests_test.go +++ b/openstack/orchestration/v1/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -42,7 +43,7 @@ func TestListVersions(t *testing.T) { count := 0 - apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractAPIVersions(page) if err != nil { @@ -81,7 +82,7 @@ func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) { w.WriteHeader(http.StatusOK) }) - apiversions.ListVersions(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { if _, err := apiversions.ExtractAPIVersions(page); err == nil { t.Fatalf("Expected error, got nil") } diff --git a/openstack/orchestration/v1/buildinfo/requests.go b/openstack/orchestration/v1/buildinfo/requests.go index 4706b93090..8f8b9c496a 100644 --- a/openstack/orchestration/v1/buildinfo/requests.go +++ b/openstack/orchestration/v1/buildinfo/requests.go @@ -1,10 +1,14 @@ package buildinfo -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // Get retreives data for the given stack template. -func Get(c *gophercloud.ServiceClient) (r GetResult) { - resp, err := c.Get(getURL(c), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient) (r GetResult) { + resp, err := c.Get(ctx, getURL(c), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/buildinfo/testing/requests_test.go b/openstack/orchestration/v1/buildinfo/testing/requests_test.go index c9cc0df4a0..5177e7d409 100644 --- a/openstack/orchestration/v1/buildinfo/testing/requests_test.go +++ b/openstack/orchestration/v1/buildinfo/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/buildinfo" @@ -13,7 +14,7 @@ func TestGetTemplate(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) - actual, err := buildinfo.Get(fake.ServiceClient()).Extract() + actual, err := buildinfo.Get(context.TODO(), fake.ServiceClient()).Extract() th.AssertNoErr(t, err) expected := GetExpected diff --git a/openstack/orchestration/v1/resourcetypes/requests.go b/openstack/orchestration/v1/resourcetypes/requests.go index 87c7498c16..f45d138cda 100644 --- a/openstack/orchestration/v1/resourcetypes/requests.go +++ b/openstack/orchestration/v1/resourcetypes/requests.go @@ -1,6 +1,8 @@ package resourcetypes import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) @@ -51,7 +53,7 @@ func (opts ListOpts) ToResourceTypeListQuery() (string, error) { } // List makes a request against the API to list available resource types. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) (r ListResult) { +func List(ctx context.Context, client *gophercloud.ServiceClient, opts ListOptsBuilder) (r ListResult) { url := listURL(client) if opts == nil { @@ -64,14 +66,14 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) (r ListResult } url += query - resp, err := client.Get(url, &r.Body, nil) + resp, err := client.Get(ctx, url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetSchema retreives the schema for a given resource type. -func GetSchema(client *gophercloud.ServiceClient, resourceType string) (r GetSchemaResult) { - resp, err := client.Get(getSchemaURL(client, resourceType), &r.Body, nil) +func GetSchema(ctx context.Context, client *gophercloud.ServiceClient, resourceType string) (r GetSchemaResult) { + resp, err := client.Get(ctx, getSchemaURL(client, resourceType), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -104,7 +106,7 @@ func (opts GenerateTemplateOpts) ToGenerateTemplateQuery() (string, error) { } // GenerateTemplate retreives an example template for a given resource type. -func GenerateTemplate(client *gophercloud.ServiceClient, resourceType string, opts GenerateTemplateOptsBuilder) (r TemplateResult) { +func GenerateTemplate(ctx context.Context, client *gophercloud.ServiceClient, resourceType string, opts GenerateTemplateOptsBuilder) (r TemplateResult) { url := generateTemplateURL(client, resourceType) if opts == nil { opts = GenerateTemplateOpts{} @@ -115,7 +117,7 @@ func GenerateTemplate(client *gophercloud.ServiceClient, resourceType string, op return } url += query - resp, err := client.Get(url, &r.Body, nil) + resp, err := client.Get(ctx, url, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/resourcetypes/testing/requests_test.go b/openstack/orchestration/v1/resourcetypes/testing/requests_test.go index 8f0f3540f0..b426d55f6a 100644 --- a/openstack/orchestration/v1/resourcetypes/testing/requests_test.go +++ b/openstack/orchestration/v1/resourcetypes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/resourcetypes" @@ -13,7 +14,7 @@ func TestBasicListResourceTypes(t *testing.T) { defer th.TeardownHTTP() HandleListSuccessfully(t) - result := resourcetypes.List(fake.ServiceClient(), nil) + result := resourcetypes.List(context.TODO(), fake.ServiceClient(), nil) th.AssertNoErr(t, result.Err) actual, err := result.Extract() @@ -27,7 +28,7 @@ func TestFullListResourceTypes(t *testing.T) { defer th.TeardownHTTP() HandleListSuccessfully(t) - result := resourcetypes.List(fake.ServiceClient(), resourcetypes.ListOpts{ + result := resourcetypes.List(context.TODO(), fake.ServiceClient(), resourcetypes.ListOpts{ WithDescription: true, }) th.AssertNoErr(t, result.Err) @@ -43,7 +44,7 @@ func TestFilteredListResourceTypes(t *testing.T) { defer th.TeardownHTTP() HandleListSuccessfully(t) - result := resourcetypes.List(fake.ServiceClient(), resourcetypes.ListOpts{ + result := resourcetypes.List(context.TODO(), fake.ServiceClient(), resourcetypes.ListOpts{ NameRegex: listFilterRegex, WithDescription: true, }) @@ -60,7 +61,7 @@ func TestGetSchema(t *testing.T) { defer th.TeardownHTTP() HandleGetSchemaSuccessfully(t) - result := resourcetypes.GetSchema(fake.ServiceClient(), "OS::Test::TestServer") + result := resourcetypes.GetSchema(context.TODO(), fake.ServiceClient(), "OS::Test::TestServer") th.AssertNoErr(t, result.Err) actual, err := result.Extract() @@ -74,7 +75,7 @@ func TestGenerateTemplate(t *testing.T) { defer th.TeardownHTTP() HandleGenerateTemplateSuccessfully(t) - result := resourcetypes.GenerateTemplate(fake.ServiceClient(), "OS::Heat::None", nil) + result := resourcetypes.GenerateTemplate(context.TODO(), fake.ServiceClient(), "OS::Heat::None", nil) th.AssertNoErr(t, result.Err) actual, err := result.Extract() diff --git a/openstack/orchestration/v1/stackevents/requests.go b/openstack/orchestration/v1/stackevents/requests.go index c6d21c185e..71836e0541 100644 --- a/openstack/orchestration/v1/stackevents/requests.go +++ b/openstack/orchestration/v1/stackevents/requests.go @@ -1,13 +1,15 @@ package stackevents import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) // Find retrieves stack events for the given stack name. -func Find(c *gophercloud.ServiceClient, stackName string) (r FindResult) { - resp, err := c.Get(findURL(c, stackName), &r.Body, nil) +func Find(ctx context.Context, c *gophercloud.ServiceClient, stackName string) (r FindResult) { + resp, err := c.Get(ctx, findURL(c, stackName), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,8 +179,8 @@ func ListResourceEvents(client *gophercloud.ServiceClient, stackName, stackID, r } // Get retreives data for the given stack resource. -func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) (r GetResult) { - resp, err := c.Get(getURL(c, stackName, stackID, resourceName, eventID), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, stackName, stackID, resourceName, eventID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/stackevents/testing/requests_test.go b/openstack/orchestration/v1/stackevents/testing/requests_test.go index c3706e0991..bed2d8a2bf 100644 --- a/openstack/orchestration/v1/stackevents/testing/requests_test.go +++ b/openstack/orchestration/v1/stackevents/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stackevents" @@ -14,7 +15,7 @@ func TestFindEvents(t *testing.T) { defer th.TeardownHTTP() HandleFindSuccessfully(t, FindOutput) - actual, err := stackevents.Find(fake.ServiceClient(), "postman_stack").Extract() + actual, err := stackevents.Find(context.TODO(), fake.ServiceClient(), "postman_stack").Extract() th.AssertNoErr(t, err) expected := FindExpected @@ -27,7 +28,7 @@ func TestList(t *testing.T) { HandleListSuccessfully(t, ListOutput) count := 0 - err := stackevents.List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) { + err := stackevents.List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := stackevents.ExtractEvents(page) th.AssertNoErr(t, err) @@ -46,7 +47,7 @@ func TestListResourceEvents(t *testing.T) { HandleListResourceEventsSuccessfully(t, ListResourceEventsOutput) count := 0 - err := stackevents.ListResourceEvents(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", nil).EachPage(func(page pagination.Page) (bool, error) { + err := stackevents.ListResourceEvents(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := stackevents.ExtractResourceEvents(page) th.AssertNoErr(t, err) @@ -64,7 +65,7 @@ func TestGetEvent(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) - actual, err := stackevents.Get(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", "93940999-7d40-44ae-8de4-19624e7b8d18").Extract() + actual, err := stackevents.Get(context.TODO(), fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", "my_resource", "93940999-7d40-44ae-8de4-19624e7b8d18").Extract() th.AssertNoErr(t, err) expected := GetExpected diff --git a/openstack/orchestration/v1/stackresources/requests.go b/openstack/orchestration/v1/stackresources/requests.go index 21260fffad..6d1b12b477 100644 --- a/openstack/orchestration/v1/stackresources/requests.go +++ b/openstack/orchestration/v1/stackresources/requests.go @@ -1,13 +1,15 @@ package stackresources import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) // Find retrieves stack resources for the given stack name. -func Find(c *gophercloud.ServiceClient, stackName string) (r FindResult) { - resp, err := c.Get(findURL(c, stackName), &r.Body, nil) +func Find(ctx context.Context, c *gophercloud.ServiceClient, stackName string) (r FindResult) { + resp, err := c.Get(ctx, findURL(c, stackName), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -47,15 +49,15 @@ func List(client *gophercloud.ServiceClient, stackName, stackID string, opts Lis } // Get retreives data for the given stack resource. -func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r GetResult) { - resp, err := c.Get(getURL(c, stackName, stackID, resourceName), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, stackName, stackID, resourceName), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Metadata retreives the metadata for the given stack resource. -func Metadata(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r MetadataResult) { - resp, err := c.Get(metadataURL(c, stackName, stackID, resourceName), &r.Body, nil) +func Metadata(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID, resourceName string) (r MetadataResult) { + resp, err := c.Get(ctx, metadataURL(c, stackName, stackID, resourceName), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -68,15 +70,15 @@ func ListTypes(client *gophercloud.ServiceClient) pagination.Pager { } // Schema retreives the schema for the given resource type. -func Schema(c *gophercloud.ServiceClient, resourceType string) (r SchemaResult) { - resp, err := c.Get(schemaURL(c, resourceType), &r.Body, nil) +func Schema(ctx context.Context, c *gophercloud.ServiceClient, resourceType string) (r SchemaResult) { + resp, err := c.Get(ctx, schemaURL(c, resourceType), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Template retreives the template representation for the given resource type. -func Template(c *gophercloud.ServiceClient, resourceType string) (r TemplateResult) { - resp, err := c.Get(templateURL(c, resourceType), &r.Body, nil) +func Template(ctx context.Context, c *gophercloud.ServiceClient, resourceType string) (r TemplateResult) { + resp, err := c.Get(ctx, templateURL(c, resourceType), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -107,13 +109,13 @@ func (opts MarkUnhealthyOpts) ToMarkUnhealthyMap() (map[string]interface{}, erro } // MarkUnhealthy marks the specified resource in the stack as unhealthy. -func MarkUnhealthy(c *gophercloud.ServiceClient, stackName, stackID, resourceName string, opts MarkUnhealthyOptsBuilder) (r MarkUnhealthyResult) { +func MarkUnhealthy(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID, resourceName string, opts MarkUnhealthyOptsBuilder) (r MarkUnhealthyResult) { b, err := opts.ToMarkUnhealthyMap() if err != nil { r.Err = err return } - resp, err := c.Patch(markUnhealthyURL(c, stackName, stackID, resourceName), b, nil, nil) + resp, err := c.Patch(ctx, markUnhealthyURL(c, stackName, stackID, resourceName), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/orchestration/v1/stackresources/testing/requests_test.go b/openstack/orchestration/v1/stackresources/testing/requests_test.go index 60624d2823..358b30621b 100644 --- a/openstack/orchestration/v1/stackresources/testing/requests_test.go +++ b/openstack/orchestration/v1/stackresources/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "sort" "testing" @@ -15,7 +16,7 @@ func TestFindResources(t *testing.T) { defer th.TeardownHTTP() HandleFindSuccessfully(t, FindOutput) - actual, err := stackresources.Find(fake.ServiceClient(), "hello_world").Extract() + actual, err := stackresources.Find(context.TODO(), fake.ServiceClient(), "hello_world").Extract() th.AssertNoErr(t, err) expected := FindExpected @@ -28,7 +29,7 @@ func TestListResources(t *testing.T) { HandleListSuccessfully(t, ListOutput) count := 0 - err := stackresources.List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(func(page pagination.Page) (bool, error) { + err := stackresources.List(fake.ServiceClient(), "hello_world", "49181cd6-169a-4130-9455-31185bbfc5bf", nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := stackresources.ExtractResources(page) th.AssertNoErr(t, err) @@ -46,7 +47,7 @@ func TestGetResource(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) - actual, err := stackresources.Get(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract() + actual, err := stackresources.Get(context.TODO(), fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract() th.AssertNoErr(t, err) expected := GetExpected @@ -58,7 +59,7 @@ func TestResourceMetadata(t *testing.T) { defer th.TeardownHTTP() HandleMetadataSuccessfully(t, MetadataOutput) - actual, err := stackresources.Metadata(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract() + actual, err := stackresources.Metadata(context.TODO(), fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance").Extract() th.AssertNoErr(t, err) expected := MetadataExpected @@ -71,7 +72,7 @@ func TestListResourceTypes(t *testing.T) { HandleListTypesSuccessfully(t, ListTypesOutput) count := 0 - err := stackresources.ListTypes(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + err := stackresources.ListTypes(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := stackresources.ExtractResourceTypes(page) th.AssertNoErr(t, err) @@ -92,7 +93,7 @@ func TestGetResourceSchema(t *testing.T) { defer th.TeardownHTTP() HandleGetSchemaSuccessfully(t, GetSchemaOutput) - actual, err := stackresources.Schema(fake.ServiceClient(), "OS::Heat::AResourceName").Extract() + actual, err := stackresources.Schema(context.TODO(), fake.ServiceClient(), "OS::Heat::AResourceName").Extract() th.AssertNoErr(t, err) expected := GetSchemaExpected @@ -104,7 +105,7 @@ func TestGetResourceTemplate(t *testing.T) { defer th.TeardownHTTP() HandleGetTemplateSuccessfully(t, GetTemplateOutput) - actual, err := stackresources.Template(fake.ServiceClient(), "OS::Heat::AResourceName").Extract() + actual, err := stackresources.Template(context.TODO(), fake.ServiceClient(), "OS::Heat::AResourceName").Extract() th.AssertNoErr(t, err) expected := GetTemplateExpected @@ -120,6 +121,6 @@ func TestMarkUnhealthyResource(t *testing.T) { MarkUnhealthy: true, ResourceStatusReason: "Kubelet.Ready is Unknown more than 10 mins.", } - err := stackresources.MarkUnhealthy(fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance", markUnhealthyOpts).ExtractErr() + err := stackresources.MarkUnhealthy(context.TODO(), fake.ServiceClient(), "teststack", "0b1771bd-9336-4f2b-ae86-a80f971faf1e", "wordpress_instance", markUnhealthyOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index af7f260642..3c8620bc2c 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "fmt" "strings" @@ -85,13 +86,13 @@ func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) { // Create accepts a CreateOpts struct and creates a new stack using the values // provided. -func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToStackCreateMap() if err != nil { r.Err = fmt.Errorf("error creating the options map: %w", err) return } - resp, err := c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -173,13 +174,13 @@ func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) { // Adopt accepts an AdoptOpts struct and creates a new stack using the resources // from another stack. -func Adopt(c *gophercloud.ServiceClient, opts AdoptOptsBuilder) (r AdoptResult) { +func Adopt(ctx context.Context, c *gophercloud.ServiceClient, opts AdoptOptsBuilder) (r AdoptResult) { b, err := opts.ToStackAdoptMap() if err != nil { r.Err = err return } - resp, err := c.Post(adoptURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, adoptURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -294,15 +295,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { } // Get retreives a stack based on the stack name and stack ID. -func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { - resp, err := c.Get(getURL(c, stackName, stackID), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, stackName, stackID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Find retrieves a stack based on the stack name or stack ID. -func Find(c *gophercloud.ServiceClient, stackIdentity string) (r GetResult) { - resp, err := c.Get(findURL(c, stackIdentity), &r.Body, nil) +func Find(ctx context.Context, c *gophercloud.ServiceClient, stackIdentity string) (r GetResult) { + resp, err := c.Get(ctx, findURL(c, stackIdentity), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -401,13 +402,13 @@ 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. -func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToStackUpdateMap() if err != nil { r.Err = err return } - resp, err := c.Put(updateURL(c, stackName, stackID), b, nil, nil) + resp, err := c.Put(ctx, updateURL(c, stackName, stackID), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -415,20 +416,20 @@ 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. -func UpdatePatch(c *gophercloud.ServiceClient, stackName, stackID string, opts UpdatePatchOptsBuilder) (r UpdateResult) { +func UpdatePatch(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID string, opts UpdatePatchOptsBuilder) (r UpdateResult) { b, err := opts.ToStackUpdatePatchMap() if err != nil { r.Err = err return } - resp, err := c.Patch(updateURL(c, stackName, stackID), b, nil, nil) + resp, err := c.Patch(ctx, updateURL(c, stackName, stackID), b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes a stack based on the stack name and stack ID. -func Delete(c *gophercloud.ServiceClient, stackName, stackID string) (r DeleteResult) { - resp, err := c.Delete(deleteURL(c, stackName, stackID), nil) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, stackName, stackID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -502,13 +503,13 @@ func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) { // Preview accepts a PreviewOptsBuilder interface and creates a preview of a stack using the values // provided. -func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) (r PreviewResult) { +func Preview(ctx context.Context, c *gophercloud.ServiceClient, opts PreviewOptsBuilder) (r PreviewResult) { b, err := opts.ToStackPreviewMap() if err != nil { r.Err = err return } - resp, err := c.Post(previewURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(ctx, previewURL(c), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -517,8 +518,8 @@ func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) (r PreviewRe // Abandon deletes the stack with the provided stackName and stackID, but leaves its // resources intact, and returns data describing the stack and its resources. -func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) (r AbandonResult) { - resp, err := c.Delete(abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{ +func Abandon(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID string) (r AbandonResult) { + resp, err := c.Delete(ctx, abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{ JSONResponse: &r.Body, OkCodes: []int{200}, }) diff --git a/openstack/orchestration/v1/stacks/testing/requests_test.go b/openstack/orchestration/v1/stacks/testing/requests_test.go index 675ce103c0..e5b68cae66 100644 --- a/openstack/orchestration/v1/stacks/testing/requests_test.go +++ b/openstack/orchestration/v1/stacks/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -32,7 +33,7 @@ func TestCreateStack(t *testing.T) { TemplateOpts: template, DisableRollback: gophercloud.Disabled, } - actual, err := stacks.Create(fake.ServiceClient(), createOpts).Extract() + actual, err := stacks.Create(context.TODO(), fake.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) expected := CreateExpected @@ -58,7 +59,7 @@ func TestCreateStackMissingRequiredInOpts(t *testing.T) { createOpts := stacks.CreateOpts{ DisableRollback: gophercloud.Disabled, } - r := stacks.Create(fake.ServiceClient(), createOpts) + r := stacks.Create(context.TODO(), fake.ServiceClient(), createOpts) th.AssertEquals(t, "error creating the options map: Missing input for argument [Name]", r.Err.Error()) } @@ -101,7 +102,7 @@ func TestAdoptStack(t *testing.T) { TemplateOpts: template, DisableRollback: gophercloud.Disabled, } - actual, err := stacks.Adopt(fake.ServiceClient(), adoptOpts).Extract() + actual, err := stacks.Adopt(context.TODO(), fake.ServiceClient(), adoptOpts).Extract() th.AssertNoErr(t, err) expected := CreateExpected @@ -114,7 +115,7 @@ func TestListStack(t *testing.T) { HandleListSuccessfully(t, FullListOutput) count := 0 - err := stacks.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := stacks.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := stacks.ExtractStacks(page) th.AssertNoErr(t, err) @@ -132,7 +133,7 @@ func TestGetStack(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) - actual, err := stacks.Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() + actual, err := stacks.Get(context.TODO(), fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() th.AssertNoErr(t, err) expected := GetExpected @@ -144,7 +145,7 @@ func TestFindStack(t *testing.T) { defer th.TeardownHTTP() HandleFindSuccessfully(t, GetOutput) - actual, err := stacks.Find(fake.ServiceClient(), "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() + actual, err := stacks.Find(context.TODO(), fake.ServiceClient(), "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() th.AssertNoErr(t, err) expected := GetExpected @@ -171,7 +172,7 @@ func TestUpdateStack(t *testing.T) { updateOpts := &stacks.UpdateOpts{ TemplateOpts: template, } - err := stacks.Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() + err := stacks.Update(context.TODO(), fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -188,7 +189,7 @@ func TestUpdateStackNoTemplate(t *testing.T) { } expected := stacks.ErrTemplateRequired{} - err := stacks.Update(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() + err := stacks.Update(context.TODO(), fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() th.AssertEquals(t, expected, err) } @@ -203,7 +204,7 @@ func TestUpdatePatchStack(t *testing.T) { updateOpts := &stacks.UpdateOpts{ Parameters: parameters, } - err := stacks.UpdatePatch(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() + err := stacks.UpdatePatch(context.TODO(), fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada", updateOpts).ExtractErr() th.AssertNoErr(t, err) } @@ -212,7 +213,7 @@ func TestDeleteStack(t *testing.T) { defer th.TeardownHTTP() HandleDeleteSuccessfully(t) - err := stacks.Delete(fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada").ExtractErr() + err := stacks.Delete(context.TODO(), fake.ServiceClient(), "gophercloud-test-stack-2", "db6977b2-27aa-4775-9ae7-6213212d4ada").ExtractErr() th.AssertNoErr(t, err) } @@ -239,7 +240,7 @@ func TestPreviewStack(t *testing.T) { TemplateOpts: template, DisableRollback: gophercloud.Disabled, } - actual, err := stacks.Preview(fake.ServiceClient(), previewOpts).Extract() + actual, err := stacks.Preview(context.TODO(), fake.ServiceClient(), previewOpts).Extract() th.AssertNoErr(t, err) expected := PreviewExpected @@ -251,7 +252,7 @@ func TestAbandonStack(t *testing.T) { defer th.TeardownHTTP() HandleAbandonSuccessfully(t, AbandonOutput) - actual, err := stacks.Abandon(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c8").Extract() + actual, err := stacks.Abandon(context.TODO(), fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c8").Extract() th.AssertNoErr(t, err) expected := AbandonExpected diff --git a/openstack/orchestration/v1/stacktemplates/requests.go b/openstack/orchestration/v1/stacktemplates/requests.go index e29de588ad..c7b3c2c838 100644 --- a/openstack/orchestration/v1/stacktemplates/requests.go +++ b/openstack/orchestration/v1/stacktemplates/requests.go @@ -1,10 +1,14 @@ package stacktemplates -import "github.com/gophercloud/gophercloud/v2" +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) // Get retreives data for the given stack template. -func Get(c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { - resp, err := c.Get(getURL(c, stackName, stackID), &r.Body, nil) +func Get(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, stackName, stackID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -27,13 +31,13 @@ func (opts ValidateOpts) ToStackTemplateValidateMap() (map[string]interface{}, e } // Validate validates the given stack template. -func Validate(c *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) { +func Validate(ctx context.Context, c *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) { b, err := opts.ToStackTemplateValidateMap() if err != nil { r.Err = err return } - resp, err := c.Post(validateURL(c), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Post(ctx, validateURL(c), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/orchestration/v1/stacktemplates/testing/requests_test.go b/openstack/orchestration/v1/stacktemplates/testing/requests_test.go index 785ef6605e..f52f4f75d7 100644 --- a/openstack/orchestration/v1/stacktemplates/testing/requests_test.go +++ b/openstack/orchestration/v1/stacktemplates/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/orchestration/v1/stacktemplates" @@ -13,7 +14,7 @@ func TestGetTemplate(t *testing.T) { defer th.TeardownHTTP() HandleGetSuccessfully(t, GetOutput) - actual, err := stacktemplates.Get(fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() + actual, err := stacktemplates.Get(context.TODO(), fake.ServiceClient(), "postman_stack", "16ef0584-4458-41eb-87c8-0dc8d5f66c87").Extract() th.AssertNoErr(t, err) expected := GetExpected @@ -50,7 +51,7 @@ func TestValidateTemplate(t *testing.T) { } }`, } - actual, err := stacktemplates.Validate(fake.ServiceClient(), opts).Extract() + actual, err := stacktemplates.Validate(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) expected := ValidateExpected diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 156ec2abb3..e9accbfbe2 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -1,6 +1,8 @@ package resourceproviders import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -85,14 +87,14 @@ func (opts CreateOpts) ToResourceProviderCreateMap() (map[string]interface{}, er } // Create makes a request against the API to create a resource provider -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToResourceProviderCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(resourceProvidersListURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, resourceProvidersListURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -100,15 +102,15 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // 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) +func Delete(ctx context.Context, c *gophercloud.ServiceClient, resourceProviderID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, resourceProviderID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) +func Get(ctx context.Context, c *gophercloud.ServiceClient, resourceProviderID string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -136,40 +138,40 @@ func (opts UpdateOpts) ToResourceProviderUpdateMap() (map[string]interface{}, er } // Update makes a request against the API to create a resource provider -func Update(client *gophercloud.ServiceClient, resourceProviderID string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, 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{ + resp, err := client.Put(ctx, 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) +func GetUsages(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r GetUsagesResult) { + resp, err := client.Get(ctx, getResourceProviderUsagesURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -func GetInventories(client *gophercloud.ServiceClient, resourceProviderID string) (r GetInventoriesResult) { - resp, err := client.Get(getResourceProviderInventoriesURL(client, resourceProviderID), &r.Body, nil) +func GetInventories(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r GetInventoriesResult) { + resp, err := client.Get(ctx, getResourceProviderInventoriesURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -func GetAllocations(client *gophercloud.ServiceClient, resourceProviderID string) (r GetAllocationsResult) { - resp, err := client.Get(getResourceProviderAllocationsURL(client, resourceProviderID), &r.Body, nil) +func GetAllocations(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r GetAllocationsResult) { + resp, err := client.Get(ctx, getResourceProviderAllocationsURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -func GetTraits(client *gophercloud.ServiceClient, resourceProviderID string) (r GetTraitsResult) { - resp, err := client.Get(getResourceProviderTraitsURL(client, resourceProviderID), &r.Body, nil) +func GetTraits(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r GetTraitsResult) { + resp, err := client.Get(ctx, getResourceProviderTraitsURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index be6554bc85..ad0db1475c 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" @@ -17,7 +18,7 @@ func TestListResourceProviders(t *testing.T) { HandleResourceProviderList(t) count := 0 - err := resourceproviders.List(fake.ServiceClient(), resourceproviders.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := resourceproviders.List(fake.ServiceClient(), resourceproviders.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := resourceproviders.ExtractResourceProviders(page) @@ -51,7 +52,7 @@ func TestCreateResourceProvider(t *testing.T) { ParentProviderUUID: ExpectedResourceProvider1.ParentProviderUUID, } - actual, err := resourceproviders.Create(fake.ServiceClient(), opts).Extract() + actual, err := resourceproviders.Create(context.TODO(), fake.ServiceClient(), opts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -65,7 +66,7 @@ func TestGetResourceProvider(t *testing.T) { expected := ExpectedResourceProvider1 - actual, err := resourceproviders.Get(fake.ServiceClient(), ExpectedResourceProvider1.UUID).Extract() + actual, err := resourceproviders.Get(context.TODO(), fake.ServiceClient(), ExpectedResourceProvider1.UUID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, &expected, actual) @@ -77,7 +78,7 @@ func TestDeleteResourceProvider(t *testing.T) { HandleResourceProviderDelete(t) - res := resourceproviders.Delete(fake.ServiceClient(), "b99b3ab4-3aa6-4fba-b827-69b88b9c544a") + res := resourceproviders.Delete(context.TODO(), fake.ServiceClient(), "b99b3ab4-3aa6-4fba-b827-69b88b9c544a") th.AssertNoErr(t, res.Err) } @@ -94,7 +95,7 @@ func TestUpdate(t *testing.T) { Name: &name, ParentProviderUUID: &parentProviderUUID, } - rp, err := resourceproviders.Update(fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() + rp, err := resourceproviders.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, rp.Name, name) @@ -107,7 +108,7 @@ func TestGetResourceProvidersUsages(t *testing.T) { HandleResourceProviderGetUsages(t) - actual, err := resourceproviders.GetUsages(fake.ServiceClient(), ResourceProviderTestID).Extract() + actual, err := resourceproviders.GetUsages(context.TODO(), fake.ServiceClient(), ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUsages, *actual) } @@ -118,7 +119,7 @@ func TestGetResourceProvidersInventories(t *testing.T) { HandleResourceProviderGetInventories(t) - actual, err := resourceproviders.GetInventories(fake.ServiceClient(), ResourceProviderTestID).Extract() + actual, err := resourceproviders.GetInventories(context.TODO(), fake.ServiceClient(), ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedInventories, *actual) } @@ -129,7 +130,7 @@ func TestGetResourceProvidersAllocations(t *testing.T) { HandleResourceProviderGetAllocations(t) - actual, err := resourceproviders.GetAllocations(fake.ServiceClient(), ResourceProviderTestID).Extract() + actual, err := resourceproviders.GetAllocations(context.TODO(), fake.ServiceClient(), ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedAllocations, *actual) } @@ -140,7 +141,7 @@ func TestGetResourceProvidersTraits(t *testing.T) { HandleResourceProviderGetTraits(t) - actual, err := resourceproviders.GetTraits(fake.ServiceClient(), ResourceProviderTestID).Extract() + actual, err := resourceproviders.GetTraits(context.TODO(), fake.ServiceClient(), ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedTraits, *actual) } diff --git a/openstack/sharedfilesystems/apiversions/requests.go b/openstack/sharedfilesystems/apiversions/requests.go index a82c605c10..02dccd30d1 100644 --- a/openstack/sharedfilesystems/apiversions/requests.go +++ b/openstack/sharedfilesystems/apiversions/requests.go @@ -1,6 +1,8 @@ package apiversions import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -13,8 +15,8 @@ func List(c *gophercloud.ServiceClient) pagination.Pager { } // Get will get a specific API version, specified by major ID. -func Get(client *gophercloud.ServiceClient, v string) (r GetResult) { - resp, err := client.Get(getURL(client, v), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, v string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, v), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/apiversions/testing/requests_test.go b/openstack/sharedfilesystems/apiversions/testing/requests_test.go index 7a7eef7e5f..1e09f7daac 100644 --- a/openstack/sharedfilesystems/apiversions/testing/requests_test.go +++ b/openstack/sharedfilesystems/apiversions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/apiversions" @@ -14,7 +15,7 @@ func TestListAPIVersions(t *testing.T) { MockListResponse(t) - allVersions, err := apiversions.List(client.ServiceClient()).AllPages() + allVersions, err := apiversions.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := apiversions.ExtractAPIVersions(allVersions) @@ -29,7 +30,7 @@ func TestGetAPIVersion(t *testing.T) { MockGetResponse(t) - actual, err := apiversions.Get(client.ServiceClient(), "v2").Extract() + actual, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v2").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ManilaAPIVersion2Result, *actual) @@ -41,7 +42,7 @@ func TestGetNoAPIVersion(t *testing.T) { MockGetNoResponse(t) - _, err := apiversions.Get(client.ServiceClient(), "v2").Extract() + _, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v2").Extract() th.AssertEquals(t, err.Error(), "Unable to find requested API version") } @@ -51,6 +52,6 @@ func TestGetMultipleAPIVersion(t *testing.T) { MockGetMultipleResponses(t) - _, err := apiversions.Get(client.ServiceClient(), "v2").Extract() + _, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v2").Extract() th.AssertEquals(t, err.Error(), "Found 2 API versions") } diff --git a/openstack/sharedfilesystems/v2/availabilityzones/testing/requests_test.go b/openstack/sharedfilesystems/v2/availabilityzones/testing/requests_test.go index dbe00da14b..8d69781c3d 100644 --- a/openstack/sharedfilesystems/v2/availabilityzones/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/availabilityzones/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -16,7 +17,7 @@ func TestList(t *testing.T) { MockListResponse(t) - allPages, err := availabilityzones.List(client.ServiceClient()).AllPages() + allPages, err := availabilityzones.List(client.ServiceClient()).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := availabilityzones.ExtractAvailabilityZones(allPages) th.AssertNoErr(t, err) diff --git a/openstack/sharedfilesystems/v2/errors/testing/request_test.go b/openstack/sharedfilesystems/v2/errors/testing/request_test.go index 3c0f5f2500..f405721d7f 100644 --- a/openstack/sharedfilesystems/v2/errors/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/errors/testing/request_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/errors" @@ -16,7 +17,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := &shares.CreateOpts{Size: 1, Name: "my_test_share", ShareProto: "NFS", SnapshotID: "70bfbebc-d3ff-4528-8bbb-58422daa280b"} - _, err := shares.Create(client.ServiceClient(), options).Extract() + _, err := shares.Create(context.TODO(), client.ServiceClient(), options).Extract() if err == nil { t.Fatal("Expected error") diff --git a/openstack/sharedfilesystems/v2/messages/requests.go b/openstack/sharedfilesystems/v2/messages/requests.go index 53be2c2b45..3162262977 100644 --- a/openstack/sharedfilesystems/v2/messages/requests.go +++ b/openstack/sharedfilesystems/v2/messages/requests.go @@ -1,13 +1,15 @@ package messages import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) // Delete will delete the existing Message with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -67,8 +69,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves the Message with the provided ID. To extract the Message // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/v2/messages/testing/requests_test.go b/openstack/sharedfilesystems/v2/messages/testing/requests_test.go index 257b18c939..75b0b57dc3 100644 --- a/openstack/sharedfilesystems/v2/messages/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/messages/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -16,7 +17,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := messages.Delete(client.ServiceClient(), "messageID") + res := messages.Delete(context.TODO(), client.ServiceClient(), "messageID") th.AssertNoErr(t, res.Err) } @@ -27,7 +28,7 @@ func TestList(t *testing.T) { MockListResponse(t) - allPages, err := messages.List(client.ServiceClient(), &messages.ListOpts{}).AllPages() + allPages, err := messages.List(client.ServiceClient(), &messages.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := messages.ExtractMessages(allPages) th.AssertNoErr(t, err) @@ -74,7 +75,7 @@ func TestFilteredList(t *testing.T) { RequestID: "req-21767eee-22ca-40a4-b6c0-ae7d35cd434f", } - allPages, err := messages.List(client.ServiceClient(), options).AllPages() + allPages, err := messages.List(client.ServiceClient(), options).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := messages.ExtractMessages(allPages) th.AssertNoErr(t, err) @@ -118,7 +119,7 @@ func TestGet(t *testing.T) { ActionID: "002", } - n, err := messages.Get(client.ServiceClient(), "2076373e-13a7-4b84-9e67-15ce8cceaff8").Extract() + n, err := messages.Get(context.TODO(), client.ServiceClient(), "2076373e-13a7-4b84-9e67-15ce8cceaff8").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, n) diff --git a/openstack/sharedfilesystems/v2/replicas/requests.go b/openstack/sharedfilesystems/v2/replicas/requests.go index 061f7a1dc5..021c1550a7 100644 --- a/openstack/sharedfilesystems/v2/replicas/requests.go +++ b/openstack/sharedfilesystems/v2/replicas/requests.go @@ -1,6 +1,8 @@ package replicas import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -37,13 +39,13 @@ func (opts CreateOpts) ToReplicaCreateMap() (map[string]interface{}, error) { // 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) { +func Create(ctx context.Context, 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{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -112,31 +114,31 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } // 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) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // 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) +func ListExportLocations(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ListExportLocationsResult) { + resp, err := client.Get(ctx, 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) +func GetExportLocation(ctx context.Context, client *gophercloud.ServiceClient, replicaID string, id string) (r GetExportLocationResult) { + resp, err := client.Get(ctx, getExportLocationURL(client, replicaID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -163,14 +165,14 @@ func (opts PromoteOpts) ToReplicaPromoteMap() (map[string]interface{}, error) { // 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) { +func Promote(ctx context.Context, 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{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -179,8 +181,8 @@ func Promote(client *gophercloud.ServiceClient, id string, opts PromoteOptsBuild // 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{ +func Resync(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResyncResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"resync": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -210,13 +212,13 @@ func (opts ResetStatusOpts) ToReplicaResetStatusMap() (map[string]interface{}, e // 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) { +func ResetStatus(ctx context.Context, 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{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -246,13 +248,13 @@ func (opts ResetStateOpts) ToReplicaResetStateMap() (map[string]interface{}, err // 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) { +func ResetState(ctx context.Context, 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{ + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -262,8 +264,8 @@ func ResetState(client *gophercloud.ServiceClient, id string, opts ResetStateOpt // 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{ +func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"force_delete": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/sharedfilesystems/v2/replicas/testing/request_test.go b/openstack/sharedfilesystems/v2/replicas/testing/request_test.go index b3717af3a7..167bc2c879 100644 --- a/openstack/sharedfilesystems/v2/replicas/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/replicas/testing/request_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -27,7 +28,7 @@ func TestCreate(t *testing.T) { ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", AvailabilityZone: "zone-1", } - actual, err := replicas.Create(getClient("2.11"), options).Extract() + actual, err := replicas.Create(context.TODO(), getClient("2.11"), options).Extract() expected := &replicas.Replica{ ID: "3b9c33e8-b136-45c6-84a6-019c8db1d550", @@ -48,7 +49,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - result := replicas.Delete(getClient("2.11"), replicaID) + result := replicas.Delete(context.TODO(), getClient("2.11"), replicaID) th.AssertNoErr(t, result.Err) } @@ -58,7 +59,7 @@ func TestForceDeleteSuccess(t *testing.T) { MockForceDeleteResponse(t) - err := replicas.ForceDelete(getClient("2.11"), replicaID).ExtractErr() + err := replicas.ForceDelete(context.TODO(), getClient("2.11"), replicaID).ExtractErr() th.AssertNoErr(t, err) } @@ -68,7 +69,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - actual, err := replicas.Get(getClient("2.11"), replicaID).Extract() + actual, err := replicas.Get(context.TODO(), getClient("2.11"), replicaID).Extract() expected := &replicas.Replica{ AvailabilityZone: "zone-1", @@ -95,7 +96,7 @@ func TestList(t *testing.T) { listOpts := &replicas.ListOpts{ ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", } - allPages, err := replicas.List(getClient("2.11"), listOpts).AllPages() + allPages, err := replicas.List(getClient("2.11"), listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := replicas.ExtractReplicas(allPages) @@ -134,7 +135,7 @@ func TestListDetail(t *testing.T) { listOpts := &replicas.ListOpts{ ShareID: "65a34695-f9e5-4eea-b48d-a0b261d82943", } - allPages, err := replicas.ListDetail(getClient("2.11"), listOpts).AllPages() + allPages, err := replicas.ListDetail(getClient("2.11"), listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := replicas.ExtractReplicas(allPages) @@ -185,7 +186,7 @@ func TestListExportLocationsSuccess(t *testing.T) { MockListExportLocationsResponse(t) - actual, err := replicas.ListExportLocations(getClient("2.47"), replicaID).Extract() + actual, err := replicas.ListExportLocations(context.TODO(), getClient("2.47"), replicaID).Extract() expected := []replicas.ExportLocation{ { @@ -214,7 +215,7 @@ func TestGetExportLocationSuccess(t *testing.T) { MockGetExportLocationResponse(t) - s, err := replicas.GetExportLocation(getClient("2.47"), replicaID, "ae73e762-e8b9-4aad-aad3-23afb7cd6825").Extract() + s, err := replicas.GetExportLocation(context.TODO(), getClient("2.47"), replicaID, "ae73e762-e8b9-4aad-aad3-23afb7cd6825").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, &replicas.ExportLocation{ @@ -234,7 +235,7 @@ func TestResetStatusSuccess(t *testing.T) { MockResetStatusResponse(t) - err := replicas.ResetStatus(getClient("2.11"), replicaID, &replicas.ResetStatusOpts{Status: "available"}).ExtractErr() + err := replicas.ResetStatus(context.TODO(), getClient("2.11"), replicaID, &replicas.ResetStatusOpts{Status: "available"}).ExtractErr() th.AssertNoErr(t, err) } @@ -244,7 +245,7 @@ func TestResetStateSuccess(t *testing.T) { MockResetStateResponse(t) - err := replicas.ResetState(getClient("2.11"), replicaID, &replicas.ResetStateOpts{State: "active"}).ExtractErr() + err := replicas.ResetState(context.TODO(), getClient("2.11"), replicaID, &replicas.ResetStateOpts{State: "active"}).ExtractErr() th.AssertNoErr(t, err) } @@ -254,7 +255,7 @@ func TestResyncSuccess(t *testing.T) { MockResyncResponse(t) - err := replicas.Resync(getClient("2.11"), replicaID).ExtractErr() + err := replicas.Resync(context.TODO(), getClient("2.11"), replicaID).ExtractErr() th.AssertNoErr(t, err) } @@ -264,6 +265,6 @@ func TestPromoteSuccess(t *testing.T) { MockPromoteResponse(t) - err := replicas.Promote(getClient("2.11"), replicaID, &replicas.PromoteOpts{QuiesceWaitTime: 30}).ExtractErr() + err := replicas.Promote(context.TODO(), getClient("2.11"), replicaID, &replicas.PromoteOpts{QuiesceWaitTime: 30}).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go index ce3b7d43af..5c2ff36ad9 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/schedulerstats" @@ -15,7 +16,7 @@ func TestListPoolsDetail(t *testing.T) { HandlePoolsListSuccessfully(t) pages := 0 - err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := schedulerstats.ExtractPools(page) @@ -39,7 +40,7 @@ func TestListPoolsDetail(t *testing.T) { } pages = 0 - err = schedulerstats.ListDetail(client.ServiceClient(), schedulerstats.ListDetailOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err = schedulerstats.ListDetail(client.ServiceClient(), schedulerstats.ListDetailOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := schedulerstats.ExtractPools(page) diff --git a/openstack/sharedfilesystems/v2/securityservices/requests.go b/openstack/sharedfilesystems/v2/securityservices/requests.go index 8a781144be..b27d65e162 100644 --- a/openstack/sharedfilesystems/v2/securityservices/requests.go +++ b/openstack/sharedfilesystems/v2/securityservices/requests.go @@ -1,6 +1,8 @@ package securityservices import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -53,13 +55,13 @@ func (opts CreateOpts) ToSecurityServiceCreateMap() (map[string]interface{}, err // Create will create a new SecurityService based on the values in CreateOpts. To // extract the SecurityService object from the response, call the Extract method // on the CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSecurityServiceCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -67,8 +69,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing SecurityService with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -128,8 +130,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get retrieves the SecurityService with the provided ID. To extract the SecurityService // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -172,13 +174,13 @@ func (opts UpdateOpts) ToSecurityServiceUpdateMap() (map[string]interface{}, err // Update will update the SecurityService with provided information. To extract the updated // SecurityService from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSecurityServiceUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go b/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go index 2c854ac42a..391485cdc2 100644 --- a/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/securityservices/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -26,7 +27,7 @@ func TestCreate(t *testing.T) { Type: "kerberos", } - s, err := securityservices.Create(client.ServiceClient(), options).Extract() + s, err := securityservices.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "SecServ1") @@ -47,7 +48,7 @@ func TestCreateFails(t *testing.T) { Password: "***", } - _, err := securityservices.Create(client.ServiceClient(), options).Extract() + _, err := securityservices.Create(context.TODO(), client.ServiceClient(), options).Extract() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } @@ -60,7 +61,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := securityservices.Delete(client.ServiceClient(), "securityServiceID") + res := securityservices.Delete(context.TODO(), client.ServiceClient(), "securityServiceID") th.AssertNoErr(t, res.Err) } @@ -71,7 +72,7 @@ func TestList(t *testing.T) { MockListResponse(t) - allPages, err := securityservices.List(client.ServiceClient(), &securityservices.ListOpts{}).AllPages() + allPages, err := securityservices.List(client.ServiceClient(), &securityservices.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := securityservices.ExtractSecurityServices(allPages) th.AssertNoErr(t, err) @@ -123,7 +124,7 @@ func TestFilteredList(t *testing.T) { Type: "kerberos", } - allPages, err := securityservices.List(client.ServiceClient(), options).AllPages() + allPages, err := securityservices.List(client.ServiceClient(), options).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := securityservices.ExtractSecurityServices(allPages) th.AssertNoErr(t, err) @@ -173,7 +174,7 @@ func TestGet(t *testing.T) { Password: "supersecret", } - n, err := securityservices.Get(client.ServiceClient(), "3c829734-0679-4c17-9637-801da48c0d5f").Extract() + n, err := securityservices.Get(context.TODO(), client.ServiceClient(), "3c829734-0679-4c17-9637-801da48c0d5f").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, n) @@ -203,7 +204,7 @@ func TestUpdate(t *testing.T) { name := "SecServ2" options := securityservices.UpdateOpts{Name: &name} - s, err := securityservices.Update(client.ServiceClient(), "securityServiceID", options).Extract() + s, err := securityservices.Update(context.TODO(), client.ServiceClient(), "securityServiceID", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, s) } diff --git a/openstack/sharedfilesystems/v2/services/testing/requests_test.go b/openstack/sharedfilesystems/v2/services/testing/requests_test.go index 530c0b7760..16607415b6 100644 --- a/openstack/sharedfilesystems/v2/services/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/services/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/services" @@ -15,7 +16,7 @@ func TestListServices(t *testing.T) { HandleListSuccessfully(t) pages := 0 - err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := services.ExtractServices(page) diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/requests.go b/openstack/sharedfilesystems/v2/shareaccessrules/requests.go index 2d48827837..3b0c177a4a 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/requests.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/requests.go @@ -1,19 +1,21 @@ package shareaccessrules import ( + "context" + "github.com/gophercloud/gophercloud/v2" ) // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, accessID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, accessID), &r.Body, nil) _, 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) +func List(ctx context.Context, client *gophercloud.ServiceClient, shareID string) (r ListResult) { + resp, err := client.Get(ctx, listURL(client, shareID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go index 4bfd639f27..9c520348f0 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -18,7 +19,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - resp := shareaccessrules.Get(client.ServiceClient(), "507bf114-36f2-4f56-8cf4-857985ca87c1") + resp := shareaccessrules.Get(context.TODO(), client.ServiceClient(), "507bf114-36f2-4f56-8cf4-857985ca87c1") th.AssertNoErr(t, resp.Err) accessRule, err := resp.Extract() diff --git a/openstack/sharedfilesystems/v2/sharenetworks/requests.go b/openstack/sharedfilesystems/v2/sharenetworks/requests.go index 66a5b38523..198925eab2 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/requests.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/requests.go @@ -1,6 +1,8 @@ package sharenetworks import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -36,13 +38,13 @@ func (opts CreateOpts) ToShareNetworkCreateMap() (map[string]interface{}, error) // Create will create a new ShareNetwork based on the values in CreateOpts. To // extract the ShareNetwork object from the response, call the Extract method // on the CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToShareNetworkCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -50,8 +52,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing ShareNetwork with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -121,8 +123,8 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // Get retrieves the ShareNetwork with the provided ID. To extract the ShareNetwork // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -157,13 +159,13 @@ func (opts UpdateOpts) ToShareNetworkUpdateMap() (map[string]interface{}, error) // Update will update the ShareNetwork with provided information. To extract the updated // ShareNetwork from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToShareNetworkUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -191,13 +193,13 @@ func (opts AddSecurityServiceOpts) ToShareNetworkAddSecurityServiceMap() (map[st // AddSecurityService will add the security service to a ShareNetwork. To extract the updated // ShareNetwork from the response, call the Extract method on the UpdateResult. -func AddSecurityService(client *gophercloud.ServiceClient, id string, opts AddSecurityServiceOptsBuilder) (r UpdateResult) { +func AddSecurityService(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AddSecurityServiceOptsBuilder) (r UpdateResult) { b, err := opts.ToShareNetworkAddSecurityServiceMap() if err != nil { r.Err = err return } - resp, err := client.Post(addSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, addSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -225,13 +227,13 @@ func (opts RemoveSecurityServiceOpts) ToShareNetworkRemoveSecurityServiceMap() ( // RemoveSecurityService will remove the security service from a ShareNetwork. To extract the updated // ShareNetwork from the response, call the Extract method on the UpdateResult. -func RemoveSecurityService(client *gophercloud.ServiceClient, id string, opts RemoveSecurityServiceOptsBuilder) (r UpdateResult) { +func RemoveSecurityService(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RemoveSecurityServiceOptsBuilder) (r UpdateResult) { b, err := opts.ToShareNetworkRemoveSecurityServiceMap() if err != nil { r.Err = err return } - resp, err := client.Post(removeSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, removeSecurityServiceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go index c951ad7341..1cbbb6df5b 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -24,7 +25,7 @@ func TestCreate(t *testing.T) { NeutronSubnetID: "53482b62-2c84-4a53-b6ab-30d9d9800d06", } - n, err := sharenetworks.Create(client.ServiceClient(), options).Extract() + n, err := sharenetworks.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_network") @@ -40,7 +41,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - res := sharenetworks.Delete(client.ServiceClient(), "fa158a3d-6d9f-4187-9ca5-abbb82646eb2") + res := sharenetworks.Delete(context.TODO(), client.ServiceClient(), "fa158a3d-6d9f-4187-9ca5-abbb82646eb2") th.AssertNoErr(t, res.Err) } @@ -51,7 +52,7 @@ func TestListDetail(t *testing.T) { MockListResponse(t) - allPages, err := sharenetworks.ListDetail(client.ServiceClient(), &sharenetworks.ListOpts{}).AllPages() + allPages, err := sharenetworks.ListDetail(client.ServiceClient(), &sharenetworks.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := sharenetworks.ExtractShareNetworks(allPages) @@ -123,7 +124,7 @@ func TestPaginatedListDetail(t *testing.T) { count := 0 - err := sharenetworks.ListDetail(client.ServiceClient(), options).EachPage(func(page pagination.Page) (bool, error) { + err := sharenetworks.ListDetail(client.ServiceClient(), options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ _, err := sharenetworks.ExtractShareNetworks(page) if err != nil { @@ -162,7 +163,7 @@ func TestGet(t *testing.T) { ProjectID: "16e1ab15c35a457e9c2b2aa189f544e1", } - n, err := sharenetworks.Get(client.ServiceClient(), "7f950b52-6141-4a08-bbb5-bb7ffa3ea5fd").Extract() + n, err := sharenetworks.Get(context.TODO(), client.ServiceClient(), "7f950b52-6141-4a08-bbb5-bb7ffa3ea5fd").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, n) @@ -200,7 +201,7 @@ func TestUpdateNeutron(t *testing.T) { NeutronSubnetID: "new-neutron-subnet-id", } - v, err := sharenetworks.Update(client.ServiceClient(), "713df749-aac0-4a54-af52-10f6c991e80c", options).Extract() + v, err := sharenetworks.Update(context.TODO(), client.ServiceClient(), "713df749-aac0-4a54-af52-10f6c991e80c", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, v) } @@ -236,7 +237,7 @@ func TestUpdateNova(t *testing.T) { NovaNetID: "new-nova-id", } - v, err := sharenetworks.Update(client.ServiceClient(), "713df749-aac0-4a54-af52-10f6c991e80c", options).Extract() + v, err := sharenetworks.Update(context.TODO(), client.ServiceClient(), "713df749-aac0-4a54-af52-10f6c991e80c", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, v) } @@ -266,7 +267,7 @@ func TestAddSecurityService(t *testing.T) { } options := sharenetworks.AddSecurityServiceOpts{SecurityServiceID: "securityServiceID"} - s, err := sharenetworks.AddSecurityService(client.ServiceClient(), "shareNetworkID", options).Extract() + s, err := sharenetworks.AddSecurityService(context.TODO(), client.ServiceClient(), "shareNetworkID", options).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, s) } @@ -279,6 +280,6 @@ func TestRemoveSecurityService(t *testing.T) { MockRemoveSecurityServiceResponse(t) options := sharenetworks.RemoveSecurityServiceOpts{SecurityServiceID: "securityServiceID"} - _, err := sharenetworks.RemoveSecurityService(client.ServiceClient(), "shareNetworkID", options).Extract() + _, err := sharenetworks.RemoveSecurityService(context.TODO(), client.ServiceClient(), "shareNetworkID", options).Extract() th.AssertNoErr(t, err) } diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index c5b38628d5..7562b2de19 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -1,6 +1,8 @@ package shares import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -57,13 +59,13 @@ func (opts CreateOpts) ToShareCreateMap() (map[string]interface{}, error) { // Create will create a new Share based on the values in CreateOpts. To extract // the Share object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToShareCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -164,31 +166,31 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } // Delete will delete an existing Share with the given UUID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ListExportLocations will list shareID's export locations. // Client must have Microversion set; minimum supported microversion for ListExportLocations is 2.9. -func ListExportLocations(client *gophercloud.ServiceClient, id string) (r ListExportLocationsResult) { - resp, err := client.Get(listExportLocationsURL(client, id), &r.Body, nil) +func ListExportLocations(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ListExportLocationsResult) { + resp, err := client.Get(ctx, listExportLocationsURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetExportLocation will get shareID's export location by an ID. // Client must have Microversion set; minimum supported microversion for GetExportLocation is 2.9. -func GetExportLocation(client *gophercloud.ServiceClient, shareID string, id string) (r GetExportLocationResult) { - resp, err := client.Get(getExportLocationURL(client, shareID, id), &r.Body, nil) +func GetExportLocation(ctx context.Context, client *gophercloud.ServiceClient, shareID string, id string) (r GetExportLocationResult) { + resp, err := client.Get(ctx, getExportLocationURL(client, shareID, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -220,13 +222,13 @@ func (opts GrantAccessOpts) ToGrantAccessMap() (map[string]interface{}, error) { // GrantAccess will grant access to a Share based on the values in GrantAccessOpts. To extract // the GrantAccess object from the response, call the Extract method on the GrantAccessResult. // Client must have Microversion set; minimum supported microversion for GrantAccess is 2.7. -func GrantAccess(client *gophercloud.ServiceClient, id string, opts GrantAccessOptsBuilder) (r GrantAccessResult) { +func GrantAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts GrantAccessOptsBuilder) (r GrantAccessResult) { b, err := opts.ToGrantAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(grantAccessURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, grantAccessURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -256,14 +258,14 @@ func (opts RevokeAccessOpts) ToRevokeAccessMap() (map[string]interface{}, error) // RevokeAccessResult contains only the error. To extract it, call the ExtractErr method on // the RevokeAccessResult. Client must have Microversion set; minimum supported microversion // for RevokeAccess is 2.7. -func RevokeAccess(client *gophercloud.ServiceClient, id string, opts RevokeAccessOptsBuilder) (r RevokeAccessResult) { +func RevokeAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RevokeAccessOptsBuilder) (r RevokeAccessResult) { b, err := opts.ToRevokeAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(revokeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, revokeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -273,9 +275,9 @@ func RevokeAccess(client *gophercloud.ServiceClient, id string, opts RevokeAcces // ListAccessRights lists all access rules assigned to a Share based on its id. To extract // the AccessRight slice from the response, call the Extract method on the ListAccessRightsResult. // Client must have Microversion set; minimum supported microversion for ListAccessRights is 2.7. -func ListAccessRights(client *gophercloud.ServiceClient, id string) (r ListAccessRightsResult) { +func ListAccessRights(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ListAccessRightsResult) { requestBody := map[string]interface{}{"access_list": nil} - resp, err := client.Post(listAccessRightsURL(client, id), requestBody, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, listAccessRightsURL(client, id), requestBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -305,14 +307,14 @@ func (opts ExtendOpts) ToShareExtendMap() (map[string]interface{}, error) { // Extend will extend the capacity of an existing share. ExtendResult contains only the error. // To extract it, call the ExtractErr method on the ExtendResult. // Client must have Microversion set; minimum supported microversion for Extend is 2.7. -func Extend(client *gophercloud.ServiceClient, id string, opts ExtendOptsBuilder) (r ExtendResult) { +func Extend(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ExtendOptsBuilder) (r ExtendResult) { b, err := opts.ToShareExtendMap() if err != nil { r.Err = err return } - resp, err := client.Post(extendURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, extendURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -342,14 +344,14 @@ func (opts ShrinkOpts) ToShareShrinkMap() (map[string]interface{}, error) { // Shrink will shrink the capacity of an existing share. ShrinkResult contains only the error. // To extract it, call the ExtractErr method on the ShrinkResult. // Client must have Microversion set; minimum supported microversion for Shrink is 2.7. -func Shrink(client *gophercloud.ServiceClient, id string, opts ShrinkOptsBuilder) (r ShrinkResult) { +func Shrink(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ShrinkOptsBuilder) (r ShrinkResult) { b, err := opts.ToShareShrinkMap() if err != nil { r.Err = err return } - resp, err := client.Post(shrinkURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, shrinkURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -382,13 +384,13 @@ func (opts UpdateOpts) ToShareUpdateMap() (map[string]interface{}, error) { // Update will update the Share with provided information. To extract the updated // Share from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToShareUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -397,16 +399,16 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder // GetMetadata retrieves metadata of the specified share. To extract the retrieved // metadata from the response, call the Extract method on the MetadataResult. -func GetMetadata(client *gophercloud.ServiceClient, id string) (r MetadataResult) { - resp, err := client.Get(getMetadataURL(client, id), &r.Body, nil) +func GetMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string) (r MetadataResult) { + resp, err := client.Get(ctx, getMetadataURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMetadatum retrieves a single metadata item of the specified share. To extract the retrieved // metadata from the response, call the Extract method on the GetMetadatumResult. -func GetMetadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { - resp, err := client.Get(getMetadatumURL(client, id, key), &r.Body, nil) +func GetMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { + resp, err := client.Get(ctx, getMetadatumURL(client, id, key), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -434,14 +436,14 @@ type SetMetadataOptsBuilder interface { // Existing metadata items are either kept or overwritten by the metadata from the request. // To extract the updated metadata from the response, call the Extract // method on the MetadataResult. -func SetMetadata(client *gophercloud.ServiceClient, id string, opts SetMetadataOptsBuilder) (r MetadataResult) { +func SetMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts SetMetadataOptsBuilder) (r MetadataResult) { b, err := opts.ToSetMetadataMap() if err != nil { r.Err = err return } - resp, err := client.Post(setMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, setMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -471,14 +473,14 @@ type UpdateMetadataOptsBuilder interface { // All existing metadata items are discarded and replaced by the metadata from the request. // To extract the updated metadata from the response, call the Extract // method on the MetadataResult. -func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r MetadataResult) { +func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r MetadataResult) { b, err := opts.ToUpdateMetadataMap() if err != nil { r.Err = err return } - resp, err := client.Post(updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -486,8 +488,8 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet } // DeleteMetadatum deletes a single key-value pair from the metadata of the specified share. -func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { - resp, err := client.Delete(deleteMetadatumURL(client, id, key), &gophercloud.RequestOpts{ +func DeleteMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { + resp, err := client.Delete(ctx, deleteMetadatumURL(client, id, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -518,14 +520,14 @@ func (opts RevertOpts) ToShareRevertMap() (map[string]interface{}, error) { // Revert will revert the existing share to a Snapshot. RevertResult contains only the error. // To extract it, call the ExtractErr method on the RevertResult. // Client must have Microversion set; minimum supported microversion for Revert is 2.27. -func Revert(client *gophercloud.ServiceClient, id string, opts RevertOptsBuilder) (r RevertResult) { +func Revert(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RevertOptsBuilder) (r RevertResult) { b, err := opts.ToShareRevertMap() if err != nil { r.Err = err return } - resp, err := client.Post(revertURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, revertURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -555,14 +557,14 @@ func (opts ResetStatusOpts) ToShareResetStatusMap() (map[string]interface{}, err // ResetStatus will reset the existing share status. ResetStatusResult contains only the error. // To extract it, call the ExtractErr method on the ResetStatusResult. // Client must have Microversion set; minimum supported microversion for ResetStatus is 2.7. -func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { +func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { b, err := opts.ToShareResetStatusMap() if err != nil { r.Err = err return } - resp, err := client.Post(resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -572,11 +574,11 @@ func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusO // ForceDelete will delete the existing share in any state. ForceDeleteResult contains only the error. // To extract it, call the ExtractErr method on the ForceDeleteResult. // Client must have Microversion set; minimum supported microversion for ForceDelete is 2.7. -func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { +func ForceDelete(ctx context.Context, 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{ + resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -587,11 +589,11 @@ func ForceDelete(client *gophercloud.ServiceClient, id string) (r ForceDeleteRes // service without deleting the share. UnmanageResult contains only the error. // To extract it, call the ExtractErr method on the UnmanageResult. // Client must have Microversion set; minimum supported microversion for Unmanage is 2.7. -func Unmanage(client *gophercloud.ServiceClient, id string) (r UnmanageResult) { +func Unmanage(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnmanageResult) { b := map[string]interface{}{ "unmanage": nil, } - resp, err := client.Post(unmanageURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, unmanageURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index abe2105df2..f9af3da84b 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -16,7 +17,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := &shares.CreateOpts{Size: 1, Name: "my_test_share", ShareProto: "NFS"} - n, err := shares.Create(client.ServiceClient(), options).Extract() + n, err := shares.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_test_share") @@ -38,7 +39,7 @@ func TestUpdate(t *testing.T) { DisplayDescription: &description, IsPublic: &iFalse, } - n, err := shares.Update(client.ServiceClient(), shareID, options).Extract() + n, err := shares.Update(context.TODO(), client.ServiceClient(), shareID, options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_new_test_share") @@ -52,7 +53,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - result := shares.Delete(client.ServiceClient(), shareID) + result := shares.Delete(context.TODO(), client.ServiceClient(), shareID) th.AssertNoErr(t, result.Err) } @@ -62,7 +63,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - s, err := shares.Get(client.ServiceClient(), shareID).Extract() + s, err := shares.Get(context.TODO(), client.ServiceClient(), shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, &shares.Share{ AvailabilityZone: "nova", @@ -112,7 +113,7 @@ func TestListDetail(t *testing.T) { MockListDetailResponse(t) - allPages, err := shares.ListDetail(client.ServiceClient(), &shares.ListOpts{}).AllPages() + allPages, err := shares.ListDetail(client.ServiceClient(), &shares.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) @@ -173,7 +174,7 @@ func TestListExportLocationsSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for List Export Locations is 2.9 c.Microversion = "2.9" - s, err := shares.ListExportLocations(c, shareID).Extract() + s, err := shares.ListExportLocations(context.TODO(), c, shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, []shares.ExportLocation{ @@ -197,7 +198,7 @@ func TestGetExportLocationSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for Get Export Location is 2.9 c.Microversion = "2.9" - s, err := shares.GetExportLocation(c, shareID, "80ed63fc-83bc-4afc-b881-da4a345ac83d").Extract() + s, err := shares.GetExportLocation(context.TODO(), c, shareID, "80ed63fc-83bc-4afc-b881-da4a345ac83d").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, &shares.ExportLocation{ @@ -224,7 +225,7 @@ func TestGrantAcessSuccess(t *testing.T) { grantAccessReq.AccessTo = "0.0.0.0/0" grantAccessReq.AccessLevel = "rw" - s, err := shares.GrantAccess(c, shareID, grantAccessReq).Extract() + s, err := shares.GrantAccess(context.TODO(), c, shareID, grantAccessReq).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, &shares.AccessRight{ @@ -250,7 +251,7 @@ func TestRevokeAccessSuccess(t *testing.T) { options := &shares.RevokeAccessOpts{AccessID: "a2f226a5-cee8-430b-8a03-78a59bd84ee8"} - err := shares.RevokeAccess(c, shareID, options).ExtractErr() + err := shares.RevokeAccess(context.TODO(), c, shareID, options).ExtractErr() th.AssertNoErr(t, err) } @@ -264,7 +265,7 @@ func TestListAccessRightsSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 c.Microversion = "2.7" - s, err := shares.ListAccessRights(c, shareID).Extract() + s, err := shares.ListAccessRights(context.TODO(), c, shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, []shares.AccessRight{ @@ -290,7 +291,7 @@ func TestExtendSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 c.Microversion = "2.7" - err := shares.Extend(c, shareID, &shares.ExtendOpts{NewSize: 2}).ExtractErr() + err := shares.Extend(context.TODO(), c, shareID, &shares.ExtendOpts{NewSize: 2}).ExtractErr() th.AssertNoErr(t, err) } @@ -304,7 +305,7 @@ func TestShrinkSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for Grant Access is 2.7 c.Microversion = "2.7" - err := shares.Shrink(c, shareID, &shares.ShrinkOpts{NewSize: 1}).ExtractErr() + err := shares.Shrink(context.TODO(), c, shareID, &shares.ShrinkOpts{NewSize: 1}).ExtractErr() th.AssertNoErr(t, err) } @@ -316,7 +317,7 @@ func TestGetMetadataSuccess(t *testing.T) { c := client.ServiceClient() - actual, err := shares.GetMetadata(c, shareID).Extract() + actual, err := shares.GetMetadata(context.TODO(), c, shareID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) } @@ -329,7 +330,7 @@ func TestGetMetadatumSuccess(t *testing.T) { c := client.ServiceClient() - actual, err := shares.GetMetadatum(c, shareID, "foo").Extract() + actual, err := shares.GetMetadatum(context.TODO(), c, shareID, "foo").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) } @@ -342,7 +343,7 @@ func TestSetMetadataSuccess(t *testing.T) { c := client.ServiceClient() - actual, err := shares.SetMetadata(c, shareID, &shares.SetMetadataOpts{Metadata: map[string]string{"foo": "bar"}}).Extract() + actual, err := shares.SetMetadata(context.TODO(), c, shareID, &shares.SetMetadataOpts{Metadata: map[string]string{"foo": "bar"}}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) } @@ -355,7 +356,7 @@ func TestUpdateMetadataSuccess(t *testing.T) { c := client.ServiceClient() - actual, err := shares.UpdateMetadata(c, shareID, &shares.UpdateMetadataOpts{Metadata: map[string]string{"foo": "bar"}}).Extract() + actual, err := shares.UpdateMetadata(context.TODO(), c, shareID, &shares.UpdateMetadataOpts{Metadata: map[string]string{"foo": "bar"}}).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, map[string]string{"foo": "bar"}, actual) } @@ -368,7 +369,7 @@ func TestUnsetMetadataSuccess(t *testing.T) { c := client.ServiceClient() - err := shares.DeleteMetadatum(c, shareID, "foo").ExtractErr() + err := shares.DeleteMetadatum(context.TODO(), c, shareID, "foo").ExtractErr() th.AssertNoErr(t, err) } @@ -382,7 +383,7 @@ func TestRevertSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for Revert is 2.27 c.Microversion = "2.27" - err := shares.Revert(c, shareID, &shares.RevertOpts{SnapshotID: "ddeac769-9742-497f-b985-5bcfa94a3fd6"}).ExtractErr() + err := shares.Revert(context.TODO(), c, shareID, &shares.RevertOpts{SnapshotID: "ddeac769-9742-497f-b985-5bcfa94a3fd6"}).ExtractErr() th.AssertNoErr(t, err) } @@ -396,7 +397,7 @@ func TestResetStatusSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for ResetStatus is 2.7 c.Microversion = "2.7" - err := shares.ResetStatus(c, shareID, &shares.ResetStatusOpts{Status: "error"}).ExtractErr() + err := shares.ResetStatus(context.TODO(), c, shareID, &shares.ResetStatusOpts{Status: "error"}).ExtractErr() th.AssertNoErr(t, err) } @@ -410,7 +411,7 @@ func TestForceDeleteSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for ForceDelete is 2.7 c.Microversion = "2.7" - err := shares.ForceDelete(c, shareID).ExtractErr() + err := shares.ForceDelete(context.TODO(), c, shareID).ExtractErr() th.AssertNoErr(t, err) } @@ -424,6 +425,6 @@ func TestUnmanageSuccess(t *testing.T) { // Client c must have Microversion set; minimum supported microversion for Unmanage is 2.7 c.Microversion = "2.7" - err := shares.Unmanage(c, shareID).ExtractErr() + err := shares.Unmanage(context.TODO(), c, shareID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/sharedfilesystems/v2/sharetransfers/requests.go b/openstack/sharedfilesystems/v2/sharetransfers/requests.go index fc5de44d51..01fa577708 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/requests.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/requests.go @@ -1,6 +1,8 @@ package sharetransfers import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -27,13 +29,13 @@ func (opts CreateOpts) ToTransferCreateMap() (map[string]interface{}, error) { } // Create will create a share tranfer request based on the values in CreateOpts. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, 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{ + resp, err := client.Post(ctx, transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -56,13 +58,13 @@ func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { } // Accept will accept a share tranfer request based on the values in AcceptOpts. -func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r AcceptResult) { +func Accept(ctx context.Context, 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{ + resp, err := client.Post(ctx, acceptURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -70,8 +72,8 @@ func Accept(client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r Ac } // Delete deletes a share transfer. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), &gophercloud.RequestOpts{ +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), &gophercloud.RequestOpts{ // DELETE requests response with a 200 code, adding it here OkCodes: []int{200, 202, 204}, }) @@ -162,8 +164,8 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // 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) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go index bd2e454b46..a0a2fefcfa 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetransfers" @@ -14,7 +15,7 @@ func TestCreateTransfer(t *testing.T) { defer th.TeardownHTTP() HandleCreateTransfer(t) - actual, err := sharetransfers.Create(client.ServiceClient(), TransferRequest).Extract() + actual, err := sharetransfers.Create(context.TODO(), client.ServiceClient(), TransferRequest).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, TransferResponse, *actual) } @@ -24,7 +25,7 @@ func TestAcceptTransfer(t *testing.T) { defer th.TeardownHTTP() HandleAcceptTransfer(t) - err := sharetransfers.Accept(client.ServiceClient(), TransferResponse.ID, AcceptRequest).ExtractErr() + err := sharetransfers.Accept(context.TODO(), client.ServiceClient(), TransferResponse.ID, AcceptRequest).ExtractErr() th.AssertNoErr(t, err) } @@ -33,7 +34,7 @@ func TestDeleteTransfer(t *testing.T) { defer th.TeardownHTTP() HandleDeleteTransfer(t) - err := sharetransfers.Delete(client.ServiceClient(), TransferResponse.ID).ExtractErr() + err := sharetransfers.Delete(context.TODO(), client.ServiceClient(), TransferResponse.ID).ExtractErr() th.AssertNoErr(t, err) } @@ -46,7 +47,7 @@ func TestListTransfers(t *testing.T) { expectedResponse[0].AuthKey = "" count := 0 - err := sharetransfers.List(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { + err := sharetransfers.List(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := sharetransfers.ExtractTransfers(page) @@ -69,7 +70,7 @@ func TestListTransfersDetail(t *testing.T) { expectedResponse[0].AuthKey = "" count := 0 - err := sharetransfers.ListDetail(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).EachPage(func(page pagination.Page) (bool, error) { + err := sharetransfers.ListDetail(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := sharetransfers.ExtractTransfers(page) @@ -91,7 +92,7 @@ func TestListTransfersAllPages(t *testing.T) { expectedResponse := TransferListResponse expectedResponse[0].AuthKey = "" - allPages, err := sharetransfers.List(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).AllPages() + allPages, err := sharetransfers.List(client.ServiceClient(), &sharetransfers.ListOpts{AllTenants: true}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := sharetransfers.ExtractTransfers(allPages) th.AssertNoErr(t, err) @@ -106,7 +107,7 @@ func TestGetTransfer(t *testing.T) { expectedResponse := TransferResponse expectedResponse.AuthKey = "" - actual, err := sharetransfers.Get(client.ServiceClient(), TransferResponse.ID).Extract() + actual, err := sharetransfers.Get(context.TODO(), client.ServiceClient(), TransferResponse.ID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expectedResponse, *actual) } diff --git a/openstack/sharedfilesystems/v2/sharetypes/requests.go b/openstack/sharedfilesystems/v2/sharetypes/requests.go index 93c6c26177..ea6aab3bd5 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/requests.go +++ b/openstack/sharedfilesystems/v2/sharetypes/requests.go @@ -1,6 +1,8 @@ package sharetypes import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -40,13 +42,13 @@ func (opts CreateOpts) ToShareTypeCreateMap() (map[string]interface{}, error) { // Create will create a new ShareType based on the values in CreateOpts. To // extract the ShareType object from the response, call the Extract method // on the CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToShareTypeCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -54,8 +56,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete will delete the existing ShareType with the provided ID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -96,15 +98,15 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // GetDefault will retrieve the default ShareType. -func GetDefault(client *gophercloud.ServiceClient) (r GetDefaultResult) { - resp, err := client.Get(getDefaultURL(client), &r.Body, nil) +func GetDefault(ctx context.Context, client *gophercloud.ServiceClient) (r GetDefaultResult) { + resp, err := client.Get(ctx, getDefaultURL(client), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetExtraSpecs will retrieve the extra specifications for a given ShareType. -func GetExtraSpecs(client *gophercloud.ServiceClient, id string) (r GetExtraSpecsResult) { - resp, err := client.Get(getExtraSpecsURL(client, id), &r.Body, nil) +func GetExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetExtraSpecsResult) { + resp, err := client.Get(ctx, getExtraSpecsURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -129,14 +131,14 @@ func (opts SetExtraSpecsOpts) ToShareTypeSetExtraSpecsMap() (map[string]interfac // SetExtraSpecs will set new specifications for a ShareType based on the values // in SetExtraSpecsOpts. To extract the extra specifications object from the response, // call the Extract method on the SetExtraSpecsResult. -func SetExtraSpecs(client *gophercloud.ServiceClient, id string, opts SetExtraSpecsOptsBuilder) (r SetExtraSpecsResult) { +func SetExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, id string, opts SetExtraSpecsOptsBuilder) (r SetExtraSpecsResult) { b, err := opts.ToShareTypeSetExtraSpecsMap() if err != nil { r.Err = err return } - resp, err := client.Post(setExtraSpecsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, setExtraSpecsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -144,15 +146,15 @@ func SetExtraSpecs(client *gophercloud.ServiceClient, id string, opts SetExtraSp } // UnsetExtraSpecs will unset an extra specification for an existing ShareType. -func UnsetExtraSpecs(client *gophercloud.ServiceClient, id string, key string) (r UnsetExtraSpecsResult) { - resp, err := client.Delete(unsetExtraSpecsURL(client, id, key), nil) +func UnsetExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, id string, key string) (r UnsetExtraSpecsResult) { + resp, err := client.Delete(ctx, unsetExtraSpecsURL(client, id, key), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ShowAccess will show access details for an existing ShareType. -func ShowAccess(client *gophercloud.ServiceClient, id string) (r ShowAccessResult) { - resp, err := client.Get(showAccessURL(client, id), &r.Body, nil) +func ShowAccess(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShowAccessResult) { + resp, err := client.Get(ctx, showAccessURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -176,14 +178,14 @@ func (opts AccessOpts) ToAddAccessMap() (map[string]interface{}, error) { // AddAccess will add access to a ShareType based on the values // in AccessOpts. -func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { +func AddAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { b, err := opts.ToAddAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(addAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, addAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -204,14 +206,14 @@ func (opts AccessOpts) ToRemoveAccessMap() (map[string]interface{}, error) { // RemoveAccess will remove access to a ShareType based on the values // in AccessOpts. -func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { +func RemoveAccess(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { b, err := opts.ToRemoveAccessMap() if err != nil { r.Err = err return } - resp, err := client.Post(removeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, removeAccessURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go index 9329e0694c..99d9c2af29 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2" @@ -28,7 +29,7 @@ func TestCreate(t *testing.T) { ExtraSpecs: extraSpecs, } - st, err := sharetypes.Create(client.ServiceClient(), options).Extract() + st, err := sharetypes.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, st.Name, "my_new_share_type") @@ -41,7 +42,7 @@ func TestCreateFails(t *testing.T) { Name: "my_new_share_type", } - _, err := sharetypes.Create(client.ServiceClient(), options).Extract() + _, err := sharetypes.Create(context.TODO(), client.ServiceClient(), options).Extract() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } @@ -54,7 +55,7 @@ func TestCreateFails(t *testing.T) { ExtraSpecs: extraSpecs, } - _, err = sharetypes.Create(client.ServiceClient(), options).Extract() + _, err = sharetypes.Create(context.TODO(), client.ServiceClient(), options).Extract() if _, ok := err.(gophercloud.ErrMissingInput); !ok { t.Fatal("ErrMissingInput was expected to occur") } @@ -66,7 +67,7 @@ func TestDelete(t *testing.T) { defer th.TeardownHTTP() MockDeleteResponse(t) - res := sharetypes.Delete(client.ServiceClient(), "shareTypeID") + res := sharetypes.Delete(context.TODO(), client.ServiceClient(), "shareTypeID") th.AssertNoErr(t, res.Err) } @@ -77,7 +78,7 @@ func TestList(t *testing.T) { MockListResponse(t) - allPages, err := sharetypes.List(client.ServiceClient(), &sharetypes.ListOpts{}).AllPages() + allPages, err := sharetypes.List(client.ServiceClient(), &sharetypes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := sharetypes.ExtractShareTypes(allPages) th.AssertNoErr(t, err) @@ -115,7 +116,7 @@ func TestGetDefault(t *testing.T) { RequiredExtraSpecs: map[string]interface{}(nil), } - actual, err := sharetypes.GetDefault(client.ServiceClient()).Extract() + actual, err := sharetypes.GetDefault(context.TODO(), client.ServiceClient()).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, &expected, actual) } @@ -127,7 +128,7 @@ func TestGetExtraSpecs(t *testing.T) { MockGetExtraSpecsResponse(t) - st, err := sharetypes.GetExtraSpecs(client.ServiceClient(), "shareTypeID").Extract() + st, err := sharetypes.GetExtraSpecs(context.TODO(), client.ServiceClient(), "shareTypeID").Extract() th.AssertNoErr(t, err) th.AssertEquals(t, st["snapshot_support"], "True") @@ -146,7 +147,7 @@ func TestSetExtraSpecs(t *testing.T) { ExtraSpecs: map[string]interface{}{"my_key": "my_value"}, } - es, err := sharetypes.SetExtraSpecs(client.ServiceClient(), "shareTypeID", options).Extract() + es, err := sharetypes.SetExtraSpecs(context.TODO(), client.ServiceClient(), "shareTypeID", options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, es["my_key"], "my_value") @@ -158,7 +159,7 @@ func TestUnsetExtraSpecs(t *testing.T) { defer th.TeardownHTTP() MockUnsetExtraSpecsResponse(t) - res := sharetypes.UnsetExtraSpecs(client.ServiceClient(), "shareTypeID", "my_key") + res := sharetypes.UnsetExtraSpecs(context.TODO(), client.ServiceClient(), "shareTypeID", "my_key") th.AssertNoErr(t, res.Err) } @@ -180,7 +181,7 @@ func TestShowAccess(t *testing.T) { }, } - shareType, err := sharetypes.ShowAccess(client.ServiceClient(), "shareTypeID").Extract() + shareType, err := sharetypes.ShowAccess(context.TODO(), client.ServiceClient(), "shareTypeID").Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expected, shareType) @@ -197,7 +198,7 @@ func TestAddAccess(t *testing.T) { Project: "e1284adea3ee4d2482af5ed214f3ad90", } - err := sharetypes.AddAccess(client.ServiceClient(), "shareTypeID", options).ExtractErr() + err := sharetypes.AddAccess(context.TODO(), client.ServiceClient(), "shareTypeID", options).ExtractErr() th.AssertNoErr(t, err) } @@ -212,6 +213,6 @@ func TestRemoveAccess(t *testing.T) { Project: "e1284adea3ee4d2482af5ed214f3ad90", } - err := sharetypes.RemoveAccess(client.ServiceClient(), "shareTypeID", options).ExtractErr() + err := sharetypes.RemoveAccess(context.TODO(), client.ServiceClient(), "shareTypeID", options).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/sharedfilesystems/v2/snapshots/requests.go b/openstack/sharedfilesystems/v2/snapshots/requests.go index efaccff78a..5e11d2cbc1 100644 --- a/openstack/sharedfilesystems/v2/snapshots/requests.go +++ b/openstack/sharedfilesystems/v2/snapshots/requests.go @@ -1,6 +1,8 @@ package snapshots import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -39,13 +41,13 @@ func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { // Create will create a new Snapshot based on the values in CreateOpts. To extract // the Snapshot object from the response, call the Extract method on the // CreateResult. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToSnapshotCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -114,15 +116,15 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } // Delete will delete an existing Snapshot with the given UUID. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get will get a single snapshot with given UUID -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -151,13 +153,13 @@ func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { // Update will update the Snapshot with provided information. To extract the updated // Snapshot from the response, call the Extract method on the UpdateResult. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToSnapshotUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -188,14 +190,14 @@ func (opts ResetStatusOpts) ToSnapshotResetStatusMap() (map[string]interface{}, // 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) { +func ResetStatus(ctx context.Context, 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{ + resp, err := client.Post(ctx, resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -204,11 +206,11 @@ func ResetStatus(client *gophercloud.ServiceClient, id string, opts ResetStatusO // 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) { +func ForceDelete(ctx context.Context, 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{ + resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go index 2d0518e35e..759ad0d78d 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/request_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "time" @@ -16,7 +17,7 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := &snapshots.CreateOpts{ShareID: shareID, Name: "test snapshot", Description: "test description"} - n, err := snapshots.Create(client.ServiceClient(), options).Extract() + n, err := snapshots.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "test snapshot") @@ -38,7 +39,7 @@ func TestUpdate(t *testing.T) { DisplayName: &name, DisplayDescription: &description, } - n, err := snapshots.Update(client.ServiceClient(), snapshotID, options).Extract() + n, err := snapshots.Update(context.TODO(), client.ServiceClient(), snapshotID, options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_new_test_snapshot") @@ -51,7 +52,7 @@ func TestDelete(t *testing.T) { MockDeleteResponse(t) - result := snapshots.Delete(client.ServiceClient(), snapshotID) + result := snapshots.Delete(context.TODO(), client.ServiceClient(), snapshotID) th.AssertNoErr(t, result.Err) } @@ -61,7 +62,7 @@ func TestGet(t *testing.T) { MockGetResponse(t) - s, err := snapshots.Get(client.ServiceClient(), snapshotID).Extract() + s, err := snapshots.Get(context.TODO(), client.ServiceClient(), snapshotID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, s, &snapshots.Snapshot{ ID: snapshotID, @@ -93,7 +94,7 @@ func TestListDetail(t *testing.T) { MockListDetailResponse(t) - allPages, err := snapshots.ListDetail(client.ServiceClient(), &snapshots.ListOpts{}).AllPages() + allPages, err := snapshots.ListDetail(client.ServiceClient(), &snapshots.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) @@ -134,7 +135,7 @@ func TestResetStatusSuccess(t *testing.T) { c := client.ServiceClient() - err := snapshots.ResetStatus(c, snapshotID, &snapshots.ResetStatusOpts{Status: "error"}).ExtractErr() + err := snapshots.ResetStatus(context.TODO(), c, snapshotID, &snapshots.ResetStatusOpts{Status: "error"}).ExtractErr() th.AssertNoErr(t, err) } @@ -146,6 +147,6 @@ func TestForceDeleteSuccess(t *testing.T) { c := client.ServiceClient() - err := snapshots.ForceDelete(c, snapshotID).ExtractErr() + err := snapshots.ForceDelete(context.TODO(), c, snapshotID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/openstack/testing/client_test.go b/openstack/testing/client_test.go index 9efa0ac182..2c158dbfc3 100644 --- a/openstack/testing/client_test.go +++ b/openstack/testing/client_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -55,7 +56,7 @@ func TestAuthenticatedClientV3(t *testing.T) { TenantName: "project", IdentityEndpoint: th.Endpoint(), } - client, err := openstack.AuthenticatedClient(options) + client, err := openstack.AuthenticatedClient(context.TODO(), options) th.AssertNoErr(t, err) th.CheckEquals(t, ID, client.TokenID) } @@ -158,7 +159,7 @@ func TestAuthenticatedClientV2(t *testing.T) { Password: "secret", IdentityEndpoint: th.Endpoint(), } - client, err := openstack.AuthenticatedClient(options) + client, err := openstack.AuthenticatedClient(context.TODO(), options) th.AssertNoErr(t, err) th.CheckEquals(t, "01234567890", client.TokenID) } @@ -283,7 +284,7 @@ func TestIdentityAdminV3Client(t *testing.T) { DomainID: "12345", IdentityEndpoint: th.Endpoint(), } - pc, err := openstack.AuthenticatedClient(options) + pc, err := openstack.AuthenticatedClient(context.TODO(), options) th.AssertNoErr(t, err) sc, err := openstack.NewIdentityV3(pc, gophercloud.EndpointOpts{ Availability: gophercloud.AvailabilityAdmin, @@ -300,7 +301,7 @@ func testAuthenticatedClientFails(t *testing.T, endpoint string) { TenantName: "project", IdentityEndpoint: endpoint, } - _, err := openstack.AuthenticatedClient(options) + _, err := openstack.AuthenticatedClient(context.TODO(), options) if err == nil { t.Fatal("expected error but call succeeded") } diff --git a/openstack/utils/choose_version.go b/openstack/utils/choose_version.go index d3c27e241b..6c720e57ef 100644 --- a/openstack/utils/choose_version.go +++ b/openstack/utils/choose_version.go @@ -1,6 +1,7 @@ package utils import ( + "context" "fmt" "strconv" "strings" @@ -27,7 +28,7 @@ var goodStatus = map[string]bool{ // endpoint that is among the recognized versions, it will be used regardless of priority. // It returns the highest-Priority Version, OR exact match with client endpoint, // among the alternatives that are provided, as well as its corresponding endpoint. -func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) { +func ChooseVersion(ctx context.Context, client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) { type linkResp struct { Href string `json:"href"` Rel string `json:"rel"` @@ -63,7 +64,7 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* } var resp response - _, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{ + _, err := client.Request(ctx, "GET", client.IdentityBase, &gophercloud.RequestOpts{ JSONResponse: &resp, OkCodes: []int{200, 300}, }) @@ -122,7 +123,7 @@ type SupportedMicroversions struct { } // GetSupportedMicroversions returns the minimum and maximum microversion that is supported by the ServiceClient Endpoint. -func GetSupportedMicroversions(client *gophercloud.ServiceClient) (SupportedMicroversions, error) { +func GetSupportedMicroversions(ctx context.Context, client *gophercloud.ServiceClient) (SupportedMicroversions, error) { type valueResp struct { ID string `json:"id"` Status string `json:"status"` @@ -137,7 +138,7 @@ func GetSupportedMicroversions(client *gophercloud.ServiceClient) (SupportedMicr var minVersion, maxVersion string var supportedMicroversions SupportedMicroversions var resp response - _, err := client.Get(client.Endpoint, &resp, &gophercloud.RequestOpts{ + _, err := client.Get(ctx, client.Endpoint, &resp, &gophercloud.RequestOpts{ OkCodes: []int{200, 300}, }) @@ -178,8 +179,8 @@ func GetSupportedMicroversions(client *gophercloud.ServiceClient) (SupportedMicr // RequireMicroversion checks that the required microversion is supported and // returns a ServiceClient with the microversion set. -func RequireMicroversion(client gophercloud.ServiceClient, required string) (gophercloud.ServiceClient, error) { - supportedMicroversions, err := GetSupportedMicroversions(&client) +func RequireMicroversion(ctx context.Context, client gophercloud.ServiceClient, required string) (gophercloud.ServiceClient, error) { + supportedMicroversions, err := GetSupportedMicroversions(ctx, &client) if err != nil { return client, fmt.Errorf("unable to determine supported microversions: %w", err) } diff --git a/openstack/utils/testing/choose_version_test.go b/openstack/utils/testing/choose_version_test.go index 53e76a6523..36d8ac37ec 100644 --- a/openstack/utils/testing/choose_version_test.go +++ b/openstack/utils/testing/choose_version_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "strings" @@ -194,7 +195,7 @@ func TestChooseVersion(t *testing.T) { IdentityBase: testhelper.Endpoint(), IdentityEndpoint: "", } - v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3}) + v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3}) if err != nil { t.Fatalf("Unexpected error from ChooseVersion: %v", err) @@ -222,7 +223,7 @@ func TestChooseVersionOpinionatedLink(t *testing.T) { IdentityBase: testhelper.Endpoint(), IdentityEndpoint: testhelper.Endpoint() + "v2.0/", } - v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3}) + v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3}) if err != nil { t.Fatalf("Unexpected error from ChooseVersion: %v", err) } @@ -248,7 +249,7 @@ func TestChooseVersionFromSuffix(t *testing.T) { IdentityBase: testhelper.Endpoint(), IdentityEndpoint: testhelper.Endpoint() + "v2.0/", } - v, endpoint, err := utils.ChooseVersion(c, []*utils.Version{v2, v3}) + v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3}) if err != nil { t.Fatalf("Unexpected error from ChooseVersion: %v", err) } @@ -315,7 +316,7 @@ func TestGetSupportedVersions(t *testing.T) { Endpoint: test.Endpoint, } - supported, err := utils.GetSupportedMicroversions(client) + supported, err := utils.GetSupportedMicroversions(context.TODO(), client) if test.ExpectedErr { if err == nil { diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index 851c8c9b35..a6fc577aa4 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -1,6 +1,7 @@ package crontriggers import ( + "context" "encoding/json" "fmt" "net/url" @@ -59,29 +60,29 @@ func (opts CreateOpts) ToCronTriggerCreateMap() (map[string]interface{}, error) } // Create requests the creation of a new cron trigger. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToCronTriggerCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, nil) + resp, err := client.Post(ctx, createURL(client), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified cron trigger. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single cron trigger. // Use Extract to convert its result into an CronTrigger. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index b4f092823b..276031dd8b 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "net/url" @@ -57,7 +58,7 @@ func TestCreateCronTrigger(t *testing.T) { }, } - actual, err := crontriggers.Create(fake.ServiceClient(), opts).Extract() + actual, err := crontriggers.Create(context.TODO(), fake.ServiceClient(), opts).Extract() if err != nil { t.Fatalf("Unable to create cron trigger: %v", err) } @@ -98,7 +99,7 @@ func TestDeleteCronTrigger(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) - res := crontriggers.Delete(fake.ServiceClient(), "0520ffd8-f7f1-4f2e-845b-55d953a1cf46") + res := crontriggers.Delete(context.TODO(), fake.ServiceClient(), "0520ffd8-f7f1-4f2e-845b-55d953a1cf46") th.AssertNoErr(t, res.Err) } @@ -128,7 +129,7 @@ func TestGetCronTrigger(t *testing.T) { } `) }) - actual, err := crontriggers.Get(fake.ServiceClient(), "0520ffd8-f7f1-4f2e-845b-55d953a1cf46").Extract() + actual, err := crontriggers.Get(context.TODO(), fake.ServiceClient(), "0520ffd8-f7f1-4f2e-845b-55d953a1cf46").Extract() if err != nil { t.Fatalf("Unable to get cron trigger: %v", err) } @@ -199,7 +200,7 @@ func TestListCronTriggers(t *testing.T) { }) pages := 0 // Get all cron triggers - err := crontriggers.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := crontriggers.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := crontriggers.ExtractCronTriggers(page) if err != nil { diff --git a/openstack/workflow/v2/executions/requests.go b/openstack/workflow/v2/executions/requests.go index d9e3e7ac19..ff18f9cd3e 100644 --- a/openstack/workflow/v2/executions/requests.go +++ b/openstack/workflow/v2/executions/requests.go @@ -1,6 +1,7 @@ package executions import ( + "context" "encoding/json" "fmt" "net/url" @@ -49,29 +50,29 @@ func (opts CreateOpts) ToExecutionCreateMap() (map[string]interface{}, error) { } // Create requests the creation of a new execution. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToExecutionCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), b, &r.Body, nil) + resp, err := client.Post(ctx, createURL(client), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single execution. // Use ExtractExecution to convert its result into an Execution. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified execution. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index 841739d730..c51196ffb5 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "net/url" @@ -53,7 +54,7 @@ func TestCreateExecution(t *testing.T) { Description: "description", } - actual, err := executions.Create(fake.ServiceClient(), opts).Extract() + actual, err := executions.Create(context.TODO(), fake.ServiceClient(), opts).Extract() if err != nil { t.Fatalf("Unable to create execution: %v", err) } @@ -112,7 +113,7 @@ func TestGetExecution(t *testing.T) { `) }) - actual, err := executions.Get(fake.ServiceClient(), "50bb59f1-eb77-4017-a77f-6d575b002667").Extract() + actual, err := executions.Get(context.TODO(), fake.ServiceClient(), "50bb59f1-eb77-4017-a77f-6d575b002667").Extract() if err != nil { t.Fatalf("Unable to get execution: %v", err) } @@ -149,7 +150,7 @@ func TestDeleteExecution(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) }) - res := executions.Delete(fake.ServiceClient(), "1") + res := executions.Delete(context.TODO(), fake.ServiceClient(), "1") th.AssertNoErr(t, res.Err) } @@ -193,7 +194,7 @@ func TestListExecutions(t *testing.T) { }) pages := 0 // Get all executions - err := executions.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := executions.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := executions.ExtractExecutions(page) if err != nil { diff --git a/openstack/workflow/v2/workflows/requests.go b/openstack/workflow/v2/workflows/requests.go index 4849ba5fb0..279c8097ac 100644 --- a/openstack/workflow/v2/workflows/requests.go +++ b/openstack/workflow/v2/workflows/requests.go @@ -1,6 +1,7 @@ package workflows import ( + "context" "fmt" "io" "net/url" @@ -37,7 +38,7 @@ func (opts CreateOpts) ToWorkflowCreateParams() (io.Reader, string, error) { } // Create requests the creation of a new execution. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { url := createURL(client) var b io.Reader if opts != nil { @@ -50,7 +51,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create b = tmpB } - resp, err := client.Post(url, b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, url, b, &r.Body, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{ "Content-Type": "text/plain", "Accept": "", // Drop default JSON Accept header @@ -61,16 +62,16 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes the specified execution. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single execution. // Use Extract to convert its result into an Workflow. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index f6091c483e..1e516010ab 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "net/url" @@ -69,7 +70,7 @@ workflow_echo: Definition: strings.NewReader(definition), } - actual, err := workflows.Create(fake.ServiceClient(), opts).Extract() + actual, err := workflows.Create(context.TODO(), fake.ServiceClient(), opts).Extract() if err != nil { t.Fatalf("Unable to create workflow: %v", err) } @@ -106,7 +107,7 @@ func TestDeleteWorkflow(t *testing.T) { w.WriteHeader(http.StatusAccepted) }) - res := workflows.Delete(fake.ServiceClient(), "604a3a1e-94e3-4066-a34a-aa56873ef236") + res := workflows.Delete(context.TODO(), fake.ServiceClient(), "604a3a1e-94e3-4066-a34a-aa56873ef236") th.AssertNoErr(t, res.Err) } @@ -132,7 +133,7 @@ func TestGetWorkflow(t *testing.T) { } `) }) - actual, err := workflows.Get(fake.ServiceClient(), "1").Extract() + actual, err := workflows.Get(context.TODO(), fake.ServiceClient(), "1").Extract() if err != nil { t.Fatalf("Unable to get workflow: %v", err) } @@ -191,7 +192,7 @@ func TestListWorkflows(t *testing.T) { }) pages := 0 // Get all workflows - err := workflows.List(fake.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := workflows.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { pages++ actual, err := workflows.ExtractWorkflows(page) if err != nil { diff --git a/pagination/http.go b/pagination/http.go index 7781df4528..0ec799e903 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -53,16 +53,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(ctx context.Context, client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { + return client.Get(ctx, url, nil, &gophercloud.RequestOpts{ MoreHeaders: headers, OkCodes: []int{200, 204, 300}, KeepResponseBody: true, }) } - -// 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 107279bb57..8048f9f74c 100644 --- a/pagination/pager.go +++ b/pagination/pager.go @@ -71,7 +71,7 @@ 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) + resp, err := Request(ctx, p.client, p.Headers, url) if err != nil { return nil, err } @@ -84,10 +84,10 @@ 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(ctx context.Context, handler func(context.Context, Page) (bool, error)) error { if p.Err != nil { return p.Err } @@ -133,16 +133,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(ctx context.Context) (Page, error) { if p.Err != nil { return nil, p.Err } @@ -175,7 +168,7 @@ func (p Pager) AllPagesWithContext(ctx context.Context) (Page, error) { // key is the map key for the page body if the body type is `map[string]interface{}`. var key string // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { + err = p.EachPage(ctx, func(_ context.Context, page Page) (bool, error) { b := page.GetBody().(map[string]interface{}) for k, v := range b { // If it's a linked page, we don't want the `links`, we want the other one. @@ -198,7 +191,7 @@ func (p Pager) AllPagesWithContext(ctx context.Context) (Page, error) { body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice)) case []byte: // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { + err = p.EachPage(ctx, func(_ context.Context, page Page) (bool, error) { b := page.GetBody().([]byte) pagesSlice = append(pagesSlice, b) // seperate pages with a comma @@ -222,7 +215,7 @@ func (p Pager) AllPagesWithContext(ctx context.Context) (Page, error) { body.SetBytes(b) case []interface{}: // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { + err = p.EachPage(ctx, func(_ context.Context, page Page) (bool, error) { b := page.GetBody().([]interface{}) pagesSlice = append(pagesSlice, b...) return true, nil @@ -261,8 +254,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()) -} diff --git a/pagination/testing/linked_test.go b/pagination/testing/linked_test.go index 93e5278b47..f73c3e14e2 100644 --- a/pagination/testing/linked_test.go +++ b/pagination/testing/linked_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "reflect" @@ -61,7 +62,7 @@ func TestEnumerateLinked(t *testing.T) { defer testhelper.TeardownHTTP() callCount := 0 - err := pager.EachPage(func(page pagination.Page) (bool, error) { + err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := ExtractLinkedInts(page) if err != nil { return false, err @@ -102,7 +103,7 @@ func TestAllPagesLinked(t *testing.T) { pager := createLinked(t) defer testhelper.TeardownHTTP() - page, err := pager.AllPages() + page, err := pager.AllPages(context.TODO()) testhelper.AssertNoErr(t, err) expected := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} diff --git a/pagination/testing/marker_test.go b/pagination/testing/marker_test.go index 2ca5035ecc..a8a01fe9ca 100644 --- a/pagination/testing/marker_test.go +++ b/pagination/testing/marker_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "strings" @@ -83,7 +84,7 @@ func TestEnumerateMarker(t *testing.T) { defer testhelper.TeardownHTTP() callCount := 0 - err := pager.EachPage(func(page pagination.Page) (bool, error) { + err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actual, err := ExtractMarkerStrings(page) if err != nil { return false, err @@ -117,7 +118,7 @@ func TestAllPagesMarker(t *testing.T) { pager := createMarkerPaged(t) defer testhelper.TeardownHTTP() - page, err := pager.AllPages() + page, err := pager.AllPages(context.TODO()) testhelper.AssertNoErr(t, err) expected := []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii"} diff --git a/pagination/testing/single_test.go b/pagination/testing/single_test.go index 094ed85270..21daedff0a 100644 --- a/pagination/testing/single_test.go +++ b/pagination/testing/single_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -52,7 +53,7 @@ func TestEnumerateSinglePaged(t *testing.T) { pager := setupSinglePaged() defer testhelper.TeardownHTTP() - err := pager.EachPage(func(page pagination.Page) (bool, error) { + err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { callCount++ expected := []int{1, 2, 3} @@ -69,7 +70,7 @@ func TestAllPagesSingle(t *testing.T) { pager := setupSinglePaged() defer testhelper.TeardownHTTP() - page, err := pager.AllPages() + page, err := pager.AllPages(context.TODO()) testhelper.AssertNoErr(t, err) expected := []int{1, 2, 3} diff --git a/provider_client.go b/provider_client.go index f3bd65b309..b6d6eda379 100644 --- a/provider_client.go +++ b/provider_client.go @@ -355,19 +355,14 @@ type requestState struct { var applicationJSON = "application/json" -// RequestWithContext performs an HTTP request using the ProviderClient's +// Request 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) { +func (client *ProviderClient) Request(ctx context.Context, method, url string, options *RequestOpts) (*http.Response, error) { return client.doRequest(ctx, method, url, options, &requestState{ hasReauthenticated: false, }) } -// 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 diff --git a/service_client.go b/service_client.go index b8e6fd1a38..61834cc613 100644 --- a/service_client.go +++ b/service_client.go @@ -60,88 +60,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) { +// Get calls `Request` with the "GET" HTTP verb. +func (client *ServiceClient) Get(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) + return client.Request(ctx, "GET", url, opts) } -// 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) { +// Post calls `Request` with the "POST" HTTP verb. +func (client *ServiceClient) Post(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) -} - -// 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) + return client.Request(ctx, "POST", url, 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) { +// Put calls `Request` with the "PUT" HTTP verb. +func (client *ServiceClient) Put(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(ctx, "PUT", url, opts) } -// 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) { +// Patch calls `Request` with the "PATCH" HTTP verb. +func (client *ServiceClient) Patch(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) -} - -// 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) + return client.Request(ctx, "PATCH", url, opts) } -// DeleteWithContext calls `Request` with the "DELETE" HTTP verb. -func (client *ServiceClient) DeleteWithContext(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { +// Delete calls `Request` with the "DELETE" HTTP verb. +func (client *ServiceClient) Delete(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, nil, opts) - return client.RequestWithContext(ctx, "DELETE", url, opts) -} - -// 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) + return client.Request(ctx, "DELETE", url, opts) } -// HeadWithContext calls `Request` with the "HEAD" HTTP verb. -func (client *ServiceClient) HeadWithContext(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { +// Head calls `Request` with the "HEAD" HTTP verb. +func (client *ServiceClient) Head(ctx context.Context, url string, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } client.initReqOpts(nil, nil, opts) - return client.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) + return client.Request(ctx, "HEAD", url, opts) } func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { @@ -164,7 +134,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(ctx context.Context, method, url string, options *RequestOpts) (*http.Response, error) { if options.MoreHeaders == nil { options.MoreHeaders = make(map[string]string) } @@ -182,12 +152,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(ctx, method, url, options) } // ParseResponse is a helper function to parse http.Response to constituents. diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index b502dc36b6..337520bdab 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -112,7 +112,7 @@ func TestConcurrentReauth(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - resp, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) + resp, err := p.Request(context.TODO(), "GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) th.CheckNoErr(t, err) if resp == nil { t.Errorf("got a nil response") @@ -190,7 +190,7 @@ func TestReauthEndLoop(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - _, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) + _, err := p.Request(context.TODO(), "GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) mut.Lock() defer mut.Unlock() @@ -293,7 +293,7 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { if i != 0 { <-info.reauthCh } - resp, err := p.Request("GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) + resp, err := p.Request(context.TODO(), "GET", fmt.Sprintf("%s/route", th.Endpoint()), reqopts) th.CheckNoErr(t, err) if resp == nil { t.Errorf("got a nil response") @@ -363,7 +363,7 @@ func TestRequestReauthsAtMostOnce(t *testing.T) { // the part before the colon), but when encountering another 401 response, we // did not attempt reauthentication again and just passed that 401 response to // the caller as ErrDefault401. - _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) expectedErrorMessage := "Successfully re-authenticated, but got error executing request: Authentication failed" th.AssertEquals(t, expectedErrorMessage, err.Error()) } @@ -377,7 +377,7 @@ func TestRequestWithContext(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) p := &gophercloud.ProviderClient{Context: ctx} - res, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) + res, err := p.Request(context.TODO(), "GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) th.AssertNoErr(t, err) _, err = io.ReadAll(res.Body) th.AssertNoErr(t, err) @@ -385,7 +385,7 @@ func TestRequestWithContext(t *testing.T) { th.AssertNoErr(t, err) cancel() - res, err = p.Request("GET", ts.URL, &gophercloud.RequestOpts{}) + res, err = p.Request(context.TODO(), "GET", ts.URL, &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } @@ -415,7 +415,7 @@ func TestRequestConnectionReuse(t *testing.T) { p := &gophercloud.ProviderClient{} for i := 0; i < iter; i++ { - _, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: false}) + _, err := p.Request(context.TODO(), "GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: false}) th.AssertNoErr(t, err) } @@ -443,7 +443,7 @@ func TestRequestConnectionClose(t *testing.T) { p := &gophercloud.ProviderClient{} for i := 0; i < iter; i++ { - _, err := p.Request("GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) + _, err := p.Request(context.TODO(), "GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) th.AssertNoErr(t, err) } @@ -509,7 +509,7 @@ func TestRequestRetry(t *testing.T) { http.Error(w, "retry later", http.StatusTooManyRequests) }) - _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } @@ -536,7 +536,7 @@ func TestRequestRetryHTTPDate(t *testing.T) { http.Error(w, "retry later", http.StatusTooManyRequests) }) - _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } @@ -563,7 +563,7 @@ func TestRequestRetryError(t *testing.T) { http.Error(w, "retry later", http.StatusTooManyRequests) }) - _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } @@ -588,7 +588,7 @@ func TestRequestRetrySuccess(t *testing.T) { http.Error(w, "retry later", http.StatusOK) }) - _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) if err != nil { t.Fatal(err) } @@ -624,7 +624,7 @@ func TestRequestRetryContext(t *testing.T) { http.Error(w, "retry later", http.StatusTooManyRequests) }) - _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } @@ -656,7 +656,7 @@ func TestRequestGeneralRetry(t *testing.T) { } }) - _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) if err != nil { t.Fatal("expecting nil, got err") } @@ -684,7 +684,7 @@ func TestRequestGeneralRetryAbort(t *testing.T) { } }) - _, err := p.Request("GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting err, got nil") } @@ -700,7 +700,7 @@ func TestRequestWrongOkCode(t *testing.T) { p := &gophercloud.ProviderClient{} - _, err := p.Request("DELETE", ts.URL, &gophercloud.RequestOpts{}) + _, err := p.Request(context.TODO(), "DELETE", ts.URL, &gophercloud.RequestOpts{}) th.AssertErr(t, err) if urErr, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { // DELETE expects a 202 or 204 by default diff --git a/testing/service_client_test.go b/testing/service_client_test.go index 63524e92d5..6253b698d2 100644 --- a/testing/service_client_test.go +++ b/testing/service_client_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "fmt" "net/http" "testing" @@ -28,7 +29,7 @@ func TestMoreHeaders(t *testing.T) { "custom": "header", } c.ProviderClient = new(gophercloud.ProviderClient) - resp, err := c.Get(fmt.Sprintf("%s/route", th.Endpoint()), nil, nil) + resp, err := c.Get(context.TODO(), fmt.Sprintf("%s/route", th.Endpoint()), nil, nil) th.AssertNoErr(t, err) th.AssertEquals(t, resp.Request.Header.Get("custom"), "header") } From 99a726e6c1638367509ddd1aa27f157a00d1d167 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:59:26 +0000 Subject: [PATCH 1760/2296] build(deps): bump kiegroup/git-backporting from 4.5.0 to 4.5.1 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.5.0 to 4.5.1. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/204ebd4376d7501696d52aec8fea2a2f6e09328f...bce5dd4f9969d47da9cbb6ed06abbf87216afc98) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- .github/workflows/backport_v2.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 17b8401495..9030ab0e92 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -36,7 +36,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@204ebd4376d7501696d52aec8fea2a2f6e09328f + uses: kiegroup/git-backporting@bce5dd4f9969d47da9cbb6ed06abbf87216afc98 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} diff --git a/.github/workflows/backport_v2.yaml b/.github/workflows/backport_v2.yaml index 43b079c990..864ab8694e 100644 --- a/.github/workflows/backport_v2.yaml +++ b/.github/workflows/backport_v2.yaml @@ -31,7 +31,7 @@ jobs: private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} - name: Backporting - uses: kiegroup/git-backporting@204ebd4376d7501696d52aec8fea2a2f6e09328f + uses: kiegroup/git-backporting@bce5dd4f9969d47da9cbb6ed06abbf87216afc98 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From 233122614f8997f78b505b9c75882b1e8e4b1eb5 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 26 Feb 2024 17:54:45 +0100 Subject: [PATCH 1761/2296] CHANGELOG: Add context --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0e59fddb8..37692596bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ BREAKING CHANGES: * [GH-2560](https://github.com/gophercloud/gophercloud/pull/2560) loadbalancer: Use CreateMemberOpts instead of BatchUpdateMemberOpts in PoolCreateOpts * [GH-2886](https://github.com/gophercloud/gophercloud/pull/2886) ports: Fix value_specs implementation * [GH-2665](https://github.com/gophercloud/gophercloud/pull/2665) Cinder: Remove multiatttach request parameter +* [GH-2936](https://github.com/gophercloud/gophercloud/pull/2936) Make Gophercloud context-aware: all function signatures triggering an HTTP call now accept a context.Context for tracing and cancellation New features and improvements: From 8602cd436bffb3ac95a79c6fded86c59ff638408 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 18:11:01 +0000 Subject: [PATCH 1762/2296] 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 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 12b70aae68..492dc09498 100644 --- a/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/openstack/compute/v2/extensions/volumeattach/requests.go @@ -58,16 +58,16 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, serverID str } // Get returns public data about a previously created VolumeAttachment. -func Get(ctx context.Context, client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, serverID, attachmentID), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, serverID, volumeID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, serverID, volumeID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous stored VolumeAttachment from // the server. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, serverID, attachmentID string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, serverID, attachmentID), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, serverID, volumeID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, serverID, volumeID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } From d9064a9e0a737e382e2a68be34fb56d9d93158fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 09:37:53 +0000 Subject: [PATCH 1763/2296] build(deps): bump golang.org/x/crypto from 0.19.0 to 0.20.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.19.0 to 0.20.0. - [Commits](https://github.com/golang/crypto/compare/v0.19.0...v0.20.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 998e430b0c..eade2a66d8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gophercloud/gophercloud/v2 go 1.21.6 require ( - golang.org/x/crypto v0.19.0 + golang.org/x/crypto v0.20.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 6d66f158ca..b1239ddefe 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= From ddb64b9fb0693d70c4dfc7a05bc9b3a8622d1eb2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 22 Feb 2024 18:13:48 +0000 Subject: [PATCH 1764/2296] Rename imageservice -> image To align with other services. Signed-off-by: Stephen Finucane --- ...l-imageservice.yaml => functional-image.yaml} | 12 ++++++------ internal/acceptance/clients/clients.go | 6 +++--- .../{imageservice => image}/v2/imagedata_test.go | 2 +- .../v2/imageimport_test.go | 8 ++++---- .../{imageservice => image}/v2/images_test.go | 16 ++++++++-------- .../{imageservice => image}/v2/imageservice.go | 16 ++++++++-------- .../{imageservice => image}/v2/tasks_test.go | 14 +++++++------- openstack/client.go | 6 +++--- openstack/{imageservice => image}/v2/README.md | 0 .../{imageservice => image}/v2/imagedata/doc.go | 0 .../v2/imagedata/requests.go | 2 +- .../v2/imagedata/results.go | 0 .../v2/imagedata/testing/doc.go | 0 .../v2/imagedata/testing/fixtures_test.go | 0 .../v2/imagedata/testing/requests_test.go | 2 +- .../{imageservice => image}/v2/imagedata/urls.go | 0 .../v2/imageimport/doc.go | 2 +- .../v2/imageimport/requests.go | 0 .../v2/imageimport/results.go | 0 .../v2/imageimport/testing/doc.go | 0 .../v2/imageimport/testing/fixtures_test.go | 0 .../v2/imageimport/testing/requests_test.go | 2 +- .../v2/imageimport/urls.go | 0 .../{imageservice => image}/v2/images/doc.go | 0 .../v2/images/requests.go | 0 .../{imageservice => image}/v2/images/results.go | 2 +- .../v2/images/testing/doc.go | 0 .../v2/images/testing/fixtures_test.go | 0 .../v2/images/testing/requests_test.go | 2 +- .../{imageservice => image}/v2/images/types.go | 0 .../{imageservice => image}/v2/images/urls.go | 0 .../{imageservice => image}/v2/members/doc.go | 0 .../v2/members/requests.go | 0 .../v2/members/results.go | 0 .../v2/members/testing/doc.go | 0 .../v2/members/testing/fixtures_test.go | 0 .../v2/members/testing/requests_test.go | 2 +- .../{imageservice => image}/v2/members/urls.go | 0 .../{imageservice => image}/v2/tasks/doc.go | 2 +- .../{imageservice => image}/v2/tasks/requests.go | 8 ++++---- .../{imageservice => image}/v2/tasks/results.go | 0 .../v2/tasks/testing/doc.go | 0 .../v2/tasks/testing/fixtures_test.go | 2 +- .../v2/tasks/testing/requests_test.go | 2 +- .../{imageservice => image}/v2/tasks/urls.go | 0 45 files changed, 54 insertions(+), 54 deletions(-) rename .github/workflows/{functional-imageservice.yaml => functional-image.yaml} (89%) rename internal/acceptance/openstack/{imageservice => image}/v2/imagedata_test.go (93%) rename internal/acceptance/openstack/{imageservice => image}/v2/imageimport_test.go (76%) rename internal/acceptance/openstack/{imageservice => image}/v2/images_test.go (91%) rename internal/acceptance/openstack/{imageservice => image}/v2/imageservice.go (89%) rename internal/acceptance/openstack/{imageservice => image}/v2/tasks_test.go (76%) rename openstack/{imageservice => image}/v2/README.md (100%) rename openstack/{imageservice => image}/v2/imagedata/doc.go (100%) rename openstack/{imageservice => image}/v2/imagedata/requests.go (98%) rename openstack/{imageservice => image}/v2/imagedata/results.go (100%) rename openstack/{imageservice => image}/v2/imagedata/testing/doc.go (100%) rename openstack/{imageservice => image}/v2/imagedata/testing/fixtures_test.go (100%) rename openstack/{imageservice => image}/v2/imagedata/testing/requests_test.go (96%) rename openstack/{imageservice => image}/v2/imagedata/urls.go (100%) rename openstack/{imageservice => image}/v2/imageimport/doc.go (94%) rename openstack/{imageservice => image}/v2/imageimport/requests.go (100%) rename openstack/{imageservice => image}/v2/imageimport/results.go (100%) rename openstack/{imageservice => image}/v2/imageimport/testing/doc.go (100%) rename openstack/{imageservice => image}/v2/imageimport/testing/fixtures_test.go (100%) rename openstack/{imageservice => image}/v2/imageimport/testing/requests_test.go (95%) rename openstack/{imageservice => image}/v2/imageimport/urls.go (100%) rename openstack/{imageservice => image}/v2/images/doc.go (100%) rename openstack/{imageservice => image}/v2/images/requests.go (100%) rename openstack/{imageservice => image}/v2/images/results.go (99%) rename openstack/{imageservice => image}/v2/images/testing/doc.go (100%) rename openstack/{imageservice => image}/v2/images/testing/fixtures_test.go (100%) rename openstack/{imageservice => image}/v2/images/testing/requests_test.go (99%) rename openstack/{imageservice => image}/v2/images/types.go (100%) rename openstack/{imageservice => image}/v2/images/urls.go (100%) rename openstack/{imageservice => image}/v2/members/doc.go (100%) rename openstack/{imageservice => image}/v2/members/requests.go (100%) rename openstack/{imageservice => image}/v2/members/results.go (100%) rename openstack/{imageservice => image}/v2/members/testing/doc.go (100%) rename openstack/{imageservice => image}/v2/members/testing/fixtures_test.go (100%) rename openstack/{imageservice => image}/v2/members/testing/requests_test.go (98%) rename openstack/{imageservice => image}/v2/members/urls.go (100%) rename openstack/{imageservice => image}/v2/tasks/doc.go (98%) rename openstack/{imageservice => image}/v2/tasks/requests.go (93%) rename openstack/{imageservice => image}/v2/tasks/results.go (100%) rename openstack/{imageservice => image}/v2/tasks/testing/doc.go (100%) rename openstack/{imageservice => image}/v2/tasks/testing/fixtures_test.go (98%) rename openstack/{imageservice => image}/v2/tasks/testing/requests_test.go (98%) rename openstack/{imageservice => image}/v2/tasks/urls.go (100%) diff --git a/.github/workflows/functional-imageservice.yaml b/.github/workflows/functional-image.yaml similarity index 89% rename from .github/workflows/functional-imageservice.yaml rename to .github/workflows/functional-image.yaml index 252e4b571c..d842f10591 100644 --- a/.github/workflows/functional-imageservice.yaml +++ b/.github/workflows/functional-image.yaml @@ -1,12 +1,12 @@ -name: functional-imageservice +name: functional-image on: pull_request: paths: - - '**imageservice**' + - '**image**' schedule: - cron: '0 0 */3 * *' jobs: - functional-imageservice: + functional-image: strategy: fail-fast: false matrix: @@ -36,7 +36,7 @@ jobs: openstack_version: "stable/victoria" ubuntu_version: "20.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Glance and run imageservice acceptance tests + name: Deploy OpenStack ${{ matrix.name }} with Glance and run image acceptance tests steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -52,7 +52,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*imageservice.*$" + ACCEPTANCE_TESTS_FILTER: "^.*image.*$" OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs @@ -61,5 +61,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-imageservice-${{ matrix.name }} + name: functional-image-${{ matrix.name }} path: /tmp/devstack-logs/* diff --git a/internal/acceptance/clients/clients.go b/internal/acceptance/clients/clients.go index 05f45fe56d..3de05132fd 100644 --- a/internal/acceptance/clients/clients.go +++ b/internal/acceptance/clients/clients.go @@ -463,10 +463,10 @@ func NewIdentityV3UnauthenticatedClient() (*gophercloud.ServiceClient, error) { return openstack.NewIdentityV3(client, gophercloud.EndpointOpts{}) } -// NewImageServiceV2Client returns a *ServiceClient for making calls to the +// NewImageV2Client returns a *ServiceClient for making calls to the // OpenStack Image v2 API. An error will be returned if authentication or // client creation was not possible. -func NewImageServiceV2Client() (*gophercloud.ServiceClient, error) { +func NewImageV2Client() (*gophercloud.ServiceClient, error) { ao, err := openstack.AuthOptionsFromEnv() if err != nil { return nil, err @@ -479,7 +479,7 @@ func NewImageServiceV2Client() (*gophercloud.ServiceClient, error) { client = configureDebug(client) - return openstack.NewImageServiceV2(client, gophercloud.EndpointOpts{ + return openstack.NewImageV2(client, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) } diff --git a/internal/acceptance/openstack/imageservice/v2/imagedata_test.go b/internal/acceptance/openstack/image/v2/imagedata_test.go similarity index 93% rename from internal/acceptance/openstack/imageservice/v2/imagedata_test.go rename to internal/acceptance/openstack/image/v2/imagedata_test.go index e6d43a1259..4ea210b945 100644 --- a/internal/acceptance/openstack/imageservice/v2/imagedata_test.go +++ b/internal/acceptance/openstack/image/v2/imagedata_test.go @@ -9,7 +9,7 @@ import ( ) func TestImageStage(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) diff --git a/internal/acceptance/openstack/imageservice/v2/imageimport_test.go b/internal/acceptance/openstack/image/v2/imageimport_test.go similarity index 76% rename from internal/acceptance/openstack/imageservice/v2/imageimport_test.go rename to internal/acceptance/openstack/image/v2/imageimport_test.go index a2b26f6832..65cef05da3 100644 --- a/internal/acceptance/openstack/imageservice/v2/imageimport_test.go +++ b/internal/acceptance/openstack/image/v2/imageimport_test.go @@ -1,5 +1,5 @@ -//go:build acceptance || imageservice || imageimport -// +build acceptance imageservice imageimport +//go:build acceptance || image || imageimport +// +build acceptance image imageimport package v2 @@ -12,7 +12,7 @@ import ( ) func TestGetImportInfo(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) importInfo, err := GetImportInfo(t, client) @@ -22,7 +22,7 @@ func TestGetImportInfo(t *testing.T) { } func TestCreateImport(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) diff --git a/internal/acceptance/openstack/imageservice/v2/images_test.go b/internal/acceptance/openstack/image/v2/images_test.go similarity index 91% rename from internal/acceptance/openstack/imageservice/v2/images_test.go rename to internal/acceptance/openstack/image/v2/images_test.go index 1013d66d3d..2ebe82b695 100644 --- a/internal/acceptance/openstack/imageservice/v2/images_test.go +++ b/internal/acceptance/openstack/image/v2/images_test.go @@ -1,5 +1,5 @@ -//go:build acceptance || imageservice || images -// +build acceptance imageservice images +//go:build acceptance || image || images +// +build acceptance image images package v2 @@ -11,13 +11,13 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/images" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/images" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestImagesListEachPage(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) listOpts := images.ListOpts{ @@ -41,7 +41,7 @@ func TestImagesListEachPage(t *testing.T) { } func TestImagesListAllPages(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) @@ -70,7 +70,7 @@ func TestImagesListAllPages(t *testing.T) { } func TestImagesListByDate(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) date := time.Date(2014, 1, 1, 1, 1, 1, 0, time.UTC) @@ -118,7 +118,7 @@ func TestImagesListByDate(t *testing.T) { } func TestImagesFilter(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) @@ -143,7 +143,7 @@ func TestImagesFilter(t *testing.T) { } func TestImagesUpdate(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) image, err := CreateEmptyImage(t, client) diff --git a/internal/acceptance/openstack/imageservice/v2/imageservice.go b/internal/acceptance/openstack/image/v2/imageservice.go similarity index 89% rename from internal/acceptance/openstack/imageservice/v2/imageservice.go rename to internal/acceptance/openstack/image/v2/imageservice.go index b44526e26e..baccc68ae3 100644 --- a/internal/acceptance/openstack/imageservice/v2/imageservice.go +++ b/internal/acceptance/openstack/image/v2/imageservice.go @@ -1,4 +1,4 @@ -// Package v2 contains common functions for creating imageservice resources +// Package v2 contains common functions for creating image resources // for use in acceptance tests. See the `*_test.go` files for example usages. package v2 @@ -11,10 +11,10 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/imagedata" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/imageimport" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/images" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/images" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/tasks" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -77,7 +77,7 @@ const ImportImageURL = "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_ // CreateTask will create a task to import the CirrOS image. // An error will be returned if a task couldn't be created. func CreateTask(t *testing.T, client *gophercloud.ServiceClient, imageURL string) (*tasks.Task, error) { - t.Logf("Attempting to create an Imageservice import task with image: %s", imageURL) + t.Logf("Attempting to create an Image service import task with image: %s", imageURL) opts := tasks.CreateOpts{ Type: "import", Input: map[string]interface{}{ @@ -104,7 +104,7 @@ func CreateTask(t *testing.T, client *gophercloud.ServiceClient, imageURL string // GetImportInfo will retrieve Import API information. func GetImportInfo(t *testing.T, client *gophercloud.ServiceClient) (*imageimport.ImportInfo, error) { - t.Log("Attempting to get the Imageservice Import API information") + t.Log("Attempting to get the Image service Import API information") importInfo, err := imageimport.Get(context.TODO(), client).Extract() if err != nil { return nil, err @@ -159,7 +159,7 @@ func DeleteImageFile(t *testing.T, filepath string) { t.Logf("Successfully deleted image file %s", filepath) } -// ImportImage will import image data from the remote source to the Imageservice. +// ImportImage will import image data from the remote source to the Image service. func ImportImage(t *testing.T, client *gophercloud.ServiceClient, imageID string) error { importOpts := imageimport.CreateOpts{ Name: imageimport.WebDownloadMethod, diff --git a/internal/acceptance/openstack/imageservice/v2/tasks_test.go b/internal/acceptance/openstack/image/v2/tasks_test.go similarity index 76% rename from internal/acceptance/openstack/imageservice/v2/tasks_test.go rename to internal/acceptance/openstack/image/v2/tasks_test.go index 1a722f19bc..fd2f2664e2 100644 --- a/internal/acceptance/openstack/imageservice/v2/tasks_test.go +++ b/internal/acceptance/openstack/image/v2/tasks_test.go @@ -1,5 +1,5 @@ -//go:build acceptance || imageservice || tasks -// +build acceptance imageservice tasks +//go:build acceptance || image || tasks +// +build acceptance image tasks package v2 @@ -9,13 +9,13 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/tasks" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestTasksListEachPage(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) listOpts := tasks.ListOpts{ @@ -36,7 +36,7 @@ func TestTasksListEachPage(t *testing.T) { } func TestTasksListAllPages(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) listOpts := tasks.ListOpts{} @@ -53,12 +53,12 @@ func TestTasksListAllPages(t *testing.T) { } func TestTaskCreate(t *testing.T) { - client, err := clients.NewImageServiceV2Client() + client, err := clients.NewImageV2Client() th.AssertNoErr(t, err) task, err := CreateTask(t, client, ImportImageURL) if err != nil { - t.Fatalf("Unable to create an Imageservice task: %v", err) + t.Fatalf("Unable to create an Image service task: %v", err) } tools.PrintResource(t, task) diff --git a/openstack/client.go b/openstack/client.go index ca3007be07..4fe07da4c4 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -434,9 +434,9 @@ func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) ( return sc, err } -// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 -// image service. -func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { +// NewImageV2 creates a ServiceClient that may be used to access the v2 image +// service. +func NewImageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { sc, err := initClientOpts(client, eo, "image") sc.ResourceBase = sc.Endpoint + "v2/" return sc, err diff --git a/openstack/imageservice/v2/README.md b/openstack/image/v2/README.md similarity index 100% rename from openstack/imageservice/v2/README.md rename to openstack/image/v2/README.md diff --git a/openstack/imageservice/v2/imagedata/doc.go b/openstack/image/v2/imagedata/doc.go similarity index 100% rename from openstack/imageservice/v2/imagedata/doc.go rename to openstack/image/v2/imagedata/doc.go diff --git a/openstack/imageservice/v2/imagedata/requests.go b/openstack/image/v2/imagedata/requests.go similarity index 98% rename from openstack/imageservice/v2/imagedata/requests.go rename to openstack/image/v2/imagedata/requests.go index 1e56bc2264..fd0f3e7ffd 100644 --- a/openstack/imageservice/v2/imagedata/requests.go +++ b/openstack/image/v2/imagedata/requests.go @@ -17,7 +17,7 @@ func Upload(ctx context.Context, client *gophercloud.ServiceClient, id string, d return } -// Stage performs PUT call on the existing image object in the Imageservice with +// Stage performs PUT call on the existing image object in the Image service with // the provided file. // Existing image object must be in the "queued" status. func Stage(ctx context.Context, client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { diff --git a/openstack/imageservice/v2/imagedata/results.go b/openstack/image/v2/imagedata/results.go similarity index 100% rename from openstack/imageservice/v2/imagedata/results.go rename to openstack/image/v2/imagedata/results.go diff --git a/openstack/imageservice/v2/imagedata/testing/doc.go b/openstack/image/v2/imagedata/testing/doc.go similarity index 100% rename from openstack/imageservice/v2/imagedata/testing/doc.go rename to openstack/image/v2/imagedata/testing/doc.go diff --git a/openstack/imageservice/v2/imagedata/testing/fixtures_test.go b/openstack/image/v2/imagedata/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/imagedata/testing/fixtures_test.go rename to openstack/image/v2/imagedata/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/imagedata/testing/requests_test.go b/openstack/image/v2/imagedata/testing/requests_test.go similarity index 96% rename from openstack/imageservice/v2/imagedata/testing/requests_test.go rename to openstack/image/v2/imagedata/testing/requests_test.go index a36d0a929c..8e1461e1c7 100644 --- a/openstack/imageservice/v2/imagedata/testing/requests_test.go +++ b/openstack/image/v2/imagedata/testing/requests_test.go @@ -6,7 +6,7 @@ import ( "io" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/imagedata" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/imagedata" th "github.com/gophercloud/gophercloud/v2/testhelper" fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/imageservice/v2/imagedata/urls.go b/openstack/image/v2/imagedata/urls.go similarity index 100% rename from openstack/imageservice/v2/imagedata/urls.go rename to openstack/image/v2/imagedata/urls.go diff --git a/openstack/imageservice/v2/imageimport/doc.go b/openstack/image/v2/imageimport/doc.go similarity index 94% rename from openstack/imageservice/v2/imageimport/doc.go rename to openstack/image/v2/imageimport/doc.go index 775a3630b7..dc163ab3b3 100644 --- a/openstack/imageservice/v2/imageimport/doc.go +++ b/openstack/image/v2/imageimport/doc.go @@ -1,6 +1,6 @@ /* Package imageimport enables management of images import and retrieval of the -Imageservice Import API information. +Image service Import API information. Example to Get an information about the Import API diff --git a/openstack/imageservice/v2/imageimport/requests.go b/openstack/image/v2/imageimport/requests.go similarity index 100% rename from openstack/imageservice/v2/imageimport/requests.go rename to openstack/image/v2/imageimport/requests.go diff --git a/openstack/imageservice/v2/imageimport/results.go b/openstack/image/v2/imageimport/results.go similarity index 100% rename from openstack/imageservice/v2/imageimport/results.go rename to openstack/image/v2/imageimport/results.go diff --git a/openstack/imageservice/v2/imageimport/testing/doc.go b/openstack/image/v2/imageimport/testing/doc.go similarity index 100% rename from openstack/imageservice/v2/imageimport/testing/doc.go rename to openstack/image/v2/imageimport/testing/doc.go diff --git a/openstack/imageservice/v2/imageimport/testing/fixtures_test.go b/openstack/image/v2/imageimport/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/imageimport/testing/fixtures_test.go rename to openstack/image/v2/imageimport/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/imageimport/testing/requests_test.go b/openstack/image/v2/imageimport/testing/requests_test.go similarity index 95% rename from openstack/imageservice/v2/imageimport/testing/requests_test.go rename to openstack/image/v2/imageimport/testing/requests_test.go index 3de8cc8054..ae353ad0ac 100644 --- a/openstack/imageservice/v2/imageimport/testing/requests_test.go +++ b/openstack/image/v2/imageimport/testing/requests_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/imageimport" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/imageimport" th "github.com/gophercloud/gophercloud/v2/testhelper" fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/imageservice/v2/imageimport/urls.go b/openstack/image/v2/imageimport/urls.go similarity index 100% rename from openstack/imageservice/v2/imageimport/urls.go rename to openstack/image/v2/imageimport/urls.go diff --git a/openstack/imageservice/v2/images/doc.go b/openstack/image/v2/images/doc.go similarity index 100% rename from openstack/imageservice/v2/images/doc.go rename to openstack/image/v2/images/doc.go diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/image/v2/images/requests.go similarity index 100% rename from openstack/imageservice/v2/images/requests.go rename to openstack/image/v2/images/requests.go diff --git a/openstack/imageservice/v2/images/results.go b/openstack/image/v2/images/results.go similarity index 99% rename from openstack/imageservice/v2/images/results.go rename to openstack/image/v2/images/results.go index 96644a9aeb..e46d6f9842 100644 --- a/openstack/imageservice/v2/images/results.go +++ b/openstack/image/v2/images/results.go @@ -20,7 +20,7 @@ type Image struct { Name string `json:"name"` // Status is the image status. It can be "queued" or "active" - // See imageservice/v2/images/type.go + // See image/v2/images/type.go Status ImageStatus `json:"status"` // Tags is a list of image tags. Tags are arbitrarily defined strings diff --git a/openstack/imageservice/v2/images/testing/doc.go b/openstack/image/v2/images/testing/doc.go similarity index 100% rename from openstack/imageservice/v2/images/testing/doc.go rename to openstack/image/v2/images/testing/doc.go diff --git a/openstack/imageservice/v2/images/testing/fixtures_test.go b/openstack/image/v2/images/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/images/testing/fixtures_test.go rename to openstack/image/v2/images/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/image/v2/images/testing/requests_test.go similarity index 99% rename from openstack/imageservice/v2/images/testing/requests_test.go rename to openstack/image/v2/images/testing/requests_test.go index a4aaf0a9ef..2872e79b69 100644 --- a/openstack/imageservice/v2/images/testing/requests_test.go +++ b/openstack/image/v2/images/testing/requests_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/images" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/images" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/imageservice/v2/images/types.go b/openstack/image/v2/images/types.go similarity index 100% rename from openstack/imageservice/v2/images/types.go rename to openstack/image/v2/images/types.go diff --git a/openstack/imageservice/v2/images/urls.go b/openstack/image/v2/images/urls.go similarity index 100% rename from openstack/imageservice/v2/images/urls.go rename to openstack/image/v2/images/urls.go diff --git a/openstack/imageservice/v2/members/doc.go b/openstack/image/v2/members/doc.go similarity index 100% rename from openstack/imageservice/v2/members/doc.go rename to openstack/image/v2/members/doc.go diff --git a/openstack/imageservice/v2/members/requests.go b/openstack/image/v2/members/requests.go similarity index 100% rename from openstack/imageservice/v2/members/requests.go rename to openstack/image/v2/members/requests.go diff --git a/openstack/imageservice/v2/members/results.go b/openstack/image/v2/members/results.go similarity index 100% rename from openstack/imageservice/v2/members/results.go rename to openstack/image/v2/members/results.go diff --git a/openstack/imageservice/v2/members/testing/doc.go b/openstack/image/v2/members/testing/doc.go similarity index 100% rename from openstack/imageservice/v2/members/testing/doc.go rename to openstack/image/v2/members/testing/doc.go diff --git a/openstack/imageservice/v2/members/testing/fixtures_test.go b/openstack/image/v2/members/testing/fixtures_test.go similarity index 100% rename from openstack/imageservice/v2/members/testing/fixtures_test.go rename to openstack/image/v2/members/testing/fixtures_test.go diff --git a/openstack/imageservice/v2/members/testing/requests_test.go b/openstack/image/v2/members/testing/requests_test.go similarity index 98% rename from openstack/imageservice/v2/members/testing/requests_test.go rename to openstack/image/v2/members/testing/requests_test.go index 04717bfeea..981a22ffff 100644 --- a/openstack/imageservice/v2/members/testing/requests_test.go +++ b/openstack/image/v2/members/testing/requests_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/members" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/members" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/imageservice/v2/members/urls.go b/openstack/image/v2/members/urls.go similarity index 100% rename from openstack/imageservice/v2/members/urls.go rename to openstack/image/v2/members/urls.go diff --git a/openstack/imageservice/v2/tasks/doc.go b/openstack/image/v2/tasks/doc.go similarity index 98% rename from openstack/imageservice/v2/tasks/doc.go rename to openstack/image/v2/tasks/doc.go index 7904c6a832..689a1c7327 100644 --- a/openstack/imageservice/v2/tasks/doc.go +++ b/openstack/image/v2/tasks/doc.go @@ -1,6 +1,6 @@ /* Package tasks enables management and retrieval of tasks from the OpenStack -Imageservice. +Image service. Example to List Tasks diff --git a/openstack/imageservice/v2/tasks/requests.go b/openstack/image/v2/tasks/requests.go similarity index 93% rename from openstack/imageservice/v2/tasks/requests.go rename to openstack/image/v2/tasks/requests.go index fa6972b529..241e2ab7a7 100644 --- a/openstack/imageservice/v2/tasks/requests.go +++ b/openstack/image/v2/tasks/requests.go @@ -33,7 +33,7 @@ type ListOptsBuilder interface { } // ListOpts allows the filtering and sorting of paginated collections through -// the OpenStack Imageservice tasks API. +// the OpenStack Image service tasks API. type ListOpts struct { // Integer value for the limit of values to return. Limit int `q:"limit"` @@ -90,7 +90,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { }) } -// Get retrieves a specific Imageservice task based on its ID. +// Get retrieves a specific Image service task based on its ID. func Get(ctx context.Context, c *gophercloud.ServiceClient, taskID string) (r GetResult) { resp, err := c.Get(ctx, getURL(c, taskID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -102,7 +102,7 @@ type CreateOptsBuilder interface { ToTaskCreateMap() (map[string]interface{}, error) } -// CreateOpts specifies parameters of a new Imageservice task. +// CreateOpts specifies parameters of a new Image service task. type CreateOpts struct { Type string `json:"type" required:"true"` Input map[string]interface{} `json:"input"` @@ -117,7 +117,7 @@ func (opts CreateOpts) ToTaskCreateMap() (map[string]interface{}, error) { return b, nil } -// Create requests the creation of a new Imageservice task on the server. +// Create requests the creation of a new Image service task on the server. func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToTaskCreateMap() if err != nil { diff --git a/openstack/imageservice/v2/tasks/results.go b/openstack/image/v2/tasks/results.go similarity index 100% rename from openstack/imageservice/v2/tasks/results.go rename to openstack/image/v2/tasks/results.go diff --git a/openstack/imageservice/v2/tasks/testing/doc.go b/openstack/image/v2/tasks/testing/doc.go similarity index 100% rename from openstack/imageservice/v2/tasks/testing/doc.go rename to openstack/image/v2/tasks/testing/doc.go diff --git a/openstack/imageservice/v2/tasks/testing/fixtures_test.go b/openstack/image/v2/tasks/testing/fixtures_test.go similarity index 98% rename from openstack/imageservice/v2/tasks/testing/fixtures_test.go rename to openstack/image/v2/tasks/testing/fixtures_test.go index fc42383c92..de52a936c0 100644 --- a/openstack/imageservice/v2/tasks/testing/fixtures_test.go +++ b/openstack/image/v2/tasks/testing/fixtures_test.go @@ -3,7 +3,7 @@ package testing import ( "time" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/tasks" ) // TasksListResult represents raw server response from a server to a list call. diff --git a/openstack/imageservice/v2/tasks/testing/requests_test.go b/openstack/image/v2/tasks/testing/requests_test.go similarity index 98% rename from openstack/imageservice/v2/tasks/testing/requests_test.go rename to openstack/image/v2/tasks/testing/requests_test.go index e6c8781d61..a8cc870fab 100644 --- a/openstack/imageservice/v2/tasks/testing/requests_test.go +++ b/openstack/image/v2/tasks/testing/requests_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/imageservice/v2/tasks" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/tasks" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" fakeclient "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/imageservice/v2/tasks/urls.go b/openstack/image/v2/tasks/urls.go similarity index 100% rename from openstack/imageservice/v2/tasks/urls.go rename to openstack/image/v2/tasks/urls.go From 5d4051c9bbbfed84e80cd8e267bd65781401ef4b Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Mon, 4 Mar 2024 15:40:27 +0100 Subject: [PATCH 1765/2296] Drop CI coverage for Victoria-Yoga Yoga has entered, and the rest will soon enter, the unmaintained [1] phase. This means that the CI stability will be in the hands of a small group of volunteers and will no longer be the responsibility of the project teams. I don't believe the Gophercloud project can maintain CI jobs on these branches in the long run, hence removing them. [1] https://docs.openstack.org/project-team-guide/stable-branches.html#unmaintained --- .github/workflows/functional-baremetal.yaml | 12 ---------- .github/workflows/functional-basic.yaml | 12 ---------- .../workflows/functional-blockstorage.yaml | 12 ---------- .github/workflows/functional-clustering.yaml | 12 ---------- .github/workflows/functional-compute.yaml | 12 ---------- .../workflows/functional-containerinfra.yaml | 24 ------------------- .github/workflows/functional-dns.yaml | 12 ---------- .github/workflows/functional-identity.yaml | 12 ---------- .github/workflows/functional-image.yaml | 12 ---------- .github/workflows/functional-keymanager.yaml | 12 ---------- .../workflows/functional-loadbalancer.yaml | 12 ---------- .github/workflows/functional-messaging.yaml | 12 ---------- .github/workflows/functional-networking.yaml | 12 ---------- .../workflows/functional-objectstorage.yaml | 12 ---------- .../workflows/functional-orchestration.yaml | 12 ---------- .github/workflows/functional-placement.yaml | 12 ---------- .../functional-sharedfilesystems.yaml | 12 ---------- 17 files changed, 216 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 8753d091f2..068537d901 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 11048088fb..bf8b40457a 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -26,18 +26,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 1ee43e742c..c6526df766 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 3ed60f16cb..79b0fbc246 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 de667ed055..e1aa044663 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 17be7db52a..ee332836a8 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -35,30 +35,6 @@ jobs: 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: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 4e789e088c..a652daa508 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -24,18 +24,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 d0966abecb..895cbe3df3 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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-image.yaml b/.github/workflows/functional-image.yaml index d842f10591..2b96be230f 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Glance and run image acceptance tests steps: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index abcb66392a..f734d1cbf2 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 5a16b1fb4a..f619d2b873 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 84d155440b..ebf1a86857 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 db8fda07f3..ff3307b666 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 d4f3c8320b..c1c6d2037e 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 3424aa1374..b7d1908380 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 3cc2d74597..31894c6f9f 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" 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 c7a107864b..1be0b3ecda 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -23,18 +23,6 @@ jobs: - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" - - name: "yoga" - openstack_version: "stable/yoga" - ubuntu_version: "20.04" - - 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" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests steps: From 888aa7be4c474e58a9477e43a7ae631b29f0e5a7 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Mon, 4 Mar 2024 16:04:39 +0100 Subject: [PATCH 1766/2296] Work around broken dnsmasq in the Ironic plugin --- .github/workflows/functional-baremetal.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 068537d901..7cd5556835 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -36,6 +36,9 @@ jobs: run: sudo apt update && sudo apt -y upgrade shell: bash if: matrix.ubuntu_version == '20.04' + - name: Work around broken dnsmasq + run: sudo apt-get purge -y dnsmasq-base + if: matrix.ubuntu_version == '22.04' - name: Deploy devstack uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: From 00636216663881e903c1c858e928ddbc1d2687fb Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Mon, 4 Mar 2024 17:47:03 +0100 Subject: [PATCH 1767/2296] Use system scope when running baremetal tests Starting with 2024.1, some actions no longer work with a project scoped token. --- .github/workflows/functional-baremetal.yaml | 6 ++++++ script/stackenv | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 7cd5556835..c6efa0cab0 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -13,16 +13,20 @@ jobs: name: ["master"] openstack_version: ["master"] ubuntu_version: ["22.04"] + os_system_scope: ["all"] include: - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + os_system_scope: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + os_system_scope: "" - name: "zed" openstack_version: "stable/zed" ubuntu_version: "20.04" + os_system_scope: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests steps: @@ -88,6 +92,8 @@ jobs: DEVSTACK_PATH: ${{ github.workspace }}/devstack ACCEPTANCE_TESTS_FILTER: '^.*baremetal(.(?!noauth).*)?$' OS_BRANCH: ${{ matrix.openstack_version }} + # TODO(dtantsur): default to "all" when no longer supporting versions before 2024.1 + OS_SYSTEM_SCOPE: ${{ matrix.os_system_scope }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/script/stackenv b/script/stackenv index d51d311e74..ef6311c039 100644 --- a/script/stackenv +++ b/script/stackenv @@ -30,4 +30,8 @@ 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 +if [[ "${OS_SYSTEM_SCOPE:-}" == "all" ]]; then + unset PROJECT_NAME + unset TENANT_NAME +fi popd From 9ec7257223ff43fcd0679d1db5f2198ffc7c9a98 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 09:53:30 +0000 Subject: [PATCH 1768/2296] build(deps): bump golang.org/x/crypto from 0.20.0 to 0.21.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.20.0 to 0.21.0. - [Commits](https://github.com/golang/crypto/compare/v0.20.0...v0.21.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index eade2a66d8..16fba4e88e 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.21.6 require ( - golang.org/x/crypto v0.20.0 + golang.org/x/crypto v0.21.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.17.0 // indirect +require golang.org/x/sys v0.18.0 // indirect diff --git a/go.sum b/go.sum index b1239ddefe..567ebb0d14 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= -golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= 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 d17658b3a30f05d32d50114512d68a28c44b4480 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 6 Mar 2024 16:38:26 +0100 Subject: [PATCH 1769/2296] Remove context from the Provider client Now that Gophercloud has per-call context, the `context.Context` in the Provider client is redundant and difficult to get right. This patch removes support for attaching a context to the Provider client. --- CHANGELOG.md | 1 + internal/ctxt/merge.go | 52 ------------- internal/ctxt/merge_test.go | 133 -------------------------------- provider_client.go | 19 +---- testing/provider_client_test.go | 12 ++- 5 files changed, 9 insertions(+), 208 deletions(-) delete mode 100644 internal/ctxt/merge.go delete mode 100644 internal/ctxt/merge_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 37692596bc..3495030478 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ BREAKING CHANGES: * [GH-2886](https://github.com/gophercloud/gophercloud/pull/2886) ports: Fix value_specs implementation * [GH-2665](https://github.com/gophercloud/gophercloud/pull/2665) Cinder: Remove multiatttach request parameter * [GH-2936](https://github.com/gophercloud/gophercloud/pull/2936) Make Gophercloud context-aware: all function signatures triggering an HTTP call now accept a context.Context for tracing and cancellation +* [GH-2970](https://github.com/gophercloud/gophercloud/pull/2970) Remove context from the Provider client New features and improvements: diff --git a/internal/ctxt/merge.go b/internal/ctxt/merge.go deleted file mode 100644 index 5575596a4e..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 := context.WithCancelCause(ctx1) - stop := context.AfterFunc(ctx2, func() { - cancel(context.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 any) any { - 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 a179bd661b..0000000000 --- a/internal/ctxt/merge_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package ctxt_test - -import ( - "context" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/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 b6d6eda379..fcbedf1480 100644 --- a/provider_client.go +++ b/provider_client.go @@ -9,8 +9,6 @@ import ( "net/http" "strings" "sync" - - "github.com/gophercloud/gophercloud/v2/internal/ctxt" ) // DefaultUserAgent is the default User-Agent string set in the request header. @@ -89,11 +87,6 @@ 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 context.Context - // Retry backoff func is called when rate limited. RetryBackoffFunc RetryBackoffFunc @@ -392,12 +385,6 @@ 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) if err != nil { return nil, err @@ -555,7 +542,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, var e error state.retries = state.retries + 1 - e = f(client.Context, &respErr, err, state.retries) + e = f(ctx, &respErr, err, state.retries) if e != nil { return resp, e @@ -592,7 +579,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, if err != nil && client.RetryFunc != nil { var e error state.retries = state.retries + 1 - e = client.RetryFunc(client.Context, method, url, options, err, state.retries) + e = client.RetryFunc(ctx, method, url, options, err, state.retries) if e != nil { return resp, e } @@ -616,7 +603,7 @@ 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(client.Context, method, url, options, err, state.retries) + e = client.RetryFunc(ctx, method, url, options, err, state.retries) if e != nil { return resp, e } diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 337520bdab..54c0d1e784 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -375,9 +375,9 @@ func TestRequestWithContext(t *testing.T) { defer ts.Close() ctx, cancel := context.WithCancel(context.Background()) - p := &gophercloud.ProviderClient{Context: ctx} + p := &gophercloud.ProviderClient{} - res, err := p.Request(context.TODO(), "GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) + res, err := p.Request(ctx, "GET", ts.URL, &gophercloud.RequestOpts{KeepResponseBody: true}) th.AssertNoErr(t, err) _, err = io.ReadAll(res.Body) th.AssertNoErr(t, err) @@ -385,7 +385,7 @@ func TestRequestWithContext(t *testing.T) { th.AssertNoErr(t, err) cancel() - res, err = p.Request(context.TODO(), "GET", ts.URL, &gophercloud.RequestOpts{}) + res, err = p.Request(ctx, "GET", ts.URL, &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } @@ -605,9 +605,7 @@ func TestRequestRetryContext(t *testing.T) { cancel() }() - p := &gophercloud.ProviderClient{ - Context: ctx, - } + p := &gophercloud.ProviderClient{} p.UseTokenLock() p.SetToken(client.TokenID) p.MaxBackoffRetries = 3 @@ -624,7 +622,7 @@ func TestRequestRetryContext(t *testing.T) { http.Error(w, "retry later", http.StatusTooManyRequests) }) - _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) + _, err := p.Request(ctx, "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } From 3fe7a696d572da789c256b31138fb1e8a2044e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 8 Mar 2024 08:48:48 +0100 Subject: [PATCH 1770/2296] Remove v2 branch until we have a need for it Maintaining a separate v2 branch requires extra efforts and resources, so let's not do it until we absolutely need it. --- .github/labels.yaml | 3 --- .github/workflows/backport_v2.yaml | 40 ------------------------------ 2 files changed, 43 deletions(-) delete mode 100644 .github/workflows/backport_v2.yaml diff --git a/.github/labels.yaml b/.github/labels.yaml index 8debbd689f..8a908009e2 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -1,9 +1,6 @@ - color: '30ABB9' description: This PR will be backported to v1 name: backport-v1 -- color: '30ABB9' - description: This PR will be backported to v2 - name: backport-v2 - color: '0366d6' description: Pull requests that update a dependency file name: dependencies diff --git a/.github/workflows/backport_v2.yaml b/.github/workflows/backport_v2.yaml deleted file mode 100644 index 864ab8694e..0000000000 --- a/.github/workflows/backport_v2.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: Pull Request backporting - -on: - pull_request_target: - types: - - closed - - labeled - -jobs: - backporting: - name: "Backporting" - # Only react to merged PRs for security reasons. - # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. - if: > - github.event.pull_request.merged - && ( - github.event.action == 'closed' - && contains(github.event.pull_request.labels.*.name, 'backport-v2') - || ( - github.event.action == 'labeled' - && contains(github.event.label.name, 'backport-v2') - ) - ) - runs-on: ubuntu-latest - steps: - - name: Generate a token from the gophercloud-backport-bot github-app - id: generate_token - uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc - with: - app_id: ${{ secrets.BACKPORT_APP_ID }} - private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} - - - name: Backporting - uses: kiegroup/git-backporting@bce5dd4f9969d47da9cbb6ed06abbf87216afc98 - with: - target-branch: v2 - pull-request: ${{ github.event.pull_request.url }} - auth: ${{ steps.generate_token.outputs.token }} - no-squash: true - strategy-option: find-renames From e25ab6cd6482f58c3f02b24058e88656fc21b742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 8 Mar 2024 10:59:06 +0100 Subject: [PATCH 1771/2296] Report on backporting errors This hopefully reduces the likelihood we're missing backports in stable branches. --- .github/workflows/backport_v1.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 9030ab0e92..1e1e0a478b 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -44,6 +44,16 @@ jobs: no-squash: true strategy-option: find-renames + - name: Report failure + if: failure() + run: gh issue comment "$NUMBER" --body "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + BODY: > + Failed to backport PR to `v1` branch. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. + - name: Report an error if backport unsupported labels if: > contains(github.event.pull_request.labels.*.name, 'semver:major') From a3e46928ce1b017e0f989ef45814fc0cc163d94b Mon Sep 17 00:00:00 2001 From: Johann Gnaucke Date: Fri, 8 Mar 2024 11:39:29 +0000 Subject: [PATCH 1772/2296] Add bootable filter to v3 cinder list options --- openstack/blockstorage/v3/volumes/requests.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 39c12be630..f899b63b77 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -147,6 +147,9 @@ type ListOpts struct { // The ID of the last-seen item. Marker string `q:"marker"` + + // Bootable will filter results based on if they are bootable volumes + Bootable bool `q:"bootable"` } // ToVolumeListQuery formats a ListOpts into a query string. From 6cbf38d6114222b5b8ceafa88edb68ab46b190fd Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Fri, 1 Mar 2024 11:25:50 +0100 Subject: [PATCH 1773/2296] baremetal: fix handling of the "fields" query argument Ironic expects a comma-separated string. Add a new feature to BuildQueryString to choose the format of slices. Closes: #2960 --- .../openstack/baremetal/v1/nodes_test.go | 32 +++++++++++++++++++ .../baremetal/v1/allocations/requests.go | 2 +- openstack/baremetal/v1/conductors/requests.go | 2 +- .../v1/conductors/testing/requests_test.go | 2 +- openstack/baremetal/v1/nodes/requests.go | 4 +-- .../v1/nodes/testing/requests_test.go | 2 +- openstack/baremetal/v1/ports/requests.go | 2 +- .../v1/ports/testing/requests_test.go | 2 +- params.go | 21 +++++++++--- 9 files changed, 57 insertions(+), 12 deletions(-) diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index e51501eadf..e63dfa1d0a 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -46,6 +46,38 @@ func TestNodesCreateDestroy(t *testing.T) { th.AssertEquals(t, found, true) } +func TestNodesFields(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.List(client, nodes.ListOpts{ + Fields: []string{"uuid", "deploy_interface"}, + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + nodeList, err := nodes.ExtractNodes(page) + if err != nil { + return false, err + } + + for _, n := range nodeList { + if n.UUID == "" || n.DeployInterface == "" { + t.Errorf("UUID or DeployInterface empty on %+v", n) + } + if n.BootInterface != "" { + t.Errorf("BootInterface was not fetched but is not empty on %+v", n) + } + } + + return true, nil + }) + th.AssertNoErr(t, err) +} + func TestNodesUpdate(t *testing.T) { clients.RequireLong(t) diff --git a/openstack/baremetal/v1/allocations/requests.go b/openstack/baremetal/v1/allocations/requests.go index 4357f8c46c..97d84270ff 100644 --- a/openstack/baremetal/v1/allocations/requests.go +++ b/openstack/baremetal/v1/allocations/requests.go @@ -82,7 +82,7 @@ type ListOpts struct { State AllocationState `q:"state"` // One or more fields to be returned in the response. - Fields []string `q:"fields"` + Fields []string `q:"fields" format:"comma-separated"` // Requests a page size of items. Limit int `q:"limit"` diff --git a/openstack/baremetal/v1/conductors/requests.go b/openstack/baremetal/v1/conductors/requests.go index 068ab215be..e05978d6c0 100644 --- a/openstack/baremetal/v1/conductors/requests.go +++ b/openstack/baremetal/v1/conductors/requests.go @@ -20,7 +20,7 @@ type ListOptsBuilder interface { // for pagination. type ListOpts struct { // One or more fields to be returned in the response. - Fields []string `q:"fields"` + Fields []string `q:"fields" format:"comma-separated"` // Requests a page size of items. Limit int `q:"limit"` diff --git a/openstack/baremetal/v1/conductors/testing/requests_test.go b/openstack/baremetal/v1/conductors/testing/requests_test.go index 7ddacf7f90..6f6aa518ad 100644 --- a/openstack/baremetal/v1/conductors/testing/requests_test.go +++ b/openstack/baremetal/v1/conductors/testing/requests_test.go @@ -88,7 +88,7 @@ func TestListOpts(t *testing.T) { // Regular ListOpts can query, err := opts.ToConductorListQuery() - th.AssertEquals(t, query, "?fields=hostname&fields=alive") + th.AssertEquals(t, "?fields=hostname%2Calive", query) th.AssertNoErr(t, err) } diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 567aa6be71..5e2bf0ce4a 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -98,7 +98,7 @@ type ListOpts struct { Fault string `q:"fault"` // One or more fields to be returned in the response. - Fields []string `q:"fields"` + Fields []string `q:"fields" format:"comma-separated"` // Requests a page size of items. Limit int `q:"limit"` @@ -658,7 +658,7 @@ type ListBIOSSettingsOpts struct { Detail bool `q:"detail"` // One or more fields to be returned in the response. - Fields []string `q:"fields"` + Fields []string `q:"fields" format:"comma-separated"` } // ToListBIOSSettingsOptsQuery formats a ListBIOSSettingsOpts into a query string diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index c0f1a28360..c6594d2b16 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -84,7 +84,7 @@ func TestListOpts(t *testing.T) { // Regular ListOpts can query, err := opts.ToNodeListQuery() - th.AssertEquals(t, query, "?fields=name&fields=uuid") + th.AssertEquals(t, "?fields=name%2Cuuid", query) th.AssertNoErr(t, err) } diff --git a/openstack/baremetal/v1/ports/requests.go b/openstack/baremetal/v1/ports/requests.go index 2c4e515191..291ddca120 100644 --- a/openstack/baremetal/v1/ports/requests.go +++ b/openstack/baremetal/v1/ports/requests.go @@ -33,7 +33,7 @@ type ListOpts struct { Address string `q:"address"` // One or more fields to be returned in the response. - Fields []string `q:"fields"` + Fields []string `q:"fields" format:"comma-separated"` // Requests a page size of items. Limit int `q:"limit"` diff --git a/openstack/baremetal/v1/ports/testing/requests_test.go b/openstack/baremetal/v1/ports/testing/requests_test.go index 95073b0a71..b52d3c521c 100644 --- a/openstack/baremetal/v1/ports/testing/requests_test.go +++ b/openstack/baremetal/v1/ports/testing/requests_test.go @@ -81,7 +81,7 @@ func TestListOpts(t *testing.T) { // Regular ListOpts can query, err := opts.ToPortListQuery() - th.AssertEquals(t, query, "?fields=uuid&fields=address") + th.AssertEquals(t, "?fields=uuid%2Caddress", query) th.AssertNoErr(t, err) } diff --git a/params.go b/params.go index 9e7bb5bde2..10e1536ae1 100644 --- a/params.go +++ b/params.go @@ -315,8 +315,15 @@ converted into query parameters based on a "q" tag. For example: will be converted into "?x_bar=AAA&lorem_ipsum=BBB". -The struct's fields may be strings, integers, or boolean values. Fields left at -their type's zero value will be omitted from the query. +The struct's fields may be strings, integers, slices, or boolean values. Fields +left at their type's zero value will be omitted from the query. + +Slice are handled in one of two ways: + + type struct Something { + Bar []string `q:"bar"` // E.g. ?bar=1&bar=2 + Baz []int `q:"baz" format="comma-separated"` // E.g. ?baz=1,2 + } */ func BuildQueryString(opts interface{}) (*url.URL, error) { optsValue := reflect.ValueOf(opts) @@ -355,16 +362,22 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { case reflect.Bool: params.Add(tags[0], strconv.FormatBool(v.Bool())) case reflect.Slice: + var values []string switch v.Type().Elem() { case reflect.TypeOf(0): for i := 0; i < v.Len(); i++ { - params.Add(tags[0], strconv.FormatInt(v.Index(i).Int(), 10)) + values = append(values, strconv.FormatInt(v.Index(i).Int(), 10)) } default: for i := 0; i < v.Len(); i++ { - params.Add(tags[0], v.Index(i).String()) + values = append(values, v.Index(i).String()) } } + if sliceFormat := f.Tag.Get("format"); sliceFormat == "comma-separated" { + params.Add(tags[0], strings.Join(values, ",")) + } else { + params[tags[0]] = append(params[tags[0]], values...) + } case reflect.Map: if v.Type().Key().Kind() == reflect.String && v.Type().Elem().Kind() == reflect.String { var s []string From a9bafc25aac75c84be61e6a9514341e4265117d7 Mon Sep 17 00:00:00 2001 From: till Date: Tue, 5 Mar 2024 17:05:42 +0100 Subject: [PATCH 1774/2296] compute: Allow setting the hostname when creating a server Related: #2967 --- openstack/compute/v2/servers/requests.go | 6 ++++++ .../v2/servers/testing/fixtures_test.go | 20 +++++++++++++++++++ .../v2/servers/testing/requests_test.go | 16 +++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index df2df35ce0..8566d74647 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -241,6 +241,12 @@ type CreateOpts struct { // Tags allows a server to be tagged with single-word metadata. // Requires microversion 2.52 or later. Tags []string `json:"tags,omitempty"` + + // (Available from 2.90) Hostname specifies the hostname to configure for the + // instance in the metadata service. Starting with microversion 2.94, this can + // be a Fully Qualified Domain Name (FQDN) of up to 255 characters in length. + // If not set, OpenStack will derive the server's hostname from the Name field. + Hostname string `json:"hostname,omitempty"` } // ToServerCreateMap assembles a request body based on the contents of a diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index 3f628efe9e..226a129896 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -267,6 +267,7 @@ const SingleServerBody = ` "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "active", "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", + "OS-EXT-SRV-ATTR:hostname": "derp.local", "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", "flavor": { @@ -849,6 +850,25 @@ func HandleServerCreationWithCustomFieldSuccessfully(t *testing.T, response stri }) } +func HandleServerCreationWithHostname(t *testing.T, response string) { + th.Mux.HandleFunc("/servers", 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, `{ + "server": { + "name": "derp", + "hostname": "derp.local", + "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", + "flavorRef": "1" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, response) + }) +} + // HandleServerCreationWithUserdata sets up the test server to respond to a server creation request // with a given response. func HandleServerCreationWithUserdata(t *testing.T, response string) { diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 19868ca3bd..bb130d568f 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -204,6 +204,22 @@ func TestCreateServerWithUserdataEncoded(t *testing.T) { th.CheckDeepEquals(t, ServerDerp, *actual) } +func TestCreateServerWithHostname(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleServerCreationWithHostname(t, SingleServerBody) + + actual, err := servers.Create(context.TODO(), client.ServiceClient(), servers.CreateOpts{ + Name: "derp", + ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", + FlavorRef: "1", + Hostname: "derp.local", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, ServerDerp, *actual) +} + func TestDeleteServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From dfe81eb5c2af16715409f42d9eab8ebe60cb6fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Sun, 10 Mar 2024 15:59:32 +0100 Subject: [PATCH 1775/2296] [CI] Fix trigger for FWaaS v2 presubmit The path was missing the version which caused changes to FWaaS files to not trigger the FWaaS presubmit jobs. The periodics were unaffected. --- .github/workflows/functional-fwaas_v2.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index f2b18fc97d..b6e9a500f9 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -2,7 +2,7 @@ name: functional-fwaas_v2 on: pull_request: paths: - - '**networking/extensions/fwaas_v2**' + - '**networking/v2/extensions/fwaas_v2**' schedule: - cron: '0 0 */3 * *' jobs: From a2f421e76ce4bc26d4972756a8b7d5a3ec1e1c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Sun, 10 Mar 2024 16:59:41 +0100 Subject: [PATCH 1776/2296] [CI] Fix portbiding tests Since https://review.opendev.org/c/openstack/neutron/+/909075, port-binding operation are restricted to the service role. Update the policies so that we can continue exercising these tests in CI. Fixes #2983 --- .github/workflows/functional-fwaas_v2.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index f2b18fc97d..55fd5fbda1 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -39,6 +39,10 @@ jobs: Q_ML2_PLUGIN_TYPE_DRIVERS=flat,gre,vlan,vxlan Q_ML2_TENANT_NETWORK_TYPE=vxlan Q_TUNNEL_TYPES=vxlan,gre + + [[post-config|/etc/neutron/policy.d/port_binding.yaml]] + "create_port:binding:profile": "rule:admin_only or rule:service_api" + "update_port:binding:profile": "rule:admin_only or rule:service_api" 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@v5 From 70aaa8a4dc54010f36440282caa32bd330169060 Mon Sep 17 00:00:00 2001 From: Johann Gnaucke Date: Mon, 11 Mar 2024 08:36:03 +0000 Subject: [PATCH 1777/2296] Make bootable list option optional --- openstack/blockstorage/v3/volumes/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index f899b63b77..32bfc41180 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -149,7 +149,7 @@ type ListOpts struct { Marker string `q:"marker"` // Bootable will filter results based on if they are bootable volumes - Bootable bool `q:"bootable"` + Bootable *bool `q:"bootable,omitempty"` } // ToVolumeListQuery formats a ListOpts into a query string. From 0637f6b63d81d05511a8fedccc779a10fc49d885 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 09:45:06 +0000 Subject: [PATCH 1778/2296] build(deps): bump kiegroup/git-backporting from 4.5.1 to 4.5.2 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.5.1 to 4.5.2. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/bce5dd4f9969d47da9cbb6ed06abbf87216afc98...f4a2683189e62ce0acf2f3285b3b27debf2eacc9) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 1e1e0a478b..71ebbb5b01 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -36,7 +36,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@bce5dd4f9969d47da9cbb6ed06abbf87216afc98 + uses: kiegroup/git-backporting@f4a2683189e62ce0acf2f3285b3b27debf2eacc9 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} From 8102f8116f992c928469f00aa8c0b3b188d3dd11 Mon Sep 17 00:00:00 2001 From: Johann Gnaucke Date: Mon, 11 Mar 2024 11:35:13 +0100 Subject: [PATCH 1779/2296] Update openstack/blockstorage/v3/volumes/requests.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin André --- openstack/blockstorage/v3/volumes/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 32bfc41180..044d18fca9 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -148,7 +148,7 @@ type ListOpts struct { // The ID of the last-seen item. Marker string `q:"marker"` - // Bootable will filter results based on if they are bootable volumes + // Bootable will filter results based on whether they are bootable volumes Bootable *bool `q:"bootable,omitempty"` } From 457ab161cb7758ea9ce6836fadb4f1f8b7c842da Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 29 Feb 2024 16:36:18 +0100 Subject: [PATCH 1780/2296] baremetal: support for virtual media attach/detach Closes: #2958 Signed-off-by: Dmitry Tantsur --- .../openstack/baremetal/v1/nodes_test.go | 30 ++++++++ openstack/baremetal/v1/nodes/requests.go | 75 +++++++++++++++++++ openstack/baremetal/v1/nodes/results.go | 8 ++ .../v1/nodes/testing/fixtures_test.go | 41 ++++++++++ .../v1/nodes/testing/requests_test.go | 63 ++++++++++++++++ openstack/baremetal/v1/nodes/urls.go | 4 + 6 files changed, 221 insertions(+) diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index e63dfa1d0a..df4cd556dd 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -200,3 +200,33 @@ func TestNodesFirmwareInterface(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, nodeFirmwareCmps, []nodes.FirmwareComponent{}) } + +func TestNodesVirtualMedia(t *testing.T) { + clients.SkipReleasesBelow(t, "master") // 2024.1 + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.89" + + node, err := CreateNode(t, client) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node) + + err = nodes.AttachVirtualMedia(context.TODO(), client, node.UUID, nodes.AttachVirtualMediaOpts{ + DeviceType: nodes.VirtualMediaCD, + // It does not matter if QOTD server is actually present: the + // request is processes asynchronously, all we need is a valid URL + // that will not result in Ironic stuck for a long time. + ImageURL: "http://127.0.0.1:17", + }).ExtractErr() + th.AssertNoErr(t, err) + + err = nodes.DetachVirtualMedia(context.TODO(), client, node.UUID, nodes.DetachVirtualMediaOpts{ + DeviceTypes: []nodes.VirtualMediaDeviceType{nodes.VirtualMediaCD}, + }).ExtractErr() + th.AssertNoErr(t, err) + + err = nodes.DetachVirtualMedia(context.TODO(), client, node.UUID, nodes.DetachVirtualMediaOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 5e2bf0ce4a..f3989dde90 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -903,3 +903,78 @@ func ListFirmware(ctx context.Context, client *gophercloud.ServiceClient, id str _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +type VirtualMediaDeviceType string + +const ( + VirtualMediaDisk VirtualMediaDeviceType = "disk" + VirtualMediaCD VirtualMediaDeviceType = "cdrom" + VirtualMediaFloppy VirtualMediaDeviceType = "floppy" +) + +type ImageDownloadSource string + +const ( + ImageDownloadSourceHTTP ImageDownloadSource = "http" + ImageDownloadSourceLocal ImageDownloadSource = "local" + ImageDownloadSourceSwift ImageDownloadSource = "swift" +) + +// The desired virtual media attachment on the baremetal node. +type AttachVirtualMediaOpts struct { + DeviceType VirtualMediaDeviceType `json:"device_type"` + ImageURL string `json:"image_url"` + ImageDownloadSource ImageDownloadSource `json:"image_download_source,omitempty"` +} + +type AttachVirtualMediaOptsBuilder interface { + ToAttachVirtualMediaMap() (map[string]interface{}, error) +} + +func (opts AttachVirtualMediaOpts) ToAttachVirtualMediaMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Request to attach a virtual media device to the Node. +func AttachVirtualMedia(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachVirtualMediaOptsBuilder) (r VirtualMediaAttachResult) { + reqBody, err := opts.ToAttachVirtualMediaMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(ctx, virtualMediaURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// The desired virtual media detachment on the baremetal node. +type DetachVirtualMediaOpts struct { + DeviceTypes []VirtualMediaDeviceType `q:"device_types" format:"comma-separated"` +} + +type DetachVirtualMediaOptsBuilder interface { + ToDetachVirtualMediaOptsQuery() (string, error) +} + +func (opts DetachVirtualMediaOpts) ToDetachVirtualMediaOptsQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Request to detach a virtual media device from the Node. +func DetachVirtualMedia(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachVirtualMediaOptsBuilder) (r VirtualMediaDetachResult) { + query, err := opts.ToDetachVirtualMediaOptsQuery() + if err != nil { + r.Err = err + return + } + + resp, err := client.Delete(ctx, virtualMediaURL(client, id)+query, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, 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 a7e02cba6a..18fbc56cfb 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -642,3 +642,11 @@ func (r ListFirmwareResult) Extract() ([]FirmwareComponent, error) { err := r.ExtractInto(&s) return s.Components, err } + +type VirtualMediaAttachResult struct { + gophercloud.ErrResult +} + +type VirtualMediaDetachResult struct { + gophercloud.ErrResult +} diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index 301d75eed7..0e4859e9dd 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -859,6 +859,21 @@ const NodeFirmwareListBody = ` } ` +const NodeVirtualMediaAttachBody = ` +{ + "image_url": "https://example.com/image", + "device_type": "cdrom" +} +` + +const NodeVirtualMediaAttachBodyWithSource = ` +{ + "image_url": "https://example.com/image", + "device_type": "cdrom", + "image_download_source": "http" +} +` + 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") @@ -1668,3 +1683,29 @@ func HandleListFirmwareSuccessfully(t *testing.T) { fmt.Fprintf(w, NodeFirmwareListBody) }) } + +func HandleAttachVirtualMediaSuccessfully(t *testing.T, withSource bool) { + th.Mux.HandleFunc("/nodes/1234asdf/vmedia", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + if withSource { + th.TestJSONRequest(t, r, NodeVirtualMediaAttachBodyWithSource) + } else { + th.TestJSONRequest(t, r, NodeVirtualMediaAttachBody) + } + w.WriteHeader(http.StatusNoContent) + }) +} + +func HandleDetachVirtualMediaSuccessfully(t *testing.T, withType bool) { + th.Mux.HandleFunc("/nodes/1234asdf/vmedia", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + if withType { + th.TestFormValues(t, r, map[string]string{"device_types": "cdrom"}) + } else { + th.TestFormValues(t, r, map[string]string{}) + } + w.WriteHeader(http.StatusNoContent) + }) +} diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index c6594d2b16..33e1640393 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -743,3 +743,66 @@ func TestListFirmware(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, NodeFirmwareList, actual) } + +func TestVirtualMediaOpts(t *testing.T) { + opts := nodes.DetachVirtualMediaOpts{ + DeviceTypes: []nodes.VirtualMediaDeviceType{nodes.VirtualMediaCD, nodes.VirtualMediaDisk}, + } + + // Regular ListOpts can + query, err := opts.ToDetachVirtualMediaOptsQuery() + th.AssertEquals(t, "?device_types=cdrom%2Cdisk", query) + th.AssertNoErr(t, err) +} + +func TestVirtualMediaAttach(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAttachVirtualMediaSuccessfully(t, false) + + c := client.ServiceClient() + opts := nodes.AttachVirtualMediaOpts{ + ImageURL: "https://example.com/image", + DeviceType: nodes.VirtualMediaCD, + } + err := nodes.AttachVirtualMedia(context.TODO(), c, "1234asdf", opts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestVirtualMediaAttachWithSource(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAttachVirtualMediaSuccessfully(t, true) + + c := client.ServiceClient() + opts := nodes.AttachVirtualMediaOpts{ + ImageURL: "https://example.com/image", + DeviceType: nodes.VirtualMediaCD, + ImageDownloadSource: nodes.ImageDownloadSourceHTTP, + } + err := nodes.AttachVirtualMedia(context.TODO(), c, "1234asdf", opts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestVirtualMediaDetach(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDetachVirtualMediaSuccessfully(t, false) + + c := client.ServiceClient() + err := nodes.DetachVirtualMedia(context.TODO(), c, "1234asdf", nodes.DetachVirtualMediaOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestVirtualMediaDetachWithTypes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDetachVirtualMediaSuccessfully(t, true) + + c := client.ServiceClient() + opts := nodes.DetachVirtualMediaOpts{ + DeviceTypes: []nodes.VirtualMediaDeviceType{nodes.VirtualMediaCD}, + } + err := nodes.DetachVirtualMedia(context.TODO(), c, "1234asdf", opts).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/nodes/urls.go b/openstack/baremetal/v1/nodes/urls.go index 34390633c9..2948bb659e 100644 --- a/openstack/baremetal/v1/nodes/urls.go +++ b/openstack/baremetal/v1/nodes/urls.go @@ -85,3 +85,7 @@ func inventoryURL(client *gophercloud.ServiceClient, id string) string { func firmwareListURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("nodes", id, "firmware") } + +func virtualMediaURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("nodes", id, "vmedia") +} From db5585b2a2f0e9e48cabe92376d107f9fba2b0d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 11 Mar 2024 14:34:50 +0100 Subject: [PATCH 1781/2296] Revert "[CI] Fix portbiding tests" This reverts commit a2f421e76ce4bc26d4972756a8b7d5a3ec1e1c60. --- .github/workflows/functional-fwaas_v2.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 62ea71466a..b6e9a500f9 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -39,10 +39,6 @@ jobs: Q_ML2_PLUGIN_TYPE_DRIVERS=flat,gre,vlan,vxlan Q_ML2_TENANT_NETWORK_TYPE=vxlan Q_TUNNEL_TYPES=vxlan,gre - - [[post-config|/etc/neutron/policy.d/port_binding.yaml]] - "create_port:binding:profile": "rule:admin_only or rule:service_api" - "update_port:binding:profile": "rule:admin_only or rule:service_api" 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@v5 From 14b906e0d364c5c794970a7e38252948738ccc94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 09:12:09 +0000 Subject: [PATCH 1782/2296] build(deps): bump EmilienM/devstack-action from 0.14 to 0.15 Bumps [EmilienM/devstack-action](https://github.com/emilienm/devstack-action) from 0.14 to 0.15. - [Release notes](https://github.com/emilienm/devstack-action/releases) - [Commits](https://github.com/emilienm/devstack-action/compare/c41f86d8df58b53c55f070207b6dfce656788cfd...e82a9cbead099cba72f99537e82a360c3e319c69) --- updated-dependencies: - dependency-name: EmilienM/devstack-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .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-image.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 c6efa0cab0..08e102d298 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -44,7 +44,7 @@ jobs: run: sudo apt-get purge -y dnsmasq-base if: matrix.ubuntu_version == '22.04' - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index bf8b40457a..1a00ecbaa5 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -32,7 +32,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 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 c6526df766..c3baf9283a 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 79b0fbc246..11297618bc 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index e1aa044663..d6a6ddb0c9 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index ee332836a8..53d886e35e 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -41,7 +41,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index a652daa508..5f630e4b34 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -30,7 +30,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 62ea71466a..faceaf0305 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 895cbe3df3..ee4fd32ae6 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 2b96be230f..ef04e891ff 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index f734d1cbf2..6438c6f7d6 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index f619d2b873..5df541d161 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index ebf1a86857..30bd829ce2 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index ff3307b666..5a3c92e80e 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index c1c6d2037e..20fac3df51 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index b7d1908380..1b7dfbdb52 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 31894c6f9f..0f30fc8d28 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} - name: Checkout go diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 1be0b3ecda..45ded72fbb 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -29,7 +29,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v4 - name: Deploy devstack - uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} conf_overrides: | From f70a5c1fd677e99c4a74c717de65c5e1de4c0ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 11 Mar 2024 14:37:25 +0100 Subject: [PATCH 1783/2296] [CI] Fix portbiding tests Since https://review.opendev.org/c/openstack/neutron/+/909075, port-binding operation are restricted to the service role. Update the policies so that we can continue exercising these tests in CI. Fixes #2983 --- .github/workflows/functional-networking.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index ff3307b666..05214d60e9 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -28,6 +28,14 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v4 + - name: Create additional neutron policies + run: | + mkdir /tmp/neutron-policies + cat << EOF >> /tmp/neutron-policies/port_binding.yaml + --- + "create_port:binding:profile": "rule:admin_only or rule:service_api" + "update_port:binding:profile": "rule:admin_only or rule:service_api" + EOF - name: Deploy devstack uses: EmilienM/devstack-action@c41f86d8df58b53c55f070207b6dfce656788cfd with: @@ -36,6 +44,10 @@ jobs: 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 + + [[post-config|\$NEUTRON_CONF]] + [oslo_policy] + policy_dirs = /tmp/neutron-policies 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@v5 From 8e35b9cf16920acefb7252e7bca274818058850f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Sun, 10 Mar 2024 14:23:43 +0100 Subject: [PATCH 1784/2296] [CI] Fix number of args passed to FWaaSv2 tests A follow-up to https://github.com/gophercloud/gophercloud/pull/2936 that missed part of the `internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go` file. Fixes #2981 --- .../networking/v2/extensions/fwaas_v2/groups_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index a948a0da5d..df4740ca20 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -51,7 +51,7 @@ func TestGroupCRUD(t *testing.T) { EgressFirewallPolicyID: &firewall_policy_id, } - updatedGroup, err := groups.Update(client, createdGroup.ID, updateOpts).Extract() + updatedGroup, err := groups.Update(context.TODO(), client, createdGroup.ID, updateOpts).Extract() if err != nil { t.Fatalf("Unable to update firewall group %s: %v", createdGroup.ID, err) } @@ -65,7 +65,7 @@ func TestGroupCRUD(t *testing.T) { t.Logf("Updated firewall group %s", updatedGroup.ID) - removeIngressPolicy, err := groups.RemoveIngressPolicy(client, updatedGroup.ID).Extract() + removeIngressPolicy, err := groups.RemoveIngressPolicy(context.TODO(), client, updatedGroup.ID).Extract() if err != nil { t.Fatalf("Unable to remove ingress firewall policy from firewall group %s: %v", removeIngressPolicy.ID, err) } @@ -75,7 +75,7 @@ func TestGroupCRUD(t *testing.T) { t.Logf("Ingress policy removed from firewall group %s", updatedGroup.ID) - removeEgressPolicy, err := groups.RemoveEgressPolicy(client, updatedGroup.ID).Extract() + removeEgressPolicy, err := groups.RemoveEgressPolicy(context.TODO(), client, updatedGroup.ID).Extract() if err != nil { t.Fatalf("Unable to remove egress firewall policy from firewall group %s: %v", removeEgressPolicy.ID, err) } From 269bf7614006045682d2837be5ff23f4dce8dd84 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 14 Mar 2024 16:58:51 +0100 Subject: [PATCH 1785/2296] functional-baremetal: remove dnsmasq-base on all versions Zed now fails with a similar error. Let's make sure we start without dnsmasq-base in all baremetal jobs. --- .github/workflows/functional-baremetal.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 08e102d298..beccda9480 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -42,7 +42,6 @@ jobs: if: matrix.ubuntu_version == '20.04' - name: Work around broken dnsmasq run: sudo apt-get purge -y dnsmasq-base - if: matrix.ubuntu_version == '22.04' - name: Deploy devstack uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: From f646332da0848c555cc5f1982f8c9ad43c5406c3 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 14 Mar 2024 15:32:31 +0100 Subject: [PATCH 1786/2296] baremetal: add WaitForProvisionState and expand tests --- .../openstack/baremetal/v1/nodes_test.go | 13 ++++++++-- openstack/baremetal/v1/nodes/util.go | 25 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 openstack/baremetal/v1/nodes/util.go diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index df4cd556dd..53e060c0b9 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -21,7 +21,7 @@ func TestNodesCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) client.Microversion = "1.38" - node, err := CreateNode(t, client) + node, err := CreateFakeNode(t, client) th.AssertNoErr(t, err) defer DeleteNode(t, client, node) @@ -42,8 +42,17 @@ func TestNodesCreateDestroy(t *testing.T) { return false, nil }) th.AssertNoErr(t, err) - th.AssertEquals(t, found, true) + + th.AssertEquals(t, node.ProvisionState, string(nodes.Enroll)) + + err = nodes.ChangeProvisionState(context.TODO(), client, node.UUID, nodes.ProvisionStateOpts{ + Target: nodes.TargetManage, + }).ExtractErr() + th.AssertNoErr(t, err) + + err = nodes.WaitForProvisionState(context.TODO(), client, node.UUID, nodes.Manageable, 5) + th.AssertNoErr(t, err) } func TestNodesFields(t *testing.T) { diff --git a/openstack/baremetal/v1/nodes/util.go b/openstack/baremetal/v1/nodes/util.go new file mode 100644 index 0000000000..86e43256c1 --- /dev/null +++ b/openstack/baremetal/v1/nodes/util.go @@ -0,0 +1,25 @@ +package nodes + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) + +// WaitForProvisionState will continually poll a node until it successfully +// transitions to a specified state. It will do this for at most the number +// of seconds specified. +func WaitForProvisionState(ctx context.Context, c *gophercloud.ServiceClient, id string, state ProvisionState, secs int) error { + return gophercloud.WaitFor(secs, func() (bool, error) { + current, err := Get(ctx, c, id).Extract() + if err != nil { + return false, err + } + + if current.ProvisionState == string(state) { + return true, nil + } + + return false, nil + }) +} From 7bcc341302ab01ba534c4dff85024f86505026a0 Mon Sep 17 00:00:00 2001 From: Vladimir Ermakov Date: Thu, 29 Feb 2024 09:20:38 +0100 Subject: [PATCH 1787/2296] Context-aware WaitFor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove timeout logic from WaitFor, just use context, Luke! Improve gophercloud.WaitFor not to wait first second testing: fix error validation with errors.Is acceptance: fix node group acceptance: remove unused ErrTimeout baremetal: update WaitFor signature Fix: #2956 Co-authored-by: Martin André Signed-off-by: Vladimir Ermakov --- .../openstack/baremetal/v1/nodes_test.go | 6 ++- .../blockstorage/extensions/extensions.go | 40 ++++++++++++---- .../extensions/schedulerhints_test.go | 11 ++++- .../blockstorage/noauth/blockstorage.go | 20 ++++++-- .../openstack/blockstorage/v1/blockstorage.go | 15 ++++-- .../openstack/blockstorage/v2/blockstorage.go | 20 ++++++-- .../openstack/blockstorage/v2/volumes_test.go | 14 ++++-- .../openstack/blockstorage/v3/blockstorage.go | 24 +++++++--- .../blockstorage/v3/snapshots_test.go | 4 +- .../blockstorage/v3/volumeattachments.go | 11 ++++- .../openstack/blockstorage/v3/volumes_test.go | 19 +++++--- .../openstack/clustering/v1/clustering.go | 8 ++-- .../openstack/compute/v2/compute.go | 15 ++++-- .../openstack/compute/v2/servers_test.go | 4 +- .../openstack/container/v1/capsules.go | 4 +- .../containerinfra/v1/containerinfra.go | 4 +- .../containerinfra/v1/nodegroups_test.go | 8 ++-- internal/acceptance/openstack/db/v1/db.go | 4 +- internal/acceptance/openstack/dns/v2/dns.go | 16 +++---- .../openstack/keymanager/v1/keymanager.go | 4 +- .../openstack/loadbalancer/v2/loadbalancer.go | 4 +- .../v2/extensions/agents/agents_test.go | 16 +++---- .../networking/v2/extensions/fwaas/fwaas.go | 4 +- .../networking/v2/extensions/layer3/layer3.go | 16 +++---- .../v2/extensions/lbaas_v2/lbaas_v2.go | 4 +- .../openstack/networking/v2/networking.go | 4 +- .../orchestration/v1/orchestration.go | 4 +- .../sharedfilesystems/v2/replicas.go | 8 ++-- .../sharedfilesystems/v2/shareaccessrules.go | 2 +- .../openstack/sharedfilesystems/v2/shares.go | 4 +- .../sharedfilesystems/v2/snapshots.go | 4 +- .../openstack/workflow/v2/execution.go | 4 +- internal/acceptance/tools/tools.go | 28 ++++------- openstack/baremetal/v1/nodes/util.go | 4 +- openstack/blockstorage/v1/snapshots/util.go | 7 ++- openstack/blockstorage/v1/volumes/util.go | 7 ++- openstack/blockstorage/v2/snapshots/util.go | 7 ++- openstack/blockstorage/v2/volumes/util.go | 7 ++- openstack/blockstorage/v3/attachments/util.go | 7 ++- openstack/blockstorage/v3/snapshots/util.go | 7 ++- openstack/blockstorage/v3/volumes/util.go | 7 ++- openstack/compute/v2/servers/util.go | 7 ++- testhelper/convenience.go | 14 ++++++ testing/util_test.go | 36 ++++++++++---- util.go | 47 +++++-------------- 45 files changed, 299 insertions(+), 211 deletions(-) diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index 53e060c0b9..d479bc0797 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -6,6 +6,7 @@ package v1 import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" @@ -51,7 +52,10 @@ func TestNodesCreateDestroy(t *testing.T) { }).ExtractErr() th.AssertNoErr(t, err) - err = nodes.WaitForProvisionState(context.TODO(), client, node.UUID, nodes.Manageable, 5) + ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) + defer cancel() + + err = nodes.WaitForProvisionState(ctx, client, node.UUID, nodes.Manageable) th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/blockstorage/extensions/extensions.go b/internal/acceptance/openstack/blockstorage/extensions/extensions.go index bc46f09f3f..4368b23a89 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/internal/acceptance/openstack/blockstorage/extensions/extensions.go @@ -8,6 +8,7 @@ import ( "fmt" "strings" "testing" + "time" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -41,7 +42,10 @@ func CreateUploadImage(t *testing.T, client *gophercloud.ServiceClient, volume * t.Logf("Uploading volume %s as volume-backed image %s", volume.ID, imageName) - if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { return volumeImage, err } @@ -87,7 +91,10 @@ func CreateVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume return err } - if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "in-use", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "in-use"); err != nil { return err } @@ -128,7 +135,10 @@ func DeleteVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } - if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) } @@ -165,7 +175,10 @@ func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *v return err } - if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { return err } @@ -241,8 +254,8 @@ func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID stri // WaitForBackupStatus will continually poll a backup, checking for a particular // status. It will do this for the amount of seconds defined. func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := backups.Get(context.TODO(), client, id).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := backups.Get(ctx, client, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "deleted" { return true, nil @@ -315,7 +328,10 @@ func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *v return err } - if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { return err } @@ -334,7 +350,10 @@ func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume * return err } - if err := volumes.WaitForStatus(context.TODO(), client, volume.ID, status, 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, status); err != nil { return err } @@ -370,7 +389,10 @@ func ReImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Vo return err } - err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") if err != nil { return err } diff --git a/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go index 3b77886402..f843af073c 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go @@ -6,6 +6,7 @@ package extensions import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -29,7 +30,10 @@ func TestSchedulerHints(t *testing.T) { volume1, err := volumes.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) - err = volumes.WaitForStatus(context.TODO(), client, volume1.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume1.ID, "available") th.AssertNoErr(t, err) defer volumes.Delete(context.TODO(), client, volume1.ID, volumes.DeleteOpts{}) @@ -53,7 +57,10 @@ func TestSchedulerHints(t *testing.T) { volume2, err := volumes.Create(context.TODO(), client, createOptsWithHints).Extract() th.AssertNoErr(t, err) - err = volumes.WaitForStatus(context.TODO(), client, volume2.ID, "available", 60) + ctx2, cancel2 := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel2() + + err = volumes.WaitForStatus(ctx2, client, volume2.ID, "available") th.AssertNoErr(t, err) err = volumes.Delete(context.TODO(), client, volume2.ID, volumes.DeleteOpts{}).ExtractErr() diff --git a/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go index 78ee6833dc..849d9791fe 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -6,6 +6,7 @@ package noauth import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -34,7 +35,10 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } - err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") if err != nil { return volume, err } @@ -68,7 +72,10 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo return volume, err } - err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") if err != nil { return volume, err } @@ -109,7 +116,10 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol return snapshot, err } - err = snapshots.WaitForStatus(context.TODO(), client, snapshot.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = snapshots.WaitForStatus(ctx, client, snapshot.ID, "available") if err != nil { return snapshot, err } @@ -127,8 +137,8 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s // Volumes can't be deleted until their snapshots have been, // so block up to 120 seconds for the snapshot to delete. - err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := snapshots.Get(ctx, client, snapshot.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/v1/blockstorage.go b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go index 98f60b5d70..d3347f0b96 100644 --- a/internal/acceptance/openstack/blockstorage/v1/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go @@ -6,6 +6,7 @@ package v1 import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -35,7 +36,10 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol return snapshot, err } - err = snapshots.WaitForStatus(context.TODO(), client, snapshot.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = snapshots.WaitForStatus(ctx, client, snapshot.ID, "available") if err != nil { return snapshot, err } @@ -65,7 +69,10 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } - err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") if err != nil { return volume, err } @@ -106,8 +113,8 @@ func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapsho // Volumes can't be deleted until their snapshots have been, // so block until the snapshot has been deleted. - err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := snapshots.Get(ctx, client, snapshot.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go index aa5677f1bd..a29c85a6b4 100644 --- a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -6,6 +6,7 @@ package v2 import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -33,7 +34,10 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol return snapshot, err } - err = snapshots.WaitForStatus(context.TODO(), client, snapshot.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = snapshots.WaitForStatus(ctx, client, snapshot.ID, "available") if err != nil { return snapshot, err } @@ -61,7 +65,10 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } - err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") if err != nil { return volume, err } @@ -98,7 +105,10 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo return volume, err } - err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") if err != nil { return volume, err } @@ -141,8 +151,8 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s // Volumes can't be deleted until their snapshots have been, // so block until the snapshot is deleted. - err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := snapshots.Get(ctx, client, snapshot.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go index b45be9a7f3..e669ade5dc 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -6,6 +6,7 @@ package v2 import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -89,7 +90,10 @@ func TestVolumesCascadeDelete(t *testing.T) { vol, err := CreateVolume(t, client) th.AssertNoErr(t, err) - err = volumes.WaitForStatus(context.TODO(), client, vol.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, vol.ID, "available") th.AssertNoErr(t, err) snapshot1, err := CreateSnapshot(t, client, vol) @@ -107,8 +111,8 @@ func TestVolumesCascadeDelete(t *testing.T) { } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { - err := tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(context.TODO(), client, sid).Extract() + err := tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := snapshots.Get(ctx, client, sid).Extract() if err != nil { return true, nil } @@ -118,8 +122,8 @@ func TestVolumesCascadeDelete(t *testing.T) { t.Logf("Successfully deleted snapshot: %s", sid) } - err = tools.WaitFor(func() (bool, error) { - _, err := volumes.Get(context.TODO(), client, vol.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := volumes.Get(ctx, client, vol.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 75686e05a1..79ae8b7c29 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -6,6 +6,7 @@ package v3 import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -34,7 +35,10 @@ func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *vol return snapshot, err } - err = snapshots.WaitForStatus(context.TODO(), client, snapshot.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = snapshots.WaitForStatus(ctx, client, snapshot.ID, "available") if err != nil { return snapshot, err } @@ -71,7 +75,10 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol return volume, err } - err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") if err != nil { return volume, err } @@ -111,7 +118,10 @@ func CreateVolumeWithType(t *testing.T, client *gophercloud.ServiceClient, vt *v return volume, err } - err = volumes.WaitForStatus(context.TODO(), client, volume.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") if err != nil { return volume, err } @@ -268,8 +278,8 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s // Volumes can't be deleted until their snapshots have been, // so block until the snapshoth as been deleted. - err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := snapshots.Get(ctx, client, snapshot.ID).Extract() if err != nil { return true, nil } @@ -299,8 +309,8 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum // VolumeTypes can't be deleted until their volumes have been, // so block until the volume is deleted. - err = tools.WaitFor(func() (bool, error) { - _, err := volumes.Get(context.TODO(), client, volume.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := volumes.Get(ctx, client, volume.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go index 4526f83746..2fb00482d5 100644 --- a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -186,8 +186,8 @@ func TestSnapshotsForceDelete(t *testing.T) { err = snapshots.ForceDelete(context.TODO(), client, snapshot.ID).ExtractErr() th.AssertNoErr(t, err) - err = tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := snapshots.Get(ctx, client, snapshot.ID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil diff --git a/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go index 536830efd4..4bd531a856 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumeattachments.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "testing" + "time" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/attachments" @@ -40,7 +41,10 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, vol return err } - if err = attachments.WaitForStatus(context.TODO(), client, attachment.ID, "attached", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err = attachments.WaitForStatus(ctx, client, attachment.ID, "attached"); err != nil { e := attachments.Delete(context.TODO(), client, attachment.ID).ExtractErr() if e != nil { t.Logf("Failed to delete %q attachment: %s", attachment.ID, err) @@ -85,7 +89,10 @@ func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, vol t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) } - if err := v3.WaitForStatus(context.TODO(), client, volume.ID, "available", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := v3.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) } diff --git a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go index b8609fd540..cb2da7ff97 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -6,6 +6,7 @@ package v3 import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -91,7 +92,10 @@ func TestVolumesMultiAttach(t *testing.T) { th.AssertNoErr(t, err) defer DeleteVolume(t, client, vol) - err = volumes.WaitForStatus(context.TODO(), client, vol.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, vol.ID, "available") th.AssertNoErr(t, err) th.AssertEquals(t, vol.Multiattach, true) @@ -106,7 +110,10 @@ func TestVolumesCascadeDelete(t *testing.T) { vol, err := CreateVolume(t, client) th.AssertNoErr(t, err) - err = volumes.WaitForStatus(context.TODO(), client, vol.ID, "available", 60) + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, vol.ID, "available") th.AssertNoErr(t, err) snapshot1, err := CreateSnapshot(t, client, vol) @@ -124,8 +131,8 @@ func TestVolumesCascadeDelete(t *testing.T) { } for _, sid := range []string{snapshot1.ID, snapshot2.ID} { - err := tools.WaitFor(func() (bool, error) { - _, err := snapshots.Get(context.TODO(), client, sid).Extract() + err := tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := snapshots.Get(ctx, client, sid).Extract() if err != nil { return true, nil } @@ -135,8 +142,8 @@ func TestVolumesCascadeDelete(t *testing.T) { t.Logf("Successfully deleted snapshot: %s", sid) } - err = tools.WaitFor(func() (bool, error) { - _, err := volumes.Get(context.TODO(), client, vol.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := volumes.Get(ctx, client, vol.ID).Extract() if err != nil { return true, nil } diff --git a/internal/acceptance/openstack/clustering/v1/clustering.go b/internal/acceptance/openstack/clustering/v1/clustering.go index 5a296ec08b..09ce437d63 100644 --- a/internal/acceptance/openstack/clustering/v1/clustering.go +++ b/internal/acceptance/openstack/clustering/v1/clustering.go @@ -431,8 +431,8 @@ func GetActionID(headers http.Header) (string, error) { } func WaitForAction(client *gophercloud.ServiceClient, actionID string) error { - return tools.WaitFor(func() (bool, error) { - action, err := actions.Get(context.TODO(), client, actionID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + action, err := actions.Get(ctx, client, actionID).Extract() if err != nil { return false, err } @@ -450,8 +450,8 @@ func WaitForAction(client *gophercloud.ServiceClient, actionID string) error { } func WaitForNodeStatus(client *gophercloud.ServiceClient, id string, status string) error { - return tools.WaitFor(func() (bool, error) { - latest, err := nodes.Get(context.TODO(), client, id).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + latest, err := nodes.Get(ctx, client, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETED" { return true, nil diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index 8210b4acf2..20567dcb52 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -8,6 +8,7 @@ import ( "crypto/rsa" "fmt" "testing" + "time" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -823,7 +824,10 @@ func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blo return volumeAttachment, err } - if err := volumes.WaitForStatus(context.TODO(), blockClient, volume.ID, "in-use", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, blockClient, volume.ID, "in-use"); err != nil { return volumeAttachment, err } @@ -956,7 +960,10 @@ func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blo t.Fatalf("Unable to detach volume: %v", err) } - if err := volumes.WaitForStatus(context.TODO(), blockClient, volumeAttachment.ID, "available", 60); err != nil { + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, blockClient, volumeAttachment.ID, "available"); err != nil { t.Fatalf("Unable to wait for volume: %v", err) } t.Logf("Deleted volume: %s", volumeAttachment.VolumeID) @@ -1127,8 +1134,8 @@ func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *serve // WaitForComputeStatus will poll an instance's status until it either matches // the specified status or the status becomes ERROR. func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error { - return tools.WaitFor(func() (bool, error) { - latest, err := servers.Get(context.TODO(), client, server.ID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + latest, err := servers.Get(ctx, client, server.ID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index ef58fe08c9..c9000b626f 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -156,8 +156,8 @@ func TestServersUpdate(t *testing.T) { th.AssertEquals(t, updated.ID, server.ID) - err = tools.WaitFor(func() (bool, error) { - latest, err := servers.Get(context.TODO(), client, updated.ID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + latest, err := servers.Get(ctx, client, updated.ID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/container/v1/capsules.go b/internal/acceptance/openstack/container/v1/capsules.go index f166400b5d..b4f48fc903 100644 --- a/internal/acceptance/openstack/container/v1/capsules.go +++ b/internal/acceptance/openstack/container/v1/capsules.go @@ -12,8 +12,8 @@ import ( // WaitForCapsuleStatus will poll a capsule's status until it either matches // the specified status or the status becomes Failed. func WaitForCapsuleStatus(client *gophercloud.ServiceClient, uuid, status string) error { - return tools.WaitFor(func() (bool, error) { - v, err := capsules.Get(context.TODO(), client, uuid).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + v, err := capsules.Get(ctx, client, uuid).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go index 8b36a47263..99933bd11f 100644 --- a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -185,8 +185,8 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { } func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status string, timeout time.Duration) error { - return tools.WaitForTimeout(func() (bool, error) { - cluster, err := clusters.Get(context.TODO(), client, clusterID).Extract() + return tools.WaitForTimeout(func(ctx context.Context) (bool, error) { + cluster, err := clusters.Get(ctx, client, clusterID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETE_COMPLETE" { return true, nil diff --git a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go index 9bb5ea15d0..ed17bb5685 100644 --- a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -49,8 +49,8 @@ func TestNodeGroupsCRUD(t *testing.T) { t.Logf("Created nodegroup: %s", nodeGroupID) // Wait for the node group to finish creating - err = tools.WaitForTimeout(func() (bool, error) { - ng, err := nodegroups.Get(context.TODO(), client, clusterID, nodeGroupID).Extract() + err = tools.WaitForTimeout(func(ctx context.Context) (bool, error) { + ng, err := nodegroups.Get(ctx, client, clusterID, nodeGroupID).Extract() if err != nil { return false, fmt.Errorf("error waiting for node group to create: %v", err) } @@ -164,8 +164,8 @@ func testNodeGroupDelete(t *testing.T, client *gophercloud.ServiceClient, cluste th.AssertNoErr(t, err) // Wait for the node group to be deleted - err = tools.WaitFor(func() (bool, error) { - _, err := nodegroups.Get(context.TODO(), client, clusterID, nodeGroupID).Extract() + err = tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := nodegroups.Get(ctx, client, clusterID, nodeGroupID).Extract() if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil } diff --git a/internal/acceptance/openstack/db/v1/db.go b/internal/acceptance/openstack/db/v1/db.go index 865b3fada0..abc92553a0 100644 --- a/internal/acceptance/openstack/db/v1/db.go +++ b/internal/acceptance/openstack/db/v1/db.go @@ -127,8 +127,8 @@ func DeleteUser(t *testing.T, client *gophercloud.ServiceClient, instanceID, nam // the specified status or the status becomes ERROR. func WaitForInstanceStatus( client *gophercloud.ServiceClient, instance *instances.Instance, status string) error { - return tools.WaitFor(func() (bool, error) { - latest, err := instances.Get(context.TODO(), client, instance.ID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + latest, err := instances.Get(ctx, client, instance.ID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/dns/v2/dns.go b/internal/acceptance/openstack/dns/v2/dns.go index 5e34637faf..c31f238a6e 100644 --- a/internal/acceptance/openstack/dns/v2/dns.go +++ b/internal/acceptance/openstack/dns/v2/dns.go @@ -213,8 +213,8 @@ func DeleteZone(t *testing.T, client *gophercloud.ServiceClient, zone *zones.Zon // WaitForRecordSetStatus will poll a record set's status until it either matches // the specified status or the status becomes ERROR. func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.RecordSet, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := recordsets.Get(context.TODO(), client, rs.ZoneID, rs.ID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := recordsets.Get(ctx, client, rs.ZoneID, rs.ID).Extract() if err != nil { return false, err } @@ -230,8 +230,8 @@ func WaitForRecordSetStatus(client *gophercloud.ServiceClient, rs *recordsets.Re // WaitForTransferRequestStatus will poll a transfer reqeust's status until it either matches // the specified status or the status becomes ERROR. func WaitForTransferRequestStatus(client *gophercloud.ServiceClient, tr *transferRequests.TransferRequest, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := transferRequests.Get(context.TODO(), client, tr.ID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := transferRequests.Get(ctx, client, tr.ID).Extract() if err != nil { return false, err } @@ -245,8 +245,8 @@ func WaitForTransferRequestStatus(client *gophercloud.ServiceClient, tr *transfe // WaitForTransferAcceptStatus will poll a transfer accept's status until it either matches // the specified status or the status becomes ERROR. func WaitForTransferAcceptStatus(client *gophercloud.ServiceClient, ta *transferAccepts.TransferAccept, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := transferAccepts.Get(context.TODO(), client, ta.ID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := transferAccepts.Get(ctx, client, ta.ID).Extract() if err != nil { return false, err } @@ -260,8 +260,8 @@ func WaitForTransferAcceptStatus(client *gophercloud.ServiceClient, ta *transfer // WaitForZoneStatus will poll a zone's status until it either matches // the specified status or the status becomes ERROR. func WaitForZoneStatus(client *gophercloud.ServiceClient, zone *zones.Zone, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := zones.Get(context.TODO(), client, zone.ID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := zones.Get(ctx, client, zone.ID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/keymanager/v1/keymanager.go b/internal/acceptance/openstack/keymanager/v1/keymanager.go index cb84db8cd1..a44b57a4bf 100644 --- a/internal/acceptance/openstack/keymanager/v1/keymanager.go +++ b/internal/acceptance/openstack/keymanager/v1/keymanager.go @@ -742,8 +742,8 @@ func CreateRSAKeyPair(t *testing.T, passphrase string) ([]byte, []byte, error) { } func WaitForOrder(client *gophercloud.ServiceClient, orderID string) error { - return tools.WaitFor(func() (bool, error) { - order, err := orders.Get(context.TODO(), client, orderID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + order, err := orders.Get(ctx, client, orderID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index a214f4626c..19206c2c3b 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -660,8 +660,8 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := loadbalancers.Get(context.TODO(), client, lbID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := loadbalancers.Get(ctx, client, lbID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { diff --git a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index c68facb7ac..bbd71e262e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -128,11 +128,11 @@ func TestBGPAgentRUD(t *testing.T) { // List the BGP Agents that accommodate the BGP Speaker err = tools.WaitForTimeout( - func() (bool, error) { + func(ctx context.Context) (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(context.TODO(), client, agt.ID).Extract() + bgpAgent, err := agents.Get(ctx, client, agt.ID).Extract() th.AssertNoErr(t, err) numOfSpeakers := int(bgpAgent.Configurations["bgp_speakers"].(float64)) flag = flag && (numOfSpeakers == 1) @@ -146,8 +146,8 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) t.Logf("BGP Speaker %s has been removed from agent %s", bgpSpeaker.ID, bgpAgents[0].ID) err = tools.WaitForTimeout( - func() (bool, error) { - bgpAgent, err := agents.Get(context.TODO(), client, bgpAgents[0].ID).Extract() + func(ctx context.Context) (bool, error) { + bgpAgent, err := agents.Get(ctx, client, bgpAgents[0].ID).Extract() th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) @@ -174,8 +174,8 @@ func TestBGPAgentRUD(t *testing.T) { t.Logf("Successfully scheduled speaker %s to agent %s", bgpSpeaker.ID, bgpAgents[0].ID) err = tools.WaitForTimeout( - func() (bool, error) { - bgpAgent, err := agents.Get(context.TODO(), client, bgpAgents[0].ID).Extract() + func(ctx context.Context) (bool, error) { + bgpAgent, err := agents.Get(ctx, client, bgpAgents[0].ID).Extract() th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) @@ -189,8 +189,8 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) t.Logf("Successfully deleted the BGP Speaker, %s", bgpSpeaker.ID) err = tools.WaitForTimeout( - func() (bool, error) { - bgpAgent, err := agents.Get(context.TODO(), client, bgpAgents[0].ID).Extract() + func(ctx context.Context) (bool, error) { + bgpAgent, err := agents.Get(ctx, client, bgpAgents[0].ID).Extract() th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go index 616eef4288..8dec1439a9 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go @@ -206,8 +206,8 @@ func DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) // WaitForFirewallState will wait until a firewall reaches a given state. func WaitForFirewallState(client *gophercloud.ServiceClient, firewallID, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := firewalls.Get(context.TODO(), client, firewallID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := firewalls.Get(ctx, client, firewallID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 54f93a3e2f..b9d5eecbe3 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -281,8 +281,8 @@ func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingI } func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string) error { - return tools.WaitFor(func() (bool, error) { - r, err := routers.Get(context.TODO(), client, routerID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + r, err := routers.Get(ctx, client, routerID).Extract() if err != nil { return false, err } @@ -296,8 +296,8 @@ func WaitForRouterToCreate(client *gophercloud.ServiceClient, routerID string) e } func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string) error { - return tools.WaitFor(func() (bool, error) { - _, err := routers.Get(context.TODO(), client, routerID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + _, err := routers.Get(ctx, client, routerID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil @@ -311,8 +311,8 @@ func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string) e } func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInterfaceID string) error { - return tools.WaitFor(func() (bool, error) { - r, err := ports.Get(context.TODO(), client, routerInterfaceID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + r, err := ports.Get(ctx, client, routerInterfaceID).Extract() if err != nil { return false, err } @@ -326,8 +326,8 @@ func WaitForRouterInterfaceToAttach(client *gophercloud.ServiceClient, routerInt } func WaitForRouterInterfaceToDetach(client *gophercloud.ServiceClient, routerInterfaceID string) error { - return tools.WaitFor(func() (bool, error) { - r, err := ports.Get(context.TODO(), client, routerInterfaceID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + r, err := ports.Get(ctx, client, routerInterfaceID).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { return true, nil diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go index 0e3c6a5003..f3398612ac 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go @@ -401,8 +401,8 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error { - return tools.WaitFor(func() (bool, error) { - current, err := loadbalancers.Get(context.TODO(), client, lbID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := loadbalancers.Get(ctx, client, lbID).Extract() if err != nil { if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { if httpStatus.Actual == 404 { diff --git a/internal/acceptance/openstack/networking/v2/networking.go b/internal/acceptance/openstack/networking/v2/networking.go index 34057cbeb8..6b1d2ef2b7 100644 --- a/internal/acceptance/openstack/networking/v2/networking.go +++ b/internal/acceptance/openstack/networking/v2/networking.go @@ -561,8 +561,8 @@ func DeleteSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID stri } func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string) error { - return tools.WaitFor(func() (bool, error) { - p, err := ports.Get(context.TODO(), client, portID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + p, err := ports.Get(ctx, client, portID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/orchestration/v1/orchestration.go b/internal/acceptance/openstack/orchestration/v1/orchestration.go index f7448732d7..3d45311fef 100644 --- a/internal/acceptance/openstack/orchestration/v1/orchestration.go +++ b/internal/acceptance/openstack/orchestration/v1/orchestration.go @@ -97,8 +97,8 @@ func DeleteStack(t *testing.T, client *gophercloud.ServiceClient, stackName, sta // WaitForStackStatus will wait until a stack has reached a certain status. func WaitForStackStatus(client *gophercloud.ServiceClient, stackName, stackID, status string) error { - return tools.WaitFor(func() (bool, error) { - latest, err := stacks.Get(context.TODO(), client, stackName, stackID).Extract() + return tools.WaitFor(func(ctx context.Context) (bool, error) { + latest, err := stacks.Get(ctx, client, stackName, stackID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go index 98de841523..1597546187 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go @@ -73,10 +73,10 @@ func ListShareReplicas(t *testing.T, client *gophercloud.ServiceClient, shareID func waitForReplicaStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) (*replicas.Replica, error) { var current *replicas.Replica - err := tools.WaitFor(func() (bool, error) { + err := tools.WaitFor(func(ctx context.Context) (bool, error) { var err error - current, err = replicas.Get(context.TODO(), c, id).Extract() + current, err = replicas.Get(ctx, c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { @@ -113,10 +113,10 @@ func waitForReplicaStatus(t *testing.T, c *gophercloud.ServiceClient, id, status func waitForReplicaState(t *testing.T, c *gophercloud.ServiceClient, id, state string) (*replicas.Replica, error) { var current *replicas.Replica - err := tools.WaitFor(func() (bool, error) { + err := tools.WaitFor(func(ctx context.Context) (bool, error) { var err error - current, err = replicas.Get(context.TODO(), c, id).Extract() + current, err = replicas.Get(ctx, c, id).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go index 3fdc55cc7a..6981790fc7 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go @@ -40,7 +40,7 @@ func WaitForShareAccessRule(t *testing.T, client *gophercloud.ServiceClient, acc return nil } - return tools.WaitFor(func() (bool, error) { + return tools.WaitFor(func(context.Context) (bool, error) { latest, err := ShareAccessRuleGet(t, client, accessRule.ID) if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go index 9f80e1c36b..7705f3c830 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -137,10 +137,10 @@ func PrintMessages(t *testing.T, c *gophercloud.ServiceClient, id 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) { + err := tools.WaitFor(func(ctx context.Context) (bool, error) { var err error - current, err = shares.Get(context.TODO(), c, id).Extract() + current, err = shares.Get(ctx, c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go index 67eebd7231..9269be378b 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go @@ -70,8 +70,8 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s } func waitForSnapshotStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) error { - err := tools.WaitFor(func() (bool, error) { - current, err := snapshots.Get(context.TODO(), c, id).Extract() + err := tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := snapshots.Get(ctx, c, id).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { switch status { diff --git a/internal/acceptance/openstack/workflow/v2/execution.go b/internal/acceptance/openstack/workflow/v2/execution.go index 398a278477..730dbefb45 100644 --- a/internal/acceptance/openstack/workflow/v2/execution.go +++ b/internal/acceptance/openstack/workflow/v2/execution.go @@ -36,8 +36,8 @@ func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow * th.AssertEquals(t, execution.Description, executionDescription) t.Logf("Wait for execution status SUCCESS: %s", executionDescription) - th.AssertNoErr(t, tools.WaitFor(func() (bool, error) { - latest, err := executions.Get(context.TODO(), client, execution.ID).Extract() + th.AssertNoErr(t, tools.WaitFor(func(ctx context.Context) (bool, error) { + latest, err := executions.Get(ctx, client, execution.ID).Extract() if err != nil { return false, err } diff --git a/internal/acceptance/tools/tools.go b/internal/acceptance/tools/tools.go index 7a5f2f7690..428d4e6e0e 100644 --- a/internal/acceptance/tools/tools.go +++ b/internal/acceptance/tools/tools.go @@ -1,39 +1,29 @@ package tools import ( + "context" "encoding/json" - "errors" "math/rand" "strings" "testing" "time" -) -// ErrTimeout is returned if WaitFor/WaitForTimeout take longer than their timeout duration. -var ErrTimeout = errors.New("Timed out") + "github.com/gophercloud/gophercloud/v2" +) // WaitFor uses WaitForTimeout to poll a predicate function once per second to -// wait for a certain state to arrive, with a default timeout of 300 seconds. -func WaitFor(predicate func() (bool, error)) error { +// wait for a certain state to arrive, with a default timeout of 600 seconds. +func WaitFor(predicate func(context.Context) (bool, error)) error { return WaitForTimeout(predicate, 600*time.Second) } // WaitForTimeout polls a predicate function once per second to wait for a // certain state to arrive, or until the given timeout is reached. -func WaitForTimeout(predicate func() (bool, error), timeout time.Duration) error { - startTime := time.Now() - for time.Since(startTime) < timeout { - time.Sleep(2 * time.Second) +func WaitForTimeout(predicate func(context.Context) (bool, error), timeout time.Duration) error { + ctx, cancel := context.WithTimeout(context.TODO(), timeout) + defer cancel() - satisfied, err := predicate() - if err != nil { - return err - } - if satisfied { - return nil - } - } - return ErrTimeout + return gophercloud.WaitFor(ctx, predicate) } // MakeNewPassword generates a new string that's guaranteed to be different than the given one. diff --git a/openstack/baremetal/v1/nodes/util.go b/openstack/baremetal/v1/nodes/util.go index 86e43256c1..c729be1955 100644 --- a/openstack/baremetal/v1/nodes/util.go +++ b/openstack/baremetal/v1/nodes/util.go @@ -9,8 +9,8 @@ import ( // WaitForProvisionState will continually poll a node until it successfully // transitions to a specified state. It will do this for at most the number // of seconds specified. -func WaitForProvisionState(ctx context.Context, c *gophercloud.ServiceClient, id string, state ProvisionState, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +func WaitForProvisionState(ctx context.Context, c *gophercloud.ServiceClient, id string, state ProvisionState) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/openstack/blockstorage/v1/snapshots/util.go b/openstack/blockstorage/v1/snapshots/util.go index a484fc3186..df10c97c2e 100644 --- a/openstack/blockstorage/v1/snapshots/util.go +++ b/openstack/blockstorage/v1/snapshots/util.go @@ -6,10 +6,9 @@ import ( "github.com/gophercloud/gophercloud/v2" ) -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +// WaitForStatus will continually poll the resource, checking for a particular status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/openstack/blockstorage/v1/volumes/util.go b/openstack/blockstorage/v1/volumes/util.go index d6a76e6c80..6f8d899f56 100644 --- a/openstack/blockstorage/v1/volumes/util.go +++ b/openstack/blockstorage/v1/volumes/util.go @@ -6,10 +6,9 @@ import ( "github.com/gophercloud/gophercloud/v2" ) -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +// WaitForStatus will continually poll the resource, checking for a particular status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/openstack/blockstorage/v2/snapshots/util.go b/openstack/blockstorage/v2/snapshots/util.go index a484fc3186..df10c97c2e 100644 --- a/openstack/blockstorage/v2/snapshots/util.go +++ b/openstack/blockstorage/v2/snapshots/util.go @@ -6,10 +6,9 @@ import ( "github.com/gophercloud/gophercloud/v2" ) -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +// WaitForStatus will continually poll the resource, checking for a particular status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/openstack/blockstorage/v2/volumes/util.go b/openstack/blockstorage/v2/volumes/util.go index d6a76e6c80..6f8d899f56 100644 --- a/openstack/blockstorage/v2/volumes/util.go +++ b/openstack/blockstorage/v2/volumes/util.go @@ -6,10 +6,9 @@ import ( "github.com/gophercloud/gophercloud/v2" ) -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +// WaitForStatus will continually poll the resource, checking for a particular status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/openstack/blockstorage/v3/attachments/util.go b/openstack/blockstorage/v3/attachments/util.go index 12409d626d..5eb6c8e39d 100644 --- a/openstack/blockstorage/v3/attachments/util.go +++ b/openstack/blockstorage/v3/attachments/util.go @@ -6,10 +6,9 @@ import ( "github.com/gophercloud/gophercloud/v2" ) -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +// WaitForStatus will continually poll the resource, checking for a particular status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/openstack/blockstorage/v3/snapshots/util.go b/openstack/blockstorage/v3/snapshots/util.go index a484fc3186..df10c97c2e 100644 --- a/openstack/blockstorage/v3/snapshots/util.go +++ b/openstack/blockstorage/v3/snapshots/util.go @@ -6,10 +6,9 @@ import ( "github.com/gophercloud/gophercloud/v2" ) -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +// WaitForStatus will continually poll the resource, checking for a particular status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/openstack/blockstorage/v3/volumes/util.go b/openstack/blockstorage/v3/volumes/util.go index d6a76e6c80..6f8d899f56 100644 --- a/openstack/blockstorage/v3/volumes/util.go +++ b/openstack/blockstorage/v3/volumes/util.go @@ -6,10 +6,9 @@ import ( "github.com/gophercloud/gophercloud/v2" ) -// WaitForStatus will continually poll the resource, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +// WaitForStatus will continually poll the resource, checking for a particular status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/openstack/compute/v2/servers/util.go b/openstack/compute/v2/servers/util.go index 37537d178f..4f611750d6 100644 --- a/openstack/compute/v2/servers/util.go +++ b/openstack/compute/v2/servers/util.go @@ -7,10 +7,9 @@ import ( ) // WaitForStatus will continually poll a server until it successfully -// transitions to a specified status. It will do this for at most the number -// of seconds specified. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string, secs int) error { - return gophercloud.WaitFor(secs, func() (bool, error) { +// transitions to a specified status. +func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { + return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { current, err := Get(ctx, c, id).Extract() if err != nil { return false, err diff --git a/testhelper/convenience.go b/testhelper/convenience.go index a767684d2d..5760b91960 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -349,6 +349,20 @@ func AssertErr(t *testing.T, e error) { } } +// AssertErrIs is a convenience function for checking whether an error value is +// target one +func AssertErrIs(t *testing.T, e error, target error) { + t.Helper() + + if e == nil { + logFatal(t, "expected error, got nil") + } + + if !errors.Is(e, target) { + logFatal(t, fmt.Sprintf("expected error %v, got %v", target, e)) + } +} + // CheckNoErr is similar to AssertNoErr, except with a non-fatal error func CheckNoErr(t *testing.T, e error) { if e != nil { diff --git a/testing/util_test.go b/testing/util_test.go index 34a88e0b3d..8440d9ed33 100644 --- a/testing/util_test.go +++ b/testing/util_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "errors" "os" "path/filepath" @@ -14,7 +15,10 @@ import ( ) func TestWaitFor(t *testing.T) { - err := gophercloud.WaitFor(2, func() (bool, error) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + err := gophercloud.WaitFor(ctx, func(context.Context) (bool, error) { return true, nil }) th.CheckNoErr(t, err) @@ -25,10 +29,13 @@ func TestWaitForTimeout(t *testing.T) { t.Skip("skipping test in short mode.") } - err := gophercloud.WaitFor(1, func() (bool, error) { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + err := gophercloud.WaitFor(ctx, func(context.Context) (bool, error) { return false, nil }) - th.AssertEquals(t, "A timeout occurred", err.Error()) + th.AssertErrIs(t, err, context.DeadlineExceeded) } func TestWaitForError(t *testing.T) { @@ -36,7 +43,10 @@ func TestWaitForError(t *testing.T) { t.Skip("skipping test in short mode.") } - err := gophercloud.WaitFor(2, func() (bool, error) { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + err := gophercloud.WaitFor(ctx, func(context.Context) (bool, error) { return false, errors.New("Error has occurred") }) th.AssertEquals(t, "Error has occurred", err.Error()) @@ -47,11 +57,20 @@ func TestWaitForPredicateExceed(t *testing.T) { t.Skip("skipping test in short mode.") } - err := gophercloud.WaitFor(1, func() (bool, error) { - time.Sleep(4 * time.Second) - return false, errors.New("Just wasting time") + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + err := gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { + // NOTE: predicate should obey context cancellation + select { + case <-ctx.Done(): + return true, ctx.Err() + + case <-time.After(4 * time.Second): + return false, errors.New("Just wasting time") + } }) - th.AssertEquals(t, "A timeout occurred", err.Error()) + th.AssertErrIs(t, err, context.DeadlineExceeded) } func TestNormalizeURL(t *testing.T) { @@ -66,7 +85,6 @@ func TestNormalizeURL(t *testing.T) { for i := 0; i < len(expected); i++ { th.CheckEquals(t, expected[i], gophercloud.NormalizeURL(urls[i])) } - } func TestNormalizePathURL(t *testing.T) { diff --git a/util.go b/util.go index 2740c301e5..65748d00b6 100644 --- a/util.go +++ b/util.go @@ -1,7 +1,7 @@ package gophercloud import ( - "fmt" + "context" "net/url" "path/filepath" "reflect" @@ -83,48 +83,27 @@ func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]i return } -// WaitFor polls a predicate function, once per second, up to a timeout limit. +// WaitFor polls a predicate function, once per second, up to a context cancellation. // This is useful to wait for a resource to transition to a certain state. -// To handle situations when the predicate might hang indefinitely, the -// predicate will be prematurely cancelled after the timeout. // Resource packages will wrap this in a more convenient function that's // specific to a certain resource, but it can also be useful on its own. -func WaitFor(timeout int, predicate func() (bool, error)) error { - type WaitForResult struct { - Success bool - Error error +func WaitFor(ctx context.Context, predicate func(context.Context) (bool, error)) error { + if done, err := predicate(ctx); done || err != nil { + return err } - start := time.Now().Unix() + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() for { - // If a timeout is set, and that's been exceeded, shut it down. - if timeout >= 0 && time.Now().Unix()-start >= int64(timeout) { - return fmt.Errorf("A timeout occurred") - } - - time.Sleep(1 * time.Second) - - var result WaitForResult - ch := make(chan bool, 1) - go func() { - defer close(ch) - satisfied, err := predicate() - result.Success = satisfied - result.Error = err - }() - select { - case <-ch: - if result.Error != nil { - return result.Error - } - if result.Success { - return nil + case <-ticker.C: + if done, err := predicate(ctx); done || err != nil { + return err } - // If the predicate has not finished by the timeout, cancel it. - case <-time.After(time.Duration(timeout) * time.Second): - return fmt.Errorf("A timeout occurred") + + case <-ctx.Done(): + return ctx.Err() } } } From 7b093f068da5e665c756757487602e272ffa4ae8 Mon Sep 17 00:00:00 2001 From: Vladimir Ermakov Date: Mon, 18 Mar 2024 11:23:04 +0100 Subject: [PATCH 1788/2296] testhelper: mark all helpers with t.Helper Fixes #2996 Signed-off-by: Vladimir Ermakov --- testhelper/convenience.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/testhelper/convenience.go b/testhelper/convenience.go index a767684d2d..56a3c8f96f 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -33,10 +33,12 @@ func yellow(str interface{}) string { } func logFatal(t *testing.T, str string) { + t.Helper() t.Fatalf(logBodyFmt, prefix(3), str) } func logError(t *testing.T, str string) { + t.Helper() t.Errorf(logBodyFmt, prefix(3), str) } @@ -214,6 +216,8 @@ func deepDiff(expected, actual interface{}, logDifference diffLogger) { // AssertEquals compares two arbitrary values and performs a comparison. If the // comparison fails, a fatal error is raised that will fail the test func AssertEquals(t *testing.T, expected, actual interface{}) { + t.Helper() + if expected != actual { logFatal(t, fmt.Sprintf("expected %s but got %s", green(expected), yellow(actual))) } @@ -221,6 +225,8 @@ func AssertEquals(t *testing.T, expected, actual interface{}) { // CheckEquals is similar to AssertEquals, except with a non-fatal error func CheckEquals(t *testing.T, expected, actual interface{}) { + t.Helper() + if expected != actual { logError(t, fmt.Sprintf("expected %s but got %s", green(expected), yellow(actual))) } @@ -229,6 +235,8 @@ func CheckEquals(t *testing.T, expected, actual interface{}) { // AssertDeepEquals - like Equals - performs a comparison - but on more complex // structures that requires deeper inspection func AssertDeepEquals(t *testing.T, expected, actual interface{}) { + t.Helper() + pre := prefix(2) differed := false @@ -247,6 +255,8 @@ func AssertDeepEquals(t *testing.T, expected, actual interface{}) { // CheckDeepEquals is similar to AssertDeepEquals, except with a non-fatal error func CheckDeepEquals(t *testing.T, expected, actual interface{}) { + t.Helper() + pre := prefix(2) deepDiff(expected, actual, func(path []string, expected, actual interface{}) { @@ -264,6 +274,8 @@ func isByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) b // AssertByteArrayEquals a convenience function for checking whether two byte arrays are equal func AssertByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) { + t.Helper() + if !isByteArrayEquals(t, expectedBytes, actualBytes) { logFatal(t, "The bytes differed.") } @@ -271,6 +283,8 @@ func AssertByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byt // CheckByteArrayEquals a convenience function for silent checking whether two byte arrays are equal func CheckByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) { + t.Helper() + if !isByteArrayEquals(t, expectedBytes, actualBytes) { logError(t, "The bytes differed.") } @@ -321,6 +335,8 @@ func isJSONEquals(t *testing.T, expectedJSON string, actual interface{}) bool { // This is useful for comparing structures that are built as nested map[string]interface{} values, // which are a pain to construct as literals. func AssertJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { + t.Helper() + if !isJSONEquals(t, expectedJSON, actual) { logFatal(t, "The generated JSON structure differed.") } @@ -328,6 +344,8 @@ func AssertJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { // CheckJSONEquals is similar to AssertJSONEquals, but nonfatal. func CheckJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { + t.Helper() + if !isJSONEquals(t, expectedJSON, actual) { logError(t, "The generated JSON structure differed.") } @@ -336,6 +354,8 @@ func CheckJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { // AssertNoErr is a convenience function for checking whether an error value is // an actual error func AssertNoErr(t *testing.T, e error) { + t.Helper() + if e != nil { logFatal(t, fmt.Sprintf("unexpected error %s", yellow(e.Error()))) } @@ -344,6 +364,8 @@ func AssertNoErr(t *testing.T, e error) { // AssertErr is a convenience function for checking whether an error value is // nil func AssertErr(t *testing.T, e error) { + t.Helper() + if e == nil { logFatal(t, "expected error, got nil") } @@ -351,6 +373,8 @@ func AssertErr(t *testing.T, e error) { // CheckNoErr is similar to AssertNoErr, except with a non-fatal error func CheckNoErr(t *testing.T, e error) { + t.Helper() + if e != nil { logError(t, fmt.Sprintf("unexpected error %s", yellow(e.Error()))) } @@ -364,6 +388,8 @@ func CheckNoErr(t *testing.T, e error) { // 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{}) { + t.Helper() + if e == nil { logError(t, "expected error, got nil") return @@ -381,6 +407,8 @@ func CheckErr(t *testing.T, e error, expected ...interface{}) { // AssertIntLesserOrEqual verifies that first value is lesser or equal than second values func AssertIntLesserOrEqual(t *testing.T, v1 int, v2 int) { + t.Helper() + if !(v1 <= v2) { logFatal(t, fmt.Sprintf("The first value \"%v\" is greater than the second value \"%v\"", v1, v2)) } @@ -388,6 +416,8 @@ func AssertIntLesserOrEqual(t *testing.T, v1 int, v2 int) { // AssertIntGreaterOrEqual verifies that first value is greater or equal than second values func AssertIntGreaterOrEqual(t *testing.T, v1 int, v2 int) { + t.Helper() + if !(v1 >= v2) { logFatal(t, fmt.Sprintf("The first value \"%v\" is lesser than the second value \"%v\"", v1, v2)) } From 45dd4a35ec693697769afbb01f8a203ff9013d10 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 19 Mar 2024 15:28:51 +0000 Subject: [PATCH 1789/2296] tests: Fix lint issues We have a number of tests that we do not appear to be running in CI. In addition, we are not running go vet with all suitable build tags resulting in some tests being skipped [1]. Combined, this has resulted in a number of tests that have not been fully updated to use context. While we will address the root cause elsewhere, we can fix the missed updates here. You can validate this fix by overriding GOFLAGS, as outlined in [1]. For example, from the root directory: GOFLAGS="-tags=acceptance" go vet ./... [1] https://github.com/golang/go/issues/29202 Signed-off-by: Stephen Finucane --- .../openstack/container/v1/capsules_test.go | 12 +++++------ .../openstack/db/v1/configurations_test.go | 11 +++++----- .../openstack/db/v1/flavors_test.go | 2 +- .../openstack/db/v1/instances_test.go | 6 +++--- .../openstack/dns/v2/recordsets_test.go | 21 +++++++++---------- .../openstack/dns/v2/transfers_test.go | 2 +- .../acceptance/openstack/dns/v2/zones_test.go | 2 +- .../v2/extensions/fwaas/firewall_test.go | 8 +++---- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/internal/acceptance/openstack/container/v1/capsules_test.go b/internal/acceptance/openstack/container/v1/capsules_test.go index e61e3a36ae..865ec9e3e7 100644 --- a/internal/acceptance/openstack/container/v1/capsules_test.go +++ b/internal/acceptance/openstack/container/v1/capsules_test.go @@ -32,7 +32,7 @@ func TestCapsuleBase(t *testing.T) { TemplateOpts: template, } - v, err := capsules.Create(client, createOpts).Extract() + v, err := capsules.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) capsule := v.(*capsules.Capsule) @@ -50,12 +50,12 @@ func TestCapsuleBase(t *testing.T) { if capsuleUUID != capsule.UUID { continue } - capsule, err := capsules.Get(client, capsuleUUID).ExtractBase() + capsule, err := capsules.Get(context.TODO(), client, capsuleUUID).ExtractBase() th.AssertNoErr(t, err) th.AssertEquals(t, capsule.MetaName, "template") - err = capsules.Delete(client, capsuleUUID).ExtractErr() + err = capsules.Delete(context.TODO(), client, capsuleUUID).ExtractErr() th.AssertNoErr(t, err) } @@ -87,7 +87,7 @@ func TestCapsuleV132(t *testing.T) { TemplateOpts: template, } - capsule, err := capsules.Create(client, createOpts).ExtractV132() + capsule, err := capsules.Create(context.TODO(), client, createOpts).ExtractV132() th.AssertNoErr(t, err) err = WaitForCapsuleStatus(client, capsule.UUID, "Running") @@ -103,12 +103,12 @@ func TestCapsuleV132(t *testing.T) { if capsuleUUID != capsule.UUID { continue } - capsule, err := capsules.Get(client, capsuleUUID).ExtractV132() + capsule, err := capsules.Get(context.TODO(), client, capsuleUUID).ExtractV132() th.AssertNoErr(t, err) th.AssertEquals(t, capsule.MetaName, "template") - err = capsules.Delete(client, capsuleUUID).ExtractErr() + err = capsules.Delete(context.TODO(), client, capsuleUUID).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/db/v1/configurations_test.go b/internal/acceptance/openstack/db/v1/configurations_test.go index 05b47401d9..3ee9a3661a 100644 --- a/internal/acceptance/openstack/db/v1/configurations_test.go +++ b/internal/acceptance/openstack/db/v1/configurations_test.go @@ -4,6 +4,7 @@ package v1 import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -38,12 +39,12 @@ func TestConfigurationsCRUD(t *testing.T) { values["collation_server"] = "latin1_swedish_ci" createOpts.Values = values - cgroup, err := configurations.Create(client, createOpts).Extract() + cgroup, err := configurations.Create(context.TODO(), client, createOpts).Extract() if err != nil { t.Fatalf("Unable to create configuration: %v", err) } - readCgroup, err := configurations.Get(client, cgroup.ID).Extract() + readCgroup, err := configurations.Get(context.TODO(), client, cgroup.ID).Extract() if err != nil { t.Fatalf("Unable to read configuration: %v", err) } @@ -61,10 +62,10 @@ func TestConfigurationsCRUD(t *testing.T) { Name: newCgroupName, Description: &newCgroupDescription, } - err = configurations.Update(client, cgroup.ID, updateOpts).ExtractErr() + err = configurations.Update(context.TODO(), client, cgroup.ID, updateOpts).ExtractErr() th.AssertNoErr(t, err) - newCgroup, err := configurations.Get(client, cgroup.ID).Extract() + newCgroup, err := configurations.Get(context.TODO(), client, cgroup.ID).Extract() if err != nil { t.Fatalf("Unable to read updated configuration: %v", err) } @@ -73,7 +74,7 @@ func TestConfigurationsCRUD(t *testing.T) { th.AssertEquals(t, newCgroup.Name, newCgroupName) th.AssertEquals(t, newCgroup.Description, newCgroupDescription) - err = configurations.Delete(client, cgroup.ID).ExtractErr() + err = configurations.Delete(context.TODO(), client, cgroup.ID).ExtractErr() if err != nil { t.Fatalf("Unable to delete configuration: %v", err) } diff --git a/internal/acceptance/openstack/db/v1/flavors_test.go b/internal/acceptance/openstack/db/v1/flavors_test.go index 7268d5ebd4..09ed3c5415 100644 --- a/internal/acceptance/openstack/db/v1/flavors_test.go +++ b/internal/acceptance/openstack/db/v1/flavors_test.go @@ -50,7 +50,7 @@ func TestFlavorsGet(t *testing.T) { } if len(allFlavors) > 0 { - flavor, err := flavors.Get(client, allFlavors[0].StrID).Extract() + flavor, err := flavors.Get(context.TODO(), client, allFlavors[0].StrID).Extract() if err != nil { t.Fatalf("Unable to get flavor: %v", err) } diff --git a/internal/acceptance/openstack/db/v1/instances_test.go b/internal/acceptance/openstack/db/v1/instances_test.go index 3098b23241..a46b476849 100644 --- a/internal/acceptance/openstack/db/v1/instances_test.go +++ b/internal/acceptance/openstack/db/v1/instances_test.go @@ -48,12 +48,12 @@ func TestInstances(t *testing.T) { } // Enable root user. - _, err = instances.EnableRootUser(client, instance.ID).Extract() + _, err = instances.EnableRootUser(context.TODO(), client, instance.ID).Extract() if err != nil { t.Fatalf("Unable to enable root user: %v", err) } - enabled, err := instances.IsRootEnabled(client, instance.ID).Extract() + enabled, err := instances.IsRootEnabled(context.TODO(), client, instance.ID).Extract() if err != nil { t.Fatalf("Unable to check if root user is enabled: %v", err) } @@ -61,7 +61,7 @@ func TestInstances(t *testing.T) { t.Logf("Root user is enabled: %t", enabled) // Restart - err = instances.Restart(client, instance.ID).ExtractErr() + err = instances.Restart(context.TODO(), client, instance.ID).ExtractErr() if err != nil { t.Fatalf("Unable to restart instance: %v", err) } diff --git a/internal/acceptance/openstack/dns/v2/recordsets_test.go b/internal/acceptance/openstack/dns/v2/recordsets_test.go index e97719f3c3..98f8020dc8 100644 --- a/internal/acceptance/openstack/dns/v2/recordsets_test.go +++ b/internal/acceptance/openstack/dns/v2/recordsets_test.go @@ -43,14 +43,13 @@ func TestRecordSetsListByZone(t *testing.T) { Limit: 1, } - err = recordsets.ListByZone(client, zone.ID, listOpts).EachPage( - func(page pagination.Page) (bool, error) { - rr, err := recordsets.ExtractRecordSets(page) - th.AssertNoErr(t, err) - th.AssertEquals(t, len(rr), 1) - return true, nil - }, - ) + pager := recordsets.ListByZone(client, zone.ID, listOpts) + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + rr, err := recordsets.ExtractRecordSets(page) + th.AssertNoErr(t, err) + th.AssertEquals(t, len(rr), 1) + return true, nil + }) th.AssertNoErr(t, err) } @@ -75,7 +74,7 @@ func TestRecordSetsCRUD(t *testing.T) { Description: &description, } - newRS, err := recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() + newRS, err := recordsets.Update(context.TODO(), client, rs.ZoneID, rs.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newRS) @@ -87,7 +86,7 @@ func TestRecordSetsCRUD(t *testing.T) { Records: records, } - newRS, err = recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() + newRS, err = recordsets.Update(context.TODO(), client, rs.ZoneID, rs.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newRS) @@ -100,7 +99,7 @@ func TestRecordSetsCRUD(t *testing.T) { TTL: &ttl, } - newRS, err = recordsets.Update(client, rs.ZoneID, rs.ID, updateOpts).Extract() + newRS, err = recordsets.Update(context.TODO(), client, rs.ZoneID, rs.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newRS) diff --git a/internal/acceptance/openstack/dns/v2/transfers_test.go b/internal/acceptance/openstack/dns/v2/transfers_test.go index 0930355aa6..1e30dd1dd4 100644 --- a/internal/acceptance/openstack/dns/v2/transfers_test.go +++ b/internal/acceptance/openstack/dns/v2/transfers_test.go @@ -49,7 +49,7 @@ func TestTransferRequestCRUD(t *testing.T) { Description: description, } - newTransferRequest, err := transferRequests.Update(client, transferRequest.ID, updateOpts).Extract() + newTransferRequest, err := transferRequests.Update(context.TODO(), client, transferRequest.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newTransferRequest) diff --git a/internal/acceptance/openstack/dns/v2/zones_test.go b/internal/acceptance/openstack/dns/v2/zones_test.go index c8b1d09864..d252541f65 100644 --- a/internal/acceptance/openstack/dns/v2/zones_test.go +++ b/internal/acceptance/openstack/dns/v2/zones_test.go @@ -46,7 +46,7 @@ func TestZonesCRUD(t *testing.T) { TTL: 0, } - newZone, err := zones.Update(client, zone.ID, updateOpts).Extract() + newZone, err := zones.Update(context.TODO(), client, zone.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, &newZone) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go index 69706ddef3..aa48631505 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go @@ -115,8 +115,8 @@ func TestFirewallCRUDRouter(t *testing.T) { } updateOpts := routerinsertion.UpdateOptsExt{ - firewallUpdateOpts, - []string{router2.ID}, + UpdateOptsBuilder: firewallUpdateOpts, + RouterIDs: []string{router2.ID}, } _, err = firewalls.Update(context.TODO(), client, firewall.ID, updateOpts).Extract() @@ -162,8 +162,8 @@ func TestFirewallCRUDRemoveRouter(t *testing.T) { } updateOpts := routerinsertion.UpdateOptsExt{ - firewallUpdateOpts, - []string{}, + UpdateOptsBuilder: firewallUpdateOpts, + RouterIDs: []string{}, } _, err = firewalls.Update(context.TODO(), client, firewall.ID, updateOpts).Extract() From 3bc86f7e056376fb4943b10d6f729b8788ef0dc7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 15:35:49 +0000 Subject: [PATCH 1790/2296] networking: Remove fwaas v1 This is ancient and has long-since been replaced by fwaas v2. We said we'd remove it in the future. Do so now. Signed-off-by: Stephen Finucane --- .../v2/extensions/fwaas/firewall_test.go | 176 -------- .../networking/v2/extensions/fwaas/fwaas.go | 228 ----------- .../v2/extensions/fwaas/policy_test.go | 64 --- .../v2/extensions/fwaas/rule_test.go | 54 --- .../v2/extensions/vpnaas/service_test.go | 4 +- .../networking/v2/extensions/fwaas/doc.go | 3 - .../v2/extensions/fwaas/firewalls/doc.go | 60 --- .../v2/extensions/fwaas/firewalls/errors.go | 11 - .../v2/extensions/fwaas/firewalls/requests.go | 145 ------- .../v2/extensions/fwaas/firewalls/results.go | 100 ----- .../extensions/fwaas/firewalls/testing/doc.go | 2 - .../fwaas/firewalls/testing/requests_test.go | 344 ---------------- .../v2/extensions/fwaas/firewalls/urls.go | 16 - .../v2/extensions/fwaas/policies/doc.go | 84 ---- .../v2/extensions/fwaas/policies/requests.go | 187 --------- .../v2/extensions/fwaas/policies/results.go | 108 ----- .../extensions/fwaas/policies/testing/doc.go | 2 - .../fwaas/policies/testing/requests_test.go | 277 ------------- .../v2/extensions/fwaas/policies/urls.go | 26 -- .../extensions/fwaas/routerinsertion/doc.go | 68 ---- .../fwaas/routerinsertion/requests.go | 43 -- .../fwaas/routerinsertion/results.go | 7 - .../fwaas/routerinsertion/testing/doc.go | 2 - .../routerinsertion/testing/requests_test.go | 240 ----------- .../v2/extensions/fwaas/rules/doc.go | 64 --- .../v2/extensions/fwaas/rules/errors.go | 12 - .../v2/extensions/fwaas/rules/requests.go | 196 --------- .../v2/extensions/fwaas/rules/results.go | 104 ----- .../v2/extensions/fwaas/rules/testing/doc.go | 2 - .../fwaas/rules/testing/requests_test.go | 382 ------------------ .../v2/extensions/fwaas/rules/urls.go | 16 - 31 files changed, 2 insertions(+), 3025 deletions(-) delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go delete mode 100644 openstack/networking/v2/extensions/fwaas/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/firewalls/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/firewalls/errors.go delete mode 100644 openstack/networking/v2/extensions/fwaas/firewalls/requests.go delete mode 100644 openstack/networking/v2/extensions/fwaas/firewalls/results.go delete mode 100644 openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/fwaas/firewalls/urls.go delete mode 100644 openstack/networking/v2/extensions/fwaas/policies/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/policies/requests.go delete mode 100644 openstack/networking/v2/extensions/fwaas/policies/results.go delete mode 100644 openstack/networking/v2/extensions/fwaas/policies/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/fwaas/policies/urls.go delete mode 100644 openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go delete mode 100644 openstack/networking/v2/extensions/fwaas/routerinsertion/results.go delete mode 100644 openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/fwaas/rules/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/rules/errors.go delete mode 100644 openstack/networking/v2/extensions/fwaas/rules/requests.go delete mode 100644 openstack/networking/v2/extensions/fwaas/rules/results.go delete mode 100644 openstack/networking/v2/extensions/fwaas/rules/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/fwaas/rules/urls.go diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go deleted file mode 100644 index aa48631505..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go +++ /dev/null @@ -1,176 +0,0 @@ -//go:build acceptance || networking || fwaas -// +build acceptance networking fwaas - -package fwaas - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - layer3 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/layer3" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/routerinsertion" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -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) - - router, err := layer3.CreateExternalRouter(t, client) - th.AssertNoErr(t, err) - defer layer3.DeleteRouter(t, client, router.ID) - - rule, err := CreateRule(t, client) - th.AssertNoErr(t, err) - defer DeleteRule(t, client, rule.ID) - - tools.PrintResource(t, rule) - - policy, err := CreatePolicy(t, client, rule.ID) - th.AssertNoErr(t, err) - defer DeletePolicy(t, client, policy.ID) - - tools.PrintResource(t, policy) - - firewall, err := CreateFirewall(t, client, policy.ID) - th.AssertNoErr(t, err) - defer DeleteFirewall(t, client, firewall.ID) - - tools.PrintResource(t, firewall) - - fwName := "" - fwDescription := "" - fwUpdateOpts := firewalls.UpdateOpts{ - Name: &fwName, - Description: &fwDescription, - PolicyID: policy.ID, - } - - _, err = firewalls.Update(context.TODO(), client, firewall.ID, fwUpdateOpts).Extract() - th.AssertNoErr(t, err) - - newFirewall, err := firewalls.Get(context.TODO(), client, firewall.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newFirewall) - th.AssertEquals(t, newFirewall.Name, fwName) - th.AssertEquals(t, newFirewall.Description, fwDescription) - th.AssertEquals(t, newFirewall.PolicyID, policy.ID) - - allPages, err := firewalls.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allFirewalls, err := firewalls.ExtractFirewalls(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, firewall := range allFirewalls { - if firewall.ID == newFirewall.ID { - found = true - } - } - - th.AssertEquals(t, found, true) -} - -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) - - router, err := layer3.CreateExternalRouter(t, client) - th.AssertNoErr(t, err) - defer layer3.DeleteRouter(t, client, router.ID) - - rule, err := CreateRule(t, client) - th.AssertNoErr(t, err) - defer DeleteRule(t, client, rule.ID) - - tools.PrintResource(t, rule) - - policy, err := CreatePolicy(t, client, rule.ID) - th.AssertNoErr(t, err) - defer DeletePolicy(t, client, policy.ID) - - tools.PrintResource(t, policy) - - firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID) - th.AssertNoErr(t, err) - defer DeleteFirewall(t, client, firewall.ID) - - tools.PrintResource(t, firewall) - - router2, err := layer3.CreateExternalRouter(t, client) - th.AssertNoErr(t, err) - defer layer3.DeleteRouter(t, client, router2.ID) - - description := "Some firewall description" - firewallUpdateOpts := firewalls.UpdateOpts{ - PolicyID: policy.ID, - Description: &description, - } - - updateOpts := routerinsertion.UpdateOptsExt{ - UpdateOptsBuilder: firewallUpdateOpts, - RouterIDs: []string{router2.ID}, - } - - _, err = firewalls.Update(context.TODO(), client, firewall.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - - newFirewall, err := firewalls.Get(context.TODO(), client, firewall.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newFirewall) -} - -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) - - router, err := layer3.CreateExternalRouter(t, client) - th.AssertNoErr(t, err) - defer layer3.DeleteRouter(t, client, router.ID) - - rule, err := CreateRule(t, client) - th.AssertNoErr(t, err) - defer DeleteRule(t, client, rule.ID) - - tools.PrintResource(t, rule) - - policy, err := CreatePolicy(t, client, rule.ID) - th.AssertNoErr(t, err) - defer DeletePolicy(t, client, policy.ID) - - tools.PrintResource(t, policy) - - firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID) - th.AssertNoErr(t, err) - defer DeleteFirewall(t, client, firewall.ID) - - tools.PrintResource(t, firewall) - - description := "Some firewall description" - firewallUpdateOpts := firewalls.UpdateOpts{ - PolicyID: policy.ID, - Description: &description, - } - - updateOpts := routerinsertion.UpdateOptsExt{ - UpdateOptsBuilder: firewallUpdateOpts, - RouterIDs: []string{}, - } - - _, err = firewalls.Update(context.TODO(), client, firewall.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - - newFirewall, err := firewalls.Get(context.TODO(), client, firewall.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newFirewall) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go deleted file mode 100644 index 8dec1439a9..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go +++ /dev/null @@ -1,228 +0,0 @@ -package fwaas - -import ( - "context" - "fmt" - "strconv" - "testing" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/policies" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/routerinsertion" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/rules" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -// CreateFirewall will create a Firewall with a random name and a specified -// policy ID. An error will be returned if the firewall could not be created. -func CreateFirewall(t *testing.T, client *gophercloud.ServiceClient, policyID string) (*firewalls.Firewall, error) { - firewallName := tools.RandomString("TESTACC-", 8) - firewallDescription := tools.RandomString("TESTACC-DESC-", 8) - - t.Logf("Attempting to create firewall %s", firewallName) - - iTrue := true - createOpts := firewalls.CreateOpts{ - Name: firewallName, - Description: firewallDescription, - PolicyID: policyID, - AdminStateUp: &iTrue, - } - - firewall, err := firewalls.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return firewall, err - } - - t.Logf("Waiting for firewall to become active.") - if err := WaitForFirewallState(client, firewall.ID, "ACTIVE"); err != nil { - return firewall, err - } - - t.Logf("Successfully created firewall %s", firewallName) - - th.AssertEquals(t, firewall.Name, firewallName) - th.AssertEquals(t, firewall.Description, firewallDescription) - - return firewall, nil -} - -// CreateFirewallOnRouter will create a Firewall with a random name and a -// specified policy ID attached to a specified Router. An error will be -// returned if the firewall could not be created. -func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, policyID string, routerID string) (*firewalls.Firewall, error) { - firewallName := tools.RandomString("TESTACC-", 8) - firewallDescription := tools.RandomString("TESTACC-DESC-", 8) - - t.Logf("Attempting to create firewall %s", firewallName) - - firewallCreateOpts := firewalls.CreateOpts{ - Name: firewallName, - Description: firewallDescription, - PolicyID: policyID, - } - - createOpts := routerinsertion.CreateOptsExt{ - CreateOptsBuilder: firewallCreateOpts, - RouterIDs: []string{routerID}, - } - - firewall, err := firewalls.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return firewall, err - } - - t.Logf("Waiting for firewall to become active.") - if err := WaitForFirewallState(client, firewall.ID, "ACTIVE"); err != nil { - return firewall, err - } - - t.Logf("Successfully created firewall %s", firewallName) - - th.AssertEquals(t, firewall.Name, firewallName) - th.AssertEquals(t, firewall.Description, firewallDescription) - - return firewall, nil -} - -// CreatePolicy will create a Firewall Policy with a random name and given -// rule. An error will be returned if the rule could not be created. -func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string) (*policies.Policy, error) { - policyName := tools.RandomString("TESTACC-", 8) - policyDescription := tools.RandomString("TESTACC-DESC-", 8) - - t.Logf("Attempting to create policy %s", policyName) - - createOpts := policies.CreateOpts{ - Name: policyName, - Description: policyDescription, - Rules: []string{ - ruleID, - }, - } - - policy, err := policies.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return policy, err - } - - t.Logf("Successfully created policy %s", policyName) - - th.AssertEquals(t, policy.Name, policyName) - th.AssertEquals(t, policy.Description, policyDescription) - th.AssertEquals(t, len(policy.Rules), 1) - - return policy, nil -} - -// CreateRule will create a Firewall Rule with a random source address and -// 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) - sourceAddress := fmt.Sprintf("192.168.1.%d", tools.RandomInt(1, 100)) - sourcePort := strconv.Itoa(tools.RandomInt(1, 100)) - destinationAddress := fmt.Sprintf("192.168.2.%d", tools.RandomInt(1, 100)) - destinationPort := strconv.Itoa(tools.RandomInt(1, 100)) - - t.Logf("Attempting to create rule %s with source %s:%s and destination %s:%s", - ruleName, sourceAddress, sourcePort, destinationAddress, destinationPort) - - createOpts := rules.CreateOpts{ - Name: ruleName, - Protocol: rules.ProtocolTCP, - Action: "allow", - SourceIPAddress: sourceAddress, - SourcePort: sourcePort, - DestinationIPAddress: destinationAddress, - DestinationPort: destinationPort, - } - - rule, err := rules.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return rule, err - } - - t.Logf("Rule %s successfully created", ruleName) - - th.AssertEquals(t, rule.Name, ruleName) - th.AssertEquals(t, rule.Protocol, rules.ProtocolTCP) - th.AssertEquals(t, rule.SourceIPAddress, sourceAddress) - th.AssertEquals(t, rule.SourcePort, sourcePort) - th.AssertEquals(t, rule.DestinationIPAddress, destinationAddress) - th.AssertEquals(t, rule.DestinationPort, destinationPort) - - return rule, nil -} - -// DeleteFirewall will delete a firewall 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 DeleteFirewall(t *testing.T, client *gophercloud.ServiceClient, firewallID string) { - t.Logf("Attempting to delete firewall: %s", firewallID) - - err := firewalls.Delete(context.TODO(), client, firewallID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete firewall %s: %v", firewallID, err) - } - - t.Logf("Waiting for firewall to delete.") - if err := WaitForFirewallState(client, firewallID, "DELETED"); err != nil { - t.Logf("Unable to delete firewall: %s", firewallID) - } - - t.Logf("Firewall deleted: %s", firewallID) -} - -// DeletePolicy will delete a policy 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 DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, policyID string) { - t.Logf("Attempting to delete policy: %s", policyID) - - err := policies.Delete(context.TODO(), client, policyID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete policy %s: %v", policyID, err) - } - - t.Logf("Deleted policy: %s", policyID) -} - -// DeleteRule will delete a rule 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 DeleteRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) { - t.Logf("Attempting to delete rule: %s", ruleID) - - err := rules.Delete(context.TODO(), client, ruleID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete rule %s: %v", ruleID, err) - } - - t.Logf("Deleted rule: %s", ruleID) -} - -// WaitForFirewallState will wait until a firewall reaches a given state. -func WaitForFirewallState(client *gophercloud.ServiceClient, firewallID, status string) error { - return tools.WaitFor(func(ctx context.Context) (bool, error) { - current, err := firewalls.Get(ctx, client, firewallID).Extract() - if err != nil { - if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { - if httpStatus.Actual == 404 { - if status == "DELETED" { - return true, nil - } - } - } - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go deleted file mode 100644 index 06375f01db..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/policy_test.go +++ /dev/null @@ -1,64 +0,0 @@ -//go:build acceptance || networking || fwaas -// +build acceptance networking fwaas - -package fwaas - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/policies" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -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) - - rule, err := CreateRule(t, client) - th.AssertNoErr(t, err) - defer DeleteRule(t, client, rule.ID) - - tools.PrintResource(t, rule) - - policy, err := CreatePolicy(t, client, rule.ID) - th.AssertNoErr(t, err) - defer DeletePolicy(t, client, policy.ID) - - tools.PrintResource(t, policy) - - name := "" - description := "" - updateOpts := policies.UpdateOpts{ - Name: &name, - Description: &description, - } - - _, err = policies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - - newPolicy, err := policies.Get(context.TODO(), client, policy.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newPolicy) - th.AssertEquals(t, newPolicy.Name, name) - th.AssertEquals(t, newPolicy.Description, description) - - allPages, err := policies.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allPolicies, err := policies.ExtractPolicies(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, policy := range allPolicies { - if policy.ID == newPolicy.ID { - found = true - } - } - - th.AssertEquals(t, found, true) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go deleted file mode 100644 index 15254dc35a..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas/rule_test.go +++ /dev/null @@ -1,54 +0,0 @@ -//go:build acceptance || networking || fwaas -// +build acceptance networking fwaas - -package fwaas - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/rules" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -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) - - rule, err := CreateRule(t, client) - th.AssertNoErr(t, err) - defer DeleteRule(t, client, rule.ID) - - tools.PrintResource(t, rule) - - ruleDescription := "Some rule description" - updateOpts := rules.UpdateOpts{ - Description: &ruleDescription, - } - - _, err = rules.Update(context.TODO(), client, rule.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - - newRule, err := rules.Get(context.TODO(), client, rule.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newRule) - - allPages, err := rules.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allRules, err := rules.ExtractRules(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, rule := range allRules { - if rule.ID == newRule.ID { - found = true - } - } - - th.AssertEquals(t, found, true) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index 872e5e8a3e..d927818dc6 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -1,5 +1,5 @@ -//go:build acceptance || networking || fwaas -// +build acceptance networking fwaas +//go:build acceptance || networking || vpnaas +// +build acceptance networking vpnaas package vpnaas diff --git a/openstack/networking/v2/extensions/fwaas/doc.go b/openstack/networking/v2/extensions/fwaas/doc.go deleted file mode 100644 index 3ec450a7b3..0000000000 --- a/openstack/networking/v2/extensions/fwaas/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package fwaas provides information and interaction with the Firewall -// as a Service extension for the OpenStack Networking service. -package fwaas diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/doc.go b/openstack/networking/v2/extensions/fwaas/firewalls/doc.go deleted file mode 100644 index c01070edc8..0000000000 --- a/openstack/networking/v2/extensions/fwaas/firewalls/doc.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Package firewalls allows management and retrieval of firewalls from the -OpenStack Networking Service. - -Example to List Firewalls - - listOpts := firewalls.ListOpts{ - TenantID: "tenant-id", - } - - allPages, err := firewalls.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allFirewalls, err := firewalls.ExtractFirewalls(allPages) - if err != nil { - panic(err) - } - - for _, fw := range allFirewalls { - fmt.Printf("%+v\n", fw) - } - -Example to Create a Firewall - - createOpts := firewalls.CreateOpts{ - Name: "firewall_1", - Description: "A firewall", - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - AdminStateUp: gophercloud.Enabled, - } - - firewall, err := firewalls.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Firewall - - firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" - - updateOpts := firewalls.UpdateOpts{ - AdminStateUp: gophercloud.Disabled, - } - - firewall, err := firewalls.Update(networkClient, firewallID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Firewall - - firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" - err := firewalls.Delete(networkClient, firewallID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package firewalls diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/errors.go b/openstack/networking/v2/extensions/fwaas/firewalls/errors.go deleted file mode 100644 index dd92bb20db..0000000000 --- a/openstack/networking/v2/extensions/fwaas/firewalls/errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package firewalls - -import "fmt" - -func err(str string) error { - return fmt.Errorf("%s", str) -} - -var ( - errPolicyRequired = err("A policy ID is required") -) diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/openstack/networking/v2/extensions/fwaas/firewalls/requests.go deleted file mode 100644 index 28b3ea725e..0000000000 --- a/openstack/networking/v2/extensions/fwaas/firewalls/requests.go +++ /dev/null @@ -1,145 +0,0 @@ -package firewalls - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToFirewallListQuery() (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 firewall attributes you want to see returned. SortKey allows you to sort -// by a particular firewall attribute. SortDir sets the direction, 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"` - AdminStateUp bool `q:"admin_state_up"` - Shared bool `q:"shared"` - PolicyID string `q:"firewall_policy_id"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToFirewallListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToFirewallListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns a Pager which allows you to iterate over a collection of -// firewalls. It accepts a ListOpts struct, which allows you to filter -// and sort the returned collection for greater efficiency. -// -// Default policy settings return only those firewalls that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := rootURL(c) - if opts != nil { - query, err := opts.ToFirewallListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return FirewallPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToFirewallCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains all the values needed to create a new firewall. -type CreateOpts struct { - PolicyID string `json:"firewall_policy_id" required:"true"` - // TenantID specifies a tenant to own the firewall. The caller must have - // an admin role in order to set this. Otherwise, this field is left unset - // and the caller will be the owner. - TenantID string `json:"tenant_id,omitempty"` - ProjectID string `json:"project_id,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - Shared *bool `json:"shared,omitempty"` -} - -// ToFirewallCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "firewall") -} - -// Create accepts a CreateOpts struct and uses the values to create a new firewall. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToFirewallCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular firewall based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToFirewallUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contains the values used when updating a firewall. -type UpdateOpts struct { - PolicyID string `json:"firewall_policy_id" required:"true"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - AdminStateUp *bool `json:"admin_state_up,omitempty"` - Shared *bool `json:"shared,omitempty"` -} - -// ToFirewallUpdateMap casts a CreateOpts struct to a map. -func (opts UpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "firewall") -} - -// Update allows firewalls to be updated. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToFirewallUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will permanently delete a particular firewall based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/results.go b/openstack/networking/v2/extensions/fwaas/firewalls/results.go deleted file mode 100644 index 27fec509d6..0000000000 --- a/openstack/networking/v2/extensions/fwaas/firewalls/results.go +++ /dev/null @@ -1,100 +0,0 @@ -package firewalls - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Firewall is an OpenStack firewall. -type Firewall struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - AdminStateUp bool `json:"admin_state_up"` - Status string `json:"status"` - PolicyID string `json:"firewall_policy_id"` - TenantID string `json:"tenant_id"` - ProjectID string `json:"project_id"` -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a firewall. -func (r commonResult) Extract() (*Firewall, error) { - var s Firewall - err := r.ExtractInto(&s) - return &s, err -} - -func (r commonResult) ExtractInto(v interface{}) error { - return r.Result.ExtractIntoStructPtr(v, "firewall") -} - -func ExtractFirewallsInto(r pagination.Page, v interface{}) error { - return r.(FirewallPage).Result.ExtractIntoSlicePtr(v, "firewalls") -} - -// FirewallPage is the page returned by a pager when traversing over a -// collection of firewalls. -type FirewallPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of firewalls 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 FirewallPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"firewalls_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractFirewalls accepts a Page struct, specifically a FirewallPage struct, -// and extracts the elements into a slice of Firewall structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractFirewalls(r pagination.Page) ([]Firewall, error) { - var s []Firewall - err := ExtractFirewallsInto(r, &s) - return s, err -} - -// GetResult represents the result of a Get operation. Call its Extract -// method to interpret it as a Firewall. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an Update operation. Call its Extract -// method to interpret it as a Firewall. -type UpdateResult struct { - commonResult -} - -// DeleteResult represents the result of a delete operation. Call its -// ExtractErr method to determine if the operation succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// CreateResult represents the result of a Create operation. Call its Extract -// method to interpret it as a Firewall. -type CreateResult struct { - commonResult -} diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go b/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go deleted file mode 100644 index 82ebf979ba..0000000000 --- a/openstack/networking/v2/extensions/fwaas/firewalls/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// firewalls unit tests -package testing diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go deleted file mode 100644 index 66b60c9b60..0000000000 --- a/openstack/networking/v2/extensions/fwaas/firewalls/testing/requests_test.go +++ /dev/null @@ -1,344 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/routerinsertion" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls", 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, ` -{ - "firewalls":[ - { - "status": "ACTIVE", - "name": "fw1", - "admin_state_up": false, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a", - "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", - "description": "OpenStack firewall 1" - }, - { - "status": "PENDING_UPDATE", - "name": "fw2", - "admin_state_up": true, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e299", - "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f99", - "description": "OpenStack firewall 2" - } - ] -} - `) - }) - - count := 0 - - firewalls.List(fake.ServiceClient(), firewalls.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := firewalls.ExtractFirewalls(page) - if err != nil { - t.Errorf("Failed to extract members: %v", err) - return false, err - } - - expected := []firewalls.Firewall{ - { - Status: "ACTIVE", - Name: "fw1", - AdminStateUp: false, - TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", - PolicyID: "34be8c83-4d42-4dca-a74e-b77fffb8e28a", - ID: "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", - Description: "OpenStack firewall 1", - }, - { - Status: "PENDING_UPDATE", - Name: "fw2", - AdminStateUp: true, - TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", - PolicyID: "34be8c83-4d42-4dca-a74e-b77fffb8e299", - ID: "fb5b5315-64f6-4ea3-8e58-981cc37c6f99", - Description: "OpenStack firewall 2", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestListWithExtensions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls", 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, ` -{ - "firewalls":[ - { - "status": "ACTIVE", - "name": "fw1", - "admin_state_up": false, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a", - "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", - "description": "OpenStack firewall 1", - "router_ids": ["abcd1234"] - }, - { - "status": "PENDING_UPDATE", - "name": "fw2", - "admin_state_up": true, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e299", - "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f99", - "description": "OpenStack firewall 2" - } - ] -} - `) - }) - - type FirewallsWithExt struct { - firewalls.Firewall - routerinsertion.FirewallExt - } - - allPages, err := firewalls.List(fake.ServiceClient(), nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - var actual []FirewallsWithExt - err = firewalls.ExtractFirewallsInto(allPages, &actual) - th.AssertNoErr(t, err) - th.AssertEquals(t, 2, len(actual)) - th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", actual[0].ID) - th.AssertEquals(t, "abcd1234", actual[0].RouterIDs[0]) -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls", 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, ` -{ - "firewall":{ - "name": "fw", - "description": "OpenStack firewall", - "admin_state_up": true, - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "firewall":{ - "status": "PENDING_CREATE", - "name": "fw", - "description": "OpenStack firewall", - "admin_state_up": true, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c" - } -} - `) - }) - - options := firewalls.CreateOpts{ - TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", - Name: "fw", - Description: "OpenStack firewall", - AdminStateUp: gophercloud.Enabled, - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - } - _, err := firewalls.Create(context.TODO(), fake.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls/fb5b5315-64f6-4ea3-8e58-981cc37c6f61", 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, ` -{ - "firewall": { - "status": "ACTIVE", - "name": "fw", - "admin_state_up": true, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a", - "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", - "description": "OpenStack firewall" - } -} - `) - }) - - fw, err := firewalls.Get(context.TODO(), fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "ACTIVE", fw.Status) - th.AssertEquals(t, "fw", fw.Name) - th.AssertEquals(t, "OpenStack firewall", fw.Description) - th.AssertEquals(t, true, fw.AdminStateUp) - th.AssertEquals(t, "34be8c83-4d42-4dca-a74e-b77fffb8e28a", fw.PolicyID) - th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", fw.ID) - th.AssertEquals(t, "b4eedccc6fb74fa8a7ad6b08382b852b", fw.TenantID) -} - -func TestGetWithExtensions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls/fb5b5315-64f6-4ea3-8e58-981cc37c6f61", 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, ` -{ - "firewall": { - "status": "ACTIVE", - "name": "fw", - "admin_state_up": true, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a", - "id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", - "description": "OpenStack firewall", - "router_ids": ["abcd1234"] - } -} - `) - }) - - var fw struct { - firewalls.Firewall - routerinsertion.FirewallExt - } - - err := firewalls.Get(context.TODO(), fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").ExtractInto(&fw) - th.AssertNoErr(t, err) - - th.AssertEquals(t, "ACTIVE", fw.Status) - th.AssertEquals(t, "fw", fw.Name) - th.AssertEquals(t, "OpenStack firewall", fw.Description) - th.AssertEquals(t, true, fw.AdminStateUp) - th.AssertEquals(t, "34be8c83-4d42-4dca-a74e-b77fffb8e28a", fw.PolicyID) - th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", fw.ID) - th.AssertEquals(t, "b4eedccc6fb74fa8a7ad6b08382b852b", fw.TenantID) - th.AssertEquals(t, "abcd1234", fw.RouterIDs[0]) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", 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":{ - "name": "fw", - "description": "updated fw", - "admin_state_up":false, - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "firewall": { - "status": "ACTIVE", - "name": "fw", - "admin_state_up": false, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576", - "description": "OpenStack firewall" - } -} - `) - }) - - var name = "fw" - var description = "updated fw" - options := firewalls.UpdateOpts{ - Name: &name, - Description: &description, - AdminStateUp: gophercloud.Disabled, - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - } - - _, err := firewalls.Update(context.TODO(), fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", options).Extract() - th.AssertNoErr(t, err) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls/4ec89087-d057-4e2c-911f-60a3b47ee304", 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) - }) - - res := firewalls.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/networking/v2/extensions/fwaas/firewalls/urls.go b/openstack/networking/v2/extensions/fwaas/firewalls/urls.go deleted file mode 100644 index d7f479355a..0000000000 --- a/openstack/networking/v2/extensions/fwaas/firewalls/urls.go +++ /dev/null @@ -1,16 +0,0 @@ -package firewalls - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "fw" - resourcePath = "firewalls" -) - -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/networking/v2/extensions/fwaas/policies/doc.go b/openstack/networking/v2/extensions/fwaas/policies/doc.go deleted file mode 100644 index ae824491f1..0000000000 --- a/openstack/networking/v2/extensions/fwaas/policies/doc.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Package policies allows management and retrieval of Firewall Policies in the -OpenStack Networking Service. - -Example to List Policies - - listOpts := policies.ListOpts{ - TenantID: "966b3c7d36a24facaf20b7e458bf2192", - } - - allPages, err := policies.List(networkClient, listOpts).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) - } - -Example to Create a Policy - - createOpts := policies.CreateOpts{ - Name: "policy_1", - Description: "A policy", - Rules: []string{ - "98a58c87-76be-ae7c-a74e-b77fffb88d95", - "7c4f087a-ed46-4ea8-8040-11ca460a61c0", - } - } - - policy, err := policies.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Policy - - policyID := "38aee955-6283-4279-b091-8b9c828000ec" - - updateOpts := policies.UpdateOpts{ - Description: "New Description", - } - - policy, err := policies.Update(networkClient, policyID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Policy - - policyID := "38aee955-6283-4279-b091-8b9c828000ec" - err := policies.Delete(networkClient, policyID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Add a Rule to a Policy - - policyID := "38aee955-6283-4279-b091-8b9c828000ec" - ruleOpts := policies.InsertRuleOpts{ - ID: "98a58c87-76be-ae7c-a74e-b77fffb88d95", - } - - policy, err := policies.AddRule(networkClient, policyID, ruleOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Rule from a Policy - - policyID := "38aee955-6283-4279-b091-8b9c828000ec" - ruleID := "98a58c87-76be-ae7c-a74e-b77fffb88d95", - - policy, err := policies.RemoveRule(networkClient, policyID, ruleID).Extract() - if err != nil { - panic(err) - } -*/ -package policies diff --git a/openstack/networking/v2/extensions/fwaas/policies/requests.go b/openstack/networking/v2/extensions/fwaas/policies/requests.go deleted file mode 100644 index 8146e142de..0000000000 --- a/openstack/networking/v2/extensions/fwaas/policies/requests.go +++ /dev/null @@ -1,187 +0,0 @@ -package policies - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToPolicyListQuery() (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 firewall policy attributes you want to see returned. SortKey allows you -// to sort by a particular firewall policy attribute. SortDir sets the direction, -// 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"` - Audited *bool `q:"audited"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToPolicyListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToPolicyListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns a Pager which allows you to iterate over a collection of -// firewall policies. It accepts a ListOpts struct, which allows you to filter -// and sort the returned collection for greater efficiency. -// -// Default policy settings return only those firewall policies that are owned by -// the tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := rootURL(c) - if opts != nil { - query, err := opts.ToPolicyListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return PolicyPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToFirewallPolicyCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains all the values needed to create a new firewall policy. -type CreateOpts struct { - // TenantID specifies a tenant to own the firewall. The caller must have - // an admin role in order to set this. Otherwise, this field is left unset - // and the caller will be the owner. - 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"` - Audited *bool `json:"audited,omitempty"` - Rules []string `json:"firewall_rules,omitempty"` -} - -// ToFirewallPolicyCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "firewall_policy") -} - -// Create accepts a CreateOpts struct and uses the values to create a new -// firewall policy. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToFirewallPolicyCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular firewall policy based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToFirewallPolicyUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contains the values used when updating a firewall policy. -type UpdateOpts struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - Shared *bool `json:"shared,omitempty"` - Audited *bool `json:"audited,omitempty"` - Rules []string `json:"firewall_rules,omitempty"` -} - -// ToFirewallPolicyUpdateMap casts a CreateOpts struct to a map. -func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "firewall_policy") -} - -// Update allows firewall policies to be updated. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToFirewallPolicyUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will permanently delete a particular firewall policy based on its -// unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// InsertRuleOptsBuilder allows extensions to add additional parameters to the -// InsertRule request. -type InsertRuleOptsBuilder interface { - ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) -} - -// InsertRuleOpts contains the values used when updating a policy's rules. -type InsertRuleOpts struct { - ID string `json:"firewall_rule_id" required:"true"` - BeforeRuleID string `json:"insert_before,omitempty"` - AfterRuleID string `json:"insert_after,omitempty"` -} - -func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "") -} - -// AddRule will add a rule to a policy. -func AddRule(ctx context.Context, c *gophercloud.ServiceClient, id string, opts InsertRuleOptsBuilder) (r InsertRuleResult) { - b, err := opts.ToFirewallPolicyInsertRuleMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// RemoveRule will add a rule to a policy. -func RemoveRule(ctx context.Context, c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { - b := map[string]interface{}{"firewall_rule_id": ruleID} - resp, err := c.Put(ctx, removeURL(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/fwaas/policies/results.go b/openstack/networking/v2/extensions/fwaas/policies/results.go deleted file mode 100644 index 2e60d37396..0000000000 --- a/openstack/networking/v2/extensions/fwaas/policies/results.go +++ /dev/null @@ -1,108 +0,0 @@ -package policies - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Policy is a firewall policy. -type Policy struct { - ID string `json:"id"` - 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"` -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a firewall policy. -func (r commonResult) Extract() (*Policy, error) { - var s struct { - Policy *Policy `json:"firewall_policy"` - } - err := r.ExtractInto(&s) - return s.Policy, err -} - -// PolicyPage is the page returned by a pager when traversing over a -// collection of firewall policies. -type PolicyPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of firewall policies 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 PolicyPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"firewall_policies_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractPolicies accepts a Page struct, specifically a Policy struct, -// and extracts the elements into a slice of Policy structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractPolicies(r pagination.Page) ([]Policy, error) { - var s struct { - Policies []Policy `json:"firewall_policies"` - } - err := (r.(PolicyPage)).ExtractInto(&s) - return s.Policies, err -} - -// GetResult represents the result of a get operation. Call its Extract -// method to interpret it as a Policy. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its -// Extract method to interpret it as a Policy. -type UpdateResult struct { - commonResult -} - -// DeleteResult represents the result of a delete operation. Call its -// ExtractErr method to determine if the operation succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a Policy. -type CreateResult struct { - commonResult -} - -// InsertRuleResult represents the result of an InsertRule operation. Call its -// Extract method to interpret it as a Policy. -type InsertRuleResult struct { - commonResult -} - -// RemoveRuleResult represents the result of a RemoveRule operation. Call its -// Extract method to interpret it as a Policy. -type RemoveRuleResult struct { - commonResult -} diff --git a/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go b/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go deleted file mode 100644 index a61f5488e3..0000000000 --- a/openstack/networking/v2/extensions/fwaas/policies/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// policies unit tests -package testing diff --git a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go deleted file mode 100644 index b0bf291cad..0000000000 --- a/openstack/networking/v2/extensions/fwaas/policies/testing/requests_test.go +++ /dev/null @@ -1,277 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/policies" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_policies", 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, ` -{ - "firewall_policies": [ - { - "name": "policy1", - "firewall_rules": [ - "75452b36-268e-4e75-aaf4-f0e7ed50bc97", - "c9e77ca0-1bc8-497d-904d-948107873dc6" - ], - "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", - "audited": true, - "shared": false, - "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", - "description": "Firewall policy 1" - }, - { - "name": "policy2", - "firewall_rules": [ - "03d2a6ad-633f-431a-8463-4370d06a22c8" - ], - "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", - "audited": false, - "shared": true, - "id": "c854fab5-bdaf-4a86-9359-78de93e5df01", - "description": "Firewall policy 2" - } - ] -} - `) - }) - - count := 0 - - policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := policies.ExtractPolicies(page) - if err != nil { - t.Errorf("Failed to extract members: %v", err) - return false, err - } - - expected := []policies.Policy{ - { - Name: "policy1", - Rules: []string{ - "75452b36-268e-4e75-aaf4-f0e7ed50bc97", - "c9e77ca0-1bc8-497d-904d-948107873dc6", - }, - TenantID: "9145d91459d248b1b02fdaca97c6a75d", - Audited: true, - Shared: false, - ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", - Description: "Firewall policy 1", - }, - { - Name: "policy2", - Rules: []string{ - "03d2a6ad-633f-431a-8463-4370d06a22c8", - }, - TenantID: "9145d91459d248b1b02fdaca97c6a75d", - Audited: false, - Shared: true, - ID: "c854fab5-bdaf-4a86-9359-78de93e5df01", - Description: "Firewall policy 2", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_policies", 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, ` -{ - "firewall_policy":{ - "name": "policy", - "firewall_rules": [ - "98a58c87-76be-ae7c-a74e-b77fffb88d95", - "11a58c87-76be-ae7c-a74e-b77fffb88a32" - ], - "description": "Firewall policy", - "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", - "audited": true, - "shared": false - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "firewall_policy":{ - "name": "policy", - "firewall_rules": [ - "98a58c87-76be-ae7c-a74e-b77fffb88d95", - "11a58c87-76be-ae7c-a74e-b77fffb88a32" - ], - "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", - "audited": false, - "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", - "description": "Firewall policy" - } -} - `) - }) - - options := policies.CreateOpts{ - TenantID: "9145d91459d248b1b02fdaca97c6a75d", - Name: "policy", - Description: "Firewall policy", - Shared: gophercloud.Disabled, - Audited: gophercloud.Enabled, - Rules: []string{ - "98a58c87-76be-ae7c-a74e-b77fffb88d95", - "11a58c87-76be-ae7c-a74e-b77fffb88a32", - }, - } - - _, err := policies.Create(context.TODO(), fake.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_policies/bcab5315-64f6-4ea3-8e58-981cc37c6f61", 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, ` -{ - "firewall_policy":{ - "name": "www", - "firewall_rules": [ - "75452b36-268e-4e75-aaf4-f0e7ed50bc97", - "c9e77ca0-1bc8-497d-904d-948107873dc6", - "03d2a6ad-633f-431a-8463-4370d06a22c8" - ], - "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", - "audited": false, - "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", - "description": "Firewall policy web" - } -} - `) - }) - - policy, err := policies.Get(context.TODO(), fake.ServiceClient(), "bcab5315-64f6-4ea3-8e58-981cc37c6f61").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "www", policy.Name) - th.AssertEquals(t, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", policy.ID) - th.AssertEquals(t, "Firewall policy web", policy.Description) - th.AssertEquals(t, 3, len(policy.Rules)) - th.AssertEquals(t, "75452b36-268e-4e75-aaf4-f0e7ed50bc97", policy.Rules[0]) - 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) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_policies/f2b08c1e-aa81-4668-8ae1-1401bcb0576c", 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_policy":{ - "name": "policy", - "firewall_rules": [ - "98a58c87-76be-ae7c-a74e-b77fffb88d95", - "11a58c87-76be-ae7c-a74e-b77fffb88a32" - ], - "description": "Firewall policy" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "firewall_policy":{ - "name": "policy", - "firewall_rules": [ - "75452b36-268e-4e75-aaf4-f0e7ed50bc97", - "c9e77ca0-1bc8-497d-904d-948107873dc6", - "03d2a6ad-633f-431a-8463-4370d06a22c8" - ], - "tenant_id": "9145d91459d248b1b02fdaca97c6a75d", - "audited": false, - "id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", - "description": "Firewall policy" - } -} - `) - }) - - var name = "policy" - var description = "Firewall policy" - options := policies.UpdateOpts{ - Name: &name, - Description: &description, - Rules: []string{ - "98a58c87-76be-ae7c-a74e-b77fffb88d95", - "11a58c87-76be-ae7c-a74e-b77fffb88a32", - }, - } - - _, err := policies.Update(context.TODO(), fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract() - th.AssertNoErr(t, err) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_policies/4ec89077-d057-4a2b-911f-60a3b47ee304", 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) - }) - - res := policies.Delete(context.TODO(), fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/networking/v2/extensions/fwaas/policies/urls.go b/openstack/networking/v2/extensions/fwaas/policies/urls.go deleted file mode 100644 index 2a8e354ae4..0000000000 --- a/openstack/networking/v2/extensions/fwaas/policies/urls.go +++ /dev/null @@ -1,26 +0,0 @@ -package policies - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "fw" - resourcePath = "firewall_policies" - insertPath = "insert_rule" - removePath = "remove_rule" -) - -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) -} - -func insertURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(rootPath, resourcePath, id, insertPath) -} - -func removeURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(rootPath, resourcePath, id, removePath) -} diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go deleted file mode 100644 index 4f0a779eec..0000000000 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Package routerinsertion implements the fwaasrouterinsertion Firewall extension. -It is used to manage the router information of a firewall. - -Example to List Firewalls with Router Information - - type FirewallsWithRouters struct { - firewalls.Firewall - routerinsertion.FirewallExt - } - - var allFirewalls []FirewallsWithRouters - - allPages, err := firewalls.List(networkClient, nil).AllPages() - if err != nil { - panic(err) - } - - err = firewalls.ExtractFirewallsInto(allPages, &allFirewalls) - if err != nil { - panic(err) - } - - for _, fw := range allFirewalls { - fmt.Printf("%+v\n", fw) - } - -Example to Create a Firewall with a Router - - firewallCreateOpts := firewalls.CreateOpts{ - Name: "firewall_1", - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - } - - createOpts := routerinsertion.CreateOptsExt{ - CreateOptsBuilder: firewallCreateOpts, - RouterIDs: []string{ - "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8", - }, - } - - firewall, err := firewalls.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Firewall with a Router - - firewallID := "a6917946-38ab-4ffd-a55a-26c0980ce5ee" - - firewallUpdateOpts := firewalls.UpdateOpts{ - Description: "updated firewall", - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - } - - updateOpts := routerinsertion.UpdateOptsExt{ - UpdateOptsBuilder: firewallUpdateOpts, - RouterIDs: []string{ - "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8", - }, - } - - firewall, err := firewalls.Update(networkClient, firewallID, updateOpts).Extract() - if err != nil { - panic(err) - } -*/ -package routerinsertion diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go deleted file mode 100644 index 0ac9abf7e9..0000000000 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go +++ /dev/null @@ -1,43 +0,0 @@ -package routerinsertion - -import ( - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" -) - -// CreateOptsExt adds the RouterIDs option to the base CreateOpts. -type CreateOptsExt struct { - firewalls.CreateOptsBuilder - RouterIDs []string `json:"router_ids"` -} - -// ToFirewallCreateMap adds router_ids to the base firewall creation options. -func (opts CreateOptsExt) ToFirewallCreateMap() (map[string]interface{}, error) { - base, err := opts.CreateOptsBuilder.ToFirewallCreateMap() - if err != nil { - return nil, err - } - - firewallMap := base["firewall"].(map[string]interface{}) - firewallMap["router_ids"] = opts.RouterIDs - - return base, nil -} - -// UpdateOptsExt adds the RouterIDs option to the base UpdateOpts. -type UpdateOptsExt struct { - firewalls.UpdateOptsBuilder - RouterIDs []string `json:"router_ids"` -} - -// ToFirewallUpdateMap adds router_ids to the base firewall update options. -func (opts UpdateOptsExt) ToFirewallUpdateMap() (map[string]interface{}, error) { - base, err := opts.UpdateOptsBuilder.ToFirewallUpdateMap() - if err != nil { - return nil, err - } - - firewallMap := base["firewall"].(map[string]interface{}) - firewallMap["router_ids"] = opts.RouterIDs - - return base, nil -} diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/results.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/results.go deleted file mode 100644 index 85c288e51e..0000000000 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/results.go +++ /dev/null @@ -1,7 +0,0 @@ -package routerinsertion - -// FirewallExt is an extension to the base Firewall object -type FirewallExt struct { - // RouterIDs are the routers that the firewall is attached to. - RouterIDs []string `json:"router_ids"` -} diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go deleted file mode 100644 index 86e710f6e1..0000000000 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// routerinsertion unit tests -package testing diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go deleted file mode 100644 index 9afee9ed5d..0000000000 --- a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go +++ /dev/null @@ -1,240 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/firewalls" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/routerinsertion" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls", 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, ` -{ - "firewall":{ - "name": "fw", - "description": "OpenStack firewall", - "admin_state_up": true, - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "router_ids": [ - "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8" - ] - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "firewall":{ - "status": "PENDING_CREATE", - "name": "fw", - "description": "OpenStack firewall", - "admin_state_up": true, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c" - } -} - `) - }) - - firewallCreateOpts := firewalls.CreateOpts{ - TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", - Name: "fw", - Description: "OpenStack firewall", - AdminStateUp: gophercloud.Enabled, - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - } - createOpts := routerinsertion.CreateOptsExt{ - CreateOptsBuilder: firewallCreateOpts, - RouterIDs: []string{"8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"}, - } - - _, err := firewalls.Create(context.TODO(), fake.ServiceClient(), createOpts).Extract() - th.AssertNoErr(t, err) -} - -func TestCreateWithNoRouters(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls", 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, ` -{ - "firewall":{ - "name": "fw", - "description": "OpenStack firewall", - "admin_state_up": true, - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "router_ids": [] - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "firewall":{ - "status": "PENDING_CREATE", - "name": "fw", - "description": "OpenStack firewall", - "admin_state_up": true, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c" - } -} - `) - }) - - firewallCreateOpts := firewalls.CreateOpts{ - TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b", - Name: "fw", - Description: "OpenStack firewall", - AdminStateUp: gophercloud.Enabled, - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - } - createOpts := routerinsertion.CreateOptsExt{ - CreateOptsBuilder: firewallCreateOpts, - RouterIDs: []string{}, - } - - _, err := firewalls.Create(context.TODO(), fake.ServiceClient(), createOpts).Extract() - th.AssertNoErr(t, err) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", 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":{ - "name": "fw", - "description": "updated fw", - "admin_state_up":false, - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - "router_ids": [ - "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8" - ] - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "firewall": { - "status": "ACTIVE", - "name": "fw", - "admin_state_up": false, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576", - "description": "OpenStack firewall" - } -} - `) - }) - - var name = "fw" - var description = "updated fw" - firewallUpdateOpts := firewalls.UpdateOpts{ - Name: &name, - Description: &description, - AdminStateUp: gophercloud.Disabled, - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - } - updateOpts := routerinsertion.UpdateOptsExt{ - UpdateOptsBuilder: firewallUpdateOpts, - RouterIDs: []string{"8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"}, - } - - _, err := firewalls.Update(context.TODO(), fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract() - th.AssertNoErr(t, err) -} - -func TestUpdateWithNoRouters(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", 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":{ - "name": "fw", - "description": "updated fw", - "admin_state_up":false, - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - "router_ids": [] - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "firewall": { - "status": "ACTIVE", - "name": "fw", - "admin_state_up": false, - "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b", - "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576", - "description": "OpenStack firewall" - } -} - `) - }) - - var name = "fw" - var description = "updated fw" - firewallUpdateOpts := firewalls.UpdateOpts{ - Name: &name, - Description: &description, - AdminStateUp: gophercloud.Disabled, - PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c", - } - updateOpts := routerinsertion.UpdateOptsExt{ - UpdateOptsBuilder: firewallUpdateOpts, - RouterIDs: []string{}, - } - - _, err := firewalls.Update(context.TODO(), fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract() - th.AssertNoErr(t, err) -} diff --git a/openstack/networking/v2/extensions/fwaas/rules/doc.go b/openstack/networking/v2/extensions/fwaas/rules/doc.go deleted file mode 100644 index 3351a3e5c9..0000000000 --- a/openstack/networking/v2/extensions/fwaas/rules/doc.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Package rules enables management and retrieval of Firewall Rules in the -OpenStack Networking Service. - -Example to List Rules - - listOpts := rules.ListOpts{ - Protocol: rules.ProtocolAny, - } - - allPages, err := rules.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allRules, err := rules.ExtractRules(allPages) - if err != nil { - panic(err) - } - - for _, rule := range allRules { - fmt.Printf("%+v\n", rule) - } - -Example to Create a Rule - - createOpts := rules.CreateOpts{ - Action: "allow", - Protocol: rules.ProtocolTCP, - Description: "ssh", - DestinationPort: 22, - DestinationIPAddress: "192.168.1.0/24", - } - - rule, err := rules.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Rule - - ruleID := "f03bd950-6c56-4f5e-a307-45967078f507" - newPort := 80 - newDescription := "http" - - updateOpts := rules.UpdateOpts{ - Description: &newDescription, - port: &newPort, - } - - rule, err := rules.Update(networkClient, ruleID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Rule - - ruleID := "f03bd950-6c56-4f5e-a307-45967078f507" - err := rules.Delete(networkClient, ruleID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package rules diff --git a/openstack/networking/v2/extensions/fwaas/rules/errors.go b/openstack/networking/v2/extensions/fwaas/rules/errors.go deleted file mode 100644 index 0b29d39fd9..0000000000 --- a/openstack/networking/v2/extensions/fwaas/rules/errors.go +++ /dev/null @@ -1,12 +0,0 @@ -package rules - -import "fmt" - -func err(str string) error { - return fmt.Errorf("%s", str) -} - -var ( - errProtocolRequired = err("A protocol is required (tcp, udp, icmp or any)") - errActionRequired = err("An action is required (allow or deny)") -) diff --git a/openstack/networking/v2/extensions/fwaas/rules/requests.go b/openstack/networking/v2/extensions/fwaas/rules/requests.go deleted file mode 100644 index fd6e1c459f..0000000000 --- a/openstack/networking/v2/extensions/fwaas/rules/requests.go +++ /dev/null @@ -1,196 +0,0 @@ -package rules - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -type ( - // Protocol represents a valid rule protocol. - Protocol string -) - -const ( - // ProtocolAny is to allow any protocol. - ProtocolAny Protocol = "any" - - // ProtocolICMP is to allow the ICMP protocol. - ProtocolICMP Protocol = "icmp" - - // ProtocolTCP is to allow the TCP protocol. - ProtocolTCP Protocol = "tcp" - - // ProtocolUDP is to allow the UDP protocol. - ProtocolUDP Protocol = "udp" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToRuleListQuery() (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 Firewall rule attributes you want to see returned. SortKey allows you to -// sort by a particular firewall rule attribute. SortDir sets the direction, 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"` - Protocol string `q:"protocol"` - Action string `q:"action"` - IPVersion int `q:"ip_version"` - SourceIPAddress string `q:"source_ip_address"` - DestinationIPAddress string `q:"destination_ip_address"` - SourcePort string `q:"source_port"` - DestinationPort string `q:"destination_port"` - Enabled bool `q:"enabled"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToRuleListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToRuleListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), nil -} - -// List returns a Pager which allows you to iterate over a collection of -// firewall rules. It accepts a ListOpts struct, which allows you to filter -// and sort the returned collection for greater efficiency. -// -// Default policy settings return only those firewall rules that are owned by -// the tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := rootURL(c) - - if opts != nil { - query, err := opts.ToRuleListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return RulePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToRuleCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains all the values needed to create a new firewall rule. -type CreateOpts struct { - Protocol Protocol `json:"protocol" required:"true"` - Action string `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"` - SourceIPAddress string `json:"source_ip_address,omitempty"` - DestinationIPAddress string `json:"destination_ip_address,omitempty"` - SourcePort string `json:"source_port,omitempty"` - DestinationPort string `json:"destination_port,omitempty"` - Shared *bool `json:"shared,omitempty"` - Enabled *bool `json:"enabled,omitempty"` -} - -// ToRuleCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "firewall_rule") - if err != nil { - return nil, err - } - - if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" { - m["protocol"] = nil - } - - return b, nil -} - -// Create accepts a CreateOpts struct and uses the values to create a new -// firewall rule. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToRuleCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular firewall rule based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToRuleUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contains the values used when updating a firewall rule. -// These fields are all pointers so that unset fields will not cause the -// existing Rule attribute to be removed. -type UpdateOpts struct { - Protocol *string `json:"protocol,omitempty"` - Action *string `json:"action,omitempty"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - IPVersion *gophercloud.IPVersion `json:"ip_version,omitempty"` - SourceIPAddress *string `json:"source_ip_address,omitempty"` - DestinationIPAddress *string `json:"destination_ip_address,omitempty"` - SourcePort *string `json:"source_port,omitempty"` - DestinationPort *string `json:"destination_port,omitempty"` - Shared *bool `json:"shared,omitempty"` - Enabled *bool `json:"enabled,omitempty"` -} - -// ToRuleUpdateMap casts a UpdateOpts struct to a map. -func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "firewall_rule") -} - -// Update allows firewall policies to be updated. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToRuleUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will permanently delete a particular firewall rule based on its -// unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/fwaas/rules/results.go b/openstack/networking/v2/extensions/fwaas/rules/results.go deleted file mode 100644 index 8f31d65556..0000000000 --- a/openstack/networking/v2/extensions/fwaas/rules/results.go +++ /dev/null @@ -1,104 +0,0 @@ -package rules - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Rule represents a firewall rule. -type Rule struct { - ID string `json:"id"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Protocol string `json:"protocol"` - Action string `json:"action"` - IPVersion int `json:"ip_version,omitempty"` - SourceIPAddress string `json:"source_ip_address,omitempty"` - DestinationIPAddress string `json:"destination_ip_address,omitempty"` - SourcePort string `json:"source_port,omitempty"` - DestinationPort string `json:"destination_port,omitempty"` - Shared bool `json:"shared,omitempty"` - Enabled bool `json:"enabled,omitempty"` - PolicyID string `json:"firewall_policy_id"` - Position int `json:"position"` - TenantID string `json:"tenant_id"` - ProjectID string `json:"project_id"` -} - -// RulePage is the page returned by a pager when traversing over a -// collection of firewall rules. -type RulePage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of firewall rules 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 RulePage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"firewall_rules_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractRules accepts a Page struct, specifically a RulePage struct, -// and extracts the elements into a slice of Rule structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractRules(r pagination.Page) ([]Rule, error) { - var s struct { - Rules []Rule `json:"firewall_rules"` - } - err := (r.(RulePage)).ExtractInto(&s) - return s.Rules, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a firewall rule. -func (r commonResult) Extract() (*Rule, error) { - var s struct { - Rule *Rule `json:"firewall_rule"` - } - err := r.ExtractInto(&s) - return s.Rule, err -} - -// GetResult represents the result of a get operation. Call its Extract method -// to interpret it as a Rule. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its Extract -// method to interpret it as a Rule. -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 -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a Rule. -type CreateResult struct { - commonResult -} diff --git a/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go b/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go deleted file mode 100644 index df31e6c5c3..0000000000 --- a/openstack/networking/v2/extensions/fwaas/rules/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// rules unit tests -package testing diff --git a/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go deleted file mode 100644 index 114483ebbe..0000000000 --- a/openstack/networking/v2/extensions/fwaas/rules/testing/requests_test.go +++ /dev/null @@ -1,382 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas/rules" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_rules", 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, ` -{ - "firewall_rules": [ - { - "protocol": "tcp", - "description": "ssh rule", - "source_port": null, - "source_ip_address": null, - "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", - "position": 2, - "destination_port": "22", - "id": "f03bd950-6c56-4f5e-a307-45967078f507", - "name": "ssh_form_any", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", - "enabled": true, - "action": "allow", - "ip_version": 4, - "shared": false - }, - { - "protocol": "udp", - "description": "udp rule", - "source_port": null, - "source_ip_address": null, - "destination_ip_address": null, - "firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", - "position": 1, - "destination_port": null, - "id": "ab7bd950-6c56-4f5e-a307-45967078f890", - "name": "deny_all_udp", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", - "enabled": true, - "action": "deny", - "ip_version": 4, - "shared": false - } - ] -} - `) - }) - - count := 0 - - rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := rules.ExtractRules(page) - if err != nil { - t.Errorf("Failed to extract members: %v", err) - return false, err - } - - expected := []rules.Rule{ - { - Protocol: "tcp", - Description: "ssh rule", - SourcePort: "", - SourceIPAddress: "", - DestinationIPAddress: "192.168.1.0/24", - PolicyID: "e2a5fb51-698c-4898-87e8-f1eee6b50919", - Position: 2, - DestinationPort: "22", - ID: "f03bd950-6c56-4f5e-a307-45967078f507", - Name: "ssh_form_any", - TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", - Enabled: true, - Action: "allow", - IPVersion: 4, - Shared: false, - }, - { - Protocol: "udp", - Description: "udp rule", - SourcePort: "", - SourceIPAddress: "", - DestinationIPAddress: "", - PolicyID: "98d7fb51-698c-4123-87e8-f1eee6b5ab7e", - Position: 1, - DestinationPort: "", - ID: "ab7bd950-6c56-4f5e-a307-45967078f890", - Name: "deny_all_udp", - TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", - Enabled: true, - Action: "deny", - IPVersion: 4, - Shared: false, - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_rules", 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, ` -{ - "firewall_rule": { - "protocol": "tcp", - "description": "ssh rule", - "destination_ip_address": "192.168.1.0/24", - "destination_port": "22", - "name": "ssh_form_any", - "action": "allow", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "firewall_rule":{ - "protocol": "tcp", - "description": "ssh rule", - "source_port": null, - "source_ip_address": null, - "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", - "position": 2, - "destination_port": "22", - "id": "f03bd950-6c56-4f5e-a307-45967078f507", - "name": "ssh_form_any", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", - "enabled": true, - "action": "allow", - "ip_version": 4, - "shared": false - } -} - `) - }) - - options := rules.CreateOpts{ - TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", - Protocol: rules.ProtocolTCP, - Description: "ssh rule", - DestinationIPAddress: "192.168.1.0/24", - DestinationPort: "22", - Name: "ssh_form_any", - Action: "allow", - } - - _, err := rules.Create(context.TODO(), fake.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) -} - -func TestCreateAnyProtocol(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_rules", 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, ` -{ - "firewall_rule": { - "protocol": null, - "description": "any to 192.168.1.0/24", - "destination_ip_address": "192.168.1.0/24", - "name": "any_to_192.168.1.0/24", - "action": "allow", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "firewall_rule":{ - "protocol": null, - "description": "any to 192.168.1.0/24", - "source_port": null, - "source_ip_address": null, - "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", - "position": 2, - "destination_port": null, - "id": "f03bd950-6c56-4f5e-a307-45967078f507", - "name": "any_to_192.168.1.0/24", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", - "enabled": true, - "action": "allow", - "ip_version": 4, - "shared": false - } -} - `) - }) - - options := rules.CreateOpts{ - TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61", - Protocol: rules.ProtocolAny, - Description: "any to 192.168.1.0/24", - DestinationIPAddress: "192.168.1.0/24", - Name: "any_to_192.168.1.0/24", - Action: "allow", - } - - _, err := rules.Create(context.TODO(), fake.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", 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, ` -{ - "firewall_rule":{ - "protocol": "tcp", - "description": "ssh rule", - "source_port": null, - "source_ip_address": null, - "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", - "position": 2, - "destination_port": "22", - "id": "f03bd950-6c56-4f5e-a307-45967078f507", - "name": "ssh_form_any", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", - "enabled": true, - "action": "allow", - "ip_version": 4, - "shared": false - } -} - `) - }) - - rule, err := rules.Get(context.TODO(), fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "tcp", rule.Protocol) - th.AssertEquals(t, "ssh rule", rule.Description) - th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress) - th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.PolicyID) - th.AssertEquals(t, 2, rule.Position) - th.AssertEquals(t, "22", rule.DestinationPort) - 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, true, rule.Enabled) - th.AssertEquals(t, "allow", rule.Action) - th.AssertEquals(t, 4, rule.IPVersion) - th.AssertEquals(t, false, rule.Shared) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", 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_rule":{ - "protocol": "tcp", - "description": "ssh rule", - "destination_ip_address": "192.168.1.0/24", - "destination_port": "22", - "name": "ssh_form_any", - "action": "allow", - "enabled": false - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "firewall_rule":{ - "protocol": "tcp", - "description": "ssh rule", - "destination_ip_address": "192.168.1.0/24", - "firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919", - "position": 2, - "destination_port": "22", - "id": "f03bd950-6c56-4f5e-a307-45967078f507", - "name": "ssh_form_any", - "tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61", - "enabled": false, - "action": "allow", - "ip_version": 4, - "shared": false - } -} - `) - }) - - newProtocol := "tcp" - newDescription := "ssh rule" - newDestinationIP := "192.168.1.0/24" - newDestintionPort := "22" - newName := "ssh_form_any" - newAction := "allow" - - options := rules.UpdateOpts{ - Protocol: &newProtocol, - Description: &newDescription, - DestinationIPAddress: &newDestinationIP, - DestinationPort: &newDestintionPort, - Name: &newName, - Action: &newAction, - Enabled: gophercloud.Disabled, - } - - _, err := rules.Update(context.TODO(), fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract() - th.AssertNoErr(t, err) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/fw/firewall_rules/4ec89077-d057-4a2b-911f-60a3b47ee304", 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) - }) - - res := rules.Delete(context.TODO(), fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/networking/v2/extensions/fwaas/rules/urls.go b/openstack/networking/v2/extensions/fwaas/rules/urls.go deleted file mode 100644 index 96212de7c5..0000000000 --- a/openstack/networking/v2/extensions/fwaas/rules/urls.go +++ /dev/null @@ -1,16 +0,0 @@ -package rules - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "fw" - resourcePath = "firewall_rules" -) - -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 2a39c62bac2029b80237ff17da108d7c410e0cb3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 21 Mar 2024 11:42:25 +0000 Subject: [PATCH 1791/2296] networking: Remove neutron LBaaS v1 This has been deprecated since the Mitaka release [1], nearly 8 years ago [2]. It has been replaced by Octavia. Users that still rely on this old feature can continue using Gophercloud v1, but we don't want to support this going forward. [1] https://wiki.openstack.org/wiki/Neutron/LBaaS/Deprecation [2] https://releases.openstack.org/mitaka/ Signed-off-by: Stephen Finucane --- .../networking/v2/extensions/lbaas/lbaas.go | 161 --------- .../v2/extensions/lbaas/members_test.go | 87 ----- .../v2/extensions/lbaas/monitors_test.go | 67 ---- .../v2/extensions/lbaas/pools_test.go | 123 ------- .../v2/extensions/lbaas/vips_test.go | 87 ----- .../networking/v2/extensions/lbaas/doc.go | 3 - .../v2/extensions/lbaas/members/doc.go | 59 ---- .../v2/extensions/lbaas/members/requests.go | 130 ------- .../v2/extensions/lbaas/members/results.go | 113 ------ .../extensions/lbaas/members/testing/doc.go | 2 - .../lbaas/members/testing/requests_test.go | 239 ------------- .../v2/extensions/lbaas/members/urls.go | 16 - .../v2/extensions/lbaas/monitors/doc.go | 63 ---- .../v2/extensions/lbaas/monitors/requests.go | 232 ------------ .../v2/extensions/lbaas/monitors/results.go | 145 -------- .../extensions/lbaas/monitors/testing/doc.go | 2 - .../lbaas/monitors/testing/requests_test.go | 311 ---------------- .../v2/extensions/lbaas/monitors/urls.go | 16 - .../v2/extensions/lbaas/pools/doc.go | 81 ----- .../v2/extensions/lbaas/pools/requests.go | 183 ---------- .../v2/extensions/lbaas/pools/results.go | 141 -------- .../v2/extensions/lbaas/pools/testing/doc.go | 2 - .../lbaas/pools/testing/requests_test.go | 318 ----------------- .../v2/extensions/lbaas/pools/urls.go | 25 -- .../v2/extensions/lbaas/vips/doc.go | 65 ---- .../v2/extensions/lbaas/vips/requests.go | 186 ---------- .../v2/extensions/lbaas/vips/results.go | 165 --------- .../v2/extensions/lbaas/vips/testing/doc.go | 2 - .../lbaas/vips/testing/requests_test.go | 331 ------------------ .../v2/extensions/lbaas/vips/urls.go | 16 - 30 files changed, 3371 deletions(-) delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/members/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/members/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas/members/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas/members/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas/members/urls.go delete mode 100644 openstack/networking/v2/extensions/lbaas/monitors/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/monitors/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas/monitors/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas/monitors/urls.go delete mode 100644 openstack/networking/v2/extensions/lbaas/pools/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/pools/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas/pools/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas/pools/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas/pools/urls.go delete mode 100644 openstack/networking/v2/extensions/lbaas/vips/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/vips/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas/vips/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas/vips/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas/vips/urls.go diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go deleted file mode 100644 index 50d07baada..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/lbaas.go +++ /dev/null @@ -1,161 +0,0 @@ -package lbaas - -import ( - "context" - "fmt" - "testing" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/members" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/monitors" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/pools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/vips" -) - -// CreateMember will create a load balancer member in a specified pool on a -// random port. An error will be returned if the member could not be created. -func CreateMember(t *testing.T, client *gophercloud.ServiceClient, poolID string) (*members.Member, error) { - protocolPort := tools.RandomInt(100, 1000) - address := tools.RandomInt(2, 200) - t.Logf("Attempting to create member in port %d", protocolPort) - - createOpts := members.CreateOpts{ - PoolID: poolID, - ProtocolPort: protocolPort, - Address: fmt.Sprintf("192.168.1.%d", address), - } - - member, err := members.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return member, err - } - - t.Logf("Successfully created member %s", member.ID) - - return member, nil -} - -// CreateMonitor will create a monitor with a random name for a specific pool. -// An error will be returned if the monitor could not be created. -func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient) (*monitors.Monitor, error) { - t.Logf("Attempting to create monitor.") - - createOpts := monitors.CreateOpts{ - Type: monitors.TypePING, - Delay: 90, - Timeout: 60, - MaxRetries: 10, - AdminStateUp: gophercloud.Enabled, - } - - monitor, err := monitors.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return monitor, err - } - - t.Logf("Successfully created monitor %s", monitor.ID) - - return monitor, nil -} - -// CreatePool will create a pool with a random name. An error will be returned -// if the pool could not be deleted. -func CreatePool(t *testing.T, client *gophercloud.ServiceClient, subnetID string) (*pools.Pool, error) { - poolName := tools.RandomString("TESTACCT-", 8) - - t.Logf("Attempting to create pool %s", poolName) - - createOpts := pools.CreateOpts{ - Name: poolName, - SubnetID: subnetID, - Protocol: pools.ProtocolTCP, - LBMethod: pools.LBMethodRoundRobin, - } - - pool, err := pools.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return pool, err - } - - t.Logf("Successfully created pool %s", poolName) - - return pool, nil -} - -// CreateVIP will create a vip with a random name and a random port in a -// specified subnet and pool. An error will be returned if the vip could -// not be created. -func CreateVIP(t *testing.T, client *gophercloud.ServiceClient, subnetID, poolID string) (*vips.VirtualIP, error) { - vipName := tools.RandomString("TESTACCT-", 8) - vipPort := tools.RandomInt(100, 10000) - - t.Logf("Attempting to create VIP %s", vipName) - - createOpts := vips.CreateOpts{ - Name: vipName, - SubnetID: subnetID, - PoolID: poolID, - Protocol: "TCP", - ProtocolPort: vipPort, - } - - vip, err := vips.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return vip, err - } - - t.Logf("Successfully created vip %s", vipName) - - return vip, nil -} - -// DeleteMember will delete a specified member. A fatal error will occur if -// the member could not be deleted. This works best when used as a deferred -// function. -func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, memberID string) { - t.Logf("Attempting to delete member %s", memberID) - - if err := members.Delete(context.TODO(), client, memberID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete member: %v", err) - } - - t.Logf("Successfully deleted member %s", memberID) -} - -// DeleteMonitor will delete a specified monitor. A fatal error will occur if -// the monitor could not be deleted. This works best when used as a deferred -// function. -func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, monitorID string) { - t.Logf("Attempting to delete monitor %s", monitorID) - - if err := monitors.Delete(context.TODO(), client, monitorID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete monitor: %v", err) - } - - t.Logf("Successfully deleted monitor %s", monitorID) -} - -// DeletePool will delete a specified pool. A fatal error will occur if the -// pool could not be deleted. This works best when used as a deferred function. -func DeletePool(t *testing.T, client *gophercloud.ServiceClient, poolID string) { - t.Logf("Attempting to delete pool %s", poolID) - - if err := pools.Delete(context.TODO(), client, poolID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete pool: %v", err) - } - - t.Logf("Successfully deleted pool %s", poolID) -} - -// DeleteVIP will delete a specified vip. A fatal error will occur if the vip -// could not be deleted. This works best when used as a deferred function. -func DeleteVIP(t *testing.T, client *gophercloud.ServiceClient, vipID string) { - t.Logf("Attempting to delete vip %s", vipID) - - if err := vips.Delete(context.TODO(), client, vipID).ExtractErr(); err != nil { - t.Fatalf("Unable to delete vip: %v", err) - } - - t.Logf("Successfully deleted vip %s", vipID) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go deleted file mode 100644 index f4f677d5e8..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/members_test.go +++ /dev/null @@ -1,87 +0,0 @@ -//go:build acceptance || networking || lbaas || member -// +build acceptance networking lbaas member - -package lbaas - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/members" -) - -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) - } - - allPages, err := members.List(client, members.ListOpts{}).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list members: %v", err) - } - - allMembers, err := members.ExtractMembers(allPages) - if err != nil { - t.Fatalf("Unable to extract members: %v", err) - } - - for _, member := range allMembers { - tools.PrintResource(t, member) - } -} - -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) - } - - network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } - defer networking.DeleteNetwork(t, client, network.ID) - - subnet, err := networking.CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } - defer networking.DeleteSubnet(t, client, subnet.ID) - - pool, err := CreatePool(t, client, subnet.ID) - if err != nil { - t.Fatalf("Unable to create pool: %v", err) - } - defer DeletePool(t, client, pool.ID) - - member, err := CreateMember(t, client, pool.ID) - if err != nil { - t.Fatalf("Unable to create member: %v", err) - } - defer DeleteMember(t, client, member.ID) - - tools.PrintResource(t, member) - - updateOpts := members.UpdateOpts{ - AdminStateUp: gophercloud.Enabled, - } - - _, err = members.Update(context.TODO(), client, member.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update member: %v", err) - } - - newMember, err := members.Get(context.TODO(), client, member.ID).Extract() - if err != nil { - t.Fatalf("Unable to get member: %v", err) - } - - tools.PrintResource(t, newMember) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go deleted file mode 100644 index 8adb4fc1bb..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/monitors_test.go +++ /dev/null @@ -1,67 +0,0 @@ -//go:build acceptance || networking || lbaas || monitors -// +build acceptance networking lbaas monitors - -package lbaas - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/monitors" -) - -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) - } - - allPages, err := monitors.List(client, monitors.ListOpts{}).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list monitors: %v", err) - } - - allMonitors, err := monitors.ExtractMonitors(allPages) - if err != nil { - t.Fatalf("Unable to extract monitors: %v", err) - } - - for _, monitor := range allMonitors { - tools.PrintResource(t, monitor) - } -} - -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) - } - - monitor, err := CreateMonitor(t, client) - if err != nil { - t.Fatalf("Unable to create monitor: %v", err) - } - defer DeleteMonitor(t, client, monitor.ID) - - tools.PrintResource(t, monitor) - - updateOpts := monitors.UpdateOpts{ - Delay: 999, - } - - _, err = monitors.Update(context.TODO(), client, monitor.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update monitor: %v", err) - } - - newMonitor, err := monitors.Get(context.TODO(), client, monitor.ID).Extract() - if err != nil { - t.Fatalf("Unable to get monitor: %v", err) - } - - tools.PrintResource(t, newMonitor) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go deleted file mode 100644 index 61fecc36a2..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/pools_test.go +++ /dev/null @@ -1,123 +0,0 @@ -//go:build acceptance || networking || lbaas || pool -// +build acceptance networking lbaas pool - -package lbaas - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/pools" -) - -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) - } - - allPages, err := pools.List(client, pools.ListOpts{}).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list pools: %v", err) - } - - allPools, err := pools.ExtractPools(allPages) - if err != nil { - t.Fatalf("Unable to extract pools: %v", err) - } - - for _, pool := range allPools { - tools.PrintResource(t, pool) - } -} - -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) - } - - network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } - defer networking.DeleteNetwork(t, client, network.ID) - - subnet, err := networking.CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } - defer networking.DeleteSubnet(t, client, subnet.ID) - - pool, err := CreatePool(t, client, subnet.ID) - if err != nil { - t.Fatalf("Unable to create pool: %v", err) - } - defer DeletePool(t, client, pool.ID) - - tools.PrintResource(t, pool) - - updateOpts := pools.UpdateOpts{ - LBMethod: pools.LBMethodLeastConnections, - } - - _, err = pools.Update(context.TODO(), client, pool.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update pool: %v", err) - } - - newPool, err := pools.Get(context.TODO(), client, pool.ID).Extract() - if err != nil { - t.Fatalf("Unable to get pool: %v", err) - } - - tools.PrintResource(t, newPool) -} - -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) - } - - network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } - defer networking.DeleteNetwork(t, client, network.ID) - - subnet, err := networking.CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } - defer networking.DeleteSubnet(t, client, subnet.ID) - - pool, err := CreatePool(t, client, subnet.ID) - if err != nil { - t.Fatalf("Unable to create pool: %v", err) - } - defer DeletePool(t, client, pool.ID) - - monitor, err := CreateMonitor(t, client) - if err != nil { - t.Fatalf("Unable to create monitor: %v", err) - } - defer DeleteMonitor(t, client, monitor.ID) - - t.Logf("Associating monitor %s with pool %s", monitor.ID, pool.ID) - if res := pools.AssociateMonitor(context.TODO(), client, pool.ID, monitor.ID); res.Err != nil { - t.Fatalf("Unable to associate monitor to pool") - } - - t.Logf("Disassociating monitor %s with pool %s", monitor.ID, pool.ID) - if res := pools.DisassociateMonitor(context.TODO(), client, pool.ID, monitor.ID); res.Err != nil { - t.Fatalf("Unable to disassociate monitor from pool") - } - -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go deleted file mode 100644 index 05f1840a4c..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas/vips_test.go +++ /dev/null @@ -1,87 +0,0 @@ -//go:build acceptance || networking || lbaas || vip -// +build acceptance networking lbaas vip - -package lbaas - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/vips" -) - -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) - } - - allPages, err := vips.List(client, vips.ListOpts{}).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list vips: %v", err) - } - - allVIPs, err := vips.ExtractVIPs(allPages) - if err != nil { - t.Fatalf("Unable to extract vips: %v", err) - } - - for _, vip := range allVIPs { - tools.PrintResource(t, vip) - } -} - -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) - } - - network, err := networking.CreateNetwork(t, client) - if err != nil { - t.Fatalf("Unable to create network: %v", err) - } - defer networking.DeleteNetwork(t, client, network.ID) - - subnet, err := networking.CreateSubnet(t, client, network.ID) - if err != nil { - t.Fatalf("Unable to create subnet: %v", err) - } - defer networking.DeleteSubnet(t, client, subnet.ID) - - pool, err := CreatePool(t, client, subnet.ID) - if err != nil { - t.Fatalf("Unable to create pool: %v", err) - } - defer DeletePool(t, client, pool.ID) - - vip, err := CreateVIP(t, client, subnet.ID, pool.ID) - if err != nil { - t.Fatalf("Unable to create vip: %v", err) - } - defer DeleteVIP(t, client, vip.ID) - - tools.PrintResource(t, vip) - - connLimit := 100 - updateOpts := vips.UpdateOpts{ - ConnLimit: &connLimit, - } - - _, err = vips.Update(context.TODO(), client, vip.ID, updateOpts).Extract() - if err != nil { - t.Fatalf("Unable to update vip: %v", err) - } - - newVIP, err := vips.Get(context.TODO(), client, vip.ID).Extract() - if err != nil { - t.Fatalf("Unable to get vip: %v", err) - } - - tools.PrintResource(t, newVIP) -} diff --git a/openstack/networking/v2/extensions/lbaas/doc.go b/openstack/networking/v2/extensions/lbaas/doc.go deleted file mode 100644 index bc1fc282f4..0000000000 --- a/openstack/networking/v2/extensions/lbaas/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package lbaas provides information and interaction with the Load Balancer -// as a Service extension for the OpenStack Networking service. -package lbaas diff --git a/openstack/networking/v2/extensions/lbaas/members/doc.go b/openstack/networking/v2/extensions/lbaas/members/doc.go deleted file mode 100644 index bad3324b5e..0000000000 --- a/openstack/networking/v2/extensions/lbaas/members/doc.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Package members provides information and interaction with Members of the -Load Balancer as a Service extension for the OpenStack Networking service. - -Example to List Members - - listOpts := members.ListOpts{ - ProtocolPort: 80, - } - - allPages, err := members.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allMembers, err := members.ExtractMembers(allPages) - if err != nil { - panic(err) - } - - for _, member := range allMembers { - fmt.Printf("%+v\n", member) - } - -Example to Create a Member - - createOpts := members.CreateOpts{ - Address: "192.168.2.14", - ProtocolPort: 80, - PoolID: "0b266a12-0fdf-4434-bd11-649d84e54bd5" - } - - member, err := members.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Member - - memberID := "46592c54-03f7-40ef-9cdf-b1fcf2775ddf" - - updateOpts := members.UpdateOpts{ - AdminStateUp: gophercloud.Disabled, - } - - member, err := members.Update(networkClient, memberID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Member - - memberID := "46592c54-03f7-40ef-9cdf-b1fcf2775ddf" - err := members.Delete(networkClient, memberID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package members diff --git a/openstack/networking/v2/extensions/lbaas/members/requests.go b/openstack/networking/v2/extensions/lbaas/members/requests.go deleted file mode 100644 index b5ad4b5c5c..0000000000 --- a/openstack/networking/v2/extensions/lbaas/members/requests.go +++ /dev/null @@ -1,130 +0,0 @@ -package members - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOpts allows the filtering and sorting of paginated collections through -// the API. Filtering is achieved by passing in struct field values that map to -// the floating IP attributes you want to see returned. SortKey allows you to -// sort by a particular network attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. -type ListOpts struct { - Status string `q:"status"` - Weight int `q:"weight"` - AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` - PoolID string `q:"pool_id"` - Address string `q:"address"` - ProtocolPort int `q:"protocol_port"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// List returns a Pager which allows you to iterate over a collection of -// members. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. -// -// Default policy settings return only those members that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { - q, err := gophercloud.BuildQueryString(&opts) - if err != nil { - return pagination.Pager{Err: err} - } - u := rootURL(c) + q.String() - return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { - return MemberPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToLBMemberCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains all the values needed to create a new pool member. -type CreateOpts struct { - // Address is the IP address of the member. - Address string `json:"address" required:"true"` - - // ProtocolPort is the port on which the application is hosted. - ProtocolPort int `json:"protocol_port" required:"true"` - - // PoolID is the pool to which this member will belong. - PoolID string `json:"pool_id" required:"true"` - - // TenantID is only required if the caller has an admin role and wants - // to create a pool for another tenant. - TenantID string `json:"tenant_id,omitempty"` -} - -// ToLBMemberCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToLBMemberCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "member") -} - -// Create accepts a CreateOpts struct and uses the values to create a new -// load balancer pool member. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToLBMemberCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular pool member based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToLBMemberUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contains the values used when updating a pool member. -type UpdateOpts struct { - // The administrative state of the member, which is up (true) or down (false). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToLBMemberUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToLBMemberUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "member") -} - -// Update allows members to be updated. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToLBMemberUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will permanently delete a particular member based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas/members/results.go b/openstack/networking/v2/extensions/lbaas/members/results.go deleted file mode 100644 index 05eff303ab..0000000000 --- a/openstack/networking/v2/extensions/lbaas/members/results.go +++ /dev/null @@ -1,113 +0,0 @@ -package members - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Member represents the application running on a backend server. -type Member struct { - // Status is the status of the member. Indicates whether the member - // is operational. - Status string - - // Weight is the weight of member. - Weight int - - // AdminStateUp is the administrative state of the member, which is up - // (true) or down (false). - AdminStateUp bool `json:"admin_state_up"` - - // TenantID is the owner of the member. - TenantID string `json:"tenant_id"` - - // PoolID is the pool to which the member belongs. - PoolID string `json:"pool_id"` - - // Address is the IP address of the member. - Address string - - // ProtocolPort is the port on which the application is hosted. - ProtocolPort int `json:"protocol_port"` - - // ID is the unique ID for the member. - ID string -} - -// MemberPage is the page returned by a pager when traversing over a -// collection of pool members. -type MemberPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of members 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 MemberPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"members_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractMembers accepts a Page struct, specifically a MemberPage struct, -// and extracts the elements into a slice of Member structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractMembers(r pagination.Page) ([]Member, error) { - var s struct { - Members []Member `json:"members"` - } - err := (r.(MemberPage)).ExtractInto(&s) - return s.Members, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a member. -func (r commonResult) Extract() (*Member, error) { - var s struct { - Member *Member `json:"member"` - } - err := r.ExtractInto(&s) - return s.Member, err -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a Member. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. Call its Extract -// method to interpret it as a Member. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its Extract -// method to interpret it as a Member. -type UpdateResult struct { - commonResult -} - -// DeleteResult represents the result of a delete operation. Call its -// ExtractErr method to determine if the result succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} diff --git a/openstack/networking/v2/extensions/lbaas/members/testing/doc.go b/openstack/networking/v2/extensions/lbaas/members/testing/doc.go deleted file mode 100644 index 1afbc434f6..0000000000 --- a/openstack/networking/v2/extensions/lbaas/members/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// members unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go deleted file mode 100644 index 5af53d8fa8..0000000000 --- a/openstack/networking/v2/extensions/lbaas/members/testing/requests_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/members" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/members", 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, ` -{ - "members":[ - { - "status":"ACTIVE", - "weight":1, - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "pool_id":"72741b06-df4d-4715-b142-276b6bce75ab", - "address":"10.0.0.4", - "protocol_port":80, - "id":"701b531b-111a-4f21-ad85-4795b7b12af6" - }, - { - "status":"ACTIVE", - "weight":1, - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "pool_id":"72741b06-df4d-4715-b142-276b6bce75ab", - "address":"10.0.0.3", - "protocol_port":80, - "id":"beb53b4d-230b-4abd-8118-575b8fa006ef" - } - ] -} - `) - }) - - count := 0 - - members.List(fake.ServiceClient(), members.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := members.ExtractMembers(page) - if err != nil { - t.Errorf("Failed to extract members: %v", err) - return false, err - } - - expected := []members.Member{ - { - Status: "ACTIVE", - Weight: 1, - AdminStateUp: true, - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - PoolID: "72741b06-df4d-4715-b142-276b6bce75ab", - Address: "10.0.0.4", - ProtocolPort: 80, - ID: "701b531b-111a-4f21-ad85-4795b7b12af6", - }, - { - Status: "ACTIVE", - Weight: 1, - AdminStateUp: true, - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - PoolID: "72741b06-df4d-4715-b142-276b6bce75ab", - Address: "10.0.0.3", - ProtocolPort: 80, - ID: "beb53b4d-230b-4abd-8118-575b8fa006ef", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/members", 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, ` -{ - "member": { - "tenant_id": "453105b9-1754-413f-aab1-55f1af620750", - "pool_id": "foo", - "address": "192.0.2.14", - "protocol_port":8080 - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "member": { - "id": "975592ca-e308-48ad-8298-731935ee9f45", - "address": "192.0.2.14", - "protocol_port": 8080, - "tenant_id": "453105b9-1754-413f-aab1-55f1af620750", - "admin_state_up":true, - "weight": 1, - "status": "DOWN" - } -} - `) - }) - - options := members.CreateOpts{ - TenantID: "453105b9-1754-413f-aab1-55f1af620750", - Address: "192.0.2.14", - ProtocolPort: 8080, - PoolID: "foo", - } - _, err := members.Create(context.TODO(), fake.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/members/975592ca-e308-48ad-8298-731935ee9f45", 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, ` -{ - "member":{ - "id":"975592ca-e308-48ad-8298-731935ee9f45", - "address":"192.0.2.14", - "protocol_port":8080, - "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", - "admin_state_up":true, - "weight":1, - "status":"DOWN" - } -} - `) - }) - - m, err := members.Get(context.TODO(), fake.ServiceClient(), "975592ca-e308-48ad-8298-731935ee9f45").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "975592ca-e308-48ad-8298-731935ee9f45", m.ID) - th.AssertEquals(t, "192.0.2.14", m.Address) - th.AssertEquals(t, 8080, m.ProtocolPort) - th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", m.TenantID) - th.AssertEquals(t, true, m.AdminStateUp) - th.AssertEquals(t, 1, m.Weight) - th.AssertEquals(t, "DOWN", m.Status) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", 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, ` -{ - "member":{ - "admin_state_up":false - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "member":{ - "status":"PENDING_UPDATE", - "protocol_port":8080, - "weight":1, - "admin_state_up":false, - "tenant_id":"4fd44f30292945e481c7b8a0c8908869", - "pool_id":"7803631d-f181-4500-b3a2-1b68ba2a75fd", - "address":"10.0.0.5", - "status_description":null, - "id":"48a471ea-64f1-4eb6-9be7-dae6bbe40a0f" - } -} - `) - }) - - options := members.UpdateOpts{AdminStateUp: gophercloud.Disabled} - - _, err := members.Update(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() - th.AssertNoErr(t, err) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/members/332abe93-f488-41ba-870b-2ac66be7f853", 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) - }) - - res := members.Delete(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/networking/v2/extensions/lbaas/members/urls.go b/openstack/networking/v2/extensions/lbaas/members/urls.go deleted file mode 100644 index 0136bd430e..0000000000 --- a/openstack/networking/v2/extensions/lbaas/members/urls.go +++ /dev/null @@ -1,16 +0,0 @@ -package members - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lb" - resourcePath = "members" -) - -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/networking/v2/extensions/lbaas/monitors/doc.go b/openstack/networking/v2/extensions/lbaas/monitors/doc.go deleted file mode 100644 index b5c0f29f05..0000000000 --- a/openstack/networking/v2/extensions/lbaas/monitors/doc.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Package monitors provides information and interaction with the Monitors -of the Load Balancer as a Service extension for the OpenStack Networking -Service. - -Example to List Monitors - - listOpts: monitors.ListOpts{ - Type: "HTTP", - } - - allPages, err := monitors.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allMonitors, err := monitors.ExtractMonitors(allPages) - if err != nil { - panic(err) - } - - for _, monitor := range allMonitors { - fmt.Printf("%+v\n", monitor) - } - -Example to Create a Monitor - - createOpts := monitors.CreateOpts{ - Type: "HTTP", - Delay: 20, - Timeout: 20, - MaxRetries: 5, - URLPath: "/check", - ExpectedCodes: "200-299", - } - - monitor, err := monitors.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Monitor - - monitorID := "681aed03-aadb-43ae-aead-b9016375650a" - - updateOpts := monitors.UpdateOpts{ - Timeout: 30, - } - - monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Member - - monitorID := "681aed03-aadb-43ae-aead-b9016375650a" - err := monitors.Delete(networkClient, monitorID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package monitors diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go deleted file mode 100644 index fa23b85db6..0000000000 --- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go +++ /dev/null @@ -1,232 +0,0 @@ -package monitors - -import ( - "context" - "fmt" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOpts allows the filtering and sorting of paginated collections through -// the API. Filtering is achieved by passing in struct field values that map to -// the floating IP attributes you want to see returned. SortKey allows you to -// sort by a particular network attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. -type ListOpts struct { - ID string `q:"id"` - TenantID string `q:"tenant_id"` - Type string `q:"type"` - Delay int `q:"delay"` - Timeout int `q:"timeout"` - MaxRetries int `q:"max_retries"` - 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"` -} - -// List returns a Pager which allows you to iterate over a collection of -// monitors. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. -// -// Default policy settings return only those monitors that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { - q, err := gophercloud.BuildQueryString(&opts) - if err != nil { - return pagination.Pager{Err: err} - } - u := rootURL(c) + q.String() - return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { - return MonitorPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// MonitorType is the type for all the types of LB monitors. -type MonitorType string - -// Constants that represent approved monitoring types. -const ( - TypePING MonitorType = "PING" - TypeTCP MonitorType = "TCP" - TypeHTTP MonitorType = "HTTP" - TypeHTTPS MonitorType = "HTTPS" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToLBMonitorCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains all the values needed to create a new health monitor. -type CreateOpts struct { - // MonitorType is the type of probe, which is PING, TCP, HTTP, or HTTPS, - // that is sent by the load balancer to verify the member state. - Type MonitorType `json:"type" required:"true"` - - // Delay is the time, in seconds, between sending probes to members. - Delay int `json:"delay" required:"true"` - - // Timeout is the 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"` - - // MaxRetries is the number of permissible ping failures before changing the - // member's status to INACTIVE. Must be a number between 1 and 10. - MaxRetries int `json:"max_retries" required:"true"` - - // URLPath is the URI path that will be accessed if monitor type - // is HTTP or HTTPS. Required for HTTP(S) types. - URLPath string `json:"url_path,omitempty"` - - // HTTPMethod is the HTTP method used for requests by the monitor. If this - // attribute is not specified, it defaults to "GET". Required for HTTP(S) - // types. - HTTPMethod string `json:"http_method,omitempty"` - - // ExpectedCodes is the 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. - ExpectedCodes string `json:"expected_codes,omitempty"` - - // TenantID is only required if the caller has an admin role and wants - // to create a pool for another tenant. - TenantID string `json:"tenant_id,omitempty"` - - // AdminStateUp denotes whether the monitor is administratively up or down. - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToLBMonitorCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToLBMonitorCreateMap() (map[string]interface{}, error) { - if opts.Type == TypeHTTP || opts.Type == TypeHTTPS { - if opts.URLPath == "" { - err := gophercloud.ErrMissingInput{} - err.Argument = "monitors.CreateOpts.URLPath" - return nil, err - } - if opts.ExpectedCodes == "" { - err := gophercloud.ErrMissingInput{} - err.Argument = "monitors.CreateOpts.ExpectedCodes" - return nil, err - } - } - if opts.Delay < opts.Timeout { - err := gophercloud.ErrInvalidInput{} - err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout" - err.Info = "Delay must be greater than or equal to timeout" - return nil, err - } - return gophercloud.BuildRequestBody(opts, "health_monitor") -} - -// 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: -// -// 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: -// -// CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, -// -// HttpMethod: "HEAD", ExpectedCodes: "200"} -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToLBMonitorCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular health monitor based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToLBMonitorUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contains all the values needed to update an existing monitor. -// Attributes not listed here but appear in CreateOpts are immutable and cannot -// be updated. -type UpdateOpts struct { - // Delay is the time, in seconds, between sending probes to members. - Delay int `json:"delay,omitempty"` - - // Timeout is the 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"` - - // MaxRetries is the number of permissible ping failures before changing the - // member's status to INACTIVE. Must be a number between 1 and 10. - MaxRetries int `json:"max_retries,omitempty"` - - // URLPath is the URI path that will be accessed if monitor type - // is HTTP or HTTPS. - URLPath string `json:"url_path,omitempty"` - - // HTTPMethod is the HTTP method used for requests by the monitor. If this - // attribute is not specified, it defaults to "GET". - HTTPMethod string `json:"http_method,omitempty"` - - // ExpectedCodes is the expected HTTP codes for a passing HTTP(S) monitor - // You can either specify a single status like "200", or a range like - // "200-202". - ExpectedCodes string `json:"expected_codes,omitempty"` - - // AdminStateUp denotes whether the monitor is administratively up or down. - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToLBMonitorUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToLBMonitorUpdateMap() (map[string]interface{}, error) { - if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout { - err := gophercloud.ErrInvalidInput{} - err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout" - err.Value = fmt.Sprintf("%d/%d", opts.Delay, opts.Timeout) - err.Info = "Delay must be greater than or equal to timeout" - return nil, err - } - return gophercloud.BuildRequestBody(opts, "health_monitor") -} - -// Update is an operation which modifies the attributes of the specified -// monitor. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToLBMonitorUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, 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 monitor based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas/monitors/results.go b/openstack/networking/v2/extensions/lbaas/monitors/results.go deleted file mode 100644 index 2b55dc7c67..0000000000 --- a/openstack/networking/v2/extensions/lbaas/monitors/results.go +++ /dev/null @@ -1,145 +0,0 @@ -package monitors - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Monitor represents a load balancer health monitor. A health monitor is used -// to determine whether or not back-end members of the VIP's pool are usable -// for processing a request. A pool can have several health monitors associated -// with it. There are different types of health monitors supported: -// -// PING: used to ping the members using ICMP. -// TCP: used to connect to the members using TCP. -// HTTP: used to send an HTTP request to the member. -// HTTPS: used to send a secure HTTP request to the member. -// -// When a pool has several monitors associated with it, each member of the pool -// is monitored by all these monitors. If any monitor declares the member as -// unhealthy, then the member status is changed to INACTIVE and the member -// won't participate in its pool's load balancing. In other words, ALL monitors -// must declare the member to be healthy for it to stay ACTIVE. -type Monitor struct { - // ID is the unique ID for the Monitor. - ID string - - // Name is the monitor name. Does not have to be unique. - Name string - - // TenantID is the owner of the Monitor. - TenantID string `json:"tenant_id"` - - // Type is the type of probe sent by the load balancer to verify the member - // state, which is PING, TCP, HTTP, or HTTPS. - Type string - - // Delay is the time, in seconds, between sending probes to members. - Delay int - - // Timeout is 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. - Timeout int - - // MaxRetries is the number of allowed connection failures before changing the - // status of the member to INACTIVE. A valid value is from 1 to 10. - MaxRetries int `json:"max_retries"` - - // HTTPMethod is the HTTP method that the monitor uses for requests. - HTTPMethod string `json:"http_method"` - - // URLPath is 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"` - - // ExpectedCodes is the expected HTTP codes for a passing HTTP(S) monitor. - ExpectedCodes string `json:"expected_codes"` - - // AdminStateUp is the administrative state of the health monitor, which is up - // (true) or down (false). - AdminStateUp bool `json:"admin_state_up"` - - // Status is the status of the health monitor. Indicates whether the health - // monitor is operational. - Status string -} - -// MonitorPage is the page returned by a pager when traversing over a -// collection of health monitors. -type MonitorPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of monitors 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 MonitorPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"health_monitors_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractMonitors accepts a Page struct, specifically a MonitorPage struct, -// and extracts the elements into a slice of Monitor structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractMonitors(r pagination.Page) ([]Monitor, error) { - var s struct { - Monitors []Monitor `json:"health_monitors"` - } - err := (r.(MonitorPage)).ExtractInto(&s) - return s.Monitors, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a monitor. -func (r commonResult) Extract() (*Monitor, error) { - var s struct { - Monitor *Monitor `json:"health_monitor"` - } - err := r.ExtractInto(&s) - return s.Monitor, err -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a Monitor. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. Call its Extract -// method to interpret it as a Monitor. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its Extract -// method to interpret it as a Monitor. -type UpdateResult struct { - commonResult -} - -// DeleteResult represents the result of a delete operation. Call its Extract -// method to determine if the request succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} diff --git a/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go b/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go deleted file mode 100644 index e2b6f12a92..0000000000 --- a/openstack/networking/v2/extensions/lbaas/monitors/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// monitors unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go deleted file mode 100644 index 8537df8b02..0000000000 --- a/openstack/networking/v2/extensions/lbaas/monitors/testing/requests_test.go +++ /dev/null @@ -1,311 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/monitors" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/health_monitors", 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, ` -{ - "health_monitors":[ - { - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "delay":10, - "max_retries":1, - "timeout":1, - "type":"PING", - "id":"466c8345-28d8-4f84-a246-e04380b0461d" - }, - { - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "delay":5, - "expected_codes":"200", - "max_retries":2, - "http_method":"GET", - "timeout":2, - "url_path":"/", - "type":"HTTP", - "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" - } - ] -} - `) - }) - - count := 0 - - monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := monitors.ExtractMonitors(page) - if err != nil { - t.Errorf("Failed to extract monitors: %v", err) - return false, err - } - - expected := []monitors.Monitor{ - { - AdminStateUp: true, - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - Delay: 10, - MaxRetries: 1, - Timeout: 1, - Type: "PING", - ID: "466c8345-28d8-4f84-a246-e04380b0461d", - }, - { - AdminStateUp: true, - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - Delay: 5, - ExpectedCodes: "200", - MaxRetries: 2, - Timeout: 2, - URLPath: "/", - Type: "HTTP", - HTTPMethod: "GET", - ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { - _, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ - Type: "HTTP", - Delay: 1, - Timeout: 10, - MaxRetries: 5, - URLPath: "/check", - ExpectedCodes: "200-299", - }).Extract() - - if err == nil { - t.Fatalf("Expected error, got none") - } - - _, err = monitors.Update(context.TODO(), fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ - Delay: 1, - Timeout: 10, - }).Extract() - - if err == nil { - t.Fatalf("Expected error, got none") - } -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/health_monitors", 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, ` -{ - "health_monitor":{ - "type":"HTTP", - "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", - "delay":20, - "timeout":10, - "max_retries":5, - "url_path":"/check", - "expected_codes":"200-299" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "health_monitor":{ - "id":"f3eeab00-8367-4524-b662-55e64d4cacb5", - "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", - "type":"HTTP", - "delay":20, - "timeout":10, - "max_retries":5, - "http_method":"GET", - "url_path":"/check", - "expected_codes":"200-299", - "admin_state_up":true, - "status":"ACTIVE" - } -} - `) - }) - - _, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ - Type: "HTTP", - TenantID: "453105b9-1754-413f-aab1-55f1af620750", - Delay: 20, - Timeout: 10, - MaxRetries: 5, - URLPath: "/check", - ExpectedCodes: "200-299", - }).Extract() - - th.AssertNoErr(t, err) -} - -func TestRequiredCreateOpts(t *testing.T) { - res := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/health_monitors/f3eeab00-8367-4524-b662-55e64d4cacb5", 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, ` -{ - "health_monitor":{ - "id":"f3eeab00-8367-4524-b662-55e64d4cacb5", - "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", - "type":"HTTP", - "delay":20, - "timeout":10, - "max_retries":5, - "http_method":"GET", - "url_path":"/check", - "expected_codes":"200-299", - "admin_state_up":true, - "status":"ACTIVE" - } -} - `) - }) - - hm, err := monitors.Get(context.TODO(), fake.ServiceClient(), "f3eeab00-8367-4524-b662-55e64d4cacb5").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "f3eeab00-8367-4524-b662-55e64d4cacb5", hm.ID) - th.AssertEquals(t, "453105b9-1754-413f-aab1-55f1af620750", hm.TenantID) - th.AssertEquals(t, "HTTP", hm.Type) - th.AssertEquals(t, 20, hm.Delay) - th.AssertEquals(t, 10, hm.Timeout) - th.AssertEquals(t, 5, hm.MaxRetries) - th.AssertEquals(t, "GET", hm.HTTPMethod) - th.AssertEquals(t, "/check", hm.URLPath) - th.AssertEquals(t, "200-299", hm.ExpectedCodes) - th.AssertEquals(t, true, hm.AdminStateUp) - th.AssertEquals(t, "ACTIVE", hm.Status) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", 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, ` -{ - "health_monitor":{ - "delay": 30, - "timeout": 20, - "max_retries": 10, - "url_path": "/another_check", - "expected_codes": "301", - "admin_state_up": true - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, ` -{ - "health_monitor": { - "admin_state_up": true, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "delay": 30, - "max_retries": 10, - "http_method": "GET", - "timeout": 20, - "pools": [ - { - "status": "PENDING_CREATE", - "status_description": null, - "pool_id": "6e55751f-6ad4-4e53-b8d4-02e442cd21df" - } - ], - "type": "PING", - "id": "b05e44b5-81f9-4551-b474-711a722698f7" - } -} - `) - }) - - _, err := monitors.Update(context.TODO(), fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", monitors.UpdateOpts{ - Delay: 30, - Timeout: 20, - MaxRetries: 10, - URLPath: "/another_check", - ExpectedCodes: "301", - AdminStateUp: gophercloud.Enabled, - }).Extract() - - th.AssertNoErr(t, err) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/health_monitors/b05e44b5-81f9-4551-b474-711a722698f7", 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) - }) - - res := monitors.Delete(context.TODO(), fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/networking/v2/extensions/lbaas/monitors/urls.go b/openstack/networking/v2/extensions/lbaas/monitors/urls.go deleted file mode 100644 index 35310dd08d..0000000000 --- a/openstack/networking/v2/extensions/lbaas/monitors/urls.go +++ /dev/null @@ -1,16 +0,0 @@ -package monitors - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lb" - resourcePath = "health_monitors" -) - -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/networking/v2/extensions/lbaas/pools/doc.go b/openstack/networking/v2/extensions/lbaas/pools/doc.go deleted file mode 100644 index 25c4204dc9..0000000000 --- a/openstack/networking/v2/extensions/lbaas/pools/doc.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Package pools provides information and interaction with the Pools of the -Load Balancing as a Service extension for the OpenStack Networking service. - -Example to List Pools - - listOpts := pools.ListOpts{ - SubnetID: "d9bd223b-f1a9-4f98-953b-df977b0f902d", - } - - allPages, err := pools.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allPools, err := pools.ExtractPools(allPages) - if err != nil { - panic(err) - } - - for _, pool := range allPools { - fmt.Printf("%+v\n", pool) - } - -Example to Create a Pool - - createOpts := pools.CreateOpts{ - LBMethod: pools.LBMethodRoundRobin, - Protocol: "HTTP", - Name: "Example pool", - SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - Provider: "haproxy", - } - - pool, err := pools.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Pool - - poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" - - updateOpts := pools.UpdateOpts{ - LBMethod: pools.LBMethodLeastConnections, - } - - pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Pool - - poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" - err := pools.Delete(networkClient, poolID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Associate a Monitor to a Pool - - poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" - monitorID := "8bbfbe1c-6faa-4d97-abdb-0df6c90df70b" - - pool, err := pools.AssociateMonitor(networkClient, poolID, monitorID).Extract() - if err != nil { - panic(err) - } - -Example to Disassociate a Monitor from a Pool - - poolID := "166db5e6-c72a-4d77-8776-3573e27ae271" - monitorID := "8bbfbe1c-6faa-4d97-abdb-0df6c90df70b" - - pool, err := pools.DisassociateMonitor(networkClient, poolID, monitorID).Extract() - if err != nil { - panic(err) - } -*/ -package pools diff --git a/openstack/networking/v2/extensions/lbaas/pools/requests.go b/openstack/networking/v2/extensions/lbaas/pools/requests.go deleted file mode 100644 index ec02374f70..0000000000 --- a/openstack/networking/v2/extensions/lbaas/pools/requests.go +++ /dev/null @@ -1,183 +0,0 @@ -package pools - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOpts allows the filtering and sorting of paginated collections through -// the API. Filtering is achieved by passing in struct field values that map to -// the floating IP attributes you want to see returned. SortKey allows you to -// sort by a particular network attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. -type ListOpts struct { - Status string `q:"status"` - LBMethod string `q:"lb_method"` - Protocol string `q:"protocol"` - SubnetID string `q:"subnet_id"` - TenantID string `q:"tenant_id"` - AdminStateUp *bool `q:"admin_state_up"` - Name string `q:"name"` - ID string `q:"id"` - VIPID string `q:"vip_id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// List returns a Pager which allows you to iterate over a collection of -// pools. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. -// -// Default policy settings return only those pools that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { - q, err := gophercloud.BuildQueryString(&opts) - if err != nil { - return pagination.Pager{Err: err} - } - u := rootURL(c) + q.String() - return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { - return PoolPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// LBMethod is a type used for possible load balancing methods. -type LBMethod string - -// LBProtocol is a type used for possible load balancing protocols. -type LBProtocol string - -// Supported attributes for create/update operations. -const ( - LBMethodRoundRobin LBMethod = "ROUND_ROBIN" - LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS" - - ProtocolTCP LBProtocol = "TCP" - ProtocolHTTP LBProtocol = "HTTP" - ProtocolHTTPS LBProtocol = "HTTPS" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToLBPoolCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains all the values needed to create a new pool. -type CreateOpts struct { - // Name of the pool. - Name string `json:"name" required:"true"` - - // Protocol used by the pool members, you can use either - // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. - Protocol LBProtocol `json:"protocol" required:"true"` - - // TenantID is only required if the caller has an admin role and wants - // to create a pool for another tenant. - TenantID string `json:"tenant_id,omitempty"` - - // SubnetID is the network on which the members of the pool will be located. - // Only members that are on this network can be added to the pool. - SubnetID string `json:"subnet_id,omitempty"` - - // LBMethod is the algorithm used to distribute load between the members of - // the pool. The current specification supports LBMethodRoundRobin and - // LBMethodLeastConnections as valid values for this attribute. - LBMethod LBMethod `json:"lb_method" required:"true"` - - // Provider of the pool. - Provider string `json:"provider,omitempty"` -} - -// ToLBPoolCreateMap builds a request body based on CreateOpts. -func (opts CreateOpts) ToLBPoolCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "pool") -} - -// Create accepts a CreateOptsBuilder and uses the values to create a new -// load balancer pool. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToLBPoolCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular pool based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters ot the -// Update request. -type UpdateOptsBuilder interface { - ToLBPoolUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contains the values used when updating a pool. -type UpdateOpts struct { - // Name of the pool. - Name *string `json:"name,omitempty"` - - // LBMethod is the algorithm used to distribute load between the members of - // the pool. The current specification supports LBMethodRoundRobin and - // LBMethodLeastConnections as valid values for this attribute. - LBMethod LBMethod `json:"lb_method,omitempty"` -} - -// ToLBPoolUpdateMap builds a request body based on UpdateOpts. -func (opts UpdateOpts) ToLBPoolUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "pool") -} - -// Update allows pools to be updated. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToLBPoolUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will permanently delete a particular pool based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// AssociateMonitor will associate a health monitor with a particular pool. -// Once associated, the health monitor will start monitoring the members of the -// pool and will deactivate these members if they are deemed unhealthy. A -// member can be deactivated (status set to INACTIVE) if any of health monitors -// finds it unhealthy. -func AssociateMonitor(ctx context.Context, c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { - b := map[string]interface{}{"health_monitor": map[string]string{"id": monitorID}} - resp, err := c.Post(ctx, associateURL(c, poolID), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// DisassociateMonitor will disassociate a health monitor with a particular -// pool. When dissociation is successful, the health monitor will no longer -// check for the health of the members of the pool. -func DisassociateMonitor(ctx context.Context, c *gophercloud.ServiceClient, poolID, monitorID string) (r AssociateResult) { - resp, err := c.Delete(ctx, disassociateURL(c, poolID, monitorID), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas/pools/results.go b/openstack/networking/v2/extensions/lbaas/pools/results.go deleted file mode 100644 index cdd7667a19..0000000000 --- a/openstack/networking/v2/extensions/lbaas/pools/results.go +++ /dev/null @@ -1,141 +0,0 @@ -package pools - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Pool represents a logical set of devices, such as web servers, that you -// group together to receive and process traffic. The load balancing function -// chooses a member of the pool according to the configured load balancing -// method to handle the new requests or connections received on the VIP address. -// There is only one pool per virtual IP. -type Pool struct { - // Status of the pool. Indicates whether the pool is operational. - Status string - - // LBMethod is the load-balancer algorithm, which is round-robin, - // least-connections, and so on. This value, which must be supported, is - // dependent on the provider. - LBMethod string `json:"lb_method"` - - // Protocol of the pool, which is TCP, HTTP, or HTTPS. - Protocol string - - // Description for the pool. - Description string - - // MonitorIDs are the IDs of associated monitors which check the health of - // the pool members. - MonitorIDs []string `json:"health_monitors"` - - // SubnetID is the network on which the members of the pool will be located. - // Only members that are on this network can be added to the pool. - SubnetID string `json:"subnet_id"` - - // TenantID is the owner of the pool. - TenantID string `json:"tenant_id"` - - // AdminStateUp is the administrative state of the pool, which is up - // (true) or down (false). - AdminStateUp bool `json:"admin_state_up"` - - // Name of the pool. - Name string - - // MemberIDs is the list of member IDs that belong to the pool. - MemberIDs []string `json:"members"` - - // ID is the unique ID for the pool. - ID string - - // VIPID is the ID of the virtual IP associated with this pool. - VIPID string `json:"vip_id"` - - // The provider. - Provider string -} - -// PoolPage is the page returned by a pager when traversing over a -// collection of pools. -type PoolPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of pools 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 PoolPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"pools_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractPools accepts a Page struct, specifically a PoolPage struct, -// and extracts the elements into a slice of Router structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractPools(r pagination.Page) ([]Pool, error) { - var s struct { - Pools []Pool `json:"pools"` - } - err := (r.(PoolPage)).ExtractInto(&s) - return s.Pools, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a router. -func (r commonResult) Extract() (*Pool, error) { - var s struct { - Pool *Pool `json:"pool"` - } - err := r.ExtractInto(&s) - return s.Pool, err -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a Pool. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. Call its Extract -// method to interpret it as a Pool. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its Extract -// method to interpret it as a Pool. -type UpdateResult struct { - commonResult -} - -// DeleteResult represents the result of a delete operation. Call its -// ExtractErr method to interpret it as a Pool. -type DeleteResult struct { - gophercloud.ErrResult -} - -// AssociateResult represents the result of an association operation. Call its Extract -// method to interpret it as a Pool. -type AssociateResult struct { - commonResult -} diff --git a/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go b/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go deleted file mode 100644 index 46e335f3f2..0000000000 --- a/openstack/networking/v2/extensions/lbaas/pools/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// pools unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go deleted file mode 100644 index db2dcf90ee..0000000000 --- a/openstack/networking/v2/extensions/lbaas/pools/testing/requests_test.go +++ /dev/null @@ -1,318 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/pools" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/pools", 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, ` -{ - "pools":[ - { - "status":"ACTIVE", - "lb_method":"ROUND_ROBIN", - "protocol":"HTTP", - "description":"", - "health_monitors":[ - "466c8345-28d8-4f84-a246-e04380b0461d", - "5d4b5228-33b0-4e60-b225-9b727c1a20e7" - ], - "members":[ - "701b531b-111a-4f21-ad85-4795b7b12af6", - "beb53b4d-230b-4abd-8118-575b8fa006ef" - ], - "status_description": null, - "id":"72741b06-df4d-4715-b142-276b6bce75ab", - "vip_id":"4ec89087-d057-4e2c-911f-60a3b47ee304", - "name":"app_pool", - "admin_state_up":true, - "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861", - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "health_monitors_status": [], - "provider": "haproxy" - } - ] -} - `) - }) - - count := 0 - - pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := pools.ExtractPools(page) - if err != nil { - t.Errorf("Failed to extract pools: %v", err) - return false, err - } - - expected := []pools.Pool{ - { - Status: "ACTIVE", - LBMethod: "ROUND_ROBIN", - Protocol: "HTTP", - Description: "", - MonitorIDs: []string{ - "466c8345-28d8-4f84-a246-e04380b0461d", - "5d4b5228-33b0-4e60-b225-9b727c1a20e7", - }, - SubnetID: "8032909d-47a1-4715-90af-5153ffe39861", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - AdminStateUp: true, - Name: "app_pool", - MemberIDs: []string{ - "701b531b-111a-4f21-ad85-4795b7b12af6", - "beb53b4d-230b-4abd-8118-575b8fa006ef", - }, - ID: "72741b06-df4d-4715-b142-276b6bce75ab", - VIPID: "4ec89087-d057-4e2c-911f-60a3b47ee304", - Provider: "haproxy", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/pools", 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, ` -{ - "pool": { - "lb_method": "ROUND_ROBIN", - "protocol": "HTTP", - "name": "Example pool", - "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", - "provider": "haproxy" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "pool": { - "status": "PENDING_CREATE", - "lb_method": "ROUND_ROBIN", - "protocol": "HTTP", - "description": "", - "health_monitors": [], - "members": [], - "status_description": null, - "id": "69055154-f603-4a28-8951-7cc2d9e54a9a", - "vip_id": null, - "name": "Example pool", - "admin_state_up": true, - "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", - "health_monitors_status": [], - "provider": "haproxy" - } -} - `) - }) - - options := pools.CreateOpts{ - LBMethod: pools.LBMethodRoundRobin, - Protocol: "HTTP", - Name: "Example pool", - SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", - Provider: "haproxy", - } - p, err := pools.Create(context.TODO(), fake.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "PENDING_CREATE", p.Status) - th.AssertEquals(t, "ROUND_ROBIN", p.LBMethod) - th.AssertEquals(t, "HTTP", p.Protocol) - th.AssertEquals(t, "", p.Description) - th.AssertDeepEquals(t, []string{}, p.MonitorIDs) - th.AssertDeepEquals(t, []string{}, p.MemberIDs) - th.AssertEquals(t, "69055154-f603-4a28-8951-7cc2d9e54a9a", p.ID) - th.AssertEquals(t, "Example pool", p.Name) - th.AssertEquals(t, "1981f108-3c48-48d2-b908-30f7d28532c9", p.SubnetID) - th.AssertEquals(t, "2ffc6e22aae24e4795f87155d24c896f", p.TenantID) - th.AssertEquals(t, "haproxy", p.Provider) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", 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, ` -{ - "pool":{ - "id":"332abe93-f488-41ba-870b-2ac66be7f853", - "tenant_id":"19eaa775-cf5d-49bc-902e-2f85f668d995", - "name":"Example pool", - "description":"", - "protocol":"tcp", - "lb_algorithm":"ROUND_ROBIN", - "session_persistence":{ - }, - "healthmonitor_id":null, - "members":[ - ], - "admin_state_up":true, - "status":"ACTIVE" - } -} - `) - }) - - n, err := pools.Get(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.ID, "332abe93-f488-41ba-870b-2ac66be7f853") -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", 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, ` -{ - "pool":{ - "name":"SuperPool", - "lb_method": "LEAST_CONNECTIONS" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "pool":{ - "status":"PENDING_UPDATE", - "lb_method":"LEAST_CONNECTIONS", - "protocol":"TCP", - "description":"", - "health_monitors":[ - - ], - "subnet_id":"8032909d-47a1-4715-90af-5153ffe39861", - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "admin_state_up":true, - "name":"SuperPool", - "members":[ - - ], - "id":"61b1f87a-7a21-4ad3-9dda-7f81d249944f", - "vip_id":null - } -} - `) - }) - - var name = "SuperPool" - options := pools.UpdateOpts{Name: &name, LBMethod: pools.LBMethodLeastConnections} - - n, err := pools.Update(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "SuperPool", n.Name) - th.AssertDeepEquals(t, "LEAST_CONNECTIONS", n.LBMethod) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853", 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) - }) - - res := pools.Delete(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853") - th.AssertNoErr(t, res.Err) -} - -func TestAssociateHealthMonitor(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors", 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, ` -{ - "health_monitor":{ - "id":"b624decf-d5d3-4c66-9a3d-f047e7786181" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{}`) - }) - - _, err := pools.AssociateMonitor(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract() - th.AssertNoErr(t, err) -} - -func TestDisassociateHealthMonitor(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/pools/332abe93-f488-41ba-870b-2ac66be7f853/health_monitors/b624decf-d5d3-4c66-9a3d-f047e7786181", 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) - }) - - res := pools.DisassociateMonitor(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/networking/v2/extensions/lbaas/pools/urls.go b/openstack/networking/v2/extensions/lbaas/pools/urls.go deleted file mode 100644 index fd64c69a21..0000000000 --- a/openstack/networking/v2/extensions/lbaas/pools/urls.go +++ /dev/null @@ -1,25 +0,0 @@ -package pools - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lb" - resourcePath = "pools" - monitorPath = "health_monitors" -) - -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) -} - -func associateURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(rootPath, resourcePath, id, monitorPath) -} - -func disassociateURL(c *gophercloud.ServiceClient, poolID, monitorID string) string { - return c.ServiceURL(rootPath, resourcePath, poolID, monitorPath, monitorID) -} diff --git a/openstack/networking/v2/extensions/lbaas/vips/doc.go b/openstack/networking/v2/extensions/lbaas/vips/doc.go deleted file mode 100644 index 7fd861044b..0000000000 --- a/openstack/networking/v2/extensions/lbaas/vips/doc.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Package vips provides information and interaction with the Virtual IPs of the -Load Balancing as a Service extension for the OpenStack Networking service. - -Example to List Virtual IPs - - listOpts := vips.ListOpts{ - SubnetID: "d9bd223b-f1a9-4f98-953b-df977b0f902d", - } - - allPages, err := vips.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allVIPs, err := vips.ExtractVIPs(allPages) - if err != nil { - panic(err) - } - - for _, vip := range allVIPs { - fmt.Printf("%+v\n", vip) - } - -Example to Create a Virtual IP - - createOpts := vips.CreateOpts{ - Protocol: "HTTP", - Name: "NewVip", - AdminStateUp: gophercloud.Enabled, - SubnetID: "8032909d-47a1-4715-90af-5153ffe39861", - PoolID: "61b1f87a-7a21-4ad3-9dda-7f81d249944f", - ProtocolPort: 80, - Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, - } - - vip, err := vips.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Virtual IP - - vipID := "93f1bad4-0423-40a8-afac-3fc541839912" - - i1000 := 1000 - updateOpts := vips.UpdateOpts{ - ConnLimit: &i1000, - Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, - } - - vip, err := vips.Update(networkClient, vipID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Virtual IP - - vipID := "93f1bad4-0423-40a8-afac-3fc541839912" - err := vips.Delete(networkClient, vipID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package vips diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go deleted file mode 100644 index 40937645b5..0000000000 --- a/openstack/networking/v2/extensions/lbaas/vips/requests.go +++ /dev/null @@ -1,186 +0,0 @@ -package vips - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOpts allows the filtering and sorting of paginated collections through -// the API. Filtering is achieved by passing in struct field values that map to -// the floating IP attributes you want to see returned. SortKey allows you to -// sort by a particular network attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. -type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` - AdminStateUp *bool `q:"admin_state_up"` - Status string `q:"status"` - TenantID string `q:"tenant_id"` - SubnetID string `q:"subnet_id"` - Address string `q:"address"` - PortID string `q:"port_id"` - Protocol string `q:"protocol"` - ProtocolPort int `q:"protocol_port"` - ConnectionLimit int `q:"connection_limit"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// List returns a Pager which allows you to iterate over a collection of -// Virtual IPs. It accepts a ListOpts struct, which allows you to filter and -// sort the returned collection for greater efficiency. -// -// Default policy settings return only those virtual IPs that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { - q, err := gophercloud.BuildQueryString(&opts) - if err != nil { - return pagination.Pager{Err: err} - } - u := rootURL(c) + q.String() - return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { - return VIPPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create Request. -type CreateOptsBuilder interface { - ToVIPCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains all the values needed to create a new virtual IP. -type CreateOpts struct { - // Name is the human-readable name for the VIP. Does not have to be unique. - Name string `json:"name" required:"true"` - - // SubnetID is the network on which to allocate the VIP's address. A tenant - // can only create VIPs on networks authorized by policy (e.g. networks that - // belong to them or networks that are shared). - SubnetID string `json:"subnet_id" required:"true"` - - // Protocol - can either be TCP, HTTP or HTTPS. - Protocol string `json:"protocol" required:"true"` - - // ProtocolPort is the port on which to listen for client traffic. - ProtocolPort int `json:"protocol_port" required:"true"` - - // PoolID is the ID of the pool with which the VIP is associated. - PoolID string `json:"pool_id" required:"true"` - - // TenantID is only required if the caller has an admin role and wants - // to create a pool for another tenant. - TenantID string `json:"tenant_id,omitempty"` - - // Address is the IP address of the VIP. - Address string `json:"address,omitempty"` - - // Description is the human-readable description for the VIP. - Description string `json:"description,omitempty"` - - // Persistence is the the of session persistence to use. - // Omit this field to prevent session persistence. - Persistence *SessionPersistence `json:"session_persistence,omitempty"` - - // ConnLimit is the maximum number of connections allowed for the VIP. - ConnLimit *int `json:"connection_limit,omitempty"` - - // AdminStateUp is the administrative state of the VIP. A valid value is - // true (UP) or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToVIPCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToVIPCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "vip") -} - -// Create is an operation which provisions a new virtual IP based on the -// configuration defined in the CreateOpts struct. Once the request is -// validated and progress has started on the provisioning process, a -// CreateResult will be returned. -// -// Please note that the PoolID should refer to a pool that is not already -// associated with another vip. If the pool is already used by another vip, -// then the operation will fail with a 409 Conflict error will be returned. -// -// Users with an admin role can create VIPs on behalf of other tenants by -// specifying a TenantID attribute different than their own. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { - b, err := opts.ToVIPCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular virtual IP based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToVIPUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contains all the values needed to update an existing virtual IP. -// Attributes not listed here but appear in CreateOpts are immutable and cannot -// be updated. -type UpdateOpts struct { - // Name is the human-readable name for the VIP. Does not have to be unique. - Name *string `json:"name,omitempty"` - - // PoolID is the ID of the pool with which the VIP is associated. - PoolID *string `json:"pool_id,omitempty"` - - // Description is the human-readable description for the VIP. - Description *string `json:"description,omitempty"` - - // Persistence is the the of session persistence to use. - // Omit this field to prevent session persistence. - Persistence *SessionPersistence `json:"session_persistence,omitempty"` - - // ConnLimit is the maximum number of connections allowed for the VIP. - ConnLimit *int `json:"connection_limit,omitempty"` - - // AdminStateUp is the administrative state of the VIP. A valid value is - // true (UP) or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToVIPUpdateMap builds a request body based on UpdateOpts. -func (opts UpdateOpts) ToVIPUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "vip") -} - -// Update is an operation which modifies the attributes of the specified VIP. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToVIPUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, 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 virtual IP based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas/vips/results.go b/openstack/networking/v2/extensions/lbaas/vips/results.go deleted file mode 100644 index c9d078e2b7..0000000000 --- a/openstack/networking/v2/extensions/lbaas/vips/results.go +++ /dev/null @@ -1,165 +0,0 @@ -package vips - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// SessionPersistence represents the session persistence feature of the load -// balancing service. It attempts to force connections or requests in the same -// session to be processed by the same member as long as it is ative. Three -// 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. -// -// 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. -// -// 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. -type SessionPersistence struct { - // Type is the type of persistence mode. - Type string `json:"type"` - - // CookieName is the name of cookie if persistence mode is set appropriately. - CookieName string `json:"cookie_name,omitempty"` -} - -// VirtualIP is the primary load balancing configuration object that specifies -// the virtual IP address and port on which client traffic is received, as well -// as other details such as the load balancing method to be use, protocol, etc. -// This entity is sometimes known in LB products under the name of a "virtual -// server", a "vserver" or a "listener". -type VirtualIP struct { - // ID is the unique ID for the VIP. - ID string `json:"id"` - - // TenantID is the owner of the VIP. - TenantID string `json:"tenant_id"` - - // Name is the human-readable name for the VIP. Does not have to be unique. - Name string `json:"name"` - - // Description is the human-readable description for the VIP. - Description string `json:"description"` - - // SubnetID is the ID of the subnet on which to allocate the VIP address. - SubnetID string `json:"subnet_id"` - - // Address is the IP address of the VIP. - Address string `json:"address"` - - // Protocol of the VIP address. A valid value is TCP, HTTP, or HTTPS. - Protocol string `json:"protocol"` - - // ProtocolPort is the port on which to listen to client traffic that is - // associated with the VIP address. A valid value is from 0 to 65535. - ProtocolPort int `json:"protocol_port"` - - // PoolID is the ID of the pool with which the VIP is associated. - PoolID string `json:"pool_id"` - - // PortID is the ID of the port which belongs to the load balancer. - PortID string `json:"port_id"` - - // Persistence indicates whether connections in the same session will be - // processed by the same pool member or not. - Persistence SessionPersistence `json:"session_persistence"` - - // ConnLimit is the maximum number of connections allowed for the VIP. - // Default is -1, meaning no limit. - ConnLimit int `json:"connection_limit"` - - // AdminStateUp is the administrative state of the VIP. A valid value is - // true (UP) or false (DOWN). - AdminStateUp bool `json:"admin_state_up"` - - // Status is the status of the VIP. Indicates whether the VIP is operational. - Status string `json:"status"` -} - -// VIPPage is the page returned by a pager when traversing over a -// collection of virtual IPs. -type VIPPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of routers 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 VIPPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"vips_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractVIPs accepts a Page struct, specifically a VIPPage struct, -// and extracts the elements into a slice of VirtualIP structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractVIPs(r pagination.Page) ([]VirtualIP, error) { - var s struct { - VIPs []VirtualIP `json:"vips"` - } - err := (r.(VIPPage)).ExtractInto(&s) - return s.VIPs, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a VirtualIP. -func (r commonResult) Extract() (*VirtualIP, error) { - var s struct { - VirtualIP *VirtualIP `json:"vip"` - } - err := r.ExtractInto(&s) - return s.VirtualIP, err -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a VirtualIP -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. Call its Extract -// method to interpret it as a VirtualIP -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its Extract -// method to interpret it as a VirtualIP -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/networking/v2/extensions/lbaas/vips/testing/doc.go b/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go deleted file mode 100644 index e04046fbef..0000000000 --- a/openstack/networking/v2/extensions/lbaas/vips/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// vips unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go deleted file mode 100644 index ae0c86e87b..0000000000 --- a/openstack/networking/v2/extensions/lbaas/vips/testing/requests_test.go +++ /dev/null @@ -1,331 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas/vips" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/vips", 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, ` -{ - "vips":[ - { - "id": "db902c0c-d5ff-4753-b465-668ad9656918", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", - "name": "web_vip", - "description": "lb config for the web tier", - "subnet_id": "96a4386a-f8c3-42ed-afce-d7954eee77b3", - "address" : "10.30.176.47", - "port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea", - "protocol": "HTTP", - "protocol_port": 80, - "pool_id" : "cfc6589d-f949-4c66-99d2-c2da56ef3764", - "admin_state_up": true, - "status": "ACTIVE" - }, - { - "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", - "name": "db_vip", - "description": "lb config for the db tier", - "subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - "address" : "10.30.176.48", - "port_id" : "cd1f7a47-4fa6-449c-9ee7-632838aedfea", - "protocol": "TCP", - "protocol_port": 3306, - "pool_id" : "41efe233-7591-43c5-9cf7-923964759f9e", - "session_persistence" : {"type" : "SOURCE_IP"}, - "connection_limit" : 2000, - "admin_state_up": true, - "status": "INACTIVE" - } - ] -} - `) - }) - - count := 0 - - vips.List(fake.ServiceClient(), vips.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := vips.ExtractVIPs(page) - if err != nil { - t.Errorf("Failed to extract LBs: %v", err) - return false, err - } - - expected := []vips.VirtualIP{ - { - ID: "db902c0c-d5ff-4753-b465-668ad9656918", - TenantID: "310df60f-2a10-4ee5-9554-98393092194c", - Name: "web_vip", - Description: "lb config for the web tier", - SubnetID: "96a4386a-f8c3-42ed-afce-d7954eee77b3", - Address: "10.30.176.47", - PortID: "cd1f7a47-4fa6-449c-9ee7-632838aedfea", - Protocol: "HTTP", - ProtocolPort: 80, - PoolID: "cfc6589d-f949-4c66-99d2-c2da56ef3764", - Persistence: vips.SessionPersistence{}, - ConnLimit: 0, - AdminStateUp: true, - Status: "ACTIVE", - }, - { - ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "310df60f-2a10-4ee5-9554-98393092194c", - Name: "db_vip", - Description: "lb config for the db tier", - SubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - Address: "10.30.176.48", - PortID: "cd1f7a47-4fa6-449c-9ee7-632838aedfea", - Protocol: "TCP", - ProtocolPort: 3306, - PoolID: "41efe233-7591-43c5-9cf7-923964759f9e", - Persistence: vips.SessionPersistence{Type: "SOURCE_IP"}, - ConnLimit: 2000, - AdminStateUp: true, - Status: "INACTIVE", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/vips", 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, ` -{ - "vip": { - "protocol": "HTTP", - "name": "NewVip", - "admin_state_up": true, - "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861", - "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f", - "protocol_port": 80, - "session_persistence": {"type": "SOURCE_IP"} - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "vip": { - "status": "PENDING_CREATE", - "protocol": "HTTP", - "description": "", - "admin_state_up": true, - "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861", - "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea", - "connection_limit": -1, - "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f", - "address": "10.0.0.11", - "protocol_port": 80, - "port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", - "id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2", - "name": "NewVip" - } -} - `) - }) - - opts := vips.CreateOpts{ - Protocol: "HTTP", - Name: "NewVip", - AdminStateUp: gophercloud.Enabled, - SubnetID: "8032909d-47a1-4715-90af-5153ffe39861", - PoolID: "61b1f87a-7a21-4ad3-9dda-7f81d249944f", - ProtocolPort: 80, - Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, - } - - r, err := vips.Create(context.TODO(), fake.ServiceClient(), opts).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "PENDING_CREATE", r.Status) - th.AssertEquals(t, "HTTP", r.Protocol) - th.AssertEquals(t, "", r.Description) - th.AssertEquals(t, true, r.AdminStateUp) - th.AssertEquals(t, "8032909d-47a1-4715-90af-5153ffe39861", r.SubnetID) - th.AssertEquals(t, "83657cfcdfe44cd5920adaf26c48ceea", r.TenantID) - th.AssertEquals(t, -1, r.ConnLimit) - th.AssertEquals(t, "61b1f87a-7a21-4ad3-9dda-7f81d249944f", r.PoolID) - th.AssertEquals(t, "10.0.0.11", r.Address) - th.AssertEquals(t, 80, r.ProtocolPort) - th.AssertEquals(t, "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", r.PortID) - th.AssertEquals(t, "c987d2be-9a3c-4ac9-a046-e8716b1350e2", r.ID) - th.AssertEquals(t, "NewVip", r.Name) -} - -func TestRequiredCreateOpts(t *testing.T) { - res := vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{Name: "foo"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = vips.Create(context.TODO(), fake.ServiceClient(), vips.CreateOpts{Name: "foo", SubnetID: "bar", Protocol: "bar", ProtocolPort: 80}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", 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, ` -{ - "vip": { - "status": "ACTIVE", - "protocol": "HTTP", - "description": "", - "admin_state_up": true, - "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861", - "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea", - "connection_limit": 1000, - "pool_id": "72741b06-df4d-4715-b142-276b6bce75ab", - "session_persistence": { - "cookie_name": "MyAppCookie", - "type": "APP_COOKIE" - }, - "address": "10.0.0.10", - "protocol_port": 80, - "port_id": "b5a743d6-056b-468b-862d-fb13a9aa694e", - "id": "4ec89087-d057-4e2c-911f-60a3b47ee304", - "name": "my-vip" - } -} - `) - }) - - vip, err := vips.Get(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "ACTIVE", vip.Status) - th.AssertEquals(t, "HTTP", vip.Protocol) - th.AssertEquals(t, "", vip.Description) - th.AssertEquals(t, true, vip.AdminStateUp) - th.AssertEquals(t, 1000, vip.ConnLimit) - th.AssertEquals(t, vips.SessionPersistence{Type: "APP_COOKIE", CookieName: "MyAppCookie"}, vip.Persistence) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", 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, ` -{ - "vip": { - "connection_limit": 1000, - "session_persistence": {"type": "SOURCE_IP"} - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, ` -{ - "vip": { - "status": "PENDING_UPDATE", - "protocol": "HTTP", - "description": "", - "admin_state_up": true, - "subnet_id": "8032909d-47a1-4715-90af-5153ffe39861", - "tenant_id": "83657cfcdfe44cd5920adaf26c48ceea", - "connection_limit": 1000, - "pool_id": "61b1f87a-7a21-4ad3-9dda-7f81d249944f", - "address": "10.0.0.11", - "protocol_port": 80, - "port_id": "f7e6fe6a-b8b5-43a8-8215-73456b32e0f5", - "id": "c987d2be-9a3c-4ac9-a046-e8716b1350e2", - "name": "NewVip" - } -} - `) - }) - - i1000 := 1000 - options := vips.UpdateOpts{ - ConnLimit: &i1000, - Persistence: &vips.SessionPersistence{Type: "SOURCE_IP"}, - } - vip, err := vips.Update(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304", options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "PENDING_UPDATE", vip.Status) - th.AssertEquals(t, 1000, vip.ConnLimit) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/v2.0/lb/vips/4ec89087-d057-4e2c-911f-60a3b47ee304", 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) - }) - - res := vips.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/networking/v2/extensions/lbaas/vips/urls.go b/openstack/networking/v2/extensions/lbaas/vips/urls.go deleted file mode 100644 index 436a9a38dc..0000000000 --- a/openstack/networking/v2/extensions/lbaas/vips/urls.go +++ /dev/null @@ -1,16 +0,0 @@ -package vips - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lb" - resourcePath = "vips" -) - -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 205c689ba0d3197496e20341160ff9eb22709e28 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 21 Mar 2024 11:54:47 +0000 Subject: [PATCH 1792/2296] networking: Remove neutron LBaaS v2 This has been deprecated since the Queens release [1], over 6 years ago [2]. It has been replaced by Octavia. Users that still rely on this old feature can continue using Gophercloud v1, but we don't want to support this going forward. [1] https://wiki.openstack.org/wiki/Neutron/LBaaS/Deprecation [2] https://releases.openstack.org/queens/ Signed-off-by: Stephen Finucane --- .../v2/extensions/lbaas_v2/l7policies_test.go | 35 -- .../v2/extensions/lbaas_v2/lbaas_v2.go | 427 ----------------- .../v2/extensions/lbaas_v2/listeners_test.go | 35 -- .../extensions/lbaas_v2/loadbalancers_test.go | 300 ------------ .../v2/extensions/lbaas_v2/monitors_test.go | 35 -- .../v2/extensions/lbaas_v2/pools_test.go | 35 -- .../networking/v2/extensions/lbaas_v2/doc.go | 3 - .../v2/extensions/lbaas_v2/l7policies/doc.go | 123 ----- .../lbaas_v2/l7policies/requests.go | 386 ---------------- .../extensions/lbaas_v2/l7policies/results.go | 253 ----------- .../lbaas_v2/l7policies/testing/doc.go | 2 - .../l7policies/testing/fixtures_test.go | 428 ------------------ .../l7policies/testing/requests_test.go | 318 ------------- .../v2/extensions/lbaas_v2/l7policies/urls.go | 25 - .../v2/extensions/lbaas_v2/listeners/doc.go | 63 --- .../extensions/lbaas_v2/listeners/requests.go | 218 --------- .../extensions/lbaas_v2/listeners/results.go | 145 ------ .../lbaas_v2/listeners/testing/doc.go | 2 - .../listeners/testing/fixtures_test.go | 214 --------- .../listeners/testing/requests_test.go | 141 ------ .../v2/extensions/lbaas_v2/listeners/urls.go | 16 - .../extensions/lbaas_v2/loadbalancers/doc.go | 79 ---- .../lbaas_v2/loadbalancers/requests.go | 212 --------- .../lbaas_v2/loadbalancers/results.go | 190 -------- .../lbaas_v2/loadbalancers/testing/doc.go | 2 - .../loadbalancers/testing/fixtures_test.go | 324 ------------- .../loadbalancers/testing/requests_test.go | 177 -------- .../extensions/lbaas_v2/loadbalancers/urls.go | 26 -- .../v2/extensions/lbaas_v2/monitors/doc.go | 69 --- .../extensions/lbaas_v2/monitors/requests.go | 262 ----------- .../extensions/lbaas_v2/monitors/results.go | 157 ------- .../lbaas_v2/monitors/testing/doc.go | 2 - .../monitors/testing/fixtures_test.go | 215 --------- .../monitors/testing/requests_test.go | 156 ------- .../v2/extensions/lbaas_v2/monitors/urls.go | 16 - .../v2/extensions/lbaas_v2/pools/doc.go | 126 ------ .../v2/extensions/lbaas_v2/pools/requests.go | 366 --------------- .../v2/extensions/lbaas_v2/pools/results.go | 304 ------------- .../extensions/lbaas_v2/pools/testing/doc.go | 2 - .../lbaas_v2/pools/testing/fixtures_test.go | 388 ---------------- .../lbaas_v2/pools/testing/requests_test.go | 267 ----------- .../v2/extensions/lbaas_v2/pools/urls.go | 25 - 42 files changed, 6569 deletions(-) delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go delete mode 100644 internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/listeners/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/listeners/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/listeners/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/monitors/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/monitors/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/monitors/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/pools/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/pools/requests.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/pools/results.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/pools/testing/doc.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go delete mode 100644 openstack/networking/v2/extensions/lbaas_v2/pools/urls.go diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go deleted file mode 100644 index f279cdbb3d..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/l7policies_test.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build acceptance || networking || loadbalancer || l7policies -// +build acceptance networking loadbalancer l7policies - -package lbaas_v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" -) - -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) - } - - allPages, err := l7policies.List(client, nil).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list l7policies: %v", err) - } - - allL7Policies, err := l7policies.ExtractL7Policies(allPages) - if err != nil { - t.Fatalf("Unable to extract l7policies: %v", err) - } - - for _, policy := range allL7Policies { - tools.PrintResource(t, policy) - } -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go deleted file mode 100644 index f3398612ac..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/lbaas_v2.go +++ /dev/null @@ -1,427 +0,0 @@ -package lbaas_v2 - -import ( - "context" - "fmt" - "strings" - "testing" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -// CreateListener will create a listener for a given load balancer on a random -// port with a random name. An error will be returned if the listener could not -// be created. -func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) { - listenerName := tools.RandomString("TESTACCT-", 8) - listenerDescription := tools.RandomString("TESTACCT-DESC-", 8) - listenerPort := tools.RandomInt(1, 100) - - t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort) - - createOpts := listeners.CreateOpts{ - Name: listenerName, - Description: listenerDescription, - LoadbalancerID: lb.ID, - Protocol: listeners.ProtocolHTTP, - ProtocolPort: listenerPort, - } - - listener, err := listeners.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return listener, err - } - - t.Logf("Successfully created listener %s", listenerName) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) - } - - th.AssertEquals(t, listener.Name, listenerName) - th.AssertEquals(t, listener.Description, listenerDescription) - th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID) - th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolHTTP)) - th.AssertEquals(t, listener.ProtocolPort, listenerPort) - - return listener, nil -} - -// 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) (*loadbalancers.LoadBalancer, error) { - lbName := tools.RandomString("TESTACCT-", 8) - lbDescription := tools.RandomString("TESTACCT-DESC-", 8) - - t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID) - - createOpts := loadbalancers.CreateOpts{ - Name: lbName, - Description: lbDescription, - VipSubnetID: subnetID, - AdminStateUp: gophercloud.Enabled, - } - - lb, err := loadbalancers.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return lb, err - } - - t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID) - t.Logf("Waiting for loadbalancer %s to become active", lbName) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - return lb, err - } - - t.Logf("LoadBalancer %s is active", lbName) - - th.AssertEquals(t, lb.Name, lbName) - th.AssertEquals(t, lb.Description, lbDescription) - th.AssertEquals(t, lb.VipSubnetID, subnetID) - th.AssertEquals(t, lb.AdminStateUp, true) - - return lb, nil -} - -// CreateMember will create a member with a random name, port, address, and -// weight. An error will be returned if the member could not be created. -func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) { - memberName := tools.RandomString("TESTACCT-", 8) - memberPort := tools.RandomInt(100, 1000) - memberWeight := tools.RandomInt(1, 10) - - cidrParts := strings.Split(subnetCIDR, "/") - subnetParts := strings.Split(cidrParts[0], ".") - memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100)) - - t.Logf("Attempting to create member %s", memberName) - - createOpts := pools.CreateMemberOpts{ - Name: memberName, - ProtocolPort: memberPort, - Weight: &memberWeight, - Address: memberAddress, - SubnetID: subnetID, - } - - t.Logf("Member create opts: %#v", createOpts) - - member, err := pools.CreateMember(context.TODO(), client, pool.ID, createOpts).Extract() - if err != nil { - return member, err - } - - t.Logf("Successfully created member %s", memberName) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - return member, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) - } - - th.AssertEquals(t, member.Name, memberName) - - return member, nil -} - -// CreateMonitor will create a monitor with a random name for a specific pool. -// An error will be returned if the monitor could not be created. -func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) { - monitorName := tools.RandomString("TESTACCT-", 8) - - t.Logf("Attempting to create monitor %s", monitorName) - - createOpts := monitors.CreateOpts{ - PoolID: pool.ID, - Name: monitorName, - Delay: 10, - Timeout: 5, - MaxRetries: 5, - Type: monitors.TypePING, - } - - monitor, err := monitors.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return monitor, err - } - - t.Logf("Successfully created monitor: %s", monitorName) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) - } - - th.AssertEquals(t, monitor.Name, monitorName) - th.AssertEquals(t, monitor.Type, monitors.TypePING) - - return monitor, nil -} - -// CreatePool will create a pool with a random name with a specified listener -// and loadbalancer. An error will be returned if the pool could not be -// created. -func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) { - poolName := tools.RandomString("TESTACCT-", 8) - poolDescription := tools.RandomString("TESTACCT-DESC-", 8) - - t.Logf("Attempting to create pool %s", poolName) - - createOpts := pools.CreateOpts{ - Name: poolName, - Description: poolDescription, - Protocol: pools.ProtocolHTTP, - LoadbalancerID: lb.ID, - LBMethod: pools.LBMethodLeastConnections, - } - - pool, err := pools.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return pool, err - } - - t.Logf("Successfully created pool %s", poolName) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) - } - - th.AssertEquals(t, pool.Name, poolName) - th.AssertEquals(t, pool.Description, poolDescription) - th.AssertEquals(t, pool.Protocol, string(pools.ProtocolHTTP)) - th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID) - th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections)) - - return pool, nil -} - -// CreateL7Policy will create a l7 policy with a random name with a specified listener -// and loadbalancer. An error will be returned if the l7 policy could not be -// created. -func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) { - policyName := tools.RandomString("TESTACCT-", 8) - policyDescription := tools.RandomString("TESTACCT-DESC-", 8) - - t.Logf("Attempting to create l7 policy %s on the %s listener ID", policyName, listener.ID) - - createOpts := l7policies.CreateOpts{ - Name: policyName, - Description: policyDescription, - ListenerID: listener.ID, - Action: l7policies.ActionRedirectToURL, - RedirectURL: "http://www.example.com", - } - - policy, err := l7policies.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return policy, err - } - - t.Logf("Successfully created l7 policy %s", policyName) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) - } - - th.AssertEquals(t, policy.Name, policyName) - th.AssertEquals(t, policy.Description, policyDescription) - th.AssertEquals(t, policy.ListenerID, listener.ID) - th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL)) - th.AssertEquals(t, policy.RedirectURL, "http://www.example.com") - - return policy, nil -} - -// CreateL7Rule creates a l7 rule for specified l7 policy. -func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer) (*l7policies.Rule, error) { - t.Logf("Attempting to create l7 rule for policy %s", policyID) - - createOpts := l7policies.CreateRuleOpts{ - RuleType: l7policies.TypePath, - CompareType: l7policies.CompareTypeStartWith, - Value: "/api", - } - - rule, err := l7policies.CreateRule(context.TODO(), client, policyID, createOpts).Extract() - if err != nil { - return rule, err - } - - t.Logf("Successfully created l7 rule for policy %s", policyID) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err) - } - - th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath)) - th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith)) - th.AssertEquals(t, rule.Value, "/api") - - return rule, nil -} - -// DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if -// the l7 policy could not be deleted. This works best when used as a deferred -// function. -func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) { - t.Logf("Attempting to delete l7 policy %s", policyID) - - if err := l7policies.Delete(context.TODO(), client, policyID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { - t.Fatalf("Unable to delete l7 policy: %v", err) - } - } - - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) - } - - t.Logf("Successfully deleted l7 policy %s", policyID) -} - -// DeleteL7Rule will delete a specified l7 rule. A fatal error will occur if -// the l7 rule could not be deleted. This works best when used as a deferred -// function. -func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) { - t.Logf("Attempting to delete l7 rule %s", ruleID) - - if err := l7policies.DeleteRule(context.TODO(), client, policyID, ruleID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { - t.Fatalf("Unable to delete l7 rule: %v", err) - } - } - - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) - } - - t.Logf("Successfully deleted l7 rule %s", ruleID) -} - -// DeleteListener will delete a specified listener. A fatal error will occur if -// the listener could not be deleted. This works best when used as a deferred -// function. -func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) { - t.Logf("Attempting to delete listener %s", listenerID) - - if err := listeners.Delete(context.TODO(), client, listenerID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { - t.Fatalf("Unable to delete listener: %v", err) - } - } - - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) - } - - t.Logf("Successfully deleted listener %s", listenerID) -} - -// DeleteMember will delete a specified member. A fatal error will occur if the -// member could not be deleted. This works best when used as a deferred -// function. -func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) { - t.Logf("Attempting to delete member %s", memberID) - - if err := pools.DeleteMember(context.TODO(), client, poolID, memberID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { - t.Fatalf("Unable to delete member: %s", memberID) - } - } - - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) - } - - t.Logf("Successfully deleted member %s", memberID) -} - -// DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will -// occur if the loadbalancer could not be deleted. This works best when used -// as a deferred function. -func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) { - t.Logf("Attempting to delete loadbalancer %s", lbID) - - if err := loadbalancers.Delete(context.TODO(), client, lbID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { - t.Fatalf("Unable to delete loadbalancer: %v", err) - } - } - - t.Logf("Waiting for loadbalancer %s to delete", lbID) - - if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil { - t.Fatalf("Loadbalancer did not delete in time: %s", err) - } - - t.Logf("Successfully deleted loadbalancer %s", lbID) -} - -// DeleteMonitor will delete a specified monitor. A fatal error will occur if -// the monitor could not be deleted. This works best when used as a deferred -// function. -func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) { - t.Logf("Attempting to delete monitor %s", monitorID) - - if err := monitors.Delete(context.TODO(), client, monitorID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { - t.Fatalf("Unable to delete monitor: %v", err) - } - } - - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) - } - - t.Logf("Successfully deleted monitor %s", monitorID) -} - -// DeletePool will delete a specified pool. A fatal error will occur if the -// pool could not be deleted. This works best when used as a deferred function. -func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) { - t.Logf("Attempting to delete pool %s", poolID) - - if err := pools.Delete(context.TODO(), client, poolID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { - t.Fatalf("Unable to delete pool: %v", err) - } - } - - if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err) - } - - t.Logf("Successfully deleted pool %s", poolID) -} - -// WaitForLoadBalancerState will wait until a loadbalancer reaches a given state. -func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error { - return tools.WaitFor(func(ctx context.Context) (bool, error) { - current, err := loadbalancers.Get(ctx, client, lbID).Extract() - if err != nil { - if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { - if httpStatus.Actual == 404 { - if status == "DELETED" { - return true, nil - } - } - } - return false, err - } - - if current.ProvisioningStatus == status { - return true, nil - } - - if current.ProvisioningStatus == "ERROR" { - return false, fmt.Errorf("Load balancer is in ERROR state") - } - - return false, nil - }) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go deleted file mode 100644 index 5d1b6fb1f2..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/listeners_test.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build acceptance || networking || loadbalancer || listeners -// +build acceptance networking loadbalancer listeners - -package lbaas_v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" -) - -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) - } - - allPages, err := listeners.List(client, nil).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list listeners: %v", err) - } - - allListeners, err := listeners.ExtractListeners(allPages) - if err != nil { - t.Fatalf("Unable to extract listeners: %v", err) - } - - for _, listener := range allListeners { - tools.PrintResource(t, listener) - } -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go deleted file mode 100644 index 45e2bd4647..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/loadbalancers_test.go +++ /dev/null @@ -1,300 +0,0 @@ -//go:build acceptance || networking || lbaas_v2 || loadbalancers -// +build acceptance networking lbaas_v2 loadbalancers - -package lbaas_v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -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) - - allPages, err := loadbalancers.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) - th.AssertNoErr(t, err) - - for _, lb := range allLoadbalancers { - tools.PrintResource(t, lb) - } -} - -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) - - 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) - - lb, err := CreateLoadBalancer(t, client, subnet.ID) - th.AssertNoErr(t, err) - defer DeleteLoadBalancer(t, client, lb.ID) - - lbDescription := "" - updateLoadBalancerOpts := loadbalancers.UpdateOpts{ - Description: &lbDescription, - } - _, err = loadbalancers.Update(context.TODO(), client, lb.ID, updateLoadBalancerOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newLB, err := loadbalancers.Get(context.TODO(), client, lb.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newLB) - - th.AssertEquals(t, newLB.Description, lbDescription) - - lbStats, err := loadbalancers.GetStats(context.TODO(), client, lb.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, lbStats) - - // Because of the time it takes to create a loadbalancer, - // this test will include some other resources. - - // Listener - listener, err := CreateListener(t, client, lb) - th.AssertNoErr(t, err) - defer DeleteListener(t, client, lb.ID, listener.ID) - - listenerName := "" - listenerDescription := "" - updateListenerOpts := listeners.UpdateOpts{ - Name: &listenerName, - Description: &listenerDescription, - } - _, err = listeners.Update(context.TODO(), client, listener.ID, updateListenerOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newListener, err := listeners.Get(context.TODO(), client, listener.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newListener) - - th.AssertEquals(t, newListener.Name, listenerName) - th.AssertEquals(t, newListener.Description, listenerDescription) - - // L7 policy - policy, err := CreateL7Policy(t, client, listener, lb) - th.AssertNoErr(t, err) - defer DeleteL7Policy(t, client, lb.ID, policy.ID) - - newDescription := "" - updateL7policyOpts := l7policies.UpdateOpts{ - Description: &newDescription, - } - _, err = l7policies.Update(context.TODO(), client, policy.ID, updateL7policyOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newPolicy, err := l7policies.Get(context.TODO(), client, policy.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newPolicy) - - th.AssertEquals(t, newPolicy.Description, newDescription) - - // L7 rule - rule, err := CreateL7Rule(t, client, newPolicy.ID, lb) - th.AssertNoErr(t, err) - defer DeleteL7Rule(t, client, lb.ID, policy.ID, rule.ID) - - allPages, err := l7policies.ListRules(client, policy.ID, l7policies.ListRulesOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - allRules, err := l7policies.ExtractRules(allPages) - th.AssertNoErr(t, err) - for _, rule := range allRules { - tools.PrintResource(t, rule) - } - - /* NOT supported on F5 driver */ - updateL7ruleOpts := l7policies.UpdateRuleOpts{ - RuleType: l7policies.TypePath, - CompareType: l7policies.CompareTypeRegex, - Value: "/images/special*", - } - _, err = l7policies.UpdateRule(context.TODO(), client, policy.ID, rule.ID, updateL7ruleOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newRule, err := l7policies.GetRule(context.TODO(), client, newPolicy.ID, rule.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newRule) - - // Pool - pool, err := CreatePool(t, client, lb) - th.AssertNoErr(t, err) - defer DeletePool(t, client, lb.ID, pool.ID) - - poolName := "" - poolDescription := "" - updatePoolOpts := pools.UpdateOpts{ - Name: &poolName, - Description: &poolDescription, - } - _, err = pools.Update(context.TODO(), client, pool.ID, updatePoolOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newPool, err := pools.Get(context.TODO(), client, 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(context.TODO(), client, policy.ID, updateL7policyOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newPolicy, err = l7policies.Get(context.TODO(), client, 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, client, lb.ID, policy.ID) - defer DeleteL7Rule(t, client, lb.ID, policy.ID, rule.ID) - - // Update listener's default pool ID - updateListenerOpts = listeners.UpdateOpts{ - DefaultPoolID: &pool.ID, - } - _, err = listeners.Update(context.TODO(), client, listener.ID, updateListenerOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newListener, err = listeners.Get(context.TODO(), client, listener.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newListener) - - th.AssertEquals(t, newListener.DefaultPoolID, pool.ID) - - // Remove listener's default pool ID - emptyPoolID := "" - updateListenerOpts = listeners.UpdateOpts{ - DefaultPoolID: &emptyPoolID, - } - _, err = listeners.Update(context.TODO(), client, listener.ID, updateListenerOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newListener, err = listeners.Get(context.TODO(), client, listener.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newListener) - - th.AssertEquals(t, newListener.DefaultPoolID, "") - - // Member - member, err := CreateMember(t, client, lb, newPool, subnet.ID, subnet.CIDR) - th.AssertNoErr(t, err) - defer DeleteMember(t, client, lb.ID, pool.ID, member.ID) - - memberName := "" - newWeight := tools.RandomInt(11, 100) - updateMemberOpts := pools.UpdateMemberOpts{ - Name: &memberName, - Weight: &newWeight, - } - _, err = pools.UpdateMember(context.TODO(), client, pool.ID, member.ID, updateMemberOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newMember, err := pools.GetMember(context.TODO(), client, pool.ID, member.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newMember) - th.AssertEquals(t, newMember.Name, memberName) - th.AssertEquals(t, newMember.Weight, newWeight) - - // Monitor - monitor, err := CreateMonitor(t, client, lb, newPool) - th.AssertNoErr(t, err) - defer DeleteMonitor(t, client, lb.ID, monitor.ID) - - monName := "" - newDelay := tools.RandomInt(20, 30) - updateMonitorOpts := monitors.UpdateOpts{ - Name: &monName, - Delay: newDelay, - } - _, err = monitors.Update(context.TODO(), client, monitor.ID, updateMonitorOpts).Extract() - th.AssertNoErr(t, err) - - if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil { - t.Fatalf("Timed out waiting for loadbalancer to become active") - } - - newMonitor, err := monitors.Get(context.TODO(), client, monitor.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newMonitor) - - th.AssertEquals(t, newMonitor.Name, monName) - th.AssertEquals(t, newMonitor.Delay, newDelay) -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go deleted file mode 100644 index 3523f487e5..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/monitors_test.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build acceptance || networking || loadbalancer || monitors -// +build acceptance networking loadbalancer monitors - -package lbaas_v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" -) - -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) - } - - allPages, err := monitors.List(client, nil).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list monitors: %v", err) - } - - allMonitors, err := monitors.ExtractMonitors(allPages) - if err != nil { - t.Fatalf("Unable to extract monitors: %v", err) - } - - for _, monitor := range allMonitors { - tools.PrintResource(t, monitor) - } -} diff --git a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go b/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go deleted file mode 100644 index 7dfc1ef3f9..0000000000 --- a/internal/acceptance/openstack/networking/v2/extensions/lbaas_v2/pools_test.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build acceptance || networking || loadbalancer || pools -// +build acceptance networking loadbalancer pools - -package lbaas_v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" -) - -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) - } - - allPages, err := pools.List(client, nil).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list pools: %v", err) - } - - allPools, err := pools.ExtractPools(allPages) - if err != nil { - t.Fatalf("Unable to extract pools: %v", err) - } - - for _, pool := range allPools { - tools.PrintResource(t, pool) - } -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/doc.go b/openstack/networking/v2/extensions/lbaas_v2/doc.go deleted file mode 100644 index ec7f9d6f04..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package lbaas_v2 provides information and interaction with the Load Balancer -// as a Service v2 extension for the OpenStack Networking service. -package lbaas_v2 diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go deleted file mode 100644 index 813579905c..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/doc.go +++ /dev/null @@ -1,123 +0,0 @@ -/* -Package l7policies provides information and interaction with L7Policies and -Rules of the LBaaS v2 extension for the OpenStack Networking service. - -Example to Create a L7Policy - - createOpts := l7policies.CreateOpts{ - Name: "redirect-example.com", - ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", - Action: l7policies.ActionRedirectToURL, - RedirectURL: "http://www.example.com", - } - l7policy, err := l7policies.Create(lbClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to List L7Policies - - listOpts := l7policies.ListOpts{ - ListenerID: "c79a4468-d788-410c-bf79-9a8ef6354852", - } - allPages, err := l7policies.List(lbClient, listOpts).AllPages() - if err != nil { - panic(err) - } - allL7Policies, err := l7policies.ExtractL7Policies(allPages) - if err != nil { - panic(err) - } - for _, l7policy := range allL7Policies { - fmt.Printf("%+v\n", l7policy) - } - -Example to Get a L7Policy - - l7policy, err := l7policies.Get(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d").Extract() - if err != nil { - panic(err) - } - -Example to Delete a L7Policy - - l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := l7policies.Delete(lbClient, l7policyID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Update a L7Policy - - l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" - name := "new-name" - updateOpts := l7policies.UpdateOpts{ - Name: &name, - } - l7policy, err := l7policies.Update(lbClient, l7policyID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Create a Rule - - l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" - createOpts := l7policies.CreateRuleOpts{ - RuleType: l7policies.TypePath, - CompareType: l7policies.CompareTypeRegex, - Value: "/images*", - } - rule, err := l7policies.CreateRule(lbClient, l7policyID, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to List L7 Rules - - l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" - listOpts := l7policies.ListRulesOpts{ - RuleType: l7policies.TypePath, - } - allPages, err := l7policies.ListRules(lbClient, l7policyID, listOpts).AllPages() - if err != nil { - panic(err) - } - allRules, err := l7policies.ExtractRules(allPages) - if err != nil { - panic(err) - } - for _, rule := allRules { - fmt.Printf("%+v\n", rule) - } - -Example to Get a l7 rule - - l7rule, err := l7policies.GetRule(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d", "53ad8ab8-40fa-11e8-a508-00224d6b7bc1").Extract() - if err != nil { - panic(err) - } - -Example to Delete a l7 rule - - l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" - ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" - err := l7policies.DeleteRule(lbClient, l7policyID, ruleID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Update a Rule - - l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" - ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" - updateOpts := l7policies.UpdateRuleOpts{ - RuleType: l7policies.TypePath, - CompareType: l7policies.CompareTypeRegex, - Value: "/images/special*", - } - rule, err := l7policies.UpdateRule(lbClient, l7policyID, ruleID, updateOpts).Extract() - if err != nil { - panic(err) - } -*/ -package l7policies diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go deleted file mode 100644 index c7ac1b1e98..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/requests.go +++ /dev/null @@ -1,386 +0,0 @@ -package l7policies - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToL7PolicyCreateMap() (map[string]interface{}, error) -} - -type Action string -type RuleType string -type CompareType string - -const ( - ActionRedirectToPool Action = "REDIRECT_TO_POOL" - ActionRedirectToURL Action = "REDIRECT_TO_URL" - ActionReject Action = "REJECT" - - TypeCookie RuleType = "COOKIE" - TypeFileType RuleType = "FILE_TYPE" - TypeHeader RuleType = "HEADER" - TypeHostName RuleType = "HOST_NAME" - TypePath RuleType = "PATH" - - CompareTypeContains CompareType = "CONTAINS" - CompareTypeEndWith CompareType = "ENDS_WITH" - CompareTypeEqual CompareType = "EQUAL_TO" - CompareTypeRegex CompareType = "REGEX" - CompareTypeStartWith CompareType = "STARTS_WITH" -) - -// CreateOpts is the common options struct used in this package's Create -// operation. -type CreateOpts struct { - // Name of the L7 policy. - Name string `json:"name,omitempty"` - - // The ID of the listener. - ListenerID string `json:"listener_id" required:"true"` - - // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. - Action Action `json:"action" required:"true"` - - // The position of this policy on the listener. - Position int32 `json:"position,omitempty"` - - // A human-readable description for the resource. - Description string `json:"description,omitempty"` - - // TenantID is the UUID of the tenant who owns the L7 policy in octavia. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,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"` - - // Requests matching this policy will be redirected to this URL. - // Only valid if action is REDIRECT_TO_URL. - RedirectURL string `json:"redirect_url,omitempty"` - - // The administrative state of the Loadbalancer. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToL7PolicyCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "l7policy") -} - -// Create accepts a CreateOpts struct and uses the values to create a new l7policy. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToL7PolicyCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToL7PolicyListQuery() (string, error) -} - -// ListOpts allows the filtering and sorting of paginated collections through -// the API. -type ListOpts struct { - Name string `q:"name"` - Description string `q:"description"` - ListenerID string `q:"listener_id"` - Action string `q:"action"` - TenantID string `q:"tenant_id"` - RedirectPoolID string `q:"redirect_pool_id"` - RedirectURL string `q:"redirect_url"` - Position int32 `q:"position"` - AdminStateUp bool `q:"admin_state_up"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToL7PolicyListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToL7PolicyListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns a Pager which allows you to iterate over a collection of -// l7policies. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. -// -// Default policy settings return only those l7policies that are owned by the -// project who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := rootURL(c) - if opts != nil { - query, err := opts.ToL7PolicyListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return L7PolicyPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Get retrieves a particular l7policy based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will permanently delete a particular l7policy based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToL7PolicyUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts is the common options struct used in this package's Update -// operation. -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. - Action Action `json:"action,omitempty"` - - // The position of this policy on the listener. - Position int32 `json:"position,omitempty"` - - // A human-readable description for the resource, empty string is allowed. - Description *string `json:"description,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"` - - // Requests matching this policy will be redirected to this URL. - // Only valid if action is REDIRECT_TO_URL. - RedirectURL *string `json:"redirect_url,omitempty"` - - // The administrative state of the Loadbalancer. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToL7PolicyUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "l7policy") - if err != nil { - return nil, err - } - - m := b["l7policy"].(map[string]interface{}) - - if m["redirect_pool_id"] == "" { - m["redirect_pool_id"] = nil - } - - if m["redirect_url"] == "" { - m["redirect_url"] = nil - } - - return b, nil -} - -// Update allows l7policy to be updated. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToL7PolicyUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// CreateRuleOpts is the common options struct used in this package's CreateRule -// operation. -type CreateRuleOpts struct { - // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. - RuleType RuleType `json:"type" required:"true"` - - // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. - CompareType CompareType `json:"compare_type" required:"true"` - - // The value to use for the comparison. For example, the file type to compare. - Value string `json:"value" required:"true"` - - // TenantID is the UUID of the tenant who owns the rule in octavia. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - - // The key to use for the comparison. For example, the name of the cookie to evaluate. - Key string `json:"key,omitempty"` - - // When true the logic of the rule is inverted. For example, with invert true, - // equal to would become not equal to. Default is false. - Invert bool `json:"invert,omitempty"` - - // The administrative state of the Loadbalancer. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToRuleCreateMap builds a request body from CreateRuleOpts. -func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "rule") -} - -// CreateRule will create and associate a Rule with a particular L7Policy. -func CreateRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, opts CreateRuleOpts) (r CreateRuleResult) { - b, err := opts.ToRuleCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, ruleRootURL(c, policyID), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListRulesOptsBuilder allows extensions to add additional parameters to the -// ListRules request. -type ListRulesOptsBuilder interface { - ToRulesListQuery() (string, error) -} - -// ListRulesOpts allows the filtering and sorting of paginated collections -// through the API. -type ListRulesOpts struct { - RuleType RuleType `q:"type"` - TenantID string `q:"tenant_id"` - CompareType CompareType `q:"compare_type"` - Value string `q:"value"` - Key string `q:"key"` - Invert bool `q:"invert"` - AdminStateUp bool `q:"admin_state_up"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToRulesListQuery formats a ListOpts into a query string. -func (opts ListRulesOpts) ToRulesListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// ListRules returns a Pager which allows you to iterate over a collection of -// rules. It accepts a ListRulesOptsBuilder, which allows you to filter and -// sort the returned collection for greater efficiency. -// -// Default policy settings return only those rules that are owned by the -// project who submits the request, unless an admin user submits the request. -func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOptsBuilder) pagination.Pager { - url := ruleRootURL(c, policyID) - if opts != nil { - query, err := opts.ToRulesListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return RulePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// GetRule retrieves a particular L7Policy Rule based on its unique ID. -func GetRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { - resp, err := c.Get(ctx, ruleResourceURL(c, policyID, ruleID), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// DeleteRule will remove a Rule from a particular L7Policy. -func DeleteRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { - resp, err := c.Delete(ctx, ruleResourceURL(c, policyID, ruleID), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateRuleOptsBuilder allows to add additional parameters to the PUT request. -type UpdateRuleOptsBuilder interface { - ToRuleUpdateMap() (map[string]interface{}, error) -} - -// UpdateRuleOpts is the common options struct used in this package's Update -// operation. -type UpdateRuleOpts struct { - // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. - RuleType RuleType `json:"type,omitempty"` - - // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. - CompareType CompareType `json:"compare_type,omitempty"` - - // The value to use for the comparison. For example, the file type to compare. - Value string `json:"value,omitempty"` - - // The key to use for the comparison. For example, the name of the cookie to evaluate. - Key *string `json:"key,omitempty"` - - // When true the logic of the rule is inverted. For example, with invert true, - // equal to would become not equal to. Default is false. - Invert *bool `json:"invert,omitempty"` - - // The administrative state of the Loadbalancer. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToRuleUpdateMap builds a request body from UpdateRuleOpts. -func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "rule") - if err != nil { - return nil, err - } - - if m := b["rule"].(map[string]interface{}); m["key"] == "" { - m["key"] = nil - } - - return b, nil -} - -// UpdateRule allows Rule to be updated. -func UpdateRule(ctx context.Context, c *gophercloud.ServiceClient, policyID string, ruleID string, opts UpdateRuleOptsBuilder) (r UpdateRuleResult) { - b, err := opts.ToRuleUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go deleted file mode 100644 index 886af9b4e1..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/results.go +++ /dev/null @@ -1,253 +0,0 @@ -package l7policies - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// L7Policy is a collection of L7 rules associated with a Listener, and which -// may also have an association to a back-end pool. -type L7Policy struct { - // The unique ID for the L7 policy. - ID string `json:"id"` - - // Name of the L7 policy. - Name string `json:"name"` - - // The ID of the listener. - ListenerID string `json:"listener_id"` - - // The L7 policy action. One of REDIRECT_TO_POOL, REDIRECT_TO_URL, or REJECT. - Action string `json:"action"` - - // The position of this policy on the listener. - Position int32 `json:"position"` - - // A human-readable description for the resource. - Description string `json:"description"` - - // TenantID is the UUID of the tenant who owns the L7 policy in octavia. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id"` - - // 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"` - - // Requests matching this policy will be redirected to this URL. - // Only valid if action is REDIRECT_TO_URL. - RedirectURL string `json:"redirect_url"` - - // The administrative state of the L7 policy, which is up (true) or down (false). - AdminStateUp bool `json:"admin_state_up"` - - // The provisioning status of the L7 policy. - // This value is ACTIVE, PENDING_* or ERROR. - // This field seems to only be returned during a call to a load balancer's /status - // see: https://github.com/gophercloud/gophercloud/issues/1362 - ProvisioningStatus string `json:"provisioning_status"` - - // The operating status of the L7 policy. - // This field seems to only be returned during a call to a load balancer's /status - // see: https://github.com/gophercloud/gophercloud/issues/1362 - OperatingStatus string `json:"operating_status"` - - // Rules are List of associated L7 rule IDs. - Rules []Rule `json:"rules"` -} - -// Rule represents layer 7 load balancing rule. -type Rule struct { - // The unique ID for the L7 rule. - ID string `json:"id"` - - // The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. - RuleType string `json:"type"` - - // The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, or STARTS_WITH. - CompareType string `json:"compare_type"` - - // The value to use for the comparison. For example, the file type to compare. - Value string `json:"value"` - - // TenantID is the UUID of the tenant who owns the rule in octavia. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id"` - - // The key to use for the comparison. For example, the name of the cookie to evaluate. - Key string `json:"key"` - - // When true the logic of the rule is inverted. For example, with invert true, - // equal to would become not equal to. Default is false. - Invert bool `json:"invert"` - - // The administrative state of the L7 rule, which is up (true) or down (false). - AdminStateUp bool `json:"admin_state_up"` - - // The provisioning status of the L7 rule. - // This value is ACTIVE, PENDING_* or ERROR. - // This field seems to only be returned during a call to a load balancer's /status - // see: https://github.com/gophercloud/gophercloud/issues/1362 - ProvisioningStatus string `json:"provisioning_status"` - - // The operating status of the L7 policy. - // This field seems to only be returned during a call to a load balancer's /status - // see: https://github.com/gophercloud/gophercloud/issues/1362 - OperatingStatus string `json:"operating_status"` -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a l7policy. -func (r commonResult) Extract() (*L7Policy, error) { - var s struct { - L7Policy *L7Policy `json:"l7policy"` - } - err := r.ExtractInto(&s) - return s.L7Policy, err -} - -// CreateResult represents the result of a Create operation. Call its Extract -// method to interpret the result as a L7Policy. -type CreateResult struct { - commonResult -} - -// L7PolicyPage is the page returned by a pager when traversing over a -// collection of l7policies. -type L7PolicyPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of l7policies 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 L7PolicyPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"l7policies_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractL7Policies accepts a Page struct, specifically a L7PolicyPage struct, -// and extracts the elements into a slice of L7Policy structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractL7Policies(r pagination.Page) ([]L7Policy, error) { - var s struct { - L7Policies []L7Policy `json:"l7policies"` - } - err := (r.(L7PolicyPage)).ExtractInto(&s) - return s.L7Policies, err -} - -// GetResult represents the result of a Get operation. Call its Extract -// method to interpret the result as a L7Policy. -type GetResult 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 -} - -// UpdateResult represents the result of an Update operation. Call its Extract -// method to interpret the result as a L7Policy. -type UpdateResult struct { - commonResult -} - -type commonRuleResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a rule. -func (r commonRuleResult) Extract() (*Rule, error) { - var s struct { - Rule *Rule `json:"rule"` - } - err := r.ExtractInto(&s) - return s.Rule, err -} - -// CreateRuleResult represents the result of a CreateRule operation. -// Call its Extract method to interpret it as a Rule. -type CreateRuleResult struct { - commonRuleResult -} - -// RulePage is the page returned by a pager when traversing over a -// collection of Rules in a L7Policy. -type RulePage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of rules 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 RulePage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"rules_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractRules accepts a Page struct, specifically a RulePage struct, -// and extracts the elements into a slice of Rules structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractRules(r pagination.Page) ([]Rule, error) { - var s struct { - Rules []Rule `json:"rules"` - } - err := (r.(RulePage)).ExtractInto(&s) - return s.Rules, err -} - -// GetRuleResult represents the result of a GetRule operation. -// Call its Extract method to interpret it as a Rule. -type GetRuleResult struct { - commonRuleResult -} - -// DeleteRuleResult represents the result of a DeleteRule operation. -// Call its ExtractErr method to determine if the request succeeded or failed. -type DeleteRuleResult struct { - gophercloud.ErrResult -} - -// UpdateRuleResult represents the result of an UpdateRule operation. -// Call its Extract method to interpret it as a Rule. -type UpdateRuleResult struct { - commonRuleResult -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/doc.go deleted file mode 100644 index f8068dfb6b..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// l7policies unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go deleted file mode 100644 index 4eb4ca22a5..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/fixtures_test.go +++ /dev/null @@ -1,428 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// SingleL7PolicyBody is the canned body of a Get request on an existing l7policy. -const SingleL7PolicyBody = ` -{ - "l7policy": { - "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", - "description": "", - "admin_state_up": true, - "redirect_pool_id": null, - "redirect_url": "http://www.example.com", - "action": "REDIRECT_TO_URL", - "position": 1, - "tenant_id": "e3cd678b11784734bc366148aa37580e", - "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", - "name": "redirect-example.com", - "rules": [] - } -} -` - -var ( - L7PolicyToURL = l7policies.L7Policy{ - ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", - Name: "redirect-example.com", - ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", - Action: "REDIRECT_TO_URL", - Position: 1, - Description: "", - TenantID: "e3cd678b11784734bc366148aa37580e", - RedirectPoolID: "", - RedirectURL: "http://www.example.com", - AdminStateUp: true, - Rules: []l7policies.Rule{}, - } - L7PolicyToPool = l7policies.L7Policy{ - ID: "964f4ba4-f6cd-405c-bebd-639460af7231", - Name: "redirect-pool", - ListenerID: "be3138a3-5cf7-4513-a4c2-bb137e668bab", - Action: "REDIRECT_TO_POOL", - Position: 1, - Description: "", - TenantID: "c1f7910086964990847dc6c8b128f63c", - RedirectPoolID: "bac433c6-5bea-4311-80da-bd1cd90fbd25", - RedirectURL: "", - AdminStateUp: true, - Rules: []l7policies.Rule{}, - } - L7PolicyUpdated = l7policies.L7Policy{ - ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", - Name: "NewL7PolicyName", - ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", - Action: "REDIRECT_TO_URL", - Position: 1, - Description: "Redirect requests to example.com", - TenantID: "e3cd678b11784734bc366148aa37580e", - RedirectPoolID: "", - RedirectURL: "http://www.new-example.com", - AdminStateUp: true, - Rules: []l7policies.Rule{}, - } - L7PolicyNullRedirectURLUpdated = l7policies.L7Policy{ - ID: "8a1412f0-4c32-4257-8b07-af4770b604fd", - Name: "NewL7PolicyName", - ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", - Action: "REDIRECT_TO_URL", - Position: 1, - Description: "Redirect requests to example.com", - TenantID: "e3cd678b11784734bc366148aa37580e", - RedirectPoolID: "", - RedirectURL: "", - AdminStateUp: true, - Rules: []l7policies.Rule{}, - } - RulePath = l7policies.Rule{ - ID: "16621dbb-a736-4888-a57a-3ecd53df784c", - RuleType: "PATH", - CompareType: "REGEX", - Value: "/images*", - TenantID: "e3cd678b11784734bc366148aa37580e", - Key: "", - Invert: true, - AdminStateUp: true, - } - RuleHostName = l7policies.Rule{ - ID: "d24521a0-df84-4468-861a-a531af116d1e", - RuleType: "HOST_NAME", - CompareType: "EQUAL_TO", - Value: "www.example.com", - TenantID: "e3cd678b11784734bc366148aa37580e", - Key: "", - Invert: false, - AdminStateUp: true, - } - RuleUpdated = l7policies.Rule{ - ID: "16621dbb-a736-4888-a57a-3ecd53df784c", - RuleType: "PATH", - CompareType: "REGEX", - Value: "/images/special*", - TenantID: "e3cd678b11784734bc366148aa37580e", - Key: "", - Invert: false, - AdminStateUp: true, - } -) - -// HandleL7PolicyCreationSuccessfully sets up the test server to respond to a l7policy creation request -// with a given response. -func HandleL7PolicyCreationSuccessfully(t *testing.T, response string) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies", 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, `{ - "l7policy": { - "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", - "redirect_url": "http://www.example.com", - "name": "redirect-example.com", - "action": "REDIRECT_TO_URL" - } - }`) - - w.WriteHeader(http.StatusAccepted) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) - }) -} - -// L7PoliciesListBody contains the canned body of a l7policy list response. -const L7PoliciesListBody = ` -{ - "l7policies": [ - { - "redirect_pool_id": null, - "description": "", - "admin_state_up": true, - "rules": [], - "tenant_id": "e3cd678b11784734bc366148aa37580e", - "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", - "redirect_url": "http://www.example.com", - "action": "REDIRECT_TO_URL", - "position": 1, - "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", - "name": "redirect-example.com" - }, - { - "redirect_pool_id": "bac433c6-5bea-4311-80da-bd1cd90fbd25", - "description": "", - "admin_state_up": true, - "rules": [], - "tenant_id": "c1f7910086964990847dc6c8b128f63c", - "listener_id": "be3138a3-5cf7-4513-a4c2-bb137e668bab", - "action": "REDIRECT_TO_POOL", - "position": 1, - "id": "964f4ba4-f6cd-405c-bebd-639460af7231", - "name": "redirect-pool" - } - ] -} -` - -// PostUpdateL7PolicyBody is the canned response body of a Update request on an existing l7policy. -const PostUpdateL7PolicyBody = ` -{ - "l7policy": { - "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", - "description": "Redirect requests to example.com", - "admin_state_up": true, - "redirect_pool_id": null, - "redirect_url": "http://www.new-example.com", - "action": "REDIRECT_TO_URL", - "position": 1, - "tenant_id": "e3cd678b11784734bc366148aa37580e", - "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", - "name": "NewL7PolicyName", - "rules": [] - } -} -` - -// PostUpdateL7PolicyNullRedirectURLBody is the canned response body of a Update request -// on an existing l7policy with a null redirect_url . -const PostUpdateL7PolicyNullRedirectURLBody = ` -{ - "l7policy": { - "listener_id": "023f2e34-7806-443b-bfae-16c324569a3d", - "description": "Redirect requests to example.com", - "admin_state_up": true, - "redirect_pool_id": null, - "redirect_url": null, - "action": "REDIRECT_TO_URL", - "position": 1, - "tenant_id": "e3cd678b11784734bc366148aa37580e", - "id": "8a1412f0-4c32-4257-8b07-af4770b604fd", - "name": "NewL7PolicyName", - "rules": [] - } -} -` - -// HandleL7PolicyListSuccessfully sets up the test server to respond to a l7policy List request. -func HandleL7PolicyListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies", 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, L7PoliciesListBody) - case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "l7policies": [] }`) - default: - t.Fatalf("/v2.0/lbaas/l7policies invoked with unexpected marker=[%s]", marker) - } - }) -} - -// HandleL7PolicyGetSuccessfully sets up the test server to respond to a l7policy Get request. -func HandleL7PolicyGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, SingleL7PolicyBody) - }) -} - -// HandleL7PolicyDeletionSuccessfully sets up the test server to respond to a l7policy deletion request. -func HandleL7PolicyDeletionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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) - }) -} - -// HandleL7PolicyUpdateSuccessfully sets up the test server to respond to a l7policy Update request. -func HandleL7PolicyUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, `{ - "l7policy": { - "name": "NewL7PolicyName", - "action": "REDIRECT_TO_URL", - "redirect_url": "http://www.new-example.com" - } - }`) - - fmt.Fprintf(w, PostUpdateL7PolicyBody) - }) -} - -// HandleL7PolicyUpdateNullRedirectURLSuccessfully sets up the test server to respond to a l7policy Update request. -func HandleL7PolicyUpdateNullRedirectURLSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd", 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, `{ - "l7policy": { - "name": "NewL7PolicyName", - "redirect_url": null - } - }`) - - fmt.Fprintf(w, PostUpdateL7PolicyNullRedirectURLBody) - }) -} - -// SingleRuleBody is the canned body of a Get request on an existing rule. -const SingleRuleBody = ` -{ - "rule": { - "compare_type": "REGEX", - "invert": true, - "admin_state_up": true, - "value": "/images*", - "key": null, - "tenant_id": "e3cd678b11784734bc366148aa37580e", - "type": "PATH", - "id": "16621dbb-a736-4888-a57a-3ecd53df784c" - } -} -` - -// HandleRuleCreationSuccessfully sets up the test server to respond to a rule creation request -// with a given response. -func HandleRuleCreationSuccessfully(t *testing.T, response string) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", 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, `{ - "rule": { - "compare_type": "REGEX", - "type": "PATH", - "value": "/images*" - } - }`) - - w.WriteHeader(http.StatusAccepted) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) - }) -} - -// RulesListBody contains the canned body of a rule list response. -const RulesListBody = ` -{ - "rules":[ - { - "compare_type": "REGEX", - "invert": true, - "admin_state_up": true, - "value": "/images*", - "key": null, - "tenant_id": "e3cd678b11784734bc366148aa37580e", - "type": "PATH", - "id": "16621dbb-a736-4888-a57a-3ecd53df784c" - }, - { - "compare_type": "EQUAL_TO", - "invert": false, - "admin_state_up": true, - "value": "www.example.com", - "key": null, - "tenant_id": "e3cd678b11784734bc366148aa37580e", - "type": "HOST_NAME", - "id": "d24521a0-df84-4468-861a-a531af116d1e" - } - ] -} -` - -// HandleRuleListSuccessfully sets up the test server to respond to a rule List request. -func HandleRuleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules", 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, RulesListBody) - case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "rules": [] }`) - default: - t.Fatalf("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules invoked with unexpected marker=[%s]", marker) - } - }) -} - -// HandleRuleGetSuccessfully sets up the test server to respond to a rule Get request. -func HandleRuleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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, SingleRuleBody) - }) -} - -// HandleRuleDeletionSuccessfully sets up the test server to respond to a rule deletion request. -func HandleRuleDeletionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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) - }) -} - -// PostUpdateRuleBody is the canned response body of a Update request on an existing rule. -const PostUpdateRuleBody = ` -{ - "rule": { - "compare_type": "REGEX", - "invert": false, - "admin_state_up": true, - "value": "/images/special*", - "key": null, - "tenant_id": "e3cd678b11784734bc366148aa37580e", - "type": "PATH", - "id": "16621dbb-a736-4888-a57a-3ecd53df784c" - } -} -` - -// HandleRuleUpdateSuccessfully sets up the test server to respond to a rule Update request. -func HandleRuleUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules/16621dbb-a736-4888-a57a-3ecd53df784c", 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, `{ - "rule": { - "compare_type": "REGEX", - "invert": false, - "key": null, - "type": "PATH", - "value": "/images/special*" - } - }`) - - fmt.Fprintf(w, PostUpdateRuleBody) - }) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go deleted file mode 100644 index 89a476c3e9..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/testing/requests_test.go +++ /dev/null @@ -1,318 +0,0 @@ -package testing - -import ( - "context" - "testing" - - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestCreateL7Policy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleL7PolicyCreationSuccessfully(t, SingleL7PolicyBody) - - actual, err := l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{ - Name: "redirect-example.com", - ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", - Action: l7policies.ActionRedirectToURL, - RedirectURL: "http://www.example.com", - }).Extract() - - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, L7PolicyToURL, *actual) -} - -func TestRequiredL7PolicyCreateOpts(t *testing.T) { - // no param specified. - res := l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - - // Action is invalid. - res = l7policies.Create(context.TODO(), fake.ServiceClient(), l7policies.CreateOpts{ - ListenerID: "023f2e34-7806-443b-bfae-16c324569a3d", - Action: l7policies.Action("invalid"), - }) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } -} - -func TestListL7Policies(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleL7PolicyListSuccessfully(t) - - pages := 0 - err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pages++ - - actual, err := l7policies.ExtractL7Policies(page) - if err != nil { - return false, err - } - - if len(actual) != 2 { - t.Fatalf("Expected 2 l7policies, got %d", len(actual)) - } - th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) - th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} - -func TestListAllL7Policies(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleL7PolicyListSuccessfully(t) - - allPages, err := l7policies.List(fake.ServiceClient(), l7policies.ListOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err := l7policies.ExtractL7Policies(allPages) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, L7PolicyToURL, actual[0]) - th.CheckDeepEquals(t, L7PolicyToPool, actual[1]) -} - -func TestGetL7Policy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleL7PolicyGetSuccessfully(t) - - client := fake.ServiceClient() - actual, err := l7policies.Get(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, L7PolicyToURL, *actual) -} - -func TestDeleteL7Policy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleL7PolicyDeletionSuccessfully(t) - - res := l7policies.Delete(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd") - th.AssertNoErr(t, res.Err) -} - -func TestUpdateL7Policy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleL7PolicyUpdateSuccessfully(t) - - client := fake.ServiceClient() - newName := "NewL7PolicyName" - redirectURL := "http://www.new-example.com" - actual, err := l7policies.Update(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", - l7policies.UpdateOpts{ - Name: &newName, - Action: l7policies.ActionRedirectToURL, - RedirectURL: &redirectURL, - }).Extract() - if err != nil { - t.Fatalf("Unexpected Update error: %v", err) - } - - th.CheckDeepEquals(t, L7PolicyUpdated, *actual) -} - -func TestUpdateL7PolicyNullRedirectURL(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleL7PolicyUpdateNullRedirectURLSuccessfully(t) - - client := fake.ServiceClient() - newName := "NewL7PolicyName" - redirectURL := "" - actual, err := l7policies.Update(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", - l7policies.UpdateOpts{ - Name: &newName, - RedirectURL: &redirectURL, - }).Extract() - if err != nil { - t.Fatalf("Unexpected Update error: %v", err) - } - - th.CheckDeepEquals(t, L7PolicyNullRedirectURLUpdated, *actual) -} - -func TestUpdateL7PolicyWithInvalidOpts(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - res := l7policies.Update(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.UpdateOpts{ - Action: l7policies.Action("invalid"), - }) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } -} - -func TestCreateRule(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleRuleCreationSuccessfully(t, SingleRuleBody) - - actual, err := l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ - RuleType: l7policies.TypePath, - CompareType: l7policies.CompareTypeRegex, - Value: "/images*", - }).Extract() - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, RulePath, *actual) -} - -func TestRequiredRuleCreateOpts(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - res := l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "", l7policies.CreateRuleOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ - RuleType: l7policies.TypePath, - }) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } - res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ - RuleType: l7policies.RuleType("invalid"), - CompareType: l7policies.CompareTypeRegex, - Value: "/images*", - }) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } - res = l7policies.CreateRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.CreateRuleOpts{ - RuleType: l7policies.TypePath, - CompareType: l7policies.CompareType("invalid"), - Value: "/images*", - }) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } -} - -func TestListRules(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleRuleListSuccessfully(t) - - pages := 0 - err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pages++ - - actual, err := l7policies.ExtractRules(page) - if err != nil { - return false, err - } - - if len(actual) != 2 { - t.Fatalf("Expected 2 rules, got %d", len(actual)) - } - th.CheckDeepEquals(t, RulePath, actual[0]) - th.CheckDeepEquals(t, RuleHostName, actual[1]) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} - -func TestListAllRules(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleRuleListSuccessfully(t) - - allPages, err := l7policies.ListRules(fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", l7policies.ListRulesOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - actual, err := l7policies.ExtractRules(allPages) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, RulePath, actual[0]) - th.CheckDeepEquals(t, RuleHostName, actual[1]) -} - -func TestGetRule(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleRuleGetSuccessfully(t) - - client := fake.ServiceClient() - actual, err := l7policies.GetRule(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, RulePath, *actual) -} - -func TestDeleteRule(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleRuleDeletionSuccessfully(t) - - res := l7policies.DeleteRule(context.TODO(), fake.ServiceClient(), "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c") - th.AssertNoErr(t, res.Err) -} - -func TestUpdateRule(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleRuleUpdateSuccessfully(t) - - client := fake.ServiceClient() - invert := false - key := "" - actual, err := l7policies.UpdateRule(context.TODO(), client, "8a1412f0-4c32-4257-8b07-af4770b604fd", "16621dbb-a736-4888-a57a-3ecd53df784c", l7policies.UpdateRuleOpts{ - RuleType: l7policies.TypePath, - CompareType: l7policies.CompareTypeRegex, - Value: "/images/special*", - Invert: &invert, - Key: &key, - }).Extract() - if err != nil { - t.Fatalf("Unexpected Update error: %v", err) - } - - th.CheckDeepEquals(t, RuleUpdated, *actual) -} - -func TestUpdateRuleWithInvalidOpts(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - res := l7policies.UpdateRule(context.TODO(), fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ - RuleType: l7policies.RuleType("invalid"), - }) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - - res = l7policies.UpdateRule(context.TODO(), fake.ServiceClient(), "", "", l7policies.UpdateRuleOpts{ - CompareType: l7policies.CompareType("invalid"), - }) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go b/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go deleted file mode 100644 index 57126a8811..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/l7policies/urls.go +++ /dev/null @@ -1,25 +0,0 @@ -package l7policies - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lbaas" - resourcePath = "l7policies" - rulePath = "rules" -) - -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) -} - -func ruleRootURL(c *gophercloud.ServiceClient, policyID string) string { - return c.ServiceURL(rootPath, resourcePath, policyID, rulePath) -} - -func ruleResourceURL(c *gophercloud.ServiceClient, policyID string, ruleID string) string { - return c.ServiceURL(rootPath, resourcePath, policyID, rulePath, ruleID) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/doc.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/doc.go deleted file mode 100644 index 108cdb03d8..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/doc.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Package listeners provides information and interaction with Listeners of the -LBaaS v2 extension for the OpenStack Networking service. - -Example to List Listeners - - listOpts := listeners.ListOpts{ - LoadbalancerID : "ca430f80-1737-4712-8dc6-3f640d55594b", - } - - allPages, err := listeners.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allListeners, err := listeners.ExtractListeners(allPages) - if err != nil { - panic(err) - } - - for _, listener := range allListeners { - fmt.Printf("%+v\n", listener) - } - -Example to Create a Listener - - createOpts := listeners.CreateOpts{ - Protocol: "TCP", - Name: "db", - LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", - AdminStateUp: gophercloud.Enabled, - DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", - ProtocolPort: 3306, - } - - listener, err := listeners.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Listener - - listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" - - i1001 := 1001 - updateOpts := listeners.UpdateOpts{ - ConnLimit: &i1001, - } - - listener, err := listeners.Update(networkClient, listenerID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Listener - - listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := listeners.Delete(networkClient, listenerID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package listeners diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go deleted file mode 100644 index c914b4adde..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/requests.go +++ /dev/null @@ -1,218 +0,0 @@ -package listeners - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Type Protocol represents a listener protocol. -type Protocol string - -// Supported attributes for create/update operations. -const ( - ProtocolTCP Protocol = "TCP" - ProtocolHTTP Protocol = "HTTP" - ProtocolHTTPS Protocol = "HTTPS" - ProtocolTerminatedHTTPS Protocol = "TERMINATED_HTTPS" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToListenerListQuery() (string, error) -} - -// ListOpts allows the filtering and sorting of paginated collections through -// the API. Filtering is achieved by passing in struct field values that map to -// the floating IP attributes you want to see returned. SortKey allows you to -// sort by a particular listener 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"` - AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - LoadbalancerID string `q:"loadbalancer_id"` - DefaultPoolID string `q:"default_pool_id"` - Protocol string `q:"protocol"` - ProtocolPort int `q:"protocol_port"` - ConnectionLimit int `q:"connection_limit"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToListenerListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToListenerListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns a Pager which allows you to iterate over a collection of -// listeners. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. -// -// Default policy settings return only those listeners that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := rootURL(c) - if opts != nil { - query, err := opts.ToListenerListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return ListenerPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToListenerCreateMap() (map[string]interface{}, error) -} - -// CreateOpts represents options for creating a listener. -type CreateOpts struct { - // The load balancer on which to provision this listener. - LoadbalancerID string `json:"loadbalancer_id" required:"true"` - - // The protocol - can either be TCP, HTTP or HTTPS. - Protocol Protocol `json:"protocol" required:"true"` - - // The port on which to listen for client traffic. - ProtocolPort int `json:"protocol_port" required:"true"` - - // TenantID is only required if the caller has an admin role and wants - // to create a pool for another project. - TenantID string `json:"tenant_id,omitempty"` - - // ProjectID is only required if the caller has an admin role and wants - // to create a pool for another project. - ProjectID string `json:"project_id,omitempty"` - - // Human-readable name for the Listener. Does not have to be unique. - Name string `json:"name,omitempty"` - - // The ID of the default pool with which the Listener is associated. - DefaultPoolID string `json:"default_pool_id,omitempty"` - - // Human-readable description for the Listener. - Description string `json:"description,omitempty"` - - // The maximum number of connections allowed for the Listener. - ConnLimit *int `json:"connection_limit,omitempty"` - - // A reference to a Barbican container of TLS secrets. - DefaultTlsContainerRef string `json:"default_tls_container_ref,omitempty"` - - // A list of references to TLS secrets. - SniContainerRefs []string `json:"sni_container_refs,omitempty"` - - // The administrative state of the Listener. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToListenerCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "listener") -} - -// Create is an operation which provisions a new Listeners based on the -// configuration defined in the CreateOpts struct. Once the request is -// validated and progress has started on the provisioning process, a -// CreateResult will be returned. -// -// Users with an admin role can create Listeners on behalf of other tenants by -// specifying a TenantID attribute different than their own. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToListenerCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular Listeners based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToListenerUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts represents options for updating a Listener. -type UpdateOpts struct { - // Human-readable name for the Listener. Does not have to be unique. - Name *string `json:"name,omitempty"` - - // The ID of the default pool with which the Listener is associated. - DefaultPoolID *string `json:"default_pool_id,omitempty"` - - // Human-readable description for the Listener. - Description *string `json:"description,omitempty"` - - // The maximum number of connections allowed for the Listener. - ConnLimit *int `json:"connection_limit,omitempty"` - - // A reference to a Barbican container of TLS secrets. - DefaultTlsContainerRef *string `json:"default_tls_container_ref,omitempty"` - - // A list of references to TLS secrets. - SniContainerRefs *[]string `json:"sni_container_refs,omitempty"` - - // The administrative state of the Listener. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToListenerUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "listener") - if err != nil { - return nil, err - } - - if m := b["listener"].(map[string]interface{}); m["default_pool_id"] == "" { - m["default_pool_id"] = nil - } - - return b, nil -} - -// Update is an operation which modifies the attributes of the specified -// Listener. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToListenerUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, 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 Listeners based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go deleted file mode 100644 index 4434a9db24..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/results.go +++ /dev/null @@ -1,145 +0,0 @@ -package listeners - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/l7policies" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -type LoadBalancerID struct { - ID string `json:"id"` -} - -// Listener is the primary load balancing configuration object that specifies -// the loadbalancer and port on which client traffic is received, as well -// as other details such as the load balancing method to be use, protocol, etc. -type Listener struct { - // The unique ID for the Listener. - ID string `json:"id"` - - // Owner of the Listener. - TenantID string `json:"tenant_id"` - - // Human-readable name for the Listener. Does not have to be unique. - Name string `json:"name"` - - // Human-readable description for the Listener. - Description string `json:"description"` - - // The protocol to loadbalance. A valid value is TCP, HTTP, or HTTPS. - Protocol string `json:"protocol"` - - // The port on which to listen to client traffic that is associated with the - // Loadbalancer. A valid value is from 0 to 65535. - ProtocolPort int `json:"protocol_port"` - - // The UUID of default pool. Must have compatible protocol with listener. - DefaultPoolID string `json:"default_pool_id"` - - // A list of load balancer IDs. - Loadbalancers []LoadBalancerID `json:"loadbalancers"` - - // The maximum number of connections allowed for the Loadbalancer. - // Default is -1, meaning no limit. - ConnLimit int `json:"connection_limit"` - - // The list of references to TLS secrets. - SniContainerRefs []string `json:"sni_container_refs"` - - // A reference to a Barbican container of TLS secrets. - DefaultTlsContainerRef string `json:"default_tls_container_ref"` - - // The administrative state of the Listener. A valid value is true (UP) or false (DOWN). - AdminStateUp bool `json:"admin_state_up"` - - // Pools are the pools which are part of this listener. - Pools []pools.Pool `json:"pools"` - - // L7policies are the L7 policies which are part of this listener. - // This field seems to only be returned during a call to a load balancer's /status - // see: https://github.com/gophercloud/gophercloud/issues/1352 - L7Policies []l7policies.L7Policy `json:"l7policies"` - - // The provisioning status of the listener. - // This value is ACTIVE, PENDING_* or ERROR. - ProvisioningStatus string `json:"provisioning_status"` -} - -// ListenerPage is the page returned by a pager when traversing over a -// collection of listeners. -type ListenerPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of listeners 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 ListenerPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"listeners_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractListeners accepts a Page struct, specifically a ListenerPage struct, -// and extracts the elements into a slice of Listener structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractListeners(r pagination.Page) ([]Listener, error) { - var s struct { - Listeners []Listener `json:"listeners"` - } - err := (r.(ListenerPage)).ExtractInto(&s) - return s.Listeners, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a listener. -func (r commonResult) Extract() (*Listener, error) { - var s struct { - Listener *Listener `json:"listener"` - } - err := r.ExtractInto(&s) - return s.Listener, err -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a Listener. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. Call its Extract -// method to interpret it as a Listener. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its Extract -// method to interpret it as a Listener. -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/networking/v2/extensions/lbaas_v2/listeners/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/doc.go deleted file mode 100644 index f41387e827..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// listeners unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go deleted file mode 100644 index 31cfbf2eb1..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/fixtures_test.go +++ /dev/null @@ -1,214 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// ListenersListBody contains the canned body of a listeners list response. -const ListenersListBody = ` -{ - "listeners":[ - { - "id": "db902c0c-d5ff-4753-b465-668ad9656918", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", - "name": "web", - "description": "listener config for the web tier", - "loadbalancers": [{"id": "53306cda-815d-4354-9444-59e09da9c3c5"}], - "protocol": "HTTP", - "protocol_port": 80, - "default_pool_id": "fad389a3-9a4a-4762-a365-8c7038508b5d", - "admin_state_up": true, - "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] - }, - { - "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", - "name": "db", - "description": "listener config for the db tier", - "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], - "protocol": "TCP", - "protocol_port": 3306, - "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", - "connection_limit": 2000, - "admin_state_up": true, - "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] - } - ] -} -` - -// SingleServerBody is the canned body of a Get request on an existing listener. -const SingleListenerBody = ` -{ - "listener": { - "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", - "name": "db", - "description": "listener config for the db tier", - "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], - "protocol": "TCP", - "protocol_port": 3306, - "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", - "connection_limit": 2000, - "admin_state_up": true, - "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] - } -} -` - -// PostUpdateListenerBody is the canned response body of a Update request on an existing listener. -const PostUpdateListenerBody = ` -{ - "listener": { - "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "310df60f-2a10-4ee5-9554-98393092194c", - "name": "NewListenerName", - "description": "listener config for the db tier", - "loadbalancers": [{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], - "protocol": "TCP", - "protocol_port": 3306, - "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", - "connection_limit": 1000, - "admin_state_up": true, - "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "sni_container_refs": ["3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"] - } -} -` - -var ( - ListenerWeb = listeners.Listener{ - ID: "db902c0c-d5ff-4753-b465-668ad9656918", - TenantID: "310df60f-2a10-4ee5-9554-98393092194c", - Name: "web", - Description: "listener config for the web tier", - Loadbalancers: []listeners.LoadBalancerID{{ID: "53306cda-815d-4354-9444-59e09da9c3c5"}}, - Protocol: "HTTP", - ProtocolPort: 80, - DefaultPoolID: "fad389a3-9a4a-4762-a365-8c7038508b5d", - AdminStateUp: true, - DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", - SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, - } - ListenerDb = listeners.Listener{ - ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "310df60f-2a10-4ee5-9554-98393092194c", - Name: "db", - Description: "listener config for the db tier", - Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, - Protocol: "TCP", - ProtocolPort: 3306, - DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", - ConnLimit: 2000, - AdminStateUp: true, - DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", - SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, - } - ListenerUpdated = listeners.Listener{ - ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "310df60f-2a10-4ee5-9554-98393092194c", - Name: "NewListenerName", - Description: "listener config for the db tier", - Loadbalancers: []listeners.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, - Protocol: "TCP", - ProtocolPort: 3306, - DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", - ConnLimit: 1000, - AdminStateUp: true, - DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", - SniContainerRefs: []string{"3d328d82-2547-4921-ac2f-61c3b452b5ff", "b3cfd7e3-8c19-455c-8ebb-d78dfd8f7e7d"}, - } -) - -// HandleListenerListSuccessfully sets up the test server to respond to a listener List request. -func HandleListenerListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/listeners", 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, ListenersListBody) - case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "listeners": [] }`) - default: - t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker) - } - }) -} - -// HandleListenerCreationSuccessfully sets up the test server to respond to a listener creation request -// with a given response. -func HandleListenerCreationSuccessfully(t *testing.T, response string) { - th.Mux.HandleFunc("/v2.0/lbaas/listeners", 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, `{ - "listener": { - "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab", - "protocol": "TCP", - "name": "db", - "admin_state_up": true, - "default_tls_container_ref": "2c433435-20de-4411-84ae-9cc8917def76", - "default_pool_id": "41efe233-7591-43c5-9cf7-923964759f9e", - "protocol_port": 3306 - } - }`) - - w.WriteHeader(http.StatusAccepted) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) - }) -} - -// HandleListenerGetSuccessfully sets up the test server to respond to a listener Get request. -func HandleListenerGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", 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, SingleListenerBody) - }) -} - -// HandleListenerDeletionSuccessfully sets up the test server to respond to a listener deletion request. -func HandleListenerDeletionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", 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) - }) -} - -// HandleListenerUpdateSuccessfully sets up the test server to respond to a listener Update request. -func HandleListenerUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/listeners/4ec89087-d057-4e2c-911f-60a3b47ee304", 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, `{ - "listener": { - "name": "NewListenerName", - "default_pool_id": null, - "connection_limit": 1001 - } - }`) - - fmt.Fprintf(w, PostUpdateListenerBody) - }) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go deleted file mode 100644 index 364bcd393e..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/testing/requests_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestListListeners(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListenerListSuccessfully(t) - - pages := 0 - err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pages++ - - actual, err := listeners.ExtractListeners(page) - if err != nil { - return false, err - } - - if len(actual) != 2 { - t.Fatalf("Expected 2 listeners, got %d", len(actual)) - } - th.CheckDeepEquals(t, ListenerWeb, actual[0]) - th.CheckDeepEquals(t, ListenerDb, actual[1]) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} - -func TestListAllListeners(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListenerListSuccessfully(t) - - allPages, err := listeners.List(fake.ServiceClient(), listeners.ListOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err := listeners.ExtractListeners(allPages) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ListenerWeb, actual[0]) - th.CheckDeepEquals(t, ListenerDb, actual[1]) -} - -func TestCreateListener(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListenerCreationSuccessfully(t, SingleListenerBody) - - actual, err := listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{ - Protocol: "TCP", - Name: "db", - LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", - AdminStateUp: gophercloud.Enabled, - DefaultTlsContainerRef: "2c433435-20de-4411-84ae-9cc8917def76", - DefaultPoolID: "41efe233-7591-43c5-9cf7-923964759f9e", - ProtocolPort: 3306, - }).Extract() - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, ListenerDb, *actual) -} - -func TestRequiredCreateOpts(t *testing.T) { - res := listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = listeners.Create(context.TODO(), fake.ServiceClient(), listeners.CreateOpts{Name: "foo", TenantID: "bar", Protocol: "bar", ProtocolPort: 80}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } -} - -func TestGetListener(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListenerGetSuccessfully(t) - - client := fake.ServiceClient() - actual, err := listeners.Get(context.TODO(), client, "4ec89087-d057-4e2c-911f-60a3b47ee304").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, ListenerDb, *actual) -} - -func TestDeleteListener(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListenerDeletionSuccessfully(t) - - res := listeners.Delete(context.TODO(), fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304") - th.AssertNoErr(t, res.Err) -} - -func TestUpdateListener(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListenerUpdateSuccessfully(t) - - client := fake.ServiceClient() - i1001 := 1001 - name := "NewListenerName" - defaultPoolID := "" - actual, err := listeners.Update(context.TODO(), client, "4ec89087-d057-4e2c-911f-60a3b47ee304", listeners.UpdateOpts{ - Name: &name, - ConnLimit: &i1001, - DefaultPoolID: &defaultPoolID, - }).Extract() - if err != nil { - t.Fatalf("Unexpected Update error: %v", err) - } - - th.CheckDeepEquals(t, ListenerUpdated, *actual) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go b/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go deleted file mode 100644 index 18c7775b29..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/listeners/urls.go +++ /dev/null @@ -1,16 +0,0 @@ -package listeners - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lbaas" - resourcePath = "listeners" -) - -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/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go deleted file mode 100644 index cb4ba4c380..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/doc.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Package loadbalancers provides information and interaction with Load Balancers -of the LBaaS v2 extension for the OpenStack Networking service. - -Example to List Load Balancers - - listOpts := loadbalancers.ListOpts{ - Provider: "haproxy", - } - - allPages, err := loadbalancers.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allLoadbalancers, err := loadbalancers.ExtractLoadBalancers(allPages) - if err != nil { - panic(err) - } - - for _, lb := range allLoadbalancers { - fmt.Printf("%+v\n", lb) - } - -Example to Create a Load Balancer - - createOpts := loadbalancers.CreateOpts{ - Name: "db_lb", - AdminStateUp: gophercloud.Enabled, - VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - VipAddress: "10.30.176.48", - FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", - Provider: "haproxy", - } - - lb, err := loadbalancers.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Load Balancer - - lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - - i1001 := 1001 - updateOpts := loadbalancers.UpdateOpts{ - Name: "new-name", - } - - lb, err := loadbalancers.Update(networkClient, lbID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Load Balancers - - lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := loadbalancers.Delete(networkClient, lbID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Get the Status of a Load Balancer - - lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - status, err := loadbalancers.GetStatuses(networkClient, LBID).Extract() - if err != nil { - panic(err) - } - -Example to Get the Statistics of a Load Balancer - - lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - stats, err := loadbalancers.GetStats(networkClient, LBID).Extract() - if err != nil { - panic(err) - } -*/ -package loadbalancers diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go deleted file mode 100644 index b2848d3790..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/requests.go +++ /dev/null @@ -1,212 +0,0 @@ -package loadbalancers - -import ( - "context" - "fmt" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToLoadBalancerListQuery() (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 Loadbalancer attributes you want to see returned. SortKey allows you to -// sort by a particular attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. -type ListOpts struct { - Description string `q:"description"` - AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - ProvisioningStatus string `q:"provisioning_status"` - VipAddress string `q:"vip_address"` - VipPortID string `q:"vip_port_id"` - VipSubnetID string `q:"vip_subnet_id"` - ID string `q:"id"` - OperatingStatus string `q:"operating_status"` - Name string `q:"name"` - FlavorID string `q:"flavor_id"` - Provider string `q:"provider"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToLoadBalancerListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToLoadBalancerListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns a Pager which allows you to iterate over a collection of -// load balancers. It accepts a ListOpts struct, which allows you to filter -// and sort the returned collection for greater efficiency. -// -// Default policy settings return only those load balancers that are owned by -// the tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := rootURL(c) - if opts != nil { - query, err := opts.ToLoadBalancerListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return LoadBalancerPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToLoadBalancerCreateMap() (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,omitempty"` - - // Human-readable description for the Loadbalancer. - Description string `json:"description,omitempty"` - - // The network on which to allocate the Loadbalancer's address. A tenant can - // only create Loadbalancers on networks authorized by policy (e.g. networks - // that belong to them or networks that are shared). - VipSubnetID string `json:"vip_subnet_id" required:"true"` - - // TenantID is the UUID of the project who owns the Loadbalancer. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - - // ProjectID is the UUID of the project who owns the Loadbalancer. - // Only administrative users can specify a project UUID other than their own. - ProjectID string `json:"project_id,omitempty"` - - // The IP address of the Loadbalancer. - VipAddress string `json:"vip_address,omitempty"` - - // The administrative state of the Loadbalancer. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` - - // The UUID of a flavor. - FlavorID string `json:"flavor_id,omitempty"` - - // The name of the provider. - Provider string `json:"provider,omitempty"` -} - -// ToLoadBalancerCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "loadbalancer") -} - -// Create is an operation which provisions a new loadbalancer based on the -// configuration defined in the CreateOpts struct. Once the request is -// validated and progress has started on the provisioning process, a -// CreateResult will be returned. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToLoadBalancerCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular Loadbalancer based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToLoadBalancerUpdateMap() (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"` - - // Human-readable description for the Loadbalancer. - Description *string `json:"description,omitempty"` - - // The administrative state of the Loadbalancer. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToLoadBalancerUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "loadbalancer") -} - -// Update is an operation which modifies the attributes of the specified -// LoadBalancer. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToLoadBalancerUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, 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 LoadBalancer based on its -// unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// CascadingDelete is like `Delete`, but will also delete any of the load balancer's -// children (listener, monitor, etc). -// NOTE: This function will only work with Octavia load balancers; Neutron does not -// support this. -func CascadingDelete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - if c.Type != "load-balancer" { - r.Err = fmt.Errorf("error prior to running cascade delete: only Octavia LBs supported") - return - } - u := fmt.Sprintf("%s?cascade=true", resourceURL(c, id)) - resp, err := c.Delete(ctx, u, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// GetStatuses will return the status of a particular LoadBalancer. -func GetStatuses(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { - resp, err := c.Get(ctx, statusRootURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// GetStats will return the shows the current statistics of a particular LoadBalancer. -func GetStats(ctx context.Context, c *gophercloud.ServiceClient, id string) (r StatsResult) { - resp, err := c.Get(ctx, statisticsRootURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go deleted file mode 100644 index 5301d1207c..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/results.go +++ /dev/null @@ -1,190 +0,0 @@ -package loadbalancers - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// LoadBalancer is the primary load balancing configuration object that -// specifies the virtual IP address on which client traffic is received, as well -// as other details such as the load balancing method to be use, protocol, etc. -type LoadBalancer struct { - // Human-readable description for the Loadbalancer. - Description string `json:"description"` - - // The administrative state of the Loadbalancer. - // A valid value is true (UP) or false (DOWN). - AdminStateUp bool `json:"admin_state_up"` - - // Owner of the LoadBalancer. - TenantID string `json:"tenant_id"` - - // The provisioning status of the LoadBalancer. - // This value is ACTIVE, PENDING_CREATE or ERROR. - ProvisioningStatus string `json:"provisioning_status"` - - // The IP address of the Loadbalancer. - VipAddress string `json:"vip_address"` - - // The UUID of the port associated with the IP address. - VipPortID string `json:"vip_port_id"` - - // The UUID of the subnet on which to allocate the virtual IP for the - // Loadbalancer address. - VipSubnetID string `json:"vip_subnet_id"` - - // The unique ID for the LoadBalancer. - ID string `json:"id"` - - // The operating status of the LoadBalancer. This value is ONLINE or OFFLINE. - OperatingStatus string `json:"operating_status"` - - // Human-readable name for the LoadBalancer. Does not have to be unique. - Name string `json:"name"` - - // The UUID of a flavor if set. - FlavorID string `json:"flavor_id"` - - // The name of the provider. - Provider string `json:"provider"` - - // Listeners are the listeners related to this Loadbalancer. - Listeners []listeners.Listener `json:"listeners"` - - // Pools are the pools related to this Loadbalancer. - Pools []pools.Pool `json:"pools"` -} - -// StatusTree represents the status of a loadbalancer. -type StatusTree struct { - Loadbalancer *LoadBalancer `json:"loadbalancer"` -} - -type Stats struct { - // The currently active connections. - ActiveConnections int `json:"active_connections"` - - // The total bytes received. - BytesIn int `json:"bytes_in"` - - // The total bytes sent. - BytesOut int `json:"bytes_out"` - - // The total requests that were unable to be fulfilled. - RequestErrors int `json:"request_errors"` - - // The total connections handled. - TotalConnections int `json:"total_connections"` -} - -// LoadBalancerPage is the page returned by a pager when traversing over a -// collection of load balancers. -type LoadBalancerPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of load balancers 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 LoadBalancerPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"loadbalancers_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractLoadBalancers accepts a Page struct, specifically a LoadbalancerPage -// struct, and extracts the elements into a slice of LoadBalancer structs. In -// other words, a generic collection is mapped into a relevant slice. -func ExtractLoadBalancers(r pagination.Page) ([]LoadBalancer, error) { - var s struct { - LoadBalancers []LoadBalancer `json:"loadbalancers"` - } - err := (r.(LoadBalancerPage)).ExtractInto(&s) - return s.LoadBalancers, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a loadbalancer. -func (r commonResult) Extract() (*LoadBalancer, error) { - var s struct { - LoadBalancer *LoadBalancer `json:"loadbalancer"` - } - err := r.ExtractInto(&s) - return s.LoadBalancer, err -} - -// GetStatusesResult represents the result of a GetStatuses operation. -// Call its Extract method to interpret it as a StatusTree. -type GetStatusesResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts the status of -// a Loadbalancer. -func (r GetStatusesResult) Extract() (*StatusTree, error) { - var s struct { - Statuses *StatusTree `json:"statuses"` - } - err := r.ExtractInto(&s) - return s.Statuses, err -} - -// StatsResult represents the result of a GetStats operation. -// Call its Extract method to interpret it as a Stats. -type StatsResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts the status of -// a Loadbalancer. -func (r StatsResult) Extract() (*Stats, error) { - var s struct { - Stats *Stats `json:"stats"` - } - err := r.ExtractInto(&s) - return s.Stats, err -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a LoadBalancer. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. Call its Extract -// method to interpret it as a LoadBalancer. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its Extract -// method to interpret it as a LoadBalancer. -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/networking/v2/extensions/lbaas_v2/loadbalancers/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/doc.go deleted file mode 100644 index b54468c82f..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// loadbalancers unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go deleted file mode 100644 index 03ecfcc941..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/fixtures_test.go +++ /dev/null @@ -1,324 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/listeners" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// LoadbalancersListBody contains the canned body of a loadbalancer list response. -const LoadbalancersListBody = ` -{ - "loadbalancers":[ - { - "id": "c331058c-6a40-4144-948e-b9fb1df9db4b", - "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", - "name": "web_lb", - "description": "lb config for the web tier", - "vip_subnet_id": "8a49c438-848f-467b-9655-ea1548708154", - "vip_address": "10.30.176.47", - "vip_port_id": "2a22e552-a347-44fd-b530-1f2b1b2a6735", - "flavor_id": "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", - "provider": "haproxy", - "admin_state_up": true, - "provisioning_status": "ACTIVE", - "operating_status": "ONLINE" - }, - { - "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", - "name": "db_lb", - "description": "lb config for the db tier", - "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - "vip_address": "10.30.176.48", - "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", - "provider": "haproxy", - "admin_state_up": true, - "provisioning_status": "PENDING_CREATE", - "operating_status": "OFFLINE" - } - ] -} -` - -// SingleLoadbalancerBody is the canned body of a Get request on an existing loadbalancer. -const SingleLoadbalancerBody = ` -{ - "loadbalancer": { - "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", - "name": "db_lb", - "description": "lb config for the db tier", - "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - "vip_address": "10.30.176.48", - "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", - "provider": "haproxy", - "admin_state_up": true, - "provisioning_status": "PENDING_CREATE", - "operating_status": "OFFLINE" - } -} -` - -// PostUpdateLoadbalancerBody is the canned response body of a Update request on an existing loadbalancer. -const PostUpdateLoadbalancerBody = ` -{ - "loadbalancer": { - "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "tenant_id": "54030507-44f7-473c-9342-b4d14a95f692", - "name": "NewLoadbalancerName", - "description": "lb config for the db tier", - "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - "vip_address": "10.30.176.48", - "vip_port_id": "2bf413c8-41a9-4477-b505-333d5cbe8b55", - "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", - "provider": "haproxy", - "admin_state_up": true, - "provisioning_status": "PENDING_CREATE", - "operating_status": "OFFLINE" - } -} -` - -// GetLoadbalancerStatusesBody is the canned request body of a Get request on loadbalancer's status. -const GetLoadbalancerStatusesBody = ` -{ - "statuses" : { - "loadbalancer": { - "id": "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - "name": "db_lb", - "provisioning_status": "PENDING_UPDATE", - "operating_status": "ACTIVE", - "listeners": [{ - "id": "db902c0c-d5ff-4753-b465-668ad9656918", - "name": "db", - "provisioning_status": "ACTIVE", - "pools": [{ - "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", - "name": "db", - "provisioning_status": "ACTIVE", - "healthmonitor": { - "id": "67306cda-815d-4354-9fe4-59e09da9c3c5", - "type":"PING", - "provisioning_status": "ACTIVE" - }, - "members":[{ - "id": "2a280670-c202-4b0b-a562-34077415aabf", - "name": "db", - "address": "10.0.2.11", - "protocol_port": 80, - "provisioning_status": "ACTIVE" - }] - }] - }] - } - } -} -` - -// LoadbalancerStatsTree is the canned request body of a Get request on loadbalancer's statistics. -const GetLoadbalancerStatsBody = ` -{ - "stats": { - "active_connections": 0, - "bytes_in": 9532, - "bytes_out": 22033, - "request_errors": 46, - "total_connections": 112 - } -} -` - -var ( - LoadbalancerWeb = loadbalancers.LoadBalancer{ - ID: "c331058c-6a40-4144-948e-b9fb1df9db4b", - TenantID: "54030507-44f7-473c-9342-b4d14a95f692", - Name: "web_lb", - Description: "lb config for the web tier", - VipSubnetID: "8a49c438-848f-467b-9655-ea1548708154", - VipAddress: "10.30.176.47", - VipPortID: "2a22e552-a347-44fd-b530-1f2b1b2a6735", - FlavorID: "60df399a-ee85-11e9-81b4-2a2ae2dbcce4", - Provider: "haproxy", - AdminStateUp: true, - ProvisioningStatus: "ACTIVE", - OperatingStatus: "ONLINE", - } - LoadbalancerDb = loadbalancers.LoadBalancer{ - ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "54030507-44f7-473c-9342-b4d14a95f692", - Name: "db_lb", - Description: "lb config for the db tier", - VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - VipAddress: "10.30.176.48", - VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", - FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", - Provider: "haproxy", - AdminStateUp: true, - ProvisioningStatus: "PENDING_CREATE", - OperatingStatus: "OFFLINE", - } - LoadbalancerUpdated = loadbalancers.LoadBalancer{ - ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - TenantID: "54030507-44f7-473c-9342-b4d14a95f692", - Name: "NewLoadbalancerName", - Description: "lb config for the db tier", - VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - VipAddress: "10.30.176.48", - VipPortID: "2bf413c8-41a9-4477-b505-333d5cbe8b55", - FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", - Provider: "haproxy", - AdminStateUp: true, - ProvisioningStatus: "PENDING_CREATE", - OperatingStatus: "OFFLINE", - } - LoadbalancerStatusesTree = loadbalancers.StatusTree{ - Loadbalancer: &loadbalancers.LoadBalancer{ - ID: "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", - Name: "db_lb", - ProvisioningStatus: "PENDING_UPDATE", - OperatingStatus: "ACTIVE", - Listeners: []listeners.Listener{{ - ID: "db902c0c-d5ff-4753-b465-668ad9656918", - Name: "db", - ProvisioningStatus: "ACTIVE", - Pools: []pools.Pool{{ - ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", - Name: "db", - ProvisioningStatus: "ACTIVE", - Monitor: monitors.Monitor{ - ID: "67306cda-815d-4354-9fe4-59e09da9c3c5", - Type: "PING", - ProvisioningStatus: "ACTIVE", - }, - Members: []pools.Member{{ - ID: "2a280670-c202-4b0b-a562-34077415aabf", - Name: "db", - Address: "10.0.2.11", - ProtocolPort: 80, - ProvisioningStatus: "ACTIVE", - }}, - }}, - }}, - }, - } - LoadbalancerStatsTree = loadbalancers.Stats{ - ActiveConnections: 0, - BytesIn: 9532, - BytesOut: 22033, - RequestErrors: 46, - TotalConnections: 112, - } -) - -// HandleLoadbalancerListSuccessfully sets up the test server to respond to a loadbalancer List request. -func HandleLoadbalancerListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", 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, LoadbalancersListBody) - case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "loadbalancers": [] }`) - default: - t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker) - } - }) -} - -// HandleLoadbalancerCreationSuccessfully sets up the test server to respond to a loadbalancer creation request -// with a given response. -func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { - th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers", 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, `{ - "loadbalancer": { - "name": "db_lb", - "vip_subnet_id": "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - "vip_address": "10.30.176.48", - "flavor_id": "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", - "provider": "haproxy", - "admin_state_up": true - } - }`) - - w.WriteHeader(http.StatusAccepted) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) - }) -} - -// HandleLoadbalancerGetSuccessfully sets up the test server to respond to a loadbalancer Get request. -func HandleLoadbalancerGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", 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, SingleLoadbalancerBody) - }) -} - -// HandleLoadbalancerGetStatusesTree sets up the test server to respond to a loadbalancer Get statuses tree request. -func HandleLoadbalancerGetStatusesTree(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/statuses", 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, GetLoadbalancerStatusesBody) - }) -} - -// HandleLoadbalancerDeletionSuccessfully sets up the test server to respond to a loadbalancer deletion request. -func HandleLoadbalancerDeletionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", 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) - }) -} - -// HandleLoadbalancerUpdateSuccessfully sets up the test server to respond to a loadbalancer Update request. -func HandleLoadbalancerUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab", 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, `{ - "loadbalancer": { - "name": "NewLoadbalancerName" - } - }`) - - fmt.Fprintf(w, PostUpdateLoadbalancerBody) - }) -} - -// HandleLoadbalancerGetStatsTree sets up the test server to respond to a loadbalancer Get stats tree request. -func HandleLoadbalancerGetStatsTree(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/loadbalancers/36e08a3e-a78f-4b40-a229-1e7e23eee1ab/stats", 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, GetLoadbalancerStatsBody) - }) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go deleted file mode 100644 index 47803c7ff9..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/testing/requests_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2" - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/loadbalancers" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestListLoadbalancers(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerListSuccessfully(t) - - pages := 0 - err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pages++ - - actual, err := loadbalancers.ExtractLoadBalancers(page) - if err != nil { - return false, err - } - - if len(actual) != 2 { - t.Fatalf("Expected 2 loadbalancers, got %d", len(actual)) - } - th.CheckDeepEquals(t, LoadbalancerWeb, actual[0]) - th.CheckDeepEquals(t, LoadbalancerDb, actual[1]) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} - -func TestListAllLoadbalancers(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerListSuccessfully(t) - - allPages, err := loadbalancers.List(fake.ServiceClient(), loadbalancers.ListOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err := loadbalancers.ExtractLoadBalancers(allPages) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, LoadbalancerWeb, actual[0]) - th.CheckDeepEquals(t, LoadbalancerDb, actual[1]) -} - -func TestCreateLoadbalancer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerCreationSuccessfully(t, SingleLoadbalancerBody) - - actual, err := loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{ - Name: "db_lb", - AdminStateUp: gophercloud.Enabled, - VipSubnetID: "9cedb85d-0759-4898-8a4b-fa5a5ea10086", - VipAddress: "10.30.176.48", - FlavorID: "bba40eb2-ee8c-11e9-81b4-2a2ae2dbcce4", - Provider: "haproxy", - }).Extract() - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, LoadbalancerDb, *actual) -} - -func TestRequiredCreateOpts(t *testing.T) { - res := loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = loadbalancers.Create(context.TODO(), fake.ServiceClient(), loadbalancers.CreateOpts{Name: "foo", Description: "bar", VipAddress: "bar"}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } -} - -func TestGetLoadbalancer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerGetSuccessfully(t) - - client := fake.ServiceClient() - actual, err := loadbalancers.Get(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, LoadbalancerDb, *actual) -} - -func TestGetLoadbalancerStatusesTree(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerGetStatusesTree(t) - - client := fake.ServiceClient() - actual, err := loadbalancers.GetStatuses(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, LoadbalancerStatusesTree, *actual) -} - -func TestDeleteLoadbalancer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerDeletionSuccessfully(t) - - res := loadbalancers.Delete(context.TODO(), fake.ServiceClient(), "36e08a3e-a78f-4b40-a229-1e7e23eee1ab") - th.AssertNoErr(t, res.Err) -} - -func TestUpdateLoadbalancer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerUpdateSuccessfully(t) - - client := fake.ServiceClient() - name := "NewLoadbalancerName" - actual, err := loadbalancers.Update(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab", loadbalancers.UpdateOpts{ - Name: &name, - }).Extract() - if err != nil { - t.Fatalf("Unexpected Update error: %v", err) - } - - th.CheckDeepEquals(t, LoadbalancerUpdated, *actual) -} - -func TestCascadingDeleteLoadbalancer(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerDeletionSuccessfully(t) - - sc := fake.ServiceClient() - sc.Type = "network" - err := loadbalancers.CascadingDelete(context.TODO(), sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() - if err == nil { - t.Fatalf("expected error running CascadingDelete with Neutron service client but didn't get one") - } - - sc.Type = "load-balancer" - err = loadbalancers.CascadingDelete(context.TODO(), sc, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestGetLoadbalancerStatsTree(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleLoadbalancerGetStatsTree(t) - - client := fake.ServiceClient() - actual, err := loadbalancers.GetStats(context.TODO(), client, "36e08a3e-a78f-4b40-a229-1e7e23eee1ab").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, LoadbalancerStatsTree, *actual) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go b/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go deleted file mode 100644 index e30c799767..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/loadbalancers/urls.go +++ /dev/null @@ -1,26 +0,0 @@ -package loadbalancers - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lbaas" - resourcePath = "loadbalancers" - statusPath = "statuses" - statisticsPath = "stats" -) - -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) -} - -func statusRootURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(rootPath, resourcePath, id, statusPath) -} - -func statisticsRootURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(rootPath, resourcePath, id, statisticsPath) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/doc.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/doc.go deleted file mode 100644 index 6ed8c8fb5f..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/doc.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Package monitors provides information and interaction with Monitors -of the LBaaS v2 extension for the OpenStack Networking service. - -Example to List Monitors - - listOpts := monitors.ListOpts{ - PoolID: "c79a4468-d788-410c-bf79-9a8ef6354852", - } - - allPages, err := monitors.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allMonitors, err := monitors.ExtractMonitors(allPages) - if err != nil { - panic(err) - } - - for _, monitor := range allMonitors { - fmt.Printf("%+v\n", monitor) - } - -Example to Create a Monitor - - createOpts := monitors.CreateOpts{ - Type: "HTTP", - Name: "db", - PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", - Delay: 20, - Timeout: 10, - MaxRetries: 5, - URLPath: "/check", - ExpectedCodes: "200-299", - } - - monitor, err := monitors.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Monitor - - monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" - - updateOpts := monitors.UpdateOpts{ - Name: "NewHealthmonitorName", - Delay: 3, - Timeout: 20, - MaxRetries: 10, - URLPath: "/another_check", - ExpectedCodes: "301", - } - - monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Monitor - - monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := monitors.Delete(networkClient, monitorID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package monitors diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go deleted file mode 100644 index 91b49e514b..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/requests.go +++ /dev/null @@ -1,262 +0,0 @@ -package monitors - -import ( - "context" - "fmt" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToMonitorListQuery() (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 Monitor attributes you want to see returned. SortKey allows you to -// 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"` - 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"` -} - -// ToMonitorListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToMonitorListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), nil -} - -// List returns a Pager which allows you to iterate over a collection of -// health monitors. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. -// -// Default policy settings return only those health monitors that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := rootURL(c) - if opts != nil { - query, err := opts.ToMonitorListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return MonitorPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Constants that represent approved monitoring types. -const ( - TypePING = "PING" - TypeTCP = "TCP" - TypeHTTP = "HTTP" - TypeHTTPS = "HTTPS" -) - -var ( - errDelayMustGETimeout = fmt.Errorf("Delay must be greater than or equal to timeout") -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// List request. -type CreateOptsBuilder interface { - ToMonitorCreateMap() (map[string]interface{}, error) -} - -// CreateOpts is the common options struct used in this package's Create -// operation. -type CreateOpts struct { - // The Pool to Monitor. - PoolID string `json:"pool_id" required:"true"` - - // The type of probe, which is PING, TCP, HTTP, or HTTPS, that is - // sent by the load balancer to verify the member state. - Type string `json:"type" required:"true"` - - // The time, in seconds, between sending probes to members. - Delay int `json:"delay" required:"true"` - - // 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"` - - // Number of permissible ping failures before changing the member's - // status to INACTIVE. Must be a number between 1 and 10. - MaxRetries int `json:"max_retries" required:"true"` - - // URI path that will be accessed if Monitor type is HTTP or HTTPS. - // Required for HTTP(S) types. - URLPath string `json:"url_path,omitempty"` - - // The HTTP method used for requests by the Monitor. If this attribute - // is not specified, it defaults to "GET". Required for HTTP(S) types. - HTTPMethod string `json:"http_method,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. - ExpectedCodes string `json:"expected_codes,omitempty"` - - // TenantID is the UUID of the project who owns the Monitor. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - - // ProjectID is the UUID of the project who owns the Monitor. - // Only administrative users can specify a project UUID other than their own. - ProjectID string `json:"project_id,omitempty"` - - // The Name of the Monitor. - Name string `json:"name,omitempty"` - - // The administrative state of the Monitor. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToMonitorCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "healthmonitor") - if err != nil { - return nil, err - } - - switch opts.Type { - case TypeHTTP, TypeHTTPS: - switch opts.URLPath { - case "": - return nil, fmt.Errorf("URLPath must be provided for HTTP and HTTPS") - } - switch opts.ExpectedCodes { - case "": - return nil, fmt.Errorf("ExpectedCodes must be provided for HTTP and HTTPS") - } - } - - return b, nil -} - -/* -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: - -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: - -CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3, -HttpMethod: "HEAD", ExpectedCodes: "200", PoolID: "2c946bfc-1804-43ab-a2ff-58f6a762b505"} -*/ -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToMonitorCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular Health Monitor based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToMonitorUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts is the common options struct used in this package's Update -// operation. -type UpdateOpts struct { - // The time, in seconds, between sending probes to members. - Delay int `json:"delay,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. - Timeout int `json:"timeout,omitempty"` - - // Number of permissible ping failures before changing the member's - // status to INACTIVE. Must be a number between 1 and 10. - MaxRetries int `json:"max_retries,omitempty"` - - // URI path that will be accessed if Monitor type is HTTP or HTTPS. - // Required for HTTP(S) types. - URLPath string `json:"url_path,omitempty"` - - // The HTTP method used for requests by the Monitor. If this attribute - // is not specified, it defaults to "GET". Required for HTTP(S) types. - HTTPMethod string `json:"http_method,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. - ExpectedCodes string `json:"expected_codes,omitempty"` - - // The Name of the Monitor. - Name *string `json:"name,omitempty"` - - // The administrative state of the Monitor. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToMonitorUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "healthmonitor") -} - -// Update is an operation which modifies the attributes of the specified -// Monitor. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToMonitorUpdateMap() - if err != nil { - r.Err = err - return - } - - resp, err := c.Put(ctx, 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 Monitor based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go deleted file mode 100644 index b03c811c83..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/results.go +++ /dev/null @@ -1,157 +0,0 @@ -package monitors - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -type PoolID struct { - ID string `json:"id"` -} - -// Monitor represents a load balancer health monitor. A health monitor is used -// to determine whether or not back-end members of the VIP's pool are usable -// for processing a request. A pool can have several health monitors associated -// with it. There are different types of health monitors supported: -// -// PING: used to ping the members using ICMP. -// TCP: used to connect to the members using TCP. -// HTTP: used to send an HTTP request to the member. -// HTTPS: used to send a secure HTTP request to the member. -// -// When a pool has several monitors associated with it, each member of the pool -// is monitored by all these monitors. If any monitor declares the member as -// unhealthy, then the member status is changed to INACTIVE and the member -// won't participate in its pool's load balancing. In other words, ALL monitors -// must declare the member to be healthy for it to stay ACTIVE. -type Monitor struct { - // The unique ID for the Monitor. - ID string `json:"id"` - - // The Name of the Monitor. - Name string `json:"name"` - - // TenantID is the owner of the Monitor. - TenantID string `json:"tenant_id"` - - // The type of probe sent by the load balancer to verify the member state, - // which is PING, TCP, HTTP, or HTTPS. - Type string `json:"type"` - - // The time, in seconds, between sending probes to members. - Delay int `json:"delay"` - - // 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. - Timeout int `json:"timeout"` - - // Number of allowed connection failures before changing the status of the - // member to INACTIVE. A valid value is from 1 to 10. - MaxRetries int `json:"max_retries"` - - // The HTTP method that the monitor uses for requests. - HTTPMethod string `json:"http_method"` - - // 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" ` - - // Expected HTTP codes for a passing HTTP(S) monitor. - ExpectedCodes string `json:"expected_codes"` - - // The administrative state of the health monitor, which is up (true) or - // down (false). - AdminStateUp bool `json:"admin_state_up"` - - // The status of the health monitor. Indicates whether the health monitor is - // operational. - Status string `json:"status"` - - // List of pools that are associated with the health monitor. - Pools []PoolID `json:"pools"` - - // The provisioning status of the monitor. - // This value is ACTIVE, PENDING_* or ERROR. - ProvisioningStatus string `json:"provisioning_status"` -} - -// MonitorPage is the page returned by a pager when traversing over a -// collection of health monitors. -type MonitorPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of monitors 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 MonitorPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"healthmonitors_links"` - } - - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractMonitors accepts a Page struct, specifically a MonitorPage struct, -// and extracts the elements into a slice of Monitor structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractMonitors(r pagination.Page) ([]Monitor, error) { - var s struct { - Monitors []Monitor `json:"healthmonitors"` - } - err := (r.(MonitorPage)).ExtractInto(&s) - return s.Monitors, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a monitor. -func (r commonResult) Extract() (*Monitor, error) { - var s struct { - Monitor *Monitor `json:"healthmonitor"` - } - err := r.ExtractInto(&s) - return s.Monitor, err -} - -// CreateResult represents the result of a create operation. Call its Extract -// method to interpret it as a Monitor. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. Call its Extract -// method to interpret it as a Monitor. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an update operation. Call its Extract -// method to interpret it as a Monitor. -type UpdateResult struct { - commonResult -} - -// DeleteResult represents the result of a delete operation. Call its -// ExtractErr method to determine if the result succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/doc.go deleted file mode 100644 index e2b6f12a92..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// monitors unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go deleted file mode 100644 index 2a3c3b0c5c..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/fixtures_test.go +++ /dev/null @@ -1,215 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// HealthmonitorsListBody contains the canned body of a healthmonitor list response. -const HealthmonitorsListBody = ` -{ - "healthmonitors":[ - { - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "delay":10, - "name":"web", - "max_retries":1, - "timeout":1, - "type":"PING", - "pools": [{"id": "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}], - "id":"466c8345-28d8-4f84-a246-e04380b0461d" - }, - { - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "delay":5, - "name":"db", - "expected_codes":"200", - "max_retries":2, - "http_method":"GET", - "timeout":2, - "url_path":"/", - "type":"HTTP", - "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], - "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" - } - ] -} -` - -// SingleHealthmonitorBody is the canned body of a Get request on an existing healthmonitor. -const SingleHealthmonitorBody = ` -{ - "healthmonitor": { - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "delay":5, - "name":"db", - "expected_codes":"200", - "max_retries":2, - "http_method":"GET", - "timeout":2, - "url_path":"/", - "type":"HTTP", - "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], - "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" - } -} -` - -// PostUpdateHealthmonitorBody is the canned response body of a Update request on an existing healthmonitor. -const PostUpdateHealthmonitorBody = ` -{ - "healthmonitor": { - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "delay":3, - "name":"NewHealthmonitorName", - "expected_codes":"301", - "max_retries":10, - "http_method":"GET", - "timeout":20, - "url_path":"/another_check", - "type":"HTTP", - "pools": [{"id": "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}], - "id":"5d4b5228-33b0-4e60-b225-9b727c1a20e7" - } -} -` - -var ( - HealthmonitorWeb = monitors.Monitor{ - AdminStateUp: true, - Name: "web", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - Delay: 10, - MaxRetries: 1, - Timeout: 1, - Type: "PING", - ID: "466c8345-28d8-4f84-a246-e04380b0461d", - Pools: []monitors.PoolID{{ID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d"}}, - } - HealthmonitorDb = monitors.Monitor{ - AdminStateUp: true, - Name: "db", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - Delay: 5, - ExpectedCodes: "200", - MaxRetries: 2, - Timeout: 2, - URLPath: "/", - Type: "HTTP", - HTTPMethod: "GET", - ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", - Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, - } - HealthmonitorUpdated = monitors.Monitor{ - AdminStateUp: true, - Name: "NewHealthmonitorName", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - Delay: 3, - ExpectedCodes: "301", - MaxRetries: 10, - Timeout: 20, - URLPath: "/another_check", - Type: "HTTP", - HTTPMethod: "GET", - ID: "5d4b5228-33b0-4e60-b225-9b727c1a20e7", - Pools: []monitors.PoolID{{ID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d"}}, - } -) - -// HandleHealthmonitorListSuccessfully sets up the test server to respond to a healthmonitor List request. -func HandleHealthmonitorListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/healthmonitors", 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, HealthmonitorsListBody) - case "556c8345-28d8-4f84-a246-e04380b0461d": - fmt.Fprintf(w, `{ "healthmonitors": [] }`) - default: - t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker) - } - }) -} - -// HandleHealthmonitorCreationSuccessfully sets up the test server to respond to a healthmonitor creation request -// with a given response. -func HandleHealthmonitorCreationSuccessfully(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":"HTTP", - "pool_id":"84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", - "tenant_id":"453105b9-1754-413f-aab1-55f1af620750", - "delay":20, - "name":"db", - "timeout":10, - "max_retries":5, - "url_path":"/check", - "expected_codes":"200-299" - } - }`) - - 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) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - - fmt.Fprintf(w, SingleHealthmonitorBody) - }) -} - -// HandleHealthmonitorDeletionSuccessfully sets up the test server to respond to a healthmonitor deletion request. -func HandleHealthmonitorDeletionSuccessfully(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, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.WriteHeader(http.StatusNoContent) - }) -} - -// HandleHealthmonitorUpdateSuccessfully sets up the test server to respond to a healthmonitor Update request. -func HandleHealthmonitorUpdateSuccessfully(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": { - "name": "NewHealthmonitorName", - "delay": 3, - "timeout": 20, - "max_retries": 10, - "url_path": "/another_check", - "expected_codes": "301" - } - }`) - - fmt.Fprintf(w, PostUpdateHealthmonitorBody) - }) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go deleted file mode 100644 index dfaaf67188..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/testing/requests_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package testing - -import ( - "context" - "testing" - - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestListHealthmonitors(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleHealthmonitorListSuccessfully(t) - - pages := 0 - err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pages++ - - actual, err := monitors.ExtractMonitors(page) - if err != nil { - return false, err - } - - if len(actual) != 2 { - t.Fatalf("Expected 2 healthmonitors, got %d", len(actual)) - } - th.CheckDeepEquals(t, HealthmonitorWeb, actual[0]) - th.CheckDeepEquals(t, HealthmonitorDb, actual[1]) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} - -func TestListAllHealthmonitors(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleHealthmonitorListSuccessfully(t) - - allPages, err := monitors.List(fake.ServiceClient(), monitors.ListOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err := monitors.ExtractMonitors(allPages) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, HealthmonitorWeb, actual[0]) - th.CheckDeepEquals(t, HealthmonitorDb, actual[1]) -} - -func TestCreateHealthmonitor(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleHealthmonitorCreationSuccessfully(t, SingleHealthmonitorBody) - - actual, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ - Type: "HTTP", - Name: "db", - PoolID: "84f1b61f-58c4-45bf-a8a9-2dafb9e5214d", - TenantID: "453105b9-1754-413f-aab1-55f1af620750", - Delay: 20, - Timeout: 10, - MaxRetries: 5, - URLPath: "/check", - ExpectedCodes: "200-299", - }).Extract() - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, HealthmonitorDb, *actual) -} - -func TestRequiredCreateOpts(t *testing.T) { - res := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{Type: monitors.TypeHTTP}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } -} - -func TestGetHealthmonitor(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleHealthmonitorGetSuccessfully(t) - - client := fake.ServiceClient() - actual, err := monitors.Get(context.TODO(), client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, HealthmonitorDb, *actual) -} - -func TestDeleteHealthmonitor(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleHealthmonitorDeletionSuccessfully(t) - - res := monitors.Delete(context.TODO(), fake.ServiceClient(), "5d4b5228-33b0-4e60-b225-9b727c1a20e7") - th.AssertNoErr(t, res.Err) -} - -func TestUpdateHealthmonitor(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleHealthmonitorUpdateSuccessfully(t) - - client := fake.ServiceClient() - name := "NewHealthmonitorName" - actual, err := monitors.Update(context.TODO(), client, "5d4b5228-33b0-4e60-b225-9b727c1a20e7", monitors.UpdateOpts{ - Name: &name, - Delay: 3, - Timeout: 20, - MaxRetries: 10, - URLPath: "/another_check", - ExpectedCodes: "301", - }).Extract() - if err != nil { - t.Fatalf("Unexpected Update error: %v", err) - } - - th.CheckDeepEquals(t, HealthmonitorUpdated, *actual) -} - -func TestDelayMustBeGreaterOrEqualThanTimeout(t *testing.T) { - _, err := monitors.Create(context.TODO(), fake.ServiceClient(), monitors.CreateOpts{ - Type: "HTTP", - PoolID: "d459f7d8-c6ee-439d-8713-d3fc08aeed8d", - Delay: 1, - Timeout: 10, - MaxRetries: 5, - URLPath: "/check", - ExpectedCodes: "200-299", - }).Extract() - - if err == nil { - t.Fatalf("Expected error, got none") - } - - _, err = monitors.Update(context.TODO(), fake.ServiceClient(), "453105b9-1754-413f-aab1-55f1af620750", monitors.UpdateOpts{ - Delay: 1, - Timeout: 10, - }).Extract() - - if err == nil { - t.Fatalf("Expected error, got none") - } -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go b/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go deleted file mode 100644 index d5723a305f..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/monitors/urls.go +++ /dev/null @@ -1,16 +0,0 @@ -package monitors - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lbaas" - resourcePath = "healthmonitors" -) - -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/networking/v2/extensions/lbaas_v2/pools/doc.go b/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go deleted file mode 100644 index 0697148680..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/doc.go +++ /dev/null @@ -1,126 +0,0 @@ -/* -Package pools provides information and interaction with Pools and -Members of the LBaaS v2 extension for the OpenStack Networking service. - -Example to List Pools - - listOpts := pools.ListOpts{ - LoadbalancerID: "c79a4468-d788-410c-bf79-9a8ef6354852", - } - - allPages, err := pools.List(networkClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allPools, err := pools.ExtractPools(allPages) - if err != nil { - panic(err) - } - - for _, pools := range allPools { - fmt.Printf("%+v\n", pool) - } - -Example to Create a Pool - - createOpts := pools.CreateOpts{ - LBMethod: pools.LBMethodRoundRobin, - Protocol: "HTTP", - Name: "Example pool", - LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", - } - - pool, err := pools.Create(networkClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Pool - - poolID := "d67d56a6-4a86-4688-a282-f46444705c64" - - updateOpts := pools.UpdateOpts{ - Name: "new-name", - } - - pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Pool - - poolID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := pools.Delete(networkClient, poolID).ExtractErr() - if err != nil { - panic(err) - } - -Example to List Pool Members - - poolID := "d67d56a6-4a86-4688-a282-f46444705c64" - - listOpts := pools.ListMemberOpts{ - ProtocolPort: 80, - } - - allPages, err := pools.ListMembers(networkClient, poolID, listOpts).AllPages() - if err != nil { - panic(err) - } - - allMembers, err := pools.ExtractMembers(allPages) - if err != nil { - panic(err) - } - - for _, member := allMembers { - fmt.Printf("%+v\n", member) - } - -Example to Create a Member - - poolID := "d67d56a6-4a86-4688-a282-f46444705c64" - - weight := 10 - createOpts := pools.CreateMemberOpts{ - Name: "db", - SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - Address: "10.0.2.11", - ProtocolPort: 80, - Weight: &weight, - } - - member, err := pools.CreateMember(networkClient, poolID, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Member - - poolID := "d67d56a6-4a86-4688-a282-f46444705c64" - memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" - - weight := 4 - updateOpts := pools.UpdateMemberOpts{ - Name: "new-name", - Weight: &weight, - } - - member, err := pools.UpdateMember(networkClient, poolID, memberID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Member - - poolID := "d67d56a6-4a86-4688-a282-f46444705c64" - memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" - - err := pools.DeleteMember(networkClient, poolID, memberID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package pools diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go b/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go deleted file mode 100644 index a5badad272..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/requests.go +++ /dev/null @@ -1,366 +0,0 @@ -package pools - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToPoolListQuery() (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 Pool attributes you want to see returned. SortKey allows you to -// sort by a particular Pool attribute. SortDir sets the direction, and is -// either `asc' or `desc'. Marker and Limit are used for pagination. -type ListOpts struct { - LBMethod string `q:"lb_algorithm"` - Protocol string `q:"protocol"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - AdminStateUp *bool `q:"admin_state_up"` - Name string `q:"name"` - ID string `q:"id"` - LoadbalancerID string `q:"loadbalancer_id"` - ListenerID string `q:"listener_id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToPoolListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToPoolListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns a Pager which allows you to iterate over a collection of -// pools. It accepts a ListOpts struct, which allows you to filter and sort -// the returned collection for greater efficiency. -// -// Default policy settings return only those pools that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := rootURL(c) - if opts != nil { - query, err := opts.ToPoolListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return PoolPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -type LBMethod string -type Protocol string - -// Supported attributes for create/update operations. -const ( - LBMethodRoundRobin LBMethod = "ROUND_ROBIN" - LBMethodLeastConnections LBMethod = "LEAST_CONNECTIONS" - LBMethodSourceIp LBMethod = "SOURCE_IP" - - ProtocolTCP Protocol = "TCP" - ProtocolHTTP Protocol = "HTTP" - ProtocolHTTPS Protocol = "HTTPS" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToPoolCreateMap() (map[string]interface{}, error) -} - -// CreateOpts is the common options struct used in this package's Create -// 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. - LBMethod LBMethod `json:"lb_algorithm" required:"true"` - - // The protocol used by the pool members, you can use either - // ProtocolTCP, ProtocolHTTP, or ProtocolHTTPS. - Protocol Protocol `json:"protocol" required:"true"` - - // The Loadbalancer on which the members of the pool will be associated with. - // Note: one of LoadbalancerID or ListenerID must be provided. - LoadbalancerID string `json:"loadbalancer_id,omitempty" xor:"ListenerID"` - - // The Listener on which the members of the pool will be associated with. - // Note: one of LoadbalancerID or ListenerID must be provided. - ListenerID string `json:"listener_id,omitempty" xor:"LoadbalancerID"` - - // TenantID is the UUID of the project who owns the Pool. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - - // ProjectID is the UUID of the project who owns the Pool. - // Only administrative users can specify a project UUID other than their own. - ProjectID string `json:"project_id,omitempty"` - - // Name of the pool. - Name string `json:"name,omitempty"` - - // Human-readable description for the pool. - Description string `json:"description,omitempty"` - - // Persistence is the session persistence of the pool. - // Omit this field to prevent session persistence. - Persistence *SessionPersistence `json:"session_persistence,omitempty"` - - // The administrative state of the Pool. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToPoolCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "pool") -} - -// Create accepts a CreateOpts struct and uses the values to create a new -// load balancer pool. -func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToPoolCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves a particular pool based on its unique ID. -func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToPoolUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts is the common options struct used in this package's Update -// operation. -type UpdateOpts struct { - // Name of the pool. - Name *string `json:"name,omitempty"` - - // Human-readable description for the pool. - 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. - LBMethod LBMethod `json:"lb_algorithm,omitempty"` - - // The administrative state of the Pool. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToPoolUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "pool") -} - -// Update allows pools to be updated. -func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToPoolUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will permanently delete a particular pool based on its unique ID. -func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, resourceURL(c, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListMemberOptsBuilder allows extensions to add additional parameters to the -// ListMembers request. -type ListMembersOptsBuilder interface { - ToMembersListQuery() (string, error) -} - -// ListMembersOpts allows the filtering and sorting of paginated collections -// through the API. Filtering is achieved by passing in struct field values -// that map to the Member attributes you want to see returned. SortKey allows -// you to sort by a particular Member attribute. SortDir sets the direction, -// and is either `asc' or `desc'. Marker and Limit are used for pagination. -type ListMembersOpts struct { - Name string `q:"name"` - Weight int `q:"weight"` - AdminStateUp *bool `q:"admin_state_up"` - TenantID string `q:"tenant_id"` - Address string `q:"address"` - ProtocolPort int `q:"protocol_port"` - ID string `q:"id"` - Limit int `q:"limit"` - Marker string `q:"marker"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` -} - -// ToMemberListQuery formats a ListOpts into a query string. -func (opts ListMembersOpts) ToMembersListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// ListMembers returns a Pager which allows you to iterate over a collection of -// members. It accepts a ListMembersOptsBuilder, which allows you to filter and -// sort the returned collection for greater efficiency. -// -// Default policy settings return only those members that are owned by the -// tenant who submits the request, unless an admin user submits the request. -func ListMembers(c *gophercloud.ServiceClient, poolID string, opts ListMembersOptsBuilder) pagination.Pager { - url := memberRootURL(c, poolID) - if opts != nil { - query, err := opts.ToMembersListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return MemberPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateMemberOptsBuilder allows extensions to add additional parameters to the -// CreateMember request. -type CreateMemberOptsBuilder interface { - ToMemberCreateMap() (map[string]interface{}, error) -} - -// CreateMemberOpts is the common options struct used in this package's CreateMember -// operation. -type CreateMemberOpts struct { - // The IP address of the member to receive traffic from the load balancer. - Address string `json:"address" required:"true"` - - // The port on which to listen for client traffic. - ProtocolPort int `json:"protocol_port" required:"true"` - - // Name of the Member. - Name string `json:"name,omitempty"` - - // TenantID is the UUID of the project who owns the Member. - // Only administrative users can specify a project UUID other than their own. - TenantID string `json:"tenant_id,omitempty"` - - // ProjectID is the UUID of the project who owns the Member. - // Only administrative users can specify a project UUID other than their own. - ProjectID string `json:"project_id,omitempty"` - - // A positive integer value that indicates the relative portion of traffic - // that this member should receive from the pool. For example, a member with - // a weight of 10 receives five times as much traffic as a member with a - // weight of 2. - Weight *int `json:"weight,omitempty"` - - // If you omit this parameter, LBaaS uses the vip_subnet_id parameter value - // for the subnet UUID. - SubnetID string `json:"subnet_id,omitempty"` - - // The administrative state of the Pool. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToMemberCreateMap builds a request body from CreateMemberOpts. -func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "member") -} - -// CreateMember will create and associate a Member with a particular Pool. -func CreateMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, opts CreateMemberOpts) (r CreateMemberResult) { - b, err := opts.ToMemberCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Post(ctx, memberRootURL(c, poolID), b, &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// GetMember retrieves a particular Pool Member based on its unique ID. -func GetMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { - resp, err := c.Get(ctx, memberResourceURL(c, poolID, memberID), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateMemberOptsBuilder allows extensions to add additional parameters to the -// List request. -type UpdateMemberOptsBuilder interface { - ToMemberUpdateMap() (map[string]interface{}, error) -} - -// UpdateMemberOpts is the common options struct used in this package's Update -// operation. -type UpdateMemberOpts struct { - // Name of the Member. - Name *string `json:"name,omitempty"` - - // A positive integer value that indicates the relative portion of traffic - // that this member should receive from the pool. For example, a member with - // a weight of 10 receives five times as much traffic as a member with a - // weight of 2. - Weight *int `json:"weight,omitempty"` - - // The administrative state of the Pool. A valid value is true (UP) - // or false (DOWN). - AdminStateUp *bool `json:"admin_state_up,omitempty"` -} - -// ToMemberUpdateMap builds a request body from UpdateMemberOpts. -func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "member") -} - -// Update allows Member to be updated. -func UpdateMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string, opts UpdateMemberOptsBuilder) (r UpdateMemberResult) { - b, err := opts.ToMemberUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := c.Put(ctx, memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// DisassociateMember will remove and disassociate a Member from a particular -// Pool. -func DeleteMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { - resp, err := c.Delete(ctx, memberResourceURL(c, poolID, memberID), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go b/openstack/networking/v2/extensions/lbaas_v2/pools/results.go deleted file mode 100644 index 7ef9698f79..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/results.go +++ /dev/null @@ -1,304 +0,0 @@ -package pools - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/monitors" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// SessionPersistence represents the session persistence feature of the load -// balancing service. It attempts to force connections or requests in the same -// session to be processed by the same member as long as it is ative. Three -// 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. -// -// 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. -// -// 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. -type SessionPersistence struct { - // The type of persistence mode. - Type string `json:"type"` - - // Name of cookie if persistence mode is set appropriately. - CookieName string `json:"cookie_name,omitempty"` -} - -// LoadBalancerID represents a load balancer. -type LoadBalancerID struct { - ID string `json:"id"` -} - -// ListenerID represents a listener. -type ListenerID struct { - ID string `json:"id"` -} - -// Pool represents a logical set of devices, such as web servers, that you -// group together to receive and process traffic. The load balancing function -// chooses a Member of the Pool according to the configured load balancing -// method to handle the new requests or connections received on the VIP address. -type Pool struct { - // The load-balancer algorithm, which is round-robin, least-connections, and - // so on. This value, which must be supported, is dependent on the provider. - // Round-robin must be supported. - LBMethod string `json:"lb_algorithm"` - - // The protocol of the Pool, which is TCP, HTTP, or HTTPS. - Protocol string `json:"protocol"` - - // Description for the Pool. - Description string `json:"description"` - - // A list of listeners objects IDs. - Listeners []ListenerID `json:"listeners"` //[]map[string]interface{} - - // A list of member objects IDs. - Members []Member `json:"members"` - - // The ID of associated health monitor. - MonitorID string `json:"healthmonitor_id"` - - // The network on which the members of the Pool will be located. Only members - // that are on this network can be added to the Pool. - SubnetID string `json:"subnet_id"` - - // Owner of the Pool. - TenantID string `json:"tenant_id"` - - // The administrative state of the Pool, which is up (true) or down (false). - AdminStateUp bool `json:"admin_state_up"` - - // Pool name. Does not have to be unique. - Name string `json:"name"` - - // The unique ID for the Pool. - ID string `json:"id"` - - // A list of load balancer objects IDs. - Loadbalancers []LoadBalancerID `json:"loadbalancers"` - - // Indicates whether connections in the same session will be processed by the - // same Pool member or not. - Persistence SessionPersistence `json:"session_persistence"` - - // The load balancer provider. - Provider string `json:"provider"` - - // The Monitor associated with this Pool. - Monitor monitors.Monitor `json:"healthmonitor"` - - // The provisioning status of the pool. - // This value is ACTIVE, PENDING_* or ERROR. - ProvisioningStatus string `json:"provisioning_status"` - - // The operating status of the pool. - // This field seems to only be returned during a call to a load balancer's /status - // see: https://github.com/gophercloud/gophercloud/issues/1362 - OperatingStatus string `json:"operating_status"` -} - -// PoolPage is the page returned by a pager when traversing over a -// collection of pools. -type PoolPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of pools 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 PoolPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"pools_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractPools accepts a Page struct, specifically a PoolPage struct, -// and extracts the elements into a slice of Pool structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractPools(r pagination.Page) ([]Pool, error) { - var s struct { - Pools []Pool `json:"pools"` - } - err := (r.(PoolPage)).ExtractInto(&s) - return s.Pools, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts a pool. -func (r commonResult) Extract() (*Pool, error) { - var s struct { - Pool *Pool `json:"pool"` - } - err := r.ExtractInto(&s) - return s.Pool, err -} - -// CreateResult represents the result of a Create operation. Call its Extract -// method to interpret the result as a Pool. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a Get operation. Call its Extract -// method to interpret the result as a Pool. -type GetResult struct { - commonResult -} - -// UpdateResult represents the result of an Update operation. Call its Extract -// method to interpret the result as a Pool. -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 -} - -// Member represents the application running on a backend server. -type Member struct { - // Name of the Member. - Name string `json:"name"` - - // Weight of Member. - Weight int `json:"weight"` - - // The administrative state of the member, which is up (true) or down (false). - AdminStateUp bool `json:"admin_state_up"` - - // Owner of the Member. - TenantID string `json:"tenant_id"` - - // Parameter value for the subnet UUID. - SubnetID string `json:"subnet_id"` - - // The Pool to which the Member belongs. - PoolID string `json:"pool_id"` - - // The IP address of the Member. - Address string `json:"address"` - - // The port on which the application is hosted. - ProtocolPort int `json:"protocol_port"` - - // The unique ID for the Member. - ID string `json:"id"` - - // The provisioning status of the member. - // This value is ACTIVE, PENDING_* or ERROR. - ProvisioningStatus string `json:"provisioning_status"` - - // The operating status of the member. - // This field seems to only be returned during a call to a load balancer's /status - // see: https://github.com/gophercloud/gophercloud/issues/1362 - OperatingStatus string `json:"operating_status"` -} - -// MemberPage is the page returned by a pager when traversing over a -// collection of Members in a Pool. -type MemberPage struct { - pagination.LinkedPageBase -} - -// NextPageURL is invoked when a paginated collection of members 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 MemberPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"members_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// 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 -} - -// ExtractMembers accepts a Page struct, specifically a MemberPage struct, -// and extracts the elements into a slice of Members structs. In other words, -// a generic collection is mapped into a relevant slice. -func ExtractMembers(r pagination.Page) ([]Member, error) { - var s struct { - Members []Member `json:"members"` - } - err := (r.(MemberPage)).ExtractInto(&s) - return s.Members, err -} - -type commonMemberResult struct { - gophercloud.Result -} - -// ExtractMember is a function that accepts a result and extracts a member. -func (r commonMemberResult) Extract() (*Member, error) { - var s struct { - Member *Member `json:"member"` - } - err := r.ExtractInto(&s) - return s.Member, err -} - -// CreateMemberResult represents the result of a CreateMember operation. -// Call its Extract method to interpret it as a Member. -type CreateMemberResult struct { - commonMemberResult -} - -// GetMemberResult represents the result of a GetMember operation. -// Call its Extract method to interpret it as a Member. -type GetMemberResult struct { - commonMemberResult -} - -// UpdateMemberResult represents the result of an UpdateMember operation. -// Call its Extract method to interpret it as a Member. -type UpdateMemberResult struct { - commonMemberResult -} - -// DeleteMemberResult represents the result of a DeleteMember operation. -// Call its ExtractErr method to determine if the request succeeded or failed. -type DeleteMemberResult struct { - gophercloud.ErrResult -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/doc.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/doc.go deleted file mode 100644 index 46e335f3f2..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// pools unit tests -package testing diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go deleted file mode 100644 index 5efc3e0145..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/fixtures_test.go +++ /dev/null @@ -1,388 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// PoolsListBody contains the canned body of a pool list response. -const PoolsListBody = ` -{ - "pools":[ - { - "lb_algorithm":"ROUND_ROBIN", - "protocol":"HTTP", - "description":"", - "healthmonitor_id": "466c8345-28d8-4f84-a246-e04380b0461d", - "members":[{"id": "53306cda-815d-4354-9fe4-59e09da9c3c5"}], - "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], - "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], - "id":"72741b06-df4d-4715-b142-276b6bce75ab", - "name":"web", - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "provider": "haproxy" - }, - { - "lb_algorithm":"LEAST_CONNECTION", - "protocol":"HTTP", - "description":"", - "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", - "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], - "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], - "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], - "id":"c3741b06-df4d-4715-b142-276b6bce75ab", - "name":"db", - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "provider": "haproxy" - } - ] -} -` - -// SinglePoolBody is the canned body of a Get request on an existing pool. -const SinglePoolBody = ` -{ - "pool": { - "lb_algorithm":"LEAST_CONNECTION", - "protocol":"HTTP", - "description":"", - "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", - "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], - "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], - "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], - "id":"c3741b06-df4d-4715-b142-276b6bce75ab", - "name":"db", - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "provider": "haproxy" - } -} -` - -// PostUpdatePoolBody is the canned response body of a Update request on an existing pool. -const PostUpdatePoolBody = ` -{ - "pool": { - "lb_algorithm":"LEAST_CONNECTION", - "protocol":"HTTP", - "description":"", - "healthmonitor_id": "5f6c8345-28d8-4f84-a246-e04380b0461d", - "members":[{"id": "67306cda-815d-4354-9fe4-59e09da9c3c5"}], - "listeners":[{"id": "2a280670-c202-4b0b-a562-34077415aabf"}], - "loadbalancers":[{"id": "79e05663-7f03-45d2-a092-8b94062f22ab"}], - "id":"c3741b06-df4d-4715-b142-276b6bce75ab", - "name":"db", - "admin_state_up":true, - "tenant_id":"83657cfcdfe44cd5920adaf26c48ceea", - "provider": "haproxy" - } -} -` - -var ( - PoolWeb = pools.Pool{ - LBMethod: "ROUND_ROBIN", - Protocol: "HTTP", - Description: "", - MonitorID: "466c8345-28d8-4f84-a246-e04380b0461d", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - AdminStateUp: true, - Name: "web", - Members: []pools.Member{{ID: "53306cda-815d-4354-9fe4-59e09da9c3c5"}}, - ID: "72741b06-df4d-4715-b142-276b6bce75ab", - Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, - Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, - Provider: "haproxy", - } - PoolDb = pools.Pool{ - LBMethod: "LEAST_CONNECTION", - Protocol: "HTTP", - Description: "", - MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - AdminStateUp: true, - Name: "db", - Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, - ID: "c3741b06-df4d-4715-b142-276b6bce75ab", - Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, - Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, - Provider: "haproxy", - } - PoolUpdated = pools.Pool{ - LBMethod: "LEAST_CONNECTION", - Protocol: "HTTP", - Description: "", - MonitorID: "5f6c8345-28d8-4f84-a246-e04380b0461d", - TenantID: "83657cfcdfe44cd5920adaf26c48ceea", - AdminStateUp: true, - Name: "db", - Members: []pools.Member{{ID: "67306cda-815d-4354-9fe4-59e09da9c3c5"}}, - ID: "c3741b06-df4d-4715-b142-276b6bce75ab", - Loadbalancers: []pools.LoadBalancerID{{ID: "79e05663-7f03-45d2-a092-8b94062f22ab"}}, - Listeners: []pools.ListenerID{{ID: "2a280670-c202-4b0b-a562-34077415aabf"}}, - Provider: "haproxy", - } -) - -// HandlePoolListSuccessfully sets up the test server to respond to a pool List request. -func HandlePoolListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/pools", 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, PoolsListBody) - case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "pools": [] }`) - default: - t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker) - } - }) -} - -// HandlePoolCreationSuccessfully sets up the test server to respond to a pool creation request -// with a given response. -func HandlePoolCreationSuccessfully(t *testing.T, response string) { - th.Mux.HandleFunc("/v2.0/lbaas/pools", 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, `{ - "pool": { - "lb_algorithm": "ROUND_ROBIN", - "protocol": "HTTP", - "name": "Example pool", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", - "loadbalancer_id": "79e05663-7f03-45d2-a092-8b94062f22ab" - } - }`) - - w.WriteHeader(http.StatusAccepted) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) - }) -} - -// HandlePoolGetSuccessfully sets up the test server to respond to a pool Get request. -func HandlePoolGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", 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, SinglePoolBody) - }) -} - -// HandlePoolDeletionSuccessfully sets up the test server to respond to a pool deletion request. -func HandlePoolDeletionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", 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) - }) -} - -// HandlePoolUpdateSuccessfully sets up the test server to respond to a pool Update request. -func HandlePoolUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/pools/c3741b06-df4d-4715-b142-276b6bce75ab", 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, `{ - "pool": { - "name": "NewPoolName", - "lb_algorithm": "LEAST_CONNECTIONS" - } - }`) - - fmt.Fprintf(w, PostUpdatePoolBody) - }) -} - -// MembersListBody contains the canned body of a member list response. -const MembersListBody = ` -{ - "members":[ - { - "id": "2a280670-c202-4b0b-a562-34077415aabf", - "address": "10.0.2.10", - "weight": 5, - "name": "web", - "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", - "admin_state_up":true, - "protocol_port": 80 - }, - { - "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", - "address": "10.0.2.11", - "weight": 10, - "name": "db", - "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", - "admin_state_up":false, - "protocol_port": 80 - } - ] -} -` - -// SingleMemberBody is the canned body of a Get request on an existing member. -const SingleMemberBody = ` -{ - "member": { - "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", - "address": "10.0.2.11", - "weight": 10, - "name": "db", - "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", - "admin_state_up":false, - "protocol_port": 80 - } -} -` - -// PostUpdateMemberBody is the canned response body of a Update request on an existing member. -const PostUpdateMemberBody = ` -{ - "member": { - "id": "fad389a3-9a4a-4762-a365-8c7038508b5d", - "address": "10.0.2.11", - "weight": 10, - "name": "db", - "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", - "admin_state_up":false, - "protocol_port": 80 - } -} -` - -var ( - MemberWeb = pools.Member{ - SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", - AdminStateUp: true, - Name: "web", - ID: "2a280670-c202-4b0b-a562-34077415aabf", - Address: "10.0.2.10", - Weight: 5, - ProtocolPort: 80, - } - MemberDb = pools.Member{ - SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", - AdminStateUp: false, - Name: "db", - ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", - Address: "10.0.2.11", - Weight: 10, - ProtocolPort: 80, - } - MemberUpdated = pools.Member{ - SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", - AdminStateUp: false, - Name: "db", - ID: "fad389a3-9a4a-4762-a365-8c7038508b5d", - Address: "10.0.2.11", - Weight: 10, - ProtocolPort: 80, - } -) - -// HandleMemberListSuccessfully sets up the test server to respond to a member List request. -func HandleMemberListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", 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, MembersListBody) - case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "members": [] }`) - default: - t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker) - } - }) -} - -// HandleMemberCreationSuccessfully sets up the test server to respond to a member creation request -// with a given response. -func HandleMemberCreationSuccessfully(t *testing.T, response string) { - th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members", 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, `{ - "member": { - "address": "10.0.2.11", - "weight": 10, - "name": "db", - "subnet_id": "1981f108-3c48-48d2-b908-30f7d28532c9", - "tenant_id": "2ffc6e22aae24e4795f87155d24c896f", - "protocol_port": 80 - } - }`) - - w.WriteHeader(http.StatusAccepted) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) - }) -} - -// HandleMemberGetSuccessfully sets up the test server to respond to a member Get request. -func HandleMemberGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", 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, SingleMemberBody) - }) -} - -// HandleMemberDeletionSuccessfully sets up the test server to respond to a member deletion request. -func HandleMemberDeletionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", 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) - }) -} - -// HandleMemberUpdateSuccessfully sets up the test server to respond to a member Update request. -func HandleMemberUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members/2a280670-c202-4b0b-a562-34077415aabf", 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, `{ - "member": { - "name": "newMemberName", - "weight": 4 - } - }`) - - fmt.Fprintf(w, PostUpdateMemberBody) - }) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go b/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go deleted file mode 100644 index 87c882bc6f..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/testing/requests_test.go +++ /dev/null @@ -1,267 +0,0 @@ -package testing - -import ( - "context" - "testing" - - fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/lbaas_v2/pools" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestListPools(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandlePoolListSuccessfully(t) - - pages := 0 - err := pools.List(fake.ServiceClient(), pools.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pages++ - - actual, err := pools.ExtractPools(page) - if err != nil { - return false, err - } - - if len(actual) != 2 { - t.Fatalf("Expected 2 pools, got %d", len(actual)) - } - th.CheckDeepEquals(t, PoolWeb, actual[0]) - th.CheckDeepEquals(t, PoolDb, actual[1]) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} - -func TestListAllPools(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandlePoolListSuccessfully(t) - - allPages, err := pools.List(fake.ServiceClient(), pools.ListOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err := pools.ExtractPools(allPages) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, PoolWeb, actual[0]) - th.CheckDeepEquals(t, PoolDb, actual[1]) -} - -func TestCreatePool(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandlePoolCreationSuccessfully(t, SinglePoolBody) - - actual, err := pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ - LBMethod: pools.LBMethodRoundRobin, - Protocol: "HTTP", - Name: "Example pool", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", - LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", - }).Extract() - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, PoolDb, *actual) -} - -func TestGetPool(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandlePoolGetSuccessfully(t) - - client := fake.ServiceClient() - actual, err := pools.Get(context.TODO(), client, "c3741b06-df4d-4715-b142-276b6bce75ab").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, PoolDb, *actual) -} - -func TestDeletePool(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandlePoolDeletionSuccessfully(t) - - res := pools.Delete(context.TODO(), fake.ServiceClient(), "c3741b06-df4d-4715-b142-276b6bce75ab") - th.AssertNoErr(t, res.Err) -} - -func TestUpdatePool(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandlePoolUpdateSuccessfully(t) - - client := fake.ServiceClient() - name := "NewPoolName" - actual, err := pools.Update(context.TODO(), client, "c3741b06-df4d-4715-b142-276b6bce75ab", pools.UpdateOpts{ - Name: &name, - LBMethod: pools.LBMethodLeastConnections, - }).Extract() - if err != nil { - t.Fatalf("Unexpected Update error: %v", err) - } - - th.CheckDeepEquals(t, PoolUpdated, *actual) -} - -func TestRequiredPoolCreateOpts(t *testing.T) { - res := pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ - LBMethod: pools.LBMethod("invalid"), - Protocol: pools.ProtocolHTTPS, - LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", - }) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } - - res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ - LBMethod: pools.LBMethodRoundRobin, - Protocol: pools.Protocol("invalid"), - LoadbalancerID: "69055154-f603-4a28-8951-7cc2d9e54a9a", - }) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } - - res = pools.Create(context.TODO(), fake.ServiceClient(), pools.CreateOpts{ - LBMethod: pools.LBMethodRoundRobin, - Protocol: pools.ProtocolHTTPS, - }) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } -} - -func TestListMembers(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleMemberListSuccessfully(t) - - pages := 0 - err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pages++ - - actual, err := pools.ExtractMembers(page) - if err != nil { - return false, err - } - - if len(actual) != 2 { - t.Fatalf("Expected 2 members, got %d", len(actual)) - } - th.CheckDeepEquals(t, MemberWeb, actual[0]) - th.CheckDeepEquals(t, MemberDb, actual[1]) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) - } -} - -func TestListAllMembers(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleMemberListSuccessfully(t) - - allPages, err := pools.ListMembers(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.ListMembersOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err := pools.ExtractMembers(allPages) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, MemberWeb, actual[0]) - th.CheckDeepEquals(t, MemberDb, actual[1]) -} - -func TestCreateMember(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleMemberCreationSuccessfully(t, SingleMemberBody) - - weight := 10 - actual, err := pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ - Name: "db", - SubnetID: "1981f108-3c48-48d2-b908-30f7d28532c9", - TenantID: "2ffc6e22aae24e4795f87155d24c896f", - Address: "10.0.2.11", - ProtocolPort: 80, - Weight: &weight, - }).Extract() - th.AssertNoErr(t, err) - - th.CheckDeepEquals(t, MemberDb, *actual) -} - -func TestRequiredMemberCreateOpts(t *testing.T) { - res := pools.CreateMember(context.TODO(), fake.ServiceClient(), "", pools.CreateMemberOpts{}) - if res.Err == nil { - t.Fatalf("Expected error, got none") - } - res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "", pools.CreateMemberOpts{Address: "1.2.3.4", ProtocolPort: 80}) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } - res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{ProtocolPort: 80}) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } - res = pools.CreateMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", pools.CreateMemberOpts{Address: "1.2.3.4"}) - if res.Err == nil { - t.Fatalf("Expected error, but got none") - } -} - -func TestGetMember(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleMemberGetSuccessfully(t) - - client := fake.ServiceClient() - actual, err := pools.GetMember(context.TODO(), client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf").Extract() - if err != nil { - t.Fatalf("Unexpected Get error: %v", err) - } - - th.CheckDeepEquals(t, MemberDb, *actual) -} - -func TestDeleteMember(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleMemberDeletionSuccessfully(t) - - res := pools.DeleteMember(context.TODO(), fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf") - th.AssertNoErr(t, res.Err) -} - -func TestUpdateMember(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleMemberUpdateSuccessfully(t) - - weight := 4 - client := fake.ServiceClient() - name := "newMemberName" - actual, err := pools.UpdateMember(context.TODO(), client, "332abe93-f488-41ba-870b-2ac66be7f853", "2a280670-c202-4b0b-a562-34077415aabf", pools.UpdateMemberOpts{ - Name: &name, - Weight: &weight, - }).Extract() - if err != nil { - t.Fatalf("Unexpected Update error: %v", err) - } - - th.CheckDeepEquals(t, MemberUpdated, *actual) -} diff --git a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go b/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go deleted file mode 100644 index a362f1b957..0000000000 --- a/openstack/networking/v2/extensions/lbaas_v2/pools/urls.go +++ /dev/null @@ -1,25 +0,0 @@ -package pools - -import "github.com/gophercloud/gophercloud/v2" - -const ( - rootPath = "lbaas" - resourcePath = "pools" - memberPath = "members" -) - -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) -} - -func memberRootURL(c *gophercloud.ServiceClient, poolId string) string { - return c.ServiceURL(rootPath, resourcePath, poolId, memberPath) -} - -func memberResourceURL(c *gophercloud.ServiceClient, poolID string, memberID string) string { - return c.ServiceURL(rootPath, resourcePath, poolID, memberPath, memberID) -} From 684f19b027ad794a99607b95e8c57d5533ff2f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 22 Mar 2024 08:22:11 +0100 Subject: [PATCH 1793/2296] Remove CDN fixtures The PR removing the code for CDN [1] merged around the same time the fixtures were modified to no longer be part of the public API [2], causing these files to stay in the tree. This commit cleans up files that shouldn't be here. [1] https://github.com/gophercloud/gophercloud/pull/2809 [2] https://github.com/gophercloud/gophercloud/pull/2811 --- .../cdn/v1/base/testing/fixtures_test.go | 53 --- .../cdn/v1/flavors/testing/fixtures_test.go | 82 ---- .../v1/serviceassets/testing/fixtures_test.go | 19 - .../cdn/v1/services/testing/fixtures_test.go | 372 ------------------ 4 files changed, 526 deletions(-) delete mode 100644 openstack/cdn/v1/base/testing/fixtures_test.go delete mode 100644 openstack/cdn/v1/flavors/testing/fixtures_test.go delete mode 100644 openstack/cdn/v1/serviceassets/testing/fixtures_test.go delete mode 100644 openstack/cdn/v1/services/testing/fixtures_test.go diff --git a/openstack/cdn/v1/base/testing/fixtures_test.go b/openstack/cdn/v1/base/testing/fixtures_test.go deleted file mode 100644 index 137c7d4625..0000000000 --- a/openstack/cdn/v1/base/testing/fixtures_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// HandleGetSuccessfully creates an HTTP handler at `/` on the test handler mux -// that responds with a `Get` response. -func HandleGetSuccessfully(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", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "resources": { - "rel/cdn": { - "href-template": "services{?marker,limit}", - "href-vars": { - "marker": "param/marker", - "limit": "param/limit" - }, - "hints": { - "allow": [ - "GET" - ], - "formats": { - "application/json": {} - } - } - } - } - } - `) - - }) -} - -// HandlePingSuccessfully creates an HTTP handler at `/ping` on the test handler -// mux that responds with a `Ping` response. -func HandlePingSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusNoContent) - }) -} diff --git a/openstack/cdn/v1/flavors/testing/fixtures_test.go b/openstack/cdn/v1/flavors/testing/fixtures_test.go deleted file mode 100644 index 3c9b8b5e09..0000000000 --- a/openstack/cdn/v1/flavors/testing/fixtures_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// HandleListCDNFlavorsSuccessfully creates an HTTP handler at `/flavors` on the test handler mux -// that responds with a `List` response. -func HandleListCDNFlavorsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "flavors": [ - { - "id": "europe", - "providers": [ - { - "provider": "Fastly", - "links": [ - { - "href": "http://www.fastly.com", - "rel": "provider_url" - } - ] - } - ], - "links": [ - { - "href": "https://www.poppycdn.io/v1.0/flavors/europe", - "rel": "self" - } - ] - } - ] - } - `) - }) -} - -// HandleGetCDNFlavorSuccessfully creates an HTTP handler at `/flavors/{id}` on the test handler mux -// that responds with a `Get` response. -func HandleGetCDNFlavorSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/flavors/asia", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "id" : "asia", - "providers" : [ - { - "provider" : "ChinaCache", - "links": [ - { - "href": "http://www.chinacache.com", - "rel": "provider_url" - } - ] - } - ], - "links": [ - { - "href": "https://www.poppycdn.io/v1.0/flavors/asia", - "rel": "self" - } - ] - } - `) - }) -} diff --git a/openstack/cdn/v1/serviceassets/testing/fixtures_test.go b/openstack/cdn/v1/serviceassets/testing/fixtures_test.go deleted file mode 100644 index daf3088264..0000000000 --- a/openstack/cdn/v1/serviceassets/testing/fixtures_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// HandleDeleteCDNAssetSuccessfully creates an HTTP handler at `/services/{id}/assets` on the test handler mux -// that responds with a `Delete` response. -func HandleDeleteCDNAssetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets", 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) - }) -} diff --git a/openstack/cdn/v1/services/testing/fixtures_test.go b/openstack/cdn/v1/services/testing/fixtures_test.go deleted file mode 100644 index 278f9080f8..0000000000 --- a/openstack/cdn/v1/services/testing/fixtures_test.go +++ /dev/null @@ -1,372 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// HandleListCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux -// that responds with a `List` response. -func HandleListCDNServiceSuccessfully(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", fake.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ` - { - "links": [ - { - "rel": "next", - "href": "https://www.poppycdn.io/v1.0/services?marker=96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0&limit=20" - } - ], - "services": [ - { - "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - "name": "mywebsite.com", - "domains": [ - { - "domain": "www.mywebsite.com" - } - ], - "origins": [ - { - "origin": "mywebsite.com", - "port": 80, - "ssl": false - } - ], - "caching": [ - { - "name": "default", - "ttl": 3600 - }, - { - "name": "home", - "ttl": 17200, - "rules": [ - { - "name": "index", - "request_url": "/index.htm" - } - ] - }, - { - "name": "images", - "ttl": 12800, - "rules": [ - { - "name": "images", - "request_url": "*.png" - } - ] - } - ], - "restrictions": [ - { - "name": "website only", - "rules": [ - { - "name": "mywebsite.com", - "referrer": "www.mywebsite.com" - } - ] - } - ], - "flavor_id": "asia", - "status": "deployed", - "errors" : [], - "links": [ - { - "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - "rel": "self" - }, - { - "href": "mywebsite.com.cdn123.poppycdn.net", - "rel": "access_url" - }, - { - "href": "https://www.poppycdn.io/v1.0/flavors/asia", - "rel": "flavor" - } - ] - }, - { - "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", - "name": "myothersite.com", - "domains": [ - { - "domain": "www.myothersite.com" - } - ], - "origins": [ - { - "origin": "44.33.22.11", - "port": 80, - "ssl": false - }, - { - "origin": "77.66.55.44", - "port": 80, - "ssl": false, - "rules": [ - { - "name": "videos", - "request_url": "^/videos/*.m3u" - } - ] - } - ], - "caching": [ - { - "name": "default", - "ttl": 3600 - } - ], - "restrictions": [ - {} - ], - "flavor_id": "europe", - "status": "deployed", - "links": [ - { - "href": "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1", - "rel": "self" - }, - { - "href": "myothersite.com.poppycdn.net", - "rel": "access_url" - }, - { - "href": "https://www.poppycdn.io/v1.0/flavors/europe", - "rel": "flavor" - } - ] - } - ] - } - `) - case "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1": - fmt.Fprintf(w, `{ - "services": [] - }`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -// HandleCreateCDNServiceSuccessfully creates an HTTP handler at `/services` on the test handler mux -// that responds with a `Create` response. -func HandleCreateCDNServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestJSONRequest(t, r, ` - { - "name": "mywebsite.com", - "domains": [ - { - "domain": "www.mywebsite.com" - }, - { - "domain": "blog.mywebsite.com" - } - ], - "origins": [ - { - "origin": "mywebsite.com", - "port": 80, - "ssl": false - } - ], - "restrictions": [ - { - "name": "website only", - "rules": [ - { - "name": "mywebsite.com", - "referrer": "www.mywebsite.com" - } - ] - } - ], - "caching": [ - { - "name": "default", - "ttl": 3600 - } - ], - - "flavor_id": "cdn" - } - `) - w.Header().Add("Location", "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0") - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleGetCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux -// that responds with a `Get` response. -func HandleGetCDNServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "id": "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - "name": "mywebsite.com", - "domains": [ - { - "domain": "www.mywebsite.com", - "protocol": "http" - } - ], - "origins": [ - { - "origin": "mywebsite.com", - "port": 80, - "ssl": false - } - ], - "caching": [ - { - "name": "default", - "ttl": 3600 - }, - { - "name": "home", - "ttl": 17200, - "rules": [ - { - "name": "index", - "request_url": "/index.htm" - } - ] - }, - { - "name": "images", - "ttl": 12800, - "rules": [ - { - "name": "images", - "request_url": "*.png" - } - ] - } - ], - "restrictions": [ - { - "name": "website only", - "rules": [ - { - "name": "mywebsite.com", - "referrer": "www.mywebsite.com" - } - ] - } - ], - "flavor_id": "cdn", - "status": "deployed", - "errors" : [], - "links": [ - { - "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", - "rel": "self" - }, - { - "href": "blog.mywebsite.com.cdn1.raxcdn.com", - "rel": "access_url" - }, - { - "href": "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn", - "rel": "flavor" - } - ] - } - `) - }) -} - -// HandleUpdateCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux -// that responds with a `Update` response. -func HandleUpdateCDNServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestJSONRequest(t, r, ` - [ - { - "op": "add", - "path": "/domains/-", - "value": {"domain": "appended.mocksite4.com"} - }, - { - "op": "add", - "path": "/domains/4", - "value": {"domain": "inserted.mocksite4.com"} - }, - { - "op": "add", - "path": "/domains", - "value": [ - {"domain": "bulkadded1.mocksite4.com"}, - {"domain": "bulkadded2.mocksite4.com"} - ] - }, - { - "op": "replace", - "path": "/origins/2", - "value": {"origin": "44.33.22.11", "port": 80, "ssl": false} - }, - { - "op": "replace", - "path": "/origins", - "value": [ - {"origin": "44.33.22.11", "port": 80, "ssl": false}, - {"origin": "55.44.33.22", "port": 443, "ssl": true} - ] - }, - { - "op": "remove", - "path": "/caching/8" - }, - { - "op": "remove", - "path": "/caching" - }, - { - "op": "replace", - "path": "/name", - "value": "differentServiceName" - } - ] - `) - w.Header().Add("Location", "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0") - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleDeleteCDNServiceSuccessfully creates an HTTP handler at `/services/{id}` on the test handler mux -// that responds with a `Delete` response. -func HandleDeleteCDNServiceSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", 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) - }) -} From 9a80e5a9beb87432087911dabbbcd8c7d898df79 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 22 Mar 2024 14:56:19 +0000 Subject: [PATCH 1794/2296] blockstorage: Remove v1 implementation While the v2 API is crufty enough, there is still some usage of it in the wild. However, it's fair to say there is little to no remaining users of the v1 API, which was deprecated all the way back in Juno (!!). Adios. Signed-off-by: Stephen Finucane --- .../openstack/blockstorage/v1/blockstorage.go | 152 ----------------- .../openstack/blockstorage/v1/pkg.go | 2 - .../blockstorage/v1/snapshots_test.go | 62 ------- .../openstack/blockstorage/v1/volumes_test.go | 73 --------- .../blockstorage/v1/volumetypes_test.go | 51 ------ openstack/blockstorage/v1/apiversions/doc.go | 11 -- .../blockstorage/v1/apiversions/requests.go | 23 --- .../blockstorage/v1/apiversions/results.go | 53 ------ .../v1/apiversions/testing/doc.go | 2 - .../v1/apiversions/testing/fixtures_test.go | 91 ----------- .../v1/apiversions/testing/requests_test.go | 65 -------- openstack/blockstorage/v1/apiversions/urls.go | 20 --- openstack/blockstorage/v1/snapshots/doc.go | 5 - .../blockstorage/v1/snapshots/requests.go | 133 --------------- .../blockstorage/v1/snapshots/results.go | 134 --------------- .../blockstorage/v1/snapshots/testing/doc.go | 2 - .../v1/snapshots/testing/fixtures_test.go | 134 --------------- .../v1/snapshots/testing/requests_test.go | 117 ------------- openstack/blockstorage/v1/snapshots/urls.go | 27 --- openstack/blockstorage/v1/snapshots/util.go | 23 --- openstack/blockstorage/v1/volumes/doc.go | 5 - openstack/blockstorage/v1/volumes/requests.go | 142 ---------------- openstack/blockstorage/v1/volumes/results.go | 113 ------------- .../blockstorage/v1/volumes/testing/doc.go | 9 - .../v1/volumes/testing/fixtures_test.go | 127 --------------- .../v1/volumes/testing/requests_test.go | 154 ------------------ openstack/blockstorage/v1/volumes/urls.go | 23 --- openstack/blockstorage/v1/volumes/util.go | 23 --- openstack/blockstorage/v1/volumetypes/doc.go | 9 - .../blockstorage/v1/volumetypes/requests.go | 64 -------- .../blockstorage/v1/volumetypes/results.go | 65 -------- .../v1/volumetypes/testing/doc.go | 2 - .../v1/volumetypes/testing/fixtures_test.go | 60 ------- .../v1/volumetypes/testing/requests_test.go | 120 -------------- openstack/blockstorage/v1/volumetypes/urls.go | 19 --- 35 files changed, 2115 deletions(-) delete mode 100644 internal/acceptance/openstack/blockstorage/v1/blockstorage.go delete mode 100644 internal/acceptance/openstack/blockstorage/v1/pkg.go delete mode 100644 internal/acceptance/openstack/blockstorage/v1/snapshots_test.go delete mode 100644 internal/acceptance/openstack/blockstorage/v1/volumes_test.go delete mode 100644 internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go delete mode 100644 openstack/blockstorage/v1/apiversions/doc.go delete mode 100644 openstack/blockstorage/v1/apiversions/requests.go delete mode 100644 openstack/blockstorage/v1/apiversions/results.go delete mode 100644 openstack/blockstorage/v1/apiversions/testing/doc.go delete mode 100644 openstack/blockstorage/v1/apiversions/testing/fixtures_test.go delete mode 100644 openstack/blockstorage/v1/apiversions/testing/requests_test.go delete mode 100644 openstack/blockstorage/v1/apiversions/urls.go delete mode 100644 openstack/blockstorage/v1/snapshots/doc.go delete mode 100644 openstack/blockstorage/v1/snapshots/requests.go delete mode 100644 openstack/blockstorage/v1/snapshots/results.go delete mode 100644 openstack/blockstorage/v1/snapshots/testing/doc.go delete mode 100644 openstack/blockstorage/v1/snapshots/testing/fixtures_test.go delete mode 100644 openstack/blockstorage/v1/snapshots/testing/requests_test.go delete mode 100644 openstack/blockstorage/v1/snapshots/urls.go delete mode 100644 openstack/blockstorage/v1/snapshots/util.go delete mode 100644 openstack/blockstorage/v1/volumes/doc.go delete mode 100644 openstack/blockstorage/v1/volumes/requests.go delete mode 100644 openstack/blockstorage/v1/volumes/results.go delete mode 100644 openstack/blockstorage/v1/volumes/testing/doc.go delete mode 100644 openstack/blockstorage/v1/volumes/testing/fixtures_test.go delete mode 100644 openstack/blockstorage/v1/volumes/testing/requests_test.go delete mode 100644 openstack/blockstorage/v1/volumes/urls.go delete mode 100644 openstack/blockstorage/v1/volumes/util.go delete mode 100644 openstack/blockstorage/v1/volumetypes/doc.go delete mode 100644 openstack/blockstorage/v1/volumetypes/requests.go delete mode 100644 openstack/blockstorage/v1/volumetypes/results.go delete mode 100644 openstack/blockstorage/v1/volumetypes/testing/doc.go delete mode 100644 openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go delete mode 100644 openstack/blockstorage/v1/volumetypes/testing/requests_test.go delete mode 100644 openstack/blockstorage/v1/volumetypes/urls.go diff --git a/internal/acceptance/openstack/blockstorage/v1/blockstorage.go b/internal/acceptance/openstack/blockstorage/v1/blockstorage.go deleted file mode 100644 index d3347f0b96..0000000000 --- a/internal/acceptance/openstack/blockstorage/v1/blockstorage.go +++ /dev/null @@ -1,152 +0,0 @@ -// Package v1 contains common functions for creating block storage based -// resources for use in acceptance tests. See the `*_test.go` files for -// example usages. -package v1 - -import ( - "context" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/snapshots" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumes" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumetypes" -) - -// CreateSnapshot will create a volume snapshot based off of a given volume and -// with a random name. An error will be returned if the snapshot failed to be -// created. -func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) { - if testing.Short() { - t.Skip("Skipping test that requires snapshot creation in short mode.") - } - - snapshotName := tools.RandomString("ACPTTEST", 16) - t.Logf("Attempting to create snapshot %s based on volume %s", snapshotName, volume.ID) - - createOpts := snapshots.CreateOpts{ - Name: snapshotName, - VolumeID: volume.ID, - } - - snapshot, err := snapshots.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return snapshot, err - } - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - err = snapshots.WaitForStatus(ctx, client, snapshot.ID, "available") - if err != nil { - return snapshot, err - } - - return snapshot, nil -} - -// CreateVolume will create a volume with a random name and size of 1GB. An -// error will be returned if the volume was unable to be created. -func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) { - if testing.Short() { - t.Skip("Skipping test that requires volume creation in short mode.") - } - - volumeName := tools.RandomString("ACPTTEST", 16) - volumeDescription := tools.RandomString("ACPTTEST-DESC", 16) - t.Logf("Attempting to create volume: %s", volumeName) - - createOpts := volumes.CreateOpts{ - Size: 1, - Name: volumeName, - Description: volumeDescription, - } - - volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return volume, err - } - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - err = volumes.WaitForStatus(ctx, client, volume.ID, "available") - if err != nil { - return volume, err - } - - return volume, nil -} - -// CreateVolumeType will create a volume type with a random name. An error will -// be returned if the volume type was unable to be created. -func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) { - volumeTypeName := tools.RandomString("ACPTTEST", 16) - t.Logf("Attempting to create volume type: %s", volumeTypeName) - - createOpts := volumetypes.CreateOpts{ - Name: volumeTypeName, - ExtraSpecs: map[string]interface{}{ - "capabilities": "ssd", - "priority": 3, - }, - } - - volumeType, err := volumetypes.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return volumeType, err - } - - return volumeType, nil -} - -// DeleteSnapshot will delete a snapshot. A fatal error will occur if the -// snapshot failed to be deleted. This works best when used as a deferred -// function. -func DeleteSnapshotshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { - err := snapshots.Delete(context.TODO(), client, snapshot.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete snapshot %s: %v", snapshot.ID, err) - } - - // Volumes can't be deleted until their snapshots have been, - // so block until the snapshot has been deleted. - err = tools.WaitFor(func(ctx context.Context) (bool, error) { - _, err := snapshots.Get(ctx, client, snapshot.ID).Extract() - if err != nil { - return true, nil - } - - return false, nil - }) - if err != nil { - t.Fatalf("Unable to wait for snapshot to delete: %v", err) - } - - t.Logf("Deleted snapshot: %s", snapshot.ID) -} - -// DeleteVolume will delete a volume. A fatal error will occur if the volume -// failed to be deleted. This works best when used as a deferred function. -func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { - err := volumes.Delete(context.TODO(), client, volume.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete volume %s: %v", volume.ID, err) - } - - t.Logf("Deleted volume: %s", volume.ID) -} - -// DeleteVolumeType will delete a volume type. A fatal error will occur if the -// volume type failed to be deleted. This works best when used as a deferred -// function. -func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, volumeType *volumetypes.VolumeType) { - err := volumetypes.Delete(context.TODO(), client, volumeType.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete volume type %s: %v", volumeType.ID, err) - } - - t.Logf("Deleted volume type: %s", volumeType.ID) -} diff --git a/internal/acceptance/openstack/blockstorage/v1/pkg.go b/internal/acceptance/openstack/blockstorage/v1/pkg.go deleted file mode 100644 index 4efa6fbf1e..0000000000 --- a/internal/acceptance/openstack/blockstorage/v1/pkg.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package v1 contains openstack cinder acceptance tests -package v1 diff --git a/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go deleted file mode 100644 index 06981d9869..0000000000 --- a/internal/acceptance/openstack/blockstorage/v1/snapshots_test.go +++ /dev/null @@ -1,62 +0,0 @@ -//go:build acceptance || blockstorage -// +build acceptance blockstorage - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/snapshots" -) - -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) - } - - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to retrieve snapshots: %v", err) - } - - allSnapshots, err := snapshots.ExtractSnapshots(allPages) - if err != nil { - t.Fatalf("Unable to extract snapshots: %v", err) - } - - for _, snapshot := range allSnapshots { - tools.PrintResource(t, snapshot) - } -} - -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) - } - - volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - defer DeleteVolume(t, client, volume) - - snapshot, err := CreateSnapshot(t, client, volume) - if err != nil { - t.Fatalf("Unable to create snapshot: %v", err) - } - defer DeleteSnapshotshot(t, client, snapshot) - - newSnapshot, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve snapshot: %v", err) - } - - tools.PrintResource(t, newSnapshot) -} diff --git a/internal/acceptance/openstack/blockstorage/v1/volumes_test.go b/internal/acceptance/openstack/blockstorage/v1/volumes_test.go deleted file mode 100644 index ad222542e1..0000000000 --- a/internal/acceptance/openstack/blockstorage/v1/volumes_test.go +++ /dev/null @@ -1,73 +0,0 @@ -//go:build acceptance || blockstorage -// +build acceptance blockstorage - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumes" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -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) - } - - allPages, err := volumes.List(client, volumes.ListOpts{}).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to retrieve volumes: %v", err) - } - - allVolumes, err := volumes.ExtractVolumes(allPages) - if err != nil { - t.Fatalf("Unable to extract volumes: %v", err) - } - - for _, volume := range allVolumes { - tools.PrintResource(t, volume) - } -} - -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) - } - - volume, err := CreateVolume(t, client) - if err != nil { - t.Fatalf("Unable to create volume: %v", err) - } - defer DeleteVolume(t, client, volume) - - newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve volume: %v", err) - } - - tools.PrintResource(t, newVolume) - th.AssertEquals(t, volume.Name, newVolume.Name) - th.AssertEquals(t, volume.Description, newVolume.Description) - - // Update volume - updatedVolumeName := "" - updatedVolumeDescription := "" - updateOpts := volumes.UpdateOpts{ - Name: &updatedVolumeName, - Description: &updatedVolumeDescription, - } - updatedVolume, err := volumes.Update(context.TODO(), client, volume.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, updatedVolume) - th.AssertEquals(t, updatedVolume.Name, updatedVolumeName) - th.AssertEquals(t, updatedVolume.Description, updatedVolumeDescription) -} diff --git a/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go deleted file mode 100644 index e1edcf79bc..0000000000 --- a/internal/acceptance/openstack/blockstorage/v1/volumetypes_test.go +++ /dev/null @@ -1,51 +0,0 @@ -//go:build acceptance || blockstorage -// +build acceptance blockstorage - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumetypes" -) - -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) - } - - allPages, err := volumetypes.List(client).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to retrieve volume types: %v", err) - } - - allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) - if err != nil { - t.Fatalf("Unable to extract volume types: %v", err) - } - - for _, volumeType := range allVolumeTypes { - tools.PrintResource(t, volumeType) - } -} - -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) - } - - volumeType, err := CreateVolumeType(t, client) - if err != nil { - t.Fatalf("Unable to create volume type: %v", err) - } - defer DeleteVolumeType(t, client, volumeType) - - tools.PrintResource(t, volumeType) -} diff --git a/openstack/blockstorage/v1/apiversions/doc.go b/openstack/blockstorage/v1/apiversions/doc.go deleted file mode 100644 index f9355ab622..0000000000 --- a/openstack/blockstorage/v1/apiversions/doc.go +++ /dev/null @@ -1,11 +0,0 @@ -/* -Package apiversions provides information and interaction with the different -API versions for the OpenStack Block Storage service, code-named Cinder. - -This package is deprecated and should only be used in environments where the -older API version format is expected. - -Consider using the gophercloud/openstack/blockstorage/apiversions package -instead. -*/ -package apiversions diff --git a/openstack/blockstorage/v1/apiversions/requests.go b/openstack/blockstorage/v1/apiversions/requests.go deleted file mode 100644 index c376b3307d..0000000000 --- a/openstack/blockstorage/v1/apiversions/requests.go +++ /dev/null @@ -1,23 +0,0 @@ -package apiversions - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// List lists all the Cinder API versions available to end-users. -func List(c *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page { - return APIVersionPage{pagination.SinglePageBase(r)} - }) -} - -// Get will retrieve the volume type with the provided ID. To extract the volume -// type from the result, call the Extract method on the GetResult. -func Get(ctx context.Context, client *gophercloud.ServiceClient, v string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, v), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/blockstorage/v1/apiversions/results.go b/openstack/blockstorage/v1/apiversions/results.go deleted file mode 100644 index 359231af13..0000000000 --- a/openstack/blockstorage/v1/apiversions/results.go +++ /dev/null @@ -1,53 +0,0 @@ -package apiversions - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// APIVersion represents an API version for Cinder. -type APIVersion struct { - ID string `json:"id"` // unique identifier - Status string `json:"status"` // current status - Updated string `json:"updated"` // date last updated -} - -// APIVersionPage is the page returned by a pager when traversing over a -// collection of API versions. -type APIVersionPage struct { - pagination.SinglePageBase -} - -// 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 -} - -// ExtractAPIVersions takes a collection page, extracts all of the elements, -// and returns them a slice of APIVersion structs. It is effectively a cast. -func ExtractAPIVersions(r pagination.Page) ([]APIVersion, error) { - var s struct { - Versions []APIVersion `json:"versions"` - } - err := (r.(APIVersionPage)).ExtractInto(&s) - return s.Versions, err -} - -// GetResult represents the result of a get operation. -type GetResult struct { - gophercloud.Result -} - -// Extract is a function that accepts a result and extracts an API version resource. -func (r GetResult) Extract() (*APIVersion, error) { - var s struct { - Version *APIVersion `json:"version"` - } - err := r.ExtractInto(&s) - return s.Version, err -} diff --git a/openstack/blockstorage/v1/apiversions/testing/doc.go b/openstack/blockstorage/v1/apiversions/testing/doc.go deleted file mode 100644 index 12e4bda0f9..0000000000 --- a/openstack/blockstorage/v1/apiversions/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// apiversions_v1 -package testing diff --git a/openstack/blockstorage/v1/apiversions/testing/fixtures_test.go b/openstack/blockstorage/v1/apiversions/testing/fixtures_test.go deleted file mode 100644 index 02959c48f7..0000000000 --- a/openstack/blockstorage/v1/apiversions/testing/fixtures_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func MockListResponse(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", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, `{ - "versions": [ - { - "status": "CURRENT", - "updated": "2012-01-04T11:33:21Z", - "id": "v1.0", - "links": [ - { - "href": "http://23.253.228.211:8776/v1/", - "rel": "self" - } - ] - }, - { - "status": "CURRENT", - "updated": "2012-11-21T11:33:21Z", - "id": "v2.0", - "links": [ - { - "href": "http://23.253.228.211:8776/v2/", - "rel": "self" - } - ] - } - ] - }`) - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/v1/", 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, `{ - "version": { - "status": "CURRENT", - "updated": "2012-01-04T11:33:21Z", - "media-types": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.volume+xml;version=1" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.volume+json;version=1" - } - ], - "id": "v1.0", - "links": [ - { - "href": "http://23.253.228.211:8776/v1/", - "rel": "self" - }, - { - "href": "http://jorgew.github.com/block-storage-api/content/os-block-storage-1.0.pdf", - "type": "application/pdf", - "rel": "describedby" - }, - { - "href": "http://docs.rackspacecloud.com/servers/api/v1.1/application.wadl", - "type": "application/vnd.sun.wadl+xml", - "rel": "describedby" - } - ] - } - }`) - }) -} diff --git a/openstack/blockstorage/v1/apiversions/testing/requests_test.go b/openstack/blockstorage/v1/apiversions/testing/requests_test.go deleted file mode 100644 index 5af2ce0f4f..0000000000 --- a/openstack/blockstorage/v1/apiversions/testing/requests_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/apiversions" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestListVersions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - apiversions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := apiversions.ExtractAPIVersions(page) - th.AssertNoErr(t, err) - - expected := []apiversions.APIVersion{ - { - ID: "v1.0", - Status: "CURRENT", - Updated: "2012-01-04T11:33:21Z", - }, - { - ID: "v2.0", - Status: "CURRENT", - Updated: "2012-11-21T11:33:21Z", - }, - } - - th.AssertDeepEquals(t, expected, actual) - - return true, nil - }) - - th.AssertEquals(t, 1, count) -} - -func TestAPIInfo(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - actual, err := apiversions.Get(context.TODO(), client.ServiceClient(), "v1").Extract() - th.AssertNoErr(t, err) - - expected := apiversions.APIVersion{ - ID: "v1.0", - Status: "CURRENT", - Updated: "2012-01-04T11:33:21Z", - } - - th.AssertEquals(t, actual.ID, expected.ID) - th.AssertEquals(t, actual.Status, expected.Status) - th.AssertEquals(t, actual.Updated, expected.Updated) -} diff --git a/openstack/blockstorage/v1/apiversions/urls.go b/openstack/blockstorage/v1/apiversions/urls.go deleted file mode 100644 index 47f8116620..0000000000 --- a/openstack/blockstorage/v1/apiversions/urls.go +++ /dev/null @@ -1,20 +0,0 @@ -package apiversions - -import ( - "strings" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/utils" -) - -func getURL(c *gophercloud.ServiceClient, version string) string { - baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) - endpoint := strings.TrimRight(baseEndpoint, "/") + "/" + strings.TrimRight(version, "/") + "/" - return endpoint -} - -func listURL(c *gophercloud.ServiceClient) string { - baseEndpoint, _ := utils.BaseEndpoint(c.Endpoint) - endpoint := strings.TrimRight(baseEndpoint, "/") + "/" - return endpoint -} diff --git a/openstack/blockstorage/v1/snapshots/doc.go b/openstack/blockstorage/v1/snapshots/doc.go deleted file mode 100644 index 198f83077c..0000000000 --- a/openstack/blockstorage/v1/snapshots/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package snapshots provides information and interaction with snapshots in the -// OpenStack Block Storage service. A snapshot is a point in time copy of the -// data contained in an external storage volume, and can be controlled -// programmatically. -package snapshots diff --git a/openstack/blockstorage/v1/snapshots/requests.go b/openstack/blockstorage/v1/snapshots/requests.go deleted file mode 100644 index 73f16310a2..0000000000 --- a/openstack/blockstorage/v1/snapshots/requests.go +++ /dev/null @@ -1,133 +0,0 @@ -package snapshots - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToSnapshotCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Snapshot. This object is passed to -// the snapshots.Create function. For more information about these parameters, -// see the Snapshot object. -type CreateOpts struct { - VolumeID string `json:"volume_id" required:"true"` - Description string `json:"display_description,omitempty"` - Force bool `json:"force,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty"` - Name string `json:"display_name,omitempty"` -} - -// ToSnapshotCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "snapshot") -} - -// Create will create a new Snapshot based on the values in CreateOpts. To -// extract the Snapshot object from the response, call the Extract method on the -// CreateResult. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToSnapshotCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will delete the existing Snapshot with the provided ID. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves the Snapshot with the provided ID. To extract the Snapshot -// object from the response, call the Extract method on the GetResult. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToSnapshotListQuery() (string, error) -} - -// ListOpts hold options for listing Snapshots. It is passed to the -// snapshots.List function. -type ListOpts struct { - Name string `q:"display_name"` - Status string `q:"status"` - VolumeID string `q:"volume_id"` -} - -// ToSnapshotListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToSnapshotListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns Snapshots optionally limited by the conditions provided in -// ListOpts. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToSnapshotListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return SnapshotPage{pagination.SinglePageBase(r)} - }) -} - -// UpdateMetadataOptsBuilder allows extensions to add additional parameters to -// the Update request. -type UpdateMetadataOptsBuilder interface { - ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) -} - -// UpdateMetadataOpts contain options for updating an existing Snapshot. This -// object is passed to the snapshots.Update function. For more information -// about the parameters, see the Snapshot object. -type UpdateMetadataOpts struct { - Metadata map[string]interface{} `json:"metadata,omitempty"` -} - -// ToSnapshotUpdateMetadataMap assembles a request body based on the contents of -// an UpdateMetadataOpts. -func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "") -} - -// UpdateMetadata will update the Snapshot with provided information. To -// extract the updated Snapshot from the response, call the ExtractMetadata -// method on the UpdateMetadataResult. -func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { - b, err := opts.ToSnapshotUpdateMetadataMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(ctx, updateMetadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/blockstorage/v1/snapshots/results.go b/openstack/blockstorage/v1/snapshots/results.go deleted file mode 100644 index dede83eef4..0000000000 --- a/openstack/blockstorage/v1/snapshots/results.go +++ /dev/null @@ -1,134 +0,0 @@ -package snapshots - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Snapshot contains all the information associated with an OpenStack Snapshot. -type Snapshot struct { - // Currect status of the Snapshot. - Status string `json:"status"` - - // Display name. - Name string `json:"display_name"` - - // Instances onto which the Snapshot is attached. - Attachments []string `json:"attachments"` - - // Logical group. - AvailabilityZone string `json:"availability_zone"` - - // Is the Snapshot bootable? - Bootable string `json:"bootable"` - - // Date created. - CreatedAt time.Time `json:"-"` - - // Display description. - Description string `json:"display_description"` - - // See VolumeType object for more information. - VolumeType string `json:"volume_type"` - - // ID of the Snapshot from which this Snapshot was created. - SnapshotID string `json:"snapshot_id"` - - // ID of the Volume from which this Snapshot was created. - VolumeID string `json:"volume_id"` - - // User-defined key-value pairs. - Metadata map[string]string `json:"metadata"` - - // Unique identifier. - ID string `json:"id"` - - // Size of the Snapshot, in GB. - Size int `json:"size"` -} - -func (r *Snapshot) UnmarshalJSON(b []byte) error { - type tmp Snapshot - var s struct { - tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Snapshot(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - - return err -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - gophercloud.ErrResult -} - -// SnapshotPage is a pagination.Pager that is returned from a call to the List function. -type SnapshotPage struct { - pagination.SinglePageBase -} - -// 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 -} - -// ExtractSnapshots extracts and returns Snapshots. It is used while iterating over a snapshots.List call. -func ExtractSnapshots(r pagination.Page) ([]Snapshot, error) { - var s struct { - Snapshots []Snapshot `json:"snapshots"` - } - err := (r.(SnapshotPage)).ExtractInto(&s) - return s.Snapshots, err -} - -// UpdateMetadataResult contains the response body and error from an UpdateMetadata request. -type UpdateMetadataResult struct { - commonResult -} - -// ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. -func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { - if r.Err != nil { - return nil, r.Err - } - m := r.Body.(map[string]interface{})["metadata"] - return m.(map[string]interface{}), nil -} - -type commonResult struct { - gophercloud.Result -} - -// Extract will get the Snapshot object out of the commonResult object. -func (r commonResult) Extract() (*Snapshot, error) { - var s struct { - Snapshot *Snapshot `json:"snapshot"` - } - err := r.ExtractInto(&s) - return s.Snapshot, err -} diff --git a/openstack/blockstorage/v1/snapshots/testing/doc.go b/openstack/blockstorage/v1/snapshots/testing/doc.go deleted file mode 100644 index 85c45f4078..0000000000 --- a/openstack/blockstorage/v1/snapshots/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// snapshots_v1 -package testing diff --git a/openstack/blockstorage/v1/snapshots/testing/fixtures_test.go b/openstack/blockstorage/v1/snapshots/testing/fixtures_test.go deleted file mode 100644 index f148d1e5c6..0000000000 --- a/openstack/blockstorage/v1/snapshots/testing/fixtures_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots", 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, ` - { - "snapshots": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "display_name": "snapshot-001", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "display_description": "Daily Backup", - "status": "available", - "size": 30, - "created_at": "2012-02-14T20:53:07" - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "display_name": "snapshot-002", - "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", - "display_description": "Weekly Backup", - "status": "available", - "size": 25, - "created_at": "2012-02-14T20:53:08" - } - ] - } - `) - }) -} - -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") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` -{ - "snapshot": { - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "display_name": "snapshot-001", - "display_description": "Daily backup", - "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "status": "available", - "size": 30, - "created_at": "2012-02-29T03:50:07" - } -} - `) - }) -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots", 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, ` -{ - "snapshot": { - "volume_id": "1234", - "display_name": "snapshot-001" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "snapshot": { - "volume_id": "1234", - "display_name": "snapshot-001", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "display_description": "Daily backup", - "volume_id": "1234", - "status": "available", - "size": 30, - "created_at": "2012-02-29T03:50:07" - } -} - `) - }) -} - -func MockUpdateMetadataResponse(t *testing.T) { - th.Mux.HandleFunc("/snapshots/123/metadata", 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.TestJSONRequest(t, r, ` - { - "metadata": { - "key": "v1" - } - } - `) - - fmt.Fprintf(w, ` - { - "metadata": { - "key": "v1" - } - } - `) - }) -} - -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") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusNoContent) - }) -} diff --git a/openstack/blockstorage/v1/snapshots/testing/requests_test.go b/openstack/blockstorage/v1/snapshots/testing/requests_test.go deleted file mode 100644 index 3f24e41238..0000000000 --- a/openstack/blockstorage/v1/snapshots/testing/requests_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package testing - -import ( - "context" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/snapshots" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := snapshots.ExtractSnapshots(page) - if err != nil { - t.Errorf("Failed to extract snapshots: %v", err) - return false, err - } - - expected := []snapshots.Snapshot{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "snapshot-001", - VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - Status: "available", - Size: 30, - CreatedAt: time.Date(2012, 2, 14, 20, 53, 7, 0, time.UTC), - Description: "Daily Backup", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "snapshot-002", - VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", - Status: "available", - Size: 25, - CreatedAt: time.Date(2012, 2, 14, 20, 53, 8, 0, time.UTC), - Description: "Weekly Backup", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - v, err := snapshots.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, v.Name, "snapshot-001") - th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := snapshots.CreateOpts{VolumeID: "1234", Name: "snapshot-001"} - n, err := snapshots.Create(context.TODO(), client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.VolumeID, "1234") - th.AssertEquals(t, n.Name, "snapshot-001") - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestUpdateMetadata(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateMetadataResponse(t) - - expected := map[string]interface{}{"key": "v1"} - - options := &snapshots.UpdateMetadataOpts{ - Metadata: map[string]interface{}{ - "key": "v1", - }, - } - - actual, err := snapshots.UpdateMetadata(context.TODO(), client.ServiceClient(), "123", options).ExtractMetadata() - - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, actual, expected) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := snapshots.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/blockstorage/v1/snapshots/urls.go b/openstack/blockstorage/v1/snapshots/urls.go deleted file mode 100644 index 7cecf6de63..0000000000 --- a/openstack/blockstorage/v1/snapshots/urls.go +++ /dev/null @@ -1,27 +0,0 @@ -package snapshots - -import "github.com/gophercloud/gophercloud/v2" - -func createURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("snapshots") -} - -func deleteURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id) -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func listURL(c *gophercloud.ServiceClient) string { - return createURL(c) -} - -func metadataURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("snapshots", id, "metadata") -} - -func updateMetadataURL(c *gophercloud.ServiceClient, id string) string { - return metadataURL(c, id) -} diff --git a/openstack/blockstorage/v1/snapshots/util.go b/openstack/blockstorage/v1/snapshots/util.go deleted file mode 100644 index df10c97c2e..0000000000 --- a/openstack/blockstorage/v1/snapshots/util.go +++ /dev/null @@ -1,23 +0,0 @@ -package snapshots - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" -) - -// WaitForStatus will continually poll the resource, checking for a particular status. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { - return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { - current, err := Get(ctx, c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} diff --git a/openstack/blockstorage/v1/volumes/doc.go b/openstack/blockstorage/v1/volumes/doc.go deleted file mode 100644 index 307b8b12d2..0000000000 --- a/openstack/blockstorage/v1/volumes/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package volumes provides information and interaction with volumes in the -// OpenStack Block Storage service. A volume is a detachable block storage -// device, akin to a USB hard drive. It can only be attached to one instance at -// a time. -package volumes diff --git a/openstack/blockstorage/v1/volumes/requests.go b/openstack/blockstorage/v1/volumes/requests.go deleted file mode 100644 index 8ac1b36745..0000000000 --- a/openstack/blockstorage/v1/volumes/requests.go +++ /dev/null @@ -1,142 +0,0 @@ -package volumes - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToVolumeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts contains options for creating a Volume. This object is passed to -// the volumes.Create function. For more information about these parameters, -// see the Volume object. -type CreateOpts struct { - Size int `json:"size" required:"true"` - AvailabilityZone string `json:"availability_zone,omitempty"` - Description string `json:"display_description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` - Name string `json:"display_name,omitempty"` - SnapshotID string `json:"snapshot_id,omitempty"` - SourceVolID string `json:"source_volid,omitempty"` - ImageID string `json:"imageRef,omitempty"` - VolumeType string `json:"volume_type,omitempty"` -} - -// ToVolumeCreateMap assembles a request body based on the contents of a -// CreateOpts. -func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume") -} - -// Create will create a new Volume based on the values in CreateOpts. To extract -// the Volume object from the response, call the Extract method on the -// CreateResult. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToVolumeCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will delete the existing Volume with the provided ID. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves the Volume with the provided ID. To extract the Volume object -// from the response, call the Extract method on the GetResult. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the List -// request. -type ListOptsBuilder interface { - ToVolumeListQuery() (string, error) -} - -// ListOpts holds options for listing Volumes. It is passed to the volumes.List -// function. -type ListOpts struct { - // admin-only option. Set it to true to see all tenant volumes. - AllTenants bool `q:"all_tenants"` - // List only volumes that contain Metadata. - Metadata map[string]string `q:"metadata"` - // List only volumes that have Name as the display name. - Name string `q:"display_name"` - // List only volumes that have a status of Status. - Status string `q:"status"` -} - -// ToVolumeListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToVolumeListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List returns Volumes optionally limited by the conditions provided in ListOpts. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToVolumeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return VolumePage{pagination.SinglePageBase(r)} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToVolumeUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts contain options for updating an existing Volume. This object is passed -// to the volumes.Update function. For more information about the parameters, see -// the Volume object. -type UpdateOpts struct { - Name *string `json:"display_name,omitempty"` - Description *string `json:"display_description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ToVolumeUpdateMap assembles a request body based on the contents of an -// UpdateOpts. -func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume") -} - -// Update will update the Volume with provided information. To extract the updated -// Volume from the response, call the Extract method on the UpdateResult. -func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToVolumeUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/blockstorage/v1/volumes/results.go b/openstack/blockstorage/v1/volumes/results.go deleted file mode 100644 index 13238eefd9..0000000000 --- a/openstack/blockstorage/v1/volumes/results.go +++ /dev/null @@ -1,113 +0,0 @@ -package volumes - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Volume contains all the information associated with an OpenStack Volume. -type Volume struct { - // Current status of the volume. - Status string `json:"status"` - // Human-readable display name for the volume. - Name string `json:"display_name"` - // Instances onto which the volume is attached. - Attachments []map[string]interface{} `json:"attachments"` - // This parameter is no longer used. - AvailabilityZone string `json:"availability_zone"` - // Indicates whether this is a bootable volume. - Bootable string `json:"bootable"` - // The date when this volume was created. - CreatedAt time.Time `json:"-"` - // Human-readable description for the volume. - Description string `json:"display_description"` - // The type of volume to create, either SATA or SSD. - VolumeType string `json:"volume_type"` - // The ID of the snapshot from which the volume was created - SnapshotID string `json:"snapshot_id"` - // The ID of another block storage volume from which the current volume was created - SourceVolID string `json:"source_volid"` - // Arbitrary key-value pairs defined by the user. - Metadata map[string]string `json:"metadata"` - // Unique identifier for the volume. - ID string `json:"id"` - // Size of the volume in GB. - Size int `json:"size"` -} - -func (r *Volume) UnmarshalJSON(b []byte) error { - type tmp Volume - var s struct { - tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Volume(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - - return err -} - -// CreateResult contains the response body and error from a Create request. -type CreateResult struct { - commonResult -} - -// GetResult contains the response body and error from a Get request. -type GetResult struct { - commonResult -} - -// DeleteResult contains the response body and error from a Delete request. -type DeleteResult struct { - gophercloud.ErrResult -} - -// VolumePage is a pagination.pager that is returned from a call to the List function. -type VolumePage struct { - pagination.SinglePageBase -} - -// IsEmpty returns true if a VolumePage contains no Volumes. -func (r VolumePage) IsEmpty() (bool, error) { - if r.StatusCode == 204 { - return true, nil - } - - volumes, err := ExtractVolumes(r) - return len(volumes) == 0, err -} - -// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. -func ExtractVolumes(r pagination.Page) ([]Volume, error) { - var s struct { - Volumes []Volume `json:"volumes"` - } - err := (r.(VolumePage)).ExtractInto(&s) - return s.Volumes, err -} - -// UpdateResult contains the response body and error from an Update request. -type UpdateResult struct { - commonResult -} - -type commonResult struct { - gophercloud.Result -} - -// Extract will get the Volume object out of the commonResult object. -func (r commonResult) Extract() (*Volume, error) { - var s struct { - Volume *Volume `json:"volume"` - } - err := r.ExtractInto(&s) - return s.Volume, err -} diff --git a/openstack/blockstorage/v1/volumes/testing/doc.go b/openstack/blockstorage/v1/volumes/testing/doc.go deleted file mode 100644 index 088e43c57f..0000000000 --- a/openstack/blockstorage/v1/volumes/testing/doc.go +++ /dev/null @@ -1,9 +0,0 @@ -// volumes_v1 -package testing - -/* -This is package created is to hold fixtures (which imports testing), -so that importing volumes package does not inadvertently import testing into production code -More information here: -https://github.com/rackspace/gophercloud/issues/473 -*/ diff --git a/openstack/blockstorage/v1/volumes/testing/fixtures_test.go b/openstack/blockstorage/v1/volumes/testing/fixtures_test.go deleted file mode 100644 index c149f4a7b3..0000000000 --- a/openstack/blockstorage/v1/volumes/testing/fixtures_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes", 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, ` - { - "volumes": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "display_name": "vol-001" - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "display_name": "vol-002" - } - ] - } - `) - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/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, ` - { - "volume": { - "id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - "display_name": "vol-001", - "display_description": "Another volume.", - "status": "active", - "size": 30, - "volume_type": "289da7f8-6440-407c-9fb4-7db01ec49164", - "metadata": { - "contents": "junk" - }, - "availability_zone": "us-east1", - "bootable": "false", - "snapshot_id": null, - "attachments": [ - { - "attachment_id": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa", - "id": "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf", - "volume_id": "6c80f8ac-e3e2-480c-8e6e-f1db92fe4bfe", - "server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f", - "host_name": "mitaka", - "device": "/" - } - ], - "created_at": "2012-02-14T20:53:07" - } - } - `) - }) -} - -func MockCreateResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes", 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, ` -{ - "volume": { - "size": 75, - "availability_zone": "us-east1" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "volume": { - "size": 4, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" - } -} - `) - }) -} - -func MockDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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 MockUpdateResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/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) - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` - { - "volume": { - "display_name": "vol-002", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" - } - } - `) - }) -} diff --git a/openstack/blockstorage/v1/volumes/testing/requests_test.go b/openstack/blockstorage/v1/volumes/testing/requests_test.go deleted file mode 100644 index 323949dfc2..0000000000 --- a/openstack/blockstorage/v1/volumes/testing/requests_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package testing - -import ( - "context" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumes" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := volumes.ExtractVolumes(page) - if err != nil { - t.Errorf("Failed to extract volumes: %v", err) - return false, err - } - - expected := []volumes.Volume{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "vol-001", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "vol-002", - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestListAll(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err := volumes.ExtractVolumes(allPages) - th.AssertNoErr(t, err) - - expected := []volumes.Volume{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "vol-001", - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "vol-002", - }, - } - - th.CheckDeepEquals(t, expected, actual) - -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - actual, err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - expected := &volumes.Volume{ - Status: "active", - Name: "vol-001", - Attachments: []map[string]interface{}{ - { - "attachment_id": "03987cd1-0ad5-40d1-9b2a-7cc48295d4fa", - "id": "47e9ecc5-4045-4ee3-9a4b-d859d546a0cf", - "volume_id": "6c80f8ac-e3e2-480c-8e6e-f1db92fe4bfe", - "server_id": "d1c4788b-9435-42e2-9b81-29f3be1cd01f", - "host_name": "mitaka", - "device": "/", - }, - }, - AvailabilityZone: "us-east1", - Bootable: "false", - CreatedAt: time.Date(2012, 2, 14, 20, 53, 07, 0, time.UTC), - Description: "Another volume.", - VolumeType: "289da7f8-6440-407c-9fb4-7db01ec49164", - SnapshotID: "", - SourceVolID: "", - Metadata: map[string]string{ - "contents": "junk", - }, - ID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", - Size: 30, - } - - th.AssertDeepEquals(t, expected, actual) -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockCreateResponse(t) - - options := &volumes.CreateOpts{ - Size: 75, - AvailabilityZone: "us-east1", - } - n, err := volumes.Create(context.TODO(), client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.Size, 4) - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDeleteResponse(t) - - res := volumes.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestUpdate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUpdateResponse(t) - - var name = "vol-002" - options := volumes.UpdateOpts{Name: &name} - v, err := volumes.Update(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() - th.AssertNoErr(t, err) - th.CheckEquals(t, "vol-002", v.Name) -} diff --git a/openstack/blockstorage/v1/volumes/urls.go b/openstack/blockstorage/v1/volumes/urls.go deleted file mode 100644 index 41f0cf75a1..0000000000 --- a/openstack/blockstorage/v1/volumes/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package volumes - -import "github.com/gophercloud/gophercloud/v2" - -func createURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("volumes") -} - -func listURL(c *gophercloud.ServiceClient) string { - return createURL(c) -} - -func deleteURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("volumes", id) -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return deleteURL(c, id) -} - -func updateURL(c *gophercloud.ServiceClient, id string) string { - return deleteURL(c, id) -} diff --git a/openstack/blockstorage/v1/volumes/util.go b/openstack/blockstorage/v1/volumes/util.go deleted file mode 100644 index 6f8d899f56..0000000000 --- a/openstack/blockstorage/v1/volumes/util.go +++ /dev/null @@ -1,23 +0,0 @@ -package volumes - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" -) - -// WaitForStatus will continually poll the resource, checking for a particular status. -func WaitForStatus(ctx context.Context, c *gophercloud.ServiceClient, id, status string) error { - return gophercloud.WaitFor(ctx, func(ctx context.Context) (bool, error) { - current, err := Get(ctx, c, id).Extract() - if err != nil { - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} diff --git a/openstack/blockstorage/v1/volumetypes/doc.go b/openstack/blockstorage/v1/volumetypes/doc.go deleted file mode 100644 index 793084f89b..0000000000 --- a/openstack/blockstorage/v1/volumetypes/doc.go +++ /dev/null @@ -1,9 +0,0 @@ -// Package volumetypes provides information and interaction with volume types -// in the OpenStack Block Storage service. A volume type indicates the type of -// a block storage volume, such as SATA, SCSCI, SSD, etc. These can be -// customized or defined by the OpenStack admin. -// -// You can also define extra_specs associated with your volume types. For -// instance, you could have a VolumeType=SATA, with extra_specs (RPM=10000, -// RAID-Level=5) . Extra_specs are defined and customized by the admin. -package volumetypes diff --git a/openstack/blockstorage/v1/volumetypes/requests.go b/openstack/blockstorage/v1/volumetypes/requests.go deleted file mode 100644 index 292c36103c..0000000000 --- a/openstack/blockstorage/v1/volumetypes/requests.go +++ /dev/null @@ -1,64 +0,0 @@ -package volumetypes - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToVolumeTypeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts are options for creating a volume type. -type CreateOpts struct { - // See VolumeType. - ExtraSpecs map[string]interface{} `json:"extra_specs,omitempty"` - // See VolumeType. - Name string `json:"name,omitempty"` -} - -// ToVolumeTypeCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume_type") -} - -// Create will create a new volume. To extract the created volume type object, -// call the Extract method on the CreateResult. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToVolumeTypeCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will delete the volume type with the provided ID. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get will retrieve the volume type with the provided ID. To extract the volume -// type from the result, call the Extract method on the GetResult. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// List returns all volume types. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { - return VolumeTypePage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/blockstorage/v1/volumetypes/results.go b/openstack/blockstorage/v1/volumetypes/results.go deleted file mode 100644 index 301f95ca2c..0000000000 --- a/openstack/blockstorage/v1/volumetypes/results.go +++ /dev/null @@ -1,65 +0,0 @@ -package volumetypes - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// VolumeType contains all information associated with an OpenStack Volume Type. -type VolumeType struct { - ExtraSpecs map[string]interface{} `json:"extra_specs"` // user-defined metadata - ID string `json:"id"` // unique identifier - Name string `json:"name"` // display name -} - -// 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 error from a Delete request. -type DeleteResult struct { - gophercloud.ErrResult -} - -// VolumeTypePage is a pagination.Pager that is returned from a call to the List function. -type VolumeTypePage struct { - pagination.SinglePageBase -} - -// 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 -} - -// ExtractVolumeTypes extracts and returns Volume Types. -func ExtractVolumeTypes(r pagination.Page) ([]VolumeType, error) { - var s struct { - VolumeTypes []VolumeType `json:"volume_types"` - } - err := (r.(VolumeTypePage)).ExtractInto(&s) - return s.VolumeTypes, err -} - -type commonResult struct { - gophercloud.Result -} - -// Extract will get the Volume Type object out of the commonResult object. -func (r commonResult) Extract() (*VolumeType, error) { - var s struct { - VolumeType *VolumeType `json:"volume_type"` - } - err := r.ExtractInto(&s) - return s.VolumeType, err -} diff --git a/openstack/blockstorage/v1/volumetypes/testing/doc.go b/openstack/blockstorage/v1/volumetypes/testing/doc.go deleted file mode 100644 index 73834ed731..0000000000 --- a/openstack/blockstorage/v1/volumetypes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// volumetypes_v1 -package testing diff --git a/openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go b/openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go deleted file mode 100644 index 93490ed24f..0000000000 --- a/openstack/blockstorage/v1/volumetypes/testing/fixtures_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func MockListResponse(t *testing.T) { - th.Mux.HandleFunc("/types", 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_types": [ - { - "id": "289da7f8-6440-407c-9fb4-7db01ec49164", - "name": "vol-type-001", - "extra_specs": { - "capabilities": "gpu" - } - }, - { - "id": "96c3bda7-c82a-4f50-be73-ca7621794835", - "name": "vol-type-002", - "extra_specs": {} - } - ] - } - `) - }) -} - -func MockGetResponse(t *testing.T) { - th.Mux.HandleFunc("/types/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, ` -{ - "volume_type": { - "name": "vol-type-001", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "extra_specs": { - "serverNumber": "2" - } - } -} - `) - }) -} diff --git a/openstack/blockstorage/v1/volumetypes/testing/requests_test.go b/openstack/blockstorage/v1/volumetypes/testing/requests_test.go deleted file mode 100644 index 004bea24f3..0000000000 --- a/openstack/blockstorage/v1/volumetypes/testing/requests_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v1/volumetypes" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockListResponse(t) - - count := 0 - - volumetypes.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := volumetypes.ExtractVolumeTypes(page) - if err != nil { - t.Errorf("Failed to extract volume types: %v", err) - return false, err - } - - expected := []volumetypes.VolumeType{ - { - ID: "289da7f8-6440-407c-9fb4-7db01ec49164", - Name: "vol-type-001", - ExtraSpecs: map[string]interface{}{ - "capabilities": "gpu", - }, - }, - { - ID: "96c3bda7-c82a-4f50-be73-ca7621794835", - Name: "vol-type-002", - ExtraSpecs: map[string]interface{}{}, - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockGetResponse(t) - - vt, err := volumetypes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, vt.ExtraSpecs, map[string]interface{}{"serverNumber": "2"}) - th.AssertEquals(t, vt.Name, "vol-type-001") - th.AssertEquals(t, vt.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "Content-Type", "application/json") - th.TestHeader(t, r, "Accept", "application/json") - th.TestJSONRequest(t, r, ` -{ - "volume_type": { - "name": "vol-type-001" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, ` -{ - "volume_type": { - "name": "vol-type-001", - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" - } -} - `) - }) - - options := &volumetypes.CreateOpts{Name: "vol-type-001"} - n, err := volumetypes.Create(context.TODO(), client.ServiceClient(), options).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, n.Name, "vol-type-001") - th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/types/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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) - }) - - err := volumetypes.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/blockstorage/v1/volumetypes/urls.go b/openstack/blockstorage/v1/volumetypes/urls.go deleted file mode 100644 index 99f9ec0dc2..0000000000 --- a/openstack/blockstorage/v1/volumetypes/urls.go +++ /dev/null @@ -1,19 +0,0 @@ -package volumetypes - -import "github.com/gophercloud/gophercloud/v2" - -func listURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("types") -} - -func createURL(c *gophercloud.ServiceClient) string { - return listURL(c) -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("types", id) -} - -func deleteURL(c *gophercloud.ServiceClient, id string) string { - return getURL(c, id) -} From 43441cb2c1f23a7c11382adb9127f84a4d2d13d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Sun, 24 Mar 2024 10:34:42 +0100 Subject: [PATCH 1795/2296] Docs: Add a context for AllPages() calls Used the following command to add a context to AllPages() calls missing one: ``` find . -type f -exec sed -i 's/\.AllPages()/\.AllPages(context\.TODO())/g' {} \; ``` --- doc.go | 2 +- .../.template/testing/requests_test.go | 2 +- openstack/blockstorage/apiversions/doc.go | 2 +- .../blockstorage/extensions/availabilityzones/doc.go | 2 +- openstack/blockstorage/extensions/backups/doc.go | 2 +- openstack/blockstorage/extensions/schedulerstats/doc.go | 2 +- openstack/blockstorage/extensions/services/doc.go | 2 +- openstack/blockstorage/extensions/volumehost/doc.go | 2 +- openstack/blockstorage/extensions/volumetenants/doc.go | 2 +- openstack/blockstorage/extensions/volumetransfers/doc.go | 2 +- openstack/blockstorage/v3/attachments/doc.go | 2 +- openstack/blockstorage/v3/qos/doc.go | 4 ++-- openstack/blockstorage/v3/snapshots/doc.go | 2 +- openstack/blockstorage/v3/volumetypes/doc.go | 4 ++-- openstack/clustering/v1/clusters/doc.go | 4 ++-- openstack/clustering/v1/nodes/doc.go | 2 +- openstack/clustering/v1/policies/doc.go | 2 +- openstack/clustering/v1/policytypes/doc.go | 2 +- openstack/clustering/v1/profiletypes/doc.go | 2 +- openstack/common/extensions/doc.go | 4 ++-- openstack/compute/apiversions/doc.go | 2 +- openstack/compute/v2/extensions/aggregates/doc.go | 2 +- openstack/compute/v2/extensions/attachinterfaces/doc.go | 2 +- openstack/compute/v2/extensions/availabilityzones/doc.go | 6 +++--- openstack/compute/v2/extensions/defsecrules/doc.go | 2 +- openstack/compute/v2/extensions/diskconfig/doc.go | 2 +- openstack/compute/v2/extensions/extendedstatus/doc.go | 2 +- openstack/compute/v2/extensions/floatingips/doc.go | 2 +- openstack/compute/v2/extensions/hypervisors/doc.go | 4 ++-- openstack/compute/v2/extensions/instanceactions/doc.go | 2 +- openstack/compute/v2/extensions/keypairs/doc.go | 4 ++-- openstack/compute/v2/extensions/networks/doc.go | 2 +- openstack/compute/v2/extensions/secgroups/doc.go | 4 ++-- openstack/compute/v2/extensions/servergroups/doc.go | 2 +- openstack/compute/v2/extensions/services/doc.go | 2 +- openstack/compute/v2/extensions/tenantnetworks/doc.go | 2 +- openstack/compute/v2/flavors/doc.go | 4 ++-- openstack/compute/v2/images/doc.go | 2 +- openstack/compute/v2/servers/doc.go | 4 ++-- openstack/containerinfra/v1/clusters/doc.go | 4 ++-- openstack/containerinfra/v1/clustertemplates/doc.go | 2 +- openstack/containerinfra/v1/nodegroups/doc.go | 2 +- openstack/dns/v2/recordsets/doc.go | 2 +- openstack/dns/v2/transfer/accept/doc.go | 2 +- openstack/dns/v2/transfer/request/doc.go | 2 +- openstack/dns/v2/zones/doc.go | 2 +- openstack/identity/v2/extensions/admin/roles/doc.go | 2 +- openstack/identity/v2/tenants/doc.go | 2 +- openstack/identity/v2/users/doc.go | 4 ++-- openstack/identity/v3/domains/doc.go | 2 +- openstack/identity/v3/endpoints/doc.go | 2 +- openstack/identity/v3/extensions/federation/doc.go | 2 +- openstack/identity/v3/extensions/oauth1/doc.go | 2 +- openstack/identity/v3/extensions/projectendpoints/doc.go | 2 +- openstack/identity/v3/extensions/trusts/doc.go | 2 +- openstack/identity/v3/groups/doc.go | 2 +- openstack/identity/v3/limits/doc.go | 2 +- openstack/identity/v3/policies/doc.go | 2 +- openstack/identity/v3/projects/doc.go | 2 +- openstack/identity/v3/regions/doc.go | 2 +- openstack/identity/v3/registeredlimits/doc.go | 2 +- openstack/identity/v3/roles/doc.go | 6 +++--- openstack/identity/v3/services/doc.go | 2 +- openstack/identity/v3/users/doc.go | 8 ++++---- openstack/image/v2/images/doc.go | 2 +- openstack/image/v2/members/doc.go | 2 +- openstack/image/v2/tasks/doc.go | 2 +- openstack/keymanager/v1/containers/doc.go | 4 ++-- openstack/keymanager/v1/orders/doc.go | 2 +- openstack/keymanager/v1/secrets/doc.go | 2 +- openstack/loadbalancer/v2/amphorae/doc.go | 2 +- openstack/loadbalancer/v2/apiversions/doc.go | 2 +- openstack/loadbalancer/v2/flavorprofiles/doc.go | 2 +- openstack/loadbalancer/v2/flavors/doc.go | 2 +- openstack/loadbalancer/v2/l7policies/doc.go | 4 ++-- openstack/loadbalancer/v2/listeners/doc.go | 2 +- openstack/loadbalancer/v2/loadbalancers/doc.go | 2 +- openstack/loadbalancer/v2/monitors/doc.go | 2 +- openstack/loadbalancer/v2/pools/doc.go | 4 ++-- openstack/loadbalancer/v2/providers/doc.go | 2 +- openstack/networking/v2/apiversions/doc.go | 2 +- openstack/networking/v2/extensions/agents/doc.go | 6 +++--- openstack/networking/v2/extensions/bgp/peers/doc.go | 2 +- openstack/networking/v2/extensions/bgp/speakers/doc.go | 4 ++-- openstack/networking/v2/extensions/external/doc.go | 2 +- .../networking/v2/extensions/layer3/addressscopes/doc.go | 2 +- .../networking/v2/extensions/layer3/floatingips/doc.go | 2 +- .../networking/v2/extensions/layer3/portforwarding/doc.go | 2 +- openstack/networking/v2/extensions/layer3/routers/doc.go | 4 ++-- .../v2/extensions/networkipavailabilities/doc.go | 2 +- openstack/networking/v2/extensions/portsecurity/doc.go | 2 +- openstack/networking/v2/extensions/provider/doc.go | 2 +- openstack/networking/v2/extensions/qos/policies/doc.go | 2 +- openstack/networking/v2/extensions/qos/rules/doc.go | 6 +++--- openstack/networking/v2/extensions/qos/ruletypes/doc.go | 2 +- openstack/networking/v2/extensions/rbacpolicies/doc.go | 2 +- openstack/networking/v2/extensions/security/groups/doc.go | 2 +- openstack/networking/v2/extensions/security/rules/doc.go | 2 +- openstack/networking/v2/extensions/subnetpools/doc.go | 2 +- openstack/networking/v2/extensions/trunks/doc.go | 2 +- openstack/networking/v2/extensions/vlantransparent/doc.go | 2 +- .../networking/v2/extensions/vpnaas/endpointgroups/doc.go | 2 +- .../networking/v2/extensions/vpnaas/ikepolicies/doc.go | 2 +- .../networking/v2/extensions/vpnaas/ipsecpolicies/doc.go | 2 +- openstack/networking/v2/extensions/vpnaas/services/doc.go | 2 +- .../v2/extensions/vpnaas/siteconnections/doc.go | 2 +- openstack/networking/v2/networks/doc.go | 2 +- openstack/networking/v2/ports/doc.go | 2 +- openstack/networking/v2/subnets/doc.go | 2 +- openstack/objectstorage/v1/containers/doc.go | 4 ++-- openstack/objectstorage/v1/objects/doc.go | 4 ++-- openstack/orchestration/v1/stackevents/doc.go | 2 +- openstack/orchestration/v1/stackresources/doc.go | 2 +- openstack/orchestration/v1/stacks/doc.go | 2 +- openstack/placement/v1/resourceproviders/doc.go | 2 +- openstack/sharedfilesystems/apiversions/doc.go | 2 +- openstack/sharedfilesystems/v2/schedulerstats/doc.go | 2 +- openstack/sharedfilesystems/v2/services/doc.go | 2 +- openstack/workflow/v2/crontriggers/doc.go | 2 +- openstack/workflow/v2/executions/doc.go | 2 +- openstack/workflow/v2/workflows/doc.go | 2 +- 121 files changed, 150 insertions(+), 150 deletions(-) diff --git a/doc.go b/doc.go index 19b64d6508..58eeda0fae 100644 --- a/doc.go +++ b/doc.go @@ -100,7 +100,7 @@ of results: If you want to obtain the entire collection of pages without doing any intermediary processing on each page, you can use the AllPages method: - allPages, err := servers.List(client, nil).AllPages() + allPages, err := servers.List(client, nil).AllPages(context.TODO()) allServers, err := servers.ExtractServers(allPages) This top-level package contains utility functions and data types that are used diff --git a/docs/contributor-tutorial/.template/testing/requests_test.go b/docs/contributor-tutorial/.template/testing/requests_test.go index 990c29146e..7040d62670 100644 --- a/docs/contributor-tutorial/.template/testing/requests_test.go +++ b/docs/contributor-tutorial/.template/testing/requests_test.go @@ -34,7 +34,7 @@ func TestListResourcesAllPages(t *testing.T) { defer th.TeardownHTTP() HandleListResourcesSuccessfully(t) - allPages, err := resources.List(client.ServiceClient(), nil).AllPages() + allPages, err := resources.List(client.ServiceClient(), nil).AllPages(context.TODO()) th.AssertNoErr(t, err) actual, err := resources.ExtractResources(allPages) th.AssertNoErr(t, err) diff --git a/openstack/blockstorage/apiversions/doc.go b/openstack/blockstorage/apiversions/doc.go index 7f05463618..8ee7c9ae24 100644 --- a/openstack/blockstorage/apiversions/doc.go +++ b/openstack/blockstorage/apiversions/doc.go @@ -4,7 +4,7 @@ API versions for the OpenStack Block Storage service, code-named Cinder. Example of Retrieving all API Versions - allPages, err := apiversions.List(client).AllPages() + allPages, err := apiversions.List(client).AllPages(context.TODO()) if err != nil { panic("unable to get API versions: " + err.Error()) } diff --git a/openstack/blockstorage/extensions/availabilityzones/doc.go b/openstack/blockstorage/extensions/availabilityzones/doc.go index 29faa8dcbc..d3bbc901c1 100644 --- a/openstack/blockstorage/extensions/availabilityzones/doc.go +++ b/openstack/blockstorage/extensions/availabilityzones/doc.go @@ -4,7 +4,7 @@ available volume availability zones. Example of Get Availability Zone Information - allPages, err := availabilityzones.List(volumeClient).AllPages() + allPages, err := availabilityzones.List(volumeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/backups/doc.go b/openstack/blockstorage/extensions/backups/doc.go index f96e6d3de4..04f3477168 100644 --- a/openstack/blockstorage/extensions/backups/doc.go +++ b/openstack/blockstorage/extensions/backups/doc.go @@ -10,7 +10,7 @@ Example to List Backups VolumeID: "uuid", } - allPages, err := backups.List(client, listOpts).AllPages() + allPages, err := backups.List(client, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/schedulerstats/doc.go b/openstack/blockstorage/extensions/schedulerstats/doc.go index b0a2c8ff30..4e028b56ed 100644 --- a/openstack/blockstorage/extensions/schedulerstats/doc.go +++ b/openstack/blockstorage/extensions/schedulerstats/doc.go @@ -6,7 +6,7 @@ and utilisation. Example: Detail: true, } - allPages, err := schedulerstats.List(client, listOpts).AllPages() + allPages, err := schedulerstats.List(client, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/services/doc.go b/openstack/blockstorage/extensions/services/doc.go index b3fba4cd62..a68ed88f49 100644 --- a/openstack/blockstorage/extensions/services/doc.go +++ b/openstack/blockstorage/extensions/services/doc.go @@ -4,7 +4,7 @@ OpenStack cloud. Example of Retrieving list of all services - allPages, err := services.List(blockstorageClient, services.ListOpts{}).AllPages() + allPages, err := services.List(blockstorageClient, services.ListOpts{}).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/volumehost/doc.go b/openstack/blockstorage/extensions/volumehost/doc.go index 6a651a7c5a..a4fc1fb549 100644 --- a/openstack/blockstorage/extensions/volumehost/doc.go +++ b/openstack/blockstorage/extensions/volumehost/doc.go @@ -9,7 +9,7 @@ information about the Openstack host holding the volume. Example: var allVolumes []VolumeWithHost - allPages, err := volumes.List(client, nil).AllPages() + allPages, err := volumes.List(client, nil).AllPages(context.TODO()) if err != nil { panic("Unable to retrieve volumes: %s", err) } diff --git a/openstack/blockstorage/extensions/volumetenants/doc.go b/openstack/blockstorage/extensions/volumetenants/doc.go index 2f501649fe..1e200a12b1 100644 --- a/openstack/blockstorage/extensions/volumetenants/doc.go +++ b/openstack/blockstorage/extensions/volumetenants/doc.go @@ -9,7 +9,7 @@ tenant/project information. Example: var allVolumes []VolumeWithTenant - allPages, err := volumes.List(client, nil).AllPages() + allPages, err := volumes.List(client, nil).AllPages(context.TODO()) if err != nil { panic("Unable to retrieve volumes: %s", err) } diff --git a/openstack/blockstorage/extensions/volumetransfers/doc.go b/openstack/blockstorage/extensions/volumetransfers/doc.go index fa1ddaa0c7..d25c82abc6 100644 --- a/openstack/blockstorage/extensions/volumetransfers/doc.go +++ b/openstack/blockstorage/extensions/volumetransfers/doc.go @@ -10,7 +10,7 @@ Example to List all Volume Transfer requests being an OpenStack admin AllTenants: true, } - allPages, err := volumetransfers.List(client, listOpts).AllPages() + allPages, err := volumetransfers.List(client, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/blockstorage/v3/attachments/doc.go b/openstack/blockstorage/v3/attachments/doc.go index 838d8962fc..68b8041f2c 100644 --- a/openstack/blockstorage/v3/attachments/doc.go +++ b/openstack/blockstorage/v3/attachments/doc.go @@ -12,7 +12,7 @@ Example to List Attachments } client.Microversion = "3.27" - allPages, err := attachments.List(client, listOpts).AllPages() + allPages, err := attachments.List(client, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 86f288fa5d..141fb9700f 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -36,7 +36,7 @@ Example to list QoS specifications listOpts := qos.ListOpts{} - allPages, err := qos.List(client, listOpts).AllPages() + allPages, err := qos.List(client, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -129,7 +129,7 @@ Example of listing all associations of a QoS qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - allQosAssociations, err := qos.ListAssociations(client, qosID).AllPages() + allQosAssociations, err := qos.ListAssociations(client, qosID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/blockstorage/v3/snapshots/doc.go b/openstack/blockstorage/v3/snapshots/doc.go index 702094c3df..45a7392ecf 100644 --- a/openstack/blockstorage/v3/snapshots/doc.go +++ b/openstack/blockstorage/v3/snapshots/doc.go @@ -6,7 +6,7 @@ programmatically. Example to list Snapshots - allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages() + allPages, err := snapshots.List(client, snapshots.ListOpts{}).AllPages(context.TODO()) if err != nil{ panic(err) } diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index 03cad7ecbd..ed5022243a 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -5,7 +5,7 @@ define the volume capabilities. Example to list Volume Types - allPages, err := volumetypes.List(client, volumetypes.ListOpts{}).AllPages() + allPages, err := volumetypes.List(client, volumetypes.ListOpts{}).AllPages(context.TODO()) if err != nil{ panic(err) } @@ -121,7 +121,7 @@ Example to List Volume Type Access typeID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - allPages, err := volumetypes.ListAccesses(client, typeID).AllPages() + allPages, err := volumetypes.ListAccesses(client, typeID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index b25dede0fe..e6f48ded73 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -30,7 +30,7 @@ Example to List Clusters Name: "testcluster", } - allPages, err := clusters.List(serviceClient, listOpts).AllPages() + allPages, err := clusters.List(serviceClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -117,7 +117,7 @@ Example to ScaleOut a cluster Example to List Policies for a Cluster clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" - allPages, err := clusters.ListPolicies(serviceClient, clusterID, nil).AllPages() + allPages, err := clusters.ListPolicies(serviceClient, clusterID, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index 230cd917cd..593757f19f 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -25,7 +25,7 @@ Example to List Nodes Name: "testnode", } - allPages, err := nodes.List(serviceClient, listOpts).AllPages() + allPages, err := nodes.List(serviceClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index 7424bc8982..b4566a665a 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -8,7 +8,7 @@ Example to List Policies Limit: 2, } - allPages, err := policies.List(clusteringClient, listOpts).AllPages() + allPages, err := policies.List(clusteringClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/clustering/v1/policytypes/doc.go b/openstack/clustering/v1/policytypes/doc.go index c8617d6d8e..3a7c3a0ee0 100644 --- a/openstack/clustering/v1/policytypes/doc.go +++ b/openstack/clustering/v1/policytypes/doc.go @@ -4,7 +4,7 @@ from the OpenStack Clustering Service. Example to List Policy Types - allPages, err := policytypes.List(clusteringClient).AllPages() + allPages, err := policytypes.List(clusteringClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/clustering/v1/profiletypes/doc.go b/openstack/clustering/v1/profiletypes/doc.go index 8c95abfe08..f11cbc4f57 100644 --- a/openstack/clustering/v1/profiletypes/doc.go +++ b/openstack/clustering/v1/profiletypes/doc.go @@ -30,7 +30,7 @@ Example of list operations supported by a profile type serviceClient.Microversion = "1.5" profileTypeName := "os.nova.server-1.0" - allPages, err := profiletypes.ListOps(serviceClient, profileTypeName).AllPages() + allPages, err := profiletypes.ListOps(serviceClient, profileTypeName).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/common/extensions/doc.go b/openstack/common/extensions/doc.go index a67542cac0..979a20e49b 100644 --- a/openstack/common/extensions/doc.go +++ b/openstack/common/extensions/doc.go @@ -26,7 +26,7 @@ Example of Retrieving Compute Extensions Region: os.Getenv("OS_REGION_NAME"), }) - allPages, err := extensions.List(computeClient).AllPages() + allPages, err := extensions.List(computeClient).AllPages(context.TODO()) allExtensions, err := extensions.ExtractExtensions(allPages) for _, extension := range allExtensions{ @@ -41,7 +41,7 @@ Example of Retrieving Network Extensions Region: os.Getenv("OS_REGION_NAME"), }) - allPages, err := extensions.List(networkClient).AllPages() + allPages, err := extensions.List(networkClient).AllPages(context.TODO()) allExtensions, err := extensions.ExtractExtensions(allPages) for _, extension := range allExtensions{ diff --git a/openstack/compute/apiversions/doc.go b/openstack/compute/apiversions/doc.go index 39688e0c2f..b9feb6a0f2 100644 --- a/openstack/compute/apiversions/doc.go +++ b/openstack/compute/apiversions/doc.go @@ -4,7 +4,7 @@ API versions for the Compute service, code-named Nova. Example to List API Versions - allPages, err := apiversions.List(computeClient).AllPages() + allPages, err := apiversions.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index e1e2519d54..3ec63a1b00 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -48,7 +48,7 @@ Example of Update Aggregate Example of Retrieving list of all aggregates - allPages, err := aggregates.List(computeClient).AllPages() + allPages, err := aggregates.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go index 3653122bf3..a6f01ce51d 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -5,7 +5,7 @@ interfaces through Nova. Example of Listing a Server's Interfaces serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" - allPages, err := attachinterfaces.List(computeClient, serverID).AllPages() + allPages, err := attachinterfaces.List(computeClient, serverID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/availabilityzones/doc.go b/openstack/compute/v2/extensions/availabilityzones/doc.go index 13b6d9d424..0d81889a2b 100644 --- a/openstack/compute/v2/extensions/availabilityzones/doc.go +++ b/openstack/compute/v2/extensions/availabilityzones/doc.go @@ -12,7 +12,7 @@ Example of Extend server result with Availability Zone Information: var allServers []ServerWithAZ - allPages, err := servers.List(client, nil).AllPages() + allPages, err := servers.List(client, nil).AllPages(context.TODO()) if err != nil { panic("Unable to retrieve servers: %s", err) } @@ -28,7 +28,7 @@ Example of Extend server result with Availability Zone Information: Example of Get Availability Zone Information - allPages, err := availabilityzones.List(computeClient).AllPages() + allPages, err := availabilityzones.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example of Get Availability Zone Information Example of Get Detailed Availability Zone Information - allPages, err := availabilityzones.ListDetail(computeClient).AllPages() + allPages, err := availabilityzones.ListDetail(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/defsecrules/doc.go b/openstack/compute/v2/extensions/defsecrules/doc.go index 255213555d..1d5777a860 100644 --- a/openstack/compute/v2/extensions/defsecrules/doc.go +++ b/openstack/compute/v2/extensions/defsecrules/doc.go @@ -10,7 +10,7 @@ not work if the OpenStack environment is running the OpenStack Networking Example of Listing Default Security Group Rules - allPages, err := defsecrules.List(computeClient).AllPages() + allPages, err := defsecrules.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/diskconfig/doc.go b/openstack/compute/v2/extensions/diskconfig/doc.go index ed9cc6f733..dbaacd3279 100644 --- a/openstack/compute/v2/extensions/diskconfig/doc.go +++ b/openstack/compute/v2/extensions/diskconfig/doc.go @@ -11,7 +11,7 @@ Example of Obtaining the Disk Config of a Server var allServers []ServerWithDiskConfig - allPages, err := servers.List(client, nil).AllPages() + allPages, err := servers.List(client, nil).AllPages(context.TODO()) if err != nil { panic("Unable to retrieve servers: %s", err) } diff --git a/openstack/compute/v2/extensions/extendedstatus/doc.go b/openstack/compute/v2/extensions/extendedstatus/doc.go index 33b1e35cd6..3a11d1f8fc 100644 --- a/openstack/compute/v2/extensions/extendedstatus/doc.go +++ b/openstack/compute/v2/extensions/extendedstatus/doc.go @@ -9,7 +9,7 @@ the extended status information. Example: var allServers []ServerWithExt - allPages, err := servers.List(client, nil).AllPages() + allPages, err := servers.List(client, nil).AllPages(context.TODO()) if err != nil { panic("Unable to retrieve servers: %s", err) } diff --git a/openstack/compute/v2/extensions/floatingips/doc.go b/openstack/compute/v2/extensions/floatingips/doc.go index f5dbdbf8b9..4eb9e94cbe 100644 --- a/openstack/compute/v2/extensions/floatingips/doc.go +++ b/openstack/compute/v2/extensions/floatingips/doc.go @@ -11,7 +11,7 @@ service. Example to List Floating IPs - allPages, err := floatingips.List(computeClient).AllPages() + allPages, err := floatingips.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index cb50014e87..e2f1a99c20 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -26,7 +26,7 @@ Example of Show Hypervisor Details when using Compute API microversion greater t Example of Retrieving Details of All Hypervisors - allPages, err := hypervisors.List(computeClient, nil).AllPages() + allPages, err := hypervisors.List(computeClient, nil).AllPages(context.TODO()) if err != nil { panic(err) } @@ -49,7 +49,7 @@ Example of Retrieving Details of All Hypervisors when using Compute API microver WithServers: &true, } - allPages, err := hypervisors.List(computeClient, listOpts).AllPages() + allPages, err := hypervisors.List(computeClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/instanceactions/doc.go b/openstack/compute/v2/extensions/instanceactions/doc.go index 8e75d50709..c322da0ae0 100644 --- a/openstack/compute/v2/extensions/instanceactions/doc.go +++ b/openstack/compute/v2/extensions/instanceactions/doc.go @@ -5,7 +5,7 @@ Package instanceactions provides the ability to list or get a server instance-ac Example to List and Get actions: - pages, err := instanceactions.List(client, "server-id", nil).AllPages() + pages, err := instanceactions.List(client, "server-id", nil).AllPages(context.TODO()) if err != nil { panic("fail to get actions pages") } diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go index 9fa914ec71..944619d2aa 100644 --- a/openstack/compute/v2/extensions/keypairs/doc.go +++ b/openstack/compute/v2/extensions/keypairs/doc.go @@ -4,7 +4,7 @@ servers with a specified key pair. Example to List Key Pairs - allPages, err := keypairs.List(computeClient, nil).AllPages() + allPages, err := keypairs.List(computeClient, nil).AllPages(context.TODO()) if err != nil { panic(err) } @@ -26,7 +26,7 @@ Example to List Key Pairs using microversion 2.10 or greater UserID: "user-id", } - allPages, err := keypairs.List(computeClient, listOpts).AllPages() + allPages, err := keypairs.List(computeClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/networks/doc.go b/openstack/compute/v2/extensions/networks/doc.go index f291734e9e..34a9ad969d 100644 --- a/openstack/compute/v2/extensions/networks/doc.go +++ b/openstack/compute/v2/extensions/networks/doc.go @@ -7,7 +7,7 @@ networks. Example to List Networks - allPages, err := networks.List(computeClient).AllPages() + allPages, err := networks.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/secgroups/doc.go b/openstack/compute/v2/extensions/secgroups/doc.go index eedabaf05e..470be87b8b 100644 --- a/openstack/compute/v2/extensions/secgroups/doc.go +++ b/openstack/compute/v2/extensions/secgroups/doc.go @@ -11,7 +11,7 @@ service. Example to List Security Groups - allPages, err := secroups.List(computeClient).AllPages() + allPages, err := secroups.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } @@ -29,7 +29,7 @@ Example to List Security Groups by Server serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" - allPages, err := secroups.ListByServer(computeClient, serverID).AllPages() + allPages, err := secroups.ListByServer(computeClient, serverID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/servergroups/doc.go b/openstack/compute/v2/extensions/servergroups/doc.go index 23fed3c8af..62df82dc6b 100644 --- a/openstack/compute/v2/extensions/servergroups/doc.go +++ b/openstack/compute/v2/extensions/servergroups/doc.go @@ -3,7 +3,7 @@ Package servergroups provides the ability to manage server groups. Example to List Server Groups - allpages, err := servergroups.List(computeClient).AllPages() + allpages, err := servergroups.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/services/doc.go b/openstack/compute/v2/extensions/services/doc.go index 3b2f6ab05e..08e224f0be 100644 --- a/openstack/compute/v2/extensions/services/doc.go +++ b/openstack/compute/v2/extensions/services/doc.go @@ -8,7 +8,7 @@ Example of Retrieving list of all services Binary: "nova-scheduler", } - allPages, err := services.List(computeClient, opts).AllPages() + allPages, err := services.List(computeClient, opts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/tenantnetworks/doc.go b/openstack/compute/v2/extensions/tenantnetworks/doc.go index a32e8ffd58..d355ba4443 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/doc.go +++ b/openstack/compute/v2/extensions/tenantnetworks/doc.go @@ -9,7 +9,7 @@ This API works in both Neutron and nova-network based OpenStack clouds. Example to List Networks Available to a Tenant - allPages, err := tenantnetworks.List(computeClient).AllPages() + allPages, err := tenantnetworks.List(computeClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index 747966d8d9..c988cb8232 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -12,7 +12,7 @@ Example to List Flavors AccessType: flavors.PublicAccess, } - allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages() + allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -59,7 +59,7 @@ Example to List Flavor Access flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages() + allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/images/doc.go b/openstack/compute/v2/images/doc.go index 22410a79a2..2b19c55360 100644 --- a/openstack/compute/v2/images/doc.go +++ b/openstack/compute/v2/images/doc.go @@ -15,7 +15,7 @@ Example to List Images Limit: 2, } - allPages, err := images.ListDetail(computeClient, listOpts).AllPages() + allPages, err := images.ListDetail(computeClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index bab72c1524..0649905abe 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -11,7 +11,7 @@ Example to List Servers AllTenants: true, } - allPages, err := servers.ListSimple(computeClient, listOpts).AllPages() + allPages, err := servers.ListSimple(computeClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -31,7 +31,7 @@ Example to List Detail Servers AllTenants: true, } - allPages, err := servers.List(computeClient, listOpts).AllPages() + allPages, err := servers.List(computeClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index 69203ff79c..9f8d609c9d 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -41,7 +41,7 @@ Example to List Clusters Limit: 20, } - allPages, err := clusters.List(serviceClient, listOpts).AllPages() + allPages, err := clusters.List(serviceClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -57,7 +57,7 @@ Example to List Clusters Example to List Clusters with detailed information - allPagesDetail, err := clusters.ListDetail(serviceClient, clusters.ListOpts{}).AllPages() + allPagesDetail, err := clusters.ListDetail(serviceClient, clusters.ListOpts{}).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/clustertemplates/doc.go b/openstack/containerinfra/v1/clustertemplates/doc.go index 12289c2abb..43b93ef5be 100644 --- a/openstack/containerinfra/v1/clustertemplates/doc.go +++ b/openstack/containerinfra/v1/clustertemplates/doc.go @@ -52,7 +52,7 @@ Example to List Clusters Templates Limit: 20, } - allPages, err := clustertemplates.List(serviceClient, listOpts).AllPages() + allPages, err := clustertemplates.List(serviceClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/nodegroups/doc.go b/openstack/containerinfra/v1/nodegroups/doc.go index ef1d55d9b4..e8e7540154 100644 --- a/openstack/containerinfra/v1/nodegroups/doc.go +++ b/openstack/containerinfra/v1/nodegroups/doc.go @@ -37,7 +37,7 @@ Example of Listing node groups: Role: "worker", } - allPages, err := nodegroups.List(client, clusterUUID, listOpts).AllPages() + allPages, err := nodegroups.List(client, clusterUUID, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/dns/v2/recordsets/doc.go b/openstack/dns/v2/recordsets/doc.go index 617fafa631..d23b832e43 100644 --- a/openstack/dns/v2/recordsets/doc.go +++ b/openstack/dns/v2/recordsets/doc.go @@ -10,7 +10,7 @@ Example to List RecordSets by Zone zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" - allPages, err := recordsets.ListByZone(dnsClient, zoneID, listOpts).AllPages() + allPages, err := recordsets.ListByZone(dnsClient, zoneID, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/dns/v2/transfer/accept/doc.go b/openstack/dns/v2/transfer/accept/doc.go index 1bb8997578..2c725f6da1 100644 --- a/openstack/dns/v2/transfer/accept/doc.go +++ b/openstack/dns/v2/transfer/accept/doc.go @@ -5,7 +5,7 @@ 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() + allPages, err := transferAccepts.List(dnsClient, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/dns/v2/transfer/request/doc.go b/openstack/dns/v2/transfer/request/doc.go index 6acb51adfa..2813edfb69 100644 --- a/openstack/dns/v2/transfer/request/doc.go +++ b/openstack/dns/v2/transfer/request/doc.go @@ -4,7 +4,7 @@ resource for the OpenStack DNS service. Example to List Zone Transfer Requests - allPages, err := transferRequests.List(dnsClient, nil).AllPages() + allPages, err := transferRequests.List(dnsClient, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/dns/v2/zones/doc.go b/openstack/dns/v2/zones/doc.go index 7733155bcf..ac9082eb10 100644 --- a/openstack/dns/v2/zones/doc.go +++ b/openstack/dns/v2/zones/doc.go @@ -8,7 +8,7 @@ Example to List Zones Email: "jdoe@example.com", } - allPages, err := zones.List(dnsClient, listOpts).AllPages() + allPages, err := zones.List(dnsClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v2/extensions/admin/roles/doc.go b/openstack/identity/v2/extensions/admin/roles/doc.go index 6aee4eae56..8e6ad07e8f 100644 --- a/openstack/identity/v2/extensions/admin/roles/doc.go +++ b/openstack/identity/v2/extensions/admin/roles/doc.go @@ -17,7 +17,7 @@ arbitrary name assigned by the user. Example to List Roles - allPages, err := roles.List(identityClient).AllPages() + allPages, err := roles.List(identityClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v2/tenants/doc.go b/openstack/identity/v2/tenants/doc.go index 348dd20839..476951762b 100644 --- a/openstack/identity/v2/tenants/doc.go +++ b/openstack/identity/v2/tenants/doc.go @@ -12,7 +12,7 @@ Example to List Tenants Limit: 2, } - allPages, err := tenants.List(identityClient, listOpts).AllPages() + allPages, err := tenants.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v2/users/doc.go b/openstack/identity/v2/users/doc.go index b72466a137..4c9a4027a3 100644 --- a/openstack/identity/v2/users/doc.go +++ b/openstack/identity/v2/users/doc.go @@ -4,7 +4,7 @@ resource for the OpenStack Identity Service. Example to List Users - allPages, err := users.List(identityClient).AllPages() + allPages, err := users.List(identityClient).AllPages(context.TODO()) if err != nil { panic(err) } @@ -58,7 +58,7 @@ Example to List a User's Roles tenantID := "1d8b6120dcc640fda4fc9194ffc80273" userID := "c39e3de9be2d4c779f1dfd6abacc176d" - allPages, err := users.ListRoles(identityClient, tenantID, userID).AllPages() + allPages, err := users.ListRoles(identityClient, tenantID, userID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/domains/doc.go b/openstack/identity/v3/domains/doc.go index 720db782f7..d7f73c0195 100644 --- a/openstack/identity/v3/domains/doc.go +++ b/openstack/identity/v3/domains/doc.go @@ -8,7 +8,7 @@ Example to List Domains Enabled: &iTrue, } - allPages, err := domains.List(identityClient, listOpts).AllPages() + allPages, err := domains.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/endpoints/doc.go b/openstack/identity/v3/endpoints/doc.go index 27e2378e06..eedbf6d891 100644 --- a/openstack/identity/v3/endpoints/doc.go +++ b/openstack/identity/v3/endpoints/doc.go @@ -13,7 +13,7 @@ Example to List Endpoints ServiceID: serviceID, } - allPages, err := endpoints.List(identityClient, listOpts).AllPages() + allPages, err := endpoints.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/extensions/federation/doc.go index 8d394d890a..12ac53d2d4 100644 --- a/openstack/identity/v3/extensions/federation/doc.go +++ b/openstack/identity/v3/extensions/federation/doc.go @@ -4,7 +4,7 @@ Openstack Identity service. Example to List Mappings - allPages, err := federation.ListMappings(identityClient).AllPages() + allPages, err := federation.ListMappings(identityClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/oauth1/doc.go b/openstack/identity/v3/extensions/oauth1/doc.go index 4294ef6c89..4c6117671e 100644 --- a/openstack/identity/v3/extensions/oauth1/doc.go +++ b/openstack/identity/v3/extensions/oauth1/doc.go @@ -64,7 +64,7 @@ Example to Create an OAuth1 Access Token Example to List User's OAuth1 Access Tokens - allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages() + allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/projectendpoints/doc.go b/openstack/identity/v3/extensions/projectendpoints/doc.go index ac07220ee2..324cca6508 100644 --- a/openstack/identity/v3/extensions/projectendpoints/doc.go +++ b/openstack/identity/v3/extensions/projectendpoints/doc.go @@ -9,7 +9,7 @@ Example to List Project Endpoints projectD := "e629d6e599d9489fb3ae5d9cc12eaea3" - allPages, err := projectendpoints.List(identityClient, projectID).AllPages() + allPages, err := projectendpoints.List(identityClient, projectID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go index d5182cd3e6..a1d887080c 100644 --- a/openstack/identity/v3/extensions/trusts/doc.go +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -69,7 +69,7 @@ Example to List a Trust TrustorUserId: "3422b7c113894f5d90665e1a79655e23", } - allPages, err := trusts.List(identityClient, listOpts).AllPages() + allPages, err := trusts.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/groups/doc.go b/openstack/identity/v3/groups/doc.go index 696e2a5d8e..480753af24 100644 --- a/openstack/identity/v3/groups/doc.go +++ b/openstack/identity/v3/groups/doc.go @@ -7,7 +7,7 @@ Example to List Groups DomainID: "default", } - allPages, err := groups.List(identityClient, listOpts).AllPages() + allPages, err := groups.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go index 67f9c6f949..61b7b91a60 100644 --- a/openstack/identity/v3/limits/doc.go +++ b/openstack/identity/v3/limits/doc.go @@ -15,7 +15,7 @@ Example to List Limits ProjectID: "3d596369fd2043bf8aca3c8decb0189e", } - allPages, err := limits.List(identityClient, listOpts).AllPages() + allPages, err := limits.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/policies/doc.go b/openstack/identity/v3/policies/doc.go index 0bdc7a6851..2d8574739a 100644 --- a/openstack/identity/v3/policies/doc.go +++ b/openstack/identity/v3/policies/doc.go @@ -8,7 +8,7 @@ Example to List Policies Type: "application/json", } - allPages, err := policies.List(identityClient, listOpts).AllPages() + allPages, err := policies.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/projects/doc.go b/openstack/identity/v3/projects/doc.go index 9c34ee0a6a..e08bf21b5f 100644 --- a/openstack/identity/v3/projects/doc.go +++ b/openstack/identity/v3/projects/doc.go @@ -8,7 +8,7 @@ Example to List Projects Enabled: gophercloud.Enabled, } - allPages, err := projects.List(identityClient, listOpts).AllPages() + allPages, err := projects.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/regions/doc.go b/openstack/identity/v3/regions/doc.go index a37b05a544..a3d1162560 100644 --- a/openstack/identity/v3/regions/doc.go +++ b/openstack/identity/v3/regions/doc.go @@ -7,7 +7,7 @@ Example to List Regions ParentRegionID: "RegionOne", } - allPages, err := regions.List(identityClient, listOpts).AllPages() + allPages, err := regions.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/registeredlimits/doc.go b/openstack/identity/v3/registeredlimits/doc.go index a8cb373d7f..567a157385 100644 --- a/openstack/identity/v3/registeredlimits/doc.go +++ b/openstack/identity/v3/registeredlimits/doc.go @@ -8,7 +8,7 @@ Example to List RegisteredLimits ResourceName: "image_size_total", } - allPages, err := registeredlimits.List(identityClient, listOpts).AllPages() + allPages, err := registeredlimits.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index f0e4d045e9..578ebeedb2 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -8,7 +8,7 @@ Example to List Roles DomainID: "default", } - allPages, err := roles.List(identityClient, listOpts).AllPages() + allPages, err := roles.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -65,7 +65,7 @@ Example to List Role Assignments ScopeProjectID: "9df1a02f5eb2416a9781e8b0c022d3ae", } - allPages, err := roles.ListAssignments(identityClient, listOpts).AllPages() + allPages, err := roles.ListAssignments(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -88,7 +88,7 @@ Example to List Role Assignments for a User on a Project ProjectID: projectID, } - allPages, err := roles.ListAssignmentsOnResource(identityClient, listAssignmentsOnResourceOpts).AllPages() + allPages, err := roles.ListAssignmentsOnResource(identityClient, listAssignmentsOnResourceOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index e0a79c2726..125e996093 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -8,7 +8,7 @@ Example to List Services ServiceType: "compute", } - allPages, err := services.List(identityClient, listOpts).AllPages() + allPages, err := services.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index 09133cda9f..ae984dd6e6 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -7,7 +7,7 @@ Example to List Users DomainID: "default", } - allPages, err := users.List(identityClient, listOpts).AllPages() + allPages, err := users.List(identityClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -82,7 +82,7 @@ Example to List Groups a User Belongs To userID := "0fe36e73809d46aeae6705c39077b1b3" - allPages, err := users.ListGroups(identityClient, userID).AllPages() + allPages, err := users.ListGroups(identityClient, userID).AllPages(context.TODO()) if err != nil { panic(err) } @@ -133,7 +133,7 @@ Example to List Projects a User Belongs To userID := "0fe36e73809d46aeae6705c39077b1b3" - allPages, err := users.ListProjects(identityClient, userID).AllPages() + allPages, err := users.ListProjects(identityClient, userID).AllPages(context.TODO()) if err != nil { panic(err) } @@ -154,7 +154,7 @@ Example to List Users in a Group DomainID: "default", } - allPages, err := users.ListInGroup(identityClient, groupID, listOpts).AllPages() + allPages, err := users.ListInGroup(identityClient, groupID, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/image/v2/images/doc.go b/openstack/image/v2/images/doc.go index 14da9ac90d..4f999a63c8 100644 --- a/openstack/image/v2/images/doc.go +++ b/openstack/image/v2/images/doc.go @@ -8,7 +8,7 @@ Example to List Images Owner: "a7509e1ae65945fda83f3e52c6296017", } - allPages, err := images.List(imagesClient, listOpts).AllPages() + allPages, err := images.List(imagesClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/image/v2/members/doc.go b/openstack/image/v2/members/doc.go index 1a7132045f..69a0555cde 100644 --- a/openstack/image/v2/members/doc.go +++ b/openstack/image/v2/members/doc.go @@ -7,7 +7,7 @@ Example to List Members of an Image imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" - allPages, err := members.List(imageID).AllPages() + allPages, err := members.List(imageID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/image/v2/tasks/doc.go b/openstack/image/v2/tasks/doc.go index 689a1c7327..f723bd6295 100644 --- a/openstack/image/v2/tasks/doc.go +++ b/openstack/image/v2/tasks/doc.go @@ -8,7 +8,7 @@ Example to List Tasks Owner: "424e7cf0243c468ca61732ba45973b3e", } - allPages, err := tasks.List(imagesClient, listOpts).AllPages() + allPages, err := tasks.List(imagesClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/keymanager/v1/containers/doc.go b/openstack/keymanager/v1/containers/doc.go index cb11920f29..7fdb4ca2c1 100644 --- a/openstack/keymanager/v1/containers/doc.go +++ b/openstack/keymanager/v1/containers/doc.go @@ -4,7 +4,7 @@ Service. Example to List Containers - allPages, err := containers.List(client, nil).AllPages() + allPages, err := containers.List(client, nil).AllPages(context.TODO()) if err != nil { panic(err) } @@ -47,7 +47,7 @@ Example to Delete a Container Example to List Consumers of a Container - allPages, err := containers.ListConsumers(client, containerID, nil).AllPages() + allPages, err := containers.ListConsumers(client, containerID, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/keymanager/v1/orders/doc.go b/openstack/keymanager/v1/orders/doc.go index fcbeccf4e5..d7d96d2671 100644 --- a/openstack/keymanager/v1/orders/doc.go +++ b/openstack/keymanager/v1/orders/doc.go @@ -4,7 +4,7 @@ Service. Example to List Orders - allPages, err := orders.List(client, nil).AllPages() + allPages, err := orders.List(client, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/keymanager/v1/secrets/doc.go b/openstack/keymanager/v1/secrets/doc.go index 2b5e299f1c..67d951740a 100644 --- a/openstack/keymanager/v1/secrets/doc.go +++ b/openstack/keymanager/v1/secrets/doc.go @@ -13,7 +13,7 @@ Example to List Secrets CreatedQuery: createdQuery, } - allPages, err := secrets.List(client, listOpts).AllPages() + allPages, err := secrets.List(client, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/amphorae/doc.go b/openstack/loadbalancer/v2/amphorae/doc.go index edce77cd39..a9e620a445 100644 --- a/openstack/loadbalancer/v2/amphorae/doc.go +++ b/openstack/loadbalancer/v2/amphorae/doc.go @@ -8,7 +8,7 @@ Example to List Amphorae LoadbalancerID: "6bd55cd3-802e-447e-a518-1e74e23bb106", } - allPages, err := amphorae.List(octaviaClient, listOpts).AllPages() + allPages, err := amphorae.List(octaviaClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/apiversions/doc.go b/openstack/loadbalancer/v2/apiversions/doc.go index d9b5eff9d9..d65e3a6dd4 100644 --- a/openstack/loadbalancer/v2/apiversions/doc.go +++ b/openstack/loadbalancer/v2/apiversions/doc.go @@ -5,7 +5,7 @@ restricted to this particular version. Example to List API Versions - allPages, err := apiversions.List(loadbalancerClient).AllPages() + allPages, err := apiversions.List(loadbalancerClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/flavorprofiles/doc.go b/openstack/loadbalancer/v2/flavorprofiles/doc.go index fcf846f3c3..4fcddeb479 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/doc.go +++ b/openstack/loadbalancer/v2/flavorprofiles/doc.go @@ -6,7 +6,7 @@ Example to List FlavorProfiles listOpts := flavorprofiles.ListOpts{} - allPages, err := flavorprofiles.List(octaviaClient, listOpts).AllPages() + allPages, err := flavorprofiles.List(octaviaClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/flavors/doc.go b/openstack/loadbalancer/v2/flavors/doc.go index cbdd127f55..13eaccd3d3 100644 --- a/openstack/loadbalancer/v2/flavors/doc.go +++ b/openstack/loadbalancer/v2/flavors/doc.go @@ -6,7 +6,7 @@ Example to List Flavors listOpts := flavors.ListOpts{} - allPages, err := flavors.List(octaviaClient, listOpts).AllPages() + allPages, err := flavors.List(octaviaClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index 813579905c..e8ff692166 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -20,7 +20,7 @@ Example to List L7Policies listOpts := l7policies.ListOpts{ ListenerID: "c79a4468-d788-410c-bf79-9a8ef6354852", } - allPages, err := l7policies.List(lbClient, listOpts).AllPages() + allPages, err := l7policies.List(lbClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -78,7 +78,7 @@ Example to List L7 Rules listOpts := l7policies.ListRulesOpts{ RuleType: l7policies.TypePath, } - allPages, err := l7policies.ListRules(lbClient, l7policyID, listOpts).AllPages() + allPages, err := l7policies.ListRules(lbClient, l7policyID, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/listeners/doc.go b/openstack/loadbalancer/v2/listeners/doc.go index c99bbc7026..8e9ae86c42 100644 --- a/openstack/loadbalancer/v2/listeners/doc.go +++ b/openstack/loadbalancer/v2/listeners/doc.go @@ -8,7 +8,7 @@ Example to List Listeners LoadbalancerID : "ca430f80-1737-4712-8dc6-3f640d55594b", } - allPages, err := listeners.List(networkClient, listOpts).AllPages() + allPages, err := listeners.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go index 8587a0d99e..4aca2e3c52 100644 --- a/openstack/loadbalancer/v2/loadbalancers/doc.go +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -8,7 +8,7 @@ Example to List Load Balancers Provider: "haproxy", } - allPages, err := loadbalancers.List(networkClient, listOpts).AllPages() + allPages, err := loadbalancers.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/monitors/doc.go b/openstack/loadbalancer/v2/monitors/doc.go index b191b45e9c..2a49b6abae 100644 --- a/openstack/loadbalancer/v2/monitors/doc.go +++ b/openstack/loadbalancer/v2/monitors/doc.go @@ -8,7 +8,7 @@ Example to List Monitors PoolID: "c79a4468-d788-410c-bf79-9a8ef6354852", } - allPages, err := monitors.List(networkClient, listOpts).AllPages() + allPages, err := monitors.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/pools/doc.go b/openstack/loadbalancer/v2/pools/doc.go index 5657cd8733..a04b9c819d 100644 --- a/openstack/loadbalancer/v2/pools/doc.go +++ b/openstack/loadbalancer/v2/pools/doc.go @@ -8,7 +8,7 @@ Example to List Pools LoadbalancerID: "c79a4468-d788-410c-bf79-9a8ef6354852", } - allPages, err := pools.List(networkClient, listOpts).AllPages() + allPages, err := pools.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -68,7 +68,7 @@ Example to List Pool Members ProtocolPort: 80, } - allPages, err := pools.ListMembers(networkClient, poolID, listOpts).AllPages() + allPages, err := pools.ListMembers(networkClient, poolID, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/providers/doc.go b/openstack/loadbalancer/v2/providers/doc.go index d03ea79237..af799758d1 100644 --- a/openstack/loadbalancer/v2/providers/doc.go +++ b/openstack/loadbalancer/v2/providers/doc.go @@ -4,7 +4,7 @@ at OpenStack Octavia Load Balancing service. Example to List Providers - allPages, err := providers.List(lbClient).AllPages() + allPages, err := providers.List(lbClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/apiversions/doc.go b/openstack/networking/v2/apiversions/doc.go index 5461942b92..449fa2e3ad 100644 --- a/openstack/networking/v2/apiversions/doc.go +++ b/openstack/networking/v2/apiversions/doc.go @@ -5,7 +5,7 @@ restricted to this particular version. Example to List API Versions - allPages, err := apiversions.ListVersions(networkingClient).AllPages() + allPages, err := apiversions.ListVersions(networkingClient).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/agents/doc.go b/openstack/networking/v2/extensions/agents/doc.go index 83bc09cfdb..f545d95dcd 100644 --- a/openstack/networking/v2/extensions/agents/doc.go +++ b/openstack/networking/v2/extensions/agents/doc.go @@ -7,7 +7,7 @@ Example of Listing Agents AgentType: "Open vSwitch agent", } - allPages, err := agents.List(networkClient, listOpts).AllPages() + allPages, err := agents.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -85,7 +85,7 @@ Example to Remove a network from a DHCP Agent Example to List BGP speakers by dragent - pages, err := agents.ListBGPSpeakers(c, agentID).AllPages() + pages, err := agents.ListBGPSpeakers(c, agentID).AllPages(context.TODO()) if err != nil { log.Panicf("%v", err) } @@ -115,7 +115,7 @@ Example to Remove bgp speaker from dragent Example to list dragents hosting specific bgp speaker - pages, err := agents.ListDRAgentHostingBGPSpeakers(client, speakerID).AllPages() + pages, err := agents.ListDRAgentHostingBGPSpeakers(client, speakerID).AllPages(context.TODO()) if err != nil { log.Panic(err) } diff --git a/openstack/networking/v2/extensions/bgp/peers/doc.go b/openstack/networking/v2/extensions/bgp/peers/doc.go index e0277b6822..0c497279d4 100644 --- a/openstack/networking/v2/extensions/bgp/peers/doc.go +++ b/openstack/networking/v2/extensions/bgp/peers/doc.go @@ -7,7 +7,7 @@ Package peers contains the functionality for working with Neutron bgp peers. Example: - pages, err := peers.List(c).AllPages() + pages, err := peers.List(c).AllPages(context.TODO()) if err != nil { log.Panic(err) } diff --git a/openstack/networking/v2/extensions/bgp/speakers/doc.go b/openstack/networking/v2/extensions/bgp/speakers/doc.go index 99f54a08ff..c1d6f2b7e0 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/doc.go +++ b/openstack/networking/v2/extensions/bgp/speakers/doc.go @@ -8,7 +8,7 @@ Package speakers contains the functionality for working with Neutron bgp speaker Example: - pages, err := speakers.List(c).AllPages() + pages, err := speakers.List(c).AllPages(context.TODO()) if err != nil { log.Panic(err) } @@ -107,7 +107,7 @@ Example: Example: - pages, err := speakers.GetAdvertisedRoutes(c, speakerID).AllPages() + pages, err := speakers.GetAdvertisedRoutes(c, speakerID).AllPages(context.TODO()) if err != nil { log.Panic(err) } diff --git a/openstack/networking/v2/extensions/external/doc.go b/openstack/networking/v2/extensions/external/doc.go index bec2c0f5f9..56d4f23041 100644 --- a/openstack/networking/v2/extensions/external/doc.go +++ b/openstack/networking/v2/extensions/external/doc.go @@ -18,7 +18,7 @@ Example to List Networks with External Information var allNetworks []NetworkWithExternalExt - allPages, err := networks.List(networkClient, listOpts).AllPages() + allPages, err := networks.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go index e25ee97fd8..188f075be1 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go @@ -7,7 +7,7 @@ Example of Listing Address scopes IPVersion: 6, } - allPages, err := addressscopes.List(networkClient, listOpts).AllPages() + allPages, err := addressscopes.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/doc.go b/openstack/networking/v2/extensions/layer3/floatingips/doc.go index a71a3ec88a..fdc20d1608 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/doc.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/doc.go @@ -8,7 +8,7 @@ Example to List Floating IPs FloatingNetworkID: "a6917946-38ab-4ffd-a55a-26c0980ce5ee", } - allPages, err := floatingips.List(networkClient, listOpts).AllPages() + allPages, err := floatingips.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go index c8f2744d8d..34e551a790 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go @@ -5,7 +5,7 @@ OpenStack Networking service. Example to list all Port Forwardings for a floating IP fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" - allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fipID).AllPages() + allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fipID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/routers/doc.go b/openstack/networking/v2/extensions/layer3/routers/doc.go index fb20e2ef54..f9958d8c6e 100644 --- a/openstack/networking/v2/extensions/layer3/routers/doc.go +++ b/openstack/networking/v2/extensions/layer3/routers/doc.go @@ -5,7 +5,7 @@ Networking service. Example to List Routers listOpts := routers.ListOpts{} - allPages, err := routers.List(networkClient, listOpts).AllPages() + allPages, err := routers.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -122,7 +122,7 @@ Example to List an L3 agents for a Router routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" - allPages, err := routers.ListL3Agents(networkClient, routerID).AllPages() + allPages, err := routers.ListL3Agents(networkClient, routerID).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/networkipavailabilities/doc.go b/openstack/networking/v2/extensions/networkipavailabilities/doc.go index f07cc7a8c7..e3284c2fa4 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/doc.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/doc.go @@ -4,7 +4,7 @@ networkipavailabilities through the Neutron API. Example of Listing NetworkIPAvailabilities - allPages, err := networkipavailabilities.List(networkClient, networkipavailabilities.ListOpts{}).AllPages() + allPages, err := networkipavailabilities.List(networkClient, networkipavailabilities.ListOpts{}).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/portsecurity/doc.go b/openstack/networking/v2/extensions/portsecurity/doc.go index f87820e0e2..a80852607c 100644 --- a/openstack/networking/v2/extensions/portsecurity/doc.go +++ b/openstack/networking/v2/extensions/portsecurity/doc.go @@ -15,7 +15,7 @@ Example to List Networks with Port Security Information Name: "network_1", } - allPages, err := networks.List(networkClient, listOpts).AllPages() + allPages, err := networks.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/provider/doc.go b/openstack/networking/v2/extensions/provider/doc.go index 8700a30e53..9906c4a3ad 100644 --- a/openstack/networking/v2/extensions/provider/doc.go +++ b/openstack/networking/v2/extensions/provider/doc.go @@ -29,7 +29,7 @@ Example to List Networks with Provider Information var allNetworks []NetworkWithProvider - allPages, err := networks.List(networkClient, nil).AllPages() + allPages, err := networks.List(networkClient, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index f2346168b8..bf223eea28 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -186,7 +186,7 @@ Example to List QoS policies Shared: &shared, } - allPages, err := policies.List(networkClient, listOpts).AllPages() + allPages, err := policies.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index 9f87dd649d..556d638a72 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -9,7 +9,7 @@ Example of Listing BandwidthLimitRules policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - allPages, err := rules.ListBandwidthLimitRules(networkClient, policyID, listOpts).AllPages() + allPages, err := rules.ListBandwidthLimitRules(networkClient, policyID, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -87,7 +87,7 @@ Example of Listing DSCP marking rules policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - allPages, err := rules.ListDSCPMarkingRules(networkClient, policyID, listOpts).AllPages() + allPages, err := rules.ListDSCPMarkingRules(networkClient, policyID, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -164,7 +164,7 @@ Example of Listing MinimumBandwidthRules policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - allPages, err := rules.ListMinimumBandwidthRules(networkClient, policyID, listOpts).AllPages() + allPages, err := rules.ListMinimumBandwidthRules(networkClient, policyID, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/doc.go b/openstack/networking/v2/extensions/qos/ruletypes/doc.go index e87efd96bd..6a9f0d7fac 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/doc.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/doc.go @@ -3,7 +3,7 @@ Package ruletypes contains functionality for working with Neutron 'quality of se Example of Listing QoS rule types - page, err := ruletypes.ListRuleTypes(client).AllPages() + page, err := ruletypes.ListRuleTypes(client).AllPages(context.TODO()) if err != nil { return } diff --git a/openstack/networking/v2/extensions/rbacpolicies/doc.go b/openstack/networking/v2/extensions/rbacpolicies/doc.go index 5e69f21a28..9b4361350d 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/doc.go +++ b/openstack/networking/v2/extensions/rbacpolicies/doc.go @@ -33,7 +33,7 @@ Example to List RBAC Policies TenantID: "a99e9b4e620e4db09a2dfb6e42a01e66", } - allPages, err := rbacpolicies.List(rbacClient, listOpts).AllPages() + allPages, err := rbacpolicies.List(rbacClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/security/groups/doc.go b/openstack/networking/v2/extensions/security/groups/doc.go index 7d8bbcaacb..9e494e034a 100644 --- a/openstack/networking/v2/extensions/security/groups/doc.go +++ b/openstack/networking/v2/extensions/security/groups/doc.go @@ -8,7 +8,7 @@ Example to List Security Groups TenantID: "966b3c7d36a24facaf20b7e458bf2192", } - allPages, err := groups.List(networkClient, listOpts).AllPages() + allPages, err := groups.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/security/rules/doc.go b/openstack/networking/v2/extensions/security/rules/doc.go index bf66dc8b40..cbac3c7731 100644 --- a/openstack/networking/v2/extensions/security/rules/doc.go +++ b/openstack/networking/v2/extensions/security/rules/doc.go @@ -8,7 +8,7 @@ Example to List Security Groups Rules Protocol: "tcp", } - allPages, err := rules.List(networkClient, listOpts).AllPages() + allPages, err := rules.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/subnetpools/doc.go b/openstack/networking/v2/extensions/subnetpools/doc.go index 2a9fe63dd4..5e841df870 100644 --- a/openstack/networking/v2/extensions/subnetpools/doc.go +++ b/openstack/networking/v2/extensions/subnetpools/doc.go @@ -7,7 +7,7 @@ Example of Listing Subnetpools IPVersion: 6, } - allPages, err := subnetpools.List(networkClient, listOpts).AllPages() + allPages, err := subnetpools.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 879e00070e..954bffc346 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -59,7 +59,7 @@ Example of deleting a Trunk Example of listing Trunks listOpts := trunks.ListOpts{} - allPages, err := trunks.List(networkClient, listOpts).AllPages() + allPages, err := trunks.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vlantransparent/doc.go b/openstack/networking/v2/extensions/vlantransparent/doc.go index bb10596dd9..6bd1a70036 100644 --- a/openstack/networking/v2/extensions/vlantransparent/doc.go +++ b/openstack/networking/v2/extensions/vlantransparent/doc.go @@ -18,7 +18,7 @@ Example of Listing Networks with the vlan-transparent extension var allNetworks []NetworkWithVLANTransparentExt - allPages, err := networks.List(networkClient, listOpts).AllPages() + allPages, err := networks.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go index 5f49bd1da4..6b0a62c1c5 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go @@ -32,7 +32,7 @@ Example to Delete an Endpoint Group Example to List Endpoint groups - allPages, err := endpointgroups.List(client, nil).AllPages() + allPages, err := endpointgroups.List(client, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index 72720fe3ae..a37fb7a75a 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -47,7 +47,7 @@ Example to Update an IKE policy Example to List IKE policies - allPages, err := ikepolicies.List(client, nil).AllPages() + allPages, err := ikepolicies.List(client, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go index 1e9303eada..bc995b9e0e 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -42,7 +42,7 @@ Example to Update an IPSec policy Example to List IPSec policies - allPages, err := ipsecpolicies.List(client, nil).AllPages() + allPages, err := ipsecpolicies.List(client, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/services/doc.go b/openstack/networking/v2/extensions/vpnaas/services/doc.go index 5cafb113f4..40a28c71f0 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/services/doc.go @@ -8,7 +8,7 @@ Example to List Services TenantID: "966b3c7d36a24facaf20b7e458bf2192", } - allPages, err := services.List(networkClient, listOpts).AllPages() + allPages, err := services.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go index 1b9c0842e4..36b3b97715 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go @@ -40,7 +40,7 @@ Example to Delete a site connection Example to List site connections - allPages, err := siteconnections.List(client, nil).AllPages() + allPages, err := siteconnections.List(client, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/networks/doc.go b/openstack/networking/v2/networks/doc.go index 9d1dd5a7ea..31bded96ed 100644 --- a/openstack/networking/v2/networks/doc.go +++ b/openstack/networking/v2/networks/doc.go @@ -14,7 +14,7 @@ Example to List Networks TenantID: "a99e9b4e620e4db09a2dfb6e42a01e66", } - allPages, err := networks.List(networkClient, listOpts).AllPages() + allPages, err := networks.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/ports/doc.go b/openstack/networking/v2/ports/doc.go index cfb1774fb4..a2c58945c8 100644 --- a/openstack/networking/v2/ports/doc.go +++ b/openstack/networking/v2/ports/doc.go @@ -14,7 +14,7 @@ Example to List Ports DeviceID: "b0b89efe-82f8-461d-958b-adbf80f50c7d", } - allPages, err := ports.List(networkClient, listOpts).AllPages() + allPages, err := ports.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/networking/v2/subnets/doc.go b/openstack/networking/v2/subnets/doc.go index 8bb4468c4e..076fcf1aa8 100644 --- a/openstack/networking/v2/subnets/doc.go +++ b/openstack/networking/v2/subnets/doc.go @@ -15,7 +15,7 @@ Example to List Subnets IPVersion: 4, } - allPages, err := subnets.List(networkClient, listOpts).AllPages() + allPages, err := subnets.List(networkClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/objectstorage/v1/containers/doc.go b/openstack/objectstorage/v1/containers/doc.go index ffc4f05297..9f2b9626a8 100644 --- a/openstack/objectstorage/v1/containers/doc.go +++ b/openstack/objectstorage/v1/containers/doc.go @@ -17,7 +17,7 @@ Example to List Containers Full: true, } - allPages, err := containers.List(objectStorageClient, listOpts).AllPages() + allPages, err := containers.List(objectStorageClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -37,7 +37,7 @@ Example to List Only Container Names Full: false, } - allPages, err := containers.List(objectStorageClient, listOpts).AllPages() + allPages, err := containers.List(objectStorageClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/objectstorage/v1/objects/doc.go b/openstack/objectstorage/v1/objects/doc.go index 7714460aad..0ed4addcc4 100644 --- a/openstack/objectstorage/v1/objects/doc.go +++ b/openstack/objectstorage/v1/objects/doc.go @@ -16,7 +16,7 @@ Example to List Objects Full: true, } - allPages, err := objects.List(objectStorageClient, containerName, listOpts).AllPages() + allPages, err := objects.List(objectStorageClient, containerName, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } @@ -38,7 +38,7 @@ Example to List Object Names Full: false, } - allPages, err := objects.List(objectStorageClient, containerName, listOpts).AllPages() + allPages, err := objects.List(objectStorageClient, containerName, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/orchestration/v1/stackevents/doc.go b/openstack/orchestration/v1/stackevents/doc.go index ff78fcc512..19f44e7eb8 100644 --- a/openstack/orchestration/v1/stackevents/doc.go +++ b/openstack/orchestration/v1/stackevents/doc.go @@ -5,7 +5,7 @@ updating and abandoning. Example for list events for a stack - pages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages() + pages, err := stackevents.List(client, stack.Name, stack.ID, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/orchestration/v1/stackresources/doc.go b/openstack/orchestration/v1/stackresources/doc.go index 2f2be2fd6e..e37b108057 100644 --- a/openstack/orchestration/v1/stackresources/doc.go +++ b/openstack/orchestration/v1/stackresources/doc.go @@ -17,7 +17,7 @@ Example of get resource information in stack Example for list stack resources - all_stack_rsrc_pages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages() + all_stack_rsrc_pages, err := stackresources.List(client, stack.Name, stack.ID, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index e0fec79a32..53be5b9b38 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -24,7 +24,7 @@ Example of Preparing Orchestration client: Example of List Stack: - all_stack_pages, err := stacks.List(client, nil).AllPages() + all_stack_pages, err := stacks.List(client, nil).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 27feb28d76..006669a94f 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -3,7 +3,7 @@ Package resourceproviders creates and lists all resource providers from the Open Example to list resource providers - allPages, err := resourceproviders.List(placementClient, resourceproviders.ListOpts{}).AllPages() + allPages, err := resourceproviders.List(placementClient, resourceproviders.ListOpts{}).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/sharedfilesystems/apiversions/doc.go b/openstack/sharedfilesystems/apiversions/doc.go index dc0a55ebad..c9bf20b5fb 100644 --- a/openstack/sharedfilesystems/apiversions/doc.go +++ b/openstack/sharedfilesystems/apiversions/doc.go @@ -4,7 +4,7 @@ API versions for the Shared File System service, code-named Manila. Example to List API Versions - allPages, err := apiversions.List(client).AllPages() + allPages, err := apiversions.List(client).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/sharedfilesystems/v2/schedulerstats/doc.go b/openstack/sharedfilesystems/v2/schedulerstats/doc.go index f74c9836d0..da294e86cd 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/doc.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/doc.go @@ -5,7 +5,7 @@ and utilisation. Example: listOpts := schedulerstats.ListOpts{ } - allPages, err := schedulerstats.List(client, listOpts).AllPages() + allPages, err := schedulerstats.List(client, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/sharedfilesystems/v2/services/doc.go b/openstack/sharedfilesystems/v2/services/doc.go index 243b8e9b73..d7434ee384 100644 --- a/openstack/sharedfilesystems/v2/services/doc.go +++ b/openstack/sharedfilesystems/v2/services/doc.go @@ -4,7 +4,7 @@ OpenStack cloud. Example of Retrieving list of all services - allPages, err := services.List(sharedFileSystemV2, services.ListOpts{}).AllPages() + allPages, err := services.List(sharedFileSystemV2, services.ListOpts{}).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go index 782899b3b0..2607d846ae 100644 --- a/openstack/workflow/v2/crontriggers/doc.go +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -20,7 +20,7 @@ Default Filter checks equality, but you can override it with provided filter typ }, } - allPages, err := crontriggers.List(mistralClient, listOpts).AllPages() + allPages, err := crontriggers.List(mistralClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/workflow/v2/executions/doc.go b/openstack/workflow/v2/executions/doc.go index 1633ff0ddb..69ff258aee 100644 --- a/openstack/workflow/v2/executions/doc.go +++ b/openstack/workflow/v2/executions/doc.go @@ -22,7 +22,7 @@ Default Filter checks equality, but you can override it with provided filter typ }, } - allPages, err := executions.List(mistralClient, listOpts).AllPages() + allPages, err := executions.List(mistralClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } diff --git a/openstack/workflow/v2/workflows/doc.go b/openstack/workflow/v2/workflows/doc.go index 23aa46d97c..888a407e74 100644 --- a/openstack/workflow/v2/workflows/doc.go +++ b/openstack/workflow/v2/workflows/doc.go @@ -12,7 +12,7 @@ List workflows Namespace: "some-namespace", } - allPages, err := workflows.List(mistralClient, listOpts).AllPages() + allPages, err := workflows.List(mistralClient, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } From 87dd24439b39a3bd1fd2ac42fbf5268793606e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Sun, 24 Mar 2024 18:06:48 +0100 Subject: [PATCH 1796/2296] Docs: Add a context for EachPage() calls Used the following command to add a context to EachPage() calls missing one: ``` find . -type f -exec sed -i 's/\.EachPage(func(/\.EachPage(context\.TODO(), func(_ context\.Context, /g' {} \; ``` --- docs/contributor-tutorial/.template/testing/requests_test.go | 2 +- openstack/baremetal/v1/conductors/doc.go | 4 ++-- openstack/baremetal/v1/drivers/doc.go | 2 +- openstack/baremetal/v1/nodes/doc.go | 4 ++-- openstack/baremetal/v1/ports/doc.go | 4 ++-- openstack/baremetalintrospection/v1/introspection/doc.go | 2 +- openstack/clustering/v1/actions/doc.go | 2 +- openstack/clustering/v1/events/doc.go | 2 +- openstack/clustering/v1/profiles/doc.go | 2 +- openstack/clustering/v1/profiletypes/doc.go | 2 +- openstack/clustering/v1/receivers/doc.go | 2 +- openstack/compute/v2/extensions/usage/doc.go | 4 ++-- openstack/messaging/v2/messages/doc.go | 2 +- openstack/messaging/v2/queues/doc.go | 2 +- 14 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/contributor-tutorial/.template/testing/requests_test.go b/docs/contributor-tutorial/.template/testing/requests_test.go index 7040d62670..d89c1b4609 100644 --- a/docs/contributor-tutorial/.template/testing/requests_test.go +++ b/docs/contributor-tutorial/.template/testing/requests_test.go @@ -15,7 +15,7 @@ func TestListResources(t *testing.T) { HandleListResourcesSuccessfully(t) count := 0 - err := resources.List(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + err := resources.List(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := resources.ExtractResources(page) diff --git a/openstack/baremetal/v1/conductors/doc.go b/openstack/baremetal/v1/conductors/doc.go index 904910044c..912abdefbd 100644 --- a/openstack/baremetal/v1/conductors/doc.go +++ b/openstack/baremetal/v1/conductors/doc.go @@ -4,7 +4,7 @@ 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) { + conductors.List(client, conductors.ListOpts{Detail: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { conductorList, err := conductors.ExtractConductors(page) if err != nil { return false, err @@ -23,7 +23,7 @@ Example to List Conductors Fields: []string{"hostname"}, } - conductors.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + conductors.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { conductorList, err := conductors.ExtractConductors(page) if err != nil { return false, err diff --git a/openstack/baremetal/v1/drivers/doc.go b/openstack/baremetal/v1/drivers/doc.go index 252c01b76a..5cb78e23ef 100644 --- a/openstack/baremetal/v1/drivers/doc.go +++ b/openstack/baremetal/v1/drivers/doc.go @@ -6,7 +6,7 @@ API reference: https://developer.openstack.org/api-ref/baremetal/#drivers-driver Example to List Drivers - drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) { + drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { driversList, err := drivers.ExtractDrivers(page) if err != nil { return false, err diff --git a/openstack/baremetal/v1/nodes/doc.go b/openstack/baremetal/v1/nodes/doc.go index 6ee2aff5e4..3fadcb7f03 100644 --- a/openstack/baremetal/v1/nodes/doc.go +++ b/openstack/baremetal/v1/nodes/doc.go @@ -4,7 +4,7 @@ resource in the OpenStack Bare Metal service. Example to List Nodes with Detail - nodes.ListDetail(client, nodes.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + nodes.ListDetail(client, nodes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err @@ -24,7 +24,7 @@ Example to List Nodes Fields: []string{"name"}, } - nodes.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + nodes.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { nodeList, err := nodes.ExtractNodes(page) if err != nil { return false, err diff --git a/openstack/baremetal/v1/ports/doc.go b/openstack/baremetal/v1/ports/doc.go index 46805dd349..f0889a43d5 100644 --- a/openstack/baremetal/v1/ports/doc.go +++ b/openstack/baremetal/v1/ports/doc.go @@ -6,7 +6,7 @@ Example to List Ports with Detail - ports.ListDetail(client, nil).EachPage(func(page pagination.Page) (bool, error) { + ports.ListDetail(client, nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err @@ -25,7 +25,7 @@ Example to List Ports Limit: 10, } - ports.List(client, listOpts).EachPage(func(page pagination.Page) (bool, error) { + ports.List(client, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { portList, err := ports.ExtractPorts(page) if err != nil { return false, err diff --git a/openstack/baremetalintrospection/v1/introspection/doc.go b/openstack/baremetalintrospection/v1/introspection/doc.go index 4238238694..71f4dff801 100644 --- a/openstack/baremetalintrospection/v1/introspection/doc.go +++ b/openstack/baremetalintrospection/v1/introspection/doc.go @@ -22,7 +22,7 @@ Example to Get an Introspection status Example to List all introspection statuses - introspection.ListIntrospections(client.ServiceClient(), nil).EachPage(func(page pagination.Page) (bool, error) { + introspection.ListIntrospections(client.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { introspectionsList, err := introspection.ExtractIntrospections(page) if err != nil { return false, err diff --git a/openstack/clustering/v1/actions/doc.go b/openstack/clustering/v1/actions/doc.go index 8e1f0619b1..467b4c84f3 100644 --- a/openstack/clustering/v1/actions/doc.go +++ b/openstack/clustering/v1/actions/doc.go @@ -8,7 +8,7 @@ Example to List Actions Limit: 5, } - err = actions.List(serviceClient, opts).EachPage(func(page pagination.Page) (bool, error) { + err = actions.List(serviceClient, opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { actionInfos, err := actions.ExtractActions(page) if err != nil { return false, err diff --git a/openstack/clustering/v1/events/doc.go b/openstack/clustering/v1/events/doc.go index eb58d68bc6..bc88cb0913 100644 --- a/openstack/clustering/v1/events/doc.go +++ b/openstack/clustering/v1/events/doc.go @@ -8,7 +8,7 @@ Example to List Events Limit: 5, } - err = events.List(serviceClient, opts).EachPage(func(page pagination.Page) (bool, error) { + err = events.List(serviceClient, opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { eventInfos, err := events.ExtractEvents(page) if err != nil { return false, err diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index 1b8348a379..b37fa9ea42 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -47,7 +47,7 @@ Example to List Profiles Limit: 2, } - profiles.List(serviceClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { + profiles.List(serviceClient, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allProfiles, err := profiles.ExtractProfiles(page) if err != nil { panic(err) diff --git a/openstack/clustering/v1/profiletypes/doc.go b/openstack/clustering/v1/profiletypes/doc.go index f11cbc4f57..2e0e1062ac 100644 --- a/openstack/clustering/v1/profiletypes/doc.go +++ b/openstack/clustering/v1/profiletypes/doc.go @@ -4,7 +4,7 @@ Clustering Service. Example to List ProfileType - err = profiletypes.List(serviceClient).EachPage(func(page pagination.Page) (bool, error) { + err = profiletypes.List(serviceClient).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { profileTypes, err := profiletypes.ExtractProfileTypes(page) if err != nil { return false, err diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index 72f7dc2285..4cb4a7d643 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -57,7 +57,7 @@ Example to List Receivers Limit: 2, } - receivers.List(serviceClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { + receivers.List(serviceClient, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allReceivers, err := receivers.ExtractReceivers(page) if err != nil { panic(err) diff --git a/openstack/compute/v2/extensions/usage/doc.go b/openstack/compute/v2/extensions/usage/doc.go index df9d79e0b9..478ac5a879 100644 --- a/openstack/compute/v2/extensions/usage/doc.go +++ b/openstack/compute/v2/extensions/usage/doc.go @@ -19,7 +19,7 @@ Example to Retrieve Usage for a Single Tenant: End: &end, } - err := usage.SingleTenant(computeClient, tenantID, singleTenantOpts).EachPage(func(page pagination.Page) (bool, error) { + err := usage.SingleTenant(computeClient, tenantID, singleTenantOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { tenantUsage, err := usage.ExtractSingleTenant(page) if err != nil { return false, err @@ -40,7 +40,7 @@ Example to Retrieve Usage for All Tenants: Detailed: true, } - err := usage.AllTenants(computeClient, allTenantsOpts).EachPage(func(page pagination.Page) (bool, error) { + err := usage.AllTenants(computeClient, allTenantsOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allTenantsUsage, err := usage.ExtractAllTenants(page) if err != nil { return false, err diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go index e5f5bce60f..f48beb62c5 100644 --- a/openstack/messaging/v2/messages/doc.go +++ b/openstack/messaging/v2/messages/doc.go @@ -12,7 +12,7 @@ Example to List Messages pager := messages.List(client, queueName, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { allMessages, err := queues.ExtractQueues(page) if err != nil { panic(err) diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index 33092d6be0..ab107eefda 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -12,7 +12,7 @@ Example to List Queues pager := queues.List(client, listOpts) - err = pager.EachPage(func(page pagination.Page) (bool, error) { + err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { queues, err := queues.ExtractQueues(page) if err != nil { panic(err) From 2bf367f8e31324a1933d1037b3bea75274575fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Sun, 24 Mar 2024 10:49:47 +0100 Subject: [PATCH 1797/2296] Docs: Add a context for AuthenticatedClient() calls Used the following commands to add a context to AuthenticatedClient() calls missing one: ``` find . -type f -exec sed -i 's/AuthenticatedClient(ao/\AuthenticatedClient(context\.TODO(), ao/g' {} \; find . -type f -exec sed -i 's/AuthenticatedClient(opts/\AuthenticatedClient(context\.TODO(), opts/g' {} \; ``` --- README.md | 4 ++-- auth_options.go | 4 ++-- doc.go | 6 +++--- openstack/auth_env.go | 2 +- openstack/common/extensions/doc.go | 4 ++-- openstack/containerinfra/v1/nodegroups/doc.go | 2 +- openstack/doc.go | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 76f6ccc651..8bbe5084d1 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ import ( opts := new(clientconfig.ClientOpts) opts.Cloud = "devstack-admin" -provider, err := clientconfig.AuthenticatedClient(opts) +provider, err := clientconfig.AuthenticatedClient(context.TODO(), opts) ``` A provider client is a top-level client that all of your OpenStack service @@ -118,7 +118,7 @@ Once you have the `opts` variable, you can pass it in and get back a `ProviderClient` struct: ```go -provider, err := openstack.AuthenticatedClient(opts) +provider, err := openstack.AuthenticatedClient(context.TODO(), opts) ``` As above, you can then use this provider client to generate a service client diff --git a/auth_options.go b/auth_options.go index 335ce87957..c13cecdc9f 100644 --- a/auth_options.go +++ b/auth_options.go @@ -19,13 +19,13 @@ An example of manually providing authentication information: TenantID: "{tenant_id}", } - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) An example of using AuthOptionsFromEnv(), where the environment variables can be read from a file, such as a standard openrc file: opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) */ type AuthOptions struct { // IdentityEndpoint specifies the HTTP endpoint that is required to work with diff --git a/doc.go b/doc.go index 58eeda0fae..d8a22c671d 100644 --- a/doc.go +++ b/doc.go @@ -29,7 +29,7 @@ specified like so: TenantID: "{tenant_id}", } - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) You can authenticate with a token by doing: @@ -39,7 +39,7 @@ You can authenticate with a token by doing: TenantID: "{tenant_id}", } - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) You may also use the openstack.AuthOptionsFromEnv() helper function. This function reads in standard environment variables frequently found in an @@ -47,7 +47,7 @@ OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant" instead of "project". opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) # Service Clients diff --git a/openstack/auth_env.go b/openstack/auth_env.go index fbabed0a4a..893787b787 100644 --- a/openstack/auth_env.go +++ b/openstack/auth_env.go @@ -31,7 +31,7 @@ To use this function, first set the OS_* environment variables (for example, by sourcing an `openrc` file), then: opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) */ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { authURL := os.Getenv("OS_AUTH_URL") diff --git a/openstack/common/extensions/doc.go b/openstack/common/extensions/doc.go index 979a20e49b..e07dd0f8f0 100644 --- a/openstack/common/extensions/doc.go +++ b/openstack/common/extensions/doc.go @@ -21,7 +21,7 @@ Service Client. Example of Retrieving Compute Extensions ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(ao) + provider, err := openstack.AuthenticatedClient(context.TODO(), ao) computeClient, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) @@ -36,7 +36,7 @@ Example of Retrieving Compute Extensions Example of Retrieving Network Extensions ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(ao) + provider, err := openstack.AuthenticatedClient(context.TODO(), ao) networkClient, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) diff --git a/openstack/containerinfra/v1/nodegroups/doc.go b/openstack/containerinfra/v1/nodegroups/doc.go index e8e7540154..971153246d 100644 --- a/openstack/containerinfra/v1/nodegroups/doc.go +++ b/openstack/containerinfra/v1/nodegroups/doc.go @@ -11,7 +11,7 @@ Create a client to use: panic(err) } - provider, err := openstack.AuthenticatedClient(opts) + provider, err := openstack.AuthenticatedClient(context.TODO(), opts) if err != nil { panic(err) } diff --git a/openstack/doc.go b/openstack/doc.go index af4bd512bf..4d89fb5d0b 100644 --- a/openstack/doc.go +++ b/openstack/doc.go @@ -6,7 +6,7 @@ OpenStack cloud and for provisioning various service-level clients. Example of Creating a Service Client ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(ao) + provider, err := openstack.AuthenticatedClient(context.TODO(), ao) client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) From bf397ec7d385e8e80c4f3a4dea1c33af260cabb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Sun, 24 Mar 2024 17:53:03 +0100 Subject: [PATCH 1798/2296] Docs: Add missing contexts This patch was generated with the following command: ``` for f in $(git show 1b7c1ceb72b30b5dada9993e8113a67fa95ab419 | grep "+func" | cut -d "(" -f 1 | cut -d " " -f 2 | sort | uniq | tail -n +2); do find . -name doc.go -exec sed -i "s/$f(\(\w\?\+[Cc]lient\)/$f(context.TODO(), \1/g" {} \;; done ``` Then manual edits for List functions that shouldn't have been modified. --- README.md | 2 +- doc.go | 6 +-- docs/FAQ.md | 4 +- .../.template/requests.go | 18 ++++---- .../.template/testing/requests_test.go | 9 ++-- openstack/baremetal/apiversions/doc.go | 4 +- openstack/baremetal/v1/conductors/doc.go | 2 +- openstack/baremetal/v1/drivers/doc.go | 6 +-- openstack/baremetal/v1/nodes/doc.go | 28 ++++++------- openstack/baremetal/v1/ports/doc.go | 8 ++-- .../baremetalintrospection/httpbasic/doc.go | 2 +- .../baremetalintrospection/noauth/doc.go | 2 +- .../v1/introspection/doc.go | 6 +-- openstack/blockstorage/apiversions/doc.go | 2 +- .../blockstorage/extensions/backups/doc.go | 12 +++--- .../blockstorage/extensions/limits/doc.go | 2 +- .../blockstorage/extensions/quotasets/doc.go | 10 ++--- .../extensions/schedulerhints/doc.go | 4 +- .../extensions/volumeactions/doc.go | 16 +++---- .../extensions/volumetransfers/doc.go | 6 +-- openstack/blockstorage/v3/attachments/doc.go | 10 ++--- openstack/blockstorage/v3/qos/doc.go | 16 +++---- openstack/blockstorage/v3/snapshots/doc.go | 8 ++-- openstack/blockstorage/v3/volumes/doc.go | 2 +- openstack/blockstorage/v3/volumetypes/doc.go | 28 ++++++------- openstack/clustering/v1/actions/doc.go | 2 +- openstack/clustering/v1/clusters/doc.go | 36 ++++++++-------- openstack/clustering/v1/events/doc.go | 2 +- openstack/clustering/v1/nodes/doc.go | 14 +++---- openstack/clustering/v1/policies/doc.go | 8 ++-- openstack/clustering/v1/policytypes/doc.go | 2 +- openstack/clustering/v1/profiles/doc.go | 10 ++--- openstack/clustering/v1/profiletypes/doc.go | 2 +- openstack/clustering/v1/receivers/doc.go | 10 ++--- openstack/clustering/v1/webhooks/doc.go | 2 +- openstack/compute/apiversions/doc.go | 2 +- .../compute/v2/extensions/aggregates/doc.go | 14 +++---- .../v2/extensions/attachinterfaces/doc.go | 6 +-- .../v2/extensions/bootfromvolume/doc.go | 8 ++-- .../compute/v2/extensions/defsecrules/doc.go | 6 +-- .../compute/v2/extensions/diagnostics/doc.go | 2 +- .../compute/v2/extensions/diskconfig/doc.go | 2 +- .../compute/v2/extensions/evacuate/doc.go | 2 +- .../extendedserverattributes/doc.go | 4 +- .../compute/v2/extensions/floatingips/doc.go | 8 ++-- .../compute/v2/extensions/hypervisors/doc.go | 10 ++--- .../v2/extensions/injectnetworkinfo/doc.go | 2 +- .../v2/extensions/instanceactions/doc.go | 2 +- .../compute/v2/extensions/keypairs/doc.go | 12 +++--- openstack/compute/v2/extensions/limits/doc.go | 2 +- .../compute/v2/extensions/lockunlock/doc.go | 4 +- .../compute/v2/extensions/migrate/doc.go | 4 +- .../compute/v2/extensions/pauseunpause/doc.go | 4 +- .../compute/v2/extensions/quotasets/doc.go | 6 +-- .../v2/extensions/remoteconsoles/doc.go | 2 +- .../v2/extensions/rescueunrescue/doc.go | 4 +- .../compute/v2/extensions/resetnetwork/doc.go | 2 +- .../compute/v2/extensions/resetstate/doc.go | 2 +- .../v2/extensions/schedulerhints/doc.go | 6 +-- .../compute/v2/extensions/secgroups/doc.go | 12 +++--- .../compute/v2/extensions/servergroups/doc.go | 6 +-- .../compute/v2/extensions/serverusage/doc.go | 2 +- .../compute/v2/extensions/services/doc.go | 4 +- .../v2/extensions/shelveunshelve/doc.go | 6 +-- .../compute/v2/extensions/startstop/doc.go | 4 +- .../v2/extensions/suspendresume/doc.go | 4 +- openstack/compute/v2/extensions/tags/doc.go | 12 +++--- .../compute/v2/extensions/volumeattach/doc.go | 4 +- openstack/compute/v2/flavors/doc.go | 16 +++---- openstack/compute/v2/servers/doc.go | 14 +++---- .../containerinfra/v1/certificates/doc.go | 6 +-- openstack/containerinfra/v1/clusters/doc.go | 10 ++--- .../containerinfra/v1/clustertemplates/doc.go | 6 +-- openstack/containerinfra/v1/nodegroups/doc.go | 10 ++--- openstack/containerinfra/v1/quotas/doc.go | 2 +- openstack/dns/v2/recordsets/doc.go | 4 +- openstack/dns/v2/transfer/accept/doc.go | 4 +- openstack/dns/v2/transfer/request/doc.go | 4 +- openstack/dns/v2/zones/doc.go | 4 +- .../identity/v2/extensions/admin/roles/doc.go | 4 +- openstack/identity/v2/tenants/doc.go | 6 +-- openstack/identity/v2/tokens/doc.go | 6 +-- openstack/identity/v2/users/doc.go | 6 +-- openstack/identity/v3/domains/doc.go | 6 +-- openstack/identity/v3/endpoints/doc.go | 6 +-- .../v3/extensions/ec2credentials/doc.go | 2 +- .../identity/v3/extensions/ec2tokens/doc.go | 4 +- .../identity/v3/extensions/federation/doc.go | 8 ++-- .../identity/v3/extensions/oauth1/doc.go | 12 +++--- .../identity/v3/extensions/trusts/doc.go | 8 ++-- openstack/identity/v3/groups/doc.go | 6 +-- openstack/identity/v3/limits/doc.go | 10 ++--- openstack/identity/v3/osinherit/doc.go | 8 ++-- openstack/identity/v3/policies/doc.go | 8 ++-- openstack/identity/v3/projects/doc.go | 8 ++-- openstack/identity/v3/regions/doc.go | 6 +-- openstack/identity/v3/registeredlimits/doc.go | 8 ++-- openstack/identity/v3/roles/doc.go | 10 ++--- openstack/identity/v3/services/doc.go | 6 +-- openstack/identity/v3/tokens/doc.go | 14 +++---- openstack/identity/v3/users/doc.go | 14 +++---- openstack/image/v2/imagedata/doc.go | 6 +-- openstack/image/v2/imageimport/doc.go | 4 +- openstack/image/v2/images/doc.go | 6 +-- openstack/image/v2/members/doc.go | 6 +-- openstack/image/v2/tasks/doc.go | 4 +- openstack/keymanager/v1/acls/doc.go | 8 ++-- openstack/keymanager/v1/containers/doc.go | 8 ++-- openstack/keymanager/v1/orders/doc.go | 4 +- openstack/keymanager/v1/secrets/doc.go | 20 ++++----- openstack/loadbalancer/v2/amphorae/doc.go | 2 +- .../loadbalancer/v2/flavorprofiles/doc.go | 6 +-- openstack/loadbalancer/v2/flavors/doc.go | 6 +-- openstack/loadbalancer/v2/l7policies/doc.go | 16 +++---- openstack/loadbalancer/v2/listeners/doc.go | 8 ++-- .../loadbalancer/v2/loadbalancers/doc.go | 14 +++---- openstack/loadbalancer/v2/monitors/doc.go | 6 +-- openstack/loadbalancer/v2/pools/doc.go | 14 +++---- openstack/loadbalancer/v2/quotas/doc.go | 4 +- openstack/messaging/v2/claims/doc.go | 8 ++-- openstack/messaging/v2/messages/doc.go | 12 +++--- openstack/messaging/v2/queues/doc.go | 42 +++++++++---------- .../networking/v2/extensions/agents/doc.go | 12 +++--- .../v2/extensions/attributestags/doc.go | 14 +++---- .../networking/v2/extensions/bgp/peers/doc.go | 10 ++--- .../v2/extensions/bgp/speakers/doc.go | 18 ++++---- .../networking/v2/extensions/external/doc.go | 2 +- .../v2/extensions/extradhcpopts/doc.go | 6 +-- .../v2/extensions/layer3/addressscopes/doc.go | 8 ++-- .../v2/extensions/layer3/floatingips/doc.go | 8 ++-- .../extensions/layer3/portforwarding/doc.go | 8 ++-- .../v2/extensions/layer3/routers/doc.go | 14 +++---- .../extensions/networkipavailabilities/doc.go | 2 +- .../v2/extensions/portsecurity/doc.go | 10 ++--- .../networking/v2/extensions/provider/doc.go | 2 +- .../v2/extensions/qos/policies/doc.go | 24 +++++------ .../networking/v2/extensions/qos/rules/doc.go | 18 ++++---- .../v2/extensions/qos/ruletypes/doc.go | 2 +- .../networking/v2/extensions/quotas/doc.go | 6 +-- .../v2/extensions/rbacpolicies/doc.go | 8 ++-- .../v2/extensions/security/groups/doc.go | 6 +-- .../v2/extensions/security/rules/doc.go | 4 +- .../v2/extensions/subnetpools/doc.go | 8 ++-- .../v2/extensions/trunk_details/doc.go | 2 +- .../networking/v2/extensions/trunks/doc.go | 18 ++++---- .../v2/extensions/vlantransparent/doc.go | 6 +-- .../extensions/vpnaas/endpointgroups/doc.go | 8 ++-- .../v2/extensions/vpnaas/ikepolicies/doc.go | 8 ++-- .../v2/extensions/vpnaas/ipsecpolicies/doc.go | 8 ++-- .../v2/extensions/vpnaas/services/doc.go | 8 ++-- .../extensions/vpnaas/siteconnections/doc.go | 8 ++-- openstack/networking/v2/networks/doc.go | 6 +-- openstack/networking/v2/ports/doc.go | 6 +-- openstack/networking/v2/subnets/doc.go | 12 +++--- openstack/objectstorage/v1/accounts/doc.go | 4 +- openstack/objectstorage/v1/containers/doc.go | 6 +-- openstack/objectstorage/v1/objects/doc.go | 8 ++-- openstack/objectstorage/v1/swauth/doc.go | 2 +- .../orchestration/v1/resourcetypes/doc.go | 2 +- .../orchestration/v1/stackresources/doc.go | 8 ++-- openstack/orchestration/v1/stacks/doc.go | 12 +++--- .../orchestration/v1/stacktemplates/doc.go | 4 +- .../placement/v1/resourceproviders/doc.go | 16 +++---- .../sharedfilesystems/apiversions/doc.go | 2 +- openstack/sharedfilesystems/v2/shares/doc.go | 8 ++-- openstack/workflow/v2/crontriggers/doc.go | 6 +-- openstack/workflow/v2/executions/doc.go | 6 +-- openstack/workflow/v2/workflows/doc.go | 4 +- 168 files changed, 646 insertions(+), 643 deletions(-) diff --git a/README.md b/README.md index 8bbe5084d1..fcdd4dba8e 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ 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{ +server, err := servers.Create(context.TODO(), client, servers.CreateOpts{ Name: "My new server!", FlavorRef: "flavor_id", ImageRef: "image_id", diff --git a/doc.go b/doc.go index d8a22c671d..a755ecb180 100644 --- a/doc.go +++ b/doc.go @@ -65,7 +65,7 @@ pass in the parent provider, like so: Resource structs are the domain models that services make use of in order to work with and represent the state of API resources: - server, err := servers.Get(client, "{serverId}").Extract() + server, err := servers.Get(context.TODO(), client, "{serverId}").Extract() Intermediate Result structs are returned for API operations, which allow generic access to the HTTP headers, response body, and any errors associated @@ -73,7 +73,7 @@ with the network transaction. To turn a result into a usable resource struct, you must call the Extract method which is chained to the response, or an Extract function from an applicable extension: - result := servers.Get(client, "{serverId}") + result := servers.Get(context.TODO(), client, "{serverId}") // Attempt to extract the disk configuration from the OS-DCF disk config // extension: @@ -85,7 +85,7 @@ Pager to handle each successive Page in a closure, then use the appropriate extraction method from that request's package to interpret that Page as a slice of results: - err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) { + err := servers.List(client, nil).EachPage(context.TODO(), func (_ context.Context, page pagination.Page) (bool, error) { s, err := servers.ExtractServers(page) if err != nil { return false, err diff --git a/docs/FAQ.md b/docs/FAQ.md index 4bc8a8a470..bb37db8019 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -94,7 +94,7 @@ myOpts := MyCreateServerOpts{ Name: "s1", Size: "100", } -server, err := servers.Create(computeClient, myOpts).Extract() +server, err := servers.Create(context.TODO(), computeClient, myOpts).Extract() // ... ``` @@ -114,7 +114,7 @@ var v struct { MyVolume `json:"volume"` } -err := volumes.Get(client, volID).ExtractInto(&v) +err := volumes.Get(context.TODO(), client, volID).ExtractInto(&v) // ... ``` diff --git a/docs/contributor-tutorial/.template/requests.go b/docs/contributor-tutorial/.template/requests.go index 31d1737e8d..ab4af53188 100644 --- a/docs/contributor-tutorial/.template/requests.go +++ b/docs/contributor-tutorial/.template/requests.go @@ -1,6 +1,8 @@ package RESOURCE import ( + "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -37,8 +39,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } // Get retrieves details of a RESOURCE. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(getURL(client, id), &r.Body, nil) +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -59,13 +61,13 @@ func (opts CreateOpts) ToResourceCreateMap() (map[string]interface{}, error) { } // Create creates a new RESOURCE. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToResourceCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, createURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -73,8 +75,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create } // Delete deletes a RESOURCE. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(deleteURL(client, id), nil) +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -95,13 +97,13 @@ func (opts UpdateOpts) ToResourceUpdateMap() (map[string]interface{}, error) { } // Update modifies the attributes of a RESOURCE. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToResourceUpdateMap() if err != nil { r.Err = err return } - resp, err := client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/docs/contributor-tutorial/.template/testing/requests_test.go b/docs/contributor-tutorial/.template/testing/requests_test.go index d89c1b4609..c1766d8624 100644 --- a/docs/contributor-tutorial/.template/testing/requests_test.go +++ b/docs/contributor-tutorial/.template/testing/requests_test.go @@ -1,6 +1,7 @@ package testing import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/openstack/service/vN/resources" @@ -46,7 +47,7 @@ func TestGetResource(t *testing.T) { defer th.TeardownHTTP() HandleGetResourceSuccessfully(t) - actual, err := resources.Get(client.ServiceClient(), "9fe1d3").Extract() + actual, err := resources.Get(context.TODO(), client.ServiceClient(), "9fe1d3").Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondResource, *actual) } @@ -60,7 +61,7 @@ func TestCreateResource(t *testing.T) { Name: "resource two", } - actual, err := resources.Create(client.ServiceClient(), createOpts).Extract() + actual, err := resources.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondResource, *actual) } @@ -70,7 +71,7 @@ func TestDeleteResource(t *testing.T) { defer th.TeardownHTTP() HandleDeleteResourceSuccessfully(t) - res := resources.Delete(client.ServiceClient(), "9fe1d3") + res := resources.Delete(context.TODO(), client.ServiceClient(), "9fe1d3") th.AssertNoErr(t, res.Err) } @@ -83,7 +84,7 @@ func TestUpdateResource(t *testing.T) { Description: "Staging Resource", } - actual, err := resources.Update(client.ServiceClient(), "9fe1d3", updateOpts).Extract() + actual, err := resources.Update(context.TODO(), client.ServiceClient(), "9fe1d3", updateOpts).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, SecondResourceUpdated, *actual) } diff --git a/openstack/baremetal/apiversions/doc.go b/openstack/baremetal/apiversions/doc.go index db8c62b974..439b662ddb 100644 --- a/openstack/baremetal/apiversions/doc.go +++ b/openstack/baremetal/apiversions/doc.go @@ -3,7 +3,7 @@ Package apiversions provides information about the versions supported by a speci Example to list versions - allVersions, err := apiversions.List(baremetalClient).Extract() + allVersions, err := apiversions.List(context.TODO(), baremetalClient).Extract() if err != nil { panic("unable to get API versions: " + err.Error()) } @@ -14,7 +14,7 @@ Package apiversions provides information about the versions supported by a speci Example to get a specific version - actual, err := apiversions.Get(baremetalClient).Extract() + actual, err := apiversions.Get(context.TODO(), baremetalClient).Extract() if err != nil { panic("unable to get API version: " + err.Error()) } diff --git a/openstack/baremetal/v1/conductors/doc.go b/openstack/baremetal/v1/conductors/doc.go index 912abdefbd..dab9d78aa0 100644 --- a/openstack/baremetal/v1/conductors/doc.go +++ b/openstack/baremetal/v1/conductors/doc.go @@ -38,7 +38,7 @@ Example to List Conductors Example to Get Conductor - showConductor, err := conductors.Get(client, "compute2.localdomain").Extract() + showConductor, err := conductors.Get(context.TODO(), client, "compute2.localdomain").Extract() if err != nil { panic(err) } diff --git a/openstack/baremetal/v1/drivers/doc.go b/openstack/baremetal/v1/drivers/doc.go index 5cb78e23ef..9b248e012d 100644 --- a/openstack/baremetal/v1/drivers/doc.go +++ b/openstack/baremetal/v1/drivers/doc.go @@ -21,21 +21,21 @@ Example to List Drivers Example to Get single Driver Details - showDriverDetails, err := drivers.GetDriverDetails(client, "ipmi").Extract() + showDriverDetails, err := drivers.GetDriverDetails(context.TODO(), client, "ipmi").Extract() if err != nil { panic(err) } Example to Get single Driver Properties - showDriverProperties, err := drivers.GetDriverProperties(client, "ipmi").Extract() + showDriverProperties, err := drivers.GetDriverProperties(context.TODO(), client, "ipmi").Extract() if err != nil { panic(err) } Example to Get single Driver Logical Disk Properties - showDriverDiskProperties, err := drivers.GetDriverDiskProperties(client, "ipmi").Extract() + showDriverDiskProperties, err := drivers.GetDriverDiskProperties(context.TODO(), client, "ipmi").Extract() if err != nil { panic(err) } diff --git a/openstack/baremetal/v1/nodes/doc.go b/openstack/baremetal/v1/nodes/doc.go index 3fadcb7f03..f463e13d24 100644 --- a/openstack/baremetal/v1/nodes/doc.go +++ b/openstack/baremetal/v1/nodes/doc.go @@ -53,14 +53,14 @@ Example to Create Node }, } - createNode, err := nodes.Create(client, createOpts).Extract() + createNode, err := nodes.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } Example to Get Node - showNode, err := nodes.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() + showNode, err := nodes.Get(context.TODO(), client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() if err != nil { panic(err) } @@ -75,35 +75,35 @@ Example to Update Node }, } - updateNode, err := nodes.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() + updateNode, err := nodes.Update(context.TODO(), client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() if err != nil { panic(err) } Example to Delete Node - err = nodes.Delete(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() + err = nodes.Delete(context.TODO(), client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() if err != nil { panic(err) } Example to Validate Node - validation, err := nodes.Validate(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + validation, err := nodes.Validate(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() if err != nil { panic(err) } Example to inject non-masking interrupts - err := nodes.InjectNMI(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").ExtractErr() + err := nodes.InjectNMI(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").ExtractErr() if err != nil { panic(err) } Example to get array of supported boot devices for a node - bootDevices, err := nodes.GetSupportedBootDevices(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + bootDevices, err := nodes.GetSupportedBootDevices(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() if err != nil { panic(err) } @@ -115,21 +115,21 @@ Example to set boot device for a node Persistent: false, } - err := nodes.SetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", bootOpts).ExtractErr() + err := nodes.SetBootDevice(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", bootOpts).ExtractErr() if err != nil { panic(err) } Example to get boot device for a node - bootDevice, err := nodes.GetBootDevice(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + bootDevice, err := nodes.GetBootDevice(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() if err != nil { panic(err) } Example to list all vendor passthru methods - methods, err := nodes.GetVendorPassthruMethods(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() + methods, err := nodes.GetVendorPassthruMethods(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8").Extract() if err != nil { panic(err) } @@ -139,7 +139,7 @@ Example to list all subscriptions method := nodes.CallVendorPassthruOpts{ Method: "get_all_subscriptions", } - allSubscriptions, err := nodes.GetAllSubscriptions(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method).Extract() + allSubscriptions, err := nodes.GetAllSubscriptions(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method).Extract() if err != nil { panic(err) } @@ -153,7 +153,7 @@ Example to get a subscription Id: "subscription id", } - subscription, err := nodes.GetSubscription(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionOpt).Extract() + subscription, err := nodes.GetSubscription(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionOpt).Extract() if err != nil { panic(err) } @@ -167,7 +167,7 @@ Example to delete a subscription Id: "subscription id", } - err := nodes.DeleteSubscription(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionDeleteOpt).ExtractErr() + err := nodes.DeleteSubscription(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionDeleteOpt).ExtractErr() if err != nil { panic(err) } @@ -185,7 +185,7 @@ Example to create a subscription HttpHeaders: [{"Key1":"Value1"}, {"Key2":"Value2"}], } - newSubscription, err := nodes.CreateSubscription(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionCreateOpt).Extract() + newSubscription, err := nodes.CreateSubscription(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8", method, subscriptionCreateOpt).Extract() if err != nil { panic(err) } diff --git a/openstack/baremetal/v1/ports/doc.go b/openstack/baremetal/v1/ports/doc.go index f0889a43d5..b1e4af2448 100644 --- a/openstack/baremetal/v1/ports/doc.go +++ b/openstack/baremetal/v1/ports/doc.go @@ -46,14 +46,14 @@ Example to Create a Port PhysicalNetwork: "my-network", } - createPort, err := ports.Create(client, createOpts).Extract() + createPort, err := ports.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } Example to Get a Port - showPort, err := ports.Get(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() + showPort, err := ports.Get(context.TODO(), client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").Extract() if err != nil { panic(err) } @@ -68,14 +68,14 @@ Example to Update a Port }, } - updatePort, err := ports.Update(client, "c9afd385-5d89-4ecb-9e1c-68194da6b474", updateOpts).Extract() + updatePort, err := ports.Update(context.TODO(), 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() + err = ports.Delete(context.TODO(), client, "c9afd385-5d89-4ecb-9e1c-68194da6b474").ExtractErr() if err != nil { panic(err) } diff --git a/openstack/baremetalintrospection/httpbasic/doc.go b/openstack/baremetalintrospection/httpbasic/doc.go index f69369e129..94df43536c 100644 --- a/openstack/baremetalintrospection/httpbasic/doc.go +++ b/openstack/baremetalintrospection/httpbasic/doc.go @@ -12,6 +12,6 @@ Example of obtaining and using a client: panic(err) } - introspection.GetIntrospectionStatus(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") + introspection.GetIntrospectionStatus(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") */ package httpbasic diff --git a/openstack/baremetalintrospection/noauth/doc.go b/openstack/baremetalintrospection/noauth/doc.go index 014180c28b..3642eba07b 100644 --- a/openstack/baremetalintrospection/noauth/doc.go +++ b/openstack/baremetalintrospection/noauth/doc.go @@ -10,6 +10,6 @@ Example of obtaining and using a client: panic(err) } - introspection.GetIntrospectionStatus(client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") + introspection.GetIntrospectionStatus(context.TODO(), client, "a62b8495-52e2-407b-b3cb-62775d04c2b8") */ package noauth diff --git a/openstack/baremetalintrospection/v1/introspection/doc.go b/openstack/baremetalintrospection/v1/introspection/doc.go index 71f4dff801..9b022894ef 100644 --- a/openstack/baremetalintrospection/v1/introspection/doc.go +++ b/openstack/baremetalintrospection/v1/introspection/doc.go @@ -8,14 +8,14 @@ API reference https://developer.openstack.org/api-ref/baremetal-introspection/#n Example to Start Introspection - err := introspection.StartIntrospection(client, NodeUUID, introspection.StartOpts{}).ExtractErr() + err := introspection.StartIntrospection(context.TODO(), client, NodeUUID, introspection.StartOpts{}).ExtractErr() if err != nil { panic(err) } Example to Get an Introspection status - _, err := introspection.GetIntrospectionStatus(client, NodeUUID).Extract() + _, err := introspection.GetIntrospectionStatus(context.TODO(), client, NodeUUID).Extract() if err != nil { panic(err) } @@ -37,7 +37,7 @@ Example to List all introspection statuses Example to Abort an Introspection - err := introspection.AbortIntrospection(client, NodeUUID).ExtractErr() + err := introspection.AbortIntrospection(context.TODO(), client, NodeUUID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/blockstorage/apiversions/doc.go b/openstack/blockstorage/apiversions/doc.go index 8ee7c9ae24..d705559ac1 100644 --- a/openstack/blockstorage/apiversions/doc.go +++ b/openstack/blockstorage/apiversions/doc.go @@ -20,7 +20,7 @@ Example of Retrieving all API Versions Example of Retrieving an API Version - version, err := apiversions.Get(client, "v3").Extract() + version, err := apiversions.Get(context.TODO(), client, "v3").Extract() if err != nil { panic("unable to get API version: " + err.Error()) } diff --git a/openstack/blockstorage/extensions/backups/doc.go b/openstack/blockstorage/extensions/backups/doc.go index 04f3477168..fdfd945fa0 100644 --- a/openstack/blockstorage/extensions/backups/doc.go +++ b/openstack/blockstorage/extensions/backups/doc.go @@ -31,7 +31,7 @@ Example to Create a Backup Name: "my-backup", } - backup, err := backups.Create(client, createOpts).Extract() + backup, err := backups.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Update a Backup Name: "new-name", } - backup, err := backups.Update(client, "uuid", updateOpts).Extract() + backup, err := backups.Update(context.TODO(), client, "uuid", updateOpts).Extract() if err != nil { panic(err) } @@ -58,7 +58,7 @@ Example to Restore a Backup to a Volume Name: "vol-001", } - restore, err := backups.RestoreFromBackup(client, "uuid", options).Extract() + restore, err := backups.RestoreFromBackup(context.TODO(), client, "uuid", options).Extract() if err != nil { panic(err) } @@ -67,14 +67,14 @@ Example to Restore a Backup to a Volume Example to Delete a Backup - err := backups.Delete(client, "uuid").ExtractErr() + err := backups.Delete(context.TODO(), client, "uuid").ExtractErr() if err != nil { panic(err) } Example to Export a Backup - export, err := backups.Export(client, "uuid").Extract() + export, err := backups.Export(context.TODO(), client, "uuid").Extract() if err != nil { panic(err) } @@ -114,7 +114,7 @@ Example to Import a Backup BackupURL: backupURL, } - backup, err := backups.Import(client, options).Extract() + backup, err := backups.Import(context.TODO(), client, options).Extract() if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/limits/doc.go b/openstack/blockstorage/extensions/limits/doc.go index d331058847..2183bd80a6 100644 --- a/openstack/blockstorage/extensions/limits/doc.go +++ b/openstack/blockstorage/extensions/limits/doc.go @@ -3,7 +3,7 @@ Package limits shows rate and limit information for a project you authorized for Example to Retrieve Limits - limits, err := limits.Get(blockStorageClient).Extract() + limits, err := limits.Get(context.TODO(), blockStorageClient).Extract() if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/quotasets/doc.go b/openstack/blockstorage/extensions/quotasets/doc.go index a60f953d0b..29964f0a7b 100644 --- a/openstack/blockstorage/extensions/quotasets/doc.go +++ b/openstack/blockstorage/extensions/quotasets/doc.go @@ -3,7 +3,7 @@ Package quotasets enables retrieving and managing Block Storage quotas. Example to Get a Quota Set - quotaset, err := quotasets.Get(blockStorageClient, "project-id").Extract() + quotaset, err := quotasets.Get(context.TODO(), blockStorageClient, "project-id").Extract() if err != nil { panic(err) } @@ -12,7 +12,7 @@ Example to Get a Quota Set Example to Get Quota Set Usage - quotaset, err := quotasets.GetUsage(blockStorageClient, "project-id").Extract() + quotaset, err := quotasets.GetUsage(context.TODO(), blockStorageClient, "project-id").Extract() if err != nil { panic(err) } @@ -25,7 +25,7 @@ Example to Update a Quota Set Volumes: gophercloud.IntToPointer(100), } - quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() + quotaset, err := quotasets.Update(context.TODO(), blockStorageClient, "project-id", updateOpts).Extract() if err != nil { panic(err) } @@ -43,7 +43,7 @@ Example to Update a Quota set with volume_type quotas }, } - quotaset, err := quotasets.Update(blockStorageClient, "project-id", updateOpts).Extract() + quotaset, err := quotasets.Update(context.TODO(), blockStorageClient, "project-id", updateOpts).Extract() if err != nil { panic(err) } @@ -52,7 +52,7 @@ Example to Update a Quota set with volume_type quotas Example to Delete a Quota Set - err := quotasets.Delete(blockStorageClient, "project-id").ExtractErr() + err := quotasets.Delete(context.TODO(), blockStorageClient, "project-id").ExtractErr() if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/schedulerhints/doc.go b/openstack/blockstorage/extensions/schedulerhints/doc.go index 3a0d451b65..ee5cee01f1 100644 --- a/openstack/blockstorage/extensions/schedulerhints/doc.go +++ b/openstack/blockstorage/extensions/schedulerhints/doc.go @@ -21,7 +21,7 @@ Example to Place Volume B on a Different Host than Volume A SchedulerHints: schedulerHints, } - volume, err := volumes.Create(computeClient, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Place Volume B on the Same Host as Volume A SchedulerHints: schedulerHints, } - volume, err := volumes.Create(computeClient, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/volumeactions/doc.go b/openstack/blockstorage/extensions/volumeactions/doc.go index 34db834f7c..8d9d3f3b67 100644 --- a/openstack/blockstorage/extensions/volumeactions/doc.go +++ b/openstack/blockstorage/extensions/volumeactions/doc.go @@ -11,7 +11,7 @@ Example of Attaching a Volume to an Instance InstanceUUID: server.ID, } - err := volumeactions.Attach(client, volume.ID, attachOpts).ExtractErr() + err := volumeactions.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr() if err != nil { panic(err) } @@ -20,7 +20,7 @@ Example of Attaching a Volume to an Instance AttachmentID: volume.Attachments[0].AttachmentID, } - err = volumeactions.Detach(client, volume.ID, detachOpts).ExtractErr() + err = volumeactions.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr() if err != nil { panic(err) } @@ -32,7 +32,7 @@ Example of Creating an Image from a Volume Force: true, } - volumeImage, err := volumeactions.UploadImage(client, volume.ID, uploadImageOpts).Extract() + volumeImage, err := volumeactions.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() if err != nil { panic(err) } @@ -45,7 +45,7 @@ Example of Extending a Volume's Size NewSize: 100, } - err := volumeactions.ExtendSize(client, volume.ID, extendOpts).ExtractErr() + err := volumeactions.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() if err != nil { panic(err) } @@ -61,7 +61,7 @@ Example of Initializing a Volume Connection OSType: "linux2", } - connectionInfo, err := volumeactions.InitializeConnection(client, volume.ID, connectOpts).Extract() + connectionInfo, err := volumeactions.InitializeConnection(context.TODO(), client, volume.ID, connectOpts).Extract() if err != nil { panic(err) } @@ -77,7 +77,7 @@ Example of Initializing a Volume Connection OSType: "linux2", } - err = volumeactions.TerminateConnection(client, volume.ID, terminateOpts).ExtractErr() + err = volumeactions.TerminateConnection(context.TODO(), client, volume.ID, terminateOpts).ExtractErr() if err != nil { panic(err) } @@ -88,7 +88,7 @@ Example of Setting a Volume's Bootable status Bootable: true, } - err := volumeactions.SetBootable(client, volume.ID, options).ExtractErr() + err := volumeactions.SetBootable(context.TODO(), client, volume.ID, options).ExtractErr() if err != nil { panic(err) } @@ -100,7 +100,7 @@ Example of Changing Type of a Volume MigrationPolicy: volumeactions.MigrationPolicyOnDemand, } - err = volumeactions.ChangeType(client, volumeID, changeTypeOpts).ExtractErr() + err = volumeactions.ChangeType(context.TODO(), client, volumeID, changeTypeOpts).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/blockstorage/extensions/volumetransfers/doc.go b/openstack/blockstorage/extensions/volumetransfers/doc.go index d25c82abc6..af85ce3e2a 100644 --- a/openstack/blockstorage/extensions/volumetransfers/doc.go +++ b/openstack/blockstorage/extensions/volumetransfers/doc.go @@ -31,7 +31,7 @@ Example to Create a Volume Transfer request Name: "my-volume-transfer", } - transfer, err := volumetransfers.Create(client, createOpts).Extract() + transfer, err := volumetransfers.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -48,7 +48,7 @@ Example to Accept a Volume Transfer request from the target project } // see the transfer ID from the create response above - transfer, err := volumetransfers.Accept(client, "transfer-uuid", acceptOpts).Extract() + transfer, err := volumetransfers.Accept(context.TODO(), client, "transfer-uuid", acceptOpts).Extract() if err != nil { panic(err) } @@ -57,7 +57,7 @@ Example to Accept a Volume Transfer request from the target project Example to Delete a Volume Transfer request from the source project - err := volumetransfers.Delete(client, "transfer-uuid").ExtractErr() + err := volumetransfers.Delete(context.TODO(), client, "transfer-uuid").ExtractErr() if err != nil { panic(err) } diff --git a/openstack/blockstorage/v3/attachments/doc.go b/openstack/blockstorage/v3/attachments/doc.go index 68b8041f2c..c2d58cea43 100644 --- a/openstack/blockstorage/v3/attachments/doc.go +++ b/openstack/blockstorage/v3/attachments/doc.go @@ -34,7 +34,7 @@ Example to Create Attachment } client.Microversion = "3.27" - attachment, err := attachments.Create(client, createOpts).Extract() + attachment, err := attachments.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Create Attachment Example to Get Attachment client.Microversion = "3.27" - attachment, err := attachments.Get(client, "uuid").Extract() + attachment, err := attachments.Get(context.TODO(), client, "uuid").Extract() if err != nil { panic(err) } @@ -60,7 +60,7 @@ Example to Update Attachment } client.Microversion = "3.27" - attachment, err := attachments.Update(client, "uuid", opts).Extract() + attachment, err := attachments.Update(context.TODO(), client, "uuid", opts).Extract() if err != nil { panic(err) } @@ -70,7 +70,7 @@ Example to Update Attachment Example to Complete Attachment client.Microversion = "3.44" - err := attachments.Complete(client, "uuid").ExtractErr() + err := attachments.Complete(context.TODO(), client, "uuid").ExtractErr() if err != nil { panic(err) } @@ -78,7 +78,7 @@ Example to Complete Attachment Example to Delete Attachment client.Microversion = "3.27" - err := attachments.Delete(client, "uuid").ExtractErr() + err := attachments.Delete(context.TODO(), client, "uuid").ExtractErr() if err != nil { panic(err) } diff --git a/openstack/blockstorage/v3/qos/doc.go b/openstack/blockstorage/v3/qos/doc.go index 141fb9700f..9f9a9a396c 100644 --- a/openstack/blockstorage/v3/qos/doc.go +++ b/openstack/blockstorage/v3/qos/doc.go @@ -12,7 +12,7 @@ Example to create a QoS specification }, } - test, err := qos.Create(client, createOpts).Extract() + test, err := qos.Create(context.TODO(), client, createOpts).Extract() if err != nil { log.Fatal(err) } @@ -27,7 +27,7 @@ Example to delete a QoS specification Force: false, } - err = qos.Delete(client, qosID, deleteOpts).ExtractErr() + err = qos.Delete(context.TODO(), client, qosID, deleteOpts).ExtractErr() if err != nil { log.Fatal(err) } @@ -54,7 +54,7 @@ Example to get a single QoS specification qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - singleQos, err := qos.Get(client, test.ID).Extract() + singleQos, err := qos.Get(context.TODO(), client, test.ID).Extract() if err != nil { panic(err) } @@ -72,7 +72,7 @@ Example of updating QoSSpec }, } - specs, err := qos.Update(client, qosID, updateOpts).Extract() + specs, err := qos.Update(context.TODO(), client, qosID, updateOpts).Extract() if err != nil { panic(err) } @@ -83,7 +83,7 @@ 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() + err = qos.DeleteKeys(context.TODO(), client, qosID, keysToDelete).ExtractErr() if err != nil { panic(err) } @@ -97,7 +97,7 @@ Example of associating a QoS with a volume type VolumeTypeID: volID, } - err = qos.Associate(client, qosID, associateOpts).ExtractErr() + err = qos.Associate(context.TODO(), client, qosID, associateOpts).ExtractErr() if err != nil { panic(err) } @@ -111,7 +111,7 @@ Example of disassociating a QoS from a volume type VolumeTypeID: volID, } - err = qos.Disassociate(client, qosID, disassociateOpts).ExtractErr() + err = qos.Disassociate(context.TODO(), client, qosID, disassociateOpts).ExtractErr() if err != nil { panic(err) } @@ -120,7 +120,7 @@ Example of disaassociating a Qos from all volume types qosID := "de075d5e-8afc-4e23-9388-b84a5183d1c0" - err = qos.DisassociateAll(client, qosID).ExtractErr() + err = qos.DisassociateAll(context.TODO(), client, qosID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/blockstorage/v3/snapshots/doc.go b/openstack/blockstorage/v3/snapshots/doc.go index 45a7392ecf..6794be12ec 100644 --- a/openstack/blockstorage/v3/snapshots/doc.go +++ b/openstack/blockstorage/v3/snapshots/doc.go @@ -21,7 +21,7 @@ Example to list Snapshots Example to get a Snapshot snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" - snapshot, err := snapshots.Get(client, snapshotID).Extract() + snapshot, err := snapshots.Get(context.TODO(), client, snapshotID).Extract() if err != nil{ panic(err) } @@ -29,7 +29,7 @@ Example to get a Snapshot Example to create a Snapshot - snapshot, err := snapshots.Create(client, snapshots.CreateOpts{ + snapshot, err := snapshots.Create(context.TODO(), client, snapshots.CreateOpts{ Name:"snapshot_001", VolumeID:"5aa119a8-d25b-45a7-8d1b-88e127885635", }).Extract() @@ -41,7 +41,7 @@ Example to create a Snapshot Example to delete a Snapshot snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" - err := snapshots.Delete(client, snapshotID).ExtractErr() + err := snapshots.Delete(context.TODO(), client, snapshotID).ExtractErr() if err != nil{ panic(err) } @@ -49,7 +49,7 @@ Example to delete a Snapshot Example to update a Snapshot snapshotID := "4a584cae-e4ce-429b-9154-d4c9eb8fda4c" - snapshot, err = snapshots.Update(client, snapshotID, snapshots.UpdateOpts{ + snapshot, err = snapshots.Update(context.TODO(), client, snapshotID, snapshots.UpdateOpts{ Name: "snapshot_002", Description:"description_002", }).Extract() diff --git a/openstack/blockstorage/v3/volumes/doc.go b/openstack/blockstorage/v3/volumes/doc.go index 0b834852dd..18889d0015 100644 --- a/openstack/blockstorage/v3/volumes/doc.go +++ b/openstack/blockstorage/v3/volumes/doc.go @@ -13,7 +13,7 @@ Example to create a Volume from a Backup } client.Microversion = "3.47" - volume, err := volumes.Create(client, options).Extract() + volume, err := volumes.Create(context.TODO(), client, options).Extract() if err != nil { panic(err) } diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index ed5022243a..a0d6fa20b2 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -20,7 +20,7 @@ Example to list Volume Types Example to show a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - volumeType, err := volumetypes.Get(client, typeID).Extract() + volumeType, err := volumetypes.Get(context.TODO(), client, typeID).Extract() if err != nil{ panic(err) } @@ -28,7 +28,7 @@ Example to show a Volume Type Example to create a Volume Type - volumeType, err := volumetypes.Create(client, volumetypes.CreateOpts{ + volumeType, err := volumetypes.Create(context.TODO(), client, volumetypes.CreateOpts{ Name:"volume_type_001", IsPublic:true, Description:"description_001", @@ -41,7 +41,7 @@ Example to create a Volume Type Example to delete a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - err := volumetypes.Delete(client, typeID).ExtractErr() + err := volumetypes.Delete(context.TODO(), client, typeID).ExtractErr() if err != nil{ panic(err) } @@ -49,7 +49,7 @@ Example to delete a Volume Type Example to update a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - volumetype, err = volumetypes.Update(client, typeID, volumetypes.UpdateOpts{ + volumetype, err = volumetypes.Update(context.TODO(), client, typeID, volumetypes.UpdateOpts{ Name: "volume_type_002", Description:"description_002", IsPublic:false, @@ -66,7 +66,7 @@ Example to Create Extra Specs for a Volume Type createOpts := volumetypes.ExtraSpecsOpts{ "capabilities": "gpu", } - createdExtraSpecs, err := volumetypes.CreateExtraSpecs(client, typeID, createOpts).Extract() + createdExtraSpecs, err := volumetypes.CreateExtraSpecs(context.TODO(), client, typeID, createOpts).Extract() if err != nil { panic(err) } @@ -77,7 +77,7 @@ Example to Get Extra Specs for a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - extraSpecs, err := volumetypes.ListExtraSpecs(client, typeID).Extract() + extraSpecs, err := volumetypes.ListExtraSpecs(context.TODO(), client, typeID).Extract() if err != nil { panic(err) } @@ -88,7 +88,7 @@ Example to Get specific Extra Spec for a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - extraSpec, err := volumetypes.GetExtraSpec(client, typeID, "capabilities").Extract() + extraSpec, err := volumetypes.GetExtraSpec(context.TODO(), client, typeID, "capabilities").Extract() if err != nil { panic(err) } @@ -102,7 +102,7 @@ Example to Update Extra Specs for a Volume Type updateOpts := volumetypes.ExtraSpecsOpts{ "capabilities": "capabilities-updated", } - updatedExtraSpec, err := volumetypes.UpdateExtraSpec(client, typeID, updateOpts).Extract() + updatedExtraSpec, err := volumetypes.UpdateExtraSpec(context.TODO(), client, typeID, updateOpts).Extract() if err != nil { panic(err) } @@ -112,7 +112,7 @@ Example to Update Extra Specs for a Volume Type Example to Delete an Extra Spec for a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - err := volumetypes.DeleteExtraSpec(client, typeID, "capabilities").ExtractErr() + err := volumetypes.DeleteExtraSpec(context.TODO(), client, typeID, "capabilities").ExtractErr() if err != nil { panic(err) } @@ -143,7 +143,7 @@ Example to Grant Access to a Volume Type Project: "15153a0979884b59b0592248ef947921", } - err := volumetypes.AddAccess(client, typeID, accessOpts).ExtractErr() + err := volumetypes.AddAccess(context.TODO(), client, typeID, accessOpts).ExtractErr() if err != nil { panic(err) } @@ -156,7 +156,7 @@ Example to Remove/Revoke Access to a Volume Type Project: "15153a0979884b59b0592248ef947921", } - err := volumetypes.RemoveAccess(client, typeID, accessOpts).ExtractErr() + err := volumetypes.RemoveAccess(context.TODO(), client, typeID, accessOpts).ExtractErr() if err != nil { panic(err) } @@ -164,7 +164,7 @@ 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{ + volumeType, err := volumetypes.CreateEncryption(context.TODO(), client, typeID, .CreateEncryptionOpts{ KeySize: 256, Provider: "luks", ControlLocation: "front-end", @@ -179,7 +179,7 @@ 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() + err := volumetypes.DeleteEncryption(context.TODO(), client, typeID, encryptionID).ExtractErr() if err != nil{ panic(err) } @@ -187,7 +187,7 @@ Example to Delete the Encryption of a Volume Type Example to Update the Encryption of a Volume Type typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" - volumetype, err = volumetypes.UpdateEncryption(client, typeID, volumetypes.UpdateEncryptionOpts{ + volumetype, err = volumetypes.UpdateEncryption(context.TODO(), client, typeID, volumetypes.UpdateEncryptionOpts{ KeySize: 256, Provider: "luks", ControlLocation: "front-end", diff --git a/openstack/clustering/v1/actions/doc.go b/openstack/clustering/v1/actions/doc.go index 467b4c84f3..34d2fb7034 100644 --- a/openstack/clustering/v1/actions/doc.go +++ b/openstack/clustering/v1/actions/doc.go @@ -23,7 +23,7 @@ Example to List Actions Example to Get an Action actionID := "edce3528-864f-41fb-8759-f4707925cc09" - action, err := actions.Get(serviceClient, actionID).Extract() + action, err := actions.Get(context.TODO(), serviceClient, actionID).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go index e6f48ded73..067c35aefe 100644 --- a/openstack/clustering/v1/clusters/doc.go +++ b/openstack/clustering/v1/clusters/doc.go @@ -10,7 +10,7 @@ Example to Create a Cluster ProfileID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", } - cluster, err := clusters.Create(serviceClient, createOpts).Extract() + cluster, err := clusters.Create(context.TODO(), serviceClient, createOpts).Extract() if err != nil { panic(err) } @@ -18,7 +18,7 @@ Example to Create a Cluster Example to Get a Cluster clusterName := "cluster123" - cluster, err := clusters.Get(serviceClient, clusterName).Extract() + cluster, err := clusters.Get(context.TODO(), serviceClient, clusterName).Extract() if err != nil { panic(err) } @@ -52,7 +52,7 @@ Example to Update a Cluster } clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" - cluster, err := clusters.Update(serviceClient, clusterName, opts).Extract() + cluster, err := clusters.Update(context.TODO(), serviceClient, clusterName, opts).Extract() if err != nil { panic(err) } @@ -61,7 +61,7 @@ Example to Update a Cluster Example to Delete a Cluster clusterID := "dc6d336e3fc4c0a951b5698cd1236ee" - err := clusters.Delete(serviceClient, clusterID).ExtractErr() + err := clusters.Delete(context.TODO(), serviceClient, clusterID).ExtractErr() if err != nil { panic(err) } @@ -83,7 +83,7 @@ Example to Resize a Cluster Strict: &strict, } - actionID, err := clusters.Resize(client, clusterName, resizeOpts).Extract() + actionID, err := clusters.Resize(context.TODO(), client, clusterName, resizeOpts).Extract() if err != nil { t.Fatalf("Unable to resize cluster: %v", err) } @@ -97,7 +97,7 @@ Example to ScaleIn a Cluster } clusterID: "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - action, err := clusters.ScaleIn(computeClient, clusterID, scaleInOpts).Extract() + action, err := clusters.ScaleIn(context.TODO(), computeClient, clusterID, scaleInOpts).Extract() if err != nil { panic(err) } @@ -109,7 +109,7 @@ Example to ScaleOut a cluster } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.ScaleOut(computeClient, clusterID, scaleOutOpts).Extract() + actionID, err := clusters.ScaleOut(context.TODO(), computeClient, clusterID, scaleOutOpts).Extract() if err != nil { panic(err) } @@ -151,7 +151,7 @@ Example to Attach a Policy to a Cluster } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.AttachPolicy(serviceClient, clusterID, attachPolicyOpts).Extract() + actionID, err := clusters.AttachPolicy(context.TODO(), serviceClient, clusterID, attachPolicyOpts).Extract() if err != nil { panic(err) } @@ -165,7 +165,7 @@ Example to Detach a Policy to Cluster } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.DetachPolicy(serviceClient, clusterID, detachpolicyOpts).Extract() + actionID, err := clusters.DetachPolicy(context.TODO(), serviceClient, clusterID, detachpolicyOpts).Extract() if err != nil { panic(err) } @@ -181,7 +181,7 @@ Example to Update a Policy to a Cluster } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.UpdatePolicy(serviceClient, clusterID, updatePolicyOpts).Extract() + actionID, err := clusters.UpdatePolicy(context.TODO(), serviceClient, clusterID, updatePolicyOpts).Extract() if err != nil { panic(err) } @@ -199,7 +199,7 @@ Example to Recover a Cluster } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.Recover(computeClient, clusterID, recoverOpts).Extract() + actionID, err := clusters.Recover(context.TODO(), computeClient, clusterID, recoverOpts).Extract() if err != nil { panic(err) } @@ -208,7 +208,7 @@ Example to Recover a Cluster Example to Check a Cluster clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - action, err := clusters.Check(computeClient, clusterID).Extract() + action, err := clusters.Check(context.TODO(), computeClient, clusterID).Extract() if err != nil { panic(err) } @@ -218,7 +218,7 @@ Example to Complete Life Cycle clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" lifecycleOpts := clusters.CompleteLifecycleOpts{LifecycleActionTokenID: "2b827124-69e1-496e-9484-33ca769fe4df"} - action, err := clusters.CompleteLifecycle(computeClient, clusterID, lifecycleOpts).Extract() + action, err := clusters.CompleteLifecycle(context.TODO(), computeClient, clusterID, lifecycleOpts).Extract() if err != nil { panic(err) } @@ -229,7 +229,7 @@ Example to add nodes to a cluster Nodes: []string{"node-123"}, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.AddNodes(serviceClient, clusterID, addNodesOpts).Extract() + actionID, err := clusters.AddNodes(context.TODO(), serviceClient, clusterID, addNodesOpts).Extract() if err != nil { panic(err) } @@ -241,7 +241,7 @@ Example to remove nodes from a cluster Nodes: []string{"node-123"}, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - err := clusters.RemoveNodes(serviceClient, clusterID, removeNodesOpts).ExtractErr() + err := clusters.RemoveNodes(context.TODO(), serviceClient, clusterID, removeNodesOpts).ExtractErr() if err != nil { panic(err) } @@ -252,7 +252,7 @@ Example to replace nodes for a cluster Nodes: map[string]string{"node-1234": "node-5678"}, } clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.ReplaceNodes(serviceClient, clusterID, replaceNodesOpts).Extract() + actionID, err := clusters.ReplaceNodes(context.TODO(), serviceClient, clusterID, replaceNodesOpts).Extract() if err != nil { panic(err) } @@ -264,7 +264,7 @@ Example to collect node attributes across a cluster opts := clusters.CollectOpts{ Path: "status", } - attrs, err := clusters.Collect(serviceClient, clusterID, opts).Extract() + attrs, err := clusters.Collect(context.TODO(), serviceClient, clusterID, opts).Extract() if err != nil { panic(err) } @@ -278,7 +278,7 @@ Example to perform an operation on a cluster Filters: clusters.OperationFilters{"role": "slave"}, Params: clusters.OperationParams{"type": "SOFT"}, } - actionID, err := clusters.Ops(serviceClient, clusterID, operationOpts).Extract() + actionID, err := clusters.Ops(context.TODO(), serviceClient, clusterID, operationOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/events/doc.go b/openstack/clustering/v1/events/doc.go index bc88cb0913..a57222044b 100644 --- a/openstack/clustering/v1/events/doc.go +++ b/openstack/clustering/v1/events/doc.go @@ -23,7 +23,7 @@ Example to List Events Example to Get an Event eventID := "edce3528-864f-41fb-8759-f4707925cc09" - event, err := events.Get(serviceClient, eventID).Extract() + event, err := events.Get(context.TODO(), serviceClient, eventID).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go index 593757f19f..2959268c0f 100644 --- a/openstack/clustering/v1/nodes/doc.go +++ b/openstack/clustering/v1/nodes/doc.go @@ -12,7 +12,7 @@ Example to Create a Node Role: "", } - node, err := nodes.Create(serviceClient, createOpts).Extract() + node, err := nodes.Create(context.TODO(), serviceClient, createOpts).Extract() if err != nil { panic(err) } @@ -46,7 +46,7 @@ Example to Update a Node } nodeID := "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1" - node, err := nodes.Update(serviceClient, nodeID, opts).Extract() + node, err := nodes.Update(context.TODO(), serviceClient, nodeID, opts).Extract() if err != nil { panic(err) } @@ -56,7 +56,7 @@ Example to Update a Node Example to Delete a Node nodeID := "6dc6d336e3fc4c0a951b5698cd1236ee" - err := nodes.Delete(serviceClient, nodeID).ExtractErr() + err := nodes.Delete(context.TODO(), serviceClient, nodeID).ExtractErr() if err != nil { panic(err) } @@ -64,7 +64,7 @@ Example to Delete a Node Example to Get a Node nodeID := "node123" - node, err := nodes.Get(serviceClient, nodeID).Extract() + node, err := nodes.Get(context.TODO(), serviceClient, nodeID).Extract() if err != nil { panic(err) } @@ -79,7 +79,7 @@ Example to Perform an Operation on a Node Operation: nodes.RebootOperation, Params: nodes.OperationParams{"type": "SOFT"}, } - actionID, err := nodes.Ops(serviceClient, nodeID, operationOpts).Extract() + actionID, err := nodes.Ops(context.TODO(), serviceClient, nodeID, operationOpts).Extract() if err != nil { panic(err) } @@ -92,7 +92,7 @@ Example to Recover a Node Operation: nodes.RebuildRecovery, Check: &check, } - actionID, err := nodes.Recover(computeClient, nodeID, recoverOpts).Extract() + actionID, err := nodes.Recover(context.TODO(), computeClient, nodeID, recoverOpts).Extract() if err != nil { panic(err) } @@ -101,7 +101,7 @@ Example to Recover a Node Example to Check a Node nodeID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := nodes.Check(serviceClient, nodeID).Extract() + actionID, err := nodes.Check(context.TODO(), serviceClient, nodeID).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go index b4566a665a..2bf65f00b5 100644 --- a/openstack/clustering/v1/policies/doc.go +++ b/openstack/clustering/v1/policies/doc.go @@ -42,7 +42,7 @@ Example to Create a Policy }, } - createdPolicy, err := policies.Create(client, createOpts).Extract() + createdPolicy, err := policies.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Create a Policy Example to Get a Policy policyName := "get_policy" - policyDetail, err := policies.Get(clusteringClient, policyName).Extract() + policyDetail, err := policies.Get(context.TODO(), clusteringClient, policyName).Extract() if err != nil { panic(err) } @@ -63,7 +63,7 @@ Example to Update a Policy Name: "update_policy", } - updatePolicy, err := policies.Update(client, opts).Extract() + updatePolicy, err := policies.Update(context.TODO(), client, opts).Extract() if err != nil { panic(err) } @@ -87,7 +87,7 @@ Example to Validate a Policy }, } - validatePolicy, err := policies.Validate(client, opts).Extract() + validatePolicy, err := policies.Validate(context.TODO(), client, opts).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/policytypes/doc.go b/openstack/clustering/v1/policytypes/doc.go index 3a7c3a0ee0..3f920bfef3 100644 --- a/openstack/clustering/v1/policytypes/doc.go +++ b/openstack/clustering/v1/policytypes/doc.go @@ -21,7 +21,7 @@ Example to List Policy Types Example to Get a Policy Type policyTypeName := "senlin.policy.affinity-1.0" - policyTypeDetail, err := policyTypes.Get(clusteringClient, policyTypeName).Extract() + policyTypeDetail, err := policyTypes.Get(context.TODO(), clusteringClient, policyTypeName).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go index b37fa9ea42..4bf27e79a0 100644 --- a/openstack/clustering/v1/profiles/doc.go +++ b/openstack/clustering/v1/profiles/doc.go @@ -25,7 +25,7 @@ Example to Create a Profile }, } - profile, err := profiles.Create(serviceClient, createOpts).Extract() + profile, err := profiles.Create(context.TODO(), serviceClient, createOpts).Extract() if err != nil { panic(err) } @@ -34,7 +34,7 @@ Example to Create a Profile Example to Get a Profile - profile, err := profiles.Get(serviceClient, "profile-name").Extract() + profile, err := profiles.Get(context.TODO(), serviceClient, "profile-name").Extract() if err != nil { panic(err) } @@ -65,7 +65,7 @@ Example to Update a Profile Name: "new-name", } - profile, err := profiles.Update(serviceClient, profileName, updateOpts).Extract() + profile, err := profiles.Update(context.TODO(), serviceClient, profileName, updateOpts).Extract() if err != nil { panic(err) } @@ -75,7 +75,7 @@ Example to Update a Profile Example to Delete a Profile profileID := "6dc6d336e3fc4c0a951b5698cd1236ee" - err := profiles.Delete(serviceClient, profileID).ExtractErr() + err := profiles.Delete(context.TODO(), serviceClient, profileID).ExtractErr() if err != nil { panic(err) } @@ -100,7 +100,7 @@ Example to Validate a profile }, } - profile, err := profiles.Validate(serviceClient, validateOpts).Extract() + profile, err := profiles.Validate(context.TODO(), serviceClient, validateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/profiletypes/doc.go b/openstack/clustering/v1/profiletypes/doc.go index 2e0e1062ac..b3349c5292 100644 --- a/openstack/clustering/v1/profiletypes/doc.go +++ b/openstack/clustering/v1/profiletypes/doc.go @@ -19,7 +19,7 @@ Example to List ProfileType Example to Get a ProfileType profileTypeName := "os.nova.server" - profileType, err := profiletypes.Get(clusteringClient, profileTypeName).Extract() + profileType, err := profiletypes.Get(context.TODO(), clusteringClient, profileTypeName).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go index 4cb4a7d643..a8316c9fc9 100644 --- a/openstack/clustering/v1/receivers/doc.go +++ b/openstack/clustering/v1/receivers/doc.go @@ -11,7 +11,7 @@ Example to Create a Receiver Type: receivers.WebhookReceiver, } - receiver, err := receivers.Create(serviceClient, createOpts).Extract() + receiver, err := receivers.Create(context.TODO(), serviceClient, createOpts).Extract() if err != nil { panic(err) } @@ -20,7 +20,7 @@ Example to Create a Receiver Example to Get a Receiver - receiver, err := receivers.Get(serviceClient, "receiver-name").Extract() + receiver, err := receivers.Get(context.TODO(), serviceClient, "receiver-name").Extract() if err != nil { panic(err) } @@ -30,7 +30,7 @@ Example to Get a Receiver Example to Delete receiver receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" - err := receivers.Delete(serviceClient, receiverID).ExtractErr() + err := receivers.Delete(context.TODO(), serviceClient, receiverID).ExtractErr() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Update Receiver } receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" - receiver, err := receivers.Update(serviceClient, receiverID, updateOpts).Extract() + receiver, err := receivers.Update(context.TODO(), serviceClient, receiverID, updateOpts).Extract() if err != nil { panic(err) } @@ -72,7 +72,7 @@ Example to List Receivers Example to Notify a Receiver receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" - requestID, err := receivers.Notify(serviceClient, receiverID).Extract() + requestID, err := receivers.Notify(context.TODO(), serviceClient, receiverID).Extract() if err != nil { panic(err) } diff --git a/openstack/clustering/v1/webhooks/doc.go b/openstack/clustering/v1/webhooks/doc.go index a96e0d2884..9ca6b0be5b 100644 --- a/openstack/clustering/v1/webhooks/doc.go +++ b/openstack/clustering/v1/webhooks/doc.go @@ -4,7 +4,7 @@ Service. Example to Trigger webhook action - result, err := webhooks.Trigger(serviceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{V: "1"}).Extract() + result, err := webhooks.Trigger(context.TODO(), serviceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{V: "1"}).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/apiversions/doc.go b/openstack/compute/apiversions/doc.go index b9feb6a0f2..a43cb0a445 100644 --- a/openstack/compute/apiversions/doc.go +++ b/openstack/compute/apiversions/doc.go @@ -20,7 +20,7 @@ Example to List API Versions Example to Get an API Version - version, err := apiVersions.Get(computeClient, "v2.1").Extract() + version, err := apiVersions.Get(context.TODO(), computeClient, "v2.1").Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/extensions/aggregates/doc.go index 3ec63a1b00..29b8bf7d9e 100644 --- a/openstack/compute/v2/extensions/aggregates/doc.go +++ b/openstack/compute/v2/extensions/aggregates/doc.go @@ -9,7 +9,7 @@ Example of Create Aggregate AvailabilityZone: "london", } - aggregate, err := aggregates.Create(computeClient, createOpts).Extract() + aggregate, err := aggregates.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -18,7 +18,7 @@ Example of Create Aggregate Example of Show Aggregate Details aggregateID := 42 - aggregate, err := aggregates.Get(computeClient, aggregateID).Extract() + aggregate, err := aggregates.Get(context.TODO(), computeClient, aggregateID).Extract() if err != nil { panic(err) } @@ -27,7 +27,7 @@ Example of Show Aggregate Details Example of Delete Aggregate aggregateID := 32 - err := aggregates.Delete(computeClient, aggregateID).ExtractErr() + err := aggregates.Delete(context.TODO(), computeClient, aggregateID).ExtractErr() if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example of Update Aggregate AvailabilityZone: "nova2", } - aggregate, err := aggregates.Update(computeClient, aggregateID, opts).Extract() + aggregate, err := aggregates.Update(context.TODO(), computeClient, aggregateID, opts).Extract() if err != nil { panic(err) } @@ -69,7 +69,7 @@ Example of Add Host Host: "newhost-cmp1", } - aggregate, err := aggregates.AddHost(computeClient, aggregateID, opts).Extract() + aggregate, err := aggregates.AddHost(context.TODO(), computeClient, aggregateID, opts).Extract() if err != nil { panic(err) } @@ -82,7 +82,7 @@ Example of Remove Host Host: "newhost-cmp1", } - aggregate, err := aggregates.RemoveHost(computeClient, aggregateID, opts).Extract() + aggregate, err := aggregates.RemoveHost(context.TODO(), computeClient, aggregateID, opts).Extract() if err != nil { panic(err) } @@ -95,7 +95,7 @@ Example of Create or Update Metadata Metadata: map[string]string{"key": "value"}, } - aggregate, err := aggregates.SetMetadata(computeClient, aggregateID, opts).Extract() + aggregate, err := aggregates.SetMetadata(context.TODO(), computeClient, aggregateID, opts).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/extensions/attachinterfaces/doc.go index a6f01ce51d..3b1ad9fb55 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/doc.go +++ b/openstack/compute/v2/extensions/attachinterfaces/doc.go @@ -23,7 +23,7 @@ Example to Get a Server's Interface portID = "0dde1598-b374-474e-986f-5b8dd1df1d4e" serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" - interface, err := attachinterfaces.Get(computeClient, serverID, portID).Extract() + interface, err := attachinterfaces.Get(context.TODO(), computeClient, serverID, portID).Extract() if err != nil { panic(err) } @@ -35,7 +35,7 @@ Example to Create a new Interface attachment on the Server attachOpts := attachinterfaces.CreateOpts{ NetworkID: networkID, } - interface, err := attachinterfaces.Create(computeClient, serverID, attachOpts).Extract() + interface, err := attachinterfaces.Create(context.TODO(), computeClient, serverID, attachOpts).Extract() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Delete an Interface attachment from the Server portID = "0dde1598-b374-474e-986f-5b8dd1df1d4e" serverID := "b07e7a3b-d951-4efc-a4f9-ac9f001afb7f" - err := attachinterfaces.Delete(computeClient, serverID, portID).ExtractErr() + err := attachinterfaces.Delete(context.TODO(), computeClient, serverID, portID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/bootfromvolume/doc.go b/openstack/compute/v2/extensions/bootfromvolume/doc.go index 79a09b33cf..8cf90bc9b0 100644 --- a/openstack/compute/v2/extensions/bootfromvolume/doc.go +++ b/openstack/compute/v2/extensions/bootfromvolume/doc.go @@ -37,7 +37,7 @@ a server without using block device mappings. BlockDevice: blockDevices, } - server, err := bootfromvolume.Create(client, createOpts).Extract() + server, err := bootfromvolume.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -67,7 +67,7 @@ server will use this volume as its root disk. BlockDevice: blockDevices, } - server, err := bootfromvolume.Create(client, createOpts).Extract() + server, err := bootfromvolume.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -95,7 +95,7 @@ This example will create a server with an existing volume as its root disk. BlockDevice: blockDevices, } - server, err := bootfromvolume.Create(client, createOpts).Extract() + server, err := bootfromvolume.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -144,7 +144,7 @@ ephemeral disks must have an index of -1. BlockDevice: blockDevices, } - server, err := bootfromvolume.Create(client, createOpts).Extract() + server, err := bootfromvolume.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/defsecrules/doc.go b/openstack/compute/v2/extensions/defsecrules/doc.go index 1d5777a860..724402ede7 100644 --- a/openstack/compute/v2/extensions/defsecrules/doc.go +++ b/openstack/compute/v2/extensions/defsecrules/doc.go @@ -26,7 +26,7 @@ Example of Listing Default Security Group Rules Example of Retrieving a Default Security Group Rule - rule, err := defsecrules.Get(computeClient, "rule-id").Extract() + rule, err := defsecrules.Get(context.TODO(), computeClient, "rule-id").Extract() if err != nil { panic(err) } @@ -40,14 +40,14 @@ Example of Creating a Default Security Group Rule CIDR: "10.10.12.0/24", } - rule, err := defsecrules.Create(computeClient, createOpts).Extract() + rule, err := defsecrules.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } Example of Deleting a Default Security Group Rule - err := defsecrules.Delete(computeClient, "rule-id").ExtractErr() + err := defsecrules.Delete(context.TODO(), computeClient, "rule-id").ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/diagnostics/doc.go b/openstack/compute/v2/extensions/diagnostics/doc.go index 8fdfc23e35..dc826ebf31 100644 --- a/openstack/compute/v2/extensions/diagnostics/doc.go +++ b/openstack/compute/v2/extensions/diagnostics/doc.go @@ -3,7 +3,7 @@ Package diagnostics returns details about a nova instance diagnostics Example of Show Diagnostics - diags, err := diagnostics.Get(computeClient, serverId).Extract() + diags, err := diagnostics.Get(context.TODO(), computeClient, serverId).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/diskconfig/doc.go b/openstack/compute/v2/extensions/diskconfig/doc.go index dbaacd3279..0ab9b9853e 100644 --- a/openstack/compute/v2/extensions/diskconfig/doc.go +++ b/openstack/compute/v2/extensions/diskconfig/doc.go @@ -38,7 +38,7 @@ Example of Creating a Server with Disk Config DiskConfig: diskconfig.Manual, } - server, err := servers.Create(computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic("Unable to create server: %s", err) } diff --git a/openstack/compute/v2/extensions/evacuate/doc.go b/openstack/compute/v2/extensions/evacuate/doc.go index faafe7c313..51a8296fff 100644 --- a/openstack/compute/v2/extensions/evacuate/doc.go +++ b/openstack/compute/v2/extensions/evacuate/doc.go @@ -5,7 +5,7 @@ provisioned by the OpenStack Compute service from a failed host to a new host. Example to Evacuate a Server from a Host serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" - err := evacuate.Evacuate(computeClient, serverID, evacuate.EvacuateOpts{}).ExtractErr() + err := evacuate.Evacuate(context.TODO(), computeClient, serverID, evacuate.EvacuateOpts{}).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/extendedserverattributes/doc.go b/openstack/compute/v2/extensions/extendedserverattributes/doc.go index 71a9609e09..afe20573b4 100644 --- a/openstack/compute/v2/extensions/extendedserverattributes/doc.go +++ b/openstack/compute/v2/extensions/extendedserverattributes/doc.go @@ -10,7 +10,7 @@ Example to Get basic extended information: } var serverWithAttributesExt serverAttributesExt - err := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) + err := servers.Get(context.TODO(), computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) if err != nil { panic(err) } @@ -20,7 +20,7 @@ Example to Get basic extended information: Example to get additional fields with microversion 2.3 or later computeClient.Microversion = "2.3" - result := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a") + result := servers.Get(context.TODO(), computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a") reservationID, err := extendedserverattributes.ExtractReservationID(result.Result) if err != nil { diff --git a/openstack/compute/v2/extensions/floatingips/doc.go b/openstack/compute/v2/extensions/floatingips/doc.go index 4eb9e94cbe..00582f456e 100644 --- a/openstack/compute/v2/extensions/floatingips/doc.go +++ b/openstack/compute/v2/extensions/floatingips/doc.go @@ -31,14 +31,14 @@ Example to Create a Floating IP Pool: "nova", } - fip, err := floatingips.Create(computeClient, createOpts).Extract() + fip, err := floatingips.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Floating IP - err := floatingips.Delete(computeClient, "floatingip-id").ExtractErr() + err := floatingips.Delete(context.TODO(), computeClient, "floatingip-id").ExtractErr() if err != nil { panic(err) } @@ -49,7 +49,7 @@ Example to Associate a Floating IP With a Server FloatingIP: "10.10.10.2", } - err := floatingips.AssociateInstance(computeClient, "server-id", associateOpts).ExtractErr() + err := floatingips.AssociateInstance(context.TODO(), computeClient, "server-id", associateOpts).ExtractErr() if err != nil { panic(err) } @@ -60,7 +60,7 @@ Example to Disassociate a Floating IP From a Server FloatingIP: "10.10.10.2", } - err := floatingips.DisassociateInstance(computeClient, "server-id", disassociateOpts).ExtractErr() + err := floatingips.DisassociateInstance(context.TODO(), computeClient, "server-id", disassociateOpts).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/extensions/hypervisors/doc.go index e2f1a99c20..a3766abffb 100644 --- a/openstack/compute/v2/extensions/hypervisors/doc.go +++ b/openstack/compute/v2/extensions/hypervisors/doc.go @@ -5,7 +5,7 @@ and shows summary statistics for all hypervisors over all compute nodes in the O Example of Show Hypervisor Details hypervisorID := "42" - hypervisor, err := hypervisors.Get(computeClient, hypervisorID).Extract() + hypervisor, err := hypervisors.Get(context.TODO(), computeClient, hypervisorID).Extract() if err != nil { panic(err) } @@ -17,7 +17,7 @@ Example of Show Hypervisor Details when using Compute API microversion greater t computeClient.Microversion = "2.53" hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" - hypervisor, err := hypervisors.Get(computeClient, hypervisorID).Extract() + hypervisor, err := hypervisors.Get(context.TODO(), computeClient, hypervisorID).Extract() if err != nil { panic(err) } @@ -65,7 +65,7 @@ Example of Retrieving Details of All Hypervisors when using Compute API microver Example of Show Hypervisors Statistics - hypervisorsStatistics, err := hypervisors.GetStatistics(computeClient).Extract() + hypervisorsStatistics, err := hypervisors.GetStatistics(context.TODO(), computeClient).Extract() if err != nil { panic(err) } @@ -75,7 +75,7 @@ Example of Show Hypervisors Statistics Example of Show Hypervisor Uptime hypervisorID := "42" - hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() + hypervisorUptime, err := hypervisors.GetUptime(context.TODO(), computeClient, hypervisorID).Extract() if err != nil { panic(err) } @@ -87,7 +87,7 @@ Example of Show Hypervisor Uptime with Compute API microversion greater than 2.5 computeClient.Microversion = "2.53" hypervisorID := "c48f6247-abe4-4a24-824e-ea39e108874f" - hypervisorUptime, err := hypervisors.GetUptime(computeClient, hypervisorID).Extract() + hypervisorUptime, err := hypervisors.GetUptime(context.TODO(), computeClient, hypervisorID).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/doc.go b/openstack/compute/v2/extensions/injectnetworkinfo/doc.go index 2ab8658b26..3355110ed4 100644 --- a/openstack/compute/v2/extensions/injectnetworkinfo/doc.go +++ b/openstack/compute/v2/extensions/injectnetworkinfo/doc.go @@ -6,7 +6,7 @@ requires admin privileges and Nova configured with a Xen hypervisor driver. Example to Inject a Network Info into a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := injectnetworkinfo.InjectNetworkInfo(client, id).ExtractErr() + err := injectnetworkinfo.InjectNetworkInfo(context.TODO(), client, id).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/instanceactions/doc.go b/openstack/compute/v2/extensions/instanceactions/doc.go index c322da0ae0..b6d4318122 100644 --- a/openstack/compute/v2/extensions/instanceactions/doc.go +++ b/openstack/compute/v2/extensions/instanceactions/doc.go @@ -16,7 +16,7 @@ Example to List and Get actions: } for _, action := range actions { - action, err = instanceactions.Get(client, "server-id", action.RequestID).Extract() + action, err = instanceactions.Get(context.TODO(), client, "server-id", action.RequestID).Extract() if err != nil { panic("fail to get instance action") } diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go index 944619d2aa..0e52dfc9a3 100644 --- a/openstack/compute/v2/extensions/keypairs/doc.go +++ b/openstack/compute/v2/extensions/keypairs/doc.go @@ -46,7 +46,7 @@ Example to Create a Key Pair Name: "keypair-name", } - keypair, err := keypairs.Create(computeClient, createOpts).Extract() + keypair, err := keypairs.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -60,14 +60,14 @@ Example to Import a Key Pair PublicKey: "public-key", } - keypair, err := keypairs.Create(computeClient, createOpts).Extract() + keypair, err := keypairs.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Key Pair - err := keypairs.Delete(computeClient, "keypair-name", nil).ExtractErr() + err := keypairs.Delete(context.TODO(), computeClient, "keypair-name", nil).ExtractErr() if err != nil { panic(err) } @@ -80,7 +80,7 @@ Example to Delete a Key Pair owned by a certain user using microversion 2.10 or UserID: "user-id", } - err := keypairs.Delete(client, "keypair-name", deleteOpts).ExtractErr() + err := keypairs.Delete(context.TODO(), client, "keypair-name", deleteOpts).ExtractErr() if err != nil { panic(err) } @@ -98,7 +98,7 @@ Example to Create a Server With a Key Pair KeyName: "keypair-name", } - server, err := servers.Create(computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -111,7 +111,7 @@ Example to Get a Key Pair owned by a certain user using microversion 2.10 or gre UserID: "user-id", } - keypair, err := keypairs.Get(computeClient, "keypair-name", getOpts).Extract() + keypair, err := keypairs.Get(context.TODO(), computeClient, "keypair-name", getOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/limits/doc.go b/openstack/compute/v2/extensions/limits/doc.go index c14d537a6d..d3c030baf3 100644 --- a/openstack/compute/v2/extensions/limits/doc.go +++ b/openstack/compute/v2/extensions/limits/doc.go @@ -7,7 +7,7 @@ Example to Retrieve Limits for a Tenant TenantID: "tenant-id", } - limits, err := limits.Get(computeClient, getOpts).Extract() + limits, err := limits.Get(context.TODO(), computeClient, getOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/lockunlock/doc.go b/openstack/compute/v2/extensions/lockunlock/doc.go index ac51a36f61..b6427bfd0b 100644 --- a/openstack/compute/v2/extensions/lockunlock/doc.go +++ b/openstack/compute/v2/extensions/lockunlock/doc.go @@ -6,12 +6,12 @@ Example to Lock and Unlock a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := lockunlock.Lock(computeClient, serverID).ExtractErr() + err := lockunlock.Lock(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } - err = lockunlock.Unlock(computeClient, serverID).ExtractErr() + err = lockunlock.Unlock(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/migrate/doc.go b/openstack/compute/v2/extensions/migrate/doc.go index f0c3291d04..2bc569b18d 100644 --- a/openstack/compute/v2/extensions/migrate/doc.go +++ b/openstack/compute/v2/extensions/migrate/doc.go @@ -5,7 +5,7 @@ provisioned by the OpenStack Compute service. Example of Migrate Server (migrate Action) serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" - err := migrate.Migrate(computeClient, serverID).ExtractErr() + err := migrate.Migrate(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } @@ -21,7 +21,7 @@ Example of Live-Migrate Server (os-migrateLive Action) BlockMigration: &blockMigration, } - err := migrate.LiveMigrate(computeClient, serverID, migrationOpts).ExtractErr() + err := migrate.LiveMigrate(context.TODO(), computeClient, serverID, migrationOpts).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/pauseunpause/doc.go b/openstack/compute/v2/extensions/pauseunpause/doc.go index b260ca0767..35cfcce197 100644 --- a/openstack/compute/v2/extensions/pauseunpause/doc.go +++ b/openstack/compute/v2/extensions/pauseunpause/doc.go @@ -5,12 +5,12 @@ have been provisioned by the OpenStack Compute service. Example to Pause and Unpause a Server serverID := "32c8baf7-1cdb-4cc2-bc31-c3a55b89f56b" - err := pauseunpause.Pause(computeClient, serverID).ExtractErr() + err := pauseunpause.Pause(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } - err = pauseunpause.Unpause(computeClient, serverID).ExtractErr() + err = pauseunpause.Unpause(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/quotasets/doc.go b/openstack/compute/v2/extensions/quotasets/doc.go index 04d9887a14..77d2ab2ff1 100644 --- a/openstack/compute/v2/extensions/quotasets/doc.go +++ b/openstack/compute/v2/extensions/quotasets/doc.go @@ -3,7 +3,7 @@ Package quotasets enables retrieving and managing Compute quotas. Example to Get a Quota Set - quotaset, err := quotasets.Get(computeClient, "tenant-id").Extract() + quotaset, err := quotasets.Get(context.TODO(), computeClient, "tenant-id").Extract() if err != nil { panic(err) } @@ -12,7 +12,7 @@ Example to Get a Quota Set Example to Get a Detailed Quota Set - quotaset, err := quotasets.GetDetail(computeClient, "tenant-id").Extract() + quotaset, err := quotasets.GetDetail(context.TODO(), computeClient, "tenant-id").Extract() if err != nil { panic(err) } @@ -26,7 +26,7 @@ Example to Update a Quota Set Cores: gophercloud.IntToPointer(64), } - quotaset, err := quotasets.Update(computeClient, "tenant-id", updateOpts).Extract() + quotaset, err := quotasets.Update(context.TODO(), computeClient, "tenant-id", updateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/remoteconsoles/doc.go b/openstack/compute/v2/extensions/remoteconsoles/doc.go index 54b281e4b7..65dc9b91d7 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/doc.go +++ b/openstack/compute/v2/extensions/remoteconsoles/doc.go @@ -15,7 +15,7 @@ Example of Creating a new RemoteConsole } serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" - remtoteConsole, err := remoteconsoles.Create(computeClient, serverID, createOpts).Extract() + remtoteConsole, err := remoteconsoles.Create(context.TODO(), computeClient, serverID, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/rescueunrescue/doc.go b/openstack/compute/v2/extensions/rescueunrescue/doc.go index e448efc5ed..64c01697a2 100644 --- a/openstack/compute/v2/extensions/rescueunrescue/doc.go +++ b/openstack/compute/v2/extensions/rescueunrescue/doc.go @@ -10,7 +10,7 @@ Example to Rescue a server } serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" - adminPass, err := rescueunrescue.Rescue(computeClient, serverID, rescueOpts).Extract() + adminPass, err := rescueunrescue.Rescue(context.TODO(), computeClient, serverID, rescueOpts).Extract() if err != nil { panic(err) } @@ -21,7 +21,7 @@ Example to Unrescue a server serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" - if err := rescueunrescue.Unrescue(computeClient, serverID).ExtractErr(); err != nil { + if err := rescueunrescue.Unrescue(context.TODO(), computeClient, serverID).ExtractErr(); err != nil { panic(err) } */ diff --git a/openstack/compute/v2/extensions/resetnetwork/doc.go b/openstack/compute/v2/extensions/resetnetwork/doc.go index 5d89c7ccfc..e105b702cc 100644 --- a/openstack/compute/v2/extensions/resetnetwork/doc.go +++ b/openstack/compute/v2/extensions/resetnetwork/doc.go @@ -6,7 +6,7 @@ requires admin privileges and Nova configured with a Xen hypervisor driver. Example to Reset a Network of a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := resetnetwork.ResetNetwork(client, id).ExtractErr() + err := resetnetwork.ResetNetwork(context.TODO(), client, id).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/resetstate/doc.go b/openstack/compute/v2/extensions/resetstate/doc.go index 00f004b022..aca92cd6b0 100644 --- a/openstack/compute/v2/extensions/resetstate/doc.go +++ b/openstack/compute/v2/extensions/resetstate/doc.go @@ -5,7 +5,7 @@ been provisioned by the OpenStack Compute service. Example to Reset a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := resetstate.ResetState(client, id, resetstate.StateActive).ExtractErr() + err := resetstate.ResetState(context.TODO(), client, id, resetstate.StateActive).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/schedulerhints/doc.go b/openstack/compute/v2/extensions/schedulerhints/doc.go index 2d9d3acdec..2321312e7e 100644 --- a/openstack/compute/v2/extensions/schedulerhints/doc.go +++ b/openstack/compute/v2/extensions/schedulerhints/doc.go @@ -20,7 +20,7 @@ Example to Add a Server to a Server Group SchedulerHints: schedulerHints, } - server, err := servers.Create(computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Place Server B on a Different Host than Server A SchedulerHints: schedulerHints, } - server, err := servers.Create(computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -68,7 +68,7 @@ Example to Place Server B on the Same Host as Server A SchedulerHints: schedulerHints, } - server, err := servers.Create(computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/secgroups/doc.go b/openstack/compute/v2/extensions/secgroups/doc.go index 470be87b8b..cae61e332f 100644 --- a/openstack/compute/v2/extensions/secgroups/doc.go +++ b/openstack/compute/v2/extensions/secgroups/doc.go @@ -50,7 +50,7 @@ Example to Create a Security Group Description: "A Security Group", } - sg, err := secgroups.Create(computeClient, createOpts).Extract() + sg, err := secgroups.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -67,7 +67,7 @@ Example to Create a Security Group Rule CIDR: "0.0.0.0/0", } - rule, err := secgroups.CreateRule(computeClient, createOpts).Extract() + rule, err := secgroups.CreateRule(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -77,7 +77,7 @@ Example to Add a Security Group to a Server serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" - err := secgroups.AddServer(computeClient, serverID, sgID).ExtractErr() + err := secgroups.AddServer(context.TODO(), computeClient, serverID, sgID).ExtractErr() if err != nil { panic(err) } @@ -87,7 +87,7 @@ Example to Remove a Security Group from a Server serverID := "aab3ad01-9956-4623-a29b-24afc89a7d36" sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" - err := secgroups.RemoveServer(computeClient, serverID, sgID).ExtractErr() + err := secgroups.RemoveServer(context.TODO(), computeClient, serverID, sgID).ExtractErr() if err != nil { panic(err) } @@ -95,7 +95,7 @@ Example to Remove a Security Group from a Server # Example to Delete a Security Group sgID := "37d94f8a-d136-465c-ae46-144f0d8ef141" - err := secgroups.Delete(computeClient, sgID).ExtractErr() + err := secgroups.Delete(context.TODO(), computeClient, sgID).ExtractErr() if err != nil { panic(err) } @@ -103,7 +103,7 @@ Example to Remove a Security Group from a Server Example to Delete a Security Group Rule ruleID := "6221fe3e-383d-46c9-a3a6-845e66c1e8b4" - err := secgroups.DeleteRule(computeClient, ruleID).ExtractErr() + err := secgroups.DeleteRule(context.TODO(), computeClient, ruleID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/servergroups/doc.go b/openstack/compute/v2/extensions/servergroups/doc.go index 62df82dc6b..063851ed77 100644 --- a/openstack/compute/v2/extensions/servergroups/doc.go +++ b/openstack/compute/v2/extensions/servergroups/doc.go @@ -24,7 +24,7 @@ Example to Create a Server Group Policies: []string{"anti-affinity"}, } - sg, err := servergroups.Create(computeClient, createOpts).Extract() + sg, err := servergroups.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example to Create a Server Group with additional microversion 2.64 fields } computeClient.Microversion = "2.64" - result := servergroups.Create(computeClient, createOpts) + result := servergroups.Create(context.TODO(), computeClient, createOpts) serverGroup, err := result.Extract() if err != nil { @@ -50,7 +50,7 @@ Example to Create a Server Group with additional microversion 2.64 fields Example to Delete a Server Group sgID := "7a6f29ad-e34d-4368-951a-58a08f11cfb7" - err := servergroups.Delete(computeClient, sgID).ExtractErr() + err := servergroups.Delete(context.TODO(), computeClient, sgID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/serverusage/doc.go b/openstack/compute/v2/extensions/serverusage/doc.go index f6310d77a9..9f11918ba3 100644 --- a/openstack/compute/v2/extensions/serverusage/doc.go +++ b/openstack/compute/v2/extensions/serverusage/doc.go @@ -10,7 +10,7 @@ Example to Get an extended information: } var serverWithUsageExt serverUsageExt - err := servers.Get(computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) + err := servers.Get(context.TODO(), computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/services/doc.go b/openstack/compute/v2/extensions/services/doc.go index 08e224f0be..97d164ca52 100644 --- a/openstack/compute/v2/extensions/services/doc.go +++ b/openstack/compute/v2/extensions/services/doc.go @@ -28,14 +28,14 @@ Example of updating a service Status: services.ServiceDisabled, } - updated, err := services.Update(client, serviceID, opts).Extract() + updated, err := services.Update(context.TODO(), client, serviceID, opts).Extract() if err != nil { panic(err) } Example of delete a service - updated, err := services.Delete(client, serviceID).Extract() + updated, err := services.Delete(context.TODO(), client, serviceID).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/shelveunshelve/doc.go b/openstack/compute/v2/extensions/shelveunshelve/doc.go index 0d33f47fcd..895b385a94 100644 --- a/openstack/compute/v2/extensions/shelveunshelve/doc.go +++ b/openstack/compute/v2/extensions/shelveunshelve/doc.go @@ -6,17 +6,17 @@ Example to Shelve, Shelve-offload and Unshelve a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := shelveunshelve.Shelve(computeClient, serverID).ExtractErr() + err := shelveunshelve.Shelve(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } - err := shelveunshelve.ShelveOffload(computeClient, serverID).ExtractErr() + err := shelveunshelve.ShelveOffload(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } - err := shelveunshelve.Unshelve(computeClient, serverID, nil).ExtractErr() + err := shelveunshelve.Unshelve(context.TODO(), computeClient, serverID, nil).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/startstop/doc.go b/openstack/compute/v2/extensions/startstop/doc.go index ab97edb776..f34dde1b50 100644 --- a/openstack/compute/v2/extensions/startstop/doc.go +++ b/openstack/compute/v2/extensions/startstop/doc.go @@ -6,12 +6,12 @@ Example to Stop and Start a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := startstop.Stop(computeClient, serverID).ExtractErr() + err := startstop.Stop(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } - err := startstop.Start(computeClient, serverID).ExtractErr() + err := startstop.Start(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/suspendresume/doc.go b/openstack/compute/v2/extensions/suspendresume/doc.go index 9851000e8a..e8dea36143 100644 --- a/openstack/compute/v2/extensions/suspendresume/doc.go +++ b/openstack/compute/v2/extensions/suspendresume/doc.go @@ -6,12 +6,12 @@ Example to Suspend and Resume a Server serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := suspendresume.Suspend(computeClient, serverID).ExtractErr() + err := suspendresume.Suspend(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } - err := suspendresume.Resume(computeClient, serverID).ExtractErr() + err := suspendresume.Resume(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/extensions/tags/doc.go b/openstack/compute/v2/extensions/tags/doc.go index d3516aa0d8..9f28058e02 100644 --- a/openstack/compute/v2/extensions/tags/doc.go +++ b/openstack/compute/v2/extensions/tags/doc.go @@ -7,7 +7,7 @@ Example to List all server Tags client.Microversion = "2.26" - serverTags, err := tags.List(client, serverID).Extract() + serverTags, err := tags.List(context.TODO(), client, serverID).Extract() if err != nil { log.Fatal(err) } @@ -18,7 +18,7 @@ Example to Check if the specific Tag exists on a server client.Microversion = "2.26" - exists, err := tags.Check(client, serverID, tag).Extract() + exists, err := tags.Check(context.TODO(), client, serverID, tag).Extract() if err != nil { log.Fatal(err) } @@ -33,7 +33,7 @@ Example to Replace all Tags on a server client.Microversion = "2.26" - newTags, err := tags.ReplaceAll(client, serverID, tags.ReplaceAllOpts{Tags: []string{"foo", "bar"}}).Extract() + newTags, err := tags.ReplaceAll(context.TODO(), client, serverID, tags.ReplaceAllOpts{Tags: []string{"foo", "bar"}}).Extract() if err != nil { log.Fatal(err) } @@ -44,7 +44,7 @@ Example to Add a new Tag on a server client.Microversion = "2.26" - err := tags.Add(client, serverID, "foo").ExtractErr() + err := tags.Add(context.TODO(), client, serverID, "foo").ExtractErr() if err != nil { log.Fatal(err) } @@ -53,7 +53,7 @@ Example to Delete a Tag on a server client.Microversion = "2.26" - err := tags.Delete(client, serverID, "foo").ExtractErr() + err := tags.Delete(context.TODO(), client, serverID, "foo").ExtractErr() if err != nil { log.Fatal(err) } @@ -62,7 +62,7 @@ Example to Delete all Tags on a server client.Microversion = "2.26" - err := tags.DeleteAll(client, serverID).ExtractErr() + err := tags.DeleteAll(context.TODO(), client, serverID).ExtractErr() if err != nil { log.Fatal(err) } diff --git a/openstack/compute/v2/extensions/volumeattach/doc.go b/openstack/compute/v2/extensions/volumeattach/doc.go index 857ab19cc5..9ab3253ef4 100644 --- a/openstack/compute/v2/extensions/volumeattach/doc.go +++ b/openstack/compute/v2/extensions/volumeattach/doc.go @@ -12,7 +12,7 @@ Example to Attach a Volume VolumeID: volumeID, } - result, err := volumeattach.Create(computeClient, serverID, createOpts).Extract() + result, err := volumeattach.Create(context.TODO(), computeClient, serverID, createOpts).Extract() if err != nil { panic(err) } @@ -22,7 +22,7 @@ Example to Detach a Volume serverID := "7ac8686c-de71-4acb-9600-ec18b1a1ed6d" volumeID := "ed081613-1c9b-4231-aa5e-ebfd4d87f983" - err := volumeattach.Delete(computeClient, serverID, volumeID).ExtractErr() + err := volumeattach.Delete(context.TODO(), computeClient, serverID, volumeID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go index c988cb8232..28a5870c23 100644 --- a/openstack/compute/v2/flavors/doc.go +++ b/openstack/compute/v2/flavors/doc.go @@ -37,7 +37,7 @@ Example to Create a Flavor RxTxFactor: 1.0, } - flavor, err := flavors.Create(computeClient, createOpts).Extract() + flavor, err := flavors.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Update a Flavor Description: "This is a good description" } - flavor, err := flavors.Update(computeClient, flavorID, updateOpts).Extract() + flavor, err := flavors.Update(context.TODO(), computeClient, flavorID, updateOpts).Extract() if err != nil { panic(err) } @@ -81,7 +81,7 @@ Example to Grant Access to a Flavor Tenant: "15153a0979884b59b0592248ef947921", } - accessList, err := flavors.AddAccess(computeClient, flavor.ID, accessOpts).Extract() + accessList, err := flavors.AddAccess(context.TODO(), computeClient, flavor.ID, accessOpts).Extract() if err != nil { panic(err) } @@ -94,7 +94,7 @@ Example to Remove/Revoke Access to a Flavor Tenant: "15153a0979884b59b0592248ef947921", } - accessList, err := flavors.RemoveAccess(computeClient, flavor.ID, accessOpts).Extract() + accessList, err := flavors.RemoveAccess(context.TODO(), computeClient, flavor.ID, accessOpts).Extract() if err != nil { panic(err) } @@ -107,7 +107,7 @@ Example to Create Extra Specs for a Flavor "hw:cpu_policy": "CPU-POLICY", "hw:cpu_thread_policy": "CPU-THREAD-POLICY", } - createdExtraSpecs, err := flavors.CreateExtraSpecs(computeClient, flavorID, createOpts).Extract() + createdExtraSpecs, err := flavors.CreateExtraSpecs(context.TODO(), computeClient, flavorID, createOpts).Extract() if err != nil { panic(err) } @@ -118,7 +118,7 @@ Example to Get Extra Specs for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - extraSpecs, err := flavors.ListExtraSpecs(computeClient, flavorID).Extract() + extraSpecs, err := flavors.ListExtraSpecs(context.TODO(), computeClient, flavorID).Extract() if err != nil { panic(err) } @@ -132,7 +132,7 @@ Example to Update Extra Specs for a Flavor updateOpts := flavors.ExtraSpecsOpts{ "hw:cpu_thread_policy": "CPU-THREAD-POLICY-UPDATED", } - updatedExtraSpec, err := flavors.UpdateExtraSpec(computeClient, flavorID, updateOpts).Extract() + updatedExtraSpec, err := flavors.UpdateExtraSpec(context.TODO(), computeClient, flavorID, updateOpts).Extract() if err != nil { panic(err) } @@ -142,7 +142,7 @@ Example to Update Extra Specs for a Flavor Example to Delete an Extra Spec for a Flavor flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" - err := flavors.DeleteExtraSpec(computeClient, flavorID, "hw:cpu_thread_policy").ExtractErr() + err := flavors.DeleteExtraSpec(context.TODO(), computeClient, flavorID, "hw:cpu_thread_policy").ExtractErr() if err != nil { panic(err) } diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index 0649905abe..fe9ce88731 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -53,7 +53,7 @@ Example to Create a Server FlavorRef: "flavor-uuid", } - server, err := servers.Create(computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() if err != nil { panic(err) } @@ -61,7 +61,7 @@ Example to Create a Server Example to Delete a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - err := servers.Delete(computeClient, serverID).ExtractErr() + err := servers.Delete(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } @@ -69,7 +69,7 @@ Example to Delete a Server Example to Force Delete a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - err := servers.ForceDelete(computeClient, serverID).ExtractErr() + err := servers.ForceDelete(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } @@ -82,7 +82,7 @@ Example to Reboot a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - err := servers.Reboot(computeClient, serverID, rebootOpts).ExtractErr() + err := servers.Reboot(context.TODO(), computeClient, serverID, rebootOpts).ExtractErr() if err != nil { panic(err) } @@ -109,12 +109,12 @@ Example to Resize a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - err := servers.Resize(computeClient, serverID, resizeOpts).ExtractErr() + err := servers.Resize(context.TODO(), computeClient, serverID, resizeOpts).ExtractErr() if err != nil { panic(err) } - err = servers.ConfirmResize(computeClient, serverID).ExtractErr() + err = servers.ConfirmResize(context.TODO(), computeClient, serverID).ExtractErr() if err != nil { panic(err) } @@ -127,7 +127,7 @@ Example to Snapshot a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" - image, err := servers.CreateImage(computeClient, serverID, snapshotOpts).ExtractImageID() + image, err := servers.CreateImage(context.TODO(), computeClient, serverID, snapshotOpts).ExtractImageID() if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/certificates/doc.go b/openstack/containerinfra/v1/certificates/doc.go index 86125e9473..505c7183e3 100644 --- a/openstack/containerinfra/v1/certificates/doc.go +++ b/openstack/containerinfra/v1/certificates/doc.go @@ -6,7 +6,7 @@ the OpenStack Container Infra service. Example to get certificates - certificate, err := certificates.Get(serviceClient, "d564b18a-2890-4152-be3d-e05d784ff72").Extract() + certificate, err := certificates.Get(context.TODO(), serviceClient, "d564b18a-2890-4152-be3d-e05d784ff72").Extract() if err != nil { panic(err) } @@ -18,14 +18,14 @@ Example to create certificates CSR: "-----BEGIN CERTIFICATE REQUEST-----\nMIIEfzCCAmcCAQAwFDESMBAGA1UEAxMJWW91ciBOYW1lMIICIjANBgkqhkiG9w0B\n-----END CERTIFICATE REQUEST-----\n", } - response, err := certificates.Create(sc, createOpts).Extract() + response, err := certificates.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } Example to update certificates - err := certificates.Update(client, clusterUUID).ExtractErr() + err := certificates.Update(context.TODO(), client, clusterUUID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/clusters/doc.go b/openstack/containerinfra/v1/clusters/doc.go index 9f8d609c9d..75920ec63d 100644 --- a/openstack/containerinfra/v1/clusters/doc.go +++ b/openstack/containerinfra/v1/clusters/doc.go @@ -21,7 +21,7 @@ Example to Create a Cluster MasterLBEnabled: &masterLBEnabled, } - cluster, err := clusters.Create(serviceClient, createOpts).Extract() + cluster, err := clusters.Create(context.TODO(), serviceClient, createOpts).Extract() if err != nil { panic(err) } @@ -29,7 +29,7 @@ Example to Create a Cluster Example to Get a Cluster clusterName := "cluster123" - cluster, err := clusters.Get(serviceClient, clusterName).Extract() + cluster, err := clusters.Get(context.TODO(), serviceClient, clusterName).Extract() if err != nil { panic(err) } @@ -85,7 +85,7 @@ Example to Update a Cluster Value: "True", }, } - clusterUUID, err := clusters.Update(serviceClient, clusterUUID, updateOpts).Extract() + clusterUUID, err := clusters.Update(context.TODO(), serviceClient, clusterUUID, updateOpts).Extract() if err != nil { panic(err) } @@ -96,7 +96,7 @@ Example to Upgrade a Cluster upgradeOpts := clusters.UpgradeOpts{ ClusterTemplate: "0562d357-8641-4759-8fed-8173f02c9633", } - clusterUUID, err := clusters.Upgrade(serviceClient, clusterUUID, upgradeOpts).Extract() + clusterUUID, err := clusters.Upgrade(context.TODO(), serviceClient, clusterUUID, upgradeOpts).Extract() if err != nil { panic(err) } @@ -105,7 +105,7 @@ Example to Upgrade a Cluster Example to Delete a Cluster clusterUUID := "dc6d336e3fc4c0a951b5698cd1236ee" - err := clusters.Delete(serviceClient, clusterUUID).ExtractErr() + err := clusters.Delete(context.TODO(), serviceClient, clusterUUID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/clustertemplates/doc.go b/openstack/containerinfra/v1/clustertemplates/doc.go index 43b93ef5be..beb9e22abe 100644 --- a/openstack/containerinfra/v1/clustertemplates/doc.go +++ b/openstack/containerinfra/v1/clustertemplates/doc.go @@ -33,7 +33,7 @@ Example to Create Cluster Template DNSNameServer: "8.8.8.8", } - clustertemplate, err := clustertemplates.Create(serviceClient, createOpts).Extract() + clustertemplate, err := clustertemplates.Create(context.TODO(), serviceClient, createOpts).Extract() if err != nil { panic(err) } @@ -41,7 +41,7 @@ Example to Create Cluster Template Example to Delete Cluster Template clusterTemplateID := "dc6d336e3fc4c0a951b5698cd1236ee" - err := clustertemplates.Delete(serviceClient, clusterTemplateID).ExtractErr() + err := clustertemplates.Delete(context.TODO(), serviceClient, clusterTemplateID).ExtractErr() if err != nil { panic(err) } @@ -81,7 +81,7 @@ Example to Update Cluster Template }, } - clustertemplate, err := clustertemplates.Update(serviceClient, updateOpts).Extract() + clustertemplate, err := clustertemplates.Update(context.TODO(), serviceClient, updateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/nodegroups/doc.go b/openstack/containerinfra/v1/nodegroups/doc.go index 971153246d..fb8073d672 100644 --- a/openstack/containerinfra/v1/nodegroups/doc.go +++ b/openstack/containerinfra/v1/nodegroups/doc.go @@ -25,7 +25,7 @@ Create a client to use: Example of Getting a node group: - ng, err := nodegroups.Get(client, clusterUUID, nodeGroupUUID).Extract() + ng, err := nodegroups.Get(context.TODO(), client, clusterUUID, nodeGroupUUID).Extract() if err != nil { panic(err) } @@ -57,7 +57,7 @@ Example of Creating a node group: // 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() + cluster, err := clusters.Get(context.TODO(), client, clusterUUID).Extract() if err != nil { panic(err) } @@ -74,7 +74,7 @@ Example of Creating a node group: Labels: labels, } - ng, err := nodegroups.Create(client, clusterUUID, createOpts).Extract() + ng, err := nodegroups.Create(context.TODO(), client, clusterUUID, createOpts).Extract() if err != nil { panic(err) } @@ -95,7 +95,7 @@ Example of Updating a node group: }, } - ng, err = nodegroups.Update(client, clusterUUID, nodeGroupUUID, updateOpts).Extract() + ng, err = nodegroups.Update(context.TODO(), client, clusterUUID, nodeGroupUUID, updateOpts).Extract() if err != nil { panic(err) } @@ -104,7 +104,7 @@ Example of Updating a node group: Example of Deleting a node group: - err = nodegroups.Delete(client, clusterUUID, nodeGroupUUID).ExtractErr() + err = nodegroups.Delete(context.TODO(), client, clusterUUID, nodeGroupUUID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/containerinfra/v1/quotas/doc.go b/openstack/containerinfra/v1/quotas/doc.go index ef6dfb666e..77c2e63ae8 100644 --- a/openstack/containerinfra/v1/quotas/doc.go +++ b/openstack/containerinfra/v1/quotas/doc.go @@ -9,7 +9,7 @@ Example to Create a Quota HardLimit: 10, } - quota, err := quotas.Create(serviceClient, createOpts).Extract() + quota, err := quotas.Create(context.TODO(), serviceClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/dns/v2/recordsets/doc.go b/openstack/dns/v2/recordsets/doc.go index d23b832e43..c1f6f93c56 100644 --- a/openstack/dns/v2/recordsets/doc.go +++ b/openstack/dns/v2/recordsets/doc.go @@ -36,7 +36,7 @@ Example to Create a RecordSet zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" - rr, err := recordsets.Create(dnsClient, zoneID, createOpts).Extract() + rr, err := recordsets.Create(context.TODO(), dnsClient, zoneID, createOpts).Extract() if err != nil { panic(err) } @@ -46,7 +46,7 @@ Example to Delete a RecordSet zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" recordsetID := "d96ed01a-b439-4eb8-9b90-7a9f71017f7b" - err := recordsets.Delete(dnsClient, zoneID, recordsetID).ExtractErr() + err := recordsets.Delete(context.TODO(), dnsClient, zoneID, recordsetID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/dns/v2/transfer/accept/doc.go b/openstack/dns/v2/transfer/accept/doc.go index 2c725f6da1..3fa5bfc26a 100644 --- a/openstack/dns/v2/transfer/accept/doc.go +++ b/openstack/dns/v2/transfer/accept/doc.go @@ -27,7 +27,7 @@ Example to Create a Zone Transfer Accept ZoneTransferRequestID: zoneTransferRequestID, Key: key, } - transferAccept, err := transferAccepts.Create(dnsClient, createOpts).Extract() + transferAccept, err := transferAccepts.Create(context.TODO(), dnsClient, createOpts).Extract() if err != nil { panic(err) } @@ -35,7 +35,7 @@ Example to Create a Zone Transfer Accept Example to Get a Zone Transfer Accept transferAcceptID := "99d10f68-5623-4491-91a0-6daafa32b60e" - transferAccept, err := transferAccepts.Get(dnsClient, transferAcceptID).Extract() + transferAccept, err := transferAccepts.Get(context.TODO(), dnsClient, transferAcceptID).Extract() if err != nil { panic(err) } diff --git a/openstack/dns/v2/transfer/request/doc.go b/openstack/dns/v2/transfer/request/doc.go index 2813edfb69..a057f1e38e 100644 --- a/openstack/dns/v2/transfer/request/doc.go +++ b/openstack/dns/v2/transfer/request/doc.go @@ -26,7 +26,7 @@ Example to Create a Zone Transfer Request TargetProjectID: targetProjectID, Description: "This is a zone transfer request.", } - transferRequest, err := transferRequests.Create(dnsClient, zoneID, createOpts).Extract() + transferRequest, err := transferRequests.Create(context.TODO(), dnsClient, zoneID, createOpts).Extract() if err != nil { panic(err) } @@ -34,7 +34,7 @@ Example to Create a Zone Transfer Request Example to Delete a Zone Transfer Request transferID := "99d10f68-5623-4491-91a0-6daafa32b60e" - err := transferRequests.Delete(dnsClient, transferID).ExtractErr() + err := transferRequests.Delete(context.TODO(), dnsClient, transferID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/dns/v2/zones/doc.go b/openstack/dns/v2/zones/doc.go index ac9082eb10..6bd7d98cb5 100644 --- a/openstack/dns/v2/zones/doc.go +++ b/openstack/dns/v2/zones/doc.go @@ -32,7 +32,7 @@ Example to Create a Zone Description: "This is a zone.", } - zone, err := zones.Create(dnsClient, createOpts).Extract() + zone, err := zones.Create(context.TODO(), dnsClient, createOpts).Extract() if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example to Create a Zone Example to Delete a Zone zoneID := "99d10f68-5623-4491-91a0-6daafa32b60e" - err := zones.Delete(dnsClient, zoneID).ExtractErr() + err := zones.Delete(context.TODO(), dnsClient, zoneID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v2/extensions/admin/roles/doc.go b/openstack/identity/v2/extensions/admin/roles/doc.go index 8e6ad07e8f..59851696cb 100644 --- a/openstack/identity/v2/extensions/admin/roles/doc.go +++ b/openstack/identity/v2/extensions/admin/roles/doc.go @@ -37,7 +37,7 @@ Example to Grant a Role to a User userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := roles.AddUser(identityClient, tenantID, userID, roleID).ExtractErr() + err := roles.AddUser(context.TODO(), identityClient, tenantID, userID, roleID).ExtractErr() if err != nil { panic(err) } @@ -48,7 +48,7 @@ Example to Remove a Role from a User userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := roles.DeleteUser(identityClient, tenantID, userID, roleID).ExtractErr() + err := roles.DeleteUser(context.TODO(), identityClient, tenantID, userID, roleID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v2/tenants/doc.go b/openstack/identity/v2/tenants/doc.go index 476951762b..b14d69a564 100644 --- a/openstack/identity/v2/tenants/doc.go +++ b/openstack/identity/v2/tenants/doc.go @@ -34,7 +34,7 @@ Example to Create a Tenant Enabled: gophercloud.Enabled, } - tenant, err := tenants.Create(identityClient, createOpts).Extract() + tenant, err := tenants.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -48,7 +48,7 @@ Example to Update a Tenant Enabled: gophercloud.Disabled, } - tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract() + tenant, err := tenants.Update(context.TODO(), identityClient, tenantID, updateOpts).Extract() if err != nil { panic(err) } @@ -57,7 +57,7 @@ Example to Delete a Tenant tenantID := "e6db6ed6277c461a853458589063b295" - err := tenants.Delete(identitYClient, tenantID).ExtractErr() + err := tenants.Delete(context.TODO(), identitYClient, tenantID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v2/tokens/doc.go b/openstack/identity/v2/tokens/doc.go index 5375eea872..7cfc367300 100644 --- a/openstack/identity/v2/tokens/doc.go +++ b/openstack/identity/v2/tokens/doc.go @@ -12,7 +12,7 @@ Example to Create an Unscoped Token from a Password Password: "pass" } - token, err := tokens.Create(identityClient, authOpts).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() if err != nil { panic(err) } @@ -25,7 +25,7 @@ Example to Create a Token from a Tenant ID and Password TenantID: "fc394f2ab2df4114bde39905f800dc57" } - token, err := tokens.Create(identityClient, authOpts).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() if err != nil { panic(err) } @@ -38,7 +38,7 @@ Example to Create a Token from a Tenant Name and Password TenantName: "tenantname" } - token, err := tokens.Create(identityClient, authOpts).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOpts).ExtractToken() if err != nil { panic(err) } diff --git a/openstack/identity/v2/users/doc.go b/openstack/identity/v2/users/doc.go index 4c9a4027a3..daf47dd1a6 100644 --- a/openstack/identity/v2/users/doc.go +++ b/openstack/identity/v2/users/doc.go @@ -26,7 +26,7 @@ Example to Create a User Enabled: gophercloud.Enabled, } - user, err := users.Create(identityClient, createOpts).Extract() + user, err := users.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example to Update a User Enabled: gophercloud.Disabled, } - user, err := users.Update(identityClient, userID, updateOpts).Extract() + user, err := users.Update(context.TODO(), identityClient, userID, updateOpts).Extract() if err != nil { panic(err) } @@ -48,7 +48,7 @@ Example to Update a User Example to Delete a User userID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := users.Delete(identityClient, userID).ExtractErr() + err := users.Delete(context.TODO(), identityClient, userID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/domains/doc.go b/openstack/identity/v3/domains/doc.go index d7f73c0195..618b2f6d42 100644 --- a/openstack/identity/v3/domains/doc.go +++ b/openstack/identity/v3/domains/doc.go @@ -29,7 +29,7 @@ Example to Create a Domain Description: "Test domain", } - domain, err := domains.Create(identityClient, createOpts).Extract() + domain, err := domains.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -43,7 +43,7 @@ Example to Update a Domain Enabled: &iFalse, } - domain, err := domains.Update(identityClient, domainID, updateOpts).Extract() + domain, err := domains.Update(context.TODO(), identityClient, domainID, updateOpts).Extract() if err != nil { panic(err) } @@ -51,7 +51,7 @@ Example to Update a Domain Example to Delete a Domain domainID := "0fe36e73809d46aeae6705c39077b1b3" - err := domains.Delete(identityClient, domainID).ExtractErr() + err := domains.Delete(context.TODO(), identityClient, domainID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/endpoints/doc.go b/openstack/identity/v3/endpoints/doc.go index eedbf6d891..f1496c5a89 100644 --- a/openstack/identity/v3/endpoints/doc.go +++ b/openstack/identity/v3/endpoints/doc.go @@ -39,7 +39,7 @@ Example to Create an Endpoint ServiceID: serviceID, } - endpoint, err := endpoints.Create(identityClient, createOpts).Extract() + endpoint, err := endpoints.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -52,7 +52,7 @@ Example to Update an Endpoint Region: "RegionTwo", } - endpoint, err := endpoints.Update(identityClient, endpointID, updateOpts).Extract() + endpoint, err := endpoints.Update(context.TODO(), identityClient, endpointID, updateOpts).Extract() if err != nil { panic(err) } @@ -60,7 +60,7 @@ Example to Update an Endpoint Example to Delete an Endpoint endpointID := "ad59deeec5154d1fa0dcff518596f499" - err := endpoints.Delete(identityClient, endpointID).ExtractErr() + err := endpoints.Delete(context.TODO(), identityClient, endpointID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/ec2credentials/doc.go b/openstack/identity/v3/extensions/ec2credentials/doc.go index 174cea5237..6a47d82d3d 100644 --- a/openstack/identity/v3/extensions/ec2credentials/doc.go +++ b/openstack/identity/v3/extensions/ec2credentials/doc.go @@ -12,7 +12,7 @@ Example to Create an EC2 credential TenantID: projectID, } - credential, err := ec2credentials.Create(identityClient, userID, createOpts).Extract() + credential, err := ec2credentials.Create(context.TODO(), identityClient, userID, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/ec2tokens/doc.go b/openstack/identity/v3/extensions/ec2tokens/doc.go index a30d0faf3a..bd74473da1 100644 --- a/openstack/identity/v3/extensions/ec2tokens/doc.go +++ b/openstack/identity/v3/extensions/ec2tokens/doc.go @@ -13,7 +13,7 @@ Example to Create a Token From an EC2 access and secret keys Secret: "18f4f6761ada4e3795fa5273c30349b9", } - token, err := ec2tokens.Create(identityClient, authOptions).ExtractToken() + token, err := ec2tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -32,7 +32,7 @@ Example to auth a client using EC2 access and secret keys AllowReauth: true, } - err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) + err = openstack.AuthenticateV3(context.TODO(), client, authOptions, gophercloud.EndpointOpts{}) if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/extensions/federation/doc.go index 12ac53d2d4..cb7e6b577c 100644 --- a/openstack/identity/v3/extensions/federation/doc.go +++ b/openstack/identity/v3/extensions/federation/doc.go @@ -46,14 +46,14 @@ Example to Create Mappings }, } - createdMapping, err := federation.CreateMapping(identityClient, "ACME", createOpts).Extract() + createdMapping, err := federation.CreateMapping(context.TODO(), identityClient, "ACME", createOpts).Extract() if err != nil { panic(err) } Example to Get a Mapping - mapping, err := federation.GetMapping(identityClient, "ACME").Extract() + mapping, err := federation.GetMapping(context.TODO(), identityClient, "ACME").Extract() if err != nil { panic(err) } @@ -90,14 +90,14 @@ Example to Update a Mapping }, }, } - updatedMapping, err := federation.UpdateMapping(identityClient, "ACME", updateOpts).Extract() + updatedMapping, err := federation.UpdateMapping(context.TODO(), identityClient, "ACME", updateOpts).Extract() if err != nil { panic(err) } Example to Delete a Mapping - err := federation.DeleteMapping(identityClient, "ACME").ExtractErr() + err := federation.DeleteMapping(context.TODO(), identityClient, "ACME").ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/oauth1/doc.go b/openstack/identity/v3/extensions/oauth1/doc.go index 4c6117671e..c0cfa924aa 100644 --- a/openstack/identity/v3/extensions/oauth1/doc.go +++ b/openstack/identity/v3/extensions/oauth1/doc.go @@ -6,7 +6,7 @@ Example to Create an OAuth1 Consumer createConsumerOpts := oauth1.CreateConsumerOpts{ Description: "My consumer", } - consumer, err := oauth1.CreateConsumer(identityClient, createConsumerOpts).Extract() + consumer, err := oauth1.CreateConsumer(context.TODO(), identityClient, createConsumerOpts).Extract() if err != nil { panic(err) } @@ -22,7 +22,7 @@ Example to Request an unauthorized OAuth1 token OAuthSignatureMethod: oauth1.HMACSHA1, RequestedProjectID: projectID, } - requestToken, err := oauth1.RequestToken(identityClient, requestTokenOpts).Extract() + requestToken, err := oauth1.RequestToken(context.TODO(), identityClient, requestTokenOpts).Extract() if err != nil { panic(err) } @@ -37,7 +37,7 @@ Example to Authorize an unauthorized OAuth1 token {Name: "member"}, }, } - authToken, err := oauth1.AuthorizeToken(identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract() + authToken, err := oauth1.AuthorizeToken(context.TODO(), identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract() if err != nil { panic(err) } @@ -54,7 +54,7 @@ Example to Create an OAuth1 Access Token OAuthVerifier: authToken.OAuthVerifier, OAuthSignatureMethod: oauth1.HMACSHA1, } - accessToken, err := oauth1.CreateAccessToken(identityClient, accessTokenOpts).Extract() + accessToken, err := oauth1.CreateAccessToken(context.TODO(), identityClient, accessTokenOpts).Extract() if err != nil { panic(err) } @@ -93,7 +93,7 @@ Example to Authenticate a client using OAuth1 method OAuthTokenSecret: accessToken.OAuthTokenSecret, OAuthSignatureMethod: oauth1.HMACSHA1, } - err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) + err = openstack.AuthenticateV3(context.TODO(), client, authOptions, gophercloud.EndpointOpts{}) if err != nil { panic(err) } @@ -114,7 +114,7 @@ Example to Create a Token using OAuth1 method OAuthTokenSecret: accessToken.OAuthTokenSecret, OAuthSignatureMethod: oauth1.HMACSHA1, } - err := tokens.Create(identityClient, createOpts).ExtractInto(&oauth1Token) + err := tokens.Create(context.TODO(), identityClient, createOpts).ExtractInto(&oauth1Token) if err != nil { panic(err) } diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go index a1d887080c..6c0bc7da6e 100644 --- a/openstack/identity/v3/extensions/trusts/doc.go +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -18,7 +18,7 @@ Example to Create a Token with Username, Password, and Trust ID TrustID: "de0945a", } - err := tokens.Create(identityClient, createOpts).ExtractInto(&trustToken) + err := tokens.Create(context.TODO(), identityClient, createOpts).ExtractInto(&trustToken) if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example to Create a Trust TrustorUserID: "959ed913a32c4ec88c041c98e61cbbc3", } - trust, err := trusts.Create(identityClient, createOpts).Extract() + trust, err := trusts.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Create a Trust Example to Delete a Trust trustID := "3422b7c113894f5d90665e1a79655e23" - err := trusts.Delete(identityClient, trustID).ExtractErr() + err := trusts.Delete(context.TODO(), identityClient, trustID).ExtractErr() if err != nil { panic(err) } @@ -58,7 +58,7 @@ Example to Delete a Trust Example to Get a Trust trustID := "3422b7c113894f5d90665e1a79655e23" - err := trusts.Get(identityClient, trustID).ExtractErr() + err := trusts.Get(context.TODO(), identityClient, trustID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/groups/doc.go b/openstack/identity/v3/groups/doc.go index 480753af24..357c765bab 100644 --- a/openstack/identity/v3/groups/doc.go +++ b/openstack/identity/v3/groups/doc.go @@ -31,7 +31,7 @@ Example to Create a Group } } - group, err := groups.Create(identityClient, createOpts).Extract() + group, err := groups.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Update a Group Description: "Updated Description for group", } - group, err := groups.Update(identityClient, groupID, updateOpts).Extract() + group, err := groups.Update(context.TODO(), identityClient, groupID, updateOpts).Extract() if err != nil { panic(err) } @@ -52,7 +52,7 @@ Example to Update a Group Example to Delete a Group groupID := "0fe36e73809d46aeae6705c39077b1b3" - err := groups.Delete(identityClient, groupID).ExtractErr() + err := groups.Delete(context.TODO(), identityClient, groupID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/limits/doc.go b/openstack/identity/v3/limits/doc.go index 61b7b91a60..d85c57aa4c 100644 --- a/openstack/identity/v3/limits/doc.go +++ b/openstack/identity/v3/limits/doc.go @@ -4,7 +4,7 @@ Openstack Identity service. Example to Get EnforcementModel - model, err := limits.GetEnforcementModel(identityClient).Extract() + model, err := limits.GetEnforcementModel(context.TODO(), identityClient).Extract() if err != nil { panic(err) } @@ -44,14 +44,14 @@ Example to Create Limits }, } - createdLimits, err := limits.Create(identityClient, batchCreateOpts).Extract() + createdLimits, err := limits.Create(context.TODO(), identityClient, batchCreateOpts).Extract() if err != nil { panic(err) } Example to Get a Limit - limit, err := limits.Get(identityClient, "25a04c7a065c430590881c646cdcdd58").Extract() + limit, err := limits.Get(context.TODO(), identityClient, "25a04c7a065c430590881c646cdcdd58").Extract() if err != nil { panic(err) } @@ -67,7 +67,7 @@ Example to Update a Limit ResourceLimit: &resourceLimit, } - limit, err := limits.Update(identityClient, limitID, updateOpts).Extract() + limit, err := limits.Update(context.TODO(), identityClient, limitID, updateOpts).Extract() if err != nil { panic(err) } @@ -75,7 +75,7 @@ Example to Update a Limit Example to Delete a Limit limitID := "0fe36e73809d46aeae6705c39077b1b3" - err := limits.Delete(identityClient, limitID).ExtractErr() + err := limits.Delete(context.TODO(), identityClient, limitID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/osinherit/doc.go b/openstack/identity/v3/osinherit/doc.go index e979f7710c..f8c5cfc6b3 100644 --- a/openstack/identity/v3/osinherit/doc.go +++ b/openstack/identity/v3/osinherit/doc.go @@ -8,7 +8,7 @@ Example to Assign a Inherited Role to a User to a Domain userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := osinherit.Assign(identityClient, roleID, osinherit.AssignOpts{ + err := osinherit.Assign(context.TODO(), identityClient, roleID, osinherit.AssignOpts{ UserID: userID, domainID: domainID, }).ExtractErr() @@ -23,7 +23,7 @@ Example to Assign a Inherited Role to a User to a Project's subtree userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := osinherit.Assign(identityClient, roleID, osinherit.AssignOpts{ + err := osinherit.Assign(context.TODO(), identityClient, roleID, osinherit.AssignOpts{ UserID: userID, ProjectID: projectID, }).ExtractErr() @@ -38,7 +38,7 @@ Example to validate a Inherited Role to a User to a Project's subtree userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := osinherit.Validate(identityClient, roleID, osinherit.validateOpts{ + err := osinherit.Validate(context.TODO(), identityClient, roleID, osinherit.validateOpts{ UserID: userID, ProjectID: projectID, }).ExtractErr() @@ -53,7 +53,7 @@ Example to unassign a Inherited Role to a User to a Project's subtree userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := osinherit.Unassign(identityClient, roleID, osinherit.UnassignOpts{ + err := osinherit.Unassign(context.TODO(), identityClient, roleID, osinherit.UnassignOpts{ UserID: userID, ProjectID: projectID, }).ExtractErr() diff --git a/openstack/identity/v3/policies/doc.go b/openstack/identity/v3/policies/doc.go index 2d8574739a..a41d6b2eba 100644 --- a/openstack/identity/v3/policies/doc.go +++ b/openstack/identity/v3/policies/doc.go @@ -32,7 +32,7 @@ Example to Create a Policy }, } - policy, err := policies.Create(identityClient, createOpts).Extract() + policy, err := policies.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example to Create a Policy Example to Get a Policy policyID := "0fe36e73809d46aeae6705c39077b1b3" - policy, err := policies.Get(identityClient, policyID).Extract() + policy, err := policies.Get(context.TODO(), identityClient, policyID).Extract() if err != nil { panic(err) } @@ -59,7 +59,7 @@ Example to Update a Policy }, } - policy, err := policies.Update(identityClient, policyID, updateOpts).Extract() + policy, err := policies.Update(context.TODO(), identityClient, policyID, updateOpts).Extract() if err != nil { panic(err) } @@ -69,7 +69,7 @@ Example to Update a Policy Example to Delete a Policy policyID := "0fe36e73809d46aeae6705c39077b1b3" - err := policies.Delete(identityClient, policyID).ExtractErr() + err := policies.Delete(context.TODO(), identityClient, policyID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/projects/doc.go b/openstack/identity/v3/projects/doc.go index e08bf21b5f..bee8760c34 100644 --- a/openstack/identity/v3/projects/doc.go +++ b/openstack/identity/v3/projects/doc.go @@ -30,7 +30,7 @@ Example to Create a Project Tags: []string{"FirstTag", "SecondTag"}, } - project, err := projects.Create(identityClient, createOpts).Extract() + project, err := projects.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -43,7 +43,7 @@ Example to Update a Project Enabled: gophercloud.Disabled, } - project, err := projects.Update(identityClient, projectID, updateOpts).Extract() + project, err := projects.Update(context.TODO(), identityClient, projectID, updateOpts).Extract() if err != nil { panic(err) } @@ -52,7 +52,7 @@ Example to Update a Project Tags: &[]string{"FirstTag"}, } - project, err = projects.Update(identityClient, projectID, updateOpts).Extract() + project, err = projects.Update(context.TODO(), identityClient, projectID, updateOpts).Extract() if err != nil { panic(err) } @@ -60,7 +60,7 @@ Example to Update a Project Example to Delete a Project projectID := "966b3c7d36a24facaf20b7e458bf2192" - err := projects.Delete(identityClient, projectID).ExtractErr() + err := projects.Delete(context.TODO(), identityClient, projectID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/regions/doc.go b/openstack/identity/v3/regions/doc.go index a3d1162560..580c6fb61c 100644 --- a/openstack/identity/v3/regions/doc.go +++ b/openstack/identity/v3/regions/doc.go @@ -31,7 +31,7 @@ Example to Create a Region } } - region, err := regions.Create(identityClient, createOpts).Extract() + region, err := regions.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -47,7 +47,7 @@ Example to Update a Region Description: "Updated Description for region", } - region, err := regions.Update(identityClient, regionID, updateOpts).Extract() + region, err := regions.Update(context.TODO(), identityClient, regionID, updateOpts).Extract() if err != nil { panic(err) } @@ -55,7 +55,7 @@ Example to Update a Region Example to Delete a Region regionID := "TestRegion" - err := regions.Delete(identityClient, regionID).ExtractErr() + err := regions.Delete(context.TODO(), identityClient, regionID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/registeredlimits/doc.go b/openstack/identity/v3/registeredlimits/doc.go index 567a157385..b00e1ae664 100644 --- a/openstack/identity/v3/registeredlimits/doc.go +++ b/openstack/identity/v3/registeredlimits/doc.go @@ -36,7 +36,7 @@ Example to Create a RegisteredLimit }, } - createdRegisteredLimits, err := limits.Create(identityClient, batchCreateOpts).Extract() + createdRegisteredLimits, err := limits.Create(context.TODO(), identityClient, batchCreateOpts).Extract() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Create a RegisteredLimit Example to Get a RegisteredLimit registeredLimitID := "966b3c7d36a24facaf20b7e458bf2192" - registered_limit, err := registeredlimits.Get(client, registeredLimitID).Extract() + registered_limit, err := registeredlimits.Get(context.TODO(), client, registeredLimitID).Extract() if err != nil { panic(err) } @@ -65,7 +65,7 @@ Example to Update a RegisteredLimit ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", } - registered_limit, err := registeredlimits.Update(client, registeredLimitID, updateOpts).Extract() + registered_limit, err := registeredlimits.Update(context.TODO(), client, registeredLimitID, updateOpts).Extract() if err != nil { panic(err) } @@ -73,7 +73,7 @@ Example to Update a RegisteredLimit Example to Delete a RegisteredLimit registeredLimitID := "966b3c7d36a24facaf20b7e458bf2192" - err := registeredlimits.Delete(identityClient, registeredLimitID).ExtractErr() + err := registeredlimits.Delete(context.TODO(), identityClient, registeredLimitID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 578ebeedb2..3a34b07610 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -32,7 +32,7 @@ Example to Create a Role } } - role, err := roles.Create(identityClient, createOpts).Extract() + role, err := roles.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -45,7 +45,7 @@ Example to Update a Role Name: "read only admin", } - role, err := roles.Update(identityClient, roleID, updateOpts).Extract() + role, err := roles.Update(context.TODO(), identityClient, roleID, updateOpts).Extract() if err != nil { panic(err) } @@ -53,7 +53,7 @@ Example to Update a Role Example to Delete a Role roleID := "0fe36e73809d46aeae6705c39077b1b3" - err := roles.Delete(identityClient, roleID).ExtractErr() + err := roles.Delete(context.TODO(), identityClient, roleID).ExtractErr() if err != nil { panic(err) } @@ -108,7 +108,7 @@ Example to Assign a Role to a User in a Project userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := roles.Assign(identityClient, roleID, roles.AssignOpts{ + err := roles.Assign(context.TODO(), identityClient, roleID, roles.AssignOpts{ UserID: userID, ProjectID: projectID, }).ExtractErr() @@ -123,7 +123,7 @@ Example to Unassign a Role From a User in a Project userID := "9df1a02f5eb2416a9781e8b0c022d3ae" roleID := "9fe2ff9ee4384b1894a90878d3e92bab" - err := roles.Unassign(identityClient, roleID, roles.UnassignOpts{ + err := roles.Unassign(context.TODO(), identityClient, roleID, roles.UnassignOpts{ UserID: userID, ProjectID: projectID, }).ExtractErr() diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index 125e996093..7ac1b2abc9 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -32,7 +32,7 @@ Example to Create a Service }, } - service, err := services.Create(identityClient, createOpts).Extract() + service, err := services.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -49,7 +49,7 @@ Example to Update a Service }, } - service, err := services.Update(identityClient, serviceID, updateOpts).Extract() + service, err := services.Update(context.TODO(), identityClient, serviceID, updateOpts).Extract() if err != nil { panic(err) } @@ -57,7 +57,7 @@ Example to Update a Service Example to Delete a Service serviceID := "3c7bbe9a6ecb453ca1789586291380ed" - err := services.Delete(identityClient, serviceID).ExtractErr() + err := services.Delete(context.TODO(), identityClient, serviceID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/identity/v3/tokens/doc.go b/openstack/identity/v3/tokens/doc.go index de74c82ecd..c711d0c28e 100644 --- a/openstack/identity/v3/tokens/doc.go +++ b/openstack/identity/v3/tokens/doc.go @@ -12,7 +12,7 @@ Example to Create a Token From a Username and Password Password: "password", } - token, err := tokens.Create(identityClient, authOptions).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -25,7 +25,7 @@ Example to Create a Token From a Username, Password, and Domain DomainID: "default", } - token, err := tokens.Create(identityClient, authOptions).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -36,7 +36,7 @@ Example to Create a Token From a Username, Password, and Domain DomainName: "default", } - token, err = tokens.Create(identityClient, authOptions).ExtractToken() + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -47,7 +47,7 @@ Example to Create a Token From a Token TokenID: "token_id", } - token, err := tokens.Create(identityClient, authOptions).ExtractToken() + token, err := tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -64,7 +64,7 @@ Example to Create a Token from a Username and Password with Project ID Scope Password: "password", } - token, err = tokens.Create(identityClient, authOptions).ExtractToken() + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -81,7 +81,7 @@ Example to Create a Token from a Username and Password with Domain ID Scope Password: "password", } - token, err = tokens.Create(identityClient, authOptions).ExtractToken() + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } @@ -99,7 +99,7 @@ Example to Create a Token from a Username and Password with Project Name Scope Password: "password", } - token, err = tokens.Create(identityClient, authOptions).ExtractToken() + token, err = tokens.Create(context.TODO(), identityClient, authOptions).ExtractToken() if err != nil { panic(err) } diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index ae984dd6e6..5189bc0c5d 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -36,7 +36,7 @@ Example to Create a User } } - user, err := users.Create(identityClient, createOpts).Extract() + user, err := users.Create(context.TODO(), identityClient, createOpts).Extract() if err != nil { panic(err) } @@ -49,7 +49,7 @@ Example to Update a User Enabled: gophercloud.Disabled, } - user, err := users.Update(identityClient, userID, updateOpts).Extract() + user, err := users.Update(context.TODO(), identityClient, userID, updateOpts).Extract() if err != nil { panic(err) } @@ -65,7 +65,7 @@ Example to Change Password of a User Password: password, } - err := users.ChangePassword(identityClient, userID, changePasswordOpts).ExtractErr() + err := users.ChangePassword(context.TODO(), identityClient, userID, changePasswordOpts).ExtractErr() if err != nil { panic(err) } @@ -73,7 +73,7 @@ Example to Change Password of a User Example to Delete a User userID := "0fe36e73809d46aeae6705c39077b1b3" - err := users.Delete(identityClient, userID).ExtractErr() + err := users.Delete(context.TODO(), identityClient, userID).ExtractErr() if err != nil { panic(err) } @@ -100,7 +100,7 @@ Example to Add a User to a Group groupID := "bede500ee1124ae9b0006ff859758b3a" userID := "0fe36e73809d46aeae6705c39077b1b3" - err := users.AddToGroup(identityClient, groupID, userID).ExtractErr() + err := users.AddToGroup(context.TODO(), identityClient, groupID, userID).ExtractErr() if err != nil { panic(err) @@ -110,7 +110,7 @@ Example to Check Whether a User Belongs to a Group groupID := "bede500ee1124ae9b0006ff859758b3a" userID := "0fe36e73809d46aeae6705c39077b1b3" - ok, err := users.IsMemberOfGroup(identityClient, groupID, userID).Extract() + ok, err := users.IsMemberOfGroup(context.TODO(), identityClient, groupID, userID).Extract() if err != nil { panic(err) } @@ -123,7 +123,7 @@ Example to Remove a User from a Group groupID := "bede500ee1124ae9b0006ff859758b3a" userID := "0fe36e73809d46aeae6705c39077b1b3" - err := users.RemoveFromGroup(identityClient, groupID, userID).ExtractErr() + err := users.RemoveFromGroup(context.TODO(), identityClient, groupID, userID).ExtractErr() if err != nil { panic(err) diff --git a/openstack/image/v2/imagedata/doc.go b/openstack/image/v2/imagedata/doc.go index bdb6583400..f99cf743fd 100644 --- a/openstack/image/v2/imagedata/doc.go +++ b/openstack/image/v2/imagedata/doc.go @@ -11,7 +11,7 @@ Example to Upload Image Data } defer imageData.Close() - err = imagedata.Upload(imageClient, imageID, imageData).ExtractErr() + err = imagedata.Upload(context.TODO(), imageClient, imageID, imageData).ExtractErr() if err != nil { panic(err) } @@ -26,7 +26,7 @@ Example to Stage Image Data } defer imageData.Close() - err = imagedata.Stage(imageClient, imageID, imageData).ExtractErr() + err = imagedata.Stage(context.TODO(), imageClient, imageID, imageData).ExtractErr() if err != nil { panic(err) } @@ -35,7 +35,7 @@ Example to Download Image Data imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" - image, err := imagedata.Download(imageClient, imageID).Extract() + image, err := imagedata.Download(context.TODO(), imageClient, imageID).Extract() if err != nil { panic(err) } diff --git a/openstack/image/v2/imageimport/doc.go b/openstack/image/v2/imageimport/doc.go index dc163ab3b3..8a36af94bf 100644 --- a/openstack/image/v2/imageimport/doc.go +++ b/openstack/image/v2/imageimport/doc.go @@ -4,7 +4,7 @@ Image service Import API information. Example to Get an information about the Import API - importInfo, err := imageimport.Get(imagesClient).Extract() + importInfo, err := imageimport.Get(context.TODO(), imagesClient).Extract() if err != nil { panic(err) } @@ -19,7 +19,7 @@ Example to Create a new image import } imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" - err := imageimport.Create(imagesClient, imageID, createOpts).ExtractErr() + err := imageimport.Create(context.TODO(), imagesClient, imageID, createOpts).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/image/v2/images/doc.go b/openstack/image/v2/images/doc.go index 4f999a63c8..bf1a6cd5b9 100644 --- a/openstack/image/v2/images/doc.go +++ b/openstack/image/v2/images/doc.go @@ -29,7 +29,7 @@ Example to Create an Image Visibility: images.ImageVisibilityPrivate, } - image, err := images.Create(imageClient, createOpts) + image, err := images.Create(context.TODO(), imageClient, createOpts) if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Update an Image }, } - image, err := images.Update(imageClient, imageID, updateOpts).Extract() + image, err := images.Update(context.TODO(), imageClient, imageID, updateOpts).Extract() if err != nil { panic(err) } @@ -52,7 +52,7 @@ Example to Update an Image Example to Delete an Image imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27" - err := images.Delete(imageClient, imageID).ExtractErr() + err := images.Delete(context.TODO(), imageClient, imageID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/image/v2/members/doc.go b/openstack/image/v2/members/doc.go index 69a0555cde..840fea3939 100644 --- a/openstack/image/v2/members/doc.go +++ b/openstack/image/v2/members/doc.go @@ -26,7 +26,7 @@ Example to Add a Member to an Image imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" projectID := "fc404778935a4cebaddcb4788fb3ff2c" - member, err := members.Create(imageClient, imageID, projectID).Extract() + member, err := members.Create(context.TODO(), imageClient, imageID, projectID).Extract() if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example to Update the Status of a Member Status: "accepted", } - member, err := members.Update(imageClient, imageID, projectID, updateOpts).Extract() + member, err := members.Update(context.TODO(), imageClient, imageID, projectID, updateOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Delete a Member from an Image imageID := "2b6cacd4-cfd6-4b95-8302-4c04ccf0be3f" projectID := "fc404778935a4cebaddcb4788fb3ff2c" - err := members.Delete(imageClient, imageID, projectID).ExtractErr() + err := members.Delete(context.TODO(), imageClient, imageID, projectID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/image/v2/tasks/doc.go b/openstack/image/v2/tasks/doc.go index f723bd6295..a95b134340 100644 --- a/openstack/image/v2/tasks/doc.go +++ b/openstack/image/v2/tasks/doc.go @@ -24,7 +24,7 @@ Example to List Tasks Example to Get a Task - task, err := tasks.Get(imagesClient, "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() + task, err := tasks.Get(context.TODO(), imagesClient, "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() if err != nil { panic(err) } @@ -45,7 +45,7 @@ Example to Create a Task }, } - task, err := tasks.Create(imagesClient, createOpts).Extract() + task, err := tasks.Create(context.TODO(), imagesClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/keymanager/v1/acls/doc.go b/openstack/keymanager/v1/acls/doc.go index d61940cb08..d9e85dd6ca 100644 --- a/openstack/keymanager/v1/acls/doc.go +++ b/openstack/keymanager/v1/acls/doc.go @@ -5,7 +5,7 @@ All functions have a Secret and Container equivalent. Example to Get a Secret's ACL - acl, err := acls.GetSecretACL(client, secretID).Extract() + acl, err := acls.GetSecretACL(context.TODO(), client, secretID).Extract() if err != nil { panic(err) } @@ -22,7 +22,7 @@ Example to Set a Secret's ACL ProjectAccess: &iFalse, } - aclRef, err := acls.SetSecretACL(client, secretID, setOpts).Extract() + aclRef, err := acls.SetSecretACL(context.TODO(), client, secretID, setOpts).Extract() if err != nil { panic(err) } @@ -37,7 +37,7 @@ Example to Update a Secret's ACL users: &users, } - aclRef, err := acls.UpdateSecretACL(client, secretID, setOpts).Extract() + aclRef, err := acls.UpdateSecretACL(context.TODO(), client, secretID, setOpts).Extract() if err != nil { panic(err) } @@ -46,7 +46,7 @@ Example to Update a Secret's ACL Example to Delete a Secret's ACL - err := acls.DeleteSecretACL(client, secretID).ExtractErr() + err := acls.DeleteSecretACL(context.TODO(), client, secretID).ExtractErr() if err != nil { panci(err) } diff --git a/openstack/keymanager/v1/containers/doc.go b/openstack/keymanager/v1/containers/doc.go index 7fdb4ca2c1..83b74db679 100644 --- a/openstack/keymanager/v1/containers/doc.go +++ b/openstack/keymanager/v1/containers/doc.go @@ -31,7 +31,7 @@ Example to Create a Container }, } - container, err := containers.Create(client, createOpts).Extract() + container, err := containers.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example to Create a Container Example to Delete a Container - err := containers.Delete(client, containerID).ExtractErr() + err := containers.Delete(context.TODO(), client, containerID).ExtractErr() if err != nil { panic(err) } @@ -66,7 +66,7 @@ Example to Create a Consumer of a Container URL: "http://example.com", } - container, err := containers.CreateConsumer(client, containerID, createOpts).Extract() + container, err := containers.CreateConsumer(context.TODO(), client, containerID, createOpts).Extract() if err != nil { panic(err) } @@ -78,7 +78,7 @@ Example to Delete a Consumer of a Container URL: "http://example.com", } - container, err := containers.DeleteConsumer(client, containerID, deleteOpts).Extract() + container, err := containers.DeleteConsumer(context.TODO(), client, containerID, deleteOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/keymanager/v1/orders/doc.go b/openstack/keymanager/v1/orders/doc.go index d7d96d2671..cb0ca01d80 100644 --- a/openstack/keymanager/v1/orders/doc.go +++ b/openstack/keymanager/v1/orders/doc.go @@ -28,7 +28,7 @@ Example to Create a Order }, } - order, err := orders.Create(client, createOpts).Extract() + order, err := orders.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -37,7 +37,7 @@ Example to Create a Order Example to Delete a Order - err := orders.Delete(client, orderID).ExtractErr() + err := orders.Delete(context.TODO(), client, orderID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/keymanager/v1/secrets/doc.go b/openstack/keymanager/v1/secrets/doc.go index 67d951740a..fc18a9c415 100644 --- a/openstack/keymanager/v1/secrets/doc.go +++ b/openstack/keymanager/v1/secrets/doc.go @@ -29,7 +29,7 @@ Example to List Secrets Example to Get a Secret - secret, err := secrets.Get(client, secretID).Extract() + secret, err := secrets.Get(context.TODO(), client, secretID).Extract() if err != nil { panic(err) } @@ -39,7 +39,7 @@ Example to Get a Secret Example to Get a Payload // if "Extract" method is not called, the HTTP connection will remain consumed - payload, err := secrets.GetPayload(client, secretID).Extract() + payload, err := secrets.GetPayload(context.TODO(), client, secretID).Extract() if err != nil { panic(err) } @@ -58,7 +58,7 @@ Example to Create a Secrets SecretType: secrets.OpaqueSecret, } - secret, err := secrets.Create(client, createOpts).Extract() + secret, err := secrets.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -72,14 +72,14 @@ Example to Add a Payload Payload: "super-secret", } - err := secrets.Update(client, secretID, updateOpts).ExtractErr() + err := secrets.Update(context.TODO(), client, secretID, updateOpts).ExtractErr() if err != nil { panic(err) } Example to Delete a Secrets - err := secrets.Delete(client, secretID).ExtractErr() + err := secrets.Delete(context.TODO(), client, secretID).ExtractErr() if err != nil { panic(err) } @@ -91,7 +91,7 @@ Example to Create Metadata for a Secret "something": "something else", } - ref, err := secrets.CreateMetadata(client, secretID, createOpts).Extract() + ref, err := secrets.CreateMetadata(context.TODO(), client, secretID, createOpts).Extract() if err != nil { panic(err) } @@ -100,7 +100,7 @@ Example to Create Metadata for a Secret Example to Get Metadata for a Secret - metadata, err := secrets.GetMetadata(client, secretID).Extract() + metadata, err := secrets.GetMetadata(context.TODO(), client, secretID).Extract() if err != nil { panic(err) } @@ -114,7 +114,7 @@ Example to Add Metadata to a Secret Value: "bar", } - err := secrets.CreateMetadatum(client, secretID, metadatumOpts).ExtractErr() + err := secrets.CreateMetadatum(context.TODO(), client, secretID, metadatumOpts).ExtractErr() if err != nil { panic(err) } @@ -126,7 +126,7 @@ Example to Update Metadata of a Secret Value: "bar", } - metadatum, err := secrets.UpdateMetadatum(client, secretID, metadatumOpts).Extract() + metadatum, err := secrets.UpdateMetadatum(context.TODO(), client, secretID, metadatumOpts).Extract() if err != nil { panic(err) } @@ -135,7 +135,7 @@ Example to Update Metadata of a Secret Example to Delete Metadata of a Secret - err := secrets.DeleteMetadatum(client, secretID, "foo").ExtractErr() + err := secrets.DeleteMetadatum(context.TODO(), client, secretID, "foo").ExtractErr() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/amphorae/doc.go b/openstack/loadbalancer/v2/amphorae/doc.go index a9e620a445..a62715d14a 100644 --- a/openstack/loadbalancer/v2/amphorae/doc.go +++ b/openstack/loadbalancer/v2/amphorae/doc.go @@ -26,7 +26,7 @@ Example to Failover an amphora ampID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := amphorae.Failover(octaviaClient, ampID).ExtractErr() + err := amphorae.Failover(context.TODO(), octaviaClient, ampID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/flavorprofiles/doc.go b/openstack/loadbalancer/v2/flavorprofiles/doc.go index 4fcddeb479..445af2d1b1 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/doc.go +++ b/openstack/loadbalancer/v2/flavorprofiles/doc.go @@ -28,7 +28,7 @@ Example to Create a FlavorProfile FlavorData: "{\"loadbalancer_topology\": \"SINGLE\"}", } - flavorProfile, err := flavorprofiles.Create(octaviaClient, createOpts).Extract() + flavorProfile, err := flavorprofiles.Create(context.TODO(), octaviaClient, createOpts).Extract() if err != nil { panic(err) } @@ -41,7 +41,7 @@ Example to Update a FlavorProfile Name: "amphora-single-updated", } - flavorProfile, err := flavorprofiles.Update(octaviaClient, flavorProfileID, updateOpts).Extract() + flavorProfile, err := flavorprofiles.Update(context.TODO(), octaviaClient, flavorProfileID, updateOpts).Extract() if err != nil { panic(err) } @@ -49,7 +49,7 @@ Example to Update a FlavorProfile Example to Delete a FlavorProfile flavorProfileID := "dd6a26af-8085-4047-a62b-3080f4c76521" - err := flavorprofiles.Delete(octaviaClient, flavorProfileID).ExtractErr() + err := flavorprofiles.Delete(context.TODO(), octaviaClient, flavorProfileID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/flavors/doc.go b/openstack/loadbalancer/v2/flavors/doc.go index 13eaccd3d3..34139cae47 100644 --- a/openstack/loadbalancer/v2/flavors/doc.go +++ b/openstack/loadbalancer/v2/flavors/doc.go @@ -29,7 +29,7 @@ Example to Create a Flavor FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", } - flavor, err := flavors.Create(octaviaClient, createOpts).Extract() + flavor, err := flavors.Create(context.TODO(), octaviaClient, createOpts).Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example to Update a Flavor Name: "New name", } - flavor, err := flavors.Update(octaviaClient, flavorID, updateOpts).Extract() + flavor, err := flavors.Update(context.TODO(), octaviaClient, flavorID, updateOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Update a Flavor Example to Delete a Flavor flavorID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := flavors.Delete(octaviaClient, flavorID).ExtractErr() + err := flavors.Delete(context.TODO(), octaviaClient, flavorID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/l7policies/doc.go b/openstack/loadbalancer/v2/l7policies/doc.go index e8ff692166..1b02b92304 100644 --- a/openstack/loadbalancer/v2/l7policies/doc.go +++ b/openstack/loadbalancer/v2/l7policies/doc.go @@ -10,7 +10,7 @@ Example to Create a L7Policy Action: l7policies.ActionRedirectToURL, RedirectURL: "http://www.example.com", } - l7policy, err := l7policies.Create(lbClient, createOpts).Extract() + l7policy, err := l7policies.Create(context.TODO(), lbClient, createOpts).Extract() if err != nil { panic(err) } @@ -34,7 +34,7 @@ Example to List L7Policies Example to Get a L7Policy - l7policy, err := l7policies.Get(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d").Extract() + l7policy, err := l7policies.Get(context.TODO(), lbClient, "023f2e34-7806-443b-bfae-16c324569a3d").Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example to Get a L7Policy Example to Delete a L7Policy l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := l7policies.Delete(lbClient, l7policyID).ExtractErr() + err := l7policies.Delete(context.TODO(), lbClient, l7policyID).ExtractErr() if err != nil { panic(err) } @@ -54,7 +54,7 @@ Example to Update a L7Policy updateOpts := l7policies.UpdateOpts{ Name: &name, } - l7policy, err := l7policies.Update(lbClient, l7policyID, updateOpts).Extract() + l7policy, err := l7policies.Update(context.TODO(), lbClient, l7policyID, updateOpts).Extract() if err != nil { panic(err) } @@ -67,7 +67,7 @@ Example to Create a Rule CompareType: l7policies.CompareTypeRegex, Value: "/images*", } - rule, err := l7policies.CreateRule(lbClient, l7policyID, createOpts).Extract() + rule, err := l7policies.CreateRule(context.TODO(), lbClient, l7policyID, createOpts).Extract() if err != nil { panic(err) } @@ -92,7 +92,7 @@ Example to List L7 Rules Example to Get a l7 rule - l7rule, err := l7policies.GetRule(lbClient, "023f2e34-7806-443b-bfae-16c324569a3d", "53ad8ab8-40fa-11e8-a508-00224d6b7bc1").Extract() + l7rule, err := l7policies.GetRule(context.TODO(), lbClient, "023f2e34-7806-443b-bfae-16c324569a3d", "53ad8ab8-40fa-11e8-a508-00224d6b7bc1").Extract() if err != nil { panic(err) } @@ -101,7 +101,7 @@ Example to Delete a l7 rule l7policyID := "d67d56a6-4a86-4688-a282-f46444705c64" ruleID := "64dba99f-8af8-4200-8882-e32a0660f23e" - err := l7policies.DeleteRule(lbClient, l7policyID, ruleID).ExtractErr() + err := l7policies.DeleteRule(context.TODO(), lbClient, l7policyID, ruleID).ExtractErr() if err != nil { panic(err) } @@ -115,7 +115,7 @@ Example to Update a Rule CompareType: l7policies.CompareTypeRegex, Value: "/images/special*", } - rule, err := l7policies.UpdateRule(lbClient, l7policyID, ruleID, updateOpts).Extract() + rule, err := l7policies.UpdateRule(context.TODO(), lbClient, l7policyID, ruleID, updateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/listeners/doc.go b/openstack/loadbalancer/v2/listeners/doc.go index 8e9ae86c42..2659715f80 100644 --- a/openstack/loadbalancer/v2/listeners/doc.go +++ b/openstack/loadbalancer/v2/listeners/doc.go @@ -34,7 +34,7 @@ Example to Create a Listener Tags: []string{"test", "stage"}, } - listener, err := listeners.Create(networkClient, createOpts).Extract() + listener, err := listeners.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -53,7 +53,7 @@ Example to Update a Listener Tags: &newTags, } - listener, err := listeners.Update(networkClient, listenerID, updateOpts).Extract() + listener, err := listeners.Update(context.TODO(), networkClient, listenerID, updateOpts).Extract() if err != nil { panic(err) } @@ -61,7 +61,7 @@ Example to Update a Listener Example to Delete a Listener listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := listeners.Delete(networkClient, listenerID).ExtractErr() + err := listeners.Delete(context.TODO(), networkClient, listenerID).ExtractErr() if err != nil { panic(err) } @@ -69,7 +69,7 @@ Example to Delete a Listener Example to Get the Statistics of a Listener listenerID := "d67d56a6-4a86-4688-a282-f46444705c64" - stats, err := listeners.GetStats(networkClient, listenerID).Extract() + stats, err := listeners.GetStats(context.TODO(), networkClient, listenerID).Extract() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/loadbalancers/doc.go b/openstack/loadbalancer/v2/loadbalancers/doc.go index 4aca2e3c52..0e318558cd 100644 --- a/openstack/loadbalancer/v2/loadbalancers/doc.go +++ b/openstack/loadbalancer/v2/loadbalancers/doc.go @@ -34,7 +34,7 @@ Example to Create a Load Balancer Tags: []string{"test", "stage"}, } - lb, err := loadbalancers.Create(networkClient, createOpts).Extract() + lb, err := loadbalancers.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -82,7 +82,7 @@ Example to Create a fully populated Load Balancer }}, } - lb, err := loadbalancers.Create(networkClient, createOpts).Extract() + lb, err := loadbalancers.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -94,7 +94,7 @@ Example to Update a Load Balancer updateOpts := loadbalancers.UpdateOpts{ Name: &name, } - lb, err := loadbalancers.Update(networkClient, lbID, updateOpts).Extract() + lb, err := loadbalancers.Update(context.TODO(), networkClient, lbID, updateOpts).Extract() if err != nil { panic(err) } @@ -107,7 +107,7 @@ Example to Delete a Load Balancers lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := loadbalancers.Delete(networkClient, lbID, deleteOpts).ExtractErr() + err := loadbalancers.Delete(context.TODO(), networkClient, lbID, deleteOpts).ExtractErr() if err != nil { panic(err) } @@ -115,7 +115,7 @@ Example to Delete a Load Balancers Example to Get the Status of a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - status, err := loadbalancers.GetStatuses(networkClient, LBID).Extract() + status, err := loadbalancers.GetStatuses(context.TODO(), networkClient, LBID).Extract() if err != nil { panic(err) } @@ -123,7 +123,7 @@ Example to Get the Status of a Load Balancer Example to Get the Statistics of a Load Balancer lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - stats, err := loadbalancers.GetStats(networkClient, LBID).Extract() + stats, err := loadbalancers.GetStats(context.TODO(), networkClient, LBID).Extract() if err != nil { panic(err) } @@ -132,7 +132,7 @@ Example to Failover a Load Balancers lbID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := loadbalancers.Failover(networkClient, lbID).ExtractErr() + err := loadbalancers.Failover(context.TODO(), networkClient, lbID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/monitors/doc.go b/openstack/loadbalancer/v2/monitors/doc.go index 2a49b6abae..f471084cfd 100644 --- a/openstack/loadbalancer/v2/monitors/doc.go +++ b/openstack/loadbalancer/v2/monitors/doc.go @@ -36,7 +36,7 @@ Example to Create a Monitor ExpectedCodes: "200-299", } - monitor, err := monitors.Create(networkClient, createOpts).Extract() + monitor, err := monitors.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -55,7 +55,7 @@ Example to Update a Monitor ExpectedCodes: "301", } - monitor, err := monitors.Update(networkClient, monitorID, updateOpts).Extract() + monitor, err := monitors.Update(context.TODO(), networkClient, monitorID, updateOpts).Extract() if err != nil { panic(err) } @@ -63,7 +63,7 @@ Example to Update a Monitor Example to Delete a Monitor monitorID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := monitors.Delete(networkClient, monitorID).ExtractErr() + err := monitors.Delete(context.TODO(), networkClient, monitorID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/pools/doc.go b/openstack/loadbalancer/v2/pools/doc.go index a04b9c819d..f5156d661a 100644 --- a/openstack/loadbalancer/v2/pools/doc.go +++ b/openstack/loadbalancer/v2/pools/doc.go @@ -32,7 +32,7 @@ Example to Create a Pool LoadbalancerID: "79e05663-7f03-45d2-a092-8b94062f22ab", } - pool, err := pools.Create(networkClient, createOpts).Extract() + pool, err := pools.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -47,7 +47,7 @@ Example to Update a Pool Tags: &newTags, } - pool, err := pools.Update(networkClient, poolID, updateOpts).Extract() + pool, err := pools.Update(context.TODO(), networkClient, poolID, updateOpts).Extract() if err != nil { panic(err) } @@ -55,7 +55,7 @@ Example to Update a Pool Example to Delete a Pool poolID := "d67d56a6-4a86-4688-a282-f46444705c64" - err := pools.Delete(networkClient, poolID).ExtractErr() + err := pools.Delete(context.TODO(), networkClient, poolID).ExtractErr() if err != nil { panic(err) } @@ -95,7 +95,7 @@ Example to Create a Member Weight: &weight, } - member, err := pools.CreateMember(networkClient, poolID, createOpts).Extract() + member, err := pools.CreateMember(context.TODO(), networkClient, poolID, createOpts).Extract() if err != nil { panic(err) } @@ -111,7 +111,7 @@ Example to Update a Member Weight: &weight, } - member, err := pools.UpdateMember(networkClient, poolID, memberID, updateOpts).Extract() + member, err := pools.UpdateMember(context.TODO(), networkClient, poolID, memberID, updateOpts).Extract() if err != nil { panic(err) } @@ -121,7 +121,7 @@ Example to Delete a Member poolID := "d67d56a6-4a86-4688-a282-f46444705c64" memberID := "64dba99f-8af8-4200-8882-e32a0660f23e" - err := pools.DeleteMember(networkClient, poolID, memberID).ExtractErr() + err := pools.DeleteMember(context.TODO(), networkClient, poolID, memberID).ExtractErr() if err != nil { panic(err) } @@ -149,7 +149,7 @@ Example to Update Members: } members := []pools.BatchUpdateMemberOpts{member1, member2} - err := pools.BatchUpdateMembers(networkClient, poolID, members).ExtractErr() + err := pools.BatchUpdateMembers(context.TODO(), networkClient, poolID, members).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/loadbalancer/v2/quotas/doc.go b/openstack/loadbalancer/v2/quotas/doc.go index 1dc27c67b2..c2c63512c3 100644 --- a/openstack/loadbalancer/v2/quotas/doc.go +++ b/openstack/loadbalancer/v2/quotas/doc.go @@ -4,7 +4,7 @@ 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() + quotasInfo, err := quotas.Get(context.TODO(), networkClient, projectID).Extract() if err != nil { log.Fatal(err) } @@ -24,7 +24,7 @@ Example to Update project quotas L7Policy: gophercloud.IntToPointer(50), L7Rule: gophercloud.IntToPointer(100), } - quotasInfo, err := quotas.Update(networkClient, projectID) + quotasInfo, err := quotas.Update(context.TODO(), networkClient, projectID) if err != nil { log.Fatal(err) } diff --git a/openstack/messaging/v2/claims/doc.go b/openstack/messaging/v2/claims/doc.go index 1d9b1d0e83..8453db44d9 100644 --- a/openstack/messaging/v2/claims/doc.go +++ b/openstack/messaging/v2/claims/doc.go @@ -12,7 +12,7 @@ Example to Create a Claim on a specified Zaqar queue queueName := "my_queue" - messages, err := claims.Create(messagingClient, queueName, createOpts).Extract() + messages, err := claims.Create(context.TODO(), messagingClient, queueName, createOpts).Extract() if err != nil { panic(err) } @@ -22,7 +22,7 @@ Example to get a claim for a specified Zaqar queue queueName := "my_queue" claimID := "123456789012345678" - claim, err := claims.Get(messagingClient, queueName, claimID).Extract() + claim, err := claims.Get(context.TODO(), messagingClient, queueName, claimID).Extract() if err != nil { panic(err) } @@ -36,7 +36,7 @@ Example to update a claim for a specified Zaqar queue queueName := "my_queue" - err := claims.Update(messagingClient, queueName, claimID, updateOpts).ExtractErr() + err := claims.Update(context.TODO(), messagingClient, queueName, claimID, updateOpts).ExtractErr() if err != nil { panic(err) } @@ -45,7 +45,7 @@ Example to delete a claim for a specified Zaqar queue queueName := "my_queue" - err := claims.Delete(messagingClient, queueName, claimID).ExtractErr() + err := claims.Delete(context.TODO(), messagingClient, queueName, claimID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go index f48beb62c5..ca3eb7b449 100644 --- a/openstack/messaging/v2/messages/doc.go +++ b/openstack/messaging/v2/messages/doc.go @@ -49,7 +49,7 @@ Example to Create Messages }, } - resources, err := messages.Create(client, queueName, createOpts).Extract() + resources, err := messages.Create(context.TODO(), client, queueName, createOpts).Extract() if err != nil { panic(err) } @@ -62,7 +62,7 @@ Example to Get a set of Messages IDs: "123456", } - messagesList, err := messages.GetMessages(client, createdQueueName, getMessageOpts).Extract() + messagesList, err := messages.GetMessages(context.TODO(), client, createdQueueName, getMessageOpts).Extract() if err != nil { panic(err) } @@ -72,7 +72,7 @@ Example to get a singular Message queueName := "my_queue" messageID := "123456" - message, err := messages.Get(client, queueName, messageID).Extract() + message, err := messages.Get(context.TODO(), client, queueName, messageID).Extract() if err != nil { panic(err) } @@ -85,7 +85,7 @@ Example to Delete a set of Messages IDs: []string{"9988776655"}, } - err := messages.DeleteMessages(client, queueName, deleteMessagesOpts).ExtractErr() + err := messages.DeleteMessages(context.TODO(), client, queueName, deleteMessagesOpts).ExtractErr() if err != nil { panic(err) } @@ -98,7 +98,7 @@ Example to Pop a set of Messages Pop: 5, } - resources, err := messages.PopMessages(client, queueName, popMessagesOpts).Extract() + resources, err := messages.PopMessages(context.TODO(), client, queueName, popMessagesOpts).Extract() if err != nil { panic(err) } @@ -113,7 +113,7 @@ Example to Delete a singular Message ClaimID: "12345", } - err := messages.Delete(client), queueName, messageID, deleteOpts).ExtractErr() + err := messages.Delete(context.TODO(), client), queueName, messageID, deleteOpts).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index ab107eefda..803c84a9ab 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(cclient, listOpts) - err = pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - queues, err := queues.ExtractQueues(page) - if err != nil { - panic(err) - } + err = pager.EachPage(context.TODO(), func(_ context.Context, 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 @@ -37,7 +37,7 @@ Example to Create a Queue Extra: map[string]interface{}{"description": "Test queue."}, } - err := queues.Create(client, createOpts).ExtractErr() + err := queues.Create(context.TODO(), client, createOpts).ExtractErr() if err != nil { panic(err) } @@ -57,28 +57,28 @@ Example to Update a Queue }, } - updateResult, err := queues.Update(client, queueName, updateOpts).Extract() + updateResult, err := queues.Update(context.TODO(), client, queueName, updateOpts).Extract() if err != nil { panic(err) } Example to Get a Queue - queue, err := queues.Get(client, queueName).Extract() + queue, err := queues.Get(context.TODO(), client, queueName).Extract() if err != nil { panic(err) } Example to Delete a Queue - err := queues.Delete(client, queueName).ExtractErr() + err := queues.Delete(context.TODO(), client, queueName).ExtractErr() if err != nil { panic(err) } Example to Get Message Stats of a Queue - queueStats, err := queues.GetStats(client, queueName).Extract() + queueStats, err := queues.GetStats(context.TODO(), client, queueName).Extract() if err != nil { panic(err) } @@ -90,7 +90,7 @@ Example to Share a queue Methods: []queues.ShareMethod{queues.MethodGet}, } - queueShare, err := queues.Share(client, queueName, shareOpts).Extract() + queueShare, err := queues.Share(context.TODO(), client, queueName, shareOpts).Extract() if err != nil { panic(err) } @@ -103,7 +103,7 @@ Example to Purge a queue }, } - err := queues.Purge(client, queueName, purgeOpts).ExtractErr() + err := queues.Purge(context.TODO(), client, queueName, purgeOpts).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/agents/doc.go b/openstack/networking/v2/extensions/agents/doc.go index f545d95dcd..4e4b91aa19 100644 --- a/openstack/networking/v2/extensions/agents/doc.go +++ b/openstack/networking/v2/extensions/agents/doc.go @@ -24,7 +24,7 @@ Example of Listing Agents Example to Get an Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" - agent, err := agents.Get(networkClient, agentID).Extract() + agent, err := agents.Get(context.TODO(), networkClient, agentID).Extract() if err != nil { panic(err) } @@ -38,7 +38,7 @@ Example to Update an Agent AdminStateUp: &adminStateUp, } agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" - agent, err := agents.Update(networkClient, agentID, updateOpts).Extract() + agent, err := agents.Update(context.TODO(), networkClient, agentID, updateOpts).Extract() if err != nil { panic(err) } @@ -46,7 +46,7 @@ Example to Update an Agent Example to Delete an Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" - err := agents.Delete(networkClient, agentID).ExtractErr() + err := agents.Delete(context.TODO(), networkClient, agentID).ExtractErr() if err != nil { panic(err) } @@ -54,7 +54,7 @@ Example to Delete an Agent Example to List Networks hosted by a DHCP Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" - networks, err := agents.ListDHCPNetworks(networkClient, agentID).Extract() + networks, err := agents.ListDHCPNetworks(context.TODO(), networkClient, agentID).Extract() if err != nil { panic(err) } @@ -69,7 +69,7 @@ Example to Schedule a network to a DHCP Agent opts := &agents.ScheduleDHCPNetworkOpts{ NetworkID: "1ae075ca-708b-4e66-b4a7-b7698632f05f", } - err := agents.ScheduleDHCPNetwork(networkClient, agentID, opts).ExtractErr() + err := agents.ScheduleDHCPNetwork(context.TODO(), networkClient, agentID, opts).ExtractErr() if err != nil { panic(err) } @@ -78,7 +78,7 @@ Example to Remove a network from a DHCP Agent agentID := "76af7b1f-d61b-4526-94f7-d2e14e2698df" networkID := "1ae075ca-708b-4e66-b4a7-b7698632f05f" - err := agents.RemoveDHCPNetwork(networkClient, agentID, networkID).ExtractErr() + err := agents.RemoveDHCPNetwork(context.TODO(), networkClient, agentID, networkID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/attributestags/doc.go b/openstack/networking/v2/extensions/attributestags/doc.go index f34b1e2e30..b511a05034 100644 --- a/openstack/networking/v2/extensions/attributestags/doc.go +++ b/openstack/networking/v2/extensions/attributestags/doc.go @@ -7,31 +7,31 @@ 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(context.TODO(), client, createOpts).Extract() tagReplaceAllOpts := attributestags.ReplaceAllOpts{ Tags: []string{"abc", "123"}, } - attributestags.ReplaceAll(conn, "networks", network.ID, tagReplaceAllOpts) + attributestags.ReplaceAll(context.TODO(), client, "networks", network.ID, tagReplaceAllOpts) Example to List all Resource Tags - tags, err = attributestags.List(conn, "networks", network.ID).Extract() + tags, err = attributestags.List(context.TODO(), client, "networks", network.ID).Extract() Example to Delete all Resource Tags - err = attributestags.DeleteAll(conn, "networks", network.ID).ExtractErr() + err = attributestags.DeleteAll(context.TODO(), client, "networks", network.ID).ExtractErr() Example to Add a tag to a Resource - err = attributestags.Add(client, "networks", network.ID, "atag").ExtractErr() + err = attributestags.Add(context.TODO(), 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(context.TODO(), client, "networks", network.ID, "atag").ExtractErr() Example to confirm if a tag exists on a resource - exists, _ := attributestags.Confirm(client, "networks", network.ID, "atag").Extract() + exists, _ := attributestags.Confirm(context.TODO(), client, "networks", network.ID, "atag").Extract() */ package attributestags diff --git a/openstack/networking/v2/extensions/bgp/peers/doc.go b/openstack/networking/v2/extensions/bgp/peers/doc.go index 0c497279d4..4f70bfc179 100644 --- a/openstack/networking/v2/extensions/bgp/peers/doc.go +++ b/openstack/networking/v2/extensions/bgp/peers/doc.go @@ -7,7 +7,7 @@ Package peers contains the functionality for working with Neutron bgp peers. Example: - pages, err := peers.List(c).AllPages(context.TODO()) + pages, err := peers.List(client).AllPages(context.TODO()) if err != nil { log.Panic(err) } @@ -23,7 +23,7 @@ Example: 2. Get BGP Peer, a.k.a. GET /bgp-peers/{id} Example: - p, err := peers.Get(c, id).Extract() + p, err := peers.Get(context.TODO(), client, id).Extract() if err != nil { log.Panic(err) @@ -39,7 +39,7 @@ Example: opts.RemoteAS = 20000 opts.Name = "gophercloud-testing-bgp-peer" opts.PeerIP = "192.168.0.1" - r, err := peers.Create(c, opts).Extract() + r, err := peers.Create(context.TODO(), client, opts).Extract() if err != nil { log.Panic(err) } @@ -49,7 +49,7 @@ Example: Example: - err := peers.Delete(c, bgpPeerID).ExtractErr() + err := peers.Delete(context.TODO(), client, bgpPeerID).ExtractErr() if err != nil { log.Panic(err) } @@ -63,7 +63,7 @@ Example: var opt peers.UpdateOpts opt.Name = "peer-name-updated" opt.Password = "superStrong" - p, err := peers.Update(c, id, opts).Extract() + p, err := peers.Update(context.TODO(), client, id, opts).Extract() if err != nil { log.Panic(err) } diff --git a/openstack/networking/v2/extensions/bgp/speakers/doc.go b/openstack/networking/v2/extensions/bgp/speakers/doc.go index c1d6f2b7e0..c772da6495 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/doc.go +++ b/openstack/networking/v2/extensions/bgp/speakers/doc.go @@ -8,7 +8,7 @@ Package speakers contains the functionality for working with Neutron bgp speaker Example: - pages, err := speakers.List(c).AllPages(context.TODO()) + pages, err := speakers.List(client).AllPages(context.TODO()) if err != nil { log.Panic(err) } @@ -26,7 +26,7 @@ Example: Example: - speaker, err := speakers.Get(c, id).Extract() + speaker, err := speakers.Get(context.TODO(), client, id).Extract() if err != nil { log.Panic(nil) } @@ -45,7 +45,7 @@ Example: LocalAS: "2000", Networks: []string{}, } - r, err := speakers.Create(c, opts).Extract() + r, err := speakers.Create(context.TODO(), client, opts).Extract() if err != nil { log.Panic(err) } @@ -72,7 +72,7 @@ Example: AdvertiseTenantNetworks: false, AdvertiseFloatingIPHostRoutes: true, } - spk, err := speakers.Update(c, bgpSpeakerID, opts).Extract() + spk, err := speakers.Update(context.TODO(), client, bgpSpeakerID, opts).Extract() if err != nil { log.Panic(err) } @@ -84,7 +84,7 @@ Example: Example: opts := speakers.AddBGPPeerOpts{BGPPeerID: bgpPeerID} - r, err := speakers.AddBGPPeer(c, bgpSpeakerID, opts).Extract() + r, err := speakers.AddBGPPeer(context.TODO(), client, bgpSpeakerID, opts).Extract() if err != nil { log.Panic(err) } @@ -96,7 +96,7 @@ Example: Example: opts := speakers.RemoveBGPPeerOpts{BGPPeerID: bgpPeerID} - err := speakers.RemoveBGPPeer(c, bgpSpeakerID, opts).ExtractErr() + err := speakers.RemoveBGPPeer(context.TODO(), client, bgpSpeakerID, opts).ExtractErr() if err != nil { log.Panic(err) } @@ -107,7 +107,7 @@ Example: Example: - pages, err := speakers.GetAdvertisedRoutes(c, speakerID).AllPages(context.TODO()) + pages, err := speakers.GetAdvertisedRoutes(client, speakerID).AllPages(context.TODO()) if err != nil { log.Panic(err) } @@ -126,7 +126,7 @@ Example: opts := speakers.AddGatewayNetworkOpts{NetworkID: networkID} - r, err := speakers.AddGatewayNetwork(c, speakerID, opts).Extract() + r, err := speakers.AddGatewayNetwork(context.TODO(), client, speakerID, opts).Extract() if err != nil { log.Panic(err) } @@ -138,7 +138,7 @@ Example: Example: opts := speakers.RemoveGatewayNetworkOpts{NetworkID: networkID} - err := speakers.RemoveGatewayNetwork(c, speakerID, opts).ExtractErr() + err := speakers.RemoveGatewayNetwork(context.TODO(), client, speakerID, opts).ExtractErr() if err != nil { log.Panic(err) } diff --git a/openstack/networking/v2/extensions/external/doc.go b/openstack/networking/v2/extensions/external/doc.go index 56d4f23041..b8a6565b42 100644 --- a/openstack/networking/v2/extensions/external/doc.go +++ b/openstack/networking/v2/extensions/external/doc.go @@ -45,7 +45,7 @@ Example to Create a Network with External Information &iTrue, } - network, err := networks.Create(networkClient, createOpts).Extract() + network, err := networks.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/extradhcpopts/doc.go b/openstack/networking/v2/extensions/extradhcpopts/doc.go index ec5d6181d6..735551bda9 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/doc.go +++ b/openstack/networking/v2/extensions/extradhcpopts/doc.go @@ -9,7 +9,7 @@ Example to Get a Port with Extra DHCP Options extradhcpopts.ExtraDHCPOptsExt } - err := ports.Get(networkClient, portID).ExtractInto(&s) + err := ports.Get(context.TODO(), networkClient, portID).ExtractInto(&s) if err != nil { panic(err) } @@ -41,7 +41,7 @@ Example to Create a Port with Extra DHCP Options }, } - err := ports.Create(networkClient, createOpts).ExtractInto(&s) + err := ports.Create(context.TODO(), networkClient, createOpts).ExtractInto(&s) if err != nil { panic(err) } @@ -72,7 +72,7 @@ Example to Update a Port with Extra DHCP Options } portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" - err := ports.Update(networkClient, portID, updateOpts).ExtractInto(&s) + err := ports.Update(context.TODO(), networkClient, portID, updateOpts).ExtractInto(&s) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go index 188f075be1..4705136fbe 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/doc.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/doc.go @@ -24,7 +24,7 @@ Example of Listing Address scopes Example to Get an Address scope addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" - addressScope, err := addressscopes.Get(networkClient, addressScopeID).Extract() + addressScope, err := addressscopes.Get(context.TODO(), networkClient, addressScopeID).Extract() if err != nil { panic(err) } @@ -35,7 +35,7 @@ Example to Create a new Address scope Name: "my_address_scope", IPVersion: 6, } - addressScope, err := addressscopes.Create(networkClient, addressScopeOpts).Extract() + addressScope, err := addressscopes.Create(context.TODO(), networkClient, addressScopeOpts).Extract() if err != nil { panic(err) } @@ -48,7 +48,7 @@ Example to Update an Address scope Name: &newName, } - addressScope, err := addressscopes.Update(networkClient, addressScopeID, updateOpts).Extract() + addressScope, err := addressscopes.Update(context.TODO(), networkClient, addressScopeID, updateOpts).Extract() if err != nil { panic(err) } @@ -56,7 +56,7 @@ Example to Update an Address scope Example to Delete an Address scope addressScopeID = "9cc35860-522a-4d35-974d-51d4b011801e" - err := addressscopes.Delete(networkClient, addressScopeID).ExtractErr() + err := addressscopes.Delete(context.TODO(), networkClient, addressScopeID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/doc.go b/openstack/networking/v2/extensions/layer3/floatingips/doc.go index fdc20d1608..ab54042a43 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/doc.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/doc.go @@ -28,7 +28,7 @@ Example to Create a Floating IP FloatingNetworkID: "a6917946-38ab-4ffd-a55a-26c0980ce5ee", } - fip, err := floatingips.Create(networkingClient, createOpts).Extract() + fip, err := floatingips.Create(context.TODO(), networkingClient, createOpts).Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example to Update a Floating IP PortID: &portID, } - fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract() + fip, err := floatingips.Update(context.TODO(), networkingClient, fipID, updateOpts).Extract() if err != nil { panic(err) } @@ -55,7 +55,7 @@ Example to Disassociate a Floating IP with a Port PortID: new(string), } - fip, err := floatingips.Update(networkingClient, fipID, updateOpts).Extract() + fip, err := floatingips.Update(context.TODO(), networkingClient, fipID, updateOpts).Extract() if err != nil { panic(err) } @@ -63,7 +63,7 @@ Example to Disassociate a Floating IP with a Port Example to Delete a Floating IP fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" - err := floatingips.Delete(networkClient, fipID).ExtractErr() + err := floatingips.Delete(context.TODO(), networkClient, fipID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go index 34e551a790..bf67b92b6b 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/doc.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/doc.go @@ -23,7 +23,7 @@ Example to Get a Port Forwarding with a certain ID fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" - pf, err := portforwarding.Get(client, fipID, pfID).Extract() + pf, err := portforwarding.Get(context.TODO(), client, fipID, pfID).Extract() if err != nil { panic(err) } @@ -38,7 +38,7 @@ Example to Create a Port Forwarding for a floating IP InternalPortID: portID, } - pf, err := portforwarding.Create(networkingClient, floatingIPID, createOpts).Extract() + pf, err := portforwarding.Create(context.TODO(), networkingClient, floatingIPID, createOpts).Extract() if err != nil { panic(err) @@ -54,7 +54,7 @@ Example to Update a Port Forwarding fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" - pf, err := portforwarding.Update(client, fipID, pfID, updateOpts).Extract() + pf, err := portforwarding.Update(context.TODO(), client, fipID, pfID, updateOpts).Extract() if err != nil { panic(err) } @@ -63,7 +63,7 @@ Example to Delete a Port forwarding fipID := "2f245a7b-796b-4f26-9cf9-9e82d248fda7" pfID := "725ade3c-9760-4880-8080-8fc2dbab9acc" - err := portforwarding.Delete(networkClient, fipID, pfID).ExtractErr() + err := portforwarding.Delete(context.TODO(), networkClient, fipID, pfID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/layer3/routers/doc.go b/openstack/networking/v2/extensions/layer3/routers/doc.go index f9958d8c6e..42ccbcf7cf 100644 --- a/openstack/networking/v2/extensions/layer3/routers/doc.go +++ b/openstack/networking/v2/extensions/layer3/routers/doc.go @@ -32,7 +32,7 @@ Example to Create a Router GatewayInfo: &gwi, } - router, err := routers.Create(networkClient, createOpts).Extract() + router, err := routers.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -51,7 +51,7 @@ Example to Update a Router Routes: &routes, } - router, err := routers.Update(networkClient, routerID, updateOpts).Extract() + router, err := routers.Update(context.TODO(), networkClient, routerID, updateOpts).Extract() if err != nil { panic(err) } @@ -64,7 +64,7 @@ Example to Update just the Router name, keeping everything else as-is Name: "new_name", } - router, err := routers.Update(networkClient, routerID, updateOpts).Extract() + router, err := routers.Update(context.TODO(), networkClient, routerID, updateOpts).Extract() if err != nil { panic(err) } @@ -79,7 +79,7 @@ Example to Remove all Routes from a Router Routes: &routes, } - router, err := routers.Update(networkClient, routerID, updateOpts).Extract() + router, err := routers.Update(context.TODO(), networkClient, routerID, updateOpts).Extract() if err != nil { panic(err) } @@ -87,7 +87,7 @@ Example to Remove all Routes from a Router Example to Delete a Router routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" - err := routers.Delete(networkClient, routerID).ExtractErr() + err := routers.Delete(context.TODO(), networkClient, routerID).ExtractErr() if err != nil { panic(err) } @@ -100,7 +100,7 @@ Example to Add an Interface to a Router SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1", } - interface, err := routers.AddInterface(networkClient, routerID, intOpts).Extract() + interface, err := routers.AddInterface(context.TODO(), networkClient, routerID, intOpts).Extract() if err != nil { panic(err) } @@ -113,7 +113,7 @@ Example to Remove an Interface from a Router SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1", } - interface, err := routers.RemoveInterface(networkClient, routerID, intOpts).Extract() + interface, err := routers.RemoveInterface(context.TODO(), networkClient, routerID, intOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/networkipavailabilities/doc.go b/openstack/networking/v2/extensions/networkipavailabilities/doc.go index e3284c2fa4..3c3ab426ec 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/doc.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/doc.go @@ -20,7 +20,7 @@ Example of Listing NetworkIPAvailabilities Example of Getting a single NetworkIPAvailability - availability, err := networkipavailabilities.Get(networkClient, "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() + availability, err := networkipavailabilities.Get(context.TODO(), networkClient, "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/portsecurity/doc.go b/openstack/networking/v2/extensions/portsecurity/doc.go index a80852607c..726e09cbbf 100644 --- a/openstack/networking/v2/extensions/portsecurity/doc.go +++ b/openstack/networking/v2/extensions/portsecurity/doc.go @@ -46,7 +46,7 @@ Example to Create a Network without Port Security PortSecurityEnabled: &iFalse, } - err := networks.Create(networkClient, createOpts).ExtractInto(&networkWithPortSecurityExt) + err := networks.Create(context.TODO(), networkClient, createOpts).ExtractInto(&networkWithPortSecurityExt) if err != nil { panic(err) } @@ -68,7 +68,7 @@ Example to Disable Port Security on an Existing Network PortSecurityEnabled: &iFalse, } - err := networks.Update(networkClient, networkID, updateOpts).ExtractInto(&networkWithPortSecurityExt) + err := networks.Update(context.TODO(), networkClient, networkID, updateOpts).ExtractInto(&networkWithPortSecurityExt) if err != nil { panic(err) } @@ -84,7 +84,7 @@ Example to Get a Port with Port Security Information portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" - err := ports.Get(networkingClient, portID).ExtractInto(&portWithPortSecurityExtensions) + err := ports.Get(context.TODO(), networkingClient, portID).ExtractInto(&portWithPortSecurityExtensions) if err != nil { panic(err) } @@ -112,7 +112,7 @@ Example to Create a Port Without Port Security PortSecurityEnabled: &iFalse, } - err := ports.Create(networkingClient, createOpts).ExtractInto(&portWithPortSecurityExtensions) + err := ports.Create(context.TODO(), networkingClient, createOpts).ExtractInto(&portWithPortSecurityExtensions) if err != nil { panic(err) } @@ -135,7 +135,7 @@ Example to Disable Port Security on an Existing Port PortSecurityEnabled: &iFalse, } - err := ports.Update(networkingClient, portID, updateOpts).ExtractInto(&portWithPortSecurityExtensions) + err := ports.Update(context.TODO(), networkingClient, portID, updateOpts).ExtractInto(&portWithPortSecurityExtensions) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/provider/doc.go b/openstack/networking/v2/extensions/provider/doc.go index 9906c4a3ad..992e4f67d4 100644 --- a/openstack/networking/v2/extensions/provider/doc.go +++ b/openstack/networking/v2/extensions/provider/doc.go @@ -65,7 +65,7 @@ Example to Create a Provider Network Segments: segments, } - network, err := networks.Create(networkClient, createOpts).Extract() + network, err := networks.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/qos/policies/doc.go b/openstack/networking/v2/extensions/qos/policies/doc.go index bf223eea28..dacc3b50a0 100644 --- a/openstack/networking/v2/extensions/qos/policies/doc.go +++ b/openstack/networking/v2/extensions/qos/policies/doc.go @@ -11,7 +11,7 @@ Example to Get a Port with a QoS policy portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" - err = ports.Get(client, portID).ExtractInto(&portWithQoS) + err = ports.Get(context.TODO(), client, portID).ExtractInto(&portWithQoS) if err != nil { log.Fatal(err) } @@ -37,7 +37,7 @@ Example to Create a Port with a QoS policy QoSPolicyID: policyID, } - err = ports.Create(client, createOpts).ExtractInto(&portWithQoS) + err = ports.Create(context.TODO(), client, createOpts).ExtractInto(&portWithQoS) if err != nil { panic(err) } @@ -60,7 +60,7 @@ Example to Add a QoS policy to an existing Port QoSPolicyID: &policyID, } - err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) + err := ports.Update(context.TODO(), client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) if err != nil { panic(err) } @@ -83,7 +83,7 @@ Example to Delete a QoS policy from the existing Port QoSPolicyID: &policyID, } - err := ports.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) + err := ports.Update(context.TODO(), client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithQoS) if err != nil { panic(err) } @@ -99,7 +99,7 @@ Example to Get a Network with a QoS policy networkID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" - err = networks.Get(client, networkID).ExtractInto(&networkWithQoS) + err = networks.Get(context.TODO(), client, networkID).ExtractInto(&networkWithQoS) if err != nil { log.Fatal(err) } @@ -125,7 +125,7 @@ Example to Create a Network with a QoS policy QoSPolicyID: policyID, } - err = networks.Create(client, createOpts).ExtractInto(&networkWithQoS) + err = networks.Create(context.TODO(), client, createOpts).ExtractInto(&networkWithQoS) if err != nil { panic(err) } @@ -148,7 +148,7 @@ Example to add a QoS policy to an existing Network QoSPolicyID: &policyID, } - err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) + err := networks.Update(context.TODO(), client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) if err != nil { panic(err) } @@ -171,7 +171,7 @@ Example to delete a QoS policy from the existing Network QoSPolicyID: &policyID, } - err := networks.Update(client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) + err := networks.Update(context.TODO(), client, "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&networkWithQoS) if err != nil { panic(err) } @@ -204,7 +204,7 @@ Example to Get a specific QoS policy policyID := "30a57f4a-336b-4382-8275-d708babd2241" - policy, err := policies.Get(networkClient, policyID).Extract() + policy, err := policies.Get(context.TODO(), networkClient, policyID).Extract() if err != nil { panic(err) } @@ -219,7 +219,7 @@ Example to Create a QoS policy IsDefault: true, } - policy, err := policies.Create(networkClient, createOpts).Extract() + policy, err := policies.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -238,7 +238,7 @@ Example to Update a QoS policy policyID := "30a57f4a-336b-4382-8275-d708babd2241" - policy, err := policies.Update(networkClient, policyID, opts).Extract() + policy, err := policies.Update(context.TODO(), networkClient, policyID, opts).Extract() if err != nil { panic(err) } @@ -249,7 +249,7 @@ Example to Delete a QoS policy policyID := "30a57f4a-336b-4382-8275-d708babd2241" - err := policies.Delete(networkClient, policyID).ExtractErr() + err := policies.Delete(context.TODO(), networkClient, policyID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/qos/rules/doc.go b/openstack/networking/v2/extensions/qos/rules/doc.go index 556d638a72..f1c2d2672a 100644 --- a/openstack/networking/v2/extensions/qos/rules/doc.go +++ b/openstack/networking/v2/extensions/qos/rules/doc.go @@ -28,7 +28,7 @@ Example of Getting a single BandwidthLimitRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.GetBandwidthLimitRule(networkClient, policyID, ruleID).ExtractBandwidthLimitRule() + rule, err := rules.GetBandwidthLimitRule(context.TODO(), networkClient, policyID, ruleID).ExtractBandwidthLimitRule() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example of Creating a single BandwidthLimitRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - rule, err := rules.CreateBandwidthLimitRule(networkClient, policyID, opts).ExtractBandwidthLimitRule() + rule, err := rules.CreateBandwidthLimitRule(context.TODO(), networkClient, policyID, opts).ExtractBandwidthLimitRule() if err != nil { panic(err) } @@ -64,7 +64,7 @@ Example of Updating a single BandwidthLimitRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.UpdateBandwidthLimitRule(networkClient, policyID, ruleID, opts).ExtractBandwidthLimitRule() + rule, err := rules.UpdateBandwidthLimitRule(context.TODO(), networkClient, policyID, ruleID, opts).ExtractBandwidthLimitRule() if err != nil { panic(err) } @@ -106,7 +106,7 @@ Example of Getting a single DSCPMarkingRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.GetDSCPMarkingRule(networkClient, policyID, ruleID).ExtractDSCPMarkingRule() + rule, err := rules.GetDSCPMarkingRule(context.TODO(), networkClient, policyID, ruleID).ExtractDSCPMarkingRule() if err != nil { panic(err) } @@ -121,7 +121,7 @@ Example of Creating a single DSCPMarkingRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - rule, err := rules.CreateDSCPMarkingRule(networkClient, policyID, opts).ExtractDSCPMarkingRule() + rule, err := rules.CreateDSCPMarkingRule(context.TODO(), networkClient, policyID, opts).ExtractDSCPMarkingRule() if err != nil { panic(err) } @@ -139,7 +139,7 @@ Example of Updating a single DSCPMarkingRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.UpdateDSCPMarkingRule(networkClient, policyID, ruleID, opts).ExtractDSCPMarkingRule() + rule, err := rules.UpdateDSCPMarkingRule(context.TODO(), networkClient, policyID, ruleID, opts).ExtractDSCPMarkingRule() if err != nil { panic(err) } @@ -183,7 +183,7 @@ Example of Getting a single MinimumBandwidthRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.GetMinimumBandwidthRule(networkClient, policyID, ruleID).ExtractMinimumBandwidthRule() + rule, err := rules.GetMinimumBandwidthRule(context.TODO(), networkClient, policyID, ruleID).ExtractMinimumBandwidthRule() if err != nil { panic(err) } @@ -198,7 +198,7 @@ Example of Creating a single MinimumBandwidthRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" - rule, err := rules.CreateMinimumBandwidthRule(networkClient, policyID, opts).ExtractMinimumBandwidthRule() + rule, err := rules.CreateMinimumBandwidthRule(context.TODO(), networkClient, policyID, opts).ExtractMinimumBandwidthRule() if err != nil { panic(err) } @@ -216,7 +216,7 @@ Example of Updating a single MinimumBandwidthRule policyID := "501005fa-3b56-4061-aaca-3f24995112e1" ruleID := "30a57f4a-336b-4382-8275-d708babd2241" - rule, err := rules.UpdateMinimumBandwidthRule(networkClient, policyID, ruleID, opts).ExtractMinimumBandwidthRule() + rule, err := rules.UpdateMinimumBandwidthRule(context.TODO(), networkClient, policyID, ruleID, opts).ExtractMinimumBandwidthRule() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/doc.go b/openstack/networking/v2/extensions/qos/ruletypes/doc.go index 6a9f0d7fac..b0085499a1 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/doc.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/doc.go @@ -19,7 +19,7 @@ Example of Getting a single QoS rule type by name ruleTypeName := "bandwidth_limit" - ruleType, err := ruletypes.Get(networkClient, ruleTypeName).Extract() + ruleType, err := ruletypes.Get(context.TODO(), networkClient, ruleTypeName).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/quotas/doc.go b/openstack/networking/v2/extensions/quotas/doc.go index 2413e92351..fe1cc26311 100644 --- a/openstack/networking/v2/extensions/quotas/doc.go +++ b/openstack/networking/v2/extensions/quotas/doc.go @@ -4,7 +4,7 @@ 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() + quotasInfo, err := quotas.Get(context.TODO(), networkClient, projectID).Extract() if err != nil { log.Fatal(err) } @@ -14,7 +14,7 @@ Example to Get project quotas Example to Get a Detailed Quota Set projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" - quotasInfo, err := quotas.GetDetail(networkClient, projectID).Extract() + quotasInfo, err := quotas.GetDetail(context.TODO(), networkClient, projectID).Extract() if err != nil { log.Fatal(err) } @@ -37,7 +37,7 @@ Example to Update project quotas SubnetPool: gophercloud.IntToPointer(0), Trunk: gophercloud.IntToPointer(0), } - quotasInfo, err := quotas.Update(networkClient, projectID) + quotasInfo, err := quotas.Update(context.TODO(), networkClient, projectID) if err != nil { log.Fatal(err) } diff --git a/openstack/networking/v2/extensions/rbacpolicies/doc.go b/openstack/networking/v2/extensions/rbacpolicies/doc.go index 9b4361350d..9e11c520d2 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/doc.go +++ b/openstack/networking/v2/extensions/rbacpolicies/doc.go @@ -22,7 +22,7 @@ Example to Create a RBAC Policy ObjectID: "240d22bf-bd17-4238-9758-25f72610ecdc" } - rbacPolicy, err := rbacpolicies.Create(rbacClient, createOpts).Extract() + rbacPolicy, err := rbacpolicies.Create(context.TODO(), rbacClient, createOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to List RBAC Policies Example to Delete a RBAC Policy rbacPolicyID := "94fe107f-da78-4d92-a9d7-5611b06dad8d" - err := rbacpolicies.Delete(rbacClient, rbacPolicyID).ExtractErr() + err := rbacpolicies.Delete(context.TODO(), rbacClient, rbacPolicyID).ExtractErr() if err != nil { panic(err) } @@ -58,7 +58,7 @@ Example to Delete a RBAC Policy Example to Get RBAC Policy by ID rbacPolicyID := "94fe107f-da78-4d92-a9d7-5611b06dad8d" - rbacpolicy, err := rbacpolicies.Get(rbacClient, rbacPolicyID).Extract() + rbacpolicy, err := rbacpolicies.Get(context.TODO(), rbacClient, rbacPolicyID).Extract() if err != nil { panic(err) } @@ -70,7 +70,7 @@ Example to Update a RBAC Policy updateOpts := rbacpolicies.UpdateOpts{ TargetTenant: "9d766060b6354c9e8e2da44cab0e8f38", } - rbacPolicy, err := rbacpolicies.Update(rbacClient, rbacPolicyID, updateOpts).Extract() + rbacPolicy, err := rbacpolicies.Update(context.TODO(), rbacClient, rbacPolicyID, updateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/security/groups/doc.go b/openstack/networking/v2/extensions/security/groups/doc.go index 9e494e034a..298d617127 100644 --- a/openstack/networking/v2/extensions/security/groups/doc.go +++ b/openstack/networking/v2/extensions/security/groups/doc.go @@ -29,7 +29,7 @@ Example to Create a Security Group Description: "A Security Group", } - group, err := groups.Create(networkClient, createOpts).Extract() + group, err := groups.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example to Update a Security Group Name: "new_name", } - group, err := groups.Update(networkClient, groupID, updateOpts).Extract() + group, err := groups.Update(context.TODO(), networkClient, groupID, updateOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Update a Security Group Example to Delete a Security Group groupID := "37d94f8a-d136-465c-ae46-144f0d8ef141" - err := groups.Delete(networkClient, groupID).ExtractErr() + err := groups.Delete(context.TODO(), networkClient, groupID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/security/rules/doc.go b/openstack/networking/v2/extensions/security/rules/doc.go index cbac3c7731..fde5e13055 100644 --- a/openstack/networking/v2/extensions/security/rules/doc.go +++ b/openstack/networking/v2/extensions/security/rules/doc.go @@ -34,7 +34,7 @@ Example to Create a Security Group Rule SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", } - rule, err := rules.Create(networkClient, createOpts).Extract() + rule, err := rules.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example to Create a Security Group Rule Example to Delete a Security Group Rule ruleID := "37d94f8a-d136-465c-ae46-144f0d8ef141" - err := rules.Delete(networkClient, ruleID).ExtractErr() + err := rules.Delete(context.TODO(), networkClient, ruleID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/subnetpools/doc.go b/openstack/networking/v2/extensions/subnetpools/doc.go index 5e841df870..5c44da26d3 100644 --- a/openstack/networking/v2/extensions/subnetpools/doc.go +++ b/openstack/networking/v2/extensions/subnetpools/doc.go @@ -24,7 +24,7 @@ Example of Listing Subnetpools Example to Get a Subnetpool subnetPoolID = "23d5d3f7-9dfa-4f73-b72b-8b0b0063ec55" - subnetPool, err := subnetpools.Get(networkClient, subnetPoolID).Extract() + subnetPool, err := subnetpools.Get(context.TODO(), networkClient, subnetPoolID).Extract() if err != nil { panic(err) } @@ -41,7 +41,7 @@ Example to Create a new Subnetpool Name: subnetPoolName, Prefixes: subnetPoolPrefixes, } - subnetPool, err := subnetpools.Create(networkClient, subnetPoolOpts).Extract() + subnetPool, err := subnetpools.Create(context.TODO(), networkClient, subnetPoolOpts).Extract() if err != nil { panic(err) } @@ -56,7 +56,7 @@ Example to Update a Subnetpool MaxPrefixLen: 72, } - subnetPool, err := subnetpools.Update(networkClient, subnetPoolID, updateOpts).Extract() + subnetPool, err := subnetpools.Update(context.TODO(), networkClient, subnetPoolID, updateOpts).Extract() if err != nil { panic(err) } @@ -64,7 +64,7 @@ Example to Update a Subnetpool Example to Delete a Subnetpool subnetPoolID := "23d5d3f7-9dfa-4f73-b72b-8b0b0063ec55" - err := subnetpools.Delete(networkClient, subnetPoolID).ExtractErr() + err := subnetpools.Delete(context.TODO(), networkClient, subnetPoolID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/trunk_details/doc.go b/openstack/networking/v2/extensions/trunk_details/doc.go index c24ad592be..943394dcf6 100644 --- a/openstack/networking/v2/extensions/trunk_details/doc.go +++ b/openstack/networking/v2/extensions/trunk_details/doc.go @@ -10,7 +10,7 @@ Example: } var portExt portExt - err := ports.Get(networkClient, "2ba3a709-e40e-462c-a541-85e99de589bf").ExtractInto(&portExt) + err := ports.Get(context.TODO(), networkClient, "2ba3a709-e40e-462c-a541-85e99de589bf").ExtractInto(&portExt) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/trunks/doc.go b/openstack/networking/v2/extensions/trunks/doc.go index 954bffc346..5da9d90e07 100644 --- a/openstack/networking/v2/extensions/trunks/doc.go +++ b/openstack/networking/v2/extensions/trunks/doc.go @@ -14,7 +14,7 @@ Example of a new empty Trunk creation PortID: "a6f0560c-b7a8-401f-bf6e-d0a5c851ae10", } - trunk, err := trunks.Create(networkClient, createOpts).Extract() + trunk, err := trunks.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -42,7 +42,7 @@ Example of a new Trunk creation with 2 subports }, } - trunk, err := trunks.Create(client, createOpts).Extract() + trunk, err := trunks.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -51,7 +51,7 @@ Example of a new Trunk creation with 2 subports Example of deleting a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" - err := trunks.Delete(networkClient, trunkID).ExtractErr() + err := trunks.Delete(context.TODO(), networkClient, trunkID).ExtractErr() if err != nil { panic(err) } @@ -74,7 +74,7 @@ Example of listing Trunks Example of getting a Trunk trunkID = "52d8d124-3dc9-4563-9fef-bad3187ecf2d" - trunk, err := trunks.Get(networkClient, trunkID).Extract() + trunk, err := trunks.Get(context.TODO(), networkClient, trunkID).Extract() if err != nil { panic(err) } @@ -83,14 +83,14 @@ Example of getting a Trunk Example of updating a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" - subports, err := trunks.GetSubports(client, trunkID).Extract() + subports, err := trunks.GetSubports(context.TODO(), client, trunkID).Extract() iFalse := false updateOpts := trunks.UpdateOpts{ AdminStateUp: &iFalse, Name: "updated_gophertrunk", Description: "trunk updated by gophercloud", } - trunk, err = trunks.Update(networkClient, trunkID, updateOpts).Extract() + trunk, err = trunks.Update(context.TODO(), networkClient, trunkID, updateOpts).Extract() if err != nil { log.Fatal(err) } @@ -99,7 +99,7 @@ Example of updating a Trunk Example of showing subports of a Trunk trunkID := "c36e7f2e-0c53-4742-8696-aee77c9df159" - subports, err := trunks.GetSubports(client, trunkID).Extract() + subports, err := trunks.GetSubports(context.TODO(), client, trunkID).Extract() fmt.Printf("%+v\n", subports) Example of adding two subports to a Trunk @@ -119,7 +119,7 @@ Example of adding two subports to a Trunk }, }, } - trunk, err := trunks.AddSubports(client, trunkID, addSubportsOpts).Extract() + trunk, err := trunks.AddSubports(context.TODO(), client, trunkID, addSubportsOpts).Extract() if err != nil { panic(err) } @@ -134,7 +134,7 @@ Example of deleting two subports from a Trunk {PortID: "2cf671b9-02b3-4121-9e85-e0af3548d112"}, }, } - trunk, err := trunks.RemoveSubports(networkClient, trunkID, removeSubportsOpts).Extract() + trunk, err := trunks.RemoveSubports(context.TODO(), networkClient, trunkID, removeSubportsOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vlantransparent/doc.go b/openstack/networking/v2/extensions/vlantransparent/doc.go index 6bd1a70036..d49141692c 100644 --- a/openstack/networking/v2/extensions/vlantransparent/doc.go +++ b/openstack/networking/v2/extensions/vlantransparent/doc.go @@ -39,7 +39,7 @@ Example of Getting a Network with the vlan-transparent extension vlantransparent.TransparentExt } - err := networks.Get(networkClient, "db193ab3-96e3-4cb3-8fc5-05f4296d0324").ExtractInto(&network) + err := networks.Get(context.TODO(), networkClient, "db193ab3-96e3-4cb3-8fc5-05f4296d0324").ExtractInto(&network) if err != nil { panic(err) } @@ -63,7 +63,7 @@ Example of Creating Network with the vlan-transparent extension vlantransparent.TransparentExt } - err := networks.Create(networkClient, createOpts).ExtractInto(&network) + err := networks.Create(context.TODO(), networkClient, createOpts).ExtractInto(&network) if err != nil { panic(err) } @@ -87,7 +87,7 @@ Example of Updating Network with the vlan-transparent extension vlantransparent.TransparentExt } - err := networks.Update(networkClient, updateOpts).ExtractInto(&network) + err := networks.Update(context.TODO(), networkClient, updateOpts).ExtractInto(&network) if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go index 6b0a62c1c5..e203d5b17a 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/doc.go @@ -11,21 +11,21 @@ Example to create an Endpoint Group "10.3.0.0/24", }, } - group, err := endpointgroups.Create(client, createOpts).Extract() + group, err := endpointgroups.Create(context.TODO(), client, createOpts).Extract() if err != nil { return group, err } Example to retrieve an Endpoint Group - group, err := endpointgroups.Get(client, "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a").Extract() + group, err := endpointgroups.Get(context.TODO(), client, "6ecd9cf3-ca64-46c7-863f-f2eb1b9e838a").Extract() if err != nil { panic(err) } Example to Delete an Endpoint Group - err := endpointgroups.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() + err := endpointgroups.Delete(context.TODO(), client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Update an endpoint group Name: &name, Description: &description, } - updatedPolicy, err := endpointgroups.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() + updatedPolicy, err := endpointgroups.Update(context.TODO(), client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go index a37fb7a75a..53b4dbad62 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/doc.go @@ -11,21 +11,21 @@ Example to Create an IKE policy PFS: ikepolicies.PFSGroup5, } - policy, err := ikepolicies.Create(networkClient, createOpts).Extract() + policy, err := ikepolicies.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Show the details of a specific IKE policy by ID - policy, err := ikepolicies.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + policy, err := ikepolicies.Get(context.TODO(), client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() if err != nil { panic(err) } Example to Delete a Policy - err := ikepolicies.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() + err := ikepolicies.Delete(context.TODO(), client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() if err != nil { panic(err) @@ -40,7 +40,7 @@ Example to Update an IKE policy Value: 7000, }, } - updatedPolicy, err := ikepolicies.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() + updatedPolicy, err := ikepolicies.Update(context.TODO(), client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go index bc995b9e0e..75440abd89 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/doc.go @@ -8,21 +8,21 @@ Example to Create a Policy Name: "IPSecPolicy_1", } - policy, err := policies.Create(networkClient, createOpts).Extract() + policy, err := policies.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } Example to Delete a Policy - err := ipsecpolicies.Delete(client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() + err := ipsecpolicies.Delete(context.TODO(), client, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() if err != nil { panic(err) } Example to Show the details of a specific IPSec policy by ID - policy, err := ipsecpolicies.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + policy, err := ipsecpolicies.Get(context.TODO(), client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() if err != nil { panic(err) } @@ -35,7 +35,7 @@ Example to Update an IPSec policy Name: &name, Description: &description, } - updatedPolicy, err := ipsecpolicies.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() + updatedPolicy, err := ipsecpolicies.Update(context.TODO(), client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/services/doc.go b/openstack/networking/v2/extensions/vpnaas/services/doc.go index 40a28c71f0..0a798308eb 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/services/doc.go @@ -31,7 +31,7 @@ Example to Create a Service AdminStateUp: gophercloud.Enabled, } - service, err := services.Create(networkClient, createOpts).Extract() + service, err := services.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -44,7 +44,7 @@ Example to Update a Service Description: "New Description", } - service, err := services.Update(networkClient, serviceID, updateOpts).Extract() + service, err := services.Update(context.TODO(), networkClient, serviceID, updateOpts).Extract() if err != nil { panic(err) } @@ -52,14 +52,14 @@ Example to Update a Service Example to Delete a Service serviceID := "38aee955-6283-4279-b091-8b9c828000ec" - err := services.Delete(networkClient, serviceID).ExtractErr() + err := services.Delete(context.TODO(), networkClient, serviceID).ExtractErr() if err != nil { panic(err) } Example to Show the details of a specific Service by ID - service, err := services.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + service, err := services.Get(context.TODO(), client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go index 36b3b97715..cacd02918e 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/doc.go @@ -18,14 +18,14 @@ OpenStack Networking Service. PeerID: "172.24.4.233", MTU: 1500, } - connection, err := siteconnections.Create(client, createOpts).Extract() + connection, err := siteconnections.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } Example to Show the details of a specific IPSec site connection by ID - conn, err := siteconnections.Get(client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + conn, err := siteconnections.Get(context.TODO(), client, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() if err != nil { panic(err) } @@ -33,7 +33,7 @@ Example to Show the details of a specific IPSec site connection by ID Example to Delete a site connection connID := "38aee955-6283-4279-b091-8b9c828000ec" - err := siteconnections.Delete(networkClient, connID).ExtractErr() + err := siteconnections.Delete(context.TODO(), networkClient, connID).ExtractErr() if err != nil { panic(err) } @@ -58,7 +58,7 @@ Example to Update an IPSec site connection Name: &name, Description: &description, } - updatedConnection, err := siteconnections.Update(client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() + updatedConnection, err := siteconnections.Update(context.TODO(), client, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/networking/v2/networks/doc.go b/openstack/networking/v2/networks/doc.go index 31bded96ed..4f8ce23aec 100644 --- a/openstack/networking/v2/networks/doc.go +++ b/openstack/networking/v2/networks/doc.go @@ -36,7 +36,7 @@ Example to Create a Network AdminStateUp: &iTrue, } - network, err := networks.Create(networkClient, createOpts).Extract() + network, err := networks.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -50,7 +50,7 @@ Example to Update a Network Name: &name, } - network, err := networks.Update(networkClient, networkID, updateOpts).Extract() + network, err := networks.Update(context.TODO(), networkClient, networkID, updateOpts).Extract() if err != nil { panic(err) } @@ -58,7 +58,7 @@ Example to Update a Network Example to Delete a Network networkID := "484cda0e-106f-4f4b-bb3f-d413710bbe78" - err := networks.Delete(networkClient, networkID).ExtractErr() + err := networks.Delete(context.TODO(), networkClient, networkID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/ports/doc.go b/openstack/networking/v2/ports/doc.go index a2c58945c8..025ad4fa94 100644 --- a/openstack/networking/v2/ports/doc.go +++ b/openstack/networking/v2/ports/doc.go @@ -43,7 +43,7 @@ Example to Create a Port }, } - port, err := ports.Create(networkClient, createOpts).Extract() + port, err := ports.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -57,7 +57,7 @@ Example to Update a Port SecurityGroups: &[]string{}, } - port, err := ports.Update(networkClient, portID, updateOpts).Extract() + port, err := ports.Update(context.TODO(), networkClient, portID, updateOpts).Extract() if err != nil { panic(err) } @@ -65,7 +65,7 @@ Example to Update a Port Example to Delete a Port portID := "c34bae2b-7641-49b6-bf6d-d8e473620ed8" - err := ports.Delete(networkClient, portID).ExtractErr() + err := ports.Delete(context.TODO(), networkClient, portID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/networking/v2/subnets/doc.go b/openstack/networking/v2/subnets/doc.go index 076fcf1aa8..eef6afd53c 100644 --- a/openstack/networking/v2/subnets/doc.go +++ b/openstack/networking/v2/subnets/doc.go @@ -47,7 +47,7 @@ Example to Create a Subnet With Specified Gateway ServiceTypes: []string{"network:floatingip"}, } - subnet, err := subnets.Create(networkClient, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -70,7 +70,7 @@ Example to Create a Subnet With No Gateway DNSNameservers: []string{}, } - subnet, err := subnets.Create(networkClient, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -90,7 +90,7 @@ Example to Create a Subnet With a Default Gateway DNSNameservers: []string{}, } - subnet, err := subnets.Create(networkClient, createOpts).Extract() + subnet, err := subnets.Create(context.TODO(), networkClient, createOpts).Extract() if err != nil { panic(err) } @@ -108,7 +108,7 @@ Example to Update a Subnet ServiceTypes: &serviceTypes, } - subnet, err := subnets.Update(networkClient, subnetID, updateOpts).Extract() + subnet, err := subnets.Update(context.TODO(), networkClient, subnetID, updateOpts).Extract() if err != nil { panic(err) } @@ -122,7 +122,7 @@ Example to Remove a Gateway From a Subnet GatewayIP: &noGateway, } - subnet, err := subnets.Update(networkClient, subnetID, updateOpts).Extract() + subnet, err := subnets.Update(context.TODO(), networkClient, subnetID, updateOpts).Extract() if err != nil { panic(err) } @@ -130,7 +130,7 @@ Example to Remove a Gateway From a Subnet Example to Delete a Subnet subnetID := "db77d064-e34f-4d06-b060-f21e28a61c23" - err := subnets.Delete(networkClient, subnetID).ExtractErr() + err := subnets.Delete(context.TODO(), networkClient, subnetID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/objectstorage/v1/accounts/doc.go b/openstack/objectstorage/v1/accounts/doc.go index 06290c6bd1..d3367745ea 100644 --- a/openstack/objectstorage/v1/accounts/doc.go +++ b/openstack/objectstorage/v1/accounts/doc.go @@ -9,7 +9,7 @@ services. Example to Get an Account - account, err := accounts.Get(objectStorageClient, nil).Extract() + account, err := accounts.Get(context.TODO(), objectStorageClient, nil).Extract() fmt.Printf("%+v\n", account) Example to Update an Account @@ -22,7 +22,7 @@ Example to Update an Account Metadata: metadata, } - updateResult, err := accounts.Update(objectStorageClient, updateOpts).Extract() + updateResult, err := accounts.Update(context.TODO(), objectStorageClient, updateOpts).Extract() fmt.Printf("%+v\n", updateResult) */ package accounts diff --git a/openstack/objectstorage/v1/containers/doc.go b/openstack/objectstorage/v1/containers/doc.go index 9f2b9626a8..dfad95ec1d 100644 --- a/openstack/objectstorage/v1/containers/doc.go +++ b/openstack/objectstorage/v1/containers/doc.go @@ -60,7 +60,7 @@ Example to Create a Container }, } - container, err := containers.Create(objectStorageClient, createOpts).Extract() + container, err := containers.Create(context.TODO(), objectStorageClient, createOpts).Extract() if err != nil { panic(err) } @@ -78,7 +78,7 @@ Example to Update a Container }, } - container, err := containers.Update(objectStorageClient, containerName, updateOpts).Extract() + container, err := containers.Update(context.TODO(), objectStorageClient, containerName, updateOpts).Extract() if err != nil { panic(err) } @@ -87,7 +87,7 @@ Example to Delete a Container containerName := "my_container" - container, err := containers.Delete(objectStorageClient, containerName).Extract() + container, err := containers.Delete(context.TODO(), objectStorageClient, containerName).Extract() if err != nil { panic(err) } diff --git a/openstack/objectstorage/v1/objects/doc.go b/openstack/objectstorage/v1/objects/doc.go index 0ed4addcc4..2a6b00098e 100644 --- a/openstack/objectstorage/v1/objects/doc.go +++ b/openstack/objectstorage/v1/objects/doc.go @@ -63,7 +63,7 @@ Example to Create an Object Content: strings.NewReader(content), } - object, err := objects.Create(objectStorageClient, containerName, objectName, createOpts).Extract() + object, err := objects.Create(context.TODO(), objectStorageClient, containerName, objectName, createOpts).Extract() if err != nil { panic(err) } @@ -77,7 +77,7 @@ Example to Copy an Object Destination: "/newContainer/newObject", } - object, err := objects.Copy(objectStorageClient, containerName, objectName, copyOpts).Extract() + object, err := objects.Copy(context.TODO(), objectStorageClient, containerName, objectName, copyOpts).Extract() if err != nil { panic(err) } @@ -87,7 +87,7 @@ Example to Delete an Object objectName := "my_object" containerName := "my_container" - object, err := objects.Delete(objectStorageClient, containerName, objectName).Extract() + object, err := objects.Delete(context.TODO(), objectStorageClient, containerName, objectName).Extract() if err != nil { panic(err) } @@ -97,7 +97,7 @@ Example to Download an Object's Data objectName := "my_object" containerName := "my_container" - object := objects.Download(objectStorageClient, containerName, objectName, nil) + object := objects.Download(context.TODO(), objectStorageClient, containerName, objectName, nil) if object.Err != nil { panic(object.Err) } diff --git a/openstack/objectstorage/v1/swauth/doc.go b/openstack/objectstorage/v1/swauth/doc.go index 989dc4ece2..99a1c172ad 100644 --- a/openstack/objectstorage/v1/swauth/doc.go +++ b/openstack/objectstorage/v1/swauth/doc.go @@ -8,7 +8,7 @@ Example to Authenticate with swauth Key: "password", } - swiftClient, err := swauth.NewObjectStorageV1(providerClient, authOpts) + swiftClient, err := swauth.NewObjectStorageV1(context.TODO(), providerClient, authOpts) if err != nil { panic(err) } diff --git a/openstack/orchestration/v1/resourcetypes/doc.go b/openstack/orchestration/v1/resourcetypes/doc.go index b964b33e42..f9fac325bb 100644 --- a/openstack/orchestration/v1/resourcetypes/doc.go +++ b/openstack/orchestration/v1/resourcetypes/doc.go @@ -9,7 +9,7 @@ Example of listing available resource types: SupportStatus: resourcetypes.SupportStatusSupported, } - resourceTypes, err := resourcetypes.List(client, listOpts).Extract() + resourceTypes, err := resourcetypes.List(context.TODO(), client, listOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/orchestration/v1/stackresources/doc.go b/openstack/orchestration/v1/stackresources/doc.go index e37b108057..2557729c5c 100644 --- a/openstack/orchestration/v1/stackresources/doc.go +++ b/openstack/orchestration/v1/stackresources/doc.go @@ -6,7 +6,7 @@ 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) + rsrc_result := stackresources.Get(context.TODO(), client, stack.Name, stack.ID, rsrc.Name) if rsrc_result.Err != nil { panic(rsrc_result.Err) } @@ -30,7 +30,7 @@ Example for list stack resources 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) + rsrc_result := stackresources.Get(context.TODO(), client, stack.Name, stack.ID, rsrc.Name) if rsrc_result.Err != nil { panic(rsrc_result.Err) } @@ -43,7 +43,7 @@ Example for list stack resources Example for get resource type schema - schema_result := stackresources.Schema(client, "OS::Heat::Stack") + schema_result := stackresources.Schema(context.TODO(), client, "OS::Heat::Stack") if schema_result.Err != nil { panic(schema_result.Err) } @@ -56,7 +56,7 @@ Example for get resource type schema Example for get resource type Template - tmp_result := stackresources.Template(client, "OS::Heat::Stack") + tmp_result := stackresources.Template(context.TODO(), client, "OS::Heat::Stack") if tmp_result.Err != nil { panic(tmp_result.Err) } diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index 53be5b9b38..9d3be7097a 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -90,7 +90,7 @@ Example to Create an Stack Tags: tags, } - r := stacks.Create(client, createOpts) + r := stacks.Create(context.TODO(), client, createOpts) //dcreated_stack := stacks.CreatedStack() if r.Err != nil { panic(r.Err) @@ -103,7 +103,7 @@ Example to Create an Stack Example for Get Stack - get_result := stacks.Get(client, stackName, created_stack.ID) + get_result := stacks.Get(context.TODO(), client, stackName, created_stack.ID) if get_result.Err != nil { panic(get_result.Err) } @@ -115,7 +115,7 @@ Example for Get Stack Example for Find Stack - find_result := stacks.Find(client, stackIdentity) + find_result := stacks.Find(context.TODO(), client, stackIdentity) if find_result.Err != nil { panic(find_result.Err) } @@ -127,7 +127,7 @@ Example for Find Stack Example for Delete Stack - del_r := stacks.Delete(client, stackName, created_stack.ID) + del_r := stacks.Delete(context.TODO(), client, stackName, created_stack.ID) if del_r.Err != nil { panic(del_r.Err) } @@ -180,7 +180,7 @@ Example to Update a Stack Using the Update (PUT) Method TemplateOpts: &template, } - res := stacks.Update(orchestrationClient, stackName, stackId, stackOpts) + res := stacks.Update(context.TODO(), orchestrationClient, stackName, stackId, stackOpts) if res.Err != nil { panic(res.Err) } @@ -197,7 +197,7 @@ Example to Update a Stack Using the UpdatePatch (PATCH) Method Parameters: params, } - res := stacks.UpdatePatch(orchestrationClient, stackName, stackId, stackOpts) + res := stacks.UpdatePatch(context.TODO(), orchestrationClient, stackName, stackId, stackOpts) if res.Err != nil { panic(res.Err) } diff --git a/openstack/orchestration/v1/stacktemplates/doc.go b/openstack/orchestration/v1/stacktemplates/doc.go index c08ea07da5..214c4576b7 100644 --- a/openstack/orchestration/v1/stacktemplates/doc.go +++ b/openstack/orchestration/v1/stacktemplates/doc.go @@ -9,7 +9,7 @@ specific application stack. Example to get stack template - temp, err := stacktemplates.Get(client, stack.Name, stack.ID).Extract() + temp, err := stacktemplates.Get(context.TODO(), client, stack.Name, stack.ID).Extract() if err != nil { panic(err) } @@ -26,7 +26,7 @@ Example to validate stack template validateOpts := &stacktemplates.ValidateOpts{ Template: string(f2), } - validate_result, err := stacktemplates.Validate(client, validateOpts).Extract() + validate_result, err := stacktemplates.Validate(context.TODO(), client, validateOpts).Extract() if err != nil { // If validate failed, you will get error message here fmt.Println("Validate failed: ", err.Error()) diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 006669a94f..d659558f15 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -25,7 +25,7 @@ Example to create resource providers ParentProvider: "c7f50b40-6f32-4d7a-9f32-9384057be83b" } - rp, err := resourceproviders.Create(placementClient, createOpts).Extract() + rp, err := resourceproviders.Create(context.TODO(), placementClient, createOpts).Extract() if err != nil { panic(err) } @@ -33,7 +33,7 @@ Example to create resource providers Example to Delete a resource provider resourceProviderID := "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" - err := resourceproviders.Delete(placementClient, resourceProviderID).ExtractErr() + err := resourceproviders.Delete(context.TODO(), placementClient, resourceProviderID).ExtractErr() if err != nil { panic(err) } @@ -41,7 +41,7 @@ Example to Delete a resource provider Example to Get a resource provider resourceProviderID := "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" - resourceProvider, err := resourceproviders.Get(placementClient, resourceProviderID).Extract() + resourceProvider, err := resourceproviders.Get(context.TODO(), placementClient, resourceProviderID).Extract() if err != nil { panic(err) } @@ -56,35 +56,35 @@ Example to Update a resource provider } placementClient.Microversion = "1.37" - resourceProvider, err := resourceproviders.Update(placementClient, resourceProviderID).Extract() + resourceProvider, err := resourceproviders.Update(context.TODO(), placementClient, resourceProviderID).Extract() if err != nil { panic(err) } Example to get resource providers usages - rp, err := resourceproviders.GetUsages(placementClient, resourceProviderID).Extract() + rp, err := resourceproviders.GetUsages(context.TODO(), placementClient, resourceProviderID).Extract() if err != nil { panic(err) } Example to get resource providers inventories - rp, err := resourceproviders.GetInventories(placementClient, resourceProviderID).Extract() + rp, err := resourceproviders.GetInventories(context.TODO(), placementClient, resourceProviderID).Extract() if err != nil { panic(err) } Example to get resource providers traits - rp, err := resourceproviders.GetTraits(placementClient, resourceProviderID).Extract() + rp, err := resourceproviders.GetTraits(context.TODO(), placementClient, resourceProviderID).Extract() if err != nil { panic(err) } Example to get resource providers allocations - rp, err := resourceproviders.GetAllocations(placementClient, resourceProviderID).Extract() + rp, err := resourceproviders.GetAllocations(context.TODO(), placementClient, resourceProviderID).Extract() if err != nil { panic(err) } diff --git a/openstack/sharedfilesystems/apiversions/doc.go b/openstack/sharedfilesystems/apiversions/doc.go index c9bf20b5fb..8697eb8b3e 100644 --- a/openstack/sharedfilesystems/apiversions/doc.go +++ b/openstack/sharedfilesystems/apiversions/doc.go @@ -20,7 +20,7 @@ Example to List API Versions Example to Get an API Version - version, err := apiVersions.Get(client, "v2.1").Extract() + version, err := apiVersions.Get(context.TODO(), client, "v2.1").Extract() if err != nil { panic(err) } diff --git a/openstack/sharedfilesystems/v2/shares/doc.go b/openstack/sharedfilesystems/v2/shares/doc.go index e9a8a35246..3946c02861 100644 --- a/openstack/sharedfilesystems/v2/shares/doc.go +++ b/openstack/sharedfilesystems/v2/shares/doc.go @@ -12,7 +12,7 @@ Example to Revert a Share to a Snapshot ID SnapshotID: "ddeac769-9742-497f-b985-5bcfa94a3fd6", } manilaClient.Microversion = "2.27" - err := shares.Revert(manilaClient, shareID, opts).ExtractErr() + err := shares.Revert(context.TODO(), manilaClient, shareID, opts).ExtractErr() if err != nil { panic(err) } @@ -24,7 +24,7 @@ Example to Reset a Share Status Status: "available", } manilaClient.Microversion = "2.7" - err := shares.ResetStatus(manilaClient, shareID, opts).ExtractErr() + err := shares.ResetStatus(context.TODO(), manilaClient, shareID, opts).ExtractErr() if err != nil { panic(err) } @@ -32,7 +32,7 @@ Example to Reset a Share Status Example to Force Delete a Share manilaClient.Microversion = "2.7" - err := shares.ForceDelete(manilaClient, shareID).ExtractErr() + err := shares.ForceDelete(context.TODO(), manilaClient, shareID).ExtractErr() if err != nil { panic(err) } @@ -40,7 +40,7 @@ Example to Force Delete a Share Example to Unmanage a Share manilaClient.Microversion = "2.7" - err := shares.Unmanage(manilaClient, shareID).ExtractErr() + err := shares.Unmanage(context.TODO(), manilaClient, shareID).ExtractErr() if err != nil { panic(err) } diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go index 2607d846ae..7083a2c25e 100644 --- a/openstack/workflow/v2/crontriggers/doc.go +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -48,14 +48,14 @@ Create a cron trigger. This example will start the workflow "echo" each day at 8 "msg": "world", }, } - crontrigger, err := crontriggers.Create(mistralClient, createOpts).Extract() + crontrigger, err := crontriggers.Create(context.TODO(), mistralClient, createOpts).Extract() if err != nil { panic(err) } Get a cron trigger - crontrigger, err := crontriggers.Get(mistralClient, "0520ffd8-f7f1-4f2e-845b-55d953a1cf46").Extract() + crontrigger, err := crontriggers.Get(context.TODO(), mistralClient, "0520ffd8-f7f1-4f2e-845b-55d953a1cf46").Extract() if err != nil { panic(err) } @@ -64,7 +64,7 @@ Get a cron trigger Delete a cron trigger - res := crontriggers.Delete(mistralClient, "0520ffd8-f7f1-4f2e-845b-55d953a1cf46") + res := crontriggers.Delete(context.TODO(), mistralClient, "0520ffd8-f7f1-4f2e-845b-55d953a1cf46") if res.Err != nil { panic(res.Err) } diff --git a/openstack/workflow/v2/executions/doc.go b/openstack/workflow/v2/executions/doc.go index 69ff258aee..aff179c855 100644 --- a/openstack/workflow/v2/executions/doc.go +++ b/openstack/workflow/v2/executions/doc.go @@ -46,14 +46,14 @@ Create an execution Description: "this is a description", } - execution, err := executions.Create(mistralClient, createOpts).Extract() + execution, err := executions.Create(context.TODO(), mistralClient, createOpts).Extract() if err != nil { panic(err) } Get an execution - execution, err := executions.Get(mistralClient, "50bb59f1-eb77-4017-a77f-6d575b002667").Extract() + execution, err := executions.Get(context.TODO(), mistralClient, "50bb59f1-eb77-4017-a77f-6d575b002667").Extract() if err != nil { panic(err) } @@ -61,7 +61,7 @@ Get an execution Delete an execution - res := executions.Delete(mistralClient, "50bb59f1-eb77-4017-a77f-6d575b002667") + res := executions.Delete(context.TODO(), mistralClient, "50bb59f1-eb77-4017-a77f-6d575b002667") if res.Err != nil { panic(res.Err) } diff --git a/openstack/workflow/v2/workflows/doc.go b/openstack/workflow/v2/workflows/doc.go index 888a407e74..52638a3469 100644 --- a/openstack/workflow/v2/workflows/doc.go +++ b/openstack/workflow/v2/workflows/doc.go @@ -28,7 +28,7 @@ List workflows Get a workflow - workflow, err := workflows.Get(mistralClient, "604a3a1e-94e3-4066-a34a-aa56873ef236").Extract() + workflow, err := workflows.Get(context.TODO(), mistralClient, "604a3a1e-94e3-4066-a34a-aa56873ef236").Extract() if err != nil { t.Fatalf("Unable to get workflow %s: %v", id, err) } @@ -56,7 +56,7 @@ Create a workflow Namespace: "some-namespace", } - workflow, err := workflows.Create(mistralClient, createOpts).Extract() + workflow, err := workflows.Create(context.TODO(), mistralClient, createOpts).Extract() if err != nil { panic(err) } From 0d4ceb73b8045513cd675c91aa69e641f1a0107d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 14:31:16 +0000 Subject: [PATCH 1799/2296] acceptance: Don't mix and match block storage API versions We should use v3 for extension tests since v2 was removed in the Cinder 19.0.0 (Xena) release [1]. [1] https://docs.openstack.org/releasenotes/cinder/xena.html#relnotes-19-0-0-stable-xena Signed-off-by: Stephen Finucane --- .../openstack/blockstorage/extensions/extensions.go | 13 ++++++------- .../blockstorage/extensions/volumeactions_test.go | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/internal/acceptance/openstack/blockstorage/extensions/extensions.go b/internal/acceptance/openstack/blockstorage/extensions/extensions.go index 4368b23a89..d66a91800d 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/internal/acceptance/openstack/blockstorage/extensions/extensions.go @@ -14,8 +14,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumeactions" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" - v3 "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" @@ -284,7 +283,7 @@ func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volume return err } - vol, err := v3.Get(context.TODO(), client, volume.ID).Extract() + vol, err := volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return err } @@ -302,7 +301,7 @@ func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volume return err } - vol, err = v3.Get(context.TODO(), client, volume.ID).Extract() + vol, err = volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return err } @@ -315,7 +314,7 @@ func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volume } // ChangeVolumeType will extend the size of a volume. -func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume, vt *volumetypes.VolumeType) error { +func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, vt *volumetypes.VolumeType) error { t.Logf("Attempting to change the type of volume %s from %s to %s", volume.ID, volume.VolumeType, vt.Name) changeOpts := volumeactions.ChangeTypeOpts{ @@ -339,7 +338,7 @@ func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *v } // ResetVolumeStatus will reset the status of a volume. -func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume *v3.Volume, status string) error { +func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.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{ @@ -397,7 +396,7 @@ func ReImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Vo return err } - vol, err := v3.Get(context.TODO(), client, volume.ID).Extract() + vol, err := volumes.Get(context.TODO(), client, volume.ID).Extract() if err != nil { return err } diff --git a/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go index 2768c90132..198f113de5 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go @@ -12,7 +12,7 @@ import ( blockstorageV3 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v3" compute "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/compute/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/v2/testhelper" ) From 195529199a8dc8a5726418aacb49851656638156 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 25 Mar 2024 16:20:25 +0000 Subject: [PATCH 1800/2296] blockstorage: Fix doc indentation Use consistent indentation. Signed-off-by: Stephen Finucane --- .../extensions/availabilityzones/doc.go | 22 ++++----- .../blockstorage/extensions/limits/doc.go | 2 +- openstack/blockstorage/v3/volumetypes/doc.go | 48 +++++++++---------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/openstack/blockstorage/extensions/availabilityzones/doc.go b/openstack/blockstorage/extensions/availabilityzones/doc.go index d3bbc901c1..eb3903ad13 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(context.TODO()) - if err != nil { - panic(err) - } + allPages, err := availabilityzones.List(volumeClient).AllPages(context.TODO()) + 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 2183bd80a6..4bd845122e 100644 --- a/openstack/blockstorage/extensions/limits/doc.go +++ b/openstack/blockstorage/extensions/limits/doc.go @@ -5,7 +5,7 @@ Example to Retrieve Limits limits, err := limits.Get(context.TODO(), blockStorageClient).Extract() if err != nil { - panic(err) + panic(err) } fmt.Printf("%+v\n", limits) diff --git a/openstack/blockstorage/v3/volumetypes/doc.go b/openstack/blockstorage/v3/volumetypes/doc.go index a0d6fa20b2..63ff21918d 100644 --- a/openstack/blockstorage/v3/volumetypes/doc.go +++ b/openstack/blockstorage/v3/volumetypes/doc.go @@ -163,26 +163,26 @@ 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(context.TODO(), 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(context.TODO(), 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(context.TODO(), client, typeID, encryptionID).ExtractErr() - if err != nil{ - panic(err) - } + typeID := "7ffaca22-f646-41d4-b79d-d7e4452ef8cc" + encryptionID := ""81e069c6-7394-4856-8df7-3b237ca61f74 + err := volumetypes.DeleteEncryption(context.TODO(), client, typeID, encryptionID).ExtractErr() + if err != nil{ + panic(err) + } Example to Update the Encryption of a Volume Type @@ -209,12 +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 From 40cf5d78ac52f06c81e48ca4c311a8545d150933 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 14:37:53 +0000 Subject: [PATCH 1801/2296] blockstorage: Merge standalone extensions Extensions are no longer a thing in any OpenStack service except Neutron. As such, maintaining what were formerly extensions in a separate module makes little sense and actively hurts us as these extensions are themselves extended with new microversions. Resolve this for the block storage service first. In this case, we simply migrate the various standalone extensions (i.e. the extensions that did not build on existing resources) into the v2 and v3 modules. This appears like we are creating a lot of new code, but we simply ran the following Python script (because Bash arrays are awful): import shutil for extension in { 'availabilityzones', 'backups', 'limits', 'quotasets', 'schedulerstats', 'services', }: src = f'openstack/blockstorage/extensions/{extension}' for api_version in {'v2', 'v3'}: dst = f'openstack/blockstorage/{api_version}/{extension}' shutil.copytree(src, dst) shutil.rmtree(src) We then fix the imports in the new files and then 'git add .' Once this is done, we need to do something very similar for the tests for these "extensions" in 'internal/acceptance/blockstorage/extensions'. Once again we copy-paste the code to both v2 and v3 modules, but this time we must both update imports and modify the v2 variants of the tests to use a v2 client. Only volumetransfers is excluded since we want to rename that module. Signed-off-by: Stephen Finucane --- .../blockstorage/extensions/extensions.go | 85 ----- .../openstack/blockstorage/v2/backups_test.go | 89 +++++ .../openstack/blockstorage/v2/blockstorage.go | 84 +++++ .../openstack/blockstorage/v2/limits_test.go | 39 ++ .../blockstorage/v2/schedulerstats_test.go | 36 ++ .../blockstorage/v2/services_test.go | 32 ++ .../{extensions => v3}/backups_test.go | 17 +- .../openstack/blockstorage/v3/blockstorage.go | 84 +++++ .../{extensions => v3}/limits_test.go | 4 +- .../blockstorage/v3/quotaset_test.go | 2 +- .../{extensions => v3}/schedulerstats_test.go | 4 +- .../{extensions => v3}/services_test.go | 4 +- .../availabilityzones/doc.go | 0 .../availabilityzones/requests.go | 0 .../availabilityzones/results.go | 0 .../availabilityzones/testing/doc.go | 0 .../testing/fixtures_test.go | 2 +- .../testing/requests_test.go | 2 +- .../availabilityzones/urls.go | 0 .../{extensions => v2}/backups/doc.go | 0 openstack/blockstorage/v2/backups/requests.go | 354 ++++++++++++++++++ .../{extensions => v2}/backups/results.go | 0 .../backups/testing/fixtures_test.go | 2 +- .../backups/testing/requests_test.go | 2 +- .../{extensions => v2}/backups/urls.go | 0 .../{extensions => v2}/limits/doc.go | 0 .../{extensions => v2}/limits/requests.go | 0 .../{extensions => v2}/limits/results.go | 0 .../limits/testing/fixtures_test.go | 4 +- .../limits/testing/requests_test.go | 2 +- .../{extensions => v2}/limits/urls.go | 0 .../{extensions => v2}/quotasets/doc.go | 0 .../{extensions => v2}/quotasets/requests.go | 0 .../{extensions => v2}/quotasets/results.go | 0 .../quotasets/testing/doc.go | 0 .../quotasets/testing/fixtures_test.go | 2 +- .../quotasets/testing/requests_test.go | 2 +- .../{extensions => v2}/quotasets/urls.go | 0 .../{extensions => v2}/schedulerstats/doc.go | 0 .../schedulerstats/requests.go | 0 .../schedulerstats/results.go | 0 .../schedulerstats/testing/fixtures_test.go | 2 +- .../schedulerstats/testing/requests_test.go | 2 +- .../{extensions => v2}/schedulerstats/urls.go | 0 .../{extensions => v2}/services/doc.go | 0 .../{extensions => v2}/services/requests.go | 0 .../{extensions => v2}/services/results.go | 0 .../services/testing/fixtures_test.go | 2 +- .../services/testing/requests_test.go | 2 +- .../{extensions => v2}/services/urls.go | 0 .../blockstorage/v3/availabilityzones/doc.go | 21 ++ .../v3/availabilityzones/requests.go | 13 + .../v3/availabilityzones/results.go | 33 ++ .../v3/availabilityzones/testing/doc.go | 2 + .../testing/fixtures_test.go | 52 +++ .../testing/requests_test.go | 26 ++ .../blockstorage/v3/availabilityzones/urls.go | 7 + openstack/blockstorage/v3/backups/doc.go | 124 ++++++ .../{extensions => v3}/backups/requests.go | 0 openstack/blockstorage/v3/backups/results.go | 351 +++++++++++++++++ .../v3/backups/testing/fixtures_test.go | 338 +++++++++++++++++ .../v3/backups/testing/requests_test.go | 213 +++++++++++ openstack/blockstorage/v3/backups/urls.go | 47 +++ openstack/blockstorage/v3/limits/doc.go | 13 + openstack/blockstorage/v3/limits/requests.go | 15 + openstack/blockstorage/v3/limits/results.go | 80 ++++ .../v3/limits/testing/fixtures_test.go | 129 +++++++ .../v3/limits/testing/requests_test.go | 20 + openstack/blockstorage/v3/limits/urls.go | 11 + openstack/blockstorage/v3/quotasets/doc.go | 60 +++ .../blockstorage/v3/quotasets/requests.go | 117 ++++++ .../blockstorage/v3/quotasets/results.go | 209 +++++++++++ .../blockstorage/v3/quotasets/testing/doc.go | 2 + .../v3/quotasets/testing/fixtures_test.go | 179 +++++++++ .../v3/quotasets/testing/requests_test.go | 81 ++++ openstack/blockstorage/v3/quotasets/urls.go | 21 ++ .../blockstorage/v3/schedulerstats/doc.go | 23 ++ .../v3/schedulerstats/requests.go | 43 +++ .../blockstorage/v3/schedulerstats/results.go | 116 ++++++ .../schedulerstats/testing/fixtures_test.go | 110 ++++++ .../schedulerstats/testing/requests_test.go | 39 ++ .../blockstorage/v3/schedulerstats/urls.go | 7 + openstack/blockstorage/v3/services/doc.go | 22 ++ .../blockstorage/v3/services/requests.go | 42 +++ openstack/blockstorage/v3/services/results.go | 88 +++++ .../v3/services/testing/fixtures_test.go | 97 +++++ .../v3/services/testing/requests_test.go | 42 +++ openstack/blockstorage/v3/services/urls.go | 7 + 88 files changed, 3546 insertions(+), 114 deletions(-) create mode 100644 internal/acceptance/openstack/blockstorage/v2/backups_test.go create mode 100644 internal/acceptance/openstack/blockstorage/v2/limits_test.go create mode 100644 internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go create mode 100644 internal/acceptance/openstack/blockstorage/v2/services_test.go rename internal/acceptance/openstack/blockstorage/{extensions => v3}/backups_test.go (76%) rename internal/acceptance/openstack/blockstorage/{extensions => v3}/limits_test.go (94%) rename internal/acceptance/openstack/blockstorage/{extensions => v3}/schedulerstats_test.go (87%) rename internal/acceptance/openstack/blockstorage/{extensions => v3}/services_test.go (87%) rename openstack/blockstorage/{extensions => v2}/availabilityzones/doc.go (100%) rename openstack/blockstorage/{extensions => v2}/availabilityzones/requests.go (100%) rename openstack/blockstorage/{extensions => v2}/availabilityzones/results.go (100%) rename openstack/blockstorage/{extensions => v2}/availabilityzones/testing/doc.go (100%) rename openstack/blockstorage/{extensions => v2}/availabilityzones/testing/fixtures_test.go (97%) rename openstack/blockstorage/{extensions => v2}/availabilityzones/testing/requests_test.go (95%) rename openstack/blockstorage/{extensions => v2}/availabilityzones/urls.go (100%) rename openstack/blockstorage/{extensions => v2}/backups/doc.go (100%) create mode 100644 openstack/blockstorage/v2/backups/requests.go rename openstack/blockstorage/{extensions => v2}/backups/results.go (100%) rename openstack/blockstorage/{extensions => v2}/backups/testing/fixtures_test.go (99%) rename openstack/blockstorage/{extensions => v2}/backups/testing/requests_test.go (98%) rename openstack/blockstorage/{extensions => v2}/backups/urls.go (100%) rename openstack/blockstorage/{extensions => v2}/limits/doc.go (100%) rename openstack/blockstorage/{extensions => v2}/limits/requests.go (100%) rename openstack/blockstorage/{extensions => v2}/limits/results.go (100%) rename openstack/blockstorage/{extensions => v2}/limits/testing/fixtures_test.go (96%) rename openstack/blockstorage/{extensions => v2}/limits/testing/requests_test.go (83%) rename openstack/blockstorage/{extensions => v2}/limits/urls.go (100%) rename openstack/blockstorage/{extensions => v2}/quotasets/doc.go (100%) rename openstack/blockstorage/{extensions => v2}/quotasets/requests.go (100%) rename openstack/blockstorage/{extensions => v2}/quotasets/results.go (100%) rename openstack/blockstorage/{extensions => v2}/quotasets/testing/doc.go (100%) rename openstack/blockstorage/{extensions => v2}/quotasets/testing/fixtures_test.go (98%) rename openstack/blockstorage/{extensions => v2}/quotasets/testing/requests_test.go (96%) rename openstack/blockstorage/{extensions => v2}/quotasets/urls.go (100%) rename openstack/blockstorage/{extensions => v2}/schedulerstats/doc.go (100%) rename openstack/blockstorage/{extensions => v2}/schedulerstats/requests.go (100%) rename openstack/blockstorage/{extensions => v2}/schedulerstats/results.go (100%) rename openstack/blockstorage/{extensions => v2}/schedulerstats/testing/fixtures_test.go (97%) rename openstack/blockstorage/{extensions => v2}/schedulerstats/testing/requests_test.go (91%) rename openstack/blockstorage/{extensions => v2}/schedulerstats/urls.go (100%) rename openstack/blockstorage/{extensions => v2}/services/doc.go (100%) rename openstack/blockstorage/{extensions => v2}/services/requests.go (100%) rename openstack/blockstorage/{extensions => v2}/services/results.go (100%) rename openstack/blockstorage/{extensions => v2}/services/testing/fixtures_test.go (96%) rename openstack/blockstorage/{extensions => v2}/services/testing/requests_test.go (92%) rename openstack/blockstorage/{extensions => v2}/services/urls.go (100%) create mode 100644 openstack/blockstorage/v3/availabilityzones/doc.go create mode 100644 openstack/blockstorage/v3/availabilityzones/requests.go create mode 100644 openstack/blockstorage/v3/availabilityzones/results.go create mode 100644 openstack/blockstorage/v3/availabilityzones/testing/doc.go create mode 100644 openstack/blockstorage/v3/availabilityzones/testing/fixtures_test.go create mode 100644 openstack/blockstorage/v3/availabilityzones/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/availabilityzones/urls.go create mode 100644 openstack/blockstorage/v3/backups/doc.go rename openstack/blockstorage/{extensions => v3}/backups/requests.go (100%) create mode 100644 openstack/blockstorage/v3/backups/results.go create mode 100644 openstack/blockstorage/v3/backups/testing/fixtures_test.go create mode 100644 openstack/blockstorage/v3/backups/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/backups/urls.go create mode 100644 openstack/blockstorage/v3/limits/doc.go create mode 100644 openstack/blockstorage/v3/limits/requests.go create mode 100644 openstack/blockstorage/v3/limits/results.go create mode 100644 openstack/blockstorage/v3/limits/testing/fixtures_test.go create mode 100644 openstack/blockstorage/v3/limits/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/limits/urls.go create mode 100644 openstack/blockstorage/v3/quotasets/doc.go create mode 100644 openstack/blockstorage/v3/quotasets/requests.go create mode 100644 openstack/blockstorage/v3/quotasets/results.go create mode 100644 openstack/blockstorage/v3/quotasets/testing/doc.go create mode 100644 openstack/blockstorage/v3/quotasets/testing/fixtures_test.go create mode 100644 openstack/blockstorage/v3/quotasets/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/quotasets/urls.go create mode 100644 openstack/blockstorage/v3/schedulerstats/doc.go create mode 100644 openstack/blockstorage/v3/schedulerstats/requests.go create mode 100644 openstack/blockstorage/v3/schedulerstats/results.go create mode 100644 openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go create mode 100644 openstack/blockstorage/v3/schedulerstats/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/schedulerstats/urls.go create mode 100644 openstack/blockstorage/v3/services/doc.go create mode 100644 openstack/blockstorage/v3/services/requests.go create mode 100644 openstack/blockstorage/v3/services/results.go create mode 100644 openstack/blockstorage/v3/services/testing/fixtures_test.go create mode 100644 openstack/blockstorage/v3/services/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/services/urls.go diff --git a/internal/acceptance/openstack/blockstorage/extensions/extensions.go b/internal/acceptance/openstack/blockstorage/extensions/extensions.go index d66a91800d..a6111d191a 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/internal/acceptance/openstack/blockstorage/extensions/extensions.go @@ -12,13 +12,11 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/v2/testhelper" ) // CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be @@ -202,74 +200,6 @@ func SetImageMetadata(t *testing.T, client *gophercloud.ServiceClient, volume *v return nil } -// CreateBackup will create a backup based on a volume. An error will be -// will be returned if the backup could not be created. -func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID string) (*backups.Backup, error) { - t.Logf("Attempting to create a backup of volume %s", volumeID) - - backupName := tools.RandomString("ACPTTEST", 16) - createOpts := backups.CreateOpts{ - VolumeID: volumeID, - Name: backupName, - } - - backup, err := backups.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return nil, err - } - - err = WaitForBackupStatus(client, backup.ID, "available") - if err != nil { - return nil, err - } - - backup, err = backups.Get(context.TODO(), client, backup.ID).Extract() - if err != nil { - return nil, err - } - - t.Logf("Successfully created backup %s", backup.ID) - tools.PrintResource(t, backup) - - th.AssertEquals(t, backup.Name, backupName) - - return backup, nil -} - -// DeleteBackup will delete a backup. A fatal error will occur if the backup -// could not be deleted. This works best when used as a deferred function. -func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { - if err := backups.Delete(context.TODO(), client, backupID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { - t.Logf("Backup %s is already deleted", backupID) - return - } - t.Fatalf("Unable to delete backup %s: %s", backupID, err) - } - - t.Logf("Deleted backup %s", backupID) -} - -// WaitForBackupStatus will continually poll a backup, checking for a particular -// status. It will do this for the amount of seconds defined. -func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) error { - return tools.WaitFor(func(ctx context.Context) (bool, error) { - current, err := backups.Get(ctx, client, id).Extract() - if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok && status == "deleted" { - return true, nil - } - return false, err - } - - if current.Status == status { - return true, nil - } - - return false, nil - }) -} - // SetBootable will set a bootable status to a volume. func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { t.Logf("Attempting to apply bootable status to volume %s", volume.ID) @@ -359,21 +289,6 @@ func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume * return nil } -// ResetBackupStatus will reset the status of a backup. -func ResetBackupStatus(t *testing.T, client *gophercloud.ServiceClient, backup *backups.Backup, status string) error { - t.Logf("Attempting to reset the status of backup %s from %s to %s", backup.ID, backup.Status, status) - - resetOpts := backups.ResetStatusOpts{ - Status: status, - } - err := backups.ResetStatus(context.TODO(), client, backup.ID, resetOpts).ExtractErr() - if err != nil { - return err - } - - return WaitForBackupStatus(client, backup.ID, status) -} - // 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/internal/acceptance/openstack/blockstorage/v2/backups_test.go b/internal/acceptance/openstack/blockstorage/v2/backups_test.go new file mode 100644 index 0000000000..58a42db7f8 --- /dev/null +++ b/internal/acceptance/openstack/blockstorage/v2/backups_test.go @@ -0,0 +1,89 @@ +//go:build acceptance || blockstorage +// +build acceptance blockstorage + +package v2 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/backups" + + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestBackupsCRUD(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + backup, err := CreateBackup(t, blockClient, volume.ID) + th.AssertNoErr(t, err) + defer DeleteBackup(t, blockClient, backup.ID) + + allPages, err := backups.List(blockClient, nil).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allBackups, err := backups.ExtractBackups(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, v := range allBackups { + if backup.Name == v.Name { + found = true + } + } + + th.AssertEquals(t, found, true) +} + +func TestBackupsResetStatus(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + backup, err := CreateBackup(t, blockClient, volume.ID) + th.AssertNoErr(t, err) + defer DeleteBackup(t, blockClient, backup.ID) + + err = ResetBackupStatus(t, blockClient, backup, "error") + th.AssertNoErr(t, err) + + err = ResetBackupStatus(t, blockClient, backup, "available") + th.AssertNoErr(t, err) +} + +func TestBackupsForceDelete(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + backup, err := CreateBackup(t, blockClient, volume.ID) + th.AssertNoErr(t, err) + defer DeleteBackup(t, blockClient, backup.ID) + + err = WaitForBackupStatus(blockClient, backup.ID, "available") + th.AssertNoErr(t, err) + + err = backups.ForceDelete(context.TODO(), blockClient, backup.ID).ExtractErr() + th.AssertNoErr(t, err) + + err = WaitForBackupStatus(blockClient, backup.ID, "deleted") + th.AssertNoErr(t, err) +} diff --git a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go index a29c85a6b4..8d2b9e31fc 100644 --- a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/backups" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -165,3 +166,86 @@ func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *s t.Logf("Successfully deleted snapshot: %s", snapshot.ID) } + +// CreateBackup will create a backup based on a volume. An error will be +// will be returned if the backup could not be created. +func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID string) (*backups.Backup, error) { + t.Logf("Attempting to create a backup of volume %s", volumeID) + + backupName := tools.RandomString("ACPTTEST", 16) + createOpts := backups.CreateOpts{ + VolumeID: volumeID, + Name: backupName, + } + + backup, err := backups.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + return nil, err + } + + err = WaitForBackupStatus(client, backup.ID, "available") + if err != nil { + return nil, err + } + + backup, err = backups.Get(context.TODO(), client, backup.ID).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created backup %s", backup.ID) + tools.PrintResource(t, backup) + + th.AssertEquals(t, backup.Name, backupName) + + return backup, nil +} + +// DeleteBackup will delete a backup. A fatal error will occur if the backup +// could not be deleted. This works best when used as a deferred function. +func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { + if err := backups.Delete(context.TODO(), client, backupID).ExtractErr(); err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + t.Logf("Backup %s is already deleted", backupID) + return + } + t.Fatalf("Unable to delete backup %s: %s", backupID, err) + } + + t.Logf("Deleted backup %s", backupID) +} + +// WaitForBackupStatus will continually poll a backup, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) error { + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := backups.Get(ctx, client, id).Extract() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok && status == "deleted" { + return true, nil + } + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} + +// ResetBackupStatus will reset the status of a backup. +func ResetBackupStatus(t *testing.T, client *gophercloud.ServiceClient, backup *backups.Backup, status string) error { + t.Logf("Attempting to reset the status of backup %s from %s to %s", backup.ID, backup.Status, status) + + resetOpts := backups.ResetStatusOpts{ + Status: status, + } + err := backups.ResetStatus(context.TODO(), client, backup.ID, resetOpts).ExtractErr() + if err != nil { + return err + } + + return WaitForBackupStatus(client, backup.ID, status) +} diff --git a/internal/acceptance/openstack/blockstorage/v2/limits_test.go b/internal/acceptance/openstack/blockstorage/v2/limits_test.go new file mode 100644 index 0000000000..f8938493e8 --- /dev/null +++ b/internal/acceptance/openstack/blockstorage/v2/limits_test.go @@ -0,0 +1,39 @@ +package v2 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestLimits(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + client, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + limits, err := limits.Get(context.TODO(), client).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, limits) + + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalVolumes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalSnapshots, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalVolumeGigabytes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalBackups, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.MaxTotalBackupGigabytes, 0) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalVolumesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalVolumesUsed, limits.Absolute.MaxTotalVolumes) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalGigabytesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalGigabytesUsed, limits.Absolute.MaxTotalVolumeGigabytes) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalSnapshotsUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalSnapshotsUsed, limits.Absolute.MaxTotalSnapshots) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalBackupsUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalBackupsUsed, limits.Absolute.MaxTotalBackups) + th.AssertIntGreaterOrEqual(t, limits.Absolute.TotalBackupGigabytesUsed, 0) + th.AssertIntLesserOrEqual(t, limits.Absolute.TotalBackupGigabytesUsed, limits.Absolute.MaxTotalBackupGigabytes) +} diff --git a/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go new file mode 100644 index 0000000000..ad4be47cb2 --- /dev/null +++ b/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go @@ -0,0 +1,36 @@ +//go:build acceptance || blockstorage +// +build acceptance blockstorage + +package v2 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/schedulerstats" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestSchedulerStatsList(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + clients.RequireAdmin(t) + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + listOpts := schedulerstats.ListOpts{ + Detail: true, + } + + allPages, err := schedulerstats.List(blockClient, listOpts).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allStats, err := schedulerstats.ExtractStoragePools(allPages) + th.AssertNoErr(t, err) + + for _, stat := range allStats { + tools.PrintResource(t, stat) + } +} diff --git a/internal/acceptance/openstack/blockstorage/v2/services_test.go b/internal/acceptance/openstack/blockstorage/v2/services_test.go new file mode 100644 index 0000000000..de40733c92 --- /dev/null +++ b/internal/acceptance/openstack/blockstorage/v2/services_test.go @@ -0,0 +1,32 @@ +//go:build acceptance || blockstorage +// +build acceptance blockstorage + +package v2 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestServicesList(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + clients.RequireAdmin(t) + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + allPages, err := services.List(blockClient, services.ListOpts{}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allServices, err := services.ExtractServices(allPages) + th.AssertNoErr(t, err) + + for _, service := range allServices { + tools.PrintResource(t, service) + } +} diff --git a/internal/acceptance/openstack/blockstorage/extensions/backups_test.go b/internal/acceptance/openstack/blockstorage/v3/backups_test.go similarity index 76% rename from internal/acceptance/openstack/blockstorage/extensions/backups_test.go rename to internal/acceptance/openstack/blockstorage/v3/backups_test.go index e90a68482a..dff3ca34d6 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/backups_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/backups_test.go @@ -1,16 +1,15 @@ //go:build acceptance || blockstorage // +build acceptance blockstorage -package extensions +package v3 import ( "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/backups" - blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v3" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -18,9 +17,9 @@ func TestBackupsCRUD(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - volume, err := blockstorage.CreateVolume(t, blockClient) + volume, err := CreateVolume(t, blockClient) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) + defer DeleteVolume(t, blockClient, volume) backup, err := CreateBackup(t, blockClient, volume.ID) th.AssertNoErr(t, err) @@ -46,9 +45,9 @@ func TestBackupsResetStatus(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - volume, err := blockstorage.CreateVolume(t, blockClient) + volume, err := CreateVolume(t, blockClient) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) + defer DeleteVolume(t, blockClient, volume) backup, err := CreateBackup(t, blockClient, volume.ID) th.AssertNoErr(t, err) @@ -65,9 +64,9 @@ func TestBackupsForceDelete(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - volume, err := blockstorage.CreateVolume(t, blockClient) + volume, err := CreateVolume(t, blockClient) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) + defer DeleteVolume(t, blockClient, volume) backup, err := CreateBackup(t, blockClient, volume.ID) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 79ae8b7c29..0b2cbdea6e 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/backups" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/qos" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" @@ -383,3 +384,86 @@ func DeleteQoS(t *testing.T, client *gophercloud.ServiceClient, qs *qos.QoS) { t.Logf("Successfully deleted QoS: %s", qs.ID) } + +// CreateBackup will create a backup based on a volume. An error will be +// will be returned if the backup could not be created. +func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID string) (*backups.Backup, error) { + t.Logf("Attempting to create a backup of volume %s", volumeID) + + backupName := tools.RandomString("ACPTTEST", 16) + createOpts := backups.CreateOpts{ + VolumeID: volumeID, + Name: backupName, + } + + backup, err := backups.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + return nil, err + } + + err = WaitForBackupStatus(client, backup.ID, "available") + if err != nil { + return nil, err + } + + backup, err = backups.Get(context.TODO(), client, backup.ID).Extract() + if err != nil { + return nil, err + } + + t.Logf("Successfully created backup %s", backup.ID) + tools.PrintResource(t, backup) + + th.AssertEquals(t, backup.Name, backupName) + + return backup, nil +} + +// DeleteBackup will delete a backup. A fatal error will occur if the backup +// could not be deleted. This works best when used as a deferred function. +func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { + if err := backups.Delete(context.TODO(), client, backupID).ExtractErr(); err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok { + t.Logf("Backup %s is already deleted", backupID) + return + } + t.Fatalf("Unable to delete backup %s: %s", backupID, err) + } + + t.Logf("Deleted backup %s", backupID) +} + +// WaitForBackupStatus will continually poll a backup, checking for a particular +// status. It will do this for the amount of seconds defined. +func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) error { + return tools.WaitFor(func(ctx context.Context) (bool, error) { + current, err := backups.Get(ctx, client, id).Extract() + if err != nil { + if _, ok := err.(gophercloud.ErrDefault404); ok && status == "deleted" { + return true, nil + } + return false, err + } + + if current.Status == status { + return true, nil + } + + return false, nil + }) +} + +// ResetBackupStatus will reset the status of a backup. +func ResetBackupStatus(t *testing.T, client *gophercloud.ServiceClient, backup *backups.Backup, status string) error { + t.Logf("Attempting to reset the status of backup %s from %s to %s", backup.ID, backup.Status, status) + + resetOpts := backups.ResetStatusOpts{ + Status: status, + } + err := backups.ResetStatus(context.TODO(), client, backup.ID, resetOpts).ExtractErr() + if err != nil { + return err + } + + return WaitForBackupStatus(client, backup.ID, status) +} diff --git a/internal/acceptance/openstack/blockstorage/extensions/limits_test.go b/internal/acceptance/openstack/blockstorage/v3/limits_test.go similarity index 94% rename from internal/acceptance/openstack/blockstorage/extensions/limits_test.go rename to internal/acceptance/openstack/blockstorage/v3/limits_test.go index 24bcee1096..3f7024f5cb 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/limits_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/limits_test.go @@ -1,4 +1,4 @@ -package extensions +package v3 import ( "context" @@ -6,7 +6,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/limits" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/limits" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go index 4a546516ff..728b4f28ec 100644 --- a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -11,7 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/quotasets" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go similarity index 87% rename from internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go rename to internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go index 5760970cac..5e5b67854a 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/schedulerstats_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go @@ -1,7 +1,7 @@ //go:build acceptance || blockstorage // +build acceptance blockstorage -package extensions +package v3 import ( "context" @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerstats" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/schedulerstats" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/blockstorage/extensions/services_test.go b/internal/acceptance/openstack/blockstorage/v3/services_test.go similarity index 87% rename from internal/acceptance/openstack/blockstorage/extensions/services_test.go rename to internal/acceptance/openstack/blockstorage/v3/services_test.go index 92ae10a016..58593cd9eb 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/services_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/services_test.go @@ -1,7 +1,7 @@ //go:build acceptance || blockstorage // +build acceptance blockstorage -package extensions +package v3 import ( "context" @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/services" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/services" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/openstack/blockstorage/extensions/availabilityzones/doc.go b/openstack/blockstorage/v2/availabilityzones/doc.go similarity index 100% rename from openstack/blockstorage/extensions/availabilityzones/doc.go rename to openstack/blockstorage/v2/availabilityzones/doc.go diff --git a/openstack/blockstorage/extensions/availabilityzones/requests.go b/openstack/blockstorage/v2/availabilityzones/requests.go similarity index 100% rename from openstack/blockstorage/extensions/availabilityzones/requests.go rename to openstack/blockstorage/v2/availabilityzones/requests.go diff --git a/openstack/blockstorage/extensions/availabilityzones/results.go b/openstack/blockstorage/v2/availabilityzones/results.go similarity index 100% rename from openstack/blockstorage/extensions/availabilityzones/results.go rename to openstack/blockstorage/v2/availabilityzones/results.go diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/doc.go b/openstack/blockstorage/v2/availabilityzones/testing/doc.go similarity index 100% rename from openstack/blockstorage/extensions/availabilityzones/testing/doc.go rename to openstack/blockstorage/v2/availabilityzones/testing/doc.go diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go b/openstack/blockstorage/v2/availabilityzones/testing/fixtures_test.go similarity index 97% rename from openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go rename to openstack/blockstorage/v2/availabilityzones/testing/fixtures_test.go index 2d7239347d..b13e464abb 100644 --- a/openstack/blockstorage/extensions/availabilityzones/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/availabilityzones/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/availabilityzones" + az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/availabilityzones" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go b/openstack/blockstorage/v2/availabilityzones/testing/requests_test.go similarity index 95% rename from openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go rename to openstack/blockstorage/v2/availabilityzones/testing/requests_test.go index 07f74d6aa0..4e8e751f45 100644 --- a/openstack/blockstorage/extensions/availabilityzones/testing/requests_test.go +++ b/openstack/blockstorage/v2/availabilityzones/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/availabilityzones" + az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/availabilityzones" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/blockstorage/extensions/availabilityzones/urls.go b/openstack/blockstorage/v2/availabilityzones/urls.go similarity index 100% rename from openstack/blockstorage/extensions/availabilityzones/urls.go rename to openstack/blockstorage/v2/availabilityzones/urls.go diff --git a/openstack/blockstorage/extensions/backups/doc.go b/openstack/blockstorage/v2/backups/doc.go similarity index 100% rename from openstack/blockstorage/extensions/backups/doc.go rename to openstack/blockstorage/v2/backups/doc.go diff --git a/openstack/blockstorage/v2/backups/requests.go b/openstack/blockstorage/v2/backups/requests.go new file mode 100644 index 0000000000..f8e0d67daf --- /dev/null +++ b/openstack/blockstorage/v2/backups/requests.go @@ -0,0 +1,354 @@ +package backups + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToBackupCreateMap() (map[string]interface{}, error) +} + +// CreateOpts contains options for creating a Backup. This object is passed to +// the backups.Create function. For more information about these parameters, +// see the Backup object. +type CreateOpts struct { + // VolumeID is the ID of the volume to create the backup from. + VolumeID string `json:"volume_id" required:"true"` + + // Force will force the creation of a backup regardless of the + //volume's status. + Force bool `json:"force,omitempty"` + + // Name is the name of the backup. + Name string `json:"name,omitempty"` + + // Description is the description of the backup. + Description string `json:"description,omitempty"` + + // Metadata is metadata for the backup. + // Requires microversion 3.43 or later. + Metadata map[string]string `json:"metadata,omitempty"` + + // Container is a container to store the backup. + Container string `json:"container,omitempty"` + + // Incremental is whether the backup should be incremental or not. + Incremental bool `json:"incremental,omitempty"` + + // SnapshotID is the ID of a snapshot to backup. + SnapshotID string `json:"snapshot_id,omitempty"` + + // AvailabilityZone is an availability zone to locate the volume or snapshot. + // Requires microversion 3.51 or later. + AvailabilityZone string `json:"availability_zone,omitempty"` +} + +// ToBackupCreateMap assembles a request body based on the contents of a +// CreateOpts. +func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "backup") +} + +// Create will create a new Backup based on the values in CreateOpts. To +// extract the Backup object from the response, call the Extract method on the +// CreateResult. +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToBackupCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete will delete the existing Backup with the provided ID. +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves the Backup with the provided ID. To extract the Backup +// object from the response, call the Extract method on the GetResult. +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToBackupListQuery() (string, error) +} + +type ListOpts struct { + // AllTenants will retrieve backups of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Name will filter by the specified backup name. + // This does not work in later microversions. + Name string `q:"name"` + + // Status will filter by the specified status. + // This does not work in later microversions. + Status string `q:"status"` + + // TenantID will filter by a specific tenant/project ID. + // Setting AllTenants is required to use this. + TenantID string `q:"project_id"` + + // VolumeID will filter by a specified volume ID. + // This does not work in later microversions. + VolumeID string `q:"volume_id"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// ToBackupListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToBackupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns Backups 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.ToBackupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return BackupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// ListDetailOptsBuilder allows extensions to add additional parameters to the ListDetail +// request. +type ListDetailOptsBuilder interface { + ToBackupListDetailQuery() (string, error) +} + +type ListDetailOpts struct { + // AllTenants will retrieve backups of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` + + // True to include `count` in the API response, supported from version 3.45 + WithCount bool `q:"with_count"` +} + +// ToBackupListDetailQuery formats a ListDetailOpts into a query string. +func (opts ListDetailOpts) ToBackupListDetailQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// ListDetail returns more detailed information about Backups optionally +// limited by the conditions provided in ListDetailOpts. +func ListDetail(client *gophercloud.ServiceClient, opts ListDetailOptsBuilder) pagination.Pager { + url := listDetailURL(client) + if opts != nil { + query, err := opts.ToBackupListDetailQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return BackupPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// UpdateOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateOptsBuilder interface { + ToBackupUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts contain options for updating an existing Backup. +type UpdateOpts struct { + // Name is the name of the backup. + Name *string `json:"name,omitempty"` + + // Description is the description of the backup. + Description *string `json:"description,omitempty"` + + // Metadata is metadata for the backup. + // Requires microversion 3.43 or later. + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ToBackupUpdateMap assembles a request body based on the contents of +// an UpdateOpts. +func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update will update the Backup with provided information. To extract +// the updated Backup from the response, call the Extract method on the +// UpdateResult. +// Requires microversion 3.9 or later. +func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToBackupUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RestoreOpts contains options for restoring a Backup. This object is passed to +// the backups.RestoreFromBackup function. +type RestoreOpts struct { + // VolumeID is the ID of the existing volume to restore the backup to. + VolumeID string `json:"volume_id,omitempty"` + + // Name is the name of the new volume to restore the backup to. + Name string `json:"name,omitempty"` +} + +// ToRestoreMap assembles a request body based on the contents of a +// RestoreOpts. +func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "restore") +} + +// RestoreFromBackup will restore a Backup to a volume based on the values in +// RestoreOpts. To extract the Restore object from the response, call the +// Extract method on the RestoreResult. +func RestoreFromBackup(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RestoreOpts) (r RestoreResult) { + b, err := opts.ToRestoreMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, restoreURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Export will export a Backup information. To extract the Backup export record +// object from the response, call the Extract method on the ExportResult. +func Export(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ExportResult) { + resp, err := client.Get(ctx, exportURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ImportOpts contains options for importing a Backup. This object is passed to +// the backups.ImportBackup function. +type ImportOpts BackupRecord + +// ToBackupImportMap assembles a request body based on the contents of a +// ImportOpts. +func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "backup-record") +} + +// Import will import a Backup data to a backup based on the values in +// ImportOpts. To extract the Backup object from the response, call the +// Extract method on the ImportResult. +func Import(ctx context.Context, client *gophercloud.ServiceClient, opts ImportOpts) (r ImportResult) { + b, err := opts.ToBackupImportMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, importURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ResetStatusOptsBuilder allows extensions to add additional parameters to the +// ResetStatus request. +type ResetStatusOptsBuilder interface { + ToBackupResetStatusMap() (map[string]interface{}, error) +} + +// ResetStatusOpts contains options for resetting a Backup status. +// For more information about these parameters, please, refer to the Block Storage API V2, +// Backup Actions, ResetStatus backup documentation. +type ResetStatusOpts struct { + // Status is a backup status to reset to. + Status string `json:"status"` +} + +// ToBackupResetStatusMap assembles a request body based on the contents of a +// ResetStatusOpts. +func (opts ResetStatusOpts) ToBackupResetStatusMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-reset_status") +} + +// ResetStatus will reset the existing backup status. ResetStatusResult contains only the error. +// To extract it, call the ExtractErr method on the ResetStatusResult. +func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToBackupResetStatusMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(ctx, resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ForceDelete will delete the existing backup in any state. ForceDeleteResult contains only the error. +// To extract it, call the ExtractErr method on the ForceDeleteResult. +func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + b := map[string]interface{}{ + "os-force_delete": struct{}{}, + } + resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/extensions/backups/results.go b/openstack/blockstorage/v2/backups/results.go similarity index 100% rename from openstack/blockstorage/extensions/backups/results.go rename to openstack/blockstorage/v2/backups/results.go diff --git a/openstack/blockstorage/extensions/backups/testing/fixtures_test.go b/openstack/blockstorage/v2/backups/testing/fixtures_test.go similarity index 99% rename from openstack/blockstorage/extensions/backups/testing/fixtures_test.go rename to openstack/blockstorage/v2/backups/testing/fixtures_test.go index 77cec05ed7..beee7c2678 100644 --- a/openstack/blockstorage/extensions/backups/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/backups/testing/fixtures_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/backups" th "github.com/gophercloud/gophercloud/v2/testhelper" fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/blockstorage/extensions/backups/testing/requests_test.go b/openstack/blockstorage/v2/backups/testing/requests_test.go similarity index 98% rename from openstack/blockstorage/extensions/backups/testing/requests_test.go rename to openstack/blockstorage/v2/backups/testing/requests_test.go index 667715c3cc..f34bb5f775 100644 --- a/openstack/blockstorage/extensions/backups/testing/requests_test.go +++ b/openstack/blockstorage/v2/backups/testing/requests_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/backups" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/backups" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/blockstorage/extensions/backups/urls.go b/openstack/blockstorage/v2/backups/urls.go similarity index 100% rename from openstack/blockstorage/extensions/backups/urls.go rename to openstack/blockstorage/v2/backups/urls.go diff --git a/openstack/blockstorage/extensions/limits/doc.go b/openstack/blockstorage/v2/limits/doc.go similarity index 100% rename from openstack/blockstorage/extensions/limits/doc.go rename to openstack/blockstorage/v2/limits/doc.go diff --git a/openstack/blockstorage/extensions/limits/requests.go b/openstack/blockstorage/v2/limits/requests.go similarity index 100% rename from openstack/blockstorage/extensions/limits/requests.go rename to openstack/blockstorage/v2/limits/requests.go diff --git a/openstack/blockstorage/extensions/limits/results.go b/openstack/blockstorage/v2/limits/results.go similarity index 100% rename from openstack/blockstorage/extensions/limits/results.go rename to openstack/blockstorage/v2/limits/results.go diff --git a/openstack/blockstorage/extensions/limits/testing/fixtures_test.go b/openstack/blockstorage/v2/limits/testing/fixtures_test.go similarity index 96% rename from openstack/blockstorage/extensions/limits/testing/fixtures_test.go rename to openstack/blockstorage/v2/limits/testing/fixtures_test.go index ddb88797a1..c3f8bf803d 100644 --- a/openstack/blockstorage/extensions/limits/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/limits/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/limits" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/limits" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -49,7 +49,7 @@ const GetOutput = ` ] } ], - "absolute": { + "absolute": { "maxTotalVolumes": 40, "maxTotalSnapshots": 40, "maxTotalVolumeGigabytes": 1000, diff --git a/openstack/blockstorage/extensions/limits/testing/requests_test.go b/openstack/blockstorage/v2/limits/testing/requests_test.go similarity index 83% rename from openstack/blockstorage/extensions/limits/testing/requests_test.go rename to openstack/blockstorage/v2/limits/testing/requests_test.go index a097c35bcf..a7b6c0a503 100644 --- a/openstack/blockstorage/extensions/limits/testing/requests_test.go +++ b/openstack/blockstorage/v2/limits/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/limits" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/limits" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/blockstorage/extensions/limits/urls.go b/openstack/blockstorage/v2/limits/urls.go similarity index 100% rename from openstack/blockstorage/extensions/limits/urls.go rename to openstack/blockstorage/v2/limits/urls.go diff --git a/openstack/blockstorage/extensions/quotasets/doc.go b/openstack/blockstorage/v2/quotasets/doc.go similarity index 100% rename from openstack/blockstorage/extensions/quotasets/doc.go rename to openstack/blockstorage/v2/quotasets/doc.go diff --git a/openstack/blockstorage/extensions/quotasets/requests.go b/openstack/blockstorage/v2/quotasets/requests.go similarity index 100% rename from openstack/blockstorage/extensions/quotasets/requests.go rename to openstack/blockstorage/v2/quotasets/requests.go diff --git a/openstack/blockstorage/extensions/quotasets/results.go b/openstack/blockstorage/v2/quotasets/results.go similarity index 100% rename from openstack/blockstorage/extensions/quotasets/results.go rename to openstack/blockstorage/v2/quotasets/results.go diff --git a/openstack/blockstorage/extensions/quotasets/testing/doc.go b/openstack/blockstorage/v2/quotasets/testing/doc.go similarity index 100% rename from openstack/blockstorage/extensions/quotasets/testing/doc.go rename to openstack/blockstorage/v2/quotasets/testing/doc.go diff --git a/openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go b/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go similarity index 98% rename from openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go rename to openstack/blockstorage/v2/quotasets/testing/fixtures_test.go index 51acf12d0e..9be8ffe90c 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/quotasets" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go b/openstack/blockstorage/v2/quotasets/testing/requests_test.go similarity index 96% rename from openstack/blockstorage/extensions/quotasets/testing/requests_test.go rename to openstack/blockstorage/v2/quotasets/testing/requests_test.go index a6a6f40ce1..3a678b5833 100644 --- a/openstack/blockstorage/extensions/quotasets/testing/requests_test.go +++ b/openstack/blockstorage/v2/quotasets/testing/requests_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/quotasets" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/blockstorage/extensions/quotasets/urls.go b/openstack/blockstorage/v2/quotasets/urls.go similarity index 100% rename from openstack/blockstorage/extensions/quotasets/urls.go rename to openstack/blockstorage/v2/quotasets/urls.go diff --git a/openstack/blockstorage/extensions/schedulerstats/doc.go b/openstack/blockstorage/v2/schedulerstats/doc.go similarity index 100% rename from openstack/blockstorage/extensions/schedulerstats/doc.go rename to openstack/blockstorage/v2/schedulerstats/doc.go diff --git a/openstack/blockstorage/extensions/schedulerstats/requests.go b/openstack/blockstorage/v2/schedulerstats/requests.go similarity index 100% rename from openstack/blockstorage/extensions/schedulerstats/requests.go rename to openstack/blockstorage/v2/schedulerstats/requests.go diff --git a/openstack/blockstorage/extensions/schedulerstats/results.go b/openstack/blockstorage/v2/schedulerstats/results.go similarity index 100% rename from openstack/blockstorage/extensions/schedulerstats/results.go rename to openstack/blockstorage/v2/schedulerstats/results.go diff --git a/openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go similarity index 97% rename from openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go rename to openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go index 27c7d30a4f..24c6f24f86 100644 --- a/openstack/blockstorage/extensions/schedulerstats/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerstats" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/schedulerstats" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go b/openstack/blockstorage/v2/schedulerstats/testing/requests_test.go similarity index 91% rename from openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go rename to openstack/blockstorage/v2/schedulerstats/testing/requests_test.go index 2895a46b89..709ddeac58 100644 --- a/openstack/blockstorage/extensions/schedulerstats/testing/requests_test.go +++ b/openstack/blockstorage/v2/schedulerstats/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerstats" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/schedulerstats" "github.com/gophercloud/gophercloud/v2/pagination" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/blockstorage/extensions/schedulerstats/urls.go b/openstack/blockstorage/v2/schedulerstats/urls.go similarity index 100% rename from openstack/blockstorage/extensions/schedulerstats/urls.go rename to openstack/blockstorage/v2/schedulerstats/urls.go diff --git a/openstack/blockstorage/extensions/services/doc.go b/openstack/blockstorage/v2/services/doc.go similarity index 100% rename from openstack/blockstorage/extensions/services/doc.go rename to openstack/blockstorage/v2/services/doc.go diff --git a/openstack/blockstorage/extensions/services/requests.go b/openstack/blockstorage/v2/services/requests.go similarity index 100% rename from openstack/blockstorage/extensions/services/requests.go rename to openstack/blockstorage/v2/services/requests.go diff --git a/openstack/blockstorage/extensions/services/results.go b/openstack/blockstorage/v2/services/results.go similarity index 100% rename from openstack/blockstorage/extensions/services/results.go rename to openstack/blockstorage/v2/services/results.go diff --git a/openstack/blockstorage/extensions/services/testing/fixtures_test.go b/openstack/blockstorage/v2/services/testing/fixtures_test.go similarity index 96% rename from openstack/blockstorage/extensions/services/testing/fixtures_test.go rename to openstack/blockstorage/v2/services/testing/fixtures_test.go index 94bd3da2b7..a396abc39e 100644 --- a/openstack/blockstorage/extensions/services/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/services/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/services" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/services" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/blockstorage/extensions/services/testing/requests_test.go b/openstack/blockstorage/v2/services/testing/requests_test.go similarity index 92% rename from openstack/blockstorage/extensions/services/testing/requests_test.go rename to openstack/blockstorage/v2/services/testing/requests_test.go index 407aa0723b..8ce1266d7e 100644 --- a/openstack/blockstorage/extensions/services/testing/requests_test.go +++ b/openstack/blockstorage/v2/services/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/services" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/services" "github.com/gophercloud/gophercloud/v2/pagination" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/blockstorage/extensions/services/urls.go b/openstack/blockstorage/v2/services/urls.go similarity index 100% rename from openstack/blockstorage/extensions/services/urls.go rename to openstack/blockstorage/v2/services/urls.go diff --git a/openstack/blockstorage/v3/availabilityzones/doc.go b/openstack/blockstorage/v3/availabilityzones/doc.go new file mode 100644 index 0000000000..eb3903ad13 --- /dev/null +++ b/openstack/blockstorage/v3/availabilityzones/doc.go @@ -0,0 +1,21 @@ +/* +Package availabilityzones provides the ability to get lists of +available volume availability zones. + +Example of Get Availability Zone Information + + allPages, err := availabilityzones.List(volumeClient).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + availabilityZoneInfo, err := availabilityzones.ExtractAvailabilityZones(allPages) + if err != nil { + panic(err) + } + + for _, zoneInfo := range availabilityZoneInfo { + fmt.Printf("%+v\n", zoneInfo) + } +*/ +package availabilityzones diff --git a/openstack/blockstorage/v3/availabilityzones/requests.go b/openstack/blockstorage/v3/availabilityzones/requests.go new file mode 100644 index 0000000000..15f9c228b2 --- /dev/null +++ b/openstack/blockstorage/v3/availabilityzones/requests.go @@ -0,0 +1,13 @@ +package availabilityzones + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// List will return the existing availability zones. +func List(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { + return AvailabilityZonePage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/blockstorage/v3/availabilityzones/results.go b/openstack/blockstorage/v3/availabilityzones/results.go new file mode 100644 index 0000000000..1e80451a36 --- /dev/null +++ b/openstack/blockstorage/v3/availabilityzones/results.go @@ -0,0 +1,33 @@ +package availabilityzones + +import ( + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ZoneState represents the current state of the availability zone. +type ZoneState struct { + // Returns true if the availability zone is available + Available bool `json:"available"` +} + +// AvailabilityZone contains all the information associated with an OpenStack +// AvailabilityZone. +type AvailabilityZone struct { + // The availability zone name + ZoneName string `json:"zoneName"` + ZoneState ZoneState `json:"zoneState"` +} + +type AvailabilityZonePage struct { + pagination.SinglePageBase +} + +// ExtractAvailabilityZones returns a slice of AvailabilityZones contained in a +// single page of results. +func ExtractAvailabilityZones(r pagination.Page) ([]AvailabilityZone, error) { + var s struct { + AvailabilityZoneInfo []AvailabilityZone `json:"availabilityZoneInfo"` + } + err := (r.(AvailabilityZonePage)).ExtractInto(&s) + return s.AvailabilityZoneInfo, err +} diff --git a/openstack/blockstorage/v3/availabilityzones/testing/doc.go b/openstack/blockstorage/v3/availabilityzones/testing/doc.go new file mode 100644 index 0000000000..a4408d7a0d --- /dev/null +++ b/openstack/blockstorage/v3/availabilityzones/testing/doc.go @@ -0,0 +1,2 @@ +// availabilityzones unittests +package testing diff --git a/openstack/blockstorage/v3/availabilityzones/testing/fixtures_test.go b/openstack/blockstorage/v3/availabilityzones/testing/fixtures_test.go new file mode 100644 index 0000000000..98f93db1d3 --- /dev/null +++ b/openstack/blockstorage/v3/availabilityzones/testing/fixtures_test.go @@ -0,0 +1,52 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/availabilityzones" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const GetOutput = ` +{ + "availabilityZoneInfo": [ + { + "zoneName": "internal", + "zoneState": { + "available": true + } + }, + { + "zoneName": "nova", + "zoneState": { + "available": true + } + } + ] +}` + +var AZResult = []az.AvailabilityZone{ + { + ZoneName: "internal", + ZoneState: az.ZoneState{Available: true}, + }, + { + ZoneName: "nova", + ZoneState: az.ZoneState{Available: true}, + }, +} + +// HandleGetSuccessfully configures the test server to respond to a Get request +// for availability zone information. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-availability-zone", 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, GetOutput) + }) +} diff --git a/openstack/blockstorage/v3/availabilityzones/testing/requests_test.go b/openstack/blockstorage/v3/availabilityzones/testing/requests_test.go new file mode 100644 index 0000000000..3d1ed1bd0f --- /dev/null +++ b/openstack/blockstorage/v3/availabilityzones/testing/requests_test.go @@ -0,0 +1,26 @@ +package testing + +import ( + "context" + "testing" + + az "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/availabilityzones" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +// Verifies that availability zones can be listed correctly +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + HandleGetSuccessfully(t) + + allPages, err := az.List(client.ServiceClient()).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + actual, err := az.ExtractAvailabilityZones(allPages) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, AZResult, actual) +} diff --git a/openstack/blockstorage/v3/availabilityzones/urls.go b/openstack/blockstorage/v3/availabilityzones/urls.go new file mode 100644 index 0000000000..f78b7c8f97 --- /dev/null +++ b/openstack/blockstorage/v3/availabilityzones/urls.go @@ -0,0 +1,7 @@ +package availabilityzones + +import "github.com/gophercloud/gophercloud/v2" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-availability-zone") +} diff --git a/openstack/blockstorage/v3/backups/doc.go b/openstack/blockstorage/v3/backups/doc.go new file mode 100644 index 0000000000..fdfd945fa0 --- /dev/null +++ b/openstack/blockstorage/v3/backups/doc.go @@ -0,0 +1,124 @@ +/* +Package backups provides information and interaction with backups in the +OpenStack Block Storage service. A backup is a point in time copy of the +data contained in an external storage volume, and can be controlled +programmatically. + +Example to List Backups + + listOpts := backups.ListOpts{ + VolumeID: "uuid", + } + + allPages, err := backups.List(client, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allBackups, err := backups.ExtractBackups(allPages) + if err != nil { + panic(err) + } + + for _, backup := range allBackups { + fmt.Println(backup) + } + +Example to Create a Backup + + createOpts := backups.CreateOpts{ + VolumeID: "uuid", + Name: "my-backup", + } + + backup, err := backups.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) + +Example to Update a Backup + + updateOpts := backups.UpdateOpts{ + Name: "new-name", + } + + backup, err := backups.Update(context.TODO(), client, "uuid", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) + +Example to Restore a Backup to a Volume + + options := backups.RestoreOpts{ + VolumeID: "1234", + Name: "vol-001", + } + + restore, err := backups.RestoreFromBackup(context.TODO(), client, "uuid", options).Extract() + if err != nil { + panic(err) + } + + fmt.Println(restore) + +Example to Delete a Backup + + err := backups.Delete(context.TODO(), client, "uuid").ExtractErr() + if err != nil { + panic(err) + } + +Example to Export a Backup + + export, err := backups.Export(context.TODO(), client, "uuid").Extract() + if err != nil { + panic(err) + } + + fmt.Println(export) + +Example to Import a Backup + + status := "available" + availabilityZone := "region1b" + host := "cinder-backup-host1" + serviceMetadata := "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" + size := 1 + objectCount := 2 + container := "my-test-backup" + service := "cinder.backup.drivers.swift.SwiftBackupDriver" + backupURL, _ := json.Marshal(backups.ImportBackup{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Status: &status, + AvailabilityZone: &availabilityZone, + VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", + UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), + Host: &host, + UserID: "93514e04-a026-4f60-8176-395c859501dd", + ServiceMetadata: &serviceMetadata, + Size: &size, + ObjectCount: &objectCount, + Container: &container, + Service: &service, + CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", + }) + + options := backups.ImportOpts{ + BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", + BackupURL: backupURL, + } + + backup, err := backups.Import(context.TODO(), client, options).Extract() + if err != nil { + panic(err) + } + + fmt.Println(backup) +*/ +package backups diff --git a/openstack/blockstorage/extensions/backups/requests.go b/openstack/blockstorage/v3/backups/requests.go similarity index 100% rename from openstack/blockstorage/extensions/backups/requests.go rename to openstack/blockstorage/v3/backups/requests.go diff --git a/openstack/blockstorage/v3/backups/results.go b/openstack/blockstorage/v3/backups/results.go new file mode 100644 index 0000000000..79c4901c09 --- /dev/null +++ b/openstack/blockstorage/v3/backups/results.go @@ -0,0 +1,351 @@ +package backups + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// Backup contains all the information associated with a Cinder Backup. +type Backup struct { + // ID is the Unique identifier of the backup. + ID string `json:"id"` + + // CreatedAt is the date the backup was created. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the date the backup was updated. + UpdatedAt time.Time `json:"-"` + + // Name is the display name of the backup. + Name string `json:"name"` + + // Description is the description of the backup. + Description string `json:"description"` + + // VolumeID is the ID of the Volume from which this backup was created. + VolumeID string `json:"volume_id"` + + // SnapshotID is the ID of the snapshot from which this backup was created. + SnapshotID string `json:"snapshot_id"` + + // Status is the status of the backup. + Status string `json:"status"` + + // Size is the size of the backup, in GB. + Size int `json:"size"` + + // Object Count is the number of objects in the backup. + ObjectCount int `json:"object_count"` + + // Container is the container where the backup is stored. + Container string `json:"container"` + + // HasDependentBackups is whether there are other backups + // depending on this backup. + HasDependentBackups bool `json:"has_dependent_backups"` + + // FailReason has the reason for the backup failure. + FailReason string `json:"fail_reason"` + + // IsIncremental is whether this is an incremental backup. + IsIncremental bool `json:"is_incremental"` + + // DataTimestamp is the time when the data on the volume was first saved. + DataTimestamp time.Time `json:"-"` + + // ProjectID is the ID of the project that owns the backup. This is + // an admin-only field. + ProjectID string `json:"os-backup-project-attr:project_id"` + + // Metadata is metadata about the backup. + // This requires microversion 3.43 or later. + Metadata *map[string]string `json:"metadata"` + + // AvailabilityZone is the Availability Zone of the backup. + // This requires microversion 3.51 or later. + AvailabilityZone *string `json:"availability_zone"` +} + +// 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 +} + +// BackupPage is a pagination.Pager that is returned from a call to the List function. +type BackupPage struct { + pagination.LinkedPageBase +} + +// UnmarshalJSON converts our JSON API response into our backup struct +func (r *Backup) UnmarshalJSON(b []byte) error { + type tmp Backup + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + DataTimestamp gophercloud.JSONRFC3339MilliNoZ `json:"data_timestamp"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Backup(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DataTimestamp = time.Time(s.DataTimestamp) + + return err +} + +// 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 +} + +func (page BackupPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"backups_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractBackups extracts and returns Backups. It is used while iterating over a backups.List call. +func ExtractBackups(r pagination.Page) ([]Backup, error) { + var s []Backup + err := ExtractBackupsInto(r, &s) + return s, err +} + +// UpdateResult contains the response body and error from an Update request. +type UpdateResult struct { + commonResult +} + +type commonResult struct { + gophercloud.Result +} + +// Extract will get the Backup object out of the commonResult object. +func (r commonResult) Extract() (*Backup, error) { + var s Backup + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup") +} + +func ExtractBackupsInto(r pagination.Page, v interface{}) error { + return r.(BackupPage).Result.ExtractIntoSlicePtr(v, "backups") +} + +// RestoreResult contains the response body and error from a restore request. +type RestoreResult struct { + commonResult +} + +// Restore contains all the information associated with a Cinder Backup restore +// response. +type Restore struct { + // BackupID is the Unique identifier of the backup. + BackupID string `json:"backup_id"` + + // VolumeID is the Unique identifier of the volume. + VolumeID string `json:"volume_id"` + + // Name is the name of the volume, where the backup was restored to. + VolumeName string `json:"volume_name"` +} + +// Extract will get the Backup restore object out of the RestoreResult object. +func (r RestoreResult) Extract() (*Restore, error) { + var s Restore + err := r.ExtractInto(&s) + return &s, err +} + +func (r RestoreResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "restore") +} + +// ExportResult contains the response body and error from an export request. +type ExportResult struct { + commonResult +} + +// BackupRecord contains an information about a backup backend storage. +type BackupRecord struct { + // The service used to perform the backup. + BackupService string `json:"backup_service"` + + // An identifier string to locate the backup. + BackupURL []byte `json:"backup_url"` +} + +// Extract will get the Backup record object out of the ExportResult object. +func (r ExportResult) Extract() (*BackupRecord, error) { + var s BackupRecord + err := r.ExtractInto(&s) + return &s, err +} + +func (r ExportResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup-record") +} + +// ImportResponse struct contains the response of the Backup Import action. +type ImportResponse struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// ImportResult contains the response body and error from an import request. +type ImportResult struct { + gophercloud.Result +} + +// Extract will get the Backup object out of the commonResult object. +func (r ImportResult) Extract() (*ImportResponse, error) { + var s ImportResponse + err := r.ExtractInto(&s) + return &s, err +} + +func (r ImportResult) ExtractInto(v interface{}) error { + return r.Result.ExtractIntoStructPtr(v, "backup") +} + +// ImportBackup contains all the information to import a Cinder Backup. +type ImportBackup struct { + ID string `json:"id"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + VolumeID string `json:"volume_id"` + SnapshotID *string `json:"snapshot_id"` + Status *string `json:"status"` + Size *int `json:"size"` + ObjectCount *int `json:"object_count"` + Container *string `json:"container"` + ServiceMetadata *string `json:"service_metadata"` + Service *string `json:"service"` + Host *string `json:"host"` + UserID string `json:"user_id"` + DeletedAt time.Time `json:"-"` + DataTimestamp time.Time `json:"-"` + TempSnapshotID *string `json:"temp_snapshot_id"` + TempVolumeID *string `json:"temp_volume_id"` + RestoreVolumeID *string `json:"restore_volume_id"` + NumDependentBackups *int `json:"num_dependent_backups"` + EncryptionKeyID *string `json:"encryption_key_id"` + ParentID *string `json:"parent_id"` + Deleted bool `json:"deleted"` + DisplayName *string `json:"display_name"` + DisplayDescription *string `json:"display_description"` + DriverInfo interface{} `json:"driver_info"` + FailReason *string `json:"fail_reason"` + ProjectID string `json:"project_id"` + Metadata map[string]string `json:"metadata"` + AvailabilityZone *string `json:"availability_zone"` +} + +// UnmarshalJSON converts our JSON API response into our backup struct +func (r *ImportBackup) UnmarshalJSON(b []byte) error { + type tmp ImportBackup + var s struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt time.Time `json:"deleted_at"` + DataTimestamp time.Time `json:"data_timestamp"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ImportBackup(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) + r.DataTimestamp = time.Time(s.DataTimestamp) + + return err +} + +// MarshalJSON converts our struct request into JSON backup import request +func (r ImportBackup) MarshalJSON() ([]byte, error) { + type b ImportBackup + type ext struct { + CreatedAt *string `json:"created_at"` + UpdatedAt *string `json:"updated_at"` + DeletedAt *string `json:"deleted_at"` + DataTimestamp *string `json:"data_timestamp"` + } + type tmp struct { + b + ext + } + + var t ext + if r.CreatedAt != (time.Time{}) { + v := r.CreatedAt.Format(time.RFC3339) + t.CreatedAt = &v + } + if r.UpdatedAt != (time.Time{}) { + v := r.UpdatedAt.Format(time.RFC3339) + t.UpdatedAt = &v + } + if r.DeletedAt != (time.Time{}) { + v := r.DeletedAt.Format(time.RFC3339) + t.DeletedAt = &v + } + if r.DataTimestamp != (time.Time{}) { + v := r.DataTimestamp.Format(time.RFC3339) + t.DataTimestamp = &v + } + + if r.Metadata == nil { + r.Metadata = make(map[string]string) + } + + s := tmp{ + b(r), + t, + } + + return json.Marshal(s) +} + +// ResetStatusResult contains the response error from a ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} + +// ForceDeleteResult contains the response error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/backups/testing/fixtures_test.go b/openstack/blockstorage/v3/backups/testing/fixtures_test.go new file mode 100644 index 0000000000..1f17f717d3 --- /dev/null +++ b/openstack/blockstorage/v3/backups/testing/fixtures_test.go @@ -0,0 +1,338 @@ +package testing + +import ( + "encoding/json" + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/backups" + th "github.com/gophercloud/gophercloud/v2/testhelper" + fake "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const ListResponse = ` +{ + "backups": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "backup-001" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "backup-002" + } + ], + "backups_links": [ + { + "href": "%s/backups?marker=1", + "rel": "next" + } + ] +} +` + +const ListDetailResponse = ` +{ + "backups": [ + { + "id": "289da7f8-6440-407c-9fb4-7db01ec49164", + "name": "backup-001", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "description": "Daily Backup", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + }, + { + "id": "96c3bda7-c82a-4f50-be73-ca7621794835", + "name": "backup-002", + "volume_id": "76b8950a-8594-4e5b-8dce-0dfa9c696358", + "description": "Weekly Backup", + "status": "available", + "size": 25, + "created_at": "2017-05-30T03:35:03.000000" + } + ], + "backups_links": [ + { + "href": "%s/backups/detail?marker=1", + "rel": "next" + } + ] +} +` + +const GetResponse = ` +{ + "backup": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "name": "backup-001", + "description": "Daily backup", + "volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} +` +const CreateRequest = ` +{ + "backup": { + "volume_id": "1234", + "name": "backup-001" + } +} +` + +const CreateResponse = ` +{ + "backup": { + "volume_id": "1234", + "name": "backup-001", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "description": "Daily backup", + "volume_id": "1234", + "status": "available", + "size": 30, + "created_at": "2017-05-30T03:35:03.000000" + } +} +` + +const RestoreRequest = ` +{ + "restore": { + "name": "vol-001", + "volume_id": "1234" + } +} +` + +const RestoreResponse = ` +{ + "restore": { + "backup_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "volume_id": "1234", + "volume_name": "vol-001" + } +} +` + +const ExportResponse = ` +{ + "backup-record": { + "backup_service": "cinder.backup.drivers.swift.SwiftBackupDriver", + "backup_url": "eyJpZCI6ImQzMjAxOWQzLWJjNmUtNDMxOS05YzFkLTY3MjJmYzEzNmEyMiIsInZvbHVtZV9pZCI6ImNmOWJjNmZhLWM1YmMtNDFmNi1iYzRlLTZlNzZjMGJlYTk1OSIsInNuYXBzaG90X2lkIjpudWxsLCJzdGF0dXMiOiJhdmFpbGFibGUiLCJzaXplIjoxLCJvYmplY3RfY291bnQiOjIsImNvbnRhaW5lciI6Im15LXRlc3QtYmFja3VwIiwic2VydmljZV9tZXRhZGF0YSI6InZvbHVtZV9jZjliYzZmYS1jNWJjLTQxZjYtYmM0ZS02ZTc2YzBiZWE5NTkvMjAyMDAzMTExOTI4NTUvYXpfcmVnaW9uYl9iYWNrdXBfYjg3YmIxZTUtMGQ0ZS00NDVlLWE1NDgtNWFlNzQyNTYyYmFjIiwic2VydmljZSI6ImNpbmRlci5iYWNrdXAuZHJpdmVycy5zd2lmdC5Td2lmdEJhY2t1cERyaXZlciIsImhvc3QiOiJjaW5kZXItYmFja3VwLWhvc3QxIiwidXNlcl9pZCI6IjkzNTE0ZTA0LWEwMjYtNGY2MC04MTc2LTM5NWM4NTk1MDFkZCIsInRlbXBfc25hcHNob3RfaWQiOm51bGwsInRlbXBfdm9sdW1lX2lkIjpudWxsLCJyZXN0b3JlX3ZvbHVtZV9pZCI6bnVsbCwibnVtX2RlcGVuZGVudF9iYWNrdXBzIjpudWxsLCJlbmNyeXB0aW9uX2tleV9pZCI6bnVsbCwicGFyZW50X2lkIjpudWxsLCJkZWxldGVkIjpmYWxzZSwiZGlzcGxheV9uYW1lIjpudWxsLCJkaXNwbGF5X2Rlc2NyaXB0aW9uIjpudWxsLCJkcml2ZXJfaW5mbyI6bnVsbCwiZmFpbF9yZWFzb24iOm51bGwsInByb2plY3RfaWQiOiIxNGYxYzFmNWQxMmI0NzU1Yjk0ZWRlZjc4ZmY4YjMyNSIsIm1ldGFkYXRhIjp7fSwiYXZhaWxhYmlsaXR5X3pvbmUiOiJyZWdpb24xYiIsImNyZWF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI1OjI0WiIsInVwZGF0ZWRfYXQiOiIyMDIwLTAzLTExVDE5OjI5OjA4WiIsImRlbGV0ZWRfYXQiOm51bGwsImRhdGFfdGltZXN0YW1wIjoiMjAyMC0wMy0xMVQxOToyNToyNFoifQ==" + } +} +` + +const ImportRequest = ExportResponse + +const ImportResponse = ` +{ + "backup": { + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "links": [ + { + "href": "https://volume/v2/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", + "rel": "self" + }, + { + "href": "https://volume/14f1c1f5d12b4755b94edef78ff8b325/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", + "rel": "bookmark" + } + ], + "name": null + } +} +` + +const ResetRequest = ` +{ + "os-reset_status": { + "status": "error" + } +} +` + +const ForceDeleteRequest = ` +{ + "os-force_delete": {} +} +` + +var ( + status = "available" + availabilityZone = "region1b" + host = "cinder-backup-host1" + serviceMetadata = "volume_cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959/20200311192855/az_regionb_backup_b87bb1e5-0d4e-445e-a548-5ae742562bac" + size = 1 + objectCount = 2 + container = "my-test-backup" + service = "cinder.backup.drivers.swift.SwiftBackupDriver" + backupImport = backups.ImportBackup{ + ID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + Status: &status, + AvailabilityZone: &availabilityZone, + VolumeID: "cf9bc6fa-c5bc-41f6-bc4e-6e76c0bea959", + UpdatedAt: time.Date(2020, 3, 11, 19, 29, 8, 0, time.UTC), + Host: &host, + UserID: "93514e04-a026-4f60-8176-395c859501dd", + ServiceMetadata: &serviceMetadata, + Size: &size, + ObjectCount: &objectCount, + Container: &container, + Service: &service, + CreatedAt: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + DataTimestamp: time.Date(2020, 3, 11, 19, 25, 24, 0, time.UTC), + ProjectID: "14f1c1f5d12b4755b94edef78ff8b325", + Metadata: make(map[string]string), + } + backupURL, _ = json.Marshal(backupImport) +) + +func MockListResponse(t *testing.T) { + th.Mux.HandleFunc("/backups", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ListResponse, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"backups": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockListDetailResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/detail", 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) + + r.ParseForm() + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprintf(w, ListDetailResponse, th.Server.URL) + case "1": + fmt.Fprintf(w, `{"backups": []}`) + default: + t.Fatalf("Unexpected marker: [%s]", marker) + } + }) +} + +func MockGetResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/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, GetResponse) + }) +} + +func MockCreateResponse(t *testing.T) { + th.Mux.HandleFunc("/backups", 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.StatusAccepted) + + fmt.Fprintf(w, CreateResponse) + }) +} + +func MockRestoreResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/restore", 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, RestoreRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, RestoreResponse) + }) +} + +func MockDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22", 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 MockExportResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/d32019d3-bc6e-4319-9c1d-6722fc136a22/export_record", 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, ExportResponse) + }) +} + +func MockImportResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/import_record", 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, ImportRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ImportResponse) + }) +} + +// MockResetStatusResponse provides mock response for reset backup status API call +func MockResetStatusResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/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, ResetRequest) + + w.WriteHeader(http.StatusAccepted) + }) +} + +// MockForceDeleteResponse provides mock response for force delete backup API call +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/backups/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, ForceDeleteRequest) + + w.WriteHeader(http.StatusAccepted) + }) +} diff --git a/openstack/blockstorage/v3/backups/testing/requests_test.go b/openstack/blockstorage/v3/backups/testing/requests_test.go new file mode 100644 index 0000000000..b9c302896d --- /dev/null +++ b/openstack/blockstorage/v3/backups/testing/requests_test.go @@ -0,0 +1,213 @@ +package testing + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/backups" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListResponse(t) + + count := 0 + + err := backups.List(client.ServiceClient(), &backups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + count++ + actual, err := backups.ExtractBackups(page) + if err != nil { + t.Errorf("Failed to extract backups: %v", err) + return false, err + } + + expected := []backups.Backup{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "backup-001", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "backup-002", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + if err != nil { + t.Errorf("EachPage returned error: %s", err) + } + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestListDetail(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockListDetailResponse(t) + + count := 0 + + err := backups.ListDetail(client.ServiceClient(), &backups.ListDetailOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + count++ + actual, err := backups.ExtractBackups(page) + if err != nil { + t.Errorf("Failed to extract backups: %v", err) + return false, err + } + + expected := []backups.Backup{ + { + ID: "289da7f8-6440-407c-9fb4-7db01ec49164", + Name: "backup-001", + VolumeID: "521752a6-acf6-4b2d-bc7a-119f9148cd8c", + Status: "available", + Size: 30, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Daily Backup", + }, + { + ID: "96c3bda7-c82a-4f50-be73-ca7621794835", + Name: "backup-002", + VolumeID: "76b8950a-8594-4e5b-8dce-0dfa9c696358", + Status: "available", + Size: 25, + CreatedAt: time.Date(2017, 5, 30, 3, 35, 3, 0, time.UTC), + Description: "Weekly Backup", + }, + } + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + if err != nil { + t.Errorf("EachPage returned error: %s", err) + } + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockGetResponse(t) + + v, err := backups.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, v.Name, "backup-001") + th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockCreateResponse(t) + + options := backups.CreateOpts{VolumeID: "1234", Name: "backup-001"} + n, err := backups.Create(context.TODO(), client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.Name, "backup-001") + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestRestore(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockRestoreResponse(t) + + options := backups.RestoreOpts{VolumeID: "1234", Name: "vol-001"} + n, err := backups.RestoreFromBackup(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.VolumeID, "1234") + th.AssertEquals(t, n.VolumeName, "vol-001") + th.AssertEquals(t, n.BackupID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDeleteResponse(t) + + res := backups.Delete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} + +func TestExport(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockExportResponse(t) + + n, err := backups.Export(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.BackupService, "cinder.backup.drivers.swift.SwiftBackupDriver") + th.AssertDeepEquals(t, n.BackupURL, backupURL) + + tmp := backups.ImportBackup{} + err = json.Unmarshal(backupURL, &tmp) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, tmp, backupImport) +} + +func TestImport(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockImportResponse(t) + + options := backups.ImportOpts{ + BackupService: "cinder.backup.drivers.swift.SwiftBackupDriver", + BackupURL: backupURL, + } + n, err := backups.Import(context.TODO(), client.ServiceClient(), options).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") +} + +func TestResetStatus(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockResetStatusResponse(t) + + opts := &backups.ResetStatusOpts{ + Status: "error", + } + res := backups.ResetStatus(context.TODO(), 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 := backups.ForceDelete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/blockstorage/v3/backups/urls.go b/openstack/blockstorage/v3/backups/urls.go new file mode 100644 index 0000000000..9a96bb56bb --- /dev/null +++ b/openstack/blockstorage/v3/backups/urls.go @@ -0,0 +1,47 @@ +package backups + +import "github.com/gophercloud/gophercloud/v2" + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups") +} + +func listDetailURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups", "detail") +} + +func updateURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id) +} + +func restoreURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "restore") +} + +func exportURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "export_record") +} + +func importURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("backups", "import_record") +} + +func resetStatusURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "action") +} + +func forceDeleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("backups", id, "action") +} diff --git a/openstack/blockstorage/v3/limits/doc.go b/openstack/blockstorage/v3/limits/doc.go new file mode 100644 index 0000000000..2183bd80a6 --- /dev/null +++ b/openstack/blockstorage/v3/limits/doc.go @@ -0,0 +1,13 @@ +/* +Package limits shows rate and limit information for a project you authorized for. + +Example to Retrieve Limits + + limits, err := limits.Get(context.TODO(), blockStorageClient).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", limits) +*/ +package limits diff --git a/openstack/blockstorage/v3/limits/requests.go b/openstack/blockstorage/v3/limits/requests.go new file mode 100644 index 0000000000..8ae37cdd99 --- /dev/null +++ b/openstack/blockstorage/v3/limits/requests.go @@ -0,0 +1,15 @@ +package limits + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) + +// Get returns the limits about the currently scoped tenant. +func Get(ctx context.Context, client *gophercloud.ServiceClient) (r GetResult) { + url := getURL(client) + resp, err := client.Get(ctx, url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/limits/results.go b/openstack/blockstorage/v3/limits/results.go new file mode 100644 index 0000000000..961f0ea696 --- /dev/null +++ b/openstack/blockstorage/v3/limits/results.go @@ -0,0 +1,80 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud/v2" +) + +// Limits is a struct that contains the response of a limit query. +type Limits struct { + // Absolute contains the limits and usage information. + // An absolute limit value of -1 indicates that the absolute limit for the item is infinite. + Absolute Absolute `json:"absolute"` + // Rate contains rate-limit volume copy bandwidth, used to mitigate slow down of data access from the instances. + Rate []Rate `json:"rate"` +} + +// Absolute is a struct that contains the current resource usage and limits +// of a project. +type Absolute struct { + // MaxTotalVolumes is the maximum number of volumes. + MaxTotalVolumes int `json:"maxTotalVolumes"` + + // MaxTotalSnapshots is the maximum number of snapshots. + MaxTotalSnapshots int `json:"maxTotalSnapshots"` + + // MaxTotalVolumeGigabytes is the maximum total amount of volumes, in gibibytes (GiB). + MaxTotalVolumeGigabytes int `json:"maxTotalVolumeGigabytes"` + + // MaxTotalBackups is the maximum number of backups. + MaxTotalBackups int `json:"maxTotalBackups"` + + // MaxTotalBackupGigabytes is the maximum total amount of backups, in gibibytes (GiB). + MaxTotalBackupGigabytes int `json:"maxTotalBackupGigabytes"` + + // TotalVolumesUsed is the total number of volumes used. + TotalVolumesUsed int `json:"totalVolumesUsed"` + + // TotalGigabytesUsed is the total number of gibibytes (GiB) used. + TotalGigabytesUsed int `json:"totalGigabytesUsed"` + + // TotalSnapshotsUsed the total number of snapshots used. + TotalSnapshotsUsed int `json:"totalSnapshotsUsed"` + + // TotalBackupsUsed is the total number of backups used. + TotalBackupsUsed int `json:"totalBackupsUsed"` + + // TotalBackupGigabytesUsed is the total number of backups gibibytes (GiB) used. + TotalBackupGigabytesUsed int `json:"totalBackupGigabytesUsed"` +} + +// Rate is a struct that contains the +// rate-limit volume copy bandwidth, used to mitigate slow down of data access from the instances. +type Rate struct { + Regex string `json:"regex"` + URI string `json:"uri"` + Limit []Limit `json:"limit"` +} + +// Limit struct contains Limit values for the Rate struct +type Limit struct { + Verb string `json:"verb"` + NextAvailable string `json:"next-available"` + Unit string `json:"unit"` + Value int `json:"value"` + Remaining int `json:"remaining"` +} + +// Extract interprets a limits result as a Limits. +func (r GetResult) Extract() (*Limits, error) { + var s struct { + Limits *Limits `json:"limits"` + } + err := r.ExtractInto(&s) + return s.Limits, err +} + +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as an Absolute. +type GetResult struct { + gophercloud.Result +} diff --git a/openstack/blockstorage/v3/limits/testing/fixtures_test.go b/openstack/blockstorage/v3/limits/testing/fixtures_test.go new file mode 100644 index 0000000000..5d3b647427 --- /dev/null +++ b/openstack/blockstorage/v3/limits/testing/fixtures_test.go @@ -0,0 +1,129 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +// GetOutput is a sample response to a Get call. +const GetOutput = ` +{ + "limits": { + "rate": [ + { + "regex": ".*", + "uri": "*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00", + "unit": "MINUTE", + "value": 10, + "remaining": 10 + }, + { + "verb": "POST", + "next-available": "1970-01-01T00:00:00", + "unit": "HOUR", + "value": 5, + "remaining": 5 + } + ] + }, + { + "regex": "changes-since", + "uri": "changes-since*", + "limit": [ + { + "verb": "GET", + "next-available": "1970-01-01T00:00:00", + "unit": "MINUTE", + "value": 5, + "remaining": 5 + } + ] + } + ], + "absolute": { + "maxTotalVolumes": 40, + "maxTotalSnapshots": 40, + "maxTotalVolumeGigabytes": 1000, + "maxTotalBackups": 10, + "maxTotalBackupGigabytes": 1000, + "totalVolumesUsed": 1, + "totalGigabytesUsed": 100, + "totalSnapshotsUsed": 1, + "totalBackupsUsed": 1, + "totalBackupGigabytesUsed": 50 + } + } +} +` + +// LimitsResult is the result of the limits in GetOutput. +var LimitsResult = limits.Limits{ + Rate: []limits.Rate{ + { + Regex: ".*", + URI: "*", + Limit: []limits.Limit{ + { + Verb: "GET", + NextAvailable: "1970-01-01T00:00:00", + Unit: "MINUTE", + Value: 10, + Remaining: 10, + }, + { + Verb: "POST", + NextAvailable: "1970-01-01T00:00:00", + Unit: "HOUR", + Value: 5, + Remaining: 5, + }, + }, + }, + { + Regex: "changes-since", + URI: "changes-since*", + Limit: []limits.Limit{ + { + Verb: "GET", + NextAvailable: "1970-01-01T00:00:00", + Unit: "MINUTE", + Value: 5, + Remaining: 5, + }, + }, + }, + }, + Absolute: limits.Absolute{ + MaxTotalVolumes: 40, + MaxTotalSnapshots: 40, + MaxTotalVolumeGigabytes: 1000, + MaxTotalBackups: 10, + MaxTotalBackupGigabytes: 1000, + TotalVolumesUsed: 1, + TotalGigabytesUsed: 100, + TotalSnapshotsUsed: 1, + TotalBackupsUsed: 1, + TotalBackupGigabytesUsed: 50, + }, +} + +// HandleGetSuccessfully configures the test server to respond to a Get request +// for a limit. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/limits", 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, GetOutput) + }) +} diff --git a/openstack/blockstorage/v3/limits/testing/requests_test.go b/openstack/blockstorage/v3/limits/testing/requests_test.go new file mode 100644 index 0000000000..bd2a2cbfd2 --- /dev/null +++ b/openstack/blockstorage/v3/limits/testing/requests_test.go @@ -0,0 +1,20 @@ +package testing + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/limits" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := limits.Get(context.TODO(), client.ServiceClient()).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &LimitsResult, actual) +} diff --git a/openstack/blockstorage/v3/limits/urls.go b/openstack/blockstorage/v3/limits/urls.go new file mode 100644 index 0000000000..ac5b0f2333 --- /dev/null +++ b/openstack/blockstorage/v3/limits/urls.go @@ -0,0 +1,11 @@ +package limits + +import ( + "github.com/gophercloud/gophercloud/v2" +) + +const resourcePath = "limits" + +func getURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} diff --git a/openstack/blockstorage/v3/quotasets/doc.go b/openstack/blockstorage/v3/quotasets/doc.go new file mode 100644 index 0000000000..29964f0a7b --- /dev/null +++ b/openstack/blockstorage/v3/quotasets/doc.go @@ -0,0 +1,60 @@ +/* +Package quotasets enables retrieving and managing Block Storage quotas. + +Example to Get a Quota Set + + quotaset, err := quotasets.Get(context.TODO(), blockStorageClient, "project-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Get Quota Set Usage + + quotaset, err := quotasets.GetUsage(context.TODO(), blockStorageClient, "project-id").Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Update a Quota Set + + updateOpts := quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(100), + } + + quotaset, err := quotasets.Update(context.TODO(), blockStorageClient, "project-id", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Update a Quota set with volume_type quotas + + updateOpts := quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(100), + Extra: map[string]interface{}{ + "gigabytes_foo": gophercloud.IntToPointer(100), + "snapshots_foo": gophercloud.IntToPointer(10), + "volumes_foo": gophercloud.IntToPointer(10), + }, + } + + quotaset, err := quotasets.Update(context.TODO(), blockStorageClient, "project-id", updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", quotaset) + +Example to Delete a Quota Set + + err := quotasets.Delete(context.TODO(), blockStorageClient, "project-id").ExtractErr() + if err != nil { + panic(err) + } +*/ +package quotasets diff --git a/openstack/blockstorage/v3/quotasets/requests.go b/openstack/blockstorage/v3/quotasets/requests.go new file mode 100644 index 0000000000..21fa7c8f97 --- /dev/null +++ b/openstack/blockstorage/v3/quotasets/requests.go @@ -0,0 +1,117 @@ +package quotasets + +import ( + "context" + "fmt" + + "github.com/gophercloud/gophercloud/v2" +) + +// Get returns public data about a previously created QuotaSet. +func Get(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetDefaults returns public data about the project's default block storage quotas. +func GetDefaults(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetResult) { + resp, err := client.Get(ctx, getDefaultsURL(client, projectID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetUsage returns detailed public data about a previously created QuotaSet. +func GetUsage(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r GetUsageResult) { + u := fmt.Sprintf("%s?usage=true", getURL(client, projectID)) + resp, err := client.Get(ctx, u, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Updates the quotas for the given projectID and returns the new QuotaSet. +func Update(ctx context.Context, client *gophercloud.ServiceClient, projectID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToBlockStorageQuotaUpdateMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(ctx, updateURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder enables extensions to add parameters to the update request. +type UpdateOptsBuilder interface { + // Extra specific name to prevent collisions with interfaces for other quotas + // (e.g. neutron) + ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) +} + +// ToBlockStorageQuotaUpdateMap builds the update options into a serializable +// format. +func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "quota_set") + if err != nil { + return nil, err + } + + if opts.Extra != nil { + if v, ok := b["quota_set"].(map[string]interface{}); ok { + for key, value := range opts.Extra { + v[key] = value + } + } + } + + return b, nil +} + +// 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 { + // Volumes is the number of volumes that are allowed for each project. + Volumes *int `json:"volumes,omitempty"` + + // Snapshots is the number of snapshots that are allowed for each project. + Snapshots *int `json:"snapshots,omitempty"` + + // Gigabytes is the size (GB) of volumes and snapshots that are allowed for + // each project. + Gigabytes *int `json:"gigabytes,omitempty"` + + // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are + // allowed for each project and the specifed volume type. + PerVolumeGigabytes *int `json:"per_volume_gigabytes,omitempty"` + + // Backups is the number of backups that are allowed for each project. + Backups *int `json:"backups,omitempty"` + + // BackupGigabytes is the size (GB) of backups that are allowed for each + // project. + BackupGigabytes *int `json:"backup_gigabytes,omitempty"` + + // Groups is the number of groups that are allowed for each project. + Groups *int `json:"groups,omitempty"` + + // Force will update the quotaset even if the quota has already been used + // and the reserved quota exceeds the new quota. + Force bool `json:"force,omitempty"` + + // Extra is a collection of miscellaneous key/values used to set + // quota per volume_type + Extra map[string]interface{} `json:"-"` +} + +// Resets the quotas for the given tenant to their default values. +func Delete(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { + resp, err := client.Delete(ctx, updateURL(client, projectID), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/quotasets/results.go b/openstack/blockstorage/v3/quotasets/results.go new file mode 100644 index 0000000000..0ee49821ce --- /dev/null +++ b/openstack/blockstorage/v3/quotasets/results.go @@ -0,0 +1,209 @@ +package quotasets + +import ( + "encoding/json" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// QuotaSet is a set of operational limits that allow for control of block +// storage usage. +type QuotaSet struct { + // ID is project associated with this QuotaSet. + ID string `json:"id"` + + // Volumes is the number of volumes that are allowed for each project. + Volumes int `json:"volumes"` + + // Snapshots is the number of snapshots that are allowed for each project. + Snapshots int `json:"snapshots"` + + // Gigabytes is the size (GB) of volumes and snapshots that are allowed for + // each project. + Gigabytes int `json:"gigabytes"` + + // PerVolumeGigabytes is the size (GB) of volumes and snapshots that are + // allowed for each project and the specifed volume type. + PerVolumeGigabytes int `json:"per_volume_gigabytes"` + + // Backups is the number of backups that are allowed for each project. + Backups int `json:"backups"` + + // BackupGigabytes is the size (GB) of backups that are allowed for each + // project. + BackupGigabytes int `json:"backup_gigabytes"` + + // Groups is the number of groups that are allowed for each project. + Groups int `json:"groups,omitempty"` + + // Extra is a collection of miscellaneous key/values used to set + // quota per volume_type + Extra map[string]interface{} `json:"-"` +} + +// UnmarshalJSON is used on QuotaSet to unmarshal extra keys that are +// used for volume_type quota +func (r *QuotaSet) UnmarshalJSON(b []byte) error { + type tmp QuotaSet + var s struct { + tmp + Extra map[string]interface{} `json:"extra"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = QuotaSet(s.tmp) + + var result interface{} + err = json.Unmarshal(b, &result) + if err != nil { + return err + } + if resultMap, ok := result.(map[string]interface{}); ok { + r.Extra = gophercloud.RemainingKeys(QuotaSet{}, resultMap) + } + + return err +} + +// QuotaUsageSet represents details of both operational limits of block +// storage resources and the current usage of those resources. +type QuotaUsageSet struct { + // ID is the project ID associated with this QuotaUsageSet. + ID string `json:"id"` + + // Volumes is the volume usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Volumes QuotaUsage `json:"volumes"` + + // Snapshots is the snapshot usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Snapshots QuotaUsage `json:"snapshots"` + + // Gigabytes is the size (GB) usage information of volumes and snapshots + // for this project, including in_use, limit, reserved and allocated + // attributes. Note: allocated attribute is available only when nested + // quota is enabled. + Gigabytes QuotaUsage `json:"gigabytes"` + + // PerVolumeGigabytes is the size (GB) usage information for each volume, + // including in_use, limit, reserved and allocated attributes. Note: + // allocated attribute is available only when nested quota is enabled and + // only limit is meaningful here. + PerVolumeGigabytes QuotaUsage `json:"per_volume_gigabytes"` + + // Backups is the backup usage information for this project, including + // in_use, limit, reserved and allocated attributes. Note: allocated + // attribute is available only when nested quota is enabled. + Backups QuotaUsage `json:"backups"` + + // BackupGigabytes is the size (GB) usage information of backup for this + // project, including in_use, limit, reserved and allocated attributes. + // Note: allocated attribute is available only when nested quota is + // enabled. + BackupGigabytes QuotaUsage `json:"backup_gigabytes"` + + // Groups is the number of groups that are allowed for each project. + // Note: allocated attribute is available only when nested quota is + // enabled. + Groups QuotaUsage `json:"groups"` +} + +// QuotaUsage is a set of details about a single operational limit that allows +// for control of block storage usage. +type QuotaUsage struct { + // InUse is the current number of provisioned resources of the given type. + InUse int `json:"in_use"` + + // Allocated is the current number of resources of a given type allocated + // for use. It is only available when nested quota is enabled. + Allocated int `json:"allocated"` + + // 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 (r QuotaSetPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + ks, err := ExtractQuotaSets(r) + 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 +} + +// 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 +} + +type quotaUsageResult struct { + gophercloud.Result +} + +// GetUsageResult is the response from a Get operation. Call its Extract +// method to interpret it as a QuotaSet. +type GetUsageResult struct { + quotaUsageResult +} + +// Extract is a method that attempts to interpret any QuotaUsageSet resource +// response as a set of QuotaUsageSet structs. +func (r quotaUsageResult) Extract() (QuotaUsageSet, error) { + var s struct { + QuotaUsageSet QuotaUsageSet `json:"quota_set"` + } + err := r.ExtractInto(&s) + return s.QuotaUsageSet, err +} + +// 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 +} diff --git a/openstack/blockstorage/v3/quotasets/testing/doc.go b/openstack/blockstorage/v3/quotasets/testing/doc.go new file mode 100644 index 0000000000..30d864eb95 --- /dev/null +++ b/openstack/blockstorage/v3/quotasets/testing/doc.go @@ -0,0 +1,2 @@ +// quotasets unit tests +package testing diff --git a/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go b/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go new file mode 100644 index 0000000000..e996e6fda6 --- /dev/null +++ b/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go @@ -0,0 +1,179 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/quotasets" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const FirstTenantID = "555544443333222211110000ffffeeee" + +var getExpectedJSONBody = ` +{ + "quota_set" : { + "volumes" : 8, + "snapshots" : 9, + "gigabytes" : 10, + "per_volume_gigabytes" : 11, + "backups" : 12, + "backup_gigabytes" : 13, + "groups": 14 + } +}` + +var getExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 8, + Snapshots: 9, + Gigabytes: 10, + PerVolumeGigabytes: 11, + Backups: 12, + BackupGigabytes: 13, + Groups: 14, + Extra: make(map[string]interface{}), +} + +var getUsageExpectedJSONBody = ` +{ + "quota_set" : { + "id": "555544443333222211110000ffffeeee", + "volumes" : { + "in_use": 15, + "limit": 16, + "reserved": 17 + }, + "snapshots" : { + "in_use": 18, + "limit": 19, + "reserved": 20 + }, + "gigabytes" : { + "in_use": 21, + "limit": 22, + "reserved": 23 + }, + "per_volume_gigabytes" : { + "in_use": 24, + "limit": 25, + "reserved": 26 + }, + "backups" : { + "in_use": 27, + "limit": 28, + "reserved": 29 + }, + "backup_gigabytes" : { + "in_use": 30, + "limit": 31, + "reserved": 32 + }, + "groups" : { + "in_use": 40, + "limit": 41, + "reserved": 42 + } + } +}` + +var getUsageExpectedQuotaSet = quotasets.QuotaUsageSet{ + ID: FirstTenantID, + Volumes: quotasets.QuotaUsage{InUse: 15, Limit: 16, Reserved: 17}, + Snapshots: quotasets.QuotaUsage{InUse: 18, Limit: 19, Reserved: 20}, + Gigabytes: quotasets.QuotaUsage{InUse: 21, Limit: 22, Reserved: 23}, + PerVolumeGigabytes: quotasets.QuotaUsage{InUse: 24, Limit: 25, Reserved: 26}, + Backups: quotasets.QuotaUsage{InUse: 27, Limit: 28, Reserved: 29}, + BackupGigabytes: quotasets.QuotaUsage{InUse: 30, Limit: 31, Reserved: 32}, + Groups: quotasets.QuotaUsage{InUse: 40, Limit: 41, Reserved: 42}, +} + +var fullUpdateExpectedJSONBody = ` +{ + "quota_set": { + "volumes": 8, + "snapshots": 9, + "gigabytes": 10, + "per_volume_gigabytes": 11, + "backups": 12, + "backup_gigabytes": 13, + "groups": 14 + } +}` + +var fullUpdateOpts = quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(8), + Snapshots: gophercloud.IntToPointer(9), + Gigabytes: gophercloud.IntToPointer(10), + PerVolumeGigabytes: gophercloud.IntToPointer(11), + Backups: gophercloud.IntToPointer(12), + BackupGigabytes: gophercloud.IntToPointer(13), + Groups: gophercloud.IntToPointer(14), + Extra: make(map[string]interface{}), +} + +var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 8, + Snapshots: 9, + Gigabytes: 10, + PerVolumeGigabytes: 11, + Backups: 12, + BackupGigabytes: 13, + Groups: 14, + Extra: make(map[string]interface{}), +} + +var partialUpdateExpectedJSONBody = ` +{ + "quota_set": { + "volumes": 200, + "snapshots": 0, + "gigabytes": 0, + "per_volume_gigabytes": 0, + "backups": 0, + "backup_gigabytes": 0 + } +}` + +var partialUpdateOpts = quotasets.UpdateOpts{ + Volumes: gophercloud.IntToPointer(200), + Snapshots: gophercloud.IntToPointer(0), + Gigabytes: gophercloud.IntToPointer(0), + PerVolumeGigabytes: gophercloud.IntToPointer(0), + Backups: gophercloud.IntToPointer(0), + BackupGigabytes: gophercloud.IntToPointer(0), + Extra: make(map[string]interface{}), +} + +var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{ + Volumes: 200, + Extra: make(map[string]interface{}), +} + +// HandleSuccessfulRequest configures the test server to respond to an HTTP request. +func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput string, uriQueryParams map[string]string) { + + th.Mux.HandleFunc(uriPath, func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, httpMethod) + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.Header().Add("Content-Type", "application/json") + + if uriQueryParams != nil { + th.TestFormValues(t, r, uriQueryParams) + } + + fmt.Fprintf(w, jsonOutput) + }) +} + +// HandleDeleteSuccessfully tests quotaset deletion. +func HandleDeleteSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, 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) + }) +} diff --git a/openstack/blockstorage/v3/quotasets/testing/requests_test.go b/openstack/blockstorage/v3/quotasets/testing/requests_test.go new file mode 100644 index 0000000000..266db833cb --- /dev/null +++ b/openstack/blockstorage/v3/quotasets/testing/requests_test.go @@ -0,0 +1,81 @@ +package testing + +import ( + "context" + "errors" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/quotasets" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Get(context.TODO(), client.ServiceClient(), FirstTenantID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &getExpectedQuotaSet, actual) +} + +func TestGetUsage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{"usage": "true"} + HandleSuccessfulRequest(t, "GET", "/os-quota-sets/"+FirstTenantID, getUsageExpectedJSONBody, uriQueryParms) + actual, err := quotasets.GetUsage(context.TODO(), client.ServiceClient(), FirstTenantID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, getUsageExpectedQuotaSet, actual) +} + +func TestFullUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, fullUpdateExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, fullUpdateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &fullUpdateExpectedQuotaSet, actual) +} + +func TestPartialUpdate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + uriQueryParms := map[string]string{} + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, partialUpdateExpectedJSONBody, uriQueryParms) + actual, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, partialUpdateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &partiualUpdateExpectedQuotaSet, actual) +} + +type ErrorUpdateOpts quotasets.UpdateOpts + +func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { + return nil, errors.New("This is an error") +} + +func TestErrorInToBlockStorageQuotaUpdateMap(t *testing.T) { + opts := &ErrorUpdateOpts{} + th.SetupHTTP() + defer th.TeardownHTTP() + HandleSuccessfulRequest(t, "PUT", "/os-quota-sets/"+FirstTenantID, "", nil) + _, err := quotasets.Update(context.TODO(), client.ServiceClient(), FirstTenantID, opts).Extract() + if err == nil { + t.Fatal("Error handling failed") + } +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteSuccessfully(t) + + err := quotasets.Delete(context.TODO(), client.ServiceClient(), FirstTenantID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/v3/quotasets/urls.go b/openstack/blockstorage/v3/quotasets/urls.go new file mode 100644 index 0000000000..c3cc4b0c71 --- /dev/null +++ b/openstack/blockstorage/v3/quotasets/urls.go @@ -0,0 +1,21 @@ +package quotasets + +import "github.com/gophercloud/gophercloud/v2" + +const resourcePath = "os-quota-sets" + +func getURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID) +} + +func getDefaultsURL(c *gophercloud.ServiceClient, projectID string) string { + return c.ServiceURL(resourcePath, projectID, "defaults") +} + +func updateURL(c *gophercloud.ServiceClient, projectID string) string { + return getURL(c, projectID) +} + +func deleteURL(c *gophercloud.ServiceClient, projectID string) string { + return getURL(c, projectID) +} diff --git a/openstack/blockstorage/v3/schedulerstats/doc.go b/openstack/blockstorage/v3/schedulerstats/doc.go new file mode 100644 index 0000000000..4e028b56ed --- /dev/null +++ b/openstack/blockstorage/v3/schedulerstats/doc.go @@ -0,0 +1,23 @@ +/* +Package schedulerstats returns information about block storage pool capacity +and utilisation. Example: + + listOpts := schedulerstats.ListOpts{ + Detail: true, + } + + allPages, err := schedulerstats.List(client, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allStats, err := schedulerstats.ExtractStoragePools(allPages) + if err != nil { + panic(err) + } + + for _, stat := range allStats { + fmt.Printf("%+v\n", stat) + } +*/ +package schedulerstats diff --git a/openstack/blockstorage/v3/schedulerstats/requests.go b/openstack/blockstorage/v3/schedulerstats/requests.go new file mode 100644 index 0000000000..629b42124d --- /dev/null +++ b/openstack/blockstorage/v3/schedulerstats/requests.go @@ -0,0 +1,43 @@ +package schedulerstats + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToStoragePoolsListQuery() (string, error) +} + +// ListOpts controls the view of data returned (e.g globally or per project) +// via tenant_id and the verbosity via detail. +type ListOpts struct { + // ID of the tenant to look up storage pools for. + TenantID string `q:"tenant_id"` + + // Whether to list extended details. + Detail bool `q:"detail"` +} + +// ToStoragePoolsListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToStoragePoolsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List makes a request against the API to list storage pool information. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := storagePoolsListURL(client) + if opts != nil { + query, err := opts.ToStoragePoolsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return StoragePoolPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/blockstorage/v3/schedulerstats/results.go b/openstack/blockstorage/v3/schedulerstats/results.go new file mode 100644 index 0000000000..2170c483ed --- /dev/null +++ b/openstack/blockstorage/v3/schedulerstats/results.go @@ -0,0 +1,116 @@ +package schedulerstats + +import ( + "encoding/json" + "math" + "strconv" + + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// Capabilities represents the information of an individual StoragePool. +type Capabilities struct { + // The following fields should be present in all storage drivers. + DriverVersion string `json:"driver_version"` + FreeCapacityGB float64 `json:"-"` + StorageProtocol string `json:"storage_protocol"` + TotalCapacityGB float64 `json:"-"` + VendorName string `json:"vendor_name"` + VolumeBackendName string `json:"volume_backend_name"` + + // The following fields are optional and may have empty values depending + // on the storage driver in use. + ReservedPercentage int64 `json:"reserved_percentage"` + LocationInfo string `json:"location_info"` + QoSSupport bool `json:"QoS_support"` + ProvisionedCapacityGB float64 `json:"provisioned_capacity_gb"` + MaxOverSubscriptionRatio string `json:"-"` + ThinProvisioningSupport bool `json:"thin_provisioning_support"` + ThickProvisioningSupport bool `json:"thick_provisioning_support"` + TotalVolumes int64 `json:"total_volumes"` + FilterFunction string `json:"filter_function"` + GoodnessFunction 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 +// schedulerstats API. +type StoragePool struct { + Name string `json:"name"` + Capabilities Capabilities `json:"capabilities"` +} + +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"` + } + 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) + + if s.MaxOverSubscriptionRatio != nil { + switch t := s.MaxOverSubscriptionRatio.(type) { + case float64: + r.MaxOverSubscriptionRatio = strconv.FormatFloat(t, 'f', -1, 64) + case string: + r.MaxOverSubscriptionRatio = t + } + } + + return nil +} + +// StoragePoolPage is a single page of all List results. +type StoragePoolPage struct { + pagination.SinglePageBase +} + +// 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 +} + +// ExtractStoragePools takes a List result and extracts the collection of +// StoragePools returned by the API. +func ExtractStoragePools(p pagination.Page) ([]StoragePool, error) { + var s struct { + StoragePools []StoragePool `json:"pools"` + } + err := (p.(StoragePoolPage)).ExtractInto(&s) + return s.StoragePools, err +} diff --git a/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go new file mode 100644 index 0000000000..d92301cc63 --- /dev/null +++ b/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go @@ -0,0 +1,110 @@ +package testing + +import ( + "fmt" + "math" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/schedulerstats" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const StoragePoolsListBody = ` +{ + "pools": [ + { + "name": "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd" + }, + { + "name": "rbd:cinder.volumes.hdd@cinder.volumes#cinder.volumes.hdd" + } + ] +} +` + +const StoragePoolsListBodyDetail = ` +{ + "pools": [ + { + "capabilities": { + "driver_version": "1.2.0", + "filter_function": null, + "free_capacity_gb": 64765, + "goodness_function": null, + "max_over_subscription_ratio": "1.5", + "multiattach": false, + "reserved_percentage": 0, + "storage_protocol": "ceph", + "timestamp": "2016-11-24T10:33:51.248360", + "total_capacity_gb": 787947.93, + "vendor_name": "Open Source", + "volume_backend_name": "cinder.volumes.ssd" + }, + "name": "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd" + }, + { + "capabilities": { + "driver_version": "1.2.0", + "filter_function": null, + "free_capacity_gb": "unknown", + "goodness_function": null, + "max_over_subscription_ratio": 1.5, + "multiattach": false, + "reserved_percentage": 0, + "storage_protocol": "ceph", + "timestamp": "2016-11-24T10:33:43.138628", + "total_capacity_gb": "infinite", + "vendor_name": "Open Source", + "volume_backend_name": "cinder.volumes.hdd" + }, + "name": "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd" + } + ] +} +` + +var ( + StoragePoolFake1 = schedulerstats.StoragePool{ + Name: "rbd:cinder.volumes.ssd@cinder.volumes.ssd#cinder.volumes.ssd", + Capabilities: schedulerstats.Capabilities{ + DriverVersion: "1.2.0", + FreeCapacityGB: 64765, + MaxOverSubscriptionRatio: "1.5", + StorageProtocol: "ceph", + TotalCapacityGB: 787947.93, + VendorName: "Open Source", + VolumeBackendName: "cinder.volumes.ssd", + }, + } + + StoragePoolFake2 = schedulerstats.StoragePool{ + Name: "rbd:cinder.volumes.hdd@cinder.volumes.hdd#cinder.volumes.hdd", + Capabilities: schedulerstats.Capabilities{ + DriverVersion: "1.2.0", + FreeCapacityGB: 0.0, + MaxOverSubscriptionRatio: "1.5", + StorageProtocol: "ceph", + TotalCapacityGB: math.Inf(1), + VendorName: "Open Source", + VolumeBackendName: "cinder.volumes.hdd", + }, + } +) + +func HandleStoragePoolsListSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/scheduler-stats/get_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() + if r.FormValue("detail") == "true" { + fmt.Fprintf(w, StoragePoolsListBodyDetail) + } else { + fmt.Fprintf(w, StoragePoolsListBody) + } + }) +} diff --git a/openstack/blockstorage/v3/schedulerstats/testing/requests_test.go b/openstack/blockstorage/v3/schedulerstats/testing/requests_test.go new file mode 100644 index 0000000000..b5e3f31ef6 --- /dev/null +++ b/openstack/blockstorage/v3/schedulerstats/testing/requests_test.go @@ -0,0 +1,39 @@ +package testing + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/schedulerstats" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestListStoragePoolsDetail(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleStoragePoolsListSuccessfully(t) + + pages := 0 + err := schedulerstats.List(client.ServiceClient(), schedulerstats.ListOpts{Detail: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + pages++ + + actual, err := schedulerstats.ExtractStoragePools(page) + testhelper.AssertNoErr(t, err) + + if len(actual) != 2 { + t.Fatalf("Expected 2 backends, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, StoragePoolFake1, actual[0]) + testhelper.CheckDeepEquals(t, StoragePoolFake2, actual[1]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} diff --git a/openstack/blockstorage/v3/schedulerstats/urls.go b/openstack/blockstorage/v3/schedulerstats/urls.go new file mode 100644 index 0000000000..0ed58a490b --- /dev/null +++ b/openstack/blockstorage/v3/schedulerstats/urls.go @@ -0,0 +1,7 @@ +package schedulerstats + +import "github.com/gophercloud/gophercloud/v2" + +func storagePoolsListURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("scheduler-stats", "get_pools") +} diff --git a/openstack/blockstorage/v3/services/doc.go b/openstack/blockstorage/v3/services/doc.go new file mode 100644 index 0000000000..a68ed88f49 --- /dev/null +++ b/openstack/blockstorage/v3/services/doc.go @@ -0,0 +1,22 @@ +/* +Package services returns information about the blockstorage services in the +OpenStack cloud. + +Example of Retrieving list of all services + + allPages, err := services.List(blockstorageClient, services.ListOpts{}).AllPages(context.TODO()) + 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/blockstorage/v3/services/requests.go b/openstack/blockstorage/v3/services/requests.go new file mode 100644 index 0000000000..fea0927da0 --- /dev/null +++ b/openstack/blockstorage/v3/services/requests.go @@ -0,0 +1,42 @@ +package services + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToServiceListQuery() (string, error) +} + +// ListOpts holds options for listing Services. +type ListOpts struct { + // Filter the service list result by binary name of the service. + Binary string `q:"binary"` + + // Filter the service list result by host name of the service. + Host string `q:"host"` +} + +// 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/blockstorage/v3/services/results.go b/openstack/blockstorage/v3/services/results.go new file mode 100644 index 0000000000..1e2d10f624 --- /dev/null +++ b/openstack/blockstorage/v3/services/results.go @@ -0,0 +1,88 @@ +package services + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// Service represents a Blockstorage service in the OpenStack cloud. +type Service struct { + // The binary name of the service. + Binary string `json:"binary"` + + // The reason for disabling a service. + DisabledReason string `json:"disabled_reason"` + + // The name of the host. + Host string `json:"host"` + + // 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"` + + // The following fields are optional + + // The host is frozen or not. Only in cinder-volume service. + Frozen bool `json:"frozen"` + + // The cluster name. Only in cinder-volume service. + Cluster string `json:"cluster"` + + // The volume service replication status. Only in cinder-volume service. + ReplicationStatus string `json:"replication_status"` + + // The ID of active storage backend. Only in cinder-volume service. + ActiveBackendID string `json:"active_backend_id"` +} + +// 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) { + if page.StatusCode == 204 { + return true, nil + } + + 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/blockstorage/v3/services/testing/fixtures_test.go b/openstack/blockstorage/v3/services/testing/fixtures_test.go new file mode 100644 index 0000000000..adf7993df7 --- /dev/null +++ b/openstack/blockstorage/v3/services/testing/fixtures_test.go @@ -0,0 +1,97 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/services" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +// ServiceListBody is sample response to the List call +const ServiceListBody = ` +{ + "services": [{ + "status": "enabled", + "binary": "cinder-scheduler", + "zone": "nova", + "state": "up", + "updated_at": "2017-06-29T05:50:35.000000", + "host": "devstack", + "disabled_reason": null + }, + { + "status": "enabled", + "binary": "cinder-backup", + "zone": "nova", + "state": "up", + "updated_at": "2017-06-29T05:50:42.000000", + "host": "devstack", + "disabled_reason": null + }, + { + "status": "enabled", + "binary": "cinder-volume", + "zone": "nova", + "frozen": false, + "state": "up", + "updated_at": "2017-06-29T05:50:39.000000", + "cluster": null, + "host": "devstack@lvmdriver-1", + "replication_status": "disabled", + "active_backend_id": null, + "disabled_reason": null + }] +} +` + +// First service from the ServiceListBody +var FirstFakeService = services.Service{ + Binary: "cinder-scheduler", + DisabledReason: "", + Host: "devstack", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 35, 0, time.UTC), + Zone: "nova", +} + +// Second service from the ServiceListBody +var SecondFakeService = services.Service{ + Binary: "cinder-backup", + DisabledReason: "", + Host: "devstack", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 42, 0, time.UTC), + Zone: "nova", +} + +// Third service from the ServiceListBody +var ThirdFakeService = services.Service{ + ActiveBackendID: "", + Binary: "cinder-volume", + Cluster: "", + DisabledReason: "", + Frozen: false, + Host: "devstack@lvmdriver-1", + ReplicationStatus: "disabled", + State: "up", + Status: "enabled", + UpdatedAt: time.Date(2017, 6, 29, 5, 50, 39, 0, time.UTC), + Zone: "nova", +} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-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/blockstorage/v3/services/testing/requests_test.go b/openstack/blockstorage/v3/services/testing/requests_test.go new file mode 100644 index 0000000000..1fd5bcdd1e --- /dev/null +++ b/openstack/blockstorage/v3/services/testing/requests_test.go @@ -0,0 +1,42 @@ +package testing + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/services" + "github.com/gophercloud/gophercloud/v2/pagination" + "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestListServices(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleListSuccessfully(t) + + pages := 0 + err := services.List(client.ServiceClient(), services.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + pages++ + + actual, err := services.ExtractServices(page) + if err != nil { + return false, err + } + + if len(actual) != 3 { + t.Fatalf("Expected 3 services, got %d", len(actual)) + } + testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) + testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) + testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) + + return true, nil + }) + + testhelper.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} diff --git a/openstack/blockstorage/v3/services/urls.go b/openstack/blockstorage/v3/services/urls.go new file mode 100644 index 0000000000..e46d27ae6a --- /dev/null +++ b/openstack/blockstorage/v3/services/urls.go @@ -0,0 +1,7 @@ +package services + +import "github.com/gophercloud/gophercloud/v2" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-services") +} From a1c73d4d6c3df87752d52695ecdf4d05d908bfc5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 15:11:08 +0000 Subject: [PATCH 1802/2296] blockstorage: Merge volumetransfers extension This is handled separately because we opt to rename the module in the process, from volumetransfers to simply transfers. Otherwise, this is effectively a standalone module. Signed-off-by: Stephen Finucane --- .../volumetransfers => v2/transfers}/doc.go | 20 +- .../transfers}/requests.go | 2 +- .../transfers}/results.go | 2 +- .../transfers}/testing/fixtures_test.go | 12 +- .../transfers}/testing/requests_test.go | 18 +- .../volumetransfers => v2/transfers}/urls.go | 2 +- openstack/blockstorage/v3/transfers/doc.go | 65 ++++++ .../blockstorage/v3/transfers/requests.go | 126 ++++++++++ .../blockstorage/v3/transfers/results.go | 106 +++++++++ .../v3/transfers/testing/fixtures_test.go | 216 ++++++++++++++++++ .../v3/transfers/testing/requests_test.go | 91 ++++++++ openstack/blockstorage/v3/transfers/urls.go | 23 ++ 12 files changed, 655 insertions(+), 28 deletions(-) rename openstack/blockstorage/{extensions/volumetransfers => v2/transfers}/doc.go (62%) rename openstack/blockstorage/{extensions/volumetransfers => v2/transfers}/requests.go (99%) rename openstack/blockstorage/{extensions/volumetransfers => v2/transfers}/results.go (99%) rename openstack/blockstorage/{extensions/volumetransfers => v2/transfers}/testing/fixtures_test.go (94%) rename openstack/blockstorage/{extensions/volumetransfers => v2/transfers}/testing/requests_test.go (62%) rename openstack/blockstorage/{extensions/volumetransfers => v2/transfers}/urls.go (96%) create mode 100644 openstack/blockstorage/v3/transfers/doc.go create mode 100644 openstack/blockstorage/v3/transfers/requests.go create mode 100644 openstack/blockstorage/v3/transfers/results.go create mode 100644 openstack/blockstorage/v3/transfers/testing/fixtures_test.go create mode 100644 openstack/blockstorage/v3/transfers/testing/requests_test.go create mode 100644 openstack/blockstorage/v3/transfers/urls.go diff --git a/openstack/blockstorage/extensions/volumetransfers/doc.go b/openstack/blockstorage/v2/transfers/doc.go similarity index 62% rename from openstack/blockstorage/extensions/volumetransfers/doc.go rename to openstack/blockstorage/v2/transfers/doc.go index af85ce3e2a..db298da464 100644 --- a/openstack/blockstorage/extensions/volumetransfers/doc.go +++ b/openstack/blockstorage/v2/transfers/doc.go @@ -1,21 +1,21 @@ /* -Package volumetransfers provides an interaction with volume transfers in the +Package transfers provides an interaction with volume transfers in the OpenStack Block Storage service. A volume transfer allows to transfer volumes between projects withing the same OpenStack region. Example to List all Volume Transfer requests being an OpenStack admin - listOpts := &volumetransfers.ListOpts{ + listOpts := &transfers.ListOpts{ // this option is available only for OpenStack cloud admin AllTenants: true, } - allPages, err := volumetransfers.List(client, listOpts).AllPages(context.TODO()) + allPages, err := transfers.List(client, listOpts).AllPages(context.TODO()) if err != nil { panic(err) } - allTransfers, err := volumetransfers.ExtractTransfers(allPages) + allTransfers, err := transfers.ExtractTransfers(allPages) if err != nil { panic(err) } @@ -26,12 +26,12 @@ Example to List all Volume Transfer requests being an OpenStack admin Example to Create a Volume Transfer request - createOpts := volumetransfers.CreateOpts{ + createOpts := transfers.CreateOpts{ VolumeID: "uuid", Name: "my-volume-transfer", } - transfer, err := volumetransfers.Create(context.TODO(), client, createOpts).Extract() + transfer, err := transfers.Create(context.TODO(), client, createOpts).Extract() if err != nil { panic(err) } @@ -42,13 +42,13 @@ Example to Create a Volume Transfer request Example to Accept a Volume Transfer request from the target project - acceptOpts := volumetransfers.AcceptOpts{ + acceptOpts := transfers.AcceptOpts{ // see the create response above AuthKey: "volume-transfer-secret-auth-key", } // see the transfer ID from the create response above - transfer, err := volumetransfers.Accept(context.TODO(), client, "transfer-uuid", acceptOpts).Extract() + transfer, err := transfers.Accept(context.TODO(), client, "transfer-uuid", acceptOpts).Extract() if err != nil { panic(err) } @@ -57,9 +57,9 @@ Example to Accept a Volume Transfer request from the target project Example to Delete a Volume Transfer request from the source project - err := volumetransfers.Delete(context.TODO(), client, "transfer-uuid").ExtractErr() + err := transfers.Delete(context.TODO(), client, "transfer-uuid").ExtractErr() if err != nil { panic(err) } */ -package volumetransfers +package transfers diff --git a/openstack/blockstorage/extensions/volumetransfers/requests.go b/openstack/blockstorage/v2/transfers/requests.go similarity index 99% rename from openstack/blockstorage/extensions/volumetransfers/requests.go rename to openstack/blockstorage/v2/transfers/requests.go index 8cdd7d152e..f32a750bfa 100644 --- a/openstack/blockstorage/extensions/volumetransfers/requests.go +++ b/openstack/blockstorage/v2/transfers/requests.go @@ -1,4 +1,4 @@ -package volumetransfers +package transfers import ( "context" diff --git a/openstack/blockstorage/extensions/volumetransfers/results.go b/openstack/blockstorage/v2/transfers/results.go similarity index 99% rename from openstack/blockstorage/extensions/volumetransfers/results.go rename to openstack/blockstorage/v2/transfers/results.go index 2eee0d2829..f5e934efea 100644 --- a/openstack/blockstorage/extensions/volumetransfers/results.go +++ b/openstack/blockstorage/v2/transfers/results.go @@ -1,4 +1,4 @@ -package volumetransfers +package transfers import ( "encoding/json" diff --git a/openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go b/openstack/blockstorage/v2/transfers/testing/fixtures_test.go similarity index 94% rename from openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go rename to openstack/blockstorage/v2/transfers/testing/fixtures_test.go index c094efd132..6a150508eb 100644 --- a/openstack/blockstorage/extensions/volumetransfers/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/transfers/testing/fixtures_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetransfers" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/transfers" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -114,12 +114,12 @@ const AcceptTransferResponse = ` } ` -var TransferRequest = volumetransfers.CreateOpts{ +var TransferRequest = transfers.CreateOpts{ VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", } var createdAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-02-28T12:44:28.051989") -var TransferResponse = volumetransfers.Transfer{ +var TransferResponse = transfers.Transfer{ ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", AuthKey: "cb67e0e7387d9eac", Name: "", @@ -137,13 +137,13 @@ var TransferResponse = volumetransfers.Transfer{ }, } -var TransferListResponse = []volumetransfers.Transfer{TransferResponse} +var TransferListResponse = []transfers.Transfer{TransferResponse} -var AcceptRequest = volumetransfers.AcceptOpts{ +var AcceptRequest = transfers.AcceptOpts{ AuthKey: "9266c59563c84664", } -var AcceptResponse = volumetransfers.Transfer{ +var AcceptResponse = transfers.Transfer{ ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", Name: "", VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", diff --git a/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go b/openstack/blockstorage/v2/transfers/testing/requests_test.go similarity index 62% rename from openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go rename to openstack/blockstorage/v2/transfers/testing/requests_test.go index d5fc321473..32501eb095 100644 --- a/openstack/blockstorage/extensions/volumetransfers/testing/requests_test.go +++ b/openstack/blockstorage/v2/transfers/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetransfers" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/transfers" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" @@ -15,7 +15,7 @@ func TestCreateTransfer(t *testing.T) { defer th.TeardownHTTP() HandleCreateTransfer(t) - actual, err := volumetransfers.Create(context.TODO(), client.ServiceClient(), TransferRequest).Extract() + actual, err := transfers.Create(context.TODO(), client.ServiceClient(), TransferRequest).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, TransferResponse, *actual) } @@ -25,7 +25,7 @@ func TestAcceptTransfer(t *testing.T) { defer th.TeardownHTTP() HandleAcceptTransfer(t) - actual, err := volumetransfers.Accept(context.TODO(), client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() + actual, err := transfers.Accept(context.TODO(), client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, AcceptResponse, *actual) } @@ -35,7 +35,7 @@ func TestDeleteTransfer(t *testing.T) { defer th.TeardownHTTP() HandleDeleteTransfer(t) - err := volumetransfers.Delete(context.TODO(), client.ServiceClient(), TransferResponse.ID).ExtractErr() + err := transfers.Delete(context.TODO(), client.ServiceClient(), TransferResponse.ID).ExtractErr() th.AssertNoErr(t, err) } @@ -48,10 +48,10 @@ func TestListTransfers(t *testing.T) { expectedResponse[0].AuthKey = "" count := 0 - err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := transfers.List(client.ServiceClient(), &transfers.ListOpts{AllTenants: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ - actual, err := volumetransfers.ExtractTransfers(page) + actual, err := transfers.ExtractTransfers(page) th.AssertNoErr(t, err) th.CheckDeepEquals(t, expectedResponse, actual) @@ -70,9 +70,9 @@ func TestListTransfersAllPages(t *testing.T) { expectedResponse := TransferListResponse expectedResponse[0].AuthKey = "" - allPages, err := volumetransfers.List(client.ServiceClient(), &volumetransfers.ListOpts{AllTenants: true}).AllPages(context.TODO()) + allPages, err := transfers.List(client.ServiceClient(), &transfers.ListOpts{AllTenants: true}).AllPages(context.TODO()) th.AssertNoErr(t, err) - actual, err := volumetransfers.ExtractTransfers(allPages) + actual, err := transfers.ExtractTransfers(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, expectedResponse, actual) } @@ -85,7 +85,7 @@ func TestGetTransfer(t *testing.T) { expectedResponse := TransferResponse expectedResponse.AuthKey = "" - actual, err := volumetransfers.Get(context.TODO(), client.ServiceClient(), TransferResponse.ID).Extract() + actual, err := transfers.Get(context.TODO(), client.ServiceClient(), TransferResponse.ID).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, expectedResponse, *actual) } diff --git a/openstack/blockstorage/extensions/volumetransfers/urls.go b/openstack/blockstorage/v2/transfers/urls.go similarity index 96% rename from openstack/blockstorage/extensions/volumetransfers/urls.go rename to openstack/blockstorage/v2/transfers/urls.go index 91c5e749bf..0366370794 100644 --- a/openstack/blockstorage/extensions/volumetransfers/urls.go +++ b/openstack/blockstorage/v2/transfers/urls.go @@ -1,4 +1,4 @@ -package volumetransfers +package transfers import "github.com/gophercloud/gophercloud/v2" diff --git a/openstack/blockstorage/v3/transfers/doc.go b/openstack/blockstorage/v3/transfers/doc.go new file mode 100644 index 0000000000..db298da464 --- /dev/null +++ b/openstack/blockstorage/v3/transfers/doc.go @@ -0,0 +1,65 @@ +/* +Package transfers provides an interaction with volume transfers in the +OpenStack Block Storage service. A volume transfer allows to transfer volumes +between projects withing the same OpenStack region. + +Example to List all Volume Transfer requests being an OpenStack admin + + listOpts := &transfers.ListOpts{ + // this option is available only for OpenStack cloud admin + AllTenants: true, + } + + allPages, err := transfers.List(client, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allTransfers, err := transfers.ExtractTransfers(allPages) + if err != nil { + panic(err) + } + + for _, transfer := range allTransfers { + fmt.Println(transfer) + } + +Example to Create a Volume Transfer request + + createOpts := transfers.CreateOpts{ + VolumeID: "uuid", + Name: "my-volume-transfer", + } + + transfer, err := transfers.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(transfer) + // secret auth key is returned only once as a create response + fmt.Printf("AuthKey: %s\n", transfer.AuthKey) + +Example to Accept a Volume Transfer request from the target project + + acceptOpts := transfers.AcceptOpts{ + // see the create response above + AuthKey: "volume-transfer-secret-auth-key", + } + + // see the transfer ID from the create response above + transfer, err := transfers.Accept(context.TODO(), client, "transfer-uuid", acceptOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Println(transfer) + +Example to Delete a Volume Transfer request from the source project + + err := transfers.Delete(context.TODO(), client, "transfer-uuid").ExtractErr() + if err != nil { + panic(err) + } +*/ +package transfers diff --git a/openstack/blockstorage/v3/transfers/requests.go b/openstack/blockstorage/v3/transfers/requests.go new file mode 100644 index 0000000000..f32a750bfa --- /dev/null +++ b/openstack/blockstorage/v3/transfers/requests.go @@ -0,0 +1,126 @@ +package transfers + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// CreateOpts contains options for a Volume transfer. +type CreateOpts struct { + // The ID of the volume to transfer. + VolumeID string `json:"volume_id" required:"true"` + + // The name of the volume transfer + Name string `json:"name,omitempty"` +} + +// ToCreateMap assembles a request body based on the contents of a +// TransferOpts. +func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "transfer") +} + +// Create will create a volume tranfer request based on the values in CreateOpts. +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := opts.ToCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, transferURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// AcceptOpts contains options for a Volume transfer accept reqeust. +type AcceptOpts struct { + // The auth key of the volume transfer to accept. + AuthKey string `json:"auth_key" required:"true"` +} + +// 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 volume tranfer request based on the values in AcceptOpts. +func Accept(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AcceptOpts) (r CreateResult) { + b, err := opts.ToAcceptMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, acceptURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete deletes a volume transfer. +func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// 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 transfers.List +// function. +type ListOpts struct { + // AllTenants will retrieve transfers of all tenants/projects. + AllTenants bool `q:"all_tenants"` + + // Comma-separated list of sort keys and optional sort directions in the + // form of [:]. + Sort string `q:"sort"` + + // Requests a page size of items. + Limit int `q:"limit"` + + // Used in conjunction with limit to return a slice of items. + Offset int `q:"offset"` + + // The ID of the last-seen item. + Marker string `q:"marker"` +} + +// 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 { + return TransferPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// 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(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/transfers/results.go b/openstack/blockstorage/v3/transfers/results.go new file mode 100644 index 0000000000..f5e934efea --- /dev/null +++ b/openstack/blockstorage/v3/transfers/results.go @@ -0,0 +1,106 @@ +package transfers + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// Transfer represents a Volume Transfer record +type Transfer struct { + ID string `json:"id"` + AuthKey string `json:"auth_key"` + Name string `json:"name"` + VolumeID string `json:"volume_id"` + CreatedAt 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"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = Transfer(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + + 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 +} + +// 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.LinkedPageBase +} + +// 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 +} + +func (page TransferPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"transfers_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} diff --git a/openstack/blockstorage/v3/transfers/testing/fixtures_test.go b/openstack/blockstorage/v3/transfers/testing/fixtures_test.go new file mode 100644 index 0000000000..b2c11f60c7 --- /dev/null +++ b/openstack/blockstorage/v3/transfers/testing/fixtures_test.go @@ -0,0 +1,216 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/transfers" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const ListOutput = ` +{ + "transfers": [ + { + "created_at": "2020-02-28T12:44:28.051989", + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null + } + ] +} +` + +const GetOutput = ` +{ + "transfer": { + "created_at": "2020-02-28T12:44:28.051989", + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758", + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null + } +} +` + +const CreateRequest = ` +{ + "transfer": { + "volume_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://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null, + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +const AcceptTransferRequest = ` +{ + "accept": { + "auth_key": "9266c59563c84664" + } +} +` + +const AcceptTransferResponse = ` +{ + "transfer": { + "id": "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "links": [ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self" + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark" + } + ], + "name": null, + "volume_id": "2f6f1684-1ded-40db-8a49-7c87dedbc758" + } +} +` + +var TransferRequest = transfers.CreateOpts{ + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", +} + +var createdAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2020-02-28T12:44:28.051989") +var TransferResponse = transfers.Transfer{ + ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + AuthKey: "cb67e0e7387d9eac", + Name: "", + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", + CreatedAt: createdAt, + Links: []map[string]string{ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self", + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark", + }, + }, +} + +var TransferListResponse = []transfers.Transfer{TransferResponse} + +var AcceptRequest = transfers.AcceptOpts{ + AuthKey: "9266c59563c84664", +} + +var AcceptResponse = transfers.Transfer{ + ID: "b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + Name: "", + VolumeID: "2f6f1684-1ded-40db-8a49-7c87dedbc758", + Links: []map[string]string{ + { + "href": "https://volume/v3/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "self", + }, + { + "href": "https://volume/53c2b94f63fb4f43a21b92d119ce549f/os-volume-transfer/b8913bfd-a4d3-4ec5-bd8b-fe2dbeef9f4f", + "rel": "bookmark", + }, + }, +} + +func HandleCreateTransfer(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer", 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("/os-volume-transfer/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) + fmt.Fprintf(w, AcceptTransferResponse) + }) +} + +func HandleDeleteTransfer(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer/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.StatusNoContent) + }) +} + +func HandleListTransfers(t *testing.T) { + th.Mux.HandleFunc("/os-volume-transfer/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("/os-volume-transfer/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/blockstorage/v3/transfers/testing/requests_test.go b/openstack/blockstorage/v3/transfers/testing/requests_test.go new file mode 100644 index 0000000000..43013a71ec --- /dev/null +++ b/openstack/blockstorage/v3/transfers/testing/requests_test.go @@ -0,0 +1,91 @@ +package testing + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/transfers" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestCreateTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateTransfer(t) + + actual, err := transfers.Create(context.TODO(), 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) + + actual, err := transfers.Accept(context.TODO(), client.ServiceClient(), TransferResponse.ID, AcceptRequest).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, AcceptResponse, *actual) +} + +func TestDeleteTransfer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteTransfer(t) + + err := transfers.Delete(context.TODO(), 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 := transfers.List(client.ServiceClient(), &transfers.ListOpts{AllTenants: true}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + count++ + + actual, err := transfers.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 := transfers.List(client.ServiceClient(), &transfers.ListOpts{AllTenants: true}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := transfers.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 := transfers.Get(context.TODO(), client.ServiceClient(), TransferResponse.ID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedResponse, *actual) +} diff --git a/openstack/blockstorage/v3/transfers/urls.go b/openstack/blockstorage/v3/transfers/urls.go new file mode 100644 index 0000000000..0366370794 --- /dev/null +++ b/openstack/blockstorage/v3/transfers/urls.go @@ -0,0 +1,23 @@ +package transfers + +import "github.com/gophercloud/gophercloud/v2" + +func transferURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-volume-transfer") +} + +func acceptURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id, "accept") +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id) +} + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("os-volume-transfer", "detail") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("os-volume-transfer", id) +} From 92dc16d89c66e5ca096f4b5f8a14ff31c9798286 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 14:25:27 +0000 Subject: [PATCH 1803/2296] blockstorage: Merge volumeactions extension This builds upon the '/volumes' resource so the code is forklifted into the 'openstack/volume/{v2,v3}/volumes' module. Signed-off-by: Stephen Finucane --- .../blockstorage/extensions/extensions.go | 324 ------------ .../extensions/volumeactions_test.go | 229 --------- .../openstack/blockstorage/v2/blockstorage.go | 250 ++++++++++ .../openstack/blockstorage/v2/volumes_test.go | 181 ++++++- .../openstack/blockstorage/v3/blockstorage.go | 311 ++++++++++++ .../openstack/blockstorage/v3/volumes_test.go | 212 ++++++++ .../extensions/volumeactions/doc.go | 108 ---- .../extensions/volumeactions/requests.go | 462 ------------------ .../extensions/volumeactions/results.go | 226 --------- .../extensions/volumeactions/testing/doc.go | 2 - .../volumeactions/testing/fixtures_test.go | 393 --------------- .../volumeactions/testing/requests_test.go | 243 --------- .../extensions/volumeactions/urls.go | 7 - openstack/blockstorage/v2/volumes/doc.go | 112 ++++- openstack/blockstorage/v2/volumes/requests.go | 455 +++++++++++++++++ openstack/blockstorage/v2/volumes/results.go | 218 +++++++++ .../blockstorage/v2/volumes/testing/doc.go | 2 +- .../v2/volumes/testing/fixtures_test.go | 383 +++++++++++++++ .../v2/volumes/testing/requests_test.go | 232 +++++++++ openstack/blockstorage/v2/volumes/urls.go | 4 + openstack/blockstorage/v3/volumes/doc.go | 102 ++++ openstack/blockstorage/v3/volumes/requests.go | 455 +++++++++++++++++ openstack/blockstorage/v3/volumes/results.go | 218 +++++++++ .../blockstorage/v3/volumes/testing/doc.go | 2 +- .../v3/volumes/testing/fixtures_test.go | 383 +++++++++++++++ .../v3/volumes/testing/requests_test.go | 232 +++++++++ openstack/blockstorage/v3/volumes/urls.go | 4 + 27 files changed, 3748 insertions(+), 2002 deletions(-) delete mode 100644 internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go delete mode 100644 openstack/blockstorage/extensions/volumeactions/doc.go delete mode 100644 openstack/blockstorage/extensions/volumeactions/requests.go delete mode 100644 openstack/blockstorage/extensions/volumeactions/results.go delete mode 100644 openstack/blockstorage/extensions/volumeactions/testing/doc.go delete mode 100644 openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go delete mode 100644 openstack/blockstorage/extensions/volumeactions/testing/requests_test.go delete mode 100644 openstack/blockstorage/extensions/volumeactions/urls.go diff --git a/internal/acceptance/openstack/blockstorage/extensions/extensions.go b/internal/acceptance/openstack/blockstorage/extensions/extensions.go index a6111d191a..27bb88251c 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/extensions.go +++ b/internal/acceptance/openstack/blockstorage/extensions/extensions.go @@ -2,327 +2,3 @@ // resources that are extensions of the block storage API. See the `*_test.go` // files for example usages. package extensions - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumeactions" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" -) - -// CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be -// returned -func CreateUploadImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (volumeactions.VolumeImage, error) { - if testing.Short() { - t.Skip("Skipping test that requires volume-backed image uploading in short mode.") - } - - imageName := tools.RandomString("ACPTTEST", 16) - uploadImageOpts := volumeactions.UploadImageOpts{ - ImageName: imageName, - Force: true, - } - - volumeImage, err := volumeactions.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() - if err != nil { - return volumeImage, err - } - - t.Logf("Uploading volume %s as volume-backed image %s", volume.ID, imageName) - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { - return volumeImage, err - } - - t.Logf("Uploaded volume %s as volume-backed image %s", volume.ID, imageName) - - return volumeImage, nil - -} - -// DeleteUploadedImage deletes uploaded image. An error will be returned -// if the deletion request failed. -func DeleteUploadedImage(t *testing.T, client *gophercloud.ServiceClient, imageID string) error { - if testing.Short() { - t.Skip("Skipping test that requires volume-backed image removing in short mode.") - } - - t.Logf("Removing image %s", imageID) - - err := images.Delete(context.TODO(), client, imageID).ExtractErr() - if err != nil { - return err - } - - return nil -} - -// CreateVolumeAttach will attach a volume to an instance. An error will be -// returned if the attachment failed. -func CreateVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, server *servers.Server) error { - if testing.Short() { - t.Skip("Skipping test that requires volume attachment in short mode.") - } - - attachOpts := volumeactions.AttachOpts{ - MountPoint: "/mnt", - Mode: "rw", - InstanceUUID: server.ID, - } - - t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) - - if err := volumeactions.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr(); err != nil { - return err - } - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - if err := volumes.WaitForStatus(ctx, client, volume.ID, "in-use"); err != nil { - return err - } - - t.Logf("Attached volume %s to server %s", volume.ID, server.ID) - - return nil -} - -// CreateVolumeReserve creates a volume reservation. An error will be returned -// if the reservation failed. -func CreateVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { - if testing.Short() { - t.Skip("Skipping test that requires volume reservation in short mode.") - } - - t.Logf("Attempting to reserve volume %s", volume.ID) - - if err := volumeactions.Reserve(context.TODO(), client, volume.ID).ExtractErr(); err != nil { - return err - } - - t.Logf("Reserved volume %s", volume.ID) - - return nil -} - -// DeleteVolumeAttach will detach a volume from an instance. A fatal error will -// occur if the snapshot failed to be deleted. This works best when used as a -// deferred function. -func DeleteVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { - t.Logf("Attepting to detach volume volume: %s", volume.ID) - - detachOpts := volumeactions.DetachOpts{ - AttachmentID: volume.Attachments[0].AttachmentID, - } - - if err := volumeactions.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr(); err != nil { - t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) - } - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { - t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) - } - - t.Logf("Detached volume: %s", volume.ID) -} - -// DeleteVolumeReserve deletes a volume reservation. A fatal error will occur -// if the deletion request failed. This works best when used as a deferred -// function. -func DeleteVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { - if testing.Short() { - t.Skip("Skipping test that requires volume reservation in short mode.") - } - - t.Logf("Attempting to unreserve volume %s", volume.ID) - - if err := volumeactions.Unreserve(context.TODO(), client, volume.ID).ExtractErr(); err != nil { - t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) - } - - t.Logf("Unreserved volume %s", volume.ID) -} - -// ExtendVolumeSize will extend the size of a volume. -func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { - t.Logf("Attempting to extend the size of volume %s", volume.ID) - - extendOpts := volumeactions.ExtendSizeOpts{ - NewSize: 2, - } - - err := volumeactions.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() - if err != nil { - return err - } - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { - return err - } - - return nil -} - -// SetImageMetadata will apply the metadata to a volume. -func SetImageMetadata(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { - t.Logf("Attempting to apply image metadata to volume %s", volume.ID) - - imageMetadataOpts := volumeactions.ImageMetadataOpts{ - Metadata: map[string]string{ - "image_name": "testimage", - }, - } - - err := volumeactions.SetImageMetadata(context.TODO(), client, volume.ID, imageMetadataOpts).ExtractErr() - if err != nil { - return err - } - - return nil -} - -// SetBootable will set a bootable status to a volume. -func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { - t.Logf("Attempting to apply bootable status to volume %s", volume.ID) - - bootableOpts := volumeactions.BootableOpts{ - Bootable: true, - } - - err := volumeactions.SetBootable(context.TODO(), client, volume.ID, bootableOpts).ExtractErr() - if err != nil { - return err - } - - vol, err := volumes.Get(context.TODO(), client, volume.ID).Extract() - if err != nil { - return err - } - - if strings.ToLower(vol.Bootable) != "true" { - return fmt.Errorf("Volume bootable status is %q, expected 'true'", vol.Bootable) - } - - bootableOpts = volumeactions.BootableOpts{ - Bootable: false, - } - - err = volumeactions.SetBootable(context.TODO(), client, volume.ID, bootableOpts).ExtractErr() - if err != nil { - return err - } - - vol, err = volumes.Get(context.TODO(), client, volume.ID).Extract() - if err != nil { - return err - } - - if strings.ToLower(vol.Bootable) == "true" { - return fmt.Errorf("Volume bootable status is %q, expected 'false'", vol.Bootable) - } - - return nil -} - -// ChangeVolumeType will extend the size of a volume. -func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, vt *volumetypes.VolumeType) error { - t.Logf("Attempting to change the type of volume %s from %s to %s", volume.ID, volume.VolumeType, vt.Name) - - changeOpts := volumeactions.ChangeTypeOpts{ - NewType: vt.Name, - MigrationPolicy: volumeactions.MigrationPolicyOnDemand, - } - - err := volumeactions.ChangeType(context.TODO(), client, volume.ID, changeOpts).ExtractErr() - if err != nil { - return err - } - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { - return err - } - - return nil -} - -// ResetVolumeStatus will reset the status of a volume. -func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.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(context.TODO(), client, volume.ID, resetOpts).ExtractErr() - if err != nil { - return err - } - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - if err := volumes.WaitForStatus(ctx, client, volume.ID, status); 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) - - reimageOpts := volumeactions.ReImageOpts{ - ImageID: imageID, - ReImageReserved: false, - } - - err := volumeactions.ReImage(context.TODO(), client, volume.ID, reimageOpts).ExtractErr() - if err != nil { - return err - } - - ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) - defer cancel() - - err = volumes.WaitForStatus(ctx, client, volume.ID, "available") - if err != nil { - return err - } - - vol, err := volumes.Get(context.TODO(), 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/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go b/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go deleted file mode 100644 index 198f113de5..0000000000 --- a/internal/acceptance/openstack/blockstorage/extensions/volumeactions_test.go +++ /dev/null @@ -1,229 +0,0 @@ -//go:build acceptance || blockstorage -// +build acceptance blockstorage - -package extensions - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v2" - blockstorageV3 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v3" - compute "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/compute/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestVolumeActionsUploadImageDestroy(t *testing.T) { - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - computeClient, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - volume, err := blockstorage.CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) - - volumeImage, err := CreateUploadImage(t, blockClient, volume) - th.AssertNoErr(t, err) - - tools.PrintResource(t, volumeImage) - - err = DeleteUploadedImage(t, computeClient, volumeImage.ImageID) - th.AssertNoErr(t, err) -} - -func TestVolumeActionsAttachCreateDestroy(t *testing.T) { - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - computeClient, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - server, err := compute.CreateServer(t, computeClient) - th.AssertNoErr(t, err) - defer compute.DeleteServer(t, computeClient, server) - - volume, err := blockstorage.CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) - - err = CreateVolumeAttach(t, blockClient, volume, server) - th.AssertNoErr(t, err) - - newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() - th.AssertNoErr(t, err) - - DeleteVolumeAttach(t, blockClient, newVolume) -} - -func TestVolumeActionsReserveUnreserve(t *testing.T) { - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - volume, err := blockstorage.CreateVolume(t, client) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, client, volume) - - err = CreateVolumeReserve(t, client, volume) - th.AssertNoErr(t, err) - defer DeleteVolumeReserve(t, client, volume) -} - -func TestVolumeActionsExtendSize(t *testing.T) { - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - volume, err := blockstorage.CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) - - tools.PrintResource(t, volume) - - err = ExtendVolumeSize(t, blockClient, volume) - th.AssertNoErr(t, err) - - newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newVolume) -} - -func TestVolumeActionsImageMetadata(t *testing.T) { - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - volume, err := blockstorage.CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) - - err = SetImageMetadata(t, blockClient, volume) - th.AssertNoErr(t, err) -} - -func TestVolumeActionsSetBootable(t *testing.T) { - blockClient, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - volume, err := blockstorage.CreateVolume(t, blockClient) - th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, blockClient, volume) - - err = SetBootable(t, blockClient, volume) - th.AssertNoErr(t, err) -} - -func TestVolumeActionsChangeType(t *testing.T) { - // clients.RequireAdmin(t) - - client, err := clients.NewBlockStorageV3Client() - th.AssertNoErr(t, err) - - volumeType1, err := blockstorageV3.CreateVolumeTypeNoExtraSpecs(t, client) - th.AssertNoErr(t, err) - defer blockstorageV3.DeleteVolumeType(t, client, volumeType1) - - volumeType2, err := blockstorageV3.CreateVolumeTypeNoExtraSpecs(t, client) - th.AssertNoErr(t, err) - defer blockstorageV3.DeleteVolumeType(t, client, volumeType2) - - volume, err := blockstorageV3.CreateVolumeWithType(t, client, volumeType1) - th.AssertNoErr(t, err) - defer blockstorageV3.DeleteVolume(t, client, volume) - - tools.PrintResource(t, volume) - - err = ChangeVolumeType(t, client, volume, volumeType2) - th.AssertNoErr(t, err) - - newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, newVolume.VolumeType, volumeType2.Name) - - 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") - - 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. -/* -func TestVolumeConns(t *testing.T) { - client, err := newClient() - th.AssertNoErr(t, err) - - t.Logf("Creating volume") - cv, err := volumes.Create(context.TODO(), client, &volumes.CreateOpts{ - Size: 1, - Name: "blockv2-volume", - }).Extract() - th.AssertNoErr(t, err) - - defer func() { - err = volumes.WaitForStatus(context.TODO(), client, cv.ID, "available", 60) - th.AssertNoErr(t, err) - - t.Logf("Deleting volume") - err = volumes.Delete(context.TODO(), client, cv.ID, volumes.DeleteOpts{}).ExtractErr() - th.AssertNoErr(t, err) - }() - - err = volumes.WaitForStatus(context.TODO(), client, cv.ID, "available", 60) - th.AssertNoErr(t, err) - - connOpts := &volumeactions.ConnectorOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: false, - Platform: "x86_64", - OSType: "linux2", - } - - t.Logf("Initializing connection") - _, err = volumeactions.InitializeConnection(client, cv.ID, connOpts).Extract() - th.AssertNoErr(t, err) - - t.Logf("Terminating connection") - err = volumeactions.TerminateConnection(client, cv.ID, connOpts).ExtractErr() - th.AssertNoErr(t, err) -} -*/ diff --git a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go index 8d2b9e31fc..1cae786437 100644 --- a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -5,6 +5,8 @@ package v2 import ( "context" + "fmt" + "strings" "testing" "time" @@ -14,6 +16,8 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/backups" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -249,3 +253,249 @@ func ResetBackupStatus(t *testing.T, client *gophercloud.ServiceClient, backup * return WaitForBackupStatus(client, backup.ID, status) } + +// CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be +// returned +func CreateUploadImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (volumes.VolumeImage, error) { + if testing.Short() { + t.Skip("Skipping test that requires volume-backed image uploading in short mode.") + } + + imageName := tools.RandomString("ACPTTEST", 16) + uploadImageOpts := volumes.UploadImageOpts{ + ImageName: imageName, + Force: true, + } + + volumeImage, err := volumes.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() + if err != nil { + return volumeImage, err + } + + t.Logf("Uploading volume %s as volume-backed image %s", volume.ID, imageName) + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { + return volumeImage, err + } + + t.Logf("Uploaded volume %s as volume-backed image %s", volume.ID, imageName) + + return volumeImage, nil + +} + +// DeleteUploadedImage deletes uploaded image. An error will be returned +// if the deletion request failed. +func DeleteUploadedImage(t *testing.T, client *gophercloud.ServiceClient, imageID string) error { + if testing.Short() { + t.Skip("Skipping test that requires volume-backed image removing in short mode.") + } + + t.Logf("Removing image %s", imageID) + + err := images.Delete(context.TODO(), client, imageID).ExtractErr() + if err != nil { + return err + } + + return nil +} + +// CreateVolumeAttach will attach a volume to an instance. An error will be +// returned if the attachment failed. +func CreateVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, server *servers.Server) error { + if testing.Short() { + t.Skip("Skipping test that requires volume attachment in short mode.") + } + + attachOpts := volumes.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: server.ID, + } + + t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) + + if err := volumes.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr(); err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "in-use"); err != nil { + return err + } + + t.Logf("Attached volume %s to server %s", volume.ID, server.ID) + + return nil +} + +// CreateVolumeReserve creates a volume reservation. An error will be returned +// if the reservation failed. +func CreateVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + if testing.Short() { + t.Skip("Skipping test that requires volume reservation in short mode.") + } + + t.Logf("Attempting to reserve volume %s", volume.ID) + + if err := volumes.Reserve(context.TODO(), client, volume.ID).ExtractErr(); err != nil { + return err + } + + t.Logf("Reserved volume %s", volume.ID) + + return nil +} + +// DeleteVolumeAttach will detach a volume from an instance. A fatal error will +// occur if the snapshot failed to be deleted. This works best when used as a +// deferred function. +func DeleteVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { + t.Logf("Attepting to detach volume volume: %s", volume.ID) + + detachOpts := volumes.DetachOpts{ + AttachmentID: volume.Attachments[0].AttachmentID, + } + + if err := volumes.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr(); err != nil { + t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { + t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) + } + + t.Logf("Detached volume: %s", volume.ID) +} + +// DeleteVolumeReserve deletes a volume reservation. A fatal error will occur +// if the deletion request failed. This works best when used as a deferred +// function. +func DeleteVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { + if testing.Short() { + t.Skip("Skipping test that requires volume reservation in short mode.") + } + + t.Logf("Attempting to unreserve volume %s", volume.ID) + + if err := volumes.Unreserve(context.TODO(), client, volume.ID).ExtractErr(); err != nil { + t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) + } + + t.Logf("Unreserved volume %s", volume.ID) +} + +// ExtendVolumeSize will extend the size of a volume. +func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to extend the size of volume %s", volume.ID) + + extendOpts := volumes.ExtendSizeOpts{ + NewSize: 2, + } + + err := volumes.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { + return err + } + + return nil +} + +// SetImageMetadata will apply the metadata to a volume. +func SetImageMetadata(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to apply image metadata to volume %s", volume.ID) + + imageMetadataOpts := volumes.ImageMetadataOpts{ + Metadata: map[string]string{ + "image_name": "testimage", + }, + } + + err := volumes.SetImageMetadata(context.TODO(), client, volume.ID, imageMetadataOpts).ExtractErr() + if err != nil { + return err + } + + return nil +} + +// SetBootable will set a bootable status to a volume. +func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to apply bootable status to volume %s", volume.ID) + + bootableOpts := volumes.BootableOpts{ + Bootable: true, + } + + err := volumes.SetBootable(context.TODO(), client, volume.ID, bootableOpts).ExtractErr() + if err != nil { + return err + } + + vol, err := volumes.Get(context.TODO(), client, volume.ID).Extract() + if err != nil { + return err + } + + if strings.ToLower(vol.Bootable) != "true" { + return fmt.Errorf("Volume bootable status is %q, expected 'true'", vol.Bootable) + } + + bootableOpts = volumes.BootableOpts{ + Bootable: false, + } + + err = volumes.SetBootable(context.TODO(), client, volume.ID, bootableOpts).ExtractErr() + if err != nil { + return err + } + + vol, err = volumes.Get(context.TODO(), client, volume.ID).Extract() + if err != nil { + return err + } + + if strings.ToLower(vol.Bootable) == "true" { + return fmt.Errorf("Volume bootable status is %q, expected 'false'", vol.Bootable) + } + + return nil +} + +// ResetVolumeStatus will reset the status of a volume. +func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, status string) error { + t.Logf("Attempting to reset the status of volume %s from %s to %s", volume.ID, volume.Status, status) + + resetOpts := volumes.ResetStatusOpts{ + Status: status, + } + err := volumes.ResetStatus(context.TODO(), client, volume.ID, resetOpts).ExtractErr() + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, status); err != nil { + return err + } + + return nil +} diff --git a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go index e669ade5dc..450e8e6158 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -9,8 +9,8 @@ import ( "time" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + compute "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/compute/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -76,7 +76,7 @@ func TestVolumesCreateForceDestroy(t *testing.T) { tools.PrintResource(t, newVolume) - err = volumeactions.ForceDelete(context.TODO(), client, newVolume.ID).ExtractErr() + err = volumes.ForceDelete(context.TODO(), client, newVolume.ID).ExtractErr() th.AssertNoErr(t, err) } @@ -132,5 +132,182 @@ func TestVolumesCascadeDelete(t *testing.T) { th.AssertNoErr(t, err) t.Logf("Successfully deleted volume: %s", vol.ID) +} + +func TestVolumeActionsUploadImageDestroy(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + volumeImage, err := CreateUploadImage(t, blockClient, volume) + th.AssertNoErr(t, err) + + tools.PrintResource(t, volumeImage) + + err = DeleteUploadedImage(t, computeClient, volumeImage.ImageID) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsAttachCreateDestroy(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := compute.CreateServer(t, computeClient) + th.AssertNoErr(t, err) + defer compute.DeleteServer(t, computeClient, server) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + err = CreateVolumeAttach(t, blockClient, volume, server) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() + th.AssertNoErr(t, err) + + DeleteVolumeAttach(t, blockClient, newVolume) +} + +func TestVolumeActionsReserveUnreserve(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + client, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume) + + err = CreateVolumeReserve(t, client, volume) + th.AssertNoErr(t, err) + defer DeleteVolumeReserve(t, client, volume) +} + +func TestVolumeActionsExtendSize(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + tools.PrintResource(t, volume) + + err = ExtendVolumeSize(t, blockClient, volume) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newVolume) +} + +func TestVolumeActionsImageMetadata(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + err = SetImageMetadata(t, blockClient, volume) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsSetBootable(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + blockClient, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + err = SetBootable(t, blockClient, volume) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsResetStatus(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + client, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer 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) +} +// Note(jtopjian): I plan to work on this at some point, but it requires +// setting up a server with iscsi utils. +/* +func TestVolumeConns(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + + client, err := newClient() + th.AssertNoErr(t, err) + + t.Logf("Creating volume") + cv, err := volumes.Create(client, &volumes.CreateOpts{ + Size: 1, + Name: "blockv2-volume", + }).Extract() + th.AssertNoErr(t, err) + + defer func() { + err = volumes.WaitForStatus(client, cv.ID, "available", 60) + th.AssertNoErr(t, err) + + t.Logf("Deleting volume") + err = volumes.Delete(client, cv.ID, volumes.DeleteOpts{}).ExtractErr() + th.AssertNoErr(t, err) + }() + + err = volumes.WaitForStatus(client, cv.ID, "available", 60) + th.AssertNoErr(t, err) + + connOpts := &volumes.ConnectorOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: false, + Platform: "x86_64", + OSType: "linux2", + } + + t.Logf("Initializing connection") + _, err = volumes.InitializeConnection(client, cv.ID, connOpts).Extract() + th.AssertNoErr(t, err) + + t.Logf("Terminating connection") + err = volumes.TerminateConnection(client, cv.ID, connOpts).ExtractErr() + th.AssertNoErr(t, err) } +*/ diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 0b2cbdea6e..94c4489951 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -5,6 +5,8 @@ package v3 import ( "context" + "fmt" + "strings" "testing" "time" @@ -15,6 +17,8 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -467,3 +471,310 @@ func ResetBackupStatus(t *testing.T, client *gophercloud.ServiceClient, backup * return WaitForBackupStatus(client, backup.ID, status) } + +// CreateUploadImage will upload volume it as volume-baked image. An name of new image or err will be +// returned +func CreateUploadImage(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (volumes.VolumeImage, error) { + if testing.Short() { + t.Skip("Skipping test that requires volume-backed image uploading in short mode.") + } + + imageName := tools.RandomString("ACPTTEST", 16) + uploadImageOpts := volumes.UploadImageOpts{ + ImageName: imageName, + Force: true, + } + + volumeImage, err := volumes.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() + if err != nil { + return volumeImage, err + } + + t.Logf("Uploading volume %s as volume-backed image %s", volume.ID, imageName) + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { + return volumeImage, err + } + + t.Logf("Uploaded volume %s as volume-backed image %s", volume.ID, imageName) + + return volumeImage, nil +} + +// DeleteUploadedImage deletes uploaded image. An error will be returned +// if the deletion request failed. +func DeleteUploadedImage(t *testing.T, client *gophercloud.ServiceClient, imageID string) error { + if testing.Short() { + t.Skip("Skipping test that requires volume-backed image removing in short mode.") + } + + t.Logf("Removing image %s", imageID) + + err := images.Delete(context.TODO(), client, imageID).ExtractErr() + if err != nil { + return err + } + + return nil +} + +// CreateVolumeAttach will attach a volume to an instance. An error will be +// returned if the attachment failed. +func CreateVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, server *servers.Server) error { + if testing.Short() { + t.Skip("Skipping test that requires volume attachment in short mode.") + } + + attachOpts := volumes.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: server.ID, + } + + t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID) + + if err := volumes.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr(); err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "in-use"); err != nil { + return err + } + + t.Logf("Attached volume %s to server %s", volume.ID, server.ID) + + return nil +} + +// CreateVolumeReserve creates a volume reservation. An error will be returned +// if the reservation failed. +func CreateVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + if testing.Short() { + t.Skip("Skipping test that requires volume reservation in short mode.") + } + + t.Logf("Attempting to reserve volume %s", volume.ID) + + if err := volumes.Reserve(context.TODO(), client, volume.ID).ExtractErr(); err != nil { + return err + } + + t.Logf("Reserved volume %s", volume.ID) + + return nil +} + +// DeleteVolumeAttach will detach a volume from an instance. A fatal error will +// occur if the snapshot failed to be deleted. This works best when used as a +// deferred function. +func DeleteVolumeAttach(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { + t.Logf("Attepting to detach volume volume: %s", volume.ID) + + detachOpts := volumes.DetachOpts{ + AttachmentID: volume.Attachments[0].AttachmentID, + } + + if err := volumes.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr(); err != nil { + t.Fatalf("Unable to detach volume %s: %v", volume.ID, err) + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { + t.Fatalf("Volume %s failed to become unavailable in 60 seconds: %v", volume.ID, err) + } + + t.Logf("Detached volume: %s", volume.ID) +} + +// DeleteVolumeReserve deletes a volume reservation. A fatal error will occur +// if the deletion request failed. This works best when used as a deferred +// function. +func DeleteVolumeReserve(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) { + if testing.Short() { + t.Skip("Skipping test that requires volume reservation in short mode.") + } + + t.Logf("Attempting to unreserve volume %s", volume.ID) + + if err := volumes.Unreserve(context.TODO(), client, volume.ID).ExtractErr(); err != nil { + t.Fatalf("Unable to unreserve volume %s: %v", volume.ID, err) + } + + t.Logf("Unreserved volume %s", volume.ID) +} + +// ExtendVolumeSize will extend the size of a volume. +func ExtendVolumeSize(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to extend the size of volume %s", volume.ID) + + extendOpts := volumes.ExtendSizeOpts{ + NewSize: 2, + } + + err := volumes.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { + return err + } + + return nil +} + +// SetImageMetadata will apply the metadata to a volume. +func SetImageMetadata(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to apply image metadata to volume %s", volume.ID) + + imageMetadataOpts := volumes.ImageMetadataOpts{ + Metadata: map[string]string{ + "image_name": "testimage", + }, + } + + err := volumes.SetImageMetadata(context.TODO(), client, volume.ID, imageMetadataOpts).ExtractErr() + if err != nil { + return err + } + + return nil +} + +// SetBootable will set a bootable status to a volume. +func SetBootable(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) error { + t.Logf("Attempting to apply bootable status to volume %s", volume.ID) + + bootableOpts := volumes.BootableOpts{ + Bootable: true, + } + + err := volumes.SetBootable(context.TODO(), client, volume.ID, bootableOpts).ExtractErr() + if err != nil { + return err + } + + vol, err := volumes.Get(context.TODO(), client, volume.ID).Extract() + if err != nil { + return err + } + + if strings.ToLower(vol.Bootable) != "true" { + return fmt.Errorf("Volume bootable status is %q, expected 'true'", vol.Bootable) + } + + bootableOpts = volumes.BootableOpts{ + Bootable: false, + } + + err = volumes.SetBootable(context.TODO(), client, volume.ID, bootableOpts).ExtractErr() + if err != nil { + return err + } + + vol, err = volumes.Get(context.TODO(), client, volume.ID).Extract() + if err != nil { + return err + } + + if strings.ToLower(vol.Bootable) == "true" { + return fmt.Errorf("Volume bootable status is %q, expected 'false'", vol.Bootable) + } + + return nil +} + +// ChangeVolumeType will extend the size of a volume. +func ChangeVolumeType(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, vt *volumetypes.VolumeType) error { + t.Logf("Attempting to change the type of volume %s from %s to %s", volume.ID, volume.VolumeType, vt.Name) + + changeOpts := volumes.ChangeTypeOpts{ + NewType: vt.Name, + MigrationPolicy: volumes.MigrationPolicyOnDemand, + } + + err := volumes.ChangeType(context.TODO(), client, volume.ID, changeOpts).ExtractErr() + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, "available"); err != nil { + return err + } + + return nil +} + +// ResetVolumeStatus will reset the status of a volume. +func ResetVolumeStatus(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume, status string) error { + t.Logf("Attempting to reset the status of volume %s from %s to %s", volume.ID, volume.Status, status) + + resetOpts := volumes.ResetStatusOpts{ + Status: status, + } + err := volumes.ResetStatus(context.TODO(), client, volume.ID, resetOpts).ExtractErr() + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + if err := volumes.WaitForStatus(ctx, client, volume.ID, status); 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) + + reimageOpts := volumes.ReImageOpts{ + ImageID: imageID, + ReImageReserved: false, + } + + err := volumes.ReImage(context.TODO(), client, volume.ID, reimageOpts).ExtractErr() + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume.ID, "available") + if err != nil { + return err + } + + vol, err := volumes.Get(context.TODO(), 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/internal/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go index cb2da7ff97..6fda94a3aa 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + compute "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/compute/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" @@ -152,5 +153,216 @@ func TestVolumesCascadeDelete(t *testing.T) { th.AssertNoErr(t, err) t.Logf("Successfully deleted volume: %s", vol.ID) +} + +func TestVolumeActionsUploadImageDestroy(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + volumeImage, err := CreateUploadImage(t, blockClient, volume) + th.AssertNoErr(t, err) + + tools.PrintResource(t, volumeImage) + + err = DeleteUploadedImage(t, computeClient, volumeImage.ImageID) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsAttachCreateDestroy(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + computeClient, err := clients.NewComputeV2Client() + th.AssertNoErr(t, err) + + server, err := compute.CreateServer(t, computeClient) + th.AssertNoErr(t, err) + defer compute.DeleteServer(t, computeClient, server) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + err = CreateVolumeAttach(t, blockClient, volume, server) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() + th.AssertNoErr(t, err) + + DeleteVolumeAttach(t, blockClient, newVolume) +} + +func TestVolumeActionsReserveUnreserve(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume) + + err = CreateVolumeReserve(t, client, volume) + th.AssertNoErr(t, err) + defer DeleteVolumeReserve(t, client, volume) +} + +func TestVolumeActionsExtendSize(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + tools.PrintResource(t, volume) + + err = ExtendVolumeSize(t, blockClient, volume) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(context.TODO(), blockClient, volume.ID).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, newVolume) +} + +func TestVolumeActionsImageMetadata(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + err = SetImageMetadata(t, blockClient, volume) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsSetBootable(t *testing.T) { + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer DeleteVolume(t, blockClient, volume) + + err = SetBootable(t, blockClient, volume) + th.AssertNoErr(t, err) +} + +func TestVolumeActionsChangeType(t *testing.T) { + // clients.RequireAdmin(t) + + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volumeType1, err := CreateVolumeTypeNoExtraSpecs(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, volumeType1) + + volumeType2, err := CreateVolumeTypeNoExtraSpecs(t, client) + th.AssertNoErr(t, err) + defer DeleteVolumeType(t, client, volumeType2) + + volume, err := CreateVolumeWithType(t, client, volumeType1) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume) + + tools.PrintResource(t, volume) + + err = ChangeVolumeType(t, client, volume, volumeType2) + th.AssertNoErr(t, err) + + newVolume, err := volumes.Get(context.TODO(), client, volume.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newVolume.VolumeType, volumeType2.Name) + + tools.PrintResource(t, newVolume) +} + +func TestVolumeActionsResetStatus(t *testing.T) { + client, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + + volume, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer 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") + + choices, err := clients.AcceptanceTestChoicesFromEnv() + if err != nil { + t.Fatal(err) + } + + blockClient, err := clients.NewBlockStorageV3Client() + th.AssertNoErr(t, err) + blockClient.Microversion = "3.68" + + volume, err := CreateVolume(t, blockClient) + th.AssertNoErr(t, err) + defer 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. +/* +func TestVolumeConns(t *testing.T) { + client, err := newClient() + th.AssertNoErr(t, err) + + t.Logf("Creating volume") + cv, err := volumes.Create(context.TODO(), client, &volumes.CreateOpts{ + Size: 1, + Name: "blockv2-volume", + }).Extract() + th.AssertNoErr(t, err) + + defer func() { + err = volumes.WaitForStatus(context.TODO(), client, cv.ID, "available", 60) + th.AssertNoErr(t, err) + + t.Logf("Deleting volume") + err = volumes.Delete(context.TODO(), client, cv.ID, volumes.DeleteOpts{}).ExtractErr() + th.AssertNoErr(t, err) + }() + + err = volumes.WaitForStatus(context.TODO(), client, cv.ID, "available", 60) + th.AssertNoErr(t, err) + + connOpts := &ConnectorOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: false, + Platform: "x86_64", + OSType: "linux2", + } + + t.Logf("Initializing connection") + _, err = InitializeConnection(client, cv.ID, connOpts).Extract() + th.AssertNoErr(t, err) + + t.Logf("Terminating connection") + err = TerminateConnection(client, cv.ID, connOpts).ExtractErr() + th.AssertNoErr(t, err) } +*/ diff --git a/openstack/blockstorage/extensions/volumeactions/doc.go b/openstack/blockstorage/extensions/volumeactions/doc.go deleted file mode 100644 index 8d9d3f3b67..0000000000 --- a/openstack/blockstorage/extensions/volumeactions/doc.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Package volumeactions provides information and interaction with volumes in the -OpenStack Block Storage service. A volume is a detachable block storage -device, akin to a USB hard drive. - -Example of Attaching a Volume to an Instance - - attachOpts := volumeactions.AttachOpts{ - MountPoint: "/mnt", - Mode: "rw", - InstanceUUID: server.ID, - } - - err := volumeactions.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr() - if err != nil { - panic(err) - } - - detachOpts := volumeactions.DetachOpts{ - AttachmentID: volume.Attachments[0].AttachmentID, - } - - err = volumeactions.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example of Creating an Image from a Volume - - uploadImageOpts := volumeactions.UploadImageOpts{ - ImageName: "my_vol", - Force: true, - } - - volumeImage, err := volumeactions.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", volumeImage) - -Example of Extending a Volume's Size - - extendOpts := volumeactions.ExtendSizeOpts{ - NewSize: 100, - } - - err := volumeactions.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example of Initializing a Volume Connection - - connectOpts := &volumeactions.InitializeConnectionOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: gophercloud.Disabled, - Platform: "x86_64", - OSType: "linux2", - } - - connectionInfo, err := volumeactions.InitializeConnection(context.TODO(), client, volume.ID, connectOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", connectionInfo["data"]) - - terminateOpts := &volumeactions.InitializeConnectionOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: gophercloud.Disabled, - Platform: "x86_64", - OSType: "linux2", - } - - err = volumeactions.TerminateConnection(context.TODO(), client, volume.ID, terminateOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example of Setting a Volume's Bootable status - - options := volumeactions.BootableOpts{ - Bootable: true, - } - - err := volumeactions.SetBootable(context.TODO(), client, volume.ID, options).ExtractErr() - if err != nil { - panic(err) - } - -Example of Changing Type of a Volume - - changeTypeOpts := volumeactions.ChangeTypeOpts{ - NewType: "ssd", - MigrationPolicy: volumeactions.MigrationPolicyOnDemand, - } - - err = volumeactions.ChangeType(context.TODO(), client, volumeID, changeTypeOpts).ExtractErr() - if err != nil { - panic(err) - } -*/ -package volumeactions diff --git a/openstack/blockstorage/extensions/volumeactions/requests.go b/openstack/blockstorage/extensions/volumeactions/requests.go deleted file mode 100644 index a435e6c754..0000000000 --- a/openstack/blockstorage/extensions/volumeactions/requests.go +++ /dev/null @@ -1,462 +0,0 @@ -package volumeactions - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" -) - -// AttachOptsBuilder allows extensions to add additional parameters to the -// Attach request. -type AttachOptsBuilder interface { - ToVolumeAttachMap() (map[string]interface{}, error) -} - -// AttachMode describes the attachment mode for volumes. -type AttachMode string - -// These constants determine how a volume is attached. -const ( - ReadOnly AttachMode = "ro" - ReadWrite AttachMode = "rw" -) - -// AttachOpts contains options for attaching a Volume. -type AttachOpts struct { - // The mountpoint of this volume. - MountPoint string `json:"mountpoint,omitempty"` - - // The nova instance ID, can't set simultaneously with HostName. - InstanceUUID string `json:"instance_uuid,omitempty"` - - // The hostname of baremetal host, can't set simultaneously with InstanceUUID. - HostName string `json:"host_name,omitempty"` - - // Mount mode of this volume. - Mode AttachMode `json:"mode,omitempty"` -} - -// ToVolumeAttachMap assembles a request body based on the contents of a -// AttachOpts. -func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-attach") -} - -// Attach will attach a volume based on the values in AttachOpts. -func Attach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { - b, err := opts.ToVolumeAttachMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// BeginDetaching will mark the volume as detaching. -func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { - b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// DetachOptsBuilder allows extensions to add additional parameters to the -// Detach request. -type DetachOptsBuilder interface { - ToVolumeDetachMap() (map[string]interface{}, error) -} - -// DetachOpts contains options for detaching a Volume. -type DetachOpts struct { - // AttachmentID is the ID of the attachment between a volume and instance. - AttachmentID string `json:"attachment_id,omitempty"` -} - -// ToVolumeDetachMap assembles a request body based on the contents of a -// DetachOpts. -func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-detach") -} - -// Detach will detach a volume based on volume ID. -func Detach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { - b, err := opts.ToVolumeDetachMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Reserve will reserve a volume based on volume ID. -func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ReserveResult) { - b := map[string]interface{}{"os-reserve": make(map[string]interface{})} - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Unreserve will unreserve a volume based on volume ID. -func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnreserveResult) { - b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// InitializeConnectionOptsBuilder allows extensions to add additional parameters to the -// InitializeConnection request. -type InitializeConnectionOptsBuilder interface { - ToVolumeInitializeConnectionMap() (map[string]interface{}, error) -} - -// InitializeConnectionOpts hosts options for InitializeConnection. -// The fields are specific to the storage driver in use and the destination -// attachment. -type InitializeConnectionOpts struct { - IP string `json:"ip,omitempty"` - Host string `json:"host,omitempty"` - Initiator string `json:"initiator,omitempty"` - Wwpns []string `json:"wwpns,omitempty"` - Wwnns string `json:"wwnns,omitempty"` - Multipath *bool `json:"multipath,omitempty"` - Platform string `json:"platform,omitempty"` - OSType string `json:"os_type,omitempty"` -} - -// ToVolumeInitializeConnectionMap assembles a request body based on the contents of a -// InitializeConnectionOpts. -func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-initialize_connection": b}, err -} - -// InitializeConnection initializes an iSCSI connection by volume ID. -func InitializeConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { - b, err := opts.ToVolumeInitializeConnectionMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// TerminateConnectionOptsBuilder allows extensions to add additional parameters to the -// TerminateConnection request. -type TerminateConnectionOptsBuilder interface { - ToVolumeTerminateConnectionMap() (map[string]interface{}, error) -} - -// TerminateConnectionOpts hosts options for TerminateConnection. -type TerminateConnectionOpts struct { - IP string `json:"ip,omitempty"` - Host string `json:"host,omitempty"` - Initiator string `json:"initiator,omitempty"` - Wwpns []string `json:"wwpns,omitempty"` - Wwnns string `json:"wwnns,omitempty"` - Multipath *bool `json:"multipath,omitempty"` - Platform string `json:"platform,omitempty"` - OSType string `json:"os_type,omitempty"` -} - -// ToVolumeTerminateConnectionMap assembles a request body based on the contents of a -// TerminateConnectionOpts. -func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-terminate_connection": b}, err -} - -// TerminateConnection terminates an iSCSI connection by volume ID. -func TerminateConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { - b, err := opts.ToVolumeTerminateConnectionMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ExtendSizeOptsBuilder allows extensions to add additional parameters to the -// ExtendSize request. -type ExtendSizeOptsBuilder interface { - ToVolumeExtendSizeMap() (map[string]interface{}, error) -} - -// ExtendSizeOpts contains options for extending the size of an existing Volume. -// This object is passed to the volumes.ExtendSize function. -type ExtendSizeOpts struct { - // NewSize is the new size of the volume, in GB. - NewSize int `json:"new_size" required:"true"` -} - -// ToVolumeExtendSizeMap assembles a request body based on the contents of an -// ExtendSizeOpts. -func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-extend") -} - -// ExtendSize will extend the size of the volume based on the provided information. -// This operation does not return a response body. -func ExtendSize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { - b, err := opts.ToVolumeExtendSizeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UploadImageOptsBuilder allows extensions to add additional parameters to the -// UploadImage request. -type UploadImageOptsBuilder interface { - ToVolumeUploadImageMap() (map[string]interface{}, error) -} - -// UploadImageOpts contains options for uploading a Volume to image storage. -type UploadImageOpts struct { - // Container format, may be bare, ofv, ova, etc. - ContainerFormat string `json:"container_format,omitempty"` - - // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. - DiskFormat string `json:"disk_format,omitempty"` - - // The name of image that will be stored in glance. - ImageName string `json:"image_name,omitempty"` - - // Force image creation, usable if volume attached to instance. - Force bool `json:"force,omitempty"` - - // Visibility defines who can see/use the image. - // supported since 3.1 microversion - Visibility string `json:"visibility,omitempty"` - - // whether the image is not deletable. - // supported since 3.1 microversion - Protected bool `json:"protected,omitempty"` -} - -// ToVolumeUploadImageMap assembles a request body based on the contents of a -// UploadImageOpts. -func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") -} - -// UploadImage will upload an image based on the values in UploadImageOptsBuilder. -func UploadImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { - b, err := opts.ToVolumeUploadImageMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ForceDelete will delete the volume regardless of state. -func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ImageMetadataOptsBuilder allows extensions to add additional parameters to the -// ImageMetadataRequest request. -type ImageMetadataOptsBuilder interface { - ToImageMetadataMap() (map[string]interface{}, error) -} - -// ImageMetadataOpts contains options for setting image metadata to a volume. -type ImageMetadataOpts struct { - // The image metadata to add to the volume as a set of metadata key and value pairs. - Metadata map[string]string `json:"metadata"` -} - -// ToImageMetadataMap assembles a request body based on the contents of a -// ImageMetadataOpts. -func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") -} - -// SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. -func SetImageMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { - b, err := opts.ToImageMetadataMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// BootableOpts contains options for setting bootable status to a volume. -type BootableOpts struct { - // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. - Bootable bool `json:"bootable"` -} - -// ToBootableMap assembles a request body based on the contents of a -// BootableOpts. -func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-set_bootable") -} - -// SetBootable will set bootable status on a volume based on the values in BootableOpts -func SetBootable(ctx context.Context, client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { - b, err := opts.ToBootableMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// MigrationPolicy type represents a migration_policy when changing types. -type MigrationPolicy string - -// Supported attributes for MigrationPolicy attribute for changeType operations. -const ( - MigrationPolicyNever MigrationPolicy = "never" - MigrationPolicyOnDemand MigrationPolicy = "on-demand" -) - -// ChangeTypeOptsBuilder allows extensions to add additional parameters to the -// ChangeType request. -type ChangeTypeOptsBuilder interface { - ToVolumeChangeTypeMap() (map[string]interface{}, error) -} - -// ChangeTypeOpts contains options for changing the type of an existing Volume. -// This object is passed to the volumes.ChangeType function. -type ChangeTypeOpts struct { - // NewType is the name of the new volume type of the volume. - NewType string `json:"new_type" required:"true"` - - // MigrationPolicy specifies if the volume should be migrated when it is - // re-typed. Possible values are "on-demand" or "never". If not specified, - // the default is "never". - MigrationPolicy MigrationPolicy `json:"migration_policy,omitempty"` -} - -// ToVolumeChangeTypeMap assembles a request body based on the contents of an -// ChangeTypeOpts. -func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-retype") -} - -// ChangeType will change the volume type of the volume based on the provided information. -// This operation does not return a response body. -func ChangeType(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { - b, err := opts.ToVolumeChangeTypeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ReImageOpts contains options for Re-image a volume. -type ReImageOpts struct { - // New image id - ImageID string `json:"image_id"` - // set true to re-image volumes in reserved state - ReImageReserved bool `json:"reimage_reserved"` -} - -// ToReImageMap assembles a request body based on the contents of a ReImageOpts. -func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-reimage") -} - -// ReImage will re-image a volume based on the values in ReImageOpts -func ReImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { - b, err := opts.ToReImageMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ResetStatusOptsBuilder allows extensions to add additional parameters to the -// ResetStatus request. -type ResetStatusOptsBuilder interface { - ToResetStatusMap() (map[string]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(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { - b, err := opts.ToResetStatusMap() - if err != nil { - r.Err = err - return - } - - resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/blockstorage/extensions/volumeactions/results.go b/openstack/blockstorage/extensions/volumeactions/results.go deleted file mode 100644 index b4fd0ea3ae..0000000000 --- a/openstack/blockstorage/extensions/volumeactions/results.go +++ /dev/null @@ -1,226 +0,0 @@ -package volumeactions - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud/v2" -) - -// AttachResult contains the response body and error from an Attach request. -type AttachResult struct { - gophercloud.ErrResult -} - -// BeginDetachingResult contains the response body and error from a BeginDetach -// request. -type BeginDetachingResult struct { - gophercloud.ErrResult -} - -// DetachResult contains the response body and error from a Detach request. -type DetachResult struct { - gophercloud.ErrResult -} - -// UploadImageResult contains the response body and error from an UploadImage -// request. -type UploadImageResult struct { - gophercloud.Result -} - -// SetImageMetadataResult contains the response body and error from an SetImageMetadata -// request. -type SetImageMetadataResult struct { - gophercloud.ErrResult -} - -// SetBootableResult contains the response body and error from a SetBootable -// request. -type SetBootableResult struct { - gophercloud.ErrResult -} - -// ReserveResult contains the response body and error from a Reserve request. -type ReserveResult struct { - gophercloud.ErrResult -} - -// UnreserveResult contains the response body and error from an Unreserve -// request. -type UnreserveResult struct { - gophercloud.ErrResult -} - -// TerminateConnectionResult contains the response body and error from a -// TerminateConnection request. -type TerminateConnectionResult struct { - gophercloud.ErrResult -} - -// InitializeConnectionResult contains the response body and error from an -// InitializeConnection request. -type InitializeConnectionResult struct { - gophercloud.Result -} - -// ExtendSizeResult contains the response body and error from an ExtendSize request. -type ExtendSizeResult struct { - gophercloud.ErrResult -} - -// Extract will get the connection information out of the -// InitializeConnectionResult object. -// -// This will be a generic map[string]interface{} and the results will be -// dependent on the type of connection made. -func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { - var s struct { - ConnectionInfo map[string]interface{} `json:"connection_info"` - } - err := r.ExtractInto(&s) - return s.ConnectionInfo, err -} - -// ImageVolumeType contains volume type information obtained from UploadImage -// action. -type ImageVolumeType struct { - // The ID of a volume type. - ID string `json:"id"` - - // Human-readable display name for the volume type. - Name string `json:"name"` - - // Human-readable description for the volume type. - Description string `json:"display_description"` - - // Flag for public access. - IsPublic bool `json:"is_public"` - - // Extra specifications for volume type. - ExtraSpecs map[string]interface{} `json:"extra_specs"` - - // ID of quality of service specs. - QosSpecsID string `json:"qos_specs_id"` - - // Flag for deletion status of volume type. - Deleted bool `json:"deleted"` - - // The date when volume type was deleted. - DeletedAt time.Time `json:"-"` - - // The date when volume type was created. - CreatedAt time.Time `json:"-"` - - // The date when this volume was last updated. - UpdatedAt time.Time `json:"-"` -} - -func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { - type tmp ImageVolumeType - var s struct { - tmp - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` - DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = ImageVolumeType(s.tmp) - - r.CreatedAt = time.Time(s.CreatedAt) - r.UpdatedAt = time.Time(s.UpdatedAt) - r.DeletedAt = time.Time(s.DeletedAt) - - return err -} - -// VolumeImage contains information about volume uploaded to an image service. -type VolumeImage struct { - // The ID of a volume an image is created from. - VolumeID string `json:"id"` - - // Container format, may be bare, ofv, ova, etc. - ContainerFormat string `json:"container_format"` - - // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. - DiskFormat string `json:"disk_format"` - - // Human-readable description for the volume. - Description string `json:"display_description"` - - // The ID of the created image. - ImageID string `json:"image_id"` - - // Human-readable display name for the image. - ImageName string `json:"image_name"` - - // Size of the volume in GB. - Size int `json:"size"` - - // Current status of the volume. - Status string `json:"status"` - - // Visibility defines who can see/use the image. - // supported since 3.1 microversion - Visibility string `json:"visibility"` - - // whether the image is not deletable. - // supported since 3.1 microversion - Protected bool `json:"protected"` - - // The date when this volume was last updated. - UpdatedAt time.Time `json:"-"` - - // Volume type object of used volume. - VolumeType ImageVolumeType `json:"volume_type"` -} - -func (r *VolumeImage) UnmarshalJSON(b []byte) error { - type tmp VolumeImage - var s struct { - tmp - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = VolumeImage(s.tmp) - - r.UpdatedAt = time.Time(s.UpdatedAt) - - return err -} - -// Extract will get an object with info about the uploaded image out of the -// UploadImageResult object. -func (r UploadImageResult) Extract() (VolumeImage, error) { - var s struct { - VolumeImage VolumeImage `json:"os-volume_upload_image"` - } - err := r.ExtractInto(&s) - return s.VolumeImage, err -} - -// ForceDeleteResult contains the response body and error from a ForceDelete request. -type ForceDeleteResult struct { - gophercloud.ErrResult -} - -// ChangeTypeResult contains the response body and error from an ChangeType request. -type ChangeTypeResult struct { - gophercloud.ErrResult -} - -// ReImageResult contains the response body and error from a ReImage request. -type ReImageResult struct { - gophercloud.ErrResult -} - -// ResetStatusResult contains the response error from a ResetStatus request. -type ResetStatusResult struct { - gophercloud.ErrResult -} diff --git a/openstack/blockstorage/extensions/volumeactions/testing/doc.go b/openstack/blockstorage/extensions/volumeactions/testing/doc.go deleted file mode 100644 index 336406df1d..0000000000 --- a/openstack/blockstorage/extensions/volumeactions/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// volumeactions unit tests -package testing diff --git a/openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go b/openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go deleted file mode 100644 index 3ba84cc0b8..0000000000 --- a/openstack/blockstorage/extensions/volumeactions/testing/fixtures_test.go +++ /dev/null @@ -1,393 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func MockAttachResponse(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-attach": - { - "mountpoint": "/mnt", - "mode": "rw", - "instance_uuid": "50902f4f-a974-46a0-85e9-7efc5e22dfdd" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockBeginDetachingResponse(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-begin_detaching": {} -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockDetachResponse(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-detach": {} -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockUploadImageResponse(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-volume_upload_image": { - "container_format": "bare", - "force": true, - "image_name": "test", - "disk_format": "raw" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, ` -{ - "os-volume_upload_image": { - "container_format": "bare", - "display_description": null, - "id": "cd281d77-8217-4830-be95-9528227c105c", - "image_id": "ecb92d98-de08-45db-8235-bbafe317269c", - "image_name": "test", - "disk_format": "raw", - "size": 5, - "status": "uploading", - "updated_at": "2017-07-17T09:29:22.000000", - "volume_type": { - "created_at": "2016-05-04T08:54:14.000000", - "deleted": false, - "deleted_at": null, - "description": null, - "extra_specs": { - "volume_backend_name": "basic.ru-2a" - }, - "id": "b7133444-62f6-4433-8da3-70ac332229b7", - "is_public": true, - "name": "basic.ru-2a", - "updated_at": "2016-05-04T09:15:33.000000" - } - } -} - `) - }) -} - -func MockReserveResponse(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-reserve": {} -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockUnreserveResponse(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-unreserve": {} -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockInitializeConnectionResponse(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-initialize_connection": - { - "connector": - { - "ip":"127.0.0.1", - "host":"stack", - "initiator":"iqn.1994-05.com.redhat:17cf566367d2", - "multipath": false, - "platform": "x86_64", - "os_type": "linux2" - } - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{ -"connection_info": { - "data": { - "target_portals": [ - "172.31.17.48:3260" - ], - "auth_method": "CHAP", - "auth_username": "5MLtcsTEmNN5jFVcT6ui", - "access_mode": "rw", - "target_lun": 0, - "volume_id": "cd281d77-8217-4830-be95-9528227c105c", - "target_luns": [ - 0 - ], - "target_iqns": [ - "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c" - ], - "auth_password": "x854ZY5Re3aCkdNL", - "target_discovered": false, - "encrypted": false, - "qos_specs": null, - "target_iqn": "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c", - "target_portal": "172.31.17.48:3260" - }, - "driver_volume_type": "iscsi" - } - }`) - }) -} - -func MockTerminateConnectionResponse(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-terminate_connection": - { - "connector": - { - "ip":"127.0.0.1", - "host":"stack", - "initiator":"iqn.1994-05.com.redhat:17cf566367d2", - "multipath": true, - "platform": "x86_64", - "os_type": "linux2" - } - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockExtendSizeResponse(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-extend": - { - "new_size": 3 - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockForceDeleteResponse(t *testing.T) { - th.Mux.HandleFunc("/volumes/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.TestBody(t, r, `{"os-force_delete":""}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func MockSetImageMetadataResponse(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-set_image_metadata": { - "metadata": { - "label": "test" - } - } -} - `) - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, `{}`) - }) -} - -func MockSetBootableResponse(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-set_bootable": { - "bootable": true - } -} - `) - w.Header().Add("Content-Type", "application/json") - w.Header().Add("Content-Length", "0") - w.WriteHeader(http.StatusOK) - }) -} - -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) { - 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-retype": - { - "new_type": "ssd", - "migration_policy": "on-demand" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - 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 deleted file mode 100644 index ac32ee2236..0000000000 --- a/openstack/blockstorage/extensions/volumeactions/testing/requests_test.go +++ /dev/null @@ -1,243 +0,0 @@ -package testing - -import ( - "context" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumeactions" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestAttach(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockAttachResponse(t) - - options := &volumeactions.AttachOpts{ - MountPoint: "/mnt", - Mode: "rw", - InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", - } - err := volumeactions.Attach(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestBeginDetaching(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockBeginDetachingResponse(t) - - err := volumeactions.BeginDetaching(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestDetach(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockDetachResponse(t) - - err := volumeactions.Detach(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumeactions.DetachOpts{}).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestUploadImage(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - MockUploadImageResponse(t) - options := &volumeactions.UploadImageOpts{ - ContainerFormat: "bare", - DiskFormat: "raw", - ImageName: "test", - Force: true, - } - - actual, err := volumeactions.UploadImage(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() - th.AssertNoErr(t, err) - - expected := volumeactions.VolumeImage{ - VolumeID: "cd281d77-8217-4830-be95-9528227c105c", - ContainerFormat: "bare", - DiskFormat: "raw", - Description: "", - ImageID: "ecb92d98-de08-45db-8235-bbafe317269c", - ImageName: "test", - Size: 5, - Status: "uploading", - UpdatedAt: time.Date(2017, 7, 17, 9, 29, 22, 0, time.UTC), - VolumeType: volumeactions.ImageVolumeType{ - ID: "b7133444-62f6-4433-8da3-70ac332229b7", - Name: "basic.ru-2a", - Description: "", - IsPublic: true, - ExtraSpecs: map[string]interface{}{"volume_backend_name": "basic.ru-2a"}, - QosSpecsID: "", - Deleted: false, - DeletedAt: time.Time{}, - CreatedAt: time.Date(2016, 5, 4, 8, 54, 14, 0, time.UTC), - UpdatedAt: time.Date(2016, 5, 4, 9, 15, 33, 0, time.UTC), - }, - } - th.AssertDeepEquals(t, expected, actual) -} - -func TestReserve(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockReserveResponse(t) - - err := volumeactions.Reserve(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestUnreserve(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockUnreserveResponse(t) - - err := volumeactions.Unreserve(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestInitializeConnection(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockInitializeConnectionResponse(t) - - options := &volumeactions.InitializeConnectionOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: gophercloud.Disabled, - Platform: "x86_64", - OSType: "linux2", - } - _, err := volumeactions.InitializeConnection(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() - th.AssertNoErr(t, err) -} - -func TestTerminateConnection(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockTerminateConnectionResponse(t) - - options := &volumeactions.TerminateConnectionOpts{ - IP: "127.0.0.1", - Host: "stack", - Initiator: "iqn.1994-05.com.redhat:17cf566367d2", - Multipath: gophercloud.Enabled, - Platform: "x86_64", - OSType: "linux2", - } - err := volumeactions.TerminateConnection(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestExtendSize(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockExtendSizeResponse(t) - - options := &volumeactions.ExtendSizeOpts{ - NewSize: 3, - } - - err := volumeactions.ExtendSize(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestForceDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockForceDeleteResponse(t) - - res := volumeactions.ForceDelete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") - th.AssertNoErr(t, res.Err) -} - -func TestSetImageMetadata(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockSetImageMetadataResponse(t) - - options := &volumeactions.ImageMetadataOpts{ - Metadata: map[string]string{ - "label": "test", - }, - } - - err := volumeactions.SetImageMetadata(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestSetBootable(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockSetBootableResponse(t) - - options := volumeactions.BootableOpts{ - Bootable: true, - } - - err := volumeactions.SetBootable(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - 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(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestChangeType(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - MockChangeTypeResponse(t) - - options := &volumeactions.ChangeTypeOpts{ - NewType: "ssd", - MigrationPolicy: "on-demand", - } - - err := volumeactions.ChangeType(context.TODO(), 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(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/blockstorage/extensions/volumeactions/urls.go b/openstack/blockstorage/extensions/volumeactions/urls.go deleted file mode 100644 index dea10c5f5b..0000000000 --- a/openstack/blockstorage/extensions/volumeactions/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package volumeactions - -import "github.com/gophercloud/gophercloud/v2" - -func actionURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL("volumes", id, "action") -} diff --git a/openstack/blockstorage/v2/volumes/doc.go b/openstack/blockstorage/v2/volumes/doc.go index 307b8b12d2..b6a11983d6 100644 --- a/openstack/blockstorage/v2/volumes/doc.go +++ b/openstack/blockstorage/v2/volumes/doc.go @@ -1,5 +1,109 @@ -// Package volumes provides information and interaction with volumes in the -// OpenStack Block Storage service. A volume is a detachable block storage -// device, akin to a USB hard drive. It can only be attached to one instance at -// a time. +/* +Package volumes provides information and interaction with volumes in the +OpenStack Block Storage service. A volume is a detachable block storage +device, akin to a USB hard drive. It can only be attached to one instance at +a time. + +Example of Creating an Image from a Volume + + uploadImageOpts := volumes.UploadImageOpts{ + ImageName: "my_vol", + Force: true, + } + + volumeImage, err := volumes.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", volumeImage) + +Example of Extending a Volume's Size + + extendOpts := volumes.ExtendSizeOpts{ + NewSize: 100, + } + + err := volumes.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Initializing a Volume Connection + + connectOpts := &volumes.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + connectionInfo, err := volumes.InitializeConnection(context.TODO(), client, volume.ID, connectOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", connectionInfo["data"]) + + terminateOpts := &volumes.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + err = volumes.TerminateConnection(context.TODO(), client, volume.ID, terminateOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Setting a Volume's Bootable status + + options := volumes.BootableOpts{ + Bootable: true, + } + + err := volumes.SetBootable(context.TODO(), client, volume.ID, options).ExtractErr() + if err != nil { + panic(err) + } + +Example of Changing Type of a Volume + + changeTypeOpts := volumes.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: volumes.MigrationPolicyOnDemand, + } + + err = volumes.ChangeType(context.TODO(), client, volumeID, changeTypeOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Attaching a Volume to an Instance + + attachOpts := volumes.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: server.ID, + } + + err := volumes.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr() + if err != nil { + panic(err) + } + + detachOpts := volumes.DetachOpts{ + AttachmentID: volume.Attachments[0].AttachmentID, + } + + err = volumes.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr() + if err != nil { + panic(err) + } +*/ package volumes diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index 7f2d5f6a55..e0fd482b3b 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -203,3 +203,458 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// AttachOptsBuilder allows extensions to add additional parameters to the +// Attach request. +type AttachOptsBuilder interface { + ToVolumeAttachMap() (map[string]interface{}, error) +} + +// AttachMode describes the attachment mode for volumes. +type AttachMode string + +// These constants determine how a volume is attached. +const ( + ReadOnly AttachMode = "ro" + ReadWrite AttachMode = "rw" +) + +// AttachOpts contains options for attaching a Volume. +type AttachOpts struct { + // The mountpoint of this volume. + MountPoint string `json:"mountpoint,omitempty"` + + // The nova instance ID, can't set simultaneously with HostName. + InstanceUUID string `json:"instance_uuid,omitempty"` + + // The hostname of baremetal host, can't set simultaneously with InstanceUUID. + HostName string `json:"host_name,omitempty"` + + // Mount mode of this volume. + Mode AttachMode `json:"mode,omitempty"` +} + +// ToVolumeAttachMap assembles a request body based on the contents of a +// AttachOpts. +func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-attach") +} + +// Attach will attach a volume based on the values in AttachOpts. +func Attach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { + b, err := opts.ToVolumeAttachMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// BeginDetaching will mark the volume as detaching. +func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { + b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DetachOptsBuilder allows extensions to add additional parameters to the +// Detach request. +type DetachOptsBuilder interface { + ToVolumeDetachMap() (map[string]interface{}, error) +} + +// DetachOpts contains options for detaching a Volume. +type DetachOpts struct { + // AttachmentID is the ID of the attachment between a volume and instance. + AttachmentID string `json:"attachment_id,omitempty"` +} + +// ToVolumeDetachMap assembles a request body based on the contents of a +// DetachOpts. +func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-detach") +} + +// Detach will detach a volume based on volume ID. +func Detach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { + b, err := opts.ToVolumeDetachMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Reserve will reserve a volume based on volume ID. +func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ReserveResult) { + b := map[string]interface{}{"os-reserve": make(map[string]interface{})} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unreserve will unreserve a volume based on volume ID. +func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnreserveResult) { + b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// InitializeConnectionOptsBuilder allows extensions to add additional parameters to the +// InitializeConnection request. +type InitializeConnectionOptsBuilder interface { + ToVolumeInitializeConnectionMap() (map[string]interface{}, error) +} + +// InitializeConnectionOpts hosts options for InitializeConnection. +// The fields are specific to the storage driver in use and the destination +// attachment. +type InitializeConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +// ToVolumeInitializeConnectionMap assembles a request body based on the contents of a +// InitializeConnectionOpts. +func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "connector") + return map[string]interface{}{"os-initialize_connection": b}, err +} + +// InitializeConnection initializes an iSCSI connection by volume ID. +func InitializeConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { + b, err := opts.ToVolumeInitializeConnectionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// TerminateConnectionOptsBuilder allows extensions to add additional parameters to the +// TerminateConnection request. +type TerminateConnectionOptsBuilder interface { + ToVolumeTerminateConnectionMap() (map[string]interface{}, error) +} + +// TerminateConnectionOpts hosts options for TerminateConnection. +type TerminateConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +// ToVolumeTerminateConnectionMap assembles a request body based on the contents of a +// TerminateConnectionOpts. +func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "connector") + return map[string]interface{}{"os-terminate_connection": b}, err +} + +// TerminateConnection terminates an iSCSI connection by volume ID. +func TerminateConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { + b, err := opts.ToVolumeTerminateConnectionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ExtendSizeOptsBuilder allows extensions to add additional parameters to the +// ExtendSize request. +type ExtendSizeOptsBuilder interface { + ToVolumeExtendSizeMap() (map[string]interface{}, error) +} + +// ExtendSizeOpts contains options for extending the size of an existing Volume. +// This object is passed to the volumes.ExtendSize function. +type ExtendSizeOpts struct { + // NewSize is the new size of the volume, in GB. + NewSize int `json:"new_size" required:"true"` +} + +// ToVolumeExtendSizeMap assembles a request body based on the contents of an +// ExtendSizeOpts. +func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-extend") +} + +// ExtendSize will extend the size of the volume based on the provided information. +// This operation does not return a response body. +func ExtendSize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { + b, err := opts.ToVolumeExtendSizeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UploadImageOptsBuilder allows extensions to add additional parameters to the +// UploadImage request. +type UploadImageOptsBuilder interface { + ToVolumeUploadImageMap() (map[string]interface{}, error) +} + +// UploadImageOpts contains options for uploading a Volume to image storage. +type UploadImageOpts struct { + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format,omitempty"` + + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format,omitempty"` + + // The name of image that will be stored in glance. + ImageName string `json:"image_name,omitempty"` + + // Force image creation, usable if volume attached to instance. + Force bool `json:"force,omitempty"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility,omitempty"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected,omitempty"` +} + +// ToVolumeUploadImageMap assembles a request body based on the contents of a +// UploadImageOpts. +func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") +} + +// UploadImage will upload an image based on the values in UploadImageOptsBuilder. +func UploadImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { + b, err := opts.ToVolumeUploadImageMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ForceDelete will delete the volume regardless of state. +func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ImageMetadataOptsBuilder allows extensions to add additional parameters to the +// ImageMetadataRequest request. +type ImageMetadataOptsBuilder interface { + ToImageMetadataMap() (map[string]interface{}, error) +} + +// ImageMetadataOpts contains options for setting image metadata to a volume. +type ImageMetadataOpts struct { + // The image metadata to add to the volume as a set of metadata key and value pairs. + Metadata map[string]string `json:"metadata"` +} + +// ToImageMetadataMap assembles a request body based on the contents of a +// ImageMetadataOpts. +func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") +} + +// SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. +func SetImageMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { + b, err := opts.ToImageMetadataMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// BootableOpts contains options for setting bootable status to a volume. +type BootableOpts struct { + // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. + Bootable bool `json:"bootable"` +} + +// ToBootableMap assembles a request body based on the contents of a +// BootableOpts. +func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-set_bootable") +} + +// SetBootable will set bootable status on a volume based on the values in BootableOpts +func SetBootable(ctx context.Context, client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { + b, err := opts.ToBootableMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// MigrationPolicy type represents a migration_policy when changing types. +type MigrationPolicy string + +// Supported attributes for MigrationPolicy attribute for changeType operations. +const ( + MigrationPolicyNever MigrationPolicy = "never" + MigrationPolicyOnDemand MigrationPolicy = "on-demand" +) + +// ChangeTypeOptsBuilder allows extensions to add additional parameters to the +// ChangeType request. +type ChangeTypeOptsBuilder interface { + ToVolumeChangeTypeMap() (map[string]interface{}, error) +} + +// ChangeTypeOpts contains options for changing the type of an existing Volume. +// This object is passed to the volumes.ChangeType function. +type ChangeTypeOpts struct { + // NewType is the name of the new volume type of the volume. + NewType string `json:"new_type" required:"true"` + + // MigrationPolicy specifies if the volume should be migrated when it is + // re-typed. Possible values are "on-demand" or "never". If not specified, + // the default is "never". + MigrationPolicy MigrationPolicy `json:"migration_policy,omitempty"` +} + +// ToVolumeChangeTypeMap assembles a request body based on the contents of an +// ChangeTypeOpts. +func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-retype") +} + +// ChangeType will change the volume type of the volume based on the provided information. +// This operation does not return a response body. +func ChangeType(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { + b, err := opts.ToVolumeChangeTypeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ReImageOpts contains options for Re-image a volume. +type ReImageOpts struct { + // New image id + ImageID string `json:"image_id"` + // set true to re-image volumes in reserved state + ReImageReserved bool `json:"reimage_reserved"` +} + +// ToReImageMap assembles a request body based on the contents of a ReImageOpts. +func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-reimage") +} + +// ReImage will re-image a volume based on the values in ReImageOpts +func ReImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { + b, err := opts.ToReImageMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ResetStatusOptsBuilder allows extensions to add additional parameters to the +// ResetStatus request. +type ResetStatusOptsBuilder interface { + ToResetStatusMap() (map[string]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(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToResetStatusMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/blockstorage/v2/volumes/results.go index a8a48af7b3..8119447522 100644 --- a/openstack/blockstorage/v2/volumes/results.go +++ b/openstack/blockstorage/v2/volumes/results.go @@ -169,3 +169,221 @@ type UpdateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// AttachResult contains the response body and error from an Attach request. +type AttachResult struct { + gophercloud.ErrResult +} + +// BeginDetachingResult contains the response body and error from a BeginDetach +// request. +type BeginDetachingResult struct { + gophercloud.ErrResult +} + +// DetachResult contains the response body and error from a Detach request. +type DetachResult struct { + gophercloud.ErrResult +} + +// UploadImageResult contains the response body and error from an UploadImage +// request. +type UploadImageResult struct { + gophercloud.Result +} + +// SetImageMetadataResult contains the response body and error from an SetImageMetadata +// request. +type SetImageMetadataResult struct { + gophercloud.ErrResult +} + +// SetBootableResult contains the response body and error from a SetBootable +// request. +type SetBootableResult struct { + gophercloud.ErrResult +} + +// ReserveResult contains the response body and error from a Reserve request. +type ReserveResult struct { + gophercloud.ErrResult +} + +// UnreserveResult contains the response body and error from an Unreserve +// request. +type UnreserveResult struct { + gophercloud.ErrResult +} + +// TerminateConnectionResult contains the response body and error from a +// TerminateConnection request. +type TerminateConnectionResult struct { + gophercloud.ErrResult +} + +// InitializeConnectionResult contains the response body and error from an +// InitializeConnection request. +type InitializeConnectionResult struct { + gophercloud.Result +} + +// ExtendSizeResult contains the response body and error from an ExtendSize request. +type ExtendSizeResult struct { + gophercloud.ErrResult +} + +// Extract will get the connection information out of the +// InitializeConnectionResult object. +// +// This will be a generic map[string]interface{} and the results will be +// dependent on the type of connection made. +func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { + var s struct { + ConnectionInfo map[string]interface{} `json:"connection_info"` + } + err := r.ExtractInto(&s) + return s.ConnectionInfo, err +} + +// ImageVolumeType contains volume type information obtained from UploadImage +// action. +type ImageVolumeType struct { + // The ID of a volume type. + ID string `json:"id"` + + // Human-readable display name for the volume type. + Name string `json:"name"` + + // Human-readable description for the volume type. + Description string `json:"display_description"` + + // Flag for public access. + IsPublic bool `json:"is_public"` + + // Extra specifications for volume type. + ExtraSpecs map[string]interface{} `json:"extra_specs"` + + // ID of quality of service specs. + QosSpecsID string `json:"qos_specs_id"` + + // Flag for deletion status of volume type. + Deleted bool `json:"deleted"` + + // The date when volume type was deleted. + DeletedAt time.Time `json:"-"` + + // The date when volume type was created. + CreatedAt time.Time `json:"-"` + + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` +} + +func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { + type tmp ImageVolumeType + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ImageVolumeType(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) + + return err +} + +// VolumeImage contains information about volume uploaded to an image service. +type VolumeImage struct { + // The ID of a volume an image is created from. + VolumeID string `json:"id"` + + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format"` + + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format"` + + // Human-readable description for the volume. + Description string `json:"display_description"` + + // The ID of the created image. + ImageID string `json:"image_id"` + + // Human-readable display name for the image. + ImageName string `json:"image_name"` + + // Size of the volume in GB. + Size int `json:"size"` + + // Current status of the volume. + Status string `json:"status"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected"` + + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` + + // Volume type object of used volume. + VolumeType ImageVolumeType `json:"volume_type"` +} + +func (r *VolumeImage) UnmarshalJSON(b []byte) error { + type tmp VolumeImage + var s struct { + tmp + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = VolumeImage(s.tmp) + + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +// Extract will get an object with info about the uploaded image out of the +// UploadImageResult object. +func (r UploadImageResult) Extract() (VolumeImage, error) { + var s struct { + VolumeImage VolumeImage `json:"os-volume_upload_image"` + } + err := r.ExtractInto(&s) + return s.VolumeImage, err +} + +// ForceDeleteResult contains the response body and error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} + +// ChangeTypeResult contains the response body and error from an ChangeType request. +type ChangeTypeResult struct { + gophercloud.ErrResult +} + +// ReImageResult contains the response body and error from a ReImage request. +type ReImageResult struct { + gophercloud.ErrResult +} + +// ResetStatusResult contains the response error from a ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v2/volumes/testing/doc.go b/openstack/blockstorage/v2/volumes/testing/doc.go index aa8351ab15..8e4457df7e 100644 --- a/openstack/blockstorage/v2/volumes/testing/doc.go +++ b/openstack/blockstorage/v2/volumes/testing/doc.go @@ -1,2 +1,2 @@ -// volumes_v2 +// volumes unit tests package testing diff --git a/openstack/blockstorage/v2/volumes/testing/fixtures_test.go b/openstack/blockstorage/v2/volumes/testing/fixtures_test.go index 369f1e7e1a..80677d2e40 100644 --- a/openstack/blockstorage/v2/volumes/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/volumes/testing/fixtures_test.go @@ -201,3 +201,386 @@ func MockUpdateResponse(t *testing.T) { `) }) } + +func MockAttachResponse(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-attach": + { + "mountpoint": "/mnt", + "mode": "rw", + "instance_uuid": "50902f4f-a974-46a0-85e9-7efc5e22dfdd" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockBeginDetachingResponse(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-begin_detaching": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockDetachResponse(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-detach": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockUploadImageResponse(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-volume_upload_image": { + "container_format": "bare", + "force": true, + "image_name": "test", + "disk_format": "raw" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, ` +{ + "os-volume_upload_image": { + "container_format": "bare", + "display_description": null, + "id": "cd281d77-8217-4830-be95-9528227c105c", + "image_id": "ecb92d98-de08-45db-8235-bbafe317269c", + "image_name": "test", + "disk_format": "raw", + "size": 5, + "status": "uploading", + "updated_at": "2017-07-17T09:29:22.000000", + "volume_type": { + "created_at": "2016-05-04T08:54:14.000000", + "deleted": false, + "deleted_at": null, + "description": null, + "extra_specs": { + "volume_backend_name": "basic.ru-2a" + }, + "id": "b7133444-62f6-4433-8da3-70ac332229b7", + "is_public": true, + "name": "basic.ru-2a", + "updated_at": "2016-05-04T09:15:33.000000" + } + } +} + `) + }) +} + +func MockReserveResponse(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-reserve": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockUnreserveResponse(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-unreserve": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockInitializeConnectionResponse(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-initialize_connection": + { + "connector": + { + "ip":"127.0.0.1", + "host":"stack", + "initiator":"iqn.1994-05.com.redhat:17cf566367d2", + "multipath": false, + "platform": "x86_64", + "os_type": "linux2" + } + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{ +"connection_info": { + "data": { + "target_portals": [ + "172.31.17.48:3260" + ], + "auth_method": "CHAP", + "auth_username": "5MLtcsTEmNN5jFVcT6ui", + "access_mode": "rw", + "target_lun": 0, + "volume_id": "cd281d77-8217-4830-be95-9528227c105c", + "target_luns": [ + 0 + ], + "target_iqns": [ + "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c" + ], + "auth_password": "x854ZY5Re3aCkdNL", + "target_discovered": false, + "encrypted": false, + "qos_specs": null, + "target_iqn": "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c", + "target_portal": "172.31.17.48:3260" + }, + "driver_volume_type": "iscsi" + } + }`) + }) +} + +func MockTerminateConnectionResponse(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-terminate_connection": + { + "connector": + { + "ip":"127.0.0.1", + "host":"stack", + "initiator":"iqn.1994-05.com.redhat:17cf566367d2", + "multipath": true, + "platform": "x86_64", + "os_type": "linux2" + } + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockExtendSizeResponse(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-extend": + { + "new_size": 3 + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/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.TestBody(t, r, `{"os-force_delete":""}`) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockSetImageMetadataResponse(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-set_image_metadata": { + "metadata": { + "label": "test" + } + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockSetBootableResponse(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-set_bootable": { + "bootable": true + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", "0") + w.WriteHeader(http.StatusOK) + }) +} + +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) { + 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-retype": + { + "new_type": "ssd", + "migration_policy": "on-demand" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + 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/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 9324f67e40..91bb67cb8a 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/v2/pagination" @@ -257,3 +258,234 @@ func TestGetWithExtensions(t *testing.T) { t.Errorf("Expected error when providing non-pointer struct") } } + +func TestAttach(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockAttachResponse(t) + + options := &volumes.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", + } + err := volumes.Attach(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestBeginDetaching(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockBeginDetachingResponse(t) + + err := volumes.BeginDetaching(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDetach(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDetachResponse(t) + + err := volumes.Detach(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumes.DetachOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUploadImage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + MockUploadImageResponse(t) + options := &volumes.UploadImageOpts{ + ContainerFormat: "bare", + DiskFormat: "raw", + ImageName: "test", + Force: true, + } + + actual, err := volumes.UploadImage(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() + th.AssertNoErr(t, err) + + expected := volumes.VolumeImage{ + VolumeID: "cd281d77-8217-4830-be95-9528227c105c", + ContainerFormat: "bare", + DiskFormat: "raw", + Description: "", + ImageID: "ecb92d98-de08-45db-8235-bbafe317269c", + ImageName: "test", + Size: 5, + Status: "uploading", + UpdatedAt: time.Date(2017, 7, 17, 9, 29, 22, 0, time.UTC), + VolumeType: volumes.ImageVolumeType{ + ID: "b7133444-62f6-4433-8da3-70ac332229b7", + Name: "basic.ru-2a", + Description: "", + IsPublic: true, + ExtraSpecs: map[string]interface{}{"volume_backend_name": "basic.ru-2a"}, + QosSpecsID: "", + Deleted: false, + DeletedAt: time.Time{}, + CreatedAt: time.Date(2016, 5, 4, 8, 54, 14, 0, time.UTC), + UpdatedAt: time.Date(2016, 5, 4, 9, 15, 33, 0, time.UTC), + }, + } + th.AssertDeepEquals(t, expected, actual) +} + +func TestReserve(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockReserveResponse(t) + + err := volumes.Reserve(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUnreserve(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUnreserveResponse(t) + + err := volumes.Unreserve(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestInitializeConnection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockInitializeConnectionResponse(t) + + options := &volumes.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + _, err := volumes.InitializeConnection(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() + th.AssertNoErr(t, err) +} + +func TestTerminateConnection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockTerminateConnectionResponse(t) + + options := &volumes.TerminateConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Enabled, + Platform: "x86_64", + OSType: "linux2", + } + err := volumes.TerminateConnection(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestExtendSize(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockExtendSizeResponse(t) + + options := &volumes.ExtendSizeOpts{ + NewSize: 3, + } + + err := volumes.ExtendSize(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestForceDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockForceDeleteResponse(t) + + res := volumes.ForceDelete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} + +func TestSetImageMetadata(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetImageMetadataResponse(t) + + options := &volumes.ImageMetadataOpts{ + Metadata: map[string]string{ + "label": "test", + }, + } + + err := volumes.SetImageMetadata(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestSetBootable(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetBootableResponse(t) + + options := volumes.BootableOpts{ + Bootable: true, + } + + err := volumes.SetBootable(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestReImage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockReImageResponse(t) + + options := volumes.ReImageOpts{ + ImageID: "71543ced-a8af-45b6-a5c4-a46282108a90", + ReImageReserved: false, + } + + err := volumes.ReImage(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestChangeType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockChangeTypeResponse(t) + + options := &volumes.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: "on-demand", + } + + err := volumes.ChangeType(context.TODO(), 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 := &volumes.ResetStatusOpts{ + Status: "error", + AttachStatus: "detached", + MigrationStatus: "migrating", + } + + err := volumes.ResetStatus(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/v2/volumes/urls.go b/openstack/blockstorage/v2/volumes/urls.go index b8704306c9..a73e2d2b13 100644 --- a/openstack/blockstorage/v2/volumes/urls.go +++ b/openstack/blockstorage/v2/volumes/urls.go @@ -21,3 +21,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func updateURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } + +func actionURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("volumes", id, "action") +} diff --git a/openstack/blockstorage/v3/volumes/doc.go b/openstack/blockstorage/v3/volumes/doc.go index 18889d0015..0dcebf51d5 100644 --- a/openstack/blockstorage/v3/volumes/doc.go +++ b/openstack/blockstorage/v3/volumes/doc.go @@ -19,5 +19,107 @@ Example to create a Volume from a Backup } fmt.Println(volume) + +Example of Creating an Image from a Volume + + uploadImageOpts := volumes.UploadImageOpts{ + ImageName: "my_vol", + Force: true, + } + + volumeImage, err := volumes.UploadImage(context.TODO(), client, volume.ID, uploadImageOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", volumeImage) + +Example of Extending a Volume's Size + + extendOpts := volumes.ExtendSizeOpts{ + NewSize: 100, + } + + err := volumes.ExtendSize(context.TODO(), client, volume.ID, extendOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Initializing a Volume Connection + + connectOpts := &volumes.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + connectionInfo, err := volumes.InitializeConnection(context.TODO(), client, volume.ID, connectOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", connectionInfo["data"]) + + terminateOpts := &volumes.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + + err = volumes.TerminateConnection(context.TODO(), client, volume.ID, terminateOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Setting a Volume's Bootable status + + options := volumes.BootableOpts{ + Bootable: true, + } + + err := volumes.SetBootable(context.TODO(), client, volume.ID, options).ExtractErr() + if err != nil { + panic(err) + } + +Example of Changing Type of a Volume + + changeTypeOpts := volumes.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: volumes.MigrationPolicyOnDemand, + } + + err = volumes.ChangeType(context.TODO(), client, volumeID, changeTypeOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example of Attaching a Volume to an Instance + + attachOpts := volumes.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: server.ID, + } + + err := volumes.Attach(context.TODO(), client, volume.ID, attachOpts).ExtractErr() + if err != nil { + panic(err) + } + + detachOpts := volumes.DetachOpts{ + AttachmentID: volume.Attachments[0].AttachmentID, + } + + err = volumes.Detach(context.TODO(), client, volume.ID, detachOpts).ExtractErr() + if err != nil { + panic(err) + } */ package volumes diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 044d18fca9..db668d06d1 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -209,3 +209,458 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// AttachOptsBuilder allows extensions to add additional parameters to the +// Attach request. +type AttachOptsBuilder interface { + ToVolumeAttachMap() (map[string]interface{}, error) +} + +// AttachMode describes the attachment mode for volumes. +type AttachMode string + +// These constants determine how a volume is attached. +const ( + ReadOnly AttachMode = "ro" + ReadWrite AttachMode = "rw" +) + +// AttachOpts contains options for attaching a Volume. +type AttachOpts struct { + // The mountpoint of this volume. + MountPoint string `json:"mountpoint,omitempty"` + + // The nova instance ID, can't set simultaneously with HostName. + InstanceUUID string `json:"instance_uuid,omitempty"` + + // The hostname of baremetal host, can't set simultaneously with InstanceUUID. + HostName string `json:"host_name,omitempty"` + + // Mount mode of this volume. + Mode AttachMode `json:"mode,omitempty"` +} + +// ToVolumeAttachMap assembles a request body based on the contents of a +// AttachOpts. +func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-attach") +} + +// Attach will attach a volume based on the values in AttachOpts. +func Attach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachOptsBuilder) (r AttachResult) { + b, err := opts.ToVolumeAttachMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// BeginDetaching will mark the volume as detaching. +func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { + b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DetachOptsBuilder allows extensions to add additional parameters to the +// Detach request. +type DetachOptsBuilder interface { + ToVolumeDetachMap() (map[string]interface{}, error) +} + +// DetachOpts contains options for detaching a Volume. +type DetachOpts struct { + // AttachmentID is the ID of the attachment between a volume and instance. + AttachmentID string `json:"attachment_id,omitempty"` +} + +// ToVolumeDetachMap assembles a request body based on the contents of a +// DetachOpts. +func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-detach") +} + +// Detach will detach a volume based on volume ID. +func Detach(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachOptsBuilder) (r DetachResult) { + b, err := opts.ToVolumeDetachMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Reserve will reserve a volume based on volume ID. +func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ReserveResult) { + b := map[string]interface{}{"os-reserve": make(map[string]interface{})} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unreserve will unreserve a volume based on volume ID. +func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnreserveResult) { + b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// InitializeConnectionOptsBuilder allows extensions to add additional parameters to the +// InitializeConnection request. +type InitializeConnectionOptsBuilder interface { + ToVolumeInitializeConnectionMap() (map[string]interface{}, error) +} + +// InitializeConnectionOpts hosts options for InitializeConnection. +// The fields are specific to the storage driver in use and the destination +// attachment. +type InitializeConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +// ToVolumeInitializeConnectionMap assembles a request body based on the contents of a +// InitializeConnectionOpts. +func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "connector") + return map[string]interface{}{"os-initialize_connection": b}, err +} + +// InitializeConnection initializes an iSCSI connection by volume ID. +func InitializeConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts InitializeConnectionOptsBuilder) (r InitializeConnectionResult) { + b, err := opts.ToVolumeInitializeConnectionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// TerminateConnectionOptsBuilder allows extensions to add additional parameters to the +// TerminateConnection request. +type TerminateConnectionOptsBuilder interface { + ToVolumeTerminateConnectionMap() (map[string]interface{}, error) +} + +// TerminateConnectionOpts hosts options for TerminateConnection. +type TerminateConnectionOpts struct { + IP string `json:"ip,omitempty"` + Host string `json:"host,omitempty"` + Initiator string `json:"initiator,omitempty"` + Wwpns []string `json:"wwpns,omitempty"` + Wwnns string `json:"wwnns,omitempty"` + Multipath *bool `json:"multipath,omitempty"` + Platform string `json:"platform,omitempty"` + OSType string `json:"os_type,omitempty"` +} + +// ToVolumeTerminateConnectionMap assembles a request body based on the contents of a +// TerminateConnectionOpts. +func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "connector") + return map[string]interface{}{"os-terminate_connection": b}, err +} + +// TerminateConnection terminates an iSCSI connection by volume ID. +func TerminateConnection(ctx context.Context, client *gophercloud.ServiceClient, id string, opts TerminateConnectionOptsBuilder) (r TerminateConnectionResult) { + b, err := opts.ToVolumeTerminateConnectionMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ExtendSizeOptsBuilder allows extensions to add additional parameters to the +// ExtendSize request. +type ExtendSizeOptsBuilder interface { + ToVolumeExtendSizeMap() (map[string]interface{}, error) +} + +// ExtendSizeOpts contains options for extending the size of an existing Volume. +// This object is passed to the volumes.ExtendSize function. +type ExtendSizeOpts struct { + // NewSize is the new size of the volume, in GB. + NewSize int `json:"new_size" required:"true"` +} + +// ToVolumeExtendSizeMap assembles a request body based on the contents of an +// ExtendSizeOpts. +func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-extend") +} + +// ExtendSize will extend the size of the volume based on the provided information. +// This operation does not return a response body. +func ExtendSize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ExtendSizeOptsBuilder) (r ExtendSizeResult) { + b, err := opts.ToVolumeExtendSizeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UploadImageOptsBuilder allows extensions to add additional parameters to the +// UploadImage request. +type UploadImageOptsBuilder interface { + ToVolumeUploadImageMap() (map[string]interface{}, error) +} + +// UploadImageOpts contains options for uploading a Volume to image storage. +type UploadImageOpts struct { + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format,omitempty"` + + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format,omitempty"` + + // The name of image that will be stored in glance. + ImageName string `json:"image_name,omitempty"` + + // Force image creation, usable if volume attached to instance. + Force bool `json:"force,omitempty"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility,omitempty"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected,omitempty"` +} + +// ToVolumeUploadImageMap assembles a request body based on the contents of a +// UploadImageOpts. +func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") +} + +// UploadImage will upload an image based on the values in UploadImageOptsBuilder. +func UploadImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UploadImageOptsBuilder) (r UploadImageResult) { + b, err := opts.ToVolumeUploadImageMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ForceDelete will delete the volume regardless of state. +func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ImageMetadataOptsBuilder allows extensions to add additional parameters to the +// ImageMetadataRequest request. +type ImageMetadataOptsBuilder interface { + ToImageMetadataMap() (map[string]interface{}, error) +} + +// ImageMetadataOpts contains options for setting image metadata to a volume. +type ImageMetadataOpts struct { + // The image metadata to add to the volume as a set of metadata key and value pairs. + Metadata map[string]string `json:"metadata"` +} + +// ToImageMetadataMap assembles a request body based on the contents of a +// ImageMetadataOpts. +func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") +} + +// SetImageMetadata will set image metadata on a volume based on the values in ImageMetadataOptsBuilder. +func SetImageMetadata(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ImageMetadataOptsBuilder) (r SetImageMetadataResult) { + b, err := opts.ToImageMetadataMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// BootableOpts contains options for setting bootable status to a volume. +type BootableOpts struct { + // Enables or disables the bootable attribute. You can boot an instance from a bootable volume. + Bootable bool `json:"bootable"` +} + +// ToBootableMap assembles a request body based on the contents of a +// BootableOpts. +func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-set_bootable") +} + +// SetBootable will set bootable status on a volume based on the values in BootableOpts +func SetBootable(ctx context.Context, client *gophercloud.ServiceClient, id string, opts BootableOpts) (r SetBootableResult) { + b, err := opts.ToBootableMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// MigrationPolicy type represents a migration_policy when changing types. +type MigrationPolicy string + +// Supported attributes for MigrationPolicy attribute for changeType operations. +const ( + MigrationPolicyNever MigrationPolicy = "never" + MigrationPolicyOnDemand MigrationPolicy = "on-demand" +) + +// ChangeTypeOptsBuilder allows extensions to add additional parameters to the +// ChangeType request. +type ChangeTypeOptsBuilder interface { + ToVolumeChangeTypeMap() (map[string]interface{}, error) +} + +// ChangeTypeOpts contains options for changing the type of an existing Volume. +// This object is passed to the volumes.ChangeType function. +type ChangeTypeOpts struct { + // NewType is the name of the new volume type of the volume. + NewType string `json:"new_type" required:"true"` + + // MigrationPolicy specifies if the volume should be migrated when it is + // re-typed. Possible values are "on-demand" or "never". If not specified, + // the default is "never". + MigrationPolicy MigrationPolicy `json:"migration_policy,omitempty"` +} + +// ToVolumeChangeTypeMap assembles a request body based on the contents of an +// ChangeTypeOpts. +func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-retype") +} + +// ChangeType will change the volume type of the volume based on the provided information. +// This operation does not return a response body. +func ChangeType(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ChangeTypeOptsBuilder) (r ChangeTypeResult) { + b, err := opts.ToVolumeChangeTypeMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ReImageOpts contains options for Re-image a volume. +type ReImageOpts struct { + // New image id + ImageID string `json:"image_id"` + // set true to re-image volumes in reserved state + ReImageReserved bool `json:"reimage_reserved"` +} + +// ToReImageMap assembles a request body based on the contents of a ReImageOpts. +func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-reimage") +} + +// ReImage will re-image a volume based on the values in ReImageOpts +func ReImage(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ReImageOpts) (r ReImageResult) { + b, err := opts.ToReImageMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ResetStatusOptsBuilder allows extensions to add additional parameters to the +// ResetStatus request. +type ResetStatusOptsBuilder interface { + ToResetStatusMap() (map[string]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(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResetStatusOptsBuilder) (r ResetStatusResult) { + b, err := opts.ToResetStatusMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{202}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index 096a7bebe0..4b6e524558 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -177,3 +177,221 @@ type UpdateResult struct { type DeleteResult struct { gophercloud.ErrResult } + +// AttachResult contains the response body and error from an Attach request. +type AttachResult struct { + gophercloud.ErrResult +} + +// BeginDetachingResult contains the response body and error from a BeginDetach +// request. +type BeginDetachingResult struct { + gophercloud.ErrResult +} + +// DetachResult contains the response body and error from a Detach request. +type DetachResult struct { + gophercloud.ErrResult +} + +// UploadImageResult contains the response body and error from an UploadImage +// request. +type UploadImageResult struct { + gophercloud.Result +} + +// SetImageMetadataResult contains the response body and error from an SetImageMetadata +// request. +type SetImageMetadataResult struct { + gophercloud.ErrResult +} + +// SetBootableResult contains the response body and error from a SetBootable +// request. +type SetBootableResult struct { + gophercloud.ErrResult +} + +// ReserveResult contains the response body and error from a Reserve request. +type ReserveResult struct { + gophercloud.ErrResult +} + +// UnreserveResult contains the response body and error from an Unreserve +// request. +type UnreserveResult struct { + gophercloud.ErrResult +} + +// TerminateConnectionResult contains the response body and error from a +// TerminateConnection request. +type TerminateConnectionResult struct { + gophercloud.ErrResult +} + +// InitializeConnectionResult contains the response body and error from an +// InitializeConnection request. +type InitializeConnectionResult struct { + gophercloud.Result +} + +// ExtendSizeResult contains the response body and error from an ExtendSize request. +type ExtendSizeResult struct { + gophercloud.ErrResult +} + +// Extract will get the connection information out of the +// InitializeConnectionResult object. +// +// This will be a generic map[string]interface{} and the results will be +// dependent on the type of connection made. +func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { + var s struct { + ConnectionInfo map[string]interface{} `json:"connection_info"` + } + err := r.ExtractInto(&s) + return s.ConnectionInfo, err +} + +// ImageVolumeType contains volume type information obtained from UploadImage +// action. +type ImageVolumeType struct { + // The ID of a volume type. + ID string `json:"id"` + + // Human-readable display name for the volume type. + Name string `json:"name"` + + // Human-readable description for the volume type. + Description string `json:"display_description"` + + // Flag for public access. + IsPublic bool `json:"is_public"` + + // Extra specifications for volume type. + ExtraSpecs map[string]interface{} `json:"extra_specs"` + + // ID of quality of service specs. + QosSpecsID string `json:"qos_specs_id"` + + // Flag for deletion status of volume type. + Deleted bool `json:"deleted"` + + // The date when volume type was deleted. + DeletedAt time.Time `json:"-"` + + // The date when volume type was created. + CreatedAt time.Time `json:"-"` + + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` +} + +func (r *ImageVolumeType) UnmarshalJSON(b []byte) error { + type tmp ImageVolumeType + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = ImageVolumeType(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + r.DeletedAt = time.Time(s.DeletedAt) + + return err +} + +// VolumeImage contains information about volume uploaded to an image service. +type VolumeImage struct { + // The ID of a volume an image is created from. + VolumeID string `json:"id"` + + // Container format, may be bare, ofv, ova, etc. + ContainerFormat string `json:"container_format"` + + // Disk format, may be raw, qcow2, vhd, vdi, vmdk, etc. + DiskFormat string `json:"disk_format"` + + // Human-readable description for the volume. + Description string `json:"display_description"` + + // The ID of the created image. + ImageID string `json:"image_id"` + + // Human-readable display name for the image. + ImageName string `json:"image_name"` + + // Size of the volume in GB. + Size int `json:"size"` + + // Current status of the volume. + Status string `json:"status"` + + // Visibility defines who can see/use the image. + // supported since 3.1 microversion + Visibility string `json:"visibility"` + + // whether the image is not deletable. + // supported since 3.1 microversion + Protected bool `json:"protected"` + + // The date when this volume was last updated. + UpdatedAt time.Time `json:"-"` + + // Volume type object of used volume. + VolumeType ImageVolumeType `json:"volume_type"` +} + +func (r *VolumeImage) UnmarshalJSON(b []byte) error { + type tmp VolumeImage + var s struct { + tmp + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = VolumeImage(s.tmp) + + r.UpdatedAt = time.Time(s.UpdatedAt) + + return err +} + +// Extract will get an object with info about the uploaded image out of the +// UploadImageResult object. +func (r UploadImageResult) Extract() (VolumeImage, error) { + var s struct { + VolumeImage VolumeImage `json:"os-volume_upload_image"` + } + err := r.ExtractInto(&s) + return s.VolumeImage, err +} + +// ForceDeleteResult contains the response body and error from a ForceDelete request. +type ForceDeleteResult struct { + gophercloud.ErrResult +} + +// ChangeTypeResult contains the response body and error from an ChangeType request. +type ChangeTypeResult struct { + gophercloud.ErrResult +} + +// ReImageResult contains the response body and error from a ReImage request. +type ReImageResult struct { + gophercloud.ErrResult +} + +// ResetStatusResult contains the response error from a ResetStatus request. +type ResetStatusResult struct { + gophercloud.ErrResult +} diff --git a/openstack/blockstorage/v3/volumes/testing/doc.go b/openstack/blockstorage/v3/volumes/testing/doc.go index a2b24b7c19..8e4457df7e 100644 --- a/openstack/blockstorage/v3/volumes/testing/doc.go +++ b/openstack/blockstorage/v3/volumes/testing/doc.go @@ -1,2 +1,2 @@ -// volumes_v3 +// volumes unit tests package testing diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go index c203ffc18d..d8ade8fc83 100644 --- a/openstack/blockstorage/v3/volumes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go @@ -263,3 +263,386 @@ func MockCreateVolumeFromBackupResponse(t *testing.T) { }`) }) } + +func MockAttachResponse(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-attach": + { + "mountpoint": "/mnt", + "mode": "rw", + "instance_uuid": "50902f4f-a974-46a0-85e9-7efc5e22dfdd" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockBeginDetachingResponse(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-begin_detaching": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockDetachResponse(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-detach": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockUploadImageResponse(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-volume_upload_image": { + "container_format": "bare", + "force": true, + "image_name": "test", + "disk_format": "raw" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, ` +{ + "os-volume_upload_image": { + "container_format": "bare", + "display_description": null, + "id": "cd281d77-8217-4830-be95-9528227c105c", + "image_id": "ecb92d98-de08-45db-8235-bbafe317269c", + "image_name": "test", + "disk_format": "raw", + "size": 5, + "status": "uploading", + "updated_at": "2017-07-17T09:29:22.000000", + "volume_type": { + "created_at": "2016-05-04T08:54:14.000000", + "deleted": false, + "deleted_at": null, + "description": null, + "extra_specs": { + "volume_backend_name": "basic.ru-2a" + }, + "id": "b7133444-62f6-4433-8da3-70ac332229b7", + "is_public": true, + "name": "basic.ru-2a", + "updated_at": "2016-05-04T09:15:33.000000" + } + } +} + `) + }) +} + +func MockReserveResponse(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-reserve": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockUnreserveResponse(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-unreserve": {} +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockInitializeConnectionResponse(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-initialize_connection": + { + "connector": + { + "ip":"127.0.0.1", + "host":"stack", + "initiator":"iqn.1994-05.com.redhat:17cf566367d2", + "multipath": false, + "platform": "x86_64", + "os_type": "linux2" + } + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{ +"connection_info": { + "data": { + "target_portals": [ + "172.31.17.48:3260" + ], + "auth_method": "CHAP", + "auth_username": "5MLtcsTEmNN5jFVcT6ui", + "access_mode": "rw", + "target_lun": 0, + "volume_id": "cd281d77-8217-4830-be95-9528227c105c", + "target_luns": [ + 0 + ], + "target_iqns": [ + "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c" + ], + "auth_password": "x854ZY5Re3aCkdNL", + "target_discovered": false, + "encrypted": false, + "qos_specs": null, + "target_iqn": "iqn.2010-10.org.openstack:volume-cd281d77-8217-4830-be95-9528227c105c", + "target_portal": "172.31.17.48:3260" + }, + "driver_volume_type": "iscsi" + } + }`) + }) +} + +func MockTerminateConnectionResponse(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-terminate_connection": + { + "connector": + { + "ip":"127.0.0.1", + "host":"stack", + "initiator":"iqn.1994-05.com.redhat:17cf566367d2", + "multipath": true, + "platform": "x86_64", + "os_type": "linux2" + } + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockExtendSizeResponse(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-extend": + { + "new_size": 3 + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockForceDeleteResponse(t *testing.T) { + th.Mux.HandleFunc("/volumes/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.TestBody(t, r, `{"os-force_delete":""}`) + w.WriteHeader(http.StatusAccepted) + }) +} + +func MockSetImageMetadataResponse(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-set_image_metadata": { + "metadata": { + "label": "test" + } + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, `{}`) + }) +} + +func MockSetBootableResponse(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-set_bootable": { + "bootable": true + } +} + `) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", "0") + w.WriteHeader(http.StatusOK) + }) +} + +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) { + 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-retype": + { + "new_type": "ssd", + "migration_policy": "on-demand" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + + 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/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index beb934d09e..69d64a6322 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumehost" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" @@ -281,3 +282,234 @@ func TestCreateFromBackup(t *testing.T) { th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, *v.BackupID, "20c792f0-bb03-434f-b653-06ef238e337e") } + +func TestAttach(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockAttachResponse(t) + + options := &volumes.AttachOpts{ + MountPoint: "/mnt", + Mode: "rw", + InstanceUUID: "50902f4f-a974-46a0-85e9-7efc5e22dfdd", + } + err := volumes.Attach(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestBeginDetaching(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockBeginDetachingResponse(t) + + err := volumes.BeginDetaching(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDetach(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockDetachResponse(t) + + err := volumes.Detach(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", &volumes.DetachOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUploadImage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + MockUploadImageResponse(t) + options := &volumes.UploadImageOpts{ + ContainerFormat: "bare", + DiskFormat: "raw", + ImageName: "test", + Force: true, + } + + actual, err := volumes.UploadImage(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() + th.AssertNoErr(t, err) + + expected := volumes.VolumeImage{ + VolumeID: "cd281d77-8217-4830-be95-9528227c105c", + ContainerFormat: "bare", + DiskFormat: "raw", + Description: "", + ImageID: "ecb92d98-de08-45db-8235-bbafe317269c", + ImageName: "test", + Size: 5, + Status: "uploading", + UpdatedAt: time.Date(2017, 7, 17, 9, 29, 22, 0, time.UTC), + VolumeType: volumes.ImageVolumeType{ + ID: "b7133444-62f6-4433-8da3-70ac332229b7", + Name: "basic.ru-2a", + Description: "", + IsPublic: true, + ExtraSpecs: map[string]interface{}{"volume_backend_name": "basic.ru-2a"}, + QosSpecsID: "", + Deleted: false, + DeletedAt: time.Time{}, + CreatedAt: time.Date(2016, 5, 4, 8, 54, 14, 0, time.UTC), + UpdatedAt: time.Date(2016, 5, 4, 9, 15, 33, 0, time.UTC), + }, + } + th.AssertDeepEquals(t, expected, actual) +} + +func TestReserve(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockReserveResponse(t) + + err := volumes.Reserve(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUnreserve(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockUnreserveResponse(t) + + err := volumes.Unreserve(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestInitializeConnection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockInitializeConnectionResponse(t) + + options := &volumes.InitializeConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Disabled, + Platform: "x86_64", + OSType: "linux2", + } + _, err := volumes.InitializeConnection(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).Extract() + th.AssertNoErr(t, err) +} + +func TestTerminateConnection(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockTerminateConnectionResponse(t) + + options := &volumes.TerminateConnectionOpts{ + IP: "127.0.0.1", + Host: "stack", + Initiator: "iqn.1994-05.com.redhat:17cf566367d2", + Multipath: gophercloud.Enabled, + Platform: "x86_64", + OSType: "linux2", + } + err := volumes.TerminateConnection(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestExtendSize(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockExtendSizeResponse(t) + + options := &volumes.ExtendSizeOpts{ + NewSize: 3, + } + + err := volumes.ExtendSize(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestForceDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockForceDeleteResponse(t) + + res := volumes.ForceDelete(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22") + th.AssertNoErr(t, res.Err) +} + +func TestSetImageMetadata(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetImageMetadataResponse(t) + + options := &volumes.ImageMetadataOpts{ + Metadata: map[string]string{ + "label": "test", + }, + } + + err := volumes.SetImageMetadata(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestSetBootable(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockSetBootableResponse(t) + + options := volumes.BootableOpts{ + Bootable: true, + } + + err := volumes.SetBootable(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestReImage(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockReImageResponse(t) + + options := volumes.ReImageOpts{ + ImageID: "71543ced-a8af-45b6-a5c4-a46282108a90", + ReImageReserved: false, + } + + err := volumes.ReImage(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestChangeType(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + MockChangeTypeResponse(t) + + options := &volumes.ChangeTypeOpts{ + NewType: "ssd", + MigrationPolicy: "on-demand", + } + + err := volumes.ChangeType(context.TODO(), 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 := &volumes.ResetStatusOpts{ + Status: "error", + AttachStatus: "detached", + MigrationStatus: "migrating", + } + + err := volumes.ResetStatus(context.TODO(), client.ServiceClient(), "cd281d77-8217-4830-be95-9528227c105c", options).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/blockstorage/v3/volumes/urls.go b/openstack/blockstorage/v3/volumes/urls.go index b8704306c9..a73e2d2b13 100644 --- a/openstack/blockstorage/v3/volumes/urls.go +++ b/openstack/blockstorage/v3/volumes/urls.go @@ -21,3 +21,7 @@ func getURL(c *gophercloud.ServiceClient, id string) string { func updateURL(c *gophercloud.ServiceClient, id string) string { return deleteURL(c, id) } + +func actionURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("volumes", id, "action") +} From a29a1e0229f0b0e33182fe9d99dcbf5b19d3a15c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 15:00:52 +0000 Subject: [PATCH 1804/2296] blockstorage: Merge volumehost extension This is just an additional field in the Volume struct. Nice and easy. Signed-off-by: Stephen Finucane --- .../blockstorage/extensions/volumehost/doc.go | 26 ------------------- .../extensions/volumehost/results.go | 7 ----- openstack/blockstorage/v2/volumes/results.go | 2 ++ openstack/blockstorage/v3/volumes/results.go | 2 ++ .../v3/volumes/testing/requests_test.go | 4 +-- 5 files changed, 6 insertions(+), 35 deletions(-) delete mode 100644 openstack/blockstorage/extensions/volumehost/doc.go delete mode 100644 openstack/blockstorage/extensions/volumehost/results.go diff --git a/openstack/blockstorage/extensions/volumehost/doc.go b/openstack/blockstorage/extensions/volumehost/doc.go deleted file mode 100644 index a4fc1fb549..0000000000 --- a/openstack/blockstorage/extensions/volumehost/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Package volumehost provides the ability to extend a volume result with -information about the Openstack host holding the volume. Example: - - type VolumeWithHost struct { - volumes.Volume - volumehost.VolumeHostExt - } - - var allVolumes []VolumeWithHost - - allPages, err := volumes.List(client, nil).AllPages(context.TODO()) - if err != nil { - panic("Unable to retrieve volumes: %s", err) - } - - err = volumes.ExtractVolumesInto(allPages, &allVolumes) - if err != nil { - panic("Unable to extract volumes: %s", err) - } - - for _, volume := range allVolumes { - fmt.Println(volume.Host) - } -*/ -package volumehost diff --git a/openstack/blockstorage/extensions/volumehost/results.go b/openstack/blockstorage/extensions/volumehost/results.go deleted file mode 100644 index 5434f37723..0000000000 --- a/openstack/blockstorage/extensions/volumehost/results.go +++ /dev/null @@ -1,7 +0,0 @@ -package volumehost - -// VolumeHostExt is an extension to the base Volume object -type VolumeHostExt struct { - // Host is the identifier of the host holding the volume. - Host string `json:"os-vol-host-attr:host"` -} diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/blockstorage/v2/volumes/results.go index 8119447522..1be359e099 100644 --- a/openstack/blockstorage/v2/volumes/results.go +++ b/openstack/blockstorage/v2/volumes/results.go @@ -75,6 +75,8 @@ type Volume struct { ConsistencyGroupID string `json:"consistencygroup_id"` // Multiattach denotes if the volume is multi-attach capable. Multiattach bool `json:"multiattach"` + // Host is the identifier of the host holding the volume. + Host string `json:"os-vol-host-attr:host"` } func (r *Volume) UnmarshalJSON(b []byte) error { diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index 4b6e524558..bc4e63a869 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -82,6 +82,8 @@ type Volume struct { Multiattach bool `json:"multiattach"` // Image metadata entries, only included for volumes that were created from an image, or from a snapshot of a volume originally created from an image. VolumeImageMetadata map[string]string `json:"volume_image_metadata"` + // Host is the identifier of the host holding the volume. + Host string `json:"os-vol-host-attr:host"` } // UnmarshalJSON another unmarshalling function diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index 69d64a6322..97ad9d1c27 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumehost" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/v2/pagination" @@ -49,6 +48,7 @@ func TestListWithExtensions(t *testing.T) { CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), Description: "", Encrypted: false, + Host: "host-001", Metadata: map[string]string{"foo": "bar"}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", @@ -106,7 +106,6 @@ func TestListAllWithExtensions(t *testing.T) { type VolumeWithExt struct { volumes.Volume volumetenants.VolumeTenantExt - volumehost.VolumeHostExt } allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) @@ -151,6 +150,7 @@ func TestListAll(t *testing.T) { CreatedAt: time.Date(2015, 9, 17, 3, 35, 3, 0, time.UTC), Description: "", Encrypted: false, + Host: "host-001", Metadata: map[string]string{"foo": "bar"}, Multiattach: false, //TenantID: "304dc00909ac4d0da6c62d816bcb3459", From f35555779acfa4c7f6dbde8c661cdd3a766f1ae5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 15:02:57 +0000 Subject: [PATCH 1805/2296] blockstorage: Merge volumetenants extension Another case of an additional field in the Volume struct. Signed-off-by: Stephen Finucane --- .../blockstorage/v2/volumetenants_test.go | 43 +++++++++++++++++++ .../{extensions => v3}/volumetenants_test.go | 15 ++----- .../extensions/volumetenants/doc.go | 26 ----------- .../extensions/volumetenants/results.go | 7 --- openstack/blockstorage/v2/volumes/results.go | 2 + .../v2/volumes/testing/requests_test.go | 27 ++++-------- openstack/blockstorage/v3/volumes/results.go | 2 + .../v3/volumes/testing/requests_test.go | 29 +++++-------- 8 files changed, 70 insertions(+), 81 deletions(-) create mode 100644 internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go rename internal/acceptance/openstack/blockstorage/{extensions => v3}/volumetenants_test.go (68%) delete mode 100644 openstack/blockstorage/extensions/volumetenants/doc.go delete mode 100644 openstack/blockstorage/extensions/volumetenants/results.go diff --git a/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go new file mode 100644 index 0000000000..3c52c49cc7 --- /dev/null +++ b/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go @@ -0,0 +1,43 @@ +//go:build acceptance || blockstorage +// +build acceptance blockstorage + +package v2 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestVolumeTenants(t *testing.T) { + var allVolumes []volumes.Volume + + clients.SkipReleasesAbove(t, "stable/ocata") + + client, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + listOpts := volumes.ListOpts{ + Name: "I SHOULD NOT EXIST", + } + allPages, err := volumes.List(client, listOpts).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(allVolumes)) + + volume1, err := CreateVolume(t, client) + th.AssertNoErr(t, err) + defer DeleteVolume(t, client, volume1) + + allPages, err = volumes.List(client, nil).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + err = volumes.ExtractVolumesInto(allPages, &allVolumes) + th.AssertNoErr(t, err) + th.AssertEquals(t, true, len(allVolumes) > 0) +} diff --git a/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go similarity index 68% rename from internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go rename to internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go index 70a2681176..65eb1b901e 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/volumetenants_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go @@ -1,26 +1,19 @@ //go:build acceptance || blockstorage // +build acceptance blockstorage -package extensions +package v3 import ( "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v3" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestVolumeTenants(t *testing.T) { - type volumeWithTenant struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - - var allVolumes []volumeWithTenant + var allVolumes []volumes.Volume client, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) @@ -35,9 +28,9 @@ func TestVolumeTenants(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(allVolumes)) - volume1, err := blockstorage.CreateVolume(t, client) + volume1, err := CreateVolume(t, client) th.AssertNoErr(t, err) - defer blockstorage.DeleteVolume(t, client, volume1) + defer DeleteVolume(t, client, volume1) allPages, err = volumes.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) diff --git a/openstack/blockstorage/extensions/volumetenants/doc.go b/openstack/blockstorage/extensions/volumetenants/doc.go deleted file mode 100644 index 1e200a12b1..0000000000 --- a/openstack/blockstorage/extensions/volumetenants/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Package volumetenants provides the ability to extend a volume result with -tenant/project information. Example: - - type VolumeWithTenant struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - - var allVolumes []VolumeWithTenant - - allPages, err := volumes.List(client, nil).AllPages(context.TODO()) - if err != nil { - panic("Unable to retrieve volumes: %s", err) - } - - err = volumes.ExtractVolumesInto(allPages, &allVolumes) - if err != nil { - panic("Unable to extract volumes: %s", err) - } - - for _, volume := range allVolumes { - fmt.Println(volume.TenantID) - } -*/ -package volumetenants diff --git a/openstack/blockstorage/extensions/volumetenants/results.go b/openstack/blockstorage/extensions/volumetenants/results.go deleted file mode 100644 index 821e523b7f..0000000000 --- a/openstack/blockstorage/extensions/volumetenants/results.go +++ /dev/null @@ -1,7 +0,0 @@ -package volumetenants - -// VolumeTenantExt is an extension to the base Volume object -type VolumeTenantExt struct { - // TenantID is the id of the project that owns the volume. - TenantID string `json:"os-vol-tenant-attr:tenant_id"` -} diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/blockstorage/v2/volumes/results.go index 1be359e099..ed21e14e70 100644 --- a/openstack/blockstorage/v2/volumes/results.go +++ b/openstack/blockstorage/v2/volumes/results.go @@ -77,6 +77,8 @@ type Volume struct { Multiattach bool `json:"multiattach"` // Host is the identifier of the host holding the volume. Host string `json:"os-vol-host-attr:host"` + // TenantID is the id of the project that owns the volume. + TenantID string `json:"os-vol-tenant-attr:tenant_id"` } func (r *Volume) UnmarshalJSON(b []byte) error { diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 91bb67cb8a..0cf11a6651 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -50,7 +49,7 @@ func TestListWithExtensions(t *testing.T) { Encrypted: false, Metadata: map[string]string{"foo": "bar"}, Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", @@ -73,7 +72,7 @@ func TestListWithExtensions(t *testing.T) { Encrypted: false, Metadata: map[string]string{}, Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", @@ -102,15 +101,10 @@ func TestListAllWithExtensions(t *testing.T) { MockListResponse(t) - type VolumeWithExt struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) - var actual []VolumeWithExt + var actual []volumes.Volume err = volumes.ExtractVolumesInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(actual)) @@ -149,7 +143,7 @@ func TestListAll(t *testing.T) { Encrypted: false, Metadata: map[string]string{"foo": "bar"}, Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", @@ -172,7 +166,7 @@ func TestListAll(t *testing.T) { Encrypted: false, Metadata: map[string]string{}, Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", @@ -245,15 +239,12 @@ func TestGetWithExtensions(t *testing.T) { MockGetResponse(t) - var s struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + var v volumes.Volume + err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&v) th.AssertNoErr(t, err) - th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) + th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", v.TenantID) - err = volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) + err = volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(v) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index bc4e63a869..b3f1d19494 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -84,6 +84,8 @@ type Volume struct { VolumeImageMetadata map[string]string `json:"volume_image_metadata"` // Host is the identifier of the host holding the volume. Host string `json:"os-vol-host-attr:host"` + // TenantID is the id of the project that owns the volume. + TenantID string `json:"os-vol-tenant-attr:tenant_id"` } // UnmarshalJSON another unmarshalling function diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index 97ad9d1c27..8a97010767 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/volumetenants" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -51,7 +50,7 @@ func TestListWithExtensions(t *testing.T) { Host: "host-001", Metadata: map[string]string{"foo": "bar"}, Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", @@ -74,7 +73,7 @@ func TestListWithExtensions(t *testing.T) { Encrypted: false, Metadata: map[string]string{}, Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", @@ -103,15 +102,10 @@ func TestListAllWithExtensions(t *testing.T) { MockListResponse(t) - type VolumeWithExt struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - allPages, err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) - var actual []VolumeWithExt + var actual []volumes.Volume err = volumes.ExtractVolumesInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, 2, len(actual)) @@ -153,7 +147,7 @@ func TestListAll(t *testing.T) { Host: "host-001", Metadata: map[string]string{"foo": "bar"}, Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", @@ -176,7 +170,7 @@ func TestListAll(t *testing.T) { Encrypted: false, Metadata: map[string]string{}, Multiattach: false, - //TenantID: "304dc00909ac4d0da6c62d816bcb3459", + TenantID: "304dc00909ac4d0da6c62d816bcb3459", //ReplicationDriverData: "", //ReplicationExtendedStatus: "", ReplicationStatus: "disabled", @@ -249,16 +243,13 @@ func TestGetWithExtensions(t *testing.T) { MockGetResponse(t) - var s struct { - volumes.Volume - volumetenants.VolumeTenantExt - } - err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&s) + var v volumes.Volume + err := volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(&v) th.AssertNoErr(t, err) - th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", s.TenantID) - th.AssertEquals(t, "centos", s.Volume.VolumeImageMetadata["image_name"]) + th.AssertEquals(t, "304dc00909ac4d0da6c62d816bcb3459", v.TenantID) + th.AssertEquals(t, "centos", v.VolumeImageMetadata["image_name"]) - err = volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(s) + err = volumes.Get(context.TODO(), client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").ExtractInto(v) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } From 4b56f7d2b4e602a3692c17c53eb6278f6d3e7161 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 15:44:42 +0000 Subject: [PATCH 1806/2296] blockstorage: Merge schedulerhints extension This ones a little complicated since it's both an extension of the Volume request struct and a validator for same. Signed-off-by: Stephen Finucane --- .../blockstorage/extensions/extensions.go | 4 - .../openstack/blockstorage/extensions/pkg.go | 3 - .../blockstorage/v2/schedulerhints_test.go | 63 +++++++++ .../{extensions => v3}/schedulerhints_test.go | 20 +-- .../extensions/schedulerhints/doc.go | 52 -------- .../extensions/schedulerhints/requests.go | 124 ------------------ .../extensions/schedulerhints/testing/doc.go | 2 - .../schedulerhints/testing/requests_test.go | 58 -------- openstack/blockstorage/v2/volumes/doc.go | 38 ++++++ openstack/blockstorage/v2/volumes/requests.go | 114 +++++++++++++++- .../v2/volumes/testing/requests_test.go | 45 +++++++ openstack/blockstorage/v3/volumes/doc.go | 40 +++++- openstack/blockstorage/v3/volumes/requests.go | 114 +++++++++++++++- .../v3/volumes/testing/requests_test.go | 45 +++++++ 14 files changed, 463 insertions(+), 259 deletions(-) delete mode 100644 internal/acceptance/openstack/blockstorage/extensions/extensions.go delete mode 100644 internal/acceptance/openstack/blockstorage/extensions/pkg.go create mode 100644 internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go rename internal/acceptance/openstack/blockstorage/{extensions => v3}/schedulerhints_test.go (79%) delete mode 100644 openstack/blockstorage/extensions/schedulerhints/doc.go delete mode 100644 openstack/blockstorage/extensions/schedulerhints/requests.go delete mode 100644 openstack/blockstorage/extensions/schedulerhints/testing/doc.go delete mode 100644 openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go diff --git a/internal/acceptance/openstack/blockstorage/extensions/extensions.go b/internal/acceptance/openstack/blockstorage/extensions/extensions.go deleted file mode 100644 index 27bb88251c..0000000000 --- a/internal/acceptance/openstack/blockstorage/extensions/extensions.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package extensions contains common functions for creating block storage -// resources that are extensions of the block storage API. See the `*_test.go` -// files for example usages. -package extensions diff --git a/internal/acceptance/openstack/blockstorage/extensions/pkg.go b/internal/acceptance/openstack/blockstorage/extensions/pkg.go deleted file mode 100644 index f18039dcb1..0000000000 --- a/internal/acceptance/openstack/blockstorage/extensions/pkg.go +++ /dev/null @@ -1,3 +0,0 @@ -// The extensions package contains acceptance tests for the Openstack Cinder extensions service. - -package extensions diff --git a/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go new file mode 100644 index 0000000000..cb8beeaea1 --- /dev/null +++ b/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go @@ -0,0 +1,63 @@ +//go:build acceptance || blockstorage +// +build acceptance blockstorage + +package v2 + +import ( + "context" + "testing" + "time" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestSchedulerHints(t *testing.T) { + clients.SkipReleasesAbove(t, "stable/ocata") + clients.RequireLong(t) + + client, err := clients.NewBlockStorageV2Client() + th.AssertNoErr(t, err) + + volumeName := tools.RandomString("ACPTTEST", 16) + createOpts := volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + + volume1, err := volumes.Create(context.TODO(), client, createOpts).Extract() + th.AssertNoErr(t, err) + + ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx, client, volume1.ID, "available") + th.AssertNoErr(t, err) + defer volumes.Delete(context.TODO(), client, volume1.ID, volumes.DeleteOpts{}) + + volumeName = tools.RandomString("ACPTTEST", 16) + schedulerHints := volumes.SchedulerHints{ + SameHost: []string{ + volume1.ID, + }, + } + createOpts = volumes.CreateOpts{ + Size: 1, + Name: volumeName, + SchedulerHints: schedulerHints, + } + + volume2, err := volumes.Create(context.TODO(), client, createOpts).Extract() + th.AssertNoErr(t, err) + + ctx2, cancel := context.WithTimeout(context.TODO(), 60*time.Second) + defer cancel() + + err = volumes.WaitForStatus(ctx2, client, volume2.ID, "available") + th.AssertNoErr(t, err) + + err = volumes.Delete(context.TODO(), client, volume2.ID, volumes.DeleteOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go similarity index 79% rename from internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go rename to internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go index f843af073c..c8681e7753 100644 --- a/internal/acceptance/openstack/blockstorage/extensions/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go @@ -1,7 +1,7 @@ //go:build acceptance || blockstorage // +build acceptance blockstorage -package extensions +package v3 import ( "context" @@ -10,7 +10,6 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerhints" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -38,23 +37,18 @@ func TestSchedulerHints(t *testing.T) { defer volumes.Delete(context.TODO(), client, volume1.ID, volumes.DeleteOpts{}) volumeName = tools.RandomString("ACPTTEST", 16) - base := volumes.CreateOpts{ - Size: 1, - Name: volumeName, - } - - schedulerHints := schedulerhints.SchedulerHints{ + schedulerHints := volumes.SchedulerHints{ SameHost: []string{ volume1.ID, }, } - - createOptsWithHints := schedulerhints.CreateOptsExt{ - VolumeCreateOptsBuilder: base, - SchedulerHints: schedulerHints, + createOpts = volumes.CreateOpts{ + Size: 1, + Name: volumeName, + SchedulerHints: schedulerHints, } - volume2, err := volumes.Create(context.TODO(), client, createOptsWithHints).Extract() + volume2, err := volumes.Create(context.TODO(), client, createOpts).Extract() th.AssertNoErr(t, err) ctx2, cancel2 := context.WithTimeout(context.TODO(), 60*time.Second) diff --git a/openstack/blockstorage/extensions/schedulerhints/doc.go b/openstack/blockstorage/extensions/schedulerhints/doc.go deleted file mode 100644 index ee5cee01f1..0000000000 --- a/openstack/blockstorage/extensions/schedulerhints/doc.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Package schedulerhints extends the volume create request with the ability to -specify additional parameters which determine where the volume will be -created in the OpenStack cloud. - -Example to Place Volume B on a Different Host than Volume A - - schedulerHints := schedulerhints.SchedulerHints{ - DifferentHost: []string{ - "volume-a-uuid", - } - } - - volumeCreateOpts := volumes.CreateOpts{ - Name: "volume_b", - Size: 10, - } - - createOpts := schedulerhints.CreateOptsExt{ - VolumeCreateOptsBuilder: volumeCreateOpts, - SchedulerHints: schedulerHints, - } - - volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Place Volume B on the Same Host as Volume A - - schedulerHints := schedulerhints.SchedulerHints{ - SameHost: []string{ - "volume-a-uuid", - } - } - - volumeCreateOpts := volumes.CreateOpts{ - Name: "volume_b", - Size: 10 - } - - createOpts := schedulerhints.CreateOptsExt{ - VolumeCreateOptsBuilder: volumeCreateOpts, - SchedulerHints: schedulerHints, - } - - volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() - if err != nil { - panic(err) - } -*/ -package schedulerhints diff --git a/openstack/blockstorage/extensions/schedulerhints/requests.go b/openstack/blockstorage/extensions/schedulerhints/requests.go deleted file mode 100644 index b01fd0b27f..0000000000 --- a/openstack/blockstorage/extensions/schedulerhints/requests.go +++ /dev/null @@ -1,124 +0,0 @@ -package schedulerhints - -import ( - "regexp" - - "github.com/gophercloud/gophercloud/v2" -) - -// SchedulerHints represents a set of scheduling hints that are passed to the -// OpenStack scheduler. -type SchedulerHints struct { - // DifferentHost will place the volume on a different back-end that does not - // host the given volumes. - DifferentHost []string - - // SameHost will place the volume on a back-end that hosts the given volumes. - SameHost []string - - // LocalToInstance will place volume on same host on a given instance - LocalToInstance string - - // Query is a conditional statement that results in back-ends able to - // host the volume. - Query string - - // AdditionalProperies are arbitrary key/values that are not validated by nova. - AdditionalProperties map[string]interface{} -} - -// VolumeCreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type VolumeCreateOptsBuilder interface { - ToVolumeCreateMap() (map[string]interface{}, error) -} - -// CreateOptsBuilder builds the scheduler hints into a serializable format. -type CreateOptsBuilder interface { - ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) -} - -// ToVolumeSchedulerHintsMap builds the scheduler hints into a serializable format. -func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) { - sh := make(map[string]interface{}) - - uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") - - if len(opts.DifferentHost) > 0 { - for _, diffHost := range opts.DifferentHost { - if !uuidRegex.MatchString(diffHost) { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.DifferentHost" - err.Value = opts.DifferentHost - err.Info = "The hosts must be in UUID format." - return nil, err - } - } - sh["different_host"] = opts.DifferentHost - } - - if len(opts.SameHost) > 0 { - for _, sameHost := range opts.SameHost { - if !uuidRegex.MatchString(sameHost) { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.SameHost" - err.Value = opts.SameHost - err.Info = "The hosts must be in UUID format." - return nil, err - } - } - sh["same_host"] = opts.SameHost - } - - if opts.LocalToInstance != "" { - if !uuidRegex.MatchString(opts.LocalToInstance) { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" - err.Value = opts.LocalToInstance - err.Info = "The instance must be in UUID format." - return nil, err - } - sh["local_to_instance"] = opts.LocalToInstance - } - - if opts.Query != "" { - sh["query"] = opts.Query - } - - if opts.AdditionalProperties != nil { - for k, v := range opts.AdditionalProperties { - sh[k] = v - } - } - - return sh, nil -} - -// CreateOptsExt adds a SchedulerHints option to the base CreateOpts. -type CreateOptsExt struct { - VolumeCreateOptsBuilder - - // SchedulerHints provides a set of hints to the scheduler. - SchedulerHints CreateOptsBuilder -} - -// ToVolumeCreateMap adds the SchedulerHints option to the base volume creation options. -func (opts CreateOptsExt) ToVolumeCreateMap() (map[string]interface{}, error) { - base, err := opts.VolumeCreateOptsBuilder.ToVolumeCreateMap() - if err != nil { - return nil, err - } - - schedulerHints, err := opts.SchedulerHints.ToVolumeSchedulerHintsCreateMap() - if err != nil { - return nil, err - } - - if len(schedulerHints) == 0 { - return base, nil - } - - base["OS-SCH-HNT:scheduler_hints"] = schedulerHints - - return base, nil -} diff --git a/openstack/blockstorage/extensions/schedulerhints/testing/doc.go b/openstack/blockstorage/extensions/schedulerhints/testing/doc.go deleted file mode 100644 index 1915aef2fe..0000000000 --- a/openstack/blockstorage/extensions/schedulerhints/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// schedulerhints unit tests -package testing diff --git a/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go b/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go deleted file mode 100644 index 6759d41fd0..0000000000 --- a/openstack/blockstorage/extensions/schedulerhints/testing/requests_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/extensions/schedulerhints" - "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestCreateOpts(t *testing.T) { - - base := volumes.CreateOpts{ - Size: 10, - Name: "testvolume", - } - schedulerHints := schedulerhints.SchedulerHints{ - DifferentHost: []string{ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287", - }, - SameHost: []string{ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287", - }, - LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", - AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, - } - - ext := schedulerhints.CreateOptsExt{ - VolumeCreateOptsBuilder: base, - SchedulerHints: schedulerHints, - } - - expected := ` - { - "volume": { - "size": 10, - "name": "testvolume" - }, - "OS-SCH-HNT:scheduler_hints": { - "different_host": [ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287" - ], - "same_host": [ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287" - ], - "local_to_instance": "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", - "mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" - } - } - ` - actual, err := ext.ToVolumeCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) -} diff --git a/openstack/blockstorage/v2/volumes/doc.go b/openstack/blockstorage/v2/volumes/doc.go index b6a11983d6..e05f245e02 100644 --- a/openstack/blockstorage/v2/volumes/doc.go +++ b/openstack/blockstorage/v2/volumes/doc.go @@ -4,6 +4,44 @@ OpenStack Block Storage service. A volume is a detachable block storage device, akin to a USB hard drive. It can only be attached to one instance at a time. +Example of creating Volume B on a Different Host than Volume A + + schedulerHints := volumes.SchedulerHints{ + DifferentHost: []string{ + "volume-a-uuid", + } + } + + createOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10, + SchedulerHints: schedulerHints, + } + + volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example of creating Volume B on the Same Host as Volume A + + schedulerHints := volumes.SchedulerHints{ + SameHost: []string{ + "volume-a-uuid", + } + } + + createOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10 + SchedulerHints: schedulerHints, + } + + volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + Example of Creating an Image from a Volume uploadImageOpts := volumes.UploadImageOpts{ diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index e0fd482b3b..e7c288d02f 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -2,11 +2,94 @@ package volumes import ( "context" + "regexp" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) +// CreateOptsBuilder builds the scheduler hints into a serializable format. +type SchedulerHintsCreateOptsBuilder interface { + ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) +} + +// SchedulerHints represents a set of scheduling hints that are passed to the +// OpenStack scheduler. +type SchedulerHints struct { + // DifferentHost will place the volume on a different back-end that does not + // host the given volumes. + DifferentHost []string + + // SameHost will place the volume on a back-end that hosts the given volumes. + SameHost []string + + // LocalToInstance will place volume on same host on a given instance + LocalToInstance string + + // Query is a conditional statement that results in back-ends able to + // host the volume. + Query string + + // AdditionalProperies are arbitrary key/values that are not validated by nova. + AdditionalProperties map[string]interface{} +} + +// ToVolumeSchedulerHintsCreateMap assembles a request body for the scheduler +func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) { + sh := make(map[string]interface{}) + + uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") + + if len(opts.DifferentHost) > 0 { + for _, diffHost := range opts.DifferentHost { + if !uuidRegex.MatchString(diffHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.DifferentHost" + err.Value = opts.DifferentHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["different_host"] = opts.DifferentHost + } + + if len(opts.SameHost) > 0 { + for _, sameHost := range opts.SameHost { + if !uuidRegex.MatchString(sameHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.SameHost" + err.Value = opts.SameHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["same_host"] = opts.SameHost + } + + if opts.LocalToInstance != "" { + if !uuidRegex.MatchString(opts.LocalToInstance) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" + err.Value = opts.LocalToInstance + err.Info = "The instance must be in UUID format." + return nil, err + } + sh["local_to_instance"] = opts.LocalToInstance + } + + if opts.Query != "" { + sh["query"] = opts.Query + } + + if opts.AdditionalProperties != nil { + for k, v := range opts.AdditionalProperties { + sh[k] = v + } + } + + return sh, nil +} + // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { @@ -40,12 +123,41 @@ type CreateOpts struct { ImageID string `json:"imageRef,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` + // SchedulerHints provides a set of hints to the scheduler. + SchedulerHints SchedulerHintsCreateOptsBuilder } // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume") + // We intentionally don't envelope the body here since we want to strip + // some fields out + base, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + delete(base, "SchedulerHints") + + // Now we do our enveloping + base = map[string]interface{}{"volume": base} + + if opts.SchedulerHints == nil { + return base, nil + } + + schedulerHints, err := opts.SchedulerHints.ToVolumeSchedulerHintsCreateMap() + if err != nil { + return nil, err + } + + if len(schedulerHints) == 0 { + return base, nil + } + + base["OS-SCH-HNT:scheduler_hints"] = schedulerHints + + return base, err } // Create will create a new Volume based on the values in CreateOpts. To extract diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 0cf11a6651..02b1170cb2 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -210,6 +210,51 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } +func TestCreateWithSchedulerHints(t *testing.T) { + + schedulerHints := volumes.SchedulerHints{ + DifferentHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + SameHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", + AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + } + base := volumes.CreateOpts{ + Size: 10, + Name: "testvolume", + SchedulerHints: schedulerHints, + } + + expected := ` + { + "volume": { + "size": 10, + "name": "testvolume" + }, + "OS-SCH-HNT:scheduler_hints": { + "different_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "same_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "local_to_instance": "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", + "mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" + } + } + ` + actual, err := base.ToVolumeCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/blockstorage/v3/volumes/doc.go b/openstack/blockstorage/v3/volumes/doc.go index 0dcebf51d5..df5127b6c6 100644 --- a/openstack/blockstorage/v3/volumes/doc.go +++ b/openstack/blockstorage/v3/volumes/doc.go @@ -4,7 +4,45 @@ OpenStack Block Storage service. A volume is a detachable block storage device, akin to a USB hard drive. It can only be attached to one instance at a time. -Example to create a Volume from a Backup +Example of creating Volume B on a Different Host than Volume A + + schedulerHints := volumes.SchedulerHints{ + DifferentHost: []string{ + "volume-a-uuid", + } + } + + createOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10, + SchedulerHints: schedulerHints, + } + + volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example of creating Volume B on the Same Host as Volume A + + schedulerHints := volumes.SchedulerHints{ + SameHost: []string{ + "volume-a-uuid", + } + } + + createOpts := volumes.CreateOpts{ + Name: "volume_b", + Size: 10 + SchedulerHints: schedulerHints, + } + + volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example of creating a Volume from a Backup backupID := "20c792f0-bb03-434f-b653-06ef238e337e" options := volumes.CreateOpts{ diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index db668d06d1..3e373f2d55 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -2,11 +2,94 @@ package volumes import ( "context" + "regexp" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) +// CreateOptsBuilder builds the scheduler hints into a serializable format. +type SchedulerHintsCreateOptsBuilder interface { + ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) +} + +// SchedulerHints represents a set of scheduling hints that are passed to the +// OpenStack scheduler. +type SchedulerHints struct { + // DifferentHost will place the volume on a different back-end that does not + // host the given volumes. + DifferentHost []string + + // SameHost will place the volume on a back-end that hosts the given volumes. + SameHost []string + + // LocalToInstance will place volume on same host on a given instance + LocalToInstance string + + // Query is a conditional statement that results in back-ends able to + // host the volume. + Query string + + // AdditionalProperies are arbitrary key/values that are not validated by nova. + AdditionalProperties map[string]interface{} +} + +// ToVolumeSchedulerHintsCreateMap assembles a request body for the scheduler +func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) { + sh := make(map[string]interface{}) + + uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") + + if len(opts.DifferentHost) > 0 { + for _, diffHost := range opts.DifferentHost { + if !uuidRegex.MatchString(diffHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.DifferentHost" + err.Value = opts.DifferentHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["different_host"] = opts.DifferentHost + } + + if len(opts.SameHost) > 0 { + for _, sameHost := range opts.SameHost { + if !uuidRegex.MatchString(sameHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.SameHost" + err.Value = opts.SameHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["same_host"] = opts.SameHost + } + + if opts.LocalToInstance != "" { + if !uuidRegex.MatchString(opts.LocalToInstance) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" + err.Value = opts.LocalToInstance + err.Info = "The instance must be in UUID format." + return nil, err + } + sh["local_to_instance"] = opts.LocalToInstance + } + + if opts.Query != "" { + sh["query"] = opts.Query + } + + if opts.AdditionalProperties != nil { + for k, v := range opts.AdditionalProperties { + sh[k] = v + } + } + + return sh, nil +} + // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { @@ -43,12 +126,41 @@ type CreateOpts struct { BackupID string `json:"backup_id,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` + // SchedulerHints provides a set of hints to the scheduler. + SchedulerHints SchedulerHintsCreateOptsBuilder } // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "volume") + // We intentionally don't envelope the body here since we want to strip + // some fields out + base, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + delete(base, "SchedulerHints") + + // Now we do our enveloping + base = map[string]interface{}{"volume": base} + + if opts.SchedulerHints == nil { + return base, nil + } + + schedulerHints, err := opts.SchedulerHints.ToVolumeSchedulerHintsCreateMap() + if err != nil { + return nil, err + } + + if len(schedulerHints) == 0 { + return base, nil + } + + base["OS-SCH-HNT:scheduler_hints"] = schedulerHints + + return base, err } // Create will create a new Volume based on the values in CreateOpts. To extract diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index 8a97010767..bf76ba5dbe 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -214,6 +214,51 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } +func TestCreateWithSchedulerHints(t *testing.T) { + + schedulerHints := volumes.SchedulerHints{ + DifferentHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + SameHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", + AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + } + base := volumes.CreateOpts{ + Size: 10, + Name: "testvolume", + SchedulerHints: schedulerHints, + } + + expected := ` + { + "volume": { + "size": 10, + "name": "testvolume" + }, + "OS-SCH-HNT:scheduler_hints": { + "different_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "same_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "local_to_instance": "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", + "mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" + } + } + ` + actual, err := base.ToVolumeCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + func TestDelete(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From b728d964747141800c8e9e22d311ec6a704ebfc3 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Tue, 19 Mar 2024 17:34:34 +0100 Subject: [PATCH 1807/2296] baremetal: add support for servicing Adds provision states and parametes for the new "service" provisioning action. Adds hold states introduced as part of the same effort. Signed-off-by: Dmitry Tantsur --- .github/workflows/functional-baremetal.yaml | 2 +- .../openstack/baremetal/v1/baremetal.go | 82 +++++++++++++++++-- .../openstack/baremetal/v1/nodes_test.go | 30 +++++++ openstack/baremetal/v1/nodes/requests.go | 20 +++++ openstack/baremetal/v1/nodes/results.go | 3 + .../v1/nodes/testing/fixtures_test.go | 24 ++++++ .../v1/nodes/testing/requests_test.go | 22 +++++ 7 files changed, 177 insertions(+), 6 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index beccda9480..12adea5a9c 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -77,7 +77,7 @@ jobs: 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 + IRONIC_ENABLED_DEPLOY_INTERFACES=direct,fake 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' diff --git a/internal/acceptance/openstack/baremetal/v1/baremetal.go b/internal/acceptance/openstack/baremetal/v1/baremetal.go index 4bb1a19631..1212057db3 100644 --- a/internal/acceptance/openstack/baremetal/v1/baremetal.go +++ b/internal/acceptance/openstack/baremetal/v1/baremetal.go @@ -3,6 +3,7 @@ package v1 import ( "context" "testing" + "time" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -36,7 +37,15 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, e // DeleteNode deletes a bare metal node via its UUID. func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Node) { - err := nodes.Delete(context.TODO(), client, node.UUID).ExtractErr() + // Force deletion of provisioned nodes requires maintenance mode. + err := nodes.SetMaintenance(context.TODO(), client, node.UUID, nodes.MaintenanceOpts{ + Reason: "forced deletion", + }).ExtractErr() + if err != nil { + t.Fatalf("Unable to move node %s into maintenance mode: %s", node.UUID, err) + } + + err = nodes.Delete(context.TODO(), client, node.UUID).ExtractErr() if err != nil { t.Fatalf("Unable to delete node %s: %s", node.UUID, err) } @@ -67,15 +76,16 @@ func DeleteAllocation(t *testing.T, client *gophercloud.ServiceClient, allocatio t.Logf("Deleted allocation: %s", allocation.UUID) } -// CreateFakeNode creates a node with fake-hardware to use for port tests. +// CreateFakeNode creates a node with fake-hardware. func CreateFakeNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, error) { name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bare metal node: %s", name) node, err := nodes.Create(context.TODO(), client, nodes.CreateOpts{ - Name: name, - Driver: "fake-hardware", - BootInterface: "fake", + Name: name, + Driver: "fake-hardware", + BootInterface: "fake", + DeployInterface: "fake", DriverInfo: map[string]interface{}{ "ipmi_port": "6230", "ipmi_username": "admin", @@ -89,6 +99,68 @@ func CreateFakeNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Nod return node, err } +func ChangeProvisionStateAndWait(ctx context.Context, client *gophercloud.ServiceClient, node *nodes.Node, + change nodes.ProvisionStateOpts, expectedState nodes.ProvisionState) (*nodes.Node, error) { + err := nodes.ChangeProvisionState(ctx, client, node.UUID, change).ExtractErr() + if err != nil { + return node, err + } + + err = nodes.WaitForProvisionState(ctx, client, node.UUID, expectedState) + if err != nil { + return node, err + } + + return nodes.Get(ctx, client, node.UUID).Extract() +} + +// DeployFakeNode deploys a node that uses fake-hardware. +func DeployFakeNode(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Node) (*nodes.Node, error) { + ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) + defer cancel() + + currentState := node.ProvisionState + + if currentState == string(nodes.Enroll) { + t.Logf("moving fake node %s to manageable", node.UUID) + err := nodes.ChangeProvisionState(ctx, client, node.UUID, nodes.ProvisionStateOpts{ + Target: nodes.TargetManage, + }).ExtractErr() + if err != nil { + return node, err + } + + err = nodes.WaitForProvisionState(ctx, client, node.UUID, nodes.Manageable) + if err != nil { + return node, err + } + + currentState = string(nodes.Manageable) + } + + if currentState == string(nodes.Manageable) { + t.Logf("moving fake node %s to available", node.UUID) + err := nodes.ChangeProvisionState(ctx, client, node.UUID, nodes.ProvisionStateOpts{ + Target: nodes.TargetProvide, + }).ExtractErr() + if err != nil { + return node, err + } + + err = nodes.WaitForProvisionState(ctx, client, node.UUID, nodes.Available) + if err != nil { + return node, err + } + + currentState = string(nodes.Available) + } + + t.Logf("deploying fake node %s", node.UUID) + return ChangeProvisionStateAndWait(ctx, client, node, nodes.ProvisionStateOpts{ + Target: nodes.TargetActive, + }, nodes.Active) +} + // CreatePort - creates a port for a node with a fixed Address func CreatePort(t *testing.T, client *gophercloud.ServiceClient, node *nodes.Node) (*ports.Port, error) { mac := "e6:72:1f:52:00:f4" diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index d479bc0797..72e1287bdc 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -243,3 +243,33 @@ func TestNodesVirtualMedia(t *testing.T) { err = nodes.DetachVirtualMedia(context.TODO(), client, node.UUID, nodes.DetachVirtualMediaOpts{}).ExtractErr() th.AssertNoErr(t, err) } + +func TestNodesServicingHold(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/2023.2") + clients.RequireLong(t) + + client, err := clients.NewBareMetalV1Client() + th.AssertNoErr(t, err) + client.Microversion = "1.87" + + node, err := CreateFakeNode(t, client) + th.AssertNoErr(t, err) + defer DeleteNode(t, client, node) + + node, err = DeployFakeNode(t, client, node) + th.AssertNoErr(t, err) + + ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) + defer cancel() + + _, err = ChangeProvisionStateAndWait(ctx, client, node, nodes.ProvisionStateOpts{ + Target: nodes.TargetService, + ServiceSteps: []nodes.ServiceStep{ + { + Interface: nodes.InterfaceDeploy, + Step: nodes.StepReboot, + }, + }, + }, nodes.Active) + th.AssertNoErr(t, err) +} diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index f3989dde90..ad913515f6 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -28,11 +28,13 @@ const ( Deploying ProvisionState = "deploying" DeployFail ProvisionState = "deploy failed" DeployDone ProvisionState = "deploy complete" + DeployHold ProvisionState = "deploy hold" Deleting ProvisionState = "deleting" Deleted ProvisionState = "deleted" Cleaning ProvisionState = "cleaning" CleanWait ProvisionState = "clean wait" CleanFail ProvisionState = "clean failed" + CleanHold ProvisionState = "clean hold" Error ProvisionState = "error" Rebuild ProvisionState = "rebuild" Inspecting ProvisionState = "inspecting" @@ -46,6 +48,10 @@ const ( UnrescueFail ProvisionState = "unrescue failed" RescueWait ProvisionState = "rescue wait" Unrescuing ProvisionState = "unrescuing" + Servicing ProvisionState = "servicing" + ServiceWait ProvisionState = "service wait" + ServiceFail ProvisionState = "service fail" + ServiceHold ProvisionState = "service hold" ) // TargetProvisionState is used when setting the provision state for a node. @@ -63,6 +69,16 @@ const ( TargetRescue TargetProvisionState = "rescue" TargetUnrescue TargetProvisionState = "unrescue" TargetRebuild TargetProvisionState = "rebuild" + TargetService TargetProvisionState = "service" + TargetUnhold TargetProvisionState = "unhold" +) + +const ( + StepHold string = "hold" + StepWait string = "wait" + StepPowerOn string = "power_on" + StepPowerOff string = "power_off" + StepReboot string = "reboot" ) // ListOpts allows the filtering and sorting of paginated collections through @@ -430,6 +446,9 @@ type CleanStep struct { Args map[string]interface{} `json:"args,omitempty"` } +// A service step looks the same as a cleaning step. +type ServiceStep = CleanStep + // A deploy step has required keys ‘interface’, ‘step’, ’args’ and ’priority’. // The value for ‘args’ is a keyword variable argument dictionary that is passed to the deploy step // method. Priority is a numeric priority at which the step is running. @@ -461,6 +480,7 @@ type ProvisionStateOpts struct { ConfigDrive interface{} `json:"configdrive,omitempty"` CleanSteps []CleanStep `json:"clean_steps,omitempty"` DeploySteps []DeployStep `json:"deploy_steps,omitempty"` + ServiceSteps []ServiceStep `json:"service_steps,omitempty"` RescuePassword string `json:"rescue_password,omitempty"` } diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 18fbc56cfb..7bfd5406fa 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -183,6 +183,9 @@ type Node struct { // Current deploy step. DeployStep map[string]interface{} `json:"deploy_step"` + // Current service step. + ServiceStep map[string]interface{} `json:"service_step"` + // String which can be used by external schedulers to identify this Node as a unit of a specific type of resource. // For more details, see: https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html ResourceClass string `json:"resource_class"` diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index 0e4859e9dd..669c38e929 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -614,6 +614,21 @@ const NodeProvisionStateConfigDriveBody = ` } ` +const NodeProvisionStateServiceBody = ` +{ + "target": "service", + "service_steps": [ + { + "interface": "bios", + "step": "apply_configuration", + "args": { + "settings": [] + } + } + ] +} +` + const NodeBIOSSettingsBody = ` { "bios": [ @@ -1458,6 +1473,15 @@ func HandleNodeChangeProvisionStateConfigDrive(t *testing.T) { }) } +func HandleNodeChangeProvisionStateService(t *testing.T) { + th.Mux.HandleFunc("/nodes/1234asdf/states/provision", 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, NodeProvisionStateServiceBody) + w.WriteHeader(http.StatusAccepted) + }) +} + // HandleChangePowerStateSuccessfully sets up the test server to respond to a change power state request for a node func HandleChangePowerStateSuccessfully(t *testing.T) { th.Mux.HandleFunc("/nodes/1234asdf/states/power", func(w http.ResponseWriter, r *http.Request) { diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 33e1640393..fa5b922b8d 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -379,6 +379,28 @@ func TestCleanStepRequiresStep(t *testing.T) { } } +func TestNodeChangeProvisionStateService(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNodeChangeProvisionStateService(t) + + c := client.ServiceClient() + err := nodes.ChangeProvisionState(context.TODO(), c, "1234asdf", nodes.ProvisionStateOpts{ + Target: nodes.TargetService, + ServiceSteps: []nodes.ServiceStep{ + { + Interface: nodes.InterfaceBIOS, + Step: "apply_configuration", + Args: map[string]interface{}{ + "settings": []string{}, + }, + }, + }, + }).ExtractErr() + + th.AssertNoErr(t, err) +} + func TestChangePowerState(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 07d392359662aeb6e02199cbc1a2b7d2f99b674c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 23 Feb 2024 00:07:04 +0000 Subject: [PATCH 1808/2296] compute: Merge standalone extensions Extensions are no longer a thing in any OpenStack service except Neutron. As such, maintaining what were formerly extensions in a separate module makes little sense and actively hurts us as these extensions are themselves extended with new microversions. Resolve this for the compute service now. In this case, we simply migrate the various standalone extensions (i.e. the extensions that did not build on existing resources) into the v2 modules. This appears like we are creating a lot of new code, but we simply ran the following Python script (because Bash arrays are awful): import shutil for extension in { 'aggregates', 'attachinterfaces', 'defsecrules', 'diagnostics', 'floatingips', 'hypervisors', 'instanceactions', 'limits', 'networks', 'quotasets', 'remoteconsoles', 'secgroups', 'servergroups', 'services', 'tags', 'tenantnetworks', 'usage', 'volumeattach', }: src = f'openstack/compute/v2/extensions/{extension}' dst = f'openstack/compute/v2/{extension}' shutil.move(src, dst) We then fix the imports in the new files and then 'git add .' Once this is done, we need to do something very similar for the tests for these "extensions" in 'internal/acceptance/compute/extensions'. Once again we move the code to the v2 modules, but this time we must update imports. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/aggregates_test.go | 4 ++-- .../compute/v2/bootfromvolume_test.go | 2 +- .../openstack/compute/v2/compute.go | 24 +++++++++---------- .../openstack/compute/v2/defsecrules_test.go | 2 +- .../openstack/compute/v2/diagnostics_test.go | 2 +- .../openstack/compute/v2/floatingip_test.go | 2 +- .../openstack/compute/v2/hypervisors_test.go | 2 +- .../compute/v2/instance_actions_test.go | 2 +- .../openstack/compute/v2/keypairs_test.go | 2 +- .../openstack/compute/v2/limits_test.go | 2 +- .../openstack/compute/v2/network_test.go | 2 +- .../openstack/compute/v2/quotaset_test.go | 2 +- .../openstack/compute/v2/secgroup_test.go | 2 +- .../openstack/compute/v2/servergroup_test.go | 2 +- .../openstack/compute/v2/servers_test.go | 4 ++-- .../openstack/compute/v2/services_test.go | 2 +- .../compute/v2/tenantnetworks_test.go | 2 +- .../openstack/compute/v2/usage_test.go | 2 +- .../v2/{extensions => }/aggregates/doc.go | 0 .../{extensions => }/aggregates/requests.go | 0 .../v2/{extensions => }/aggregates/results.go | 0 .../aggregates/testing/fixtures_test.go | 2 +- .../aggregates/testing/requests_test.go | 2 +- .../v2/{extensions => }/aggregates/urls.go | 0 .../{extensions => }/attachinterfaces/doc.go | 0 .../attachinterfaces/requests.go | 0 .../attachinterfaces/results.go | 0 .../attachinterfaces/testing/doc.go | 0 .../attachinterfaces/testing/fixtures_test.go | 2 +- .../attachinterfaces/testing/requests_test.go | 2 +- .../{extensions => }/attachinterfaces/urls.go | 0 .../v2/{extensions => }/defsecrules/doc.go | 0 .../{extensions => }/defsecrules/requests.go | 0 .../{extensions => }/defsecrules/results.go | 4 ++-- .../defsecrules/testing/doc.go | 0 .../defsecrules/testing/fixtures_test.go | 0 .../defsecrules/testing/requests_test.go | 4 ++-- .../v2/{extensions => }/defsecrules/urls.go | 0 .../v2/{extensions => }/diagnostics/doc.go | 0 .../{extensions => }/diagnostics/requests.go | 0 .../{extensions => }/diagnostics/results.go | 0 .../diagnostics/testing/fixtures_test.go | 0 .../diagnostics/testing/requests_test.go | 2 +- .../v2/{extensions => }/diagnostics/urls.go | 0 .../v2/{extensions => }/floatingips/doc.go | 0 .../{extensions => }/floatingips/requests.go | 0 .../{extensions => }/floatingips/results.go | 0 .../floatingips/testing/doc.go | 0 .../floatingips/testing/fixtures_test.go | 2 +- .../floatingips/testing/requests_test.go | 2 +- .../v2/{extensions => }/floatingips/urls.go | 0 .../v2/{extensions => }/hypervisors/doc.go | 0 .../{extensions => }/hypervisors/requests.go | 0 .../{extensions => }/hypervisors/results.go | 0 .../hypervisors/testing/fixtures_test.go | 2 +- .../hypervisors/testing/requests_test.go | 2 +- .../v2/{extensions => }/hypervisors/urls.go | 0 .../{extensions => }/instanceactions/doc.go | 0 .../instanceactions/request.go | 0 .../instanceactions/results.go | 0 .../instanceactions/testing/doc.go | 0 .../instanceactions/testing/fixtures_test.go | 2 +- .../instanceactions/testing/request_test.go | 2 +- .../{extensions => }/instanceactions/urls.go | 0 .../v2/{extensions => }/keypairs/doc.go | 0 .../v2/{extensions => }/keypairs/requests.go | 0 .../v2/{extensions => }/keypairs/results.go | 0 .../{extensions => }/keypairs/testing/doc.go | 0 .../keypairs/testing/fixtures_test.go | 2 +- .../keypairs/testing/requests_test.go | 2 +- .../v2/{extensions => }/keypairs/urls.go | 0 .../compute/v2/{extensions => }/limits/doc.go | 0 .../v2/{extensions => }/limits/requests.go | 0 .../v2/{extensions => }/limits/results.go | 0 .../limits/testing/fixtures_test.go | 2 +- .../limits/testing/requests_test.go | 2 +- .../v2/{extensions => }/limits/urls.go | 0 .../v2/{extensions => }/networks/doc.go | 0 .../v2/{extensions => }/networks/requests.go | 0 .../v2/{extensions => }/networks/results.go | 0 .../{extensions => }/networks/testing/doc.go | 0 .../networks/testing/fixtures_test.go | 2 +- .../networks/testing/requests_test.go | 2 +- .../v2/{extensions => }/networks/urls.go | 0 .../v2/{extensions => }/quotasets/doc.go | 0 .../v2/{extensions => }/quotasets/requests.go | 0 .../v2/{extensions => }/quotasets/results.go | 0 .../{extensions => }/quotasets/testing/doc.go | 0 .../quotasets/testing/fixtures_test.go | 8 +++---- .../quotasets/testing/requests_test.go | 2 +- .../v2/{extensions => }/quotasets/urls.go | 0 .../v2/{extensions => }/remoteconsoles/doc.go | 0 .../remoteconsoles/requests.go | 0 .../remoteconsoles/results.go | 0 .../remoteconsoles/testing/doc.go | 0 .../remoteconsoles/testing/fixtures_test.go | 0 .../remoteconsoles/testing/requests_test.go | 2 +- .../{extensions => }/remoteconsoles/urls.go | 0 .../v2/{extensions => }/secgroups/doc.go | 0 .../v2/{extensions => }/secgroups/requests.go | 0 .../v2/{extensions => }/secgroups/results.go | 0 .../{extensions => }/secgroups/testing/doc.go | 0 .../secgroups/testing/fixtures_test.go | 0 .../secgroups/testing/requests_test.go | 2 +- .../v2/{extensions => }/secgroups/urls.go | 0 .../v2/{extensions => }/servergroups/doc.go | 0 .../{extensions => }/servergroups/requests.go | 0 .../{extensions => }/servergroups/results.go | 0 .../servergroups/testing/doc.go | 0 .../servergroups/testing/fixtures_test.go | 2 +- .../servergroups/testing/requests_test.go | 2 +- .../v2/{extensions => }/servergroups/urls.go | 0 .../v2/{extensions => }/services/doc.go | 0 .../v2/{extensions => }/services/requests.go | 0 .../v2/{extensions => }/services/results.go | 0 .../services/testing/fixtures_test.go | 2 +- .../services/testing/requests_test.go | 2 +- .../v2/{extensions => }/services/urls.go | 0 .../compute/v2/{extensions => }/tags/doc.go | 0 .../v2/{extensions => }/tags/requests.go | 0 .../v2/{extensions => }/tags/results.go | 0 .../v2/{extensions => }/tags/testing/doc.go | 0 .../tags/testing/fixtures_test.go | 0 .../tags/testing/requests_test.go | 2 +- .../compute/v2/{extensions => }/tags/urls.go | 0 .../v2/{extensions => }/tenantnetworks/doc.go | 0 .../tenantnetworks/requests.go | 0 .../tenantnetworks/results.go | 0 .../tenantnetworks/testing/doc.go | 0 .../tenantnetworks/testing/fixtures_test.go | 2 +- .../tenantnetworks/testing/requests_test.go | 2 +- .../{extensions => }/tenantnetworks/urls.go | 0 .../compute/v2/{extensions => }/usage/doc.go | 0 .../v2/{extensions => }/usage/requests.go | 0 .../v2/{extensions => }/usage/results.go | 0 .../v2/{extensions => }/usage/testing/doc.go | 0 .../usage/testing/fixtures_test.go | 2 +- .../usage/testing/requests_test.go | 2 +- .../compute/v2/{extensions => }/usage/urls.go | 0 .../v2/{extensions => }/volumeattach/doc.go | 0 .../{extensions => }/volumeattach/requests.go | 0 .../{extensions => }/volumeattach/results.go | 0 .../volumeattach/testing/doc.go | 0 .../volumeattach/testing/fixtures_test.go | 0 .../volumeattach/testing/requests_test.go | 2 +- .../v2/{extensions => }/volumeattach/urls.go | 0 script/format | 2 +- 147 files changed, 70 insertions(+), 70 deletions(-) rename openstack/compute/v2/{extensions => }/aggregates/doc.go (100%) rename openstack/compute/v2/{extensions => }/aggregates/requests.go (100%) rename openstack/compute/v2/{extensions => }/aggregates/results.go (100%) rename openstack/compute/v2/{extensions => }/aggregates/testing/fixtures_test.go (99%) rename openstack/compute/v2/{extensions => }/aggregates/testing/requests_test.go (97%) rename openstack/compute/v2/{extensions => }/aggregates/urls.go (100%) rename openstack/compute/v2/{extensions => }/attachinterfaces/doc.go (100%) rename openstack/compute/v2/{extensions => }/attachinterfaces/requests.go (100%) rename openstack/compute/v2/{extensions => }/attachinterfaces/results.go (100%) rename openstack/compute/v2/{extensions => }/attachinterfaces/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/attachinterfaces/testing/fixtures_test.go (98%) rename openstack/compute/v2/{extensions => }/attachinterfaces/testing/requests_test.go (96%) rename openstack/compute/v2/{extensions => }/attachinterfaces/urls.go (100%) rename openstack/compute/v2/{extensions => }/defsecrules/doc.go (100%) rename openstack/compute/v2/{extensions => }/defsecrules/requests.go (100%) rename openstack/compute/v2/{extensions => }/defsecrules/results.go (92%) rename openstack/compute/v2/{extensions => }/defsecrules/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/defsecrules/testing/fixtures_test.go (100%) rename openstack/compute/v2/{extensions => }/defsecrules/testing/requests_test.go (94%) rename openstack/compute/v2/{extensions => }/defsecrules/urls.go (100%) rename openstack/compute/v2/{extensions => }/diagnostics/doc.go (100%) rename openstack/compute/v2/{extensions => }/diagnostics/requests.go (100%) rename openstack/compute/v2/{extensions => }/diagnostics/results.go (100%) rename openstack/compute/v2/{extensions => }/diagnostics/testing/fixtures_test.go (100%) rename openstack/compute/v2/{extensions => }/diagnostics/testing/requests_test.go (86%) rename openstack/compute/v2/{extensions => }/diagnostics/urls.go (100%) rename openstack/compute/v2/{extensions => }/floatingips/doc.go (100%) rename openstack/compute/v2/{extensions => }/floatingips/requests.go (100%) rename openstack/compute/v2/{extensions => }/floatingips/results.go (100%) rename openstack/compute/v2/{extensions => }/floatingips/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/floatingips/testing/fixtures_test.go (98%) rename openstack/compute/v2/{extensions => }/floatingips/testing/requests_test.go (97%) rename openstack/compute/v2/{extensions => }/floatingips/urls.go (100%) rename openstack/compute/v2/{extensions => }/hypervisors/doc.go (100%) rename openstack/compute/v2/{extensions => }/hypervisors/requests.go (100%) rename openstack/compute/v2/{extensions => }/hypervisors/results.go (100%) rename openstack/compute/v2/{extensions => }/hypervisors/testing/fixtures_test.go (99%) rename openstack/compute/v2/{extensions => }/hypervisors/testing/requests_test.go (98%) rename openstack/compute/v2/{extensions => }/hypervisors/urls.go (100%) rename openstack/compute/v2/{extensions => }/instanceactions/doc.go (100%) rename openstack/compute/v2/{extensions => }/instanceactions/request.go (100%) rename openstack/compute/v2/{extensions => }/instanceactions/results.go (100%) rename openstack/compute/v2/{extensions => }/instanceactions/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/instanceactions/testing/fixtures_test.go (97%) rename openstack/compute/v2/{extensions => }/instanceactions/testing/request_test.go (93%) rename openstack/compute/v2/{extensions => }/instanceactions/urls.go (100%) rename openstack/compute/v2/{extensions => }/keypairs/doc.go (100%) rename openstack/compute/v2/{extensions => }/keypairs/requests.go (100%) rename openstack/compute/v2/{extensions => }/keypairs/results.go (100%) rename openstack/compute/v2/{extensions => }/keypairs/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/keypairs/testing/fixtures_test.go (99%) rename openstack/compute/v2/{extensions => }/keypairs/testing/requests_test.go (97%) rename openstack/compute/v2/{extensions => }/keypairs/urls.go (100%) rename openstack/compute/v2/{extensions => }/limits/doc.go (100%) rename openstack/compute/v2/{extensions => }/limits/requests.go (100%) rename openstack/compute/v2/{extensions => }/limits/results.go (100%) rename openstack/compute/v2/{extensions => }/limits/testing/fixtures_test.go (96%) rename openstack/compute/v2/{extensions => }/limits/testing/requests_test.go (85%) rename openstack/compute/v2/{extensions => }/limits/urls.go (100%) rename openstack/compute/v2/{extensions => }/networks/doc.go (100%) rename openstack/compute/v2/{extensions => }/networks/requests.go (100%) rename openstack/compute/v2/{extensions => }/networks/results.go (100%) rename openstack/compute/v2/{extensions => }/networks/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/networks/testing/fixtures_test.go (98%) rename openstack/compute/v2/{extensions => }/networks/testing/requests_test.go (92%) rename openstack/compute/v2/{extensions => }/networks/urls.go (100%) rename openstack/compute/v2/{extensions => }/quotasets/doc.go (100%) rename openstack/compute/v2/{extensions => }/quotasets/requests.go (100%) rename openstack/compute/v2/{extensions => }/quotasets/results.go (100%) rename openstack/compute/v2/{extensions => }/quotasets/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/quotasets/testing/fixtures_test.go (97%) rename openstack/compute/v2/{extensions => }/quotasets/testing/requests_test.go (96%) rename openstack/compute/v2/{extensions => }/quotasets/urls.go (100%) rename openstack/compute/v2/{extensions => }/remoteconsoles/doc.go (100%) rename openstack/compute/v2/{extensions => }/remoteconsoles/requests.go (100%) rename openstack/compute/v2/{extensions => }/remoteconsoles/results.go (100%) rename openstack/compute/v2/{extensions => }/remoteconsoles/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/remoteconsoles/testing/fixtures_test.go (100%) rename openstack/compute/v2/{extensions => }/remoteconsoles/testing/requests_test.go (93%) rename openstack/compute/v2/{extensions => }/remoteconsoles/urls.go (100%) rename openstack/compute/v2/{extensions => }/secgroups/doc.go (100%) rename openstack/compute/v2/{extensions => }/secgroups/requests.go (100%) rename openstack/compute/v2/{extensions => }/secgroups/results.go (100%) rename openstack/compute/v2/{extensions => }/secgroups/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/secgroups/testing/fixtures_test.go (100%) rename openstack/compute/v2/{extensions => }/secgroups/testing/requests_test.go (98%) rename openstack/compute/v2/{extensions => }/secgroups/urls.go (100%) rename openstack/compute/v2/{extensions => }/servergroups/doc.go (100%) rename openstack/compute/v2/{extensions => }/servergroups/requests.go (100%) rename openstack/compute/v2/{extensions => }/servergroups/results.go (100%) rename openstack/compute/v2/{extensions => }/servergroups/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/servergroups/testing/fixtures_test.go (98%) rename openstack/compute/v2/{extensions => }/servergroups/testing/requests_test.go (96%) rename openstack/compute/v2/{extensions => }/servergroups/urls.go (100%) rename openstack/compute/v2/{extensions => }/services/doc.go (100%) rename openstack/compute/v2/{extensions => }/services/requests.go (100%) rename openstack/compute/v2/{extensions => }/services/results.go (100%) rename openstack/compute/v2/{extensions => }/services/testing/fixtures_test.go (99%) rename openstack/compute/v2/{extensions => }/services/testing/requests_test.go (97%) rename openstack/compute/v2/{extensions => }/services/urls.go (100%) rename openstack/compute/v2/{extensions => }/tags/doc.go (100%) rename openstack/compute/v2/{extensions => }/tags/requests.go (100%) rename openstack/compute/v2/{extensions => }/tags/results.go (100%) rename openstack/compute/v2/{extensions => }/tags/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/tags/testing/fixtures_test.go (100%) rename openstack/compute/v2/{extensions => }/tags/testing/requests_test.go (98%) rename openstack/compute/v2/{extensions => }/tags/urls.go (100%) rename openstack/compute/v2/{extensions => }/tenantnetworks/doc.go (100%) rename openstack/compute/v2/{extensions => }/tenantnetworks/requests.go (100%) rename openstack/compute/v2/{extensions => }/tenantnetworks/results.go (100%) rename openstack/compute/v2/{extensions => }/tenantnetworks/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/tenantnetworks/testing/fixtures_test.go (96%) rename openstack/compute/v2/{extensions => }/tenantnetworks/testing/requests_test.go (91%) rename openstack/compute/v2/{extensions => }/tenantnetworks/urls.go (100%) rename openstack/compute/v2/{extensions => }/usage/doc.go (100%) rename openstack/compute/v2/{extensions => }/usage/requests.go (100%) rename openstack/compute/v2/{extensions => }/usage/results.go (100%) rename openstack/compute/v2/{extensions => }/usage/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/usage/testing/fixtures_test.go (99%) rename openstack/compute/v2/{extensions => }/usage/testing/requests_test.go (94%) rename openstack/compute/v2/{extensions => }/usage/urls.go (100%) rename openstack/compute/v2/{extensions => }/volumeattach/doc.go (100%) rename openstack/compute/v2/{extensions => }/volumeattach/requests.go (100%) rename openstack/compute/v2/{extensions => }/volumeattach/results.go (100%) rename openstack/compute/v2/{extensions => }/volumeattach/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/volumeattach/testing/fixtures_test.go (100%) rename openstack/compute/v2/{extensions => }/volumeattach/testing/requests_test.go (97%) rename openstack/compute/v2/{extensions => }/volumeattach/urls.go (100%) diff --git a/internal/acceptance/openstack/compute/v2/aggregates_test.go b/internal/acceptance/openstack/compute/v2/aggregates_test.go index ed460944ea..c1a1959569 100644 --- a/internal/acceptance/openstack/compute/v2/aggregates_test.go +++ b/internal/acceptance/openstack/compute/v2/aggregates_test.go @@ -12,8 +12,8 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/hypervisors" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go index 45faadc132..475b8ef9bc 100644 --- a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -11,7 +11,7 @@ import ( blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/bootfromvolume" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/volumeattach" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index 20567dcb52..5688666fe3 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -14,23 +14,23 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" + dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/bootfromvolume" - dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/defsecrules" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/quotasets" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/remoteconsoles" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/rescueunrescue" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/schedulerhints" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/volumeattach" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/remoteconsoles" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servergroups" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tenantnetworks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach" neutron "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/v2/testhelper" diff --git a/internal/acceptance/openstack/compute/v2/defsecrules_test.go b/internal/acceptance/openstack/compute/v2/defsecrules_test.go index c457be6920..d4fef06523 100644 --- a/internal/acceptance/openstack/compute/v2/defsecrules_test.go +++ b/internal/acceptance/openstack/compute/v2/defsecrules_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/defsecrules" + dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/diagnostics_test.go b/internal/acceptance/openstack/compute/v2/diagnostics_test.go index 2de23bfb62..aa63587997 100644 --- a/internal/acceptance/openstack/compute/v2/diagnostics_test.go +++ b/internal/acceptance/openstack/compute/v2/diagnostics_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diagnostics" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/diagnostics" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/floatingip_test.go b/internal/acceptance/openstack/compute/v2/floatingip_test.go index 0445e31066..047bc8e0f6 100644 --- a/internal/acceptance/openstack/compute/v2/floatingip_test.go +++ b/internal/acceptance/openstack/compute/v2/floatingip_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/hypervisors_test.go b/internal/acceptance/openstack/compute/v2/hypervisors_test.go index 44b7c6a3ac..a0390a1ace 100644 --- a/internal/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/internal/acceptance/openstack/compute/v2/hypervisors_test.go @@ -11,7 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/hypervisors" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/instance_actions_test.go b/internal/acceptance/openstack/compute/v2/instance_actions_test.go index 7d1a7a1abe..4a47fedcdf 100644 --- a/internal/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/internal/acceptance/openstack/compute/v2/instance_actions_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/instanceactions" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/instanceactions" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/keypairs_test.go b/internal/acceptance/openstack/compute/v2/keypairs_test.go index ac4e2207c8..035d890ac0 100644 --- a/internal/acceptance/openstack/compute/v2/keypairs_test.go +++ b/internal/acceptance/openstack/compute/v2/keypairs_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" identity "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/identity/v3" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/v2/testhelper" "golang.org/x/crypto/ssh" diff --git a/internal/acceptance/openstack/compute/v2/limits_test.go b/internal/acceptance/openstack/compute/v2/limits_test.go index a8257a288d..1eb85a8fe3 100644 --- a/internal/acceptance/openstack/compute/v2/limits_test.go +++ b/internal/acceptance/openstack/compute/v2/limits_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/limits" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/limits" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/network_test.go b/internal/acceptance/openstack/compute/v2/network_test.go index c32459fd66..5fbad4069d 100644 --- a/internal/acceptance/openstack/compute/v2/network_test.go +++ b/internal/acceptance/openstack/compute/v2/network_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/quotaset_test.go b/internal/acceptance/openstack/compute/v2/quotaset_test.go index dfd55af976..734f15d4b8 100644 --- a/internal/acceptance/openstack/compute/v2/quotaset_test.go +++ b/internal/acceptance/openstack/compute/v2/quotaset_test.go @@ -12,7 +12,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/quotasets" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/secgroup_test.go b/internal/acceptance/openstack/compute/v2/secgroup_test.go index 130397c4ef..9cda46eba6 100644 --- a/internal/acceptance/openstack/compute/v2/secgroup_test.go +++ b/internal/acceptance/openstack/compute/v2/secgroup_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/servergroup_test.go b/internal/acceptance/openstack/compute/v2/servergroup_test.go index 5421150024..064350c6d4 100644 --- a/internal/acceptance/openstack/compute/v2/servergroup_test.go +++ b/internal/acceptance/openstack/compute/v2/servergroup_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servergroups" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index c9000b626f..bb5aed5a63 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -12,7 +12,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" networks "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedserverattributes" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedstatus" @@ -20,8 +20,8 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/serverusage" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/suspendresume" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tags" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tags" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/services_test.go b/internal/acceptance/openstack/compute/v2/services_test.go index 91f67e1792..b298fb87f8 100644 --- a/internal/acceptance/openstack/compute/v2/services_test.go +++ b/internal/acceptance/openstack/compute/v2/services_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/services" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/services" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go index 61347506d0..4fe754903e 100644 --- a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go +++ b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tenantnetworks" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/usage_test.go b/internal/acceptance/openstack/compute/v2/usage_test.go index 65c85f87f8..80fa7c4e9b 100644 --- a/internal/acceptance/openstack/compute/v2/usage_test.go +++ b/internal/acceptance/openstack/compute/v2/usage_test.go @@ -11,7 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/usage" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/usage" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/openstack/compute/v2/extensions/aggregates/doc.go b/openstack/compute/v2/aggregates/doc.go similarity index 100% rename from openstack/compute/v2/extensions/aggregates/doc.go rename to openstack/compute/v2/aggregates/doc.go diff --git a/openstack/compute/v2/extensions/aggregates/requests.go b/openstack/compute/v2/aggregates/requests.go similarity index 100% rename from openstack/compute/v2/extensions/aggregates/requests.go rename to openstack/compute/v2/aggregates/requests.go diff --git a/openstack/compute/v2/extensions/aggregates/results.go b/openstack/compute/v2/aggregates/results.go similarity index 100% rename from openstack/compute/v2/extensions/aggregates/results.go rename to openstack/compute/v2/aggregates/results.go diff --git a/openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go b/openstack/compute/v2/aggregates/testing/fixtures_test.go similarity index 99% rename from openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go rename to openstack/compute/v2/aggregates/testing/fixtures_test.go index 8bd4387bc5..a67678404f 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/fixtures_test.go +++ b/openstack/compute/v2/aggregates/testing/fixtures_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go b/openstack/compute/v2/aggregates/testing/requests_test.go similarity index 97% rename from openstack/compute/v2/extensions/aggregates/testing/requests_test.go rename to openstack/compute/v2/aggregates/testing/requests_test.go index f0a5c3afc7..e334fdb4b7 100644 --- a/openstack/compute/v2/extensions/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/aggregates/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/aggregates" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/aggregates/urls.go b/openstack/compute/v2/aggregates/urls.go similarity index 100% rename from openstack/compute/v2/extensions/aggregates/urls.go rename to openstack/compute/v2/aggregates/urls.go diff --git a/openstack/compute/v2/extensions/attachinterfaces/doc.go b/openstack/compute/v2/attachinterfaces/doc.go similarity index 100% rename from openstack/compute/v2/extensions/attachinterfaces/doc.go rename to openstack/compute/v2/attachinterfaces/doc.go diff --git a/openstack/compute/v2/extensions/attachinterfaces/requests.go b/openstack/compute/v2/attachinterfaces/requests.go similarity index 100% rename from openstack/compute/v2/extensions/attachinterfaces/requests.go rename to openstack/compute/v2/attachinterfaces/requests.go diff --git a/openstack/compute/v2/extensions/attachinterfaces/results.go b/openstack/compute/v2/attachinterfaces/results.go similarity index 100% rename from openstack/compute/v2/extensions/attachinterfaces/results.go rename to openstack/compute/v2/attachinterfaces/results.go diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/doc.go b/openstack/compute/v2/attachinterfaces/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/attachinterfaces/testing/doc.go rename to openstack/compute/v2/attachinterfaces/testing/doc.go diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go b/openstack/compute/v2/attachinterfaces/testing/fixtures_test.go similarity index 98% rename from openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go rename to openstack/compute/v2/attachinterfaces/testing/fixtures_test.go index 43a3c971ff..77bc48f55e 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/fixtures_test.go +++ b/openstack/compute/v2/attachinterfaces/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go b/openstack/compute/v2/attachinterfaces/testing/requests_test.go similarity index 96% rename from openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go rename to openstack/compute/v2/attachinterfaces/testing/requests_test.go index 28fa9efb3f..d436bc081d 100644 --- a/openstack/compute/v2/extensions/attachinterfaces/testing/requests_test.go +++ b/openstack/compute/v2/attachinterfaces/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/attachinterfaces" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/attachinterfaces/urls.go b/openstack/compute/v2/attachinterfaces/urls.go similarity index 100% rename from openstack/compute/v2/extensions/attachinterfaces/urls.go rename to openstack/compute/v2/attachinterfaces/urls.go diff --git a/openstack/compute/v2/extensions/defsecrules/doc.go b/openstack/compute/v2/defsecrules/doc.go similarity index 100% rename from openstack/compute/v2/extensions/defsecrules/doc.go rename to openstack/compute/v2/defsecrules/doc.go diff --git a/openstack/compute/v2/extensions/defsecrules/requests.go b/openstack/compute/v2/defsecrules/requests.go similarity index 100% rename from openstack/compute/v2/extensions/defsecrules/requests.go rename to openstack/compute/v2/defsecrules/requests.go diff --git a/openstack/compute/v2/extensions/defsecrules/results.go b/openstack/compute/v2/defsecrules/results.go similarity index 92% rename from openstack/compute/v2/extensions/defsecrules/results.go rename to openstack/compute/v2/defsecrules/results.go index f28457f243..d10226ec94 100644 --- a/openstack/compute/v2/extensions/defsecrules/results.go +++ b/openstack/compute/v2/defsecrules/results.go @@ -4,12 +4,12 @@ import ( "encoding/json" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" "github.com/gophercloud/gophercloud/v2/pagination" ) // DefaultRule represents a rule belonging to the "default" security group. -// It is identical to an openstack/compute/v2/extensions/secgroups.Rule. +// It is identical to an openstack/compute/v2/secgroups.Rule. type DefaultRule secgroups.Rule func (r *DefaultRule) UnmarshalJSON(b []byte) error { diff --git a/openstack/compute/v2/extensions/defsecrules/testing/doc.go b/openstack/compute/v2/defsecrules/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/defsecrules/testing/doc.go rename to openstack/compute/v2/defsecrules/testing/doc.go diff --git a/openstack/compute/v2/extensions/defsecrules/testing/fixtures_test.go b/openstack/compute/v2/defsecrules/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/defsecrules/testing/fixtures_test.go rename to openstack/compute/v2/defsecrules/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go b/openstack/compute/v2/defsecrules/testing/requests_test.go similarity index 94% rename from openstack/compute/v2/extensions/defsecrules/testing/requests_test.go rename to openstack/compute/v2/defsecrules/testing/requests_test.go index 9b92c8cc83..9779fcda1f 100644 --- a/openstack/compute/v2/extensions/defsecrules/testing/requests_test.go +++ b/openstack/compute/v2/defsecrules/testing/requests_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/defsecrules" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/defsecrules/urls.go b/openstack/compute/v2/defsecrules/urls.go similarity index 100% rename from openstack/compute/v2/extensions/defsecrules/urls.go rename to openstack/compute/v2/defsecrules/urls.go diff --git a/openstack/compute/v2/extensions/diagnostics/doc.go b/openstack/compute/v2/diagnostics/doc.go similarity index 100% rename from openstack/compute/v2/extensions/diagnostics/doc.go rename to openstack/compute/v2/diagnostics/doc.go diff --git a/openstack/compute/v2/extensions/diagnostics/requests.go b/openstack/compute/v2/diagnostics/requests.go similarity index 100% rename from openstack/compute/v2/extensions/diagnostics/requests.go rename to openstack/compute/v2/diagnostics/requests.go diff --git a/openstack/compute/v2/extensions/diagnostics/results.go b/openstack/compute/v2/diagnostics/results.go similarity index 100% rename from openstack/compute/v2/extensions/diagnostics/results.go rename to openstack/compute/v2/diagnostics/results.go diff --git a/openstack/compute/v2/extensions/diagnostics/testing/fixtures_test.go b/openstack/compute/v2/diagnostics/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/diagnostics/testing/fixtures_test.go rename to openstack/compute/v2/diagnostics/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go b/openstack/compute/v2/diagnostics/testing/requests_test.go similarity index 86% rename from openstack/compute/v2/extensions/diagnostics/testing/requests_test.go rename to openstack/compute/v2/diagnostics/testing/requests_test.go index 05f3d8ca57..29f40eb133 100644 --- a/openstack/compute/v2/extensions/diagnostics/testing/requests_test.go +++ b/openstack/compute/v2/diagnostics/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diagnostics" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/diagnostics" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/diagnostics/urls.go b/openstack/compute/v2/diagnostics/urls.go similarity index 100% rename from openstack/compute/v2/extensions/diagnostics/urls.go rename to openstack/compute/v2/diagnostics/urls.go diff --git a/openstack/compute/v2/extensions/floatingips/doc.go b/openstack/compute/v2/floatingips/doc.go similarity index 100% rename from openstack/compute/v2/extensions/floatingips/doc.go rename to openstack/compute/v2/floatingips/doc.go diff --git a/openstack/compute/v2/extensions/floatingips/requests.go b/openstack/compute/v2/floatingips/requests.go similarity index 100% rename from openstack/compute/v2/extensions/floatingips/requests.go rename to openstack/compute/v2/floatingips/requests.go diff --git a/openstack/compute/v2/extensions/floatingips/results.go b/openstack/compute/v2/floatingips/results.go similarity index 100% rename from openstack/compute/v2/extensions/floatingips/results.go rename to openstack/compute/v2/floatingips/results.go diff --git a/openstack/compute/v2/extensions/floatingips/testing/doc.go b/openstack/compute/v2/floatingips/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/floatingips/testing/doc.go rename to openstack/compute/v2/floatingips/testing/doc.go diff --git a/openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go b/openstack/compute/v2/floatingips/testing/fixtures_test.go similarity index 98% rename from openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go rename to openstack/compute/v2/floatingips/testing/fixtures_test.go index 101464ac33..4a61a5d71b 100644 --- a/openstack/compute/v2/extensions/floatingips/testing/fixtures_test.go +++ b/openstack/compute/v2/floatingips/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/floatingips/testing/requests_test.go b/openstack/compute/v2/floatingips/testing/requests_test.go similarity index 97% rename from openstack/compute/v2/extensions/floatingips/testing/requests_test.go rename to openstack/compute/v2/floatingips/testing/requests_test.go index 757f5b6e91..2a375000f4 100644 --- a/openstack/compute/v2/extensions/floatingips/testing/requests_test.go +++ b/openstack/compute/v2/floatingips/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/floatingips/urls.go b/openstack/compute/v2/floatingips/urls.go similarity index 100% rename from openstack/compute/v2/extensions/floatingips/urls.go rename to openstack/compute/v2/floatingips/urls.go diff --git a/openstack/compute/v2/extensions/hypervisors/doc.go b/openstack/compute/v2/hypervisors/doc.go similarity index 100% rename from openstack/compute/v2/extensions/hypervisors/doc.go rename to openstack/compute/v2/hypervisors/doc.go diff --git a/openstack/compute/v2/extensions/hypervisors/requests.go b/openstack/compute/v2/hypervisors/requests.go similarity index 100% rename from openstack/compute/v2/extensions/hypervisors/requests.go rename to openstack/compute/v2/hypervisors/requests.go diff --git a/openstack/compute/v2/extensions/hypervisors/results.go b/openstack/compute/v2/hypervisors/results.go similarity index 100% rename from openstack/compute/v2/extensions/hypervisors/results.go rename to openstack/compute/v2/hypervisors/results.go diff --git a/openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go b/openstack/compute/v2/hypervisors/testing/fixtures_test.go similarity index 99% rename from openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go rename to openstack/compute/v2/hypervisors/testing/fixtures_test.go index 1052efa9bc..1942c842e0 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/fixtures_test.go +++ b/openstack/compute/v2/hypervisors/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/hypervisors" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go b/openstack/compute/v2/hypervisors/testing/requests_test.go similarity index 98% rename from openstack/compute/v2/extensions/hypervisors/testing/requests_test.go rename to openstack/compute/v2/hypervisors/testing/requests_test.go index 6774bc3032..900eadc78b 100644 --- a/openstack/compute/v2/extensions/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/hypervisors/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/hypervisors" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/hypervisors" "github.com/gophercloud/gophercloud/v2/pagination" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/hypervisors/urls.go b/openstack/compute/v2/hypervisors/urls.go similarity index 100% rename from openstack/compute/v2/extensions/hypervisors/urls.go rename to openstack/compute/v2/hypervisors/urls.go diff --git a/openstack/compute/v2/extensions/instanceactions/doc.go b/openstack/compute/v2/instanceactions/doc.go similarity index 100% rename from openstack/compute/v2/extensions/instanceactions/doc.go rename to openstack/compute/v2/instanceactions/doc.go diff --git a/openstack/compute/v2/extensions/instanceactions/request.go b/openstack/compute/v2/instanceactions/request.go similarity index 100% rename from openstack/compute/v2/extensions/instanceactions/request.go rename to openstack/compute/v2/instanceactions/request.go diff --git a/openstack/compute/v2/extensions/instanceactions/results.go b/openstack/compute/v2/instanceactions/results.go similarity index 100% rename from openstack/compute/v2/extensions/instanceactions/results.go rename to openstack/compute/v2/instanceactions/results.go diff --git a/openstack/compute/v2/extensions/instanceactions/testing/doc.go b/openstack/compute/v2/instanceactions/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/instanceactions/testing/doc.go rename to openstack/compute/v2/instanceactions/testing/doc.go diff --git a/openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go b/openstack/compute/v2/instanceactions/testing/fixtures_test.go similarity index 97% rename from openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go rename to openstack/compute/v2/instanceactions/testing/fixtures_test.go index 8e593d5873..3b24801c38 100644 --- a/openstack/compute/v2/extensions/instanceactions/testing/fixtures_test.go +++ b/openstack/compute/v2/instanceactions/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/instanceactions" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/instanceactions" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go b/openstack/compute/v2/instanceactions/testing/request_test.go similarity index 93% rename from openstack/compute/v2/extensions/instanceactions/testing/request_test.go rename to openstack/compute/v2/instanceactions/testing/request_test.go index 142d8d3014..459c776948 100644 --- a/openstack/compute/v2/extensions/instanceactions/testing/request_test.go +++ b/openstack/compute/v2/instanceactions/testing/request_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/instanceactions" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/instanceactions" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/instanceactions/urls.go b/openstack/compute/v2/instanceactions/urls.go similarity index 100% rename from openstack/compute/v2/extensions/instanceactions/urls.go rename to openstack/compute/v2/instanceactions/urls.go diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/keypairs/doc.go similarity index 100% rename from openstack/compute/v2/extensions/keypairs/doc.go rename to openstack/compute/v2/keypairs/doc.go diff --git a/openstack/compute/v2/extensions/keypairs/requests.go b/openstack/compute/v2/keypairs/requests.go similarity index 100% rename from openstack/compute/v2/extensions/keypairs/requests.go rename to openstack/compute/v2/keypairs/requests.go diff --git a/openstack/compute/v2/extensions/keypairs/results.go b/openstack/compute/v2/keypairs/results.go similarity index 100% rename from openstack/compute/v2/extensions/keypairs/results.go rename to openstack/compute/v2/keypairs/results.go diff --git a/openstack/compute/v2/extensions/keypairs/testing/doc.go b/openstack/compute/v2/keypairs/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/keypairs/testing/doc.go rename to openstack/compute/v2/keypairs/testing/doc.go diff --git a/openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go b/openstack/compute/v2/keypairs/testing/fixtures_test.go similarity index 99% rename from openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go rename to openstack/compute/v2/keypairs/testing/fixtures_test.go index 3c4c33eec9..41532c03cc 100644 --- a/openstack/compute/v2/extensions/keypairs/testing/fixtures_test.go +++ b/openstack/compute/v2/keypairs/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go b/openstack/compute/v2/keypairs/testing/requests_test.go similarity index 97% rename from openstack/compute/v2/extensions/keypairs/testing/requests_test.go rename to openstack/compute/v2/keypairs/testing/requests_test.go index c6c4dc0045..35e9f7653b 100644 --- a/openstack/compute/v2/extensions/keypairs/testing/requests_test.go +++ b/openstack/compute/v2/keypairs/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/keypairs" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/keypairs/urls.go b/openstack/compute/v2/keypairs/urls.go similarity index 100% rename from openstack/compute/v2/extensions/keypairs/urls.go rename to openstack/compute/v2/keypairs/urls.go diff --git a/openstack/compute/v2/extensions/limits/doc.go b/openstack/compute/v2/limits/doc.go similarity index 100% rename from openstack/compute/v2/extensions/limits/doc.go rename to openstack/compute/v2/limits/doc.go diff --git a/openstack/compute/v2/extensions/limits/requests.go b/openstack/compute/v2/limits/requests.go similarity index 100% rename from openstack/compute/v2/extensions/limits/requests.go rename to openstack/compute/v2/limits/requests.go diff --git a/openstack/compute/v2/extensions/limits/results.go b/openstack/compute/v2/limits/results.go similarity index 100% rename from openstack/compute/v2/extensions/limits/results.go rename to openstack/compute/v2/limits/results.go diff --git a/openstack/compute/v2/extensions/limits/testing/fixtures_test.go b/openstack/compute/v2/limits/testing/fixtures_test.go similarity index 96% rename from openstack/compute/v2/extensions/limits/testing/fixtures_test.go rename to openstack/compute/v2/limits/testing/fixtures_test.go index 6b8abd991f..3751c2d8b6 100644 --- a/openstack/compute/v2/extensions/limits/testing/fixtures_test.go +++ b/openstack/compute/v2/limits/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/limits" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/limits" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/limits/testing/requests_test.go b/openstack/compute/v2/limits/testing/requests_test.go similarity index 85% rename from openstack/compute/v2/extensions/limits/testing/requests_test.go rename to openstack/compute/v2/limits/testing/requests_test.go index 33dbbdc5ce..ae9650097e 100644 --- a/openstack/compute/v2/extensions/limits/testing/requests_test.go +++ b/openstack/compute/v2/limits/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/limits" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/limits" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/limits/urls.go b/openstack/compute/v2/limits/urls.go similarity index 100% rename from openstack/compute/v2/extensions/limits/urls.go rename to openstack/compute/v2/limits/urls.go diff --git a/openstack/compute/v2/extensions/networks/doc.go b/openstack/compute/v2/networks/doc.go similarity index 100% rename from openstack/compute/v2/extensions/networks/doc.go rename to openstack/compute/v2/networks/doc.go diff --git a/openstack/compute/v2/extensions/networks/requests.go b/openstack/compute/v2/networks/requests.go similarity index 100% rename from openstack/compute/v2/extensions/networks/requests.go rename to openstack/compute/v2/networks/requests.go diff --git a/openstack/compute/v2/extensions/networks/results.go b/openstack/compute/v2/networks/results.go similarity index 100% rename from openstack/compute/v2/extensions/networks/results.go rename to openstack/compute/v2/networks/results.go diff --git a/openstack/compute/v2/extensions/networks/testing/doc.go b/openstack/compute/v2/networks/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/networks/testing/doc.go rename to openstack/compute/v2/networks/testing/doc.go diff --git a/openstack/compute/v2/extensions/networks/testing/fixtures_test.go b/openstack/compute/v2/networks/testing/fixtures_test.go similarity index 98% rename from openstack/compute/v2/extensions/networks/testing/fixtures_test.go rename to openstack/compute/v2/networks/testing/fixtures_test.go index 782efe1fed..298896ab22 100644 --- a/openstack/compute/v2/extensions/networks/testing/fixtures_test.go +++ b/openstack/compute/v2/networks/testing/fixtures_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/networks/testing/requests_test.go b/openstack/compute/v2/networks/testing/requests_test.go similarity index 92% rename from openstack/compute/v2/extensions/networks/testing/requests_test.go rename to openstack/compute/v2/networks/testing/requests_test.go index 2fbc9503bf..fed0635d2a 100644 --- a/openstack/compute/v2/extensions/networks/testing/requests_test.go +++ b/openstack/compute/v2/networks/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/networks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/networks/urls.go b/openstack/compute/v2/networks/urls.go similarity index 100% rename from openstack/compute/v2/extensions/networks/urls.go rename to openstack/compute/v2/networks/urls.go diff --git a/openstack/compute/v2/extensions/quotasets/doc.go b/openstack/compute/v2/quotasets/doc.go similarity index 100% rename from openstack/compute/v2/extensions/quotasets/doc.go rename to openstack/compute/v2/quotasets/doc.go diff --git a/openstack/compute/v2/extensions/quotasets/requests.go b/openstack/compute/v2/quotasets/requests.go similarity index 100% rename from openstack/compute/v2/extensions/quotasets/requests.go rename to openstack/compute/v2/quotasets/requests.go diff --git a/openstack/compute/v2/extensions/quotasets/results.go b/openstack/compute/v2/quotasets/results.go similarity index 100% rename from openstack/compute/v2/extensions/quotasets/results.go rename to openstack/compute/v2/quotasets/results.go diff --git a/openstack/compute/v2/extensions/quotasets/testing/doc.go b/openstack/compute/v2/quotasets/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/quotasets/testing/doc.go rename to openstack/compute/v2/quotasets/testing/doc.go diff --git a/openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go b/openstack/compute/v2/quotasets/testing/fixtures_test.go similarity index 97% rename from openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go rename to openstack/compute/v2/quotasets/testing/fixtures_test.go index 7d34f43f71..d1c714714b 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/fixtures_test.go +++ b/openstack/compute/v2/quotasets/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/quotasets" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -25,8 +25,8 @@ const GetOutput = ` "ram" : 9216000, "key_pairs" : 10, "injected_file_path_bytes" : 255, - "server_groups" : 2, - "server_group_members" : 3 + "server_groups" : 2, + "server_group_members" : 3 } } ` @@ -35,7 +35,7 @@ const GetOutput = ` const GetDetailsOutput = ` { "quota_set" : { - "id": "555544443333222211110000ffffeeee", + "id": "555544443333222211110000ffffeeee", "instances" : { "in_use": 0, "limit": 25, diff --git a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go b/openstack/compute/v2/quotasets/testing/requests_test.go similarity index 96% rename from openstack/compute/v2/extensions/quotasets/testing/requests_test.go rename to openstack/compute/v2/quotasets/testing/requests_test.go index 75f1364b74..8d6d30e8d3 100644 --- a/openstack/compute/v2/extensions/quotasets/testing/requests_test.go +++ b/openstack/compute/v2/quotasets/testing/requests_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/quotasets" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/quotasets" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/quotasets/urls.go b/openstack/compute/v2/quotasets/urls.go similarity index 100% rename from openstack/compute/v2/extensions/quotasets/urls.go rename to openstack/compute/v2/quotasets/urls.go diff --git a/openstack/compute/v2/extensions/remoteconsoles/doc.go b/openstack/compute/v2/remoteconsoles/doc.go similarity index 100% rename from openstack/compute/v2/extensions/remoteconsoles/doc.go rename to openstack/compute/v2/remoteconsoles/doc.go diff --git a/openstack/compute/v2/extensions/remoteconsoles/requests.go b/openstack/compute/v2/remoteconsoles/requests.go similarity index 100% rename from openstack/compute/v2/extensions/remoteconsoles/requests.go rename to openstack/compute/v2/remoteconsoles/requests.go diff --git a/openstack/compute/v2/extensions/remoteconsoles/results.go b/openstack/compute/v2/remoteconsoles/results.go similarity index 100% rename from openstack/compute/v2/extensions/remoteconsoles/results.go rename to openstack/compute/v2/remoteconsoles/results.go diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/doc.go b/openstack/compute/v2/remoteconsoles/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/remoteconsoles/testing/doc.go rename to openstack/compute/v2/remoteconsoles/testing/doc.go diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/fixtures_test.go b/openstack/compute/v2/remoteconsoles/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/remoteconsoles/testing/fixtures_test.go rename to openstack/compute/v2/remoteconsoles/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go b/openstack/compute/v2/remoteconsoles/testing/requests_test.go similarity index 93% rename from openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go rename to openstack/compute/v2/remoteconsoles/testing/requests_test.go index 04b7546dd7..e96b897d0f 100644 --- a/openstack/compute/v2/extensions/remoteconsoles/testing/requests_test.go +++ b/openstack/compute/v2/remoteconsoles/testing/requests_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/remoteconsoles" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/remoteconsoles" th "github.com/gophercloud/gophercloud/v2/testhelper" fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/remoteconsoles/urls.go b/openstack/compute/v2/remoteconsoles/urls.go similarity index 100% rename from openstack/compute/v2/extensions/remoteconsoles/urls.go rename to openstack/compute/v2/remoteconsoles/urls.go diff --git a/openstack/compute/v2/extensions/secgroups/doc.go b/openstack/compute/v2/secgroups/doc.go similarity index 100% rename from openstack/compute/v2/extensions/secgroups/doc.go rename to openstack/compute/v2/secgroups/doc.go diff --git a/openstack/compute/v2/extensions/secgroups/requests.go b/openstack/compute/v2/secgroups/requests.go similarity index 100% rename from openstack/compute/v2/extensions/secgroups/requests.go rename to openstack/compute/v2/secgroups/requests.go diff --git a/openstack/compute/v2/extensions/secgroups/results.go b/openstack/compute/v2/secgroups/results.go similarity index 100% rename from openstack/compute/v2/extensions/secgroups/results.go rename to openstack/compute/v2/secgroups/results.go diff --git a/openstack/compute/v2/extensions/secgroups/testing/doc.go b/openstack/compute/v2/secgroups/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/secgroups/testing/doc.go rename to openstack/compute/v2/secgroups/testing/doc.go diff --git a/openstack/compute/v2/extensions/secgroups/testing/fixtures_test.go b/openstack/compute/v2/secgroups/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/secgroups/testing/fixtures_test.go rename to openstack/compute/v2/secgroups/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go b/openstack/compute/v2/secgroups/testing/requests_test.go similarity index 98% rename from openstack/compute/v2/extensions/secgroups/testing/requests_test.go rename to openstack/compute/v2/secgroups/testing/requests_test.go index 73dc1f1fe7..222295d5e8 100644 --- a/openstack/compute/v2/extensions/secgroups/testing/requests_test.go +++ b/openstack/compute/v2/secgroups/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/secgroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/secgroups/urls.go b/openstack/compute/v2/secgroups/urls.go similarity index 100% rename from openstack/compute/v2/extensions/secgroups/urls.go rename to openstack/compute/v2/secgroups/urls.go diff --git a/openstack/compute/v2/extensions/servergroups/doc.go b/openstack/compute/v2/servergroups/doc.go similarity index 100% rename from openstack/compute/v2/extensions/servergroups/doc.go rename to openstack/compute/v2/servergroups/doc.go diff --git a/openstack/compute/v2/extensions/servergroups/requests.go b/openstack/compute/v2/servergroups/requests.go similarity index 100% rename from openstack/compute/v2/extensions/servergroups/requests.go rename to openstack/compute/v2/servergroups/requests.go diff --git a/openstack/compute/v2/extensions/servergroups/results.go b/openstack/compute/v2/servergroups/results.go similarity index 100% rename from openstack/compute/v2/extensions/servergroups/results.go rename to openstack/compute/v2/servergroups/results.go diff --git a/openstack/compute/v2/extensions/servergroups/testing/doc.go b/openstack/compute/v2/servergroups/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/servergroups/testing/doc.go rename to openstack/compute/v2/servergroups/testing/doc.go diff --git a/openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go b/openstack/compute/v2/servergroups/testing/fixtures_test.go similarity index 98% rename from openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go rename to openstack/compute/v2/servergroups/testing/fixtures_test.go index 05839c2e50..fd692080ae 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/fixtures_test.go +++ b/openstack/compute/v2/servergroups/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servergroups" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go b/openstack/compute/v2/servergroups/testing/requests_test.go similarity index 96% rename from openstack/compute/v2/extensions/servergroups/testing/requests_test.go rename to openstack/compute/v2/servergroups/testing/requests_test.go index 3557f81079..91a9b75168 100644 --- a/openstack/compute/v2/extensions/servergroups/testing/requests_test.go +++ b/openstack/compute/v2/servergroups/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/servergroups" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servergroups" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/servergroups/urls.go b/openstack/compute/v2/servergroups/urls.go similarity index 100% rename from openstack/compute/v2/extensions/servergroups/urls.go rename to openstack/compute/v2/servergroups/urls.go diff --git a/openstack/compute/v2/extensions/services/doc.go b/openstack/compute/v2/services/doc.go similarity index 100% rename from openstack/compute/v2/extensions/services/doc.go rename to openstack/compute/v2/services/doc.go diff --git a/openstack/compute/v2/extensions/services/requests.go b/openstack/compute/v2/services/requests.go similarity index 100% rename from openstack/compute/v2/extensions/services/requests.go rename to openstack/compute/v2/services/requests.go diff --git a/openstack/compute/v2/extensions/services/results.go b/openstack/compute/v2/services/results.go similarity index 100% rename from openstack/compute/v2/extensions/services/results.go rename to openstack/compute/v2/services/results.go diff --git a/openstack/compute/v2/extensions/services/testing/fixtures_test.go b/openstack/compute/v2/services/testing/fixtures_test.go similarity index 99% rename from openstack/compute/v2/extensions/services/testing/fixtures_test.go rename to openstack/compute/v2/services/testing/fixtures_test.go index dc9661ce27..2104dc08cb 100644 --- a/openstack/compute/v2/extensions/services/testing/fixtures_test.go +++ b/openstack/compute/v2/services/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/services" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/services" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/services/testing/requests_test.go b/openstack/compute/v2/services/testing/requests_test.go similarity index 97% rename from openstack/compute/v2/extensions/services/testing/requests_test.go rename to openstack/compute/v2/services/testing/requests_test.go index 3bbcb478c2..c201c9f4d6 100644 --- a/openstack/compute/v2/extensions/services/testing/requests_test.go +++ b/openstack/compute/v2/services/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/services" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/services" "github.com/gophercloud/gophercloud/v2/pagination" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/services/urls.go b/openstack/compute/v2/services/urls.go similarity index 100% rename from openstack/compute/v2/extensions/services/urls.go rename to openstack/compute/v2/services/urls.go diff --git a/openstack/compute/v2/extensions/tags/doc.go b/openstack/compute/v2/tags/doc.go similarity index 100% rename from openstack/compute/v2/extensions/tags/doc.go rename to openstack/compute/v2/tags/doc.go diff --git a/openstack/compute/v2/extensions/tags/requests.go b/openstack/compute/v2/tags/requests.go similarity index 100% rename from openstack/compute/v2/extensions/tags/requests.go rename to openstack/compute/v2/tags/requests.go diff --git a/openstack/compute/v2/extensions/tags/results.go b/openstack/compute/v2/tags/results.go similarity index 100% rename from openstack/compute/v2/extensions/tags/results.go rename to openstack/compute/v2/tags/results.go diff --git a/openstack/compute/v2/extensions/tags/testing/doc.go b/openstack/compute/v2/tags/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/tags/testing/doc.go rename to openstack/compute/v2/tags/testing/doc.go diff --git a/openstack/compute/v2/extensions/tags/testing/fixtures_test.go b/openstack/compute/v2/tags/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/tags/testing/fixtures_test.go rename to openstack/compute/v2/tags/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/tags/testing/requests_test.go b/openstack/compute/v2/tags/testing/requests_test.go similarity index 98% rename from openstack/compute/v2/extensions/tags/testing/requests_test.go rename to openstack/compute/v2/tags/testing/requests_test.go index f106b44769..03f4260a7d 100644 --- a/openstack/compute/v2/extensions/tags/testing/requests_test.go +++ b/openstack/compute/v2/tags/testing/requests_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tags" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tags" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/tags/urls.go b/openstack/compute/v2/tags/urls.go similarity index 100% rename from openstack/compute/v2/extensions/tags/urls.go rename to openstack/compute/v2/tags/urls.go diff --git a/openstack/compute/v2/extensions/tenantnetworks/doc.go b/openstack/compute/v2/tenantnetworks/doc.go similarity index 100% rename from openstack/compute/v2/extensions/tenantnetworks/doc.go rename to openstack/compute/v2/tenantnetworks/doc.go diff --git a/openstack/compute/v2/extensions/tenantnetworks/requests.go b/openstack/compute/v2/tenantnetworks/requests.go similarity index 100% rename from openstack/compute/v2/extensions/tenantnetworks/requests.go rename to openstack/compute/v2/tenantnetworks/requests.go diff --git a/openstack/compute/v2/extensions/tenantnetworks/results.go b/openstack/compute/v2/tenantnetworks/results.go similarity index 100% rename from openstack/compute/v2/extensions/tenantnetworks/results.go rename to openstack/compute/v2/tenantnetworks/results.go diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/doc.go b/openstack/compute/v2/tenantnetworks/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/tenantnetworks/testing/doc.go rename to openstack/compute/v2/tenantnetworks/testing/doc.go diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go b/openstack/compute/v2/tenantnetworks/testing/fixtures_test.go similarity index 96% rename from openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go rename to openstack/compute/v2/tenantnetworks/testing/fixtures_test.go index 1fd6827e5a..c6c8f2d99f 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/testing/fixtures_test.go +++ b/openstack/compute/v2/tenantnetworks/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tenantnetworks" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go b/openstack/compute/v2/tenantnetworks/testing/requests_test.go similarity index 91% rename from openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go rename to openstack/compute/v2/tenantnetworks/testing/requests_test.go index 092c391aca..f94e223280 100644 --- a/openstack/compute/v2/extensions/tenantnetworks/testing/requests_test.go +++ b/openstack/compute/v2/tenantnetworks/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/tenantnetworks" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tenantnetworks" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/tenantnetworks/urls.go b/openstack/compute/v2/tenantnetworks/urls.go similarity index 100% rename from openstack/compute/v2/extensions/tenantnetworks/urls.go rename to openstack/compute/v2/tenantnetworks/urls.go diff --git a/openstack/compute/v2/extensions/usage/doc.go b/openstack/compute/v2/usage/doc.go similarity index 100% rename from openstack/compute/v2/extensions/usage/doc.go rename to openstack/compute/v2/usage/doc.go diff --git a/openstack/compute/v2/extensions/usage/requests.go b/openstack/compute/v2/usage/requests.go similarity index 100% rename from openstack/compute/v2/extensions/usage/requests.go rename to openstack/compute/v2/usage/requests.go diff --git a/openstack/compute/v2/extensions/usage/results.go b/openstack/compute/v2/usage/results.go similarity index 100% rename from openstack/compute/v2/extensions/usage/results.go rename to openstack/compute/v2/usage/results.go diff --git a/openstack/compute/v2/extensions/usage/testing/doc.go b/openstack/compute/v2/usage/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/usage/testing/doc.go rename to openstack/compute/v2/usage/testing/doc.go diff --git a/openstack/compute/v2/extensions/usage/testing/fixtures_test.go b/openstack/compute/v2/usage/testing/fixtures_test.go similarity index 99% rename from openstack/compute/v2/extensions/usage/testing/fixtures_test.go rename to openstack/compute/v2/usage/testing/fixtures_test.go index dbd1dabdb3..f5b92ca095 100644 --- a/openstack/compute/v2/extensions/usage/testing/fixtures_test.go +++ b/openstack/compute/v2/usage/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/usage" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/usage" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/usage/testing/requests_test.go b/openstack/compute/v2/usage/testing/requests_test.go similarity index 94% rename from openstack/compute/v2/extensions/usage/testing/requests_test.go rename to openstack/compute/v2/usage/testing/requests_test.go index 2aeab1199c..163ab1082d 100644 --- a/openstack/compute/v2/extensions/usage/testing/requests_test.go +++ b/openstack/compute/v2/usage/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/usage" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/usage" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/usage/urls.go b/openstack/compute/v2/usage/urls.go similarity index 100% rename from openstack/compute/v2/extensions/usage/urls.go rename to openstack/compute/v2/usage/urls.go diff --git a/openstack/compute/v2/extensions/volumeattach/doc.go b/openstack/compute/v2/volumeattach/doc.go similarity index 100% rename from openstack/compute/v2/extensions/volumeattach/doc.go rename to openstack/compute/v2/volumeattach/doc.go diff --git a/openstack/compute/v2/extensions/volumeattach/requests.go b/openstack/compute/v2/volumeattach/requests.go similarity index 100% rename from openstack/compute/v2/extensions/volumeattach/requests.go rename to openstack/compute/v2/volumeattach/requests.go diff --git a/openstack/compute/v2/extensions/volumeattach/results.go b/openstack/compute/v2/volumeattach/results.go similarity index 100% rename from openstack/compute/v2/extensions/volumeattach/results.go rename to openstack/compute/v2/volumeattach/results.go diff --git a/openstack/compute/v2/extensions/volumeattach/testing/doc.go b/openstack/compute/v2/volumeattach/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/volumeattach/testing/doc.go rename to openstack/compute/v2/volumeattach/testing/doc.go diff --git a/openstack/compute/v2/extensions/volumeattach/testing/fixtures_test.go b/openstack/compute/v2/volumeattach/testing/fixtures_test.go similarity index 100% rename from openstack/compute/v2/extensions/volumeattach/testing/fixtures_test.go rename to openstack/compute/v2/volumeattach/testing/fixtures_test.go diff --git a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go b/openstack/compute/v2/volumeattach/testing/requests_test.go similarity index 97% rename from openstack/compute/v2/extensions/volumeattach/testing/requests_test.go rename to openstack/compute/v2/volumeattach/testing/requests_test.go index 1d0884a073..92da356cb1 100644 --- a/openstack/compute/v2/extensions/volumeattach/testing/requests_test.go +++ b/openstack/compute/v2/volumeattach/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/volumeattach" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/compute/v2/extensions/volumeattach/urls.go b/openstack/compute/v2/volumeattach/urls.go similarity index 100% rename from openstack/compute/v2/extensions/volumeattach/urls.go rename to openstack/compute/v2/volumeattach/urls.go diff --git a/script/format b/script/format index 05645a8252..672896b998 100755 --- a/script/format +++ b/script/format @@ -17,7 +17,7 @@ find_files() { } ignore_files=( - "./openstack/compute/v2/extensions/quotasets/testing/fixtures.go" + "./openstack/compute/v2/quotasets/testing/fixtures.go" "./openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go" ) From d50448b3ea0ccef6dcf3493e3950e8bd0fcd3cc3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 10:40:33 +0000 Subject: [PATCH 1809/2296] compute: Merge bootfromvolume extension This is relatively easy to merge since it's a well understood, well used extension. Signed-off-by: Stephen Finucane --- .../compute/v2/bootfromvolume_test.go | 60 +-- .../openstack/compute/v2/compute.go | 29 +- .../v2/extensions/bootfromvolume/doc.go | 152 ------- .../v2/extensions/bootfromvolume/requests.go | 144 ------- .../v2/extensions/bootfromvolume/results.go | 12 - .../extensions/bootfromvolume/testing/doc.go | 2 - .../bootfromvolume/testing/fixtures_test.go | 360 ---------------- .../bootfromvolume/testing/requests_test.go | 55 --- .../v2/extensions/bootfromvolume/urls.go | 7 - openstack/compute/v2/servers/doc.go | 123 ++++++ openstack/compute/v2/servers/requests.go | 92 +++- .../v2/servers/testing/requests_test.go | 396 ++++++++++++++++++ 12 files changed, 652 insertions(+), 780 deletions(-) delete mode 100644 openstack/compute/v2/extensions/bootfromvolume/doc.go delete mode 100644 openstack/compute/v2/extensions/bootfromvolume/requests.go delete mode 100644 openstack/compute/v2/extensions/bootfromvolume/results.go delete mode 100644 openstack/compute/v2/extensions/bootfromvolume/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/bootfromvolume/urls.go diff --git a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go index 475b8ef9bc..957fd1cb32 100644 --- a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" blockstorage "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/blockstorage/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/bootfromvolume" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -24,12 +24,12 @@ func TestBootFromImage(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - blockDevices := []bootfromvolume.BlockDevice{ + blockDevices := []servers.BlockDevice{ { BootIndex: 0, DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - SourceType: bootfromvolume.SourceImage, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, UUID: choices.ImageID, }, } @@ -57,11 +57,11 @@ func TestBootFromNewVolume(t *testing.T) { client.Microversion = "2.70" tagName := "tag1" - blockDevices := []bootfromvolume.BlockDevice{ + blockDevices := []servers.BlockDevice{ { DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationVolume, - SourceType: bootfromvolume.SourceImage, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceImage, UUID: choices.ImageID, VolumeSize: 2, Tag: tagName, @@ -106,11 +106,11 @@ func TestBootFromExistingVolume(t *testing.T) { tools.PrintResource(t, volume) - blockDevices := []bootfromvolume.BlockDevice{ + blockDevices := []servers.BlockDevice{ { DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationVolume, - SourceType: bootfromvolume.SourceVolume, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceVolume, UUID: volume.ID, }, } @@ -146,29 +146,29 @@ func TestBootFromMultiEphemeralServer(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - blockDevices := []bootfromvolume.BlockDevice{ + blockDevices := []servers.BlockDevice{ { BootIndex: 0, - DestinationType: bootfromvolume.DestinationLocal, + DestinationType: servers.DestinationLocal, DeleteOnTermination: true, - SourceType: bootfromvolume.SourceImage, + SourceType: servers.SourceImage, UUID: choices.ImageID, VolumeSize: 5, }, { BootIndex: -1, - DestinationType: bootfromvolume.DestinationLocal, + DestinationType: servers.DestinationLocal, DeleteOnTermination: true, GuestFormat: "ext4", - SourceType: bootfromvolume.SourceBlank, + SourceType: servers.SourceBlank, VolumeSize: 1, }, { BootIndex: -1, - DestinationType: bootfromvolume.DestinationLocal, + DestinationType: servers.DestinationLocal, DeleteOnTermination: true, GuestFormat: "ext4", - SourceType: bootfromvolume.SourceBlank, + SourceType: servers.SourceBlank, VolumeSize: 1, }, } @@ -189,19 +189,19 @@ func TestAttachNewVolume(t *testing.T) { choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) - blockDevices := []bootfromvolume.BlockDevice{ + blockDevices := []servers.BlockDevice{ { BootIndex: 0, DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - SourceType: bootfromvolume.SourceImage, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, UUID: choices.ImageID, }, { BootIndex: 1, DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationVolume, - SourceType: bootfromvolume.SourceBlank, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceBlank, VolumeSize: 2, }, } @@ -240,19 +240,19 @@ func TestAttachExistingVolume(t *testing.T) { volume, err := blockstorage.CreateVolume(t, blockStorageClient) th.AssertNoErr(t, err) - blockDevices := []bootfromvolume.BlockDevice{ + blockDevices := []servers.BlockDevice{ { BootIndex: 0, DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - SourceType: bootfromvolume.SourceImage, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, UUID: choices.ImageID, }, { BootIndex: 1, DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationVolume, - SourceType: bootfromvolume.SourceVolume, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceVolume, UUID: volume.ID, }, } @@ -292,11 +292,11 @@ func TestBootFromNewCustomizedVolume(t *testing.T) { t.Fatal(err) } - blockDevices := []bootfromvolume.BlockDevice{ + blockDevices := []servers.BlockDevice{ { DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationVolume, - SourceType: bootfromvolume.SourceImage, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceImage, UUID: choices.ImageID, VolumeSize: 2, DeviceType: "disk", diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index 5688666fe3..ef0d7a0a40 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -17,7 +17,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/bootfromvolume" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/rescueunrescue" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/schedulerhints" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" @@ -131,9 +130,9 @@ func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregat } // CreateBootableVolumeServer works like CreateServer but is configured with -// one or more block devices defined by passing in []bootfromvolume.BlockDevice. +// one or more block devices defined by passing in []servers.BlockDevice. // An error will be returned if a server was unable to be created. -func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { +func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []servers.BlockDevice) (*servers.Server, error) { var server *servers.Server choices, err := clients.AcceptanceTestChoicesFromEnv() @@ -149,22 +148,20 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bootable volume server: %s", name) - serverCreateOpts := servers.CreateOpts{ + createOpts := servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, Networks: []servers.Network{ {UUID: networkID}, }, + BlockDevice: blockDevices, } - if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal { - serverCreateOpts.ImageRef = blockDevices[0].UUID + if blockDevices[0].SourceType == servers.SourceImage && blockDevices[0].DestinationType == servers.DestinationLocal { + createOpts.ImageRef = blockDevices[0].UUID } - server, err = bootfromvolume.Create(context.TODO(), client, bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - BlockDevice: blockDevices, - }).Extract() + server, err = servers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return server, err @@ -302,11 +299,11 @@ func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.K } // CreateMultiEphemeralServer works like CreateServer but is configured with -// one or more block devices defined by passing in []bootfromvolume.BlockDevice. +// one or more block devices defined by passing in []servers.BlockDevice. // These block devices act like block devices when booting from a volume but // are actually local ephemeral disks. // An error will be returned if a server was unable to be created. -func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) { +func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []servers.BlockDevice) (*servers.Server, error) { var server *servers.Server choices, err := clients.AcceptanceTestChoicesFromEnv() @@ -322,19 +319,17 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create bootable volume server: %s", name) - serverCreateOpts := servers.CreateOpts{ + createOpts := servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, Networks: []servers.Network{ {UUID: networkID}, }, + BlockDevice: blockDevices, } - server, err = bootfromvolume.Create(context.TODO(), client, bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - BlockDevice: blockDevices, - }).Extract() + server, err = servers.Create(context.TODO(), client, createOpts).Extract() if err != nil { return server, err diff --git a/openstack/compute/v2/extensions/bootfromvolume/doc.go b/openstack/compute/v2/extensions/bootfromvolume/doc.go deleted file mode 100644 index 8cf90bc9b0..0000000000 --- a/openstack/compute/v2/extensions/bootfromvolume/doc.go +++ /dev/null @@ -1,152 +0,0 @@ -/* -Package bootfromvolume extends a server create request with the ability to -specify block device options. This can be used to boot a server from a block -storage volume as well as specify multiple ephemeral disks upon creation. - -It is recommended to refer to the Block Device Mapping documentation to see -all possible ways to configure a server's block devices at creation time: - -https://docs.openstack.org/nova/latest/user/block-device-mapping.html - -Note that this package implements `block_device_mapping_v2`. - -# Example of Creating a Server From an Image - -This example will boot a server from an image and use a standard ephemeral -disk as the server's root disk. This is virtually no different than creating -a server without using block device mappings. - - blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ - BootIndex: 0, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - SourceType: bootfromvolume.SourceImage, - UUID: "image-uuid", - }, - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_name", - FlavorRef: "flavor-uuid", - ImageRef: "image-uuid", - } - - createOpts := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - BlockDevice: blockDevices, - } - - server, err := bootfromvolume.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - panic(err) - } - -# Example of Creating a Server From a New Volume - -This example will create a block storage volume based on the given Image. The -server will use this volume as its root disk. - - blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationVolume, - SourceType: bootfromvolume.SourceImage, - UUID: "image-uuid", - VolumeSize: 2, - }, - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_name", - FlavorRef: "flavor-uuid", - } - - createOpts := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - BlockDevice: blockDevices, - } - - server, err := bootfromvolume.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - panic(err) - } - -# Example of Creating a Server From an Existing Volume - -This example will create a server with an existing volume as its root disk. - - blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationVolume, - SourceType: bootfromvolume.SourceVolume, - UUID: "volume-uuid", - }, - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_name", - FlavorRef: "flavor-uuid", - } - - createOpts := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - BlockDevice: blockDevices, - } - - server, err := bootfromvolume.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - panic(err) - } - -# Example of Creating a Server with Multiple Ephemeral Disks - -This example will create a server with multiple ephemeral disks. The first -block device will be based off of an existing Image. Each additional -ephemeral disks must have an index of -1. - - blockDevices := []bootfromvolume.BlockDevice{ - bootfromvolume.BlockDevice{ - BootIndex: 0, - DestinationType: bootfromvolume.DestinationLocal, - DeleteOnTermination: true, - SourceType: bootfromvolume.SourceImage, - UUID: "image-uuid", - VolumeSize: 5, - }, - bootfromvolume.BlockDevice{ - BootIndex: -1, - DestinationType: bootfromvolume.DestinationLocal, - DeleteOnTermination: true, - GuestFormat: "ext4", - SourceType: bootfromvolume.SourceBlank, - VolumeSize: 1, - }, - bootfromvolume.BlockDevice{ - BootIndex: -1, - DestinationType: bootfromvolume.DestinationLocal, - DeleteOnTermination: true, - GuestFormat: "ext4", - SourceType: bootfromvolume.SourceBlank, - VolumeSize: 1, - }, - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_name", - FlavorRef: "flavor-uuid", - ImageRef: "image-uuid", - } - - createOpts := bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - BlockDevice: blockDevices, - } - - server, err := bootfromvolume.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - panic(err) - } -*/ -package bootfromvolume diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go deleted file mode 100644 index 6d4d75116c..0000000000 --- a/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ /dev/null @@ -1,144 +0,0 @@ -package bootfromvolume - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" -) - -type ( - // DestinationType represents the type of medium being used as the - // destination of the bootable device. - DestinationType string - - // SourceType represents the type of medium being used as the source of the - // bootable device. - SourceType string -) - -const ( - // DestinationLocal DestinationType is for using an ephemeral disk as the - // destination. - DestinationLocal DestinationType = "local" - - // DestinationVolume DestinationType is for using a volume as the destination. - DestinationVolume DestinationType = "volume" - - // SourceBlank SourceType is for a "blank" or empty source. - SourceBlank SourceType = "blank" - - // SourceImage SourceType is for using images as the source of a block device. - SourceImage SourceType = "image" - - // SourceSnapshot SourceType is for using a volume snapshot as the source of - // a block device. - SourceSnapshot SourceType = "snapshot" - - // SourceVolume SourceType is for using a volume as the source of block - // device. - SourceVolume SourceType = "volume" -) - -// BlockDevice is a structure with options for creating block devices in a -// server. The block device may be created from an image, snapshot, new volume, -// or existing volume. The destination may be a new volume, existing volume -// which will be attached to the instance, ephemeral disk, or boot device. -type BlockDevice struct { - // SourceType must be one of: "volume", "snapshot", "image", or "blank". - SourceType SourceType `json:"source_type" required:"true"` - - // UUID is the unique identifier for the existing volume, snapshot, or - // image (see above). - UUID string `json:"uuid,omitempty"` - - // BootIndex is the boot index. It defaults to 0. - BootIndex int `json:"boot_index"` - - // DeleteOnTermination specifies whether or not to delete the attached volume - // when the server is deleted. Defaults to `false`. - DeleteOnTermination bool `json:"delete_on_termination"` - - // DestinationType is the type that gets created. Possible values are "volume" - // and "local". - DestinationType DestinationType `json:"destination_type,omitempty"` - - // GuestFormat specifies the format of the block device. - // Not specifying this will cause the device to be formatted to the default in Nova - // which is currently vfat. - // https://opendev.org/openstack/nova/src/commit/d0b459423dd81644e8d9382b6c87fabaa4f03ad4/nova/privsep/fs.py#L257 - GuestFormat string `json:"guest_format,omitempty"` - - // VolumeSize is the size of the volume to create (in gigabytes). This can be - // omitted for existing volumes. - VolumeSize int `json:"volume_size,omitempty"` - - // DeviceType specifies the device type of the block devices. - // Examples of this are disk, cdrom, floppy, lun, etc. - DeviceType string `json:"device_type,omitempty"` - - // DiskBus is the bus type of the block devices. - // Examples of this are ide, usb, virtio, scsi, etc. - DiskBus string `json:"disk_bus,omitempty"` - - // VolumeType is the volume type of the block device. - // This requires Compute API microversion 2.67 or later. - VolumeType string `json:"volume_type,omitempty"` - - // Tag is an arbitrary string that can be applied to a block device. - // Information about the device tags can be obtained from the metadata API - // and the config drive, allowing devices to be easily identified. - // This requires Compute API microversion 2.42 or later. - Tag string `json:"tag,omitempty"` -} - -// CreateOptsExt is a structure that extends the server `CreateOpts` structure -// by allowing for a block device mapping. -type CreateOptsExt struct { - servers.CreateOptsBuilder - BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"` -} - -// ToServerCreateMap adds the block device mapping option to the base server -// creation options. -func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { - base, err := opts.CreateOptsBuilder.ToServerCreateMap() - if err != nil { - return nil, err - } - - if len(opts.BlockDevice) == 0 { - err := gophercloud.ErrMissingInput{} - err.Argument = "bootfromvolume.CreateOptsExt.BlockDevice" - return nil, err - } - - serverMap := base["server"].(map[string]interface{}) - - blockDevice := make([]map[string]interface{}, len(opts.BlockDevice)) - - for i, bd := range opts.BlockDevice { - b, err := gophercloud.BuildRequestBody(bd, "") - if err != nil { - return nil, err - } - blockDevice[i] = b - } - serverMap["block_device_mapping_v2"] = blockDevice - - return base, nil -} - -// Create requests the creation of a server from the given block device mapping. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) (r servers.CreateResult) { - b, err := opts.ToServerCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/bootfromvolume/results.go b/openstack/compute/v2/extensions/bootfromvolume/results.go deleted file mode 100644 index 8a36abbb6d..0000000000 --- a/openstack/compute/v2/extensions/bootfromvolume/results.go +++ /dev/null @@ -1,12 +0,0 @@ -package bootfromvolume - -import ( - os "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" -) - -// CreateResult temporarily contains the response from a Create call. -// It embeds the standard servers.CreateResults type and so can be used the -// same way as a standard server request result. -type CreateResult struct { - os.CreateResult -} diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go b/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go deleted file mode 100644 index cf5048acba..0000000000 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// bootfromvolume unit tests -package testing diff --git a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go b/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go deleted file mode 100644 index 5c2ff60d38..0000000000 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/fixtures_test.go +++ /dev/null @@ -1,360 +0,0 @@ -package testing - -import ( - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/bootfromvolume" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" -) - -var BaseCreateOpts = servers.CreateOpts{ - Name: "createdserver", - FlavorRef: "performance1-1", -} - -var BaseCreateOptsWithImageRef = servers.CreateOpts{ - Name: "createdserver", - FlavorRef: "performance1-1", - ImageRef: "asdfasdfasdf", -} - -const ExpectedNewVolumeRequest = ` -{ - "server": { - "name":"createdserver", - "flavorRef":"performance1-1", - "imageRef":"", - "block_device_mapping_v2":[ - { - "uuid":"123456", - "source_type":"image", - "destination_type":"volume", - "boot_index": 0, - "delete_on_termination": true, - "volume_size": 10 - } - ] - } -} -` - -var NewVolumeRequest = bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: BaseCreateOpts, - BlockDevice: []bootfromvolume.BlockDevice{ - { - UUID: "123456", - SourceType: bootfromvolume.SourceImage, - DestinationType: bootfromvolume.DestinationVolume, - VolumeSize: 10, - DeleteOnTermination: true, - }, - }, -} - -const ExpectedExistingVolumeRequest = ` -{ - "server": { - "name":"createdserver", - "flavorRef":"performance1-1", - "imageRef":"", - "block_device_mapping_v2":[ - { - "uuid":"123456", - "source_type":"volume", - "destination_type":"volume", - "boot_index": 0, - "delete_on_termination": true - } - ] - } -} -` - -var ExistingVolumeRequest = bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: BaseCreateOpts, - BlockDevice: []bootfromvolume.BlockDevice{ - { - UUID: "123456", - SourceType: bootfromvolume.SourceVolume, - DestinationType: bootfromvolume.DestinationVolume, - DeleteOnTermination: true, - }, - }, -} - -const ExpectedImageRequest = ` -{ - "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" - } - ] - } -} -` - -var ImageRequest = bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: BaseCreateOptsWithImageRef, - BlockDevice: []bootfromvolume.BlockDevice{ - { - BootIndex: 0, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - SourceType: bootfromvolume.SourceImage, - UUID: "asdfasdfasdf", - }, - }, -} - -const ExpectedMultiEphemeralRequest = ` -{ - "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":"local", - "guest_format":"ext4", - "source_type":"blank", - "volume_size": 1 - }, - { - "boot_index": -1, - "delete_on_termination": true, - "destination_type":"local", - "guest_format":"ext4", - "source_type":"blank", - "volume_size": 1 - } - ] - } -} -` - -var MultiEphemeralRequest = 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.DestinationLocal, - GuestFormat: "ext4", - SourceType: bootfromvolume.SourceBlank, - VolumeSize: 1, - }, - { - BootIndex: -1, - DeleteOnTermination: true, - DestinationType: bootfromvolume.DestinationLocal, - GuestFormat: "ext4", - SourceType: bootfromvolume.SourceBlank, - VolumeSize: 1, - }, - }, -} - -const ExpectedImageAndNewVolumeRequest = ` -{ - "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":"blank", - "volume_size": 1, - "device_type": "disk", - "disk_bus": "scsi" - } - ] - } -} -` - -var ImageAndNewVolumeRequest = 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.SourceBlank, - VolumeSize: 1, - DeviceType: "disk", - DiskBus: "scsi", - }, - }, -} - -const ExpectedImageAndExistingVolumeRequest = ` -{ - "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", - "uuid":"123456", - "volume_size": 1 - } - ] - } -} -` - -var ImageAndExistingVolumeRequest = 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, - UUID: "123456", - VolumeSize: 1, - }, - }, -} - -const ExpectedNewVolumeTypeRequest = ` -{ - "server": { - "name":"createdserver", - "flavorRef":"performance1-1", - "imageRef":"", - "block_device_mapping_v2":[ - { - "uuid":"123456", - "source_type":"image", - "destination_type":"volume", - "boot_index": 0, - "delete_on_termination": true, - "volume_size": 10, - "volume_type": "ssd" - } - ] - } -} -` - -var NewVolumeTypeRequest = bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: BaseCreateOpts, - BlockDevice: []bootfromvolume.BlockDevice{ - { - UUID: "123456", - SourceType: bootfromvolume.SourceImage, - DestinationType: bootfromvolume.DestinationVolume, - VolumeSize: 10, - DeleteOnTermination: true, - VolumeType: "ssd", - }, - }, -} - -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 deleted file mode 100644 index b5fa104773..0000000000 --- a/openstack/compute/v2/extensions/bootfromvolume/testing/requests_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package testing - -import ( - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestBootFromNewVolume(t *testing.T) { - actual, err := NewVolumeRequest.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, ExpectedNewVolumeRequest, actual) -} - -func TestBootFromExistingVolume(t *testing.T) { - actual, err := ExistingVolumeRequest.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, ExpectedExistingVolumeRequest, actual) -} - -func TestBootFromImage(t *testing.T) { - actual, err := ImageRequest.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, ExpectedImageRequest, actual) -} - -func TestCreateMultiEphemeralOpts(t *testing.T) { - actual, err := MultiEphemeralRequest.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, ExpectedMultiEphemeralRequest, actual) -} - -func TestAttachNewVolume(t *testing.T) { - actual, err := ImageAndNewVolumeRequest.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, ExpectedImageAndNewVolumeRequest, actual) -} - -func TestAttachExistingVolume(t *testing.T) { - actual, err := ImageAndExistingVolumeRequest.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, ExpectedImageAndExistingVolumeRequest, actual) -} - -func TestBootFromNewVolumeType(t *testing.T) { - actual, err := NewVolumeTypeRequest.ToServerCreateMap() - 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) -} diff --git a/openstack/compute/v2/extensions/bootfromvolume/urls.go b/openstack/compute/v2/extensions/bootfromvolume/urls.go deleted file mode 100644 index b6aa7d652b..0000000000 --- a/openstack/compute/v2/extensions/bootfromvolume/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package bootfromvolume - -import "github.com/gophercloud/gophercloud/v2" - -func createURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("servers") -} diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index fe9ce88731..83a2e4015f 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -58,6 +58,129 @@ Example to Create a Server panic(err) } +# Example to Create a Server From an Image + +This example will boot a server from an image and use a standard ephemeral +disk as the server's root disk. This is virtually no different than creating +a server without using block device mappings. + + blockDevices := []servers.BlockDevice{ + servers.BlockDevice{ + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, + UUID: "image-uuid", + }, + } + + createOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + ImageRef: "image-uuid", + BlockDevice: blockDevices, + } + + server, err := servers.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + panic(err) + } + +# Example to Create a Server From a New Volume + +This example will create a block storage volume based on the given Image. The +server will use this volume as its root disk. + + blockDevices := []servers.BlockDevice{ + servers.BlockDevice{ + DeleteOnTermination: true, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceImage, + UUID: "image-uuid", + VolumeSize: 2, + }, + } + + createOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + BlockDevice: blockDevices, + } + + server, err := servers.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + panic(err) + } + +# Example to Create a Server From an Existing Volume + +This example will create a server with an existing volume as its root disk. + + blockDevices := []servers.BlockDevice{ + servers.BlockDevice{ + DeleteOnTermination: true, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceVolume, + UUID: "volume-uuid", + }, + } + + createOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + BlockDevice: blockDevices, + } + + server, err := servers.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + panic(err) + } + +# Example to Create a Server with Multiple Ephemeral Disks + +This example will create a server with multiple ephemeral disks. The first +block device will be based off of an existing Image. Each additional +ephemeral disks must have an index of -1. + + blockDevices := []servers.BlockDevice{ + servers.BlockDevice{ + BootIndex: 0, + DestinationType: servers.DestinationLocal, + DeleteOnTermination: true, + SourceType: servers.SourceImage, + UUID: "image-uuid", + VolumeSize: 5, + }, + servers.BlockDevice{ + BootIndex: -1, + DestinationType: servers.DestinationLocal, + DeleteOnTermination: true, + GuestFormat: "ext4", + SourceType: servers.SourceBlank, + VolumeSize: 1, + }, + servers.BlockDevice{ + BootIndex: -1, + DestinationType: servers.DestinationLocal, + DeleteOnTermination: true, + GuestFormat: "ext4", + SourceType: servers.SourceBlank, + VolumeSize: 1, + }, + } + + CreateOpts := servers.CreateOpts{ + Name: "server_name", + FlavorRef: "flavor-uuid", + ImageRef: "image-uuid", + BlockDevice: blockDevices, + } + + server, err := servers.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + panic(err) + } + Example to Delete a Server serverID := "d9072956-1560-487c-97f2-18bdf65ec749" diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 8566d74647..8fdbeac5da 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -153,6 +153,91 @@ type Network struct { Tag string } +type ( + // DestinationType represents the type of medium being used as the + // destination of the bootable device. + DestinationType string + + // SourceType represents the type of medium being used as the source of the + // bootable device. + SourceType string +) + +const ( + // DestinationLocal DestinationType is for using an ephemeral disk as the + // destination. + DestinationLocal DestinationType = "local" + + // DestinationVolume DestinationType is for using a volume as the destination. + DestinationVolume DestinationType = "volume" + + // SourceBlank SourceType is for a "blank" or empty source. + SourceBlank SourceType = "blank" + + // SourceImage SourceType is for using images as the source of a block device. + SourceImage SourceType = "image" + + // SourceSnapshot SourceType is for using a volume snapshot as the source of + // a block device. + SourceSnapshot SourceType = "snapshot" + + // SourceVolume SourceType is for using a volume as the source of block + // device. + SourceVolume SourceType = "volume" +) + +// BlockDevice is a structure with options for creating block devices in a +// server. The block device may be created from an image, snapshot, new volume, +// or existing volume. The destination may be a new volume, existing volume +// which will be attached to the instance, ephemeral disk, or boot device. +type BlockDevice struct { + // SourceType must be one of: "volume", "snapshot", "image", or "blank". + SourceType SourceType `json:"source_type" required:"true"` + + // UUID is the unique identifier for the existing volume, snapshot, or + // image (see above). + UUID string `json:"uuid,omitempty"` + + // BootIndex is the boot index. It defaults to 0. + BootIndex int `json:"boot_index"` + + // DeleteOnTermination specifies whether or not to delete the attached volume + // when the server is deleted. Defaults to `false`. + DeleteOnTermination bool `json:"delete_on_termination"` + + // DestinationType is the type that gets created. Possible values are "volume" + // and "local". + DestinationType DestinationType `json:"destination_type,omitempty"` + + // GuestFormat specifies the format of the block device. + // Not specifying this will cause the device to be formatted to the default in Nova + // which is currently vfat. + // https://opendev.org/openstack/nova/src/commit/d0b459423dd81644e8d9382b6c87fabaa4f03ad4/nova/privsep/fs.py#L257 + GuestFormat string `json:"guest_format,omitempty"` + + // VolumeSize is the size of the volume to create (in gigabytes). This can be + // omitted for existing volumes. + VolumeSize int `json:"volume_size,omitempty"` + + // DeviceType specifies the device type of the block devices. + // Examples of this are disk, cdrom, floppy, lun, etc. + DeviceType string `json:"device_type,omitempty"` + + // DiskBus is the bus type of the block devices. + // Examples of this are ide, usb, virtio, scsi, etc. + DiskBus string `json:"disk_bus,omitempty"` + + // VolumeType is the volume type of the block device. + // This requires Compute API microversion 2.67 or later. + VolumeType string `json:"volume_type,omitempty"` + + // Tag is an arbitrary string that can be applied to a block device. + // Information about the device tags can be obtained from the metadata API + // and the config drive, allowing devices to be easily identified. + // This requires Compute API microversion 2.42 or later. + Tag string `json:"tag,omitempty"` +} + // Personality is an array of files that are injected into the server at launch. type Personality []*File @@ -247,6 +332,9 @@ type CreateOpts struct { // be a Fully Qualified Domain Name (FQDN) of up to 255 characters in length. // If not set, OpenStack will derive the server's hostname from the Name field. Hostname string `json:"hostname,omitempty"` + + // BlockDevice describes the mapping of various block devices. + BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"` } // ToServerCreateMap assembles a request body based on the contents of a @@ -322,7 +410,9 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO r.Err = err return } - resp, err := client.Post(ctx, listURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(ctx, createURL(client), reqBody, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, + }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index bb130d568f..5911553eee 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -220,6 +220,402 @@ func TestCreateServerWithHostname(t *testing.T) { th.CheckDeepEquals(t, ServerDerp, *actual) } +func TestCreateServerWithBFVBootFromNewVolume(t *testing.T) { + opts := servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + BlockDevice: []servers.BlockDevice{ + { + UUID: "123456", + SourceType: servers.SourceImage, + DestinationType: servers.DestinationVolume, + VolumeSize: 10, + DeleteOnTermination: true, + }, + }, + } + expected := ` + { + "server": { + "name":"createdserver", + "flavorRef":"performance1-1", + "imageRef":"", + "block_device_mapping_v2":[ + { + "uuid":"123456", + "source_type":"image", + "destination_type":"volume", + "boot_index": 0, + "delete_on_termination": true, + "volume_size": 10 + } + ] + } + } + ` + + actual, err := opts.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + +func TestCreateServerWithBFVBootFromExistingVolume(t *testing.T) { + opts := servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + BlockDevice: []servers.BlockDevice{ + { + UUID: "123456", + SourceType: servers.SourceVolume, + DestinationType: servers.DestinationVolume, + DeleteOnTermination: true, + }, + }, + } + expected := ` + { + "server": { + "name":"createdserver", + "flavorRef":"performance1-1", + "imageRef":"", + "block_device_mapping_v2":[ + { + "uuid":"123456", + "source_type":"volume", + "destination_type":"volume", + "boot_index": 0, + "delete_on_termination": true + } + ] + } + } + ` + + actual, err := opts.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + +func TestCreateServerWithBFVBootFromImage(t *testing.T) { + var ImageRequest = servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + ImageRef: "asdfasdfasdf", + BlockDevice: []servers.BlockDevice{ + { + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, + UUID: "asdfasdfasdf", + }, + }, + } + const ExpectedImageRequest = ` + { + "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" + } + ] + } + } + ` + + actual, err := ImageRequest.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, ExpectedImageRequest, actual) +} + +func TestCreateServerWithBFVCreateMultiEphemeralOpts(t *testing.T) { + var MultiEphemeralRequest = servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + ImageRef: "asdfasdfasdf", + BlockDevice: []servers.BlockDevice{ + { + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, + UUID: "asdfasdfasdf", + }, + { + BootIndex: -1, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + GuestFormat: "ext4", + SourceType: servers.SourceBlank, + VolumeSize: 1, + }, + { + BootIndex: -1, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + GuestFormat: "ext4", + SourceType: servers.SourceBlank, + VolumeSize: 1, + }, + }, + } + const ExpectedMultiEphemeralRequest = ` + { + "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":"local", + "guest_format":"ext4", + "source_type":"blank", + "volume_size": 1 + }, + { + "boot_index": -1, + "delete_on_termination": true, + "destination_type":"local", + "guest_format":"ext4", + "source_type":"blank", + "volume_size": 1 + } + ] + } + } + ` + + actual, err := MultiEphemeralRequest.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, ExpectedMultiEphemeralRequest, actual) +} + +func TestCreateServerWithBFVAttachNewVolume(t *testing.T) { + opts := servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + ImageRef: "asdfasdfasdf", + BlockDevice: []servers.BlockDevice{ + { + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, + UUID: "asdfasdfasdf", + }, + { + BootIndex: 1, + DeleteOnTermination: true, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceBlank, + VolumeSize: 1, + DeviceType: "disk", + DiskBus: "scsi", + }, + }, + } + expected := ` + { + "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":"blank", + "volume_size": 1, + "device_type": "disk", + "disk_bus": "scsi" + } + ] + } + } + ` + + actual, err := opts.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + +func TestCreateServerWithBFVAttachExistingVolume(t *testing.T) { + opts := servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + ImageRef: "asdfasdfasdf", + BlockDevice: []servers.BlockDevice{ + { + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, + UUID: "asdfasdfasdf", + }, + { + BootIndex: 1, + DeleteOnTermination: true, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceVolume, + UUID: "123456", + VolumeSize: 1, + }, + }, + } + expected := ` + { + "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", + "uuid":"123456", + "volume_size": 1 + } + ] + } + } + ` + + actual, err := opts.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + +func TestCreateServerWithBFVBootFromNewVolumeType(t *testing.T) { + var NewVolumeTypeRequest = servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + BlockDevice: []servers.BlockDevice{ + { + UUID: "123456", + SourceType: servers.SourceImage, + DestinationType: servers.DestinationVolume, + VolumeSize: 10, + DeleteOnTermination: true, + VolumeType: "ssd", + }, + }, + } + const ExpectedNewVolumeTypeRequest = ` + { + "server": { + "name":"createdserver", + "flavorRef":"performance1-1", + "imageRef":"", + "block_device_mapping_v2":[ + { + "uuid":"123456", + "source_type":"image", + "destination_type":"volume", + "boot_index": 0, + "delete_on_termination": true, + "volume_size": 10, + "volume_type": "ssd" + } + ] + } + } + ` + + actual, err := NewVolumeTypeRequest.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, ExpectedNewVolumeTypeRequest, actual) +} + +func TestCreateServerWithBFVAttachExistingVolumeWithTag(t *testing.T) { + var ImageAndExistingVolumeWithTagRequest = servers.CreateOpts{ + Name: "createdserver", + FlavorRef: "performance1-1", + ImageRef: "asdfasdfasdf", + BlockDevice: []servers.BlockDevice{ + { + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: servers.DestinationLocal, + SourceType: servers.SourceImage, + UUID: "asdfasdfasdf", + }, + { + BootIndex: -1, + DeleteOnTermination: true, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceVolume, + Tag: "volume-tag", + UUID: "123456", + VolumeSize: 1, + }, + }, + } + 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 + } + ] + } + } + ` + + actual, err := ImageAndExistingVolumeWithTagRequest.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, ExpectedImageAndExistingVolumeWithTagRequest, actual) +} + func TestDeleteServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From 20ef7ceacbf886fe5ff040f3d4df6abdaff4d79d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 26 Feb 2024 17:13:35 +0000 Subject: [PATCH 1810/2296] compute: Merge extendedserverattributes extension Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/servers_test.go | 25 +++---- .../extendedserverattributes/doc.go | 67 ------------------- .../extendedserverattributes/results.go | 44 ------------ .../extendedserverattributes/testing/doc.go | 1 - .../testing/fixtures_test.go | 28 -------- .../testing/requests_test.go | 45 ------------- openstack/compute/v2/servers/results.go | 38 +++++++++++ .../v2/servers/testing/fixtures_test.go | 25 ++++++- 8 files changed, 71 insertions(+), 202 deletions(-) delete mode 100644 openstack/compute/v2/extensions/extendedserverattributes/doc.go delete mode 100644 openstack/compute/v2/extensions/extendedserverattributes/results.go delete mode 100644 openstack/compute/v2/extensions/extendedserverattributes/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index bb5aed5a63..84d44e2ada 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -14,7 +14,6 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedserverattributes" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedstatus" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" @@ -572,24 +571,18 @@ func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - type serverAttributesExt struct { - servers.Server - extendedserverattributes.ServerAttributesExt - } - var serverWithAttributesExt serverAttributesExt - - err = servers.Get(context.TODO(), client, server.ID).ExtractInto(&serverWithAttributesExt) + created, err := servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) - t.Logf("Server With Extended Attributes: %#v", serverWithAttributesExt) + t.Logf("Server With Extended Attributes: %#v", created) - th.AssertEquals(t, *serverWithAttributesExt.ReservationID != "", true) - th.AssertEquals(t, *serverWithAttributesExt.LaunchIndex, 0) - th.AssertEquals(t, *serverWithAttributesExt.RAMDiskID == "", true) - th.AssertEquals(t, *serverWithAttributesExt.KernelID == "", true) - th.AssertEquals(t, *serverWithAttributesExt.Hostname != "", true) - th.AssertEquals(t, *serverWithAttributesExt.RootDeviceName != "", true) - th.AssertEquals(t, serverWithAttributesExt.Userdata == nil, true) + th.AssertEquals(t, *created.ReservationID != "", true) + th.AssertEquals(t, *created.LaunchIndex, 0) + th.AssertEquals(t, *created.RAMDiskID == "", true) + th.AssertEquals(t, *created.KernelID == "", true) + th.AssertEquals(t, *created.Hostname != "", true) + th.AssertEquals(t, *created.RootDeviceName != "", true) + th.AssertEquals(t, created.Userdata == nil, true) } func TestServerNoNetworkCreateDestroy(t *testing.T) { diff --git a/openstack/compute/v2/extensions/extendedserverattributes/doc.go b/openstack/compute/v2/extensions/extendedserverattributes/doc.go deleted file mode 100644 index afe20573b4..0000000000 --- a/openstack/compute/v2/extensions/extendedserverattributes/doc.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Package extendedserverattributes provides the ability to extend a -server result with the extended usage information. - -Example to Get basic extended information: - - type serverAttributesExt struct { - servers.Server - extendedserverattributes.ServerAttributesExt - } - var serverWithAttributesExt serverAttributesExt - - err := servers.Get(context.TODO(), computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", serverWithAttributesExt) - -Example to get additional fields with microversion 2.3 or later - - computeClient.Microversion = "2.3" - result := servers.Get(context.TODO(), computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a") - - 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) - - 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) - - 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) - - 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/extendedserverattributes/results.go b/openstack/compute/v2/extensions/extendedserverattributes/results.go deleted file mode 100644 index 1640cf5106..0000000000 --- a/openstack/compute/v2/extensions/extendedserverattributes/results.go +++ /dev/null @@ -1,44 +0,0 @@ -package extendedserverattributes - -// ServerAttributesExt represents basic OS-EXT-SRV-ATTR server response fields. -// You should use extract methods from microversions.go to retrieve additional -// fields. -type ServerAttributesExt struct { - // Host is the host/hypervisor that the instance is hosted on. - Host string `json:"OS-EXT-SRV-ATTR:host"` - - // InstanceName is the name of the instance. - InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` - - // HypervisorHostname is the hostname of the host/hypervisor that the - // instance is hosted on. - HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` - - // ReservationID is the reservation ID of the instance. - // This requires microversion 2.3 or later. - ReservationID *string `json:"OS-EXT-SRV-ATTR:reservation_id"` - - // LaunchIndex is the launch index of the instance. - // This requires microversion 2.3 or later. - LaunchIndex *int `json:"OS-EXT-SRV-ATTR:launch_index"` - - // RAMDiskID is the ID of the RAM disk image of the instance. - // This requires microversion 2.3 or later. - RAMDiskID *string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` - - // KernelID is the ID of the kernel image of the instance. - // This requires microversion 2.3 or later. - KernelID *string `json:"OS-EXT-SRV-ATTR:kernel_id"` - - // Hostname is the hostname of the instance. - // This requires microversion 2.3 or later. - Hostname *string `json:"OS-EXT-SRV-ATTR:hostname"` - - // RootDeviceName is the name of the root device of the instance. - // This requires microversion 2.3 or later. - RootDeviceName *string `json:"OS-EXT-SRV-ATTR:root_device_name"` - - // Userdata is the userdata of the instance. - // This requires microversion 2.3 or later. - Userdata *string `json:"OS-EXT-SRV-ATTR:user_data"` -} diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/doc.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/doc.go deleted file mode 100644 index 7603f836a0..0000000000 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/doc.go +++ /dev/null @@ -1 +0,0 @@ -package testing diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures_test.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures_test.go deleted file mode 100644 index 1034bee09f..0000000000 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/fixtures_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package testing - -// ServerWithAttributesExtResult represents a raw server response from the -// Compute API with OS-EXT-SRV-ATTR data. -// Most of the actual fields were deleted from the response. -const ServerWithAttributesExtResult = ` -{ - "server": { - "OS-EXT-SRV-ATTR:user_data": "Zm9v", - "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", - "OS-EXT-SRV-ATTR:root_device_name": "/dev/sda", - "OS-EXT-SRV-ATTR:hostname": "test00", - "OS-EXT-SRV-ATTR:reservation_id": "r-ky9gim1l", - "OS-EXT-SRV-ATTR:ramdisk_id": "", - "OS-EXT-SRV-ATTR:host": "compute01", - "OS-EXT-SRV-ATTR:kernel_id": "", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "compute01", - "OS-EXT-SRV-ATTR:launch_index": 0, - "created": "2018-07-27T09:15:48Z", - "updated": "2018-07-27T09:15:55Z", - "id": "d650a0ce-17c3-497d-961a-43c4af80998a", - "name": "test_instance", - "status": "ACTIVE", - "user_id": "0f2f3822679e4b3ea073e5d1c6ed5f02", - "tenant_id": "424e7cf0243c468ca61732ba45973b3e" - } -} -` diff --git a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go b/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go deleted file mode 100644 index fb2752b505..0000000000 --- a/openstack/compute/v2/extensions/extendedserverattributes/testing/requests_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedserverattributes" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestServerWithUsageExt(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/servers/d650a0ce-17c3-497d-961a-43c4af80998a", 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.Fprintf(w, ServerWithAttributesExtResult) - }) - - type serverAttributesExt struct { - servers.Server - extendedserverattributes.ServerAttributesExt - } - var serverWithAttributesExt serverAttributesExt - - // Extract basic fields. - err := servers.Get(context.TODO(), fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithAttributesExt) - th.AssertNoErr(t, err) - - th.AssertEquals(t, serverWithAttributesExt.Host, "compute01") - th.AssertEquals(t, serverWithAttributesExt.InstanceName, "instance-00000001") - th.AssertEquals(t, serverWithAttributesExt.HypervisorHostname, "compute01") - th.AssertEquals(t, *serverWithAttributesExt.Userdata, "Zm9v") - th.AssertEquals(t, *serverWithAttributesExt.ReservationID, "r-ky9gim1l") - th.AssertEquals(t, *serverWithAttributesExt.LaunchIndex, 0) - th.AssertEquals(t, *serverWithAttributesExt.Hostname, "test00") - th.AssertEquals(t, *serverWithAttributesExt.RootDeviceName, "/dev/sda") -} diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 26b3463baa..74653ee35f 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -228,6 +228,44 @@ type Server struct { // contain at most one entry. // New in microversion 2.71 ServerGroups *[]string `json:"server_groups"` + + // Host is the host/hypervisor that the instance is hosted on. + Host string `json:"OS-EXT-SRV-ATTR:host"` + + // InstanceName is the name of the instance. + InstanceName string `json:"OS-EXT-SRV-ATTR:instance_name"` + + // HypervisorHostname is the hostname of the host/hypervisor that the + // instance is hosted on. + HypervisorHostname string `json:"OS-EXT-SRV-ATTR:hypervisor_hostname"` + + // ReservationID is the reservation ID of the instance. + // This requires microversion 2.3 or later. + ReservationID *string `json:"OS-EXT-SRV-ATTR:reservation_id"` + + // LaunchIndex is the launch index of the instance. + // This requires microversion 2.3 or later. + LaunchIndex *int `json:"OS-EXT-SRV-ATTR:launch_index"` + + // RAMDiskID is the ID of the RAM disk image of the instance. + // This requires microversion 2.3 or later. + RAMDiskID *string `json:"OS-EXT-SRV-ATTR:ramdisk_id"` + + // KernelID is the ID of the kernel image of the instance. + // This requires microversion 2.3 or later. + KernelID *string `json:"OS-EXT-SRV-ATTR:kernel_id"` + + // Hostname is the hostname of the instance. + // This requires microversion 2.3 or later. + Hostname *string `json:"OS-EXT-SRV-ATTR:hostname"` + + // RootDeviceName is the name of the root device of the instance. + // This requires microversion 2.3 or later. + RootDeviceName *string `json:"OS-EXT-SRV-ATTR:root_device_name"` + + // Userdata is the userdata of the instance. + // This requires microversion 2.3 or later. + Userdata *string `json:"OS-EXT-SRV-ATTR:user_data"` } type AttachedVolume struct { diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index 226a129896..26a346c902 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -267,7 +267,6 @@ const SingleServerBody = ` "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "active", "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", - "OS-EXT-SRV-ATTR:hostname": "derp.local", "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", "flavor": { @@ -545,6 +544,14 @@ var ( "name": "default", }, }, + Host: "devstack", + Hostname: nil, + HypervisorHostname: "devstack", + InstanceName: "instance-0000001e", + LaunchIndex: nil, + ReservationID: nil, + RootDeviceName: nil, + Userdata: nil, } derpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:41Z") @@ -604,6 +611,14 @@ var ( "name": "default", }, }, + Host: "devstack", + Hostname: nil, + HypervisorHostname: "devstack", + InstanceName: "instance-0000001d", + LaunchIndex: nil, + ReservationID: nil, + RootDeviceName: nil, + Userdata: nil, } ConsoleOutput = "abc" @@ -657,6 +672,14 @@ var ( "name": "default", }, }, + Host: "devstack", + Hostname: nil, + HypervisorHostname: "devstack", + InstanceName: "instance-0000001d", + LaunchIndex: nil, + ReservationID: nil, + RootDeviceName: nil, + Userdata: nil, } faultTimeCreated, _ = time.Parse(time.RFC3339, "2017-11-11T07:58:39Z") From 345d7e0a5529438e33158eb0d27f05d47d246a61 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 26 Feb 2024 17:28:28 +0000 Subject: [PATCH 1811/2296] compute: Merge extendedstatus extension Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/servers_test.go | 4 +- .../v2/extensions/extendedstatus/doc.go | 28 ------------ .../v2/extensions/extendedstatus/results.go | 41 ----------------- openstack/compute/v2/servers/results.go | 44 +++++++++++++++++++ .../v2/servers/testing/fixtures_test.go | 6 +++ .../v2/servers/testing/requests_test.go | 3 -- 6 files changed, 51 insertions(+), 75 deletions(-) delete mode 100644 openstack/compute/v2/extensions/extendedstatus/doc.go delete mode 100644 openstack/compute/v2/extensions/extendedstatus/results.go diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 84d44e2ada..080b3617d0 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -14,7 +14,6 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedstatus" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/serverusage" @@ -92,7 +91,6 @@ func TestServersWithExtensionsCreateDestroy(t *testing.T) { var extendedServer struct { servers.Server availabilityzones.ServerAvailabilityZoneExt - extendedstatus.ServerExtendedStatusExt serverusage.UsageExt } @@ -108,7 +106,7 @@ func TestServersWithExtensionsCreateDestroy(t *testing.T) { tools.PrintResource(t, extendedServer) th.AssertEquals(t, extendedServer.AvailabilityZone, "nova") - th.AssertEquals(t, int(extendedServer.PowerState), extendedstatus.RUNNING) + th.AssertEquals(t, int(extendedServer.PowerState), servers.RUNNING) th.AssertEquals(t, extendedServer.TaskState, "") th.AssertEquals(t, extendedServer.VmState, "active") th.AssertEquals(t, extendedServer.LaunchedAt.IsZero(), false) diff --git a/openstack/compute/v2/extensions/extendedstatus/doc.go b/openstack/compute/v2/extensions/extendedstatus/doc.go deleted file mode 100644 index 3a11d1f8fc..0000000000 --- a/openstack/compute/v2/extensions/extendedstatus/doc.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Package extendedstatus provides the ability to extend a server result with -the extended status information. Example: - - type ServerWithExt struct { - servers.Server - extendedstatus.ServerExtendedStatusExt - } - - var allServers []ServerWithExt - - allPages, err := servers.List(client, nil).AllPages(context.TODO()) - if err != nil { - panic("Unable to retrieve servers: %s", err) - } - - err = servers.ExtractServersInto(allPages, &allServers) - if err != nil { - panic("Unable to extract servers: %s", err) - } - - for _, server := range allServers { - fmt.Println(server.TaskState) - fmt.Println(server.VmState) - fmt.Println(server.PowerState) - } -*/ -package extendedstatus diff --git a/openstack/compute/v2/extensions/extendedstatus/results.go b/openstack/compute/v2/extensions/extendedstatus/results.go deleted file mode 100644 index acfbd3fb24..0000000000 --- a/openstack/compute/v2/extensions/extendedstatus/results.go +++ /dev/null @@ -1,41 +0,0 @@ -package extendedstatus - -type PowerState int - -type ServerExtendedStatusExt struct { - TaskState string `json:"OS-EXT-STS:task_state"` - VmState string `json:"OS-EXT-STS:vm_state"` - PowerState PowerState `json:"OS-EXT-STS:power_state"` -} - -const ( - NOSTATE = iota - RUNNING - _UNUSED1 - PAUSED - SHUTDOWN - _UNUSED2 - CRASHED - SUSPENDED -) - -func (r PowerState) String() string { - switch r { - case NOSTATE: - return "NOSTATE" - case RUNNING: - return "RUNNING" - case PAUSED: - return "PAUSED" - case SHUTDOWN: - return "SHUTDOWN" - case CRASHED: - return "CRASHED" - case SUSPENDED: - return "SUSPENDED" - case _UNUSED1, _UNUSED2: - return "_UNUSED" - default: - return "N/A" - } -} diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 74653ee35f..7319346f21 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -266,6 +266,10 @@ type Server struct { // Userdata is the userdata of the instance. // This requires microversion 2.3 or later. Userdata *string `json:"OS-EXT-SRV-ATTR:user_data"` + + TaskState string `json:"OS-EXT-STS:task_state"` + VmState string `json:"OS-EXT-STS:vm_state"` + PowerState PowerState `json:"OS-EXT-STS:power_state"` } type AttachedVolume struct { @@ -279,6 +283,46 @@ type Fault struct { Message string `json:"message"` } +type PowerState int + +type ServerExtendedStatusExt struct { + TaskState string `json:"OS-EXT-STS:task_state"` + VmState string `json:"OS-EXT-STS:vm_state"` + PowerState PowerState `json:"OS-EXT-STS:power_state"` +} + +const ( + NOSTATE = iota + RUNNING + _UNUSED1 + PAUSED + SHUTDOWN + _UNUSED2 + CRASHED + SUSPENDED +) + +func (r PowerState) String() string { + switch r { + case NOSTATE: + return "NOSTATE" + case RUNNING: + return "RUNNING" + case PAUSED: + return "PAUSED" + case SHUTDOWN: + return "SHUTDOWN" + case CRASHED: + return "CRASHED" + case SUSPENDED: + return "SUSPENDED" + case _UNUSED1, _UNUSED2: + return "_UNUSED" + default: + return "N/A" + } +} + func (r *Server) UnmarshalJSON(b []byte) error { type tmp Server var s struct { diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index 26a346c902..f83851bd7e 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -552,6 +552,8 @@ var ( ReservationID: nil, RootDeviceName: nil, Userdata: nil, + VmState: "active", + PowerState: servers.RUNNING, } derpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:41Z") @@ -619,6 +621,8 @@ var ( ReservationID: nil, RootDeviceName: nil, Userdata: nil, + VmState: "active", + PowerState: servers.RUNNING, } ConsoleOutput = "abc" @@ -680,6 +684,8 @@ var ( ReservationID: nil, RootDeviceName: nil, Userdata: nil, + VmState: "active", + PowerState: servers.RUNNING, } faultTimeCreated, _ = time.Parse(time.RFC3339, "2017-11-11T07:58:39Z") diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 5911553eee..5ca4047499 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -9,7 +9,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diskconfig" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/extendedstatus" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -68,7 +67,6 @@ func TestListAllServersWithExtensions(t *testing.T) { type ServerWithExt struct { servers.Server availabilityzones.ServerAvailabilityZoneExt - extendedstatus.ServerExtendedStatusExt diskconfig.ServerDiskConfigExt } @@ -672,7 +670,6 @@ func TestGetServerWithExtensions(t *testing.T) { var s struct { servers.Server availabilityzones.ServerAvailabilityZoneExt - extendedstatus.ServerExtendedStatusExt diskconfig.ServerDiskConfigExt } From d294834aaf120647a3f86fcb60f9c9cb9d67f170 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 26 Feb 2024 17:48:57 +0000 Subject: [PATCH 1812/2296] compute: Merge serverusage extension Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/servers_test.go | 2 - .../compute/v2/extensions/serverusage/doc.go | 20 --------- .../v2/extensions/serverusage/results.go | 34 -------------- .../v2/extensions/serverusage/testing/doc.go | 1 - .../serverusage/testing/fixtures_test.go | 20 --------- .../serverusage/testing/requests_test.go | 45 ------------------- openstack/compute/v2/servers/results.go | 10 ++++- .../v2/servers/testing/fixtures_test.go | 6 +++ 8 files changed, 15 insertions(+), 123 deletions(-) delete mode 100644 openstack/compute/v2/extensions/serverusage/doc.go delete mode 100644 openstack/compute/v2/extensions/serverusage/results.go delete mode 100644 openstack/compute/v2/extensions/serverusage/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/serverusage/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/serverusage/testing/requests_test.go diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 080b3617d0..8eaccd329d 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -16,7 +16,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/serverusage" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/suspendresume" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tags" @@ -91,7 +90,6 @@ func TestServersWithExtensionsCreateDestroy(t *testing.T) { var extendedServer struct { servers.Server availabilityzones.ServerAvailabilityZoneExt - serverusage.UsageExt } client, err := clients.NewComputeV2Client() diff --git a/openstack/compute/v2/extensions/serverusage/doc.go b/openstack/compute/v2/extensions/serverusage/doc.go deleted file mode 100644 index 9f11918ba3..0000000000 --- a/openstack/compute/v2/extensions/serverusage/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Package serverusage provides the ability the ability to extend a server result -with the extended usage information. - -Example to Get an extended information: - - type serverUsageExt struct { - servers.Server - serverusage.UsageExt - } - var serverWithUsageExt serverUsageExt - - err := servers.Get(context.TODO(), computeClient, "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", serverWithUsageExt) -*/ -package serverusage diff --git a/openstack/compute/v2/extensions/serverusage/results.go b/openstack/compute/v2/extensions/serverusage/results.go deleted file mode 100644 index fa637f2940..0000000000 --- a/openstack/compute/v2/extensions/serverusage/results.go +++ /dev/null @@ -1,34 +0,0 @@ -package serverusage - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud/v2" -) - -// UsageExt represents OS-SRV-USG server response fields. -type UsageExt struct { - LaunchedAt time.Time `json:"-"` - TerminatedAt time.Time `json:"-"` -} - -// UnmarshalJSON helps to unmarshal UsageExt fields into needed values. -func (r *UsageExt) UnmarshalJSON(b []byte) error { - type tmp UsageExt - var s struct { - tmp - LaunchedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:launched_at"` - TerminatedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:terminated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = UsageExt(s.tmp) - - r.LaunchedAt = time.Time(s.LaunchedAt) - r.TerminatedAt = time.Time(s.TerminatedAt) - - return nil -} diff --git a/openstack/compute/v2/extensions/serverusage/testing/doc.go b/openstack/compute/v2/extensions/serverusage/testing/doc.go deleted file mode 100644 index 7603f836a0..0000000000 --- a/openstack/compute/v2/extensions/serverusage/testing/doc.go +++ /dev/null @@ -1 +0,0 @@ -package testing diff --git a/openstack/compute/v2/extensions/serverusage/testing/fixtures_test.go b/openstack/compute/v2/extensions/serverusage/testing/fixtures_test.go deleted file mode 100644 index 2d2cf2ef53..0000000000 --- a/openstack/compute/v2/extensions/serverusage/testing/fixtures_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package testing - -// ServerWithUsageExtResult represents a raw server response from the Compute API -// with OS-SRV-USG data. -// Most of the actual fields were deleted from the response. -const ServerWithUsageExtResult = ` -{ - "server": { - "OS-SRV-USG:launched_at": "2018-07-27T09:15:55.000000", - "OS-SRV-USG:terminated_at": null, - "created": "2018-07-27T09:15:48Z", - "updated": "2018-07-27T09:15:55Z", - "id": "d650a0ce-17c3-497d-961a-43c4af80998a", - "name": "test_instance", - "status": "ACTIVE", - "user_id": "0f2f3822679e4b3ea073e5d1c6ed5f02", - "tenant_id": "424e7cf0243c468ca61732ba45973b3e" - } -} -` diff --git a/openstack/compute/v2/extensions/serverusage/testing/requests_test.go b/openstack/compute/v2/extensions/serverusage/testing/requests_test.go deleted file mode 100644 index c81b4553f0..0000000000 --- a/openstack/compute/v2/extensions/serverusage/testing/requests_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/serverusage" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestServerWithUsageExt(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/servers/d650a0ce-17c3-497d-961a-43c4af80998a", 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.Fprintf(w, ServerWithUsageExtResult) - }) - - type serverUsageExt struct { - servers.Server - serverusage.UsageExt - } - var serverWithUsageExt serverUsageExt - err := servers.Get(context.TODO(), fake.ServiceClient(), "d650a0ce-17c3-497d-961a-43c4af80998a").ExtractInto(&serverWithUsageExt) - th.AssertNoErr(t, err) - - th.AssertEquals(t, serverWithUsageExt.LaunchedAt, time.Date(2018, 07, 27, 9, 15, 55, 0, time.UTC)) - th.AssertEquals(t, serverWithUsageExt.TerminatedAt, time.Time{}) - th.AssertEquals(t, serverWithUsageExt.Created, time.Date(2018, 07, 27, 9, 15, 48, 0, time.UTC)) - th.AssertEquals(t, serverWithUsageExt.Updated, time.Date(2018, 07, 27, 9, 15, 55, 0, time.UTC)) - th.AssertEquals(t, serverWithUsageExt.ID, "d650a0ce-17c3-497d-961a-43c4af80998a") - th.AssertEquals(t, serverWithUsageExt.Name, "test_instance") - th.AssertEquals(t, serverWithUsageExt.Status, "ACTIVE") - th.AssertEquals(t, serverWithUsageExt.UserID, "0f2f3822679e4b3ea073e5d1c6ed5f02") - th.AssertEquals(t, serverWithUsageExt.TenantID, "424e7cf0243c468ca61732ba45973b3e") -} diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 7319346f21..1e57da9b49 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -270,6 +270,9 @@ type Server struct { TaskState string `json:"OS-EXT-STS:task_state"` VmState string `json:"OS-EXT-STS:vm_state"` PowerState PowerState `json:"OS-EXT-STS:power_state"` + + LaunchedAt time.Time `json:"-"` + TerminatedAt time.Time `json:"-"` } type AttachedVolume struct { @@ -327,7 +330,9 @@ func (r *Server) UnmarshalJSON(b []byte) error { type tmp Server var s struct { tmp - Image interface{} `json:"image"` + Image interface{} `json:"image"` + LaunchedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:launched_at"` + TerminatedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:terminated_at"` } err := json.Unmarshal(b, &s) if err != nil { @@ -346,6 +351,9 @@ func (r *Server) UnmarshalJSON(b []byte) error { } } + r.LaunchedAt = time.Time(s.LaunchedAt) + r.TerminatedAt = time.Time(s.TerminatedAt) + return err } diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index f83851bd7e..7cd4743eec 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -554,6 +554,8 @@ var ( Userdata: nil, VmState: "active", PowerState: servers.RUNNING, + LaunchedAt: time.Date(2014, 9, 25, 13, 10, 10, 0, time.UTC), + TerminatedAt: time.Time{}, } derpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:41Z") @@ -623,6 +625,8 @@ var ( Userdata: nil, VmState: "active", PowerState: servers.RUNNING, + LaunchedAt: time.Date(2014, 9, 25, 13, 04, 49, 0, time.UTC), + TerminatedAt: time.Time{}, } ConsoleOutput = "abc" @@ -686,6 +690,8 @@ var ( Userdata: nil, VmState: "active", PowerState: servers.RUNNING, + LaunchedAt: time.Date(2014, 9, 25, 13, 04, 49, 0, time.UTC), + TerminatedAt: time.Time{}, } faultTimeCreated, _ = time.Parse(time.RFC3339, "2017-11-11T07:58:39Z") From 050934cf897c312ef35f71578792bd40f4f9cd86 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 26 Feb 2024 18:03:18 +0000 Subject: [PATCH 1813/2296] compute: Merge diskconfig extension Signed-off-by: Stephen Finucane --- .../compute/v2/extensions/diskconfig/doc.go | 46 -------- .../v2/extensions/diskconfig/requests.go | 106 ------------------ .../v2/extensions/diskconfig/results.go | 6 - .../v2/extensions/diskconfig/testing/doc.go | 2 - .../diskconfig/testing/requests_test.go | 88 --------------- openstack/compute/v2/servers/requests.go | 42 +++++++ openstack/compute/v2/servers/results.go | 3 + .../v2/servers/testing/fixtures_test.go | 3 + .../v2/servers/testing/requests_test.go | 72 +++++++++++- 9 files changed, 115 insertions(+), 253 deletions(-) delete mode 100644 openstack/compute/v2/extensions/diskconfig/doc.go delete mode 100644 openstack/compute/v2/extensions/diskconfig/requests.go delete mode 100644 openstack/compute/v2/extensions/diskconfig/results.go delete mode 100644 openstack/compute/v2/extensions/diskconfig/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/diskconfig/testing/requests_test.go diff --git a/openstack/compute/v2/extensions/diskconfig/doc.go b/openstack/compute/v2/extensions/diskconfig/doc.go deleted file mode 100644 index 0ab9b9853e..0000000000 --- a/openstack/compute/v2/extensions/diskconfig/doc.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Package diskconfig provides information and interaction with the Disk Config -extension that works with the OpenStack Compute service. - -Example of Obtaining the Disk Config of a Server - - type ServerWithDiskConfig { - servers.Server - diskconfig.ServerDiskConfigExt - } - - var allServers []ServerWithDiskConfig - - allPages, err := servers.List(client, nil).AllPages(context.TODO()) - if err != nil { - panic("Unable to retrieve servers: %s", err) - } - - err = servers.ExtractServersInto(allPages, &allServers) - if err != nil { - panic("Unable to extract servers: %s", err) - } - - for _, server := range allServers { - fmt.Println(server.DiskConfig) - } - -Example of Creating a Server with Disk Config - - serverCreateOpts := servers.CreateOpts{ - Name: "server_name", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - } - - createOpts := diskconfig.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - DiskConfig: diskconfig.Manual, - } - - server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() - if err != nil { - panic("Unable to create server: %s", err) - } -*/ -package diskconfig diff --git a/openstack/compute/v2/extensions/diskconfig/requests.go b/openstack/compute/v2/extensions/diskconfig/requests.go deleted file mode 100644 index 5638f477f4..0000000000 --- a/openstack/compute/v2/extensions/diskconfig/requests.go +++ /dev/null @@ -1,106 +0,0 @@ -package diskconfig - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" -) - -// DiskConfig represents one of the two possible settings for the DiskConfig -// option when creating, rebuilding, or resizing servers: Auto or Manual. -type DiskConfig string - -const ( - // Auto builds a server with a single partition the size of the target flavor - // disk and automatically adjusts the filesystem to fit the entire partition. - // Auto may only be used with images and servers that use a single EXT3 - // partition. - Auto DiskConfig = "AUTO" - - // Manual builds a server using whatever partition scheme and filesystem are - // present in the source image. If the target flavor disk is larger, the - // remaining space is left unpartitioned. This enables images to have non-EXT3 - // filesystems, multiple partitions, and so on, and enables you to manage the - // disk configuration. It also results in slightly shorter boot times. - Manual DiskConfig = "MANUAL" -) - -// CreateOptsExt adds a DiskConfig option to the base CreateOpts. -type CreateOptsExt struct { - servers.CreateOptsBuilder - - // DiskConfig [optional] controls how the created server's disk is partitioned. - DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` -} - -// ToServerCreateMap adds the diskconfig option to the base server creation options. -func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { - base, err := opts.CreateOptsBuilder.ToServerCreateMap() - if err != nil { - return nil, err - } - - if string(opts.DiskConfig) == "" { - return base, nil - } - - serverMap := base["server"].(map[string]interface{}) - serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) - - return base, nil -} - -// RebuildOptsExt adds a DiskConfig option to the base RebuildOpts. -type RebuildOptsExt struct { - servers.RebuildOptsBuilder - - // DiskConfig controls how the rebuilt server's disk is partitioned. - DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` -} - -// ToServerRebuildMap adds the diskconfig option to the base server rebuild options. -func (opts RebuildOptsExt) ToServerRebuildMap() (map[string]interface{}, error) { - if opts.DiskConfig != Auto && opts.DiskConfig != Manual { - err := gophercloud.ErrInvalidInput{} - err.Argument = "diskconfig.RebuildOptsExt.DiskConfig" - err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" - return nil, err - } - - base, err := opts.RebuildOptsBuilder.ToServerRebuildMap() - if err != nil { - return nil, err - } - - serverMap := base["rebuild"].(map[string]interface{}) - serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) - - return base, nil -} - -// ResizeOptsExt adds a DiskConfig option to the base server resize options. -type ResizeOptsExt struct { - servers.ResizeOptsBuilder - - // DiskConfig [optional] controls how the resized server's disk is partitioned. - DiskConfig DiskConfig -} - -// ToServerResizeMap adds the diskconfig option to the base server creation options. -func (opts ResizeOptsExt) ToServerResizeMap() (map[string]interface{}, error) { - if opts.DiskConfig != Auto && opts.DiskConfig != Manual { - err := gophercloud.ErrInvalidInput{} - err.Argument = "diskconfig.ResizeOptsExt.DiskConfig" - err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" - return nil, err - } - - base, err := opts.ResizeOptsBuilder.ToServerResizeMap() - if err != nil { - return nil, err - } - - serverMap := base["resize"].(map[string]interface{}) - serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) - - return base, nil -} diff --git a/openstack/compute/v2/extensions/diskconfig/results.go b/openstack/compute/v2/extensions/diskconfig/results.go deleted file mode 100644 index 239b2683d7..0000000000 --- a/openstack/compute/v2/extensions/diskconfig/results.go +++ /dev/null @@ -1,6 +0,0 @@ -package diskconfig - -type ServerDiskConfigExt struct { - // DiskConfig is the disk configuration of the server. - DiskConfig DiskConfig `json:"OS-DCF:diskConfig"` -} diff --git a/openstack/compute/v2/extensions/diskconfig/testing/doc.go b/openstack/compute/v2/extensions/diskconfig/testing/doc.go deleted file mode 100644 index 52ab247565..0000000000 --- a/openstack/compute/v2/extensions/diskconfig/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// diskconfig unit tests -package testing diff --git a/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go b/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go deleted file mode 100644 index b21b1cf48e..0000000000 --- a/openstack/compute/v2/extensions/diskconfig/testing/requests_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diskconfig" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestCreateOpts(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - } - - ext := diskconfig.CreateOptsExt{ - CreateOptsBuilder: base, - DiskConfig: diskconfig.Manual, - } - - expected := ` - { - "server": { - "name": "createdserver", - "imageRef": "asdfasdfasdf", - "flavorRef": "performance1-1", - "OS-DCF:diskConfig": "MANUAL" - } - } - ` - actual, err := ext.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) -} - -func TestRebuildOpts(t *testing.T) { - base := servers.RebuildOpts{ - Name: "rebuiltserver", - AdminPass: "swordfish", - ImageRef: "asdfasdfasdf", - } - - ext := diskconfig.RebuildOptsExt{ - RebuildOptsBuilder: base, - DiskConfig: diskconfig.Auto, - } - - actual, err := ext.ToServerRebuildMap() - th.AssertNoErr(t, err) - - expected := ` - { - "rebuild": { - "name": "rebuiltserver", - "imageRef": "asdfasdfasdf", - "adminPass": "swordfish", - "OS-DCF:diskConfig": "AUTO" - } - } - ` - th.CheckJSONEquals(t, expected, actual) -} - -func TestResizeOpts(t *testing.T) { - base := servers.ResizeOpts{ - FlavorRef: "performance1-8", - } - - ext := diskconfig.ResizeOptsExt{ - ResizeOptsBuilder: base, - DiskConfig: diskconfig.Auto, - } - - actual, err := ext.ToServerResizeMap() - th.AssertNoErr(t, err) - - expected := ` - { - "resize": { - "flavorRef": "performance1-8", - "OS-DCF:diskConfig": "AUTO" - } - } - ` - th.CheckJSONEquals(t, expected, actual) -} diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 8fdbeac5da..da007377ed 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -265,6 +265,25 @@ func (f *File) MarshalJSON() ([]byte, error) { return json.Marshal(file) } +// DiskConfig represents one of the two possible settings for the DiskConfig +// option when creating, rebuilding, or resizing servers: Auto or Manual. +type DiskConfig string + +const ( + // Auto builds a server with a single partition the size of the target flavor + // disk and automatically adjusts the filesystem to fit the entire partition. + // Auto may only be used with images and servers that use a single EXT3 + // partition. + Auto DiskConfig = "AUTO" + + // Manual builds a server using whatever partition scheme and filesystem are + // present in the source image. If the target flavor disk is larger, the + // remaining space is left unpartitioned. This enables images to have non-EXT3 + // filesystems, multiple partitions, and so on, and enables you to manage the + // disk configuration. It also results in slightly shorter boot times. + Manual DiskConfig = "MANUAL" +) + // CreateOpts specifies server creation parameters. type CreateOpts struct { // Name is the name to assign to the newly launched server. @@ -335,6 +354,9 @@ type CreateOpts struct { // BlockDevice describes the mapping of various block devices. BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"` + + // DiskConfig [optional] controls how the created server's disk is partitioned. + DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` } // ToServerCreateMap assembles a request body based on the contents of a @@ -580,10 +602,20 @@ type RebuildOpts struct { // Personality [optional] includes files to inject into the server at launch. // Rebuild will base64-encode file contents for you. Personality Personality `json:"personality,omitempty"` + + // DiskConfig controls how the rebuilt server's disk is partitioned. + DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` } // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { + if opts.DiskConfig != "" && opts.DiskConfig != Auto && opts.DiskConfig != Manual { + err := gophercloud.ErrInvalidInput{} + err.Argument = "servers.RebuildOpts.DiskConfig" + err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" + return nil, err + } + b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -616,11 +648,21 @@ type ResizeOptsBuilder interface { type ResizeOpts struct { // FlavorRef is the ID of the flavor you wish your server to become. FlavorRef string `json:"flavorRef" required:"true"` + + // DiskConfig [optional] controls how the resized server's disk is partitioned. + DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` } // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON // request body for the Resize request. func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { + if opts.DiskConfig != "" && opts.DiskConfig != Auto && opts.DiskConfig != Manual { + err := gophercloud.ErrInvalidInput{} + err.Argument = "servers.ResizeOpts.DiskConfig" + err.Info = "Must be either diskconfig.Auto or diskconfig.Manual" + return nil, err + } + return gophercloud.BuildRequestBody(opts, "resize") } diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 1e57da9b49..009371014b 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -273,6 +273,9 @@ type Server struct { LaunchedAt time.Time `json:"-"` TerminatedAt time.Time `json:"-"` + + // DiskConfig is the disk configuration of the server. + DiskConfig DiskConfig `json:"OS-DCF:diskConfig"` } type AttachedVolume struct { diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index 7cd4743eec..caf0f0241c 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -556,6 +556,7 @@ var ( PowerState: servers.RUNNING, LaunchedAt: time.Date(2014, 9, 25, 13, 10, 10, 0, time.UTC), TerminatedAt: time.Time{}, + DiskConfig: servers.Manual, } derpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:41Z") @@ -627,6 +628,7 @@ var ( PowerState: servers.RUNNING, LaunchedAt: time.Date(2014, 9, 25, 13, 04, 49, 0, time.UTC), TerminatedAt: time.Time{}, + DiskConfig: servers.Manual, } ConsoleOutput = "abc" @@ -692,6 +694,7 @@ var ( PowerState: servers.RUNNING, LaunchedAt: time.Date(2014, 9, 25, 13, 04, 49, 0, time.UTC), TerminatedAt: time.Time{}, + DiskConfig: servers.Manual, } faultTimeCreated, _ = time.Parse(time.RFC3339, "2017-11-11T07:58:39Z") diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 5ca4047499..4860bdffe2 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/diskconfig" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -67,7 +66,6 @@ func TestListAllServersWithExtensions(t *testing.T) { type ServerWithExt struct { servers.Server availabilityzones.ServerAvailabilityZoneExt - diskconfig.ServerDiskConfigExt } allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages(context.TODO()) @@ -81,7 +79,7 @@ func TestListAllServersWithExtensions(t *testing.T) { th.AssertEquals(t, "RUNNING", actual[0].PowerState.String()) th.AssertEquals(t, "", actual[0].TaskState) th.AssertEquals(t, "active", actual[0].VmState) - th.AssertEquals(t, diskconfig.Manual, actual[0].DiskConfig) + th.AssertEquals(t, servers.Manual, actual[0].DiskConfig) } func TestCreateServer(t *testing.T) { @@ -218,6 +216,29 @@ func TestCreateServerWithHostname(t *testing.T) { th.CheckDeepEquals(t, ServerDerp, *actual) } +func TestCreateServerWithDiskConfig(t *testing.T) { + opts := servers.CreateOpts{ + Name: "createdserver", + ImageRef: "asdfasdfasdf", + FlavorRef: "performance1-1", + DiskConfig: servers.Manual, + } + expected := ` + { + "server": { + "name": "createdserver", + "imageRef": "asdfasdfasdf", + "flavorRef": "performance1-1", + "OS-DCF:diskConfig": "MANUAL" + } + } + ` + + actual, err := opts.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + func TestCreateServerWithBFVBootFromNewVolume(t *testing.T) { opts := servers.CreateOpts{ Name: "createdserver", @@ -670,7 +691,6 @@ func TestGetServerWithExtensions(t *testing.T) { var s struct { servers.Server availabilityzones.ServerAvailabilityZoneExt - diskconfig.ServerDiskConfigExt } err := servers.Get(context.TODO(), client.ServiceClient(), "1234asdf").ExtractInto(&s) @@ -679,7 +699,7 @@ func TestGetServerWithExtensions(t *testing.T) { th.AssertEquals(t, "RUNNING", s.PowerState.String()) th.AssertEquals(t, "", s.TaskState) th.AssertEquals(t, "active", s.VmState) - th.AssertEquals(t, diskconfig.Manual, s.DiskConfig) + th.AssertEquals(t, servers.Manual, s.DiskConfig) err = servers.Get(context.TODO(), client.ServiceClient(), "1234asdf").ExtractInto(s) if err == nil { @@ -762,6 +782,29 @@ func TestRebuildServer(t *testing.T) { th.CheckDeepEquals(t, ServerDerp, *actual) } +func TestRebuildServerWithDiskConfig(t *testing.T) { + opts := servers.RebuildOpts{ + Name: "rebuiltserver", + AdminPass: "swordfish", + ImageRef: "asdfasdfasdf", + DiskConfig: servers.Auto, + } + expected := ` + { + "rebuild": { + "name": "rebuiltserver", + "imageRef": "asdfasdfasdf", + "adminPass": "swordfish", + "OS-DCF:diskConfig": "AUTO" + } + } + ` + + actual, err := opts.ToServerRebuildMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + func TestResizeServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -778,6 +821,25 @@ func TestResizeServer(t *testing.T) { th.AssertNoErr(t, res.Err) } +func TestResizeServerWithDiskConfig(t *testing.T) { + opts := servers.ResizeOpts{ + FlavorRef: "performance1-8", + DiskConfig: servers.Auto, + } + expected := ` + { + "resize": { + "flavorRef": "performance1-8", + "OS-DCF:diskConfig": "AUTO" + } + } + ` + + actual, err := opts.ToServerResizeMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + func TestConfirmResize(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From c9aebb283404409cbed728613574aeb96976621f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 26 Feb 2024 18:27:55 +0000 Subject: [PATCH 1814/2296] compute: Merge availabilityzones extension This one is partially standalone, partially an extension of servers. Signed-off-by: Stephen Finucane --- .../compute/v2/availabilityzones_test.go | 2 +- .../openstack/compute/v2/servers_test.go | 22 +++++++----------- .../{extensions => }/availabilityzones/doc.go | 23 ------------------- .../availabilityzones/requests.go | 0 .../availabilityzones/results.go | 6 ----- .../availabilityzones/testing/doc.go | 0 .../testing/fixtures_test.go | 2 +- .../testing/requests_test.go | 2 +- .../availabilityzones/urls.go | 0 openstack/compute/v2/servers/results.go | 3 +++ .../v2/servers/testing/fixtures_test.go | 3 +++ .../v2/servers/testing/requests_test.go | 14 ++++------- 12 files changed, 21 insertions(+), 56 deletions(-) rename openstack/compute/v2/{extensions => }/availabilityzones/doc.go (64%) rename openstack/compute/v2/{extensions => }/availabilityzones/requests.go (100%) rename openstack/compute/v2/{extensions => }/availabilityzones/results.go (88%) rename openstack/compute/v2/{extensions => }/availabilityzones/testing/doc.go (100%) rename openstack/compute/v2/{extensions => }/availabilityzones/testing/fixtures_test.go (99%) rename openstack/compute/v2/{extensions => }/availabilityzones/testing/requests_test.go (97%) rename openstack/compute/v2/{extensions => }/availabilityzones/urls.go (100%) diff --git a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go index 54e82168ed..7c27c0d664 100644 --- a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 8eaccd329d..a2d323067c 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -13,7 +13,6 @@ import ( networks "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/suspendresume" @@ -87,11 +86,6 @@ func TestServersCreateDestroy(t *testing.T) { func TestServersWithExtensionsCreateDestroy(t *testing.T) { clients.RequireLong(t) - var extendedServer struct { - servers.Server - availabilityzones.ServerAvailabilityZoneExt - } - client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) @@ -99,16 +93,16 @@ func TestServersWithExtensionsCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) defer DeleteServer(t, client, server) - err = servers.Get(context.TODO(), client, server.ID).ExtractInto(&extendedServer) + created, err := servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) - tools.PrintResource(t, extendedServer) + tools.PrintResource(t, created) - th.AssertEquals(t, extendedServer.AvailabilityZone, "nova") - th.AssertEquals(t, int(extendedServer.PowerState), servers.RUNNING) - th.AssertEquals(t, extendedServer.TaskState, "") - th.AssertEquals(t, extendedServer.VmState, "active") - th.AssertEquals(t, extendedServer.LaunchedAt.IsZero(), false) - th.AssertEquals(t, extendedServer.TerminatedAt.IsZero(), true) + th.AssertEquals(t, created.AvailabilityZone, "nova") + th.AssertEquals(t, int(created.PowerState), servers.RUNNING) + th.AssertEquals(t, created.TaskState, "") + th.AssertEquals(t, created.VmState, "active") + th.AssertEquals(t, created.LaunchedAt.IsZero(), false) + th.AssertEquals(t, created.TerminatedAt.IsZero(), true) } func TestServersWithoutImageRef(t *testing.T) { diff --git a/openstack/compute/v2/extensions/availabilityzones/doc.go b/openstack/compute/v2/availabilityzones/doc.go similarity index 64% rename from openstack/compute/v2/extensions/availabilityzones/doc.go rename to openstack/compute/v2/availabilityzones/doc.go index 0d81889a2b..59278fda32 100644 --- a/openstack/compute/v2/extensions/availabilityzones/doc.go +++ b/openstack/compute/v2/availabilityzones/doc.go @@ -3,29 +3,6 @@ Package availabilityzones provides the ability to get lists and detailed availability zone information and to extend a server result with availability zone information. -Example of Extend server result with Availability Zone Information: - - type ServerWithAZ struct { - servers.Server - availabilityzones.ServerAvailabilityZoneExt - } - - var allServers []ServerWithAZ - - allPages, err := servers.List(client, nil).AllPages(context.TODO()) - if err != nil { - panic("Unable to retrieve servers: %s", err) - } - - err = servers.ExtractServersInto(allPages, &allServers) - if err != nil { - panic("Unable to extract servers: %s", err) - } - - for _, server := range allServers { - fmt.Println(server.AvailabilityZone) - } - Example of Get Availability Zone Information allPages, err := availabilityzones.List(computeClient).AllPages(context.TODO()) diff --git a/openstack/compute/v2/extensions/availabilityzones/requests.go b/openstack/compute/v2/availabilityzones/requests.go similarity index 100% rename from openstack/compute/v2/extensions/availabilityzones/requests.go rename to openstack/compute/v2/availabilityzones/requests.go diff --git a/openstack/compute/v2/extensions/availabilityzones/results.go b/openstack/compute/v2/availabilityzones/results.go similarity index 88% rename from openstack/compute/v2/extensions/availabilityzones/results.go rename to openstack/compute/v2/availabilityzones/results.go index ce7f039b77..3c95a8a24b 100644 --- a/openstack/compute/v2/extensions/availabilityzones/results.go +++ b/openstack/compute/v2/availabilityzones/results.go @@ -8,12 +8,6 @@ import ( "github.com/gophercloud/gophercloud/v2/pagination" ) -// ServerAvailabilityZoneExt is an extension to the base Server object. -type ServerAvailabilityZoneExt struct { - // AvailabilityZone is the availabilty zone the server is in. - AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` -} - // ServiceState represents the state of a service in an AvailabilityZone. type ServiceState struct { Active bool `json:"active"` diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/doc.go b/openstack/compute/v2/availabilityzones/testing/doc.go similarity index 100% rename from openstack/compute/v2/extensions/availabilityzones/testing/doc.go rename to openstack/compute/v2/availabilityzones/testing/doc.go diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go b/openstack/compute/v2/availabilityzones/testing/fixtures_test.go similarity index 99% rename from openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go rename to openstack/compute/v2/availabilityzones/testing/fixtures_test.go index 95c90aa5d6..059364bcc0 100644 --- a/openstack/compute/v2/extensions/availabilityzones/testing/fixtures_test.go +++ b/openstack/compute/v2/availabilityzones/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - az "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" + az "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go b/openstack/compute/v2/availabilityzones/testing/requests_test.go similarity index 97% rename from openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go rename to openstack/compute/v2/availabilityzones/testing/requests_test.go index ff441f7bb5..f537d9d18b 100644 --- a/openstack/compute/v2/extensions/availabilityzones/testing/requests_test.go +++ b/openstack/compute/v2/availabilityzones/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - az "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" + az "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/availabilityzones" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/compute/v2/extensions/availabilityzones/urls.go b/openstack/compute/v2/availabilityzones/urls.go similarity index 100% rename from openstack/compute/v2/extensions/availabilityzones/urls.go rename to openstack/compute/v2/availabilityzones/urls.go diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 009371014b..7793940c9a 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -276,6 +276,9 @@ type Server struct { // DiskConfig is the disk configuration of the server. DiskConfig DiskConfig `json:"OS-DCF:diskConfig"` + + // AvailabilityZone is the availabilty zone the server is in. + AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` } type AttachedVolume struct { diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index caf0f0241c..ac43be8047 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -557,6 +557,7 @@ var ( LaunchedAt: time.Date(2014, 9, 25, 13, 10, 10, 0, time.UTC), TerminatedAt: time.Time{}, DiskConfig: servers.Manual, + AvailabilityZone: "nova", } derpTimeCreated, _ = time.Parse(time.RFC3339, "2014-09-25T13:04:41Z") @@ -629,6 +630,7 @@ var ( LaunchedAt: time.Date(2014, 9, 25, 13, 04, 49, 0, time.UTC), TerminatedAt: time.Time{}, DiskConfig: servers.Manual, + AvailabilityZone: "nova", } ConsoleOutput = "abc" @@ -695,6 +697,7 @@ var ( LaunchedAt: time.Date(2014, 9, 25, 13, 04, 49, 0, time.UTC), TerminatedAt: time.Time{}, DiskConfig: servers.Manual, + AvailabilityZone: "nova", } faultTimeCreated, _ = time.Parse(time.RFC3339, "2017-11-11T07:58:39Z") diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 4860bdffe2..f64cee4c54 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -7,7 +7,6 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/availabilityzones" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -63,15 +62,10 @@ func TestListAllServersWithExtensions(t *testing.T) { defer th.TeardownHTTP() HandleServerListSuccessfully(t) - type ServerWithExt struct { - servers.Server - availabilityzones.ServerAvailabilityZoneExt - } - allPages, err := servers.List(client.ServiceClient(), servers.ListOpts{}).AllPages(context.TODO()) th.AssertNoErr(t, err) - var actual []ServerWithExt + var actual []servers.Server err = servers.ExtractServersInto(allPages, &actual) th.AssertNoErr(t, err) th.AssertEquals(t, 3, len(actual)) @@ -690,10 +684,10 @@ func TestGetServerWithExtensions(t *testing.T) { var s struct { servers.Server - availabilityzones.ServerAvailabilityZoneExt } - err := servers.Get(context.TODO(), client.ServiceClient(), "1234asdf").ExtractInto(&s) + client := client.ServiceClient() + err := servers.Get(context.TODO(), client, "1234asdf").ExtractInto(&s) th.AssertNoErr(t, err) th.AssertEquals(t, "nova", s.AvailabilityZone) th.AssertEquals(t, "RUNNING", s.PowerState.String()) @@ -701,7 +695,7 @@ func TestGetServerWithExtensions(t *testing.T) { th.AssertEquals(t, "active", s.VmState) th.AssertEquals(t, servers.Manual, s.DiskConfig) - err = servers.Get(context.TODO(), client.ServiceClient(), "1234asdf").ExtractInto(s) + err = servers.Get(context.TODO(), client, "1234asdf").ExtractInto(s) if err == nil { t.Errorf("Expected error when providing non-pointer struct") } From d780949e833ebc03a9d65378c185d940d680e27d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 26 Feb 2024 18:51:32 +0000 Subject: [PATCH 1815/2296] compute: Merge '/action' extensions These are all done at once since they are (a) very simple and (b) pretty much all identical. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/compute.go | 5 +- .../openstack/compute/v2/migrate_test.go | 8 +- .../openstack/compute/v2/servers_test.go | 15 +- .../compute/v2/extensions/evacuate/doc.go | 13 - .../v2/extensions/evacuate/requests.go | 45 --- .../compute/v2/extensions/evacuate/results.go | 23 -- .../v2/extensions/evacuate/testing/doc.go | 2 - .../evacuate/testing/fixtures_test.go | 83 ------ .../evacuate/testing/requests_test.go | 61 ---- .../v2/extensions/injectnetworkinfo/doc.go | 14 - .../extensions/injectnetworkinfo/requests.go | 18 -- .../extensions/injectnetworkinfo/results.go | 11 - .../testing/fixtures_test.go | 18 -- .../testing/requests_test.go | 22 -- .../compute/v2/extensions/lockunlock/doc.go | 19 -- .../v2/extensions/lockunlock/requests.go | 22 -- .../v2/extensions/lockunlock/results.go | 16 - .../v2/extensions/lockunlock/testing/doc.go | 2 - .../lockunlock/testing/fixtures_test.go | 27 -- .../lockunlock/testing/request_test.go | 32 -- .../compute/v2/extensions/migrate/doc.go | 29 -- .../compute/v2/extensions/migrate/requests.go | 55 ---- .../compute/v2/extensions/migrate/results.go | 11 - .../v2/extensions/migrate/testing/doc.go | 2 - .../migrate/testing/fixtures_test.go | 33 --- .../migrate/testing/requests_test.go | 42 --- .../compute/v2/extensions/pauseunpause/doc.go | 18 -- .../v2/extensions/pauseunpause/requests.go | 22 -- .../v2/extensions/pauseunpause/results.go | 15 - .../v2/extensions/pauseunpause/testing/doc.go | 2 - .../pauseunpause/testing/fixtures_test.go | 27 -- .../pauseunpause/testing/requests_test.go | 32 -- .../v2/extensions/rescueunrescue/doc.go | 28 -- .../v2/extensions/rescueunrescue/requests.go | 55 ---- .../v2/extensions/rescueunrescue/results.go | 28 -- .../extensions/rescueunrescue/testing/doc.go | 1 - .../rescueunrescue/testing/fixtures_test.go | 25 -- .../rescueunrescue/testing/requests_test.go | 50 ---- .../compute/v2/extensions/resetnetwork/doc.go | 14 - .../v2/extensions/resetnetwork/requests.go | 18 -- .../v2/extensions/resetnetwork/results.go | 11 - .../resetnetwork/testing/fixtures_test.go | 18 -- .../resetnetwork/testing/requests_test.go | 22 -- .../compute/v2/extensions/resetstate/doc.go | 13 - .../v2/extensions/resetstate/requests.go | 27 -- .../v2/extensions/resetstate/results.go | 11 - .../v2/extensions/resetstate/testing/doc.go | 1 - .../resetstate/testing/fixtures_test.go | 19 -- .../resetstate/testing/requests_test.go | 22 -- .../v2/extensions/shelveunshelve/doc.go | 24 -- .../v2/extensions/shelveunshelve/requests.go | 62 ---- .../v2/extensions/shelveunshelve/results.go | 21 -- .../shelveunshelve/testing/fixtures_test.go | 50 ---- .../shelveunshelve/testing/requests_test.go | 59 ---- .../compute/v2/extensions/startstop/doc.go | 19 -- .../v2/extensions/startstop/requests.go | 22 -- .../v2/extensions/startstop/results.go | 15 - .../v2/extensions/startstop/testing/doc.go | 2 - .../startstop/testing/fixtures_test.go | 27 -- .../startstop/testing/requests_test.go | 32 -- .../v2/extensions/suspendresume/doc.go | 19 -- .../v2/extensions/suspendresume/requests.go | 22 -- .../v2/extensions/suspendresume/results.go | 15 - .../extensions/suspendresume/testing/doc.go | 2 - .../suspendresume/testing/fixtures_test.go | 27 -- .../suspendresume/testing/requests_test.go | 32 -- openstack/compute/v2/servers/requests.go | 280 ++++++++++++++++++ openstack/compute/v2/servers/results.go | 132 +++++++++ 68 files changed, 424 insertions(+), 1545 deletions(-) delete mode 100644 openstack/compute/v2/extensions/evacuate/doc.go delete mode 100644 openstack/compute/v2/extensions/evacuate/requests.go delete mode 100644 openstack/compute/v2/extensions/evacuate/results.go delete mode 100644 openstack/compute/v2/extensions/evacuate/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/evacuate/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/doc.go delete mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/requests.go delete mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/results.go delete mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/lockunlock/doc.go delete mode 100644 openstack/compute/v2/extensions/lockunlock/requests.go delete mode 100644 openstack/compute/v2/extensions/lockunlock/results.go delete mode 100644 openstack/compute/v2/extensions/lockunlock/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/lockunlock/testing/request_test.go delete mode 100644 openstack/compute/v2/extensions/migrate/doc.go delete mode 100644 openstack/compute/v2/extensions/migrate/requests.go delete mode 100644 openstack/compute/v2/extensions/migrate/results.go delete mode 100644 openstack/compute/v2/extensions/migrate/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/migrate/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/migrate/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/pauseunpause/doc.go delete mode 100644 openstack/compute/v2/extensions/pauseunpause/requests.go delete mode 100644 openstack/compute/v2/extensions/pauseunpause/results.go delete mode 100644 openstack/compute/v2/extensions/pauseunpause/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/rescueunrescue/doc.go delete mode 100644 openstack/compute/v2/extensions/rescueunrescue/requests.go delete mode 100644 openstack/compute/v2/extensions/rescueunrescue/results.go delete mode 100644 openstack/compute/v2/extensions/rescueunrescue/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/rescueunrescue/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/resetnetwork/doc.go delete mode 100644 openstack/compute/v2/extensions/resetnetwork/requests.go delete mode 100644 openstack/compute/v2/extensions/resetnetwork/results.go delete mode 100644 openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/resetstate/doc.go delete mode 100644 openstack/compute/v2/extensions/resetstate/requests.go delete mode 100644 openstack/compute/v2/extensions/resetstate/results.go delete mode 100644 openstack/compute/v2/extensions/resetstate/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/resetstate/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/shelveunshelve/doc.go delete mode 100644 openstack/compute/v2/extensions/shelveunshelve/requests.go delete mode 100644 openstack/compute/v2/extensions/shelveunshelve/results.go delete mode 100644 openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/startstop/doc.go delete mode 100644 openstack/compute/v2/extensions/startstop/requests.go delete mode 100644 openstack/compute/v2/extensions/startstop/results.go delete mode 100644 openstack/compute/v2/extensions/startstop/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/startstop/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/startstop/testing/requests_test.go delete mode 100644 openstack/compute/v2/extensions/suspendresume/doc.go delete mode 100644 openstack/compute/v2/extensions/suspendresume/requests.go delete mode 100644 openstack/compute/v2/extensions/suspendresume/results.go delete mode 100644 openstack/compute/v2/extensions/suspendresume/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/extensions/suspendresume/testing/requests_test.go diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index ef0d7a0a40..b8e553a177 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -17,7 +17,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/rescueunrescue" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/schedulerhints" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" @@ -1169,7 +1168,7 @@ func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOp // RescueServer will place the specified server into rescue mode. func RescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { t.Logf("Attempting to put server %s into rescue mode", server.ID) - _, err := rescueunrescue.Rescue(context.TODO(), client, server.ID, rescueunrescue.RescueOpts{}).Extract() + _, err := servers.Rescue(context.TODO(), client, server.ID, servers.RescueOpts{}).Extract() if err != nil { return err } @@ -1184,7 +1183,7 @@ func RescueServer(t *testing.T, client *gophercloud.ServiceClient, server *serve // UnrescueServer will return server from rescue mode. func UnrescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error { t.Logf("Attempting to return server %s from rescue mode", server.ID) - if err := rescueunrescue.Unrescue(context.TODO(), client, server.ID).ExtractErr(); err != nil { + if err := servers.Unrescue(context.TODO(), client, server.ID).ExtractErr(); err != nil { return err } diff --git a/internal/acceptance/openstack/compute/v2/migrate_test.go b/internal/acceptance/openstack/compute/v2/migrate_test.go index 0e320739d1..cf9056b156 100644 --- a/internal/acceptance/openstack/compute/v2/migrate_test.go +++ b/internal/acceptance/openstack/compute/v2/migrate_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/migrate" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -25,7 +25,7 @@ func TestMigrate(t *testing.T) { t.Logf("Attempting to migrate server %s", server.ID) - err = migrate.Migrate(context.TODO(), client, server.ID).ExtractErr() + err = servers.Migrate(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) } @@ -46,11 +46,11 @@ func TestLiveMigrate(t *testing.T) { blockMigration := false diskOverCommit := false - liveMigrateOpts := migrate.LiveMigrateOpts{ + liveMigrateOpts := servers.LiveMigrateOpts{ BlockMigration: &blockMigration, DiskOverCommit: &diskOverCommit, } - err = migrate.LiveMigrate(context.TODO(), client, server.ID, liveMigrateOpts).ExtractErr() + err = servers.LiveMigrate(context.TODO(), client, server.ID, liveMigrateOpts).ExtractErr() th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index a2d323067c..4c6e6b3462 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -13,9 +13,6 @@ import ( networks "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/suspendresume" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tags" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -394,13 +391,13 @@ func TestServersActionPause(t *testing.T) { defer DeleteServer(t, client, server) t.Logf("Attempting to pause server %s", server.ID) - err = pauseunpause.Pause(context.TODO(), client, server.ID).ExtractErr() + err = servers.Pause(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "PAUSED") th.AssertNoErr(t, err) - err = pauseunpause.Unpause(context.TODO(), client, server.ID).ExtractErr() + err = servers.Unpause(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") @@ -418,13 +415,13 @@ func TestServersActionSuspend(t *testing.T) { defer DeleteServer(t, client, server) t.Logf("Attempting to suspend server %s", server.ID) - err = suspendresume.Suspend(context.TODO(), client, server.ID).ExtractErr() + err = servers.Suspend(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "SUSPENDED") th.AssertNoErr(t, err) - err = suspendresume.Resume(context.TODO(), client, server.ID).ExtractErr() + err = servers.Resume(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") @@ -443,7 +440,7 @@ func TestServersActionLock(t *testing.T) { defer DeleteServer(t, client, server) t.Logf("Attempting to Lock server %s", server.ID) - err = lockunlock.Lock(context.TODO(), client, server.ID).ExtractErr() + err = servers.Lock(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) t.Logf("Attempting to delete locked server %s", server.ID) @@ -451,7 +448,7 @@ func TestServersActionLock(t *testing.T) { th.AssertEquals(t, err != nil, true) t.Logf("Attempting to unlock server %s", server.ID) - err = lockunlock.Unlock(context.TODO(), client, server.ID).ExtractErr() + err = servers.Unlock(context.TODO(), client, server.ID).ExtractErr() th.AssertNoErr(t, err) err = WaitForComputeStatus(client, server, "ACTIVE") diff --git a/openstack/compute/v2/extensions/evacuate/doc.go b/openstack/compute/v2/extensions/evacuate/doc.go deleted file mode 100644 index 51a8296fff..0000000000 --- a/openstack/compute/v2/extensions/evacuate/doc.go +++ /dev/null @@ -1,13 +0,0 @@ -/* -Package evacuate provides functionality to evacuates servers that have been -provisioned by the OpenStack Compute service from a failed host to a new host. - -Example to Evacuate a Server from a Host - - serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" - err := evacuate.Evacuate(context.TODO(), computeClient, serverID, evacuate.EvacuateOpts{}).ExtractErr() - if err != nil { - panic(err) - } -*/ -package evacuate diff --git a/openstack/compute/v2/extensions/evacuate/requests.go b/openstack/compute/v2/extensions/evacuate/requests.go deleted file mode 100644 index 52b650c864..0000000000 --- a/openstack/compute/v2/extensions/evacuate/requests.go +++ /dev/null @@ -1,45 +0,0 @@ -package evacuate - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// EvacuateOptsBuilder allows extensions to add additional parameters to the -// the Evacuate request. -type EvacuateOptsBuilder interface { - ToEvacuateMap() (map[string]interface{}, error) -} - -// EvacuateOpts specifies Evacuate action parameters. -type EvacuateOpts struct { - // The name of the host to which the server is evacuated - Host string `json:"host,omitempty"` - - // Indicates whether server is on shared storage - OnSharedStorage bool `json:"onSharedStorage"` - - // An administrative password to access the evacuated server - AdminPass string `json:"adminPass,omitempty"` -} - -// ToServerGroupCreateMap constructs a request body from CreateOpts. -func (opts EvacuateOpts) ToEvacuateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "evacuate") -} - -// Evacuate will Evacuate a failed instance to another host. -func Evacuate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts EvacuateOptsBuilder) (r EvacuateResult) { - b, err := opts.ToEvacuateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/evacuate/results.go b/openstack/compute/v2/extensions/evacuate/results.go deleted file mode 100644 index 803d9d4772..0000000000 --- a/openstack/compute/v2/extensions/evacuate/results.go +++ /dev/null @@ -1,23 +0,0 @@ -package evacuate - -import ( - "github.com/gophercloud/gophercloud/v2" -) - -// EvacuateResult is the response from an Evacuate operation. -// Call its ExtractAdminPass method to retrieve the admin password of the instance. -// The admin password will be an empty string if the cloud is not configured to inject admin passwords.. -type EvacuateResult struct { - gophercloud.Result -} - -func (r EvacuateResult) ExtractAdminPass() (string, error) { - var s struct { - AdminPass string `json:"adminPass"` - } - err := r.ExtractInto(&s) - if err != nil && err.Error() == "EOF" { - return "", nil - } - return s.AdminPass, err -} diff --git a/openstack/compute/v2/extensions/evacuate/testing/doc.go b/openstack/compute/v2/extensions/evacuate/testing/doc.go deleted file mode 100644 index 613ac1d4b6..0000000000 --- a/openstack/compute/v2/extensions/evacuate/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// compute_extensions_evacuate_v2 -package testing diff --git a/openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go b/openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go deleted file mode 100644 index c306643ce0..0000000000 --- a/openstack/compute/v2/extensions/evacuate/testing/fixtures_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockEvacuateResponse(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, ` - { - "evacuate": { - "adminPass": "MySecretPass", - "host": "derp", - "onSharedStorage": false - } - - } - `) - w.WriteHeader(http.StatusOK) - }) -} - -func mockEvacuateResponseWithHost(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, ` - { - "evacuate": { - "host": "derp", - "onSharedStorage": false - } - - } - `) - w.WriteHeader(http.StatusOK) - }) -} - -func mockEvacuateResponseWithNoOpts(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, ` - { - "evacuate": { - "onSharedStorage": false - } - - } - `) - w.WriteHeader(http.StatusOK) - }) -} - -const EvacuateResponse = ` -{ - "adminPass": "MySecretPass" -} -` - -func mockEvacuateAdminpassResponse(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, ` - { - "evacuate": { - "onSharedStorage": false - } - } - `) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, EvacuateResponse) - }) -} diff --git a/openstack/compute/v2/extensions/evacuate/testing/requests_test.go b/openstack/compute/v2/extensions/evacuate/testing/requests_test.go deleted file mode 100644 index 6a066fc7a7..0000000000 --- a/openstack/compute/v2/extensions/evacuate/testing/requests_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/evacuate" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestEvacuate(t *testing.T) { - const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" - th.SetupHTTP() - defer th.TeardownHTTP() - - mockEvacuateResponse(t, serverID) - - _, err := evacuate.Evacuate(context.TODO(), client.ServiceClient(), serverID, evacuate.EvacuateOpts{ - Host: "derp", - AdminPass: "MySecretPass", - OnSharedStorage: false, - }).ExtractAdminPass() - th.AssertNoErr(t, err) -} - -func TestEvacuateWithHost(t *testing.T) { - const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" - th.SetupHTTP() - defer th.TeardownHTTP() - - mockEvacuateResponseWithHost(t, serverID) - - _, err := evacuate.Evacuate(context.TODO(), client.ServiceClient(), serverID, evacuate.EvacuateOpts{ - Host: "derp", - }).ExtractAdminPass() - th.AssertNoErr(t, err) -} - -func TestEvacuateWithNoOpts(t *testing.T) { - const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" - th.SetupHTTP() - defer th.TeardownHTTP() - - mockEvacuateResponseWithNoOpts(t, serverID) - - _, err := evacuate.Evacuate(context.TODO(), client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() - th.AssertNoErr(t, err) -} - -func TestEvacuateAdminpassResponse(t *testing.T) { - const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" - th.SetupHTTP() - defer th.TeardownHTTP() - - mockEvacuateAdminpassResponse(t, serverID) - - actual, err := evacuate.Evacuate(context.TODO(), client.ServiceClient(), serverID, evacuate.EvacuateOpts{}).ExtractAdminPass() - th.CheckEquals(t, "MySecretPass", actual) - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/doc.go b/openstack/compute/v2/extensions/injectnetworkinfo/doc.go deleted file mode 100644 index 3355110ed4..0000000000 --- a/openstack/compute/v2/extensions/injectnetworkinfo/doc.go +++ /dev/null @@ -1,14 +0,0 @@ -/* -Package injectnetworkinfo provides functionality to inject the network info into -a server that has been provisioned by the OpenStack Compute service. This action -requires admin privileges and Nova configured with a Xen hypervisor driver. - -Example to Inject a Network Info into a Server - - serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := injectnetworkinfo.InjectNetworkInfo(context.TODO(), client, id).ExtractErr() - if err != nil { - panic(err) - } -*/ -package injectnetworkinfo diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/requests.go b/openstack/compute/v2/extensions/injectnetworkinfo/requests.go deleted file mode 100644 index 91d51be8fc..0000000000 --- a/openstack/compute/v2/extensions/injectnetworkinfo/requests.go +++ /dev/null @@ -1,18 +0,0 @@ -package injectnetworkinfo - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// InjectNetworkInfo will inject the network info into a server -func InjectNetworkInfo(ctx context.Context, client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { - b := map[string]interface{}{ - "injectNetworkInfo": nil, - } - resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/results.go b/openstack/compute/v2/extensions/injectnetworkinfo/results.go deleted file mode 100644 index 339fe2ab7b..0000000000 --- a/openstack/compute/v2/extensions/injectnetworkinfo/results.go +++ /dev/null @@ -1,11 +0,0 @@ -package injectnetworkinfo - -import ( - "github.com/gophercloud/gophercloud/v2" -) - -// InjectNetworkResult is the response of a InjectNetworkInfo operation. Call -// its ExtractErr method to determine if the request suceeded or failed. -type InjectNetworkResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go b/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go deleted file mode 100644 index 13a9148396..0000000000 --- a/openstack/compute/v2/extensions/injectnetworkinfo/testing/fixtures_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockInjectNetworkInfoResponse(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, `{"injectNetworkInfo": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go b/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go deleted file mode 100644 index 819f2aa0ec..0000000000 --- a/openstack/compute/v2/extensions/injectnetworkinfo/testing/requests_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/injectnetworkinfo" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" - -func TestInjectNetworkInfo(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockInjectNetworkInfoResponse(t, serverID) - - err := injectnetworkinfo.InjectNetworkInfo(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/lockunlock/doc.go b/openstack/compute/v2/extensions/lockunlock/doc.go deleted file mode 100644 index b6427bfd0b..0000000000 --- a/openstack/compute/v2/extensions/lockunlock/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Package lockunlock provides functionality to lock and unlock servers that -have been provisioned by the OpenStack Compute service. - -Example to Lock and Unlock a Server - - serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - - err := lockunlock.Lock(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - - err = lockunlock.Unlock(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package lockunlock diff --git a/openstack/compute/v2/extensions/lockunlock/requests.go b/openstack/compute/v2/extensions/lockunlock/requests.go deleted file mode 100644 index dd88824303..0000000000 --- a/openstack/compute/v2/extensions/lockunlock/requests.go +++ /dev/null @@ -1,22 +0,0 @@ -package lockunlock - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// Lock is the operation responsible for locking a Compute server. -func Lock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r LockResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Unlock is the operation responsible for unlocking a Compute server. -func Unlock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnlockResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/lockunlock/results.go b/openstack/compute/v2/extensions/lockunlock/results.go deleted file mode 100644 index 3f4abc77d0..0000000000 --- a/openstack/compute/v2/extensions/lockunlock/results.go +++ /dev/null @@ -1,16 +0,0 @@ -package lockunlock - -import ( - "github.com/gophercloud/gophercloud/v2" -) - -// LockResult and UnlockResult are the responses from a Lock and Unlock -// operations respectively. Call their ExtractErr methods to determine if the -// requests suceeded or failed. -type LockResult struct { - gophercloud.ErrResult -} - -type UnlockResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/lockunlock/testing/doc.go b/openstack/compute/v2/extensions/lockunlock/testing/doc.go deleted file mode 100644 index 59cb9be471..0000000000 --- a/openstack/compute/v2/extensions/lockunlock/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// unlocklock unit tests -package testing diff --git a/openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go b/openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go deleted file mode 100644 index db713c4963..0000000000 --- a/openstack/compute/v2/extensions/lockunlock/testing/fixtures_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockStartServerResponse(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, `{"lock": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockStopServerResponse(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, `{"unlock": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/extensions/lockunlock/testing/request_test.go b/openstack/compute/v2/extensions/lockunlock/testing/request_test.go deleted file mode 100644 index 7d1a982d8a..0000000000 --- a/openstack/compute/v2/extensions/lockunlock/testing/request_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/lockunlock" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "{serverId}" - -func TestLock(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockStartServerResponse(t, serverID) - - err := lockunlock.Lock(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestUnlock(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockStopServerResponse(t, serverID) - - err := lockunlock.Unlock(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/migrate/doc.go b/openstack/compute/v2/extensions/migrate/doc.go deleted file mode 100644 index 2bc569b18d..0000000000 --- a/openstack/compute/v2/extensions/migrate/doc.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Package migrate provides functionality to migrate servers that have been -provisioned by the OpenStack Compute service. - -Example of Migrate Server (migrate Action) - - serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" - err := migrate.Migrate(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - -Example of Live-Migrate Server (os-migrateLive Action) - - serverID := "b16ba811-199d-4ffd-8839-ba96c1185a67" - host := "01c0cadef72d47e28a672a76060d492c" - blockMigration := false - - migrationOpts := migrate.LiveMigrateOpts{ - Host: &host, - BlockMigration: &blockMigration, - } - - err := migrate.LiveMigrate(context.TODO(), computeClient, serverID, migrationOpts).ExtractErr() - if err != nil { - panic(err) - } -*/ -package migrate diff --git a/openstack/compute/v2/extensions/migrate/requests.go b/openstack/compute/v2/extensions/migrate/requests.go deleted file mode 100644 index d28bd179c4..0000000000 --- a/openstack/compute/v2/extensions/migrate/requests.go +++ /dev/null @@ -1,55 +0,0 @@ -package migrate - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// Migrate will initiate a migration of the instance to another host. -func Migrate(ctx context.Context, client *gophercloud.ServiceClient, id string) (r MigrateResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// LiveMigrateOptsBuilder allows extensions to add additional parameters to the -// LiveMigrate request. -type LiveMigrateOptsBuilder interface { - ToLiveMigrateMap() (map[string]interface{}, error) -} - -// LiveMigrateOpts specifies parameters of live migrate action. -type LiveMigrateOpts struct { - // The host to which to migrate the server. - // If this parameter is None, the scheduler chooses a host. - Host *string `json:"host"` - - // Set to True to migrate local disks by using block migration. - // If the source or destination host uses shared storage and you set - // this value to True, the live migration fails. - BlockMigration *bool `json:"block_migration,omitempty"` - - // Set to True to enable over commit when the destination host is checked - // for available disk space. Set to False to disable over commit. This setting - // affects only the libvirt virt driver. - DiskOverCommit *bool `json:"disk_over_commit,omitempty"` -} - -// ToLiveMigrateMap constructs a request body from LiveMigrateOpts. -func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "os-migrateLive") -} - -// LiveMigrate will initiate a live-migration (without rebooting) of the instance to another host. -func LiveMigrate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts LiveMigrateOptsBuilder) (r MigrateResult) { - b, err := opts.ToLiveMigrateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/migrate/results.go b/openstack/compute/v2/extensions/migrate/results.go deleted file mode 100644 index 3648fc1f48..0000000000 --- a/openstack/compute/v2/extensions/migrate/results.go +++ /dev/null @@ -1,11 +0,0 @@ -package migrate - -import ( - "github.com/gophercloud/gophercloud/v2" -) - -// MigrateResult is the response from a Migrate operation. Call its ExtractErr -// method to determine if the request suceeded or failed. -type MigrateResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/migrate/testing/doc.go b/openstack/compute/v2/extensions/migrate/testing/doc.go deleted file mode 100644 index 6135475739..0000000000 --- a/openstack/compute/v2/extensions/migrate/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// compute_extensions_startstop_v2 -package testing diff --git a/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go b/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go deleted file mode 100644 index cbdacedac6..0000000000 --- a/openstack/compute/v2/extensions/migrate/testing/fixtures_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockMigrateResponse(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, `{"migrate": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockLiveMigrateResponse(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": false, - "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 deleted file mode 100644 index c57687f001..0000000000 --- a/openstack/compute/v2/extensions/migrate/testing/requests_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/migrate" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" - -func TestMigrate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockMigrateResponse(t, serverID) - - err := migrate.Migrate(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestLiveMigrate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockLiveMigrateResponse(t, serverID) - - host := "01c0cadef72d47e28a672a76060d492c" - blockMigration := false - diskOverCommit := true - - migrationOpts := migrate.LiveMigrateOpts{ - Host: &host, - BlockMigration: &blockMigration, - DiskOverCommit: &diskOverCommit, - } - - err := migrate.LiveMigrate(context.TODO(), client.ServiceClient(), serverID, migrationOpts).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/pauseunpause/doc.go b/openstack/compute/v2/extensions/pauseunpause/doc.go deleted file mode 100644 index 35cfcce197..0000000000 --- a/openstack/compute/v2/extensions/pauseunpause/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Package pauseunpause provides functionality to pause and unpause servers that -have been provisioned by the OpenStack Compute service. - -Example to Pause and Unpause a Server - - serverID := "32c8baf7-1cdb-4cc2-bc31-c3a55b89f56b" - err := pauseunpause.Pause(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - - err = pauseunpause.Unpause(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package pauseunpause diff --git a/openstack/compute/v2/extensions/pauseunpause/requests.go b/openstack/compute/v2/extensions/pauseunpause/requests.go deleted file mode 100644 index 8e17792f46..0000000000 --- a/openstack/compute/v2/extensions/pauseunpause/requests.go +++ /dev/null @@ -1,22 +0,0 @@ -package pauseunpause - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// Pause is the operation responsible for pausing a Compute server. -func Pause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r PauseResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Unpause is the operation responsible for unpausing a Compute server. -func Unpause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnpauseResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/pauseunpause/results.go b/openstack/compute/v2/extensions/pauseunpause/results.go deleted file mode 100644 index 4a71d2b2fb..0000000000 --- a/openstack/compute/v2/extensions/pauseunpause/results.go +++ /dev/null @@ -1,15 +0,0 @@ -package pauseunpause - -import "github.com/gophercloud/gophercloud/v2" - -// PauseResult is the response from a Pause operation. Call its ExtractErr -// method to determine if the request succeeded or failed. -type PauseResult struct { - gophercloud.ErrResult -} - -// UnpauseResult is the response from an Unpause operation. Call its ExtractErr -// method to determine if the request succeeded or failed. -type UnpauseResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/pauseunpause/testing/doc.go b/openstack/compute/v2/extensions/pauseunpause/testing/doc.go deleted file mode 100644 index 0953867509..0000000000 --- a/openstack/compute/v2/extensions/pauseunpause/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// pauseunpause unit tests -package testing diff --git a/openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go b/openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go deleted file mode 100644 index 645bf2ea6b..0000000000 --- a/openstack/compute/v2/extensions/pauseunpause/testing/fixtures_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockPauseServerResponse(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, `{"pause": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockUnpauseServerResponse(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, `{"unpause": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go b/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go deleted file mode 100644 index 61185bb730..0000000000 --- a/openstack/compute/v2/extensions/pauseunpause/testing/requests_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/pauseunpause" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "{serverId}" - -func TestPause(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockPauseServerResponse(t, serverID) - - err := pauseunpause.Pause(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestUnpause(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockUnpauseServerResponse(t, serverID) - - err := pauseunpause.Unpause(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/rescueunrescue/doc.go b/openstack/compute/v2/extensions/rescueunrescue/doc.go deleted file mode 100644 index 64c01697a2..0000000000 --- a/openstack/compute/v2/extensions/rescueunrescue/doc.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Package rescueunrescue provides the ability to place a server into rescue mode -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" - - adminPass, err := rescueunrescue.Rescue(context.TODO(), computeClient, serverID, rescueOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("adminPass of the rescued server %s: %s\n", serverID, adminPass) - -Example to Unrescue a server - - serverID := "3f54d05f-3430-4d80-aa07-63e6af9e2488" - - if err := rescueunrescue.Unrescue(context.TODO(), computeClient, serverID).ExtractErr(); err != nil { - panic(err) - } -*/ -package rescueunrescue diff --git a/openstack/compute/v2/extensions/rescueunrescue/requests.go b/openstack/compute/v2/extensions/rescueunrescue/requests.go deleted file mode 100644 index 26614dbc47..0000000000 --- a/openstack/compute/v2/extensions/rescueunrescue/requests.go +++ /dev/null @@ -1,55 +0,0 @@ -package rescueunrescue - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// RescueOptsBuilder is an interface that allows extensions to override the -// default structure of a Rescue request. -type RescueOptsBuilder interface { - ToServerRescueMap() (map[string]interface{}, error) -} - -// RescueOpts represents the configuration options used to control a Rescue -// option. -type RescueOpts struct { - // AdminPass is the desired administrative password for the instance in - // RESCUE mode. - // If it's left blank, the server will generate a password. - AdminPass string `json:"adminPass,omitempty"` - - // RescueImageRef contains reference on an image that needs to be used as - // rescue image. - // If it's left blank, the server will be rescued with the default image. - RescueImageRef string `json:"rescue_image_ref,omitempty"` -} - -// ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON -// request body for the Rescue request. -func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "rescue") -} - -// Rescue instructs the provider to place the server into RESCUE mode. -func Rescue(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { - b, err := opts.ToServerRescueMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Unrescue instructs the provider to return the server from RESCUE mode. -func Unrescue(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnrescueResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/rescueunrescue/results.go b/openstack/compute/v2/extensions/rescueunrescue/results.go deleted file mode 100644 index 12073ba677..0000000000 --- a/openstack/compute/v2/extensions/rescueunrescue/results.go +++ /dev/null @@ -1,28 +0,0 @@ -package rescueunrescue - -import "github.com/gophercloud/gophercloud/v2" - -type commonResult struct { - gophercloud.Result -} - -// RescueResult is the response from a Rescue operation. Call its Extract -// method to retrieve adminPass for a rescued server. -type RescueResult struct { - commonResult -} - -// UnrescueResult is the response from an UnRescue operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type UnrescueResult struct { - gophercloud.ErrResult -} - -// Extract interprets any RescueResult as an AdminPass, if possible. -func (r RescueResult) Extract() (string, error) { - var s struct { - AdminPass string `json:"adminPass"` - } - err := r.ExtractInto(&s) - return s.AdminPass, err -} diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/doc.go b/openstack/compute/v2/extensions/rescueunrescue/testing/doc.go deleted file mode 100644 index 7603f836a0..0000000000 --- a/openstack/compute/v2/extensions/rescueunrescue/testing/doc.go +++ /dev/null @@ -1 +0,0 @@ -package testing diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/fixtures_test.go b/openstack/compute/v2/extensions/rescueunrescue/testing/fixtures_test.go deleted file mode 100644 index c822193d6a..0000000000 --- a/openstack/compute/v2/extensions/rescueunrescue/testing/fixtures_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package testing - -// RescueRequest represents request to rescue a server. -const RescueRequest = ` -{ - "rescue": { - "adminPass": "aUPtawPzE9NU", - "rescue_image_ref": "115e5c5b-72f0-4a0a-9067-60706545248c" - } -} -` - -// RescueResult represents a raw server response to a RescueRequest. -const RescueResult = ` -{ - "adminPass": "aUPtawPzE9NU" -} -` - -// UnrescueRequest represents request to unrescue a server. -const UnrescueRequest = ` -{ - "unrescue": null -} -` diff --git a/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go b/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go deleted file mode 100644 index a69982ffcd..0000000000 --- a/openstack/compute/v2/extensions/rescueunrescue/testing/requests_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package testing - -import ( - "context" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/rescueunrescue" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestRescue(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/servers/3f54d05f-3430-4d80-aa07-63e6af9e2488/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestJSONRequest(t, r, RescueRequest) - - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, RescueResult) - }) - - s, err := rescueunrescue.Rescue(context.TODO(), fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488", rescueunrescue.RescueOpts{ - AdminPass: "aUPtawPzE9NU", - RescueImageRef: "115e5c5b-72f0-4a0a-9067-60706545248c", - }).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, "aUPtawPzE9NU", s) -} - -func TestUnrescue(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/servers/3f54d05f-3430-4d80-aa07-63e6af9e2488/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestJSONRequest(t, r, UnrescueRequest) - - w.WriteHeader(http.StatusAccepted) - }) - - err := rescueunrescue.Unrescue(context.TODO(), fake.ServiceClient(), "3f54d05f-3430-4d80-aa07-63e6af9e2488").ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/resetnetwork/doc.go b/openstack/compute/v2/extensions/resetnetwork/doc.go deleted file mode 100644 index e105b702cc..0000000000 --- a/openstack/compute/v2/extensions/resetnetwork/doc.go +++ /dev/null @@ -1,14 +0,0 @@ -/* -Package resetnetwork provides functionality to reset the network of a server -that has been provisioned by the OpenStack Compute service. This action -requires admin privileges and Nova configured with a Xen hypervisor driver. - -Example to Reset a Network of a Server - - serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := resetnetwork.ResetNetwork(context.TODO(), client, id).ExtractErr() - if err != nil { - panic(err) - } -*/ -package resetnetwork diff --git a/openstack/compute/v2/extensions/resetnetwork/requests.go b/openstack/compute/v2/extensions/resetnetwork/requests.go deleted file mode 100644 index 55092f32f1..0000000000 --- a/openstack/compute/v2/extensions/resetnetwork/requests.go +++ /dev/null @@ -1,18 +0,0 @@ -package resetnetwork - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// ResetNetwork will reset the network of a server -func ResetNetwork(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResetResult) { - b := map[string]interface{}{ - "resetNetwork": nil, - } - resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/resetnetwork/results.go b/openstack/compute/v2/extensions/resetnetwork/results.go deleted file mode 100644 index a9522d5440..0000000000 --- a/openstack/compute/v2/extensions/resetnetwork/results.go +++ /dev/null @@ -1,11 +0,0 @@ -package resetnetwork - -import ( - "github.com/gophercloud/gophercloud/v2" -) - -// ResetResult is the response of a ResetNetwork operation. Call its ExtractErr -// method to determine if the request suceeded or failed. -type ResetResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go b/openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go deleted file mode 100644 index feb48a25ad..0000000000 --- a/openstack/compute/v2/extensions/resetnetwork/testing/fixtures_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockResetNetworkResponse(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, `{"resetNetwork": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go b/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go deleted file mode 100644 index 29979eab32..0000000000 --- a/openstack/compute/v2/extensions/resetnetwork/testing/requests_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/resetnetwork" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" - -func TestResetNetwork(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockResetNetworkResponse(t, serverID) - - err := resetnetwork.ResetNetwork(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/resetstate/doc.go b/openstack/compute/v2/extensions/resetstate/doc.go deleted file mode 100644 index aca92cd6b0..0000000000 --- a/openstack/compute/v2/extensions/resetstate/doc.go +++ /dev/null @@ -1,13 +0,0 @@ -/* -Package resetstate provides functionality to reset the state of a server that has -been provisioned by the OpenStack Compute service. - -Example to Reset a Server - - serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - err := resetstate.ResetState(context.TODO(), client, id, resetstate.StateActive).ExtractErr() - if err != nil { - panic(err) - } -*/ -package resetstate diff --git a/openstack/compute/v2/extensions/resetstate/requests.go b/openstack/compute/v2/extensions/resetstate/requests.go deleted file mode 100644 index 2a63f26f70..0000000000 --- a/openstack/compute/v2/extensions/resetstate/requests.go +++ /dev/null @@ -1,27 +0,0 @@ -package resetstate - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// ServerState refers to the states usable in ResetState Action -type ServerState string - -const ( - // StateActive returns the state of the server as active - StateActive ServerState = "active" - - // StateError returns the state of the server as error - StateError ServerState = "error" -) - -// ResetState will reset the state of a server -func ResetState(ctx context.Context, client *gophercloud.ServiceClient, id string, state ServerState) (r ResetResult) { - stateMap := map[string]interface{}{"state": state} - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/resetstate/results.go b/openstack/compute/v2/extensions/resetstate/results.go deleted file mode 100644 index 5f3c63db69..0000000000 --- a/openstack/compute/v2/extensions/resetstate/results.go +++ /dev/null @@ -1,11 +0,0 @@ -package resetstate - -import ( - "github.com/gophercloud/gophercloud/v2" -) - -// ResetResult is the response of a ResetState operation. Call its ExtractErr -// method to determine if the request suceeded or failed. -type ResetResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/resetstate/testing/doc.go b/openstack/compute/v2/extensions/resetstate/testing/doc.go deleted file mode 100644 index 7603f836a0..0000000000 --- a/openstack/compute/v2/extensions/resetstate/testing/doc.go +++ /dev/null @@ -1 +0,0 @@ -package testing diff --git a/openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go b/openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go deleted file mode 100644 index 7384908c23..0000000000 --- a/openstack/compute/v2/extensions/resetstate/testing/fixtures_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockResetStateResponse(t *testing.T, id string, state 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, fmt.Sprintf(`{"os-resetState": {"state": "%s"}}`, state)) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/extensions/resetstate/testing/requests_test.go b/openstack/compute/v2/extensions/resetstate/testing/requests_test.go deleted file mode 100644 index 7ae25169df..0000000000 --- a/openstack/compute/v2/extensions/resetstate/testing/requests_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/resetstate" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "b16ba811-199d-4ffd-8839-ba96c1185a67" - -func TestResetState(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockResetStateResponse(t, serverID, "active") - - err := resetstate.ResetState(context.TODO(), client.ServiceClient(), serverID, "active").ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/shelveunshelve/doc.go b/openstack/compute/v2/extensions/shelveunshelve/doc.go deleted file mode 100644 index 895b385a94..0000000000 --- a/openstack/compute/v2/extensions/shelveunshelve/doc.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Package shelveunshelve provides functionality to start and stop servers that have -been provisioned by the OpenStack Compute service. - -Example to Shelve, Shelve-offload and Unshelve a Server - - serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - - err := shelveunshelve.Shelve(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - - err := shelveunshelve.ShelveOffload(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - - err := shelveunshelve.Unshelve(context.TODO(), computeClient, serverID, nil).ExtractErr() - if err != nil { - panic(err) - } -*/ -package shelveunshelve diff --git a/openstack/compute/v2/extensions/shelveunshelve/requests.go b/openstack/compute/v2/extensions/shelveunshelve/requests.go deleted file mode 100644 index 2f5551c0b4..0000000000 --- a/openstack/compute/v2/extensions/shelveunshelve/requests.go +++ /dev/null @@ -1,62 +0,0 @@ -package shelveunshelve - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// Shelve is the operation responsible for shelving a Compute server. -func Shelve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ShelveOffload is the operation responsible for Shelve-Offload a Compute server. -func ShelveOffload(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UnshelveOptsBuilder allows extensions to add additional parameters to the -// Unshelve request. -type UnshelveOptsBuilder interface { - ToUnshelveMap() (map[string]interface{}, error) -} - -// UnshelveOpts specifies parameters of shelve-offload action. -type UnshelveOpts struct { - // Sets the availability zone to unshelve a server - // Available only after nova 2.77 - AvailabilityZone string `json:"availability_zone,omitempty"` -} - -func (opts UnshelveOpts) ToUnshelveMap() (map[string]interface{}, error) { - // Key 'availabilty_zone' is required if the unshelve action is an object - // i.e {"unshelve": {}} will be rejected - b, err := gophercloud.BuildRequestBody(opts, "unshelve") - if err != nil { - return nil, err - } - - if _, ok := b["unshelve"].(map[string]interface{})["availability_zone"]; !ok { - b["unshelve"] = nil - } - - return b, err -} - -// Unshelve is the operation responsible for unshelve a Compute server. -func Unshelve(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UnshelveOptsBuilder) (r UnshelveResult) { - b, err := opts.ToUnshelveMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, extensions.ActionURL(client, id), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/shelveunshelve/results.go b/openstack/compute/v2/extensions/shelveunshelve/results.go deleted file mode 100644 index 553b60d0a7..0000000000 --- a/openstack/compute/v2/extensions/shelveunshelve/results.go +++ /dev/null @@ -1,21 +0,0 @@ -package shelveunshelve - -import "github.com/gophercloud/gophercloud/v2" - -// ShelveResult is the response from a Shelve operation. Call its ExtractErr -// method to determine if the request succeeded or failed. -type ShelveResult struct { - gophercloud.ErrResult -} - -// ShelveOffloadResult is the response from a Shelve operation. Call its ExtractErr -// method to determine if the request succeeded or failed. -type ShelveOffloadResult struct { - gophercloud.ErrResult -} - -// UnshelveResult is the response from Stop operation. Call its ExtractErr -// method to determine if the request succeeded or failed. -type UnshelveResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go b/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go deleted file mode 100644 index fbe958d31e..0000000000 --- a/openstack/compute/v2/extensions/shelveunshelve/testing/fixtures_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockShelveServerResponse(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, `{"shelve": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockShelveOffloadServerResponse(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, `{"shelveOffload": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockUnshelveServerResponseWithAvailabilityZone(t *testing.T, id string, az 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, fmt.Sprintf(`{ - "unshelve": { - "availability_zone": "%s" - } - }`, az)) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockUnshelveServerResponseNoAvailabilityZone(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, `{"unshelve": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go b/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go deleted file mode 100644 index 60f9304d1f..0000000000 --- a/openstack/compute/v2/extensions/shelveunshelve/testing/requests_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/shelveunshelve" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "{serverId}" -const availabilityZone = "test-zone" - -func TestShelve(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockShelveServerResponse(t, serverID) - - err := shelveunshelve.Shelve(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestShelveOffload(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockShelveOffloadServerResponse(t, serverID) - - err := shelveunshelve.ShelveOffload(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestUnshelveNoAvailabilityZone(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - unshelveOpts := shelveunshelve.UnshelveOpts{} - - mockUnshelveServerResponseNoAvailabilityZone(t, serverID) - - err := shelveunshelve.Unshelve(context.TODO(), client.ServiceClient(), serverID, unshelveOpts).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestUnshelveWithAvailabilityZone(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - unshelveOpts := shelveunshelve.UnshelveOpts{ - AvailabilityZone: availabilityZone, - } - - mockUnshelveServerResponseWithAvailabilityZone(t, serverID, availabilityZone) - - err := shelveunshelve.Unshelve(context.TODO(), client.ServiceClient(), serverID, unshelveOpts).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/startstop/doc.go b/openstack/compute/v2/extensions/startstop/doc.go deleted file mode 100644 index f34dde1b50..0000000000 --- a/openstack/compute/v2/extensions/startstop/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Package startstop provides functionality to start and stop servers that have -been provisioned by the OpenStack Compute service. - -Example to Stop and Start a Server - - serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - - err := startstop.Stop(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - - err := startstop.Start(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package startstop diff --git a/openstack/compute/v2/extensions/startstop/requests.go b/openstack/compute/v2/extensions/startstop/requests.go deleted file mode 100644 index 9b430ef7d7..0000000000 --- a/openstack/compute/v2/extensions/startstop/requests.go +++ /dev/null @@ -1,22 +0,0 @@ -package startstop - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// Start is the operation responsible for starting a Compute server. -func Start(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StartResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Stop is the operation responsible for stopping a Compute server. -func Stop(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StopResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/startstop/results.go b/openstack/compute/v2/extensions/startstop/results.go deleted file mode 100644 index 5c5f94005a..0000000000 --- a/openstack/compute/v2/extensions/startstop/results.go +++ /dev/null @@ -1,15 +0,0 @@ -package startstop - -import "github.com/gophercloud/gophercloud/v2" - -// StartResult is the response from a Start operation. Call its ExtractErr -// method to determine if the request succeeded or failed. -type StartResult struct { - gophercloud.ErrResult -} - -// StopResult is the response from Stop operation. Call its ExtractErr -// method to determine if the request succeeded or failed. -type StopResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/startstop/testing/doc.go b/openstack/compute/v2/extensions/startstop/testing/doc.go deleted file mode 100644 index b6c5b8c140..0000000000 --- a/openstack/compute/v2/extensions/startstop/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// startstop unit tests -package testing diff --git a/openstack/compute/v2/extensions/startstop/testing/fixtures_test.go b/openstack/compute/v2/extensions/startstop/testing/fixtures_test.go deleted file mode 100644 index 6d98be4bc6..0000000000 --- a/openstack/compute/v2/extensions/startstop/testing/fixtures_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockStartServerResponse(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-start": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockStopServerResponse(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-stop": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/extensions/startstop/testing/requests_test.go b/openstack/compute/v2/extensions/startstop/testing/requests_test.go deleted file mode 100644 index fc65e76ed2..0000000000 --- a/openstack/compute/v2/extensions/startstop/testing/requests_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/startstop" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "{serverId}" - -func TestStart(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockStartServerResponse(t, serverID) - - err := startstop.Start(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestStop(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockStopServerResponse(t, serverID) - - err := startstop.Stop(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/extensions/suspendresume/doc.go b/openstack/compute/v2/extensions/suspendresume/doc.go deleted file mode 100644 index e8dea36143..0000000000 --- a/openstack/compute/v2/extensions/suspendresume/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Package suspendresume provides functionality to suspend and resume servers that have -been provisioned by the OpenStack Compute service. - -Example to Suspend and Resume a Server - - serverID := "47b6b7b7-568d-40e4-868c-d5c41735532e" - - err := suspendresume.Suspend(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } - - err := suspendresume.Resume(context.TODO(), computeClient, serverID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package suspendresume diff --git a/openstack/compute/v2/extensions/suspendresume/requests.go b/openstack/compute/v2/extensions/suspendresume/requests.go deleted file mode 100644 index 0ffd5f14fc..0000000000 --- a/openstack/compute/v2/extensions/suspendresume/requests.go +++ /dev/null @@ -1,22 +0,0 @@ -package suspendresume - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions" -) - -// Suspend is the operation responsible for suspending a Compute server. -func Suspend(ctx context.Context, client *gophercloud.ServiceClient, id string) (r SuspendResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Resume is the operation responsible for resuming a Compute server. -func Resume(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnsuspendResult) { - resp, err := client.Post(ctx, extensions.ActionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/extensions/suspendresume/results.go b/openstack/compute/v2/extensions/suspendresume/results.go deleted file mode 100644 index 5207bf89f8..0000000000 --- a/openstack/compute/v2/extensions/suspendresume/results.go +++ /dev/null @@ -1,15 +0,0 @@ -package suspendresume - -import "github.com/gophercloud/gophercloud/v2" - -// SuspendResult is the response from a Suspend operation. Call its -// ExtractErr method to determine if the request succeeded or failed. -type SuspendResult struct { - gophercloud.ErrResult -} - -// UnsuspendResult is the response from an Unsuspend operation. Call -// its ExtractErr method to determine if the request succeeded or failed. -type UnsuspendResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/extensions/suspendresume/testing/doc.go b/openstack/compute/v2/extensions/suspendresume/testing/doc.go deleted file mode 100644 index 834f25516f..0000000000 --- a/openstack/compute/v2/extensions/suspendresume/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// suspendresume unit tests -package testing diff --git a/openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go b/openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go deleted file mode 100644 index 11e6b55e4d..0000000000 --- a/openstack/compute/v2/extensions/suspendresume/testing/fixtures_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package testing - -import ( - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func mockSuspendServerResponse(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, `{"suspend": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockResumeServerResponse(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, `{"resume": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go b/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go deleted file mode 100644 index 27f13aa592..0000000000 --- a/openstack/compute/v2/extensions/suspendresume/testing/requests_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/suspendresume" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const serverID = "{serverId}" - -func TestSuspend(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockSuspendServerResponse(t, serverID) - - err := suspendresume.Suspend(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestResume(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockResumeServerResponse(t, serverID) - - err := suspendresume.Resume(context.TODO(), client.ServiceClient(), serverID).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index da007377ed..585e0c28e5 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -921,3 +921,283 @@ func ShowConsoleOutput(ctx context.Context, client *gophercloud.ServiceClient, i _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// EvacuateOptsBuilder allows extensions to add additional parameters to the +// the Evacuate request. +type EvacuateOptsBuilder interface { + ToEvacuateMap() (map[string]interface{}, error) +} + +// EvacuateOpts specifies Evacuate action parameters. +type EvacuateOpts struct { + // The name of the host to which the server is evacuated + Host string `json:"host,omitempty"` + + // Indicates whether server is on shared storage + OnSharedStorage bool `json:"onSharedStorage"` + + // An administrative password to access the evacuated server + AdminPass string `json:"adminPass,omitempty"` +} + +// ToServerGroupCreateMap constructs a request body from CreateOpts. +func (opts EvacuateOpts) ToEvacuateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "evacuate") +} + +// Evacuate will Evacuate a failed instance to another host. +func Evacuate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts EvacuateOptsBuilder) (r EvacuateResult) { + b, err := opts.ToEvacuateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// InjectNetworkInfo will inject the network info into a server +func InjectNetworkInfo(ctx context.Context, client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { + b := map[string]interface{}{ + "injectNetworkInfo": nil, + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Lock is the operation responsible for locking a Compute server. +func Lock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r LockResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unlock is the operation responsible for unlocking a Compute server. +func Unlock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnlockResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Migrate will initiate a migration of the instance to another host. +func Migrate(ctx context.Context, client *gophercloud.ServiceClient, id string) (r MigrateResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// LiveMigrateOptsBuilder allows extensions to add additional parameters to the +// LiveMigrate request. +type LiveMigrateOptsBuilder interface { + ToLiveMigrateMap() (map[string]interface{}, error) +} + +// LiveMigrateOpts specifies parameters of live migrate action. +type LiveMigrateOpts struct { + // The host to which to migrate the server. + // If this parameter is None, the scheduler chooses a host. + Host *string `json:"host"` + + // Set to True to migrate local disks by using block migration. + // If the source or destination host uses shared storage and you set + // this value to True, the live migration fails. + BlockMigration *bool `json:"block_migration,omitempty"` + + // Set to True to enable over commit when the destination host is checked + // for available disk space. Set to False to disable over commit. This setting + // affects only the libvirt virt driver. + DiskOverCommit *bool `json:"disk_over_commit,omitempty"` +} + +// ToLiveMigrateMap constructs a request body from LiveMigrateOpts. +func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-migrateLive") +} + +// LiveMigrate will initiate a live-migration (without rebooting) of the instance to another host. +func LiveMigrate(ctx context.Context, client *gophercloud.ServiceClient, id string, opts LiveMigrateOptsBuilder) (r MigrateResult) { + b, err := opts.ToLiveMigrateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Pause is the operation responsible for pausing a Compute server. +func Pause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r PauseResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unpause is the operation responsible for unpausing a Compute server. +func Unpause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnpauseResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RescueOptsBuilder is an interface that allows extensions to override the +// default structure of a Rescue request. +type RescueOptsBuilder interface { + ToServerRescueMap() (map[string]interface{}, error) +} + +// RescueOpts represents the configuration options used to control a Rescue +// option. +type RescueOpts struct { + // AdminPass is the desired administrative password for the instance in + // RESCUE mode. + // If it's left blank, the server will generate a password. + AdminPass string `json:"adminPass,omitempty"` + + // RescueImageRef contains reference on an image that needs to be used as + // rescue image. + // If it's left blank, the server will be rescued with the default image. + RescueImageRef string `json:"rescue_image_ref,omitempty"` +} + +// ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON +// request body for the Rescue request. +func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "rescue") +} + +// Rescue instructs the provider to place the server into RESCUE mode. +func Rescue(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { + b, err := opts.ToServerRescueMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Unrescue instructs the provider to return the server from RESCUE mode. +func Unrescue(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnrescueResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ResetNetwork will reset the network of a server +func ResetNetwork(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResetNetworkResult) { + b := map[string]interface{}{ + "resetNetwork": nil, + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ServerState refers to the states usable in ResetState Action +type ServerState string + +const ( + // StateActive returns the state of the server as active + StateActive ServerState = "active" + + // StateError returns the state of the server as error + StateError ServerState = "error" +) + +// ResetState will reset the state of a server +func ResetState(ctx context.Context, client *gophercloud.ServiceClient, id string, state ServerState) (r ResetStateResult) { + stateMap := map[string]interface{}{"state": state} + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Shelve is the operation responsible for shelving a Compute server. +func Shelve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ShelveOffload is the operation responsible for Shelve-Offload a Compute server. +func ShelveOffload(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UnshelveOptsBuilder allows extensions to add additional parameters to the +// Unshelve request. +type UnshelveOptsBuilder interface { + ToUnshelveMap() (map[string]interface{}, error) +} + +// UnshelveOpts specifies parameters of shelve-offload action. +type UnshelveOpts struct { + // Sets the availability zone to unshelve a server + // Available only after nova 2.77 + AvailabilityZone string `json:"availability_zone,omitempty"` +} + +func (opts UnshelveOpts) ToUnshelveMap() (map[string]interface{}, error) { + // Key 'availabilty_zone' is required if the unshelve action is an object + // i.e {"unshelve": {}} will be rejected + b, err := gophercloud.BuildRequestBody(opts, "unshelve") + if err != nil { + return nil, err + } + + if _, ok := b["unshelve"].(map[string]interface{})["availability_zone"]; !ok { + b["unshelve"] = nil + } + + return b, err +} + +// Unshelve is the operation responsible for unshelve a Compute server. +func Unshelve(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UnshelveOptsBuilder) (r UnshelveResult) { + b, err := opts.ToUnshelveMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Start is the operation responsible for starting a Compute server. +func Start(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StartResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Stop is the operation responsible for stopping a Compute server. +func Stop(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StopResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Suspend is the operation responsible for suspending a Compute server. +func Suspend(ctx context.Context, client *gophercloud.ServiceClient, id string) (r SuspendResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Resume is the operation responsible for resuming a Compute server. +func Resume(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResumeResult) { + resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 7793940c9a..286253d9ce 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -538,3 +538,135 @@ func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) { return s[key], err } + +// EvacuateResult is the response from an Evacuate operation. +// Call its ExtractAdminPass method to retrieve the admin password of the instance. +// The admin password will be an empty string if the cloud is not configured to inject admin passwords.. +type EvacuateResult struct { + gophercloud.Result +} + +func (r EvacuateResult) ExtractAdminPass() (string, error) { + var s struct { + AdminPass string `json:"adminPass"` + } + err := r.ExtractInto(&s) + if err != nil && err.Error() == "EOF" { + return "", nil + } + return s.AdminPass, err +} + +// InjectNetworkResult is the response of a InjectNetworkInfo operation. Call +// its ExtractErr method to determine if the request suceeded or failed. +type InjectNetworkResult struct { + gophercloud.ErrResult +} + +// LockResult and UnlockResult are the responses from a Lock and Unlock +// operations respectively. Call their ExtractErr methods to determine if the +// requests suceeded or failed. +type LockResult struct { + gophercloud.ErrResult +} + +type UnlockResult struct { + gophercloud.ErrResult +} + +// MigrateResult is the response from a Migrate operation. Call its ExtractErr +// method to determine if the request suceeded or failed. +type MigrateResult struct { + gophercloud.ErrResult +} + +// PauseResult is the response from a Pause operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type PauseResult struct { + gophercloud.ErrResult +} + +// UnpauseResult is the response from an Unpause operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type UnpauseResult struct { + gophercloud.ErrResult +} + +type commonResult struct { + gophercloud.Result +} + +// RescueResult is the response from a Rescue operation. Call its Extract +// method to retrieve adminPass for a rescued server. +type RescueResult struct { + commonResult +} + +// UnrescueResult is the response from an UnRescue operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type UnrescueResult struct { + gophercloud.ErrResult +} + +// Extract interprets any RescueResult as an AdminPass, if possible. +func (r RescueResult) Extract() (string, error) { + var s struct { + AdminPass string `json:"adminPass"` + } + err := r.ExtractInto(&s) + return s.AdminPass, err +} + +// ResetResult is the response of a ResetNetwork operation. Call its ExtractErr +// method to determine if the request suceeded or failed. +type ResetNetworkResult struct { + gophercloud.ErrResult +} + +// ResetResult is the response of a ResetState operation. Call its ExtractErr +// method to determine if the request suceeded or failed. +type ResetStateResult struct { + gophercloud.ErrResult +} + +// ShelveResult is the response from a Shelve operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type ShelveResult struct { + gophercloud.ErrResult +} + +// ShelveOffloadResult is the response from a Shelve operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type ShelveOffloadResult struct { + gophercloud.ErrResult +} + +// UnshelveResult is the response from Stop operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type UnshelveResult struct { + gophercloud.ErrResult +} + +// StartResult is the response from a Start operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type StartResult struct { + gophercloud.ErrResult +} + +// StopResult is the response from Stop operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type StopResult struct { + gophercloud.ErrResult +} + +// SuspendResult is the response from a Suspend operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type SuspendResult struct { + gophercloud.ErrResult +} + +// ResumeResult is the response from an Unsuspend operation. Call +// its ExtractErr method to determine if the request succeeded or failed. +type ResumeResult struct { + gophercloud.ErrResult +} From 982ecaaa7f481e6b572f63c99e67359f3f00b3af Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 12:26:55 +0000 Subject: [PATCH 1816/2296] compute: Merge schedulerhints extension This ones a little complicated since it's both an extension of the Server request struct and a validator for same. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/compute.go | 10 +- .../v2/extensions/schedulerhints/doc.go | 76 -------- .../v2/extensions/schedulerhints/requests.go | 183 ------------------ .../extensions/schedulerhints/testing/doc.go | 2 - .../schedulerhints/testing/requests_test.go | 141 -------------- openstack/compute/v2/servers/doc.go | 70 +++++++ openstack/compute/v2/servers/requests.go | 181 ++++++++++++++++- .../v2/servers/testing/fixtures_test.go | 9 +- .../v2/servers/testing/requests_test.go | 124 ++++++++++++ 9 files changed, 381 insertions(+), 415 deletions(-) delete mode 100644 openstack/compute/v2/extensions/schedulerhints/doc.go delete mode 100644 openstack/compute/v2/extensions/schedulerhints/requests.go delete mode 100644 openstack/compute/v2/extensions/schedulerhints/testing/doc.go delete mode 100644 openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index b8e553a177..35eb043504 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -17,7 +17,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/schedulerhints" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" @@ -722,15 +721,12 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, Networks: []servers.Network{ {UUID: networkID}, }, - } - - schedulerHintsOpts := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - SchedulerHints: schedulerhints.SchedulerHints{ + SchedulerHints: servers.SchedulerHints{ Group: serverGroup.ID, }, } - server, err := servers.Create(context.TODO(), client, schedulerHintsOpts).Extract() + + server, err := servers.Create(context.TODO(), client, serverCreateOpts).Extract() if err != nil { return nil, err } diff --git a/openstack/compute/v2/extensions/schedulerhints/doc.go b/openstack/compute/v2/extensions/schedulerhints/doc.go deleted file mode 100644 index 2321312e7e..0000000000 --- a/openstack/compute/v2/extensions/schedulerhints/doc.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Package schedulerhints extends the server create request with the ability to -specify additional parameters which determine where the server will be -created in the OpenStack cloud. - -Example to Add a Server to a Server Group - - schedulerHints := schedulerhints.SchedulerHints{ - Group: "servergroup-uuid", - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_name", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - } - - createOpts := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - SchedulerHints: schedulerHints, - } - - server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Place Server B on a Different Host than Server A - - schedulerHints := schedulerhints.SchedulerHints{ - DifferentHost: []string{ - "server-a-uuid", - } - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_b", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - } - - createOpts := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - SchedulerHints: schedulerHints, - } - - server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Place Server B on the Same Host as Server A - - schedulerHints := schedulerhints.SchedulerHints{ - SameHost: []string{ - "server-a-uuid", - } - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_b", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - } - - createOpts := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - SchedulerHints: schedulerHints, - } - - server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() - if err != nil { - panic(err) - } -*/ -package schedulerhints diff --git a/openstack/compute/v2/extensions/schedulerhints/requests.go b/openstack/compute/v2/extensions/schedulerhints/requests.go deleted file mode 100644 index 0535d32556..0000000000 --- a/openstack/compute/v2/extensions/schedulerhints/requests.go +++ /dev/null @@ -1,183 +0,0 @@ -package schedulerhints - -import ( - "encoding/json" - "net" - "regexp" - "strings" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" -) - -// SchedulerHints represents a set of scheduling hints that are passed to the -// OpenStack scheduler. -type SchedulerHints struct { - // Group specifies a Server Group to place the instance in. - Group string - - // DifferentHost will place the instance on a compute node that does not - // host the given instances. - DifferentHost []string - - // SameHost will place the instance on a compute node that hosts the given - // instances. - SameHost []string - - // Query is a conditional statement that results in compute nodes able to - // host the instance. - Query []interface{} - - // TargetCell specifies a cell name where the instance will be placed. - TargetCell string `json:"target_cell,omitempty"` - - // DifferentCell specifies cells names where an instance should not be placed. - DifferentCell []string `json:"different_cell,omitempty"` - - // BuildNearHostIP specifies a subnet of compute nodes to host the instance. - BuildNearHostIP string - - // AdditionalProperies are arbitrary key/values that are not validated by nova. - AdditionalProperties map[string]interface{} -} - -// CreateOptsBuilder builds the scheduler hints into a serializable format. -type CreateOptsBuilder interface { - ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) -} - -// ToServerSchedulerHintsMap builds the scheduler hints into a serializable format. -func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) { - sh := make(map[string]interface{}) - - uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") - - if opts.Group != "" { - if !uuidRegex.MatchString(opts.Group) { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.Group" - err.Value = opts.Group - err.Info = "Group must be a UUID" - return nil, err - } - sh["group"] = opts.Group - } - - if len(opts.DifferentHost) > 0 { - for _, diffHost := range opts.DifferentHost { - if !uuidRegex.MatchString(diffHost) { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.DifferentHost" - err.Value = opts.DifferentHost - err.Info = "The hosts must be in UUID format." - return nil, err - } - } - sh["different_host"] = opts.DifferentHost - } - - if len(opts.SameHost) > 0 { - for _, sameHost := range opts.SameHost { - if !uuidRegex.MatchString(sameHost) { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.SameHost" - err.Value = opts.SameHost - err.Info = "The hosts must be in UUID format." - return nil, err - } - } - sh["same_host"] = opts.SameHost - } - - /* - Query can be something simple like: - [">=", "$free_ram_mb", 1024] - - Or more complex like: - ['and', - ['>=', '$free_ram_mb', 1024], - ['>=', '$free_disk_mb', 200 * 1024] - ] - - Because of the possible complexity, just make sure the length is a minimum of 3. - */ - if len(opts.Query) > 0 { - if len(opts.Query) < 3 { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.Query" - err.Value = opts.Query - err.Info = "Must be a conditional statement in the format of [op,variable,value]" - return nil, err - } - - // The query needs to be sent as a marshalled string. - b, err := json.Marshal(opts.Query) - if err != nil { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.Query" - err.Value = opts.Query - err.Info = "Must be a conditional statement in the format of [op,variable,value]" - return nil, err - } - - sh["query"] = string(b) - } - - if opts.TargetCell != "" { - sh["target_cell"] = opts.TargetCell - } - - if len(opts.DifferentCell) > 0 { - sh["different_cell"] = opts.DifferentCell - } - - if opts.BuildNearHostIP != "" { - if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil { - err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.BuildNearHostIP" - err.Value = opts.BuildNearHostIP - err.Info = "Must be a valid subnet in the form 192.168.1.1/24" - return nil, err - } - ipParts := strings.Split(opts.BuildNearHostIP, "/") - sh["build_near_host_ip"] = ipParts[0] - sh["cidr"] = "/" + ipParts[1] - } - - if opts.AdditionalProperties != nil { - for k, v := range opts.AdditionalProperties { - sh[k] = v - } - } - - return sh, nil -} - -// CreateOptsExt adds a SchedulerHints option to the base CreateOpts. -type CreateOptsExt struct { - servers.CreateOptsBuilder - - // SchedulerHints provides a set of hints to the scheduler. - SchedulerHints CreateOptsBuilder -} - -// ToServerCreateMap adds the SchedulerHints option to the base server creation options. -func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { - base, err := opts.CreateOptsBuilder.ToServerCreateMap() - if err != nil { - return nil, err - } - - schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsCreateMap() - if err != nil { - return nil, err - } - - if len(schedulerHints) == 0 { - return base, nil - } - - base["os:scheduler_hints"] = schedulerHints - - return base, nil -} diff --git a/openstack/compute/v2/extensions/schedulerhints/testing/doc.go b/openstack/compute/v2/extensions/schedulerhints/testing/doc.go deleted file mode 100644 index 1915aef2fe..0000000000 --- a/openstack/compute/v2/extensions/schedulerhints/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// schedulerhints unit tests -package testing diff --git a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go b/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go deleted file mode 100644 index b25a7579d6..0000000000 --- a/openstack/compute/v2/extensions/schedulerhints/testing/requests_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package testing - -import ( - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/extensions/schedulerhints" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestCreateOpts(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - } - - schedulerHints := schedulerhints.SchedulerHints{ - Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba", - DifferentHost: []string{ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287", - }, - SameHost: []string{ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287", - }, - Query: []interface{}{"=", "$free_ram_mb", "1024"}, - TargetCell: "foobar", - DifferentCell: []string{ - "bazbar", - "barbaz", - }, - BuildNearHostIP: "192.168.1.1/24", - AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, - } - - ext := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: base, - SchedulerHints: schedulerHints, - } - - expected := ` - { - "server": { - "name": "createdserver", - "imageRef": "asdfasdfasdf", - "flavorRef": "performance1-1" - }, - "os:scheduler_hints": { - "group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba", - "different_host": [ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287" - ], - "same_host": [ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287" - ], - "query": "[\"=\",\"$free_ram_mb\",\"1024\"]", - "target_cell": "foobar", - "different_cell": [ - "bazbar", - "barbaz" - ], - "build_near_host_ip": "192.168.1.1", - "cidr": "/24", - "reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" - } - } - ` - actual, err := ext.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) -} - -func TestCreateOptsWithComplexQuery(t *testing.T) { - base := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - } - - schedulerHints := schedulerhints.SchedulerHints{ - Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba", - DifferentHost: []string{ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287", - }, - SameHost: []string{ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287", - }, - Query: []interface{}{"and", []string{"=", "$free_ram_mb", "1024"}, []string{"=", "$free_disk_mb", "204800"}}, - TargetCell: "foobar", - DifferentCell: []string{ - "bazbar", - "barbaz", - }, - BuildNearHostIP: "192.168.1.1/24", - AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, - } - - ext := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: base, - SchedulerHints: schedulerHints, - } - - expected := ` - { - "server": { - "name": "createdserver", - "imageRef": "asdfasdfasdf", - "flavorRef": "performance1-1" - }, - "os:scheduler_hints": { - "group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba", - "different_host": [ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287" - ], - "same_host": [ - "a0cf03a5-d921-4877-bb5c-86d26cf818e1", - "8c19174f-4220-44f0-824a-cd1eeef10287" - ], - "query": "[\"and\",[\"=\",\"$free_ram_mb\",\"1024\"],[\"=\",\"$free_disk_mb\",\"204800\"]]", - "target_cell": "foobar", - "different_cell": [ - "bazbar", - "barbaz" - ], - "build_near_host_ip": "192.168.1.1", - "cidr": "/24", - "reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" - } - } - ` - actual, err := ext.ToServerCreateMap() - th.AssertNoErr(t, err) - th.CheckJSONEquals(t, expected, actual) -} diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index 83a2e4015f..b6a435e2bf 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -58,6 +58,76 @@ Example to Create a Server panic(err) } +Example to Add a Server to a Server Group + + schedulerHints := schedulerhints.SchedulerHints{ + Group: "servergroup-uuid", + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := schedulerhints.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + SchedulerHints: schedulerHints, + } + + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Place Server B on a Different Host than Server A + + schedulerHints := schedulerhints.SchedulerHints{ + DifferentHost: []string{ + "server-a-uuid", + } + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_b", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := schedulerhints.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + SchedulerHints: schedulerHints, + } + + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Place Server B on the Same Host as Server A + + schedulerHints := schedulerhints.SchedulerHints{ + SameHost: []string{ + "server-a-uuid", + } + } + + serverCreateOpts := servers.CreateOpts{ + Name: "server_b", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := schedulerhints.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + SchedulerHints: schedulerHints, + } + + server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + # Example to Create a Server From an Image This example will boot a server from an image and use a standard ephemeral diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 585e0c28e5..1b45d823fd 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -5,6 +5,9 @@ import ( "encoding/base64" "encoding/json" "fmt" + "net" + "regexp" + "strings" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" @@ -125,10 +128,147 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToServerCreateMap() (map[string]interface{}, error) +// SchedulerHintsCreateOptsBuilder builds the scheduler hints into a serializable format. +type SchedulerHintsCreateOptsBuilder interface { + ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) +} + +// SchedulerHints represents a set of scheduling hints that are passed to the +// OpenStack scheduler. +type SchedulerHints struct { + // Group specifies a Server Group to place the instance in. + Group string + + // DifferentHost will place the instance on a compute node that does not + // host the given instances. + DifferentHost []string + + // SameHost will place the instance on a compute node that hosts the given + // instances. + SameHost []string + + // Query is a conditional statement that results in compute nodes able to + // host the instance. + Query []interface{} + + // TargetCell specifies a cell name where the instance will be placed. + TargetCell string `json:"target_cell,omitempty"` + + // DifferentCell specifies cells names where an instance should not be placed. + DifferentCell []string `json:"different_cell,omitempty"` + + // BuildNearHostIP specifies a subnet of compute nodes to host the instance. + BuildNearHostIP string + + // AdditionalProperies are arbitrary key/values that are not validated by nova. + AdditionalProperties map[string]interface{} +} + +// ToServerSchedulerHintsMap builds the scheduler hints into a serializable format. +func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) { + sh := make(map[string]interface{}) + + uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") + + if opts.Group != "" { + if !uuidRegex.MatchString(opts.Group) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.Group" + err.Value = opts.Group + err.Info = "Group must be a UUID" + return nil, err + } + sh["group"] = opts.Group + } + + if len(opts.DifferentHost) > 0 { + for _, diffHost := range opts.DifferentHost { + if !uuidRegex.MatchString(diffHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.DifferentHost" + err.Value = opts.DifferentHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["different_host"] = opts.DifferentHost + } + + if len(opts.SameHost) > 0 { + for _, sameHost := range opts.SameHost { + if !uuidRegex.MatchString(sameHost) { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.SameHost" + err.Value = opts.SameHost + err.Info = "The hosts must be in UUID format." + return nil, err + } + } + sh["same_host"] = opts.SameHost + } + + /* + Query can be something simple like: + [">=", "$free_ram_mb", 1024] + + Or more complex like: + ['and', + ['>=', '$free_ram_mb', 1024], + ['>=', '$free_disk_mb', 200 * 1024] + ] + + Because of the possible complexity, just make sure the length is a minimum of 3. + */ + if len(opts.Query) > 0 { + if len(opts.Query) < 3 { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.Query" + err.Value = opts.Query + err.Info = "Must be a conditional statement in the format of [op,variable,value]" + return nil, err + } + + // The query needs to be sent as a marshalled string. + b, err := json.Marshal(opts.Query) + if err != nil { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.Query" + err.Value = opts.Query + err.Info = "Must be a conditional statement in the format of [op,variable,value]" + return nil, err + } + + sh["query"] = string(b) + } + + if opts.TargetCell != "" { + sh["target_cell"] = opts.TargetCell + } + + if len(opts.DifferentCell) > 0 { + sh["different_cell"] = opts.DifferentCell + } + + if opts.BuildNearHostIP != "" { + if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil { + err := gophercloud.ErrInvalidInput{} + err.Argument = "schedulerhints.SchedulerHints.BuildNearHostIP" + err.Value = opts.BuildNearHostIP + err.Info = "Must be a valid subnet in the form 192.168.1.1/24" + return nil, err + } + ipParts := strings.Split(opts.BuildNearHostIP, "/") + sh["build_near_host_ip"] = ipParts[0] + sh["cidr"] = "/" + ipParts[1] + } + + if opts.AdditionalProperties != nil { + for k, v := range opts.AdditionalProperties { + sh[k] = v + } + } + + return sh, nil } // Network is used within CreateOpts to control a new server's network @@ -284,6 +424,12 @@ const ( Manual DiskConfig = "MANUAL" ) +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToServerCreateMap() (map[string]interface{}, error) +} + // CreateOpts specifies server creation parameters. type CreateOpts struct { // Name is the name to assign to the newly launched server. @@ -357,16 +503,23 @@ type CreateOpts struct { // DiskConfig [optional] controls how the created server's disk is partitioned. DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` + + // SchedulerHints provides a set of hints to the scheduler. + SchedulerHints SchedulerHintsCreateOptsBuilder } // ToServerCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { + // We intentionally don't envelope the body here since we want to strip + // some fields out and modify others b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } + delete(b, "SchedulerHints") + if opts.UserData != nil { var userData string if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil { @@ -422,7 +575,25 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { b["max_count"] = opts.Max } - return map[string]interface{}{"server": b}, nil + // Now we do our enveloping + b = map[string]interface{}{"server": b} + + if opts.SchedulerHints == nil { + return b, nil + } + + schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsCreateMap() + if err != nil { + return nil, err + } + + if len(schedulerHints) == 0 { + return b, nil + } + + b["os:scheduler_hints"] = schedulerHints + + return b, nil } // Create requests a server to be provisioned to the user in the current tenant. diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index ac43be8047..e5cde0d8fb 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -716,7 +716,14 @@ type CreateOptsWithCustomField struct { } func (opts CreateOptsWithCustomField) ToServerCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "server") + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + delete(b, "SchedulerHints") + + return map[string]interface{}{"server": b}, nil } // HandleServerNoNetworkCreationSuccessfully sets up the test server with no diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index f64cee4c54..446e31dbd2 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -629,6 +629,130 @@ func TestCreateServerWithBFVAttachExistingVolumeWithTag(t *testing.T) { th.CheckJSONEquals(t, ExpectedImageAndExistingVolumeWithTagRequest, actual) } +func TestCreateServerWithSchedulerHints(t *testing.T) { + schedulerHints := servers.SchedulerHints{ + Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba", + DifferentHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + SameHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + Query: []interface{}{"=", "$free_ram_mb", "1024"}, + TargetCell: "foobar", + DifferentCell: []string{ + "bazbar", + "barbaz", + }, + BuildNearHostIP: "192.168.1.1/24", + AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + } + + opts := servers.CreateOpts{ + Name: "createdserver", + ImageRef: "asdfasdfasdf", + FlavorRef: "performance1-1", + SchedulerHints: schedulerHints, + } + + expected := ` + { + "server": { + "name": "createdserver", + "imageRef": "asdfasdfasdf", + "flavorRef": "performance1-1" + }, + "os:scheduler_hints": { + "group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba", + "different_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "same_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "query": "[\"=\",\"$free_ram_mb\",\"1024\"]", + "target_cell": "foobar", + "different_cell": [ + "bazbar", + "barbaz" + ], + "build_near_host_ip": "192.168.1.1", + "cidr": "/24", + "reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" + } + } + ` + actual, err := opts.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + +func TestCreateServerWithComplexSchedulerHints(t *testing.T) { + schedulerHints := servers.SchedulerHints{ + Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba", + DifferentHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + SameHost: []string{ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287", + }, + Query: []interface{}{"and", []string{"=", "$free_ram_mb", "1024"}, []string{"=", "$free_disk_mb", "204800"}}, + TargetCell: "foobar", + DifferentCell: []string{ + "bazbar", + "barbaz", + }, + BuildNearHostIP: "192.168.1.1/24", + AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + } + + opts := servers.CreateOpts{ + Name: "createdserver", + ImageRef: "asdfasdfasdf", + FlavorRef: "performance1-1", + SchedulerHints: schedulerHints, + } + + expected := ` + { + "server": { + "name": "createdserver", + "imageRef": "asdfasdfasdf", + "flavorRef": "performance1-1" + }, + "os:scheduler_hints": { + "group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba", + "different_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "same_host": [ + "a0cf03a5-d921-4877-bb5c-86d26cf818e1", + "8c19174f-4220-44f0-824a-cd1eeef10287" + ], + "query": "[\"and\",[\"=\",\"$free_ram_mb\",\"1024\"],[\"=\",\"$free_disk_mb\",\"204800\"]]", + "target_cell": "foobar", + "different_cell": [ + "bazbar", + "barbaz" + ], + "build_near_host_ip": "192.168.1.1", + "cidr": "/24", + "reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1" + } + } + ` + actual, err := opts.ToServerCreateMap() + th.AssertNoErr(t, err) + th.CheckJSONEquals(t, expected, actual) +} + func TestDeleteServer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From a1ac2897bcf5af870cc23ee8b03e42b86afb6c28 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 27 Feb 2024 13:21:37 +0000 Subject: [PATCH 1817/2296] identity: Merge roles v2 extension Signed-off-by: Stephen Finucane --- internal/acceptance/openstack/identity/v2/identity.go | 2 +- internal/acceptance/openstack/identity/v2/role_test.go | 2 +- openstack/identity/v2/{extensions/admin => }/roles/doc.go | 0 openstack/identity/v2/{extensions/admin => }/roles/requests.go | 0 openstack/identity/v2/{extensions/admin => }/roles/results.go | 0 .../identity/v2/{extensions/admin => }/roles/testing/doc.go | 0 .../v2/{extensions/admin => }/roles/testing/fixtures_test.go | 0 .../v2/{extensions/admin => }/roles/testing/requests_test.go | 2 +- openstack/identity/v2/{extensions/admin => }/roles/urls.go | 0 9 files changed, 3 insertions(+), 3 deletions(-) rename openstack/identity/v2/{extensions/admin => }/roles/doc.go (100%) rename openstack/identity/v2/{extensions/admin => }/roles/requests.go (100%) rename openstack/identity/v2/{extensions/admin => }/roles/results.go (100%) rename openstack/identity/v2/{extensions/admin => }/roles/testing/doc.go (100%) rename openstack/identity/v2/{extensions/admin => }/roles/testing/fixtures_test.go (100%) rename openstack/identity/v2/{extensions/admin => }/roles/testing/requests_test.go (94%) rename openstack/identity/v2/{extensions/admin => }/roles/urls.go (100%) diff --git a/internal/acceptance/openstack/identity/v2/identity.go b/internal/acceptance/openstack/identity/v2/identity.go index 89e946fc3f..032a01a72f 100644 --- a/internal/acceptance/openstack/identity/v2/identity.go +++ b/internal/acceptance/openstack/identity/v2/identity.go @@ -8,7 +8,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions/admin/roles" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/roles" "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tenants" "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/users" th "github.com/gophercloud/gophercloud/v2/testhelper" diff --git a/internal/acceptance/openstack/identity/v2/role_test.go b/internal/acceptance/openstack/identity/v2/role_test.go index 667c3addff..8d98b8b520 100644 --- a/internal/acceptance/openstack/identity/v2/role_test.go +++ b/internal/acceptance/openstack/identity/v2/role_test.go @@ -9,7 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions/admin/roles" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/roles" "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/users" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/openstack/identity/v2/extensions/admin/roles/doc.go b/openstack/identity/v2/roles/doc.go similarity index 100% rename from openstack/identity/v2/extensions/admin/roles/doc.go rename to openstack/identity/v2/roles/doc.go diff --git a/openstack/identity/v2/extensions/admin/roles/requests.go b/openstack/identity/v2/roles/requests.go similarity index 100% rename from openstack/identity/v2/extensions/admin/roles/requests.go rename to openstack/identity/v2/roles/requests.go diff --git a/openstack/identity/v2/extensions/admin/roles/results.go b/openstack/identity/v2/roles/results.go similarity index 100% rename from openstack/identity/v2/extensions/admin/roles/results.go rename to openstack/identity/v2/roles/results.go diff --git a/openstack/identity/v2/extensions/admin/roles/testing/doc.go b/openstack/identity/v2/roles/testing/doc.go similarity index 100% rename from openstack/identity/v2/extensions/admin/roles/testing/doc.go rename to openstack/identity/v2/roles/testing/doc.go diff --git a/openstack/identity/v2/extensions/admin/roles/testing/fixtures_test.go b/openstack/identity/v2/roles/testing/fixtures_test.go similarity index 100% rename from openstack/identity/v2/extensions/admin/roles/testing/fixtures_test.go rename to openstack/identity/v2/roles/testing/fixtures_test.go diff --git a/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go b/openstack/identity/v2/roles/testing/requests_test.go similarity index 94% rename from openstack/identity/v2/extensions/admin/roles/testing/requests_test.go rename to openstack/identity/v2/roles/testing/requests_test.go index ce14915671..a75f362208 100644 --- a/openstack/identity/v2/extensions/admin/roles/testing/requests_test.go +++ b/openstack/identity/v2/roles/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/extensions/admin/roles" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/roles" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/identity/v2/extensions/admin/roles/urls.go b/openstack/identity/v2/roles/urls.go similarity index 100% rename from openstack/identity/v2/extensions/admin/roles/urls.go rename to openstack/identity/v2/roles/urls.go From c5a3ed1a6dc1c6a32fe0be88e4687d2912edd245 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 27 Feb 2024 13:23:56 +0000 Subject: [PATCH 1818/2296] identity: Rename URL const This URL is for user tenant roles, not user roles. This can be confirmed by looking at 'keystone/v2_crud/admin_crud.py' at the 'pike-eol' tag [1]. [1] https://github.com/openstack/keystone/blob/pike-eol/keystone/v2_crud/admin_crud.py#L169-L179 Signed-off-by: Stephen Finucane --- openstack/identity/v2/roles/requests.go | 4 ++-- openstack/identity/v2/roles/urls.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/identity/v2/roles/requests.go b/openstack/identity/v2/roles/requests.go index c5c1140c16..2659b4d4d5 100644 --- a/openstack/identity/v2/roles/requests.go +++ b/openstack/identity/v2/roles/requests.go @@ -19,7 +19,7 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // a user. This is confined to the scope of the user's tenant - so the tenant // ID is a required argument. func AddUser(ctx context.Context, client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { - resp, err := client.Put(ctx, userRoleURL(client, tenantID, userID, roleID), nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(ctx, userTenantRoleURL(client, tenantID, userID, roleID), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -30,7 +30,7 @@ func AddUser(ctx context.Context, client *gophercloud.ServiceClient, tenantID, u // from a user. This is confined to the scope of the user's tenant - so the // tenant ID is a required argument. func DeleteUser(ctx context.Context, client *gophercloud.ServiceClient, tenantID, userID, roleID string) (r UserRoleResult) { - resp, err := client.Delete(ctx, userRoleURL(client, tenantID, userID, roleID), nil) + resp, err := client.Delete(ctx, userTenantRoleURL(client, tenantID, userID, roleID), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/identity/v2/roles/urls.go b/openstack/identity/v2/roles/urls.go index f662ed87ac..a07e7b2e14 100644 --- a/openstack/identity/v2/roles/urls.go +++ b/openstack/identity/v2/roles/urls.go @@ -16,6 +16,6 @@ func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(ExtPath, RolePath) } -func userRoleURL(c *gophercloud.ServiceClient, tenantID, userID, roleID string) string { +func userTenantRoleURL(c *gophercloud.ServiceClient, tenantID, userID, roleID string) string { return c.ServiceURL("tenants", tenantID, UserPath, userID, RolePath, ExtPath, roleID) } From 882ba2ab260181e534a07e4107369adc44dab574 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 27 Feb 2024 13:27:28 +0000 Subject: [PATCH 1819/2296] identity: Restructure remaining v2 extensions code Model things after how we do it for all other modules. Signed-off-by: Stephen Finucane --- openstack/identity/v2/extensions/requests.go | 24 +++++++++++++++++++ .../v2/extensions/{delegate.go => results.go} | 18 -------------- .../{delegate_test.go => requests_test.go} | 0 3 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 openstack/identity/v2/extensions/requests.go rename openstack/identity/v2/extensions/{delegate.go => results.go} (58%) rename openstack/identity/v2/extensions/testing/{delegate_test.go => requests_test.go} (100%) diff --git a/openstack/identity/v2/extensions/requests.go b/openstack/identity/v2/extensions/requests.go new file mode 100644 index 0000000000..f01d065852 --- /dev/null +++ b/openstack/identity/v2/extensions/requests.go @@ -0,0 +1,24 @@ +package extensions + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// Get retrieves information for a specific extension using its alias. +func Get(ctx context.Context, c *gophercloud.ServiceClient, alias string) common.GetResult { + return common.Get(ctx, c, alias) +} + +// List returns a Pager which allows you to iterate over the full collection of extensions. +// It does not accept query parameters. +func List(c *gophercloud.ServiceClient) pagination.Pager { + return common.List(c).WithPageCreator(func(r pagination.PageResult) pagination.Page { + return ExtensionPage{ + ExtensionPage: common.ExtensionPage{SinglePageBase: pagination.SinglePageBase(r)}, + } + }) +} diff --git a/openstack/identity/v2/extensions/delegate.go b/openstack/identity/v2/extensions/results.go similarity index 58% rename from openstack/identity/v2/extensions/delegate.go rename to openstack/identity/v2/extensions/results.go index e918cf6a4d..6a480e4e77 100644 --- a/openstack/identity/v2/extensions/delegate.go +++ b/openstack/identity/v2/extensions/results.go @@ -1,9 +1,6 @@ package extensions import ( - "context" - - "github.com/gophercloud/gophercloud/v2" common "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -31,18 +28,3 @@ func ExtractExtensions(page pagination.Page) ([]common.Extension, error) { err := page.(ExtensionPage).ExtractInto(&s) return s.Extensions.Values, err } - -// Get retrieves information for a specific extension using its alias. -func Get(ctx context.Context, c *gophercloud.ServiceClient, alias string) common.GetResult { - return common.Get(ctx, c, alias) -} - -// List returns a Pager which allows you to iterate over the full collection of extensions. -// It does not accept query parameters. -func List(c *gophercloud.ServiceClient) pagination.Pager { - return common.List(c).WithPageCreator(func(r pagination.PageResult) pagination.Page { - return ExtensionPage{ - ExtensionPage: common.ExtensionPage{SinglePageBase: pagination.SinglePageBase(r)}, - } - }) -} diff --git a/openstack/identity/v2/extensions/testing/delegate_test.go b/openstack/identity/v2/extensions/testing/requests_test.go similarity index 100% rename from openstack/identity/v2/extensions/testing/delegate_test.go rename to openstack/identity/v2/extensions/testing/requests_test.go From d64bb4f8d508121feda4d2d9669efe730ab325b4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 27 Feb 2024 13:32:42 +0000 Subject: [PATCH 1820/2296] identity: Merge standalone v3 extensions Extensions are no longer a thing in any OpenStack service except Neutron. As such, maintaining what were formerly extensions in a separate module makes little sense and actively hurts us as these extensions are themselves extended with new microversions. It's the identity services turn now and we do as we've done for blockstorage and compute previously, moving these extensions up a level to the root 'openstack/identity/v3' module, updating references in the process. Signed-off-by: Stephen Finucane --- internal/acceptance/openstack/client_test.go | 2 +- internal/acceptance/openstack/identity/v3/credentials_test.go | 2 +- .../acceptance/openstack/identity/v3/ec2credentials_test.go | 2 +- internal/acceptance/openstack/identity/v3/federation_test.go | 2 +- internal/acceptance/openstack/identity/v3/oauth1_test.go | 2 +- .../acceptance/openstack/identity/v3/projectendpoint_test.go | 2 +- openstack/client.go | 4 ++-- openstack/identity/v3/{extensions => }/ec2credentials/doc.go | 0 .../identity/v3/{extensions => }/ec2credentials/requests.go | 0 .../identity/v3/{extensions => }/ec2credentials/results.go | 0 .../{extensions => }/ec2credentials/testing/fixtures_test.go | 2 +- .../{extensions => }/ec2credentials/testing/requests_test.go | 2 +- openstack/identity/v3/{extensions => }/ec2credentials/urls.go | 0 openstack/identity/v3/{extensions => }/ec2tokens/doc.go | 0 openstack/identity/v3/{extensions => }/ec2tokens/requests.go | 0 .../v3/{extensions => }/ec2tokens/testing/requests_test.go | 2 +- openstack/identity/v3/{extensions => }/ec2tokens/urls.go | 0 openstack/identity/v3/{extensions => }/federation/doc.go | 0 openstack/identity/v3/{extensions => }/federation/requests.go | 0 openstack/identity/v3/{extensions => }/federation/results.go | 0 .../v3/{extensions => }/federation/testing/fixtures_test.go | 2 +- .../v3/{extensions => }/federation/testing/requests_test.go | 2 +- openstack/identity/v3/{extensions => }/federation/urls.go | 0 openstack/identity/v3/{extensions => }/oauth1/doc.go | 0 openstack/identity/v3/{extensions => }/oauth1/requests.go | 0 openstack/identity/v3/{extensions => }/oauth1/results.go | 0 .../v3/{extensions => }/oauth1/testing/fixtures_test.go | 2 +- .../v3/{extensions => }/oauth1/testing/requests_test.go | 2 +- openstack/identity/v3/{extensions => }/oauth1/urls.go | 0 .../identity/v3/{extensions => }/projectendpoints/doc.go | 0 .../identity/v3/{extensions => }/projectendpoints/requests.go | 0 .../identity/v3/{extensions => }/projectendpoints/results.go | 0 .../v3/{extensions => }/projectendpoints/testing/doc.go | 0 .../projectendpoints/testing/requests_test.go | 2 +- .../identity/v3/{extensions => }/projectendpoints/urls.go | 0 35 files changed, 16 insertions(+), 16 deletions(-) rename openstack/identity/v3/{extensions => }/ec2credentials/doc.go (100%) rename openstack/identity/v3/{extensions => }/ec2credentials/requests.go (100%) rename openstack/identity/v3/{extensions => }/ec2credentials/results.go (100%) rename openstack/identity/v3/{extensions => }/ec2credentials/testing/fixtures_test.go (98%) rename openstack/identity/v3/{extensions => }/ec2credentials/testing/requests_test.go (95%) rename openstack/identity/v3/{extensions => }/ec2credentials/urls.go (100%) rename openstack/identity/v3/{extensions => }/ec2tokens/doc.go (100%) rename openstack/identity/v3/{extensions => }/ec2tokens/requests.go (100%) rename openstack/identity/v3/{extensions => }/ec2tokens/testing/requests_test.go (99%) rename openstack/identity/v3/{extensions => }/ec2tokens/urls.go (100%) rename openstack/identity/v3/{extensions => }/federation/doc.go (100%) rename openstack/identity/v3/{extensions => }/federation/requests.go (100%) rename openstack/identity/v3/{extensions => }/federation/results.go (100%) rename openstack/identity/v3/{extensions => }/federation/testing/fixtures_test.go (99%) rename openstack/identity/v3/{extensions => }/federation/testing/requests_test.go (97%) rename openstack/identity/v3/{extensions => }/federation/urls.go (100%) rename openstack/identity/v3/{extensions => }/oauth1/doc.go (100%) rename openstack/identity/v3/{extensions => }/oauth1/requests.go (100%) rename openstack/identity/v3/{extensions => }/oauth1/results.go (100%) rename openstack/identity/v3/{extensions => }/oauth1/testing/fixtures_test.go (99%) rename openstack/identity/v3/{extensions => }/oauth1/testing/requests_test.go (98%) rename openstack/identity/v3/{extensions => }/oauth1/urls.go (100%) rename openstack/identity/v3/{extensions => }/projectendpoints/doc.go (100%) rename openstack/identity/v3/{extensions => }/projectendpoints/requests.go (100%) rename openstack/identity/v3/{extensions => }/projectendpoints/results.go (100%) rename openstack/identity/v3/{extensions => }/projectendpoints/testing/doc.go (100%) rename openstack/identity/v3/{extensions => }/projectendpoints/testing/requests_test.go (97%) rename openstack/identity/v3/{extensions => }/projectendpoints/urls.go (100%) diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index f44df3cd42..efdbafd25c 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -14,7 +14,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/credentials" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/identity/v3/credentials_test.go b/internal/acceptance/openstack/identity/v3/credentials_test.go index 00bc2052e8..a93a1a62aa 100644 --- a/internal/acceptance/openstack/identity/v3/credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/credentials_test.go @@ -11,7 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/credentials" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go index 69c3877374..21e3a6a2b5 100644 --- a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2credentials" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2credentials" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/identity/v3/federation_test.go b/internal/acceptance/openstack/identity/v3/federation_test.go index cc964c20c0..6e7db6846d 100644 --- a/internal/acceptance/openstack/identity/v3/federation_test.go +++ b/internal/acceptance/openstack/identity/v3/federation_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/federation" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/federation" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index a88ac39cc0..632a6e29cf 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -11,7 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/oauth1" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go index 1da742cc8b..4150a538d3 100644 --- a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go @@ -10,7 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/endpoints" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/projectendpoints" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projectendpoints" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/openstack/client.go b/openstack/client.go index 4fe07da4c4..6fe0b3f215 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -8,8 +8,8 @@ import ( "github.com/gophercloud/gophercloud/v2" tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2tokens" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/oauth1" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1" tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/v2/openstack/utils" ) diff --git a/openstack/identity/v3/extensions/ec2credentials/doc.go b/openstack/identity/v3/ec2credentials/doc.go similarity index 100% rename from openstack/identity/v3/extensions/ec2credentials/doc.go rename to openstack/identity/v3/ec2credentials/doc.go diff --git a/openstack/identity/v3/extensions/ec2credentials/requests.go b/openstack/identity/v3/ec2credentials/requests.go similarity index 100% rename from openstack/identity/v3/extensions/ec2credentials/requests.go rename to openstack/identity/v3/ec2credentials/requests.go diff --git a/openstack/identity/v3/extensions/ec2credentials/results.go b/openstack/identity/v3/ec2credentials/results.go similarity index 100% rename from openstack/identity/v3/extensions/ec2credentials/results.go rename to openstack/identity/v3/ec2credentials/results.go diff --git a/openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go b/openstack/identity/v3/ec2credentials/testing/fixtures_test.go similarity index 98% rename from openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go rename to openstack/identity/v3/ec2credentials/testing/fixtures_test.go index c497b6e4b7..cdb25dbc78 100644 --- a/openstack/identity/v3/extensions/ec2credentials/testing/fixtures_test.go +++ b/openstack/identity/v3/ec2credentials/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2credentials" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2credentials" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go b/openstack/identity/v3/ec2credentials/testing/requests_test.go similarity index 95% rename from openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go rename to openstack/identity/v3/ec2credentials/testing/requests_test.go index a5e35c8ed6..a20e4b70f9 100644 --- a/openstack/identity/v3/extensions/ec2credentials/testing/requests_test.go +++ b/openstack/identity/v3/ec2credentials/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2credentials" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2credentials" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/identity/v3/extensions/ec2credentials/urls.go b/openstack/identity/v3/ec2credentials/urls.go similarity index 100% rename from openstack/identity/v3/extensions/ec2credentials/urls.go rename to openstack/identity/v3/ec2credentials/urls.go diff --git a/openstack/identity/v3/extensions/ec2tokens/doc.go b/openstack/identity/v3/ec2tokens/doc.go similarity index 100% rename from openstack/identity/v3/extensions/ec2tokens/doc.go rename to openstack/identity/v3/ec2tokens/doc.go diff --git a/openstack/identity/v3/extensions/ec2tokens/requests.go b/openstack/identity/v3/ec2tokens/requests.go similarity index 100% rename from openstack/identity/v3/extensions/ec2tokens/requests.go rename to openstack/identity/v3/ec2tokens/requests.go diff --git a/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go b/openstack/identity/v3/ec2tokens/testing/requests_test.go similarity index 99% rename from openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go rename to openstack/identity/v3/ec2tokens/testing/requests_test.go index 4c502d7628..188329daab 100644 --- a/openstack/identity/v3/extensions/ec2tokens/testing/requests_test.go +++ b/openstack/identity/v3/ec2tokens/testing/requests_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" tokens_testing "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/testing" "github.com/gophercloud/gophercloud/v2/testhelper" diff --git a/openstack/identity/v3/extensions/ec2tokens/urls.go b/openstack/identity/v3/ec2tokens/urls.go similarity index 100% rename from openstack/identity/v3/extensions/ec2tokens/urls.go rename to openstack/identity/v3/ec2tokens/urls.go diff --git a/openstack/identity/v3/extensions/federation/doc.go b/openstack/identity/v3/federation/doc.go similarity index 100% rename from openstack/identity/v3/extensions/federation/doc.go rename to openstack/identity/v3/federation/doc.go diff --git a/openstack/identity/v3/extensions/federation/requests.go b/openstack/identity/v3/federation/requests.go similarity index 100% rename from openstack/identity/v3/extensions/federation/requests.go rename to openstack/identity/v3/federation/requests.go diff --git a/openstack/identity/v3/extensions/federation/results.go b/openstack/identity/v3/federation/results.go similarity index 100% rename from openstack/identity/v3/extensions/federation/results.go rename to openstack/identity/v3/federation/results.go diff --git a/openstack/identity/v3/extensions/federation/testing/fixtures_test.go b/openstack/identity/v3/federation/testing/fixtures_test.go similarity index 99% rename from openstack/identity/v3/extensions/federation/testing/fixtures_test.go rename to openstack/identity/v3/federation/testing/fixtures_test.go index 8a1fd42dce..2dbdaa9927 100644 --- a/openstack/identity/v3/extensions/federation/testing/fixtures_test.go +++ b/openstack/identity/v3/federation/testing/fixtures_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/federation" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/federation" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/identity/v3/extensions/federation/testing/requests_test.go b/openstack/identity/v3/federation/testing/requests_test.go similarity index 97% rename from openstack/identity/v3/extensions/federation/testing/requests_test.go rename to openstack/identity/v3/federation/testing/requests_test.go index fe62d8e658..0d156b8d19 100644 --- a/openstack/identity/v3/extensions/federation/testing/requests_test.go +++ b/openstack/identity/v3/federation/testing/requests_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/federation" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/federation" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/identity/v3/extensions/federation/urls.go b/openstack/identity/v3/federation/urls.go similarity index 100% rename from openstack/identity/v3/extensions/federation/urls.go rename to openstack/identity/v3/federation/urls.go diff --git a/openstack/identity/v3/extensions/oauth1/doc.go b/openstack/identity/v3/oauth1/doc.go similarity index 100% rename from openstack/identity/v3/extensions/oauth1/doc.go rename to openstack/identity/v3/oauth1/doc.go diff --git a/openstack/identity/v3/extensions/oauth1/requests.go b/openstack/identity/v3/oauth1/requests.go similarity index 100% rename from openstack/identity/v3/extensions/oauth1/requests.go rename to openstack/identity/v3/oauth1/requests.go diff --git a/openstack/identity/v3/extensions/oauth1/results.go b/openstack/identity/v3/oauth1/results.go similarity index 100% rename from openstack/identity/v3/extensions/oauth1/results.go rename to openstack/identity/v3/oauth1/results.go diff --git a/openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go b/openstack/identity/v3/oauth1/testing/fixtures_test.go similarity index 99% rename from openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go rename to openstack/identity/v3/oauth1/testing/fixtures_test.go index 18dfe05934..8a2bfe5cdb 100644 --- a/openstack/identity/v3/extensions/oauth1/testing/fixtures_test.go +++ b/openstack/identity/v3/oauth1/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/oauth1" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1" tokens "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/testing" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/identity/v3/extensions/oauth1/testing/requests_test.go b/openstack/identity/v3/oauth1/testing/requests_test.go similarity index 98% rename from openstack/identity/v3/extensions/oauth1/testing/requests_test.go rename to openstack/identity/v3/oauth1/testing/requests_test.go index 4c78cc2461..75f37bca0f 100644 --- a/openstack/identity/v3/extensions/oauth1/testing/requests_test.go +++ b/openstack/identity/v3/oauth1/testing/requests_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/oauth1" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" diff --git a/openstack/identity/v3/extensions/oauth1/urls.go b/openstack/identity/v3/oauth1/urls.go similarity index 100% rename from openstack/identity/v3/extensions/oauth1/urls.go rename to openstack/identity/v3/oauth1/urls.go diff --git a/openstack/identity/v3/extensions/projectendpoints/doc.go b/openstack/identity/v3/projectendpoints/doc.go similarity index 100% rename from openstack/identity/v3/extensions/projectendpoints/doc.go rename to openstack/identity/v3/projectendpoints/doc.go diff --git a/openstack/identity/v3/extensions/projectendpoints/requests.go b/openstack/identity/v3/projectendpoints/requests.go similarity index 100% rename from openstack/identity/v3/extensions/projectendpoints/requests.go rename to openstack/identity/v3/projectendpoints/requests.go diff --git a/openstack/identity/v3/extensions/projectendpoints/results.go b/openstack/identity/v3/projectendpoints/results.go similarity index 100% rename from openstack/identity/v3/extensions/projectendpoints/results.go rename to openstack/identity/v3/projectendpoints/results.go diff --git a/openstack/identity/v3/extensions/projectendpoints/testing/doc.go b/openstack/identity/v3/projectendpoints/testing/doc.go similarity index 100% rename from openstack/identity/v3/extensions/projectendpoints/testing/doc.go rename to openstack/identity/v3/projectendpoints/testing/doc.go diff --git a/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go b/openstack/identity/v3/projectendpoints/testing/requests_test.go similarity index 97% rename from openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go rename to openstack/identity/v3/projectendpoints/testing/requests_test.go index 9833e01643..8df70d5a76 100644 --- a/openstack/identity/v3/extensions/projectendpoints/testing/requests_test.go +++ b/openstack/identity/v3/projectendpoints/testing/requests_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/projectendpoints" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projectendpoints" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/identity/v3/extensions/projectendpoints/urls.go b/openstack/identity/v3/projectendpoints/urls.go similarity index 100% rename from openstack/identity/v3/extensions/projectendpoints/urls.go rename to openstack/identity/v3/projectendpoints/urls.go From 8d85d0dcb4f5ec62a9470db842719a75f5c44c1f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 28 Feb 2024 12:43:32 +0000 Subject: [PATCH 1821/2296] identity: Add unit tests for AuthOptions.ToTokenV3ScopeMap We'll use these as we conduct some surgery shortly. Signed-off-by: Stephen Finucane --- testing/auth_options_test.go | 191 +++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 testing/auth_options_test.go diff --git a/testing/auth_options_test.go b/testing/auth_options_test.go new file mode 100644 index 0000000000..4406d6bfee --- /dev/null +++ b/testing/auth_options_test.go @@ -0,0 +1,191 @@ +package testing + +import ( + "reflect" + "testing" + + "github.com/gophercloud/gophercloud/v2" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestToTokenV3ScopeMap(t *testing.T) { + projectID := "685038cd-3c25-4faf-8f9b-78c18e503190" + projectName := "admin" + domainID := "e4b515b8-e453-49d8-9cce-4bec244fa84e" + domainName := "Default" + + var successCases = []struct { + opts gophercloud.AuthOptions + expected map[string]interface{} + }{ + // System-scoped + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + System: true, + }, + }, + map[string]interface{}{ + "system": map[string]interface{}{ + "all": true, + }, + }, + }, + // Project-scoped (ID) + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + ProjectID: projectID, + }, + }, + map[string]interface{}{ + "project": map[string]interface{}{ + "id": &projectID, + }, + }, + }, + // Project-scoped (name) + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + ProjectName: projectName, + DomainName: domainName, + }, + }, + map[string]interface{}{ + "project": map[string]interface{}{ + "name": &projectName, + "domain": map[string]interface{}{ + "name": &domainName, + }, + }, + }, + }, + // Domain-scoped (ID) + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + DomainID: domainID, + }, + }, + map[string]interface{}{ + "domain": map[string]interface{}{ + "id": &domainID, + }, + }, + }, + // Domain-scoped (name) + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + DomainName: domainName, + }, + }, + map[string]interface{}{ + "domain": map[string]interface{}{ + "name": &domainName, + }, + }, + }, + // Empty with project fallback (ID) + { + gophercloud.AuthOptions{ + TenantID: projectID, + Scope: nil, + }, + map[string]interface{}{ + "project": map[string]interface{}{ + "id": &projectID, + }, + }, + }, + // Empty with project fallback (name) + { + gophercloud.AuthOptions{ + TenantName: projectName, + DomainName: domainName, + Scope: nil, + }, + map[string]interface{}{ + "project": map[string]interface{}{ + "name": &projectName, + "domain": map[string]interface{}{ + "name": &domainName, + }, + }, + }, + }, + // Empty without fallback + { + gophercloud.AuthOptions{ + Scope: nil, + }, + nil, + }, + } + for _, successCase := range successCases { + actual, err := successCase.opts.ToTokenV3ScopeMap() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, successCase.expected, actual) + } + + var failCases = []struct { + opts gophercloud.AuthOptions + expected error + }{ + // Project-scoped with name but missing domain ID/name + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + ProjectName: "admin", + }, + }, + gophercloud.ErrScopeDomainIDOrDomainName{}, + }, + // Project-scoped with both project name and project ID + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + ProjectName: "admin", + ProjectID: "685038cd-3c25-4faf-8f9b-78c18e503190", + DomainName: "Default", + }, + }, + gophercloud.ErrScopeProjectIDOrProjectName{}, + }, + // Project-scoped with name and unnecessary domain ID + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + ProjectID: "685038cd-3c25-4faf-8f9b-78c18e503190", + DomainID: "e4b515b8-e453-49d8-9cce-4bec244fa84e", + }, + }, + gophercloud.ErrScopeProjectIDAlone{}, + }, + // Project-scoped with name and unnecessary domain name + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + ProjectID: "685038cd-3c25-4faf-8f9b-78c18e503190", + DomainName: "Default", + }, + }, + gophercloud.ErrScopeProjectIDAlone{}, + }, + // Domain-scoped with both domain name and domain ID + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + DomainID: "e4b515b8-e453-49d8-9cce-4bec244fa84e", + DomainName: "Default", + }, + }, + gophercloud.ErrScopeDomainIDOrDomainName{}, + }, + } + for _, failCase := range failCases { + _, err := failCase.opts.ToTokenV3ScopeMap() + th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err)) + } +} From e168bf63d70067b834b5a37ec19896d2478ae30b Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 28 Feb 2024 12:44:32 +0000 Subject: [PATCH 1822/2296] identity: Merge trusts extension (1/2) This is part 1 of 2. In this part, we fold in the 'TrustID' parameter, unflattening it in the process. Signed-off-by: Stephen Finucane --- auth_options.go | 9 ++ .../identity/v3/extensions/trusts/doc.go | 22 --- .../identity/v3/extensions/trusts/requests.go | 37 ----- .../trusts/testing/fixtures_test.go | 58 ------- .../trusts/testing/requests_test.go | 58 ------- openstack/identity/v3/tokens/requests.go | 1 + openstack/identity/v3/tokens/results.go | 21 +++ .../v3/tokens/testing/requests_test.go | 145 +++++++++++++++--- testing/auth_options_test.go | 13 ++ 9 files changed, 170 insertions(+), 194 deletions(-) diff --git a/auth_options.go b/auth_options.go index c13cecdc9f..9d52c07d73 100644 --- a/auth_options.go +++ b/auth_options.go @@ -102,6 +102,7 @@ type AuthScope struct { DomainID string DomainName string System bool + TrustID string } // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder @@ -430,6 +431,14 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { }, nil } + if opts.Scope.TrustID != "" { + return map[string]interface{}{ + "OS-TRUST:trust": map[string]string{ + "id": opts.Scope.TrustID, + }, + }, nil + } + if opts.Scope.ProjectName != "" { // ProjectName provided: either DomainID or DomainName must also be supplied. // ProjectID may not be supplied. diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/extensions/trusts/doc.go index 6c0bc7da6e..78ca456a41 100644 --- a/openstack/identity/v3/extensions/trusts/doc.go +++ b/openstack/identity/v3/extensions/trusts/doc.go @@ -1,28 +1,6 @@ /* Package trusts enables management of OpenStack Identity Trusts. -Example to Create a Token with Username, Password, and Trust ID - - var trustToken struct { - tokens.Token - trusts.TokenExt - } - - authOptions := tokens.AuthOptions{ - UserID: "username", - Password: "password", - } - - createOpts := trusts.AuthOptsExt{ - AuthOptionsBuilder: authOptions, - TrustID: "de0945a", - } - - err := tokens.Create(context.TODO(), identityClient, createOpts).ExtractInto(&trustToken) - if err != nil { - panic(err) - } - Example to Create a Trust expiresAt := time.Date(2019, 12, 1, 14, 0, 0, 999999999, time.UTC) diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/extensions/trusts/requests.go index e8d6b6441f..55ea9bfac3 100644 --- a/openstack/identity/v3/extensions/trusts/requests.go +++ b/openstack/identity/v3/extensions/trusts/requests.go @@ -5,46 +5,9 @@ import ( "time" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/v2/pagination" ) -// AuthOptsExt extends the base Identity v3 tokens AuthOpts with a TrustID. -type AuthOptsExt struct { - tokens.AuthOptionsBuilder - - // TrustID is the ID of the trust. - TrustID string `json:"id"` -} - -// ToTokenV3CreateMap builds a create request body from the AuthOpts. -func (opts AuthOptsExt) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { - return opts.AuthOptionsBuilder.ToTokenV3CreateMap(scope) -} - -// ToTokenV3ScopeMap builds a scope from AuthOpts. -func (opts AuthOptsExt) ToTokenV3ScopeMap() (map[string]interface{}, error) { - b, err := opts.AuthOptionsBuilder.ToTokenV3ScopeMap() - if err != nil { - return nil, err - } - - if opts.TrustID != "" { - if b == nil { - b = make(map[string]interface{}) - } - b["OS-TRUST:trust"] = map[string]interface{}{ - "id": opts.TrustID, - } - } - - return b, nil -} - -func (opts AuthOptsExt) CanReauth() bool { - return opts.AuthOptionsBuilder.CanReauth() -} - // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go b/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go index 6de29c1598..2809adc91e 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go @@ -7,7 +7,6 @@ import ( "time" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -199,63 +198,6 @@ var SecondRole = trusts.Role{ var ExpectedTrustRolesSlice = []trusts.Role{FirstRole, SecondRole} -// HandleCreateTokenWithTrustID verifies that providing certain AuthOptions and Scope results in an expected JSON structure. -func HandleCreateTokenWithTrustID(t *testing.T, options tokens.AuthOptionsBuilder, requestJSON string) { - testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "Content-Type", "application/json") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestJSONRequest(t, r, requestJSON) - - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ - "token": { - "expires_at": "2013-02-27T18:30:59.999999Z", - "issued_at": "2013-02-27T16:30:59.999999Z", - "methods": [ - "password" - ], - "OS-TRUST:trust": { - "id": "fe0aef", - "impersonation": false, - "redelegated_trust_id": "3ba234", - "redelegation_count": 2, - "links": { - "self": "http://example.com/identity/v3/trusts/fe0aef" - }, - "trustee_user": { - "id": "0ca8f6", - "links": { - "self": "http://example.com/identity/v3/users/0ca8f6" - } - }, - "trustor_user": { - "id": "bd263c", - "links": { - "self": "http://example.com/identity/v3/users/bd263c" - } - } - }, - "user": { - "domain": { - "id": "1789d1", - "links": { - "self": "http://example.com/identity/v3/domains/1789d1" - }, - "name": "example.com" - }, - "email": "joe@example.com", - "id": "0ca8f6", - "links": { - "self": "http://example.com/identity/v3/users/0ca8f6" - }, - "name": "Joe" - } - } -}`) - }) -} - // HandleCreateTrust creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that tests trust creation. func HandleCreateTrust(t *testing.T) { diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/extensions/trusts/testing/requests_test.go index d63184ebc2..8be7949369 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/extensions/trusts/testing/requests_test.go @@ -6,69 +6,11 @@ import ( "time" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) -func TestCreateUserIDPasswordTrustID(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - ao := trusts.AuthOptsExt{ - TrustID: "de0945a", - AuthOptionsBuilder: &tokens.AuthOptions{ - UserID: "me", - Password: "squirrel!", - }, - } - HandleCreateTokenWithTrustID(t, ao, ` - { - "auth": { - "identity": { - "methods": ["password"], - "password": { - "user": { "id": "me", "password": "squirrel!" } - } - }, - "scope": { - "OS-TRUST:trust": { - "id": "de0945a" - } - } - } - } - `) - - var actual struct { - tokens.Token - trusts.TokenExt - } - err := tokens.Create(context.TODO(), client.ServiceClient(), ao).ExtractInto(&actual) - if err != nil { - t.Errorf("Create returned an error: %v", err) - } - expected := struct { - tokens.Token - trusts.TokenExt - }{ - tokens.Token{ - ExpiresAt: time.Date(2013, 02, 27, 18, 30, 59, 999999000, time.UTC), - }, - trusts.TokenExt{ - Trust: trusts.Trust{ - ID: "fe0aef", - Impersonation: false, - RedelegatedTrustID: "3ba234", - RedelegationCount: 2, - }, - }, - } - - th.AssertDeepEquals(t, expected, actual) -} - func TestCreateTrust(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index 4a54ed1d3f..f7003cf3e1 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -13,6 +13,7 @@ type Scope struct { DomainID string DomainName string System bool + TrustID string } // AuthOptionsBuilder provides the ability for extensions to add additional diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index ea4900e910..c1a72207cb 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -76,6 +76,18 @@ type Project struct { Name string `json:"name"` } +type TrustUser struct { + ID string `json:"id"` +} + +// Trust provides information about trust with which User is authorized. +type Trust struct { + ID string `json:"id"` + Impersonation bool `json:"impersonation"` + TrusteeUserID TrustUser `json:"trustee_user"` + TrustorUserID TrustUser `json:"trustor_user"` +} + // commonResult is the response from a request. A commonResult has various // methods which can be used to extract different details about the result. type commonResult struct { @@ -160,6 +172,15 @@ func (r commonResult) ExtractDomain() (*Domain, error) { return s.Domain, err } +// ExtractTrust returns Trust to which User is authorized. +func (r commonResult) ExtractTrust() (*Trust, error) { + var s struct { + Trust *Trust `json:"OS-TRUST:trust"` + } + err := r.ExtractInto(&s) + return s.Trust, err +} + // CreateResult is the response from a Create request. Use ExtractToken() // to interpret it as a Token, or ExtractServiceCatalog() to interpret it // as a service catalog. diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index 240adf2950..9a9a59fc14 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. @@ -199,25 +200,25 @@ func TestCreateDomainNameScope(t *testing.T) { options := tokens.AuthOptions{UserID: "someuser", Password: "somepassword"} scope := &tokens.Scope{DomainName: "evil-plans"} authTokenPost(t, options, scope, ` - { - "auth": { - "identity": { - "methods": ["password"], - "password": { - "user": { - "id": "someuser", - "password": "somepassword" - } - } - }, - "scope": { - "domain": { - "name": "evil-plans" - } - } - } - } - `) + { + "auth": { + "identity": { + "methods": ["password"], + "password": { + "user": { + "id": "someuser", + "password": "somepassword" + } + } + }, + "scope": { + "domain": { + "name": "evil-plans" + } + } + } + } + `) } func TestCreateProjectNameAndDomainIDScope(t *testing.T) { @@ -301,6 +302,112 @@ func TestCreateSystemScope(t *testing.T) { `) } +func TestCreateUserIDPasswordTrustID(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + + requestJSON := `{ + "auth": { + "identity": { + "methods": ["password"], + "password": { + "user": { "id": "demo", "password": "squirrel!" } + } + }, + "scope": { + "OS-TRUST:trust": { + "id": "95946f9eef864fdc993079d8fe3e5747" + } + } + } + }` + responseJSON := `{ + "token": { + "OS-TRUST:trust": { + "id": "95946f9eef864fdc993079d8fe3e5747", + "impersonation": false, + "trustee_user": { + "id": "64f9caa2872b442c98d42a986ee3b37a" + }, + "trustor_user": { + "id": "c88693b7c81c408e9084ac1e51082bfb" + } + }, + "audit_ids": [ + "wwcoUZGPR6mCIIl-COn8Kg" + ], + "catalog": [], + "expires_at": "2024-02-28T12:10:39.000000Z", + "issued_at": "2024-02-28T11:10:39.000000Z", + "methods": [ + "password" + ], + "project": { + "domain": { + "id": "default", + "name": "Default" + }, + "id": "1fd93a4455c74d2ea94b929fc5f0e488", + "name": "admin" + }, + "roles": [], + "user": { + "domain": { + "id": "default", + "name": "Default" + }, + "id": "64f9caa2872b442c98d42a986ee3b37a", + "name": "demo", + "password_expires_at": null + } + } + }` + testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + testhelper.TestMethod(t, r, "POST") + testhelper.TestHeader(t, r, "Content-Type", "application/json") + testhelper.TestHeader(t, r, "Accept", "application/json") + testhelper.TestJSONRequest(t, r, requestJSON) + + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, responseJSON) + }) + + ao := gophercloud.AuthOptions{ + UserID: "demo", + Password: "squirrel!", + Scope: &gophercloud.AuthScope{ + TrustID: "95946f9eef864fdc993079d8fe3e5747", + }, + } + + rsp := tokens.Create(context.TODO(), client.ServiceClient(), &ao) + + token, err := rsp.Extract() + if err != nil { + t.Errorf("Create returned an error: %v", err) + } + expectedToken := &tokens.Token{ + ExpiresAt: time.Date(2024, 02, 28, 12, 10, 39, 0, time.UTC), + } + testhelper.AssertDeepEquals(t, expectedToken, token) + + trust, err := rsp.ExtractTrust() + if err != nil { + t.Errorf("ExtractTrust returned an error: %v", err) + } + expectedTrust := &tokens.Trust{ + ID: "95946f9eef864fdc993079d8fe3e5747", + Impersonation: false, + TrusteeUserID: tokens.TrustUser{ + ID: "64f9caa2872b442c98d42a986ee3b37a", + }, + TrustorUserID: tokens.TrustUser{ + ID: "c88693b7c81c408e9084ac1e51082bfb", + }, + } + testhelper.AssertDeepEquals(t, expectedTrust, trust) +} + func TestCreateApplicationCredentialIDAndSecret(t *testing.T) { authTokenPost(t, tokens.AuthOptions{ApplicationCredentialID: "12345abcdef", ApplicationCredentialSecret: "mysecret"}, nil, ` { diff --git a/testing/auth_options_test.go b/testing/auth_options_test.go index 4406d6bfee..ed402aef05 100644 --- a/testing/auth_options_test.go +++ b/testing/auth_options_test.go @@ -31,6 +31,19 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, + // Trust-scoped + { + gophercloud.AuthOptions{ + Scope: &gophercloud.AuthScope{ + TrustID: "05144328-1f7d-46a9-a978-17eaad187077", + }, + }, + map[string]interface{}{ + "OS-TRUST:trust": map[string]string{ + "id": "05144328-1f7d-46a9-a978-17eaad187077", + }, + }, + }, // Project-scoped (ID) { gophercloud.AuthOptions{ From 5d2ddbad766dd9029e94d43a7a607eb98a9ab4bf Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 28 Feb 2024 13:01:21 +0000 Subject: [PATCH 1823/2296] identity: Merge trusts extension (2/2) This is part 2 of 2. In this part, we move the wrappers around the CRUD APIs for interacting with trusts themselves rather than authenticating with one. This a straightforward procedure. Signed-off-by: Stephen Finucane --- internal/acceptance/openstack/identity/v3/identity.go | 2 +- internal/acceptance/openstack/identity/v3/trusts_test.go | 2 +- openstack/identity/v3/{extensions => }/trusts/doc.go | 0 openstack/identity/v3/{extensions => }/trusts/requests.go | 0 openstack/identity/v3/{extensions => }/trusts/results.go | 0 openstack/identity/v3/{extensions => }/trusts/testing/doc.go | 0 .../v3/{extensions => }/trusts/testing/fixtures_test.go | 2 +- .../v3/{extensions => }/trusts/testing/requests_test.go | 2 +- openstack/identity/v3/{extensions => }/trusts/urls.go | 0 9 files changed, 4 insertions(+), 4 deletions(-) rename openstack/identity/v3/{extensions => }/trusts/doc.go (100%) rename openstack/identity/v3/{extensions => }/trusts/requests.go (100%) rename openstack/identity/v3/{extensions => }/trusts/results.go (100%) rename openstack/identity/v3/{extensions => }/trusts/testing/doc.go (100%) rename openstack/identity/v3/{extensions => }/trusts/testing/fixtures_test.go (99%) rename openstack/identity/v3/{extensions => }/trusts/testing/requests_test.go (98%) rename openstack/identity/v3/{extensions => }/trusts/urls.go (100%) diff --git a/internal/acceptance/openstack/identity/v3/identity.go b/internal/acceptance/openstack/identity/v3/identity.go index bd5d2470ef..ab34bb7530 100644 --- a/internal/acceptance/openstack/identity/v3/identity.go +++ b/internal/acceptance/openstack/identity/v3/identity.go @@ -7,12 +7,12 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/regions" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/trusts" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/users" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/identity/v3/trusts_test.go b/internal/acceptance/openstack/identity/v3/trusts_test.go index 0d848496ce..8cf70f9709 100644 --- a/internal/acceptance/openstack/identity/v3/trusts_test.go +++ b/internal/acceptance/openstack/identity/v3/trusts_test.go @@ -11,9 +11,9 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/roles" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/trusts" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/users" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/openstack/identity/v3/extensions/trusts/doc.go b/openstack/identity/v3/trusts/doc.go similarity index 100% rename from openstack/identity/v3/extensions/trusts/doc.go rename to openstack/identity/v3/trusts/doc.go diff --git a/openstack/identity/v3/extensions/trusts/requests.go b/openstack/identity/v3/trusts/requests.go similarity index 100% rename from openstack/identity/v3/extensions/trusts/requests.go rename to openstack/identity/v3/trusts/requests.go diff --git a/openstack/identity/v3/extensions/trusts/results.go b/openstack/identity/v3/trusts/results.go similarity index 100% rename from openstack/identity/v3/extensions/trusts/results.go rename to openstack/identity/v3/trusts/results.go diff --git a/openstack/identity/v3/extensions/trusts/testing/doc.go b/openstack/identity/v3/trusts/testing/doc.go similarity index 100% rename from openstack/identity/v3/extensions/trusts/testing/doc.go rename to openstack/identity/v3/trusts/testing/doc.go diff --git a/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go b/openstack/identity/v3/trusts/testing/fixtures_test.go similarity index 99% rename from openstack/identity/v3/extensions/trusts/testing/fixtures_test.go rename to openstack/identity/v3/trusts/testing/fixtures_test.go index 2809adc91e..19ede76569 100644 --- a/openstack/identity/v3/extensions/trusts/testing/fixtures_test.go +++ b/openstack/identity/v3/trusts/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/trusts" "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) diff --git a/openstack/identity/v3/extensions/trusts/testing/requests_test.go b/openstack/identity/v3/trusts/testing/requests_test.go similarity index 98% rename from openstack/identity/v3/extensions/trusts/testing/requests_test.go rename to openstack/identity/v3/trusts/testing/requests_test.go index 8be7949369..0b2d8149cb 100644 --- a/openstack/identity/v3/extensions/trusts/testing/requests_test.go +++ b/openstack/identity/v3/trusts/testing/requests_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/extensions/trusts" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/trusts" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" diff --git a/openstack/identity/v3/extensions/trusts/urls.go b/openstack/identity/v3/trusts/urls.go similarity index 100% rename from openstack/identity/v3/extensions/trusts/urls.go rename to openstack/identity/v3/trusts/urls.go From 862a540cc87f060e1564a237b5b7d98de438b7eb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 13:42:19 +0000 Subject: [PATCH 1824/2296] tests: Remove deprecated '+build' conditional compilation directives 'go:build' is understood by all supported versions of go [1]. There's no reason to keep these legacy headers around anymore. [1] https://groups.google.com/g/golang-nuts/c/ENsnV2FtH5I/m/5FVmK30QBgAJ Signed-off-by: Stephen Finucane --- .../acceptance/openstack/baremetal/httpbasic/allocations_test.go | 1 - internal/acceptance/openstack/baremetal/httpbasic/ports_test.go | 1 - .../acceptance/openstack/baremetal/noauth/allocations_test.go | 1 - internal/acceptance/openstack/baremetal/noauth/ports_test.go | 1 - internal/acceptance/openstack/baremetal/v1/allocations_test.go | 1 - internal/acceptance/openstack/baremetal/v1/conductors_test.go | 1 - internal/acceptance/openstack/baremetal/v1/nodes_test.go | 1 - internal/acceptance/openstack/baremetal/v1/ports_test.go | 1 - internal/acceptance/openstack/blockstorage/apiversions_test.go | 1 - .../acceptance/openstack/blockstorage/noauth/snapshots_test.go | 1 - .../acceptance/openstack/blockstorage/noauth/volumes_test.go | 1 - internal/acceptance/openstack/blockstorage/v2/backups_test.go | 1 - .../acceptance/openstack/blockstorage/v2/schedulerhints_test.go | 1 - .../acceptance/openstack/blockstorage/v2/schedulerstats_test.go | 1 - internal/acceptance/openstack/blockstorage/v2/services_test.go | 1 - internal/acceptance/openstack/blockstorage/v2/snapshots_test.go | 1 - internal/acceptance/openstack/blockstorage/v2/volumes_test.go | 1 - .../acceptance/openstack/blockstorage/v2/volumetenants_test.go | 1 - internal/acceptance/openstack/blockstorage/v3/backups_test.go | 1 - internal/acceptance/openstack/blockstorage/v3/quotaset_test.go | 1 - .../acceptance/openstack/blockstorage/v3/schedulerhints_test.go | 1 - .../acceptance/openstack/blockstorage/v3/schedulerstats_test.go | 1 - internal/acceptance/openstack/blockstorage/v3/services_test.go | 1 - internal/acceptance/openstack/blockstorage/v3/snapshots_test.go | 1 - .../openstack/blockstorage/v3/volumeattachments_test.go | 1 - internal/acceptance/openstack/blockstorage/v3/volumes_test.go | 1 - .../acceptance/openstack/blockstorage/v3/volumetenants_test.go | 1 - .../acceptance/openstack/blockstorage/v3/volumetypes_test.go | 1 - internal/acceptance/openstack/client_test.go | 1 - internal/acceptance/openstack/clustering/v1/actions_test.go | 1 - internal/acceptance/openstack/clustering/v1/clusters_test.go | 1 - internal/acceptance/openstack/clustering/v1/events_test.go | 1 - internal/acceptance/openstack/clustering/v1/nodes_test.go | 1 - internal/acceptance/openstack/clustering/v1/policies_test.go | 1 - internal/acceptance/openstack/clustering/v1/policytypes_test.go | 1 - internal/acceptance/openstack/clustering/v1/profiles_test.go | 1 - internal/acceptance/openstack/clustering/v1/profiletypes_test.go | 1 - internal/acceptance/openstack/clustering/v1/receivers_test.go | 1 - .../acceptance/openstack/clustering/v1/webhooktrigger_test.go | 1 - internal/acceptance/openstack/compute/v2/aggregates_test.go | 1 - .../acceptance/openstack/compute/v2/attachinterfaces_test.go | 1 - .../acceptance/openstack/compute/v2/availabilityzones_test.go | 1 - internal/acceptance/openstack/compute/v2/bootfromvolume_test.go | 1 - internal/acceptance/openstack/compute/v2/defsecrules_test.go | 1 - internal/acceptance/openstack/compute/v2/diagnostics_test.go | 1 - internal/acceptance/openstack/compute/v2/extension_test.go | 1 - internal/acceptance/openstack/compute/v2/flavors_test.go | 1 - internal/acceptance/openstack/compute/v2/floatingip_test.go | 1 - internal/acceptance/openstack/compute/v2/hypervisors_test.go | 1 - internal/acceptance/openstack/compute/v2/images_test.go | 1 - .../acceptance/openstack/compute/v2/instance_actions_test.go | 1 - internal/acceptance/openstack/compute/v2/keypairs_test.go | 1 - internal/acceptance/openstack/compute/v2/limits_test.go | 1 - internal/acceptance/openstack/compute/v2/migrate_test.go | 1 - internal/acceptance/openstack/compute/v2/network_test.go | 1 - internal/acceptance/openstack/compute/v2/quotaset_test.go | 1 - internal/acceptance/openstack/compute/v2/remoteconsoles_test.go | 1 - internal/acceptance/openstack/compute/v2/rescueunrescue_test.go | 1 - internal/acceptance/openstack/compute/v2/secgroup_test.go | 1 - internal/acceptance/openstack/compute/v2/servergroup_test.go | 1 - internal/acceptance/openstack/compute/v2/servers_test.go | 1 - internal/acceptance/openstack/compute/v2/services_test.go | 1 - internal/acceptance/openstack/compute/v2/tenantnetworks_test.go | 1 - internal/acceptance/openstack/compute/v2/usage_test.go | 1 - internal/acceptance/openstack/compute/v2/volumeattach_test.go | 1 - internal/acceptance/openstack/container/v1/capsules_test.go | 1 - .../acceptance/openstack/containerinfra/v1/certificates_test.go | 1 - internal/acceptance/openstack/containerinfra/v1/clusters_test.go | 1 - .../openstack/containerinfra/v1/clustertemplates_test.go | 1 - .../acceptance/openstack/containerinfra/v1/nodegroups_test.go | 1 - internal/acceptance/openstack/containerinfra/v1/quotas_test.go | 1 - internal/acceptance/openstack/db/v1/configurations_test.go | 1 - internal/acceptance/openstack/db/v1/databases_test.go | 1 - internal/acceptance/openstack/db/v1/flavors_test.go | 1 - internal/acceptance/openstack/db/v1/instances_test.go | 1 - internal/acceptance/openstack/db/v1/users_test.go | 1 - internal/acceptance/openstack/dns/v2/recordsets_test.go | 1 - internal/acceptance/openstack/dns/v2/transfers_test.go | 1 - internal/acceptance/openstack/dns/v2/zones_test.go | 1 - internal/acceptance/openstack/identity/v2/extension_test.go | 1 - internal/acceptance/openstack/identity/v2/role_test.go | 1 - internal/acceptance/openstack/identity/v2/tenant_test.go | 1 - internal/acceptance/openstack/identity/v2/token_test.go | 1 - internal/acceptance/openstack/identity/v2/user_test.go | 1 - .../openstack/identity/v3/applicationcredentials_test.go | 1 - internal/acceptance/openstack/identity/v3/credentials_test.go | 1 - internal/acceptance/openstack/identity/v3/domains_test.go | 1 - internal/acceptance/openstack/identity/v3/ec2credentials_test.go | 1 - internal/acceptance/openstack/identity/v3/endpoint_test.go | 1 - internal/acceptance/openstack/identity/v3/federation_test.go | 1 - internal/acceptance/openstack/identity/v3/groups_test.go | 1 - internal/acceptance/openstack/identity/v3/limits_test.go | 1 - internal/acceptance/openstack/identity/v3/oauth1_test.go | 1 - internal/acceptance/openstack/identity/v3/osinherit_test.go | 1 - internal/acceptance/openstack/identity/v3/policies_test.go | 1 - .../acceptance/openstack/identity/v3/projectendpoint_test.go | 1 - internal/acceptance/openstack/identity/v3/projects_test.go | 1 - internal/acceptance/openstack/identity/v3/reauth_test.go | 1 - internal/acceptance/openstack/identity/v3/regions_test.go | 1 - .../acceptance/openstack/identity/v3/registeredlimits_test.go | 1 - internal/acceptance/openstack/identity/v3/roles_test.go | 1 - internal/acceptance/openstack/identity/v3/service_test.go | 1 - internal/acceptance/openstack/identity/v3/token_test.go | 1 - internal/acceptance/openstack/identity/v3/trusts_test.go | 1 - internal/acceptance/openstack/identity/v3/users_test.go | 1 - internal/acceptance/openstack/image/v2/imageimport_test.go | 1 - internal/acceptance/openstack/image/v2/images_test.go | 1 - internal/acceptance/openstack/image/v2/tasks_test.go | 1 - internal/acceptance/openstack/keymanager/v1/acls_test.go | 1 - internal/acceptance/openstack/keymanager/v1/containers_test.go | 1 - internal/acceptance/openstack/keymanager/v1/orders_test.go | 1 - internal/acceptance/openstack/keymanager/v1/secrets_test.go | 1 - internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go | 1 - .../acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go | 1 - internal/acceptance/openstack/loadbalancer/v2/flavors_test.go | 1 - internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go | 1 - internal/acceptance/openstack/loadbalancer/v2/listeners_test.go | 1 - .../acceptance/openstack/loadbalancer/v2/loadbalancers_test.go | 1 - internal/acceptance/openstack/loadbalancer/v2/monitors_test.go | 1 - internal/acceptance/openstack/loadbalancer/v2/pools_test.go | 1 - internal/acceptance/openstack/loadbalancer/v2/providers_test.go | 1 - internal/acceptance/openstack/loadbalancer/v2/quotas_test.go | 1 - internal/acceptance/openstack/messaging/v2/claims_test.go | 1 - internal/acceptance/openstack/messaging/v2/message_test.go | 1 - internal/acceptance/openstack/messaging/v2/queue_test.go | 1 - internal/acceptance/openstack/networking/v2/apiversion_test.go | 1 - internal/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 - .../openstack/networking/v2/extensions/dns/dns_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/extraroutes_test.go | 1 - .../networking/v2/extensions/layer3/floatingips_test.go | 1 - .../networking/v2/extensions/layer3/l3_scheduling_test.go | 1 - .../openstack/networking/v2/extensions/layer3/routers_test.go | 1 - .../openstack/networking/v2/extensions/mtu/mtu_test.go | 1 - .../networkipavailabilities/networkipavailabilities_test.go | 1 - .../networking/v2/extensions/portsbinding/portsbinding_test.go | 1 - .../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 - .../openstack/networking/v2/extensions/security_test.go | 1 - .../networking/v2/extensions/subnetpools/subnetpools_test.go | 1 - .../networking/v2/extensions/trunk_details/trunks_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 - internal/acceptance/openstack/networking/v2/networks_test.go | 1 - internal/acceptance/openstack/networking/v2/ports_test.go | 1 - internal/acceptance/openstack/networking/v2/subnets_test.go | 1 - internal/acceptance/openstack/objectstorage/v1/accounts_test.go | 1 - .../acceptance/openstack/objectstorage/v1/containers_test.go | 1 - internal/acceptance/openstack/objectstorage/v1/objects_test.go | 1 - .../acceptance/openstack/objectstorage/v1/versioning_test.go | 1 - internal/acceptance/openstack/orchestration/v1/buildinfo_test.go | 1 - .../acceptance/openstack/orchestration/v1/stackevents_test.go | 1 - .../acceptance/openstack/orchestration/v1/stackresources_test.go | 1 - internal/acceptance/openstack/orchestration/v1/stacks_test.go | 1 - .../acceptance/openstack/orchestration/v1/stacktemplates_test.go | 1 - internal/acceptance/openstack/pkg.go | 1 - .../openstack/sharedfilesystems/v2/availabilityzones_test.go | 1 - .../acceptance/openstack/sharedfilesystems/v2/replicas_test.go | 1 - .../openstack/sharedfilesystems/v2/schedulerstats_test.go | 1 - .../openstack/sharedfilesystems/v2/securityservices_test.go | 1 - .../acceptance/openstack/sharedfilesystems/v2/services_test.go | 1 - .../openstack/sharedfilesystems/v2/shareaccessrules_test.go | 1 - .../openstack/sharedfilesystems/v2/sharenetworks_test.go | 1 - .../acceptance/openstack/sharedfilesystems/v2/shares_test.go | 1 - .../openstack/sharedfilesystems/v2/sharetransfers_test.go | 1 - .../acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go | 1 - .../acceptance/openstack/sharedfilesystems/v2/snapshots_test.go | 1 - 179 files changed, 179 deletions(-) diff --git a/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go b/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go index 2a677452c6..932c067ad0 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/allocations_test.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal || allocations -// +build acceptance baremetal allocations package httpbasic diff --git a/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go index 618b4b8cba..d425230c8f 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal || ports -// +build acceptance baremetal ports package httpbasic diff --git a/internal/acceptance/openstack/baremetal/noauth/allocations_test.go b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go index 11d792d1c4..bad5c1400a 100644 --- a/internal/acceptance/openstack/baremetal/noauth/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal || allocations -// +build acceptance baremetal allocations package noauth diff --git a/internal/acceptance/openstack/baremetal/noauth/ports_test.go b/internal/acceptance/openstack/baremetal/noauth/ports_test.go index 4215002a97..9af3d38de7 100644 --- a/internal/acceptance/openstack/baremetal/noauth/ports_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/ports_test.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal || ports -// +build acceptance baremetal ports package noauth diff --git a/internal/acceptance/openstack/baremetal/v1/allocations_test.go b/internal/acceptance/openstack/baremetal/v1/allocations_test.go index 88747fdbcf..6c2734f875 100644 --- a/internal/acceptance/openstack/baremetal/v1/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/v1/allocations_test.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal || allocations -// +build acceptance baremetal allocations package v1 diff --git a/internal/acceptance/openstack/baremetal/v1/conductors_test.go b/internal/acceptance/openstack/baremetal/v1/conductors_test.go index f63b7eec46..de9e352ae9 100644 --- a/internal/acceptance/openstack/baremetal/v1/conductors_test.go +++ b/internal/acceptance/openstack/baremetal/v1/conductors_test.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal || conductors -// +build acceptance baremetal conductors package v1 diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index d479bc0797..2d3f122193 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal || nodes -// +build acceptance baremetal nodes package v1 diff --git a/internal/acceptance/openstack/baremetal/v1/ports_test.go b/internal/acceptance/openstack/baremetal/v1/ports_test.go index 6ee31a7ad0..783c0c556c 100644 --- a/internal/acceptance/openstack/baremetal/v1/ports_test.go +++ b/internal/acceptance/openstack/baremetal/v1/ports_test.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal || ports -// +build acceptance baremetal ports package v1 diff --git a/internal/acceptance/openstack/blockstorage/apiversions_test.go b/internal/acceptance/openstack/blockstorage/apiversions_test.go index 1b86ee5490..c51561cd95 100644 --- a/internal/acceptance/openstack/blockstorage/apiversions_test.go +++ b/internal/acceptance/openstack/blockstorage/apiversions_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package blockstorage diff --git a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go index ecce2d8e92..4343c5bbc3 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package noauth diff --git a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go index e0356b0650..e2617c04ec 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package noauth diff --git a/internal/acceptance/openstack/blockstorage/v2/backups_test.go b/internal/acceptance/openstack/blockstorage/v2/backups_test.go index 58a42db7f8..eec56b929d 100644 --- a/internal/acceptance/openstack/blockstorage/v2/backups_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/backups_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go index cb8beeaea1..07d2d88d3d 100644 --- a/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go index ad4be47cb2..53efc3c7b3 100644 --- a/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/services_test.go b/internal/acceptance/openstack/blockstorage/v2/services_test.go index de40733c92..0ebe7ffe5f 100644 --- a/internal/acceptance/openstack/blockstorage/v2/services_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/services_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go index 0ccdea2eae..8112d953d9 100644 --- a/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go index 450e8e6158..6950fd9839 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go index 3c52c49cc7..a9d397a1a7 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v2 diff --git a/internal/acceptance/openstack/blockstorage/v3/backups_test.go b/internal/acceptance/openstack/blockstorage/v3/backups_test.go index dff3ca34d6..81745e1371 100644 --- a/internal/acceptance/openstack/blockstorage/v3/backups_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/backups_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go index 728b4f28ec..933aba3e83 100644 --- a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -1,5 +1,4 @@ //go:build acceptance || quotasets -// +build acceptance quotasets package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go index c8681e7753..2a8377e8d8 100644 --- a/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go index 5e5b67854a..a3054997b5 100644 --- a/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/services_test.go b/internal/acceptance/openstack/blockstorage/v3/services_test.go index 58593cd9eb..f9975db66f 100644 --- a/internal/acceptance/openstack/blockstorage/v3/services_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/services_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go index 2fb00482d5..97e043c1ad 100644 --- a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index 2cf187d44d..d8cc0dc8f0 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go index 6fda94a3aa..5c81df5605 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go index 65eb1b901e..5ab1bd9ae3 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go index da8dc95463..cffe82e93c 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// +build acceptance blockstorage package v3 diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index f44df3cd42..af88de1c5e 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package openstack diff --git a/internal/acceptance/openstack/clustering/v1/actions_test.go b/internal/acceptance/openstack/clustering/v1/actions_test.go index 9a4aa10708..4aa7585fd8 100644 --- a/internal/acceptance/openstack/clustering/v1/actions_test.go +++ b/internal/acceptance/openstack/clustering/v1/actions_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || actions -// +build acceptance clustering actions package v1 diff --git a/internal/acceptance/openstack/clustering/v1/clusters_test.go b/internal/acceptance/openstack/clustering/v1/clusters_test.go index 53cdaa5aa9..ebaa4e3697 100644 --- a/internal/acceptance/openstack/clustering/v1/clusters_test.go +++ b/internal/acceptance/openstack/clustering/v1/clusters_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || policies -// +build acceptance clustering policies package v1 diff --git a/internal/acceptance/openstack/clustering/v1/events_test.go b/internal/acceptance/openstack/clustering/v1/events_test.go index 98676da5d9..90ba4038f1 100644 --- a/internal/acceptance/openstack/clustering/v1/events_test.go +++ b/internal/acceptance/openstack/clustering/v1/events_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || events -// +build acceptance clustering events package v1 diff --git a/internal/acceptance/openstack/clustering/v1/nodes_test.go b/internal/acceptance/openstack/clustering/v1/nodes_test.go index 13534256a8..18b510b8b4 100644 --- a/internal/acceptance/openstack/clustering/v1/nodes_test.go +++ b/internal/acceptance/openstack/clustering/v1/nodes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || policies -// +build acceptance clustering policies package v1 diff --git a/internal/acceptance/openstack/clustering/v1/policies_test.go b/internal/acceptance/openstack/clustering/v1/policies_test.go index a763dd6b6f..db696f7a74 100644 --- a/internal/acceptance/openstack/clustering/v1/policies_test.go +++ b/internal/acceptance/openstack/clustering/v1/policies_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || policies -// +build acceptance clustering policies package v1 diff --git a/internal/acceptance/openstack/clustering/v1/policytypes_test.go b/internal/acceptance/openstack/clustering/v1/policytypes_test.go index 4170928e35..3906e70e48 100644 --- a/internal/acceptance/openstack/clustering/v1/policytypes_test.go +++ b/internal/acceptance/openstack/clustering/v1/policytypes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || policytypes -// +build acceptance clustering policytypes package v1 diff --git a/internal/acceptance/openstack/clustering/v1/profiles_test.go b/internal/acceptance/openstack/clustering/v1/profiles_test.go index 550c889449..a9e6e79635 100644 --- a/internal/acceptance/openstack/clustering/v1/profiles_test.go +++ b/internal/acceptance/openstack/clustering/v1/profiles_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || policies -// +build acceptance clustering policies package v1 diff --git a/internal/acceptance/openstack/clustering/v1/profiletypes_test.go b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go index 43ba35ffa0..0ca53748d1 100644 --- a/internal/acceptance/openstack/clustering/v1/profiletypes_test.go +++ b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || profiletypes -// +build acceptance clustering profiletypes package v1 diff --git a/internal/acceptance/openstack/clustering/v1/receivers_test.go b/internal/acceptance/openstack/clustering/v1/receivers_test.go index 2376e83d32..2cf5f40b2c 100644 --- a/internal/acceptance/openstack/clustering/v1/receivers_test.go +++ b/internal/acceptance/openstack/clustering/v1/receivers_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || policies -// +build acceptance clustering policies package v1 diff --git a/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go index 8f34bf717c..6d7345c0f1 100644 --- a/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go @@ -1,5 +1,4 @@ //go:build acceptance || clustering || webhooks -// +build acceptance clustering webhooks package v1 diff --git a/internal/acceptance/openstack/compute/v2/aggregates_test.go b/internal/acceptance/openstack/compute/v2/aggregates_test.go index c1a1959569..febaebd158 100644 --- a/internal/acceptance/openstack/compute/v2/aggregates_test.go +++ b/internal/acceptance/openstack/compute/v2/aggregates_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || aggregates -// +build acceptance compute aggregates package v2 diff --git a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go index 9d46ec0ac9..e888d05cf4 100644 --- a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go +++ b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || servers -// +build acceptance compute servers package v2 diff --git a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go index 7c27c0d664..b3af24b0a1 100644 --- a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || availabilityzones -// +build acceptance compute availabilityzones package v2 diff --git a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go index 957fd1cb32..5bf7f75e13 100644 --- a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || bootfromvolume -// +build acceptance compute bootfromvolume package v2 diff --git a/internal/acceptance/openstack/compute/v2/defsecrules_test.go b/internal/acceptance/openstack/compute/v2/defsecrules_test.go index d4fef06523..8b543feebf 100644 --- a/internal/acceptance/openstack/compute/v2/defsecrules_test.go +++ b/internal/acceptance/openstack/compute/v2/defsecrules_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || defsecrules -// +build acceptance compute defsecrules package v2 diff --git a/internal/acceptance/openstack/compute/v2/diagnostics_test.go b/internal/acceptance/openstack/compute/v2/diagnostics_test.go index aa63587997..e51984bd52 100644 --- a/internal/acceptance/openstack/compute/v2/diagnostics_test.go +++ b/internal/acceptance/openstack/compute/v2/diagnostics_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || limits -// +build acceptance compute limits package v2 diff --git a/internal/acceptance/openstack/compute/v2/extension_test.go b/internal/acceptance/openstack/compute/v2/extension_test.go index 3ed26bcbce..0339b251fb 100644 --- a/internal/acceptance/openstack/compute/v2/extension_test.go +++ b/internal/acceptance/openstack/compute/v2/extension_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || extensions -// +build acceptance compute extensions package v2 diff --git a/internal/acceptance/openstack/compute/v2/flavors_test.go b/internal/acceptance/openstack/compute/v2/flavors_test.go index 31ed4fb6de..cb6bd319a1 100644 --- a/internal/acceptance/openstack/compute/v2/flavors_test.go +++ b/internal/acceptance/openstack/compute/v2/flavors_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || flavors -// +build acceptance compute flavors package v2 diff --git a/internal/acceptance/openstack/compute/v2/floatingip_test.go b/internal/acceptance/openstack/compute/v2/floatingip_test.go index 047bc8e0f6..7c91d0c068 100644 --- a/internal/acceptance/openstack/compute/v2/floatingip_test.go +++ b/internal/acceptance/openstack/compute/v2/floatingip_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || servers -// +build acceptance compute servers package v2 diff --git a/internal/acceptance/openstack/compute/v2/hypervisors_test.go b/internal/acceptance/openstack/compute/v2/hypervisors_test.go index a0390a1ace..dae1bbcdbc 100644 --- a/internal/acceptance/openstack/compute/v2/hypervisors_test.go +++ b/internal/acceptance/openstack/compute/v2/hypervisors_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || hypervisors -// +build acceptance compute hypervisors package v2 diff --git a/internal/acceptance/openstack/compute/v2/images_test.go b/internal/acceptance/openstack/compute/v2/images_test.go index eae54cb3e9..ac1e13684c 100644 --- a/internal/acceptance/openstack/compute/v2/images_test.go +++ b/internal/acceptance/openstack/compute/v2/images_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || images -// +build acceptance compute images package v2 diff --git a/internal/acceptance/openstack/compute/v2/instance_actions_test.go b/internal/acceptance/openstack/compute/v2/instance_actions_test.go index 4a47fedcdf..2e533acef4 100644 --- a/internal/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/internal/acceptance/openstack/compute/v2/instance_actions_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || limits -// +build acceptance compute limits package v2 diff --git a/internal/acceptance/openstack/compute/v2/keypairs_test.go b/internal/acceptance/openstack/compute/v2/keypairs_test.go index 035d890ac0..f5fec8138c 100644 --- a/internal/acceptance/openstack/compute/v2/keypairs_test.go +++ b/internal/acceptance/openstack/compute/v2/keypairs_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || keypairs -// +build acceptance compute keypairs package v2 diff --git a/internal/acceptance/openstack/compute/v2/limits_test.go b/internal/acceptance/openstack/compute/v2/limits_test.go index 1eb85a8fe3..55ef2854f2 100644 --- a/internal/acceptance/openstack/compute/v2/limits_test.go +++ b/internal/acceptance/openstack/compute/v2/limits_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || limits -// +build acceptance compute limits package v2 diff --git a/internal/acceptance/openstack/compute/v2/migrate_test.go b/internal/acceptance/openstack/compute/v2/migrate_test.go index cf9056b156..f22d6e67c6 100644 --- a/internal/acceptance/openstack/compute/v2/migrate_test.go +++ b/internal/acceptance/openstack/compute/v2/migrate_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || servers -// +build acceptance compute servers package v2 diff --git a/internal/acceptance/openstack/compute/v2/network_test.go b/internal/acceptance/openstack/compute/v2/network_test.go index 5fbad4069d..00516baed4 100644 --- a/internal/acceptance/openstack/compute/v2/network_test.go +++ b/internal/acceptance/openstack/compute/v2/network_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || servers -// +build acceptance compute servers package v2 diff --git a/internal/acceptance/openstack/compute/v2/quotaset_test.go b/internal/acceptance/openstack/compute/v2/quotaset_test.go index 734f15d4b8..78ebbae2f0 100644 --- a/internal/acceptance/openstack/compute/v2/quotaset_test.go +++ b/internal/acceptance/openstack/compute/v2/quotaset_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || quotasets -// +build acceptance compute quotasets package v2 diff --git a/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go b/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go index 55068d2ed2..b05fbad2e3 100644 --- a/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go +++ b/internal/acceptance/openstack/compute/v2/remoteconsoles_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || remoteconsoles -// +build acceptance compute remoteconsoles package v2 diff --git a/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go b/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go index 8492197387..5cda0e3d7c 100644 --- a/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go +++ b/internal/acceptance/openstack/compute/v2/rescueunrescue_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || rescueunrescue -// +build acceptance compute rescueunrescue package v2 diff --git a/internal/acceptance/openstack/compute/v2/secgroup_test.go b/internal/acceptance/openstack/compute/v2/secgroup_test.go index 9cda46eba6..0809bb0dda 100644 --- a/internal/acceptance/openstack/compute/v2/secgroup_test.go +++ b/internal/acceptance/openstack/compute/v2/secgroup_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || secgroups -// +build acceptance compute secgroups package v2 diff --git a/internal/acceptance/openstack/compute/v2/servergroup_test.go b/internal/acceptance/openstack/compute/v2/servergroup_test.go index 064350c6d4..8d23ea8135 100644 --- a/internal/acceptance/openstack/compute/v2/servergroup_test.go +++ b/internal/acceptance/openstack/compute/v2/servergroup_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || servergroups -// +build acceptance compute servergroups package v2 diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 4c6e6b3462..53401d6169 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || servers -// +build acceptance compute servers package v2 diff --git a/internal/acceptance/openstack/compute/v2/services_test.go b/internal/acceptance/openstack/compute/v2/services_test.go index b298fb87f8..155255fb43 100644 --- a/internal/acceptance/openstack/compute/v2/services_test.go +++ b/internal/acceptance/openstack/compute/v2/services_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || services -// +build acceptance compute services package v2 diff --git a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go index 4fe754903e..89487adb1b 100644 --- a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go +++ b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || servers -// +build acceptance compute servers package v2 diff --git a/internal/acceptance/openstack/compute/v2/usage_test.go b/internal/acceptance/openstack/compute/v2/usage_test.go index 80fa7c4e9b..42f5dac881 100644 --- a/internal/acceptance/openstack/compute/v2/usage_test.go +++ b/internal/acceptance/openstack/compute/v2/usage_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || usage -// +build acceptance compute usage package v2 diff --git a/internal/acceptance/openstack/compute/v2/volumeattach_test.go b/internal/acceptance/openstack/compute/v2/volumeattach_test.go index 53488cd5d1..d4cce8d700 100644 --- a/internal/acceptance/openstack/compute/v2/volumeattach_test.go +++ b/internal/acceptance/openstack/compute/v2/volumeattach_test.go @@ -1,5 +1,4 @@ //go:build acceptance || compute || volumeattach -// +build acceptance compute volumeattach package v2 diff --git a/internal/acceptance/openstack/container/v1/capsules_test.go b/internal/acceptance/openstack/container/v1/capsules_test.go index 865ec9e3e7..5da7010948 100644 --- a/internal/acceptance/openstack/container/v1/capsules_test.go +++ b/internal/acceptance/openstack/container/v1/capsules_test.go @@ -1,5 +1,4 @@ //go:build acceptance || containers || capsules -// +build acceptance containers capsules package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/certificates_test.go b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go index 3e26eea692..0c7be7b7a0 100644 --- a/internal/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -1,5 +1,4 @@ //go:build acceptance || containerinfra -// +build acceptance containerinfra package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go index 47b63f1288..725470c706 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -1,5 +1,4 @@ //go:build acceptance || containerinfra -// +build acceptance containerinfra package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index 91e72ce569..92975d5e76 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -1,5 +1,4 @@ //go:build acceptance || containerinfra -// +build acceptance containerinfra package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go index ed17bb5685..62499bb37b 100644 --- a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -1,5 +1,4 @@ //go:build acceptance || containerinfra -// +build acceptance containerinfra package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/quotas_test.go b/internal/acceptance/openstack/containerinfra/v1/quotas_test.go index 17618b2e66..1bdd8b05f9 100644 --- a/internal/acceptance/openstack/containerinfra/v1/quotas_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/quotas_test.go @@ -1,5 +1,4 @@ //go:build acceptance || containerinfra -// +build acceptance containerinfra package v1 diff --git a/internal/acceptance/openstack/db/v1/configurations_test.go b/internal/acceptance/openstack/db/v1/configurations_test.go index 3ee9a3661a..39462843dc 100644 --- a/internal/acceptance/openstack/db/v1/configurations_test.go +++ b/internal/acceptance/openstack/db/v1/configurations_test.go @@ -1,5 +1,4 @@ //go:build acceptance || db -// +build acceptance db package v1 diff --git a/internal/acceptance/openstack/db/v1/databases_test.go b/internal/acceptance/openstack/db/v1/databases_test.go index 76cba92389..1d54d071ec 100644 --- a/internal/acceptance/openstack/db/v1/databases_test.go +++ b/internal/acceptance/openstack/db/v1/databases_test.go @@ -1,5 +1,4 @@ //go:build acceptance || db -// +build acceptance db package v1 diff --git a/internal/acceptance/openstack/db/v1/flavors_test.go b/internal/acceptance/openstack/db/v1/flavors_test.go index 09ed3c5415..dad7c4a1e9 100644 --- a/internal/acceptance/openstack/db/v1/flavors_test.go +++ b/internal/acceptance/openstack/db/v1/flavors_test.go @@ -1,5 +1,4 @@ //go:build acceptance || db -// +build acceptance db package v1 diff --git a/internal/acceptance/openstack/db/v1/instances_test.go b/internal/acceptance/openstack/db/v1/instances_test.go index a46b476849..790918cf69 100644 --- a/internal/acceptance/openstack/db/v1/instances_test.go +++ b/internal/acceptance/openstack/db/v1/instances_test.go @@ -1,5 +1,4 @@ //go:build acceptance || db -// +build acceptance db package v1 diff --git a/internal/acceptance/openstack/db/v1/users_test.go b/internal/acceptance/openstack/db/v1/users_test.go index 4530d63180..707a4483b0 100644 --- a/internal/acceptance/openstack/db/v1/users_test.go +++ b/internal/acceptance/openstack/db/v1/users_test.go @@ -1,5 +1,4 @@ //go:build acceptance || db -// +build acceptance db package v1 diff --git a/internal/acceptance/openstack/dns/v2/recordsets_test.go b/internal/acceptance/openstack/dns/v2/recordsets_test.go index 98f8020dc8..3cad12b88f 100644 --- a/internal/acceptance/openstack/dns/v2/recordsets_test.go +++ b/internal/acceptance/openstack/dns/v2/recordsets_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/dns/v2/transfers_test.go b/internal/acceptance/openstack/dns/v2/transfers_test.go index 1e30dd1dd4..53cf5dfe35 100644 --- a/internal/acceptance/openstack/dns/v2/transfers_test.go +++ b/internal/acceptance/openstack/dns/v2/transfers_test.go @@ -1,5 +1,4 @@ //go:build acceptance || dns || transfers -// +build acceptance dns transfers package v2 diff --git a/internal/acceptance/openstack/dns/v2/zones_test.go b/internal/acceptance/openstack/dns/v2/zones_test.go index d252541f65..2281094377 100644 --- a/internal/acceptance/openstack/dns/v2/zones_test.go +++ b/internal/acceptance/openstack/dns/v2/zones_test.go @@ -1,5 +1,4 @@ //go:build acceptance || dns || zones -// +build acceptance dns zones package v2 diff --git a/internal/acceptance/openstack/identity/v2/extension_test.go b/internal/acceptance/openstack/identity/v2/extension_test.go index 1b3538a12b..43fd837e4d 100644 --- a/internal/acceptance/openstack/identity/v2/extension_test.go +++ b/internal/acceptance/openstack/identity/v2/extension_test.go @@ -1,5 +1,4 @@ //go:build acceptance || identity -// +build acceptance identity package v2 diff --git a/internal/acceptance/openstack/identity/v2/role_test.go b/internal/acceptance/openstack/identity/v2/role_test.go index 667c3addff..adffc6de1e 100644 --- a/internal/acceptance/openstack/identity/v2/role_test.go +++ b/internal/acceptance/openstack/identity/v2/role_test.go @@ -1,5 +1,4 @@ //go:build acceptance || identity || roles -// +build acceptance identity roles package v2 diff --git a/internal/acceptance/openstack/identity/v2/tenant_test.go b/internal/acceptance/openstack/identity/v2/tenant_test.go index b8f35474d0..f6e51bd3c6 100644 --- a/internal/acceptance/openstack/identity/v2/tenant_test.go +++ b/internal/acceptance/openstack/identity/v2/tenant_test.go @@ -1,5 +1,4 @@ //go:build acceptance || identity -// +build acceptance identity package v2 diff --git a/internal/acceptance/openstack/identity/v2/token_test.go b/internal/acceptance/openstack/identity/v2/token_test.go index 9b3234d33c..1ea8d389a2 100644 --- a/internal/acceptance/openstack/identity/v2/token_test.go +++ b/internal/acceptance/openstack/identity/v2/token_test.go @@ -1,5 +1,4 @@ //go:build acceptance || identity -// +build acceptance identity package v2 diff --git a/internal/acceptance/openstack/identity/v2/user_test.go b/internal/acceptance/openstack/identity/v2/user_test.go index 1fd8c2bed4..b13b422127 100644 --- a/internal/acceptance/openstack/identity/v2/user_test.go +++ b/internal/acceptance/openstack/identity/v2/user_test.go @@ -1,5 +1,4 @@ //go:build acceptance || identity -// +build acceptance identity package v2 diff --git a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go index 0f1f754529..90d9946ef5 100644 --- a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/credentials_test.go b/internal/acceptance/openstack/identity/v3/credentials_test.go index 00bc2052e8..45e8666699 100644 --- a/internal/acceptance/openstack/identity/v3/credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/credentials_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/domains_test.go b/internal/acceptance/openstack/identity/v3/domains_test.go index c0864fffe1..df426af684 100644 --- a/internal/acceptance/openstack/identity/v3/domains_test.go +++ b/internal/acceptance/openstack/identity/v3/domains_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go index 69c3877374..cece5d9f31 100644 --- a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go index 7e1e37b5d0..91f755caca 100644 --- a/internal/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/federation_test.go b/internal/acceptance/openstack/identity/v3/federation_test.go index cc964c20c0..144134d49d 100644 --- a/internal/acceptance/openstack/identity/v3/federation_test.go +++ b/internal/acceptance/openstack/identity/v3/federation_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/groups_test.go b/internal/acceptance/openstack/identity/v3/groups_test.go index 3fc21a946c..d615eb788f 100644 --- a/internal/acceptance/openstack/identity/v3/groups_test.go +++ b/internal/acceptance/openstack/identity/v3/groups_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/limits_test.go b/internal/acceptance/openstack/identity/v3/limits_test.go index 8b2f42f642..010ae064e3 100644 --- a/internal/acceptance/openstack/identity/v3/limits_test.go +++ b/internal/acceptance/openstack/identity/v3/limits_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index a88ac39cc0..cd19b55449 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/osinherit_test.go b/internal/acceptance/openstack/identity/v3/osinherit_test.go index 9d0e6d004f..868ed225b7 100644 --- a/internal/acceptance/openstack/identity/v3/osinherit_test.go +++ b/internal/acceptance/openstack/identity/v3/osinherit_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/policies_test.go b/internal/acceptance/openstack/identity/v3/policies_test.go index eb21878370..ec9f0c4634 100644 --- a/internal/acceptance/openstack/identity/v3/policies_test.go +++ b/internal/acceptance/openstack/identity/v3/policies_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go index 1da742cc8b..18590a82ae 100644 --- a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/projects_test.go b/internal/acceptance/openstack/identity/v3/projects_test.go index 95fff1aebd..7406879fd7 100644 --- a/internal/acceptance/openstack/identity/v3/projects_test.go +++ b/internal/acceptance/openstack/identity/v3/projects_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/reauth_test.go b/internal/acceptance/openstack/identity/v3/reauth_test.go index 4b1611ea3d..e07fb485a1 100644 --- a/internal/acceptance/openstack/identity/v3/reauth_test.go +++ b/internal/acceptance/openstack/identity/v3/reauth_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/regions_test.go b/internal/acceptance/openstack/identity/v3/regions_test.go index 62d27de7ba..c71eb78eb8 100644 --- a/internal/acceptance/openstack/identity/v3/regions_test.go +++ b/internal/acceptance/openstack/identity/v3/regions_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go index d94efe7b9f..deb4806fda 100644 --- a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go +++ b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index 9b8508b422..d4c58ac995 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/service_test.go b/internal/acceptance/openstack/identity/v3/service_test.go index be249c18fd..9c3ef84766 100644 --- a/internal/acceptance/openstack/identity/v3/service_test.go +++ b/internal/acceptance/openstack/identity/v3/service_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/token_test.go b/internal/acceptance/openstack/identity/v3/token_test.go index cd5aeb6d30..ce09930e21 100644 --- a/internal/acceptance/openstack/identity/v3/token_test.go +++ b/internal/acceptance/openstack/identity/v3/token_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/identity/v3/trusts_test.go b/internal/acceptance/openstack/identity/v3/trusts_test.go index 0d848496ce..5b8592dd79 100644 --- a/internal/acceptance/openstack/identity/v3/trusts_test.go +++ b/internal/acceptance/openstack/identity/v3/trusts_test.go @@ -1,5 +1,4 @@ //go:build acceptance || identity || trusts -// +build acceptance identity trusts package v3 diff --git a/internal/acceptance/openstack/identity/v3/users_test.go b/internal/acceptance/openstack/identity/v3/users_test.go index ac67b98c72..97a0ddd67b 100644 --- a/internal/acceptance/openstack/identity/v3/users_test.go +++ b/internal/acceptance/openstack/identity/v3/users_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v3 diff --git a/internal/acceptance/openstack/image/v2/imageimport_test.go b/internal/acceptance/openstack/image/v2/imageimport_test.go index 65cef05da3..60740428fb 100644 --- a/internal/acceptance/openstack/image/v2/imageimport_test.go +++ b/internal/acceptance/openstack/image/v2/imageimport_test.go @@ -1,5 +1,4 @@ //go:build acceptance || image || imageimport -// +build acceptance image imageimport package v2 diff --git a/internal/acceptance/openstack/image/v2/images_test.go b/internal/acceptance/openstack/image/v2/images_test.go index 2ebe82b695..24a1088b99 100644 --- a/internal/acceptance/openstack/image/v2/images_test.go +++ b/internal/acceptance/openstack/image/v2/images_test.go @@ -1,5 +1,4 @@ //go:build acceptance || image || images -// +build acceptance image images package v2 diff --git a/internal/acceptance/openstack/image/v2/tasks_test.go b/internal/acceptance/openstack/image/v2/tasks_test.go index fd2f2664e2..d5c4bcccd0 100644 --- a/internal/acceptance/openstack/image/v2/tasks_test.go +++ b/internal/acceptance/openstack/image/v2/tasks_test.go @@ -1,5 +1,4 @@ //go:build acceptance || image || tasks -// +build acceptance image tasks package v2 diff --git a/internal/acceptance/openstack/keymanager/v1/acls_test.go b/internal/acceptance/openstack/keymanager/v1/acls_test.go index e8b0b6db28..ab9cd61716 100644 --- a/internal/acceptance/openstack/keymanager/v1/acls_test.go +++ b/internal/acceptance/openstack/keymanager/v1/acls_test.go @@ -1,5 +1,4 @@ //go:build acceptance || keymanager || acls -// +build acceptance keymanager acls package v1 diff --git a/internal/acceptance/openstack/keymanager/v1/containers_test.go b/internal/acceptance/openstack/keymanager/v1/containers_test.go index 50795a2678..7459558f93 100644 --- a/internal/acceptance/openstack/keymanager/v1/containers_test.go +++ b/internal/acceptance/openstack/keymanager/v1/containers_test.go @@ -1,5 +1,4 @@ //go:build acceptance || keymanager || containers -// +build acceptance keymanager containers package v1 diff --git a/internal/acceptance/openstack/keymanager/v1/orders_test.go b/internal/acceptance/openstack/keymanager/v1/orders_test.go index 73fd4ea851..b5235ff88f 100644 --- a/internal/acceptance/openstack/keymanager/v1/orders_test.go +++ b/internal/acceptance/openstack/keymanager/v1/orders_test.go @@ -1,5 +1,4 @@ //go:build acceptance || keymanager || orders -// +build acceptance keymanager orders package v1 diff --git a/internal/acceptance/openstack/keymanager/v1/secrets_test.go b/internal/acceptance/openstack/keymanager/v1/secrets_test.go index a6133dc320..078d190587 100644 --- a/internal/acceptance/openstack/keymanager/v1/secrets_test.go +++ b/internal/acceptance/openstack/keymanager/v1/secrets_test.go @@ -1,5 +1,4 @@ //go:build acceptance || keymanager || secrets -// +build acceptance keymanager secrets package v1 diff --git a/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go b/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go index 9fa07addc1..737cc3d4d8 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/amphorae_test.go @@ -1,5 +1,4 @@ //go:build acceptance || containers || capsules -// +build acceptance containers capsules package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go index bdd930d3c0..1a6ebadf28 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || flavorprofiles -// +build acceptance networking loadbalancer flavorprofiles package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go index 7681809841..b714af7324 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || flavors -// +build acceptance networking loadbalancer flavors package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go b/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go index 30f26b3e14..089632f9a5 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/l7policies_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || l7policies -// +build acceptance networking loadbalancer l7policies package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go b/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go index a172695638..1bcce8c929 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/listeners_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || listeners -// +build acceptance networking loadbalancer listeners package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go index 4334adbb14..98556fbc25 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancers_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || loadbalancers -// +build acceptance networking loadbalancer loadbalancers package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go b/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go index 63e36c35ea..4c7d729bfc 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/monitors_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || monitors -// +build acceptance networking loadbalancer monitors package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/pools_test.go b/internal/acceptance/openstack/loadbalancer/v2/pools_test.go index 7ac60041c5..1d2d6f5ba4 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/pools_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/pools_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || pools -// +build acceptance networking loadbalancer pools package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/providers_test.go b/internal/acceptance/openstack/loadbalancer/v2/providers_test.go index 987864ee36..3d35368e7e 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/providers_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/providers_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || providers -// +build acceptance networking loadbalancer providers package v2 diff --git a/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go index 2f722c51aa..cc778e5403 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || loadbalancer || quotas -// +build acceptance networking loadbalancer quotas package v2 diff --git a/internal/acceptance/openstack/messaging/v2/claims_test.go b/internal/acceptance/openstack/messaging/v2/claims_test.go index 54f32f19f0..679996e43b 100644 --- a/internal/acceptance/openstack/messaging/v2/claims_test.go +++ b/internal/acceptance/openstack/messaging/v2/claims_test.go @@ -1,5 +1,4 @@ //go:build acceptance || messaging || claims -// +build acceptance messaging claims package v2 diff --git a/internal/acceptance/openstack/messaging/v2/message_test.go b/internal/acceptance/openstack/messaging/v2/message_test.go index 88ebb416d9..f64e1c2db3 100644 --- a/internal/acceptance/openstack/messaging/v2/message_test.go +++ b/internal/acceptance/openstack/messaging/v2/message_test.go @@ -1,5 +1,4 @@ //go:build acceptance || messaging || messages -// +build acceptance messaging messages package v2 diff --git a/internal/acceptance/openstack/messaging/v2/queue_test.go b/internal/acceptance/openstack/messaging/v2/queue_test.go index b8eaaa0cf6..99cf177cf0 100644 --- a/internal/acceptance/openstack/messaging/v2/queue_test.go +++ b/internal/acceptance/openstack/messaging/v2/queue_test.go @@ -1,5 +1,4 @@ //go:build acceptance || messaging || queues -// +build acceptance messaging queues package v2 diff --git a/internal/acceptance/openstack/networking/v2/apiversion_test.go b/internal/acceptance/openstack/networking/v2/apiversion_test.go index 6237210ea9..5a5af959f9 100644 --- a/internal/acceptance/openstack/networking/v2/apiversion_test.go +++ b/internal/acceptance/openstack/networking/v2/apiversion_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking -// +build acceptance networking package v2 diff --git a/internal/acceptance/openstack/networking/v2/extension_test.go b/internal/acceptance/openstack/networking/v2/extension_test.go index dd8f563135..b31f1a1676 100644 --- a/internal/acceptance/openstack/networking/v2/extension_test.go +++ b/internal/acceptance/openstack/networking/v2/extension_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || extensions -// +build acceptance networking extensions package v2 diff --git a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index bbd71e262e..3ce0f8972e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || agents -// +build acceptance networking agents package agents diff --git a/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go index 8f029269b0..d35ae5cc30 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || tags -// +build acceptance networking tags package extensions diff --git a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index bdd9e9d7d8..6b84bdd9d4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking -// +build acceptance networking package dns diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index df4740ca20..dd4479c5ae 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || fwaas_v2 -// +build acceptance networking fwaas_v2 package fwaas_v2 diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go index 5c683a65b6..2b8035cf81 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || fwaas_v2 -// +build acceptance networking fwaas_v2 package fwaas_v2 diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go index fc1ea944e0..56fd490171 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || fwaas_v2 -// +build acceptance networking fwaas_v2 package fwaas_v2 diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go index 194bcfd336..bb4c8ce2d4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/addressscopes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || layer3 || addressscopes -// +build acceptance networking layer3 addressscopes package layer3 diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go index 5a8469ddc3..b327b1a138 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/extraroutes_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || layer3 || router -// +build acceptance networking layer3 router package layer3 diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go index f258bec4ea..fee8cacd38 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/floatingips_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || layer3 || floatingips -// +build acceptance networking layer3 floatingips package layer3 diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index 57818c160d..70db4e9037 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || layer3 || router -// +build acceptance networking layer3 router package layer3 diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index e94ab3488e..8d1800f719 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || layer3 || router -// +build acceptance networking layer3 router package layer3 diff --git a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 6e07ab2be9..018ffb8d81 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking -// +build acceptance networking package mtu diff --git a/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go b/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go index 73e46a379e..a7b678674b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/networkipavailabilities/networkipavailabilities_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || networkipavailabilities -// +build acceptance networking networkipavailabilities package networkipavailabilities 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 c1e328277b..93347e627f 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking -// +build acceptance networking package portsbinding diff --git a/internal/acceptance/openstack/networking/v2/extensions/provider_test.go b/internal/acceptance/openstack/networking/v2/extensions/provider_test.go index e4046a38b7..777ccba2cd 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/provider_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/provider_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || provider -// +build acceptance networking provider package extensions diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go index cbc20c4b6f..fdd50cf21f 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || qos || policies -// +build acceptance networking qos policies package policies diff --git a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go index cda108f213..3eaf7d232b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || quotas -// +build acceptance networking quotas package quotas diff --git a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index bd749f2294..290cdcd5b2 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package rbacpolicies diff --git a/internal/acceptance/openstack/networking/v2/extensions/security_test.go b/internal/acceptance/openstack/networking/v2/extensions/security_test.go index b5b424a634..7d0d967e46 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/security_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || security -// +build acceptance networking security package extensions diff --git a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go index 2e714d8ab0..8a0575d8fe 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/subnetpools/subnetpools_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || subnetpools -// +build acceptance networking subnetpools package v2 diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go index 42cfb737da..dab26f9310 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -1,5 +1,4 @@ //go:build acceptance || trunks -// +build acceptance trunks package trunk_details diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 8bd5a265d4..2f2fc33f0f 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -1,5 +1,4 @@ //go:build acceptance || trunks -// +build acceptance trunks package trunks diff --git a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go index c21c9c69de..bef19819b8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || vlantransparent -// +build acceptance networking vlantransparent package v2 diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go index bfb16ef898..8fe5e0638c 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/group_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || vpnaas -// +build acceptance networking vpnaas package vpnaas diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go index d8a233b8c4..c0407c99a4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ikepolicy_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || vpnaas -// +build acceptance networking vpnaas package vpnaas diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go index fbed901888..f9aed9b2aa 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/ipsecpolicy_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || vpnaas -// +build acceptance networking vpnaas package vpnaas diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go index d927818dc6..2ac1c7734c 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/service_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || vpnaas -// +build acceptance networking vpnaas package vpnaas diff --git a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go index c201483295..8aa8e49f57 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vpnaas/siteconnection_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking || vpnaas -// +build acceptance networking vpnaas package vpnaas diff --git a/internal/acceptance/openstack/networking/v2/networks_test.go b/internal/acceptance/openstack/networking/v2/networks_test.go index b9b7b10352..6dedc21000 100644 --- a/internal/acceptance/openstack/networking/v2/networks_test.go +++ b/internal/acceptance/openstack/networking/v2/networks_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking -// +build acceptance networking package v2 diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index 0c7352ec34..d24145bf7e 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking -// +build acceptance networking package v2 diff --git a/internal/acceptance/openstack/networking/v2/subnets_test.go b/internal/acceptance/openstack/networking/v2/subnets_test.go index 66740c8222..90ee87a6fe 100644 --- a/internal/acceptance/openstack/networking/v2/subnets_test.go +++ b/internal/acceptance/openstack/networking/v2/subnets_test.go @@ -1,5 +1,4 @@ //go:build acceptance || networking -// +build acceptance networking package v2 diff --git a/internal/acceptance/openstack/objectstorage/v1/accounts_test.go b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go index 79f64b7e9a..234e317adb 100644 --- a/internal/acceptance/openstack/objectstorage/v1/accounts_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/objectstorage/v1/containers_test.go b/internal/acceptance/openstack/objectstorage/v1/containers_test.go index 8f1eb00578..881bc93d02 100644 --- a/internal/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/containers_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/objectstorage/v1/objects_test.go b/internal/acceptance/openstack/objectstorage/v1/objects_test.go index 60a5444ce8..36449f528e 100644 --- a/internal/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/objects_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go index d38629e04d..5ec46167b9 100644 --- a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go index 4a11af0d24..94435eef3e 100644 --- a/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/stackevents_test.go b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go index bfdd0079a8..44ea23812c 100644 --- a/internal/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/stackresources_test.go b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go index 05930db255..dfaedd756d 100644 --- a/internal/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/stacks_test.go b/internal/acceptance/openstack/orchestration/v1/stacks_test.go index a7dbc90928..1774ccbc53 100644 --- a/internal/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacks_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go index 696fc9e530..b2feb84697 100644 --- a/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v1 diff --git a/internal/acceptance/openstack/pkg.go b/internal/acceptance/openstack/pkg.go index caec0ab6ef..41589adc75 100644 --- a/internal/acceptance/openstack/pkg.go +++ b/internal/acceptance/openstack/pkg.go @@ -1,4 +1,3 @@ //go:build acceptance -// +build acceptance package openstack diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go index 75441f6e14..6c10b644d8 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go index 847512581f..a7c1e8e897 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go index 672578a642..c7ec12f606 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index 32860ea6f2..5f7e9ef76e 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go index 65d0acdbf7..d9e5911e3a 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go index c6a9626974..9df9690add 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index f212702b5e..3e7f8fcea0 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go index a7f05516b9..028ae58977 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go index 2d77d32a8c..7faf6aa806 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go @@ -1,5 +1,4 @@ //go:build acceptance || share || transfers -// +build acceptance share transfers package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index d51da2ef3e..94a978efc4 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index e457d3c704..5913c5888c 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -1,5 +1,4 @@ //go:build acceptance -// +build acceptance package v2 From f2f1f6860df3bd82442eb64f572a5bc3ed3ebade Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 13:52:14 +0000 Subject: [PATCH 1825/2296] tests: Add missing 'go:build' conditional compilation directives So these integration tests aren't run as part of unit tests. Files identified using the following Silver Searcher invocation: ag -L 'go:build' -G '.*_test.go' internal/ ...which found all files in 'internal/' that *did not* contain 'go:build' but *did* end in '_test.go'. This was followed by: ag 'go:build acceptance$' -G '.*_test.go' internal/ ...which found all files in 'internal/' that only contained 'go:build acceptance' (i.e. no service-specific suffix) ending in '_test.go'. Signed-off-by: Stephen Finucane --- internal/acceptance/clients/testing/conditions_test.go | 2 ++ internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go | 2 ++ internal/acceptance/openstack/baremetal/noauth/nodes_test.go | 2 ++ internal/acceptance/openstack/blockstorage/apiversions_test.go | 2 +- .../acceptance/openstack/blockstorage/noauth/snapshots_test.go | 2 +- .../acceptance/openstack/blockstorage/noauth/volumes_test.go | 2 +- internal/acceptance/openstack/blockstorage/v2/backups_test.go | 2 +- internal/acceptance/openstack/blockstorage/v2/limits_test.go | 2 ++ .../acceptance/openstack/blockstorage/v2/schedulerhints_test.go | 2 +- .../acceptance/openstack/blockstorage/v2/schedulerstats_test.go | 2 +- internal/acceptance/openstack/blockstorage/v2/services_test.go | 2 +- internal/acceptance/openstack/blockstorage/v2/snapshots_test.go | 2 +- internal/acceptance/openstack/blockstorage/v2/volumes_test.go | 2 +- .../acceptance/openstack/blockstorage/v2/volumetenants_test.go | 2 +- internal/acceptance/openstack/blockstorage/v3/backups_test.go | 2 +- internal/acceptance/openstack/blockstorage/v3/limits_test.go | 2 ++ internal/acceptance/openstack/blockstorage/v3/qos_test.go | 2 ++ internal/acceptance/openstack/blockstorage/v3/quotaset_test.go | 2 +- .../acceptance/openstack/blockstorage/v3/schedulerhints_test.go | 2 +- .../acceptance/openstack/blockstorage/v3/schedulerstats_test.go | 2 +- internal/acceptance/openstack/blockstorage/v3/services_test.go | 2 +- internal/acceptance/openstack/blockstorage/v3/snapshots_test.go | 2 +- .../openstack/blockstorage/v3/volumeattachments_test.go | 2 +- internal/acceptance/openstack/blockstorage/v3/volumes_test.go | 2 +- .../acceptance/openstack/blockstorage/v3/volumetenants_test.go | 2 +- .../acceptance/openstack/blockstorage/v3/volumetypes_test.go | 2 +- .../acceptance/openstack/containerinfra/v1/certificates_test.go | 2 +- .../acceptance/openstack/containerinfra/v1/clusters_test.go | 2 +- .../openstack/containerinfra/v1/clustertemplates_test.go | 2 +- .../acceptance/openstack/containerinfra/v1/nodegroups_test.go | 2 +- internal/acceptance/openstack/containerinfra/v1/quotas_test.go | 2 +- internal/acceptance/openstack/db/v1/configurations_test.go | 2 +- internal/acceptance/openstack/db/v1/databases_test.go | 2 +- internal/acceptance/openstack/db/v1/flavors_test.go | 2 +- internal/acceptance/openstack/db/v1/instances_test.go | 2 +- internal/acceptance/openstack/db/v1/users_test.go | 2 +- internal/acceptance/openstack/dns/v2/recordsets_test.go | 2 +- internal/acceptance/openstack/identity/v2/extension_test.go | 2 +- internal/acceptance/openstack/identity/v2/tenant_test.go | 2 +- internal/acceptance/openstack/identity/v2/token_test.go | 2 +- internal/acceptance/openstack/identity/v2/user_test.go | 2 +- .../openstack/identity/v3/applicationcredentials_test.go | 2 +- internal/acceptance/openstack/identity/v3/catalog_test.go | 2 ++ internal/acceptance/openstack/identity/v3/credentials_test.go | 2 +- internal/acceptance/openstack/identity/v3/domains_test.go | 2 +- .../acceptance/openstack/identity/v3/ec2credentials_test.go | 2 +- internal/acceptance/openstack/identity/v3/endpoint_test.go | 2 +- internal/acceptance/openstack/identity/v3/federation_test.go | 2 +- internal/acceptance/openstack/identity/v3/groups_test.go | 2 +- internal/acceptance/openstack/identity/v3/limits_test.go | 2 +- internal/acceptance/openstack/identity/v3/oauth1_test.go | 2 +- internal/acceptance/openstack/identity/v3/osinherit_test.go | 2 +- internal/acceptance/openstack/identity/v3/policies_test.go | 2 +- .../acceptance/openstack/identity/v3/projectendpoint_test.go | 2 +- internal/acceptance/openstack/identity/v3/projects_test.go | 2 +- internal/acceptance/openstack/identity/v3/reauth_test.go | 2 +- internal/acceptance/openstack/identity/v3/regions_test.go | 2 +- .../acceptance/openstack/identity/v3/registeredlimits_test.go | 2 +- internal/acceptance/openstack/identity/v3/roles_test.go | 2 +- internal/acceptance/openstack/identity/v3/service_test.go | 2 +- internal/acceptance/openstack/identity/v3/token_test.go | 2 +- internal/acceptance/openstack/identity/v3/users_test.go | 2 +- internal/acceptance/openstack/image/v2/imagedata_test.go | 2 ++ internal/acceptance/openstack/networking/v2/apiversion_test.go | 2 +- .../networking/v2/extensions/bgp/peers/bgppeers_test.go | 2 ++ .../networking/v2/extensions/bgp/speakers/bgpspeakers_test.go | 2 ++ .../openstack/networking/v2/extensions/dns/dns_test.go | 2 +- .../networking/v2/extensions/layer3/portforwardings_test.go | 2 ++ .../openstack/networking/v2/extensions/mtu/mtu_test.go | 2 +- .../networking/v2/extensions/portsbinding/portsbinding_test.go | 2 +- .../openstack/networking/v2/extensions/qos/rules/rules_test.go | 2 ++ .../networking/v2/extensions/qos/ruletypes/ruletypes_test.go | 2 ++ .../networking/v2/extensions/rbacpolicies/rbacpolicies_test.go | 2 +- .../networking/v2/extensions/trunk_details/trunks_test.go | 2 +- .../openstack/networking/v2/extensions/trunks/trunks_test.go | 2 +- internal/acceptance/openstack/networking/v2/networks_test.go | 2 +- internal/acceptance/openstack/networking/v2/ports_test.go | 2 +- internal/acceptance/openstack/networking/v2/subnets_test.go | 2 +- internal/acceptance/openstack/objectstorage/v1/accounts_test.go | 2 +- .../acceptance/openstack/objectstorage/v1/containers_test.go | 2 +- internal/acceptance/openstack/objectstorage/v1/objects_test.go | 2 +- .../acceptance/openstack/objectstorage/v1/versioning_test.go | 2 +- .../acceptance/openstack/orchestration/v1/buildinfo_test.go | 2 +- .../acceptance/openstack/orchestration/v1/stackevents_test.go | 2 +- .../openstack/orchestration/v1/stackresources_test.go | 2 +- internal/acceptance/openstack/orchestration/v1/stacks_test.go | 2 +- .../openstack/orchestration/v1/stacktemplates_test.go | 2 +- .../acceptance/openstack/placement/v1/resourceproviders_test.go | 2 ++ .../openstack/sharedfilesystems/v2/availabilityzones_test.go | 2 +- .../openstack/sharedfilesystems/v2/messages/messages_test.go | 2 ++ .../acceptance/openstack/sharedfilesystems/v2/replicas_test.go | 2 +- .../openstack/sharedfilesystems/v2/schedulerstats_test.go | 2 +- .../openstack/sharedfilesystems/v2/securityservices_test.go | 2 +- .../acceptance/openstack/sharedfilesystems/v2/services_test.go | 2 +- .../openstack/sharedfilesystems/v2/shareaccessrules_test.go | 2 +- .../openstack/sharedfilesystems/v2/sharenetworks_test.go | 2 +- .../acceptance/openstack/sharedfilesystems/v2/shares_test.go | 2 +- .../openstack/sharedfilesystems/v2/sharetransfers_test.go | 2 +- .../openstack/sharedfilesystems/v2/sharetypes_test.go | 2 +- .../acceptance/openstack/sharedfilesystems/v2/snapshots_test.go | 2 +- internal/acceptance/openstack/workflow/v2/crontriggers_test.go | 2 ++ internal/acceptance/openstack/workflow/v2/executions_test.go | 2 ++ internal/acceptance/openstack/workflow/v2/workflows_test.go | 2 ++ 103 files changed, 121 insertions(+), 85 deletions(-) diff --git a/internal/acceptance/clients/testing/conditions_test.go b/internal/acceptance/clients/testing/conditions_test.go index afa8beb6b8..500dc0f3cc 100644 --- a/internal/acceptance/clients/testing/conditions_test.go +++ b/internal/acceptance/clients/testing/conditions_test.go @@ -1,3 +1,5 @@ +//go:build acceptance + package testing import ( diff --git a/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go index ca204eeecf..e35ae1e25a 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/nodes_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || baremetal || httpbasic + package httpbasic import ( diff --git a/internal/acceptance/openstack/baremetal/noauth/nodes_test.go b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go index 773dcdd9f6..b7e530d9d6 100644 --- a/internal/acceptance/openstack/baremetal/noauth/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || baremetal || noauth + package noauth import ( diff --git a/internal/acceptance/openstack/blockstorage/apiversions_test.go b/internal/acceptance/openstack/blockstorage/apiversions_test.go index c51561cd95..ab87334348 100644 --- a/internal/acceptance/openstack/blockstorage/apiversions_test.go +++ b/internal/acceptance/openstack/blockstorage/apiversions_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || apiversions package blockstorage diff --git a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go index 4343c5bbc3..58ea0880a0 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || snapshots package noauth diff --git a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go index e2617c04ec..90fa1bf7fe 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || volumes package noauth diff --git a/internal/acceptance/openstack/blockstorage/v2/backups_test.go b/internal/acceptance/openstack/blockstorage/v2/backups_test.go index eec56b929d..00ade44bde 100644 --- a/internal/acceptance/openstack/blockstorage/v2/backups_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/backups_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || backups package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/limits_test.go b/internal/acceptance/openstack/blockstorage/v2/limits_test.go index f8938493e8..42df33e58a 100644 --- a/internal/acceptance/openstack/blockstorage/v2/limits_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/limits_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || blockstorage || limits + package v2 import ( diff --git a/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go index 07d2d88d3d..34e4f7c75f 100644 --- a/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || schedulerhints package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go index 53efc3c7b3..e3373b0f61 100644 --- a/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/schedulerstats_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || schedulerstats package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/services_test.go b/internal/acceptance/openstack/blockstorage/v2/services_test.go index 0ebe7ffe5f..a53e52354d 100644 --- a/internal/acceptance/openstack/blockstorage/v2/services_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/services_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || services package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go index 8112d953d9..50ab203700 100644 --- a/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/snapshots_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || snapshots package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go index 6950fd9839..e6a21eb0ca 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || volumes package v2 diff --git a/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go index a9d397a1a7..45d9babbad 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumetenants_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || volumetenants package v2 diff --git a/internal/acceptance/openstack/blockstorage/v3/backups_test.go b/internal/acceptance/openstack/blockstorage/v3/backups_test.go index 81745e1371..0050c551ab 100644 --- a/internal/acceptance/openstack/blockstorage/v3/backups_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/backups_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || backups package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/limits_test.go b/internal/acceptance/openstack/blockstorage/v3/limits_test.go index 3f7024f5cb..55cb75ba73 100644 --- a/internal/acceptance/openstack/blockstorage/v3/limits_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/limits_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || blockstorage || limits + package v3 import ( diff --git a/internal/acceptance/openstack/blockstorage/v3/qos_test.go b/internal/acceptance/openstack/blockstorage/v3/qos_test.go index 2fc694792d..9c3d341cf1 100644 --- a/internal/acceptance/openstack/blockstorage/v3/qos_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/qos_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || blockstorage || qos + package v3 import ( diff --git a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go index 933aba3e83..14531c6ef3 100644 --- a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || quotasets +//go:build acceptance || blockstorage || quotasets package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go index 2a8377e8d8..b9c5a76c3c 100644 --- a/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || schedulerhints package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go b/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go index a3054997b5..e35cc420a9 100644 --- a/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/schedulerstats_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || schedulerstats package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/services_test.go b/internal/acceptance/openstack/blockstorage/v3/services_test.go index f9975db66f..675135bed4 100644 --- a/internal/acceptance/openstack/blockstorage/v3/services_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/services_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || services package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go index 97e043c1ad..4cb389882f 100644 --- a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || snapshots package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go index d8cc0dc8f0..99cf7a3eee 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumeattachments_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || volumeattachments package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go index 5c81df5605..fccecb43de 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || volumes package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go index 5ab1bd9ae3..e03be8d3c7 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetenants_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || volumetenants package v3 diff --git a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go index cffe82e93c..f19fd033de 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || blockstorage +//go:build acceptance || blockstorage || volumetypes package v3 diff --git a/internal/acceptance/openstack/containerinfra/v1/certificates_test.go b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go index 0c7be7b7a0..2f2e6546b4 100644 --- a/internal/acceptance/openstack/containerinfra/v1/certificates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/certificates_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || containerinfra +//go:build acceptance || containerinfra || certificates package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go index 725470c706..7a277b318a 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || containerinfra +//go:build acceptance || containerinfra || clusters package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go index 92975d5e76..fec59e8229 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clustertemplates_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || containerinfra +//go:build acceptance || containerinfra || clustertemplates package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go index 62499bb37b..3feba2bd64 100644 --- a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || containerinfra +//go:build acceptance || containerinfra || nodegroups package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/quotas_test.go b/internal/acceptance/openstack/containerinfra/v1/quotas_test.go index 1bdd8b05f9..ec28d54e0b 100644 --- a/internal/acceptance/openstack/containerinfra/v1/quotas_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/quotas_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || containerinfra +//go:build acceptance || containerinfra || quotas package v1 diff --git a/internal/acceptance/openstack/db/v1/configurations_test.go b/internal/acceptance/openstack/db/v1/configurations_test.go index 39462843dc..ef62d9db0d 100644 --- a/internal/acceptance/openstack/db/v1/configurations_test.go +++ b/internal/acceptance/openstack/db/v1/configurations_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || db +//go:build acceptance || db || configurations package v1 diff --git a/internal/acceptance/openstack/db/v1/databases_test.go b/internal/acceptance/openstack/db/v1/databases_test.go index 1d54d071ec..608e748bd6 100644 --- a/internal/acceptance/openstack/db/v1/databases_test.go +++ b/internal/acceptance/openstack/db/v1/databases_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || db +//go:build acceptance || db || databases package v1 diff --git a/internal/acceptance/openstack/db/v1/flavors_test.go b/internal/acceptance/openstack/db/v1/flavors_test.go index dad7c4a1e9..f5acb33282 100644 --- a/internal/acceptance/openstack/db/v1/flavors_test.go +++ b/internal/acceptance/openstack/db/v1/flavors_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || db +//go:build acceptance || db || flavors package v1 diff --git a/internal/acceptance/openstack/db/v1/instances_test.go b/internal/acceptance/openstack/db/v1/instances_test.go index 790918cf69..6250d665e3 100644 --- a/internal/acceptance/openstack/db/v1/instances_test.go +++ b/internal/acceptance/openstack/db/v1/instances_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || db +//go:build acceptance || db || instances package v1 diff --git a/internal/acceptance/openstack/db/v1/users_test.go b/internal/acceptance/openstack/db/v1/users_test.go index 707a4483b0..524832c7b7 100644 --- a/internal/acceptance/openstack/db/v1/users_test.go +++ b/internal/acceptance/openstack/db/v1/users_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || db +//go:build acceptance || db || users package v1 diff --git a/internal/acceptance/openstack/dns/v2/recordsets_test.go b/internal/acceptance/openstack/dns/v2/recordsets_test.go index 3cad12b88f..fd2e4d41bf 100644 --- a/internal/acceptance/openstack/dns/v2/recordsets_test.go +++ b/internal/acceptance/openstack/dns/v2/recordsets_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || dns || recordsets package v2 diff --git a/internal/acceptance/openstack/identity/v2/extension_test.go b/internal/acceptance/openstack/identity/v2/extension_test.go index 43fd837e4d..ada4ddef4f 100644 --- a/internal/acceptance/openstack/identity/v2/extension_test.go +++ b/internal/acceptance/openstack/identity/v2/extension_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || identity +//go:build acceptance || identity || extensions package v2 diff --git a/internal/acceptance/openstack/identity/v2/tenant_test.go b/internal/acceptance/openstack/identity/v2/tenant_test.go index f6e51bd3c6..d390f14d35 100644 --- a/internal/acceptance/openstack/identity/v2/tenant_test.go +++ b/internal/acceptance/openstack/identity/v2/tenant_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || identity +//go:build acceptance || identity || tenants package v2 diff --git a/internal/acceptance/openstack/identity/v2/token_test.go b/internal/acceptance/openstack/identity/v2/token_test.go index 1ea8d389a2..fba2dbc0a5 100644 --- a/internal/acceptance/openstack/identity/v2/token_test.go +++ b/internal/acceptance/openstack/identity/v2/token_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || identity +//go:build acceptance || identity || tokens package v2 diff --git a/internal/acceptance/openstack/identity/v2/user_test.go b/internal/acceptance/openstack/identity/v2/user_test.go index b13b422127..f54e218d30 100644 --- a/internal/acceptance/openstack/identity/v2/user_test.go +++ b/internal/acceptance/openstack/identity/v2/user_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || identity +//go:build acceptance || identity || users package v2 diff --git a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go index 90d9946ef5..1390e284a9 100644 --- a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || applicationcredentials package v3 diff --git a/internal/acceptance/openstack/identity/v3/catalog_test.go b/internal/acceptance/openstack/identity/v3/catalog_test.go index 6d15b1054b..e8991e686d 100644 --- a/internal/acceptance/openstack/identity/v3/catalog_test.go +++ b/internal/acceptance/openstack/identity/v3/catalog_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || identity || catalog + package v3 import ( diff --git a/internal/acceptance/openstack/identity/v3/credentials_test.go b/internal/acceptance/openstack/identity/v3/credentials_test.go index 45e8666699..a590d576bd 100644 --- a/internal/acceptance/openstack/identity/v3/credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/credentials_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || credentials package v3 diff --git a/internal/acceptance/openstack/identity/v3/domains_test.go b/internal/acceptance/openstack/identity/v3/domains_test.go index df426af684..3d1208ae9c 100644 --- a/internal/acceptance/openstack/identity/v3/domains_test.go +++ b/internal/acceptance/openstack/identity/v3/domains_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || domains package v3 diff --git a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go index cece5d9f31..05507792df 100644 --- a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || ec2credentials package v3 diff --git a/internal/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go index 91f755caca..ae2254102a 100644 --- a/internal/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || endpoints package v3 diff --git a/internal/acceptance/openstack/identity/v3/federation_test.go b/internal/acceptance/openstack/identity/v3/federation_test.go index 144134d49d..d8ceee1c9d 100644 --- a/internal/acceptance/openstack/identity/v3/federation_test.go +++ b/internal/acceptance/openstack/identity/v3/federation_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || federation package v3 diff --git a/internal/acceptance/openstack/identity/v3/groups_test.go b/internal/acceptance/openstack/identity/v3/groups_test.go index d615eb788f..fcb6be66a9 100644 --- a/internal/acceptance/openstack/identity/v3/groups_test.go +++ b/internal/acceptance/openstack/identity/v3/groups_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || groups package v3 diff --git a/internal/acceptance/openstack/identity/v3/limits_test.go b/internal/acceptance/openstack/identity/v3/limits_test.go index 010ae064e3..71ddda57cf 100644 --- a/internal/acceptance/openstack/identity/v3/limits_test.go +++ b/internal/acceptance/openstack/identity/v3/limits_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || limits package v3 diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index cd19b55449..2f5695f2f1 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || oauth1 package v3 diff --git a/internal/acceptance/openstack/identity/v3/osinherit_test.go b/internal/acceptance/openstack/identity/v3/osinherit_test.go index 868ed225b7..30a2f424f1 100644 --- a/internal/acceptance/openstack/identity/v3/osinherit_test.go +++ b/internal/acceptance/openstack/identity/v3/osinherit_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || osinherit package v3 diff --git a/internal/acceptance/openstack/identity/v3/policies_test.go b/internal/acceptance/openstack/identity/v3/policies_test.go index ec9f0c4634..1af2a96f1b 100644 --- a/internal/acceptance/openstack/identity/v3/policies_test.go +++ b/internal/acceptance/openstack/identity/v3/policies_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || policies package v3 diff --git a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go index 18590a82ae..e332e3aff8 100644 --- a/internal/acceptance/openstack/identity/v3/projectendpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/projectendpoint_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || projectendpoints package v3 diff --git a/internal/acceptance/openstack/identity/v3/projects_test.go b/internal/acceptance/openstack/identity/v3/projects_test.go index 7406879fd7..f19861aab5 100644 --- a/internal/acceptance/openstack/identity/v3/projects_test.go +++ b/internal/acceptance/openstack/identity/v3/projects_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || projects package v3 diff --git a/internal/acceptance/openstack/identity/v3/reauth_test.go b/internal/acceptance/openstack/identity/v3/reauth_test.go index e07fb485a1..838222eae8 100644 --- a/internal/acceptance/openstack/identity/v3/reauth_test.go +++ b/internal/acceptance/openstack/identity/v3/reauth_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || reauth package v3 diff --git a/internal/acceptance/openstack/identity/v3/regions_test.go b/internal/acceptance/openstack/identity/v3/regions_test.go index c71eb78eb8..5b2a0d3efa 100644 --- a/internal/acceptance/openstack/identity/v3/regions_test.go +++ b/internal/acceptance/openstack/identity/v3/regions_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || regions package v3 diff --git a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go index deb4806fda..bb4df961e2 100644 --- a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go +++ b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || registeredlimits package v3 diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index d4c58ac995..60b2d9782f 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || roles package v3 diff --git a/internal/acceptance/openstack/identity/v3/service_test.go b/internal/acceptance/openstack/identity/v3/service_test.go index 9c3ef84766..a4a2c43b53 100644 --- a/internal/acceptance/openstack/identity/v3/service_test.go +++ b/internal/acceptance/openstack/identity/v3/service_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || services package v3 diff --git a/internal/acceptance/openstack/identity/v3/token_test.go b/internal/acceptance/openstack/identity/v3/token_test.go index ce09930e21..d5274ff6b1 100644 --- a/internal/acceptance/openstack/identity/v3/token_test.go +++ b/internal/acceptance/openstack/identity/v3/token_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || tokens package v3 diff --git a/internal/acceptance/openstack/identity/v3/users_test.go b/internal/acceptance/openstack/identity/v3/users_test.go index 97a0ddd67b..5dd71a550f 100644 --- a/internal/acceptance/openstack/identity/v3/users_test.go +++ b/internal/acceptance/openstack/identity/v3/users_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || identity || users package v3 diff --git a/internal/acceptance/openstack/image/v2/imagedata_test.go b/internal/acceptance/openstack/image/v2/imagedata_test.go index 4ea210b945..31324e980b 100644 --- a/internal/acceptance/openstack/image/v2/imagedata_test.go +++ b/internal/acceptance/openstack/image/v2/imagedata_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || image || imagedata + package v2 import ( diff --git a/internal/acceptance/openstack/networking/v2/apiversion_test.go b/internal/acceptance/openstack/networking/v2/apiversion_test.go index 5a5af959f9..585615a0f2 100644 --- a/internal/acceptance/openstack/networking/v2/apiversion_test.go +++ b/internal/acceptance/openstack/networking/v2/apiversion_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || networking +//go:build acceptance || networking || apiversion package v2 diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go index 5cd0959765..9cca803be8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || networking || bgp || peers + package peers import ( diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go index ed0a381b15..5b50d6df59 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/speakers/bgpspeakers_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || networking || bgp || speakers + package speakers import ( diff --git a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index 6b84bdd9d4..555ebe28fa 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || networking +//go:build acceptance || networking || dns package dns diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index 216a997d5b..bd9ccf3ad4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || networking || layer3 || portforwardings + package layer3 import ( diff --git a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 018ffb8d81..71ad8d4581 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || networking +//go:build acceptance || networking || mtu package mtu 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 93347e627f..9e424d849d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || networking +//go:build acceptance || networking || portbinding package portsbinding diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go index 82e15899b3..c441b58a8b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || networking || qos || rules + package rules import ( diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go index 223f3ea7ac..470408b163 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || networking || qos || ruletypes + package ruletypes import ( diff --git a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go index 290cdcd5b2..3bd33becd6 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/rbacpolicies/rbacpolicies_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || networking || rbacpolicies package rbacpolicies diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go index dab26f9310..0daaca55e8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || trunks +//go:build acceptance || networking || trunks package trunk_details diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 2f2fc33f0f..0fbf1b81d8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || trunks +//go:build acceptance || networking || trunks package trunks diff --git a/internal/acceptance/openstack/networking/v2/networks_test.go b/internal/acceptance/openstack/networking/v2/networks_test.go index 6dedc21000..8dfa1986da 100644 --- a/internal/acceptance/openstack/networking/v2/networks_test.go +++ b/internal/acceptance/openstack/networking/v2/networks_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || networking +//go:build acceptance || networking || networks package v2 diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index d24145bf7e..af13180615 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || networking +//go:build acceptance || networking || ports package v2 diff --git a/internal/acceptance/openstack/networking/v2/subnets_test.go b/internal/acceptance/openstack/networking/v2/subnets_test.go index 90ee87a6fe..de58dc73f0 100644 --- a/internal/acceptance/openstack/networking/v2/subnets_test.go +++ b/internal/acceptance/openstack/networking/v2/subnets_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || networking +//go:build acceptance || networking || subnets package v2 diff --git a/internal/acceptance/openstack/objectstorage/v1/accounts_test.go b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go index 234e317adb..b46ea69c6d 100644 --- a/internal/acceptance/openstack/objectstorage/v1/accounts_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/accounts_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || objectstorage || accounts package v1 diff --git a/internal/acceptance/openstack/objectstorage/v1/containers_test.go b/internal/acceptance/openstack/objectstorage/v1/containers_test.go index 881bc93d02..6a5e326ce4 100644 --- a/internal/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/containers_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || objectstorage || containers package v1 diff --git a/internal/acceptance/openstack/objectstorage/v1/objects_test.go b/internal/acceptance/openstack/objectstorage/v1/objects_test.go index 36449f528e..5fcf806936 100644 --- a/internal/acceptance/openstack/objectstorage/v1/objects_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/objects_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || objectstorage || objects package v1 diff --git a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go index 5ec46167b9..7d52c8f1cc 100644 --- a/internal/acceptance/openstack/objectstorage/v1/versioning_test.go +++ b/internal/acceptance/openstack/objectstorage/v1/versioning_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || objectstorage || versioning package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go index 94435eef3e..3c276a5169 100644 --- a/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go +++ b/internal/acceptance/openstack/orchestration/v1/buildinfo_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || orchestration || buildinfo package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/stackevents_test.go b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go index 44ea23812c..66632321dc 100644 --- a/internal/acceptance/openstack/orchestration/v1/stackevents_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackevents_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || orchestration || stackevents package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/stackresources_test.go b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go index dfaedd756d..1e47f789b8 100644 --- a/internal/acceptance/openstack/orchestration/v1/stackresources_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stackresources_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || orchestration || stackresources package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/stacks_test.go b/internal/acceptance/openstack/orchestration/v1/stacks_test.go index 1774ccbc53..56bb779f9b 100644 --- a/internal/acceptance/openstack/orchestration/v1/stacks_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacks_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || orchestration || stacks package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go index b2feb84697..e49553361d 100644 --- a/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go +++ b/internal/acceptance/openstack/orchestration/v1/stacktemplates_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || orchestration || stacktemplates package v1 diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index b0b1bd5f3d..bd6c3f1fa4 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || placement || resourceproviders + package v1 import ( diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go index 6c10b644d8..adc17dc6c4 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/availabilityzones_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || availabilityzones package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go index 4b1d53e99d..32ad0f5528 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || sharedfilesystems || messages + package messages import ( diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go index a7c1e8e897..0d564c95e0 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || replicas package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go index c7ec12f606..6181f39760 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/schedulerstats_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || schedulerstats package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index 5f7e9ef76e..a149a83324 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || securityservices package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go index d9e5911e3a..e9a80f8675 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/services_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || services package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go index 9df9690add..ea467f3804 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || shareaccessrules package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index 3e7f8fcea0..c6a9bf1530 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || sharenetworks package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 028ae58977..3d8bb0d32e 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || shares package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go index 7faf6aa806..296b7ca807 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || share || transfers +//go:build acceptance || sharedfilesystems || sharetransfers package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index 94a978efc4..c62f76a754 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || sharetypes package v2 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index 5913c5888c..46b7c6d488 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -1,4 +1,4 @@ -//go:build acceptance +//go:build acceptance || sharedfilesystems || snapshots package v2 diff --git a/internal/acceptance/openstack/workflow/v2/crontriggers_test.go b/internal/acceptance/openstack/workflow/v2/crontriggers_test.go index d76f5902e8..01ae66d5ac 100644 --- a/internal/acceptance/openstack/workflow/v2/crontriggers_test.go +++ b/internal/acceptance/openstack/workflow/v2/crontriggers_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || workflow || crontriggers + package v2 import ( diff --git a/internal/acceptance/openstack/workflow/v2/executions_test.go b/internal/acceptance/openstack/workflow/v2/executions_test.go index 708484ad66..492aa51bb0 100644 --- a/internal/acceptance/openstack/workflow/v2/executions_test.go +++ b/internal/acceptance/openstack/workflow/v2/executions_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || workflow || executions + package v2 import ( diff --git a/internal/acceptance/openstack/workflow/v2/workflows_test.go b/internal/acceptance/openstack/workflow/v2/workflows_test.go index 9ce86b4225..1b024ddc35 100644 --- a/internal/acceptance/openstack/workflow/v2/workflows_test.go +++ b/internal/acceptance/openstack/workflow/v2/workflows_test.go @@ -1,3 +1,5 @@ +//go:build acceptance || workflow || workflows + package v2 import ( From 093f2887a9e6f87c258e77276d610dfb3a4bbb0e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 14:10:17 +0000 Subject: [PATCH 1826/2296] tests: Add missing module descriptions The internal/acceptance/openstack/sharedfilesystems/v2/messages module is also removed. Signed-off-by: Stephen Finucane --- internal/acceptance/openstack/baremetal/httpbasic/pkg.go | 5 +++++ internal/acceptance/openstack/baremetal/noauth/pkg.go | 5 +++++ internal/acceptance/openstack/baremetal/v1/pkg.go | 5 +++++ internal/acceptance/openstack/blockstorage/noauth/pkg.go | 5 +++++ internal/acceptance/openstack/blockstorage/v2/pkg.go | 4 +++- internal/acceptance/openstack/blockstorage/v3/pkg.go | 4 +++- internal/acceptance/openstack/clustering/v1/pkg.go | 5 ++++- internal/acceptance/openstack/compute/v2/pkg.go | 5 ++++- internal/acceptance/openstack/container/v1/pkg.go | 5 +++++ internal/acceptance/openstack/containerinfra/v1/pkg.go | 4 ++++ internal/acceptance/openstack/db/v1/pkg.go | 4 ++++ internal/acceptance/openstack/dns/v2/pkg.go | 5 +++++ internal/acceptance/openstack/identity/v2/pkg.go | 4 ++++ internal/acceptance/openstack/identity/v3/pkg.go | 4 ++++ internal/acceptance/openstack/image/v2/pkg.go | 5 +++++ internal/acceptance/openstack/keymanager/v1/pkg.go | 5 +++++ internal/acceptance/openstack/loadbalancer/v2/pkg.go | 4 ++++ internal/acceptance/openstack/messaging/v2/pkg.go | 5 +++++ internal/acceptance/openstack/networking/v2/pkg.go | 5 +++++ internal/acceptance/openstack/objectstorage/v1/pkg.go | 4 ++++ internal/acceptance/openstack/orchestration/v1/pkg.go | 5 +++++ internal/acceptance/openstack/placement/v1/pkg.go | 5 ++++- .../sharedfilesystems/v2/{messages => }/messages.go | 2 +- .../openstack/sharedfilesystems/v2/messages/pkg.go | 3 --- .../sharedfilesystems/v2/{messages => }/messages_test.go | 2 +- internal/acceptance/openstack/sharedfilesystems/v2/pkg.go | 4 +++- internal/acceptance/openstack/workflow/v2/pkg.go | 5 +++++ 27 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 internal/acceptance/openstack/baremetal/httpbasic/pkg.go create mode 100644 internal/acceptance/openstack/baremetal/noauth/pkg.go create mode 100644 internal/acceptance/openstack/baremetal/v1/pkg.go create mode 100644 internal/acceptance/openstack/blockstorage/noauth/pkg.go create mode 100644 internal/acceptance/openstack/container/v1/pkg.go create mode 100644 internal/acceptance/openstack/dns/v2/pkg.go create mode 100644 internal/acceptance/openstack/image/v2/pkg.go create mode 100644 internal/acceptance/openstack/keymanager/v1/pkg.go create mode 100644 internal/acceptance/openstack/messaging/v2/pkg.go create mode 100644 internal/acceptance/openstack/networking/v2/pkg.go create mode 100644 internal/acceptance/openstack/orchestration/v1/pkg.go rename internal/acceptance/openstack/sharedfilesystems/v2/{messages => }/messages.go (97%) delete mode 100644 internal/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go rename internal/acceptance/openstack/sharedfilesystems/v2/{messages => }/messages_test.go (99%) create mode 100644 internal/acceptance/openstack/workflow/v2/pkg.go diff --git a/internal/acceptance/openstack/baremetal/httpbasic/pkg.go b/internal/acceptance/openstack/baremetal/httpbasic/pkg.go new file mode 100644 index 0000000000..bfe9e98db9 --- /dev/null +++ b/internal/acceptance/openstack/baremetal/httpbasic/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || baremetal + +// The httpbasic package contains acceptance tests for the OpenStack Bare Metal v1 service with HTTP Basic authentation. + +package httpbasic diff --git a/internal/acceptance/openstack/baremetal/noauth/pkg.go b/internal/acceptance/openstack/baremetal/noauth/pkg.go new file mode 100644 index 0000000000..e601a40ce5 --- /dev/null +++ b/internal/acceptance/openstack/baremetal/noauth/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || baremetal + +// The noauth package contains acceptance tests for the OpenStack Bare Metal v1 service without authentation. + +package noauth diff --git a/internal/acceptance/openstack/baremetal/v1/pkg.go b/internal/acceptance/openstack/baremetal/v1/pkg.go new file mode 100644 index 0000000000..faf454b835 --- /dev/null +++ b/internal/acceptance/openstack/baremetal/v1/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || baremetal + +// The v1 package contains acceptance tests for the OpenStack Bare Metal v1 service. + +package v1 diff --git a/internal/acceptance/openstack/blockstorage/noauth/pkg.go b/internal/acceptance/openstack/blockstorage/noauth/pkg.go new file mode 100644 index 0000000000..60df33443c --- /dev/null +++ b/internal/acceptance/openstack/blockstorage/noauth/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || blockstorage + +// The noauth package contains acceptance tests for the OpenStack Block Storage v3 service with no authentation. + +package noauth diff --git a/internal/acceptance/openstack/blockstorage/v2/pkg.go b/internal/acceptance/openstack/blockstorage/v2/pkg.go index 31dd0ffcb0..039a168159 100644 --- a/internal/acceptance/openstack/blockstorage/v2/pkg.go +++ b/internal/acceptance/openstack/blockstorage/v2/pkg.go @@ -1,3 +1,5 @@ -// The v2 package contains acceptance tests for the Openstack Cinder V2 service. +//go:build acceptance || blockstorage + +// The v2 package contains acceptance tests for the Openstack Block Storage v2 service. package v2 diff --git a/internal/acceptance/openstack/blockstorage/v3/pkg.go b/internal/acceptance/openstack/blockstorage/v3/pkg.go index b86245c710..e0b01c5f49 100644 --- a/internal/acceptance/openstack/blockstorage/v3/pkg.go +++ b/internal/acceptance/openstack/blockstorage/v3/pkg.go @@ -1,3 +1,5 @@ -// The v3 package contains acceptance tests for the OpenStack Cinder V3 service. +//go:build acceptance || blockstorage + +// The v3 package contains acceptance tests for the OpenStack Block Storage v3 service. package v3 diff --git a/internal/acceptance/openstack/clustering/v1/pkg.go b/internal/acceptance/openstack/clustering/v1/pkg.go index e2200bc5ba..159aed7e11 100644 --- a/internal/acceptance/openstack/clustering/v1/pkg.go +++ b/internal/acceptance/openstack/clustering/v1/pkg.go @@ -1,2 +1,5 @@ -// Package v1 package contains acceptance tests for the Openstack Clustering V1 service. +//go:build acceptance || clustering + +// The v1 package contains acceptance tests for the Openstack Clustering v1 service. + package v1 diff --git a/internal/acceptance/openstack/compute/v2/pkg.go b/internal/acceptance/openstack/compute/v2/pkg.go index a57c1e7bfa..7ff7750b48 100644 --- a/internal/acceptance/openstack/compute/v2/pkg.go +++ b/internal/acceptance/openstack/compute/v2/pkg.go @@ -1,2 +1,5 @@ -// Package v2 package contains acceptance tests for the Openstack Compute V2 service. +//go:build acceptance || compute + +// Package v2 package contains acceptance tests for the Openstack Compute v2 service. + package v2 diff --git a/internal/acceptance/openstack/container/v1/pkg.go b/internal/acceptance/openstack/container/v1/pkg.go new file mode 100644 index 0000000000..357c83b3d4 --- /dev/null +++ b/internal/acceptance/openstack/container/v1/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || container + +// The v1 package contains acceptance tests for the Openstack Container v1 service. + +package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/pkg.go b/internal/acceptance/openstack/containerinfra/v1/pkg.go index b7b1f993d5..0babe5ed78 100644 --- a/internal/acceptance/openstack/containerinfra/v1/pkg.go +++ b/internal/acceptance/openstack/containerinfra/v1/pkg.go @@ -1 +1,5 @@ +//go:build acceptance || containerinfra + +// The v1 package contains acceptance tests for the Openstack Container Infra v1 service. + package v1 diff --git a/internal/acceptance/openstack/db/v1/pkg.go b/internal/acceptance/openstack/db/v1/pkg.go index b7b1f993d5..6b6f5ac92d 100644 --- a/internal/acceptance/openstack/db/v1/pkg.go +++ b/internal/acceptance/openstack/db/v1/pkg.go @@ -1 +1,5 @@ +//go:build acceptance || db + +// The v1 package contains acceptance tests for the Openstack DB v1 service. + package v1 diff --git a/internal/acceptance/openstack/dns/v2/pkg.go b/internal/acceptance/openstack/dns/v2/pkg.go new file mode 100644 index 0000000000..749b2380dd --- /dev/null +++ b/internal/acceptance/openstack/dns/v2/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || dns + +// The v1 package contains acceptance tests for the Openstack DNS v1 service. + +package v2 diff --git a/internal/acceptance/openstack/identity/v2/pkg.go b/internal/acceptance/openstack/identity/v2/pkg.go index 5ec3cc8e83..4c072f590d 100644 --- a/internal/acceptance/openstack/identity/v2/pkg.go +++ b/internal/acceptance/openstack/identity/v2/pkg.go @@ -1 +1,5 @@ +//go:build acceptance || identity + +// The v2 package contains acceptance tests for the Openstack Identity v2 service. + package v2 diff --git a/internal/acceptance/openstack/identity/v3/pkg.go b/internal/acceptance/openstack/identity/v3/pkg.go index eac3ae96a1..22c792fdda 100644 --- a/internal/acceptance/openstack/identity/v3/pkg.go +++ b/internal/acceptance/openstack/identity/v3/pkg.go @@ -1 +1,5 @@ +//go:build acceptance || identity + +// The v3 package contains acceptance tests for the Openstack Identity v3 service. + package v3 diff --git a/internal/acceptance/openstack/image/v2/pkg.go b/internal/acceptance/openstack/image/v2/pkg.go new file mode 100644 index 0000000000..a61c1825b4 --- /dev/null +++ b/internal/acceptance/openstack/image/v2/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || image + +// The v2 package contains acceptance tests for the Openstack Image v2 service. + +package v2 diff --git a/internal/acceptance/openstack/keymanager/v1/pkg.go b/internal/acceptance/openstack/keymanager/v1/pkg.go new file mode 100644 index 0000000000..9839bc5094 --- /dev/null +++ b/internal/acceptance/openstack/keymanager/v1/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || keymanager + +// The v1 package contains acceptance tests for the Openstack Keymanager v1 service. + +package v1 diff --git a/internal/acceptance/openstack/loadbalancer/v2/pkg.go b/internal/acceptance/openstack/loadbalancer/v2/pkg.go index 5ec3cc8e83..790c5b4737 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/pkg.go +++ b/internal/acceptance/openstack/loadbalancer/v2/pkg.go @@ -1 +1,5 @@ +//go:build acceptance || loadbalancer + +// The v2 package contains acceptance tests for the Openstack Loadbalancer v2 service. + package v2 diff --git a/internal/acceptance/openstack/messaging/v2/pkg.go b/internal/acceptance/openstack/messaging/v2/pkg.go new file mode 100644 index 0000000000..be815df451 --- /dev/null +++ b/internal/acceptance/openstack/messaging/v2/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || messaging + +// The v2 package contains acceptance tests for the Openstack Messaging v2 service. + +package v2 diff --git a/internal/acceptance/openstack/networking/v2/pkg.go b/internal/acceptance/openstack/networking/v2/pkg.go new file mode 100644 index 0000000000..01ac7a87bb --- /dev/null +++ b/internal/acceptance/openstack/networking/v2/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || networking + +// The v2 package contains acceptance tests for the Openstack Networking v2 service. + +package v2 diff --git a/internal/acceptance/openstack/objectstorage/v1/pkg.go b/internal/acceptance/openstack/objectstorage/v1/pkg.go index b7b1f993d5..d8a67e553c 100644 --- a/internal/acceptance/openstack/objectstorage/v1/pkg.go +++ b/internal/acceptance/openstack/objectstorage/v1/pkg.go @@ -1 +1,5 @@ +//go:build acceptance || objectstorage + +// The v1 package contains acceptance tests for the Openstack Object Storage v1 service. + package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/pkg.go b/internal/acceptance/openstack/orchestration/v1/pkg.go new file mode 100644 index 0000000000..e825736c0a --- /dev/null +++ b/internal/acceptance/openstack/orchestration/v1/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || orchestration + +// The v1 package contains acceptance tests for the Openstack Orchestration v1 service. + +package v1 diff --git a/internal/acceptance/openstack/placement/v1/pkg.go b/internal/acceptance/openstack/placement/v1/pkg.go index d8a8b16cb9..316618153a 100644 --- a/internal/acceptance/openstack/placement/v1/pkg.go +++ b/internal/acceptance/openstack/placement/v1/pkg.go @@ -1,2 +1,5 @@ -// Package placement contains acceptance tests for the Openstack Placement service. +//go:build acceptance || placement + +// The v1 package contains acceptance tests for the Openstack Placment v1 service. + package v1 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages.go similarity index 97% rename from internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go rename to internal/acceptance/openstack/sharedfilesystems/v2/messages.go index 1c50770e75..ac07ddeb62 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/messages.go @@ -1,4 +1,4 @@ -package messages +package v2 import ( "context" diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go deleted file mode 100644 index 0046213058..0000000000 --- a/internal/acceptance/openstack/sharedfilesystems/v2/messages/pkg.go +++ /dev/null @@ -1,3 +0,0 @@ -// The v2 package contains acceptance tests for the Openstack Manila V2 messages package - -package messages diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/messages_test.go similarity index 99% rename from internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go rename to internal/acceptance/openstack/sharedfilesystems/v2/messages_test.go index 32ad0f5528..c78e6d6696 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/messages/messages_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/messages_test.go @@ -1,6 +1,6 @@ //go:build acceptance || sharedfilesystems || messages -package messages +package v2 import ( "context" diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go b/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go index 5a5cd2b3f0..6c94d20ae8 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go @@ -1,3 +1,5 @@ -// The v2 package contains acceptance tests for the Openstack Manila V2 service. +//go:build acceptance || sharedfilesystems + +// The v2 package contains acceptance tests for the Openstack Shared File Systems v2 service. package v2 diff --git a/internal/acceptance/openstack/workflow/v2/pkg.go b/internal/acceptance/openstack/workflow/v2/pkg.go new file mode 100644 index 0000000000..716529fd0d --- /dev/null +++ b/internal/acceptance/openstack/workflow/v2/pkg.go @@ -0,0 +1,5 @@ +//go:build acceptance || workflow + +// The v2 package contains acceptance tests for the Openstack Workflow v2 service. + +package v2 From 0d610423d2d10010ef7750b1478567d90080aa19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:40:22 +0000 Subject: [PATCH 1827/2296] build(deps): bump kiegroup/git-backporting from 4.5.2 to 4.7.1 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.5.2 to 4.7.1. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/f4a2683189e62ce0acf2f3285b3b27debf2eacc9...fc5dba6703e6e81cbb4f52d15c12384c1bf896c5) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index 71ebbb5b01..db6d41bdac 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -36,7 +36,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@f4a2683189e62ce0acf2f3285b3b27debf2eacc9 + uses: kiegroup/git-backporting@fc5dba6703e6e81cbb4f52d15c12384c1bf896c5 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} From b81d5de9855e620d7067e12760201cbb7d995f35 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 26 Mar 2024 10:17:10 +0100 Subject: [PATCH 1828/2296] README: Document the auth options of v2 Provide examples that only use this repository and reframe the examples in terms of what the user has (clouds.yaml, openrc etc.). --- README.md | 194 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 127 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index fcdd4dba8e..660acba864 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,22 @@ # Gophercloud: an OpenStack SDK for Go [![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master) -[Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud) +[Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud/v2) Gophercloud is a Go SDK for OpenStack. Join us on kubernetes slack, on [#gophercloud](https://kubernetes.slack.com/archives/C05G4NJ6P6X). Visit [slack.k8s.io](https://slack.k8s.io) for an invitation. -This is the development branch of Gophercloud; stable releases are cut from the branch `v1`. +> **Note** +> This branch contains the current stable branch of Gophercloud: `v2`. +> The legacy stable version can be found in the `v1` branch. ## How to install Reference a Gophercloud package in your code: ```go -import "github.com/gophercloud/gophercloud" +import "github.com/gophercloud/gophercloud/v2" ``` Then update your `go.mod`: @@ -48,86 +50,140 @@ 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 +### Gophercloud authentication -Once you have access to your credentials, you can begin plugging them into -Gophercloud. The next step is authentication, which is handled by a base -"Provider" struct. There are number of ways to construct such a struct. +Gophercloud authentication is organized into two layered abstractions: +* `ProviderClient` holds the authentication token and can be used to build a + `ServiceClient`. +* `ServiceClient` specializes against one specific OpenStack module and can + directly be used to make API calls. -**With `gophercloud/utils`** +A provider client is a top-level client that all of your OpenStack service +clients derive from. The provider contains all of the authentication details +that allow your Go code to access the API - such as the base URL and token ID. + +One single Provider client can be used to build as many Service clients as needed. -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: +**With `clouds.yaml`** ```go +package main + import ( - "github.com/gophercloud/utils/openstack/clientconfig" -) + "context" -// You can also skip configuring this and instead set 'OS_CLOUD' in your -// environment -opts := new(clientconfig.ClientOpts) -opts.Cloud = "devstack-admin" + "github.com/gophercloud/gophercloud/v2/openstack" + "github.com/gophercloud/gophercloud/v2/openstack/config" + "github.com/gophercloud/gophercloud/v2/openstack/config/clouds" +) -provider, err := clientconfig.AuthenticatedClient(context.TODO(), opts) +func main() { + ctx := context.Background() + + // Fetch coordinates from a `cloud.yaml` in the current directory, or + // in the well-known config directories (different for each operating + // system). + authOptions, endpointOptions, tlsConfig, err := clouds.Parse() + if err != nil { + panic(err) + } + + // Call Keystone to get an authentication token, and use it to + // construct a ProviderClient. All functions hitting the OpenStack API + // accept a `context.Context` to enable tracing and cancellation. + providerClient, err := config.NewProviderClient(ctx, authOptions, config.WithTLSConfig(tlsConfig)) + if err != nil { + panic(err) + } + + // Use the ProviderClient and the endpoint options fetched from + // `clouds.yaml` to build a service client: a compute client in this + // case. Note that the contructor does not accept a `context.Context`: + // no further call to the OpenStack API is needed at this stage. + computeClient, err := openstack.NewComputeV2(providerClient, endpointOptions) + if err != nil { + panic(err) + } + + // use the computeClient +} ``` -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. +**With environment variables (`openrc`)** -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: +Gophercloud can parse the environment variables set by running `source openrc`: ```go -client, err := clientconfig.NewServiceClient("compute", opts) -``` +package main -**Without `gophercloud/utils`** +import ( + "context" + "os" -> *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`. + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" +) -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: +func main() { + ctx := context.Background() -```go -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" -) + opts, err := openstack.AuthOptionsFromEnv() + if err != nil { + panic(err) + } -// Option 1: Pass in the values yourself -opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - Username: "{username}", - Password: "{password}", -} + providerClient, err := openstack.AuthenticatedClient(ctx, opts) + if err != nil { + panic(err) + } + + computeClient, err := openstack.NewComputeV2(providerClient, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) + if err != nil { + panic(err) + } -// Option 2: Use a utility function to retrieve all your environment variables -opts, err := openstack.AuthOptionsFromEnv() + // use the computeClient +} ``` -Once you have the `opts` variable, you can pass it in and get back a -`ProviderClient` struct: +**Manually** + +You can also generate a "Provider" by passing in your credentials +explicitly: ```go -provider, err := openstack.AuthenticatedClient(context.TODO(), opts) -``` +package main -As above, you can then use this provider client to generate a service client -for a particular OpenStack service: +import ( + "context" -```go -client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), -}) + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack" +) + +func main() { + ctx := context.Background() + + providerClient, err := openstack.AuthenticatedClient(ctx, gophercloud.AuthOptions{ + IdentityEndpoint: "https://openstack.example.com:5000/v2.0", + Username: "username", + Password: "password", + }) + if err != nil { + panic(err) + } + + computeClient, err := openstack.NewComputeV2(providerClient, gophercloud.EndpointOpts{ + Region: "RegionName", + }) + if err != nil { + panic(err) + } + + // use the computeClient +} ``` ### Provision a server @@ -138,18 +194,22 @@ 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" +import "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + +func main() { + // [...] + + server, err := servers.Create(context.TODO(), computeClient, servers.CreateOpts{ + Name: "My new server!", + FlavorRef: "flavor_id", + ImageRef: "image_id", + }).Extract() -server, err := servers.Create(context.TODO(), client, servers.CreateOpts{ - Name: "My new server!", - FlavorRef: "flavor_id", - ImageRef: "image_id", -}).Extract() + // [...] ``` -The above code sample creates a new server with the parameters, and embodies the -new resource in the `server` variable (a -[`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct). +The above code sample creates a new server with the parameters, and returns a +[`servers.Server`](https://pkg.go.dev/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers#Server). ## Advanced Usage From 8762dc6944caa7ee5e7fe2cc17207b37a7216263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 22 Mar 2024 18:18:10 +0100 Subject: [PATCH 1829/2296] Update migration guide for v2 This is a start and should be updated as we merge more breaking changes. --- docs/MIGRATING.md | 153 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 132 insertions(+), 21 deletions(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index ef1a2bb900..565e8ca234 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -1,32 +1,143 @@ -# Compute +# Migration guide -## Floating IPs +Gophercloud follows [semver](https://semver.org/) and each major release brings +a number of changes breaking backward compatibility. This guide details those +changes and explains how to migrate from one major version of Gophercloud to +another. -* `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingip` is now `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips` -* `floatingips.Associate` and `floatingips.Disassociate` have been removed. -* `floatingips.DisassociateOpts` is now required to disassociate a Floating IP. +## From v1 to v2 -## Security Groups +### Change import path -* `secgroups.AddServerToGroup` is now `secgroups.AddServer`. -* `secgroups.RemoveServerFromGroup` is now `secgroups.RemoveServer`. +The module is now named `github.com/gophercloud/gophercloud/v2`. Consequently, +you need to update all your imports: -## Servers +```diff +import ( +- "github.com/gophercloud/gophercloud" +- "github.com/gophercloud/gophercloud/pagination" ++ "github.com/gophercloud/gophercloud/v2" ++ "github.com/gophercloud/gophercloud/v2/pagination" +) +``` -* `servers.Reboot` now requires a `servers.RebootOpts` struct: +### Go version - ```golang - rebootOpts := servers.RebootOpts{ - Type: servers.SoftReboot, - } - res := servers.Reboot(client, server.ID, rebootOpts) - ``` +The minimum go version for Gophercloud v2 is now v1.21.6. -# Identity +### Context-awareness -## V3 +Gophercloud is now context aware, for tracing and cancellation. All function +signatures triggering an HTTP call now take a `context.Context` as their first +argument. -### Tokens +While you previously called: +```go +myServer, err := servers.Get(client, server.ID) +``` -* `Token.ExpiresAt` is now of type `gophercloud.JSONRFC3339Milli` instead of - `time.Time` +You now need to pass it a context, for example: +```go +ctx := context.TODO() +myServer, err := servers.Get(ctx, client, server.ID) +``` + +Now that every method accept a context, it is no longer possible to attach +a context to the Provider client. Use per-call context instead. + +The `WaitFor` functions now take a context as well, and we've dropped the +timeout argument. This means that the following code: + +```go +err = attachments.WaitForStatus(client, attachment.ID, "attached", 60) +``` + +Must be changed to use a context with timeout. For example: +```go +ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) +defer cancel() + +err = attachments.WaitForStatus(ctx, client, attachment.ID, "attached") +``` + +### Type changes + +`loadbalancer/v2/pools/CreateOpts.Members` is now a slice of `CreateMemberOpts` +rather than a slice of `BatchUpdateMemberOpts`. + +`blockstorage/v3/volumes/CreateOpts.Multiattach` is removed. Use a volume type +with `multiattach` capability instead. + +The following structs are no longer comparable due to the addition of a non comparable field: +- `compute/v2/flavors/Flavor` +- `loadbalancer/v2/l7policies/CreateRuleOpts` +- `loadbalancer/v2/l7policies/UpdateOpts` +- `loadbalancer/v2/l7policies/UpdateRuleOpts` +- `loadbalancer/v2/listeners/ListOpts` +- `loadbalancer/v2/monitors/ListOpts` +- `loadbalancer/v2/monitors/CreateOpts` +- `loadbalancer/v2/monitors/UpdateOpts` +- `loadbalancer/v2/pools/ListOpts` + +This means that you were previously able to use `==` to compare these objects, +this is no longer the case with Gophercloud v2. + +### Image + +The `imageservice` service is renamed to simply `image` to conform with the other services. + +If you previously imported from +`github.com/gophercloud/gophercloud/v2/openstack/imageservice/`, you now need +to import from `github.com/gophercloud/gophercloud/v2/openstack/image/`. + +Additionally, `NewImageServiceV2()` is renamed `NewImageV2()`. + +### Baremetal inventory + +The Baremetal inventory types moved from +`baremetalintrospection/v1/introspection` to `baremetal/inventory`. This +includes `BootInfoType`, `CPUType`, `LLDPTLVType`, `InterfaceType`, +`InventoryType`, `MemoryType`, `RootDiskType`, `SystemFirmwareType`, +`SystemVendorType`, `ExtraHardwareDataType`, `ExtraHardwareData`, +`ExtraHardwareDataSection`, `NUMATopology`, `NUMACPU`, `NUMANIC`, and +`NUMARAM`. + +Additionally, a few of these types were renamed in the process: +- `ExtraHardwareDataType` became `ExtraDataType` +- `ExtraHardwareData` became `ExtraDataItem` +- `ExtraHardwareDataSection` became `ExtraHardwareDataSection` + +### Object storage + +Gophercloud now escapes container and object names in all `objects` and +`containers` functions. If you were previously escaping names (with, for +example, `url.PathEscape` or `url.QueryEscape`), then you should REMOVE that +and pass the intended names to Gophercloud directly. + +The `objectstorage/v1/containers.ListOpts#Full` and +`objectstorage/v1/objects.ListOpts#Full` properties are removed from the +Gophercloud API. Plaintext listing is unfixably wrong and won't handle special +characters reliably (i.e. `\n`). Object listing and container listing now +always behave like “Full” did. + +Empty container names, container names containing a slash (`/`), and empty +object names are now rejected in Gophercloud before any call to Swift. + +The `ErrInvalidContainerName` error has been moved from +`objectstorage/v1/containers` to `objectstorage/v1`. In addition, two new name +validation errors have been added: `objectstorage.v1.ErrEmptyContainerName` and +`objectstorage.v1.ErrEmptyObjectName`. + +The `objectstorage/v1/objects.Copy#Destination` field must be in the form +`/container/object`. The function will reject a destination path if it doesn't +start with a slash (`/`). + +### Removed services and extensions + +Support for services that are no longer supported upstream has been removed. +Users that still rely on theses old services should continue using Gophercloud v1. + +- Cinder (Blockstorage) v1 +- Neutron (Networking) LBaaS and LBaaS v2 extensions. They have been replaced by Octavia. +- Neutron (Networking) FWaaS extension. +- Poppy (CDNaaS). From ba9bd100176043bb454df02e0fd1f604a2b2ac4d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Apr 2024 14:11:11 +0100 Subject: [PATCH 1830/2296] objectstorage: Remove traces of etag validation This was never actually implemented. Remove the traces of same. Signed-off-by: Stephen Finucane --- openstack/objectstorage/v1/objects/errors.go | 13 ------------- openstack/objectstorage/v1/objects/requests.go | 4 +--- openstack/objectstorage/v1/objects/results.go | 4 ---- .../v1/objects/testing/requests_test.go | 18 ------------------ 4 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 openstack/objectstorage/v1/objects/errors.go diff --git a/openstack/objectstorage/v1/objects/errors.go b/openstack/objectstorage/v1/objects/errors.go deleted file mode 100644 index f48c01fbdb..0000000000 --- a/openstack/objectstorage/v1/objects/errors.go +++ /dev/null @@ -1,13 +0,0 @@ -package objects - -import "github.com/gophercloud/gophercloud/v2" - -// ErrWrongChecksum is the error when the checksum generated for an object -// doesn't match the ETAG header. -type ErrWrongChecksum struct { - gophercloud.BaseError -} - -func (e ErrWrongChecksum) Error() string { - return "Local checksum does not match API ETag header" -} diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index 44a46d76d0..a9001f1396 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -249,9 +249,7 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str } // Create is a function that creates a new object or replaces an existing -// object. If the returned response's ETag header fails to match the local -// checksum, the failed request will automatically be retried up to a maximum -// of 3 times. +// object. func Create(ctx context.Context, c *gophercloud.ServiceClient, containerName, objectName string, opts CreateOptsBuilder) (r CreateResult) { url, err := createURL(c, containerName, objectName) if err != nil { diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index 15bb57c748..b9f872e874 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -329,15 +329,11 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { // CreateResult represents the result of a create operation. type CreateResult struct { - checksum string gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Create. func (r CreateResult) Extract() (*CreateHeader, error) { - //if r.Header.Get("ETag") != fmt.Sprintf("%x", localChecksum) { - // return nil, ErrWrongChecksum{} - //} var s CreateHeader err := r.ExtractInto(&s) return &s, err diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index bb9b5d5ac5..f20bdb9f0b 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -330,24 +330,6 @@ func TestCreateObjectWithoutContentType(t *testing.T) { th.AssertNoErr(t, res.Err) } -/* -func TestErrorIsRaisedForChecksumMismatch(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("ETag", "acbd18db4cc2f85cedef654fccc4a4d8") - w.WriteHeader(http.StatusCreated) - }) - - content := strings.NewReader("The sky was the color of television, tuned to a dead channel.") - res := Create(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", &CreateOpts{Content: content}) - - err := fmt.Errorf("Local checksum does not match API ETag header") - th.AssertDeepEquals(t, err, res.Err) -} -*/ - func TestCopyObject(t *testing.T) { t.Run("simple", func(t *testing.T) { th.SetupHTTP() From 82e53934ab4bcd5ea206e9f3a44a6b0de0cebabe Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 4 Apr 2024 11:04:59 +0100 Subject: [PATCH 1831/2296] compute: Fix mistakes in docs These were missed while folding in the scheduler hints extension. Signed-off-by: Stephen Finucane --- openstack/compute/v2/servers/doc.go | 68 +++++++++--------------- openstack/compute/v2/servers/requests.go | 12 ++--- 2 files changed, 31 insertions(+), 49 deletions(-) diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index b6a435e2bf..76c4a94b3c 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -60,19 +60,13 @@ Example to Create a Server Example to Add a Server to a Server Group - schedulerHints := schedulerhints.SchedulerHints{ - Group: "servergroup-uuid", - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_name", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - } - - createOpts := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - SchedulerHints: schedulerHints, + createOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + SchedulerHints: servers.SchedulerHints{ + Group: "servergroup-uuid", + }, } server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() @@ -82,21 +76,15 @@ Example to Add a Server to a Server Group Example to Place Server B on a Different Host than Server A - schedulerHints := schedulerhints.SchedulerHints{ - DifferentHost: []string{ - "server-a-uuid", - } - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_b", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - } - - createOpts := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - SchedulerHints: schedulerHints, + createOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + SchedulerHints: servers.SchedulerHints{ + DifferentHost: []string{ + "server-a-uuid", + } + }, } server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() @@ -106,21 +94,15 @@ Example to Place Server B on a Different Host than Server A Example to Place Server B on the Same Host as Server A - schedulerHints := schedulerhints.SchedulerHints{ - SameHost: []string{ - "server-a-uuid", - } - } - - serverCreateOpts := servers.CreateOpts{ - Name: "server_b", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - } - - createOpts := schedulerhints.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - SchedulerHints: schedulerHints, + createOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + SchedulerHints: servers.SchedulerHints{ + SameHost: []string{ + "server-a-uuid", + } + }, } server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 1b45d823fd..c5d0b79ef4 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -173,7 +173,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa if opts.Group != "" { if !uuidRegex.MatchString(opts.Group) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.Group" + err.Argument = "servers.SchedulerHints.Group" err.Value = opts.Group err.Info = "Group must be a UUID" return nil, err @@ -185,7 +185,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa for _, diffHost := range opts.DifferentHost { if !uuidRegex.MatchString(diffHost) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.DifferentHost" + err.Argument = "servers.SchedulerHints.DifferentHost" err.Value = opts.DifferentHost err.Info = "The hosts must be in UUID format." return nil, err @@ -198,7 +198,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa for _, sameHost := range opts.SameHost { if !uuidRegex.MatchString(sameHost) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.SameHost" + err.Argument = "servers.SchedulerHints.SameHost" err.Value = opts.SameHost err.Info = "The hosts must be in UUID format." return nil, err @@ -222,7 +222,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa if len(opts.Query) > 0 { if len(opts.Query) < 3 { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.Query" + err.Argument = "servers.SchedulerHints.Query" err.Value = opts.Query err.Info = "Must be a conditional statement in the format of [op,variable,value]" return nil, err @@ -232,7 +232,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa b, err := json.Marshal(opts.Query) if err != nil { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.Query" + err.Argument = "servers.SchedulerHints.Query" err.Value = opts.Query err.Info = "Must be a conditional statement in the format of [op,variable,value]" return nil, err @@ -252,7 +252,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa if opts.BuildNearHostIP != "" { if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.BuildNearHostIP" + err.Argument = "servers.SchedulerHints.BuildNearHostIP" err.Value = opts.BuildNearHostIP err.Info = "Must be a valid subnet in the form 192.168.1.1/24" return nil, err From 9d01333d697cf2d9ff8bba5cc7a8cc786e47a8ba Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 4 Apr 2024 14:00:10 +0100 Subject: [PATCH 1832/2296] compute: Remove removed proxy APIs (defsecrules) All APIs have been removed from Nova as they were related to nova-network Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/compute.go | 34 ----- .../openstack/compute/v2/defsecrules_test.go | 62 -------- openstack/compute/v2/defsecrules/doc.go | 55 ------- openstack/compute/v2/defsecrules/requests.go | 79 ---------- openstack/compute/v2/defsecrules/results.go | 77 ---------- .../compute/v2/defsecrules/testing/doc.go | 2 - .../v2/defsecrules/testing/fixtures_test.go | 143 ------------------ .../v2/defsecrules/testing/requests_test.go | 128 ---------------- openstack/compute/v2/defsecrules/urls.go | 13 -- 9 files changed, 593 deletions(-) delete mode 100644 internal/acceptance/openstack/compute/v2/defsecrules_test.go delete mode 100644 openstack/compute/v2/defsecrules/doc.go delete mode 100644 openstack/compute/v2/defsecrules/requests.go delete mode 100644 openstack/compute/v2/defsecrules/results.go delete mode 100644 openstack/compute/v2/defsecrules/testing/doc.go delete mode 100644 openstack/compute/v2/defsecrules/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/defsecrules/testing/requests_test.go delete mode 100644 openstack/compute/v2/defsecrules/urls.go diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index 35eb043504..646013a2da 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -16,7 +16,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" - dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" @@ -179,27 +178,6 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, return newServer, nil } -// CreateDefaultRule will create a default security group rule with a -// random port range between 80 and 90. An error will be returned if -// a default rule was unable to be created. -func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.DefaultRule, error) { - createOpts := dsr.CreateOpts{ - FromPort: tools.RandomInt(80, 89), - ToPort: tools.RandomInt(90, 99), - IPProtocol: "TCP", - CIDR: "0.0.0.0/0", - } - - defaultRule, err := dsr.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return *defaultRule, err - } - - t.Logf("Created default rule: %s", defaultRule.ID) - - return *defaultRule, nil -} - // CreateFlavor will create a flavor with a random name. // An error will be returned if the flavor could not be created. func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) { @@ -836,18 +814,6 @@ func DeleteAggregate(t *testing.T, client *gophercloud.ServiceClient, aggregate t.Logf("Deleted aggregate: %d", aggregate.ID) } -// DeleteDefaultRule deletes a default security group rule. -// A fatal error will occur if the rule failed to delete. This works best when -// using it as a deferred function. -func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) { - err := dsr.Delete(context.TODO(), client, defaultRule.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err) - } - - t.Logf("Deleted default rule: %s", defaultRule.ID) -} - // DeleteFlavor will delete a flavor. A fatal error will occur if the flavor // could not be deleted. This works best when using it as a deferred function. func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) { diff --git a/internal/acceptance/openstack/compute/v2/defsecrules_test.go b/internal/acceptance/openstack/compute/v2/defsecrules_test.go deleted file mode 100644 index 8b543feebf..0000000000 --- a/internal/acceptance/openstack/compute/v2/defsecrules_test.go +++ /dev/null @@ -1,62 +0,0 @@ -//go:build acceptance || compute || defsecrules - -package v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - dsr "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestDefSecRulesList(t *testing.T) { - clients.RequireAdmin(t) - clients.RequireNovaNetwork(t) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - allPages, err := dsr.List(client).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allDefaultRules, err := dsr.ExtractDefaultRules(allPages) - th.AssertNoErr(t, err) - - for _, defaultRule := range allDefaultRules { - tools.PrintResource(t, defaultRule) - } -} - -func TestDefSecRulesCreate(t *testing.T) { - clients.RequireAdmin(t) - clients.RequireNovaNetwork(t) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - defaultRule, err := CreateDefaultRule(t, client) - th.AssertNoErr(t, err) - defer DeleteDefaultRule(t, client, defaultRule) - - tools.PrintResource(t, defaultRule) -} - -func TestDefSecRulesGet(t *testing.T) { - clients.RequireAdmin(t) - clients.RequireNovaNetwork(t) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - defaultRule, err := CreateDefaultRule(t, client) - th.AssertNoErr(t, err) - defer DeleteDefaultRule(t, client, defaultRule) - - newDefaultRule, err := dsr.Get(context.TODO(), client, defaultRule.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newDefaultRule) -} diff --git a/openstack/compute/v2/defsecrules/doc.go b/openstack/compute/v2/defsecrules/doc.go deleted file mode 100644 index 724402ede7..0000000000 --- a/openstack/compute/v2/defsecrules/doc.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Package defsecrules enables management of default security group rules. - -Default security group rules are rules that are managed in the "default" -security group. - -This is only applicable in environments running nova-network. This package will -not work if the OpenStack environment is running the OpenStack Networking -(Neutron) service. - -Example of Listing Default Security Group Rules - - allPages, err := defsecrules.List(computeClient).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allDefaultRules, err := defsecrules.ExtractDefaultRules(allPages) - if err != nil { - panic(err) - } - - for _, df := range allDefaultRules { - fmt.Printf("%+v\n", df) - } - -Example of Retrieving a Default Security Group Rule - - rule, err := defsecrules.Get(context.TODO(), computeClient, "rule-id").Extract() - if err != nil { - panic(err) - } - -Example of Creating a Default Security Group Rule - - createOpts := defsecrules.CreateOpts{ - IPProtocol: "TCP", - FromPort: 80, - ToPort: 80, - CIDR: "10.10.12.0/24", - } - - rule, err := defsecrules.Create(context.TODO(), computeClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example of Deleting a Default Security Group Rule - - err := defsecrules.Delete(context.TODO(), computeClient, "rule-id").ExtractErr() - if err != nil { - panic(err) - } -*/ -package defsecrules diff --git a/openstack/compute/v2/defsecrules/requests.go b/openstack/compute/v2/defsecrules/requests.go deleted file mode 100644 index 21c29fb351..0000000000 --- a/openstack/compute/v2/defsecrules/requests.go +++ /dev/null @@ -1,79 +0,0 @@ -package defsecrules - -import ( - "context" - "strings" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// List will return a collection of default rules. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, rootURL(client), func(r pagination.PageResult) pagination.Page { - return DefaultRulePage{pagination.SinglePageBase(r)} - }) -} - -// CreateOpts represents the configuration for adding a new default rule. -type CreateOpts struct { - // The lower bound of the port range that will be opened. - FromPort int `json:"from_port"` - - // The upper bound of the port range that will be opened. - ToPort int `json:"to_port"` - - // The protocol type that will be allowed, e.g. TCP. - IPProtocol string `json:"ip_protocol" required:"true"` - - // ONLY required if FromGroupID is blank. This represents the IP range that - // will be the source of network traffic to your security group. - // - // Use 0.0.0.0/0 to allow all IPv4 addresses. - // Use ::/0 to allow all IPv6 addresses. - CIDR string `json:"cidr,omitempty"` -} - -// CreateOptsBuilder builds the create rule options into a serializable format. -type CreateOptsBuilder interface { - ToRuleCreateMap() (map[string]interface{}, error) -} - -// ToRuleCreateMap builds the create rule options into a serializable format. -func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { - if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" { - return nil, gophercloud.ErrMissingInput{Argument: "FromPort"} - } - if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" { - return nil, gophercloud.ErrMissingInput{Argument: "ToPort"} - } - return gophercloud.BuildRequestBody(opts, "security_group_default_rule") -} - -// Create is the operation responsible for creating a new default rule. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToRuleCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, rootURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get will return details for a particular default rule. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, resourceURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete will permanently delete a rule the project's default security group. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, resourceURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/defsecrules/results.go b/openstack/compute/v2/defsecrules/results.go deleted file mode 100644 index d10226ec94..0000000000 --- a/openstack/compute/v2/defsecrules/results.go +++ /dev/null @@ -1,77 +0,0 @@ -package defsecrules - -import ( - "encoding/json" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// DefaultRule represents a rule belonging to the "default" security group. -// It is identical to an openstack/compute/v2/secgroups.Rule. -type DefaultRule secgroups.Rule - -func (r *DefaultRule) UnmarshalJSON(b []byte) error { - var s secgroups.Rule - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = DefaultRule(s) - return nil -} - -// DefaultRulePage is a single page of a DefaultRule collection. -type DefaultRulePage struct { - pagination.SinglePageBase -} - -// 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 -} - -// ExtractDefaultRules returns a slice of DefaultRules contained in a single -// page of results. -func ExtractDefaultRules(r pagination.Page) ([]DefaultRule, error) { - var s struct { - DefaultRules []DefaultRule `json:"security_group_default_rules"` - } - err := (r.(DefaultRulePage)).ExtractInto(&s) - return s.DefaultRules, err -} - -type commonResult struct { - gophercloud.Result -} - -// CreateResult represents the result of a create operation. -type CreateResult struct { - commonResult -} - -// GetResult represents the result of a get operation. -type GetResult struct { - commonResult -} - -// Extract will extract a DefaultRule struct from a Create or Get response. -func (r commonResult) Extract() (*DefaultRule, error) { - var s struct { - DefaultRule DefaultRule `json:"security_group_default_rule"` - } - err := r.ExtractInto(&s) - return &s.DefaultRule, err -} - -// 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 -} diff --git a/openstack/compute/v2/defsecrules/testing/doc.go b/openstack/compute/v2/defsecrules/testing/doc.go deleted file mode 100644 index 6eeb60f057..0000000000 --- a/openstack/compute/v2/defsecrules/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// defsecrules unit tests -package testing diff --git a/openstack/compute/v2/defsecrules/testing/fixtures_test.go b/openstack/compute/v2/defsecrules/testing/fixtures_test.go deleted file mode 100644 index 4a8547c9a0..0000000000 --- a/openstack/compute/v2/defsecrules/testing/fixtures_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const rootPath = "/os-security-group-default-rules" - -func mockListRulesResponse(t *testing.T) { - th.Mux.HandleFunc(rootPath, 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, ` -{ - "security_group_default_rules": [ - { - "from_port": 80, - "id": "{ruleID}", - "ip_protocol": "TCP", - "ip_range": { - "cidr": "10.10.10.0/24" - }, - "to_port": 80 - } - ] -} - `) - }) -} - -func mockCreateRuleResponse(t *testing.T) { - th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - th.TestJSONRequest(t, r, ` -{ - "security_group_default_rule": { - "ip_protocol": "TCP", - "from_port": 80, - "to_port": 80, - "cidr": "10.10.12.0/24" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "security_group_default_rule": { - "from_port": 80, - "id": "{ruleID}", - "ip_protocol": "TCP", - "ip_range": { - "cidr": "10.10.12.0/24" - }, - "to_port": 80 - } -} -`) - }) -} - -func mockCreateRuleResponseICMPZero(t *testing.T) { - th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - th.TestJSONRequest(t, r, ` -{ - "security_group_default_rule": { - "ip_protocol": "ICMP", - "from_port": 0, - "to_port": 0, - "cidr": "10.10.12.0/24" - } -} - `) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` -{ - "security_group_default_rule": { - "from_port": 0, - "id": "{ruleID}", - "ip_protocol": "ICMP", - "ip_range": { - "cidr": "10.10.12.0/24" - }, - "to_port": 0 - } -} -`) - }) -} - -func mockGetRuleResponse(t *testing.T, ruleID string) { - url := rootPath + "/" + ruleID - th.Mux.HandleFunc(url, 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, ` -{ - "security_group_default_rule": { - "id": "{ruleID}", - "from_port": 80, - "to_port": 80, - "ip_protocol": "TCP", - "ip_range": { - "cidr": "10.10.12.0/24" - } - } -} - `) - }) -} - -func mockDeleteRuleResponse(t *testing.T, ruleID string) { - url := rootPath + "/" + ruleID - th.Mux.HandleFunc(url, 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) - }) -} diff --git a/openstack/compute/v2/defsecrules/testing/requests_test.go b/openstack/compute/v2/defsecrules/testing/requests_test.go deleted file mode 100644 index 9779fcda1f..0000000000 --- a/openstack/compute/v2/defsecrules/testing/requests_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/defsecrules" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const ruleID = "{ruleID}" - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockListRulesResponse(t) - - count := 0 - - err := defsecrules.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := defsecrules.ExtractDefaultRules(page) - th.AssertNoErr(t, err) - - expected := []defsecrules.DefaultRule{ - { - FromPort: 80, - ID: ruleID, - IPProtocol: "TCP", - IPRange: secgroups.IPRange{CIDR: "10.10.10.0/24"}, - ToPort: 80, - }, - } - - th.CheckDeepEquals(t, expected, actual) - - return true, nil - }) - - th.AssertNoErr(t, err) - th.AssertEquals(t, 1, count) -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockCreateRuleResponse(t) - - opts := defsecrules.CreateOpts{ - IPProtocol: "TCP", - FromPort: 80, - ToPort: 80, - CIDR: "10.10.12.0/24", - } - - group, err := defsecrules.Create(context.TODO(), client.ServiceClient(), opts).Extract() - th.AssertNoErr(t, err) - - expected := &defsecrules.DefaultRule{ - ID: ruleID, - FromPort: 80, - ToPort: 80, - IPProtocol: "TCP", - IPRange: secgroups.IPRange{CIDR: "10.10.12.0/24"}, - } - th.AssertDeepEquals(t, expected, group) -} - -func TestCreateICMPZero(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockCreateRuleResponseICMPZero(t) - - opts := defsecrules.CreateOpts{ - IPProtocol: "ICMP", - FromPort: 0, - ToPort: 0, - CIDR: "10.10.12.0/24", - } - - group, err := defsecrules.Create(context.TODO(), client.ServiceClient(), opts).Extract() - th.AssertNoErr(t, err) - - expected := &defsecrules.DefaultRule{ - ID: ruleID, - FromPort: 0, - ToPort: 0, - IPProtocol: "ICMP", - IPRange: secgroups.IPRange{CIDR: "10.10.12.0/24"}, - } - th.AssertDeepEquals(t, expected, group) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockGetRuleResponse(t, ruleID) - - group, err := defsecrules.Get(context.TODO(), client.ServiceClient(), ruleID).Extract() - th.AssertNoErr(t, err) - - expected := &defsecrules.DefaultRule{ - ID: ruleID, - FromPort: 80, - ToPort: 80, - IPProtocol: "TCP", - IPRange: secgroups.IPRange{CIDR: "10.10.12.0/24"}, - } - - th.AssertDeepEquals(t, expected, group) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - mockDeleteRuleResponse(t, ruleID) - - err := defsecrules.Delete(context.TODO(), client.ServiceClient(), ruleID).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/defsecrules/urls.go b/openstack/compute/v2/defsecrules/urls.go deleted file mode 100644 index 2cba51d3fc..0000000000 --- a/openstack/compute/v2/defsecrules/urls.go +++ /dev/null @@ -1,13 +0,0 @@ -package defsecrules - -import "github.com/gophercloud/gophercloud/v2" - -const rulepath = "os-security-group-default-rules" - -func resourceURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(rulepath, id) -} - -func rootURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL(rulepath) -} From 2d1f7c588d2c5bd887d8cc5d1e92c33ef1e7bb05 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 4 Apr 2024 14:03:15 +0100 Subject: [PATCH 1833/2296] compute: Remove deprecated proxy APIs (floatingips) All APIs are deprecated in Nova since 2.36. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/compute.go | 83 ------- .../openstack/compute/v2/floatingip_test.go | 124 ---------- openstack/compute/v2/floatingips/doc.go | 68 ------ openstack/compute/v2/floatingips/requests.go | 121 ---------- openstack/compute/v2/floatingips/results.go | 119 ---------- .../compute/v2/floatingips/testing/doc.go | 2 - .../v2/floatingips/testing/fixtures_test.go | 223 ------------------ .../v2/floatingips/testing/requests_test.go | 112 --------- openstack/compute/v2/floatingips/urls.go | 37 --- 9 files changed, 889 deletions(-) delete mode 100644 internal/acceptance/openstack/compute/v2/floatingip_test.go delete mode 100644 openstack/compute/v2/floatingips/doc.go delete mode 100644 openstack/compute/v2/floatingips/requests.go delete mode 100644 openstack/compute/v2/floatingips/results.go delete mode 100644 openstack/compute/v2/floatingips/testing/doc.go delete mode 100644 openstack/compute/v2/floatingips/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/floatingips/testing/requests_test.go delete mode 100644 openstack/compute/v2/floatingips/urls.go diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index 646013a2da..a450f07c27 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -17,7 +17,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/quotasets" @@ -33,40 +32,6 @@ import ( "golang.org/x/crypto/ssh" ) -// AssociateFloatingIP will associate a floating IP with an instance. An error -// will be returned if the floating IP was unable to be associated. -func AssociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) error { - associateOpts := floatingips.AssociateOpts{ - FloatingIP: floatingIP.IP, - } - - t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID) - err := floatingips.AssociateInstance(context.TODO(), client, server.ID, associateOpts).ExtractErr() - if err != nil { - return err - } - - return nil -} - -// AssociateFloatingIPWithFixedIP will associate a floating IP with an -// instance's specific fixed IP. An error will be returend if the floating IP -// was unable to be associated. -func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server, fixedIP string) error { - associateOpts := floatingips.AssociateOpts{ - FloatingIP: floatingIP.IP, - FixedIP: fixedIP, - } - - t.Logf("Attempting to associate floating IP %s to fixed IP %s on instance %s", floatingIP.IP, fixedIP, server.ID) - err := floatingips.AssociateInstance(context.TODO(), client, server.ID, associateOpts).ExtractErr() - if err != nil { - return err - } - - return nil -} - // AttachInterface will create and attach an interface on a given server. // An error will returned if the interface could not be created. func AttachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*attachinterfaces.Interface, error) { @@ -215,26 +180,6 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Fla return flavor, nil } -// CreateFloatingIP will allocate a floating IP. -// An error will be returend if one was unable to be allocated. -func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) { - choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - t.Fatal(err) - } - - createOpts := floatingips.CreateOpts{ - Pool: choices.FloatingIPPoolName, - } - floatingIP, err := floatingips.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - return floatingIP, err - } - - t.Logf("Created floating IP: %s", floatingIP.ID) - return floatingIP, nil -} - func createKey() (string, error) { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { @@ -825,18 +770,6 @@ func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavo t.Logf("Deleted flavor: %s", flavor.ID) } -// DeleteFloatingIP will de-allocate a floating IP. A fatal error will occur if -// the floating IP failed to de-allocate. This works best when using it as a -// deferred function. -func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) { - err := floatingips.Delete(context.TODO(), client, floatingIP.ID).ExtractErr() - if err != nil { - t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err) - } - - t.Logf("Deleted floating IP: %s", floatingIP.ID) -} - // DeleteKeyPair will delete a specified keypair. A fatal error will occur if // the keypair failed to be deleted. This works best when used as a deferred // function. @@ -939,22 +872,6 @@ func DetachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID, t.Logf("Detached interface %s from server %s", portID, serverID) } -// DisassociateFloatingIP will disassociate a floating IP from an instance. A -// fatal error will occur if the floating IP failed to disassociate. This works -// best when using it as a deferred function. -func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) { - disassociateOpts := floatingips.DisassociateOpts{ - FloatingIP: floatingIP.IP, - } - - err := floatingips.DisassociateInstance(context.TODO(), client, server.ID, disassociateOpts).ExtractErr() - if err != nil { - t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err) - } - - t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID) -} - // GetNetworkIDFromOSNetworks will return the network ID from a specified network // UUID using the os-networks API extension. An error will be returned if the // network could not be retrieved. diff --git a/internal/acceptance/openstack/compute/v2/floatingip_test.go b/internal/acceptance/openstack/compute/v2/floatingip_test.go deleted file mode 100644 index 7c91d0c068..0000000000 --- a/internal/acceptance/openstack/compute/v2/floatingip_test.go +++ /dev/null @@ -1,124 +0,0 @@ -//go:build acceptance || compute || servers - -package v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestFloatingIPsCreateDelete(t *testing.T) { - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - floatingIP, err := CreateFloatingIP(t, client) - th.AssertNoErr(t, err) - defer DeleteFloatingIP(t, client, floatingIP) - - tools.PrintResource(t, floatingIP) - - allPages, err := floatingips.List(client).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, fip := range allFloatingIPs { - tools.PrintResource(t, floatingIP) - - if fip.ID == floatingIP.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - fip, err := floatingips.Get(context.TODO(), client, floatingIP.ID).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, floatingIP.ID, fip.ID) -} - -func TestFloatingIPsAssociate(t *testing.T) { - clients.RequireLong(t) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - server, err := CreateServer(t, client) - th.AssertNoErr(t, err) - defer DeleteServer(t, client, server) - - floatingIP, err := CreateFloatingIP(t, client) - th.AssertNoErr(t, err) - defer DeleteFloatingIP(t, client, floatingIP) - - tools.PrintResource(t, floatingIP) - - err = AssociateFloatingIP(t, client, floatingIP, server) - th.AssertNoErr(t, err) - defer DisassociateFloatingIP(t, client, floatingIP, server) - - newFloatingIP, err := floatingips.Get(context.TODO(), client, floatingIP.ID).Extract() - th.AssertNoErr(t, err) - - t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP) - - tools.PrintResource(t, newFloatingIP) - - th.AssertEquals(t, newFloatingIP.InstanceID, server.ID) -} - -func TestFloatingIPsFixedIPAssociate(t *testing.T) { - clients.RequireLong(t) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - server, err := CreateServer(t, client) - th.AssertNoErr(t, err) - defer DeleteServer(t, client, server) - - newServer, err := servers.Get(context.TODO(), client, server.ID).Extract() - th.AssertNoErr(t, err) - - floatingIP, err := CreateFloatingIP(t, client) - th.AssertNoErr(t, err) - defer DeleteFloatingIP(t, client, floatingIP) - - tools.PrintResource(t, floatingIP) - - var fixedIP string - for _, networkAddresses := range newServer.Addresses[choices.NetworkName].([]interface{}) { - address := networkAddresses.(map[string]interface{}) - if address["OS-EXT-IPS:type"] == "fixed" { - if address["version"].(float64) == 4 { - fixedIP = address["addr"].(string) - } - } - } - - err = AssociateFloatingIPWithFixedIP(t, client, floatingIP, newServer, fixedIP) - th.AssertNoErr(t, err) - defer DisassociateFloatingIP(t, client, floatingIP, newServer) - - newFloatingIP, err := floatingips.Get(context.TODO(), client, floatingIP.ID).Extract() - th.AssertNoErr(t, err) - - t.Logf("Floating IP %s is associated with Fixed IP %s", floatingIP.IP, newFloatingIP.FixedIP) - - tools.PrintResource(t, newFloatingIP) - - th.AssertEquals(t, newFloatingIP.InstanceID, server.ID) - th.AssertEquals(t, newFloatingIP.FixedIP, fixedIP) -} diff --git a/openstack/compute/v2/floatingips/doc.go b/openstack/compute/v2/floatingips/doc.go deleted file mode 100644 index 00582f456e..0000000000 --- a/openstack/compute/v2/floatingips/doc.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Package floatingips provides the ability to manage floating ips through the -Nova API. - -This API has been deprecated and will be removed from a future release of the -Nova API service. - -For environements that support this extension, this package can be used -regardless of if either Neutron or nova-network is used as the cloud's network -service. - -Example to List Floating IPs - - allPages, err := floatingips.List(computeClient).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allFloatingIPs, err := floatingips.ExtractFloatingIPs(allPages) - if err != nil { - panic(err) - } - - for _, fip := range allFloatingIPs { - fmt.Printf("%+v\n", fip) - } - -Example to Create a Floating IP - - createOpts := floatingips.CreateOpts{ - Pool: "nova", - } - - fip, err := floatingips.Create(context.TODO(), computeClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Floating IP - - err := floatingips.Delete(context.TODO(), computeClient, "floatingip-id").ExtractErr() - if err != nil { - panic(err) - } - -Example to Associate a Floating IP With a Server - - associateOpts := floatingips.AssociateOpts{ - FloatingIP: "10.10.10.2", - } - - err := floatingips.AssociateInstance(context.TODO(), computeClient, "server-id", associateOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example to Disassociate a Floating IP From a Server - - disassociateOpts := floatingips.DisassociateOpts{ - FloatingIP: "10.10.10.2", - } - - err := floatingips.DisassociateInstance(context.TODO(), computeClient, "server-id", disassociateOpts).ExtractErr() - if err != nil { - panic(err) - } -*/ -package floatingips diff --git a/openstack/compute/v2/floatingips/requests.go b/openstack/compute/v2/floatingips/requests.go deleted file mode 100644 index 32306304ba..0000000000 --- a/openstack/compute/v2/floatingips/requests.go +++ /dev/null @@ -1,121 +0,0 @@ -package floatingips - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// List returns a Pager that allows you to iterate over a collection of FloatingIPs. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { - return FloatingIPPage{pagination.SinglePageBase(r)} - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToFloatingIPCreateMap() (map[string]interface{}, error) -} - -// CreateOpts specifies a Floating IP allocation request. -type CreateOpts struct { - // Pool is the pool of Floating IPs to allocate one from. - Pool string `json:"pool" required:"true"` -} - -// ToFloatingIPCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "") -} - -// Create requests the creation of a new Floating IP. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToFloatingIPCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get returns data about a previously created Floating IP. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete requests the deletion of a previous allocated Floating IP. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// AssociateOptsBuilder allows extensions to add additional parameters to the -// Associate request. -type AssociateOptsBuilder interface { - ToFloatingIPAssociateMap() (map[string]interface{}, error) -} - -// AssociateOpts specifies the required information to associate a Floating IP with an instance -type AssociateOpts struct { - // FloatingIP is the Floating IP to associate with an instance. - FloatingIP string `json:"address" required:"true"` - - // FixedIP is an optional fixed IP address of the server. - FixedIP string `json:"fixed_address,omitempty"` -} - -// ToFloatingIPAssociateMap constructs a request body from AssociateOpts. -func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "addFloatingIp") -} - -// AssociateInstance pairs an allocated Floating IP with a server. -func AssociateInstance(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) { - b, err := opts.ToFloatingIPAssociateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, associateURL(client, serverID), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// DisassociateOptsBuilder allows extensions to add additional parameters to -// the Disassociate request. -type DisassociateOptsBuilder interface { - ToFloatingIPDisassociateMap() (map[string]interface{}, error) -} - -// DisassociateOpts specifies the required information to disassociate a -// Floating IP with a server. -type DisassociateOpts struct { - FloatingIP string `json:"address" required:"true"` -} - -// ToFloatingIPDisassociateMap constructs a request body from DisassociateOpts. -func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "removeFloatingIp") -} - -// DisassociateInstance decouples an allocated Floating IP from an instance -func DisassociateInstance(ctx context.Context, client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) { - b, err := opts.ToFloatingIPDisassociateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, disassociateURL(client, serverID), b, nil, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/floatingips/results.go b/openstack/compute/v2/floatingips/results.go deleted file mode 100644 index 7cefbd048e..0000000000 --- a/openstack/compute/v2/floatingips/results.go +++ /dev/null @@ -1,119 +0,0 @@ -package floatingips - -import ( - "encoding/json" - "strconv" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// A FloatingIP is an IP that can be associated with a server. -type FloatingIP struct { - // ID is a unique ID of the Floating IP - ID string `json:"-"` - - // FixedIP is a specific IP on the server to pair the Floating IP with. - FixedIP string `json:"fixed_ip,omitempty"` - - // InstanceID is the ID of the server that is using the Floating IP. - InstanceID string `json:"instance_id"` - - // IP is the actual Floating IP. - IP string `json:"ip"` - - // Pool is the pool of Floating IPs that this Floating IP belongs to. - Pool string `json:"pool"` -} - -func (r *FloatingIP) UnmarshalJSON(b []byte) error { - type tmp FloatingIP - var s struct { - tmp - ID interface{} `json:"id"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - - *r = FloatingIP(s.tmp) - - switch t := s.ID.(type) { - case float64: - r.ID = strconv.FormatFloat(t, 'f', -1, 64) - case string: - r.ID = t - } - - return err -} - -// FloatingIPPage stores a single page of FloatingIPs from a List call. -type FloatingIPPage struct { - pagination.SinglePageBase -} - -// IsEmpty determines whether or not a FloatingIPsPage is empty. -func (page FloatingIPPage) IsEmpty() (bool, error) { - if page.StatusCode == 204 { - return true, nil - } - - va, err := ExtractFloatingIPs(page) - return len(va) == 0, err -} - -// ExtractFloatingIPs interprets a page of results as a slice of FloatingIPs. -func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { - var s struct { - FloatingIPs []FloatingIP `json:"floating_ips"` - } - err := (r.(FloatingIPPage)).ExtractInto(&s) - return s.FloatingIPs, err -} - -// FloatingIPResult is the raw result from a FloatingIP request. -type FloatingIPResult struct { - gophercloud.Result -} - -// Extract is a method that attempts to interpret any FloatingIP resource -// response as a FloatingIP struct. -func (r FloatingIPResult) Extract() (*FloatingIP, error) { - var s struct { - FloatingIP *FloatingIP `json:"floating_ip"` - } - err := r.ExtractInto(&s) - return s.FloatingIP, err -} - -// CreateResult is the response from a Create operation. Call its Extract method -// to interpret it as a FloatingIP. -type CreateResult struct { - FloatingIPResult -} - -// GetResult is the response from a Get operation. Call its Extract method to -// interpret it as a FloatingIP. -type GetResult struct { - FloatingIPResult -} - -// DeleteResult is the response from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// AssociateResult is the response from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type AssociateResult struct { - gophercloud.ErrResult -} - -// DisassociateResult is the response from a Delete operation. Call its -// ExtractErr method to determine if the call succeeded or failed. -type DisassociateResult struct { - gophercloud.ErrResult -} diff --git a/openstack/compute/v2/floatingips/testing/doc.go b/openstack/compute/v2/floatingips/testing/doc.go deleted file mode 100644 index 82dfbe7fed..0000000000 --- a/openstack/compute/v2/floatingips/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// floatingips unit tests -package testing diff --git a/openstack/compute/v2/floatingips/testing/fixtures_test.go b/openstack/compute/v2/floatingips/testing/fixtures_test.go deleted file mode 100644 index 4a61a5d71b..0000000000 --- a/openstack/compute/v2/floatingips/testing/fixtures_test.go +++ /dev/null @@ -1,223 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// ListOutput is a sample response to a List call. -const ListOutput = ` -{ - "floating_ips": [ - { - "fixed_ip": null, - "id": "1", - "instance_id": null, - "ip": "10.10.10.1", - "pool": "nova" - }, - { - "fixed_ip": "166.78.185.201", - "id": "2", - "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", - "ip": "10.10.10.2", - "pool": "nova" - } - ] -} -` - -// GetOutput is a sample response to a Get call. -const GetOutput = ` -{ - "floating_ip": { - "fixed_ip": "166.78.185.201", - "id": "2", - "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", - "ip": "10.10.10.2", - "pool": "nova" - } -} -` - -// CreateOutput is a sample response to a Post call -const CreateOutput = ` -{ - "floating_ip": { - "fixed_ip": null, - "id": "1", - "instance_id": null, - "ip": "10.10.10.1", - "pool": "nova" - } -} -` - -// CreateOutputWithNumericID is a sample response to a Post call -// with a legacy nova-network-based numeric ID. -const CreateOutputWithNumericID = ` -{ - "floating_ip": { - "fixed_ip": null, - "id": 1, - "instance_id": null, - "ip": "10.10.10.1", - "pool": "nova" - } -} -` - -// FirstFloatingIP is the first result in ListOutput. -var FirstFloatingIP = floatingips.FloatingIP{ - ID: "1", - IP: "10.10.10.1", - Pool: "nova", -} - -// SecondFloatingIP is the first result in ListOutput. -var SecondFloatingIP = floatingips.FloatingIP{ - FixedIP: "166.78.185.201", - ID: "2", - InstanceID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", - IP: "10.10.10.2", - Pool: "nova", -} - -// ExpectedFloatingIPsSlice is the slice of results that should be parsed -// from ListOutput, in the expected order. -var ExpectedFloatingIPsSlice = []floatingips.FloatingIP{FirstFloatingIP, SecondFloatingIP} - -// CreatedFloatingIP is the parsed result from CreateOutput. -var CreatedFloatingIP = floatingips.FloatingIP{ - ID: "1", - IP: "10.10.10.1", - Pool: "nova", -} - -// HandleListSuccessfully configures the test server to respond to a List request. -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips", 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, ListOutput) - }) -} - -// HandleGetSuccessfully configures the test server to respond to a Get request -// for an existing floating ip -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips/2", 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, GetOutput) - }) -} - -// HandleCreateSuccessfully configures the test server to respond to a Create request -// for a new floating ip -func HandleCreateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips", 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, ` -{ - "pool": "nova" -} -`) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutput) - }) -} - -// HandleCreateWithNumericIDSuccessfully configures the test server to respond to a Create request -// for a new floating ip -func HandleCreateWithNumericIDSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips", 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, ` -{ - "pool": "nova" -} -`) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutputWithNumericID) - }) -} - -// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a -// an existing floating ip -func HandleDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips/1", 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) - }) -} - -// HandleAssociateSuccessfully configures the test server to respond to a Post request -// to associate an allocated floating IP -func HandleAssociateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/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, ` -{ - "addFloatingIp": { - "address": "10.10.10.2" - } -} -`) - - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleFixedAssociateSucessfully configures the test server to respond to a Post request -// to associate an allocated floating IP with a specific fixed IP address -func HandleAssociateFixedSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/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, ` -{ - "addFloatingIp": { - "address": "10.10.10.2", - "fixed_address": "166.78.185.201" - } -} -`) - - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleDisassociateSuccessfully configures the test server to respond to a Post request -// to disassociate an allocated floating IP -func HandleDisassociateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/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, ` -{ - "removeFloatingIp": { - "address": "10.10.10.2" - } -} -`) - - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/openstack/compute/v2/floatingips/testing/requests_test.go b/openstack/compute/v2/floatingips/testing/requests_test.go deleted file mode 100644 index 2a375000f4..0000000000 --- a/openstack/compute/v2/floatingips/testing/requests_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/floatingips" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListSuccessfully(t) - - count := 0 - err := floatingips.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := floatingips.ExtractFloatingIPs(page) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedFloatingIPsSlice, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - th.CheckEquals(t, 1, count) -} - -func TestCreate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleCreateSuccessfully(t) - - actual, err := floatingips.Create(context.TODO(), client.ServiceClient(), floatingips.CreateOpts{ - Pool: "nova", - }).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &CreatedFloatingIP, actual) -} - -func TestCreateWithNumericID(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleCreateWithNumericIDSuccessfully(t) - - actual, err := floatingips.Create(context.TODO(), client.ServiceClient(), floatingips.CreateOpts{ - Pool: "nova", - }).Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &CreatedFloatingIP, actual) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleGetSuccessfully(t) - - actual, err := floatingips.Get(context.TODO(), client.ServiceClient(), "2").Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &SecondFloatingIP, actual) -} - -func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleDeleteSuccessfully(t) - - err := floatingips.Delete(context.TODO(), client.ServiceClient(), "1").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestAssociate(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleAssociateSuccessfully(t) - - associateOpts := floatingips.AssociateOpts{ - FloatingIP: "10.10.10.2", - } - - err := floatingips.AssociateInstance(context.TODO(), client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestAssociateFixed(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleAssociateFixedSuccessfully(t) - - associateOpts := floatingips.AssociateOpts{ - FloatingIP: "10.10.10.2", - FixedIP: "166.78.185.201", - } - - err := floatingips.AssociateInstance(context.TODO(), client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", associateOpts).ExtractErr() - th.AssertNoErr(t, err) -} - -func TestDisassociateInstance(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleDisassociateSuccessfully(t) - - disassociateOpts := floatingips.DisassociateOpts{ - FloatingIP: "10.10.10.2", - } - - err := floatingips.DisassociateInstance(context.TODO(), client.ServiceClient(), "4d8c3732-a248-40ed-bebc-539a6ffd25c0", disassociateOpts).ExtractErr() - th.AssertNoErr(t, err) -} diff --git a/openstack/compute/v2/floatingips/urls.go b/openstack/compute/v2/floatingips/urls.go deleted file mode 100644 index 853110aa7e..0000000000 --- a/openstack/compute/v2/floatingips/urls.go +++ /dev/null @@ -1,37 +0,0 @@ -package floatingips - -import "github.com/gophercloud/gophercloud/v2" - -const resourcePath = "os-floating-ips" - -func resourceURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL(resourcePath) -} - -func listURL(c *gophercloud.ServiceClient) string { - return resourceURL(c) -} - -func createURL(c *gophercloud.ServiceClient) string { - return resourceURL(c) -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(resourcePath, id) -} - -func deleteURL(c *gophercloud.ServiceClient, id string) string { - return getURL(c, id) -} - -func serverURL(c *gophercloud.ServiceClient, serverID string) string { - return c.ServiceURL("servers/" + serverID + "/action") -} - -func associateURL(c *gophercloud.ServiceClient, serverID string) string { - return serverURL(c, serverID) -} - -func disassociateURL(c *gophercloud.ServiceClient, serverID string) string { - return serverURL(c, serverID) -} From abd1bbe2f6b959e774245551ab68a54c7e49a484 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 4 Apr 2024 14:05:07 +0100 Subject: [PATCH 1834/2296] compute: Remove deprecated proxy APIs (tenantnetworks) The create and delete APIs have been removed from Nova but were never supported in Gophercloud. The list and show APIs are supported though and are deprecated in Nova since 2.36. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/compute.go | 40 +-------- .../compute/v2/tenantnetworks_test.go | 54 ------------ openstack/compute/v2/tenantnetworks/doc.go | 26 ------ .../compute/v2/tenantnetworks/requests.go | 22 ----- .../compute/v2/tenantnetworks/results.go | 62 -------------- .../compute/v2/tenantnetworks/testing/doc.go | 2 - .../tenantnetworks/testing/fixtures_test.go | 83 ------------------- .../tenantnetworks/testing/requests_test.go | 39 --------- openstack/compute/v2/tenantnetworks/urls.go | 17 ---- 9 files changed, 1 insertion(+), 344 deletions(-) delete mode 100644 internal/acceptance/openstack/compute/v2/tenantnetworks_test.go delete mode 100644 openstack/compute/v2/tenantnetworks/doc.go delete mode 100644 openstack/compute/v2/tenantnetworks/requests.go delete mode 100644 openstack/compute/v2/tenantnetworks/results.go delete mode 100644 openstack/compute/v2/tenantnetworks/testing/doc.go delete mode 100644 openstack/compute/v2/tenantnetworks/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/tenantnetworks/testing/requests_test.go delete mode 100644 openstack/compute/v2/tenantnetworks/urls.go diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index a450f07c27..395d3832bd 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -24,7 +24,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servergroups" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tenantnetworks" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach" neutron "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -899,47 +898,10 @@ func GetNetworkIDFromOSNetworks(t *testing.T, client *gophercloud.ServiceClient, return networkID, nil } -// GetNetworkIDFromTenantNetworks will return the network UUID for a given -// network name using the os-tenant-networks API extension. An error will be -// returned if the network could not be retrieved. -func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { - allPages, err := tenantnetworks.List(client).AllPages(context.TODO()) - if err != nil { - return "", err - } - - allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) - if err != nil { - return "", err - } - - for _, network := range allTenantNetworks { - if network.Name == networkName { - return network.ID, nil - } - } - - return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName) -} - // GetNetworkIDFromNetworks will return the network UUID for a given network -// name using either the os-tenant-networks API extension or Neutron API. +// name using the Neutron API. // An error will be returned if the network could not be retrieved. func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { - allPages, err := tenantnetworks.List(client).AllPages(context.TODO()) - if err == nil { - allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) - if err != nil { - return "", err - } - - for _, network := range allTenantNetworks { - if network.Name == networkName { - return network.ID, nil - } - } - } - networkClient, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go b/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go deleted file mode 100644 index 89487adb1b..0000000000 --- a/internal/acceptance/openstack/compute/v2/tenantnetworks_test.go +++ /dev/null @@ -1,54 +0,0 @@ -//go:build acceptance || compute || servers - -package v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tenantnetworks" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestTenantNetworksList(t *testing.T) { - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - allPages, err := tenantnetworks.List(client).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, network := range allTenantNetworks { - tools.PrintResource(t, network) - - if network.Name == choices.NetworkName { - found = true - } - } - - th.AssertEquals(t, found, true) -} - -func TestTenantNetworksGet(t *testing.T) { - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - networkID, err := GetNetworkIDFromTenantNetworks(t, client, choices.NetworkName) - th.AssertNoErr(t, err) - - network, err := tenantnetworks.Get(context.TODO(), client, networkID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, network) -} diff --git a/openstack/compute/v2/tenantnetworks/doc.go b/openstack/compute/v2/tenantnetworks/doc.go deleted file mode 100644 index d355ba4443..0000000000 --- a/openstack/compute/v2/tenantnetworks/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Package tenantnetworks provides the ability for tenants to see information -about the networks they have access to. - -This is a deprecated API and will be removed from the Nova API service in a -future version. - -This API works in both Neutron and nova-network based OpenStack clouds. - -Example to List Networks Available to a Tenant - - allPages, err := tenantnetworks.List(computeClient).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allNetworks, err := tenantnetworks.ExtractNetworks(allPages) - if err != nil { - panic(err) - } - - for _, network := range allNetworks { - fmt.Printf("%+v\n", network) - } -*/ -package tenantnetworks diff --git a/openstack/compute/v2/tenantnetworks/requests.go b/openstack/compute/v2/tenantnetworks/requests.go deleted file mode 100644 index bbc825a227..0000000000 --- a/openstack/compute/v2/tenantnetworks/requests.go +++ /dev/null @@ -1,22 +0,0 @@ -package tenantnetworks - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// List returns a Pager that allows you to iterate over a collection of Networks. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { - return NetworkPage{pagination.SinglePageBase(r)} - }) -} - -// Get returns data about a previously created Network. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/tenantnetworks/results.go b/openstack/compute/v2/tenantnetworks/results.go deleted file mode 100644 index 386072ea74..0000000000 --- a/openstack/compute/v2/tenantnetworks/results.go +++ /dev/null @@ -1,62 +0,0 @@ -package tenantnetworks - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// A Network represents a network that a server communicates on. -type Network struct { - // CIDR is the IPv4 subnet. - CIDR string `json:"cidr"` - - // ID is the UUID of the network. - ID string `json:"id"` - - // Name is the common name that the network has. - Name string `json:"label"` -} - -// NetworkPage stores a single page of all Networks results from a List call. -type NetworkPage struct { - pagination.SinglePageBase -} - -// IsEmpty determines whether or not a NetworkPage is empty. -func (page NetworkPage) IsEmpty() (bool, error) { - if page.StatusCode == 204 { - return true, nil - } - - va, err := ExtractNetworks(page) - return len(va) == 0, err -} - -// ExtractNetworks interprets a page of results as a slice of Network. -func ExtractNetworks(r pagination.Page) ([]Network, error) { - var s struct { - Networks []Network `json:"networks"` - } - err := (r.(NetworkPage)).ExtractInto(&s) - return s.Networks, err -} - -type NetworkResult struct { - gophercloud.Result -} - -// Extract is a method that attempts to interpret any Network resource response -// as a Network struct. -func (r NetworkResult) Extract() (*Network, error) { - var s struct { - Network *Network `json:"network"` - } - err := r.ExtractInto(&s) - return s.Network, err -} - -// GetResult is the response from a Get operation. Call its Extract method to -// interpret it as a Network. -type GetResult struct { - NetworkResult -} diff --git a/openstack/compute/v2/tenantnetworks/testing/doc.go b/openstack/compute/v2/tenantnetworks/testing/doc.go deleted file mode 100644 index 4639153ff3..0000000000 --- a/openstack/compute/v2/tenantnetworks/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// tenantnetworks unit tests -package testing diff --git a/openstack/compute/v2/tenantnetworks/testing/fixtures_test.go b/openstack/compute/v2/tenantnetworks/testing/fixtures_test.go deleted file mode 100644 index c6c8f2d99f..0000000000 --- a/openstack/compute/v2/tenantnetworks/testing/fixtures_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tenantnetworks" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// ListOutput is a sample response to a List call. -const ListOutput = ` -{ - "networks": [ - { - "cidr": "10.0.0.0/29", - "id": "20c8acc0-f747-4d71-a389-46d078ebf047", - "label": "mynet_0" - }, - { - "cidr": "10.0.0.10/29", - "id": "20c8acc0-f747-4d71-a389-46d078ebf000", - "label": "mynet_1" - } - ] -} -` - -// GetOutput is a sample response to a Get call. -const GetOutput = ` -{ - "network": { - "cidr": "10.0.0.10/29", - "id": "20c8acc0-f747-4d71-a389-46d078ebf000", - "label": "mynet_1" - } -} -` - -// FirstNetwork is the first result in ListOutput. -var nilTime time.Time -var FirstNetwork = tenantnetworks.Network{ - CIDR: "10.0.0.0/29", - ID: "20c8acc0-f747-4d71-a389-46d078ebf047", - Name: "mynet_0", -} - -// SecondNetwork is the second result in ListOutput. -var SecondNetwork = tenantnetworks.Network{ - CIDR: "10.0.0.10/29", - ID: "20c8acc0-f747-4d71-a389-46d078ebf000", - Name: "mynet_1", -} - -// ExpectedNetworkSlice is the slice of results that should be parsed -// from ListOutput, in the expected order. -var ExpectedNetworkSlice = []tenantnetworks.Network{FirstNetwork, SecondNetwork} - -// HandleListSuccessfully configures the test server to respond to a List request. -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-tenant-networks", 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, ListOutput) - }) -} - -// HandleGetSuccessfully configures the test server to respond to a Get request -// for an existing network. -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-tenant-networks/20c8acc0-f747-4d71-a389-46d078ebf000", 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, GetOutput) - }) -} diff --git a/openstack/compute/v2/tenantnetworks/testing/requests_test.go b/openstack/compute/v2/tenantnetworks/testing/requests_test.go deleted file mode 100644 index f94e223280..0000000000 --- a/openstack/compute/v2/tenantnetworks/testing/requests_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/tenantnetworks" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListSuccessfully(t) - - count := 0 - err := tenantnetworks.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := tenantnetworks.ExtractNetworks(page) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedNetworkSlice, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - th.CheckEquals(t, 1, count) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleGetSuccessfully(t) - - actual, err := tenantnetworks.Get(context.TODO(), client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &SecondNetwork, actual) -} diff --git a/openstack/compute/v2/tenantnetworks/urls.go b/openstack/compute/v2/tenantnetworks/urls.go deleted file mode 100644 index 0409256d5f..0000000000 --- a/openstack/compute/v2/tenantnetworks/urls.go +++ /dev/null @@ -1,17 +0,0 @@ -package tenantnetworks - -import "github.com/gophercloud/gophercloud/v2" - -const resourcePath = "os-tenant-networks" - -func resourceURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL(resourcePath) -} - -func listURL(c *gophercloud.ServiceClient) string { - return resourceURL(c) -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(resourcePath, id) -} From bd501d5547132336b68566ec24bcae73f04c68ba Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 4 Apr 2024 14:08:30 +0100 Subject: [PATCH 1835/2296] compute: Remove deprecated proxy APIs (networks) The create, delete and "add" APIs, along with the disassociate actions have been removed from Nova, though these were never supported in Gophercloud. The list and show APIs are supported though and are deprecated in Nova since 2.36. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/compute.go | 28 --- .../openstack/compute/v2/network_test.go | 56 ----- openstack/compute/v2/networks/doc.go | 24 --- openstack/compute/v2/networks/requests.go | 22 -- openstack/compute/v2/networks/results.go | 137 ------------ openstack/compute/v2/networks/testing/doc.go | 2 - .../v2/networks/testing/fixtures_test.go | 204 ------------------ .../v2/networks/testing/requests_test.go | 39 ---- openstack/compute/v2/networks/urls.go | 17 -- 9 files changed, 529 deletions(-) delete mode 100644 internal/acceptance/openstack/compute/v2/network_test.go delete mode 100644 openstack/compute/v2/networks/doc.go delete mode 100644 openstack/compute/v2/networks/requests.go delete mode 100644 openstack/compute/v2/networks/results.go delete mode 100644 openstack/compute/v2/networks/testing/doc.go delete mode 100644 openstack/compute/v2/networks/testing/fixtures_test.go delete mode 100644 openstack/compute/v2/networks/testing/requests_test.go delete mode 100644 openstack/compute/v2/networks/urls.go diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index 395d3832bd..e0f892984a 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -18,7 +18,6 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/attachinterfaces" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/keypairs" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/quotasets" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/remoteconsoles" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" @@ -871,33 +870,6 @@ func DetachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID, t.Logf("Detached interface %s from server %s", portID, serverID) } -// GetNetworkIDFromOSNetworks will return the network ID from a specified network -// UUID using the os-networks API extension. An error will be returned if the -// network could not be retrieved. -func GetNetworkIDFromOSNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { - allPages, err := networks.List(client).AllPages(context.TODO()) - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } - - networkList, err := networks.ExtractNetworks(allPages) - if err != nil { - t.Fatalf("Unable to list networks: %v", err) - } - - networkID := "" - for _, network := range networkList { - t.Logf("Network: %v", network) - if network.Label == networkName { - networkID = network.ID - } - } - - t.Logf("Found network ID for %s: %s", networkName, networkID) - - return networkID, nil -} - // GetNetworkIDFromNetworks will return the network UUID for a given network // name using the Neutron API. // An error will be returned if the network could not be retrieved. diff --git a/internal/acceptance/openstack/compute/v2/network_test.go b/internal/acceptance/openstack/compute/v2/network_test.go deleted file mode 100644 index 00516baed4..0000000000 --- a/internal/acceptance/openstack/compute/v2/network_test.go +++ /dev/null @@ -1,56 +0,0 @@ -//go:build acceptance || compute || servers - -package v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestNetworksList(t *testing.T) { - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - allPages, err := networks.List(client).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allNetworks, err := networks.ExtractNetworks(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, network := range allNetworks { - tools.PrintResource(t, network) - - if network.Label == choices.NetworkName { - found = true - } - } - - th.AssertEquals(t, found, true) -} - -func TestNetworksGet(t *testing.T) { - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - networkID, err := GetNetworkIDFromOSNetworks(t, client, choices.NetworkName) - th.AssertNoErr(t, err) - - network, err := networks.Get(context.TODO(), client, networkID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, network) - - th.AssertEquals(t, network.Label, choices.NetworkName) -} diff --git a/openstack/compute/v2/networks/doc.go b/openstack/compute/v2/networks/doc.go deleted file mode 100644 index 34a9ad969d..0000000000 --- a/openstack/compute/v2/networks/doc.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Package networks provides the ability to create and manage networks in cloud -environments using nova-network. - -This package can also be used to retrieve network details of Neutron-based -networks. - -Example to List Networks - - allPages, err := networks.List(computeClient).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allNetworks, err := networks.ExtractNetworks(allPages) - if err != nil { - panic(err) - } - - for _, network := range allNetworks { - fmt.Printf("%+v\n", network) - } -*/ -package networks diff --git a/openstack/compute/v2/networks/requests.go b/openstack/compute/v2/networks/requests.go deleted file mode 100644 index 3957a6faf2..0000000000 --- a/openstack/compute/v2/networks/requests.go +++ /dev/null @@ -1,22 +0,0 @@ -package networks - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// List returns a Pager that allows you to iterate over a collection of Network. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { - return NetworkPage{pagination.SinglePageBase(r)} - }) -} - -// Get returns data about a previously created Network. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/networks/results.go b/openstack/compute/v2/networks/results.go deleted file mode 100644 index 7f87d595e7..0000000000 --- a/openstack/compute/v2/networks/results.go +++ /dev/null @@ -1,137 +0,0 @@ -package networks - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// A Network represents a network in an OpenStack cloud. -type Network struct { - // The Bridge that VIFs on this network are connected to - Bridge string `json:"bridge"` - - // BridgeInterface is what interface is connected to the Bridge - BridgeInterface string `json:"bridge_interface"` - - // The Broadcast address of the network. - Broadcast string `json:"broadcast"` - - // CIDR is the IPv4 subnet. - CIDR string `json:"cidr"` - - // CIDRv6 is the IPv6 subnet. - CIDRv6 string `json:"cidr_v6"` - - // CreatedAt is when the network was created.. - CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at,omitempty"` - - // Deleted shows if the network has been deleted. - Deleted bool `json:"deleted"` - - // DeletedAt is the time when the network was deleted. - DeletedAt gophercloud.JSONRFC3339MilliNoZ `json:"deleted_at,omitempty"` - - // DHCPStart is the start of the DHCP address range. - DHCPStart string `json:"dhcp_start"` - - // DNS1 is the first DNS server to use through DHCP. - DNS1 string `json:"dns_1"` - - // DNS2 is the first DNS server to use through DHCP. - DNS2 string `json:"dns_2"` - - // Gateway is the network gateway. - Gateway string `json:"gateway"` - - // Gatewayv6 is the IPv6 network gateway. - Gatewayv6 string `json:"gateway_v6"` - - // Host is the host that the network service is running on. - Host string `json:"host"` - - // ID is the UUID of the network. - ID string `json:"id"` - - // Injected determines if network information is injected into the host. - Injected bool `json:"injected"` - - // Label is the common name that the network has.. - Label string `json:"label"` - - // MultiHost is if multi-host networking is enablec.. - MultiHost bool `json:"multi_host"` - - // Netmask is the network netmask. - Netmask string `json:"netmask"` - - // Netmaskv6 is the IPv6 netmask. - Netmaskv6 string `json:"netmask_v6"` - - // Priority is the network interface priority. - Priority int `json:"priority"` - - // ProjectID is the project associated with this network. - ProjectID string `json:"project_id"` - - // RXTXBase configures bandwidth entitlement. - RXTXBase int `json:"rxtx_base"` - - // UpdatedAt is the time when the network was last updated. - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at,omitempty"` - - // VLAN is the vlan this network runs on. - VLAN int `json:"vlan"` - - // VPNPrivateAddress is the private address of the CloudPipe VPN. - VPNPrivateAddress string `json:"vpn_private_address"` - - // VPNPublicAddress is the public address of the CloudPipe VPN. - VPNPublicAddress string `json:"vpn_public_address"` - - // VPNPublicPort is the port of the CloudPipe VPN. - VPNPublicPort int `json:"vpn_public_port"` -} - -// NetworkPage stores a single page of all Network results from a List call. -type NetworkPage struct { - pagination.SinglePageBase -} - -// IsEmpty determines whether or not a NetworkPage is empty. -func (page NetworkPage) IsEmpty() (bool, error) { - if page.StatusCode == 204 { - return true, nil - } - - va, err := ExtractNetworks(page) - return len(va) == 0, err -} - -// ExtractNetworks interprets a page of results as a slice of Networks. -func ExtractNetworks(r pagination.Page) ([]Network, error) { - var s struct { - Networks []Network `json:"networks"` - } - err := (r.(NetworkPage)).ExtractInto(&s) - return s.Networks, err -} - -type NetworkResult struct { - gophercloud.Result -} - -// Extract is a method that attempts to interpret any Network resource -// response as a Network struct. -func (r NetworkResult) Extract() (*Network, error) { - var s struct { - Network *Network `json:"network"` - } - err := r.ExtractInto(&s) - return s.Network, err -} - -// GetResult is the response from a Get operation. Call its Extract method to -// interpret it as a Network. -type GetResult struct { - NetworkResult -} diff --git a/openstack/compute/v2/networks/testing/doc.go b/openstack/compute/v2/networks/testing/doc.go deleted file mode 100644 index fc8511de47..0000000000 --- a/openstack/compute/v2/networks/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// networks unit tests -package testing diff --git a/openstack/compute/v2/networks/testing/fixtures_test.go b/openstack/compute/v2/networks/testing/fixtures_test.go deleted file mode 100644 index 298896ab22..0000000000 --- a/openstack/compute/v2/networks/testing/fixtures_test.go +++ /dev/null @@ -1,204 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -// ListOutput is a sample response to a List call. -const ListOutput = ` -{ - "networks": [ - { - "bridge": "br100", - "bridge_interface": "eth0", - "broadcast": "10.0.0.7", - "cidr": "10.0.0.0/29", - "cidr_v6": null, - "created_at": "2011-08-15T06:19:19.387525", - "deleted": false, - "dhcp_start": "10.0.0.3", - "dns1": null, - "dns2": null, - "gateway": "10.0.0.1", - "gateway_v6": null, - "host": "nsokolov-desktop", - "id": "20c8acc0-f747-4d71-a389-46d078ebf047", - "injected": false, - "label": "mynet_0", - "multi_host": false, - "netmask": "255.255.255.248", - "netmask_v6": null, - "priority": null, - "project_id": "1234", - "rxtx_base": null, - "updated_at": "2011-08-16T09:26:13.048257", - "vlan": 100, - "vpn_private_address": "10.0.0.2", - "vpn_public_address": "127.0.0.1", - "vpn_public_port": 1000 - }, - { - "bridge": "br101", - "bridge_interface": "eth0", - "broadcast": "10.0.0.15", - "cidr": "10.0.0.10/29", - "cidr_v6": null, - "created_at": "2011-08-15T06:19:19.387525", - "deleted": false, - "dhcp_start": "10.0.0.11", - "dns1": null, - "dns2": null, - "gateway": "10.0.0.9", - "gateway_v6": null, - "host": null, - "id": "20c8acc0-f747-4d71-a389-46d078ebf000", - "injected": false, - "label": "mynet_1", - "multi_host": false, - "netmask": "255.255.255.248", - "netmask_v6": null, - "priority": null, - "project_id": null, - "rxtx_base": null, - "vlan": 101, - "vpn_private_address": "10.0.0.10", - "vpn_public_address": null, - "vpn_public_port": 1001 - } - ] -} -` - -// GetOutput is a sample response to a Get call. -const GetOutput = ` -{ - "network": { - "bridge": "br101", - "bridge_interface": "eth0", - "broadcast": "10.0.0.15", - "cidr": "10.0.0.10/29", - "cidr_v6": null, - "created_at": "2011-08-15T06:19:19.387525", - "deleted": false, - "dhcp_start": "10.0.0.11", - "dns1": null, - "dns2": null, - "gateway": "10.0.0.9", - "gateway_v6": null, - "host": null, - "id": "20c8acc0-f747-4d71-a389-46d078ebf000", - "injected": false, - "label": "mynet_1", - "multi_host": false, - "netmask": "255.255.255.248", - "netmask_v6": null, - "priority": null, - "project_id": null, - "rxtx_base": null, - "vlan": 101, - "vpn_private_address": "10.0.0.10", - "vpn_public_address": null, - "vpn_public_port": 1001 - } -} -` - -// FirstNetwork is the first result in ListOutput. -var nilTime time.Time -var FirstNetwork = networks.Network{ - Bridge: "br100", - BridgeInterface: "eth0", - Broadcast: "10.0.0.7", - CIDR: "10.0.0.0/29", - CIDRv6: "", - CreatedAt: gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC)), - Deleted: false, - DeletedAt: gophercloud.JSONRFC3339MilliNoZ(nilTime), - DHCPStart: "10.0.0.3", - DNS1: "", - DNS2: "", - Gateway: "10.0.0.1", - Gatewayv6: "", - Host: "nsokolov-desktop", - ID: "20c8acc0-f747-4d71-a389-46d078ebf047", - Injected: false, - Label: "mynet_0", - MultiHost: false, - Netmask: "255.255.255.248", - Netmaskv6: "", - Priority: 0, - ProjectID: "1234", - RXTXBase: 0, - UpdatedAt: gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 16, 9, 26, 13, 48257000, time.UTC)), - VLAN: 100, - VPNPrivateAddress: "10.0.0.2", - VPNPublicAddress: "127.0.0.1", - VPNPublicPort: 1000, -} - -// SecondNetwork is the second result in ListOutput. -var SecondNetwork = networks.Network{ - Bridge: "br101", - BridgeInterface: "eth0", - Broadcast: "10.0.0.15", - CIDR: "10.0.0.10/29", - CIDRv6: "", - CreatedAt: gophercloud.JSONRFC3339MilliNoZ(time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC)), - Deleted: false, - DeletedAt: gophercloud.JSONRFC3339MilliNoZ(nilTime), - DHCPStart: "10.0.0.11", - DNS1: "", - DNS2: "", - Gateway: "10.0.0.9", - Gatewayv6: "", - Host: "", - ID: "20c8acc0-f747-4d71-a389-46d078ebf000", - Injected: false, - Label: "mynet_1", - MultiHost: false, - Netmask: "255.255.255.248", - Netmaskv6: "", - Priority: 0, - ProjectID: "", - RXTXBase: 0, - UpdatedAt: gophercloud.JSONRFC3339MilliNoZ(nilTime), - VLAN: 101, - VPNPrivateAddress: "10.0.0.10", - VPNPublicAddress: "", - VPNPublicPort: 1001, -} - -// ExpectedNetworkSlice is the slice of results that should be parsed -// from ListOutput, in the expected order. -var ExpectedNetworkSlice = []networks.Network{FirstNetwork, SecondNetwork} - -// HandleListSuccessfully configures the test server to respond to a List request. -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-networks", 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, ListOutput) - }) -} - -// HandleGetSuccessfully configures the test server to respond to a Get request -// for an existing network. -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-networks/20c8acc0-f747-4d71-a389-46d078ebf000", 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, GetOutput) - }) -} diff --git a/openstack/compute/v2/networks/testing/requests_test.go b/openstack/compute/v2/networks/testing/requests_test.go deleted file mode 100644 index fed0635d2a..0000000000 --- a/openstack/compute/v2/networks/testing/requests_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/networks" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleListSuccessfully(t) - - count := 0 - err := networks.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := networks.ExtractNetworks(page) - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedNetworkSlice, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - th.CheckEquals(t, 1, count) -} - -func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleGetSuccessfully(t) - - actual, err := networks.Get(context.TODO(), client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, &SecondNetwork, actual) -} diff --git a/openstack/compute/v2/networks/urls.go b/openstack/compute/v2/networks/urls.go deleted file mode 100644 index f041841b1d..0000000000 --- a/openstack/compute/v2/networks/urls.go +++ /dev/null @@ -1,17 +0,0 @@ -package networks - -import "github.com/gophercloud/gophercloud/v2" - -const resourcePath = "os-networks" - -func resourceURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL(resourcePath) -} - -func listURL(c *gophercloud.ServiceClient) string { - return resourceURL(c) -} - -func getURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(resourcePath, id) -} From bfa8997572fb0d33a3ad1a6043e84b6f9fc94829 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 4 Apr 2024 13:53:35 +0100 Subject: [PATCH 1836/2296] compute: Remove deprecated proxy APIs (images) All APIs were deprecated in Nova since 2.36. Signed-off-by: Stephen Finucane --- .../openstack/blockstorage/v2/blockstorage.go | 2 +- .../openstack/blockstorage/v2/volumes_test.go | 4 +- .../openstack/blockstorage/v3/blockstorage.go | 2 +- .../openstack/blockstorage/v3/volumes_test.go | 4 +- .../openstack/compute/v2/images_test.go | 53 ---- openstack/compute/v2/images/doc.go | 32 --- openstack/compute/v2/images/requests.go | 74 ------ openstack/compute/v2/images/results.go | 99 -------- openstack/compute/v2/images/testing/doc.go | 2 - .../v2/images/testing/requests_test.go | 226 ------------------ openstack/compute/v2/images/urls.go | 15 -- 11 files changed, 6 insertions(+), 507 deletions(-) delete mode 100644 internal/acceptance/openstack/compute/v2/images_test.go delete mode 100644 openstack/compute/v2/images/doc.go delete mode 100644 openstack/compute/v2/images/requests.go delete mode 100644 openstack/compute/v2/images/results.go delete mode 100644 openstack/compute/v2/images/testing/doc.go delete mode 100644 openstack/compute/v2/images/testing/requests_test.go delete mode 100644 openstack/compute/v2/images/urls.go diff --git a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go index 1cae786437..ab209f12ac 100644 --- a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -16,8 +16,8 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/backups" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/snapshots" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/images" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go index e6a21eb0ca..f3dd985558 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -139,7 +139,7 @@ func TestVolumeActionsUploadImageDestroy(t *testing.T) { blockClient, err := clients.NewBlockStorageV2Client() th.AssertNoErr(t, err) - computeClient, err := clients.NewComputeV2Client() + imageClient, err := clients.NewImageV2Client() th.AssertNoErr(t, err) volume, err := CreateVolume(t, blockClient) @@ -151,7 +151,7 @@ func TestVolumeActionsUploadImageDestroy(t *testing.T) { tools.PrintResource(t, volumeImage) - err = DeleteUploadedImage(t, computeClient, volumeImage.ImageID) + err = DeleteUploadedImage(t, imageClient, volumeImage.ImageID) th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 94c4489951..42941bf876 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -17,8 +17,8 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumetypes" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/image/v2/images" th "github.com/gophercloud/gophercloud/v2/testhelper" ) diff --git a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go index fccecb43de..188ac1ac47 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -158,7 +158,7 @@ func TestVolumeActionsUploadImageDestroy(t *testing.T) { blockClient, err := clients.NewBlockStorageV3Client() th.AssertNoErr(t, err) - computeClient, err := clients.NewComputeV2Client() + imageClient, err := clients.NewImageV2Client() th.AssertNoErr(t, err) volume, err := CreateVolume(t, blockClient) @@ -170,7 +170,7 @@ func TestVolumeActionsUploadImageDestroy(t *testing.T) { tools.PrintResource(t, volumeImage) - err = DeleteUploadedImage(t, computeClient, volumeImage.ImageID) + err = DeleteUploadedImage(t, imageClient, volumeImage.ImageID) th.AssertNoErr(t, err) } diff --git a/internal/acceptance/openstack/compute/v2/images_test.go b/internal/acceptance/openstack/compute/v2/images_test.go deleted file mode 100644 index ac1e13684c..0000000000 --- a/internal/acceptance/openstack/compute/v2/images_test.go +++ /dev/null @@ -1,53 +0,0 @@ -//go:build acceptance || compute || images - -package v2 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestImagesList(t *testing.T) { - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - allPages, err := images.ListDetail(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allImages, err := images.ExtractImages(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, image := range allImages { - tools.PrintResource(t, image) - - if image.ID == choices.ImageID { - found = true - } - } - - th.AssertEquals(t, found, true) -} - -func TestImagesGet(t *testing.T) { - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - image, err := images.Get(context.TODO(), client, choices.ImageID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, image) - - th.AssertEquals(t, choices.ImageID, image.ID) -} diff --git a/openstack/compute/v2/images/doc.go b/openstack/compute/v2/images/doc.go deleted file mode 100644 index 2b19c55360..0000000000 --- a/openstack/compute/v2/images/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Package images provides information and interaction with the images through -the OpenStack Compute service. - -This API is deprecated and will be removed from a future version of the Nova -API service. - -An image is a collection of files used to create or rebuild a server. -Operators provide a number of pre-built OS images by default. You may also -create custom images from cloud servers you have launched. - -Example to List Images - - listOpts := images.ListOpts{ - Limit: 2, - } - - allPages, err := images.ListDetail(computeClient, listOpts).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allImages, err := images.ExtractImages(allPages) - if err != nil { - panic(err) - } - - for _, image := range allImages { - fmt.Printf("%+v\n", image) - } -*/ -package images diff --git a/openstack/compute/v2/images/requests.go b/openstack/compute/v2/images/requests.go deleted file mode 100644 index ac93d6de61..0000000000 --- a/openstack/compute/v2/images/requests.go +++ /dev/null @@ -1,74 +0,0 @@ -package images - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// ListDetail request. -type ListOptsBuilder interface { - ToImageListQuery() (string, error) -} - -// ListOpts contain options filtering Images returned from a call to ListDetail. -type ListOpts struct { - // ChangesSince filters Images based on the last changed status (in date-time - // format). - ChangesSince string `q:"changes-since"` - - // Limit limits the number of Images to return. - Limit int `q:"limit"` - - // Mark is an Image UUID at which to set a marker. - Marker string `q:"marker"` - - // Name is the name of the Image. - Name string `q:"name"` - - // Server is the name of the Server (in URL format). - Server string `q:"server"` - - // Status is the current status of the Image. - Status string `q:"status"` - - // Type is the type of image (e.g. BASE, SERVER, ALL). - Type string `q:"type"` -} - -// ToImageListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToImageListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// ListDetail enumerates the available images. -func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listDetailURL(client) - if opts != nil { - query, err := opts.ToImageListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ImagePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Get returns data about a specific image by its ID. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete deletes the specified image ID. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/compute/v2/images/results.go b/openstack/compute/v2/images/results.go deleted file mode 100644 index 33ba105c39..0000000000 --- a/openstack/compute/v2/images/results.go +++ /dev/null @@ -1,99 +0,0 @@ -package images - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// GetResult is the response from a Get operation. Call its Extract method to -// interpret it as an Image. -type GetResult struct { - gophercloud.Result -} - -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// Extract interprets a GetResult as an Image. -func (r GetResult) Extract() (*Image, error) { - var s struct { - Image *Image `json:"image"` - } - err := r.ExtractInto(&s) - return s.Image, err -} - -// Image represents an Image returned by the Compute API. -type Image struct { - // ID is the unique ID of an image. - ID string - - // Created is the date when the image was created. - Created string - - // MinDisk is the minimum amount of disk a flavor must have to be able - // to create a server based on the image, measured in GB. - MinDisk int - - // MinRAM is the minimum amount of RAM a flavor must have to be able - // to create a server based on the image, measured in MB. - MinRAM int - - // Name provides a human-readable moniker for the OS image. - Name string - - // The Progress and Status fields indicate image-creation status. - Progress int - - // Status is the current status of the image. - Status string - - // Update is the date when the image was updated. - Updated string - - // Metadata provides free-form key/value pairs that further describe the - // image. - Metadata map[string]interface{} -} - -// ImagePage contains a single page of all Images returne from a ListDetail -// operation. Use ExtractImages to convert it into a slice of usable structs. -type ImagePage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if an ImagePage contains no Image results. -func (page ImagePage) IsEmpty() (bool, error) { - if page.StatusCode == 204 { - return true, nil - } - - images, err := ExtractImages(page) - return len(images) == 0, err -} - -// NextPageURL uses the response's embedded link reference to navigate to the -// next page of results. -func (page ImagePage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"images_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// ExtractImages converts a page of List results into a slice of usable Image -// structs. -func ExtractImages(r pagination.Page) ([]Image, error) { - var s struct { - Images []Image `json:"images"` - } - err := (r.(ImagePage)).ExtractInto(&s) - return s.Images, err -} diff --git a/openstack/compute/v2/images/testing/doc.go b/openstack/compute/v2/images/testing/doc.go deleted file mode 100644 index db10451530..0000000000 --- a/openstack/compute/v2/images/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// images unit tests -package testing diff --git a/openstack/compute/v2/images/testing/requests_test.go b/openstack/compute/v2/images/testing/requests_test.go deleted file mode 100644 index 91bc4da6cd..0000000000 --- a/openstack/compute/v2/images/testing/requests_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package testing - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "reflect" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/images" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestListImages(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/images/detail", 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") - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ` - { - "images": [ - { - "status": "ACTIVE", - "updated": "2014-09-23T12:54:56Z", - "id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", - "OS-EXT-IMG-SIZE:size": 476704768, - "name": "F17-x86_64-cfntools", - "created": "2014-09-23T12:54:52Z", - "minDisk": 0, - "progress": 100, - "minRam": 0, - "metadata": { - "architecture": "x86_64", - "block_device_mapping": { - "guest_format": null, - "boot_index": 0, - "device_name": "/dev/vda", - "delete_on_termination": false - } - } - }, - { - "status": "ACTIVE", - "updated": "2014-09-23T12:51:43Z", - "id": "f90f6034-2570-4974-8351-6b49732ef2eb", - "OS-EXT-IMG-SIZE:size": 13167616, - "name": "cirros-0.3.2-x86_64-disk", - "created": "2014-09-23T12:51:42Z", - "minDisk": 0, - "progress": 100, - "minRam": 0 - } - ] - } - `) - case "2": - fmt.Fprintf(w, `{ "images": [] }`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) - - pages := 0 - options := &images.ListOpts{Limit: 2} - err := images.ListDetail(fake.ServiceClient(), options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pages++ - - actual, err := images.ExtractImages(page) - if err != nil { - return false, err - } - - expected := []images.Image{ - { - ID: "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", - Name: "F17-x86_64-cfntools", - Created: "2014-09-23T12:54:52Z", - Updated: "2014-09-23T12:54:56Z", - MinDisk: 0, - MinRAM: 0, - Progress: 100, - Status: "ACTIVE", - Metadata: map[string]interface{}{ - "architecture": "x86_64", - "block_device_mapping": map[string]interface{}{ - "guest_format": interface{}(nil), - "boot_index": float64(0), - "device_name": "/dev/vda", - "delete_on_termination": false, - }, - }, - }, - { - ID: "f90f6034-2570-4974-8351-6b49732ef2eb", - Name: "cirros-0.3.2-x86_64-disk", - Created: "2014-09-23T12:51:42Z", - Updated: "2014-09-23T12:51:43Z", - MinDisk: 0, - MinRAM: 0, - Progress: 100, - Status: "ACTIVE", - }, - } - - if !reflect.DeepEqual(expected, actual) { - t.Errorf("Unexpected page contents: expected %#v, got %#v", expected, actual) - } - - return false, nil - }) - - if err != nil { - t.Fatalf("EachPage error: %v", err) - } - if pages != 1 { - t.Errorf("Expected one page, got %d", pages) - } -} - -func TestGetImage(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/images/12345678", 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, ` - { - "image": { - "status": "ACTIVE", - "updated": "2014-09-23T12:54:56Z", - "id": "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", - "OS-EXT-IMG-SIZE:size": 476704768, - "name": "F17-x86_64-cfntools", - "created": "2014-09-23T12:54:52Z", - "minDisk": 0, - "progress": 100, - "minRam": 0, - "metadata": { - "architecture": "x86_64", - "block_device_mapping": { - "guest_format": null, - "boot_index": 0, - "device_name": "/dev/vda", - "delete_on_termination": false - } - } - } - } - `) - }) - - actual, err := images.Get(context.TODO(), fake.ServiceClient(), "12345678").Extract() - if err != nil { - t.Fatalf("Unexpected error from Get: %v", err) - } - - expected := &images.Image{ - Status: "ACTIVE", - Updated: "2014-09-23T12:54:56Z", - ID: "f3e4a95d-1f4f-4989-97ce-f3a1fb8c04d7", - Name: "F17-x86_64-cfntools", - Created: "2014-09-23T12:54:52Z", - MinDisk: 0, - Progress: 100, - MinRAM: 0, - Metadata: map[string]interface{}{ - "architecture": "x86_64", - "block_device_mapping": map[string]interface{}{ - "guest_format": interface{}(nil), - "boot_index": float64(0), - "device_name": "/dev/vda", - "delete_on_termination": false, - }, - }, - } - - if !reflect.DeepEqual(expected, actual) { - t.Errorf("Expected %#v, but got %#v", expected, actual) - } -} - -func TestNextPageURL(t *testing.T) { - var page images.ImagePage - var body map[string]interface{} - bodyString := []byte(`{"images":{"links":[{"href":"http://192.154.23.87/12345/images/image3","rel":"bookmark"}]}, "images_links":[{"href":"http://192.154.23.87/12345/images/image4","rel":"next"}]}`) - err := json.Unmarshal(bodyString, &body) - if err != nil { - t.Fatalf("Error unmarshaling data into page body: %v", err) - } - page.Body = body - - expected := "http://192.154.23.87/12345/images/image4" - actual, err := page.NextPageURL() - th.AssertNoErr(t, err) - th.CheckEquals(t, expected, actual) -} - -// Test Image delete -func TestDeleteImage(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/images/12345678", 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) - }) - - res := images.Delete(context.TODO(), fake.ServiceClient(), "12345678") - th.AssertNoErr(t, res.Err) -} diff --git a/openstack/compute/v2/images/urls.go b/openstack/compute/v2/images/urls.go deleted file mode 100644 index 2f351b307f..0000000000 --- a/openstack/compute/v2/images/urls.go +++ /dev/null @@ -1,15 +0,0 @@ -package images - -import "github.com/gophercloud/gophercloud/v2" - -func listDetailURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("images", "detail") -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("images", id) -} - -func deleteURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("images", id) -} From 68ba6fab02110937f81082c09be39c647e97a6f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Apr 2024 09:31:09 +0000 Subject: [PATCH 1837/2296] build(deps): bump golang.org/x/crypto from 0.21.0 to 0.22.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.21.0 to 0.22.0. - [Commits](https://github.com/golang/crypto/compare/v0.21.0...v0.22.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 16fba4e88e..ad426ca9bb 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.21.6 require ( - golang.org/x/crypto v0.21.0 + golang.org/x/crypto v0.22.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.18.0 // indirect +require golang.org/x/sys v0.19.0 // indirect diff --git a/go.sum b/go.sum index 567ebb0d14..d614b257eb 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= 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 17e052e62344aeb29fd61318f760a197b9cb9086 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:19:28 +0000 Subject: [PATCH 1838/2296] build(deps): bump kiegroup/git-backporting from 4.7.1 to 4.8.0 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.7.1 to 4.8.0. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/fc5dba6703e6e81cbb4f52d15c12384c1bf896c5...c22286f85e0a14ebb66755b381163ab9cd8310fa) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport_v1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport_v1.yaml index db6d41bdac..40714a4582 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport_v1.yaml @@ -36,7 +36,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@fc5dba6703e6e81cbb4f52d15c12384c1bf896c5 + uses: kiegroup/git-backporting@c22286f85e0a14ebb66755b381163ab9cd8310fa with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} From dfa4bec967349c2312c7704545d627a626b11bce Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 22 Feb 2024 12:03:47 +0000 Subject: [PATCH 1839/2296] Add minimal Makefile To quickly run linters, unit tests and potentially acceptance tests against a local DevStack. We will eventually add golangci-lint to the 'lint' target but that's a larger piece of work that should be done separately. We should also rework the GitHub Actions jobs to use these targets and remove the scripts, but that requires more work on job skips to be able to do that. Signed-off-by: Stephen Finucane --- .gitignore | 1 + Makefile | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index d7a5e5293a..8b1b79e617 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea .vscode testing_*.coverprofile +/cover.out diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..79c1cc5615 --- /dev/null +++ b/Makefile @@ -0,0 +1,93 @@ +undefine GOFLAGS + +lint: + go fmt ./... + go vet -tags "fixtures acceptance" ./... +.PHONY: lint + +unit: + go test ./... +.PHONY: unit + +coverage: + go test -covermode count -coverprofile cover.out -coverpkg=./... ./... +.PHONY: coverage + +acceptance: acceptance-baremetal acceptance-blockstorage acceptance-clustering acceptance-compute acceptance-container acceptance-containerinfra acceptance-db acceptance-dns acceptance-identity acceptance-imageservice acceptance-keymanager acceptance-loadbalancer acceptance-messaging acceptance-networking acceptance-objectstorage acceptance-orchestration acceptance-placement acceptance-sharedfilesystems acceptance-workflow +.PHONY: acceptance + +acceptance-baremetal: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/baremetal/... +.PHONY: acceptance-baremetal + +acceptance-blockstorage: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/blockstorage/... +.PHONY: acceptance-blockstorage + +acceptance-clustering: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/clustering/... +.PHONY: acceptance-clustering + +acceptance-compute: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/compute/... +.PHONY: acceptance-compute + +acceptance-container: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/container/... +.PHONY: acceptance-container + +acceptance-containerinfra: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/containerinfra/... +.PHONY: acceptance-containerinfra + +acceptance-db: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/db/... +.PHONY: acceptance-db + +acceptance-dns: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/dns/... +.PHONY: acceptance-dns + +acceptance-identity: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/identity/... +.PHONY: acceptance-identity + +acceptance-image: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/imageservice/... +.PHONY: acceptance-image + +acceptance-keymanager: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/keymanager/... +.PHONY: acceptance-keymanager + +acceptance-loadbalancer: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/loadbalancer/... +.PHONY: acceptance-loadbalancer + +acceptance-messaging: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/messaging/... +.PHONY: acceptance-messaging + +acceptance-networking: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/networking/... +.PHONY: acceptance-networking + +acceptance-objectstorage: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/objectstorage/... +.PHONY: acceptance-objectstorage + +acceptance-orchestration: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/orchestration/... +.PHONY: acceptance-orchestration + +acceptance-placement: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/placement/... +.PHONY: acceptance-placement + +acceptance-sharedfilesystems: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/sharedfilesystems/... +.PHONY: acceptance-sharefilesystems + +acceptance-workflow: + go test -tags "fixtures acceptance" ./internal/acceptance/openstack/workflow/... +.PHONY: acceptance-workflow From a4b0b28a9b62392852d6fae453343e6d4d6bfc79 Mon Sep 17 00:00:00 2001 From: Tuomo Tanskanen Date: Tue, 16 Apr 2024 09:16:59 +0300 Subject: [PATCH 1840/2296] remove golang patch version from main go.mod Since golang 1.21, the patch version in go.mod is trickled to downstream consumers as minimum go version, and to their consumers etc. Remove the patch version, and use only minor version to allow consumers some flexibility. Fixes: #3031 Signed-off-by: Tuomo Tanskanen --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ad426ca9bb..2cd703c0d4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gophercloud/gophercloud/v2 -go 1.21.6 +go 1.21 require ( golang.org/x/crypto v0.22.0 From 42c4b25d1ab45c69b98b58cb3c873f21217e0547 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Fri, 9 Feb 2024 13:06:21 +0100 Subject: [PATCH 1841/2296] remove the error types for specific response codes This is a breaking change intended for the v2.0 release. Rationale: The ErrDefault series of types (ErrDefault400, ErrDefault401, etc.) serve the apparent purpose of allowing to assert on a specific response code (e.g. to handle 404 responses differently from other 4xx/5xx responses). However, because of their design, they often tend to obscure the real underlying error message in many cases, reporting just something like "Method not allowed" instead of the default error string on ErrUnexpectedResponseCode that explains which request failed and includes the actual error message returned by the OpenStack service. This commit removes the types, and all of the associated boilerplate, and replaces them with a single small helper function for quickly asserting that an error is an ErrUnexpectedResponseCode with a specific response code. This also gets rid of the undocumented and untested ProviderClient.ErrorContext interface that I have legitimately no idea what it's supposed to be good for, besides maybe skipping the generation of ErrDefaultXXX-type errors. --- errors.go | 253 ++---------------- .../openstack/blockstorage/v2/blockstorage.go | 5 +- .../openstack/blockstorage/v3/blockstorage.go | 9 +- .../blockstorage/v3/snapshots_test.go | 3 +- .../openstack/clustering/v1/clustering.go | 2 +- .../openstack/compute/v2/compute.go | 3 +- .../containerinfra/v1/containerinfra.go | 3 +- .../containerinfra/v1/nodegroups_test.go | 3 +- .../openstack/identity/v3/federation_test.go | 5 +- .../openstack/loadbalancer/v2/loadbalancer.go | 23 +- .../networking/v2/extensions/layer3/layer3.go | 5 +- .../sharedfilesystems/v2/replicas.go | 5 +- .../sharedfilesystems/v2/shareaccessrules.go | 3 +- .../openstack/sharedfilesystems/v2/shares.go | 5 +- .../sharedfilesystems/v2/sharetransfers.go | 3 +- .../sharedfilesystems/v2/snapshots.go | 5 +- .../v1/nodes/testing/requests_test.go | 9 +- openstack/compute/v2/tags/results.go | 12 +- .../v1/nodegroups/testing/requests_test.go | 49 +--- .../v2/extensions/attributestags/results.go | 8 +- .../sharedfilesystems/v2/errors/errors.go | 30 +-- provider_client.go | 63 ----- testing/errors_test.go | 89 ++---- 23 files changed, 113 insertions(+), 482 deletions(-) diff --git a/errors.go b/errors.go index 8ab592ca49..732ac22c67 100644 --- a/errors.go +++ b/errors.go @@ -1,6 +1,7 @@ package gophercloud import ( + "errors" "fmt" "net/http" "strings" @@ -99,244 +100,22 @@ func (e ErrUnexpectedResponseCode) GetStatusCode() int { return e.Actual } -// StatusCodeError is a convenience interface to easily allow access to the -// status code field of the various ErrDefault* types. +// ResponseCodeIs returns true if this error is or contains an ErrUnexpectedResponseCode reporting +// that the request failed with the given response code. For example, this checks if a request +// failed because of a 404 error: // -// By using this interface, you only have to make a single type cast of -// the returned error to err.(StatusCodeError) and then call GetStatusCode() -// instead of having a large switch statement checking for each of the -// ErrDefault* types. -type StatusCodeError interface { - Error() string - GetStatusCode() int -} - -// ErrDefault400 is the default error type returned on a 400 HTTP response code. -type ErrDefault400 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault400) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault401 is the default error type returned on a 401 HTTP response code. -type ErrDefault401 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault401) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault403 is the default error type returned on a 403 HTTP response code. -type ErrDefault403 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault403) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault404 is the default error type returned on a 404 HTTP response code. -type ErrDefault404 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault404) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault405 is the default error type returned on a 405 HTTP response code. -type ErrDefault405 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault405) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault408 is the default error type returned on a 408 HTTP response code. -type ErrDefault408 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault408) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault409 is the default error type returned on a 409 HTTP response code. -type ErrDefault409 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault409) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault429 is the default error type returned on a 429 HTTP response code. -type ErrDefault429 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault429) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault500 is the default error type returned on a 500 HTTP response code. -type ErrDefault500 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault500) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault502 is the default error type returned on a 502 HTTP response code. -type ErrDefault502 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault502) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault503 is the default error type returned on a 503 HTTP response code. -type ErrDefault503 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault503) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -// ErrDefault504 is the default error type returned on a 504 HTTP response code. -type ErrDefault504 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault504) Unwrap() error { - return e.ErrUnexpectedResponseCode -} - -func (e ErrDefault400) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Bad request with: [%s %s], error message: %s", - e.Method, e.URL, e.Body, - ) - return e.choseErrString() -} -func (e ErrDefault401) Error() string { - return "Authentication failed" -} -func (e ErrDefault403) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Request forbidden: [%s %s], error message: %s", - e.Method, e.URL, e.Body, - ) - return e.choseErrString() -} -func (e ErrDefault404) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Resource not found: [%s %s], error message: %s", - e.Method, e.URL, e.Body, - ) - return e.choseErrString() -} -func (e ErrDefault405) Error() string { - return "Method not allowed" -} -func (e ErrDefault408) Error() string { - return "The server timed out waiting for the request" -} -func (e ErrDefault429) Error() string { - return "Too many requests have been sent in a given amount of time. Pause" + - " requests, wait up to one minute, and try again." -} -func (e ErrDefault500) Error() string { - return "Internal Server Error" -} -func (e ErrDefault502) Error() string { - return "Bad Gateway" -} -func (e ErrDefault503) Error() string { - return "The service is currently unable to handle the request due to a temporary" + - " overloading or maintenance. This is a temporary condition. Try again later." -} -func (e ErrDefault504) Error() string { - return "Gateway Timeout" -} - -// Err400er is the interface resource error types implement to override the error message -// from a 400 error. -type Err400er interface { - Error400(ErrUnexpectedResponseCode) error -} - -// Err401er is the interface resource error types implement to override the error message -// from a 401 error. -type Err401er interface { - Error401(ErrUnexpectedResponseCode) error -} - -// Err403er is the interface resource error types implement to override the error message -// from a 403 error. -type Err403er interface { - Error403(ErrUnexpectedResponseCode) error -} - -// Err404er is the interface resource error types implement to override the error message -// from a 404 error. -type Err404er interface { - Error404(ErrUnexpectedResponseCode) error -} - -// Err405er is the interface resource error types implement to override the error message -// from a 405 error. -type Err405er interface { - Error405(ErrUnexpectedResponseCode) error -} - -// Err408er is the interface resource error types implement to override the error message -// from a 408 error. -type Err408er interface { - Error408(ErrUnexpectedResponseCode) error -} - -// Err409er is the interface resource error types implement to override the error message -// from a 409 error. -type Err409er interface { - Error409(ErrUnexpectedResponseCode) error -} - -// Err429er is the interface resource error types implement to override the error message -// from a 429 error. -type Err429er interface { - Error429(ErrUnexpectedResponseCode) error -} - -// Err500er is the interface resource error types implement to override the error message -// from a 500 error. -type Err500er interface { - Error500(ErrUnexpectedResponseCode) error -} - -// Err502er is the interface resource error types implement to override the error message -// from a 502 error. -type Err502er interface { - Error502(ErrUnexpectedResponseCode) error -} - -// Err503er is the interface resource error types implement to override the error message -// from a 503 error. -type Err503er interface { - Error503(ErrUnexpectedResponseCode) error -} - -// Err504er is the interface resource error types implement to override the error message -// from a 504 error. -type Err504er interface { - Error504(ErrUnexpectedResponseCode) error +// allServers, err := servers.List(client, servers.ListOpts{}) +// if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { +// handleNotFound() +// } +// +// It is safe to pass a nil error, in which case this function always returns false. +func ResponseCodeIs(err error, status int) bool { + var codeError ErrUnexpectedResponseCode + if errors.As(err, &codeError) { + return codeError.Actual == status + } + return false } // ErrTimeOut is the error type returned when an operations times out. diff --git a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go index ab209f12ac..40aaefb052 100644 --- a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -6,6 +6,7 @@ package v2 import ( "context" "fmt" + "net/http" "strings" "testing" "time" @@ -209,7 +210,7 @@ func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID stri // could not be deleted. This works best when used as a deferred function. func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { if err := backups.Delete(context.TODO(), client, backupID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Logf("Backup %s is already deleted", backupID) return } @@ -225,7 +226,7 @@ func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) e return tools.WaitFor(func(ctx context.Context) (bool, error) { current, err := backups.Get(ctx, client, id).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok && status == "deleted" { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) && status == "deleted" { return true, nil } return false, err diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 42941bf876..83b7ca0217 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -6,6 +6,7 @@ package v3 import ( "context" "fmt" + "net/http" "strings" "testing" "time" @@ -274,7 +275,7 @@ func CreatePrivateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (* func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { err := snapshots.Delete(context.TODO(), client, snapshot.ID).ExtractErr() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Logf("Snapshot %s is already deleted", snapshot.ID) return } @@ -305,7 +306,7 @@ func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volum err := volumes.Delete(context.TODO(), client, volume.ID, volumes.DeleteOpts{}).ExtractErr() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Logf("Volume %s is already deleted", volume.ID) return } @@ -427,7 +428,7 @@ func CreateBackup(t *testing.T, client *gophercloud.ServiceClient, volumeID stri // could not be deleted. This works best when used as a deferred function. func DeleteBackup(t *testing.T, client *gophercloud.ServiceClient, backupID string) { if err := backups.Delete(context.TODO(), client, backupID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Logf("Backup %s is already deleted", backupID) return } @@ -443,7 +444,7 @@ func WaitForBackupStatus(client *gophercloud.ServiceClient, id, status string) e return tools.WaitFor(func(ctx context.Context) (bool, error) { current, err := backups.Get(ctx, client, id).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok && status == "deleted" { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) && status == "deleted" { return true, nil } return false, err diff --git a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go index 4cb389882f..bdb4c8fed3 100644 --- a/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/snapshots_test.go @@ -5,6 +5,7 @@ package v3 import ( "context" "fmt" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2" @@ -188,7 +189,7 @@ func TestSnapshotsForceDelete(t *testing.T) { err = tools.WaitFor(func(ctx context.Context) (bool, error) { _, err := snapshots.Get(ctx, client, snapshot.ID).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return true, nil } } diff --git a/internal/acceptance/openstack/clustering/v1/clustering.go b/internal/acceptance/openstack/clustering/v1/clustering.go index 09ce437d63..c7233fae41 100644 --- a/internal/acceptance/openstack/clustering/v1/clustering.go +++ b/internal/acceptance/openstack/clustering/v1/clustering.go @@ -453,7 +453,7 @@ func WaitForNodeStatus(client *gophercloud.ServiceClient, id string, status stri return tools.WaitFor(func(ctx context.Context) (bool, error) { latest, err := nodes.Get(ctx, client, id).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETED" { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) && status == "DELETED" { return true, nil } diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index e0f892984a..c2cd4b58b3 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -7,6 +7,7 @@ import ( "crypto/rand" "crypto/rsa" "fmt" + "net/http" "testing" "time" @@ -813,7 +814,7 @@ func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *serve } if err := WaitForComputeStatus(client, server, "DELETED"); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Logf("Deleted server: %s", server.ID) return } diff --git a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go index 99933bd11f..67a11fdef1 100644 --- a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math" + "net/http" "strings" "testing" "time" @@ -188,7 +189,7 @@ func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status return tools.WaitForTimeout(func(ctx context.Context) (bool, error) { cluster, err := clusters.Get(ctx, client, clusterID).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok && status == "DELETE_COMPLETE" { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) && status == "DELETE_COMPLETE" { return true, nil } diff --git a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go index 3feba2bd64..d6493fa338 100644 --- a/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/nodegroups_test.go @@ -5,6 +5,7 @@ package v1 import ( "context" "fmt" + "net/http" "testing" "time" @@ -165,7 +166,7 @@ func testNodeGroupDelete(t *testing.T, client *gophercloud.ServiceClient, cluste // Wait for the node group to be deleted err = tools.WaitFor(func(ctx context.Context) (bool, error) { _, err := nodegroups.Get(ctx, client, clusterID, nodeGroupID).Extract() - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return true, nil } return false, nil diff --git a/internal/acceptance/openstack/identity/v3/federation_test.go b/internal/acceptance/openstack/identity/v3/federation_test.go index d64754145e..b52d54cc30 100644 --- a/internal/acceptance/openstack/identity/v3/federation_test.go +++ b/internal/acceptance/openstack/identity/v3/federation_test.go @@ -4,6 +4,7 @@ package v3 import ( "context" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2" @@ -115,7 +116,5 @@ func TestMappingsCRUD(t *testing.T) { th.AssertNoErr(t, err) resp := federation.GetMapping(context.TODO(), client, mappingName) - th.AssertErr(t, resp.Err) - _, ok := resp.Err.(gophercloud.ErrDefault404) - th.AssertEquals(t, true, ok) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(resp.Err, http.StatusNotFound)) } diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 19206c2c3b..e8596506ef 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -3,6 +3,7 @@ package v2 import ( "context" "fmt" + "net/http" "strings" "testing" @@ -504,7 +505,7 @@ func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, polic t.Logf("Attempting to delete l7 policy %s", policyID) if err := l7policies.Delete(context.TODO(), client, policyID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { + if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Fatalf("Unable to delete l7 policy: %v", err) } } @@ -523,7 +524,7 @@ func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyI t.Logf("Attempting to delete l7 rule %s", ruleID) if err := l7policies.DeleteRule(context.TODO(), client, policyID, ruleID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { + if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Fatalf("Unable to delete l7 rule: %v", err) } } @@ -542,7 +543,7 @@ func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, liste t.Logf("Attempting to delete listener %s", listenerID) if err := listeners.Delete(context.TODO(), client, listenerID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { + if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Fatalf("Unable to delete listener: %v", err) } } @@ -561,7 +562,7 @@ func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, t.Logf("Attempting to delete member %s", memberID) if err := pools.DeleteMember(context.TODO(), client, poolID, memberID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { + if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Fatalf("Unable to delete member: %s", memberID) } } @@ -584,7 +585,7 @@ func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID st } if err := loadbalancers.Delete(context.TODO(), client, lbID, deleteOpts).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { + if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Fatalf("Unable to delete loadbalancer: %v", err) } } @@ -628,7 +629,7 @@ func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monito t.Logf("Attempting to delete monitor %s", monitorID) if err := monitors.Delete(context.TODO(), client, monitorID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { + if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Fatalf("Unable to delete monitor: %v", err) } } @@ -646,7 +647,7 @@ func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID st t.Logf("Attempting to delete pool %s", poolID) if err := pools.Delete(context.TODO(), client, poolID).ExtractErr(); err != nil { - if _, ok := err.(gophercloud.ErrDefault404); !ok { + if !gophercloud.ResponseCodeIs(err, http.StatusNotFound) { t.Fatalf("Unable to delete pool: %v", err) } } @@ -663,12 +664,8 @@ func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status st return tools.WaitFor(func(ctx context.Context) (bool, error) { current, err := loadbalancers.Get(ctx, client, lbID).Extract() if err != nil { - if httpStatus, ok := err.(gophercloud.ErrDefault404); ok { - if httpStatus.Actual == 404 { - if status == "DELETED" { - return true, nil - } - } + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) && status == "DELETED" { + return true, nil } return false, err } diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index b9d5eecbe3..1edc2d0cc8 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -2,6 +2,7 @@ package layer3 import ( "context" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/addressscopes" @@ -299,7 +300,7 @@ func WaitForRouterToDelete(client *gophercloud.ServiceClient, routerID string) e return tools.WaitFor(func(ctx context.Context) (bool, error) { _, err := routers.Get(ctx, client, routerID).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return true, nil } @@ -329,7 +330,7 @@ func WaitForRouterInterfaceToDetach(client *gophercloud.ServiceClient, routerInt return tools.WaitFor(func(ctx context.Context) (bool, error) { r, err := ports.Get(ctx, client, routerInterfaceID).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return true, nil } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go index 1597546187..f9ab7e9827 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go @@ -3,6 +3,7 @@ package v2 import ( "context" "fmt" + "net/http" "strings" "testing" @@ -42,7 +43,7 @@ func CreateReplica(t *testing.T, client *gophercloud.ServiceClient, share *share func DeleteReplica(t *testing.T, client *gophercloud.ServiceClient, replica *replicas.Replica) { err := replicas.Delete(context.TODO(), client, replica.ID).ExtractErr() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return } t.Errorf("Unable to delete replica %s: %v", replica.ID, err) @@ -78,7 +79,7 @@ func waitForReplicaStatus(t *testing.T, c *gophercloud.ServiceClient, id, status current, err = replicas.Get(ctx, c, id).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { switch status { case "deleted": return true, nil diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go index 6981790fc7..ab6c6434cd 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shareaccessrules.go @@ -3,6 +3,7 @@ package v2 import ( "context" "fmt" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2" @@ -43,7 +44,7 @@ func WaitForShareAccessRule(t *testing.T, client *gophercloud.ServiceClient, acc return tools.WaitFor(func(context.Context) (bool, error) { latest, err := ShareAccessRuleGet(t, client, accessRule.ID) if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return false, nil } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go index 7705f3c830..afb699627c 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares.go @@ -3,6 +3,7 @@ package v2 import ( "context" "fmt" + "net/http" "strings" "testing" @@ -90,7 +91,7 @@ func GetAccessRightsSlice(t *testing.T, client *gophercloud.ServiceClient, share func DeleteShare(t *testing.T, client *gophercloud.ServiceClient, share *shares.Share) { err := shares.Delete(context.TODO(), client, share.ID).ExtractErr() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return } t.Errorf("Unable to delete share %s: %v", share.ID, err) @@ -142,7 +143,7 @@ func waitForStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string current, err = shares.Get(ctx, c, id).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { switch status { case "deleted": return true, nil diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go index 6b95497452..254dfbaae8 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetransfers.go @@ -3,6 +3,7 @@ package v2 import ( "context" "fmt" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2" @@ -39,7 +40,7 @@ func AcceptTransfer(t *testing.T, client *gophercloud.ServiceClient, transferReq func DeleteTransferRequest(t *testing.T, client *gophercloud.ServiceClient, transfer *sharetransfers.Transfer) { err := sharetransfers.Delete(context.TODO(), client, transfer.ID).ExtractErr() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return } t.Errorf("Unable to delete share transfer %s: %v", transfer.ID, err) diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go index 9269be378b..4fb3128f29 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots.go @@ -3,6 +3,7 @@ package v2 import ( "context" "fmt" + "net/http" "strings" "testing" @@ -55,7 +56,7 @@ func ListSnapshots(t *testing.T, client *gophercloud.ServiceClient) ([]snapshots func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) { err := snapshots.Delete(context.TODO(), client, snapshot.ID).ExtractErr() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { return } t.Errorf("Unable to delete snapshot %s: %v", snapshot.ID, err) @@ -73,7 +74,7 @@ func waitForSnapshotStatus(t *testing.T, c *gophercloud.ServiceClient, id, statu err := tools.WaitFor(func(ctx context.Context) (bool, error) { current, err := snapshots.Get(ctx, c, id).Extract() if err != nil { - if _, ok := err.(gophercloud.ErrDefault404); ok { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { switch status { case "deleted": return true, nil diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index 33e1640393..0306484a08 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -2,6 +2,7 @@ package testing import ( "context" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2" @@ -336,8 +337,8 @@ func TestNodeChangeProvisionStateCleanWithConflict(t *testing.T) { }, }).ExtractErr() - if _, ok := err.(gophercloud.ErrDefault409); !ok { - t.Fatal("ErrDefault409 was expected to occur") + if !gophercloud.ResponseCodeIs(err, http.StatusConflict) { + t.Fatalf("expected 409 response, but got %s", err.Error()) } } @@ -406,8 +407,8 @@ func TestChangePowerStateWithConflict(t *testing.T) { c := client.ServiceClient() err := nodes.ChangePowerState(context.TODO(), c, "1234asdf", opts).ExtractErr() - if _, ok := err.(gophercloud.ErrDefault409); !ok { - t.Fatal("ErrDefault409 was expected to occur") + if !gophercloud.ResponseCodeIs(err, http.StatusConflict) { + t.Fatalf("expected 409 response, but got %s", err.Error()) } } diff --git a/openstack/compute/v2/tags/results.go b/openstack/compute/v2/tags/results.go index a3158504a6..9a471420fd 100644 --- a/openstack/compute/v2/tags/results.go +++ b/openstack/compute/v2/tags/results.go @@ -1,6 +1,10 @@ package tags -import "github.com/gophercloud/gophercloud/v2" +import ( + "net/http" + + "github.com/gophercloud/gophercloud/v2" +) type commonResult struct { gophercloud.Result @@ -27,10 +31,8 @@ type CheckResult struct { func (r CheckResult) Extract() (bool, error) { exists := r.Err == nil - if r.Err != nil { - if _, ok := r.Err.(gophercloud.ErrDefault404); ok { - r.Err = nil - } + if gophercloud.ResponseCodeIs(r.Err, http.StatusNotFound) { + r.Err = nil } return exists, r.Err diff --git a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go index bf4a4f8d6d..6e929a2e17 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/requests_test.go +++ b/openstack/containerinfra/v1/nodegroups/testing/requests_test.go @@ -2,6 +2,7 @@ package testing import ( "context" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2" @@ -37,10 +38,7 @@ func TestGetNodeGroupNotFound(t *testing.T) { sc.Endpoint = sc.Endpoint + "v1/" _, err := nodegroups.Get(context.TODO(), sc, clusterUUID, badNodeGroupUUID).Extract() - th.AssertEquals(t, true, err != nil) - - _, isNotFound := err.(gophercloud.ErrDefault404) - th.AssertEquals(t, true, isNotFound) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } // TestGetNodeGroupClusterNotFound tries to get a node group in @@ -55,10 +53,7 @@ func TestGetNodeGroupClusterNotFound(t *testing.T) { sc.Endpoint = sc.Endpoint + "v1/" _, err := nodegroups.Get(context.TODO(), sc, badClusterUUID, badNodeGroupUUID).Extract() - th.AssertEquals(t, true, err != nil) - - _, isNotFound := err.(gophercloud.ErrDefault404) - th.AssertEquals(t, true, isNotFound) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } // TestListNodeGroupsSuccess lists the node groups of a cluster successfully. @@ -116,10 +111,7 @@ func TestListNodeGroupsClusterNotFound(t *testing.T) { sc.Endpoint = sc.Endpoint + "v1/" _, err := nodegroups.List(sc, clusterUUID, nodegroups.ListOpts{}).AllPages(context.TODO()) - th.AssertEquals(t, true, err != nil) - - _, isNotFound := err.(gophercloud.ErrDefault404) - th.AssertEquals(t, true, isNotFound) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } // TestCreateNodeGroupSuccess creates a node group successfully. @@ -158,9 +150,7 @@ func TestCreateNodeGroupDuplicate(t *testing.T) { } _, err := nodegroups.Create(context.TODO(), sc, clusterUUID, createOpts).Extract() - th.AssertEquals(t, true, err != nil) - _, isNotAccepted := err.(gophercloud.ErrDefault409) - th.AssertEquals(t, true, isNotAccepted) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) } // TestCreateNodeGroupMaster creates a node group with @@ -180,9 +170,7 @@ func TestCreateNodeGroupMaster(t *testing.T) { } _, err := nodegroups.Create(context.TODO(), sc, clusterUUID, createOpts).Extract() - th.AssertEquals(t, true, err != nil) - _, isBadRequest := err.(gophercloud.ErrDefault400) - th.AssertEquals(t, true, isBadRequest) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } // TestCreateNodeGroupBadSizes creates a node group with @@ -204,9 +192,7 @@ func TestCreateNodeGroupBadSizes(t *testing.T) { } _, err := nodegroups.Create(context.TODO(), sc, clusterUUID, createOpts).Extract() - th.AssertEquals(t, true, err != nil) - _, isNotAccepted := err.(gophercloud.ErrDefault409) - th.AssertEquals(t, true, isNotAccepted) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) } // TestUpdateNodeGroupSuccess updates a node group successfully. @@ -252,9 +238,7 @@ func TestUpdateNodeGroupInternal(t *testing.T) { } _, err := nodegroups.Update(context.TODO(), sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() - th.AssertEquals(t, true, err != nil) - _, isBadRequest := err.(gophercloud.ErrDefault400) - th.AssertEquals(t, true, isBadRequest) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } // TestUpdateNodeGroupBadField tries to update a @@ -277,9 +261,7 @@ func TestUpdateNodeGroupBadField(t *testing.T) { } _, err := nodegroups.Update(context.TODO(), sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() - th.AssertEquals(t, true, err != nil) - _, isBadRequest := err.(gophercloud.ErrDefault400) - th.AssertEquals(t, true, isBadRequest) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } // TestUpdateNodeGroupBadMin tries to set a minimum node count @@ -302,9 +284,7 @@ func TestUpdateNodeGroupBadMin(t *testing.T) { } _, err := nodegroups.Update(context.TODO(), sc, clusterUUID, nodeGroup2UUID, updateOpts).Extract() - th.AssertEquals(t, true, err != nil) - _, isNotAccepted := err.(gophercloud.ErrDefault409) - th.AssertEquals(t, true, isNotAccepted) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) } // TestDeleteNodeGroupSuccess deletes a node group successfully. @@ -332,8 +312,7 @@ func TestDeleteNodeGroupNotFound(t *testing.T) { sc.Endpoint = sc.Endpoint + "v1/" err := nodegroups.Delete(context.TODO(), sc, clusterUUID, badNodeGroupUUID).ExtractErr() - _, isNotFound := err.(gophercloud.ErrDefault404) - th.AssertEquals(t, true, isNotFound) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } // TestDeleteNodeGroupClusterNotFound tries to delete a node group in a cluster that does not exist. @@ -347,8 +326,7 @@ func TestDeleteNodeGroupClusterNotFound(t *testing.T) { sc.Endpoint = sc.Endpoint + "v1/" err := nodegroups.Delete(context.TODO(), sc, badClusterUUID, badNodeGroupUUID).ExtractErr() - _, isNotFound := err.(gophercloud.ErrDefault404) - th.AssertEquals(t, true, isNotFound) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } // TestDeleteNodeGroupDefault tries to delete a protected default node group. @@ -362,6 +340,5 @@ func TestDeleteNodeGroupDefault(t *testing.T) { sc.Endpoint = sc.Endpoint + "v1/" err := nodegroups.Delete(context.TODO(), sc, clusterUUID, nodeGroup2UUID).ExtractErr() - _, isBadRequest := err.(gophercloud.ErrDefault400) - th.AssertEquals(t, true, isBadRequest) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } diff --git a/openstack/networking/v2/extensions/attributestags/results.go b/openstack/networking/v2/extensions/attributestags/results.go index 1dba4cc7ab..9114cc7c28 100644 --- a/openstack/networking/v2/extensions/attributestags/results.go +++ b/openstack/networking/v2/extensions/attributestags/results.go @@ -1,6 +1,8 @@ package attributestags import ( + "net/http" + "github.com/gophercloud/gophercloud/v2" ) @@ -47,10 +49,8 @@ type ConfirmResult struct { func (r ConfirmResult) Extract() (bool, error) { exists := r.Err == nil - if r.Err != nil { - if _, ok := r.Err.(gophercloud.ErrDefault404); ok { - r.Err = nil - } + if gophercloud.ResponseCodeIs(r.Err, http.StatusNotFound) { + r.Err = nil } return exists, r.Err diff --git a/openstack/sharedfilesystems/v2/errors/errors.go b/openstack/sharedfilesystems/v2/errors/errors.go index 1e56210b18..f01d6bd588 100644 --- a/openstack/sharedfilesystems/v2/errors/errors.go +++ b/openstack/sharedfilesystems/v2/errors/errors.go @@ -2,7 +2,7 @@ package errors import ( "encoding/json" - "fmt" + "errors" "github.com/gophercloud/gophercloud/v2" ) @@ -17,28 +17,10 @@ type ErrorDetails map[string]ManilaError // error types from provider_client.go func ExtractErrorInto(rawError error, errorDetails *ErrorDetails) (err error) { - switch e := rawError.(type) { - case gophercloud.ErrDefault400: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - case gophercloud.ErrDefault401: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - case gophercloud.ErrDefault403: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - case gophercloud.ErrDefault404: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - case gophercloud.ErrDefault405: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - case gophercloud.ErrDefault408: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - case gophercloud.ErrDefault429: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - case gophercloud.ErrDefault500: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - case gophercloud.ErrDefault503: - err = json.Unmarshal(e.ErrUnexpectedResponseCode.Body, errorDetails) - default: - err = fmt.Errorf("Unable to extract detailed error message") + var codeError gophercloud.ErrUnexpectedResponseCode + if errors.As(rawError, &codeError) { + return json.Unmarshal(codeError.Body, errorDetails) + } else { + return errors.New("Unable to extract detailed error message") } - - return err } diff --git a/provider_client.go b/provider_client.go index fcbedf1480..89c8335a0a 100644 --- a/provider_client.go +++ b/provider_client.go @@ -327,9 +327,6 @@ type RequestOpts struct { // OmitHeaders specifies the HTTP headers which should be omitted. // OmitHeaders will override MoreHeaders OmitHeaders []string - // ErrorContext specifies the resource error type to return if an error is encountered. - // This lets resources override default error messages based on the response status code. - ErrorContext error // KeepResponseBody specifies whether to keep the HTTP response body. Usually used, when the HTTP // response body is considered for further use. Valid when JSONResponse is nil. KeepResponseBody bool @@ -461,13 +458,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, ResponseHeader: resp.Header, } - errType := options.ErrorContext switch resp.StatusCode { - case http.StatusBadRequest: - err = ErrDefault400{respErr} - if error400er, ok := errType.(Err400er); ok { - err = error400er.Error400(respErr) - } case http.StatusUnauthorized: if client.ReauthFunc != nil && !state.hasReauthenticated { err = client.Reauthenticate(ctx, prereqtok) @@ -498,41 +489,7 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, } return resp, nil } - err = ErrDefault401{respErr} - if error401er, ok := errType.(Err401er); ok { - err = error401er.Error401(respErr) - } - case http.StatusForbidden: - err = ErrDefault403{respErr} - if error403er, ok := errType.(Err403er); ok { - err = error403er.Error403(respErr) - } - case http.StatusNotFound: - err = ErrDefault404{respErr} - if error404er, ok := errType.(Err404er); ok { - err = error404er.Error404(respErr) - } - case http.StatusMethodNotAllowed: - err = ErrDefault405{respErr} - if error405er, ok := errType.(Err405er); ok { - err = error405er.Error405(respErr) - } - case http.StatusRequestTimeout: - err = ErrDefault408{respErr} - if error408er, ok := errType.(Err408er); ok { - err = error408er.Error408(respErr) - } - case http.StatusConflict: - err = ErrDefault409{respErr} - if error409er, ok := errType.(Err409er); ok { - err = error409er.Error409(respErr) - } case http.StatusTooManyRequests, 498: - err = ErrDefault429{respErr} - if error429er, ok := errType.(Err429er); ok { - err = error429er.Error429(respErr) - } - maxTries := client.MaxBackoffRetries if maxTries == 0 { maxTries = DefaultMaxBackoffRetries @@ -550,26 +507,6 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, return client.doRequest(ctx, method, url, options, state) } - case http.StatusInternalServerError: - err = ErrDefault500{respErr} - if error500er, ok := errType.(Err500er); ok { - err = error500er.Error500(respErr) - } - case http.StatusBadGateway: - err = ErrDefault502{respErr} - if error502er, ok := errType.(Err502er); ok { - err = error502er.Error502(respErr) - } - case http.StatusServiceUnavailable: - err = ErrDefault503{respErr} - if error503er, ok := errType.(Err503er); ok { - err = error503er.Error503(respErr) - } - case http.StatusGatewayTimeout: - err = ErrDefault504{respErr} - if error504er, ok := errType.(Err504er); ok { - err = error504er.Error504(respErr) - } } if err == nil { diff --git a/testing/errors_test.go b/testing/errors_test.go index d9e6156adc..8e3d1640b7 100644 --- a/testing/errors_test.go +++ b/testing/errors_test.go @@ -1,7 +1,8 @@ package testing import ( - "errors" + "fmt" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2" @@ -19,78 +20,22 @@ func returnsUnexpectedResp(code int) gophercloud.ErrUnexpectedResponseCode { } } -func TestGetResponseCode404(t *testing.T) { - var err404 error = gophercloud.ErrDefault404{ErrUnexpectedResponseCode: returnsUnexpectedResp(404)} +func TestErrUnexpectedResponseCode(t *testing.T) { + err := gophercloud.ErrUnexpectedResponseCode{ + URL: "http://example.com", + Method: "GET", + Expected: []int{200}, + Actual: 404, + Body: []byte("the response body"), + ResponseHeader: nil, + } - err, ok := err404.(gophercloud.StatusCodeError) - th.AssertEquals(t, true, ok) th.AssertEquals(t, err.GetStatusCode(), 404) + th.AssertEquals(t, gophercloud.ResponseCodeIs(err, http.StatusNotFound), true) + th.AssertEquals(t, gophercloud.ResponseCodeIs(err, http.StatusInternalServerError), false) - 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) { - var err502 error = gophercloud.ErrDefault502{ErrUnexpectedResponseCode: returnsUnexpectedResp(502)} - - 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) { - var err504 error = gophercloud.ErrDefault504{ErrUnexpectedResponseCode: returnsUnexpectedResp(504)} - - 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") - } - }) + //even if application code wraps our error, ResponseCodeIs() should still work + errWrapped := fmt.Errorf("could not frobnicate the foobar: %w", err) + th.AssertEquals(t, gophercloud.ResponseCodeIs(errWrapped, http.StatusNotFound), true) + th.AssertEquals(t, gophercloud.ResponseCodeIs(errWrapped, http.StatusInternalServerError), false) } - -// 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 c4d6bce64f56aa1fd2552b36f757b314a554c1e4 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Fri, 9 Feb 2024 13:33:42 +0100 Subject: [PATCH 1842/2296] fix test assertion matching on ErrDefault401 error format This includes a backwards-incompatible change to the error message returned by ErrUnexpectedResponseCode. Error messages are not supposed to contain line breaks because several logging systems do not handle them well (e.g. syslog and fluentd), so we try at least a little bit to get rid of them. --- errors.go | 5 +++-- testing/provider_client_test.go | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/errors.go b/errors.go index 732ac22c67..3595ce47a5 100644 --- a/errors.go +++ b/errors.go @@ -1,6 +1,7 @@ package gophercloud import ( + "bytes" "errors" "fmt" "net/http" @@ -89,8 +90,8 @@ type ErrUnexpectedResponseCode struct { func (e ErrUnexpectedResponseCode) Error() string { e.DefaultErrString = fmt.Sprintf( - "Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s", - e.Expected, e.Method, e.URL, e.Actual, e.Body, + "Expected HTTP response code %v when accessing [%s %s], but got %d instead: %s", + e.Expected, e.Method, e.URL, e.Actual, bytes.TrimSpace(e.Body), ) return e.choseErrString() } diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 54c0d1e784..fa789ba459 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "net/http/httptest" + "regexp" "strconv" "strings" "sync" @@ -364,8 +365,10 @@ func TestRequestReauthsAtMostOnce(t *testing.T) { // did not attempt reauthentication again and just passed that 401 response to // the caller as ErrDefault401. _, err := p.Request(context.TODO(), "GET", th.Endpoint()+"/route", &gophercloud.RequestOpts{}) - expectedErrorMessage := "Successfully re-authenticated, but got error executing request: Authentication failed" - th.AssertEquals(t, expectedErrorMessage, err.Error()) + expectedErrorRx := regexp.MustCompile(`^Successfully re-authenticated, but got error executing request: Expected HTTP response code \[200\] when accessing \[GET http://[^/]*//route\], but got 401 instead: unauthorized$`) + if !expectedErrorRx.MatchString(err.Error()) { + t.Errorf("expected error that looks like %q, but got %q", expectedErrorRx.String(), err.Error()) + } } func TestRequestWithContext(t *testing.T) { From 0541b41d62d596c666dd32366fc4e08e8ae2bf37 Mon Sep 17 00:00:00 2001 From: nikParasyr Date: Mon, 22 Apr 2024 11:22:41 +0200 Subject: [PATCH 1843/2296] Add ci jobs for openstack caracal Openstack caracal has been released. Update functional workflows to add jobs that cover it --- .github/workflows/functional-baremetal.yaml | 4 ++++ .github/workflows/functional-basic.yaml | 3 +++ .github/workflows/functional-blockstorage.yaml | 3 +++ .github/workflows/functional-clustering.yaml | 4 ++++ .github/workflows/functional-compute.yaml | 3 +++ .github/workflows/functional-containerinfra.yaml | 6 ++++++ .github/workflows/functional-dns.yaml | 3 +++ .github/workflows/functional-fwaas_v2.yaml | 3 +++ .github/workflows/functional-identity.yaml | 3 +++ .github/workflows/functional-image.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, 59 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index beccda9480..603b20caeb 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -15,6 +15,10 @@ jobs: ubuntu_version: ["22.04"] os_system_scope: ["all"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" + os_system_scope: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 1a00ecbaa5..9cf4c92d45 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -17,6 +17,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index c3baf9283a..876c950cc2 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml index 11297618bc..45f1b9fe95 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -14,6 +14,10 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: +## Senlin has not made a caracal release yet +# - name: "caracal" +# openstack_version: "stable/2024.1" +# ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index d6a6ddb0c9..7e96a079f5 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 53d886e35e..0b5e11d1d9 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -17,6 +17,12 @@ jobs: devstack_conf_overrides: | enable_plugin magnum https://github.com/openstack/magnum master MAGNUMCLIENT_BRANCH=master + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" + devstack_conf_overrides: | + enable_plugin magnum https://github.com/openstack/magnum stable/2024.1 + MAGNUMCLIENT_BRANCH=stable/2024.1 - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 5f630e4b34..ca9330e33c 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -15,6 +15,9 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 43a07d9edd..c044fb60e3 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index ee4fd32ae6..2e0d9d8646 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index ef04e891ff..833aea61c2 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 6438c6f7d6..a681272c2d 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 5df541d161..b8ddbf7f53 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 30bd829ce2..8ec2691470 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 2ea6cdaad3..ad5811f0ea 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -14,6 +14,9 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 20fac3df51..cfecd0c2ea 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 1b7dfbdb52..ae11f3f64f 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 0f30fc8d28..41457060ea 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 45ded72fbb..65972c6f71 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -14,6 +14,9 @@ jobs: openstack_version: ["master"] ubuntu_version: ["22.04"] include: + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" From a4190faa463d9b5464288b9438dac54eaaa187fc Mon Sep 17 00:00:00 2001 From: georgeb Date: Thu, 25 Apr 2024 15:50:47 +0200 Subject: [PATCH 1844/2296] Add support for implied roles --- .../openstack/identity/v3/roles_test.go | 48 ++++ openstack/identity/v3/roles/doc.go | 41 ++++ openstack/identity/v3/roles/requests.go | 32 +++ openstack/identity/v3/roles/results.go | 96 ++++++++ .../v3/roles/testing/fixtures_test.go | 228 ++++++++++++++++++ .../v3/roles/testing/requests_test.go | 39 +++ openstack/identity/v3/roles/urls.go | 16 ++ 7 files changed, 500 insertions(+) diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index 60b2d9782f..3ab1e53826 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -789,3 +789,51 @@ func TestRolesAssignToGroupOnProject(t *testing.T) { th.AssertEquals(t, found, true) } + +func TestCRUDRoleInferenceRule(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + priorRoleCreateOpts := roles.CreateOpts{ + Name: "priorRole", + Extra: map[string]interface{}{ + "description": "prior_role description", + }, + } + // Create prior_role in the default domain + priorRole, err := CreateRole(t, client, &priorRoleCreateOpts) + th.AssertNoErr(t, err) + defer DeleteRole(t, client, priorRole.ID) + tools.PrintResource(t, priorRole) + tools.PrintResource(t, priorRole.Extra) + + impliedRoleCreateOpts := roles.CreateOpts{ + Name: "impliedRole", + Extra: map[string]interface{}{ + "description": "implied_role description", + }, + } + // Create implied_role in the default domain + impliedRole, err := CreateRole(t, client, &impliedRoleCreateOpts) + th.AssertNoErr(t, err) + defer DeleteRole(t, client, impliedRole.ID) + tools.PrintResource(t, impliedRole) + tools.PrintResource(t, impliedRole.Extra) + + roleInferenceRule, err := roles.CreateRoleInferenceRule(context.TODO(), client, priorRole.ID, impliedRole.ID).Extract() + defer roles.DeleteRoleInferenceRule(context.TODO(), client, priorRole.ID, impliedRole.ID) + + th.AssertNoErr(t, err) + tools.PrintResource(t, roleInferenceRule) + + getRoleInferenceRule, err := roles.GetRoleInferenceRule(context.TODO(), client, priorRole.ID, impliedRole.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getRoleInferenceRule) + + roleInferenceRuleList, err := roles.ListRoleInferenceRules(context.TODO(), client).Extract() + tools.PrintResource(t, roleInferenceRuleList) + th.AssertNoErr(t, err) + +} diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 3a34b07610..6af00f1632 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -128,6 +128,47 @@ Example to Unassign a Role From a User in a Project ProjectID: projectID, }).ExtractErr() + if err != nil { + panic(err) + } + +Example to Create a Role Inference Rule + + priorRoleID := "7ceab6192ea34a548cc71b24f72e762c" + impliedRoleID := "97e2f5d38bc94842bc3da818c16762ed" + + actual, err := roles.CreateRoleInferenceRule(context.TODO(), identityClient, priorRoleID, impliedRoleID).Extract() + + if err != nil { + panic(err) + } + +Example to Get a Role Inference Rule + + priorRoleID := "7ceab6192ea34a548cc71b24f72e762c" + impliedRoleID := "97e2f5d38bc94842bc3da818c16762ed" + + actual, err := roles.GetRoleInferenceRule(context.TODO(), identityClient, priorRoleID, impliedRoleID).Extract() + + if err != nil { + panic(err) + } + +Example to Delete a Role Inference Rule + + priorRoleID := "7ceab6192ea34a548cc71b24f72e762c" + impliedRoleID := "97e2f5d38bc94842bc3da818c16762ed" + + actual, err := roles.DeleteRoleInferenceRule(context.TODO(), identityClient, priorRoleID, impliedRoleID).ExtractErr() + + if err != nil { + panic(err) + } + +Example to List Role Inference Rules + + actual, err := roles.ListRoleInferenceRules(context.TODO(), identityClient).Extract() + if err != nil { panic(err) } diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index ac6d1bfea4..217f9d334b 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -406,3 +406,35 @@ func Unassign(ctx context.Context, client *gophercloud.ServiceClient, roleID str _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +func CreateRoleInferenceRule(ctx context.Context, client *gophercloud.ServiceClient, priorRoleID, impliedRoleID string) (r CreateImpliedRoleResult) { + resp, err := client.Put(ctx, createRoleInferenceRuleURL(client, priorRoleID, impliedRoleID), nil, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +func GetRoleInferenceRule(ctx context.Context, client *gophercloud.ServiceClient, priorRoleID, impliedRoleID string) (r CreateImpliedRoleResult) { + resp, err := client.Get(ctx, getRoleInferenceRuleURL(client, priorRoleID, impliedRoleID), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +func DeleteRoleInferenceRule(ctx context.Context, client *gophercloud.ServiceClient, priorRoleID, impliedRoleID string) (r DeleteImpliedRoleResult) { + resp, err := client.Delete(ctx, deleteRoleInferenceRuleURL(client, priorRoleID, impliedRoleID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +func ListRoleInferenceRules(ctx context.Context, client *gophercloud.ServiceClient) (r ListImpliedRolesResult) { + resp, err := client.Get(ctx, listRoleInferenceRulesURL(client), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index e6948bb7fd..32f633ab39 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -227,3 +227,99 @@ type AssignmentResult struct { type UnassignmentResult struct { gophercloud.ErrResult } + +type impliedRoleResult struct { + gophercloud.Result +} + +// ImpliedRoleResult is the result of an PUT request. Call its Extract method to +// interpret it as a roleInference. +type CreateImpliedRoleResult struct { + impliedRoleResult +} + +type GetImpliedRoleResult struct { + impliedRoleResult +} +type PriorRole struct { + // ID contains the ID of the role in a prior_role object. + ID string `json:"id,omitempty"` + // Name contains the name of a role in a prior_role object. + Name string `json:"name,omitempty"` + // Links contains referencing links to the prior_role. + Links map[string]interface{} `json:"links"` +} + +type ImpliedRole struct { + // ID contains the ID of the role in an implied_role object. + ID string `json:"id,omitempty"` + // Name contains the name of role in an implied_role. + Name string `json:"name,omitempty"` + // Links contains referencing links to the implied_role. + Links map[string]interface{} `json:"links"` +} + +type RoleInference struct { + // PriorRole is the role object that implies a list of implied_role objects. + PriorRole PriorRole `json:"prior_role"` + // Implies is an array of implied_role objects implied by a prior_role object. + ImpliedRole ImpliedRole `json:"implies"` +} + +type RoleInferenceRule struct { + RoleInference RoleInference `json:"role_inference"` + Links map[string]interface{} `json:"links"` +} + +func (r impliedRoleResult) Extract() (*RoleInferenceRule, error) { + var s = &RoleInferenceRule{} + err := r.ExtractInto(s) + return s, err +} + +type ListImpliedRolesResult struct { + gophercloud.Result +} + +type ImpliedRoleObject struct { + // ID contains the ID of the role in an implied_role object. + ID string `json:"id,omitempty"` + // Name contains the name of role in an implied_role. + Name string `json:"name,omitempty"` + // Name contains the name of role in an implied_role. + Description string `json:"description,omitempty"` + // Links contains referencing links to the implied_role. + Links map[string]interface{} `json:"links"` +} + +type PriorRoleObject struct { + // ID contains the ID of the role in an implied_role object. + ID string `json:"id,omitempty"` + // Name contains the name of role in an implied_role. + Name string `json:"name,omitempty"` + // Name contains the name of role in an implied_role. + Description string `json:"description,omitempty"` + // Links contains referencing links to the implied_role. + Links map[string]interface{} `json:"links"` +} +type RoleInferenceRules struct { + // PriorRole is the role object that implies a list of implied_role objects. + PriorRole PriorRoleObject `json:"prior_role"` + // Implies is an array of implied_role objects implied by a prior_role object. + ImpliedRoles []ImpliedRoleObject `json:"implies"` +} + +type RoleInferenceRuleList struct { + RoleInferenceRuleList []RoleInferenceRules `json:"role_inferences"` + Links map[string]interface{} `json:"links"` +} + +func (r ListImpliedRolesResult) Extract() (*RoleInferenceRuleList, error) { + var s = &RoleInferenceRuleList{} + err := r.ExtractInto(s) + return s, err +} + +type DeleteImpliedRoleResult struct { + gophercloud.ErrResult +} diff --git a/openstack/identity/v3/roles/testing/fixtures_test.go b/openstack/identity/v3/roles/testing/fixtures_test.go index f64e7cbec4..da48950961 100644 --- a/openstack/identity/v3/roles/testing/fixtures_test.go +++ b/openstack/identity/v3/roles/testing/fixtures_test.go @@ -210,6 +210,96 @@ const ListAssignmentsOnResourceOutput = ` } ` +const CreateRoleInferenceRuleOutput = ` +{ + "role_inference": { + "prior_role": { + "id": "7ceab6192ea34a548cc71b24f72e762c", + "links": { + "self": "http://example.com/identity/v3/roles/7ceab6192ea34a548cc71b24f72e762c" + }, + "name": "prior role name" + }, + "implies": { + "id": "97e2f5d38bc94842bc3da818c16762ed", + "links": { + "self": "http://example.com/identity/v3/roles/97e2f5d38bc94842bc3da818c16762ed" + }, + "name": "implied role name" + } + }, + "links": { + "self": "http://example.com/identity/v3/roles/7ceab6192ea34a548cc71b24f72e762c/implies/97e2f5d38bc94842bc3da818c16762ed" + } +} +` + +const ListRoleInferenceRulesOutput = ` +{ + "role_inferences": [ + { + "prior_role": { + "id": "1acd3c5aa0e246b9a7427d252160dcd1", + "links": { + "self": "http://example.com/identity/v3/roles/1acd3c5aa0e246b9a7427d252160dcd1" + }, + "description": "My new role", + "name": "prior role name" + }, + "implies": [ + { + "id": "3602510e2e1f499589f78a0724dcf614", + "links": { + "self": "http://example.com/identity/v3/roles/3602510e2e1f499589f78a0724dcf614" + }, + "description": "My new role", + "name": "implied role1 name" + }, + { + "id": "738289aeef684e73a987f7cf2ec6d925", + "links": { + "self": "http://example.com/identity/v3/roles/738289aeef684e73a987f7cf2ec6d925" + }, + "description": "My new role", + "name": "implied role2 name" + } + ] + }, + { + "prior_role": { + "id": "bbf7a5098bb34407b7164eb6ff9f144e", + "links": { + "self" : "http://example.com/identity/v3/roles/bbf7a5098bb34407b7164eb6ff9f144e" + }, + "description": "My new role", + "name": "prior role name" + }, + "implies": [ + { + "id": "872b20ad124c4c1bafaef2b1aae316ab", + "links": { + "self": "http://example.com/identity/v3/roles/872b20ad124c4c1bafaef2b1aae316ab" + }, + "description": null, + "name": "implied role1 name" + }, + { + "id": "1d865b1b2da14cb7b05254677e5f36a2", + "links": { + "self": "http://example.com/identity/v3/roles/1d865b1b2da14cb7b05254677e5f36a2" + }, + "description": null, + "name": "implied role2 name" + } + ] + } + ], + "links": { + "self": "http://example.com/identity/v3/role_inferences" + } +} +` + // FirstRole is the first role in the List request. var FirstRole = roles.Role{ DomainID: "default", @@ -514,3 +604,141 @@ func HandleListAssignmentsOnResourceSuccessfully_DomainsGroups(t *testing.T) { th.Mux.HandleFunc("/domains/{domain_id}/groups/{group_id}/roles", fn) } + +var expectedRoleInferenceRule = roles.RoleInferenceRule{ + RoleInference: roles.RoleInference{ + PriorRole: roles.PriorRole{ + ID: "7ceab6192ea34a548cc71b24f72e762c", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/7ceab6192ea34a548cc71b24f72e762c", + }, + Name: "prior role name", + }, + ImpliedRole: roles.ImpliedRole{ + ID: "97e2f5d38bc94842bc3da818c16762ed", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/97e2f5d38bc94842bc3da818c16762ed", + }, + Name: "implied role name", + }, + }, + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/7ceab6192ea34a548cc71b24f72e762c/implies/97e2f5d38bc94842bc3da818c16762ed", + }, +} + +func HandleCreateRoleInferenceRule(t *testing.T) { + fn := func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateRoleInferenceRuleOutput) + } + + th.Mux.HandleFunc("/roles/7ceab6192ea34a548cc71b24f72e762c/implies/97e2f5d38bc94842bc3da818c16762ed", fn) +} + +var expectedRoleInferenceRuleList = roles.RoleInferenceRuleList{ + RoleInferenceRuleList: []roles.RoleInferenceRules{ + { + PriorRole: roles.PriorRoleObject{ + ID: "1acd3c5aa0e246b9a7427d252160dcd1", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/1acd3c5aa0e246b9a7427d252160dcd1", + }, + Name: "prior role name", + Description: "My new role", + }, + ImpliedRoles: []roles.ImpliedRoleObject{ + { + ID: "3602510e2e1f499589f78a0724dcf614", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/3602510e2e1f499589f78a0724dcf614", + }, + Name: "implied role1 name", + Description: "My new role", + }, + { + ID: "738289aeef684e73a987f7cf2ec6d925", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/738289aeef684e73a987f7cf2ec6d925", + }, + Name: "implied role2 name", + Description: "My new role", + }, + }, + }, + { + PriorRole: roles.PriorRoleObject{ + ID: "bbf7a5098bb34407b7164eb6ff9f144e", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/bbf7a5098bb34407b7164eb6ff9f144e", + }, + Name: "prior role name", + Description: "My new role", + }, + ImpliedRoles: []roles.ImpliedRoleObject{ + { + ID: "872b20ad124c4c1bafaef2b1aae316ab", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/872b20ad124c4c1bafaef2b1aae316ab", + }, + Name: "implied role1 name", + Description: "", + }, + { + ID: "1d865b1b2da14cb7b05254677e5f36a2", + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/roles/1d865b1b2da14cb7b05254677e5f36a2", + }, + Name: "implied role2 name", + Description: "", + }, + }, + }, + }, + Links: map[string]interface{}{ + "self": "http://example.com/identity/v3/role_inferences", + }, +} + +func HandleListRoleInferenceRules(t *testing.T) { + fn := 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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListRoleInferenceRulesOutput) + } + + th.Mux.HandleFunc("/role_inferences", fn) +} + +func HandleDeleteRoleInferenceRule(t *testing.T) { + fn := 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("/roles/7ceab6192ea34a548cc71b24f72e762c/implies/97e2f5d38bc94842bc3da818c16762ed", fn) +} + +func HandleGetRoleInferenceRule(t *testing.T) { + fn := 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) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, CreateRoleInferenceRuleOutput) + } + + th.Mux.HandleFunc("/roles/7ceab6192ea34a548cc71b24f72e762c/implies/97e2f5d38bc94842bc3da818c16762ed", fn) +} diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index ce41285bb7..0eb6f9c3c7 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -343,3 +343,42 @@ func TestUnassign(t *testing.T) { }).ExtractErr() th.AssertNoErr(t, err) } + +func TestCreateRoleInferenceRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleCreateRoleInferenceRule(t) + + actual, err := roles.CreateRoleInferenceRule(context.TODO(), client.ServiceClient(), "7ceab6192ea34a548cc71b24f72e762c", "97e2f5d38bc94842bc3da818c16762ed").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedRoleInferenceRule, *actual) +} + +func TestListRoleInferenceRules(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListRoleInferenceRules(t) + + actual, err := roles.ListRoleInferenceRules(context.TODO(), client.ServiceClient()).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedRoleInferenceRuleList, *actual) +} + +func TestDeleteRoleInferenceRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteRoleInferenceRule(t) + + err := roles.DeleteRoleInferenceRule(context.TODO(), client.ServiceClient(), "7ceab6192ea34a548cc71b24f72e762c", "97e2f5d38bc94842bc3da818c16762ed").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestGetInferenceRule(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetRoleInferenceRule(t) + + actual, err := roles.GetRoleInferenceRule(context.TODO(), client.ServiceClient(), "7ceab6192ea34a548cc71b24f72e762c", "97e2f5d38bc94842bc3da818c16762ed").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expectedRoleInferenceRule, *actual) +} diff --git a/openstack/identity/v3/roles/urls.go b/openstack/identity/v3/roles/urls.go index 8bc46ac312..8b842e2159 100644 --- a/openstack/identity/v3/roles/urls.go +++ b/openstack/identity/v3/roles/urls.go @@ -37,3 +37,19 @@ func listAssignmentsOnResourceURL(client *gophercloud.ServiceClient, targetType, func assignURL(client *gophercloud.ServiceClient, targetType, targetID, actorType, actorID, roleID string) string { return client.ServiceURL(targetType, targetID, actorType, actorID, rolePath, roleID) } + +func createRoleInferenceRuleURL(client *gophercloud.ServiceClient, priorRoleID, impliedRoleID string) string { + return client.ServiceURL(rolePath, priorRoleID, "implies", impliedRoleID) +} + +func getRoleInferenceRuleURL(client *gophercloud.ServiceClient, priorRoleID, impliedRoleID string) string { + return client.ServiceURL(rolePath, priorRoleID, "implies", impliedRoleID) +} + +func listRoleInferenceRulesURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("role_inferences") +} + +func deleteRoleInferenceRuleURL(client *gophercloud.ServiceClient, priorRoleID, impliedRoleID string) string { + return client.ServiceURL(rolePath, priorRoleID, "implies", impliedRoleID) +} From b18f2717f53062851c6c717e5c9cf437924be052 Mon Sep 17 00:00:00 2001 From: georgeb Date: Wed, 1 May 2024 22:54:45 +0200 Subject: [PATCH 1845/2296] Support list-modify-delete project tags api calls --- .../openstack/identity/v3/projects_test.go | 29 ++++++ openstack/identity/v3/projects/doc.go | 25 +++++ openstack/identity/v3/projects/requests.go | 55 +++++++++++ openstack/identity/v3/projects/results.go | 46 ++++++++++ .../v3/projects/testing/fixtures_test.go | 92 +++++++++++++++++++ .../v3/projects/testing/requests_test.go | 33 +++++++ openstack/identity/v3/projects/urls.go | 12 +++ 7 files changed, 292 insertions(+) diff --git a/internal/acceptance/openstack/identity/v3/projects_test.go b/internal/acceptance/openstack/identity/v3/projects_test.go index f19861aab5..528a30b2a7 100644 --- a/internal/acceptance/openstack/identity/v3/projects_test.go +++ b/internal/acceptance/openstack/identity/v3/projects_test.go @@ -364,3 +364,32 @@ func TestProjectsTags(t *testing.T) { tools.PrintResource(t, updatedProject) th.AssertEquals(t, len(updatedProject.Tags), 0) } + +func TestProjectsTagsCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + createOpts := projects.CreateOpts{ + Tags: []string{"Tag1", "Tag2"}, + } + + projectMain, err := CreateProject(t, client, &createOpts) + th.AssertNoErr(t, err) + defer DeleteProject(t, client, projectMain.ID) + + projectTagsList, err := projects.ListTags(context.TODO(), client, projectMain.ID).Extract() + tools.PrintResource(t, projectTagsList) + th.AssertNoErr(t, err) + + modifyOpts := projects.ModifyTagsOpts{ + Tags: []string{"foo", "bar"}, + } + projectTags, err := projects.ModifyTags(context.TODO(), client, projectMain.ID, modifyOpts).Extract() + tools.PrintResource(t, projectTags) + th.AssertNoErr(t, err) + + err = projects.DeleteTags(context.TODO(), client, projectMain.ID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/identity/v3/projects/doc.go b/openstack/identity/v3/projects/doc.go index bee8760c34..6aea466a51 100644 --- a/openstack/identity/v3/projects/doc.go +++ b/openstack/identity/v3/projects/doc.go @@ -64,5 +64,30 @@ Example to Delete a Project if err != nil { panic(err) } + +Example to List all tags of a Project + + projectID := "966b3c7d36a24facaf20b7e458bf2192" + err := projects.ListTags(context.TODO(), identityClient, projectID).Extract() + if err != nil { + panic(err) + } + +Example to modify all tags of a Project + + projectID := "966b3c7d36a24facaf20b7e458bf2192" + tags := ["foo", "bar"] + projects, err := projects.ModifyTags(context.TODO(), identityClient, projectID, tags).Extract() + if err != nil { + panic(err) + } + +Example to Delete all tags of a Project + + projectID := "966b3c7d36a24facaf20b7e458bf2192" + err := projects.DeleteTags(context.TODO(), identityClient, projectID).ExtractErr() + if err != nil { + panic(err) + } */ package projects diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 2d9d5eaa39..568d12a719 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -243,3 +243,58 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// CheckTags lists tags for a project. +func ListTags(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r ListTagsResult) { + resp, err := client.Get(ctx, listTagsURL(client, projectID), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Tags represents a list of Tags object. +type ModifyTagsOpts struct { + // Tags is the list of tags associated with the project. + Tags []string `json:"tags,omitempty"` +} + +// ModifyTagsOptsBuilder allows extensions to add additional parameters to +// the Modify request. +type ModifyTagsOptsBuilder interface { + ToModifyTagsCreateMap() (map[string]interface{}, error) +} + +// ToModifyTagsCreateMap formats a ModifyTagsOpts into a Modify tags request. +func (opts ModifyTagsOpts) ToModifyTagsCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + + if err != nil { + return nil, err + } + return b, nil +} + +// ModifyTags deletes all tags of a project and adds new ones. +func ModifyTags(ctx context.Context, client *gophercloud.ServiceClient, projectID string, opts ModifyTagsOpts) (r ModifyTagsResult) { + + b, err := opts.ToModifyTagsCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, modifyTagsURL(client, projectID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteTag deletes a tag from a project. +func DeleteTags(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r DeleteTagsResult) { + resp, err := client.Delete(ctx, deleteTagsURL(client, projectID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/identity/v3/projects/results.go b/openstack/identity/v3/projects/results.go index 23f482f14a..f571334313 100644 --- a/openstack/identity/v3/projects/results.go +++ b/openstack/identity/v3/projects/results.go @@ -154,3 +154,49 @@ func (r projectResult) Extract() (*Project, error) { err := r.ExtractInto(&s) return s.Project, err } + +// Tags represents a list of Tags object. +type Tags struct { + // Tags is the list of tags associated with the project. + Tags []string `json:"tags,omitempty"` +} + +// ListTagsResult is the result of a List Tags request. Call its Extract method to +// interpret it as a list of tags. +type ListTagsResult struct { + gophercloud.Result +} + +// Extract interprets any ListTagsResult as a Tags Object. +func (r ListTagsResult) Extract() (*Tags, error) { + var s = &Tags{} + err := r.ExtractInto(&s) + return s, err +} + +// ProjectTags represents a list of Tags object. +type ProjectTags struct { + // Tags is the list of tags associated with the project. + Projects []Project `json:"projects,omitempty"` + // Links contains referencing links to the implied_role. + Links map[string]interface{} `json:"links"` +} + +// ModifyTagsResLinksult is the result of a Tags request. Call its Extract method to +// interpret it as a project of tags. +type ModifyTagsResult struct { + gophercloud.Result +} + +// Extract interprets any ModifyTags as a Tags Object. +func (r ModifyTagsResult) Extract() (*ProjectTags, error) { + var s = &ProjectTags{} + err := r.ExtractInto(&s) + return s, err +} + +// DeleteTagsResult is the result of a Delete Tags request. Call its ExtractErr method to +// determine if the request succeeded or failed. +type DeleteTagsResult struct { + gophercloud.ErrResult +} diff --git a/openstack/identity/v3/projects/testing/fixtures_test.go b/openstack/identity/v3/projects/testing/fixtures_test.go index f78d2660ef..9d07b66f24 100644 --- a/openstack/identity/v3/projects/testing/fixtures_test.go +++ b/openstack/identity/v3/projects/testing/fixtures_test.go @@ -138,6 +138,44 @@ const UpdateOutput = ` } ` +// ListTagsOutput provides the output to a ListTags request. +const ListTagsOutput = ` +{ + "tags": ["foo", "bar"] +} +` + +// ModifyProjectTagsRequest provides the input to a ModifyTags request. +const ModifyProjectTagsRequest = ` +{ + "tags": ["foo", "bar"] +} +` + +// ModifyProjectTagsOutput provides the output to a ModifyTags request. +const ModifyProjectTagsOutput = ` +{ + "links": { + "next": null, + "previous": null, + "self": "http://identity:5000/v3/projects" + }, + "projects": [ + { + "description": "Test Project", + "domain_id": "default", + "enabled": true, + "id": "3d4c2c82bd5948f0bcab0cf3a7c9b48c", + "links": { + "self": "http://identity:5000/v3/projects/3d4c2c82bd5948f0bcab0cf3a7c9b48c" + }, + "name": "demo", + "tags": ["foo", "bar"] + } + ] +} +` + // FirstProject is a Project fixture. var FirstProject = projects.Project{ Description: "my first project", @@ -212,6 +250,31 @@ var ExpectedAvailableProjectsSlice = []projects.Project{FirstProject, SecondProj // ExpectedProjectSlice is the slice of projects expected to be returned from ListOutput. var ExpectedProjectSlice = []projects.Project{RedTeam, BlueTeam} +var ExpectedTags = projects.Tags{ + Tags: []string{"foo", "bar"}, +} + +var ExpectedProjects = projects.ProjectTags{ + Projects: []projects.Project{ + { + Description: "Test Project", + DomainID: "default", + Enabled: true, + ID: "3d4c2c82bd5948f0bcab0cf3a7c9b48c", + Extra: map[string]interface{}{"links": map[string]interface{}{ + "self": "http://identity:5000/v3/projects/3d4c2c82bd5948f0bcab0cf3a7c9b48c", + }}, + Name: "demo", + Tags: []string{"foo", "bar"}, + }, + }, + Links: map[string]interface{}{ + "next": nil, + "previous": nil, + "self": "http://identity:5000/v3/projects", + }, +} + // HandleListAvailableProjectsSuccessfully creates an HTTP handler at `/auth/projects` // on the test handler mux that responds with a list of two tenants. func HandleListAvailableProjectsSuccessfully(t *testing.T) { @@ -290,3 +353,32 @@ func HandleUpdateProjectSuccessfully(t *testing.T) { fmt.Fprintf(w, UpdateOutput) }) } + +func HandleListProjectTagsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/projects/966b3c7d36a24facaf20b7e458bf2192/tags", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListTagsOutput) + }) +} + +func HandleModifyProjectTagsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/projects/966b3c7d36a24facaf20b7e458bf2192/tags", 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, ModifyProjectTagsRequest) + + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ModifyProjectTagsOutput) + }) +} +func HandleDeleteProjectTagsSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/projects/966b3c7d36a24facaf20b7e458bf2192/tags", 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/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 3ca4898123..6ff4efc7bc 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -134,4 +134,37 @@ func TestUpdateProject(t *testing.T) { actual, err := projects.Update(context.TODO(), client.ServiceClient(), "1234", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, UpdatedRedTeam, *actual) + t.Log(projects.Update(context.TODO(), client.ServiceClient(), "1234", updateOpts)) +} + +func TestListProjectTags(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListProjectTagsSuccessfully(t) + + actual, err := projects.ListTags(context.TODO(), client.ServiceClient(), "966b3c7d36a24facaf20b7e458bf2192").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedTags, *actual) +} + +func TestModifyProjectTags(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleModifyProjectTagsSuccessfully(t) + + modifyOpts := projects.ModifyTagsOpts{ + Tags: []string{"foo", "bar"}, + } + actual, err := projects.ModifyTags(context.TODO(), client.ServiceClient(), "966b3c7d36a24facaf20b7e458bf2192", modifyOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedProjects, *actual) +} + +func TestDeleteTags(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleDeleteProjectTagsSuccessfully(t) + + err := projects.DeleteTags(context.TODO(), client.ServiceClient(), "966b3c7d36a24facaf20b7e458bf2192").ExtractErr() + th.AssertNoErr(t, err) } diff --git a/openstack/identity/v3/projects/urls.go b/openstack/identity/v3/projects/urls.go index fc22c02dc5..27cd6c2019 100644 --- a/openstack/identity/v3/projects/urls.go +++ b/openstack/identity/v3/projects/urls.go @@ -25,3 +25,15 @@ func deleteURL(client *gophercloud.ServiceClient, projectID string) string { func updateURL(client *gophercloud.ServiceClient, projectID string) string { return client.ServiceURL("projects", projectID) } + +func listTagsURL(client *gophercloud.ServiceClient, projectID string) string { + return client.ServiceURL("projects", projectID, "tags") +} + +func modifyTagsURL(client *gophercloud.ServiceClient, projectID string) string { + return client.ServiceURL("projects", projectID, "tags") +} + +func deleteTagsURL(client *gophercloud.ServiceClient, projectID string) string { + return client.ServiceURL("projects", projectID, "tags") +} From 181727ed45b0bcf6bb3c242d9448d642798103cf Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 6 May 2024 12:08:02 +0200 Subject: [PATCH 1846/2296] clouds: Fix secure.yaml parsing Before this patch, no `secure.yaml` could ever be found by the Parse function, because a programming error caused Gophercloud to always look at a path that doesn't exist. --- openstack/config/clouds/clouds.go | 2 +- openstack/config/clouds/clouds_test.go | 62 ++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index 84e533a960..df01c42fe6 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -100,7 +100,7 @@ func Parse(opts ...ParseOption) (gophercloud.AuthOptions, gophercloud.EndpointOp options.cloudsyamlReader = f if options.secureyamlReader == nil { - securePath := path.Join(path.Base(cloudsPath), "secure.yaml") + securePath := path.Join(path.Dir(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) diff --git a/openstack/config/clouds/clouds_test.go b/openstack/config/clouds/clouds_test.go index 2e13924cee..8b70c9136d 100644 --- a/openstack/config/clouds/clouds_test.go +++ b/openstack/config/clouds/clouds_test.go @@ -2,7 +2,9 @@ package clouds_test import ( "fmt" + "os" "strings" + "testing" "github.com/gophercloud/gophercloud/v2/openstack/config/clouds" ) @@ -62,3 +64,63 @@ func ExampleWithRegion() { fmt.Println(eo.Region) // Output: mars } + +func TestParse(t *testing.T) { + t.Run("parses the local clouds.yaml and secure.yaml if present", func(t *testing.T) { + const cloudsYAML = `clouds: + gophercloud-test: + auth: + auth_url: https://example.com/gophercloud-test-12345:13000` + const secureYAML = `clouds: + gophercloud-test: + auth: + password: secret + username: gophercloud-test-username` + + tmpDir, err := os.MkdirTemp(os.TempDir(), "gophercloud-test") + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer func(tmpDir string) { + if err := os.RemoveAll(tmpDir); err != nil { + panic("unable to remove the temporary files: " + err.Error()) + } + }(tmpDir) + + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("unable to determine the current working directory: %v", err) + } + if err := os.Chdir(tmpDir); err != nil { + t.Fatalf("unable to move to a temporary directory: %v", err) + } + defer func() { + if err := os.Chdir(cwd); err != nil { + panic("unable to reset the current working directory: " + err.Error()) + } + }() + + if err := os.WriteFile("clouds.yaml", []byte(cloudsYAML), 0644); err != nil { + t.Fatalf("unable to create a mock clouds.yaml file: %v", err) + } + + if err := os.WriteFile("secure.yaml", []byte(secureYAML), 0644); err != nil { + t.Fatalf("unable to create a mock secure.yaml file: %v", err) + } + + ao, _, _, err := clouds.Parse( + clouds.WithCloudName("gophercloud-test"), + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if got := ao.IdentityEndpoint; got != "https://example.com/gophercloud-test-12345:13000" { + t.Errorf("unexpected identity endpoint: %q", got) + } + + if got := ao.Username; got != "gophercloud-test-username" { + t.Errorf("unexpected username: %q", got) + } + }) +} From 5eb171eb638f87a518774efd52bd7bdd78a67d29 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 6 May 2024 11:50:50 +0200 Subject: [PATCH 1847/2296] clouds: Fix the clouds.yaml locations Before this patch, all default locations were scanned for a `clouds.yaml` even when a match was found. The last found `clouds.yaml` replaced any previous finding, effectively reversing the order of priority for the locations. --- openstack/config/clouds/clouds.go | 28 +++---- openstack/config/clouds/clouds_test.go | 111 +++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 23 deletions(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index df01c42fe6..b4ad3a9252 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -22,7 +22,6 @@ package clouds import ( "crypto/tls" "encoding/json" - "errors" "fmt" "os" "path" @@ -90,27 +89,22 @@ func Parse(opts ...ParseOption) (gophercloud.AuthOptions, gophercloud.EndpointOp } 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 { + continue } - if err == nil { - defer f.Close() - options.cloudsyamlReader = f + defer f.Close() + options.cloudsyamlReader = f - if options.secureyamlReader == nil { - securePath := path.Join(path.Dir(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.secureyamlReader == nil { + securePath := path.Join(path.Dir(cloudsPath), "secure.yaml") + secureF, err := os.Open(securePath) + if err == nil { + defer secureF.Close() + options.secureyamlReader = secureF } } + break } if options.cloudsyamlReader == nil { return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("clouds file not found. Search locations were: %v", options.locations) diff --git a/openstack/config/clouds/clouds_test.go b/openstack/config/clouds/clouds_test.go index 8b70c9136d..384aea92d8 100644 --- a/openstack/config/clouds/clouds_test.go +++ b/openstack/config/clouds/clouds_test.go @@ -3,6 +3,7 @@ package clouds_test import ( "fmt" "os" + "path" "strings" "testing" @@ -66,6 +67,14 @@ func ExampleWithRegion() { } func TestParse(t *testing.T) { + const tempDirPrefix = "gophercloud-test-" + + rmTmpDirOrPanic := func(tmpDir string) { + if err := os.RemoveAll(tmpDir); err != nil { + panic("unable to remove the temporary files: " + err.Error()) + } + } + t.Run("parses the local clouds.yaml and secure.yaml if present", func(t *testing.T) { const cloudsYAML = `clouds: gophercloud-test: @@ -77,15 +86,11 @@ func TestParse(t *testing.T) { password: secret username: gophercloud-test-username` - tmpDir, err := os.MkdirTemp(os.TempDir(), "gophercloud-test") + tmpDir, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) if err != nil { t.Fatalf("unable to create a temporary directory: %v", err) } - defer func(tmpDir string) { - if err := os.RemoveAll(tmpDir); err != nil { - panic("unable to remove the temporary files: " + err.Error()) - } - }(tmpDir) + defer rmTmpDirOrPanic(tmpDir) cwd, err := os.Getwd() if err != nil { @@ -123,4 +128,98 @@ func TestParse(t *testing.T) { t.Errorf("unexpected username: %q", got) } }) + + t.Run("parses the locations in order", func(t *testing.T) { + const cloudsYAML1 = `clouds: + gophercloud-test: + auth: + auth_url: https://example.com/gophercloud-test-1:13000` + const cloudsYAML2 = `clouds: + gophercloud-test: + auth: + auth_url: https://example.com/gophercloud-test-2:13000` + + tmpDir1, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(tmpDir1) + + tmpDir2, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(tmpDir2) + + cloudsPath1, cloudsPath2 := path.Join(tmpDir1, "clouds.yaml"), path.Join(tmpDir2, "clouds.yaml") + + if err := os.WriteFile(cloudsPath1, []byte(cloudsYAML1), 0644); err != nil { + t.Fatalf("unable to create a mock clouds.yaml file in path %q: %v", cloudsPath1, err) + } + if err := os.WriteFile(cloudsPath2, []byte(cloudsYAML2), 0644); err != nil { + t.Fatalf("unable to create a mock clouds.yaml file in path %q: %v", cloudsPath2, err) + } + + ao, _, _, err := clouds.Parse( + clouds.WithCloudName("gophercloud-test"), + clouds.WithLocations(cloudsPath1, cloudsPath2), + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if got := ao.IdentityEndpoint; got != "https://example.com/gophercloud-test-1:13000" { + t.Errorf("unexpected identity endpoint: %q", got) + } + }) + + t.Run("falls back to the next location if clouds.yaml is not found", func(t *testing.T) { + const cloudsYAML1 = `clouds: + gophercloud-test: + auth: + auth_url: https://example.com/gophercloud-test-1:13000` + const cloudsYAML2 = `clouds: + gophercloud-test: + auth: + auth_url: https://example.com/gophercloud-test-2:13000` + + tmpDir0, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(tmpDir0) + + tmpDir1, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(tmpDir1) + + tmpDir2, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(tmpDir2) + + cloudsPath0, cloudsPath1, cloudsPath2 := path.Join(tmpDir0, "clouds.yaml"), path.Join(tmpDir1, "clouds.yaml"), path.Join(tmpDir2, "clouds.yaml") + + if err := os.WriteFile(cloudsPath1, []byte(cloudsYAML1), 0644); err != nil { + t.Fatalf("unable to create a mock clouds.yaml file in path %q: %v", cloudsPath1, err) + } + if err := os.WriteFile(cloudsPath2, []byte(cloudsYAML2), 0644); err != nil { + t.Fatalf("unable to create a mock clouds.yaml file in path %q: %v", cloudsPath2, err) + } + + ao, _, _, err := clouds.Parse( + clouds.WithCloudName("gophercloud-test"), + clouds.WithLocations(cloudsPath0, cloudsPath1, cloudsPath2), + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if got := ao.IdentityEndpoint; got != "https://example.com/gophercloud-test-1:13000" { + t.Errorf("unexpected identity endpoint: %q", got) + } + }) } From 7ec9c56af6b4bf4e9e218c6dc72f607a568ebfd8 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Mon, 6 May 2024 10:34:30 -0400 Subject: [PATCH 1848/2296] CI: remove Zed from testing coverage This isn't maintained anymore, this could break anytime. --- .github/workflows/functional-baremetal.yaml | 4 ---- .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 | 6 ------ .github/workflows/functional-dns.yaml | 3 --- .github/workflows/functional-fwaas_v2.yaml | 3 --- .github/workflows/functional-identity.yaml | 3 --- .github/workflows/functional-image.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, 58 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index f5813b9e33..fc1bbf2536 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.1" ubuntu_version: "22.04" os_system_scope: "" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.04" - os_system_scope: "" 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 9cf4c92d45..722b1a5510 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -26,9 +26,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 876c950cc2..91a60b4d4d 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 45f1b9fe95..daca0d8d33 100644 --- a/.github/workflows/functional-clustering.yaml +++ b/.github/workflows/functional-clustering.yaml @@ -24,9 +24,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 7e96a079f5..92d2371fb0 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 0b5e11d1d9..73c6ef6ae6 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -35,12 +35,6 @@ jobs: 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 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 ca9330e33c..6fe7d99547 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -24,9 +24,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.04" 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-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index c044fb60e3..ae31ef344d 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with enabled FWaaS_v2 and run networking acceptance tests steps: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 2e0d9d8646..5a365ac9d4 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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-image.yaml b/.github/workflows/functional-image.yaml index 833aea61c2..e9f557c1d4 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Glance and run image acceptance tests steps: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index a681272c2d..b1d02ef6f7 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 b8ddbf7f53..1c669aed77 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 8ec2691470..c4ba4b6e82 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 ad5811f0ea..7db740ea9c 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.04" 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 cfecd0c2ea..a4f347eaa4 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 ae11f3f64f..816bf55ad6 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 41457060ea..486769f130 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.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 65972c6f71..24dc989fcc 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -23,9 +23,6 @@ jobs: - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - - name: "zed" - openstack_version: "stable/zed" - ubuntu_version: "20.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests steps: From 3450b524e64f49a34b97fa15e8797907b8de8324 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 09:40:49 +0000 Subject: [PATCH 1849/2296] build(deps): bump golang.org/x/crypto from 0.22.0 to 0.23.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.22.0 to 0.23.0. - [Commits](https://github.com/golang/crypto/compare/v0.22.0...v0.23.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 2cd703c0d4..629086e8d3 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.21 require ( - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.23.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.19.0 // indirect +require golang.org/x/sys v0.20.0 // indirect diff --git a/go.sum b/go.sum index d614b257eb..9ff4e2353f 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 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 84534a19702b07a0d5021ca8cdfcbf50f7e771e5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 14:33:38 +0000 Subject: [PATCH 1850/2296] acceptance: Skip noauth tests if config not provided This is a non-default (and somewhat esoteric) configuration. Skip the tests unless explicitly provided. Signed-off-by: Stephen Finucane --- internal/acceptance/clients/clients.go | 2 +- internal/acceptance/clients/conditions.go | 20 +++++++++++++++++-- .../baremetal/noauth/allocations_test.go | 1 + .../openstack/baremetal/noauth/nodes_test.go | 3 +++ .../openstack/baremetal/noauth/ports_test.go | 2 ++ .../blockstorage/noauth/snapshots_test.go | 4 ++++ .../blockstorage/noauth/volumes_test.go | 4 ++++ 7 files changed, 33 insertions(+), 3 deletions(-) diff --git a/internal/acceptance/clients/clients.go b/internal/acceptance/clients/clients.go index 3de05132fd..9fdde23c10 100644 --- a/internal/acceptance/clients/clients.go +++ b/internal/acceptance/clients/clients.go @@ -218,7 +218,7 @@ func NewBlockStorageV2NoAuthClient() (*gophercloud.ServiceClient, error) { } // NewBlockStorageV3NoAuthClient returns a noauth *ServiceClient for -// making calls to the OpenStack Block Storage v2 API. An error will be +// making calls to the OpenStack Block Storage v3 API. An error will be // returned if client creation was not possible. func NewBlockStorageV3NoAuthClient() (*gophercloud.ServiceClient, error) { client, err := blockstorageNoAuth.NewClient(gophercloud.AuthOptions{ diff --git a/internal/acceptance/clients/conditions.go b/internal/acceptance/clients/conditions.go index a1239a1ba7..619d31a94e 100644 --- a/internal/acceptance/clients/conditions.go +++ b/internal/acceptance/clients/conditions.go @@ -83,8 +83,24 @@ func RequireNovaNetwork(t *testing.T) { } } -// RequireIronicHTTPBasic will restric a test to be only run in -// environments that have Ironic using http_basic. +// RequireCinderNoAuth will restrict a test to be only run in environments that +// have Cinder using noauth. +func RequireCinderNoAuth(t *testing.T) { + if os.Getenv("CINDER_ENDPOINT") == "" || os.Getenv("OS_USERNAME") == "" { + t.Skip("this test requires Cinder using noauth, set OS_USERNAME and CINDER_ENDPOINT") + } +} + +// RequireIronicNoAuth will restrict a test to be only run in environments that +// have Ironic using noauth. +func RequireIronicNoAuth(t *testing.T) { + if os.Getenv("IRONIC_ENDPOINT") == "" || os.Getenv("OS_USERNAME") == "" { + t.Skip("this test requires IRONIC using noauth, set OS_USERNAME and IRONIC_ENDPOINT") + } +} + +// RequireIronicHTTPBasic will restrict a test to be only run in environments +// that have Ironic using http_basic. func RequireIronicHTTPBasic(t *testing.T) { if os.Getenv("IRONIC_ENDPOINT") == "" || os.Getenv("OS_USERNAME") == "" || os.Getenv("OS_PASSWORD") == "" { t.Skip("this test requires Ironic using http_basic, set OS_USERNAME, OS_PASSWORD and IRONIC_ENDPOINT") diff --git a/internal/acceptance/openstack/baremetal/noauth/allocations_test.go b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go index bad5c1400a..e6cb33ac3d 100644 --- a/internal/acceptance/openstack/baremetal/noauth/allocations_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/allocations_test.go @@ -15,6 +15,7 @@ import ( func TestAllocationsCreateDestroy(t *testing.T) { clients.RequireLong(t) + clients.RequireIronicNoAuth(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/baremetal/noauth/nodes_test.go b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go index b7e530d9d6..bd05203b2e 100644 --- a/internal/acceptance/openstack/baremetal/noauth/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/nodes_test.go @@ -16,6 +16,7 @@ import ( func TestNodesCreateDestroy(t *testing.T) { clients.RequireLong(t) + clients.RequireIronicNoAuth(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) @@ -48,6 +49,7 @@ func TestNodesCreateDestroy(t *testing.T) { func TestNodesUpdate(t *testing.T) { clients.RequireLong(t) + clients.RequireIronicNoAuth(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) @@ -72,6 +74,7 @@ func TestNodesUpdate(t *testing.T) { func TestNodesRAIDConfig(t *testing.T) { clients.SkipReleasesBelow(t, "stable/ussuri") clients.RequireLong(t) + clients.RequireIronicNoAuth(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/baremetal/noauth/ports_test.go b/internal/acceptance/openstack/baremetal/noauth/ports_test.go index 9af3d38de7..f27b7fe78b 100644 --- a/internal/acceptance/openstack/baremetal/noauth/ports_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/ports_test.go @@ -16,6 +16,7 @@ import ( func TestPortsCreateDestroy(t *testing.T) { clients.RequireLong(t) + clients.RequireIronicNoAuth(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) @@ -50,6 +51,7 @@ func TestPortsCreateDestroy(t *testing.T) { func TestPortsUpdate(t *testing.T) { clients.RequireLong(t) + clients.RequireIronicNoAuth(t) client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go index 58ea0880a0..5b8bb2a982 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/snapshots_test.go @@ -12,6 +12,8 @@ import ( ) func TestSnapshotsList(t *testing.T) { + clients.RequireCinderNoAuth(t) + client, err := clients.NewBlockStorageV3NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) @@ -33,6 +35,8 @@ func TestSnapshotsList(t *testing.T) { } func TestSnapshotsCreateDelete(t *testing.T) { + clients.RequireCinderNoAuth(t) + client, err := clients.NewBlockStorageV3NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) diff --git a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go index 90fa1bf7fe..0c993addad 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/noauth/volumes_test.go @@ -12,6 +12,8 @@ import ( ) func TestVolumesList(t *testing.T) { + clients.RequireCinderNoAuth(t) + client, err := clients.NewBlockStorageV3NoAuthClient() if err != nil { t.Fatalf("Unable to create a blockstorage client: %v", err) @@ -33,6 +35,8 @@ func TestVolumesList(t *testing.T) { } func TestVolumesCreateDestroy(t *testing.T) { + clients.RequireCinderNoAuth(t) + client, err := clients.NewBlockStorageV3NoAuthClient() if err != nil { t.Fatalf("Unable to create blockstorage client: %v", err) From 79b13242d696ddb636e43af99341ee7c4c53d9be Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 14:50:26 +0000 Subject: [PATCH 1851/2296] acceptance: Add 'RequireNeutronExtension' helper Skip neutron tests if the relevant extension is not available in our environment. We migrate existing variations of this to the new helper and add some for tests that depend on neutron's 'fwaas_v2' extension (though not for fwaas v1, since those tests are all already skipped and should be removed in a separate change). Signed-off-by: Stephen Finucane --- .../openstack/networking/v2/conditions.go | 18 ++++++++++++ .../networking/v2/extensions/dns/dns_test.go | 22 ++++---------- .../v2/extensions/fwaas_v2/groups_test.go | 4 +++ .../v2/extensions/fwaas_v2/policy_test.go | 4 +++ .../v2/extensions/fwaas_v2/rule_test.go | 4 +++ .../extensions/layer3/l3_scheduling_test.go | 17 +++++------ .../networking/v2/extensions/mtu/mtu_test.go | 7 ++--- .../extensions/qos/policies/policies_test.go | 9 ++---- .../v2/extensions/qos/rules/rules_test.go | 23 +++++---------- .../qos/ruletypes/ruletypes_test.go | 9 ++---- .../extensions/trunk_details/trunks_test.go | 7 ++--- .../v2/extensions/trunks/trunks_test.go | 29 +++++-------------- .../vlantransparent/vlantransparent_test.go | 13 +++------ 13 files changed, 72 insertions(+), 94 deletions(-) create mode 100644 internal/acceptance/openstack/networking/v2/conditions.go diff --git a/internal/acceptance/openstack/networking/v2/conditions.go b/internal/acceptance/openstack/networking/v2/conditions.go new file mode 100644 index 0000000000..036cba6044 --- /dev/null +++ b/internal/acceptance/openstack/networking/v2/conditions.go @@ -0,0 +1,18 @@ +package v2 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" +) + +// RequireNeutronExtension will restrict a test to be only run in environments +// with the requested Neutron extension present. +func RequireNeutronExtension(t *testing.T, client *gophercloud.ServiceClient, extension string) { + _, err := extensions.Get(context.TODO(), client, extension).Extract() + if err != nil { + t.Skipf("this test requires %s Neutron extension", extension) + } +} diff --git a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index 555ebe28fa..c9c32d4940 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -11,7 +11,6 @@ import ( networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/layer3" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/dns" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks" @@ -26,11 +25,8 @@ func TestDNSPortCRUDL(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "dns-integration").Extract() - if err != nil { - t.Skip("This test requires dns-integration Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "dns-integration") // Create Network networkDNSDomain := "local." @@ -153,11 +149,8 @@ func TestDNSFloatingIPCRDL(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "dns-integration").Extract() - if err != nil { - t.Skip("This test requires dns-integration Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "dns-integration") choices, err := clients.AcceptanceTestChoicesFromEnv() th.AssertNoErr(t, err) @@ -215,11 +208,8 @@ func TestDNSNetwork(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "dns-integration").Extract() - if err != nil { - t.Skip("This test requires dns-integration Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "dns-integration") // Create Network networkDNSDomain := "local." diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go index dd4479c5ae..b8ee611c95 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/groups_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/groups" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -20,6 +21,9 @@ func TestGroupCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "fwaas_v2") + createdGroup, err := CreateGroup(t, client) th.AssertNoErr(t, err) defer DeleteGroup(t, client, createdGroup.ID) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go index 2b8035cf81..881e9a87aa 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/policy_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/policies" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -20,6 +21,9 @@ func TestPolicyCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "fwaas_v2") + // Create First Rule. This will be used as part of the Policy creation rule, err := CreateRule(t, client) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go index 56fd490171..53de93fa84 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/rule_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/rules" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -22,6 +23,9 @@ func TestRuleCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "fwaas_v2") + rule, err := CreateRule(t, client) th.AssertNoErr(t, err) defer DeleteRule(t, client, rule.ID) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index 70db4e9037..ed110dfff4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -7,9 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + v2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/agents" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -19,18 +18,16 @@ func TestLayer3RouterScheduling(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - _, err = extensions.Get(context.TODO(), client, "l3_agent_scheduler").Extract() - if err != nil { - t.Skip("Extension l3_agent_scheduler not present") - } + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "l3_agent_scheduler") - network, err := networking.CreateNetwork(t, client) + network, err := v2.CreateNetwork(t, client) th.AssertNoErr(t, err) - defer networking.DeleteNetwork(t, client, network.ID) + defer v2.DeleteNetwork(t, client, network.ID) - subnet, err := networking.CreateSubnet(t, client, network.ID) + subnet, err := v2.CreateSubnet(t, client, network.ID) th.AssertNoErr(t, err) - defer networking.DeleteSubnet(t, client, subnet.ID) + defer v2.DeleteSubnet(t, client, subnet.ID) router, err := CreateRouter(t, client, network.ID) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 71ad8d4581..8f0e3b5e38 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -22,11 +22,8 @@ func TestMTUNetworkCRUDL(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "net-mtu").Extract() - if err != nil { - t.Skip("This test requires net-mtu Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "net-mtu") mtuWritable, _ := extensions.Get(context.TODO(), client, "net-mtu-writable").Extract() tools.PrintResource(t, mtuWritable) diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go index fdd50cf21f..fc9563df38 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/policies/policies_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/policies" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -17,11 +17,8 @@ func TestPoliciesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "qos").Extract() - if err != nil { - t.Skip("This test requires qos Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "qos") // Create a QoS policy. policy, err := CreateQoSPolicy(t, client) diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go index c441b58a8b..811e798862 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/rules/rules_test.go @@ -7,9 +7,9 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + v2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" accpolicies "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/policies" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/rules" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -19,11 +19,8 @@ func TestBandwidthLimitRulesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "qos").Extract() - if err != nil { - t.Skip("This test requires qos Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "qos") // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) @@ -68,11 +65,8 @@ func TestDSCPMarkingRulesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "qos").Extract() - if err != nil { - t.Skip("This test requires qos Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "qos") // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) @@ -117,11 +111,8 @@ func TestMinimumBandwidthRulesCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "qos").Extract() - if err != nil { - t.Skip("This test requires qos Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "qos") // Create a QoS policy policy, err := accpolicies.CreateQoSPolicy(t, client) diff --git a/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go index 470408b163..7defe0358e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/qos/ruletypes/ruletypes_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/qos/ruletypes" ) @@ -19,11 +19,8 @@ func TestRuleTypes(t *testing.T) { return } - extension, err := extensions.Get(context.TODO(), client, "qos").Extract() - if err != nil { - t.Skip("This test requires qos Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "qos") page, err := ruletypes.ListRuleTypes(client).AllPages(context.TODO()) if err != nil { diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go index 0daaca55e8..2cab0d035a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -9,7 +9,6 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" v2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" v2Trunks "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/trunks" - "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunk_details" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" @@ -27,10 +26,8 @@ func TestListPortWithSubports(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - _, err = extensions.Get(context.TODO(), client, "trunk-details").Extract() - if err != nil { - t.Skip("This test requires trunk-details Neutron extension") - } + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "trunk-details") // Create Network network, err := v2.CreateNetwork(t, client) diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index 0fbf1b81d8..fac6a9df5d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -10,7 +10,6 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" v2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -22,11 +21,8 @@ func TestTrunkCRUD(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(context.TODO(), client, "trunk").Extract() - if err != nil { - t.Skip("This test requires trunk Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "trunk") // Create Network network, err := v2.CreateNetwork(t, client) @@ -112,11 +108,8 @@ func TestTrunkList(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(context.TODO(), client, "trunk").Extract() - if err != nil { - t.Skip("This test requires trunk Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "trunk") allPages, err := trunks.List(client, nil).AllPages(context.TODO()) if err != nil { @@ -139,11 +132,8 @@ func TestTrunkSubportOperation(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(context.TODO(), client, "trunk").Extract() - if err != nil { - t.Skip("This test requires trunk Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "trunk") // Create Network network, err := v2.CreateNetwork(t, client) @@ -227,11 +217,8 @@ func TestTrunkTags(t *testing.T) { t.Fatalf("Unable to create a network client: %v", err) } - extension, err := extensions.Get(context.TODO(), client, "trunk").Extract() - if err != nil { - t.Skip("This test requires trunk Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + v2.RequireNeutronExtension(t, client, "trunk") // Create Network network, err := v2.CreateNetwork(t, client) diff --git a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go index bef19819b8..b36d01c071 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/vlantransparent/vlantransparent_test.go @@ -3,13 +3,11 @@ package v2 import ( - "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - networkingv2 "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/common/extensions" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -17,16 +15,13 @@ func TestVLANTransparentCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) - extension, err := extensions.Get(context.TODO(), client, "vlan-transparent").Extract() - if err != nil { - t.Skip("This test requires vlan-transparent Neutron extension") - } - tools.PrintResource(t, extension) + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "vlan-transparent") // Create a VLAN transparent network. network, err := CreateVLANTransparentNetwork(t, client) th.AssertNoErr(t, err) - defer networkingv2.DeleteNetwork(t, client, network.ID) + defer networking.DeleteNetwork(t, client, network.ID) tools.PrintResource(t, network) From 0811a76e16a881135bdd8250367afaa637e7649a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 21 Feb 2024 17:17:12 +0000 Subject: [PATCH 1852/2296] blockstorage: Don't munge scheduler hints This is an alternative approach. The main downside is that we now need to pass an additional argument to the volumes.Create function but that seems a more rigorous way to do things. Signed-off-by: Stephen Finucane --- .../blockstorage/noauth/blockstorage.go | 4 +- .../openstack/blockstorage/v2/blockstorage.go | 4 +- .../blockstorage/v2/schedulerhints_test.go | 15 ++-- .../openstack/blockstorage/v2/volumes_test.go | 2 +- .../openstack/blockstorage/v3/blockstorage.go | 4 +- .../blockstorage/v3/schedulerhints_test.go | 15 ++-- .../openstack/blockstorage/v3/volumes_test.go | 4 +- openstack/blockstorage/v2/volumes/doc.go | 10 +-- openstack/blockstorage/v2/volumes/requests.go | 73 ++++++++----------- .../v2/volumes/testing/requests_test.go | 19 +---- openstack/blockstorage/v3/volumes/doc.go | 12 ++- openstack/blockstorage/v3/volumes/requests.go | 73 ++++++++----------- .../v3/volumes/testing/requests_test.go | 22 ++---- 13 files changed, 101 insertions(+), 156 deletions(-) diff --git a/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go index 849d9791fe..cedccbd3cb 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/noauth/blockstorage.go @@ -30,7 +30,7 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol Name: volumeName, } - volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return volume, err } @@ -67,7 +67,7 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo ImageID: choices.ImageID, } - volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return volume, err } diff --git a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go index 40aaefb052..9c22a2a4f8 100644 --- a/internal/acceptance/openstack/blockstorage/v2/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v2/blockstorage.go @@ -66,7 +66,7 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol Description: volumeDescription, } - volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return volume, err } @@ -106,7 +106,7 @@ func CreateVolumeFromImage(t *testing.T, client *gophercloud.ServiceClient) (*vo ImageID: choices.ImageID, } - volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return volume, err } diff --git a/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go index 34e4f7c75f..bf4035895a 100644 --- a/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/schedulerhints_test.go @@ -26,7 +26,7 @@ func TestSchedulerHints(t *testing.T) { Name: volumeName, } - volume1, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume1, err := volumes.Create(context.TODO(), client, createOpts, nil).Extract() th.AssertNoErr(t, err) ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) @@ -37,18 +37,17 @@ func TestSchedulerHints(t *testing.T) { defer volumes.Delete(context.TODO(), client, volume1.ID, volumes.DeleteOpts{}) volumeName = tools.RandomString("ACPTTEST", 16) - schedulerHints := volumes.SchedulerHints{ + createOpts = volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + schedulerHintOpts := volumes.SchedulerHintOpts{ SameHost: []string{ volume1.ID, }, } - createOpts = volumes.CreateOpts{ - Size: 1, - Name: volumeName, - SchedulerHints: schedulerHints, - } - volume2, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume2, err := volumes.Create(context.TODO(), client, createOpts, schedulerHintOpts).Extract() th.AssertNoErr(t, err) ctx2, cancel := context.WithTimeout(context.TODO(), 60*time.Second) diff --git a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go index f3dd985558..d54c6f8c5c 100644 --- a/internal/acceptance/openstack/blockstorage/v2/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v2/volumes_test.go @@ -277,7 +277,7 @@ func TestVolumeConns(t *testing.T) { cv, err := volumes.Create(client, &volumes.CreateOpts{ Size: 1, Name: "blockv2-volume", - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) defer func() { diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 83b7ca0217..56cf612abf 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -76,7 +76,7 @@ func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Vol Description: volumeDescription, } - volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return volume, err } @@ -119,7 +119,7 @@ func CreateVolumeWithType(t *testing.T, client *gophercloud.ServiceClient, vt *v VolumeType: vt.Name, } - volume, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return volume, err } diff --git a/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go b/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go index b9c5a76c3c..5ab3da7c8c 100644 --- a/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/schedulerhints_test.go @@ -25,7 +25,7 @@ func TestSchedulerHints(t *testing.T) { Name: volumeName, } - volume1, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume1, err := volumes.Create(context.TODO(), client, createOpts, nil).Extract() th.AssertNoErr(t, err) ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) @@ -36,18 +36,17 @@ func TestSchedulerHints(t *testing.T) { defer volumes.Delete(context.TODO(), client, volume1.ID, volumes.DeleteOpts{}) volumeName = tools.RandomString("ACPTTEST", 16) - schedulerHints := volumes.SchedulerHints{ + createOpts = volumes.CreateOpts{ + Size: 1, + Name: volumeName, + } + schedulerHintOpts := volumes.SchedulerHintOpts{ SameHost: []string{ volume1.ID, }, } - createOpts = volumes.CreateOpts{ - Size: 1, - Name: volumeName, - SchedulerHints: schedulerHints, - } - volume2, err := volumes.Create(context.TODO(), client, createOpts).Extract() + volume2, err := volumes.Create(context.TODO(), client, createOpts, schedulerHintOpts).Extract() th.AssertNoErr(t, err) ctx2, cancel2 := context.WithTimeout(context.TODO(), 60*time.Second) diff --git a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go index 188ac1ac47..5510a3cd92 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumes_test.go @@ -88,7 +88,7 @@ func TestVolumesMultiAttach(t *testing.T) { VolumeType: vt.ID, } - vol, err := volumes.Create(context.TODO(), client, volOpts).Extract() + vol, err := volumes.Create(context.TODO(), client, volOpts, nil).Extract() th.AssertNoErr(t, err) defer DeleteVolume(t, client, vol) @@ -332,7 +332,7 @@ func TestVolumeConns(t *testing.T) { cv, err := volumes.Create(context.TODO(), client, &volumes.CreateOpts{ Size: 1, Name: "blockv2-volume", - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) defer func() { diff --git a/openstack/blockstorage/v2/volumes/doc.go b/openstack/blockstorage/v2/volumes/doc.go index e05f245e02..8985f7007f 100644 --- a/openstack/blockstorage/v2/volumes/doc.go +++ b/openstack/blockstorage/v2/volumes/doc.go @@ -6,7 +6,7 @@ a time. Example of creating Volume B on a Different Host than Volume A - schedulerHints := volumes.SchedulerHints{ + schedulerHintOpts := volumes.SchedulerHintCreateOpts{ DifferentHost: []string{ "volume-a-uuid", } @@ -15,17 +15,16 @@ Example of creating Volume B on a Different Host than Volume A createOpts := volumes.CreateOpts{ Name: "volume_b", Size: 10, - SchedulerHints: schedulerHints, } - volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() if err != nil { panic(err) } Example of creating Volume B on the Same Host as Volume A - schedulerHints := volumes.SchedulerHints{ + schedulerHintOpts := volumes.SchedulerHintCreateOpts{ SameHost: []string{ "volume-a-uuid", } @@ -34,10 +33,9 @@ Example of creating Volume B on the Same Host as Volume A createOpts := volumes.CreateOpts{ Name: "volume_b", Size: 10 - SchedulerHints: schedulerHints, } - volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() if err != nil { panic(err) } diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index e7c288d02f..8f60c6e281 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -2,20 +2,22 @@ package volumes import ( "context" + "maps" "regexp" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) -// CreateOptsBuilder builds the scheduler hints into a serializable format. -type SchedulerHintsCreateOptsBuilder interface { - ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) +// SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. +type SchedulerHintOptsBuilder interface { + ToSchedulerHintsMap() (map[string]interface{}, error) } -// SchedulerHints represents a set of scheduling hints that are passed to the -// OpenStack scheduler. -type SchedulerHints struct { +// SchedulerHintOpts contains options for providing scheduler hints +// when creating a Volume. This object is passed to the volumes.Create function. +// For more information about these parameters, see the Volume object. +type SchedulerHintOpts struct { // DifferentHost will place the volume on a different back-end that does not // host the given volumes. DifferentHost []string @@ -34,8 +36,8 @@ type SchedulerHints struct { AdditionalProperties map[string]interface{} } -// ToVolumeSchedulerHintsCreateMap assembles a request body for the scheduler -func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) { +// ToSchedulerHintsMap assembles a request body for scheduler hints +func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, error) { sh := make(map[string]interface{}) uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") @@ -44,7 +46,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa for _, diffHost := range opts.DifferentHost { if !uuidRegex.MatchString(diffHost) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.DifferentHost" + err.Argument = "volumes.SchedulerHintOpts.DifferentHost" err.Value = opts.DifferentHost err.Info = "The hosts must be in UUID format." return nil, err @@ -57,7 +59,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa for _, sameHost := range opts.SameHost { if !uuidRegex.MatchString(sameHost) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.SameHost" + err.Argument = "volumes.SchedulerHintOpts.SameHost" err.Value = opts.SameHost err.Info = "The hosts must be in UUID format." return nil, err @@ -69,7 +71,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa if opts.LocalToInstance != "" { if !uuidRegex.MatchString(opts.LocalToInstance) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" + err.Argument = "volumes.SchedulerHintOpts.LocalToInstance" err.Value = opts.LocalToInstance err.Info = "The instance must be in UUID format." return nil, err @@ -87,7 +89,11 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa } } - return sh, nil + if len(sh) == 0 { + return sh, nil + } + + return map[string]interface{}{"OS-SCH-HNT:scheduler_hints": sh}, nil } // CreateOptsBuilder allows extensions to add additional parameters to the @@ -123,52 +129,33 @@ type CreateOpts struct { ImageID string `json:"imageRef,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` - // SchedulerHints provides a set of hints to the scheduler. - SchedulerHints SchedulerHintsCreateOptsBuilder } // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - // We intentionally don't envelope the body here since we want to strip - // some fields out - base, err := gophercloud.BuildRequestBody(opts, "") - if err != nil { - return nil, err - } - - delete(base, "SchedulerHints") - - // Now we do our enveloping - base = map[string]interface{}{"volume": base} - - if opts.SchedulerHints == nil { - return base, nil - } - - schedulerHints, err := opts.SchedulerHints.ToVolumeSchedulerHintsCreateMap() - if err != nil { - return nil, err - } - - if len(schedulerHints) == 0 { - return base, nil - } - - base["OS-SCH-HNT:scheduler_hints"] = schedulerHints - - return base, err + return gophercloud.BuildRequestBody(opts, "volume") } // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder, hintOpts SchedulerHintOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } + + if hintOpts != nil { + sh, err := hintOpts.ToSchedulerHintsMap() + if err != nil { + r.Err = err + return + } + maps.Copy(b, sh) + } + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 02b1170cb2..2be7d0d57b 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -203,16 +203,15 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} - n, err := volumes.Create(context.TODO(), client.ServiceClient(), options).Extract() + n, err := volumes.Create(context.TODO(), client.ServiceClient(), options, nil).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Size, 75) th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } -func TestCreateWithSchedulerHints(t *testing.T) { - - schedulerHints := volumes.SchedulerHints{ +func TestCreateSchedulerHints(t *testing.T) { + base := volumes.SchedulerHintOpts{ DifferentHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", @@ -224,18 +223,8 @@ func TestCreateWithSchedulerHints(t *testing.T) { LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } - base := volumes.CreateOpts{ - Size: 10, - Name: "testvolume", - SchedulerHints: schedulerHints, - } - expected := ` { - "volume": { - "size": 10, - "name": "testvolume" - }, "OS-SCH-HNT:scheduler_hints": { "different_host": [ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", @@ -250,7 +239,7 @@ func TestCreateWithSchedulerHints(t *testing.T) { } } ` - actual, err := base.ToVolumeCreateMap() + actual, err := base.ToSchedulerHintsMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, expected, actual) } diff --git a/openstack/blockstorage/v3/volumes/doc.go b/openstack/blockstorage/v3/volumes/doc.go index df5127b6c6..2ab4af93ee 100644 --- a/openstack/blockstorage/v3/volumes/doc.go +++ b/openstack/blockstorage/v3/volumes/doc.go @@ -6,7 +6,7 @@ a time. Example of creating Volume B on a Different Host than Volume A - schedulerHints := volumes.SchedulerHints{ + schedulerHintOpts := volumes.SchedulerHintCreateOpts{ DifferentHost: []string{ "volume-a-uuid", } @@ -15,17 +15,16 @@ Example of creating Volume B on a Different Host than Volume A createOpts := volumes.CreateOpts{ Name: "volume_b", Size: 10, - SchedulerHints: schedulerHints, } - volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() if err != nil { panic(err) } Example of creating Volume B on the Same Host as Volume A - schedulerHints := volumes.SchedulerHints{ + schedulerHintOpts := volumes.SchedulerHintCreateOpts{ SameHost: []string{ "volume-a-uuid", } @@ -34,10 +33,9 @@ Example of creating Volume B on the Same Host as Volume A createOpts := volumes.CreateOpts{ Name: "volume_b", Size: 10 - SchedulerHints: schedulerHints, } - volume, err := volumes.Create(context.TODO(), computeClient, createOpts).Extract() + volume, err := volumes.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() if err != nil { panic(err) } @@ -51,7 +49,7 @@ Example of creating a Volume from a Backup } client.Microversion = "3.47" - volume, err := volumes.Create(context.TODO(), client, options).Extract() + volume, err := volumes.Create(context.TODO(), client, options, nil).Extract() if err != nil { panic(err) } diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 3e373f2d55..214e252310 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -2,20 +2,22 @@ package volumes import ( "context" + "maps" "regexp" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) -// CreateOptsBuilder builds the scheduler hints into a serializable format. -type SchedulerHintsCreateOptsBuilder interface { - ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) +// SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. +type SchedulerHintOptsBuilder interface { + ToSchedulerHintsMap() (map[string]interface{}, error) } -// SchedulerHints represents a set of scheduling hints that are passed to the -// OpenStack scheduler. -type SchedulerHints struct { +// SchedulerHintOpts contains options for providing scheduler hints +// when creating a Volume. This object is passed to the volumes.Create function. +// For more information about these parameters, see the Volume object. +type SchedulerHintOpts struct { // DifferentHost will place the volume on a different back-end that does not // host the given volumes. DifferentHost []string @@ -34,8 +36,8 @@ type SchedulerHints struct { AdditionalProperties map[string]interface{} } -// ToVolumeSchedulerHintsCreateMap assembles a request body for the scheduler -func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interface{}, error) { +// ToSchedulerHintsMap assembles a request body for scheduler hints +func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, error) { sh := make(map[string]interface{}) uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") @@ -44,7 +46,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa for _, diffHost := range opts.DifferentHost { if !uuidRegex.MatchString(diffHost) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.DifferentHost" + err.Argument = "volumes.SchedulerHintOpts.DifferentHost" err.Value = opts.DifferentHost err.Info = "The hosts must be in UUID format." return nil, err @@ -57,7 +59,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa for _, sameHost := range opts.SameHost { if !uuidRegex.MatchString(sameHost) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.SameHost" + err.Argument = "volumes.SchedulerHintOpts.SameHost" err.Value = opts.SameHost err.Info = "The hosts must be in UUID format." return nil, err @@ -69,7 +71,7 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa if opts.LocalToInstance != "" { if !uuidRegex.MatchString(opts.LocalToInstance) { err := gophercloud.ErrInvalidInput{} - err.Argument = "schedulerhints.SchedulerHints.LocalToInstance" + err.Argument = "volumes.SchedulerHintOpts.LocalToInstance" err.Value = opts.LocalToInstance err.Info = "The instance must be in UUID format." return nil, err @@ -87,7 +89,11 @@ func (opts SchedulerHints) ToVolumeSchedulerHintsCreateMap() (map[string]interfa } } - return sh, nil + if len(sh) == 0 { + return sh, nil + } + + return map[string]interface{}{"OS-SCH-HNT:scheduler_hints": sh}, nil } // CreateOptsBuilder allows extensions to add additional parameters to the @@ -126,52 +132,33 @@ type CreateOpts struct { BackupID string `json:"backup_id,omitempty"` // The associated volume type VolumeType string `json:"volume_type,omitempty"` - // SchedulerHints provides a set of hints to the scheduler. - SchedulerHints SchedulerHintsCreateOptsBuilder } // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { - // We intentionally don't envelope the body here since we want to strip - // some fields out - base, err := gophercloud.BuildRequestBody(opts, "") - if err != nil { - return nil, err - } - - delete(base, "SchedulerHints") - - // Now we do our enveloping - base = map[string]interface{}{"volume": base} - - if opts.SchedulerHints == nil { - return base, nil - } - - schedulerHints, err := opts.SchedulerHints.ToVolumeSchedulerHintsCreateMap() - if err != nil { - return nil, err - } - - if len(schedulerHints) == 0 { - return base, nil - } - - base["OS-SCH-HNT:scheduler_hints"] = schedulerHints - - return base, err + return gophercloud.BuildRequestBody(opts, "volume") } // Create will create a new Volume based on the values in CreateOpts. To extract // the Volume object from the response, call the Extract method on the // CreateResult. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder, hintOpts SchedulerHintOptsBuilder) (r CreateResult) { b, err := opts.ToVolumeCreateMap() if err != nil { r.Err = err return } + + if hintOpts != nil { + sh, err := hintOpts.ToSchedulerHintsMap() + if err != nil { + r.Err = err + return + } + maps.Copy(b, sh) + } + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index bf76ba5dbe..fbd4d9a9f5 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -207,16 +207,15 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) options := &volumes.CreateOpts{Size: 75, Name: "vol-001"} - n, err := volumes.Create(context.TODO(), client.ServiceClient(), options).Extract() + n, err := volumes.Create(context.TODO(), client.ServiceClient(), options, nil).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Size, 75) th.AssertEquals(t, n.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") } -func TestCreateWithSchedulerHints(t *testing.T) { - - schedulerHints := volumes.SchedulerHints{ +func TestCreateSchedulerHints(t *testing.T) { + base := volumes.SchedulerHintOpts{ DifferentHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", @@ -228,18 +227,8 @@ func TestCreateWithSchedulerHints(t *testing.T) { LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } - base := volumes.CreateOpts{ - Size: 10, - Name: "testvolume", - SchedulerHints: schedulerHints, - } - expected := ` { - "volume": { - "size": 10, - "name": "testvolume" - }, "OS-SCH-HNT:scheduler_hints": { "different_host": [ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", @@ -254,7 +243,7 @@ func TestCreateWithSchedulerHints(t *testing.T) { } } ` - actual, err := base.ToVolumeCreateMap() + actual, err := base.ToSchedulerHintsMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, expected, actual) } @@ -310,8 +299,7 @@ func TestCreateFromBackup(t *testing.T) { Name: "vol-001", BackupID: "20c792f0-bb03-434f-b653-06ef238e337e", } - - v, err := volumes.Create(context.TODO(), client.ServiceClient(), options).Extract() + v, err := volumes.Create(context.TODO(), client.ServiceClient(), options, nil).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, v.Size, 30) From d520ff82714c8b3f76b8f54ce7823c70921bdf37 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 4 Apr 2024 11:05:35 +0100 Subject: [PATCH 1853/2296] compute: Don't munge scheduler hints Do as we did for the block storage equivalent before and unmunge the scheduler hints options from the general server create options. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/compute.go | 24 +++---- openstack/compute/v2/servers/doc.go | 63 +++++++++-------- openstack/compute/v2/servers/requests.go | 69 +++++++++---------- .../v2/servers/testing/fixtures_test.go | 2 - .../v2/servers/testing/requests_test.go | 54 ++++----------- 5 files changed, 92 insertions(+), 120 deletions(-) diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index c2cd4b58b3..513344082f 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -122,7 +122,7 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, createOpts.ImageRef = blockDevices[0].UUID } - server, err = servers.Create(context.TODO(), client, createOpts).Extract() + server, err = servers.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return server, err @@ -249,7 +249,7 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, BlockDevice: blockDevices, } - server, err = servers.Create(context.TODO(), client, createOpts).Extract() + server, err = servers.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return server, err @@ -389,7 +389,7 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser Contents: []byte("hello world"), }, }, - }).Extract() + }, nil).Extract() if err != nil { return server, err } @@ -443,7 +443,7 @@ func CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) ( Metadata: map[string]string{ "abc": "def", }, - }).Extract() + }, nil).Extract() if err != nil { return server, err } @@ -497,7 +497,7 @@ func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient Contents: []byte("hello world"), }, }, - }).Extract() + }, nil).Extract() if err != nil { return nil, err } @@ -544,7 +544,7 @@ func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, netwo }, }, Tags: []string{"tag1", "tag2"}, - }).Extract() + }, nil).Extract() if err != nil { return server, err } @@ -643,12 +643,12 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, Networks: []servers.Network{ {UUID: networkID}, }, - SchedulerHints: servers.SchedulerHints{ - Group: serverGroup.ID, - }, + } + schedulerHintOpts := servers.SchedulerHintOpts{ + Group: serverGroup.ID, } - server, err := servers.Create(context.TODO(), client, serverCreateOpts).Extract() + server, err := servers.Create(context.TODO(), client, serverCreateOpts, schedulerHintOpts).Extract() if err != nil { return nil, err } @@ -697,7 +697,7 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, server, err := servers.Create(context.TODO(), client, keypairs.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, KeyName: keyPairName, - }).Extract() + }, nil).Extract() if err != nil { return nil, err } @@ -1056,7 +1056,7 @@ func CreateServerNoNetwork(t *testing.T, client *gophercloud.ServiceClient) (*se Contents: []byte("hello world"), }, }, - }).Extract() + }, nil).Extract() if err != nil { return server, err } diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go index 76c4a94b3c..72381f0744 100644 --- a/openstack/compute/v2/servers/doc.go +++ b/openstack/compute/v2/servers/doc.go @@ -53,59 +53,62 @@ Example to Create a Server FlavorRef: "flavor-uuid", } - server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts, nil).Extract() if err != nil { panic(err) } Example to Add a Server to a Server Group + schedulerHintOpts := servers.SchedulerHintOpts{ + Group: "servergroup-uuid", + } + createOpts := servers.CreateOpts{ - Name: "server_name", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - SchedulerHints: servers.SchedulerHints{ - Group: "servergroup-uuid", - }, + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", } - server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() if err != nil { panic(err) } Example to Place Server B on a Different Host than Server A + schedulerHintOpts := servers.SchedulerHintOpts{ + DifferentHost: []string{ + "server-a-uuid", + } + } + createOpts := servers.CreateOpts{ - Name: "server_name", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - SchedulerHints: servers.SchedulerHints{ - DifferentHost: []string{ - "server-a-uuid", - } - }, + Name: "server_b", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", } - server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() if err != nil { panic(err) } Example to Place Server B on the Same Host as Server A + schedulerHintOpts := servers.SchedulerHintOpts{ + SameHost: []string{ + "server-a-uuid", + } + } + createOpts := servers.CreateOpts{ - Name: "server_name", - ImageRef: "image-uuid", - FlavorRef: "flavor-uuid", - SchedulerHints: servers.SchedulerHints{ - SameHost: []string{ - "server-a-uuid", - } - }, + Name: "server_b", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", } - server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() + server, err := servers.Create(context.TODO(), computeClient, createOpts, schedulerHintOpts).Extract() if err != nil { panic(err) } @@ -133,7 +136,7 @@ a server without using block device mappings. BlockDevice: blockDevices, } - server, err := servers.Create(context.TODO(), client, createOpts).Extract() + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { panic(err) } @@ -159,7 +162,7 @@ server will use this volume as its root disk. BlockDevice: blockDevices, } - server, err := servers.Create(context.TODO(), client, createOpts).Extract() + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { panic(err) } @@ -183,7 +186,7 @@ This example will create a server with an existing volume as its root disk. BlockDevice: blockDevices, } - server, err := servers.Create(context.TODO(), client, createOpts).Extract() + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { panic(err) } @@ -228,7 +231,7 @@ ephemeral disks must have an index of -1. BlockDevice: blockDevices, } - server, err := servers.Create(context.TODO(), client, createOpts).Extract() + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { panic(err) } diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index c5d0b79ef4..ea2df1846c 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "maps" "net" "regexp" "strings" @@ -128,14 +129,14 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// SchedulerHintsCreateOptsBuilder builds the scheduler hints into a serializable format. -type SchedulerHintsCreateOptsBuilder interface { - ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) +// SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. +type SchedulerHintOptsBuilder interface { + ToSchedulerHintsMap() (map[string]interface{}, error) } -// SchedulerHints represents a set of scheduling hints that are passed to the +// SchedulerHintOpts represents a set of scheduling hints that are passed to the // OpenStack scheduler. -type SchedulerHints struct { +type SchedulerHintOpts struct { // Group specifies a Server Group to place the instance in. Group string @@ -164,8 +165,8 @@ type SchedulerHints struct { AdditionalProperties map[string]interface{} } -// ToServerSchedulerHintsMap builds the scheduler hints into a serializable format. -func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) { +// ToSchedulerHintsMap assembles a request body for scheduler hints. +func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, error) { sh := make(map[string]interface{}) uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") @@ -173,7 +174,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa if opts.Group != "" { if !uuidRegex.MatchString(opts.Group) { err := gophercloud.ErrInvalidInput{} - err.Argument = "servers.SchedulerHints.Group" + err.Argument = "servers.schedulerhints.SchedulerHintOpts.Group" err.Value = opts.Group err.Info = "Group must be a UUID" return nil, err @@ -185,7 +186,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa for _, diffHost := range opts.DifferentHost { if !uuidRegex.MatchString(diffHost) { err := gophercloud.ErrInvalidInput{} - err.Argument = "servers.SchedulerHints.DifferentHost" + err.Argument = "servers.schedulerhints.SchedulerHintOpts.DifferentHost" err.Value = opts.DifferentHost err.Info = "The hosts must be in UUID format." return nil, err @@ -198,7 +199,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa for _, sameHost := range opts.SameHost { if !uuidRegex.MatchString(sameHost) { err := gophercloud.ErrInvalidInput{} - err.Argument = "servers.SchedulerHints.SameHost" + err.Argument = "servers.schedulerhints.SchedulerHintOpts.SameHost" err.Value = opts.SameHost err.Info = "The hosts must be in UUID format." return nil, err @@ -222,7 +223,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa if len(opts.Query) > 0 { if len(opts.Query) < 3 { err := gophercloud.ErrInvalidInput{} - err.Argument = "servers.SchedulerHints.Query" + err.Argument = "servers.schedulerhints.SchedulerHintOpts.Query" err.Value = opts.Query err.Info = "Must be a conditional statement in the format of [op,variable,value]" return nil, err @@ -232,7 +233,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa b, err := json.Marshal(opts.Query) if err != nil { err := gophercloud.ErrInvalidInput{} - err.Argument = "servers.SchedulerHints.Query" + err.Argument = "servers.schedulerhints.SchedulerHintOpts.Query" err.Value = opts.Query err.Info = "Must be a conditional statement in the format of [op,variable,value]" return nil, err @@ -252,7 +253,7 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa if opts.BuildNearHostIP != "" { if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil { err := gophercloud.ErrInvalidInput{} - err.Argument = "servers.SchedulerHints.BuildNearHostIP" + err.Argument = "servers.schedulerhints.SchedulerHintOpts.BuildNearHostIP" err.Value = opts.BuildNearHostIP err.Info = "Must be a valid subnet in the form 192.168.1.1/24" return nil, err @@ -268,7 +269,11 @@ func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interfa } } - return sh, nil + if len(sh) == 0 { + return sh, nil + } + + return map[string]interface{}{"os:scheduler_hints": sh}, nil } // Network is used within CreateOpts to control a new server's network @@ -503,9 +508,6 @@ type CreateOpts struct { // DiskConfig [optional] controls how the created server's disk is partitioned. DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` - - // SchedulerHints provides a set of hints to the scheduler. - SchedulerHints SchedulerHintsCreateOptsBuilder } // ToServerCreateMap assembles a request body based on the contents of a @@ -518,8 +520,6 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { return nil, err } - delete(b, "SchedulerHints") - if opts.UserData != nil { var userData string if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil { @@ -578,32 +578,27 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { // Now we do our enveloping b = map[string]interface{}{"server": b} - if opts.SchedulerHints == nil { - return b, nil - } - - schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsCreateMap() - if err != nil { - return nil, err - } - - if len(schedulerHints) == 0 { - return b, nil - } - - b["os:scheduler_hints"] = schedulerHints - return b, nil } // Create requests a server to be provisioned to the user in the current tenant. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - reqBody, err := opts.ToServerCreateMap() +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder, hintOpts SchedulerHintOptsBuilder) (r CreateResult) { + b, err := opts.ToServerCreateMap() if err != nil { r.Err = err return } - resp, err := client.Post(ctx, createURL(client), reqBody, &r.Body, &gophercloud.RequestOpts{ + + if hintOpts != nil { + sh, err := hintOpts.ToSchedulerHintsMap() + if err != nil { + r.Err = err + return + } + maps.Copy(b, sh) + } + + resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index e5cde0d8fb..a7cc90563e 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -721,8 +721,6 @@ func (opts CreateOptsWithCustomField) ToServerCreateMap() (map[string]interface{ return nil, err } - delete(b, "SchedulerHints") - return map[string]interface{}{"server": b}, nil } diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 446e31dbd2..15c6995010 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -85,7 +85,7 @@ func TestCreateServer(t *testing.T) { Name: "derp", ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -101,7 +101,7 @@ func TestCreateServerNoNetwork(t *testing.T) { ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", Networks: "none", - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -118,7 +118,7 @@ func TestCreateServers(t *testing.T) { FlavorRef: "1", Min: 3, Max: 3, - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -136,7 +136,7 @@ func TestCreateServerWithCustomField(t *testing.T) { FlavorRef: "1", }, Foo: "bar", - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -154,7 +154,7 @@ func TestCreateServerWithMetadata(t *testing.T) { Metadata: map[string]string{ "abc": "def", }, - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -170,7 +170,7 @@ func TestCreateServerWithUserdataString(t *testing.T) { ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", UserData: []byte("userdata string"), - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -188,7 +188,7 @@ func TestCreateServerWithUserdataEncoded(t *testing.T) { ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", UserData: []byte(encoded), - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -204,7 +204,7 @@ func TestCreateServerWithHostname(t *testing.T) { ImageRef: "f90f6034-2570-4974-8351-6b49732ef2eb", FlavorRef: "1", Hostname: "derp.local", - }).Extract() + }, nil).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, ServerDerp, *actual) @@ -629,8 +629,8 @@ func TestCreateServerWithBFVAttachExistingVolumeWithTag(t *testing.T) { th.CheckJSONEquals(t, ExpectedImageAndExistingVolumeWithTagRequest, actual) } -func TestCreateServerWithSchedulerHints(t *testing.T) { - schedulerHints := servers.SchedulerHints{ +func TestCreateSchedulerHints(t *testing.T) { + opts := servers.SchedulerHintOpts{ Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba", DifferentHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", @@ -650,20 +650,8 @@ func TestCreateServerWithSchedulerHints(t *testing.T) { AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } - opts := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - SchedulerHints: schedulerHints, - } - expected := ` { - "server": { - "name": "createdserver", - "imageRef": "asdfasdfasdf", - "flavorRef": "performance1-1" - }, "os:scheduler_hints": { "group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba", "different_host": [ @@ -686,13 +674,13 @@ func TestCreateServerWithSchedulerHints(t *testing.T) { } } ` - actual, err := opts.ToServerCreateMap() + actual, err := opts.ToSchedulerHintsMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, expected, actual) } -func TestCreateServerWithComplexSchedulerHints(t *testing.T) { - schedulerHints := servers.SchedulerHints{ +func TestCreateComplexSchedulerHints(t *testing.T) { + opts := servers.SchedulerHintOpts{ Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba", DifferentHost: []string{ "a0cf03a5-d921-4877-bb5c-86d26cf818e1", @@ -712,20 +700,8 @@ func TestCreateServerWithComplexSchedulerHints(t *testing.T) { AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } - opts := servers.CreateOpts{ - Name: "createdserver", - ImageRef: "asdfasdfasdf", - FlavorRef: "performance1-1", - SchedulerHints: schedulerHints, - } - expected := ` { - "server": { - "name": "createdserver", - "imageRef": "asdfasdfasdf", - "flavorRef": "performance1-1" - }, "os:scheduler_hints": { "group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba", "different_host": [ @@ -748,7 +724,7 @@ func TestCreateServerWithComplexSchedulerHints(t *testing.T) { } } ` - actual, err := opts.ToServerCreateMap() + actual, err := opts.ToSchedulerHintsMap() th.AssertNoErr(t, err) th.CheckJSONEquals(t, expected, actual) } @@ -1176,7 +1152,7 @@ func TestCreateServerWithTags(t *testing.T) { FlavorRef: "1", Tags: tags, } - res := servers.Create(context.TODO(), c, createOpts) + res := servers.Create(context.TODO(), c, createOpts, nil) th.AssertNoErr(t, res.Err) actualServer, err := res.Extract() th.AssertNoErr(t, err) From 3dc4c9ea3ffba48e246f6d6c26ae3444297bf486 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 20 May 2024 17:10:19 +0100 Subject: [PATCH 1854/2296] Drop support for clustering service (Senlin) The Senlin project has not been maintained in recent releases and has been retired [1]. We shouldn't continue to support it in 2.x. Users who still need support can continue to use 1.x. [1] https://review.opendev.org/c/openstack/governance/+/919347 Signed-off-by: Stephen Finucane --- .github/workflows/functional-clustering.yaml | 58 -- Makefile | 6 +- internal/acceptance/clients/clients.go | 21 - .../openstack/clustering/v1/actions_test.go | 32 - .../openstack/clustering/v1/clustering.go | 473 ------------ .../openstack/clustering/v1/clusters_test.go | 509 ------------- .../openstack/clustering/v1/events_test.go | 32 - .../openstack/clustering/v1/nodes_test.go | 216 ------ .../acceptance/openstack/clustering/v1/pkg.go | 5 - .../openstack/clustering/v1/policies_test.go | 70 -- .../clustering/v1/policytypes_test.go | 65 -- .../openstack/clustering/v1/profiles_test.go | 77 -- .../clustering/v1/profiletypes_test.go | 48 -- .../openstack/clustering/v1/receivers_test.go | 83 --- .../clustering/v1/webhooktrigger_test.go | 65 -- openstack/client.go | 6 - openstack/clustering/v1/actions/doc.go | 33 - openstack/clustering/v1/actions/requests.go | 54 -- openstack/clustering/v1/actions/results.go | 110 --- .../clustering/v1/actions/testing/doc.go | 2 - .../v1/actions/testing/fixtures_test.go | 166 ----- .../v1/actions/testing/requests_test.go | 45 -- openstack/clustering/v1/actions/urls.go | 22 - openstack/clustering/v1/clusters/doc.go | 286 ------- openstack/clustering/v1/clusters/requests.go | 625 ---------------- openstack/clustering/v1/clusters/results.go | 227 ------ .../clustering/v1/clusters/testing/doc.go | 2 - .../v1/clusters/testing/fixtures_test.go | 705 ------------------ .../v1/clusters/testing/requests_test.go | 497 ------------ openstack/clustering/v1/clusters/urls.go | 58 -- openstack/clustering/v1/events/doc.go | 33 - openstack/clustering/v1/events/requests.go | 56 -- openstack/clustering/v1/events/results.go | 70 -- openstack/clustering/v1/events/testing/doc.go | 2 - .../v1/events/testing/fixtures_test.go | 131 ---- .../v1/events/testing/requests_test.go | 46 -- openstack/clustering/v1/events/urls.go | 22 - openstack/clustering/v1/nodes/doc.go | 110 --- openstack/clustering/v1/nodes/requests.go | 229 ------ openstack/clustering/v1/nodes/results.go | 148 ---- openstack/clustering/v1/nodes/testing/doc.go | 2 - .../v1/nodes/testing/fixtures_test.go | 364 --------- .../v1/nodes/testing/requests_test.go | 145 ---- openstack/clustering/v1/nodes/urls.go | 42 -- openstack/clustering/v1/policies/doc.go | 95 --- openstack/clustering/v1/policies/requests.go | 177 ----- openstack/clustering/v1/policies/results.go | 189 ----- .../clustering/v1/policies/testing/doc.go | 2 - .../v1/policies/testing/fixtures_test.go | 513 ------------- .../v1/policies/testing/requests_test.go | 149 ---- openstack/clustering/v1/policies/urls.go | 32 - openstack/clustering/v1/policytypes/doc.go | 31 - .../clustering/v1/policytypes/requests.go | 28 - .../clustering/v1/policytypes/results.go | 72 -- .../clustering/v1/policytypes/testing/doc.go | 2 - .../v1/policytypes/testing/fixtures_test.go | 235 ------ .../v1/policytypes/testing/requests_test.go | 50 -- openstack/clustering/v1/policytypes/urls.go | 16 - openstack/clustering/v1/profiles/doc.go | 108 --- openstack/clustering/v1/profiles/requests.go | 155 ---- openstack/clustering/v1/profiles/results.go | 170 ----- .../clustering/v1/profiles/testing/doc.go | 2 - .../v1/profiles/testing/fixtures_test.go | 463 ------------ .../v1/profiles/testing/requests_test.go | 141 ---- openstack/clustering/v1/profiles/urls.go | 38 - openstack/clustering/v1/profiletypes/doc.go | 47 -- .../clustering/v1/profiletypes/requests.go | 30 - .../clustering/v1/profiletypes/results.go | 71 -- .../clustering/v1/profiletypes/testing/doc.go | 2 - .../v1/profiletypes/testing/fixtures_test.go | 296 -------- .../v1/profiletypes/testing/requests_test.go | 75 -- openstack/clustering/v1/profiletypes/urls.go | 28 - openstack/clustering/v1/receivers/doc.go | 80 -- openstack/clustering/v1/receivers/requests.go | 147 ---- openstack/clustering/v1/receivers/results.go | 130 ---- .../clustering/v1/receivers/testing/doc.go | 2 - .../v1/receivers/testing/fixtures_test.go | 241 ------ .../v1/receivers/testing/requests_test.go | 106 --- openstack/clustering/v1/receivers/urls.go | 38 - openstack/clustering/v1/webhooks/doc.go | 14 - openstack/clustering/v1/webhooks/requests.go | 55 -- openstack/clustering/v1/webhooks/results.go | 22 - .../clustering/v1/webhooks/testing/doc.go | 2 - .../v1/webhooks/testing/requests_test.go | 136 ---- openstack/clustering/v1/webhooks/urls.go | 7 - 85 files changed, 1 insertion(+), 10194 deletions(-) delete mode 100644 .github/workflows/functional-clustering.yaml delete mode 100644 internal/acceptance/openstack/clustering/v1/actions_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/clustering.go delete mode 100644 internal/acceptance/openstack/clustering/v1/clusters_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/events_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/nodes_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/pkg.go delete mode 100644 internal/acceptance/openstack/clustering/v1/policies_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/policytypes_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/profiles_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/profiletypes_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/receivers_test.go delete mode 100644 internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go delete mode 100644 openstack/clustering/v1/actions/doc.go delete mode 100644 openstack/clustering/v1/actions/requests.go delete mode 100644 openstack/clustering/v1/actions/results.go delete mode 100644 openstack/clustering/v1/actions/testing/doc.go delete mode 100644 openstack/clustering/v1/actions/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/actions/testing/requests_test.go delete mode 100644 openstack/clustering/v1/actions/urls.go delete mode 100644 openstack/clustering/v1/clusters/doc.go delete mode 100644 openstack/clustering/v1/clusters/requests.go delete mode 100644 openstack/clustering/v1/clusters/results.go delete mode 100644 openstack/clustering/v1/clusters/testing/doc.go delete mode 100644 openstack/clustering/v1/clusters/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/clusters/testing/requests_test.go delete mode 100644 openstack/clustering/v1/clusters/urls.go delete mode 100644 openstack/clustering/v1/events/doc.go delete mode 100644 openstack/clustering/v1/events/requests.go delete mode 100644 openstack/clustering/v1/events/results.go delete mode 100644 openstack/clustering/v1/events/testing/doc.go delete mode 100644 openstack/clustering/v1/events/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/events/testing/requests_test.go delete mode 100644 openstack/clustering/v1/events/urls.go delete mode 100644 openstack/clustering/v1/nodes/doc.go delete mode 100644 openstack/clustering/v1/nodes/requests.go delete mode 100644 openstack/clustering/v1/nodes/results.go delete mode 100644 openstack/clustering/v1/nodes/testing/doc.go delete mode 100644 openstack/clustering/v1/nodes/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/nodes/testing/requests_test.go delete mode 100644 openstack/clustering/v1/nodes/urls.go delete mode 100644 openstack/clustering/v1/policies/doc.go delete mode 100644 openstack/clustering/v1/policies/requests.go delete mode 100644 openstack/clustering/v1/policies/results.go delete mode 100644 openstack/clustering/v1/policies/testing/doc.go delete mode 100644 openstack/clustering/v1/policies/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/policies/testing/requests_test.go delete mode 100644 openstack/clustering/v1/policies/urls.go delete mode 100644 openstack/clustering/v1/policytypes/doc.go delete mode 100644 openstack/clustering/v1/policytypes/requests.go delete mode 100644 openstack/clustering/v1/policytypes/results.go delete mode 100644 openstack/clustering/v1/policytypes/testing/doc.go delete mode 100644 openstack/clustering/v1/policytypes/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/policytypes/testing/requests_test.go delete mode 100644 openstack/clustering/v1/policytypes/urls.go delete mode 100644 openstack/clustering/v1/profiles/doc.go delete mode 100644 openstack/clustering/v1/profiles/requests.go delete mode 100644 openstack/clustering/v1/profiles/results.go delete mode 100644 openstack/clustering/v1/profiles/testing/doc.go delete mode 100644 openstack/clustering/v1/profiles/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/profiles/testing/requests_test.go delete mode 100644 openstack/clustering/v1/profiles/urls.go delete mode 100644 openstack/clustering/v1/profiletypes/doc.go delete mode 100644 openstack/clustering/v1/profiletypes/requests.go delete mode 100644 openstack/clustering/v1/profiletypes/results.go delete mode 100644 openstack/clustering/v1/profiletypes/testing/doc.go delete mode 100644 openstack/clustering/v1/profiletypes/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/profiletypes/testing/requests_test.go delete mode 100644 openstack/clustering/v1/profiletypes/urls.go delete mode 100644 openstack/clustering/v1/receivers/doc.go delete mode 100644 openstack/clustering/v1/receivers/requests.go delete mode 100644 openstack/clustering/v1/receivers/results.go delete mode 100644 openstack/clustering/v1/receivers/testing/doc.go delete mode 100644 openstack/clustering/v1/receivers/testing/fixtures_test.go delete mode 100644 openstack/clustering/v1/receivers/testing/requests_test.go delete mode 100644 openstack/clustering/v1/receivers/urls.go delete mode 100644 openstack/clustering/v1/webhooks/doc.go delete mode 100644 openstack/clustering/v1/webhooks/requests.go delete mode 100644 openstack/clustering/v1/webhooks/results.go delete mode 100644 openstack/clustering/v1/webhooks/testing/doc.go delete mode 100644 openstack/clustering/v1/webhooks/testing/requests_test.go delete mode 100644 openstack/clustering/v1/webhooks/urls.go diff --git a/.github/workflows/functional-clustering.yaml b/.github/workflows/functional-clustering.yaml deleted file mode 100644 index daca0d8d33..0000000000 --- a/.github/workflows/functional-clustering.yaml +++ /dev/null @@ -1,58 +0,0 @@ -name: functional-clustering -on: - pull_request: - paths: - - '**clustering**' - schedule: - - cron: '0 0 */3 * *' -jobs: - functional-clustering: - strategy: - fail-fast: false - matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] - include: -## Senlin has not made a caracal release yet -# - name: "caracal" -# openstack_version: "stable/2024.1" -# 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" - 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@v4 - - name: Deploy devstack - uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 - with: - branch: ${{ matrix.openstack_version }} - conf_overrides: | - 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@v5 - with: - go-version: '^1.20' - - name: Run Gophercloud acceptance tests - run: ./script/acceptancetest - 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() - - name: Upload logs artifacts on failure - if: failure() - uses: actions/upload-artifact@v4 - with: - name: functional-clustering-${{ matrix.name }} - path: /tmp/devstack-logs/* diff --git a/Makefile b/Makefile index 79c1cc5615..74b88d0598 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ coverage: go test -covermode count -coverprofile cover.out -coverpkg=./... ./... .PHONY: coverage -acceptance: acceptance-baremetal acceptance-blockstorage acceptance-clustering acceptance-compute acceptance-container acceptance-containerinfra acceptance-db acceptance-dns acceptance-identity acceptance-imageservice acceptance-keymanager acceptance-loadbalancer acceptance-messaging acceptance-networking acceptance-objectstorage acceptance-orchestration acceptance-placement acceptance-sharedfilesystems acceptance-workflow +acceptance: acceptance-baremetal acceptance-blockstorage acceptance-compute acceptance-container acceptance-containerinfra acceptance-db acceptance-dns acceptance-identity acceptance-imageservice acceptance-keymanager acceptance-loadbalancer acceptance-messaging acceptance-networking acceptance-objectstorage acceptance-orchestration acceptance-placement acceptance-sharedfilesystems acceptance-workflow .PHONY: acceptance acceptance-baremetal: @@ -24,10 +24,6 @@ acceptance-blockstorage: go test -tags "fixtures acceptance" ./internal/acceptance/openstack/blockstorage/... .PHONY: acceptance-blockstorage -acceptance-clustering: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/clustering/... -.PHONY: acceptance-clustering - acceptance-compute: go test -tags "fixtures acceptance" ./internal/acceptance/openstack/compute/... .PHONY: acceptance-compute diff --git a/internal/acceptance/clients/clients.go b/internal/acceptance/clients/clients.go index 9fdde23c10..21be9dbea2 100644 --- a/internal/acceptance/clients/clients.go +++ b/internal/acceptance/clients/clients.go @@ -568,27 +568,6 @@ func NewLoadBalancerV2Client() (*gophercloud.ServiceClient, error) { }) } -// NewClusteringV1Client returns a *ServiceClient for making calls -// to the OpenStack Clustering v1 API. An error will be returned -// if authentication or client creation was not possible. -func NewClusteringV1Client() (*gophercloud.ServiceClient, error) { - ao, err := openstack.AuthOptionsFromEnv() - if err != nil { - return nil, err - } - - client, err := openstack.AuthenticatedClient(context.TODO(), ao) - if err != nil { - return nil, err - } - - client = configureDebug(client) - - return openstack.NewClusteringV1(client, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), - }) -} - // NewMessagingV2Client returns a *ServiceClient for making calls // to the OpenStack Messaging (Zaqar) v2 API. An error will be returned // if authentication or client creation was not possible. diff --git a/internal/acceptance/openstack/clustering/v1/actions_test.go b/internal/acceptance/openstack/clustering/v1/actions_test.go deleted file mode 100644 index 4aa7585fd8..0000000000 --- a/internal/acceptance/openstack/clustering/v1/actions_test.go +++ /dev/null @@ -1,32 +0,0 @@ -//go:build acceptance || clustering || actions - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestActionsList(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - opts := actions.ListOpts{ - Limit: 200, - } - - allPages, err := actions.List(client, opts).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allActions, err := actions.ExtractActions(allPages) - th.AssertNoErr(t, err) - - for _, action := range allActions { - tools.PrintResource(t, action) - } -} diff --git a/internal/acceptance/openstack/clustering/v1/clustering.go b/internal/acceptance/openstack/clustering/v1/clustering.go deleted file mode 100644 index c7233fae41..0000000000 --- a/internal/acceptance/openstack/clustering/v1/clustering.go +++ /dev/null @@ -1,473 +0,0 @@ -package v1 - -import ( - "context" - "fmt" - "net/http" - "strings" - "testing" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/clusters" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -var TestPolicySpec = policies.Spec{ - Description: "new policy description", - Properties: map[string]interface{}{ - "destroy_after_deletion": true, - "grace_period": 60, - "reduce_desired_capacity": false, - "criteria": "OLDEST_FIRST", - }, - Type: "senlin.policy.deletion", - Version: "1.1", -} - -// CreateCluster creates a random cluster. An error will be returned if -// the cluster could not be created. -func CreateCluster(t *testing.T, client *gophercloud.ServiceClient, profileID string) (*clusters.Cluster, error) { - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create cluster: %s", name) - - createOpts := clusters.CreateOpts{ - Name: name, - DesiredCapacity: 1, - ProfileID: profileID, - MinSize: new(int), - MaxSize: 20, - Timeout: 3600, - Metadata: map[string]interface{}{ - "foo": "bar", - "test": map[string]interface{}{ - "nil_interface": interface{}(nil), - "float_value": float64(123.3), - "string_value": "test_string", - "bool_value": false, - }, - }, - Config: map[string]interface{}{}, - } - - res := clusters.Create(context.TODO(), client, createOpts) - if res.Err != nil { - return nil, res.Err - } - - requestID := res.Header.Get("X-OpenStack-Request-Id") - th.AssertEquals(t, true, requestID != "") - t.Logf("Cluster %s request ID: %s", name, requestID) - - actionID, err := GetActionID(res.Header) - th.AssertNoErr(t, err) - th.AssertEquals(t, true, actionID != "") - t.Logf("Cluster %s action ID: %s", name, actionID) - - err = WaitForAction(client, actionID) - if err != nil { - return nil, err - } - - cluster, err := res.Extract() - if err != nil { - return nil, err - } - - t.Logf("Successfully created cluster: %s", cluster.ID) - - tools.PrintResource(t, cluster) - tools.PrintResource(t, cluster.CreatedAt) - - th.AssertEquals(t, name, cluster.Name) - th.AssertEquals(t, profileID, cluster.ProfileID) - - return cluster, nil -} - -// CreateNode creates a random node. An error will be returned if -// the node could not be created. -func CreateNode(t *testing.T, client *gophercloud.ServiceClient, clusterID, profileID string) (*nodes.Node, error) { - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create node: %s", name) - - createOpts := nodes.CreateOpts{ - ClusterID: clusterID, - Metadata: map[string]interface{}{ - "foo": "bar", - "test": map[string]interface{}{ - "nil_interface": interface{}(nil), - "float_value": float64(123.3), - "string_value": "test_string", - "bool_value": false, - }, - }, - Name: name, - ProfileID: profileID, - Role: "", - } - - res := nodes.Create(context.TODO(), client, createOpts) - if res.Err != nil { - return nil, res.Err - } - - requestID := res.Header.Get("X-OpenStack-Request-Id") - th.AssertEquals(t, true, requestID != "") - t.Logf("Node %s request ID: %s", name, requestID) - - actionID, err := GetActionID(res.Header) - th.AssertNoErr(t, err) - th.AssertEquals(t, true, actionID != "") - t.Logf("Node %s action ID: %s", name, actionID) - - err = WaitForAction(client, actionID) - if err != nil { - return nil, err - } - - node, err := res.Extract() - if err != nil { - return nil, err - } - - err = WaitForNodeStatus(client, node.ID, "ACTIVE") - if err != nil { - return nil, err - } - - t.Logf("Successfully created node: %s", node.ID) - - node, err = nodes.Get(context.TODO(), client, node.ID).Extract() - if err != nil { - return nil, err - } - - tools.PrintResource(t, node) - tools.PrintResource(t, node.CreatedAt) - - th.AssertEquals(t, profileID, node.ProfileID) - th.AssertEquals(t, clusterID, node.ClusterID) - th.AssertDeepEquals(t, createOpts.Metadata, node.Metadata) - - return node, nil -} - -// CreatePolicy creates a random policy. An error will be returned if the -// policy could not be created. -func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient) (*policies.Policy, error) { - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create policy: %s", name) - - createOpts := policies.CreateOpts{ - Name: name, - Spec: TestPolicySpec, - } - - res := policies.Create(context.TODO(), client, createOpts) - if res.Err != nil { - return nil, res.Err - } - - requestID := res.Header.Get("X-OpenStack-Request-Id") - th.AssertEquals(t, true, requestID != "") - - t.Logf("Policy %s request ID: %s", name, requestID) - - policy, err := res.Extract() - if err != nil { - return nil, err - } - - t.Logf("Successfully created policy: %s", policy.ID) - - tools.PrintResource(t, policy) - tools.PrintResource(t, policy.CreatedAt) - - th.AssertEquals(t, name, policy.Name) - - return policy, nil -} - -// CreateProfile will create a random profile. An error will be returned if the -// profile could not be created. -func CreateProfile(t *testing.T, client *gophercloud.ServiceClient) (*profiles.Profile, error) { - choices, err := clients.AcceptanceTestChoicesFromEnv() - if err != nil { - return nil, err - } - - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create profile: %s", name) - - networks := []map[string]interface{}{ - {"network": choices.NetworkName}, - } - - props := map[string]interface{}{ - "name": name, - "flavor": choices.FlavorID, - "image": choices.ImageID, - "networks": networks, - "security_groups": "", - } - - createOpts := profiles.CreateOpts{ - Name: name, - Spec: profiles.Spec{ - Type: "os.nova.server", - Version: "1.0", - Properties: props, - }, - } - - res := profiles.Create(context.TODO(), client, createOpts) - if res.Err != nil { - return nil, res.Err - } - - requestID := res.Header.Get("X-OpenStack-Request-Id") - th.AssertEquals(t, true, requestID != "") - - t.Logf("Profile %s request ID: %s", name, requestID) - - profile, err := res.Extract() - if err != nil { - return nil, err - } - - t.Logf("Successfully created profile: %s", profile.ID) - - tools.PrintResource(t, profile) - tools.PrintResource(t, profile.CreatedAt) - - th.AssertEquals(t, name, profile.Name) - th.AssertEquals(t, profile.Spec.Type, "os.nova.server") - th.AssertEquals(t, profile.Spec.Version, "1.0") - - return profile, nil -} - -// CreateWebhookReceiver will create a random webhook receiver. An error will be returned if the -// receiver could not be created. -func CreateWebhookReceiver(t *testing.T, client *gophercloud.ServiceClient, clusterID string) (*receivers.Receiver, error) { - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create receiver: %s", name) - - createOpts := receivers.CreateOpts{ - Name: name, - ClusterID: clusterID, - Type: receivers.WebhookReceiver, - Action: "CLUSTER_SCALE_OUT", - } - - res := receivers.Create(context.TODO(), client, createOpts) - if res.Err != nil { - return nil, res.Err - } - - receiver, err := res.Extract() - if err != nil { - return nil, err - } - - t.Logf("Successfully created webhook receiver: %s", receiver.ID) - - tools.PrintResource(t, receiver) - tools.PrintResource(t, receiver.CreatedAt) - - th.AssertEquals(t, name, receiver.Name) - th.AssertEquals(t, createOpts.Action, receiver.Action) - - return receiver, nil -} - -// CreateMessageReceiver will create a message receiver with a random name. An error will be returned if the -// receiver could not be created. -func CreateMessageReceiver(t *testing.T, client *gophercloud.ServiceClient, clusterID string) (*receivers.Receiver, error) { - name := tools.RandomString("TESTACC-", 8) - t.Logf("Attempting to create receiver: %s", name) - - createOpts := receivers.CreateOpts{ - Name: name, - ClusterID: clusterID, - Type: receivers.MessageReceiver, - } - - res := receivers.Create(context.TODO(), client, createOpts) - if res.Err != nil { - return nil, res.Err - } - - receiver, err := res.Extract() - if err != nil { - return nil, err - } - - t.Logf("Successfully created message receiver: %s", receiver.ID) - - tools.PrintResource(t, receiver) - tools.PrintResource(t, receiver.CreatedAt) - - th.AssertEquals(t, name, receiver.Name) - th.AssertEquals(t, createOpts.Action, receiver.Action) - - return receiver, nil -} - -// DeleteCluster will delete a given policy. A fatal error will occur if the -// cluster could not be deleted. This works best as a deferred function. -func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { - t.Logf("Attempting to delete cluster: %s", id) - - res := clusters.Delete(context.TODO(), client, id) - if res.Err != nil { - t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) - } - - actionID, err := GetActionID(res.Header) - if err != nil { - t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) - } - - err = WaitForAction(client, actionID) - if err != nil { - t.Fatalf("Error deleting cluster %s: %s:", id, res.Err) - } - - t.Logf("Successfully deleted cluster: %s", id) - - return -} - -// DeleteNode will delete a given node. A fatal error will occur if the -// node could not be deleted. This works best as a deferred function. -func DeleteNode(t *testing.T, client *gophercloud.ServiceClient, id string) { - t.Logf("Attempting to delete node: %s", id) - - res := nodes.Delete(context.TODO(), client, id) - if res.Err != nil { - t.Fatalf("Error deleting node %s: %s:", id, res.Err) - } - - actionID, err := GetActionID(res.Header) - if err != nil { - t.Fatalf("Error getting actionID %s: %s:", id, err) - } - - err = WaitForAction(client, actionID) - - if err != nil { - t.Fatalf("Error deleting node %s: %s", id, err) - } - - t.Logf("Successfully deleted node: %s", id) - - return -} - -// DeletePolicy will delete a given policy. A fatal error will occur if the -// policy could not be deleted. This works best as a deferred function. -func DeletePolicy(t *testing.T, client *gophercloud.ServiceClient, id string) { - t.Logf("Attempting to delete policy: %s", id) - - err := policies.Delete(context.TODO(), client, id).ExtractErr() - if err != nil { - t.Fatalf("Error deleting policy %s: %s:", id, err) - } - - t.Logf("Successfully deleted policy: %s", id) - - return -} - -// DeleteProfile will delete a given profile. A fatal error will occur if the -// profile could not be deleted. This works best as a deferred function. -func DeleteProfile(t *testing.T, client *gophercloud.ServiceClient, id string) { - t.Logf("Attempting to delete profile: %s", id) - - err := profiles.Delete(context.TODO(), client, id).ExtractErr() - if err != nil { - t.Fatalf("Error deleting profile %s: %s:", id, err) - } - - t.Logf("Successfully deleted profile: %s", id) - - return -} - -// DeleteReceiver will delete a given receiver. A fatal error will occur if the -// receiver could not be deleted. This works best as a deferred function. -func DeleteReceiver(t *testing.T, client *gophercloud.ServiceClient, id string) { - t.Logf("Attempting to delete Receiver: %s", id) - - res := receivers.Delete(context.TODO(), client, id) - if res.Err != nil { - t.Fatalf("Error deleting receiver %s: %s:", id, res.Err) - } - - t.Logf("Successfully deleted receiver: %s", id) - - return -} - -// GetActionID parses an HTTP header and returns the action ID. -func GetActionID(headers http.Header) (string, error) { - location := headers.Get("Location") - v := strings.Split(location, "actions/") - if len(v) < 2 { - return "", fmt.Errorf("unable to determine action ID") - } - - actionID := v[1] - - return actionID, nil -} - -func WaitForAction(client *gophercloud.ServiceClient, actionID string) error { - return tools.WaitFor(func(ctx context.Context) (bool, error) { - action, err := actions.Get(ctx, client, actionID).Extract() - if err != nil { - return false, err - } - - if action.Status == "SUCCEEDED" { - return true, nil - } - - if action.Status == "FAILED" { - return false, fmt.Errorf("Action %s in FAILED state", actionID) - } - - return false, nil - }) -} - -func WaitForNodeStatus(client *gophercloud.ServiceClient, id string, status string) error { - return tools.WaitFor(func(ctx context.Context) (bool, error) { - latest, err := nodes.Get(ctx, client, id).Extract() - if err != nil { - if gophercloud.ResponseCodeIs(err, http.StatusNotFound) && status == "DELETED" { - return true, nil - } - - return false, err - } - - if latest.Status == status { - return true, nil - } - - if latest.Status == "ERROR" { - return false, fmt.Errorf("Node %s in ERROR state", id) - } - - return false, nil - }) -} diff --git a/internal/acceptance/openstack/clustering/v1/clusters_test.go b/internal/acceptance/openstack/clustering/v1/clusters_test.go deleted file mode 100644 index ebaa4e3697..0000000000 --- a/internal/acceptance/openstack/clustering/v1/clusters_test.go +++ /dev/null @@ -1,509 +0,0 @@ -//go:build acceptance || clustering || policies - -package v1 - -import ( - "context" - "sort" - "strings" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/clusters" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestClustersCRUD(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - // Test clusters list - allPages, err := clusters.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allClusters, err := clusters.ExtractClusters(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, v := range allClusters { - if v.ID == cluster.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - // Test cluster update - updateOpts := clusters.UpdateOpts{ - Name: cluster.Name + "-UPDATED", - } - - res := clusters.Update(context.TODO(), client, cluster.ID, updateOpts) - th.AssertNoErr(t, res.Err) - - actionID, err := GetActionID(res.Header) - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - newCluster, err := clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, newCluster.Name, cluster.Name+"-UPDATED") - - tools.PrintResource(t, newCluster) - - // Test cluster health - actionID, err = clusters.Check(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) -} - -func TestClustersResize(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - iTrue := true - resizeOpts := clusters.ResizeOpts{ - AdjustmentType: clusters.ChangeInCapacityAdjustment, - Number: 1, - Strict: &iTrue, - } - - actionID, err := clusters.Resize(context.TODO(), client, cluster.ID, resizeOpts).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - newCluster, err := clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, newCluster.DesiredCapacity, 2) - - tools.PrintResource(t, newCluster) -} - -func TestClustersScale(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - // increase cluster size to 2 - scaleOutOpts := clusters.ScaleOutOpts{ - Count: 1, - } - actionID, err := clusters.ScaleOut(context.TODO(), client, cluster.ID, scaleOutOpts).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - newCluster, err := clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, newCluster.DesiredCapacity, 2) - - // reduce cluster size to 0 - count := 2 - scaleInOpts := clusters.ScaleInOpts{ - Count: &count, - } - - actionID, err = clusters.ScaleIn(context.TODO(), client, cluster.ID, scaleInOpts).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - newCluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, newCluster.DesiredCapacity, 0) - - tools.PrintResource(t, newCluster) -} - -func TestClustersPolicies(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - client.Microversion = "1.5" - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - policy, err := CreatePolicy(t, client) - th.AssertNoErr(t, err) - defer DeletePolicy(t, client, policy.ID) - - iTrue := true - attachPolicyOpts := clusters.AttachPolicyOpts{ - PolicyID: policy.ID, - Enabled: &iTrue, - } - - actionID, err := clusters.AttachPolicy(context.TODO(), client, cluster.ID, attachPolicyOpts).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - // List all policies in the cluster to see if the policy was - // successfully attached. - allPages, err := clusters.ListPolicies(client, cluster.ID, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allPolicies, err := clusters.ExtractClusterPolicies(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, v := range allPolicies { - tools.PrintResource(t, v) - if v.PolicyID == policy.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - // Set the policy to disabled - iFalse := false - updatePolicyOpts := clusters.UpdatePolicyOpts{ - PolicyID: policy.ID, - Enabled: &iFalse, - } - - actionID, err = clusters.UpdatePolicy(context.TODO(), client, cluster.ID, updatePolicyOpts).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - clusterPolicy, err := clusters.GetPolicy(context.TODO(), client, cluster.ID, policy.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, clusterPolicy.Enabled, false) - - // Detach the policy - detachPolicyOpts := clusters.DetachPolicyOpts{ - PolicyID: policy.ID, - } - - actionID, err = clusters.DetachPolicy(context.TODO(), client, cluster.ID, detachPolicyOpts).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - // List all policies in the cluster to see if the policy was - // successfully detached. - allPages, err = clusters.ListPolicies(client, cluster.ID, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allPolicies, err = clusters.ExtractClusterPolicies(allPages) - th.AssertNoErr(t, err) - - found = false - for _, v := range allPolicies { - tools.PrintResource(t, v) - if v.PolicyID == policy.ID { - found = true - } - } - - th.AssertEquals(t, found, false) -} - -func TestClustersRecovery(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - recoverOpts := clusters.RecoverOpts{ - Operation: clusters.RebuildRecovery, - } - - actionID, err := clusters.Recover(context.TODO(), client, cluster.ID, recoverOpts).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - newCluster, err := clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, newCluster) -} - -func TestClustersAddNode(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - node1, err := CreateNode(t, client, "", profile.ID) - th.AssertNoErr(t, err) - // Even tho deleting the cluster will delete the nodes but only if added into cluster successfully. - defer DeleteNode(t, client, node1.ID) - - node2, err := CreateNode(t, client, "", profile.ID) - th.AssertNoErr(t, err) - // Even tho deleting the cluster will delete the nodes but only if added into cluster successfully. - defer DeleteNode(t, client, node2.ID) - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - - nodeIDs := []string{node1.ID, node2.ID} - nodeIDs = append(nodeIDs, cluster.Nodes...) - - nodeNames := []string{node1.Name, node2.Name} - addNodesOpts := clusters.AddNodesOpts{ - Nodes: nodeNames, - } - actionID, err := clusters.AddNodes(context.TODO(), client, cluster.ID, addNodesOpts).Extract() - if err != nil { - t.Fatalf("Unable to add nodes to cluster: %v", err) - } - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - - sort.Strings(nodeIDs) - sort.Strings(cluster.Nodes) - - tools.PrintResource(t, nodeIDs) - tools.PrintResource(t, cluster.Nodes) - - th.AssertDeepEquals(t, nodeIDs, cluster.Nodes) - - tools.PrintResource(t, cluster) -} - -func TestClustersRemoveNodeFromCluster(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - tools.PrintResource(t, cluster) - - opt := clusters.RemoveNodesOpts{Nodes: cluster.Nodes} - actionID, err := clusters.RemoveNodes(context.TODO(), client, cluster.ID, opt).Extract() - if err != nil { - t.Fatalf("Unable to remove nodes to cluster: %v", err) - } - - for _, n := range cluster.Nodes { - defer DeleteNode(t, client, n) - } - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, 0, len(cluster.Nodes)) - - tools.PrintResource(t, cluster) -} - -func TestClustersReplaceNode(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - client.Microversion = "1.3" - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - node1, err := CreateNode(t, client, "", profile.ID) - th.AssertNoErr(t, err) - defer DeleteNode(t, client, node1.ID) - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, true, len(cluster.Nodes) > 0) - for _, n := range cluster.Nodes { - defer DeleteNode(t, client, n) - } - - nodeIDToBeReplaced := cluster.Nodes[0] - opts := clusters.ReplaceNodesOpts{Nodes: map[string]string{nodeIDToBeReplaced: node1.ID}} - actionID, err := clusters.ReplaceNodes(context.TODO(), client, cluster.ID, opts).Extract() - th.AssertNoErr(t, err) - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - - clusterNodes := strings.Join(cluster.Nodes, ",") - th.AssertEquals(t, true, strings.Contains(clusterNodes, node1.ID)) - th.AssertEquals(t, false, strings.Contains(clusterNodes, nodeIDToBeReplaced)) - tools.PrintResource(t, cluster) -} - -func TestClustersCollectAttributes(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - client.Microversion = "1.2" - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, true, len(cluster.Nodes) > 0) - - _, err = CreateNode(t, client, cluster.ID, profile.ID) - th.AssertNoErr(t, err) - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, true, len(cluster.Nodes) > 0) - - for _, n := range cluster.Nodes { - defer DeleteNode(t, client, n) - } - - opts := clusters.CollectOpts{ - Path: "status", - } - attrs, err := clusters.Collect(context.TODO(), client, cluster.ID, opts).Extract() - th.AssertNoErr(t, err) - for _, attr := range attrs { - th.AssertEquals(t, attr.Value, "ACTIVE") - } - - opts = clusters.CollectOpts{ - Path: "data.placement.zone", - } - attrs, err = clusters.Collect(context.TODO(), client, cluster.ID, opts).Extract() - th.AssertNoErr(t, err) - for _, attr := range attrs { - th.AssertEquals(t, attr.Value, "nova") - } - -} - -// Performs an operation on a cluster -func TestClustersOps(t *testing.T) { - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - client.Microversion = "1.4" - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - node, err := CreateNode(t, client, cluster.ID, profile.ID) - th.AssertNoErr(t, err) - defer DeleteNode(t, client, node.ID) - - ops := []clusters.OperationOpts{ - // 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.LockOperation}, - {Operation: clusters.UnlockOperation}, - {Operation: clusters.SuspendOperation}, - {Operation: clusters.ResumeOperation}, - {Operation: clusters.RescueOperation, Params: clusters.OperationParams{"image_ref": choices.ImageID}}, - {Operation: clusters.PauseOperation}, - {Operation: clusters.UnpauseOperation}, - {Operation: clusters.StopOperation}, - {Operation: clusters.StartOperation}, - } - - for _, op := range ops { - opName := string(op.Operation) - t.Logf("Attempting to perform '%s' on cluster: %s", opName, cluster.ID) - actionID, res := clusters.Ops(context.TODO(), client, cluster.ID, op).Extract() - th.AssertNoErr(t, res) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - action, err := actions.Get(context.TODO(), client, actionID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, "SUCCEEDED", action.Status) - - t.Logf("Successfully performed '%s' on cluster: %s", opName, cluster.ID) - } - - cluster, err = clusters.Get(context.TODO(), client, cluster.ID).Extract() - th.AssertNoErr(t, err) - tools.PrintResource(t, cluster) -} diff --git a/internal/acceptance/openstack/clustering/v1/events_test.go b/internal/acceptance/openstack/clustering/v1/events_test.go deleted file mode 100644 index 90ba4038f1..0000000000 --- a/internal/acceptance/openstack/clustering/v1/events_test.go +++ /dev/null @@ -1,32 +0,0 @@ -//go:build acceptance || clustering || events - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/events" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestEventsList(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - opts := events.ListOpts{ - Limit: 200, - } - - allPages, err := events.List(client, opts).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allEvents, err := events.ExtractEvents(allPages) - th.AssertNoErr(t, err) - - for _, event := range allEvents { - tools.PrintResource(t, event) - } -} diff --git a/internal/acceptance/openstack/clustering/v1/nodes_test.go b/internal/acceptance/openstack/clustering/v1/nodes_test.go deleted file mode 100644 index 18b510b8b4..0000000000 --- a/internal/acceptance/openstack/clustering/v1/nodes_test.go +++ /dev/null @@ -1,216 +0,0 @@ -//go:build acceptance || clustering || policies - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestNodesCRUD(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - node, err := CreateNode(t, client, cluster.ID, profile.ID) - th.AssertNoErr(t, err) - defer DeleteNode(t, client, node.ID) - - // Test nodes list - allPages, err := nodes.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allNodes, err := nodes.ExtractNodes(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, v := range allNodes { - if v.ID == node.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - // Test nodes update - t.Logf("Attempting to update node %s", node.ID) - - updateOpts := nodes.UpdateOpts{ - Metadata: map[string]interface{}{ - "bar": "baz", - }, - } - - res := nodes.Update(context.TODO(), client, node.ID, updateOpts) - th.AssertNoErr(t, res.Err) - - actionID, err := GetActionID(res.Header) - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - node, err = nodes.Get(context.TODO(), client, node.ID).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, node) - tools.PrintResource(t, node.Metadata) -} - -// Performs an operation on a node -func TestNodesOps(t *testing.T) { - choices, err := clients.AcceptanceTestChoicesFromEnv() - th.AssertNoErr(t, err) - - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - client.Microversion = "1.4" - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - node, err := CreateNode(t, client, cluster.ID, profile.ID) - th.AssertNoErr(t, err) - defer DeleteNode(t, client, node.ID) - - ops := []nodes.OperationOpts{ - // 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.LockOperation}, - {Operation: nodes.UnlockOperation}, - {Operation: nodes.SuspendOperation}, - {Operation: nodes.ResumeOperation}, - {Operation: nodes.RescueOperation, Params: nodes.OperationParams{"image_ref": choices.ImageID}}, - {Operation: nodes.PauseOperation}, - {Operation: nodes.UnpauseOperation}, - {Operation: nodes.StopOperation}, - {Operation: nodes.StartOperation}, - } - - for _, op := range ops { - opName := string(op.Operation) - t.Logf("Attempting to perform '%s' on node: %s", opName, node.ID) - actionID, res := nodes.Ops(context.TODO(), client, node.ID, op).Extract() - th.AssertNoErr(t, res) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - node, err = nodes.Get(context.TODO(), client, node.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, "Operation '"+opName+"' succeeded", node.StatusReason) - t.Logf("Successfully performed '%s' on node: %s", opName, node.ID) - } -} - -func TestNodesRecover(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - client.Microversion = "1.6" - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - node, err := CreateNode(t, client, cluster.ID, profile.ID) - th.AssertNoErr(t, err) - defer DeleteNode(t, client, node.ID) - - checkTrue := true - checkFalse := false - - // TODO: nodes.RebuildRecovery is commented out as of 12/14/2018 the API backend can't perform the action without returning error - ops := []nodes.RecoverOpts{ - // Microversion < 1.6 legacy support where argument DOES NOT support Check - {}, - {Operation: nodes.RebootRecovery}, - // nodes.RecoverOpts{Operation: nodes.RebuildRecovery}, - - // MicroVersion >= 1.6 that supports Check where Check is true - {Check: &checkTrue}, - {Operation: nodes.RebootRecovery, Check: &checkTrue}, - //nodes.RecoverOpts{Operation: nodes.RebuildRecovery, Check: &checkTrue}, - - // MicroVersion >= 1.6 that supports Check where Check is false - {Check: &checkFalse}, - {Operation: nodes.RebootRecovery, Check: &checkFalse}, - //nodes.RecoverOpts{Operation: nodes.RebuildRecovery, Check: &checkFalse}, - } - - for _, recoverOpt := range ops { - if recoverOpt.Check != nil { - t.Logf("Attempting to recover by using '%s' check=%t on node: %s", recoverOpt.Operation, *recoverOpt.Check, node.ID) - } else { - t.Logf("Attempting to recover by using '%s' on node: %s", recoverOpt.Operation, node.ID) - } - - actionID, err := nodes.Recover(context.TODO(), client, node.ID, recoverOpt).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - if recoverOpt.Check != nil { - t.Logf("Successfully recovered by using '%s' check=%t on node: %s", recoverOpt.Operation, *recoverOpt.Check, node.ID) - } else { - t.Logf("Successfully recovered by using '%s' on node: %s", recoverOpt.Operation, node.ID) - } - - node, err := nodes.Get(context.TODO(), client, node.ID).Extract() - th.AssertNoErr(t, err) - tools.PrintResource(t, node) - } -} - -func TestNodeCheck(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - node, err := CreateNode(t, client, cluster.ID, profile.ID) - th.AssertNoErr(t, err) - defer DeleteNode(t, client, node.ID) - - t.Logf("Attempting to check on node: %s", node.ID) - - actionID, err := nodes.Check(context.TODO(), client, node.ID).Extract() - th.AssertNoErr(t, err) - - err = WaitForAction(client, actionID) - th.AssertNoErr(t, err) - - node, err = nodes.Get(context.TODO(), client, node.ID).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, "Check: Node is ACTIVE.", node.StatusReason) - tools.PrintResource(t, node) -} diff --git a/internal/acceptance/openstack/clustering/v1/pkg.go b/internal/acceptance/openstack/clustering/v1/pkg.go deleted file mode 100644 index 159aed7e11..0000000000 --- a/internal/acceptance/openstack/clustering/v1/pkg.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:build acceptance || clustering - -// The v1 package contains acceptance tests for the Openstack Clustering v1 service. - -package v1 diff --git a/internal/acceptance/openstack/clustering/v1/policies_test.go b/internal/acceptance/openstack/clustering/v1/policies_test.go deleted file mode 100644 index db696f7a74..0000000000 --- a/internal/acceptance/openstack/clustering/v1/policies_test.go +++ /dev/null @@ -1,70 +0,0 @@ -//go:build acceptance || clustering || policies - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestPoliciesCRUD(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - client.Microversion = "1.5" - - policy, err := CreatePolicy(t, client) - th.AssertNoErr(t, err) - defer DeletePolicy(t, client, policy.ID) - - // Test listing policies - allPages, err := policies.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allPolicies, err := policies.ExtractPolicies(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, v := range allPolicies { - if v.ID == policy.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - // Test Get policy - getPolicy, err := policies.Get(context.TODO(), client, policy.ID).Extract() - th.AssertNoErr(t, err) - tools.PrintResource(t, getPolicy) - - // Test updating policy - updateOpts := policies.UpdateOpts{ - Name: policy.Name + "-UPDATE", - } - - t.Logf("Attempting to update policy: %s", policy.ID) - updatePolicy, err := policies.Update(context.TODO(), client, policy.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, updatePolicy) - tools.PrintResource(t, updatePolicy.UpdatedAt) - - // Test validating policy - t.Logf("Attempting to validate policy: %s", policy.ID) - validateOpts := policies.ValidateOpts{ - Spec: TestPolicySpec, - } - - validatePolicy, err := policies.Validate(context.TODO(), client, validateOpts).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, validatePolicy) - - th.AssertEquals(t, validatePolicy.Name, "validated_policy") - th.AssertEquals(t, validatePolicy.Spec.Version, TestPolicySpec.Version) -} diff --git a/internal/acceptance/openstack/clustering/v1/policytypes_test.go b/internal/acceptance/openstack/clustering/v1/policytypes_test.go deleted file mode 100644 index 3906e70e48..0000000000 --- a/internal/acceptance/openstack/clustering/v1/policytypes_test.go +++ /dev/null @@ -1,65 +0,0 @@ -//go:build acceptance || clustering || policytypes - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policytypes" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestPolicyTypeList(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - allPages, err := policytypes.List(client).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allPolicyTypes, err := policytypes.ExtractPolicyTypes(allPages) - th.AssertNoErr(t, err) - - for _, v := range allPolicyTypes { - tools.PrintResource(t, v) - } -} - -func TestPolicyTypeList_v_1_5(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - client.Microversion = "1.5" - allPages, err := policytypes.List(client).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allPolicyTypes, err := policytypes.ExtractPolicyTypes(allPages) - th.AssertNoErr(t, err) - - for _, v := range allPolicyTypes { - tools.PrintResource(t, v) - } -} - -func TestPolicyTypeGet(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - policyType, err := policytypes.Get(context.TODO(), client, "senlin.policy.batch-1.0").Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, policyType) -} - -func TestPolicyTypeGet_v_1_5(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - client.Microversion = "1.5" - policyType, err := policytypes.Get(context.TODO(), client, "senlin.policy.batch-1.0").Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, policyType) -} diff --git a/internal/acceptance/openstack/clustering/v1/profiles_test.go b/internal/acceptance/openstack/clustering/v1/profiles_test.go deleted file mode 100644 index a9e6e79635..0000000000 --- a/internal/acceptance/openstack/clustering/v1/profiles_test.go +++ /dev/null @@ -1,77 +0,0 @@ -//go:build acceptance || clustering || policies - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestProfilesCRUD(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - // Test listing profiles - allPages, err := profiles.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allProfiles, err := profiles.ExtractProfiles(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, v := range allProfiles { - if v.ID == profile.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - // Test updating profile - updateOpts := profiles.UpdateOpts{ - Name: profile.Name + "-UPDATED", - } - - newProfile, err := profiles.Update(context.TODO(), client, profile.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, newProfile.Name, profile.Name+"-UPDATED") - - tools.PrintResource(t, newProfile) - tools.PrintResource(t, newProfile.UpdatedAt) -} - -func TestProfileValidate(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - client.Microversion = "1.2" - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - opts := profiles.ValidateOpts{ - Spec: profile.Spec, - } - validatedProfile, err := profiles.Validate(context.TODO(), client, opts).Extract() - th.AssertNoErr(t, err) - - // Do not validate the following fields for AssertDeepEquals() because the actual fields are either missing or hardcoded. - profile.CreatedAt = validatedProfile.CreatedAt - profile.Domain = validatedProfile.Domain - profile.ID = validatedProfile.ID - profile.Metadata = validatedProfile.Metadata - profile.Name = "validated_profile" - profile.UpdatedAt = validatedProfile.UpdatedAt - - th.AssertDeepEquals(t, validatedProfile, profile) - tools.PrintResource(t, validatedProfile) -} diff --git a/internal/acceptance/openstack/clustering/v1/profiletypes_test.go b/internal/acceptance/openstack/clustering/v1/profiletypes_test.go deleted file mode 100644 index 0ca53748d1..0000000000 --- a/internal/acceptance/openstack/clustering/v1/profiletypes_test.go +++ /dev/null @@ -1,48 +0,0 @@ -//go:build acceptance || clustering || profiletypes - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiletypes" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestProfileTypesList(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - client.Microversion = "1.5" - - allPages, err := profiletypes.List(client).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allProfileTypes, err := profiletypes.ExtractProfileTypes(allPages) - th.AssertNoErr(t, err) - - for _, profileType := range allProfileTypes { - tools.PrintResource(t, profileType) - } -} -func TestProfileTypesOpsList(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - client.Microversion = "1.5" - - profileTypeName := "os.nova.server-1.0" - allPages, err := profiletypes.ListOps(client, profileTypeName).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - ops, err := profiletypes.ExtractOps(allPages) - th.AssertNoErr(t, err) - - for k, v := range ops { - tools.PrintResource(t, k) - tools.PrintResource(t, v) - } -} diff --git a/internal/acceptance/openstack/clustering/v1/receivers_test.go b/internal/acceptance/openstack/clustering/v1/receivers_test.go deleted file mode 100644 index 2cf5f40b2c..0000000000 --- a/internal/acceptance/openstack/clustering/v1/receivers_test.go +++ /dev/null @@ -1,83 +0,0 @@ -//go:build acceptance || clustering || policies - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestReceiversCRUD(t *testing.T) { - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - receiver, err := CreateWebhookReceiver(t, client, cluster.ID) - th.AssertNoErr(t, err) - defer DeleteReceiver(t, client, receiver.ID) - - // Test listing receivers - allPages, err := receivers.List(client, nil).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allReceivers, err := receivers.ExtractReceivers(allPages) - th.AssertNoErr(t, err) - - var found bool - for _, v := range allReceivers { - if v.ID == receiver.ID { - found = true - } - } - - th.AssertEquals(t, found, true) - - // Test updating receivers - newName := receiver.Name + "-UPDATED" - updateOpts := receivers.UpdateOpts{ - Name: newName, - } - - receiver, err = receivers.Update(context.TODO(), client, receiver.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - - tools.PrintResource(t, receiver) - tools.PrintResource(t, receiver.UpdatedAt) - - th.AssertEquals(t, receiver.Name, newName) -} - -func TestReceiversNotify(t *testing.T) { - t.Parallel() - client, err := clients.NewClusteringV1Client() - th.AssertNoErr(t, err) - - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - receiver, err := CreateMessageReceiver(t, client, cluster.ID) - th.AssertNoErr(t, err) - defer DeleteReceiver(t, client, receiver.ID) - t.Logf("Created Mesage Receiver Name:[%s] Message Receiver ID:[%s]", receiver.Name, receiver.ID) - - requestID, err := receivers.Notify(context.TODO(), client, receiver.ID).Extract() - th.AssertNoErr(t, err) - t.Logf("Receiver Notify Service Request ID: %s", requestID) -} diff --git a/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go b/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go deleted file mode 100644 index 6d7345c0f1..0000000000 --- a/internal/acceptance/openstack/clustering/v1/webhooktrigger_test.go +++ /dev/null @@ -1,65 +0,0 @@ -//go:build acceptance || clustering || webhooks - -package v1 - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/webhooks" - th "github.com/gophercloud/gophercloud/v2/testhelper" -) - -func TestClusteringWebhookTrigger(t *testing.T) { - - client, err := clients.NewClusteringV1Client() - if err != nil { - t.Fatalf("Unable to create clustering client: %v", err) - } - - opts := webhooks.TriggerOpts{ - V: "1", - } - - // create profile, cluster and receiver first - profile, err := CreateProfile(t, client) - th.AssertNoErr(t, err) - defer DeleteProfile(t, client, profile.ID) - - cluster, err := CreateCluster(t, client, profile.ID) - th.AssertNoErr(t, err) - defer DeleteCluster(t, client, cluster.ID) - - receiver, err := CreateWebhookReceiver(t, client, cluster.ID) - th.AssertNoErr(t, err) - defer DeleteReceiver(t, client, receiver.ID) - - // trigger webhook - actionID, err := webhooks.Trigger(context.TODO(), client, receiver.ID, opts).Extract() - if err != nil { - t.Fatalf("Unable to extract webhooks trigger: %v", err) - } else { - t.Logf("Webhook trigger action id %s", actionID) - } - - err = WaitForAction(client, actionID) - if err != nil { - t.Fatalf("Error scaling out cluster %s as a result from webhook trigger: %s:", cluster.ID, err) - } - - // check that new node was created - nodelistopts := nodes.ListOpts{ - ClusterID: cluster.ID, - } - - allPages, err := nodes.List(client, nodelistopts).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allNodes, err := nodes.ExtractNodes(allPages) - th.AssertNoErr(t, err) - - // there should be 2 nodes in the cluster after triggering webhook - th.AssertEquals(t, len(allNodes), 2) -} diff --git a/openstack/client.go b/openstack/client.go index 6fe0b3f215..5bc626781b 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -454,12 +454,6 @@ func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi return sc, err } -// NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering -// package. -func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "clustering") -} - // NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging // service. func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { diff --git a/openstack/clustering/v1/actions/doc.go b/openstack/clustering/v1/actions/doc.go deleted file mode 100644 index 34d2fb7034..0000000000 --- a/openstack/clustering/v1/actions/doc.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Package actions provides listing and retrieving of senlin actions for the -OpenStack Clustering Service. - -Example to List Actions - - opts := actions.ListOpts{ - Limit: 5, - } - - err = actions.List(serviceClient, opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - actionInfos, err := actions.ExtractActions(page) - if err != nil { - return false, err - } - - for _, actionInfo := range actionInfos { - fmt.Printf("%+v\n", actionInfo) - } - return true, nil - }) - -Example to Get an Action - - actionID := "edce3528-864f-41fb-8759-f4707925cc09" - action, err := actions.Get(context.TODO(), serviceClient, actionID).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("Action %+v: ", action) -*/ -package actions diff --git a/openstack/clustering/v1/actions/requests.go b/openstack/clustering/v1/actions/requests.go deleted file mode 100644 index 7165f63978..0000000000 --- a/openstack/clustering/v1/actions/requests.go +++ /dev/null @@ -1,54 +0,0 @@ -package actions - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToActionListQuery() (string, error) -} - -// ListOpts represents options used to list actions. -type ListOpts struct { - Limit int `q:"limit"` - Marker string `q:"marker"` - Sort string `q:"sort"` - GlobalProject *bool `q:"global_project"` - Name string `q:"name"` - Target string `q:"target"` - Action string `q:"action"` - Status string `q:"status"` -} - -// ToClusterListQuery builds a query string from ListOpts. -func (opts ListOpts) ToActionListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to provide a list of actions. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToActionListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ActionPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Get retrieves details of a single action. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/actions/results.go b/openstack/clustering/v1/actions/results.go deleted file mode 100644 index 59c1b8aa6a..0000000000 --- a/openstack/clustering/v1/actions/results.go +++ /dev/null @@ -1,110 +0,0 @@ -package actions - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Action represents a detailed Action. -type Action struct { - Action string `json:"action"` - Cause string `json:"cause"` - CreatedAt time.Time `json:"-"` - Data map[string]interface{} `json:"data"` - DependedBy []string `json:"depended_by"` - DependsOn []string `json:"depends_on"` - StartTime float64 `json:"start_time"` - EndTime float64 `json:"end_time"` - ID string `json:"id"` - Inputs map[string]interface{} `json:"inputs"` - Interval int `json:"interval"` - Name string `json:"name"` - Outputs map[string]interface{} `json:"outputs"` - Owner string `json:"owner"` - Project string `json:"project"` - Status string `json:"status"` - StatusReason string `json:"status_reason"` - Target string `json:"target"` - Timeout int `json:"timeout"` - UpdatedAt time.Time `json:"-"` - User string `json:"user"` -} - -func (r *Action) UnmarshalJSON(b []byte) error { - type tmp Action - var s struct { - tmp - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - } - - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - - *r = Action(s.tmp) - - if s.CreatedAt != "" { - r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) - if err != nil { - return err - } - } - - if s.UpdatedAt != "" { - r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) - if err != nil { - return err - } - } - - return nil -} - -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// Extract interprets any commonResult-based result as an Action. -func (r commonResult) Extract() (*Action, error) { - var s struct { - Action *Action `json:"action"` - } - err := r.ExtractInto(&s) - return s.Action, err -} - -// GetResult is the response of a Get operations. Call its Extract method to -// interpret it as an Action. -type GetResult struct { - commonResult -} - -// ActionPage contains a single page of all actions from a List call. -type ActionPage struct { - pagination.LinkedPageBase -} - -// 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 -} - -// ExtractActions returns a slice of Actions from the List operation. -func ExtractActions(r pagination.Page) ([]Action, error) { - var s struct { - Actions []Action `json:"actions"` - } - err := (r.(ActionPage)).ExtractInto(&s) - return s.Actions, err -} diff --git a/openstack/clustering/v1/actions/testing/doc.go b/openstack/clustering/v1/actions/testing/doc.go deleted file mode 100644 index ffecf4d863..0000000000 --- a/openstack/clustering/v1/actions/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_actions_v1 -package testing diff --git a/openstack/clustering/v1/actions/testing/fixtures_test.go b/openstack/clustering/v1/actions/testing/fixtures_test.go deleted file mode 100644 index 8f178963cb..0000000000 --- a/openstack/clustering/v1/actions/testing/fixtures_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const ListResponse = ` -{ - "actions": [ - { - "action": "NODE_DELETE", - "cause": "RPC Request", - "created_at": "2015-11-04T05:21:41Z", - "data": {}, - "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "end_time": 1425550000.0, - "id": "edce3528-864f-41fb-8759-f4707925cc09", - "inputs": {}, - "interval": -1, - "name": "node_delete_f0de9b9c", - "outputs": {}, - "owner": null, - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "start_time": 1425550000.0, - "status": "SUCCEEDED", - "status_reason": "Action completed successfully.", - "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - "timeout": 3600, - "updated_at": "2016-11-04T05:21:41Z", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - }, - { - "action": "NODE_DELETE", - "cause": "RPC Request", - "created_at": null, - "data": {}, - "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "end_time": 1425550000.0, - "id": "edce3528-864f-41fb-8759-f4707925cc09", - "inputs": {}, - "interval": -1, - "name": "node_delete_f0de9b9c", - "outputs": {}, - "owner": null, - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "start_time": 1425550000.0, - "status": "SUCCEEDED", - "status_reason": "Action completed successfully.", - "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - "timeout": 3600, - "updated_at": "", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - } - ] -} -` - -const GetResponse = ` -{ - "action": { - "action": "NODE_DELETE", - "cause": "RPC Request", - "created_at": "2015-11-04T05:21:41Z", - "data": {}, - "depended_by": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "depends_on": ["ef67fe80-6547-40f2-ba1b-83e950aa38df"], - "end_time": 1425550000.0, - "id": "edce3528-864f-41fb-8759-f4707925cc09", - "inputs": {}, - "interval": -1, - "name": "node_delete_f0de9b9c", - "outputs": {}, - "owner": null, - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "start_time": 1425550000.0, - "status": "SUCCEEDED", - "status_reason": "Action completed successfully.", - "target": "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - "timeout": 3600, - "updated_at": "2016-11-04T05:21:41Z", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - } -} -` - -var ExpectedAction1 = actions.Action{ - Action: "NODE_DELETE", - Cause: "RPC Request", - CreatedAt: time.Date(2015, 11, 4, 5, 21, 41, 0, time.UTC), - Data: map[string]interface{}{}, - DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - EndTime: 1425550000.0, - ID: "edce3528-864f-41fb-8759-f4707925cc09", - Inputs: make(map[string]interface{}), - Interval: -1, - Name: "node_delete_f0de9b9c", - Outputs: make(map[string]interface{}), - Owner: "", - Project: "f1fe61dcda2f4618a14c10dc7abc214d", - StartTime: 1425550000.0, - Status: "SUCCEEDED", - StatusReason: "Action completed successfully.", - Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - Timeout: 3600, - UpdatedAt: time.Date(2016, 11, 4, 5, 21, 41, 0, time.UTC), - User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", -} - -var ExpectedAction2 = actions.Action{ - Action: "NODE_DELETE", - Cause: "RPC Request", - CreatedAt: time.Time{}, - Data: map[string]interface{}{}, - DependedBy: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - DependsOn: []string{"ef67fe80-6547-40f2-ba1b-83e950aa38df"}, - EndTime: 1425550000.0, - ID: "edce3528-864f-41fb-8759-f4707925cc09", - Inputs: make(map[string]interface{}), - Interval: -1, - Name: "node_delete_f0de9b9c", - Outputs: make(map[string]interface{}), - Owner: "", - Project: "f1fe61dcda2f4618a14c10dc7abc214d", - StartTime: 1425550000.0, - Status: "SUCCEEDED", - StatusReason: "Action completed successfully.", - Target: "f0de9b9c-6d48-4a46-af21-2ca8607777fe", - Timeout: 3600, - UpdatedAt: time.Time{}, - User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", -} - -var ExpectedActions = []actions.Action{ExpectedAction1, ExpectedAction2} - -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/actions", 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, ListResponse) - }) -} - -func HandleGetSuccessfully(t *testing.T, id string) { - th.Mux.HandleFunc("/v1/actions/"+id, 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, GetResponse) - }) -} diff --git a/openstack/clustering/v1/actions/testing/requests_test.go b/openstack/clustering/v1/actions/testing/requests_test.go deleted file mode 100644 index 0fa6ca0e07..0000000000 --- a/openstack/clustering/v1/actions/testing/requests_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/actions" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestListActions(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListSuccessfully(t) - - pageCount := 0 - err := actions.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pageCount++ - actual, err := actions.ExtractActions(page) - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, ExpectedActions, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - - if pageCount != 1 { - t.Errorf("Expected 1 page, got %d", pageCount) - } -} - -func TestGetAction(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetSuccessfully(t, ExpectedAction1.ID) - - actual, err := actions.Get(context.TODO(), fake.ServiceClient(), ExpectedAction1.ID).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedAction1, *actual) -} diff --git a/openstack/clustering/v1/actions/urls.go b/openstack/clustering/v1/actions/urls.go deleted file mode 100644 index 25a739c41e..0000000000 --- a/openstack/clustering/v1/actions/urls.go +++ /dev/null @@ -1,22 +0,0 @@ -package actions - -import "github.com/gophercloud/gophercloud/v2" - -var apiVersion = "v1" -var apiName = "actions" - -func commonURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func listURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func idURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id) -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} diff --git a/openstack/clustering/v1/clusters/doc.go b/openstack/clustering/v1/clusters/doc.go deleted file mode 100644 index 067c35aefe..0000000000 --- a/openstack/clustering/v1/clusters/doc.go +++ /dev/null @@ -1,286 +0,0 @@ -/* -Package clusters provides information and interaction with the clusters through -the OpenStack Clustering service. - -Example to Create a Cluster - - createOpts := clusters.CreateOpts{ - Name: "test-cluster", - DesiredCapacity: 1, - ProfileID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", - } - - cluster, err := clusters.Create(context.TODO(), serviceClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Get a Cluster - - clusterName := "cluster123" - cluster, err := clusters.Get(context.TODO(), serviceClient, clusterName).Extract() - if err != nil { - panic(err) - } - fmt.Printf("%+v\n", cluster) - -Example to List Clusters - - listOpts := clusters.ListOpts{ - Name: "testcluster", - } - - allPages, err := clusters.List(serviceClient, listOpts).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allClusters, err := clusters.ExtractClusters(allPages) - if err != nil { - panic(err) - } - - for _, cluster := range allClusters { - fmt.Printf("%+v\n", cluster) - } - -Example to Update a Cluster - - updateOpts := clusters.UpdateOpts{ - Name: "testcluster", - ProfileID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2da", - } - - clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" - cluster, err := clusters.Update(context.TODO(), serviceClient, clusterName, opts).Extract() - if err != nil { - panic(err) - } - fmt.Printf("%+v\n", cluster) - -Example to Delete a Cluster - - clusterID := "dc6d336e3fc4c0a951b5698cd1236ee" - err := clusters.Delete(context.TODO(), serviceClient, clusterID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Resize a Cluster - - number := 1 - maxSize := 5 - minSize := 1 - minStep := 1 - strict := true - - resizeOpts := clusters.ResizeOpts{ - AdjustmentType: clusters.ChangeInCapacityAdjustment, - Number: number, - MaxSize: &maxSize, - MinSize: &minSize, - MinStep: &minStep, - Strict: &strict, - } - - actionID, err := clusters.Resize(context.TODO(), client, clusterName, resizeOpts).Extract() - if err != nil { - t.Fatalf("Unable to resize cluster: %v", err) - } - fmt.Println("Resize actionID", actionID) - -Example to ScaleIn a Cluster - - count := 2 - scaleInOpts := clusters.ScaleInOpts{ - Count: &count, - } - clusterID: "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - - action, err := clusters.ScaleIn(context.TODO(), computeClient, clusterID, scaleInOpts).Extract() - if err != nil { - panic(err) - } - -Example to ScaleOut a cluster - - scaleOutOpts := clusters.ScaleOutOpts{ - Count: 2, - } - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - - actionID, err := clusters.ScaleOut(context.TODO(), computeClient, clusterID, scaleOutOpts).Extract() - if err != nil { - panic(err) - } - -Example to List Policies for a Cluster - - clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" - allPages, err := clusters.ListPolicies(serviceClient, clusterID, nil).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allClusterPolicies, err := clusters.ExtractClusterPolicies(allPages) - if err != nil { - panic(err) - } - - for _, clusterPolicy := range allClusterPolicies { - fmt.Printf("%+v\n", clusterPolicy) - } - -Example to Get a Cluster Policy - - clusterID := "7d85f602-a948-4a30-afd4-e84f47471c15" - profileID := "714fe676-a08f-4196-b7af-61d52eeded15" - clusterPolicy, err := clusterpolicies.Get(serviceCLient, clusterID, profileID).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", clusterPolicy) - -Example to Attach a Policy to a Cluster - - enabled := true - attachPolicyOpts := clusters.AttachPolicyOpts{ - PolicyID: "policy-123", - Enabled: &enabled, - } - - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.AttachPolicy(context.TODO(), serviceClient, clusterID, attachPolicyOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println("Attach Policy actionID", actionID) - -Example to Detach a Policy to Cluster - - detachpolicyOpts := clusters.DetachPolicyOpts{ - PolicyID: "policy-123", - } - - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.DetachPolicy(context.TODO(), serviceClient, clusterID, detachpolicyOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println("Update Policy actionID", actionID) - -Example to Update a Policy to a Cluster - - enabled := true - updatePolicyOpts := clusters.UpdatePolicyOpts{ - PolicyID: "policy-123", - Enabled: &enabled, - } - - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.UpdatePolicy(context.TODO(), serviceClient, clusterID, updatePolicyOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println("Attach Policy actionID", actionID) - -Example to Recover a Cluster - - check := true - checkCapacity := true - recoverOpts := clusters.RecoverOpts{ - Operation: clusters.RebuildRecovery, - Check: &check, - CheckCapacity: &checkCapacity, - } - - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.Recover(context.TODO(), computeClient, clusterID, recoverOpts).Extract() - if err != nil { - panic(err) - } - fmt.Println("action=", actionID) - -Example to Check a Cluster - - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - action, err := clusters.Check(context.TODO(), computeClient, clusterID).Extract() - if err != nil { - panic(err) - } - -Example to Complete Life Cycle - - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - lifecycleOpts := clusters.CompleteLifecycleOpts{LifecycleActionTokenID: "2b827124-69e1-496e-9484-33ca769fe4df"} - - action, err := clusters.CompleteLifecycle(context.TODO(), computeClient, clusterID, lifecycleOpts).Extract() - if err != nil { - panic(err) - } - -Example to add nodes to a cluster - - addNodesOpts := clusters.AddNodesOpts{ - Nodes: []string{"node-123"}, - } - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.AddNodes(context.TODO(), serviceClient, clusterID, addNodesOpts).Extract() - if err != nil { - panic(err) - } - fmt.Println("action=", actionID) - -Example to remove nodes from a cluster - - removeNodesOpts := clusters.RemoveNodesOpts{ - Nodes: []string{"node-123"}, - } - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - err := clusters.RemoveNodes(context.TODO(), serviceClient, clusterID, removeNodesOpts).ExtractErr() - if err != nil { - panic(err) - } - -Example to replace nodes for a cluster - - replaceNodesOpts := clusters.ReplaceNodesOpts{ - Nodes: map[string]string{"node-1234": "node-5678"}, - } - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := clusters.ReplaceNodes(context.TODO(), serviceClient, clusterID, replaceNodesOpts).Extract() - if err != nil { - panic(err) - } - -Example to collect node attributes across a cluster - - serviceClient.Microversion = "1.2" - clusterID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - opts := clusters.CollectOpts{ - Path: "status", - } - attrs, err := clusters.Collect(context.TODO(), serviceClient, clusterID, opts).Extract() - if err != nil { - panic(err) - } - -Example to perform an operation on a cluster - - serviceClient.Microversion = "1.4" - clusterID := "cluster123" - operationOpts := clusters.OperationOpts{ - Operation: clusters.RebootOperation, - Filters: clusters.OperationFilters{"role": "slave"}, - Params: clusters.OperationParams{"type": "SOFT"}, - } - actionID, err := clusters.Ops(context.TODO(), serviceClient, clusterID, operationOpts).Extract() - if err != nil { - panic(err) - } -*/ -package clusters diff --git a/openstack/clustering/v1/clusters/requests.go b/openstack/clustering/v1/clusters/requests.go deleted file mode 100644 index 56ac847345..0000000000 --- a/openstack/clustering/v1/clusters/requests.go +++ /dev/null @@ -1,625 +0,0 @@ -package clusters - -import ( - "context" - "fmt" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// AdjustmentType represents valid values for resizing a cluster. -type AdjustmentType string - -const ( - ExactCapacityAdjustment AdjustmentType = "EXACT_CAPACITY" - ChangeInCapacityAdjustment AdjustmentType = "CHANGE_IN_CAPACITY" - ChangeInPercentageAdjustment AdjustmentType = "CHANGE_IN_PERCENTAGE" -) - -// RecoveryAction represents valid values for recovering a cluster. -type RecoveryAction string - -const ( - RebootRecovery RecoveryAction = "REBOOT" - RebuildRecovery RecoveryAction = "REBUILD" - RecreateRecovery RecoveryAction = "RECREATE" -) - -// CreateOptsBuilder allows extensions to add additional parameters -// to the Create request. -type CreateOptsBuilder interface { - ToClusterCreateMap() (map[string]interface{}, error) -} - -// CreateOpts represents options used to create a cluster. -type CreateOpts struct { - Name string `json:"name" required:"true"` - DesiredCapacity int `json:"desired_capacity"` - ProfileID string `json:"profile_id" required:"true"` - MinSize *int `json:"min_size,omitempty"` - Timeout int `json:"timeout,omitempty"` - MaxSize int `json:"max_size,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty"` - Config map[string]interface{} `json:"config,omitempty"` -} - -// ToClusterCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "cluster") -} - -// Create requests the creation of a new cluster. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToClusterCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves details of a single cluster. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to -// the List request. -type ListOptsBuilder interface { - ToClusterListQuery() (string, error) -} - -// ListOpts represents options to list clusters. -type ListOpts struct { - Limit int `q:"limit"` - Marker string `q:"marker"` - Sort string `q:"sort"` - GlobalProject *bool `q:"global_project"` - Name string `q:"name,omitempty"` - Status string `q:"status,omitempty"` -} - -// ToClusterListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToClusterListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to provide a list of clusters. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToClusterListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ClusterPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToClusterUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts represents options to update a cluster. -type UpdateOpts struct { - Config string `json:"config,omitempty"` - Name string `json:"name,omitempty"` - ProfileID string `json:"profile_id,omitempty"` - Timeout *int `json:"timeout,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty"` - ProfileOnly *bool `json:"profile_only,omitempty"` -} - -// ToClusterUpdateMap assembles a request body based on the contents of -// UpdateOpts. -func (opts UpdateOpts) ToClusterUpdateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "cluster") - if err != nil { - return nil, err - } - return b, nil -} - -// Update will update an existing cluster. -func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToClusterUpdateMap() - if err != nil { - r.Err = err - return r - } - resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete deletes the specified cluster ID. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ResizeOptsBuilder allows extensions to add additional parameters to the -// resize request. -type ResizeOptsBuilder interface { - ToClusterResizeMap() (map[string]interface{}, error) -} - -// ResizeOpts represents options for resizing a cluster. -type ResizeOpts struct { - AdjustmentType AdjustmentType `json:"adjustment_type,omitempty"` - Number interface{} `json:"number,omitempty"` - MinSize *int `json:"min_size,omitempty"` - MaxSize *int `json:"max_size,omitempty"` - MinStep *int `json:"min_step,omitempty"` - Strict *bool `json:"strict,omitempty"` -} - -// ToClusterResizeMap constructs a request body from ResizeOpts. -func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { - if opts.AdjustmentType != "" && opts.Number == nil { - return nil, fmt.Errorf("Number field MUST NOT be empty when AdjustmentType field used") - } - - switch opts.Number.(type) { - case nil, int, int32, int64: - // Valid type. Always allow - case float32, float64: - if opts.AdjustmentType != ChangeInPercentageAdjustment { - return nil, fmt.Errorf("Only ChangeInPercentageAdjustment allows float value for Number field") - } - default: - return nil, fmt.Errorf("Number field must be either int, float, or omitted") - } - - return gophercloud.BuildRequestBody(opts, "resize") -} - -func Resize(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { - b, err := opts.ToClusterResizeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ScaleInOptsBuilder allows extensions to add additional parameters to the -// ScaleIn request. -type ScaleInOptsBuilder interface { - ToClusterScaleInMap() (map[string]interface{}, error) -} - -// ScaleInOpts represents options used to scale-in a cluster. -type ScaleInOpts struct { - Count *int `json:"count,omitempty"` -} - -// ToClusterScaleInMap constructs a request body from ScaleInOpts. -func (opts ScaleInOpts) ToClusterScaleInMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "scale_in") -} - -// ScaleIn will reduce the capacity of a cluster. -func ScaleIn(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ScaleInOptsBuilder) (r ActionResult) { - b, err := opts.ToClusterScaleInMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ScaleOutOptsBuilder allows extensions to add additional parameters to the -// ScaleOut request. -type ScaleOutOptsBuilder interface { - ToClusterScaleOutMap() (map[string]interface{}, error) -} - -// ScaleOutOpts represents options used to scale-out a cluster. -type ScaleOutOpts struct { - Count int `json:"count,omitempty"` -} - -// ToClusterScaleOutMap constructs a request body from ScaleOutOpts. -func (opts ScaleOutOpts) ToClusterScaleOutMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "scale_out") -} - -// ScaleOut will increase the capacity of a cluster. -func ScaleOut(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ScaleOutOptsBuilder) (r ActionResult) { - b, err := opts.ToClusterScaleOutMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// AttachPolicyOptsBuilder allows extensions to add additional parameters to the -// AttachPolicy request. -type AttachPolicyOptsBuilder interface { - ToClusterAttachPolicyMap() (map[string]interface{}, error) -} - -// PolicyOpts params -type AttachPolicyOpts struct { - PolicyID string `json:"policy_id" required:"true"` - Enabled *bool `json:"enabled,omitempty"` -} - -// ToClusterAttachPolicyMap constructs a request body from AttachPolicyOpts. -func (opts AttachPolicyOpts) ToClusterAttachPolicyMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "policy_attach") -} - -// Attach Policy will attach a policy to a cluster. -func AttachPolicy(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AttachPolicyOptsBuilder) (r ActionResult) { - b, err := opts.ToClusterAttachPolicyMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdatePolicyOptsBuilder allows extensions to add additional parameters to the -// UpdatePolicy request. -type UpdatePolicyOptsBuilder interface { - ToClusterUpdatePolicyMap() (map[string]interface{}, error) -} - -// UpdatePolicyOpts represents options used to update a cluster policy. -type UpdatePolicyOpts struct { - PolicyID string `json:"policy_id" required:"true"` - Enabled *bool `json:"enabled,omitempty" required:"true"` -} - -// ToClusterUpdatePolicyMap constructs a request body from UpdatePolicyOpts. -func (opts UpdatePolicyOpts) ToClusterUpdatePolicyMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "policy_update") -} - -// UpdatePolicy will update a cluster's policy. -func UpdatePolicy(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdatePolicyOptsBuilder) (r ActionResult) { - b, err := opts.ToClusterUpdatePolicyMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// DetachPolicyOptsBuilder allows extensions to add additional parameters to the -// DetachPolicy request. -type DetachPolicyOptsBuilder interface { - ToClusterDetachPolicyMap() (map[string]interface{}, error) -} - -// DetachPolicyOpts represents options used to detach a policy from a cluster. -type DetachPolicyOpts struct { - PolicyID string `json:"policy_id" required:"true"` -} - -// ToClusterDetachPolicyMap constructs a request body from DetachPolicyOpts. -func (opts DetachPolicyOpts) ToClusterDetachPolicyMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "policy_detach") -} - -// DetachPolicy will detach a policy from a cluster. -func DetachPolicy(ctx context.Context, client *gophercloud.ServiceClient, id string, opts DetachPolicyOptsBuilder) (r ActionResult) { - b, err := opts.ToClusterDetachPolicyMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListPolicyOptsBuilder allows extensions to add additional parameters to the -// ListPolicies request. -type ListPoliciesOptsBuilder interface { - ToClusterPoliciesListQuery() (string, error) -} - -// ListPoliciesOpts represents options to list a cluster's policies. -type ListPoliciesOpts struct { - Enabled *bool `q:"enabled"` - Name string `q:"policy_name"` - Type string `q:"policy_type"` - Sort string `q:"sort"` -} - -// ToClusterPoliciesListQuery formats a ListOpts into a query string. -func (opts ListPoliciesOpts) ToClusterPoliciesListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// ListPolicies instructs OpenStack to provide a list of policies for a cluster. -func ListPolicies(client *gophercloud.ServiceClient, clusterID string, opts ListPoliciesOptsBuilder) pagination.Pager { - url := listPoliciesURL(client, clusterID) - if opts != nil { - query, err := opts.ToClusterPoliciesListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ClusterPolicyPage{pagination.SinglePageBase(r)} - }) -} - -// GetPolicy retrieves details of a cluster policy. -func GetPolicy(ctx context.Context, client *gophercloud.ServiceClient, clusterID string, policyID string) (r GetPolicyResult) { - resp, err := client.Get(ctx, getPolicyURL(client, clusterID, policyID), &r.Body, nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// RecoverOptsBuilder allows extensions to add additional parameters to the -// Recover request. -type RecoverOptsBuilder interface { - ToClusterRecoverMap() (map[string]interface{}, error) -} - -// RecoverOpts represents options used to recover a cluster. -type RecoverOpts struct { - Operation RecoveryAction `json:"operation,omitempty"` - Check *bool `json:"check,omitempty"` - CheckCapacity *bool `json:"check_capacity,omitempty"` -} - -// ToClusterRecovermap constructs a request body from RecoverOpts. -func (opts RecoverOpts) ToClusterRecoverMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "recover") -} - -// Recover implements cluster recover request. -func Recover(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RecoverOptsBuilder) (r ActionResult) { - b, err := opts.ToClusterRecoverMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Check will perform a health check on a cluster. -func Check(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { - b := map[string]interface{}{ - "check": map[string]interface{}{}, - } - - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ToClusterCompleteLifecycleMap constructs a request body from CompleteLifecycleOpts. -func (opts CompleteLifecycleOpts) ToClusterCompleteLifecycleMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "complete_lifecycle") -} - -type CompleteLifecycleOpts struct { - LifecycleActionTokenID string `json:"lifecycle_action_token" required:"true"` -} - -func CompleteLifecycle(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CompleteLifecycleOpts) (r ActionResult) { - b, err := opts.ToClusterCompleteLifecycleMap() - if err != nil { - r.Err = err - return - } - - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -func (opts AddNodesOpts) ToClusterAddNodeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "add_nodes") -} - -type AddNodesOpts struct { - Nodes []string `json:"nodes" required:"true"` -} - -func AddNodes(ctx context.Context, client *gophercloud.ServiceClient, id string, opts AddNodesOpts) (r ActionResult) { - b, err := opts.ToClusterAddNodeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -func (opts RemoveNodesOpts) ToClusterRemoveNodeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "del_nodes") -} - -type RemoveNodesOpts struct { - Nodes []string `json:"nodes" required:"true"` -} - -func RemoveNodes(ctx context.Context, client *gophercloud.ServiceClient, clusterID string, opts RemoveNodesOpts) (r ActionResult) { - b, err := opts.ToClusterRemoveNodeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, nodeURL(client, clusterID), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -func (opts ReplaceNodesOpts) ToClusterReplaceNodeMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "replace_nodes") -} - -type ReplaceNodesOpts struct { - Nodes map[string]string `json:"nodes" required:"true"` -} - -func ReplaceNodes(ctx context.Context, client *gophercloud.ServiceClient, id string, opts ReplaceNodesOpts) (r ActionResult) { - b, err := opts.ToClusterReplaceNodeMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, nodeURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -type CollectOptsBuilder interface { - ToClusterCollectMap() (string, error) -} - -// CollectOpts represents options to collect attribute values across a cluster -type CollectOpts struct { - Path string `q:"path" required:"true"` -} - -func (opts CollectOpts) ToClusterCollectMap() (string, error) { - return opts.Path, nil -} - -// Collect instructs OpenStack to aggregate attribute values across a cluster -func Collect(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CollectOptsBuilder) (r CollectResult) { - query, err := opts.ToClusterCollectMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Get(ctx, collectURL(client, id, query), &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// OperationName represents valid values for cluster operation -type OperationName string - -const ( - // Nova Profile Op Names - RebootOperation OperationName = "reboot" - RebuildOperation OperationName = "rebuild" - ChangePasswordOperation OperationName = "change_password" - PauseOperation OperationName = "pause" - UnpauseOperation OperationName = "unpause" - SuspendOperation OperationName = "suspend" - ResumeOperation OperationName = "resume" - LockOperation OperationName = "lock" - UnlockOperation OperationName = "unlock" - StartOperation OperationName = "start" - StopOperation OperationName = "stop" - RescueOperation OperationName = "rescue" - UnrescueOperation OperationName = "unrescue" - EvacuateOperation OperationName = "evacuate" - - // Heat Pofile Op Names - AbandonOperation OperationName = "abandon" -) - -// ToClusterOperationMap constructs a request body from OperationOpts. -func (opts OperationOpts) ToClusterOperationMap() (map[string]interface{}, error) { - operationArg := struct { - Filters OperationFilters `json:"filters,omitempty"` - Params OperationParams `json:"params,omitempty"` - }{ - Filters: opts.Filters, - Params: opts.Params, - } - - return gophercloud.BuildRequestBody(operationArg, string(opts.Operation)) -} - -// OperationOptsBuilder allows extensions to add additional parameters to the -// Op request. -type OperationOptsBuilder interface { - ToClusterOperationMap() (map[string]interface{}, error) -} -type OperationFilters map[string]interface{} -type OperationParams map[string]interface{} - -// OperationOpts represents options used to perform an operation on a cluster -type OperationOpts struct { - Operation OperationName `json:"operation" required:"true"` - Filters OperationFilters `json:"filters,omitempty"` - Params OperationParams `json:"params,omitempty"` -} - -func Ops(ctx context.Context, client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { - b, err := opts.ToClusterOperationMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/clusters/results.go b/openstack/clustering/v1/clusters/results.go deleted file mode 100644 index f8a1b312fb..0000000000 --- a/openstack/clustering/v1/clusters/results.go +++ /dev/null @@ -1,227 +0,0 @@ -package clusters - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Cluster represents an OpenStack Clustering cluster. -type Cluster struct { - Config map[string]interface{} `json:"config"` - CreatedAt time.Time `json:"-"` - Data map[string]interface{} `json:"data"` - Dependents map[string]interface{} `json:"dependents"` - DesiredCapacity int `json:"desired_capacity"` - Domain string `json:"domain"` - ID string `json:"id"` - InitAt time.Time `json:"-"` - MaxSize int `json:"max_size"` - Metadata map[string]interface{} `json:"metadata"` - MinSize int `json:"min_size"` - Name string `json:"name"` - Nodes []string `json:"nodes"` - Policies []string `json:"policies"` - ProfileID string `json:"profile_id"` - ProfileName string `json:"profile_name"` - Project string `json:"project"` - Status string `json:"status"` - StatusReason string `json:"status_reason"` - Timeout int `json:"timeout"` - UpdatedAt time.Time `json:"-"` - User string `json:"user"` -} - -func (r *Cluster) UnmarshalJSON(b []byte) error { - type tmp Cluster - var s struct { - tmp - CreatedAt string `json:"created_at"` - InitAt string `json:"init_at"` - UpdatedAt string `json:"updated_at"` - } - - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Cluster(s.tmp) - - if s.CreatedAt != "" { - r.CreatedAt, err = time.Parse(gophercloud.RFC3339Milli, s.CreatedAt) - if err != nil { - return err - } - } - - if s.InitAt != "" { - r.InitAt, err = time.Parse(gophercloud.RFC3339Milli, s.InitAt) - if err != nil { - return err - } - } - - if s.UpdatedAt != "" { - r.UpdatedAt, err = time.Parse(gophercloud.RFC3339Milli, s.UpdatedAt) - if err != nil { - return err - } - } - - return nil -} - -// ClusterPolicy represents and OpenStack Clustering cluster policy. -type ClusterPolicy struct { - ClusterID string `json:"cluster_id"` - ClusterName string `json:"cluster_name"` - Enabled bool `json:"enabled"` - ID string `json:"id"` - PolicyID string `json:"policy_id"` - PolicyName string `json:"policy_name"` - PolicyType string `json:"policy_type"` -} - -type ClusterAttributes struct { - ID string `json:"id"` - Value interface{} `json:"value"` -} - -// Action represents an OpenStack Clustering action. -type Action struct { - Action string `json:"action"` -} - -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// Extract interprets any commonResult-based result as a Cluster. -func (r commonResult) Extract() (*Cluster, error) { - var s struct { - Cluster *Cluster `json:"cluster"` - } - - err := r.ExtractInto(&s) - return s.Cluster, err -} - -// CreateResult is the response of a Create operations. Call its Extract method -// to interpret it as a Cluster. -type CreateResult struct { - commonResult -} - -// GetResult is the response of a Get operations. Call its Extract method to -// interpret it as a Cluster. -type GetResult struct { - commonResult -} - -// UpdateResult is the response of a Update operations. Call its Extract method -// to interpret it as a Cluster. -type UpdateResult struct { - commonResult -} - -// GetPolicyResult is the response of a Get operations. Call its Extract method -// to interpret it as a ClusterPolicy. -type GetPolicyResult struct { - gophercloud.Result -} - -// Extract interprets a GetPolicyResult as a ClusterPolicy. -func (r GetPolicyResult) Extract() (*ClusterPolicy, error) { - var s struct { - ClusterPolicy *ClusterPolicy `json:"cluster_policy"` - } - err := r.ExtractInto(&s) - return s.ClusterPolicy, err -} - -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// ClusterPage contains a single page of all clusters from a List call. -type ClusterPage struct { - pagination.LinkedPageBase -} - -// 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 -} - -// ClusterPolicyPage contains a single page of all policies from a List call -type ClusterPolicyPage struct { - pagination.SinglePageBase -} - -// 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 -} - -// ActionResult is the response of Senlin actions. Call its Extract method to -// obtain the Action ID of the action. -type ActionResult struct { - gophercloud.Result -} - -// Extract interprets any Action result as an Action. -func (r ActionResult) Extract() (string, error) { - var s struct { - Action string `json:"action"` - } - err := r.ExtractInto(&s) - return s.Action, err -} - -type CollectResult struct { - gophercloud.Result -} - -// ExtractClusters returns a slice of Clusters from the List operation. -func ExtractClusters(r pagination.Page) ([]Cluster, error) { - var s struct { - Clusters []Cluster `json:"clusters"` - } - err := (r.(ClusterPage)).ExtractInto(&s) - return s.Clusters, err -} - -// ExtractClusterPolicies returns a slice of ClusterPolicies from the -// ListClusterPolicies operation. -func ExtractClusterPolicies(r pagination.Page) ([]ClusterPolicy, error) { - var s struct { - ClusterPolicies []ClusterPolicy `json:"cluster_policies"` - } - err := (r.(ClusterPolicyPage)).ExtractInto(&s) - return s.ClusterPolicies, err -} - -// Extract returns collected attributes across a cluster -func (r CollectResult) Extract() ([]ClusterAttributes, error) { - var s struct { - Attributes []ClusterAttributes `json:"cluster_attributes"` - } - err := r.ExtractInto(&s) - return s.Attributes, err -} diff --git a/openstack/clustering/v1/clusters/testing/doc.go b/openstack/clustering/v1/clusters/testing/doc.go deleted file mode 100644 index 0221577893..0000000000 --- a/openstack/clustering/v1/clusters/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_clusters_v1 -package testing diff --git a/openstack/clustering/v1/clusters/testing/fixtures_test.go b/openstack/clustering/v1/clusters/testing/fixtures_test.go deleted file mode 100644 index fe7847df1e..0000000000 --- a/openstack/clustering/v1/clusters/testing/fixtures_test.go +++ /dev/null @@ -1,705 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/clusters" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const ClusterResponse = ` -{ - "cluster": { - "config": {}, - "created_at": "2015-02-10T14:26:14Z", - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "2015-02-10T15:26:14Z", - "max_size": 20, - "metadata": {}, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "2015-02-10T16:26:14Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -var ExpectedCluster = clusters.Cluster{ - Config: map[string]interface{}{}, - CreatedAt: time.Date(2015, 2, 10, 14, 26, 14, 0, time.UTC), - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 3, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: time.Date(2015, 2, 10, 15, 26, 14, 0, time.UTC), - MaxSize: 20, - Metadata: map[string]interface{}{}, - MinSize: 1, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: time.Date(2015, 2, 10, 16, 26, 14, 0, time.UTC), - User: "5e5bf8027826429c96af157f68dc9072", -} - -const ClusterResponse_EmptyTime = ` -{ - "cluster": { - "config": {}, - "created_at": null, - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": null, - "max_size": 20, - "metadata": {}, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -var ExpectedCluster_EmptyTime = clusters.Cluster{ - Config: map[string]interface{}{}, - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 3, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - MaxSize: 20, - Metadata: map[string]interface{}{}, - MinSize: 1, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - User: "5e5bf8027826429c96af157f68dc9072", -} - -const ClusterResponse_Metadata = ` -{ - "cluster": { - "config": {}, - "created_at": "2015-02-10T14:26:14Z", - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "2015-02-10T15:26:14Z", - "max_size": 20, - "metadata": { - "test": { - "nil_interface": null, - "bool_value": false, - "string_value": "test_string", - "float_value": 123.3 - }, - "foo": "bar" - }, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "2015-02-10T16:26:14Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -var ExpectedCluster_Metadata = clusters.Cluster{ - Config: map[string]interface{}{}, - CreatedAt: time.Date(2015, 2, 10, 14, 26, 14, 0, time.UTC), - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - DesiredCapacity: 3, - Domain: "", - ID: "7d85f602-a948-4a30-afd4-e84f47471c15", - InitAt: time.Date(2015, 2, 10, 15, 26, 14, 0, time.UTC), - MaxSize: 20, - MinSize: 1, - Metadata: map[string]interface{}{ - "foo": "bar", - "test": map[string]interface{}{ - "nil_interface": interface{}(nil), - "float_value": float64(123.3), - "string_value": "test_string", - "bool_value": false, - }, - }, - Name: "cluster1", - Nodes: []string{ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac", - }, - Policies: []string{}, - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - ProfileName: "mystack", - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Status: "ACTIVE", - StatusReason: "Cluster scale-in succeeded", - Timeout: 3600, - UpdatedAt: time.Date(2015, 2, 10, 16, 26, 14, 0, time.UTC), - User: "5e5bf8027826429c96af157f68dc9072", -} - -const ListResponse = ` -{ - "clusters": [ - { - "config": {}, - "created_at": "2015-02-10T14:26:14Z", - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "2015-02-10T15:26:14Z", - "max_size": 20, - "min_size": 1, - "metadata": {}, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "2015-02-10T16:26:14Z", - "user": "5e5bf8027826429c96af157f68dc9072" - }, - { - "config": {}, - "created_at": null, - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": null, - "max_size": 20, - "metadata": {}, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } - ] -}` - -var ExpectedClusters = []clusters.Cluster{ExpectedCluster, ExpectedCluster_EmptyTime} - -const UpdateResponse = ` -{ - "cluster": { - "config": {}, - "created_at": "2015-02-10T14:26:14Z", - "data": {}, - "dependents": {}, - "desired_capacity": 4, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": "2015-02-10T15:26:14Z", - "max_size": -1, - "metadata": {}, - "min_size": 0, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "profile1", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": "2015-02-10T16:26:14Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -const UpdateResponse_EmptyTime = ` -{ - "cluster": { - "config": {}, - "created_at": null, - "data": {}, - "dependents": {}, - "desired_capacity": 3, - "domain": null, - "id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "init_at": null, - "max_size": 20, - "metadata": {}, - "min_size": 1, - "name": "cluster1", - "nodes": [ - "b07c57c8-7ab2-47bf-bdf8-e894c0c601b9", - "ecc23d3e-bb68-48f8-8260-c9cf6bcb6e61", - "da1e9c87-e584-4626-a120-022da5062dac" - ], - "policies": [], - "profile_id": "edc63d0a-2ca4-48fa-9854-27926da76a4a", - "profile_name": "mystack", - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "status": "ACTIVE", - "status_reason": "Cluster scale-in succeeded", - "timeout": 3600, - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -const ActionResponse = ` -{ - "action": "2a0ff107-e789-4660-a122-3816c43af703" -}` - -const ExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" - -const OperationActionResponse = ` -{ - "action": "2a0ff107-e789-4660-a122-3816c43af703" -}` - -const OperationExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" - -const ListPoliciesResult = `{ - "cluster_policies": [ - { - "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "cluster_name": "cluster4", - "enabled": true, - "id": "06be3a1f-b238-4a96-a737-ceec5714087e", - "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", - "policy_name": "dp01", - "policy_type": "senlin.policy.deletion-1.0" - } - ] -}` - -var ExpectedClusterPolicy = clusters.ClusterPolicy{ - ClusterID: "7d85f602-a948-4a30-afd4-e84f47471c15", - ClusterName: "cluster4", - Enabled: true, - ID: "06be3a1f-b238-4a96-a737-ceec5714087e", - PolicyID: "714fe676-a08f-4196-b7af-61d52eeded15", - PolicyName: "dp01", - PolicyType: "senlin.policy.deletion-1.0", -} - -var ExpectedListPolicies = []clusters.ClusterPolicy{ExpectedClusterPolicy} - -const GetPolicyResponse = ` -{ - "cluster_policy": { - "cluster_id": "7d85f602-a948-4a30-afd4-e84f47471c15", - "cluster_name": "cluster4", - "enabled": true, - "id": "06be3a1f-b238-4a96-a737-ceec5714087e", - "policy_id": "714fe676-a08f-4196-b7af-61d52eeded15", - "policy_name": "dp01", - "policy_type": "senlin.policy.deletion-1.0" - } -}` - -const CollectResponse = ` -{ - "cluster_attributes": [{ - "id": "foo", - "value": "bar" - } - ] -}` - -var ExpectedCollectAttributes = []clusters.ClusterAttributes{ - { - ID: "foo", - Value: string("bar"), - }, -} - -func HandleCreateClusterSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") - w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ClusterResponse) - }) -} - -func HandleCreateClusterEmptyTimeSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ClusterResponse_EmptyTime) - }) -} - -func HandleCreateClusterMetadataSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ClusterResponse_Metadata) - }) -} - -func HandleGetClusterSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ClusterResponse) - }) -} - -func HandleGetClusterEmptyTimeSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15", 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, ClusterResponse_EmptyTime) - }) -} - -func HandleListClusterSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters", 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, ListResponse) - }) -} - -func HandleUpdateClusterSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/"+ExpectedCluster.ID, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ClusterResponse) - }) -} - -func HandleUpdateClusterEmptyTimeSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/"+ExpectedCluster_EmptyTime.ID, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, UpdateResponse_EmptyTime) - }) -} - -func HandleDeleteClusterSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/6dc6d336e3fc4c0a951b5698cd1236ee", 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) - }) -} - -func HandleResizeSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleScaleInSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleScaleOutSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleListPoliciesSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies", 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, ListPoliciesResult) - }) -} - -func HandleGetPolicySuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/policies/714fe676-a08f-4196-b7af-61d52eeded15", 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, GetPolicyResponse) - }) -} - -func HandleRecoverSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleAttachPolicySuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleDetachPolicySuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleUpdatePolicySuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleCheckSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleLifecycleSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") - w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/2a0ff107-e789-4660-a122-3816c43af703") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleAddNodesSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleRemoveNodesSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") - w.WriteHeader(http.StatusAccepted) - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleReplaceNodeSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") - w.WriteHeader(http.StatusAccepted) - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleClusterCollectSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/attrs/foo.bar", 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.Header().Add("X-OpenStack-Request-ID", "req-781e9bdc-4163-46eb-91c9-786c53188bbb") - w.WriteHeader(http.StatusOK) - fmt.Fprint(w, CollectResponse) - }) -} - -func HandleOpsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/clusters/7d85f602-a948-4a30-afd4-e84f47471c15/ops", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprint(w, OperationActionResponse) - }) -} diff --git a/openstack/clustering/v1/clusters/testing/requests_test.go b/openstack/clustering/v1/clusters/testing/requests_test.go deleted file mode 100644 index a21dff9051..0000000000 --- a/openstack/clustering/v1/clusters/testing/requests_test.go +++ /dev/null @@ -1,497 +0,0 @@ -package testing - -import ( - "context" - "strings" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/clusters" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestCreateCluster(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCreateClusterSuccessfully(t) - - minSize := 1 - opts := clusters.CreateOpts{ - Name: "cluster1", - DesiredCapacity: 3, - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - MinSize: &minSize, - MaxSize: 20, - Timeout: 3600, - Metadata: map[string]interface{}{}, - Config: map[string]interface{}{}, - } - - res := clusters.Create(context.TODO(), fake.ServiceClient(), opts) - th.AssertNoErr(t, res.Err) - - location := res.Header.Get("Location") - th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/625628cd-f877-44be-bde0-fec79f84e13d", location) - - locationFields := strings.Split(location, "actions/") - actionID := locationFields[1] - th.AssertEquals(t, "625628cd-f877-44be-bde0-fec79f84e13d", actionID) - - actual, err := res.Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCluster, *actual) -} - -func TestCreateClusterEmptyTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCreateClusterEmptyTimeSuccessfully(t) - - minSize := 1 - opts := clusters.CreateOpts{ - Name: "cluster1", - DesiredCapacity: 3, - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - MinSize: &minSize, - MaxSize: 20, - Timeout: 3600, - Metadata: map[string]interface{}{}, - Config: map[string]interface{}{}, - } - - actual, err := clusters.Create(context.TODO(), fake.ServiceClient(), opts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) -} - -func TestCreateClusterMetadata(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCreateClusterMetadataSuccessfully(t) - - minSize := 1 - opts := clusters.CreateOpts{ - Name: "cluster1", - DesiredCapacity: 3, - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - MinSize: &minSize, - MaxSize: 20, - Timeout: 3600, - Metadata: map[string]interface{}{ - "foo": "bar", - "test": map[string]interface{}{ - "nil_interface": interface{}(nil), - "float_value": float64(123.3), - "string_value": "test_string", - "bool_value": false, - }, - }, - Config: map[string]interface{}{}, - } - - actual, err := clusters.Create(context.TODO(), fake.ServiceClient(), opts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCluster_Metadata, *actual) -} - -func TestGetCluster(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetClusterSuccessfully(t) - - actual, err := clusters.Get(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCluster, *actual) -} - -func TestGetClusterEmptyTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetClusterEmptyTimeSuccessfully(t) - - actual, err := clusters.Get(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15").Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) -} - -func TestListClusters(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListClusterSuccessfully(t) - - count := 0 - - clusters.List(fake.ServiceClient(), clusters.ListOpts{GlobalProject: new(bool)}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := clusters.ExtractClusters(page) - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedClusters, actual) - - return true, nil - }) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestUpdateCluster(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleUpdateClusterSuccessfully(t) - - updateOpts := clusters.UpdateOpts{ - Name: "cluster1", - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - } - - actual, err := clusters.Update(context.TODO(), fake.ServiceClient(), ExpectedCluster.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCluster, *actual) -} - -func TestUpdateClusterEmptyTime(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleUpdateClusterEmptyTimeSuccessfully(t) - - updateOpts := clusters.UpdateOpts{ - Name: "cluster1", - ProfileID: "edc63d0a-2ca4-48fa-9854-27926da76a4a", - } - - actual, err := clusters.Update(context.TODO(), fake.ServiceClient(), ExpectedCluster_EmptyTime.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCluster_EmptyTime, *actual) -} - -func TestDeleteCluster(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleDeleteClusterSuccessfully(t) - - err := clusters.Delete(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").ExtractErr() - th.AssertNoErr(t, err) -} - -func TestResizeCluster(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleResizeSuccessfully(t) - - maxSize := 5 - minSize := 1 - number := -2 - strict := true - opts := clusters.ResizeOpts{ - AdjustmentType: "CHANGE_IN_CAPACITY", - MaxSize: &maxSize, - MinSize: &minSize, - Number: number, - Strict: &strict, - } - - actionID, err := clusters.Resize(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -// Test case for Number field having a float value -func TestResizeClusterNumberFloat(t *testing.T) { - maxSize := 5 - minSize := 1 - number := 100.0 - strict := true - opts := clusters.ResizeOpts{ - AdjustmentType: "CHANGE_IN_PERCENTAGE", - MaxSize: &maxSize, - MinSize: &minSize, - Number: number, - Strict: &strict, - } - - _, err := opts.ToClusterResizeMap() - th.AssertNoErr(t, err) -} - -// Test case for missing Number field. -func TestResizeClusterMissingNumber(t *testing.T) { - maxSize := 5 - minSize := 1 - strict := true - opts := clusters.ResizeOpts{ - MaxSize: &maxSize, - MinSize: &minSize, - Strict: &strict, - } - - _, err := opts.ToClusterResizeMap() - th.AssertNoErr(t, err) -} - -// Test case for missing Number field which is required when AdjustmentType is specified -func TestResizeClusterInvalidParamsMissingNumber(t *testing.T) { - maxSize := 5 - minSize := 1 - strict := true - opts := clusters.ResizeOpts{ - AdjustmentType: "CHANGE_IN_CAPACITY", - MaxSize: &maxSize, - MinSize: &minSize, - Strict: &strict, - } - - _, err := opts.ToClusterResizeMap() - isValid := err == nil - th.AssertEquals(t, false, isValid) -} - -// Test case for float Number field which is only valid for CHANGE_IN_PERCENTAGE. -func TestResizeClusterInvalidParamsNumberFloat(t *testing.T) { - maxSize := 5 - minSize := 1 - number := 100.0 - strict := true - opts := clusters.ResizeOpts{ - AdjustmentType: "CHANGE_IN_CAPACITY", - MaxSize: &maxSize, - MinSize: &minSize, - Number: number, - Strict: &strict, - } - - _, err := opts.ToClusterResizeMap() - isValid := err == nil - th.AssertEquals(t, false, isValid) -} - -func TestClusterScaleIn(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleScaleInSuccessfully(t) - - count := 5 - scaleOpts := clusters.ScaleInOpts{ - Count: &count, - } - actionID, err := clusters.ScaleIn(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOpts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -func TestListClusterPolicies(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListPoliciesSuccessfully(t) - - pageCount := 0 - err := clusters.ListPolicies(fake.ServiceClient(), ExpectedClusterPolicy.ClusterID, clusters.ListPoliciesOpts{Name: "Test"}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pageCount++ - actual, err := clusters.ExtractClusterPolicies(page) - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedListPolicies, actual) - - return true, nil - }) - - th.AssertNoErr(t, err) - th.AssertEquals(t, pageCount, 1) -} - -func TestGetClusterPolicies(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetPolicySuccessfully(t) - - actual, err := clusters.GetPolicy(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", "714fe676-a08f-4196-b7af-61d52eeded15").Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedClusterPolicy, *actual) -} - -func TestClusterRecover(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleRecoverSuccessfully(t) - - recoverOpts := clusters.RecoverOpts{ - Operation: clusters.RebuildRecovery, - Check: new(bool), - CheckCapacity: new(bool), - } - actionID, err := clusters.Recover(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -func TestAttachPolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleAttachPolicySuccessfully(t) - - enabled := true - opts := clusters.AttachPolicyOpts{ - PolicyID: "policy1", - Enabled: &enabled, - } - actionID, err := clusters.AttachPolicy(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -func TestDetachPolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleDetachPolicySuccessfully(t) - - opts := clusters.DetachPolicyOpts{ - PolicyID: "policy1", - } - actionID, err := clusters.DetachPolicy(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -func TestUpdatePolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleUpdatePolicySuccessfully(t) - - enabled := true - opts := clusters.UpdatePolicyOpts{ - PolicyID: "policy1", - Enabled: &enabled, - } - actionID, err := clusters.UpdatePolicy(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -func TestClusterScaleOut(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleScaleOutSuccessfully(t) - - scaleOutOpts := clusters.ScaleOutOpts{ - Count: 5, - } - actionID, err := clusters.ScaleOut(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", scaleOutOpts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -func TestClusterCheck(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCheckSuccessfully(t) - - actionID, err := clusters.Check(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -func TestLifecycle(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleLifecycleSuccessfully(t) - - opts := clusters.CompleteLifecycleOpts{ - LifecycleActionTokenID: "976528c6-dcf6-4d8d-9f4c-588f4e675f29", - } - - res := clusters.CompleteLifecycle(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", opts) - location := res.Header.Get("Location") - th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/2a0ff107-e789-4660-a122-3816c43af703", location) - - actionID, err := res.Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, "2a0ff107-e789-4660-a122-3816c43af703", actionID) -} - -func TestAddNodes(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleAddNodesSuccessfully(t) - - opts := clusters.AddNodesOpts{ - Nodes: []string{"node1", "node2", "node3"}, - } - result, err := clusters.AddNodes(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") -} - -func TestRemoveNodes(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleRemoveNodesSuccessfully(t) - - opts := clusters.RemoveNodesOpts{ - Nodes: []string{"node1", "node2", "node3"}, - } - result, err := clusters.RemoveNodes(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, result, "2a0ff107-e789-4660-a122-3816c43af703") -} - -func TestReplaceNodes(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleReplaceNodeSuccessfully(t) - opts := clusters.ReplaceNodesOpts{ - Nodes: map[string]string{"node-1234": "node-5678"}, - } - actionID, err := clusters.ReplaceNodes(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, actionID, "2a0ff107-e789-4660-a122-3816c43af703") -} - -func TestClusterCollect(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - HandleClusterCollectSuccessfully(t) - opts := clusters.CollectOpts{ - Path: "foo.bar", - } - attributes, err := clusters.Collect(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", opts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCollectAttributes, attributes) -} - -func TestOperation(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleOpsSuccessfully(t) - - clusterOpts := clusters.OperationOpts{ - Operation: clusters.PauseOperation, - Filters: clusters.OperationFilters{"role": "slave"}, - Params: clusters.OperationParams{"type": "soft"}, - } - actual, err := clusters.Ops(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", clusterOpts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, OperationExpectedActionID, actual) -} diff --git a/openstack/clustering/v1/clusters/urls.go b/openstack/clustering/v1/clusters/urls.go deleted file mode 100644 index 44d05578a3..0000000000 --- a/openstack/clustering/v1/clusters/urls.go +++ /dev/null @@ -1,58 +0,0 @@ -package clusters - -import "github.com/gophercloud/gophercloud/v2" - -var apiVersion = "v1" -var apiName = "clusters" - -func commonURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func idURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id) -} - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "actions") -} - -func createURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func listURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func updateURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func deleteURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func listPoliciesURL(client *gophercloud.ServiceClient, clusterID string) string { - return client.ServiceURL(apiVersion, apiName, clusterID, "policies") -} - -func getPolicyURL(client *gophercloud.ServiceClient, clusterID string, policyID string) string { - return client.ServiceURL(apiVersion, apiName, clusterID, "policies", policyID) -} - -func nodeURL(client *gophercloud.ServiceClient, id string) string { - return actionURL(client, id) -} - -func collectURL(client *gophercloud.ServiceClient, clusterID string, path string) string { - return client.ServiceURL(apiVersion, apiName, clusterID, "attrs", path) -} - -func opsURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "ops") -} diff --git a/openstack/clustering/v1/events/doc.go b/openstack/clustering/v1/events/doc.go deleted file mode 100644 index a57222044b..0000000000 --- a/openstack/clustering/v1/events/doc.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Package events provides listing and retrieving of senlin events for the -OpenStack Clustering Service. - -Example to List Events - - opts := events.ListOpts{ - Limit: 5, - } - - err = events.List(serviceClient, opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - eventInfos, err := events.ExtractEvents(page) - if err != nil { - return false, err - } - - for _, eventInfo := range eventInfos { - fmt.Printf("%+v\n", eventInfo) - } - return true, nil - }) - -Example to Get an Event - - eventID := "edce3528-864f-41fb-8759-f4707925cc09" - event, err := events.Get(context.TODO(), serviceClient, eventID).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("Event %+v: ", event) -*/ -package events diff --git a/openstack/clustering/v1/events/requests.go b/openstack/clustering/v1/events/requests.go deleted file mode 100644 index 164a34efae..0000000000 --- a/openstack/clustering/v1/events/requests.go +++ /dev/null @@ -1,56 +0,0 @@ -package events - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToEventListQuery() (string, error) -} - -// ListOpts represents options used to list events. -type ListOpts struct { - Limit int `q:"limit,omitempty"` - Level int `q:"level,omitempty"` - Marker string `q:"marker,omitempty"` - Sort string `q:"sort,omitempty"` - GlobalProject *bool `q:"global_project,omitempty"` - OID string `q:"oid,omitempty"` - OType string `q:"otype,omitempty"` - OName string `q:"oname,omitempty"` - ClusterID string `q:"cluster_id,omitempty"` - Action string `q:"action"` -} - -// ToEventListQuery builds a query string from ListOpts. -func (opts ListOpts) ToEventListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to provide a list of events. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToEventListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return EventPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Get retrieves details of a single event. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/events/results.go b/openstack/clustering/v1/events/results.go deleted file mode 100644 index c130fbf69b..0000000000 --- a/openstack/clustering/v1/events/results.go +++ /dev/null @@ -1,70 +0,0 @@ -package events - -import ( - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Event represents a detailed Event. -type Event struct { - Action string `json:"action"` - Cluster string `json:"cluster"` - ClusterID string `json:"cluster_id"` - ID string `json:"id"` - Level string `json:"level"` - Metadata map[string]interface{} `json:"meta_data"` - OID string `json:"oid"` - OName string `json:"oname"` - OType string `json:"otype"` - Project string `json:"project"` - Status string `json:"status"` - StatusReason string `json:"status_reason"` - Timestamp time.Time `json:"timestamp"` - User string `json:"user"` -} - -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// Extract interprets any commonResult-based result as an Event. -func (r commonResult) Extract() (*Event, error) { - var s struct { - Event *Event `json:"event"` - } - err := r.ExtractInto(&s) - return s.Event, err -} - -// GetResult is the response of a Get operations. Call its Extract method to -// interpret it as an Event. -type GetResult struct { - commonResult -} - -// EventPage contains a single page of all events from a List call. -type EventPage struct { - pagination.LinkedPageBase -} - -// 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 -} - -// ExtractEvents returns a slice of Events from the List operation. -func ExtractEvents(r pagination.Page) ([]Event, error) { - var s struct { - Events []Event `json:"events"` - } - err := (r.(EventPage)).ExtractInto(&s) - return s.Events, err -} diff --git a/openstack/clustering/v1/events/testing/doc.go b/openstack/clustering/v1/events/testing/doc.go deleted file mode 100644 index 93a668d499..0000000000 --- a/openstack/clustering/v1/events/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_events_v1 -package testing diff --git a/openstack/clustering/v1/events/testing/fixtures_test.go b/openstack/clustering/v1/events/testing/fixtures_test.go deleted file mode 100644 index adfc75c07e..0000000000 --- a/openstack/clustering/v1/events/testing/fixtures_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/events" - - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const ListResponse = ` -{ - "events": [ - { - "action": "CLUSTER_CREATE", - "cluster": null, - "cluster_id": null, - "id": "edce3528-864f-41fb-8759-f4707925cc09", - "level": "INFO", - "meta_data": {}, - "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", - "oname": "cluster001", - "otype": "CLUSTER", - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "status": "start", - "status_reason": "Initializing", - "timestamp": "2015-03-05T08:53:15Z", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - }, - { - "action": "NODE_DELETE", - "cluster": null, - "cluster_id": null, - "id": "abcd1234-864f-41fb-8759-f4707925dd10", - "level": "INFO", - "meta_data": {}, - "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", - "oname": "node119", - "otype": "node", - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "status": "start", - "status_reason": "84492c96", - "timestamp": "2015-03-06T18:53:15Z", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - } - ] -} -` - -const GetResponse = ` -{ - "event": { - "action": "CLUSTER_CREATE", - "cluster_id": null, - "id": "edce3528-864f-41fb-8759-f4707925cc09", - "level": "INFO", - "meta_data": {}, - "oid": "0df0931b-e251-4f2e-8719-4ebfda3627ba", - "oname": "cluster001", - "otype": "CLUSTER", - "project": "f1fe61dcda2f4618a14c10dc7abc214d", - "status": "start", - "status_reason": "Initializing", - "timestamp": "2015-03-05T08:53:15Z", - "user": "8bcd2cdca7684c02afc9e4f2fc0f0c79" - } -} -` - -var ExpectedEvent1 = events.Event{ - Action: "CLUSTER_CREATE", - Cluster: "", - ClusterID: "", - ID: "edce3528-864f-41fb-8759-f4707925cc09", - Level: "INFO", - Metadata: map[string]interface{}{}, - OID: "0df0931b-e251-4f2e-8719-4ebfda3627ba", - OName: "cluster001", - OType: "CLUSTER", - Project: "f1fe61dcda2f4618a14c10dc7abc214d", - Status: "start", - StatusReason: "Initializing", - Timestamp: time.Date(2015, 3, 5, 8, 53, 15, 0, time.UTC), - User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", -} - -var ExpectedEvent2 = events.Event{ - Action: "NODE_DELETE", - Cluster: "", - ClusterID: "", - ID: "abcd1234-864f-41fb-8759-f4707925dd10", - Level: "INFO", - Metadata: map[string]interface{}{}, - OID: "0df0931b-e251-4f2e-8719-4ebfda3627ba", - OName: "node119", - OType: "node", - Project: "f1fe61dcda2f4618a14c10dc7abc214d", - Status: "start", - StatusReason: "84492c96", - Timestamp: time.Date(2015, 3, 6, 18, 53, 15, 0, time.UTC), - User: "8bcd2cdca7684c02afc9e4f2fc0f0c79", -} - -var ExpectedEvents = []events.Event{ExpectedEvent1, ExpectedEvent2} - -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/events", 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, ListResponse) - }) -} - -func HandleGetSuccessfully(t *testing.T, id string) { - th.Mux.HandleFunc("/v1/events/"+id, 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, GetResponse) - }) -} diff --git a/openstack/clustering/v1/events/testing/requests_test.go b/openstack/clustering/v1/events/testing/requests_test.go deleted file mode 100644 index 592aaa81ea..0000000000 --- a/openstack/clustering/v1/events/testing/requests_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/events" - - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestListEvents(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListSuccessfully(t) - - pageCount := 0 - err := events.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pageCount++ - actual, err := events.ExtractEvents(page) - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, ExpectedEvents, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - - if pageCount != 1 { - t.Errorf("Expected 1 page, got %d", pageCount) - } -} - -func TestGetEvent(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetSuccessfully(t, ExpectedEvent1.ID) - - actual, err := events.Get(context.TODO(), fake.ServiceClient(), ExpectedEvent1.ID).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedEvent1, *actual) -} diff --git a/openstack/clustering/v1/events/urls.go b/openstack/clustering/v1/events/urls.go deleted file mode 100644 index a3807e1b55..0000000000 --- a/openstack/clustering/v1/events/urls.go +++ /dev/null @@ -1,22 +0,0 @@ -package events - -import "github.com/gophercloud/gophercloud/v2" - -var apiVersion = "v1" -var apiName = "events" - -func commonURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func listURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func idURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id) -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} diff --git a/openstack/clustering/v1/nodes/doc.go b/openstack/clustering/v1/nodes/doc.go deleted file mode 100644 index 2959268c0f..0000000000 --- a/openstack/clustering/v1/nodes/doc.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Package nodes provides information and interaction with the nodes through -the OpenStack Clustering service. - -Example to Create a Node - - createOpts := nodes.CreateOpts{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - Metadata: map[string]interface{}{}, - Name: "node-e395be1e-002", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - Role: "", - } - - node, err := nodes.Create(context.TODO(), serviceClient, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("node", node) - -Example to List Nodes - - listOpts := nodes.ListOpts{ - Name: "testnode", - } - - allPages, err := nodes.List(serviceClient, listOpts).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allNodes, err := nodes.ExtractNodes(allPages) - if err != nil { - panic(err) - } - - for _, node := range allNodes { - fmt.Printf("%+v\n", node) - } - -Example to Update a Node - - opts := nodes.UpdateOpts{ - Name: "new-node-name", - } - - nodeID := "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1" - node, err := nodes.Update(context.TODO(), serviceClient, nodeID, opts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", node) - -Example to Delete a Node - - nodeID := "6dc6d336e3fc4c0a951b5698cd1236ee" - err := nodes.Delete(context.TODO(), serviceClient, nodeID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Get a Node - - nodeID := "node123" - node, err := nodes.Get(context.TODO(), serviceClient, nodeID).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", node) - -Example to Perform an Operation on a Node - - serviceClient.Microversion = "1.4" - nodeID := "node123" - operationOpts := nodes.OperationOpts{ - Operation: nodes.RebootOperation, - Params: nodes.OperationParams{"type": "SOFT"}, - } - actionID, err := nodes.Ops(context.TODO(), serviceClient, nodeID, operationOpts).Extract() - if err != nil { - panic(err) - } - -Example to Recover a Node - - nodeID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - check := true - recoverOpts := nodes.RecoverOpts{ - Operation: nodes.RebuildRecovery, - Check: &check, - } - actionID, err := nodes.Recover(context.TODO(), computeClient, nodeID, recoverOpts).Extract() - if err != nil { - panic(err) - } - fmt.Println("action=", actionID) - -Example to Check a Node - - nodeID := "b7b870e3-d3c5-4a93-b9d7-846c53b2c2da" - actionID, err := nodes.Check(context.TODO(), serviceClient, nodeID).Extract() - if err != nil { - panic(err) - } - fmt.Println("action=", actionID) -*/ -package nodes diff --git a/openstack/clustering/v1/nodes/requests.go b/openstack/clustering/v1/nodes/requests.go deleted file mode 100644 index b8c8c28130..0000000000 --- a/openstack/clustering/v1/nodes/requests.go +++ /dev/null @@ -1,229 +0,0 @@ -package nodes - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToNodeCreateMap() (map[string]interface{}, error) -} - -// CreateOpts represents options used to create a Node. -type CreateOpts struct { - Role string `json:"role,omitempty"` - ProfileID string `json:"profile_id" required:"true"` - ClusterID string `json:"cluster_id,omitempty"` - Name string `json:"name" required:"true"` - Metadata map[string]interface{} `json:"metadata,omitempty"` -} - -// ToNodeCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "node") -} - -// Create requests the creation of a new node. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToNodeCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToNodeUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts represents options used to update a Node. -type UpdateOpts struct { - Name string `json:"name,omitempty"` - ProfileID string `json:"profile_id,omitempty"` - Role string `json:"role,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty"` -} - -// ToNodeUpdateMap constructs a request body from UpdateOpts. -func (opts UpdateOpts) ToNodeUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "node") -} - -// Update requests the update of a node. -func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToNodeUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parmeters to the -// List request. -type ListOptsBuilder interface { - ToNodeListQuery() (string, error) -} - -// ListOpts represents options used to list nodes. -type ListOpts struct { - Limit int `q:"limit"` - Marker string `q:"marker"` - Sort string `q:"sort"` - GlobalProject *bool `q:"global_project"` - ClusterID string `q:"cluster_id"` - Name string `q:"name"` - Status string `q:"status"` -} - -// ToNodeListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToNodeListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to provide a list of nodes. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToNodeListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return NodePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Delete deletes the specified node. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get makes a request against senlin to get a details of a node type -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// OperationName represents valid values for node operation -type OperationName string - -const ( - // Nova Profile Op Names - RebootOperation OperationName = "reboot" - RebuildOperation OperationName = "rebuild" - ChangePasswordOperation OperationName = "change_password" - PauseOperation OperationName = "pause" - UnpauseOperation OperationName = "unpause" - SuspendOperation OperationName = "suspend" - ResumeOperation OperationName = "resume" - LockOperation OperationName = "lock" - UnlockOperation OperationName = "unlock" - StartOperation OperationName = "start" - StopOperation OperationName = "stop" - RescueOperation OperationName = "rescue" - UnrescueOperation OperationName = "unrescue" - EvacuateOperation OperationName = "evacuate" - - // Heat Pofile Op Names - AbandonOperation OperationName = "abandon" -) - -// ToNodeOperationMap constructs a request body from OperationOpts. -func (opts OperationOpts) ToNodeOperationMap() (map[string]interface{}, error) { - optsMap := map[string]interface{}{string(opts.Operation): opts.Params} - return optsMap, nil -} - -// OperationOptsBuilder allows extensions to add additional parameters to the -// Op request. -type OperationOptsBuilder interface { - ToNodeOperationMap() (map[string]interface{}, error) -} -type OperationParams map[string]interface{} - -// OperationOpts represents options used to perform an operation on a node -type OperationOpts struct { - Operation OperationName `json:"operation" required:"true"` - Params OperationParams `json:"params,omitempty"` -} - -func Ops(ctx context.Context, client *gophercloud.ServiceClient, id string, opts OperationOptsBuilder) (r ActionResult) { - b, err := opts.ToNodeOperationMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, opsURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -func (opts RecoverOpts) ToNodeRecoverMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "recover") -} - -// RecoverAction represents valid values for recovering a node. -type RecoverAction string - -const ( - RebootRecovery RecoverAction = "REBOOT" - RebuildRecovery RecoverAction = "REBUILD" - // RECREATE currently is NOT supported. See https://github.com/openstack/senlin/blob/b30b2b8496b2b8af243ccd5292f38aec7a95664f/senlin/profiles/base.py#L533 - RecreateRecovery RecoverAction = "RECREATE" -) - -type RecoverOpts struct { - Operation RecoverAction `json:"operation,omitempty"` - Check *bool `json:"check,omitempty"` -} - -func Recover(ctx context.Context, client *gophercloud.ServiceClient, id string, opts RecoverOpts) (r ActionResult) { - b, err := opts.ToNodeRecoverMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -func Check(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { - b := map[string]interface{}{ - "check": map[string]interface{}{}, - } - resp, err := client.Post(ctx, actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/nodes/results.go b/openstack/clustering/v1/nodes/results.go deleted file mode 100644 index 196594ad6f..0000000000 --- a/openstack/clustering/v1/nodes/results.go +++ /dev/null @@ -1,148 +0,0 @@ -package nodes - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Node represents an OpenStack clustering node. -type Node struct { - ClusterID string `json:"cluster_id"` - CreatedAt time.Time `json:"-"` - Data map[string]interface{} `json:"data"` - Dependents map[string]interface{} `json:"dependents"` - Domain string `json:"domain"` - ID string `json:"id"` - Index int `json:"index"` - InitAt time.Time `json:"-"` - Metadata map[string]interface{} `json:"metadata"` - Name string `json:"name"` - PhysicalID string `json:"physical_id"` - ProfileID string `json:"profile_id"` - ProfileName string `json:"profile_name"` - Project string `json:"project"` - Role string `json:"role"` - Status string `json:"status"` - StatusReason string `json:"status_reason"` - UpdatedAt time.Time `json:"-"` - User string `json:"user"` -} - -func (r *Node) UnmarshalJSON(b []byte) error { - type tmp Node - var s struct { - tmp - CreatedAt string `json:"created_at"` - InitAt string `json:"init_at"` - UpdatedAt string `json:"updated_at"` - } - - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Node(s.tmp) - - if s.CreatedAt != "" { - r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) - if err != nil { - return err - } - } - - if s.InitAt != "" { - r.InitAt, err = time.Parse(time.RFC3339, s.InitAt) - if err != nil { - return err - } - } - - if s.UpdatedAt != "" { - r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) - if err != nil { - return err - } - } - - return nil -} - -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// Extract interprets any commonResult-based result as a Node. -func (r commonResult) Extract() (*Node, error) { - var s struct { - Node *Node `json:"node"` - } - err := r.ExtractInto(&s) - return s.Node, err -} - -// CreateResult is the result of a Create operation. Call its Extract -// method to intepret it as a Node. -type CreateResult struct { - commonResult -} - -// GetResult is the result of a Get operation. Call its Extract method to -// interpret it as a Node. -type GetResult struct { - commonResult -} - -// DeleteResult is the result from a Delete operation. Call ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// UpdateResult is the result of an Update operation. Call its Extract method -// to interpet it as a Node. -type UpdateResult struct { - commonResult -} - -// NodePage contains a single page of all nodes from a List call. -type NodePage struct { - pagination.LinkedPageBase -} - -// 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 -} - -// ExtractNodes returns a slice of Nodes from the List operation. -func ExtractNodes(r pagination.Page) ([]Node, error) { - var s struct { - Nodes []Node `json:"nodes"` - } - err := (r.(NodePage)).ExtractInto(&s) - return s.Nodes, err -} - -// ActionResult is the response of Senlin actions. Call its Extract method to -// obtain the Action ID of the action. -type ActionResult struct { - gophercloud.Result -} - -// Extract interprets any Action result as an Action. -func (r ActionResult) Extract() (string, error) { - var s struct { - Action string `json:"action"` - } - err := r.ExtractInto(&s) - return s.Action, err -} diff --git a/openstack/clustering/v1/nodes/testing/doc.go b/openstack/clustering/v1/nodes/testing/doc.go deleted file mode 100644 index cb76666d2d..0000000000 --- a/openstack/clustering/v1/nodes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_nodes_v1 -package testing diff --git a/openstack/clustering/v1/nodes/testing/fixtures_test.go b/openstack/clustering/v1/nodes/testing/fixtures_test.go deleted file mode 100644 index 5f6a838aaf..0000000000 --- a/openstack/clustering/v1/nodes/testing/fixtures_test.go +++ /dev/null @@ -1,364 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const CreateResponse = `{ - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "2016-05-13T07:02:20Z", - "data": { - "internal_ports": [ - { - "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", - "security_group_ids": ["8db277ab-1d98-4148-ba72-724721789427"], - "fixed_ips": [ - { - "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", - "ip_address": "10.63.177.162" - } - ], - "id": "43aa53d7-a70b-4f40-812f-4feecb687018", - "remove": true - } - ], - "placement": { - "zone": "nova" - } - }, - "dependents": {}, - "domain": "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "2016-05-13T08:02:04Z", - "metadata": { - "test": { - "nil_interface": null, - "bool_value": false, - "string_value": "test_string", - "float_value": 123.3 - }, - "foo": "bar" - }, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": null, - "user": "ab79b9647d074e46ac223a8fa297b846" - } -}` - -var ExpectedCreate = nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), - Data: map[string]interface{}{ - "internal_ports": []map[string]interface{}{ - { - "network_id": "847e4f65-1ff1-42b1-9e74-74e6a109ad11", - "security_group_ids": []interface{}{ - "8db277ab-1d98-4148-ba72-724721789427", - }, - "fixed_ips": []interface{}{ - map[string]interface{}{ - "subnet_id": "863b20c0-c011-4650-85c2-ad531f4570a4", - "ip_address": "10.63.177.162", - }, - }, - "id": "43aa53d7-a70b-4f40-812f-4feecb687018", - "remove": true, - }, - }, - "placement": map[string]interface{}{ - "zone": "nova", - }, - }, - Dependents: map[string]interface{}{}, - Domain: "1235be1e-8d8e-43bb-bd6c-943eccf76a6d", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), - Metadata: map[string]interface{}{ - "foo": "bar", - "test": map[string]interface{}{ - "nil_interface": interface{}(nil), - "float_value": float64(123.3), - "string_value": "test_string", - "bool_value": false, - }, - }, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - Project: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - User: "ab79b9647d074e46ac223a8fa297b846", -} - -const ListResponse = ` -{ - "nodes": [ - { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "2016-05-13T07:02:20Z", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "2016-05-13T08:02:04Z", - "metadata": {}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "2016-05-13T09:02:04Z", - "user": "ab79b9647d074e46ac223a8fa297b846" } - ] -}` - -var ExpectedList1 = nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - Domain: "", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), - Metadata: map[string]interface{}{}, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - Project: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: time.Date(2016, 5, 13, 9, 2, 4, 0, time.UTC), - User: "ab79b9647d074e46ac223a8fa297b846", -} - -var ExpectedList = []nodes.Node{ExpectedList1} - -const GetResponse = ` -{ - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "2016-05-13T07:02:20Z", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "2016-05-13T07:02:04Z", - "metadata": {"foo": "bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "2016-05-13T07:02:20Z", - "user": "ab79b9647d074e46ac223a8fa297b846" - } -}` - -var ExpectedGet = nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - Domain: "", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: time.Date(2016, 5, 13, 7, 2, 4, 0, time.UTC), - Metadata: map[string]interface{}{"foo": "bar"}, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - Project: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), - User: "ab79b9647d074e46ac223a8fa297b846", -} - -const UpdateResponse = ` -{ - "node": { - "cluster_id": "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - "created_at": "2016-05-13T07:02:20Z", - "data": {}, - "dependents": {}, - "domain": null, - "id": "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - "index": 2, - "init_at": "2016-05-13T08:02:04Z", - "metadata": {"foo":"bar"}, - "name": "node-e395be1e-002", - "physical_id": "66a81d68-bf48-4af5-897b-a3bfef7279a8", - "profile_id": "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - "profile_name": "pcirros", - "project": "eee0b7c083e84501bdd50fb269d2a10e", - "role": "", - "status": "ACTIVE", - "status_reason": "Creation succeeded", - "updated_at": "2016-05-13T09:02:04Z", - "user": "ab79b9647d074e46ac223a8fa297b846" - } -}` - -var ExpectedUpdate = nodes.Node{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - CreatedAt: time.Date(2016, 5, 13, 7, 2, 20, 0, time.UTC), - Data: map[string]interface{}{}, - Dependents: map[string]interface{}{}, - Domain: "", - ID: "82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", - Index: 2, - InitAt: time.Date(2016, 5, 13, 8, 2, 4, 0, time.UTC), - Metadata: map[string]interface{}{"foo": "bar"}, - Name: "node-e395be1e-002", - PhysicalID: "66a81d68-bf48-4af5-897b-a3bfef7279a8", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - ProfileName: "pcirros", - Project: "eee0b7c083e84501bdd50fb269d2a10e", - Role: "", - Status: "ACTIVE", - StatusReason: "Creation succeeded", - UpdatedAt: time.Date(2016, 5, 13, 9, 2, 4, 0, time.UTC), - User: "ab79b9647d074e46ac223a8fa297b846", -} - -const OperationActionResponse = ` -{ - "action": "2a0ff107-e789-4660-a122-3816c43af703" -}` - -const OperationExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" - -const ActionResponse = ` -{ - "action": "2a0ff107-e789-4660-a122-3816c43af703" -}` - -const ExpectedActionID = "2a0ff107-e789-4660-a122-3816c43af703" - -func HandleCreateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/nodes", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-3791a089-9d46-4671-a3f9-55e95e55d2b4") - w.Header().Add("Location", "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da") - - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, CreateResponse) - }) -} - -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/nodes", 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, ListResponse) - }) -} - -func HandleDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/nodes/6dc6d336e3fc4c0a951b5698cd1236ee", 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) - }) -} - -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/nodes/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, GetResponse) - }) -} - -func HandleUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/nodes/82fe28e0-9fcb-42ca-a2fa-6eb7dddd75a1", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, UpdateResponse) - }) -} - -func HandleOpsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/nodes/7d85f602-a948-4a30-afd4-e84f47471c15/ops", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - - fmt.Fprint(w, OperationActionResponse) - }) -} - -func HandleRecoverSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/nodes/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-edce3528-864f-41fb-8759-f4707925cc09") - w.WriteHeader(http.StatusAccepted) - fmt.Fprint(w, ActionResponse) - }) -} - -func HandleCheckSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/nodes/edce3528-864f-41fb-8759-f4707925cc09/actions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-ID", "req-edce3528-864f-41fb-8759-f4707925cc09") - w.WriteHeader(http.StatusAccepted) - fmt.Fprint(w, ActionResponse) - }) -} diff --git a/openstack/clustering/v1/nodes/testing/requests_test.go b/openstack/clustering/v1/nodes/testing/requests_test.go deleted file mode 100644 index c969eb76e7..0000000000 --- a/openstack/clustering/v1/nodes/testing/requests_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package testing - -import ( - "context" - "strings" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/nodes" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestCreateNode(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCreateSuccessfully(t) - - createOpts := nodes.CreateOpts{ - ClusterID: "e395be1e-8d8e-43bb-bd6c-943eccf76a6d", - Metadata: map[string]interface{}{ - "foo": "bar", - "test": map[string]interface{}{ - "nil_interface": interface{}(nil), - "float_value": float64(123.3), - "string_value": "test_string", - "bool_value": false, - }, - }, - Name: "node-e395be1e-002", - ProfileID: "d8a48377-f6a3-4af4-bbbb-6e8bcaa0cbc0", - Role: "", - } - - res := nodes.Create(context.TODO(), fake.ServiceClient(), createOpts) - th.AssertNoErr(t, res.Err) - - requestID := res.Header.Get("X-Openstack-Request-Id") - th.AssertEquals(t, "req-3791a089-9d46-4671-a3f9-55e95e55d2b4", requestID) - - location := res.Header.Get("Location") - th.AssertEquals(t, "http://senlin.cloud.blizzard.net:8778/v1/actions/ffd94dd8-6266-4887-9a8c-5b78b72136da", location) - - locationFields := strings.Split(location, "actions/") - actionID := locationFields[1] - th.AssertEquals(t, "ffd94dd8-6266-4887-9a8c-5b78b72136da", actionID) - - actual, err := res.Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedCreate, *actual) -} - -func TestListNodes(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListSuccessfully(t) - - count := 0 - err := nodes.List(fake.ServiceClient(), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - actual, err := nodes.ExtractNodes(page) - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedList, actual) - count++ - return true, nil - }) - - th.AssertNoErr(t, err) - th.AssertEquals(t, count, 1) -} - -func TestDeleteNode(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleDeleteSuccessfully(t) - - deleteResult := nodes.Delete(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") - th.AssertNoErr(t, deleteResult.ExtractErr()) -} - -func TestGetNode(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetSuccessfully(t) - - actual, err := nodes.Get(context.TODO(), fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedGet, *actual) -} - -func TestUpdateNode(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleUpdateSuccessfully(t) - - nodeOpts := nodes.UpdateOpts{ - Name: "node-e395be1e-002", - } - actual, err := nodes.Update(context.TODO(), fake.ServiceClient(), ExpectedUpdate.ID, nodeOpts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedUpdate, *actual) -} - -func TestOpsNode(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleOpsSuccessfully(t) - - nodeOpts := nodes.OperationOpts{ - Operation: nodes.PauseOperation, - } - actual, err := nodes.Ops(context.TODO(), fake.ServiceClient(), "7d85f602-a948-4a30-afd4-e84f47471c15", nodeOpts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, OperationExpectedActionID, actual) -} - -func TestNodeRecover(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleRecoverSuccessfully(t) - recoverOpts := nodes.RecoverOpts{ - Operation: nodes.RebuildRecovery, - Check: new(bool), - } - actionID, err := nodes.Recover(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09", recoverOpts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} - -func TestNodeCheck(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCheckSuccessfully(t) - - actionID, err := nodes.Check(context.TODO(), fake.ServiceClient(), "edce3528-864f-41fb-8759-f4707925cc09").Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedActionID, actionID) -} diff --git a/openstack/clustering/v1/nodes/urls.go b/openstack/clustering/v1/nodes/urls.go deleted file mode 100644 index 05f695f573..0000000000 --- a/openstack/clustering/v1/nodes/urls.go +++ /dev/null @@ -1,42 +0,0 @@ -package nodes - -import "github.com/gophercloud/gophercloud/v2" - -var apiVersion = "v1" -var apiName = "nodes" - -func commonURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "actions") -} - -func createURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func listURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func idURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id) -} - -func deleteURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func updateURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func opsURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "ops") -} diff --git a/openstack/clustering/v1/policies/doc.go b/openstack/clustering/v1/policies/doc.go deleted file mode 100644 index 2bf65f00b5..0000000000 --- a/openstack/clustering/v1/policies/doc.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Package policies provides information and interaction with the policies through -the OpenStack Clustering service. - -Example to List Policies - - listOpts := policies.ListOpts{ - Limit: 2, - } - - allPages, err := policies.List(clusteringClient, listOpts).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allPolicies, err := policies.ExtractPolicies(allPages) - if err != nil { - panic(err) - } - - for _, policy := range allPolicies { - fmt.Printf("%+v\n", policy) - } - -Example to Create a Policy - - createOpts := policies.CreateOpts{ - Name: "new_policy", - Spec: policies.Spec{ - Description: "new policy description", - Properties: map[string]interface{}{ - "hooks": map[string]interface{}{ - "type": "zaqar", - "params": map[string]interface{}{ - "queue": "my_zaqar_queue", - }, - "timeout": 10, - }, - }, - Type: "senlin.policy.deletion", - Version: "1.1", - }, - } - - createdPolicy, err := policies.Create(context.TODO(), client, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Get a Policy - - policyName := "get_policy" - policyDetail, err := policies.Get(context.TODO(), clusteringClient, policyName).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", policyDetail) - -Example to Update a Policy - - opts := policies.UpdateOpts{ - Name: "update_policy", - } - - updatePolicy, err := policies.Update(context.TODO(), client, opts).Extract() - if err != nil { - panic(err) - } - -Example to Validate a Policy - - opts := policies.ValidateOpts{ - Spec: policies.Spec{ - Description: "new policy description", - Properties: map[string]interface{}{ - "hooks": map[string]interface{}{ - "type": "zaqar", - "params": map[string]interface{}{ - "queue": "my_zaqar_queue", - }, - "timeout": 10, - }, - }, - Type: "senlin.policy.deletion", - Version: "1.1", - }, - } - - validatePolicy, err := policies.Validate(context.TODO(), client, opts).Extract() - if err != nil { - panic(err) - } -*/ -package policies diff --git a/openstack/clustering/v1/policies/requests.go b/openstack/clustering/v1/policies/requests.go deleted file mode 100644 index 5196bd71f1..0000000000 --- a/openstack/clustering/v1/policies/requests.go +++ /dev/null @@ -1,177 +0,0 @@ -package policies - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToPolicyListQuery() (string, error) -} - -// ListOpts represents options used to list policies. -type ListOpts struct { - // Limit limits the number of Policies to return. - Limit int `q:"limit"` - - // Marker and Limit control paging. Marker instructs List where to start - // listing from. - Marker string `q:"marker"` - - // Sorts the response by one or more attribute and optional sort direction - // combinations. - Sort string `q:"sort"` - - // GlobalProject indicates whether to include resources for all projects or - // resources for the current project. - GlobalProject *bool `q:"global_project"` - - // Name to filter the response by the specified name property of the object. - Name string `q:"name"` - - // Filter the response by the specified type property of the object. - Type string `q:"type"` -} - -// ToPolicyListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToPolicyListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to retrieve a list of policies. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := policyListURL(client) - if opts != nil { - query, err := opts.ToPolicyListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - p := PolicyPage{pagination.MarkerPageBase{PageResult: r}} - p.MarkerPageBase.Owner = p - return p - }) -} - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToPolicyCreateMap() (map[string]interface{}, error) -} - -// CreateOpts represents options used to create a policy. -type CreateOpts struct { - Name string `json:"name"` - Spec Spec `json:"spec"` -} - -// ToPolicyCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { - b, err := gophercloud.BuildRequestBody(opts, "") - if err != nil { - return nil, err - } - - return map[string]interface{}{"policy": b}, nil -} - -// Create makes a request against the API to create a policy -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToPolicyCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, policyCreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{201}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete makes a request against the API to delete a policy. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, policyID string) (r DeleteResult) { - resp, err := client.Delete(ctx, policyDeleteURL(client, policyID), &gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToPolicyUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts represents options to update a policy. -type UpdateOpts struct { - Name string `json:"name,omitempty"` -} - -// ToPolicyUpdateMap constructs a request body from UpdateOpts. -func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "policy") -} - -// Update updates a specified policy. -func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToPolicyUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ValidateOptsBuilder allows extensions to add additional parameters to the -// Validate request. -type ValidateOptsBuilder interface { - ToPolicyValidateMap() (map[string]interface{}, error) -} - -// ValidateOpts represents options used to validate a policy. -type ValidateOpts struct { - Spec Spec `json:"spec"` -} - -// ToPolicyValidateMap formats a CreateOpts into a body map. -func (opts ValidateOpts) ToPolicyValidateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "policy") -} - -// Validate policy will validate a specified policy. -func Validate(ctx context.Context, client *gophercloud.ServiceClient, opts ValidateOptsBuilder) (r ValidateResult) { - b, err := opts.ToPolicyValidateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get makes a request against the API to get details for a policy. -func Get(ctx context.Context, client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { - url := policyGetURL(client, policyTypeName) - resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/policies/results.go b/openstack/clustering/v1/policies/results.go deleted file mode 100644 index 3942a2cb3c..0000000000 --- a/openstack/clustering/v1/policies/results.go +++ /dev/null @@ -1,189 +0,0 @@ -package policies - -import ( - "encoding/json" - "fmt" - "strconv" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Policy represents a clustering policy in the Openstack cloud. -type Policy struct { - CreatedAt time.Time `json:"-"` - Data map[string]interface{} `json:"data"` - Domain string `json:"domain"` - ID string `json:"id"` - Name string `json:"name"` - Project string `json:"project"` - Spec Spec `json:"spec"` - Type string `json:"type"` - UpdatedAt time.Time `json:"-"` - User string `json:"user"` -} - -func (r *Policy) UnmarshalJSON(b []byte) error { - type tmp Policy - var s struct { - tmp - CreatedAt string `json:"created_at,omitempty"` - UpdatedAt string `json:"updated_at,omitempty"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Policy(s.tmp) - - if s.CreatedAt != "" { - r.CreatedAt, err = time.Parse(gophercloud.RFC3339MilliNoZ, s.CreatedAt) - if err != nil { - r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) - if err != nil { - return err - } - } - } - - if s.UpdatedAt != "" { - r.UpdatedAt, err = time.Parse(gophercloud.RFC3339MilliNoZ, s.UpdatedAt) - if err != nil { - r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) - if err != nil { - return err - } - } - } - - return nil -} - -// Spec represents an OpenStack clustering policy spec. -type Spec struct { - Description string `json:"description"` - Properties map[string]interface{} `json:"properties"` - Type string `json:"type"` - Version string `json:"-"` -} - -func (r *Spec) UnmarshalJSON(b []byte) error { - type tmp Spec - var s struct { - tmp - Version interface{} `json:"version"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Spec(s.tmp) - - switch t := s.Version.(type) { - case float64: - if t == 1 { - r.Version = fmt.Sprintf("%.1f", t) - } else { - r.Version = strconv.FormatFloat(t, 'f', -1, 64) - } - case string: - r.Version = t - } - - return nil -} - -func (r Spec) MarshalJSON() ([]byte, error) { - spec := struct { - Type string `json:"type"` - Version string `json:"version"` - Properties map[string]interface{} `json:"properties"` - }{ - Type: r.Type, - Version: r.Version, - Properties: r.Properties, - } - return json.Marshal(spec) -} - -// policyResult is the resposne of a base Policy result. -type policyResult struct { - gophercloud.Result -} - -// Extract interpets any policyResult-base result as a Policy. -func (r policyResult) Extract() (*Policy, error) { - var s struct { - Policy *Policy `json:"policy"` - } - err := r.ExtractInto(&s) - - return s.Policy, err -} - -// CreateResult is the result of an Update operation. Call its Extract -// method to interpret it as a Policy. -type CreateResult struct { - policyResult -} - -// GetResult is the result of a Get operation. Call its Extract method to -// interpret it as a Policy. -type GetResult struct { - policyResult -} - -// UpdateResult is the result of an Update operation. Call its Extract -// method to interpret it as a Policy. -type UpdateResult struct { - policyResult -} - -// ValidateResult is the result of a Validate operation. Call its Extract -// method to interpret it as a Policy. -type ValidateResult struct { - policyResult -} - -// DeleteResult is the result of a Delete operation. Call its Extract -// method to interpret it as a DeleteHeader. -type DeleteResult struct { - gophercloud.ErrResult -} - -// PolicyPage contains a list page of all policies from a List call. -type PolicyPage struct { - pagination.MarkerPageBase -} - -// 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 -} - -// LastMarker returns the last policy ID in a ListResult. -func (r PolicyPage) LastMarker() (string, error) { - policies, err := ExtractPolicies(r) - if err != nil { - return "", err - } - if len(policies) == 0 { - return "", nil - } - return policies[len(policies)-1].ID, nil -} - -// ExtractPolicies returns a slice of Policies from the List operation. -func ExtractPolicies(r pagination.Page) ([]Policy, error) { - var s struct { - Policies []Policy `json:"policies"` - } - err := (r.(PolicyPage)).ExtractInto(&s) - return s.Policies, err -} diff --git a/openstack/clustering/v1/policies/testing/doc.go b/openstack/clustering/v1/policies/testing/doc.go deleted file mode 100644 index 61bc1c3b6d..0000000000 --- a/openstack/clustering/v1/policies/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_policies_v1 -package testing diff --git a/openstack/clustering/v1/policies/testing/fixtures_test.go b/openstack/clustering/v1/policies/testing/fixtures_test.go deleted file mode 100644 index 9d1273737c..0000000000 --- a/openstack/clustering/v1/policies/testing/fixtures_test.go +++ /dev/null @@ -1,513 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const PolicyListBody1 = ` -{ - "policies": [ - { - "created_at": "2018-04-02T21:43:30.000000", - "data": {}, - "domain": null, - "id": "PolicyListBodyID1", - "name": "delpol", - "project": "018cd0909fb44cd5bc9b7a3cd664920e", - "spec": { - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": { - "criteria": "OLDEST_FIRST", - "destroy_after_deletion": true, - "grace_period": 60, - "reduce_desired_capacity": false - }, - "type": "senlin.policy.deletion", - "version": 1 - }, - "type": "senlin.policy.deletion-1.0", - "updated_at": "2018-04-02T00:19:12Z", - "user": "fe43e41739154b72818565e0d2580819" - } - ] -} -` - -const PolicyListBody2 = ` -{ - "policies": [ - { - "created_at": "2018-04-02T22:29:36.000000", - "data": {}, - "domain": null, - "id": "PolicyListBodyID2", - "name": "delpol2", - "project": "018cd0909fb44cd5bc9b7a3cd664920e", - "spec": { - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": { - "criteria": "OLDEST_FIRST", - "destroy_after_deletion": true, - "grace_period": 60, - "reduce_desired_capacity": false - }, - "type": "senlin.policy.deletion", - "version": "1.0" - }, - "type": "senlin.policy.deletion-1.0", - "updated_at": "2018-04-02T23:15:11.000000", - "user": "fe43e41739154b72818565e0d2580819" - } - ] -} -` - -const PolicyCreateBody = ` -{ - "policy": { - "created_at": "2018-04-04T00:18:36Z", - "data": {}, - "domain": null, - "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - "name": "delpol4", - "project": "018cd0909fb44cd5bc9b7a3cd664920e", - "spec": { - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": { - "hooks": { - "params": { - "queue": "zaqar_queue_name" - }, - "timeout": 180, - "type": "zaqar" - } - }, - "type": "senlin.policy.deletion", - "version": 1.1 - }, - "type": "senlin.policy.deletion-1.1", - "updated_at": null, - "user": "fe43e41739154b72818565e0d2580819" - } -} -` - -const PolicyGetBody = ` -{ - "policy": { - "created_at": "2018-04-02T21:43:30.000000", - "data": {}, - "domain": null, - "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - "name": "delpol", - "project": "018cd0909fb44cd5bc9b7a3cd664920e", - "spec": { - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": { - "criteria": "OLDEST_FIRST", - "destroy_after_deletion": true, - "grace_period": 60, - "reduce_desired_capacity": false - }, - "type": "senlin.policy.deletion", - "version": 1 - }, - "type": "senlin.policy.deletion-1.0", - "updated_at": "2018-04-02T00:19:12Z", - "user": "fe43e41739154b72818565e0d2580819" - } -} -` - -const PolicyUpdateBody = ` -{ - "policy": { - "created_at": "2018-04-02T21:43:30.000000", - "data": {}, - "domain": null, - "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - "name": "delpol4", - "project": "018cd0909fb44cd5bc9b7a3cd664920e", - "spec": { - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": { - "hooks": { - "params": { - "queue": "zaqar_queue_name" - }, - "timeout": 180, - "type": "zaqar" - } - }, - "type": "senlin.policy.deletion", - "version": 1.1 - }, - "type": "senlin.policy.deletion-1.1", - "updated_at": null, - "user": "fe43e41739154b72818565e0d2580819" - } -} -` - -const PolicyBadUpdateBody = ` -{ - "policy": { - "created_at": "invalid", - "data": {}, - "domain": null, - "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - "name": "delpol4", - "project": "018cd0909fb44cd5bc9b7a3cd664920e", - "spec": { - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": { - "hooks": { - "params": { - "queue": "zaqar_queue_name" - }, - "timeout": 180, - "type": "zaqar" - } - }, - "type": "senlin.policy.deletion", - "version": 1.1 - }, - "type": "invalid", - "updated_at": null, - "user": "fe43e41739154b72818565e0d2580819" - } -} -` - -const PolicyValidateBody = ` -{ - "policy": { - "created_at": "2018-04-02T21:43:30.000000", - "data": {}, - "domain": null, - "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - "name": "delpol4", - "project": "018cd0909fb44cd5bc9b7a3cd664920e", - "spec": { - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": { - "hooks": { - "params": { - "queue": "zaqar_queue_name" - }, - "timeout": 180, - "type": "zaqar" - } - }, - "type": "senlin.policy.deletion", - "version": 1.1 - }, - "type": "senlin.policy.deletion-1.1", - "updated_at": null, - "user": "fe43e41739154b72818565e0d2580819" - } -} -` - -const PolicyBadValidateBody = ` -{ - "policy": { - "created_at": "invalid", - "data": {}, - "domain": null, - "id": "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - "name": "delpol4", - "project": "018cd0909fb44cd5bc9b7a3cd664920e", - "spec": { - "description": "A policy for choosing victim node(s) from a cluster for deletion.", - "properties": { - "hooks": { - "params": { - "queue": "zaqar_queue_name" - }, - "timeout": 180, - "type": "zaqar" - } - }, - "type": "senlin.policy.deletion", - "version": 1.1 - }, - "type": "invalid", - "updated_at": null, - "user": "fe43e41739154b72818565e0d2580819" - } -} -` - -const PolicyDeleteRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" -const PolicyIDtoUpdate = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" -const PolicyIDtoGet = "b99b3ab4-3aa6-4fba-b827-69b88b9c544a" -const PolicyIDtoDelete = "1" - -var ExpectedPolicy1 = policies.Policy{ - CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), - Data: map[string]interface{}{}, - Domain: "", - ID: "PolicyListBodyID1", - Name: "delpol", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "criteria": "OLDEST_FIRST", - "destroy_after_deletion": true, - "grace_period": float64(60), - "reduce_desired_capacity": false, - }, - Type: "senlin.policy.deletion", - Version: "1.0", - }, - Type: "senlin.policy.deletion-1.0", - User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: time.Date(2018, 4, 2, 0, 19, 12, 0, time.UTC), -} - -var ExpectedPolicy2 = policies.Policy{ - CreatedAt: time.Date(2018, 4, 2, 22, 29, 36, 0, time.UTC), - Data: map[string]interface{}{}, - Domain: "", - ID: "PolicyListBodyID2", - Name: "delpol2", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "criteria": "OLDEST_FIRST", - "destroy_after_deletion": true, - "grace_period": float64(60), - "reduce_desired_capacity": false, - }, - Type: "senlin.policy.deletion", - Version: "1.0", - }, - Type: "senlin.policy.deletion-1.0", - User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: time.Date(2018, 4, 2, 23, 15, 11, 0, time.UTC), -} - -var ExpectedPolicies = [][]policies.Policy{ - {ExpectedPolicy1}, - {ExpectedPolicy2}, -} - -var ExpectedCreatePolicy = policies.Policy{ - CreatedAt: time.Date(2018, 4, 4, 0, 18, 36, 0, time.UTC), - Data: map[string]interface{}{}, - Domain: "", - ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - Name: "delpol4", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "hooks": map[string]interface{}{ - "params": map[string]interface{}{ - "queue": "zaqar_queue_name", - }, - "timeout": float64(180), - "type": "zaqar", - }, - }, - Type: "senlin.policy.deletion", - Version: "1.1", - }, - Type: "senlin.policy.deletion-1.1", - User: "fe43e41739154b72818565e0d2580819", -} - -var ExpectedGetPolicy = policies.Policy{ - CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), - Data: map[string]interface{}{}, - Domain: "", - ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - Name: "delpol", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "criteria": "OLDEST_FIRST", - "destroy_after_deletion": true, - "grace_period": float64(60), - "reduce_desired_capacity": false, - }, - Type: "senlin.policy.deletion", - Version: "1.0", - }, - Type: "senlin.policy.deletion-1.0", - User: "fe43e41739154b72818565e0d2580819", - UpdatedAt: time.Date(2018, 4, 2, 0, 19, 12, 0, time.UTC), -} - -var ExpectedUpdatePolicy = policies.Policy{ - CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), - Data: map[string]interface{}{}, - Domain: "", - ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - Name: "delpol4", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "hooks": map[string]interface{}{ - "params": map[string]interface{}{ - "queue": "zaqar_queue_name", - }, - "timeout": float64(180), - "type": "zaqar", - }, - }, - Type: "senlin.policy.deletion", - Version: "1.1", - }, - Type: "senlin.policy.deletion-1.1", - User: "fe43e41739154b72818565e0d2580819", -} - -var ExpectedValidatePolicy = policies.Policy{ - CreatedAt: time.Date(2018, 4, 2, 21, 43, 30, 0, time.UTC), - Data: map[string]interface{}{}, - Domain: "", - ID: "b99b3ab4-3aa6-4fba-b827-69b88b9c544a", - Name: "delpol4", - Project: "018cd0909fb44cd5bc9b7a3cd664920e", - - Spec: policies.Spec{ - Description: "A policy for choosing victim node(s) from a cluster for deletion.", - Properties: map[string]interface{}{ - "hooks": map[string]interface{}{ - "params": map[string]interface{}{ - "queue": "zaqar_queue_name", - }, - "timeout": float64(180), - "type": "zaqar", - }, - }, - Type: "senlin.policy.deletion", - Version: "1.1", - }, - Type: "senlin.policy.deletion-1.1", - User: "fe43e41739154b72818565e0d2580819", -} - -func HandlePolicyList(t *testing.T) { - th.Mux.HandleFunc("/v1/policies", 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) - - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, PolicyListBody1) - case "PolicyListBodyID1": - fmt.Fprintf(w, PolicyListBody2) - case "PolicyListBodyID2": - fmt.Fprintf(w, `{"policies":[]}`) - default: - t.Fatalf("Unexpected marker: [%s]", marker) - } - }) -} - -func HandlePolicyCreate(t *testing.T) { - th.Mux.HandleFunc("/v1/policies", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusCreated) - - fmt.Fprintf(w, PolicyCreateBody) - }) -} - -func HandlePolicyDelete(t *testing.T) { - th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoDelete, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("X-OpenStack-Request-Id", PolicyDeleteRequestID) - w.WriteHeader(http.StatusNoContent) - }) -} - -func HandlePolicyGet(t *testing.T) { - th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoGet, - 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, PolicyGetBody) - }) -} - -func HandlePolicyUpdate(t *testing.T) { - th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoUpdate, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, PolicyUpdateBody) - }) -} - -func HandleBadPolicyUpdate(t *testing.T) { - th.Mux.HandleFunc("/v1/policies/"+PolicyIDtoUpdate, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, PolicyBadUpdateBody) - }) -} - -func HandlePolicyValidate(t *testing.T) { - th.Mux.HandleFunc("/v1/policies/validate", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, PolicyValidateBody) - }) -} - -func HandleBadPolicyValidate(t *testing.T) { - th.Mux.HandleFunc("/v1/policies/validate", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, PolicyBadValidateBody) - }) -} diff --git a/openstack/clustering/v1/policies/testing/requests_test.go b/openstack/clustering/v1/policies/testing/requests_test.go deleted file mode 100644 index ffedb87983..0000000000 --- a/openstack/clustering/v1/policies/testing/requests_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policies" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestListPolicies(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandlePolicyList(t) - - listOpts := policies.ListOpts{ - Limit: 1, - } - - count := 0 - err := policies.List(fake.ServiceClient(), listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - actual, err := policies.ExtractPolicies(page) - if err != nil { - t.Errorf("Failed to extract policies: %v", err) - return false, err - } - - th.AssertDeepEquals(t, ExpectedPolicies[count], actual) - count++ - - return true, nil - }) - - th.AssertNoErr(t, err) - - if count != 2 { - t.Errorf("Expected 2 pages, got %d", count) - } -} - -func TestCreatePolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandlePolicyCreate(t) - - expected := ExpectedCreatePolicy - - opts := policies.CreateOpts{ - Name: ExpectedCreatePolicy.Name, - Spec: ExpectedCreatePolicy.Spec, - } - - actual, err := policies.Create(context.TODO(), fake.ServiceClient(), opts).Extract() - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, &expected, actual) -} - -func TestDeletePolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandlePolicyDelete(t) - - res := policies.Delete(context.TODO(), fake.ServiceClient(), PolicyIDtoDelete) - th.AssertNoErr(t, res.ExtractErr()) - - requestID := res.Header["X-Openstack-Request-Id"][0] - th.AssertEquals(t, PolicyDeleteRequestID, requestID) -} - -func TestUpdatePolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandlePolicyUpdate(t) - - expected := ExpectedUpdatePolicy - - opts := policies.UpdateOpts{ - Name: ExpectedUpdatePolicy.Name, - } - - actual, err := policies.Update(context.TODO(), fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, &expected, actual) -} - -func TestBadUpdatePolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleBadPolicyUpdate(t) - - opts := policies.UpdateOpts{ - Name: ExpectedUpdatePolicy.Name, - } - - _, err := policies.Update(context.TODO(), fake.ServiceClient(), PolicyIDtoUpdate, opts).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestValidatePolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandlePolicyValidate(t) - - expected := ExpectedValidatePolicy - - opts := policies.ValidateOpts{ - Spec: ExpectedValidatePolicy.Spec, - } - - actual, err := policies.Validate(context.TODO(), fake.ServiceClient(), opts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, &expected, actual) -} - -func TestBadValidatePolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleBadPolicyValidate(t) - - opts := policies.ValidateOpts{ - Spec: ExpectedValidatePolicy.Spec, - } - - _, err := policies.Validate(context.TODO(), fake.ServiceClient(), opts).Extract() - th.AssertEquals(t, false, err == nil) -} - -func TestGetPolicy(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandlePolicyGet(t) - - actual, err := policies.Get(context.TODO(), fake.ServiceClient(), PolicyIDtoGet).Extract() - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, ExpectedGetPolicy, *actual) -} diff --git a/openstack/clustering/v1/policies/urls.go b/openstack/clustering/v1/policies/urls.go deleted file mode 100644 index 94b76f8617..0000000000 --- a/openstack/clustering/v1/policies/urls.go +++ /dev/null @@ -1,32 +0,0 @@ -package policies - -import "github.com/gophercloud/gophercloud/v2" - -const ( - apiVersion = "v1" - apiName = "policies" -) - -func policyListURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func policyCreateURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func policyDeleteURL(client *gophercloud.ServiceClient, policyID string) string { - return client.ServiceURL(apiVersion, apiName, policyID) -} - -func policyGetURL(client *gophercloud.ServiceClient, policyID string) string { - return client.ServiceURL(apiVersion, apiName, policyID) -} - -func updateURL(client *gophercloud.ServiceClient, policyID string) string { - return client.ServiceURL(apiVersion, apiName, policyID) -} - -func validateURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName, "validate") -} diff --git a/openstack/clustering/v1/policytypes/doc.go b/openstack/clustering/v1/policytypes/doc.go deleted file mode 100644 index 3f920bfef3..0000000000 --- a/openstack/clustering/v1/policytypes/doc.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Package policytypes lists all policy types and shows details for a policy type -from the OpenStack Clustering Service. - -Example to List Policy Types - - allPages, err := policytypes.List(clusteringClient).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - allPolicyTypes, err := actions.ExtractPolicyTypes(allPages) - if err != nil { - panic(err) - } - - 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(context.TODO(), clusteringClient, policyTypeName).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%+v\n", policyTypeDetail) -*/ -package policytypes diff --git a/openstack/clustering/v1/policytypes/requests.go b/openstack/clustering/v1/policytypes/requests.go deleted file mode 100644 index 2da89848fa..0000000000 --- a/openstack/clustering/v1/policytypes/requests.go +++ /dev/null @@ -1,28 +0,0 @@ -package policytypes - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// List makes a request against the API to list policy types. -func List(client *gophercloud.ServiceClient) pagination.Pager { - url := policyTypeListURL(client) - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return PolicyTypePage{pagination.SinglePageBase(r)} - }) -} - -// Get makes a request against the API to get details for a policy type. -func Get(ctx context.Context, client *gophercloud.ServiceClient, policyTypeName string) (r GetResult) { - url := policyTypeGetURL(client, policyTypeName) - - resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/policytypes/results.go b/openstack/clustering/v1/policytypes/results.go deleted file mode 100644 index 1045f2bb17..0000000000 --- a/openstack/clustering/v1/policytypes/results.go +++ /dev/null @@ -1,72 +0,0 @@ -package policytypes - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// PolicyType represents a clustering policy type in the Openstack cloud. -type PolicyType struct { - Name string `json:"name"` - Version string `json:"version"` - SupportStatus map[string][]SupportStatusType `json:"support_status"` -} - -// SupportStatusType represents the support status information for a -// clustering policy type. -type SupportStatusType struct { - Status string `json:"status"` - Since string `json:"since"` -} - -// PolicyTypeDetail represents the detailed policy type information for a -// clustering policy type. -type PolicyTypeDetail struct { - Name string `json:"name"` - Schema map[string]interface{} `json:"schema"` - SupportStatus map[string][]SupportStatusType `json:"support_status,omitempty"` -} - -// policyTypeResult is the base result of a Policy Type operation. -type policyTypeResult struct { - gophercloud.Result -} - -// Extract interprets any policyTypeResult result as a PolicyTypeDetail. -func (r policyTypeResult) Extract() (*PolicyTypeDetail, error) { - var s struct { - PolicyType *PolicyTypeDetail `json:"policy_type"` - } - err := r.ExtractInto(&s) - return s.PolicyType, err -} - -// GetResult is the result of a Get operation. Call its Extract method to -// interpret it as a PolicyTypeDetail. -type GetResult struct { - policyTypeResult -} - -// PolicyTypePage contains a single page of all policy types from a List call. -type PolicyTypePage struct { - pagination.SinglePageBase -} - -// 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 -} - -// ExtractPolicyTypes returns a slice of PolicyTypes from a List operation. -func ExtractPolicyTypes(r pagination.Page) ([]PolicyType, error) { - var s struct { - PolicyTypes []PolicyType `json:"policy_types"` - } - err := (r.(PolicyTypePage)).ExtractInto(&s) - return s.PolicyTypes, err -} diff --git a/openstack/clustering/v1/policytypes/testing/doc.go b/openstack/clustering/v1/policytypes/testing/doc.go deleted file mode 100644 index 5bd30a4704..0000000000 --- a/openstack/clustering/v1/policytypes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_policytypes_v1 -package testing diff --git a/openstack/clustering/v1/policytypes/testing/fixtures_test.go b/openstack/clustering/v1/policytypes/testing/fixtures_test.go deleted file mode 100644 index 05b15985b7..0000000000 --- a/openstack/clustering/v1/policytypes/testing/fixtures_test.go +++ /dev/null @@ -1,235 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policytypes" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const FakePolicyTypetoGet = "fake-policytype" - -const PolicyTypeBody = ` -{ - "policy_types": [ - { - "name": "senlin.policy.affinity", - "version": "1.0", - "support_status": { - "1.0": [ - { - "status": "SUPPORTED", - "since": "2016.10" - } - ] - } - }, - { - "name": "senlin.policy.health", - "version": "1.0", - "support_status": { - "1.0": [ - { - "status": "EXPERIMENTAL", - "since": "2016.10" - } - ] - } - }, - { - "name": "senlin.policy.scaling", - "version": "1.0", - "support_status": { - "1.0": [ - { - "status": "SUPPORTED", - "since": "2016.04" - } - ] - } - }, - { - "name": "senlin.policy.region_placement", - "version": "1.0", - "support_status": { - "1.0": [ - { - "status": "EXPERIMENTAL", - "since": "2016.04" - }, - { - "status": "SUPPORTED", - "since": "2016.10" - } - ] - } - } - ] -} -` - -const PolicyTypeDetailBody = ` -{ - "policy_type": { - "name": "senlin.policy.batch-1.0", - "schema": { - "max_batch_size": { - "default": -1, - "description": "Maximum number of nodes that will be updated in parallel.", - "required": false, - "type": "Integer", - "updatable": false - }, - "min_in_service": { - "default": 1, - "description": "Minimum number of nodes in service when performing updates.", - "required": false, - "type": "Integer", - "updatable": false - }, - "pause_time": { - "default": 60, - "description": "Interval in seconds between update batches if any.", - "required": false, - "type": "Integer", - "updatable": false - } - }, - "support_status": { - "1.0": [ - { - "status": "EXPERIMENTAL", - "since": "2017.02" - } - ] - } - } -} -` - -var ExpectedPolicyType1 = policytypes.PolicyType{ - Name: "senlin.policy.affinity", - Version: "1.0", - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "SUPPORTED", - Since: "2016.10", - }, - }, - }, -} - -var ExpectedPolicyType2 = policytypes.PolicyType{ - Name: "senlin.policy.health", - Version: "1.0", - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "EXPERIMENTAL", - Since: "2016.10", - }, - }, - }, -} - -var ExpectedPolicyType3 = policytypes.PolicyType{ - Name: "senlin.policy.scaling", - Version: "1.0", - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "SUPPORTED", - Since: "2016.04", - }, - }, - }, -} - -var ExpectedPolicyType4 = policytypes.PolicyType{ - Name: "senlin.policy.region_placement", - Version: "1.0", - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "EXPERIMENTAL", - Since: "2016.04", - }, - { - Status: "SUPPORTED", - Since: "2016.10", - }, - }, - }, -} - -var ExpectedPolicyTypes = []policytypes.PolicyType{ - ExpectedPolicyType1, - ExpectedPolicyType2, - ExpectedPolicyType3, - ExpectedPolicyType4, -} - -var ExpectedPolicyTypeDetail = &policytypes.PolicyTypeDetail{ - Name: "senlin.policy.batch-1.0", - Schema: map[string]interface{}{ - "max_batch_size": map[string]interface{}{ - "default": float64(-1), - "description": "Maximum number of nodes that will be updated in parallel.", - "required": false, - "type": "Integer", - "updatable": false, - }, - "min_in_service": map[string]interface{}{ - "default": float64(1), - "description": "Minimum number of nodes in service when performing updates.", - "required": false, - "type": "Integer", - "updatable": false, - }, - "pause_time": map[string]interface{}{ - "default": float64(60), - "description": "Interval in seconds between update batches if any.", - "required": false, - "type": "Integer", - "updatable": false, - }, - }, - SupportStatus: map[string][]policytypes.SupportStatusType{ - "1.0": { - { - Status: "EXPERIMENTAL", - Since: "2017.02", - }, - }, - }, -} - -func HandlePolicyTypeList(t *testing.T) { - th.Mux.HandleFunc("/v1/policy-types", - 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, PolicyTypeBody) - }) -} - -func HandlePolicyTypeGet(t *testing.T) { - th.Mux.HandleFunc("/v1/policy-types/"+FakePolicyTypetoGet, - 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, PolicyTypeDetailBody) - }) -} diff --git a/openstack/clustering/v1/policytypes/testing/requests_test.go b/openstack/clustering/v1/policytypes/testing/requests_test.go deleted file mode 100644 index 827616df7a..0000000000 --- a/openstack/clustering/v1/policytypes/testing/requests_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/policytypes" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestListPolicyTypes(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandlePolicyTypeList(t) - - count := 0 - err := policytypes.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - - actual, err := policytypes.ExtractPolicyTypes(page) - if err != nil { - t.Errorf("Failed to extract policy types: %v", err) - return false, err - } - th.AssertDeepEquals(t, ExpectedPolicyTypes, actual) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if count != 1 { - t.Errorf("Expected 1 page, got %d", count) - } -} - -func TestGetPolicyType(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandlePolicyTypeGet(t) - - actual, err := policytypes.Get(context.TODO(), fake.ServiceClient(), FakePolicyTypetoGet).Extract() - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, ExpectedPolicyTypeDetail, actual) -} diff --git a/openstack/clustering/v1/policytypes/urls.go b/openstack/clustering/v1/policytypes/urls.go deleted file mode 100644 index 4d1c420b8b..0000000000 --- a/openstack/clustering/v1/policytypes/urls.go +++ /dev/null @@ -1,16 +0,0 @@ -package policytypes - -import "github.com/gophercloud/gophercloud/v2" - -const ( - apiVersion = "v1" - apiName = "policy-types" -) - -func policyTypeListURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func policyTypeGetURL(client *gophercloud.ServiceClient, policyTypeName string) string { - return client.ServiceURL(apiVersion, apiName, policyTypeName) -} diff --git a/openstack/clustering/v1/profiles/doc.go b/openstack/clustering/v1/profiles/doc.go deleted file mode 100644 index 4bf27e79a0..0000000000 --- a/openstack/clustering/v1/profiles/doc.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Package profiles provides information and interaction with profiles through -the OpenStack Clustering service. - -Example to Create a Profile - - networks := []map[string]interface{} { - {"network": "test-network"}, - } - - props := map[string]interface{}{ - "name": "test_gophercloud_profile", - "flavor": "t2.micro", - "image": "centos7.3-latest", - "networks": networks, - "security_groups": "", - } - - createOpts := profiles.CreateOpts { - Name: "test_profile", - Spec: profiles.Spec{ - Type: "os.nova.server", - Version: "1.0", - Properties: props, - }, - } - - profile, err := profiles.Create(context.TODO(), serviceClient, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Println("Profile", profile) - -Example to Get a Profile - - profile, err := profiles.Get(context.TODO(), serviceClient, "profile-name").Extract() - if err != nil { - panic(err) - } - - fmt.Print("profile", profile) - -Example to List Profiles - - listOpts := profiles.ListOpts{ - Limit: 2, - } - - profiles.List(serviceClient, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - allProfiles, err := profiles.ExtractProfiles(page) - if err != nil { - panic(err) - } - - for _, profile := range allProfiles { - fmt.Printf("%+v\n", profile) - } - return true, nil - }) - -Example to Update a Profile - - updateOpts := profiles.UpdateOpts{ - Name: "new-name", - } - - profile, err := profiles.Update(context.TODO(), serviceClient, profileName, updateOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Print("profile", profile) - -Example to Delete a Profile - - profileID := "6dc6d336e3fc4c0a951b5698cd1236ee" - err := profiles.Delete(context.TODO(), serviceClient, profileID).ExtractErr() - if err != nil { - panic(err) - } - -Example to Validate a profile - - serviceClient.Microversion = "1.2" - - validateOpts := profiles.ValidateOpts{ - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.micro", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - } - - profile, err := profiles.Validate(context.TODO(), serviceClient, validateOpts).Extract() - if err != nil { - panic(err) - } -*/ -package profiles diff --git a/openstack/clustering/v1/profiles/requests.go b/openstack/clustering/v1/profiles/requests.go deleted file mode 100644 index 7673135aee..0000000000 --- a/openstack/clustering/v1/profiles/requests.go +++ /dev/null @@ -1,155 +0,0 @@ -package profiles - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToProfileCreateMap() (map[string]interface{}, error) -} - -// CreateOpts represents options used for creating a profile. -type CreateOpts struct { - Name string `json:"name" required:"true"` - Metadata map[string]interface{} `json:"metadata,omitempty"` - Spec Spec `json:"spec" required:"true"` -} - -// ToProfileCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToProfileCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "profile") -} - -// Create requests the creation of a new profile on the server. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToProfileCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves detail of a single profile. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToProfileListQuery() (string, error) -} - -// ListOpts represents options used to list profiles. -type ListOpts struct { - GlobalProject *bool `q:"global_project"` - Limit int `q:"limit"` - Marker string `q:"marker"` - Name string `q:"name"` - Sort string `q:"sort"` - Type string `q:"type"` -} - -// ToProfileListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToProfileListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to provide a list of profiles. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToProfileListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ProfilePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToProfileUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts represents options used to update a profile. -type UpdateOpts struct { - Metadata map[string]interface{} `json:"metadata,omitempty"` - Name string `json:"name,omitempty"` -} - -// ToProfileUpdateMap constructs a request body from UpdateOpts. -func (opts UpdateOpts) ToProfileUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "profile") -} - -// Update updates a profile. -func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToProfileUpdateMap() - if err != nil { - r.Err = err - return r - } - resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Delete deletes the specified profile via profile id. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ValidateOptsBuilder allows extensions to add additional parameters to the -// Validate request. -type ValidateOptsBuilder interface { - ToProfileValidateMap() (map[string]interface{}, error) -} - -// ValidateOpts params -type ValidateOpts struct { - Spec Spec `json:"spec" required:"true"` -} - -// ToProfileValidateMap formats a CreateOpts into a body map. -func (opts ValidateOpts) ToProfileValidateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "profile") -} - -// Validate profile. -func Validate(ctx context.Context, client *gophercloud.ServiceClient, opts ValidateOpts) (r ValidateResult) { - b, err := opts.ToProfileValidateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, validateURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/profiles/results.go b/openstack/clustering/v1/profiles/results.go deleted file mode 100644 index 6cdd21f75f..0000000000 --- a/openstack/clustering/v1/profiles/results.go +++ /dev/null @@ -1,170 +0,0 @@ -package profiles - -import ( - "encoding/json" - "fmt" - "strconv" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Profile represent a detailed profile. -type Profile struct { - CreatedAt time.Time `json:"-"` - Domain string `json:"domain"` - ID string `json:"id"` - Metadata map[string]interface{} `json:"metadata"` - Name string `json:"name"` - Project string `json:"project"` - Spec Spec `json:"spec"` - Type string `json:"type"` - UpdatedAt time.Time `json:"-"` - User string `json:"user"` -} - -func (r *Profile) UnmarshalJSON(b []byte) error { - type tmp Profile - var s struct { - tmp - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - } - - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Profile(s.tmp) - - if s.CreatedAt != "" { - r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) - if err != nil { - return err - } - } - - if s.UpdatedAt != "" { - r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) - if err != nil { - return err - } - } - - return nil -} - -// Spec represents a profile spec. -type Spec struct { - Type string `json:"type"` - Version string `json:"-"` - Properties map[string]interface{} `json:"properties"` -} - -func (r *Spec) UnmarshalJSON(b []byte) error { - type tmp Spec - var s struct { - tmp - Version interface{} `json:"version"` - } - - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Spec(s.tmp) - - switch t := s.Version.(type) { - case float64: - if t == 1 { - r.Version = fmt.Sprintf("%.1f", t) - } else { - r.Version = strconv.FormatFloat(t, 'f', -1, 64) - } - case string: - r.Version = t - } - - return nil -} - -func (r Spec) MarshalJSON() ([]byte, error) { - spec := struct { - Type string `json:"type"` - Version string `json:"version"` - Properties map[string]interface{} `json:"properties"` - }{ - Type: r.Type, - Version: r.Version, - Properties: r.Properties, - } - return json.Marshal(spec) -} - -// commonResult is the base result of a Profile operation. -type commonResult struct { - gophercloud.Result -} - -// Extract provides access to Profile returned by the Get and Create functions. -func (r commonResult) Extract() (*Profile, error) { - var s struct { - Profile *Profile `json:"profile"` - } - err := r.ExtractInto(&s) - return s.Profile, err -} - -// CreateResult is the result of a Create operation. Call its Extract -// method to interpret it as a Profile. -type CreateResult struct { - commonResult -} - -// GetResult is the result of a Get operations. Call its Extract -// method to interpret it as a Profile. -type GetResult struct { - commonResult -} - -// UpdateResult is the result of a Update operations. Call its Extract -// method to interpret it as a Profile. -type UpdateResult struct { - commonResult -} - -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// ValidateResult is the response of a Validate operations. -type ValidateResult struct { - commonResult -} - -// ProfilePage contains a single page of all profiles from a List operation. -type ProfilePage struct { - pagination.LinkedPageBase -} - -// 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 -} - -// ExtractProfiles returns a slice of Profiles from the List operation. -func ExtractProfiles(r pagination.Page) ([]Profile, error) { - var s struct { - Profiles []Profile `json:"profiles"` - } - err := (r.(ProfilePage)).ExtractInto(&s) - return s.Profiles, err -} diff --git a/openstack/clustering/v1/profiles/testing/doc.go b/openstack/clustering/v1/profiles/testing/doc.go deleted file mode 100644 index 2a99a8a64b..0000000000 --- a/openstack/clustering/v1/profiles/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_profiles_v1 -package testing diff --git a/openstack/clustering/v1/profiles/testing/fixtures_test.go b/openstack/clustering/v1/profiles/testing/fixtures_test.go deleted file mode 100644 index cd038c6a64..0000000000 --- a/openstack/clustering/v1/profiles/testing/fixtures_test.go +++ /dev/null @@ -1,463 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const CreateResponse = ` -{ - "profile": { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "test-profile", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "centos7.3-latest", - "name": "centos_server", - "networks": [ - { - "network": "private-network" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "2016-01-03T17:22:23Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -var ExpectedCreate = profiles.Profile{ - CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "test-profile", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.small", - "image": "centos7.3-latest", - "name": "centos_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private-network"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), - User: "5e5bf8027826429c96af157f68dc9072", -} - -const GetResponse = ` -{ - "profile": { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -var ExpectedGet = profiles.Profile{ - CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": float64(1), - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - User: "5e5bf8027826429c96af157f68dc9072", -} - -const ListResponse = ` -{ - "profiles": [ - { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": 1.0 - }, - "type": "os.nova.server-1.0", - "updated_at": "2016-01-03T17:22:23Z", - "user": "5e5bf8027826429c96af157f68dc9072" - }, - { - "created_at": null, - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": 1.0 - }, - "type": "os.nova.server-1.0", - "updated_at": null, - "user": "5e5bf8027826429c96af157f68dc9072" - }, - { - "created_at": "", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "", - "user": "5e5bf8027826429c96af157f68dc9072" - } - ] -}` - -var ExpectedListProfile1 = profiles.Profile{ - CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), - User: "5e5bf8027826429c96af157f68dc9072", -} - -var ExpectedListProfile2 = profiles.Profile{ - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - User: "5e5bf8027826429c96af157f68dc9072", -} - -var ExpectedListProfile3 = profiles.Profile{ - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.small", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - User: "5e5bf8027826429c96af157f68dc9072", -} - -var ExpectedList = []profiles.Profile{ - ExpectedListProfile1, - ExpectedListProfile2, - ExpectedListProfile3, -} - -const UpdateResponse = ` -{ - "profile": { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": { - "foo": "bar" - }, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": 1, - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "2016-01-03T17:22:23Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -var ExpectedUpdate = profiles.Profile{ - CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{"foo": "bar"}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": float64(1), - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), - User: "5e5bf8027826429c96af157f68dc9072", -} - -const ValidateResponse = ` -{ - "profile": { - "created_at": "2016-01-03T16:22:23Z", - "domain": null, - "id": "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - "metadata": {}, - "name": "pserver", - "project": "42d9e9663331431f97b75e25136307ff", - "spec": { - "properties": { - "flavor": "t2.micro", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": [ - { - "network": "private" - } - ] - }, - "type": "os.nova.server", - "version": "1.0" - }, - "type": "os.nova.server-1.0", - "updated_at": "2016-01-03T17:22:23Z", - "user": "5e5bf8027826429c96af157f68dc9072" - } -}` - -var ExpectedValidate = profiles.Profile{ - CreatedAt: time.Date(2016, 1, 3, 16, 22, 23, 0, time.UTC), - Domain: "", - ID: "9e1c6f42-acf5-4688-be2c-8ce954ef0f23", - Metadata: map[string]interface{}{}, - Name: "pserver", - Project: "42d9e9663331431f97b75e25136307ff", - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.micro", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - Type: "os.nova.server-1.0", - UpdatedAt: time.Date(2016, 1, 3, 17, 22, 23, 0, time.UTC), - User: "5e5bf8027826429c96af157f68dc9072", -} - -func HandleCreateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/profiles", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, CreateResponse) - }) -} - -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", 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, GetResponse) - }) -} - -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/profiles", 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, ListResponse) - }) -} - -func HandleUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/profiles/9e1c6f42-acf5-4688-be2c-8ce954ef0f23", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, UpdateResponse) - }) -} - -func HandleDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/profiles/6dc6d336e3fc4c0a951b5698cd1236ee", 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) - }) -} - -func HandleValidateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/profiles/validate", 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, "OpenStack-API-Version", "clustering 1.2") - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ValidateResponse) - }) -} diff --git a/openstack/clustering/v1/profiles/testing/requests_test.go b/openstack/clustering/v1/profiles/testing/requests_test.go deleted file mode 100644 index bb4cd41568..0000000000 --- a/openstack/clustering/v1/profiles/testing/requests_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiles" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestCreateProfile(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCreateSuccessfully(t) - - networks := []map[string]interface{}{ - {"network": "private-network"}, - } - - props := map[string]interface{}{ - "name": "test_gopher_cloud_profile", - "flavor": "t2.small", - "image": "centos7.3-latest", - "networks": networks, - "security_groups": "", - } - - createOpts := &profiles.CreateOpts{ - Name: "TestProfile", - Spec: profiles.Spec{ - Type: "os.nova.server", - Version: "1.0", - Properties: props, - }, - } - - profile, err := profiles.Create(context.TODO(), fake.ServiceClient(), createOpts).Extract() - if err != nil { - t.Errorf("Failed to extract profile: %v", err) - } - - th.AssertDeepEquals(t, ExpectedCreate, *profile) -} - -func TestGetProfile(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetSuccessfully(t) - - actual, err := profiles.Get(context.TODO(), fake.ServiceClient(), ExpectedGet.ID).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedGet, *actual) -} - -func TestListProfiles(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListSuccessfully(t) - - var iFalse bool - listOpts := profiles.ListOpts{ - GlobalProject: &iFalse, - } - - count := 0 - err := profiles.List(fake.ServiceClient(), listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := profiles.ExtractProfiles(page) - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedList, actual) - - return true, nil - }) - - th.AssertNoErr(t, err) - - if count != 1 { - t.Errorf("Expected 1 page of profiles, got %d pages instead", count) - } -} - -func TestUpdateProfile(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleUpdateSuccessfully(t) - - updateOpts := profiles.UpdateOpts{ - Name: "pserver", - } - - actual, err := profiles.Update(context.TODO(), fake.ServiceClient(), ExpectedUpdate.ID, updateOpts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedUpdate, *actual) -} - -func TestDeleteProfile(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleDeleteSuccessfully(t) - - deleteResult := profiles.Delete(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") - th.AssertNoErr(t, deleteResult.ExtractErr()) -} - -func TestValidateProfile(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleValidateSuccessfully(t) - - validateOpts := profiles.ValidateOpts{ - Spec: profiles.Spec{ - Properties: map[string]interface{}{ - "flavor": "t2.micro", - "image": "cirros-0.3.4-x86_64-uec", - "key_name": "oskey", - "name": "cirros_server", - "networks": []interface{}{ - map[string]interface{}{"network": "private"}, - }, - }, - Type: "os.nova.server", - Version: "1.0", - }, - } - - client := fake.ServiceClient() - client.Microversion = "1.2" - client.Type = "clustering" - - profile, err := profiles.Validate(context.TODO(), client, validateOpts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedValidate, *profile) -} diff --git a/openstack/clustering/v1/profiles/urls.go b/openstack/clustering/v1/profiles/urls.go deleted file mode 100644 index 3cc1f2102e..0000000000 --- a/openstack/clustering/v1/profiles/urls.go +++ /dev/null @@ -1,38 +0,0 @@ -package profiles - -import "github.com/gophercloud/gophercloud/v2" - -var apiVersion = "v1" -var apiName = "profiles" - -func commonURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func createURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func idURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id) -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func listURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func updateURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func deleteURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func validateURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName, "validate") -} diff --git a/openstack/clustering/v1/profiletypes/doc.go b/openstack/clustering/v1/profiletypes/doc.go deleted file mode 100644 index b3349c5292..0000000000 --- a/openstack/clustering/v1/profiletypes/doc.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Package profiletypes lists all profile types and shows details for a profile type from the OpenStack -Clustering Service. - -Example to List ProfileType - - err = profiletypes.List(serviceClient).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - profileTypes, err := profiletypes.ExtractProfileTypes(page) - if err != nil { - return false, err - } - - for _, profileType := range profileTypes { - fmt.Printf("%+v\n", profileType) - } - return true, nil - }) - -Example to Get a ProfileType - - profileTypeName := "os.nova.server" - profileType, err := profiletypes.Get(context.TODO(), clusteringClient, profileTypeName).Extract() - if err != nil { - panic(err) - } - fmt.Printf("%+v\n", profileType) - -Example of list operations supported by a profile type - - serviceClient.Microversion = "1.5" - - profileTypeName := "os.nova.server-1.0" - allPages, err := profiletypes.ListOps(serviceClient, profileTypeName).AllPages(context.TODO()) - if err != nil { - panic(err) - } - - ops, err := profiletypes.ExtractOps(allPages) - if err != nil { - panic(err) - } - - for _, op := range ops { - fmt.Printf("%+v\n", op) - } -*/ -package profiletypes diff --git a/openstack/clustering/v1/profiletypes/requests.go b/openstack/clustering/v1/profiletypes/requests.go deleted file mode 100644 index 91124bc6de..0000000000 --- a/openstack/clustering/v1/profiletypes/requests.go +++ /dev/null @@ -1,30 +0,0 @@ -package profiletypes - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, - &gophercloud.RequestOpts{OkCodes: []int{200}}) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// List makes a request against the API to list profile types. -func List(client *gophercloud.ServiceClient) pagination.Pager { - url := listURL(client) - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ProfileTypePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -func ListOps(client *gophercloud.ServiceClient, id string) pagination.Pager { - url := listOpsURL(client, id) - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return OperationPage{pagination.SinglePageBase(r)} - }) -} diff --git a/openstack/clustering/v1/profiletypes/results.go b/openstack/clustering/v1/profiletypes/results.go deleted file mode 100644 index fe0fd2d8b0..0000000000 --- a/openstack/clustering/v1/profiletypes/results.go +++ /dev/null @@ -1,71 +0,0 @@ -package profiletypes - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// GetResult is the response of a Get operations. -type GetResult struct { - commonResult -} - -type Schema map[string]interface{} -type SupportStatus map[string]interface{} - -type ProfileType struct { - Name string `json:"name"` - Schema map[string]Schema `json:"schema"` - SupportStatus map[string][]SupportStatus `json:"support_status"` -} - -func (r commonResult) Extract() (*ProfileType, error) { - var s struct { - ProfileType *ProfileType `json:"profile_type"` - } - err := r.ExtractInto(&s) - return s.ProfileType, err -} - -// ExtractProfileTypes provides access to the list of profiles in a page acquired from the List operation. -func ExtractProfileTypes(r pagination.Page) ([]ProfileType, error) { - var s struct { - ProfileTypes []ProfileType `json:"profile_types"` - } - err := (r.(ProfileTypePage)).ExtractInto(&s) - return s.ProfileTypes, err -} - -// ProfileTypePage contains a single page of all profiles from a List call. -type ProfileTypePage struct { - pagination.LinkedPageBase -} - -// 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 -} - -// OperationPage contains a single page of all profile type operations from a ListOps call. -type OperationPage struct { - pagination.SinglePageBase -} - -// ExtractOps provides access to the list of operations in a page acquired from the ListOps operation. -func ExtractOps(r pagination.Page) (map[string]interface{}, error) { - var s struct { - Operations map[string]interface{} `json:"operations"` - } - err := (r.(OperationPage)).ExtractInto(&s) - return s.Operations, err -} diff --git a/openstack/clustering/v1/profiletypes/testing/doc.go b/openstack/clustering/v1/profiletypes/testing/doc.go deleted file mode 100644 index 7890e62543..0000000000 --- a/openstack/clustering/v1/profiletypes/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_profiletypes_v1 -package testing diff --git a/openstack/clustering/v1/profiletypes/testing/fixtures_test.go b/openstack/clustering/v1/profiletypes/testing/fixtures_test.go deleted file mode 100644 index df18955840..0000000000 --- a/openstack/clustering/v1/profiletypes/testing/fixtures_test.go +++ /dev/null @@ -1,296 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiletypes" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const ProfileTypeRequestID = "req-7328d1b0-9945-456f-b2cd-5166b77d14a8" -const ListResponse = ` -{ - "profile_types": [ - { - "name": "os.nova.server-1.0", - "schema": { - "context": { - "description": "Customized security context for operating containers.", - "required": false, - "type": "Map", - "updatable": false - }, - "name": { - "description": "The name of the container.", - "required": false, - "type": "Map", - "updatable": false - } - } - }, - { - "name": "os.heat.stack-1.0", - "schema": { - "context": { - "default": {}, - "description": "A dictionary for specifying the customized context for stack operations", - "required": false, - "type": "Map", - "updatable": false - }, - "disable_rollback": { - "default": true, - "description": "A boolean specifying whether a stack operation can be rolled back.", - "required": false, - "type": "Boolean", - "updatable": true - }, - "environment": { - "default": {}, - "description": "A map that specifies the environment used for stack operations.", - "required": false, - "type": "Map", - "updatable": true - }, - "files": { - "default": {}, - "description": "Contents of files referenced by the template, if any.", - "required": false, - "type": "Map", - "updatable": true - } - }, - "support_status": { - "1.0": [ - { - "status": "SUPPORTED", - "since": "2016.04" - } - ] - } - } - ] -} -` - -const GetResponse1 = ` -{ - "profile_type": { - "name": "os.nova.server-1.0", - "schema": { - "context": { - "description": "Customized security context for operating containers.", - "required": false, - "type": "Map", - "updatable": false - }, - "name": { - "description": "The name of the container.", - "required": false, - "type": "Map", - "updatable": false - } - } - } -} -` - -const GetResponse15 = ` -{ - "profile_type": { - "name": "os.heat.stack-1.0", - "schema": { - "context": { - "default": {}, - "description": "A dictionary for specifying the customized context for stack operations", - "required": false, - "type": "Map", - "updatable": false - }, - "disable_rollback": { - "default": true, - "description": "A boolean specifying whether a stack operation can be rolled back.", - "required": false, - "type": "Boolean", - "updatable": true - }, - "environment": { - "default": {}, - "description": "A map that specifies the environment used for stack operations.", - "required": false, - "type": "Map", - "updatable": true - }, - "files": { - "default": {}, - "description": "Contents of files referenced by the template, if any.", - "required": false, - "type": "Map", - "updatable": true - } - }, - "support_status": { - "1.0": [ - { - "status": "SUPPORTED", - "since": "2016.04" - } - ] - } - } -} -` - -var ExpectedProfileType1 = profiletypes.ProfileType{ - Name: "os.nova.server-1.0", - Schema: map[string]profiletypes.Schema{ - "context": { - "description": "Customized security context for operating containers.", - "required": false, - "type": "Map", - "updatable": false, - }, - "name": { - "description": "The name of the container.", - "required": false, - "type": "Map", - "updatable": false, - }, - }, -} - -var ExpectedProfileType15 = profiletypes.ProfileType{ - Name: "os.heat.stack-1.0", - Schema: map[string]profiletypes.Schema{ - "context": { - "default": map[string]interface{}{}, - "description": "A dictionary for specifying the customized context for stack operations", - "required": false, - "type": "Map", - "updatable": false, - }, - "disable_rollback": { - "default": true, - "description": "A boolean specifying whether a stack operation can be rolled back.", - "required": false, - "type": "Boolean", - "updatable": true, - }, - "environment": { - "default": map[string]interface{}{}, - "description": "A map that specifies the environment used for stack operations.", - "required": false, - "type": "Map", - "updatable": true, - }, - "files": { - "default": map[string]interface{}{}, - "description": "Contents of files referenced by the template, if any.", - "required": false, - "type": "Map", - "updatable": true, - }, - }, - SupportStatus: map[string][]profiletypes.SupportStatus{ - "1.0": { - { - "status": "SUPPORTED", - "since": "2016.04", - }, - }, - }, -} - -var ExpectedProfileTypes = []profiletypes.ProfileType{ExpectedProfileType1, ExpectedProfileType15} - -func HandleList1Successfully(t *testing.T) { - th.Mux.HandleFunc("/v1/profile-types", 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, ListResponse) - }) -} - -func HandleGet1Successfully(t *testing.T, id string) { - th.Mux.HandleFunc("/v1/profile-types/"+id, 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.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, GetResponse1) - }) -} - -func HandleGet15Successfully(t *testing.T, id string) { - th.Mux.HandleFunc("/v1/profile-types/"+id, 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.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, GetResponse15) - }) -} - -const ProfileTypeName = "os.nova.server-1.0" - -const ListOpsResponse = ` -{ - "operations": { - "pause": { - "description": "Pause the server from running.", - "parameter": null - }, - "change_password": { - "description": "Change the administrator password.", - "parameters": { - "admin_pass": { - "description": "New password for the administrator.", - "required": false, - "type": "String" - } - } - } - } -} -` - -var ExpectedOps = map[string]interface{}{ - "change_password": map[string]interface{}{ - "description": "Change the administrator password.", - "parameters": map[string]interface{}{ - "admin_pass": map[string]interface{}{ - "description": "New password for the administrator.", - "required": false, - "type": "String", - }, - }, - }, - "pause": map[string]interface{}{ - "description": "Pause the server from running.", - "parameter": nil, - }, -} - -func HandleListOpsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/profile-types/"+ProfileTypeName+"/ops", 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.Header().Add("X-OpenStack-Request-ID", ProfileTypeRequestID) - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOpsResponse) - }) -} diff --git a/openstack/clustering/v1/profiletypes/testing/requests_test.go b/openstack/clustering/v1/profiletypes/testing/requests_test.go deleted file mode 100644 index 9dc1c55385..0000000000 --- a/openstack/clustering/v1/profiletypes/testing/requests_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/profiletypes" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestListProfileTypes(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleList1Successfully(t) - - pageCount := 0 - err := profiletypes.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - pageCount++ - actual, err := profiletypes.ExtractProfileTypes(page) - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, ExpectedProfileTypes, actual) - - return true, nil - }) - th.AssertNoErr(t, err) - - if pageCount != 1 { - t.Errorf("Expected 1 page, got %d", pageCount) - } -} - -func TestGetProfileType10(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGet1Successfully(t, ExpectedProfileType1.Name) - - actual, err := profiletypes.Get(context.TODO(), fake.ServiceClient(), ExpectedProfileType1.Name).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedProfileType1, *actual) -} - -func TestGetProfileType15(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGet15Successfully(t, ExpectedProfileType15.Name) - - actual, err := profiletypes.Get(context.TODO(), fake.ServiceClient(), ExpectedProfileType15.Name).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedProfileType15, *actual) -} - -func TestListProfileTypesOps(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListOpsSuccessfully(t) - - allPages, err := profiletypes.ListOps(fake.ServiceClient(), ProfileTypeName).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allPolicyTypes, err := profiletypes.ExtractOps(allPages) - th.AssertNoErr(t, err) - - for k, v := range allPolicyTypes { - tools.PrintResource(t, k) - tools.PrintResource(t, v) - } -} diff --git a/openstack/clustering/v1/profiletypes/urls.go b/openstack/clustering/v1/profiletypes/urls.go deleted file mode 100644 index 9577fc3843..0000000000 --- a/openstack/clustering/v1/profiletypes/urls.go +++ /dev/null @@ -1,28 +0,0 @@ -package profiletypes - -import "github.com/gophercloud/gophercloud/v2" - -const ( - apiVersion = "v1" - apiName = "profile-types" -) - -func commonURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func profileTypeURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id) -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return profileTypeURL(client, id) -} - -func listURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func listOpsURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "ops") -} diff --git a/openstack/clustering/v1/receivers/doc.go b/openstack/clustering/v1/receivers/doc.go deleted file mode 100644 index a8316c9fc9..0000000000 --- a/openstack/clustering/v1/receivers/doc.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Package receivers provides information and interaction with the receivers through -the OpenStack Clustering service. - -Example to Create a Receiver - - createOpts := receivers.CreateOpts{ - Action: "CLUSTER_DEL_NODES", - ClusterID: "b7b870ee-d3c5-4a93-b9d7-846c53b2c2dc", - Name: "test_receiver", - Type: receivers.WebhookReceiver, - } - - receiver, err := receivers.Create(context.TODO(), serviceClient, createOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%v\n", receiver) - -Example to Get a Receiver - - receiver, err := receivers.Get(context.TODO(), serviceClient, "receiver-name").Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%v\n", receiver) - -Example to Delete receiver - - receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" - err := receivers.Delete(context.TODO(), serviceClient, receiverID).ExtractErr() - if err != nil { - panic(err) - } - - fmt.Printf("%v\n", receiver) - -Example to Update Receiver - - updateOpts := receivers.UpdateOpts{ - Name: "new-name", - } - - receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" - receiver, err := receivers.Update(context.TODO(), serviceClient, receiverID, updateOpts).Extract() - if err != nil { - panic(err) - } - - fmt.Printf("%v\n", receiver) - -Example to List Receivers - - listOpts := receivers.ListOpts{ - Limit: 2, - } - - receivers.List(serviceClient, listOpts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - allReceivers, err := receivers.ExtractReceivers(page) - if err != nil { - panic(err) - } - - for _, receiver := range allReceivers { - fmt.Printf("%+v\n", receiver) - } - return true, nil - }) - -Example to Notify a Receiver - - receiverID := "6dc6d336e3fc4c0a951b5698cd1236ee" - requestID, err := receivers.Notify(context.TODO(), serviceClient, receiverID).Extract() - if err != nil { - panic(err) - } -*/ -package receivers diff --git a/openstack/clustering/v1/receivers/requests.go b/openstack/clustering/v1/receivers/requests.go deleted file mode 100644 index 99c2cc1e2c..0000000000 --- a/openstack/clustering/v1/receivers/requests.go +++ /dev/null @@ -1,147 +0,0 @@ -package receivers - -import ( - "context" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// ReceiverType represents a valid type of receiver -type ReceiverType string - -const ( - WebhookReceiver ReceiverType = "webhook" - MessageReceiver ReceiverType = "message" -) - -// CreateOptsBuilder allows extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToReceiverCreateMap() (map[string]interface{}, error) -} - -// CreatOpts represents options used to create a receiver. -type CreateOpts struct { - Name string `json:"name" required:"true"` - ClusterID string `json:"cluster_id,omitempty"` - Type ReceiverType `json:"type" required:"true"` - Action string `json:"action,omitempty"` - Actor map[string]interface{} `json:"actor,omitempty"` - Params map[string]interface{} `json:"params,omitempty"` -} - -// ToReceiverCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToReceiverCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "receiver") -} - -// Create requests the creation of a new receiver. -func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToReceiverCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Post(ctx, createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToReceiverUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts represents options used to update a receiver. -type UpdateOpts struct { - Name string `json:"name,omitempty"` - Action string `json:"action,omitempty"` - Params map[string]interface{} `json:"params,omitempty"` -} - -// ToReceiverUpdateMap constructs a request body from UpdateOpts. -func (opts UpdateOpts) ToReceiverUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "receiver") -} - -// Update requests the update of a receiver. -func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToReceiverUpdateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Patch(ctx, updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Get retrieves details of a single receiver. Use Extract to convert its result into a Receiver. -func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { - resp, err := client.Get(ctx, getURL(client, id), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// ListOptsBuilder allows extensions to add additional parameters to the -// List request. -type ListOptsBuilder interface { - ToReceiverListQuery() (string, error) -} - -// ListOpts represents options used to list recievers. -type ListOpts struct { - Limit int `q:"limit"` - Marker string `q:"marker"` - Sort string `q:"sort"` - GlobalProject *bool `q:"global_project"` - Name string `q:"name"` - Type string `q:"type"` - ClusterID string `q:"cluster_id"` - Action string `q:"action"` - User string `q:"user"` -} - -// ToReceiverListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToReceiverListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// List instructs OpenStack to provide a list of cluster. -func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listURL(client) - if opts != nil { - query, err := opts.ToReceiverListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ReceiverPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Delete deletes the specified receiver ID. -func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := client.Delete(ctx, deleteURL(client, id), nil) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} - -// Notify Notifies message type receiver -func Notify(ctx context.Context, client *gophercloud.ServiceClient, id string) (r NotifyResult) { - resp, err := client.Post(ctx, notifyURL(client, id), nil, nil, &gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/receivers/results.go b/openstack/clustering/v1/receivers/results.go deleted file mode 100644 index 3c6cb1590e..0000000000 --- a/openstack/clustering/v1/receivers/results.go +++ /dev/null @@ -1,130 +0,0 @@ -package receivers - -import ( - "encoding/json" - "time" - - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/pagination" -) - -// Receiver represent a detailed receiver -type Receiver struct { - Action string `json:"action"` - Actor map[string]interface{} `json:"actor"` - Channel map[string]interface{} `json:"channel"` - ClusterID string `json:"cluster_id"` - CreatedAt time.Time `json:"-"` - Domain string `json:"domain"` - ID string `json:"id"` - Name string `json:"name"` - Params map[string]interface{} `json:"params"` - Project string `json:"project"` - Type string `json:"type"` - UpdatedAt time.Time `json:"-"` - User string `json:"user"` -} - -func (r *Receiver) UnmarshalJSON(b []byte) error { - type tmp Receiver - var s struct { - tmp - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` - } - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - *r = Receiver(s.tmp) - - if s.CreatedAt != "" { - r.CreatedAt, err = time.Parse(time.RFC3339, s.CreatedAt) - if err != nil { - return err - } - } - - if s.UpdatedAt != "" { - r.UpdatedAt, err = time.Parse(time.RFC3339, s.UpdatedAt) - if err != nil { - return err - } - } - - return nil -} - -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - -// Extract interprets any commonResult-based result as a Receiver. -func (r commonResult) Extract() (*Receiver, error) { - var s struct { - Receiver *Receiver `json:"receiver"` - } - err := r.ExtractInto(&s) - return s.Receiver, err -} - -// CreateResult is the result of a Create operation. Call its Extract method -// to interpret it as a Receiver. -type CreateResult struct { - commonResult -} - -// GetResult is the result for of a Get operation. Call its Extract method -// to interpret it as a Receiver. -type GetResult struct { - commonResult -} - -// UpdateResult is the result of a Update operation. Call its Extract method -// to interpret it as a Receiver. -type UpdateResult struct { - commonResult -} - -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// NotifyResult is the result from a Notify operation. Call its Extract -// method to determine if the call succeeded or failed. -type NotifyResult struct { - commonResult -} - -// ReceiverPage contains a single page of all nodes from a List operation. -type ReceiverPage struct { - pagination.LinkedPageBase -} - -// 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 -} - -// ExtractReceivers returns a slice of Receivers from the List operation. -func ExtractReceivers(r pagination.Page) ([]Receiver, error) { - var s struct { - Receivers []Receiver `json:"receivers"` - } - err := (r.(ReceiverPage)).ExtractInto(&s) - return s.Receivers, err -} - -// Extract returns action for notify receivers -func (r NotifyResult) Extract() (string, error) { - requestID := r.Header.Get("X-Openstack-Request-Id") - return requestID, r.Err -} diff --git a/openstack/clustering/v1/receivers/testing/doc.go b/openstack/clustering/v1/receivers/testing/doc.go deleted file mode 100644 index c692a35a01..0000000000 --- a/openstack/clustering/v1/receivers/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_receivers_v1 -package testing diff --git a/openstack/clustering/v1/receivers/testing/fixtures_test.go b/openstack/clustering/v1/receivers/testing/fixtures_test.go deleted file mode 100644 index f69eee146e..0000000000 --- a/openstack/clustering/v1/receivers/testing/fixtures_test.go +++ /dev/null @@ -1,241 +0,0 @@ -package testing - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -const CreateResponse = ` -{ - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "2015-11-04T05:21:41Z", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": "2016-11-04T05:21:41Z", - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } -}` - -var ExpectedReceiver = receivers.Receiver{ - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{ - "trust_id": []string{ - "6dc6d336e3fc4c0a951b5698cd1236d9", - }, - }, - Channel: map[string]interface{}{ - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", - }, - ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - CreatedAt: time.Date(2015, 11, 4, 5, 21, 41, 0, time.UTC), - Domain: "Default", - ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - Name: "cluster_inflate", - Params: map[string]interface{}{ - "count": "1", - }, - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Type: "webhook", - UpdatedAt: time.Date(2016, 11, 4, 5, 21, 41, 0, time.UTC), - User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", -} - -const GetResponse = ` -{ - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "2015-11-04T05:21:41Z", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": "2016-11-04T05:21:41Z", - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } -}` - -const UpdateResponse = ` -{ - "receiver": { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "2015-06-27T05:09:43Z", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": null, - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } -}` - -var ExpectedUpdateReceiver = receivers.Receiver{ - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{ - "trust_id": []string{ - "6dc6d336e3fc4c0a951b5698cd1236d9", - }, - }, - Channel: map[string]interface{}{ - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1", - }, - ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - CreatedAt: time.Date(2015, 6, 27, 5, 9, 43, 0, time.UTC), - Domain: "Default", - ID: "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - Name: "cluster_inflate", - Params: map[string]interface{}{ - "count": "1", - }, - Project: "6e18cc2bdbeb48a5b3cad2dc499f6804", - Type: "webhook", - User: "b4ad2d6e18cc2b9c48049f6dbe8a5b3c", -} - -const ListResponse = ` -{ - "receivers": [ - { - "action": "CLUSTER_SCALE_OUT", - "actor": { - "trust_id": [ - "6dc6d336e3fc4c0a951b5698cd1236d9" - ] - }, - "channel": { - "alarm_url": "http://node1:8778/v1/webhooks/e03dd2e5-8f2e-4ec1-8c6a-74ba891e5422/trigger?V=1&count=1" - }, - "cluster_id": "ae63a10b-4a90-452c-aef1-113a0b255ee3", - "created_at": "2015-06-27T05:09:43Z", - "domain": "Default", - "id": "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", - "name": "cluster_inflate", - "params": { - "count": "1" - }, - "project": "6e18cc2bdbeb48a5b3cad2dc499f6804", - "type": "webhook", - "updated_at": null, - "user": "b4ad2d6e18cc2b9c48049f6dbe8a5b3c" - } - ] -}` - -var ExpectedReceiversList = []receivers.Receiver{ExpectedUpdateReceiver} -var ExpectedNotifyRequestID = "66a81d68-bf48-4af5-897b-a3bfef7279a8" - -func HandleCreateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, CreateResponse) - }) -} - -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/receivers/573aa1ba-bf45-49fd-907d-6b5d6e6adfd3", 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, GetResponse) - }) -} - -func HandleUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PATCH") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, UpdateResponse) - }) -} - -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/receivers", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestFormValues(t, r, map[string]string{"limit": "2", "sort": "name:asc,status:desc"}) - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprint(w, ListResponse) - }) -} - -func HandleDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee", 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) - }) -} - -func HandleNotifySuccessfully(t *testing.T) { - th.Mux.HandleFunc("/v1/receivers/6dc6d336e3fc4c0a951b5698cd1236ee/notify", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.Header().Add("X-OpenStack-Request-Id", ExpectedNotifyRequestID) - w.WriteHeader(http.StatusNoContent) - }) -} diff --git a/openstack/clustering/v1/receivers/testing/requests_test.go b/openstack/clustering/v1/receivers/testing/requests_test.go deleted file mode 100644 index 842f88ed27..0000000000 --- a/openstack/clustering/v1/receivers/testing/requests_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package testing - -import ( - "context" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/receivers" - "github.com/gophercloud/gophercloud/v2/pagination" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestCreateReceiver(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleCreateSuccessfully(t) - - opts := receivers.CreateOpts{ - Name: "cluster_inflate", - ClusterID: "ae63a10b-4a90-452c-aef1-113a0b255ee3", - Type: receivers.WebhookReceiver, - Action: "CLUSTER_SCALE_OUT", - Actor: map[string]interface{}{}, - Params: map[string]interface{}{}, - } - - actual, err := receivers.Create(context.TODO(), fake.ServiceClient(), opts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedReceiver, *actual) -} - -func TestGetReceivers(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleGetSuccessfully(t) - - actual, err := receivers.Get(context.TODO(), fake.ServiceClient(), "573aa1ba-bf45-49fd-907d-6b5d6e6adfd3").Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedReceiver, *actual) -} - -func TestUpdateReceiver(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleUpdateSuccessfully(t) - - opts := receivers.UpdateOpts{ - Name: "cluster_inflate", - Action: "CLUSTER_SCALE_OUT", - Params: map[string]interface{}{ - "count": "2", - }, - } - actual, err := receivers.Update(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee", opts).Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, ExpectedUpdateReceiver, *actual) -} - -func TestListReceivers(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleListSuccessfully(t) - - opts := receivers.ListOpts{ - Limit: 2, - Sort: "name:asc,status:desc", - } - - count := 0 - receivers.List(fake.ServiceClient(), opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - count++ - actual, err := receivers.ExtractReceivers(page) - th.AssertNoErr(t, err) - - th.AssertDeepEquals(t, ExpectedReceiversList, actual) - - return true, nil - }) - - th.AssertEquals(t, count, 1) -} - -func TestDeleteReceiver(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleDeleteSuccessfully(t) - - deleteResult := receivers.Delete(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee") - th.AssertNoErr(t, deleteResult.ExtractErr()) -} - -func TestNotifyReceivers(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - HandleNotifySuccessfully(t) - - requestID, err := receivers.Notify(context.TODO(), fake.ServiceClient(), "6dc6d336e3fc4c0a951b5698cd1236ee").Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, ExpectedNotifyRequestID, requestID) -} diff --git a/openstack/clustering/v1/receivers/urls.go b/openstack/clustering/v1/receivers/urls.go deleted file mode 100644 index 7b7021d619..0000000000 --- a/openstack/clustering/v1/receivers/urls.go +++ /dev/null @@ -1,38 +0,0 @@ -package receivers - -import "github.com/gophercloud/gophercloud/v2" - -var apiVersion = "v1" -var apiName = "receivers" - -func commonURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(apiVersion, apiName) -} - -func createURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func idURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id) -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func updateURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func listURL(client *gophercloud.ServiceClient) string { - return commonURL(client) -} - -func deleteURL(client *gophercloud.ServiceClient, id string) string { - return idURL(client, id) -} - -func notifyURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL(apiVersion, apiName, id, "notify") -} diff --git a/openstack/clustering/v1/webhooks/doc.go b/openstack/clustering/v1/webhooks/doc.go deleted file mode 100644 index 9ca6b0be5b..0000000000 --- a/openstack/clustering/v1/webhooks/doc.go +++ /dev/null @@ -1,14 +0,0 @@ -/* -Package webhooks provides the ability to trigger an action represented by a webhook from the OpenStack Clustering -Service. - -Example to Trigger webhook action - - result, err := webhooks.Trigger(context.TODO(), serviceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{V: "1"}).Extract() - if err != nil { - panic(err) - } - - fmt.Println("result", result) -*/ -package webhooks diff --git a/openstack/clustering/v1/webhooks/requests.go b/openstack/clustering/v1/webhooks/requests.go deleted file mode 100644 index 05bbc475b9..0000000000 --- a/openstack/clustering/v1/webhooks/requests.go +++ /dev/null @@ -1,55 +0,0 @@ -package webhooks - -import ( - "context" - "fmt" - "net/url" - - "github.com/gophercloud/gophercloud/v2" -) - -// TriggerOpts represents options used for triggering an action -type TriggerOpts struct { - V string `q:"V" required:"true"` - Params map[string]string -} - -// TriggerOptsBuilder Query string builder interface for webhooks -type TriggerOptsBuilder interface { - ToWebhookTriggerQuery() (string, error) -} - -// Query string builder for webhooks -func (opts TriggerOpts) ToWebhookTriggerQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - params := q.Query() - - for k, v := range opts.Params { - params.Add(k, v) - } - - q = &url.URL{RawQuery: params.Encode()} - return q.String(), err -} - -// Trigger an action represented by a webhook. -func Trigger(ctx context.Context, client *gophercloud.ServiceClient, id string, opts TriggerOptsBuilder) (r TriggerResult) { - url := triggerURL(client, id) - if opts != nil { - query, err := opts.ToWebhookTriggerQuery() - if err != nil { - r.Err = err - return - } - url += query - } else { - r.Err = fmt.Errorf("Must contain V for TriggerOpt") - return - } - - resp, err := client.Post(ctx, url, nil, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201, 202}, - }) - _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) - return -} diff --git a/openstack/clustering/v1/webhooks/results.go b/openstack/clustering/v1/webhooks/results.go deleted file mode 100644 index 47f3579970..0000000000 --- a/openstack/clustering/v1/webhooks/results.go +++ /dev/null @@ -1,22 +0,0 @@ -package webhooks - -import ( - "github.com/gophercloud/gophercloud/v2" -) - -type commonResult struct { - gophercloud.Result -} - -type TriggerResult struct { - commonResult -} - -// Extract retrieves the response action -func (r commonResult) Extract() (string, error) { - var s struct { - Action string `json:"action"` - } - err := r.ExtractInto(&s) - return s.Action, err -} diff --git a/openstack/clustering/v1/webhooks/testing/doc.go b/openstack/clustering/v1/webhooks/testing/doc.go deleted file mode 100644 index 9a759ee29a..0000000000 --- a/openstack/clustering/v1/webhooks/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// clustering_webhooks_v1 -package testing diff --git a/openstack/clustering/v1/webhooks/testing/requests_test.go b/openstack/clustering/v1/webhooks/testing/requests_test.go deleted file mode 100644 index 48033e421b..0000000000 --- a/openstack/clustering/v1/webhooks/testing/requests_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package testing - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "testing" - - "github.com/gophercloud/gophercloud/v2/openstack/clustering/v1/webhooks" - th "github.com/gophercloud/gophercloud/v2/testhelper" - fake "github.com/gophercloud/gophercloud/v2/testhelper/client" -) - -func TestWebhookTrigger(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" - }`) - }) - - triggerOpts := webhooks.TriggerOpts{ - V: "1", - Params: map[string]string{ - "foo": "bar", - "bar": "baz", - }, - } - result, err := webhooks.Trigger(context.TODO(), fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() - th.AssertNoErr(t, err) - th.AssertEquals(t, result, "290c44fa-c60f-4d75-a0eb-87433ba982a3") -} - -// Test webhook with params that generates query strings -func TestWebhookParams(t *testing.T) { - triggerOpts := webhooks.TriggerOpts{ - V: "1", - Params: map[string]string{ - "foo": "bar", - "bar": "baz", - }, - } - expected := "?V=1&bar=baz&foo=bar" - actual, err := triggerOpts.ToWebhookTriggerQuery() - th.AssertNoErr(t, err) - th.AssertEquals(t, actual, expected) -} - -// Nagative test case for returning invalid type (integer) for action id -func TestWebhooksInvalidAction(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": 123 - }`) - }) - - triggerOpts := webhooks.TriggerOpts{ - V: "1", - Params: map[string]string{ - "foo": "bar", - "bar": "baz", - }, - } - _, err := webhooks.Trigger(context.TODO(), fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", triggerOpts).Extract() - isValid := err.(*json.UnmarshalTypeError) == nil - th.AssertEquals(t, false, isValid) -} - -// Negative test case for passing empty TriggerOpt -func TestWebhookTriggerInvalidEmptyOpt(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" - }`) - }) - - _, err := webhooks.Trigger(context.TODO(), fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", webhooks.TriggerOpts{}).Extract() - if err == nil { - t.Errorf("Expected error without V param") - } -} - -// Negative test case for passing in nil for TriggerOpt -func TestWebhookTriggerInvalidNilOpt(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, ` - { - "action": "290c44fa-c60f-4d75-a0eb-87433ba982a3" - }`) - }) - - _, err := webhooks.Trigger(context.TODO(), fake.ServiceClient(), "f93f83f6-762b-41b6-b757-80507834d394", nil).Extract() - - if err == nil { - t.Errorf("Expected error with nil param") - } -} diff --git a/openstack/clustering/v1/webhooks/urls.go b/openstack/clustering/v1/webhooks/urls.go deleted file mode 100644 index 52c2a7deb8..0000000000 --- a/openstack/clustering/v1/webhooks/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package webhooks - -import "github.com/gophercloud/gophercloud/v2" - -func triggerURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("v1", "webhooks", id, "trigger") -} From 4101ba4c713243768c1c1b9f076e7d53dba331f7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 20 May 2024 17:31:16 +0100 Subject: [PATCH 1855/2296] README: Add matrix of supported services Signed-off-by: Stephen Finucane --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 660acba864..e9ba39bb79 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,27 @@ func main() { The above code sample creates a new server with the parameters, and returns a [`servers.Server`](https://pkg.go.dev/github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers#Server). +## Supported Services + +| **Service** | **Name** | **Module** | **1.x** | **2.x** | +|:------------------------:|------------------|:----------------------------------:|:-------:|:-------:| +| Baremetal | Ironic | `openstack/baremetal` | ✔ | ✔ | +| Baremetal Introspection | Ironic Inspector | `openstack/baremetalintrospection` | ✔ | ✔ | +| Block Storage | Cinder | `openstack/blockstorage` | ✔ | ✔ | +| Clustering | Senlin | `openstack/clustering` | ✔ | ✘ | +| Compute | Nova | `openstack/compute` | ✔ | ✔ | +| Container | Zun | `openstack/container` | ✔ | ✔ | +| Container Infrastructure | Magnum | `openstack/containerinfra` | ✔ | ✔ | +| Database | Trove | `openstack/db` | ✔ | ✔ | +| DNS | Designate | `openstack/dns` | ✔ | ✔ | +| Identity | Keystone | `openstack/identity` | ✔ | ✔ | +| Image | Glance | `openstack/image` | ✔ | ✔ | +| Key Management | Barbican | `openstack/keymanager` | ✔ | ✔ | +| Load Balancing | Octavia | `openstack/loadbalancer` | ✔ | ✔ | +| Messaging | Zaqar | `openstack/messaging` | ✔ | ✔ | +| Networking | Neutron | `openstack/networking` | ✔ | ✔ | +| Object Storage | Swift | `openstack/objectstorage` | ✔ | ✔ | + ## Advanced Usage Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gophercloud works. From 5584c126905fa24a93ce44317e93c31b34042804 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 14:43:52 +0000 Subject: [PATCH 1856/2296] make: Add golangci-lint to lint target Plus a note for Fedora users. All current failures are ignored while we work on fixing them. Signed-off-by: Stephen Finucane --- .golangci.yaml | 20 ++++++++++++++++++++ Makefile | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 .golangci.yaml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000000..1ff26f22ee --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,20 @@ +--- +linters: + disable: + - errcheck # Error return value is not checked (errcheck) + - ineffassign # ineffectual assignment to tags (ineffassign) + - unused +issues: + exclude: + - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) + - SA1019 # (deprecated standard library function) (staticcheck) + - SA4006 # this value of `***` is never used (staticcheck) + - SA5011 # possible nil pointer dereference (staticcheck) + - SA6005 # should use strings.EqualFold instead (staticcheck) + - S1005 # unnecessary assignment to the blank identifier (gosimple) + - S1020 # when ok is true, err can't be nil (gosimple) + - S1021 # should merge variable declaration with assignment on next line (gosimple) + - S1023 # redundant `return` statement (gosimple) + - S1030 # should use buf.String() instead of string(buf.Bytes()) (gosimple) + - S1033 # unnecessary guard around call to delete (gosimple) + - S1034 # assigning the result of this type assertion to a variable (***) could eliminate type assertions in switch cases (gosimple) diff --git a/Makefile b/Makefile index 74b88d0598..dc8e486d0c 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,30 @@ undefine GOFLAGS +GOLANGCI_LINT_VERSION?=v1.57.1 + +ifeq ($(shell command -v podman 2> /dev/null),) + RUNNER=docker +else + RUNNER=podman +endif + +# if the golangci-lint steps fails with the following error message: +# +# directory prefix . does not contain main module or its selected dependencies +# +# you probably have to fix the SELinux security context for root directory plus your cache +# +# chcon -Rt svirt_sandbox_file_t . +# chcon -Rt svirt_sandbox_file_t ~/.cache/golangci-lint lint: go fmt ./... go vet -tags "fixtures acceptance" ./... + $(RUNNER) run -t --rm \ + -v $(shell pwd):/app \ + -v ~/.cache/golangci-lint/$(GOLANGCI_LINT_VERSION):/root/.cache \ + -w /app \ + -e GOFLAGS="-tags=acceptance" \ + golangci/golangci-lint:$(GOLANGCI_LINT_VERSION) golangci-lint run .PHONY: lint unit: From 7975d2bc86103ca348ced14a21c5974ec2381b96 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 15:14:55 +0000 Subject: [PATCH 1857/2296] lint: Remove redundant returns (gosimple) Signed-off-by: Stephen Finucane --- .golangci.yaml | 1 - .../acceptance/openstack/containerinfra/v1/containerinfra.go | 4 ---- testing/provider_client_test.go | 1 - 3 files changed, 6 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 1ff26f22ee..73770a8c48 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -14,7 +14,6 @@ issues: - S1005 # unnecessary assignment to the blank identifier (gosimple) - S1020 # when ok is true, err can't be nil (gosimple) - S1021 # should merge variable declaration with assignment on next line (gosimple) - - S1023 # redundant `return` statement (gosimple) - S1030 # should use buf.String() instead of string(buf.Bytes()) (gosimple) - S1033 # unnecessary guard around call to delete (gosimple) - S1034 # assigning the result of this type assertion to a variable (***) could eliminate type assertions in switch cases (gosimple) diff --git a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go index 67a11fdef1..9b26c0ad2d 100644 --- a/internal/acceptance/openstack/containerinfra/v1/containerinfra.go +++ b/internal/acceptance/openstack/containerinfra/v1/containerinfra.go @@ -97,8 +97,6 @@ func DeleteClusterTemplate(t *testing.T, client *gophercloud.ServiceClient, id s } t.Logf("Successfully deleted cluster-template: %s", id) - - return } // CreateClusterTimeout will create a random cluster and wait for it to reach CREATE_COMPLETE status @@ -181,8 +179,6 @@ func DeleteCluster(t *testing.T, client *gophercloud.ServiceClient, id string) { } t.Logf("Successfully deleted cluster: %s", id) - - return } func WaitForCluster(client *gophercloud.ServiceClient, clusterID string, status string, timeout time.Duration) error { diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index fa789ba459..9706017c95 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -177,7 +177,6 @@ func TestReauthEndLoop(t *testing.T) { th.Mux.HandleFunc("/route", func(w http.ResponseWriter, r *http.Request) { // route always return 401 w.WriteHeader(http.StatusUnauthorized) - return }) reqopts := new(gophercloud.RequestOpts) From de749a9d111dba6e6f2d9f84d5a75e45997bb66f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 15:17:00 +0000 Subject: [PATCH 1858/2296] lint: Remove unnecessary guards around calls to delete (gosimple) Signed-off-by: Stephen Finucane --- .golangci.yaml | 1 - openstack/identity/v3/ec2tokens/requests.go | 12 +++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 73770a8c48..602cd1f385 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -15,5 +15,4 @@ issues: - S1020 # when ok is true, err can't be nil (gosimple) - S1021 # should merge variable declaration with assignment on next line (gosimple) - S1030 # should use buf.String() instead of string(buf.Bytes()) (gosimple) - - S1033 # unnecessary guard around call to delete (gosimple) - S1034 # assigning the result of this type assertion to a variable (***) could eliminate type assertions in switch cases (gosimple) diff --git a/openstack/identity/v3/ec2tokens/requests.go b/openstack/identity/v3/ec2tokens/requests.go index bd1c1293f5..241216b7fd 100644 --- a/openstack/identity/v3/ec2tokens/requests.go +++ b/openstack/identity/v3/ec2tokens/requests.go @@ -238,12 +238,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] // detect and process a signature v2 if v, ok := p["SignatureVersion"]; ok && v == "2" { - if _, ok := c["body_hash"]; ok { - delete(c, "body_hash") - } - if _, ok := c["headers"]; ok { - delete(c, "headers") - } + delete(c, "body_hash") + delete(c, "headers") if v, ok := p["SignatureMethod"]; ok { // params is a map of strings strToSign := EC2CredentialsBuildStringToSignV2(*opts) @@ -370,9 +366,7 @@ func interfaceToMap(c map[string]interface{}, key string) map[string]string { func deleteBodyElements(b map[string]interface{}, elements ...string) { if c, ok := b["credentials"].(map[string]interface{}); ok { for _, k := range elements { - if _, ok := c[k]; ok { - delete(c, k) - } + delete(c, k) } } } From c317647efcee2002efad1a6be5d71845200c22c8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 15:19:51 +0000 Subject: [PATCH 1859/2296] lint: Remove unnecessary assignments to the blank identifier (gosimple) Signed-off-by: Stephen Finucane --- .golangci.yaml | 1 - openstack/identity/v3/ec2tokens/requests.go | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 602cd1f385..39e1a215ad 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -11,7 +11,6 @@ issues: - SA4006 # this value of `***` is never used (staticcheck) - SA5011 # possible nil pointer dereference (staticcheck) - SA6005 # should use strings.EqualFold instead (staticcheck) - - S1005 # unnecessary assignment to the blank identifier (gosimple) - S1020 # when ok is true, err can't be nil (gosimple) - S1021 # should merge variable declaration with assignment on next line (gosimple) - S1030 # should use buf.String() instead of string(buf.Bytes()) (gosimple) diff --git a/openstack/identity/v3/ec2tokens/requests.go b/openstack/identity/v3/ec2tokens/requests.go index 241216b7fd..cb237cc4f4 100644 --- a/openstack/identity/v3/ec2tokens/requests.go +++ b/openstack/identity/v3/ec2tokens/requests.go @@ -265,12 +265,12 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] if opts.Timestamp != nil { date = *opts.Timestamp } - if v, _ := c["body_hash"]; v == nil { + if v := c["body_hash"]; v == nil { // when body_hash is not set, generate a random one c["body_hash"] = randomBodyHash() } - signedHeaders, _ := h["X-Amz-SignedHeaders"] + signedHeaders := h["X-Amz-SignedHeaders"] stringToSign := EC2CredentialsBuildStringToSignV4(*opts, signedHeaders, c["body_hash"].(string), date) key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, opts.Region, opts.Service, date) From 8439724f9ab847f98aeab5b4a0aae5d42429e8a4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 15:22:24 +0000 Subject: [PATCH 1860/2296] lint: Remove unused variables (unused) Signed-off-by: Stephen Finucane --- .golangci.yaml | 1 - auth_options.go | 6 ------ .../openstack/compute/v2/keypairs_test.go | 2 -- openstack/blockstorage/v2/quotasets/requests.go | 2 +- openstack/blockstorage/v3/quotasets/requests.go | 2 +- openstack/blockstorage/v3/snapshots/requests.go | 2 +- .../compute/v2/flavors/testing/requests_test.go | 2 -- openstack/compute/v2/quotasets/urls.go | 4 ---- openstack/identity/v2/roles/urls.go | 4 ---- openstack/identity/v3/registeredlimits/urls.go | 4 ---- openstack/keymanager/v1/containers/results.go | 15 ++------------- openstack/loadbalancer/v2/monitors/requests.go | 5 ----- openstack/messaging/v2/messages/results.go | 5 ----- .../networking/v2/extensions/agents/requests.go | 2 +- .../v1/containers/testing/requests_test.go | 4 ---- testing/errors_test.go | 11 ----------- testing/provider_client_test.go | 2 +- 17 files changed, 7 insertions(+), 66 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 39e1a215ad..e096eec65f 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -3,7 +3,6 @@ linters: disable: - errcheck # Error return value is not checked (errcheck) - ineffassign # ineffectual assignment to tags (ineffassign) - - unused issues: exclude: - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) diff --git a/auth_options.go b/auth_options.go index 9d52c07d73..3f0c394c31 100644 --- a/auth_options.go +++ b/auth_options.go @@ -146,12 +146,6 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s Name *string `json:"name,omitempty"` } - type projectReq struct { - Domain *domainReq `json:"domain,omitempty"` - Name *string `json:"name,omitempty"` - ID *string `json:"id,omitempty"` - } - type userReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` diff --git a/internal/acceptance/openstack/compute/v2/keypairs_test.go b/internal/acceptance/openstack/compute/v2/keypairs_test.go index f5fec8138c..f3a0ac7e32 100644 --- a/internal/acceptance/openstack/compute/v2/keypairs_test.go +++ b/internal/acceptance/openstack/compute/v2/keypairs_test.go @@ -15,8 +15,6 @@ import ( "golang.org/x/crypto/ssh" ) -const keyName = "gophercloud_test_key_pair" - func TestKeyPairsParse(t *testing.T) { client, err := clients.NewComputeV2Client() th.AssertNoErr(t, err) diff --git a/openstack/blockstorage/v2/quotasets/requests.go b/openstack/blockstorage/v2/quotasets/requests.go index 21fa7c8f97..9189f92aa5 100644 --- a/openstack/blockstorage/v2/quotasets/requests.go +++ b/openstack/blockstorage/v2/quotasets/requests.go @@ -109,7 +109,7 @@ type UpdateOpts struct { // Resets the quotas for the given tenant to their default values. func Delete(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { - resp, err := client.Delete(ctx, updateURL(client, projectID), &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, deleteURL(client, projectID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v3/quotasets/requests.go b/openstack/blockstorage/v3/quotasets/requests.go index 21fa7c8f97..9189f92aa5 100644 --- a/openstack/blockstorage/v3/quotasets/requests.go +++ b/openstack/blockstorage/v3/quotasets/requests.go @@ -109,7 +109,7 @@ type UpdateOpts struct { // Resets the quotas for the given tenant to their default values. func Delete(ctx context.Context, client *gophercloud.ServiceClient, projectID string) (r DeleteResult) { - resp, err := client.Delete(ctx, updateURL(client, projectID), &gophercloud.RequestOpts{ + resp, err := client.Delete(ctx, deleteURL(client, projectID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index cec3be5437..4971ae2fab 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -259,7 +259,7 @@ func UpdateStatus(ctx context.Context, client *gophercloud.ServiceClient, id str return } - resp, err := client.Post(ctx, resetStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, updateStatusURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index fa328680eb..0f84219ec4 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -13,8 +13,6 @@ import ( fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) -const tokenID = "blerb" - func TestListFlavors(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/openstack/compute/v2/quotasets/urls.go b/openstack/compute/v2/quotasets/urls.go index 4e0a2a213f..231d1f97e9 100644 --- a/openstack/compute/v2/quotasets/urls.go +++ b/openstack/compute/v2/quotasets/urls.go @@ -4,10 +4,6 @@ import "github.com/gophercloud/gophercloud/v2" const resourcePath = "os-quota-sets" -func resourceURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL(resourcePath) -} - func getURL(c *gophercloud.ServiceClient, tenantID string) string { return c.ServiceURL(resourcePath, tenantID) } diff --git a/openstack/identity/v2/roles/urls.go b/openstack/identity/v2/roles/urls.go index a07e7b2e14..0e7c20090f 100644 --- a/openstack/identity/v2/roles/urls.go +++ b/openstack/identity/v2/roles/urls.go @@ -8,10 +8,6 @@ const ( UserPath = "users" ) -func resourceURL(c *gophercloud.ServiceClient, id string) string { - return c.ServiceURL(ExtPath, RolePath, id) -} - func rootURL(c *gophercloud.ServiceClient) string { return c.ServiceURL(ExtPath, RolePath) } diff --git a/openstack/identity/v3/registeredlimits/urls.go b/openstack/identity/v3/registeredlimits/urls.go index 3b1123150b..1eb9eafff4 100644 --- a/openstack/identity/v3/registeredlimits/urls.go +++ b/openstack/identity/v3/registeredlimits/urls.go @@ -7,10 +7,6 @@ const ( enforcementModelPath = "model" ) -func enforcementModelURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL(rootPath, enforcementModelPath) -} - func rootURL(client *gophercloud.ServiceClient) string { return client.ServiceURL(rootPath) } diff --git a/openstack/keymanager/v1/containers/results.go b/openstack/keymanager/v1/containers/results.go index 2ea8c3e258..cf8ab3b678 100644 --- a/openstack/keymanager/v1/containers/results.go +++ b/openstack/keymanager/v1/containers/results.go @@ -176,28 +176,17 @@ func (r *Consumer) UnmarshalJSON(b []byte) error { return nil } -type consumerResult struct { - gophercloud.Result -} - -// Extract interprets any consumerResult as a Consumer. -func (r consumerResult) Extract() (*Consumer, error) { - var s *Consumer - err := r.ExtractInto(&s) - return s, err -} - // CreateConsumerResult is the response from a CreateConsumer operation. // Call its Extract method to interpret it as a container. type CreateConsumerResult struct { - // This is not a typo. + // This is not a typo: the API returns a Container, not a Consumer commonResult } // DeleteConsumerResult is the response from a DeleteConsumer operation. // Call its Extract to interpret it as a container. type DeleteConsumerResult struct { - // This is not a typo. + // This is not a typo: the API returns a Container, not a Consumer commonResult } diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index f7c3db49b3..e13cfcf6e1 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -2,7 +2,6 @@ package monitors import ( "context" - "fmt" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" @@ -82,10 +81,6 @@ const ( TypeSCTP = "SCTP" ) -var ( - errDelayMustGETimeout = fmt.Errorf("Delay must be greater than or equal to timeout") -) - // CreateOptsBuilder allows extensions to add additional parameters to the // List request. type CreateOptsBuilder interface { diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go index 76bd91a2f5..385ff2014d 100644 --- a/openstack/messaging/v2/messages/results.go +++ b/openstack/messaging/v2/messages/results.go @@ -5,11 +5,6 @@ import ( "github.com/gophercloud/gophercloud/v2/pagination" ) -// commonResult is the response of a base result. -type commonResult struct { - gophercloud.Result -} - // CreateResult is the response of a Create operations. type CreateResult struct { gophercloud.Result diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index 7b78647736..abd4e647a8 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -101,7 +101,7 @@ func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts U // Delete deletes a specific agent based on its ID. func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - resp, err := c.Delete(ctx, getURL(c, id), nil) + resp, err := c.Delete(ctx, deleteURL(c, id), nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/objectstorage/v1/containers/testing/requests_test.go b/openstack/objectstorage/v1/containers/testing/requests_test.go index 70a331410f..9a2074d510 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -12,10 +12,6 @@ import ( fake "github.com/gophercloud/gophercloud/v2/testhelper/client" ) -var ( - metadata = map[string]string{"gophercloud-test": "containers"} -) - func TestContainerNames(t *testing.T) { for _, tc := range [...]struct { name string diff --git a/testing/errors_test.go b/testing/errors_test.go index 8e3d1640b7..21e6b2c2e2 100644 --- a/testing/errors_test.go +++ b/testing/errors_test.go @@ -9,17 +9,6 @@ import ( th "github.com/gophercloud/gophercloud/v2/testhelper" ) -func returnsUnexpectedResp(code int) gophercloud.ErrUnexpectedResponseCode { - return gophercloud.ErrUnexpectedResponseCode{ - URL: "http://example.com", - Method: "GET", - Expected: []int{200}, - Actual: code, - Body: []byte("the response body"), - ResponseHeader: nil, - } -} - func TestErrUnexpectedResponseCode(t *testing.T) { err := gophercloud.ErrUnexpectedResponseCode{ URL: "http://example.com", diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 9706017c95..18704fd4b1 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -387,7 +387,7 @@ func TestRequestWithContext(t *testing.T) { th.AssertNoErr(t, err) cancel() - res, err = p.Request(ctx, "GET", ts.URL, &gophercloud.RequestOpts{}) + _, err = p.Request(ctx, "GET", ts.URL, &gophercloud.RequestOpts{}) if err == nil { t.Fatal("expecting error, got nil") } From 9ecf395d5b4eb749a95532c990ebfab6626c9b06 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 16:48:06 +0000 Subject: [PATCH 1861/2296] lint: Check all return codes (errcheck) Signed-off-by: Stephen Finucane --- .golangci.yaml | 1 - .../openstack/compute/v2/servers_test.go | 6 ++-- .../openstack/image/v2/images_test.go | 1 + .../openstack/messaging/v2/claims_test.go | 7 +++-- .../openstack/messaging/v2/message_test.go | 27 ++++++++++------ .../openstack/messaging/v2/messaging.go | 4 ++- .../v2/extensions/agents/agents_test.go | 2 +- .../v1/allocations/testing/fixtures_test.go | 4 ++- .../v1/conductors/testing/fixtures_test.go | 8 +++-- .../v1/drivers/testing/fixtures_test.go | 4 ++- .../v1/nodes/testing/fixtures_test.go | 8 +++-- .../v1/ports/testing/fixtures_test.go | 8 +++-- .../v1/introspection/testing/fixtures.go | 4 ++- .../v2/backups/testing/fixtures_test.go | 8 +++-- .../schedulerstats/testing/fixtures_test.go | 4 ++- .../v2/snapshots/testing/requests_test.go | 3 +- .../v2/volumes/testing/requests_test.go | 3 +- .../v3/attachments/testing/fixtures_test.go | 4 ++- .../v3/backups/testing/fixtures_test.go | 8 +++-- .../v3/qos/testing/fixtures_test.go | 4 ++- .../schedulerstats/testing/fixtures_test.go | 4 ++- .../v3/snapshots/testing/fixtures_test.go | 4 ++- .../v3/snapshots/testing/requests_test.go | 3 +- .../v3/volumes/testing/fixtures_test.go | 4 ++- .../v3/volumes/testing/requests_test.go | 3 +- .../v3/volumetypes/testing/fixtures_test.go | 4 ++- openstack/client.go | 10 ++++-- .../extensions/testing/requests_test.go | 3 +- .../v2/diagnostics/testing/fixtures_test.go | 3 +- .../v2/extensions/testing/delegate_test.go | 3 +- .../v2/flavors/testing/requests_test.go | 4 ++- .../v2/servers/testing/fixtures_test.go | 31 +++++++++++++------ .../v1/clusters/testing/requests_test.go | 6 ++-- .../clustertemplates/testing/requests_test.go | 3 +- .../v1/nodegroups/testing/fixtures_test.go | 4 ++- .../v2/recordsets/testing/fixtures_test.go | 29 ++++++++--------- openstack/identity/v3/ec2tokens/requests.go | 14 ++++++--- .../v3/endpoints/testing/requests_test.go | 3 +- .../projectendpoints/testing/requests_test.go | 3 +- .../image/v2/tasks/testing/requests_test.go | 3 +- .../v2/amphorae/testing/fixtures_test.go | 4 ++- .../v2/flavorprofiles/testing/fixtures.go | 4 ++- .../v2/flavors/testing/fixtures.go | 4 ++- .../v2/l7policies/testing/fixtures_test.go | 8 +++-- .../v2/listeners/testing/fixtures_test.go | 4 ++- .../v2/loadbalancers/testing/fixtures_test.go | 4 ++- .../v2/monitors/testing/fixtures_test.go | 4 ++- .../v2/pools/testing/fixtures_test.go | 8 +++-- .../v2/providers/testing/fixtures_test.go | 4 ++- .../v2/apiversions/testing/requests_test.go | 12 ++++--- .../agents/testing/requests_test.go | 9 ++++-- .../bgp/peers/testing/requests_test.go | 3 +- .../bgp/speakers/testing/requests_test.go | 6 ++-- .../fwaas_v2/groups/testing/requests_test.go | 3 +- .../policies/testing/requests_test.go | 3 +- .../fwaas_v2/rules/testing/requests_test.go | 3 +- .../addressscopes/testing/requests_test.go | 3 +- .../floatingips/testing/requests_test.go | 8 +++-- .../portforwarding/testing/requests_test.go | 3 +- .../layer3/routers/testing/requests_test.go | 3 +- .../rbacpolicies/testing/requests_test.go | 3 +- .../security/rules/testing/requests_test.go | 3 +- .../v2/extensions/testing/delegate_test.go | 3 +- .../trunks/testing/requests_test.go | 3 +- .../endpointgroups/testing/requests_test.go | 3 +- .../ikepolicies/testing/requests_test.go | 3 +- .../ipsecpolicies/testing/requests_test.go | 3 +- .../vpnaas/services/testing/requests_test.go | 3 +- .../siteconnections/testing/requests_test.go | 3 +- .../v2/subnets/testing/requests_test.go | 3 +- .../v1/containers/testing/fixtures.go | 4 ++- .../objectstorage/v1/objects/requests.go | 5 ++- .../v1/objects/testing/fixtures_test.go | 17 +++++++--- .../v1/objects/testing/requests_test.go | 6 ++-- .../v1/apiversions/testing/requests_test.go | 6 ++-- .../v1/resourcetypes/testing/fixtures_test.go | 8 +++-- .../v1/stackevents/testing/fixtures_test.go | 8 +++-- .../stackresources/testing/fixtures_test.go | 4 ++- .../v1/stacks/testing/fixtures_test.go | 4 ++- .../v2/replicas/testing/fixtures_test.go | 8 +++-- .../schedulerstats/testing/fixtures_test.go | 8 +++-- .../v2/sharenetworks/testing/fixtures_test.go | 8 +++-- .../v2/shares/testing/fixtures_test.go | 4 ++- .../v2/snapshots/testing/fixtures_test.go | 4 ++- .../v2/crontriggers/testing/requests_test.go | 4 ++- .../v2/executions/testing/requests_test.go | 4 ++- .../v2/workflows/testing/requests_test.go | 4 ++- pagination/testing/marker_test.go | 4 ++- provider_client.go | 4 ++- testhelper/http_responses.go | 4 ++- 90 files changed, 357 insertions(+), 150 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index e096eec65f..301c329d77 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,7 +1,6 @@ --- linters: disable: - - errcheck # Error return value is not checked (errcheck) - ineffassign # ineffectual assignment to tags (ineffassign) issues: exclude: diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 53401d6169..9b6367674a 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -331,7 +331,8 @@ func TestServersActionResizeConfirm(t *testing.T) { defer DeleteServer(t, client, server) t.Logf("Attempting to resize server %s", server.ID) - ResizeServer(t, client, server) + err = ResizeServer(t, client, server) + th.AssertNoErr(t, err) t.Logf("Attempting to confirm resize for server %s", server.ID) if res := servers.ConfirmResize(context.TODO(), client, server.ID); res.Err != nil { @@ -362,7 +363,8 @@ func TestServersActionResizeRevert(t *testing.T) { defer DeleteServer(t, client, server) t.Logf("Attempting to resize server %s", server.ID) - ResizeServer(t, client, server) + err = ResizeServer(t, client, server) + th.AssertNoErr(t, err) t.Logf("Attempting to revert resize for server %s", server.ID) if res := servers.RevertResize(context.TODO(), client, server.ID); res.Err != nil { diff --git a/internal/acceptance/openstack/image/v2/images_test.go b/internal/acceptance/openstack/image/v2/images_test.go index 24a1088b99..2053a2047b 100644 --- a/internal/acceptance/openstack/image/v2/images_test.go +++ b/internal/acceptance/openstack/image/v2/images_test.go @@ -37,6 +37,7 @@ func TestImagesListEachPage(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) } func TestImagesListAllPages(t *testing.T) { diff --git a/internal/acceptance/openstack/messaging/v2/claims_test.go b/internal/acceptance/openstack/messaging/v2/claims_test.go index 679996e43b..e917c5561d 100644 --- a/internal/acceptance/openstack/messaging/v2/claims_test.go +++ b/internal/acceptance/openstack/messaging/v2/claims_test.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/claims" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCRUDClaim(t *testing.T) { @@ -29,7 +30,8 @@ func TestCRUDClaim(t *testing.T) { t.Fatalf("Unable to create a messaging service client: %v", err) } for i := 0; i < 3; i++ { - CreateMessage(t, client, createdQueueName) + _, err := CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) } clientID = "3381af92-2b9e-11e3-b191-7186130073dd" @@ -59,6 +61,7 @@ func TestCRUDClaim(t *testing.T) { } tools.PrintResource(t, updatedClaim) - DeleteClaim(t, client, createdQueueName, claimID) + err := DeleteClaim(t, client, createdQueueName, claimID) + th.AssertNoErr(t, err) } } diff --git a/internal/acceptance/openstack/messaging/v2/message_test.go b/internal/acceptance/openstack/messaging/v2/message_test.go index f64e1c2db3..e2ad2a7e70 100644 --- a/internal/acceptance/openstack/messaging/v2/message_test.go +++ b/internal/acceptance/openstack/messaging/v2/message_test.go @@ -28,7 +28,8 @@ func TestListMessages(t *testing.T) { currentNumberOfMessages := 0 for i := 0; i < totalNumberOfMessages; i++ { - CreateMessage(t, client, createdQueueName) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) } // Use a different client/clientID in order to see messages on the Queue @@ -65,7 +66,8 @@ func TestCreateMessages(t *testing.T) { createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) - CreateMessage(t, client, createdQueueName) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) } func TestGetMessages(t *testing.T) { @@ -79,8 +81,10 @@ func TestGetMessages(t *testing.T) { createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) - CreateMessage(t, client, createdQueueName) - CreateMessage(t, client, createdQueueName) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" @@ -127,7 +131,8 @@ func TestGetMessage(t *testing.T) { createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) - CreateMessage(t, client, createdQueueName) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" @@ -172,8 +177,10 @@ func TestDeleteMessagesIDs(t *testing.T) { createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) - CreateMessage(t, client, createdQueueName) - CreateMessage(t, client, createdQueueName) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" @@ -229,7 +236,8 @@ func TestDeleteMessagesPop(t *testing.T) { defer DeleteQueue(t, client, createdQueueName) for i := 0; i < 5; i++ { - CreateMessage(t, client, createdQueueName) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) } // Use a different client/clientID in order to see messages on the Queue @@ -271,7 +279,8 @@ func TestDeleteMessage(t *testing.T) { createdQueueName, err := CreateQueue(t, client) defer DeleteQueue(t, client, createdQueueName) - CreateMessage(t, client, createdQueueName) + _, err = CreateMessage(t, client, createdQueueName) + th.AssertNoErr(t, err) // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" diff --git a/internal/acceptance/openstack/messaging/v2/messaging.go b/internal/acceptance/openstack/messaging/v2/messaging.go index 5f0f1231bb..ce93bb0a44 100644 --- a/internal/acceptance/openstack/messaging/v2/messaging.go +++ b/internal/acceptance/openstack/messaging/v2/messaging.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/messages" "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/queues" "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error) { @@ -32,7 +33,8 @@ func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error t.Fatalf("Unable to create Queue: %v", createErr) } - GetQueue(t, client, queueName) + _, err := GetQueue(t, client, queueName) + th.AssertNoErr(t, err) t.Logf("Created Queue: %s", queueName) return queueName, nil diff --git a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index 3ce0f8972e..c55529b0f5 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -184,7 +184,7 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) // Delete the BGP Speaker - speakers.Delete(context.TODO(), client, bgpSpeaker.ID).ExtractErr() + err = speakers.Delete(context.TODO(), client, bgpSpeaker.ID).ExtractErr() th.AssertNoErr(t, err) t.Logf("Successfully deleted the BGP Speaker, %s", bgpSpeaker.ID) err = tools.WaitForTimeout( diff --git a/openstack/baremetal/v1/allocations/testing/fixtures_test.go b/openstack/baremetal/v1/allocations/testing/fixtures_test.go index a2b843e5b8..0ccf3ed0d5 100644 --- a/openstack/baremetal/v1/allocations/testing/fixtures_test.go +++ b/openstack/baremetal/v1/allocations/testing/fixtures_test.go @@ -113,7 +113,9 @@ func HandleAllocationListSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { diff --git a/openstack/baremetal/v1/conductors/testing/fixtures_test.go b/openstack/baremetal/v1/conductors/testing/fixtures_test.go index e0b0c374e2..8e59ddaab4 100644 --- a/openstack/baremetal/v1/conductors/testing/fixtures_test.go +++ b/openstack/baremetal/v1/conductors/testing/fixtures_test.go @@ -143,7 +143,9 @@ func HandleConductorListSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { @@ -164,7 +166,9 @@ func HandleConductorListDetailSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } fmt.Fprintf(w, ConductorListDetailBody) }) diff --git a/openstack/baremetal/v1/drivers/testing/fixtures_test.go b/openstack/baremetal/v1/drivers/testing/fixtures_test.go index d29325d408..fcd329ca6d 100644 --- a/openstack/baremetal/v1/drivers/testing/fixtures_test.go +++ b/openstack/baremetal/v1/drivers/testing/fixtures_test.go @@ -373,7 +373,9 @@ func HandleListDriversSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } fmt.Fprintf(w, ListDriversBody) }) diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index 669c38e929..8bfb474b74 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -1290,7 +1290,9 @@ func HandleNodeListSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { @@ -1311,7 +1313,9 @@ func HandleNodeListDetailSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } fmt.Fprintf(w, NodeListDetailBody) }) diff --git a/openstack/baremetal/v1/ports/testing/fixtures_test.go b/openstack/baremetal/v1/ports/testing/fixtures_test.go index 2f8c041fc0..6a97dde39c 100644 --- a/openstack/baremetal/v1/ports/testing/fixtures_test.go +++ b/openstack/baremetal/v1/ports/testing/fixtures_test.go @@ -174,7 +174,9 @@ func HandlePortListSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { @@ -195,7 +197,9 @@ func HandlePortListDetailSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } fmt.Fprintf(w, PortListDetailBody) }) diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index ae8c384028..370d10d769 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -459,7 +459,9 @@ func HandleListIntrospectionsSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") diff --git a/openstack/blockstorage/v2/backups/testing/fixtures_test.go b/openstack/blockstorage/v2/backups/testing/fixtures_test.go index beee7c2678..2f24173418 100644 --- a/openstack/blockstorage/v2/backups/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/backups/testing/fixtures_test.go @@ -202,7 +202,9 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -223,7 +225,9 @@ func MockListDetailResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go index 24c6f24f86..6f0317ef60 100644 --- a/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go @@ -100,7 +100,9 @@ func HandleStoragePoolsListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } if r.FormValue("detail") == "true" { fmt.Fprintf(w, StoragePoolsListBodyDetail) } else { diff --git a/openstack/blockstorage/v2/snapshots/testing/requests_test.go b/openstack/blockstorage/v2/snapshots/testing/requests_test.go index 43a3fbd131..c44bda3876 100644 --- a/openstack/blockstorage/v2/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v2/snapshots/testing/requests_test.go @@ -19,7 +19,7 @@ func TestList(t *testing.T) { count := 0 - snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := snapshots.ExtractSnapshots(page) if err != nil { @@ -52,6 +52,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 2be7d0d57b..7e36a22339 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -20,7 +20,7 @@ func TestListWithExtensions(t *testing.T) { count := 0 - volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := volumes.ExtractVolumes(page) if err != nil { @@ -89,6 +89,7 @@ func TestListWithExtensions(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/blockstorage/v3/attachments/testing/fixtures_test.go b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go index a42caf8638..22d66ddea8 100644 --- a/openstack/blockstorage/v3/attachments/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go @@ -35,7 +35,9 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/blockstorage/v3/backups/testing/fixtures_test.go b/openstack/blockstorage/v3/backups/testing/fixtures_test.go index 1f17f717d3..13fc1d61d9 100644 --- a/openstack/blockstorage/v3/backups/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/backups/testing/fixtures_test.go @@ -202,7 +202,9 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -223,7 +225,9 @@ func MockListDetailResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/blockstorage/v3/qos/testing/fixtures_test.go b/openstack/blockstorage/v3/qos/testing/fixtures_test.go index af3d02e01c..8f9d2e2d51 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures_test.go @@ -76,7 +76,9 @@ func MockListResponse(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go index d92301cc63..df0ccaa7f5 100644 --- a/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go @@ -100,7 +100,9 @@ func HandleStoragePoolsListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } if r.FormValue("detail") == "true" { fmt.Fprintf(w, StoragePoolsListBodyDetail) } else { diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go b/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go index eb4443b3af..8bbc45e77d 100644 --- a/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go @@ -18,7 +18,9 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/blockstorage/v3/snapshots/testing/requests_test.go b/openstack/blockstorage/v3/snapshots/testing/requests_test.go index ddca839153..80cd68d182 100644 --- a/openstack/blockstorage/v3/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/requests_test.go @@ -19,7 +19,7 @@ func TestList(t *testing.T) { count := 0 - snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := snapshots.List(client.ServiceClient(), &snapshots.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := snapshots.ExtractSnapshots(page) if err != nil { @@ -52,6 +52,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go index d8ade8fc83..4efb9e08f4 100644 --- a/openstack/blockstorage/v3/volumes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go @@ -17,7 +17,9 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index fbd4d9a9f5..6a22016101 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -20,7 +20,7 @@ func TestListWithExtensions(t *testing.T) { count := 0 - volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := volumes.List(client.ServiceClient(), &volumes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := volumes.ExtractVolumes(page) if err != nil { @@ -90,6 +90,7 @@ func TestListWithExtensions(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go index 433a6d47a7..e1db5dfbd4 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go @@ -17,7 +17,9 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/client.go b/openstack/client.go index 5bc626781b..f68fe14417 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -152,7 +152,10 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tac := *client tac.SetThrowaway(true) tac.ReauthFunc = nil - tac.SetTokenAndAuthResult(nil) + err := tac.SetTokenAndAuthResult(nil) + if err != nil { + return err + } tao := options tao.AllowReauth = false client.ReauthFunc = func(ctx context.Context) error { @@ -251,7 +254,10 @@ func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st tac := *client tac.SetThrowaway(true) tac.ReauthFunc = nil - tac.SetTokenAndAuthResult(nil) + err = tac.SetTokenAndAuthResult(nil) + if err != nil { + return err + } var tao tokens3.AuthOptionsBuilder switch ot := opts.(type) { case *gophercloud.AuthOptions: diff --git a/openstack/common/extensions/testing/requests_test.go b/openstack/common/extensions/testing/requests_test.go index 5a0c49ce78..37a7a5092d 100644 --- a/openstack/common/extensions/testing/requests_test.go +++ b/openstack/common/extensions/testing/requests_test.go @@ -17,7 +17,7 @@ func TestList(t *testing.T) { count := 0 - extensions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := extensions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) th.AssertNoErr(t, err) @@ -25,6 +25,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } diff --git a/openstack/compute/v2/diagnostics/testing/fixtures_test.go b/openstack/compute/v2/diagnostics/testing/fixtures_test.go index 924716c827..35cfd134bb 100644 --- a/openstack/compute/v2/diagnostics/testing/fixtures_test.go +++ b/openstack/compute/v2/diagnostics/testing/fixtures_test.go @@ -16,6 +16,7 @@ func HandleDiagnosticGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"cpu0_time":173,"memory":524288}`)) + _, err := w.Write([]byte(`{"cpu0_time":173,"memory":524288}`)) + th.AssertNoErr(t, err) }) } diff --git a/openstack/compute/v2/extensions/testing/delegate_test.go b/openstack/compute/v2/extensions/testing/delegate_test.go index c0897a16c7..a08c19a957 100644 --- a/openstack/compute/v2/extensions/testing/delegate_test.go +++ b/openstack/compute/v2/extensions/testing/delegate_test.go @@ -18,7 +18,7 @@ func TestList(t *testing.T) { HandleListExtensionsSuccessfully(t) count := 0 - extensions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := extensions.List(client.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) th.AssertNoErr(t, err) @@ -37,6 +37,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) th.CheckEquals(t, 1, count) } diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index 0f84219ec4..d22e3dfa9c 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -22,7 +22,9 @@ func TestListFlavors(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index a7cc90563e..13f7022231 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -769,7 +769,9 @@ func HandleServerCreationSuccessfully(t *testing.T, response string) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -813,7 +815,9 @@ func HandleServerCreationSuccessfully(t *testing.T, response string) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -966,7 +970,9 @@ func HandleServerListSimpleSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -986,7 +992,9 @@ func HandleServerListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -1124,7 +1132,8 @@ func HandleMetadatumGetSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - w.Write([]byte(`{ "meta": {"foo":"bar"}}`)) + _, err := w.Write([]byte(`{ "meta": {"foo":"bar"}}`)) + th.AssertNoErr(t, err) }) } @@ -1141,7 +1150,8 @@ func HandleMetadatumCreateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - w.Write([]byte(`{ "meta": {"foo":"bar"}}`)) + _, err := w.Write([]byte(`{ "meta": {"foo":"bar"}}`)) + th.AssertNoErr(t, err) }) } @@ -1163,7 +1173,8 @@ func HandleMetadataGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusOK) - w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`)) + _, err := w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`)) + th.AssertNoErr(t, err) }) } @@ -1181,7 +1192,8 @@ func HandleMetadataResetSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`)) + _, err := w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`)) + th.AssertNoErr(t, err) }) } @@ -1199,7 +1211,8 @@ func HandleMetadataUpdateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - w.Write([]byte(`{ "metadata": {"foo":"baz", "this":"those"}}`)) + _, err := w.Write([]byte(`{ "metadata": {"foo":"baz", "this":"those"}}`)) + th.AssertNoErr(t, err) }) } diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index fef218410d..0cc1afcdb6 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -77,7 +77,7 @@ func TestListClusters(t *testing.T) { count := 0 sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - clusters.List(sc, clusters.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := clusters.List(sc, clusters.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := clusters.ExtractClusters(page) th.AssertNoErr(t, err) @@ -89,6 +89,7 @@ func TestListClusters(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) @@ -104,7 +105,7 @@ func TestListDetailClusters(t *testing.T) { count := 0 sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - clusters.ListDetail(sc, clusters.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := clusters.ListDetail(sc, clusters.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := clusters.ExtractClusters(page) th.AssertNoErr(t, err) @@ -116,6 +117,7 @@ func TestListDetailClusters(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go index 3e9cb717a9..8bab4017f2 100644 --- a/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go +++ b/openstack/containerinfra/v1/clustertemplates/testing/requests_test.go @@ -85,7 +85,7 @@ func TestListClusterTemplates(t *testing.T) { sc := fake.ServiceClient() sc.Endpoint = sc.Endpoint + "v1/" - clustertemplates.List(sc, clustertemplates.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := clustertemplates.List(sc, clustertemplates.ListOpts{Limit: 2}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := clustertemplates.ExtractClusterTemplates(page) th.AssertNoErr(t, err) @@ -96,6 +96,7 @@ func TestListClusterTemplates(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go b/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go index ae4c4f00bd..51e63cc318 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go @@ -181,7 +181,9 @@ func handleListNodeGroupsLimitSuccess(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } if marker, ok := r.Form["marker"]; !ok { // No marker, this is the first request. th.TestFormValues(t, r, map[string]string{"limit": "1"}) diff --git a/openstack/dns/v2/recordsets/testing/fixtures_test.go b/openstack/dns/v2/recordsets/testing/fixtures_test.go index 4b0ba245c1..9d2289f100 100644 --- a/openstack/dns/v2/recordsets/testing/fixtures_test.go +++ b/openstack/dns/v2/recordsets/testing/fixtures_test.go @@ -201,21 +201,22 @@ var ExpectedRecordSetSliceLimited = []recordsets.RecordSet{SecondRecordSet} // HandleListByZoneSuccessfully configures the test server to respond to a ListByZone request. func HandleListByZoneSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets", - func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/zones/2150b1bf-dee2-4221-9d85-11f7886fb15f/recordsets", 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 "f7b10e9b-0cae-4a91-b162-562bc6096648": - fmt.Fprintf(w, ListByZoneOutputLimited) - case "": - fmt.Fprintf(w, ListByZoneOutput) - } - }) + w.Header().Add("Content-Type", "application/json") + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } + marker := r.Form.Get("marker") + switch marker { + case "f7b10e9b-0cae-4a91-b162-562bc6096648": + fmt.Fprintf(w, ListByZoneOutputLimited) + case "": + fmt.Fprintf(w, ListByZoneOutput) + } + }) } // HandleGetSuccessfully configures the test server to respond to a Get request. diff --git a/openstack/identity/v3/ec2tokens/requests.go b/openstack/identity/v3/ec2tokens/requests.go index cb237cc4f4..5761602736 100644 --- a/openstack/identity/v3/ec2tokens/requests.go +++ b/openstack/identity/v3/ec2tokens/requests.go @@ -267,7 +267,11 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] } if v := c["body_hash"]; v == nil { // when body_hash is not set, generate a random one - c["body_hash"] = randomBodyHash() + bodyHash, err := randomBodyHash() + if err != nil { + return nil, fmt.Errorf("failed to generate random hash") + } + c["body_hash"] = bodyHash } signedHeaders := h["X-Amz-SignedHeaders"] @@ -340,10 +344,12 @@ func sumHMAC256(key []byte, data []byte) []byte { } // randomBodyHash is a func to generate a random sha256 hexdigest. -func randomBodyHash() string { +func randomBodyHash() (string, error) { h := make([]byte, 64) - rand.Read(h) - return hex.EncodeToString(h) + if _, err := rand.Read(h); err != nil { + return "", err + } + return hex.EncodeToString(h), nil } // interfaceToMap is a func used to represent a "credentials" map element as a diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 457a6c08ed..8ea857bad4 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -119,7 +119,7 @@ func TestListEndpoints(t *testing.T) { }) count := 0 - endpoints.List(client.ServiceClient(), endpoints.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := endpoints.List(client.ServiceClient(), endpoints.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := endpoints.ExtractEndpoints(page) if err != nil { @@ -150,6 +150,7 @@ func TestListEndpoints(t *testing.T) { th.AssertDeepEquals(t, expected, actual) return true, nil }) + th.AssertNoErr(t, err) th.AssertEquals(t, 1, count) } diff --git a/openstack/identity/v3/projectendpoints/testing/requests_test.go b/openstack/identity/v3/projectendpoints/testing/requests_test.go index 8df70d5a76..9a9c17139f 100644 --- a/openstack/identity/v3/projectendpoints/testing/requests_test.go +++ b/openstack/identity/v3/projectendpoints/testing/requests_test.go @@ -71,7 +71,7 @@ func TestListEndpoints(t *testing.T) { }) count := 0 - projectendpoints.List(client.ServiceClient(), "project-id").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := projectendpoints.List(client.ServiceClient(), "project-id").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := projectendpoints.ExtractEndpoints(page) if err != nil { @@ -98,6 +98,7 @@ func TestListEndpoints(t *testing.T) { th.AssertDeepEquals(t, expected, actual) return true, nil }) + th.AssertNoErr(t, err) th.AssertEquals(t, 1, count) } diff --git a/openstack/image/v2/tasks/testing/requests_test.go b/openstack/image/v2/tasks/testing/requests_test.go index a8cc870fab..4e33814e45 100644 --- a/openstack/image/v2/tasks/testing/requests_test.go +++ b/openstack/image/v2/tasks/testing/requests_test.go @@ -29,7 +29,7 @@ func TestList(t *testing.T) { count := 0 - tasks.List(fakeclient.ServiceClient(), tasks.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := tasks.List(fakeclient.ServiceClient(), tasks.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := tasks.ExtractTasks(page) if err != nil { @@ -46,6 +46,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go b/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go index 4beea47591..8ffafa253e 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go @@ -144,7 +144,9 @@ func HandleAmphoraListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go b/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go index 2ed07d52e7..adbdf1b7b3 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go @@ -88,7 +88,9 @@ func HandleFlavorProfileListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/loadbalancer/v2/flavors/testing/fixtures.go b/openstack/loadbalancer/v2/flavors/testing/fixtures.go index dac0e584be..a3169d7ece 100644 --- a/openstack/loadbalancer/v2/flavors/testing/fixtures.go +++ b/openstack/loadbalancer/v2/flavors/testing/fixtures.go @@ -96,7 +96,9 @@ func HandleFlavorListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go index a2569f8489..a1773287f9 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go @@ -212,7 +212,9 @@ func HandleL7PolicyListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -354,7 +356,9 @@ func HandleRuleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go b/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go index 305a9c87e3..edda9b2bac 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go @@ -212,7 +212,9 @@ func HandleListenerListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go index e3604da29a..d4f714f7c5 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go @@ -447,7 +447,9 @@ func HandleLoadbalancerListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go index 379dc8c6ee..9526b9cbca 100644 --- a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go @@ -146,7 +146,9 @@ func HandleHealthmonitorListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures_test.go b/openstack/loadbalancer/v2/pools/testing/fixtures_test.go index 0544ae9eab..47d7c373f8 100644 --- a/openstack/loadbalancer/v2/pools/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/pools/testing/fixtures_test.go @@ -139,7 +139,9 @@ func HandlePoolListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -334,7 +336,9 @@ func HandleMemberListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/loadbalancer/v2/providers/testing/fixtures_test.go b/openstack/loadbalancer/v2/providers/testing/fixtures_test.go index d9bc1be49f..64db9a39bf 100644 --- a/openstack/loadbalancer/v2/providers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/providers/testing/fixtures_test.go @@ -44,7 +44,9 @@ func HandleProviderListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/networking/v2/apiversions/testing/requests_test.go b/openstack/networking/v2/apiversions/testing/requests_test.go index 7f6f482883..c99c99488b 100644 --- a/openstack/networking/v2/apiversions/testing/requests_test.go +++ b/openstack/networking/v2/apiversions/testing/requests_test.go @@ -42,7 +42,7 @@ func TestListVersions(t *testing.T) { count := 0 - apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractAPIVersions(page) if err != nil { @@ -61,6 +61,7 @@ func TestListVersions(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) @@ -75,12 +76,13 @@ func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) { w.WriteHeader(http.StatusOK) }) - apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { if _, err := apiversions.ExtractAPIVersions(page); err == nil { t.Fatalf("Expected error, got nil") } return true, nil }) + th.AssertErr(t, err) } func TestAPIInfo(t *testing.T) { @@ -134,7 +136,7 @@ func TestAPIInfo(t *testing.T) { count := 0 - apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractVersionResources(page) if err != nil { @@ -161,6 +163,7 @@ func TestAPIInfo(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) @@ -175,10 +178,11 @@ func TestNonJSONCannotBeExtractedIntoAPIVersionResources(t *testing.T) { w.WriteHeader(http.StatusOK) }) - apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := apiversions.ListVersionResources(fake.ServiceClient(), "v2.0").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { if _, err := apiversions.ExtractVersionResources(page); err == nil { t.Fatalf("Expected error, got nil") } return true, nil }) + th.AssertErr(t, err) } diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index 2b2a9b9b8f..8571183ea6 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -30,7 +30,7 @@ func TestList(t *testing.T) { count := 0 - agents.List(fake.ServiceClient(), agents.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := agents.List(fake.ServiceClient(), agents.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := agents.ExtractAgents(page) @@ -48,6 +48,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) @@ -226,7 +227,7 @@ func TestListBGPSpeakers(t *testing.T) { }) count := 0 - agents.ListBGPSpeakers(fake.ServiceClient(), agentID).EachPage( + err := agents.ListBGPSpeakers(fake.ServiceClient(), agentID).EachPage( context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ @@ -240,6 +241,7 @@ func TestListBGPSpeakers(t *testing.T) { th.AssertEquals(t, actual[0].IPVersion, 4) return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) } @@ -307,7 +309,7 @@ func TestListDRAgentHostingBGPSpeakers(t *testing.T) { }) count := 0 - agents.ListDRAgentHostingBGPSpeakers(fake.ServiceClient(), speakerID).EachPage( + err := agents.ListDRAgentHostingBGPSpeakers(fake.ServiceClient(), speakerID).EachPage( context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ @@ -322,6 +324,7 @@ func TestListDRAgentHostingBGPSpeakers(t *testing.T) { th.CheckDeepEquals(t, expected, actual) return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) 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 e07f40db15..3ec3faa391 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go @@ -27,7 +27,7 @@ func TestList(t *testing.T) { }) count := 0 - peers.List(fake.ServiceClient()).EachPage( + err := peers.List(fake.ServiceClient()).EachPage( context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ @@ -41,6 +41,7 @@ func TestList(t *testing.T) { th.CheckDeepEquals(t, expected, actual) return true, nil }) + th.AssertNoErr(t, err) } func TestGet(t *testing.T) { 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 911c328d2f..8f77107607 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -28,7 +28,7 @@ func TestList(t *testing.T) { }) count := 0 - speakers.List(fake.ServiceClient()).EachPage( + err := speakers.List(fake.ServiceClient()).EachPage( context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ @@ -42,6 +42,7 @@ func TestList(t *testing.T) { th.CheckDeepEquals(t, expected, actual) return true, nil }) + th.AssertNoErr(t, err) } func TestGet(t *testing.T) { @@ -211,7 +212,7 @@ func TestGetAdvertisedRoutes(t *testing.T) { }) count := 0 - speakers.GetAdvertisedRoutes(fake.ServiceClient(), bgpSpeakerID).EachPage( + err := speakers.GetAdvertisedRoutes(fake.ServiceClient(), bgpSpeakerID).EachPage( context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ @@ -231,6 +232,7 @@ func TestGetAdvertisedRoutes(t *testing.T) { th.CheckDeepEquals(t, expected, actual) return true, nil }) + th.AssertNoErr(t, err) } func TestAddGatewayNetwork(t *testing.T) { 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 6d40203746..59768a756d 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 @@ -68,7 +68,7 @@ func TestList(t *testing.T) { count := 0 - groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := groups.List(fake.ServiceClient(), groups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := groups.ExtractGroups(page) if err != nil { @@ -118,6 +118,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) 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 af03dccaeb..3141f19795 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 @@ -59,7 +59,7 @@ func TestList(t *testing.T) { count := 0 - policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := policies.List(fake.ServiceClient(), policies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := policies.ExtractPolicies(page) if err != nil { @@ -99,6 +99,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) 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 5f40b5844b..36469967d8 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 @@ -68,7 +68,7 @@ func TestList(t *testing.T) { count := 0 - rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractRules(page) if err != nil { @@ -117,6 +117,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go index 05e5a85174..b59bd3893d 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go @@ -28,7 +28,7 @@ func TestList(t *testing.T) { count := 0 - addressscopes.List(fake.ServiceClient(), addressscopes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := addressscopes.List(fake.ServiceClient(), addressscopes.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := addressscopes.ExtractAddressScopes(page) if err != nil { @@ -45,6 +45,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index 5ac2828806..92830dabb4 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -91,10 +91,14 @@ func TestInvalidNextPageURLs(t *testing.T) { fmt.Fprintf(w, `{"floatingips": [{}], "floatingips_links": {}}`) }) - floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - floatingips.ExtractFloatingIPs(page) + err := floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + _, err := floatingips.ExtractFloatingIPs(page) + if err != nil { + return false, err + } return true, nil }) + th.AssertErr(t, err) } func TestRequiredFieldsForCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go index a2d58ec4e8..c4ad108146 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -28,7 +28,7 @@ func TestPortForwardingList(t *testing.T) { count := 0 - portforwarding.List(fake.ServiceClient(), portforwarding.ListOpts{}, "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := portforwarding.List(fake.ServiceClient(), portforwarding.ListOpts{}, "2f95fd2b-9f6a-4e8e-9e9a-2cbe286cbf9e").EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := portforwarding.ExtractPortForwardings(page) if err != nil { @@ -59,6 +59,7 @@ func TestPortForwardingList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index fbb07c71ae..192d217eef 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -70,7 +70,7 @@ func TestList(t *testing.T) { count := 0 - routers.List(fake.ServiceClient(), routers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := routers.List(fake.ServiceClient(), routers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := routers.ExtractRouters(page) if err != nil { @@ -119,6 +119,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go index 227ef33912..7503bc7a74 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -76,7 +76,7 @@ func TestList(t *testing.T) { client := fake.ServiceClient() count := 0 - rbacpolicies.List(client, rbacpolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := rbacpolicies.List(client, rbacpolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rbacpolicies.ExtractRBACPolicies(page) if err != nil { @@ -88,6 +88,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 30a5d15565..454399f306 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -57,7 +57,7 @@ func TestList(t *testing.T) { count := 0 - rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := rules.List(fake.ServiceClient(), rules.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := rules.ExtractRules(page) if err != nil { @@ -97,6 +97,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/testing/delegate_test.go b/openstack/networking/v2/extensions/testing/delegate_test.go index c8f847873d..4741b8653f 100644 --- a/openstack/networking/v2/extensions/testing/delegate_test.go +++ b/openstack/networking/v2/extensions/testing/delegate_test.go @@ -41,7 +41,7 @@ func TestList(t *testing.T) { count := 0 - extensions.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := extensions.List(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := extensions.ExtractExtensions(page) if err != nil { @@ -65,6 +65,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index e4d0818ff6..bc4a2341ab 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -121,7 +121,7 @@ func TestList(t *testing.T) { client := fake.ServiceClient() count := 0 - trunks.List(client, trunks.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := trunks.List(client, trunks.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := trunks.ExtractTrunks(page) if err != nil { @@ -135,6 +135,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go index fd27de2add..806580dd4e 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -157,7 +157,7 @@ func TestList(t *testing.T) { count := 0 - endpointgroups.List(fake.ServiceClient(), endpointgroups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := endpointgroups.List(fake.ServiceClient(), endpointgroups.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := endpointgroups.ExtractEndpointGroups(page) if err != nil { @@ -182,6 +182,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index 993c5752cb..74b9427cc1 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -191,7 +191,7 @@ func TestList(t *testing.T) { count := 0 - ikepolicies.List(fake.ServiceClient(), ikepolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := ikepolicies.List(fake.ServiceClient(), ikepolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := ikepolicies.ExtractPolicies(page) if err != nil { @@ -222,6 +222,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go index f8ae98a27b..0363c7c63e 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -206,7 +206,7 @@ func TestList(t *testing.T) { count := 0 - ipsecpolicies.List(fake.ServiceClient(), ipsecpolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := ipsecpolicies.List(fake.ServiceClient(), ipsecpolicies.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := ipsecpolicies.ExtractPolicies(page) if err != nil { @@ -237,6 +237,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go index e697a5312a..ce9e50171e 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go @@ -111,7 +111,7 @@ func TestList(t *testing.T) { count := 0 - services.List(fake.ServiceClient(), services.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := services.List(fake.ServiceClient(), services.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := services.ExtractServices(page) if err != nil { @@ -136,6 +136,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go index 034ff0ac77..68977acee4 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -263,7 +263,7 @@ func TestList(t *testing.T) { count := 0 - siteconnections.List(fake.ServiceClient(), siteconnections.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := siteconnections.List(fake.ServiceClient(), siteconnections.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := siteconnections.ExtractConnections(page) if err != nil { @@ -306,6 +306,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index dfa3c5bf33..c4bac8dc0c 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -28,7 +28,7 @@ func TestList(t *testing.T) { count := 0 - subnets.List(fake.ServiceClient(), subnets.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := subnets.List(fake.ServiceClient(), subnets.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := subnets.ExtractSubnets(page) if err != nil { @@ -47,6 +47,7 @@ func TestList(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 7b791f38df..0f440472ac 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -50,7 +50,9 @@ func HandleListContainerInfoSuccessfully(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index a9001f1396..ca462a061f 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -241,7 +241,10 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str if _, err := io.Copy(hash, readSeeker); err != nil { return nil, nil, "", err } - readSeeker.Seek(0, io.SeekStart) + _, err = readSeeker.Seek(0, io.SeekStart) + if err != nil { + return nil, nil, "", err + } h["ETag"] = fmt.Sprintf("%x", hash.Sum(nil)) diff --git a/openstack/objectstorage/v1/objects/testing/fixtures_test.go b/openstack/objectstorage/v1/objects/testing/fixtures_test.go index 5c4127a256..fd24937f6c 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures_test.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures_test.go @@ -112,7 +112,9 @@ func HandleListObjectsInfoSuccessfully(t *testing.T, options ...option) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -149,7 +151,9 @@ func HandleListSubdirSuccessfully(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -197,7 +201,8 @@ func HandleCreateTextObjectSuccessfully(t *testing.T, content string, options .. th.TestBody(t, r, `Did gyre and gimble in the wabe`) hash := md5.New() - io.WriteString(hash, content) + _, err := io.WriteString(hash, content) + th.AssertNoErr(t, err) localChecksum := hash.Sum(nil) w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum)) @@ -216,7 +221,8 @@ func HandleCreateTextWithCacheControlSuccessfully(t *testing.T, content string) th.TestBody(t, r, `All mimsy were the borogoves`) hash := md5.New() - io.WriteString(hash, content) + _, err := io.WriteString(hash, content) + th.AssertNoErr(t, err) localChecksum := hash.Sum(nil) w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum)) @@ -239,7 +245,8 @@ func HandleCreateTypelessObjectSuccessfully(t *testing.T, content string) { } hash := md5.New() - io.WriteString(hash, content) + _, err := io.WriteString(hash, content) + th.AssertNoErr(t, err) localChecksum := hash.Sum(nil) w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum)) diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index f20bdb9f0b..0d471b8781 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -135,7 +135,8 @@ func TestDownloadReader(t *testing.T) { // Check reader buf := bytes.NewBuffer(make([]byte, 0)) - io.CopyN(buf, response.Body, 10) + _, err := io.CopyN(buf, response.Body, 10) + th.AssertNoErr(t, err) th.CheckEquals(t, "Successful", string(buf.Bytes())) } @@ -448,7 +449,8 @@ func TestETag(t *testing.T) { th.AssertEquals(t, false, ok) hash := md5.New() - io.WriteString(hash, content) + _, err = io.WriteString(hash, content) + th.AssertNoErr(t, err) localChecksum := fmt.Sprintf("%x", hash.Sum(nil)) createOpts = objects.CreateOpts{ diff --git a/openstack/orchestration/v1/apiversions/testing/requests_test.go b/openstack/orchestration/v1/apiversions/testing/requests_test.go index 163469fec1..ec4ad6d228 100644 --- a/openstack/orchestration/v1/apiversions/testing/requests_test.go +++ b/openstack/orchestration/v1/apiversions/testing/requests_test.go @@ -43,7 +43,7 @@ func TestListVersions(t *testing.T) { count := 0 - apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := apiversions.ExtractAPIVersions(page) if err != nil { @@ -68,6 +68,7 @@ func TestListVersions(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) if count != 1 { t.Errorf("Expected 1 page, got %d", count) @@ -82,10 +83,11 @@ func TestNonJSONCannotBeExtractedIntoAPIVersions(t *testing.T) { w.WriteHeader(http.StatusOK) }) - apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + err := apiversions.ListVersions(fake.ServiceClient()).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { if _, err := apiversions.ExtractAPIVersions(page); err == nil { t.Fatalf("Expected error, got nil") } return true, nil }) + th.AssertErr(t, err) } diff --git a/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go b/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go index 50db64b3f4..c97ba01e4e 100644 --- a/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go +++ b/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go @@ -84,7 +84,9 @@ func HandleListSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } var output string if r.Form.Get("with_description") == "true" { if r.Form.Get("name") == listFilterRegex { @@ -376,7 +378,9 @@ func HandleGenerateTemplateSuccessfully(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } if r.Form.Get("template_type") == "hot" { w.WriteHeader(http.StatusOK) fmt.Fprint(w, GenerateTemplateOutput) diff --git a/openstack/orchestration/v1/stackevents/testing/fixtures_test.go b/openstack/orchestration/v1/stackevents/testing/fixtures_test.go index 28dd6cc01e..4c22428a9a 100644 --- a/openstack/orchestration/v1/stackevents/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stackevents/testing/fixtures_test.go @@ -244,7 +244,9 @@ func HandleListSuccessfully(t *testing.T, output string) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": @@ -369,7 +371,9 @@ func HandleListResourceEventsSuccessfully(t *testing.T, output string) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/orchestration/v1/stackresources/testing/fixtures_test.go b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go index 63b79569b0..e675ab642a 100644 --- a/openstack/orchestration/v1/stackresources/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go @@ -151,7 +151,9 @@ func HandleListSuccessfully(t *testing.T, output string) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/orchestration/v1/stacks/testing/fixtures_test.go b/openstack/orchestration/v1/stacks/testing/fixtures_test.go index d473a50e2f..605f544399 100644 --- a/openstack/orchestration/v1/stacks/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stacks/testing/fixtures_test.go @@ -136,7 +136,9 @@ func HandleListSuccessfully(t *testing.T, output string) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Set("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go index e2470ab177..00585c9c26 100644 --- a/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go @@ -215,7 +215,9 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("offset") shareID := r.Form.Get("share_id") if shareID != "65a34695-f9e5-4eea-b48d-a0b261d82943" { @@ -282,7 +284,9 @@ func MockListDetailResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("offset") shareID := r.Form.Get("share_id") if shareID != "65a34695-f9e5-4eea-b48d-a0b261d82943" { diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go index cc1d664d88..acdd04d53f 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go @@ -271,7 +271,9 @@ func HandlePoolsListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } fmt.Fprintf(w, PoolsListBody) }) @@ -281,7 +283,9 @@ func HandlePoolsListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } fmt.Fprintf(w, PoolsListBodyDetail) }) } diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go index 9ce5d2b928..cfa3b1712d 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go @@ -78,7 +78,9 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("offset") switch marker { @@ -149,7 +151,9 @@ func MockFilteredListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("offset") switch marker { case "": diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go index f6370a1f85..89a2c5461e 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go @@ -265,7 +265,9 @@ func MockListDetailResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("offset") switch marker { diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go index 75432bdb9f..e34d1081a4 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go @@ -193,7 +193,9 @@ func MockListDetailResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("offset") switch marker { diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index 276031dd8b..858b30f88b 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -167,7 +167,9 @@ func TestListCronTriggers(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index c51196ffb5..bc824b34a2 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -161,7 +161,9 @@ func TestListExecutions(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index 1e516010ab..78f0f78a09 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -163,7 +163,9 @@ func TestListWorkflows(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } marker := r.Form.Get("marker") switch marker { case "": diff --git a/pagination/testing/marker_test.go b/pagination/testing/marker_test.go index a8a01fe9ca..a1084ac12f 100644 --- a/pagination/testing/marker_test.go +++ b/pagination/testing/marker_test.go @@ -40,7 +40,9 @@ func createMarkerPaged(t *testing.T) pagination.Pager { testhelper.SetupHTTP() testhelper.Mux.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) { - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } ms := r.Form["marker"] switch { case len(ms) == 0: diff --git a/provider_client.go b/provider_client.go index 89c8335a0a..d8cc62524b 100644 --- a/provider_client.go +++ b/provider_client.go @@ -470,7 +470,9 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, } if options.RawBody != nil { if seeker, ok := options.RawBody.(io.Seeker); ok { - seeker.Seek(0, 0) + if _, err := seeker.Seek(0, 0); err != nil { + return nil, err + } } } state.hasReauthenticated = true diff --git a/testhelper/http_responses.go b/testhelper/http_responses.go index 6f8f2db4b1..a8a7f36140 100644 --- a/testhelper/http_responses.go +++ b/testhelper/http_responses.go @@ -71,7 +71,9 @@ func TestFormValues(t *testing.T, r *http.Request, values map[string]string) { want.Add(k, v) } - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", r) + } if !reflect.DeepEqual(want, r.Form) { t.Errorf("Request parameters = %v, want %v", r.Form, want) } From 9b5a6af4269e394fb5939f934bfe8c4a036afc89 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Apr 2024 14:21:43 +0100 Subject: [PATCH 1862/2296] lint: Resolve ineffectual assignments to err (ineffassign) Signed-off-by: Stephen Finucane --- .golangci.yaml | 3 --- .../baremetal/httpbasic/ports_test.go | 2 ++ .../openstack/baremetal/noauth/ports_test.go | 2 ++ .../compute/v2/instance_actions_test.go | 1 + .../openstack/compute/v2/servers_test.go | 1 + .../openstack/dns/v2/transfers_test.go | 2 ++ .../openstack/identity/v3/limits_test.go | 1 + .../openstack/identity/v3/reauth_test.go | 1 + .../identity/v3/registeredlimits_test.go | 1 + .../openstack/image/v2/tasks_test.go | 1 + .../keymanager/v1/containers_test.go | 2 ++ .../openstack/messaging/v2/claims_test.go | 16 +++++++------- .../openstack/messaging/v2/message_test.go | 21 +++++++++++++++++++ .../openstack/messaging/v2/queue_test.go | 9 ++++++++ .../v2/extensions/attributestags_test.go | 1 + .../extensions/trunk_details/trunks_test.go | 1 + .../v2/extensions/trunks/trunks_test.go | 4 ++-- .../openstack/networking/v2/networks_test.go | 1 + .../openstack/networking/v2/ports_test.go | 12 +++++------ openstack/config/clouds/clouds.go | 3 +++ 20 files changed, 66 insertions(+), 19 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 301c329d77..be881c44f3 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,7 +1,4 @@ --- -linters: - disable: - - ineffassign # ineffectual assignment to tags (ineffassign) issues: exclude: - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) diff --git a/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go index d425230c8f..9912c49322 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/ports_test.go @@ -23,6 +23,7 @@ func TestPortsCreateDestroy(t *testing.T) { client.Microversion = "1.53" node, err := v1.CreateFakeNode(t, client) + th.AssertNoErr(t, err) port, err := v1.CreatePort(t, client, node) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) @@ -58,6 +59,7 @@ func TestPortsUpdate(t *testing.T) { client.Microversion = "1.53" node, err := v1.CreateFakeNode(t, client) + th.AssertNoErr(t, err) port, err := v1.CreatePort(t, client, node) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) diff --git a/internal/acceptance/openstack/baremetal/noauth/ports_test.go b/internal/acceptance/openstack/baremetal/noauth/ports_test.go index f27b7fe78b..980b2dec32 100644 --- a/internal/acceptance/openstack/baremetal/noauth/ports_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/ports_test.go @@ -23,6 +23,7 @@ func TestPortsCreateDestroy(t *testing.T) { client.Microversion = "1.53" node, err := v1.CreateFakeNode(t, client) + th.AssertNoErr(t, err) port, err := v1.CreatePort(t, client, node) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) @@ -58,6 +59,7 @@ func TestPortsUpdate(t *testing.T) { client.Microversion = "1.53" node, err := v1.CreateFakeNode(t, client) + th.AssertNoErr(t, err) port, err := v1.CreatePort(t, client, node) th.AssertNoErr(t, err) defer v1.DeleteNode(t, client, node) diff --git a/internal/acceptance/openstack/compute/v2/instance_actions_test.go b/internal/acceptance/openstack/compute/v2/instance_actions_test.go index 2e533acef4..fb43cd72d6 100644 --- a/internal/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/internal/acceptance/openstack/compute/v2/instance_actions_test.go @@ -60,6 +60,7 @@ func TestInstanceActionsMicroversions(t *testing.T) { } err = servers.Reboot(context.TODO(), client, server.ID, rebootOpts).ExtractErr() + th.AssertNoErr(t, err) if err = WaitForComputeStatus(client, server, "ACTIVE"); err != nil { t.Fatal(err) } diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 9b6367674a..9aa8133de9 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -149,6 +149,7 @@ func TestServersUpdate(t *testing.T) { return latest.Name == alternateName, nil }) + th.AssertNoErr(t, err) } func TestServersMetadata(t *testing.T) { diff --git a/internal/acceptance/openstack/dns/v2/transfers_test.go b/internal/acceptance/openstack/dns/v2/transfers_test.go index 53cf5dfe35..1149bfd0b3 100644 --- a/internal/acceptance/openstack/dns/v2/transfers_test.go +++ b/internal/acceptance/openstack/dns/v2/transfers_test.go @@ -74,9 +74,11 @@ func TestTransferRequestAccept(t *testing.T) { // Create transfers request to new tenant transferRequest, err := CreateTransferRequest(t, client, zone, project.ID) + th.AssertNoErr(t, err) // Accept Zone Transfer Request transferAccept, err := CreateTransferAccept(t, client, transferRequest.ID, transferRequest.Key) + th.AssertNoErr(t, err) allTransferAcceptsPages, err := transferAccepts.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/identity/v3/limits_test.go b/internal/acceptance/openstack/identity/v3/limits_test.go index 71ddda57cf..72dc12e4fb 100644 --- a/internal/acceptance/openstack/identity/v3/limits_test.go +++ b/internal/acceptance/openstack/identity/v3/limits_test.go @@ -60,6 +60,7 @@ func TestLimitsCRUD(t *testing.T) { th.AssertNoErr(t, err) svList, err := services.ExtractServices(allPages) + th.AssertNoErr(t, err) serviceID := "" for _, service := range svList { serviceID = service.ID diff --git a/internal/acceptance/openstack/identity/v3/reauth_test.go b/internal/acceptance/openstack/identity/v3/reauth_test.go index 838222eae8..2936ffce19 100644 --- a/internal/acceptance/openstack/identity/v3/reauth_test.go +++ b/internal/acceptance/openstack/identity/v3/reauth_test.go @@ -28,6 +28,7 @@ func TestReauthAuthResultDeadlock(t *testing.T) { provider.SetToken("this is not a valid token") client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) + th.AssertNoErr(t, err) pages, err := projects.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) _, err = projects.ExtractProjects(pages) diff --git a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go index bb4df961e2..ee0b61022b 100644 --- a/internal/acceptance/openstack/identity/v3/registeredlimits_test.go +++ b/internal/acceptance/openstack/identity/v3/registeredlimits_test.go @@ -29,6 +29,7 @@ func TestRegisteredLimitsCRUD(t *testing.T) { th.AssertNoErr(t, err) svList, err := services.ExtractServices(allServicePages) + th.AssertNoErr(t, err) serviceID := "" for _, service := range svList { serviceID = service.ID diff --git a/internal/acceptance/openstack/image/v2/tasks_test.go b/internal/acceptance/openstack/image/v2/tasks_test.go index d5c4bcccd0..5859ff5632 100644 --- a/internal/acceptance/openstack/image/v2/tasks_test.go +++ b/internal/acceptance/openstack/image/v2/tasks_test.go @@ -32,6 +32,7 @@ func TestTasksListEachPage(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) } func TestTasksListAllPages(t *testing.T) { diff --git a/internal/acceptance/openstack/keymanager/v1/containers_test.go b/internal/acceptance/openstack/keymanager/v1/containers_test.go index 7459558f93..9ebb5b3058 100644 --- a/internal/acceptance/openstack/keymanager/v1/containers_test.go +++ b/internal/acceptance/openstack/keymanager/v1/containers_test.go @@ -97,6 +97,7 @@ func TestCertificateContainer(t *testing.T) { container, err := CreateCertificateContainer(t, client, passphrase, private, certificate) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) + th.AssertNoErr(t, err) defer DeleteContainer(t, client, containerID) } @@ -141,6 +142,7 @@ func TestRSAContainer(t *testing.T) { container, err := CreateRSAContainer(t, client, passphrase, private, public) th.AssertNoErr(t, err) containerID, err := ParseID(container.ContainerRef) + th.AssertNoErr(t, err) defer DeleteContainer(t, client, containerID) } diff --git a/internal/acceptance/openstack/messaging/v2/claims_test.go b/internal/acceptance/openstack/messaging/v2/claims_test.go index e917c5561d..ad2772f50e 100644 --- a/internal/acceptance/openstack/messaging/v2/claims_test.go +++ b/internal/acceptance/openstack/messaging/v2/claims_test.go @@ -21,6 +21,7 @@ func TestCRUDClaim(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) clientID = "3381af92-2b9e-11e3-b191-71861300734d" @@ -34,8 +35,8 @@ func TestCRUDClaim(t *testing.T) { th.AssertNoErr(t, err) } - clientID = "3381af92-2b9e-11e3-b191-7186130073dd" claimedMessages, err := CreateClaim(t, client, createdQueueName) + th.AssertNoErr(t, err) claimIDs, _ := ExtractIDs(claimedMessages) tools.PrintResource(t, claimedMessages) @@ -47,21 +48,20 @@ func TestCRUDClaim(t *testing.T) { for _, claimID := range claimIDs { t.Logf("Attempting to update claim: %s", claimID) - updateErr := claims.Update(context.TODO(), client, createdQueueName, claimID, updateOpts).ExtractErr() - - if updateErr != nil { + err := claims.Update(context.TODO(), client, createdQueueName, claimID, updateOpts).ExtractErr() + if err != nil { t.Fatalf("Unable to update claim %s: %v", claimID, err) } else { t.Logf("Successfully updated claim: %s", claimID) } - updatedClaim, getErr := GetClaim(t, client, createdQueueName, claimID) - if getErr != nil { - t.Fatalf("Unable to retrieve claim %s: %v", claimID, getErr) + updatedClaim, err := GetClaim(t, client, createdQueueName, claimID) + if err != nil { + t.Fatalf("Unable to retrieve claim %s: %v", claimID, err) } tools.PrintResource(t, updatedClaim) - err := DeleteClaim(t, client, createdQueueName, claimID) + err = DeleteClaim(t, client, createdQueueName, claimID) th.AssertNoErr(t, err) } } diff --git a/internal/acceptance/openstack/messaging/v2/message_test.go b/internal/acceptance/openstack/messaging/v2/message_test.go index e2ad2a7e70..fed1dc6db8 100644 --- a/internal/acceptance/openstack/messaging/v2/message_test.go +++ b/internal/acceptance/openstack/messaging/v2/message_test.go @@ -22,6 +22,7 @@ func TestListMessages(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) totalNumberOfMessages := 3 @@ -35,6 +36,7 @@ func TestListMessages(t *testing.T) { // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) + th.AssertNoErr(t, err) listOpts := messages.ListOpts{} @@ -52,6 +54,7 @@ func TestListMessages(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) th.AssertEquals(t, totalNumberOfMessages, currentNumberOfMessages) } @@ -64,6 +67,7 @@ func TestCreateMessages(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) _, err = CreateMessage(t, client, createdQueueName) @@ -79,6 +83,7 @@ func TestGetMessages(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) _, err = CreateMessage(t, client, createdQueueName) @@ -89,6 +94,7 @@ func TestGetMessages(t *testing.T) { // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) + th.AssertNoErr(t, err) listOpts := messages.ListOpts{} @@ -107,6 +113,7 @@ func TestGetMessages(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) getMessageOpts := messages.GetMessagesOpts{ IDs: messageIDs, @@ -129,6 +136,7 @@ func TestGetMessage(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) _, err = CreateMessage(t, client, createdQueueName) @@ -137,6 +145,7 @@ func TestGetMessage(t *testing.T) { // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) + th.AssertNoErr(t, err) listOpts := messages.ListOpts{} @@ -155,6 +164,7 @@ func TestGetMessage(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) for _, messageID := range messageIDs { t.Logf("Attempting to get message from queue %s: %s", createdQueueName, messageID) @@ -175,6 +185,7 @@ func TestDeleteMessagesIDs(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) _, err = CreateMessage(t, client, createdQueueName) @@ -186,6 +197,7 @@ func TestDeleteMessagesIDs(t *testing.T) { clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) + th.AssertNoErr(t, err) listOpts := messages.ListOpts{} @@ -205,6 +217,7 @@ func TestDeleteMessagesIDs(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) deleteOpts := messages.DeleteMessagesOpts{ IDs: messageIDs, @@ -218,6 +231,7 @@ func TestDeleteMessagesIDs(t *testing.T) { t.Logf("Attempting to list messages.") messageList, err := ListMessages(t, client, createdQueueName) + th.AssertNoErr(t, err) if len(messageList) > 0 { t.Fatalf("Did not delete all specified messages in the queue.") @@ -233,6 +247,7 @@ func TestDeleteMessagesPop(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) for i := 0; i < 5; i++ { @@ -244,8 +259,10 @@ func TestDeleteMessagesPop(t *testing.T) { clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) + th.AssertNoErr(t, err) messageList, err := ListMessages(t, client, createdQueueName) + th.AssertNoErr(t, err) messagesNumber := len(messageList) popNumber := 3 @@ -263,6 +280,7 @@ func TestDeleteMessagesPop(t *testing.T) { tools.PrintResource(t, popMessages) messageList, err = ListMessages(t, client, createdQueueName) + th.AssertNoErr(t, err) if len(messageList) != messagesNumber-popNumber { t.Fatalf("Unable to Pop specified number of messages.") } @@ -277,6 +295,7 @@ func TestDeleteMessage(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) _, err = CreateMessage(t, client, createdQueueName) @@ -285,6 +304,7 @@ func TestDeleteMessage(t *testing.T) { // Use a different client/clientID in order to see messages on the Queue clientID = "3381af92-2b9e-11e3-b191-71861300734d" client, err = clients.NewMessagingV2Client(clientID) + th.AssertNoErr(t, err) listOpts := messages.ListOpts{} @@ -303,6 +323,7 @@ func TestDeleteMessage(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) for _, messageID := range messageIDs { t.Logf("Attempting to delete message from queue %s: %s", createdQueueName, messageID) diff --git a/internal/acceptance/openstack/messaging/v2/queue_test.go b/internal/acceptance/openstack/messaging/v2/queue_test.go index 99cf177cf0..a259fd5fb9 100644 --- a/internal/acceptance/openstack/messaging/v2/queue_test.go +++ b/internal/acceptance/openstack/messaging/v2/queue_test.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/messaging/v2/queues" "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestCRUDQueues(t *testing.T) { @@ -21,9 +22,11 @@ func TestCRUDQueues(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) createdQueue, err := queues.Get(context.TODO(), client, createdQueueName).Extract() + th.AssertNoErr(t, err) tools.PrintResource(t, createdQueue) tools.PrintResource(t, createdQueue.Extra) @@ -48,6 +51,7 @@ func TestCRUDQueues(t *testing.T) { } updatedQueue, err := GetQueue(t, client, createdQueueName) + th.AssertNoErr(t, err) tools.PrintResource(t, updateResult) tools.PrintResource(t, updatedQueue) @@ -63,9 +67,11 @@ func TestListQueues(t *testing.T) { } firstQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, firstQueueName) secondQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, secondQueueName) listOpts := queues.ListOpts{ @@ -86,6 +92,7 @@ func TestListQueues(t *testing.T) { return true, nil }) + th.AssertNoErr(t, err) } func TestStatQueue(t *testing.T) { @@ -97,6 +104,7 @@ func TestStatQueue(t *testing.T) { } createdQueueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, createdQueueName) queueStats, err := queues.GetStats(context.TODO(), client, createdQueueName).Extract() @@ -139,6 +147,7 @@ func TestPurge(t *testing.T) { } queueName, err := CreateQueue(t, client) + th.AssertNoErr(t, err) defer DeleteQueue(t, client, queueName) purgeOpts := queues.PurgeOpts{ diff --git a/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go index d35ae5cc30..c691a575f3 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/attributestags_test.go @@ -67,6 +67,7 @@ func TestTags(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, true, exists) noexists, err := attributestags.Confirm(context.TODO(), client, "networks", network.ID, "a").Extract() + th.AssertNoErr(t, err) th.AssertEquals(t, false, noexists) // Delete all tags diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go index 2cab0d035a..3a9870d35b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunk_details/trunks_test.go @@ -101,6 +101,7 @@ func TestListPortWithSubports(t *testing.T) { // Test GET port with trunk details err = ports.Get(context.TODO(), client, parentPort.ID).ExtractInto(&port) + th.AssertNoErr(t, err) th.AssertEquals(t, trunk.ID, port.TrunkDetails.TrunkID) th.AssertEquals(t, 2, len(port.TrunkDetails.SubPorts)) th.AssertDeepEquals(t, trunk_details.Subport{ diff --git a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go index fac6a9df5d..65ebd90a78 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/trunks/trunks_test.go @@ -263,7 +263,7 @@ func TestTrunkTags(t *testing.T) { // docs say list of tags, but it's a set e.g no duplicates Tags: []string{"a", "b", "c"}, } - tags, err := attributestags.ReplaceAll(context.TODO(), client, "trunks", trunk.ID, tagReplaceAllOpts).Extract() + _, err = attributestags.ReplaceAll(context.TODO(), client, "trunks", trunk.ID, tagReplaceAllOpts).Extract() if err != nil { t.Fatalf("Unable to set trunk tags: %v", err) } @@ -272,7 +272,7 @@ func TestTrunkTags(t *testing.T) { if err != nil { t.Fatalf("Unable to get trunk: %v", err) } - tags = gtrunk.Tags + tags := gtrunk.Tags sort.Strings(tags) // Ensure ordering, older OpenStack versions aren't sorted... th.AssertDeepEquals(t, []string{"a", "b", "c"}, tags) diff --git a/internal/acceptance/openstack/networking/v2/networks_test.go b/internal/acceptance/openstack/networking/v2/networks_test.go index 8dfa1986da..a3e3928590 100644 --- a/internal/acceptance/openstack/networking/v2/networks_test.go +++ b/internal/acceptance/openstack/networking/v2/networks_test.go @@ -66,6 +66,7 @@ func TestNetworksExternalList(t *testing.T) { th.AssertNoErr(t, err) v, err := networks.ExtractNetworks(allPages) + th.AssertNoErr(t, err) th.AssertEquals(t, len(v), 0) } diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index af13180615..70e6d05bb1 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -189,14 +189,14 @@ func TestPortsRemoveSecurityGroups(t *testing.T) { updateOpts := ports.UpdateOpts{ SecurityGroups: &[]string{group.ID}, } - newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() + _, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Remove the group updateOpts = ports.UpdateOpts{ SecurityGroups: &[]string{}, } - newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -236,7 +236,7 @@ func TestPortsDontAlterSecurityGroups(t *testing.T) { updateOpts := ports.UpdateOpts{ SecurityGroups: &[]string{group.ID}, } - newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() + _, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Update the port again @@ -244,7 +244,7 @@ func TestPortsDontAlterSecurityGroups(t *testing.T) { updateOpts = ports.UpdateOpts{ Name: &name, } - newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) @@ -307,14 +307,14 @@ func TestPortsRemoveAddressPair(t *testing.T) { {IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"}, }, } - newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() + _, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) // Remove the address pair updateOpts = ports.UpdateOpts{ AllowedAddressPairs: &[]ports.AddressPair{}, } - newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() + newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) tools.PrintResource(t, newPort) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index b4ad3a9252..55e46f2da3 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -217,6 +217,9 @@ func mergeClouds(override, cloud Cloud) (Cloud, error) { var mergedCloud Cloud mergedInterface := mergeInterfaces(overrideInterface, cloudInterface) mergedJson, err := json.Marshal(mergedInterface) + if err != nil { + return Cloud{}, err + } err = json.Unmarshal(mergedJson, &mergedCloud) if err != nil { return Cloud{}, err From 2cbd121dd85afb6d5ce7800726ccf1490508fd8a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Apr 2024 14:57:08 +0100 Subject: [PATCH 1863/2296] lint: Simplify various bits of code (gosimple) This resolves remaining issues with the gosimple linter. Signed-off-by: Stephen Finucane --- .golangci.yaml | 4 ---- internal/acceptance/openstack/client_test.go | 3 +-- .../networking/v2/extensions/dns/dns_test.go | 6 ++---- .../networking/v2/extensions/mtu/mtu_test.go | 3 +-- .../extensions/portsbinding/portsbinding_test.go | 4 +--- .../blockstorage/v2/schedulerstats/results.go | 6 +++--- .../blockstorage/v3/schedulerstats/results.go | 6 +++--- .../v1/clusters/testing/requests_test.go | 6 ++---- .../v2/extensions/dns/testing/requests_test.go | 3 +-- .../v1/objects/testing/requests_test.go | 2 +- .../sharedfilesystems/v2/schedulerstats/results.go | 6 +++--- provider_client.go | 14 +++++++------- 12 files changed, 25 insertions(+), 38 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index be881c44f3..fd527266b0 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -6,7 +6,3 @@ issues: - SA4006 # this value of `***` is never used (staticcheck) - SA5011 # possible nil pointer dereference (staticcheck) - SA6005 # should use strings.EqualFold instead (staticcheck) - - S1020 # when ok is true, err can't be nil (gosimple) - - S1021 # should merge variable declaration with assignment on next line (gosimple) - - S1030 # should use buf.String() instead of string(buf.Bytes()) (gosimple) - - S1034 # assigning the result of this type assertion to a variable (***) could eliminate type assertions in switch cases (gosimple) diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index 515f615361..7902961c55 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -97,8 +97,7 @@ func TestEC2AuthMethod(t *testing.T) { newClient, err := clients.NewIdentityV3UnauthenticatedClient() th.AssertNoErr(t, err) - var ec2AuthOptions tokens.AuthOptionsBuilder - ec2AuthOptions = &ec2tokens.AuthOptions{ + ec2AuthOptions := &ec2tokens.AuthOptions{ Access: "181920", Secret: "secretKey", } diff --git a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go index c9c32d4940..d709a4252d 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/dns/dns_test.go @@ -115,8 +115,7 @@ func TestDNSPortCRUDL(t *testing.T) { Name: &newPortName, Description: &newPortDescription, } - var updateOpts ports.UpdateOptsBuilder - updateOpts = dns.PortUpdateOptsExt{ + updateOpts := dns.PortUpdateOptsExt{ UpdateOptsBuilder: portUpdateOpts, DNSName: &newDNSName, } @@ -233,8 +232,7 @@ func TestDNSNetwork(t *testing.T) { Name: &newNetworkName, Description: &newNetworkDescription, } - var updateOpts networks.UpdateOptsBuilder - updateOpts = dns.NetworkUpdateOptsExt{ + updateOpts := dns.NetworkUpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, DNSDomain: &newNetworkDNSDomain, } diff --git a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go index 8f0e3b5e38..a71c96cc43 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/mtu/mtu_test.go @@ -106,8 +106,7 @@ func TestMTUNetworkCRUDL(t *testing.T) { networkUpdateOpts := networks.UpdateOpts{ Description: &newNetworkDescription, } - var updateOpts networks.UpdateOptsBuilder - updateOpts = mtu.UpdateOptsExt{ + updateOpts := mtu.UpdateOptsExt{ UpdateOptsBuilder: networkUpdateOpts, MTU: newNetworkMTU, } 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 9e424d849d..50a4dcd28c 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -54,9 +54,7 @@ func TestPortsbindingCRUD(t *testing.T) { Description: &newPortDescription, } - var finalUpdateOpts ports.UpdateOptsBuilder - - finalUpdateOpts = portsbinding.UpdateOptsExt{ + finalUpdateOpts := portsbinding.UpdateOptsExt{ UpdateOptsBuilder: updateOpts, HostID: &newHostID, Profile: newProfile, diff --git a/openstack/blockstorage/v2/schedulerstats/results.go b/openstack/blockstorage/v2/schedulerstats/results.go index 2170c483ed..8c18b261ba 100644 --- a/openstack/blockstorage/v2/schedulerstats/results.go +++ b/openstack/blockstorage/v2/schedulerstats/results.go @@ -61,11 +61,11 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { // value, "unknown", or "infinite" parseCapacity := func(capacity interface{}) float64 { if capacity != nil { - switch capacity.(type) { + switch c := capacity.(type) { case float64: - return capacity.(float64) + return c case string: - if capacity.(string) == "infinite" { + if c == "infinite" { return math.Inf(1) } } diff --git a/openstack/blockstorage/v3/schedulerstats/results.go b/openstack/blockstorage/v3/schedulerstats/results.go index 2170c483ed..8c18b261ba 100644 --- a/openstack/blockstorage/v3/schedulerstats/results.go +++ b/openstack/blockstorage/v3/schedulerstats/results.go @@ -61,11 +61,11 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { // value, "unknown", or "infinite" parseCapacity := func(capacity interface{}) float64 { if capacity != nil { - switch capacity.(type) { + switch c := capacity.(type) { case float64: - return capacity.(float64) + return c case string: - if capacity.(string) == "infinite" { + if c == "infinite" { return math.Inf(1) } } diff --git a/openstack/containerinfra/v1/clusters/testing/requests_test.go b/openstack/containerinfra/v1/clusters/testing/requests_test.go index 0cc1afcdb6..c7cd21efcc 100644 --- a/openstack/containerinfra/v1/clusters/testing/requests_test.go +++ b/openstack/containerinfra/v1/clusters/testing/requests_test.go @@ -163,8 +163,7 @@ func TestUpgradeCluster(t *testing.T) { HandleUpgradeClusterSuccessfully(t) - var opts clusters.UpgradeOptsBuilder - opts = clusters.UpgradeOpts{ + opts := clusters.UpgradeOpts{ ClusterTemplate: "0562d357-8641-4759-8fed-8173f02c9633", } @@ -216,8 +215,7 @@ func TestResizeCluster(t *testing.T) { nodeCount := 2 - var opts clusters.ResizeOptsBuilder - opts = clusters.ResizeOpts{ + opts := clusters.ResizeOpts{ NodeCount: &nodeCount, } diff --git a/openstack/networking/v2/extensions/dns/testing/requests_test.go b/openstack/networking/v2/extensions/dns/testing/requests_test.go index 0f6c99c1a2..645edf7d32 100644 --- a/openstack/networking/v2/extensions/dns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/dns/testing/requests_test.go @@ -74,8 +74,7 @@ func TestPortList(t *testing.T) { }, } - var listOptsBuilder ports.ListOptsBuilder - listOptsBuilder = dns.PortListOptsExt{ + listOptsBuilder := dns.PortListOptsExt{ ListOptsBuilder: ports.ListOpts{}, DNSName: "test-port", } diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 0d471b8781..60747a2efc 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -137,7 +137,7 @@ func TestDownloadReader(t *testing.T) { buf := bytes.NewBuffer(make([]byte, 0)) _, err := io.CopyN(buf, response.Body, 10) th.AssertNoErr(t, err) - th.CheckEquals(t, "Successful", string(buf.Bytes())) + th.CheckEquals(t, "Successful", buf.String()) } func TestDownloadExtraction(t *testing.T) { diff --git a/openstack/sharedfilesystems/v2/schedulerstats/results.go b/openstack/sharedfilesystems/v2/schedulerstats/results.go index fd25924851..8aa84baa3c 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/results.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/results.go @@ -74,11 +74,11 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { // value, "unknown", or "infinite" parseCapacity := func(capacity interface{}) float64 { if capacity != nil { - switch capacity.(type) { + switch c := capacity.(type) { case float64: - return capacity.(float64) + return c case string: - if capacity.(string) == "infinite" { + if c == "infinite" { return math.Inf(1) } } diff --git a/provider_client.go b/provider_client.go index d8cc62524b..4a57f3d1e6 100644 --- a/provider_client.go +++ b/provider_client.go @@ -478,15 +478,15 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, state.hasReauthenticated = true resp, err = client.doRequest(ctx, method, url, options, state) if err != nil { - switch err.(type) { + switch e := err.(type) { case *ErrUnexpectedResponseCode: - e := &ErrErrorAfterReauthentication{} - e.ErrOriginal = err.(*ErrUnexpectedResponseCode) - return nil, e + err := &ErrErrorAfterReauthentication{} + err.ErrOriginal = e + return nil, err default: - e := &ErrErrorAfterReauthentication{} - e.ErrOriginal = err - return nil, e + err := &ErrErrorAfterReauthentication{} + err.ErrOriginal = e + return nil, err } } return resp, nil From d2b231897e89bda28439e73f3e0511fb7fbaa4ce Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Mar 2024 15:20:03 +0000 Subject: [PATCH 1864/2296] lint: Remove unused variables (staticcheck) Signed-off-by: Stephen Finucane --- .golangci.yaml | 1 - .../networking/v2/extensions/bgp/peers/bgppeers_test.go | 2 +- internal/acceptance/openstack/networking/v2/ports_test.go | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index fd527266b0..5108f3708c 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -3,6 +3,5 @@ issues: exclude: - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) - SA1019 # (deprecated standard library function) (staticcheck) - - SA4006 # this value of `***` is never used (staticcheck) - SA5011 # possible nil pointer dereference (staticcheck) - SA6005 # should use strings.EqualFold instead (staticcheck) diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go index 9cca803be8..8fdee3c034 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/bgp/peers/bgppeers_test.go @@ -54,7 +54,7 @@ func TestBGPPeerCRUD(t *testing.T) { err = peers.Delete(context.TODO(), client, bgpPeerGot.ID).ExtractErr() th.AssertNoErr(t, err) - bgpPeerGot, err = peers.Get(context.TODO(), client, bgpPeerGot.ID).Extract() + _, err = peers.Get(context.TODO(), client, bgpPeerGot.ID).Extract() th.AssertErr(t, err) t.Logf("BGP Peer %s deleted", bgpPeerUpdated.Name) } diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index 70e6d05bb1..569beeebd4 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -503,7 +503,7 @@ func TestPortsRevision(t *testing.T) { AllowedAddressPairs: &[]ports.AddressPair{}, RevisionNumber: &port.RevisionNumber, } - newPort, err = ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() + _, err = ports.Update(context.TODO(), 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) From c05c419874b4521d65a8bac5972218ade245e0e1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Apr 2024 15:35:30 +0100 Subject: [PATCH 1865/2296] lint: Resolve nil pointer dereference (staticcheck) Signed-off-by: Stephen Finucane --- .golangci.yaml | 1 - openstack/image/v2/members/testing/requests_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 5108f3708c..447aa7b436 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -3,5 +3,4 @@ issues: exclude: - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) - SA1019 # (deprecated standard library function) (staticcheck) - - SA5011 # possible nil pointer dereference (staticcheck) - SA6005 # should use strings.EqualFold instead (staticcheck) diff --git a/openstack/image/v2/members/testing/requests_test.go b/openstack/image/v2/members/testing/requests_test.go index 981a22ffff..273d82581d 100644 --- a/openstack/image/v2/members/testing/requests_test.go +++ b/openstack/image/v2/members/testing/requests_test.go @@ -111,7 +111,7 @@ func TestShowMemberDetails(t *testing.T) { th.AssertNoErr(t, err) if md == nil { - t.Errorf("Expected non-nil value for md") + t.Fatalf("Expected non-nil value for md") } createdAt, err := time.Parse(time.RFC3339, "2013-11-26T07:21:21Z") From 72ec7ca26c0a9964d5d66ebb7365126b0360fc57 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Apr 2024 15:36:39 +0100 Subject: [PATCH 1866/2296] lint: Use strings.Equalfold (staticcheck) Signed-off-by: Stephen Finucane --- .golangci.yaml | 1 - internal/acceptance/clients/http.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 447aa7b436..cbf5a2761c 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -3,4 +3,3 @@ issues: exclude: - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) - SA1019 # (deprecated standard library function) (staticcheck) - - SA6005 # should use strings.EqualFold instead (staticcheck) diff --git a/internal/acceptance/clients/http.go b/internal/acceptance/clients/http.go index 1af2bea6b8..2babf46897 100644 --- a/internal/acceptance/clients/http.go +++ b/internal/acceptance/clients/http.go @@ -159,7 +159,7 @@ func redactHeaders(headers http.Header) (processedHeaders []string) { var sensitive bool for _, redact_header := range REDACT_HEADERS { - if strings.ToLower(name) == strings.ToLower(redact_header) { + if strings.EqualFold(name, redact_header) { sensitive = true } } From 31e9a9705978d032c3e370c2d1c0476dfe6f273c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Apr 2024 15:37:10 +0100 Subject: [PATCH 1867/2296] lint: Replace use of deprecated stdlib functions (staticcheck) We ignore the two warnings since (a) string.Title is perfectly fine for our use case here, and (b) x509.EncryptPEMBlock is only used in tests. Signed-off-by: Stephen Finucane --- .golangci.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.golangci.yaml b/.golangci.yaml index cbf5a2761c..7e0e9926cf 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -2,4 +2,7 @@ issues: exclude: - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) - - SA1019 # (deprecated standard library function) (staticcheck) + exclude-rules: + - linters: + - staticcheck + text: 'SA1019: (x509.EncryptPEMBlock|strings.Title)' From 19e8a7da271304f14a9baf6c2d04386e6670051b Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Apr 2024 15:59:26 +0100 Subject: [PATCH 1868/2296] lint: Enable additional linters Fix in the one go. We can remove the separate 'go vet' and 'go fmt' invocations since these are now run by golangci-lint Signed-off-by: Stephen Finucane --- .golangci.yaml | 11 +++++++++++ Makefile | 2 -- .../openstack/identity/v3/oauth1_test.go | 4 ++-- .../v2/extensions/layer3/l3_scheduling_test.go | 2 +- .../openstack/sharedfilesystems/v2/replicas.go | 16 ++++++++-------- .../sharedfilesystems/v2/replicas_test.go | 12 ++++++------ .../bgp/speakers/testing/requests_test.go | 6 +++--- openstack/objectstorage/v1/objects/requests.go | 5 +---- openstack/objectstorage/v1/objects/urls.go | 4 ++-- pagination/testing/linked_test.go | 6 +++--- testhelper/convenience.go | 6 +++--- 11 files changed, 40 insertions(+), 34 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 7e0e9926cf..828a099a40 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,4 +1,15 @@ --- +linters: + disable-all: true + enable: + - errcheck + - gofmt + - goimports + - govet + - staticcheck + - unparam + - unused + issues: exclude: - SA1006 # printf-style function with dynamic format string and no further arguments should use print-style function instead (staticcheck) diff --git a/Makefile b/Makefile index dc8e486d0c..128beec005 100644 --- a/Makefile +++ b/Makefile @@ -17,8 +17,6 @@ endif # chcon -Rt svirt_sandbox_file_t . # chcon -Rt svirt_sandbox_file_t ~/.cache/golangci-lint lint: - go fmt ./... - go vet -tags "fixtures acceptance" ./... $(RUNNER) run -t --rm \ -v $(shell pwd):/app \ -v ~/.cache/golangci-lint/$(GOLANGCI_LINT_VERSION):/root/.cache \ diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index 58e71daf27..da6c8be148 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -93,11 +93,11 @@ func TestOAuth1CRUD(t *testing.T) { // test HMACSHA1 and PLAINTEXT signature methods for _, method := range []oauth1.SignatureMethod{oauth1.HMACSHA1, oauth1.PLAINTEXT} { - oauth1MethodTest(t, client, consumer, method, user, project, roles, ao.IdentityEndpoint) + oauth1MethodTest(t, client, consumer, method, user, project, roles) } } -func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer *oauth1.Consumer, method oauth1.SignatureMethod, user *tokens.User, project *tokens.Project, roles []tokens.Role, identityEndpoint string) { +func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer *oauth1.Consumer, method oauth1.SignatureMethod, user *tokens.User, project *tokens.Project, roles []tokens.Role) { // Request a token requestTokenOpts := oauth1.RequestTokenOpts{ OAuthConsumerKey: consumer.ID, diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go index ed110dfff4..f186118d44 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/l3_scheduling_test.go @@ -54,7 +54,7 @@ func TestLayer3RouterScheduling(t *testing.T) { containsRouterFunc := func(rs []routers.Router, routerID string) bool { for _, r := range rs { - if r.ID == router.ID { + if r.ID == routerID { return true } } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go index f9ab7e9827..6a2d31d08a 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas.go @@ -28,7 +28,7 @@ func CreateReplica(t *testing.T, client *gophercloud.ServiceClient, share *share return nil, err } - _, err = waitForReplicaStatus(t, client, replica.ID, "available") + err = waitForReplicaStatus(t, client, replica.ID, "available") if err != nil { t.Logf("Failed to get %s replica status", replica.ID) DeleteReplica(t, client, replica) @@ -49,7 +49,7 @@ func DeleteReplica(t *testing.T, client *gophercloud.ServiceClient, replica *rep t.Errorf("Unable to delete replica %s: %v", replica.ID, err) } - _, err = waitForReplicaStatus(t, client, replica.ID, "deleted") + 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 { @@ -71,7 +71,7 @@ func ListShareReplicas(t *testing.T, client *gophercloud.ServiceClient, shareID return replicas.ExtractReplicas(pages) } -func waitForReplicaStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) (*replicas.Replica, error) { +func waitForReplicaStatus(t *testing.T, c *gophercloud.ServiceClient, id, status string) error { var current *replicas.Replica err := tools.WaitFor(func(ctx context.Context) (bool, error) { @@ -104,14 +104,14 @@ func waitForReplicaStatus(t *testing.T, c *gophercloud.ServiceClient, id, status 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 fmt.Errorf("Replica status is '%s' and unable to get manila messages: %s", err, mErr) } } - return current, err + return err } -func waitForReplicaState(t *testing.T, c *gophercloud.ServiceClient, id, state string) (*replicas.Replica, error) { +func waitForReplicaState(t *testing.T, c *gophercloud.ServiceClient, id, state string) error { var current *replicas.Replica err := tools.WaitFor(func(ctx context.Context) (bool, error) { @@ -136,9 +136,9 @@ func waitForReplicaState(t *testing.T, c *gophercloud.ServiceClient, id, state s 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 fmt.Errorf("Replica state is '%s' and unable to get manila messages: %s", err, mErr) } } - return current, err + return err } diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go index 0d564c95e0..41913ac266 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -86,7 +86,7 @@ func TestReplicaPromote(t *testing.T) { // sync new replica err = replicas.Resync(context.TODO(), client, created.ID).ExtractErr() th.AssertNoErr(t, err) - _, err = waitForReplicaState(t, client, created.ID, "in_sync") + err = waitForReplicaState(t, client, created.ID, "in_sync") if err != nil { t.Fatalf("Replica status error: %v", err) } @@ -95,7 +95,7 @@ func TestReplicaPromote(t *testing.T) { err = replicas.Promote(context.TODO(), client, created.ID, &replicas.PromoteOpts{}).ExtractErr() th.AssertNoErr(t, err) - _, err = waitForReplicaState(t, client, created.ID, "active") + err = waitForReplicaState(t, client, created.ID, "active") if err != nil { t.Fatalf("Replica status error: %v", err) } @@ -117,14 +117,14 @@ func TestReplicaPromote(t *testing.T) { // sync old replica err = replicas.Resync(context.TODO(), client, oldReplicaID).ExtractErr() th.AssertNoErr(t, err) - _, err = waitForReplicaState(t, client, oldReplicaID, "in_sync") + err = waitForReplicaState(t, client, oldReplicaID, "in_sync") if err != nil { t.Fatalf("Replica status error: %v", err) } err = replicas.Promote(context.TODO(), client, oldReplicaID, &replicas.PromoteOpts{}).ExtractErr() th.AssertNoErr(t, err) - _, err = waitForReplicaState(t, client, oldReplicaID, "active") + err = waitForReplicaState(t, client, oldReplicaID, "active") if err != nil { t.Fatalf("Replica status error: %v", err) } @@ -262,7 +262,7 @@ func TestReplicaResetStatus(t *testing.T) { } // We need to wait till the Extend operation is done - _, err = waitForReplicaStatus(t, client, replica.ID, "error") + err = waitForReplicaStatus(t, client, replica.ID, "error") if err != nil { t.Fatalf("Replica status error: %v", err) } @@ -300,7 +300,7 @@ func TestReplicaForceDelete(t *testing.T) { t.Fatalf("Unable to force delete a replica: %v", err) } - _, err = waitForReplicaStatus(t, client, replica.ID, "deleted") + err = waitForReplicaStatus(t, client, replica.ID, "deleted") if err != nil { t.Fatalf("Replica status error: %v", err) } 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 8f77107607..9e8df52b0c 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -224,9 +224,9 @@ func TestGetAdvertisedRoutes(t *testing.T) { } 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"}, + {NextHop: "172.17.128.212", Destination: "172.17.129.192/27"}, + {NextHop: "172.17.128.218", Destination: "172.17.129.0/27"}, + {NextHop: "172.17.128.231", Destination: "172.17.129.160/27"}, } th.CheckDeepEquals(t, count, 1) th.CheckDeepEquals(t, expected, actual) diff --git a/openstack/objectstorage/v1/objects/requests.go b/openstack/objectstorage/v1/objects/requests.go index ca462a061f..abc56f4773 100644 --- a/openstack/objectstorage/v1/objects/requests.go +++ b/openstack/objectstorage/v1/objects/requests.go @@ -596,10 +596,7 @@ func CreateTempURL(ctx context.Context, c *gophercloud.ServiceClient, containerN if err != nil { return "", err } - urlToBeSigned, err := tempURL(c, containerName, objectName) - if err != nil { - return "", err - } + urlToBeSigned := tempURL(c, containerName, objectName) if opts.Split == "" { opts.Split = "/v1/" diff --git a/openstack/objectstorage/v1/objects/urls.go b/openstack/objectstorage/v1/objects/urls.go index 4758a04f04..86f5823121 100644 --- a/openstack/objectstorage/v1/objects/urls.go +++ b/openstack/objectstorage/v1/objects/urls.go @@ -11,8 +11,8 @@ import ( // Names must not be URL-encoded in this case. // // See: https://docs.openstack.org/swift/latest/api/temporary_url_middleware.html#hmac-signature-for-temporary-urls -func tempURL(c *gophercloud.ServiceClient, container, object string) (string, error) { - return c.ServiceURL(container, object), nil +func tempURL(c *gophercloud.ServiceClient, container, object string) string { + return c.ServiceURL(container, object) } func listURL(c *gophercloud.ServiceClient, container string) (string, error) { diff --git a/pagination/testing/linked_test.go b/pagination/testing/linked_test.go index f73c3e14e2..83673f19b8 100644 --- a/pagination/testing/linked_test.go +++ b/pagination/testing/linked_test.go @@ -30,7 +30,7 @@ func ExtractLinkedInts(r pagination.Page) ([]int, error) { return s.Ints, err } -func createLinked(t *testing.T) pagination.Pager { +func createLinked() pagination.Pager { testhelper.SetupHTTP() testhelper.Mux.HandleFunc("/page1", func(w http.ResponseWriter, r *http.Request) { @@ -58,7 +58,7 @@ func createLinked(t *testing.T) pagination.Pager { } func TestEnumerateLinked(t *testing.T) { - pager := createLinked(t) + pager := createLinked() defer testhelper.TeardownHTTP() callCount := 0 @@ -100,7 +100,7 @@ func TestEnumerateLinked(t *testing.T) { } func TestAllPagesLinked(t *testing.T) { - pager := createLinked(t) + pager := createLinked() defer testhelper.TeardownHTTP() page, err := pager.AllPages(context.TODO()) diff --git a/testhelper/convenience.go b/testhelper/convenience.go index 8a6108e25c..166e60c5d1 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -268,7 +268,7 @@ func CheckDeepEquals(t *testing.T, expected, actual interface{}) { }) } -func isByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) bool { +func isByteArrayEquals(expectedBytes []byte, actualBytes []byte) bool { return bytes.Equal(expectedBytes, actualBytes) } @@ -276,7 +276,7 @@ func isByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) b func AssertByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) { t.Helper() - if !isByteArrayEquals(t, expectedBytes, actualBytes) { + if !isByteArrayEquals(expectedBytes, actualBytes) { logFatal(t, "The bytes differed.") } } @@ -285,7 +285,7 @@ func AssertByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byt func CheckByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte) { t.Helper() - if !isByteArrayEquals(t, expectedBytes, actualBytes) { + if !isByteArrayEquals(expectedBytes, actualBytes) { logError(t, "The bytes differed.") } } From 2b6b99165c2e9eea2a3c784c15fec3fa0a4ce899 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 22 May 2024 14:22:39 +0100 Subject: [PATCH 1869/2296] CI: Consistent matrix definitions Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 8 ++++---- .github/workflows/functional-basic.yaml | 6 +++--- .github/workflows/functional-blockstorage.yaml | 6 +++--- .github/workflows/functional-compute.yaml | 6 +++--- .github/workflows/functional-fwaas_v2.yaml | 6 +++--- .github/workflows/functional-identity.yaml | 6 +++--- .github/workflows/functional-image.yaml | 6 +++--- .github/workflows/functional-keymanager.yaml | 6 +++--- .github/workflows/functional-loadbalancer.yaml | 6 +++--- .github/workflows/functional-messaging.yaml | 6 +++--- .github/workflows/functional-objectstorage.yaml | 6 +++--- .github/workflows/functional-orchestration.yaml | 6 +++--- .github/workflows/functional-placement.yaml | 6 +++--- .github/workflows/functional-sharedfilesystems.yaml | 6 +++--- 14 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index fc1bbf2536..1a2903fe5d 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -10,11 +10,11 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] - os_system_scope: ["all"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" + os_system_scope: "all" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 722b1a5510..c698ed386e 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -13,10 +13,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 91a60b4d4d..2c416fc225 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 92d2371fb0..12b0d3dea0 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index ae31ef344d..75617898da 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 5a365ac9d4..e8aa1ca8c9 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index e9f557c1d4..ec3f0ebe73 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index b1d02ef6f7..0d428a3ae5 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 1c669aed77..c57385234f 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index c4ba4b6e82..e87847027b 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index a4f347eaa4..89e92ee838 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 816bf55ad6..4815075261 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 486769f130..25d2a1b008 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 24dc989fcc..108818a1be 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: false matrix: - name: ["master"] - openstack_version: ["master"] - ubuntu_version: ["22.04"] include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" From f018747518afe2ec0e703bb510112ba363a559a1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 22 May 2024 12:43:23 +0100 Subject: [PATCH 1870/2296] CI: Enable openstack-client-server This can shave minutes off a DevStack deployment by avoiding repeated startup times for OSC. Nova recently enabled it in their upstream gates [1] and we can do the same here. [1] https://review.opendev.org/c/openstack/nova/+/919738 Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 6 +++++- .github/workflows/functional-basic.yaml | 6 +++++- .github/workflows/functional-blockstorage.yaml | 6 +++++- .github/workflows/functional-compute.yaml | 5 +++++ .github/workflows/functional-containerinfra.yaml | 6 +++++- .github/workflows/functional-dns.yaml | 6 +++++- .github/workflows/functional-fwaas_v2.yaml | 6 +++++- .github/workflows/functional-identity.yaml | 5 +++++ .github/workflows/functional-image.yaml | 5 +++++ .github/workflows/functional-keymanager.yaml | 6 +++++- .github/workflows/functional-loadbalancer.yaml | 6 +++++- .github/workflows/functional-messaging.yaml | 5 +++++ .github/workflows/functional-networking.yaml | 6 +++++- .github/workflows/functional-objectstorage.yaml | 6 +++++- .github/workflows/functional-orchestration.yaml | 6 +++++- .github/workflows/functional-placement.yaml | 5 +++++ .github/workflows/functional-sharedfilesystems.yaml | 5 +++++ 17 files changed, 85 insertions(+), 11 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 1a2903fe5d..3fdc1fbaa8 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -15,18 +15,22 @@ jobs: openstack_version: "master" ubuntu_version: "22.04" os_system_scope: "all" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" os_system_scope: "" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" os_system_scope: "" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" os_system_scope: "" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests steps: @@ -80,7 +84,7 @@ jobs: IRONIC_ENABLED_DEPLOY_INTERFACES=direct,fake 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' + 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,${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index c698ed386e..4facd4c4bd 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -17,15 +17,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with defaults and run basic acceptance tests steps: @@ -35,7 +39,7 @@ jobs: uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} - enabled_services: 's-account,s-container,s-object,s-proxy' + enabled_services: 's-account,s-container,s-object,s-proxy,${{ matrix.additional_services }}' - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 2c416fc225..358f21551d 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Cinder and run blockstorage acceptance tests steps: @@ -34,7 +38,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | CINDER_ISCSI_HELPER=lioadm - enabled_services: 's-account,s-container,s-object,s-proxy,c-bak' + enabled_services: "s-account,s-container,s-object,s-proxy,c-bak,${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 12b0d3dea0..22fa1f9b14 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Nova and run compute acceptance tests steps: @@ -34,6 +38,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | CINDER_ISCSI_HELPER=lioadm + enabled_services: "${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 73c6ef6ae6..0a9297380a 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -17,24 +17,28 @@ jobs: devstack_conf_overrides: | enable_plugin magnum https://github.com/openstack/magnum master MAGNUMCLIENT_BRANCH=master + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" devstack_conf_overrides: | enable_plugin magnum https://github.com/openstack/magnum stable/2024.1 MAGNUMCLIENT_BRANCH=stable/2024.1 + additional_services: "" - 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 + additional_services: "" - 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 + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests steps: @@ -51,7 +55,7 @@ jobs: SWIFT_MAX_FILE_SIZE=5368709122 KEYSTONE_ADMIN_ENDPOINT=true ${{ matrix.devstack_conf_overrides }} - enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' + enabled_services: "h-eng,h-api,h-api-cfn,h-api-cw,${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 6fe7d99547..d5a93ab3a2 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -15,15 +15,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests steps: @@ -35,7 +39,7 @@ jobs: branch: ${{ matrix.openstack_version }} 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' + enabled_services: "designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns,${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 75617898da..6cfa173ed5 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with enabled FWaaS_v2 and run networking acceptance tests steps: @@ -39,7 +43,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,-cinder,-horizon,-tempest,-swift,-c-sch,-c-api,-c-vol,-c-bak,-ovn,-ovn-controller,-ovn-northd,-q-ovn-metadata-agent' + 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,${{ matrix.additional_services }}' - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index e8aa1ca8c9..7a3291e944 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Keystone and run identity acceptance tests steps: @@ -32,6 +36,7 @@ jobs: uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} + enabled_services: "${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index ec3f0ebe73..ee6bd21f82 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Glance and run image acceptance tests steps: @@ -32,6 +36,7 @@ jobs: uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} + enabled_services: "${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 0d428a3ae5..f9ce350b9c 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Barbican and run keymanager acceptance tests steps: @@ -34,7 +38,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin barbican https://github.com/openstack/barbican ${{ matrix.openstack_version }} - enabled_services: 'barbican-svc,barbican-retry,barbican-keystone-listener' + enabled_services: "barbican-svc,barbican-retry,barbican-keystone-listener,${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index c57385234f..c7ea99cd0f 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Octavia and run loadbalancer acceptance tests steps: @@ -35,7 +39,7 @@ jobs: conf_overrides: | 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' + enabled_services: "octavia,o-api,o-cw,o-hk,o-hm,o-da,neutron-qos,${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index e87847027b..85b63883af 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Zaqar and run messaging acceptance tests steps: @@ -35,6 +39,7 @@ jobs: conf_overrides: | enable_plugin zaqar https://github.com/openstack/zaqar ${{ matrix.openstack_version }} ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} + enabled_services: "${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 7db740ea9c..3df3816d0e 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests steps: @@ -48,7 +52,7 @@ jobs: [[post-config|\$NEUTRON_CONF]] [oslo_policy] policy_dirs = /tmp/neutron-policies - enabled_services: 'neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding' + enabled_services: "neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding,${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 89e92ee838..f632516b9a 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Swift and run objectstorage acceptance tests steps: @@ -38,7 +42,7 @@ jobs: [[post-config|\$SWIFT_CONFIG_PROXY_SERVER]] [filter:versioned_writes] allow_object_versioning = true - enabled_services: 's-account,s-container,s-object,s-proxy' + enabled_services: 's-account,s-container,s-object,s-proxy,${{ matrix.additional_services }}' - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 4815075261..8d58039f65 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Heat and run orchestration acceptance tests steps: @@ -34,7 +38,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin heat https://github.com/openstack/heat ${{ matrix.openstack_version }} - enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw' + enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw,${{ matrix.additional_services }}' - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 25d2a1b008..b1eafc72b4 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Placement and run placement acceptance tests steps: @@ -32,6 +36,7 @@ jobs: uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 with: branch: ${{ matrix.openstack_version }} + enabled_services: "${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 108818a1be..6c21cdc632 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -14,15 +14,19 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" + additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" + additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" + additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests steps: @@ -48,6 +52,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 MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE=false + enabled_services: "${{ matrix.additional_services }}" - name: Checkout go uses: actions/setup-go@v5 with: From 2852f07b9fe125014c209d682460745a68935d8f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 22 May 2024 15:38:54 +0100 Subject: [PATCH 1871/2296] script: Run collectlogs through shellcheck Signed-off-by: Stephen Finucane --- script/collectlogs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/script/collectlogs b/script/collectlogs index c8e438d391..a0e71aa54b 100755 --- a/script/collectlogs +++ b/script/collectlogs @@ -2,12 +2,14 @@ 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 {} \; +mkdir -p "$LOG_DIR" +# shellcheck disable=SC2024 +sudo journalctl -o short-precise --no-pager &> "$LOG_DIR/journal.log" +# shellcheck disable=SC2024 +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 269836be2cc37bf21d6eb7dfae514132c11e6698 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 22 May 2024 15:44:11 +0100 Subject: [PATCH 1872/2296] Capture service logs We are seeing pretty consistent failures in some of our neutron jobs. Start storing logs so that we can debug this. Signed-off-by: Stephen Finucane --- script/collectlogs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/script/collectlogs b/script/collectlogs index a0e71aa54b..5ad61c86e4 100755 --- a/script/collectlogs +++ b/script/collectlogs @@ -7,6 +7,10 @@ mkdir -p "$LOG_DIR" sudo journalctl -o short-precise --no-pager &> "$LOG_DIR/journal.log" # shellcheck disable=SC2024 sudo systemctl status "devstack@*" &> "$LOG_DIR/devstack-services.txt" +for service in $(systemctl list-units --output json devstack@* | jq -r '.[].unit | capture("devstack@(?[a-z\\-]+).service") | '.svc'') +do + journalctl -u "devstack@${service}.service" --no-tail > "${LOG_DIR}/${service}.log" +done free -m > "$LOG_DIR/free.txt" dpkg -l > "$LOG_DIR/dpkg-l.txt" pip freeze > "$LOG_DIR/pip-freeze.txt" From eafcf2c46759c3cc06562a386fedfda4af266ad9 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Wed, 22 May 2024 13:07:30 +0200 Subject: [PATCH 1873/2296] CI: baremetal: also use system scope in 2024.1 The RBAC changes were already there but the correct scope was not set when adding the caracal branch. --- .github/workflows/functional-baremetal.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 3fdc1fbaa8..9b86bd5e36 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -14,12 +14,15 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "22.04" + # NOTE(dtantsur): this forces running tests with a system scope + # token, which is required for certain actions. Use "all" on + # versions starting with 2024.1. os_system_scope: "all" additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" - os_system_scope: "" + os_system_scope: "all" additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" From de76eeb83a6f1eec1b9ebe8ca52c15c9e44a1cf0 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 22 May 2024 18:26:53 +0100 Subject: [PATCH 1874/2296] trivial: Replace 'interface{}' with any MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some syntactic sugar 🍭. sed -i 's/interface{}/any/g' $(ag 'interface{}' -Q -l) gofmt -w ./.. Signed-off-by: Stephen Finucane --- auth_options.go | 48 +++---- errors.go | 2 +- internal/acceptance/clients/http.go | 18 +-- .../openstack/baremetal/v1/baremetal.go | 4 +- .../openstack/baremetal/v1/nodes_test.go | 2 +- .../blockstorage/v3/quotaset_test.go | 4 +- .../openstack/compute/v2/aggregates_test.go | 4 +- .../compute/v2/attachinterfaces_test.go | 4 +- .../openstack/db/v1/configurations_test.go | 2 +- .../openstack/identity/v3/groups_test.go | 4 +- .../openstack/identity/v3/policies_test.go | 4 +- .../openstack/identity/v3/regions_test.go | 4 +- .../openstack/identity/v3/roles_test.go | 10 +- .../openstack/identity/v3/service_test.go | 4 +- .../openstack/identity/v3/users_test.go | 10 +- .../openstack/image/v2/imageservice.go | 4 +- .../openstack/messaging/v2/messaging.go | 4 +- .../extensions/portsbinding/portsbinding.go | 2 +- .../portsbinding/portsbinding_test.go | 4 +- .../sharedfilesystems/v2/sharetypes_test.go | 2 +- .../openstack/workflow/v2/crontrigger.go | 2 +- .../openstack/workflow/v2/execution.go | 2 +- internal/acceptance/tools/tools.go | 2 +- openstack/baremetal/inventory/plugindata.go | 6 +- .../baremetal/inventory/testing/fixtures.go | 30 ++-- .../baremetal/v1/allocations/requests.go | 4 +- openstack/baremetal/v1/allocations/results.go | 6 +- .../v1/allocations/testing/fixtures_test.go | 2 +- openstack/baremetal/v1/conductors/results.go | 4 +- openstack/baremetal/v1/drivers/results.go | 12 +- .../v1/drivers/testing/fixtures_test.go | 36 ++--- openstack/baremetal/v1/nodes/doc.go | 2 +- openstack/baremetal/v1/nodes/requests.go | 82 +++++------ openstack/baremetal/v1/nodes/results.go | 28 ++-- .../v1/nodes/testing/fixtures_test.go | 60 ++++---- .../v1/nodes/testing/requests_test.go | 44 +++--- openstack/baremetal/v1/ports/requests.go | 22 +-- openstack/baremetal/v1/ports/results.go | 12 +- .../v1/ports/testing/fixtures_test.go | 16 +-- .../v1/introspection/results.go | 16 +-- .../v1/introspection/testing/fixtures.go | 38 ++--- openstack/blockstorage/v2/backups/requests.go | 18 +-- openstack/blockstorage/v2/backups/results.go | 12 +- openstack/blockstorage/v2/quotasets/doc.go | 2 +- .../blockstorage/v2/quotasets/requests.go | 8 +- .../blockstorage/v2/quotasets/results.go | 8 +- .../v2/quotasets/testing/fixtures_test.go | 10 +- .../v2/quotasets/testing/requests_test.go | 2 +- .../blockstorage/v2/schedulerstats/results.go | 10 +- .../blockstorage/v2/snapshots/requests.go | 10 +- .../blockstorage/v2/snapshots/results.go | 6 +- .../v2/snapshots/testing/requests_test.go | 4 +- .../blockstorage/v2/transfers/requests.go | 4 +- .../blockstorage/v2/transfers/results.go | 4 +- openstack/blockstorage/v2/volumes/requests.go | 70 +++++----- openstack/blockstorage/v2/volumes/results.go | 12 +- .../v2/volumes/testing/requests_test.go | 4 +- openstack/blockstorage/v3/attachments/doc.go | 2 +- .../blockstorage/v3/attachments/requests.go | 14 +- .../blockstorage/v3/attachments/results.go | 6 +- .../v3/attachments/testing/fixtures_test.go | 2 +- .../v3/attachments/testing/requests_test.go | 4 +- openstack/blockstorage/v3/backups/requests.go | 18 +-- openstack/blockstorage/v3/backups/results.go | 12 +- openstack/blockstorage/v3/qos/requests.go | 20 +-- openstack/blockstorage/v3/qos/results.go | 2 +- openstack/blockstorage/v3/quotasets/doc.go | 2 +- .../blockstorage/v3/quotasets/requests.go | 8 +- .../blockstorage/v3/quotasets/results.go | 8 +- .../v3/quotasets/testing/fixtures_test.go | 10 +- .../v3/quotasets/testing/requests_test.go | 2 +- .../blockstorage/v3/schedulerstats/results.go | 10 +- .../blockstorage/v3/snapshots/requests.go | 24 ++-- .../blockstorage/v3/snapshots/results.go | 6 +- .../v3/snapshots/testing/requests_test.go | 4 +- .../blockstorage/v3/transfers/requests.go | 4 +- .../blockstorage/v3/transfers/results.go | 4 +- openstack/blockstorage/v3/volumes/requests.go | 70 +++++----- openstack/blockstorage/v3/volumes/results.go | 12 +- .../v3/volumes/testing/requests_test.go | 4 +- .../blockstorage/v3/volumetypes/requests.go | 30 ++-- .../blockstorage/v3/volumetypes/results.go | 10 +- openstack/common/extensions/results.go | 12 +- .../common/extensions/testing/fixtures.go | 4 +- openstack/compute/v2/aggregates/requests.go | 12 +- .../v2/aggregates/testing/requests_test.go | 2 +- .../compute/v2/attachinterfaces/requests.go | 4 +- openstack/compute/v2/diagnostics/results.go | 4 +- .../v2/diagnostics/testing/requests_test.go | 2 +- .../v2/extensions/testing/delegate_test.go | 2 +- openstack/compute/v2/flavors/requests.go | 22 +-- openstack/compute/v2/flavors/results.go | 2 +- openstack/compute/v2/hypervisors/results.go | 16 +-- .../compute/v2/instanceactions/results.go | 4 +- openstack/compute/v2/keypairs/requests.go | 8 +- openstack/compute/v2/quotasets/requests.go | 4 +- .../v2/quotasets/testing/requests_test.go | 2 +- .../compute/v2/remoteconsoles/requests.go | 4 +- openstack/compute/v2/secgroups/requests.go | 12 +- openstack/compute/v2/secgroups/results.go | 6 +- openstack/compute/v2/servergroups/requests.go | 4 +- openstack/compute/v2/servergroups/results.go | 2 +- .../v2/servergroups/testing/fixtures_test.go | 6 +- openstack/compute/v2/servers/requests.go | 130 +++++++++--------- openstack/compute/v2/servers/results.go | 18 +-- .../v2/servers/testing/fixtures_test.go | 76 +++++----- .../v2/servers/testing/requests_test.go | 8 +- .../v2/servers/testing/results_test.go | 6 +- openstack/compute/v2/services/requests.go | 2 +- openstack/compute/v2/services/results.go | 2 +- openstack/compute/v2/tags/requests.go | 4 +- openstack/compute/v2/volumeattach/requests.go | 4 +- openstack/config/clouds/clouds.go | 16 +-- openstack/config/clouds/types.go | 2 +- .../container/v1/capsules/microversions.go | 2 +- openstack/container/v1/capsules/requests.go | 4 +- openstack/container/v1/capsules/results.go | 10 +- openstack/container/v1/capsules/template.go | 2 +- .../v1/capsules/testing/fixtures_test.go | 52 +++---- .../v1/certificates/requests.go | 4 +- .../containerinfra/v1/clusters/requests.go | 24 ++-- .../containerinfra/v1/clusters/results.go | 68 ++++----- .../v1/clusters/testing/fixtures_test.go | 4 +- .../v1/clustertemplates/requests.go | 16 +-- .../containerinfra/v1/nodegroups/requests.go | 16 +-- .../containerinfra/v1/quotas/requests.go | 4 +- openstack/containerinfra/v1/quotas/results.go | 2 +- openstack/db/v1/configurations/requests.go | 12 +- openstack/db/v1/configurations/results.go | 2 +- .../configurations/testing/fixtures_test.go | 2 +- .../configurations/testing/requests_test.go | 6 +- openstack/db/v1/databases/requests.go | 10 +- openstack/db/v1/instances/requests.go | 26 ++-- openstack/db/v1/instances/results.go | 2 +- openstack/db/v1/users/requests.go | 10 +- openstack/dns/v2/recordsets/requests.go | 8 +- openstack/dns/v2/recordsets/results.go | 2 +- .../v2/recordsets/testing/requests_test.go | 2 +- openstack/dns/v2/transfer/accept/requests.go | 4 +- openstack/dns/v2/transfer/accept/results.go | 2 +- .../transfer/accept/testing/fixtures_test.go | 4 +- openstack/dns/v2/transfer/request/requests.go | 8 +- openstack/dns/v2/transfer/request/results.go | 2 +- .../transfer/request/testing/fixtures_test.go | 4 +- openstack/dns/v2/zones/requests.go | 8 +- openstack/dns/v2/zones/results.go | 4 +- .../dns/v2/zones/testing/fixtures_test.go | 4 +- openstack/identity/v2/tenants/requests.go | 8 +- openstack/identity/v2/tokens/requests.go | 4 +- openstack/identity/v2/users/requests.go | 8 +- .../v3/applicationcredentials/requests.go | 6 +- .../v3/applicationcredentials/results.go | 2 +- .../testing/fixtures_test.go | 10 +- openstack/identity/v3/credentials/requests.go | 8 +- openstack/identity/v3/credentials/results.go | 2 +- .../v3/credentials/testing/fixtures_test.go | 8 +- openstack/identity/v3/domains/requests.go | 8 +- openstack/identity/v3/domains/results.go | 2 +- .../v3/domains/testing/fixtures_test.go | 10 +- .../identity/v3/ec2credentials/requests.go | 2 +- .../identity/v3/ec2credentials/results.go | 2 +- .../ec2credentials/testing/fixtures_test.go | 4 +- openstack/identity/v3/ec2tokens/requests.go | 20 +-- openstack/identity/v3/endpoints/requests.go | 8 +- openstack/identity/v3/federation/requests.go | 8 +- openstack/identity/v3/federation/results.go | 2 +- .../v3/federation/testing/fixtures_test.go | 4 +- openstack/identity/v3/groups/doc.go | 2 +- openstack/identity/v3/groups/requests.go | 16 +-- openstack/identity/v3/groups/results.go | 10 +- .../v3/groups/testing/fixtures_test.go | 12 +- .../v3/groups/testing/requests_test.go | 4 +- openstack/identity/v3/limits/requests.go | 14 +- openstack/identity/v3/limits/results.go | 2 +- .../v3/limits/testing/fixtures_test.go | 6 +- openstack/identity/v3/oauth1/requests.go | 20 +-- openstack/identity/v3/policies/doc.go | 4 +- openstack/identity/v3/policies/requests.go | 16 +-- openstack/identity/v3/policies/results.go | 10 +- .../v3/policies/testing/fixtures_test.go | 12 +- .../v3/policies/testing/requests_test.go | 4 +- .../identity/v3/projectendpoints/requests.go | 2 +- openstack/identity/v3/projects/requests.go | 24 ++-- openstack/identity/v3/projects/results.go | 12 +- .../v3/projects/testing/fixtures_test.go | 20 +-- .../v3/projects/testing/requests_test.go | 4 +- openstack/identity/v3/regions/doc.go | 2 +- openstack/identity/v3/regions/requests.go | 16 +-- openstack/identity/v3/regions/results.go | 10 +- .../v3/regions/testing/fixtures_test.go | 12 +- .../v3/regions/testing/requests_test.go | 4 +- .../identity/v3/registeredlimits/requests.go | 14 +- .../identity/v3/registeredlimits/results.go | 2 +- .../registeredlimits/testing/fixtures_test.go | 6 +- openstack/identity/v3/roles/doc.go | 2 +- openstack/identity/v3/roles/requests.go | 16 +-- openstack/identity/v3/roles/results.go | 26 ++-- .../v3/roles/testing/fixtures_test.go | 36 ++--- .../v3/roles/testing/requests_test.go | 4 +- openstack/identity/v3/services/doc.go | 4 +- openstack/identity/v3/services/requests.go | 16 +-- openstack/identity/v3/services/results.go | 10 +- .../v3/services/testing/fixtures_test.go | 12 +- .../v3/services/testing/requests_test.go | 4 +- openstack/identity/v3/tokens/requests.go | 12 +- openstack/identity/v3/tokens/results.go | 2 +- openstack/identity/v3/trusts/requests.go | 6 +- openstack/identity/v3/users/doc.go | 2 +- openstack/identity/v3/users/requests.go | 24 ++-- openstack/identity/v3/users/results.go | 12 +- .../v3/users/testing/fixtures_test.go | 38 ++--- .../v3/users/testing/requests_test.go | 12 +- openstack/image/v2/imageimport/requests.go | 6 +- openstack/image/v2/images/requests.go | 48 +++---- openstack/image/v2/images/results.go | 14 +- .../image/v2/images/testing/requests_test.go | 12 +- openstack/image/v2/members/requests.go | 8 +- openstack/image/v2/tasks/doc.go | 4 +- openstack/image/v2/tasks/requests.go | 8 +- openstack/image/v2/tasks/results.go | 4 +- .../image/v2/tasks/testing/requests_test.go | 16 +-- openstack/keymanager/v1/acls/requests.go | 6 +- .../keymanager/v1/containers/requests.go | 16 +-- openstack/keymanager/v1/orders/requests.go | 6 +- openstack/keymanager/v1/secrets/requests.go | 18 +-- .../v2/flavorprofiles/requests.go | 8 +- openstack/loadbalancer/v2/flavors/requests.go | 8 +- .../loadbalancer/v2/l7policies/requests.go | 18 +-- .../loadbalancer/v2/listeners/requests.go | 10 +- .../loadbalancer/v2/loadbalancers/requests.go | 8 +- .../loadbalancer/v2/monitors/requests.go | 8 +- openstack/loadbalancer/v2/pools/requests.go | 24 ++-- openstack/loadbalancer/v2/pools/results.go | 2 +- openstack/loadbalancer/v2/quotas/requests.go | 4 +- openstack/messaging/v2/claims/requests.go | 8 +- openstack/messaging/v2/claims/results.go | 8 +- .../v2/claims/testing/fixtures_test.go | 4 +- openstack/messaging/v2/messages/doc.go | 4 +- openstack/messaging/v2/messages/requests.go | 12 +- openstack/messaging/v2/messages/results.go | 24 ++-- .../v2/messages/testing/fixtures_test.go | 8 +- .../v2/messages/testing/requests_test.go | 4 +- openstack/messaging/v2/queues/doc.go | 2 +- openstack/messaging/v2/queues/requests.go | 28 ++-- openstack/messaging/v2/queues/results.go | 8 +- .../v2/queues/testing/fixtures_test.go | 6 +- .../v2/queues/testing/requests_test.go | 4 +- .../v2/extensions/agents/requests.go | 16 +-- .../v2/extensions/agents/results.go | 2 +- .../agents/testing/fixtures_test.go | 14 +- .../agents/testing/requests_test.go | 2 +- .../v2/extensions/attributestags/requests.go | 4 +- .../v2/extensions/bgp/peers/requests.go | 8 +- .../v2/extensions/bgp/peers/results.go | 4 +- .../v2/extensions/bgp/speakers/requests.go | 24 ++-- .../v2/extensions/bgp/speakers/results.go | 10 +- .../networking/v2/extensions/dns/requests.go | 20 +-- .../v2/extensions/external/requests.go | 8 +- .../v2/extensions/extradhcpopts/requests.go | 12 +- .../v2/extensions/fwaas_v2/groups/requests.go | 8 +- .../extensions/fwaas_v2/policies/requests.go | 14 +- .../v2/extensions/fwaas_v2/rules/requests.go | 10 +- .../layer3/addressscopes/requests.go | 8 +- .../extensions/layer3/extraroutes/requests.go | 4 +- .../extensions/layer3/floatingips/requests.go | 10 +- .../extensions/layer3/floatingips/results.go | 4 +- .../layer3/portforwarding/requests.go | 8 +- .../layer3/portforwarding/results.go | 2 +- .../v2/extensions/layer3/routers/requests.go | 16 +-- .../v2/extensions/layer3/routers/results.go | 4 +- .../layer3/routers/testing/requests_test.go | 8 +- .../networking/v2/extensions/mtu/requests.go | 8 +- .../v2/extensions/portsbinding/requests.go | 12 +- .../v2/extensions/portsbinding/results.go | 4 +- .../portsbinding/testing/requests_test.go | 2 +- .../v2/extensions/portsecurity/requests.go | 16 +-- .../v2/extensions/provider/requests.go | 8 +- .../v2/extensions/provider/results.go | 2 +- .../v2/extensions/qos/policies/requests.go | 24 ++-- .../v2/extensions/qos/policies/results.go | 4 +- .../qos/policies/testing/fixtures_test.go | 4 +- .../qos/policies/testing/requests_test.go | 2 +- .../v2/extensions/qos/rules/requests.go | 24 ++-- .../v2/extensions/qos/rules/results.go | 6 +- .../v2/extensions/qos/ruletypes/results.go | 6 +- .../v2/extensions/quotas/requests.go | 4 +- .../v2/extensions/quotas/results.go | 2 +- .../v2/extensions/rbacpolicies/requests.go | 8 +- .../v2/extensions/rbacpolicies/results.go | 4 +- .../v2/extensions/security/groups/requests.go | 8 +- .../v2/extensions/security/rules/requests.go | 4 +- .../v2/extensions/subnetpools/requests.go | 8 +- .../v2/extensions/subnetpools/results.go | 12 +- .../v2/extensions/testing/delegate_test.go | 2 +- .../v2/extensions/trunks/requests.go | 16 +-- .../v2/extensions/vlantransparent/requests.go | 8 +- .../vpnaas/endpointgroups/requests.go | 8 +- .../extensions/vpnaas/ikepolicies/requests.go | 8 +- .../vpnaas/ipsecpolicies/requests.go | 8 +- .../v2/extensions/vpnaas/services/requests.go | 8 +- .../vpnaas/siteconnections/requests.go | 8 +- openstack/networking/v2/networks/requests.go | 8 +- openstack/networking/v2/networks/results.go | 4 +- openstack/networking/v2/ports/requests.go | 18 +-- openstack/networking/v2/ports/results.go | 4 +- openstack/networking/v2/subnets/requests.go | 12 +- .../v2/subnets/testing/results_test.go | 2 +- openstack/objectstorage/v1/objects/results.go | 4 +- .../orchestration/v1/resourcetypes/results.go | 6 +- .../v1/resourcetypes/testing/fixtures_test.go | 4 +- .../orchestration/v1/stackevents/results.go | 2 +- .../v1/stackresources/requests.go | 4 +- .../v1/stackresources/results.go | 34 ++--- .../stackresources/testing/fixtures_test.go | 22 +-- openstack/orchestration/v1/stacks/doc.go | 10 +- .../orchestration/v1/stacks/environment.go | 8 +- .../v1/stacks/environment_test.go | 10 +- openstack/orchestration/v1/stacks/fixtures.go | 32 ++--- openstack/orchestration/v1/stacks/requests.go | 30 ++-- openstack/orchestration/v1/stacks/results.go | 58 ++++---- openstack/orchestration/v1/stacks/template.go | 12 +- .../orchestration/v1/stacks/template_test.go | 56 ++++---- .../v1/stacks/testing/fixtures_test.go | 40 +++--- .../v1/stacks/testing/requests_test.go | 4 +- openstack/orchestration/v1/stacks/utils.go | 20 +-- .../orchestration/v1/stacks/utils_test.go | 4 +- .../v1/stacktemplates/requests.go | 4 +- .../v1/stacktemplates/results.go | 6 +- .../stacktemplates/testing/fixtures_test.go | 4 +- .../v1/resourceproviders/requests.go | 8 +- .../sharedfilesystems/v2/replicas/requests.go | 20 +-- .../sharedfilesystems/v2/replicas/results.go | 2 +- .../v2/schedulerstats/results.go | 8 +- .../v2/securityservices/requests.go | 8 +- .../v2/shareaccessrules/results.go | 2 +- .../shareaccessrules/testing/requests_test.go | 2 +- .../v2/sharenetworks/requests.go | 16 +-- .../sharedfilesystems/v2/shares/requests.go | 46 +++---- .../v2/sharetransfers/requests.go | 6 +- .../v2/sharetransfers/results.go | 4 +- .../v2/sharetypes/requests.go | 18 +-- .../v2/sharetypes/results.go | 6 +- .../v2/sharetypes/testing/requests_test.go | 14 +- .../v2/snapshots/requests.go | 14 +- openstack/workflow/v2/crontriggers/doc.go | 4 +- .../workflow/v2/crontriggers/requests.go | 14 +- openstack/workflow/v2/crontriggers/results.go | 4 +- .../v2/crontriggers/testing/requests_test.go | 18 +-- openstack/workflow/v2/executions/doc.go | 2 +- openstack/workflow/v2/executions/requests.go | 16 +-- openstack/workflow/v2/executions/results.go | 6 +- .../v2/executions/testing/requests_test.go | 26 ++-- pagination/http.go | 4 +- pagination/linked.go | 14 +- pagination/marker.go | 6 +- pagination/pager.go | 28 ++-- pagination/single.go | 6 +- params.go | 10 +- provider_client.go | 4 +- results.go | 20 +-- service_client.go | 10 +- testhelper/convenience.go | 34 ++--- testhelper/http_responses.go | 2 +- testing/auth_options_test.go | 36 ++--- testing/params_test.go | 20 +-- testing/results_test.go | 10 +- testing/util_test.go | 4 +- util.go | 4 +- 368 files changed, 2063 insertions(+), 2063 deletions(-) diff --git a/auth_options.go b/auth_options.go index 3f0c394c31..616919d000 100644 --- a/auth_options.go +++ b/auth_options.go @@ -107,13 +107,13 @@ type AuthScope struct { // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v2 tokens package -func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { +func (opts AuthOptions) ToTokenV2CreateMap() (map[string]any, error) { // Populate the request map. - authMap := make(map[string]interface{}) + authMap := make(map[string]any) if opts.Username != "" { if opts.Password != "" { - authMap["passwordCredentials"] = map[string]interface{}{ + authMap["passwordCredentials"] = map[string]any{ "username": opts.Username, "password": opts.Password, } @@ -121,7 +121,7 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { return nil, ErrMissingInput{Argument: "Password"} } } else if opts.TokenID != "" { - authMap["token"] = map[string]interface{}{ + authMap["token"] = map[string]any{ "id": opts.TokenID, } } else { @@ -135,12 +135,12 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { authMap["tenantName"] = opts.TenantName } - return map[string]interface{}{"auth": authMap}, nil + return map[string]any{"auth": authMap}, nil } // ToTokenV3CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package -func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]any) (map[string]any, error) { type domainReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` @@ -392,7 +392,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } if len(scope) != 0 { - b["auth"].(map[string]interface{})["scope"] = scope + b["auth"].(map[string]any)["scope"] = scope } return b, nil @@ -400,7 +400,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // ToTokenV3ScopeMap builds a scope from AuthOptions and satisfies interface in // the v3 tokens package. -func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { // For backwards compatibility. // If AuthOptions.Scope was not set, try to determine it. // This works well for common scenarios. @@ -418,15 +418,15 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } if opts.Scope.System { - return map[string]interface{}{ - "system": map[string]interface{}{ + return map[string]any{ + "system": map[string]any{ "all": true, }, }, nil } if opts.Scope.TrustID != "" { - return map[string]interface{}{ + return map[string]any{ "OS-TRUST:trust": map[string]string{ "id": opts.Scope.TrustID, }, @@ -445,20 +445,20 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { if opts.Scope.DomainID != "" { // ProjectName + DomainID - return map[string]interface{}{ - "project": map[string]interface{}{ + return map[string]any{ + "project": map[string]any{ "name": &opts.Scope.ProjectName, - "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, + "domain": map[string]any{"id": &opts.Scope.DomainID}, }, }, nil } if opts.Scope.DomainName != "" { // ProjectName + DomainName - return map[string]interface{}{ - "project": map[string]interface{}{ + return map[string]any{ + "project": map[string]any{ "name": &opts.Scope.ProjectName, - "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, + "domain": map[string]any{"name": &opts.Scope.DomainName}, }, }, nil } @@ -472,8 +472,8 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } // ProjectID - return map[string]interface{}{ - "project": map[string]interface{}{ + return map[string]any{ + "project": map[string]any{ "id": &opts.Scope.ProjectID, }, }, nil @@ -484,15 +484,15 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } // DomainID - return map[string]interface{}{ - "domain": map[string]interface{}{ + return map[string]any{ + "domain": map[string]any{ "id": &opts.Scope.DomainID, }, }, nil } else if opts.Scope.DomainName != "" { // DomainName - return map[string]interface{}{ - "domain": map[string]interface{}{ + return map[string]any{ + "domain": map[string]any{ "name": &opts.Scope.DomainName, }, }, nil @@ -512,6 +512,6 @@ func (opts AuthOptions) CanReauth() bool { // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. -func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, error) { return nil, nil } diff --git a/errors.go b/errors.go index 3595ce47a5..2698c20395 100644 --- a/errors.go +++ b/errors.go @@ -41,7 +41,7 @@ func (e ErrMissingInput) Error() string { // ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors. type ErrInvalidInput struct { ErrMissingInput - Value interface{} + Value any } func (e ErrInvalidInput) Error() string { diff --git a/internal/acceptance/clients/http.go b/internal/acceptance/clients/http.go index 2babf46897..d4e8489198 100644 --- a/internal/acceptance/clients/http.go +++ b/internal/acceptance/clients/http.go @@ -101,7 +101,7 @@ func (lrt *LogRoundTripper) logResponse(original io.ReadCloser, contentType stri // formatJSON will try to pretty-format a JSON body. // It will also mask known fields which contain sensitive information. func (lrt *LogRoundTripper) formatJSON(raw []byte) string { - var rawData interface{} + var rawData any err := json.Unmarshal(raw, &rawData) if err != nil { @@ -109,7 +109,7 @@ func (lrt *LogRoundTripper) formatJSON(raw []byte) string { return string(raw) } - data, ok := rawData.(map[string]interface{}) + data, ok := rawData.(map[string]any) if !ok { pretty, err := json.MarshalIndent(rawData, "", " ") if err != nil { @@ -121,24 +121,24 @@ func (lrt *LogRoundTripper) formatJSON(raw []byte) string { } // Mask known password fields - if v, ok := data["auth"].(map[string]interface{}); ok { - if v, ok := v["identity"].(map[string]interface{}); ok { - if v, ok := v["password"].(map[string]interface{}); ok { - if v, ok := v["user"].(map[string]interface{}); ok { + if v, ok := data["auth"].(map[string]any); ok { + if v, ok := v["identity"].(map[string]any); ok { + if v, ok := v["password"].(map[string]any); ok { + if v, ok := v["user"].(map[string]any); ok { v["password"] = "***" } } - if v, ok := v["application_credential"].(map[string]interface{}); ok { + if v, ok := v["application_credential"].(map[string]any); ok { v["secret"] = "***" } - if v, ok := v["token"].(map[string]interface{}); ok { + if v, ok := v["token"].(map[string]any); ok { v["id"] = "***" } } } // Ignore the catalog - if v, ok := data["token"].(map[string]interface{}); ok { + if v, ok := data["token"].(map[string]any); ok { if _, ok := v["catalog"]; ok { return "" } diff --git a/internal/acceptance/openstack/baremetal/v1/baremetal.go b/internal/acceptance/openstack/baremetal/v1/baremetal.go index 1212057db3..a47f942dad 100644 --- a/internal/acceptance/openstack/baremetal/v1/baremetal.go +++ b/internal/acceptance/openstack/baremetal/v1/baremetal.go @@ -22,7 +22,7 @@ func CreateNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Node, e Driver: "ipmi", BootInterface: "ipxe", RAIDInterface: "agent", - DriverInfo: map[string]interface{}{ + DriverInfo: map[string]any{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", @@ -86,7 +86,7 @@ func CreateFakeNode(t *testing.T, client *gophercloud.ServiceClient) (*nodes.Nod Driver: "fake-hardware", BootInterface: "fake", DeployInterface: "fake", - DriverInfo: map[string]interface{}{ + DriverInfo: map[string]any{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index 223033f25c..669ea69cdc 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -167,7 +167,7 @@ func TestNodesRAIDConfig(t *testing.T) { IsRootVolume: &isTrue, RAIDLevel: nodes.RAID5, Controller: "software", - PhysicalDisks: []interface{}{ + PhysicalDisks: []any{ map[string]string{ "size": "> 100", }, diff --git a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go index 14531c6ef3..327ea0d79d 100644 --- a/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/quotaset_test.go @@ -56,7 +56,7 @@ var UpdateQuotaOpts = quotasets.UpdateOpts{ Backups: gophercloud.IntToPointer(2), BackupGigabytes: gophercloud.IntToPointer(300), Groups: gophercloud.IntToPointer(350), - Extra: map[string]interface{}{ + Extra: map[string]any{ "volumes_foo": gophercloud.IntToPointer(100), }, } @@ -133,7 +133,7 @@ func TestQuotasetUpdate(t *testing.T) { // unpopulate resultQuotas.Extra as it is different per cloud and test // rest of the quotaSet - resultQuotas.Extra = map[string]interface{}(nil) + resultQuotas.Extra = map[string]any(nil) th.AssertDeepEquals(t, UpdatedQuotas, *resultQuotas) } diff --git a/internal/acceptance/openstack/compute/v2/aggregates_test.go b/internal/acceptance/openstack/compute/v2/aggregates_test.go index febaebd158..88ac3a3365 100644 --- a/internal/acceptance/openstack/compute/v2/aggregates_test.go +++ b/internal/acceptance/openstack/compute/v2/aggregates_test.go @@ -107,7 +107,7 @@ func TestAggregatesSetRemoveMetadata(t *testing.T) { defer DeleteAggregate(t, client, aggregate) opts := aggregates.SetMetadataOpts{ - Metadata: map[string]interface{}{"key": "value"}, + Metadata: map[string]any{"key": "value"}, } aggregateWithMetadata, err := aggregates.SetMetadata(context.TODO(), client, aggregate.ID, opts).Extract() @@ -120,7 +120,7 @@ func TestAggregatesSetRemoveMetadata(t *testing.T) { } optsToRemove := aggregates.SetMetadataOpts{ - Metadata: map[string]interface{}{"key": nil}, + Metadata: map[string]any{"key": nil}, } aggregateWithRemovedKey, err := aggregates.SetMetadata(context.TODO(), client, aggregate.ID, optsToRemove).Extract() diff --git a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go index e888d05cf4..cf9b1a973e 100644 --- a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go +++ b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -35,8 +35,8 @@ func TestAttachDetachInterface(t *testing.T) { th.AssertNoErr(t, err) var found bool - for _, networkAddresses := range server.Addresses[choices.NetworkName].([]interface{}) { - address := networkAddresses.(map[string]interface{}) + for _, networkAddresses := range server.Addresses[choices.NetworkName].([]any) { + address := networkAddresses.(map[string]any) if address["OS-EXT-IPS:type"] == "fixed" { fixedIP := address["addr"].(string) diff --git a/internal/acceptance/openstack/db/v1/configurations_test.go b/internal/acceptance/openstack/db/v1/configurations_test.go index ef62d9db0d..51058e16ba 100644 --- a/internal/acceptance/openstack/db/v1/configurations_test.go +++ b/internal/acceptance/openstack/db/v1/configurations_test.go @@ -34,7 +34,7 @@ func TestConfigurationsCRUD(t *testing.T) { } createOpts.Datastore = &datastore - values := make(map[string]interface{}) + values := make(map[string]any) values["collation_server"] = "latin1_swedish_ci" createOpts.Values = values diff --git a/internal/acceptance/openstack/identity/v3/groups_test.go b/internal/acceptance/openstack/identity/v3/groups_test.go index fcb6be66a9..b60fbd5a71 100644 --- a/internal/acceptance/openstack/identity/v3/groups_test.go +++ b/internal/acceptance/openstack/identity/v3/groups_test.go @@ -23,7 +23,7 @@ func TestGroupCRUD(t *testing.T) { createOpts := groups.CreateOpts{ Description: description, DomainID: domainID, - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "testgroup@example.com", }, } @@ -43,7 +43,7 @@ func TestGroupCRUD(t *testing.T) { description = "" updateOpts := groups.UpdateOpts{ Description: &description, - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "thetestgroup@example.com", }, } diff --git a/internal/acceptance/openstack/identity/v3/policies_test.go b/internal/acceptance/openstack/identity/v3/policies_test.go index 1af2a96f1b..0b23bdd9f5 100644 --- a/internal/acceptance/openstack/identity/v3/policies_test.go +++ b/internal/acceptance/openstack/identity/v3/policies_test.go @@ -38,7 +38,7 @@ func TestPoliciesCRUD(t *testing.T) { createOpts := policies.CreateOpts{ Type: "application/json", Blob: []byte("{'foobar_user': 'role:compute-user'}"), - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "policy for foobar_user", }, } @@ -124,7 +124,7 @@ func TestPoliciesCRUD(t *testing.T) { updateOpts := policies.UpdateOpts{ Type: "text/plain", Blob: []byte("'foobar_user': 'role:compute-user'"), - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "updated policy for foobar_user", }, } diff --git a/internal/acceptance/openstack/identity/v3/regions_test.go b/internal/acceptance/openstack/identity/v3/regions_test.go index 5b2a0d3efa..4a4df55f9e 100644 --- a/internal/acceptance/openstack/identity/v3/regions_test.go +++ b/internal/acceptance/openstack/identity/v3/regions_test.go @@ -63,7 +63,7 @@ func TestRegionsCRUD(t *testing.T) { createOpts := regions.CreateOpts{ ID: "testregion", Description: "Region for testing", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "testregion@example.com", }, } @@ -84,7 +84,7 @@ func TestRegionsCRUD(t *testing.T) { // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // The following lines should be uncommented once the fix is merged. - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "testregionA@example.com", }, */ diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index 3ab1e53826..ec69a9ba4e 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -59,7 +59,7 @@ func TestRolesCRUD(t *testing.T) { createOpts := roles.CreateOpts{ Name: "testrole", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "test role description", }, } @@ -92,7 +92,7 @@ func TestRolesCRUD(t *testing.T) { th.AssertEquals(t, found, true) updateOpts := roles.UpdateOpts{ - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "updated test role description", }, } @@ -114,7 +114,7 @@ func TestRolesFilterList(t *testing.T) { createOpts := roles.CreateOpts{ Name: "testrole", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "test role description", }, } @@ -798,7 +798,7 @@ func TestCRUDRoleInferenceRule(t *testing.T) { priorRoleCreateOpts := roles.CreateOpts{ Name: "priorRole", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "prior_role description", }, } @@ -811,7 +811,7 @@ func TestCRUDRoleInferenceRule(t *testing.T) { impliedRoleCreateOpts := roles.CreateOpts{ Name: "impliedRole", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "implied_role description", }, } diff --git a/internal/acceptance/openstack/identity/v3/service_test.go b/internal/acceptance/openstack/identity/v3/service_test.go index a4a2c43b53..8686f2c890 100644 --- a/internal/acceptance/openstack/identity/v3/service_test.go +++ b/internal/acceptance/openstack/identity/v3/service_test.go @@ -48,7 +48,7 @@ func TestServicesCRUD(t *testing.T) { createOpts := services.CreateOpts{ Type: "testing", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "testservice@example.com", }, } @@ -63,7 +63,7 @@ func TestServicesCRUD(t *testing.T) { updateOpts := services.UpdateOpts{ Type: "testing2", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "Test Users", "email": "thetestservice@example.com", }, diff --git a/internal/acceptance/openstack/identity/v3/users_test.go b/internal/acceptance/openstack/identity/v3/users_test.go index 5dd71a550f..9b2e63bfdf 100644 --- a/internal/acceptance/openstack/identity/v3/users_test.go +++ b/internal/acceptance/openstack/identity/v3/users_test.go @@ -126,14 +126,14 @@ func TestUserCRUD(t *testing.T) { Description: "test description", Password: "foobar", DomainID: "default", - Options: map[users.Option]interface{}{ + Options: map[users.Option]any{ users.IgnorePasswordExpiry: true, - users.MultiFactorAuthRules: []interface{}{ + users.MultiFactorAuthRules: []any{ []string{"password", "totp"}, []string{"password", "custom-auth-method"}, }, }, - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "jsmith@example.com", }, } @@ -155,10 +155,10 @@ func TestUserCRUD(t *testing.T) { Name: name, Description: &description, Enabled: &iFalse, - Options: map[users.Option]interface{}{ + Options: map[users.Option]any{ users.MultiFactorAuthRules: nil, }, - Extra: map[string]interface{}{ + Extra: map[string]any{ "disabled_reason": "DDOS", }, } diff --git a/internal/acceptance/openstack/image/v2/imageservice.go b/internal/acceptance/openstack/image/v2/imageservice.go index baccc68ae3..caa832ce6b 100644 --- a/internal/acceptance/openstack/image/v2/imageservice.go +++ b/internal/acceptance/openstack/image/v2/imageservice.go @@ -80,8 +80,8 @@ func CreateTask(t *testing.T, client *gophercloud.ServiceClient, imageURL string t.Logf("Attempting to create an Image service import task with image: %s", imageURL) opts := tasks.CreateOpts{ Type: "import", - Input: map[string]interface{}{ - "image_properties": map[string]interface{}{ + Input: map[string]any{ + "image_properties": map[string]any{ "container_format": "bare", "disk_format": "raw", }, diff --git a/internal/acceptance/openstack/messaging/v2/messaging.go b/internal/acceptance/openstack/messaging/v2/messaging.go index ce93bb0a44..39a463b99b 100644 --- a/internal/acceptance/openstack/messaging/v2/messaging.go +++ b/internal/acceptance/openstack/messaging/v2/messaging.go @@ -25,7 +25,7 @@ func CreateQueue(t *testing.T, client *gophercloud.ServiceClient) (string, error DefaultMessageTTL: 3700, DeadLetterQueueMessagesTTL: 3500, MaxClaimCount: 10, - Extra: map[string]interface{}{"description": "Test Queue for Gophercloud acceptance tests."}, + Extra: map[string]any{"description": "Test Queue for Gophercloud acceptance tests."}, } createErr := queues.Create(context.TODO(), client, createOpts).ExtractErr() @@ -77,7 +77,7 @@ func CreateMessage(t *testing.T, client *gophercloud.ServiceClient, queueName st createOpts := messages.BatchCreateOpts{ messages.CreateOpts{ TTL: 300, - Body: map[string]interface{}{"Key": tools.RandomString("ACPTTEST", 8)}, + Body: map[string]any{"Key": tools.RandomString("ACPTTEST", 8)}, }, } diff --git a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go index bbeaa17818..9c6bad662e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding.go @@ -19,7 +19,7 @@ type PortWithBindingExt struct { // CreatePortsbinding will create a port on the specified subnet. An error will be // returned if the port could not be created. -func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string, profile map[string]interface{}) (PortWithBindingExt, error) { +func CreatePortsbinding(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID, hostID string, profile map[string]any) (PortWithBindingExt, error) { portName := tools.RandomString("TESTACC-", 8) portDescription := tools.RandomString("TESTACC-PORT-DESC-", 8) iFalse := false 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 50a4dcd28c..025217b710 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/portsbinding/portsbinding_test.go @@ -32,7 +32,7 @@ func TestPortsbindingCRUD(t *testing.T) { // Define a host hostID := "localhost" - profile := map[string]interface{}{"foo": "bar"} + profile := map[string]any{"foo": "bar"} // Create port port, err := CreatePortsbinding(t, client, network.ID, subnet.ID, hostID, profile) @@ -48,7 +48,7 @@ func TestPortsbindingCRUD(t *testing.T) { newPortName := "" newPortDescription := "" newHostID := "127.0.0.1" - newProfile := map[string]interface{}{} + newProfile := map[string]any{} updateOpts := ports.UpdateOpts{ Name: &newPortName, Description: &newPortDescription, diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go index c62f76a754..9c24283e2a 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharetypes_test.go @@ -74,7 +74,7 @@ func TestShareTypeExtraSpecs(t *testing.T) { } options := sharetypes.SetExtraSpecsOpts{ - ExtraSpecs: map[string]interface{}{"my_new_key": "my_value"}, + ExtraSpecs: map[string]any{"my_new_key": "my_value"}, } _, err = sharetypes.SetExtraSpecs(context.TODO(), client, shareType.ID, options).Extract() diff --git a/internal/acceptance/openstack/workflow/v2/crontrigger.go b/internal/acceptance/openstack/workflow/v2/crontrigger.go index 821129c318..109807c81c 100644 --- a/internal/acceptance/openstack/workflow/v2/crontrigger.go +++ b/internal/acceptance/openstack/workflow/v2/crontrigger.go @@ -22,7 +22,7 @@ func CreateCronTrigger(t *testing.T, client *gophercloud.ServiceClient, workflow WorkflowID: workflow.ID, Name: crontriggerName, Pattern: "0 0 1 1 *", - WorkflowInput: map[string]interface{}{ + WorkflowInput: map[string]any{ "msg": "Hello World!", }, FirstExecutionTime: &firstExecution, diff --git a/internal/acceptance/openstack/workflow/v2/execution.go b/internal/acceptance/openstack/workflow/v2/execution.go index 730dbefb45..d2c2a71970 100644 --- a/internal/acceptance/openstack/workflow/v2/execution.go +++ b/internal/acceptance/openstack/workflow/v2/execution.go @@ -22,7 +22,7 @@ func CreateExecution(t *testing.T, client *gophercloud.ServiceClient, workflow * WorkflowID: workflow.ID, WorkflowNamespace: workflow.Namespace, Description: executionDescription, - Input: map[string]interface{}{ + Input: map[string]any{ "msg": "Hello World!", }, } diff --git a/internal/acceptance/tools/tools.go b/internal/acceptance/tools/tools.go index 428d4e6e0e..8b0fad2ea6 100644 --- a/internal/acceptance/tools/tools.go +++ b/internal/acceptance/tools/tools.go @@ -79,7 +79,7 @@ func Elide(value string) string { } // PrintResource returns a resource as a readable structure -func PrintResource(t *testing.T, resource interface{}) { +func PrintResource(t *testing.T, resource any) { b, _ := json.MarshalIndent(resource, "", " ") t.Logf(string(b)) } diff --git a/openstack/baremetal/inventory/plugindata.go b/openstack/baremetal/inventory/plugindata.go index c19f77610e..3c4666eb34 100644 --- a/openstack/baremetal/inventory/plugindata.go +++ b/openstack/baremetal/inventory/plugindata.go @@ -5,7 +5,7 @@ import ( "fmt" ) -type ExtraDataItem map[string]interface{} +type ExtraDataItem map[string]any type ExtraDataSection map[string]ExtraDataItem @@ -48,7 +48,7 @@ type LLDPTLVType struct { // UnmarshalJSON interprets an LLDP TLV [key, value] pair as an LLDPTLVType structure func (r *LLDPTLVType) UnmarshalJSON(data []byte) error { - var list []interface{} + var list []any if err := json.Unmarshal(data, &list); err != nil { return err } @@ -87,7 +87,7 @@ type ConfigurationType struct { Managers []HardwareManager `json:"managers"` } -type ParsedLLDP = map[string]interface{} +type ParsedLLDP = map[string]any type ProcessedInterfaceType struct { InterfaceType diff --git a/openstack/baremetal/inventory/testing/fixtures.go b/openstack/baremetal/inventory/testing/fixtures.go index 07acd9ada0..bc146b97de 100644 --- a/openstack/baremetal/inventory/testing/fixtures.go +++ b/openstack/baremetal/inventory/testing/fixtures.go @@ -335,69 +335,69 @@ var Inventory = inventory.InventoryType{ var ExtraData = inventory.ExtraDataType{ CPU: inventory.ExtraDataSection{ - "logical": map[string]interface{}{ + "logical": map[string]any{ "number": float64(16), }, - "physical": map[string]interface{}{ + "physical": map[string]any{ "clock": float64(2105032704), "cores": float64(8), "flags": "lm fpu fpu_exception wp vme de", }, }, Disk: inventory.ExtraDataSection{ - "sda": map[string]interface{}{ + "sda": map[string]any{ "rotational": float64(1), "vendor": "TEST", }, }, Firmware: inventory.ExtraDataSection{ - "bios": map[string]interface{}{ + "bios": map[string]any{ "date": "01/01/1970", "vendor": "test", }, }, IPMI: inventory.ExtraDataSection{ - "Fan1A RPM": map[string]interface{}{ + "Fan1A RPM": map[string]any{ "unit": "RPM", "value": float64(3120), }, - "Fan1B RPM": map[string]interface{}{ + "Fan1B RPM": map[string]any{ "unit": "RPM", "value": float64(2280), }, }, Memory: inventory.ExtraDataSection{ - "bank0": map[string]interface{}{ + "bank0": map[string]any{ "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", }, - "bank1": map[string]interface{}{ + "bank1": map[string]any{ "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", }, }, Network: inventory.ExtraDataSection{ - "em1": map[string]interface{}{ + "em1": map[string]any{ "Autonegotiate": "on", "loopback": "off [fixed]", }, - "p2p1": map[string]interface{}{ + "p2p1": map[string]any{ "Autonegotiate": "on", "loopback": "off [fixed]", }, }, System: inventory.ExtraDataSection{ - "ipmi": map[string]interface{}{ + "ipmi": map[string]any{ "channel": float64(1), }, - "kernel": map[string]interface{}{ + "kernel": map[string]any{ "arch": "x86_64", "version": "3.10.0", }, - "motherboard": map[string]interface{}{ + "motherboard": map[string]any{ "vendor": "Test", }, - "product": map[string]interface{}{ + "product": map[string]any{ "name": "test", "vendor": "Test", }, @@ -478,7 +478,7 @@ var StandardPluginData = inventory.StandardPluginData{ Extra: ExtraData, MACs: []string{"52:54:00:4e:3d:30"}, ParsedLLDP: map[string]inventory.ParsedLLDP{ - "eth0": map[string]interface{}{ + "eth0": map[string]any{ "switch_chassis_id": "11:22:33:aa:bb:cc", "switch_system_name": "sw01-dist-1b-b12", }, diff --git a/openstack/baremetal/v1/allocations/requests.go b/openstack/baremetal/v1/allocations/requests.go index 97d84270ff..aab9c31d80 100644 --- a/openstack/baremetal/v1/allocations/requests.go +++ b/openstack/baremetal/v1/allocations/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToAllocationCreateMap() (map[string]interface{}, error) + ToAllocationCreateMap() (map[string]any, error) } // CreateOpts specifies allocation creation parameters @@ -35,7 +35,7 @@ type CreateOpts struct { } // ToAllocationCreateMap assembles a request body based on the contents of a CreateOpts. -func (opts CreateOpts) ToAllocationCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToAllocationCreateMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/baremetal/v1/allocations/results.go b/openstack/baremetal/v1/allocations/results.go index 61fa2d88c3..932862e283 100644 --- a/openstack/baremetal/v1/allocations/results.go +++ b/openstack/baremetal/v1/allocations/results.go @@ -42,7 +42,7 @@ type Allocation struct { UpdatedAt time.Time `json:"updated_at"` // A list of relative links. Includes the self and bookmark links. - Links []interface{} `json:"links"` + Links []any `json:"links"` } type allocationResult struct { @@ -55,11 +55,11 @@ func (r allocationResult) Extract() (*Allocation, error) { return &s, err } -func (r allocationResult) ExtractInto(v interface{}) error { +func (r allocationResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "") } -func ExtractAllocationsInto(r pagination.Page, v interface{}) error { +func ExtractAllocationsInto(r pagination.Page, v any) error { return r.(AllocationPage).Result.ExtractIntoSlicePtr(v, "allocations") } diff --git a/openstack/baremetal/v1/allocations/testing/fixtures_test.go b/openstack/baremetal/v1/allocations/testing/fixtures_test.go index 0ccf3ed0d5..6b8655169b 100644 --- a/openstack/baremetal/v1/allocations/testing/fixtures_test.go +++ b/openstack/baremetal/v1/allocations/testing/fixtures_test.go @@ -103,7 +103,7 @@ var ( Traits: []string{"foo"}, Extra: map[string]string{}, CreatedAt: createdAt, - Links: []interface{}{map[string]interface{}{"href": "http://127.0.0.1:6385/v1/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "self"}, map[string]interface{}{"href": "http://127.0.0.1:6385/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "bookmark"}}, + Links: []any{map[string]any{"href": "http://127.0.0.1:6385/v1/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "self"}, map[string]any{"href": "http://127.0.0.1:6385/allocations/5344a3e2-978a-444e-990a-cbf47c62ef88", "rel": "bookmark"}}, } ) diff --git a/openstack/baremetal/v1/conductors/results.go b/openstack/baremetal/v1/conductors/results.go index 831a5d99d9..5431106108 100644 --- a/openstack/baremetal/v1/conductors/results.go +++ b/openstack/baremetal/v1/conductors/results.go @@ -18,11 +18,11 @@ func (r conductorResult) Extract() (*Conductor, error) { return &s, err } -func (r conductorResult) ExtractInto(v interface{}) error { +func (r conductorResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "") } -func ExtractConductorInto(r pagination.Page, v interface{}) error { +func ExtractConductorInto(r pagination.Page, v any) error { return r.(ConductorPage).Result.ExtractIntoSlicePtr(v, "conductors") } diff --git a/openstack/baremetal/v1/drivers/results.go b/openstack/baremetal/v1/drivers/results.go index 611257e854..4905fa902b 100644 --- a/openstack/baremetal/v1/drivers/results.go +++ b/openstack/baremetal/v1/drivers/results.go @@ -16,11 +16,11 @@ func (r driverResult) Extract() (*Driver, error) { return &s, err } -func (r driverResult) ExtractInto(v interface{}) error { +func (r driverResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "") } -func ExtractDriversInto(r pagination.Page, v interface{}) error { +func ExtractDriversInto(r pagination.Page, v any) error { return r.(DriverPage).Result.ExtractIntoSlicePtr(v, "drivers") } @@ -127,10 +127,10 @@ type Driver struct { EnabledVendorInterfaces []string `json:"enabled_vendor_interfaces"` //A list of relative links. Includes the self and bookmark links. - Links []interface{} `json:"links"` + Links []any `json:"links"` // A list of links to driver properties. - Properties []interface{} `json:"properties"` + Properties []any `json:"properties"` } // DriverPage abstracts the raw results of making a ListDrivers() request @@ -177,7 +177,7 @@ type GetDriverResult struct { } // DriverProperties represents driver properties in the OpenStack Bare Metal API. -type DriverProperties map[string]interface{} +type DriverProperties map[string]any // Extract interprets any GetPropertiesResult as DriverProperties, if possible. func (r GetPropertiesResult) Extract() (*DriverProperties, error) { @@ -193,7 +193,7 @@ type GetPropertiesResult struct { } // DiskProperties represents driver disk properties in the OpenStack Bare Metal API. -type DiskProperties map[string]interface{} +type DiskProperties map[string]any // Extract interprets any GetDiskPropertiesResult as DiskProperties, if possible. func (r GetDiskPropertiesResult) Extract() (*DiskProperties, error) { diff --git a/openstack/baremetal/v1/drivers/testing/fixtures_test.go b/openstack/baremetal/v1/drivers/testing/fixtures_test.go index fcd329ca6d..a06bcee928 100644 --- a/openstack/baremetal/v1/drivers/testing/fixtures_test.go +++ b/openstack/baremetal/v1/drivers/testing/fixtures_test.go @@ -229,22 +229,22 @@ var ( Name: "agent_ipmitool", Type: "classic", Hosts: []string{"897ab1dad809"}, - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://127.0.0.1:6385/drivers/agent_ipmitool", "rel": "bookmark", }, }, - Properties: []interface{}{ - map[string]interface{}{ + Properties: []any{ + map[string]any{ "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool/properties", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://127.0.0.1:6385/drivers/agent_ipmitool/properties", "rel": "bookmark", }, @@ -255,22 +255,22 @@ var ( Name: "fake", Type: "classic", Hosts: []string{"897ab1dad809"}, - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://127.0.0.1:6385/v1/drivers/fake", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://127.0.0.1:6385/drivers/fake", "rel": "bookmark", }, }, - Properties: []interface{}{ - map[string]interface{}{ + Properties: []any{ + map[string]any{ "href": "http://127.0.0.1:6385/v1/drivers/fake/properties", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://127.0.0.1:6385/drivers/fake/properties", "rel": "bookmark", }, @@ -307,22 +307,22 @@ var ( EnabledRaidInterfaces: []string{"no-raid", "agent"}, EnabledStorageInterfaces: []string{"noop"}, EnabledVendorInterfaces: []string{"no-vendor"}, - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://127.0.0.1:6385/v1/drivers/ipmi", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://127.0.0.1:6385/drivers/ipmi", "rel": "bookmark", }, }, - Properties: []interface{}{ - map[string]interface{}{ + Properties: []any{ + map[string]any{ "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://127.0.0.1:6385/drivers/ipmi/properties", "rel": "bookmark", }, diff --git a/openstack/baremetal/v1/nodes/doc.go b/openstack/baremetal/v1/nodes/doc.go index f463e13d24..e78a5848c6 100644 --- a/openstack/baremetal/v1/nodes/doc.go +++ b/openstack/baremetal/v1/nodes/doc.go @@ -43,7 +43,7 @@ Example to Create Node Driver: "ipmi", BootInterface: "pxe", Name: "coconuts", - DriverInfo: map[string]interface{}{ + DriverInfo: map[string]any{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index ad913515f6..f79721b4e7 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -194,7 +194,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToNodeCreateMap() (map[string]interface{}, error) + ToNodeCreateMap() (map[string]any, error) } // CreateOpts specifies node creation parameters. @@ -220,13 +220,13 @@ type CreateOpts struct { // All the metadata required by the driver to manage this Node. List of fields varies between drivers, and can // be retrieved from the /v1/drivers//properties resource. - DriverInfo map[string]interface{} `json:"driver_info,omitempty"` + DriverInfo map[string]any `json:"driver_info,omitempty"` // name of the driver used to manage this Node. Driver string `json:"driver,omitempty"` // A set of one or more arbitrary metadata key and value pairs. - Extra map[string]interface{} `json:"extra,omitempty"` + Extra map[string]any `json:"extra,omitempty"` // The firmware interface for a node, e.g. "redfish" FirmwareInterface string `json:"firmware_interface,omitempty"` @@ -248,7 +248,7 @@ type CreateOpts struct { // Physical characteristics of this Node. Populated during inspection, if performed. Can be edited via the REST // API at any time. - Properties map[string]interface{} `json:"properties,omitempty"` + Properties map[string]any `json:"properties,omitempty"` // Interface used for configuring RAID on this node, e.g. “no-raid”. RAIDInterface string `json:"raid_interface,omitempty"` @@ -273,11 +273,11 @@ type CreateOpts struct { Owner string `json:"owner,omitempty"` // Static network configuration to use during deployment and cleaning. - NetworkData map[string]interface{} `json:"network_data,omitempty"` + NetworkData map[string]any `json:"network_data,omitempty"` } // ToNodeCreateMap assembles a request body based on the contents of a CreateOpts. -func (opts CreateOpts) ToNodeCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToNodeCreateMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -300,7 +300,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO } type Patch interface { - ToNodeUpdateMap() (map[string]interface{}, error) + ToNodeUpdateMap() (map[string]any, error) } // UpdateOpts is a slice of Patches used to update a node @@ -315,18 +315,18 @@ const ( ) type UpdateOperation struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value interface{} `json:"value,omitempty"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value any `json:"value,omitempty"` } -func (opts UpdateOperation) ToNodeUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOperation) ToNodeUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } // Update requests that a node be updated func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { - body := make([]map[string]interface{}, len(opts)) + body := make([]map[string]any, len(opts)) for i, patch := range opts { result, err := patch.ToNodeUpdateMap() if err != nil { @@ -378,11 +378,11 @@ type BootDeviceOpts struct { // BootDeviceOptsBuilder allows extensions to add additional parameters to the // SetBootDevice request. type BootDeviceOptsBuilder interface { - ToBootDeviceMap() (map[string]interface{}, error) + ToBootDeviceMap() (map[string]any, error) } // ToBootDeviceSetMap assembles a request body based on the contents of a BootDeviceOpts. -func (opts BootDeviceOpts) ToBootDeviceMap() (map[string]interface{}, error) { +func (opts BootDeviceOpts) ToBootDeviceMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -441,9 +441,9 @@ const ( // the value for ‘args’ is a keyword variable argument dictionary that is passed to the cleaning step // method. type CleanStep struct { - Interface StepInterface `json:"interface" required:"true"` - Step string `json:"step" required:"true"` - Args map[string]interface{} `json:"args,omitempty"` + Interface StepInterface `json:"interface" required:"true"` + Step string `json:"step" required:"true"` + Args map[string]any `json:"args,omitempty"` } // A service step looks the same as a cleaning step. @@ -453,31 +453,31 @@ type ServiceStep = CleanStep // The value for ‘args’ is a keyword variable argument dictionary that is passed to the deploy step // method. Priority is a numeric priority at which the step is running. type DeployStep struct { - Interface StepInterface `json:"interface" required:"true"` - Step string `json:"step" required:"true"` - Args map[string]interface{} `json:"args" required:"true"` - Priority int `json:"priority" required:"true"` + Interface StepInterface `json:"interface" required:"true"` + Step string `json:"step" required:"true"` + Args map[string]any `json:"args" required:"true"` + Priority int `json:"priority" required:"true"` } // ProvisionStateOptsBuilder allows extensions to add additional parameters to the // ChangeProvisionState request. type ProvisionStateOptsBuilder interface { - ToProvisionStateMap() (map[string]interface{}, error) + ToProvisionStateMap() (map[string]any, error) } // Starting with Ironic API version 1.56, a configdrive may be a JSON object with structured data. // Prior to this version, it must be a base64-encoded, gzipped ISO9660 image. type ConfigDrive struct { - MetaData map[string]interface{} `json:"meta_data,omitempty"` - NetworkData map[string]interface{} `json:"network_data,omitempty"` - UserData interface{} `json:"user_data,omitempty"` + MetaData map[string]any `json:"meta_data,omitempty"` + NetworkData map[string]any `json:"network_data,omitempty"` + UserData any `json:"user_data,omitempty"` } // ProvisionStateOpts for a request to change a node's provision state. A config drive should be base64-encoded // gzipped ISO9660 image. Deploy steps are supported starting with API 1.69. type ProvisionStateOpts struct { Target TargetProvisionState `json:"target" required:"true"` - ConfigDrive interface{} `json:"configdrive,omitempty"` + ConfigDrive any `json:"configdrive,omitempty"` CleanSteps []CleanStep `json:"clean_steps,omitempty"` DeploySteps []DeployStep `json:"deploy_steps,omitempty"` ServiceSteps []ServiceStep `json:"service_steps,omitempty"` @@ -485,7 +485,7 @@ type ProvisionStateOpts struct { } // ToProvisionStateMap assembles a request body based on the contents of a CreateOpts. -func (opts ProvisionStateOpts) ToProvisionStateMap() (map[string]interface{}, error) { +func (opts ProvisionStateOpts) ToProvisionStateMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -523,7 +523,7 @@ const ( // PowerStateOptsBuilder allows extensions to add additional parameters to the ChangePowerState request. type PowerStateOptsBuilder interface { - ToPowerStateMap() (map[string]interface{}, error) + ToPowerStateMap() (map[string]any, error) } // PowerStateOpts for a request to change a node's power state. @@ -533,7 +533,7 @@ type PowerStateOpts struct { } // ToPowerStateMap assembles a request body based on the contents of a PowerStateOpts. -func (opts PowerStateOpts) ToPowerStateMap() (map[string]interface{}, error) { +func (opts PowerStateOpts) ToPowerStateMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -564,7 +564,7 @@ type RAIDConfigOpts struct { // RAIDConfigOptsBuilder allows extensions to modify a set RAID config request. type RAIDConfigOptsBuilder interface { - ToRAIDConfigMap() (map[string]interface{}, error) + ToRAIDConfigMap() (map[string]any, error) } // RAIDLevel type is used to specify the RAID level for a logical disk. @@ -629,18 +629,18 @@ type LogicalDisk struct { Controller string `json:"controller,omitempty"` // A list of physical disks to use as read by the RAID interface. - PhysicalDisks []interface{} `json:"physical_disks,omitempty"` + PhysicalDisks []any `json:"physical_disks,omitempty"` } -func (opts RAIDConfigOpts) ToRAIDConfigMap() (map[string]interface{}, error) { +func (opts RAIDConfigOpts) ToRAIDConfigMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if body["logical_disks"] != nil { - for _, v := range body["logical_disks"].([]interface{}) { - if logicalDisk, ok := v.(map[string]interface{}); ok { + for _, v := range body["logical_disks"].([]any) { + if logicalDisk, ok := v.(map[string]any); ok { if logicalDisk["size_gb"] == nil { logicalDisk["size_gb"] = "MAX" } @@ -762,7 +762,7 @@ type GetSubscriptionOpts struct { } // ToGetSubscriptionMap assembles a query based on the contents of CallVendorPassthruOpts and a request body based on the contents of a GetSubscriptionOpts -func ToGetSubscriptionMap(method CallVendorPassthruOpts, opts GetSubscriptionOpts) (string, map[string]interface{}, error) { +func ToGetSubscriptionMap(method CallVendorPassthruOpts, opts GetSubscriptionOpts) (string, map[string]any, error) { q, err := gophercloud.BuildQueryString(method) if err != nil { return q.String(), nil, err @@ -797,7 +797,7 @@ type DeleteSubscriptionOpts struct { } // ToDeleteSubscriptionMap assembles a query based on the contents of CallVendorPassthruOpts and a request body based on the contents of a DeleteSubscriptionOpts -func ToDeleteSubscriptionMap(method CallVendorPassthruOpts, opts DeleteSubscriptionOpts) (string, map[string]interface{}, error) { +func ToDeleteSubscriptionMap(method CallVendorPassthruOpts, opts DeleteSubscriptionOpts) (string, map[string]any, error) { q, err := gophercloud.BuildQueryString(method) if err != nil { return q.String(), nil, err @@ -835,7 +835,7 @@ type CreateSubscriptionOpts struct { } // ToCreateSubscriptionMap assembles a query based on the contents of CallVendorPassthruOpts and a request body based on the contents of a CreateSubscriptionOpts -func ToCreateSubscriptionMap(method CallVendorPassthruOpts, opts CreateSubscriptionOpts) (string, map[string]interface{}, error) { +func ToCreateSubscriptionMap(method CallVendorPassthruOpts, opts CreateSubscriptionOpts) (string, map[string]any, error) { q, err := gophercloud.BuildQueryString(method) if err != nil { return q.String(), nil, err @@ -869,11 +869,11 @@ type MaintenanceOpts struct { // MaintenanceOptsBuilder allows extensions to add additional parameters to the SetMaintenance request. type MaintenanceOptsBuilder interface { - ToMaintenanceMap() (map[string]interface{}, error) + ToMaintenanceMap() (map[string]any, error) } // ToMaintenanceMap assembles a request body based on the contents of a MaintenanceOpts. -func (opts MaintenanceOpts) ToMaintenanceMap() (map[string]interface{}, error) { +func (opts MaintenanceOpts) ToMaintenanceMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -948,10 +948,10 @@ type AttachVirtualMediaOpts struct { } type AttachVirtualMediaOptsBuilder interface { - ToAttachVirtualMediaMap() (map[string]interface{}, error) + ToAttachVirtualMediaMap() (map[string]any, error) } -func (opts AttachVirtualMediaOpts) ToAttachVirtualMediaMap() (map[string]interface{}, error) { +func (opts AttachVirtualMediaOpts) ToAttachVirtualMediaMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 7bfd5406fa..0602add768 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -46,11 +46,11 @@ func (r ValidateResult) Extract() (*NodeValidation, error) { return &s, err } -func (r nodeResult) ExtractInto(v interface{}) error { +func (r nodeResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "") } -func ExtractNodesInto(r pagination.Page, v interface{}) error { +func ExtractNodesInto(r pagination.Page, v any) error { return r.(NodePage).Result.ExtractIntoSlicePtr(v, "nodes") } @@ -144,19 +144,19 @@ type Node struct { // The metadata required by the driver to manage this Node. List of fields varies between drivers, and can be // retrieved from the /v1/drivers//properties resource. - DriverInfo map[string]interface{} `json:"driver_info"` + DriverInfo map[string]any `json:"driver_info"` // Metadata set and stored by the Node’s driver. This field is read-only. - DriverInternalInfo map[string]interface{} `json:"driver_internal_info"` + DriverInternalInfo map[string]any `json:"driver_internal_info"` // Characteristics of this Node. Populated by ironic-inspector during inspection. May be edited via the REST // API at any time. - Properties map[string]interface{} `json:"properties"` + Properties map[string]any `json:"properties"` // Used to customize the deployed image. May include root partition size, a base 64 encoded config drive, and other // metadata. Note that this field is erased automatically when the instance is deleted (this is done by requesting // the Node provision state be changed to DELETED). - InstanceInfo map[string]interface{} `json:"instance_info"` + InstanceInfo map[string]any `json:"instance_info"` // ID of the Nova instance associated with this Node. InstanceUUID string `json:"instance_uuid"` @@ -165,26 +165,26 @@ type Node struct { ChassisUUID string `json:"chassis_uuid"` // Set of one or more arbitrary metadata key and value pairs. - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` // Whether console access is enabled or disabled on this node. ConsoleEnabled bool `json:"console_enabled"` // The current RAID configuration of the node. Introduced with the cleaning feature. - RAIDConfig map[string]interface{} `json:"raid_config"` + RAIDConfig map[string]any `json:"raid_config"` // The requested RAID configuration of the node, which will be applied when the Node next transitions // through the CLEANING state. Introduced with the cleaning feature. - TargetRAIDConfig map[string]interface{} `json:"target_raid_config"` + TargetRAIDConfig map[string]any `json:"target_raid_config"` // Current clean step. Introduced with the cleaning feature. - CleanStep map[string]interface{} `json:"clean_step"` + CleanStep map[string]any `json:"clean_step"` // Current deploy step. - DeployStep map[string]interface{} `json:"deploy_step"` + DeployStep map[string]any `json:"deploy_step"` // Current service step. - ServiceStep map[string]interface{} `json:"service_step"` + ServiceStep map[string]any `json:"service_step"` // String which can be used by external schedulers to identify this Node as a unit of a specific type of resource. // For more details, see: https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html @@ -245,7 +245,7 @@ type Node struct { Owner string `json:"owner"` // Static network configuration to use during deployment and cleaning. - NetworkData map[string]interface{} `json:"network_data"` + NetworkData map[string]any `json:"network_data"` // The UTC date and time when the resource was created, ISO 8601 format. CreatedAt time.Time `json:"created_at"` @@ -549,7 +549,7 @@ type PluginData struct { } // Interpret plugin data as a free-form mapping. -func (pd PluginData) AsMap() (result map[string]interface{}, err error) { +func (pd PluginData) AsMap() (result map[string]any, err error) { err = json.Unmarshal(pd.RawMessage, &result) return } diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index 8bfb474b74..8ab570b54d 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -909,7 +909,7 @@ var ( LastError: "", Reservation: "", Driver: "ipmi", - DriverInfo: map[string]interface{}{ + DriverInfo: map[string]any{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", @@ -917,17 +917,17 @@ var ( "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, - DriverInternalInfo: map[string]interface{}{}, - Properties: map[string]interface{}{}, - InstanceInfo: map[string]interface{}{}, + DriverInternalInfo: map[string]any{}, + Properties: map[string]any{}, + InstanceInfo: map[string]any{}, InstanceUUID: "", ChassisUUID: "", - Extra: map[string]interface{}{}, + Extra: map[string]any{}, ConsoleEnabled: false, - RAIDConfig: map[string]interface{}{}, - TargetRAIDConfig: map[string]interface{}{}, - CleanStep: map[string]interface{}{}, - DeployStep: map[string]interface{}{}, + RAIDConfig: map[string]any{}, + TargetRAIDConfig: map[string]any{}, + CleanStep: map[string]any{}, + DeployStep: map[string]any{}, ResourceClass: "", BIOSInterface: "no-bios", BootInterface: "pxe", @@ -1024,18 +1024,18 @@ var ( LastError: "", Reservation: "", Driver: "ipmi", - DriverInfo: map[string]interface{}{}, - DriverInternalInfo: map[string]interface{}{}, - Properties: map[string]interface{}{}, - InstanceInfo: map[string]interface{}{}, + DriverInfo: map[string]any{}, + DriverInternalInfo: map[string]any{}, + Properties: map[string]any{}, + InstanceInfo: map[string]any{}, InstanceUUID: "", ChassisUUID: "", - Extra: map[string]interface{}{}, + Extra: map[string]any{}, ConsoleEnabled: false, - RAIDConfig: map[string]interface{}{}, - TargetRAIDConfig: map[string]interface{}{}, - CleanStep: map[string]interface{}{}, - DeployStep: map[string]interface{}{}, + RAIDConfig: map[string]any{}, + TargetRAIDConfig: map[string]any{}, + CleanStep: map[string]any{}, + DeployStep: map[string]any{}, ResourceClass: "", BIOSInterface: "no-bios", BootInterface: "pxe", @@ -1073,18 +1073,18 @@ var ( LastError: "", Reservation: "", Driver: "ipmi", - DriverInfo: map[string]interface{}{}, - DriverInternalInfo: map[string]interface{}{}, - Properties: map[string]interface{}{}, - InstanceInfo: map[string]interface{}{}, + DriverInfo: map[string]any{}, + DriverInternalInfo: map[string]any{}, + Properties: map[string]any{}, + InstanceInfo: map[string]any{}, InstanceUUID: "", ChassisUUID: "", - Extra: map[string]interface{}{}, + Extra: map[string]any{}, ConsoleEnabled: false, - RAIDConfig: map[string]interface{}{}, - TargetRAIDConfig: map[string]interface{}{}, - CleanStep: map[string]interface{}{}, - DeployStep: map[string]interface{}{}, + RAIDConfig: map[string]any{}, + TargetRAIDConfig: map[string]any{}, + CleanStep: map[string]any{}, + DeployStep: map[string]any{}, ResourceClass: "", BIOSInterface: "no-bios", BootInterface: "pxe", @@ -1108,12 +1108,12 @@ var ( } ConfigDriveMap = nodes.ConfigDrive{ - UserData: map[string]interface{}{ + UserData: map[string]any{ "ignition": map[string]string{ "version": "2.2.0", }, - "systemd": map[string]interface{}{ - "units": []map[string]interface{}{{ + "systemd": map[string]any{ + "units": []map[string]any{{ "name": "example.service", "enabled": true, }, diff --git a/openstack/baremetal/v1/nodes/testing/requests_test.go b/openstack/baremetal/v1/nodes/testing/requests_test.go index dfafacba91..5529e795ad 100644 --- a/openstack/baremetal/v1/nodes/testing/requests_test.go +++ b/openstack/baremetal/v1/nodes/testing/requests_test.go @@ -98,7 +98,7 @@ func TestCreateNode(t *testing.T) { Name: "foo", Driver: "ipmi", BootInterface: "pxe", - DriverInfo: map[string]interface{}{ + DriverInfo: map[string]any{ "ipmi_port": "6230", "ipmi_username": "admin", "deploy_kernel": "http://172.22.0.1/images/tinyipa-stable-rocky.vmlinuz", @@ -146,7 +146,7 @@ func TestUpdateNode(t *testing.T) { nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/properties", - Value: map[string]interface{}{ + Value: map[string]any{ "root_gb": 25, }, }, @@ -270,8 +270,8 @@ func TestNodeChangeProvisionStateActiveWithSteps(t *testing.T) { Interface: nodes.InterfaceDeploy, Step: "inject_files", Priority: 50, - Args: map[string]interface{}{ - "files": []interface{}{}, + Args: map[string]any{ + "files": []any{}, }, }, }, @@ -308,7 +308,7 @@ func TestNodeChangeProvisionStateClean(t *testing.T) { { Interface: nodes.InterfaceDeploy, Step: "upgrade_firmware", - Args: map[string]interface{}{ + Args: map[string]any{ "force": "True", }, }, @@ -330,7 +330,7 @@ func TestNodeChangeProvisionStateCleanWithConflict(t *testing.T) { { Interface: nodes.InterfaceDeploy, Step: "upgrade_firmware", - Args: map[string]interface{}{ + Args: map[string]any{ "force": "True", }, }, @@ -349,7 +349,7 @@ func TestCleanStepRequiresInterface(t *testing.T) { CleanSteps: []nodes.CleanStep{ { Step: "upgrade_firmware", - Args: map[string]interface{}{ + Args: map[string]any{ "force": "True", }, }, @@ -368,7 +368,7 @@ func TestCleanStepRequiresStep(t *testing.T) { CleanSteps: []nodes.CleanStep{ { Interface: nodes.InterfaceDeploy, - Args: map[string]interface{}{ + Args: map[string]any{ "force": "True", }, }, @@ -392,7 +392,7 @@ func TestNodeChangeProvisionStateService(t *testing.T) { { Interface: nodes.InterfaceBIOS, Step: "apply_configuration", - Args: map[string]interface{}{ + Args: map[string]any{ "settings": []string{}, }, }, @@ -483,12 +483,12 @@ func TestToRAIDConfigMap(t *testing.T) { cases := []struct { name string opts nodes.RAIDConfigOpts - expected map[string]interface{} + expected map[string]any }{ { name: "LogicalDisks is empty", opts: nodes.RAIDConfigOpts{}, - expected: map[string]interface{}{ + expected: map[string]any{ "logical_disks": nil, }, }, @@ -497,7 +497,7 @@ func TestToRAIDConfigMap(t *testing.T) { opts: nodes.RAIDConfigOpts{ LogicalDisks: nil, }, - expected: map[string]interface{}{ + expected: map[string]any{ "logical_disks": nil, }, }, @@ -508,17 +508,17 @@ func TestToRAIDConfigMap(t *testing.T) { { RAIDLevel: "0", VolumeName: "root", - PhysicalDisks: []interface{}{"6I:1:5", "6I:1:6", "6I:1:7"}, + PhysicalDisks: []any{"6I:1:5", "6I:1:6", "6I:1:7"}, }, }, }, - expected: map[string]interface{}{ - "logical_disks": []map[string]interface{}{ + expected: map[string]any{ + "logical_disks": []map[string]any{ { "raid_level": "0", "size_gb": "MAX", "volume_name": "root", - "physical_disks": []interface{}{"6I:1:5", "6I:1:6", "6I:1:7"}, + "physical_disks": []any{"6I:1:5", "6I:1:6", "6I:1:7"}, }, }, }, @@ -531,7 +531,7 @@ func TestToRAIDConfigMap(t *testing.T) { RAIDLevel: "0", VolumeName: "root", Controller: "software", - PhysicalDisks: []interface{}{ + PhysicalDisks: []any{ map[string]string{ "size": "> 100", }, @@ -542,18 +542,18 @@ func TestToRAIDConfigMap(t *testing.T) { }, }, }, - expected: map[string]interface{}{ - "logical_disks": []map[string]interface{}{ + expected: map[string]any{ + "logical_disks": []map[string]any{ { "raid_level": "0", "size_gb": "MAX", "volume_name": "root", "controller": "software", - "physical_disks": []interface{}{ - map[string]interface{}{ + "physical_disks": []any{ + map[string]any{ "size": "> 100", }, - map[string]interface{}{ + map[string]any{ "size": "> 100", }, }, diff --git a/openstack/baremetal/v1/ports/requests.go b/openstack/baremetal/v1/ports/requests.go index 291ddca120..873de897f8 100644 --- a/openstack/baremetal/v1/ports/requests.go +++ b/openstack/baremetal/v1/ports/requests.go @@ -110,7 +110,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToPortCreateMap() (map[string]interface{}, error) + ToPortCreateMap() (map[string]any, error) } // CreateOpts specifies port creation parameters. @@ -130,7 +130,7 @@ type CreateOpts struct { // field) and port_id (identifier of the physical port on the switch to which // node’s port is connected to) fields. switch_info is an optional string // field to be used to store any vendor-specific information. - LocalLinkConnection map[string]interface{} `json:"local_link_connection,omitempty"` + LocalLinkConnection map[string]any `json:"local_link_connection,omitempty"` // Indicates whether PXE is enabled or disabled on the Port. PXEEnabled *bool `json:"pxe_enabled,omitempty"` @@ -139,14 +139,14 @@ type CreateOpts struct { PhysicalNetwork string `json:"physical_network,omitempty"` // A set of one or more arbitrary metadata key and value pairs. - Extra map[string]interface{} `json:"extra,omitempty"` + Extra map[string]any `json:"extra,omitempty"` // Indicates whether the Port is a Smart NIC port. IsSmartNIC *bool `json:"is_smartnic,omitempty"` } // ToPortCreateMap assembles a request body based on the contents of a CreateOpts. -func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPortCreateMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -170,7 +170,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // TODO Update type Patch interface { - ToPortUpdateMap() map[string]interface{} + ToPortUpdateMap() map[string]any } // UpdateOpts is a slice of Patches used to update a port @@ -185,13 +185,13 @@ const ( ) type UpdateOperation struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value interface{} `json:"value,omitempty"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value any `json:"value,omitempty"` } -func (opts UpdateOperation) ToPortUpdateMap() map[string]interface{} { - return map[string]interface{}{ +func (opts UpdateOperation) ToPortUpdateMap() map[string]any { + return map[string]any{ "op": opts.Op, "path": opts.Path, "value": opts.Value, @@ -200,7 +200,7 @@ func (opts UpdateOperation) ToPortUpdateMap() map[string]interface{} { // Update - requests the update of a port func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { - body := make([]map[string]interface{}, len(opts)) + body := make([]map[string]any, len(opts)) for i, patch := range opts { body[i] = patch.ToPortUpdateMap() } diff --git a/openstack/baremetal/v1/ports/results.go b/openstack/baremetal/v1/ports/results.go index d6c6ae22d7..b1ec5cca81 100644 --- a/openstack/baremetal/v1/ports/results.go +++ b/openstack/baremetal/v1/ports/results.go @@ -17,11 +17,11 @@ func (r portResult) Extract() (*Port, error) { return &s, err } -func (r portResult) ExtractInto(v interface{}) error { +func (r portResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "") } -func ExtractPortsInto(r pagination.Page, v interface{}) error { +func ExtractPortsInto(r pagination.Page, v any) error { return r.(PortPage).Result.ExtractIntoSlicePtr(v, "ports") } @@ -45,7 +45,7 @@ type Port struct { // field) and port_id (identifier of the physical port on the switch to which // node’s port is connected to) fields. switch_info is an optional string // field to be used to store any vendor-specific information. - LocalLinkConnection map[string]interface{} `json:"local_link_connection"` + LocalLinkConnection map[string]any `json:"local_link_connection"` // Indicates whether PXE is enabled or disabled on the Port. PXEEnabled bool `json:"pxe_enabled"` @@ -55,10 +55,10 @@ type Port struct { PhysicalNetwork string `json:"physical_network"` // Internal metadata set and stored by the Port. This field is read-only. - InternalInfo map[string]interface{} `json:"internal_info"` + InternalInfo map[string]any `json:"internal_info"` // A set of one or more arbitrary metadata key and value pairs. - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` // The UTC date and time when the resource was created, ISO 8601 format. CreatedAt time.Time `json:"created_at"` @@ -68,7 +68,7 @@ type Port struct { UpdatedAt time.Time `json:"updated_at"` // A list of relative links. Includes the self and bookmark links. - Links []interface{} `json:"links"` + Links []any `json:"links"` // Indicates whether the Port is a Smart NIC port. IsSmartNIC bool `json:"is_smartnic"` diff --git a/openstack/baremetal/v1/ports/testing/fixtures_test.go b/openstack/baremetal/v1/ports/testing/fixtures_test.go index 6a97dde39c..492aa42aa6 100644 --- a/openstack/baremetal/v1/ports/testing/fixtures_test.go +++ b/openstack/baremetal/v1/ports/testing/fixtures_test.go @@ -146,12 +146,12 @@ var ( NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", Address: "52:54:00:4d:87:e6", PXEEnabled: true, - LocalLinkConnection: map[string]interface{}{}, - InternalInfo: map[string]interface{}{}, - Extra: map[string]interface{}{}, + LocalLinkConnection: map[string]any{}, + InternalInfo: map[string]any{}, + Extra: map[string]any{}, CreatedAt: fooCreated, UpdatedAt: fooUpdated, - Links: []interface{}{map[string]interface{}{"href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "self"}, map[string]interface{}{"href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "bookmark"}}, + Links: []any{map[string]any{"href": "http://192.168.0.8/baremetal/v1/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "self"}, map[string]any{"href": "http://192.168.0.8/baremetal/ports/f2845e11-dbd4-4728-a8c0-30d19f48924a", "rel": "bookmark"}}, } PortBar = ports.Port{ @@ -159,12 +159,12 @@ var ( NodeUUID: "ddd06a60-b91e-4ab4-a6e7-56c0b25b6086", Address: "52:54:00:0a:af:d1", PXEEnabled: true, - LocalLinkConnection: map[string]interface{}{}, - InternalInfo: map[string]interface{}{}, - Extra: map[string]interface{}{}, + LocalLinkConnection: map[string]any{}, + InternalInfo: map[string]any{}, + Extra: map[string]any{}, CreatedAt: BarCreated, UpdatedAt: BarUpdated, - Links: []interface{}{map[string]interface{}{"href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "self"}, map[string]interface{}{"rel": "bookmark", "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7"}}, + Links: []any{map[string]any{"href": "http://192.168.0.8/baremetal/v1/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7", "rel": "self"}, map[string]any{"rel": "bookmark", "href": "http://192.168.0.8/baremetal/ports/3abe3f36-9708-4e9f-b07e-0f898061d3a7"}}, } ) diff --git a/openstack/baremetalintrospection/v1/introspection/results.go b/openstack/baremetalintrospection/v1/introspection/results.go index 872a8dc30e..00e0287bc4 100644 --- a/openstack/baremetalintrospection/v1/introspection/results.go +++ b/openstack/baremetalintrospection/v1/introspection/results.go @@ -21,13 +21,13 @@ func (r introspectionResult) Extract() (*Introspection, error) { } // ExtractInto will extract a response body into an Introspection struct. -func (r introspectionResult) ExtractInto(v interface{}) error { +func (r introspectionResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "") } // ExtractIntrospectionsInto will extract a collection of introspectResult pages into a // slice of Introspection entities. -func ExtractIntrospectionsInto(r pagination.Page, v interface{}) error { +func ExtractIntrospectionsInto(r pagination.Page, v any) error { return r.(IntrospectionPage).Result.ExtractIntoSlicePtr(v, "introspection") } @@ -68,7 +68,7 @@ type Introspection struct { FinishedAt time.Time `json:"-"` // Link to the introspection URL - Links []interface{} `json:"links"` + Links []any `json:"links"` } // IsEmpty returns true if a page contains no Introspection results. @@ -182,11 +182,11 @@ type Data struct { // Sub Types defined under Data and deeper in the structure type BaseInterfaceType struct { - ClientID string `json:"client_id"` - IP string `json:"ip"` - MAC string `json:"mac"` - PXE bool `json:"pxe"` - LLDPProcessed map[string]interface{} `json:"lldp_processed"` + ClientID string `json:"client_id"` + IP string `json:"ip"` + MAC string `json:"mac"` + PXE bool `json:"pxe"` + LLDPProcessed map[string]any `json:"lldp_processed"` } // Extract interprets any IntrospectionDataResult as IntrospectionData, if possible. diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index 370d10d769..af2ad9e7f5 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -267,8 +267,8 @@ var ( UUID: "05ccda19-581b-49bf-8f5a-6ded99701d87", StartedAt: fooTimeStarted, FinishedAt: fooTimeFinished, - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://127.0.0.1:5050/v1/introspection/05ccda19-581b-49bf-8f5a-6ded99701d87", "rel": "self", }, @@ -284,8 +284,8 @@ var ( UUID: "c244557e-899f-46fa-a1ff-5b2c6718616b", StartedAt: barTimeStarted, FinishedAt: barTimeFinished, - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://127.0.0.1:5050/v1/introspection/c244557e-899f-46fa-a1ff-5b2c6718616b", "rel": "self", }, @@ -326,7 +326,7 @@ var ( IP: "172.24.42.100", MAC: "52:54:00:4e:3d:30", PXE: true, - LLDPProcessed: map[string]interface{}{ + LLDPProcessed: map[string]any{ "switch_chassis_id": "11:22:33:aa:bb:cc", "switch_system_name": "sw01-dist-1b-b12", }, @@ -348,69 +348,69 @@ var ( IntrospectionExtraHardware = inventory.ExtraDataType{ CPU: inventory.ExtraDataSection{ - "logical": map[string]interface{}{ + "logical": map[string]any{ "number": float64(16), }, - "physical": map[string]interface{}{ + "physical": map[string]any{ "clock": float64(2105032704), "cores": float64(8), "flags": "lm fpu fpu_exception wp vme de", }, }, Disk: inventory.ExtraDataSection{ - "sda": map[string]interface{}{ + "sda": map[string]any{ "rotational": float64(1), "vendor": "TEST", }, }, Firmware: inventory.ExtraDataSection{ - "bios": map[string]interface{}{ + "bios": map[string]any{ "date": "01/01/1970", "vendor": "test", }, }, IPMI: inventory.ExtraDataSection{ - "Fan1A RPM": map[string]interface{}{ + "Fan1A RPM": map[string]any{ "unit": "RPM", "value": float64(3120), }, - "Fan1B RPM": map[string]interface{}{ + "Fan1B RPM": map[string]any{ "unit": "RPM", "value": float64(2280), }, }, Memory: inventory.ExtraDataSection{ - "bank0": map[string]interface{}{ + "bank0": map[string]any{ "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", }, - "bank1": map[string]interface{}{ + "bank1": map[string]any{ "clock": 1600000000.0, "description": "DIMM DDR3 Synchronous Registered (Buffered) 1600 MHz (0.6 ns)", }, }, Network: inventory.ExtraDataSection{ - "em1": map[string]interface{}{ + "em1": map[string]any{ "Autonegotiate": "on", "loopback": "off [fixed]", }, - "p2p1": map[string]interface{}{ + "p2p1": map[string]any{ "Autonegotiate": "on", "loopback": "off [fixed]", }, }, System: inventory.ExtraDataSection{ - "ipmi": map[string]interface{}{ + "ipmi": map[string]any{ "channel": float64(1), }, - "kernel": map[string]interface{}{ + "kernel": map[string]any{ "arch": "x86_64", "version": "3.10.0", }, - "motherboard": map[string]interface{}{ + "motherboard": map[string]any{ "vendor": "Test", }, - "product": map[string]interface{}{ + "product": map[string]any{ "name": "test", "vendor": "Test", }, diff --git a/openstack/blockstorage/v2/backups/requests.go b/openstack/blockstorage/v2/backups/requests.go index f8e0d67daf..573946ab25 100644 --- a/openstack/blockstorage/v2/backups/requests.go +++ b/openstack/blockstorage/v2/backups/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToBackupCreateMap() (map[string]interface{}, error) + ToBackupCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a Backup. This object is passed to @@ -50,7 +50,7 @@ type CreateOpts struct { // ToBackupCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToBackupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "backup") } @@ -199,7 +199,7 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListDetailOptsBuilder) p // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToBackupUpdateMap() (map[string]interface{}, error) + ToBackupUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Backup. @@ -217,7 +217,7 @@ type UpdateOpts struct { // ToBackupUpdateMap assembles a request body based on the contents of // an UpdateOpts. -func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToBackupUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -250,7 +250,7 @@ type RestoreOpts struct { // ToRestoreMap assembles a request body based on the contents of a // RestoreOpts. -func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { +func (opts RestoreOpts) ToRestoreMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "restore") } @@ -284,7 +284,7 @@ type ImportOpts BackupRecord // ToBackupImportMap assembles a request body based on the contents of a // ImportOpts. -func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { +func (opts ImportOpts) ToBackupImportMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "backup-record") } @@ -307,7 +307,7 @@ func Import(ctx context.Context, client *gophercloud.ServiceClient, opts ImportO // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { - ToBackupResetStatusMap() (map[string]interface{}, error) + ToBackupResetStatusMap() (map[string]any, error) } // ResetStatusOpts contains options for resetting a Backup status. @@ -320,7 +320,7 @@ type ResetStatusOpts struct { // ToBackupResetStatusMap assembles a request body based on the contents of a // ResetStatusOpts. -func (opts ResetStatusOpts) ToBackupResetStatusMap() (map[string]interface{}, error) { +func (opts ResetStatusOpts) ToBackupResetStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-reset_status") } @@ -343,7 +343,7 @@ func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id stri // ForceDelete will delete the existing backup in any state. ForceDeleteResult contains only the error. // To extract it, call the ExtractErr method on the ForceDeleteResult. func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - b := map[string]interface{}{ + b := map[string]any{ "os-force_delete": struct{}{}, } resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ diff --git a/openstack/blockstorage/v2/backups/results.go b/openstack/blockstorage/v2/backups/results.go index 79c4901c09..77620de917 100644 --- a/openstack/blockstorage/v2/backups/results.go +++ b/openstack/blockstorage/v2/backups/results.go @@ -155,11 +155,11 @@ func (r commonResult) Extract() (*Backup, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "backup") } -func ExtractBackupsInto(r pagination.Page, v interface{}) error { +func ExtractBackupsInto(r pagination.Page, v any) error { return r.(BackupPage).Result.ExtractIntoSlicePtr(v, "backups") } @@ -188,7 +188,7 @@ func (r RestoreResult) Extract() (*Restore, error) { return &s, err } -func (r RestoreResult) ExtractInto(v interface{}) error { +func (r RestoreResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "restore") } @@ -213,7 +213,7 @@ func (r ExportResult) Extract() (*BackupRecord, error) { return &s, err } -func (r ExportResult) ExtractInto(v interface{}) error { +func (r ExportResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "backup-record") } @@ -235,7 +235,7 @@ func (r ImportResult) Extract() (*ImportResponse, error) { return &s, err } -func (r ImportResult) ExtractInto(v interface{}) error { +func (r ImportResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "backup") } @@ -265,7 +265,7 @@ type ImportBackup struct { Deleted bool `json:"deleted"` DisplayName *string `json:"display_name"` DisplayDescription *string `json:"display_description"` - DriverInfo interface{} `json:"driver_info"` + DriverInfo any `json:"driver_info"` FailReason *string `json:"fail_reason"` ProjectID string `json:"project_id"` Metadata map[string]string `json:"metadata"` diff --git a/openstack/blockstorage/v2/quotasets/doc.go b/openstack/blockstorage/v2/quotasets/doc.go index 29964f0a7b..f7b999c6f7 100644 --- a/openstack/blockstorage/v2/quotasets/doc.go +++ b/openstack/blockstorage/v2/quotasets/doc.go @@ -36,7 +36,7 @@ Example to Update a Quota set with volume_type quotas updateOpts := quotasets.UpdateOpts{ Volumes: gophercloud.IntToPointer(100), - Extra: map[string]interface{}{ + Extra: map[string]any{ "gigabytes_foo": gophercloud.IntToPointer(100), "snapshots_foo": gophercloud.IntToPointer(10), "volumes_foo": gophercloud.IntToPointer(10), diff --git a/openstack/blockstorage/v2/quotasets/requests.go b/openstack/blockstorage/v2/quotasets/requests.go index 9189f92aa5..f53015c9b2 100644 --- a/openstack/blockstorage/v2/quotasets/requests.go +++ b/openstack/blockstorage/v2/quotasets/requests.go @@ -48,19 +48,19 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, projectID st type UpdateOptsBuilder interface { // Extra specific name to prevent collisions with interfaces for other quotas // (e.g. neutron) - ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) + ToBlockStorageQuotaUpdateMap() (map[string]any, error) } // ToBlockStorageQuotaUpdateMap builds the update options into a serializable // format. -func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "quota_set") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["quota_set"].(map[string]interface{}); ok { + if v, ok := b["quota_set"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -104,7 +104,7 @@ type UpdateOpts struct { // Extra is a collection of miscellaneous key/values used to set // quota per volume_type - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // Resets the quotas for the given tenant to their default values. diff --git a/openstack/blockstorage/v2/quotasets/results.go b/openstack/blockstorage/v2/quotasets/results.go index 0ee49821ce..e75aea5b51 100644 --- a/openstack/blockstorage/v2/quotasets/results.go +++ b/openstack/blockstorage/v2/quotasets/results.go @@ -39,7 +39,7 @@ type QuotaSet struct { // Extra is a collection of miscellaneous key/values used to set // quota per volume_type - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // UnmarshalJSON is used on QuotaSet to unmarshal extra keys that are @@ -48,7 +48,7 @@ func (r *QuotaSet) UnmarshalJSON(b []byte) error { type tmp QuotaSet var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -56,12 +56,12 @@ func (r *QuotaSet) UnmarshalJSON(b []byte) error { } *r = QuotaSet(s.tmp) - var result interface{} + var result any err = json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(QuotaSet{}, resultMap) } diff --git a/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go b/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go index 9be8ffe90c..066e1729c8 100644 --- a/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go @@ -34,7 +34,7 @@ var getExpectedQuotaSet = quotasets.QuotaSet{ Backups: 12, BackupGigabytes: 13, Groups: 14, - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } var getUsageExpectedJSONBody = ` @@ -111,7 +111,7 @@ var fullUpdateOpts = quotasets.UpdateOpts{ Backups: gophercloud.IntToPointer(12), BackupGigabytes: gophercloud.IntToPointer(13), Groups: gophercloud.IntToPointer(14), - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ @@ -122,7 +122,7 @@ var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ Backups: 12, BackupGigabytes: 13, Groups: 14, - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } var partialUpdateExpectedJSONBody = ` @@ -144,12 +144,12 @@ var partialUpdateOpts = quotasets.UpdateOpts{ PerVolumeGigabytes: gophercloud.IntToPointer(0), Backups: gophercloud.IntToPointer(0), BackupGigabytes: gophercloud.IntToPointer(0), - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{ Volumes: 200, - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } // HandleSuccessfulRequest configures the test server to respond to an HTTP request. diff --git a/openstack/blockstorage/v2/quotasets/testing/requests_test.go b/openstack/blockstorage/v2/quotasets/testing/requests_test.go index 3a678b5833..563b2d1d3d 100644 --- a/openstack/blockstorage/v2/quotasets/testing/requests_test.go +++ b/openstack/blockstorage/v2/quotasets/testing/requests_test.go @@ -56,7 +56,7 @@ func TestPartialUpdate(t *testing.T) { type ErrorUpdateOpts quotasets.UpdateOpts -func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { +func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]any, error) { return nil, errors.New("This is an error") } diff --git a/openstack/blockstorage/v2/schedulerstats/results.go b/openstack/blockstorage/v2/schedulerstats/results.go index 8c18b261ba..fe43d0522b 100644 --- a/openstack/blockstorage/v2/schedulerstats/results.go +++ b/openstack/blockstorage/v2/schedulerstats/results.go @@ -46,10 +46,10 @@ 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"` + AllocatedCapacityGB any `json:"allocated_capacity_gb"` + FreeCapacityGB any `json:"free_capacity_gb"` + MaxOverSubscriptionRatio any `json:"max_over_subscription_ratio"` + TotalCapacityGB any `json:"total_capacity_gb"` } err := json.Unmarshal(b, &s) if err != nil { @@ -59,7 +59,7 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { // Generic function to parse a capacity value which may be a numeric // value, "unknown", or "infinite" - parseCapacity := func(capacity interface{}) float64 { + parseCapacity := func(capacity any) float64 { if capacity != nil { switch c := capacity.(type) { case float64: diff --git a/openstack/blockstorage/v2/snapshots/requests.go b/openstack/blockstorage/v2/snapshots/requests.go index ccf39043f0..e55b70abaf 100644 --- a/openstack/blockstorage/v2/snapshots/requests.go +++ b/openstack/blockstorage/v2/snapshots/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSnapshotCreateMap() (map[string]interface{}, error) + ToSnapshotCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a Snapshot. This object is passed to @@ -26,7 +26,7 @@ type CreateOpts struct { // ToSnapshotCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSnapshotCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } @@ -112,19 +112,19 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMetadataOptsBuilder interface { - ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) + ToSnapshotUpdateMetadataMap() (map[string]any, error) } // UpdateMetadataOpts contain options for updating an existing Snapshot. This // object is passed to the snapshots.Update function. For more information // about the parameters, see the Snapshot object. type UpdateMetadataOpts struct { - Metadata map[string]interface{} `json:"metadata,omitempty"` + Metadata map[string]any `json:"metadata,omitempty"` } // ToSnapshotUpdateMetadataMap assembles a request body based on the contents of // an UpdateMetadataOpts. -func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { +func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/blockstorage/v2/snapshots/results.go b/openstack/blockstorage/v2/snapshots/results.go index 7412ff3b3a..565b558c30 100644 --- a/openstack/blockstorage/v2/snapshots/results.go +++ b/openstack/blockstorage/v2/snapshots/results.go @@ -102,12 +102,12 @@ type UpdateMetadataResult struct { } // ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. -func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { +func (r UpdateMetadataResult) ExtractMetadata() (map[string]any, error) { if r.Err != nil { return nil, r.Err } - m := r.Body.(map[string]interface{})["metadata"] - return m.(map[string]interface{}), nil + m := r.Body.(map[string]any)["metadata"] + return m.(map[string]any), nil } type commonResult struct { diff --git a/openstack/blockstorage/v2/snapshots/testing/requests_test.go b/openstack/blockstorage/v2/snapshots/testing/requests_test.go index c44bda3876..07d74a14a7 100644 --- a/openstack/blockstorage/v2/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v2/snapshots/testing/requests_test.go @@ -93,10 +93,10 @@ func TestUpdateMetadata(t *testing.T) { MockUpdateMetadataResponse(t) - expected := map[string]interface{}{"key": "v1"} + expected := map[string]any{"key": "v1"} options := &snapshots.UpdateMetadataOpts{ - Metadata: map[string]interface{}{ + Metadata: map[string]any{ "key": "v1", }, } diff --git a/openstack/blockstorage/v2/transfers/requests.go b/openstack/blockstorage/v2/transfers/requests.go index f32a750bfa..9c762cfe1e 100644 --- a/openstack/blockstorage/v2/transfers/requests.go +++ b/openstack/blockstorage/v2/transfers/requests.go @@ -18,7 +18,7 @@ type CreateOpts struct { // ToCreateMap assembles a request body based on the contents of a // TransferOpts. -func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "transfer") } @@ -44,7 +44,7 @@ type AcceptOpts struct { // ToAcceptMap assembles a request body based on the contents of a // AcceptOpts. -func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { +func (opts AcceptOpts) ToAcceptMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "accept") } diff --git a/openstack/blockstorage/v2/transfers/results.go b/openstack/blockstorage/v2/transfers/results.go index f5e934efea..42885c27ef 100644 --- a/openstack/blockstorage/v2/transfers/results.go +++ b/openstack/blockstorage/v2/transfers/results.go @@ -48,7 +48,7 @@ func (r commonResult) Extract() (*Transfer, error) { } // ExtractInto converts our response data into a transfer struct -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "transfer") } @@ -75,7 +75,7 @@ func ExtractTransfers(r pagination.Page) ([]Transfer, error) { } // ExtractTransfersInto similar to ExtractInto but operates on a `list` of transfers -func ExtractTransfersInto(r pagination.Page, v interface{}) error { +func ExtractTransfersInto(r pagination.Page, v any) error { return r.(TransferPage).Result.ExtractIntoSlicePtr(v, "transfers") } diff --git a/openstack/blockstorage/v2/volumes/requests.go b/openstack/blockstorage/v2/volumes/requests.go index 8f60c6e281..9d1e797fc9 100644 --- a/openstack/blockstorage/v2/volumes/requests.go +++ b/openstack/blockstorage/v2/volumes/requests.go @@ -11,7 +11,7 @@ import ( // SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. type SchedulerHintOptsBuilder interface { - ToSchedulerHintsMap() (map[string]interface{}, error) + ToSchedulerHintsMap() (map[string]any, error) } // SchedulerHintOpts contains options for providing scheduler hints @@ -33,12 +33,12 @@ type SchedulerHintOpts struct { Query string // AdditionalProperies are arbitrary key/values that are not validated by nova. - AdditionalProperties map[string]interface{} + AdditionalProperties map[string]any } // ToSchedulerHintsMap assembles a request body for scheduler hints -func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, error) { - sh := make(map[string]interface{}) +func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]any, error) { + sh := make(map[string]any) uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") @@ -93,13 +93,13 @@ func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, err return sh, nil } - return map[string]interface{}{"OS-SCH-HNT:scheduler_hints": sh}, nil + return map[string]any{"OS-SCH-HNT:scheduler_hints": sh}, nil } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToVolumeCreateMap() (map[string]interface{}, error) + ToVolumeCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a Volume. This object is passed to @@ -133,7 +133,7 @@ type CreateOpts struct { // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToVolumeCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "volume") } @@ -270,7 +270,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToVolumeUpdateMap() (map[string]interface{}, error) + ToVolumeUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Volume. This object is passed @@ -284,7 +284,7 @@ type UpdateOpts struct { // ToVolumeUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "volume") } @@ -306,7 +306,7 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o // AttachOptsBuilder allows extensions to add additional parameters to the // Attach request. type AttachOptsBuilder interface { - ToVolumeAttachMap() (map[string]interface{}, error) + ToVolumeAttachMap() (map[string]any, error) } // AttachMode describes the attachment mode for volumes. @@ -335,7 +335,7 @@ type AttachOpts struct { // ToVolumeAttachMap assembles a request body based on the contents of a // AttachOpts. -func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { +func (opts AttachOpts) ToVolumeAttachMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-attach") } @@ -355,7 +355,7 @@ func Attach(ctx context.Context, client *gophercloud.ServiceClient, id string, o // BeginDetaching will mark the volume as detaching. func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { - b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} + b := map[string]any{"os-begin_detaching": make(map[string]any)} resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) @@ -366,7 +366,7 @@ func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id s // DetachOptsBuilder allows extensions to add additional parameters to the // Detach request. type DetachOptsBuilder interface { - ToVolumeDetachMap() (map[string]interface{}, error) + ToVolumeDetachMap() (map[string]any, error) } // DetachOpts contains options for detaching a Volume. @@ -377,7 +377,7 @@ type DetachOpts struct { // ToVolumeDetachMap assembles a request body based on the contents of a // DetachOpts. -func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { +func (opts DetachOpts) ToVolumeDetachMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-detach") } @@ -397,7 +397,7 @@ func Detach(ctx context.Context, client *gophercloud.ServiceClient, id string, o // Reserve will reserve a volume based on volume ID. func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ReserveResult) { - b := map[string]interface{}{"os-reserve": make(map[string]interface{})} + b := map[string]any{"os-reserve": make(map[string]any)} resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) @@ -407,7 +407,7 @@ func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) // Unreserve will unreserve a volume based on volume ID. func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnreserveResult) { - b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} + b := map[string]any{"os-unreserve": make(map[string]any)} resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) @@ -418,7 +418,7 @@ func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string // InitializeConnectionOptsBuilder allows extensions to add additional parameters to the // InitializeConnection request. type InitializeConnectionOptsBuilder interface { - ToVolumeInitializeConnectionMap() (map[string]interface{}, error) + ToVolumeInitializeConnectionMap() (map[string]any, error) } // InitializeConnectionOpts hosts options for InitializeConnection. @@ -437,9 +437,9 @@ type InitializeConnectionOpts struct { // ToVolumeInitializeConnectionMap assembles a request body based on the contents of a // InitializeConnectionOpts. -func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { +func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-initialize_connection": b}, err + return map[string]any{"os-initialize_connection": b}, err } // InitializeConnection initializes an iSCSI connection by volume ID. @@ -459,7 +459,7 @@ func InitializeConnection(ctx context.Context, client *gophercloud.ServiceClient // TerminateConnectionOptsBuilder allows extensions to add additional parameters to the // TerminateConnection request. type TerminateConnectionOptsBuilder interface { - ToVolumeTerminateConnectionMap() (map[string]interface{}, error) + ToVolumeTerminateConnectionMap() (map[string]any, error) } // TerminateConnectionOpts hosts options for TerminateConnection. @@ -476,9 +476,9 @@ type TerminateConnectionOpts struct { // ToVolumeTerminateConnectionMap assembles a request body based on the contents of a // TerminateConnectionOpts. -func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { +func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-terminate_connection": b}, err + return map[string]any{"os-terminate_connection": b}, err } // TerminateConnection terminates an iSCSI connection by volume ID. @@ -498,7 +498,7 @@ func TerminateConnection(ctx context.Context, client *gophercloud.ServiceClient, // ExtendSizeOptsBuilder allows extensions to add additional parameters to the // ExtendSize request. type ExtendSizeOptsBuilder interface { - ToVolumeExtendSizeMap() (map[string]interface{}, error) + ToVolumeExtendSizeMap() (map[string]any, error) } // ExtendSizeOpts contains options for extending the size of an existing Volume. @@ -510,7 +510,7 @@ type ExtendSizeOpts struct { // ToVolumeExtendSizeMap assembles a request body based on the contents of an // ExtendSizeOpts. -func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { +func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-extend") } @@ -532,7 +532,7 @@ func ExtendSize(ctx context.Context, client *gophercloud.ServiceClient, id strin // UploadImageOptsBuilder allows extensions to add additional parameters to the // UploadImage request. type UploadImageOptsBuilder interface { - ToVolumeUploadImageMap() (map[string]interface{}, error) + ToVolumeUploadImageMap() (map[string]any, error) } // UploadImageOpts contains options for uploading a Volume to image storage. @@ -560,7 +560,7 @@ type UploadImageOpts struct { // ToVolumeUploadImageMap assembles a request body based on the contents of a // UploadImageOpts. -func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { +func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") } @@ -580,7 +580,7 @@ func UploadImage(ctx context.Context, client *gophercloud.ServiceClient, id stri // ForceDelete will delete the volume regardless of state. func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-force_delete": ""}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -588,7 +588,7 @@ func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id stri // ImageMetadataOptsBuilder allows extensions to add additional parameters to the // ImageMetadataRequest request. type ImageMetadataOptsBuilder interface { - ToImageMetadataMap() (map[string]interface{}, error) + ToImageMetadataMap() (map[string]any, error) } // ImageMetadataOpts contains options for setting image metadata to a volume. @@ -599,7 +599,7 @@ type ImageMetadataOpts struct { // ToImageMetadataMap assembles a request body based on the contents of a // ImageMetadataOpts. -func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { +func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") } @@ -625,7 +625,7 @@ type BootableOpts struct { // ToBootableMap assembles a request body based on the contents of a // BootableOpts. -func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { +func (opts BootableOpts) ToBootableMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-set_bootable") } @@ -655,7 +655,7 @@ const ( // ChangeTypeOptsBuilder allows extensions to add additional parameters to the // ChangeType request. type ChangeTypeOptsBuilder interface { - ToVolumeChangeTypeMap() (map[string]interface{}, error) + ToVolumeChangeTypeMap() (map[string]any, error) } // ChangeTypeOpts contains options for changing the type of an existing Volume. @@ -672,7 +672,7 @@ type ChangeTypeOpts struct { // ToVolumeChangeTypeMap assembles a request body based on the contents of an // ChangeTypeOpts. -func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { +func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-retype") } @@ -700,7 +700,7 @@ type ReImageOpts struct { } // ToReImageMap assembles a request body based on the contents of a ReImageOpts. -func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { +func (opts ReImageOpts) ToReImageMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-reimage") } @@ -721,7 +721,7 @@ func ReImage(ctx context.Context, client *gophercloud.ServiceClient, id string, // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { - ToResetStatusMap() (map[string]interface{}, error) + ToResetStatusMap() (map[string]any, error) } // ResetStatusOpts contains options for resetting a Volume status. @@ -738,7 +738,7 @@ type ResetStatusOpts struct { // ToResetStatusMap assembles a request body based on the contents of a // ResetStatusOpts. -func (opts ResetStatusOpts) ToResetStatusMap() (map[string]interface{}, error) { +func (opts ResetStatusOpts) ToResetStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-reset_status") } diff --git a/openstack/blockstorage/v2/volumes/results.go b/openstack/blockstorage/v2/volumes/results.go index ed21e14e70..061309e991 100644 --- a/openstack/blockstorage/v2/volumes/results.go +++ b/openstack/blockstorage/v2/volumes/results.go @@ -146,11 +146,11 @@ func (r commonResult) Extract() (*Volume, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "volume") } -func ExtractVolumesInto(r pagination.Page, v interface{}) error { +func ExtractVolumesInto(r pagination.Page, v any) error { return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") } @@ -239,11 +239,11 @@ type ExtendSizeResult struct { // Extract will get the connection information out of the // InitializeConnectionResult object. // -// This will be a generic map[string]interface{} and the results will be +// This will be a generic map[string]any and the results will be // dependent on the type of connection made. -func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { +func (r InitializeConnectionResult) Extract() (map[string]any, error) { var s struct { - ConnectionInfo map[string]interface{} `json:"connection_info"` + ConnectionInfo map[string]any `json:"connection_info"` } err := r.ExtractInto(&s) return s.ConnectionInfo, err @@ -265,7 +265,7 @@ type ImageVolumeType struct { IsPublic bool `json:"is_public"` // Extra specifications for volume type. - ExtraSpecs map[string]interface{} `json:"extra_specs"` + ExtraSpecs map[string]any `json:"extra_specs"` // ID of quality of service specs. QosSpecsID string `json:"qos_specs_id"` diff --git a/openstack/blockstorage/v2/volumes/testing/requests_test.go b/openstack/blockstorage/v2/volumes/testing/requests_test.go index 7e36a22339..3d829a56ae 100644 --- a/openstack/blockstorage/v2/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v2/volumes/testing/requests_test.go @@ -222,7 +222,7 @@ func TestCreateSchedulerHints(t *testing.T) { "8c19174f-4220-44f0-824a-cd1eeef10287", }, LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", - AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + AdditionalProperties: map[string]any{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } expected := ` { @@ -349,7 +349,7 @@ func TestUploadImage(t *testing.T) { Name: "basic.ru-2a", Description: "", IsPublic: true, - ExtraSpecs: map[string]interface{}{"volume_backend_name": "basic.ru-2a"}, + ExtraSpecs: map[string]any{"volume_backend_name": "basic.ru-2a"}, QosSpecsID: "", Deleted: false, DeletedAt: time.Time{}, diff --git a/openstack/blockstorage/v3/attachments/doc.go b/openstack/blockstorage/v3/attachments/doc.go index c2d58cea43..c2e6f29ff2 100644 --- a/openstack/blockstorage/v3/attachments/doc.go +++ b/openstack/blockstorage/v3/attachments/doc.go @@ -54,7 +54,7 @@ Example to Get Attachment Example to Update Attachment opts := &attachments.UpdateOpts{ - Connector: map[string]interface{}{ + Connector: map[string]any{ "mode": "ro", } } diff --git a/openstack/blockstorage/v3/attachments/requests.go b/openstack/blockstorage/v3/attachments/requests.go index 0d8cb7d716..59b4b09c67 100644 --- a/openstack/blockstorage/v3/attachments/requests.go +++ b/openstack/blockstorage/v3/attachments/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToAttachmentCreateMap() (map[string]interface{}, error) + ToAttachmentCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a Volume attachment. This object is @@ -26,7 +26,7 @@ type CreateOpts struct { InstanceUUID string `json:"instance_uuid"` // Connector is an optional map containing all of the needed atachment // information for exmaple initiator IQN, etc. - Connector map[string]interface{} `json:"connector,omitempty"` + Connector map[string]any `json:"connector,omitempty"` // Mode is an attachment mode. Acceptable values are read-only ('ro') // and read-and-write ('rw'). Available only since 3.54 microversion. // For APIs from 3.27 till 3.53 use Connector["mode"] = "rw|ro". @@ -35,7 +35,7 @@ type CreateOpts struct { // ToAttachmentCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToAttachmentCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToAttachmentCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "attachment") } @@ -136,19 +136,19 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToAttachmentUpdateMap() (map[string]interface{}, error) + ToAttachmentUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Attachment. // This is used to finalize an attachment that was created without a // connector (reserve). type UpdateOpts struct { - Connector map[string]interface{} `json:"connector"` + Connector map[string]any `json:"connector"` } // ToAttachmentUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToAttachmentUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToAttachmentUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "attachment") } @@ -171,7 +171,7 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o // Complete will complete an attachment for a cinder volume. // Available starting in the 3.44 microversion. func Complete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r CompleteResult) { - b := map[string]interface{}{ + b := map[string]any{ "os-complete": nil, } resp, err := client.Post(ctx, completeURL(client, id), b, nil, &gophercloud.RequestOpts{ diff --git a/openstack/blockstorage/v3/attachments/results.go b/openstack/blockstorage/v3/attachments/results.go index 04dfa54031..8c572047ae 100644 --- a/openstack/blockstorage/v3/attachments/results.go +++ b/openstack/blockstorage/v3/attachments/results.go @@ -29,7 +29,7 @@ type Attachment struct { AttachMode string `json:"attach_mode"` // ConnectionInfo is the required info for a node to make a connection // provided by the driver. - ConnectionInfo map[string]interface{} `json:"connection_info"` + ConnectionInfo map[string]any `json:"connection_info"` } // UnmarshalJSON is our unmarshalling helper @@ -88,13 +88,13 @@ func (r commonResult) Extract() (*Attachment, error) { } // ExtractInto converts our response data into a attachment struct. -func (r commonResult) ExtractInto(a interface{}) error { +func (r commonResult) ExtractInto(a any) error { return r.Result.ExtractIntoStructPtr(a, "attachment") } // ExtractAttachmentsInto similar to ExtractInto but operates on a List of // attachments. -func ExtractAttachmentsInto(r pagination.Page, a interface{}) error { +func ExtractAttachmentsInto(r pagination.Page, a any) error { return r.(AttachmentPage).Result.ExtractIntoSlicePtr(a, "attachments") } diff --git a/openstack/blockstorage/v3/attachments/testing/fixtures_test.go b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go index 22d66ddea8..d489ce16ef 100644 --- a/openstack/blockstorage/v3/attachments/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go @@ -23,7 +23,7 @@ var ( Status: "attaching", AttachedAt: attachedAt, DetachedAt: detachedAt, - ConnectionInfo: map[string]interface{}{}, + ConnectionInfo: map[string]any{}, } ) diff --git a/openstack/blockstorage/v3/attachments/testing/requests_test.go b/openstack/blockstorage/v3/attachments/testing/requests_test.go index 9c7e399add..4f9f91dcb4 100644 --- a/openstack/blockstorage/v3/attachments/testing/requests_test.go +++ b/openstack/blockstorage/v3/attachments/testing/requests_test.go @@ -46,7 +46,7 @@ func TestCreate(t *testing.T) { options := &attachments.CreateOpts{ InstanceUUID: "83ec2e3b-4321-422b-8706-a84185f52a0a", - Connector: map[string]interface{}{ + Connector: map[string]any{ "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", "ip": "192.168.1.20", "platform": "x86_64", @@ -81,7 +81,7 @@ func TestUpdate(t *testing.T) { MockUpdateResponse(t) options := &attachments.UpdateOpts{ - Connector: map[string]interface{}{ + Connector: map[string]any{ "initiator": "iqn.1993-08.org.debian: 01: cad181614cec", "ip": "192.168.1.20", "platform": "x86_64", diff --git a/openstack/blockstorage/v3/backups/requests.go b/openstack/blockstorage/v3/backups/requests.go index 57f5f55ee3..a1f3cc8eef 100644 --- a/openstack/blockstorage/v3/backups/requests.go +++ b/openstack/blockstorage/v3/backups/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToBackupCreateMap() (map[string]interface{}, error) + ToBackupCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a Backup. This object is passed to @@ -50,7 +50,7 @@ type CreateOpts struct { // ToBackupCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToBackupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "backup") } @@ -199,7 +199,7 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListDetailOptsBuilder) p // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToBackupUpdateMap() (map[string]interface{}, error) + ToBackupUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Backup. @@ -217,7 +217,7 @@ type UpdateOpts struct { // ToBackupUpdateMap assembles a request body based on the contents of // an UpdateOpts. -func (opts UpdateOpts) ToBackupUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToBackupUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -250,7 +250,7 @@ type RestoreOpts struct { // ToRestoreMap assembles a request body based on the contents of a // RestoreOpts. -func (opts RestoreOpts) ToRestoreMap() (map[string]interface{}, error) { +func (opts RestoreOpts) ToRestoreMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "restore") } @@ -284,7 +284,7 @@ type ImportOpts BackupRecord // ToBackupImportMap assembles a request body based on the contents of a // ImportOpts. -func (opts ImportOpts) ToBackupImportMap() (map[string]interface{}, error) { +func (opts ImportOpts) ToBackupImportMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "backup-record") } @@ -307,7 +307,7 @@ func Import(ctx context.Context, client *gophercloud.ServiceClient, opts ImportO // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { - ToBackupResetStatusMap() (map[string]interface{}, error) + ToBackupResetStatusMap() (map[string]any, error) } // ResetStatusOpts contains options for resetting a Backup status. @@ -320,7 +320,7 @@ type ResetStatusOpts struct { // ToBackupResetStatusMap assembles a request body based on the contents of a // ResetStatusOpts. -func (opts ResetStatusOpts) ToBackupResetStatusMap() (map[string]interface{}, error) { +func (opts ResetStatusOpts) ToBackupResetStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-reset_status") } @@ -343,7 +343,7 @@ func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id stri // ForceDelete will delete the existing backup in any state. ForceDeleteResult contains only the error. // To extract it, call the ExtractErr method on the ForceDeleteResult. func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - b := map[string]interface{}{ + b := map[string]any{ "os-force_delete": struct{}{}, } resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ diff --git a/openstack/blockstorage/v3/backups/results.go b/openstack/blockstorage/v3/backups/results.go index 79c4901c09..77620de917 100644 --- a/openstack/blockstorage/v3/backups/results.go +++ b/openstack/blockstorage/v3/backups/results.go @@ -155,11 +155,11 @@ func (r commonResult) Extract() (*Backup, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "backup") } -func ExtractBackupsInto(r pagination.Page, v interface{}) error { +func ExtractBackupsInto(r pagination.Page, v any) error { return r.(BackupPage).Result.ExtractIntoSlicePtr(v, "backups") } @@ -188,7 +188,7 @@ func (r RestoreResult) Extract() (*Restore, error) { return &s, err } -func (r RestoreResult) ExtractInto(v interface{}) error { +func (r RestoreResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "restore") } @@ -213,7 +213,7 @@ func (r ExportResult) Extract() (*BackupRecord, error) { return &s, err } -func (r ExportResult) ExtractInto(v interface{}) error { +func (r ExportResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "backup-record") } @@ -235,7 +235,7 @@ func (r ImportResult) Extract() (*ImportResponse, error) { return &s, err } -func (r ImportResult) ExtractInto(v interface{}) error { +func (r ImportResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "backup") } @@ -265,7 +265,7 @@ type ImportBackup struct { Deleted bool `json:"deleted"` DisplayName *string `json:"display_name"` DisplayDescription *string `json:"display_description"` - DriverInfo interface{} `json:"driver_info"` + DriverInfo any `json:"driver_info"` FailReason *string `json:"fail_reason"` ProjectID string `json:"project_id"` Metadata map[string]string `json:"metadata"` diff --git a/openstack/blockstorage/v3/qos/requests.go b/openstack/blockstorage/v3/qos/requests.go index 7ffacd3a6f..1b39ef6544 100644 --- a/openstack/blockstorage/v3/qos/requests.go +++ b/openstack/blockstorage/v3/qos/requests.go @@ -8,7 +8,7 @@ import ( ) type CreateOptsBuilder interface { - ToQoSCreateMap() (map[string]interface{}, error) + ToQoSCreateMap() (map[string]any, error) } // ListOptsBuilder allows extensions to add additional parameters to the @@ -40,14 +40,14 @@ type CreateOpts struct { // ToQoSCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToQoSCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToQoSCreateMap() (map[string]any, 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 { + if v, ok := b["qos_specs"].(map[string]any); ok { for key, value := range opts.Specs { v[key] = value } @@ -159,7 +159,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateQosSpecsOptsBuilder allows extensions to add additional parameters to the // CreateQosSpecs requests. type CreateQosSpecsOptsBuilder interface { - ToQosSpecsCreateMap() (map[string]interface{}, error) + ToQosSpecsCreateMap() (map[string]any, error) } // UpdateOpts contains options for creating a QoS specification. @@ -174,19 +174,19 @@ type UpdateOpts struct { } type UpdateOptsBuilder interface { - ToQoSUpdateMap() (map[string]interface{}, error) + ToQoSUpdateMap() (map[string]any, error) } // ToQoSUpdateMap assembles a request body based on the contents of a // UpdateOpts. -func (opts UpdateOpts) ToQoSUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToQoSUpdateMap() (map[string]any, 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 { + if v, ok := b["qos_specs"].(map[string]any); ok { for key, value := range opts.Specs { v[key] = value } @@ -215,7 +215,7 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o // DeleteKeysOptsBuilder allows extensions to add additional parameters to the // CreateExtraSpecs requests. type DeleteKeysOptsBuilder interface { - ToDeleteKeysCreateMap() (map[string]interface{}, error) + ToDeleteKeysCreateMap() (map[string]any, error) } // DeleteKeysOpts is a string slice that contains keys to be deleted. @@ -223,8 +223,8 @@ 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 +func (opts DeleteKeysOpts) ToDeleteKeysCreateMap() (map[string]any, error) { + return map[string]any{"keys": opts}, nil } // DeleteKeys will delete the keys/specs from the specified QoS diff --git a/openstack/blockstorage/v3/qos/results.go b/openstack/blockstorage/v3/qos/results.go index 9efa30cc2e..314bf878ca 100644 --- a/openstack/blockstorage/v3/qos/results.go +++ b/openstack/blockstorage/v3/qos/results.go @@ -29,7 +29,7 @@ func (r commonResult) Extract() (*QoS, error) { } // ExtractInto converts our response data into a QoS struct -func (r commonResult) ExtractInto(qos interface{}) error { +func (r commonResult) ExtractInto(qos any) error { return r.Result.ExtractIntoStructPtr(qos, "qos_specs") } diff --git a/openstack/blockstorage/v3/quotasets/doc.go b/openstack/blockstorage/v3/quotasets/doc.go index 29964f0a7b..f7b999c6f7 100644 --- a/openstack/blockstorage/v3/quotasets/doc.go +++ b/openstack/blockstorage/v3/quotasets/doc.go @@ -36,7 +36,7 @@ Example to Update a Quota set with volume_type quotas updateOpts := quotasets.UpdateOpts{ Volumes: gophercloud.IntToPointer(100), - Extra: map[string]interface{}{ + Extra: map[string]any{ "gigabytes_foo": gophercloud.IntToPointer(100), "snapshots_foo": gophercloud.IntToPointer(10), "volumes_foo": gophercloud.IntToPointer(10), diff --git a/openstack/blockstorage/v3/quotasets/requests.go b/openstack/blockstorage/v3/quotasets/requests.go index 9189f92aa5..f53015c9b2 100644 --- a/openstack/blockstorage/v3/quotasets/requests.go +++ b/openstack/blockstorage/v3/quotasets/requests.go @@ -48,19 +48,19 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, projectID st type UpdateOptsBuilder interface { // Extra specific name to prevent collisions with interfaces for other quotas // (e.g. neutron) - ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) + ToBlockStorageQuotaUpdateMap() (map[string]any, error) } // ToBlockStorageQuotaUpdateMap builds the update options into a serializable // format. -func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "quota_set") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["quota_set"].(map[string]interface{}); ok { + if v, ok := b["quota_set"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -104,7 +104,7 @@ type UpdateOpts struct { // Extra is a collection of miscellaneous key/values used to set // quota per volume_type - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // Resets the quotas for the given tenant to their default values. diff --git a/openstack/blockstorage/v3/quotasets/results.go b/openstack/blockstorage/v3/quotasets/results.go index 0ee49821ce..e75aea5b51 100644 --- a/openstack/blockstorage/v3/quotasets/results.go +++ b/openstack/blockstorage/v3/quotasets/results.go @@ -39,7 +39,7 @@ type QuotaSet struct { // Extra is a collection of miscellaneous key/values used to set // quota per volume_type - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // UnmarshalJSON is used on QuotaSet to unmarshal extra keys that are @@ -48,7 +48,7 @@ func (r *QuotaSet) UnmarshalJSON(b []byte) error { type tmp QuotaSet var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -56,12 +56,12 @@ func (r *QuotaSet) UnmarshalJSON(b []byte) error { } *r = QuotaSet(s.tmp) - var result interface{} + var result any err = json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(QuotaSet{}, resultMap) } diff --git a/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go b/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go index e996e6fda6..df029ff8e1 100644 --- a/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go @@ -34,7 +34,7 @@ var getExpectedQuotaSet = quotasets.QuotaSet{ Backups: 12, BackupGigabytes: 13, Groups: 14, - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } var getUsageExpectedJSONBody = ` @@ -111,7 +111,7 @@ var fullUpdateOpts = quotasets.UpdateOpts{ Backups: gophercloud.IntToPointer(12), BackupGigabytes: gophercloud.IntToPointer(13), Groups: gophercloud.IntToPointer(14), - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ @@ -122,7 +122,7 @@ var fullUpdateExpectedQuotaSet = quotasets.QuotaSet{ Backups: 12, BackupGigabytes: 13, Groups: 14, - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } var partialUpdateExpectedJSONBody = ` @@ -144,12 +144,12 @@ var partialUpdateOpts = quotasets.UpdateOpts{ PerVolumeGigabytes: gophercloud.IntToPointer(0), Backups: gophercloud.IntToPointer(0), BackupGigabytes: gophercloud.IntToPointer(0), - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } var partiualUpdateExpectedQuotaSet = quotasets.QuotaSet{ Volumes: 200, - Extra: make(map[string]interface{}), + Extra: make(map[string]any), } // HandleSuccessfulRequest configures the test server to respond to an HTTP request. diff --git a/openstack/blockstorage/v3/quotasets/testing/requests_test.go b/openstack/blockstorage/v3/quotasets/testing/requests_test.go index 266db833cb..ca17ef10b4 100644 --- a/openstack/blockstorage/v3/quotasets/testing/requests_test.go +++ b/openstack/blockstorage/v3/quotasets/testing/requests_test.go @@ -56,7 +56,7 @@ func TestPartialUpdate(t *testing.T) { type ErrorUpdateOpts quotasets.UpdateOpts -func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]interface{}, error) { +func (opts ErrorUpdateOpts) ToBlockStorageQuotaUpdateMap() (map[string]any, error) { return nil, errors.New("This is an error") } diff --git a/openstack/blockstorage/v3/schedulerstats/results.go b/openstack/blockstorage/v3/schedulerstats/results.go index 8c18b261ba..fe43d0522b 100644 --- a/openstack/blockstorage/v3/schedulerstats/results.go +++ b/openstack/blockstorage/v3/schedulerstats/results.go @@ -46,10 +46,10 @@ 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"` + AllocatedCapacityGB any `json:"allocated_capacity_gb"` + FreeCapacityGB any `json:"free_capacity_gb"` + MaxOverSubscriptionRatio any `json:"max_over_subscription_ratio"` + TotalCapacityGB any `json:"total_capacity_gb"` } err := json.Unmarshal(b, &s) if err != nil { @@ -59,7 +59,7 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { // Generic function to parse a capacity value which may be a numeric // value, "unknown", or "infinite" - parseCapacity := func(capacity interface{}) float64 { + parseCapacity := func(capacity any) float64 { if capacity != nil { switch c := capacity.(type) { case float64: diff --git a/openstack/blockstorage/v3/snapshots/requests.go b/openstack/blockstorage/v3/snapshots/requests.go index 4971ae2fab..644e9cb1d3 100644 --- a/openstack/blockstorage/v3/snapshots/requests.go +++ b/openstack/blockstorage/v3/snapshots/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSnapshotCreateMap() (map[string]interface{}, error) + ToSnapshotCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a Snapshot. This object is passed to @@ -26,7 +26,7 @@ type CreateOpts struct { // ToSnapshotCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSnapshotCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } @@ -125,7 +125,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToSnapshotUpdateMap() (map[string]interface{}, error) + ToSnapshotUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Snapshot. This object is passed @@ -138,7 +138,7 @@ type UpdateOpts struct { // ToSnapshotUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } @@ -160,19 +160,19 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMetadataOptsBuilder interface { - ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) + ToSnapshotUpdateMetadataMap() (map[string]any, error) } // UpdateMetadataOpts contain options for updating an existing Snapshot. This // object is passed to the snapshots.Update function. For more information // about the parameters, see the Snapshot object. type UpdateMetadataOpts struct { - Metadata map[string]interface{} `json:"metadata,omitempty"` + Metadata map[string]any `json:"metadata,omitempty"` } // ToSnapshotUpdateMetadataMap assembles a request body based on the contents of // an UpdateMetadataOpts. -func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]interface{}, error) { +func (opts UpdateMetadataOpts) ToSnapshotUpdateMetadataMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -195,7 +195,7 @@ func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id s // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { - ToSnapshotResetStatusMap() (map[string]interface{}, error) + ToSnapshotResetStatusMap() (map[string]any, error) } // ResetStatusOpts contains options for resetting a Snapshot status. @@ -208,7 +208,7 @@ type ResetStatusOpts struct { // ToSnapshotResetStatusMap assembles a request body based on the contents of a // ResetStatusOpts. -func (opts ResetStatusOpts) ToSnapshotResetStatusMap() (map[string]interface{}, error) { +func (opts ResetStatusOpts) ToSnapshotResetStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-reset_status") } @@ -231,7 +231,7 @@ func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id stri // UpdateStatusOptsBuilder allows extensions to add additional parameters to the // UpdateStatus request. type UpdateStatusOptsBuilder interface { - ToSnapshotUpdateStatusMap() (map[string]interface{}, error) + ToSnapshotUpdateStatusMap() (map[string]any, error) } // UpdateStatusOpts contains options for resetting a Snapshot status. @@ -246,7 +246,7 @@ type UpdateStatusOpts struct { // ToSnapshotUpdateStatusMap assembles a request body based on the contents of a // UpdateStatusOpts. -func (opts UpdateStatusOpts) ToSnapshotUpdateStatusMap() (map[string]interface{}, error) { +func (opts UpdateStatusOpts) ToSnapshotUpdateStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-update_snapshot_status") } @@ -269,7 +269,7 @@ func UpdateStatus(ctx context.Context, client *gophercloud.ServiceClient, id str // 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(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - b := map[string]interface{}{ + b := map[string]any{ "os-force_delete": struct{}{}, } resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ diff --git a/openstack/blockstorage/v3/snapshots/results.go b/openstack/blockstorage/v3/snapshots/results.go index e3413f2811..8a1440da42 100644 --- a/openstack/blockstorage/v3/snapshots/results.go +++ b/openstack/blockstorage/v3/snapshots/results.go @@ -121,12 +121,12 @@ type UpdateMetadataResult struct { } // ExtractMetadata returns the metadata from a response from snapshots.UpdateMetadata. -func (r UpdateMetadataResult) ExtractMetadata() (map[string]interface{}, error) { +func (r UpdateMetadataResult) ExtractMetadata() (map[string]any, error) { if r.Err != nil { return nil, r.Err } - m := r.Body.(map[string]interface{})["metadata"] - return m.(map[string]interface{}), nil + m := r.Body.(map[string]any)["metadata"] + return m.(map[string]any), nil } type commonResult struct { diff --git a/openstack/blockstorage/v3/snapshots/testing/requests_test.go b/openstack/blockstorage/v3/snapshots/testing/requests_test.go index 80cd68d182..e0ca0b8e0f 100644 --- a/openstack/blockstorage/v3/snapshots/testing/requests_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/requests_test.go @@ -93,10 +93,10 @@ func TestUpdateMetadata(t *testing.T) { MockUpdateMetadataResponse(t) - expected := map[string]interface{}{"key": "v1"} + expected := map[string]any{"key": "v1"} options := &snapshots.UpdateMetadataOpts{ - Metadata: map[string]interface{}{ + Metadata: map[string]any{ "key": "v1", }, } diff --git a/openstack/blockstorage/v3/transfers/requests.go b/openstack/blockstorage/v3/transfers/requests.go index f32a750bfa..9c762cfe1e 100644 --- a/openstack/blockstorage/v3/transfers/requests.go +++ b/openstack/blockstorage/v3/transfers/requests.go @@ -18,7 +18,7 @@ type CreateOpts struct { // ToCreateMap assembles a request body based on the contents of a // TransferOpts. -func (opts CreateOpts) ToCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "transfer") } @@ -44,7 +44,7 @@ type AcceptOpts struct { // ToAcceptMap assembles a request body based on the contents of a // AcceptOpts. -func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { +func (opts AcceptOpts) ToAcceptMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "accept") } diff --git a/openstack/blockstorage/v3/transfers/results.go b/openstack/blockstorage/v3/transfers/results.go index f5e934efea..42885c27ef 100644 --- a/openstack/blockstorage/v3/transfers/results.go +++ b/openstack/blockstorage/v3/transfers/results.go @@ -48,7 +48,7 @@ func (r commonResult) Extract() (*Transfer, error) { } // ExtractInto converts our response data into a transfer struct -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "transfer") } @@ -75,7 +75,7 @@ func ExtractTransfers(r pagination.Page) ([]Transfer, error) { } // ExtractTransfersInto similar to ExtractInto but operates on a `list` of transfers -func ExtractTransfersInto(r pagination.Page, v interface{}) error { +func ExtractTransfersInto(r pagination.Page, v any) error { return r.(TransferPage).Result.ExtractIntoSlicePtr(v, "transfers") } diff --git a/openstack/blockstorage/v3/volumes/requests.go b/openstack/blockstorage/v3/volumes/requests.go index 214e252310..77210943b5 100644 --- a/openstack/blockstorage/v3/volumes/requests.go +++ b/openstack/blockstorage/v3/volumes/requests.go @@ -11,7 +11,7 @@ import ( // SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. type SchedulerHintOptsBuilder interface { - ToSchedulerHintsMap() (map[string]interface{}, error) + ToSchedulerHintsMap() (map[string]any, error) } // SchedulerHintOpts contains options for providing scheduler hints @@ -33,12 +33,12 @@ type SchedulerHintOpts struct { Query string // AdditionalProperies are arbitrary key/values that are not validated by nova. - AdditionalProperties map[string]interface{} + AdditionalProperties map[string]any } // ToSchedulerHintsMap assembles a request body for scheduler hints -func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, error) { - sh := make(map[string]interface{}) +func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]any, error) { + sh := make(map[string]any) uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") @@ -93,13 +93,13 @@ func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, err return sh, nil } - return map[string]interface{}{"OS-SCH-HNT:scheduler_hints": sh}, nil + return map[string]any{"OS-SCH-HNT:scheduler_hints": sh}, nil } // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToVolumeCreateMap() (map[string]interface{}, error) + ToVolumeCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a Volume. This object is passed to @@ -136,7 +136,7 @@ type CreateOpts struct { // ToVolumeCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToVolumeCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "volume") } @@ -276,7 +276,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToVolumeUpdateMap() (map[string]interface{}, error) + ToVolumeUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Volume. This object is passed @@ -290,7 +290,7 @@ type UpdateOpts struct { // ToVolumeUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "volume") } @@ -312,7 +312,7 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o // AttachOptsBuilder allows extensions to add additional parameters to the // Attach request. type AttachOptsBuilder interface { - ToVolumeAttachMap() (map[string]interface{}, error) + ToVolumeAttachMap() (map[string]any, error) } // AttachMode describes the attachment mode for volumes. @@ -341,7 +341,7 @@ type AttachOpts struct { // ToVolumeAttachMap assembles a request body based on the contents of a // AttachOpts. -func (opts AttachOpts) ToVolumeAttachMap() (map[string]interface{}, error) { +func (opts AttachOpts) ToVolumeAttachMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-attach") } @@ -361,7 +361,7 @@ func Attach(ctx context.Context, client *gophercloud.ServiceClient, id string, o // BeginDetaching will mark the volume as detaching. func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id string) (r BeginDetachingResult) { - b := map[string]interface{}{"os-begin_detaching": make(map[string]interface{})} + b := map[string]any{"os-begin_detaching": make(map[string]any)} resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) @@ -372,7 +372,7 @@ func BeginDetaching(ctx context.Context, client *gophercloud.ServiceClient, id s // DetachOptsBuilder allows extensions to add additional parameters to the // Detach request. type DetachOptsBuilder interface { - ToVolumeDetachMap() (map[string]interface{}, error) + ToVolumeDetachMap() (map[string]any, error) } // DetachOpts contains options for detaching a Volume. @@ -383,7 +383,7 @@ type DetachOpts struct { // ToVolumeDetachMap assembles a request body based on the contents of a // DetachOpts. -func (opts DetachOpts) ToVolumeDetachMap() (map[string]interface{}, error) { +func (opts DetachOpts) ToVolumeDetachMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-detach") } @@ -403,7 +403,7 @@ func Detach(ctx context.Context, client *gophercloud.ServiceClient, id string, o // Reserve will reserve a volume based on volume ID. func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ReserveResult) { - b := map[string]interface{}{"os-reserve": make(map[string]interface{})} + b := map[string]any{"os-reserve": make(map[string]any)} resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) @@ -413,7 +413,7 @@ func Reserve(ctx context.Context, client *gophercloud.ServiceClient, id string) // Unreserve will unreserve a volume based on volume ID. func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnreserveResult) { - b := map[string]interface{}{"os-unreserve": make(map[string]interface{})} + b := map[string]any{"os-unreserve": make(map[string]any)} resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) @@ -424,7 +424,7 @@ func Unreserve(ctx context.Context, client *gophercloud.ServiceClient, id string // InitializeConnectionOptsBuilder allows extensions to add additional parameters to the // InitializeConnection request. type InitializeConnectionOptsBuilder interface { - ToVolumeInitializeConnectionMap() (map[string]interface{}, error) + ToVolumeInitializeConnectionMap() (map[string]any, error) } // InitializeConnectionOpts hosts options for InitializeConnection. @@ -443,9 +443,9 @@ type InitializeConnectionOpts struct { // ToVolumeInitializeConnectionMap assembles a request body based on the contents of a // InitializeConnectionOpts. -func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]interface{}, error) { +func (opts InitializeConnectionOpts) ToVolumeInitializeConnectionMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-initialize_connection": b}, err + return map[string]any{"os-initialize_connection": b}, err } // InitializeConnection initializes an iSCSI connection by volume ID. @@ -465,7 +465,7 @@ func InitializeConnection(ctx context.Context, client *gophercloud.ServiceClient // TerminateConnectionOptsBuilder allows extensions to add additional parameters to the // TerminateConnection request. type TerminateConnectionOptsBuilder interface { - ToVolumeTerminateConnectionMap() (map[string]interface{}, error) + ToVolumeTerminateConnectionMap() (map[string]any, error) } // TerminateConnectionOpts hosts options for TerminateConnection. @@ -482,9 +482,9 @@ type TerminateConnectionOpts struct { // ToVolumeTerminateConnectionMap assembles a request body based on the contents of a // TerminateConnectionOpts. -func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]interface{}, error) { +func (opts TerminateConnectionOpts) ToVolumeTerminateConnectionMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "connector") - return map[string]interface{}{"os-terminate_connection": b}, err + return map[string]any{"os-terminate_connection": b}, err } // TerminateConnection terminates an iSCSI connection by volume ID. @@ -504,7 +504,7 @@ func TerminateConnection(ctx context.Context, client *gophercloud.ServiceClient, // ExtendSizeOptsBuilder allows extensions to add additional parameters to the // ExtendSize request. type ExtendSizeOptsBuilder interface { - ToVolumeExtendSizeMap() (map[string]interface{}, error) + ToVolumeExtendSizeMap() (map[string]any, error) } // ExtendSizeOpts contains options for extending the size of an existing Volume. @@ -516,7 +516,7 @@ type ExtendSizeOpts struct { // ToVolumeExtendSizeMap assembles a request body based on the contents of an // ExtendSizeOpts. -func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]interface{}, error) { +func (opts ExtendSizeOpts) ToVolumeExtendSizeMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-extend") } @@ -538,7 +538,7 @@ func ExtendSize(ctx context.Context, client *gophercloud.ServiceClient, id strin // UploadImageOptsBuilder allows extensions to add additional parameters to the // UploadImage request. type UploadImageOptsBuilder interface { - ToVolumeUploadImageMap() (map[string]interface{}, error) + ToVolumeUploadImageMap() (map[string]any, error) } // UploadImageOpts contains options for uploading a Volume to image storage. @@ -566,7 +566,7 @@ type UploadImageOpts struct { // ToVolumeUploadImageMap assembles a request body based on the contents of a // UploadImageOpts. -func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]interface{}, error) { +func (opts UploadImageOpts) ToVolumeUploadImageMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-volume_upload_image") } @@ -586,7 +586,7 @@ func UploadImage(ctx context.Context, client *gophercloud.ServiceClient, id stri // ForceDelete will delete the volume regardless of state. func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-force_delete": ""}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-force_delete": ""}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -594,7 +594,7 @@ func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id stri // ImageMetadataOptsBuilder allows extensions to add additional parameters to the // ImageMetadataRequest request. type ImageMetadataOptsBuilder interface { - ToImageMetadataMap() (map[string]interface{}, error) + ToImageMetadataMap() (map[string]any, error) } // ImageMetadataOpts contains options for setting image metadata to a volume. @@ -605,7 +605,7 @@ type ImageMetadataOpts struct { // ToImageMetadataMap assembles a request body based on the contents of a // ImageMetadataOpts. -func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]interface{}, error) { +func (opts ImageMetadataOpts) ToImageMetadataMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-set_image_metadata") } @@ -631,7 +631,7 @@ type BootableOpts struct { // ToBootableMap assembles a request body based on the contents of a // BootableOpts. -func (opts BootableOpts) ToBootableMap() (map[string]interface{}, error) { +func (opts BootableOpts) ToBootableMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-set_bootable") } @@ -661,7 +661,7 @@ const ( // ChangeTypeOptsBuilder allows extensions to add additional parameters to the // ChangeType request. type ChangeTypeOptsBuilder interface { - ToVolumeChangeTypeMap() (map[string]interface{}, error) + ToVolumeChangeTypeMap() (map[string]any, error) } // ChangeTypeOpts contains options for changing the type of an existing Volume. @@ -678,7 +678,7 @@ type ChangeTypeOpts struct { // ToVolumeChangeTypeMap assembles a request body based on the contents of an // ChangeTypeOpts. -func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]interface{}, error) { +func (opts ChangeTypeOpts) ToVolumeChangeTypeMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-retype") } @@ -706,7 +706,7 @@ type ReImageOpts struct { } // ToReImageMap assembles a request body based on the contents of a ReImageOpts. -func (opts ReImageOpts) ToReImageMap() (map[string]interface{}, error) { +func (opts ReImageOpts) ToReImageMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-reimage") } @@ -727,7 +727,7 @@ func ReImage(ctx context.Context, client *gophercloud.ServiceClient, id string, // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { - ToResetStatusMap() (map[string]interface{}, error) + ToResetStatusMap() (map[string]any, error) } // ResetStatusOpts contains options for resetting a Volume status. @@ -744,7 +744,7 @@ type ResetStatusOpts struct { // ToResetStatusMap assembles a request body based on the contents of a // ResetStatusOpts. -func (opts ResetStatusOpts) ToResetStatusMap() (map[string]interface{}, error) { +func (opts ResetStatusOpts) ToResetStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-reset_status") } diff --git a/openstack/blockstorage/v3/volumes/results.go b/openstack/blockstorage/v3/volumes/results.go index b3f1d19494..3f184b398e 100644 --- a/openstack/blockstorage/v3/volumes/results.go +++ b/openstack/blockstorage/v3/volumes/results.go @@ -153,12 +153,12 @@ func (r commonResult) Extract() (*Volume, error) { } // ExtractInto converts our response data into a volume struct -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "volume") } // ExtractVolumesInto similar to ExtractInto but operates on a `list` of volumes -func ExtractVolumesInto(r pagination.Page, v interface{}) error { +func ExtractVolumesInto(r pagination.Page, v any) error { return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes") } @@ -247,11 +247,11 @@ type ExtendSizeResult struct { // Extract will get the connection information out of the // InitializeConnectionResult object. // -// This will be a generic map[string]interface{} and the results will be +// This will be a generic map[string]any and the results will be // dependent on the type of connection made. -func (r InitializeConnectionResult) Extract() (map[string]interface{}, error) { +func (r InitializeConnectionResult) Extract() (map[string]any, error) { var s struct { - ConnectionInfo map[string]interface{} `json:"connection_info"` + ConnectionInfo map[string]any `json:"connection_info"` } err := r.ExtractInto(&s) return s.ConnectionInfo, err @@ -273,7 +273,7 @@ type ImageVolumeType struct { IsPublic bool `json:"is_public"` // Extra specifications for volume type. - ExtraSpecs map[string]interface{} `json:"extra_specs"` + ExtraSpecs map[string]any `json:"extra_specs"` // ID of quality of service specs. QosSpecsID string `json:"qos_specs_id"` diff --git a/openstack/blockstorage/v3/volumes/testing/requests_test.go b/openstack/blockstorage/v3/volumes/testing/requests_test.go index 6a22016101..5e32346747 100644 --- a/openstack/blockstorage/v3/volumes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumes/testing/requests_test.go @@ -226,7 +226,7 @@ func TestCreateSchedulerHints(t *testing.T) { "8c19174f-4220-44f0-824a-cd1eeef10287", }, LocalToInstance: "0ffb2c1b-d621-4fc1-9ae4-88d99c088ff6", - AdditionalProperties: map[string]interface{}{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + AdditionalProperties: map[string]any{"mark": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } expected := ` { @@ -372,7 +372,7 @@ func TestUploadImage(t *testing.T) { Name: "basic.ru-2a", Description: "", IsPublic: true, - ExtraSpecs: map[string]interface{}{"volume_backend_name": "basic.ru-2a"}, + ExtraSpecs: map[string]any{"volume_backend_name": "basic.ru-2a"}, QosSpecsID: "", Deleted: false, DeletedAt: time.Time{}, diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 7b5a750af8..d419d75d9b 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToVolumeTypeCreateMap() (map[string]interface{}, error) + ToVolumeTypeCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a Volume Type. This object is passed to @@ -29,7 +29,7 @@ type CreateOpts struct { // ToVolumeTypeCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToVolumeTypeCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "volume_type") } @@ -110,7 +110,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToVolumeTypeUpdateMap() (map[string]interface{}, error) + ToVolumeTypeUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Volume Type. This object is passed @@ -124,7 +124,7 @@ type UpdateOpts struct { // ToVolumeTypeUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToVolumeTypeUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "volume_type") } @@ -160,7 +160,7 @@ func GetExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, volume // CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the // CreateExtraSpecs requests. type CreateExtraSpecsOptsBuilder interface { - ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) + ToVolumeTypeExtraSpecsCreateMap() (map[string]any, error) } // ExtraSpecsOpts is a map that contains key-value pairs. @@ -168,8 +168,8 @@ type ExtraSpecsOpts map[string]string // ToVolumeTypeExtraSpecsCreateMap assembles a body for a Create request based on // the contents of ExtraSpecsOpts. -func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]interface{}, error) { - return map[string]interface{}{"extra_specs": opts}, nil +func (opts ExtraSpecsOpts) ToVolumeTypeExtraSpecsCreateMap() (map[string]any, error) { + return map[string]any{"extra_specs": opts}, nil } // CreateExtraSpecs will create or update the extra-specs key-value pairs for @@ -248,7 +248,7 @@ func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager // AddAccessOptsBuilder allows extensions to add additional parameters to the // AddAccess requests. type AddAccessOptsBuilder interface { - ToVolumeTypeAddAccessMap() (map[string]interface{}, error) + ToVolumeTypeAddAccessMap() (map[string]any, error) } // AddAccessOpts represents options for adding access to a volume type. @@ -258,7 +258,7 @@ type AddAccessOpts struct { } // ToVolumeTypeAddAccessMap constructs a request body from AddAccessOpts. -func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]interface{}, error) { +func (opts AddAccessOpts) ToVolumeTypeAddAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "addProjectAccess") } @@ -279,7 +279,7 @@ func AddAccess(ctx context.Context, client *gophercloud.ServiceClient, id string // RemoveAccessOptsBuilder allows extensions to add additional parameters to the // RemoveAccess requests. type RemoveAccessOptsBuilder interface { - ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) + ToVolumeTypeRemoveAccessMap() (map[string]any, error) } // RemoveAccessOpts represents options for removing access to a volume type. @@ -289,7 +289,7 @@ type RemoveAccessOpts struct { } // ToVolumeTypeRemoveAccessMap constructs a request body from RemoveAccessOpts. -func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]interface{}, error) { +func (opts RemoveAccessOpts) ToVolumeTypeRemoveAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "removeProjectAccess") } @@ -310,7 +310,7 @@ func RemoveAccess(ctx context.Context, client *gophercloud.ServiceClient, id str // CreateEncryptionOptsBuilder allows extensions to add additional parameters to the // Create Encryption request. type CreateEncryptionOptsBuilder interface { - ToEncryptionCreateMap() (map[string]interface{}, error) + ToEncryptionCreateMap() (map[string]any, error) } // CreateEncryptionOpts contains options for creating an Encryption Type object. @@ -329,7 +329,7 @@ type CreateEncryptionOpts struct { // ToEncryptionCreateMap assembles a request body based on the contents of a // CreateEncryptionOpts. -func (opts CreateEncryptionOpts) ToEncryptionCreateMap() (map[string]interface{}, error) { +func (opts CreateEncryptionOpts) ToEncryptionCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "encryption") } @@ -373,7 +373,7 @@ func GetEncryptionSpec(ctx context.Context, client *gophercloud.ServiceClient, i // UpdateEncryptionOptsBuilder allows extensions to add additional parameters to the // Update encryption request. type UpdateEncryptionOptsBuilder interface { - ToUpdateEncryptionMap() (map[string]interface{}, error) + ToUpdateEncryptionMap() (map[string]any, error) } // Update Encryption Opts contains options for creating an Update Encryption Type. This object is passed to @@ -392,7 +392,7 @@ type UpdateEncryptionOpts struct { // ToEncryptionCreateMap assembles a request body based on the contents of a // UpdateEncryptionOpts. -func (opts UpdateEncryptionOpts) ToUpdateEncryptionMap() (map[string]interface{}, error) { +func (opts UpdateEncryptionOpts) ToUpdateEncryptionMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "encryption") } diff --git a/openstack/blockstorage/v3/volumetypes/results.go b/openstack/blockstorage/v3/volumetypes/results.go index 93e6ffdd7a..9d5dce56ee 100644 --- a/openstack/blockstorage/v3/volumetypes/results.go +++ b/openstack/blockstorage/v3/volumetypes/results.go @@ -68,12 +68,12 @@ func (r commonResult) Extract() (*VolumeType, error) { } // ExtractInto converts our response data into a volume type struct -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "volume_type") } // ExtractVolumeTypesInto similar to ExtractInto but operates on a `list` of volume types -func ExtractVolumeTypesInto(r pagination.Page, v interface{}) error { +func ExtractVolumeTypesInto(r pagination.Page, v any) error { return r.(VolumeTypePage).Result.ExtractIntoSlicePtr(v, "volume_types") } @@ -227,7 +227,7 @@ func (r encryptionResult) Extract() (*EncryptionType, error) { } // ExtractInto converts our response data into a volume type struct -func (r encryptionResult) ExtractInto(v interface{}) error { +func (r encryptionResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "encryption") } @@ -288,8 +288,8 @@ type encryptionShowSpecResult struct { } // Extract interprets any empty interface Result as an empty interface. -func (r encryptionShowSpecResult) Extract() (map[string]interface{}, error) { - var s map[string]interface{} +func (r encryptionShowSpecResult) Extract() (map[string]any, error) { + var s map[string]any err := r.ExtractInto(&s) return s, err } diff --git a/openstack/common/extensions/results.go b/openstack/common/extensions/results.go index b845091383..0db049ad02 100644 --- a/openstack/common/extensions/results.go +++ b/openstack/common/extensions/results.go @@ -22,12 +22,12 @@ func (r GetResult) Extract() (*Extension, error) { // Extension is a struct that represents an OpenStack extension. type Extension struct { - Updated string `json:"updated"` - Name string `json:"name"` - Links []interface{} `json:"links"` - Namespace string `json:"namespace"` - Alias string `json:"alias"` - Description string `json:"description"` + Updated string `json:"updated"` + Name string `json:"name"` + Links []any `json:"links"` + Namespace string `json:"namespace"` + Alias string `json:"alias"` + Description string `json:"description"` } // ExtensionPage is the page returned by a pager when traversing over a collection of extensions. diff --git a/openstack/common/extensions/testing/fixtures.go b/openstack/common/extensions/testing/fixtures.go index 9b8c21e4da..cfcdbe90e9 100644 --- a/openstack/common/extensions/testing/fixtures.go +++ b/openstack/common/extensions/testing/fixtures.go @@ -43,7 +43,7 @@ const GetOutput = ` var ListedExtension = extensions.Extension{ Updated: "2013-01-20T00:00:00-00:00", Name: "Neutron Service Type Management", - Links: []interface{}{}, + Links: []any{}, Namespace: "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", Alias: "service-type", Description: "API for retrieving service providers for Neutron advanced services", @@ -56,7 +56,7 @@ var ExpectedExtensions = []extensions.Extension{ListedExtension} var SingleExtension = &extensions.Extension{ Updated: "2013-02-03T10:00:00-00:00", Name: "agent", - Links: []interface{}{}, + Links: []any{}, Namespace: "http://docs.openstack.org/ext/agent/api/v2.0", Alias: "agent", Description: "The agent management extension.", diff --git a/openstack/compute/v2/aggregates/requests.go b/openstack/compute/v2/aggregates/requests.go index 9324c4f88a..7111c203b0 100644 --- a/openstack/compute/v2/aggregates/requests.go +++ b/openstack/compute/v2/aggregates/requests.go @@ -26,7 +26,7 @@ type CreateOpts struct { AvailabilityZone string `json:"availability_zone,omitempty"` } -func (opts CreateOpts) ToAggregatesCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToAggregatesCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "aggregate") } @@ -75,7 +75,7 @@ type UpdateOpts struct { AvailabilityZone string `json:"availability_zone,omitempty"` } -func (opts UpdateOpts) ToAggregatesUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToAggregatesUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "aggregate") } @@ -100,7 +100,7 @@ type AddHostOpts struct { Host string `json:"host" required:"true"` } -func (opts AddHostOpts) ToAggregatesAddHostMap() (map[string]interface{}, error) { +func (opts AddHostOpts) ToAggregatesAddHostMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "add_host") } @@ -125,7 +125,7 @@ type RemoveHostOpts struct { Host string `json:"host" required:"true"` } -func (opts RemoveHostOpts) ToAggregatesRemoveHostMap() (map[string]interface{}, error) { +func (opts RemoveHostOpts) ToAggregatesRemoveHostMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "remove_host") } @@ -146,10 +146,10 @@ func RemoveHost(ctx context.Context, client *gophercloud.ServiceClient, aggregat } type SetMetadataOpts struct { - Metadata map[string]interface{} `json:"metadata" required:"true"` + Metadata map[string]any `json:"metadata" required:"true"` } -func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]interface{}, error) { +func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "set_metadata") } diff --git a/openstack/compute/v2/aggregates/testing/requests_test.go b/openstack/compute/v2/aggregates/testing/requests_test.go index e334fdb4b7..2e229bf71c 100644 --- a/openstack/compute/v2/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/aggregates/testing/requests_test.go @@ -140,7 +140,7 @@ func TestSetMetadataAggregate(t *testing.T) { expected := AggregateWithUpdatedMetadata opts := aggregates.SetMetadataOpts{ - Metadata: map[string]interface{}{"key": "value"}, + Metadata: map[string]any{"key": "value"}, } actual, err := aggregates.SetMetadata(context.TODO(), client.ServiceClient(), expected.ID, opts).Extract() diff --git a/openstack/compute/v2/attachinterfaces/requests.go b/openstack/compute/v2/attachinterfaces/requests.go index 6aa1754f80..9be0ac68cb 100644 --- a/openstack/compute/v2/attachinterfaces/requests.go +++ b/openstack/compute/v2/attachinterfaces/requests.go @@ -26,7 +26,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, serverID, portI // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToAttachInterfacesCreateMap() (map[string]interface{}, error) + ToAttachInterfacesCreateMap() (map[string]any, error) } // CreateOpts specifies parameters of a new interface attachment. @@ -50,7 +50,7 @@ type CreateOpts struct { } // ToAttachInterfacesCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToAttachInterfacesCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToAttachInterfacesCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "interfaceAttachment") } diff --git a/openstack/compute/v2/diagnostics/results.go b/openstack/compute/v2/diagnostics/results.go index b8cac8b285..7e79f482a6 100644 --- a/openstack/compute/v2/diagnostics/results.go +++ b/openstack/compute/v2/diagnostics/results.go @@ -9,8 +9,8 @@ type serverDiagnosticsResult struct { } // Extract interprets any diagnostic response as a map -func (r serverDiagnosticsResult) Extract() (map[string]interface{}, error) { - var s map[string]interface{} +func (r serverDiagnosticsResult) Extract() (map[string]any, error) { + var s map[string]any err := r.ExtractInto(&s) return s, err } diff --git a/openstack/compute/v2/diagnostics/testing/requests_test.go b/openstack/compute/v2/diagnostics/testing/requests_test.go index 29f40eb133..983c4d18e9 100644 --- a/openstack/compute/v2/diagnostics/testing/requests_test.go +++ b/openstack/compute/v2/diagnostics/testing/requests_test.go @@ -15,7 +15,7 @@ func TestGetDiagnostics(t *testing.T) { HandleDiagnosticGetSuccessfully(t) - expected := map[string]interface{}{"cpu0_time": float64(173), "memory": float64(524288)} + expected := map[string]any{"cpu0_time": float64(173), "memory": float64(524288)} res, err := diagnostics.Get(context.TODO(), client.ServiceClient(), "1234asdf").Extract() th.AssertNoErr(t, err) diff --git a/openstack/compute/v2/extensions/testing/delegate_test.go b/openstack/compute/v2/extensions/testing/delegate_test.go index a08c19a957..a2b8774c8e 100644 --- a/openstack/compute/v2/extensions/testing/delegate_test.go +++ b/openstack/compute/v2/extensions/testing/delegate_test.go @@ -27,7 +27,7 @@ func TestList(t *testing.T) { { Updated: "2013-01-20T00:00:00-00:00", Name: "Neutron Service Type Management", - Links: []interface{}{}, + Links: []any{}, Namespace: "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", Alias: "service-type", Description: "API for retrieving service providers for Neutron advanced services", diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 26bc4f71a8..119f5e78e8 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -99,7 +99,7 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } type CreateOptsBuilder interface { - ToFlavorCreateMap() (map[string]interface{}, error) + ToFlavorCreateMap() (map[string]any, error) } // CreateOpts specifies parameters used for creating a flavor. @@ -138,7 +138,7 @@ type CreateOpts struct { } // ToFlavorCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToFlavorCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "flavor") } @@ -157,7 +157,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO } type UpdateOptsBuilder interface { - ToFlavorUpdateMap() (map[string]interface{}, error) + ToFlavorUpdateMap() (map[string]any, error) } // UpdateOpts specifies parameters used for updating a flavor. @@ -169,7 +169,7 @@ type UpdateOpts struct { } // ToFlavorUpdateMap constructs a request body from UpdateOpts. -func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "flavor") } @@ -214,7 +214,7 @@ func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager // AddAccessOptsBuilder allows extensions to add additional parameters to the // AddAccess requests. type AddAccessOptsBuilder interface { - ToFlavorAddAccessMap() (map[string]interface{}, error) + ToFlavorAddAccessMap() (map[string]any, error) } // AddAccessOpts represents options for adding access to a flavor. @@ -224,7 +224,7 @@ type AddAccessOpts struct { } // ToFlavorAddAccessMap constructs a request body from AddAccessOpts. -func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]interface{}, error) { +func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "addTenantAccess") } @@ -245,7 +245,7 @@ func AddAccess(ctx context.Context, client *gophercloud.ServiceClient, id string // RemoveAccessOptsBuilder allows extensions to add additional parameters to the // RemoveAccess requests. type RemoveAccessOptsBuilder interface { - ToFlavorRemoveAccessMap() (map[string]interface{}, error) + ToFlavorRemoveAccessMap() (map[string]any, error) } // RemoveAccessOpts represents options for removing access to a flavor. @@ -255,7 +255,7 @@ type RemoveAccessOpts struct { } // ToFlavorRemoveAccessMap constructs a request body from RemoveAccessOpts. -func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]interface{}, error) { +func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "removeTenantAccess") } @@ -289,7 +289,7 @@ func GetExtraSpec(ctx context.Context, client *gophercloud.ServiceClient, flavor // CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the // CreateExtraSpecs requests. type CreateExtraSpecsOptsBuilder interface { - ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) + ToFlavorExtraSpecsCreateMap() (map[string]any, error) } // ExtraSpecsOpts is a map that contains key-value pairs. @@ -297,8 +297,8 @@ type ExtraSpecsOpts map[string]string // ToFlavorExtraSpecsCreateMap assembles a body for a Create request based on // the contents of ExtraSpecsOpts. -func (opts ExtraSpecsOpts) ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) { - return map[string]interface{}{"extra_specs": opts}, nil +func (opts ExtraSpecsOpts) ToFlavorExtraSpecsCreateMap() (map[string]any, error) { + return map[string]any{"extra_specs": opts}, nil } // CreateExtraSpecs will create or update the extra-specs key-value pairs for diff --git a/openstack/compute/v2/flavors/results.go b/openstack/compute/v2/flavors/results.go index 5ca0132301..387268af85 100644 --- a/openstack/compute/v2/flavors/results.go +++ b/openstack/compute/v2/flavors/results.go @@ -92,7 +92,7 @@ func (r *Flavor) UnmarshalJSON(b []byte) error { type tmp Flavor var s struct { tmp - Swap interface{} `json:"swap"` + Swap any `json:"swap"` } err := json.Unmarshal(b, &s) if err != nil { diff --git a/openstack/compute/v2/hypervisors/results.go b/openstack/compute/v2/hypervisors/results.go index 7413dde45d..2e8d244763 100644 --- a/openstack/compute/v2/hypervisors/results.go +++ b/openstack/compute/v2/hypervisors/results.go @@ -36,7 +36,7 @@ func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp - ID interface{} `json:"id"` + ID any `json:"id"` } err := json.Unmarshal(b, &s) @@ -144,11 +144,11 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { type tmp Hypervisor var s struct { tmp - ID interface{} `json:"id"` - CPUInfo interface{} `json:"cpu_info"` - HypervisorVersion interface{} `json:"hypervisor_version"` - FreeDiskGB interface{} `json:"free_disk_gb"` - LocalGB interface{} `json:"local_gb"` + ID any `json:"id"` + CPUInfo any `json:"cpu_info"` + HypervisorVersion any `json:"hypervisor_version"` + FreeDiskGB any `json:"free_disk_gb"` + LocalGB any `json:"local_gb"` } err := json.Unmarshal(b, &s) @@ -166,7 +166,7 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { switch t := s.CPUInfo.(type) { case string: tmpb = []byte(t) - case map[string]interface{}: + case map[string]any: tmpb, err = json.Marshal(t) if err != nil { return err @@ -341,7 +341,7 @@ func (r *Uptime) UnmarshalJSON(b []byte) error { type tmp Uptime var s struct { tmp - ID interface{} `json:"id"` + ID any `json:"id"` } err := json.Unmarshal(b, &s) diff --git a/openstack/compute/v2/instanceactions/results.go b/openstack/compute/v2/instanceactions/results.go index 9f0bcc6045..2015be88c2 100644 --- a/openstack/compute/v2/instanceactions/results.go +++ b/openstack/compute/v2/instanceactions/results.go @@ -185,10 +185,10 @@ func (r InstanceActionResult) Extract() (InstanceActionDetail, error) { return s, err } -func (r InstanceActionResult) ExtractInto(v interface{}) error { +func (r InstanceActionResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "instanceAction") } -func ExtractInstanceActionsInto(r pagination.Page, v interface{}) error { +func ExtractInstanceActionsInto(r pagination.Page, v any) error { return r.(InstanceActionPage).Result.ExtractIntoSlicePtr(v, "instanceActions") } diff --git a/openstack/compute/v2/keypairs/requests.go b/openstack/compute/v2/keypairs/requests.go index c632f424aa..bc4cc270f1 100644 --- a/openstack/compute/v2/keypairs/requests.go +++ b/openstack/compute/v2/keypairs/requests.go @@ -17,7 +17,7 @@ type CreateOptsExt struct { } // ToServerCreateMap adds the key_name to the base server creation options. -func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsExt) ToServerCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToServerCreateMap() if err != nil { return nil, err @@ -27,7 +27,7 @@ func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { return base, nil } - serverMap := base["server"].(map[string]interface{}) + serverMap := base["server"].(map[string]any) serverMap["key_name"] = opts.KeyName return base, nil @@ -70,7 +70,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToKeyPairCreateMap() (map[string]interface{}, error) + ToKeyPairCreateMap() (map[string]any, error) } // CreateOpts specifies KeyPair creation or import parameters. @@ -93,7 +93,7 @@ type CreateOpts struct { } // ToKeyPairCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToKeyPairCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "keypair") } diff --git a/openstack/compute/v2/quotasets/requests.go b/openstack/compute/v2/quotasets/requests.go index a7ff87c4d2..28e0f6c2c1 100644 --- a/openstack/compute/v2/quotasets/requests.go +++ b/openstack/compute/v2/quotasets/requests.go @@ -95,11 +95,11 @@ type UpdateOpts struct { type UpdateOptsBuilder interface { // Extra specific name to prevent collisions with interfaces for other quotas // (e.g. neutron) - ToComputeQuotaUpdateMap() (map[string]interface{}, error) + ToComputeQuotaUpdateMap() (map[string]any, error) } // ToComputeQuotaUpdateMap builds the update options into a serializable // format. -func (opts UpdateOpts) ToComputeQuotaUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToComputeQuotaUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "quota_set") } diff --git a/openstack/compute/v2/quotasets/testing/requests_test.go b/openstack/compute/v2/quotasets/testing/requests_test.go index 8d6d30e8d3..eac6e99d0d 100644 --- a/openstack/compute/v2/quotasets/testing/requests_test.go +++ b/openstack/compute/v2/quotasets/testing/requests_test.go @@ -58,7 +58,7 @@ func TestDelete(t *testing.T) { type ErrorUpdateOpts quotasets.UpdateOpts -func (opts ErrorUpdateOpts) ToComputeQuotaUpdateMap() (map[string]interface{}, error) { +func (opts ErrorUpdateOpts) ToComputeQuotaUpdateMap() (map[string]any, error) { return nil, errors.New("This is an error") } diff --git a/openstack/compute/v2/remoteconsoles/requests.go b/openstack/compute/v2/remoteconsoles/requests.go index 7b26b9e42d..b6d61ef799 100644 --- a/openstack/compute/v2/remoteconsoles/requests.go +++ b/openstack/compute/v2/remoteconsoles/requests.go @@ -53,7 +53,7 @@ const ( // CreateOptsBuilder allows to add additional parameters to the Create request. type CreateOptsBuilder interface { - ToRemoteConsoleCreateMap() (map[string]interface{}, error) + ToRemoteConsoleCreateMap() (map[string]any, error) } // CreateOpts specifies parameters to the Create request. @@ -66,7 +66,7 @@ type CreateOpts struct { } // ToRemoteConsoleCreateMap builds a request body from the CreateOpts. -func (opts CreateOpts) ToRemoteConsoleCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToRemoteConsoleCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "remote_console") } diff --git a/openstack/compute/v2/secgroups/requests.go b/openstack/compute/v2/secgroups/requests.go index 5c9102e1a0..d69a99b4f0 100644 --- a/openstack/compute/v2/secgroups/requests.go +++ b/openstack/compute/v2/secgroups/requests.go @@ -36,11 +36,11 @@ type CreateOpts struct { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSecGroupCreateMap() (map[string]interface{}, error) + ToSecGroupCreateMap() (map[string]any, error) } // ToSecGroupCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSecGroupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group") } @@ -69,11 +69,11 @@ type UpdateOpts struct { // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToSecGroupUpdateMap() (map[string]interface{}, error) + ToSecGroupUpdateMap() (map[string]any, error) } // ToSecGroupUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group") } @@ -140,11 +140,11 @@ type CreateRuleOpts struct { // CreateRuleOptsBuilder allows extensions to add additional parameters to the // CreateRule request. type CreateRuleOptsBuilder interface { - ToRuleCreateMap() (map[string]interface{}, error) + ToRuleCreateMap() (map[string]any, error) } // ToRuleCreateMap builds a request body from CreateRuleOpts. -func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { +func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group_rule") } diff --git a/openstack/compute/v2/secgroups/results.go b/openstack/compute/v2/secgroups/results.go index 30ab4fdb9c..c50fd156dc 100644 --- a/openstack/compute/v2/secgroups/results.go +++ b/openstack/compute/v2/secgroups/results.go @@ -32,7 +32,7 @@ func (r *SecurityGroup) UnmarshalJSON(b []byte) error { type tmp SecurityGroup var s struct { tmp - ID interface{} `json:"id"` + ID any `json:"id"` } err := json.Unmarshal(b, &s) if err != nil { @@ -82,8 +82,8 @@ func (r *Rule) UnmarshalJSON(b []byte) error { type tmp Rule var s struct { tmp - ID interface{} `json:"id"` - ParentGroupID interface{} `json:"parent_group_id"` + ID any `json:"id"` + ParentGroupID any `json:"parent_group_id"` } err := json.Unmarshal(b, &s) if err != nil { diff --git a/openstack/compute/v2/servergroups/requests.go b/openstack/compute/v2/servergroups/requests.go index 2fd10d4d34..c2b8400011 100644 --- a/openstack/compute/v2/servergroups/requests.go +++ b/openstack/compute/v2/servergroups/requests.go @@ -48,7 +48,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToServerGroupCreateMap() (map[string]interface{}, error) + ToServerGroupCreateMap() (map[string]any, error) } // CreateOpts specifies Server Group creation parameters. @@ -69,7 +69,7 @@ type CreateOpts struct { } // ToServerGroupCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToServerGroupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "server_group") } diff --git a/openstack/compute/v2/servergroups/results.go b/openstack/compute/v2/servergroups/results.go index 053fc6ea0c..1d97dcc9e8 100644 --- a/openstack/compute/v2/servergroups/results.go +++ b/openstack/compute/v2/servergroups/results.go @@ -37,7 +37,7 @@ type ServerGroup struct { // Metadata includes a list of all user-specified key-value pairs attached // to the Server Group. - Metadata map[string]interface{} + Metadata map[string]any // Policy is the policy of a server group. // This requires microversion 2.64 or later. diff --git a/openstack/compute/v2/servergroups/testing/fixtures_test.go b/openstack/compute/v2/servergroups/testing/fixtures_test.go index fd692080ae..de36c3ff89 100644 --- a/openstack/compute/v2/servergroups/testing/fixtures_test.go +++ b/openstack/compute/v2/servergroups/testing/fixtures_test.go @@ -112,7 +112,7 @@ var FirstServerGroup = servergroups.ServerGroup{ "anti-affinity", }, Members: []string{}, - Metadata: map[string]interface{}{}, + Metadata: map[string]any{}, } // SecondServerGroup is the second result in ListOutput. @@ -123,7 +123,7 @@ var SecondServerGroup = servergroups.ServerGroup{ "affinity", }, Members: []string{}, - Metadata: map[string]interface{}{}, + Metadata: map[string]any{}, } // ExpectedServerGroupSlice is the slice of results that should be parsed @@ -138,7 +138,7 @@ var CreatedServerGroup = servergroups.ServerGroup{ "anti-affinity", }, Members: []string{}, - Metadata: map[string]interface{}{}, + Metadata: map[string]any{}, } // HandleListSuccessfully configures the test server to respond to a List request. diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index ea2df1846c..dd3b132d1d 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -131,7 +131,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // SchedulerHintOptsBuilder builds the scheduler hints into a serializable format. type SchedulerHintOptsBuilder interface { - ToSchedulerHintsMap() (map[string]interface{}, error) + ToSchedulerHintsMap() (map[string]any, error) } // SchedulerHintOpts represents a set of scheduling hints that are passed to the @@ -150,7 +150,7 @@ type SchedulerHintOpts struct { // Query is a conditional statement that results in compute nodes able to // host the instance. - Query []interface{} + Query []any // TargetCell specifies a cell name where the instance will be placed. TargetCell string `json:"target_cell,omitempty"` @@ -162,12 +162,12 @@ type SchedulerHintOpts struct { BuildNearHostIP string // AdditionalProperies are arbitrary key/values that are not validated by nova. - AdditionalProperties map[string]interface{} + AdditionalProperties map[string]any } // ToSchedulerHintsMap assembles a request body for scheduler hints. -func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, error) { - sh := make(map[string]interface{}) +func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]any, error) { + sh := make(map[string]any) uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$") @@ -273,7 +273,7 @@ func (opts SchedulerHintOpts) ToSchedulerHintsMap() (map[string]interface{}, err return sh, nil } - return map[string]interface{}{"os:scheduler_hints": sh}, nil + return map[string]any{"os:scheduler_hints": sh}, nil } // Network is used within CreateOpts to control a new server's network @@ -432,7 +432,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToServerCreateMap() (map[string]interface{}, error) + ToServerCreateMap() (map[string]any, error) } // CreateOpts specifies server creation parameters. @@ -464,7 +464,7 @@ type CreateOpts struct { // tenant. // Starting with microversion 2.37 networks can also be an "auto" or "none" // string. - Networks interface{} `json:"-"` + Networks any `json:"-"` // Metadata contains key-value pairs (up to 255 bytes each) to attach to the // server. @@ -512,7 +512,7 @@ type CreateOpts struct { // ToServerCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToServerCreateMap() (map[string]any, error) { // We intentionally don't envelope the body here since we want to strip // some fields out and modify others b, err := gophercloud.BuildRequestBody(opts, "") @@ -531,9 +531,9 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { } if len(opts.SecurityGroups) > 0 { - securityGroups := make([]map[string]interface{}, len(opts.SecurityGroups)) + securityGroups := make([]map[string]any, len(opts.SecurityGroups)) for i, groupName := range opts.SecurityGroups { - securityGroups[i] = map[string]interface{}{"name": groupName} + securityGroups[i] = map[string]any{"name": groupName} } b["security_groups"] = securityGroups } @@ -541,9 +541,9 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { switch v := opts.Networks.(type) { case []Network: if len(v) > 0 { - networks := make([]map[string]interface{}, len(v)) + networks := make([]map[string]any, len(v)) for i, net := range v { - networks[i] = make(map[string]interface{}) + networks[i] = make(map[string]any) if net.UUID != "" { networks[i]["uuid"] = net.UUID } @@ -576,7 +576,7 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { } // Now we do our enveloping - b = map[string]interface{}{"server": b} + b = map[string]any{"server": b} return b, nil } @@ -615,7 +615,7 @@ func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) ( // ForceDelete forces the deletion of a server. func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"forceDelete": ""}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -632,7 +632,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { - ToServerUpdateMap() (map[string]interface{}, error) + ToServerUpdateMap() (map[string]any, error) } // UpdateOpts specifies the base attributes that may be updated on an existing @@ -651,7 +651,7 @@ type UpdateOpts struct { } // ToServerUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToServerUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "server") } @@ -672,7 +672,7 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o // ChangeAdminPassword alters the administrator or root password for a specified // server. func ChangeAdminPassword(ctx context.Context, client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { - b := map[string]interface{}{ + b := map[string]any{ "changePassword": map[string]string{ "adminPass": newPassword, }, @@ -697,7 +697,7 @@ const ( // RebootOptsBuilder allows extensions to add additional parameters to the // reboot request. type RebootOptsBuilder interface { - ToServerRebootMap() (map[string]interface{}, error) + ToServerRebootMap() (map[string]any, error) } // RebootOpts provides options to the reboot request. @@ -707,7 +707,7 @@ type RebootOpts struct { } // ToServerRebootMap builds a body for the reboot request. -func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { +func (opts RebootOpts) ToServerRebootMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "reboot") } @@ -740,7 +740,7 @@ func Reboot(ctx context.Context, client *gophercloud.ServiceClient, id string, o // RebuildOptsBuilder allows extensions to provide additional parameters to the // rebuild request. type RebuildOptsBuilder interface { - ToServerRebuildMap() (map[string]interface{}, error) + ToServerRebuildMap() (map[string]any, error) } // RebuildOpts represents the configuration options used in a server rebuild @@ -774,7 +774,7 @@ type RebuildOpts struct { } // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON -func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { +func (opts RebuildOpts) ToServerRebuildMap() (map[string]any, error) { if opts.DiskConfig != "" && opts.DiskConfig != Auto && opts.DiskConfig != Manual { err := gophercloud.ErrInvalidInput{} err.Argument = "servers.RebuildOpts.DiskConfig" @@ -787,7 +787,7 @@ func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { return nil, err } - return map[string]interface{}{"rebuild": b}, nil + return map[string]any{"rebuild": b}, nil } // Rebuild will reprovision the server according to the configuration options @@ -806,7 +806,7 @@ func Rebuild(ctx context.Context, client *gophercloud.ServiceClient, id string, // ResizeOptsBuilder allows extensions to add additional parameters to the // resize request. type ResizeOptsBuilder interface { - ToServerResizeMap() (map[string]interface{}, error) + ToServerResizeMap() (map[string]any, error) } // ResizeOpts represents the configuration options used to control a Resize @@ -821,7 +821,7 @@ type ResizeOpts struct { // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON // request body for the Resize request. -func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { +func (opts ResizeOpts) ToServerResizeMap() (map[string]any, error) { if opts.DiskConfig != "" && opts.DiskConfig != Auto && opts.DiskConfig != Manual { err := gophercloud.ErrInvalidInput{} err.Argument = "servers.ResizeOpts.DiskConfig" @@ -855,7 +855,7 @@ func Resize(ctx context.Context, client *gophercloud.ServiceClient, id string, o // ConfirmResize confirms a previous resize operation on a server. // See Resize() for more details. func ConfirmResize(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 202, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -865,7 +865,7 @@ func ConfirmResize(ctx context.Context, client *gophercloud.ServiceClient, id st // RevertResize cancels a previous resize operation on a server. // See Resize() for more details. func RevertResize(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"revertResize": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -873,7 +873,7 @@ func RevertResize(ctx context.Context, client *gophercloud.ServiceClient, id str // ResetMetadataOptsBuilder allows extensions to add additional parameters to // the Reset request. type ResetMetadataOptsBuilder interface { - ToMetadataResetMap() (map[string]interface{}, error) + ToMetadataResetMap() (map[string]any, error) } // MetadataOpts is a map that contains key-value pairs. @@ -881,14 +881,14 @@ type MetadataOpts map[string]string // ToMetadataResetMap assembles a body for a Reset request based on the contents // of a MetadataOpts. -func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) { - return map[string]interface{}{"metadata": opts}, nil +func (opts MetadataOpts) ToMetadataResetMap() (map[string]any, error) { + return map[string]any{"metadata": opts}, nil } // ToMetadataUpdateMap assembles a body for an Update request based on the // contents of a MetadataOpts. -func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) { - return map[string]interface{}{"metadata": opts}, nil +func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]any, error) { + return map[string]any{"metadata": opts}, nil } // ResetMetadata will create multiple new key-value pairs for the given server @@ -919,7 +919,7 @@ func Metadata(ctx context.Context, client *gophercloud.ServiceClient, id string) // UpdateMetadataOptsBuilder allows extensions to add additional parameters to // the Create request. type UpdateMetadataOptsBuilder interface { - ToMetadataUpdateMap() (map[string]interface{}, error) + ToMetadataUpdateMap() (map[string]any, error) } // UpdateMetadata updates (or creates) all the metadata specified by opts for @@ -941,7 +941,7 @@ func UpdateMetadata(ctx context.Context, client *gophercloud.ServiceClient, id s // MetadatumOptsBuilder allows extensions to add additional parameters to the // Create request. type MetadatumOptsBuilder interface { - ToMetadatumCreateMap() (map[string]interface{}, string, error) + ToMetadatumCreateMap() (map[string]any, string, error) } // MetadatumOpts is a map of length one that contains a key-value pair. @@ -949,14 +949,14 @@ type MetadatumOpts map[string]string // ToMetadatumCreateMap assembles a body for a Create request based on the // contents of a MetadataumOpts. -func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) { +func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]any, string, error) { if len(opts) != 1 { err := gophercloud.ErrInvalidInput{} err.Argument = "servers.MetadatumOpts" err.Info = "Must have 1 and only 1 key-value pair" return nil, "", err } - metadatum := map[string]interface{}{"meta": opts} + metadatum := map[string]any{"meta": opts} var key string for k := range metadatum["meta"].(MetadatumOpts) { key = k @@ -1014,7 +1014,7 @@ func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network strin // CreateImageOptsBuilder allows extensions to add additional parameters to the // CreateImage request. type CreateImageOptsBuilder interface { - ToServerCreateImageMap() (map[string]interface{}, error) + ToServerCreateImageMap() (map[string]any, error) } // CreateImageOpts provides options to pass to the CreateImage request. @@ -1029,7 +1029,7 @@ type CreateImageOpts struct { // ToServerCreateImageMap formats a CreateImageOpts structure into a request // body. -func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) { +func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "createImage") } @@ -1059,7 +1059,7 @@ func GetPassword(ctx context.Context, client *gophercloud.ServiceClient, serverI // ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be // used as ShowConsoleOutput options type ShowConsoleOutputOptsBuilder interface { - ToServerShowConsoleOutputMap() (map[string]interface{}, error) + ToServerShowConsoleOutputMap() (map[string]any, error) } // ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder @@ -1070,7 +1070,7 @@ type ShowConsoleOutputOpts struct { } // ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body. -func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]interface{}, error) { +func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-getConsoleOutput") } @@ -1091,7 +1091,7 @@ func ShowConsoleOutput(ctx context.Context, client *gophercloud.ServiceClient, i // EvacuateOptsBuilder allows extensions to add additional parameters to the // the Evacuate request. type EvacuateOptsBuilder interface { - ToEvacuateMap() (map[string]interface{}, error) + ToEvacuateMap() (map[string]any, error) } // EvacuateOpts specifies Evacuate action parameters. @@ -1107,7 +1107,7 @@ type EvacuateOpts struct { } // ToServerGroupCreateMap constructs a request body from CreateOpts. -func (opts EvacuateOpts) ToEvacuateMap() (map[string]interface{}, error) { +func (opts EvacuateOpts) ToEvacuateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "evacuate") } @@ -1127,7 +1127,7 @@ func Evacuate(ctx context.Context, client *gophercloud.ServiceClient, id string, // InjectNetworkInfo will inject the network info into a server func InjectNetworkInfo(ctx context.Context, client *gophercloud.ServiceClient, id string) (r InjectNetworkResult) { - b := map[string]interface{}{ + b := map[string]any{ "injectNetworkInfo": nil, } resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) @@ -1137,21 +1137,21 @@ func InjectNetworkInfo(ctx context.Context, client *gophercloud.ServiceClient, i // Lock is the operation responsible for locking a Compute server. func Lock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r LockResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"lock": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"lock": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unlock is the operation responsible for unlocking a Compute server. func Unlock(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnlockResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"unlock": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unlock": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Migrate will initiate a migration of the instance to another host. func Migrate(ctx context.Context, client *gophercloud.ServiceClient, id string) (r MigrateResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"migrate": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"migrate": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -1159,7 +1159,7 @@ func Migrate(ctx context.Context, client *gophercloud.ServiceClient, id string) // LiveMigrateOptsBuilder allows extensions to add additional parameters to the // LiveMigrate request. type LiveMigrateOptsBuilder interface { - ToLiveMigrateMap() (map[string]interface{}, error) + ToLiveMigrateMap() (map[string]any, error) } // LiveMigrateOpts specifies parameters of live migrate action. @@ -1180,7 +1180,7 @@ type LiveMigrateOpts struct { } // ToLiveMigrateMap constructs a request body from LiveMigrateOpts. -func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]interface{}, error) { +func (opts LiveMigrateOpts) ToLiveMigrateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "os-migrateLive") } @@ -1198,14 +1198,14 @@ func LiveMigrate(ctx context.Context, client *gophercloud.ServiceClient, id stri // Pause is the operation responsible for pausing a Compute server. func Pause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r PauseResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"pause": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"pause": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Unpause is the operation responsible for unpausing a Compute server. func Unpause(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnpauseResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"unpause": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unpause": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -1213,7 +1213,7 @@ func Unpause(ctx context.Context, client *gophercloud.ServiceClient, id string) // RescueOptsBuilder is an interface that allows extensions to override the // default structure of a Rescue request. type RescueOptsBuilder interface { - ToServerRescueMap() (map[string]interface{}, error) + ToServerRescueMap() (map[string]any, error) } // RescueOpts represents the configuration options used to control a Rescue @@ -1232,7 +1232,7 @@ type RescueOpts struct { // ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON // request body for the Rescue request. -func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { +func (opts RescueOpts) ToServerRescueMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "rescue") } @@ -1252,14 +1252,14 @@ func Rescue(ctx context.Context, client *gophercloud.ServiceClient, id string, o // Unrescue instructs the provider to return the server from RESCUE mode. func Unrescue(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnrescueResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"unrescue": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"unrescue": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ResetNetwork will reset the network of a server func ResetNetwork(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResetNetworkResult) { - b := map[string]interface{}{ + b := map[string]any{ "resetNetwork": nil, } resp, err := client.Post(ctx, actionURL(client, id), b, nil, nil) @@ -1280,22 +1280,22 @@ const ( // ResetState will reset the state of a server func ResetState(ctx context.Context, client *gophercloud.ServiceClient, id string, state ServerState) (r ResetStateResult) { - stateMap := map[string]interface{}{"state": state} - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-resetState": stateMap}, nil, nil) + stateMap := map[string]any{"state": state} + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-resetState": stateMap}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Shelve is the operation responsible for shelving a Compute server. func Shelve(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"shelve": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"shelve": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ShelveOffload is the operation responsible for Shelve-Offload a Compute server. func ShelveOffload(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ShelveOffloadResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"shelveOffload": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"shelveOffload": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -1303,7 +1303,7 @@ func ShelveOffload(ctx context.Context, client *gophercloud.ServiceClient, id st // UnshelveOptsBuilder allows extensions to add additional parameters to the // Unshelve request. type UnshelveOptsBuilder interface { - ToUnshelveMap() (map[string]interface{}, error) + ToUnshelveMap() (map[string]any, error) } // UnshelveOpts specifies parameters of shelve-offload action. @@ -1313,7 +1313,7 @@ type UnshelveOpts struct { AvailabilityZone string `json:"availability_zone,omitempty"` } -func (opts UnshelveOpts) ToUnshelveMap() (map[string]interface{}, error) { +func (opts UnshelveOpts) ToUnshelveMap() (map[string]any, error) { // Key 'availabilty_zone' is required if the unshelve action is an object // i.e {"unshelve": {}} will be rejected b, err := gophercloud.BuildRequestBody(opts, "unshelve") @@ -1321,7 +1321,7 @@ func (opts UnshelveOpts) ToUnshelveMap() (map[string]interface{}, error) { return nil, err } - if _, ok := b["unshelve"].(map[string]interface{})["availability_zone"]; !ok { + if _, ok := b["unshelve"].(map[string]any)["availability_zone"]; !ok { b["unshelve"] = nil } @@ -1342,28 +1342,28 @@ func Unshelve(ctx context.Context, client *gophercloud.ServiceClient, id string, // Start is the operation responsible for starting a Compute server. func Start(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StartResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-start": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Stop is the operation responsible for stopping a Compute server. func Stop(ctx context.Context, client *gophercloud.ServiceClient, id string) (r StopResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"os-stop": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Suspend is the operation responsible for suspending a Compute server. func Suspend(ctx context.Context, client *gophercloud.ServiceClient, id string) (r SuspendResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"suspend": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"suspend": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Resume is the operation responsible for resuming a Compute server. func Resume(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResumeResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"resume": nil}, nil, nil) + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"resume": nil}, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 286253d9ce..dc51564176 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -24,11 +24,11 @@ func (r serverResult) Extract() (*Server, error) { return &s, err } -func (r serverResult) ExtractInto(v interface{}) error { +func (r serverResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "server") } -func ExtractServersInto(r pagination.Page, v interface{}) error { +func ExtractServersInto(r pagination.Page, v any) error { return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers") } @@ -182,15 +182,15 @@ type Server struct { // Image refers to a JSON object, which itself indicates the OS image used to // deploy the server. - Image map[string]interface{} `json:"-"` + Image map[string]any `json:"-"` // Flavor refers to a JSON object, which itself indicates the hardware // configuration of the deployed server. - Flavor map[string]interface{} `json:"flavor"` + Flavor map[string]any `json:"flavor"` // Addresses includes a list of all IP addresses assigned to the server, // keyed by pool. - Addresses map[string]interface{} `json:"addresses"` + Addresses map[string]any `json:"addresses"` // Metadata includes a list of all user-specified key-value pairs attached // to the server. @@ -198,7 +198,7 @@ type Server struct { // Links includes HTTP references to the itself, useful for passing along to // other APIs that might want a server reference. - Links []interface{} `json:"links"` + Links []any `json:"links"` // KeyName indicates which public key was injected into the server on launch. KeyName string `json:"key_name"` @@ -211,7 +211,7 @@ type Server struct { // SecurityGroups includes the security groups that this instance has applied // to it. - SecurityGroups []map[string]interface{} `json:"security_groups"` + SecurityGroups []map[string]any `json:"security_groups"` // AttachedVolumes includes the volume attachments of this instance AttachedVolumes []AttachedVolume `json:"os-extended-volumes:volumes_attached"` @@ -336,7 +336,7 @@ func (r *Server) UnmarshalJSON(b []byte) error { type tmp Server var s struct { tmp - Image interface{} `json:"image"` + Image any `json:"image"` LaunchedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:launched_at"` TerminatedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:terminated_at"` } @@ -348,7 +348,7 @@ func (r *Server) UnmarshalJSON(b []byte) error { *r = Server(s.tmp) switch t := s.Image.(type) { - case map[string]interface{}: + case map[string]any: r.Image = t case string: switch t { diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index 13f7022231..2ce6282b02 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -490,9 +490,9 @@ var ( Status: "ACTIVE", Updated: herpTimeUpdated, HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", - Addresses: map[string]interface{}{ - "private": []interface{}{ - map[string]interface{}{ + Addresses: map[string]any{ + "private": []any{ + map[string]any{ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b", "version": float64(4), "addr": "10.0.0.32", @@ -500,29 +500,29 @@ var ( }, }, }, - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", "rel": "bookmark", }, }, - Image: map[string]interface{}{ + Image: map[string]any{ "id": "f90f6034-2570-4974-8351-6b49732ef2eb", - "links": []interface{}{ - map[string]interface{}{ + "links": []any{ + map[string]any{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "rel": "bookmark", }, }, }, - Flavor: map[string]interface{}{ + Flavor: map[string]any{ "id": "1", - "links": []interface{}{ - map[string]interface{}{ + "links": []any{ + map[string]any{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark", }, @@ -539,7 +539,7 @@ var ( ID: "2bdbc40f-a277-45d4-94ac-d9881c777d33", }, }, - SecurityGroups: []map[string]interface{}{ + SecurityGroups: []map[string]any{ { "name": "default", }, @@ -567,9 +567,9 @@ var ( Status: "ACTIVE", Updated: derpTimeUpdated, HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", - Addresses: map[string]interface{}{ - "private": []interface{}{ - map[string]interface{}{ + Addresses: map[string]any{ + "private": []any{ + map[string]any{ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", "version": float64(4), "addr": "10.0.0.31", @@ -577,29 +577,29 @@ var ( }, }, }, - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "bookmark", }, }, - Image: map[string]interface{}{ + Image: map[string]any{ "id": "f90f6034-2570-4974-8351-6b49732ef2eb", - "links": []interface{}{ - map[string]interface{}{ + "links": []any{ + map[string]any{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", "rel": "bookmark", }, }, }, - Flavor: map[string]interface{}{ + Flavor: map[string]any{ "id": "1", - "links": []interface{}{ - map[string]interface{}{ + "links": []any{ + map[string]any{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark", }, @@ -612,7 +612,7 @@ var ( TenantID: "fcad67a6189847c4aecfa3c81a05783b", Metadata: map[string]string{}, AttachedVolumes: []servers.AttachedVolume{}, - SecurityGroups: []map[string]interface{}{ + SecurityGroups: []map[string]any{ { "name": "default", }, @@ -642,9 +642,9 @@ var ( Status: "ACTIVE", Updated: merpTimeUpdated, HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", - Addresses: map[string]interface{}{ - "private": []interface{}{ - map[string]interface{}{ + Addresses: map[string]any{ + "private": []any{ + map[string]any{ "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", "version": float64(4), "addr": "10.0.0.31", @@ -652,21 +652,21 @@ var ( }, }, }, - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", "rel": "bookmark", }, }, Image: nil, - Flavor: map[string]interface{}{ + Flavor: map[string]any{ "id": "1", - "links": []interface{}{ - map[string]interface{}{ + "links": []any{ + map[string]any{ "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", "rel": "bookmark", }, @@ -679,7 +679,7 @@ var ( TenantID: "fcad67a6189847c4aecfa3c81a05783b", Metadata: map[string]string{}, AttachedVolumes: []servers.AttachedVolume{}, - SecurityGroups: []map[string]interface{}{ + SecurityGroups: []map[string]any{ { "name": "default", }, @@ -715,13 +715,13 @@ type CreateOptsWithCustomField struct { Foo string `json:"foo,omitempty"` } -func (opts CreateOptsWithCustomField) ToServerCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsWithCustomField) ToServerCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } - return map[string]interface{}{"server": b}, nil + return map[string]any{"server": b}, nil } // HandleServerNoNetworkCreationSuccessfully sets up the test server with no diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 15c6995010..b6ebefce5e 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -640,14 +640,14 @@ func TestCreateSchedulerHints(t *testing.T) { "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, - Query: []interface{}{"=", "$free_ram_mb", "1024"}, + Query: []any{"=", "$free_ram_mb", "1024"}, TargetCell: "foobar", DifferentCell: []string{ "bazbar", "barbaz", }, BuildNearHostIP: "192.168.1.1/24", - AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + AdditionalProperties: map[string]any{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } expected := ` @@ -690,14 +690,14 @@ func TestCreateComplexSchedulerHints(t *testing.T) { "a0cf03a5-d921-4877-bb5c-86d26cf818e1", "8c19174f-4220-44f0-824a-cd1eeef10287", }, - Query: []interface{}{"and", []string{"=", "$free_ram_mb", "1024"}, []string{"=", "$free_disk_mb", "204800"}}, + Query: []any{"and", []string{"=", "$free_ram_mb", "1024"}, []string{"=", "$free_disk_mb", "204800"}}, TargetCell: "foobar", DifferentCell: []string{ "bazbar", "barbaz", }, BuildNearHostIP: "192.168.1.1/24", - AdditionalProperties: map[string]interface{}{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, + AdditionalProperties: map[string]any{"reservation": "a0cf03a5-d921-4877-bb5c-86d26cf818e1"}, } expected := ` diff --git a/openstack/compute/v2/servers/testing/results_test.go b/openstack/compute/v2/servers/testing/results_test.go index 517d9e4868..eb751341fa 100644 --- a/openstack/compute/v2/servers/testing/results_test.go +++ b/openstack/compute/v2/servers/testing/results_test.go @@ -17,7 +17,7 @@ import ( // Fail - No password in JSON. func TestExtractPassword_no_pwd_data(t *testing.T) { - var dejson interface{} + var dejson any err := json.Unmarshal([]byte(`{ "Crappy data": ".-.-." }`), &dejson) if err != nil { t.Fatalf("%s", err) @@ -32,7 +32,7 @@ func TestExtractPassword_no_pwd_data(t *testing.T) { // Ok - return encrypted password when no private key is given. func TestExtractPassword_encrypted_pwd(t *testing.T) { - var dejson interface{} + var dejson any sejson := []byte(`{"password":"PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw=="}`) err := json.Unmarshal(sejson, &dejson) @@ -86,7 +86,7 @@ KSde3I0ybDz7iS2EtceKB7m4C0slYd+oBkm4efuF00rCOKDwpFq45m0= t.Fatalf("Error parsing private key: %s\n", err) } - var dejson interface{} + var dejson any sejson := []byte(`{"password":"PP8EnwPO9DhEc8+O/6CKAkPF379mKsUsfFY6yyw0734XXvKsSdV9KbiHQ2hrBvzeZxtGMrlFaikVunCRizyLLWLMuOi4hoH+qy9F9sQid61gQIGkxwDAt85d/7Eau2/KzorFnZhgxArl7IiqJ67X6xjKkR3zur+Yp3V/mtVIehpPYIaAvPbcp2t4mQXl1I9J8yrQfEZOctLL1L4heDEVXnxvNihVLK6pivlVggp6SZCtjj9cduZGrYGsxsOCso1dqJQr7GCojfwvuLOoG0OYwEGuWVTZppxWxi/q1QgeHFhGKA5QUXlz7pS71oqpjYsTeViuHnfvlqb5TVYZpQ1haw=="}`) err = json.Unmarshal(sejson, &dejson) diff --git a/openstack/compute/v2/services/requests.go b/openstack/compute/v2/services/requests.go index f979dd5dee..7f6ac1d6d8 100644 --- a/openstack/compute/v2/services/requests.go +++ b/openstack/compute/v2/services/requests.go @@ -65,7 +65,7 @@ type UpdateOpts struct { } // ToServiceUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToServiceUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/compute/v2/services/results.go b/openstack/compute/v2/services/results.go index 769acb8103..da2bde61d7 100644 --- a/openstack/compute/v2/services/results.go +++ b/openstack/compute/v2/services/results.go @@ -45,7 +45,7 @@ func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp - ID interface{} `json:"id"` + ID any `json:"id"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` } err := json.Unmarshal(b, &s) diff --git a/openstack/compute/v2/tags/requests.go b/openstack/compute/v2/tags/requests.go index 759b579d3d..c004d1f9ca 100644 --- a/openstack/compute/v2/tags/requests.go +++ b/openstack/compute/v2/tags/requests.go @@ -28,7 +28,7 @@ func Check(ctx context.Context, client *gophercloud.ServiceClient, serverID, tag // ReplaceAllOptsBuilder allows to add additional parameters to the ReplaceAll request. type ReplaceAllOptsBuilder interface { - ToTagsReplaceAllMap() (map[string]interface{}, error) + ToTagsReplaceAllMap() (map[string]any, error) } // ReplaceAllOpts provides options used to replace Tags on a server. @@ -37,7 +37,7 @@ type ReplaceAllOpts struct { } // ToTagsReplaceAllMap formats a ReplaceALlOpts into the body of the ReplaceAll request. -func (opts ReplaceAllOpts) ToTagsReplaceAllMap() (map[string]interface{}, error) { +func (opts ReplaceAllOpts) ToTagsReplaceAllMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/compute/v2/volumeattach/requests.go b/openstack/compute/v2/volumeattach/requests.go index 492dc09498..60d89ad7d2 100644 --- a/openstack/compute/v2/volumeattach/requests.go +++ b/openstack/compute/v2/volumeattach/requests.go @@ -17,7 +17,7 @@ func List(client *gophercloud.ServiceClient, serverID string) pagination.Pager { // CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { - ToVolumeAttachmentCreateMap() (map[string]interface{}, error) + ToVolumeAttachmentCreateMap() (map[string]any, error) } // CreateOpts specifies volume attachment creation or import parameters. @@ -39,7 +39,7 @@ type CreateOpts struct { } // ToVolumeAttachmentCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "volumeAttachment") } diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index 55e46f2da3..e890b5b5ce 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -204,12 +204,12 @@ func mergeClouds(override, cloud Cloud) (Cloud, error) { if err != nil { return Cloud{}, err } - var overrideInterface interface{} + var overrideInterface any err = json.Unmarshal(overrideJson, &overrideInterface) if err != nil { return Cloud{}, err } - var cloudInterface interface{} + var cloudInterface any err = json.Unmarshal(cloudJson, &cloudInterface) if err != nil { return Cloud{}, err @@ -229,10 +229,10 @@ func mergeClouds(override, cloud Cloud) (Cloud, error) { // 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{} { +func mergeInterfaces(overridingInterface, inferiorInterface any) any { switch overriding := overridingInterface.(type) { - case map[string]interface{}: - interfaceMap, ok := inferiorInterface.(map[string]interface{}) + case map[string]any: + interfaceMap, ok := inferiorInterface.(map[string]any) if !ok { return overriding } @@ -243,8 +243,8 @@ func mergeInterfaces(overridingInterface, inferiorInterface interface{}) interfa overriding[k] = v } } - case []interface{}: - list, ok := inferiorInterface.([]interface{}) + case []any: + list, ok := inferiorInterface.([]any) if !ok { return overriding } @@ -252,7 +252,7 @@ func mergeInterfaces(overridingInterface, inferiorInterface interface{}) interfa return append(overriding, list...) case nil: // mergeClouds(nil, map[string]interface{...}) -> map[string]interface{...} - v, ok := inferiorInterface.(map[string]interface{}) + v, ok := inferiorInterface.(map[string]any) if ok { return v } diff --git a/openstack/config/clouds/types.go b/openstack/config/clouds/types.go index c1914633ff..fc2da75925 100644 --- a/openstack/config/clouds/types.go +++ b/openstack/config/clouds/types.go @@ -161,7 +161,7 @@ func (r *Region) UnmarshalJSON(data []byte) error { // 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 { +func (r *Region) UnmarshalYAML(unmarshal func(any) error) error { var name string if err := unmarshal(&name); err == nil { r.Name = name diff --git a/openstack/container/v1/capsules/microversions.go b/openstack/container/v1/capsules/microversions.go index 6cdc04512c..07a26a92d5 100644 --- a/openstack/container/v1/capsules/microversions.go +++ b/openstack/container/v1/capsules/microversions.go @@ -49,7 +49,7 @@ type CapsuleV132 struct { // Links includes HTTP references to the itself, useful for passing along to // other APIs that might want a capsule reference. - Links []interface{} `json:"links"` + Links []any `json:"links"` // The capsule restart policy RestartPolicy map[string]string `json:"restart_policy"` diff --git a/openstack/container/v1/capsules/requests.go b/openstack/container/v1/capsules/requests.go index 7d572d4f30..101ef386bb 100644 --- a/openstack/container/v1/capsules/requests.go +++ b/openstack/container/v1/capsules/requests.go @@ -12,7 +12,7 @@ import ( // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { - ToCapsuleCreateMap() (map[string]interface{}, error) + ToCapsuleCreateMap() (map[string]any, error) } // ListOptsBuilder allows extensions to add additional parameters to the @@ -40,7 +40,7 @@ type CreateOpts struct { // ToCapsuleCreateMap assembles a request body based on the contents of // a CreateOpts. -func (opts CreateOpts) ToCapsuleCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToCapsuleCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/container/v1/capsules/results.go b/openstack/container/v1/capsules/results.go index 0c51b6e5b5..c417bb2038 100644 --- a/openstack/container/v1/capsules/results.go +++ b/openstack/container/v1/capsules/results.go @@ -22,9 +22,9 @@ func (r commonResult) ExtractBase() (*Capsule, error) { } // Extract is a function that accepts a result and extracts a capsule result. -// The result will be returned as an interface{} where it should be able to +// The result will be returned as an any where it should be able to // be casted as either a Capsule or CapsuleV132. -func (r commonResult) Extract() (interface{}, error) { +func (r commonResult) Extract() (any, error) { s, err := r.ExtractBase() if err == nil { return s, nil @@ -91,7 +91,7 @@ type Capsule struct { // Links includes HTTP references to the itself, useful for passing along to // other APIs that might want a capsule reference. - Links []interface{} `json:"links"` + Links []any `json:"links"` // The capsule version CapsuleVersion string `json:"capsule_version"` @@ -157,7 +157,7 @@ type Container struct { // Links includes HTTP references to the itself, useful for passing along to // other APIs that might want a capsule reference. - Links []interface{} `json:"links"` + Links []any `json:"links"` // auto remove flag token for the container AutoRemove bool `json:"auto_remove"` @@ -277,7 +277,7 @@ func ExtractCapsulesBase(r pagination.Page) ([]Capsule, error) { // and extracts the elements into an interface. // This interface should be able to be casted as either a Capsule or // CapsuleV132 struct -func ExtractCapsules(r pagination.Page) (interface{}, error) { +func ExtractCapsules(r pagination.Page) (any, error) { s, err := ExtractCapsulesBase(r) if err == nil { return s, nil diff --git a/openstack/container/v1/capsules/template.go b/openstack/container/v1/capsules/template.go index c55428fa46..b3b4446131 100644 --- a/openstack/container/v1/capsules/template.go +++ b/openstack/container/v1/capsules/template.go @@ -13,7 +13,7 @@ type Template struct { // Parsed contains a parsed version of Bin. Since there are 2 different // fields referring to the same value, you must be careful when accessing // this filed. - Parsed map[string]interface{} + Parsed map[string]any } // Parse will parse the contents and then validate. The contents MUST be either JSON or YAML. diff --git a/openstack/container/v1/capsules/testing/fixtures_test.go b/openstack/container/v1/capsules/testing/fixtures_test.go index 6e434f8027..fe7b008013 100644 --- a/openstack/container/v1/capsules/testing/fixtures_test.go +++ b/openstack/container/v1/capsules/testing/fixtures_test.go @@ -88,41 +88,41 @@ spec: ` // ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate -var ValidJSONTemplateParsed = map[string]interface{}{ +var ValidJSONTemplateParsed = map[string]any{ "capsuleVersion": "beta", "kind": "capsule", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "template", "labels": map[string]string{ "app": "web", "app1": "web1", }, }, - "spec": map[string]interface{}{ + "spec": map[string]any{ "restartPolicy": "Always", - "containers": []map[string]interface{}{ + "containers": []map[string]any{ { "image": "ubuntu", - "command": []interface{}{ + "command": []any{ "/bin/bash", }, "imagePullPolicy": "ifnotpresent", "workDir": "/root", - "ports": []interface{}{ - map[string]interface{}{ + "ports": []any{ + map[string]any{ "name": "nginx-port", "containerPort": float64(80), "hostPort": float64(80), "protocol": "TCP", }, }, - "resources": map[string]interface{}{ - "requests": map[string]interface{}{ + "resources": map[string]any{ + "requests": map[string]any{ "cpu": float64(1), "memory": float64(1024), }, }, - "env": map[string]interface{}{ + "env": map[string]any{ "ENV1": "/usr/local/bin", "ENV2": "/usr/bin", }, @@ -132,41 +132,41 @@ var ValidJSONTemplateParsed = map[string]interface{}{ } // ValidYAMLTemplateParsed is the expected parsed version of ValidYAMLTemplate -var ValidYAMLTemplateParsed = map[string]interface{}{ +var ValidYAMLTemplateParsed = map[string]any{ "capsuleVersion": "beta", "kind": "capsule", - "metadata": map[string]interface{}{ + "metadata": map[string]any{ "name": "template", "labels": map[string]string{ "app": "web", "app1": "web1", }, }, - "spec": map[interface{}]interface{}{ + "spec": map[any]any{ "restartPolicy": "Always", - "containers": []map[interface{}]interface{}{ + "containers": []map[any]any{ { "image": "ubuntu", - "command": []interface{}{ + "command": []any{ "/bin/bash", }, "imagePullPolicy": "ifnotpresent", "workDir": "/root", - "ports": []interface{}{ - map[interface{}]interface{}{ + "ports": []any{ + map[any]any{ "name": "nginx-port", "containerPort": 80, "hostPort": 80, "protocol": "TCP", }, }, - "resources": map[interface{}]interface{}{ - "requests": map[interface{}]interface{}{ + "resources": map[any]any{ + "requests": map[any]any{ "cpu": 1, "memory": 1024, }, }, - "env": map[interface{}]interface{}{ + "env": map[any]any{ "ENV1": "/usr/local/bin", "ENV2": "/usr/bin", }, @@ -517,12 +517,12 @@ var ExpectedCapsule = capsules.Capsule{ CPU: float64(1), Memory: "1024M", MetaName: "test", - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "bookmark", }, @@ -566,12 +566,12 @@ var ExpectedCapsuleV132 = capsules.CapsuleV132{ CPU: float64(1), Memory: "1024M", MetaName: "test", - Links: []interface{}{ - map[string]interface{}{ + Links: []any{ + map[string]any{ "href": "http://10.10.10.10/v1/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "self", }, - map[string]interface{}{ + map[string]any{ "href": "http://10.10.10.10/capsules/cc654059-1a77-47a3-bfcf-715bde5aad9e", "rel": "bookmark", }, diff --git a/openstack/containerinfra/v1/certificates/requests.go b/openstack/containerinfra/v1/certificates/requests.go index b98b0005b8..d538501482 100644 --- a/openstack/containerinfra/v1/certificates/requests.go +++ b/openstack/containerinfra/v1/certificates/requests.go @@ -9,7 +9,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters // to the Create request. type CreateOptsBuilder interface { - ToCertificateCreateMap() (map[string]interface{}, error) + ToCertificateCreateMap() (map[string]any, error) } // CreateOpts represents options used to create a certificate. @@ -20,7 +20,7 @@ type CreateOpts struct { } // ToCertificateCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToCertificateCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToCertificateCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 9ed06cc4f2..26a2ec16a9 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -9,7 +9,7 @@ import ( // CreateOptsBuilder Builder. type CreateOptsBuilder interface { - ToClusterCreateMap() (map[string]interface{}, error) + ToClusterCreateMap() (map[string]any, error) } // CreateOpts params @@ -33,7 +33,7 @@ type CreateOpts struct { } // ToClusterCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToClusterCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -132,26 +132,26 @@ const ( ) type UpdateOpts struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value interface{} `json:"value,omitempty"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value any `json:"value,omitempty"` } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToClustersUpdateMap() (map[string]interface{}, error) + ToClustersUpdateMap() (map[string]any, error) } // ToClusterUpdateMap assembles a request body based on the contents of // UpdateOpts. -func (opts UpdateOpts) ToClustersUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToClustersUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } // Update implements cluster updated request. func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { - var o []map[string]interface{} + var o []map[string]any for _, opt := range opts { b, err := opt.ToClustersUpdateMap() if err != nil { @@ -176,11 +176,11 @@ type UpgradeOpts struct { // UpgradeOptsBuilder allows extensions to add additional parameters to the // Upgrade request. type UpgradeOptsBuilder interface { - ToClustersUpgradeMap() (map[string]interface{}, error) + ToClustersUpgradeMap() (map[string]any, error) } // ToClustersUpgradeMap constructs a request body from UpgradeOpts. -func (opts UpgradeOpts) ToClustersUpgradeMap() (map[string]interface{}, error) { +func (opts UpgradeOpts) ToClustersUpgradeMap() (map[string]any, error) { if opts.MaxBatchSize == nil { defaultMaxBatchSize := 1 opts.MaxBatchSize = &defaultMaxBatchSize @@ -206,7 +206,7 @@ func Upgrade(ctx context.Context, client *gophercloud.ServiceClient, id string, // ResizeOptsBuilder allows extensions to add additional parameters to the // Resize request. type ResizeOptsBuilder interface { - ToClusterResizeMap() (map[string]interface{}, error) + ToClusterResizeMap() (map[string]any, error) } // ResizeOpts params @@ -217,7 +217,7 @@ type ResizeOpts struct { } // ToClusterResizeMap constructs a request body from ResizeOpts. -func (opts ResizeOpts) ToClusterResizeMap() (map[string]interface{}, error) { +func (opts ResizeOpts) ToClusterResizeMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 5f176c91a7..6c235dcd23 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -82,40 +82,40 @@ func (r ResizeResult) Extract() (string, error) { } type Cluster struct { - APIAddress string `json:"api_address"` - COEVersion string `json:"coe_version"` - ClusterTemplateID string `json:"cluster_template_id"` - ContainerVersion string `json:"container_version"` - CreateTimeout int `json:"create_timeout"` - CreatedAt time.Time `json:"created_at"` - DiscoveryURL string `json:"discovery_url"` - DockerVolumeSize int `json:"docker_volume_size"` - Faults map[string]string `json:"faults"` - 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"` - MasterCount int `json:"master_count"` - Name string `json:"name"` - NodeAddresses []string `json:"node_addresses"` - NodeCount int `json:"node_count"` - ProjectID string `json:"project_id"` - StackID string `json:"stack_id"` - Status string `json:"status"` - StatusReason string `json:"status_reason"` - UUID string `json:"uuid"` - UpdatedAt time.Time `json:"updated_at"` - UserID string `json:"user_id"` - FloatingIPEnabled bool `json:"floating_ip_enabled"` - FixedNetwork string `json:"fixed_network"` - FixedSubnet string `json:"fixed_subnet"` - HealthStatus string `json:"health_status"` - HealthStatusReason map[string]interface{} `json:"health_status_reason"` + APIAddress string `json:"api_address"` + COEVersion string `json:"coe_version"` + ClusterTemplateID string `json:"cluster_template_id"` + ContainerVersion string `json:"container_version"` + CreateTimeout int `json:"create_timeout"` + CreatedAt time.Time `json:"created_at"` + DiscoveryURL string `json:"discovery_url"` + DockerVolumeSize int `json:"docker_volume_size"` + Faults map[string]string `json:"faults"` + 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"` + MasterCount int `json:"master_count"` + Name string `json:"name"` + NodeAddresses []string `json:"node_addresses"` + NodeCount int `json:"node_count"` + ProjectID string `json:"project_id"` + StackID string `json:"stack_id"` + Status string `json:"status"` + StatusReason string `json:"status_reason"` + UUID string `json:"uuid"` + UpdatedAt time.Time `json:"updated_at"` + UserID string `json:"user_id"` + FloatingIPEnabled bool `json:"floating_ip_enabled"` + FixedNetwork string `json:"fixed_network"` + FixedSubnet string `json:"fixed_subnet"` + HealthStatus string `json:"health_status"` + HealthStatusReason map[string]any `json:"health_status_reason"` } type ClusterPage struct { diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures_test.go b/openstack/containerinfra/v1/clusters/testing/fixtures_test.go index c50b3495fd..640533e2c8 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures_test.go @@ -53,7 +53,7 @@ var ExpectedCluster = clusters.Cluster{ FixedNetwork: "private_network", FixedSubnet: "private_subnet", HealthStatus: "HEALTHY", - HealthStatusReason: map[string]interface{}{"api": "ok"}, + HealthStatusReason: map[string]any{"api": "ok"}, } var ExpectedCluster2 = clusters.Cluster{ @@ -88,7 +88,7 @@ var ExpectedCluster2 = clusters.Cluster{ FixedNetwork: "private_network", FixedSubnet: "private_subnet", HealthStatus: "HEALTHY", - HealthStatusReason: map[string]interface{}{"api": "ok"}, + HealthStatusReason: map[string]any{"api": "ok"}, } var ExpectedClusterUUID = clusterUUID diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index 6d88e6f948..3421317523 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -9,7 +9,7 @@ import ( // CreateOptsBuilder Builder. type CreateOptsBuilder interface { - ToClusterCreateMap() (map[string]interface{}, error) + ToClusterCreateMap() (map[string]any, error) } // CreateOpts params @@ -44,7 +44,7 @@ type CreateOpts struct { } // ToClusterCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToClusterCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToClusterCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -125,20 +125,20 @@ const ( ) type UpdateOpts struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value interface{} `json:"value,omitempty"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value any `json:"value,omitempty"` } // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToClusterTemplateUpdateMap() (map[string]interface{}, error) + ToClusterTemplateUpdateMap() (map[string]any, error) } // ToClusterUpdateMap assembles a request body based on the contents of // UpdateOpts. -func (opts UpdateOpts) ToClusterTemplateUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToClusterTemplateUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -149,7 +149,7 @@ func (opts UpdateOpts) ToClusterTemplateUpdateMap() (map[string]interface{}, err // Update implements cluster updated request. func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { - var o []map[string]interface{} + var o []map[string]any for _, opt := range opts { b, err := opt.ToClusterTemplateUpdateMap() if err != nil { diff --git a/openstack/containerinfra/v1/nodegroups/requests.go b/openstack/containerinfra/v1/nodegroups/requests.go index 91686bff80..d2f4dc8789 100644 --- a/openstack/containerinfra/v1/nodegroups/requests.go +++ b/openstack/containerinfra/v1/nodegroups/requests.go @@ -69,7 +69,7 @@ func List(client *gophercloud.ServiceClient, clusterID string, opts ListOptsBuil } type CreateOptsBuilder interface { - ToNodeGroupCreateMap() (map[string]interface{}, error) + ToNodeGroupCreateMap() (map[string]any, error) } // CreateOpts is used to set available fields upon node group creation. @@ -93,7 +93,7 @@ type CreateOpts struct { MergeLabels *bool `json:"merge_labels,omitempty"` } -func (opts CreateOpts) ToNodeGroupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToNodeGroupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -113,7 +113,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, clusterID st } type UpdateOptsBuilder interface { - ToResourceUpdateMap() (map[string]interface{}, error) + ToResourceUpdateMap() (map[string]any, error) } type UpdateOp string @@ -129,12 +129,12 @@ const ( // Valid Ops are "add", "remove", "replace" // Valid Paths are "/min_node_count" and "/max_node_count" type UpdateOpts struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value interface{} `json:"value,omitempty"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value any `json:"value,omitempty"` } -func (opts UpdateOpts) ToResourceUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToResourceUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -144,7 +144,7 @@ func (opts UpdateOpts) ToResourceUpdateMap() (map[string]interface{}, error) { // Use the Extract method of the returned UpdateResult to extract the // updated node group from the result. func Update(ctx context.Context, client *gophercloud.ServiceClient, clusterID string, nodeGroupID string, opts []UpdateOptsBuilder) (r UpdateResult) { - var o []map[string]interface{} + var o []map[string]any for _, opt := range opts { b, err := opt.ToResourceUpdateMap() if err != nil { diff --git a/openstack/containerinfra/v1/quotas/requests.go b/openstack/containerinfra/v1/quotas/requests.go index 274aaaa775..14ebca4c84 100644 --- a/openstack/containerinfra/v1/quotas/requests.go +++ b/openstack/containerinfra/v1/quotas/requests.go @@ -8,7 +8,7 @@ import ( // CreateOptsBuilder Builder. type CreateOptsBuilder interface { - ToQuotaCreateMap() (map[string]interface{}, error) + ToQuotaCreateMap() (map[string]any, error) } // CreateOpts params @@ -19,7 +19,7 @@ type CreateOpts struct { } // ToQuotaCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToQuotaCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToQuotaCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/containerinfra/v1/quotas/results.go b/openstack/containerinfra/v1/quotas/results.go index ab9e67912a..540850fa08 100644 --- a/openstack/containerinfra/v1/quotas/results.go +++ b/openstack/containerinfra/v1/quotas/results.go @@ -37,7 +37,7 @@ func (r *Quotas) UnmarshalJSON(b []byte) error { type tmp Quotas var s struct { tmp - ID interface{} `json:"id"` + ID any `json:"id"` } err := json.Unmarshal(b, &s) diff --git a/openstack/db/v1/configurations/requests.go b/openstack/db/v1/configurations/requests.go index 18066ffe30..13b9c85dcb 100644 --- a/openstack/db/v1/configurations/requests.go +++ b/openstack/db/v1/configurations/requests.go @@ -17,7 +17,7 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // CreateOptsBuilder is a top-level interface which renders a JSON map. type CreateOptsBuilder interface { - ToConfigCreateMap() (map[string]interface{}, error) + ToConfigCreateMap() (map[string]any, error) } // DatastoreOpts is the primary options struct for creating and modifying @@ -36,7 +36,7 @@ type CreateOpts struct { // A map of user-defined configuration settings that will define // how each associated datastore works. Each key/value pair is specific to a // datastore type. - Values map[string]interface{} `json:"values" required:"true"` + Values map[string]any `json:"values" required:"true"` // Associates the configuration group with a particular datastore. Datastore *DatastoreOpts `json:"datastore,omitempty"` // A human-readable explanation for the group. @@ -44,7 +44,7 @@ type CreateOpts struct { } // ToConfigCreateMap casts a CreateOpts struct into a JSON map. -func (opts CreateOpts) ToConfigCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToConfigCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "configuration") } @@ -70,7 +70,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, configID string // UpdateOptsBuilder is the top-level interface for casting update options into // JSON maps. type UpdateOptsBuilder interface { - ToConfigUpdateMap() (map[string]interface{}, error) + ToConfigUpdateMap() (map[string]any, error) } // UpdateOpts is the struct responsible for modifying existing configurations. @@ -80,7 +80,7 @@ type UpdateOpts struct { // A map of user-defined configuration settings that will define // how each associated datastore works. Each key/value pair is specific to a // datastore type. - Values map[string]interface{} `json:"values,omitempty"` + Values map[string]any `json:"values,omitempty"` // Associates the configuration group with a particular datastore. Datastore *DatastoreOpts `json:"datastore,omitempty"` // A human-readable explanation for the group. @@ -88,7 +88,7 @@ type UpdateOpts struct { } // ToConfigUpdateMap will cast an UpdateOpts struct into a JSON map. -func (opts UpdateOpts) ToConfigUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToConfigUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "configuration") } diff --git a/openstack/db/v1/configurations/results.go b/openstack/db/v1/configurations/results.go index 2c6588f6df..97d63b1359 100644 --- a/openstack/db/v1/configurations/results.go +++ b/openstack/db/v1/configurations/results.go @@ -18,7 +18,7 @@ type Config struct { Description string ID string Name string - Values map[string]interface{} + Values map[string]any } func (r *Config) UnmarshalJSON(b []byte) error { diff --git a/openstack/db/v1/configurations/testing/fixtures_test.go b/openstack/db/v1/configurations/testing/fixtures_test.go index 12e6819d4c..10c8362d3e 100644 --- a/openstack/db/v1/configurations/testing/fixtures_test.go +++ b/openstack/db/v1/configurations/testing/fixtures_test.go @@ -153,7 +153,7 @@ var ExampleConfigWithValues = configurations.Config{ ID: "005a8bb7-a8df-40ee-b0b7-fc144641abc2", Name: "example-configuration-name", Updated: timeVal, - Values: map[string]interface{}{ + Values: map[string]any{ "collation_server": "latin1_swedish_ci", "connect_timeout": float64(120), }, diff --git a/openstack/db/v1/configurations/testing/requests_test.go b/openstack/db/v1/configurations/testing/requests_test.go index 4694e0b73d..49d928e94e 100644 --- a/openstack/db/v1/configurations/testing/requests_test.go +++ b/openstack/db/v1/configurations/testing/requests_test.go @@ -69,7 +69,7 @@ func TestCreate(t *testing.T) { }, Description: "example description", Name: "example-configuration-name", - Values: map[string]interface{}{ + Values: map[string]any{ "collation_server": "latin1_swedish_ci", "connect_timeout": 120, }, @@ -86,7 +86,7 @@ func TestUpdate(t *testing.T) { fixture.SetupHandler(t, resURL, "PATCH", UpdateReq, "", 200) opts := configurations.UpdateOpts{ - Values: map[string]interface{}{ + Values: map[string]any{ "connect_timeout": 300, }, } @@ -101,7 +101,7 @@ func TestReplace(t *testing.T) { fixture.SetupHandler(t, resURL, "PUT", UpdateReq, "", 202) opts := configurations.UpdateOpts{ - Values: map[string]interface{}{ + Values: map[string]any{ "connect_timeout": 300, }, } diff --git a/openstack/db/v1/databases/requests.go b/openstack/db/v1/databases/requests.go index bea255e48c..422d88fc2c 100644 --- a/openstack/db/v1/databases/requests.go +++ b/openstack/db/v1/databases/requests.go @@ -9,7 +9,7 @@ import ( // CreateOptsBuilder builds create options type CreateOptsBuilder interface { - ToDBCreateMap() (map[string]interface{}, error) + ToDBCreateMap() (map[string]any, error) } // CreateOpts is the struct responsible for configuring a database; often in @@ -35,7 +35,7 @@ type CreateOpts struct { // ToMap is a helper function to convert individual DB create opt structures // into sub-maps. -func (opts CreateOpts) ToMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToMap() (map[string]any, error) { if len(opts.Name) > 64 { err := gophercloud.ErrInvalidInput{} err.Argument = "databases.CreateOpts.Name" @@ -50,8 +50,8 @@ func (opts CreateOpts) ToMap() (map[string]interface{}, error) { type BatchCreateOpts []CreateOpts // ToDBCreateMap renders a JSON map for creating DBs. -func (opts BatchCreateOpts) ToDBCreateMap() (map[string]interface{}, error) { - dbs := make([]map[string]interface{}, len(opts)) +func (opts BatchCreateOpts) ToDBCreateMap() (map[string]any, error) { + dbs := make([]map[string]any, len(opts)) for i, db := range opts { dbMap, err := db.ToMap() if err != nil { @@ -59,7 +59,7 @@ func (opts BatchCreateOpts) ToDBCreateMap() (map[string]interface{}, error) { } dbs[i] = dbMap } - return map[string]interface{}{"databases": dbs}, nil + return map[string]any{"databases": dbs}, nil } // Create will create a new database within the specified instance. If the diff --git a/openstack/db/v1/instances/requests.go b/openstack/db/v1/instances/requests.go index cc8953bec3..b4e946025f 100644 --- a/openstack/db/v1/instances/requests.go +++ b/openstack/db/v1/instances/requests.go @@ -11,7 +11,7 @@ import ( // CreateOptsBuilder is the top-level interface for create options. type CreateOptsBuilder interface { - ToInstanceCreateMap() (map[string]interface{}, error) + ToInstanceCreateMap() (map[string]any, error) } // DatastoreOpts represents the configuration for how an instance stores data. @@ -21,7 +21,7 @@ type DatastoreOpts struct { } // ToMap converts a DatastoreOpts to a map[string]string (for a request body) -func (opts DatastoreOpts) ToMap() (map[string]interface{}, error) { +func (opts DatastoreOpts) ToMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -43,7 +43,7 @@ type NetworkOpts struct { } // ToMap converts a NetworkOpts to a map[string]string (for a request body) -func (opts NetworkOpts) ToMap() (map[string]interface{}, error) { +func (opts NetworkOpts) ToMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -76,7 +76,7 @@ type CreateOpts struct { } // ToInstanceCreateMap will render a JSON map. -func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToInstanceCreateMap() (map[string]any, error) { if opts.Size > 300 || opts.Size < 1 { err := gophercloud.ErrInvalidInput{} err.Argument = "instances.CreateOpts.Size" @@ -89,7 +89,7 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { return nil, gophercloud.ErrMissingInput{Argument: "instances.CreateOpts.FlavorRef"} } - instance := map[string]interface{}{ + instance := map[string]any{ "flavorRef": opts.FlavorRef, } @@ -127,7 +127,7 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { } if len(opts.Networks) > 0 { - networks := make([]map[string]interface{}, len(opts.Networks)) + networks := make([]map[string]any, len(opts.Networks)) for i, net := range opts.Networks { var err error networks[i], err = net.ToMap() @@ -138,7 +138,7 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { instance["nics"] = networks } - volume := map[string]interface{}{ + volume := map[string]any{ "size": opts.Size, } @@ -148,7 +148,7 @@ func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) { instance["volume"] = volume - return map[string]interface{}{"instance": instance}, nil + return map[string]any{"instance": instance}, nil } // Create asynchronously provisions a new database instance. It requires the @@ -212,7 +212,7 @@ func IsRootEnabled(ctx context.Context, client *gophercloud.ServiceClient, id st // erase any dynamic configuration settings that you have made within MySQL. // The MySQL service will be unavailable until the instance restarts. func Restart(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ActionResult) { - b := map[string]interface{}{"restart": struct{}{}} + b := map[string]any{"restart": struct{}{}} resp, err := client.Post(ctx, actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return @@ -221,7 +221,7 @@ func Restart(ctx context.Context, client *gophercloud.ServiceClient, id string) // Resize changes the memory size of the instance, assuming a valid // flavorRef is provided. It will also restart the MySQL service. func Resize(ctx context.Context, client *gophercloud.ServiceClient, id, flavorRef string) (r ActionResult) { - b := map[string]interface{}{"resize": map[string]string{"flavorRef": flavorRef}} + b := map[string]any{"resize": map[string]string{"flavorRef": flavorRef}} resp, err := client.Post(ctx, actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return @@ -231,7 +231,7 @@ func Resize(ctx context.Context, client *gophercloud.ServiceClient, id, flavorRe // only increasing the volume size and does not support decreasing the size. // The volume size is in gigabytes (GB) and must be an integer. func ResizeVolume(ctx context.Context, client *gophercloud.ServiceClient, id string, size int) (r ActionResult) { - b := map[string]interface{}{"resize": map[string]interface{}{"volume": map[string]int{"size": size}}} + b := map[string]any{"resize": map[string]any{"volume": map[string]int{"size": size}}} resp, err := client.Post(ctx, actionURL(client, id), &b, nil, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return @@ -239,7 +239,7 @@ func ResizeVolume(ctx context.Context, client *gophercloud.ServiceClient, id str // AttachConfigurationGroup will attach configuration group to the instance func AttachConfigurationGroup(ctx context.Context, client *gophercloud.ServiceClient, instanceID string, configID string) (r ConfigurationResult) { - b := map[string]interface{}{"instance": map[string]interface{}{"configuration": configID}} + b := map[string]any{"instance": map[string]any{"configuration": configID}} resp, err := client.Put(ctx, resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return @@ -247,7 +247,7 @@ func AttachConfigurationGroup(ctx context.Context, client *gophercloud.ServiceCl // DetachConfigurationGroup will dettach configuration group from the instance func DetachConfigurationGroup(ctx context.Context, client *gophercloud.ServiceClient, instanceID string) (r ConfigurationResult) { - b := map[string]interface{}{"instance": map[string]interface{}{}} + b := map[string]any{"instance": map[string]any{}} resp, err := client.Put(ctx, resourceURL(client, instanceID), &b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/db/v1/instances/results.go b/openstack/db/v1/instances/results.go index e66a86abca..d8903e2361 100644 --- a/openstack/db/v1/instances/results.go +++ b/openstack/db/v1/instances/results.go @@ -232,5 +232,5 @@ type IsRootEnabledResult struct { // Extract is used to extract the data from a IsRootEnabledResult. func (r IsRootEnabledResult) Extract() (bool, error) { - return r.Body.(map[string]interface{})["rootEnabled"] == true, r.Err + return r.Body.(map[string]any)["rootEnabled"] == true, r.Err } diff --git a/openstack/db/v1/users/requests.go b/openstack/db/v1/users/requests.go index d83bdc3c57..5958c64719 100644 --- a/openstack/db/v1/users/requests.go +++ b/openstack/db/v1/users/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder is the top-level interface for creating JSON maps. type CreateOptsBuilder interface { - ToUserCreateMap() (map[string]interface{}, error) + ToUserCreateMap() (map[string]any, error) } // CreateOpts is the struct responsible for configuring a new user; often in the @@ -36,7 +36,7 @@ type CreateOpts struct { } // ToMap is a convenience function for creating sub-maps for individual users. -func (opts CreateOpts) ToMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToMap() (map[string]any, error) { if opts.Name == "root" { err := gophercloud.ErrInvalidInput{} err.Argument = "users.CreateOpts.Name" @@ -51,8 +51,8 @@ func (opts CreateOpts) ToMap() (map[string]interface{}, error) { type BatchCreateOpts []CreateOpts // ToUserCreateMap will generate a JSON map. -func (opts BatchCreateOpts) ToUserCreateMap() (map[string]interface{}, error) { - users := make([]map[string]interface{}, len(opts)) +func (opts BatchCreateOpts) ToUserCreateMap() (map[string]any, error) { + users := make([]map[string]any, len(opts)) for i, opt := range opts { user, err := opt.ToMap() if err != nil { @@ -60,7 +60,7 @@ func (opts BatchCreateOpts) ToUserCreateMap() (map[string]interface{}, error) { } users[i] = user } - return map[string]interface{}{"users": users}, nil + return map[string]any{"users": users}, nil } // Create asynchronously provisions a new user for the specified database diff --git a/openstack/dns/v2/recordsets/requests.go b/openstack/dns/v2/recordsets/requests.go index a89bc8134a..49e629b393 100644 --- a/openstack/dns/v2/recordsets/requests.go +++ b/openstack/dns/v2/recordsets/requests.go @@ -67,7 +67,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, zoneID string, // CreateOptsBuilder allows extensions to add additional attributes to the // Create request. type CreateOptsBuilder interface { - ToRecordSetCreateMap() (map[string]interface{}, error) + ToRecordSetCreateMap() (map[string]any, error) } // CreateOpts specifies the base attributes that may be used to create a @@ -90,7 +90,7 @@ type CreateOpts struct { } // ToRecordSetCreateMap formats an CreateOpts structure into a request body. -func (opts CreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToRecordSetCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -116,7 +116,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, zoneID strin // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { - ToRecordSetUpdateMap() (map[string]interface{}, error) + ToRecordSetUpdateMap() (map[string]any, error) } // UpdateOpts specifies the base attributes that may be updated on an existing @@ -133,7 +133,7 @@ type UpdateOpts struct { } // ToRecordSetUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToRecordSetUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToRecordSetUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/dns/v2/recordsets/results.go b/openstack/dns/v2/recordsets/results.go index c0593e1de9..4478088c45 100644 --- a/openstack/dns/v2/recordsets/results.go +++ b/openstack/dns/v2/recordsets/results.go @@ -129,7 +129,7 @@ func (r *RecordSet) UnmarshalJSON(b []byte) error { tmp CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } err := json.Unmarshal(b, &s) if err != nil { diff --git a/openstack/dns/v2/recordsets/testing/requests_test.go b/openstack/dns/v2/recordsets/testing/requests_test.go index 05dde60ed3..0c2a51b219 100644 --- a/openstack/dns/v2/recordsets/testing/requests_test.go +++ b/openstack/dns/v2/recordsets/testing/requests_test.go @@ -75,7 +75,7 @@ func TestGet(t *testing.T) { func TestNextPageURL(t *testing.T) { var page recordsets.RecordSetPage - var body map[string]interface{} + var body map[string]any err := json.Unmarshal([]byte(NextPageRequest), &body) if err != nil { t.Fatalf("Error unmarshaling data into page body: %v", err) diff --git a/openstack/dns/v2/transfer/accept/requests.go b/openstack/dns/v2/transfer/accept/requests.go index ff4112dc29..089e3cabab 100644 --- a/openstack/dns/v2/transfer/accept/requests.go +++ b/openstack/dns/v2/transfer/accept/requests.go @@ -52,7 +52,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, transferAcceptI // CreateOptsBuilder allows extensions to add additional attributes to the // Create request. type CreateOptsBuilder interface { - ToTransferAcceptCreateMap() (map[string]interface{}, error) + ToTransferAcceptCreateMap() (map[string]any, error) } // CreateOpts specifies the attributes used to create a transfer accept. @@ -66,7 +66,7 @@ type CreateOpts struct { } // ToTransferAcceptCreateMap formats an CreateOpts structure into a request body. -func (opts CreateOpts) ToTransferAcceptCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToTransferAcceptCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/dns/v2/transfer/accept/results.go b/openstack/dns/v2/transfer/accept/results.go index 7c54e73503..5824f3910e 100644 --- a/openstack/dns/v2/transfer/accept/results.go +++ b/openstack/dns/v2/transfer/accept/results.go @@ -85,7 +85,7 @@ type TransferAccept struct { // Links includes HTTP references to the itself, useful for passing along // to other APIs that might want a server reference. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } func (r *TransferAccept) UnmarshalJSON(b []byte) error { diff --git a/openstack/dns/v2/transfer/accept/testing/fixtures_test.go b/openstack/dns/v2/transfer/accept/testing/fixtures_test.go index e52b08576f..b8c3fe5888 100644 --- a/openstack/dns/v2/transfer/accept/testing/fixtures_test.go +++ b/openstack/dns/v2/transfer/accept/testing/fixtures_test.go @@ -97,7 +97,7 @@ var FirstTransferAccept = transferAccepts.TransferAccept{ Key: "M2KA0Y20", Status: "COMPLETE", CreatedAt: FirstTransferAcceptCreatedAt, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/92236f39-0fad-4f8f-bf25-fbdf027de34d", "zone": "https://127.0.0.1:9001/v2/zones/cd046f4b-f4dc-4e41-b946-1a2d32e1be40", }, @@ -115,7 +115,7 @@ var SecondTransferAccept = transferAccepts.TransferAccept{ Key: "SDF32HJ1", CreatedAt: SecondTransferAcceptCreatedAt, UpdatedAt: SecondTransferAcceptUpdatedAt, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_accepts/f785ef12-7ee0-4c30-bd67-a2b9edba0dff", "zone": "https://127.0.0.1:9001/v2/zones/30d67a9a-d6df-4ba7-9b55-fb49e7987f84", }, diff --git a/openstack/dns/v2/transfer/request/requests.go b/openstack/dns/v2/transfer/request/requests.go index 55c4823591..436cc8e671 100644 --- a/openstack/dns/v2/transfer/request/requests.go +++ b/openstack/dns/v2/transfer/request/requests.go @@ -52,7 +52,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, transferRequest // CreateOptsBuilder allows extensions to add additional attributes to the // Create request. type CreateOptsBuilder interface { - ToTransferRequestCreateMap() (map[string]interface{}, error) + ToTransferRequestCreateMap() (map[string]any, error) } // CreateOpts specifies the attributes used to create a transfer request. @@ -66,7 +66,7 @@ type CreateOpts struct { } // ToTransferRequestCreateMap formats an CreateOpts structure into a request body. -func (opts CreateOpts) ToTransferRequestCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToTransferRequestCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -91,7 +91,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, zoneID strin // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { - ToTransferRequestUpdateMap() (map[string]interface{}, error) + ToTransferRequestUpdateMap() (map[string]any, error) } // UpdateOpts specifies the attributes to update a transfer request. @@ -105,7 +105,7 @@ type UpdateOpts struct { } // ToTransferRequestUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToTransferRequestUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToTransferRequestUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/dns/v2/transfer/request/results.go b/openstack/dns/v2/transfer/request/results.go index e56ffc7ef4..eb25a6454a 100644 --- a/openstack/dns/v2/transfer/request/results.go +++ b/openstack/dns/v2/transfer/request/results.go @@ -104,7 +104,7 @@ type TransferRequest struct { // Links includes HTTP references to the itself, useful for passing along // to other APIs that might want a server reference. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } func (r *TransferRequest) UnmarshalJSON(b []byte) error { diff --git a/openstack/dns/v2/transfer/request/testing/fixtures_test.go b/openstack/dns/v2/transfer/request/testing/fixtures_test.go index a0460bf643..758b566604 100644 --- a/openstack/dns/v2/transfer/request/testing/fixtures_test.go +++ b/openstack/dns/v2/transfer/request/testing/fixtures_test.go @@ -83,7 +83,7 @@ var FirstTransferRequest = transferRequests.TransferRequest{ Description: "This is a first example zone transfer request.", Status: "ACTIVE", CreatedAt: FirstTransferRequestCreatedAt, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", }, } @@ -102,7 +102,7 @@ var SecondTransferRequest = transferRequests.TransferRequest{ Status: "ACTIVE", CreatedAt: SecondTransferRequestCreatedAt, UpdatedAt: SecondTransferRequestUpdatedAt, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://127.0.0.1:9001/v2/zones/tasks/transfer_requests/34c4561c-9205-4386-9df5-167436f5a222", }, } diff --git a/openstack/dns/v2/zones/requests.go b/openstack/dns/v2/zones/requests.go index aa6844f600..fba4371ced 100644 --- a/openstack/dns/v2/zones/requests.go +++ b/openstack/dns/v2/zones/requests.go @@ -65,7 +65,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, zoneID string) // CreateOptsBuilder allows extensions to add additional attributes to the // Create request. type CreateOptsBuilder interface { - ToZoneCreateMap() (map[string]interface{}, error) + ToZoneCreateMap() (map[string]any, error) } // CreateOpts specifies the attributes used to create a zone. @@ -93,7 +93,7 @@ type CreateOpts struct { } // ToZoneCreateMap formats an CreateOpts structure into a request body. -func (opts CreateOpts) ToZoneCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToZoneCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -123,7 +123,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { - ToZoneUpdateMap() (map[string]interface{}, error) + ToZoneUpdateMap() (map[string]any, error) } // UpdateOpts specifies the attributes to update a zone. @@ -142,7 +142,7 @@ type UpdateOpts struct { } // ToZoneUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToZoneUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToZoneUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/dns/v2/zones/results.go b/openstack/dns/v2/zones/results.go index 7321880aca..ae2f27dbc5 100644 --- a/openstack/dns/v2/zones/results.go +++ b/openstack/dns/v2/zones/results.go @@ -128,7 +128,7 @@ type Zone struct { // Links includes HTTP references to the itself, useful for passing along // to other APIs that might want a server reference. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } func (r *Zone) UnmarshalJSON(b []byte) error { @@ -138,7 +138,7 @@ func (r *Zone) UnmarshalJSON(b []byte) error { CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` TransferredAt gophercloud.JSONRFC3339MilliNoZ `json:"transferred_at"` - Serial interface{} `json:"serial"` + Serial any `json:"serial"` } err := json.Unmarshal(b, &s) if err != nil { diff --git a/openstack/dns/v2/zones/testing/fixtures_test.go b/openstack/dns/v2/zones/testing/fixtures_test.go index 047b78080b..d7d9120643 100644 --- a/openstack/dns/v2/zones/testing/fixtures_test.go +++ b/openstack/dns/v2/zones/testing/fixtures_test.go @@ -110,7 +110,7 @@ var FirstZone = zones.Zone{ Type: "PRIMARY", Version: 1, CreatedAt: FirstZoneCreatedAt, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3", }, } @@ -133,7 +133,7 @@ var SecondZone = zones.Zone{ Version: 1, CreatedAt: SecondZoneCreatedAt, UpdatedAt: SecondZoneUpdatedAt, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://127.0.0.1:9001/v2/zones/34c4561c-9205-4386-9df5-167436f5a222", }, } diff --git a/openstack/identity/v2/tenants/requests.go b/openstack/identity/v2/tenants/requests.go index 4a986b890a..a08980df2c 100644 --- a/openstack/identity/v2/tenants/requests.go +++ b/openstack/identity/v2/tenants/requests.go @@ -46,12 +46,12 @@ type CreateOpts struct { // CreateOptsBuilder enables extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToTenantCreateMap() (map[string]interface{}, error) + ToTenantCreateMap() (map[string]any, error) } // ToTenantCreateMap assembles a request body based on the contents of // a CreateOpts. -func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToTenantCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "tenant") } @@ -79,7 +79,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToTenantUpdateMap() (map[string]interface{}, error) + ToTenantUpdateMap() (map[string]any, error) } // UpdateOpts specifies the base attributes that may be updated on an existing @@ -96,7 +96,7 @@ type UpdateOpts struct { } // ToTenantUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToTenantUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "tenant") } diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 03b602ca42..59304fb246 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -41,7 +41,7 @@ type AuthOptionsV2 struct { type AuthOptionsBuilder interface { // ToTokenCreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. - ToTokenV2CreateMap() (map[string]interface{}, error) + ToTokenV2CreateMap() (map[string]any, error) } // AuthOptions are the valid options for Openstack Identity v2 authentication. @@ -57,7 +57,7 @@ type AuthOptions struct { } // ToTokenV2CreateMap builds a token request body from the given AuthOptions. -func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { +func (opts AuthOptions) ToTokenV2CreateMap() (map[string]any, error) { v2Opts := AuthOptionsV2{ TenantID: opts.TenantID, TenantName: opts.TenantName, diff --git a/openstack/identity/v2/users/requests.go b/openstack/identity/v2/users/requests.go index 4f47a7eed9..1d6f4c3288 100644 --- a/openstack/identity/v2/users/requests.go +++ b/openstack/identity/v2/users/requests.go @@ -39,12 +39,12 @@ type CreateOpts CommonOpts // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToUserCreateMap() (map[string]interface{}, error) + ToUserCreateMap() (map[string]any, error) } // ToUserCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToUserCreateMap() (map[string]any, error) { if opts.Name == "" && opts.Username == "" { err := gophercloud.ErrMissingInput{} err.Argument = "users.CreateOpts.Name/users.CreateOpts.Username" @@ -78,7 +78,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToUserUpdateMap() (map[string]interface{}, error) + ToUserUpdateMap() (map[string]any, error) } // UpdateOpts specifies the base attributes that may be updated on an @@ -86,7 +86,7 @@ type UpdateOptsBuilder interface { type UpdateOpts CommonOpts // ToUserUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToUserUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "user") } diff --git a/openstack/identity/v3/applicationcredentials/requests.go b/openstack/identity/v3/applicationcredentials/requests.go index bb8d3b4d3b..5619229f59 100644 --- a/openstack/identity/v3/applicationcredentials/requests.go +++ b/openstack/identity/v3/applicationcredentials/requests.go @@ -51,7 +51,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, userID string, // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToApplicationCredentialCreateMap() (map[string]interface{}, error) + ToApplicationCredentialCreateMap() (map[string]any, error) } // CreateOpts provides options used to create an application credential. @@ -77,7 +77,7 @@ type CreateOpts struct { } // ToApplicationCredentialCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToApplicationCredentialCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToApplicationCredentialCreateMap() (map[string]any, error) { parent := "application_credential" b, err := gophercloud.BuildRequestBody(opts, parent) if err != nil { @@ -85,7 +85,7 @@ func (opts CreateOpts) ToApplicationCredentialCreateMap() (map[string]interface{ } if opts.ExpiresAt != nil { - if v, ok := b[parent].(map[string]interface{}); ok { + if v, ok := b[parent].(map[string]any); ok { v["expires_at"] = opts.ExpiresAt.Format(gophercloud.RFC3339MilliNoZ) } } diff --git a/openstack/identity/v3/applicationcredentials/results.go b/openstack/identity/v3/applicationcredentials/results.go index d74e218d4b..c1745e7bec 100644 --- a/openstack/identity/v3/applicationcredentials/results.go +++ b/openstack/identity/v3/applicationcredentials/results.go @@ -56,7 +56,7 @@ type ApplicationCredential struct { // A list of access rules objects. AccessRules []AccessRule `json:"access_rules,omitempty"` // Links contains referencing links to the application credential. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } func (r *ApplicationCredential) UnmarshalJSON(b []byte) error { diff --git a/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go b/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go index 6ddb513810..fabb021a0c 100644 --- a/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go +++ b/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go @@ -301,7 +301,7 @@ var ApplicationCredential = applicationcredentials.ApplicationCredential{ }, }, ExpiresAt: nilTime, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/f741662395b249c9b8acdebf1722c5ae", }, } @@ -320,7 +320,7 @@ var ApplicationCredentialNoSecretResponse = applicationcredentials.ApplicationCr }, }, ExpiresAt: nilTime, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7", }, } @@ -344,7 +344,7 @@ var UnrestrictedApplicationCredential = applicationcredentials.ApplicationCreden }, }, ExpiresAt: ApplationCredentialExpiresAt, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb", }, } @@ -363,7 +363,7 @@ var FirstApplicationCredential = applicationcredentials.ApplicationCredential{ }, }, ExpiresAt: nilTime, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/c4859fb437df4b87a51a8f5adcfb0bc7", }, } @@ -386,7 +386,7 @@ var SecondApplicationCredential = applicationcredentials.ApplicationCredential{ }, }, ExpiresAt: ApplationCredentialExpiresAt, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://identity/v3/users/2844b2a08be147a08ef58317d6471f1f/application_credentials/6b8cc7647da64166a4a3cc0c88ebbabb", }, } diff --git a/openstack/identity/v3/credentials/requests.go b/openstack/identity/v3/credentials/requests.go index 978748d156..8ee99b7019 100644 --- a/openstack/identity/v3/credentials/requests.go +++ b/openstack/identity/v3/credentials/requests.go @@ -52,7 +52,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToCredentialCreateMap() (map[string]interface{}, error) + ToCredentialCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a credential. @@ -68,7 +68,7 @@ type CreateOpts struct { } // ToCredentialCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToCredentialCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToCredentialCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "credential") } @@ -96,7 +96,7 @@ func Delete(ctx context.Context, client *gophercloud.ServiceClient, id string) ( // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToCredentialsUpdateMap() (map[string]interface{}, error) + ToCredentialsUpdateMap() (map[string]any, error) } // UpdateOpts represents parameters to update a credential. @@ -112,7 +112,7 @@ type UpdateOpts struct { } // ToUpdateCreateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToCredentialsUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToCredentialsUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "credential") } diff --git a/openstack/identity/v3/credentials/results.go b/openstack/identity/v3/credentials/results.go index 0a876744d1..e61b631ffe 100644 --- a/openstack/identity/v3/credentials/results.go +++ b/openstack/identity/v3/credentials/results.go @@ -18,7 +18,7 @@ type Credential struct { // The ID of the project the credential was created for. ProjectID string `json:"project_id"` // Links contains referencing links to the credential. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } type credentialResult struct { diff --git a/openstack/identity/v3/credentials/testing/fixtures_test.go b/openstack/identity/v3/credentials/testing/fixtures_test.go index eaa9d4610e..8da4769ed6 100644 --- a/openstack/identity/v3/credentials/testing/fixtures_test.go +++ b/openstack/identity/v3/credentials/testing/fixtures_test.go @@ -109,7 +109,7 @@ var Credential = credentials.Credential{ Type: "ec2", UserID: userID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", }, } @@ -120,7 +120,7 @@ var FirstCredential = credentials.Credential{ Type: "ec2", UserID: userID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://identity/v3/credentials/3d3367228f9c7665266604462ec60029bcd83ad89614021a80b2eb879c572510", }, } @@ -131,7 +131,7 @@ var SecondCredential = credentials.Credential{ Type: "ec2", UserID: "6f556708d04b4ea6bc72d7df2296b71a", Blob: "{\"access\":\"7da79ff0aa364e1396f067e352b9b79a\",\"secret\":\"7a18d68ba8834b799d396f3ff6f1e98c\"}", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", }, } @@ -143,7 +143,7 @@ var SecondCredentialUpdated = credentials.Credential{ Type: "ec2", UserID: userID, Blob: "{\"access\":\"181920\",\"secret\":\"secretKey\"}", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://identity/v3/credentials/2441494e52ab6d594a34d74586075cb299489bdd1e9389e3ab06467a4f460609", }, } diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index 723c511c76..8ce72a9b33 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -61,7 +61,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToDomainCreateMap() (map[string]interface{}, error) + ToDomainCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a domain. @@ -77,7 +77,7 @@ type CreateOpts struct { } // ToDomainCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToDomainCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToDomainCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "domain") } @@ -105,7 +105,7 @@ func Delete(ctx context.Context, client *gophercloud.ServiceClient, domainID str // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToDomainUpdateMap() (map[string]interface{}, error) + ToDomainUpdateMap() (map[string]any, error) } // UpdateOpts represents parameters to update a domain. @@ -121,7 +121,7 @@ type UpdateOpts struct { } // ToUpdateCreateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToDomainUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToDomainUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "domain") } diff --git a/openstack/identity/v3/domains/results.go b/openstack/identity/v3/domains/results.go index 58b6ecd424..7227748236 100644 --- a/openstack/identity/v3/domains/results.go +++ b/openstack/identity/v3/domains/results.go @@ -17,7 +17,7 @@ type Domain struct { ID string `json:"id"` // Links contains referencing links to the domain. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` // Name is the name of the domain. Name string `json:"name"` diff --git a/openstack/identity/v3/domains/testing/fixtures_test.go b/openstack/identity/v3/domains/testing/fixtures_test.go index 562d44f819..f5025a15c8 100644 --- a/openstack/identity/v3/domains/testing/fixtures_test.go +++ b/openstack/identity/v3/domains/testing/fixtures_test.go @@ -122,7 +122,7 @@ const UpdateOutput = ` var ProdDomain = domains.Domain{ Enabled: true, ID: "a720688fb87f4575a4c000d818061eae", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/v3/domains/a720688fb87f4575a4c000d818061eae", }, Name: "ProdDomain", @@ -133,7 +133,7 @@ var ProdDomain = domains.Domain{ var TestDomain = domains.Domain{ Enabled: false, ID: "52af04aec5f84182b06959d2775d2000", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/v3/domains/52af04aec5f84182b06959d2775d2000", }, Name: "TestDomain", @@ -144,7 +144,7 @@ var TestDomain = domains.Domain{ var FirstDomain = domains.Domain{ Enabled: true, ID: "2844b2a08be147a08ef58317d6471f1f", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/domains/2844b2a08be147a08ef58317d6471f1f", }, Name: "domain one", @@ -155,7 +155,7 @@ var FirstDomain = domains.Domain{ var SecondDomain = domains.Domain{ Enabled: true, ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/domains/9fe1d3", }, Name: "domain two", @@ -165,7 +165,7 @@ var SecondDomain = domains.Domain{ var SecondDomainUpdated = domains.Domain{ Enabled: true, ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/domains/9fe1d3", }, Name: "domain two", diff --git a/openstack/identity/v3/ec2credentials/requests.go b/openstack/identity/v3/ec2credentials/requests.go index cf33069172..aefb2b14b7 100644 --- a/openstack/identity/v3/ec2credentials/requests.go +++ b/openstack/identity/v3/ec2credentials/requests.go @@ -29,7 +29,7 @@ type CreateOpts struct { } // ToCredentialCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToCredentialCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToCredentialCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/identity/v3/ec2credentials/results.go b/openstack/identity/v3/ec2credentials/results.go index 6815362169..fd718a1a03 100644 --- a/openstack/identity/v3/ec2credentials/results.go +++ b/openstack/identity/v3/ec2credentials/results.go @@ -18,7 +18,7 @@ type Credential struct { // TrustID contains an EC2 credential trust ID scope. TrustID string `json:"trust_id"` // Links contains referencing links to the application credential. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } type credentialResult struct { diff --git a/openstack/identity/v3/ec2credentials/testing/fixtures_test.go b/openstack/identity/v3/ec2credentials/testing/fixtures_test.go index cdb25dbc78..738161477d 100644 --- a/openstack/identity/v3/ec2credentials/testing/fixtures_test.go +++ b/openstack/identity/v3/ec2credentials/testing/fixtures_test.go @@ -90,7 +90,7 @@ var EC2Credential = ec2credentials.Credential{ Access: "f741662395b249c9b8acdebf1722c5ae", Secret: "6a61eb0296034c89b49cc51dde9b40aa", TrustID: "", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/f741662395b249c9b8acdebf1722c5ae", }, } @@ -101,7 +101,7 @@ var SecondEC2Credential = ec2credentials.Credential{ Access: "ad6fc85fc2df49e6b5c23d5b5bdff980", Secret: "eb233f680a204097ac329ebe8dba6d32", TrustID: "", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://identity:5000/v3/users/2844b2a08be147a08ef58317d6471f1f/credentials/OS-EC2/ad6fc85fc2df49e6b5c23d5b5bdff980", }, } diff --git a/openstack/identity/v3/ec2tokens/requests.go b/openstack/identity/v3/ec2tokens/requests.go index 5761602736..5b1f3d6882 100644 --- a/openstack/identity/v3/ec2tokens/requests.go +++ b/openstack/identity/v3/ec2tokens/requests.go @@ -67,7 +67,7 @@ type AuthOptions struct { // Signature can be either a []byte (encoded to base64 automatically) or // a string. You can set the singature explicitly, when you already know // it. In this case default Params won't be automatically set. Optional. - Signature interface{} `json:"signature"` + Signature any `json:"signature"` // BodyHash is a HTTP request body sha256 hash. When nil and Signature // is not set, a random hash is generated. Optional. BodyHash *string `json:"body_hash"` @@ -205,13 +205,13 @@ func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders st // ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder // interface. -func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { return nil, nil } // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. -func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, error) { return nil, nil } @@ -221,7 +221,7 @@ func (opts *AuthOptions) CanReauth() bool { } // ToTokenV3CreateMap formats an AuthOptions into a create request. -func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3CreateMap(map[string]any) (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "credentials") if err != nil { return nil, err @@ -232,7 +232,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string] } // calculate signature, when it is not set - c, _ := b["credentials"].(map[string]interface{}) + c, _ := b["credentials"].(map[string]any) h := interfaceToMap(c, "headers") p := interfaceToMap(c, "params") @@ -354,10 +354,10 @@ func randomBodyHash() (string, error) { // interfaceToMap is a func used to represent a "credentials" map element as a // "map[string]string" -func interfaceToMap(c map[string]interface{}, key string) map[string]string { - // convert map[string]interface{} to map[string]string +func interfaceToMap(c map[string]any, key string) map[string]string { + // convert map[string]any to map[string]string m := make(map[string]string) - if v, _ := c[key].(map[string]interface{}); v != nil { + if v, _ := c[key].(map[string]any); v != nil { for k, v := range v { m[k] = v.(string) } @@ -369,8 +369,8 @@ func interfaceToMap(c map[string]interface{}, key string) map[string]string { } // deleteBodyElements deletes map body elements -func deleteBodyElements(b map[string]interface{}, elements ...string) { - if c, ok := b["credentials"].(map[string]interface{}); ok { +func deleteBodyElements(b map[string]any, elements ...string) { + if c, ok := b["credentials"].(map[string]any); ok { for _, k := range elements { delete(c, k) } diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 7cb347f787..35eb966077 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -8,7 +8,7 @@ import ( ) type CreateOptsBuilder interface { - ToEndpointCreateMap() (map[string]interface{}, error) + ToEndpointCreateMap() (map[string]any, error) } // CreateOpts contains the subset of Endpoint attributes that should be used @@ -33,7 +33,7 @@ type CreateOpts struct { } // ToEndpointCreateMap builds a request body from the Endpoint Create options. -func (opts CreateOpts) ToEndpointCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToEndpointCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "endpoint") } @@ -92,7 +92,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // UpdateOptsBuilder allows extensions to add parameters to the Update request. type UpdateOptsBuilder interface { - ToEndpointUpdateMap() (map[string]interface{}, error) + ToEndpointUpdateMap() (map[string]any, error) } // UpdateOpts contains the subset of Endpoint attributes that should be used to @@ -117,7 +117,7 @@ type UpdateOpts struct { } // ToEndpointUpdateMap builds an update request body from the Update options. -func (opts UpdateOpts) ToEndpointUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToEndpointUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "endpoint") } diff --git a/openstack/identity/v3/federation/requests.go b/openstack/identity/v3/federation/requests.go index e80383b28a..01b536892c 100644 --- a/openstack/identity/v3/federation/requests.go +++ b/openstack/identity/v3/federation/requests.go @@ -17,7 +17,7 @@ func ListMappings(client *gophercloud.ServiceClient) pagination.Pager { // CreateMappingOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateMappingOptsBuilder interface { - ToMappingCreateMap() (map[string]interface{}, error) + ToMappingCreateMap() (map[string]any, error) } // UpdateMappingOpts provides options for creating a mapping. @@ -27,7 +27,7 @@ type CreateMappingOpts struct { } // ToMappingCreateMap formats a CreateMappingOpts into a create request. -func (opts CreateMappingOpts) ToMappingCreateMap() (map[string]interface{}, error) { +func (opts CreateMappingOpts) ToMappingCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "mapping") } @@ -55,7 +55,7 @@ func GetMapping(ctx context.Context, client *gophercloud.ServiceClient, mappingI // UpdateMappingOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateMappingOptsBuilder interface { - ToMappingUpdateMap() (map[string]interface{}, error) + ToMappingUpdateMap() (map[string]any, error) } // UpdateMappingOpts provides options for updating a mapping. @@ -65,7 +65,7 @@ type UpdateMappingOpts struct { } // ToMappingUpdateMap formats a UpdateOpts into an update request. -func (opts UpdateMappingOpts) ToMappingUpdateMap() (map[string]interface{}, error) { +func (opts UpdateMappingOpts) ToMappingUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "mapping") } diff --git a/openstack/identity/v3/federation/results.go b/openstack/identity/v3/federation/results.go index f6a0b045f7..f0d8bf23ae 100644 --- a/openstack/identity/v3/federation/results.go +++ b/openstack/identity/v3/federation/results.go @@ -19,7 +19,7 @@ type Mapping struct { ID string `json:"id"` // Links contains referencing links to the limit. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` // The list of rules used to map remote users into local users Rules []MappingRule `json:"rules"` diff --git a/openstack/identity/v3/federation/testing/fixtures_test.go b/openstack/identity/v3/federation/testing/fixtures_test.go index 2dbdaa9927..225cc57997 100644 --- a/openstack/identity/v3/federation/testing/fixtures_test.go +++ b/openstack/identity/v3/federation/testing/fixtures_test.go @@ -208,7 +208,7 @@ const UpdateOutput = ` var MappingACME = federation.Mapping{ ID: "ACME", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/OS-FEDERATION/mappings/ACME", }, Rules: []federation.MappingRule{ @@ -243,7 +243,7 @@ var MappingACME = federation.Mapping{ var MappingUpdated = federation.Mapping{ ID: "ACME", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/OS-FEDERATION/mappings/ACME", }, Rules: []federation.MappingRule{ diff --git a/openstack/identity/v3/groups/doc.go b/openstack/identity/v3/groups/doc.go index 357c765bab..61803c80ff 100644 --- a/openstack/identity/v3/groups/doc.go +++ b/openstack/identity/v3/groups/doc.go @@ -26,7 +26,7 @@ Example to Create a Group createOpts := groups.CreateOpts{ Name: "groupname", DomainID: "default", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "groupname@example.com", } } diff --git a/openstack/identity/v3/groups/requests.go b/openstack/identity/v3/groups/requests.go index 528244f1ba..ff44de113b 100644 --- a/openstack/identity/v3/groups/requests.go +++ b/openstack/identity/v3/groups/requests.go @@ -74,7 +74,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToGroupCreateMap() (map[string]interface{}, error) + ToGroupCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a group. @@ -89,18 +89,18 @@ type CreateOpts struct { DomainID string `json:"domain_id,omitempty"` // Extra is free-form extra key/value pairs to describe the group. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToGroupCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToGroupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToGroupCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "group") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["group"].(map[string]interface{}); ok { + if v, ok := b["group"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -127,7 +127,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToGroupUpdateMap() (map[string]interface{}, error) + ToGroupUpdateMap() (map[string]any, error) } // UpdateOpts provides options for updating a group. @@ -142,18 +142,18 @@ type UpdateOpts struct { DomainID string `json:"domain_id,omitempty"` // Extra is free-form extra key/value pairs to describe the group. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToGroupUpdateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToGroupUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToGroupUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "group") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["group"].(map[string]interface{}); ok { + if v, ok := b["group"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } diff --git a/openstack/identity/v3/groups/results.go b/openstack/identity/v3/groups/results.go index 64d9a581ba..5d1f5ceede 100644 --- a/openstack/identity/v3/groups/results.go +++ b/openstack/identity/v3/groups/results.go @@ -19,10 +19,10 @@ type Group struct { ID string `json:"id"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // Links contains referencing links to the group. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` // Name is the name of the group. Name string `json:"name"` @@ -32,7 +32,7 @@ func (r *Group) UnmarshalJSON(b []byte) error { type tmp Group var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -45,12 +45,12 @@ func (r *Group) UnmarshalJSON(b []byte) error { if s.Extra != nil { r.Extra = s.Extra } else { - var result interface{} + var result any err := json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(Group{}, resultMap) } } diff --git a/openstack/identity/v3/groups/testing/fixtures_test.go b/openstack/identity/v3/groups/testing/fixtures_test.go index d20c4c9619..0357a1fe06 100644 --- a/openstack/identity/v3/groups/testing/fixtures_test.go +++ b/openstack/identity/v3/groups/testing/fixtures_test.go @@ -109,12 +109,12 @@ const UpdateOutput = ` var FirstGroup = groups.Group{ DomainID: "default", ID: "2844b2a08be147a08ef58317d6471f1f", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/groups/2844b2a08be147a08ef58317d6471f1f", }, Name: "internal support", Description: "group for internal support users", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "support@localhost", }, } @@ -123,12 +123,12 @@ var FirstGroup = groups.Group{ var SecondGroup = groups.Group{ DomainID: "1789d1", ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/groups/9fe1d3", }, Name: "support", Description: "group for support users", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "support@example.com", }, } @@ -137,12 +137,12 @@ var SecondGroup = groups.Group{ var SecondGroupUpdated = groups.Group{ DomainID: "1789d1", ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/groups/9fe1d3", }, Name: "support", Description: "L2 Support Team", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "supportteam@example.com", }, } diff --git a/openstack/identity/v3/groups/testing/requests_test.go b/openstack/identity/v3/groups/testing/requests_test.go index 47035d3aa8..5f6b7b9f56 100644 --- a/openstack/identity/v3/groups/testing/requests_test.go +++ b/openstack/identity/v3/groups/testing/requests_test.go @@ -97,7 +97,7 @@ func TestCreateGroup(t *testing.T) { Name: "support", DomainID: "1789d1", Description: "group for support users", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "support@example.com", }, } @@ -115,7 +115,7 @@ func TestUpdateGroup(t *testing.T) { var description = "L2 Support Team" updateOpts := groups.UpdateOpts{ Description: &description, - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "supportteam@example.com", }, } diff --git a/openstack/identity/v3/limits/requests.go b/openstack/identity/v3/limits/requests.go index babf82f707..f39db45e70 100644 --- a/openstack/identity/v3/limits/requests.go +++ b/openstack/identity/v3/limits/requests.go @@ -62,7 +62,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // BatchCreateOptsBuilder allows extensions to add additional parameters to // the Create request. type BatchCreateOptsBuilder interface { - ToLimitsCreateMap() (map[string]interface{}, error) + ToLimitsCreateMap() (map[string]any, error) } type CreateOpts struct { @@ -92,8 +92,8 @@ type CreateOpts struct { 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)) +func (opts BatchCreateOpts) ToLimitsCreateMap() (map[string]any, error) { + limits := make([]map[string]any, len(opts)) for i, limit := range opts { limitMap, err := limit.ToMap() if err != nil { @@ -101,10 +101,10 @@ func (opts BatchCreateOpts) ToLimitsCreateMap() (map[string]interface{}, error) } limits[i] = limitMap } - return map[string]interface{}{"limits": limits}, nil + return map[string]any{"limits": limits}, nil } -func (opts CreateOpts) ToMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -132,7 +132,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, limitID string) // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToLimitUpdateMap() (map[string]interface{}, error) + ToLimitUpdateMap() (map[string]any, error) } // UpdateOpts represents parameters to update a domain. @@ -145,7 +145,7 @@ type UpdateOpts struct { } // ToLimitUpdateMap formats UpdateOpts into an update request. -func (opts UpdateOpts) ToLimitUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToLimitUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "limit") } diff --git a/openstack/identity/v3/limits/results.go b/openstack/identity/v3/limits/results.go index d508f25a46..00eb738824 100644 --- a/openstack/identity/v3/limits/results.go +++ b/openstack/identity/v3/limits/results.go @@ -56,7 +56,7 @@ type Limit struct { ResourceLimit int `json:"resource_limit"` // Links contains referencing links to the limit. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } // A LimitsOutput is an array of limits returned by List and BatchCreate operations diff --git a/openstack/identity/v3/limits/testing/fixtures_test.go b/openstack/identity/v3/limits/testing/fixtures_test.go index b491bc4d76..6275235b54 100644 --- a/openstack/identity/v3/limits/testing/fixtures_test.go +++ b/openstack/identity/v3/limits/testing/fixtures_test.go @@ -133,7 +133,7 @@ const CreateOutput = ListOutput // FirstLimit is the first limit in the List request. var FirstLimit = limits.Limit{ ResourceName: "volume", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://10.3.150.25/identity/v3/limits/25a04c7a065c430590881c646cdcdd58", }, ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", @@ -147,7 +147,7 @@ var FirstLimit = limits.Limit{ var SecondLimit = limits.Limit{ ResourceName: "snapshot", RegionID: "RegionOne", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://10.3.150.25/identity/v3/limits/3229b3849f584faea483d6851f7aab05", }, ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", @@ -160,7 +160,7 @@ var SecondLimit = limits.Limit{ var SecondLimitUpdated = limits.Limit{ ResourceName: "snapshot", RegionID: "RegionOne", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://10.3.150.25/identity/v3/limits/3229b3849f584faea483d6851f7aab05", }, ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", diff --git a/openstack/identity/v3/oauth1/requests.go b/openstack/identity/v3/oauth1/requests.go index 43cdc59e7d..8c66b36e20 100644 --- a/openstack/identity/v3/oauth1/requests.go +++ b/openstack/identity/v3/oauth1/requests.go @@ -72,7 +72,7 @@ type AuthOptions struct { // ToTokenV3HeadersMap builds the headers required for an OAuth1-based create // request. -func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) (map[string]string, error) { +func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]any) (map[string]string, error) { q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") if err != nil { return nil, err @@ -97,7 +97,7 @@ func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) ( // ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder // interface. -func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { +func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { return nil, nil } @@ -108,7 +108,7 @@ func (opts AuthOptions) CanReauth() bool { } // ToTokenV3CreateMap builds a create request body. -func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { +func (opts AuthOptions) ToTokenV3CreateMap(map[string]any) (map[string]any, error) { // identityReq defines the "identity" portion of an OAuth1-based authentication // create request body. type identityReq struct { @@ -143,7 +143,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts tokens. return } - headerOpts := map[string]interface{}{ + headerOpts := map[string]any{ "method": "POST", "url": authURL(client), } @@ -165,7 +165,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts tokens. // CreateConsumerOptsBuilder allows extensions to add additional parameters to // the CreateConsumer request. type CreateConsumerOptsBuilder interface { - ToOAuth1CreateConsumerMap() (map[string]interface{}, error) + ToOAuth1CreateConsumerMap() (map[string]any, error) } // CreateConsumerOpts provides options used to create a new Consumer. @@ -175,7 +175,7 @@ type CreateConsumerOpts struct { } // ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request. -func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface{}, error) { +func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "consumer") } @@ -222,7 +222,7 @@ type UpdateConsumerOpts struct { // ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update // request. -func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface{}, error) { +func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "consumer") } @@ -327,7 +327,7 @@ func RequestToken(ctx context.Context, client *gophercloud.ServiceClient, opts R // AuthorizeTokenOptsBuilder allows extensions to add additional parameters to // the AuthorizeToken request. type AuthorizeTokenOptsBuilder interface { - ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) + ToOAuth1AuthorizeTokenMap() (map[string]any, error) } // AuthorizeTokenOpts provides options used to authorize a request token. @@ -343,7 +343,7 @@ type Role struct { // ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token // request. -func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) { +func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]any, error) { for _, r := range opts.Roles { if r == (Role{}) { return nil, fmt.Errorf("role must not be empty") @@ -494,7 +494,7 @@ func GetAccessTokenRole(ctx context.Context, client *gophercloud.ServiceClient, // buildOAuth1QueryString builds a URLEncoded parameters string specific for // OAuth1-based requests. -func buildOAuth1QueryString(opts interface{}, timestamp *time.Time, callback string) (*url.URL, error) { +func buildOAuth1QueryString(opts any, timestamp *time.Time, callback string) (*url.URL, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return nil, err diff --git a/openstack/identity/v3/policies/doc.go b/openstack/identity/v3/policies/doc.go index a41d6b2eba..701c34fe14 100644 --- a/openstack/identity/v3/policies/doc.go +++ b/openstack/identity/v3/policies/doc.go @@ -27,7 +27,7 @@ Example to Create a Policy createOpts := policies.CreateOpts{ Type: "application/json", Blob: []byte("{'foobar_user': 'role:compute-user'}"), - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "policy for foobar_user", }, } @@ -54,7 +54,7 @@ Example to Update a Policy updateOpts := policies.UpdateOpts{ Type: "application/json", Blob: []byte("{'foobar_user': 'role:compute-user'}"), - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "policy for foobar_user", }, } diff --git a/openstack/identity/v3/policies/requests.go b/openstack/identity/v3/policies/requests.go index 3b8340e7ec..aec36dba91 100644 --- a/openstack/identity/v3/policies/requests.go +++ b/openstack/identity/v3/policies/requests.go @@ -68,7 +68,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToPolicyCreateMap() (map[string]interface{}, error) + ToPolicyCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a policy. @@ -80,11 +80,11 @@ type CreateOpts struct { Blob []byte `json:"-" required:"true"` // Extra is free-form extra key/value pairs to describe the policy. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToPolicyCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPolicyCreateMap() (map[string]any, error) { if len(opts.Type) > policyTypeMaxLength { return nil, StringFieldLengthExceedsLimit{ Field: "type", @@ -97,7 +97,7 @@ func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { return nil, err } - if v, ok := b["policy"].(map[string]interface{}); ok { + if v, ok := b["policy"].(map[string]any); ok { v["blob"] = string(opts.Blob) if opts.Extra != nil { @@ -134,7 +134,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, policyID string // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToPolicyUpdateMap() (map[string]interface{}, error) + ToPolicyUpdateMap() (map[string]any, error) } // UpdateOpts provides options for updating a policy. @@ -146,11 +146,11 @@ type UpdateOpts struct { Blob []byte `json:"-"` // Extra is free-form extra key/value pairs to describe the policy. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToPolicyUpdateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]any, error) { if len(opts.Type) > policyTypeMaxLength { return nil, StringFieldLengthExceedsLimit{ Field: "type", @@ -163,7 +163,7 @@ func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { return nil, err } - if v, ok := b["policy"].(map[string]interface{}); ok { + if v, ok := b["policy"].(map[string]any); ok { if len(opts.Blob) != 0 { v["blob"] = string(opts.Blob) } diff --git a/openstack/identity/v3/policies/results.go b/openstack/identity/v3/policies/results.go index 3d298e26c7..90a8436ea1 100644 --- a/openstack/identity/v3/policies/results.go +++ b/openstack/identity/v3/policies/results.go @@ -20,17 +20,17 @@ type Policy struct { Type string `json:"type"` // Links contains referencing links to the policy. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } func (r *Policy) UnmarshalJSON(b []byte) error { type tmp Policy var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -43,12 +43,12 @@ func (r *Policy) UnmarshalJSON(b []byte) error { if s.Extra != nil { r.Extra = s.Extra } else { - var result interface{} + var result any err := json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(Policy{}, resultMap) } } diff --git a/openstack/identity/v3/policies/testing/fixtures_test.go b/openstack/identity/v3/policies/testing/fixtures_test.go index ed29f45221..2ebc6c6c41 100644 --- a/openstack/identity/v3/policies/testing/fixtures_test.go +++ b/openstack/identity/v3/policies/testing/fixtures_test.go @@ -117,10 +117,10 @@ var FirstPolicy = policies.Policy{ ID: "2844b2a08be147a08ef58317d6471f1f", Blob: "'foo_user': 'role:compute-user'", Type: "text/plain", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/policies/2844b2a08be147a08ef58317d6471f1f", }, - Extra: map[string]interface{}{}, + Extra: map[string]any{}, } // SecondPolicy is the second policy in the List request. @@ -128,10 +128,10 @@ var SecondPolicy = policies.Policy{ ID: "b49884da9d31494ea02aff38d4b4e701", Blob: "{'bar_user': 'role:network-user'}", Type: "application/json", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701", }, - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "policy for bar_user", }, } @@ -141,10 +141,10 @@ var SecondPolicyUpdated = policies.Policy{ ID: "b49884da9d31494ea02aff38d4b4e701", Blob: "{'bar_user': 'role:network-user'}", Type: "application/json", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/policies/b49884da9d31494ea02aff38d4b4e701", }, - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "updated policy for bar_user", }, } diff --git a/openstack/identity/v3/policies/testing/requests_test.go b/openstack/identity/v3/policies/testing/requests_test.go index 81709d4adf..f837543b31 100644 --- a/openstack/identity/v3/policies/testing/requests_test.go +++ b/openstack/identity/v3/policies/testing/requests_test.go @@ -98,7 +98,7 @@ func TestCreatePolicy(t *testing.T) { createOpts := policies.CreateOpts{ Type: "application/json", Blob: []byte("{'bar_user': 'role:network-user'}"), - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "policy for bar_user", }, } @@ -169,7 +169,7 @@ func TestUpdatePolicy(t *testing.T) { HandleUpdatePolicySuccessfully(t) updateOpts := policies.UpdateOpts{ - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "updated policy for bar_user", }, } diff --git a/openstack/identity/v3/projectendpoints/requests.go b/openstack/identity/v3/projectendpoints/requests.go index 83bda977e4..9959324663 100644 --- a/openstack/identity/v3/projectendpoints/requests.go +++ b/openstack/identity/v3/projectendpoints/requests.go @@ -8,7 +8,7 @@ import ( ) type CreateOptsBuilder interface { - ToEndpointCreateMap() (map[string]interface{}, error) + ToEndpointCreateMap() (map[string]any, error) } // Create inserts a new Endpoint association to a project. diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 568d12a719..30338fc514 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -104,7 +104,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToProjectCreateMap() (map[string]interface{}, error) + ToProjectCreateMap() (map[string]any, error) } // CreateOpts represents parameters used to create a project. @@ -131,14 +131,14 @@ type CreateOpts struct { Tags []string `json:"tags,omitempty"` // Extra is free-form extra key/value pairs to describe the project. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // Options are defined options in the API to enable certain features. - Options map[Option]interface{} `json:"options,omitempty"` + Options map[Option]any `json:"options,omitempty"` } // ToProjectCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToProjectCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToProjectCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "project") if err != nil { @@ -146,7 +146,7 @@ func (opts CreateOpts) ToProjectCreateMap() (map[string]interface{}, error) { } if opts.Extra != nil { - if v, ok := b["project"].(map[string]interface{}); ok { + if v, ok := b["project"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -178,7 +178,7 @@ func Delete(ctx context.Context, client *gophercloud.ServiceClient, projectID st // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToProjectUpdateMap() (map[string]interface{}, error) + ToProjectUpdateMap() (map[string]any, error) } // UpdateOpts represents parameters to update a project. @@ -205,14 +205,14 @@ type UpdateOpts struct { Tags *[]string `json:"tags,omitempty"` // Extra is free-form extra key/value pairs to describe the project. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // Options are defined options in the API to enable certain features. - Options map[Option]interface{} `json:"options,omitempty"` + Options map[Option]any `json:"options,omitempty"` } // ToUpdateCreateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToProjectUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToProjectUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "project") if err != nil { @@ -220,7 +220,7 @@ func (opts UpdateOpts) ToProjectUpdateMap() (map[string]interface{}, error) { } if opts.Extra != nil { - if v, ok := b["project"].(map[string]interface{}); ok { + if v, ok := b["project"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -262,11 +262,11 @@ type ModifyTagsOpts struct { // ModifyTagsOptsBuilder allows extensions to add additional parameters to // the Modify request. type ModifyTagsOptsBuilder interface { - ToModifyTagsCreateMap() (map[string]interface{}, error) + ToModifyTagsCreateMap() (map[string]any, error) } // ToModifyTagsCreateMap formats a ModifyTagsOpts into a Modify tags request. -func (opts ModifyTagsOpts) ToModifyTagsCreateMap() (map[string]interface{}, error) { +func (opts ModifyTagsOpts) ToModifyTagsCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { diff --git a/openstack/identity/v3/projects/results.go b/openstack/identity/v3/projects/results.go index f571334313..131b5c5166 100644 --- a/openstack/identity/v3/projects/results.go +++ b/openstack/identity/v3/projects/results.go @@ -70,17 +70,17 @@ type Project struct { Tags []string `json:"tags,omitempty"` // Extra is free-form extra key/value pairs to describe the project. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // Options are defined options in the API to enable certain features. - Options map[Option]interface{} `json:"options,omitempty"` + Options map[Option]any `json:"options,omitempty"` } func (r *Project) UnmarshalJSON(b []byte) error { type tmp Project var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -93,12 +93,12 @@ func (r *Project) UnmarshalJSON(b []byte) error { if s.Extra != nil { r.Extra = s.Extra } else { - var result interface{} + var result any err := json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(Project{}, resultMap) } } @@ -179,7 +179,7 @@ type ProjectTags struct { // Tags is the list of tags associated with the project. Projects []Project `json:"projects,omitempty"` // Links contains referencing links to the implied_role. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } // ModifyTagsResLinksult is the result of a Tags request. Call its Extract method to diff --git a/openstack/identity/v3/projects/testing/fixtures_test.go b/openstack/identity/v3/projects/testing/fixtures_test.go index 9d07b66f24..a54edfe21f 100644 --- a/openstack/identity/v3/projects/testing/fixtures_test.go +++ b/openstack/identity/v3/projects/testing/fixtures_test.go @@ -184,8 +184,8 @@ var FirstProject = projects.Project{ ID: "abcde", Name: "project 1", ParentID: "11111", - Extra: map[string]interface{}{ - "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/abcde"}, + Extra: map[string]any{ + "links": map[string]any{"self": "http://localhost:5000/identity/v3/projects/abcde"}, }, } @@ -197,8 +197,8 @@ var SecondProject = projects.Project{ ID: "bcdef", Name: "project 2", ParentID: "22222", - Extra: map[string]interface{}{ - "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/bcdef"}, + Extra: map[string]any{ + "links": map[string]any{"self": "http://localhost:5000/identity/v3/projects/bcdef"}, }, } @@ -212,7 +212,7 @@ var RedTeam = projects.Project{ Name: "Red Team", ParentID: "", Tags: []string{"Red", "Team"}, - Extra: map[string]interface{}{"test": "old"}, + Extra: map[string]any{"test": "old"}, } // BlueTeam is a Project fixture. @@ -224,8 +224,8 @@ var BlueTeam = projects.Project{ ID: "9876", Name: "Blue Team", ParentID: "", - Extra: make(map[string]interface{}), - Options: map[projects.Option]interface{}{ + Extra: make(map[string]any), + Options: map[projects.Option]any{ projects.Immutable: true, }, } @@ -240,7 +240,7 @@ var UpdatedRedTeam = projects.Project{ Name: "Bright Red Team", ParentID: "", Tags: []string{"Red"}, - Extra: map[string]interface{}{"test": "new"}, + Extra: map[string]any{"test": "new"}, } // ExpectedAvailableProjectsSlice is the slice of projects expected to be returned @@ -261,14 +261,14 @@ var ExpectedProjects = projects.ProjectTags{ DomainID: "default", Enabled: true, ID: "3d4c2c82bd5948f0bcab0cf3a7c9b48c", - Extra: map[string]interface{}{"links": map[string]interface{}{ + Extra: map[string]any{"links": map[string]any{ "self": "http://identity:5000/v3/projects/3d4c2c82bd5948f0bcab0cf3a7c9b48c", }}, Name: "demo", Tags: []string{"foo", "bar"}, }, }, - Links: map[string]interface{}{ + Links: map[string]any{ "next": nil, "previous": nil, "self": "http://identity:5000/v3/projects", diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 6ff4efc7bc..aa27c50946 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -101,7 +101,7 @@ func TestCreateProject(t *testing.T) { Name: "Red Team", Description: "The team that is red", Tags: []string{"Red", "Team"}, - Extra: map[string]interface{}{"test": "old"}, + Extra: map[string]any{"test": "old"}, } actual, err := projects.Create(context.TODO(), client.ServiceClient(), createOpts).Extract() @@ -128,7 +128,7 @@ func TestUpdateProject(t *testing.T) { Name: "Bright Red Team", Description: &description, Tags: &[]string{"Red"}, - Extra: map[string]interface{}{"test": "new"}, + Extra: map[string]any{"test": "new"}, } actual, err := projects.Update(context.TODO(), client.ServiceClient(), "1234", updateOpts).Extract() diff --git a/openstack/identity/v3/regions/doc.go b/openstack/identity/v3/regions/doc.go index 580c6fb61c..c4f0999810 100644 --- a/openstack/identity/v3/regions/doc.go +++ b/openstack/identity/v3/regions/doc.go @@ -26,7 +26,7 @@ Example to Create a Region createOpts := regions.CreateOpts{ ID: "TestRegion", Description: "Region for testing" - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "testregionsupport@example.com", } } diff --git a/openstack/identity/v3/regions/requests.go b/openstack/identity/v3/regions/requests.go index fd8b5374bc..d02ebfc8da 100644 --- a/openstack/identity/v3/regions/requests.go +++ b/openstack/identity/v3/regions/requests.go @@ -50,7 +50,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToRegionCreateMap() (map[string]interface{}, error) + ToRegionCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a region. @@ -65,18 +65,18 @@ type CreateOpts struct { ParentRegionID string `json:"parent_region_id,omitempty"` // Extra is free-form extra key/value pairs to describe the region. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToRegionCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToRegionCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToRegionCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "region") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["region"].(map[string]interface{}); ok { + if v, ok := b["region"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -103,7 +103,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToRegionUpdateMap() (map[string]interface{}, error) + ToRegionUpdateMap() (map[string]any, error) } // UpdateOpts provides options for updating a region. @@ -120,12 +120,12 @@ type UpdateOpts struct { // The following lines should be uncommented once the fix is merged. // Extra is free-form extra key/value pairs to describe the region. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` */ } // ToRegionUpdateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToRegionUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToRegionUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "region") if err != nil { return nil, err @@ -137,7 +137,7 @@ func (opts UpdateOpts) ToRegionUpdateMap() (map[string]interface{}, error) { // The following lines should be uncommented once the fix is merged. if opts.Extra != nil { - if v, ok := b["region"].(map[string]interface{}); ok { + if v, ok := b["region"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } diff --git a/openstack/identity/v3/regions/results.go b/openstack/identity/v3/regions/results.go index 751c7ce5fc..7847c9def8 100644 --- a/openstack/identity/v3/regions/results.go +++ b/openstack/identity/v3/regions/results.go @@ -16,10 +16,10 @@ type Region struct { ID string `json:"id"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // Links contains referencing links to the region. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` // ParentRegionID is the ID of the parent region. ParentRegionID string `json:"parent_region_id"` @@ -29,7 +29,7 @@ func (r *Region) UnmarshalJSON(b []byte) error { type tmp Region var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -42,12 +42,12 @@ func (r *Region) UnmarshalJSON(b []byte) error { if s.Extra != nil { r.Extra = s.Extra } else { - var result interface{} + var result any err := json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(Region{}, resultMap) } } diff --git a/openstack/identity/v3/regions/testing/fixtures_test.go b/openstack/identity/v3/regions/testing/fixtures_test.go index 665a336de0..a4c3a40c38 100644 --- a/openstack/identity/v3/regions/testing/fixtures_test.go +++ b/openstack/identity/v3/regions/testing/fixtures_test.go @@ -117,22 +117,22 @@ const UpdateOutput = ` // FirstRegion is the first region in the List request. var FirstRegion = regions.Region{ ID: "RegionOne-East", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/regions/RegionOne-East", }, Description: "East sub-region of RegionOne", - Extra: map[string]interface{}{}, + Extra: map[string]any{}, ParentRegionID: "RegionOne", } // SecondRegion is the second region in the List request. var SecondRegion = regions.Region{ ID: "RegionOne-West", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/regions/RegionOne-West", }, Description: "West sub-region of RegionOne", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "westsupport@example.com", }, ParentRegionID: "RegionOne", @@ -149,11 +149,11 @@ var SecondRegion = regions.Region{ // SecondRegionUpdated is the second region in the List request. var SecondRegionUpdated = regions.Region{ ID: "RegionOne-West", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/regions/RegionOne-West", }, Description: "First West sub-region of RegionOne", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "westsupport@example.com", }, ParentRegionID: "RegionOne", diff --git a/openstack/identity/v3/regions/testing/requests_test.go b/openstack/identity/v3/regions/testing/requests_test.go index 8860b8721a..b85b112d13 100644 --- a/openstack/identity/v3/regions/testing/requests_test.go +++ b/openstack/identity/v3/regions/testing/requests_test.go @@ -62,7 +62,7 @@ func TestCreateRegion(t *testing.T) { createOpts := regions.CreateOpts{ ID: "RegionOne-West", Description: "West sub-region of RegionOne", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "westsupport@example.com", }, ParentRegionID: "RegionOne", @@ -86,7 +86,7 @@ func TestUpdateRegion(t *testing.T) { // is not updatable, see: https://bugs.launchpad.net/keystone/+bug/1729933 // The following lines should be uncommented once the fix is merged. - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "1stwestsupport@example.com", }, */ diff --git a/openstack/identity/v3/registeredlimits/requests.go b/openstack/identity/v3/registeredlimits/requests.go index 232b32e2a5..40eec0fe17 100644 --- a/openstack/identity/v3/registeredlimits/requests.go +++ b/openstack/identity/v3/registeredlimits/requests.go @@ -49,7 +49,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // BatchCreateOptsBuilder allows extensions to add additional parameters to // the Create request. type BatchCreateOptsBuilder interface { - ToRegisteredLimitsCreateMap() (map[string]interface{}, error) + ToRegisteredLimitsCreateMap() (map[string]any, error) } type CreateOpts struct { @@ -73,8 +73,8 @@ type CreateOpts struct { 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)) +func (opts BatchCreateOpts) ToRegisteredLimitsCreateMap() (map[string]any, error) { + registered_limits := make([]map[string]any, len(opts)) for i, registered_limit := range opts { registeredLimitMap, err := registered_limit.ToMap() if err != nil { @@ -82,10 +82,10 @@ func (opts BatchCreateOpts) ToRegisteredLimitsCreateMap() (map[string]interface{ } registered_limits[i] = registeredLimitMap } - return map[string]interface{}{"registered_limits": registered_limits}, nil + return map[string]any{"registered_limits": registered_limits}, nil } -func (opts CreateOpts) ToMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -113,7 +113,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, registeredLimit // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToRegisteredLimitUpdateMap() (map[string]interface{}, error) + ToRegisteredLimitUpdateMap() (map[string]any, error) } // UpdateOpts represents parameters to update a domain. @@ -136,7 +136,7 @@ type UpdateOpts struct { } // ToRegisteredLimitUpdateMap formats UpdateOpts into an update request. -func (opts UpdateOpts) ToRegisteredLimitUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToRegisteredLimitUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "registered_limit") } diff --git a/openstack/identity/v3/registeredlimits/results.go b/openstack/identity/v3/registeredlimits/results.go index 80d742cedd..5709e2ac35 100644 --- a/openstack/identity/v3/registeredlimits/results.go +++ b/openstack/identity/v3/registeredlimits/results.go @@ -50,7 +50,7 @@ type RegisteredLimit struct { DefaultLimit int `json:"default_limit"` // Links contains referencing links to the limit. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } // A LimitsOutput is an array of limits returned by List and BatchCreate operations diff --git a/openstack/identity/v3/registeredlimits/testing/fixtures_test.go b/openstack/identity/v3/registeredlimits/testing/fixtures_test.go index 1e865ceb4a..37e11ed6eb 100644 --- a/openstack/identity/v3/registeredlimits/testing/fixtures_test.go +++ b/openstack/identity/v3/registeredlimits/testing/fixtures_test.go @@ -116,7 +116,7 @@ const CreateOutput = ListOutput var FirstRegisteredLimit = registeredlimits.RegisteredLimit{ ResourceName: "volume", RegionID: "RegionOne", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://10.3.150.25/identity/v3/registered_limits/25a04c7a065c430590881c646cdcdd58", }, ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", @@ -129,7 +129,7 @@ var FirstRegisteredLimit = registeredlimits.RegisteredLimit{ var SecondRegisteredLimit = registeredlimits.RegisteredLimit{ ResourceName: "snapshot", RegionID: "RegionOne", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://10.3.150.25/identity/v3/registered_limits/3229b3849f584faea483d6851f7aab05", }, ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", @@ -141,7 +141,7 @@ var SecondRegisteredLimit = registeredlimits.RegisteredLimit{ var UpdatedSecondRegisteredLimit = registeredlimits.RegisteredLimit{ ResourceName: "volumes", RegionID: "RegionOne", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://10.3.150.25/identity/v3/registered_limits/3229b3849f584faea483d6851f7aab05", }, ServiceID: "9408080f1970482aa0e38bc2d4ea34b7", diff --git a/openstack/identity/v3/roles/doc.go b/openstack/identity/v3/roles/doc.go index 6af00f1632..53293e2029 100644 --- a/openstack/identity/v3/roles/doc.go +++ b/openstack/identity/v3/roles/doc.go @@ -27,7 +27,7 @@ Example to Create a Role createOpts := roles.CreateOpts{ Name: "read-only-admin", DomainID: "default", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "this role grants read-only privilege cross tenant", } } diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 217f9d334b..2e40e93d6d 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -75,7 +75,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToRoleCreateMap() (map[string]interface{}, error) + ToRoleCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a role. @@ -87,18 +87,18 @@ type CreateOpts struct { DomainID string `json:"domain_id,omitempty"` // Extra is free-form extra key/value pairs to describe the role. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToRoleCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToRoleCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToRoleCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "role") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["role"].(map[string]interface{}); ok { + if v, ok := b["role"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -125,7 +125,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToRoleUpdateMap() (map[string]interface{}, error) + ToRoleUpdateMap() (map[string]any, error) } // UpdateOpts provides options for updating a role. @@ -134,18 +134,18 @@ type UpdateOpts struct { Name string `json:"name,omitempty"` // Extra is free-form extra key/value pairs to describe the role. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToRoleUpdateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToRoleUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToRoleUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "role") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["role"].(map[string]interface{}); ok { + if v, ok := b["role"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 32f633ab39..075e3cbe73 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -16,20 +16,20 @@ type Role struct { ID string `json:"id"` // Links contains referencing links to the role. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` // Name is the role name Name string `json:"name"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } func (r *Role) UnmarshalJSON(b []byte) error { type tmp Role var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -42,12 +42,12 @@ func (r *Role) UnmarshalJSON(b []byte) error { if s.Extra != nil { r.Extra = s.Extra } else { - var result interface{} + var result any err := json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(Role{}, resultMap) } } @@ -247,7 +247,7 @@ type PriorRole struct { // Name contains the name of a role in a prior_role object. Name string `json:"name,omitempty"` // Links contains referencing links to the prior_role. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } type ImpliedRole struct { @@ -256,7 +256,7 @@ type ImpliedRole struct { // Name contains the name of role in an implied_role. Name string `json:"name,omitempty"` // Links contains referencing links to the implied_role. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } type RoleInference struct { @@ -267,8 +267,8 @@ type RoleInference struct { } type RoleInferenceRule struct { - RoleInference RoleInference `json:"role_inference"` - Links map[string]interface{} `json:"links"` + RoleInference RoleInference `json:"role_inference"` + Links map[string]any `json:"links"` } func (r impliedRoleResult) Extract() (*RoleInferenceRule, error) { @@ -289,7 +289,7 @@ type ImpliedRoleObject struct { // Name contains the name of role in an implied_role. Description string `json:"description,omitempty"` // Links contains referencing links to the implied_role. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } type PriorRoleObject struct { @@ -300,7 +300,7 @@ type PriorRoleObject struct { // Name contains the name of role in an implied_role. Description string `json:"description,omitempty"` // Links contains referencing links to the implied_role. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` } type RoleInferenceRules struct { // PriorRole is the role object that implies a list of implied_role objects. @@ -310,8 +310,8 @@ type RoleInferenceRules struct { } type RoleInferenceRuleList struct { - RoleInferenceRuleList []RoleInferenceRules `json:"role_inferences"` - Links map[string]interface{} `json:"links"` + RoleInferenceRuleList []RoleInferenceRules `json:"role_inferences"` + Links map[string]any `json:"links"` } func (r ListImpliedRolesResult) Extract() (*RoleInferenceRuleList, error) { diff --git a/openstack/identity/v3/roles/testing/fixtures_test.go b/openstack/identity/v3/roles/testing/fixtures_test.go index da48950961..755f14d4e8 100644 --- a/openstack/identity/v3/roles/testing/fixtures_test.go +++ b/openstack/identity/v3/roles/testing/fixtures_test.go @@ -304,22 +304,22 @@ const ListRoleInferenceRulesOutput = ` var FirstRole = roles.Role{ DomainID: "default", ID: "2844b2a08be147a08ef58317d6471f1f", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/2844b2a08be147a08ef58317d6471f1f", }, Name: "admin-read-only", - Extra: map[string]interface{}{}, + Extra: map[string]any{}, } // SecondRole is the second role in the List request. var SecondRole = roles.Role{ DomainID: "1789d1", ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, Name: "support", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "read-only support role", }, } @@ -328,11 +328,11 @@ var SecondRole = roles.Role{ var SecondRoleUpdated = roles.Role{ DomainID: "1789d1", ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, Name: "support", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "admin read-only support role", }, } @@ -536,11 +536,11 @@ func HandleListRoleAssignmentsWithSubtreeSuccessfully(t *testing.T) { // RoleOnResource is the role in the ListAssignmentsOnResource request. var RoleOnResource = roles.Role{ ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, Name: "support", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "read-only support role", }, } @@ -609,20 +609,20 @@ var expectedRoleInferenceRule = roles.RoleInferenceRule{ RoleInference: roles.RoleInference{ PriorRole: roles.PriorRole{ ID: "7ceab6192ea34a548cc71b24f72e762c", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/7ceab6192ea34a548cc71b24f72e762c", }, Name: "prior role name", }, ImpliedRole: roles.ImpliedRole{ ID: "97e2f5d38bc94842bc3da818c16762ed", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/97e2f5d38bc94842bc3da818c16762ed", }, Name: "implied role name", }, }, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/7ceab6192ea34a548cc71b24f72e762c/implies/97e2f5d38bc94842bc3da818c16762ed", }, } @@ -646,7 +646,7 @@ var expectedRoleInferenceRuleList = roles.RoleInferenceRuleList{ { PriorRole: roles.PriorRoleObject{ ID: "1acd3c5aa0e246b9a7427d252160dcd1", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/1acd3c5aa0e246b9a7427d252160dcd1", }, Name: "prior role name", @@ -655,7 +655,7 @@ var expectedRoleInferenceRuleList = roles.RoleInferenceRuleList{ ImpliedRoles: []roles.ImpliedRoleObject{ { ID: "3602510e2e1f499589f78a0724dcf614", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/3602510e2e1f499589f78a0724dcf614", }, Name: "implied role1 name", @@ -663,7 +663,7 @@ var expectedRoleInferenceRuleList = roles.RoleInferenceRuleList{ }, { ID: "738289aeef684e73a987f7cf2ec6d925", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/738289aeef684e73a987f7cf2ec6d925", }, Name: "implied role2 name", @@ -674,7 +674,7 @@ var expectedRoleInferenceRuleList = roles.RoleInferenceRuleList{ { PriorRole: roles.PriorRoleObject{ ID: "bbf7a5098bb34407b7164eb6ff9f144e", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/bbf7a5098bb34407b7164eb6ff9f144e", }, Name: "prior role name", @@ -683,7 +683,7 @@ var expectedRoleInferenceRuleList = roles.RoleInferenceRuleList{ ImpliedRoles: []roles.ImpliedRoleObject{ { ID: "872b20ad124c4c1bafaef2b1aae316ab", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/872b20ad124c4c1bafaef2b1aae316ab", }, Name: "implied role1 name", @@ -691,7 +691,7 @@ var expectedRoleInferenceRuleList = roles.RoleInferenceRuleList{ }, { ID: "1d865b1b2da14cb7b05254677e5f36a2", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/roles/1d865b1b2da14cb7b05254677e5f36a2", }, Name: "implied role2 name", @@ -700,7 +700,7 @@ var expectedRoleInferenceRuleList = roles.RoleInferenceRuleList{ }, }, }, - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/role_inferences", }, } diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 0eb6f9c3c7..5268fc06c7 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -94,7 +94,7 @@ func TestCreateRole(t *testing.T) { createOpts := roles.CreateOpts{ Name: "support", DomainID: "1789d1", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "read-only support role", }, } @@ -110,7 +110,7 @@ func TestUpdateRole(t *testing.T) { HandleUpdateRoleSuccessfully(t) updateOpts := roles.UpdateOpts{ - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "admin read-only support role", }, } diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go index 7ac1b2abc9..3d980b4349 100644 --- a/openstack/identity/v3/services/doc.go +++ b/openstack/identity/v3/services/doc.go @@ -26,7 +26,7 @@ Example to Create a Service createOpts := services.CreateOpts{ Type: "compute", - Extra: map[string]interface{}{ + Extra: map[string]any{ "name": "compute-service", "description": "Compute Service", }, @@ -44,7 +44,7 @@ Example to Update a Service var iFalse bool = false updateOpts := services.UpdateOpts{ Enabled: &iFalse, - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "Disabled Compute Service" }, } diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index f01f683525..c1e07908ad 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToServiceCreateMap() (map[string]interface{}, error) + ToServiceCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a service. @@ -22,18 +22,18 @@ type CreateOpts struct { Enabled *bool `json:"enabled,omitempty"` // Extra is free-form extra key/value pairs to describe the service. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToServiceCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToServiceCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "service") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["service"].(map[string]interface{}); ok { + if v, ok := b["service"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -103,7 +103,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, serviceID strin // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToServiceUpdateMap() (map[string]interface{}, error) + ToServiceUpdateMap() (map[string]any, error) } // UpdateOpts provides options for updating a service. @@ -115,18 +115,18 @@ type UpdateOpts struct { Enabled *bool `json:"enabled,omitempty"` // Extra is free-form extra key/value pairs to describe the service. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToServiceUpdateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToServiceUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "service") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["service"].(map[string]interface{}); ok { + if v, ok := b["service"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index 1426644897..8309065000 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -57,17 +57,17 @@ type Service struct { Enabled bool `json:"enabled"` // Links contains referencing links to the service. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } func (r *Service) UnmarshalJSON(b []byte) error { type tmp Service var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -80,12 +80,12 @@ func (r *Service) UnmarshalJSON(b []byte) error { if s.Extra != nil { r.Extra = s.Extra } else { - var result interface{} + var result any err := json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(Service{}, resultMap) } } diff --git a/openstack/identity/v3/services/testing/fixtures_test.go b/openstack/identity/v3/services/testing/fixtures_test.go index 339cbcdbe5..da713482f8 100644 --- a/openstack/identity/v3/services/testing/fixtures_test.go +++ b/openstack/identity/v3/services/testing/fixtures_test.go @@ -110,12 +110,12 @@ const UpdateOutput = ` // FirstService is the first service in the List request. var FirstService = services.Service{ ID: "1234", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/services/1234", }, Type: "identity", Enabled: false, - Extra: map[string]interface{}{ + Extra: map[string]any{ "name": "service-one", "description": "Service One", }, @@ -124,12 +124,12 @@ var FirstService = services.Service{ // SecondService is the second service in the List request. var SecondService = services.Service{ ID: "9876", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/services/9876", }, Type: "compute", Enabled: false, - Extra: map[string]interface{}{ + Extra: map[string]any{ "name": "service-two", "description": "Service Two", "email": "service@example.com", @@ -139,12 +139,12 @@ var SecondService = services.Service{ // SecondServiceUpdated is the SecondService should look after an Update. var SecondServiceUpdated = services.Service{ ID: "9876", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/services/9876", }, Type: "compute2", Enabled: false, - Extra: map[string]interface{}{ + Extra: map[string]any{ "name": "service-two", "description": "Service Two Updated", "email": "service@example.com", diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index a7afd5a9a9..77a212654e 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -18,7 +18,7 @@ func TestCreateSuccessful(t *testing.T) { createOpts := services.CreateOpts{ Type: "compute", - Extra: map[string]interface{}{ + Extra: map[string]any{ "name": "service-two", "description": "Service Two", "email": "service@example.com", @@ -83,7 +83,7 @@ func TestUpdateSuccessful(t *testing.T) { updateOpts := services.UpdateOpts{ Type: "compute2", - Extra: map[string]interface{}{ + Extra: map[string]any{ "description": "Service Two Updated", }, } diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index f7003cf3e1..fa8b925d04 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -21,9 +21,9 @@ type Scope struct { type AuthOptionsBuilder interface { // ToTokenV3CreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. - ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) - ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) - ToTokenV3ScopeMap() (map[string]interface{}, error) + ToTokenV3CreateMap(map[string]any) (map[string]any, error) + ToTokenV3HeadersMap(map[string]any) (map[string]string, error) + ToTokenV3ScopeMap() (map[string]any, error) CanReauth() bool } @@ -72,7 +72,7 @@ type AuthOptions struct { } // ToTokenV3CreateMap builds a request body from AuthOptions. -func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]any) (map[string]any, error) { gophercloudAuthOpts := gophercloud.AuthOptions{ Username: opts.Username, UserID: opts.UserID, @@ -91,7 +91,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } // ToTokenV3ScopeMap builds a scope request body from AuthOptions. -func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]any, error) { scope := gophercloud.AuthScope(opts.Scope) gophercloudAuthOpts := gophercloud.AuthOptions{ @@ -114,7 +114,7 @@ func (opts *AuthOptions) CanReauth() bool { // ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder // interface in the v3 tokens package. -func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, error) { return nil, nil } diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index c1a72207cb..bd61a9a67d 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -210,6 +210,6 @@ type Token struct { ExpiresAt time.Time `json:"expires_at"` } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.ExtractIntoStructPtr(v, "token") } diff --git a/openstack/identity/v3/trusts/requests.go b/openstack/identity/v3/trusts/requests.go index 55ea9bfac3..cab9721f54 100644 --- a/openstack/identity/v3/trusts/requests.go +++ b/openstack/identity/v3/trusts/requests.go @@ -11,7 +11,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToTrustCreateMap() (map[string]interface{}, error) + ToTrustCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a new trust. @@ -45,7 +45,7 @@ type CreateOpts struct { } // ToTrustCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToTrustCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToTrustCreateMap() (map[string]any, error) { parent := "trust" b, err := gophercloud.BuildRequestBody(opts, parent) if err != nil { @@ -53,7 +53,7 @@ func (opts CreateOpts) ToTrustCreateMap() (map[string]interface{}, error) { } if opts.ExpiresAt != nil { - if v, ok := b[parent].(map[string]interface{}); ok { + if v, ok := b[parent].(map[string]any); ok { v["expires_at"] = opts.ExpiresAt.Format(gophercloud.RFC3339Milli) } } diff --git a/openstack/identity/v3/users/doc.go b/openstack/identity/v3/users/doc.go index 5189bc0c5d..5ccd4c3e84 100644 --- a/openstack/identity/v3/users/doc.go +++ b/openstack/identity/v3/users/doc.go @@ -31,7 +31,7 @@ Example to Create a User DefaultProjectID: projectID, Enabled: gophercloud.Enabled, Password: "supersecret", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "username@example.com", } } diff --git a/openstack/identity/v3/users/requests.go b/openstack/identity/v3/users/requests.go index 0987c6f92a..c92a99df7a 100644 --- a/openstack/identity/v3/users/requests.go +++ b/openstack/identity/v3/users/requests.go @@ -103,7 +103,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToUserCreateMap() (map[string]interface{}, error) + ToUserCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a user. @@ -124,24 +124,24 @@ type CreateOpts struct { Enabled *bool `json:"enabled,omitempty"` // Extra is free-form extra key/value pairs to describe the user. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // Options are defined options in the API to enable certain features. - Options map[Option]interface{} `json:"options,omitempty"` + Options map[Option]any `json:"options,omitempty"` // Password is the password of the new user. Password string `json:"password,omitempty"` } // ToUserCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToUserCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToUserCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "user") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["user"].(map[string]interface{}); ok { + if v, ok := b["user"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -168,7 +168,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional parameters to // the Update request. type UpdateOptsBuilder interface { - ToUserUpdateMap() (map[string]interface{}, error) + ToUserUpdateMap() (map[string]any, error) } // UpdateOpts provides options for updating a user account. @@ -189,24 +189,24 @@ type UpdateOpts struct { Enabled *bool `json:"enabled,omitempty"` // Extra is free-form extra key/value pairs to describe the user. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // Options are defined options in the API to enable certain features. - Options map[Option]interface{} `json:"options,omitempty"` + Options map[Option]any `json:"options,omitempty"` // Password is the password of the new user. Password string `json:"password,omitempty"` } // ToUserUpdateMap formats a UpdateOpts into an update request. -func (opts UpdateOpts) ToUserUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToUserUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "user") if err != nil { return nil, err } if opts.Extra != nil { - if v, ok := b["user"].(map[string]interface{}); ok { + if v, ok := b["user"].(map[string]any); ok { for key, value := range opts.Extra { v[key] = value } @@ -233,7 +233,7 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, userID strin // ChangePasswordOptsBuilder allows extensions to add additional parameters to // the ChangePassword request. type ChangePasswordOptsBuilder interface { - ToUserChangePasswordMap() (map[string]interface{}, error) + ToUserChangePasswordMap() (map[string]any, error) } // ChangePasswordOpts provides options for changing password for a user. @@ -246,7 +246,7 @@ type ChangePasswordOpts struct { } // ToUserChangePasswordMap formats a ChangePasswordOpts into a ChangePassword request. -func (opts ChangePasswordOpts) ToUserChangePasswordMap() (map[string]interface{}, error) { +func (opts ChangePasswordOpts) ToUserChangePasswordMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "user") if err != nil { return nil, err diff --git a/openstack/identity/v3/users/results.go b/openstack/identity/v3/users/results.go index 8abd6965b3..dc48da2a2f 100644 --- a/openstack/identity/v3/users/results.go +++ b/openstack/identity/v3/users/results.go @@ -23,19 +23,19 @@ type User struct { Enabled bool `json:"enabled"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // ID is the unique ID of the user. ID string `json:"id"` // Links contains referencing links to the user. - Links map[string]interface{} `json:"links"` + Links map[string]any `json:"links"` // Name is the name of the user. Name string `json:"name"` // Options are a set of defined options of the user. - Options map[string]interface{} `json:"options"` + Options map[string]any `json:"options"` // PasswordExpiresAt is the timestamp when the user's password expires. PasswordExpiresAt time.Time `json:"-"` @@ -45,7 +45,7 @@ func (r *User) UnmarshalJSON(b []byte) error { type tmp User var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` PasswordExpiresAt gophercloud.JSONRFC3339MilliNoZ `json:"password_expires_at"` } err := json.Unmarshal(b, &s) @@ -61,12 +61,12 @@ func (r *User) UnmarshalJSON(b []byte) error { if s.Extra != nil { r.Extra = s.Extra } else { - var result interface{} + var result any err := json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { delete(resultMap, "password_expires_at") r.Extra = gophercloud.RemainingKeys(User{}, resultMap) } diff --git a/openstack/identity/v3/users/testing/fixtures_test.go b/openstack/identity/v3/users/testing/fixtures_test.go index 621c0a67cf..023124cb79 100644 --- a/openstack/identity/v3/users/testing/fixtures_test.go +++ b/openstack/identity/v3/users/testing/fixtures_test.go @@ -247,13 +247,13 @@ var FirstUser = users.User{ DomainID: "default", Enabled: true, ID: "2844b2a08be147a08ef58317d6471f1f", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "http://example.com/identity/v3/users/2844b2a08be147a08ef58317d6471f1f", }, Name: "glance", PasswordExpiresAt: nilTime, Description: "some description", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "glance@localhost", }, } @@ -265,17 +265,17 @@ var SecondUser = users.User{ DomainID: "1789d1", Enabled: true, ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/users/9fe1d3", }, Name: "jsmith", PasswordExpiresAt: SecondUserPasswordExpiresAt, - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "jsmith@example.com", }, - Options: map[string]interface{}{ + Options: map[string]any{ "ignore_password_expiry": true, - "multi_factor_auth_rules": []interface{}{ + "multi_factor_auth_rules": []any{ []string{"password", "totp"}, []string{"password", "custom-auth-method"}, }, @@ -287,12 +287,12 @@ var SecondUserNoOptions = users.User{ DomainID: "1789d1", Enabled: true, ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/users/9fe1d3", }, Name: "jsmith", PasswordExpiresAt: SecondUserPasswordExpiresAt, - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "jsmith@example.com", }, } @@ -303,16 +303,16 @@ var SecondUserUpdated = users.User{ DomainID: "1789d1", Enabled: false, ID: "9fe1d3", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/users/9fe1d3", }, Name: "jsmith", PasswordExpiresAt: SecondUserPasswordExpiresAt, - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "jsmith@example.com", "disabled_reason": "DDOS", }, - Options: map[string]interface{}{ + Options: map[string]any{ "ignore_password_expiry": true, }, } @@ -324,10 +324,10 @@ var FirstGroup = groups.Group{ Description: "Developers cleared for work on all general projects", DomainID: "1789d1", ID: "ea167b", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/groups/ea167b", }, - Extra: map[string]interface{}{ + Extra: map[string]any{ "building": "Hilltop A", }, Name: "Developers", @@ -337,10 +337,10 @@ var SecondGroup = groups.Group{ Description: "Developers cleared for work on secret projects", DomainID: "1789d1", ID: "a62db1", - Links: map[string]interface{}{ + Links: map[string]any{ "self": "https://example.com/identity/v3/groups/a62db1", }, - Extra: map[string]interface{}{}, + Extra: map[string]any{}, Name: "Secure Developers", } @@ -353,8 +353,8 @@ var FirstProject = projects.Project{ ID: "abcde", Name: "project 1", ParentID: "11111", - Extra: map[string]interface{}{ - "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/abcde"}, + Extra: map[string]any{ + "links": map[string]any{"self": "http://localhost:5000/identity/v3/projects/abcde"}, }, } @@ -365,8 +365,8 @@ var SecondProject = projects.Project{ ID: "bcdef", Name: "project 2", ParentID: "22222", - Extra: map[string]interface{}{ - "links": map[string]interface{}{"self": "http://localhost:5000/identity/v3/projects/bcdef"}, + Extra: map[string]any{ + "links": map[string]any{"self": "http://localhost:5000/identity/v3/projects/bcdef"}, }, } diff --git a/openstack/identity/v3/users/testing/requests_test.go b/openstack/identity/v3/users/testing/requests_test.go index c3abfe9f49..cb890808ee 100644 --- a/openstack/identity/v3/users/testing/requests_test.go +++ b/openstack/identity/v3/users/testing/requests_test.go @@ -101,14 +101,14 @@ func TestCreateUser(t *testing.T) { Enabled: &iTrue, Password: "secretsecret", DefaultProjectID: "263fd9", - Options: map[users.Option]interface{}{ + Options: map[users.Option]any{ users.IgnorePasswordExpiry: true, - users.MultiFactorAuthRules: []interface{}{ + users.MultiFactorAuthRules: []any{ []string{"password", "totp"}, []string{"password", "custom-auth-method"}, }, }, - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "jsmith@example.com", }, } @@ -130,7 +130,7 @@ func TestCreateNoOptionsUser(t *testing.T) { Enabled: &iTrue, Password: "secretsecret", DefaultProjectID: "263fd9", - Extra: map[string]interface{}{ + Extra: map[string]any{ "email": "jsmith@example.com", }, } @@ -148,10 +148,10 @@ func TestUpdateUser(t *testing.T) { iFalse := false updateOpts := users.UpdateOpts{ Enabled: &iFalse, - Options: map[users.Option]interface{}{ + Options: map[users.Option]any{ users.MultiFactorAuthRules: nil, }, - Extra: map[string]interface{}{ + Extra: map[string]any{ "disabled_reason": "DDOS", }, } diff --git a/openstack/image/v2/imageimport/requests.go b/openstack/image/v2/imageimport/requests.go index d78376bafe..186e5bab31 100644 --- a/openstack/image/v2/imageimport/requests.go +++ b/openstack/image/v2/imageimport/requests.go @@ -26,7 +26,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient) (r GetResult) { // CreateOptsBuilder allows to add additional parameters to the Create request. type CreateOptsBuilder interface { - ToImportCreateMap() (map[string]interface{}, error) + ToImportCreateMap() (map[string]any, error) } // CreateOpts specifies parameters of a new image import. @@ -36,12 +36,12 @@ type CreateOpts struct { } // ToImportCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToImportCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToImportCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } - return map[string]interface{}{"method": b}, nil + return map[string]any{"method": b}, nil } // Create requests the creation of a new image import on the server. diff --git a/openstack/image/v2/images/requests.go b/openstack/image/v2/images/requests.go index 9c52569286..a5f61dd615 100644 --- a/openstack/image/v2/images/requests.go +++ b/openstack/image/v2/images/requests.go @@ -145,7 +145,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { // Returns value that can be passed to json.Marshal - ToImageCreateMap() (map[string]interface{}, error) + ToImageCreateMap() (map[string]any, error) } // CreateOpts represents options used to create an image. @@ -192,7 +192,7 @@ type CreateOpts struct { // ToImageCreateMap assembles a request body based on the contents of // a CreateOpts. -func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToImageCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -253,7 +253,7 @@ type UpdateOptsBuilder interface { // returns value implementing json.Marshaler which when marshaled matches // the patch schema: // http://specs.openstack.org/openstack/glance-specs/specs/api/v2/http-patch-image-api-v2.html - ToImageUpdateMap() ([]interface{}, error) + ToImageUpdateMap() ([]any, error) } // UpdateOpts implements UpdateOpts @@ -261,8 +261,8 @@ type UpdateOpts []Patch // ToImageUpdateMap assembles a request body based on the contents of // UpdateOpts. -func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { - m := make([]interface{}, len(opts)) +func (opts UpdateOpts) ToImageUpdateMap() ([]any, error) { + m := make([]any, len(opts)) for i, patch := range opts { patchJSON := patch.ToImagePatchMap() m[i] = patchJSON @@ -273,7 +273,7 @@ func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { // Patch represents a single update to an existing image. Multiple updates // to an image can be submitted at the same time. type Patch interface { - ToImagePatchMap() map[string]interface{} + ToImagePatchMap() map[string]any } // UpdateVisibility represents an updated visibility property request. @@ -282,8 +282,8 @@ type UpdateVisibility struct { } // ToImagePatchMap assembles a request body based on UpdateVisibility. -func (r UpdateVisibility) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r UpdateVisibility) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/visibility", "value": r.Visibility, @@ -296,8 +296,8 @@ type ReplaceImageHidden struct { } // ToImagePatchMap assembles a request body based on ReplaceImageHidden. -func (r ReplaceImageHidden) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageHidden) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/os_hidden", "value": r.NewHidden, @@ -310,8 +310,8 @@ type ReplaceImageName struct { } // ToImagePatchMap assembles a request body based on ReplaceImageName. -func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageName) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/name", "value": r.NewName, @@ -324,8 +324,8 @@ type ReplaceImageChecksum struct { } // ReplaceImageChecksum assembles a request body based on ReplaceImageChecksum. -func (r ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageChecksum) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/checksum", "value": r.Checksum, @@ -338,8 +338,8 @@ type ReplaceImageTags struct { } // ToImagePatchMap assembles a request body based on ReplaceImageTags. -func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageTags) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/tags", "value": r.NewTags, @@ -352,8 +352,8 @@ type ReplaceImageMinDisk struct { } // ToImagePatchMap assembles a request body based on ReplaceImageTags. -func (r ReplaceImageMinDisk) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageMinDisk) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/min_disk", "value": r.NewMinDisk, @@ -366,8 +366,8 @@ type ReplaceImageMinRam struct { } // ToImagePatchMap assembles a request body based on ReplaceImageTags. -func (r ReplaceImageMinRam) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageMinRam) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/min_ram", "value": r.NewMinRam, @@ -380,8 +380,8 @@ type ReplaceImageProtected struct { } // ToImagePatchMap assembles a request body based on ReplaceImageProtected -func (r ReplaceImageProtected) ToImagePatchMap() map[string]interface{} { - return map[string]interface{}{ +func (r ReplaceImageProtected) ToImagePatchMap() map[string]any { + return map[string]any{ "op": "replace", "path": "/protected", "value": r.NewProtected, @@ -405,8 +405,8 @@ type UpdateImageProperty struct { } // ToImagePatchMap assembles a request body based on UpdateImageProperty. -func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { - updateMap := map[string]interface{}{ +func (r UpdateImageProperty) ToImagePatchMap() map[string]any { + updateMap := map[string]any{ "op": r.Op, "path": fmt.Sprintf("/%s", r.Name), } diff --git a/openstack/image/v2/images/results.go b/openstack/image/v2/images/results.go index e46d6f9842..6652f0e791 100644 --- a/openstack/image/v2/images/results.go +++ b/openstack/image/v2/images/results.go @@ -70,7 +70,7 @@ type Image struct { // Properties is a set of key-value pairs, if any, that are associated with // the image. - Properties map[string]interface{} + Properties map[string]any // CreatedAt is the date when the image has been created. CreatedAt time.Time `json:"created_at"` @@ -102,9 +102,9 @@ func (r *Image) UnmarshalJSON(b []byte) error { type tmp Image var s struct { tmp - SizeBytes interface{} `json:"size"` - OpenStackImageImportMethods string `json:"openstack-image-import-methods"` - OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` + SizeBytes any `json:"size"` + OpenStackImageImportMethods string `json:"openstack-image-import-methods"` + OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` } err := json.Unmarshal(b, &s) if err != nil { @@ -124,12 +124,12 @@ func (r *Image) UnmarshalJSON(b []byte) error { } // Bundle all other fields into Properties - var result interface{} + var result any err = json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { delete(resultMap, "self") delete(resultMap, "size") delete(resultMap, "openstack-image-import-methods") @@ -154,7 +154,7 @@ type commonResult struct { // Extract interprets any commonResult as an Image. func (r commonResult) Extract() (*Image, error) { var s *Image - if v, ok := r.Body.(map[string]interface{}); ok { + if v, ok := r.Body.(map[string]any); ok { for k, h := range r.Header { if strings.ToLower(k) == "openstack-image-import-methods" { for _, s := range h { diff --git a/openstack/image/v2/images/testing/requests_test.go b/openstack/image/v2/images/testing/requests_test.go index 2872e79b69..8b857871af 100644 --- a/openstack/image/v2/images/testing/requests_test.go +++ b/openstack/image/v2/images/testing/requests_test.go @@ -108,7 +108,7 @@ func TestCreateImage(t *testing.T) { UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, - Properties: map[string]interface{}{ + Properties: map[string]any{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", @@ -147,7 +147,7 @@ func TestCreateImageNulls(t *testing.T) { createdDate := actualImage.CreatedAt lastUpdate := actualImage.UpdatedAt schema := "/v2/schemas/image" - properties := map[string]interface{}{ + properties := map[string]any{ "architecture": "x86_64", } sizeBytes := int64(0) @@ -235,7 +235,7 @@ func TestGetImage(t *testing.T) { UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, - Properties: map[string]interface{}{ + Properties: map[string]any{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", @@ -311,7 +311,7 @@ func TestUpdateImage(t *testing.T) { UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, - Properties: map[string]interface{}{ + Properties: map[string]any{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", @@ -399,7 +399,7 @@ func TestImageListByTags(t *testing.T) { UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, - Properties: map[string]interface{}{ + Properties: map[string]any{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", @@ -467,7 +467,7 @@ func TestUpdateImageProperties(t *testing.T) { UpdatedAt: lastUpdate, Schema: schema, VirtualSize: 0, - Properties: map[string]interface{}{ + Properties: map[string]any{ "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", diff --git a/openstack/image/v2/members/requests.go b/openstack/image/v2/members/requests.go index 12717e6a8f..248845a8e5 100644 --- a/openstack/image/v2/members/requests.go +++ b/openstack/image/v2/members/requests.go @@ -26,7 +26,7 @@ More details here: http://developer.openstack.org/api-ref-image-v2.html#createImageMember-v2 */ func Create(ctx context.Context, client *gophercloud.ServiceClient, id string, member string) (r CreateResult) { - b := map[string]interface{}{"member": member} + b := map[string]any{"member": member} resp, err := client.Post(ctx, createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) @@ -58,7 +58,7 @@ func Delete(ctx context.Context, client *gophercloud.ServiceClient, imageID stri // UpdateOptsBuilder allows extensions to add additional attributes to the // Update request. type UpdateOptsBuilder interface { - ToImageMemberUpdateMap() (map[string]interface{}, error) + ToImageMemberUpdateMap() (map[string]any, error) } // UpdateOpts represents options to an Update request. @@ -67,8 +67,8 @@ type UpdateOpts struct { } // ToMemberUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToImageMemberUpdateMap() (map[string]interface{}, error) { - return map[string]interface{}{ +func (opts UpdateOpts) ToImageMemberUpdateMap() (map[string]any, error) { + return map[string]any{ "status": opts.Status, }, nil } diff --git a/openstack/image/v2/tasks/doc.go b/openstack/image/v2/tasks/doc.go index a95b134340..65e57b3e4e 100644 --- a/openstack/image/v2/tasks/doc.go +++ b/openstack/image/v2/tasks/doc.go @@ -35,8 +35,8 @@ Example to Create a Task createOpts := tasks.CreateOpts{ Type: "import", - Input: map[string]interface{}{ - "image_properties": map[string]interface{}{ + Input: map[string]any{ + "image_properties": map[string]any{ "container_format": "bare", "disk_format": "raw", }, diff --git a/openstack/image/v2/tasks/requests.go b/openstack/image/v2/tasks/requests.go index 241e2ab7a7..80eb69bd1f 100644 --- a/openstack/image/v2/tasks/requests.go +++ b/openstack/image/v2/tasks/requests.go @@ -99,17 +99,17 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, taskID string) (r Ge // CreateOptsBuilder allows to add additional parameters to the Create request. type CreateOptsBuilder interface { - ToTaskCreateMap() (map[string]interface{}, error) + ToTaskCreateMap() (map[string]any, error) } // CreateOpts specifies parameters of a new Image service task. type CreateOpts struct { - Type string `json:"type" required:"true"` - Input map[string]interface{} `json:"input"` + Type string `json:"type" required:"true"` + Input map[string]any `json:"input"` } // ToTaskCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToTaskCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToTaskCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/image/v2/tasks/results.go b/openstack/image/v2/tasks/results.go index d6471ea8af..2e11e5c3a7 100644 --- a/openstack/image/v2/tasks/results.go +++ b/openstack/image/v2/tasks/results.go @@ -37,10 +37,10 @@ type Task struct { Status string `json:"status"` // Input represents different parameters for the task. - Input map[string]interface{} `json:"input"` + Input map[string]any `json:"input"` // Result represents task result details. - Result map[string]interface{} `json:"result"` + Result map[string]any `json:"result"` // Owner is a unique identifier of the task owner. Owner string `json:"owner"` diff --git a/openstack/image/v2/tasks/testing/requests_test.go b/openstack/image/v2/tasks/testing/requests_test.go index 4e33814e45..feb74068bc 100644 --- a/openstack/image/v2/tasks/testing/requests_test.go +++ b/openstack/image/v2/tasks/testing/requests_test.go @@ -79,11 +79,11 @@ func TestGet(t *testing.T) { th.AssertEquals(t, s.Type, "import") th.AssertEquals(t, s.ID, "1252f636-1246-4319-bfba-c47cde0efbe0") th.AssertEquals(t, s.Schema, "/v2/schemas/task") - th.AssertDeepEquals(t, s.Result, map[string]interface{}(nil)) - th.AssertDeepEquals(t, s.Input, map[string]interface{}{ + th.AssertDeepEquals(t, s.Result, map[string]any(nil)) + th.AssertDeepEquals(t, s.Input, map[string]any{ "import_from": "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img", "import_from_format": "raw", - "image_properties": map[string]interface{}{ + "image_properties": map[string]any{ "container_format": "bare", "disk_format": "raw", }, @@ -107,8 +107,8 @@ func TestCreate(t *testing.T) { opts := tasks.CreateOpts{ Type: "import", - Input: map[string]interface{}{ - "image_properties": map[string]interface{}{ + Input: map[string]any{ + "image_properties": map[string]any{ "container_format": "bare", "disk_format": "raw", }, @@ -128,11 +128,11 @@ func TestCreate(t *testing.T) { th.AssertEquals(t, s.Type, "import") th.AssertEquals(t, s.ID, "d550c87d-86ed-430a-9895-c7a1f5ce87e9") th.AssertEquals(t, s.Schema, "/v2/schemas/task") - th.AssertDeepEquals(t, s.Result, map[string]interface{}(nil)) - th.AssertDeepEquals(t, s.Input, map[string]interface{}{ + th.AssertDeepEquals(t, s.Result, map[string]any(nil)) + th.AssertDeepEquals(t, s.Input, map[string]any{ "import_from": "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img", "import_from_format": "raw", - "image_properties": map[string]interface{}{ + "image_properties": map[string]any{ "container_format": "bare", "disk_format": "raw", }, diff --git a/openstack/keymanager/v1/acls/requests.go b/openstack/keymanager/v1/acls/requests.go index 62899d4b22..3d62b96451 100644 --- a/openstack/keymanager/v1/acls/requests.go +++ b/openstack/keymanager/v1/acls/requests.go @@ -23,7 +23,7 @@ func GetSecretACL(ctx context.Context, client *gophercloud.ServiceClient, secret // SetOptsBuilder allows extensions to add additional parameters to the // Set request. type SetOptsBuilder interface { - ToACLSetMap() (map[string]interface{}, error) + ToACLSetMap() (map[string]any, error) } // SetOpt represents options to set a particular ACL type on a resource. @@ -42,8 +42,8 @@ type SetOpt struct { type SetOpts []SetOpt // ToACLSetMap formats a SetOpts into a set request. -func (opts SetOpts) ToACLSetMap() (map[string]interface{}, error) { - b := make(map[string]interface{}) +func (opts SetOpts) ToACLSetMap() (map[string]any, error) { + b := make(map[string]any) for _, v := range opts { m, err := gophercloud.BuildRequestBody(v, v.Type) if err != nil { diff --git a/openstack/keymanager/v1/containers/requests.go b/openstack/keymanager/v1/containers/requests.go index cfab2e7ea5..a611ef91e0 100644 --- a/openstack/keymanager/v1/containers/requests.go +++ b/openstack/keymanager/v1/containers/requests.go @@ -65,7 +65,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToContainerCreateMap() (map[string]interface{}, error) + ToContainerCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a container. @@ -81,7 +81,7 @@ type CreateOpts struct { } // ToContainerCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToContainerCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToContainerCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -146,7 +146,7 @@ func ListConsumers(client *gophercloud.ServiceClient, containerID string, opts L // CreateConsumerOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateConsumerOptsBuilder interface { - ToContainerConsumerCreateMap() (map[string]interface{}, error) + ToContainerConsumerCreateMap() (map[string]any, error) } // CreateConsumerOpts provides options used to create a container. @@ -160,7 +160,7 @@ type CreateConsumerOpts struct { // ToContainerConsumerCreateMap formats a CreateConsumerOpts into a create // request. -func (opts CreateConsumerOpts) ToContainerConsumerCreateMap() (map[string]interface{}, error) { +func (opts CreateConsumerOpts) ToContainerConsumerCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -181,7 +181,7 @@ func CreateConsumer(ctx context.Context, client *gophercloud.ServiceClient, cont // DeleteConsumerOptsBuilder allows extensions to add additional parameters to // the Delete request. type DeleteConsumerOptsBuilder interface { - ToContainerConsumerDeleteMap() (map[string]interface{}, error) + ToContainerConsumerDeleteMap() (map[string]any, error) } // DeleteConsumerOpts represents options used for deleting a consumer. @@ -195,7 +195,7 @@ type DeleteConsumerOpts struct { // ToContainerConsumerDeleteMap formats a DeleteConsumerOpts into a create // request. -func (opts DeleteConsumerOpts) ToContainerConsumerDeleteMap() (map[string]interface{}, error) { +func (opts DeleteConsumerOpts) ToContainerConsumerDeleteMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -221,12 +221,12 @@ func DeleteConsumer(ctx context.Context, client *gophercloud.ServiceClient, cont // SecretRefBuilder allows extensions to add additional parameters to the // Create request. type SecretRefBuilder interface { - ToContainerSecretRefMap() (map[string]interface{}, error) + ToContainerSecretRefMap() (map[string]any, error) } // ToContainerSecretRefMap formats a SecretRefBuilder into a create // request. -func (opts SecretRef) ToContainerSecretRefMap() (map[string]interface{}, error) { +func (opts SecretRef) ToContainerSecretRefMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/keymanager/v1/orders/requests.go b/openstack/keymanager/v1/orders/requests.go index 30c0ffe787..3faf307810 100644 --- a/openstack/keymanager/v1/orders/requests.go +++ b/openstack/keymanager/v1/orders/requests.go @@ -62,7 +62,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToOrderCreateMap() (map[string]interface{}, error) + ToOrderCreateMap() (map[string]any, error) } // MetaOpts represents options used for creating an order. @@ -96,14 +96,14 @@ type CreateOpts struct { } // ToOrderCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToOrderCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToOrderCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err } if opts.Meta.Expiration != nil { - meta := b["meta"].(map[string]interface{}) + meta := b["meta"].(map[string]any) meta["expiration"] = opts.Meta.Expiration.Format(gophercloud.RFC3339NoZ) b["meta"] = meta } diff --git a/openstack/keymanager/v1/secrets/requests.go b/openstack/keymanager/v1/secrets/requests.go index f836d28f70..18f2b38c26 100644 --- a/openstack/keymanager/v1/secrets/requests.go +++ b/openstack/keymanager/v1/secrets/requests.go @@ -193,7 +193,7 @@ func GetPayload(ctx context.Context, client *gophercloud.ServiceClient, id strin // CreateOptsBuilder allows extensions to add additional parameters to // the Create request. type CreateOptsBuilder interface { - ToSecretCreateMap() (map[string]interface{}, error) + ToSecretCreateMap() (map[string]any, error) } // CreateOpts provides options used to create a secrets. @@ -227,7 +227,7 @@ type CreateOpts struct { } // ToSecretCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToSecretCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSecretCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -331,12 +331,12 @@ type MetadataOpts map[string]string // CreateMetadataOptsBuilder allows extensions to add additional parameters to // the CreateMetadata request. type CreateMetadataOptsBuilder interface { - ToMetadataCreateMap() (map[string]interface{}, error) + ToMetadataCreateMap() (map[string]any, error) } // ToMetadataCreateMap converts a MetadataOpts into a request body. -func (opts MetadataOpts) ToMetadataCreateMap() (map[string]interface{}, error) { - return map[string]interface{}{"metadata": opts}, nil +func (opts MetadataOpts) ToMetadataCreateMap() (map[string]any, error) { + return map[string]any{"metadata": opts}, nil } // CreateMetadata will set metadata for a given secret. @@ -369,11 +369,11 @@ type MetadatumOpts struct { // CreateMetadatumOptsBuilder allows extensions to add additional parameters to // the CreateMetadatum request. type CreateMetadatumOptsBuilder interface { - ToMetadatumCreateMap() (map[string]interface{}, error) + ToMetadatumCreateMap() (map[string]any, error) } // ToMetadatumCreateMap converts a MetadatumOpts into a request body. -func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, error) { +func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -394,11 +394,11 @@ func CreateMetadatum(ctx context.Context, client *gophercloud.ServiceClient, sec // UpdateMetadatumOptsBuilder allows extensions to add additional parameters to // the UpdateMetadatum request. type UpdateMetadatumOptsBuilder interface { - ToMetadatumUpdateMap() (map[string]interface{}, string, error) + ToMetadatumUpdateMap() (map[string]any, string, error) } // ToMetadatumUpdateMap converts a MetadataOpts into a request body. -func (opts MetadatumOpts) ToMetadatumUpdateMap() (map[string]interface{}, string, error) { +func (opts MetadatumOpts) ToMetadatumUpdateMap() (map[string]any, string, error) { b, err := gophercloud.BuildRequestBody(opts, "") return b, opts.Key, err } diff --git a/openstack/loadbalancer/v2/flavorprofiles/requests.go b/openstack/loadbalancer/v2/flavorprofiles/requests.go index cb9f848392..594b79739a 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/requests.go +++ b/openstack/loadbalancer/v2/flavorprofiles/requests.go @@ -45,7 +45,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToFlavorProfileCreateMap() (map[string]interface{}, error) + ToFlavorProfileCreateMap() (map[string]any, error) } // CreateOpts is the common options struct used in this package's Create @@ -62,7 +62,7 @@ type CreateOpts struct { } // ToFlavorProfileCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToFlavorProfileCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToFlavorProfileCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "flavorprofile") } @@ -89,7 +89,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToFlavorProfileUpdateMap() (map[string]interface{}, error) + ToFlavorProfileUpdateMap() (map[string]any, error) } // UpdateOpts is the common options struct used in this package's Update @@ -106,7 +106,7 @@ type UpdateOpts struct { } // ToFlavorProfileUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToFlavorProfileUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToFlavorProfileUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "flavorprofile") if err != nil { return nil, err diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go index 833d16873f..6c4859116c 100644 --- a/openstack/loadbalancer/v2/flavors/requests.go +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -45,7 +45,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToFlavorCreateMap() (map[string]interface{}, error) + ToFlavorCreateMap() (map[string]any, error) } // CreateOpts is the common options struct used in this package's Create @@ -66,7 +66,7 @@ type CreateOpts struct { } // ToFlavorCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToFlavorCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "flavor") } @@ -93,7 +93,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToFlavorUpdateMap() (map[string]interface{}, error) + ToFlavorUpdateMap() (map[string]any, error) } // UpdateOpts is the common options struct used in this package's Update @@ -110,7 +110,7 @@ type UpdateOpts struct { } // ToFlavorUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToFlavorUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "flavor") if err != nil { return nil, err diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index fb624010a3..b82d030b0d 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToL7PolicyCreateMap() (map[string]interface{}, error) + ToL7PolicyCreateMap() (map[string]any, error) } type Action string @@ -91,7 +91,7 @@ type CreateOpts struct { } // ToL7PolicyCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToL7PolicyCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "l7policy") } @@ -175,7 +175,7 @@ func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r Del // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToL7PolicyUpdateMap() (map[string]interface{}, error) + ToL7PolicyUpdateMap() (map[string]any, error) } // UpdateOpts is the common options struct used in this package's Update @@ -219,13 +219,13 @@ type UpdateOpts struct { } // ToL7PolicyUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToL7PolicyUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "l7policy") if err != nil { return nil, err } - m := b["l7policy"].(map[string]interface{}) + m := b["l7policy"].(map[string]any) if m["redirect_pool_id"] == "" { m["redirect_pool_id"] = nil @@ -292,7 +292,7 @@ type CreateRuleOpts struct { } // ToRuleCreateMap builds a request body from CreateRuleOpts. -func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) { +func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "rule") } @@ -373,7 +373,7 @@ func DeleteRule(ctx context.Context, c *gophercloud.ServiceClient, policyID stri // UpdateRuleOptsBuilder allows to add additional parameters to the PUT request. type UpdateRuleOptsBuilder interface { - ToRuleUpdateMap() (map[string]interface{}, error) + ToRuleUpdateMap() (map[string]any, error) } // UpdateRuleOpts is the common options struct used in this package's Update @@ -404,13 +404,13 @@ type UpdateRuleOpts struct { } // ToRuleUpdateMap builds a request body from UpdateRuleOpts. -func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]interface{}, error) { +func (opts UpdateRuleOpts) ToRuleUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "rule") if err != nil { return nil, err } - if m := b["rule"].(map[string]interface{}); m["key"] == "" { + if m := b["rule"].(map[string]any); m["key"] == "" { m["key"] = nil } diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 7d845d1034..ea5b10dd2f 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -98,7 +98,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToListenerCreateMap() (map[string]interface{}, error) + ToListenerCreateMap() (map[string]any, error) } // CreateOpts represents options for creating a listener. @@ -178,7 +178,7 @@ type CreateOpts struct { } // ToListenerCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToListenerCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToListenerCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "listener") } @@ -210,7 +210,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToListenerUpdateMap() (map[string]interface{}, error) + ToListenerUpdateMap() (map[string]any, error) } // UpdateOpts represents options for updating a Listener. @@ -263,13 +263,13 @@ type UpdateOpts struct { } // ToListenerUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToListenerUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToListenerUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "listener") if err != nil { return nil, err } - if m := b["listener"].(map[string]interface{}); m["default_pool_id"] == "" { + if m := b["listener"].(map[string]any); m["default_pool_id"] == "" { m["default_pool_id"] = nil } diff --git a/openstack/loadbalancer/v2/loadbalancers/requests.go b/openstack/loadbalancer/v2/loadbalancers/requests.go index 9b0b32ac6b..f815806f39 100644 --- a/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -74,7 +74,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToLoadBalancerCreateMap() (map[string]interface{}, error) + ToLoadBalancerCreateMap() (map[string]any, error) } // CreateOpts is the common options struct used in this package's Create @@ -149,7 +149,7 @@ type CreateOpts struct { } // ToLoadBalancerCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToLoadBalancerCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "loadbalancer") } @@ -178,7 +178,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToLoadBalancerUpdateMap() (map[string]interface{}, error) + ToLoadBalancerUpdateMap() (map[string]any, error) } // UpdateOpts is the common options struct used in this package's Update @@ -202,7 +202,7 @@ type UpdateOpts struct { } // ToLoadBalancerUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToLoadBalancerUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "loadbalancer") } diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index e13cfcf6e1..5667955add 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -84,7 +84,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // List request. type CreateOptsBuilder interface { - ToMonitorCreateMap() (map[string]interface{}, error) + ToMonitorCreateMap() (map[string]any, error) } // CreateOpts is the common options struct used in this package's Create @@ -144,7 +144,7 @@ type CreateOpts struct { } // ToMonitorCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToMonitorCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToMonitorCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "healthmonitor") } @@ -184,7 +184,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToMonitorUpdateMap() (map[string]interface{}, error) + ToMonitorUpdateMap() (map[string]any, error) } // UpdateOpts is the common options struct used in this package's Update @@ -230,7 +230,7 @@ type UpdateOpts struct { } // ToMonitorUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToMonitorUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "healthmonitor") } diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index d60568a06e..3683651744 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -84,7 +84,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToPoolCreateMap() (map[string]interface{}, error) + ToPoolCreateMap() (map[string]any, error) } // CreateOpts is the common options struct used in this package's Create @@ -145,7 +145,7 @@ type CreateOpts struct { } // ToPoolCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToPoolCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPoolCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "pool") } @@ -172,7 +172,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToPoolUpdateMap() (map[string]interface{}, error) + ToPoolUpdateMap() (map[string]any, error) } // UpdateOpts is the common options struct used in this package's Update @@ -201,7 +201,7 @@ type UpdateOpts struct { } // ToPoolUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToPoolUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToPoolUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "pool") } @@ -280,7 +280,7 @@ func ListMembers(c *gophercloud.ServiceClient, poolID string, opts ListMembersOp // CreateMemberOptsBuilder allows extensions to add additional parameters to the // CreateMember request. type CreateMemberOptsBuilder interface { - ToMemberCreateMap() (map[string]interface{}, error) + ToMemberCreateMap() (map[string]any, error) } // CreateMemberOpts is the common options struct used in this package's CreateMember @@ -330,7 +330,7 @@ type CreateMemberOpts struct { } // ToMemberCreateMap builds a request body from CreateMemberOpts. -func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]interface{}, error) { +func (opts CreateMemberOpts) ToMemberCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "member") } @@ -356,7 +356,7 @@ func GetMember(ctx context.Context, c *gophercloud.ServiceClient, poolID string, // UpdateMemberOptsBuilder allows extensions to add additional parameters to the // List request. type UpdateMemberOptsBuilder interface { - ToMemberUpdateMap() (map[string]interface{}, error) + ToMemberUpdateMap() (map[string]any, error) } // UpdateMemberOpts is the common options struct used in this package's Update @@ -392,7 +392,7 @@ type UpdateMemberOpts struct { } // ToMemberUpdateMap builds a request body from UpdateMemberOpts. -func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]interface{}, error) { +func (opts UpdateMemberOpts) ToMemberUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "member") } @@ -412,7 +412,7 @@ func UpdateMember(ctx context.Context, c *gophercloud.ServiceClient, poolID stri // BatchUpdateMemberOptsBuilder allows extensions to add additional parameters to the BatchUpdateMembers request. type BatchUpdateMemberOptsBuilder interface { - ToBatchMemberUpdateMap() (map[string]interface{}, error) + ToBatchMemberUpdateMap() (map[string]any, error) } // BatchUpdateMemberOpts is the common options struct used in this package's BatchUpdateMembers @@ -462,7 +462,7 @@ type BatchUpdateMemberOpts struct { } // ToBatchMemberUpdateMap builds a request body from BatchUpdateMemberOpts. -func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]interface{}, error) { +func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -477,7 +477,7 @@ func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]interface // BatchUpdateMembers updates the pool members in batch func BatchUpdateMembers(ctx context.Context, c *gophercloud.ServiceClient, poolID string, opts []BatchUpdateMemberOpts) (r UpdateMembersResult) { - members := []map[string]interface{}{} + members := []map[string]any{} for _, opt := range opts { b, err := opt.ToBatchMemberUpdateMap() if err != nil { @@ -487,7 +487,7 @@ func BatchUpdateMembers(ctx context.Context, c *gophercloud.ServiceClient, poolI members = append(members, b) } - b := map[string]interface{}{"members": members} + b := map[string]any{"members": members} resp, err := c.Put(ctx, memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index 164582315c..1f27752e0d 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -64,7 +64,7 @@ type Pool struct { Description string `json:"description"` // A list of listeners objects IDs. - Listeners []ListenerID `json:"listeners"` //[]map[string]interface{} + Listeners []ListenerID `json:"listeners"` //[]map[string]any // A list of member objects IDs. Members []Member `json:"members"` diff --git a/openstack/loadbalancer/v2/quotas/requests.go b/openstack/loadbalancer/v2/quotas/requests.go index 1169785be7..8b7771eea8 100644 --- a/openstack/loadbalancer/v2/quotas/requests.go +++ b/openstack/loadbalancer/v2/quotas/requests.go @@ -16,7 +16,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, projectID strin // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToQuotaUpdateMap() (map[string]interface{}, error) + ToQuotaUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update the load balancer Quotas. @@ -44,7 +44,7 @@ type UpdateOpts struct { } // ToQuotaUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "quota") } diff --git a/openstack/messaging/v2/claims/requests.go b/openstack/messaging/v2/claims/requests.go index c216b1b7fd..301f37b0d6 100644 --- a/openstack/messaging/v2/claims/requests.go +++ b/openstack/messaging/v2/claims/requests.go @@ -8,7 +8,7 @@ import ( // CreateOptsBuilder Builder. type CreateOptsBuilder interface { - ToClaimCreateRequest() (map[string]interface{}, string, error) + ToClaimCreateRequest() (map[string]any, string, error) } // CreateOpts params to be used with Create. @@ -26,7 +26,7 @@ type CreateOpts struct { // ToClaimCreateRequest assembles a body and URL for a Create request based on // the contents of a CreateOpts. -func (opts CreateOpts) ToClaimCreateRequest() (map[string]interface{}, string, error) { +func (opts CreateOpts) ToClaimCreateRequest() (map[string]any, string, error) { q, err := gophercloud.BuildQueryString(opts) if err != nil { return nil, q.String(), err @@ -71,7 +71,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, queueName strin // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToClaimUpdateMap() (map[string]interface{}, error) + ToClaimUpdateMap() (map[string]any, error) } // UpdateOpts implements UpdateOpts. @@ -85,7 +85,7 @@ type UpdateOpts struct { // ToClaimUpdateMap assembles a request body based on the contents of // UpdateOpts. -func (opts UpdateOpts) ToClaimUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToClaimUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/messaging/v2/claims/results.go b/openstack/messaging/v2/claims/results.go index cf76641bb5..35170896c7 100644 --- a/openstack/messaging/v2/claims/results.go +++ b/openstack/messaging/v2/claims/results.go @@ -38,10 +38,10 @@ type DeleteResult struct { } type Messages struct { - Age float32 `json:"age"` - Href string `json:"href"` - TTL int `json:"ttl"` - Body map[string]interface{} `json:"body"` + Age float32 `json:"age"` + Href string `json:"href"` + TTL int `json:"ttl"` + Body map[string]any `json:"body"` } type Claim struct { diff --git a/openstack/messaging/v2/claims/testing/fixtures_test.go b/openstack/messaging/v2/claims/testing/fixtures_test.go index 25d5060c02..f713687892 100644 --- a/openstack/messaging/v2/claims/testing/fixtures_test.go +++ b/openstack/messaging/v2/claims/testing/fixtures_test.go @@ -64,7 +64,7 @@ var CreatedClaim = []claims.Messages{ Age: 57, Href: fmt.Sprintf("/v2/queues/%s/messages/51db6f78c508f17ddc924357?claim_id=%s", QueueName, ClaimID), TTL: 300, - Body: map[string]interface{}{"event": "BackupStarted"}, + Body: map[string]any{"event": "BackupStarted"}, }, } @@ -77,7 +77,7 @@ var FirstClaim = claims.Claim{ Age: 57, Href: fmt.Sprintf("/v2/queues/%s/messages/51db6f78c508f17ddc924357?claim_id=%s", QueueName, ClaimID), TTL: 300, - Body: map[string]interface{}{"event": "BackupStarted"}, + Body: map[string]any{"event": "BackupStarted"}, }, }, TTL: 50, diff --git a/openstack/messaging/v2/messages/doc.go b/openstack/messaging/v2/messages/doc.go index ca3eb7b449..712b9a1cc9 100644 --- a/openstack/messaging/v2/messages/doc.go +++ b/openstack/messaging/v2/messages/doc.go @@ -34,13 +34,13 @@ Example to Create Messages { TTL: 300, Delay: 20, - Body: map[string]interface{}{ + Body: map[string]any{ "event": "BackupStarted", "backup_id": "c378813c-3f0b-11e2-ad92-7823d2b0f3ce", }, }, { - Body: map[string]interface{}{ + Body: map[string]any{ "event": "BackupProgress", "current_bytes": "0", "total_bytes": "99614720", diff --git a/openstack/messaging/v2/messages/requests.go b/openstack/messaging/v2/messages/requests.go index 221cb0b3ac..04810c3cce 100644 --- a/openstack/messaging/v2/messages/requests.go +++ b/openstack/messaging/v2/messages/requests.go @@ -55,7 +55,7 @@ func List(client *gophercloud.ServiceClient, queueName string, opts ListOptsBuil // CreateOptsBuilder Builder. type CreateOptsBuilder interface { - ToMessageCreateMap() (map[string]interface{}, error) + ToMessageCreateMap() (map[string]any, error) } // BatchCreateOpts is an array of CreateOpts. @@ -71,12 +71,12 @@ type CreateOpts struct { Delay int `json:"delay,omitempty"` // Body specifies an arbitrary document that constitutes the body of the message being sent. - Body map[string]interface{} `json:"body" required:"true"` + Body map[string]any `json:"body" required:"true"` } // ToMessageCreateMap constructs a request body from BatchCreateOpts. -func (opts BatchCreateOpts) ToMessageCreateMap() (map[string]interface{}, error) { - messages := make([]map[string]interface{}, len(opts)) +func (opts BatchCreateOpts) ToMessageCreateMap() (map[string]any, error) { + messages := make([]map[string]any, len(opts)) for i, message := range opts { messageMap, err := message.ToMap() if err != nil { @@ -84,11 +84,11 @@ func (opts BatchCreateOpts) ToMessageCreateMap() (map[string]interface{}, error) } messages[i] = messageMap } - return map[string]interface{}{"messages": messages}, nil + return map[string]any{"messages": messages}, nil } // ToMap constructs a request body from UpdateOpts. -func (opts CreateOpts) ToMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/messaging/v2/messages/results.go b/openstack/messaging/v2/messages/results.go index 385ff2014d..a0b76a7373 100644 --- a/openstack/messaging/v2/messages/results.go +++ b/openstack/messaging/v2/messages/results.go @@ -38,22 +38,22 @@ type GetResult struct { // Message represents a message on a queue. type Message struct { - Body map[string]interface{} `json:"body"` - Age int `json:"age"` - Href string `json:"href"` - ID string `json:"id"` - TTL int `json:"ttl"` - Checksum string `json:"checksum"` + Body map[string]any `json:"body"` + Age int `json:"age"` + Href string `json:"href"` + ID string `json:"id"` + TTL int `json:"ttl"` + Checksum string `json:"checksum"` } // PopMessage represents a message returned from PopMessages. type PopMessage struct { - Body map[string]interface{} `json:"body"` - Age int `json:"age"` - ID string `json:"id"` - TTL int `json:"ttl"` - ClaimCount int `json:"claim_count"` - ClaimID string `json:"claim_id"` + Body map[string]any `json:"body"` + Age int `json:"age"` + ID string `json:"id"` + TTL int `json:"ttl"` + ClaimCount int `json:"claim_count"` + ClaimID string `json:"claim_id"` } // ResourceList represents the result of creating a message. diff --git a/openstack/messaging/v2/messages/testing/fixtures_test.go b/openstack/messaging/v2/messages/testing/fixtures_test.go index 94aa07e574..a5a82db253 100644 --- a/openstack/messaging/v2/messages/testing/fixtures_test.go +++ b/openstack/messaging/v2/messages/testing/fixtures_test.go @@ -160,7 +160,7 @@ var ExpectedResources = messages.ResourceList{ // FirstMessage is the first result in a List. var FirstMessage = messages.Message{ - Body: map[string]interface{}{ + Body: map[string]any{ "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720", @@ -174,7 +174,7 @@ var FirstMessage = messages.Message{ // SecondMessage is the second result in a List. var SecondMessage = messages.Message{ - Body: map[string]interface{}{ + Body: map[string]any{ "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720", @@ -192,7 +192,7 @@ var ExpectedMessagesSlice = [][]messages.Message{{FirstMessage}, {SecondMessage} // ExpectedMessagesSet is the expected result in GetMessages var ExpectedMessagesSet = []messages.Message{ { - Body: map[string]interface{}{ + Body: map[string]any{ "total_bytes": "99614720", "current_bytes": "0", "event": "BackupProgress", @@ -207,7 +207,7 @@ var ExpectedMessagesSet = []messages.Message{ // ExpectedPopMessage is the expected result of a Pop. var ExpectedPopMessage = []messages.PopMessage{{ - Body: map[string]interface{}{ + Body: map[string]any{ "current_bytes": "0", "event": "BackupProgress", "total_bytes": "99614720", diff --git a/openstack/messaging/v2/messages/testing/requests_test.go b/openstack/messaging/v2/messages/testing/requests_test.go index 0bcc73c7c7..e2ee71be83 100644 --- a/openstack/messaging/v2/messages/testing/requests_test.go +++ b/openstack/messaging/v2/messages/testing/requests_test.go @@ -43,13 +43,13 @@ func TestCreate(t *testing.T) { messages.CreateOpts{ TTL: 300, Delay: 20, - Body: map[string]interface{}{ + Body: map[string]any{ "event": "BackupStarted", "backup_id": "c378813c-3f0b-11e2-ad92-7823d2b0f3ce", }, }, messages.CreateOpts{ - Body: map[string]interface{}{ + Body: map[string]any{ "event": "BackupProgress", "current_bytes": "0", "total_bytes": "99614720", diff --git a/openstack/messaging/v2/queues/doc.go b/openstack/messaging/v2/queues/doc.go index 803c84a9ab..f01917c912 100644 --- a/openstack/messaging/v2/queues/doc.go +++ b/openstack/messaging/v2/queues/doc.go @@ -34,7 +34,7 @@ Example to Create a Queue DefaultMessageDelay: 25, DeadLetterQueueMessageTTL: 3500, MaxClaimCount: 10, - Extra: map[string]interface{}{"description": "Test queue."}, + Extra: map[string]any{"description": "Test queue."}, } err := queues.Create(context.TODO(), client, createOpts).ExtractErr() diff --git a/openstack/messaging/v2/queues/requests.go b/openstack/messaging/v2/queues/requests.go index c82e43e56b..2344921a89 100644 --- a/openstack/messaging/v2/queues/requests.go +++ b/openstack/messaging/v2/queues/requests.go @@ -58,7 +58,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToQueueCreateMap() (map[string]interface{}, error) + ToQueueCreateMap() (map[string]any, error) } // CreateOpts specifies the queue creation parameters. @@ -97,11 +97,11 @@ type CreateOpts struct { EnableEncryptMessages *bool `json:"_enable_encrypt_messages,omitempty"` // Extra is free-form extra key/value pairs to describe the queue. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` } // ToQueueCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToQueueCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToQueueCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -137,7 +137,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional parameters to the // update request. type UpdateOptsBuilder interface { - ToQueueUpdateMap() ([]map[string]interface{}, error) + ToQueueUpdateMap() ([]map[string]any, error) } // BatchUpdateOpts is an array of UpdateOpts. @@ -145,9 +145,9 @@ type BatchUpdateOpts []UpdateOpts // UpdateOpts is the struct responsible for updating a property of a queue. type UpdateOpts struct { - Op UpdateOp `json:"op" required:"true"` - Path string `json:"path" required:"true"` - Value interface{} `json:"value" required:"true"` + Op UpdateOp `json:"op" required:"true"` + Path string `json:"path" required:"true"` + Value any `json:"value" required:"true"` } type UpdateOp string @@ -159,8 +159,8 @@ const ( ) // ToQueueUpdateMap constructs a request body from UpdateOpts. -func (opts BatchUpdateOpts) ToQueueUpdateMap() ([]map[string]interface{}, error) { - queuesUpdates := make([]map[string]interface{}, len(opts)) +func (opts BatchUpdateOpts) ToQueueUpdateMap() ([]map[string]any, error) { + queuesUpdates := make([]map[string]any, len(opts)) for i, queue := range opts { queueMap, err := queue.ToMap() if err != nil { @@ -172,7 +172,7 @@ func (opts BatchUpdateOpts) ToQueueUpdateMap() ([]map[string]interface{}, error) } // ToMap constructs a request body from UpdateOpts. -func (opts UpdateOpts) ToMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -241,11 +241,11 @@ type ShareOpts struct { // ShareOptsBuilder allows extensions to add additional attributes to the // Share request. type ShareOptsBuilder interface { - ToQueueShareMap() (map[string]interface{}, error) + ToQueueShareMap() (map[string]any, error) } // ToShareQueueMap formats a ShareOpts structure into a request body. -func (opts ShareOpts) ToQueueShareMap() (map[string]interface{}, error) { +func (opts ShareOpts) ToQueueShareMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -282,11 +282,11 @@ type PurgeOpts struct { // PurgeOptsBuilder allows extensions to add additional attributes to the // Purge request. type PurgeOptsBuilder interface { - ToQueuePurgeMap() (map[string]interface{}, error) + ToQueuePurgeMap() (map[string]any, error) } // ToPurgeQueueMap formats a PurgeOpts structure into a request body -func (opts PurgeOpts) ToQueuePurgeMap() (map[string]interface{}, error) { +func (opts PurgeOpts) ToQueuePurgeMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/messaging/v2/queues/results.go b/openstack/messaging/v2/queues/results.go index 53f7cd9f60..8190ddfa52 100644 --- a/openstack/messaging/v2/queues/results.go +++ b/openstack/messaging/v2/queues/results.go @@ -79,7 +79,7 @@ type QueueDetails struct { DefaultMessageTTL int `json:"_default_message_ttl"` // Extra is a collection of miscellaneous key/values. - Extra map[string]interface{} `json:"-"` + Extra map[string]any `json:"-"` // The max number the message can be claimed from the queue. MaxClaimCount int `json:"_max_claim_count"` @@ -192,7 +192,7 @@ func (r *QueueDetails) UnmarshalJSON(b []byte) error { type tmp QueueDetails var s struct { tmp - Extra map[string]interface{} `json:"extra"` + Extra map[string]any `json:"extra"` } err := json.Unmarshal(b, &s) if err != nil { @@ -205,12 +205,12 @@ func (r *QueueDetails) UnmarshalJSON(b []byte) error { if s.Extra != nil { r.Extra = s.Extra } else { - var result interface{} + var result any err := json.Unmarshal(b, &result) if err != nil { return err } - if resultMap, ok := result.(map[string]interface{}); ok { + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(QueueDetails{}, resultMap) } } diff --git a/openstack/messaging/v2/queues/testing/fixtures_test.go b/openstack/messaging/v2/queues/testing/fixtures_test.go index fa329117ab..2e9cf5e752 100644 --- a/openstack/messaging/v2/queues/testing/fixtures_test.go +++ b/openstack/messaging/v2/queues/testing/fixtures_test.go @@ -160,7 +160,7 @@ var FirstQueue = queues.Queue{ MaxClaimCount: 10, MaxMessagesPostSize: 262143, EnableEncryptMessages: true, - Extra: map[string]interface{}{"description": "Test queue."}, + Extra: map[string]any{"description": "Test queue."}, }, } @@ -175,7 +175,7 @@ var SecondQueue = queues.Queue{ DefaultMessageTTL: 3700, MaxClaimCount: 10, MaxMessagesPostSize: 262143, - Extra: map[string]interface{}{"description": "Test queue."}, + Extra: map[string]any{"description": "Test queue."}, }, } @@ -186,7 +186,7 @@ var ExpectedQueueSlice = [][]queues.Queue{{FirstQueue}, {SecondQueue}} var QueueDetails = queues.QueueDetails{ DefaultMessageTTL: 3600, MaxMessagesPostSize: 262144, - Extra: map[string]interface{}{"description": "Queue used for unit testing."}, + Extra: map[string]any{"description": "Queue used for unit testing."}, } // ExpectedStats is the expected result in a GetStats. diff --git a/openstack/messaging/v2/queues/testing/requests_test.go b/openstack/messaging/v2/queues/testing/requests_test.go index 3acd118eb3..eb83908d5d 100644 --- a/openstack/messaging/v2/queues/testing/requests_test.go +++ b/openstack/messaging/v2/queues/testing/requests_test.go @@ -53,7 +53,7 @@ func TestCreate(t *testing.T) { DeadLetterQueueMessagesTTL: 3600, MaxClaimCount: 10, EnableEncryptMessages: enableEncrypted, - Extra: map[string]interface{}{"description": "Queue for unit testing."}, + Extra: map[string]any{"description": "Queue for unit testing."}, } err := queues.Create(context.TODO(), fake.ServiceClient(), createOpts).ExtractErr() @@ -73,7 +73,7 @@ func TestUpdate(t *testing.T) { }, } updatedQueueResult := queues.QueueDetails{ - Extra: map[string]interface{}{"description": "Update queue description"}, + Extra: map[string]any{"description": "Update queue description"}, } actual, err := queues.Update(context.TODO(), fake.ServiceClient(), QueueName, updateOpts).Extract() diff --git a/openstack/networking/v2/extensions/agents/requests.go b/openstack/networking/v2/extensions/agents/requests.go index abd4e647a8..6a98eda425 100644 --- a/openstack/networking/v2/extensions/agents/requests.go +++ b/openstack/networking/v2/extensions/agents/requests.go @@ -71,7 +71,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToAgentUpdateMap() (map[string]interface{}, error) + ToAgentUpdateMap() (map[string]any, error) } // UpdateOpts represents the attributes used when updating an existing agent. @@ -81,7 +81,7 @@ type UpdateOpts struct { } // ToAgentUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToAgentUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToAgentUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "agent") } @@ -117,7 +117,7 @@ func ListDHCPNetworks(ctx context.Context, c *gophercloud.ServiceClient, id stri // ScheduleDHCPNetworkOptsBuilder allows extensions to add additional parameters // to the ScheduleDHCPNetwork request. type ScheduleDHCPNetworkOptsBuilder interface { - ToAgentScheduleDHCPNetworkMap() (map[string]interface{}, error) + ToAgentScheduleDHCPNetworkMap() (map[string]any, error) } // ScheduleDHCPNetworkOpts represents the attributes used when scheduling a @@ -127,7 +127,7 @@ type ScheduleDHCPNetworkOpts struct { } // ToAgentScheduleDHCPNetworkMap builds a request body from ScheduleDHCPNetworkOpts. -func (opts ScheduleDHCPNetworkOpts) ToAgentScheduleDHCPNetworkMap() (map[string]interface{}, error) { +func (opts ScheduleDHCPNetworkOpts) ToAgentScheduleDHCPNetworkMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -163,7 +163,7 @@ func ListBGPSpeakers(c *gophercloud.ServiceClient, agentID string) pagination.Pa // ScheduleBGPSpeakerOptsBuilder declare a function that build ScheduleBGPSpeakerOpts into a request body type ScheduleBGPSpeakerOptsBuilder interface { - ToAgentScheduleBGPSpeakerMap() (map[string]interface{}, error) + ToAgentScheduleBGPSpeakerMap() (map[string]any, error) } // ScheduleBGPSpeakerOpts represents the data that would be POST to the endpoint @@ -172,7 +172,7 @@ type ScheduleBGPSpeakerOpts struct { } // ToAgentScheduleBGPSpeakerMap builds a request body from ScheduleBGPSpeakerOpts -func (opts ScheduleBGPSpeakerOpts) ToAgentScheduleBGPSpeakerMap() (map[string]interface{}, error) { +func (opts ScheduleBGPSpeakerOpts) ToAgentScheduleBGPSpeakerMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -219,7 +219,7 @@ func ListL3Routers(ctx context.Context, c *gophercloud.ServiceClient, id string) // ScheduleL3RouterOptsBuilder allows extensions to add additional parameters // to the ScheduleL3Router request. type ScheduleL3RouterOptsBuilder interface { - ToAgentScheduleL3RouterMap() (map[string]interface{}, error) + ToAgentScheduleL3RouterMap() (map[string]any, error) } // ScheduleL3RouterOpts represents the attributes used when scheduling a @@ -229,7 +229,7 @@ type ScheduleL3RouterOpts struct { } // ToAgentScheduleL3RouterMap builds a request body from ScheduleL3RouterOpts. -func (opts ScheduleL3RouterOpts) ToAgentScheduleL3RouterMap() (map[string]interface{}, error) { +func (opts ScheduleL3RouterOpts) ToAgentScheduleL3RouterMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/networking/v2/extensions/agents/results.go b/openstack/networking/v2/extensions/agents/results.go index 6b5c32b563..166702009d 100644 --- a/openstack/networking/v2/extensions/agents/results.go +++ b/openstack/networking/v2/extensions/agents/results.go @@ -96,7 +96,7 @@ type Agent struct { // Configurations is a configuration specific key/value pairs that are // determined by the agent binary and type. - Configurations map[string]interface{} `json:"configurations"` + Configurations map[string]any `json:"configurations"` // CreatedAt is a creation timestamp. CreatedAt time.Time `json:"-"` diff --git a/openstack/networking/v2/extensions/agents/testing/fixtures_test.go b/openstack/networking/v2/extensions/agents/testing/fixtures_test.go index 74711a8dc6..fdf61fa311 100644 --- a/openstack/networking/v2/extensions/agents/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/agents/testing/fixtures_test.go @@ -73,7 +73,7 @@ var Agent = agents.Agent{ Alive: true, ResourcesSynced: true, Binary: "neutron-openvswitch-agent", - Configurations: map[string]interface{}{ + Configurations: map[string]any{ "ovs_hybrid_plug": false, "datapath_type": "system", "vhostuser_socket_dir": "/var/run/openvswitch", @@ -96,9 +96,9 @@ var Agent1 = agents.Agent{ AgentType: "Open vSwitch agent", Alive: true, Binary: "neutron-openvswitch-agent", - Configurations: map[string]interface{}{ + Configurations: map[string]any{ "datapath_type": "system", - "extensions": []interface{}{ + "extensions": []any{ "qos", }, }, @@ -117,9 +117,9 @@ var Agent2 = agents.Agent{ AgentType: "Open vSwitch agent", Alive: true, Binary: "neutron-openvswitch-agent", - Configurations: map[string]interface{}{ + Configurations: map[string]any{ "datapath_type": "system", - "extensions": []interface{}{ + "extensions": []any{ "qos", }, }, @@ -267,7 +267,7 @@ var BGPAgent1 = agents.Agent{ AgentType: "BGP dynamic routing agent", Alive: true, Binary: "neutron-bgp-dragent", - Configurations: map[string]interface{}{ + Configurations: map[string]any{ "advertise_routes": float64(2), "bgp_peers": float64(2), "bgp_speakers": float64(1), @@ -285,7 +285,7 @@ var BGPAgent2 = agents.Agent{ AgentType: "BGP dynamic routing agent", Alive: true, Binary: "neutron-bgp-dragent", - Configurations: map[string]interface{}{ + Configurations: map[string]any{ "advertise_routes": float64(2), "bgp_peers": float64(2), "bgp_speakers": float64(1), diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index 8571183ea6..070ff1b184 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -82,7 +82,7 @@ func TestGet(t *testing.T) { th.AssertEquals(t, s.HeartbeatTimestamp, time.Date(2019, 1, 9, 11, 43, 01, 0, time.UTC)) th.AssertEquals(t, s.StartedAt, time.Date(2018, 6, 26, 21, 46, 20, 0, time.UTC)) th.AssertEquals(t, s.CreatedAt, time.Date(2017, 7, 26, 23, 2, 5, 0, time.UTC)) - th.AssertDeepEquals(t, s.Configurations, map[string]interface{}{ + th.AssertDeepEquals(t, s.Configurations, map[string]any{ "ovs_hybrid_plug": false, "datapath_type": "system", "vhostuser_socket_dir": "/var/run/openvswitch", diff --git a/openstack/networking/v2/extensions/attributestags/requests.go b/openstack/networking/v2/extensions/attributestags/requests.go index 5a58783d8a..f22a57385a 100644 --- a/openstack/networking/v2/extensions/attributestags/requests.go +++ b/openstack/networking/v2/extensions/attributestags/requests.go @@ -9,7 +9,7 @@ import ( // ReplaceAllOptsBuilder allows extensions to add additional parameters to // the ReplaceAll request. type ReplaceAllOptsBuilder interface { - ToAttributeTagsReplaceAllMap() (map[string]interface{}, error) + ToAttributeTagsReplaceAllMap() (map[string]any, error) } // ReplaceAllOpts provides options used to create Tags on a Resource @@ -19,7 +19,7 @@ type ReplaceAllOpts struct { // ToAttributeTagsReplaceAllMap formats a ReplaceAllOpts into the body of the // replace request -func (opts ReplaceAllOpts) ToAttributeTagsReplaceAllMap() (map[string]interface{}, error) { +func (opts ReplaceAllOpts) ToAttributeTagsReplaceAllMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/networking/v2/extensions/bgp/peers/requests.go b/openstack/networking/v2/extensions/bgp/peers/requests.go index 0bf5844c76..eef38c0b79 100644 --- a/openstack/networking/v2/extensions/bgp/peers/requests.go +++ b/openstack/networking/v2/extensions/bgp/peers/requests.go @@ -25,7 +25,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToPeerCreateMap() (map[string]interface{}, error) + ToPeerCreateMap() (map[string]any, error) } // CreateOpts represents options used to create a BGP Peer. @@ -38,7 +38,7 @@ type CreateOpts struct { } // ToPeerCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToPeerCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPeerCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, jroot) } @@ -64,7 +64,7 @@ func Delete(ctx context.Context, c *gophercloud.ServiceClient, bgpPeerID string) // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToPeerUpdateMap() (map[string]interface{}, error) + ToPeerUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update a BGP Peer. @@ -74,7 +74,7 @@ type UpdateOpts struct { } // ToPeerUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToPeerUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToPeerUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, jroot) } diff --git a/openstack/networking/v2/extensions/bgp/peers/results.go b/openstack/networking/v2/extensions/bgp/peers/results.go index a3a1b5fd34..afd771b1e4 100644 --- a/openstack/networking/v2/extensions/bgp/peers/results.go +++ b/openstack/networking/v2/extensions/bgp/peers/results.go @@ -18,7 +18,7 @@ func (r commonResult) Extract() (*BGPPeer, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, jroot) } @@ -71,7 +71,7 @@ func ExtractBGPPeers(r pagination.Page) ([]BGPPeer, error) { return s, err } -func ExtractBGPPeersInto(r pagination.Page, v interface{}) error { +func ExtractBGPPeersInto(r pagination.Page, v any) error { return r.(BGPPeerPage).Result.ExtractIntoSlicePtr(v, "bgp_peers") } diff --git a/openstack/networking/v2/extensions/bgp/speakers/requests.go b/openstack/networking/v2/extensions/bgp/speakers/requests.go index ff3c88e7dd..4c352c2cc3 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/requests.go +++ b/openstack/networking/v2/extensions/bgp/speakers/requests.go @@ -34,11 +34,11 @@ type CreateOpts struct { // CreateOptsBuilder declare a function that build CreateOpts into a Create request body. type CreateOptsBuilder interface { - ToSpeakerCreateMap() (map[string]interface{}, error) + ToSpeakerCreateMap() (map[string]any, error) } // ToSpeakerCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToSpeakerCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSpeakerCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, jroot) } @@ -69,14 +69,14 @@ type UpdateOpts struct { } // ToSpeakerUpdateMap build a request body from UpdateOpts -func (opts UpdateOpts) ToSpeakerUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSpeakerUpdateMap() (map[string]any, 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) + ToSpeakerUpdateMap() (map[string]any, error) } // Update accepts a UpdateOpts and update the BGP Speaker. @@ -100,11 +100,11 @@ type AddBGPPeerOpts struct { // AddBGPPeerOptsBuilder declare a funtion that encode AddBGPPeerOpts into a request body type AddBGPPeerOptsBuilder interface { - ToBGPSpeakerAddBGPPeerMap() (map[string]interface{}, error) + ToBGPSpeakerAddBGPPeerMap() (map[string]any, error) } // ToBGPSpeakerAddBGPPeerMap build a request body from AddBGPPeerOpts -func (opts AddBGPPeerOpts) ToBGPSpeakerAddBGPPeerMap() (map[string]interface{}, error) { +func (opts AddBGPPeerOpts) ToBGPSpeakerAddBGPPeerMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -127,11 +127,11 @@ type RemoveBGPPeerOpts AddBGPPeerOpts // RemoveBGPPeerOptsBuilder declare a funtion that encode RemoveBGPPeerOpts into a request body type RemoveBGPPeerOptsBuilder interface { - ToBGPSpeakerRemoveBGPPeerMap() (map[string]interface{}, error) + ToBGPSpeakerRemoveBGPPeerMap() (map[string]any, error) } // ToBGPSpeakerRemoveBGPPeerMap build a request body from RemoveBGPPeerOpts -func (opts RemoveBGPPeerOpts) ToBGPSpeakerRemoveBGPPeerMap() (map[string]interface{}, error) { +func (opts RemoveBGPPeerOpts) ToBGPSpeakerRemoveBGPPeerMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -159,7 +159,7 @@ func GetAdvertisedRoutes(c *gophercloud.ServiceClient, bgpSpeakerID string) pagi // AddGatewayNetworkOptsBuilder declare a function that build AddGatewayNetworkOpts into a request body. type AddGatewayNetworkOptsBuilder interface { - ToBGPSpeakerAddGatewayNetworkMap() (map[string]interface{}, error) + ToBGPSpeakerAddGatewayNetworkMap() (map[string]any, error) } // AddGatewayNetworkOpts represents the data that would be PUT to the endpoint @@ -169,7 +169,7 @@ type AddGatewayNetworkOpts struct { } // ToBGPSpeakerAddGatewayNetworkMap implements the function -func (opts AddGatewayNetworkOpts) ToBGPSpeakerAddGatewayNetworkMap() (map[string]interface{}, error) { +func (opts AddGatewayNetworkOpts) ToBGPSpeakerAddGatewayNetworkMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -189,14 +189,14 @@ func AddGatewayNetwork(ctx context.Context, c *gophercloud.ServiceClient, bgpSpe // RemoveGatewayNetworkOptsBuilder declare a function that build RemoveGatewayNetworkOpts into a request body. type RemoveGatewayNetworkOptsBuilder interface { - ToBGPSpeakerRemoveGatewayNetworkMap() (map[string]interface{}, error) + ToBGPSpeakerRemoveGatewayNetworkMap() (map[string]any, 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) { +func (opts RemoveGatewayNetworkOpts) ToBGPSpeakerRemoveGatewayNetworkMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/networking/v2/extensions/bgp/speakers/results.go b/openstack/networking/v2/extensions/bgp/speakers/results.go index d7353da85c..93c967ca47 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/results.go +++ b/openstack/networking/v2/extensions/bgp/speakers/results.go @@ -18,7 +18,7 @@ func (r commonResult) Extract() (*BGPSpeaker, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, jroot) } @@ -80,10 +80,10 @@ func ExtractBGPSpeakers(r pagination.Page) ([]BGPSpeaker, error) { return s, err } -// ExtractBGPSpeakersInto accepts a Page struct and an interface{}. The former contains +// ExtractBGPSpeakersInto accepts a Page struct and an any. 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 { +func ExtractBGPSpeakersInto(r pagination.Page, v any) error { return r.(BGPSpeakerPage).Result.ExtractIntoSlicePtr(v, "bgp_speakers") } @@ -123,7 +123,7 @@ func (r AddBGPPeerResult) Extract() (*AddBGPPeerOpts, error) { return &s, err } -func (r AddBGPPeerResult) ExtractInto(v interface{}) error { +func (r AddBGPPeerResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "") } @@ -166,7 +166,7 @@ func ExtractAdvertisedRoutes(r pagination.Page) ([]AdvertisedRoute, error) { } // ExtractAdvertisedRoutesInto extract the advertised routes from the first param into the 2nd -func ExtractAdvertisedRoutesInto(r pagination.Page, v interface{}) error { +func ExtractAdvertisedRoutesInto(r pagination.Page, v any) error { return r.(AdvertisedRoutePage).Result.ExtractIntoSlicePtr(v, "advertised_routes") } diff --git a/openstack/networking/v2/extensions/dns/requests.go b/openstack/networking/v2/extensions/dns/requests.go index b93a5a353a..987b35240f 100644 --- a/openstack/networking/v2/extensions/dns/requests.go +++ b/openstack/networking/v2/extensions/dns/requests.go @@ -44,13 +44,13 @@ type PortCreateOptsExt struct { } // ToPortCreateMap casts a CreateOpts struct to a map. -func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { +func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) if opts.DNSName != "" { port["dns_name"] = opts.DNSName @@ -70,13 +70,13 @@ type PortUpdateOptsExt struct { } // ToPortUpdateMap casts an UpdateOpts struct to a map. -func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { +func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) if opts.DNSName != nil { port["dns_name"] = *opts.DNSName @@ -99,13 +99,13 @@ type FloatingIPCreateOptsExt struct { } // ToFloatingIPCreateMap casts a CreateOpts struct to a map. -func (opts FloatingIPCreateOptsExt) ToFloatingIPCreateMap() (map[string]interface{}, error) { +func (opts FloatingIPCreateOptsExt) ToFloatingIPCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToFloatingIPCreateMap() if err != nil { return nil, err } - floatingip := base["floatingip"].(map[string]interface{}) + floatingip := base["floatingip"].(map[string]any) if opts.DNSName != "" { floatingip["dns_name"] = opts.DNSName @@ -129,13 +129,13 @@ type NetworkCreateOptsExt struct { } // ToNetworkCreateMap casts a CreateOpts struct to a map. -func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err } - network := base["network"].(map[string]interface{}) + network := base["network"].(map[string]any) if opts.DNSDomain != "" { network["dns_domain"] = opts.DNSDomain @@ -155,13 +155,13 @@ type NetworkUpdateOptsExt struct { } // ToNetworkUpdateMap casts an UpdateOpts struct to a map. -func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err } - network := base["network"].(map[string]interface{}) + network := base["network"].(map[string]any) if opts.DNSDomain != nil { network["dns_domain"] = *opts.DNSDomain diff --git a/openstack/networking/v2/extensions/external/requests.go b/openstack/networking/v2/extensions/external/requests.go index 8b53828a55..c62d992ebf 100644 --- a/openstack/networking/v2/extensions/external/requests.go +++ b/openstack/networking/v2/extensions/external/requests.go @@ -42,7 +42,7 @@ type CreateOptsExt struct { // ToNetworkCreateMap adds the router:external options to the base network // creation options. -func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err @@ -52,7 +52,7 @@ func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { return base, nil } - networkMap := base["network"].(map[string]interface{}) + networkMap := base["network"].(map[string]any) networkMap["router:external"] = opts.External return base, nil @@ -67,7 +67,7 @@ type UpdateOptsExt struct { } // ToNetworkUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err @@ -77,7 +77,7 @@ func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { return base, nil } - networkMap := base["network"].(map[string]interface{}) + networkMap := base["network"].(map[string]any) networkMap["router:external"] = opts.External return base, nil diff --git a/openstack/networking/v2/extensions/extradhcpopts/requests.go b/openstack/networking/v2/extensions/extradhcpopts/requests.go index 5df95ff3eb..02cac21848 100644 --- a/openstack/networking/v2/extensions/extradhcpopts/requests.go +++ b/openstack/networking/v2/extensions/extradhcpopts/requests.go @@ -29,17 +29,17 @@ type CreateExtraDHCPOpt struct { } // ToPortCreateMap casts a CreateOptsExt struct to a map. -func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsExt) ToPortCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) // Convert opts.ExtraDHCPOpts to a slice of maps. if opts.ExtraDHCPOpts != nil { - extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) + extraDHCPOpts := make([]map[string]any, len(opts.ExtraDHCPOpts)) for i, opt := range opts.ExtraDHCPOpts { b, err := gophercloud.BuildRequestBody(opt, "") if err != nil { @@ -77,17 +77,17 @@ type UpdateExtraDHCPOpt struct { } // ToPortUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) // Convert opts.ExtraDHCPOpts to a slice of maps. if opts.ExtraDHCPOpts != nil { - extraDHCPOpts := make([]map[string]interface{}, len(opts.ExtraDHCPOpts)) + extraDHCPOpts := make([]map[string]any, len(opts.ExtraDHCPOpts)) for i, opt := range opts.ExtraDHCPOpts { b, err := gophercloud.BuildRequestBody(opt, "") if err != nil { diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go index 20f6b8aab8..9b12c62842 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go @@ -78,7 +78,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { - ToFirewallGroupCreateMap() (map[string]interface{}, error) + ToFirewallGroupCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new firewall group. @@ -96,7 +96,7 @@ type CreateOpts struct { } // ToFirewallGroupCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToFirewallGroupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToFirewallGroupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "firewall_group") } @@ -116,7 +116,7 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type UpdateOptsBuilder interface { - ToFirewallGroupUpdateMap() (map[string]interface{}, error) + ToFirewallGroupUpdateMap() (map[string]any, error) } // UpdateOpts contains the values used when updating a firewall group. @@ -131,7 +131,7 @@ type UpdateOpts struct { } // ToFirewallGroupUpdateMap casts a UpdateOpts struct to a map. -func (opts UpdateOpts) ToFirewallGroupUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToFirewallGroupUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "firewall_group") } diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go index e908249b00..57d25a6726 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go @@ -63,7 +63,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { - ToFirewallPolicyCreateMap() (map[string]interface{}, error) + ToFirewallPolicyCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new firewall policy. @@ -80,7 +80,7 @@ type CreateOpts struct { } // ToFirewallPolicyCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToFirewallPolicyCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "firewall_policy") } @@ -106,7 +106,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type UpdateOptsBuilder interface { - ToFirewallPolicyUpdateMap() (map[string]interface{}, error) + ToFirewallPolicyUpdateMap() (map[string]any, error) } // UpdateOpts contains the values used when updating a firewall policy. @@ -119,7 +119,7 @@ type UpdateOpts struct { } // ToFirewallPolicyUpdateMap casts a CreateOpts struct to a map. -func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToFirewallPolicyUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "firewall_policy") } @@ -143,7 +143,7 @@ func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r Del } type InsertRuleOptsBuilder interface { - ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) + ToFirewallPolicyInsertRuleMap() (map[string]any, error) } type InsertRuleOpts struct { @@ -152,7 +152,7 @@ type InsertRuleOpts struct { InsertAfter string `json:"insert_after,omitempty" xor:"InsertBefore"` } -func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]interface{}, error) { +func (opts InsertRuleOpts) ToFirewallPolicyInsertRuleMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -169,7 +169,7 @@ func InsertRule(ctx context.Context, c *gophercloud.ServiceClient, id string, op } func RemoveRule(ctx context.Context, c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { - b := map[string]interface{}{"firewall_rule_id": ruleID} + b := map[string]any{"firewall_rule_id": ruleID} _, r.Err = c.Put(ctx, removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) diff --git a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go index cd47062252..521c9ef3ce 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/rules/requests.go @@ -111,7 +111,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { - ToRuleCreateMap() (map[string]interface{}, error) + ToRuleCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new firewall rule. @@ -132,13 +132,13 @@ type CreateOpts struct { } // ToRuleCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToRuleCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "firewall_rule") if err != nil { return nil, err } - if m := b["firewall_rule"].(map[string]interface{}); m["protocol"] == "any" { + if m := b["firewall_rule"].(map[string]any); m["protocol"] == "any" { m["protocol"] = nil } @@ -169,7 +169,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type UpdateOptsBuilder interface { - ToRuleUpdateMap() (map[string]interface{}, error) + ToRuleUpdateMap() (map[string]any, error) } // UpdateOpts contains the values used when updating a firewall rule. @@ -188,7 +188,7 @@ type UpdateOpts struct { } // ToRuleUpdateMap casts a UpdateOpts struct to a map. -func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToRuleUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "firewall_rule") } diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go index 918d42cf12..22e4df39de 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go @@ -70,7 +70,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // CreateOptsBuilder allows to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToAddressScopeCreateMap() (map[string]interface{}, error) + ToAddressScopeCreateMap() (map[string]any, error) } // CreateOpts specifies parameters of a new address-scope. @@ -92,7 +92,7 @@ type CreateOpts struct { } // ToAddressScopeCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToAddressScopeCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToAddressScopeCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "address_scope") } @@ -113,7 +113,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToAddressScopeUpdateMap() (map[string]interface{}, error) + ToAddressScopeUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update an address-scope. @@ -126,7 +126,7 @@ type UpdateOpts struct { } // ToAddressScopeUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToAddressScopeUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToAddressScopeUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "address_scope") } diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/requests.go b/openstack/networking/v2/extensions/layer3/extraroutes/requests.go index 8e7229047b..a56dc25d31 100644 --- a/openstack/networking/v2/extensions/layer3/extraroutes/requests.go +++ b/openstack/networking/v2/extensions/layer3/extraroutes/requests.go @@ -10,7 +10,7 @@ import ( // OptsBuilder allows extensions to add additional parameters to the Add or // Remove requests. type OptsBuilder interface { - ToExtraRoutesUpdateMap() (map[string]interface{}, error) + ToExtraRoutesUpdateMap() (map[string]any, error) } // Opts contains the values needed to add or remove a list og routes on a @@ -20,7 +20,7 @@ type Opts struct { } // ToExtraRoutesUpdateMap builds a body based on Opts. -func (opts Opts) ToExtraRoutesUpdateMap() (map[string]interface{}, error) { +func (opts Opts) ToExtraRoutesUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "router") } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go index d14dcfb6bd..a3afb0403c 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -65,7 +65,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToFloatingIPCreateMap() (map[string]interface{}, error) + ToFloatingIPCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new floating IP @@ -84,7 +84,7 @@ type CreateOpts struct { // ToFloatingIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder // interface -func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "floatingip") } @@ -133,7 +133,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToFloatingIPUpdateMap() (map[string]interface{}, error) + ToFloatingIPUpdateMap() (map[string]any, error) } // UpdateOpts contains the values used when updating a floating IP resource. The @@ -148,13 +148,13 @@ type UpdateOpts struct { // ToFloatingIPUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder // interface -func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToFloatingIPUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "floatingip") if err != nil { return nil, err } - if m := b["floatingip"].(map[string]interface{}); m["port_id"] == "" { + if m := b["floatingip"].(map[string]any); m["port_id"] == "" { m["port_id"] = nil } diff --git a/openstack/networking/v2/extensions/layer3/floatingips/results.go b/openstack/networking/v2/extensions/layer3/floatingips/results.go index 4524ceb161..50740ebf30 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/results.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/results.go @@ -107,7 +107,7 @@ func (r commonResult) Extract() (*FloatingIP, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "floatingip") } @@ -176,6 +176,6 @@ func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { return s.FloatingIPs, err } -func ExtractFloatingIPsInto(r pagination.Page, v interface{}) error { +func ExtractFloatingIPsInto(r pagination.Page, v any) error { return r.(FloatingIPPage).Result.ExtractIntoSlicePtr(v, "floatingips") } diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index 95a0a37283..42f90332ac 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -73,12 +73,12 @@ type CreateOpts struct { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToPortForwardingCreateMap() (map[string]interface{}, error) + ToPortForwardingCreateMap() (map[string]any, error) } // ToPortForwardingCreateMap allows CreateOpts to satisfy the CreateOptsBuilder // interface -func (opts CreateOpts) ToPortForwardingCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPortForwardingCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "port_forwarding") } @@ -106,7 +106,7 @@ type UpdateOpts struct { // ToPortForwardingUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder // interface -func (opts UpdateOpts) ToPortForwardingUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToPortForwardingUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "port_forwarding") if err != nil { return nil, err @@ -118,7 +118,7 @@ func (opts UpdateOpts) ToPortForwardingUpdateMap() (map[string]interface{}, erro // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToPortForwardingUpdateMap() (map[string]interface{}, error) + ToPortForwardingUpdateMap() (map[string]any, error) } // Update allows port forwarding resources to be updated. diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go index 0824471d57..17c5281acc 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/results.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -62,7 +62,7 @@ func (r commonResult) Extract() (*PortForwarding, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "port_forwarding") } diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index 67061533ea..dcc7977a46 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -51,7 +51,7 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToRouterCreateMap() (map[string]interface{}, error) + ToRouterCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new router. There are @@ -68,7 +68,7 @@ type CreateOpts struct { } // ToRouterCreateMap builds a create request body from CreateOpts. -func (opts CreateOpts) ToRouterCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToRouterCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "router") } @@ -101,7 +101,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToRouterUpdateMap() (map[string]interface{}, error) + ToRouterUpdateMap() (map[string]any, error) } // UpdateOpts contains the values used when updating a router. @@ -115,7 +115,7 @@ type UpdateOpts struct { } // ToRouterUpdateMap builds an update body based on UpdateOpts. -func (opts UpdateOpts) ToRouterUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToRouterUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "router") } @@ -147,7 +147,7 @@ func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r Del // AddInterfaceOptsBuilder allows extensions to add additional parameters to // the AddInterface request. type AddInterfaceOptsBuilder interface { - ToRouterAddInterfaceMap() (map[string]interface{}, error) + ToRouterAddInterfaceMap() (map[string]any, error) } // AddInterfaceOpts represents the options for adding an interface to a router. @@ -157,7 +157,7 @@ type AddInterfaceOpts struct { } // ToRouterAddInterfaceMap builds a request body from AddInterfaceOpts. -func (opts AddInterfaceOpts) ToRouterAddInterfaceMap() (map[string]interface{}, error) { +func (opts AddInterfaceOpts) ToRouterAddInterfaceMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -198,7 +198,7 @@ func AddInterface(ctx context.Context, c *gophercloud.ServiceClient, id string, // RemoveInterfaceOptsBuilder allows extensions to add additional parameters to // the RemoveInterface request. type RemoveInterfaceOptsBuilder interface { - ToRouterRemoveInterfaceMap() (map[string]interface{}, error) + ToRouterRemoveInterfaceMap() (map[string]any, error) } // RemoveInterfaceOpts represents options for removing an interface from @@ -210,7 +210,7 @@ type RemoveInterfaceOpts struct { // ToRouterRemoveInterfaceMap builds a request body based on // RemoveInterfaceOpts. -func (opts RemoveInterfaceOpts) ToRouterRemoveInterfaceMap() (map[string]interface{}, error) { +func (opts RemoveInterfaceOpts) ToRouterRemoveInterfaceMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index 9bf8a02b6e..25e8ab7b59 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -214,7 +214,7 @@ type L3Agent struct { // Configurations is a configuration specific key/value pairs that are // determined by the agent binary and type. - Configurations map[string]interface{} `json:"configurations"` + Configurations map[string]any `json:"configurations"` // CreatedAt is a creation timestamp. CreatedAt time.Time `json:"-"` @@ -238,7 +238,7 @@ type L3Agent struct { HAState string `json:"ha_state"` // ResourceVersions is a list agent known objects and version numbers - ResourceVersions map[string]interface{} `json:"resource_versions"` + ResourceVersions map[string]any `json:"resource_versions"` } // UnmarshalJSON helps to convert the timestamps into the time.Time type. diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 192d217eef..541dd656b5 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -638,7 +638,7 @@ func TestListL3Agents(t *testing.T) { ResourcesSynced: true, Binary: "neutron-l3-agent", AvailabilityZone: "nova", - Configurations: map[string]interface{}{ + Configurations: map[string]any{ "agent_mode": "legacy", "ex_gw_ports": float64(2), "floating_ips": float64(2), @@ -654,7 +654,7 @@ func TestListL3Agents(t *testing.T) { Host: "os-ctrl-02", Topic: "l3_agent", HAState: "standby", - ResourceVersions: map[string]interface{}{}, + ResourceVersions: map[string]any{}, }, { ID: "4541cc6c-87bc-4cee-bad2-36ca78836c91", @@ -665,7 +665,7 @@ func TestListL3Agents(t *testing.T) { ResourcesSynced: true, Binary: "neutron-l3-agent", AvailabilityZone: "nova", - Configurations: map[string]interface{}{ + Configurations: map[string]any{ "agent_mode": "legacy", "ex_gw_ports": float64(2), "floating_ips": float64(2), @@ -681,7 +681,7 @@ func TestListL3Agents(t *testing.T) { Host: "os-ctrl-03", Topic: "l3_agent", HAState: "active", - ResourceVersions: map[string]interface{}{}, + ResourceVersions: map[string]any{}, }, } th.CheckDeepEquals(t, expected, actual) diff --git a/openstack/networking/v2/extensions/mtu/requests.go b/openstack/networking/v2/extensions/mtu/requests.go index 9495076da4..74ab2205fc 100644 --- a/openstack/networking/v2/extensions/mtu/requests.go +++ b/openstack/networking/v2/extensions/mtu/requests.go @@ -44,7 +44,7 @@ type CreateOptsExt struct { } // ToNetworkCreateMap adds an MTU to the base network creation options. -func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err @@ -54,7 +54,7 @@ func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { return base, nil } - networkMap := base["network"].(map[string]interface{}) + networkMap := base["network"].(map[string]any) networkMap["mtu"] = opts.MTU return base, nil @@ -70,7 +70,7 @@ type UpdateOptsExt struct { } // ToNetworkUpdateMap adds an MTU to the base network uptade options. -func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err @@ -80,7 +80,7 @@ func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { return base, nil } - networkMap := base["network"].(map[string]interface{}) + networkMap := base["network"].(map[string]any) networkMap["mtu"] = opts.MTU return base, nil diff --git a/openstack/networking/v2/extensions/portsbinding/requests.go b/openstack/networking/v2/extensions/portsbinding/requests.go index fc9233d51e..aab621901e 100644 --- a/openstack/networking/v2/extensions/portsbinding/requests.go +++ b/openstack/networking/v2/extensions/portsbinding/requests.go @@ -20,17 +20,17 @@ type CreateOptsExt struct { // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. - Profile map[string]interface{} `json:"binding:profile,omitempty"` + Profile map[string]any `json:"binding:profile,omitempty"` } // ToPortCreateMap casts a CreateOpts struct to a map. -func (opts CreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsExt) ToPortCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) if opts.HostID != "" { port["binding:host_id"] = opts.HostID @@ -63,17 +63,17 @@ type UpdateOptsExt struct { // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. - Profile map[string]interface{} `json:"binding:profile,omitempty"` + Profile map[string]any `json:"binding:profile,omitempty"` } // ToPortUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOptsExt) ToPortUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) if opts.HostID != nil { port["binding:host_id"] = *opts.HostID diff --git a/openstack/networking/v2/extensions/portsbinding/results.go b/openstack/networking/v2/extensions/portsbinding/results.go index 4d8f856a0b..bebf7ef157 100644 --- a/openstack/networking/v2/extensions/portsbinding/results.go +++ b/openstack/networking/v2/extensions/portsbinding/results.go @@ -8,7 +8,7 @@ type PortsBindingExt struct { // A dictionary that enables the application to pass information about // functions that the Networking API provides. - VIFDetails map[string]interface{} `json:"binding:vif_details"` + VIFDetails map[string]any `json:"binding:vif_details"` // The VIF type for the port. VIFType string `json:"binding:vif_type"` @@ -20,5 +20,5 @@ type PortsBindingExt struct { // A dictionary that enables the application running on the specified // host to pass and receive virtual network interface (VIF) port-specific // information to the plug-in. - Profile map[string]interface{} `json:"binding:profile"` + Profile map[string]any `json:"binding:profile"` } diff --git a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go index 3331e784d5..4f0073b4a5 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/requests_test.go @@ -92,7 +92,7 @@ func TestGet(t *testing.T) { th.AssertEquals(t, s.HostID, "devstack") th.AssertEquals(t, s.VNICType, "normal") th.AssertEquals(t, s.VIFType, "ovs") - th.AssertDeepEquals(t, s.VIFDetails, map[string]interface{}{"port_filter": true, "ovs_hybrid_plug": true}) + th.AssertDeepEquals(t, s.VIFDetails, map[string]any{"port_filter": true, "ovs_hybrid_plug": true}) } func TestCreate(t *testing.T) { diff --git a/openstack/networking/v2/extensions/portsecurity/requests.go b/openstack/networking/v2/extensions/portsecurity/requests.go index 1eb670c67b..652aaf1ea6 100644 --- a/openstack/networking/v2/extensions/portsecurity/requests.go +++ b/openstack/networking/v2/extensions/portsecurity/requests.go @@ -14,13 +14,13 @@ type PortCreateOptsExt struct { } // ToPortCreateMap casts a CreateOpts struct to a map. -func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { +func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) if opts.PortSecurityEnabled != nil { port["port_security_enabled"] = &opts.PortSecurityEnabled @@ -38,13 +38,13 @@ type PortUpdateOptsExt struct { } // ToPortUpdateMap casts a UpdateOpts struct to a map. -func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { +func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) if opts.PortSecurityEnabled != nil { port["port_security_enabled"] = &opts.PortSecurityEnabled @@ -63,13 +63,13 @@ type NetworkCreateOptsExt struct { } // ToNetworkCreateMap casts a CreateOpts struct to a map. -func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err } - network := base["network"].(map[string]interface{}) + network := base["network"].(map[string]any) if opts.PortSecurityEnabled != nil { network["port_security_enabled"] = &opts.PortSecurityEnabled @@ -88,13 +88,13 @@ type NetworkUpdateOptsExt struct { } // ToNetworkUpdateMap casts a UpdateOpts struct to a map. -func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err } - network := base["network"].(map[string]interface{}) + network := base["network"].(map[string]any) if opts.PortSecurityEnabled != nil { network["port_security_enabled"] = &opts.PortSecurityEnabled diff --git a/openstack/networking/v2/extensions/provider/requests.go b/openstack/networking/v2/extensions/provider/requests.go index 0ed1735dc5..cb488f8d1d 100644 --- a/openstack/networking/v2/extensions/provider/requests.go +++ b/openstack/networking/v2/extensions/provider/requests.go @@ -11,7 +11,7 @@ type CreateOptsExt struct { } // ToNetworkCreateMap adds segments to the base network creation options. -func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err @@ -21,7 +21,7 @@ func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { return base, nil } - providerMap := base["network"].(map[string]interface{}) + providerMap := base["network"].(map[string]any) providerMap["segments"] = opts.Segments return base, nil @@ -34,7 +34,7 @@ type UpdateOptsExt struct { } // ToNetworkUpdateMap adds segments to the base network update options. -func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err @@ -44,7 +44,7 @@ func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { return base, nil } - providerMap := base["network"].(map[string]interface{}) + providerMap := base["network"].(map[string]any) providerMap["segments"] = opts.Segments return base, nil diff --git a/openstack/networking/v2/extensions/provider/results.go b/openstack/networking/v2/extensions/provider/results.go index 9babd2ab6e..18b3382c1e 100644 --- a/openstack/networking/v2/extensions/provider/results.go +++ b/openstack/networking/v2/extensions/provider/results.go @@ -42,7 +42,7 @@ func (r *NetworkProviderExt) UnmarshalJSON(b []byte) error { type tmp NetworkProviderExt var networkProviderExt struct { tmp - SegmentationID interface{} `json:"provider:segmentation_id"` + SegmentationID any `json:"provider:segmentation_id"` } if err := json.Unmarshal(b, &networkProviderExt); err != nil { diff --git a/openstack/networking/v2/extensions/qos/policies/requests.go b/openstack/networking/v2/extensions/qos/policies/requests.go index 23a2df70a3..88d9f85fb8 100644 --- a/openstack/networking/v2/extensions/qos/policies/requests.go +++ b/openstack/networking/v2/extensions/qos/policies/requests.go @@ -18,13 +18,13 @@ type PortCreateOptsExt struct { } // ToPortCreateMap casts a CreateOpts struct to a map. -func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]interface{}, error) { +func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToPortCreateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) if opts.QoSPolicyID != "" { port["qos_policy_id"] = opts.QoSPolicyID @@ -43,13 +43,13 @@ type PortUpdateOptsExt struct { } // ToPortUpdateMap casts a UpdateOpts struct to a map. -func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]interface{}, error) { +func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() if err != nil { return nil, err } - port := base["port"].(map[string]interface{}) + port := base["port"].(map[string]any) if opts.QoSPolicyID != nil { qosPolicyID := *opts.QoSPolicyID @@ -72,13 +72,13 @@ type NetworkCreateOptsExt struct { } // ToNetworkCreateMap casts a CreateOpts struct to a map. -func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts NetworkCreateOptsExt) ToNetworkCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err } - network := base["network"].(map[string]interface{}) + network := base["network"].(map[string]any) if opts.QoSPolicyID != "" { network["qos_policy_id"] = opts.QoSPolicyID @@ -97,13 +97,13 @@ type NetworkUpdateOptsExt struct { } // ToNetworkUpdateMap casts a UpdateOpts struct to a map. -func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts NetworkUpdateOptsExt) ToNetworkUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err } - network := base["network"].(map[string]interface{}) + network := base["network"].(map[string]any) if opts.QoSPolicyID != nil { qosPolicyID := *opts.QoSPolicyID @@ -181,7 +181,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // CreateOptsBuilder allows to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToPolicyCreateMap() (map[string]interface{}, error) + ToPolicyCreateMap() (map[string]any, error) } // CreateOpts specifies parameters of a new QoS policy. @@ -206,7 +206,7 @@ type CreateOpts struct { } // ToPolicyCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPolicyCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "policy") } @@ -227,7 +227,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToPolicyUpdateMap() (map[string]interface{}, error) + ToPolicyUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update a QoS policy. @@ -246,7 +246,7 @@ type UpdateOpts struct { } // ToPolicyUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "policy") } diff --git a/openstack/networking/v2/extensions/qos/policies/results.go b/openstack/networking/v2/extensions/qos/policies/results.go index e1d22775a9..70c70379d7 100644 --- a/openstack/networking/v2/extensions/qos/policies/results.go +++ b/openstack/networking/v2/extensions/qos/policies/results.go @@ -83,7 +83,7 @@ type Policy struct { RevisionNumber int `json:"revision_number"` // Rules represents QoS rules of the policy. - Rules []map[string]interface{} `json:"rules"` + Rules []map[string]any `json:"rules"` // Tags optionally set via extensions/attributestags Tags []string `json:"tags"` @@ -126,6 +126,6 @@ func ExtractPolicies(r pagination.Page) ([]Policy, error) { } // ExtractPoliciesInto extracts the elements into a slice of RBAC Policy structs. -func ExtractPolicysInto(r pagination.Page, v interface{}) error { +func ExtractPolicysInto(r pagination.Page, v any) error { return r.(PolicyPage).Result.ExtractIntoSlicePtr(v, "policies") } diff --git a/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go b/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go index 58aa19e554..2ebc8c287a 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/fixtures_test.go @@ -185,7 +185,7 @@ const ListPoliciesResponse = ` var Policy1 = policies.Policy{ Name: "bw-limiter", - Rules: []map[string]interface{}{ + Rules: []map[string]any{ { "type": "bandwidth_limit", "max_kbps": float64(3000), @@ -207,7 +207,7 @@ var Policy1 = policies.Policy{ var Policy2 = policies.Policy{ Name: "no-rules", Tags: []string{}, - Rules: []map[string]interface{}{}, + Rules: []map[string]any{}, TenantID: "a77cbe0998374aed9a6798ad6c61677e", CreatedAt: time.Date(2019, 6, 1, 10, 38, 58, 0, time.UTC), UpdatedAt: time.Date(2019, 6, 1, 10, 38, 58, 0, time.UTC), diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index c5c1eab261..72b17e5330 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -355,7 +355,7 @@ func TestGetPolicy(t *testing.T) { th.AssertEquals(t, "bw-limiter", p.Name) th.AssertDeepEquals(t, []string{}, p.Tags) - th.AssertDeepEquals(t, []map[string]interface{}{ + th.AssertDeepEquals(t, []map[string]any{ { "max_kbps": float64(3000), "direction": "egress", diff --git a/openstack/networking/v2/extensions/qos/rules/requests.go b/openstack/networking/v2/extensions/qos/rules/requests.go index a8b11521b2..b34c090213 100644 --- a/openstack/networking/v2/extensions/qos/rules/requests.go +++ b/openstack/networking/v2/extensions/qos/rules/requests.go @@ -69,7 +69,7 @@ func GetBandwidthLimitRule(ctx context.Context, c *gophercloud.ServiceClient, po // CreateBandwidthLimitRuleOptsBuilder allows to add additional parameters to the // CreateBandwidthLimitRule request. type CreateBandwidthLimitRuleOptsBuilder interface { - ToBandwidthLimitRuleCreateMap() (map[string]interface{}, error) + ToBandwidthLimitRuleCreateMap() (map[string]any, error) } // CreateBandwidthLimitRuleOpts specifies parameters of a new BandwidthLimitRule. @@ -85,7 +85,7 @@ type CreateBandwidthLimitRuleOpts struct { } // ToBandwidthLimitRuleCreateMap constructs a request body from CreateBandwidthLimitRuleOpts. -func (opts CreateBandwidthLimitRuleOpts) ToBandwidthLimitRuleCreateMap() (map[string]interface{}, error) { +func (opts CreateBandwidthLimitRuleOpts) ToBandwidthLimitRuleCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "bandwidth_limit_rule") } @@ -106,7 +106,7 @@ func CreateBandwidthLimitRule(ctx context.Context, client *gophercloud.ServiceCl // UpdateBandwidthLimitRuleOptsBuilder allows to add additional parameters to the // UpdateBandwidthLimitRule request. type UpdateBandwidthLimitRuleOptsBuilder interface { - ToBandwidthLimitRuleUpdateMap() (map[string]interface{}, error) + ToBandwidthLimitRuleUpdateMap() (map[string]any, error) } // UpdateBandwidthLimitRuleOpts specifies parameters for the Update call. @@ -122,7 +122,7 @@ type UpdateBandwidthLimitRuleOpts struct { } // ToBandwidthLimitRuleUpdateMap constructs a request body from UpdateBandwidthLimitRuleOpts. -func (opts UpdateBandwidthLimitRuleOpts) ToBandwidthLimitRuleUpdateMap() (map[string]interface{}, error) { +func (opts UpdateBandwidthLimitRuleOpts) ToBandwidthLimitRuleUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "bandwidth_limit_rule") } @@ -207,7 +207,7 @@ func GetDSCPMarkingRule(ctx context.Context, c *gophercloud.ServiceClient, polic // CreateDSCPMarkingRuleOptsBuilder allows to add additional parameters to the // CreateDSCPMarkingRule request. type CreateDSCPMarkingRuleOptsBuilder interface { - ToDSCPMarkingRuleCreateMap() (map[string]interface{}, error) + ToDSCPMarkingRuleCreateMap() (map[string]any, error) } // CreateDSCPMarkingRuleOpts specifies parameters of a new DSCPMarkingRule. @@ -217,7 +217,7 @@ type CreateDSCPMarkingRuleOpts struct { } // ToDSCPMarkingRuleCreateMap constructs a request body from CreateDSCPMarkingRuleOpts. -func (opts CreateDSCPMarkingRuleOpts) ToDSCPMarkingRuleCreateMap() (map[string]interface{}, error) { +func (opts CreateDSCPMarkingRuleOpts) ToDSCPMarkingRuleCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "dscp_marking_rule") } @@ -238,7 +238,7 @@ func CreateDSCPMarkingRule(ctx context.Context, client *gophercloud.ServiceClien // UpdateDSCPMarkingRuleOptsBuilder allows to add additional parameters to the // UpdateDSCPMarkingRule request. type UpdateDSCPMarkingRuleOptsBuilder interface { - ToDSCPMarkingRuleUpdateMap() (map[string]interface{}, error) + ToDSCPMarkingRuleUpdateMap() (map[string]any, error) } // UpdateDSCPMarkingRuleOpts specifies parameters for the Update call. @@ -248,7 +248,7 @@ type UpdateDSCPMarkingRuleOpts struct { } // ToDSCPMarkingRuleUpdateMap constructs a request body from UpdateDSCPMarkingRuleOpts. -func (opts UpdateDSCPMarkingRuleOpts) ToDSCPMarkingRuleUpdateMap() (map[string]interface{}, error) { +func (opts UpdateDSCPMarkingRuleOpts) ToDSCPMarkingRuleUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "dscp_marking_rule") } @@ -334,7 +334,7 @@ func GetMinimumBandwidthRule(ctx context.Context, c *gophercloud.ServiceClient, // CreateMinimumBandwidthRuleOptsBuilder allows to add additional parameters to the // CreateMinimumBandwidthRule request. type CreateMinimumBandwidthRuleOptsBuilder interface { - ToMinimumBandwidthRuleCreateMap() (map[string]interface{}, error) + ToMinimumBandwidthRuleCreateMap() (map[string]any, error) } // CreateMinimumBandwidthRuleOpts specifies parameters of a new MinimumBandwidthRule. @@ -347,7 +347,7 @@ type CreateMinimumBandwidthRuleOpts struct { } // ToMinimumBandwidthRuleCreateMap constructs a request body from CreateMinimumBandwidthRuleOpts. -func (opts CreateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleCreateMap() (map[string]interface{}, error) { +func (opts CreateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "minimum_bandwidth_rule") } @@ -368,7 +368,7 @@ func CreateMinimumBandwidthRule(ctx context.Context, client *gophercloud.Service // UpdateMinimumBandwidthRuleOptsBuilder allows to add additional parameters to the // UpdateMinimumBandwidthRule request. type UpdateMinimumBandwidthRuleOptsBuilder interface { - ToMinimumBandwidthRuleUpdateMap() (map[string]interface{}, error) + ToMinimumBandwidthRuleUpdateMap() (map[string]any, error) } // UpdateMinimumBandwidthRuleOpts specifies parameters for the Update call. @@ -381,7 +381,7 @@ type UpdateMinimumBandwidthRuleOpts struct { } // ToMinimumBandwidthRuleUpdateMap constructs a request body from UpdateMinimumBandwidthRuleOpts. -func (opts UpdateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleUpdateMap() (map[string]interface{}, error) { +func (opts UpdateMinimumBandwidthRuleOpts) ToMinimumBandwidthRuleUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "minimum_bandwidth_rule") } diff --git a/openstack/networking/v2/extensions/qos/rules/results.go b/openstack/networking/v2/extensions/qos/rules/results.go index 39617b7e76..cb2ee24887 100644 --- a/openstack/networking/v2/extensions/qos/rules/results.go +++ b/openstack/networking/v2/extensions/qos/rules/results.go @@ -87,7 +87,7 @@ func ExtractBandwidthLimitRules(r pagination.Page) ([]BandwidthLimitRule, error) } // ExtractBandwidthLimitRulesInto extracts the elements into a slice of RBAC Policy structs. -func ExtractBandwidthLimitRulesInto(r pagination.Page, v interface{}) error { +func ExtractBandwidthLimitRulesInto(r pagination.Page, v any) error { return r.(BandwidthLimitRulePage).Result.ExtractIntoSlicePtr(v, "bandwidth_limit_rules") } @@ -163,7 +163,7 @@ func ExtractDSCPMarkingRules(r pagination.Page) ([]DSCPMarkingRule, error) { } // ExtractDSCPMarkingRulesInto extracts the elements into a slice of RBAC Policy structs. -func ExtractDSCPMarkingRulesInto(r pagination.Page, v interface{}) error { +func ExtractDSCPMarkingRulesInto(r pagination.Page, v any) error { return r.(DSCPMarkingRulePage).Result.ExtractIntoSlicePtr(v, "dscp_marking_rules") } @@ -242,6 +242,6 @@ func ExtractMinimumBandwidthRules(r pagination.Page) ([]MinimumBandwidthRule, er } // ExtractMinimumBandwidthRulesInto extracts the elements into a slice of RBAC Policy structs. -func ExtractMinimumBandwidthRulesInto(r pagination.Page, v interface{}) error { +func ExtractMinimumBandwidthRulesInto(r pagination.Page, v any) error { return r.(MinimumBandwidthRulePage).Result.ExtractIntoSlicePtr(v, "minimum_bandwidth_rules") } diff --git a/openstack/networking/v2/extensions/qos/ruletypes/results.go b/openstack/networking/v2/extensions/qos/ruletypes/results.go index 558295fdf2..a7b5786739 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/results.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/results.go @@ -37,9 +37,9 @@ type Driver struct { // SupportedParameter represents a single set of supported parameters for a some QoS driver's . type SupportedParameter struct { - ParameterName string `json:"parameter_name"` - ParameterType string `json:"parameter_type"` - ParameterValues interface{} `json:"parameter_values"` + ParameterName string `json:"parameter_name"` + ParameterType string `json:"parameter_type"` + ParameterValues any `json:"parameter_values"` } type ListRuleTypesPage struct { diff --git a/openstack/networking/v2/extensions/quotas/requests.go b/openstack/networking/v2/extensions/quotas/requests.go index 9b9e0f4a22..859c552cef 100644 --- a/openstack/networking/v2/extensions/quotas/requests.go +++ b/openstack/networking/v2/extensions/quotas/requests.go @@ -23,7 +23,7 @@ func GetDetail(ctx context.Context, client *gophercloud.ServiceClient, projectID // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToQuotaUpdateMap() (map[string]interface{}, error) + ToQuotaUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update the Networking Quotas. @@ -60,7 +60,7 @@ type UpdateOpts struct { } // ToQuotaUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToQuotaUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "quota") } diff --git a/openstack/networking/v2/extensions/quotas/results.go b/openstack/networking/v2/extensions/quotas/results.go index f430c8bb2a..8b1ae8a95d 100644 --- a/openstack/networking/v2/extensions/quotas/results.go +++ b/openstack/networking/v2/extensions/quotas/results.go @@ -148,7 +148,7 @@ func (q *QuotaDetail) UnmarshalJSON(b []byte) error { type tmp QuotaDetail var s struct { tmp - Reserved interface{} `json:"reserved"` + Reserved any `json:"reserved"` } err := json.Unmarshal(b, &s) diff --git a/openstack/networking/v2/extensions/rbacpolicies/requests.go b/openstack/networking/v2/extensions/rbacpolicies/requests.go index a2ac10da6e..6950628d11 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/requests.go +++ b/openstack/networking/v2/extensions/rbacpolicies/requests.go @@ -81,7 +81,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToRBACPolicyCreateMap() (map[string]interface{}, error) + ToRBACPolicyCreateMap() (map[string]any, error) } // CreateOpts represents options used to create a rbac-policy. @@ -93,7 +93,7 @@ type CreateOpts struct { } // ToRBACPolicyCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToRBACPolicyCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToRBACPolicyCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "rbac_policy") } @@ -123,7 +123,7 @@ func Delete(ctx context.Context, c *gophercloud.ServiceClient, rbacPolicyID stri // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToRBACPolicyUpdateMap() (map[string]interface{}, error) + ToRBACPolicyUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update a rbac-policy. @@ -132,7 +132,7 @@ type UpdateOpts struct { } // ToRBACPolicyUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToRBACPolicyUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToRBACPolicyUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "rbac_policy") } diff --git a/openstack/networking/v2/extensions/rbacpolicies/results.go b/openstack/networking/v2/extensions/rbacpolicies/results.go index 4fdebd61de..abbd044b91 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/results.go +++ b/openstack/networking/v2/extensions/rbacpolicies/results.go @@ -16,7 +16,7 @@ func (r commonResult) Extract() (*RBACPolicy, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "rbac_policy") } @@ -100,6 +100,6 @@ func ExtractRBACPolicies(r pagination.Page) ([]RBACPolicy, error) { } // ExtractRBACPolicesInto extracts the elements into a slice of RBAC Policy structs. -func ExtractRBACPolicesInto(r pagination.Page, v interface{}) error { +func ExtractRBACPolicesInto(r pagination.Page, v any) error { return r.(RBACPolicyPage).Result.ExtractIntoSlicePtr(v, "rbac_policies") } diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index caddeeaa83..a45403f13b 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -45,7 +45,7 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSecGroupCreateMap() (map[string]interface{}, error) + ToSecGroupCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new security group. @@ -66,7 +66,7 @@ type CreateOpts struct { } // ToSecGroupCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSecGroupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group") } @@ -86,7 +86,7 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToSecGroupUpdateMap() (map[string]interface{}, error) + ToSecGroupUpdateMap() (map[string]any, error) } // UpdateOpts contains all the values needed to update an existing security @@ -100,7 +100,7 @@ type UpdateOpts struct { } // ToSecGroupUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group") } diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 72372afeb1..78b67b0df0 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -83,7 +83,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSecGroupRuleCreateMap() (map[string]interface{}, error) + ToSecGroupRuleCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new security group @@ -133,7 +133,7 @@ type CreateOpts struct { } // ToSecGroupRuleCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_group_rule") } diff --git a/openstack/networking/v2/extensions/subnetpools/requests.go b/openstack/networking/v2/extensions/subnetpools/requests.go index 4656e0eda3..8823e94b2f 100644 --- a/openstack/networking/v2/extensions/subnetpools/requests.go +++ b/openstack/networking/v2/extensions/subnetpools/requests.go @@ -80,7 +80,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // CreateOptsBuilder allows to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSubnetPoolCreateMap() (map[string]interface{}, error) + ToSubnetPoolCreateMap() (map[string]any, error) } // CreateOpts specifies parameters of a new subnetpool. @@ -133,7 +133,7 @@ type CreateOpts struct { } // ToSubnetPoolCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToSubnetPoolCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSubnetPoolCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "subnetpool") } @@ -154,7 +154,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToSubnetPoolUpdateMap() (map[string]interface{}, error) + ToSubnetPoolUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update a network. @@ -204,7 +204,7 @@ type UpdateOpts struct { } // ToSubnetPoolUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToSubnetPoolUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSubnetPoolUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "subnetpool") } diff --git a/openstack/networking/v2/extensions/subnetpools/results.go b/openstack/networking/v2/extensions/subnetpools/results.go index 977d3d605a..e68fbfa9f7 100644 --- a/openstack/networking/v2/extensions/subnetpools/results.go +++ b/openstack/networking/v2/extensions/subnetpools/results.go @@ -122,9 +122,9 @@ func (r *SubnetPool) UnmarshalJSON(b []byte) error { // Support for older neutron time format var s1 struct { tmp - DefaultPrefixLen interface{} `json:"default_prefixlen"` - MinPrefixLen interface{} `json:"min_prefixlen"` - MaxPrefixLen interface{} `json:"max_prefixlen"` + DefaultPrefixLen any `json:"default_prefixlen"` + MinPrefixLen any `json:"min_prefixlen"` + MaxPrefixLen any `json:"max_prefixlen"` CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` @@ -176,9 +176,9 @@ func (r *SubnetPool) UnmarshalJSON(b []byte) error { // Support for newer neutron time format var s2 struct { tmp - DefaultPrefixLen interface{} `json:"default_prefixlen"` - MinPrefixLen interface{} `json:"min_prefixlen"` - MaxPrefixLen interface{} `json:"max_prefixlen"` + DefaultPrefixLen any `json:"default_prefixlen"` + MinPrefixLen any `json:"min_prefixlen"` + MaxPrefixLen any `json:"max_prefixlen"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` diff --git a/openstack/networking/v2/extensions/testing/delegate_test.go b/openstack/networking/v2/extensions/testing/delegate_test.go index 4741b8653f..eb130cd4b0 100644 --- a/openstack/networking/v2/extensions/testing/delegate_test.go +++ b/openstack/networking/v2/extensions/testing/delegate_test.go @@ -53,7 +53,7 @@ func TestList(t *testing.T) { Extension: common.Extension{ Updated: "2013-01-20T00:00:00-00:00", Name: "Neutron Service Type Management", - Links: []interface{}{}, + Links: []any{}, Namespace: "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", Alias: "service-type", Description: "API for retrieving service providers for Neutron advanced services", diff --git a/openstack/networking/v2/extensions/trunks/requests.go b/openstack/networking/v2/extensions/trunks/requests.go index 251e317743..a58d81ac17 100644 --- a/openstack/networking/v2/extensions/trunks/requests.go +++ b/openstack/networking/v2/extensions/trunks/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToTrunkCreateMap() (map[string]interface{}, error) + ToTrunkCreateMap() (map[string]any, error) } // CreateOpts represents the attributes used when creating a new trunk. @@ -25,7 +25,7 @@ type CreateOpts struct { } // ToTrunkCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToTrunkCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToTrunkCreateMap() (map[string]any, error) { if opts.Subports == nil { opts.Subports = []Subport{} } @@ -115,7 +115,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes } type UpdateOptsBuilder interface { - ToTrunkUpdateMap() (map[string]interface{}, error) + ToTrunkUpdateMap() (map[string]any, error) } type UpdateOpts struct { @@ -124,7 +124,7 @@ type UpdateOpts struct { Description *string `json:"description,omitempty"` } -func (opts UpdateOpts) ToTrunkUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToTrunkUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "trunk") } @@ -154,10 +154,10 @@ type AddSubportsOpts struct { } type AddSubportsOptsBuilder interface { - ToTrunkAddSubportsMap() (map[string]interface{}, error) + ToTrunkAddSubportsMap() (map[string]any, error) } -func (opts AddSubportsOpts) ToTrunkAddSubportsMap() (map[string]interface{}, error) { +func (opts AddSubportsOpts) ToTrunkAddSubportsMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -183,10 +183,10 @@ type RemoveSubportsOpts struct { } type RemoveSubportsOptsBuilder interface { - ToTrunkRemoveSubportsMap() (map[string]interface{}, error) + ToTrunkRemoveSubportsMap() (map[string]any, error) } -func (opts RemoveSubportsOpts) ToTrunkRemoveSubportsMap() (map[string]interface{}, error) { +func (opts RemoveSubportsOpts) ToTrunkRemoveSubportsMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/networking/v2/extensions/vlantransparent/requests.go b/openstack/networking/v2/extensions/vlantransparent/requests.go index cd82324dc9..994b0b34da 100644 --- a/openstack/networking/v2/extensions/vlantransparent/requests.go +++ b/openstack/networking/v2/extensions/vlantransparent/requests.go @@ -42,7 +42,7 @@ type CreateOptsExt struct { // ToNetworkCreateMap adds the vlan_transparent option to the base network // creation options. -func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]any, error) { base, err := opts.CreateOptsBuilder.ToNetworkCreateMap() if err != nil { return nil, err @@ -52,7 +52,7 @@ func (opts CreateOptsExt) ToNetworkCreateMap() (map[string]interface{}, error) { return base, nil } - networkMap := base["network"].(map[string]interface{}) + networkMap := base["network"].(map[string]any) networkMap["vlan_transparent"] = opts.VLANTransparent return base, nil @@ -67,7 +67,7 @@ type UpdateOptsExt struct { } // ToNetworkUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]any, error) { base, err := opts.UpdateOptsBuilder.ToNetworkUpdateMap() if err != nil { return nil, err @@ -77,7 +77,7 @@ func (opts UpdateOptsExt) ToNetworkUpdateMap() (map[string]interface{}, error) { return base, nil } - networkMap := base["network"].(map[string]interface{}) + networkMap := base["network"].(map[string]any) networkMap["vlan_transparent"] = opts.VLANTransparent return base, nil diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go index 74272beeee..55b4b469bf 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/requests.go @@ -20,7 +20,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToEndpointGroupCreateMap() (map[string]interface{}, error) + ToEndpointGroupCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new endpoint group @@ -46,7 +46,7 @@ type CreateOpts struct { } // ToEndpointGroupCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToEndpointGroupCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToEndpointGroupCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "endpoint_group") } @@ -121,7 +121,7 @@ func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r Del // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToEndpointGroupUpdateMap() (map[string]interface{}, error) + ToEndpointGroupUpdateMap() (map[string]any, error) } // UpdateOpts contains the values used when updating an endpoint group. @@ -131,7 +131,7 @@ type UpdateOpts struct { } // ToEndpointGroupUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOpts) ToEndpointGroupUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToEndpointGroupUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "endpoint_group") } diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go index 03f76aedb9..7d5c81e13b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/requests.go @@ -36,7 +36,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToPolicyCreateMap() (map[string]interface{}, error) + ToPolicyCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new IKE policy @@ -95,7 +95,7 @@ type LifetimeCreateOpts struct { } // ToPolicyCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPolicyCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "ikepolicy") } @@ -175,7 +175,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToPolicyUpdateMap() (map[string]interface{}, error) + ToPolicyUpdateMap() (map[string]any, error) } type LifetimeUpdateOpts struct { @@ -196,7 +196,7 @@ type UpdateOpts struct { } // ToPolicyUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "ikepolicy") } diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go index 2bb155885b..4397c0587b 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/requests.go @@ -38,7 +38,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToPolicyCreateMap() (map[string]interface{}, error) + ToPolicyCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new IPSec policy @@ -98,7 +98,7 @@ type LifetimeCreateOpts struct { } // ToPolicyCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPolicyCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "ipsecpolicy") } @@ -177,7 +177,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToPolicyUpdateMap() (map[string]interface{}, error) + ToPolicyUpdateMap() (map[string]any, error) } type LifetimeUpdateOpts struct { @@ -198,7 +198,7 @@ type UpdateOpts struct { } // ToPolicyUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "ipsecpolicy") } diff --git a/openstack/networking/v2/extensions/vpnaas/services/requests.go b/openstack/networking/v2/extensions/vpnaas/services/requests.go index 2a2b85b431..e6f500d109 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/services/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToServiceCreateMap() (map[string]interface{}, error) + ToServiceCreateMap() (map[string]any, error) } // CreateOpts contains all the values needed to create a new VPN service @@ -40,7 +40,7 @@ type CreateOpts struct { } // ToServiceCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToServiceCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToServiceCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "vpnservice") } @@ -68,7 +68,7 @@ func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r Del // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToServiceUpdateMap() (map[string]interface{}, error) + ToServiceUpdateMap() (map[string]any, error) } // UpdateOpts contains the values used when updating a VPN service @@ -84,7 +84,7 @@ type UpdateOpts struct { } // ToServiceUpdateMap casts aa UodateOpts struct to a map. -func (opts UpdateOpts) ToServiceUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToServiceUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "vpnservice") } diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go index bbe709467d..1104cef0ee 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToConnectionCreateMap() (map[string]interface{}, error) + ToConnectionCreateMap() (map[string]any, error) } type Action string type Initiator string @@ -115,7 +115,7 @@ type CreateOpts struct { } // ToConnectionCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToConnectionCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToConnectionCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "ipsec_site_connection") } @@ -201,7 +201,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToConnectionUpdateMap() (map[string]interface{}, error) + ToConnectionUpdateMap() (map[string]any, error) } // UpdateOpts contains the values used when updating the DPD of an IPSec site connection @@ -229,7 +229,7 @@ type UpdateOpts struct { } // ToConnectionUpdateMap casts an UpdateOpts struct to a map. -func (opts UpdateOpts) ToConnectionUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToConnectionUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "ipsec_site_connection") } diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go index 1fcbaa9e9a..29d14187ab 100644 --- a/openstack/networking/v2/networks/requests.go +++ b/openstack/networking/v2/networks/requests.go @@ -71,7 +71,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToNetworkCreateMap() (map[string]interface{}, error) + ToNetworkCreateMap() (map[string]any, error) } // CreateOpts represents options used to create a network. @@ -86,7 +86,7 @@ type CreateOpts struct { } // ToNetworkCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToNetworkCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "network") } @@ -111,7 +111,7 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToNetworkUpdateMap() (map[string]interface{}, error) + ToNetworkUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update a network. @@ -128,7 +128,7 @@ type UpdateOpts struct { } // ToNetworkUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToNetworkUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "network") } diff --git a/openstack/networking/v2/networks/results.go b/openstack/networking/v2/networks/results.go index 01dcbaedb7..53927bf0c0 100644 --- a/openstack/networking/v2/networks/results.go +++ b/openstack/networking/v2/networks/results.go @@ -19,7 +19,7 @@ func (r commonResult) Extract() (*Network, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "network") } @@ -172,6 +172,6 @@ func ExtractNetworks(r pagination.Page) ([]Network, error) { return s, err } -func ExtractNetworksInto(r pagination.Page, v interface{}) error { +func ExtractNetworksInto(r pagination.Page, v any) error { return r.(NetworkPage).Result.ExtractIntoSlicePtr(v, "networks") } diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index 750548af06..218c2897f7 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -109,7 +109,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToPortCreateMap() (map[string]interface{}, error) + ToPortCreateMap() (map[string]any, error) } // CreateOpts represents the attributes used when creating a new port. @@ -119,7 +119,7 @@ type CreateOpts struct { Description string `json:"description,omitempty"` AdminStateUp *bool `json:"admin_state_up,omitempty"` MACAddress string `json:"mac_address,omitempty"` - FixedIPs interface{} `json:"fixed_ips,omitempty"` + FixedIPs any `json:"fixed_ips,omitempty"` DeviceID string `json:"device_id,omitempty"` DeviceOwner string `json:"device_owner,omitempty"` TenantID string `json:"tenant_id,omitempty"` @@ -131,7 +131,7 @@ type CreateOpts struct { } // ToPortCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToPortCreateMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "port") if err != nil { return nil, err @@ -143,13 +143,13 @@ func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) { // AddValueSpecs expands the 'value_specs' object and removes 'value_specs' // from the request body. It will return error if the value specs would overwrite // an existing field or contains forbidden keys. -func AddValueSpecs(body map[string]interface{}) (map[string]interface{}, error) { +func AddValueSpecs(body map[string]any) (map[string]any, error) { // Banned the same as in heat. See https://github.com/openstack/heat/blob/dd7319e373b88812cb18897f742b5196a07227ea/heat/engine/resources/openstack/neutron/neutron.py#L59 bannedKeys := []string{"shared", "tenant_id"} - port := body["port"].(map[string]interface{}) + port := body["port"].(map[string]any) if port["value_specs"] != nil { - for k, v := range port["value_specs"].(map[string]interface{}) { + for k, v := range port["value_specs"].(map[string]any) { if slices.Contains(bannedKeys, k) { return nil, fmt.Errorf("forbidden key in value_specs: %s", k) } @@ -181,7 +181,7 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToPortUpdateMap() (map[string]interface{}, error) + ToPortUpdateMap() (map[string]any, error) } // UpdateOpts represents the attributes used when updating an existing port. @@ -189,7 +189,7 @@ 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"` + FixedIPs any `json:"fixed_ips,omitempty"` DeviceID *string `json:"device_id,omitempty"` DeviceOwner *string `json:"device_owner,omitempty"` SecurityGroups *[]string `json:"security_groups,omitempty"` @@ -204,7 +204,7 @@ type UpdateOpts struct { } // ToPortUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToPortUpdateMap() (map[string]any, error) { body, err := gophercloud.BuildRequestBody(opts, "port") if err != nil { return nil, err diff --git a/openstack/networking/v2/ports/results.go b/openstack/networking/v2/ports/results.go index c9bb2446db..74a0fa3b49 100644 --- a/openstack/networking/v2/ports/results.go +++ b/openstack/networking/v2/ports/results.go @@ -19,7 +19,7 @@ func (r commonResult) Extract() (*Port, error) { return &s, err } -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "port") } @@ -201,6 +201,6 @@ func ExtractPorts(r pagination.Page) ([]Port, error) { return s, err } -func ExtractPortsInto(r pagination.Page, v interface{}) error { +func ExtractPortsInto(r pagination.Page, v any) error { return r.(PortPage).Result.ExtractIntoSlicePtr(v, "ports") } diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 89ca3ce8bf..20f747b5a9 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -80,7 +80,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetRes // CreateOptsBuilder allows extensions to add additional parameters to the // List request. type CreateOptsBuilder interface { - ToSubnetCreateMap() (map[string]interface{}, error) + ToSubnetCreateMap() (map[string]any, error) } // CreateOpts represents the attributes used when creating a new subnet. @@ -145,13 +145,13 @@ type CreateOpts struct { } // ToSubnetCreateMap builds a request body from CreateOpts. -func (opts CreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSubnetCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "subnet") if err != nil { return nil, err } - if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" { + if m := b["subnet"].(map[string]any); m["gateway_ip"] == "" { m["gateway_ip"] = nil } @@ -175,7 +175,7 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToSubnetUpdateMap() (map[string]interface{}, error) + ToSubnetUpdateMap() (map[string]any, error) } // UpdateOpts represents the attributes used when updating an existing subnet. @@ -214,13 +214,13 @@ type UpdateOpts struct { } // ToSubnetUpdateMap builds a request body from UpdateOpts. -func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "subnet") if err != nil { return nil, err } - if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" { + if m := b["subnet"].(map[string]any); m["gateway_ip"] == "" { m["gateway_ip"] = nil } diff --git a/openstack/networking/v2/subnets/testing/results_test.go b/openstack/networking/v2/subnets/testing/results_test.go index e3caf5fa2f..273607bb51 100644 --- a/openstack/networking/v2/subnets/testing/results_test.go +++ b/openstack/networking/v2/subnets/testing/results_test.go @@ -39,7 +39,7 @@ func TestHostRoute(t *testing.T) { }} `) - var dejson interface{} + var dejson any err := json.Unmarshal(sejson, &dejson) if err != nil { t.Fatalf("%s", err) diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go index b9f872e874..e98c542960 100644 --- a/openstack/objectstorage/v1/objects/results.go +++ b/openstack/objectstorage/v1/objects/results.go @@ -162,7 +162,7 @@ func (r *DownloadHeader) UnmarshalJSON(b []byte) error { Date gophercloud.JSONRFC1123 `json:"Date"` DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` - StaticLargeObject interface{} `json:"X-Static-Large-Object"` + StaticLargeObject any `json:"X-Static-Large-Object"` } err := json.Unmarshal(b, &s) if err != nil { @@ -241,7 +241,7 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { Date gophercloud.JSONRFC1123 `json:"Date"` DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` - StaticLargeObject interface{} `json:"X-Static-Large-Object"` + StaticLargeObject any `json:"X-Static-Large-Object"` } err := json.Unmarshal(b, &s) if err != nil { diff --git a/openstack/orchestration/v1/resourcetypes/results.go b/openstack/orchestration/v1/resourcetypes/results.go index 56a2d3dbad..f78d4d83fe 100644 --- a/openstack/orchestration/v1/resourcetypes/results.go +++ b/openstack/orchestration/v1/resourcetypes/results.go @@ -59,7 +59,7 @@ type ConstraintSchema struct { Range *MinMaxConstraint `json:"range,omitempty"` Length *MinMaxConstraint `json:"length,omitempty"` Modulo *ModuloConstraint `json:"modulo,omitempty"` - AllowedValues *[]interface{} `json:"allowed_values,omitempty"` + AllowedValues *[]any `json:"allowed_values,omitempty"` AllowedPattern *string `json:"allowed_pattern,omitempty"` CustomConstraint *string `json:"custom_constraint,omitempty"` } @@ -68,7 +68,7 @@ type ConstraintSchema struct { type PropertySchema struct { Type PropertyType `json:"type"` Description string `json:"description,omitempty"` - Default interface{} `json:"default,omitempty"` + Default any `json:"default,omitempty"` Constraints []ConstraintSchema `json:"constraints,omitempty"` Required bool `json:"required"` Immutable bool `json:"immutable"` @@ -144,7 +144,7 @@ type TemplateResult struct { // Extract returns a Template object and is called after a Template get // operation. -func (r TemplateResult) Extract() (template map[string]interface{}, err error) { +func (r TemplateResult) Extract() (template map[string]any, err error) { err = r.ExtractInto(&template) return } diff --git a/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go b/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go index c97ba01e4e..37a91b0901 100644 --- a/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go +++ b/openstack/orchestration/v1/resourcetypes/testing/fixtures_test.go @@ -155,7 +155,7 @@ var GetSchemaExpected = resourcetypes.ResourceSchema{ Description: "The format of the local ephemeral block device.", Constraints: []resourcetypes.ConstraintSchema{ { - AllowedValues: &[]interface{}{ + AllowedValues: &[]any{ "ext3", "ext4", "xfs", }, }, @@ -188,7 +188,7 @@ var GetSchemaExpected = resourcetypes.ResourceSchema{ Default: "REBUILD", Constraints: []resourcetypes.ConstraintSchema{ { - AllowedValues: &[]interface{}{ + AllowedValues: &[]any{ "REBUILD", "REPLACE", }, }, diff --git a/openstack/orchestration/v1/stackevents/results.go b/openstack/orchestration/v1/stackevents/results.go index 45673b06bd..ad7d8b4096 100644 --- a/openstack/orchestration/v1/stackevents/results.go +++ b/openstack/orchestration/v1/stackevents/results.go @@ -27,7 +27,7 @@ type Event struct { // The event ID. ID string `json:"id"` // Properties of the stack resource. - ResourceProperties map[string]interface{} `json:"resource_properties"` + ResourceProperties map[string]any `json:"resource_properties"` } func (r *Event) UnmarshalJSON(b []byte) error { diff --git a/openstack/orchestration/v1/stackresources/requests.go b/openstack/orchestration/v1/stackresources/requests.go index 6d1b12b477..1d8ed15864 100644 --- a/openstack/orchestration/v1/stackresources/requests.go +++ b/openstack/orchestration/v1/stackresources/requests.go @@ -95,12 +95,12 @@ type MarkUnhealthyOpts struct { // MarkUnhealthyOptsBuilder is the interface options structs have to satisfy in order // to be used in the MarkUnhealthy operation in this package type MarkUnhealthyOptsBuilder interface { - ToMarkUnhealthyMap() (map[string]interface{}, error) + ToMarkUnhealthyMap() (map[string]any, error) } // ToMarkUnhealthyMap validates that a template was supplied and calls // the ToMarkUnhealthyMap private function. -func (opts MarkUnhealthyOpts) ToMarkUnhealthyMap() (map[string]interface{}, error) { +func (opts MarkUnhealthyOpts) ToMarkUnhealthyMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go index 48e1f3e649..b710c66d5a 100644 --- a/openstack/orchestration/v1/stackresources/results.go +++ b/openstack/orchestration/v1/stackresources/results.go @@ -10,19 +10,19 @@ import ( // Resource represents a stack resource. type Resource struct { - Attributes map[string]interface{} `json:"attributes"` - CreationTime time.Time `json:"-"` - Description string `json:"description"` - Links []gophercloud.Link `json:"links"` - LogicalID string `json:"logical_resource_id"` - Name string `json:"resource_name"` - ParentResource string `json:"parent_resource"` - PhysicalID string `json:"physical_resource_id"` - RequiredBy []interface{} `json:"required_by"` - Status string `json:"resource_status"` - StatusReason string `json:"resource_status_reason"` - Type string `json:"resource_type"` - UpdatedTime time.Time `json:"-"` + Attributes map[string]any `json:"attributes"` + CreationTime time.Time `json:"-"` + Description string `json:"description"` + Links []gophercloud.Link `json:"links"` + LogicalID string `json:"logical_resource_id"` + Name string `json:"resource_name"` + ParentResource string `json:"parent_resource"` + PhysicalID string `json:"physical_resource_id"` + RequiredBy []any `json:"required_by"` + Status string `json:"resource_status"` + StatusReason string `json:"resource_status_reason"` + Type string `json:"resource_type"` + UpdatedTime time.Time `json:"-"` } func (r *Resource) UnmarshalJSON(b []byte) error { @@ -180,10 +180,10 @@ func ExtractResourceTypes(r pagination.Page) (ResourceTypes, error) { // TypeSchema represents a stack resource schema. type TypeSchema struct { - Attributes map[string]interface{} `json:"attributes"` - Properties map[string]interface{} `json:"properties"` - ResourceType string `json:"resource_type"` - SupportStatus map[string]interface{} `json:"support_status"` + Attributes map[string]any `json:"attributes"` + Properties map[string]any `json:"properties"` + ResourceType string `json:"resource_type"` + SupportStatus map[string]any `json:"support_status"` } // SchemaResult represents the result of a Schema operation. diff --git a/openstack/orchestration/v1/stackresources/testing/fixtures_test.go b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go index e675ab642a..a306d8de8b 100644 --- a/openstack/orchestration/v1/stackresources/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go @@ -33,11 +33,11 @@ var FindExpected = []stackresources.Resource{ StatusReason: "state changed", UpdatedTime: Updated_time, CreationTime: Create_time, - RequiredBy: []interface{}{}, + RequiredBy: []any{}, Status: "CREATE_IN_PROGRESS", PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf", Type: "OS::Nova::Server", - Attributes: map[string]interface{}{"SXSW": "atx"}, + Attributes: map[string]any{"SXSW": "atx"}, Description: "Some resource", }, } @@ -104,11 +104,11 @@ var ListExpected = []stackresources.Resource{ StatusReason: "state changed", UpdatedTime: Updated_time, CreationTime: Create_time, - RequiredBy: []interface{}{}, + RequiredBy: []any{}, Status: "CREATE_IN_PROGRESS", PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf", Type: "OS::Nova::Server", - Attributes: map[string]interface{}{"SXSW": "atx"}, + Attributes: map[string]any{"SXSW": "atx"}, Description: "Some resource", }, } @@ -180,10 +180,10 @@ var GetExpected = &stackresources.Resource{ }, }, LogicalID: "wordpress_instance", - Attributes: map[string]interface{}{"SXSW": "atx"}, + Attributes: map[string]any{"SXSW": "atx"}, StatusReason: "state changed", UpdatedTime: Updated_time, - RequiredBy: []interface{}{}, + RequiredBy: []any{}, Status: "CREATE_COMPLETE", PhysicalID: "00e3a2fe-c65d-403c-9483-4db9930dd194", Type: "OS::Nova::Server", @@ -315,13 +315,13 @@ func HandleListTypesSuccessfully(t *testing.T, output string) { // GetSchemaExpected represents the expected object from a Schema request. var GetSchemaExpected = &stackresources.TypeSchema{ - Attributes: map[string]interface{}{ - "an_attribute": map[string]interface{}{ + Attributes: map[string]any{ + "an_attribute": map[string]any{ "description": "An attribute description .", }, }, - Properties: map[string]interface{}{ - "a_property": map[string]interface{}{ + Properties: map[string]any{ + "a_property": map[string]any{ "update_allowed": false, "required": true, "type": "string", @@ -329,7 +329,7 @@ var GetSchemaExpected = &stackresources.TypeSchema{ }, }, ResourceType: "OS::Heat::AResourceName", - SupportStatus: map[string]interface{}{ + SupportStatus: map[string]any{ "message": "A status message", "status": "SUPPORTED", "version": "2014.1", diff --git a/openstack/orchestration/v1/stacks/doc.go b/openstack/orchestration/v1/stacks/doc.go index 9d3be7097a..772c0f4450 100644 --- a/openstack/orchestration/v1/stacks/doc.go +++ b/openstack/orchestration/v1/stacks/doc.go @@ -41,7 +41,7 @@ Example of List Stack: Example to Create an Stack // Create Template - t := make(map[string]interface{}) + t := make(map[string]any) f, err := os.ReadFile("template.yaml") if err != nil { panic(err) @@ -56,7 +56,7 @@ Example to Create an Stack Bin: f, } // Create Environment if needed - t_env := make(map[string]interface{}) + t_env := make(map[string]any) f_env, err := os.ReadFile("env.yaml") if err != nil { panic(err) @@ -154,7 +154,7 @@ raw_template value. Example to Update a Stack Using the Update (PUT) Method - t := make(map[string]interface{}) + t := make(map[string]any) f, err := os.ReadFile("template.yaml") if err != nil { panic(err) @@ -169,7 +169,7 @@ Example to Update a Stack Using the Update (PUT) Method Bin: f, } - var params = make(map[string]interface{}) + var params = make(map[string]any) params["number_of_nodes"] = 2 stackName := "my_stack" @@ -187,7 +187,7 @@ Example to Update a Stack Using the Update (PUT) Method Example to Update a Stack Using the UpdatePatch (PATCH) Method - var params = make(map[string]interface{}) + var params = make(map[string]any) params["number_of_nodes"] = 2 stackName := "my_stack" diff --git a/openstack/orchestration/v1/stacks/environment.go b/openstack/orchestration/v1/stacks/environment.go index f7bc155e06..bb9004a9f8 100644 --- a/openstack/orchestration/v1/stacks/environment.go +++ b/openstack/orchestration/v1/stacks/environment.go @@ -52,7 +52,7 @@ func (e *Environment) getRRFileContents(ignoreIf igFunc) error { // search the resource registry for URLs switch rr.(type) { // process further only if the resource registry is a map - case map[string]interface{}, map[interface{}]interface{}: + case map[string]any, map[any]any: rrMap, err := toStringKeys(rr) if err != nil { return err @@ -83,14 +83,14 @@ func (e *Environment) getRRFileContents(ignoreIf igFunc) error { if val, ok := rrMap["resources"]; ok { switch val.(type) { // process further only if the contents are a map - case map[string]interface{}, map[interface{}]interface{}: + case map[string]any, map[any]any: resourcesMap, err := toStringKeys(val) if err != nil { return err } for _, v := range resourcesMap { switch v.(type) { - case map[string]interface{}, map[interface{}]interface{}: + case map[string]any, map[any]any: resourceMap, err := toStringKeys(v) if err != nil { return err @@ -130,7 +130,7 @@ func (e *Environment) getRRFileContents(ignoreIf igFunc) error { } // function to choose keys whose values are other environment files -func ignoreIfEnvironment(key string, value interface{}) bool { +func ignoreIfEnvironment(key string, value any) bool { // base_url and hooks refer to components which cannot have urls if key == "base_url" || key == "hooks" { return true diff --git a/openstack/orchestration/v1/stacks/environment_test.go b/openstack/orchestration/v1/stacks/environment_test.go index 21e9f716ae..b69e527050 100644 --- a/openstack/orchestration/v1/stacks/environment_test.go +++ b/openstack/orchestration/v1/stacks/environment_test.go @@ -49,7 +49,7 @@ func TestEnvironmentParsing(t *testing.T) { func TestIgnoreIfEnvironment(t *testing.T) { var keyValueTests = []struct { key string - value interface{} + value any out bool }{ {"base_url", "afksdf", true}, @@ -153,11 +153,11 @@ service_db: "my_db.yaml": fakeDBURL, } - expectedParsed := map[string]interface{}{ - "resource_registry": map[string]interface{}{ + expectedParsed := map[string]any{ + "resource_registry": map[string]any{ "My::WP::Server": fakeEnvURL, - "resources": map[string]interface{}{ - "my_db_server": map[string]interface{}{ + "resources": map[string]any{ + "my_db_server": map[string]any{ "OS::DBInstance": fakeDBURL, }, }, diff --git a/openstack/orchestration/v1/stacks/fixtures.go b/openstack/orchestration/v1/stacks/fixtures.go index 58987d4bfd..4aea58a590 100644 --- a/openstack/orchestration/v1/stacks/fixtures.go +++ b/openstack/orchestration/v1/stacks/fixtures.go @@ -142,30 +142,30 @@ parameter_defaults: ` // ValidJSONEnvironmentParsed is the expected parsed version of ValidJSONEnvironment -var ValidJSONEnvironmentParsed = map[string]interface{}{ - "parameters": map[string]interface{}{ +var ValidJSONEnvironmentParsed = map[string]any{ + "parameters": map[string]any{ "user_key": "userkey", }, - "resource_registry": map[string]interface{}{ + "resource_registry": map[string]any{ "My::WP::Server": "file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml", "OS::Quantum*": "OS::Neutron*", "AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml", "OS::Metering::Alarm": "OS::Ceilometer::Alarm", "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml", - "resources": map[string]interface{}{ - "my_db_server": map[string]interface{}{ + "resources": map[string]any{ + "my_db_server": map[string]any{ "OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml", }, - "my_server": map[string]interface{}{ + "my_server": map[string]any{ "OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml", "hooks": "pre-create", }, - "nested_stack": map[string]interface{}{ - "nested_resource": map[string]interface{}{ + "nested_stack": map[string]any{ + "nested_resource": map[string]any{ "hooks": "pre-update", }, - "another_resource": map[string]interface{}{ - "hooks": []interface{}{ + "another_resource": map[string]any{ + "hooks": []any{ "pre-create", "pre-update", }, @@ -176,19 +176,19 @@ var ValidJSONEnvironmentParsed = map[string]interface{}{ } // ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate -var ValidJSONTemplateParsed = map[string]interface{}{ +var ValidJSONTemplateParsed = map[string]any{ "heat_template_version": "2014-10-16", - "parameters": map[string]interface{}{ - "flavor": map[string]interface{}{ + "parameters": map[string]any{ + "flavor": map[string]any{ "default": "debian2G", "description": "Flavor for the server to be created", "hidden": true, "type": "string", }, }, - "resources": map[string]interface{}{ - "test_server": map[string]interface{}{ - "properties": map[string]interface{}{ + "resources": map[string]any{ + "test_server": map[string]any{ + "properties": map[string]any{ "flavor": "2 GB General Purpose v1", "image": "Debian 7 (Wheezy) (PVHVM)", "name": "test-server", diff --git a/openstack/orchestration/v1/stacks/requests.go b/openstack/orchestration/v1/stacks/requests.go index 3c8620bc2c..c553351344 100644 --- a/openstack/orchestration/v1/stacks/requests.go +++ b/openstack/orchestration/v1/stacks/requests.go @@ -14,7 +14,7 @@ import ( // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type CreateOptsBuilder interface { - ToStackCreateMap() (map[string]interface{}, error) + ToStackCreateMap() (map[string]any, error) } // CreateOpts is the common options struct used in this package's Create @@ -32,7 +32,7 @@ type CreateOpts struct { // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. - Parameters map[string]interface{} `json:"parameters,omitempty"` + Parameters map[string]any `json:"parameters,omitempty"` // The timeout for stack creation in minutes. Timeout int `json:"timeout_mins,omitempty"` // A list of tags to assosciate with the Stack @@ -40,7 +40,7 @@ type CreateOpts struct { } // ToStackCreateMap casts a CreateOpts struct to a map. -func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToStackCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -102,7 +102,7 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu // extensions decorate or modify the common logic, it is useful for them to // satisfy a basic interface in order for them to be used. type AdoptOptsBuilder interface { - ToStackAdoptMap() (map[string]interface{}, error) + ToStackAdoptMap() (map[string]any, error) } // AdoptOpts is the common options struct used in this package's Adopt @@ -128,11 +128,11 @@ type AdoptOpts struct { // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. - Parameters map[string]interface{} `json:"parameters,omitempty"` + Parameters map[string]any `json:"parameters,omitempty"` } // ToStackAdoptMap casts a CreateOpts struct to a map. -func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) { +func (opts AdoptOpts) ToStackAdoptMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -311,13 +311,13 @@ func Find(ctx context.Context, c *gophercloud.ServiceClient, stackIdentity strin // UpdateOptsBuilder is the interface options structs have to satisfy in order // to be used in the Update operation in this package. type UpdateOptsBuilder interface { - ToStackUpdateMap() (map[string]interface{}, error) + ToStackUpdateMap() (map[string]any, error) } // UpdatePatchOptsBuilder is the interface options structs have to satisfy in order // to be used in the UpdatePatch operation in this package type UpdatePatchOptsBuilder interface { - ToStackUpdatePatchMap() (map[string]interface{}, error) + ToStackUpdatePatchMap() (map[string]any, error) } // UpdateOpts contains the common options struct used in this package's Update @@ -329,7 +329,7 @@ type UpdateOpts struct { // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. - Parameters map[string]interface{} `json:"parameters,omitempty"` + Parameters map[string]any `json:"parameters,omitempty"` // The timeout for stack creation in minutes. Timeout int `json:"timeout_mins,omitempty"` // A list of tags to associate with the Stack @@ -338,7 +338,7 @@ type UpdateOpts struct { // ToStackUpdateMap validates that a template was supplied and calls // the toStackUpdateMap private function. -func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToStackUpdateMap() (map[string]any, error) { if opts.TemplateOpts == nil { return nil, ErrTemplateRequired{} } @@ -347,12 +347,12 @@ func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) { // ToStackUpdatePatchMap calls the private function toStackUpdateMap // directly. -func (opts UpdateOpts) ToStackUpdatePatchMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToStackUpdatePatchMap() (map[string]any, error) { return toStackUpdateMap(opts) } // ToStackUpdateMap casts a CreateOpts struct to a map. -func toStackUpdateMap(opts UpdateOpts) (map[string]interface{}, error) { +func toStackUpdateMap(opts UpdateOpts) (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -437,7 +437,7 @@ func Delete(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackI // PreviewOptsBuilder is the interface options structs have to satisfy in order // to be used in the Preview operation in this package. type PreviewOptsBuilder interface { - ToStackPreviewMap() (map[string]interface{}, error) + ToStackPreviewMap() (map[string]any, error) } // PreviewOpts contains the common options struct used in this package's Preview @@ -457,11 +457,11 @@ type PreviewOpts struct { // A structure that contains details for the environment of the stack. EnvironmentOpts *Environment `json:"-"` // User-defined parameters to pass to the template. - Parameters map[string]interface{} `json:"parameters,omitempty"` + Parameters map[string]any `json:"parameters,omitempty"` } // ToStackPreviewMap casts a PreviewOpts struct to a map. -func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) { +func (opts PreviewOpts) ToStackPreviewMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go index b501d960a5..94a0695ef5 100644 --- a/openstack/orchestration/v1/stacks/results.go +++ b/openstack/orchestration/v1/stacks/results.go @@ -115,22 +115,22 @@ func ExtractStacks(r pagination.Page) ([]ListedStack, error) { // RetrievedStack represents the object extracted from a Get operation. type RetrievedStack struct { - Capabilities []interface{} `json:"capabilities"` - CreationTime time.Time `json:"-"` - Description string `json:"description"` - DisableRollback bool `json:"disable_rollback"` - ID string `json:"id"` - Links []gophercloud.Link `json:"links"` - NotificationTopics []interface{} `json:"notification_topics"` - Outputs []map[string]interface{} `json:"outputs"` - Parameters map[string]string `json:"parameters"` - Name string `json:"stack_name"` - Status string `json:"stack_status"` - StatusReason string `json:"stack_status_reason"` - Tags []string `json:"tags"` - TemplateDescription string `json:"template_description"` - Timeout int `json:"timeout_mins"` - UpdatedTime time.Time `json:"-"` + Capabilities []any `json:"capabilities"` + CreationTime time.Time `json:"-"` + Description string `json:"description"` + DisableRollback bool `json:"disable_rollback"` + ID string `json:"id"` + Links []gophercloud.Link `json:"links"` + NotificationTopics []any `json:"notification_topics"` + Outputs []map[string]any `json:"outputs"` + Parameters map[string]string `json:"parameters"` + Name string `json:"stack_name"` + Status string `json:"stack_status"` + StatusReason string `json:"stack_status_reason"` + Tags []string `json:"tags"` + TemplateDescription string `json:"template_description"` + Timeout int `json:"timeout_mins"` + UpdatedTime time.Time `json:"-"` } func (r *RetrievedStack) UnmarshalJSON(b []byte) error { @@ -200,16 +200,16 @@ type DeleteResult struct { // PreviewedStack represents the result of a Preview operation. type PreviewedStack struct { - Capabilities []interface{} `json:"capabilities"` + Capabilities []any `json:"capabilities"` CreationTime time.Time `json:"-"` Description string `json:"description"` DisableRollback bool `json:"disable_rollback"` ID string `json:"id"` Links []gophercloud.Link `json:"links"` Name string `json:"stack_name"` - NotificationTopics []interface{} `json:"notification_topics"` + NotificationTopics []any `json:"notification_topics"` Parameters map[string]string `json:"parameters"` - Resources []interface{} `json:"resources"` + Resources []any `json:"resources"` TemplateDescription string `json:"template_description"` Timeout int `json:"timeout_mins"` UpdatedTime time.Time `json:"-"` @@ -272,16 +272,16 @@ func (r PreviewResult) Extract() (*PreviewedStack, error) { // AbandonedStack represents the result of an Abandon operation. type AbandonedStack struct { - Status string `json:"status"` - Name string `json:"name"` - Template map[string]interface{} `json:"template"` - Action string `json:"action"` - ID string `json:"id"` - Resources map[string]interface{} `json:"resources"` - Files map[string]string `json:"files"` - StackUserProjectID string `json:"stack_user_project_id"` - ProjectID string `json:"project_id"` - Environment map[string]interface{} `json:"environment"` + Status string `json:"status"` + Name string `json:"name"` + Template map[string]any `json:"template"` + Action string `json:"action"` + ID string `json:"id"` + Resources map[string]any `json:"resources"` + Files map[string]string `json:"files"` + StackUserProjectID string `json:"stack_user_project_id"` + ProjectID string `json:"project_id"` + Environment map[string]any `json:"environment"` } // AbandonResult represents the result of an Abandon operation. diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index bb71ac9dfa..75eb6a97cf 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -84,7 +84,7 @@ func (t *Template) makeChildTemplate(childURL string, ignoreIf igFunc, recurse b // Applies the transformation for getFileContents() to just one element of a map. // In case the element requires transforming, the function returns its new value. -func (t *Template) mapElemFileContents(k interface{}, v interface{}, ignoreIf igFunc, recurse bool) (interface{}, error) { +func (t *Template) mapElemFileContents(k any, v any, ignoreIf igFunc, recurse bool) (any, error) { key, ok := k.(string) if !ok { return nil, fmt.Errorf("can't convert map key to string: %v", k) @@ -130,7 +130,7 @@ func (t *Template) mapElemFileContents(k interface{}, v interface{}, ignoreIf ig // parameter of the template structure. This is the only way that a user can // use child templates that are located in their filesystem; urls located on the // web (e.g. on github or swift) can be fetched directly by Heat engine. -func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool) error { +func (t *Template) getFileContents(te any, ignoreIf igFunc, recurse bool) error { // initialize template if empty if t.Files == nil { t.Files = make(map[string]string) @@ -143,7 +143,7 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool switch teTyped := (te).(type) { // if te is a map[string], go check all elements for URLs to replace - case map[string]interface{}: + case map[string]any: for k, v := range teTyped { newVal, err := t.mapElemFileContents(k, v, ignoreIf, recurse) if err != nil { @@ -155,7 +155,7 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool } // same if te is a map[non-string] (can't group with above case because we // can't range over and update 'te' without knowing its key type) - case map[interface{}]interface{}: + case map[any]any: for k, v := range teTyped { newVal, err := t.mapElemFileContents(k, v, ignoreIf, recurse) if err != nil { @@ -166,7 +166,7 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool } } // if te is a slice, call the function on each element of the slice. - case []interface{}: + case []any: for i := range teTyped { if err := t.getFileContents(teTyped[i], ignoreIf, recurse); err != nil { return err @@ -191,7 +191,7 @@ func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool } // function to choose keys whose values are other template files -func ignoreIfTemplate(key string, value interface{}) bool { +func ignoreIfTemplate(key string, value any) bool { // key must be either `get_file` or `type` for value to be a URL if key != "get_file" && key != "type" { return true diff --git a/openstack/orchestration/v1/stacks/template_test.go b/openstack/orchestration/v1/stacks/template_test.go index 593f464580..9ab257d70d 100644 --- a/openstack/orchestration/v1/stacks/template_test.go +++ b/openstack/orchestration/v1/stacks/template_test.go @@ -45,7 +45,7 @@ func TestTemplateParsing(t *testing.T) { func TestIgnoreIfTemplate(t *testing.T) { var keyValueTests = []struct { key string - value interface{} + value any out bool }{ {"not_get_file", "afksdf", true}, @@ -83,25 +83,25 @@ resources: networks: - {uuid: 11111111-1111-1111-1111-111111111111}` -var myNovaExpected = map[string]interface{}{ +var myNovaExpected = map[string]any{ "heat_template_version": "2014-10-16", - "parameters": map[string]interface{}{ - "flavor": map[string]interface{}{ + "parameters": map[string]any{ + "flavor": map[string]any{ "type": "string", "description": "Flavor for the server to be created", "default": 4353, "hidden": true, }, }, - "resources": map[string]interface{}{ - "test_server": map[string]interface{}{ + "resources": map[string]any{ + "test_server": map[string]any{ "type": "OS::Nova::Server", - "properties": map[string]interface{}{ + "properties": map[string]any{ "name": "test-server", "flavor": "2 GB General Purpose v1", "image": "Debian 7 (Wheezy) (PVHVM)", - "networks": []interface{}{ - map[string]interface{}{ + "networks": []any{ + map[string]any{ "uuid": "11111111-1111-1111-1111-111111111111", }, }, @@ -130,10 +130,10 @@ resources: th.AssertNoErr(t, te.getFileContents(te.Parsed, ignoreIfTemplate, true)) // Now check template and referenced file - expectedParsed := map[string]interface{}{ + expectedParsed := map[string]any{ "heat_template_version": "2015-04-30", - "resources": map[string]interface{}{ - "my_server": map[string]interface{}{ + "resources": map[string]any{ + "my_server": map[string]any{ "type": fakeURL, }, }, @@ -176,14 +176,14 @@ resources: "somefile": "Welcome!", } th.AssertEquals(t, expectedFiles["somefile"], te.Files[fakeURL]) - expectedParsed := map[string]interface{}{ + expectedParsed := map[string]any{ "heat_template_version": "2015-04-30", - "resources": map[string]interface{}{ - "test_resource": map[string]interface{}{ + "resources": map[string]any{ + "test_resource": map[string]any{ "type": "OS::Heat::TestResource", - "properties": map[string]interface{}{ + "properties": map[string]any{ "path": "somefile", - "value": map[string]interface{}{ + "value": map[string]any{ "get_file": fakeURL, }, }, @@ -215,20 +215,20 @@ resources: image: Debian 7 (Wheezy) (PVHVM) networks: - {uuid: 11111111-1111-1111-1111-111111111111}` - mySubstrackExpected := map[string]interface{}{ + mySubstrackExpected := map[string]any{ "heat_template_version": "2015-04-30", - "resources": map[string]interface{}{ - "my_server": map[string]interface{}{ + "resources": map[string]any{ + "my_server": map[string]any{ "type": novaURL, }, - "my_backend": map[string]interface{}{ + "my_backend": map[string]any{ "type": "OS::Nova::Server", - "properties": map[string]interface{}{ + "properties": map[string]any{ "name": "test-backend", "flavor": "4 GB General Purpose v1", "image": "Debian 7 (Wheezy) (PVHVM)", - "networks": []interface{}{ - map[string]interface{}{ + "networks": []any{ + map[string]any{ "uuid": "11111111-1111-1111-1111-111111111111", }, }, @@ -250,10 +250,10 @@ resources: th.AssertNoErr(t, te.Parse()) th.AssertNoErr(t, te.getFileContents(te.Parsed, ignoreIfTemplate, true)) - expectedParsed := map[string]interface{}{ + expectedParsed := map[string]any{ "heat_template_version": "2015-04-30", - "resources": map[string]interface{}{ - "my_stack": map[string]interface{}{ + "resources": map[string]any{ + "my_stack": map[string]any{ "type": subStackURL, }, }, @@ -261,7 +261,7 @@ resources: th.AssertNoErr(t, te.Parse()) th.AssertDeepEquals(t, expectedParsed, te.Parsed) - expectedFiles := map[string]interface{}{ + expectedFiles := map[string]any{ novaURL: myNovaExpected, subStackURL: mySubstrackExpected, } diff --git a/openstack/orchestration/v1/stacks/testing/fixtures_test.go b/openstack/orchestration/v1/stacks/testing/fixtures_test.go index 605f544399..2244b7d89d 100644 --- a/openstack/orchestration/v1/stacks/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stacks/testing/fixtures_test.go @@ -162,7 +162,7 @@ var GetExpected = &stacks.RetrievedStack{ }, StatusReason: "Stack CREATE completed successfully", Name: "postman_stack", - Outputs: []map[string]interface{}{}, + Outputs: []map[string]any{}, CreationTime: Create_time, Links: []gophercloud.Link{ { @@ -170,8 +170,8 @@ var GetExpected = &stacks.RetrievedStack{ Rel: "self", }, }, - Capabilities: []interface{}{}, - NotificationTopics: []interface{}{}, + Capabilities: []any{}, + NotificationTopics: []any{}, Status: "CREATE_COMPLETE", ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", TemplateDescription: "Simple template to test heat commands", @@ -293,8 +293,8 @@ var PreviewExpected = &stacks.PreviewedStack{ Rel: "self", }, }, - Capabilities: []interface{}{}, - NotificationTopics: []interface{}{}, + Capabilities: []any{}, + NotificationTopics: []any{}, ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", TemplateDescription: "Simple template to test heat commands", } @@ -317,21 +317,21 @@ func HandlePreviewSuccessfully(t *testing.T, output string) { var AbandonExpected = &stacks.AbandonedStack{ Status: "COMPLETE", Name: "postman_stack", - Template: map[string]interface{}{ + Template: map[string]any{ "heat_template_version": "2013-05-23", "description": "Simple template to test heat commands", - "parameters": map[string]interface{}{ - "flavor": map[string]interface{}{ + "parameters": map[string]any{ + "flavor": map[string]any{ "default": "m1.tiny", "type": "string", }, }, - "resources": map[string]interface{}{ - "hello_world": map[string]interface{}{ + "resources": map[string]any{ + "hello_world": map[string]any{ "type": "OS::Nova::Server", - "properties": map[string]interface{}{ + "properties": map[string]any{ "key_name": "heat_key", - "flavor": map[string]interface{}{ + "flavor": map[string]any{ "get_param": "flavor", }, "image": "ad091b52-742f-469e-8f3c-fd81cadf0743", @@ -342,8 +342,8 @@ var AbandonExpected = &stacks.AbandonedStack{ }, Action: "CREATE", ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87", - Resources: map[string]interface{}{ - "hello_world": map[string]interface{}{ + Resources: map[string]any{ + "hello_world": map[string]any{ "status": "COMPLETE", "name": "hello_world", "resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63", @@ -356,13 +356,13 @@ var AbandonExpected = &stacks.AbandonedStack{ }, StackUserProjectID: "897686", ProjectID: "897686", - Environment: map[string]interface{}{ - "encrypted_param_names": make([]map[string]interface{}, 0), - "parameter_defaults": make(map[string]interface{}), - "parameters": make(map[string]interface{}), - "resource_registry": map[string]interface{}{ + Environment: map[string]any{ + "encrypted_param_names": make([]map[string]any, 0), + "parameter_defaults": make(map[string]any), + "parameters": make(map[string]any), + "resource_registry": map[string]any{ "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml", - "resources": make(map[string]interface{}), + "resources": make(map[string]any), }, }, } diff --git a/openstack/orchestration/v1/stacks/testing/requests_test.go b/openstack/orchestration/v1/stacks/testing/requests_test.go index e5b68cae66..224893f5d0 100644 --- a/openstack/orchestration/v1/stacks/testing/requests_test.go +++ b/openstack/orchestration/v1/stacks/testing/requests_test.go @@ -181,7 +181,7 @@ func TestUpdateStackNoTemplate(t *testing.T) { defer th.TeardownHTTP() HandleUpdateSuccessfully(t) - parameters := make(map[string]interface{}) + parameters := make(map[string]any) parameters["flavor"] = "m1.tiny" updateOpts := &stacks.UpdateOpts{ @@ -198,7 +198,7 @@ func TestUpdatePatchStack(t *testing.T) { defer th.TeardownHTTP() HandleUpdatePatchSuccessfully(t) - parameters := make(map[string]interface{}) + parameters := make(map[string]any) parameters["flavor"] = "m1.tiny" updateOpts := &stacks.UpdateOpts{ diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go index bb836c7ac1..56bc48d041 100644 --- a/openstack/orchestration/v1/stacks/utils.go +++ b/openstack/orchestration/v1/stacks/utils.go @@ -29,7 +29,7 @@ type TE struct { // Parsed contains a parsed version of Bin. Since there are 2 different // fields referring to the same value, you must be careful when accessing // this filed. - Parsed map[string]interface{} + Parsed map[string]any // Files contains a mapping between the urls in templates to their contents. Files map[string]string // fileMaps is a map used internally when determining Files. @@ -123,22 +123,22 @@ func (t *TE) Parse() error { // igfunc is a parameter used by GetFileContents and GetRRFileContents to check // for valid URL's. -type igFunc func(string, interface{}) bool +type igFunc func(string, any) bool -// convert map[interface{}]interface{} to map[string]interface{} -func toStringKeys(m interface{}) (map[string]interface{}, error) { +// convert map[any]any to map[string]any +func toStringKeys(m any) (map[string]any, error) { switch m.(type) { - case map[string]interface{}, map[interface{}]interface{}: - typedMap := make(map[string]interface{}) - if _, ok := m.(map[interface{}]interface{}); ok { - for k, v := range m.(map[interface{}]interface{}) { + case map[string]any, map[any]any: + typedMap := make(map[string]any) + if _, ok := m.(map[any]any); ok { + for k, v := range m.(map[any]any) { typedMap[k.(string)] = v } } else { - typedMap = m.(map[string]interface{}) + typedMap = m.(map[string]any) } return typedMap, nil default: - return nil, gophercloud.ErrUnexpectedType{Expected: "map[string]interface{}/map[interface{}]interface{}", Actual: fmt.Sprintf("%v", reflect.TypeOf(m))} + return nil, gophercloud.ErrUnexpectedType{Expected: "map[string]any/map[any]any", Actual: fmt.Sprintf("%v", reflect.TypeOf(m))} } } diff --git a/openstack/orchestration/v1/stacks/utils_test.go b/openstack/orchestration/v1/stacks/utils_test.go index 3f7250b0fc..07fbb90cba 100644 --- a/openstack/orchestration/v1/stacks/utils_test.go +++ b/openstack/orchestration/v1/stacks/utils_test.go @@ -11,14 +11,14 @@ import ( ) func TestToStringKeys(t *testing.T) { - var test1 interface{} = map[interface{}]interface{}{ + var test1 any = map[any]any{ "Adam": "Smith", "Isaac": "Newton", } result1, err := toStringKeys(test1) th.AssertNoErr(t, err) - expected := map[string]interface{}{ + expected := map[string]any{ "Adam": "Smith", "Isaac": "Newton", } diff --git a/openstack/orchestration/v1/stacktemplates/requests.go b/openstack/orchestration/v1/stacktemplates/requests.go index c7b3c2c838..67874f8f7c 100644 --- a/openstack/orchestration/v1/stacktemplates/requests.go +++ b/openstack/orchestration/v1/stacktemplates/requests.go @@ -16,7 +16,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, stackName, stackID s // ValidateOptsBuilder describes struct types that can be accepted by the Validate call. // The ValidateOpts struct in this package does. type ValidateOptsBuilder interface { - ToStackTemplateValidateMap() (map[string]interface{}, error) + ToStackTemplateValidateMap() (map[string]any, error) } // ValidateOpts specifies the template validation parameters. @@ -26,7 +26,7 @@ type ValidateOpts struct { } // ToStackTemplateValidateMap assembles a request body based on the contents of a ValidateOpts. -func (opts ValidateOpts) ToStackTemplateValidateMap() (map[string]interface{}, error) { +func (opts ValidateOpts) ToStackTemplateValidateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/orchestration/v1/stacktemplates/results.go b/openstack/orchestration/v1/stacktemplates/results.go index 56b24ff196..e2f27068d4 100644 --- a/openstack/orchestration/v1/stacktemplates/results.go +++ b/openstack/orchestration/v1/stacktemplates/results.go @@ -25,9 +25,9 @@ func (r GetResult) Extract() ([]byte, error) { // ValidatedTemplate represents the parsed object returned from a Validate request. type ValidatedTemplate struct { - Description string `json:"Description"` - Parameters map[string]interface{} `json:"Parameters"` - ParameterGroups map[string]interface{} `json:"ParameterGroups"` + Description string `json:"Description"` + Parameters map[string]any `json:"Parameters"` + ParameterGroups map[string]any `json:"ParameterGroups"` } // ValidateResult represents the result of a Validate operation. diff --git a/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go b/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go index cfef51adab..5b9c9ad345 100644 --- a/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go @@ -55,8 +55,8 @@ func HandleGetSuccessfully(t *testing.T, output string) { // ValidateExpected represents the expected object from a Validate request. var ValidateExpected = &stacktemplates.ValidatedTemplate{ Description: "Simple template to test heat commands", - Parameters: map[string]interface{}{ - "flavor": map[string]interface{}{ + Parameters: map[string]any{ + "flavor": map[string]any{ "Default": "m1.tiny", "Type": "String", "NoEcho": "false", diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index e9accbfbe2..5c5075e445 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -64,7 +64,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToResourceProviderCreateMap() (map[string]interface{}, error) + ToResourceProviderCreateMap() (map[string]any, error) } // CreateOpts represents options used to create a resource provider. @@ -77,7 +77,7 @@ type CreateOpts struct { } // ToResourceProviderCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToResourceProviderCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToResourceProviderCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -118,7 +118,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, resourceProviderID s // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToResourceProviderUpdateMap() (map[string]interface{}, error) + ToResourceProviderUpdateMap() (map[string]any, error) } // UpdateOpts represents options used to update a resource provider. @@ -133,7 +133,7 @@ type UpdateOpts struct { } // ToResourceProviderUpdateMap constructs a request body from UpdateOpts. -func (opts UpdateOpts) ToResourceProviderUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToResourceProviderUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } diff --git a/openstack/sharedfilesystems/v2/replicas/requests.go b/openstack/sharedfilesystems/v2/replicas/requests.go index 021c1550a7..415e67b37d 100644 --- a/openstack/sharedfilesystems/v2/replicas/requests.go +++ b/openstack/sharedfilesystems/v2/replicas/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToReplicaCreateMap() (map[string]interface{}, error) + ToReplicaCreateMap() (map[string]any, error) } // CreateOpts contains the options for create a Share Replica. This object is @@ -32,7 +32,7 @@ type CreateOpts struct { // ToReplicaCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToReplicaCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToReplicaCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "share_replica") } @@ -146,7 +146,7 @@ func GetExportLocation(ctx context.Context, client *gophercloud.ServiceClient, r // PromoteOptsBuilder allows extensions to add additional parameters to the // Promote request. type PromoteOptsBuilder interface { - ToReplicaPromoteMap() (map[string]interface{}, error) + ToReplicaPromoteMap() (map[string]any, error) } // PromoteOpts contains options for promoteing a Replica to active replica state. @@ -159,7 +159,7 @@ type PromoteOpts struct { // ToReplicaPromoteMap assembles a request body based on the contents of a // PromoteOpts. -func (opts PromoteOpts) ToReplicaPromoteMap() (map[string]interface{}, error) { +func (opts PromoteOpts) ToReplicaPromoteMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "promote") } @@ -182,7 +182,7 @@ func Promote(ctx context.Context, client *gophercloud.ServiceClient, id string, // Resync a replica with its active mirror. ResyncResult contains only the error. // To extract it, call the ExtractErr method on the ResyncResult. func Resync(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ResyncResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"resync": nil}, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"resync": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -192,7 +192,7 @@ func Resync(ctx context.Context, client *gophercloud.ServiceClient, id string) ( // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { - ToReplicaResetStatusMap() (map[string]interface{}, error) + ToReplicaResetStatusMap() (map[string]any, error) } // ResetStatusOpts contain options for updating a Share Replica status. This object is passed @@ -205,7 +205,7 @@ type ResetStatusOpts struct { // ToReplicaResetStatusMap assembles a request body based on the contents of an // ResetStatusOpts. -func (opts ResetStatusOpts) ToReplicaResetStatusMap() (map[string]interface{}, error) { +func (opts ResetStatusOpts) ToReplicaResetStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "reset_status") } @@ -228,7 +228,7 @@ func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id stri // ResetStateOptsBuilder allows extensions to add additional parameters to the // ResetState request. type ResetStateOptsBuilder interface { - ToReplicaResetStateMap() (map[string]interface{}, error) + ToReplicaResetStateMap() (map[string]any, error) } // ResetStateOpts contain options for updating a Share Replica state. This object is passed @@ -241,7 +241,7 @@ type ResetStateOpts struct { // ToReplicaResetStateMap assembles a request body based on the contents of an // ResetStateOpts. -func (opts ResetStateOpts) ToReplicaResetStateMap() (map[string]interface{}, error) { +func (opts ResetStateOpts) ToReplicaResetStateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "reset_replica_state") } @@ -265,7 +265,7 @@ func ResetState(ctx context.Context, client *gophercloud.ServiceClient, id strin // contains only the error. To extract it, call the ExtractErr method on the // ForceDeleteResult. Administrator only. func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - resp, err := client.Post(ctx, actionURL(client, id), map[string]interface{}{"force_delete": nil}, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(ctx, actionURL(client, id), map[string]any{"force_delete": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/sharedfilesystems/v2/replicas/results.go b/openstack/sharedfilesystems/v2/replicas/results.go index 764139f0c6..7b75117747 100644 --- a/openstack/sharedfilesystems/v2/replicas/results.go +++ b/openstack/sharedfilesystems/v2/replicas/results.go @@ -159,7 +159,7 @@ func ExtractReplicas(r pagination.Page) ([]Replica, error) { // ExtractReplicasInto similar to ExtractReplicas but operates on a `list` of // replicas. -func ExtractReplicasInto(r pagination.Page, v interface{}) error { +func ExtractReplicasInto(r pagination.Page, v any) error { return r.(ReplicaPage).Result.ExtractIntoSlicePtr(v, "share_replicas") } diff --git a/openstack/sharedfilesystems/v2/schedulerstats/results.go b/openstack/sharedfilesystems/v2/schedulerstats/results.go index 8aa84baa3c..cf1b952f63 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/results.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/results.go @@ -60,9 +60,9 @@ 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"` + AllocatedCapacityGB any `json:"allocated_capacity_gb"` + FreeCapacityGB any `json:"free_capacity_gb"` + TotalCapacityGB any `json:"total_capacity_gb"` } err := json.Unmarshal(b, &s) if err != nil { @@ -72,7 +72,7 @@ func (r *Capabilities) UnmarshalJSON(b []byte) error { // Generic function to parse a capacity value which may be a numeric // value, "unknown", or "infinite" - parseCapacity := func(capacity interface{}) float64 { + parseCapacity := func(capacity any) float64 { if capacity != nil { switch c := capacity.(type) { case float64: diff --git a/openstack/sharedfilesystems/v2/securityservices/requests.go b/openstack/sharedfilesystems/v2/securityservices/requests.go index b27d65e162..ff4b467392 100644 --- a/openstack/sharedfilesystems/v2/securityservices/requests.go +++ b/openstack/sharedfilesystems/v2/securityservices/requests.go @@ -19,7 +19,7 @@ const ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSecurityServiceCreateMap() (map[string]interface{}, error) + ToSecurityServiceCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a SecurityService. This object is @@ -48,7 +48,7 @@ type CreateOpts struct { // ToSecurityServicesCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToSecurityServiceCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSecurityServiceCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_service") } @@ -139,7 +139,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToSecurityServiceUpdateMap() (map[string]interface{}, error) + ToSecurityServiceUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing SecurityService. This object is passed @@ -168,7 +168,7 @@ type UpdateOpts struct { // ToSecurityServiceUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToSecurityServiceUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSecurityServiceUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "security_service") } diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/results.go b/openstack/sharedfilesystems/v2/shareaccessrules/results.go index fa63c05e68..e19f077233 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/results.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/results.go @@ -28,7 +28,7 @@ type ShareAccess struct { // The access rule ID. ID string `json:"id"` // Access rule metadata. - Metadata map[string]interface{} `json:"metadata"` + Metadata map[string]any `json:"metadata"` } func (r *ShareAccess) UnmarshalJSON(b []byte) error { diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go index 9c520348f0..4f86a642d4 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go @@ -35,7 +35,7 @@ func TestGet(t *testing.T) { State: "error", AccessLevel: "rw", ID: "507bf114-36f2-4f56-8cf4-857985ca87c1", - Metadata: map[string]interface{}{ + Metadata: map[string]any{ "key1": "value1", "key2": "value2", }, diff --git a/openstack/sharedfilesystems/v2/sharenetworks/requests.go b/openstack/sharedfilesystems/v2/sharenetworks/requests.go index 198925eab2..6df73b5eed 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/requests.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToShareNetworkCreateMap() (map[string]interface{}, error) + ToShareNetworkCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a ShareNetwork. This object is @@ -31,7 +31,7 @@ type CreateOpts struct { // ToShareNetworkCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToShareNetworkCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToShareNetworkCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "share_network") } @@ -132,7 +132,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToShareNetworkUpdateMap() (map[string]interface{}, error) + ToShareNetworkUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing ShareNetwork. This object is passed @@ -153,7 +153,7 @@ type UpdateOpts struct { // ToShareNetworkUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToShareNetworkUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToShareNetworkUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "share_network") } @@ -175,7 +175,7 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o // AddSecurityServiceOptsBuilder allows extensions to add additional parameters to the // AddSecurityService request. type AddSecurityServiceOptsBuilder interface { - ToShareNetworkAddSecurityServiceMap() (map[string]interface{}, error) + ToShareNetworkAddSecurityServiceMap() (map[string]any, error) } // AddSecurityServiceOpts contain options for adding a security service to an @@ -187,7 +187,7 @@ type AddSecurityServiceOpts struct { // ToShareNetworkAddSecurityServiceMap assembles a request body based on the contents of an // AddSecurityServiceOpts. -func (opts AddSecurityServiceOpts) ToShareNetworkAddSecurityServiceMap() (map[string]interface{}, error) { +func (opts AddSecurityServiceOpts) ToShareNetworkAddSecurityServiceMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "add_security_service") } @@ -209,7 +209,7 @@ func AddSecurityService(ctx context.Context, client *gophercloud.ServiceClient, // RemoveSecurityServiceOptsBuilder allows extensions to add additional parameters to the // RemoveSecurityService request. type RemoveSecurityServiceOptsBuilder interface { - ToShareNetworkRemoveSecurityServiceMap() (map[string]interface{}, error) + ToShareNetworkRemoveSecurityServiceMap() (map[string]any, error) } // RemoveSecurityServiceOpts contain options for removing a security service from an @@ -221,7 +221,7 @@ type RemoveSecurityServiceOpts struct { // ToShareNetworkRemoveSecurityServiceMap assembles a request body based on the contents of an // RemoveSecurityServiceOpts. -func (opts RemoveSecurityServiceOpts) ToShareNetworkRemoveSecurityServiceMap() (map[string]interface{}, error) { +func (opts RemoveSecurityServiceOpts) ToShareNetworkRemoveSecurityServiceMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "remove_security_service") } diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index 7562b2de19..d228f6a36c 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToShareCreateMap() (map[string]interface{}, error) + ToShareCreateMap() (map[string]any, error) } // CreateOpts contains the options for create a Share. This object is @@ -52,7 +52,7 @@ type CreateOpts struct { // ToShareCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToShareCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToShareCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "share") } @@ -198,7 +198,7 @@ func GetExportLocation(ctx context.Context, client *gophercloud.ServiceClient, s // GrantAccessOptsBuilder allows extensions to add additional parameters to the // GrantAccess request. type GrantAccessOptsBuilder interface { - ToGrantAccessMap() (map[string]interface{}, error) + ToGrantAccessMap() (map[string]any, error) } // GrantAccessOpts contains the options for creation of an GrantAccess request. @@ -215,7 +215,7 @@ type GrantAccessOpts struct { // ToGrantAccessMap assembles a request body based on the contents of a // GrantAccessOpts. -func (opts GrantAccessOpts) ToGrantAccessMap() (map[string]interface{}, error) { +func (opts GrantAccessOpts) ToGrantAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "allow_access") } @@ -238,7 +238,7 @@ func GrantAccess(ctx context.Context, client *gophercloud.ServiceClient, id stri // RevokeAccessOptsBuilder allows extensions to add additional parameters to the // RevokeAccess request. type RevokeAccessOptsBuilder interface { - ToRevokeAccessMap() (map[string]interface{}, error) + ToRevokeAccessMap() (map[string]any, error) } // RevokeAccessOpts contains the options for creation of a RevokeAccess request. @@ -250,7 +250,7 @@ type RevokeAccessOpts struct { // ToRevokeAccessMap assembles a request body based on the contents of a // RevokeAccessOpts. -func (opts RevokeAccessOpts) ToRevokeAccessMap() (map[string]interface{}, error) { +func (opts RevokeAccessOpts) ToRevokeAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "deny_access") } @@ -276,7 +276,7 @@ func RevokeAccess(ctx context.Context, client *gophercloud.ServiceClient, id str // the AccessRight slice from the response, call the Extract method on the ListAccessRightsResult. // Client must have Microversion set; minimum supported microversion for ListAccessRights is 2.7. func ListAccessRights(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ListAccessRightsResult) { - requestBody := map[string]interface{}{"access_list": nil} + requestBody := map[string]any{"access_list": nil} resp, err := client.Post(ctx, listAccessRightsURL(client, id), requestBody, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) @@ -287,7 +287,7 @@ func ListAccessRights(ctx context.Context, client *gophercloud.ServiceClient, id // ExtendOptsBuilder allows extensions to add additional parameters to the // Extend request. type ExtendOptsBuilder interface { - ToShareExtendMap() (map[string]interface{}, error) + ToShareExtendMap() (map[string]any, error) } // ExtendOpts contains options for extending a Share. @@ -300,7 +300,7 @@ type ExtendOpts struct { // ToShareExtendMap assembles a request body based on the contents of a // ExtendOpts. -func (opts ExtendOpts) ToShareExtendMap() (map[string]interface{}, error) { +func (opts ExtendOpts) ToShareExtendMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "extend") } @@ -324,7 +324,7 @@ func Extend(ctx context.Context, client *gophercloud.ServiceClient, id string, o // ShrinkOptsBuilder allows extensions to add additional parameters to the // Shrink request. type ShrinkOptsBuilder interface { - ToShareShrinkMap() (map[string]interface{}, error) + ToShareShrinkMap() (map[string]any, error) } // ShrinkOpts contains options for shrinking a Share. @@ -337,7 +337,7 @@ type ShrinkOpts struct { // ToShareShrinkMap assembles a request body based on the contents of a // ShrinkOpts. -func (opts ShrinkOpts) ToShareShrinkMap() (map[string]interface{}, error) { +func (opts ShrinkOpts) ToShareShrinkMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "shrink") } @@ -361,7 +361,7 @@ func Shrink(ctx context.Context, client *gophercloud.ServiceClient, id string, o // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToShareUpdateMap() (map[string]interface{}, error) + ToShareUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Share. This object is passed @@ -378,7 +378,7 @@ type UpdateOpts struct { // ToShareUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToShareUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToShareUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "share") } @@ -422,14 +422,14 @@ type SetMetadataOpts struct { // ToSetMetadataMap assembles a request body based on the contents of an // SetMetadataOpts. -func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]interface{}, error) { +func (opts SetMetadataOpts) ToSetMetadataMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } // SetMetadataOptsBuilder allows extensions to add additional parameters to the // SetMetadata request. type SetMetadataOptsBuilder interface { - ToSetMetadataMap() (map[string]interface{}, error) + ToSetMetadataMap() (map[string]any, error) } // SetMetadata sets metadata of the specified share. @@ -459,14 +459,14 @@ type UpdateMetadataOpts struct { // ToUpdateMetadataMap assembles a request body based on the contents of an // UpdateMetadataOpts. -func (opts UpdateMetadataOpts) ToUpdateMetadataMap() (map[string]interface{}, error) { +func (opts UpdateMetadataOpts) ToUpdateMetadataMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } // UpdateMetadataOptsBuilder allows extensions to add additional parameters to the // UpdateMetadata request. type UpdateMetadataOptsBuilder interface { - ToUpdateMetadataMap() (map[string]interface{}, error) + ToUpdateMetadataMap() (map[string]any, error) } // UpdateMetadata updates metadata of the specified share. @@ -499,7 +499,7 @@ func DeleteMetadatum(ctx context.Context, client *gophercloud.ServiceClient, id, // RevertOptsBuilder allows extensions to add additional parameters to the // Revert request. type RevertOptsBuilder interface { - ToShareRevertMap() (map[string]interface{}, error) + ToShareRevertMap() (map[string]any, error) } // RevertOpts contains options for reverting a Share to a snapshot. @@ -513,7 +513,7 @@ type RevertOpts struct { // ToShareRevertMap assembles a request body based on the contents of a // RevertOpts. -func (opts RevertOpts) ToShareRevertMap() (map[string]interface{}, error) { +func (opts RevertOpts) ToShareRevertMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "revert") } @@ -537,7 +537,7 @@ func Revert(ctx context.Context, client *gophercloud.ServiceClient, id string, o // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { - ToShareResetStatusMap() (map[string]interface{}, error) + ToShareResetStatusMap() (map[string]any, error) } // ResetStatusOpts contains options for resetting a Share status. @@ -550,7 +550,7 @@ type ResetStatusOpts struct { // ToShareResetStatusMap assembles a request body based on the contents of a // ResetStatusOpts. -func (opts ResetStatusOpts) ToShareResetStatusMap() (map[string]interface{}, error) { +func (opts ResetStatusOpts) ToShareResetStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "reset_status") } @@ -575,7 +575,7 @@ func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id stri // To extract it, call the ExtractErr method on the ForceDeleteResult. // Client must have Microversion set; minimum supported microversion for ForceDelete is 2.7. func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - b := map[string]interface{}{ + b := map[string]any{ "force_delete": nil, } resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ @@ -590,7 +590,7 @@ func ForceDelete(ctx context.Context, client *gophercloud.ServiceClient, id stri // To extract it, call the ExtractErr method on the UnmanageResult. // Client must have Microversion set; minimum supported microversion for Unmanage is 2.7. func Unmanage(ctx context.Context, client *gophercloud.ServiceClient, id string) (r UnmanageResult) { - b := map[string]interface{}{ + b := map[string]any{ "unmanage": nil, } resp, err := client.Post(ctx, unmanageURL(client, id), b, nil, &gophercloud.RequestOpts{ diff --git a/openstack/sharedfilesystems/v2/sharetransfers/requests.go b/openstack/sharedfilesystems/v2/sharetransfers/requests.go index 01fa577708..642d2a36d2 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/requests.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToTransferCreateMap() (map[string]interface{}, error) + ToTransferCreateMap() (map[string]any, error) } // CreateOpts contains options for a Share transfer. @@ -24,7 +24,7 @@ type CreateOpts struct { // ToCreateMap assembles a request body based on the contents of a // TransferOpts. -func (opts CreateOpts) ToTransferCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToTransferCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "transfer") } @@ -53,7 +53,7 @@ type AcceptOpts struct { // ToAcceptMap assembles a request body based on the contents of a // AcceptOpts. -func (opts AcceptOpts) ToAcceptMap() (map[string]interface{}, error) { +func (opts AcceptOpts) ToAcceptMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "accept") } diff --git a/openstack/sharedfilesystems/v2/sharetransfers/results.go b/openstack/sharedfilesystems/v2/sharetransfers/results.go index 02b8ec71b0..264951383d 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/results.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/results.go @@ -61,7 +61,7 @@ func (r commonResult) Extract() (*Transfer, error) { } // ExtractInto converts our response data into a transfer struct. -func (r commonResult) ExtractInto(v interface{}) error { +func (r commonResult) ExtractInto(v any) error { return r.Result.ExtractIntoStructPtr(v, "transfer") } @@ -93,7 +93,7 @@ func ExtractTransfers(r pagination.Page) ([]Transfer, error) { } // ExtractTransfersInto similar to ExtractInto but operates on a `list` of transfers -func ExtractTransfersInto(r pagination.Page, v interface{}) error { +func ExtractTransfersInto(r pagination.Page, v any) error { return r.(TransferPage).Result.ExtractIntoSlicePtr(v, "transfers") } diff --git a/openstack/sharedfilesystems/v2/sharetypes/requests.go b/openstack/sharedfilesystems/v2/sharetypes/requests.go index ea6aab3bd5..66428ad0c9 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/requests.go +++ b/openstack/sharedfilesystems/v2/sharetypes/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToShareTypeCreateMap() (map[string]interface{}, error) + ToShareTypeCreateMap() (map[string]any, error) } // CreateOpts contains options for creating a ShareType. This object is @@ -35,7 +35,7 @@ type ExtraSpecsOpts struct { // ToShareTypeCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToShareTypeCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToShareTypeCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "share_type") } @@ -114,17 +114,17 @@ func GetExtraSpecs(ctx context.Context, client *gophercloud.ServiceClient, id st // SetExtraSpecsOptsBuilder allows extensions to add additional parameters to the // SetExtraSpecs request. type SetExtraSpecsOptsBuilder interface { - ToShareTypeSetExtraSpecsMap() (map[string]interface{}, error) + ToShareTypeSetExtraSpecsMap() (map[string]any, error) } type SetExtraSpecsOpts struct { // A list of all extra specifications to be added to a ShareType - ExtraSpecs map[string]interface{} `json:"extra_specs" required:"true"` + ExtraSpecs map[string]any `json:"extra_specs" required:"true"` } // ToShareTypeSetExtraSpecsMap assembles a request body based on the contents of a // SetExtraSpecsOpts. -func (opts SetExtraSpecsOpts) ToShareTypeSetExtraSpecsMap() (map[string]interface{}, error) { +func (opts SetExtraSpecsOpts) ToShareTypeSetExtraSpecsMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -162,7 +162,7 @@ func ShowAccess(ctx context.Context, client *gophercloud.ServiceClient, id strin // AddAccessOptsBuilder allows extensions to add additional parameters to the // AddAccess type AddAccessOptsBuilder interface { - ToAddAccessMap() (map[string]interface{}, error) + ToAddAccessMap() (map[string]any, error) } type AccessOpts struct { @@ -172,7 +172,7 @@ type AccessOpts struct { // ToAddAccessMap assembles a request body based on the contents of a // AccessOpts. -func (opts AccessOpts) ToAddAccessMap() (map[string]interface{}, error) { +func (opts AccessOpts) ToAddAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "addProjectAccess") } @@ -195,12 +195,12 @@ func AddAccess(ctx context.Context, client *gophercloud.ServiceClient, id string // RemoveAccessOptsBuilder allows extensions to add additional parameters to the // RemoveAccess type RemoveAccessOptsBuilder interface { - ToRemoveAccessMap() (map[string]interface{}, error) + ToRemoveAccessMap() (map[string]any, error) } // ToRemoveAccessMap assembles a request body based on the contents of a // AccessOpts. -func (opts AccessOpts) ToRemoveAccessMap() (map[string]interface{}, error) { +func (opts AccessOpts) ToRemoveAccessMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "removeProjectAccess") } diff --git a/openstack/sharedfilesystems/v2/sharetypes/results.go b/openstack/sharedfilesystems/v2/sharetypes/results.go index 0ac2cf0bbd..9fb54fa8d9 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/results.go +++ b/openstack/sharedfilesystems/v2/sharetypes/results.go @@ -15,9 +15,9 @@ type ShareType struct { // Indicates whether a share type is publicly accessible IsPublic bool `json:"os-share-type-access:is_public"` // The required extra specifications for the share type - RequiredExtraSpecs map[string]interface{} `json:"required_extra_specs"` + RequiredExtraSpecs map[string]any `json:"required_extra_specs"` // The extra specifications for the share type - ExtraSpecs map[string]interface{} `json:"extra_specs"` + ExtraSpecs map[string]any `json:"extra_specs"` } type commonResult struct { @@ -75,7 +75,7 @@ type GetDefaultResult struct { // ExtraSpecs contains all the information associated with extra specifications // for an Openstack ShareType. -type ExtraSpecs map[string]interface{} +type ExtraSpecs map[string]any type extraSpecsResult struct { gophercloud.Result diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go index 99d9c2af29..a88145561f 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/sharetypes/testing/requests_test.go @@ -87,15 +87,15 @@ func TestList(t *testing.T) { ID: "be27425c-f807-4500-a056-d00721db45cf", Name: "default", IsPublic: true, - ExtraSpecs: map[string]interface{}{"snapshot_support": "True", "driver_handles_share_servers": "True"}, - RequiredExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "True"}, + ExtraSpecs: map[string]any{"snapshot_support": "True", "driver_handles_share_servers": "True"}, + RequiredExtraSpecs: map[string]any{"driver_handles_share_servers": "True"}, }, { ID: "f015bebe-c38b-4c49-8832-00143b10253b", Name: "d", IsPublic: true, - ExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "false", "snapshot_support": "True"}, - RequiredExtraSpecs: map[string]interface{}{"driver_handles_share_servers": "false"}, + ExtraSpecs: map[string]any{"driver_handles_share_servers": "false", "snapshot_support": "True"}, + RequiredExtraSpecs: map[string]any{"driver_handles_share_servers": "false"}, }, } @@ -112,8 +112,8 @@ func TestGetDefault(t *testing.T) { expected := sharetypes.ShareType{ ID: "be27425c-f807-4500-a056-d00721db45cf", Name: "default", - ExtraSpecs: map[string]interface{}{"snapshot_support": "True", "driver_handles_share_servers": "True"}, - RequiredExtraSpecs: map[string]interface{}(nil), + ExtraSpecs: map[string]any{"snapshot_support": "True", "driver_handles_share_servers": "True"}, + RequiredExtraSpecs: map[string]any(nil), } actual, err := sharetypes.GetDefault(context.TODO(), client.ServiceClient()).Extract() @@ -144,7 +144,7 @@ func TestSetExtraSpecs(t *testing.T) { MockSetExtraSpecsResponse(t) options := &sharetypes.SetExtraSpecsOpts{ - ExtraSpecs: map[string]interface{}{"my_key": "my_value"}, + ExtraSpecs: map[string]any{"my_key": "my_value"}, } es, err := sharetypes.SetExtraSpecs(context.TODO(), client.ServiceClient(), "shareTypeID", options).Extract() diff --git a/openstack/sharedfilesystems/v2/snapshots/requests.go b/openstack/sharedfilesystems/v2/snapshots/requests.go index 5e11d2cbc1..db043f1131 100644 --- a/openstack/sharedfilesystems/v2/snapshots/requests.go +++ b/openstack/sharedfilesystems/v2/snapshots/requests.go @@ -10,7 +10,7 @@ import ( // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { - ToSnapshotCreateMap() (map[string]interface{}, error) + ToSnapshotCreateMap() (map[string]any, error) } // CreateOpts contains the options for create a Snapshot. This object is @@ -34,7 +34,7 @@ type CreateOpts struct { // ToSnapshotCreateMap assembles a request body based on the contents of a // CreateOpts. -func (opts CreateOpts) ToSnapshotCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToSnapshotCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } @@ -132,7 +132,7 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r G // UpdateOptsBuilder allows extensions to add additional parameters to the // Update request. type UpdateOptsBuilder interface { - ToSnapshotUpdateMap() (map[string]interface{}, error) + ToSnapshotUpdateMap() (map[string]any, error) } // UpdateOpts contain options for updating an existing Snapshot. This object is passed @@ -147,7 +147,7 @@ type UpdateOpts struct { // ToSnapshotUpdateMap assembles a request body based on the contents of an // UpdateOpts. -func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]interface{}, error) { +func (opts UpdateOpts) ToSnapshotUpdateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "snapshot") } @@ -169,7 +169,7 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, o // ResetStatusOptsBuilder allows extensions to add additional parameters to the // ResetStatus request. type ResetStatusOptsBuilder interface { - ToSnapshotResetStatusMap() (map[string]interface{}, error) + ToSnapshotResetStatusMap() (map[string]any, error) } // ResetStatusOpts contains options for resetting a Snapshot status. @@ -184,7 +184,7 @@ type ResetStatusOpts struct { // ToSnapshotResetStatusMap assembles a request body based on the contents of a // ResetStatusOpts. -func (opts ResetStatusOpts) ToSnapshotResetStatusMap() (map[string]interface{}, error) { +func (opts ResetStatusOpts) ToSnapshotResetStatusMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "reset_status") } @@ -207,7 +207,7 @@ func ResetStatus(ctx context.Context, client *gophercloud.ServiceClient, id stri // 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(ctx context.Context, client *gophercloud.ServiceClient, id string) (r ForceDeleteResult) { - b := map[string]interface{}{ + b := map[string]any{ "force_delete": nil, } resp, err := client.Post(ctx, forceDeleteURL(client, id), b, nil, &gophercloud.RequestOpts{ diff --git a/openstack/workflow/v2/crontriggers/doc.go b/openstack/workflow/v2/crontriggers/doc.go index 7083a2c25e..882d028f8b 100644 --- a/openstack/workflow/v2/crontriggers/doc.go +++ b/openstack/workflow/v2/crontriggers/doc.go @@ -41,10 +41,10 @@ Create a cron trigger. This example will start the workflow "echo" each day at 8 Pattern: "0 8 * * *", WorkflowName: "echo", RemainingExecutions: 10, - WorkflowParams: map[string]interface{}{ + WorkflowParams: map[string]any{ "msg": "hello", }, - WorkflowInput: map[string]interface{}{ + WorkflowInput: map[string]any{ "msg": "world", }, } diff --git a/openstack/workflow/v2/crontriggers/requests.go b/openstack/workflow/v2/crontriggers/requests.go index a6fc577aa4..dd9a312324 100644 --- a/openstack/workflow/v2/crontriggers/requests.go +++ b/openstack/workflow/v2/crontriggers/requests.go @@ -14,7 +14,7 @@ import ( // CreateOptsBuilder allows extension to add additional parameters to the Create request. type CreateOptsBuilder interface { - ToCronTriggerCreateMap() (map[string]interface{}, error) + ToCronTriggerCreateMap() (map[string]any, error) } // CreateOpts specifies parameters used to create a cron trigger. @@ -36,17 +36,17 @@ type CreateOpts struct { WorkflowName string `json:"workflow_name,omitempty" or:"WorkflowID"` // WorkflowParams defines workflow type specific parameters. - WorkflowParams map[string]interface{} `json:"workflow_params,omitempty"` + WorkflowParams map[string]any `json:"workflow_params,omitempty"` // WorkflowInput defines workflow input values. - WorkflowInput map[string]interface{} `json:"workflow_input,omitempty"` + WorkflowInput map[string]any `json:"workflow_input,omitempty"` // FirstExecutionTime defines the first execution time of the trigger. FirstExecutionTime *time.Time `json:"-"` } // ToCronTriggerCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToCronTriggerCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToCronTriggerCreateMap() (map[string]any, error) { b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { return nil, err @@ -99,9 +99,9 @@ type ListOpts struct { // WorkflowID allows to filter by workflow id. WorkflowID string `q:"workflow_id"` // WorkflowInput allows to filter by specific workflow inputs. - WorkflowInput map[string]interface{} `q:"-"` + WorkflowInput map[string]any `q:"-"` // WorkflowParams allows to filter by specific workflow parameters. - WorkflowParams map[string]interface{} `q:"-"` + WorkflowParams map[string]any `q:"-"` // Scope filters by the trigger's scope. // Values can be "private" or "public". Scope string `q:"scope"` @@ -212,7 +212,7 @@ func (opts ListOpts) ToCronTriggerListQuery() (string, error) { } params := q.Query() - for queryParam, value := range map[string]map[string]interface{}{"workflow_params": opts.WorkflowParams, "workflow_input": opts.WorkflowInput} { + for queryParam, value := range map[string]map[string]any{"workflow_params": opts.WorkflowParams, "workflow_input": opts.WorkflowInput} { if value != nil { b, err := json.Marshal(value) if err != nil { diff --git a/openstack/workflow/v2/crontriggers/results.go b/openstack/workflow/v2/crontriggers/results.go index 41476c5ddd..16ec7d1c9f 100644 --- a/openstack/workflow/v2/crontriggers/results.go +++ b/openstack/workflow/v2/crontriggers/results.go @@ -63,10 +63,10 @@ type CronTrigger struct { WorkflowName string `json:"workflow_name"` // WorkflowInput contains the workflow input values. - WorkflowInput map[string]interface{} `json:"-"` + WorkflowInput map[string]any `json:"-"` // WorkflowParams contains workflow type specific parameters. - WorkflowParams map[string]interface{} `json:"-"` + WorkflowParams map[string]any `json:"-"` // CreatedAt contains the cron trigger creation date. CreatedAt time.Time `json:"-"` diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index 858b30f88b..fd3bac58e0 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -50,10 +50,10 @@ func TestCreateCronTrigger(t *testing.T) { WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", Name: "trigger", FirstExecutionTime: &firstExecution, - WorkflowParams: map[string]interface{}{ + WorkflowParams: map[string]any{ "msg": "world", }, - WorkflowInput: map[string]interface{}{ + WorkflowInput: map[string]any{ "msg": "hello", }, } @@ -72,10 +72,10 @@ func TestCreateCronTrigger(t *testing.T) { Scope: "private", WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", WorkflowName: "workflow_echo", - WorkflowParams: map[string]interface{}{ + WorkflowParams: map[string]any{ "msg": "world", }, - WorkflowInput: map[string]interface{}{ + WorkflowInput: map[string]any{ "msg": "hello", }, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), @@ -145,10 +145,10 @@ func TestGetCronTrigger(t *testing.T) { Scope: "private", WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", WorkflowName: "workflow_echo", - WorkflowParams: map[string]interface{}{ + WorkflowParams: map[string]any{ "msg": "world", }, - WorkflowInput: map[string]interface{}{ + WorkflowInput: map[string]any{ "msg": "hello", }, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), @@ -221,10 +221,10 @@ func TestListCronTriggers(t *testing.T) { Scope: "private", WorkflowID: "604a3a1e-94e3-4066-a34a-aa56873ef236", WorkflowName: "workflow_echo", - WorkflowParams: map[string]interface{}{ + WorkflowParams: map[string]any{ "msg": "world", }, - WorkflowInput: map[string]interface{}{ + WorkflowInput: map[string]any{ "msg": "hello", }, CreatedAt: time.Date(2018, time.September, 12, 15, 48, 18, 0, time.UTC), @@ -249,7 +249,7 @@ func TestListCronTriggers(t *testing.T) { func TestToExecutionListQuery(t *testing.T) { for expected, opts := range map[string]*crontriggers.ListOpts{ newValue("workflow_input", `{"msg":"Hello"}`): { - WorkflowInput: map[string]interface{}{ + WorkflowInput: map[string]any{ "msg": "Hello", }, }, diff --git a/openstack/workflow/v2/executions/doc.go b/openstack/workflow/v2/executions/doc.go index aff179c855..070ef138cc 100644 --- a/openstack/workflow/v2/executions/doc.go +++ b/openstack/workflow/v2/executions/doc.go @@ -40,7 +40,7 @@ Create an execution createOpts := &executions.CreateOpts{ WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", - Input: map[string]interface{}{ + Input: map[string]any{ "msg": "Hello", }, Description: "this is a description", diff --git a/openstack/workflow/v2/executions/requests.go b/openstack/workflow/v2/executions/requests.go index ff18f9cd3e..e06f7475bf 100644 --- a/openstack/workflow/v2/executions/requests.go +++ b/openstack/workflow/v2/executions/requests.go @@ -14,7 +14,7 @@ import ( // CreateOptsBuilder allows extension to add additional parameters to the Create request. type CreateOptsBuilder interface { - ToExecutionCreateMap() (map[string]interface{}, error) + ToExecutionCreateMap() (map[string]any, error) } // CreateOpts specifies parameters used to create an execution. @@ -35,17 +35,17 @@ type CreateOpts struct { WorkflowNamespace string `json:"workflow_namespace,omitempty"` // Input is a JSON structure containing workflow input values, serialized as string. - Input map[string]interface{} `json:"input,omitempty"` + Input map[string]any `json:"input,omitempty"` // Params define workflow type specific parameters. - Params map[string]interface{} `json:"params,omitempty"` + Params map[string]any `json:"params,omitempty"` // Description is the description of the workflow execution. Description string `json:"description,omitempty"` } // ToExecutionCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToExecutionCreateMap() (map[string]interface{}, error) { +func (opts CreateOpts) ToExecutionCreateMap() (map[string]any, error) { return gophercloud.BuildRequestBody(opts, "") } @@ -91,7 +91,7 @@ type ListOpts struct { // Description allows to filter by execution description. Description *ListFilter `q:"-"` // Params allows to filter by specific parameters. - Params map[string]interface{} `q:"-"` + Params map[string]any `q:"-"` // TaskExecutionID allows to filter with a specific task execution id. TaskExecutionID string `q:"task_execution_id"` // RootExecutionID allows to filter with a specific root execution id. @@ -102,9 +102,9 @@ type ListOpts struct { // StateInfo allows to filter by state info. StateInfo *ListFilter `q:"-"` // Input allows to filter by specific input. - Input map[string]interface{} `q:"-"` + Input map[string]any `q:"-"` // Output allows to filter by specific output. - Output map[string]interface{} `q:"-"` + Output map[string]any `q:"-"` // CreatedAt allows to filter by execution creation date. CreatedAt *ListDateFilter `q:"-"` // UpdatedAt allows to filter by last execution update date. @@ -197,7 +197,7 @@ func (opts ListOpts) ToExecutionListQuery() (string, error) { params.Add("include_output", "1") } - for queryParam, value := range map[string]map[string]interface{}{"params": opts.Params, "input": opts.Input, "output": opts.Output} { + for queryParam, value := range map[string]map[string]any{"params": opts.Params, "input": opts.Input, "output": opts.Output} { if value != nil { b, err := json.Marshal(value) if err != nil { diff --git a/openstack/workflow/v2/executions/results.go b/openstack/workflow/v2/executions/results.go index 68abd2f31c..0111a195ee 100644 --- a/openstack/workflow/v2/executions/results.go +++ b/openstack/workflow/v2/executions/results.go @@ -55,13 +55,13 @@ type Execution struct { Description string `json:"description"` // Input contains the workflow input values. - Input map[string]interface{} `json:"-"` + Input map[string]any `json:"-"` // Ouput contains the workflow output values. - Output map[string]interface{} `json:"-"` + Output map[string]any `json:"-"` // Params contains workflow type specific parameters. - Params map[string]interface{} `json:"-"` + Params map[string]any `json:"-"` // ProjectID is the project id owner of the execution. ProjectID string `json:"project_id"` diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index bc824b34a2..d0b4641793 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -48,7 +48,7 @@ func TestCreateExecution(t *testing.T) { opts := &executions.CreateOpts{ WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", - Input: map[string]interface{}{ + Input: map[string]any{ "msg": "Hello", }, Description: "description", @@ -62,14 +62,14 @@ func TestCreateExecution(t *testing.T) { expected := &executions.Execution{ ID: "50bb59f1-eb77-4017-a77f-6d575b002667", Description: "description", - Input: map[string]interface{}{ + Input: map[string]any{ "msg": "Hello", }, - Params: map[string]interface{}{ + Params: map[string]any{ "namespace": "", - "env": map[string]interface{}{}, + "env": map[string]any{}, }, - Output: map[string]interface{}{}, + Output: map[string]any{}, ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", State: "SUCCESS", WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", @@ -121,14 +121,14 @@ func TestGetExecution(t *testing.T) { expected := &executions.Execution{ ID: "50bb59f1-eb77-4017-a77f-6d575b002667", Description: "description", - Input: map[string]interface{}{ + Input: map[string]any{ "msg": "Hello", }, - Params: map[string]interface{}{ + Params: map[string]any{ "namespace": "", - "env": map[string]interface{}{}, + "env": map[string]any{}, }, - Output: map[string]interface{}{}, + Output: map[string]any{}, ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", State: "SUCCESS", WorkflowID: "6656c143-a009-4bcb-9814-cc100a20bbfa", @@ -207,12 +207,12 @@ func TestListExecutions(t *testing.T) { { ID: "50bb59f1-eb77-4017-a77f-6d575b002667", Description: "description", - Input: map[string]interface{}{ + Input: map[string]any{ "msg": "Hello", }, - Params: map[string]interface{}{ + Params: map[string]any{ "namespace": "", - "env": map[string]interface{}{}, + "env": map[string]any{}, }, ProjectID: "778c0f25df0d492a9a868ee9e2fbb513", State: "SUCCESS", @@ -239,7 +239,7 @@ func TestListExecutions(t *testing.T) { func TestToExecutionListQuery(t *testing.T) { for expected, opts := range map[string]*executions.ListOpts{ newValue("input", `{"msg":"Hello"}`): { - Input: map[string]interface{}{ + Input: map[string]any{ "msg": "Hello", }, }, diff --git a/pagination/http.go b/pagination/http.go index 0ec799e903..cf188b89b9 100644 --- a/pagination/http.go +++ b/pagination/http.go @@ -20,7 +20,7 @@ type PageResult struct { // PageResultFrom parses an HTTP response as JSON and returns a PageResult containing the // results, interpreting it as JSON if the content type indicates. func PageResultFrom(resp *http.Response) (PageResult, error) { - var parsedBody interface{} + var parsedBody any defer resp.Body.Close() rawBody, err := io.ReadAll(resp.Body) @@ -42,7 +42,7 @@ func PageResultFrom(resp *http.Response) (PageResult, error) { // PageResultFromParsed constructs a PageResult from an HTTP response that has already had its // body parsed as JSON (and closed). -func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { +func PageResultFromParsed(resp *http.Response, body any) PageResult { return PageResult{ Result: gophercloud.Result{ Body: body, diff --git a/pagination/linked.go b/pagination/linked.go index a5d8727886..7e4de4f7ae 100644 --- a/pagination/linked.go +++ b/pagination/linked.go @@ -31,10 +31,10 @@ func (current LinkedPageBase) NextPageURL() (string, error) { path = current.LinkPath } - submap, ok := current.Body.(map[string]interface{}) + submap, ok := current.Body.(map[string]any) if !ok { err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}" + err.Expected = "map[string]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return "", err } @@ -48,10 +48,10 @@ func (current LinkedPageBase) NextPageURL() (string, error) { } if len(path) > 0 { - submap, ok = value.(map[string]interface{}) + submap, ok = value.(map[string]any) if !ok { err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}" + err.Expected = "map[string]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) return "", err } @@ -76,17 +76,17 @@ func (current LinkedPageBase) NextPageURL() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (current LinkedPageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { + if b, ok := current.Body.([]any); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" + err.Expected = "[]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the linked page's body. This method is needed to satisfy the // Page interface. -func (current LinkedPageBase) GetBody() interface{} { +func (current LinkedPageBase) GetBody() any { return current.Body } diff --git a/pagination/marker.go b/pagination/marker.go index 228bbc1612..1d101fe2db 100644 --- a/pagination/marker.go +++ b/pagination/marker.go @@ -42,17 +42,17 @@ func (current MarkerPageBase) NextPageURL() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (current MarkerPageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { + if b, ok := current.Body.([]any); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" + err.Expected = "[]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the linked page's body. This method is needed to satisfy the // Page interface. -func (current MarkerPageBase) GetBody() interface{} { +func (current MarkerPageBase) GetBody() any { return current.Body } diff --git a/pagination/pager.go b/pagination/pager.go index 8048f9f74c..3581012566 100644 --- a/pagination/pager.go +++ b/pagination/pager.go @@ -31,7 +31,7 @@ type Page interface { IsEmpty() (bool, error) // GetBody returns the Page Body. This is used in the `AllPages` method. - GetBody() interface{} + GetBody() any } // Pager knows how to advance through a specific resource collection, one page at a time. @@ -140,7 +140,7 @@ func (p Pager) AllPages(ctx context.Context) (Page, error) { return nil, p.Err } // pagesSlice holds all the pages until they get converted into as Page Body. - var pagesSlice []interface{} + var pagesSlice []any // body will contain the final concatenated Page body. var body reflect.Value @@ -161,21 +161,21 @@ func (p Pager) AllPages(ctx context.Context) (Page, error) { // store the first page to avoid getting it twice p.firstPage = firstPage - // Switch on the page body type. Recognized types are `map[string]interface{}`, - // `[]byte`, and `[]interface{}`. + // Switch on the page body type. Recognized types are `map[string]any`, + // `[]byte`, and `[]any`. switch pb := firstPage.GetBody().(type) { - case map[string]interface{}: - // key is the map key for the page body if the body type is `map[string]interface{}`. + case map[string]any: + // key is the map key for the page body if the body type is `map[string]any`. var key string // Iterate over the pages to concatenate the bodies. err = p.EachPage(ctx, func(_ context.Context, page Page) (bool, error) { - b := page.GetBody().(map[string]interface{}) + b := page.GetBody().(map[string]any) for k, v := range b { // If it's a linked page, we don't want the `links`, we want the other one. if !strings.HasSuffix(k, "links") { - // check the field's type. we only want []interface{} (which is really []map[string]interface{}) + // check the field's type. we only want []any (which is really []map[string]any) switch vt := v.(type) { - case []interface{}: + case []any: key = k pagesSlice = append(pagesSlice, vt...) } @@ -186,7 +186,7 @@ func (p Pager) AllPages(ctx context.Context) (Page, error) { if err != nil { return nil, err } - // Set body to value of type `map[string]interface{}` + // Set body to value of type `map[string]any` body = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(pagesSlice))) body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice)) case []byte: @@ -213,24 +213,24 @@ func (p Pager) AllPages(ctx context.Context) (Page, error) { // Set body to value of type `bytes`. body = reflect.New(reflect.TypeOf(b)).Elem() body.SetBytes(b) - case []interface{}: + case []any: // Iterate over the pages to concatenate the bodies. err = p.EachPage(ctx, func(_ context.Context, page Page) (bool, error) { - b := page.GetBody().([]interface{}) + b := page.GetBody().([]any) pagesSlice = append(pagesSlice, b...) return true, nil }) if err != nil { return nil, err } - // Set body to value of type `[]interface{}` + // Set body to value of type `[]any` body = reflect.MakeSlice(reflect.TypeOf(pagesSlice), len(pagesSlice), len(pagesSlice)) for i, s := range pagesSlice { body.Index(i).Set(reflect.ValueOf(s)) } default: err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}/[]byte/[]interface{}" + err.Expected = "map[string]any/[]byte/[]any" err.Actual = fmt.Sprintf("%T", pb) return nil, err } diff --git a/pagination/single.go b/pagination/single.go index ba1176ec9a..416621121b 100644 --- a/pagination/single.go +++ b/pagination/single.go @@ -17,17 +17,17 @@ func (current SinglePageBase) NextPageURL() (string, error) { // IsEmpty satisifies the IsEmpty method of the Page interface func (current SinglePageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { + if b, ok := current.Body.([]any); ok { return len(b) == 0, nil } err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" + err.Expected = "[]any" err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) return true, err } // GetBody returns the single page's body. This method is needed to satisfy the // Page interface. -func (current SinglePageBase) GetBody() interface{} { +func (current SinglePageBase) GetBody() any { return current.Body } diff --git a/params.go b/params.go index 10e1536ae1..09b322a6a2 100644 --- a/params.go +++ b/params.go @@ -32,7 +32,7 @@ BuildRequestBody is used within Gophercloud to more fully understand how it fits within the request process as a whole rather than use it directly as shown above. */ -func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) { +func BuildRequestBody(opts any, parent string) (map[string]any, error) { optsValue := reflect.ValueOf(opts) if optsValue.Kind() == reflect.Ptr { optsValue = optsValue.Elem() @@ -43,7 +43,7 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, optsType = optsType.Elem() } - optsMap := make(map[string]interface{}) + optsMap := make(map[string]any) if optsValue.Kind() == reflect.Struct { //fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind()) for i := 0; i < optsValue.NumField(); i++ { @@ -180,7 +180,7 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, //fmt.Printf("optsMap: %+v\n", optsMap) if parent != "" { - optsMap = map[string]interface{}{parent: optsMap} + optsMap = map[string]any{parent: optsMap} } //fmt.Printf("optsMap after parent added: %+v\n", optsMap) return optsMap, nil @@ -325,7 +325,7 @@ Slice are handled in one of two ways: Baz []int `q:"baz" format="comma-separated"` // E.g. ?baz=1,2 } */ -func BuildQueryString(opts interface{}) (*url.URL, error) { +func BuildQueryString(opts any) (*url.URL, error) { optsValue := reflect.ValueOf(opts) if optsValue.Kind() == reflect.Ptr { optsValue = optsValue.Elem() @@ -431,7 +431,7 @@ will be converted into: Untagged fields and fields left at their zero values are skipped. Integers, booleans and string values are supported. */ -func BuildHeaders(opts interface{}) (map[string]string, error) { +func BuildHeaders(opts any) (map[string]string, error) { optsValue := reflect.ValueOf(opts) if optsValue.Kind() == reflect.Ptr { optsValue = optsValue.Elem() diff --git a/provider_client.go b/provider_client.go index 4a57f3d1e6..556a4bc567 100644 --- a/provider_client.go +++ b/provider_client.go @@ -311,13 +311,13 @@ type RequestOpts struct { // JSONBody, if provided, will be encoded as JSON and used as the body of the HTTP request. The // content type of the request will default to "application/json" unless overridden by MoreHeaders. // It's an error to specify both a JSONBody and a RawBody. - JSONBody interface{} + JSONBody any // RawBody contains an io.Reader that will be consumed by the request directly. No content-type // will be set unless one is provided explicitly by MoreHeaders. RawBody io.Reader // JSONResponse, if provided, will be populated with the contents of the response body parsed as // JSON. - JSONResponse interface{} + JSONResponse any // OkCodes contains a list of numeric HTTP status codes that should be interpreted as success. If // the response has a different code, an error will be returned. OkCodes []int diff --git a/results.go b/results.go index b3ee9d5682..9e6f630abb 100644 --- a/results.go +++ b/results.go @@ -28,7 +28,7 @@ provider- or extension-specific information as well. type Result struct { // Body is the payload of the HTTP response from the server. In most cases, // this will be the deserialized JSON structure. - Body interface{} + Body any // StatusCode is the HTTP status code of the original response. Will be // one of the OkCodes defined on the gophercloud.RequestOpts that was @@ -46,7 +46,7 @@ type Result struct { // ExtractInto allows users to provide an object into which `Extract` will extract // the `Result.Body`. This would be useful for OpenStack providers that have // different fields in the response object than OpenStack proper. -func (r Result) ExtractInto(to interface{}) error { +func (r Result) ExtractInto(to any) error { if r.Err != nil { return r.Err } @@ -67,12 +67,12 @@ func (r Result) ExtractInto(to interface{}) error { return err } -func (r Result) extractIntoPtr(to interface{}, label string) error { +func (r Result) extractIntoPtr(to any, label string) error { if label == "" { return r.ExtractInto(&to) } - var m map[string]interface{} + var m map[string]any err := r.ExtractInto(&m) if err != nil { return err @@ -95,7 +95,7 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) - if mSlice, ok := m[label].([]interface{}); ok { + if mSlice, ok := m[label].([]any); ok { for _, v := range mSlice { // For each iteration of the slice, we create a new struct. // This is to work around a bug where elements of a slice @@ -171,7 +171,7 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { } // ExtractIntoStructPtr will unmarshal the Result (r) into the provided -// interface{} (to). +// any (to). // // NOTE: For internal use only // @@ -179,7 +179,7 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { // // If provided, `label` will be filtered out of the response // body prior to `r` being unmarshalled into `to`. -func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { +func (r Result) ExtractIntoStructPtr(to any, label string) error { if r.Err != nil { return r.Err } @@ -197,7 +197,7 @@ func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { } // ExtractIntoSlicePtr will unmarshal the Result (r) into the provided -// interface{} (to). +// any (to). // // NOTE: For internal use only // @@ -205,7 +205,7 @@ func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { // // If provided, `label` will be filtered out of the response // body prior to `r` being unmarshalled into `to`. -func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error { +func (r Result) ExtractIntoSlicePtr(to any, label string) error { if r.Err != nil { return r.Err } @@ -267,7 +267,7 @@ type HeaderResult struct { // ExtractInto allows users to provide an object into which `Extract` will // extract the http.Header headers of the result. -func (r HeaderResult) ExtractInto(to interface{}) error { +func (r HeaderResult) ExtractInto(to any) error { if r.Err != nil { return r.Err } diff --git a/service_client.go b/service_client.go index 61834cc613..11b80108c3 100644 --- a/service_client.go +++ b/service_client.go @@ -48,7 +48,7 @@ func (client *ServiceClient) ServiceURL(parts ...string) string { return client.ResourceBaseURL() + strings.Join(parts, "/") } -func (client *ServiceClient) initReqOpts(JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) { +func (client *ServiceClient) initReqOpts(JSONBody any, JSONResponse any, opts *RequestOpts) { if v, ok := (JSONBody).(io.Reader); ok { opts.RawBody = v } else if JSONBody != nil { @@ -61,7 +61,7 @@ func (client *ServiceClient) initReqOpts(JSONBody interface{}, JSONResponse inte } // Get calls `Request` with the "GET" HTTP verb. -func (client *ServiceClient) Get(ctx context.Context, url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Get(ctx context.Context, url string, JSONResponse any, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } @@ -70,7 +70,7 @@ func (client *ServiceClient) Get(ctx context.Context, url string, JSONResponse i } // Post calls `Request` with the "POST" HTTP verb. -func (client *ServiceClient) Post(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Post(ctx context.Context, url string, JSONBody any, JSONResponse any, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } @@ -79,7 +79,7 @@ func (client *ServiceClient) Post(ctx context.Context, url string, JSONBody inte } // Put calls `Request` with the "PUT" HTTP verb. -func (client *ServiceClient) Put(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Put(ctx context.Context, url string, JSONBody any, JSONResponse any, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } @@ -88,7 +88,7 @@ func (client *ServiceClient) Put(ctx context.Context, url string, JSONBody inter } // Patch calls `Request` with the "PATCH" HTTP verb. -func (client *ServiceClient) Patch(ctx context.Context, url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { +func (client *ServiceClient) Patch(ctx context.Context, url string, JSONBody any, JSONResponse any, opts *RequestOpts) (*http.Response, error) { if opts == nil { opts = new(RequestOpts) } diff --git a/testhelper/convenience.go b/testhelper/convenience.go index 166e60c5d1..3b3e63979d 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -24,11 +24,11 @@ func prefix(depth int) string { return fmt.Sprintf("Failure in %s, line %d:", filepath.Base(file), line) } -func green(str interface{}) string { +func green(str any) string { return fmt.Sprintf("%s%#v%s", greenCode, str, resetCode) } -func yellow(str interface{}) string { +func yellow(str any) string { return fmt.Sprintf("%s%#v%s", yellowCode, str, resetCode) } @@ -42,7 +42,7 @@ func logError(t *testing.T, str string) { t.Errorf(logBodyFmt, prefix(3), str) } -type diffLogger func([]string, interface{}, interface{}) +type diffLogger func([]string, any, any) type visit struct { a1 uintptr @@ -57,7 +57,7 @@ func deepDiffEqual(expected, actual reflect.Value, visited map[visit]bool, path defer func() { // Fall back to the regular reflect.DeepEquals function. if r := recover(); r != nil { - var e, a interface{} + var e, a any if expected.IsValid() { e = expected.Interface() } @@ -197,7 +197,7 @@ func deepDiffEqual(expected, actual reflect.Value, visited map[visit]bool, path } } -func deepDiff(expected, actual interface{}, logDifference diffLogger) { +func deepDiff(expected, actual any, logDifference diffLogger) { if expected == nil || actual == nil { logDifference([]string{}, expected, actual) return @@ -215,7 +215,7 @@ func deepDiff(expected, actual interface{}, logDifference diffLogger) { // AssertEquals compares two arbitrary values and performs a comparison. If the // comparison fails, a fatal error is raised that will fail the test -func AssertEquals(t *testing.T, expected, actual interface{}) { +func AssertEquals(t *testing.T, expected, actual any) { t.Helper() if expected != actual { @@ -224,7 +224,7 @@ func AssertEquals(t *testing.T, expected, actual interface{}) { } // CheckEquals is similar to AssertEquals, except with a non-fatal error -func CheckEquals(t *testing.T, expected, actual interface{}) { +func CheckEquals(t *testing.T, expected, actual any) { t.Helper() if expected != actual { @@ -234,13 +234,13 @@ func CheckEquals(t *testing.T, expected, actual interface{}) { // AssertDeepEquals - like Equals - performs a comparison - but on more complex // structures that requires deeper inspection -func AssertDeepEquals(t *testing.T, expected, actual interface{}) { +func AssertDeepEquals(t *testing.T, expected, actual any) { t.Helper() pre := prefix(2) differed := false - deepDiff(expected, actual, func(path []string, expected, actual interface{}) { + deepDiff(expected, actual, func(path []string, expected, actual any) { differed = true t.Errorf("\033[1;31m%sat %s expected %s, but got %s\033[0m", pre, @@ -254,12 +254,12 @@ func AssertDeepEquals(t *testing.T, expected, actual interface{}) { } // CheckDeepEquals is similar to AssertDeepEquals, except with a non-fatal error -func CheckDeepEquals(t *testing.T, expected, actual interface{}) { +func CheckDeepEquals(t *testing.T, expected, actual any) { t.Helper() pre := prefix(2) - deepDiff(expected, actual, func(path []string, expected, actual interface{}) { + deepDiff(expected, actual, func(path []string, expected, actual any) { t.Errorf("\033[1;31m%s at %s expected %s, but got %s\033[0m", pre, strings.Join(path, ""), @@ -292,8 +292,8 @@ func CheckByteArrayEquals(t *testing.T, expectedBytes []byte, actualBytes []byte // isJSONEquals is a utility function that implements JSON comparison for AssertJSONEquals and // CheckJSONEquals. -func isJSONEquals(t *testing.T, expectedJSON string, actual interface{}) bool { - var parsedExpected, parsedActual interface{} +func isJSONEquals(t *testing.T, expectedJSON string, actual any) bool { + var parsedExpected, parsedActual any err := json.Unmarshal([]byte(expectedJSON), &parsedExpected) if err != nil { t.Errorf("Unable to parse expected value as JSON: %v", err) @@ -332,9 +332,9 @@ func isJSONEquals(t *testing.T, expectedJSON string, actual interface{}) bool { // both are consistent. If they aren't, the expected and actual structures are pretty-printed and // shown for comparison. // -// This is useful for comparing structures that are built as nested map[string]interface{} values, +// This is useful for comparing structures that are built as nested map[string]any values, // which are a pain to construct as literals. -func AssertJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { +func AssertJSONEquals(t *testing.T, expectedJSON string, actual any) { t.Helper() if !isJSONEquals(t, expectedJSON, actual) { @@ -343,7 +343,7 @@ func AssertJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { } // CheckJSONEquals is similar to AssertJSONEquals, but nonfatal. -func CheckJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { +func CheckJSONEquals(t *testing.T, expectedJSON string, actual any) { t.Helper() if !isJSONEquals(t, expectedJSON, actual) { @@ -401,7 +401,7 @@ func CheckNoErr(t *testing.T, e error) { // // 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{}) { +func CheckErr(t *testing.T, e error, expected ...any) { t.Helper() if e == nil { diff --git a/testhelper/http_responses.go b/testhelper/http_responses.go index a8a7f36140..28ecb1e53e 100644 --- a/testhelper/http_responses.go +++ b/testhelper/http_responses.go @@ -126,7 +126,7 @@ func TestJSONRequest(t *testing.T, r *http.Request, expected string) { t.Errorf("Unable to read request body: %v", err) } - var actualJSON interface{} + var actualJSON any err = json.Unmarshal(b, &actualJSON) if err != nil { t.Errorf("Unable to parse request body as JSON: %v", err) diff --git a/testing/auth_options_test.go b/testing/auth_options_test.go index ed402aef05..1f9b230b50 100644 --- a/testing/auth_options_test.go +++ b/testing/auth_options_test.go @@ -16,7 +16,7 @@ func TestToTokenV3ScopeMap(t *testing.T) { var successCases = []struct { opts gophercloud.AuthOptions - expected map[string]interface{} + expected map[string]any }{ // System-scoped { @@ -25,8 +25,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { System: true, }, }, - map[string]interface{}{ - "system": map[string]interface{}{ + map[string]any{ + "system": map[string]any{ "all": true, }, }, @@ -38,7 +38,7 @@ func TestToTokenV3ScopeMap(t *testing.T) { TrustID: "05144328-1f7d-46a9-a978-17eaad187077", }, }, - map[string]interface{}{ + map[string]any{ "OS-TRUST:trust": map[string]string{ "id": "05144328-1f7d-46a9-a978-17eaad187077", }, @@ -51,8 +51,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { ProjectID: projectID, }, }, - map[string]interface{}{ - "project": map[string]interface{}{ + map[string]any{ + "project": map[string]any{ "id": &projectID, }, }, @@ -65,10 +65,10 @@ func TestToTokenV3ScopeMap(t *testing.T) { DomainName: domainName, }, }, - map[string]interface{}{ - "project": map[string]interface{}{ + map[string]any{ + "project": map[string]any{ "name": &projectName, - "domain": map[string]interface{}{ + "domain": map[string]any{ "name": &domainName, }, }, @@ -81,8 +81,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { DomainID: domainID, }, }, - map[string]interface{}{ - "domain": map[string]interface{}{ + map[string]any{ + "domain": map[string]any{ "id": &domainID, }, }, @@ -94,8 +94,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { DomainName: domainName, }, }, - map[string]interface{}{ - "domain": map[string]interface{}{ + map[string]any{ + "domain": map[string]any{ "name": &domainName, }, }, @@ -106,8 +106,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { TenantID: projectID, Scope: nil, }, - map[string]interface{}{ - "project": map[string]interface{}{ + map[string]any{ + "project": map[string]any{ "id": &projectID, }, }, @@ -119,10 +119,10 @@ func TestToTokenV3ScopeMap(t *testing.T) { DomainName: domainName, Scope: nil, }, - map[string]interface{}{ - "project": map[string]interface{}{ + map[string]any{ + "project": map[string]any{ "name": &projectName, - "domain": map[string]interface{}{ + "domain": map[string]any{ "name": &domainName, }, }, diff --git a/testing/params_test.go b/testing/params_test.go index 1303cf6fb9..9e74bdd9ea 100644 --- a/testing/params_test.go +++ b/testing/params_test.go @@ -82,7 +82,7 @@ func TestBuildQueryString(t *testing.T) { } th.CheckDeepEquals(t, expected, actual) - _, err = gophercloud.BuildQueryString(map[string]interface{}{"Number": 4}) + _, err = gophercloud.BuildQueryString(map[string]any{"Number": 4}) if err == nil { t.Errorf("Expected error: 'Options type is not a struct'") } @@ -111,7 +111,7 @@ func TestBuildHeaders(t *testing.T) { t.Errorf("Expected error: 'Required header not set'") } - _, err = gophercloud.BuildHeaders(map[string]interface{}{"Number": 4}) + _, err = gophercloud.BuildHeaders(map[string]any{"Number": 4}) if err == nil { t.Errorf("Expected error: 'Options type is not a struct'") } @@ -168,7 +168,7 @@ func TestBuildRequestBody(t *testing.T) { var successCases = []struct { opts AuthOptions - expected map[string]interface{} + expected map[string]any }{ { AuthOptions{ @@ -177,9 +177,9 @@ func TestBuildRequestBody(t *testing.T) { Password: "swordfish", }, }, - map[string]interface{}{ - "auth": map[string]interface{}{ - "passwordCredentials": map[string]interface{}{ + map[string]any{ + "auth": map[string]any{ + "passwordCredentials": map[string]any{ "password": "swordfish", "username": "me", }, @@ -192,9 +192,9 @@ func TestBuildRequestBody(t *testing.T) { ID: "1234567", }, }, - map[string]interface{}{ - "auth": map[string]interface{}{ - "token": map[string]interface{}{ + map[string]any{ + "auth": map[string]any{ + "token": map[string]any{ "id": "1234567", }, }, @@ -267,7 +267,7 @@ func TestBuildRequestBody(t *testing.T) { CreatedAt: &createdAt, } - expectedComplexFields := map[string]interface{}{ + expectedComplexFields := map[string]any{ "username": "jdoe", } diff --git a/testing/results_test.go b/testing/results_test.go index 8bfcc03683..21ef44c802 100644 --- a/testing/results_test.go +++ b/testing/results_test.go @@ -95,7 +95,7 @@ type TestPersonWithExtensionsNamed struct { func TestUnmarshalAnonymousStructs(t *testing.T) { var actual TestPersonWithExtensions - var dejson interface{} + var dejson any sejson := []byte(singleResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { @@ -118,7 +118,7 @@ func TestUnmarshalAnonymousStructs(t *testing.T) { func TestUnmarshalSliceOfAnonymousStructs(t *testing.T) { var actual []TestPersonWithExtensions - var dejson interface{} + var dejson any sejson := []byte(multiResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { @@ -143,7 +143,7 @@ func TestUnmarshalSliceOfAnonymousStructs(t *testing.T) { func TestUnmarshalSliceofStruct(t *testing.T) { var actual []TestPerson - var dejson interface{} + var dejson any sejson := []byte(multiResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { @@ -165,7 +165,7 @@ func TestUnmarshalSliceofStruct(t *testing.T) { func TestUnmarshalNamedStructs(t *testing.T) { var actual TestPersonWithExtensionsNamed - var dejson interface{} + var dejson any sejson := []byte(singleResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { @@ -187,7 +187,7 @@ func TestUnmarshalNamedStructs(t *testing.T) { func TestUnmarshalSliceOfNamedStructs(t *testing.T) { var actual []TestPersonWithExtensionsNamed - var dejson interface{} + var dejson any sejson := []byte(multiResponse) err := json.Unmarshal(sejson, &dejson) if err != nil { diff --git a/testing/util_test.go b/testing/util_test.go index 8440d9ed33..6b6b425f45 100644 --- a/testing/util_test.go +++ b/testing/util_test.go @@ -150,7 +150,7 @@ func TestRemainingKeys(t *testing.T) { IsAdmin bool } - userResponse := map[string]interface{}{ + userResponse := map[string]any{ "user_id": "abcd1234", "username": "jdoe", "location": "Hawaii", @@ -160,7 +160,7 @@ func TestRemainingKeys(t *testing.T) { "custom_field": "foo", } - expected := map[string]interface{}{ + expected := map[string]any{ "created_at": "2017-06-08T02:49:03.000000", "is_admin": "true", "custom_field": "foo", diff --git a/util.go b/util.go index 65748d00b6..ad8a7dfaaa 100644 --- a/util.go +++ b/util.go @@ -61,8 +61,8 @@ func NormalizeURL(url string) string { // // This is useful for determining the extra fields returned in response bodies // for resources that can contain an arbitrary or dynamic number of fields. -func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]interface{}) { - extras = make(map[string]interface{}) +func RemainingKeys(s any, m map[string]any) (extras map[string]any) { + extras = make(map[string]any) for k, v := range m { extras[k] = v } From 08f68928b309021cb58a09c236c4ac51ed9ed9ae Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 19 Mar 2024 16:18:36 +0000 Subject: [PATCH 1875/2296] script: Remove cibuild This has not been used since we migrated to Travis. Signed-off-by: Stephen Finucane --- script/cibuild | 5 ----- 1 file changed, 5 deletions(-) delete mode 100755 script/cibuild diff --git a/script/cibuild b/script/cibuild deleted file mode 100755 index 1cb389e7dc..0000000000 --- a/script/cibuild +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# -# Test script to be invoked by Travis. - -exec script/unittest -v From bfdf221bbc754ca8511402ff8e801441081e27f5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 19 Mar 2024 15:59:45 +0000 Subject: [PATCH 1876/2296] script: Add 'go vet' wrapper ...to ensure we set the GOFLAGS correctly. Signed-off-by: Stephen Finucane --- .github/workflows/unit.yml | 2 +- script/vet | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100755 script/vet diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 6f40060af1..c7bc500cde 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -38,7 +38,7 @@ jobs: - name: Run go vet run: | - go vet ./... + ./script/vet - name: Run unit tests run: | diff --git a/script/vet b/script/vet new file mode 100755 index 0000000000..d82a2a0cff --- /dev/null +++ b/script/vet @@ -0,0 +1,8 @@ +#!/bin/bash +# +# Run go vet. + +set -e + +GOFLAGS="-tags=acceptance" +go vet ./... From df679fd63ead61044f975f23ee70c0c476437a08 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 19 Mar 2024 16:29:49 +0000 Subject: [PATCH 1877/2296] script: Consistent 'set' usage Well, mostly consistent: the acceptancetest script needs some special treatment due to DevStack being weird. Signed-off-by: Stephen Finucane --- script/acceptancetest | 14 +++++++++++--- script/collectlogs | 7 ++++++- script/coverage | 4 +++- script/format | 4 ++++ script/test | 2 ++ script/unittest | 2 ++ script/vet | 2 +- 7 files changed, 29 insertions(+), 6 deletions(-) diff --git a/script/acceptancetest b/script/acceptancetest index 9e540b2d84..023f48b853 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -1,13 +1,21 @@ #!/bin/bash # -set -x -set -o pipefail +# Run acceptance tests. -source `dirname $0`/stackenv +# We intentionally don't set '-u' (error on unbound variables) or '-e' (exit on +# first failure) initially since DevStack is not designed to run with these +# flags and things crash and burn *spectacularly* 🔥🔥🔥 +set -xo pipefail + +source $(dirname $0)/stackenv + +# ...but we can do it after the fact +set -eu timeout="60m" failed= +LOG_DIR=${LOG_DIR:-} if [[ -z "${LOG_DIR}" ]]; then echo "LOG_DIR not set, will set a temp directory" LOG_DIR=/tmp/devstack-logs diff --git a/script/collectlogs b/script/collectlogs index 5ad61c86e4..55ba3ddbbd 100755 --- a/script/collectlogs +++ b/script/collectlogs @@ -1,5 +1,10 @@ #!/bin/bash -set -x +# +# Collect logs after an integration test failure. + +# We intentionally don't set '-e' (exit on first failure) since we don't want +# to fail on these diagnostic steps +set -uxo pipefail LOG_DIR=${LOG_DIR:-/tmp/devstack-logs} mkdir -p "$LOG_DIR" diff --git a/script/coverage b/script/coverage index 6b76a1be7b..cd4dec06e5 100755 --- a/script/coverage +++ b/script/coverage @@ -1,6 +1,8 @@ #!/bin/bash +# +# Run unit tests with coverage enabled. -set -e +set -euxo pipefail n=1 for testpkg in $(go list ./testing ./.../testing); do diff --git a/script/format b/script/format index 672896b998..9f80a0c2b1 100755 --- a/script/format +++ b/script/format @@ -1,4 +1,8 @@ #!/usr/bin/env bash +# +# Run 'go fmt' (or rather, goimports) to warn about unformatted code + +set -euxo pipefail goimports="goimports" diff --git a/script/test b/script/test index 1e03dff8ab..cb6fee4ee9 100755 --- a/script/test +++ b/script/test @@ -2,4 +2,6 @@ # # Run all the tests. +set -euxo pipefail + exec go test -tags 'acceptance fixtures' ./... $@ diff --git a/script/unittest b/script/unittest index 609f74cd0c..67f3ea1431 100755 --- a/script/unittest +++ b/script/unittest @@ -2,6 +2,8 @@ # # Run the unit tests. +set -euxo pipefail + # Do extra rounds of testing to help identify reauth concurrency issues. # All other packages are tested in the `coverage` tests. go test -v -race -count=5 ./testing diff --git a/script/vet b/script/vet index d82a2a0cff..4f3db40a28 100755 --- a/script/vet +++ b/script/vet @@ -2,7 +2,7 @@ # # Run go vet. -set -e +set -euxo pipefail GOFLAGS="-tags=acceptance" go vet ./... From 7d8ccf5754d445d2c6f44da5655b4826fd38e75f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 13:19:26 +0000 Subject: [PATCH 1878/2296] script: Remove use of exec I'm guessing this was here to ensure we respected the exit code of the 'go test' invocation. We now do this through the appropriate use of 'set' so we no longer need to use 'exec'. Signed-off-by: Stephen Finucane --- .github/workflows/unit.yml | 2 +- script/test | 2 +- script/unittest | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index c7bc500cde..084cb3cba8 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -44,7 +44,7 @@ jobs: run: | ./script/coverage ./script/format - ./script/unittest -v + ./script/unittest - name: Coveralls Parallel uses: coverallsapp/github-action@v2 diff --git a/script/test b/script/test index cb6fee4ee9..77f6af5017 100755 --- a/script/test +++ b/script/test @@ -4,4 +4,4 @@ set -euxo pipefail -exec go test -tags 'acceptance fixtures' ./... $@ +go test -v -tags 'acceptance fixtures' ./... $@ diff --git a/script/unittest b/script/unittest index 67f3ea1431..2763e0efd9 100755 --- a/script/unittest +++ b/script/unittest @@ -6,4 +6,4 @@ set -euxo pipefail # Do extra rounds of testing to help identify reauth concurrency issues. # All other packages are tested in the `coverage` tests. -go test -v -race -count=5 ./testing +go test -v -race -count=5 ./testing $@ From 132d71459f5cb44993efc6add4cdb9d40def1828 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 13:14:00 +0000 Subject: [PATCH 1879/2296] script: Replace 'goimports' with 'gofmt' We now use 'go vet' in CI, which includes import checks. Since we don't want to *fix* these failures in CI - merely report them - this is a better choice. Drop 'goimports' in favour of 'gofmt'. Signed-off-by: Stephen Finucane --- .github/workflows/unit.yml | 1 - script/format | 42 ++------------------------------------ 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 084cb3cba8..aa6ae834c2 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -34,7 +34,6 @@ jobs: go mod init unit_tests go install github.com/wadey/gocovmerge@master - go install golang.org/x/tools/cmd/goimports@latest - name: Run go vet run: | diff --git a/script/format b/script/format index 9f80a0c2b1..1bbf74ce39 100755 --- a/script/format +++ b/script/format @@ -1,45 +1,7 @@ #!/usr/bin/env bash # -# Run 'go fmt' (or rather, goimports) to warn about unformatted code +# Run 'go fmt' to warn about unformatted code set -euxo pipefail -goimports="goimports" - -find_files() { - find . -not \( \ - \( \ - -wholename './output' \ - -o -wholename './_output' \ - -o -wholename './_gopath' \ - -o -wholename './release' \ - -o -wholename './target' \ - -o -wholename '*/third_party/*' \ - -o -wholename '*/vendor/*' \ - \) -prune \ - \) -name '*.go' -} - -ignore_files=( - "./openstack/compute/v2/quotasets/testing/fixtures.go" - "./openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go" -) - -bad_files=$(find_files | xargs ${goimports} -l) - -final_files=() -for bad_file in $bad_files; do - found= - for ignore_file in "${ignore_files[@]}"; do - [[ "${bad_file}" == "${ignore_file}" ]] && { found=1; break; } - done - [[ -n $found ]] || final_files+=("$bad_file") -done - -if [[ "${#final_files[@]}" -gt 0 ]]; then - diff=$(echo "${final_files[@]}" | xargs ${goimports} -d -e 2>&1) - if [[ -n "${diff}" ]]; then - echo "${diff}" - exit 1 - fi -fi +go fmt ./... From e88c33e86b15810e89eb9a4c8bf28a9e02f143f7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 13:26:10 +0000 Subject: [PATCH 1880/2296] CI: Rename 'gomod' to 'lint' and move linter steps I often see failing "unit tests" only to discover the actual failures are linter related. Move these linter jobs to a separate workflow to avoid this confusion going forward. Signed-off-by: Stephen Finucane --- .github/workflows/gomod.yml | 14 -------------- .github/workflows/lint.yaml | 27 +++++++++++++++++++++++++++ .github/workflows/unit.yml | 9 ++------- 3 files changed, 29 insertions(+), 21 deletions(-) delete mode 100644 .github/workflows/gomod.yml create mode 100644 .github/workflows/lint.yaml diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml deleted file mode 100644 index 43dc5c242b..0000000000 --- a/.github/workflows/gomod.yml +++ /dev/null @@ -1,14 +0,0 @@ -on: [push, pull_request] -name: go mod -permissions: - contents: read - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - 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/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000000..665b887187 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,27 @@ +on: [push, pull_request] +name: Linters +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-go@v5 + with: + go-version: '1' + + - uses: actions/checkout@v4 + + - name: Run go fmt + run: | + ./script/format + + - name: Run go vet + run: | + ./script/vet + + - name: Ensure go.mod is up-to-date + run: | + if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index aa6ae834c2..a09d6df227 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -35,17 +35,12 @@ jobs: go install github.com/wadey/gocovmerge@master - - name: Run go vet - run: | - ./script/vet - - name: Run unit tests run: | ./script/coverage - ./script/format ./script/unittest - - name: Coveralls Parallel + - name: Check coverage uses: coverallsapp/github-action@v2 with: file: cover.out @@ -59,7 +54,7 @@ jobs: if: ${{ always() }} runs-on: ubuntu-latest steps: - - name: Coveralls Finished + - name: Store coverage results uses: coverallsapp/github-action@v2 with: parallel-finished: true From fde61a3d049b71411fd229f6935faa6fd0001b8f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 13:29:39 +0000 Subject: [PATCH 1881/2296] CI: Remove 'reauth-retests' workflow This is already run as part of the 'unit' workflow. Signed-off-by: Stephen Finucane --- .github/workflows/reauth-retests.yaml | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 .github/workflows/reauth-retests.yaml diff --git a/.github/workflows/reauth-retests.yaml b/.github/workflows/reauth-retests.yaml deleted file mode 100644 index 983102a048..0000000000 --- a/.github/workflows/reauth-retests.yaml +++ /dev/null @@ -1,25 +0,0 @@ -on: [push, pull_request] -name: Reauth retests -permissions: - contents: read - -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@v5 - with: - go-version: ${{ matrix.go-version }} - - - uses: actions/checkout@v4 - - - name: Run reauth retests - run: | - ./script/unittest From 236b671d90cd10136d6e3f10f0960b83c994ca8e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 15:14:22 +0000 Subject: [PATCH 1882/2296] CI: Simplify 'acceptancetest' script Now that we skip tests that require optional features or extensions, we can use standard module discovery rather than a glob of bash to run our tests. Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 2 +- .github/workflows/functional-basic.yaml | 2 +- .../workflows/functional-blockstorage.yaml | 2 +- .github/workflows/functional-compute.yaml | 2 +- .../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-image.yaml | 2 +- .github/workflows/functional-keymanager.yaml | 2 +- .../workflows/functional-loadbalancer.yaml | 2 +- .github/workflows/functional-messaging.yaml | 2 +- .github/workflows/functional-networking.yaml | 2 +- .../workflows/functional-objectstorage.yaml | 2 +- .../workflows/functional-orchestration.yaml | 2 +- .github/workflows/functional-placement.yaml | 2 +- .../functional-sharedfilesystems.yaml | 2 +- script/acceptancetest | 27 +------------------ 18 files changed, 18 insertions(+), 43 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 9b86bd5e36..42c3866925 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -96,7 +96,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: '^.*baremetal(.(?!noauth).*)?$' + PACKAGE: "./internal/acceptance/openstack/baremetal/..." OS_BRANCH: ${{ matrix.openstack_version }} # TODO(dtantsur): default to "all" when no longer supporting versions before 2024.1 OS_SYSTEM_SCOPE: ${{ matrix.os_system_scope }} diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 4facd4c4bd..cc134dfb91 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -48,7 +48,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: '^internal/acceptance/openstack$' + PACKAGE: './internal/acceptance/openstack' OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 358f21551d..daf2bb31ce 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -47,7 +47,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: ^.*blockstorage(.(?!noauth).*)?$ + PACKAGE: "./internal/acceptance/openstack/blockstorage/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 22fa1f9b14..0065891bdb 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -47,7 +47,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*compute.*$" + PACKAGE: "./internal/acceptance/openstack/compute/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 0a9297380a..d42add38cb 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -64,7 +64,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*containerinfra.*$" + PACKAGE: "./internal/acceptance/openstack/containerinfra/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index d5a93ab3a2..9145ae0198 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -48,7 +48,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^acceptance/openstack/dns.*$" + PACKAGE: "./internal/acceptance/openstack/dns/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 6cfa173ed5..6accf2828c 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -52,7 +52,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*fwaas_v2.*$" + PACKAGE: "./internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 7a3291e944..ebe76b40a0 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -45,7 +45,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*identity.*$" + PACKAGE: "./internal/acceptance/openstack/identity/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index ee6bd21f82..70ab27eeef 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -45,7 +45,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*image.*$" + PACKAGE: "./internal/acceptance/openstack/image/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index f9ce350b9c..de2c95c503 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -47,7 +47,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*keymanager.*$" + PACKAGE: "./internal/acceptance/openstack/keymanager/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index c7ea99cd0f..b93014dadd 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -48,7 +48,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*loadbalancer.*$" + PACKAGE: "./internal/acceptance/openstack/loadbalancer/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 85b63883af..6703dbb835 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -48,7 +48,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*messaging.*$" + PACKAGE: "./internal/acceptance/openstack/messaging/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 3df3816d0e..e83b5cbe78 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -61,7 +61,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^(?!.*fwaas_v2.*).*networking.*$" + PACKAGE: "./internal/acceptance/openstack/networking/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index f632516b9a..ebb63e8a41 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -51,7 +51,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: '^.*objectstorage.*$' + PACKAGE: "./internal/acceptance/openstack/objectstorage/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 8d58039f65..8e7bc6b8aa 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -47,7 +47,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: ^.*orchestration.*$ + PACKAGE: "./internal/acceptance/openstack/orchestration/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index b1eafc72b4..d407413ac0 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -45,7 +45,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: ^.*placement.*$ + PACKAGE: "./internal/acceptance/openstack/placement/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 6c21cdc632..e46a39f0ee 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -61,7 +61,7 @@ jobs: run: ./script/acceptancetest env: DEVSTACK_PATH: ${{ github.workspace }}/devstack - ACCEPTANCE_TESTS_FILTER: "^.*sharedfilesystems.*$" + PACKAGE: "./internal/acceptance/openstack/sharedfilesystems/..." OS_BRANCH: ${{ matrix.openstack_version }} - name: Generate logs on failure run: ./script/collectlogs diff --git a/script/acceptancetest b/script/acceptancetest index 023f48b853..234f5b0bb6 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -22,29 +22,4 @@ if [[ -z "${LOG_DIR}" ]]; then fi mkdir -p ${LOG_DIR} -if [[ -z "${ACCEPTANCE_TESTS_FILTER}" ]]; then - 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") -fi -ACCEPTANCE_TESTS=($ACCEPTANCE_TESTS) - -if [[ -z $ACCEPTANCE_TESTS ]]; then - echo "No acceptance tests to run" - exit 0 -fi - -for acceptance_test in "${ACCEPTANCE_TESTS[@]}"; do - 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 - fi -done - -# If any of the test suites failed, exit 1 -if [[ -n $failed ]]; then - exit 1 -fi - -exit 0 +go test -v -timeout $timeout -tags "fixtures acceptance" ${PACKAGE:-./internal/acceptance/openstack/...} $@ |& tee -a ${LOG_DIR}/acceptance_tests.log From 1cbc4081ff1950ae1ef7d7a4df30deb0895dbcc5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 21 Mar 2024 13:51:03 +0000 Subject: [PATCH 1883/2296] script: Add missing shebang Signed-off-by: Stephen Finucane --- script/stackenv | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/stackenv b/script/stackenv index ef6311c039..86d67ef82c 100644 --- a/script/stackenv +++ b/script/stackenv @@ -1,5 +1,7 @@ +#!/bin/bash +# # Prep the testing environment by creating the required testing resources and -# environment variables. This env is for theopenlab CI jobs, you might need +# environment variables. This env is used for the CI jobs and you might need # to modify this according to your setup DEVSTACK_PATH=${DEVSTACK_PATH:-/opt/stack/new/devstack} From f2c373288d20762f58e2d33cdb4a81f1ac132cc2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 21 Mar 2024 12:28:05 +0000 Subject: [PATCH 1884/2296] CI: Remove GO111MODULE config This isn't necessary since Go 1.16 [1]. [1] https://maelvls.dev/go111module-everywhere/ Signed-off-by: Stephen Finucane --- .github/workflows/unit.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index a09d6df227..4f12f87442 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -16,9 +16,6 @@ jobs: - "1.21" - "1" - env: - GO111MODULE: "on" - steps: - name: Setup Go ${{ matrix.go-version }} uses: actions/setup-go@v5 From 4f14be1b06442564d8b4e66ae20c769b9b18e181 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 23 May 2024 14:51:41 +0100 Subject: [PATCH 1885/2296] Fix godoc strings godoc is pretty strict about how these are formatted. Signed-off-by: Stephen Finucane --- internal/acceptance/openstack/baremetal/httpbasic/pkg.go | 3 +-- internal/acceptance/openstack/baremetal/noauth/pkg.go | 3 +-- internal/acceptance/openstack/baremetal/v1/pkg.go | 3 +-- internal/acceptance/openstack/blockstorage/noauth/pkg.go | 3 +-- internal/acceptance/openstack/blockstorage/v2/pkg.go | 3 +-- internal/acceptance/openstack/blockstorage/v3/pkg.go | 3 +-- internal/acceptance/openstack/compute/v2/pkg.go | 3 +-- internal/acceptance/openstack/container/v1/pkg.go | 3 +-- internal/acceptance/openstack/containerinfra/v1/pkg.go | 3 +-- internal/acceptance/openstack/db/v1/pkg.go | 3 +-- internal/acceptance/openstack/dns/v2/pkg.go | 3 +-- internal/acceptance/openstack/identity/v2/pkg.go | 3 +-- internal/acceptance/openstack/identity/v3/pkg.go | 3 +-- internal/acceptance/openstack/image/v2/pkg.go | 3 +-- internal/acceptance/openstack/keymanager/v1/pkg.go | 3 +-- internal/acceptance/openstack/loadbalancer/v2/pkg.go | 3 +-- internal/acceptance/openstack/messaging/v2/pkg.go | 3 +-- internal/acceptance/openstack/networking/v2/pkg.go | 3 +-- internal/acceptance/openstack/objectstorage/v1/pkg.go | 3 +-- internal/acceptance/openstack/orchestration/v1/pkg.go | 3 +-- internal/acceptance/openstack/pkg.go | 1 + internal/acceptance/openstack/placement/v1/pkg.go | 3 +-- internal/acceptance/openstack/sharedfilesystems/v2/pkg.go | 3 +-- internal/acceptance/openstack/workflow/v2/pkg.go | 3 +-- internal/acceptance/tools/pkg.go | 3 +++ 25 files changed, 27 insertions(+), 46 deletions(-) diff --git a/internal/acceptance/openstack/baremetal/httpbasic/pkg.go b/internal/acceptance/openstack/baremetal/httpbasic/pkg.go index bfe9e98db9..0945847785 100644 --- a/internal/acceptance/openstack/baremetal/httpbasic/pkg.go +++ b/internal/acceptance/openstack/baremetal/httpbasic/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal -// The httpbasic package contains acceptance tests for the OpenStack Bare Metal v1 service with HTTP Basic authentation. - +// Package httpbasic contains acceptance tests for the OpenStack Bare Metal v1 service with HTTP Basic authentation. package httpbasic diff --git a/internal/acceptance/openstack/baremetal/noauth/pkg.go b/internal/acceptance/openstack/baremetal/noauth/pkg.go index e601a40ce5..b46f68138c 100644 --- a/internal/acceptance/openstack/baremetal/noauth/pkg.go +++ b/internal/acceptance/openstack/baremetal/noauth/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal -// The noauth package contains acceptance tests for the OpenStack Bare Metal v1 service without authentation. - +// Package noauth contains acceptance tests for the OpenStack Bare Metal v1 service without authentation. package noauth diff --git a/internal/acceptance/openstack/baremetal/v1/pkg.go b/internal/acceptance/openstack/baremetal/v1/pkg.go index faf454b835..eecd22f17c 100644 --- a/internal/acceptance/openstack/baremetal/v1/pkg.go +++ b/internal/acceptance/openstack/baremetal/v1/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || baremetal -// The v1 package contains acceptance tests for the OpenStack Bare Metal v1 service. - +// Package v1 contains acceptance tests for the OpenStack Bare Metal v1 service. package v1 diff --git a/internal/acceptance/openstack/blockstorage/noauth/pkg.go b/internal/acceptance/openstack/blockstorage/noauth/pkg.go index 60df33443c..4851fe9216 100644 --- a/internal/acceptance/openstack/blockstorage/noauth/pkg.go +++ b/internal/acceptance/openstack/blockstorage/noauth/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// The noauth package contains acceptance tests for the OpenStack Block Storage v3 service with no authentation. - +// Package noauth contains acceptance tests for the OpenStack Block Storage v3 service with no authentation. package noauth diff --git a/internal/acceptance/openstack/blockstorage/v2/pkg.go b/internal/acceptance/openstack/blockstorage/v2/pkg.go index 039a168159..e79bdc497b 100644 --- a/internal/acceptance/openstack/blockstorage/v2/pkg.go +++ b/internal/acceptance/openstack/blockstorage/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// The v2 package contains acceptance tests for the Openstack Block Storage v2 service. - +// Package v2 contains acceptance tests for the Openstack Block Storage v2 service. package v2 diff --git a/internal/acceptance/openstack/blockstorage/v3/pkg.go b/internal/acceptance/openstack/blockstorage/v3/pkg.go index e0b01c5f49..bd9aaf217a 100644 --- a/internal/acceptance/openstack/blockstorage/v3/pkg.go +++ b/internal/acceptance/openstack/blockstorage/v3/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || blockstorage -// The v3 package contains acceptance tests for the OpenStack Block Storage v3 service. - +// Package v3 contains acceptance tests for the OpenStack Block Storage v3 service. package v3 diff --git a/internal/acceptance/openstack/compute/v2/pkg.go b/internal/acceptance/openstack/compute/v2/pkg.go index 7ff7750b48..38e2d51b40 100644 --- a/internal/acceptance/openstack/compute/v2/pkg.go +++ b/internal/acceptance/openstack/compute/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || compute -// Package v2 package contains acceptance tests for the Openstack Compute v2 service. - +// Package v2 contains acceptance tests for the Openstack Compute v2 service. package v2 diff --git a/internal/acceptance/openstack/container/v1/pkg.go b/internal/acceptance/openstack/container/v1/pkg.go index 357c83b3d4..73e16ac07f 100644 --- a/internal/acceptance/openstack/container/v1/pkg.go +++ b/internal/acceptance/openstack/container/v1/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || container -// The v1 package contains acceptance tests for the Openstack Container v1 service. - +// Package v1 contains acceptance tests for the Openstack Container v1 service. package v1 diff --git a/internal/acceptance/openstack/containerinfra/v1/pkg.go b/internal/acceptance/openstack/containerinfra/v1/pkg.go index 0babe5ed78..592c547f2e 100644 --- a/internal/acceptance/openstack/containerinfra/v1/pkg.go +++ b/internal/acceptance/openstack/containerinfra/v1/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || containerinfra -// The v1 package contains acceptance tests for the Openstack Container Infra v1 service. - +// Package v1 contains acceptance tests for the Openstack Container Infra v1 service. package v1 diff --git a/internal/acceptance/openstack/db/v1/pkg.go b/internal/acceptance/openstack/db/v1/pkg.go index 6b6f5ac92d..2fbd02ac4f 100644 --- a/internal/acceptance/openstack/db/v1/pkg.go +++ b/internal/acceptance/openstack/db/v1/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || db -// The v1 package contains acceptance tests for the Openstack DB v1 service. - +// Package v1 contains acceptance tests for the Openstack DB v1 service. package v1 diff --git a/internal/acceptance/openstack/dns/v2/pkg.go b/internal/acceptance/openstack/dns/v2/pkg.go index 749b2380dd..e016d7f03b 100644 --- a/internal/acceptance/openstack/dns/v2/pkg.go +++ b/internal/acceptance/openstack/dns/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || dns -// The v1 package contains acceptance tests for the Openstack DNS v1 service. - +// Package v2 contains acceptance tests for the Openstack DNS v2 service. package v2 diff --git a/internal/acceptance/openstack/identity/v2/pkg.go b/internal/acceptance/openstack/identity/v2/pkg.go index 4c072f590d..35db9a6fb6 100644 --- a/internal/acceptance/openstack/identity/v2/pkg.go +++ b/internal/acceptance/openstack/identity/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || identity -// The v2 package contains acceptance tests for the Openstack Identity v2 service. - +// Package v2 contains acceptance tests for the Openstack Identity v2 service. package v2 diff --git a/internal/acceptance/openstack/identity/v3/pkg.go b/internal/acceptance/openstack/identity/v3/pkg.go index 22c792fdda..eb5cbe000c 100644 --- a/internal/acceptance/openstack/identity/v3/pkg.go +++ b/internal/acceptance/openstack/identity/v3/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || identity -// The v3 package contains acceptance tests for the Openstack Identity v3 service. - +// Package v3 contains acceptance tests for the Openstack Identity v3 service. package v3 diff --git a/internal/acceptance/openstack/image/v2/pkg.go b/internal/acceptance/openstack/image/v2/pkg.go index a61c1825b4..6e65e201d2 100644 --- a/internal/acceptance/openstack/image/v2/pkg.go +++ b/internal/acceptance/openstack/image/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || image -// The v2 package contains acceptance tests for the Openstack Image v2 service. - +// Package v2 contains acceptance tests for the Openstack Image v2 service. package v2 diff --git a/internal/acceptance/openstack/keymanager/v1/pkg.go b/internal/acceptance/openstack/keymanager/v1/pkg.go index 9839bc5094..f14cf0e0b2 100644 --- a/internal/acceptance/openstack/keymanager/v1/pkg.go +++ b/internal/acceptance/openstack/keymanager/v1/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || keymanager -// The v1 package contains acceptance tests for the Openstack Keymanager v1 service. - +// Package v1 contains acceptance tests for the Openstack Keymanager v1 service. package v1 diff --git a/internal/acceptance/openstack/loadbalancer/v2/pkg.go b/internal/acceptance/openstack/loadbalancer/v2/pkg.go index 790c5b4737..399d8370c3 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/pkg.go +++ b/internal/acceptance/openstack/loadbalancer/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || loadbalancer -// The v2 package contains acceptance tests for the Openstack Loadbalancer v2 service. - +// Package v2 contains acceptance tests for the Openstack Loadbalancer v2 service. package v2 diff --git a/internal/acceptance/openstack/messaging/v2/pkg.go b/internal/acceptance/openstack/messaging/v2/pkg.go index be815df451..cedee64f3f 100644 --- a/internal/acceptance/openstack/messaging/v2/pkg.go +++ b/internal/acceptance/openstack/messaging/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || messaging -// The v2 package contains acceptance tests for the Openstack Messaging v2 service. - +// Package v2 contains acceptance tests for the Openstack Messaging v2 service. package v2 diff --git a/internal/acceptance/openstack/networking/v2/pkg.go b/internal/acceptance/openstack/networking/v2/pkg.go index 01ac7a87bb..a8250f09ff 100644 --- a/internal/acceptance/openstack/networking/v2/pkg.go +++ b/internal/acceptance/openstack/networking/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || networking -// The v2 package contains acceptance tests for the Openstack Networking v2 service. - +// Package v2 contains acceptance tests for the Openstack Networking v2 service. package v2 diff --git a/internal/acceptance/openstack/objectstorage/v1/pkg.go b/internal/acceptance/openstack/objectstorage/v1/pkg.go index d8a67e553c..4060468e8b 100644 --- a/internal/acceptance/openstack/objectstorage/v1/pkg.go +++ b/internal/acceptance/openstack/objectstorage/v1/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || objectstorage -// The v1 package contains acceptance tests for the Openstack Object Storage v1 service. - +// Package v1 contains acceptance tests for the Openstack Object Storage v1 service. package v1 diff --git a/internal/acceptance/openstack/orchestration/v1/pkg.go b/internal/acceptance/openstack/orchestration/v1/pkg.go index e825736c0a..2772c2e9ee 100644 --- a/internal/acceptance/openstack/orchestration/v1/pkg.go +++ b/internal/acceptance/openstack/orchestration/v1/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || orchestration -// The v1 package contains acceptance tests for the Openstack Orchestration v1 service. - +// Package v1 contains acceptance tests for the Openstack Orchestration v1 service. package v1 diff --git a/internal/acceptance/openstack/pkg.go b/internal/acceptance/openstack/pkg.go index 41589adc75..6a60eddef9 100644 --- a/internal/acceptance/openstack/pkg.go +++ b/internal/acceptance/openstack/pkg.go @@ -1,3 +1,4 @@ //go:build acceptance +// Package openstack contains acceptance tests for various Openstack services. package openstack diff --git a/internal/acceptance/openstack/placement/v1/pkg.go b/internal/acceptance/openstack/placement/v1/pkg.go index 316618153a..f2ce217707 100644 --- a/internal/acceptance/openstack/placement/v1/pkg.go +++ b/internal/acceptance/openstack/placement/v1/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || placement -// The v1 package contains acceptance tests for the Openstack Placment v1 service. - +// Package v1 contains acceptance tests for the Openstack Placment v1 service. package v1 diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go b/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go index 6c94d20ae8..ebf1e4125b 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || sharedfilesystems -// The v2 package contains acceptance tests for the Openstack Shared File Systems v2 service. - +// Package v2 contains acceptance tests for the Openstack Shared File Systems v2 service. package v2 diff --git a/internal/acceptance/openstack/workflow/v2/pkg.go b/internal/acceptance/openstack/workflow/v2/pkg.go index 716529fd0d..ccfd74b979 100644 --- a/internal/acceptance/openstack/workflow/v2/pkg.go +++ b/internal/acceptance/openstack/workflow/v2/pkg.go @@ -1,5 +1,4 @@ //go:build acceptance || workflow -// The v2 package contains acceptance tests for the Openstack Workflow v2 service. - +// Package v2 contains acceptance tests for the Openstack Workflow v2 service. package v2 diff --git a/internal/acceptance/tools/pkg.go b/internal/acceptance/tools/pkg.go index f7eca1298a..9bed54e999 100644 --- a/internal/acceptance/tools/pkg.go +++ b/internal/acceptance/tools/pkg.go @@ -1 +1,4 @@ +//go:build acceptance || compute + +// Package tools contains helpers for acceptance tests. package tools From bb73e81f5b8f4e01229a23bc0b13cffc4e1f8c56 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 23 May 2024 15:19:39 +0100 Subject: [PATCH 1886/2296] acceptance: Add additional logs for neutron BGP tests To help us diagnose some regular CI failures we're seeing. Signed-off-by: Stephen Finucane --- .../v2/extensions/agents/agents_test.go | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index c55529b0f5..d799817aca 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -140,6 +140,23 @@ func TestBGPAgentRUD(t *testing.T) { }, timeout) th.AssertNoErr(t, err) + // List the BGP speakers on the first agent + bgpAgent, err := agents.Get(context.TODO(), client, bgpAgents[0].ID).Extract() + th.AssertNoErr(t, err) + agentConf := bgpAgent.Configurations + numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) + t.Logf("Agent %s has %d speaker(s)", bgpAgents[0].ID, numOfSpeakers) + + pages, err = agents.ListBGPSpeakers(client, bgpAgents[0].ID).AllPages(context.TODO()) + th.AssertNoErr(t, err) + allSpeakers, err := agents.ExtractBGPSpeakers(pages) + th.AssertNoErr(t, err) + out := "Speakers:" + for _, speaker := range allSpeakers { + out += " " + speaker.ID + } + t.Log(out) + // Remove the BGP Speaker from the first agent err = agents.RemoveBGPSpeaker(context.TODO(), client, bgpAgents[0].ID, bgpSpeaker.ID).ExtractErr() th.AssertNoErr(t, err) @@ -150,7 +167,7 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) - t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers) + t.Logf("Agent %s has %d speaker(s)", bgpAgent.ID, numOfSpeakers) return numOfSpeakers == 0, nil }, timeout) th.AssertNoErr(t, err) @@ -158,7 +175,7 @@ func TestBGPAgentRUD(t *testing.T) { // Remove all BGP Speakers from the agent pages, err = agents.ListBGPSpeakers(client, bgpAgents[0].ID).AllPages(context.TODO()) th.AssertNoErr(t, err) - allSpeakers, err := agents.ExtractBGPSpeakers(pages) + allSpeakers, err = agents.ExtractBGPSpeakers(pages) th.AssertNoErr(t, err) for _, speaker := range allSpeakers { th.AssertNoErr(t, agents.RemoveBGPSpeaker(context.TODO(), client, bgpAgents[0].ID, speaker.ID).ExtractErr()) @@ -178,7 +195,7 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) - t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers) + t.Logf("Agent %s has %d speaker(s)", bgpAgent.ID, numOfSpeakers) return 1 == numOfSpeakers, nil }, timeout) th.AssertNoErr(t, err) @@ -193,7 +210,7 @@ func TestBGPAgentRUD(t *testing.T) { th.AssertNoErr(t, err) agentConf := bgpAgent.Configurations numOfSpeakers := int(agentConf["bgp_speakers"].(float64)) - t.Logf("Agent %s has %d speakers", bgpAgent.ID, numOfSpeakers) + t.Logf("Agent %s has %d speaker(s)", bgpAgent.ID, numOfSpeakers) return 0 == numOfSpeakers, nil }, timeout) th.AssertNoErr(t, err) From 821ccf4a0a39ff506f1b46f4e97a7ccd99dd88c8 Mon Sep 17 00:00:00 2001 From: nikParasyr Date: Thu, 23 May 2024 18:27:55 +0200 Subject: [PATCH 1887/2296] Add more L7 rule types Add support for SSL_CONN_HAS_CERT, SSL_VERIFY_RESULT and SSL_DN_FIELD type for L7 rules. --- openstack/loadbalancer/v2/l7policies/requests.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openstack/loadbalancer/v2/l7policies/requests.go b/openstack/loadbalancer/v2/l7policies/requests.go index b82d030b0d..62a4f179ee 100644 --- a/openstack/loadbalancer/v2/l7policies/requests.go +++ b/openstack/loadbalancer/v2/l7policies/requests.go @@ -23,11 +23,14 @@ const ( ActionRedirectToURL Action = "REDIRECT_TO_URL" ActionReject Action = "REJECT" - TypeCookie RuleType = "COOKIE" - TypeFileType RuleType = "FILE_TYPE" - TypeHeader RuleType = "HEADER" - TypeHostName RuleType = "HOST_NAME" - TypePath RuleType = "PATH" + TypeCookie RuleType = "COOKIE" + TypeFileType RuleType = "FILE_TYPE" + TypeHeader RuleType = "HEADER" + TypeHostName RuleType = "HOST_NAME" + TypePath RuleType = "PATH" + TypeSSLConnHasCert RuleType = "SSL_CONN_HAS_CERT" + TypeSSLVerifyResult RuleType = "SSL_VERIFY_RESULT" + TypeSSLDNField RuleType = "SSL_DN_FIELD" CompareTypeContains CompareType = "CONTAINS" CompareTypeEndWith CompareType = "ENDS_WITH" From 7666e2fe87b21840ce8d541e29cc149523a0b0e0 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 27 May 2024 15:44:34 +0200 Subject: [PATCH 1888/2296] Prepare v2.0.0 --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/unit.yml | 2 +- CHANGELOG.md | 35 ++++++++++++++++++++++++--- docs/MIGRATING.md | 2 +- go.mod | 2 +- provider_client.go | 2 +- 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f0e26e7713..b9492ca444 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22" - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 4f12f87442..11fddb5f68 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: go-version: - - "1.21" + - "1.22.3" - "1" steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3495030478..1d7dfbc469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -## v2.0.0-beta.1 +## v2.0.0 (2024-05-27) -BREAKING CHANGES: +MAIN BREAKING CHANGES: -* **The minimum required Go version is now v1.21.6.** +* **Gophercloud now requires Go v1.22.** * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Gophercloud now escapes container and object names in all `objects` and `containers` functions. If you were previously escaping names (with, for example: `url.PathEscape` or `url.QueryEscape`), then you should REMOVE that and pass the intended names to Gophercloud directly. * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) The `containers.ListOpts#Full` and `objects.ListOpts#Full` properties are REMOVED from the Gophercloud API. The reason for that is: plaintext listing is unfixably wrong and won't handle special characters reliably (i.e. `\n`). * [GH-2821](https://github.com/gophercloud/gophercloud/pull/2821) Empty container names, container names containing a slash (`/`), and empty object names are now rejected in Gophercloud before any call to Swift. @@ -16,6 +16,7 @@ BREAKING CHANGES: * [GH-2665](https://github.com/gophercloud/gophercloud/pull/2665) Cinder: Remove multiatttach request parameter * [GH-2936](https://github.com/gophercloud/gophercloud/pull/2936) Make Gophercloud context-aware: all function signatures triggering an HTTP call now accept a context.Context for tracing and cancellation * [GH-2970](https://github.com/gophercloud/gophercloud/pull/2970) Remove context from the Provider client +* [GH-2904](https://github.com/gophercloud/gophercloud/pull/2904) Remove error code types New features and improvements: @@ -40,7 +41,33 @@ New features and improvements: * [GH-2883](https://github.com/gophercloud/gophercloud/pull/2883) Context-aware methods to ProviderClient and ServiceClient * [GH-2892](https://github.com/gophercloud/gophercloud/pull/2892) Authenticate with a clouds.yaml -## v1.9.0 (2024-02-02) +## v1.12.0 (2024-05-27) + +* [GH-2979](https://github.com/gophercloud/gophercloud/pull/2979) [v1] CI backports +* [GH-2985](https://github.com/gophercloud/gophercloud/pull/2985) [v1] baremetal: fix handling of the "fields" query argument +* [GH-2989](https://github.com/gophercloud/gophercloud/pull/2989) [v1] [CI] Fix portbiding tests +* [GH-2992](https://github.com/gophercloud/gophercloud/pull/2992) [v1] [CI] Fix portbiding tests +* [GH-2993](https://github.com/gophercloud/gophercloud/pull/2993) [v1] build(deps): bump EmilienM/devstack-action from 0.14 to 0.15 +* [GH-2998](https://github.com/gophercloud/gophercloud/pull/2998) [v1] testhelper: mark all helpers with t.Helper +* [GH-3043](https://github.com/gophercloud/gophercloud/pull/3043) [v1] CI: remove Zed from testing coverage + +## v1.11.0 (2024-03-07) + +This version reverts the inclusion of Context in the v1 branch. This inclusion +didn't add much value because no packages were using it; on the other hand, it +introduced a bug when using the Context property of the Provider client. + +## v1.10.0 (2024-02-27) **RETRACTED**: see https://github.com/gophercloud/gophercloud/issues/2969 + +* [GH-2893](https://github.com/gophercloud/gophercloud/pull/2893) [v1] authentication: Add WithContext functions +* [GH-2894](https://github.com/gophercloud/gophercloud/pull/2894) [v1] pager: Add WithContext functions +* [GH-2899](https://github.com/gophercloud/gophercloud/pull/2899) [v1] Authenticate with a clouds.yaml +* [GH-2917](https://github.com/gophercloud/gophercloud/pull/2917) [v1] Add ParseOption type to made clouds.Parse() more usable for optional With* funcs +* [GH-2924](https://github.com/gophercloud/gophercloud/pull/2924) [v1] build(deps): bump EmilienM/devstack-action from 0.11 to 0.14 +* [GH-2933](https://github.com/gophercloud/gophercloud/pull/2933) [v1] Fix AllowReauth reauthentication +* [GH-2950](https://github.com/gophercloud/gophercloud/pull/2950) [v1] compute: Use volumeID, not attachmentID for volume attachments + +## v1.9.0 (2024-02-02) **RETRACTED**: see https://github.com/gophercloud/gophercloud/issues/2969 New features and improvements: diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 565e8ca234..0f804c9157 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -23,7 +23,7 @@ import ( ### Go version -The minimum go version for Gophercloud v2 is now v1.21.6. +The minimum go version for Gophercloud v2 is now v1.22. ### Context-awareness diff --git a/go.mod b/go.mod index 629086e8d3..d2714ef153 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gophercloud/gophercloud/v2 -go 1.21 +go 1.22 require ( golang.org/x/crypto v0.23.0 diff --git a/provider_client.go b/provider_client.go index 556a4bc567..f8c4928cbd 100644 --- a/provider_client.go +++ b/provider_client.go @@ -13,7 +13,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v2-unreleased" + DefaultUserAgent = "gophercloud/v2.0.0" DefaultMaxBackoffRetries = 60 ) From 7b6319ab499468d8e4aa441386791ed073523eff Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Thu, 22 Jun 2023 11:31:18 -0500 Subject: [PATCH 1889/2296] identity: use AuthOptionsBuilder for v2 auth Rather than creating a tokens2.AuthOptions inside the authentication flow for identity v2, allow any structure that implements the tokens2.AuthBuilderOptions interface to be used. The interface is modified to add a CanReauth function like the v3 version has but provides a default implementation. The gophercloud.AuthOptions already implements this interface so there is no change to its API. This will allow an out of tree identity v2 auth mechanism to be implemented, just like the v3. fixes #2651. ref #1330. --- openstack/client.go | 30 ++++++++++-------------- openstack/client_test.go | 5 ++++ openstack/identity/v2/tokens/requests.go | 5 ++++ 3 files changed, 22 insertions(+), 18 deletions(-) create mode 100644 openstack/client_test.go diff --git a/openstack/client.go b/openstack/client.go index f68fe14417..43b569d3b4 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -99,7 +99,7 @@ func Authenticate(ctx context.Context, client *gophercloud.ProviderClient, optio switch chosen.ID { case v2: - return v2auth(ctx, client, endpoint, options, gophercloud.EndpointOpts{}) + return v2auth(ctx, client, endpoint, &options, gophercloud.EndpointOpts{}) case v3: return v3auth(ctx, client, endpoint, &options, gophercloud.EndpointOpts{}) default: @@ -109,11 +109,17 @@ func Authenticate(ctx context.Context, client *gophercloud.ProviderClient, optio } // AuthenticateV2 explicitly authenticates against the identity v2 endpoint. -func AuthenticateV2(ctx context.Context, client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { +func AuthenticateV2(ctx context.Context, client *gophercloud.ProviderClient, options tokens2.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { return v2auth(ctx, client, "", options, eo) } -func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { +type v2TokenNoReauth struct { + tokens2.AuthOptionsBuilder +} + +func (v2TokenNoReauth) CanReauth() bool { return false } + +func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint string, options tokens2.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { v2Client, err := NewIdentityV2(client, eo) if err != nil { return err @@ -123,17 +129,7 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st v2Client.Endpoint = endpoint } - v2Opts := tokens2.AuthOptions{ - IdentityEndpoint: options.IdentityEndpoint, - Username: options.Username, - Password: options.Password, - TenantID: options.TenantID, - TenantName: options.TenantName, - AllowReauth: options.AllowReauth, - TokenID: options.TokenID, - } - - result := tokens2.Create(ctx, v2Client, v2Opts) + result := tokens2.Create(ctx, v2Client, options) err = client.SetTokenAndAuthResult(result) if err != nil { @@ -145,7 +141,7 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st return err } - if options.AllowReauth { + if options.CanReauth() { // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, // this should retry authentication only once @@ -156,10 +152,8 @@ func v2auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st if err != nil { return err } - tao := options - tao.AllowReauth = false client.ReauthFunc = func(ctx context.Context) error { - err := v2auth(ctx, &tac, endpoint, tao, eo) + err := v2auth(ctx, &tac, endpoint, &v2TokenNoReauth{options}, eo) if err != nil { return err } diff --git a/openstack/client_test.go b/openstack/client_test.go new file mode 100644 index 0000000000..257b92174e --- /dev/null +++ b/openstack/client_test.go @@ -0,0 +1,5 @@ +package openstack + +import tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" + +var _ tokens2.AuthOptionsBuilder = &v2TokenNoReauth{} diff --git a/openstack/identity/v2/tokens/requests.go b/openstack/identity/v2/tokens/requests.go index 59304fb246..5afa8fd26c 100644 --- a/openstack/identity/v2/tokens/requests.go +++ b/openstack/identity/v2/tokens/requests.go @@ -42,6 +42,7 @@ type AuthOptionsBuilder interface { // ToTokenCreateMap assembles the Create request body, returning an error // if parameters are missing or inconsistent. ToTokenV2CreateMap() (map[string]any, error) + CanReauth() bool } // AuthOptions are the valid options for Openstack Identity v2 authentication. @@ -81,6 +82,10 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]any, error) { return b, nil } +func (opts AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + // Create authenticates to the identity service and attempts to acquire a Token. // Generally, rather than interact with this call directly, end users should // call openstack.AuthenticatedClient(), which abstracts all of the gory details From a73ddcd68e76110602f3621dcad7731106ccc58f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 28 May 2024 13:53:46 +0100 Subject: [PATCH 1890/2296] script/stackenv: Don't set 'OS_SYSTEM_SCOPE' until necessary We were setting OS_SYSTEM_SCOPE in some of the functional ironic tests. This was resulting in the following errors when attempting to create resources: Authentication cannot be scoped to multiple targets. Pick one of: project, domain, trust, system or unscoped Unfortunately, because we weren't setting 'errexit' ('set -e') this wasn't being noticed, but we can "correct" things now. I say "correct" because, adding to the misfortune, how we fix this is technically incorrect - you should have project-scoped configuration *or* system-scoped, not both - but since we don't yet have the infrastructure to switch between sets of auth info on a test-by-test basis, we need to do a Bad Thing. Hopefully this will be a short-lived thing. Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 16 +++++++------- script/stackenv | 24 ++++++++++++++++----- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 42c3866925..ddaeb1fd14 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -15,24 +15,24 @@ jobs: openstack_version: "master" ubuntu_version: "22.04" # NOTE(dtantsur): this forces running tests with a system scope - # token, which is required for certain actions. Use "all" on - # versions starting with 2024.1. - os_system_scope: "all" + # token, which is required for certain actions. Use it on versions + # starting with 2024.1. + use_system_scope: true additional_services: "openstack-cli-server" - name: "caracal" openstack_version: "stable/2024.1" ubuntu_version: "22.04" - os_system_scope: "all" + use_system_scope: true additional_services: "" - name: "bobcat" openstack_version: "stable/2023.2" ubuntu_version: "22.04" - os_system_scope: "" + use_system_scope: false additional_services: "" - name: "antelope" openstack_version: "stable/2023.1" ubuntu_version: "22.04" - os_system_scope: "" + use_system_scope: false additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests @@ -98,8 +98,8 @@ jobs: DEVSTACK_PATH: ${{ github.workspace }}/devstack PACKAGE: "./internal/acceptance/openstack/baremetal/..." OS_BRANCH: ${{ matrix.openstack_version }} - # TODO(dtantsur): default to "all" when no longer supporting versions before 2024.1 - OS_SYSTEM_SCOPE: ${{ matrix.os_system_scope }} + # TODO(dtantsur): default to true when no longer supporting versions before 2024.1 + USE_SYSTEM_SCOPE: ${{ matrix.use_system_scope }} - name: Generate logs on failure run: ./script/collectlogs if: failure() diff --git a/script/stackenv b/script/stackenv index 86d67ef82c..a608fc519f 100644 --- a/script/stackenv +++ b/script/stackenv @@ -7,6 +7,25 @@ DEVSTACK_PATH=${DEVSTACK_PATH:-/opt/stack/new/devstack} pushd $DEVSTACK_PATH source openrc admin admin + +if [[ "${USE_SYSTEM_SCOPE:-}" == "true" ]]; then + # use system-scoped tokens + echo export OS_SYSTEM_SCOPE=all >> openrc +fi +# TODO: This should only be set when using project-scoped tokens (and we should +# unsetting things like OS_PROJECT_NAME and OS_PROJECT_DOMAIN_ID when not using +# these) but our tests require both which means we need to export both. This +# causes OSC to (correctly) fail since keystoneauth (which is handling +# authentication for OSC and most other clients) can't tell if we want project- +# or system-scoped tokens. As such, post running this script, the 'openrc' file +# will no longer be usable with OSC or other clients. +# +# The long-term fix for this likely involves a mechanism to switch between +# different sets of auth info on a test-by-test basis. Achieving this almost +# certainly means switching our tests to use clouds.yaml with well-known cloud +# names rather than openrc file currently used. +echo export OS_DOMAIN_ID=default >> openrc + 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 @@ -28,12 +47,7 @@ echo export OS_EXTGW_ID="$_EXTGW_ID" >> openrc 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 -if [[ "${OS_SYSTEM_SCOPE:-}" == "all" ]]; then - unset PROJECT_NAME - unset TENANT_NAME -fi popd From b28bc4e5ac62c194f6402e362290cc9db07ed87f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 28 May 2024 14:10:53 +0100 Subject: [PATCH 1891/2296] script/stackenv: Run through shellcheck, enable verbosity Signed-off-by: Stephen Finucane --- script/stackenv | 59 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/script/stackenv b/script/stackenv index a608fc519f..6e2a32ebeb 100644 --- a/script/stackenv +++ b/script/stackenv @@ -4,9 +4,16 @@ # environment variables. This env is used for the CI jobs and you might need # to modify this according to your setup +set -euxo pipefail + DEVSTACK_PATH=${DEVSTACK_PATH:-/opt/stack/new/devstack} -pushd $DEVSTACK_PATH + +pushd "$DEVSTACK_PATH" + +set +u +# shellcheck disable=SC1091 source openrc admin admin +set -u if [[ "${USE_SYSTEM_SCOPE:-}" == "true" ]]; then # use system-scoped tokens @@ -26,28 +33,46 @@ fi # names rather than openrc file currently used. echo export OS_DOMAIN_ID=default >> openrc -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 +_FLAVOR_ID=99 +_FLAVOR_ALT_ID=98 +openstack flavor create m1.acctest --id "$_FLAVOR_ID" --ram 512 --disk 10 --vcpu 1 --ephemeral 10 +openstack flavor create m1.resize --id "$_FLAVOR_ALT_ID" --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) _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) -if [ -z "$_MAGNUM_IMAGE_ID" ]; then +_IMAGE_ID=$(echo "$_IMAGE" | awk -F\| '{print $2}' | tr -d ' ') +_IMAGE_NAME=$(echo "$_IMAGE" | awk -F\| '{print $3}' | tr -d ' ') + +cat >> "openrc" <> "openrc" <> openrc -echo export OS_IMAGE_ID="$_IMAGE_ID" >> openrc -echo export OS_NETWORK_ID="$_NETWORK_ID" >> openrc -echo export OS_SUBNET_ID="$_SUBNET_ID" >> openrc -echo export OS_EXTGW_ID="$_EXTGW_ID" >> openrc -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_MAGNUM_IMAGE_ID="$_MAGNUM_IMAGE_ID" >> openrc -echo export OS_MAGNUM_KEYPAIR=magnum >> openrc + +set +u +# shellcheck disable=SC1091 source openrc admin admin +set -u + popd From da6fdaaec7d895b4cd79bd3511f2d401c7ffd376 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:49:50 +0000 Subject: [PATCH 1892/2296] build(deps): bump golang.org/x/crypto from 0.23.0 to 0.24.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.23.0 to 0.24.0. - [Commits](https://github.com/golang/crypto/compare/v0.23.0...v0.24.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index d2714ef153..0f0d601f78 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.22 require ( - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.24.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.20.0 // indirect +require golang.org/x/sys v0.21.0 // indirect diff --git a/go.sum b/go.sum index 9ff4e2353f..e5ba5798aa 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= 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 b80e37de90c2ee4b29a79fc253f91991bdf6b242 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 5 Jun 2024 13:10:47 +0100 Subject: [PATCH 1893/2296] docs: Add newline before code blocks, lists Signed-off-by: Stephen Finucane --- docs/MIGRATING.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 0f804c9157..4f493ee666 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -29,14 +29,16 @@ The minimum go version for Gophercloud v2 is now v1.22. Gophercloud is now context aware, for tracing and cancellation. All function signatures triggering an HTTP call now take a `context.Context` as their first -argument. +argument. While you previously called: + ```go myServer, err := servers.Get(client, server.ID) ``` You now need to pass it a context, for example: + ```go ctx := context.TODO() myServer, err := servers.Get(ctx, client, server.ID) @@ -53,6 +55,7 @@ err = attachments.WaitForStatus(client, attachment.ID, "attached", 60) ``` Must be changed to use a context with timeout. For example: + ```go ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second) defer cancel() @@ -68,7 +71,8 @@ rather than a slice of `BatchUpdateMemberOpts`. `blockstorage/v3/volumes/CreateOpts.Multiattach` is removed. Use a volume type with `multiattach` capability instead. -The following structs are no longer comparable due to the addition of a non comparable field: +The following structs are no longer comparable due to the addition of a non-comparable field: + - `compute/v2/flavors/Flavor` - `loadbalancer/v2/l7policies/CreateRuleOpts` - `loadbalancer/v2/l7policies/UpdateOpts` @@ -103,6 +107,7 @@ includes `BootInfoType`, `CPUType`, `LLDPTLVType`, `InterfaceType`, `NUMARAM`. Additionally, a few of these types were renamed in the process: + - `ExtraHardwareDataType` became `ExtraDataType` - `ExtraHardwareData` became `ExtraDataItem` - `ExtraHardwareDataSection` became `ExtraHardwareDataSection` From 58b30e5c47ddee813dc4f21c7dfaffd3a251bdb2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 5 Jun 2024 13:15:10 +0100 Subject: [PATCH 1894/2296] MIGRATING: Note removal of Senlin support Signed-off-by: Stephen Finucane Closes: #3061 --- docs/MIGRATING.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 4f493ee666..0f681b4317 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -143,6 +143,7 @@ Support for services that are no longer supported upstream has been removed. Users that still rely on theses old services should continue using Gophercloud v1. - Cinder (Blockstorage) v1 -- Neutron (Networking) LBaaS and LBaaS v2 extensions. They have been replaced by Octavia. -- Neutron (Networking) FWaaS extension. -- Poppy (CDNaaS). +- Neutron (Networking) LBaaS and LBaaS v2 extensions +- Neutron (Networking) FWaaS extension +- Poppy (CDNaaS) service +- Senlin (Clustering) service From a71e6b84ba7f2c4c50655070bce525600e1c5e98 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 5 Jun 2024 14:16:39 +0100 Subject: [PATCH 1895/2296] docs: Document removal of `extensions` modules Signed-off-by: Stephen Finucane Closes: #3063 --- docs/MIGRATING.md | 260 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 0f681b4317..b86579c1c0 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -63,6 +63,266 @@ defer cancel() err = attachments.WaitForStatus(ctx, client, attachment.ID, "attached") ``` +### Removal of `extensions` modules + +A number of services previously supported API extensions but have long since +switched to using microversions to allow API changes. This is now reflected +in Gophercloud v2 and the contents of the follow modules have been largely +migrated: + +- `openstack/blockstorage/extensions` +- `openstack/compute/v2/extensions` +- `openstack/identity/v2/extensions` +- `openstack/identity/v3/extensions` + +The replacement for these depends on the type of the former extension. For +extensions that added wholly new APIs, these APIs have been moved into the +main module for the corresponding service. These are: + +- `openstack/blockstorage/extensions/availabilityzones` + + Moved to `openstack/blockstorage/v2/availabilityzones` and + `openstack/blockstorage/v3/availabilityzones`. + +- `openstack/blockstorage/extensions/backups` + + Moved to `openstack/blockstorage/v2/backups` and + `openstack/blockstorage/v3/backups`. + +- `openstack/blockstorage/extensions/limits` + + Moved to `openstack/blockstorage/v2/limits` and + `openstack/blockstorage/v3/limits`. + +- `openstack/blockstorage/extensions/quotasets` + + Moved to `openstack/blockstorage/v2/quotasets` and + `openstack/blockstorage/v3/quotasets`. + +- `openstack/blockstorage/extensions/schedulerstats` + + Moved to `openstack/blockstorage/v2/schedulerstats` and + `openstack/blockstorage/v3/schedulerstats`. + +- `openstack/blockstorage/extensions/services` + + Moved to `openstack/blockstorage/v2/services` and + `openstack/blockstorage/v3/services`. + +- `openstack/blockstorage/extensions/volumetransfers` + + Moved to `openstack/blockstorage/v2/transfers` and + `openstack/blockstorage/v3/transfers`. + +- `openstack/compute/v2/extensions/aggregates` + + Moved to `openstack/compute/v2/aggregates`. + +- `openstack/compute/v2/extensions/attachinterfaces` + + Moved to `openstack/compute/v2/attachinterfaces`. + +- `openstack/compute/v2/extensions/diagnostics` + + Moved to `openstack/compute/v2/diagnostics`. + +- `openstack/compute/v2/extensions/hypervisors` + + Moved to `openstack/compute/v2/hypervisors`. + +- `openstack/compute/v2/extensions/instanceactions` + + Moved to `openstack/compute/v2/instanceactions`. + +- `openstack/compute/v2/extensions/keypairs` + + Moved to `openstack/compute/v2/keypairs`. + +- `openstack/compute/v2/extensions/quotasets` + + Moved to `openstack/compute/v2/quotasets`. + +- `openstack/compute/v2/extensions/remoteconsoles` + + Moved to `openstack/compute/v2/remoteconsoles`. + +- `openstack/compute/v2/extensions/secgroups` + + Moved to `openstack/compute/v2/secgroups`. + +- `openstack/compute/v2/extensions/servergroups` + + Moved to `openstack/compute/v2/servergroups`. + +- `openstack/compute/v2/extensions/services` + + Moved to `openstack/compute/v2/services`. + +- `openstack/compute/v2/extensions/tags` + + Moved to `openstack/compute/v2/tags`. + +- `openstack/compute/v2/extensions/usage` + + Moved to `openstack/compute/v2/usage`. + +- `openstack/compute/v2/extensions/volumeattach` + + Moved to `openstack/compute/v2/volumeattach`. + +- `openstack/identity/v2/extensions/admin/roles` + + Moved to `openstack/identity/v2/roles`. + +- `openstack/identity/v3/extensions/ec2credentials` + + Moved to `openstack/identity/v3/ec2credentials`. + +- `openstack/identity/v3/extensions/ec2tokens` + + Moved to `openstack/identity/v3/ec2tokens`. + +- `openstack/identity/v3/extensions/federation` + + Moved to `openstack/identity/v3/federation` + +- `openstack/identity/v3/extensions/oauth1`. + + Moved to `openstack/identity/v3/oauth1` + +- `openstack/identity/v3/extensions/projectendpoints` + + Moved to `openstack/identity/v3/projectendpoints`. + +For extensions that modified existing APIs, these modifications have been +folded into the modified APIs. These are: + +- `openstack/blockstorage/extensions/schedulerhints` + + `SchedulerHintOpts` has been renamed to `SchedulerHints` and moved to + `openstack/blockstorage/v2/volumes` and `openstack/blockstorage/v3/volumes`. + This is now a required argument of `volumes.Create` for both modules. + +- `openstack/blockstorage/extensions/volumeactions` + + All functions and supporting structs and interfaces have been moved to + `openstack/blockstorage/v2/volumes` and `openstack/blockstorage/v3/volumes`. + +- `openstack/blockstorage/extensions/volumehost` + + The `VolumeHostExt` struct has been removed and a `Host` field added to the + `Volume` struct in `openstack/blockstorage/v2/volumes` and + `openstack/blockstorage/v3/volumes`. + +- `openstack/blockstorage/extensions/volumetenants` + + The `VolumeTenantExt` struct has been removed and a `TenantID` field added to + the `Volume` struct in `openstack/blockstorage/v2/volumes` and + `openstack/blockstorage/v3/volumes`. + +- `openstack/compute/v2/extensions/bootfromvolume` + + The `CreateOptsExt` struct has been removed and a `BlockDevice` field added + to the `CreateOpts` struct in `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/diskconfig` + + The `CreateOptsExt` struct has been removed and a `DiskConfig` field added to + the `CreateOpts` struct in `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/evacuate` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/extendedserverattributes` + + The `ServerAttributesExt` struct has been removed and all fields added to the + `Server` struct in `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/extendedstatus` + + The `ServerExtendedStatusExt` struct has been removed and all fields added to + the `Server` struct in `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/injectnetworkinfo` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/lockunlock` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/migrate` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/pauseunpause` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/rescueunrescue` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/resetnetwork` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/resetstate` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/schedulerhints` + + `SchedulerHintOpts` has been moved to `openstack/compute/v2/servers` and + renamed to `SchedulerHints`. This is now a required argument of + `servers.Create`. + +- `openstack/compute/v2/extensions/serverusage` + + The `serverusage` struct has been removed and all fields added to the + `Server` struct in `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/shelveunshelve` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/startstop` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +- `openstack/compute/v2/extensions/suspendresume` + + All functions and supporting structs and interfaces have been moved to + `openstack/compute/v2/servers`. + +Finally, for extensions that added new APIs *and* modified existing APIs, the +new APIs are moved into the main module of the corresponding service while the +modifications are folded into the modified APIs. These are: + +- `openstack/compute/v2/extensions/availabilityzones` + + The `ServerAvailabilityZoneExt` struct has been removed and a + `AvailabilityZone` field added to the `Server` struct in + `openstack/compute/v2/servers`. Everything else is moved moved to + `openstack/compute/v2/availabilityzones`. + +- `openstack/identity/v3/extensions/trusts` + + The `AuthOptsExt` struct has been removed and a `TrustID` field added to the + `Scope` struct in `openstack/identity/v3/tokens`. Everything else is moved + moved to `openstack/identity/v3/trusts`. + ### Type changes `loadbalancer/v2/pools/CreateOpts.Members` is now a slice of `CreateMemberOpts` From d8924d4c4913b7c9f9c6fe0d4ed331f183b3dfcb Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 5 Jun 2024 15:53:27 +0200 Subject: [PATCH 1896/2296] Accept interfaces wherever possible Use generics to allow passing slices of both bare structs and interfaces. --- openstack/containerinfra/v1/clusters/requests.go | 2 +- openstack/containerinfra/v1/clustertemplates/requests.go | 2 +- openstack/containerinfra/v1/nodegroups/requests.go | 2 +- openstack/loadbalancer/v2/pools/requests.go | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openstack/containerinfra/v1/clusters/requests.go b/openstack/containerinfra/v1/clusters/requests.go index 26a2ec16a9..ec9357a1c5 100644 --- a/openstack/containerinfra/v1/clusters/requests.go +++ b/openstack/containerinfra/v1/clusters/requests.go @@ -150,7 +150,7 @@ func (opts UpdateOpts) ToClustersUpdateMap() (map[string]any, error) { } // Update implements cluster updated request. -func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { +func Update[T UpdateOptsBuilder](ctx context.Context, client *gophercloud.ServiceClient, id string, opts []T) (r UpdateResult) { var o []map[string]any for _, opt := range opts { b, err := opt.ToClustersUpdateMap() diff --git a/openstack/containerinfra/v1/clustertemplates/requests.go b/openstack/containerinfra/v1/clustertemplates/requests.go index 3421317523..9a79d7b481 100644 --- a/openstack/containerinfra/v1/clustertemplates/requests.go +++ b/openstack/containerinfra/v1/clustertemplates/requests.go @@ -148,7 +148,7 @@ func (opts UpdateOpts) ToClusterTemplateUpdateMap() (map[string]any, error) { } // Update implements cluster updated request. -func Update(ctx context.Context, client *gophercloud.ServiceClient, id string, opts []UpdateOptsBuilder) (r UpdateResult) { +func Update[T UpdateOptsBuilder](ctx context.Context, client *gophercloud.ServiceClient, id string, opts []T) (r UpdateResult) { var o []map[string]any for _, opt := range opts { b, err := opt.ToClusterTemplateUpdateMap() diff --git a/openstack/containerinfra/v1/nodegroups/requests.go b/openstack/containerinfra/v1/nodegroups/requests.go index d2f4dc8789..14ea2ca4a9 100644 --- a/openstack/containerinfra/v1/nodegroups/requests.go +++ b/openstack/containerinfra/v1/nodegroups/requests.go @@ -143,7 +143,7 @@ func (opts UpdateOpts) ToResourceUpdateMap() (map[string]any, error) { // one UpdateOpts can be passed at a time. // Use the Extract method of the returned UpdateResult to extract the // updated node group from the result. -func Update(ctx context.Context, client *gophercloud.ServiceClient, clusterID string, nodeGroupID string, opts []UpdateOptsBuilder) (r UpdateResult) { +func Update[T UpdateOptsBuilder](ctx context.Context, client *gophercloud.ServiceClient, clusterID string, nodeGroupID string, opts []T) (r UpdateResult) { var o []map[string]any for _, opt := range opts { b, err := opt.ToResourceUpdateMap() diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 3683651744..452bfaddb9 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -138,7 +138,7 @@ type CreateOpts struct { // // This is only possible to use when creating a fully populated // Loadbalancer. - Monitor *monitors.CreateOpts `json:"healthmonitor,omitempty"` + Monitor monitors.CreateOptsBuilder `json:"healthmonitor,omitempty"` // Tags is a set of resource tags. New in version 2.5 Tags []string `json:"tags,omitempty"` @@ -476,7 +476,7 @@ func (opts BatchUpdateMemberOpts) ToBatchMemberUpdateMap() (map[string]any, erro } // BatchUpdateMembers updates the pool members in batch -func BatchUpdateMembers(ctx context.Context, c *gophercloud.ServiceClient, poolID string, opts []BatchUpdateMemberOpts) (r UpdateMembersResult) { +func BatchUpdateMembers[T BatchUpdateMemberOptsBuilder](ctx context.Context, c *gophercloud.ServiceClient, poolID string, opts []T) (r UpdateMembersResult) { members := []map[string]any{} for _, opt := range opts { b, err := opt.ToBatchMemberUpdateMap() From c5d4623599058d55263ec5301973a196294f99a2 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Wed, 26 Jun 2024 10:25:51 +0200 Subject: [PATCH 1897/2296] add migration docs for error handling changes --- docs/MIGRATING.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index b86579c1c0..8cfab4ec57 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -63,6 +63,29 @@ defer cancel() err = attachments.WaitForStatus(ctx, client, attachment.ID, "attached") ``` +### Error handling + +The error types for specific response codes (`ErrDefault400`, `ErrDefault401`, etc.) have been removed. +All unexpected response codes will now return `ErrUnexpectedResponseCode` instead. +For quickly checking whether a request resulted in a specific response code, use the new `ResponseCodeIs` function: + +```go +server, err := servers.Get(ctx, client, serverID).Extract() + +// before +if _, ok := err.(gophercloud.ErrDefault404); ok { + handleServerNotFound() +} + +// after +if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { + handleServerNotFound() +} +``` + +Furthermore, the error messages returned by ErrUnexpectedResponseCode now include less newlines than before. +If you match on error messages using regexes, please double-check your regexes. + ### Removal of `extensions` modules A number of services previously supported API extensions but have long since From 943bfe66e53e31ae44767a8e7492ea66959e7291 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 26 Jun 2024 14:33:43 +0200 Subject: [PATCH 1898/2296] migrating.md: Document utils error handling --- docs/MIGRATING.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 8cfab4ec57..b587401016 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -74,18 +74,34 @@ server, err := servers.Get(ctx, client, serverID).Extract() // before if _, ok := err.(gophercloud.ErrDefault404); ok { - handleServerNotFound() + handleServerNotFound() } // after if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { - handleServerNotFound() + handleServerNotFound() } ``` Furthermore, the error messages returned by ErrUnexpectedResponseCode now include less newlines than before. If you match on error messages using regexes, please double-check your regexes. +#### With gophercloud/utils + +If using the `utils` library, note that the `IDFromName` functions return +`ErrResourceNotFound` rather than `ErrUnexpectedResponseCode`. In that +scenario, type assertions for a "not found" error are still necessary: + +```Go +func IsNotFound(err error) bool { + if _, ok := err.(gophercloud.ErrResourceNotFound); ok { // <-- this + return true + } + + return gophercloud.ResponseCodeIs(err, http.StatusNotFound) +} +``` + ### Removal of `extensions` modules A number of services previously supported API extensions but have long since From 769baf3b66e7e06f50b36ee167c6a2497e9c22b0 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 26 Jun 2024 14:40:04 +0200 Subject: [PATCH 1899/2296] migrating.md: Document updating the utils package --- docs/MIGRATING.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 8cfab4ec57..8ab7bea287 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -21,6 +21,18 @@ import ( ) ``` +If using [gophercloud/utils](https://github.com/gophercloud/utils), you will +also need to update those imports: + +```diff +import ( +- "github.com/gophercloud/gophercloud" +- serverutils "github.com/gophercloud/utils/openstack/compute/v2/servers" ++ "github.com/gophercloud/gophercloud/v2" ++ serverutils "github.com/gophercloud/utils/v2/openstack/compute/v2/servers" +) +``` + ### Go version The minimum go version for Gophercloud v2 is now v1.22. From 2de734a1748df00868d4645b45b1fcb52df76570 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 2 Jul 2024 16:07:22 +0200 Subject: [PATCH 1900/2296] [tests]: increase timeout for the TestBGPAgentRUD test --- .../openstack/networking/v2/extensions/agents/agents_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go index d799817aca..37b5e39c3b 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/agents/agents_test.go @@ -97,7 +97,7 @@ func TestAgentsRUD(t *testing.T) { } func TestBGPAgentRUD(t *testing.T) { - timeout := 120 * time.Second + timeout := 15 * time.Minute clients.RequireAdmin(t) client, err := clients.NewNetworkV2Client() From f07b68b257145c62ac74295d388915b4c342b133 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 1 Jul 2024 15:43:56 +0200 Subject: [PATCH 1901/2296] [networking]: add BGP VPNs support --- .github/workflows/functional-networking.yaml | 1 + .../v2/extensions/bgpvpns/bgpvpns.go | 28 + .../v2/extensions/bgpvpns/bgpvpns_test.go | 247 +++++++++ .../networking/v2/extensions/bgpvpns/doc.go | 2 + .../networking/v2/extensions/bgpvpns/doc.go | 66 +++ .../v2/extensions/bgpvpns/requests.go | 478 ++++++++++++++++ .../v2/extensions/bgpvpns/results.go | 517 +++++++++++++++++ .../v2/extensions/bgpvpns/testing/doc.go | 2 + .../v2/extensions/bgpvpns/testing/fixture.go | 434 +++++++++++++++ .../bgpvpns/testing/requests_test.go | 518 ++++++++++++++++++ .../networking/v2/extensions/bgpvpns/urls.go | 140 +++++ 11 files changed, 2433 insertions(+) create mode 100644 internal/acceptance/openstack/networking/v2/extensions/bgpvpns/bgpvpns.go create mode 100644 internal/acceptance/openstack/networking/v2/extensions/bgpvpns/bgpvpns_test.go create mode 100644 internal/acceptance/openstack/networking/v2/extensions/bgpvpns/doc.go create mode 100644 openstack/networking/v2/extensions/bgpvpns/doc.go create mode 100644 openstack/networking/v2/extensions/bgpvpns/requests.go create mode 100644 openstack/networking/v2/extensions/bgpvpns/results.go create mode 100644 openstack/networking/v2/extensions/bgpvpns/testing/doc.go create mode 100644 openstack/networking/v2/extensions/bgpvpns/testing/fixture.go create mode 100644 openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/bgpvpns/urls.go diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index e83b5cbe78..feb896eb45 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -47,6 +47,7 @@ jobs: 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 }} + enable_plugin networking-bgpvpn https://github.com/openstack/networking-bgpvpn.git ${{ matrix.openstack_version }} Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords [[post-config|\$NEUTRON_CONF]] diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/bgpvpns.go b/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/bgpvpns.go new file mode 100644 index 0000000000..f8408bdfbd --- /dev/null +++ b/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/bgpvpns.go @@ -0,0 +1,28 @@ +package bgpvpns + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgpvpns" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func CreateBGPVPN(t *testing.T, client *gophercloud.ServiceClient) (*bgpvpns.BGPVPN, error) { + opts := bgpvpns.CreateOpts{ + Name: tools.RandomString("TESTACC-BGPVPN-", 10), + } + + t.Logf("Attempting to create BGP VPN: %s", opts.Name) + bgpVpn, err := bgpvpns.Create(context.TODO(), client, opts).Extract() + if err != nil { + return bgpVpn, err + } + + th.AssertEquals(t, bgpVpn.Name, opts.Name) + t.Logf("Successfully created BGP VPN") + tools.PrintResource(t, bgpVpn) + return bgpVpn, err +} diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/bgpvpns_test.go b/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/bgpvpns_test.go new file mode 100644 index 0000000000..01542d6ec3 --- /dev/null +++ b/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/bgpvpns_test.go @@ -0,0 +1,247 @@ +//go:build acceptance || networking || bgp || bgpvpns + +package bgpvpns + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2/extensions/layer3" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgpvpns" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestBGPVPNCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a BGP VPN + bgpVpnCreated, err := CreateBGPVPN(t, client) + th.AssertNoErr(t, err) + + // Get a BGP VPN + bgpVpnGot, err := bgpvpns.Get(context.TODO(), client, bgpVpnCreated.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, bgpVpnCreated.ID, bgpVpnGot.ID) + th.AssertEquals(t, bgpVpnCreated.Name, bgpVpnGot.Name) + + // Update a BGP VPN + newBGPVPNName := tools.RandomString("TESTACC-BGPVPN-", 10) + updateBGPOpts := bgpvpns.UpdateOpts{ + Name: &newBGPVPNName, + } + bgpVpnUpdated, err := bgpvpns.Update(context.TODO(), client, bgpVpnGot.ID, updateBGPOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, newBGPVPNName, bgpVpnUpdated.Name) + t.Logf("Update BGP VPN, renamed from %s to %s", bgpVpnGot.Name, bgpVpnUpdated.Name) + + // List all BGP VPNs + allPages, err := bgpvpns.List(client, bgpvpns.ListOpts{}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + allVPNs, err := bgpvpns.ExtractBGPVPNs(allPages) + th.AssertNoErr(t, err) + + t.Logf("Retrieved BGP VPNs") + tools.PrintResource(t, allVPNs) + th.AssertIntGreaterOrEqual(t, len(allVPNs), 1) + + // Delete a BGP VPN + t.Logf("Attempting to delete BGP VPN: %s", bgpVpnUpdated.Name) + err = bgpvpns.Delete(context.TODO(), client, bgpVpnUpdated.ID).ExtractErr() + th.AssertNoErr(t, err) + + _, err = bgpvpns.Get(context.TODO(), client, bgpVpnGot.ID).Extract() + th.AssertErr(t, err) + t.Logf("BGP VPN %s deleted", bgpVpnUpdated.Name) +} + +func TestBGPVPNNetworkAssociationCRD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a BGP VPN + bgpVpnCreated, err := CreateBGPVPN(t, client) + th.AssertNoErr(t, err) + defer func() { + err = bgpvpns.Delete(context.TODO(), client, bgpVpnCreated.ID).ExtractErr() + th.AssertNoErr(t, err) + }() + + // Create a Network + network, err := networking.CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + // Associate a Network with a BGP VPN + assocOpts := bgpvpns.CreateNetworkAssociationOpts{ + NetworkID: network.ID, + } + assoc, err := bgpvpns.CreateNetworkAssociation(context.TODO(), client, bgpVpnCreated.ID, assocOpts).Extract() + th.AssertNoErr(t, err) + defer func() { + err = bgpvpns.DeleteNetworkAssociation(context.TODO(), client, bgpVpnCreated.ID, assoc.ID).ExtractErr() + th.AssertNoErr(t, err) + }() + th.AssertEquals(t, network.ID, assoc.NetworkID) + + // Get a Network Association + assocGot, err := bgpvpns.GetNetworkAssociation(context.TODO(), client, bgpVpnCreated.ID, assoc.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, network.ID, assocGot.NetworkID) + + // List all Network Associations + allPages, err := bgpvpns.ListNetworkAssociations(client, bgpVpnCreated.ID, bgpvpns.ListNetworkAssociationsOpts{}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + allAssocs, err := bgpvpns.ExtractNetworkAssociations(allPages) + th.AssertNoErr(t, err) + t.Logf("Retrieved Network Associations") + tools.PrintResource(t, allAssocs) + th.AssertIntGreaterOrEqual(t, len(allAssocs), 1) + + // Get BGP VPN with associations + getBgpVpn, err := bgpvpns.Get(context.TODO(), client, bgpVpnCreated.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getBgpVpn) +} + +func TestBGPVPNRouterAssociationCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a BGP VPN + bgpVpnCreated, err := CreateBGPVPN(t, client) + th.AssertNoErr(t, err) + defer func() { + err = bgpvpns.Delete(context.TODO(), client, bgpVpnCreated.ID).ExtractErr() + th.AssertNoErr(t, err) + }() + + // Create a Network + network, err := networking.CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + // Create a Router + routerCreated, err := layer3.CreateRouter(t, client, network.ID) + th.AssertNoErr(t, err) + defer layer3.DeleteRouter(t, client, routerCreated.ID) + + // Associate a Router with a BGP VPN + assocOpts := bgpvpns.CreateRouterAssociationOpts{ + RouterID: routerCreated.ID, + } + assoc, err := bgpvpns.CreateRouterAssociation(context.TODO(), client, bgpVpnCreated.ID, assocOpts).Extract() + th.AssertNoErr(t, err) + defer func() { + err = bgpvpns.DeleteRouterAssociation(context.TODO(), client, bgpVpnCreated.ID, assoc.ID).ExtractErr() + th.AssertNoErr(t, err) + }() + th.AssertEquals(t, routerCreated.ID, assoc.RouterID) + + // Get a Router Association + assocGot, err := bgpvpns.GetRouterAssociation(context.TODO(), client, bgpVpnCreated.ID, assoc.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, routerCreated.ID, assocGot.RouterID) + + // Update a Router Association + assocUpdOpts := bgpvpns.UpdateRouterAssociationOpts{ + AdvertiseExtraRoutes: new(bool), + } + assocUpdate, err := bgpvpns.UpdateRouterAssociation(context.TODO(), client, bgpVpnCreated.ID, assoc.ID, assocUpdOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, routerCreated.ID, assocUpdate.RouterID) + th.AssertEquals(t, false, assocUpdate.AdvertiseExtraRoutes) + + // List all Router Associations + allPages, err := bgpvpns.ListRouterAssociations(client, bgpVpnCreated.ID, bgpvpns.ListRouterAssociationsOpts{}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + allAssocs, err := bgpvpns.ExtractRouterAssociations(allPages) + th.AssertNoErr(t, err) + t.Logf("Retrieved Router Associations") + tools.PrintResource(t, allAssocs) + th.AssertIntGreaterOrEqual(t, len(allAssocs), 1) + + // Get BGP VPN with associations + getBgpVpn, err := bgpvpns.Get(context.TODO(), client, bgpVpnCreated.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getBgpVpn) +} + +func TestBGPVPNPortAssociationCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Create a BGP VPN + bgpVpnCreated, err := CreateBGPVPN(t, client) + th.AssertNoErr(t, err) + defer func() { + err = bgpvpns.Delete(context.TODO(), client, bgpVpnCreated.ID).ExtractErr() + th.AssertNoErr(t, err) + }() + + // Create a Network + network, err := networking.CreateNetwork(t, client) + th.AssertNoErr(t, err) + defer networking.DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := networking.CreateSubnet(t, client, network.ID) + th.AssertNoErr(t, err) + defer networking.DeleteSubnet(t, client, subnet.ID) + + // Create port + port, err := networking.CreatePort(t, client, network.ID, subnet.ID) + th.AssertNoErr(t, err) + defer networking.DeletePort(t, client, port.ID) + + // Associate a Port with a BGP VPN + assocOpts := bgpvpns.CreatePortAssociationOpts{ + PortID: port.ID, + } + assoc, err := bgpvpns.CreatePortAssociation(context.TODO(), client, bgpVpnCreated.ID, assocOpts).Extract() + th.AssertNoErr(t, err) + defer func() { + err = bgpvpns.DeletePortAssociation(context.TODO(), client, bgpVpnCreated.ID, assoc.ID).ExtractErr() + th.AssertNoErr(t, err) + }() + th.AssertEquals(t, port.ID, assoc.PortID) + + // Get a Port Association + assocGot, err := bgpvpns.GetPortAssociation(context.TODO(), client, bgpVpnCreated.ID, assoc.ID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, port.ID, assocGot.PortID) + + // Update a Port Association + assocUpdOpts := bgpvpns.UpdatePortAssociationOpts{ + AdvertiseFixedIPs: new(bool), + } + assocUpdate, err := bgpvpns.UpdatePortAssociation(context.TODO(), client, bgpVpnCreated.ID, assoc.ID, assocUpdOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, port.ID, assocUpdate.PortID) + th.AssertEquals(t, false, assocUpdate.AdvertiseFixedIPs) + + // List all Port Associations + allPages, err := bgpvpns.ListPortAssociations(client, bgpVpnCreated.ID, bgpvpns.ListPortAssociationsOpts{}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + allAssocs, err := bgpvpns.ExtractPortAssociations(allPages) + th.AssertNoErr(t, err) + t.Logf("Retrieved Port Associations") + tools.PrintResource(t, allAssocs) + th.AssertIntGreaterOrEqual(t, len(allAssocs), 1) + + // Get BGP VPN with associations + getBgpVpn, err := bgpvpns.Get(context.TODO(), client, bgpVpnCreated.ID).Extract() + th.AssertNoErr(t, err) + tools.PrintResource(t, getBgpVpn) +} diff --git a/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/doc.go b/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/doc.go new file mode 100644 index 0000000000..d6db7c50b2 --- /dev/null +++ b/internal/acceptance/openstack/networking/v2/extensions/bgpvpns/doc.go @@ -0,0 +1,2 @@ +// BGP VPN acceptance tests +package bgpvpns diff --git a/openstack/networking/v2/extensions/bgpvpns/doc.go b/openstack/networking/v2/extensions/bgpvpns/doc.go new file mode 100644 index 0000000000..cdb85dc101 --- /dev/null +++ b/openstack/networking/v2/extensions/bgpvpns/doc.go @@ -0,0 +1,66 @@ +package bgpvpns + +/* +Package bgpvpns contains the functionality for working with Neutron BGP VPNs. + +1. List BGP VPNs, a.k.a. GET /bgpvpn/bgpvpns + +Example: + + pages, err := bgpvpns.List(client).AllPages(context.TODO()) + if err != nil { + log.Panic(err) + } + allVPNs, err := bgpvpns.ExtractBGPVPNs(pages) + if err != nil { + log.Panic(err) + } + + for _, bgpvpn := range allVPNs { + log.Printf("%+v", bgpvpn) + } + +2. Get BGP VPN, a.k.a. GET /bgpvpn/bgpvpns/{id} + +Example: + p, err := bgpvpns.Get(context.TODO(), client, id).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", *p) + +3. Create BGP VPN, a.k.a. POST /bgpvpn/bgpvpns + +Example: + opts := bgpvpns.CreateOpts{ + name: "gophercloud-testing-bgpvpn". + } + r, err := bgpvpns.Create(context.TODO(), client, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", *r) + +4. Delete BGP VPN, a.k.a. DELETE /bgpvpn/bgpvpns/{id} + +Example: + err := bgpvpns.Delete(context.TODO(), client, bgpVpnID).ExtractErr() + if err != nil { + log.Panic(err) + } + log.Printf("BGP VPN deleted") + + +5. Update BGP VPN, a.k.a. PUT /bgpvpn/bgpvpns/{id} + +Example: + nameUpdated := "bgpvpn-name-updated" + opts := bgpvpns.UpdateOpts{ + name: &nameUpdated, + } + p, err := bgpvpns.Update(context.TODO(), client, id, opts).Extract() + if err != nil { + log.Panic(err) + } + log.Printf("%+v", p) +*/ diff --git a/openstack/networking/v2/extensions/bgpvpns/requests.go b/openstack/networking/v2/extensions/bgpvpns/requests.go new file mode 100644 index 0000000000..587961edee --- /dev/null +++ b/openstack/networking/v2/extensions/bgpvpns/requests.go @@ -0,0 +1,478 @@ +package bgpvpns + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the List +// request. +type ListOptsBuilder interface { + ToBGPVPNListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through the API. +type ListOpts struct { + Fields []string `q:"fields"` + ProjectID string `q:"project_id"` + Networks []string `q:"networks"` + Routers []string `q:"routers"` + Ports []string `q:"ports"` + Limit int `q:"limit"` + Marker string `q:"marker"` +} + +// ToBGPVPNListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToBGPVPNListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), nil +} + +// List the BGP VPNs +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + query, err := opts.ToBGPVPNListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + p := BGPVPNPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// Get retrieve the specific BGP VPN by its uuid +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToBGPVPNCreateMap() (map[string]any, error) +} + +// CreateOpts represents options used to create a BGP VPN. +type CreateOpts struct { + Name string `json:"name,omitempty"` + RouteDistinguishers []string `json:"route_distinguishers,omitempty"` + RouteTargets []string `json:"route_targets,omitempty"` + ImportTargets []string `json:"import_targets,omitempty"` + ExportTargets []string `json:"export_targets,omitempty"` + LocalPref int `json:"local_pref,omitempty"` + VNI int `json:"vni,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` + Type string `json:"type,omitempty"` +} + +// ToBGPVPNCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToBGPVPNCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "bgpvpn") +} + +// Create a BGP VPN +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOpts) (r CreateResult) { + b, err := opts.ToBGPVPNCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(ctx, createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete accepts a unique ID and deletes the BGP VPN associated with it. +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToBGPVPNUpdateMap() (map[string]any, error) +} + +// UpdateOpts represents options used to update a BGP VPN. +type UpdateOpts struct { + Name *string `json:"name,omitempty"` + RouteDistinguishers *[]string `json:"route_distinguishers,omitempty"` + RouteTargets *[]string `json:"route_targets,omitempty"` + ImportTargets *[]string `json:"import_targets,omitempty"` + ExportTargets *[]string `json:"export_targets,omitempty"` + LocalPref *int `json:"local_pref,omitempty"` +} + +// ToBGPVPNUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToBGPVPNUpdateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "bgpvpn") +} + +// Update accept a BGP VPN ID and an UpdateOpts and update the BGP VPN +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateResult) { + b, err := opts.ToBGPVPNUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(ctx, updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListNetworkAssociationsOptsBuilder allows extensions to add additional +// parameters to the ListNetworkAssociations request. +type ListNetworkAssociationsOptsBuilder interface { + ToNetworkAssociationsListQuery() (string, error) +} + +// ListNetworkAssociationsOpts allows the filtering and sorting of paginated +// collections through the API. +type ListNetworkAssociationsOpts struct { + Fields []string `q:"fields"` + Limit int `q:"limit"` + Marker string `q:"marker"` +} + +// ToNetworkAssociationsListQuery formats a ListNetworkAssociationsOpts into a +// query string. +func (opts ListNetworkAssociationsOpts) ToNetworkAssociationsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), nil +} + +// ListNetworkAssociations pages over the network associations of a specified +// BGP VPN. +func ListNetworkAssociations(c *gophercloud.ServiceClient, id string, opts ListNetworkAssociationsOptsBuilder) pagination.Pager { + url := listNetworkAssociationsURL(c, id) + query, err := opts.ToNetworkAssociationsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + p := NetworkAssociationPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// CreateNetworkAssociationOptsBuilder allows extensions to add additional +// parameters to the CreateNetworkAssociation request. +type CreateNetworkAssociationOptsBuilder interface { + ToNetworkAssociationCreateMap() (map[string]interface{}, error) +} + +// CreateNetworkAssociationOpts represents options used to create a BGP VPN +// network association. +type CreateNetworkAssociationOpts struct { + NetworkID string `json:"network_id" required:"true"` + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` +} + +// ToNetworkAssociationCreateMap builds a request body from +// CreateNetworkAssociationOpts. +func (opts CreateNetworkAssociationOpts) ToNetworkAssociationCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "network_association") +} + +// CreateNetworkAssociation creates a new network association for a specified +// BGP VPN. +func CreateNetworkAssociation(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CreateNetworkAssociationOptsBuilder) (r CreateNetworkAssociationResult) { + b, err := opts.ToNetworkAssociationCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, createNetworkAssociationURL(client, id), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetNetworkAssociation retrieves a specific network association by BGP VPN id +// and network association id. +func GetNetworkAssociation(ctx context.Context, c *gophercloud.ServiceClient, bgpVpnID string, id string) (r GetNetworkAssociationResult) { + resp, err := c.Get(ctx, getNetworkAssociationURL(c, bgpVpnID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteNetworkAssociation deletes a specific network association by BGP VPN id +// and network association id. +func DeleteNetworkAssociation(ctx context.Context, c *gophercloud.ServiceClient, bgpVpnID string, id string) (r DeleteNetworkAssociationResult) { + resp, err := c.Delete(ctx, deleteNetworkAssociationURL(c, bgpVpnID, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListRouterAssociationsOptsBuilder allows extensions to add additional +// parameters to the ListRouterAssociations request. +type ListRouterAssociationsOptsBuilder interface { + ToRouterAssociationsListQuery() (string, error) +} + +// ListRouterAssociationsOpts allows the filtering and sorting of paginated +// collections through the API. +type ListRouterAssociationsOpts struct { + Fields []string `q:"fields"` + Limit int `q:"limit"` + Marker string `q:"marker"` +} + +// ToRouterAssociationsListQuery formats a ListRouterAssociationsOpts into a +// query string. +func (opts ListRouterAssociationsOpts) ToRouterAssociationsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), nil +} + +// ListRouterAssociations pages over the router associations of a specified +// BGP VPN. +func ListRouterAssociations(c *gophercloud.ServiceClient, id string, opts ListRouterAssociationsOptsBuilder) pagination.Pager { + url := listRouterAssociationsURL(c, id) + query, err := opts.ToRouterAssociationsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + p := RouterAssociationPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// CreateRouterAssociationOptsBuilder allows extensions to add additional +// parameters to the CreateRouterAssociation request. +type CreateRouterAssociationOptsBuilder interface { + ToRouterAssociationCreateMap() (map[string]interface{}, error) +} + +// CreateRouterAssociationOpts represents options used to create a BGP VPN +// router association. +type CreateRouterAssociationOpts struct { + RouterID string `json:"router_id" required:"true"` + AdvertiseExtraRoutes *bool `json:"advertise_extra_routes,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` +} + +// ToRouterAssociationCreateMap builds a request body from +// CreateRouterAssociationOpts. +func (opts CreateRouterAssociationOpts) ToRouterAssociationCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "router_association") +} + +// CreateRouterAssociation creates a new router association for a specified +// BGP VPN. +func CreateRouterAssociation(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CreateRouterAssociationOptsBuilder) (r CreateRouterAssociationResult) { + b, err := opts.ToRouterAssociationCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, createRouterAssociationURL(client, id), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetRouterAssociation retrieves a specific router association by BGP VPN id +// and router association id. +func GetRouterAssociation(ctx context.Context, c *gophercloud.ServiceClient, bgpVpnID string, id string) (r GetRouterAssociationResult) { + resp, err := c.Get(ctx, getRouterAssociationURL(c, bgpVpnID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteRouterAssociation deletes a specific router association by BGP VPN id +// and router association id. +func DeleteRouterAssociation(ctx context.Context, c *gophercloud.ServiceClient, bgpVpnID string, id string) (r DeleteRouterAssociationResult) { + resp, err := c.Delete(ctx, deleteRouterAssociationURL(c, bgpVpnID, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateRouterAssociationOptsBuilder allows extensions to add additional +// parameters to the UpdateRouterAssociation request. +type UpdateRouterAssociationOptsBuilder interface { + ToRouterAssociationUpdateMap() (map[string]interface{}, error) +} + +// UpdateRouterAssociationOpts represents options used to update a BGP VPN +// router association. +type UpdateRouterAssociationOpts struct { + AdvertiseExtraRoutes *bool `json:"advertise_extra_routes,omitempty"` +} + +// ToRouterAssociationUpdateMap builds a request body from +// UpdateRouterAssociationOpts. +func (opts UpdateRouterAssociationOpts) ToRouterAssociationUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "router_association") +} + +// UpdateRouterAssociation updates a router association for a specified BGP VPN. +func UpdateRouterAssociation(ctx context.Context, client *gophercloud.ServiceClient, bgpVpnID string, id string, opts UpdateRouterAssociationOptsBuilder) (r UpdateRouterAssociationResult) { + b, err := opts.ToRouterAssociationUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, updateRouterAssociationURL(client, bgpVpnID, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListPortAssociationsOptsBuilder allows extensions to add additional +// parameters to the ListPortAssociations request. +type ListPortAssociationsOptsBuilder interface { + ToPortAssociationsListQuery() (string, error) +} + +// ListPortAssociationsOpts allows the filtering and sorting of paginated +// collections through the API. +type ListPortAssociationsOpts struct { + Fields []string `q:"fields"` + Limit int `q:"limit"` + Marker string `q:"marker"` +} + +// ToPortAssociationsListQuery formats a ListPortAssociationsOpts into a +// query string. +func (opts ListPortAssociationsOpts) ToPortAssociationsListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + return q.String(), nil +} + +// ListPortAssociations pages over the port associations of a specified +// BGP VPN. +func ListPortAssociations(c *gophercloud.ServiceClient, id string, opts ListPortAssociationsOptsBuilder) pagination.Pager { + url := listPortAssociationsURL(c, id) + query, err := opts.ToPortAssociationsListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + p := PortAssociationPage{pagination.MarkerPageBase{PageResult: r}} + p.MarkerPageBase.Owner = p + return p + }) +} + +// PortRoutes represents the routes to be advertised by a BGP VPN port +type PortRoutes struct { + Type string `json:"type" required:"true"` + Prefix string `json:"prefix,omitempty"` + BGPVPNID string `json:"bgpvpn_id,omitempty"` + LocalPref *int `json:"local_pref,omitempty"` +} + +// CreatePortAssociationOptsBuilder allows extensions to add additional +// parameters to the CreatePortAssociation request. +type CreatePortAssociationOptsBuilder interface { + ToPortAssociationCreateMap() (map[string]interface{}, error) +} + +// CreatePortAssociationOpts represents options used to create a BGP VPN +// port association. +type CreatePortAssociationOpts struct { + PortID string `json:"port_id" required:"true"` + Routes []PortRoutes `json:"routes,omitempty"` + AdvertiseFixedIPs *bool `json:"advertise_fixed_ips,omitempty"` + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` +} + +// ToPortAssociationCreateMap builds a request body from +// CreatePortAssociationOpts. +func (opts CreatePortAssociationOpts) ToPortAssociationCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "port_association") +} + +// CreatePortAssociation creates a new port association for a specified +// BGP VPN. +func CreatePortAssociation(ctx context.Context, client *gophercloud.ServiceClient, id string, opts CreatePortAssociationOptsBuilder) (r CreatePortAssociationResult) { + b, err := opts.ToPortAssociationCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, createPortAssociationURL(client, id), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// GetPortAssociation retrieves a specific port association by BGP VPN id +// and port association id. +func GetPortAssociation(ctx context.Context, c *gophercloud.ServiceClient, bgpVpnID string, id string) (r GetPortAssociationResult) { + resp, err := c.Get(ctx, getPortAssociationURL(c, bgpVpnID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeletePortAssociation deletes a specific port association by BGP VPN id +// and port association id. +func DeletePortAssociation(ctx context.Context, c *gophercloud.ServiceClient, bgpVpnID string, id string) (r DeletePortAssociationResult) { + resp, err := c.Delete(ctx, deletePortAssociationURL(c, bgpVpnID, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdatePortAssociationOptsBuilder allows extensions to add additional +// parameters to the UpdatePortAssociation request. +type UpdatePortAssociationOptsBuilder interface { + ToPortAssociationUpdateMap() (map[string]interface{}, error) +} + +// UpdatePortAssociationOpts represents options used to update a BGP VPN +// port association. +type UpdatePortAssociationOpts struct { + Routes *[]PortRoutes `json:"routes,omitempty"` + AdvertiseFixedIPs *bool `json:"advertise_fixed_ips,omitempty"` +} + +// ToPortAssociationUpdateMap builds a request body from +// UpdatePortAssociationOpts. +func (opts UpdatePortAssociationOpts) ToPortAssociationUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "port_association") +} + +// UpdatePortAssociation updates a port association for a specified BGP VPN. +func UpdatePortAssociation(ctx context.Context, client *gophercloud.ServiceClient, bgpVpnID string, id string, opts UpdatePortAssociationOptsBuilder) (r UpdatePortAssociationResult) { + b, err := opts.ToPortAssociationUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, updatePortAssociationURL(client, bgpVpnID, 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/bgpvpns/results.go b/openstack/networking/v2/extensions/bgpvpns/results.go new file mode 100644 index 0000000000..df0587568c --- /dev/null +++ b/openstack/networking/v2/extensions/bgpvpns/results.go @@ -0,0 +1,517 @@ +package bgpvpns + +import ( + "net/url" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +const ( + invalidMarker = "-1" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a BGP VPN resource. +func (r commonResult) Extract() (*BGPVPN, error) { + var s BGPVPN + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonResult) ExtractInto(v any) error { + return r.Result.ExtractIntoStructPtr(v, "bgpvpn") +} + +// BGPVPN represents an MPLS network with which Neutron routers and/or networks +// may be associated +type BGPVPN struct { + // The ID of the BGP VPN. + ID string `json:"id"` + + // The user meaningful name of the BGP VPN. + Name string `json:"name"` + + // Selection of the type of VPN and the technology behind it. Allowed + // values are l2 or l3. + Type string `json:"type"` + + // Indicates whether this BGP VPN is shared across tenants. + Shared bool `json:"shared"` + + // List of route distinguisher strings. If this parameter is specified, + // one of these RDs will be used to advertise VPN routes. + RouteDistinguishers []string `json:"route_distinguishers"` + + // Route Targets that will be both imported and used for export. + RouteTargets []string `json:"route_targets"` + + // Additional Route Targets that will be imported. + ImportTargets []string `json:"import_targets"` + + // Additional Route Targets that will be used for export. + ExportTargets []string `json:"export_targets"` + + // This read-only list of network IDs reflects the associations defined + // by Network association API resources. + Networks []string `json:"networks"` + + // This read-only list of router IDs reflects the associations defined + // by Router association API resources. + Routers []string `json:"routers"` + + // This read-only list of port IDs reflects the associations defined by + // Port association API resources (only present if the + // bgpvpn-routes-control API extension is enabled). + Ports []string `json:"ports"` + + // The default BGP LOCAL_PREF of routes that will be advertised to the + // BGPVPN (unless overridden per-route). + LocalPref *int `json:"local_pref"` + + // The globally-assigned VXLAN vni for the BGP VPN. + VNI int `json:"vni"` + + // The ID of the project. + TenantID string `json:"tenant_id"` + + // The ID of the project. + ProjectID string `json:"project_id"` +} + +// BGPVPNPage is the page returned by a pager when traversing over a +// collection of BGP VPNs. +type BGPVPNPage struct { + pagination.MarkerPageBase +} + +// NextPageURL generates the URL for the page of results after this one. +func (r BGPVPNPage) 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("marker", mark) + currentURL.RawQuery = q.Encode() + return currentURL.String(), nil +} + +// LastMarker returns the last offset in a ListResult. +func (r BGPVPNPage) LastMarker() (string, error) { + results, err := ExtractBGPVPNs(r) + if err != nil { + return invalidMarker, err + } + if len(results) == 0 { + return invalidMarker, nil + } + + u, err := url.Parse(r.URL.String()) + if err != nil { + return invalidMarker, err + } + queryParams := u.Query() + limit := queryParams.Get("limit") + + // Limit is not present, only one page required + if limit == "" { + return invalidMarker, nil + } + + return results[len(results)-1].ID, nil +} + +// IsEmpty checks whether a BGPPage struct is empty. +func (r BGPVPNPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + is, err := ExtractBGPVPNs(r) + return len(is) == 0, err +} + +// ExtractBGPVPNs accepts a Page struct, specifically a BGPVPNPage struct, +// and extracts the elements into a slice of BGPVPN structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractBGPVPNs(r pagination.Page) ([]BGPVPN, error) { + var s []BGPVPN + err := ExtractBGPVPNsInto(r, &s) + return s, err +} + +func ExtractBGPVPNsInto(r pagination.Page, v any) error { + return r.(BGPVPNPage).Result.ExtractIntoSlicePtr(v, "bgpvpns") +} + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a BGPVPN. +type GetResult struct { + commonResult +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to intepret it as a BGPVPN. +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 +} + +// UpdateResult represents the result of an update operation. Call its Extract +// method to interpret it as a BGPVPN. +type UpdateResult struct { + commonResult +} + +type commonNetworkAssociationResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a BGP VPN resource. +func (r commonNetworkAssociationResult) Extract() (*NetworkAssociation, error) { + var s NetworkAssociation + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonNetworkAssociationResult) ExtractInto(v any) error { + return r.Result.ExtractIntoStructPtr(v, "network_association") +} + +// NetworkAssociation represents a BGP VPN network association object. +type NetworkAssociation struct { + ID string `json:"id"` + NetworkID string `json:"network_id"` + TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` +} + +// NetworkAssociationPage is the page returned by a pager when traversing over a +// collection of network associations. +type NetworkAssociationPage struct { + pagination.MarkerPageBase +} + +// NextPageURL generates the URL for the page of results after this one. +func (r NetworkAssociationPage) 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("marker", mark) + currentURL.RawQuery = q.Encode() + return currentURL.String(), nil +} + +// LastMarker returns the last offset in a ListResult. +func (r NetworkAssociationPage) LastMarker() (string, error) { + results, err := ExtractNetworkAssociations(r) + if err != nil { + return invalidMarker, err + } + if len(results) == 0 { + return invalidMarker, nil + } + + u, err := url.Parse(r.URL.String()) + if err != nil { + return invalidMarker, err + } + queryParams := u.Query() + limit := queryParams.Get("limit") + + // Limit is not present, only one page required + if limit == "" { + return invalidMarker, nil + } + + return results[len(results)-1].ID, nil +} + +// IsEmpty checks whether a NetworkAssociationPage struct is empty. +func (r NetworkAssociationPage) IsEmpty() (bool, error) { + is, err := ExtractNetworkAssociations(r) + return len(is) == 0, err +} + +// ExtractNetworkAssociations accepts a Page struct, specifically a NetworkAssociationPage struct, +// and extracts the elements into a slice of NetworkAssociation structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractNetworkAssociations(r pagination.Page) ([]NetworkAssociation, error) { + var s []NetworkAssociation + err := ExtractNetworkAssociationsInto(r, &s) + return s, err +} + +func ExtractNetworkAssociationsInto(r pagination.Page, v interface{}) error { + return r.(NetworkAssociationPage).Result.ExtractIntoSlicePtr(v, "network_associations") +} + +// CreateNetworkAssociationResult represents the result of a create operation. Call its Extract +// method to interpret it as a NetworkAssociation. +type CreateNetworkAssociationResult struct { + commonNetworkAssociationResult +} + +// GetNetworkAssociationResult represents the result of a get operation. Call its Extract +// method to interpret it as a NetworkAssociation. +type GetNetworkAssociationResult struct { + commonNetworkAssociationResult +} + +// DeleteNetworkAssociationResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteNetworkAssociationResult struct { + gophercloud.ErrResult +} + +type commonRouterAssociationResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a BGP VPN resource. +func (r commonRouterAssociationResult) Extract() (*RouterAssociation, error) { + var s RouterAssociation + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonRouterAssociationResult) ExtractInto(v any) error { + return r.Result.ExtractIntoStructPtr(v, "router_association") +} + +// RouterAssociation represents a BGP VPN router association object. +type RouterAssociation struct { + ID string `json:"id"` + RouterID string `json:"router_id"` + TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` + AdvertiseExtraRoutes bool `json:"advertise_extra_routes"` +} + +// RouterAssociationPage is the page returned by a pager when traversing over a +// collection of router associations. +type RouterAssociationPage struct { + pagination.MarkerPageBase +} + +// NextPageURL generates the URL for the page of results after this one. +func (r RouterAssociationPage) 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("marker", mark) + currentURL.RawQuery = q.Encode() + return currentURL.String(), nil +} + +// LastMarker returns the last offset in a ListResult. +func (r RouterAssociationPage) LastMarker() (string, error) { + results, err := ExtractRouterAssociations(r) + if err != nil { + return invalidMarker, err + } + if len(results) == 0 { + return invalidMarker, nil + } + + u, err := url.Parse(r.URL.String()) + if err != nil { + return invalidMarker, err + } + queryParams := u.Query() + limit := queryParams.Get("limit") + + // Limit is not present, only one page required + if limit == "" { + return invalidMarker, nil + } + + return results[len(results)-1].ID, nil +} + +// IsEmpty checks whether a RouterAssociationPage struct is empty. +func (r RouterAssociationPage) IsEmpty() (bool, error) { + is, err := ExtractRouterAssociations(r) + return len(is) == 0, err +} + +// ExtractRouterAssociations accepts a Page struct, specifically a RouterAssociationPage struct, +// and extracts the elements into a slice of RouterAssociation structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractRouterAssociations(r pagination.Page) ([]RouterAssociation, error) { + var s []RouterAssociation + err := ExtractRouterAssociationsInto(r, &s) + return s, err +} + +func ExtractRouterAssociationsInto(r pagination.Page, v interface{}) error { + return r.(RouterAssociationPage).Result.ExtractIntoSlicePtr(v, "router_associations") +} + +// CreateRouterAssociationResult represents the result of a create operation. Call its Extract +// method to interpret it as a RouterAssociation. +type CreateRouterAssociationResult struct { + commonRouterAssociationResult +} + +// GetRouterAssociationResult represents the result of a get operation. Call its Extract +// method to interpret it as a RouterAssociation. +type GetRouterAssociationResult struct { + commonRouterAssociationResult +} + +// DeleteRouterAssociationResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeleteRouterAssociationResult struct { + gophercloud.ErrResult +} + +// UpdateRouterAssociationResult represents the result of an update operation. Call its Extract +// method to interpret it as a RouterAssociation. +type UpdateRouterAssociationResult struct { + commonRouterAssociationResult +} + +type commonPortAssociationResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a BGP VPN resource. +func (r commonPortAssociationResult) Extract() (*PortAssociation, error) { + var s PortAssociation + err := r.ExtractInto(&s) + return &s, err +} + +func (r commonPortAssociationResult) ExtractInto(v any) error { + return r.Result.ExtractIntoStructPtr(v, "port_association") +} + +// PortAssociation represents a BGP VPN port association object. +type PortAssociation struct { + ID string `json:"id"` + PortID string `json:"port_id"` + TenantID string `json:"tenant_id"` + ProjectID string `json:"project_id"` + Routes []PortRoutes `json:"routes"` + AdvertiseFixedIPs bool `json:"advertise_fixed_ips"` +} + +// PortAssociationPage is the page returned by a pager when traversing over a +// collection of port associations. +type PortAssociationPage struct { + pagination.MarkerPageBase +} + +// NextPageURL generates the URL for the page of results after this one. +func (r PortAssociationPage) 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("marker", mark) + currentURL.RawQuery = q.Encode() + return currentURL.String(), nil +} + +// LastMarker returns the last offset in a ListResult. +func (r PortAssociationPage) LastMarker() (string, error) { + results, err := ExtractPortAssociations(r) + if err != nil { + return invalidMarker, err + } + if len(results) == 0 { + return invalidMarker, nil + } + + u, err := url.Parse(r.URL.String()) + if err != nil { + return invalidMarker, err + } + queryParams := u.Query() + limit := queryParams.Get("limit") + + // Limit is not present, only one page required + if limit == "" { + return invalidMarker, nil + } + + return results[len(results)-1].ID, nil +} + +// IsEmpty checks whether a PortAssociationPage struct is empty. +func (r PortAssociationPage) IsEmpty() (bool, error) { + is, err := ExtractPortAssociations(r) + return len(is) == 0, err +} + +// ExtractPortAssociations accepts a Page struct, specifically a PortAssociationPage struct, +// and extracts the elements into a slice of PortAssociation structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractPortAssociations(r pagination.Page) ([]PortAssociation, error) { + var s []PortAssociation + err := ExtractPortAssociationsInto(r, &s) + return s, err +} + +func ExtractPortAssociationsInto(r pagination.Page, v interface{}) error { + return r.(PortAssociationPage).Result.ExtractIntoSlicePtr(v, "port_associations") +} + +// CreatePortAssociationResult represents the result of a create operation. Call its Extract +// method to interpret it as a PortAssociation. +type CreatePortAssociationResult struct { + commonPortAssociationResult +} + +// GetPortAssociationResult represents the result of a get operation. Call its Extract +// method to interpret it as a PortAssociation. +type GetPortAssociationResult struct { + commonPortAssociationResult +} + +// DeletePortAssociationResult represents the result of a delete operation. Call its +// ExtractErr method to determine if the request succeeded or failed. +type DeletePortAssociationResult struct { + gophercloud.ErrResult +} + +// UpdatePortAssociationResult represents the result of an update operation. Call its Extract +// method to interpret it as a PortAssociation. +type UpdatePortAssociationResult struct { + commonPortAssociationResult +} diff --git a/openstack/networking/v2/extensions/bgpvpns/testing/doc.go b/openstack/networking/v2/extensions/bgpvpns/testing/doc.go new file mode 100644 index 0000000000..7a770b9160 --- /dev/null +++ b/openstack/networking/v2/extensions/bgpvpns/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing for bgpvpns +package testing diff --git a/openstack/networking/v2/extensions/bgpvpns/testing/fixture.go b/openstack/networking/v2/extensions/bgpvpns/testing/fixture.go new file mode 100644 index 0000000000..1be32894bc --- /dev/null +++ b/openstack/networking/v2/extensions/bgpvpns/testing/fixture.go @@ -0,0 +1,434 @@ +package testing + +import "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgpvpns" + +const ListBGPVPNsResult = ` +{ + "bgpvpns": [ + { + "export_targets": [ + "64512:1666" + ], + "name": "", + "routers": [], + "route_distinguishers": [ + "64512:1777", + "64512:1888", + "64512:1999" + ], + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "import_targets": [ + "64512:1555" + ], + "route_targets": [ + "64512:1444" + ], + "type": "l3", + "id": "0f9d472a-908f-40f5-8574-b4e8a63ccbf0", + "networks": [], + "local_pref": null, + "vni": 1000 + } + ] +} +` + +var BGPVPN = bgpvpns.BGPVPN{ + ID: "0f9d472a-908f-40f5-8574-b4e8a63ccbf0", + Name: "", + RouteDistinguishers: []string{"64512:1777", "64512:1888", "64512:1999"}, + RouteTargets: []string{"64512:1444"}, + ImportTargets: []string{"64512:1555"}, + ExportTargets: []string{"64512:1666"}, + LocalPref: nil, + VNI: 1000, + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", + Type: "l3", + Routers: []string{}, + Networks: []string{}, +} + +const GetBGPVPNResult = ` +{ + "bgpvpn": { + "id": "460ac411-3dfb-45bb-8116-ed1a7233d143", + "name": "foo", + "route_targets": ["64512:1444"], + "export_targets": [], + "import_targets": [], + "type": "l3", + "tenant_id": "f94ea398564d49dfb0d542f086c68ce7", + "project_id": "f94ea398564d49dfb0d542f086c68ce7", + "routers": [], + "route_distinguishers": [], + "networks": [ + "a4f2b8df-cb42-4893-a333-d0b5c36ade17" + ], + "local_pref": null, + "vni": 1000 + } +} +` + +var GetBGPVPN = bgpvpns.BGPVPN{ + ID: "460ac411-3dfb-45bb-8116-ed1a7233d143", + Name: "foo", + RouteDistinguishers: []string{}, + RouteTargets: []string{"64512:1444"}, + ImportTargets: []string{}, + ExportTargets: []string{}, + LocalPref: nil, + VNI: 1000, + TenantID: "f94ea398564d49dfb0d542f086c68ce7", + ProjectID: "f94ea398564d49dfb0d542f086c68ce7", + Type: "l3", + Routers: []string{}, + Networks: []string{"a4f2b8df-cb42-4893-a333-d0b5c36ade17"}, +} + +const CreateRequest = ` +{ + "bgpvpn": { + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "route_targets": ["64512:1444"], + "import_targets": ["64512:1555"], + "export_targets": ["64512:1666"], + "route_distinguishers": ["64512:1777", "64512:1888", "64512:1999"], + "type": "l3", + "vni": 1000 + } +} +` + +const CreateResponse = ` +{ + "bgpvpn": { + "export_targets": [ + "64512:1666" + ], + "name": "", + "routers": [], + "route_distinguishers": [ + "64512:1777", + "64512:1888", + "64512:1999" + ], + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "import_targets": [ + "64512:1555" + ], + "route_targets": [ + "64512:1444" + ], + "type": "l3", + "id": "0f9d472a-908f-40f5-8574-b4e8a63ccbf0", + "networks": [], + "local_pref": null, + "vni": 1000 + } +} +` + +var CreateBGPVPN = bgpvpns.BGPVPN{ + ID: "0f9d472a-908f-40f5-8574-b4e8a63ccbf0", + RouteDistinguishers: []string{ + "64512:1777", + "64512:1888", + "64512:1999", + }, + RouteTargets: []string{"64512:1444"}, + ImportTargets: []string{"64512:1555"}, + ExportTargets: []string{"64512:1666"}, + LocalPref: nil, + VNI: 1000, + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", + Type: "l3", + Routers: []string{}, + Networks: []string{}, +} + +const UpdateBGPVPNRequest = ` +{ + "bgpvpn": { + "name": "foo", + "route_targets": ["64512:1444"], + "export_targets": [], + "import_targets": [] + } +} +` + +const UpdateBGPVPNResponse = ` +{ + "bgpvpn": { + "export_targets": [], + "name": "foo", + "routers": [], + "route_distinguishers": [ + "12345:1234" + ], + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "import_targets": [], + "route_targets": ["64512:1444"], + "type": "l3", + "id": "4d627abf-06dd-45ab-920b-8e61422bb984", + "networks": [], + "local_pref": null, + "vni": 1000 + } +} +` + +const ListNetworkAssociationsResult = ` +{ + "network_associations": [ + { + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "network_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9" + } + ] +} +` + +var NetworkAssociation = bgpvpns.NetworkAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + NetworkID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", +} + +const GetNetworkAssociationResult = ` +{ + "network_association": { + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "network_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9" + } +} +` + +var GetNetworkAssociation = bgpvpns.NetworkAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + NetworkID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", +} + +const CreateNetworkAssociationRequest = ` +{ + "network_association": { + "network_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd" + } +} +` +const CreateNetworkAssociationResponse = ` +{ + "network_association": { + "network_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + } +} +` + +var CreateNetworkAssociation = bgpvpns.NetworkAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + NetworkID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", +} + +const ListRouterAssociationsResult = ` +{ + "router_associations": [ + { + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "router_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9" + } + ] +} +` + +var RouterAssociation = bgpvpns.RouterAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + RouterID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", +} + +const GetRouterAssociationResult = ` +{ + "router_association": { + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "router_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9" + } +} +` + +var GetRouterAssociation = bgpvpns.RouterAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + RouterID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", +} + +const CreateRouterAssociationRequest = ` +{ + "router_association": { + "router_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd" + } +} +` +const CreateRouterAssociationResponse = ` +{ + "router_association": { + "router_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "advertise_extra_routes": true + } +} +` + +var CreateRouterAssociation = bgpvpns.RouterAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + RouterID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", + AdvertiseExtraRoutes: true, +} + +const UpdateRouterAssociationRequest = ` +{ + "router_association": { + "advertise_extra_routes": false + } +} +` +const UpdateRouterAssociationResponse = ` +{ + "router_association": { + "router_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + } +} +` + +var UpdateRouterAssociation = bgpvpns.RouterAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + RouterID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", + AdvertiseExtraRoutes: false, +} + +const ListPortAssociationsResult = ` +{ + "port_associations": [ + { + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "port_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "advertise_fixed_ips": true + } + ] +} +` + +var PortAssociation = bgpvpns.PortAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + PortID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", + AdvertiseFixedIPs: true, +} + +const GetPortAssociationResult = ` +{ + "port_association": { + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "port_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "advertise_fixed_ips": true + } +} +` + +var GetPortAssociation = bgpvpns.PortAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + PortID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", + AdvertiseFixedIPs: true, +} + +const CreatePortAssociationRequest = ` +{ + "port_association": { + "port_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd" + } +} +` +const CreatePortAssociationResponse = ` +{ + "port_association": { + "port_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "advertise_fixed_ips": true + } +} +` + +var CreatePortAssociation = bgpvpns.PortAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + PortID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", + AdvertiseFixedIPs: true, +} + +const UpdatePortAssociationRequest = ` +{ + "port_association": { + "advertise_fixed_ips": false + } +} +` +const UpdatePortAssociationResponse = ` +{ + "port_association": { + "port_id": "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + "tenant_id": "b7549121395844bea941bb92feb3fad9", + "project_id": "b7549121395844bea941bb92feb3fad9", + "id": "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + "advertise_fixed_ips": false + } +} +` + +var UpdatePortAssociation = bgpvpns.PortAssociation{ + ID: "73238ca1-e05d-4c7a-b4d4-70407b4b8730", + PortID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + TenantID: "b7549121395844bea941bb92feb3fad9", + ProjectID: "b7549121395844bea941bb92feb3fad9", + AdvertiseFixedIPs: false, +} diff --git a/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go b/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go new file mode 100644 index 0000000000..859441f6f4 --- /dev/null +++ b/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go @@ -0,0 +1,518 @@ +package testing + +import ( + "context" + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/bgpvpns" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + filterProjectID := []string{"b7549121395844bea941bb92feb3fad9"} + fields := []string{"id", "name"} + listOpts := bgpvpns.ListOpts{ + Fields: fields, + ProjectID: filterProjectID[0], + } + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + r.ParseForm() + th.AssertDeepEquals(t, r.Form["fields"], fields) + th.AssertDeepEquals(t, r.Form["project_id"], filterProjectID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListBGPVPNsResult) + }) + count := 0 + + err := bgpvpns.List(fake.ServiceClient(), listOpts).EachPage( + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { + count++ + actual, err := bgpvpns.ExtractBGPVPNs(page) + if err != nil { + t.Errorf("Failed to extract BGP VPNs: %v", err) + return false, nil + } + + expected := []bgpvpns.BGPVPN{BGPVPN} + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID, 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, GetBGPVPNResult) + }) + + r, err := bgpvpns.Get(context.TODO(), fake.ServiceClient(), bgpVpnID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, GetBGPVPN, *r) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns", 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 := bgpvpns.CreateOpts{ + TenantID: "b7549121395844bea941bb92feb3fad9", + RouteTargets: []string{ + "64512:1444", + }, + ImportTargets: []string{ + "64512:1555", + }, + ExportTargets: []string{ + "64512:1666", + }, + RouteDistinguishers: []string{ + "64512:1777", + "64512:1888", + "64512:1999", + }, + Type: "l3", + VNI: 1000, + } + + r, err := bgpvpns.Create(context.TODO(), fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, CreateBGPVPN, *r) +} + +func TestDelete(t *testing.T) { + bgpVpnID := "0f9d472a-908f-40f5-8574-b4e8a63ccbf0" + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID, 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 := bgpvpns.Delete(context.TODO(), fake.ServiceClient(), bgpVpnID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUpdate(t *testing.T) { + bgpVpnID := "4d627abf-06dd-45ab-920b-8e61422bb984" + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID, 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, UpdateBGPVPNRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, UpdateBGPVPNResponse) + }) + + name := "foo" + routeTargets := []string{"64512:1444"} + emptyTarget := []string{} + opts := bgpvpns.UpdateOpts{ + Name: &name, + RouteTargets: &routeTargets, + ImportTargets: &emptyTarget, + ExportTargets: &emptyTarget, + } + + r, err := bgpvpns.Update(context.TODO(), fake.ServiceClient(), bgpVpnID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, *opts.Name, r.Name) +} + +func TestListNetworkAssociations(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + fields := []string{"id", "name"} + listOpts := bgpvpns.ListNetworkAssociationsOpts{ + Fields: fields, + } + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/network_associations", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + r.ParseForm() + th.AssertDeepEquals(t, fields, r.Form["fields"]) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListNetworkAssociationsResult) + }) + + count := 0 + err := bgpvpns.ListNetworkAssociations(fake.ServiceClient(), bgpVpnID, listOpts).EachPage( + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { + count++ + actual, err := bgpvpns.ExtractNetworkAssociations(page) + if err != nil { + t.Errorf("Failed to extract network associations: %v", err) + return false, nil + } + + expected := []bgpvpns.NetworkAssociation{NetworkAssociation} + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, count) +} + +func TestCreateNetworkAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/network_associations", 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, CreateNetworkAssociationRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateNetworkAssociationResponse) + }) + + opts := bgpvpns.CreateNetworkAssociationOpts{ + NetworkID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + } + r, err := bgpvpns.CreateNetworkAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, CreateNetworkAssociation, *r) +} + +func TestGetNetworkAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + networkAssociationID := "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/network_associations/"+networkAssociationID, 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, GetNetworkAssociationResult) + }) + + r, err := bgpvpns.GetNetworkAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, networkAssociationID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, GetNetworkAssociation, *r) +} + +func TestDeleteNetworkAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + networkAssociationID := "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/network_associations/"+networkAssociationID, 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) + }) + + err := bgpvpns.DeleteNetworkAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, networkAssociationID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListRouterAssociations(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + fields := []string{"id", "name"} + listOpts := bgpvpns.ListRouterAssociationsOpts{ + Fields: fields, + } + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/router_associations", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + r.ParseForm() + th.AssertDeepEquals(t, fields, r.Form["fields"]) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListRouterAssociationsResult) + }) + + count := 0 + err := bgpvpns.ListRouterAssociations(fake.ServiceClient(), bgpVpnID, listOpts).EachPage( + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { + count++ + actual, err := bgpvpns.ExtractRouterAssociations(page) + if err != nil { + t.Errorf("Failed to extract router associations: %v", err) + return false, nil + } + + expected := []bgpvpns.RouterAssociation{RouterAssociation} + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, count) +} + +func TestCreateRouterAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/router_associations", 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, CreateRouterAssociationRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreateRouterAssociationResponse) + }) + + opts := bgpvpns.CreateRouterAssociationOpts{ + RouterID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + } + r, err := bgpvpns.CreateRouterAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, CreateRouterAssociation, *r) +} + +func TestGetRouterAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + routerAssociationID := "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/router_associations/"+routerAssociationID, 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, GetRouterAssociationResult) + }) + + r, err := bgpvpns.GetRouterAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, routerAssociationID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, GetRouterAssociation, *r) +} + +func TestUpdateRouterAssociation(t *testing.T) { + bgpVpnID := "4d627abf-06dd-45ab-920b-8e61422bb984" + routerAssociationID := "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/router_associations/"+routerAssociationID, 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, UpdateRouterAssociationRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdateRouterAssociationResponse) + }) + + opts := bgpvpns.UpdateRouterAssociationOpts{ + AdvertiseExtraRoutes: new(bool), + } + r, err := bgpvpns.UpdateRouterAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, routerAssociationID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, UpdateRouterAssociation, *r) +} + +func TestDeleteRouterAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + routerAssociationID := "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/router_associations/"+routerAssociationID, 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) + }) + + err := bgpvpns.DeleteRouterAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, routerAssociationID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListPortAssociations(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + fields := []string{"id", "name"} + listOpts := bgpvpns.ListPortAssociationsOpts{ + Fields: fields, + } + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/port_associations", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + r.ParseForm() + th.AssertDeepEquals(t, fields, r.Form["fields"]) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ListPortAssociationsResult) + }) + + count := 0 + err := bgpvpns.ListPortAssociations(fake.ServiceClient(), bgpVpnID, listOpts).EachPage( + context.TODO(), + func(_ context.Context, page pagination.Page) (bool, error) { + count++ + actual, err := bgpvpns.ExtractPortAssociations(page) + if err != nil { + t.Errorf("Failed to extract port associations: %v", err) + return false, nil + } + + expected := []bgpvpns.PortAssociation{PortAssociation} + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + th.AssertEquals(t, 1, count) +} + +func TestCreatePortAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/port_associations", 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, CreatePortAssociationRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, CreatePortAssociationResponse) + }) + + opts := bgpvpns.CreatePortAssociationOpts{ + PortID: "8c5d88dc-60ac-4b02-a65a-36b65888ddcd", + } + r, err := bgpvpns.CreatePortAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, CreatePortAssociation, *r) +} + +func TestGetPortAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + portAssociationID := "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/port_associations/"+portAssociationID, 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, GetPortAssociationResult) + }) + + r, err := bgpvpns.GetPortAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, portAssociationID).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, GetPortAssociation, *r) +} + +func TestUpdatePortAssociation(t *testing.T) { + bgpVpnID := "4d627abf-06dd-45ab-920b-8e61422bb984" + portAssociationID := "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/port_associations/"+portAssociationID, 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, UpdatePortAssociationRequest) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, UpdatePortAssociationResponse) + }) + + opts := bgpvpns.UpdatePortAssociationOpts{ + AdvertiseFixedIPs: new(bool), + } + r, err := bgpvpns.UpdatePortAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, portAssociationID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, UpdatePortAssociation, *r) +} + +func TestDeletePortAssociation(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + bgpVpnID := "460ac411-3dfb-45bb-8116-ed1a7233d143" + portAssociationID := "73238ca1-e05d-4c7a-b4d4-70407b4b8730" + th.Mux.HandleFunc("/v2.0/bgpvpn/bgpvpns/"+bgpVpnID+"/port_associations/"+portAssociationID, 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) + }) + + err := bgpvpns.DeletePortAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, portAssociationID).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/bgpvpns/urls.go b/openstack/networking/v2/extensions/bgpvpns/urls.go new file mode 100644 index 0000000000..49d3d07747 --- /dev/null +++ b/openstack/networking/v2/extensions/bgpvpns/urls.go @@ -0,0 +1,140 @@ +package bgpvpns + +import "github.com/gophercloud/gophercloud/v2" + +const urlBase = "bgpvpn/bgpvpns" + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id} +func resourceURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(urlBase, id) +} + +// return /v2.0/bgpvpn/bgpvpns +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(urlBase) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id} +func getURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +// return /v2.0/bgpvpn/bgpvpns +func listURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +// return /v2.0/bgpvpn/bgpvpns +func createURL(c *gophercloud.ServiceClient) string { + return rootURL(c) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id} +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id} +func updateURL(c *gophercloud.ServiceClient, id string) string { + return resourceURL(c, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/network_associations +func networkAssociationsURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return c.ServiceURL(urlBase, bgpVpnID, "network_associations") +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/network_associations/{network-association-id} +func networkAssociationResourceURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return c.ServiceURL(urlBase, bgpVpnID, "network_associations", id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/network_associations +func listNetworkAssociationsURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return networkAssociationsURL(c, bgpVpnID) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/network_associations +func createNetworkAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return networkAssociationsURL(c, bgpVpnID) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/network_associations/{network-association-id} +func getNetworkAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return networkAssociationResourceURL(c, bgpVpnID, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/network_associations/{network-association-id} +func deleteNetworkAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return networkAssociationResourceURL(c, bgpVpnID, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/router_associations +func routerAssociationsURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return c.ServiceURL(urlBase, bgpVpnID, "router_associations") +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/router_associations/{router-association-id} +func routerAssociationResourceURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return c.ServiceURL(urlBase, bgpVpnID, "router_associations", id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/router_associations +func listRouterAssociationsURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return routerAssociationsURL(c, bgpVpnID) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/router_associations +func createRouterAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return routerAssociationsURL(c, bgpVpnID) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/router_associations/{router-association-id} +func getRouterAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return routerAssociationResourceURL(c, bgpVpnID, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/router_associations/{router-association-id} +func updateRouterAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return routerAssociationResourceURL(c, bgpVpnID, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/router_associations/{router-association-id} +func deleteRouterAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return routerAssociationResourceURL(c, bgpVpnID, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/port_associations +func portAssociationsURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return c.ServiceURL(urlBase, bgpVpnID, "port_associations") +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/port_associations/{port-association-id} +func portAssociationResourceURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return c.ServiceURL(urlBase, bgpVpnID, "port_associations", id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/port_associations +func listPortAssociationsURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return portAssociationsURL(c, bgpVpnID) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/port_associations +func createPortAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string) string { + return portAssociationsURL(c, bgpVpnID) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/port_associations/{port-association-id} +func getPortAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return portAssociationResourceURL(c, bgpVpnID, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/port_associations/{port-association-id} +func updatePortAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return portAssociationResourceURL(c, bgpVpnID, id) +} + +// return /v2.0/bgpvpn/bgpvpns/{bgpvpn-id}/port_associations/{port-association-id} +func deletePortAssociationURL(c *gophercloud.ServiceClient, bgpVpnID string, id string) string { + return portAssociationResourceURL(c, bgpVpnID, id) +} From 8fa79d4963b46e4da2c36254c840ffa0568dda5f Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 5 Jul 2024 11:15:01 +0200 Subject: [PATCH 1902/2296] MIGRATING.md: Mention compute limits --- docs/MIGRATING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 96cd80d74b..49a5e87010 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -189,6 +189,10 @@ main module for the corresponding service. These are: Moved to `openstack/compute/v2/keypairs`. +- `openstack/compute/v2/extensions/limits` + + Moved to `openstack/compute/v2/limits`. + - `openstack/compute/v2/extensions/quotasets` Moved to `openstack/compute/v2/quotasets`. From 522add8d06ec51a10ee0d65739821d8977d01cd1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 5 Jul 2024 10:30:48 +0100 Subject: [PATCH 1903/2296] docs: Document removed extensions These were initially migrated and then removed before v2 was cut. Signed-off-by: Stephen Finucane Closes: #3083 --- docs/MIGRATING.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 96cd80d74b..83bf9fdc9a 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -357,8 +357,8 @@ folded into the modified APIs. These are: All functions and supporting structs and interfaces have been moved to `openstack/compute/v2/servers`. -Finally, for extensions that added new APIs *and* modified existing APIs, the -new APIs are moved into the main module of the corresponding service while the +For extensions that added new APIs *and* modified existing APIs, the new APIs +are moved into the main module of the corresponding service while the modifications are folded into the modified APIs. These are: - `openstack/compute/v2/extensions/availabilityzones` @@ -374,6 +374,34 @@ modifications are folded into the modified APIs. These are: `Scope` struct in `openstack/identity/v3/tokens`. Everything else is moved moved to `openstack/identity/v3/trusts`. +Finally, for extensions that are deprecated and have been removed in a +microversion, the APIs were removed entirely. These are: + +- `openstack/compute/v2/extensions/defsecrules` + + This was a proxy for the Networking service, Neutron. Use + `openstack/networking/v2/extensions/security/groups` instead. + +- `openstack/compute/v2/extensions/floatingips` + + This was a proxy for the Networking service, Neutron. Use + `openstack/networking/v2/extensions/layer3/floatingips` instead. + +- `openstack/compute/v2/extensions/images` + + This was a proxy for the Image service, Glance. Use + `openstack/image/v2/images` instead. + +- `openstack/compute/v2/extensions/networks` + + This was a proxy for the Networking service, Neutron. Use + `openstack/networking/v2/networks` instead. + +- `openstack/compute/v2/extensions/tenantnetworks` + + This was a proxy for the Networking service, Neutron. Use + `openstack/networking/v2/networks` instead. + ### Type changes `loadbalancer/v2/pools/CreateOpts.Members` is now a slice of `CreateMemberOpts` From a030c6c3fb9a79ae8ffc7b7ba2d84a6487af3801 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 5 Jul 2024 10:35:00 +0100 Subject: [PATCH 1904/2296] docs: Indicate former paths of removed services Signed-off-by: Stephen Finucane --- docs/MIGRATING.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 83bf9fdc9a..2620fa8f1a 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -481,8 +481,11 @@ start with a slash (`/`). Support for services that are no longer supported upstream has been removed. Users that still rely on theses old services should continue using Gophercloud v1. -- Cinder (Blockstorage) v1 +- Cinder (Blockstorage) v1 (`openstack/blockstorage/v1`) - Neutron (Networking) LBaaS and LBaaS v2 extensions + (`openstack/networking/v2/extensions/lbaas`, + `openstack/networking/v2/extensions/lbaas_v2`) - Neutron (Networking) FWaaS extension -- Poppy (CDNaaS) service -- Senlin (Clustering) service + (`openstack/networking/v2/extensions/fwaas`) +- Poppy (CDNaaS) service (`openstack/cdn`) +- Senlin (Clustering) service (`openstack/clustering`) From 3e80027bf3a11fd41a2a187ecc0d66910186aecd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:40:20 +0000 Subject: [PATCH 1905/2296] build(deps): bump golang.org/x/crypto from 0.24.0 to 0.25.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.24.0 to 0.25.0. - [Commits](https://github.com/golang/crypto/compare/v0.24.0...v0.25.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 0f0d601f78..d4ac50a838 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.22 require ( - golang.org/x/crypto v0.24.0 + golang.org/x/crypto v0.25.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.21.0 // indirect +require golang.org/x/sys v0.22.0 // indirect diff --git a/go.sum b/go.sum index e5ba5798aa..1e1f48a722 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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 e1ee9b045c6b0335fc8caf284739b427990184d5 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 8 Jul 2024 17:22:43 +0200 Subject: [PATCH 1906/2296] MIGRATING.md: SchedulerHintOpts --- docs/MIGRATING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 8dcbde5d38..a507820bf5 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -254,7 +254,7 @@ folded into the modified APIs. These are: - `openstack/blockstorage/extensions/schedulerhints` - `SchedulerHintOpts` has been renamed to `SchedulerHints` and moved to + `SchedulerHints` has been renamed to `SchedulerHintOpts` and moved to `openstack/blockstorage/v2/volumes` and `openstack/blockstorage/v3/volumes`. This is now a required argument of `volumes.Create` for both modules. @@ -337,8 +337,8 @@ folded into the modified APIs. These are: - `openstack/compute/v2/extensions/schedulerhints` - `SchedulerHintOpts` has been moved to `openstack/compute/v2/servers` and - renamed to `SchedulerHints`. This is now a required argument of + `SchedulerHints` has been moved to `openstack/compute/v2/servers` and + renamed to `SchedulerHintOpts`. This is now a required argument of `servers.Create`. - `openstack/compute/v2/extensions/serverusage` From 2b979bf0a28b92ab0b3d5077886447583cd2b67c Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 9 Jul 2024 21:05:40 +0200 Subject: [PATCH 1907/2296] [neutron]: introduce Stateful argument for the security groups --- .../networking/v2/extensions/security_test.go | 3 ++ .../v2/extensions/security/groups/requests.go | 34 +++++++++++++++---- .../v2/extensions/security/groups/results.go | 3 ++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/security_test.go b/internal/acceptance/openstack/networking/v2/extensions/security_test.go index 7d0d967e46..670735ec5e 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/security_test.go @@ -20,6 +20,7 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { group, err := CreateSecurityGroup(t, client) th.AssertNoErr(t, err) defer DeleteSecurityGroup(t, client, group.ID) + th.AssertEquals(t, group.Stateful, true) rule, err := CreateSecurityGroupRule(t, client, group.ID) th.AssertNoErr(t, err) @@ -32,6 +33,7 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { updateOpts := groups.UpdateOpts{ Name: name, Description: &description, + Stateful: new(bool), } newGroup, err := groups.Update(context.TODO(), client, group.ID, updateOpts).Extract() @@ -40,6 +42,7 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { tools.PrintResource(t, newGroup) th.AssertEquals(t, newGroup.Name, name) th.AssertEquals(t, newGroup.Description, description) + th.AssertEquals(t, newGroup.Stateful, false) listOpts := groups.ListOpts{} allPages, err := groups.List(client, listOpts).AllPages(context.TODO()) diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go index a45403f13b..a0e509173f 100644 --- a/openstack/networking/v2/extensions/security/groups/requests.go +++ b/openstack/networking/v2/extensions/security/groups/requests.go @@ -7,6 +7,12 @@ import ( "github.com/gophercloud/gophercloud/v2/pagination" ) +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToSecGroupListQuery() (string, error) +} + // ListOpts allows the filtering and sorting of paginated collections through // the API. Filtering is achieved by passing in struct field values that map to // the group attributes you want to see returned. SortKey allows you to @@ -16,6 +22,7 @@ type ListOpts struct { ID string `q:"id"` Name string `q:"name"` Description string `q:"description"` + Stateful *bool `q:"stateful"` TenantID string `q:"tenant_id"` ProjectID string `q:"project_id"` Limit int `q:"limit"` @@ -28,16 +35,25 @@ type ListOpts struct { NotTagsAny string `q:"not-tags-any"` } +// ToPortListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToSecGroupListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // List returns a Pager which allows you to iterate over a collection of // security groups. It accepts a ListOpts struct, which allows you to filter // and sort the returned collection for greater efficiency. -func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager { - q, err := gophercloud.BuildQueryString(&opts) - if err != nil { - return pagination.Pager{Err: err} +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToSecGroupListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query } - u := rootURL(c) + q.String() - return pagination.NewPager(c, u, func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { return SecGroupPage{pagination.LinkedPageBase{PageResult: r}} }) } @@ -63,6 +79,9 @@ type CreateOpts struct { // Describes the security group. Description string `json:"description,omitempty"` + + // Stateful indicates if the security group is stateful or stateless. + Stateful *bool `json:"stateful,omitempty"` } // ToSecGroupCreateMap builds a request body from CreateOpts. @@ -97,6 +116,9 @@ type UpdateOpts struct { // Describes the security group. Description *string `json:"description,omitempty"` + + // Stateful indicates if the security group is stateful or stateless. + Stateful *bool `json:"stateful,omitempty"` } // ToSecGroupUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/networking/v2/extensions/security/groups/results.go b/openstack/networking/v2/extensions/security/groups/results.go index 6f1a4e1620..b3aa2efb48 100644 --- a/openstack/networking/v2/extensions/security/groups/results.go +++ b/openstack/networking/v2/extensions/security/groups/results.go @@ -25,6 +25,9 @@ type SecGroup struct { // traffic entering and leaving the group. Rules []rules.SecGroupRule `json:"security_group_rules"` + // Indicates if the security group is stateful or stateless. + Stateful bool `json:"stateful"` + // TenantID is the project owner of the security group. TenantID string `json:"tenant_id"` From 8be3327ccff96da218bbb2df869f4e71af161385 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 9 Jul 2024 21:18:32 +0200 Subject: [PATCH 1908/2296] [neutron]: introduce Description argument for the portforwarding --- .../networking/v2/extensions/layer3/layer3.go | 2 ++ .../extensions/layer3/portforwardings_test.go | 3 +++ .../layer3/portforwarding/requests.go | 20 +++++++++---------- .../layer3/portforwarding/results.go | 4 ++++ 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go index 1edc2d0cc8..c45bb08069 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/layer3.go @@ -75,7 +75,9 @@ func CreatePortForwarding(t *testing.T, client *gophercloud.ServiceClient, fipID fixedIP := portFixedIPs[0] internalIP := fixedIP.IPAddress + pfDescription := "Test description" createOpts := &portforwarding.CreateOpts{ + Description: pfDescription, Protocol: "tcp", InternalPort: 25, ExternalPort: 2230, diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go index bd9ccf3ad4..3d3f208529 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/portforwardings_test.go @@ -56,6 +56,7 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { pf, err := CreatePortForwarding(t, client, fip.ID, port.ID, port.FixedIPs) th.AssertNoErr(t, err) + th.AssertEquals(t, pf.Description, "Test description") defer DeletePortForwarding(t, client, fip.ID, pf.ID) tools.PrintResource(t, pf) @@ -63,6 +64,7 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { th.AssertNoErr(t, err) updateOpts := portforwarding.UpdateOpts{ + Description: new(string), Protocol: "udp", InternalPort: 30, ExternalPort: 678, @@ -73,6 +75,7 @@ func TestLayer3PortForwardingsCreateDelete(t *testing.T) { newPf, err = portforwarding.Get(context.TODO(), client, fip.ID, pf.ID).Extract() th.AssertNoErr(t, err) + th.AssertEquals(t, newPf.Description, "") allPages, err := portforwarding.List(client, portforwarding.ListOpts{}, fip.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go index 42f90332ac..1b07b6b7c9 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/requests.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/requests.go @@ -18,6 +18,7 @@ type ListOptsBuilder interface { // either `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { ID string `q:"id"` + Description string `q:"description"` InternalPortID string `q:"internal_port_id"` ExternalPort string `q:"external_port"` InternalIPAddress string `q:"internal_ip_address"` @@ -63,6 +64,7 @@ func Get(ctx context.Context, c *gophercloud.ServiceClient, floatingIpId string, // CreateOpts contains all the values needed to create a new port forwarding // resource. All attributes are required. type CreateOpts struct { + Description string `json:"description,omitempty"` InternalPortID string `json:"internal_port_id"` InternalIPAddress string `json:"internal_ip_address"` InternalPort int `json:"internal_port"` @@ -97,22 +99,18 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, floatingIpId stri // UpdateOpts contains the values used when updating a port forwarding resource. type UpdateOpts struct { - InternalPortID string `json:"internal_port_id,omitempty"` - InternalIPAddress string `json:"internal_ip_address,omitempty"` - InternalPort int `json:"internal_port,omitempty"` - ExternalPort int `json:"external_port,omitempty"` - Protocol string `json:"protocol,omitempty"` + Description *string `json:"description,omitempty"` + InternalPortID string `json:"internal_port_id,omitempty"` + InternalIPAddress string `json:"internal_ip_address,omitempty"` + InternalPort int `json:"internal_port,omitempty"` + ExternalPort int `json:"external_port,omitempty"` + Protocol string `json:"protocol,omitempty"` } // ToPortForwardingUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder // interface func (opts UpdateOpts) ToPortForwardingUpdateMap() (map[string]any, error) { - b, err := gophercloud.BuildRequestBody(opts, "port_forwarding") - if err != nil { - return nil, err - } - - return b, nil + return gophercloud.BuildRequestBody(opts, "port_forwarding") } // UpdateOptsBuilder allows extensions to add additional parameters to the diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/results.go b/openstack/networking/v2/extensions/layer3/portforwarding/results.go index 17c5281acc..89e71efd0e 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/results.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/results.go @@ -9,6 +9,10 @@ type PortForwarding struct { // The ID of the floating IP port forwarding ID string `json:"id"` + // A text describing the rule, which helps users to manage/find easily + // theirs rules. + Description string `json:"description"` + // The ID of the Neutron port associated to the floating IP port forwarding. InternalPortID string `json:"internal_port_id"` From 15fab7dca9d8be546d715ada1d545f00a589929a Mon Sep 17 00:00:00 2001 From: Pablo COLSON Date: Mon, 8 Jul 2024 20:21:49 +0200 Subject: [PATCH 1909/2296] [networking]: subnet add field dns_publish_fixed_ip --- openstack/networking/v2/subnets/requests.go | 49 +- openstack/networking/v2/subnets/results.go | 3 + .../v2/subnets/testing/fixtures_test.go | 795 +++++++++--------- 3 files changed, 435 insertions(+), 412 deletions(-) diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go index 20f747b5a9..db597d6864 100644 --- a/openstack/networking/v2/subnets/requests.go +++ b/openstack/networking/v2/subnets/requests.go @@ -20,27 +20,28 @@ type ListOptsBuilder interface { // by a particular subnet attribute. SortDir sets the direction, and is either // `asc' or `desc'. Marker and Limit are used for pagination. type ListOpts struct { - Name string `q:"name"` - Description string `q:"description"` - EnableDHCP *bool `q:"enable_dhcp"` - NetworkID string `q:"network_id"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - IPVersion int `q:"ip_version"` - GatewayIP string `q:"gateway_ip"` - CIDR string `q:"cidr"` - IPv6AddressMode string `q:"ipv6_address_mode"` - IPv6RAMode string `q:"ipv6_ra_mode"` - ID string `q:"id"` - SubnetPoolID string `q:"subnetpool_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"` + Name string `q:"name"` + Description string `q:"description"` + DNSPublishFixedIP *bool `q:"dns_publish_fixed_ip"` + EnableDHCP *bool `q:"enable_dhcp"` + NetworkID string `q:"network_id"` + TenantID string `q:"tenant_id"` + ProjectID string `q:"project_id"` + IPVersion int `q:"ip_version"` + GatewayIP string `q:"gateway_ip"` + CIDR string `q:"cidr"` + IPv6AddressMode string `q:"ipv6_address_mode"` + IPv6RAMode string `q:"ipv6_ra_mode"` + ID string `q:"id"` + SubnetPoolID string `q:"subnetpool_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"` } // ToSubnetListQuery formats a ListOpts into a query string. @@ -123,6 +124,9 @@ type CreateOpts struct { // DNSNameservers are the nameservers to be set via DHCP. DNSNameservers []string `json:"dns_nameservers,omitempty"` + // DNSPublishFixedIP will either enable or disable the publication of fixed IPs to the DNS + DNSPublishFixedIP *bool `json:"dns_publish_fixed_ip,omitempty"` + // ServiceTypes are the service types associated with the subnet. ServiceTypes []string `json:"service_types,omitempty"` @@ -198,6 +202,9 @@ type UpdateOpts struct { // DNSNameservers are the nameservers to be set via DHCP. DNSNameservers *[]string `json:"dns_nameservers,omitempty"` + // DNSPublishFixedIP will either enable or disable the publication of fixed IPs to the DNS + DNSPublishFixedIP *bool `json:"dns_publish_fixed_ip,omitempty"` + // ServiceTypes are the service types associated with the subnet. ServiceTypes *[]string `json:"service_types,omitempty"` diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index a105c75015..cab10623e0 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"` + // Specifies whether the fixed IP addresses are published to the DNS. + DNSPublishFixedIP bool `json:"dns_publish_fixed_ip,omitempty"` + // Service types associated with the subnet. ServiceTypes []string `json:"service_types"` diff --git a/openstack/networking/v2/subnets/testing/fixtures_test.go b/openstack/networking/v2/subnets/testing/fixtures_test.go index e6079975ec..a44f1cc77e 100644 --- a/openstack/networking/v2/subnets/testing/fixtures_test.go +++ b/openstack/networking/v2/subnets/testing/fixtures_test.go @@ -6,90 +6,94 @@ import ( const SubnetListResult = ` { - "subnets": [ - { - "name": "private-subnet", - "enable_dhcp": true, - "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "10.0.0.2", - "end": "10.0.0.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": "10.0.0.1", - "cidr": "10.0.0.0/24", - "id": "08eae331-0402-425a-923c-34f7cfe39c1b" - }, - { - "name": "my_subnet", - "enable_dhcp": true, - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "192.0.0.2", - "end": "192.255.255.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": "192.0.0.1", - "cidr": "192.0.0.0/8", - "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" - }, - { - "name": "my_gatewayless_subnet", - "enable_dhcp": true, - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "192.168.1.2", - "end": "192.168.1.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": null, - "cidr": "192.168.1.0/24", - "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" - }, - { - "name": "my_subnet_with_subnetpool", - "enable_dhcp": false, - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "10.11.12.2", - "end": "10.11.12.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": null, - "cidr": "10.11.12.0/24", - "id": "38186a51-f373-4bbc-838b-6eaa1aa13eac", - "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" - } - ] + "subnets": [ + { + "name": "private-subnet", + "enable_dhcp": true, + "dns_publish_fixed_ip": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "10.0.0.1", + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + }, + { + "name": "my_subnet", + "enable_dhcp": true, + "dns_publish_fixed_ip": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "192.0.0.2", + "end": "192.255.255.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "192.0.0.1", + "cidr": "192.0.0.0/8", + "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + }, + { + "name": "my_gatewayless_subnet", + "enable_dhcp": true, + "dns_publish_fixed_ip": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "192.168.1.2", + "end": "192.168.1.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": null, + "cidr": "192.168.1.0/24", + "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" + }, + { + "name": "my_subnet_with_subnetpool", + "enable_dhcp": false, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.11.12.2", + "end": "10.11.12.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": null, + "cidr": "10.11.12.0/24", + "id": "38186a51-f373-4bbc-838b-6eaa1aa13eac", + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" + } + ] } ` var Subnet1 = subnets.Subnet{ - Name: "private-subnet", - EnableDHCP: true, - NetworkID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", - DNSNameservers: []string{}, + Name: "private-subnet", + EnableDHCP: true, + NetworkID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + TenantID: "26a7980765d0414dbc1fc1f88cdb7e6e", + DNSNameservers: []string{}, + DNSPublishFixedIP: true, AllocationPools: []subnets.AllocationPool{ { Start: "10.0.0.2", @@ -104,11 +108,13 @@ var Subnet1 = subnets.Subnet{ } var Subnet2 = subnets.Subnet{ - Name: "my_subnet", - EnableDHCP: true, - NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", - TenantID: "4fd44f30292945e481c7b8a0c8908869", - DNSNameservers: []string{}, + Name: "my_subnet", + EnableDHCP: true, + + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", + TenantID: "4fd44f30292945e481c7b8a0c8908869", + DNSNameservers: []string{}, + DNSPublishFixedIP: true, AllocationPools: []subnets.AllocationPool{ { Start: "192.0.0.2", @@ -123,11 +129,12 @@ var Subnet2 = subnets.Subnet{ } var Subnet3 = subnets.Subnet{ - Name: "my_gatewayless_subnet", - EnableDHCP: true, - NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", - TenantID: "4fd44f30292945e481c7b8a0c8908869", - DNSNameservers: []string{}, + Name: "my_gatewayless_subnet", + EnableDHCP: true, + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", + TenantID: "4fd44f30292945e481c7b8a0c8908869", + DNSNameservers: []string{}, + DNSPublishFixedIP: true, AllocationPools: []subnets.AllocationPool{ { Start: "192.168.1.2", @@ -142,11 +149,12 @@ var Subnet3 = subnets.Subnet{ } var Subnet4 = subnets.Subnet{ - Name: "my_subnet_with_subnetpool", - EnableDHCP: false, - NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", - TenantID: "4fd44f30292945e481c7b8a0c8908869", - DNSNameservers: []string{}, + Name: "my_subnet_with_subnetpool", + EnableDHCP: false, + NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a23", + TenantID: "4fd44f30292945e481c7b8a0c8908869", + DNSNameservers: []string{}, + DNSPublishFixedIP: false, AllocationPools: []subnets.AllocationPool{ { Start: "10.11.12.2", @@ -163,409 +171,414 @@ var Subnet4 = subnets.Subnet{ const SubnetGetResult = ` { - "subnet": { - "name": "my_subnet", - "enable_dhcp": true, - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "192.0.0.2", - "end": "192.255.255.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": "192.0.0.1", - "cidr": "192.0.0.0/8", - "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b", - "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" - } + "subnet": { + "name": "my_subnet", + "enable_dhcp": true, + "dns_publish_fixed_ip": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "192.0.0.2", + "end": "192.255.255.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "192.0.0.1", + "cidr": "192.0.0.0/8", + "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b", + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" + } } ` const SubnetCreateRequest = ` { - "subnet": { - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "ip_version": 4, - "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", - "end": "192.168.199.254" - } - ], - "host_routes": [{"destination":"","nexthop": "bar"}], - "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" - } + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ip_version": 4, + "dns_publish_fixed_ip": true, + "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", + "end": "192.168.199.254" + } + ], + "host_routes": [{"destination":"","nexthop": "bar"}], + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" + } } ` const SubnetCreateResult = ` { - "subnet": { - "name": "", - "enable_dhcp": true, - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "dns_nameservers": ["foo"], - "service_types": ["network:routed"], - "allocation_pools": [ - { - "start": "192.168.199.2", - "end": "192.168.199.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": "192.168.199.1", - "cidr": "192.168.199.0/24", - "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126", - "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" - } + "subnet": { + "name": "", + "enable_dhcp": true, + "dns_publish_fixed_ip": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": ["foo"], + "service_types": ["network:routed"], + "allocation_pools": [ + { + "start": "192.168.199.2", + "end": "192.168.199.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "192.168.199.1", + "cidr": "192.168.199.0/24", + "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126", + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" + } } ` const SubnetCreateWithNoGatewayRequest = ` { - "subnet": { - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", - "ip_version": 4, - "cidr": "192.168.1.0/24", - "gateway_ip": null, - "allocation_pools": [ - { - "start": "192.168.1.2", - "end": "192.168.1.254" - } - ] - } + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", + "ip_version": 4, + "cidr": "192.168.1.0/24", + "gateway_ip": null, + "allocation_pools": [ + { + "start": "192.168.1.2", + "end": "192.168.1.254" + } + ] + } } ` const SubnetCreateWithNoGatewayResponse = ` { - "subnet": { - "name": "", - "enable_dhcp": true, - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "allocation_pools": [ - { - "start": "192.168.1.2", - "end": "192.168.1.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": null, - "cidr": "192.168.1.0/24", - "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" - } + "subnet": { + "name": "", + "enable_dhcp": true, + "dns_publish_fixed_ip": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "allocation_pools": [ + { + "start": "192.168.1.2", + "end": "192.168.1.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": null, + "cidr": "192.168.1.0/24", + "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" + } } ` const SubnetCreateWithDefaultGatewayRequest = ` { - "subnet": { - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", - "ip_version": 4, - "cidr": "192.168.1.0/24", - "allocation_pools": [ - { - "start": "192.168.1.2", - "end": "192.168.1.254" - } - ] - } + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", + "ip_version": 4, + "cidr": "192.168.1.0/24", + "allocation_pools": [ + { + "start": "192.168.1.2", + "end": "192.168.1.254" + } + ] + } } ` const SubnetCreateWithDefaultGatewayResponse = ` { - "subnet": { - "name": "", - "enable_dhcp": true, - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "allocation_pools": [ - { - "start": "192.168.1.2", - "end": "192.168.1.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": "192.168.1.1", - "cidr": "192.168.1.0/24", - "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" - } + "subnet": { + "name": "", + "enable_dhcp": true, + "dns_publish_fixed_ip": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a23", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "allocation_pools": [ + { + "start": "192.168.1.2", + "end": "192.168.1.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "192.168.1.1", + "cidr": "192.168.1.0/24", + "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0c" + } } ` const SubnetCreateWithIPv6RaAddressModeRequest = ` { - "subnet": { - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "ip_version": 6, - "gateway_ip": "2001:db8:0:a::1", - "cidr": "2001:db8:0:a:0:0:0:0/64", - "ipv6_address_mode": "slaac", - "ipv6_ra_mode": "slaac" - } + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ip_version": 6, + "gateway_ip": "2001:db8:0:a::1", + "cidr": "2001:db8:0:a:0:0:0:0/64", + "ipv6_address_mode": "slaac", + "ipv6_ra_mode": "slaac" + } } ` const SubnetCreateWithIPv6RaAddressModeResponse = ` { - "subnet": { - "name": "", - "enable_dhcp": true, - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "dns_nameservers": [], - "host_routes": [], - "ip_version": 6, - "gateway_ip": "2001:db8:0:a::1", - "cidr": "2001:db8:0:a:0:0:0:0/64", - "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126", - "ipv6_address_mode": "slaac", - "ipv6_ra_mode": "slaac" - } + "subnet": { + "name": "", + "enable_dhcp": true, + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "dns_nameservers": [], + "host_routes": [], + "ip_version": 6, + "gateway_ip": "2001:db8:0:a::1", + "cidr": "2001:db8:0:a:0:0:0:0/64", + "id": "3b80198d-4f7b-4f77-9ef5-774d54e17126", + "ipv6_address_mode": "slaac", + "ipv6_ra_mode": "slaac" + } } ` const SubnetCreateRequestWithNoCIDR = ` { - "subnet": { - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "ip_version": 4, - "dns_nameservers": ["foo"], - "host_routes": [{"destination":"","nexthop": "bar"}], - "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" - } + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ip_version": 4, + "dns_nameservers": ["foo"], + "host_routes": [{"destination":"","nexthop": "bar"}], + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b" + } } ` const SubnetCreateRequestWithPrefixlen = ` { - "subnet": { - "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", - "ip_version": 4, - "dns_nameservers": ["foo"], - "host_routes": [{"destination":"","nexthop": "bar"}], - "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b", - "prefixlen": 12 - } + "subnet": { + "network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ip_version": 4, + "dns_nameservers": ["foo"], + "host_routes": [{"destination":"","nexthop": "bar"}], + "subnetpool_id": "b80340c7-9960-4f67-a99c-02501656284b", + "prefixlen": 12 + } } ` const SubnetUpdateRequest = ` { - "subnet": { - "name": "my_new_subnet", - "dns_nameservers": ["foo"], - "host_routes": [{"destination":"","nexthop": "bar"}] - } + "subnet": { + "name": "my_new_subnet", + "dns_nameservers": ["foo"], + "host_routes": [{"destination":"","nexthop": "bar"}] + } } ` const SubnetUpdateResponse = ` { - "subnet": { - "name": "my_new_subnet", - "enable_dhcp": true, - "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "10.0.0.2", - "end": "10.0.0.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": "10.0.0.1", - "cidr": "10.0.0.0/24", - "id": "08eae331-0402-425a-923c-34f7cfe39c1b" - } + "subnet": { + "name": "my_new_subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "10.0.0.1", + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + } } ` const SubnetUpdateGatewayRequest = ` { - "subnet": { - "name": "my_new_subnet", - "gateway_ip": "10.0.0.1" - } + "subnet": { + "name": "my_new_subnet", + "gateway_ip": "10.0.0.1" + } } ` const SubnetUpdateGatewayResponse = ` { - "subnet": { - "name": "my_new_subnet", - "enable_dhcp": true, - "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "10.0.0.2", - "end": "10.0.0.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": "10.0.0.1", - "cidr": "10.0.0.0/24", - "id": "08eae331-0402-425a-923c-34f7cfe39c1b" - } + "subnet": { + "name": "my_new_subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "10.0.0.1", + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + } } ` const SubnetUpdateRemoveGatewayRequest = ` { - "subnet": { - "name": "my_new_subnet", - "gateway_ip": null - } + "subnet": { + "name": "my_new_subnet", + "gateway_ip": null + } } ` const SubnetUpdateRemoveGatewayResponse = ` { - "subnet": { - "name": "my_new_subnet", - "enable_dhcp": true, - "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "10.0.0.2", - "end": "10.0.0.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": null, - "cidr": "10.0.0.0/24", - "id": "08eae331-0402-425a-923c-34f7cfe39c1b" - } + "subnet": { + "name": "my_new_subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": null, + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + } } ` const SubnetUpdateHostRoutesRequest = ` { - "subnet": { - "name": "my_new_subnet", - "host_routes": [ - { - "destination": "192.168.1.1/24", - "nexthop": "bar" - } - ] - } + "subnet": { + "name": "my_new_subnet", + "host_routes": [ + { + "destination": "192.168.1.1/24", + "nexthop": "bar" + } + ] + } } ` const SubnetUpdateHostRoutesResponse = ` { - "subnet": { - "name": "my_new_subnet", - "enable_dhcp": true, - "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "10.0.0.2", - "end": "10.0.0.254" - } - ], - "ip_version": 4, - "gateway_ip": "10.0.0.1", - "host_routes": [ - { - "destination": "192.168.1.1/24", - "nexthop": "bar" - } - ], - "cidr": "10.0.0.0/24", - "id": "08eae331-0402-425a-923c-34f7cfe39c1b" - } + "subnet": { + "name": "my_new_subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "ip_version": 4, + "gateway_ip": "10.0.0.1", + "host_routes": [ + { + "destination": "192.168.1.1/24", + "nexthop": "bar" + } + ], + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + } } ` const SubnetUpdateRemoveHostRoutesRequest = ` { - "subnet": { - "host_routes": [] - } + "subnet": { + "host_routes": [] + } } ` const SubnetUpdateRemoveHostRoutesResponse = ` { - "subnet": { - "name": "my_new_subnet", - "enable_dhcp": true, - "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "10.0.0.2", - "end": "10.0.0.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": null, - "cidr": "10.0.0.0/24", - "id": "08eae331-0402-425a-923c-34f7cfe39c1b" - } + "subnet": { + "name": "my_new_subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.0.0.2", + "end": "10.0.0.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": null, + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + } } ` const SubnetUpdateAllocationPoolRequest = ` { - "subnet": { - "name": "my_new_subnet", - "allocation_pools": [ - { - "start": "10.1.0.2", - "end": "10.1.0.254" - } - ] - } + "subnet": { + "name": "my_new_subnet", + "allocation_pools": [ + { + "start": "10.1.0.2", + "end": "10.1.0.254" + } + ] + } } ` const SubnetUpdateAllocationPoolResponse = ` { - "subnet": { - "name": "my_new_subnet", - "enable_dhcp": true, - "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", - "dns_nameservers": [], - "allocation_pools": [ - { - "start": "10.1.0.2", - "end": "10.1.0.254" - } - ], - "host_routes": [], - "ip_version": 4, - "gateway_ip": "10.0.0.1", - "cidr": "10.0.0.0/24", - "id": "08eae331-0402-425a-923c-34f7cfe39c1b" - } + "subnet": { + "name": "my_new_subnet", + "enable_dhcp": true, + "network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", + "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", + "dns_nameservers": [], + "allocation_pools": [ + { + "start": "10.1.0.2", + "end": "10.1.0.254" + } + ], + "host_routes": [], + "ip_version": 4, + "gateway_ip": "10.0.0.1", + "cidr": "10.0.0.0/24", + "id": "08eae331-0402-425a-923c-34f7cfe39c1b" + } } ` From 0514fa6f5cb6c912222e365f94bd9cdf90210eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?p=C3=BDrus?= Date: Wed, 10 Jul 2024 10:17:02 +0200 Subject: [PATCH 1910/2296] Update openstack/networking/v2/subnets/results.go --- openstack/networking/v2/subnets/results.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index cab10623e0..7d5ba13cc5 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -84,7 +84,7 @@ type Subnet struct { DNSNameservers []string `json:"dns_nameservers"` // Specifies whether the fixed IP addresses are published to the DNS. - DNSPublishFixedIP bool `json:"dns_publish_fixed_ip,omitempty"` + DNSPublishFixedIP bool `json:"dns_publish_fixed_ip"` // Service types associated with the subnet. ServiceTypes []string `json:"service_types"` From 61b73cd5bcfaec157cc0358c0f1d7db03f0d343d Mon Sep 17 00:00:00 2001 From: Pablo COLSON Date: Wed, 10 Jul 2024 14:34:18 +0200 Subject: [PATCH 1911/2296] fix(tests): forgot to set DNSPublishFixedIP in requests_test.go --- openstack/networking/v2/subnets/testing/requests_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index c4bac8dc0c..e6f140ebe4 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -108,6 +108,7 @@ func TestCreate(t *testing.T) { }) var gatewayIP = "192.168.199.1" + var dnsPublishFixedIP = true opts := subnets.CreateOpts{ NetworkID: "d32019d3-bc6e-4319-9c1d-6722fc136a22", IPVersion: 4, @@ -120,6 +121,7 @@ func TestCreate(t *testing.T) { }, }, DNSNameservers: []string{"foo"}, + DNSPublishFixedIP: &dnsPublishFixedIP, ServiceTypes: []string{"network:routed"}, HostRoutes: []subnets.HostRoute{ {NextHop: "bar"}, @@ -130,6 +132,7 @@ func TestCreate(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") + th.AssertEquals(t, s.DNSPublishFixedIP, true) th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") @@ -320,6 +323,7 @@ func TestCreateWithNoCIDR(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") + th.AssertEquals(t, s.DNSPublishFixedIP, true) th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") @@ -369,6 +373,7 @@ func TestCreateWithPrefixlen(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") + th.AssertEquals(t, s.DNSPublishFixedIP, true) th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") From 0337faa89cbe3b7e4911d4bb67e8bf5c71307724 Mon Sep 17 00:00:00 2001 From: Pablo COLSON Date: Wed, 10 Jul 2024 14:47:39 +0200 Subject: [PATCH 1912/2296] fix: formating --- openstack/networking/v2/subnets/testing/requests_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index e6f140ebe4..1df6fd1ce3 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -120,9 +120,9 @@ func TestCreate(t *testing.T) { End: "192.168.199.254", }, }, - DNSNameservers: []string{"foo"}, + DNSNameservers: []string{"foo"}, DNSPublishFixedIP: &dnsPublishFixedIP, - ServiceTypes: []string{"network:routed"}, + ServiceTypes: []string{"network:routed"}, HostRoutes: []subnets.HostRoute{ {NextHop: "bar"}, }, @@ -373,7 +373,7 @@ func TestCreateWithPrefixlen(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, s.Name, "") - th.AssertEquals(t, s.DNSPublishFixedIP, true) + th.AssertEquals(t, s.DNSPublishFixedIP, true) th.AssertEquals(t, s.EnableDHCP, true) th.AssertEquals(t, s.NetworkID, "d32019d3-bc6e-4319-9c1d-6722fc136a22") th.AssertEquals(t, s.TenantID, "4fd44f30292945e481c7b8a0c8908869") From cde7e064a57d012ebc6b9eb2bef857c081d144b8 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 15 Jul 2024 14:31:48 +0200 Subject: [PATCH 1913/2296] [tests]: bump golang to 1.22 --- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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 ddaeb1fd14..c4aa44d8f5 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -91,7 +91,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index cc134dfb91..8157156702 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -43,7 +43,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index daf2bb31ce..dceb7596a3 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -42,7 +42,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 0065891bdb..0e1bab61e9 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -42,7 +42,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index d42add38cb..df45bae93e 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -59,7 +59,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 9145ae0198..a20d5c1f85 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -43,7 +43,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 6accf2828c..09d03db28b 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -47,7 +47,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index ebe76b40a0..f6f083bc62 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -40,7 +40,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 70ab27eeef..554c89fcc3 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -40,7 +40,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index de2c95c503..9cdd3cbdf1 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -42,7 +42,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index b93014dadd..c40e8ee2c4 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -43,7 +43,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 6703dbb835..db990e0d81 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -43,7 +43,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index feb896eb45..480a493d88 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -57,7 +57,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index ebb63e8a41..c0b204a1b4 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -46,7 +46,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 8e7bc6b8aa..0f627474b5 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -42,7 +42,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index d407413ac0..640df2c1d5 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -40,7 +40,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index e46a39f0ee..e4d8bbea39 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -56,7 +56,7 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.20' + go-version: '^1.22' - name: Run Gophercloud acceptance tests run: ./script/acceptancetest env: From 8dc1948d352b6a4300bfb05a8aeffe8bb2330065 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 15 Jul 2024 14:40:02 +0200 Subject: [PATCH 1914/2296] [gophercloud v2]: introduce v2 branch with a backport functionality --- .github/labels.yaml | 3 ++ .github/workflows/backport_v2.yaml | 67 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 .github/workflows/backport_v2.yaml diff --git a/.github/labels.yaml b/.github/labels.yaml index 8a908009e2..fdb0a3fc96 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -1,6 +1,9 @@ - color: '30ABB9' description: This PR will be backported to v1 name: backport-v1 +- color: 'E99695' + description: This PR will be backported to v2 + name: backport-v2 - color: '0366d6' description: Pull requests that update a dependency file name: dependencies diff --git a/.github/workflows/backport_v2.yaml b/.github/workflows/backport_v2.yaml new file mode 100644 index 0000000000..decfc344c2 --- /dev/null +++ b/.github/workflows/backport_v2.yaml @@ -0,0 +1,67 @@ +name: Pull Request backporting + +on: + pull_request_target: + types: + - closed + - labeled + +jobs: + backporting: + name: "Backporting" + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + && contains(github.event.pull_request.labels.*.name, 'backport-v2') + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport-v2') + ) + ) + runs-on: ubuntu-latest + steps: + - name: Generate a token from the gophercloud-backport-bot github-app + id: generate_token + uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc + with: + app_id: ${{ secrets.BACKPORT_APP_ID }} + private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} + + - name: Backporting + if: > + contains(github.event.pull_request.labels.*.name, 'semver:patch') + || contains(github.event.pull_request.labels.*.name, 'semver:minor') + || contains(github.event.label.name, 'semver:patch') + || contains(github.event.label.name, 'semver:minor') + uses: kiegroup/git-backporting@c22286f85e0a14ebb66755b381163ab9cd8310fa + with: + target-branch: v2 + pull-request: ${{ github.event.pull_request.url }} + auth: ${{ steps.generate_token.outputs.token }} + no-squash: true + strategy-option: find-renames + + - name: Report failure + if: failure() + run: gh issue comment "$NUMBER" --body "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + BODY: > + Failed to backport PR to `v2` branch. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. + + - name: Report an error if backport unsupported labels + if: > + contains(github.event.pull_request.labels.*.name, 'semver:major') + || contains(github.event.pull_request.labels.*.name, 'semver:unknown') + || contains(github.event.label.name, 'semver:major') + || contains(github.event.label.name, 'semver:unknown') + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 + with: + message: | + Labels `semver-major` or `semver-unknown` can not trigger backports. + The PR has to be labeled `semver-patch` or `semver-minor`. From 2a17e649cce098cc29450ed61d537eafb3075f15 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 16 Jul 2024 15:39:40 +0200 Subject: [PATCH 1915/2296] clouds: Parse trust_id from clouds.yaml --- openstack/config/clouds/clouds.go | 20 ++++++++++++++------ openstack/config/clouds/types.go | 3 +++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index e890b5b5ce..e2310fbc10 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -148,6 +148,13 @@ func Parse(opts ...ParseOption) (gophercloud.AuthOptions, gophercloud.EndpointOp endpointType := coalesce(options.endpointType, cloud.EndpointType, cloud.Interface) + var scope *gophercloud.AuthScope + if trustID := cloud.AuthInfo.TrustID; trustID != "" { + scope = &gophercloud.AuthScope{ + TrustID: trustID, + } + } + return gophercloud.AuthOptions{ IdentityEndpoint: coalesce(options.authURL, cloud.AuthInfo.AuthURL), Username: coalesce(options.username, cloud.AuthInfo.Username), @@ -158,7 +165,7 @@ func Parse(opts ...ParseOption) (gophercloud.AuthOptions, gophercloud.EndpointOp TenantID: coalesce(options.projectID, cloud.AuthInfo.ProjectID), TenantName: coalesce(options.projectName, cloud.AuthInfo.ProjectName), TokenID: coalesce(options.token, cloud.AuthInfo.Token), - Scope: options.scope, + Scope: coalesce(options.scope, scope), ApplicationCredentialID: coalesce(options.applicationCredentialID, cloud.AuthInfo.ApplicationCredentialID), ApplicationCredentialName: coalesce(options.applicationCredentialName, cloud.AuthInfo.ApplicationCredentialName), ApplicationCredentialSecret: coalesce(options.applicationCredentialSecret, cloud.AuthInfo.ApplicationCredentialSecret), @@ -182,15 +189,16 @@ func computeAvailability(endpointType string) gophercloud.Availability { return gophercloud.AvailabilityPublic } -// coalesce returns the first argument that is not the empty string, or the -// empty string. -func coalesce(items ...string) string { +// coalesce returns the first argument that is not the zero value for its type, +// or the zero value for its type. +func coalesce[T comparable](items ...T) T { + var t T for _, item := range items { - if item != "" { + if item != t { return item } } - return "" + return t } // mergeClouds merges two Clouds recursively (the AuthInfo also gets merged). diff --git a/openstack/config/clouds/types.go b/openstack/config/clouds/types.go index fc2da75925..93fc093268 100644 --- a/openstack/config/clouds/types.go +++ b/openstack/config/clouds/types.go @@ -123,6 +123,9 @@ type AuthInfo struct { // been specified and a domain is required for scope. DefaultDomain string `yaml:"default_domain,omitempty" json:"default_domain,omitempty"` + // TrustID is the ID of the trust to use as a trustee. + TrustID string `yaml:"trust_id,omitempty" json:"trust_id,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 From 0554098baf530e25ab12dbb8ae9f4960d4267972 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 16 Jul 2024 14:49:49 +0100 Subject: [PATCH 1916/2296] CI: Append job ID to artifact name Otherwise you end up with lots of duplicate 'functional-{service}-{branch}' tarballs when debugging. Signed-off-by: Stephen Finucane --- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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 c4aa44d8f5..c383c47286 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -107,5 +107,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-baremetal-${{ matrix.name }} + name: functional-baremetal-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 8157156702..810a63868c 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -57,5 +57,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-basic-${{ matrix.name }} + name: functional-basic-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index dceb7596a3..42ac4edb4d 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -56,5 +56,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-blockstorage-${{ matrix.name }} + name: functional-blockstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 0e1bab61e9..6cd9962c1c 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -56,5 +56,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-compute-${{ matrix.name }} + name: functional-compute-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index df45bae93e..a5f25b2d68 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -73,5 +73,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-containerinfra-${{ matrix.name }} + name: functional-containerinfra-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index a20d5c1f85..1a06d3d80e 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -57,5 +57,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-dns-${{ matrix.name }} + name: functional-dns-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 09d03db28b..5beeb4da1f 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -61,5 +61,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-fwaas_v2-${{ matrix.name }} + name: functional-fwaas_v2-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index f6f083bc62..f29da5323d 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -54,5 +54,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-identity-${{ matrix.name }} + name: functional-identity-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 554c89fcc3..1dde9ddb16 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -54,5 +54,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-image-${{ matrix.name }} + name: functional-image-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 9cdd3cbdf1..f20268060e 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -56,5 +56,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-keymanager-${{ matrix.name }} + name: functional-keymanager-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index c40e8ee2c4..a0fb56f1d2 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -57,5 +57,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-loadbalancer-${{ matrix.name }} + name: functional-loadbalancer-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index db990e0d81..ebf136bd59 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -57,5 +57,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-messaging-${{ matrix.name }} + name: functional-messaging-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 480a493d88..a5dc220076 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -71,5 +71,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-networking-${{ matrix.name }} + name: functional-networking-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index c0b204a1b4..6f12439d72 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -60,5 +60,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-objectstorage-${{ matrix.name }} + name: functional-objectstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 0f627474b5..0f397a7979 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -56,5 +56,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-orchestration-${{ matrix.name }} + name: functional-orchestration-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 640df2c1d5..0a4b1f2979 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -54,5 +54,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-placement-${{ matrix.name }} + name: functional-placement-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index e4d8bbea39..92182f5219 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -70,5 +70,5 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: functional-sharedfilesystems-${{ matrix.name }} + name: functional-sharedfilesystems-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* From ad2fed21efddb186fb4fc318b4ba08b41a13487c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Tue, 16 Jul 2024 16:53:25 +0200 Subject: [PATCH 1917/2296] Consolidate backporting workflows My main concern was to differentiate between the v1 and the v2 workflows, but it also seems better to only trigger a single workflow with individual jobs for each branch. --- .../{backport_v1.yaml => backport.yaml} | 63 ++++++++++++++++- .github/workflows/backport_v2.yaml | 67 ------------------- 2 files changed, 61 insertions(+), 69 deletions(-) rename .github/workflows/{backport_v1.yaml => backport.yaml} (50%) delete mode 100644 .github/workflows/backport_v2.yaml diff --git a/.github/workflows/backport_v1.yaml b/.github/workflows/backport.yaml similarity index 50% rename from .github/workflows/backport_v1.yaml rename to .github/workflows/backport.yaml index 40714a4582..c9ed20eb1d 100644 --- a/.github/workflows/backport_v1.yaml +++ b/.github/workflows/backport.yaml @@ -7,8 +7,8 @@ on: - labeled jobs: - backporting: - name: "Backporting" + backport_v1: + name: "Backport to v1" # Only react to merged PRs for security reasons. # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. if: > @@ -65,3 +65,62 @@ jobs: message: | Labels `semver-major` or `semver-unknown` can not trigger backports. The PR has to be labeled `semver-patch` or `semver-minor`. + + backport_v2: + name: "Backport to v2" + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + && contains(github.event.pull_request.labels.*.name, 'backport-v2') + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport-v2') + ) + ) + runs-on: ubuntu-latest + steps: + - name: Generate a token from the gophercloud-backport-bot github-app + id: generate_token + uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc + with: + app_id: ${{ secrets.BACKPORT_APP_ID }} + private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} + + - name: Backporting + if: > + contains(github.event.pull_request.labels.*.name, 'semver:patch') + || contains(github.event.pull_request.labels.*.name, 'semver:minor') + || contains(github.event.label.name, 'semver:patch') + || contains(github.event.label.name, 'semver:minor') + uses: kiegroup/git-backporting@c22286f85e0a14ebb66755b381163ab9cd8310fa + with: + target-branch: v2 + pull-request: ${{ github.event.pull_request.url }} + auth: ${{ steps.generate_token.outputs.token }} + no-squash: true + strategy-option: find-renames + + - name: Report failure + if: failure() + run: gh issue comment "$NUMBER" --body "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + BODY: > + Failed to backport PR to `v2` branch. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. + + - name: Report an error if backport unsupported labels + if: > + contains(github.event.pull_request.labels.*.name, 'semver:major') + || contains(github.event.pull_request.labels.*.name, 'semver:unknown') + || contains(github.event.label.name, 'semver:major') + || contains(github.event.label.name, 'semver:unknown') + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 + with: + message: | + Labels `semver-major` or `semver-unknown` can not trigger backports. + The PR has to be labeled `semver-patch` or `semver-minor`. diff --git a/.github/workflows/backport_v2.yaml b/.github/workflows/backport_v2.yaml deleted file mode 100644 index decfc344c2..0000000000 --- a/.github/workflows/backport_v2.yaml +++ /dev/null @@ -1,67 +0,0 @@ -name: Pull Request backporting - -on: - pull_request_target: - types: - - closed - - labeled - -jobs: - backporting: - name: "Backporting" - # Only react to merged PRs for security reasons. - # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. - if: > - github.event.pull_request.merged - && ( - github.event.action == 'closed' - && contains(github.event.pull_request.labels.*.name, 'backport-v2') - || ( - github.event.action == 'labeled' - && contains(github.event.label.name, 'backport-v2') - ) - ) - runs-on: ubuntu-latest - steps: - - name: Generate a token from the gophercloud-backport-bot github-app - id: generate_token - uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc - with: - app_id: ${{ secrets.BACKPORT_APP_ID }} - private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} - - - name: Backporting - if: > - contains(github.event.pull_request.labels.*.name, 'semver:patch') - || contains(github.event.pull_request.labels.*.name, 'semver:minor') - || contains(github.event.label.name, 'semver:patch') - || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@c22286f85e0a14ebb66755b381163ab9cd8310fa - with: - target-branch: v2 - pull-request: ${{ github.event.pull_request.url }} - auth: ${{ steps.generate_token.outputs.token }} - no-squash: true - strategy-option: find-renames - - - name: Report failure - if: failure() - run: gh issue comment "$NUMBER" --body "$BODY" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_REPO: ${{ github.repository }} - NUMBER: ${{ github.event.pull_request.number }} - BODY: > - Failed to backport PR to `v2` branch. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. - - - name: Report an error if backport unsupported labels - if: > - contains(github.event.pull_request.labels.*.name, 'semver:major') - || contains(github.event.pull_request.labels.*.name, 'semver:unknown') - || contains(github.event.label.name, 'semver:major') - || contains(github.event.label.name, 'semver:unknown') - uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 - with: - message: | - Labels `semver-major` or `semver-unknown` can not trigger backports. - The PR has to be labeled `semver-patch` or `semver-minor`. From 1b3ea63662ba4d2de59b290f99c965353d95fa12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Tue, 16 Jul 2024 18:09:07 +0200 Subject: [PATCH 1918/2296] Bump git-backporting This should solve the cherry-pick order when backporting. --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index c9ed20eb1d..ff00ccef45 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -36,7 +36,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@c22286f85e0a14ebb66755b381163ab9cd8310fa + uses: kiegroup/git-backporting@cb3473d7c9de66cb7bec188f08538e134cdc4bc0 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -95,7 +95,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@c22286f85e0a14ebb66755b381163ab9cd8310fa + uses: kiegroup/git-backporting@cb3473d7c9de66cb7bec188f08538e134cdc4bc0 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From a7f6d08242a9dc506c4520e1b099f39e86bcdf40 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 17 Jul 2024 11:14:24 +0100 Subject: [PATCH 1919/2296] tests: Remove service update integration test Go runs tests in parallel for different packages in parallel. The service update test was disabling and re-enabling all compute services, and if this ran at the same time as another test that created a server then the server creation (and therefore the test) would fail. There does not appear to be any way to resolve this. We could run all integration tests serially instead, but this would be a backwards steps that will massively increase test run time. We could also rewrite the test to only disable 1 of N nova-compute services, but that would require us to fence off this compute node in all other tests using non-default parameters to server create. Given the lack of better options, we simply remove the test and assume our testing to date on this method has been good enough and unit tests will be enough to carry us forward. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/services_test.go | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/internal/acceptance/openstack/compute/v2/services_test.go b/internal/acceptance/openstack/compute/v2/services_test.go index 155255fb43..30b1613aa5 100644 --- a/internal/acceptance/openstack/compute/v2/services_test.go +++ b/internal/acceptance/openstack/compute/v2/services_test.go @@ -64,60 +64,3 @@ func TestServicesListWithOpts(t *testing.T) { th.AssertEquals(t, found, true) } - -func TestServicesUpdate(t *testing.T) { - clients.RequireAdmin(t) - - client, err := clients.NewComputeV2Client() - th.AssertNoErr(t, err) - - listOpts := services.ListOpts{ - Binary: "nova-compute", - } - - client.Microversion = "2.53" - allPages, err := services.List(client, listOpts).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allServices, err := services.ExtractServices(allPages) - th.AssertNoErr(t, err) - - // disable all services - for _, service := range allServices { - opts := services.UpdateOpts{ - Status: services.ServiceDisabled, - } - updated, err := services.Update(context.TODO(), client, service.ID, opts).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, updated.ID, service.ID) - } - - // verify all services are disabled - allPages, err = services.List(client, listOpts).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allServices, err = services.ExtractServices(allPages) - th.AssertNoErr(t, err) - - for _, service := range allServices { - th.AssertEquals(t, service.Status, "disabled") - } - - // reenable all services - allPages, err = services.List(client, listOpts).AllPages(context.TODO()) - th.AssertNoErr(t, err) - - allServices, err = services.ExtractServices(allPages) - th.AssertNoErr(t, err) - - for _, service := range allServices { - opts := services.UpdateOpts{ - Status: services.ServiceEnabled, - } - updated, err := services.Update(context.TODO(), client, service.ID, opts).Extract() - th.AssertNoErr(t, err) - - th.AssertEquals(t, updated.ID, service.ID) - } -} From e734b3b48985d31d56cd54e1f98f70f81d0f95b7 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 17 Jul 2024 12:23:07 +0200 Subject: [PATCH 1920/2296] actions: Add a label to hold merging Create the label 'hold' and add a check that fails when the PR has that label. --- .github/labels.yaml | 3 +++ .github/workflows/check-pr-labels.yaml | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 .github/workflows/check-pr-labels.yaml diff --git a/.github/labels.yaml b/.github/labels.yaml index fdb0a3fc96..479fb5f3a7 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -25,3 +25,6 @@ - color: 'EC0101' description: Unable to figure out the semver type name: semver:unknown +- color: 'D73A4A' + description: Do not merge + name: hold diff --git a/.github/workflows/check-pr-labels.yaml b/.github/workflows/check-pr-labels.yaml new file mode 100644 index 0000000000..ea8068f602 --- /dev/null +++ b/.github/workflows/check-pr-labels.yaml @@ -0,0 +1,18 @@ +name: Ready +on: + pull_request_target: + types: + - opened + - labeled + +jobs: + hold: + if: github.event.pull_request.merged == false + runs-on: ubuntu-latest + steps: + - if: > + contains(github.event.pull_request.labels.*.name, 'hold') + run: 'false' + - if: > + !contains(github.event.pull_request.labels.*.name, 'hold') + run: 'true' From 9e8bf12290844c4fdfe171a83de0f0e3c1ce5a9f Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 17 Jul 2024 13:33:16 +0200 Subject: [PATCH 1921/2296] Block `semver:minor` backports to v1 With `v2` out, we are freezing `v1` to only receive bug fixes. Backports to `v1` can be forced by manually creating a PR against `v1`. --- .github/workflows/backport.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index ff00ccef45..f0c9b55300 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -33,9 +33,7 @@ jobs: - name: Backporting if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') - || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') - || contains(github.event.label.name, 'semver:minor') uses: kiegroup/git-backporting@cb3473d7c9de66cb7bec188f08538e134cdc4bc0 with: target-branch: v1 @@ -57,14 +55,16 @@ jobs: - name: Report an error if backport unsupported labels if: > contains(github.event.pull_request.labels.*.name, 'semver:major') + || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.pull_request.labels.*.name, 'semver:unknown') || contains(github.event.label.name, 'semver:major') + || contains(github.event.label.name, 'semver:minor') || contains(github.event.label.name, 'semver:unknown') uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 with: message: | - Labels `semver-major` or `semver-unknown` can not trigger backports. - The PR has to be labeled `semver-patch` or `semver-minor`. + Labels `semver-major`, `semver-minor` and `semver-unknown` block + backports to the legacy branch `v1`. backport_v2: name: "Backport to v2" @@ -122,5 +122,5 @@ jobs: uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 with: message: | - Labels `semver-major` or `semver-unknown` can not trigger backports. - The PR has to be labeled `semver-patch` or `semver-minor`. + Labels `semver-major` and `semver-unknown` block backports to the + stable branch `v2`. From 5037ab21eb94ac98405636f93b15dcc0b5e16208 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:20:38 +0000 Subject: [PATCH 1922/2296] build(deps): bump kiegroup/git-backporting Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from cb3473d7c9de66cb7bec188f08538e134cdc4bc0 to c5d7f0ea567c9f997ba4d15e442ad3204abe2030. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/cb3473d7c9de66cb7bec188f08538e134cdc4bc0...c5d7f0ea567c9f997ba4d15e442ad3204abe2030) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index f0c9b55300..f690f497fe 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -34,7 +34,7 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') || contains(github.event.label.name, 'semver:patch') - uses: kiegroup/git-backporting@cb3473d7c9de66cb7bec188f08538e134cdc4bc0 + uses: kiegroup/git-backporting@c5d7f0ea567c9f997ba4d15e442ad3204abe2030 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -95,7 +95,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@cb3473d7c9de66cb7bec188f08538e134cdc4bc0 + uses: kiegroup/git-backporting@c5d7f0ea567c9f997ba4d15e442ad3204abe2030 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From c0c473618eab0c430c038ebac26adc0933dcdece Mon Sep 17 00:00:00 2001 From: Samuel Allan Date: Wed, 10 Apr 2024 13:52:52 +0930 Subject: [PATCH 1923/2296] Handle nova api version > 2.87 for hypervisor fixes: #3026 --- openstack/compute/v2/hypervisors/results.go | 76 ++++++++++++--------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/openstack/compute/v2/hypervisors/results.go b/openstack/compute/v2/hypervisors/results.go index 2e8d244763..ee6046c6c0 100644 --- a/openstack/compute/v2/hypervisors/results.go +++ b/openstack/compute/v2/hypervisors/results.go @@ -158,27 +158,31 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { *r = Hypervisor(s.tmp) - // Newer versions return the CPU info as the correct type. - // Older versions return the CPU info as a string and need to be - // unmarshalled by the json parser. - var tmpb []byte - - switch t := s.CPUInfo.(type) { - case string: - tmpb = []byte(t) - case map[string]any: - tmpb, err = json.Marshal(t) - if err != nil { - return err + // cpu_info doesn't exist after api version 2.87, + // see https://docs.openstack.org/api-ref/compute/#id288 + if s.CPUInfo != nil { + // api versions 2.28 to 2.87 return the CPU info as the correct type. + // api versions < 2.28 return the CPU info as a string and need to be + // unmarshalled by the json parser. + var tmpb []byte + + switch t := s.CPUInfo.(type) { + case string: + tmpb = []byte(t) + case map[string]any: + tmpb, err = json.Marshal(t) + if err != nil { + return err + } + default: + return fmt.Errorf("CPUInfo has unexpected type: %T", t) } - default: - return fmt.Errorf("CPUInfo has unexpected type: %T", t) - } - if len(tmpb) != 0 { - err = json.Unmarshal(tmpb, &r.CPUInfo) - if err != nil { - return err + if len(tmpb) != 0 { + err = json.Unmarshal(tmpb, &r.CPUInfo) + if err != nil { + return err + } } } @@ -193,22 +197,28 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { return fmt.Errorf("Hypervisor version has unexpected type: %T", t) } - switch t := s.FreeDiskGB.(type) { - case int: - r.FreeDiskGB = t - case float64: - r.FreeDiskGB = int(t) - default: - return fmt.Errorf("Free disk GB has unexpected type: %T", t) + // free_disk_gb doesn't exist after api version 2.87 + if s.FreeDiskGB != nil { + switch t := s.FreeDiskGB.(type) { + case int: + r.FreeDiskGB = t + case float64: + r.FreeDiskGB = int(t) + default: + return fmt.Errorf("Free disk GB has unexpected type: %T", t) + } } - switch t := s.LocalGB.(type) { - case int: - r.LocalGB = t - case float64: - r.LocalGB = int(t) - default: - return fmt.Errorf("Local GB has unexpected type: %T", t) + // local_gb doesn't exist after api version 2.87 + if s.LocalGB != nil { + switch t := s.LocalGB.(type) { + case int: + r.LocalGB = t + case float64: + r.LocalGB = int(t) + default: + return fmt.Errorf("Local GB has unexpected type: %T", t) + } } // OpenStack Compute service returns ID in string representation since From 5f4d2492839059f339d594559452ec04c5248830 Mon Sep 17 00:00:00 2001 From: Samuel Allan Date: Thu, 18 Jul 2024 07:35:37 +0930 Subject: [PATCH 1924/2296] Add unit test --- .../v2/hypervisors/testing/fixtures_test.go | 68 +++++++++++++++++++ .../v2/hypervisors/testing/requests_test.go | 12 ++++ 2 files changed, 80 insertions(+) diff --git a/openstack/compute/v2/hypervisors/testing/fixtures_test.go b/openstack/compute/v2/hypervisors/testing/fixtures_test.go index 1942c842e0..a969f1f825 100644 --- a/openstack/compute/v2/hypervisors/testing/fixtures_test.go +++ b/openstack/compute/v2/hypervisors/testing/fixtures_test.go @@ -346,6 +346,36 @@ const HypervisorGetEmptyCPUInfoBody = ` } ` +// HypervisorAfterV287ResponseBody represents a raw hypervisor GET result with +// missing cpu_info, free_disk_gb, local_gb as seen after v2.87 +const HypervisorAfterV287ResponseBody = ` +{ + "hypervisor":{ + "current_workload":0, + "status":"enabled", + "state":"up", + "disk_available_least":0, + "host_ip":"1.1.1.1", + "free_ram_mb":7680, + "hypervisor_hostname":"fake-mini", + "hypervisor_type":"fake", + "hypervisor_version":2002000, + "id":"c48f6247-abe4-4a24-824e-ea39e108874f", + "local_gb_used":0, + "memory_mb":8192, + "memory_mb_used":512, + "running_vms":0, + "service":{ + "host":"e6a37ee802d74863ab8b91ade8f12a67", + "id":"9c2566e7-7a54-4777-a1ae-c2662f0c407c", + "disabled_reason":null + }, + "vcpus":1, + "vcpus_used":0 + } +} +` + // HypervisorUptimeBody represents a raw hypervisor uptime request result with // Pike+ release. const HypervisorUptimeBody = ` @@ -492,6 +522,7 @@ var ( } HypervisorEmptyCPUInfo = hypervisors.Hypervisor{ + CPUInfo: hypervisors.CPUInfo{}, CurrentWorkload: 0, Status: "enabled", State: "up", @@ -517,6 +548,33 @@ var ( VCPUsUsed: 0, } + HypervisorAfterV287Response = hypervisors.Hypervisor{ + CPUInfo: hypervisors.CPUInfo{}, + CurrentWorkload: 0, + Status: "enabled", + State: "up", + DiskAvailableLeast: 0, + HostIP: "1.1.1.1", + FreeDiskGB: 0, + FreeRamMB: 7680, + HypervisorHostname: "fake-mini", + HypervisorType: "fake", + HypervisorVersion: 2002000, + ID: "c48f6247-abe4-4a24-824e-ea39e108874f", + LocalGB: 0, + LocalGBUsed: 0, + MemoryMB: 8192, + MemoryMBUsed: 512, + RunningVMs: 0, + Service: hypervisors.Service{ + Host: "e6a37ee802d74863ab8b91ade8f12a67", + ID: "9c2566e7-7a54-4777-a1ae-c2662f0c407c", + DisabledReason: "", + }, + VCPUs: 1, + VCPUsUsed: 0, + } + HypervisorsStatisticsExpected = hypervisors.Statistics{ Count: 1, CurrentWorkload: 0, @@ -604,6 +662,16 @@ func HandleHypervisorGetEmptyCPUInfoSuccessfully(t *testing.T) { }) } +func HandleHypervisorAfterV287ResponseSuccessfully(t *testing.T) { + testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, 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") + fmt.Fprintf(w, HypervisorAfterV287ResponseBody) + }) +} + func HandleHypervisorUptimeSuccessfully(t *testing.T) { testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID+"/uptime", func(w http.ResponseWriter, r *http.Request) { testhelper.TestMethod(t, r, "GET") diff --git a/openstack/compute/v2/hypervisors/testing/requests_test.go b/openstack/compute/v2/hypervisors/testing/requests_test.go index 900eadc78b..493e8138d1 100644 --- a/openstack/compute/v2/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/hypervisors/testing/requests_test.go @@ -148,6 +148,18 @@ func TestGetHypervisorEmptyCPUInfo(t *testing.T) { testhelper.CheckDeepEquals(t, &expected, actual) } +func TestGetHypervisorAfterV287Response(t *testing.T) { + testhelper.SetupHTTP() + defer testhelper.TeardownHTTP() + HandleHypervisorAfterV287ResponseSuccessfully(t) + + expected := HypervisorAfterV287Response + + actual, err := hypervisors.Get(context.TODO(), client.ServiceClient(), expected.ID).Extract() + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, &expected, actual) +} + func TestHypervisorsUptime(t *testing.T) { testhelper.SetupHTTP() defer testhelper.TeardownHTTP() From 755deaf2bef9242399883a77831bf2557e509c71 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 5 Jul 2024 16:29:27 +0200 Subject: [PATCH 1925/2296] MIGRATING.md: Provide a migration script Some steps in the migration from Gophercloud v1 to v2 can be automated. Provide a script that can take out some of the pain. --- docs/MIGRATING.md | 134 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index a507820bf5..c398cf0e65 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -493,3 +493,137 @@ Users that still rely on theses old services should continue using Gophercloud v (`openstack/networking/v2/extensions/fwaas`) - Poppy (CDNaaS) service (`openstack/cdn`) - Senlin (Clustering) service (`openstack/clustering`) + +### Script-assisted migration + +#### Expected outcome + +After running the script, your code may not compile. The idea is that at this point, you're only left with a few changes that can't reasonably be automated. + +#### What it does + +* Add `/v2` to all Gophercloud imports, except to the packages that have been removed without replacement +* Adjust the import path of moved packages +* Adjust the package identifier in the code where possible +* Add `context.TODO()` where required + +#### Limitations + +* it doesn't fix the use of removed extensions. For example, if you used `openstack/blockstorage/extensions/availabilityzones`, you will have to manually put that back into e.g. `servers.CreateOpts` +* it will just put `context.TODO()` where a context is required to satisfy the function signature. It's up to you to actually replace that with a variable and provide proper cancellation +* it will add `context.TODO()` to `blockstorage/v1` calls, even though that package only exists in Gophercloud v1 + +```bash +# Adjust the blockstorage version appropriately +blockstorageversion=v3 + +openstack='github.com/gophercloud/gophercloud/openstack' +openstack_utils='github.com/gophercloud/utils/openstack' +find . -type f -name '*.go' -exec sed -i ' + /^import ($/,/^)$/ { + + # 1: These packages have been removed and their functionality moved into the main module for the corresponding service. + /\(\/openstack\/blockstorage\/v1\|\/openstack\/networking\/v2\/extensions\/lbaas\|\/openstack\/networking\/v2\/extensions\/lbaas_v2\|\/openstack\/networking\/v2\/extensions\/fwaas\|\/openstack\/cdn\|\/openstack\/clustering\)/! { + /\/openstack\/blockstorage\/extensions\/volumehost/d + /\/openstack\/blockstorage\/extensions\/volumetenants/d + /\/openstack\/compute\/v2\/extensions\/bootfromvolume/d + /\/openstack\/compute\/v2\/extensions\/diskconfig/d + /\/openstack\/compute\/v2\/extensions\/extendedserverattributes/d + /\/openstack\/compute\/v2\/extensions\/extendedstatus/d + /\/openstack\/compute\/v2\/extensions\/schedulerhints/d + /\/openstack\/compute\/v2\/extensions\/serverusage/d + /\/openstack\/compute\/v2\/extensions\/availabilityzones/d + /\/openstack\/identity\/v3\/extensions\/trusts/d + } + + '" + # 2: Functions and supporting structs and interfaces of these packages have been moved to an existing package + s|${openstack}/blockstorage/extensions/schedulerhints|${openstack}/blockstorage/${blockstorageversion}/volumes|g + s|${openstack}/blockstorage/extensions/volumeactions|${openstack}/blockstorage/${blockstorageversion}/volumes|g + s|${openstack}/compute/v2/extensions/evacuate|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/injectnetworkinfo|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/lockunlock|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/migrate|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/pauseunpause|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/rescueunrescue|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/resetnetwork|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/resetstate|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/shelveunshelve|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/startstop|${openstack}/compute/v2/servers|g + s|${openstack}/compute/v2/extensions/suspendresume|${openstack}/compute/v2/servers|g + + # 3: These packages have been renamed + s|${openstack}/imageservice|${openstack}/image|g + s|${openstack_utils}/imageservice|${openstack_utils}/image|g + s|${openstack}/blockstorage/extensions/availabilityzones|${openstack}/blockstorage/${blockstorageversion}/availabilityzones|g + s|${openstack}/blockstorage/extensions/backups|${openstack}/blockstorage/${blockstorageversion}/backups|g + s|${openstack}/blockstorage/extensions/limits|${openstack}/blockstorage/${blockstorageversion}/limits|g + s|${openstack}/blockstorage/extensions/quotasets|${openstack}/blockstorage/${blockstorageversion}/quotasets|g + s|${openstack}/blockstorage/extensions/schedulerstats|${openstack}/blockstorage/${blockstorageversion}/schedulerstats|g + s|${openstack}/blockstorage/extensions/services|${openstack}/blockstorage/${blockstorageversion}/services|g + s|${openstack}/blockstorage/extensions/volumetransfers|${openstack}/blockstorage/${blockstorageversion}/transfers|g + s|${openstack}/compute/v2/extensions/aggregates|${openstack}/compute/v2/aggregates|g + s|${openstack}/compute/v2/extensions/attachinterfaces|${openstack}/compute/v2/attachinterfaces|g + s|${openstack}/compute/v2/extensions/diagnostics|${openstack}/compute/v2/diagnostics|g + s|${openstack}/compute/v2/extensions/hypervisors|${openstack}/compute/v2/hypervisors|g + s|${openstack}/compute/v2/extensions/instanceactions|${openstack}/compute/v2/instanceactions|g + s|${openstack}/compute/v2/extensions/keypairs|${openstack}/compute/v2/keypairs|g + s|${openstack}/compute/v2/extensions/limits|${openstack}/compute/v2/limits|g + s|${openstack}/compute/v2/extensions/quotasets|${openstack}/compute/v2/quotasets|g + s|${openstack}/compute/v2/extensions/remoteconsoles|${openstack}/compute/v2/remoteconsoles|g + s|${openstack}/compute/v2/extensions/secgroups|${openstack}/compute/v2/secgroups|g + s|${openstack}/compute/v2/extensions/servergroups|${openstack}/compute/v2/servergroups|g + s|${openstack}/compute/v2/extensions/services|${openstack}/compute/v2/services|g + s|${openstack}/compute/v2/extensions/tags|${openstack}/compute/v2/tags|g + s|${openstack}/compute/v2/extensions/usage|${openstack}/compute/v2/usage|g + s|${openstack}/compute/v2/extensions/volumeattach|${openstack}/compute/v2/volumeattach|g + s|${openstack}/identity/v2/extensions/admin/roles|${openstack}/identity/v2/roles|g + s|${openstack}/identity/v3/extensions/ec2credentials|${openstack}/identity/v3/ec2credentials|g + s|${openstack}/identity/v3/extensions/ec2tokens|${openstack}/identity/v3/ec2tokens|g + s|${openstack}/identity/v3/extensions/federation|${openstack}/identity/v3/federation|g + s|${openstack}/identity/v3/extensions/oauth1|${openstack}/identity/v3/oauth1|g + s|${openstack}/identity/v3/extensions/projectendpoints|${openstack}/identity/v3/projectendpoints|g + + # 4: These removed packages existed as proxies of others + s|${openstack}/compute/v2/extensions/defsecrules|${openstack}/networking/v2/extensions/security/groups|g + s|${openstack}/compute/v2/extensions/floatingips|${openstack}/networking/v2/extensions/layer3/floatingips|g + s|${openstack}/compute/v2/extensions/images|${openstack}/image/v2/images|g + s|${openstack}/compute/v2/extensions/networks|${openstack}/networking/v2/networks|g + s|${openstack}/compute/v2/extensions/tenantnetworks|${openstack}/networking/v2/networks|g + "' + + # 5: Update to v2, except for packages that were removed without replacement + s|github.com/gophercloud/utils|github.com/gophercloud/utils/v2|g + /\(\/openstack\/blockstorage\/v1\|\/openstack\/networking\/v2\/extensions\/lbaas\|\/openstack\/networking\/v2\/extensions\/lbaas_v2\|\/openstack\/networking\/v2\/extensions\/fwaas\|\/openstack\/cdn\|\/openstack\/clustering\)/! s|github.com/gophercloud/gophercloud|github.com/gophercloud/gophercloud/v2|g + } + + /^)$/,$ { + + # 6: Rename identifiers of items of step 2 above + s#\(schedulerhints\|volumeactions\)\.\([A-Z][A-Z_a-z_0-9]*\)#volumes.\2#g + s#\(evacuate\|injectnetworkinfo\|lockunlock\|migrate\|pauseunpause\|rescueunrescue\|resetnetwork\|resetstate\|shelveunshelve\|startstop\|suspendresume\)\.\([A-Z][A-Z_a-z_0-9]*\)#servers.\2#g + + # 7: Add context.TODO() + s#\(accept\.Create\|accept\.Get\|accounts\.Get\|accounts\.Update\|acls\.DeleteContainerACL\|acls\.DeleteSecretACL\|acls\.GetContainerACL\|acls\.GetSecretACL\|acls\.SetContainerACL\|acls\.SetSecretACL\|acls\.UpdateContainerACL\|acls\.UpdateSecretACL\|addressscopes\.Create\|addressscopes\.Delete\|addressscopes\.Get\|addressscopes\.Update\|agents\.Delete\|agents\.Get\|agents\.ListDHCPNetworks\|agents\.ListL3Routers\|agents\.RemoveBGPSpeaker\|agents\.RemoveDHCPNetwork\|agents\.RemoveL3Router\|agents\.ScheduleBGPSpeaker\|agents\.ScheduleDHCPNetwork\|agents\.ScheduleL3Router\|agents\.Update\|aggregates\.AddHost\|aggregates\.Create\|aggregates\.Delete\|aggregates\.Get\|aggregates\.RemoveHost\|aggregates\.SetMetadata\|aggregates\.Update\|allocations\.Create\|allocations\.Delete\|allocations\.Get\|amphorae\.Failover\|amphorae\.Get\|apiversions\.Get\|apiversions\.List\|applicationcredentials\.Create\|applicationcredentials\.Delete\|applicationcredentials\.DeleteAccessRule\|applicationcredentials\.Get\|applicationcredentials\.GetAccessRule\|attachinterfaces\.Create\|attachinterfaces\.Delete\|attachinterfaces\.Get\|attachments\.Complete\|attachments\.Create\|attachments\.Delete\|attachments\.Get\|attachments\.Update\|attachments\.WaitForStatus\|backups\.Create\|backups\.Delete\|backups\.Export\|backups\.ForceDelete\|backups\.Get\|backups\.Import\|backups\.ResetStatus\|backups\.RestoreFromBackup\|backups\.Update\|bgpvpns\.Create\|bgpvpns\.CreateNetworkAssociation\|bgpvpns\.CreatePortAssociation\|bgpvpns\.CreateRouterAssociation\|bgpvpns\.Delete\|bgpvpns\.DeleteNetworkAssociation\|bgpvpns\.DeletePortAssociation\|bgpvpns\.DeleteRouterAssociation\|bgpvpns\.Get\|bgpvpns\.GetNetworkAssociation\|bgpvpns\.GetPortAssociation\|bgpvpns\.GetRouterAssociation\|bgpvpns\.Update\|bgpvpns\.UpdatePortAssociation\|bgpvpns\.UpdateRouterAssociation\|buildinfo\.Get\|capsules\.Create\|capsules\.Delete\|capsules\.Get\|certificates\.Create\|certificates\.Get\|certificates\.Update\|claims\.Create\|claims\.Delete\|claims\.Get\|claims\.Update\|clusters\.Create\|clusters\.Delete\|clusters\.Get\|clusters\.Resize\|clusters\.Update\|clusters\.Upgrade\|clustertemplates\.Create\|clustertemplates\.Delete\|clustertemplates\.Get\|clustertemplates\.Update\|conductors\.Get\|config\.NewProviderClient\|configurations\.Create\|configurations\.Delete\|configurations\.Get\|configurations\.GetDatastoreParam\|configurations\.GetGlobalParam\|configurations\.Replace\|configurations\.Update\|containers\.BulkDelete\|containers\.Create\|containers\.CreateConsumer\|containers\.CreateSecretRef\|containers\.Delete\|containers\.DeleteConsumer\|containers\.DeleteSecretRef\|containers\.Get\|containers\.Update\|credentials\.Create\|credentials\.Delete\|credentials\.Get\|credentials\.Update\|crontriggers\.Create\|crontriggers\.Delete\|crontriggers\.Get\|databases\.Create\|databases\.Delete\|datastores\.Get\|datastores\.GetVersion\|diagnostics\.Get\|domains\.Create\|domains\.Delete\|domains\.Get\|domains\.Update\|drivers\.GetDriverDetails\|drivers\.GetDriverDiskProperties\|drivers\.GetDriverProperties\|ec2credentials\.Create\|ec2credentials\.Delete\|ec2credentials\.Get\|ec2tokens\.Create\|ec2tokens\.ValidateS3Token\|endpointgroups\.Create\|endpointgroups\.Delete\|endpointgroups\.Get\|endpointgroups\.Update\|endpoints\.Create\|endpoints\.Delete\|endpoints\.Update\|executions\.Create\|executions\.Delete\|executions\.Get\|extensions\.Get\|extraroutes\.Add\|extraroutes\.Remove\|federation\.CreateMapping\|federation\.DeleteMapping\|federation\.GetMapping\|federation\.UpdateMapping\|flavorprofiles\.Create\|flavorprofiles\.Delete\|flavorprofiles\.Get\|flavorprofiles\.Update\|flavors\.AddAccess\|flavors\.Create\|flavors\.CreateExtraSpecs\|flavors\.Delete\|flavors\.DeleteExtraSpec\|flavors\.Get\|flavors\.GetExtraSpec\|flavors\.ListExtraSpecs\|flavors\.RemoveAccess\|flavors\.Update\|flavors\.UpdateExtraSpec\|floatingips\.Create\|floatingips\.Delete\|floatingips\.Get\|floatingips\.Update\|gophercloud\.WaitFor\|groups\.Create\|groups\.Delete\|groups\.Get\|groups\.RemoveEgressPolicy\|groups\.RemoveIngressPolicy\|groups\.Update\|hypervisors\.Get\|hypervisors\.GetStatistics\|hypervisors\.GetUptime\|ikepolicies\.Create\|ikepolicies\.Delete\|ikepolicies\.Get\|ikepolicies\.Update\|imagedata\.Download\|imagedata\.Stage\|imagedata\.Upload\|imageimport\.Create\|imageimport\.Get\|images\.Create\|images\.Delete\|images\.Get\|images\.Update\|instanceactions\.Get\|instances\.AttachConfigurationGroup\|instances\.Create\|instances\.Delete\|instances\.DetachConfigurationGroup\|instances\.EnableRootUser\|instances\.Get\|instances\.IsRootEnabled\|instances\.Resize\|instances\.ResizeVolume\|instances\.Restart\|introspection\.AbortIntrospection\|introspection\.GetIntrospectionData\|introspection\.GetIntrospectionStatus\|introspection\.ReApplyIntrospection\|introspection\.StartIntrospection\|ipsecpolicies\.Create\|ipsecpolicies\.Delete\|ipsecpolicies\.Get\|ipsecpolicies\.Update\|keypairs\.Create\|keypairs\.Delete\|keypairs\.Get\|l7policies\.Create\|l7policies\.CreateRule\|l7policies\.Delete\|l7policies\.DeleteRule\|l7policies\.Get\|l7policies\.GetRule\|l7policies\.Update\|l7policies\.UpdateRule\|limits\.BatchCreate\|limits\.Delete\|limits\.Get\|limits\.GetEnforcementModel\|limits\.Update\|listeners\.Create\|listeners\.Delete\|listeners\.Get\|listeners\.GetStats\|listeners\.Update\|loadbalancers\.Create\|loadbalancers\.Delete\|loadbalancers\.Failover\|loadbalancers\.Get\|loadbalancers\.GetStats\|loadbalancers\.GetStatuses\|loadbalancers\.Update\|members\.Create\|members\.Delete\|members\.Get\|members\.Update\|messages\.Create\|messages\.Delete\|messages\.DeleteMessages\|messages\.Get\|messages\.GetMessages\|messages\.PopMessages\|monitors\.Create\|monitors\.Delete\|monitors\.Get\|monitors\.Update\|networkipavailabilities\.Get\|networks\.Create\|networks\.Delete\|networks\.Get\|networks\.Update\|nodegroups\.Create\|nodegroups\.Delete\|nodegroups\.Get\|nodegroups\.Update\|nodes\.AttachVirtualMedia\|nodes\.ChangePowerState\|nodes\.ChangeProvisionState\|nodes\.Create\|nodes\.CreateSubscription\|nodes\.Delete\|nodes\.DeleteSubscription\|nodes\.DetachVirtualMedia\|nodes\.Get\|nodes\.GetAllSubscriptions\|nodes\.GetBIOSSetting\|nodes\.GetBootDevice\|nodes\.GetInventory\|nodes\.GetSubscription\|nodes\.GetSupportedBootDevices\|nodes\.GetVendorPassthruMethods\|nodes\.InjectNMI\|nodes\.ListBIOSSettings\|nodes\.ListFirmware\|nodes\.SetBootDevice\|nodes\.SetMaintenance\|nodes\.SetRAIDConfig\|nodes\.UnsetMaintenance\|nodes\.Update\|nodes\.Validate\|nodes\.WaitForProvisionState\|oauth1\.AuthorizeToken\|oauth1\.Create\|oauth1\.CreateAccessToken\|oauth1\.CreateConsumer\|oauth1\.DeleteConsumer\|oauth1\.GetAccessToken\|oauth1\.GetAccessTokenRole\|oauth1\.GetConsumer\|oauth1\.RequestToken\|oauth1\.RevokeAccessToken\|oauth1\.UpdateConsumer\|objects\.BulkDelete\|objects\.Copy\|objects\.Create\|objects\.CreateTempURL\|objects\.Delete\|objects\.Download\|objects\.Get\|objects\.Update\|openstack\.Authenticate\|openstack\.AuthenticatedClient\|openstack\.AuthenticateV2\|openstack\.AuthenticateV3\|orders\.Create\|orders\.Delete\|orders\.Get\|osinherit\.Assign\|osinherit\.Unassign\|osinherit\.Validate\|pagination\.Request\|peers\.Create\|peers\.Delete\|peers\.Get\|peers\.Update\|policies\.Create\|policies\.Delete\|policies\.Get\|policies\.InsertRule\|policies\.RemoveRule\|policies\.Update\|pools\.BatchUpdateMembers\|pools\.Create\|pools\.CreateMember\|pools\.Delete\|pools\.DeleteMember\|pools\.Get\|pools\.GetMember\|pools\.Update\|pools\.UpdateMember\|portforwarding\.Create\|portforwarding\.Delete\|portforwarding\.Get\|portforwarding\.Update\|ports\.Create\|ports\.Delete\|ports\.Get\|ports\.Update\|projectendpoints\.Create\|projectendpoints\.Delete\|projects\.Create\|projects\.Delete\|projects\.DeleteTags\|projects\.Get\|projects\.ListTags\|projects\.ModifyTags\|projects\.Update\|qos\.Associate\|qos\.Create\|qos\.Delete\|qos\.DeleteKeys\|qos\.Disassociate\|qos\.DisassociateAll\|qos\.Get\|qos\.Update\|queues\.Create\|queues\.Delete\|queues\.Get\|queues\.GetStats\|queues\.Purge\|queues\.Share\|queues\.Update\|quotas\.Create\|quotasets\.Delete\|quotasets\.Get\|quotasets\.GetDefaults\|quotasets\.GetDetail\|quotasets\.GetUsage\|quotasets\.Update\|quotas\.Get\|quotas\.GetDetail\|quotas\.Update\|rbacpolicies\.Create\|rbacpolicies\.Delete\|rbacpolicies\.Get\|rbacpolicies\.Update\|recordsets\.Create\|recordsets\.Delete\|recordsets\.Get\|recordsets\.Update\|regions\.Create\|regions\.Delete\|regions\.Get\|regions\.Update\|registeredlimits\.BatchCreate\|registeredlimits\.Delete\|registeredlimits\.Get\|registeredlimits\.Update\|remoteconsoles\.Create\|replicas\.Create\|replicas\.Delete\|replicas\.ForceDelete\|replicas\.Get\|replicas\.GetExportLocation\|replicas\.ListExportLocations\|replicas\.Promote\|replicas\.ResetState\|replicas\.ResetStatus\|replicas\.Resync\|request\.Create\|request\.Delete\|request\.Get\|request\.Update\|resourceproviders\.Create\|resourceproviders\.Delete\|resourceproviders\.Get\|resourceproviders\.GetAllocations\|resourceproviders\.GetInventories\|resourceproviders\.GetTraits\|resourceproviders\.GetUsages\|resourceproviders\.Update\|resourcetypes\.GenerateTemplate\|resourcetypes\.GetSchema\|resourcetypes\.List\|roles\.AddUser\|roles\.Assign\|roles\.Create\|roles\.CreateRoleInferenceRule\|roles\.Delete\|roles\.DeleteRoleInferenceRule\|roles\.DeleteUser\|roles\.Get\|roles\.GetRoleInferenceRule\|roles\.ListRoleInferenceRules\|roles\.Unassign\|roles\.Update\|routers\.AddInterface\|routers\.Create\|routers\.Delete\|routers\.Get\|routers\.RemoveInterface\|routers\.Update\|rules\.Create\|rules\.CreateBandwidthLimitRule\|rules\.CreateDSCPMarkingRule\|rules\.CreateMinimumBandwidthRule\|rules\.Delete\|rules\.DeleteBandwidthLimitRule\|rules\.DeleteDSCPMarkingRule\|rules\.DeleteMinimumBandwidthRule\|rules\.Get\|rules\.GetBandwidthLimitRule\|rules\.GetDSCPMarkingRule\|rules\.GetMinimumBandwidthRule\|rules\.Update\|rules\.UpdateBandwidthLimitRule\|rules\.UpdateDSCPMarkingRule\|rules\.UpdateMinimumBandwidthRule\|ruletypes\.GetRuleType\|secgroups\.AddServer\|secgroups\.Create\|secgroups\.CreateRule\|secgroups\.Delete\|secgroups\.DeleteRule\|secgroups\.Get\|secgroups\.RemoveServer\|secgroups\.Update\|secrets\.Create\|secrets\.CreateMetadata\|secrets\.CreateMetadatum\|secrets\.Delete\|secrets\.DeleteMetadatum\|secrets\.Get\|secrets\.GetMetadata\|secrets\.GetMetadatum\|secrets\.GetPayload\|secrets\.Update\|secrets\.UpdateMetadatum\|securityservices\.Create\|securityservices\.Delete\|securityservices\.Get\|securityservices\.Update\|servergroups\.Create\|servergroups\.Delete\|servergroups\.Get\|servers\.ChangeAdminPassword\|servers\.ConfirmResize\|servers\.Create\|servers\.CreateImage\|servers\.CreateMetadatum\|servers\.Delete\|servers\.DeleteMetadatum\|servers\.Evacuate\|servers\.ForceDelete\|servers\.Get\|servers\.GetPassword\|servers\.InjectNetworkInfo\|servers\.LiveMigrate\|servers\.Lock\|servers\.Metadata\|servers\.Metadatum\|servers\.Migrate\|servers\.Pause\|servers\.Reboot\|servers\.Rebuild\|servers\.Rescue\|servers\.ResetMetadata\|servers\.ResetNetwork\|servers\.ResetState\|servers\.Resize\|servers\.Resume\|servers\.RevertResize\|servers\.Shelve\|servers\.ShelveOffload\|servers\.ShowConsoleOutput\|servers\.Start\|servers\.Stop\|servers\.Suspend\|servers\.Unlock\|servers\.Unpause\|servers\.Unrescue\|servers\.Unshelve\|servers\.Update\|servers\.UpdateMetadata\|servers\.WaitForStatus\|services\.Create\|services\.Delete\|services\.Get\|services\.Update\|shareaccessrules\.Get\|shareaccessrules\.List\|sharenetworks\.AddSecurityService\|sharenetworks\.Create\|sharenetworks\.Delete\|sharenetworks\.Get\|sharenetworks\.RemoveSecurityService\|sharenetworks\.Update\|shares\.Create\|shares\.Delete\|shares\.DeleteMetadatum\|shares\.Extend\|shares\.ForceDelete\|shares\.Get\|shares\.GetExportLocation\|shares\.GetMetadata\|shares\.GetMetadatum\|shares\.GrantAccess\|shares\.ListAccessRights\|shares\.ListExportLocations\|shares\.ResetStatus\|shares\.Revert\|shares\.RevokeAccess\|shares\.SetMetadata\|shares\.Shrink\|shares\.Unmanage\|shares\.Update\|shares\.UpdateMetadata\|sharetransfers\.Accept\|sharetransfers\.Create\|sharetransfers\.Delete\|sharetransfers\.Get\|sharetypes\.AddAccess\|sharetypes\.Create\|sharetypes\.Delete\|sharetypes\.GetDefault\|sharetypes\.GetExtraSpecs\|sharetypes\.RemoveAccess\|sharetypes\.SetExtraSpecs\|sharetypes\.ShowAccess\|sharetypes\.UnsetExtraSpecs\|siteconnections\.Create\|siteconnections\.Delete\|siteconnections\.Get\|siteconnections\.Update\|snapshots\.Create\|snapshots\.Delete\|snapshots\.ForceDelete\|snapshots\.Get\|snapshots\.ResetStatus\|snapshots\.Update\|snapshots\.UpdateMetadata\|snapshots\.UpdateStatus\|snapshots\.WaitForStatus\|speakers\.AddBGPPeer\|speakers\.AddGatewayNetwork\|speakers\.Create\|speakers\.Delete\|speakers\.Get\|speakers\.RemoveBGPPeer\|speakers\.RemoveGatewayNetwork\|speakers\.Update\|stackevents\.Find\|stackevents\.Get\|stackresources\.Find\|stackresources\.Get\|stackresources\.MarkUnhealthy\|stackresources\.Metadata\|stackresources\.Schema\|stackresources\.Template\|stacks\.Abandon\|stacks\.Adopt\|stacks\.Create\|stacks\.Delete\|stacks\.Find\|stacks\.Get\|stacks\.Preview\|stacks\.Update\|stacks\.UpdatePatch\|stacktemplates\.Get\|stacktemplates\.Validate\|subnetpools\.Create\|subnetpools\.Delete\|subnetpools\.Get\|subnetpools\.Update\|subnets\.Create\|subnets\.Delete\|subnets\.Get\|subnets\.Update\|swauth\.Auth\|swauth\.NewObjectStorageV1\|tags\.Add\|tags\.Check\|tags\.Delete\|tags\.DeleteAll\|tags\.List\|tags\.ReplaceAll\|tasks\.Create\|tasks\.Get\|tenants\.Create\|tenants\.Delete\|tenants\.Get\|tenants\.Update\|tokens\.Create\|tokens\.Get\|tokens\.Revoke\|tokens\.Validate\|transfers\.Accept\|transfers\.Create\|transfers\.Delete\|transfers\.Get\|trunks\.AddSubports\|trunks\.Create\|trunks\.Delete\|trunks\.Get\|trunks\.GetSubports\|trunks\.RemoveSubports\|trunks\.Update\|trusts\.CheckRole\|trusts\.Create\|trusts\.Delete\|trusts\.Get\|trusts\.GetRole\|users\.AddToGroup\|users\.ChangePassword\|users\.Create\|users\.Delete\|users\.Get\|users\.IsMemberOfGroup\|users\.RemoveFromGroup\|users\.Update\|utils\.ChooseVersion\|utils\.GetSupportedMicroversions\|utils\.RequireMicroversion\|volumeattach\.Create\|volumeattach\.Delete\|volumeattach\.Get\|volumes\.Attach\|volumes\.BeginDetaching\|volumes\.ChangeType\|volumes\.Create\|volumes\.Delete\|volumes\.Detach\|volumes\.ExtendSize\|volumes\.ForceDelete\|volumes\.Get\|volumes\.InitializeConnection\|volumes\.ReImage\|volumes\.Reserve\|volumes\.ResetStatus\|volumes\.SetBootable\|volumes\.SetImageMetadata\|volumes\.TerminateConnection\|volumes\.Unreserve\|volumes\.Update\|volumes\.UploadImage\|volumes\.WaitForStatus\|volumetypes\.AddAccess\|volumetypes\.Create\|volumetypes\.CreateEncryption\|volumetypes\.CreateExtraSpecs\|volumetypes\.Delete\|volumetypes\.DeleteEncryption\|volumetypes\.DeleteExtraSpec\|volumetypes\.Get\|volumetypes\.GetEncryption\|volumetypes\.GetEncryptionSpec\|volumetypes\.GetExtraSpec\|volumetypes\.ListExtraSpecs\|volumetypes\.RemoveAccess\|volumetypes\.Update\|volumetypes\.UpdateEncryption\|volumetypes\.UpdateExtraSpec\|workflows\.Create\|workflows\.Delete\|workflows\.Get\|zones\.Create\|zones\.Delete\|zones\.Get\|zones\.Update\)(#\1(context.TODO(), #g + s#\(\.AllPages(\)#\1context.TODO(), #g + s#\(\.EachPage(\)\(func(\)#\1context.TODO(), \2ctx context.Context, #g + + # 8: Rename identifiers that were changed in v2 + s#\(\(volumes\|servers\)\.SchedulerHint\)s#\2.SchedulerHintOpts#g + + # 9: Tentatively replace error handling for 404s + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault404); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusNotFound) {#g + } + ' {} \; + +grep -r -l 'context\.TODO' | xargs -r sed -i ' + /^import ($/ a "context" + ' + +grep -r -l 'http\.Status' | xargs -r sed -i ' + /^import ($/ a "net/http" + ' + +goimports -format-only -w . +go mod tidy +``` From 1856c8a781936d22e7036d1e41e0097776260c6e Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 18 Jul 2024 10:34:03 +0200 Subject: [PATCH 1926/2296] actions: Correctly refresh hold state Before adding these trigger types, the `hold` job was often skipped, unnecessarily blocking the Pull request. --- .github/workflows/check-pr-labels.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-pr-labels.yaml b/.github/workflows/check-pr-labels.yaml index ea8068f602..4fbb7fc0b8 100644 --- a/.github/workflows/check-pr-labels.yaml +++ b/.github/workflows/check-pr-labels.yaml @@ -2,8 +2,11 @@ name: Ready on: pull_request_target: types: - - opened - labeled + - opened + - reopened + - synchronize + - unlabeled jobs: hold: From 3b8dd05aca27f4609e4a7ba4a5cf49c15248c68c Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 18 Jul 2024 15:05:33 +0200 Subject: [PATCH 1927/2296] actions: Refactor the semver assessment Using `gh` lets us drop two external dependencies, and condensate the steps a bit. After this change: * new pushes in the PR won't trigger relabeling if the assessment is unchanged * failures in the workflow will be notified as comments in the PR rather than via the `semver:unknown` label The next natural step is to set this job as mandatory, to that a failure to assess semver will block merging. --- .github/labels.yaml | 3 -- .github/workflows/semver-auto.yaml | 71 ++++++++++++++++-------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/.github/labels.yaml b/.github/labels.yaml index 479fb5f3a7..3069b5bd6b 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -22,9 +22,6 @@ - color: '6E7624' description: No API change name: semver:patch -- color: 'EC0101' - description: Unable to figure out the semver type - name: semver:unknown - color: 'D73A4A' description: Do not merge name: hold diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/semver-auto.yaml index c042be134a..eb92701452 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/semver-auto.yaml @@ -1,21 +1,15 @@ -name: Add PR semver labels +name: Add semver labels on: pull_request_target: - types: [opened, synchronize, reopened] + types: + - opened + - synchronize + - reopened + 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@v4 with: fetch-depth: 0 @@ -30,13 +24,6 @@ jobs: env: GIT_SEQUENCE_EDITOR: '/usr/bin/true' - - 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@v5 with: go-version: '1' @@ -54,23 +41,39 @@ jobs: if: steps.go-apidiff.outcome != 'success' && steps.go-apidiff.outputs.semver-type != 'major' run: exit 1 - - name: Add semver:patch label + - name: Add label semver: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 + run: gh pr edit "$NUMBER" --add-label "semver:patch" --remove-label "semver:major,semver:minor" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} - - name: Add semver:minor label + - name: Add label semver: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 + run: gh pr edit "$NUMBER" --add-label "semver:minor" --remove-label "semver:major,semver:patch" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} - - name: Add semver:major label + - name: Add label semver:major if: steps.go-apidiff.outputs.semver-type == 'major' - uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - labels: semver:major + run: gh pr edit "$NUMBER" --add-label "semver:major" --remove-label "semver:minor,semver:patch" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + + - name: Report failure + if: failure() + run: | + gh pr edit "$NUMBER" --remove-label "semver:major,semver:minor,semver:patch" + gh issue comment "$NUMBER" --body "$BODY" + exit 1 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + BODY: > + Failed to assess the semver bump. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. From 364b4bcaf3321a512e92b1bd4145d300a82b870b Mon Sep 17 00:00:00 2001 From: Przemyslaw Szczerbik Date: Tue, 16 Jul 2024 00:36:22 -0700 Subject: [PATCH 1928/2296] Align ServiceFail provisioning state value with Ironic There is a mismatch between ServiceFail values used by Gophercloud and Ironic. Ironic uses "service failed" value [1], rather than "service fail". This commit addresses this discrepancy. [1] https://github.com/openstack/ironic/blob/stable/2024.1/ironic/common/states.py#L248 Signed-off-by: Przemyslaw Szczerbik --- openstack/baremetal/v1/nodes/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index f79721b4e7..8cb0de9e05 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -50,7 +50,7 @@ const ( Unrescuing ProvisionState = "unrescuing" Servicing ProvisionState = "servicing" ServiceWait ProvisionState = "service wait" - ServiceFail ProvisionState = "service fail" + ServiceFail ProvisionState = "service failed" ServiceHold ProvisionState = "service hold" ) From 6815cc5264b642ef7cc7a1083be430837cb6aec7 Mon Sep 17 00:00:00 2001 From: Sharpz7 Date: Fri, 19 Jul 2024 11:43:51 +0000 Subject: [PATCH 1929/2296] Added node.Retired --- openstack/baremetal/v1/nodes/results.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 0602add768..5c6eb5c190 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -261,6 +261,13 @@ type Node struct { // 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"` + + // Whether the node is retired. A Node tagged as retired will prevent any further + // scheduling of instances, but will still allow for other operations, such as cleaning, to happen + Retired bool `json:"retired"` + + // Reason the node is marked as retired. + RetiredReason string `json:"retired_reason"` } // NodePage abstracts the raw results of making a List() request against From 505e8cd50fe77e0b1b18912e0ae4d6515049c957 Mon Sep 17 00:00:00 2001 From: Sharpz7 Date: Fri, 19 Jul 2024 14:51:12 +0000 Subject: [PATCH 1930/2296] Added tests --- .../v1/nodes/testing/fixtures_test.go | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index 8ab570b54d..d47d70b13c 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -149,6 +149,8 @@ const NodeListDetailBody = ` "provision_updated_at": "2019-02-15T17:21:29+00:00", "raid_config": {}, "raid_interface": "no-raid", + "retired": false, + "retired_reason": "No longer needed", "rescue_interface": "no-rescue", "reservation": null, "resource_class": null, @@ -247,6 +249,8 @@ const NodeListDetailBody = ` "provision_updated_at": null, "raid_config": {}, "raid_interface": "no-raid", + "retired": false, + "retired_reason": "No longer needed", "rescue_interface": "no-rescue", "reservation": null, "resource_class": null, @@ -345,6 +349,8 @@ const NodeListDetailBody = ` "provision_updated_at": null, "raid_config": {}, "raid_interface": "no-raid", + "retired": false, + "retired_reason": "No longer needed", "rescue_interface": "no-rescue", "reservation": null, "resource_class": null, @@ -456,6 +462,8 @@ const SingleNodeBody = ` "provision_updated_at": "2019-02-15T17:21:29+00:00", "raid_config": {}, "raid_interface": "no-raid", + "retired": false, + "retired_reason": "No longer needed", "rescue_interface": "no-rescue", "reservation": null, "resource_class": null, @@ -855,7 +863,7 @@ const NodeFirmwareListBody = ` { "firmware": [ { - "created_at": "2023-10-03T18:30:00+00:00", + "created_at": "2023-10-03T18:30:00+00:00", "updated_at": null, "component": "bios", "initial_version": "U30 v2.36 (07/16/2020)", @@ -864,10 +872,10 @@ const NodeFirmwareListBody = ` }, { "created_at": "2023-10-03T18:30:00+00:00", - "updated_at": "2023-10-03T18:45:54+00:00", - "component": "bmc", - "initial_version": "iLO 5 v2.78", - "current_version": "iLO 5 v2.81", + "updated_at": "2023-10-03T18:45:54+00:00", + "component": "bmc", + "initial_version": "iLO 5 v2.78", + "current_version": "iLO 5 v2.81", "last_version_flashed": "iLO 5 v2.81" } ] @@ -949,6 +957,8 @@ var ( CreatedAt: createdAtFoo, UpdatedAt: updatedAt, ProvisionUpdatedAt: provisonUpdatedAt, + Retired: false, + RetiredReason: "No longer needed", } NodeFooValidation = nodes.NodeValidation{ @@ -1058,6 +1068,8 @@ var ( UpdatedAt: updatedAt, InspectionStartedAt: &InspectionStartedAt, InspectionFinishedAt: &InspectionFinishedAt, + Retired: false, + RetiredReason: "No longer needed", } NodeBaz = nodes.Node{ @@ -1105,6 +1117,8 @@ var ( ProtectedReason: "", CreatedAt: createdAtBaz, UpdatedAt: updatedAt, + Retired: false, + RetiredReason: "No longer needed", } ConfigDriveMap = nodes.ConfigDrive{ From e5197df8061baf5812d2239d09696f9a28d0b6df Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 22 Jul 2024 10:15:35 +0200 Subject: [PATCH 1931/2296] Add needinfo label Includes a Github action that automatically removes the `needinfo` label when the original author adds a comment. --- .github/labels.yaml | 3 +++ .github/workflows/label-issue.yaml | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 .github/workflows/label-issue.yaml diff --git a/.github/labels.yaml b/.github/labels.yaml index 3069b5bd6b..a266cf1678 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -25,3 +25,6 @@ - color: 'D73A4A' description: Do not merge name: hold +- color: 'F9D0C4' + description: Additional information requested + name: needinfo diff --git a/.github/workflows/label-issue.yaml b/.github/workflows/label-issue.yaml new file mode 100644 index 0000000000..723f4cd04e --- /dev/null +++ b/.github/workflows/label-issue.yaml @@ -0,0 +1,17 @@ +name: Label issue +on: + issue_comment: + types: + - created + +jobs: + clear_needinfo: + name: Clear needinfo + if: ${{ github.event.issue.user.login }} == ${{ github.event.comment.user.login }} + runs-on: ubuntu-latest + steps: + - run: gh pr edit "$NUMBER" --remove-label "needinfo" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} From a4a2239bb7bb15ea1e9ffa5fce55e15a405956ce Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 19 Jul 2024 12:07:37 +0200 Subject: [PATCH 1932/2296] actions: Label PRs based on the paths of files being changed To facilitate PR assignment and ultimately reviews, label the PRs based on what they touch. --- .github/labeler.yml | 144 ++++++++++++++++++ .github/labels.yaml | 95 +++++++++++- .../{semver-auto.yaml => label-pr.yaml} | 12 +- 3 files changed, 243 insertions(+), 8 deletions(-) create mode 100644 .github/labeler.yml rename .github/workflows/{semver-auto.yaml => label-pr.yaml} (94%) diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000..a07b39fe3e --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,144 @@ +edit:dependencies: +- changed-files: + - any-glob-to-any-file: + - 'go.mod' + - 'go.sum' + - '.github/dependabot.yml' +edit:actions: +- changed-files: + - any-glob-to-any-file: + - '.github/**' +edit:gophercloud: +- changed-files: + - any-glob-to-any-file: + - '*.go' + - 'testing/**' + - 'pagination/**' +edit:openstack: +- changed-files: + - any-glob-to-any-file: + - 'openstack/*' + - 'openstack/testing/**' +edit:baremetal: +- changed-files: + - any-glob-to-any-file: + - 'openstack/baremetal/**' + - 'internal/acceptance/openstack/baremetal/**' +edit:baremetalintrospection: +- changed-files: + - any-glob-to-any-file: + - 'openstack/baremetalintrospection/**' +edit:blockstorage: +- changed-files: + - any-glob-to-any-file: + - 'openstack/blockstorage/**' + - 'internal/acceptance/openstack/blockstorage/**' +edit:common: +- changed-files: + - any-glob-to-any-file: + - 'openstack/common/**' +edit:compute: +- changed-files: + - any-glob-to-any-file: + - 'openstack/compute/**' + - 'internal/acceptance/openstack/compute/**' +edit:config: +- changed-files: + - any-glob-to-any-file: + - 'openstack/config/**' +edit:container: +- changed-files: + - any-glob-to-any-file: + - 'openstack/container/**' + - 'internal/acceptance/openstack/container/**' +edit:containerinfra: +- changed-files: + - any-glob-to-any-file: + - 'openstack/containerinfra/**' + - 'internal/acceptance/openstack/containerinfra/**' +edit:db: +- changed-files: + - any-glob-to-any-file: + - 'openstack/db/**' + - 'internal/acceptance/openstack/db/**' +edit:dns: +- changed-files: + - any-glob-to-any-file: + - 'openstack/dns/**' + - 'internal/acceptance/openstack/dns/**' +edit:identity: +- changed-files: + - any-glob-to-any-file: + - 'openstack/identity/**' + - 'internal/acceptance/openstack/identity/**' +edit:image: +- changed-files: + - any-glob-to-any-file: + - 'openstack/image/**' + - 'internal/acceptance/openstack/image/**' +edit:keymanager: +- changed-files: + - any-glob-to-any-file: + - 'openstack/keymanager/**' + - 'internal/acceptance/openstack/keymanager/**' +edit:loadbalancer: +- changed-files: + - any-glob-to-any-file: + - 'openstack/loadbalancer/**' + - 'internal/acceptance/openstack/loadbalancer/**' +edit:messaging: +- changed-files: + - any-glob-to-any-file: + - 'openstack/messaging/**' + - 'internal/acceptance/openstack/messaging/**' +edit:networking: +- changed-files: + - any-glob-to-any-file: + - 'openstack/networking/**' + - 'internal/acceptance/openstack/networking/**' +edit:objectstorage: +- changed-files: + - any-glob-to-any-file: + - 'openstack/objectstorage/**' + - 'internal/acceptance/openstack/objectstorage/**' +edit:orchestration: +- changed-files: + - any-glob-to-any-file: + - 'openstack/orchestration/**' + - 'internal/acceptance/openstack/orchestration/**' +edit:placement: +- changed-files: + - any-glob-to-any-file: + - 'openstack/placement/**' + - 'internal/acceptance/openstack/placement/**' +edit:sharedfilesystems: +- changed-files: + - any-glob-to-any-file: + - 'openstack/sharedfilesystems/**' + - 'internal/acceptance/openstack/sharedfilesystems/**' +edit:testinfra: +- changed-files: + - any-glob-to-any-file: + - 'testhelper/**' + - 'internal/acceptance/*' + - 'internal/acceptance/openstack/*' + - 'internal/acceptance/clients/**' + - 'internal/acceptance/tools/**' + - '.github/workflows/functional-*.yaml' + - '.github/workflows/unit.yaml' + - '.github/workflows/lint.yaml' + - 'script/**' +edit:utils: +- changed-files: + - any-glob-to-any-file: + - 'openstack/utils/**' +edit:workflow: +- changed-files: + - any-glob-to-any-file: + - 'openstack/workflow/**' + - 'internal/acceptance/openstack/workflow/**' + +v1: +- base-branch: 'v1' +v2: +- base-branch: 'v2' diff --git a/.github/labels.yaml b/.github/labels.yaml index a266cf1678..4f888b4d4f 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -4,12 +4,6 @@ - color: 'E99695' description: This PR will be backported to v2 name: backport-v2 -- color: '0366d6' - description: Pull requests that update a dependency file - name: dependencies -- color: '000000' - description: Pull requests that update GitHub Actions code - name: github_actions - color: 'BCF611' description: A good issue for first-time contributors name: good first issue @@ -28,3 +22,92 @@ - color: 'F9D0C4' description: Additional information requested name: needinfo + +- color: '30ABB9' + description: This PR targets v1 + name: v1 +- color: 'E99695' + description: This PR targets v2 + name: v2 + +- color: '000000' + description: This PR updates dependencies + name: edit:dependencies +- color: '000000' + description: This PR updates GitHub Actions code + name: edit:actions +- color: '000000' + description: This PR updates common Gophercloud code + name: edit:gophercloud +- color: '000000' + description: This PR updates common OpenStack code + name: edit:openstack +- color: '000000' + description: This PR updates baremetal code + name: edit:baremetal +- color: '000000' + description: This PR updates baremetalintrospection code + name: edit:baremetalintrospection +- color: '000000' + description: This PR updates blockstorage code + name: edit:blockstorage +- color: '000000' + description: This PR updates common code + name: edit:common +- color: '000000' + description: This PR updates compute code + name: edit:compute +- color: '000000' + description: This PR updates config code + name: edit:config +- color: '000000' + description: This PR updates container code + name: edit:container +- color: '000000' + description: This PR updates containerinfra code + name: edit:containerinfra +- color: '000000' + description: This PR updates db code + name: edit:db +- color: '000000' + description: This PR updates dns code + name: edit:dns +- color: '000000' + description: This PR updates identity code + name: edit:identity +- color: '000000' + description: This PR updates image code + name: edit:image +- color: '000000' + description: This PR updates keymanager code + name: edit:keymanager +- color: '000000' + description: This PR updates loadbalancer code + name: edit:loadbalancer +- color: '000000' + description: This PR updates messaging code + name: edit:messaging +- color: '000000' + description: This PR updates networking code + name: edit:networking +- color: '000000' + description: This PR updates objectstorage code + name: edit:objectstorage +- color: '000000' + description: This PR updates orchestration code + name: edit:orchestration +- color: '000000' + description: This PR updates placement code + name: edit:placement +- color: '000000' + description: This PR updates sharedfilesystems code + name: edit:sharedfilesystems +- color: '000000' + description: This PR updates testing code + name: edit:testing +- color: '000000' + description: This PR updates utils code + name: edit:utils +- color: '000000' + description: This PR updates workflow code + name: edit:workflow diff --git a/.github/workflows/semver-auto.yaml b/.github/workflows/label-pr.yaml similarity index 94% rename from .github/workflows/semver-auto.yaml rename to .github/workflows/label-pr.yaml index eb92701452..44d46b0e15 100644 --- a/.github/workflows/semver-auto.yaml +++ b/.github/workflows/label-pr.yaml @@ -1,4 +1,4 @@ -name: Add semver labels +name: Label PR on: pull_request_target: types: @@ -7,7 +7,7 @@ on: - reopened jobs: - go-apidiff: + semver: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -77,3 +77,11 @@ jobs: NUMBER: ${{ github.event.pull_request.number }} BODY: > Failed to assess the semver bump. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. + + edits: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 From 059261336036fc3121fd60ebefafe7a3159f5352 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 22 Jul 2024 15:57:00 +0200 Subject: [PATCH 1933/2296] Github templates: Hide suggestions Use HTML comments to hide the suggestions from the final issue or Pull request text. --- .github/ISSUE_TEMPLATE | 2 ++ .github/PULL_REQUEST_TEMPLATE | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index 2c3be773ad..da9c8472d6 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -1 +1,3 @@ + diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index 6978cc2c53..fb271a3334 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,9 +1,11 @@ + Fixes #[PUT ISSUE NUMBER HERE] Links to the line numbers/files in the OpenStack source code that support the From ff438b14e97707ca5ea514455c3cd08cfe57a5ce Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 23 Jul 2024 14:57:53 +0200 Subject: [PATCH 1934/2296] Prepare v2.1.0 --- CHANGELOG.md | 10 ++++++++++ provider_client.go | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d7dfbc469..4e4d1c854e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## v2.1.0 + +* [GH-3078](https://github.com/gophercloud/gophercloud/pull/3078) [networking]: add BGP VPNs support +* [GH-3086](https://github.com/gophercloud/gophercloud/pull/3086) build(deps): bump golang.org/x/crypto from 0.24.0 to 0.25.0 +* [GH-3090](https://github.com/gophercloud/gophercloud/pull/3090) Adding support for field dns_publish_fixed_ip in a subnet +* [GH-3092](https://github.com/gophercloud/gophercloud/pull/3092) [neutron]: introduce Stateful argument for the security groups +* [GH-3094](https://github.com/gophercloud/gophercloud/pull/3094) [neutron]: introduce Description argument for the portforwarding +* [GH-3106](https://github.com/gophercloud/gophercloud/pull/3106) clouds: Parse trust_id from clouds.yaml +* [GH-3131](https://github.com/gophercloud/gophercloud/pull/3131) Align ServiceFail provisioning state value with Ironic + ## v2.0.0 (2024-05-27) MAIN BREAKING CHANGES: diff --git a/provider_client.go b/provider_client.go index f8c4928cbd..a4a8dce51d 100644 --- a/provider_client.go +++ b/provider_client.go @@ -13,7 +13,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v2.0.0" + DefaultUserAgent = "gophercloud/v2.1.0" DefaultMaxBackoffRetries = 60 ) From a9b9111de59a4b70c93a69b2fecf5a2c02b29f54 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Tue, 23 Jul 2024 14:57:53 +0200 Subject: [PATCH 1935/2296] Prepare v2.1.0 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e4d1c854e..bae5109cfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## v2.1.0 +## v2.1.0 (2024-07-24) * [GH-3078](https://github.com/gophercloud/gophercloud/pull/3078) [networking]: add BGP VPNs support * [GH-3086](https://github.com/gophercloud/gophercloud/pull/3086) build(deps): bump golang.org/x/crypto from 0.24.0 to 0.25.0 @@ -7,6 +7,7 @@ * [GH-3094](https://github.com/gophercloud/gophercloud/pull/3094) [neutron]: introduce Description argument for the portforwarding * [GH-3106](https://github.com/gophercloud/gophercloud/pull/3106) clouds: Parse trust_id from clouds.yaml * [GH-3131](https://github.com/gophercloud/gophercloud/pull/3131) Align ServiceFail provisioning state value with Ironic +* [GH-3136](https://github.com/gophercloud/gophercloud/pull/3136) Added node.Retired ## v2.0.0 (2024-05-27) From dd7130d65994f874114fcb2efe94a544b244531e Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Wed, 24 Jul 2024 11:25:33 +0200 Subject: [PATCH 1936/2296] chore: Set UNRELEASED user-agent Set a special header value to identify user agents built from the development branch. --- provider_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider_client.go b/provider_client.go index a4a8dce51d..598596c61e 100644 --- a/provider_client.go +++ b/provider_client.go @@ -13,7 +13,7 @@ import ( // DefaultUserAgent is the default User-Agent string set in the request header. const ( - DefaultUserAgent = "gophercloud/v2.1.0" + DefaultUserAgent = "gophercloud/v3.0.0-UNRELEASED" DefaultMaxBackoffRetries = 60 ) From d8ce99fc1ccd0467b11e7abe1e38e43a70a0f3d5 Mon Sep 17 00:00:00 2001 From: whitefox Date: Wed, 24 Jul 2024 20:55:42 +0500 Subject: [PATCH 1937/2296] fix: create security group rule with any protocol https://github.com/gophercloud/gophercloud/issues/2442 --- .../v2/extensions/security/rules/requests.go | 5 ++ .../security/rules/testing/requests_test.go | 60 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 78b67b0df0..d9079dd96a 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -145,6 +145,11 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu r.Err = err return } + if m, mOk := b["security_group_rule"].(map[string]any); mOk { + if p, ok := m["protocol"]; ok && p == "any" { + m["protocol"] = nil + } + } resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 454399f306..7d9ceefdff 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -164,6 +164,66 @@ func TestCreate(t *testing.T) { th.AssertNoErr(t, err) } +func TestCreateAnyProtocol(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/security-group-rules", 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, ` +{ + "security_group_rule": { + "description": "test description of rule", + "direction": "ingress", + "port_range_min": 80, + "ethertype": "IPv4", + "port_range_max": 80, + "protocol": null, + "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` +{ + "security_group_rule": { + "description": "test description of rule", + "direction": "ingress", + "ethertype": "IPv4", + "id": "2bc0accf-312e-429a-956e-e4407625eb62", + "port_range_max": 80, + "port_range_min": 80, + "protocol": null, + "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "remote_ip_prefix": null, + "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } +} + `) + }) + + opts := rules.CreateOpts{ + Description: "test description of rule", + Direction: "ingress", + PortRangeMin: 80, + EtherType: rules.EtherType4, + PortRangeMax: 80, + Protocol: "any", + RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + } + _, err := rules.Create(context.TODO(), fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) +} + func TestRequiredCreateOpts(t *testing.T) { res := rules.Create(context.TODO(), fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress}) if res.Err == nil { From d94838e603995807253e64d4915e37b9b7c34c48 Mon Sep 17 00:00:00 2001 From: whitefox Date: Wed, 24 Jul 2024 21:07:47 +0500 Subject: [PATCH 1938/2296] use const --- openstack/networking/v2/extensions/security/rules/requests.go | 2 +- .../v2/extensions/security/rules/testing/requests_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index d9079dd96a..8fa5d9ee83 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -146,7 +146,7 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu return } if m, mOk := b["security_group_rule"].(map[string]any); mOk { - if p, ok := m["protocol"]; ok && p == "any" { + if p, ok := m["protocol"]; ok && p == string(ProtocolAny) { m["protocol"] = nil } } diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 7d9ceefdff..3b44db3837 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -216,7 +216,7 @@ func TestCreateAnyProtocol(t *testing.T) { PortRangeMin: 80, EtherType: rules.EtherType4, PortRangeMax: 80, - Protocol: "any", + Protocol: rules.ProtocolAny, RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", } From 856bb3140c17a3c406b4190bac6b67b125c78ff7 Mon Sep 17 00:00:00 2001 From: whitefox Date: Wed, 24 Jul 2024 23:11:12 +0500 Subject: [PATCH 1939/2296] move to ToSecGroupRuleCreateMap --- .../v2/extensions/security/rules/requests.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 8fa5d9ee83..cad532bd93 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -134,7 +134,16 @@ type CreateOpts struct { // ToSecGroupRuleCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]any, error) { - return gophercloud.BuildRequestBody(opts, "security_group_rule") + b, err := gophercloud.BuildRequestBody(opts, "security_group_rule") + if err != nil { + return nil, err + } + if m, mOk := b["security_group_rule"].(map[string]any); mOk { + if p, ok := m["protocol"]; ok && p == string(ProtocolAny) { + m["protocol"] = nil + } + } + return b, err } // Create is an operation which adds a new security group rule and associates it @@ -145,11 +154,6 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu r.Err = err return } - if m, mOk := b["security_group_rule"].(map[string]any); mOk { - if p, ok := m["protocol"]; ok && p == string(ProtocolAny) { - m["protocol"] = nil - } - } resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return From c28246a6fe47a5a9d0cd7a1a729f7a6b36fb44f2 Mon Sep 17 00:00:00 2001 From: whitefox Date: Wed, 24 Jul 2024 23:44:21 +0500 Subject: [PATCH 1940/2296] emoved redundant key check --- .../networking/v2/extensions/security/rules/requests.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index cad532bd93..55a6cde88f 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -2,6 +2,7 @@ package rules import ( "context" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/rules" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" @@ -138,10 +139,8 @@ func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]any, error) { if err != nil { return nil, err } - if m, mOk := b["security_group_rule"].(map[string]any); mOk { - if p, ok := m["protocol"]; ok && p == string(ProtocolAny) { - m["protocol"] = nil - } + if m := b["security_group_rule"].(map[string]any); m["protocol"] == string(rules.ProtocolAny) { + m["protocol"] = nil } return b, err } From 1f7c5ec3fc309c0ac6e3ed845d30b4d55aed701f Mon Sep 17 00:00:00 2001 From: whitefox Date: Thu, 25 Jul 2024 00:34:18 +0500 Subject: [PATCH 1941/2296] extract ProtocolAny --- openstack/networking/v2/extensions/security/rules/results.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index cfdb27fa17..bcaea974ce 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -109,6 +109,9 @@ func (r commonResult) Extract() (*SecGroupRule, error) { SecGroupRule *SecGroupRule `json:"security_group_rule"` } err := r.ExtractInto(&s) + if err == nil && len(s.SecGroupRule.Protocol) == 0 { + s.SecGroupRule.Protocol = string(ProtocolAny) + } return s.SecGroupRule, err } From 58b35241d21d3681939a66cff69d03e481ab3f0e Mon Sep 17 00:00:00 2001 From: whitefox Date: Fri, 26 Jul 2024 22:57:11 +0500 Subject: [PATCH 1942/2296] protocol any to empty string --- .../v2/extensions/security/rules/requests.go | 13 ++----------- .../v2/extensions/security/rules/results.go | 3 --- .../security/rules/testing/requests_test.go | 2 -- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index 55a6cde88f..eaf6deab80 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -2,8 +2,6 @@ package rules import ( "context" - "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/fwaas_v2/rules" - "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -78,7 +76,7 @@ const ( ProtocolUDP RuleProtocol = "udp" ProtocolUDPLite RuleProtocol = "udplite" ProtocolVRRP RuleProtocol = "vrrp" - ProtocolAny RuleProtocol = "any" + ProtocolAny RuleProtocol = "" ) // CreateOptsBuilder allows extensions to add additional parameters to the @@ -135,14 +133,7 @@ type CreateOpts struct { // ToSecGroupRuleCreateMap builds a request body from CreateOpts. func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]any, error) { - b, err := gophercloud.BuildRequestBody(opts, "security_group_rule") - if err != nil { - return nil, err - } - if m := b["security_group_rule"].(map[string]any); m["protocol"] == string(rules.ProtocolAny) { - m["protocol"] = nil - } - return b, err + return gophercloud.BuildRequestBody(opts, "security_group_rule") } // Create is an operation which adds a new security group rule and associates it diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index bcaea974ce..cfdb27fa17 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -109,9 +109,6 @@ func (r commonResult) Extract() (*SecGroupRule, error) { SecGroupRule *SecGroupRule `json:"security_group_rule"` } err := r.ExtractInto(&s) - if err == nil && len(s.SecGroupRule.Protocol) == 0 { - s.SecGroupRule.Protocol = string(ProtocolAny) - } return s.SecGroupRule, err } diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 3b44db3837..40372c298a 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -181,7 +181,6 @@ func TestCreateAnyProtocol(t *testing.T) { "port_range_min": 80, "ethertype": "IPv4", "port_range_max": 80, - "protocol": null, "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a" } @@ -200,7 +199,6 @@ func TestCreateAnyProtocol(t *testing.T) { "id": "2bc0accf-312e-429a-956e-e4407625eb62", "port_range_max": 80, "port_range_min": 80, - "protocol": null, "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "remote_ip_prefix": null, "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a", From 97ea7e1a84e7071c5d4e488576528429e6629db9 Mon Sep 17 00:00:00 2001 From: whitefox Date: Fri, 26 Jul 2024 23:52:25 +0500 Subject: [PATCH 1943/2296] revert newline --- 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 eaf6deab80..f2ea9f2d16 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -2,6 +2,7 @@ package rules import ( "context" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/pagination" ) From fdb400d7bdc4d741870b259b5ca1cda0b8128ad8 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sat, 27 Jul 2024 12:15:17 +0200 Subject: [PATCH 1944/2296] [containerinfra]: add "MasterLBEnabled" in Cluster results --- .../acceptance/openstack/containerinfra/v1/clusters_test.go | 1 + openstack/containerinfra/v1/clusters/results.go | 1 + .../containerinfra/v1/clusters/testing/fixtures_test.go | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go index 7a277b318a..d12c31bf4a 100644 --- a/internal/acceptance/openstack/containerinfra/v1/clusters_test.go +++ b/internal/acceptance/openstack/containerinfra/v1/clusters_test.go @@ -63,6 +63,7 @@ func TestClustersCRUD(t *testing.T) { newCluster, err := clusters.Get(context.TODO(), client, clusterID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newCluster.UUID, clusterID) + th.AssertEquals(t, newCluster.MasterLBEnabled, false) allPagesDetail, err := clusters.ListDetail(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) diff --git a/openstack/containerinfra/v1/clusters/results.go b/openstack/containerinfra/v1/clusters/results.go index 6c235dcd23..04adc8ea48 100644 --- a/openstack/containerinfra/v1/clusters/results.go +++ b/openstack/containerinfra/v1/clusters/results.go @@ -112,6 +112,7 @@ type Cluster struct { UpdatedAt time.Time `json:"updated_at"` UserID string `json:"user_id"` FloatingIPEnabled bool `json:"floating_ip_enabled"` + MasterLBEnabled bool `json:"master_lb_enabled"` FixedNetwork string `json:"fixed_network"` FixedSubnet string `json:"fixed_subnet"` HealthStatus string `json:"health_status"` diff --git a/openstack/containerinfra/v1/clusters/testing/fixtures_test.go b/openstack/containerinfra/v1/clusters/testing/fixtures_test.go index 640533e2c8..ccbfddfc6d 100644 --- a/openstack/containerinfra/v1/clusters/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/clusters/testing/fixtures_test.go @@ -50,6 +50,7 @@ var ExpectedCluster = clusters.Cluster{ UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), UUID: clusterUUID, FloatingIPEnabled: true, + MasterLBEnabled: true, FixedNetwork: "private_network", FixedSubnet: "private_subnet", HealthStatus: "HEALTHY", @@ -85,6 +86,7 @@ var ExpectedCluster2 = clusters.Cluster{ UpdatedAt: time.Date(2016, 8, 29, 6, 53, 24, 0, time.UTC), UUID: clusterUUID2, FloatingIPEnabled: true, + MasterLBEnabled: true, FixedNetwork: "private_network", FixedSubnet: "private_subnet", HealthStatus: "HEALTHY", @@ -152,6 +154,7 @@ var ClusterGetResponse = fmt.Sprintf(` "create_timeout":60, "name":"k8s", "floating_ip_enabled": true, + "master_lb_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet", "health_status": "HEALTHY", @@ -194,6 +197,7 @@ var ClusterListResponse = fmt.Sprintf(` "updated_at":"2016-08-29T06:53:24+00:00", "uuid":"%s", "floating_ip_enabled": true, + "master_lb_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet", "health_status": "HEALTHY", @@ -232,6 +236,7 @@ var ClusterListResponse = fmt.Sprintf(` "updated_at":null, "uuid":"%s", "floating_ip_enabled": true, + "master_lb_enabled": true, "fixed_network": "private_network", "fixed_subnet": "private_subnet", "health_status": "HEALTHY", From 614749bde00060baee8430cfb3d5440c1f34268c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 09:11:02 +0000 Subject: [PATCH 1945/2296] build(deps): bump golang.org/x/crypto from 0.25.0 to 0.26.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.25.0 to 0.26.0. - [Commits](https://github.com/golang/crypto/compare/v0.25.0...v0.26.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index d4ac50a838..1a02e360ff 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.22 require ( - golang.org/x/crypto v0.25.0 + golang.org/x/crypto v0.26.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.22.0 // indirect +require golang.org/x/sys v0.23.0 // indirect diff --git a/go.sum b/go.sum index 1e1f48a722..70de9bd3f3 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= 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 8b63747ce6d4214f5dc78242fe4556f6d1295b23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 09:44:59 +0000 Subject: [PATCH 1946/2296] build(deps): bump getsentry/action-github-app-token from 3.0.0 to 3.1.0 Bumps [getsentry/action-github-app-token](https://github.com/getsentry/action-github-app-token) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/getsentry/action-github-app-token/releases) - [Commits](https://github.com/getsentry/action-github-app-token/compare/d4b5da6c5e37703f8c3b3e43abb5705b46e159cc...a0061014b82a6a5d6aeeb3b824aced47e3c3a7ef) --- updated-dependencies: - dependency-name: getsentry/action-github-app-token dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index f690f497fe..0071ecba1d 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -25,7 +25,7 @@ jobs: steps: - name: Generate a token from the gophercloud-backport-bot github-app id: generate_token - uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc + uses: getsentry/action-github-app-token@a0061014b82a6a5d6aeeb3b824aced47e3c3a7ef with: app_id: ${{ secrets.BACKPORT_APP_ID }} private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} @@ -84,7 +84,7 @@ jobs: steps: - name: Generate a token from the gophercloud-backport-bot github-app id: generate_token - uses: getsentry/action-github-app-token@d4b5da6c5e37703f8c3b3e43abb5705b46e159cc + uses: getsentry/action-github-app-token@a0061014b82a6a5d6aeeb3b824aced47e3c3a7ef with: app_id: ${{ secrets.BACKPORT_APP_ID }} private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} From e3ae7f55a1f7f5abbc5c5207e5858ccab68b01ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 09:58:44 +0000 Subject: [PATCH 1947/2296] build(deps): bump golang.org/x/crypto from 0.26.0 to 0.27.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.26.0 to 0.27.0. - [Commits](https://github.com/golang/crypto/compare/v0.26.0...v0.27.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 1a02e360ff..0f39082357 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.22 require ( - golang.org/x/crypto v0.26.0 + golang.org/x/crypto v0.27.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.23.0 // indirect +require golang.org/x/sys v0.25.0 // indirect diff --git a/go.sum b/go.sum index 70de9bd3f3..77cbe99406 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= 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 1ea105cd5789d35c2a67b5744746813d5832369a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 11 Sep 2024 17:16:00 +0200 Subject: [PATCH 1948/2296] migration.md: exclude vendor directory for automated script --- docs/MIGRATING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index c398cf0e65..75c4724a95 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -519,7 +519,7 @@ blockstorageversion=v3 openstack='github.com/gophercloud/gophercloud/openstack' openstack_utils='github.com/gophercloud/utils/openstack' -find . -type f -name '*.go' -exec sed -i ' +find . -type f -name '*.go' -not -path "*/vendor/*" -exec sed -i ' /^import ($/,/^)$/ { # 1: These packages have been removed and their functionality moved into the main module for the corresponding service. From 02a183d94c57799aadd77cad6fd76bb339309c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 11 Sep 2024 17:18:03 +0200 Subject: [PATCH 1949/2296] migration.md: try converting more error types We should try converting all ErrDefaultXXX errors that existed in gophercloud v1 to their net/http equivalent. --- docs/MIGRATING.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/MIGRATING.md b/docs/MIGRATING.md index 75c4724a95..7bf4ed9708 100644 --- a/docs/MIGRATING.md +++ b/docs/MIGRATING.md @@ -611,8 +611,19 @@ find . -type f -name '*.go' -not -path "*/vendor/*" -exec sed -i ' # 8: Rename identifiers that were changed in v2 s#\(\(volumes\|servers\)\.SchedulerHint\)s#\2.SchedulerHintOpts#g - # 9: Tentatively replace error handling for 404s + # 9: Tentatively replace error handling + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault400); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusBadRequest) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault401); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusUnauthorized) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault403); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusForbidden) {#g s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault404); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusNotFound) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault405); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusMethodNotAllowed) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault408); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusRequestTimeout) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault409); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusConflict) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault429); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusTooManyRequests) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault500); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusInternalServerError) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault502); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusBadGateway) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault503); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusServiceUnavailable) {#g + s#\(\t\+\)if _, ok := err.(gophercloud.ErrDefault504); \(!\?\)ok {#\1if \2gophercloud.ResponseCodeIs(err, http.StatusGatewayTimeout) {#g } ' {} \; From c8233ba73c5d5b72f4369bc5a664f13f464ea187 Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 23 Sep 2024 23:00:36 +0200 Subject: [PATCH 1950/2296] octavia: add new security options to pools and listeners --- .../loadbalancer/v2/listeners/requests.go | 80 +++++++++++++++++++ .../loadbalancer/v2/listeners/results.go | 18 +++++ openstack/loadbalancer/v2/pools/requests.go | 62 ++++++++++++++ openstack/loadbalancer/v2/pools/results.go | 31 +++++++ 4 files changed, 191 insertions(+) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index ea5b10dd2f..5c66db23be 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -170,6 +170,46 @@ type CreateOpts struct { // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs []string `json:"allowed_cidrs,omitempty"` + // A list of ALPN protocols. Available protocols: http/1.0, http/1.1, + // h2. Available from microversion 2.20. + ALPNProtocols []string `json:"alpn_protocols,omitempty"` + + // The TLS client authentication mode. One of the options NONE, + // OPTIONAL or MANDATORY. Available from microversion 2.8. + ClientAuthentication string `json:"client_authentication,omitempty"` + + // The ref of the key manager service secret containing a PEM format + // client CA certificate bundle for TERMINATED_HTTPS listeners. + // Available from microversion 2.8. + ClientCATLSContainerRef string `json:"client_ca_tls_container_ref,omitempty"` + + // The URI of the key manager service secret containing a PEM format CA + // revocation list file for TERMINATED_HTTPS listeners. Available from + // microversion 2.8. + ClientCRLContainerRef string `json:"client_crl_container_ref,omitempty"` + + // Defines whether the includeSubDomains directive should be added to + // the Strict-Transport-Security HTTP response header. This requires + // setting the hsts_max_age option as well in order to become + // effective. Available from microversion 2.27. + HSTSIncludeSubdomains bool `json:"hsts_include_subdomains,omitempty"` + + // The value of the max_age directive for the Strict-Transport-Security + // HTTP response header. Setting this enables HTTP Strict Transport + // Security (HSTS) for the TLS-terminated listener. Available from + // microversion 2.27. + HSTSMaxAge int `json:"hsts_max_age,omitempty"` + + // Defines whether the preload directive should be added to the + // Strict-Transport-Security HTTP response header. This requires + // setting the hsts_max_age option as well in order to become + // effective. Available from microversion 2.27. + HSTSPreload bool `json:"hsts_preload,omitempty"` + + // List of ciphers in OpenSSL format (colon-separated). Available from + // microversion 2.15. + TLSCiphers string `json:"tls_ciphers,omitempty"` + // A list of TLS protocol versions. Available from microversion 2.17 TLSVersions []TLSVersion `json:"tls_versions,omitempty"` @@ -255,6 +295,46 @@ type UpdateOpts struct { // A list of IPv4, IPv6 or mix of both CIDRs AllowedCIDRs *[]string `json:"allowed_cidrs,omitempty"` + // A list of ALPN protocols. Available protocols: http/1.0, http/1.1, + // h2. Available from microversion 2.20. + ALPNProtocols *[]string `json:"alpn_protocols,omitempty"` + + // The TLS client authentication mode. One of the options NONE, + // OPTIONAL or MANDATORY. Available from microversion 2.8. + ClientAuthentication *string `json:"client_authentication,omitempty"` + + // The ref of the key manager service secret containing a PEM format + // client CA certificate bundle for TERMINATED_HTTPS listeners. + // Available from microversion 2.8. + ClientCATLSContainerRef *string `json:"client_ca_tls_container_ref,omitempty"` + + // The URI of the key manager service secret containing a PEM format CA + // revocation list file for TERMINATED_HTTPS listeners. Available from + // microversion 2.8. + ClientCRLContainerRef *string `json:"client_crl_container_ref,omitempty"` + + // Defines whether the includeSubDomains directive should be added to + // the Strict-Transport-Security HTTP response header. This requires + // setting the hsts_max_age option as well in order to become + // effective. Available from microversion 2.27. + HSTSIncludeSubdomains *bool `json:"hsts_include_subdomains,omitempty"` + + // The value of the max_age directive for the Strict-Transport-Security + // HTTP response header. Setting this enables HTTP Strict Transport + // Security (HSTS) for the TLS-terminated listener. Available from + // microversion 2.27. + HSTSMaxAge *int `json:"hsts_max_age,omitempty"` + + // Defines whether the preload directive should be added to the + // Strict-Transport-Security HTTP response header. This requires + // setting the hsts_max_age option as well in order to become + // effective. Available from microversion 2.27. + HSTSPreload *bool `json:"hsts_preload,omitempty"` + + // List of ciphers in OpenSSL format (colon-separated). Available from + // microversion 2.15. + TLSCiphers *string `json:"tls_ciphers,omitempty"` + // A list of TLS protocol versions. Available from microversion 2.17 TLSVersions *[]TLSVersion `json:"tls_versions,omitempty"` diff --git a/openstack/loadbalancer/v2/listeners/results.go b/openstack/loadbalancer/v2/listeners/results.go index 562a7618dd..0bd08f783a 100644 --- a/openstack/loadbalancer/v2/listeners/results.go +++ b/openstack/loadbalancer/v2/listeners/results.go @@ -114,6 +114,24 @@ type Listener struct { // New in version 2.8 ClientCRLContainerRef string `json:"client_crl_container_ref"` + // Defines whether the includeSubDomains directive should be added to + // the Strict-Transport-Security HTTP response header. This requires + // setting the hsts_max_age option as well in order to become + // effective. Available from microversion 2.27. + HSTSIncludeSubdomains bool `json:"hsts_include_subdomains"` + + // The value of the max_age directive for the Strict-Transport-Security + // HTTP response header. Setting this enables HTTP Strict Transport + // Security (HSTS) for the TLS-terminated listener. Available from + // microversion 2.27. + HSTSMaxAge int `json:"hsts_max_age"` + + // Defines whether the preload directive should be added to the + // Strict-Transport-Security HTTP response header. This requires + // setting the hsts_max_age option as well in order to become + // effective. Available from microversion 2.27. + HSTSPreload bool `json:"hsts_preload"` + // The operating status of the resource OperatingStatus string `json:"operating_status"` } diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 452bfaddb9..60d1132eba 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -122,6 +122,37 @@ type CreateOpts struct { // Omit this field to prevent session persistence. Persistence *SessionPersistence `json:"session_persistence,omitempty"` + // A list of ALPN protocols. Available protocols: http/1.0, http/1.1, + // h2. Available from microversion 2.24. + ALPNProtocols []string `json:"alpn_protocols,omitempty"` + + // The reference of the key manager service secret containing a PEM + // format CA certificate bundle for tls_enabled pools. Available from + // microversion 2.8. + CATLSContainerRef string `json:"ca_tls_container_ref,omitempty"` + + // The reference of the key manager service secret containing a PEM + // format CA revocation list file for tls_enabled pools. Available from + // microversion 2.8. + CRLContainerRef string `json:"crl_container_ref,omitempty"` + + // When true connections to backend member servers will use TLS + // encryption. Default is false. Available from microversion 2.8. + TLSEnabled bool `json:"tls_enabled,omitempty"` + + // List of ciphers in OpenSSL format (colon-separated). Available from + // microversion 2.15. + TLSCiphers string `json:"tls_ciphers,omitempty"` + + // The reference to the key manager service secret containing a PKCS12 + // format certificate/key bundle for tls_enabled pools for TLS client + // authentication to the member servers. Available from microversion 2.8. + TLSContainerRef string `json:"tls_container_ref,omitempty"` + + // A list of TLS protocol versions. Available versions: SSLv3, TLSv1, + // TLSv1.1, TLSv1.2, TLSv1.3. Available from microversion 2.17. + TLSVersions []string `json:"tls_versions,omitempty"` + // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` @@ -196,6 +227,37 @@ type UpdateOpts struct { // Persistence is the session persistence of the pool. Persistence *SessionPersistence `json:"session_persistence,omitempty"` + // A list of ALPN protocols. Available protocols: http/1.0, http/1.1, + // h2. Available from microversion 2.24. + ALPNProtocols *[]string `json:"alpn_protocols,omitempty"` + + // The reference of the key manager service secret containing a PEM + // format CA certificate bundle for tls_enabled pools. Available from + // microversion 2.8. + CATLSContainerRef *string `json:"ca_tls_container_ref,omitempty"` + + // The reference of the key manager service secret containing a PEM + // format CA revocation list file for tls_enabled pools. Available from + // microversion 2.8. + CRLContainerRef *string `json:"crl_container_ref,omitempty"` + + // When true connections to backend member servers will use TLS + // encryption. Default is false. Available from microversion 2.8. + TLSEnabled *bool `json:"tls_enabled,omitempty"` + + // List of ciphers in OpenSSL format (colon-separated). Available from + // microversion 2.15. + TLSCiphers *string `json:"tls_ciphers,omitempty"` + + // The reference to the key manager service secret containing a PKCS12 + // format certificate/key bundle for tls_enabled pools for TLS client + // authentication to the member servers. Available from microversion 2.8. + TLSContainerRef *string `json:"tls_container_ref,omitempty"` + + // A list of TLS protocol versions. Available versions: SSLv3, TLSv1, + // TLSv1.1, TLSv1.2, TLSv1.3. Available from microversion 2.17. + TLSVersions *[]string `json:"tls_versions,omitempty"` + // Tags is a set of resource tags. New in version 2.5 Tags *[]string `json:"tags,omitempty"` } diff --git a/openstack/loadbalancer/v2/pools/results.go b/openstack/loadbalancer/v2/pools/results.go index 1f27752e0d..bff336ea4a 100644 --- a/openstack/loadbalancer/v2/pools/results.go +++ b/openstack/loadbalancer/v2/pools/results.go @@ -95,6 +95,37 @@ type Pool struct { // same Pool member or not. Persistence SessionPersistence `json:"session_persistence"` + // A list of ALPN protocols. Available protocols: http/1.0, http/1.1, + // h2. Available from microversion 2.24. + ALPNProtocols []string `json:"alpn_protocols"` + + // The reference of the key manager service secret containing a PEM + // format CA certificate bundle for tls_enabled pools. Available from + // microversion 2.8. + CATLSContainerRef string `json:"ca_tls_container_ref"` + + // The reference of the key manager service secret containing a PEM + // format CA revocation list file for tls_enabled pools. Available from + // microversion 2.8. + CRLContainerRef string `json:"crl_container_ref"` + + // When true connections to backend member servers will use TLS + // encryption. Default is false. Available from microversion 2.8. + TLSEnabled bool `json:"tls_enabled"` + + // List of ciphers in OpenSSL format (colon-separated). Available from + // microversion 2.15. + TLSCiphers string `json:"tls_ciphers"` + + // The reference to the key manager service secret containing a PKCS12 + // format certificate/key bundle for tls_enabled pools for TLS client + // authentication to the member servers. Available from microversion 2.8. + TLSContainerRef string `json:"tls_container_ref"` + + // A list of TLS protocol versions. Available versions: SSLv3, TLSv1, + // TLSv1.1, TLSv1.2, TLSv1.3. Available from microversion 2.17. + TLSVersions []string `json:"tls_versions"` + // The load balancer provider. Provider string `json:"provider"` From 90c1d821799ca1950aaafa824c6f426463dcf51d Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 24 Sep 2024 01:54:02 +0200 Subject: [PATCH 1951/2296] Allow unsetting certain parameters --- .../loadbalancer/v2/listeners/requests.go | 28 +++++++++++-- openstack/loadbalancer/v2/pools/requests.go | 39 +++++++++++++++++-- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/openstack/loadbalancer/v2/listeners/requests.go b/openstack/loadbalancer/v2/listeners/requests.go index 5c66db23be..3216fbddd0 100644 --- a/openstack/loadbalancer/v2/listeners/requests.go +++ b/openstack/loadbalancer/v2/listeners/requests.go @@ -37,6 +37,15 @@ const ( TLSVersionTLSv1_3 TLSVersion = "TLSv1.3" ) +// ClientAuthentication represents the TLS client authentication mode. +type ClientAuthentication string + +const ( + ClientAuthenticationNone ClientAuthentication = "NONE" + ClientAuthenticationOptional ClientAuthentication = "OPTIONAL" + ClientAuthenticationMandatory ClientAuthentication = "MANDATORY" +) + // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { @@ -176,7 +185,7 @@ type CreateOpts struct { // The TLS client authentication mode. One of the options NONE, // OPTIONAL or MANDATORY. Available from microversion 2.8. - ClientAuthentication string `json:"client_authentication,omitempty"` + ClientAuthentication ClientAuthentication `json:"client_authentication,omitempty"` // The ref of the key manager service secret containing a PEM format // client CA certificate bundle for TERMINATED_HTTPS listeners. @@ -301,7 +310,7 @@ type UpdateOpts struct { // The TLS client authentication mode. One of the options NONE, // OPTIONAL or MANDATORY. Available from microversion 2.8. - ClientAuthentication *string `json:"client_authentication,omitempty"` + ClientAuthentication *ClientAuthentication `json:"client_authentication,omitempty"` // The ref of the key manager service secret containing a PEM format // client CA certificate bundle for TERMINATED_HTTPS listeners. @@ -349,10 +358,23 @@ func (opts UpdateOpts) ToListenerUpdateMap() (map[string]any, error) { return nil, err } - if m := b["listener"].(map[string]any); m["default_pool_id"] == "" { + m := b["listener"].(map[string]any) + + // allow to unset default_pool_id on empty string + if m["default_pool_id"] == "" { m["default_pool_id"] = nil } + // allow to unset alpn_protocols on empty slice + if opts.ALPNProtocols != nil && len(*opts.ALPNProtocols) == 0 { + m["alpn_protocols"] = nil + } + + // allow to unset tls_versions on empty slice + if opts.TLSVersions != nil && len(*opts.TLSVersions) == 0 { + m["tls_versions"] = nil + } + return b, nil } diff --git a/openstack/loadbalancer/v2/pools/requests.go b/openstack/loadbalancer/v2/pools/requests.go index 60d1132eba..3443686370 100644 --- a/openstack/loadbalancer/v2/pools/requests.go +++ b/openstack/loadbalancer/v2/pools/requests.go @@ -8,6 +8,17 @@ import ( "github.com/gophercloud/gophercloud/v2/pagination" ) +// Type TLSVersion represents a tls version +type TLSVersion string + +const ( + TLSVersionSSLv3 TLSVersion = "SSLv3" + TLSVersionTLSv1 TLSVersion = "TLSv1" + TLSVersionTLSv1_1 TLSVersion = "TLSv1.1" + TLSVersionTLSv1_2 TLSVersion = "TLSv1.2" + TLSVersionTLSv1_3 TLSVersion = "TLSv1.3" +) + // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { @@ -151,7 +162,7 @@ type CreateOpts struct { // A list of TLS protocol versions. Available versions: SSLv3, TLSv1, // TLSv1.1, TLSv1.2, TLSv1.3. Available from microversion 2.17. - TLSVersions []string `json:"tls_versions,omitempty"` + TLSVersions []TLSVersion `json:"tls_versions,omitempty"` // The administrative state of the Pool. A valid value is true (UP) // or false (DOWN). @@ -256,7 +267,7 @@ type UpdateOpts struct { // A list of TLS protocol versions. Available versions: SSLv3, TLSv1, // TLSv1.1, TLSv1.2, TLSv1.3. Available from microversion 2.17. - TLSVersions *[]string `json:"tls_versions,omitempty"` + TLSVersions *[]TLSVersion `json:"tls_versions,omitempty"` // Tags is a set of resource tags. New in version 2.5 Tags *[]string `json:"tags,omitempty"` @@ -264,7 +275,29 @@ type UpdateOpts struct { // ToPoolUpdateMap builds a request body from UpdateOpts. func (opts UpdateOpts) ToPoolUpdateMap() (map[string]any, error) { - return gophercloud.BuildRequestBody(opts, "pool") + b, err := gophercloud.BuildRequestBody(opts, "pool") + if err != nil { + return nil, err + } + + m := b["pool"].(map[string]any) + + // allow to unset session_persistence on empty SessionPersistence struct + if opts.Persistence != nil && *opts.Persistence == (SessionPersistence{}) { + m["session_persistence"] = nil + } + + // allow to unset alpn_protocols on empty slice + if opts.ALPNProtocols != nil && len(*opts.ALPNProtocols) == 0 { + m["alpn_protocols"] = nil + } + + // allow to unset tls_versions on empty slice + if opts.TLSVersions != nil && len(*opts.TLSVersions) == 0 { + m["tls_versions"] = nil + } + + return b, nil } // Update allows pools to be updated. From 11a3fcceab77cd1954e4f172fd075126e91e9011 Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 24 Sep 2024 02:40:00 +0200 Subject: [PATCH 1952/2296] octavia: add new options to health monitors --- openstack/loadbalancer/v2/monitors/requests.go | 16 ++++++++++++++++ openstack/loadbalancer/v2/monitors/results.go | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/openstack/loadbalancer/v2/monitors/requests.go b/openstack/loadbalancer/v2/monitors/requests.go index 5667955add..be5701c5f4 100644 --- a/openstack/loadbalancer/v2/monitors/requests.go +++ b/openstack/loadbalancer/v2/monitors/requests.go @@ -119,6 +119,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. One of 1.0 or 1.1. The default is 1.0. New in + // version 2.10. + HTTPVersion string `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". @@ -139,6 +143,10 @@ type CreateOpts struct { // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,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,omitempty"` + // Tags is a set of resource tags. New in version 2.5 Tags []string `json:"tags,omitempty"` } @@ -213,6 +221,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. One of 1.0 or 1.1. The default is 1.0. New in + // version 2.10. + HTTPVersion *string `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. @@ -221,6 +233,10 @@ type UpdateOpts struct { // The Name of the Monitor. Name *string `json:"name,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,omitempty"` + // The administrative state of the Monitor. A valid value is true (UP) // or false (DOWN). AdminStateUp *bool `json:"admin_state_up,omitempty"` diff --git a/openstack/loadbalancer/v2/monitors/results.go b/openstack/loadbalancer/v2/monitors/results.go index 3a29da9f40..644ef18700 100644 --- a/openstack/loadbalancer/v2/monitors/results.go +++ b/openstack/loadbalancer/v2/monitors/results.go @@ -60,6 +60,9 @@ type Monitor struct { // The HTTP method that the monitor uses for requests. HTTPMethod string `json:"http_method"` + // The HTTP version that the monitor uses for requests. + HTTPVersion string `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" ` @@ -67,6 +70,9 @@ type Monitor struct { // Expected HTTP codes for a passing HTTP(S) monitor. ExpectedCodes string `json:"expected_codes"` + // The HTTP host header that the monitor uses for requests. + DomainName string `json:"domain_name"` + // The administrative state of the health monitor, which is up (true) or // down (false). AdminStateUp bool `json:"admin_state_up"` From 4d5dc1359a2c0ec9a2680374d5973fbac02e4d0d Mon Sep 17 00:00:00 2001 From: Paul des Garets Date: Fri, 29 Apr 2022 12:59:55 +0200 Subject: [PATCH 1953/2296] ObjectStorage v1: Add Sync headers --- openstack/objectstorage/v1/containers/results.go | 2 ++ openstack/objectstorage/v1/containers/testing/fixtures.go | 2 ++ openstack/objectstorage/v1/containers/testing/requests_test.go | 2 ++ 3 files changed, 6 insertions(+) diff --git a/openstack/objectstorage/v1/containers/results.go b/openstack/objectstorage/v1/containers/results.go index 8e71c15063..fddfa26bf1 100644 --- a/openstack/objectstorage/v1/containers/results.go +++ b/openstack/objectstorage/v1/containers/results.go @@ -111,6 +111,8 @@ type GetHeader struct { TempURLKey2 string `json:"X-Container-Meta-Temp-URL-Key-2"` Timestamp float64 `json:"X-Timestamp,string"` VersionsEnabled bool `json:"-"` + SyncKey string `json:"X-Sync-Key"` + SyncTo string `json:"X-Sync-To"` } func (r *GetHeader) UnmarshalJSON(b []byte) error { diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 0f440472ac..ef6d45b03a 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -258,6 +258,8 @@ func HandleGetContainerSuccessfully(t *testing.T, options ...option) { w.Header().Set("X-Trans-Id", "tx554ed59667a64c61866f1-0057b4ba37") w.Header().Set("X-Storage-Policy", "test_policy") w.Header().Set("X-Versions-Enabled", "True") + w.Header().Set("X-Sync-Key", "272465181849") + w.Header().Set("X-Sync-To", "anotherContainer") 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 9a2074d510..fedf0f2fe6 100644 --- a/openstack/objectstorage/v1/containers/testing/requests_test.go +++ b/openstack/objectstorage/v1/containers/testing/requests_test.go @@ -244,6 +244,8 @@ func TestGetContainer(t *testing.T) { StoragePolicy: "test_policy", Timestamp: 1471298837.95721, VersionsEnabled: true, + SyncKey: "272465181849", + SyncTo: "anotherContainer", } actual, err := res.Extract() th.AssertNoErr(t, err) From cec507c899d06801d7424c920f54793170ad5f32 Mon Sep 17 00:00:00 2001 From: Paul des Garets Date: Fri, 29 Apr 2022 13:00:13 +0200 Subject: [PATCH 1954/2296] Compute v2: Add locked status --- openstack/compute/v2/servers/results.go | 4 ++++ openstack/compute/v2/servers/testing/fixtures_test.go | 7 +++++-- openstack/compute/v2/servers/testing/requests_test.go | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index dc51564176..385001c8dd 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -279,6 +279,10 @@ type Server struct { // AvailabilityZone is the availabilty zone the server is in. AvailabilityZone string `json:"OS-EXT-AZ:availability_zone"` + + // Locked indicates the lock status of the server + // This requires microversion 2.9 or later + Locked *bool `json:"locked"` } type AttachedVolume struct { diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index 2ce6282b02..f3fd27fce3 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -158,7 +158,8 @@ const ServerListBody = ` "progress": 0, "OS-EXT-STS:power_state": 1, "config_drive": "", - "metadata": {} + "metadata": {}, + "locked": true }, { "status": "ACTIVE", @@ -297,7 +298,8 @@ const SingleServerBody = ` "progress": 0, "OS-EXT-STS:power_state": 1, "config_drive": "", - "metadata": {} + "metadata": {}, + "locked": true } } ` @@ -631,6 +633,7 @@ var ( TerminatedAt: time.Time{}, DiskConfig: servers.Manual, AvailabilityZone: "nova", + Locked: func() *bool { b := true; return &b }(), } ConsoleOutput = "abc" diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index b6ebefce5e..b50ea185c9 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -774,6 +774,7 @@ func TestGetFaultyServer(t *testing.T) { FaultyServer := ServerDerp FaultyServer.Fault = DerpFault + FaultyServer.Locked = nil th.CheckDeepEquals(t, FaultyServer, *actual) } @@ -1145,6 +1146,7 @@ func TestCreateServerWithTags(t *testing.T) { tags := []string{"foo", "bar"} ServerDerpTags := ServerDerp ServerDerpTags.Tags = &tags + ServerDerpTags.Locked = nil createOpts := servers.CreateOpts{ Name: "derp", From 5d1815c7404f24d98f74bf3ac0d8c5f9b0a2a547 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 26 Sep 2024 17:36:28 +0200 Subject: [PATCH 1955/2296] Add label: just-needs-tests Several pull requests in the backlog are code-complete according to their authors, but lack tests. This label will let us be more aware of the problem. --- .github/labels.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/labels.yaml b/.github/labels.yaml index 4f888b4d4f..ad21863831 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -22,6 +22,9 @@ - color: 'F9D0C4' description: Additional information requested name: needinfo +- color: 'FEF2C0' + description: This PR lacks tests before it can be merged + name: just-needs-tests - color: '30ABB9' description: This PR targets v1 From 9c94b9596001f0046de88468543870a417b3dbe4 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 26 Sep 2024 22:09:13 +0200 Subject: [PATCH 1956/2296] [core]: handle empty response body --- openstack/networking/v2/extensions/bgp/speakers/requests.go | 4 ++-- .../v2/extensions/bgp/speakers/testing/requests_test.go | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openstack/networking/v2/extensions/bgp/speakers/requests.go b/openstack/networking/v2/extensions/bgp/speakers/requests.go index 4c352c2cc3..b6dd26d909 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/requests.go +++ b/openstack/networking/v2/extensions/bgp/speakers/requests.go @@ -142,7 +142,7 @@ func RemoveBGPPeer(ctx context.Context, c *gophercloud.ServiceClient, bgpSpeaker r.Err = err return } - resp, err := c.Put(ctx, removeBGPPeerURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, removeBGPPeerURL(c, bgpSpeakerID), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) @@ -207,7 +207,7 @@ func RemoveGatewayNetwork(ctx context.Context, c *gophercloud.ServiceClient, bgp r.Err = err return } - resp, err := c.Put(ctx, removeGatewayNetworkURL(c, bgpSpeakerID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, removeGatewayNetworkURL(c, bgpSpeakerID), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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 9e8df52b0c..d163086a8a 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -3,7 +3,6 @@ package testing import ( "context" "fmt" - "io" "net/http" "testing" @@ -195,7 +194,7 @@ func TestRemoveBGPPeer(t *testing.T) { opts := speakers.RemoveBGPPeerOpts{BGPPeerID: bgpPeerID} err := speakers.RemoveBGPPeer(context.TODO(), fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() - th.AssertEquals(t, err, io.EOF) + th.AssertNoErr(t, err) } func TestGetAdvertisedRoutes(t *testing.T) { @@ -279,5 +278,5 @@ func TestRemoveGatewayNetwork(t *testing.T) { opts := speakers.RemoveGatewayNetworkOpts{NetworkID: networkID} err := speakers.RemoveGatewayNetwork(context.TODO(), fake.ServiceClient(), bgpSpeakerID, opts).ExtractErr() - th.AssertEquals(t, err, io.EOF) + th.AssertNoErr(t, err) } From 3e74ae8989100d2399f2158fc9c65a9dc53a2138 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 26 Sep 2024 23:52:53 +0200 Subject: [PATCH 1957/2296] [core]: proper ParseResponse handling --- .../v2/extensions/fwaas_v2/groups/requests.go | 18 ++++++++++++------ .../extensions/fwaas_v2/policies/requests.go | 18 ++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go index 9b12c62842..856e21548f 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/groups/requests.go @@ -69,7 +69,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a particular firewall group based on its unique ID. func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(ctx, resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -107,7 +108,8 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu r.Err = err return } - _, r.Err = c.Post(ctx, rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -142,9 +144,10 @@ func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts U r.Err = err return } - _, r.Err = c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -165,9 +168,10 @@ func RemoveIngressPolicy(ctx context.Context, c *gophercloud.ServiceClient, id s r.Err = err return } - _, r.Err = c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -181,14 +185,16 @@ func RemoveEgressPolicy(ctx context.Context, c *gophercloud.ServiceClient, id st r.Err = err return } - _, r.Err = c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall group based on its unique ID. func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(ctx, resourceURL(c, id), nil) + resp, err := c.Delete(ctx, resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go index 57d25a6726..476360dbb3 100644 --- a/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go +++ b/openstack/networking/v2/extensions/fwaas_v2/policies/requests.go @@ -91,13 +91,15 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu r.Err = err return } - _, r.Err = c.Post(ctx, rootURL(c), b, &r.Body, nil) + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular firewall policy based on its unique ID. func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(ctx, resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -130,15 +132,17 @@ func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts U r.Err = err return } - _, r.Err = c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular firewall policy based on its unique ID. func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(ctx, resourceURL(c, id), nil) + resp, err := c.Delete(ctx, resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -162,16 +166,18 @@ func InsertRule(ctx context.Context, c *gophercloud.ServiceClient, id string, op r.Err = err return } - _, r.Err = c.Put(ctx, insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, insertURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func RemoveRule(ctx context.Context, c *gophercloud.ServiceClient, id, ruleID string) (r RemoveRuleResult) { b := map[string]any{"firewall_rule_id": ruleID} - _, r.Err = c.Put(ctx, removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ctx, removeURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } From 0948e1ad71e855326e0828d3029be156ac339778 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 27 Sep 2024 16:30:28 +0200 Subject: [PATCH 1958/2296] [octavia] add an ability to filter flavors and flavorprofiles --- .../v2/flavorprofiles/requests.go | 4 ++ openstack/loadbalancer/v2/flavors/requests.go | 6 +++ .../v2/flavors/testing/requests_test.go | 47 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/openstack/loadbalancer/v2/flavorprofiles/requests.go b/openstack/loadbalancer/v2/flavorprofiles/requests.go index 594b79739a..e51b9d99e6 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/requests.go +++ b/openstack/loadbalancer/v2/flavorprofiles/requests.go @@ -15,6 +15,10 @@ type ListOptsBuilder interface { // ListOpts allows to manage the output of the request. type ListOpts struct { + // The name of the flavor profile to filter by. + Name string `q:"name"` + // The provider name of the flavor profile to filter by. + ProviderName string `q:"provider_name"` // The fields that you want the server to return Fields []string `q:"fields"` } diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go index 6c4859116c..cba77c1d78 100644 --- a/openstack/loadbalancer/v2/flavors/requests.go +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -15,6 +15,12 @@ type ListOptsBuilder interface { // ListOpts allows to manage the output of the request. type ListOpts struct { + // The name of the flavor to filter by. + Name string `q:"name"` + // The flavor profile id to filter by. + FlavorProfileId string `q:"flavor_profile_id"` + // The enabled status of the flavor to filter by. + Enabled *bool `q:"enabled"` // The fields that you want the server to return Fields []string `q:"fields"` } diff --git a/openstack/loadbalancer/v2/flavors/testing/requests_test.go b/openstack/loadbalancer/v2/flavors/testing/requests_test.go index 14a74dbe45..fd668a2134 100644 --- a/openstack/loadbalancer/v2/flavors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavors/testing/requests_test.go @@ -2,6 +2,8 @@ package testing import ( "context" + "fmt" + "net/http" "testing" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" @@ -9,6 +11,7 @@ import ( fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListFlavors(t *testing.T) { @@ -41,6 +44,50 @@ func TestListFlavors(t *testing.T) { } } +func TestListFlavorsEnabled(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + func() { + testCases := []string{ + "true", + "false", + "", + } + + cases := 0 + 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") + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } + enabled := r.Form.Get("enabled") + if enabled != testCases[cases] { + t.Errorf("Expected enabled=%s got %q", testCases[cases], enabled) + } + cases++ + fmt.Fprintf(w, `{"flavorprofiles":[]}`) + }) + }() + + var nilBool *bool + enabled := true + filters := []*bool{ + &enabled, + new(bool), + nilBool, + } + for _, filter := range filters { + allPages, err := flavors.List(fake.ServiceClient(), flavors.ListOpts{Enabled: filter}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + _, err = flavors.ExtractFlavors(allPages) + th.AssertNoErr(t, err) + } +} + func TestListAllFlavors(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() From b236ccd5c490d6a3cd9a64cc1b9b029c4a2a69d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?p=C3=BDrus?= Date: Fri, 4 Oct 2024 09:05:35 +0200 Subject: [PATCH 1959/2296] Update openstack/loadbalancer/v2/flavors/requests.go Co-authored-by: Emilien Macchi --- openstack/loadbalancer/v2/flavors/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go index cba77c1d78..710a6edf5b 100644 --- a/openstack/loadbalancer/v2/flavors/requests.go +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -18,7 +18,7 @@ type ListOpts struct { // The name of the flavor to filter by. Name string `q:"name"` // The flavor profile id to filter by. - FlavorProfileId string `q:"flavor_profile_id"` + FlavorProfileID string `q:"flavor_profile_id"` // The enabled status of the flavor to filter by. Enabled *bool `q:"enabled"` // The fields that you want the server to return From a814c3be18fe1f99eaf494641a47d1929ec05956 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 4 Oct 2024 12:09:02 +0200 Subject: [PATCH 1960/2296] compute: Fix expected and actual test results Our test convenience function `AssertEquals` accept the expected value first, then the actual value. Many tests in Compute were using it the other way around, thus potentially returning confusing error messages. --- .../compute/v2/attachinterfaces_test.go | 2 +- .../compute/v2/availabilityzones_test.go | 4 +- .../compute/v2/bootfromvolume_test.go | 20 ++--- .../openstack/compute/v2/compute.go | 84 +++++++++---------- .../openstack/compute/v2/extension_test.go | 4 +- .../openstack/compute/v2/flavors_test.go | 34 ++++---- .../compute/v2/instance_actions_test.go | 6 +- .../openstack/compute/v2/keypairs_test.go | 6 +- .../openstack/compute/v2/limits_test.go | 4 +- .../openstack/compute/v2/quotaset_test.go | 2 +- .../openstack/compute/v2/secgroup_test.go | 12 +-- .../openstack/compute/v2/servergroup_test.go | 4 +- .../openstack/compute/v2/servers_test.go | 40 ++++----- .../openstack/compute/v2/services_test.go | 6 +- .../openstack/compute/v2/volumeattach_test.go | 2 +- 15 files changed, 115 insertions(+), 115 deletions(-) diff --git a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go index cf9b1a973e..95ad0ebf2d 100644 --- a/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go +++ b/internal/acceptance/openstack/compute/v2/attachinterfaces_test.go @@ -48,5 +48,5 @@ func TestAttachDetachInterface(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } diff --git a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go index b3af24b0a1..3b37db49b0 100644 --- a/internal/acceptance/openstack/compute/v2/availabilityzones_test.go +++ b/internal/acceptance/openstack/compute/v2/availabilityzones_test.go @@ -31,7 +31,7 @@ func TestAvailabilityZonesList(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } func TestAvailabilityZonesListDetail(t *testing.T) { @@ -55,5 +55,5 @@ func TestAvailabilityZonesListDetail(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } diff --git a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go index 5bf7f75e13..4b7082ab3a 100644 --- a/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go +++ b/internal/acceptance/openstack/compute/v2/bootfromvolume_test.go @@ -39,7 +39,7 @@ func TestBootFromImage(t *testing.T) { tools.PrintResource(t, server) - th.AssertEquals(t, server.Image["id"], choices.ImageID) + th.AssertEquals(t, choices.ImageID, server.Image["id"]) } func TestBootFromNewVolume(t *testing.T) { @@ -80,13 +80,13 @@ func TestBootFromNewVolume(t *testing.T) { tools.PrintResource(t, server) tools.PrintResource(t, attachments) attachmentTag := *attachments[0].Tag - th.AssertEquals(t, attachmentTag, tagName) + th.AssertEquals(t, tagName, attachmentTag) if server.Image != nil { t.Fatalf("server image should be nil") } - th.AssertEquals(t, len(attachments), 1) + th.AssertEquals(t, 1, len(attachments)) // TODO: volumes_attached extension } @@ -131,8 +131,8 @@ func TestBootFromExistingVolume(t *testing.T) { t.Fatalf("server image should be nil") } - th.AssertEquals(t, len(attachments), 1) - th.AssertEquals(t, attachments[0].VolumeID, volume.ID) + th.AssertEquals(t, 1, len(attachments)) + th.AssertEquals(t, volume.ID, attachments[0].VolumeID) // TODO: volumes_attached extension } @@ -218,8 +218,8 @@ func TestAttachNewVolume(t *testing.T) { tools.PrintResource(t, server) tools.PrintResource(t, attachments) - th.AssertEquals(t, server.Image["id"], choices.ImageID) - th.AssertEquals(t, len(attachments), 1) + th.AssertEquals(t, choices.ImageID, server.Image["id"]) + th.AssertEquals(t, 1, len(attachments)) // TODO: volumes_attached extension } @@ -269,9 +269,9 @@ func TestAttachExistingVolume(t *testing.T) { tools.PrintResource(t, server) tools.PrintResource(t, attachments) - th.AssertEquals(t, server.Image["id"], choices.ImageID) - th.AssertEquals(t, len(attachments), 1) - th.AssertEquals(t, attachments[0].VolumeID, volume.ID) + th.AssertEquals(t, choices.ImageID, server.Image["id"]) + th.AssertEquals(t, 1, len(attachments)) + th.AssertEquals(t, volume.ID, attachments[0].VolumeID) // TODO: volumes_attached extension } diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index 513344082f..4a5ec655a2 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -84,8 +84,8 @@ func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregat return nil, err } - th.AssertEquals(t, aggregate.Name, aggregateName) - th.AssertEquals(t, aggregate.AvailabilityZone, availabilityZone) + th.AssertEquals(t, aggregateName, aggregate.Name) + th.AssertEquals(t, availabilityZone, aggregate.AvailabilityZone) return aggregate, nil } @@ -137,7 +137,7 @@ func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, return nil, err } - th.AssertEquals(t, newServer.Name, name) + th.AssertEquals(t, name, newServer.Name) return newServer, nil } @@ -169,12 +169,12 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Fla t.Logf("Successfully created flavor %s", flavor.ID) - th.AssertEquals(t, flavor.Name, flavorName) - th.AssertEquals(t, flavor.RAM, 1) - th.AssertEquals(t, flavor.Disk, 1) - th.AssertEquals(t, flavor.VCPUs, 1) - th.AssertEquals(t, flavor.IsPublic, true) - th.AssertEquals(t, flavor.Description, flavorDescription) + th.AssertEquals(t, flavorName, flavor.Name) + th.AssertEquals(t, 1, flavor.RAM) + th.AssertEquals(t, 1, flavor.Disk) + th.AssertEquals(t, 1, flavor.VCPUs) + th.AssertEquals(t, true, flavor.IsPublic) + th.AssertEquals(t, flavorDescription, flavor.Description) return flavor, nil } @@ -213,7 +213,7 @@ func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.K t.Logf("Created keypair: %s", keyPairName) - th.AssertEquals(t, keyPair.Name, keyPairName) + th.AssertEquals(t, keyPairName, keyPair.Name) return keyPair, nil } @@ -263,9 +263,9 @@ func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, if err != nil { return server, err } - th.AssertEquals(t, newServer.Name, name) - th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) - th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + th.AssertEquals(t, name, newServer.Name) + th.AssertEquals(t, choices.FlavorID, newServer.Flavor["id"]) + th.AssertEquals(t, choices.ImageID, newServer.Image["id"]) return newServer, nil } @@ -292,11 +292,11 @@ func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flav t.Logf("Successfully created flavor %s", flavor.ID) - th.AssertEquals(t, flavor.Name, flavorName) - th.AssertEquals(t, flavor.RAM, 1) - th.AssertEquals(t, flavor.Disk, 1) - th.AssertEquals(t, flavor.VCPUs, 1) - th.AssertEquals(t, flavor.IsPublic, false) + th.AssertEquals(t, flavorName, flavor.Name) + th.AssertEquals(t, 1, flavor.RAM) + th.AssertEquals(t, 1, flavor.Disk) + th.AssertEquals(t, 1, flavor.VCPUs) + th.AssertEquals(t, false, flavor.IsPublic) return flavor, nil } @@ -318,7 +318,7 @@ func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*secg t.Logf("Created security group: %s", securityGroup.ID) - th.AssertEquals(t, securityGroup.Name, name) + th.AssertEquals(t, name, securityGroup.Name) return securityGroup, nil } @@ -344,9 +344,9 @@ func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, se t.Logf("Created security group rule: %s", rule.ID) - th.AssertEquals(t, rule.FromPort, fromPort) - th.AssertEquals(t, rule.ToPort, toPort) - th.AssertEquals(t, rule.ParentGroupID, securityGroupID) + th.AssertEquals(t, fromPort, rule.FromPort) + th.AssertEquals(t, toPort, rule.ToPort) + th.AssertEquals(t, securityGroupID, rule.ParentGroupID) return rule, nil } @@ -403,9 +403,9 @@ func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Ser return nil, err } - th.AssertEquals(t, newServer.Name, name) - th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) - th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + th.AssertEquals(t, name, newServer.Name) + th.AssertEquals(t, choices.FlavorID, newServer.Flavor["id"]) + th.AssertEquals(t, choices.ImageID, newServer.Image["id"]) return newServer, nil } @@ -457,8 +457,8 @@ func CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) ( return nil, err } - th.AssertEquals(t, newServer.Name, name) - th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + th.AssertEquals(t, name, newServer.Name) + th.AssertEquals(t, choices.ImageID, newServer.Image["id"]) return newServer, nil } @@ -560,8 +560,8 @@ func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, netwo newServer, err := res.Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, newServer.Name, name) - th.AssertDeepEquals(t, *newServer.Tags, []string{"tag1", "tag2"}) + th.AssertEquals(t, name, newServer.Name) + th.AssertDeepEquals(t, []string{"tag1", "tag2"}, *newServer.Tags) return newServer, nil } @@ -584,7 +584,7 @@ func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy s t.Logf("Successfully created server group %s", name) - th.AssertEquals(t, sg.Name, name) + th.AssertEquals(t, name, sg.Name) return sg, nil } @@ -612,7 +612,7 @@ func CreateServerGroupMicroversion(t *testing.T, client *gophercloud.ServiceClie t.Logf("Successfully created server group %s", name) - th.AssertEquals(t, sg.Name, name) + th.AssertEquals(t, name, sg.Name) return sg, nil } @@ -662,9 +662,9 @@ func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, return nil, err } - th.AssertEquals(t, newServer.Name, name) - th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) - th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + th.AssertEquals(t, name, newServer.Name) + th.AssertEquals(t, choices.FlavorID, newServer.Flavor["id"]) + th.AssertEquals(t, choices.ImageID, newServer.Image["id"]) return newServer, nil } @@ -711,9 +711,9 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, return nil, err } - th.AssertEquals(t, newServer.Name, name) - th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) - th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + th.AssertEquals(t, name, newServer.Name) + th.AssertEquals(t, choices.FlavorID, newServer.Flavor["id"]) + th.AssertEquals(t, choices.ImageID, newServer.Image["id"]) return newServer, nil } @@ -910,8 +910,8 @@ func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey t.Logf("Created keypair: %s", keyPairName) - th.AssertEquals(t, keyPair.Name, keyPairName) - th.AssertEquals(t, keyPair.PublicKey, publicKey) + th.AssertEquals(t, keyPairName, keyPair.Name) + th.AssertEquals(t, publicKey, keyPair.PublicKey) return keyPair, nil } @@ -1070,9 +1070,9 @@ func CreateServerNoNetwork(t *testing.T, client *gophercloud.ServiceClient) (*se return nil, err } - th.AssertEquals(t, newServer.Name, name) - th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID) - th.AssertEquals(t, newServer.Image["id"], choices.ImageID) + th.AssertEquals(t, name, newServer.Name) + th.AssertEquals(t, choices.FlavorID, newServer.Flavor["id"]) + th.AssertEquals(t, choices.ImageID, newServer.Image["id"]) return newServer, nil } diff --git a/internal/acceptance/openstack/compute/v2/extension_test.go b/internal/acceptance/openstack/compute/v2/extension_test.go index 0339b251fb..8040282289 100644 --- a/internal/acceptance/openstack/compute/v2/extension_test.go +++ b/internal/acceptance/openstack/compute/v2/extension_test.go @@ -31,7 +31,7 @@ func TestExtensionsList(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } func TestExtensionsGet(t *testing.T) { @@ -43,5 +43,5 @@ func TestExtensionsGet(t *testing.T) { tools.PrintResource(t, extension) - th.AssertEquals(t, extension.Name, "AdminActions") + th.AssertEquals(t, "AdminActions", extension.Name) } diff --git a/internal/acceptance/openstack/compute/v2/flavors_test.go b/internal/acceptance/openstack/compute/v2/flavors_test.go index cb6bd319a1..412149adfe 100644 --- a/internal/acceptance/openstack/compute/v2/flavors_test.go +++ b/internal/acceptance/openstack/compute/v2/flavors_test.go @@ -35,7 +35,7 @@ func TestFlavorsList(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } func TestFlavorsAccessTypeList(t *testing.T) { @@ -74,7 +74,7 @@ func TestFlavorsGet(t *testing.T) { tools.PrintResource(t, flavor) - th.AssertEquals(t, flavor.ID, choices.FlavorID) + th.AssertEquals(t, choices.FlavorID, flavor.ID) } func TestFlavorExtraSpecsGet(t *testing.T) { @@ -103,9 +103,9 @@ func TestFlavorExtraSpecsGet(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, flavor) - th.AssertEquals(t, len(flavor.ExtraSpecs), 2) - th.AssertEquals(t, flavor.ExtraSpecs["hw:cpu_policy"], "CPU-POLICY") - th.AssertEquals(t, flavor.ExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY") + th.AssertEquals(t, 2, len(flavor.ExtraSpecs)) + th.AssertEquals(t, "CPU-POLICY", flavor.ExtraSpecs["hw:cpu_policy"]) + th.AssertEquals(t, "CPU-THREAD-POLICY", flavor.ExtraSpecs["hw:cpu_thread_policy"]) } func TestFlavorsCreateDelete(t *testing.T) { @@ -140,7 +140,7 @@ func TestFlavorsCreateUpdateDelete(t *testing.T) { flavor, err = flavors.Update(context.TODO(), client, flavor.ID, updateOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, flavor.Description, newFlavorDescription) + th.AssertEquals(t, newFlavorDescription, flavor.Description) tools.PrintResource(t, flavor) } @@ -161,7 +161,7 @@ func TestFlavorsAccessesList(t *testing.T) { allAccesses, err := flavors.ExtractAccesses(allPages) th.AssertNoErr(t, err) - th.AssertEquals(t, len(allAccesses), 0) + th.AssertEquals(t, 0, len(allAccesses)) } func TestFlavorsAccessCRUD(t *testing.T) { @@ -188,9 +188,9 @@ func TestFlavorsAccessCRUD(t *testing.T) { accessList, err := flavors.AddAccess(context.TODO(), client, flavor.ID, addAccessOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, len(accessList), 1) - th.AssertEquals(t, accessList[0].TenantID, project.ID) - th.AssertEquals(t, accessList[0].FlavorID, flavor.ID) + th.AssertEquals(t, 1, len(accessList)) + th.AssertEquals(t, project.ID, accessList[0].TenantID) + th.AssertEquals(t, flavor.ID, accessList[0].FlavorID) for _, access := range accessList { tools.PrintResource(t, access) @@ -203,7 +203,7 @@ func TestFlavorsAccessCRUD(t *testing.T) { accessList, err = flavors.RemoveAccess(context.TODO(), client, flavor.ID, removeAccessOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, len(accessList), 0) + th.AssertEquals(t, 0, len(accessList)) } func TestFlavorsExtraSpecsCRUD(t *testing.T) { @@ -225,9 +225,9 @@ func TestFlavorsExtraSpecsCRUD(t *testing.T) { tools.PrintResource(t, createdExtraSpecs) - th.AssertEquals(t, len(createdExtraSpecs), 2) - th.AssertEquals(t, createdExtraSpecs["hw:cpu_policy"], "CPU-POLICY") - th.AssertEquals(t, createdExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY") + th.AssertEquals(t, 2, len(createdExtraSpecs)) + th.AssertEquals(t, "CPU-POLICY", createdExtraSpecs["hw:cpu_policy"]) + th.AssertEquals(t, "CPU-THREAD-POLICY", createdExtraSpecs["hw:cpu_thread_policy"]) err = flavors.DeleteExtraSpec(context.TODO(), client, flavor.ID, "hw:cpu_policy").ExtractErr() th.AssertNoErr(t, err) @@ -245,13 +245,13 @@ func TestFlavorsExtraSpecsCRUD(t *testing.T) { tools.PrintResource(t, allExtraSpecs) - th.AssertEquals(t, len(allExtraSpecs), 1) - th.AssertEquals(t, allExtraSpecs["hw:cpu_thread_policy"], "CPU-THREAD-POLICY-BETTER") + th.AssertEquals(t, 1, len(allExtraSpecs)) + th.AssertEquals(t, "CPU-THREAD-POLICY-BETTER", allExtraSpecs["hw:cpu_thread_policy"]) spec, err := flavors.GetExtraSpec(context.TODO(), client, flavor.ID, "hw:cpu_thread_policy").Extract() th.AssertNoErr(t, err) tools.PrintResource(t, spec) - th.AssertEquals(t, spec["hw:cpu_thread_policy"], "CPU-THREAD-POLICY-BETTER") + th.AssertEquals(t, "CPU-THREAD-POLICY-BETTER", spec["hw:cpu_thread_policy"]) } diff --git a/internal/acceptance/openstack/compute/v2/instance_actions_test.go b/internal/acceptance/openstack/compute/v2/instance_actions_test.go index fb43cd72d6..01dd9fc72c 100644 --- a/internal/acceptance/openstack/compute/v2/instance_actions_test.go +++ b/internal/acceptance/openstack/compute/v2/instance_actions_test.go @@ -39,7 +39,7 @@ func TestInstanceActions(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } func TestInstanceActionsMicroversions(t *testing.T) { @@ -88,7 +88,7 @@ func TestInstanceActionsMicroversions(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) listOpts = instanceactions.ListOpts{ Limit: 1, @@ -101,5 +101,5 @@ func TestInstanceActionsMicroversions(t *testing.T) { allActions, err = instanceactions.ExtractInstanceActions(allPages) th.AssertNoErr(t, err) - th.AssertEquals(t, len(allActions), 0) + th.AssertEquals(t, 0, len(allActions)) } diff --git a/internal/acceptance/openstack/compute/v2/keypairs_test.go b/internal/acceptance/openstack/compute/v2/keypairs_test.go index f3a0ac7e32..09d28f24f5 100644 --- a/internal/acceptance/openstack/compute/v2/keypairs_test.go +++ b/internal/acceptance/openstack/compute/v2/keypairs_test.go @@ -57,7 +57,7 @@ func TestKeyPairsCreateDelete(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } func TestKeyPairsImportPublicKey(t *testing.T) { @@ -94,7 +94,7 @@ func TestKeyPairsServerCreateWithKey(t *testing.T) { server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, server.KeyName, keyPair.Name) + th.AssertEquals(t, keyPair.Name, server.KeyName) } func TestKeyPairsCreateDeleteByID(t *testing.T) { @@ -146,7 +146,7 @@ func TestKeyPairsCreateDeleteByID(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) deleteOpts := keypairs.DeleteOpts{ UserID: user.ID, diff --git a/internal/acceptance/openstack/compute/v2/limits_test.go b/internal/acceptance/openstack/compute/v2/limits_test.go index 55ef2854f2..a4240ee5e0 100644 --- a/internal/acceptance/openstack/compute/v2/limits_test.go +++ b/internal/acceptance/openstack/compute/v2/limits_test.go @@ -22,7 +22,7 @@ func TestLimits(t *testing.T) { tools.PrintResource(t, limits) - th.AssertEquals(t, limits.Absolute.MaxPersonalitySize, 10240) + th.AssertEquals(t, 10240, limits.Absolute.MaxPersonalitySize) } func TestLimitsForTenant(t *testing.T) { @@ -47,5 +47,5 @@ func TestLimitsForTenant(t *testing.T) { tools.PrintResource(t, limits) - th.AssertEquals(t, limits.Absolute.MaxPersonalitySize, 10240) + th.AssertEquals(t, 10240, limits.Absolute.MaxPersonalitySize) } diff --git a/internal/acceptance/openstack/compute/v2/quotaset_test.go b/internal/acceptance/openstack/compute/v2/quotaset_test.go index 78ebbae2f0..53d94b538e 100644 --- a/internal/acceptance/openstack/compute/v2/quotaset_test.go +++ b/internal/acceptance/openstack/compute/v2/quotaset_test.go @@ -31,7 +31,7 @@ func TestQuotasetGet(t *testing.T) { tools.PrintResource(t, quotaSet) - th.AssertEquals(t, quotaSet.FixedIPs, -1) + th.AssertEquals(t, -1, quotaSet.FixedIPs) } func getProjectID(t *testing.T, client *gophercloud.ServiceClient) (string, error) { diff --git a/internal/acceptance/openstack/compute/v2/secgroup_test.go b/internal/acceptance/openstack/compute/v2/secgroup_test.go index 0809bb0dda..dd6b6dd85e 100644 --- a/internal/acceptance/openstack/compute/v2/secgroup_test.go +++ b/internal/acceptance/openstack/compute/v2/secgroup_test.go @@ -32,7 +32,7 @@ func TestSecGroupsList(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } func TestSecGroupsCRUD(t *testing.T) { @@ -58,8 +58,8 @@ func TestSecGroupsCRUD(t *testing.T) { t.Logf("Updated %s's name to %s", updatedSecurityGroup.ID, updatedSecurityGroup.Name) - th.AssertEquals(t, updatedSecurityGroup.Name, newName) - th.AssertEquals(t, updatedSecurityGroup.Description, description) + th.AssertEquals(t, newName, updatedSecurityGroup.Name) + th.AssertEquals(t, description, updatedSecurityGroup.Description) } func TestSecGroupsRuleCreate(t *testing.T) { @@ -83,7 +83,7 @@ func TestSecGroupsRuleCreate(t *testing.T) { tools.PrintResource(t, newSecurityGroup) - th.AssertEquals(t, len(newSecurityGroup.Rules), 1) + th.AssertEquals(t, 1, len(newSecurityGroup.Rules)) } func TestSecGroupsAddGroupToServer(t *testing.T) { @@ -120,7 +120,7 @@ func TestSecGroupsAddGroupToServer(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) t.Logf("Removing group %s from server %s", securityGroup.ID, server.ID) err = secgroups.RemoveServer(context.TODO(), client, server.ID, securityGroup.Name).ExtractErr() @@ -139,5 +139,5 @@ func TestSecGroupsAddGroupToServer(t *testing.T) { } } - th.AssertEquals(t, found, false) + th.AssertEquals(t, false, found) } diff --git a/internal/acceptance/openstack/compute/v2/servergroup_test.go b/internal/acceptance/openstack/compute/v2/servergroup_test.go index 8d23ea8135..09d351a7b4 100644 --- a/internal/acceptance/openstack/compute/v2/servergroup_test.go +++ b/internal/acceptance/openstack/compute/v2/servergroup_test.go @@ -41,7 +41,7 @@ func TestServergroupsCreateDelete(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } func TestServergroupsAffinityPolicy(t *testing.T) { @@ -100,5 +100,5 @@ func TestServergroupsMicroversionCreateDelete(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 9aa8133de9..5934805ba2 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -45,7 +45,7 @@ func TestServersCreateDestroy(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) @@ -93,12 +93,12 @@ func TestServersWithExtensionsCreateDestroy(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, created) - th.AssertEquals(t, created.AvailabilityZone, "nova") - th.AssertEquals(t, int(created.PowerState), servers.RUNNING) - th.AssertEquals(t, created.TaskState, "") - th.AssertEquals(t, created.VmState, "active") - th.AssertEquals(t, created.LaunchedAt.IsZero(), false) - th.AssertEquals(t, created.TerminatedAt.IsZero(), true) + th.AssertEquals(t, "nova", created.AvailabilityZone) + th.AssertEquals(t, servers.RUNNING, int(created.PowerState)) + th.AssertEquals(t, "", created.TaskState) + th.AssertEquals(t, "active", created.VmState) + th.AssertEquals(t, false, created.LaunchedAt.IsZero()) + th.AssertEquals(t, true, created.TerminatedAt.IsZero()) } func TestServersWithoutImageRef(t *testing.T) { @@ -139,7 +139,7 @@ func TestServersUpdate(t *testing.T) { updated, err := servers.Update(context.TODO(), client, server.ID, updateOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, updated.ID, server.ID) + th.AssertEquals(t, server.ID, updated.ID) err = tools.WaitFor(func(ctx context.Context) (bool, error) { latest, err := servers.Get(ctx, client, updated.ID).Extract() @@ -307,7 +307,7 @@ func TestServersActionRebuild(t *testing.T) { rebuilt, err := servers.Rebuild(context.TODO(), client, server.ID, rebuildOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, rebuilt.ID, server.ID) + th.AssertEquals(t, server.ID, rebuilt.ID) if err = WaitForComputeStatus(client, rebuilt, "REBUILD"); err != nil { t.Fatal(err) @@ -347,7 +347,7 @@ func TestServersActionResizeConfirm(t *testing.T) { server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, server.Flavor["id"], choices.FlavorIDResize) + th.AssertEquals(t, choices.FlavorIDResize, server.Flavor["id"]) } func TestServersActionResizeRevert(t *testing.T) { @@ -379,7 +379,7 @@ func TestServersActionResizeRevert(t *testing.T) { server, err = servers.Get(context.TODO(), client, server.ID).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, server.Flavor["id"], choices.FlavorID) + th.AssertEquals(t, choices.FlavorID, server.Flavor["id"]) } func TestServersActionPause(t *testing.T) { @@ -447,7 +447,7 @@ func TestServersActionLock(t *testing.T) { t.Logf("Attempting to delete locked server %s", server.ID) err = servers.Delete(context.TODO(), client, server.ID).ExtractErr() - th.AssertEquals(t, err != nil, true) + th.AssertEquals(t, true, err != nil) t.Logf("Attempting to unlock server %s", server.ID) err = servers.Unlock(context.TODO(), client, server.ID).ExtractErr() @@ -565,13 +565,13 @@ func TestServersWithExtendedAttributesCreateDestroy(t *testing.T) { t.Logf("Server With Extended Attributes: %#v", created) - th.AssertEquals(t, *created.ReservationID != "", true) - th.AssertEquals(t, *created.LaunchIndex, 0) - th.AssertEquals(t, *created.RAMDiskID == "", true) - th.AssertEquals(t, *created.KernelID == "", true) - th.AssertEquals(t, *created.Hostname != "", true) - th.AssertEquals(t, *created.RootDeviceName != "", true) - th.AssertEquals(t, created.Userdata == nil, true) + th.AssertEquals(t, true, *created.ReservationID != "") + th.AssertEquals(t, 0, *created.LaunchIndex) + th.AssertEquals(t, true, *created.RAMDiskID == "") + th.AssertEquals(t, true, *created.KernelID == "") + th.AssertEquals(t, true, *created.Hostname != "") + th.AssertEquals(t, true, *created.RootDeviceName != "") + th.AssertEquals(t, true, created.Userdata == nil) } func TestServerNoNetworkCreateDestroy(t *testing.T) { @@ -604,7 +604,7 @@ func TestServerNoNetworkCreateDestroy(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) allAddressPages, err := servers.ListAddresses(client, server.ID).AllPages(context.TODO()) th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/compute/v2/services_test.go b/internal/acceptance/openstack/compute/v2/services_test.go index 30b1613aa5..7b8d8725b5 100644 --- a/internal/acceptance/openstack/compute/v2/services_test.go +++ b/internal/acceptance/openstack/compute/v2/services_test.go @@ -33,7 +33,7 @@ func TestServicesList(t *testing.T) { } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } func TestServicesListWithOpts(t *testing.T) { @@ -55,12 +55,12 @@ func TestServicesListWithOpts(t *testing.T) { var found bool for _, service := range allServices { tools.PrintResource(t, service) - th.AssertEquals(t, service.Binary, "nova-scheduler") + th.AssertEquals(t, "nova-scheduler", service.Binary) if service.Binary == "nova-scheduler" { found = true } } - th.AssertEquals(t, found, true) + th.AssertEquals(t, true, found) } diff --git a/internal/acceptance/openstack/compute/v2/volumeattach_test.go b/internal/acceptance/openstack/compute/v2/volumeattach_test.go index d4cce8d700..157f97c4c0 100644 --- a/internal/acceptance/openstack/compute/v2/volumeattach_test.go +++ b/internal/acceptance/openstack/compute/v2/volumeattach_test.go @@ -35,5 +35,5 @@ func TestVolumeAttachAttachment(t *testing.T) { tools.PrintResource(t, volumeAttachment) - th.AssertEquals(t, volumeAttachment.ServerID, server.ID) + th.AssertEquals(t, server.ID, volumeAttachment.ServerID) } From e899b1b8e831a8d661c59e43c9752ec3577fafa5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:49:00 +0000 Subject: [PATCH 1961/2296] build(deps): bump golang.org/x/crypto from 0.27.0 to 0.28.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.27.0 to 0.28.0. - [Commits](https://github.com/golang/crypto/compare/v0.27.0...v0.28.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 0f39082357..8e527ec8e5 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.22 require ( - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.28.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.25.0 // indirect +require golang.org/x/sys v0.26.0 // indirect diff --git a/go.sum b/go.sum index 77cbe99406..7cc4cf43b5 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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 962a74bd19b967b05c87f59ad669bef6a7b17ed8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:53:34 +0000 Subject: [PATCH 1962/2296] build(deps): bump kiegroup/git-backporting from 4.8.1 to 4.8.2 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.8.1 to 4.8.2. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/c5d7f0ea567c9f997ba4d15e442ad3204abe2030...c3daf803062a072dc39fe3e1fb892f6b98a7a4e0) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 0071ecba1d..025e1b6bf3 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -34,7 +34,7 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') || contains(github.event.label.name, 'semver:patch') - uses: kiegroup/git-backporting@c5d7f0ea567c9f997ba4d15e442ad3204abe2030 + uses: kiegroup/git-backporting@c3daf803062a072dc39fe3e1fb892f6b98a7a4e0 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -95,7 +95,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@c5d7f0ea567c9f997ba4d15e442ad3204abe2030 + uses: kiegroup/git-backporting@c3daf803062a072dc39fe3e1fb892f6b98a7a4e0 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From cbb11892042f6436b8f4b200359f11fc5f2acd15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:06:06 +0000 Subject: [PATCH 1963/2296] build(deps): bump kiegroup/git-backporting from 4.8.2 to 4.8.3 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.8.2 to 4.8.3. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/c3daf803062a072dc39fe3e1fb892f6b98a7a4e0...a14014e89e9bac5210298eb1c4047f037b359c97) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 025e1b6bf3..1f7d064ca3 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -34,7 +34,7 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') || contains(github.event.label.name, 'semver:patch') - uses: kiegroup/git-backporting@c3daf803062a072dc39fe3e1fb892f6b98a7a4e0 + uses: kiegroup/git-backporting@a14014e89e9bac5210298eb1c4047f037b359c97 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -95,7 +95,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@c3daf803062a072dc39fe3e1fb892f6b98a7a4e0 + uses: kiegroup/git-backporting@a14014e89e9bac5210298eb1c4047f037b359c97 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From 2b109d77d065c0eac5f3521cd57338a2248c81fe Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 10 Oct 2024 18:02:04 +0200 Subject: [PATCH 1964/2296] [manila] add scheduler_hints to the shares CreateOpts --- .../sharedfilesystems/v2/shares/requests.go | 19 +++++++++++++++++++ .../v2/shares/testing/fixtures_test.go | 10 ++++++++-- .../v2/shares/testing/request_test.go | 12 +++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index d228f6a36c..8c111b4837 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -7,6 +7,22 @@ import ( "github.com/gophercloud/gophercloud/v2/pagination" ) +// SchedulerHints contains options for providing scheduler hints when creating +// a Share. +type SchedulerHints struct { + // DifferentHost will place the share on a different back-end that does not + // host the given shares. + DifferentHost string `json:"different_host,omitempty"` + + // SameHost will place the share on a back-end that hosts the given shares. + SameHost string `json:"same_host,omitempty"` + + // OnlyHost value must be a manage-share service host in + // host@backend#POOL format (admin only). Only available in and beyond + // API version 2.67 + OnlyHost string `json:"only_host,omitempty"` +} + // CreateOptsBuilder allows extensions to add additional parameters to the // Create request. type CreateOptsBuilder interface { @@ -48,6 +64,9 @@ type CreateOpts struct { ConsistencyGroupID string `json:"consistency_group_id,omitempty"` // The availability zone of the share AvailabilityZone string `json:"availability_zone,omitempty"` + // SchedulerHints are hints for the scheduler to select the share backend + // Only available in and beyond API version 2.65 + SchedulerHints *SchedulerHints `json:"scheduler_hints,omitempty"` } // ToShareCreateMap assembles a request body based on the contents of a diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go index 89a2c5461e..cfb9e73d2e 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go @@ -18,7 +18,11 @@ var createRequest = `{ "share": { "name": "my_test_share", "size": 1, - "share_proto": "NFS" + "share_proto": "NFS", + "scheduler_hints": { + "same_host": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", + "different_host": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef" + } } }` @@ -61,7 +65,9 @@ var createResponse = `{ "is_public": true, "metadata": { "project": "my_app", - "aim": "doc" + "aim": "doc", + "__affinity_same_host": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", + "__affinity_different_host": "e268f4aa-d571-43dd-9ab3-f49ad06ffaef" }, "id": "011d21e2-fbc3-4e4a-9993-9ea223f73264", "description": "My custom share London" diff --git a/openstack/sharedfilesystems/v2/shares/testing/request_test.go b/openstack/sharedfilesystems/v2/shares/testing/request_test.go index f9af3da84b..396c73a795 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/request_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/request_test.go @@ -16,13 +16,23 @@ func TestCreate(t *testing.T) { MockCreateResponse(t) - options := &shares.CreateOpts{Size: 1, Name: "my_test_share", ShareProto: "NFS"} + options := &shares.CreateOpts{ + Size: 1, + Name: "my_test_share", + ShareProto: "NFS", + SchedulerHints: &shares.SchedulerHints{ + SameHost: "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", + DifferentHost: "e268f4aa-d571-43dd-9ab3-f49ad06ffaef", + }, + } n, err := shares.Create(context.TODO(), client.ServiceClient(), options).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, n.Name, "my_test_share") th.AssertEquals(t, n.Size, 1) th.AssertEquals(t, n.ShareProto, "NFS") + th.AssertEquals(t, n.Metadata["__affinity_same_host"], "e268f4aa-d571-43dd-9ab3-f49ad06ffaef") + th.AssertEquals(t, n.Metadata["__affinity_different_host"], "e268f4aa-d571-43dd-9ab3-f49ad06ffaef") } func TestUpdate(t *testing.T) { From f8007eedf55a73bcefb69f0da5efe1f555179c67 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 9 Oct 2024 13:51:57 +0200 Subject: [PATCH 1965/2296] [manila] add share_group_id to share's CreateOpts --- openstack/sharedfilesystems/v2/shares/requests.go | 2 ++ openstack/sharedfilesystems/v2/shares/results.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/openstack/sharedfilesystems/v2/shares/requests.go b/openstack/sharedfilesystems/v2/shares/requests.go index d228f6a36c..07949b3caa 100644 --- a/openstack/sharedfilesystems/v2/shares/requests.go +++ b/openstack/sharedfilesystems/v2/shares/requests.go @@ -40,6 +40,8 @@ type CreateOpts struct { SnapshotID string `json:"snapshot_id,omitempty"` // Determines whether or not the share is public IsPublic *bool `json:"is_public,omitempty"` + // The UUID of the share group. Available starting from the microversion 2.31 + ShareGroupID string `json:"share_group_id,omitempty"` // Key value pairs of user defined metadata Metadata map[string]string `json:"metadata,omitempty"` // The UUID of the share network to which the share belongs to diff --git a/openstack/sharedfilesystems/v2/shares/results.go b/openstack/sharedfilesystems/v2/shares/results.go index d3fe9d8437..7c1b621327 100644 --- a/openstack/sharedfilesystems/v2/shares/results.go +++ b/openstack/sharedfilesystems/v2/shares/results.go @@ -54,6 +54,8 @@ type Share struct { ShareType string `json:"share_type"` // The name of the share type. ShareTypeName string `json:"share_type_name"` + // The UUID of the share group. Available starting from the microversion 2.31 + ShareGroupID string `json:"share_group_id"` // Size of the share in GB Size int `json:"size"` // UUID of the snapshot from which to create the share From e7b3ff02ec518dc21a7a77c89b20b286b9724b66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2024 09:46:44 +0000 Subject: [PATCH 1966/2296] build(deps): bump thollander/actions-comment-pull-request Bumps [thollander/actions-comment-pull-request](https://github.com/thollander/actions-comment-pull-request) from 2.5.0 to 3.0.0. - [Release notes](https://github.com/thollander/actions-comment-pull-request/releases) - [Commits](https://github.com/thollander/actions-comment-pull-request/compare/fabd468d3a1a0b97feee5f6b9e499eab0dd903f6...e2c37e53a7d2227b61585343765f73a9ca57eda9) --- updated-dependencies: - dependency-name: thollander/actions-comment-pull-request dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 1f7d064ca3..a7837adfef 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -60,7 +60,7 @@ jobs: || contains(github.event.label.name, 'semver:major') || contains(github.event.label.name, 'semver:minor') || contains(github.event.label.name, 'semver:unknown') - uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 + uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 with: message: | Labels `semver-major`, `semver-minor` and `semver-unknown` block @@ -119,7 +119,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:unknown') || contains(github.event.label.name, 'semver:major') || contains(github.event.label.name, 'semver:unknown') - uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 + uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 with: message: | Labels `semver-major` and `semver-unknown` block backports to the From 470f1bc8d813adf72a36854991ebd843c4e86d42 Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 27 Sep 2024 10:00:51 +0200 Subject: [PATCH 1967/2296] [core]: allow empty struct member updates --- .../acceptance/openstack/compute/v2/aggregates_test.go | 5 +++-- internal/acceptance/openstack/compute/v2/flavors_test.go | 2 +- internal/acceptance/openstack/compute/v2/secgroup_test.go | 2 +- internal/acceptance/openstack/compute/v2/servers_test.go | 2 +- .../openstack/loadbalancer/v2/flavorprofiles_test.go | 5 +++-- .../acceptance/openstack/loadbalancer/v2/flavors_test.go | 5 +++-- internal/ptr/ptr.go | 5 +++++ openstack/compute/v2/aggregates/requests.go | 4 ++-- openstack/compute/v2/aggregates/testing/requests_test.go | 6 +++--- openstack/compute/v2/flavors/requests.go | 2 +- openstack/compute/v2/flavors/testing/requests_test.go | 3 ++- openstack/compute/v2/secgroups/requests.go | 2 +- openstack/compute/v2/secgroups/testing/requests_test.go | 6 +++--- openstack/compute/v2/servers/requests.go | 6 +++--- openstack/compute/v2/servers/testing/requests_test.go | 3 ++- openstack/loadbalancer/v2/flavorprofiles/requests.go | 6 +++--- .../v2/flavorprofiles/testing/requests_test.go | 7 ++++--- openstack/loadbalancer/v2/flavors/requests.go | 6 +++--- openstack/loadbalancer/v2/flavors/testing/requests_test.go | 7 ++++--- 19 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 internal/ptr/ptr.go diff --git a/internal/acceptance/openstack/compute/v2/aggregates_test.go b/internal/acceptance/openstack/compute/v2/aggregates_test.go index 88ac3a3365..4b20b3ff29 100644 --- a/internal/acceptance/openstack/compute/v2/aggregates_test.go +++ b/internal/acceptance/openstack/compute/v2/aggregates_test.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/hypervisors" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -47,8 +48,8 @@ func TestAggregatesCRUD(t *testing.T) { tools.PrintResource(t, aggregate) updateOpts := aggregates.UpdateOpts{ - Name: "new_aggregate_name", - AvailabilityZone: "new_azone", + Name: ptr.To("new_aggregate_name"), + AvailabilityZone: ptr.To("new_azone"), } updatedAggregate, err := aggregates.Update(context.TODO(), client, aggregate.ID, updateOpts).Extract() diff --git a/internal/acceptance/openstack/compute/v2/flavors_test.go b/internal/acceptance/openstack/compute/v2/flavors_test.go index 412149adfe..7a6ca6b2d5 100644 --- a/internal/acceptance/openstack/compute/v2/flavors_test.go +++ b/internal/acceptance/openstack/compute/v2/flavors_test.go @@ -135,7 +135,7 @@ func TestFlavorsCreateUpdateDelete(t *testing.T) { newFlavorDescription := "This is the new description" updateOpts := flavors.UpdateOpts{ - Description: newFlavorDescription, + Description: &newFlavorDescription, } flavor, err = flavors.Update(context.TODO(), client, flavor.ID, updateOpts).Extract() diff --git a/internal/acceptance/openstack/compute/v2/secgroup_test.go b/internal/acceptance/openstack/compute/v2/secgroup_test.go index dd6b6dd85e..0a883d3789 100644 --- a/internal/acceptance/openstack/compute/v2/secgroup_test.go +++ b/internal/acceptance/openstack/compute/v2/secgroup_test.go @@ -48,7 +48,7 @@ func TestSecGroupsCRUD(t *testing.T) { newName := tools.RandomString("secgroup_", 4) description := "" updateOpts := secgroups.UpdateOpts{ - Name: newName, + Name: &newName, Description: &description, } updatedSecurityGroup, err := secgroups.Update(context.TODO(), client, securityGroup.ID, updateOpts).Extract() diff --git a/internal/acceptance/openstack/compute/v2/servers_test.go b/internal/acceptance/openstack/compute/v2/servers_test.go index 5934805ba2..67ef63028f 100644 --- a/internal/acceptance/openstack/compute/v2/servers_test.go +++ b/internal/acceptance/openstack/compute/v2/servers_test.go @@ -133,7 +133,7 @@ func TestServersUpdate(t *testing.T) { t.Logf("Attempting to rename the server to %s.", alternateName) updateOpts := servers.UpdateOpts{ - Name: alternateName, + Name: &alternateName, } updated, err := servers.Update(context.TODO(), client, server.ID, updateOpts).Extract() diff --git a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go index 1a6ebadf28..7151a3458b 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/flavorprofiles_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -41,13 +42,13 @@ func TestFlavorProfilesCRUD(t *testing.T) { th.AssertEquals(t, "amphora", flavorProfile.ProviderName) flavorProfileUpdateOpts := flavorprofiles.UpdateOpts{ - Name: tools.RandomString("TESTACCTUP-", 8), + Name: ptr.To(tools.RandomString("TESTACCTUP-", 8)), } flavorProfileUpdated, err := flavorprofiles.Update(context.TODO(), lbClient, flavorProfile.ID, flavorProfileUpdateOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, flavorProfileUpdateOpts.Name, flavorProfileUpdated.Name) + 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 index b714af7324..40bd1cc11a 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/flavors_test.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -54,13 +55,13 @@ func TestFlavorsCRUD(t *testing.T) { th.AssertEquals(t, flavor.FlavorProfileId, flavorProfile.ID) flavorUpdateOpts := flavors.UpdateOpts{ - Name: tools.RandomString("TESTACCTUP-", 8), + Name: ptr.To(tools.RandomString("TESTACCTUP-", 8)), } flavorUpdated, err := flavors.Update(context.TODO(), lbClient, flavor.ID, flavorUpdateOpts).Extract() th.AssertNoErr(t, err) - th.AssertEquals(t, flavorUpdateOpts.Name, flavorUpdated.Name) + th.AssertEquals(t, *flavorUpdateOpts.Name, flavorUpdated.Name) t.Logf("Successfully updated flavor %s", flavorUpdated.Name) } diff --git a/internal/ptr/ptr.go b/internal/ptr/ptr.go new file mode 100644 index 0000000000..6c3ee9bd51 --- /dev/null +++ b/internal/ptr/ptr.go @@ -0,0 +1,5 @@ +package ptr + +func To[T any](v T) *T { + return &v +} diff --git a/openstack/compute/v2/aggregates/requests.go b/openstack/compute/v2/aggregates/requests.go index 7111c203b0..be20cbffe2 100644 --- a/openstack/compute/v2/aggregates/requests.go +++ b/openstack/compute/v2/aggregates/requests.go @@ -66,13 +66,13 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, aggregateID int type UpdateOpts struct { // The name of the host aggregate. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // The availability zone of the host aggregate. // You should use a custom availability zone rather than // the default returned by the os-availability-zone API. // The availability zone must not include ‘:’ in its name. - AvailabilityZone string `json:"availability_zone,omitempty"` + AvailabilityZone *string `json:"availability_zone,omitempty"` } func (opts UpdateOpts) ToAggregatesUpdateMap() (map[string]any, error) { diff --git a/openstack/compute/v2/aggregates/testing/requests_test.go b/openstack/compute/v2/aggregates/testing/requests_test.go index 2e229bf71c..601150a58d 100644 --- a/openstack/compute/v2/aggregates/testing/requests_test.go +++ b/openstack/compute/v2/aggregates/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -86,10 +87,9 @@ func TestUpdateAggregate(t *testing.T) { HandleUpdateSuccessfully(t) expected := UpdatedAggregate - opts := aggregates.UpdateOpts{ - Name: "test-aggregates2", - AvailabilityZone: "nova2", + Name: ptr.To("test-aggregates2"), + AvailabilityZone: ptr.To("nova2"), } actual, err := aggregates.Update(context.TODO(), client.ServiceClient(), expected.ID, opts).Extract() diff --git a/openstack/compute/v2/flavors/requests.go b/openstack/compute/v2/flavors/requests.go index 119f5e78e8..fe27fe97da 100644 --- a/openstack/compute/v2/flavors/requests.go +++ b/openstack/compute/v2/flavors/requests.go @@ -165,7 +165,7 @@ 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"` + Description *string `json:"description,omitempty"` } // ToFlavorUpdateMap constructs a request body from UpdateOpts. diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index d22e3dfa9c..79710fc126 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -7,6 +7,7 @@ import ( "reflect" "testing" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -242,7 +243,7 @@ func TestUpdateFlavor(t *testing.T) { }) opts := &flavors.UpdateOpts{ - Description: "foo", + Description: ptr.To("foo"), } actual, err := flavors.Update(context.TODO(), fake.ServiceClient(), "12345678", opts).Extract() if err != nil { diff --git a/openstack/compute/v2/secgroups/requests.go b/openstack/compute/v2/secgroups/requests.go index d69a99b4f0..c43575aeb4 100644 --- a/openstack/compute/v2/secgroups/requests.go +++ b/openstack/compute/v2/secgroups/requests.go @@ -61,7 +61,7 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateO // UpdateOpts is the struct responsible for updating an existing security group. type UpdateOpts struct { // the name of your security group. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // the description of your security group. Description *string `json:"description,omitempty"` } diff --git a/openstack/compute/v2/secgroups/testing/requests_test.go b/openstack/compute/v2/secgroups/testing/requests_test.go index 222295d5e8..53c473ce0a 100644 --- a/openstack/compute/v2/secgroups/testing/requests_test.go +++ b/openstack/compute/v2/secgroups/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/secgroups" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -116,10 +117,9 @@ func TestUpdate(t *testing.T) { mockUpdateGroupResponse(t, groupID) - description := "new_desc" opts := secgroups.UpdateOpts{ - Name: "new_name", - Description: &description, + Name: ptr.To("new_name"), + Description: ptr.To("new_desc"), } group, err := secgroups.Update(context.TODO(), client.ServiceClient(), groupID, opts).Extract() diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index dd3b132d1d..d7d5ad81c1 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -641,13 +641,13 @@ type UpdateOpts struct { // Name changes the displayed name of the server. // The server host name will *not* change. // Server names are not constrained to be unique, even within the same tenant. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // AccessIPv4 provides a new IPv4 address for the instance. - AccessIPv4 string `json:"accessIPv4,omitempty"` + AccessIPv4 *string `json:"accessIPv4,omitempty"` // AccessIPv6 provides a new IPv6 address for the instance. - AccessIPv6 string `json:"accessIPv6,omitempty"` + AccessIPv6 *string `json:"accessIPv6,omitempty"` } // ToServerUpdateMap formats an UpdateOpts structure into a request body. diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index b6ebefce5e..8024add04e 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -7,6 +7,7 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" @@ -807,7 +808,7 @@ func TestUpdateServer(t *testing.T) { HandleServerUpdateSuccessfully(t) client := client.ServiceClient() - actual, err := servers.Update(context.TODO(), client, "1234asdf", servers.UpdateOpts{Name: "new-name"}).Extract() + actual, err := servers.Update(context.TODO(), client, "1234asdf", servers.UpdateOpts{Name: ptr.To("new-name")}).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) } diff --git a/openstack/loadbalancer/v2/flavorprofiles/requests.go b/openstack/loadbalancer/v2/flavorprofiles/requests.go index e51b9d99e6..3c764592cc 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/requests.go +++ b/openstack/loadbalancer/v2/flavorprofiles/requests.go @@ -100,13 +100,13 @@ type UpdateOptsBuilder interface { // operation. type UpdateOpts struct { // Human-readable name for the Loadbalancer. Does not have to be unique. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Providing the name of the provider supported by the Octavia installation. - ProviderName string `json:"provider_name,omitempty"` + ProviderName *string `json:"provider_name,omitempty"` // Providing the json string containing the flavor metadata. - FlavorData string `json:"flavor_data,omitempty"` + FlavorData *string `json:"flavor_data,omitempty"` } // ToFlavorProfileUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go b/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go index aebb2c9408..5cefc8c971 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/requests_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" "github.com/gophercloud/gophercloud/v2/pagination" @@ -106,9 +107,9 @@ func TestUpdateFlavorProfile(t *testing.T) { client := fake.ServiceClient() actual, err := flavorprofiles.Update(context.TODO(), client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272", flavorprofiles.UpdateOpts{ - Name: "amphora-test-updated", - ProviderName: "amphora", - FlavorData: "{\"loadbalancer_topology\": \"SINGLE\"}", + Name: ptr.To("amphora-test-updated"), + ProviderName: ptr.To("amphora"), + FlavorData: ptr.To(`{"loadbalancer_topology": "SINGLE"}`), }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go index 710a6edf5b..4a0640d5ae 100644 --- a/openstack/loadbalancer/v2/flavors/requests.go +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -106,13 +106,13 @@ type UpdateOptsBuilder interface { // operation. type UpdateOpts struct { // Human-readable name for the Loadbalancer. Does not have to be unique. - Name string `json:"name,omitempty"` + Name *string `json:"name,omitempty"` // Human-readable description for the Flavor. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` // If the resource is available for use. - Enabled bool `json:"enabled,omitempty"` + Enabled *bool `json:"enabled,omitempty"` } // ToFlavorUpdateMap builds a request body from UpdateOpts. diff --git a/openstack/loadbalancer/v2/flavors/testing/requests_test.go b/openstack/loadbalancer/v2/flavors/testing/requests_test.go index fd668a2134..80a9ad2d05 100644 --- a/openstack/loadbalancer/v2/flavors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavors/testing/requests_test.go @@ -6,6 +6,7 @@ import ( "net/http" "testing" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" "github.com/gophercloud/gophercloud/v2/pagination" @@ -154,9 +155,9 @@ func TestUpdateFlavor(t *testing.T) { client := fake.ServiceClient() actual, err := flavors.Update(context.TODO(), client, "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", flavors.UpdateOpts{ - Name: "Basic v2", - Description: "Rename flavor", - Enabled: true, + Name: ptr.To("Basic v2"), + Description: ptr.To("Rename flavor"), + Enabled: ptr.To(true), }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From c9703e7913d8c333b79a593a8b22bfdca3b5f06d Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 27 Sep 2024 10:24:14 +0200 Subject: [PATCH 1968/2296] [octavia]: allow creating a disabled flavor Octavia Flavor's `enabled` is true by default --- .../openstack/loadbalancer/v2/loadbalancer.go | 5 ++- openstack/loadbalancer/v2/flavors/requests.go | 2 +- .../v2/flavors/testing/fixtures.go | 39 +++++++++++++++++++ .../v2/flavors/testing/requests_test.go | 18 ++++++++- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index e8596506ef..907e9d5658 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -10,6 +10,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/internal/ptr" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" @@ -724,7 +725,7 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient, flavorProfile Name: flavorName, Description: description, FlavorProfileId: flavorProfile.ID, - Enabled: true, + Enabled: ptr.To(false), } flavor, err := flavors.Create(context.TODO(), client, createOpts).Extract() @@ -737,7 +738,7 @@ func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient, flavorProfile 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) + th.AssertEquals(t, false, flavor.Enabled) return flavor, nil } diff --git a/openstack/loadbalancer/v2/flavors/requests.go b/openstack/loadbalancer/v2/flavors/requests.go index 4a0640d5ae..499bb3bd0d 100644 --- a/openstack/loadbalancer/v2/flavors/requests.go +++ b/openstack/loadbalancer/v2/flavors/requests.go @@ -68,7 +68,7 @@ type CreateOpts struct { FlavorProfileId string `json:"flavor_profile_id" required:"true"` // If the resource is available for use. The default is True. - Enabled bool `json:"enabled,omitempty"` + Enabled *bool `json:"enabled,omitempty"` } // ToFlavorCreateMap builds a request body from CreateOpts. diff --git a/openstack/loadbalancer/v2/flavors/testing/fixtures.go b/openstack/loadbalancer/v2/flavors/testing/fixtures.go index a3169d7ece..5423ca16b7 100644 --- a/openstack/loadbalancer/v2/flavors/testing/fixtures.go +++ b/openstack/loadbalancer/v2/flavors/testing/fixtures.go @@ -44,6 +44,18 @@ const SingleFlavorBody = ` } ` +const SingleFlavorDisabledBody = ` +{ + "flavor": { + "id": "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", + "name": "Basic", + "description": "A basic standalone Octavia load balancer.", + "enabled": false, + "flavor_profile_id": "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1" + } +} +` + const PostUpdateFlavorBody = ` { "flavor": { @@ -81,6 +93,14 @@ var ( FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", } + FlavorDisabled = flavors.Flavor{ + ID: "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", + Name: "Basic", + Description: "A basic standalone Octavia load balancer.", + Enabled: false, + FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", + } + FlavorUpdated = flavors.Flavor{ ID: "5548c807-e6e8-43d7-9ea4-b38d34dd74a0", Name: "Basic v2", @@ -130,6 +150,25 @@ func HandleFlavorCreationSuccessfully(t *testing.T, response string) { }) } +func HandleFlavorCreationSuccessfullyDisabled(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": false, + "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") diff --git a/openstack/loadbalancer/v2/flavors/testing/requests_test.go b/openstack/loadbalancer/v2/flavors/testing/requests_test.go index 80a9ad2d05..5b19f962ca 100644 --- a/openstack/loadbalancer/v2/flavors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavors/testing/requests_test.go @@ -110,7 +110,7 @@ func TestCreateFlavor(t *testing.T) { actual, err := flavors.Create(context.TODO(), fake.ServiceClient(), flavors.CreateOpts{ Name: "Basic", Description: "A basic standalone Octavia load balancer.", - Enabled: true, + Enabled: ptr.To(true), FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", }).Extract() th.AssertNoErr(t, err) @@ -118,6 +118,22 @@ func TestCreateFlavor(t *testing.T) { th.CheckDeepEquals(t, FlavorDb, *actual) } +func TestCreateFlavorDisabled(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleFlavorCreationSuccessfullyDisabled(t, SingleFlavorDisabledBody) + + actual, err := flavors.Create(context.TODO(), fake.ServiceClient(), flavors.CreateOpts{ + Name: "Basic", + Description: "A basic standalone Octavia load balancer.", + Enabled: ptr.To(false), + FlavorProfileId: "9daa2768-74e7-4d13-bf5d-1b8e0dc239e1", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, FlavorDisabled, *actual) +} + func TestRequiredCreateOpts(t *testing.T) { res := flavors.Create(context.TODO(), fake.ServiceClient(), flavors.CreateOpts{}) if res.Err == nil { From 3966ef34a7fe9c5518d23a4452b23a12a4d55893 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 17 Oct 2024 10:23:58 +0200 Subject: [PATCH 1969/2296] docs: Remove outdated godoc The `doc.go` file, and [go.dev](https://pkg.go.dev/github.com/gophercloud/gophercloud/v2) as a consequence, was still recommending to use utils for authentication. Since the automatically-generated documentation on the Go website already includes the README, we can delete doc.go and keep README.md as the canonical introduction to the module. --- README.md | 2 - doc.go | 148 ------------------------------------------------------ 2 files changed, 150 deletions(-) delete mode 100644 doc.go diff --git a/README.md b/README.md index e9ba39bb79..ca47f5b0ba 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # Gophercloud: an OpenStack SDK for Go [![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master) -[Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud/v2) - Gophercloud is a Go SDK for OpenStack. Join us on kubernetes slack, on [#gophercloud](https://kubernetes.slack.com/archives/C05G4NJ6P6X). Visit [slack.k8s.io](https://slack.k8s.io) for an invitation. diff --git a/doc.go b/doc.go deleted file mode 100644 index a755ecb180..0000000000 --- a/doc.go +++ /dev/null @@ -1,148 +0,0 @@ -/* -Package gophercloud provides a multi-vendor interface to OpenStack-compatible -clouds. The library has a three-level hierarchy: providers, services, and -resources. - -# Authenticating with Providers - -Provider structs represent the cloud providers that offer and manage a -collection of services. You will generally want to create one Provider -client per OpenStack cloud. - - It is now recommended to use the `clientconfig` package found at - https://github.com/gophercloud/utils/tree/master/openstack/clientconfig - for all authentication purposes. - - The below documentation is still relevant. clientconfig simply implements - the below and presents it in an easier and more flexible way. - -Use your OpenStack credentials to create a Provider client. The -IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in -information provided by the cloud operator. Additionally, the cloud may refer to -TenantID or TenantName as project_id and project_name. Credentials are -specified like so: - - opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - Username: "{username}", - Password: "{password}", - TenantID: "{tenant_id}", - } - - provider, err := openstack.AuthenticatedClient(context.TODO(), opts) - -You can authenticate with a token by doing: - - opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - TokenID: "{token_id}", - TenantID: "{tenant_id}", - } - - provider, err := openstack.AuthenticatedClient(context.TODO(), opts) - -You may also use the openstack.AuthOptionsFromEnv() helper function. This -function reads in standard environment variables frequently found in an -OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant" -instead of "project". - - opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(context.TODO(), opts) - -# Service Clients - -Service structs are specific to a provider and handle all of the logic and -operations for a particular OpenStack service. Examples of services include: -Compute, Object Storage, Block Storage. In order to define one, you need to -pass in the parent provider, like so: - - opts := gophercloud.EndpointOpts{Region: "RegionOne"} - - client, err := openstack.NewComputeV2(provider, opts) - -# Resources - -Resource structs are the domain models that services make use of in order -to work with and represent the state of API resources: - - server, err := servers.Get(context.TODO(), client, "{serverId}").Extract() - -Intermediate Result structs are returned for API operations, which allow -generic access to the HTTP headers, response body, and any errors associated -with the network transaction. To turn a result into a usable resource struct, -you must call the Extract method which is chained to the response, or an -Extract function from an applicable extension: - - result := servers.Get(context.TODO(), client, "{serverId}") - - // Attempt to extract the disk configuration from the OS-DCF disk config - // extension: - config, err := diskconfig.ExtractGet(result) - -All requests that enumerate a collection return a Pager struct that is used to -iterate through the results one page at a time. Use the EachPage method on that -Pager to handle each successive Page in a closure, then use the appropriate -extraction method from that request's package to interpret that Page as a slice -of results: - - err := servers.List(client, nil).EachPage(context.TODO(), func (_ context.Context, page pagination.Page) (bool, error) { - s, err := servers.ExtractServers(page) - if err != nil { - return false, err - } - - // Handle the []servers.Server slice. - - // Return "false" or an error to prematurely stop fetching new pages. - return true, nil - }) - -If you want to obtain the entire collection of pages without doing any -intermediary processing on each page, you can use the AllPages method: - - allPages, err := servers.List(client, nil).AllPages(context.TODO()) - allServers, err := servers.ExtractServers(allPages) - -This top-level package contains utility functions and data types that are used -throughout the provider and service packages. Of particular note for end users -are the AuthOptions and EndpointOpts structs. - -An example retry backoff function, which respects the 429 HTTP response code and a "Retry-After" header: - - endpoint := "http://localhost:5000" - provider, err := openstack.NewClient(endpoint) - if err != nil { - panic(err) - } - provider.MaxBackoffRetries = 3 // max three retries - provider.RetryBackoffFunc = func(ctx context.Context, respErr *ErrUnexpectedResponseCode, e error, retries uint) error { - retryAfter := respErr.ResponseHeader.Get("Retry-After") - if retryAfter == "" { - return e - } - - var sleep time.Duration - - // Parse delay seconds or HTTP date - if v, err := strconv.ParseUint(retryAfter, 10, 32); err == nil { - sleep = time.Duration(v) * time.Second - } else if v, err := time.Parse(http.TimeFormat, retryAfter); err == nil { - sleep = time.Until(v) - } else { - return e - } - - if ctx != nil { - select { - case <-time.After(sleep): - case <-ctx.Done(): - return e - } - } else { - time.Sleep(sleep) - } - - return nil - } -*/ -package gophercloud From eb799fe35bb0cff797d62d1bdd85c08c82fb8def Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 21 Oct 2024 12:04:40 +0200 Subject: [PATCH 1970/2296] Fix clear-needinfo The gh tool must be set to operate on an issue rather than on a PR. --- .github/workflows/label-issue.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/label-issue.yaml b/.github/workflows/label-issue.yaml index 723f4cd04e..a1fbff16e0 100644 --- a/.github/workflows/label-issue.yaml +++ b/.github/workflows/label-issue.yaml @@ -9,9 +9,11 @@ jobs: name: Clear needinfo if: ${{ github.event.issue.user.login }} == ${{ github.event.comment.user.login }} runs-on: ubuntu-latest + permissions: + issues: write steps: - - run: gh pr edit "$NUMBER" --remove-label "needinfo" + - run: gh issue edit "$NUMBER" --remove-label "needinfo" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - NUMBER: ${{ github.event.pull_request.number }} + NUMBER: ${{ github.event.issue.number }} From c74ec6b293da38c0915737fcf23d2523ec76c98a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:11:02 +0000 Subject: [PATCH 1971/2296] build(deps): bump kiegroup/git-backporting from 4.8.3 to 4.8.4 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.8.3 to 4.8.4. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/a14014e89e9bac5210298eb1c4047f037b359c97...b9ed3ac959d1479e81bf4f0a5e5f0a72251ce895) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index a7837adfef..f8cbaf657c 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -34,7 +34,7 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') || contains(github.event.label.name, 'semver:patch') - uses: kiegroup/git-backporting@a14014e89e9bac5210298eb1c4047f037b359c97 + uses: kiegroup/git-backporting@b9ed3ac959d1479e81bf4f0a5e5f0a72251ce895 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -95,7 +95,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@a14014e89e9bac5210298eb1c4047f037b359c97 + uses: kiegroup/git-backporting@b9ed3ac959d1479e81bf4f0a5e5f0a72251ce895 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From 4659d9bb9f069d92ed52e7a9193b1a1244c0da6b Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Mon, 4 Nov 2024 11:26:20 +0100 Subject: [PATCH 1972/2296] actions: Replace external dependency with gh Comment with [gh](https://cli.github.com/). --- .github/workflows/backport.yaml | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index f8cbaf657c..69896c8af2 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -60,11 +60,13 @@ jobs: || contains(github.event.label.name, 'semver:major') || contains(github.event.label.name, 'semver:minor') || contains(github.event.label.name, 'semver:unknown') - uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 - with: - message: | - Labels `semver-major`, `semver-minor` and `semver-unknown` block - backports to the legacy branch `v1`. + run: gh pr comment "$NUMBER" --body "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + BODY: > + Labels `semver-major`, `semver-minor` and `semver-unknown` block backports to the legacy branch `v1`. backport_v2: name: "Backport to v2" @@ -119,8 +121,10 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:unknown') || contains(github.event.label.name, 'semver:major') || contains(github.event.label.name, 'semver:unknown') - uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 - with: - message: | - Labels `semver-major` and `semver-unknown` block backports to the - stable branch `v2`. + run: gh pr comment "$NUMBER" --body "$BODY" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ github.event.pull_request.number }} + BODY: > + Labels `semver-major` and `semver-unknown` block backports to the stable branch `v2`. From 700c3fd3a0a4f855b6e9f1e81f36688948d54292 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 09:21:06 +0000 Subject: [PATCH 1973/2296] build(deps): bump golang.org/x/crypto from 0.28.0 to 0.29.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.28.0 to 0.29.0. - [Commits](https://github.com/golang/crypto/compare/v0.28.0...v0.29.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 8e527ec8e5..45961985de 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.22 require ( - golang.org/x/crypto v0.28.0 + golang.org/x/crypto v0.29.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.26.0 // indirect +require golang.org/x/sys v0.27.0 // indirect diff --git a/go.sum b/go.sum index 7cc4cf43b5..a14f597436 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= 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 0e5b612d57257cf88fb061c2245c4b52eb290cd8 Mon Sep 17 00:00:00 2001 From: Anthony ROUSSEL Date: Sat, 16 Nov 2024 21:19:52 +0100 Subject: [PATCH 1974/2296] Adding CI job for testing workflow (Mistral) --- .github/workflows/functional-workflow.yaml | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/functional-workflow.yaml diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml new file mode 100644 index 0000000000..f124fc6412 --- /dev/null +++ b/.github/workflows/functional-workflow.yaml @@ -0,0 +1,67 @@ +name: functional-workflow +on: + pull_request: + paths: + - '**workflow**' + schedule: + - cron: '0 0 */3 * *' +jobs: + functional-workflow: + strategy: + fail-fast: false + matrix: + include: + - name: "master" + openstack_version: "master" + ubuntu_version: "22.04" + mistral_plugin_version: "master" + additional_services: "openstack-cli-server" + - name: "dalmatian" + openstack_version: "stable/2024.2" + ubuntu_version: "22.04" + mistral_plugin_version: "stable/2024.2" + additional_services: "openstack-cli-server" + - name: "caracal" + openstack_version: "stable/2024.1" + ubuntu_version: "22.04" + mistral_plugin_version: "stable/2024.1" + additional_services: "" + - name: "bobcat" + openstack_version: "stable/2023.2" + ubuntu_version: "22.04" + # Devstack support is broken with Mistral v2023.2, and requires 2 patches: + # * https://github.com/openstack/mistral/commit/e343ccb078d8ba261ac70afca93f4358589730d3 + # * https://github.com/openstack/mistral/commit/ecdeadeb7a1aa87cba2cdb0c1a2bb1ffc4aabf25 + mistral_plugin_version: "ecdeadeb7a1aa87cba2cdb0c1a2bb1ffc4aabf25" + additional_services: "" + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Deploy OpenStack ${{ matrix.name }} with Mistral and run workflow acceptance tests + steps: + - name: Checkout Gophercloud + uses: actions/checkout@v4 + - name: Deploy devstack + uses: EmilienM/devstack-action@e82a9cbead099cba72f99537e82a360c3e319c69 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin mistral https://github.com/openstack/mistral ${{ matrix.mistral_plugin_version }} + enabled_services: "mistral,mistral-api,mistral-engine,mistral-executor,mistral-event-engine,${{ matrix.additional_services }}" + - name: Checkout go + uses: actions/setup-go@v5 + with: + go-version: '^1.22' + - name: Run Gophercloud acceptance tests + run: ./script/acceptancetest + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + PACKAGE: "./internal/acceptance/openstack/workflow/..." + 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@v4 + with: + name: functional-workflow-${{ matrix.name }}-${{ github.run_id }} + path: /tmp/devstack-logs/* From 3d2bb51a2a1dd1feedf7f190b8688dfaf15ddbac Mon Sep 17 00:00:00 2001 From: Sharpz7 Date: Wed, 30 Oct 2024 01:06:21 +0000 Subject: [PATCH 1975/2296] Added fields for Node API Parity --- openstack/baremetal/v1/nodes/results.go | 82 ++++++++--- .../v1/nodes/testing/fixtures_test.go | 133 +++++++++++++++--- 2 files changed, 177 insertions(+), 38 deletions(-) diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 5c6eb5c190..22babe02d3 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -90,12 +90,19 @@ func (r SubscriptionVendorPassthruResult) Extract() (*SubscriptionVendorPassthru return &s, err } +// Link represents a hyperlink and its relationship to the current resource. +type Link struct { + // Href is the URL of the related resource. + Href string `json:"href"` + + // Rel describes the relationship of the resource to the current + // context (e.g., "self", "bookmark"). + Rel string `json:"rel"` +} + // Node represents a node in the OpenStack Bare Metal API. +// https://docs.openstack.org/api-ref/baremetal/#list-nodes-detailed type Node struct { - // Whether automated cleaning is enabled or disabled on this node. - // Requires microversion 1.47 or later. - AutomatedClean *bool `json:"automated_clean"` - // UUID for the resource. UUID string `json:"uuid"` @@ -183,8 +190,17 @@ type Node struct { // Current deploy step. DeployStep map[string]any `json:"deploy_step"` - // Current service step. - ServiceStep map[string]any `json:"service_step"` + // A list of relative links. Includes the self and bookmark links. + Links []Link `json:"links"` + + // Links to the collection of ports on this node + Ports []Link `json:"ports"` + + // Links to the collection of portgroups on this node. + PortGroups []Link `json:"portgroups"` + + // Links to the collection of states. Note that this resource is also used to request state transitions. + States []Link `json:"states"` // String which can be used by external schedulers to identify this Node as a unit of a specific type of resource. // For more details, see: https://docs.openstack.org/ironic/latest/install/configure-nova-flavors.html @@ -202,9 +218,6 @@ type Node struct { // Deploy interface for a node, e.g. “iscsi”. DeployInterface string `json:"deploy_interface"` - // Firmware interface for a node, e.g. “redfish”. - FirmwareInterface string `json:"firmware_interface"` - // Interface used for node inspection, e.g. “no-inspect”. InspectInterface string `json:"inspect_interface"` @@ -232,9 +245,15 @@ type Node struct { // For vendor-specific functionality on this node, e.g. “no-vendor”. VendorInterface string `json:"vendor_interface"` + // Links to the volume resources. + Volume []Link `json:"volume"` + // Conductor group for a node. Case-insensitive string up to 255 characters, containing a-z, 0-9, _, -, and .. ConductorGroup string `json:"conductor_group"` + // An optional UUID which can be used to denote the “parent” baremetal node. + ParentNode string `json:"parent_node"` + // The node is protected from undeploying, rebuilding and deletion. Protected bool `json:"protected"` @@ -244,14 +263,42 @@ type Node struct { // A string or UUID of the tenant who owns the baremetal node. Owner string `json:"owner"` + // A string or UUID of the tenant who is leasing the object. + Lessee string `json:"lessee"` + + // A string indicating the shard this node belongs to. + Shard string `json:"shard"` + + // Informational text about this node. + Description string `json:"description"` + + // The conductor currently servicing a node. This field is read-only. + Conductor string `json:"conductor"` + + // The UUID of the allocation associated with the node. If not null, will be the same as instance_uuid + // (the opposite is not always true). Unlike instance_uuid, this field is read-only. Please use the + // Allocation API to remove allocations. + AllocationUUID string `json:"allocation_uuid"` + + // Whether the node is retired. A Node tagged as retired will prevent any further + // scheduling of instances, but will still allow for other operations, such as cleaning, to happen + Retired bool `json:"retired"` + + // Reason the node is marked as retired. + RetiredReason string `json:"retired_reason"` + // Static network configuration to use during deployment and cleaning. NetworkData map[string]any `json:"network_data"` - // The UTC date and time when the resource was created, ISO 8601 format. - CreatedAt time.Time `json:"created_at"` + // Whether automated cleaning is enabled or disabled on this node. + // Requires microversion 1.47 or later. + AutomatedClean *bool `json:"automated_clean"` - // The UTC date and time when the resource was updated, ISO 8601 format. May be “null”. - UpdatedAt time.Time `json:"updated_at"` + // Current service step. + ServiceStep map[string]any `json:"service_step"` + + // Firmware interface for a node, e.g. “redfish”. + FirmwareInterface string `json:"firmware_interface"` // The UTC date and time when the provision state was updated, ISO 8601 format. May be “null”. ProvisionUpdatedAt time.Time `json:"provision_updated_at"` @@ -262,12 +309,11 @@ type Node struct { // 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"` - // Whether the node is retired. A Node tagged as retired will prevent any further - // scheduling of instances, but will still allow for other operations, such as cleaning, to happen - Retired bool `json:"retired"` + // The UTC date and time when the resource was created, ISO 8601 format. + CreatedAt time.Time `json:"created_at"` - // Reason the node is marked as retired. - RetiredReason string `json:"retired_reason"` + // 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_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index d47d70b13c..d174edde2b 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -925,23 +925,57 @@ var ( "deploy_ramdisk": "http://172.22.0.1/images/tinyipa-stable-rocky.gz", "ipmi_password": "admin", }, - DriverInternalInfo: map[string]any{}, - Properties: map[string]any{}, - InstanceInfo: map[string]any{}, - InstanceUUID: "", - ChassisUUID: "", - Extra: map[string]any{}, - ConsoleEnabled: false, - RAIDConfig: map[string]any{}, - TargetRAIDConfig: map[string]any{}, - CleanStep: map[string]any{}, - DeployStep: map[string]any{}, + DriverInternalInfo: map[string]any{}, + Properties: map[string]any{}, + InstanceInfo: map[string]any{}, + InstanceUUID: "", + ChassisUUID: "", + Extra: map[string]any{}, + ConsoleEnabled: false, + RAIDConfig: map[string]any{}, + TargetRAIDConfig: map[string]any{}, + CleanStep: map[string]any{}, + DeployStep: map[string]any{}, + Links: []nodes.Link{ + { + Href: "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", + Rel: "self", + }, + { + Href: "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e", + Rel: "bookmark", + }, + }, + Ports: []nodes.Link{ + { + Href: "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", + Rel: "self", + }, + { + Href: "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/ports", + Rel: "bookmark", + }, + }, + PortGroups: []nodes.Link{ + { + Href: "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", + Rel: "self", + }, + { + Href: "http://ironic.example.com:6385/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/portgroups", + Rel: "bookmark"}, + }, + States: []nodes.Link{ + { + Href: "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/states", + Rel: "self", + }, + }, ResourceClass: "", BIOSInterface: "no-bios", BootInterface: "pxe", ConsoleInterface: "no-console", DeployInterface: "iscsi", - FirmwareInterface: "no-firmware", InspectInterface: "no-inspect", ManagementInterface: "ipmitool", NetworkInterface: "flat", @@ -951,14 +985,33 @@ var ( StorageInterface: "noop", Traits: []string{}, VendorInterface: "ipmitool", - ConductorGroup: "", - Protected: false, - ProtectedReason: "", - CreatedAt: createdAtFoo, - UpdatedAt: updatedAt, - ProvisionUpdatedAt: provisonUpdatedAt, - Retired: false, - RetiredReason: "No longer needed", + Volume: []nodes.Link{ + { + Href: "http://ironic.example.com:6385/v1/nodes/d2630783-6ec8-4836-b556-ab427c4b581e/volume", + Rel: "self", + }, + }, + ConductorGroup: "", + ParentNode: "", + Protected: false, + ProtectedReason: "", + Owner: "", + Lessee: "", + Shard: "", + Description: "", + Conductor: "", + AllocationUUID: "", + Retired: false, + RetiredReason: "No longer needed", + NetworkData: map[string]interface{}(nil), + AutomatedClean: nil, + ServiceStep: map[string]interface{}(nil), + FirmwareInterface: "no-firmware", + ProvisionUpdatedAt: provisonUpdatedAt, + InspectionStartedAt: nil, + InspectionFinishedAt: nil, + CreatedAt: createdAtFoo, + UpdatedAt: updatedAt, } NodeFooValidation = nodes.NodeValidation{ @@ -1070,6 +1123,26 @@ var ( InspectionFinishedAt: &InspectionFinishedAt, Retired: false, RetiredReason: "No longer needed", + Links: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", Rel: "bookmark"}, + }, + Ports: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/ports", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/ports", Rel: "bookmark"}, + }, + PortGroups: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/portgroups", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/portgroups", Rel: "bookmark"}, + }, + States: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/states", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/states", Rel: "bookmark"}, + }, + Volume: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/volume", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662/volume", Rel: "bookmark"}, + }, } NodeBaz = nodes.Node{ @@ -1119,6 +1192,26 @@ var ( UpdatedAt: updatedAt, Retired: false, RetiredReason: "No longer needed", + Links: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", Rel: "bookmark"}, + }, + Ports: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/ports", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/ports", Rel: "bookmark"}, + }, + PortGroups: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/portgroups", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/portgroups", Rel: "bookmark"}, + }, + States: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/states", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/states", Rel: "bookmark"}, + }, + Volume: []nodes.Link{ + {Href: "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/volume", Rel: "self"}, + {Href: "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474/volume", Rel: "bookmark"}, + }, } ConfigDriveMap = nodes.ConfigDrive{ From 0d7a8655a2c49553d3f00454eedd0c91f07ca4d4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Nov 2024 17:58:30 +0000 Subject: [PATCH 1976/2296] Drop CI coverage for Antelope Antelope has entered the unmaintained [1] phase. We can no longer rely on this for testing. [1] https://docs.openstack.org/project-team-guide/stable-branches.html#unmaintained Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 5 ----- .github/workflows/functional-basic.yaml | 4 ---- .github/workflows/functional-blockstorage.yaml | 4 ---- .github/workflows/functional-compute.yaml | 4 ---- .github/workflows/functional-containerinfra.yaml | 7 ------- .github/workflows/functional-dns.yaml | 4 ---- .github/workflows/functional-fwaas_v2.yaml | 4 ---- .github/workflows/functional-identity.yaml | 4 ---- .github/workflows/functional-image.yaml | 4 ---- .github/workflows/functional-keymanager.yaml | 4 ---- .github/workflows/functional-loadbalancer.yaml | 4 ---- .github/workflows/functional-messaging.yaml | 4 ---- .github/workflows/functional-networking.yaml | 4 ---- .github/workflows/functional-objectstorage.yaml | 4 ---- .github/workflows/functional-orchestration.yaml | 4 ---- .github/workflows/functional-placement.yaml | 4 ---- .github/workflows/functional-sharedfilesystems.yaml | 4 ---- 17 files changed, 72 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 2b9c24459e..ac2d37d6f2 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -34,11 +34,6 @@ jobs: ubuntu_version: "22.04" use_system_scope: false additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - use_system_scope: false - additional_services: "" 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 ec52a6a971..392ea1f06f 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -30,10 +30,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 a98ba4281b..c0b58484a7 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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-compute.yaml b/.github/workflows/functional-compute.yaml index 158b1c4301..0ad2dfa8d6 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 45c1c95f53..a2ed183682 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -39,13 +39,6 @@ jobs: enable_plugin magnum https://github.com/openstack/magnum stable/2023.2 MAGNUMCLIENT_BRANCH=stable/2023.2 additional_services: "" - - 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 - additional_services: "" 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 28392b4577..6b52023985 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -28,10 +28,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 9b30f72220..d308ade3ed 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with enabled FWaaS_v2 and run networking acceptance tests steps: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index badb239f9b..7a9d4ffaeb 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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-image.yaml b/.github/workflows/functional-image.yaml index e9192b1a7c..c6b956b882 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Glance and run image acceptance tests steps: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 83245c0fe5..595a096697 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 1069b530ca..11d67aef31 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 711824957b..87c5b93ded 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 0a9ff0b737..67ab119605 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 76edde3d21..c71d17f372 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 57344aa9ec..71213eea50 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 566eba1642..15982c5318 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" 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 59f442b341..d2702759a5 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -27,10 +27,6 @@ jobs: openstack_version: "stable/2023.2" ubuntu_version: "22.04" additional_services: "" - - name: "antelope" - openstack_version: "stable/2023.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests steps: From 37efb25177ce9a88c9fa3d298eb615e116fadcb6 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 29 May 2024 14:46:10 +0100 Subject: [PATCH 1977/2296] make: Add 'format' target Signed-off-by: Stephen Finucane --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 128beec005..4298ae0e2a 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,10 @@ lint: golangci/golangci-lint:$(GOLANGCI_LINT_VERSION) golangci-lint run .PHONY: lint +format: + gofmt -w -s $(shell pwd) +.PHONY: format + unit: go test ./... .PHONY: unit From 2e647143a83375272a543f4300562710da444409 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Nov 2024 13:49:00 +0000 Subject: [PATCH 1978/2296] script: Remove unused bootstrap script This is not referenced or documented anywhere and it has not been touched in years. Remove it. Signed-off-by: Stephen Finucane --- script/bootstrap | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100755 script/bootstrap diff --git a/script/bootstrap b/script/bootstrap deleted file mode 100755 index 78a195dcf7..0000000000 --- a/script/bootstrap +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# -# This script helps new contributors set up their local workstation for -# gophercloud development and contributions. - -# Create the environment -export GOPATH=$HOME/go/gophercloud -mkdir -p $GOPATH - -# Download gophercloud into that environment -go get github.com/gophercloud/gophercloud -cd $GOPATH/src/github.com/gophercloud/gophercloud -git checkout master - -# Write out the env.sh convenience file. -cd $GOPATH -cat <env.sh -#!/bin/bash -export GOPATH=$(pwd) -export GOPHERCLOUD=$GOPATH/src/github.com/gophercloud/gophercloud -EOF -chmod a+x env.sh - -# Make changes immediately available as a convenience. -. ./env.sh From baa5df96941191c7875826f577d9676579bec9e7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Mar 2024 13:31:11 +0000 Subject: [PATCH 1979/2296] trivial: Consistent file extensions Signed-off-by: Stephen Finucane --- .github/{dependabot.yml => dependabot.yaml} | 0 .github/workflows/{codeql-analysis.yml => codeql-analysis.yaml} | 0 .github/workflows/{greetings.yml => greetings.yaml} | 0 .github/workflows/{unit.yml => unit.yaml} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename .github/{dependabot.yml => dependabot.yaml} (100%) rename .github/workflows/{codeql-analysis.yml => codeql-analysis.yaml} (100%) rename .github/workflows/{greetings.yml => greetings.yaml} (100%) rename .github/workflows/{unit.yml => unit.yaml} (100%) diff --git a/.github/dependabot.yml b/.github/dependabot.yaml similarity index 100% rename from .github/dependabot.yml rename to .github/dependabot.yaml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yaml similarity index 100% rename from .github/workflows/codeql-analysis.yml rename to .github/workflows/codeql-analysis.yaml diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yaml similarity index 100% rename from .github/workflows/greetings.yml rename to .github/workflows/greetings.yaml diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yaml similarity index 100% rename from .github/workflows/unit.yml rename to .github/workflows/unit.yaml From 65463f13d5b57aad057642b9bb42d3b62291afef Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 21 Mar 2024 12:23:28 +0000 Subject: [PATCH 1980/2296] script: Address shellcheck warnings Signed-off-by: Stephen Finucane --- script/acceptancetest | 9 +++++---- script/coverage | 3 ++- script/test | 1 + script/unittest | 1 + script/vet | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/script/acceptancetest b/script/acceptancetest index 234f5b0bb6..7e223bae5e 100755 --- a/script/acceptancetest +++ b/script/acceptancetest @@ -7,19 +7,20 @@ # flags and things crash and burn *spectacularly* 🔥🔥🔥 set -xo pipefail -source $(dirname $0)/stackenv +# shellcheck disable=SC1091 +source "$(dirname "$0")/stackenv" # ...but we can do it after the fact set -eu timeout="60m" -failed= LOG_DIR=${LOG_DIR:-} 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} +mkdir -p "${LOG_DIR}" -go test -v -timeout $timeout -tags "fixtures acceptance" ${PACKAGE:-./internal/acceptance/openstack/...} $@ |& tee -a ${LOG_DIR}/acceptance_tests.log +# shellcheck disable=SC2068 +go test -v -timeout $timeout -tags "fixtures acceptance" "${PACKAGE:-./internal/acceptance/openstack/...}" $@ |& tee -a "${LOG_DIR}/acceptance_tests.log" diff --git a/script/coverage b/script/coverage index cd4dec06e5..ec5b2776b8 100755 --- a/script/coverage +++ b/script/coverage @@ -19,5 +19,6 @@ for path in $(find . -path '*/testing' -prune -o -path '*/internal' -prune -o -n n=$((n+1)) done -gocovmerge `ls *.coverprofile` > cover.out +# shellcheck disable=SC2046 +gocovmerge $(ls -- *.coverprofile) > cover.out rm ./*.coverprofile diff --git a/script/test b/script/test index 77f6af5017..e71e7514bb 100755 --- a/script/test +++ b/script/test @@ -4,4 +4,5 @@ set -euxo pipefail +# shellcheck disable=SC2068 go test -v -tags 'acceptance fixtures' ./... $@ diff --git a/script/unittest b/script/unittest index 2763e0efd9..1f1b1864f4 100755 --- a/script/unittest +++ b/script/unittest @@ -6,4 +6,5 @@ set -euxo pipefail # Do extra rounds of testing to help identify reauth concurrency issues. # All other packages are tested in the `coverage` tests. +# shellcheck disable=SC2068 go test -v -race -count=5 ./testing $@ diff --git a/script/vet b/script/vet index 4f3db40a28..ecb74ef37d 100755 --- a/script/vet +++ b/script/vet @@ -4,5 +4,5 @@ set -euxo pipefail -GOFLAGS="-tags=acceptance" +export GOFLAGS="-tags=acceptance" go vet ./... From 67c72f8db8b971305971fc9d1ec808fffc094011 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 30 May 2024 23:51:52 +0100 Subject: [PATCH 1981/2296] tests: Consist use of th alias testhelper is too long give how often these things are used. Signed-off-by: Stephen Finucane --- .../v1/nodes/testing/results_test.go | 50 +++---- .../schedulerstats/testing/fixtures_test.go | 8 +- .../schedulerstats/testing/requests_test.go | 14 +- .../v2/services/testing/requests_test.go | 14 +- .../schedulerstats/testing/fixtures_test.go | 8 +- .../schedulerstats/testing/requests_test.go | 14 +- .../v3/services/testing/requests_test.go | 14 +- .../v2/hypervisors/testing/fixtures_test.go | 52 +++---- .../v2/hypervisors/testing/requests_test.go | 98 ++++++------- .../v2/services/testing/requests_test.go | 42 +++--- .../v3/ec2tokens/testing/requests_test.go | 36 ++--- .../v3/oauth1/testing/fixtures_test.go | 136 +++++++++--------- .../identity/v3/tokens/testing/fixtures.go | 6 +- .../v3/tokens/testing/requests_test.go | 118 +++++++-------- .../v3/tokens/testing/results_test.go | 26 ++-- .../v3/trusts/testing/fixtures_test.go | 68 ++++----- .../schedulerstats/testing/fixtures_test.go | 14 +- .../schedulerstats/testing/requests_test.go | 30 ++-- .../v2/services/testing/requests_test.go | 12 +- .../utils/testing/choose_version_test.go | 66 ++++----- pagination/testing/linked_test.go | 26 ++-- pagination/testing/marker_test.go | 24 ++-- pagination/testing/pagination_test.go | 4 +- pagination/testing/single_test.go | 26 ++-- testhelper/client/fake.go | 4 +- 25 files changed, 455 insertions(+), 455 deletions(-) diff --git a/openstack/baremetal/v1/nodes/testing/results_test.go b/openstack/baremetal/v1/nodes/testing/results_test.go index 7c683ebecb..acdb59e774 100644 --- a/openstack/baremetal/v1/nodes/testing/results_test.go +++ b/openstack/baremetal/v1/nodes/testing/results_test.go @@ -8,68 +8,68 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/baremetal/v1/nodes" "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection" insptest "github.com/gophercloud/gophercloud/v2/openstack/baremetalintrospection/v1/introspection/testing" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestStandardPluginData(t *testing.T) { var pluginData nodes.PluginData err := pluginData.RawMessage.UnmarshalJSON([]byte(invtest.StandardPluginDataSample)) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) parsedData, err := pluginData.AsStandardData() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, invtest.StandardPluginData, parsedData) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, invtest.StandardPluginData, parsedData) irData, inspData, err := pluginData.GuessFormat() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, invtest.StandardPluginData, *irData) - testhelper.CheckEquals(t, (*introspection.Data)(nil), inspData) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, invtest.StandardPluginData, *irData) + th.CheckEquals(t, (*introspection.Data)(nil), inspData) } func TestInspectorPluginData(t *testing.T) { var pluginData nodes.PluginData err := pluginData.RawMessage.UnmarshalJSON([]byte(insptest.IntrospectionDataJSONSample)) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) parsedData, err := pluginData.AsInspectorData() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, insptest.IntrospectionDataRes, parsedData) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, insptest.IntrospectionDataRes, parsedData) irData, inspData, err := pluginData.GuessFormat() - testhelper.AssertNoErr(t, err) - testhelper.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) - testhelper.CheckDeepEquals(t, insptest.IntrospectionDataRes, *inspData) + th.AssertNoErr(t, err) + th.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) + th.CheckDeepEquals(t, insptest.IntrospectionDataRes, *inspData) } func TestGuessFormatUnknownDefaultsToIronic(t *testing.T) { var pluginData nodes.PluginData err := pluginData.RawMessage.UnmarshalJSON([]byte("{}")) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) irData, inspData, err := pluginData.GuessFormat() - testhelper.CheckDeepEquals(t, inventory.StandardPluginData{}, *irData) - testhelper.CheckEquals(t, (*introspection.Data)(nil), inspData) - testhelper.AssertNoErr(t, err) + th.CheckDeepEquals(t, inventory.StandardPluginData{}, *irData) + th.CheckEquals(t, (*introspection.Data)(nil), inspData) + th.AssertNoErr(t, err) } func TestGuessFormatErrors(t *testing.T) { var pluginData nodes.PluginData err := pluginData.RawMessage.UnmarshalJSON([]byte("\"banana\"")) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) irData, inspData, err := pluginData.GuessFormat() - testhelper.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) - testhelper.CheckEquals(t, (*introspection.Data)(nil), inspData) - testhelper.AssertErr(t, err) + th.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) + th.CheckEquals(t, (*introspection.Data)(nil), inspData) + th.AssertErr(t, err) failsInspectorConversion := `{ "interfaces": "banana" }` err = pluginData.RawMessage.UnmarshalJSON([]byte(failsInspectorConversion)) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) irData, inspData, err = pluginData.GuessFormat() - testhelper.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) - testhelper.CheckEquals(t, (*introspection.Data)(nil), inspData) - testhelper.AssertErr(t, err) + th.CheckEquals(t, (*inventory.StandardPluginData)(nil), irData) + th.CheckEquals(t, (*introspection.Data)(nil), inspData) + th.AssertErr(t, err) } diff --git a/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go index 6f0317ef60..3a7fee8160 100644 --- a/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/schedulerstats" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -94,9 +94,9 @@ var ( ) func HandleStoragePoolsListSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/scheduler-stats/get_pools", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/scheduler-stats/get_pools", 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") diff --git a/openstack/blockstorage/v2/schedulerstats/testing/requests_test.go b/openstack/blockstorage/v2/schedulerstats/testing/requests_test.go index 709ddeac58..7a6cb8b450 100644 --- a/openstack/blockstorage/v2/schedulerstats/testing/requests_test.go +++ b/openstack/blockstorage/v2/schedulerstats/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/schedulerstats" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListStoragePoolsDetail(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleStoragePoolsListSuccessfully(t) pages := 0 @@ -20,18 +20,18 @@ func TestListStoragePoolsDetail(t *testing.T) { pages++ actual, err := schedulerstats.ExtractStoragePools(page) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if len(actual) != 2 { t.Fatalf("Expected 2 backends, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, StoragePoolFake1, actual[0]) - testhelper.CheckDeepEquals(t, StoragePoolFake2, actual[1]) + th.CheckDeepEquals(t, StoragePoolFake1, actual[0]) + th.CheckDeepEquals(t, StoragePoolFake2, actual[1]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) diff --git a/openstack/blockstorage/v2/services/testing/requests_test.go b/openstack/blockstorage/v2/services/testing/requests_test.go index 8ce1266d7e..d1277a99bf 100644 --- a/openstack/blockstorage/v2/services/testing/requests_test.go +++ b/openstack/blockstorage/v2/services/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/services" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListServices(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleListSuccessfully(t) pages := 0 @@ -27,14 +27,14 @@ func TestListServices(t *testing.T) { if len(actual) != 3 { t.Fatalf("Expected 3 services, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) - testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) - testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) + th.CheckDeepEquals(t, FirstFakeService, actual[0]) + th.CheckDeepEquals(t, SecondFakeService, actual[1]) + th.CheckDeepEquals(t, ThirdFakeService, actual[2]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) diff --git a/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go index df0ccaa7f5..0a1286e862 100644 --- a/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/schedulerstats" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -94,9 +94,9 @@ var ( ) func HandleStoragePoolsListSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/scheduler-stats/get_pools", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/scheduler-stats/get_pools", 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") diff --git a/openstack/blockstorage/v3/schedulerstats/testing/requests_test.go b/openstack/blockstorage/v3/schedulerstats/testing/requests_test.go index b5e3f31ef6..c125763276 100644 --- a/openstack/blockstorage/v3/schedulerstats/testing/requests_test.go +++ b/openstack/blockstorage/v3/schedulerstats/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/schedulerstats" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListStoragePoolsDetail(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleStoragePoolsListSuccessfully(t) pages := 0 @@ -20,18 +20,18 @@ func TestListStoragePoolsDetail(t *testing.T) { pages++ actual, err := schedulerstats.ExtractStoragePools(page) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if len(actual) != 2 { t.Fatalf("Expected 2 backends, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, StoragePoolFake1, actual[0]) - testhelper.CheckDeepEquals(t, StoragePoolFake2, actual[1]) + th.CheckDeepEquals(t, StoragePoolFake1, actual[0]) + th.CheckDeepEquals(t, StoragePoolFake2, actual[1]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) diff --git a/openstack/blockstorage/v3/services/testing/requests_test.go b/openstack/blockstorage/v3/services/testing/requests_test.go index 1fd5bcdd1e..4e291da96b 100644 --- a/openstack/blockstorage/v3/services/testing/requests_test.go +++ b/openstack/blockstorage/v3/services/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/services" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListServices(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleListSuccessfully(t) pages := 0 @@ -27,14 +27,14 @@ func TestListServices(t *testing.T) { if len(actual) != 3 { t.Fatalf("Expected 3 services, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) - testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) - testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) + th.CheckDeepEquals(t, FirstFakeService, actual[0]) + th.CheckDeepEquals(t, SecondFakeService, actual[1]) + th.CheckDeepEquals(t, ThirdFakeService, actual[2]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) diff --git a/openstack/compute/v2/hypervisors/testing/fixtures_test.go b/openstack/compute/v2/hypervisors/testing/fixtures_test.go index a969f1f825..6e4b2e4aff 100644 --- a/openstack/compute/v2/hypervisors/testing/fixtures_test.go +++ b/openstack/compute/v2/hypervisors/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/hypervisors" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -600,9 +600,9 @@ var ( ) func HandleHypervisorsStatisticsSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/os-hypervisors/statistics", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/os-hypervisors/statistics", 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, HypervisorsStatisticsBody) @@ -610,9 +610,9 @@ func HandleHypervisorsStatisticsSuccessfully(t *testing.T) { } func HandleHypervisorListPre253Successfully(t *testing.T) { - testhelper.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/os-hypervisors/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") fmt.Fprintf(w, HypervisorListBodyPre253) @@ -620,9 +620,9 @@ func HandleHypervisorListPre253Successfully(t *testing.T) { } func HandleHypervisorListSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/os-hypervisors/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") fmt.Fprintf(w, HypervisorListBody) @@ -630,10 +630,10 @@ func HandleHypervisorListSuccessfully(t *testing.T) { } func HandleHypervisorListWithParametersSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) - testhelper.TestFormValues(t, r, map[string]string{ + th.Mux.HandleFunc("/os-hypervisors/detail", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestFormValues(t, r, map[string]string{ "with_servers": "true", }) @@ -643,9 +643,9 @@ func HandleHypervisorListWithParametersSuccessfully(t *testing.T) { } func HandleHypervisorGetSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, 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, HypervisorGetBody) @@ -653,9 +653,9 @@ func HandleHypervisorGetSuccessfully(t *testing.T) { } func HandleHypervisorGetEmptyCPUInfoSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, 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, HypervisorGetEmptyCPUInfoBody) @@ -663,9 +663,9 @@ func HandleHypervisorGetEmptyCPUInfoSuccessfully(t *testing.T) { } func HandleHypervisorAfterV287ResponseSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, 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, HypervisorAfterV287ResponseBody) @@ -673,9 +673,9 @@ func HandleHypervisorAfterV287ResponseSuccessfully(t *testing.T) { } func HandleHypervisorUptimeSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID+"/uptime", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID+"/uptime", 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, HypervisorUptimeBody) diff --git a/openstack/compute/v2/hypervisors/testing/requests_test.go b/openstack/compute/v2/hypervisors/testing/requests_test.go index 493e8138d1..0303f7f1d0 100644 --- a/openstack/compute/v2/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/hypervisors/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/hypervisors" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListHypervisorsPre253(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorListPre253Successfully(t) pages := 0 @@ -28,13 +28,13 @@ func TestListHypervisorsPre253(t *testing.T) { if len(actual) != 2 { t.Fatalf("Expected 2 hypervisors, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[0]) - testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[1]) + th.CheckDeepEquals(t, HypervisorFakePre253, actual[0]) + th.CheckDeepEquals(t, HypervisorFakePre253, actual[1]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) @@ -42,21 +42,21 @@ func TestListHypervisorsPre253(t *testing.T) { } func TestListAllHypervisorsPre253(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorListPre253Successfully(t) allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{}).AllPages(context.TODO()) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[0]) - testhelper.CheckDeepEquals(t, HypervisorFakePre253, actual[1]) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, HypervisorFakePre253, actual[0]) + th.CheckDeepEquals(t, HypervisorFakePre253, actual[1]) } func TestListHypervisors(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorListSuccessfully(t) pages := 0 @@ -72,13 +72,13 @@ func TestListHypervisors(t *testing.T) { if len(actual) != 2 { t.Fatalf("Expected 2 hypervisors, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, HypervisorFake, actual[0]) - testhelper.CheckDeepEquals(t, HypervisorFake, actual[1]) + th.CheckDeepEquals(t, HypervisorFake, actual[0]) + th.CheckDeepEquals(t, HypervisorFake, actual[1]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) @@ -86,88 +86,88 @@ func TestListHypervisors(t *testing.T) { } func TestListAllHypervisors(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorListSuccessfully(t) allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{}).AllPages(context.TODO()) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, HypervisorFake, actual[0]) - testhelper.CheckDeepEquals(t, HypervisorFake, actual[1]) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, HypervisorFake, actual[0]) + th.CheckDeepEquals(t, HypervisorFake, actual[1]) } func TestListAllHypervisorsWithParameters(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorListWithParametersSuccessfully(t) with_servers := true allPages, err := hypervisors.List(client.ServiceClient(), hypervisors.ListOpts{WithServers: &with_servers}).AllPages(context.TODO()) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) actual, err := hypervisors.ExtractHypervisors(allPages) - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, HypervisorFakeWithParameters, actual[0]) - testhelper.CheckDeepEquals(t, HypervisorFakeWithParameters, actual[1]) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, HypervisorFakeWithParameters, actual[0]) + th.CheckDeepEquals(t, HypervisorFakeWithParameters, actual[1]) } func TestHypervisorsStatistics(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorsStatisticsSuccessfully(t) expected := HypervisorsStatisticsExpected actual, err := hypervisors.GetStatistics(context.TODO(), client.ServiceClient()).Extract() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expected, actual) } func TestGetHypervisor(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorGetSuccessfully(t) expected := HypervisorFake actual, err := hypervisors.Get(context.TODO(), client.ServiceClient(), expected.ID).Extract() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expected, actual) } func TestGetHypervisorEmptyCPUInfo(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorGetEmptyCPUInfoSuccessfully(t) expected := HypervisorEmptyCPUInfo actual, err := hypervisors.Get(context.TODO(), client.ServiceClient(), expected.ID).Extract() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expected, actual) } func TestGetHypervisorAfterV287Response(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorAfterV287ResponseSuccessfully(t) expected := HypervisorAfterV287Response actual, err := hypervisors.Get(context.TODO(), client.ServiceClient(), expected.ID).Extract() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expected, actual) } func TestHypervisorsUptime(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleHypervisorUptimeSuccessfully(t) expected := HypervisorUptimeExpected actual, err := hypervisors.GetUptime(context.TODO(), client.ServiceClient(), HypervisorFake.ID).Extract() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expected, actual) } diff --git a/openstack/compute/v2/services/testing/requests_test.go b/openstack/compute/v2/services/testing/requests_test.go index c201c9f4d6..df7a9e9a17 100644 --- a/openstack/compute/v2/services/testing/requests_test.go +++ b/openstack/compute/v2/services/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/services" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListServicesPre253(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleListPre253Successfully(t) pages := 0 @@ -27,15 +27,15 @@ func TestListServicesPre253(t *testing.T) { if len(actual) != 4 { t.Fatalf("Expected 4 services, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, FirstFakeServicePre253, actual[0]) - testhelper.CheckDeepEquals(t, SecondFakeServicePre253, actual[1]) - testhelper.CheckDeepEquals(t, ThirdFakeServicePre253, actual[2]) - testhelper.CheckDeepEquals(t, FourthFakeServicePre253, actual[3]) + th.CheckDeepEquals(t, FirstFakeServicePre253, actual[0]) + th.CheckDeepEquals(t, SecondFakeServicePre253, actual[1]) + th.CheckDeepEquals(t, ThirdFakeServicePre253, actual[2]) + th.CheckDeepEquals(t, FourthFakeServicePre253, actual[3]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) @@ -43,8 +43,8 @@ func TestListServicesPre253(t *testing.T) { } func TestListServices(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleListSuccessfully(t) pages := 0 @@ -63,15 +63,15 @@ func TestListServices(t *testing.T) { if len(actual) != 4 { t.Fatalf("Expected 4 services, got %d", len(actual)) } - testhelper.CheckDeepEquals(t, FirstFakeService, actual[0]) - testhelper.CheckDeepEquals(t, SecondFakeService, actual[1]) - testhelper.CheckDeepEquals(t, ThirdFakeService, actual[2]) - testhelper.CheckDeepEquals(t, FourthFakeService, actual[3]) + th.CheckDeepEquals(t, FirstFakeService, actual[0]) + th.CheckDeepEquals(t, SecondFakeService, actual[1]) + th.CheckDeepEquals(t, ThirdFakeService, actual[2]) + th.CheckDeepEquals(t, FourthFakeService, actual[3]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) @@ -79,8 +79,8 @@ func TestListServices(t *testing.T) { } func TestUpdateService(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleUpdateSuccessfully(t) client := client.ServiceClient() @@ -89,16 +89,16 @@ func TestUpdateService(t *testing.T) { t.Fatalf("Unexpected Update error: %v", err) } - testhelper.CheckDeepEquals(t, FakeServiceUpdateBody, *actual) + th.CheckDeepEquals(t, FakeServiceUpdateBody, *actual) } func TestDeleteService(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleDeleteSuccessfully(t) client := client.ServiceClient() res := services.Delete(context.TODO(), client, "fake-service-id") - testhelper.AssertNoErr(t, res.Err) + th.AssertNoErr(t, res.Err) } diff --git a/openstack/identity/v3/ec2tokens/testing/requests_test.go b/openstack/identity/v3/ec2tokens/testing/requests_test.go index 188329daab..13239e2d91 100644 --- a/openstack/identity/v3/ec2tokens/testing/requests_test.go +++ b/openstack/identity/v3/ec2tokens/testing/requests_test.go @@ -12,24 +12,24 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/ec2tokens" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" tokens_testing "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/testing" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. func authTokenPost(t *testing.T, options ec2tokens.AuthOptions, requestJSON string) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } - testhelper.Mux.HandleFunc("/ec2tokens", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "Content-Type", "application/json") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestJSONRequest(t, r, requestJSON) + th.Mux.HandleFunc("/ec2tokens", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, requestJSON) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, tokens_testing.TokenOutput) @@ -40,8 +40,8 @@ func authTokenPost(t *testing.T, options ec2tokens.AuthOptions, requestJSON stri } actual, err := ec2tokens.Create(context.TODO(), &client, &options).Extract() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) } func TestCreateV2(t *testing.T) { @@ -224,7 +224,7 @@ func TestEC2CredentialsBuildCanonicalQueryStringV2(t *testing.T) { "Value": "bar", } expected := "Action=foo&Value=bar" - testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalQueryStringV2(params)) + th.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalQueryStringV2(params)) } func TestEC2CredentialsBuildStringToSignV2(t *testing.T) { @@ -238,7 +238,7 @@ func TestEC2CredentialsBuildStringToSignV2(t *testing.T) { }, } expected := []byte("GET\nlocalhost\n/\nAction=foo&Value=bar") - testhelper.CheckDeepEquals(t, expected, ec2tokens.EC2CredentialsBuildStringToSignV2(opts)) + th.CheckDeepEquals(t, expected, ec2tokens.EC2CredentialsBuildStringToSignV2(opts)) } func TestEC2CredentialsBuildCanonicalQueryStringV4(t *testing.T) { @@ -247,8 +247,8 @@ func TestEC2CredentialsBuildCanonicalQueryStringV4(t *testing.T) { "Value": "bar", } expected := "Action=foo&Value=bar" - testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalQueryStringV4("foo", params)) - testhelper.CheckEquals(t, "", ec2tokens.EC2CredentialsBuildCanonicalQueryStringV4("POST", params)) + th.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalQueryStringV4("foo", params)) + th.CheckEquals(t, "", ec2tokens.EC2CredentialsBuildCanonicalQueryStringV4("POST", params)) } func TestEC2CredentialsBuildCanonicalHeadersV4(t *testing.T) { @@ -258,12 +258,12 @@ func TestEC2CredentialsBuildCanonicalHeadersV4(t *testing.T) { } signedHeaders := "foo;baz" expected := "foo:bar\nbaz:qux\n" - testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalHeadersV4(headers, signedHeaders)) + th.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildCanonicalHeadersV4(headers, signedHeaders)) } func TestEC2CredentialsBuildSignatureKeyV4(t *testing.T) { expected := "246626bd815b0a0cae4bedc3f4e124ca25e208cd75fd812d836aeae184de038a" - testhelper.CheckEquals(t, expected, hex.EncodeToString((ec2tokens.EC2CredentialsBuildSignatureKeyV4("foo", "bar", "baz", time.Time{})))) + th.CheckEquals(t, expected, hex.EncodeToString((ec2tokens.EC2CredentialsBuildSignatureKeyV4("foo", "bar", "baz", time.Time{})))) } func TestEC2CredentialsBuildSignatureV4(t *testing.T) { @@ -284,5 +284,5 @@ func TestEC2CredentialsBuildSignatureV4(t *testing.T) { stringToSign := ec2tokens.EC2CredentialsBuildStringToSignV4(opts, "host", "foo", date) key := ec2tokens.EC2CredentialsBuildSignatureKeyV4("", "", "", date) - testhelper.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildSignatureV4(key, stringToSign)) + th.CheckEquals(t, expected, ec2tokens.EC2CredentialsBuildSignatureV4(key, stringToSign)) } diff --git a/openstack/identity/v3/oauth1/testing/fixtures_test.go b/openstack/identity/v3/oauth1/testing/fixtures_test.go index 8a2bfe5cdb..3e8d144047 100644 --- a/openstack/identity/v3/oauth1/testing/fixtures_test.go +++ b/openstack/identity/v3/oauth1/testing/fixtures_test.go @@ -8,7 +8,7 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/oauth1" tokens "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens/testing" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -221,39 +221,39 @@ var ExpectedUserAccessTokenRolesSlice = []oauth1.AccessTokenRole{UserAccessToken // HandleCreateConsumer creates an HTTP handler at `/OS-OAUTH1/consumers` on the // test handler mux that tests consumer creation. func HandleCreateConsumer(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "Content-Type", "application/json") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestJSONRequest(t, r, CreateConsumerRequest) + th.Mux.HandleFunc("/OS-OAUTH1/consumers", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, CreateConsumerRequest) w.WriteHeader(http.StatusCreated) _, err := fmt.Fprintf(w, CreateConsumerResponse) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) }) } // HandleUpdateConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the // test handler mux that tests consumer update. func HandleUpdateConsumer(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "PATCH") - testhelper.TestHeader(t, r, "Content-Type", "application/json") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestJSONRequest(t, r, UpdateConsumerRequest) + th.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PATCH") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, UpdateConsumerRequest) w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, UpdateConsumerResponse) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) }) } // HandleDeleteConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the // test handler mux that tests consumer deletion. func HandleDeleteConsumer(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "DELETE") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", 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) }) @@ -262,10 +262,10 @@ func HandleDeleteConsumer(t *testing.T) { // HandleGetConsumer creates an HTTP handler at `/OS-OAUTH1/consumers/7fea2d` on the // test handler mux that responds with a single consumer. func HandleGetConsumer(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-OAUTH1/consumers/7fea2d", 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) @@ -299,10 +299,10 @@ var ExpectedConsumersSlice = []oauth1.Consumer{FirstConsumer, SecondConsumer} // HandleListConsumers creates an HTTP handler at `/OS-OAUTH1/consumers` on the // test handler mux that responds with a list of two consumers. func HandleListConsumers(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-OAUTH1/consumers", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-OAUTH1/consumers", 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) @@ -319,13 +319,13 @@ var Token = oauth1.Token{ // HandleRequestToken creates an HTTP handler at `/OS-OAUTH1/request_token` on the // test handler mux that responds with a OAuth1 unauthorized token. func HandleRequestToken(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-OAUTH1/request_token", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) - testhelper.TestHeader(t, r, "Authorization", `OAuth oauth_callback="oob", oauth_consumer_key="7fea2d", oauth_nonce="71416001758914252991586795052", oauth_signature_method="HMAC-SHA1", oauth_timestamp="0", oauth_version="1.0", oauth_signature="jCSPVryCYF52Ks0VNNmBmeKSGuw%3D"`) - testhelper.TestHeader(t, r, "Requested-Project-Id", "1df927e8a466498f98788ed73d3c8ab4") - testhelper.TestBody(t, r, "") + th.Mux.HandleFunc("/OS-OAUTH1/request_token", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Authorization", `OAuth oauth_callback="oob", oauth_consumer_key="7fea2d", oauth_nonce="71416001758914252991586795052", oauth_signature_method="HMAC-SHA1", oauth_timestamp="0", oauth_version="1.0", oauth_signature="jCSPVryCYF52Ks0VNNmBmeKSGuw%3D"`) + th.TestHeader(t, r, "Requested-Project-Id", "1df927e8a466498f98788ed73d3c8ab4") + th.TestBody(t, r, "") w.Header().Set("Content-Type", oauth1.OAuth1TokenContentType) w.WriteHeader(http.StatusCreated) @@ -336,15 +336,15 @@ func HandleRequestToken(t *testing.T) { // HandleAuthorizeToken creates an HTTP handler at `/OS-OAUTH1/authorize/29971f` on the // test handler mux that tests unauthorized token authorization. func HandleAuthorizeToken(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-OAUTH1/authorize/29971f", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "PUT") - testhelper.TestHeader(t, r, "Content-Type", "application/json") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestJSONRequest(t, r, AuthorizeTokenRequest) + th.Mux.HandleFunc("/OS-OAUTH1/authorize/29971f", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, AuthorizeTokenRequest) w.WriteHeader(http.StatusOK) _, err := fmt.Fprintf(w, AuthorizeTokenResponse) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) }) } @@ -357,12 +357,12 @@ var AccessToken = oauth1.Token{ // HandleCreateAccessToken creates an HTTP handler at `/OS-OAUTH1/access_token` on the // test handler mux that responds with a OAuth1 access token. func HandleCreateAccessToken(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-OAUTH1/access_token", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) - testhelper.TestHeader(t, r, "Authorization", `OAuth oauth_consumer_key="7fea2d", oauth_nonce="66148873158553341551586804894", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1586804894", oauth_token="29971f", oauth_verifier="8171", oauth_version="1.0", oauth_signature="usQ89Y3IYG0IBE7%2Ft8aVsc8XgEk%3D"`) - testhelper.TestBody(t, r, "") + th.Mux.HandleFunc("/OS-OAUTH1/access_token", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Authorization", `OAuth oauth_consumer_key="7fea2d", oauth_nonce="66148873158553341551586804894", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1586804894", oauth_token="29971f", oauth_verifier="8171", oauth_version="1.0", oauth_signature="usQ89Y3IYG0IBE7%2Ft8aVsc8XgEk%3D"`) + th.TestBody(t, r, "") w.Header().Set("Content-Type", oauth1.OAuth1TokenContentType) w.WriteHeader(http.StatusCreated) @@ -373,10 +373,10 @@ func HandleCreateAccessToken(t *testing.T) { // HandleGetAccessToken creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a` on the // test handler mux that responds with a single access token. func HandleGetAccessToken(t *testing.T) { - testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a", 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) @@ -387,9 +387,9 @@ func HandleGetAccessToken(t *testing.T) { // HandleRevokeAccessToken creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a` on the // test handler mux that tests access token deletion. func HandleRevokeAccessToken(t *testing.T) { - testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "DELETE") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a", 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) }) @@ -398,10 +398,10 @@ func HandleRevokeAccessToken(t *testing.T) { // HandleListAccessTokens creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens` on the // test handler mux that responds with a slice of access tokens. func HandleListAccessTokens(t *testing.T) { - testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens", 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) @@ -412,10 +412,10 @@ func HandleListAccessTokens(t *testing.T) { // HandleListAccessTokenRoles creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles` on the // test handler mux that responds with a slice of access token roles. func HandleListAccessTokenRoles(t *testing.T) { - testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles", 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) @@ -426,10 +426,10 @@ func HandleListAccessTokenRoles(t *testing.T) { // HandleGetAccessTokenRole creates an HTTP handler at `/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles/5ad150` on the // test handler mux that responds with an access token role. func HandleGetAccessTokenRole(t *testing.T) { - testhelper.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles/5ad150", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/users/ce9e07/OS-OAUTH1/access_tokens/6be26a/roles/5ad150", 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) @@ -440,12 +440,12 @@ func HandleGetAccessTokenRole(t *testing.T) { // HandleAuthenticate creates an HTTP handler at `/auth/tokens` on the // test handler mux that responds with an OpenStack token. func HandleAuthenticate(t *testing.T) { - testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "Content-Type", "application/json") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "Authorization", `OAuth oauth_consumer_key="7fea2d", oauth_nonce="66148873158553341551586804894", oauth_signature_method="HMAC-SHA1", oauth_timestamp="0", oauth_token="accd36", oauth_version="1.0", oauth_signature="JgMHu4e7rXGlqz3A%2FLhHDMvtjp8%3D"`) - testhelper.TestJSONRequest(t, r, `{"auth": {"identity": {"oauth1": {}, "methods": ["oauth1"]}}}`) + th.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "Authorization", `OAuth oauth_consumer_key="7fea2d", oauth_nonce="66148873158553341551586804894", oauth_signature_method="HMAC-SHA1", oauth_timestamp="0", oauth_token="accd36", oauth_version="1.0", oauth_signature="JgMHu4e7rXGlqz3A%2FLhHDMvtjp8%3D"`) + th.TestJSONRequest(t, r, `{"auth": {"identity": {"oauth1": {}, "methods": ["oauth1"]}}}`) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) diff --git a/openstack/identity/v3/tokens/testing/fixtures.go b/openstack/identity/v3/tokens/testing/fixtures.go index 0b4ccafa26..00797867c9 100644 --- a/openstack/identity/v3/tokens/testing/fixtures.go +++ b/openstack/identity/v3/tokens/testing/fixtures.go @@ -8,7 +8,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) const testTokenID = "130f6c17-420e-4a0b-97b0-0c9cf2a05f30" @@ -310,7 +310,7 @@ func getGetResult(t *testing.T) tokens.GetResult { "X-Subject-Token": []string{testTokenID}, } err := json.Unmarshal([]byte(TokenOutput), &result.Body) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) return result } @@ -320,6 +320,6 @@ func getGetDomainResult(t *testing.T) tokens.GetResult { "X-Subject-Token": []string{testTokenID}, } err := json.Unmarshal([]byte(DomainToken), &result.Body) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) return result } diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index 9a9a59fc14..c0aafb9ad4 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -9,25 +9,25 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) // authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. func authTokenPost(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, requestJSON string) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } - testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "Content-Type", "application/json") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestJSONRequest(t, r, requestJSON) + th.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, requestJSON) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{ @@ -45,17 +45,17 @@ func authTokenPost(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope ExpiresAt: time.Date(2014, 10, 2, 13, 45, 0, 0, time.UTC), } actual, err := tokens.Create(context.TODO(), &client, &options).Extract() - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) } func authTokenPostErr(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope, includeToken bool, expectedErr error) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } if includeToken { client.TokenID = "abcdef123456" @@ -303,8 +303,8 @@ func TestCreateSystemScope(t *testing.T) { } func TestCreateUserIDPasswordTrustID(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() requestJSON := `{ "auth": { @@ -362,11 +362,11 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) { } } }` - testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "Content-Type", "application/json") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestJSONRequest(t, r, requestJSON) + th.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, requestJSON) w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, responseJSON) @@ -389,7 +389,7 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) { expectedToken := &tokens.Token{ ExpiresAt: time.Date(2024, 02, 28, 12, 10, 39, 0, time.UTC), } - testhelper.AssertDeepEquals(t, expectedToken, token) + th.AssertDeepEquals(t, expectedToken, token) trust, err := rsp.ExtractTrust() if err != nil { @@ -405,7 +405,7 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) { ID: "c88693b7c81c408e9084ac1e51082bfb", }, } - testhelper.AssertDeepEquals(t, expectedTrust, trust) + th.AssertDeepEquals(t, expectedTrust, trust) } func TestCreateApplicationCredentialIDAndSecret(t *testing.T) { @@ -513,15 +513,15 @@ func TestCreatePasswordTOTPProjectNameAndDomainNameScope(t *testing.T) { } func TestCreateExtractsTokenFromResponse(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } - testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("X-Subject-Token", "aaa111") w.WriteHeader(http.StatusCreated) @@ -652,22 +652,22 @@ func TestCreateFailureEmptyScope(t *testing.T) { */ func TestGetRequest(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{ TokenID: "12345abcdef", }, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } - testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeaderUnset(t, r, "Content-Type") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef") - testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345") + th.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeaderUnset(t, r, "Content-Type") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", "12345abcdef") + th.TestHeader(t, r, "X-Subject-Token", "abcdef12345") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, ` @@ -691,15 +691,15 @@ func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) go ProviderClient: &gophercloud.ProviderClient{ TokenID: "12345abcdef", }, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } - testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, expectedMethod) - testhelper.TestHeaderUnset(t, r, "Content-Type") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", "12345abcdef") - testhelper.TestHeader(t, r, "X-Subject-Token", "abcdef12345") + th.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, expectedMethod) + th.TestHeaderUnset(t, r, "Content-Type") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", "12345abcdef") + th.TestHeader(t, r, "X-Subject-Token", "abcdef12345") w.WriteHeader(status) }) @@ -708,8 +708,8 @@ func prepareAuthTokenHandler(t *testing.T, expectedMethod string, status int) go } func TestValidateRequestSuccessful(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusNoContent) ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345") @@ -723,8 +723,8 @@ func TestValidateRequestSuccessful(t *testing.T) { } func TestValidateRequestFailure(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusNotFound) ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345") @@ -738,8 +738,8 @@ func TestValidateRequestFailure(t *testing.T) { } func TestValidateRequestError(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := prepareAuthTokenHandler(t, "HEAD", http.StatusMethodNotAllowed) _, err := tokens.Validate(context.TODO(), &client, "abcdef12345") @@ -749,17 +749,17 @@ func TestValidateRequestError(t *testing.T) { } func TestRevokeRequestSuccessful(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := prepareAuthTokenHandler(t, "DELETE", http.StatusNoContent) res := tokens.Revoke(context.TODO(), &client, "abcdef12345") - testhelper.AssertNoErr(t, res.Err) + th.AssertNoErr(t, res.Err) } func TestRevokeRequestError(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := prepareAuthTokenHandler(t, "DELETE", http.StatusNotFound) res := tokens.Revoke(context.TODO(), &client, "abcdef12345") @@ -769,20 +769,20 @@ func TestRevokeRequestError(t *testing.T) { } func TestNoTokenInResponse(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() client := gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{}, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } - testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) fmt.Fprintf(w, `{}`) }) options := tokens.AuthOptions{UserID: "me", Password: "squirrel!"} _, err := tokens.Create(context.TODO(), &client, &options).Extract() - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) } diff --git a/openstack/identity/v3/tokens/testing/results_test.go b/openstack/identity/v3/tokens/testing/results_test.go index e142ac9c57..c45581ffde 100644 --- a/openstack/identity/v3/tokens/testing/results_test.go +++ b/openstack/identity/v3/tokens/testing/results_test.go @@ -3,59 +3,59 @@ package testing import ( "testing" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestExtractToken(t *testing.T) { result := getGetResult(t) token, err := result.ExtractToken() - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &ExpectedToken, token) + th.CheckDeepEquals(t, &ExpectedToken, token) } func TestExtractCatalog(t *testing.T) { result := getGetResult(t) catalog, err := result.ExtractServiceCatalog() - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &ExpectedServiceCatalog, catalog) + th.CheckDeepEquals(t, &ExpectedServiceCatalog, catalog) } func TestExtractUser(t *testing.T) { result := getGetResult(t) user, err := result.ExtractUser() - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &ExpectedUser, user) + th.CheckDeepEquals(t, &ExpectedUser, user) } func TestExtractRoles(t *testing.T) { result := getGetResult(t) roles, err := result.ExtractRoles() - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, ExpectedRoles, roles) + th.CheckDeepEquals(t, ExpectedRoles, roles) } func TestExtractProject(t *testing.T) { result := getGetResult(t) project, err := result.ExtractProject() - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &ExpectedProject, project) + th.CheckDeepEquals(t, &ExpectedProject, project) } func TestExtractDomain(t *testing.T) { result := getGetDomainResult(t) domain, err := result.ExtractDomain() - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, &ExpectedDomain, domain) + th.CheckDeepEquals(t, &ExpectedDomain, domain) } diff --git a/openstack/identity/v3/trusts/testing/fixtures_test.go b/openstack/identity/v3/trusts/testing/fixtures_test.go index 19ede76569..6cc9a8a7d1 100644 --- a/openstack/identity/v3/trusts/testing/fixtures_test.go +++ b/openstack/identity/v3/trusts/testing/fixtures_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/trusts" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -201,37 +201,37 @@ var ExpectedTrustRolesSlice = []trusts.Role{FirstRole, SecondRole} // HandleCreateTrust creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that tests trust creation. func HandleCreateTrust(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) - testhelper.TestJSONRequest(t, r, CreateRequest) + th.Mux.HandleFunc("/OS-TRUST/trusts", 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) _, err := fmt.Fprintf(w, CreateResponse) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) }) } // HandleCreateTrustNoExpire creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that tests trust creation. func HandleCreateTrustNoExpire(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "POST") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) - testhelper.TestJSONRequest(t, r, CreateRequestNoExpire) + th.Mux.HandleFunc("/OS-TRUST/trusts", 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, CreateRequestNoExpire) w.WriteHeader(http.StatusCreated) _, err := fmt.Fprintf(w, CreateResponseNoExpire) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) }) } // HandleDeleteUserSuccessfully creates an HTTP handler at `/users` on the // test handler mux that tests user deletion. func HandleDeleteTrust(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-TRUST/trusts/3422b7c113894f5d90665e1a79655e23", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "DELETE") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-TRUST/trusts/3422b7c113894f5d90665e1a79655e23", 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) }) @@ -240,10 +240,10 @@ func HandleDeleteTrust(t *testing.T) { // HandleGetTrustSuccessfully creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that responds with a single trusts. func HandleGetTrustSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-TRUST/trusts/987fe8", 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) @@ -310,10 +310,10 @@ var ExpectedTrustsSlice = []trusts.Trust{FirstTrust, SecondTrust} // HandleListTrustsSuccessfully creates an HTTP handler at `/OS-TRUST/trusts` on the // test handler mux that responds with a list of two trusts. func HandleListTrustsSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-TRUST/trusts", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-TRUST/trusts", 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) @@ -324,10 +324,10 @@ func HandleListTrustsSuccessfully(t *testing.T) { // HandleListTrustRolesSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles` on the // test handler mux that responds with a list trust roles. func HandleListTrustRolesSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles", 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) @@ -338,10 +338,10 @@ func HandleListTrustRolesSuccessfully(t *testing.T) { // HandleGetTrustRoleSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles/c1648e` on the // test handler mux that responds with a trust role details. func HandleGetTrustRoleSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles/c1648e", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "GET") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles/c1648e", 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) @@ -352,10 +352,10 @@ func HandleGetTrustRoleSuccessfully(t *testing.T) { // HandleCheckTrustRoleSuccessfully creates an HTTP handler at `/OS-TRUST/trusts/987fe8/roles/c1648e` on the // test handler mux that responds with a list trust roles. func HandleCheckTrustRoleSuccessfully(t *testing.T) { - testhelper.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles/c1648e", func(w http.ResponseWriter, r *http.Request) { - testhelper.TestMethod(t, r, "HEAD") - testhelper.TestHeader(t, r, "Accept", "application/json") - testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.Mux.HandleFunc("/OS-TRUST/trusts/987fe8/roles/c1648e", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "Accept", "application/json") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) }) diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go index acdd04d53f..88c4b31203 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/schedulerstats" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -265,9 +265,9 @@ var ( ) 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) + th.Mux.HandleFunc("/scheduler-stats/pools", 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") @@ -277,9 +277,9 @@ func HandlePoolsListSuccessfully(t *testing.T) { 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) + th.Mux.HandleFunc("/scheduler-stats/pools/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") diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go index 5c2ff36ad9..d6b826ed74 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/schedulerstats" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListPoolsDetail(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandlePoolsListSuccessfully(t) pages := 0 @@ -20,20 +20,20 @@ func TestListPoolsDetail(t *testing.T) { pages++ actual, err := schedulerstats.ExtractPools(page) - testhelper.AssertNoErr(t, err) + th.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]) + th.CheckDeepEquals(t, PoolFake1, actual[0]) + th.CheckDeepEquals(t, PoolFake2, actual[1]) + th.CheckDeepEquals(t, PoolFake3, actual[2]) + th.CheckDeepEquals(t, PoolFake4, actual[3]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) @@ -44,20 +44,20 @@ func TestListPoolsDetail(t *testing.T) { pages++ actual, err := schedulerstats.ExtractPools(page) - testhelper.AssertNoErr(t, err) + th.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]) + th.CheckDeepEquals(t, PoolDetailFake1, actual[0]) + th.CheckDeepEquals(t, PoolDetailFake2, actual[1]) + th.CheckDeepEquals(t, PoolDetailFake3, actual[2]) + th.CheckDeepEquals(t, PoolDetailFake4, actual[3]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) diff --git a/openstack/sharedfilesystems/v2/services/testing/requests_test.go b/openstack/sharedfilesystems/v2/services/testing/requests_test.go index 16607415b6..d4845ca09e 100644 --- a/openstack/sharedfilesystems/v2/services/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/services/testing/requests_test.go @@ -6,13 +6,13 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/services" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) func TestListServices(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() HandleListSuccessfully(t) pages := 0 @@ -27,13 +27,13 @@ func TestListServices(t *testing.T) { 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]) + th.CheckDeepEquals(t, FirstFakeService, actual[0]) + th.CheckDeepEquals(t, SecondFakeService, actual[1]) return true, nil }) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) if pages != 1 { t.Errorf("Expected 1 page, saw %d", pages) diff --git a/openstack/utils/testing/choose_version_test.go b/openstack/utils/testing/choose_version_test.go index 36d8ac37ec..c237e4e036 100644 --- a/openstack/utils/testing/choose_version_test.go +++ b/openstack/utils/testing/choose_version_test.go @@ -9,11 +9,11 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/utils" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func setupVersionHandler() { - testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "versions": { @@ -35,10 +35,10 @@ func setupVersionHandler() { ] } } - `, testhelper.Server.URL, testhelper.Server.URL) + `, th.Server.URL, th.Server.URL) }) // Compute v2.1 API - testhelper.Mux.HandleFunc("/compute/v2.1/", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/compute/v2.1/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "version": { @@ -66,10 +66,10 @@ func setupVersionHandler() { ] } } - `, testhelper.Server.URL) + `, th.Server.URL) }) // Compute v2 API - testhelper.Mux.HandleFunc("/compute/v2/", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/compute/v2/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "version": { @@ -97,10 +97,10 @@ func setupVersionHandler() { ] } } - `, testhelper.Server.URL) + `, th.Server.URL) }) // Ironic API - testhelper.Mux.HandleFunc("/ironic/v1/", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/ironic/v1/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "name": "OpenStack Ironic API", @@ -132,10 +132,10 @@ func setupVersionHandler() { } ] } - `, testhelper.Server.URL, testhelper.Server.URL) + `, th.Server.URL, th.Server.URL) }) // Ironic multi-version - testhelper.Mux.HandleFunc("/ironic/v1.2/", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/ironic/v1.2/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ` { "name": "OpenStack Ironic API", @@ -179,20 +179,20 @@ func setupVersionHandler() { } ] } - `, testhelper.Server.URL, testhelper.Server.URL, testhelper.Server.URL) + `, th.Server.URL, th.Server.URL, th.Server.URL) }) } func TestChooseVersion(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() setupVersionHandler() v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "blarg"} v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "hargl"} c := &gophercloud.ProviderClient{ - IdentityBase: testhelper.Endpoint(), + IdentityBase: th.Endpoint(), IdentityEndpoint: "", } v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3}) @@ -205,23 +205,23 @@ func TestChooseVersion(t *testing.T) { t.Errorf("Expected %#v to win, but %#v did instead", v3, v) } - expected := testhelper.Endpoint() + "v3.0/" + expected := th.Endpoint() + "v3.0/" if endpoint != expected { t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint) } } func TestChooseVersionOpinionatedLink(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() setupVersionHandler() v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "nope"} v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "northis"} c := &gophercloud.ProviderClient{ - IdentityBase: testhelper.Endpoint(), - IdentityEndpoint: testhelper.Endpoint() + "v2.0/", + IdentityBase: th.Endpoint(), + IdentityEndpoint: th.Endpoint() + "v2.0/", } v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3}) if err != nil { @@ -232,22 +232,22 @@ func TestChooseVersionOpinionatedLink(t *testing.T) { t.Errorf("Expected %#v to win, but %#v did instead", v2, v) } - expected := testhelper.Endpoint() + "v2.0/" + expected := th.Endpoint() + "v2.0/" if endpoint != expected { t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint) } } func TestChooseVersionFromSuffix(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() v2 := &utils.Version{ID: "v2.0", Priority: 2, Suffix: "/v2.0/"} v3 := &utils.Version{ID: "v3.0", Priority: 3, Suffix: "/v3.0/"} c := &gophercloud.ProviderClient{ - IdentityBase: testhelper.Endpoint(), - IdentityEndpoint: testhelper.Endpoint() + "v2.0/", + IdentityBase: th.Endpoint(), + IdentityEndpoint: th.Endpoint() + "v2.0/", } v, endpoint, err := utils.ChooseVersion(context.TODO(), c, []*utils.Version{v2, v3}) if err != nil { @@ -258,7 +258,7 @@ func TestChooseVersionFromSuffix(t *testing.T) { t.Errorf("Expected %#v to win, but %#v did instead", v2, v) } - expected := testhelper.Endpoint() + "v2.0/" + expected := th.Endpoint() + "v2.0/" if endpoint != expected { t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint) } @@ -272,33 +272,33 @@ type getSupportedServiceMicroversions struct { } func TestGetSupportedVersions(t *testing.T) { - testhelper.SetupHTTP() - defer testhelper.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() setupVersionHandler() tests := []getSupportedServiceMicroversions{ { // v2 does not support microversions and returns error - Endpoint: testhelper.Endpoint() + "compute/v2/", + Endpoint: th.Endpoint() + "compute/v2/", ExpectedMax: "", ExpectedMin: "", ExpectedErr: true, }, { - Endpoint: testhelper.Endpoint() + "compute/v2.1/", + Endpoint: th.Endpoint() + "compute/v2.1/", ExpectedMax: "2.90", ExpectedMin: "2.1", ExpectedErr: false, }, { - Endpoint: testhelper.Endpoint() + "ironic/v1/", + Endpoint: th.Endpoint() + "ironic/v1/", ExpectedMax: "1.87", ExpectedMin: "1.1", ExpectedErr: false, }, { // This endpoint returns multiple versions, which is not supported - Endpoint: testhelper.Endpoint() + "ironic/v1.2/", + Endpoint: th.Endpoint() + "ironic/v1.2/", ExpectedMax: "not-relevant", ExpectedMin: "not-relevant", ExpectedErr: true, @@ -307,8 +307,8 @@ func TestGetSupportedVersions(t *testing.T) { for _, test := range tests { c := &gophercloud.ProviderClient{ - IdentityBase: testhelper.Endpoint(), - IdentityEndpoint: testhelper.Endpoint() + "v2.0/", + IdentityBase: th.Endpoint(), + IdentityEndpoint: th.Endpoint() + "v2.0/", } client := &gophercloud.ServiceClient{ diff --git a/pagination/testing/linked_test.go b/pagination/testing/linked_test.go index 83673f19b8..25166ad861 100644 --- a/pagination/testing/linked_test.go +++ b/pagination/testing/linked_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // LinkedPager sample and test cases. @@ -31,19 +31,19 @@ func ExtractLinkedInts(r pagination.Page) ([]int, error) { } func createLinked() pagination.Pager { - testhelper.SetupHTTP() + th.SetupHTTP() - testhelper.Mux.HandleFunc("/page1", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/page1", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ "ints": [1, 2, 3], "links": { "next": "%s/page2" } }`, testhelper.Server.URL) + fmt.Fprintf(w, `{ "ints": [1, 2, 3], "links": { "next": "%s/page2" } }`, th.Server.URL) }) - testhelper.Mux.HandleFunc("/page2", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/page2", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ "ints": [4, 5, 6], "links": { "next": "%s/page3" } }`, testhelper.Server.URL) + fmt.Fprintf(w, `{ "ints": [4, 5, 6], "links": { "next": "%s/page3" } }`, th.Server.URL) }) - testhelper.Mux.HandleFunc("/page3", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/page3", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "ints": [7, 8, 9], "links": { "next": null } }`) }) @@ -54,12 +54,12 @@ func createLinked() pagination.Pager { return LinkedPageResult{pagination.LinkedPageBase{PageResult: r}} } - return pagination.NewPager(client, testhelper.Server.URL+"/page1", createPage) + return pagination.NewPager(client, th.Server.URL+"/page1", createPage) } func TestEnumerateLinked(t *testing.T) { pager := createLinked() - defer testhelper.TeardownHTTP() + defer th.TeardownHTTP() callCount := 0 err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { @@ -101,13 +101,13 @@ func TestEnumerateLinked(t *testing.T) { func TestAllPagesLinked(t *testing.T) { pager := createLinked() - defer testhelper.TeardownHTTP() + defer th.TeardownHTTP() page, err := pager.AllPages(context.TODO()) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) expected := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} actual, err := ExtractLinkedInts(page) - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) } diff --git a/pagination/testing/marker_test.go b/pagination/testing/marker_test.go index a1084ac12f..45c943e5b7 100644 --- a/pagination/testing/marker_test.go +++ b/pagination/testing/marker_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // MarkerPager sample and test cases. @@ -37,9 +37,9 @@ func (r MarkerPageResult) LastMarker() (string, error) { } func createMarkerPaged(t *testing.T) pagination.Pager { - testhelper.SetupHTTP() + th.SetupHTTP() - testhelper.Mux.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { t.Errorf("Failed to parse request form %v", err) } @@ -66,7 +66,7 @@ func createMarkerPaged(t *testing.T) pagination.Pager { return p } - return pagination.NewPager(client, testhelper.Server.URL+"/page", createPage) + return pagination.NewPager(client, th.Server.URL+"/page", createPage) } func ExtractMarkerStrings(page pagination.Page) ([]string, error) { @@ -83,7 +83,7 @@ func ExtractMarkerStrings(page pagination.Page) ([]string, error) { func TestEnumerateMarker(t *testing.T) { pager := createMarkerPaged(t) - defer testhelper.TeardownHTTP() + defer th.TeardownHTTP() callCount := 0 err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { @@ -107,24 +107,24 @@ func TestEnumerateMarker(t *testing.T) { return false, nil } - testhelper.CheckDeepEquals(t, expected, actual) + th.CheckDeepEquals(t, expected, actual) callCount++ return true, nil }) - testhelper.AssertNoErr(t, err) - testhelper.AssertEquals(t, callCount, 3) + th.AssertNoErr(t, err) + th.AssertEquals(t, callCount, 3) } func TestAllPagesMarker(t *testing.T) { pager := createMarkerPaged(t) - defer testhelper.TeardownHTTP() + defer th.TeardownHTTP() page, err := pager.AllPages(context.TODO()) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) expected := []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii"} actual, err := ExtractMarkerStrings(page) - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) } diff --git a/pagination/testing/pagination_test.go b/pagination/testing/pagination_test.go index b730676d4e..e10ef56847 100644 --- a/pagination/testing/pagination_test.go +++ b/pagination/testing/pagination_test.go @@ -2,12 +2,12 @@ package testing import ( "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func createClient() *gophercloud.ServiceClient { return &gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{TokenID: "abc123"}, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } } diff --git a/pagination/testing/single_test.go b/pagination/testing/single_test.go index 21daedff0a..6bcd930cb2 100644 --- a/pagination/testing/single_test.go +++ b/pagination/testing/single_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/pagination" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // SinglePage sample and test cases. @@ -33,10 +33,10 @@ func ExtractSingleInts(r pagination.Page) ([]int, error) { } func setupSinglePaged() pagination.Pager { - testhelper.SetupHTTP() + th.SetupHTTP() client := createClient() - testhelper.Mux.HandleFunc("/only", func(w http.ResponseWriter, r *http.Request) { + th.Mux.HandleFunc("/only", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, `{ "ints": [1, 2, 3] }`) }) @@ -45,36 +45,36 @@ func setupSinglePaged() pagination.Pager { return SinglePageResult{pagination.SinglePageBase(r)} } - return pagination.NewPager(client, testhelper.Server.URL+"/only", createPage) + return pagination.NewPager(client, th.Server.URL+"/only", createPage) } func TestEnumerateSinglePaged(t *testing.T) { callCount := 0 pager := setupSinglePaged() - defer testhelper.TeardownHTTP() + defer th.TeardownHTTP() err := pager.EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { callCount++ expected := []int{1, 2, 3} actual, err := ExtractSingleInts(page) - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) return true, nil }) - testhelper.CheckNoErr(t, err) - testhelper.CheckEquals(t, 1, callCount) + th.CheckNoErr(t, err) + th.CheckEquals(t, 1, callCount) } func TestAllPagesSingle(t *testing.T) { pager := setupSinglePaged() - defer testhelper.TeardownHTTP() + defer th.TeardownHTTP() page, err := pager.AllPages(context.TODO()) - testhelper.AssertNoErr(t, err) + th.AssertNoErr(t, err) expected := []int{1, 2, 3} actual, err := ExtractSingleInts(page) - testhelper.AssertNoErr(t, err) - testhelper.CheckDeepEquals(t, expected, actual) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) } diff --git a/testhelper/client/fake.go b/testhelper/client/fake.go index 6e25f759d7..29514b0a65 100644 --- a/testhelper/client/fake.go +++ b/testhelper/client/fake.go @@ -2,7 +2,7 @@ package client import ( "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) // Fake token to use. @@ -12,6 +12,6 @@ const TokenID = "cbc36478b0bd8e67e89469c7749d4127" func ServiceClient() *gophercloud.ServiceClient { return &gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{TokenID: TokenID}, - Endpoint: testhelper.Endpoint(), + Endpoint: th.Endpoint(), } } From eb1fa6273c5d105d931ef850c68e8f3389d808f8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 30 May 2024 23:54:21 +0100 Subject: [PATCH 1982/2296] tests: Add AssertTypeOf helper Better errors messages since we see the different types. Signed-off-by: Stephen Finucane --- testhelper/convenience.go | 10 ++++++++++ testing/auth_options_test.go | 3 +-- testing/params_test.go | 3 +-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/testhelper/convenience.go b/testhelper/convenience.go index 3b3e63979d..1ea727045f 100644 --- a/testhelper/convenience.go +++ b/testhelper/convenience.go @@ -232,6 +232,16 @@ func CheckEquals(t *testing.T, expected, actual any) { } } +// AssertDeepEquals - like Equals - performs a comparison - but on more complex +// structures that requires deeper inspection +func AssertTypeEquals(t *testing.T, expected, actual any) { + t.Helper() + + if reflect.TypeOf(expected) != reflect.TypeOf(actual) { + logFatal(t, fmt.Sprintf("expected %s but got %s", green(expected), yellow(actual))) + } +} + // AssertDeepEquals - like Equals - performs a comparison - but on more complex // structures that requires deeper inspection func AssertDeepEquals(t *testing.T, expected, actual any) { diff --git a/testing/auth_options_test.go b/testing/auth_options_test.go index 1f9b230b50..fc2c936f89 100644 --- a/testing/auth_options_test.go +++ b/testing/auth_options_test.go @@ -1,7 +1,6 @@ package testing import ( - "reflect" "testing" "github.com/gophercloud/gophercloud/v2" @@ -199,6 +198,6 @@ func TestToTokenV3ScopeMap(t *testing.T) { } for _, failCase := range failCases { _, err := failCase.opts.ToTokenV3ScopeMap() - th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err)) + th.AssertTypeEquals(t, failCase.expected, err) } } diff --git a/testing/params_test.go b/testing/params_test.go index 9e74bdd9ea..057a23b58a 100644 --- a/testing/params_test.go +++ b/testing/params_test.go @@ -2,7 +2,6 @@ package testing import ( "net/url" - "reflect" "testing" "time" @@ -255,7 +254,7 @@ func TestBuildRequestBody(t *testing.T) { for _, failCase := range failCases { _, err := gophercloud.BuildRequestBody(failCase.opts, "auth") - th.AssertDeepEquals(t, reflect.TypeOf(failCase.expected), reflect.TypeOf(err)) + th.AssertTypeEquals(t, failCase.expected, err) } createdAt := time.Date(2018, 1, 4, 10, 00, 12, 0, time.UTC) From 1d81d001a5c1fdb5ecb49001169186aab34f8496 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 31 May 2024 00:03:43 +0100 Subject: [PATCH 1983/2296] testing: Set test case names Make this a little easier to understand failures when they occur. Signed-off-by: Stephen Finucane --- testing/auth_options_test.go | 44 ++++++++++++++++++++---------------- testing/params_test.go | 22 ++++++++++++++---- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/testing/auth_options_test.go b/testing/auth_options_test.go index fc2c936f89..67b06174e6 100644 --- a/testing/auth_options_test.go +++ b/testing/auth_options_test.go @@ -14,11 +14,12 @@ func TestToTokenV3ScopeMap(t *testing.T) { domainName := "Default" var successCases = []struct { + name string opts gophercloud.AuthOptions expected map[string]any }{ - // System-scoped { + "System-scoped", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ System: true, @@ -30,8 +31,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, - // Trust-scoped { + "Trust-scoped", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ TrustID: "05144328-1f7d-46a9-a978-17eaad187077", @@ -43,8 +44,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, - // Project-scoped (ID) { + "Project-scoped (ID)", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ ProjectID: projectID, @@ -56,8 +57,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, - // Project-scoped (name) { + "Project-scoped (name)", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ ProjectName: projectName, @@ -73,8 +74,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, - // Domain-scoped (ID) { + "Domain-scoped (ID)", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ DomainID: domainID, @@ -86,8 +87,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, - // Domain-scoped (name) { + "Domain-scoped (name)", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ DomainName: domainName, @@ -99,8 +100,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, - // Empty with project fallback (ID) { + "Empty with project fallback (ID)", gophercloud.AuthOptions{ TenantID: projectID, Scope: nil, @@ -111,8 +112,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, - // Empty with project fallback (name) { + "Empty with project fallback (name)", gophercloud.AuthOptions{ TenantName: projectName, DomainName: domainName, @@ -127,8 +128,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, }, }, - // Empty without fallback { + "Empty without fallback", gophercloud.AuthOptions{ Scope: nil, }, @@ -136,17 +137,20 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, } for _, successCase := range successCases { - actual, err := successCase.opts.ToTokenV3ScopeMap() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, successCase.expected, actual) + t.Run(successCase.name, func(t *testing.T) { + actual, err := successCase.opts.ToTokenV3ScopeMap() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, successCase.expected, actual) + }) } var failCases = []struct { + name string opts gophercloud.AuthOptions expected error }{ - // Project-scoped with name but missing domain ID/name { + "Project-scoped with name but missing domain ID/name", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ ProjectName: "admin", @@ -154,8 +158,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, gophercloud.ErrScopeDomainIDOrDomainName{}, }, - // Project-scoped with both project name and project ID { + "Project-scoped with both project name and project ID", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ ProjectName: "admin", @@ -165,8 +169,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, gophercloud.ErrScopeProjectIDOrProjectName{}, }, - // Project-scoped with name and unnecessary domain ID { + "Project-scoped with name and unnecessary domain ID", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ ProjectID: "685038cd-3c25-4faf-8f9b-78c18e503190", @@ -175,8 +179,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, gophercloud.ErrScopeProjectIDAlone{}, }, - // Project-scoped with name and unnecessary domain name { + "Project-scoped with name and unnecessary domain name", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ ProjectID: "685038cd-3c25-4faf-8f9b-78c18e503190", @@ -185,8 +189,8 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, gophercloud.ErrScopeProjectIDAlone{}, }, - // Domain-scoped with both domain name and domain ID { + "Domain-scoped with both domain name and domain ID", gophercloud.AuthOptions{ Scope: &gophercloud.AuthScope{ DomainID: "e4b515b8-e453-49d8-9cce-4bec244fa84e", @@ -197,7 +201,9 @@ func TestToTokenV3ScopeMap(t *testing.T) { }, } for _, failCase := range failCases { - _, err := failCase.opts.ToTokenV3ScopeMap() - th.AssertTypeEquals(t, failCase.expected, err) + t.Run(failCase.name, func(t *testing.T) { + _, err := failCase.opts.ToTokenV3ScopeMap() + th.AssertTypeEquals(t, failCase.expected, err) + }) } } diff --git a/testing/params_test.go b/testing/params_test.go index 057a23b58a..146fc27025 100644 --- a/testing/params_test.go +++ b/testing/params_test.go @@ -166,10 +166,12 @@ func TestBuildRequestBody(t *testing.T) { } var successCases = []struct { + name string opts AuthOptions expected map[string]any }{ { + "Password", AuthOptions{ PasswordCredentials: &PasswordCredentials{ Username: "me", @@ -186,6 +188,7 @@ func TestBuildRequestBody(t *testing.T) { }, }, { + "Token", AuthOptions{ TokenCredentials: &TokenCredentials{ ID: "1234567", @@ -202,16 +205,20 @@ func TestBuildRequestBody(t *testing.T) { } for _, successCase := range successCases { - actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth") - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, successCase.expected, actual) + t.Run(successCase.name, func(t *testing.T) { + actual, err := gophercloud.BuildRequestBody(successCase.opts, "auth") + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, successCase.expected, actual) + }) } var failCases = []struct { + name string opts AuthOptions expected error }{ { + "Conflicting tenant name and ID", AuthOptions{ TenantID: "987654321", TenantName: "me", @@ -219,6 +226,7 @@ func TestBuildRequestBody(t *testing.T) { gophercloud.ErrMissingInput{}, }, { + "Conflicting password and token auth", AuthOptions{ TokenCredentials: &TokenCredentials{ ID: "1234567", @@ -231,6 +239,7 @@ func TestBuildRequestBody(t *testing.T) { gophercloud.ErrMissingInput{}, }, { + "Missing Username or UserID", AuthOptions{ PasswordCredentials: &PasswordCredentials{ Password: "swordfish", @@ -239,6 +248,7 @@ func TestBuildRequestBody(t *testing.T) { gophercloud.ErrMissingInput{}, }, { + "Missing filler fields", AuthOptions{ PasswordCredentials: &PasswordCredentials{ Username: "me", @@ -253,8 +263,10 @@ func TestBuildRequestBody(t *testing.T) { } for _, failCase := range failCases { - _, err := gophercloud.BuildRequestBody(failCase.opts, "auth") - th.AssertTypeEquals(t, failCase.expected, err) + t.Run(failCase.name, func(t *testing.T) { + _, err := gophercloud.BuildRequestBody(failCase.opts, "auth") + th.AssertTypeEquals(t, failCase.expected, err) + }) } createdAt := time.Date(2018, 1, 4, 10, 00, 12, 0, time.UTC) From a9bd661231e4b862ea7ea5c33c927a88927b549a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 31 May 2024 15:25:54 +0100 Subject: [PATCH 1984/2296] tests: Be consistent in our generation of credentials We were not passing through user IDs, despite our test wrappers indicating you could user either user IDs or usernames. Similarly, we were not always setting scope, which could cause issues when using a project ID. Correct all issues. We should eventually move this to another test helper but that's a job for a later change. Signed-off-by: Stephen Finucane --- internal/acceptance/openstack/client_test.go | 2 +- .../identity/v3/applicationcredentials_test.go | 4 ++-- .../openstack/identity/v3/credentials_test.go | 4 ++-- .../openstack/identity/v3/ec2credentials_test.go | 2 +- .../acceptance/openstack/identity/v3/oauth1_test.go | 2 +- .../acceptance/openstack/identity/v3/token_test.go | 10 +++++++++- .../acceptance/openstack/identity/v3/trusts_test.go | 7 +++++++ 7 files changed, 23 insertions(+), 8 deletions(-) diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index 7902961c55..69e9a09367 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -56,10 +56,10 @@ func TestEC2AuthMethod(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, - // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, diff --git a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go index 1390e284a9..7a4ad01e1c 100644 --- a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -34,10 +34,10 @@ func TestApplicationCredentialsCRD(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, - // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, @@ -178,10 +178,10 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, - // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, diff --git a/internal/acceptance/openstack/identity/v3/credentials_test.go b/internal/acceptance/openstack/identity/v3/credentials_test.go index a1167ac647..ce0362abee 100644 --- a/internal/acceptance/openstack/identity/v3/credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/credentials_test.go @@ -24,10 +24,10 @@ func TestCredentialsCRUD(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, - // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, @@ -101,10 +101,10 @@ func TestCredentialsValidateS3(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, - // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, diff --git a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go index 6500aa6f0c..7f27f51ed4 100644 --- a/internal/acceptance/openstack/identity/v3/ec2credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/ec2credentials_test.go @@ -23,10 +23,10 @@ func TestEC2CredentialsCRD(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, - // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index da6c8be148..7a93d43fb4 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -24,10 +24,10 @@ func TestOAuth1CRUD(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, - // We need a scope to get the token roles list Scope: tokens.Scope{ ProjectID: ao.TenantID, ProjectName: ao.TenantName, diff --git a/internal/acceptance/openstack/identity/v3/token_test.go b/internal/acceptance/openstack/identity/v3/token_test.go index d5274ff6b1..1bc436caa7 100644 --- a/internal/acceptance/openstack/identity/v3/token_test.go +++ b/internal/acceptance/openstack/identity/v3/token_test.go @@ -24,8 +24,16 @@ func TestTokensGet(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, - DomainName: "default", + DomainName: ao.DomainName, + DomainID: ao.DomainID, + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, } token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() diff --git a/internal/acceptance/openstack/identity/v3/trusts_test.go b/internal/acceptance/openstack/identity/v3/trusts_test.go index 39c7191eea..2eb659d55b 100644 --- a/internal/acceptance/openstack/identity/v3/trusts_test.go +++ b/internal/acceptance/openstack/identity/v3/trusts_test.go @@ -29,9 +29,16 @@ func TestTrustCRUD(t *testing.T) { authOptions := tokens.AuthOptions{ Username: ao.Username, + UserID: ao.UserID, Password: ao.Password, DomainName: ao.DomainName, DomainID: ao.DomainID, + Scope: tokens.Scope{ + ProjectID: ao.TenantID, + ProjectName: ao.TenantName, + DomainID: ao.DomainID, + DomainName: ao.DomainName, + }, } token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() From fb4700ae9b750281e92dc24b320ab77824381968 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 31 May 2024 15:29:19 +0100 Subject: [PATCH 1985/2296] tests: Add missing RequireAdmin calls We're clearly not testing with non-admin credentials. That would be a good future test but for now we simply fix the issues. Signed-off-by: Stephen Finucane --- .../acceptance/openstack/identity/v3/federation_test.go | 6 ++++-- internal/acceptance/openstack/identity/v3/oauth1_test.go | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/acceptance/openstack/identity/v3/federation_test.go b/internal/acceptance/openstack/identity/v3/federation_test.go index b52d54cc30..35986dc15c 100644 --- a/internal/acceptance/openstack/identity/v3/federation_test.go +++ b/internal/acceptance/openstack/identity/v3/federation_test.go @@ -15,6 +15,8 @@ import ( ) func TestListMappings(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) @@ -28,10 +30,10 @@ func TestListMappings(t *testing.T) { } func TestMappingsCRUD(t *testing.T) { - mappingName := tools.RandomString("TESTMAPPING-", 8) - clients.RequireAdmin(t) + mappingName := tools.RandomString("TESTMAPPING-", 8) + client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index 7a93d43fb4..125fdd0588 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -16,6 +16,8 @@ import ( ) func TestOAuth1CRUD(t *testing.T) { + clients.RequireAdmin(t) + client, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) From 7580d132d0eb2f558048664916a74d8eab25bcd8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 31 May 2024 15:30:16 +0100 Subject: [PATCH 1986/2296] scripts: Add getenvvar tool Setting the necessary required environment variables has proven relatively tricky to do. Add a tool that will start doing this for us. Signed-off-by: Stephen Finucane --- script/getenvvar | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100755 script/getenvvar diff --git a/script/getenvvar b/script/getenvvar new file mode 100755 index 0000000000..dbaa08f445 --- /dev/null +++ b/script/getenvvar @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +""" +Set environment variables required for the CI jobs by inspection of the +clouds.yaml file. This is useful where you only have this file. + +To set variables: + + $ eval $(./script/getenvvar) + +To unset them: + + $ unset $(compgen -v | grep OS_) +""" + +import argparse +from pathlib import Path +import sys + +import yaml + +p = Path('~/.config/openstack/clouds.yaml').expanduser() +parser = argparse.ArgumentParser() +parser.add_argument( + 'cloud', + help="Cloud to export credentials for", +) + +args = parser.parse_args() + +with p.open() as fh: + data = yaml.safe_load(fh) + +if args.cloud not in data.get('clouds', {}) or {}: + print(f'Could not find cloud {args.cloud} in {str(p)}', file=sys.stderr) + sys.exit(1) + +cloud = data['clouds'][args.cloud] + +if 'auth' not in cloud: + print(f'Missing auth section for cloud {cloud}', file=sys.stderr) + sys.exit(1) + +auth = cloud['auth'] + +if 'username' not in auth or 'password' not in auth: + print('Only password authentication supported', file=sys.stderr) + sys.exit(1) + +# FIXME: This should work but does not, since the check for auth credentials +# is just 'OS_USERNAME == admin' + +# user_id = auth.get('user_id') +# project_id = auth.get('project_id') +# if not user_id or not project_id: +# import openstack +# conn = openstack.connect(args.cloud) +# auth_ref = conn.config.get_auth().get_auth_ref(conn.session) +# +# if not user_id: +# user_id = auth_ref.user_id +# +# if not project_id: +# project_id = auth_ref.project_id +# +# result = f""" +# unset OS_CLOUD +# export OS_AUTH_URL={auth['auth_url']} +# export OS_USERID={user_id} +# export OS_PASSWORD={auth['password']} +# export OS_PROJECT_ID={project_id} +# export OS_REGION_NAME={cloud['region_name']} +# """.strip() + +result = f""" +unset OS_CLOUD +export OS_AUTH_URL={auth['auth_url']} +export OS_USERNAME={auth['username']} +export OS_PASSWORD={auth['password']} +export OS_PROJECT_NAME={auth['project_name']} +export OS_DOMAIN_ID={auth['user_domain_id']} +export OS_REGION_NAME={cloud['region_name']} +""" + +print(result) From fb846bdeccd110a44d1fed2423521ad90e227a4f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 29 Nov 2024 11:13:38 +0000 Subject: [PATCH 1987/2296] make: Switch default test runner gotestsum provides far more readable output than the default runner. Signed-off-by: Stephen Finucane --- Makefile | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 128beec005..2a7a9376d9 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ undefine GOFLAGS GOLANGCI_LINT_VERSION?=v1.57.1 +GO_TEST?=go run gotest.tools/gotestsum@latest --format testname ifeq ($(shell command -v podman 2> /dev/null),) RUNNER=docker @@ -26,84 +27,84 @@ lint: .PHONY: lint unit: - go test ./... + $(GO_TEST) ./... .PHONY: unit coverage: - go test -covermode count -coverprofile cover.out -coverpkg=./... ./... + $(GO_TEST) -covermode count -coverprofile cover.out -coverpkg=./... ./... .PHONY: coverage acceptance: acceptance-baremetal acceptance-blockstorage acceptance-compute acceptance-container acceptance-containerinfra acceptance-db acceptance-dns acceptance-identity acceptance-imageservice acceptance-keymanager acceptance-loadbalancer acceptance-messaging acceptance-networking acceptance-objectstorage acceptance-orchestration acceptance-placement acceptance-sharedfilesystems acceptance-workflow .PHONY: acceptance acceptance-baremetal: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/baremetal/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/baremetal/... .PHONY: acceptance-baremetal acceptance-blockstorage: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/blockstorage/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/blockstorage/... .PHONY: acceptance-blockstorage acceptance-compute: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/compute/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/compute/... .PHONY: acceptance-compute acceptance-container: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/container/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/container/... .PHONY: acceptance-container acceptance-containerinfra: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/containerinfra/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/containerinfra/... .PHONY: acceptance-containerinfra acceptance-db: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/db/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/db/... .PHONY: acceptance-db acceptance-dns: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/dns/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/dns/... .PHONY: acceptance-dns acceptance-identity: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/identity/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/identity/... .PHONY: acceptance-identity acceptance-image: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/imageservice/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/imageservice/... .PHONY: acceptance-image acceptance-keymanager: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/keymanager/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/keymanager/... .PHONY: acceptance-keymanager acceptance-loadbalancer: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/loadbalancer/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/loadbalancer/... .PHONY: acceptance-loadbalancer acceptance-messaging: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/messaging/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/messaging/... .PHONY: acceptance-messaging acceptance-networking: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/networking/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/networking/... .PHONY: acceptance-networking acceptance-objectstorage: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/objectstorage/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/objectstorage/... .PHONY: acceptance-objectstorage acceptance-orchestration: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/orchestration/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/orchestration/... .PHONY: acceptance-orchestration acceptance-placement: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/placement/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/placement/... .PHONY: acceptance-placement acceptance-sharedfilesystems: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/sharedfilesystems/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/sharedfilesystems/... .PHONY: acceptance-sharefilesystems acceptance-workflow: - go test -tags "fixtures acceptance" ./internal/acceptance/openstack/workflow/... + $(GO_TEST) -tags "fixtures acceptance" ./internal/acceptance/openstack/workflow/... .PHONY: acceptance-workflow From eff7d91cd2662c11fc70d37e77a92efa7d7a3ed3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 10 Oct 2024 10:30:16 +0100 Subject: [PATCH 1988/2296] Add EndpointOpts.Aliases field, Types() method This allows us to request an endpoint by either its service type or one of its aliases. Signed-off-by: Stephen Finucane --- endpoint_search.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/endpoint_search.go b/endpoint_search.go index 2fbc3c97f1..cef845ab8c 100644 --- a/endpoint_search.go +++ b/endpoint_search.go @@ -30,8 +30,9 @@ const ( // package, like "openstack.NewComputeV2()". type EndpointOpts struct { // Type [required] is the service type for the client (e.g., "compute", - // "object-store"). Generally, this will be supplied by the service client - // function, but a user-given value will be honored if provided. + // "object-store"), as defined by the OpenStack Service Types Authority. + // This will generally be supplied by the service client function, but a + // user-given value will be honored if provided. Type string // Name [optional] is the service name for the client (e.g., "nova") as it @@ -39,6 +40,13 @@ type EndpointOpts struct { // different Name, which is why both Type and Name are sometimes needed. Name string + // Aliases [optional] is the set of aliases of the service type (e.g. + // "volumev2"/"volumev3", "volume" and "block-store" for the + // "block-storage" service type), as defined by the OpenStack Service Types + // Authority. As with Type, this will generally be supplied by the service + // client function, but a user-given value will be honored if provided. + Aliases []string + // Region [required] is the geographic region in which the endpoint resides, // generally specifying which datacenter should house your resources. // Required only for services that span multiple regions. @@ -74,3 +82,7 @@ func (eo *EndpointOpts) ApplyDefaults(t string) { eo.Availability = AvailabilityPublic } } + +func (eo *EndpointOpts) Types() []string { + return append([]string{eo.Type}, eo.Aliases...) +} From c507e2a9bf977d2bda06e55e9d0359baeaf11aa2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 10 Oct 2024 11:21:53 +0100 Subject: [PATCH 1989/2296] Extend EndpointOpts.ApplyDefaults to set Aliases This demonstrates an unhappy path that we may wish to deprecate in the future, but no mechanism exists for doing aside from logging, which we don't do anywhere else. Signed-off-by: Stephen Finucane --- endpoint_search.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/endpoint_search.go b/endpoint_search.go index cef845ab8c..8818e769b8 100644 --- a/endpoint_search.go +++ b/endpoint_search.go @@ -1,5 +1,7 @@ package gophercloud +import "slices" + // Availability indicates to whom a specific service endpoint is accessible: // the internet at large, internal networks only, or only to administrators. // Different identity services use different terminology for these. Identity v2 @@ -22,6 +24,31 @@ const ( AvailabilityInternal Availability = "internal" ) +// ServiceTypeAliases contains a mapping of service types to any aliases, as +// defined by the OpenStack Service Types Authority. Only service types that +// we support are included. +var ServiceTypeAliases = map[string][]string{ + "application-container": {"container"}, + "baremetal": {"bare-metal"}, + "baremetal-introspection": {}, + "block-storage": {"block-store", "volume", "volumev2", "volumev3"}, + "compute": {}, + "container-infrastructure-management": {"container-infrastructure", "container-infra"}, + "database": {}, + "dns": {}, + "identity": {}, + "image": {}, + "key-manager": {}, + "load-balancer": {}, + "message": {"messaging"}, + "networking": {}, + "object-store": {}, + "orchestration": {}, + "placement": {}, + "shared-file-system": {"sharev2", "share"}, + "workflow": {"workflowv2"}, +} + // EndpointOpts specifies search criteria used by queries against an // OpenStack service catalog. The options must contain enough information to // unambiguously identify one, and only one, endpoint within the catalog. @@ -81,6 +108,24 @@ func (eo *EndpointOpts) ApplyDefaults(t string) { if eo.Availability == "" { eo.Availability = AvailabilityPublic } + if len(eo.Aliases) == 0 { + if aliases, ok := ServiceTypeAliases[eo.Type]; ok { + // happy path: user requested a service type by its official name + eo.Aliases = aliases + } else { + // unhappy path: user requested a service type by its alias or an + // invalid/unsupported service type + // TODO(stephenfin): This should probably be an error in v3 + for t, aliases := range ServiceTypeAliases { + if slices.Contains(aliases, eo.Type) { + // we intentionally override the service type, even if it + // was explicitly requested by the user + eo.Type = t + eo.Aliases = aliases + } + } + } + } } func (eo *EndpointOpts) Types() []string { From 0ba521aec71039e918235a4b5bbdeedbef732a7d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 10 Oct 2024 10:54:09 +0100 Subject: [PATCH 1990/2296] Start respecting service types when searching for endpoints Signed-off-by: Stephen Finucane --- openstack/endpoint_location.go | 6 ++++-- testing/endpoint_search_test.go | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/openstack/endpoint_location.go b/openstack/endpoint_location.go index 2cdbd3e7f7..14cff0d755 100644 --- a/openstack/endpoint_location.go +++ b/openstack/endpoint_location.go @@ -1,6 +1,8 @@ package openstack import ( + "slices" + "github.com/gophercloud/gophercloud/v2" tokens2 "github.com/gophercloud/gophercloud/v2/openstack/identity/v2/tokens" tokens3 "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/tokens" @@ -20,7 +22,7 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt // Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided. var endpoints = make([]tokens2.Endpoint, 0, 1) for _, entry := range catalog.Entries { - if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { + if (slices.Contains(opts.Types(), entry.Type)) && (opts.Name == "" || entry.Name == opts.Name) { for _, endpoint := range entry.Endpoints { if opts.Region == "" || endpoint.Region == opts.Region { endpoints = append(endpoints, endpoint) @@ -74,7 +76,7 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpt // Name if provided, and Region if provided. var endpoints = make([]tokens3.Endpoint, 0, 1) for _, entry := range catalog.Entries { - if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { + if (slices.Contains(opts.Types(), entry.Type)) && (opts.Name == "" || entry.Name == opts.Name) { for _, endpoint := range entry.Endpoints { if opts.Availability != gophercloud.AvailabilityAdmin && opts.Availability != gophercloud.AvailabilityPublic && diff --git a/testing/endpoint_search_test.go b/testing/endpoint_search_test.go index 278955ecdf..c1ae9b92db 100644 --- a/testing/endpoint_search_test.go +++ b/testing/endpoint_search_test.go @@ -10,11 +10,11 @@ import ( func TestApplyDefaultsToEndpointOpts(t *testing.T) { eo := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic} eo.ApplyDefaults("compute") - expected := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute"} + expected := gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute", Aliases: []string{}} th.CheckDeepEquals(t, expected, eo) eo = gophercloud.EndpointOpts{Type: "compute"} eo.ApplyDefaults("object-store") - expected = gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute"} + expected = gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic, Type: "compute", Aliases: []string{}} th.CheckDeepEquals(t, expected, eo) } From e3947338f69b8b52bd494f09388152febc49c6aa Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 10 Oct 2024 11:08:33 +0100 Subject: [PATCH 1991/2296] Use official service types We now have alias support and Gophercloud will automatically negotiate on our behalf. This means we set the service types to their official value rather than one of the aliases. Signed-off-by: Stephen Finucane --- openstack/blockstorage/noauth/requests.go | 4 ++-- openstack/client.go | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/openstack/blockstorage/noauth/requests.go b/openstack/blockstorage/noauth/requests.go index 87f2d9b7f9..cf887553f0 100644 --- a/openstack/blockstorage/noauth/requests.go +++ b/openstack/blockstorage/noauth/requests.go @@ -51,10 +51,10 @@ func initClientOpts(client *gophercloud.ProviderClient, eo EndpointOpts, clientT // 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") + return initClientOpts(client, eo, "block-storage") } // 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") + return initClientOpts(client, eo, "block-storage") } diff --git a/openstack/client.go b/openstack/client.go index 43b569d3b4..122a3ee699 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -344,6 +344,7 @@ func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp }, nil } +// TODO(stephenfin): Allow passing aliases to all New${SERVICE}V${VERSION} methods in v3 func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) { sc := new(gophercloud.ServiceClient) eo.ApplyDefaults(clientType) @@ -393,6 +394,7 @@ func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpt return sc, err } +// TODO(stephenfin): Remove this in v3. We no longer support the V1 Block Storage service. // NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 // block storage service. func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { @@ -402,17 +404,17 @@ func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoi // NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 // block storage service. func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "volumev2") + return initClientOpts(client, eo, "block-storage") } // NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service. func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "volumev3") + return initClientOpts(client, eo, "block-storage") } // NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service. func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "sharev2") + return initClientOpts(client, eo, "shared-file-system") } // NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 @@ -457,14 +459,14 @@ func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi // NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging // service. func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "messaging") + sc, err := initClientOpts(client, eo, "message") sc.MoreHeaders = map[string]string{"Client-ID": clientID} return sc, err } // NewContainerV1 creates a ServiceClient that may be used with v1 container package func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "container") + return initClientOpts(client, eo, "application-container") } // NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key @@ -478,12 +480,12 @@ func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoint // NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management // package. func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "container-infra") + return initClientOpts(client, eo, "container-infrastructure-management") } // NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package. func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "workflowv2") + return initClientOpts(client, eo, "workflow") } // NewPlacementV1 creates a ServiceClient that may be used with the placement package. From 5a3e9f6c82ea7c66c460653ef0abd5a1e18e47ee Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 29 Nov 2024 14:21:46 +0000 Subject: [PATCH 1992/2296] Set correct service name for microversion header Despite the official service type being block-storage, cinder only accepts volume as the service type in the OpenStack-API-Version header :( Signed-off-by: Stephen Finucane --- service_client.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/service_client.go b/service_client.go index 11b80108c3..c1f9f41d4d 100644 --- a/service_client.go +++ b/service_client.go @@ -115,13 +115,17 @@ func (client *ServiceClient) Head(ctx context.Context, url string, opts *Request } func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { + serviceType := client.Type + switch client.Type { case "compute": opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion - case "sharev2": + case "shared-file-system", "sharev2", "share": opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion - case "volume": + case "block-storage", "block-store", "volume", "volumev3": opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion + // cinder should accept block-storage but (as of Dalmatian) does not + serviceType = "volume" case "baremetal": opts.MoreHeaders["X-OpenStack-Ironic-API-Version"] = client.Microversion case "baremetal-introspection": @@ -129,7 +133,7 @@ func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { } if client.Type != "" { - opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion + opts.MoreHeaders["OpenStack-API-Version"] = serviceType + " " + client.Microversion } } From eaf292310871e42f3bb09e2aaeb32d804f6a46fa Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 17 Jul 2024 12:41:23 +0100 Subject: [PATCH 1993/2296] lint: Address recent lint issues We really need to run golangci-lint in the gate but fix these issues for now. Signed-off-by: Stephen Finucane --- .../extensions/bgpvpns/testing/requests_test.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go b/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go index 859441f6f4..3b39e7cee0 100644 --- a/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go @@ -27,7 +27,9 @@ func TestList(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } th.AssertDeepEquals(t, r.Form["fields"], fields) th.AssertDeepEquals(t, r.Form["project_id"], filterProjectID) @@ -177,7 +179,9 @@ func TestListNetworkAssociations(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } th.AssertDeepEquals(t, fields, r.Form["fields"]) w.Header().Add("Content-Type", "application/json") @@ -278,7 +282,9 @@ func TestListRouterAssociations(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } th.AssertDeepEquals(t, fields, r.Form["fields"]) w.Header().Add("Content-Type", "application/json") @@ -404,7 +410,9 @@ func TestListPortAssociations(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - r.ParseForm() + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } th.AssertDeepEquals(t, fields, r.Form["fields"]) w.Header().Add("Content-Type", "application/json") From ce56d121f0139488477330743b89bad1cd244964 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 29 Nov 2024 14:39:32 +0000 Subject: [PATCH 1994/2296] lint: Remove non-constant format string in calls (govet) We were incorrectly using 'fmt.Printf', 'fmt.Errorf' and 't.Logf' with non-template strings/no arguments. The fix to this is replace these calls with the non-suffixed variants. There are many users of 'fmt.Fprint' - too many to do by hand - so this replacement was resolved using 'sed': sed 's/Fprintf/Fprint/g' -i $(ag fmt.Fprintf -l) We then manually fix the 25 cases where 'fmt.Fprintf' is actually warranted and manually replaced the errant users of 'fmt.Errorf' and 't.Logf'. We also rework 'internal/acceptance/clients/clients.go' slightly to make the code a bit clearer. PS: This is apparently going to be an issue in go 1.24 (specifically in 'go vet') [1] so this is not just golangci-lint being annoying. @PierrePrinetti, that's directed at you ;) [1] https://github.com/golang/go/issues/60529 Signed-off-by: Stephen Finucane --- internal/acceptance/clients/clients.go | 16 +++---- internal/acceptance/tools/tools.go | 2 +- .../apiversions/testing/fixtures_test.go | 4 +- .../v1/allocations/testing/fixtures_test.go | 8 ++-- .../v1/conductors/testing/fixtures_test.go | 8 ++-- .../v1/drivers/testing/fixtures_test.go | 8 ++-- .../v1/nodes/testing/fixtures_test.go | 38 +++++++-------- .../v1/ports/testing/fixtures_test.go | 12 ++--- .../v1/introspection/testing/fixtures.go | 8 ++-- .../apiversions/testing/fixtures_test.go | 4 +- .../testing/fixtures_test.go | 2 +- .../v2/backups/testing/fixtures_test.go | 14 +++--- .../v2/limits/testing/fixtures_test.go | 2 +- .../v2/quotasets/testing/fixtures_test.go | 2 +- .../schedulerstats/testing/fixtures_test.go | 4 +- .../v2/services/testing/fixtures_test.go | 2 +- .../v2/snapshots/testing/fixtures_test.go | 8 ++-- .../v2/transfers/testing/fixtures_test.go | 8 ++-- .../v2/volumes/testing/fixtures_test.go | 30 ++++++------ .../v3/attachments/testing/fixtures_test.go | 10 ++-- .../testing/fixtures_test.go | 2 +- .../v3/backups/testing/fixtures_test.go | 14 +++--- .../v3/limits/testing/fixtures_test.go | 2 +- .../v3/qos/testing/fixtures_test.go | 10 ++-- .../v3/quotasets/testing/fixtures_test.go | 2 +- .../schedulerstats/testing/fixtures_test.go | 4 +- .../v3/services/testing/fixtures_test.go | 2 +- .../v3/snapshots/testing/fixtures_test.go | 10 ++-- .../v3/transfers/testing/fixtures_test.go | 8 ++-- .../v3/volumes/testing/fixtures_test.go | 32 ++++++------- .../v3/volumetypes/testing/fixtures_test.go | 24 +++++----- .../v3/volumetypes/testing/requests_test.go | 2 +- .../common/extensions/testing/fixtures.go | 4 +- .../apiversions/testing/fixtures_test.go | 6 +-- .../v2/aggregates/testing/fixtures_test.go | 14 +++--- .../attachinterfaces/testing/fixtures_test.go | 6 +-- .../testing/fixtures_test.go | 4 +- .../v2/extensions/testing/fixtures_test.go | 4 +- .../v2/flavors/testing/fixtures_test.go | 8 ++-- .../v2/flavors/testing/requests_test.go | 14 +++--- .../v2/hypervisors/testing/fixtures_test.go | 16 +++---- .../instanceactions/testing/fixtures_test.go | 4 +- .../v2/keypairs/testing/fixtures_test.go | 12 ++--- .../v2/limits/testing/fixtures_test.go | 2 +- .../v2/quotasets/testing/fixtures_test.go | 8 ++-- .../remoteconsoles/testing/requests_test.go | 2 +- .../v2/secgroups/testing/fixtures_test.go | 14 +++--- .../v2/servergroups/testing/fixtures_test.go | 10 ++-- .../v2/servers/testing/fixtures_test.go | 46 +++++++++--------- .../v2/services/testing/fixtures_test.go | 6 +-- .../compute/v2/tags/testing/requests_test.go | 4 +- .../v2/volumeattach/testing/fixtures_test.go | 6 +-- .../v1/capsules/testing/fixtures_test.go | 10 ++-- .../apiversions/testing/fixtures_test.go | 4 +- .../v1/certificates/testing/fixtures_test.go | 2 +- .../v1/nodegroups/testing/fixtures_test.go | 22 ++++----- .../v2/recordsets/testing/fixtures_test.go | 12 ++--- .../transfer/accept/testing/fixtures_test.go | 8 ++-- .../transfer/request/testing/fixtures_test.go | 8 ++-- .../dns/v2/zones/testing/fixtures_test.go | 10 ++-- .../v2/extensions/testing/fixtures_test.go | 2 +- .../v2/roles/testing/fixtures_test.go | 2 +- .../v2/tenants/testing/fixtures_test.go | 8 ++-- .../v2/tokens/testing/fixtures_test.go | 4 +- .../v2/users/testing/fixtures_test.go | 10 ++-- .../testing/fixtures_test.go | 14 +++--- .../v3/catalog/testing/fixtures_test.go | 2 +- .../v3/credentials/testing/fixtures_test.go | 8 ++-- .../v3/domains/testing/fixtures_test.go | 10 ++-- .../ec2credentials/testing/fixtures_test.go | 6 +-- .../v3/ec2tokens/testing/requests_test.go | 2 +- .../v3/endpoints/testing/requests_test.go | 6 +-- .../v3/federation/testing/fixtures_test.go | 8 ++-- .../v3/groups/testing/fixtures_test.go | 8 ++-- .../v3/limits/testing/fixtures_test.go | 10 ++-- .../v3/oauth1/testing/fixtures_test.go | 24 +++++----- .../v3/policies/testing/fixtures_test.go | 10 ++-- .../projectendpoints/testing/requests_test.go | 2 +- .../v3/projects/testing/fixtures_test.go | 14 +++--- .../v3/regions/testing/fixtures_test.go | 8 ++-- .../registeredlimits/testing/fixtures_test.go | 8 ++-- .../v3/roles/testing/fixtures_test.go | 28 +++++------ .../v3/services/testing/fixtures_test.go | 8 ++-- .../v3/tokens/testing/requests_test.go | 10 ++-- .../v3/trusts/testing/fixtures_test.go | 12 ++--- .../v3/users/testing/fixtures_test.go | 16 +++---- .../v2/imageimport/testing/requests_test.go | 4 +- .../image/v2/images/testing/fixtures_test.go | 16 +++---- .../image/v2/members/testing/fixtures_test.go | 10 ++-- .../image/v2/tasks/testing/requests_test.go | 6 +-- .../v1/acls/testing/fixtures_test.go | 12 ++--- .../v1/containers/testing/fixtures_test.go | 12 ++--- .../v1/orders/testing/fixtures_test.go | 6 +-- .../v1/secrets/testing/fixtures_test.go | 18 +++---- .../v2/amphorae/testing/fixtures_test.go | 6 +-- .../v2/apiversions/testing/fixture.go | 2 +- .../v2/flavorprofiles/testing/fixtures.go | 10 ++-- .../v2/flavors/testing/fixtures.go | 10 ++-- .../v2/flavors/testing/requests_test.go | 2 +- .../v2/l7policies/testing/fixtures_test.go | 22 ++++----- .../v2/listeners/testing/fixtures_test.go | 12 ++--- .../v2/loadbalancers/testing/fixtures_test.go | 16 +++---- .../v2/monitors/testing/fixtures_test.go | 10 ++-- .../v2/pools/testing/fixtures_test.go | 20 ++++---- .../v2/providers/testing/fixtures_test.go | 2 +- .../v2/quotas/testing/requests_test.go | 8 ++-- .../v2/claims/testing/fixtures_test.go | 4 +- .../v2/messages/testing/fixtures_test.go | 10 ++-- .../v2/queues/testing/fixtures_test.go | 10 ++-- .../v2/apiversions/testing/requests_test.go | 4 +- .../agents/testing/requests_test.go | 14 +++--- .../attributestags/testing/requests_test.go | 4 +- .../bgp/peers/testing/requests_test.go | 8 ++-- .../bgp/speakers/testing/requests_test.go | 18 +++---- .../bgpvpns/testing/requests_test.go | 30 ++++++------ .../extensions/dns/testing/fixtures_test.go | 22 ++++----- .../external/testing/results_test.go | 8 ++-- .../fwaas_v2/groups/testing/requests_test.go | 12 ++--- .../policies/testing/requests_test.go | 12 ++--- .../fwaas_v2/rules/testing/requests_test.go | 10 ++-- .../addressscopes/testing/requests_test.go | 8 ++-- .../extraroutes/testing/requests_test.go | 4 +- .../floatingips/testing/requests_test.go | 16 +++---- .../portforwarding/testing/requests_test.go | 8 ++-- .../layer3/routers/testing/requests_test.go | 18 +++---- .../v2/extensions/mtu/testing/results_test.go | 8 ++-- .../testing/requests_test.go | 4 +- .../portsbinding/testing/fixtures_test.go | 8 ++-- .../provider/testing/results_test.go | 10 ++-- .../qos/policies/testing/requests_test.go | 24 +++++----- .../qos/rules/testing/requests_test.go | 24 +++++----- .../qos/ruletypes/testing/requests_test.go | 2 +- .../quotas/testing/requests_test.go | 6 +-- .../rbacpolicies/testing/requests_test.go | 10 ++-- .../security/groups/testing/requests_test.go | 8 ++-- .../security/rules/testing/requests_test.go | 8 ++-- .../subnetpools/testing/requests_test.go | 8 ++-- .../v2/extensions/testing/delegate_test.go | 4 +- .../trunks/testing/requests_test.go | 16 +++---- .../vlantransparent/testing/requests_test.go | 8 ++-- .../endpointgroups/testing/requests_test.go | 8 ++-- .../ikepolicies/testing/requests_test.go | 8 ++-- .../ipsecpolicies/testing/requests_test.go | 8 ++-- .../vpnaas/services/testing/requests_test.go | 8 ++-- .../siteconnections/testing/requests_test.go | 8 ++-- .../v2/networks/testing/requests_test.go | 22 ++++----- .../v2/ports/testing/requests_test.go | 48 +++++++++---------- .../v2/subnets/testing/requests_test.go | 32 ++++++------- .../v1/containers/testing/fixtures.go | 8 ++-- .../v1/objects/testing/fixtures_test.go | 12 ++--- .../v1/swauth/testing/fixtures_test.go | 2 +- .../v1/apiversions/testing/requests_test.go | 2 +- .../v1/buildinfo/testing/fixtures_test.go | 2 +- .../v1/stackevents/testing/fixtures_test.go | 12 ++--- .../stackresources/testing/fixtures_test.go | 16 +++---- .../v1/stacks/testing/fixtures_test.go | 14 +++--- .../orchestration/v1/stacks/utils_test.go | 2 +- .../stacktemplates/testing/fixtures_test.go | 4 +- .../testing/fixtures_test.go | 16 +++---- .../apiversions/testing/fixtures_test.go | 8 ++-- .../testing/fixtures_test.go | 2 +- .../v2/errors/testing/fixtures_test.go | 2 +- .../v2/messages/testing/fixtures_test.go | 6 +-- .../v2/replicas/testing/fixtures_test.go | 8 ++-- .../schedulerstats/testing/fixtures_test.go | 4 +- .../securityservices/testing/fixtures_test.go | 10 ++-- .../v2/services/testing/fixtures_test.go | 2 +- .../shareaccessrules/testing/fixtures_test.go | 2 +- .../shareaccessrules/testing/requests_test.go | 2 +- .../v2/sharenetworks/testing/fixtures_test.go | 24 +++++----- .../v2/shares/testing/fixtures_test.go | 14 +++--- .../sharetransfers/testing/fixtures_test.go | 8 ++-- .../v2/sharetypes/testing/fixtures_test.go | 12 ++--- .../v2/snapshots/testing/fixtures_test.go | 6 +-- openstack/testing/client_test.go | 6 +-- .../v2/crontriggers/testing/requests_test.go | 6 +-- .../v2/executions/testing/requests_test.go | 6 +-- .../v2/workflows/testing/requests_test.go | 10 ++-- pagination/testing/linked_test.go | 2 +- pagination/testing/marker_test.go | 6 +-- pagination/testing/single_test.go | 2 +- testhelper/fixture/helper.go | 2 +- testing/provider_client_test.go | 4 +- 183 files changed, 882 insertions(+), 884 deletions(-) diff --git a/internal/acceptance/clients/clients.go b/internal/acceptance/clients/clients.go index 21be9dbea2..f981ffcea2 100644 --- a/internal/acceptance/clients/clients.go +++ b/internal/acceptance/clients/clients.go @@ -107,16 +107,14 @@ func AcceptanceTestChoicesFromEnv() (*AcceptanceTestChoices, error) { notDistinct = "OS_FLAVOR_ID and OS_FLAVOR_ID_RESIZE must be distinct." } - if len(missing) > 0 || notDistinct != "" { - text := "You're missing some important setup:\n" - if len(missing) > 0 { - text += " * These environment variables must be provided: " + strings.Join(missing, ", ") + "\n" - } - if notDistinct != "" { - text += " * " + notDistinct + "\n" - } + if len(missing) > 0 { + text := "You're missing some important setup:\n * These environment variables must be provided: %s\n" + return nil, fmt.Errorf(text, strings.Join(missing, ", ")) + } - return nil, fmt.Errorf(text) + if notDistinct != "" { + text := "You're missing some important setup:\n * %s\n" + return nil, fmt.Errorf(text, notDistinct) } return &AcceptanceTestChoices{ diff --git a/internal/acceptance/tools/tools.go b/internal/acceptance/tools/tools.go index 8b0fad2ea6..75ac004473 100644 --- a/internal/acceptance/tools/tools.go +++ b/internal/acceptance/tools/tools.go @@ -81,5 +81,5 @@ func Elide(value string) string { // PrintResource returns a resource as a readable structure func PrintResource(t *testing.T, resource any) { b, _ := json.MarshalIndent(resource, "", " ") - t.Logf(string(b)) + t.Log(string(b)) } diff --git a/openstack/baremetal/apiversions/testing/fixtures_test.go b/openstack/baremetal/apiversions/testing/fixtures_test.go index 5e169d36fc..d0e11553c2 100644 --- a/openstack/baremetal/apiversions/testing/fixtures_test.go +++ b/openstack/baremetal/apiversions/testing/fixtures_test.go @@ -89,7 +89,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, IronicAPIAllVersionResponse) + fmt.Fprint(w, IronicAPIAllVersionResponse) }) } @@ -101,6 +101,6 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, IronicAPIVersionResponse) + fmt.Fprint(w, IronicAPIVersionResponse) }) } diff --git a/openstack/baremetal/v1/allocations/testing/fixtures_test.go b/openstack/baremetal/v1/allocations/testing/fixtures_test.go index 6b8655169b..7ee9fa4834 100644 --- a/openstack/baremetal/v1/allocations/testing/fixtures_test.go +++ b/openstack/baremetal/v1/allocations/testing/fixtures_test.go @@ -120,10 +120,10 @@ func HandleAllocationListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, AllocationListBody) + fmt.Fprint(w, AllocationListBody) case "eff80f47-75f0-4d41-b1aa-cf07c201adac": - fmt.Fprintf(w, `{ "allocations": [] }`) + fmt.Fprint(w, `{ "allocations": [] }`) default: t.Fatalf("/allocations invoked with unexpected marker=[%s]", marker) } @@ -145,7 +145,7 @@ func HandleAllocationCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -165,6 +165,6 @@ func HandleAllocationGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleAllocationBody) + fmt.Fprint(w, SingleAllocationBody) }) } diff --git a/openstack/baremetal/v1/conductors/testing/fixtures_test.go b/openstack/baremetal/v1/conductors/testing/fixtures_test.go index 8e59ddaab4..fd66a34d4b 100644 --- a/openstack/baremetal/v1/conductors/testing/fixtures_test.go +++ b/openstack/baremetal/v1/conductors/testing/fixtures_test.go @@ -150,10 +150,10 @@ func HandleConductorListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, ConductorListBody) + fmt.Fprint(w, ConductorListBody) case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": - fmt.Fprintf(w, `{ "servers": [] }`) + fmt.Fprint(w, `{ "servers": [] }`) default: t.Fatalf("/conductors invoked with unexpected marker=[%s]", marker) } @@ -170,7 +170,7 @@ func HandleConductorListDetailSuccessfully(t *testing.T) { t.Errorf("Failed to parse request form %v", err) } - fmt.Fprintf(w, ConductorListDetailBody) + fmt.Fprint(w, ConductorListDetailBody) }) } @@ -180,6 +180,6 @@ func HandleConductorGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleConductorBody) + fmt.Fprint(w, SingleConductorBody) }) } diff --git a/openstack/baremetal/v1/drivers/testing/fixtures_test.go b/openstack/baremetal/v1/drivers/testing/fixtures_test.go index a06bcee928..6c6d0fcc7e 100644 --- a/openstack/baremetal/v1/drivers/testing/fixtures_test.go +++ b/openstack/baremetal/v1/drivers/testing/fixtures_test.go @@ -377,7 +377,7 @@ func HandleListDriversSuccessfully(t *testing.T) { t.Errorf("Failed to parse request form %v", err) } - fmt.Fprintf(w, ListDriversBody) + fmt.Fprint(w, ListDriversBody) }) } @@ -388,7 +388,7 @@ func HandleGetDriverDetailsSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleDriverDetails) + fmt.Fprint(w, SingleDriverDetails) }) } @@ -399,7 +399,7 @@ func HandleGetDriverPropertiesSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleDriverProperties) + fmt.Fprint(w, SingleDriverProperties) }) } @@ -410,6 +410,6 @@ func HandleGetDriverDiskPropertiesSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleDriverDiskProperties) + fmt.Fprint(w, SingleDriverDiskProperties) }) } diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index d174edde2b..10589c5f1b 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -1404,10 +1404,10 @@ func HandleNodeListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, NodeListBody) + fmt.Fprint(w, NodeListBody) case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": - fmt.Fprintf(w, `{ "servers": [] }`) + fmt.Fprint(w, `{ "servers": [] }`) default: t.Fatalf("/nodes invoked with unexpected marker=[%s]", marker) } @@ -1424,7 +1424,7 @@ func HandleNodeListDetailSuccessfully(t *testing.T) { t.Errorf("Failed to parse request form %v", err) } - fmt.Fprintf(w, NodeListDetailBody) + fmt.Fprint(w, NodeListDetailBody) }) } @@ -1451,7 +1451,7 @@ func HandleNodeCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -1471,7 +1471,7 @@ func HandleNodeGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleNodeBody) + fmt.Fprint(w, SingleNodeBody) }) } @@ -1483,7 +1483,7 @@ func HandleNodeUpdateSuccessfully(t *testing.T, response string) { th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `[{"op": "replace", "path": "/properties", "value": {"root_gb": 25}}]`) - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -1493,7 +1493,7 @@ func HandleNodeValidateSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, NodeValidationBody) + fmt.Fprint(w, NodeValidationBody) }) } @@ -1525,7 +1525,7 @@ func HandleGetBootDeviceSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NodeBootDeviceBody) + fmt.Fprint(w, NodeBootDeviceBody) }) } @@ -1535,7 +1535,7 @@ func HandleGetSupportedBootDeviceSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NodeSupportedBootDeviceBody) + fmt.Fprint(w, NodeSupportedBootDeviceBody) }) } @@ -1667,7 +1667,7 @@ func HandleListBIOSSettingsSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, NodeBIOSSettingsBody) + fmt.Fprint(w, NodeBIOSSettingsBody) }) } @@ -1677,7 +1677,7 @@ func HandleListDetailBIOSSettingsSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, NodeDetailBIOSSettingsBody) + fmt.Fprint(w, NodeDetailBIOSSettingsBody) }) } @@ -1687,7 +1687,7 @@ func HandleGetBIOSSettingSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, NodeSingleBIOSSettingBody) + fmt.Fprint(w, NodeSingleBIOSSettingBody) }) } @@ -1697,7 +1697,7 @@ func HandleGetVendorPassthruMethodsSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, NodeVendorPassthruMethodsBody) + fmt.Fprint(w, NodeVendorPassthruMethodsBody) }) } @@ -1708,7 +1708,7 @@ func HandleGetAllSubscriptionsVendorPassthruSuccessfully(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") th.TestFormValues(t, r, map[string]string{"method": "get_all_subscriptions"}) - fmt.Fprintf(w, NodeGetAllSubscriptionsVnedorPassthruBody) + fmt.Fprint(w, NodeGetAllSubscriptionsVnedorPassthruBody) }) } @@ -1724,7 +1724,7 @@ func HandleGetSubscriptionVendorPassthruSuccessfully(t *testing.T) { } `) - fmt.Fprintf(w, NodeGetSubscriptionVendorPassthruBody) + fmt.Fprint(w, NodeGetSubscriptionVendorPassthruBody) }) } @@ -1744,7 +1744,7 @@ func HandleCreateSubscriptionVendorPassthruAllParametersSuccessfully(t *testing. } `) - fmt.Fprintf(w, NodeCreateSubscriptionVendorPassthruAllParametersBody) + fmt.Fprint(w, NodeCreateSubscriptionVendorPassthruAllParametersBody) }) } @@ -1760,7 +1760,7 @@ func HandleCreateSubscriptionVendorPassthruRequiredParametersSuccessfully(t *tes } `) - fmt.Fprintf(w, NodeCreateSubscriptionVendorPassthruRequiredParametersBody) + fmt.Fprint(w, NodeCreateSubscriptionVendorPassthruRequiredParametersBody) }) } @@ -1805,7 +1805,7 @@ func HandleGetInventorySuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NodeInventoryBody) + fmt.Fprint(w, NodeInventoryBody) }) } @@ -1815,7 +1815,7 @@ func HandleListFirmwareSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NodeFirmwareListBody) + fmt.Fprint(w, NodeFirmwareListBody) }) } diff --git a/openstack/baremetal/v1/ports/testing/fixtures_test.go b/openstack/baremetal/v1/ports/testing/fixtures_test.go index 492aa42aa6..a6221ecd4a 100644 --- a/openstack/baremetal/v1/ports/testing/fixtures_test.go +++ b/openstack/baremetal/v1/ports/testing/fixtures_test.go @@ -181,10 +181,10 @@ func HandlePortListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, PortListBody) + fmt.Fprint(w, PortListBody) case "f2845e11-dbd4-4728-a8c0-30d19f48924a": - fmt.Fprintf(w, `{ "ports": [] }`) + fmt.Fprint(w, `{ "ports": [] }`) default: t.Fatalf("/ports invoked with unexpected marker=[%s]", marker) } @@ -201,7 +201,7 @@ func HandlePortListDetailSuccessfully(t *testing.T) { t.Errorf("Failed to parse request form %v", err) } - fmt.Fprintf(w, PortListDetailBody) + fmt.Fprint(w, PortListDetailBody) }) } @@ -219,7 +219,7 @@ func HandlePortCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -239,7 +239,7 @@ func HandlePortGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SinglePortBody) + fmt.Fprint(w, SinglePortBody) }) } @@ -251,6 +251,6 @@ func HandlePortUpdateSuccessfully(t *testing.T, response string) { th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `[{"op": "replace", "path": "/address", "value": "22:22:22:22:22:22"}]`) - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } diff --git a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go index af2ad9e7f5..56a4299aef 100644 --- a/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go +++ b/openstack/baremetalintrospection/v1/introspection/testing/fixtures.go @@ -467,10 +467,10 @@ func HandleListIntrospectionsSuccessfully(t *testing.T) { switch marker { case "": - fmt.Fprintf(w, IntrospectionListBody) + fmt.Fprint(w, IntrospectionListBody) case "c244557e-899f-46fa-a1ff-5b2c6718616b": - fmt.Fprintf(w, `{ "introspection": [] }`) + fmt.Fprint(w, `{ "introspection": [] }`) default: t.Fatalf("/introspection invoked with unexpected marker=[%s]", marker) @@ -484,7 +484,7 @@ func HandleGetIntrospectionStatusSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, IntrospectionStatus) + fmt.Fprint(w, IntrospectionStatus) }) } @@ -513,7 +513,7 @@ func HandleGetIntrospectionDataSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, IntrospectionDataJSONSample) + fmt.Fprint(w, IntrospectionDataJSONSample) }) } diff --git a/openstack/blockstorage/apiversions/testing/fixtures_test.go b/openstack/blockstorage/apiversions/testing/fixtures_test.go index 4a368e9da9..ed78b1e472 100644 --- a/openstack/blockstorage/apiversions/testing/fixtures_test.go +++ b/openstack/blockstorage/apiversions/testing/fixtures_test.go @@ -124,7 +124,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, APIListResponse) + fmt.Fprint(w, APIListResponse) }) } @@ -136,6 +136,6 @@ func MockListOldResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, APIListOldResponse) + fmt.Fprint(w, APIListOldResponse) }) } diff --git a/openstack/blockstorage/v2/availabilityzones/testing/fixtures_test.go b/openstack/blockstorage/v2/availabilityzones/testing/fixtures_test.go index b13e464abb..5c48bdce2e 100644 --- a/openstack/blockstorage/v2/availabilityzones/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/availabilityzones/testing/fixtures_test.go @@ -47,6 +47,6 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/blockstorage/v2/backups/testing/fixtures_test.go b/openstack/blockstorage/v2/backups/testing/fixtures_test.go index 2f24173418..4f1f45c122 100644 --- a/openstack/blockstorage/v2/backups/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/backups/testing/fixtures_test.go @@ -210,7 +210,7 @@ func MockListResponse(t *testing.T) { case "": fmt.Fprintf(w, ListResponse, th.Server.URL) case "1": - fmt.Fprintf(w, `{"backups": []}`) + fmt.Fprint(w, `{"backups": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -233,7 +233,7 @@ func MockListDetailResponse(t *testing.T) { case "": fmt.Fprintf(w, ListDetailResponse, th.Server.URL) case "1": - fmt.Fprintf(w, `{"backups": []}`) + fmt.Fprint(w, `{"backups": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -247,7 +247,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -262,7 +262,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) } @@ -277,7 +277,7 @@ func MockRestoreResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, RestoreResponse) + fmt.Fprint(w, RestoreResponse) }) } @@ -298,7 +298,7 @@ func MockExportResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ExportResponse) + fmt.Fprint(w, ExportResponse) }) } @@ -313,7 +313,7 @@ func MockImportResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ImportResponse) + fmt.Fprint(w, ImportResponse) }) } diff --git a/openstack/blockstorage/v2/limits/testing/fixtures_test.go b/openstack/blockstorage/v2/limits/testing/fixtures_test.go index c3f8bf803d..bdd1308a17 100644 --- a/openstack/blockstorage/v2/limits/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/limits/testing/fixtures_test.go @@ -124,6 +124,6 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go b/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go index 066e1729c8..854ff08b63 100644 --- a/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/quotasets/testing/fixtures_test.go @@ -164,7 +164,7 @@ func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput strin th.TestFormValues(t, r, uriQueryParams) } - fmt.Fprintf(w, jsonOutput) + fmt.Fprint(w, jsonOutput) }) } diff --git a/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go index 3a7fee8160..05512dbf6d 100644 --- a/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/schedulerstats/testing/fixtures_test.go @@ -104,9 +104,9 @@ func HandleStoragePoolsListSuccessfully(t *testing.T) { t.Errorf("Failed to parse request form %v", err) } if r.FormValue("detail") == "true" { - fmt.Fprintf(w, StoragePoolsListBodyDetail) + fmt.Fprint(w, StoragePoolsListBodyDetail) } else { - fmt.Fprintf(w, StoragePoolsListBody) + fmt.Fprint(w, StoragePoolsListBody) } }) } diff --git a/openstack/blockstorage/v2/services/testing/fixtures_test.go b/openstack/blockstorage/v2/services/testing/fixtures_test.go index a396abc39e..41fb5bc614 100644 --- a/openstack/blockstorage/v2/services/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/services/testing/fixtures_test.go @@ -92,6 +92,6 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ServiceListBody) + fmt.Fprint(w, ServiceListBody) }) } diff --git a/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go b/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go index 2c00c8a05f..b2e5cf4c06 100644 --- a/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/snapshots/testing/fixtures_test.go @@ -17,7 +17,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "snapshots": [ { @@ -51,7 +51,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "snapshot": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", @@ -85,7 +85,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "snapshot": { "volume_id": "1234", @@ -115,7 +115,7 @@ func MockUpdateMetadataResponse(t *testing.T) { } `) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "metadata": { "key": "v1" diff --git a/openstack/blockstorage/v2/transfers/testing/fixtures_test.go b/openstack/blockstorage/v2/transfers/testing/fixtures_test.go index 6a150508eb..866d5068fc 100644 --- a/openstack/blockstorage/v2/transfers/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/transfers/testing/fixtures_test.go @@ -167,7 +167,7 @@ func HandleCreateTransfer(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) } @@ -179,7 +179,7 @@ func HandleAcceptTransfer(t *testing.T) { th.TestJSONRequest(t, r, AcceptTransferRequest) w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, AcceptTransferResponse) + fmt.Fprint(w, AcceptTransferResponse) }) } @@ -200,7 +200,7 @@ func HandleListTransfers(t *testing.T) { th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -211,6 +211,6 @@ func HandleGetTransfer(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/blockstorage/v2/volumes/testing/fixtures_test.go b/openstack/blockstorage/v2/volumes/testing/fixtures_test.go index 80677d2e40..d6688f7850 100644 --- a/openstack/blockstorage/v2/volumes/testing/fixtures_test.go +++ b/openstack/blockstorage/v2/volumes/testing/fixtures_test.go @@ -17,7 +17,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volumes": [ { @@ -93,7 +93,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume": { "volume_type": "lvmdriver-1", @@ -152,7 +152,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume": { "size": 75, @@ -192,7 +192,7 @@ func MockUpdateResponse(t *testing.T) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume": { "name": "vol-002" @@ -223,7 +223,7 @@ func MockAttachResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -243,7 +243,7 @@ func MockBeginDetachingResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -263,7 +263,7 @@ func MockDetachResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -288,7 +288,7 @@ func MockUploadImageResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "os-volume_upload_image": { "container_format": "bare", @@ -335,7 +335,7 @@ func MockReserveResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -355,7 +355,7 @@ func MockUnreserveResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -386,7 +386,7 @@ func MockInitializeConnectionResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "connection_info": { "data": { "target_portals": [ @@ -443,7 +443,7 @@ func MockTerminateConnectionResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -466,7 +466,7 @@ func MockExtendSizeResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -497,7 +497,7 @@ func MockSetImageMetadataResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -560,7 +560,7 @@ func MockChangeTypeResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } diff --git a/openstack/blockstorage/v3/attachments/testing/fixtures_test.go b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go index d489ce16ef..18471c3b38 100644 --- a/openstack/blockstorage/v3/attachments/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/attachments/testing/fixtures_test.go @@ -64,7 +64,7 @@ func MockListResponse(t *testing.T) { } `, th.Server.URL) case "1": - fmt.Fprintf(w, `{"volumes": []}`) + fmt.Fprint(w, `{"volumes": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -78,7 +78,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "attachment": { "status": "attaching", @@ -123,7 +123,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "attachment": { "status": "attaching", @@ -171,7 +171,7 @@ func MockUpdateResponse(t *testing.T) { `) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "attachment": { "status": "attaching", @@ -202,7 +202,7 @@ func MockUpdateEmptyResponse(t *testing.T) { `) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "attachment": { "status": "attaching", diff --git a/openstack/blockstorage/v3/availabilityzones/testing/fixtures_test.go b/openstack/blockstorage/v3/availabilityzones/testing/fixtures_test.go index 98f93db1d3..780c7dec26 100644 --- a/openstack/blockstorage/v3/availabilityzones/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/availabilityzones/testing/fixtures_test.go @@ -47,6 +47,6 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/blockstorage/v3/backups/testing/fixtures_test.go b/openstack/blockstorage/v3/backups/testing/fixtures_test.go index 13fc1d61d9..9bbaec87d4 100644 --- a/openstack/blockstorage/v3/backups/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/backups/testing/fixtures_test.go @@ -210,7 +210,7 @@ func MockListResponse(t *testing.T) { case "": fmt.Fprintf(w, ListResponse, th.Server.URL) case "1": - fmt.Fprintf(w, `{"backups": []}`) + fmt.Fprint(w, `{"backups": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -233,7 +233,7 @@ func MockListDetailResponse(t *testing.T) { case "": fmt.Fprintf(w, ListDetailResponse, th.Server.URL) case "1": - fmt.Fprintf(w, `{"backups": []}`) + fmt.Fprint(w, `{"backups": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -247,7 +247,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -262,7 +262,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) } @@ -277,7 +277,7 @@ func MockRestoreResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, RestoreResponse) + fmt.Fprint(w, RestoreResponse) }) } @@ -298,7 +298,7 @@ func MockExportResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ExportResponse) + fmt.Fprint(w, ExportResponse) }) } @@ -313,7 +313,7 @@ func MockImportResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ImportResponse) + fmt.Fprint(w, ImportResponse) }) } diff --git a/openstack/blockstorage/v3/limits/testing/fixtures_test.go b/openstack/blockstorage/v3/limits/testing/fixtures_test.go index 5d3b647427..68fe8ae833 100644 --- a/openstack/blockstorage/v3/limits/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/limits/testing/fixtures_test.go @@ -124,6 +124,6 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/blockstorage/v3/qos/testing/fixtures_test.go b/openstack/blockstorage/v3/qos/testing/fixtures_test.go index 8f9d2e2d51..e6cd002cea 100644 --- a/openstack/blockstorage/v3/qos/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/qos/testing/fixtures_test.go @@ -47,7 +47,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "qos_specs": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", @@ -110,7 +110,7 @@ func MockListResponse(t *testing.T) { } `, th.Server.URL) case "2": - fmt.Fprintf(w, `{ "qos_specs": [] }`) + fmt.Fprint(w, `{ "qos_specs": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -124,7 +124,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "qos_specs": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", @@ -172,7 +172,7 @@ func MockUpdateResponse(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateBody) + fmt.Fprint(w, UpdateBody) }) } @@ -219,7 +219,7 @@ func MockListAssociationsResponse(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "qos_associations": [ { diff --git a/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go b/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go index df029ff8e1..103afef6ef 100644 --- a/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/quotasets/testing/fixtures_test.go @@ -164,7 +164,7 @@ func HandleSuccessfulRequest(t *testing.T, httpMethod, uriPath, jsonOutput strin th.TestFormValues(t, r, uriQueryParams) } - fmt.Fprintf(w, jsonOutput) + fmt.Fprint(w, jsonOutput) }) } diff --git a/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go b/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go index 0a1286e862..18462bfb20 100644 --- a/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/schedulerstats/testing/fixtures_test.go @@ -104,9 +104,9 @@ func HandleStoragePoolsListSuccessfully(t *testing.T) { t.Errorf("Failed to parse request form %v", err) } if r.FormValue("detail") == "true" { - fmt.Fprintf(w, StoragePoolsListBodyDetail) + fmt.Fprint(w, StoragePoolsListBodyDetail) } else { - fmt.Fprintf(w, StoragePoolsListBody) + fmt.Fprint(w, StoragePoolsListBody) } }) } diff --git a/openstack/blockstorage/v3/services/testing/fixtures_test.go b/openstack/blockstorage/v3/services/testing/fixtures_test.go index adf7993df7..63771ff032 100644 --- a/openstack/blockstorage/v3/services/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/services/testing/fixtures_test.go @@ -92,6 +92,6 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ServiceListBody) + fmt.Fprint(w, ServiceListBody) }) } diff --git a/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go b/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go index 8bbc45e77d..25e54b9b3e 100644 --- a/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/snapshots/testing/fixtures_test.go @@ -54,7 +54,7 @@ func MockListResponse(t *testing.T) { } `, th.Server.URL) case "1": - fmt.Fprintf(w, `{"snapshots": []}`) + fmt.Fprint(w, `{"snapshots": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -69,7 +69,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "snapshot": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", @@ -104,7 +104,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "snapshot": { "volume_id": "1234", @@ -135,7 +135,7 @@ func MockUpdateMetadataResponse(t *testing.T) { } `) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "metadata": { "key": "v1" @@ -162,7 +162,7 @@ func MockUpdateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "snapshot": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", diff --git a/openstack/blockstorage/v3/transfers/testing/fixtures_test.go b/openstack/blockstorage/v3/transfers/testing/fixtures_test.go index b2c11f60c7..859e454eca 100644 --- a/openstack/blockstorage/v3/transfers/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/transfers/testing/fixtures_test.go @@ -167,7 +167,7 @@ func HandleCreateTransfer(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) } @@ -179,7 +179,7 @@ func HandleAcceptTransfer(t *testing.T) { th.TestJSONRequest(t, r, AcceptTransferRequest) w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, AcceptTransferResponse) + fmt.Fprint(w, AcceptTransferResponse) }) } @@ -200,7 +200,7 @@ func HandleListTransfers(t *testing.T) { th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -211,6 +211,6 @@ func HandleGetTransfer(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/blockstorage/v3/volumes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go index 4efb9e08f4..9b4630a978 100644 --- a/openstack/blockstorage/v3/volumes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumes/testing/fixtures_test.go @@ -95,7 +95,7 @@ func MockListResponse(t *testing.T) { } `, th.Server.URL) case "1": - fmt.Fprintf(w, `{"volumes": []}`) + fmt.Fprint(w, `{"volumes": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -109,7 +109,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume": { "volume_type": "lvmdriver-1", @@ -171,7 +171,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume": { "size": 75, @@ -211,7 +211,7 @@ func MockUpdateResponse(t *testing.T) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume": { "name": "vol-002" @@ -239,7 +239,7 @@ func MockCreateVolumeFromBackupResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume": { "size": 30, @@ -287,7 +287,7 @@ func MockAttachResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -307,7 +307,7 @@ func MockBeginDetachingResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -327,7 +327,7 @@ func MockDetachResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -352,7 +352,7 @@ func MockUploadImageResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "os-volume_upload_image": { "container_format": "bare", @@ -399,7 +399,7 @@ func MockReserveResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -419,7 +419,7 @@ func MockUnreserveResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -450,7 +450,7 @@ func MockInitializeConnectionResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "connection_info": { "data": { "target_portals": [ @@ -507,7 +507,7 @@ func MockTerminateConnectionResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -530,7 +530,7 @@ func MockExtendSizeResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -561,7 +561,7 @@ func MockSetImageMetadataResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } @@ -624,7 +624,7 @@ func MockChangeTypeResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go index e1db5dfbd4..c7eb37893e 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go @@ -58,7 +58,7 @@ func MockListResponse(t *testing.T) { } `, th.Server.URL) case "1": - fmt.Fprintf(w, `{"volume_types": []}`) + fmt.Fprint(w, `{"volume_types": []}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -72,7 +72,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume_type": { "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", @@ -112,7 +112,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume_type": { "name": "test_type", @@ -143,7 +143,7 @@ func MockUpdateResponse(t *testing.T) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume_type": { "name": "vol-type-002", @@ -203,7 +203,7 @@ func HandleExtraSpecsListSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ExtraSpecsGetBody) + fmt.Fprint(w, ExtraSpecsGetBody) }) } @@ -215,7 +215,7 @@ func HandleExtraSpecGetSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetExtraSpecBody) + fmt.Fprint(w, GetExtraSpecBody) }) } @@ -233,7 +233,7 @@ func HandleExtraSpecsCreateSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ExtraSpecsGetBody) + fmt.Fprint(w, ExtraSpecsGetBody) }) } @@ -248,7 +248,7 @@ func HandleExtraSpecUpdateSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdatedExtraSpecBody) + fmt.Fprint(w, UpdatedExtraSpecBody) }) } @@ -281,7 +281,7 @@ func MockEncryptionCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "encryption": { "volume_type_id": "a5082c24-2a27-43a4-b48e-fcec1240e36b", @@ -324,7 +324,7 @@ func MockEncryptionUpdateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "encryption": { "control_location": "front-end", @@ -345,7 +345,7 @@ func MockEncryptionGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume_type_id": "a5082c24-2a27-43a4-b48e-fcec1240e36b", "control_location": "front-end", @@ -370,7 +370,7 @@ func MockEncryptionGetSpecResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(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 56f22632f9..f222be2395 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -190,7 +190,7 @@ func TestVolumeTypeListAccesses(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume_type_access": [ { diff --git a/openstack/common/extensions/testing/fixtures.go b/openstack/common/extensions/testing/fixtures.go index cfcdbe90e9..91f6a041b1 100644 --- a/openstack/common/extensions/testing/fixtures.go +++ b/openstack/common/extensions/testing/fixtures.go @@ -71,7 +71,7 @@ func HandleListExtensionsSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -85,6 +85,6 @@ func HandleGetExtensionSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/compute/apiversions/testing/fixtures_test.go b/openstack/compute/apiversions/testing/fixtures_test.go index c7f4b1a977..49162c77ce 100644 --- a/openstack/compute/apiversions/testing/fixtures_test.go +++ b/openstack/compute/apiversions/testing/fixtures_test.go @@ -169,7 +169,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NovaAllAPIVersionsResponse) + fmt.Fprint(w, NovaAllAPIVersionsResponse) }) } @@ -181,7 +181,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NovaAPIVersionResponse_21) + fmt.Fprint(w, NovaAPIVersionResponse_21) }) } @@ -193,6 +193,6 @@ func MockGetMultipleResponses(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NovaAPIInvalidVersionResponse) + fmt.Fprint(w, NovaAPIInvalidVersionResponse) }) } diff --git a/openstack/compute/v2/aggregates/testing/fixtures_test.go b/openstack/compute/v2/aggregates/testing/fixtures_test.go index a67678404f..11cebc8915 100644 --- a/openstack/compute/v2/aggregates/testing/fixtures_test.go +++ b/openstack/compute/v2/aggregates/testing/fixtures_test.go @@ -264,7 +264,7 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, AggregateListBody) + fmt.Fprint(w, AggregateListBody) }) } @@ -274,7 +274,7 @@ func HandleCreateSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, AggregateCreateBody) + fmt.Fprint(w, AggregateCreateBody) }) } @@ -295,7 +295,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, AggregateGetBody) + fmt.Fprint(w, AggregateGetBody) }) } @@ -306,7 +306,7 @@ func HandleUpdateSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, AggregateUpdateBody) + fmt.Fprint(w, AggregateUpdateBody) }) } @@ -317,7 +317,7 @@ func HandleAddHostSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, AggregateAddHostBody) + fmt.Fprint(w, AggregateAddHostBody) }) } @@ -328,7 +328,7 @@ func HandleRemoveHostSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, AggregateRemoveHostBody) + fmt.Fprint(w, AggregateRemoveHostBody) }) } @@ -339,6 +339,6 @@ func HandleSetMetadataSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, AggregateSetMetadataBody) + fmt.Fprint(w, AggregateSetMetadataBody) }) } diff --git a/openstack/compute/v2/attachinterfaces/testing/fixtures_test.go b/openstack/compute/v2/attachinterfaces/testing/fixtures_test.go index 77bc48f55e..59a03d57d2 100644 --- a/openstack/compute/v2/attachinterfaces/testing/fixtures_test.go +++ b/openstack/compute/v2/attachinterfaces/testing/fixtures_test.go @@ -69,7 +69,7 @@ func HandleInterfaceListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "interfaceAttachments": [ { "port_state":"ACTIVE", @@ -99,7 +99,7 @@ func HandleInterfaceGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "interfaceAttachment": { "port_state":"ACTIVE", @@ -133,7 +133,7 @@ func HandleInterfaceCreateSuccessfully(t *testing.T) { }`) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "interfaceAttachment": { "port_state":"ACTIVE", diff --git a/openstack/compute/v2/availabilityzones/testing/fixtures_test.go b/openstack/compute/v2/availabilityzones/testing/fixtures_test.go index 059364bcc0..a36c64649c 100644 --- a/openstack/compute/v2/availabilityzones/testing/fixtures_test.go +++ b/openstack/compute/v2/availabilityzones/testing/fixtures_test.go @@ -180,7 +180,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -192,6 +192,6 @@ func HandleGetDetailSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetDetailOutput) + fmt.Fprint(w, GetDetailOutput) }) } diff --git a/openstack/compute/v2/extensions/testing/fixtures_test.go b/openstack/compute/v2/extensions/testing/fixtures_test.go index 6827dab20c..b1622af764 100644 --- a/openstack/compute/v2/extensions/testing/fixtures_test.go +++ b/openstack/compute/v2/extensions/testing/fixtures_test.go @@ -16,7 +16,7 @@ func HandleListExtensionsSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "extensions": [ { @@ -41,7 +41,7 @@ func HandleGetExtensionsSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "extension": { "updated": "2013-02-03T10:00:00-00:00", diff --git a/openstack/compute/v2/flavors/testing/fixtures_test.go b/openstack/compute/v2/flavors/testing/fixtures_test.go index 94ac8c8a59..a92cdcd1d8 100644 --- a/openstack/compute/v2/flavors/testing/fixtures_test.go +++ b/openstack/compute/v2/flavors/testing/fixtures_test.go @@ -57,7 +57,7 @@ func HandleExtraSpecsListSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ExtraSpecsGetBody) + fmt.Fprint(w, ExtraSpecsGetBody) }) } @@ -69,7 +69,7 @@ func HandleExtraSpecGetSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetExtraSpecBody) + fmt.Fprint(w, GetExtraSpecBody) }) } @@ -87,7 +87,7 @@ func HandleExtraSpecsCreateSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ExtraSpecsGetBody) + fmt.Fprint(w, ExtraSpecsGetBody) }) } @@ -102,7 +102,7 @@ func HandleExtraSpecUpdateSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdatedExtraSpecBody) + fmt.Fprint(w, UpdatedExtraSpecBody) }) } diff --git a/openstack/compute/v2/flavors/testing/requests_test.go b/openstack/compute/v2/flavors/testing/requests_test.go index d22e3dfa9c..efd6e4bcee 100644 --- a/openstack/compute/v2/flavors/testing/requests_test.go +++ b/openstack/compute/v2/flavors/testing/requests_test.go @@ -76,7 +76,7 @@ func TestListFlavors(t *testing.T) { } `, th.Server.URL) case "2": - fmt.Fprintf(w, `{ "flavors": [] }`) + fmt.Fprint(w, `{ "flavors": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -121,7 +121,7 @@ func TestGetFlavor(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "flavor": { "id": "1", @@ -170,7 +170,7 @@ func TestCreateFlavor(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "flavor": { "id": "1", @@ -225,7 +225,7 @@ func TestUpdateFlavor(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "flavor": { "id": "1", @@ -288,7 +288,7 @@ func TestFlavorAccessesList(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "flavor_access": [ { @@ -336,7 +336,7 @@ func TestFlavorAccessAdd(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "flavor_access": [ { @@ -385,7 +385,7 @@ func TestFlavorAccessRemove(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "flavor_access": [] } diff --git a/openstack/compute/v2/hypervisors/testing/fixtures_test.go b/openstack/compute/v2/hypervisors/testing/fixtures_test.go index 6e4b2e4aff..fa0b93a3f8 100644 --- a/openstack/compute/v2/hypervisors/testing/fixtures_test.go +++ b/openstack/compute/v2/hypervisors/testing/fixtures_test.go @@ -605,7 +605,7 @@ func HandleHypervisorsStatisticsSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, HypervisorsStatisticsBody) + fmt.Fprint(w, HypervisorsStatisticsBody) }) } @@ -615,7 +615,7 @@ func HandleHypervisorListPre253Successfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, HypervisorListBodyPre253) + fmt.Fprint(w, HypervisorListBodyPre253) }) } @@ -625,7 +625,7 @@ func HandleHypervisorListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, HypervisorListBody) + fmt.Fprint(w, HypervisorListBody) }) } @@ -638,7 +638,7 @@ func HandleHypervisorListWithParametersSuccessfully(t *testing.T) { }) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, HypervisorListWithParametersBody) + fmt.Fprint(w, HypervisorListWithParametersBody) }) } @@ -648,7 +648,7 @@ func HandleHypervisorGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, HypervisorGetBody) + fmt.Fprint(w, HypervisorGetBody) }) } @@ -658,7 +658,7 @@ func HandleHypervisorGetEmptyCPUInfoSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, HypervisorGetEmptyCPUInfoBody) + fmt.Fprint(w, HypervisorGetEmptyCPUInfoBody) }) } @@ -668,7 +668,7 @@ func HandleHypervisorAfterV287ResponseSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, HypervisorAfterV287ResponseBody) + fmt.Fprint(w, HypervisorAfterV287ResponseBody) }) } @@ -678,6 +678,6 @@ func HandleHypervisorUptimeSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, HypervisorUptimeBody) + fmt.Fprint(w, HypervisorUptimeBody) }) } diff --git a/openstack/compute/v2/instanceactions/testing/fixtures_test.go b/openstack/compute/v2/instanceactions/testing/fixtures_test.go index 3b24801c38..cbbf6def28 100644 --- a/openstack/compute/v2/instanceactions/testing/fixtures_test.go +++ b/openstack/compute/v2/instanceactions/testing/fixtures_test.go @@ -40,7 +40,7 @@ func HandleInstanceActionListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "instanceActions": [ { "action": "stop", @@ -100,7 +100,7 @@ func HandleInstanceActionGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "instanceAction": { "action": "stop", diff --git a/openstack/compute/v2/keypairs/testing/fixtures_test.go b/openstack/compute/v2/keypairs/testing/fixtures_test.go index 41532c03cc..92460e9259 100644 --- a/openstack/compute/v2/keypairs/testing/fixtures_test.go +++ b/openstack/compute/v2/keypairs/testing/fixtures_test.go @@ -157,7 +157,7 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -169,11 +169,11 @@ func HandleGetSuccessfully(t *testing.T) { if r.URL.Query().Get("user_id") == "fake2" { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutputOtherUser) + fmt.Fprint(w, GetOutputOtherUser) } else { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) } }) @@ -188,7 +188,7 @@ func HandleCreateSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, `{ "keypair": { "name": "createdkey" } }`) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutput) + fmt.Fprint(w, CreateOutput) }) } @@ -201,7 +201,7 @@ func HandleCreateSuccessfullyOtherUser(t *testing.T) { th.TestJSONRequest(t, r, `{ "keypair": { "name": "createdkey", "user_id": "fake2" } }`) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutputOtherUser) + fmt.Fprint(w, CreateOutputOtherUser) }) } @@ -221,7 +221,7 @@ func HandleImportSuccessfully(t *testing.T) { `) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ImportOutput) + fmt.Fprint(w, ImportOutput) }) } diff --git a/openstack/compute/v2/limits/testing/fixtures_test.go b/openstack/compute/v2/limits/testing/fixtures_test.go index 3751c2d8b6..ee04c72442 100644 --- a/openstack/compute/v2/limits/testing/fixtures_test.go +++ b/openstack/compute/v2/limits/testing/fixtures_test.go @@ -75,6 +75,6 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/compute/v2/quotasets/testing/fixtures_test.go b/openstack/compute/v2/quotasets/testing/fixtures_test.go index d1c714714b..5d0ef24931 100644 --- a/openstack/compute/v2/quotasets/testing/fixtures_test.go +++ b/openstack/compute/v2/quotasets/testing/fixtures_test.go @@ -167,7 +167,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -177,7 +177,7 @@ func HandleGetDetailSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetDetailsOutput) + fmt.Fprint(w, GetDetailsOutput) }) } @@ -188,7 +188,7 @@ func HandlePutSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, UpdateOutput) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } @@ -199,7 +199,7 @@ func HandlePartialPutSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestJSONRequest(t, r, PartialUpdateBody) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/compute/v2/remoteconsoles/testing/requests_test.go b/openstack/compute/v2/remoteconsoles/testing/requests_test.go index e96b897d0f..3a49e4d46f 100644 --- a/openstack/compute/v2/remoteconsoles/testing/requests_test.go +++ b/openstack/compute/v2/remoteconsoles/testing/requests_test.go @@ -25,7 +25,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, RemoteConsoleCreateResult) + fmt.Fprint(w, RemoteConsoleCreateResult) }) opts := remoteconsoles.CreateOpts{ diff --git a/openstack/compute/v2/secgroups/testing/fixtures_test.go b/openstack/compute/v2/secgroups/testing/fixtures_test.go index 0b35cc71d8..df3614b66b 100644 --- a/openstack/compute/v2/secgroups/testing/fixtures_test.go +++ b/openstack/compute/v2/secgroups/testing/fixtures_test.go @@ -33,7 +33,7 @@ func mockListGroupsResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, listGroupsJSON) + fmt.Fprint(w, listGroupsJSON) }) } @@ -46,7 +46,7 @@ func mockListGroupsByServerResponse(t *testing.T, serverID string) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, listGroupsJSON) + fmt.Fprint(w, listGroupsJSON) }) } @@ -67,7 +67,7 @@ func mockCreateGroupResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group": { "description": "something", @@ -99,7 +99,7 @@ func mockUpdateGroupResponse(t *testing.T, groupID string) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group": { "description": "something", @@ -122,7 +122,7 @@ func mockGetGroupsResponse(t *testing.T, groupID string) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group": { "description": "default", @@ -224,7 +224,7 @@ func mockAddRuleResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group_rule": { "from_port": 22, @@ -260,7 +260,7 @@ func mockAddRuleResponseICMPZero(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group_rule": { "from_port": 0, diff --git a/openstack/compute/v2/servergroups/testing/fixtures_test.go b/openstack/compute/v2/servergroups/testing/fixtures_test.go index de36c3ff89..2ad519023a 100644 --- a/openstack/compute/v2/servergroups/testing/fixtures_test.go +++ b/openstack/compute/v2/servergroups/testing/fixtures_test.go @@ -148,7 +148,7 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -160,7 +160,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -172,7 +172,7 @@ func HandleGetMicroversionSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutputMicroversion) + fmt.Fprint(w, GetOutputMicroversion) }) } @@ -194,7 +194,7 @@ func HandleCreateSuccessfully(t *testing.T) { `) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutput) + fmt.Fprint(w, CreateOutput) }) } @@ -220,7 +220,7 @@ func HandleCreateMicroversionSuccessfully(t *testing.T) { `) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutputMicroversion) + fmt.Fprint(w, CreateOutputMicroversion) }) } diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index 2ce6282b02..1c25b40074 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -741,7 +741,7 @@ func HandleServerNoNetworkCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -761,7 +761,7 @@ func HandleServerCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) th.Mux.HandleFunc("/images/detail", func(w http.ResponseWriter, r *http.Request) { @@ -775,7 +775,7 @@ func HandleServerCreationSuccessfully(t *testing.T, response string) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "images": [ { @@ -804,7 +804,7 @@ func HandleServerCreationSuccessfully(t *testing.T, response string) { } `) case "2": - fmt.Fprintf(w, `{ "images": [] }`) + fmt.Fprint(w, `{ "images": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -850,7 +850,7 @@ func HandleServerCreationSuccessfully(t *testing.T, response string) { } `, th.Server.URL) case "2": - fmt.Fprintf(w, `{ "flavors": [] }`) + fmt.Fprint(w, `{ "flavors": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -875,7 +875,7 @@ func HandleServersCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -896,7 +896,7 @@ func HandleServerCreationWithCustomFieldSuccessfully(t *testing.T, response stri w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -915,7 +915,7 @@ func HandleServerCreationWithHostname(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -936,7 +936,7 @@ func HandleServerCreationWithUserdata(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -959,7 +959,7 @@ func HandleServerCreationWithMetadata(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -976,9 +976,9 @@ func HandleServerListSimpleSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, ServerListBody) + fmt.Fprint(w, ServerListBody) case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": - fmt.Fprintf(w, `{ "servers": [] }`) + fmt.Fprint(w, `{ "servers": [] }`) default: t.Fatalf("/servers invoked with unexpected marker=[%s]", marker) } @@ -998,9 +998,9 @@ func HandleServerListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, ServerListBody) + fmt.Fprint(w, ServerListBody) case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": - fmt.Fprintf(w, `{ "servers": [] }`) + fmt.Fprint(w, `{ "servers": [] }`) default: t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker) } @@ -1036,7 +1036,7 @@ func HandleServerGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleServerBody) + fmt.Fprint(w, SingleServerBody) }) } @@ -1048,7 +1048,7 @@ func HandleServerGetFaultSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, FaultyServerBody) + fmt.Fprint(w, FaultyServerBody) }) } @@ -1061,7 +1061,7 @@ func HandleServerUpdateSuccessfully(t *testing.T) { th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{ "server": { "name": "new-name" } }`) - fmt.Fprintf(w, SingleServerBody) + fmt.Fprint(w, SingleServerBody) }) } @@ -1097,7 +1097,7 @@ func HandleShowConsoleOutputSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -1119,7 +1119,7 @@ func HandleRebuildSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -1243,7 +1243,7 @@ func HandleAddressListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "addresses": { "public": [ { @@ -1285,7 +1285,7 @@ func HandleNetworkAddressListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "public": [ { "version": 4, @@ -1317,7 +1317,7 @@ func HandlePasswordGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, ServerPasswordBody) + fmt.Fprint(w, ServerPasswordBody) }) } @@ -1332,6 +1332,6 @@ func HandleServerWithTagsCreationSuccessfully(t *testing.T) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, SingleServerWithTagsBody) + fmt.Fprint(w, SingleServerWithTagsBody) }) } diff --git a/openstack/compute/v2/services/testing/fixtures_test.go b/openstack/compute/v2/services/testing/fixtures_test.go index 2104dc08cb..93b124df90 100644 --- a/openstack/compute/v2/services/testing/fixtures_test.go +++ b/openstack/compute/v2/services/testing/fixtures_test.go @@ -259,7 +259,7 @@ func HandleListPre253Successfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ServiceListBodyPre253) + fmt.Fprint(w, ServiceListBodyPre253) }) } @@ -271,7 +271,7 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ServiceListBody) + fmt.Fprint(w, ServiceListBody) }) } @@ -285,7 +285,7 @@ func HandleUpdateSuccessfully(t *testing.T) { th.TestHeader(t, r, "Content-Type", "application/json") th.TestJSONRequest(t, r, `{"status": "disabled"}`) - fmt.Fprintf(w, ServiceUpdate) + fmt.Fprint(w, ServiceUpdate) }) } diff --git a/openstack/compute/v2/tags/testing/requests_test.go b/openstack/compute/v2/tags/testing/requests_test.go index 03f4260a7d..dc524a2306 100644 --- a/openstack/compute/v2/tags/testing/requests_test.go +++ b/openstack/compute/v2/tags/testing/requests_test.go @@ -22,7 +22,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, TagsListResponse) + _, err := fmt.Fprint(w, TagsListResponse) th.AssertNoErr(t, err) }) @@ -78,7 +78,7 @@ func TestReplaceAll(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, TagsReplaceAllResponse) + _, err := fmt.Fprint(w, TagsReplaceAllResponse) th.AssertNoErr(t, err) }) diff --git a/openstack/compute/v2/volumeattach/testing/fixtures_test.go b/openstack/compute/v2/volumeattach/testing/fixtures_test.go index c5f0435a96..d9ada1e8c1 100644 --- a/openstack/compute/v2/volumeattach/testing/fixtures_test.go +++ b/openstack/compute/v2/volumeattach/testing/fixtures_test.go @@ -62,7 +62,7 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -74,7 +74,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -96,7 +96,7 @@ func HandleCreateSuccessfully(t *testing.T) { `) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutput) + fmt.Fprint(w, CreateOutput) }) } diff --git a/openstack/container/v1/capsules/testing/fixtures_test.go b/openstack/container/v1/capsules/testing/fixtures_test.go index fe7b008013..779a41a0df 100644 --- a/openstack/container/v1/capsules/testing/fixtures_test.go +++ b/openstack/container/v1/capsules/testing/fixtures_test.go @@ -609,7 +609,7 @@ func HandleCapsuleGetOldTimeSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CapsuleGetBody_OldTime) + fmt.Fprint(w, CapsuleGetBody_OldTime) }) } @@ -621,7 +621,7 @@ func HandleCapsuleGetNewTimeSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CapsuleGetBody_NewTime) + fmt.Fprint(w, CapsuleGetBody_NewTime) }) } @@ -633,7 +633,7 @@ func HandleCapsuleCreateSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, CapsuleGetBody_NewTime) + fmt.Fprint(w, CapsuleGetBody_NewTime) }) } @@ -645,7 +645,7 @@ func HandleCapsuleListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, CapsuleListBody) + fmt.Fprint(w, CapsuleListBody) }) } @@ -657,7 +657,7 @@ func HandleCapsuleV132ListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, CapsuleV132ListBody) + fmt.Fprint(w, CapsuleV132ListBody) }) } diff --git a/openstack/containerinfra/apiversions/testing/fixtures_test.go b/openstack/containerinfra/apiversions/testing/fixtures_test.go index e95058151d..f42844214c 100644 --- a/openstack/containerinfra/apiversions/testing/fixtures_test.go +++ b/openstack/containerinfra/apiversions/testing/fixtures_test.go @@ -71,7 +71,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, MagnumAllAPIVersionsResponse) + fmt.Fprint(w, MagnumAllAPIVersionsResponse) }) } @@ -83,6 +83,6 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, MagnumAPIVersionResponse) + fmt.Fprint(w, MagnumAPIVersionResponse) }) } diff --git a/openstack/containerinfra/v1/certificates/testing/fixtures_test.go b/openstack/containerinfra/v1/certificates/testing/fixtures_test.go index 3f88c34242..b51d77cbd4 100644 --- a/openstack/containerinfra/v1/certificates/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/certificates/testing/fixtures_test.go @@ -106,6 +106,6 @@ func HandleUpdateCertificateSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) } diff --git a/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go b/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go index 51e63cc318..90de8b6fe6 100644 --- a/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go +++ b/openstack/containerinfra/v1/nodegroups/testing/fixtures_test.go @@ -221,7 +221,7 @@ func handleCreateNodeGroupSuccess(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, nodeGroupCreateResponse) + fmt.Fprint(w, nodeGroupCreateResponse) }) } @@ -233,7 +233,7 @@ func handleCreateNodeGroupDuplicate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) - fmt.Fprintf(w, nodeGroupCreateDuplicateResponse) + fmt.Fprint(w, nodeGroupCreateDuplicateResponse) }) } @@ -245,7 +245,7 @@ func handleCreateNodeGroupMaster(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, nodeGroupCreateMasterResponse) + fmt.Fprint(w, nodeGroupCreateMasterResponse) }) } @@ -257,7 +257,7 @@ func handleCreateNodeGroupBadSizes(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) - fmt.Fprintf(w, nodeGroupCreateBadSizesResponse) + fmt.Fprint(w, nodeGroupCreateBadSizesResponse) }) } @@ -269,7 +269,7 @@ func handleUpdateNodeGroupSuccess(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, nodeGroupUpdateResponse) + fmt.Fprint(w, nodeGroupUpdateResponse) }) } @@ -281,7 +281,7 @@ func handleUpdateNodeGroupInternal(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, nodeGroupUpdateInternalResponse) + fmt.Fprint(w, nodeGroupUpdateInternalResponse) }) } @@ -293,7 +293,7 @@ func handleUpdateNodeGroupBadField(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, nodeGroupUpdateBadFieldResponse) + fmt.Fprint(w, nodeGroupUpdateBadFieldResponse) }) } @@ -305,7 +305,7 @@ func handleUpdateNodeGroupBadMin(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusConflict) - fmt.Fprintf(w, nodeGroupUpdateBadMinResponse) + fmt.Fprint(w, nodeGroupUpdateBadMinResponse) }) } @@ -326,7 +326,7 @@ func handleDeleteNodeGroupNotFound(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) - fmt.Fprintf(w, nodeGroupDeleteNotFoundResponse) + fmt.Fprint(w, nodeGroupDeleteNotFoundResponse) }) } @@ -338,7 +338,7 @@ func handleDeleteNodeGroupClusterNotFound(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) - fmt.Fprintf(w, nodeGroupDeleteClusterNotFoundResponse) + fmt.Fprint(w, nodeGroupDeleteClusterNotFoundResponse) }) } @@ -350,7 +350,7 @@ func handleDeleteNodeGroupDefault(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, nodeGroupDeleteDefaultResponse) + fmt.Fprint(w, nodeGroupDeleteDefaultResponse) }) } diff --git a/openstack/dns/v2/recordsets/testing/fixtures_test.go b/openstack/dns/v2/recordsets/testing/fixtures_test.go index 9d2289f100..71d1d0898e 100644 --- a/openstack/dns/v2/recordsets/testing/fixtures_test.go +++ b/openstack/dns/v2/recordsets/testing/fixtures_test.go @@ -212,9 +212,9 @@ func HandleListByZoneSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "f7b10e9b-0cae-4a91-b162-562bc6096648": - fmt.Fprintf(w, ListByZoneOutputLimited) + fmt.Fprint(w, ListByZoneOutputLimited) case "": - fmt.Fprintf(w, ListByZoneOutput) + fmt.Fprint(w, ListByZoneOutput) } }) } @@ -227,7 +227,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -282,7 +282,7 @@ func HandleCreateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateRecordSetResponse) + fmt.Fprint(w, CreateRecordSetResponse) }) } @@ -334,7 +334,7 @@ func HandleUpdateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, UpdateRecordSetResponse) + fmt.Fprint(w, UpdateRecordSetResponse) }) } @@ -373,6 +373,6 @@ func HandleDeleteSuccessfully(t *testing.T) { w.WriteHeader(http.StatusAccepted) //w.Header().Add("Content-Type", "application/json") - //fmt.Fprintf(w, DeleteZoneResponse) + //fmt.Fprint(w, DeleteZoneResponse) }) } diff --git a/openstack/dns/v2/transfer/accept/testing/fixtures_test.go b/openstack/dns/v2/transfer/accept/testing/fixtures_test.go index b8c3fe5888..3dae54e6aa 100644 --- a/openstack/dns/v2/transfer/accept/testing/fixtures_test.go +++ b/openstack/dns/v2/transfer/accept/testing/fixtures_test.go @@ -133,7 +133,7 @@ func HandleListSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -145,7 +145,7 @@ func HandleFilteredListSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, FilteredListOutput) + fmt.Fprint(w, FilteredListOutput) }) } @@ -157,7 +157,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -200,6 +200,6 @@ func HandleCreateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateTransferAcceptResponse) + fmt.Fprint(w, CreateTransferAcceptResponse) }) } diff --git a/openstack/dns/v2/transfer/request/testing/fixtures_test.go b/openstack/dns/v2/transfer/request/testing/fixtures_test.go index 758b566604..3de1f2aca3 100644 --- a/openstack/dns/v2/transfer/request/testing/fixtures_test.go +++ b/openstack/dns/v2/transfer/request/testing/fixtures_test.go @@ -119,7 +119,7 @@ func HandleListSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -131,7 +131,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -175,7 +175,7 @@ func HandleCreateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateTransferRequestResponse) + fmt.Fprint(w, CreateTransferRequestResponse) }) } @@ -215,7 +215,7 @@ func HandleUpdateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, UpdatedTransferRequestResponse) + fmt.Fprint(w, UpdatedTransferRequestResponse) }) } diff --git a/openstack/dns/v2/zones/testing/fixtures_test.go b/openstack/dns/v2/zones/testing/fixtures_test.go index d7d9120643..6b0ee7ccf7 100644 --- a/openstack/dns/v2/zones/testing/fixtures_test.go +++ b/openstack/dns/v2/zones/testing/fixtures_test.go @@ -149,7 +149,7 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -160,7 +160,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -212,7 +212,7 @@ func HandleCreateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateZoneResponse) + fmt.Fprint(w, CreateZoneResponse) }) } @@ -259,7 +259,7 @@ func HandleUpdateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, UpdateZoneResponse) + fmt.Fprint(w, UpdateZoneResponse) }) } @@ -297,6 +297,6 @@ func HandleDeleteSuccessfully(t *testing.T) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, DeleteZoneResponse) + fmt.Fprint(w, DeleteZoneResponse) }) } diff --git a/openstack/identity/v2/extensions/testing/fixtures_test.go b/openstack/identity/v2/extensions/testing/fixtures_test.go index 3733eba312..8e9bfa14da 100644 --- a/openstack/identity/v2/extensions/testing/fixtures_test.go +++ b/openstack/identity/v2/extensions/testing/fixtures_test.go @@ -37,7 +37,7 @@ func HandleListExtensionsSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "extensions": { "values": [ diff --git a/openstack/identity/v2/roles/testing/fixtures_test.go b/openstack/identity/v2/roles/testing/fixtures_test.go index 0b5af6e541..170e80099c 100644 --- a/openstack/identity/v2/roles/testing/fixtures_test.go +++ b/openstack/identity/v2/roles/testing/fixtures_test.go @@ -17,7 +17,7 @@ func MockListRoleResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "roles": [ { diff --git a/openstack/identity/v2/tenants/testing/fixtures_test.go b/openstack/identity/v2/tenants/testing/fixtures_test.go index 7c50152faa..e7de74ccd5 100644 --- a/openstack/identity/v2/tenants/testing/fixtures_test.go +++ b/openstack/identity/v2/tenants/testing/fixtures_test.go @@ -59,7 +59,7 @@ func HandleListTenantsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -81,7 +81,7 @@ func mockCreateTenantResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "tenant": { "name": "new_tenant", @@ -120,7 +120,7 @@ func mockUpdateTenantResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "tenant": { "name": "new_name", @@ -141,7 +141,7 @@ func mockGetTenantResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "tenant": { "name": "new_tenant", diff --git a/openstack/identity/v2/tokens/testing/fixtures_test.go b/openstack/identity/v2/tokens/testing/fixtures_test.go index 8eb7d6f7ea..1ee6d32e75 100644 --- a/openstack/identity/v2/tokens/testing/fixtures_test.go +++ b/openstack/identity/v2/tokens/testing/fixtures_test.go @@ -152,7 +152,7 @@ func HandleTokenPost(t *testing.T, requestJSON string) { } w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, TokenCreationResponse) + fmt.Fprint(w, TokenCreationResponse) }) } @@ -165,7 +165,7 @@ func HandleTokenGet(t *testing.T, token string) { th.TestHeader(t, r, "X-Auth-Token", thclient.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, TokenGetResponse) + fmt.Fprint(w, TokenGetResponse) }) } diff --git a/openstack/identity/v2/users/testing/fixtures_test.go b/openstack/identity/v2/users/testing/fixtures_test.go index f38f05dba2..3e5285360b 100644 --- a/openstack/identity/v2/users/testing/fixtures_test.go +++ b/openstack/identity/v2/users/testing/fixtures_test.go @@ -17,7 +17,7 @@ func MockListUserResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "users":[ { @@ -61,7 +61,7 @@ func mockCreateUserResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "user": { "name": "new_user", @@ -83,7 +83,7 @@ func mockGetUserResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "user": { "name": "new_user", @@ -115,7 +115,7 @@ func mockUpdateUserResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "user": { "name": "new_name", @@ -145,7 +145,7 @@ func mockListRolesResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "roles": [ { diff --git a/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go b/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go index fabb021a0c..ae06c1bc57 100644 --- a/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go +++ b/openstack/identity/v3/applicationcredentials/testing/fixtures_test.go @@ -415,7 +415,7 @@ func HandleListApplicationCredentialsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -429,7 +429,7 @@ func HandleGetApplicationCredentialSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -442,7 +442,7 @@ func HandleCreateApplicationCredentialSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) } @@ -455,7 +455,7 @@ func HandleCreateNoSecretApplicationCredentialSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateNoSecretRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateNoSecretResponse) + fmt.Fprint(w, CreateNoSecretResponse) }) } @@ -466,7 +466,7 @@ func HandleCreateUnrestrictedApplicationCredentialSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateUnrestrictedRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateUnrestrictedResponse) + fmt.Fprint(w, CreateUnrestrictedResponse) }) } @@ -491,7 +491,7 @@ func HandleListAccessRulesSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAccessRulesOutput) + fmt.Fprint(w, ListAccessRulesOutput) }) } @@ -505,7 +505,7 @@ func HandleGetAccessRuleSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetAccessRuleOutput) + fmt.Fprint(w, GetAccessRuleOutput) }) } diff --git a/openstack/identity/v3/catalog/testing/fixtures_test.go b/openstack/identity/v3/catalog/testing/fixtures_test.go index 541fcac4af..ad9e9d9ce2 100644 --- a/openstack/identity/v3/catalog/testing/fixtures_test.go +++ b/openstack/identity/v3/catalog/testing/fixtures_test.go @@ -85,6 +85,6 @@ func HandleListCatalogSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, _ = fmt.Fprintf(w, ListOutput) + _, _ = fmt.Fprint(w, ListOutput) }) } diff --git a/openstack/identity/v3/credentials/testing/fixtures_test.go b/openstack/identity/v3/credentials/testing/fixtures_test.go index 8da4769ed6..e32d77d0db 100644 --- a/openstack/identity/v3/credentials/testing/fixtures_test.go +++ b/openstack/identity/v3/credentials/testing/fixtures_test.go @@ -161,7 +161,7 @@ func HandleListCredentialsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -175,7 +175,7 @@ func HandleGetCredentialSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -188,7 +188,7 @@ func HandleCreateCredentialSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -212,6 +212,6 @@ func HandleUpdateCredentialSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/identity/v3/domains/testing/fixtures_test.go b/openstack/identity/v3/domains/testing/fixtures_test.go index f5025a15c8..a35813a23c 100644 --- a/openstack/identity/v3/domains/testing/fixtures_test.go +++ b/openstack/identity/v3/domains/testing/fixtures_test.go @@ -189,7 +189,7 @@ func HandleListAvailableDomainsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAvailableOutput) + fmt.Fprint(w, ListAvailableOutput) }) } @@ -203,7 +203,7 @@ func HandleListDomainsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -217,7 +217,7 @@ func HandleGetDomainSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -230,7 +230,7 @@ func HandleCreateDomainSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -254,6 +254,6 @@ func HandleUpdateDomainSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/identity/v3/ec2credentials/testing/fixtures_test.go b/openstack/identity/v3/ec2credentials/testing/fixtures_test.go index 738161477d..72a82e1ff4 100644 --- a/openstack/identity/v3/ec2credentials/testing/fixtures_test.go +++ b/openstack/identity/v3/ec2credentials/testing/fixtures_test.go @@ -119,7 +119,7 @@ func HandleListEC2CredentialsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -133,7 +133,7 @@ func HandleGetEC2CredentialSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -146,7 +146,7 @@ func HandleCreateEC2CredentialSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) } diff --git a/openstack/identity/v3/ec2tokens/testing/requests_test.go b/openstack/identity/v3/ec2tokens/testing/requests_test.go index 13239e2d91..8fe0b3c3f8 100644 --- a/openstack/identity/v3/ec2tokens/testing/requests_test.go +++ b/openstack/identity/v3/ec2tokens/testing/requests_test.go @@ -32,7 +32,7 @@ func authTokenPost(t *testing.T, options ec2tokens.AuthOptions, requestJSON stri th.TestJSONRequest(t, r, requestJSON) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, tokens_testing.TokenOutput) + fmt.Fprint(w, tokens_testing.TokenOutput) }) expected := &tokens.Token{ diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 8ea857bad4..348a1f1c6e 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -33,7 +33,7 @@ func TestCreateSuccessful(t *testing.T) { `) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "endpoint": { "id": "12", @@ -82,7 +82,7 @@ func TestListEndpoints(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "endpoints": [ { @@ -170,7 +170,7 @@ func TestUpdateEndpoint(t *testing.T) { } `) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "endpoint": { "id": "12", diff --git a/openstack/identity/v3/federation/testing/fixtures_test.go b/openstack/identity/v3/federation/testing/fixtures_test.go index 225cc57997..229d2f489a 100644 --- a/openstack/identity/v3/federation/testing/fixtures_test.go +++ b/openstack/identity/v3/federation/testing/fixtures_test.go @@ -289,7 +289,7 @@ func HandleListMappingsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -302,7 +302,7 @@ func HandleCreateMappingSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateOutput) + fmt.Fprint(w, CreateOutput) }) } @@ -316,7 +316,7 @@ func HandleGetMappingSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -329,7 +329,7 @@ func HandleUpdateMappingSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/identity/v3/groups/testing/fixtures_test.go b/openstack/identity/v3/groups/testing/fixtures_test.go index 0357a1fe06..415d81555f 100644 --- a/openstack/identity/v3/groups/testing/fixtures_test.go +++ b/openstack/identity/v3/groups/testing/fixtures_test.go @@ -160,7 +160,7 @@ func HandleListGroupsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -174,7 +174,7 @@ func HandleGetGroupSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -187,7 +187,7 @@ func HandleCreateGroupSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -200,7 +200,7 @@ func HandleUpdateGroupSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/identity/v3/limits/testing/fixtures_test.go b/openstack/identity/v3/limits/testing/fixtures_test.go index 6275235b54..5f8cbff160 100644 --- a/openstack/identity/v3/limits/testing/fixtures_test.go +++ b/openstack/identity/v3/limits/testing/fixtures_test.go @@ -183,7 +183,7 @@ func HandleGetEnforcementModelSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetEnforcementModelOutput) + fmt.Fprint(w, GetEnforcementModelOutput) }) } @@ -197,7 +197,7 @@ func HandleListLimitsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -210,7 +210,7 @@ func HandleCreateLimitSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateOutput) + fmt.Fprint(w, CreateOutput) }) } @@ -224,7 +224,7 @@ func HandleGetLimitSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -237,7 +237,7 @@ func HandleUpdateLimitSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/identity/v3/oauth1/testing/fixtures_test.go b/openstack/identity/v3/oauth1/testing/fixtures_test.go index 3e8d144047..b8d5dc3327 100644 --- a/openstack/identity/v3/oauth1/testing/fixtures_test.go +++ b/openstack/identity/v3/oauth1/testing/fixtures_test.go @@ -228,7 +228,7 @@ func HandleCreateConsumer(t *testing.T) { th.TestJSONRequest(t, r, CreateConsumerRequest) w.WriteHeader(http.StatusCreated) - _, err := fmt.Fprintf(w, CreateConsumerResponse) + _, err := fmt.Fprint(w, CreateConsumerResponse) th.AssertNoErr(t, err) }) } @@ -243,7 +243,7 @@ func HandleUpdateConsumer(t *testing.T) { th.TestJSONRequest(t, r, UpdateConsumerRequest) w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, UpdateConsumerResponse) + _, err := fmt.Fprint(w, UpdateConsumerResponse) th.AssertNoErr(t, err) }) } @@ -269,7 +269,7 @@ func HandleGetConsumer(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetConsumerResponse) + fmt.Fprint(w, GetConsumerResponse) }) } @@ -306,7 +306,7 @@ func HandleListConsumers(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListConsumersResponse) + fmt.Fprint(w, ListConsumersResponse) }) } @@ -329,7 +329,7 @@ func HandleRequestToken(t *testing.T) { w.Header().Set("Content-Type", oauth1.OAuth1TokenContentType) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `oauth_token=29971f&oauth_token_secret=238eb8&oauth_expires_at=2013-09-11T06:07:51.501805Z`) + fmt.Fprint(w, `oauth_token=29971f&oauth_token_secret=238eb8&oauth_expires_at=2013-09-11T06:07:51.501805Z`) }) } @@ -343,7 +343,7 @@ func HandleAuthorizeToken(t *testing.T) { th.TestJSONRequest(t, r, AuthorizeTokenRequest) w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, AuthorizeTokenResponse) + _, err := fmt.Fprint(w, AuthorizeTokenResponse) th.AssertNoErr(t, err) }) } @@ -366,7 +366,7 @@ func HandleCreateAccessToken(t *testing.T) { w.Header().Set("Content-Type", oauth1.OAuth1TokenContentType) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `oauth_token=accd36&oauth_token_secret=aa47da&oauth_expires_at=2013-09-11T06:07:51.501805Z`) + fmt.Fprint(w, `oauth_token=accd36&oauth_token_secret=aa47da&oauth_expires_at=2013-09-11T06:07:51.501805Z`) }) } @@ -380,7 +380,7 @@ func HandleGetAccessToken(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetUserAccessTokenResponse) + fmt.Fprint(w, GetUserAccessTokenResponse) }) } @@ -405,7 +405,7 @@ func HandleListAccessTokens(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListUserAccessTokensResponse) + fmt.Fprint(w, ListUserAccessTokensResponse) }) } @@ -419,7 +419,7 @@ func HandleListAccessTokenRoles(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListUserAccessTokenRolesResponse) + fmt.Fprint(w, ListUserAccessTokenRolesResponse) }) } @@ -433,7 +433,7 @@ func HandleGetAccessTokenRole(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListUserAccessTokenRoleResponse) + fmt.Fprint(w, ListUserAccessTokenRoleResponse) }) } @@ -449,6 +449,6 @@ func HandleAuthenticate(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, tokens.TokenOutput) + fmt.Fprint(w, tokens.TokenOutput) }) } diff --git a/openstack/identity/v3/policies/testing/fixtures_test.go b/openstack/identity/v3/policies/testing/fixtures_test.go index 2ebc6c6c41..d261ed865c 100644 --- a/openstack/identity/v3/policies/testing/fixtures_test.go +++ b/openstack/identity/v3/policies/testing/fixtures_test.go @@ -164,9 +164,9 @@ func HandleListPoliciesSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) switch r.URL.Query().Get("type") { case "": - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) case "application/json": - fmt.Fprintf(w, ListWithFilterOutput) + fmt.Fprint(w, ListWithFilterOutput) default: w.WriteHeader(http.StatusBadRequest) } @@ -182,7 +182,7 @@ func HandleCreatePolicySuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -197,7 +197,7 @@ func HandleGetPolicySuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }, ) } @@ -212,7 +212,7 @@ func HandleUpdatePolicySuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }, ) } diff --git a/openstack/identity/v3/projectendpoints/testing/requests_test.go b/openstack/identity/v3/projectendpoints/testing/requests_test.go index 9a9c17139f..2e8f6aad4c 100644 --- a/openstack/identity/v3/projectendpoints/testing/requests_test.go +++ b/openstack/identity/v3/projectendpoints/testing/requests_test.go @@ -37,7 +37,7 @@ func TestListEndpoints(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "endpoints": [ { diff --git a/openstack/identity/v3/projects/testing/fixtures_test.go b/openstack/identity/v3/projects/testing/fixtures_test.go index a54edfe21f..568305008a 100644 --- a/openstack/identity/v3/projects/testing/fixtures_test.go +++ b/openstack/identity/v3/projects/testing/fixtures_test.go @@ -285,7 +285,7 @@ func HandleListAvailableProjectsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAvailableOutput) + fmt.Fprint(w, ListAvailableOutput) }) } @@ -299,7 +299,7 @@ func HandleListProjectsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -313,7 +313,7 @@ func HandleGetProjectSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -326,7 +326,7 @@ func HandleCreateProjectSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -350,7 +350,7 @@ func HandleUpdateProjectSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } @@ -360,7 +360,7 @@ func HandleListProjectTagsSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListTagsOutput) + fmt.Fprint(w, ListTagsOutput) }) } @@ -371,7 +371,7 @@ func HandleModifyProjectTagsSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, ModifyProjectTagsRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ModifyProjectTagsOutput) + fmt.Fprint(w, ModifyProjectTagsOutput) }) } func HandleDeleteProjectTagsSuccessfully(t *testing.T) { diff --git a/openstack/identity/v3/regions/testing/fixtures_test.go b/openstack/identity/v3/regions/testing/fixtures_test.go index a4c3a40c38..633ae86dcb 100644 --- a/openstack/identity/v3/regions/testing/fixtures_test.go +++ b/openstack/identity/v3/regions/testing/fixtures_test.go @@ -172,7 +172,7 @@ func HandleListRegionsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -186,7 +186,7 @@ func HandleGetRegionSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -199,7 +199,7 @@ func HandleCreateRegionSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -212,7 +212,7 @@ func HandleUpdateRegionSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/identity/v3/registeredlimits/testing/fixtures_test.go b/openstack/identity/v3/registeredlimits/testing/fixtures_test.go index 37e11ed6eb..1bcfc5e867 100644 --- a/openstack/identity/v3/registeredlimits/testing/fixtures_test.go +++ b/openstack/identity/v3/registeredlimits/testing/fixtures_test.go @@ -163,7 +163,7 @@ func HandleListRegisteredLimitsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -177,7 +177,7 @@ func HandleGetRegisteredLimitSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -190,7 +190,7 @@ func HandleCreateRegisteredLimitSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateOutput) + fmt.Fprint(w, CreateOutput) }) } @@ -214,6 +214,6 @@ func HandleUpdateRegisteredLimitSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/identity/v3/roles/testing/fixtures_test.go b/openstack/identity/v3/roles/testing/fixtures_test.go index 755f14d4e8..32fe745a94 100644 --- a/openstack/identity/v3/roles/testing/fixtures_test.go +++ b/openstack/identity/v3/roles/testing/fixtures_test.go @@ -350,7 +350,7 @@ func HandleListRolesSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -364,7 +364,7 @@ func HandleGetRoleSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -377,7 +377,7 @@ func HandleCreateRoleSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -390,7 +390,7 @@ func HandleUpdateRoleSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } @@ -499,7 +499,7 @@ func HandleListRoleAssignmentsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAssignmentOutput) + fmt.Fprint(w, ListAssignmentOutput) }) } @@ -514,7 +514,7 @@ func HandleListRoleAssignmentsWithNamesSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAssignmentWithNamesOutput) + fmt.Fprint(w, ListAssignmentWithNamesOutput) }) } @@ -529,7 +529,7 @@ func HandleListRoleAssignmentsWithSubtreeSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAssignmentOutput) + fmt.Fprint(w, ListAssignmentOutput) }) } @@ -557,7 +557,7 @@ func HandleListAssignmentsOnResourceSuccessfully_ProjectsUsers(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAssignmentsOnResourceOutput) + fmt.Fprint(w, ListAssignmentsOnResourceOutput) } th.Mux.HandleFunc("/projects/{project_id}/users/{user_id}/roles", fn) @@ -571,7 +571,7 @@ func HandleListAssignmentsOnResourceSuccessfully_ProjectsGroups(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAssignmentsOnResourceOutput) + fmt.Fprint(w, ListAssignmentsOnResourceOutput) } th.Mux.HandleFunc("/projects/{project_id}/groups/{group_id}/roles", fn) @@ -585,7 +585,7 @@ func HandleListAssignmentsOnResourceSuccessfully_DomainsUsers(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAssignmentsOnResourceOutput) + fmt.Fprint(w, ListAssignmentsOnResourceOutput) } th.Mux.HandleFunc("/domains/{domain_id}/users/{user_id}/roles", fn) @@ -599,7 +599,7 @@ func HandleListAssignmentsOnResourceSuccessfully_DomainsGroups(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListAssignmentsOnResourceOutput) + fmt.Fprint(w, ListAssignmentsOnResourceOutput) } th.Mux.HandleFunc("/domains/{domain_id}/groups/{group_id}/roles", fn) @@ -635,7 +635,7 @@ func HandleCreateRoleInferenceRule(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateRoleInferenceRuleOutput) + fmt.Fprint(w, CreateRoleInferenceRuleOutput) } th.Mux.HandleFunc("/roles/7ceab6192ea34a548cc71b24f72e762c/implies/97e2f5d38bc94842bc3da818c16762ed", fn) @@ -713,7 +713,7 @@ func HandleListRoleInferenceRules(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListRoleInferenceRulesOutput) + fmt.Fprint(w, ListRoleInferenceRulesOutput) } th.Mux.HandleFunc("/role_inferences", fn) @@ -737,7 +737,7 @@ func HandleGetRoleInferenceRule(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, CreateRoleInferenceRuleOutput) + fmt.Fprint(w, CreateRoleInferenceRuleOutput) } th.Mux.HandleFunc("/roles/7ceab6192ea34a548cc71b24f72e762c/implies/97e2f5d38bc94842bc3da818c16762ed", fn) diff --git a/openstack/identity/v3/services/testing/fixtures_test.go b/openstack/identity/v3/services/testing/fixtures_test.go index da713482f8..0a9274c500 100644 --- a/openstack/identity/v3/services/testing/fixtures_test.go +++ b/openstack/identity/v3/services/testing/fixtures_test.go @@ -164,7 +164,7 @@ func HandleListServicesSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -178,7 +178,7 @@ func HandleGetServiceSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -191,7 +191,7 @@ func HandleCreateServiceSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -204,6 +204,6 @@ func HandleUpdateServiceSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index c0aafb9ad4..8bb00fbffa 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -30,7 +30,7 @@ func authTokenPost(t *testing.T, options tokens.AuthOptions, scope *tokens.Scope th.TestJSONRequest(t, r, requestJSON) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "token": { "expires_at": "2014-10-02T13:45:00.000000Z" } @@ -369,7 +369,7 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) { th.TestJSONRequest(t, r, requestJSON) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, responseJSON) + fmt.Fprint(w, responseJSON) }) ao := gophercloud.AuthOptions{ @@ -525,7 +525,7 @@ func TestCreateExtractsTokenFromResponse(t *testing.T) { w.Header().Add("X-Subject-Token", "aaa111") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "token": { "expires_at": "2014-10-02T13:45:00.000000Z" } @@ -670,7 +670,7 @@ func TestGetRequest(t *testing.T) { th.TestHeader(t, r, "X-Subject-Token", "abcdef12345") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } } `) }) @@ -779,7 +779,7 @@ func TestNoTokenInResponse(t *testing.T) { th.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) options := tokens.AuthOptions{UserID: "me", Password: "squirrel!"} diff --git a/openstack/identity/v3/trusts/testing/fixtures_test.go b/openstack/identity/v3/trusts/testing/fixtures_test.go index 6cc9a8a7d1..3e7e77fc3c 100644 --- a/openstack/identity/v3/trusts/testing/fixtures_test.go +++ b/openstack/identity/v3/trusts/testing/fixtures_test.go @@ -207,7 +207,7 @@ func HandleCreateTrust(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - _, err := fmt.Fprintf(w, CreateResponse) + _, err := fmt.Fprint(w, CreateResponse) th.AssertNoErr(t, err) }) } @@ -221,7 +221,7 @@ func HandleCreateTrustNoExpire(t *testing.T) { th.TestJSONRequest(t, r, CreateRequestNoExpire) w.WriteHeader(http.StatusCreated) - _, err := fmt.Fprintf(w, CreateResponseNoExpire) + _, err := fmt.Fprint(w, CreateResponseNoExpire) th.AssertNoErr(t, err) }) } @@ -247,7 +247,7 @@ func HandleGetTrustSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -317,7 +317,7 @@ func HandleListTrustsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) } @@ -331,7 +331,7 @@ func HandleListTrustRolesSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListTrustRolesResponse) + fmt.Fprint(w, ListTrustRolesResponse) }) } @@ -345,7 +345,7 @@ func HandleGetTrustRoleSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetTrustRoleResponse) + fmt.Fprint(w, GetTrustRoleResponse) }) } diff --git a/openstack/identity/v3/users/testing/fixtures_test.go b/openstack/identity/v3/users/testing/fixtures_test.go index 023124cb79..faff58d4d0 100644 --- a/openstack/identity/v3/users/testing/fixtures_test.go +++ b/openstack/identity/v3/users/testing/fixtures_test.go @@ -382,7 +382,7 @@ func HandleListUsersSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -396,7 +396,7 @@ func HandleGetUserSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -409,7 +409,7 @@ func HandleCreateUserSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } @@ -422,7 +422,7 @@ func HandleCreateNoOptionsUserSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateNoOptionsRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetOutputNoOptions) + fmt.Fprint(w, GetOutputNoOptions) }) } @@ -435,7 +435,7 @@ func HandleUpdateUserSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOutput) + fmt.Fprint(w, UpdateOutput) }) } @@ -472,7 +472,7 @@ func HandleListUserGroupsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListGroupsOutput) + fmt.Fprint(w, ListGroupsOutput) }) } @@ -519,7 +519,7 @@ func HandleListUserProjectsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListProjectsOutput) + fmt.Fprint(w, ListProjectsOutput) }) } @@ -533,6 +533,6 @@ func HandleListInGroupSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } diff --git a/openstack/image/v2/imageimport/testing/requests_test.go b/openstack/image/v2/imageimport/testing/requests_test.go index ae353ad0ac..74df12e548 100644 --- a/openstack/image/v2/imageimport/testing/requests_test.go +++ b/openstack/image/v2/imageimport/testing/requests_test.go @@ -22,7 +22,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ImportGetResult) + fmt.Fprint(w, ImportGetResult) }) validImportMethods := []string{ @@ -49,7 +49,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) opts := imageimport.CreateOpts{ diff --git a/openstack/image/v2/images/testing/fixtures_test.go b/openstack/image/v2/images/testing/fixtures_test.go index 0c9a7b0318..693f6b9965 100644 --- a/openstack/image/v2/images/testing/fixtures_test.go +++ b/openstack/image/v2/images/testing/fixtures_test.go @@ -130,7 +130,7 @@ func HandleImageListSuccessfully(t *testing.T) { addNext := false var imageJSON []string - fmt.Fprintf(w, `{"images": [`) + fmt.Fprint(w, `{"images": [`) for _, i := range images { if marker == "" || addNext { @@ -149,7 +149,7 @@ func HandleImageListSuccessfully(t *testing.T) { } } t.Logf("Writing out %v image(s)", len(imageJSON)) - fmt.Fprintf(w, strings.Join(imageJSON, ",")) + fmt.Fprint(w, strings.Join(imageJSON, ",")) fmt.Fprintf(w, `], "next": "/images?marker=%s&limit=%v", @@ -176,7 +176,7 @@ func HandleImageCreationSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "status": "queued", "name": "Ubuntu 12.10", "protected": false, @@ -224,7 +224,7 @@ func HandleImageCreationSuccessfullyNulls(t *testing.T) { w.Header().Set("OpenStack-image-import-methods", "glance-direct,web-download") w.Header().Set("OpenStack-image-store-ids", "123,456") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "architecture": "x86_64", "status": "queued", "name": "Ubuntu 12.10", @@ -258,7 +258,7 @@ func HandleImageGetSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "status": "active", "name": "cirros-0.3.2-x86_64-disk", "tags": [], @@ -347,7 +347,7 @@ func HandleImageUpdateSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "name": "Fedora 17", "status": "active", @@ -389,7 +389,7 @@ func HandleImageListByTagsSuccessfully(t *testing.T) { w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "images": [ { "status": "active", @@ -449,7 +449,7 @@ func HandleImageUpdatePropertiesSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "name": "Fedora 17", "status": "active", diff --git a/openstack/image/v2/members/testing/fixtures_test.go b/openstack/image/v2/members/testing/fixtures_test.go index 4100b186f4..6e0bd326e7 100644 --- a/openstack/image/v2/members/testing/fixtures_test.go +++ b/openstack/image/v2/members/testing/fixtures_test.go @@ -18,7 +18,7 @@ func HandleCreateImageMemberSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, `{"member": "8989447062e04a818baf9e073fd04fa7"}`) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "created_at": "2013-09-20T19:22:19Z", "image_id": "da3b75d9-3f4a-40e7-8a2c-bfab23927dea", "member_id": "8989447062e04a818baf9e073fd04fa7", @@ -37,7 +37,7 @@ func HandleImageMemberList(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "members": [ { "created_at": "2013-10-07T17:58:03Z", @@ -68,7 +68,7 @@ func HandleImageMemberEmptyList(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "members": [], "schema": "/v2/schemas/members" }`) @@ -82,7 +82,7 @@ func HandleImageMemberDetails(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fakeclient.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "status": "pending", "created_at": "2013-11-26T07:21:21Z", "updated_at": "2013-11-26T07:21:21Z", @@ -120,7 +120,7 @@ func HandleImageMemberUpdate(t *testing.T) *CallsCounter { w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "status": "accepted", "created_at": "2013-11-26T07:21:21Z", "updated_at": "2013-11-26T07:21:21Z", diff --git a/openstack/image/v2/tasks/testing/requests_test.go b/openstack/image/v2/tasks/testing/requests_test.go index feb74068bc..f930304427 100644 --- a/openstack/image/v2/tasks/testing/requests_test.go +++ b/openstack/image/v2/tasks/testing/requests_test.go @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, TasksListResult) + fmt.Fprint(w, TasksListResult) }) count := 0 @@ -64,7 +64,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, TasksGetResult) + fmt.Fprint(w, TasksGetResult) }) s, err := tasks.Get(context.TODO(), fakeclient.ServiceClient(), "1252f636-1246-4319-bfba-c47cde0efbe0").Extract() @@ -102,7 +102,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, TaskCreateResult) + fmt.Fprint(w, TaskCreateResult) }) opts := tasks.CreateOpts{ diff --git a/openstack/keymanager/v1/acls/testing/fixtures_test.go b/openstack/keymanager/v1/acls/testing/fixtures_test.go index 7db2b37b4f..89b6ea7d4d 100644 --- a/openstack/keymanager/v1/acls/testing/fixtures_test.go +++ b/openstack/keymanager/v1/acls/testing/fixtures_test.go @@ -75,7 +75,7 @@ func HandleGetSecretACLSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -89,7 +89,7 @@ func HandleGetContainerACLSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -102,7 +102,7 @@ func HandleSetSecretACLSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, SetRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SecretSetResponse) + fmt.Fprint(w, SecretSetResponse) }) } @@ -115,7 +115,7 @@ func HandleSetContainerACLSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, SetRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ContainerSetResponse) + fmt.Fprint(w, ContainerSetResponse) }) } @@ -128,7 +128,7 @@ func HandleUpdateSecretACLSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SecretSetResponse) + fmt.Fprint(w, SecretSetResponse) }) } @@ -141,7 +141,7 @@ func HandleUpdateContainerACLSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ContainerSetResponse) + fmt.Fprint(w, ContainerSetResponse) }) } diff --git a/openstack/keymanager/v1/containers/testing/fixtures_test.go b/openstack/keymanager/v1/containers/testing/fixtures_test.go index 4b3997f786..d290a5a9a6 100644 --- a/openstack/keymanager/v1/containers/testing/fixtures_test.go +++ b/openstack/keymanager/v1/containers/testing/fixtures_test.go @@ -235,7 +235,7 @@ func HandleListContainersSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) } @@ -249,7 +249,7 @@ func HandleGetContainerSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -262,7 +262,7 @@ func HandleCreateContainerSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -288,7 +288,7 @@ func HandleListConsumersSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListConsumersResponse) + fmt.Fprint(w, ListConsumersResponse) }) } @@ -302,7 +302,7 @@ func HandleCreateConsumerSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateConsumerRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, CreateConsumerResponse) + fmt.Fprint(w, CreateConsumerResponse) }) } @@ -316,6 +316,6 @@ func HandleDeleteConsumerSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateConsumerRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } diff --git a/openstack/keymanager/v1/orders/testing/fixtures_test.go b/openstack/keymanager/v1/orders/testing/fixtures_test.go index 636fe7eb3f..7eb14e0edf 100644 --- a/openstack/keymanager/v1/orders/testing/fixtures_test.go +++ b/openstack/keymanager/v1/orders/testing/fixtures_test.go @@ -143,7 +143,7 @@ func HandleListOrdersSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) } @@ -157,7 +157,7 @@ func HandleGetOrderSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -170,7 +170,7 @@ func HandleCreateOrderSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } diff --git a/openstack/keymanager/v1/secrets/testing/fixtures_test.go b/openstack/keymanager/v1/secrets/testing/fixtures_test.go index ff13102354..ed0e9ffad0 100644 --- a/openstack/keymanager/v1/secrets/testing/fixtures_test.go +++ b/openstack/keymanager/v1/secrets/testing/fixtures_test.go @@ -198,7 +198,7 @@ func HandleListSecretsSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) } @@ -212,7 +212,7 @@ func HandleGetSecretSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) } @@ -225,7 +225,7 @@ func HandleGetPayloadSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetPayloadResponse) + fmt.Fprint(w, GetPayloadResponse) }) } @@ -238,7 +238,7 @@ func HandleCreateSecretSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) } @@ -277,7 +277,7 @@ func HandleGetMetadataSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetMetadataResponse) + fmt.Fprint(w, GetMetadataResponse) }) } @@ -293,7 +293,7 @@ func HandleCreateMetadataSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateMetadataResponse) + fmt.Fprint(w, CreateMetadataResponse) }) } @@ -308,7 +308,7 @@ func HandleGetMetadatumSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, MetadatumResponse) + fmt.Fprint(w, MetadatumResponse) }) } @@ -324,7 +324,7 @@ func HandleCreateMetadatumSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, MetadatumResponse) + fmt.Fprint(w, MetadatumResponse) }) } @@ -340,7 +340,7 @@ func HandleUpdateMetadatumSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, MetadatumResponse) + fmt.Fprint(w, MetadatumResponse) }) } diff --git a/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go b/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go index 8ffafa253e..049f0697bf 100644 --- a/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/amphorae/testing/fixtures_test.go @@ -150,9 +150,9 @@ func HandleAmphoraListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, AmphoraeListBody) + fmt.Fprint(w, AmphoraeListBody) case "7f890893-ced0-46ed-8697-33415d070e5a": - fmt.Fprintf(w, `{ "amphorae": [] }`) + fmt.Fprint(w, `{ "amphorae": [] }`) default: t.Fatalf("/v2.0/octavia/amphorae invoked with unexpected marker=[%s]", marker) } @@ -166,7 +166,7 @@ func HandleAmphoraGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleAmphoraBody) + fmt.Fprint(w, SingleAmphoraBody) }) } diff --git a/openstack/loadbalancer/v2/apiversions/testing/fixture.go b/openstack/loadbalancer/v2/apiversions/testing/fixture.go index d21507dbab..b427378d95 100644 --- a/openstack/loadbalancer/v2/apiversions/testing/fixture.go +++ b/openstack/loadbalancer/v2/apiversions/testing/fixture.go @@ -88,6 +88,6 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, OctaviaAllAPIVersionsResponse) + fmt.Fprint(w, OctaviaAllAPIVersionsResponse) }) } diff --git a/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go b/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go index adbdf1b7b3..3558a21919 100644 --- a/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go +++ b/openstack/loadbalancer/v2/flavorprofiles/testing/fixtures.go @@ -94,9 +94,9 @@ func HandleFlavorProfileListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, FlavorProfilesListBody) + fmt.Fprint(w, FlavorProfilesListBody) case "3a0d060b-fcec-4250-9ab6-940b806a12dd": - fmt.Fprintf(w, `{ "flavors": [] }`) + fmt.Fprint(w, `{ "flavors": [] }`) default: t.Fatalf("/v2.0/lbaas/flavors invoked with unexpected marker=[%s]", marker) } @@ -117,7 +117,7 @@ func HandleFlavorProfileCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -127,7 +127,7 @@ func HandleFlavorProfileGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleFlavorProfileBody) + fmt.Fprint(w, SingleFlavorProfileBody) }) } @@ -154,6 +154,6 @@ func HandleFlavorProfileUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateFlavorBody) + fmt.Fprint(w, PostUpdateFlavorBody) }) } diff --git a/openstack/loadbalancer/v2/flavors/testing/fixtures.go b/openstack/loadbalancer/v2/flavors/testing/fixtures.go index a3169d7ece..1ec935c156 100644 --- a/openstack/loadbalancer/v2/flavors/testing/fixtures.go +++ b/openstack/loadbalancer/v2/flavors/testing/fixtures.go @@ -102,9 +102,9 @@ func HandleFlavorListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, FlavorsListBody) + fmt.Fprint(w, FlavorsListBody) case "3a0d060b-fcec-4250-9ab6-940b806a12dd": - fmt.Fprintf(w, `{ "flavors": [] }`) + fmt.Fprint(w, `{ "flavors": [] }`) default: t.Fatalf("/v2.0/lbaas/flavors invoked with unexpected marker=[%s]", marker) } @@ -126,7 +126,7 @@ func HandleFlavorCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -136,7 +136,7 @@ func HandleFlavorGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleFlavorBody) + fmt.Fprint(w, SingleFlavorBody) }) } @@ -163,6 +163,6 @@ func HandleFlavorUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateFlavorBody) + fmt.Fprint(w, PostUpdateFlavorBody) }) } diff --git a/openstack/loadbalancer/v2/flavors/testing/requests_test.go b/openstack/loadbalancer/v2/flavors/testing/requests_test.go index fd668a2134..bc73b1271d 100644 --- a/openstack/loadbalancer/v2/flavors/testing/requests_test.go +++ b/openstack/loadbalancer/v2/flavors/testing/requests_test.go @@ -69,7 +69,7 @@ func TestListFlavorsEnabled(t *testing.T) { t.Errorf("Expected enabled=%s got %q", testCases[cases], enabled) } cases++ - fmt.Fprintf(w, `{"flavorprofiles":[]}`) + fmt.Fprint(w, `{"flavorprofiles":[]}`) }) }() diff --git a/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go b/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go index a1773287f9..56b35cd0e0 100644 --- a/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/l7policies/testing/fixtures_test.go @@ -131,7 +131,7 @@ func HandleL7PolicyCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -218,9 +218,9 @@ func HandleL7PolicyListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, L7PoliciesListBody) + fmt.Fprint(w, L7PoliciesListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "l7policies": [] }`) + fmt.Fprint(w, `{ "l7policies": [] }`) default: t.Fatalf("/v2.0/lbaas/l7policies invoked with unexpected marker=[%s]", marker) } @@ -234,7 +234,7 @@ func HandleL7PolicyGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleL7PolicyBody) + fmt.Fprint(w, SingleL7PolicyBody) }) } @@ -263,7 +263,7 @@ func HandleL7PolicyUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateL7PolicyBody) + fmt.Fprint(w, PostUpdateL7PolicyBody) }) } @@ -281,7 +281,7 @@ func HandleL7PolicyUpdateNullRedirectURLSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateL7PolicyNullRedirectURLBody) + fmt.Fprint(w, PostUpdateL7PolicyNullRedirectURLBody) }) } @@ -317,7 +317,7 @@ func HandleRuleCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -362,9 +362,9 @@ func HandleRuleListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, RulesListBody) + fmt.Fprint(w, RulesListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "rules": [] }`) + fmt.Fprint(w, `{ "rules": [] }`) default: t.Fatalf("/v2.0/lbaas/l7policies/8a1412f0-4c32-4257-8b07-af4770b604fd/rules invoked with unexpected marker=[%s]", marker) } @@ -378,7 +378,7 @@ func HandleRuleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleRuleBody) + fmt.Fprint(w, SingleRuleBody) }) } @@ -425,6 +425,6 @@ func HandleRuleUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateRuleBody) + fmt.Fprint(w, PostUpdateRuleBody) }) } diff --git a/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go b/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go index edda9b2bac..9f85649411 100644 --- a/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/listeners/testing/fixtures_test.go @@ -218,9 +218,9 @@ func HandleListenerListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, ListenersListBody) + fmt.Fprint(w, ListenersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "listeners": [] }`) + fmt.Fprint(w, `{ "listeners": [] }`) default: t.Fatalf("/v2.0/lbaas/listeners invoked with unexpected marker=[%s]", marker) } @@ -255,7 +255,7 @@ func HandleListenerCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -266,7 +266,7 @@ func HandleListenerGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleListenerBody) + fmt.Fprint(w, SingleListenerBody) }) } @@ -304,7 +304,7 @@ func HandleListenerUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateListenerBody) + fmt.Fprint(w, PostUpdateListenerBody) }) } @@ -315,6 +315,6 @@ func HandleListenerGetStatsTree(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, GetListenerStatsBody) + fmt.Fprint(w, GetListenerStatsBody) }) } diff --git a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go index d4f714f7c5..76ef3ec2a7 100644 --- a/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/loadbalancers/testing/fixtures_test.go @@ -453,9 +453,9 @@ func HandleLoadbalancerListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, LoadbalancersListBody) + fmt.Fprint(w, LoadbalancersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "loadbalancers": [] }`) + fmt.Fprint(w, `{ "loadbalancers": [] }`) default: t.Fatalf("/v2.0/lbaas/loadbalancers invoked with unexpected marker=[%s]", marker) } @@ -533,7 +533,7 @@ func HandleFullyPopulatedLoadbalancerCreationSuccessfully(t *testing.T, response w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -559,7 +559,7 @@ func HandleLoadbalancerCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -570,7 +570,7 @@ func HandleLoadbalancerGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleLoadbalancerBody) + fmt.Fprint(w, SingleLoadbalancerBody) }) } @@ -581,7 +581,7 @@ func HandleLoadbalancerGetStatusesTree(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, GetLoadbalancerStatusesBody) + fmt.Fprint(w, GetLoadbalancerStatusesBody) }) } @@ -609,7 +609,7 @@ func HandleLoadbalancerUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateLoadbalancerBody) + fmt.Fprint(w, PostUpdateLoadbalancerBody) }) } @@ -620,7 +620,7 @@ func HandleLoadbalancerGetStatsTree(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, GetLoadbalancerStatsBody) + fmt.Fprint(w, GetLoadbalancerStatsBody) }) } diff --git a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go index 9526b9cbca..6b12d2c11c 100644 --- a/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/monitors/testing/fixtures_test.go @@ -152,9 +152,9 @@ func HandleHealthmonitorListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, HealthmonitorsListBody) + fmt.Fprint(w, HealthmonitorsListBody) case "556c8345-28d8-4f84-a246-e04380b0461d": - fmt.Fprintf(w, `{ "healthmonitors": [] }`) + fmt.Fprint(w, `{ "healthmonitors": [] }`) default: t.Fatalf("/v2.0/lbaas/healthmonitors invoked with unexpected marker=[%s]", marker) } @@ -184,7 +184,7 @@ func HandleHealthmonitorCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -195,7 +195,7 @@ func HandleHealthmonitorGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleHealthmonitorBody) + fmt.Fprint(w, SingleHealthmonitorBody) }) } @@ -228,6 +228,6 @@ func HandleHealthmonitorUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateHealthmonitorBody) + fmt.Fprint(w, PostUpdateHealthmonitorBody) }) } diff --git a/openstack/loadbalancer/v2/pools/testing/fixtures_test.go b/openstack/loadbalancer/v2/pools/testing/fixtures_test.go index 47d7c373f8..bcc582a638 100644 --- a/openstack/loadbalancer/v2/pools/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/pools/testing/fixtures_test.go @@ -145,9 +145,9 @@ func HandlePoolListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, PoolsListBody) + fmt.Fprint(w, PoolsListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "pools": [] }`) + fmt.Fprint(w, `{ "pools": [] }`) default: t.Fatalf("/v2.0/lbaas/pools invoked with unexpected marker=[%s]", marker) } @@ -172,7 +172,7 @@ func HandlePoolCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -183,7 +183,7 @@ func HandlePoolGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SinglePoolBody) + fmt.Fprint(w, SinglePoolBody) }) } @@ -211,7 +211,7 @@ func HandlePoolUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdatePoolBody) + fmt.Fprint(w, PostUpdatePoolBody) }) } @@ -342,9 +342,9 @@ func HandleMemberListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, MembersListBody) + fmt.Fprint(w, MembersListBody) case "45e08a3e-a78f-4b40-a229-1e7e23eee1ab": - fmt.Fprintf(w, `{ "members": [] }`) + fmt.Fprint(w, `{ "members": [] }`) default: t.Fatalf("/v2.0/lbaas/pools/332abe93-f488-41ba-870b-2ac66be7f853/members invoked with unexpected marker=[%s]", marker) } @@ -370,7 +370,7 @@ func HandleMemberCreationSuccessfully(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } @@ -381,7 +381,7 @@ func HandleMemberGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Accept", "application/json") - fmt.Fprintf(w, SingleMemberBody) + fmt.Fprint(w, SingleMemberBody) }) } @@ -409,7 +409,7 @@ func HandleMemberUpdateSuccessfully(t *testing.T) { } }`) - fmt.Fprintf(w, PostUpdateMemberBody) + fmt.Fprint(w, PostUpdateMemberBody) }) } diff --git a/openstack/loadbalancer/v2/providers/testing/fixtures_test.go b/openstack/loadbalancer/v2/providers/testing/fixtures_test.go index 64db9a39bf..b8a857e618 100644 --- a/openstack/loadbalancer/v2/providers/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/providers/testing/fixtures_test.go @@ -50,7 +50,7 @@ func HandleProviderListSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, ProvidersListBody) + fmt.Fprint(w, ProvidersListBody) default: t.Fatalf("/v2.0/lbaas/providers invoked with unexpected marker=[%s]", marker) } diff --git a/openstack/loadbalancer/v2/quotas/testing/requests_test.go b/openstack/loadbalancer/v2/quotas/testing/requests_test.go index 64c819aecf..6f990428fc 100644 --- a/openstack/loadbalancer/v2/quotas/testing/requests_test.go +++ b/openstack/loadbalancer/v2/quotas/testing/requests_test.go @@ -23,7 +23,7 @@ func TestGet_1(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponseRaw_1) + fmt.Fprint(w, GetResponseRaw_1) }) q, err := quotas.Get(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() @@ -42,7 +42,7 @@ func TestGet_2(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponseRaw_2) + fmt.Fprint(w, GetResponseRaw_2) }) q, err := quotas.Get(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() @@ -61,7 +61,7 @@ func TestUpdate_1(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, UpdateRequestResponseRaw_1) + fmt.Fprint(w, UpdateRequestResponseRaw_1) }) q, err := quotas.Update(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ @@ -89,7 +89,7 @@ func TestUpdate_2(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, UpdateRequestResponseRaw_2) + fmt.Fprint(w, UpdateRequestResponseRaw_2) }) q, err := quotas.Update(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ diff --git a/openstack/messaging/v2/claims/testing/fixtures_test.go b/openstack/messaging/v2/claims/testing/fixtures_test.go index f713687892..5912ff64e6 100644 --- a/openstack/messaging/v2/claims/testing/fixtures_test.go +++ b/openstack/messaging/v2/claims/testing/fixtures_test.go @@ -93,7 +93,7 @@ func HandleCreateSuccessfully(t *testing.T) { w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateClaimResponse) + fmt.Fprint(w, CreateClaimResponse) }) } @@ -117,7 +117,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetClaimResponse) + fmt.Fprint(w, GetClaimResponse) }) } diff --git a/openstack/messaging/v2/messages/testing/fixtures_test.go b/openstack/messaging/v2/messages/testing/fixtures_test.go index a5a82db253..95cd47592b 100644 --- a/openstack/messaging/v2/messages/testing/fixtures_test.go +++ b/openstack/messaging/v2/messages/testing/fixtures_test.go @@ -229,7 +229,7 @@ func HandleCreateSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateMessageResponse) + fmt.Fprint(w, CreateMessageResponse) }) } @@ -245,7 +245,7 @@ func HandleListSuccessfully(t *testing.T) { switch next { case fmt.Sprintf("/v2/queues/%s/messages?limit=1", QueueName): - fmt.Fprintf(w, ListMessagesResponse1) + fmt.Fprint(w, ListMessagesResponse1) case fmt.Sprintf("/v2/queues/%s/messages?marker=1", QueueName): fmt.Fprint(w, ListMessagesResponse2) case fmt.Sprintf("/v2/queues/%s/messages?marker=2", QueueName): @@ -262,7 +262,7 @@ func HandleGetMessagesSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetMessagesResponse) + fmt.Fprint(w, GetMessagesResponse) }) } @@ -274,7 +274,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetMessageResponse) + fmt.Fprint(w, GetMessageResponse) }) } @@ -299,7 +299,7 @@ func HandlePopSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, PopMessageResponse) + fmt.Fprint(w, PopMessageResponse) }) } diff --git a/openstack/messaging/v2/queues/testing/fixtures_test.go b/openstack/messaging/v2/queues/testing/fixtures_test.go index 2e9cf5e752..7abfdc35ba 100644 --- a/openstack/messaging/v2/queues/testing/fixtures_test.go +++ b/openstack/messaging/v2/queues/testing/fixtures_test.go @@ -226,7 +226,7 @@ func HandleListSuccessfully(t *testing.T) { switch next { case "/v2/queues?limit=1&with_count=true": - fmt.Fprintf(w, ListQueuesResponse1) + fmt.Fprint(w, ListQueuesResponse1) case "/v2/queues?marker=london": fmt.Fprint(w, ListQueuesResponse2) case "/v2/queues?marker=beijing": @@ -256,7 +256,7 @@ func HandleUpdateSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, UpdateQueueRequest) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, UpdateQueueResponse) + fmt.Fprint(w, UpdateQueueResponse) }) } @@ -268,7 +268,7 @@ func HandleGetSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetQueueResponse) + fmt.Fprint(w, GetQueueResponse) }) } @@ -290,7 +290,7 @@ func HandleGetStatsSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetStatsResponse) + fmt.Fprint(w, GetStatsResponse) }) } @@ -303,7 +303,7 @@ func HandleShareSuccessfully(t *testing.T) { th.TestJSONRequest(t, r, CreateShareRequest) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateShareResponse) + fmt.Fprint(w, CreateShareResponse) }) } diff --git a/openstack/networking/v2/apiversions/testing/requests_test.go b/openstack/networking/v2/apiversions/testing/requests_test.go index c99c99488b..dbdf61d9fd 100644 --- a/openstack/networking/v2/apiversions/testing/requests_test.go +++ b/openstack/networking/v2/apiversions/testing/requests_test.go @@ -23,7 +23,7 @@ func TestListVersions(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "versions": [ { @@ -96,7 +96,7 @@ func TestAPIInfo(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "resources": [ { diff --git a/openstack/networking/v2/extensions/agents/testing/requests_test.go b/openstack/networking/v2/extensions/agents/testing/requests_test.go index 070ff1b184..41380edac7 100644 --- a/openstack/networking/v2/extensions/agents/testing/requests_test.go +++ b/openstack/networking/v2/extensions/agents/testing/requests_test.go @@ -25,7 +25,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AgentsListResult) + fmt.Fprint(w, AgentsListResult) }) count := 0 @@ -66,7 +66,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AgentsGetResult) + fmt.Fprint(w, AgentsGetResult) }) s, err := agents.Get(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() @@ -106,7 +106,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AgentsUpdateResult) + fmt.Fprint(w, AgentsUpdateResult) }) iTrue := true @@ -149,7 +149,7 @@ func TestListDHCPNetworks(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AgentDHCPNetworksListResult) + fmt.Fprint(w, AgentDHCPNetworksListResult) }) s, err := agents.ListDHCPNetworks(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() @@ -223,7 +223,7 @@ func TestListBGPSpeakers(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListBGPSpeakersResult) + fmt.Fprint(w, ListBGPSpeakersResult) }) count := 0 @@ -305,7 +305,7 @@ func TestListDRAgentHostingBGPSpeakers(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListDRAgentHostingBGPSpeakersResult) + fmt.Fprint(w, ListDRAgentHostingBGPSpeakersResult) }) count := 0 @@ -342,7 +342,7 @@ func TestListL3Routers(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AgentL3RoutersListResult) + fmt.Fprint(w, AgentL3RoutersListResult) }) s, err := agents.ListL3Routers(context.TODO(), fake.ServiceClient(), "43583cf5-472e-4dc8-af5b-6aed4c94ee3a").Extract() diff --git a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go index e9dc3498fb..5f20bdff5f 100644 --- a/openstack/networking/v2/extensions/attributestags/testing/requests_test.go +++ b/openstack/networking/v2/extensions/attributestags/testing/requests_test.go @@ -25,7 +25,7 @@ func TestReplaceAll(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, attributestagsReplaceAllResult) + fmt.Fprint(w, attributestagsReplaceAllResult) }) opts := attributestags.ReplaceAllOpts{ @@ -48,7 +48,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, attributestagsListResult) + fmt.Fprint(w, attributestagsListResult) }) res, err := attributestags.List(context.TODO(), fake.ServiceClient(), "networks", "fakeid").Extract() 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 3ec3faa391..f24a0a1a0c 100644 --- a/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/peers/testing/requests_test.go @@ -23,7 +23,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListBGPPeersResult) + fmt.Fprint(w, ListBGPPeersResult) }) count := 0 @@ -54,7 +54,7 @@ func TestGet(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetBGPPeerResult) + fmt.Fprint(w, GetBGPPeerResult) }) s, err := peers.Get(context.TODO(), fake.ServiceClient(), bgpPeerID).Extract() @@ -74,7 +74,7 @@ func TestCreate(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) var opts peers.CreateOpts @@ -124,7 +124,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateBGPPeerResponse) + fmt.Fprint(w, UpdateBGPPeerResponse) }) var opts peers.UpdateOpts 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 d163086a8a..0ace6b2403 100644 --- a/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgp/speakers/testing/requests_test.go @@ -23,7 +23,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListBGPSpeakerResult) + fmt.Fprint(w, ListBGPSpeakerResult) }) count := 0 @@ -54,7 +54,7 @@ func TestGet(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetBGPSpeakerResult) + fmt.Fprint(w, GetBGPSpeakerResult) }) s, err := speakers.Get(context.TODO(), fake.ServiceClient(), bgpSpeakerID).Extract() @@ -74,7 +74,7 @@ func TestCreate(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) opts := speakers.CreateOpts{ @@ -124,7 +124,7 @@ func TestUpdate(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetBGPSpeakerResult) + fmt.Fprint(w, GetBGPSpeakerResult) } else if r.Method == "PUT" { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) @@ -134,7 +134,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateBGPSpeakerResponse) + fmt.Fprint(w, UpdateBGPSpeakerResponse) } else { panic("Unexpected Request") } @@ -168,7 +168,7 @@ func TestAddBGPPeer(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AddRemoveBGPPeerJSON) + fmt.Fprint(w, AddRemoveBGPPeerJSON) }) opts := speakers.AddBGPPeerOpts{BGPPeerID: bgpPeerID} @@ -207,7 +207,7 @@ func TestGetAdvertisedRoutes(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetAdvertisedRoutesResult) + fmt.Fprint(w, GetAdvertisedRoutesResult) }) count := 0 @@ -249,7 +249,7 @@ func TestAddGatewayNetwork(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AddRemoveGatewayNetworkJSON) + fmt.Fprint(w, AddRemoveGatewayNetworkJSON) }) opts := speakers.AddGatewayNetworkOpts{NetworkID: networkID} @@ -273,7 +273,7 @@ func TestRemoveGatewayNetwork(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, "") + fmt.Fprint(w, "") }) opts := speakers.RemoveGatewayNetworkOpts{NetworkID: networkID} diff --git a/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go b/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go index 3b39e7cee0..aacc57aa90 100644 --- a/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go +++ b/openstack/networking/v2/extensions/bgpvpns/testing/requests_test.go @@ -35,7 +35,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListBGPVPNsResult) + fmt.Fprint(w, ListBGPVPNsResult) }) count := 0 @@ -67,7 +67,7 @@ func TestGet(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetBGPVPNResult) + fmt.Fprint(w, GetBGPVPNResult) }) r, err := bgpvpns.Get(context.TODO(), fake.ServiceClient(), bgpVpnID).Extract() @@ -87,7 +87,7 @@ func TestCreate(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) opts := bgpvpns.CreateOpts{ @@ -148,7 +148,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateBGPVPNResponse) + fmt.Fprint(w, UpdateBGPVPNResponse) }) name := "foo" @@ -186,7 +186,7 @@ func TestListNetworkAssociations(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListNetworkAssociationsResult) + fmt.Fprint(w, ListNetworkAssociationsResult) }) count := 0 @@ -223,7 +223,7 @@ func TestCreateNetworkAssociation(t *testing.T) { th.TestJSONRequest(t, r, CreateNetworkAssociationRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateNetworkAssociationResponse) + fmt.Fprint(w, CreateNetworkAssociationResponse) }) opts := bgpvpns.CreateNetworkAssociationOpts{ @@ -245,7 +245,7 @@ func TestGetNetworkAssociation(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetNetworkAssociationResult) + fmt.Fprint(w, GetNetworkAssociationResult) }) r, err := bgpvpns.GetNetworkAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, networkAssociationID).Extract() @@ -289,7 +289,7 @@ func TestListRouterAssociations(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListRouterAssociationsResult) + fmt.Fprint(w, ListRouterAssociationsResult) }) count := 0 @@ -326,7 +326,7 @@ func TestCreateRouterAssociation(t *testing.T) { th.TestJSONRequest(t, r, CreateRouterAssociationRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateRouterAssociationResponse) + fmt.Fprint(w, CreateRouterAssociationResponse) }) opts := bgpvpns.CreateRouterAssociationOpts{ @@ -348,7 +348,7 @@ func TestGetRouterAssociation(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetRouterAssociationResult) + fmt.Fprint(w, GetRouterAssociationResult) }) r, err := bgpvpns.GetRouterAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, routerAssociationID).Extract() @@ -370,7 +370,7 @@ func TestUpdateRouterAssociation(t *testing.T) { th.TestJSONRequest(t, r, UpdateRouterAssociationRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateRouterAssociationResponse) + fmt.Fprint(w, UpdateRouterAssociationResponse) }) opts := bgpvpns.UpdateRouterAssociationOpts{ @@ -417,7 +417,7 @@ func TestListPortAssociations(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListPortAssociationsResult) + fmt.Fprint(w, ListPortAssociationsResult) }) count := 0 @@ -454,7 +454,7 @@ func TestCreatePortAssociation(t *testing.T) { th.TestJSONRequest(t, r, CreatePortAssociationRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreatePortAssociationResponse) + fmt.Fprint(w, CreatePortAssociationResponse) }) opts := bgpvpns.CreatePortAssociationOpts{ @@ -476,7 +476,7 @@ func TestGetPortAssociation(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetPortAssociationResult) + fmt.Fprint(w, GetPortAssociationResult) }) r, err := bgpvpns.GetPortAssociation(context.TODO(), fake.ServiceClient(), bgpVpnID, portAssociationID).Extract() @@ -498,7 +498,7 @@ func TestUpdatePortAssociation(t *testing.T) { th.TestJSONRequest(t, r, UpdatePortAssociationRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdatePortAssociationResponse) + fmt.Fprint(w, UpdatePortAssociationResponse) }) opts := bgpvpns.UpdatePortAssociationOpts{ diff --git a/openstack/networking/v2/extensions/dns/testing/fixtures_test.go b/openstack/networking/v2/extensions/dns/testing/fixtures_test.go index 340c90083c..85679382de 100644 --- a/openstack/networking/v2/extensions/dns/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/dns/testing/fixtures_test.go @@ -78,7 +78,7 @@ func PortHandleListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, porttest.ListResponse) + fmt.Fprint(w, porttest.ListResponse) }) } @@ -90,7 +90,7 @@ func PortHandleGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, porttest.GetResponse) + fmt.Fprint(w, porttest.GetResponse) }) } @@ -121,7 +121,7 @@ func PortHandleCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "port": { "status": "DOWN", @@ -184,7 +184,7 @@ func PortHandleUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "port": { "status": "DOWN", @@ -229,7 +229,7 @@ func FloatingIPHandleList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, floatingiptest.ListResponseDNS) + fmt.Fprint(w, floatingiptest.ListResponseDNS) }) } @@ -241,7 +241,7 @@ func FloatingIPHandleGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, fmt.Sprintf(`{"floatingip": %s}`, floatingiptest.FipDNS)) + fmt.Fprint(w, fmt.Sprintf(`{"floatingip": %s}`, floatingiptest.FipDNS)) }) } @@ -264,7 +264,7 @@ func FloatingIPHandleCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, fmt.Sprintf(`{"floatingip": %s}`, floatingiptest.FipDNS)) + fmt.Fprint(w, fmt.Sprintf(`{"floatingip": %s}`, floatingiptest.FipDNS)) }) } @@ -278,7 +278,7 @@ func NetworkHandleList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, networktest.ListResponse) + fmt.Fprint(w, networktest.ListResponse) }) } @@ -290,7 +290,7 @@ func NetworkHandleGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, networktest.GetResponse) + fmt.Fprint(w, networktest.GetResponse) }) } @@ -304,7 +304,7 @@ func NetworkHandleCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, NetworkCreateResponse) + fmt.Fprint(w, NetworkCreateResponse) }) } @@ -319,6 +319,6 @@ func NetworkHandleUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NetworkUpdateResponse) + fmt.Fprint(w, NetworkUpdateResponse) }) } diff --git a/openstack/networking/v2/extensions/external/testing/results_test.go b/openstack/networking/v2/extensions/external/testing/results_test.go index 12062e53d8..bf5c02d636 100644 --- a/openstack/networking/v2/extensions/external/testing/results_test.go +++ b/openstack/networking/v2/extensions/external/testing/results_test.go @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, nettest.ListResponse) + fmt.Fprint(w, nettest.ListResponse) }) type NetworkWithExternalExt struct { @@ -54,7 +54,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, nettest.GetResponse) + fmt.Fprint(w, nettest.GetResponse) }) var s struct { @@ -83,7 +83,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) iTrue := true @@ -118,7 +118,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) iTrue := true 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 59768a756d..540f80aaf4 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 @@ -23,7 +23,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_groups": [ { @@ -136,7 +136,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_group": { "id": "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", @@ -199,7 +199,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_group": { "id": "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", @@ -260,7 +260,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_group": { "id": "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", @@ -319,7 +319,7 @@ func TestRemoveIngressPolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_group": { "id": "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", @@ -367,7 +367,7 @@ func TestRemoveEgressPolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_group": { "id": "6bfb0f10-07f7-4a40-b534-bad4b4ca3428", 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 3141f19795..217c062f4c 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 @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_policies": [ { @@ -135,7 +135,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_policy":{ "name": "policy", @@ -189,7 +189,7 @@ func TestInsertRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "audited": false, "description": "TESTACC-DESC-8P12aLfW", @@ -252,7 +252,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_policy":{ "name": "www", @@ -310,7 +310,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_policy":{ "name": "policy", @@ -377,7 +377,7 @@ func TestRemoveRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "audited": false, "description": "TESTACC-DESC-skno2e52", 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 36469967d8..f251c563c0 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 @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_rules": [ { @@ -150,7 +150,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_rule":{ "protocol": "tcp", @@ -215,7 +215,7 @@ func TestCreateAnyProtocol(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_rule":{ "protocol": null, @@ -264,7 +264,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_rule":{ "protocol": "tcp", @@ -333,7 +333,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "firewall_rule":{ "protocol": "tcp", diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go index b59bd3893d..815a0c0f08 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/testing/requests_test.go @@ -23,7 +23,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AddressScopesListResult) + fmt.Fprint(w, AddressScopesListResult) }) count := 0 @@ -63,7 +63,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AddressScopesGetResult) + fmt.Fprint(w, AddressScopesGetResult) }) s, err := addressscopes.Get(context.TODO(), fake.ServiceClient(), "9cc35860-522a-4d35-974d-51d4b011801e").Extract() @@ -91,7 +91,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, AddressScopeCreateResult) + fmt.Fprint(w, AddressScopeCreateResult) }) opts := addressscopes.CreateOpts{ @@ -124,7 +124,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AddressScopeUpdateResult) + fmt.Fprint(w, AddressScopeUpdateResult) }) shared := true diff --git a/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go index 66d08c8898..35d7516e1c 100644 --- a/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/extraroutes/testing/requests_test.go @@ -35,7 +35,7 @@ func TestAddExtraRoutes(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "router": { "name": "name", @@ -109,7 +109,7 @@ func TestRemoveExtraRoutes(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "router": { "name": "name", diff --git a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go index 92830dabb4..40f5054b99 100644 --- a/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/floatingips/testing/requests_test.go @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) count := 0 @@ -88,7 +88,7 @@ func TestInvalidNextPageURLs(t *testing.T) { th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{"floatingips": [{}], "floatingips_links": {}}`) + fmt.Fprint(w, `{"floatingips": [{}], "floatingips_links": {}}`) }) err := floatingips.List(fake.ServiceClient(), floatingips.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { @@ -134,7 +134,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", @@ -189,7 +189,7 @@ func TestCreateEmptyPort(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", @@ -243,7 +243,7 @@ func TestCreateWithSubnetID(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "floatingip": { "router_id": null, @@ -289,7 +289,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "floatingip": { "floating_network_id": "90f742b1-6d17-487b-ba95-71881dbc0b64", @@ -342,7 +342,7 @@ func TestAssociate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", @@ -384,7 +384,7 @@ func TestDisassociate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "floatingip": { "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", diff --git a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go index c4ad108146..dc2aa8c49b 100644 --- a/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/portforwarding/testing/requests_test.go @@ -23,7 +23,7 @@ func TestPortForwardingList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) count := 0 @@ -91,7 +91,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "port_forwarding": { "protocol": "tcp", @@ -134,7 +134,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "port_forwarding": { "protocol": "tcp", @@ -196,7 +196,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "port_forwarding": { "protocol": "udp", diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index 541dd656b5..6ff1242366 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "routers": [ { @@ -156,7 +156,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "router": { "status": "ACTIVE", @@ -224,7 +224,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "router": { "status": "ACTIVE", @@ -301,7 +301,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "router": { "status": "ACTIVE", @@ -367,7 +367,7 @@ func TestUpdateWithoutRoutes(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "router": { "status": "ACTIVE", @@ -422,7 +422,7 @@ func TestAllRoutesRemoved(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "router": { "status": "ACTIVE", @@ -481,7 +481,7 @@ func TestAddInterface(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188", "tenant_id": "017d8de156df4177889f31a9bd6edc00", @@ -530,7 +530,7 @@ func TestRemoveInterface(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188", "tenant_id": "017d8de156df4177889f31a9bd6edc00", @@ -561,7 +561,7 @@ func TestListL3Agents(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "agents": [ { diff --git a/openstack/networking/v2/extensions/mtu/testing/results_test.go b/openstack/networking/v2/extensions/mtu/testing/results_test.go index 273bdb5166..916bbc00f8 100644 --- a/openstack/networking/v2/extensions/mtu/testing/results_test.go +++ b/openstack/networking/v2/extensions/mtu/testing/results_test.go @@ -29,7 +29,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, nettest.ListResponse) + fmt.Fprint(w, nettest.ListResponse) }) type NetworkWithMTUExt struct { @@ -59,7 +59,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, nettest.GetResponse) + fmt.Fprint(w, nettest.GetResponse) }) var s NetworkMTU @@ -85,7 +85,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) iTrue := true @@ -123,7 +123,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) iTrue := true diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go index 86166b5e33..cdd6d8d4e2 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/requests_test.go @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NetworkIPAvailabilityListResult) + fmt.Fprint(w, NetworkIPAvailabilityListResult) }) count := 0 @@ -65,7 +65,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NetworkIPAvailabilityGetResult) + fmt.Fprint(w, NetworkIPAvailabilityGetResult) }) s, err := networkipavailabilities.Get(context.TODO(), fake.ServiceClient(), "cf11ab78-2302-49fa-870f-851a08c7afb8").Extract() diff --git a/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go b/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go index 494fe49091..44e2b5db9b 100644 --- a/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/portsbinding/testing/fixtures_test.go @@ -18,7 +18,7 @@ func HandleListSuccessfully(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, porttest.ListResponse) + fmt.Fprint(w, porttest.ListResponse) }) } @@ -30,7 +30,7 @@ func HandleGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, porttest.GetResponse) + fmt.Fprint(w, porttest.GetResponse) }) } @@ -62,7 +62,7 @@ func HandleCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "port": { "status": "DOWN", @@ -120,7 +120,7 @@ func HandleUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "port": { "status": "DOWN", diff --git a/openstack/networking/v2/extensions/provider/testing/results_test.go b/openstack/networking/v2/extensions/provider/testing/results_test.go index 4e21481de1..acbf9f7a1f 100644 --- a/openstack/networking/v2/extensions/provider/testing/results_test.go +++ b/openstack/networking/v2/extensions/provider/testing/results_test.go @@ -25,7 +25,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, nettest.ListResponse) + fmt.Fprint(w, nettest.ListResponse) }) type NetworkWithExt struct { @@ -60,7 +60,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, nettest.GetResponse) + fmt.Fprint(w, nettest.GetResponse) }) var s struct { @@ -91,7 +91,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, nettest.CreateResponse) + fmt.Fprint(w, nettest.CreateResponse) }) var s struct { @@ -142,7 +142,7 @@ func TestCreateWithMultipleProvider(t *testing.T) { `) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "network": { "status": "ACTIVE", @@ -227,7 +227,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, nettest.UpdateResponse) + fmt.Fprint(w, nettest.UpdateResponse) }) var s struct { diff --git a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go index 72b17e5330..4ad4cf3088 100644 --- a/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/policies/testing/requests_test.go @@ -26,7 +26,7 @@ func TestGetPort(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, GetPortResponse) + _, err := fmt.Fprint(w, GetPortResponse) th.AssertNoErr(t, err) }) @@ -55,7 +55,7 @@ func TestCreatePort(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - _, err := fmt.Fprintf(w, CreatePortResponse) + _, err := fmt.Fprint(w, CreatePortResponse) th.AssertNoErr(t, err) }) @@ -93,7 +93,7 @@ func TestUpdatePortWithPolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, UpdatePortWithPolicyResponse) + _, err := fmt.Fprint(w, UpdatePortWithPolicyResponse) th.AssertNoErr(t, err) }) @@ -131,7 +131,7 @@ func TestUpdatePortWithoutPolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, UpdatePortWithoutPolicyResponse) + _, err := fmt.Fprint(w, UpdatePortWithoutPolicyResponse) th.AssertNoErr(t, err) }) @@ -166,7 +166,7 @@ func TestGetNetwork(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, GetNetworkResponse) + _, err := fmt.Fprint(w, GetNetworkResponse) th.AssertNoErr(t, err) }) @@ -195,7 +195,7 @@ func TestCreateNetwork(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - _, err := fmt.Fprintf(w, CreateNetworkResponse) + _, err := fmt.Fprint(w, CreateNetworkResponse) th.AssertNoErr(t, err) }) @@ -232,7 +232,7 @@ func TestUpdateNetworkWithPolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, UpdateNetworkWithPolicyResponse) + _, err := fmt.Fprint(w, UpdateNetworkWithPolicyResponse) th.AssertNoErr(t, err) }) @@ -273,7 +273,7 @@ func TestUpdateNetworkWithoutPolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, UpdateNetworkWithoutPolicyResponse) + _, err := fmt.Fprint(w, UpdateNetworkWithoutPolicyResponse) th.AssertNoErr(t, err) }) @@ -307,7 +307,7 @@ func TestListPolicies(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListPoliciesResponse) + fmt.Fprint(w, ListPoliciesResponse) }) count := 0 @@ -347,7 +347,7 @@ func TestGetPolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetPolicyResponse) + fmt.Fprint(w, GetPolicyResponse) }) p, err := policies.Get(context.TODO(), fake.ServiceClient(), "30a57f4a-336b-4382-8275-d708babd2241").Extract() @@ -387,7 +387,7 @@ func TestCreatePolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreatePolicyResponse) + fmt.Fprint(w, CreatePolicyResponse) }) opts := policies.CreateOpts{ @@ -425,7 +425,7 @@ func TestUpdatePolicy(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdatePolicyResponse) + fmt.Fprint(w, UpdatePolicyResponse) }) shared := true diff --git a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go index 4ee49605ea..20de755132 100644 --- a/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/rules/testing/requests_test.go @@ -23,7 +23,7 @@ func TestListBandwidthLimitRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, BandwidthLimitRulesListResult) + fmt.Fprint(w, BandwidthLimitRulesListResult) }) count := 0 @@ -71,7 +71,7 @@ func TestGetBandwidthLimitRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, BandwidthLimitRulesGetResult) + fmt.Fprint(w, BandwidthLimitRulesGetResult) }) r, err := rules.GetBandwidthLimitRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractBandwidthLimitRule() @@ -97,7 +97,7 @@ func TestCreateBandwidthLimitRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, BandwidthLimitRulesCreateResult) + fmt.Fprint(w, BandwidthLimitRulesCreateResult) }) opts := rules.CreateBandwidthLimitRuleOpts{ @@ -125,7 +125,7 @@ func TestUpdateBandwidthLimitRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, BandwidthLimitRulesUpdateResult) + fmt.Fprint(w, BandwidthLimitRulesUpdateResult) }) maxKBps := 500 @@ -166,7 +166,7 @@ func TestListDSCPMarkingRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, DSCPMarkingRulesListResult) + fmt.Fprint(w, DSCPMarkingRulesListResult) }) count := 0 @@ -212,7 +212,7 @@ func TestGetDSCPMarkingRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, DSCPMarkingRuleGetResult) + fmt.Fprint(w, DSCPMarkingRuleGetResult) }) r, err := rules.GetDSCPMarkingRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractDSCPMarkingRule() @@ -236,7 +236,7 @@ func TestCreateDSCPMarkingRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, DSCPMarkingRuleCreateResult) + fmt.Fprint(w, DSCPMarkingRuleCreateResult) }) opts := rules.CreateDSCPMarkingRuleOpts{ @@ -263,7 +263,7 @@ func TestUpdateDSCPMarkingRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, DSCPMarkingRuleUpdateResult) + fmt.Fprint(w, DSCPMarkingRuleUpdateResult) }) dscpMark := 26 @@ -302,7 +302,7 @@ func TestListMinimumBandwidthRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, MinimumBandwidthRulesListResult) + fmt.Fprint(w, MinimumBandwidthRulesListResult) }) count := 0 @@ -349,7 +349,7 @@ func TestGetMinimumBandwidthRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, MinimumBandwidthRulesGetResult) + fmt.Fprint(w, MinimumBandwidthRulesGetResult) }) r, err := rules.GetMinimumBandwidthRule(context.TODO(), fake.ServiceClient(), "501005fa-3b56-4061-aaca-3f24995112e1", "30a57f4a-336b-4382-8275-d708babd2241").ExtractMinimumBandwidthRule() @@ -374,7 +374,7 @@ func TestCreateMinimumBandwidthRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, MinimumBandwidthRulesCreateResult) + fmt.Fprint(w, MinimumBandwidthRulesCreateResult) }) opts := rules.CreateMinimumBandwidthRuleOpts{ @@ -400,7 +400,7 @@ func TestUpdateMinimumBandwidthRule(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, MinimumBandwidthRulesUpdateResult) + fmt.Fprint(w, MinimumBandwidthRulesUpdateResult) }) minKBps := 500 diff --git a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go index 74eeb49aa4..7bea9f6b66 100644 --- a/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go +++ b/openstack/networking/v2/extensions/qos/ruletypes/testing/requests_test.go @@ -52,7 +52,7 @@ func TestGetRuleType(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - _, err := fmt.Fprintf(w, GetRuleTypeResponse) + _, err := fmt.Fprint(w, GetRuleTypeResponse) th.AssertNoErr(t, err) }) diff --git a/openstack/networking/v2/extensions/quotas/testing/requests_test.go b/openstack/networking/v2/extensions/quotas/testing/requests_test.go index 7ed3100242..a5714d17fe 100644 --- a/openstack/networking/v2/extensions/quotas/testing/requests_test.go +++ b/openstack/networking/v2/extensions/quotas/testing/requests_test.go @@ -23,7 +23,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponseRaw) + fmt.Fprint(w, GetResponseRaw) }) q, err := quotas.Get(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() @@ -42,7 +42,7 @@ func TestGetDetail(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetDetailedResponseRaw) + fmt.Fprint(w, GetDetailedResponseRaw) }) q, err := quotas.GetDetail(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76").Extract() @@ -61,7 +61,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateRequestResponseRaw) + fmt.Fprint(w, UpdateRequestResponseRaw) }) q, err := quotas.Update(context.TODO(), fake.ServiceClient(), "0a73845280574ad389c292f6a74afa76", quotas.UpdateOpts{ diff --git a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go index 7503bc7a74..9fc04412f4 100644 --- a/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/rbacpolicies/testing/requests_test.go @@ -25,7 +25,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) options := rbacpolicies.CreateOpts{ @@ -51,7 +51,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) n, err := rbacpolicies.Get(context.TODO(), fake.ServiceClient(), "2cf7523a-93b5-4e69-9360-6c6bf986bb7c").Extract() @@ -70,7 +70,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) client := fake.ServiceClient() @@ -106,7 +106,7 @@ func TestListWithAllPages(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) client := fake.ServiceClient() @@ -159,7 +159,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) options := rbacpolicies.UpdateOpts{TargetTenant: "9d766060b6354c9e8e2da44cab0e8f38"} diff --git a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go index 30c3ab6359..f816f0d6d2 100644 --- a/openstack/networking/v2/extensions/security/groups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/groups/testing/requests_test.go @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SecurityGroupListResponse) + fmt.Fprint(w, SecurityGroupListResponse) }) count := 0 @@ -64,7 +64,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SecurityGroupCreateResponse) + fmt.Fprint(w, SecurityGroupCreateResponse) }) opts := groups.CreateOpts{Name: "new-webservers", Description: "security group for webservers"} @@ -87,7 +87,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SecurityGroupUpdateResponse) + fmt.Fprint(w, SecurityGroupUpdateResponse) }) opts := groups.UpdateOpts{Name: "newer-webservers"} @@ -112,7 +112,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SecurityGroupGetResponse) + fmt.Fprint(w, SecurityGroupGetResponse) }) sg, err := groups.Get(context.TODO(), fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract() diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 40372c298a..843d7eb202 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -23,7 +23,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group_rules": [ { @@ -131,7 +131,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group_rule": { "description": "test description of rule", @@ -190,7 +190,7 @@ func TestCreateAnyProtocol(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group_rule": { "description": "test description of rule", @@ -252,7 +252,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_group_rule": { "direction": "egress", diff --git a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go index 50f479b9d1..2d8df70180 100644 --- a/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go +++ b/openstack/networking/v2/extensions/subnetpools/testing/requests_test.go @@ -24,7 +24,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SubnetPoolsListResult) + fmt.Fprint(w, SubnetPoolsListResult) }) count := 0 @@ -66,7 +66,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SubnetPoolGetResult) + fmt.Fprint(w, SubnetPoolGetResult) }) s, err := subnetpools.Get(context.TODO(), fake.ServiceClient(), "0a738452-8057-4ad3-89c2-92f6a74afa76").Extract() @@ -106,7 +106,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetPoolCreateResult) + fmt.Fprint(w, SubnetPoolCreateResult) }) opts := subnetpools.CreateOpts{ @@ -148,7 +148,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SubnetPoolUpdateResponse) + fmt.Fprint(w, SubnetPoolUpdateResponse) }) nullString := "" diff --git a/openstack/networking/v2/extensions/testing/delegate_test.go b/openstack/networking/v2/extensions/testing/delegate_test.go index eb130cd4b0..9f3421f272 100644 --- a/openstack/networking/v2/extensions/testing/delegate_test.go +++ b/openstack/networking/v2/extensions/testing/delegate_test.go @@ -23,7 +23,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "extensions": [ { @@ -83,7 +83,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "extension": { "updated": "2013-02-03T10:00:00-00:00", diff --git a/openstack/networking/v2/extensions/trunks/testing/requests_test.go b/openstack/networking/v2/extensions/trunks/testing/requests_test.go index bc4a2341ab..81e6bef8c2 100644 --- a/openstack/networking/v2/extensions/trunks/testing/requests_test.go +++ b/openstack/networking/v2/extensions/trunks/testing/requests_test.go @@ -25,7 +25,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) iTrue := true @@ -73,7 +73,7 @@ func TestCreateNoSubports(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateNoSubportsResponse) + fmt.Fprint(w, CreateNoSubportsResponse) }) iTrue := true @@ -115,7 +115,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) client := fake.ServiceClient() @@ -153,7 +153,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) n, err := trunks.Get(context.TODO(), fake.ServiceClient(), "f6a9718c-5a64-43e3-944f-4deccad8e78c").Extract() @@ -177,7 +177,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) iFalse := false @@ -207,7 +207,7 @@ func TestGetSubports(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListSubportsResponse) + fmt.Fprint(w, ListSubportsResponse) }) client := fake.ServiceClient() @@ -259,7 +259,7 @@ func TestAddSubports(t *testing.T) { th.TestJSONRequest(t, r, AddSubportsRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AddSubportsResponse) + fmt.Fprint(w, AddSubportsResponse) }) client := fake.ServiceClient() @@ -287,7 +287,7 @@ func TestRemoveSubports(t *testing.T) { th.TestJSONRequest(t, r, RemoveSubportsRequest) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, RemoveSubportsResponse) + fmt.Fprint(w, RemoveSubportsResponse) }) client := fake.ServiceClient() diff --git a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go index d298894de7..dc5d55043a 100644 --- a/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vlantransparent/testing/requests_test.go @@ -23,7 +23,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NetworksVLANTransparentListResult) + fmt.Fprint(w, NetworksVLANTransparentListResult) }) type networkVLANTransparentExt struct { @@ -59,7 +59,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NetworksVLANTransparentGetResult) + fmt.Fprint(w, NetworksVLANTransparentGetResult) }) var s struct { @@ -94,7 +94,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, NetworksVLANTransparentCreateResult) + fmt.Fprint(w, NetworksVLANTransparentCreateResult) }) iTrue := true @@ -139,7 +139,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, NetworksVLANTransparentUpdateResult) + fmt.Fprint(w, NetworksVLANTransparentUpdateResult) }) iFalse := false diff --git a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go index 806580dd4e..dbf3ba515e 100644 --- a/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/endpointgroups/testing/requests_test.go @@ -36,7 +36,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "endpoint_group": { "description": "", @@ -90,7 +90,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "endpoint_group": { "description": "", @@ -135,7 +135,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "endpoint_groups": [ { @@ -224,7 +224,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "endpoint_group": { "description": "updated description", diff --git a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go index 74b9427cc1..e8e63dfde7 100644 --- a/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ikepolicies/testing/requests_test.go @@ -35,7 +35,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ikepolicy":{ "name": "policy", @@ -97,7 +97,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ikepolicy":{ "name": "policy", @@ -165,7 +165,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ikepolicies": [ { @@ -253,7 +253,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ikepolicy": { "name": "updatedname", diff --git a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go index 0363c7c63e..35c09a4ad5 100644 --- a/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/ipsecpolicies/testing/requests_test.go @@ -41,7 +41,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ipsecpolicy": { "name": "ipsecpolicy1", @@ -111,7 +111,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ipsecpolicy": { "name": "ipsecpolicy1", @@ -180,7 +180,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ipsecpolicies": [ { @@ -268,7 +268,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ipsecpolicy": { diff --git a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go index ce9e50171e..c0a33e8d0f 100644 --- a/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/services/testing/requests_test.go @@ -36,7 +36,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "vpnservice": { "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", @@ -91,7 +91,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "vpnservices":[ { @@ -154,7 +154,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "vpnservice": { "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", @@ -223,7 +223,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "vpnservice": { "router_id": "66e3b16c-8ce5-40fb-bb49-ab6d8dc3f2aa", diff --git a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go index 68977acee4..58279b75d5 100644 --- a/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go +++ b/openstack/networking/v2/extensions/vpnaas/siteconnections/testing/requests_test.go @@ -45,7 +45,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ipsec_site_connection": { "status": "PENDING_CREATE", @@ -150,7 +150,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ipsec_site_connection": { "status": "PENDING_CREATE", @@ -227,7 +227,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ipsec_site_connections":[ { @@ -336,7 +336,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "ipsec_site_connection": { diff --git a/openstack/networking/v2/networks/testing/requests_test.go b/openstack/networking/v2/networks/testing/requests_test.go index c28dd2ed0f..f4e905a64f 100644 --- a/openstack/networking/v2/networks/testing/requests_test.go +++ b/openstack/networking/v2/networks/testing/requests_test.go @@ -25,7 +25,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) client := fake.ServiceClient() @@ -62,7 +62,7 @@ func TestListWithExtensions(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) client := fake.ServiceClient() @@ -101,7 +101,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) n, err := networks.Get(context.TODO(), fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() @@ -122,7 +122,7 @@ func TestGetWithExtensions(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) var networkWithExtensions struct { @@ -150,7 +150,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) iTrue := true @@ -176,7 +176,7 @@ func TestCreateWithOptionalFields(t *testing.T) { th.TestJSONRequest(t, r, CreateOptionalFieldsRequest) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) iTrue := true @@ -205,7 +205,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) iTrue, iFalse := true, false @@ -237,7 +237,7 @@ func TestUpdateRevision(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) th.Mux.HandleFunc("/v2.0/networks/4e8e5957-649f-477b-9e5b-f1f75b21c03d", func(w http.ResponseWriter, r *http.Request) { @@ -251,7 +251,7 @@ func TestUpdateRevision(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) iTrue, iFalse := true, false @@ -293,7 +293,7 @@ func TestCreatePortSecurity(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreatePortSecurityResponse) + fmt.Fprint(w, CreatePortSecurityResponse) }) var networkWithExtensions struct { @@ -330,7 +330,7 @@ func TestUpdatePortSecurity(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdatePortSecurityResponse) + fmt.Fprint(w, UpdatePortSecurityResponse) }) var networkWithExtensions struct { diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index 7c3569b70b..1b77780610 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -27,7 +27,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) count := 0 @@ -86,7 +86,7 @@ func TestListWithExtensions(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListResponse) + fmt.Fprint(w, ListResponse) }) type portWithExt struct { @@ -117,7 +117,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) n, err := ports.Get(context.TODO(), fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract() @@ -152,7 +152,7 @@ func TestGetWithExtensions(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetResponse) + fmt.Fprint(w, GetResponse) }) var portWithExtensions struct { @@ -181,7 +181,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) asu := true @@ -231,7 +231,7 @@ func TestCreateOmitSecurityGroups(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateOmitSecurityGroupsResponse) + fmt.Fprint(w, CreateOmitSecurityGroupsResponse) }) asu := true @@ -280,7 +280,7 @@ func TestCreateWithNoSecurityGroup(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateWithNoSecurityGroupsResponse) + fmt.Fprint(w, CreateWithNoSecurityGroupsResponse) }) asu := true @@ -329,7 +329,7 @@ func TestCreateWithPropagateUplinkStatus(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreatePropagateUplinkStatusResponse) + fmt.Fprint(w, CreatePropagateUplinkStatusResponse) }) asu := true @@ -374,7 +374,7 @@ func TestCreateWithValueSpecs(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateValueSpecResponse) + fmt.Fprint(w, CreateValueSpecResponse) }) asu := true @@ -427,7 +427,7 @@ func TestCreateWithInvalidValueSpecs(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateValueSpecResponse) + fmt.Fprint(w, CreateValueSpecResponse) }) asu := true @@ -483,7 +483,7 @@ func TestCreatePortSecurity(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreatePortSecurityResponse) + fmt.Fprint(w, CreatePortSecurityResponse) }) var portWithExt struct { @@ -531,7 +531,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) name := "new_port_name" @@ -573,7 +573,7 @@ func TestUpdateOmitSecurityGroups(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateOmitSecurityGroupsResponse) + fmt.Fprint(w, UpdateOmitSecurityGroupsResponse) }) name := "new_port_name" @@ -614,7 +614,7 @@ func TestUpdatePropagateUplinkStatus(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdatePropagateUplinkStatusResponse) + fmt.Fprint(w, UpdatePropagateUplinkStatusResponse) }) propagateUplinkStatus := true @@ -642,7 +642,7 @@ func TestUpdateValueSpecs(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateValueSpecsResponse) + fmt.Fprint(w, UpdateValueSpecsResponse) }) options := ports.UpdateOpts{ @@ -669,7 +669,7 @@ func TestUpdatePortSecurity(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdatePortSecurityResponse) + fmt.Fprint(w, UpdatePortSecurityResponse) }) var portWithExt struct { @@ -707,7 +707,7 @@ func TestUpdateRevision(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(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") @@ -720,7 +720,7 @@ func TestUpdateRevision(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateResponse) + fmt.Fprint(w, UpdateResponse) }) name := "new_port_name" @@ -757,7 +757,7 @@ func TestRemoveSecurityGroups(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, RemoveSecurityGroupResponse) + fmt.Fprint(w, RemoveSecurityGroupResponse) }) name := "new_port_name" @@ -799,7 +799,7 @@ func TestRemoveAllowedAddressPairs(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, RemoveAllowedAddressPairsResponse) + fmt.Fprint(w, RemoveAllowedAddressPairsResponse) }) name := "new_port_name" @@ -837,7 +837,7 @@ func TestDontUpdateAllowedAddressPairs(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, DontUpdateAllowedAddressPairsResponse) + fmt.Fprint(w, DontUpdateAllowedAddressPairsResponse) }) name := "new_port_name" @@ -887,7 +887,7 @@ func TestGetWithExtraDHCPOpts(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetWithExtraDHCPOptsResponse) + fmt.Fprint(w, GetWithExtraDHCPOptsResponse) }) var s struct { @@ -933,7 +933,7 @@ func TestCreateWithExtraDHCPOpts(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, CreateWithExtraDHCPOptsResponse) + fmt.Fprint(w, CreateWithExtraDHCPOptsResponse) }) adminStateUp := true @@ -996,7 +996,7 @@ func TestUpdateWithExtraDHCPOpts(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UpdateWithExtraDHCPOptsResponse) + fmt.Fprint(w, UpdateWithExtraDHCPOptsResponse) }) name := "updated-port-with-dhcp-opts" diff --git a/openstack/networking/v2/subnets/testing/requests_test.go b/openstack/networking/v2/subnets/testing/requests_test.go index 1df6fd1ce3..41ec607cd8 100644 --- a/openstack/networking/v2/subnets/testing/requests_test.go +++ b/openstack/networking/v2/subnets/testing/requests_test.go @@ -23,7 +23,7 @@ func TestList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SubnetListResult) + fmt.Fprint(w, SubnetListResult) }) count := 0 @@ -65,7 +65,7 @@ func TestGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, SubnetGetResult) + fmt.Fprint(w, SubnetGetResult) }) s, err := subnets.Get(context.TODO(), fake.ServiceClient(), "54d6f61d-db07-451c-9ab3-b9609b6b6f0b").Extract() @@ -104,7 +104,7 @@ func TestCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetCreateResult) + fmt.Fprint(w, SubnetCreateResult) }) var gatewayIP = "192.168.199.1" @@ -166,7 +166,7 @@ func TestCreateNoGateway(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetCreateWithNoGatewayResponse) + fmt.Fprint(w, SubnetCreateWithNoGatewayResponse) }) var noGateway = "" @@ -217,7 +217,7 @@ func TestCreateDefaultGateway(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetCreateWithDefaultGatewayResponse) + fmt.Fprint(w, SubnetCreateWithDefaultGatewayResponse) }) opts := subnets.CreateOpts{ @@ -266,7 +266,7 @@ func TestCreateIPv6RaAddressMode(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetCreateWithIPv6RaAddressModeResponse) + fmt.Fprint(w, SubnetCreateWithIPv6RaAddressModeResponse) }) var gatewayIP = "2001:db8:0:a::1" @@ -307,7 +307,7 @@ func TestCreateWithNoCIDR(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetCreateResult) + fmt.Fprint(w, SubnetCreateResult) }) opts := subnets.CreateOpts{ @@ -356,7 +356,7 @@ func TestCreateWithPrefixlen(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetCreateResult) + fmt.Fprint(w, SubnetCreateResult) }) opts := subnets.CreateOpts{ @@ -423,7 +423,7 @@ func TestUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetUpdateResponse) + fmt.Fprint(w, SubnetUpdateResponse) }) dnsNameservers := []string{"foo"} @@ -456,7 +456,7 @@ func TestUpdateGateway(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetUpdateGatewayResponse) + fmt.Fprint(w, SubnetUpdateGatewayResponse) }) var gatewayIP = "10.0.0.1" @@ -487,7 +487,7 @@ func TestUpdateRemoveGateway(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetUpdateRemoveGatewayResponse) + fmt.Fprint(w, SubnetUpdateRemoveGatewayResponse) }) var noGateway = "" @@ -518,7 +518,7 @@ func TestUpdateHostRoutes(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetUpdateHostRoutesResponse) + fmt.Fprint(w, SubnetUpdateHostRoutesResponse) }) HostRoutes := []subnets.HostRoute{ @@ -555,7 +555,7 @@ func TestUpdateRemoveHostRoutes(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetUpdateRemoveHostRoutesResponse) + fmt.Fprint(w, SubnetUpdateRemoveHostRoutesResponse) }) noHostRoutes := []subnets.HostRoute{} @@ -584,7 +584,7 @@ func TestUpdateAllocationPool(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetUpdateAllocationPoolResponse) + fmt.Fprint(w, SubnetUpdateAllocationPoolResponse) }) name := "my_new_subnet" @@ -625,7 +625,7 @@ func TestUpdateRevision(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetUpdateResponse) + fmt.Fprint(w, SubnetUpdateResponse) }) th.Mux.HandleFunc("/v2.0/subnets/08eae331-0402-425a-923c-34f7cfe39c1c", func(w http.ResponseWriter, r *http.Request) { @@ -639,7 +639,7 @@ func TestUpdateRevision(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, SubnetUpdateResponse) + fmt.Fprint(w, SubnetUpdateResponse) }) dnsNameservers := []string{"foo"} diff --git a/openstack/objectstorage/v1/containers/testing/fixtures.go b/openstack/objectstorage/v1/containers/testing/fixtures.go index 0f440472ac..55f4f723b6 100644 --- a/openstack/objectstorage/v1/containers/testing/fixtures.go +++ b/openstack/objectstorage/v1/containers/testing/fixtures.go @@ -56,7 +56,7 @@ func HandleListContainerInfoSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, `[ + fmt.Fprint(w, `[ { "count": 0, "bytes": 0, @@ -69,7 +69,7 @@ func HandleListContainerInfoSuccessfully(t *testing.T) { } ]`) case "janeausten": - fmt.Fprintf(w, `[ + fmt.Fprint(w, `[ { "count": 1, "bytes": 14, @@ -77,7 +77,7 @@ func HandleListContainerInfoSuccessfully(t *testing.T) { } ]`) case "marktwain": - fmt.Fprintf(w, `[]`) + fmt.Fprint(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -158,7 +158,7 @@ func HandleBulkDeleteSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, bulkDeleteResponse) + fmt.Fprint(w, bulkDeleteResponse) }) } diff --git a/openstack/objectstorage/v1/objects/testing/fixtures_test.go b/openstack/objectstorage/v1/objects/testing/fixtures_test.go index fd24937f6c..f4546d6ab0 100644 --- a/openstack/objectstorage/v1/objects/testing/fixtures_test.go +++ b/openstack/objectstorage/v1/objects/testing/fixtures_test.go @@ -61,7 +61,7 @@ func HandleDownloadObjectSuccessfully(t *testing.T, options ...option) { } w.Header().Set("Last-Modified", date.Format(time.RFC1123)) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, "Successful download with Gophercloud") + fmt.Fprint(w, "Successful download with Gophercloud") }) } @@ -118,7 +118,7 @@ func HandleListObjectsInfoSuccessfully(t *testing.T, options ...option) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, `[ + fmt.Fprint(w, `[ { "hash": "451e372e48e0f6b1114fa0724aa79fa1", "last_modified": "2016-08-17T22:11:58.602650", @@ -135,7 +135,7 @@ func HandleListObjectsInfoSuccessfully(t *testing.T, options ...option) { } ]`) case "hello": - fmt.Fprintf(w, `[]`) + fmt.Fprint(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -157,13 +157,13 @@ func HandleListSubdirSuccessfully(t *testing.T) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, `[ + fmt.Fprint(w, `[ { "subdir": "directory/" } ]`) case "directory/": - fmt.Fprintf(w, `[]`) + fmt.Fprint(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -323,7 +323,7 @@ func HandleBulkDeleteSuccessfully(t *testing.T) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, bulkDeleteResponse) + fmt.Fprint(w, bulkDeleteResponse) }) } diff --git a/openstack/objectstorage/v1/swauth/testing/fixtures_test.go b/openstack/objectstorage/v1/swauth/testing/fixtures_test.go index b8e0b33f24..01bbfd1a84 100644 --- a/openstack/objectstorage/v1/swauth/testing/fixtures_test.go +++ b/openstack/objectstorage/v1/swauth/testing/fixtures_test.go @@ -24,6 +24,6 @@ func HandleAuthSuccessfully(t *testing.T, authOpts swauth.AuthOpts) { w.Header().Add("X-Auth-Token", AuthResult.Token) w.Header().Add("X-Storage-Url", AuthResult.StorageURL) - fmt.Fprintf(w, "") + fmt.Fprint(w, "") }) } diff --git a/openstack/orchestration/v1/apiversions/testing/requests_test.go b/openstack/orchestration/v1/apiversions/testing/requests_test.go index ec4ad6d228..d7149f1cbd 100644 --- a/openstack/orchestration/v1/apiversions/testing/requests_test.go +++ b/openstack/orchestration/v1/apiversions/testing/requests_test.go @@ -24,7 +24,7 @@ func TestListVersions(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "versions": [ { diff --git a/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go b/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go index 72527032d8..74a91e4df3 100644 --- a/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go +++ b/openstack/orchestration/v1/buildinfo/testing/fixtures_test.go @@ -41,6 +41,6 @@ func HandleGetSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } diff --git a/openstack/orchestration/v1/stackevents/testing/fixtures_test.go b/openstack/orchestration/v1/stackevents/testing/fixtures_test.go index 4c22428a9a..a0e31b4ba5 100644 --- a/openstack/orchestration/v1/stackevents/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stackevents/testing/fixtures_test.go @@ -128,7 +128,7 @@ func HandleFindSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -250,9 +250,9 @@ func HandleListSuccessfully(t *testing.T, output string) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, output) + fmt.Fprint(w, output) case "93940999-7d40-44ae-8de4-19624e7b8d18": - fmt.Fprintf(w, `{"events":[]}`) + fmt.Fprint(w, `{"events":[]}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -377,9 +377,9 @@ func HandleListResourceEventsSuccessfully(t *testing.T, output string) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, output) + fmt.Fprint(w, output) case "93940999-7d40-44ae-8de4-19624e7b8d18": - fmt.Fprintf(w, `{"events":[]}`) + fmt.Fprint(w, `{"events":[]}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -449,6 +449,6 @@ func HandleGetSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } diff --git a/openstack/orchestration/v1/stackresources/testing/fixtures_test.go b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go index a306d8de8b..fa1e66575e 100644 --- a/openstack/orchestration/v1/stackresources/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stackresources/testing/fixtures_test.go @@ -82,7 +82,7 @@ func HandleFindSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -157,9 +157,9 @@ func HandleListSuccessfully(t *testing.T, output string) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, output) + fmt.Fprint(w, output) case "49181cd6-169a-4130-9455-31185bbfc5bf": - fmt.Fprintf(w, `{"resources":[]}`) + fmt.Fprint(w, `{"resources":[]}`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -227,7 +227,7 @@ func HandleGetSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -256,7 +256,7 @@ func HandleMetadataSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -309,7 +309,7 @@ func HandleListTypesSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -370,7 +370,7 @@ func HandleGetSchemaSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -440,7 +440,7 @@ func HandleGetTemplateSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } diff --git a/openstack/orchestration/v1/stacks/testing/fixtures_test.go b/openstack/orchestration/v1/stacks/testing/fixtures_test.go index 2244b7d89d..9baed3aced 100644 --- a/openstack/orchestration/v1/stacks/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stacks/testing/fixtures_test.go @@ -48,7 +48,7 @@ func HandleCreateSuccessfully(t *testing.T, output string) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) th.TestHeader(t, r, "Accept", "application/json") w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -142,9 +142,9 @@ func HandleListSuccessfully(t *testing.T, output string) { marker := r.Form.Get("marker") switch marker { case "": - fmt.Fprintf(w, output) + fmt.Fprint(w, output) case "db6977b2-27aa-4775-9ae7-6213212d4ada": - fmt.Fprintf(w, `[]`) + fmt.Fprint(w, `[]`) default: t.Fatalf("Unexpected marker: [%s]", marker) } @@ -221,7 +221,7 @@ func HandleGetSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -233,7 +233,7 @@ func HandleFindSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -309,7 +309,7 @@ func HandlePreviewSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -432,6 +432,6 @@ func HandleAbandonSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } diff --git a/openstack/orchestration/v1/stacks/utils_test.go b/openstack/orchestration/v1/stacks/utils_test.go index 07fbb90cba..b7dd86bf0e 100644 --- a/openstack/orchestration/v1/stacks/utils_test.go +++ b/openstack/orchestration/v1/stacks/utils_test.go @@ -68,7 +68,7 @@ func TestFetch(t *testing.T) { th.TestMethod(t, r, "GET") w.Header().Set("Content-Type", "application/jason") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, "Fee-fi-fo-fum") + fmt.Fprint(w, "Fee-fi-fo-fum") }) client := fakeClient{BaseClient: getHTTPClient()} diff --git a/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go b/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go index 5b9c9ad345..2e6505702e 100644 --- a/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go +++ b/openstack/orchestration/v1/stacktemplates/testing/fixtures_test.go @@ -48,7 +48,7 @@ func HandleGetSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } @@ -91,6 +91,6 @@ func HandleValidateSuccessfully(t *testing.T, output string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, output) + fmt.Fprint(w, output) }) } diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index e663cab280..8766d88e27 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -277,7 +277,7 @@ func HandleResourceProviderList(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ResourceProvidersBody) + fmt.Fprint(w, ResourceProvidersBody) }) } @@ -289,7 +289,7 @@ func HandleResourceProviderCreate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ResourceProviderCreateBody) + fmt.Fprint(w, ResourceProviderCreateBody) }) } @@ -301,7 +301,7 @@ func HandleResourceProviderGet(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ResourceProviderCreateBody) + fmt.Fprint(w, ResourceProviderCreateBody) }) } @@ -324,7 +324,7 @@ func HandleResourceProviderUpdate(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ResourceProviderUpdateResponse) + fmt.Fprint(w, ResourceProviderUpdateResponse) }) } @@ -339,7 +339,7 @@ func HandleResourceProviderGetUsages(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, UsagesBody) + fmt.Fprint(w, UsagesBody) }) } @@ -354,7 +354,7 @@ func HandleResourceProviderGetInventories(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, InventoriesBody) + fmt.Fprint(w, InventoriesBody) }) } @@ -369,7 +369,7 @@ func HandleResourceProviderGetAllocations(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, AllocationsBody) + fmt.Fprint(w, AllocationsBody) }) } @@ -384,6 +384,6 @@ func HandleResourceProviderGetTraits(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, TraitsBody) + fmt.Fprint(w, TraitsBody) }) } diff --git a/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go b/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go index 949434480f..37826cab70 100644 --- a/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/apiversions/testing/fixtures_test.go @@ -186,7 +186,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ManilaAllAPIVersionsResponse) + fmt.Fprint(w, ManilaAllAPIVersionsResponse) }) } @@ -198,7 +198,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ManilaAPIVersionResponse) + fmt.Fprint(w, ManilaAPIVersionResponse) }) } @@ -210,7 +210,7 @@ func MockGetNoResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ManilaAPIInvalidVersionResponse_1) + fmt.Fprint(w, ManilaAPIInvalidVersionResponse_1) }) } @@ -222,6 +222,6 @@ func MockGetMultipleResponses(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ManilaAPIInvalidVersionResponse_2) + fmt.Fprint(w, ManilaAPIInvalidVersionResponse_2) }) } diff --git a/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go index d6cd25830f..fa18c4cfc9 100644 --- a/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/availabilityzones/testing/fixtures_test.go @@ -17,7 +17,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "availability_zones": [ { diff --git a/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go index 5a6f7f013c..3adbc89b25 100644 --- a/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/errors/testing/fixtures_test.go @@ -37,6 +37,6 @@ func MockCreateResponse(t *testing.T) { th.TestJSONRequest(t, r, createRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) - fmt.Fprintf(w, createResponse) + fmt.Fprint(w, createResponse) }) } diff --git a/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go index 111b6dbf97..123bad35ce 100644 --- a/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/messages/testing/fixtures_test.go @@ -25,7 +25,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "messages": [ { @@ -67,7 +67,7 @@ func MockFilteredListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "messages": [ { @@ -95,7 +95,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "message": { "resource_id": "4336d74f-3bdc-4f27-9657-c01ec63680bf", diff --git a/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go index 00585c9c26..41fa0164c9 100644 --- a/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/replicas/testing/fixtures_test.go @@ -48,7 +48,7 @@ func MockCreateResponse(t *testing.T) { th.TestJSONRequest(t, r, createRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, createResponse) + fmt.Fprint(w, createResponse) }) } @@ -175,7 +175,7 @@ func MockGetResponse(t *testing.T) { 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) + fmt.Fprint(w, getResponse) }) } @@ -330,7 +330,7 @@ func MockListExportLocationsResponse(t *testing.T) { 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) + fmt.Fprint(w, listExportLocationsResponse) }) } @@ -355,6 +355,6 @@ func MockGetExportLocationResponse(t *testing.T) { 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) + fmt.Fprint(w, getExportLocationResponse) }) } diff --git a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go index 88c4b31203..fdd861d9c8 100644 --- a/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/schedulerstats/testing/fixtures_test.go @@ -274,7 +274,7 @@ func HandlePoolsListSuccessfully(t *testing.T) { if err := r.ParseForm(); err != nil { t.Errorf("Failed to parse request form %v", err) } - fmt.Fprintf(w, PoolsListBody) + fmt.Fprint(w, PoolsListBody) }) th.Mux.HandleFunc("/scheduler-stats/pools/detail", func(w http.ResponseWriter, r *http.Request) { @@ -286,6 +286,6 @@ func HandlePoolsListSuccessfully(t *testing.T) { if err := r.ParseForm(); err != nil { t.Errorf("Failed to parse request form %v", err) } - fmt.Fprintf(w, PoolsListBodyDetail) + fmt.Fprint(w, PoolsListBodyDetail) }) } diff --git a/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go index c0b7645770..279fcee4a4 100644 --- a/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/securityservices/testing/fixtures_test.go @@ -30,7 +30,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_service": { "status": "new", @@ -67,7 +67,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_services": [ { @@ -113,7 +113,7 @@ func MockFilteredListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_services": [ { @@ -143,7 +143,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_service": { "status": "new", @@ -169,7 +169,7 @@ func MockUpdateResponse(t *testing.T) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "security_service": { "status": "new", diff --git a/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go index 67f7b94c78..9916b033fc 100644 --- a/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/services/testing/fixtures_test.go @@ -66,6 +66,6 @@ func HandleListSuccessfully(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ServiceListBody) + fmt.Fprint(w, ServiceListBody) }) } diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go index d82014357d..cc63ffac9c 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/fixtures_test.go @@ -40,7 +40,7 @@ func MockGetResponse(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, getResponse) + fmt.Fprint(w, getResponse) }) } diff --git a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go index 4f86a642d4..7566788e40 100644 --- a/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go +++ b/openstack/sharedfilesystems/v2/shareaccessrules/testing/requests_test.go @@ -49,6 +49,6 @@ func MockListResponse(t *testing.T) { th.TestHeader(t, r, "Accept", "application/json") w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, listResponse) + fmt.Fprint(w, listResponse) }) } diff --git a/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go index cfa3b1712d..eb608f9b6a 100644 --- a/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/sharenetworks/testing/fixtures_test.go @@ -55,7 +55,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, createResp("my_network", + fmt.Fprint(w, createResp("my_network", "This is my share network", "998b42ee-2cee-4d36-8b95-67b5ca1f2109", "53482b62-2c84-4a53-b6ab-30d9d9800d06")) @@ -85,7 +85,7 @@ func MockListResponse(t *testing.T) { switch marker { case "": - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "share_networks": [ { "name": "net_my1", @@ -135,7 +135,7 @@ func MockListResponse(t *testing.T) { ] }`) default: - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_networks": [] }`) @@ -157,7 +157,7 @@ func MockFilteredListResponse(t *testing.T) { marker := r.Form.Get("offset") switch marker { case "": - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_networks": [ { @@ -178,7 +178,7 @@ func MockFilteredListResponse(t *testing.T) { ] }`) case "1": - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_networks": [ { @@ -199,7 +199,7 @@ func MockFilteredListResponse(t *testing.T) { ] }`) case "2": - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_networks": [ { @@ -220,7 +220,7 @@ func MockFilteredListResponse(t *testing.T) { ] }`) default: - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_networks": [] }`) @@ -235,7 +235,7 @@ func MockGetResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_network": { "name": "net_my1", @@ -261,7 +261,7 @@ func MockUpdateNeutronResponse(t *testing.T) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_network": { "name": "net_my2", @@ -288,7 +288,7 @@ func MockUpdateNovaResponse(t *testing.T) { th.TestMethod(t, r, "PUT") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_network": { "name": "net_my2", @@ -315,7 +315,7 @@ func MockAddSecurityServiceResponse(t *testing.T) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_network": { "name": "net2", @@ -341,7 +341,7 @@ func MockRemoveSecurityServiceResponse(t *testing.T) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_network": { "name": "net2", diff --git a/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go index cfb9e73d2e..36db8b7a22 100644 --- a/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/shares/testing/fixtures_test.go @@ -84,7 +84,7 @@ func MockCreateResponse(t *testing.T) { th.TestJSONRequest(t, r, createRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, createResponse) + fmt.Fprint(w, createResponse) }) } @@ -158,7 +158,7 @@ func MockUpdateResponse(t *testing.T) { th.TestJSONRequest(t, r, updateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, updateResponse) + fmt.Fprint(w, updateResponse) }) } @@ -211,7 +211,7 @@ func MockGetResponse(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, getResponse) + fmt.Fprint(w, getResponse) }) } @@ -304,7 +304,7 @@ func MockListExportLocationsResponse(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, listExportLocationsResponse) + fmt.Fprint(w, listExportLocationsResponse) }) } @@ -325,7 +325,7 @@ func MockGetExportLocationResponse(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, getExportLocationResponse) + fmt.Fprint(w, getExportLocationResponse) }) } @@ -359,7 +359,7 @@ func MockGrantAccessResponse(t *testing.T) { th.TestJSONRequest(t, r, grantAccessRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, grantAccessResponse) + fmt.Fprint(w, grantAccessResponse) }) } @@ -410,7 +410,7 @@ func MockListAccessRightsResponse(t *testing.T) { th.TestJSONRequest(t, r, listAccessRightsRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, listAccessRightsResponse) + fmt.Fprint(w, listAccessRightsResponse) }) } diff --git a/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go index 980292f38f..342c4c5101 100644 --- a/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/sharetransfers/testing/fixtures_test.go @@ -147,7 +147,7 @@ func HandleCreateTransfer(t *testing.T) { th.TestJSONRequest(t, r, CreateRequest) w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, CreateResponse) + fmt.Fprint(w, CreateResponse) }) } @@ -179,7 +179,7 @@ func HandleListTransfers(t *testing.T) { th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -191,7 +191,7 @@ func HandleListTransfersDetail(t *testing.T) { th.TestFormValues(t, r, map[string]string{"all_tenants": "true"}) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) + fmt.Fprint(w, ListOutput) }) } @@ -202,6 +202,6 @@ func HandleGetTransfer(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, GetOutput) + fmt.Fprint(w, GetOutput) }) } diff --git a/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go index 767d42807a..17fb21fa78 100644 --- a/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/sharetypes/testing/fixtures_test.go @@ -30,7 +30,7 @@ func MockCreateResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume_type": { "os-share-type-access:is_public": true, @@ -76,7 +76,7 @@ func MockListResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume_types": [ { @@ -141,7 +141,7 @@ func MockGetDefaultResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "volume_type": { "required_extra_specs": null, @@ -172,7 +172,7 @@ func MockGetExtraSpecsResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "extra_specs": { "snapshot_support": "True", @@ -199,7 +199,7 @@ func MockSetExtraSpecsResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "extra_specs": { "my_key": "my_value" @@ -223,7 +223,7 @@ func MockShowAccessResponse(t *testing.T) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "share_type_access": [ { diff --git a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go index e34d1081a4..88e94c371a 100644 --- a/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go +++ b/openstack/sharedfilesystems/v2/snapshots/testing/fixtures_test.go @@ -59,7 +59,7 @@ func MockCreateResponse(t *testing.T) { th.TestJSONRequest(t, r, createRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) - fmt.Fprintf(w, createResponse) + fmt.Fprint(w, createResponse) }) } @@ -114,7 +114,7 @@ func MockUpdateResponse(t *testing.T) { th.TestJSONRequest(t, r, updateRequest) w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, updateResponse) + fmt.Fprint(w, updateResponse) }) } @@ -150,7 +150,7 @@ func MockGetResponse(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, getResponse) + fmt.Fprint(w, getResponse) }) } diff --git a/openstack/testing/client_test.go b/openstack/testing/client_test.go index 2c158dbfc3..75192a524c 100644 --- a/openstack/testing/client_test.go +++ b/openstack/testing/client_test.go @@ -46,7 +46,7 @@ func TestAuthenticatedClientV3(t *testing.T) { w.Header().Add("X-Subject-Token", ID) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, `{ "token": { "expires_at": "2013-02-02T18:30:59.000000Z" } }`) + fmt.Fprint(w, `{ "token": { "expires_at": "2013-02-02T18:30:59.000000Z" } }`) }) options := gophercloud.AuthOptions{ @@ -91,7 +91,7 @@ func TestAuthenticatedClientV2(t *testing.T) { }) th.Mux.HandleFunc("/v2.0/tokens", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "access": { "token": { @@ -197,7 +197,7 @@ func TestIdentityAdminV3Client(t *testing.T) { w.Header().Add("X-Subject-Token", ID) w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "token": { "audit_ids": ["VcxU2JYqT8OzfUVvrjEITQ", "qNUTIJntTzO1-XUk5STybw"], diff --git a/openstack/workflow/v2/crontriggers/testing/requests_test.go b/openstack/workflow/v2/crontriggers/testing/requests_test.go index fd3bac58e0..8267e2bc79 100644 --- a/openstack/workflow/v2/crontriggers/testing/requests_test.go +++ b/openstack/workflow/v2/crontriggers/testing/requests_test.go @@ -25,7 +25,7 @@ func TestCreateCronTrigger(t *testing.T) { w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "created_at": "2018-09-12 15:48:18", "first_execution_time": "2018-09-12 17:48:00", @@ -110,7 +110,7 @@ func TestGetCronTrigger(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "created_at": "2018-09-12 15:48:18", "first_execution_time": "2018-09-12 17:48:00", @@ -195,7 +195,7 @@ func TestListCronTriggers(t *testing.T) { "next": "%s/cron_triggers?marker=0520ffd8-f7f1-4f2e-845b-55d953a1cf46" }`, th.Server.URL) case "0520ffd8-f7f1-4f2e-845b-55d953a1cf46": - fmt.Fprintf(w, `{ "cron_triggers": [] }`) + fmt.Fprint(w, `{ "cron_triggers": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } diff --git a/openstack/workflow/v2/executions/testing/requests_test.go b/openstack/workflow/v2/executions/testing/requests_test.go index d0b4641793..86a4700457 100644 --- a/openstack/workflow/v2/executions/testing/requests_test.go +++ b/openstack/workflow/v2/executions/testing/requests_test.go @@ -25,7 +25,7 @@ func TestCreateExecution(t *testing.T) { w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "created_at": "2018-09-12 14:48:49", "description": "description", @@ -92,7 +92,7 @@ func TestGetExecution(t *testing.T) { th.TestHeader(t, r, "X-Auth-token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "created_at": "2018-09-12 14:48:49", "description": "description", @@ -189,7 +189,7 @@ func TestListExecutions(t *testing.T) { "next": "%s/executions?marker=50bb59f1-eb77-4017-a77f-6d575b002667" }`, th.Server.URL) case "50bb59f1-eb77-4017-a77f-6d575b002667": - fmt.Fprintf(w, `{ "executions": [] }`) + fmt.Fprint(w, `{ "executions": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } diff --git a/openstack/workflow/v2/workflows/testing/requests_test.go b/openstack/workflow/v2/workflows/testing/requests_test.go index 78f0f78a09..a5d656685f 100644 --- a/openstack/workflow/v2/workflows/testing/requests_test.go +++ b/openstack/workflow/v2/workflows/testing/requests_test.go @@ -46,11 +46,11 @@ workflow_echo: w.WriteHeader(http.StatusCreated) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "workflows": [ { "created_at": "2018-09-12 15:48:17", - "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<%% $.msg %%>\"", + "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", "id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "input": "msg", "name": "workflow_echo", @@ -118,10 +118,10 @@ func TestGetWorkflow(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-token", fake.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` + fmt.Fprint(w, ` { "created_at": "2018-09-12 15:48:17", - "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<%% $.msg %%>\"", + "definition": "---\nversion: '2.0'\n\nworkflow_echo:\n description: Simple workflow example\n type: direct\n\n input:\n - msg\n\n tasks:\n test:\n action: std.echo output=\"<% $.msg %>\"", "id": "604a3a1e-94e3-4066-a34a-aa56873ef236", "input": "msg", "name": "workflow_echo", @@ -187,7 +187,7 @@ func TestListWorkflows(t *testing.T) { ] }`, th.Server.URL) case "604a3a1e-94e3-4066-a34a-aa56873ef236": - fmt.Fprintf(w, `{ "workflows": [] }`) + fmt.Fprint(w, `{ "workflows": [] }`) default: t.Fatalf("Unexpected marker: [%s]", marker) } diff --git a/pagination/testing/linked_test.go b/pagination/testing/linked_test.go index 25166ad861..e28c2b0483 100644 --- a/pagination/testing/linked_test.go +++ b/pagination/testing/linked_test.go @@ -45,7 +45,7 @@ func createLinked() pagination.Pager { th.Mux.HandleFunc("/page3", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ "ints": [7, 8, 9], "links": { "next": null } }`) + fmt.Fprint(w, `{ "ints": [7, 8, 9], "links": { "next": null } }`) }) client := createClient() diff --git a/pagination/testing/marker_test.go b/pagination/testing/marker_test.go index 45c943e5b7..a41915175d 100644 --- a/pagination/testing/marker_test.go +++ b/pagination/testing/marker_test.go @@ -46,11 +46,11 @@ func createMarkerPaged(t *testing.T) pagination.Pager { ms := r.Form["marker"] switch { case len(ms) == 0: - fmt.Fprintf(w, "aaa\nbbb\nccc") + fmt.Fprint(w, "aaa\nbbb\nccc") case len(ms) == 1 && ms[0] == "ccc": - fmt.Fprintf(w, "ddd\neee\nfff") + fmt.Fprint(w, "ddd\neee\nfff") case len(ms) == 1 && ms[0] == "fff": - fmt.Fprintf(w, "ggg\nhhh\niii") + fmt.Fprint(w, "ggg\nhhh\niii") case len(ms) == 1 && ms[0] == "iii": w.WriteHeader(http.StatusNoContent) default: diff --git a/pagination/testing/single_test.go b/pagination/testing/single_test.go index 6bcd930cb2..090324a573 100644 --- a/pagination/testing/single_test.go +++ b/pagination/testing/single_test.go @@ -38,7 +38,7 @@ func setupSinglePaged() pagination.Pager { th.Mux.HandleFunc("/only", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ "ints": [1, 2, 3] }`) + fmt.Fprint(w, `{ "ints": [1, 2, 3] }`) }) createPage := func(r pagination.PageResult) pagination.Page { diff --git a/testhelper/fixture/helper.go b/testhelper/fixture/helper.go index a0f3b5c7b2..7c84378770 100644 --- a/testhelper/fixture/helper.go +++ b/testhelper/fixture/helper.go @@ -25,7 +25,7 @@ func SetupHandler(t *testing.T, url, method, requestBody, responseBody string, s w.WriteHeader(status) if responseBody != "" { - fmt.Fprintf(w, responseBody) + fmt.Fprint(w, responseBody) } }) } diff --git a/testing/provider_client_test.go b/testing/provider_client_test.go index 18704fd4b1..44fc37756c 100644 --- a/testing/provider_client_test.go +++ b/testing/provider_client_test.go @@ -99,7 +99,7 @@ func TestConcurrentReauth(t *testing.T) { } w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) wg := new(sync.WaitGroup) @@ -276,7 +276,7 @@ func TestRequestThatCameDuringReauthWaitsUntilItIsCompleted(t *testing.T) { } w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{}`) + fmt.Fprint(w, `{}`) }) wg := new(sync.WaitGroup) From 0d4b68d9c7a4fe5e0461bff8d423efd6819fa7fe Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 28 Nov 2024 14:11:35 +0000 Subject: [PATCH 1995/2296] make: Bump golangci-lint We also add the '-v' and '--max-same-issues' flags so that we get some indication that things are running and show more output. Signed-off-by: Stephen Finucane --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 066946cf8d..2a0618a6b6 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ undefine GOFLAGS -GOLANGCI_LINT_VERSION?=v1.57.1 -GO_TEST?=go run gotest.tools/gotestsum@latest --format testname +GOLANGCI_LINT_VERSION?=v1.62.2 +GO_TEST?=go run gotest.tools/gotestsum@latest --format testname -- ifeq ($(shell command -v podman 2> /dev/null),) RUNNER=docker @@ -23,7 +23,7 @@ lint: -v ~/.cache/golangci-lint/$(GOLANGCI_LINT_VERSION):/root/.cache \ -w /app \ -e GOFLAGS="-tags=acceptance" \ - golangci/golangci-lint:$(GOLANGCI_LINT_VERSION) golangci-lint run + golangci/golangci-lint:$(GOLANGCI_LINT_VERSION) golangci-lint run -v --max-same-issues 50 .PHONY: lint format: From bdfd59d2de6e136237b66a95561813d0c94b37c0 Mon Sep 17 00:00:00 2001 From: Derek Higgins Date: Wed, 13 Nov 2024 10:28:17 +0000 Subject: [PATCH 1996/2296] Add support for disable_power_off Added in baremetal api v1.95 disable_power_off expcicitly prevents a node from being powered off by ironic. See https://specs.openstack.org/openstack/ironic-specs/specs/approved/nc-si.html --- openstack/baremetal/v1/nodes/requests.go | 4 ++++ openstack/baremetal/v1/nodes/results.go | 4 ++++ openstack/baremetal/v1/nodes/testing/fixtures_test.go | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 8cb0de9e05..8b2eb4b218 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -274,6 +274,10 @@ type CreateOpts struct { // Static network configuration to use during deployment and cleaning. NetworkData map[string]any `json:"network_data,omitempty"` + + // Whether disable_power_off is enabled or disabled on this node. + // Requires microversion 1.95 or later. + DisablePowerOff *bool `json:"disable_power_off,omitempty"` } // ToNodeCreateMap assembles a request body based on the contents of a CreateOpts. diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 22babe02d3..507e0d6a4f 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -314,6 +314,10 @@ 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"` + + // Whether disable_power_off is enabled or disabled on this node. + // Requires microversion 1.95 or later. + DisablePowerOff bool `json:"disable_power_off"` } // NodePage abstracts the raw results of making a List() request against diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index 10589c5f1b..88abebcebb 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -87,6 +87,7 @@ const NodeListDetailBody = ` "created_at": "2019-01-31T19:59:28+00:00", "deploy_interface": "iscsi", "deploy_step": {}, + "disable_power_off": false, "driver": "ipmi", "driver_info": { "ipmi_port": "6230", @@ -195,6 +196,7 @@ const NodeListDetailBody = ` "created_at": "2019-01-31T19:59:29+00:00", "deploy_interface": "iscsi", "deploy_step": {}, + "disable_power_off": false, "driver": "ipmi", "driver_info": {}, "driver_internal_info": {}, @@ -295,6 +297,7 @@ const NodeListDetailBody = ` "created_at": "2019-01-31T19:59:30+00:00", "deploy_interface": "iscsi", "deploy_step": {}, + "disable_power_off": true, "driver": "ipmi", "driver_info": {}, "driver_internal_info": {}, @@ -1123,6 +1126,7 @@ var ( InspectionFinishedAt: &InspectionFinishedAt, Retired: false, RetiredReason: "No longer needed", + DisablePowerOff: false, Links: []nodes.Link{ {Href: "http://ironic.example.com:6385/v1/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", Rel: "self"}, {Href: "http://ironic.example.com:6385/nodes/08c84581-58f5-4ea2-a0c6-dd2e5d2b3662", Rel: "bookmark"}, @@ -1192,6 +1196,7 @@ var ( UpdatedAt: updatedAt, Retired: false, RetiredReason: "No longer needed", + DisablePowerOff: true, Links: []nodes.Link{ {Href: "http://ironic.example.com:6385/v1/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", Rel: "self"}, {Href: "http://ironic.example.com:6385/nodes/c9afd385-5d89-4ecb-9e1c-68194da6b474", Rel: "bookmark"}, From c5e3f8e32a54a33d2516cce3ff50e6fe7ad5c01c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:00:56 +0000 Subject: [PATCH 1997/2296] build(deps): bump golang.org/x/crypto from 0.29.0 to 0.30.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.29.0 to 0.30.0. - [Commits](https://github.com/golang/crypto/compare/v0.29.0...v0.30.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 45961985de..dc5d92e323 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.22 require ( - golang.org/x/crypto v0.29.0 + golang.org/x/crypto v0.30.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.27.0 // indirect +require golang.org/x/sys v0.28.0 // indirect diff --git a/go.sum b/go.sum index a14f597436..1812f7828d 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 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 88ca397dfe8d28f868cf6a703d91215df04a8dee Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 6 Dec 2024 16:03:36 +0100 Subject: [PATCH 1998/2296] labels: Add edit:testinfra The `edit:testinfra` label was added to `labeler.yaml` for automatic labeling based on path, but not to the list of managed labels. With this change `edit:testinfra` is added to the list of managed labels, so that automation doesn't fight to create and remove it. --- .github/labels.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/labels.yaml b/.github/labels.yaml index ad21863831..f0f9eefd80 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -105,6 +105,9 @@ - color: '000000' description: This PR updates sharedfilesystems code name: edit:sharedfilesystems +- color: '000000' + description: This PR updates testing infrastructure code + name: edit:testinfra - color: '000000' description: This PR updates testing code name: edit:testing From d5ca800b92dc7a65bb8d9a24bcf4755f9afbdaca Mon Sep 17 00:00:00 2001 From: chuliang Date: Mon, 4 Sep 2023 13:56:18 +0000 Subject: [PATCH 1999/2296] identity: Add Get endpoint by ID Co-Authored-By: chuliang Co-Authored-By: Pierre Prinetti Co-Authored-By: Stephen Finucane --- .../openstack/identity/v3/endpoint_test.go | 23 +++ openstack/identity/v3/endpoints/requests.go | 7 + openstack/identity/v3/endpoints/results.go | 6 + .../v3/endpoints/testing/requests_test.go | 169 +++++++++++------- openstack/identity/v3/projects/doc.go | 2 +- 5 files changed, 137 insertions(+), 70 deletions(-) diff --git a/internal/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go index ae2254102a..8dd9e5bad1 100644 --- a/internal/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -39,6 +39,29 @@ func TestEndpointsList(t *testing.T) { th.AssertEquals(t, found, true) } +func TestEndpointsGet(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + allPages, err := endpoints.List(client, nil).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allEndpoints, err := endpoints.ExtractEndpoints(allPages) + th.AssertNoErr(t, err) + + endpoint := allEndpoints[0] + e, err := endpoints.Get(context.TODO(), client, endpoint.ID).Extract() + if err != nil { + t.Fatalf("Unable to get endpoint: %v", err) + } + + tools.PrintResource(t, e) + + th.AssertEquals(t, e.Name, e.Name) +} + func TestEndpointsNavigateCatalog(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 35eb966077..4386bb9bc9 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -90,6 +90,13 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } +// Get retrieves details on a single endpoint, by ID. +func Get(ctx context.Context, client *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := client.Get(ctx, 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]any, error) diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index 9efec30af2..19d279ebc6 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 { diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 348a1f1c6e..ad138acd49 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -20,35 +20,31 @@ func TestCreateSuccessful(t *testing.T) { th.Mux.HandleFunc("/endpoints", 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, ` - { - "endpoint": { - "interface": "public", - "name": "the-endiest-of-points", - "region": "underground", - "url": "https://1.2.3.4:9000/", - "service_id": "asdfasdfasdfasdf" - } - } - `) + th.TestJSONRequest(t, r, `{ + "endpoint": { + "interface": "public", + "name": "the-endiest-of-points", + "region": "underground", + "url": "https://1.2.3.4:9000/", + "service_id": "asdfasdfasdfasdf" + } + }`) w.WriteHeader(http.StatusCreated) - fmt.Fprint(w, ` - { - "endpoint": { - "id": "12", - "interface": "public", - "enabled": true, - "links": { - "self": "https://localhost:5000/v3/endpoints/12" - }, - "name": "the-endiest-of-points", - "region": "underground", - "service_id": "asdfasdfasdfasdf", - "url": "https://1.2.3.4:9000/" - } - } - `) + fmt.Fprint(w, `{ + "endpoint": { + "id": "12", + "interface": "public", + "enabled": true, + "links": { + "self": "https://localhost:5000/v3/endpoints/12" + }, + "name": "the-endiest-of-points", + "region": "underground", + "service_id": "asdfasdfasdfasdf", + "url": "https://1.2.3.4:9000/" + } + }`) }) actual, err := endpoints.Create(context.TODO(), client.ServiceClient(), endpoints.CreateOpts{ @@ -82,40 +78,38 @@ func TestListEndpoints(t *testing.T) { th.TestHeader(t, r, "X-Auth-Token", client.TokenID) w.Header().Add("Content-Type", "application/json") - fmt.Fprint(w, ` - { - "endpoints": [ - { - "id": "12", - "interface": "public", - "enabled": true, - "links": { - "self": "https://localhost:5000/v3/endpoints/12" - }, - "name": "the-endiest-of-points", - "region": "underground", - "service_id": "asdfasdfasdfasdf", - "url": "https://1.2.3.4:9000/" + fmt.Fprint(w, `{ + "endpoints": [ + { + "id": "12", + "interface": "public", + "enabled": true, + "links": { + "self": "https://localhost:5000/v3/endpoints/12" }, - { - "id": "13", - "interface": "internal", - "enabled": false, - "links": { - "self": "https://localhost:5000/v3/endpoints/13" - }, - "name": "shhhh", - "region": "underground", - "service_id": "asdfasdfasdfasdf", - "url": "https://1.2.3.4:9001/" - } - ], - "links": { - "next": null, - "previous": null + "name": "the-endiest-of-points", + "region": "underground", + "service_id": "asdfasdfasdfasdf", + "url": "https://1.2.3.4:9000/" + }, + { + "id": "13", + "interface": "internal", + "enabled": false, + "links": { + "self": "https://localhost:5000/v3/endpoints/13" + }, + "name": "shhhh", + "region": "underground", + "service_id": "asdfasdfasdfasdf", + "url": "https://1.2.3.4:9001/" } + ], + "links": { + "next": null, + "previous": null } - `) + }`) }) count := 0 @@ -154,6 +148,47 @@ func TestListEndpoints(t *testing.T) { th.AssertEquals(t, 1, count) } +func TestGetEndpoint(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/endpoints/12", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + fmt.Fprint(w, `{ + "endpoint": { + "id": "12", + "interface": "public", + "enabled": true, + "links": { + "self": "https://localhost:5000/v3/endpoints/12" + }, + "name": "the-endiest-of-points", + "region": "underground", + "service_id": "asdfasdfasdfasdf", + "url": "https://1.2.3.4:9000/" + } + }`) + }) + + actual, err := endpoints.Get(context.TODO(), client.ServiceClient(), "12").Extract() + if err != nil { + t.Fatalf("Unexpected error from Get: %v", err) + } + + expected := &endpoints.Endpoint{ + ID: "12", + Availability: gophercloud.AvailabilityPublic, + Enabled: true, + Name: "the-endiest-of-points", + Region: "underground", + ServiceID: "asdfasdfasdfasdf", + URL: "https://1.2.3.4:9000/", + } + th.AssertDeepEquals(t, expected, actual) +} + func TestUpdateEndpoint(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -161,17 +196,14 @@ func TestUpdateEndpoint(t *testing.T) { th.Mux.HandleFunc("/endpoints/12", 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, ` - { - "endpoint": { - "name": "renamed", + th.TestJSONRequest(t, r, `{ + "endpoint": { + "name": "renamed", "region": "somewhere-else" - } - } - `) + } + }`) - fmt.Fprint(w, ` - { + fmt.Fprint(w, `{ "endpoint": { "id": "12", "interface": "public", @@ -184,8 +216,7 @@ func TestUpdateEndpoint(t *testing.T) { "service_id": "asdfasdfasdfasdf", "url": "https://1.2.3.4:9000/" } - } - `) + }`) }) actual, err := endpoints.Update(context.TODO(), client.ServiceClient(), "12", endpoints.UpdateOpts{ diff --git a/openstack/identity/v3/projects/doc.go b/openstack/identity/v3/projects/doc.go index 6aea466a51..6100327397 100644 --- a/openstack/identity/v3/projects/doc.go +++ b/openstack/identity/v3/projects/doc.go @@ -73,7 +73,7 @@ Example to List all tags of a Project panic(err) } -Example to modify all tags of a Project +Example to modify all tags of a Project projectID := "966b3c7d36a24facaf20b7e458bf2192" tags := ["foo", "bar"] From 97c0a325b35aa66b26724d72afb22d03d7296111 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Mon, 21 Oct 2024 15:46:48 +0200 Subject: [PATCH 2000/2296] [nova] fix pagination of hypervisors.List() This is a mildly backwards-incompatible change, but since most people only use this API on the level of pagination.Pager, it ought not be a big deal in practice. Closes #3222. --- openstack/compute/v2/hypervisors/requests.go | 2 +- openstack/compute/v2/hypervisors/results.go | 15 +++++++- .../v2/hypervisors/testing/fixtures_test.go | 35 ++++++++++++++++--- .../v2/hypervisors/testing/requests_test.go | 9 +++-- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/openstack/compute/v2/hypervisors/requests.go b/openstack/compute/v2/hypervisors/requests.go index 37ecbee17a..b17dd35781 100644 --- a/openstack/compute/v2/hypervisors/requests.go +++ b/openstack/compute/v2/hypervisors/requests.go @@ -53,7 +53,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return HypervisorPage{pagination.SinglePageBase(r)} + return HypervisorPage{pagination.LinkedPageBase{PageResult: r}} }) } diff --git a/openstack/compute/v2/hypervisors/results.go b/openstack/compute/v2/hypervisors/results.go index ee6046c6c0..0a35dfdf0f 100644 --- a/openstack/compute/v2/hypervisors/results.go +++ b/openstack/compute/v2/hypervisors/results.go @@ -240,7 +240,7 @@ func (r *Hypervisor) UnmarshalJSON(b []byte) error { // HypervisorPage represents a single page of all Hypervisors from a List // request. type HypervisorPage struct { - pagination.SinglePageBase + pagination.LinkedPageBase } // IsEmpty determines whether or not a HypervisorPage is empty. @@ -253,6 +253,19 @@ func (page HypervisorPage) IsEmpty() (bool, error) { return len(va) == 0, err } +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. +func (page HypervisorPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"hypervisors_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + // ExtractHypervisors interprets a page of results as a slice of Hypervisors. func ExtractHypervisors(p pagination.Page) ([]Hypervisor, error) { var h struct { diff --git a/openstack/compute/v2/hypervisors/testing/fixtures_test.go b/openstack/compute/v2/hypervisors/testing/fixtures_test.go index fa0b93a3f8..4bdb2c0613 100644 --- a/openstack/compute/v2/hypervisors/testing/fixtures_test.go +++ b/openstack/compute/v2/hypervisors/testing/fixtures_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/hypervisors" + "github.com/gophercloud/gophercloud/v2/testhelper" th "github.com/gophercloud/gophercloud/v2/testhelper" "github.com/gophercloud/gophercloud/v2/testhelper/client" ) @@ -85,8 +86,8 @@ const HypervisorListBodyPre253 = ` ] }` -// HypervisorListBody represents a raw hypervisor list result with Pike+ release. -const HypervisorListBody = ` +// HypervisorListBodyPage1 represents page 1 of a raw hypervisor list result with Pike+ release. +const HypervisorListBodyPage1 = ` { "hypervisors": [ { @@ -127,7 +128,20 @@ const HypervisorListBody = ` }, "vcpus": 1, "vcpus_used": 0 - }, + } + ], + "hypervisors_links": [ + { + "href": "%s/os-hypervisors/detail?marker=c48f6247-abe4-4a24-824e-ea39e108874f", + "rel": "next" + } + ] +}` + +// HypervisorListBodyPage2 represents page 2 of a raw hypervisor list result with Pike+ release. +const HypervisorListBodyPage2 = ` +{ + "hypervisors": [ { "cpu_info": "{\"arch\": \"x86_64\", \"model\": \"Nehalem\", \"vendor\": \"Intel\", \"features\": [\"pge\", \"clflush\"], \"topology\": {\"cores\": 1, \"threads\": 1, \"sockets\": 4}}", "current_workload": 0, @@ -157,6 +171,9 @@ const HypervisorListBody = ` ] }` +// HypervisorListBodyEmpty represents an empty raw hypervisor list result, marking the end of pagination. +const HypervisorListBodyEmpty = `{ "hypervisors": [] }` + // HypervisorListWithParametersBody represents a raw hypervisor list result with Pike+ release. const HypervisorListWithParametersBody = ` { @@ -624,8 +641,16 @@ func HandleHypervisorListSuccessfully(t *testing.T) { th.TestMethod(t, r, "GET") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Content-Type", "application/json") - fmt.Fprint(w, HypervisorListBody) + switch r.URL.Query().Get("marker") { + case "": + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, HypervisorListBodyPage1, testhelper.Server.URL) + case "c48f6247-abe4-4a24-824e-ea39e108874f": + w.Header().Add("Content-Type", "application/json") + fmt.Fprint(w, HypervisorListBodyPage2) + default: + http.Error(w, "unexpected marker value", http.StatusInternalServerError) + } }) } diff --git a/openstack/compute/v2/hypervisors/testing/requests_test.go b/openstack/compute/v2/hypervisors/testing/requests_test.go index 0303f7f1d0..5acf7d0d1a 100644 --- a/openstack/compute/v2/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/hypervisors/testing/requests_test.go @@ -69,19 +69,18 @@ func TestListHypervisors(t *testing.T) { return false, err } - if len(actual) != 2 { - t.Fatalf("Expected 2 hypervisors, got %d", len(actual)) + if len(actual) != 1 { + t.Fatalf("Expected 1 hypervisors on page %d, got %d", pages, len(actual)) } th.CheckDeepEquals(t, HypervisorFake, actual[0]) - th.CheckDeepEquals(t, HypervisorFake, actual[1]) return true, nil }) th.AssertNoErr(t, err) - if pages != 1 { - t.Errorf("Expected 1 page, saw %d", pages) + if pages != 2 { + t.Errorf("Expected 2 pages, saw %d", pages) } } From 19a73fb7933cbc53f9de5304ce63b44c45a7c9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 6 Dec 2024 18:08:53 +0100 Subject: [PATCH 2001/2296] trivial: change order of args for AssertEquals() calls The expected order is "expected, actual" and getting this right helps debugging. --- .../networking/v2/extensions/extensions.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/extensions.go b/internal/acceptance/openstack/networking/v2/extensions/extensions.go index 4de1401f4d..84f020ff38 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/internal/acceptance/openstack/networking/v2/extensions/extensions.go @@ -43,8 +43,8 @@ func CreateExternalNetwork(t *testing.T, client *gophercloud.ServiceClient) (*ne t.Logf("Created external network: %s", networkName) - th.AssertEquals(t, network.Name, networkName) - th.AssertEquals(t, network.Description, networkDescription) + th.AssertEquals(t, networkName, network.Name) + th.AssertEquals(t, networkDescription, network.Description) return network, nil } @@ -74,9 +74,9 @@ func CreatePortWithSecurityGroup(t *testing.T, client *gophercloud.ServiceClient t.Logf("Successfully created port: %s", portName) - th.AssertEquals(t, port.Name, portName) - th.AssertEquals(t, port.Description, portDescription) - th.AssertEquals(t, port.NetworkID, networkID) + th.AssertEquals(t, portName, port.Name) + th.AssertEquals(t, portDescription, port.Description) + th.AssertEquals(t, networkID, port.NetworkID) return port, nil } @@ -101,8 +101,8 @@ func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*grou t.Logf("Created security group: %s", secGroup.ID) - th.AssertEquals(t, secGroup.Name, secGroupName) - th.AssertEquals(t, secGroup.Description, secGroupDescription) + th.AssertEquals(t, secGroupName, secGroup.Name) + th.AssertEquals(t, secGroupDescription, secGroup.Description) return secGroup, nil } From c925eeacebf44c92577e9310c05bd3b6245cf012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Sun, 8 Dec 2024 22:34:47 +0100 Subject: [PATCH 2002/2296] Allow BuildRequestBody() to work with collection of structs When passing an array or a slice to `BuildRequestBody()`, the `parent` string becomes mandatory. This makes it possible to build requests for bulk operations. --- params.go | 52 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/params.go b/params.go index 09b322a6a2..4a2ed6c942 100644 --- a/params.go +++ b/params.go @@ -11,9 +11,11 @@ import ( ) /* -BuildRequestBody builds a map[string]interface from the given `struct`. If -parent is not an empty string, the final map[string]interface returned will -encapsulate the built one. For example: +BuildRequestBody builds a map[string]interface from the given `struct`, or +collection of `structs`. If parent is not an empty string, the final +map[string]interface returned will encapsulate the built one. Parent is +required when passing a list of `structs`. +For example: disk := 1 createOpts := flavors.CreateOpts{ @@ -27,7 +29,29 @@ encapsulate the built one. For example: body, err := gophercloud.BuildRequestBody(createOpts, "flavor") -The above example can be run as-is, however it is recommended to look at how + + opts := []rules.CreateOpts{ + { + Direction: "ingress", + PortRangeMin: 80, + EtherType: rules.EtherType4, + PortRangeMax: 80, + Protocol: "tcp", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + }, + { + Direction: "ingress", + PortRangeMin: 443, + EtherType: rules.EtherType4, + PortRangeMax: 443, + Protocol: "tcp", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + }, + } + + body, err := gophercloud.BuildRequestBody(opts, "security_group_rules") + +The above examples can be run as-is, however it is recommended to look at how BuildRequestBody is used within Gophercloud to more fully understand how it fits within the request process as a whole rather than use it directly as shown above. @@ -44,7 +68,8 @@ func BuildRequestBody(opts any, parent string) (map[string]any, error) { } optsMap := make(map[string]any) - if optsValue.Kind() == reflect.Struct { + switch optsValue.Kind() { + case reflect.Struct: //fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind()) for i := 0; i < optsValue.NumField(); i++ { v := optsValue.Field(i) @@ -184,9 +209,22 @@ func BuildRequestBody(opts any, parent string) (map[string]any, error) { } //fmt.Printf("optsMap after parent added: %+v\n", optsMap) return optsMap, nil + case reflect.Slice, reflect.Array: + optsMaps := make([]map[string]any, optsValue.Len()) + for i := 0; i < optsValue.Len(); i++ { + b, err := BuildRequestBody(optsValue.Index(i).Interface(), "") + if err != nil { + return nil, err + } + optsMaps[i] = b + } + if parent == "" { + return nil, fmt.Errorf("Parent is required when passing an array or a slice.") + } + return map[string]any{parent: optsMaps}, nil } - // Return an error if the underlying type of 'opts' isn't a struct. - return nil, fmt.Errorf("Options type is not a struct.") + // Return an error if we can't work with the underlying type of 'opts' + return nil, fmt.Errorf("Options type is not a struct, a slice, or an array.") } // EnabledState is a convenience type, mostly used in Create and Update From 6e6dbe5bbcaaa4428852966b70b6405197ec9d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 6 Dec 2024 17:53:37 +0100 Subject: [PATCH 2003/2296] SG rules: implement bulk create Implement bulk creation of security group rules [1]. [1] https://docs.openstack.org/api-ref/network/v2/#bulk-create-security-group-rule --- .../networking/v2/extensions/extensions.go | 38 ++++++++ .../networking/v2/extensions/security_test.go | 6 ++ .../v2/extensions/security/rules/requests.go | 17 ++++ .../v2/extensions/security/rules/results.go | 19 ++++ .../security/rules/testing/requests_test.go | 95 +++++++++++++++++++ 5 files changed, 175 insertions(+) diff --git a/internal/acceptance/openstack/networking/v2/extensions/extensions.go b/internal/acceptance/openstack/networking/v2/extensions/extensions.go index 4de1401f4d..4eb9df5cf2 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/extensions.go +++ b/internal/acceptance/openstack/networking/v2/extensions/extensions.go @@ -140,6 +140,44 @@ func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, se return rule, nil } +// CreateSecurityGroupRulesBulk will create security group rules with a random name +// and random port between 80 and 99. +// An error will be returned if one was failed to be created. +func CreateSecurityGroupRulesBulk(t *testing.T, client *gophercloud.ServiceClient, secGroupID string) ([]rules.SecGroupRule, error) { + t.Logf("Attempting to bulk create security group rules in group: %s", secGroupID) + + sgRulesCreateOpts := make([]rules.CreateOpts, 3) + for i := range 3 { + description := "Rule description" + fromPort := tools.RandomInt(80, 89) + toPort := tools.RandomInt(90, 99) + + sgRulesCreateOpts[i] = rules.CreateOpts{ + Description: description, + Direction: "ingress", + EtherType: "IPv4", + SecGroupID: secGroupID, + PortRangeMin: fromPort, + PortRangeMax: toPort, + Protocol: rules.ProtocolTCP, + } + } + + rules, err := rules.CreateBulk(context.TODO(), client, sgRulesCreateOpts).Extract() + if err != nil { + return rules, err + } + + for i, rule := range rules { + t.Logf("Created security group rule: %s", rule.ID) + + th.AssertEquals(t, sgRulesCreateOpts[i].SecGroupID, rule.SecGroupID) + th.AssertEquals(t, sgRulesCreateOpts[i].Description, rule.Description) + } + + return rules, nil +} + // DeleteSecurityGroup will delete a security group of a specified ID. // A fatal error will occur if the deletion failed. This works best as a // deferred function diff --git a/internal/acceptance/openstack/networking/v2/extensions/security_test.go b/internal/acceptance/openstack/networking/v2/extensions/security_test.go index 670735ec5e..8d855bbb0a 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/security_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/security_test.go @@ -26,6 +26,12 @@ func TestSecurityGroupsCreateUpdateDelete(t *testing.T) { th.AssertNoErr(t, err) defer DeleteSecurityGroupRule(t, client, rule.ID) + rules, err := CreateSecurityGroupRulesBulk(t, client, group.ID) + th.AssertNoErr(t, err) + for _, r := range rules { + defer DeleteSecurityGroupRule(t, client, r.ID) + } + tools.PrintResource(t, group) var name = "Update group" diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go index f2ea9f2d16..8976224b42 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -150,6 +150,23 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu return } +// CreateBulk is an operation which adds new security group rules and associates them +// with an existing security group (whose ID is specified in CreateOpts). +// As of Dalmatian (2024.2) neutron only allows bulk creation of rules when +// they all belong to the same tenant and security group. +// https://github.com/openstack/neutron/blob/6183792/neutron/db/securitygroups_db.py#L814-L828 +func CreateBulk(ctx context.Context, c *gophercloud.ServiceClient, opts []CreateOpts) (r CreateBulkResult) { + body, err := gophercloud.BuildRequestBody(opts, "security_group_rules") + if err != nil { + r.Err = err + return + } + + resp, err := c.Post(ctx, rootURL(c), body, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + // Get retrieves a particular security group rule based on its unique ID. func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index cfdb27fa17..0901ced578 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -103,6 +103,10 @@ type commonResult struct { gophercloud.Result } +type bulkResult struct { + gophercloud.Result +} + // Extract is a function that accepts a result and extracts a security rule. func (r commonResult) Extract() (*SecGroupRule, error) { var s struct { @@ -112,12 +116,27 @@ func (r commonResult) Extract() (*SecGroupRule, error) { return s.SecGroupRule, err } +// Extract is a function that accepts a result and extracts security rules. +func (r bulkResult) Extract() ([]SecGroupRule, error) { + var s struct { + SecGroupRules []SecGroupRule `json:"security_group_rules"` + } + err := r.ExtractInto(&s) + return s.SecGroupRules, err +} + // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a SecGroupRule. type CreateResult struct { commonResult } +// CreateBulkResult represents the result of a bulk create operation. Call its +// Extract method to interpret it as a slice of SecGroupRules. +type CreateBulkResult struct { + bulkResult +} + // GetResult represents the result of a get operation. Call its Extract // method to interpret it as a SecGroupRule. type GetResult struct { diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 843d7eb202..10aa461908 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -222,6 +222,101 @@ func TestCreateAnyProtocol(t *testing.T) { th.AssertNoErr(t, err) } +func TestCreateBulk(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/security-group-rules", 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, ` +{ + "security_group_rules": [ + { + "description": "test description of rule", + "direction": "ingress", + "port_range_min": 80, + "ethertype": "IPv4", + "port_range_max": 80, + "protocol": "tcp", + "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a" + }, + { + "description": "test description of rule", + "direction": "ingress", + "port_range_min": 443, + "ethertype": "IPv4", + "port_range_max": 443, + "protocol": "tcp", + "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a" + } + ] +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprint(w, ` +{ + "security_group_rules": [ + { + "description": "test description of rule", + "direction": "ingress", + "ethertype": "IPv4", + "port_range_max": 80, + "port_range_min": 80, + "protocol": "tcp", + "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "remote_ip_prefix": null, + "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + }, + { + "description": "test description of rule", + "direction": "ingress", + "ethertype": "IPv4", + "port_range_max": 443, + "port_range_min": 443, + "protocol": "tcp", + "remote_group_id": null, + "remote_ip_prefix": null, + "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + ] +} + `) + }) + + opts := []rules.CreateOpts{ + { + Description: "test description of rule", + Direction: "ingress", + PortRangeMin: 80, + EtherType: rules.EtherType4, + PortRangeMax: 80, + Protocol: "tcp", + RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + }, + { + Description: "test description of rule", + Direction: "ingress", + PortRangeMin: 443, + EtherType: rules.EtherType4, + PortRangeMax: 443, + Protocol: "tcp", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + }, + } + _, err := rules.CreateBulk(context.TODO(), fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) +} + func TestRequiredCreateOpts(t *testing.T) { res := rules.Create(context.TODO(), fake.ServiceClient(), rules.CreateOpts{Direction: rules.DirIngress}) if res.Err == nil { From 53d13552640adfc3749772624972946dadf19349 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:27:03 +0000 Subject: [PATCH 2004/2296] build(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dc5d92e323..3c5d9e0e23 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gophercloud/gophercloud/v2 go 1.22 require ( - golang.org/x/crypto v0.30.0 + golang.org/x/crypto v0.31.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 1812f7828d..428bb87959 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= From 026af19b1fe0a2973dd50b58c2e0499763cfedb8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 18 Dec 2024 15:49:58 +0000 Subject: [PATCH 2005/2296] compute: Merge keypair CreateOptsExt This was missed in v2. Correct it in v3. Signed-off-by: Stephen Finucane --- .../openstack/compute/v2/compute.go | 8 +++--- openstack/compute/v2/keypairs/doc.go | 8 ++---- openstack/compute/v2/keypairs/requests.go | 26 ------------------- openstack/compute/v2/servers/requests.go | 3 +++ .../v2/flavors/testing/fixtures.go | 2 +- 5 files changed, 9 insertions(+), 38 deletions(-) diff --git a/internal/acceptance/openstack/compute/v2/compute.go b/internal/acceptance/openstack/compute/v2/compute.go index 4a5ec655a2..3d5da42a6c 100644 --- a/internal/acceptance/openstack/compute/v2/compute.go +++ b/internal/acceptance/openstack/compute/v2/compute.go @@ -685,19 +685,17 @@ func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, name := tools.RandomString("ACPTTEST", 16) t.Logf("Attempting to create server: %s", name) - serverCreateOpts := servers.CreateOpts{ + createOpts := servers.CreateOpts{ Name: name, FlavorRef: choices.FlavorID, ImageRef: choices.ImageID, Networks: []servers.Network{ {UUID: networkID}, }, + KeyName: keyPairName, } - server, err := servers.Create(context.TODO(), client, keypairs.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - KeyName: keyPairName, - }, nil).Extract() + server, err := servers.Create(context.TODO(), client, createOpts, nil).Extract() if err != nil { return nil, err } diff --git a/openstack/compute/v2/keypairs/doc.go b/openstack/compute/v2/keypairs/doc.go index 0e52dfc9a3..b54d7945da 100644 --- a/openstack/compute/v2/keypairs/doc.go +++ b/openstack/compute/v2/keypairs/doc.go @@ -87,15 +87,11 @@ Example to Delete a Key Pair owned by a certain user using microversion 2.10 or Example to Create a Server With a Key Pair - serverCreateOpts := servers.CreateOpts{ + createOpts := servers.CreateOpts{ Name: "server_name", ImageRef: "image-uuid", FlavorRef: "flavor-uuid", - } - - createOpts := keypairs.CreateOptsExt{ - CreateOptsBuilder: serverCreateOpts, - KeyName: "keypair-name", + KeyName: "keypair-name", } server, err := servers.Create(context.TODO(), computeClient, createOpts).Extract() diff --git a/openstack/compute/v2/keypairs/requests.go b/openstack/compute/v2/keypairs/requests.go index bc4cc270f1..b2d17c73b9 100644 --- a/openstack/compute/v2/keypairs/requests.go +++ b/openstack/compute/v2/keypairs/requests.go @@ -4,35 +4,9 @@ import ( "context" "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/pagination" ) -// CreateOptsExt adds a KeyPair option to the base CreateOpts. -type CreateOptsExt struct { - servers.CreateOptsBuilder - - // KeyName is the name of the key pair. - KeyName string `json:"key_name,omitempty"` -} - -// ToServerCreateMap adds the key_name to the base server creation options. -func (opts CreateOptsExt) ToServerCreateMap() (map[string]any, error) { - base, err := opts.CreateOptsBuilder.ToServerCreateMap() - if err != nil { - return nil, err - } - - if opts.KeyName == "" { - return base, nil - } - - serverMap := base["server"].(map[string]any) - serverMap["key_name"] = opts.KeyName - - return base, nil -} - // ListOptsBuilder allows extensions to add additional parameters to the // List request. type ListOptsBuilder interface { diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index d7d5ad81c1..f9c3bf0657 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -508,6 +508,9 @@ type CreateOpts struct { // DiskConfig [optional] controls how the created server's disk is partitioned. DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"` + + // KeyName is the name of the key pair. + KeyName string `json:"key_name,omitempty"` } // ToServerCreateMap assembles a request body based on the contents of a diff --git a/openstack/loadbalancer/v2/flavors/testing/fixtures.go b/openstack/loadbalancer/v2/flavors/testing/fixtures.go index b5b14e5fa3..b33166dfa2 100644 --- a/openstack/loadbalancer/v2/flavors/testing/fixtures.go +++ b/openstack/loadbalancer/v2/flavors/testing/fixtures.go @@ -165,7 +165,7 @@ func HandleFlavorCreationSuccessfullyDisabled(t *testing.T, response string) { w.WriteHeader(http.StatusAccepted) w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) + fmt.Fprint(w, response) }) } From 434d932996e23871f5224e254774bceb72a3ca5c Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Fri, 20 Dec 2024 12:13:35 +0100 Subject: [PATCH 2006/2296] Rename development branch from `master` to `main` --- .github/CONTRIBUTING.md | 6 +++--- .github/ISSUE_TEMPLATE | 2 +- .github/PULL_REQUEST_TEMPLATE | 2 +- .github/workflows/ensure-labels.yaml | 2 +- .github/workflows/greetings.yaml | 4 ++-- README.md | 2 +- docs/contributor-tutorial/step-05-pull-requests.md | 6 +++--- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index af0bdb979a..ed777696ba 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -9,7 +9,7 @@ ## New Contributor Tutorial For new contributors, we've put together a detailed tutorial -[here](https://github.com/gophercloud/gophercloud/tree/master/docs/contributor-tutorial)! +[here](https://github.com/gophercloud/gophercloud/tree/main/docs/contributor-tutorial)! ## 3 ways to get involved @@ -89,7 +89,7 @@ fork as `origin` instead: 4. Checkout the latest development branch: ```bash - git checkout master + git checkout main ``` 5. If you're working on something (discussed more in detail below), you will @@ -107,7 +107,7 @@ need to checkout a new feature branch: git commit ``` -7. Submit your branch as a [Pull Request](https://help.github.com/articles/creating-a-pull-request/). When submitting a Pull Request, please follow our [Style Guide](https://github.com/gophercloud/gophercloud/blob/master/docs/STYLEGUIDE.md). +7. Submit your branch as a [Pull Request](https://help.github.com/articles/creating-a-pull-request/). When submitting a Pull Request, please follow our [Style Guide](https://github.com/gophercloud/gophercloud/blob/main/docs/STYLEGUIDE.md). > Further information about using Git can be found [here](https://git-scm.com/book/en/v2). diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index da9c8472d6..d4db3bdab9 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -1,3 +1,3 @@ diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index fb271a3334..03aa4bb3dd 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,6 +1,6 @@ diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml new file mode 100644 index 0000000000..7aff19fe7d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -0,0 +1,29 @@ +name: Bug Report +description: File a bug report. +title: "[Bug]: " +# type: Bug ## Not supported yet, despite docs saying otherwise: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! Check out our [contributor guide](https://github.com/gophercloud/gophercloud/blob/main/docs/contributor-tutorial/step-02-issues.md) to create effective issues. + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + placeholder: What happened, and what did you expect to happen? + validations: + required: true + - type: dropdown + id: version + attributes: + label: Version + description: What version of our Gophercloud are you running? + options: + - v2 (current) + - v1 (legacy) + - main (development) + default: 0 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/rfe.yaml b/.github/ISSUE_TEMPLATE/rfe.yaml new file mode 100644 index 0000000000..41548cfb9c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfe.yaml @@ -0,0 +1,18 @@ +name: Feature request +description: Suggest an idea for this project. +title: "[RFE]: " +# type: Feature ## Not supported yet, despite docs saying otherwise: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this enhancement request! Check out our [contributor guide](https://github.com/gophercloud/gophercloud/blob/main/docs/contributor-tutorial/step-02-issues.md) to create effective issues. + - type: textarea + id: what + attributes: + label: What is missing? + description: | + Is your feature request related to a problem? Please describe. Also feel free to describe the solution you'd like. + If you've found a missing field in an existing struct, please provide a link to the actual service's Python code which defines the missing field. + validations: + required: true diff --git a/.github/workflows/greetings.yaml b/.github/workflows/greetings.yaml deleted file mode 100644 index 72295f8965..0000000000 --- a/.github/workflows/greetings.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: Greetings - -on: [pull_request_target, 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/main/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/main/docs/contributor-tutorial/step-05-pull-requests.md). From 9e5efb7546781a9e8296ed20cba02addc4fc61d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?p=C3=BDrus?= Date: Wed, 16 Jul 2025 13:42:15 +0200 Subject: [PATCH 2148/2296] core: clone service type aliases instead of referencing global slice --- endpoint_search.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/endpoint_search.go b/endpoint_search.go index e594dcb28e..e0a900f0f8 100644 --- a/endpoint_search.go +++ b/endpoint_search.go @@ -119,7 +119,7 @@ func (eo *EndpointOpts) ApplyDefaults(t string) { if len(eo.Aliases) == 0 { if aliases, ok := ServiceTypeAliases[eo.Type]; ok { // happy path: user requested a service type by its official name - eo.Aliases = aliases + eo.Aliases = slices.Clone(aliases) } else { // unhappy path: user requested a service type by its alias or an // invalid/unsupported service type @@ -129,7 +129,7 @@ func (eo *EndpointOpts) ApplyDefaults(t string) { // we intentionally override the service type, even if it // was explicitly requested by the user eo.Type = t - eo.Aliases = aliases + eo.Aliases = slices.Clone(aliases) } } } From 97368e998b835124e9367d9ed9327de9200f6982 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Thu, 17 Jul 2025 09:37:40 +0200 Subject: [PATCH 2149/2296] Add issue type to issue template It seems supported now --- .github/ISSUE_TEMPLATE/bug.yaml | 3 +-- .github/ISSUE_TEMPLATE/rfe.yaml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index 7aff19fe7d..b25d573119 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -1,7 +1,6 @@ name: Bug Report description: File a bug report. -title: "[Bug]: " -# type: Bug ## Not supported yet, despite docs saying otherwise: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax +type: Bug body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/rfe.yaml b/.github/ISSUE_TEMPLATE/rfe.yaml index 41548cfb9c..ab50e2b2ad 100644 --- a/.github/ISSUE_TEMPLATE/rfe.yaml +++ b/.github/ISSUE_TEMPLATE/rfe.yaml @@ -1,7 +1,6 @@ name: Feature request description: Suggest an idea for this project. -title: "[RFE]: " -# type: Feature ## Not supported yet, despite docs saying otherwise: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax +type: Feature body: - type: markdown attributes: From 6d9143e99f3c670c0eb86c74a18fdecda0f6bd45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?p=C3=BDrus?= Date: Fri, 18 Jul 2025 12:10:37 +0200 Subject: [PATCH 2150/2296] tests: shorten GH-A job names and use go-version-file: 'go.mod' for go version source --- .github/workflows/functional-baremetal.yaml | 5 +++-- .github/workflows/functional-basic.yaml | 5 +++-- .github/workflows/functional-blockstorage.yaml | 5 +++-- .github/workflows/functional-compute.yaml | 5 +++-- .github/workflows/functional-containerinfra.yaml | 5 +++-- .github/workflows/functional-dns.yaml | 5 +++-- .github/workflows/functional-fwaas_v2.yaml | 5 +++-- .github/workflows/functional-identity.yaml | 5 +++-- .github/workflows/functional-image.yaml | 5 +++-- .github/workflows/functional-keymanager.yaml | 5 +++-- .github/workflows/functional-loadbalancer.yaml | 5 +++-- .github/workflows/functional-messaging.yaml | 5 +++-- .github/workflows/functional-networking.yaml | 5 +++-- .github/workflows/functional-objectstorage.yaml | 5 +++-- .github/workflows/functional-orchestration.yaml | 5 +++-- .github/workflows/functional-placement.yaml | 5 +++-- .github/workflows/functional-sharedfilesystems.yaml | 5 +++-- .github/workflows/functional-workflow.yaml | 5 +++-- 18 files changed, 54 insertions(+), 36 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index e67d9a8b86..8d562f0514 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Ironic and run baremetal acceptance tests + name: Ironic on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -84,7 +84,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 0269188b2e..562946a0da 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -31,7 +31,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with defaults and run basic acceptance tests + name: basic tests on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -43,7 +43,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 3616677505..2d2f33b981 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Cinder and run blockstorage acceptance tests + name: Cinder on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -50,7 +50,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index b31f319fbc..c347d93e74 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Nova and run compute acceptance tests + name: Nova on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -50,7 +50,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 1eba68df9d..1239962b0c 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -60,7 +60,7 @@ jobs: MAGNUMCLIENT_BRANCH=stable/2024.1 additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Magnum and run containerinfra acceptance tests + name: Magnum on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -80,7 +80,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 8f405ada85..042ab75fca 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -42,7 +42,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Designate and run dns acceptance tests + name: Designate on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -58,7 +58,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index d2596ee780..bda886c68b 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -40,7 +40,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with enabled FWaaS_v2 and run networking acceptance tests + name: FWaaS_v2 on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -71,7 +71,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index daf5195daf..0db81b6f85 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Keystone and run identity acceptance tests + name: Keystone on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -48,7 +48,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 1e0075411c..6e6e884739 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Glance and run image acceptance tests + name: Glance on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -48,7 +48,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 2dc6fb9133..04016bba41 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -48,7 +48,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Barbican and run keymanager acceptance tests + name: Barbican on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -64,7 +64,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index e93f751a8b..ff35ede770 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -42,7 +42,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Octavia and run loadbalancer acceptance tests + name: Octavia on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -59,7 +59,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 6c9688483b..ee54f43050 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Zaqar and run messaging acceptance tests + name: Zaqar on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -51,7 +51,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 742a1c9dfc..d07bcce7f5 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Neutron and run networking acceptance tests + name: Neutron on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -66,7 +66,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 61a128a5dc..3797c2cdcb 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Swift and run objectstorage acceptance tests + name: Swift on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -54,7 +54,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 3fae34a1fe..930e31ba63 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Heat and run orchestration acceptance tests + name: Heat on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -50,7 +50,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 4a578fcbad..43dd35ea72 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -36,7 +36,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Placement and run placement acceptance tests + name: Placement on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -48,7 +48,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 4700807969..8fbc3e3d2b 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -42,7 +42,7 @@ jobs: ubuntu_version: "22.04" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Manila and run sharedfilesystems acceptance tests + name: Manila on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -72,7 +72,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 2dbd86db07..5fad4cec42 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -40,7 +40,7 @@ jobs: mistral_plugin_version: "stable/2024.1" additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} - name: Deploy OpenStack ${{ matrix.name }} with Mistral and run workflow acceptance tests + name: Mistral on Deploy OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud uses: actions/checkout@v4 @@ -54,7 +54,8 @@ jobs: - name: Checkout go uses: actions/setup-go@v5 with: - go-version: '^1.23' + go-version-file: 'go.mod' + cache: true - name: Run Gophercloud acceptance tests run: | source ${{ github.workspace }}/script/stackenv From e652773b28535f90ee0efd83521f638971d7f38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?p=C3=BDrus?= Date: Fri, 18 Jul 2025 12:23:43 +0200 Subject: [PATCH 2151/2296] tests: use go.mod as a version source in other workflows --- .github/workflows/codeql-analysis.yaml | 9 +++++---- .github/workflows/lint.yaml | 4 +--- .github/workflows/unit.yaml | 11 +++-------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index a9695c7cff..8efeaf761c 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -17,13 +17,14 @@ jobs: language: [ 'go' ] steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Go uses: actions/setup-go@v5 with: - go-version: "1.23" - - - name: Checkout repository - uses: actions/checkout@v4 + go-version-file: 'go.mod' + cache: true - name: Initialize CodeQL uses: github/codeql-action/init@v3 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index ed7399819d..be44ea86bc 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -16,8 +16,6 @@ jobs: - name: Run linters run: | make lint - # TODO: Use 'go mod tidy -diff' instead once go 1.23 is out - # https://github.com/golang/go/issues/27005 - name: Ensure go.mod is up-to-date run: | - if [ $(go mod tidy && git diff | wc -l) -gt 0 ]; then git diff && exit 1; fi + if [ $(go mod tidy -diff | wc -l) -gt 0 ]; then git diff && exit 1; fi diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index ab11e26561..140e64fbed 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -12,17 +12,14 @@ jobs: runs-on: ubuntu-latest strategy: fail-fast: false - matrix: - go-version: - - "1.23.0" - - "1" steps: - name: Checkout Gophercloud uses: actions/checkout@v4 - - name: Setup Go ${{ matrix.go-version }} + - name: Setup Go uses: actions/setup-go@v5 with: - go-version: ${{ matrix.go-version }} + go-version-file: 'go.mod' + cache: true - name: Setup environment run: | # Changing into a different directory to avoid polluting go.sum with "go get" @@ -37,7 +34,6 @@ jobs: uses: coverallsapp/github-action@v2 with: file: cover.out - flag-name: Go-${{ matrix.go-version }} parallel: true finish: permissions: @@ -50,4 +46,3 @@ jobs: uses: coverallsapp/github-action@v2 with: parallel-finished: true - carryforward: Go-${{ join(matrix.go-version.*, '-') }} From b9132fc208fb20e16e4b2785e3ff8dba19b84996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 10 Mar 2025 09:46:12 +0100 Subject: [PATCH 2152/2296] Switch to a version of gocovmerge compatible with go 1.22 The original `gocovmerge` repository [1] has not seen a commit in 9 years and should be considered unmaintained. Switch to fork has a go.mod pinned to go 1.22. Please review the diff [2] between the two repos to check if it's safe to use in gophercloud. [1] https://github.com/wadey/gocovmerge [2] https://github.com/wadey/gocovmerge/compare/master...alexfalkowski:gocovmerge:v1.4.0 --- .github/workflows/unit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index 140e64fbed..ae3b2f0059 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -25,7 +25,7 @@ jobs: # 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 github.com/alexfalkowski/gocovmerge@v1.4.0 - name: Run unit tests run: | make unit From 4b7e159b3c5b2181a10d15c5644d99f37af02a0d Mon Sep 17 00:00:00 2001 From: Fabian Wiesel Date: Mon, 21 Jul 2025 15:07:25 +0200 Subject: [PATCH 2153/2296] Change compute services UpdateOpts fields ForceDown to pointer This follows the same pattern as in the rest of the code and uses a pointer to fix #3471 and allow the caller to unset a prior ForceDown. This unfortunately changes though the API in a breaking way. --- openstack/compute/v2/services/requests.go | 2 +- .../v2/services/testing/fixtures_test.go | 57 +++++++++++++++++++ .../v2/services/testing/requests_test.go | 30 ++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/openstack/compute/v2/services/requests.go b/openstack/compute/v2/services/requests.go index 32be17cb70..6fce69eb15 100644 --- a/openstack/compute/v2/services/requests.go +++ b/openstack/compute/v2/services/requests.go @@ -67,7 +67,7 @@ type UpdateOpts struct { // ForcedDown is a manual override to tell nova that the service in question // has been fenced manually by the operations team. - ForcedDown bool `json:"forced_down,omitempty"` + ForcedDown *bool `json:"forced_down,omitempty"` } // ToServiceUpdateMap formats an UpdateOpts structure into a request body. diff --git a/openstack/compute/v2/services/testing/fixtures_test.go b/openstack/compute/v2/services/testing/fixtures_test.go index 47e549e1b3..2a17d7d120 100644 --- a/openstack/compute/v2/services/testing/fixtures_test.go +++ b/openstack/compute/v2/services/testing/fixtures_test.go @@ -238,6 +238,23 @@ const ServiceUpdate = ` } ` +const ServiceUpdateForceDown = ` +{ + "service": + { + "id": 1, + "binary": "nova-scheduler", + "disabled_reason": "test1", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:02.000000", + "forced_down": true, + "zone": "internal" + } +} +` + // FakeServiceUpdateBody represents the updated service var FakeServiceUpdateBody = services.Service{ Binary: "nova-scheduler", @@ -251,6 +268,18 @@ var FakeServiceUpdateBody = services.Service{ Zone: "internal", } +var FakeServiceUpdateForceDownBody = services.Service{ + Binary: "nova-scheduler", + DisabledReason: "test1", + ForcedDown: true, + Host: "host1", + ID: "1", + State: "up", + Status: "disabled", + UpdatedAt: time.Date(2012, 10, 29, 13, 42, 2, 0, time.UTC), + Zone: "internal", +} + // HandleListPre253Successfully configures the test server to respond to a List // request to a Compute server API pre 2.53 microversion release. func HandleListPre253Successfully(t *testing.T, fakeServer th.FakeServer) { @@ -289,6 +318,34 @@ func HandleUpdateSuccessfully(t *testing.T, fakeServer th.FakeServer) { }) } +// HandleForceDownSuccessfully configures the test server to respond to a Update +// request to a Compute server with Pike+ release. +func HandleForceDownSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/os-services/fake-service-id", 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, `{"forced_down": true}`) + + fmt.Fprint(w, ServiceUpdateForceDown) + }) +} + +// HandleDisableForceDownSuccessfully configures the test server to respond to a Update +// request to a Compute server with Pike+ release. +func HandleDisableForceDownSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/os-services/fake-service-id", 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, `{"forced_down": false}`) + + fmt.Fprint(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, fakeServer th.FakeServer) { diff --git a/openstack/compute/v2/services/testing/requests_test.go b/openstack/compute/v2/services/testing/requests_test.go index 3fac76abfe..d92d7d209e 100644 --- a/openstack/compute/v2/services/testing/requests_test.go +++ b/openstack/compute/v2/services/testing/requests_test.go @@ -92,6 +92,36 @@ func TestUpdateService(t *testing.T) { th.CheckDeepEquals(t, FakeServiceUpdateBody, *actual) } +func TestUpdateServiceForceDown(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleForceDownSuccessfully(t, fakeServer) + + client := client.ServiceClient(fakeServer) + trueVal := true + actual, err := services.Update(context.TODO(), client, "fake-service-id", services.UpdateOpts{ForcedDown: &trueVal}).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, FakeServiceUpdateForceDownBody, *actual) +} + +func TestUpdateServiceDisableForceDown(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleDisableForceDownSuccessfully(t, fakeServer) + + client := client.ServiceClient(fakeServer) + falseVal := false + actual, err := services.Update(context.TODO(), client, "fake-service-id", services.UpdateOpts{ForcedDown: &falseVal}).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, FakeServiceUpdateBody, *actual) +} + func TestDeleteService(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() From 8e2cf7ede225d90a1a3f642cbb16516922c882fd Mon Sep 17 00:00:00 2001 From: Fabian Wiesel Date: Tue, 22 Jul 2025 10:19:18 +0200 Subject: [PATCH 2154/2296] Implement update & delete traits on resource provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements #2420. The relevant server side code can be found here: https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/trait.py#L216-L274 Co-authored-by: pýrus --- .../v1/resourceproviders/requests.go | 35 +++++++++++++++++++ .../testing/fixtures_test.go | 26 ++++++++++++++ .../testing/requests_test.go | 22 ++++++++++++ 3 files changed, 83 insertions(+) diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 5c5075e445..f0dfa9d66f 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -175,3 +175,38 @@ func GetTraits(ctx context.Context, client *gophercloud.ServiceClient, resourceP _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// UpdateTraitsOptsBuilder allows extensions to add additional parameters to the +// UpdateTraits request. +type UpdateTraitsOptsBuilder interface { + ToResourceProviderUpdateTraitsMap() (map[string]any, error) +} + +// UpdateTraitsOpts represents options used to update traits of a resource provider. +type UpdateTraitsOpts = ResourceProviderTraits + +// ToResourceProviderUpdateTraitsMap constructs a request body from UpdateTraitsOpts. +func (opts UpdateTraitsOpts) ToResourceProviderUpdateTraitsMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +func UpdateTraits(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string, opts UpdateTraitsOptsBuilder) (r GetTraitsResult) { + b, err := opts.ToResourceProviderUpdateTraitsMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, getResourceProviderTraitsURL(client, resourceProviderID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +func DeleteTraits(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r DeleteResult) { + resp, err := client.Delete(ctx, getResourceProviderTraitsURL(client, resourceProviderID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index 7ddc91b240..6ac4b8ca73 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -387,3 +387,29 @@ func HandleResourceProviderGetTraits(t *testing.T, fakeServer th.FakeServer) { fmt.Fprint(w, TraitsBody) }) } + +func HandleResourceProviderPutTraits(t *testing.T, fakeServer th.FakeServer) { + traitsTestUrl := fmt.Sprintf("/resource_providers/%s/traits", ResourceProviderTestID) + + fakeServer.Mux.HandleFunc(traitsTestUrl, + 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.Fprint(w, TraitsBody) + }) +} + +func HandleResourceProviderDeleteTraits(t *testing.T, fakeServer th.FakeServer) { + traitsTestUrl := fmt.Sprintf("/resource_providers/%s/traits", ResourceProviderTestID) + + fakeServer.Mux.HandleFunc(traitsTestUrl, + 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/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 1336126ecb..4633c81136 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -145,3 +145,25 @@ func TestGetResourceProvidersTraits(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedTraits, *actual) } + +func TestUpdateResourceProvidersTraits(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderPutTraits(t, fakeServer) + + opts := resourceproviders.UpdateTraitsOpts(ExpectedTraits) + actual, err := resourceproviders.UpdateTraits(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedTraits, *actual) +} + +func TestDeleteResourceProvidersTraits(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderDeleteTraits(t, fakeServer) + + err := resourceproviders.DeleteTraits(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).ExtractErr() + th.AssertNoErr(t, err) +} From 440ef87e6581c404fec36b0c0066f116b9a55373 Mon Sep 17 00:00:00 2001 From: Amir Aslan Aslan Date: Sat, 26 Jul 2025 14:34:06 +0330 Subject: [PATCH 2155/2296] fix: correct comment for PortRangeMax in CreateOpts to specify ICMP code --- 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 1cba0624bb..c691059b42 100644 --- a/openstack/networking/v2/extensions/security/rules/requests.go +++ b/openstack/networking/v2/extensions/security/rules/requests.go @@ -125,7 +125,7 @@ type CreateOpts struct { // The maximum port number in the range that is matched by the security group // rule. The PortRangeMin attribute constrains the PortRangeMax attribute. If - // the protocol is ICMP, this value must be an ICMP type. + // the protocol is ICMP, this value must be an ICMP code. PortRangeMax int `json:"port_range_max,omitempty"` // The minimum port number in the range that is matched by the security group From f3d2128b861d4bbd70d29ae341e9d8efcff4105c Mon Sep 17 00:00:00 2001 From: Fabian Wiesel Date: Thu, 31 Jul 2025 12:34:18 +0200 Subject: [PATCH 2156/2296] Implement hypervisors.GetExt: Get with Query parameter This introduces a new function GetExt in order to avoid breaking backwards compatibility. Implements #3479 --- openstack/compute/v2/hypervisors/requests.go | 35 ++++- .../v2/hypervisors/testing/fixtures_test.go | 120 ++++++++++++++++++ .../v2/hypervisors/testing/requests_test.go | 12 ++ 3 files changed, 166 insertions(+), 1 deletion(-) diff --git a/openstack/compute/v2/hypervisors/requests.go b/openstack/compute/v2/hypervisors/requests.go index b17dd35781..2483d73407 100644 --- a/openstack/compute/v2/hypervisors/requests.go +++ b/openstack/compute/v2/hypervisors/requests.go @@ -66,9 +66,42 @@ func GetStatistics(ctx context.Context, client *gophercloud.ServiceClient) (r St return } +// GetOptsBuilder allows extensions to add additional parameters to the +// Get request. +type GetOptsBuilder interface { + ToHypervisorGetQuery() (string, error) +} + +// GetOpts allows the opt-in to add the servers to the response +type GetOpts struct { + // WithServers is a bool to include all servers which belong to the hypervisor + // This requires microversion 2.53 or later + WithServers *bool `q:"with_servers"` +} + +// ToHypervisorGetQuery formats a GetOpts into a query string. +func (opts GetOpts) ToHypervisorGetQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + // Get makes a request against the API to get details for specific hypervisor. func Get(ctx context.Context, client *gophercloud.ServiceClient, hypervisorID string) (r HypervisorResult) { - resp, err := client.Get(ctx, hypervisorsGetURL(client, hypervisorID), &r.Body, &gophercloud.RequestOpts{ + return GetExt(ctx, client, hypervisorID, nil) +} + +// Show makes a request against the API to get details for specific hypervisor with optional query parameters +func GetExt(ctx context.Context, client *gophercloud.ServiceClient, hypervisorID string, opts GetOptsBuilder) (r HypervisorResult) { + url := hypervisorsGetURL(client, hypervisorID) + if opts != nil { + query, err := opts.ToHypervisorGetQuery() + if err != nil { + return HypervisorResult{gophercloud.Result{Err: err}} + } + url += query + } + + resp, err := client.Get(ctx, url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/compute/v2/hypervisors/testing/fixtures_test.go b/openstack/compute/v2/hypervisors/testing/fixtures_test.go index ebfd816c03..166c415106 100644 --- a/openstack/compute/v2/hypervisors/testing/fixtures_test.go +++ b/openstack/compute/v2/hypervisors/testing/fixtures_test.go @@ -329,6 +329,62 @@ const HypervisorGetBody = ` } ` +// HypervisorGetPost253Body represents a raw hypervisor GET result with Pike+ +// release with optional server list +const HypervisorGetPost253Body = ` +{ + "hypervisor":{ + "cpu_info":{ + "arch":"x86_64", + "model":"Nehalem", + "vendor":"Intel", + "features":[ + "pge", + "clflush" + ], + "topology":{ + "cores":1, + "threads":1, + "sockets":4 + } + }, + "current_workload":0, + "status":"enabled", + "state":"up", + "servers": [ + { + "name": "test_server1", + "uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + }, + { + "name": "test_server2", + "uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" + } + ], + "disk_available_least":0, + "host_ip":"1.1.1.1", + "free_disk_gb":1028, + "free_ram_mb":7680, + "hypervisor_hostname":"fake-mini", + "hypervisor_type":"fake", + "hypervisor_version":2002000, + "id":"c48f6247-abe4-4a24-824e-ea39e108874f", + "local_gb":1028, + "local_gb_used":0, + "memory_mb":8192, + "memory_mb_used":512, + "running_vms":2, + "service":{ + "host":"e6a37ee802d74863ab8b91ade8f12a67", + "id":"9c2566e7-7a54-4777-a1ae-c2662f0c407c", + "disabled_reason":null + }, + "vcpus":1, + "vcpus_used":0 + } +} +` + // HypervisorGetEmptyCPUInfoBody represents a raw hypervisor GET result with // no cpu_info const HypervisorGetEmptyCPUInfoBody = ` @@ -487,6 +543,56 @@ var ( VCPUsUsed: 0, } + HypervisorFakeWithServers = hypervisors.Hypervisor{ + CPUInfo: hypervisors.CPUInfo{ + Arch: "x86_64", + Model: "Nehalem", + Vendor: "Intel", + Features: []string{ + "pge", + "clflush", + }, + Topology: hypervisors.Topology{ + Cores: 1, + Threads: 1, + Sockets: 4, + }, + }, + CurrentWorkload: 0, + Status: "enabled", + State: "up", + Servers: &[]hypervisors.Server{ + { + Name: "test_server1", + UUID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + }, + { + Name: "test_server2", + UUID: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", + }, + }, + DiskAvailableLeast: 0, + HostIP: "1.1.1.1", + FreeDiskGB: 1028, + FreeRamMB: 7680, + HypervisorHostname: "fake-mini", + HypervisorType: "fake", + HypervisorVersion: 2002000, + ID: "c48f6247-abe4-4a24-824e-ea39e108874f", + LocalGB: 1028, + LocalGBUsed: 0, + MemoryMB: 8192, + MemoryMBUsed: 512, + RunningVMs: 2, + Service: hypervisors.Service{ + Host: "e6a37ee802d74863ab8b91ade8f12a67", + ID: "9c2566e7-7a54-4777-a1ae-c2662f0c407c", + DisabledReason: "", + }, + VCPUs: 1, + VCPUsUsed: 0, + } + HypervisorFakeWithParameters = hypervisors.Hypervisor{ CPUInfo: hypervisors.CPUInfo{ Arch: "x86_64", @@ -676,6 +782,20 @@ func HandleHypervisorGetSuccessfully(t *testing.T, fakeServer th.FakeServer) { }) } +func HandleHypervisorGetPost253Successfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, 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") + if r.URL.Query().Get("with_servers") == "true" { + fmt.Fprint(w, HypervisorGetPost253Body) + } else { + fmt.Fprint(w, HypervisorGetBody) + } + }) +} + func HandleHypervisorGetEmptyCPUInfoSuccessfully(t *testing.T, fakeServer th.FakeServer) { fakeServer.Mux.HandleFunc("/os-hypervisors/"+HypervisorFake.ID, func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") diff --git a/openstack/compute/v2/hypervisors/testing/requests_test.go b/openstack/compute/v2/hypervisors/testing/requests_test.go index a2b2183e48..c6c7d9141f 100644 --- a/openstack/compute/v2/hypervisors/testing/requests_test.go +++ b/openstack/compute/v2/hypervisors/testing/requests_test.go @@ -135,6 +135,18 @@ func TestGetHypervisor(t *testing.T) { th.CheckDeepEquals(t, &expected, actual) } +func TestGetWithServersHypervisor(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleHypervisorGetPost253Successfully(t, fakeServer) + + expected := HypervisorFakeWithServers + withServers := true + actual, err := hypervisors.GetExt(context.TODO(), client.ServiceClient(fakeServer), expected.ID, hypervisors.GetOpts{WithServers: &withServers}).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &expected, actual) +} + func TestGetHypervisorEmptyCPUInfo(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() From 38c1bcac0998926568a75ece4b885ae8df4e953b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <80331839+vydrazde@users.noreply.github.com> Date: Wed, 6 Aug 2025 13:12:35 +0200 Subject: [PATCH 2157/2296] Add networking taas tapmirror create --- .../networking/v2/extensions/taas/taas.go | 44 +++++++++ .../v2/extensions/taas/tapmirrors_test.go | 39 ++++++++ .../v2/extensions/taas/tapmirrors/doc.go | 23 +++++ .../v2/extensions/taas/tapmirrors/requests.go | 67 ++++++++++++++ .../v2/extensions/taas/tapmirrors/results.go | 69 ++++++++++++++ .../taas/tapmirrors/testing/requests_test.go | 91 +++++++++++++++++++ .../v2/extensions/taas/tapmirrors/urls.go | 12 +++ 7 files changed, 345 insertions(+) create mode 100644 internal/acceptance/openstack/networking/v2/extensions/taas/taas.go create mode 100644 internal/acceptance/openstack/networking/v2/extensions/taas/tapmirrors_test.go create mode 100644 openstack/networking/v2/extensions/taas/tapmirrors/doc.go create mode 100644 openstack/networking/v2/extensions/taas/tapmirrors/requests.go create mode 100644 openstack/networking/v2/extensions/taas/tapmirrors/results.go create mode 100644 openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go create mode 100644 openstack/networking/v2/extensions/taas/tapmirrors/urls.go diff --git a/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go b/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go new file mode 100644 index 0000000000..026c5d2292 --- /dev/null +++ b/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go @@ -0,0 +1,44 @@ +package taas + +import ( + "context" + "strconv" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/taas/tapmirrors" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +// CreateTapMirror will create a Tap Mirror with the specified portID and remoteIP. An error +// will be returned if the Tap Mirror could not be created. +func CreateTapMirror(t *testing.T, client *gophercloud.ServiceClient, portID string, remoteIP string) (*tapmirrors.TapMirror, error) { + mirrorName := tools.RandomString("TESTACC-", 8) + mirrorDescription := tools.RandomString("TESTACC-DESC-", 8) + mirrorDirectionIN := tools.RandomInt(1, 1000000) + t.Logf("Attempting to create tap mirror: %s", mirrorName) + + createopts := tapmirrors.CreateOpts{ + Name: mirrorName, + Description: mirrorDescription, + PortID: portID, + MirrorType: tapmirrors.MirrorTypeErspanv1, + RemoteIP: remoteIP, + Directions: tapmirrors.Directions{ + In: strconv.Itoa(mirrorDirectionIN), + Out: strconv.Itoa(mirrorDirectionIN + 1), + }, + } + + mirror, err := tapmirrors.Create(context.TODO(), client, createopts).Extract() + if err != nil { + return nil, err + } + + th.AssertEquals(t, mirror.Name, mirrorName) + th.AssertEquals(t, mirror.Description, mirrorDescription) + + t.Logf("Created Tap Mirror: %s", mirror.ID) + return mirror, nil +} diff --git a/internal/acceptance/openstack/networking/v2/extensions/taas/tapmirrors_test.go b/internal/acceptance/openstack/networking/v2/extensions/taas/tapmirrors_test.go new file mode 100644 index 0000000000..8fe74c0b51 --- /dev/null +++ b/internal/acceptance/openstack/networking/v2/extensions/taas/tapmirrors_test.go @@ -0,0 +1,39 @@ +//go:build acceptance || networking || taas + +package taas + +import ( + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestTapMirrorCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "taas") + + // Create Port + 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) + + port, err := networking.CreatePort(t, client, network.ID, subnet.ID) + th.AssertNoErr(t, err) + defer networking.DeletePort(t, client, port.ID) + + // Create Tap Mirror + mirror, err := CreateTapMirror(t, client, port.ID, port.FixedIPs[0].IPAddress) + th.AssertNoErr(t, err) + + tools.PrintResource(t, mirror) +} diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/doc.go b/openstack/networking/v2/extensions/taas/tapmirrors/doc.go new file mode 100644 index 0000000000..481ad3db8e --- /dev/null +++ b/openstack/networking/v2/extensions/taas/tapmirrors/doc.go @@ -0,0 +1,23 @@ +/* +Package tapmirrors manages and retrieves Tap Mirrors in the OpenStack Networking Service. + +Example to Create a Tap Mirror + + createopts := tapmirrors.CreateOpts{ + Name: "tapmirror1", + Description: "Description of tapmirror1", + PortID: "a25290e9-1a54-4c26-a5b3-34458d122acc", + MirrorType: tapmirrors.MirrorTypeErspanv1, + RemoteIP: "192.168.54.217", + Directions: tapmirrors.Directions{ + In: "1", + Out: "2", + }, + } + + mirror, err := tapmirrors.Create(context.TODO(), networkClient, createopts).Extract() + if err != nil { + panic(err) + } +*/ +package tapmirrors diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/requests.go b/openstack/networking/v2/extensions/taas/tapmirrors/requests.go new file mode 100644 index 0000000000..27662fc91e --- /dev/null +++ b/openstack/networking/v2/extensions/taas/tapmirrors/requests.go @@ -0,0 +1,67 @@ +package tapmirrors + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) + +type MirrorType string + +const ( + MirrorTypeErspanv1 MirrorType = "erspanv1" + MirrorTypeGre MirrorType = "gre" +) + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToTapMirrorCreateMap() (map[string]any, error) +} + +// CreateOpts contains all the values needed to create a new tap mirror +type CreateOpts struct { + // The name of the Tap Mirror. + Name string `json:"name"` + + // A human-readable description of the Tap Mirror. + Description string `json:"description,omitempty"` + + // The ID of the project. The caller must have an admin role in + // order to set this. Otherwise, this field is left unset + // and the caller will be the owner. + TenantID string `json:"tenant_id,omitempty"` + + // The Port ID of the Tap Mirror, this will be the source of the mirrored traffic, + // and this traffic will be tunneled into the GRE or ERSPAN v1 tunnel. + // The tunnel itself is not starting from this port. + PortID string `json:"port_id"` + + // The type of the mirroring, it can be gre or erspanv1. + MirrorType MirrorType `json:"mirror_type"` + + // The remote IP of the Tap Mirror, this will be the remote end of the GRE or ERSPAN v1 tunnel. + RemoteIP string `json:"remote_ip"` + + // A dictionary of direction and tunnel_id. Directions are IN and OUT. + // The values of the directions must be unique within the project and + // must be convertible to int. + Directions Directions `json:"directions"` +} + +// ToTapMirrorCreateMap casts a CreateOpts struct to a map. +func (opts CreateOpts) ToTapMirrorCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "tap_mirror") +} + +// Create accepts a CreateOpts struct and uses the values to create a new Tap Mirror. +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTapMirrorCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/results.go b/openstack/networking/v2/extensions/taas/tapmirrors/results.go new file mode 100644 index 0000000000..51b1164e50 --- /dev/null +++ b/openstack/networking/v2/extensions/taas/tapmirrors/results.go @@ -0,0 +1,69 @@ +package tapmirrors + +import ( + "github.com/gophercloud/gophercloud/v2" +) + +// TapMirror represents a Tap Mirror of the networking service taas extension +type TapMirror struct { + // The ID of the Tap Mirror. + ID string `json:"id"` + + // The name of the Tap Mirror. + Name string `json:"name"` + + // A human-readable description of the Tap Mirror. + Description string `json:"description"` + + // The ID of the tenant. + TenantID string `json:"tenant_id"` + + // The ID of the project. + ProjectID string `json:"project_id"` + + // The Port ID of the Tap Mirror, this will be the source of the mirrored traffic, + // and this traffic will be tunneled into the GRE or ERSPAN v1 tunnel. + // The tunnel itself is not starting from this port. + PortID string `json:"port_id"` + + // The type of the mirroring, it can be gre or erspanv1. + MirrorType string `json:"mirror_type"` + + // The remote IP of the Tap Mirror, this will be the remote end of the GRE or ERSPAN v1 tunnel. + RemoteIP string `json:"remote_ip"` + + // A dictionary of direction and tunnel_id. Directions are In and Out. In specifies + // ingress traffic to the port will be mirrored, Out specifies egress traffic will be mirrored. + // The values of the directions are the identifiers of the ERSPAN or GRE session between + // the source and destination, these must be unique within the project and must be convertible to int. + Directions Directions `json:"directions"` +} + +type Directions struct { + // Unique identifier of the tunnel with ingress traffic. Must be convertible to int. + // Omit to not capture ingress traffic. + In string `json:"IN,omitempty"` + + // Unique identifier of the tunnel with egress traffic. Must be convertible to int. + // Omit to not capture egress traffic. + Out string `json:"OUT,omitempty"` +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a Tap Mirror. +func (r commonResult) Extract() (*TapMirror, error) { + var s struct { + TapMirror *TapMirror `json:"tap_mirror"` + } + err := r.ExtractInto(&s) + return s.TapMirror, err +} + +// CreateResult represents the result of a create operation. Call its Extract +// method to interpret it as a Tap Mirror. +type CreateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go b/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go new file mode 100644 index 0000000000..eac8d40c1e --- /dev/null +++ b/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go @@ -0,0 +1,91 @@ +package testing + +import ( + "context" + "fmt" + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/taas/tapmirrors" + + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestCreate(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/taas/tap_mirrors", 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, ` +{ + "tap_mirror": { + "description": "description", + "directions": { + "IN": "1", + "OUT": "2" + }, + "mirror_type": "erspanv1", + "name": "test", + "port_id": "a25290e9-1a54-4c26-a5b3-34458d122acc", + "remote_ip": "192.168.54.217" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprint(w, ` +{ + "tap_mirror": { + "id": "bd64a6e3-12b8-4092-a348-6fc7e27c298a", + "project_id": "6776f022d64443a898ee3fab89dc8c05", + "name": "test", + "description": "description", + "port_id": "a25290e9-1a54-4c26-a5b3-34458d122acc", + "directions": { + "IN": "1", + "OUT": "2" + }, + "remote_ip": "192.168.54.217", + "mirror_type": "erspanv1", + "tenant_id": "6776f022d64443a898ee3fab89dc8c05" + } +} + `) + }) + + options := tapmirrors.CreateOpts{ + Name: "test", + Description: "description", + PortID: "a25290e9-1a54-4c26-a5b3-34458d122acc", + MirrorType: tapmirrors.MirrorTypeErspanv1, + RemoteIP: "192.168.54.217", + Directions: tapmirrors.Directions{ + In: "1", + Out: "2", + }, + } + actual, err := tapmirrors.Create(context.TODO(), fake.ServiceClient(fakeServer), options).Extract() + th.AssertNoErr(t, err) + expected := tapmirrors.TapMirror{ + ID: "bd64a6e3-12b8-4092-a348-6fc7e27c298a", + TenantID: "6776f022d64443a898ee3fab89dc8c05", + ProjectID: "6776f022d64443a898ee3fab89dc8c05", + Name: "test", + Description: "description", + PortID: "a25290e9-1a54-4c26-a5b3-34458d122acc", + MirrorType: "erspanv1", + RemoteIP: "192.168.54.217", + Directions: tapmirrors.Directions{ + In: "1", + Out: "2", + }, + } + th.AssertDeepEquals(t, expected, *actual) +} diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/urls.go b/openstack/networking/v2/extensions/taas/tapmirrors/urls.go new file mode 100644 index 0000000000..fc291c9573 --- /dev/null +++ b/openstack/networking/v2/extensions/taas/tapmirrors/urls.go @@ -0,0 +1,12 @@ +package tapmirrors + +import "github.com/gophercloud/gophercloud/v2" + +const ( + rootPath = "taas" + resourcePath = "tap_mirrors" +) + +func rootURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(rootPath, resourcePath) +} From bc9ccfa268289e47af4ffeab156a86baef8ebbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <80331839+vydrazde@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:17:28 +0200 Subject: [PATCH 2158/2296] Add networking taas tapmirror list, get, update and delete --- .../networking/v2/extensions/taas/taas.go | 18 +- .../v2/extensions/taas/tapmirrors_test.go | 40 +++- .../v2/extensions/taas/tapmirrors/doc.go | 40 ++++ .../v2/extensions/taas/tapmirrors/requests.go | 88 ++++++++ .../v2/extensions/taas/tapmirrors/results.go | 60 ++++++ .../taas/tapmirrors/testing/requests_test.go | 200 ++++++++++++++++++ .../v2/extensions/taas/tapmirrors/urls.go | 4 + 7 files changed, 447 insertions(+), 3 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go b/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go index 026c5d2292..cbab622398 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go @@ -36,9 +36,23 @@ func CreateTapMirror(t *testing.T, client *gophercloud.ServiceClient, portID str return nil, err } - th.AssertEquals(t, mirror.Name, mirrorName) - th.AssertEquals(t, mirror.Description, mirrorDescription) + th.AssertEquals(t, mirrorName, mirror.Name) + th.AssertEquals(t, mirrorDescription, mirror.Description) t.Logf("Created Tap Mirror: %s", mirror.ID) return mirror, nil } + +// DeleteTapMirror will delete a Tap Mirror 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 DeleteTapMirror(t *testing.T, client *gophercloud.ServiceClient, mirrorID string) { + t.Logf("Attempting to delete Tap Mirror: %s", mirrorID) + + err := tapmirrors.Delete(context.TODO(), client, mirrorID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete Tap Mirror %s: %v", mirrorID, err) + } + + t.Logf("Deleted Tap Mirror: %s", mirrorID) +} diff --git a/internal/acceptance/openstack/networking/v2/extensions/taas/tapmirrors_test.go b/internal/acceptance/openstack/networking/v2/extensions/taas/tapmirrors_test.go index 8fe74c0b51..b46d5ae581 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/taas/tapmirrors_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/taas/tapmirrors_test.go @@ -3,14 +3,34 @@ package taas import ( + "context" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/taas/tapmirrors" th "github.com/gophercloud/gophercloud/v2/testhelper" ) +func TestTapMirrorList(t *testing.T) { + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Skip these tests if we don't have the required extension + networking.RequireNeutronExtension(t, client, "taas") + + allPages, err := tapmirrors.List(client, nil).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allMirrors, err := tapmirrors.ExtractTapMirrors(allPages) + th.AssertNoErr(t, err) + + for _, mirror := range allMirrors { + tools.PrintResource(t, mirror) + } +} + func TestTapMirrorCRUD(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) @@ -31,9 +51,27 @@ func TestTapMirrorCRUD(t *testing.T) { th.AssertNoErr(t, err) defer networking.DeletePort(t, client, port.ID) - // Create Tap Mirror + // Create and defer Delete Tap Mirror mirror, err := CreateTapMirror(t, client, port.ID, port.FixedIPs[0].IPAddress) th.AssertNoErr(t, err) + defer DeleteTapMirror(t, client, mirror.ID) tools.PrintResource(t, mirror) + + // Get Tap Mirror + newmirror, err := tapmirrors.Get(context.TODO(), client, mirror.ID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, mirror, newmirror) + + // Update Tap Mirror + updatedName := "TESTACC-updated name" + updatedDescription := "TESTACC-updated mirror description" + updateOpts := tapmirrors.UpdateOpts{ + Name: &updatedName, + Description: &updatedDescription, + } + updatedmirror, err := tapmirrors.Update(context.TODO(), client, mirror.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, updatedName, updatedmirror.Name) + th.AssertEquals(t, updatedDescription, updatedmirror.Description) } diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/doc.go b/openstack/networking/v2/extensions/taas/tapmirrors/doc.go index 481ad3db8e..46e1d9966b 100644 --- a/openstack/networking/v2/extensions/taas/tapmirrors/doc.go +++ b/openstack/networking/v2/extensions/taas/tapmirrors/doc.go @@ -19,5 +19,45 @@ Example to Create a Tap Mirror if err != nil { panic(err) } + +Example to Show the details of a specific Tap Mirror by ID + + tapMirror, err := tapmirrors.Get(context.TODO(), networkClient, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c").Extract() + if err != nil { + panic(err) + } + +Example to Delete a Tap Mirror + + err = tapmirrors.Delete(context.TODO(), networkClient, "5291b189-fd84-46e5-84bd-78f40c05d69c").ExtractErr() + if err != nil { + panic(err) + } + +Example to Update an Tap Mirror + + name := "updated name" + description := "updated description" + updateOps := tapmirrors.UpdateOpts{ + Description: &description, + Name: &name, + } + + updatedTapMirror, err := tapmirrors.Update(context.TODO(), networkClient, "5c561d9d-eaea-45f6-ae3e-08d1a7080828", updateOps).Extract() + if err != nil { + panic(err) + } + +Example to List Tap Mirrors + + allPages, err := tapmirrors.List(networkClient, nil).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allTapMirrors, err := tapmirrors.ExtractTapMirrors(allPages) + if err != nil { + panic(err) + } */ package tapmirrors diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/requests.go b/openstack/networking/v2/extensions/taas/tapmirrors/requests.go index 27662fc91e..61887314b6 100644 --- a/openstack/networking/v2/extensions/taas/tapmirrors/requests.go +++ b/openstack/networking/v2/extensions/taas/tapmirrors/requests.go @@ -4,6 +4,7 @@ import ( "context" "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) type MirrorType string @@ -65,3 +66,90 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBu _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Get retrieves a particular Tap Mirror on its ID. +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToTapMirrorListQuery() (string, error) +} + +// ListOpts allows the filtering of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the Endpoint group attributes you want to see returned. +type ListOpts struct { + ProjectID string `q:"project_id"` + Name string `q:"name"` + Description string `q:"description"` + TenantID string `q:"tenant_id"` + PortID string `q:"port_id"` + MirrorType MirrorType `q:"mirror_type"` + RemoteIP string `q:"remote_ip"` +} + +// ToTapMirrorListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTapMirrorListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// Tap Mirrors. It accepts a ListOpts struct, which allows you to filter +// the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := rootURL(c) + if opts != nil { + query, err := opts.ToTapMirrorListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return TapMirrorPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Delete will permanently delete a Tap Mirror based on its ID. +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. +type UpdateOptsBuilder interface { + ToTapMirrorUpdateMap() (map[string]any, error) +} + +// UpdateOpts contains the values used when updating a Tap Mirror. +type UpdateOpts struct { + Description *string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` +} + +// ToTapMirrorUpdateMap casts an UpdateOpts struct to a map. +func (opts UpdateOpts) ToTapMirrorUpdateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "tap_mirror") +} + +// Update allows Tap Mirrors to be updated. +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToTapMirrorUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(ctx, resourceURL(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/taas/tapmirrors/results.go b/openstack/networking/v2/extensions/taas/tapmirrors/results.go index 51b1164e50..fa83b876da 100644 --- a/openstack/networking/v2/extensions/taas/tapmirrors/results.go +++ b/openstack/networking/v2/extensions/taas/tapmirrors/results.go @@ -2,6 +2,7 @@ package tapmirrors import ( "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" ) // TapMirror represents a Tap Mirror of the networking service taas extension @@ -62,8 +63,67 @@ func (r commonResult) Extract() (*TapMirror, error) { return s.TapMirror, err } +// TapMirrorPage is the page returned by a pager when traversing over a +// collection of Policies. +type TapMirrorPage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of Endpoint groups 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 TapMirrorPage) NextPageURL(endpointURL string) (string, error) { + var s struct { + Links []gophercloud.Link `json:"tap_mirrors_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether an TapMirrorPage struct is empty. +func (r TapMirrorPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + is, err := ExtractTapMirrors(r) + return len(is) == 0, err +} + +// ExtractTapMirrors accepts a Page struct, specifically an TapMirrorPage struct, +// and extracts the elements into a slice of Tap Mirror structs. In other words, +// a generic collection is mapped into a relevant slice. +func ExtractTapMirrors(r pagination.Page) ([]TapMirror, error) { + var s struct { + TapMirrors []TapMirror `json:"tap_mirrors"` + } + err := (r.(TapMirrorPage)).ExtractInto(&s) + return s.TapMirrors, err +} + // CreateResult represents the result of a create operation. Call its Extract // method to interpret it as a Tap Mirror. type CreateResult struct { commonResult } + +// GetResult represents the result of a get operation. Call its Extract +// method to interpret it as a TapMirror. +type GetResult struct { + commonResult +} + +// DeleteResult represents the results of a Delete operation. Call its ExtractErr method +// to determine whether the operation succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// UpdateResult represents the result of an update operation. Call its Extract method +// to interpret it as a TapMirror. +type UpdateResult struct { + commonResult +} diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go b/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go index eac8d40c1e..f9819745ac 100644 --- a/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go +++ b/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go @@ -8,6 +8,7 @@ import ( fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/taas/tapmirrors" + "github.com/gophercloud/gophercloud/v2/pagination" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -89,3 +90,202 @@ func TestCreate(t *testing.T) { } th.AssertDeepEquals(t, expected, *actual) } + +func TestGet(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/taas/tap_mirrors/0837b488-f0e2-4689-99b3-e3ed531f9b10", 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, ` +{ + "tap_mirror": { + "id": "0837b488-f0e2-4689-99b3-e3ed531f9b10", + "project_id": "6776f022d64443a898ee3fab89dc8c05", + "name": "test", + "description": "description", + "port_id": "a25290e9-1a54-4c26-a5b3-34458d122acc", + "directions": { + "IN": "1", + "OUT": "2" + }, + "remote_ip": "192.168.54.217", + "mirror_type": "erspanv1", + "tenant_id": "6776f022d64443a898ee3fab89dc8c05" + } +} + `) + }) + + actual, err := tapmirrors.Get(context.TODO(), fake.ServiceClient(fakeServer), "0837b488-f0e2-4689-99b3-e3ed531f9b10").Extract() + th.AssertNoErr(t, err) + expected := tapmirrors.TapMirror{ + ID: "0837b488-f0e2-4689-99b3-e3ed531f9b10", + TenantID: "6776f022d64443a898ee3fab89dc8c05", + ProjectID: "6776f022d64443a898ee3fab89dc8c05", + Name: "test", + Description: "description", + PortID: "a25290e9-1a54-4c26-a5b3-34458d122acc", + MirrorType: "erspanv1", + RemoteIP: "192.168.54.217", + Directions: tapmirrors.Directions{ + In: "1", + Out: "2", + }, + } + th.AssertDeepEquals(t, expected, *actual) +} + +func TestDelete(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/taas/tap_mirrors/0837b488-f0e2-4689-99b3-e3ed531f9b10", 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) + }) + + res := tapmirrors.Delete(context.TODO(), fake.ServiceClient(fakeServer), "0837b488-f0e2-4689-99b3-e3ed531f9b10") + th.AssertNoErr(t, res.Err) +} + +func TestList(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/taas/tap_mirrors", 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, ` +{ + "tap_mirrors": [ + { + "id": "0837b488-f0e2-4689-99b3-e3ed531f9b10", + "project_id": "6776f022d64443a898ee3fab89dc8c05", + "name": "test", + "description": "description", + "port_id": "a25290e9-1a54-4c26-a5b3-34458d122acc", + "directions": { + "IN": "1", + "OUT": "2" + }, + "remote_ip": "192.168.54.217", + "mirror_type": "erspanv1", + "tenant_id": "6776f022d64443a898ee3fab89dc8c05" + } + ] +} + `) + }) + + count := 0 + + err := tapmirrors.List(fake.ServiceClient(fakeServer), tapmirrors.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + count++ + actual, err := tapmirrors.ExtractTapMirrors(page) + if err != nil { + t.Errorf("Failed to extract members: %v", err) + return false, err + } + expected := []tapmirrors.TapMirror{ + { + ID: "0837b488-f0e2-4689-99b3-e3ed531f9b10", + TenantID: "6776f022d64443a898ee3fab89dc8c05", + ProjectID: "6776f022d64443a898ee3fab89dc8c05", + Name: "test", + Description: "description", + PortID: "a25290e9-1a54-4c26-a5b3-34458d122acc", + MirrorType: "erspanv1", + RemoteIP: "192.168.54.217", + Directions: tapmirrors.Directions{ + In: "1", + Out: "2", + }, + }, + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + + if count != 1 { + t.Errorf("Expected 1 page, got %d", count) + } +} + +func TestUpdate(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/taas/tap_mirrors/d031da31-fb9b-4bd9-8d37-aaf04a12d45f", 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, ` +{ + "tap_mirror": { + "name": "new name", + "description": "new description" + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ` +{ + "tap_mirror": { + "id": "d031da31-fb9b-4bd9-8d37-aaf04a12d45f", + "project_id": "6776f022d64443a898ee3fab89dc8c05", + "name": "new name", + "description": "new description", + "port_id": "a25290e9-1a54-4c26-a5b3-34458d122acc", + "directions": { + "IN": "1", + "OUT": "2" + }, + "remote_ip": "192.168.54.217", + "mirror_type": "erspanv1", + "tenant_id": "6776f022d64443a898ee3fab89dc8c05" + } +} +`) + }) + + updatedName := "new name" + updatedDescription := "new description" + options := tapmirrors.UpdateOpts{ + Name: &updatedName, + Description: &updatedDescription, + } + + actual, err := tapmirrors.Update(context.TODO(), fake.ServiceClient(fakeServer), "d031da31-fb9b-4bd9-8d37-aaf04a12d45f", options).Extract() + th.AssertNoErr(t, err) + expected := tapmirrors.TapMirror{ + ID: "d031da31-fb9b-4bd9-8d37-aaf04a12d45f", + TenantID: "6776f022d64443a898ee3fab89dc8c05", + ProjectID: "6776f022d64443a898ee3fab89dc8c05", + Name: "new name", + Description: "new description", + PortID: "a25290e9-1a54-4c26-a5b3-34458d122acc", + MirrorType: "erspanv1", + RemoteIP: "192.168.54.217", + Directions: tapmirrors.Directions{ + In: "1", + Out: "2", + }, + } + th.AssertDeepEquals(t, expected, *actual) +} diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/urls.go b/openstack/networking/v2/extensions/taas/tapmirrors/urls.go index fc291c9573..1b38a7cec3 100644 --- a/openstack/networking/v2/extensions/taas/tapmirrors/urls.go +++ b/openstack/networking/v2/extensions/taas/tapmirrors/urls.go @@ -10,3 +10,7 @@ const ( 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 1fd250626bda8339009fe486ed1366ec1c4067e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:19:37 +0000 Subject: [PATCH 2159/2296] build(deps): bump golang.org/x/crypto from 0.40.0 to 0.41.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.40.0 to 0.41.0. - [Commits](https://github.com/golang/crypto/compare/v0.40.0...v0.41.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.41.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 210e9ada68..9b62787571 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.23.0 toolchain go1.23.6 require ( - golang.org/x/crypto v0.40.0 + golang.org/x/crypto v0.41.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.34.0 // indirect +require golang.org/x/sys v0.35.0 // indirect diff --git a/go.sum b/go.sum index 6c13d941f0..f1687a61ee 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= 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 2c6c60b38f2863b9818c12f95e1d6afd79cea96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <80331839+vydrazde@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:00:14 +0200 Subject: [PATCH 2160/2296] Change Direction value type to int --- .../networking/v2/extensions/taas/taas.go | 5 ++--- .../v2/extensions/taas/tapmirrors/requests.go | 7 ++++--- .../v2/extensions/taas/tapmirrors/results.go | 14 ++++++------- .../taas/tapmirrors/testing/requests_test.go | 20 +++++++++---------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go b/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go index cbab622398..3d4f3979f4 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go +++ b/internal/acceptance/openstack/networking/v2/extensions/taas/taas.go @@ -2,7 +2,6 @@ package taas import ( "context" - "strconv" "testing" "github.com/gophercloud/gophercloud/v2" @@ -26,8 +25,8 @@ func CreateTapMirror(t *testing.T, client *gophercloud.ServiceClient, portID str MirrorType: tapmirrors.MirrorTypeErspanv1, RemoteIP: remoteIP, Directions: tapmirrors.Directions{ - In: strconv.Itoa(mirrorDirectionIN), - Out: strconv.Itoa(mirrorDirectionIN + 1), + In: mirrorDirectionIN, + Out: mirrorDirectionIN + 1, }, } diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/requests.go b/openstack/networking/v2/extensions/taas/tapmirrors/requests.go index 61887314b6..7b236374bd 100644 --- a/openstack/networking/v2/extensions/taas/tapmirrors/requests.go +++ b/openstack/networking/v2/extensions/taas/tapmirrors/requests.go @@ -44,9 +44,10 @@ type CreateOpts struct { // The remote IP of the Tap Mirror, this will be the remote end of the GRE or ERSPAN v1 tunnel. RemoteIP string `json:"remote_ip"` - // A dictionary of direction and tunnel_id. Directions are IN and OUT. - // The values of the directions must be unique within the project and - // must be convertible to int. + // A dictionary of direction and tunnel_id. Directions are In and Out. In specifies + // ingress traffic to the port will be mirrored, Out specifies egress traffic will be mirrored. + // The values of the directions are the identifiers of the ERSPAN or GRE session between + // the source and destination, these must be unique within the project. Directions Directions `json:"directions"` } diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/results.go b/openstack/networking/v2/extensions/taas/tapmirrors/results.go index fa83b876da..f6ca8b1aa0 100644 --- a/openstack/networking/v2/extensions/taas/tapmirrors/results.go +++ b/openstack/networking/v2/extensions/taas/tapmirrors/results.go @@ -36,18 +36,18 @@ type TapMirror struct { // A dictionary of direction and tunnel_id. Directions are In and Out. In specifies // ingress traffic to the port will be mirrored, Out specifies egress traffic will be mirrored. // The values of the directions are the identifiers of the ERSPAN or GRE session between - // the source and destination, these must be unique within the project and must be convertible to int. + // the source and destination, these must be unique within the project. Directions Directions `json:"directions"` } type Directions struct { - // Unique identifier of the tunnel with ingress traffic. Must be convertible to int. - // Omit to not capture ingress traffic. - In string `json:"IN,omitempty"` + // Unique identifier of the tunnel with ingress traffic. Omit to not capture ingress traffic. + // Encoded as JSON string to be compatible with python tap-as-a-service client. + In int `json:"IN,omitempty,string"` - // Unique identifier of the tunnel with egress traffic. Must be convertible to int. - // Omit to not capture egress traffic. - Out string `json:"OUT,omitempty"` + // Unique identifier of the tunnel with egress traffic. Omit to not capture egress traffic. + // Encoded as JSON string to be compatible with python tap-as-a-service client. + Out int `json:"OUT,omitempty,string"` } type commonResult struct { diff --git a/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go b/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go index f9819745ac..7d98c403b5 100644 --- a/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go +++ b/openstack/networking/v2/extensions/taas/tapmirrors/testing/requests_test.go @@ -68,8 +68,8 @@ func TestCreate(t *testing.T) { MirrorType: tapmirrors.MirrorTypeErspanv1, RemoteIP: "192.168.54.217", Directions: tapmirrors.Directions{ - In: "1", - Out: "2", + In: 1, + Out: 2, }, } actual, err := tapmirrors.Create(context.TODO(), fake.ServiceClient(fakeServer), options).Extract() @@ -84,8 +84,8 @@ func TestCreate(t *testing.T) { MirrorType: "erspanv1", RemoteIP: "192.168.54.217", Directions: tapmirrors.Directions{ - In: "1", - Out: "2", + In: 1, + Out: 2, }, } th.AssertDeepEquals(t, expected, *actual) @@ -134,8 +134,8 @@ func TestGet(t *testing.T) { MirrorType: "erspanv1", RemoteIP: "192.168.54.217", Directions: tapmirrors.Directions{ - In: "1", - Out: "2", + In: 1, + Out: 2, }, } th.AssertDeepEquals(t, expected, *actual) @@ -207,8 +207,8 @@ func TestList(t *testing.T) { MirrorType: "erspanv1", RemoteIP: "192.168.54.217", Directions: tapmirrors.Directions{ - In: "1", - Out: "2", + In: 1, + Out: 2, }, }, } @@ -283,8 +283,8 @@ func TestUpdate(t *testing.T) { MirrorType: "erspanv1", RemoteIP: "192.168.54.217", Directions: tapmirrors.Directions{ - In: "1", - Out: "2", + In: 1, + Out: 2, }, } th.AssertDeepEquals(t, expected, *actual) From 9bfae3eb292bad158a5d623112b9b7ecedc8d8d2 Mon Sep 17 00:00:00 2001 From: "peter.bro" Date: Wed, 26 Mar 2025 15:28:27 +0900 Subject: [PATCH 2161/2296] [Key-Manager] resolve ContainerConsumerListOpts receiver error issue --- openstack/keymanager/v1/containers/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/keymanager/v1/containers/requests.go b/openstack/keymanager/v1/containers/requests.go index a611ef91e0..5797153b14 100644 --- a/openstack/keymanager/v1/containers/requests.go +++ b/openstack/keymanager/v1/containers/requests.go @@ -123,7 +123,7 @@ type ListConsumersOpts struct { // ToContainerListConsumersQuery formats a ListConsumersOpts into a query // string. -func (opts ListOpts) ToContainerListConsumersQuery() (string, error) { +func (opts ListConsumersOpts) ToContainerListConsumersQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) return q.String(), err } From eecd1a9603098928c1b1e7fa22a1c4ee08a7f172 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 18:04:35 +0000 Subject: [PATCH 2162/2296] build(deps): bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [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/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yaml | 2 +- .github/workflows/ensure-labels.yaml | 2 +- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- .github/workflows/label-pr.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/unit.yaml | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 8efeaf761c..58952951c6 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Go uses: actions/setup-go@v5 diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index 3096aa0104..3f8bd42cb2 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -10,7 +10,7 @@ jobs: ensure: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: micnncim/action-label-syncer@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 8d562f0514..ae0e40333f 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -39,7 +39,7 @@ jobs: name: Ironic on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Work around broken dnsmasq run: sudo apt-get purge -y dnsmasq-base - name: Deploy devstack diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 562946a0da..5b28d3c9b1 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -34,7 +34,7 @@ jobs: name: basic tests on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 2d2f33b981..c747d5b899 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -39,7 +39,7 @@ jobs: name: Cinder on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index c347d93e74..516be94de5 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -39,7 +39,7 @@ jobs: name: Nova on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 1239962b0c..60df8d7839 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -63,7 +63,7 @@ jobs: name: Magnum on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 042ab75fca..243cd71e8b 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -45,7 +45,7 @@ jobs: name: Designate on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index bda886c68b..da944898ae 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -43,7 +43,7 @@ jobs: name: FWaaS_v2 on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Create additional neutron policies run: | mkdir /tmp/neutron-policies diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 0db81b6f85..48e605b6f5 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -39,7 +39,7 @@ jobs: name: Keystone on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 6e6e884739..5de98b2d95 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -39,7 +39,7 @@ jobs: name: Glance on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 04016bba41..1c3a9a0256 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -51,7 +51,7 @@ jobs: name: Barbican on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index ff35ede770..ad0071c84b 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -45,7 +45,7 @@ jobs: name: Octavia on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index ee54f43050..aad3ed9754 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -39,7 +39,7 @@ jobs: name: Zaqar on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index d07bcce7f5..c1a0661d92 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -39,7 +39,7 @@ jobs: name: Neutron on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Create additional neutron policies run: | mkdir /tmp/neutron-policies diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 3797c2cdcb..494e44fa1c 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -39,7 +39,7 @@ jobs: name: Swift on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 930e31ba63..f54f9d7a81 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -39,7 +39,7 @@ jobs: name: Heat on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 43dd35ea72..bacc97940d 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -39,7 +39,7 @@ jobs: name: Placement on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 8fbc3e3d2b..db803850de 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -45,7 +45,7 @@ jobs: name: Manila on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 5fad4cec42..c0ef7839cd 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -43,7 +43,7 @@ jobs: name: Mistral on Deploy OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Deploy devstack uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index a9a34072f6..15abc3203c 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -10,7 +10,7 @@ jobs: semver: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index be44ea86bc..4a627ec507 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - uses: actions/setup-go@v5 with: go-version: '1' diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index ae3b2f0059..c462a6dd27 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -14,7 +14,7 @@ jobs: fail-fast: false steps: - name: Checkout Gophercloud - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Go uses: actions/setup-go@v5 with: From e852df2d71bed2ac4b55b408ffb087e5238b716d Mon Sep 17 00:00:00 2001 From: Thomas Morin Date: Wed, 13 Aug 2025 15:06:13 +0200 Subject: [PATCH 2163/2296] Networking v2: Support two time formats for subnet, router, SG rules (#3492) --- .../v2/extensions/layer3/routers/results.go | 42 +++++++++++++++++- .../layer3/routers/testing/requests_test.go | 8 ++++ .../v2/extensions/security/rules/results.go | 43 ++++++++++++++++++- .../security/rules/testing/requests_test.go | 13 ++++++ openstack/networking/v2/subnets/results.go | 43 ++++++++++++++++++- .../v2/subnets/testing/fixtures_test.go | 10 +++++ 6 files changed, 153 insertions(+), 6 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/routers/results.go b/openstack/networking/v2/extensions/layer3/routers/results.go index e9cd9783bd..73f3b93134 100644 --- a/openstack/networking/v2/extensions/layer3/routers/results.go +++ b/openstack/networking/v2/extensions/layer3/routers/results.go @@ -82,10 +82,48 @@ type Router struct { RevisionNumber int `json:"revision_number"` // Timestamp when the router was created - CreatedAt time.Time `json:"created_at"` + CreatedAt time.Time `json:"-"` // Timestamp when the router was last updated - UpdatedAt time.Time `json:"updated_at"` + UpdatedAt time.Time `json:"-"` +} + +func (r *Router) UnmarshalJSON(b []byte) error { + type tmp Router + + // 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 = Router(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 = Router(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil } // RouterPage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index f66dda4e06..f6cd2c0bbd 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -34,6 +34,8 @@ func TestList(t *testing.T) { "admin_state_up": true, "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", "distributed": false, + "created_at": "2017-12-28T07:21:40Z", + "updated_at": "2017-12-28T07:21:40Z", "id": "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b" }, { @@ -45,6 +47,8 @@ func TestList(t *testing.T) { "admin_state_up": true, "tenant_id": "33a40233088643acb66ff6eb0ebea679", "distributed": false, + "created_at": "2017-12-28T07:21:40", + "updated_at": "2017-12-28T07:21:40", "id": "a9254bdb-2613-4a13-ac4c-adc581fba50d" }, { @@ -86,6 +90,8 @@ func TestList(t *testing.T) { Distributed: false, Name: "second_routers", ID: "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b", + CreatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), TenantID: "6b96ff0cb17a4b859e1e575d221683d3", }, { @@ -95,6 +101,8 @@ func TestList(t *testing.T) { Distributed: false, Name: "router1", ID: "a9254bdb-2613-4a13-ac4c-adc581fba50d", + CreatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), TenantID: "33a40233088643acb66ff6eb0ebea679", }, { diff --git a/openstack/networking/v2/extensions/security/rules/results.go b/openstack/networking/v2/extensions/security/rules/results.go index 9f1a92e355..ddc7c5b7c7 100644 --- a/openstack/networking/v2/extensions/security/rules/results.go +++ b/openstack/networking/v2/extensions/security/rules/results.go @@ -1,6 +1,7 @@ package rules import ( + "encoding/json" "time" "github.com/gophercloud/gophercloud/v2" @@ -67,10 +68,48 @@ type SecGroupRule struct { RevisionNumber int `json:"revision_number"` // Timestamp when the rule was created - CreatedAt time.Time `json:"created_at"` + CreatedAt time.Time `json:"-"` // Timestamp when the rule was last updated - UpdatedAt time.Time `json:"updated_at"` + UpdatedAt time.Time `json:"-"` +} + +func (r *SecGroupRule) UnmarshalJSON(b []byte) error { + type tmp SecGroupRule + + // Support for older neutron time format + var s1 struct { + tmp + CreatedAt gophercloud.JSONRFC3339NoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339NoZ `json:"updated_at"` + } + + err := json.Unmarshal(b, &s1) + if err == nil { + *r = SecGroupRule(s1.tmp) + r.CreatedAt = time.Time(s1.CreatedAt) + r.UpdatedAt = time.Time(s1.UpdatedAt) + + return nil + } + + // Support for newer neutron time format + var s2 struct { + tmp + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + } + + err = json.Unmarshal(b, &s2) + if err != nil { + return err + } + + *r = SecGroupRule(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil } // SecGroupRulePage is the page returned by a pager when traversing over a diff --git a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go index 15975ed4ea..4e1938984b 100644 --- a/openstack/networking/v2/extensions/security/rules/testing/requests_test.go +++ b/openstack/networking/v2/extensions/security/rules/testing/requests_test.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "testing" + "time" fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/rules" @@ -35,6 +36,8 @@ func TestList(t *testing.T) { "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, + "created_at": "2017-12-28T07:21:40Z", + "updated_at": "2017-12-28T07:21:40Z", "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" }, @@ -47,6 +50,8 @@ func TestList(t *testing.T) { "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, + "created_at": "2017-12-28T07:21:40", + "updated_at": "2017-12-28T07:21:40", "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } @@ -76,6 +81,8 @@ func TestList(t *testing.T) { Protocol: "", RemoteGroupID: "", RemoteIPPrefix: "", + CreatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", }, @@ -88,6 +95,8 @@ func TestList(t *testing.T) { Protocol: "", RemoteGroupID: "", RemoteIPPrefix: "", + CreatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", }, @@ -372,6 +381,8 @@ func TestGet(t *testing.T) { "protocol": null, "remote_group_id": null, "remote_ip_prefix": null, + "created_at": "2017-12-28T07:21:40Z", + "updated_at": "2017-12-28T07:21:40Z", "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" } @@ -390,6 +401,8 @@ func TestGet(t *testing.T) { th.AssertEquals(t, "", sr.Protocol) th.AssertEquals(t, "", sr.RemoteGroupID) th.AssertEquals(t, "", sr.RemoteIPPrefix) + th.AssertEquals(t, time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), sr.UpdatedAt) + th.AssertEquals(t, time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), sr.CreatedAt) th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sr.SecGroupID) th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sr.TenantID) } diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go index bde6823ea7..cb5831e9e6 100644 --- a/openstack/networking/v2/subnets/results.go +++ b/openstack/networking/v2/subnets/results.go @@ -1,6 +1,7 @@ package subnets import ( + "encoding/json" "time" "github.com/gophercloud/gophercloud/v2" @@ -129,10 +130,48 @@ type Subnet struct { SegmentID string `json:"segment_id"` // Timestamp when the subnet was created - CreatedAt time.Time `json:"created_at"` + CreatedAt time.Time `json:"-"` // Timestamp when the subnet was last updated - UpdatedAt time.Time `json:"updated_at"` + UpdatedAt time.Time `json:"-"` +} + +func (r *Subnet) UnmarshalJSON(b []byte) error { + type tmp Subnet + + // 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 = Subnet(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 = Subnet(s2.tmp) + r.CreatedAt = time.Time(s2.CreatedAt) + r.UpdatedAt = time.Time(s2.UpdatedAt) + + return nil } // SubnetPage is the page returned by a pager when traversing over a collection diff --git a/openstack/networking/v2/subnets/testing/fixtures_test.go b/openstack/networking/v2/subnets/testing/fixtures_test.go index a44f1cc77e..ebb005f62d 100644 --- a/openstack/networking/v2/subnets/testing/fixtures_test.go +++ b/openstack/networking/v2/subnets/testing/fixtures_test.go @@ -1,6 +1,8 @@ package testing import ( + "time" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/subnets" ) @@ -24,6 +26,8 @@ const SubnetListResult = ` "ip_version": 4, "gateway_ip": "10.0.0.1", "cidr": "10.0.0.0/24", + "created_at": "2017-12-28T07:21:40Z", + "updated_at": "2017-12-28T07:21:40Z", "id": "08eae331-0402-425a-923c-34f7cfe39c1b" }, { @@ -43,6 +47,8 @@ const SubnetListResult = ` "ip_version": 4, "gateway_ip": "192.0.0.1", "cidr": "192.0.0.0/8", + "created_at": "2017-12-28T07:21:40", + "updated_at": "2017-12-28T07:21:40", "id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" }, { @@ -104,6 +110,8 @@ var Subnet1 = subnets.Subnet{ IPVersion: 4, GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", + CreatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), ID: "08eae331-0402-425a-923c-34f7cfe39c1b", } @@ -125,6 +133,8 @@ var Subnet2 = subnets.Subnet{ IPVersion: 4, GatewayIP: "192.0.0.1", CIDR: "192.0.0.0/8", + CreatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), + UpdatedAt: time.Date(2017, 12, 28, 07, 21, 40, 0, time.UTC), ID: "54d6f61d-db07-451c-9ab3-b9609b6b6f0b", } From c249fbc12ca323705abb92892eeca541dd837a10 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 19 Jun 2025 17:53:58 +0100 Subject: [PATCH 2164/2296] Prepare for merge queues Add merge_group as an event. https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 1 + .github/workflows/functional-basic.yaml | 1 + .github/workflows/functional-blockstorage.yaml | 1 + .github/workflows/functional-compute.yaml | 1 + .github/workflows/functional-containerinfra.yaml | 1 + .github/workflows/functional-dns.yaml | 1 + .github/workflows/functional-fwaas_v2.yaml | 1 + .github/workflows/functional-identity.yaml | 1 + .github/workflows/functional-image.yaml | 1 + .github/workflows/functional-keymanager.yaml | 1 + .github/workflows/functional-loadbalancer.yaml | 1 + .github/workflows/functional-messaging.yaml | 1 + .github/workflows/functional-networking.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 + .github/workflows/functional-workflow.yaml | 1 + .github/workflows/lint.yaml | 1 + .github/workflows/unit.yaml | 1 + 20 files changed, 20 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index ae0e40333f..de497cfd3c 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -1,5 +1,6 @@ name: functional-baremetal on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 5b28d3c9b1..3e87b70194 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -1,5 +1,6 @@ name: functional-basic on: + merge_group: pull_request: paths-ignore: - 'docs/**' diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index c747d5b899..4a108b3247 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -1,5 +1,6 @@ name: functional-blockstorage on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 516be94de5..4a684edd84 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -1,5 +1,6 @@ name: functional-compute on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 60df8d7839..dad5a6b4d7 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -1,5 +1,6 @@ name: functional-containerinfra on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 243cd71e8b..21879e8321 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -1,5 +1,6 @@ name: functional-dns on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index da944898ae..d7856d4571 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -4,6 +4,7 @@ # [1] https://bugs.launchpad.net/neutron/+bug/1971958 name: functional-fwaas_v2 on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 48e605b6f5..58c9b2188f 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -1,5 +1,6 @@ name: functional-identity on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 5de98b2d95..e4d290339f 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -1,5 +1,6 @@ name: functional-image on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 1c3a9a0256..e1bd85c0e9 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -1,5 +1,6 @@ name: functional-keymanager on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index ad0071c84b..9aa8ffed3b 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -1,5 +1,6 @@ name: functional-loadbalancer on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index aad3ed9754..415c85086e 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -1,5 +1,6 @@ name: functional-messaging on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index c1a0661d92..4f57d1da57 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -1,5 +1,6 @@ name: functional-networking on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 494e44fa1c..d484ea1b33 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -1,5 +1,6 @@ name: functional-objectstorage on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index f54f9d7a81..ca553ad82e 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -1,5 +1,6 @@ name: functional-orchestration on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index bacc97940d..9345989800 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -1,5 +1,6 @@ name: functional-placement on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index db803850de..79bb42844d 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -1,5 +1,6 @@ name: functional-sharedfilesystems on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index c0ef7839cd..2288e90e70 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -1,5 +1,6 @@ name: functional-workflow on: + merge_group: pull_request: paths: - 'openstack/auth_env.go' diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 4a627ec507..f1696959e9 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -1,5 +1,6 @@ name: Linters on: + - merge_group - push - pull_request permissions: diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index c462a6dd27..ee7eb7fd58 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -1,5 +1,6 @@ name: Unit Testing on: + - merge_group - push - pull_request permissions: From 5fb0f179be566e1a3c290661a03ecad1f92d1a35 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 1 Sep 2025 16:26:54 +0100 Subject: [PATCH 2165/2296] Add local GitHub action To allow us to retrieve the list of changed files in the PR and compare them against a whitelist. This filtering only happens for non-scheduled jobs. For scheduled jobs, we should always run. Signed-off-by: Stephen Finucane --- .github/actions/file-filter/Dockerfile | 5 + .github/actions/file-filter/action.yml | 24 +++ .../actions/file-filter/file_filter_action.py | 148 ++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 .github/actions/file-filter/Dockerfile create mode 100644 .github/actions/file-filter/action.yml create mode 100755 .github/actions/file-filter/file_filter_action.py diff --git a/.github/actions/file-filter/Dockerfile b/.github/actions/file-filter/Dockerfile new file mode 100644 index 0000000000..4994652170 --- /dev/null +++ b/.github/actions/file-filter/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.11-slim +WORKDIR /app +RUN pip install 'PyGithub~=2.7.0' 'requests~=2.32' +COPY file_filter_action.py ./ +ENTRYPOINT ["python", "/app/file_filter_action.py"] diff --git a/.github/actions/file-filter/action.yml b/.github/actions/file-filter/action.yml new file mode 100644 index 0000000000..afd9fac6c8 --- /dev/null +++ b/.github/actions/file-filter/action.yml @@ -0,0 +1,24 @@ +name: 'File Filter' +description: 'Filter PR files by glob patterns and return true/false if any files match' +inputs: + patterns: + description: 'String containing one or more glob patterns separated by spaces' + required: true + exclude: + description: 'Whether patterns is a list of glob patterns to *exclude* rather than *include*' + required: false + default: false + token: + description: 'GitHub token for API access' + required: false + default: ${{ github.token }} +outputs: + matches: + description: 'True if any files match the patterns or if event_type is schedule, false otherwise (boolean)' + count: + description: 'Number of files that matched the patterns; will be unset if event_type is schedule (integer)' + files: + description: 'Files that matched the patterns; will unset if event_type is schedule (JSON array of strings)' +runs: + using: 'docker' + image: 'Dockerfile' diff --git a/.github/actions/file-filter/file_filter_action.py b/.github/actions/file-filter/file_filter_action.py new file mode 100755 index 0000000000..75bb0960c3 --- /dev/null +++ b/.github/actions/file-filter/file_filter_action.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 + +import argparse +import fnmatch +import json +import os +import sys + +import github + + +def parse_patterns(patterns_input: str) -> list[str]: + """Parse glob patterns from newline- or space-separated string input.""" + if not patterns_input: + return [] + + patterns = [p.strip() for p in patterns_input.split() if p.strip()] + + if not patterns: + raise ValueError('No valid patterns found in input') + + return patterns + + +def get_changed_files( + github_client: github.Github, repo_name: str, +) -> list[str]: + """Get list of changed files between base and head refs.""" + try: + repo = github_client.get_repo(repo_name) + + # Try to get PR context first + if 'GITHUB_EVENT_PATH' in os.environ: + try: + with open(os.environ['GITHUB_EVENT_PATH']) as f: + event_data = json.load(f) + + if 'pull_request' in event_data: + pr_number = event_data['pull_request']['number'] + pr = repo.get_pull(pr_number) + files = pr.get_files() + return [f.filename for f in files] + except (FileNotFoundError, KeyError, json.JSONDecodeError): + pass + + base_ref = os.environ.get('GITHUB_BASE_REF', 'main') + head_ref = os.environ.get('GITHUB_HEAD_REF', os.environ.get('GITHUB_SHA')) + + if head_ref: + comparison = repo.compare(base_ref, head_ref) + return [f.filename for f in comparison.files] + + print('Warning: Could not determine changed files', file=sys.stderr) + return [] + except github.GithubException as e: + print(f'GitHub API error: {e}', file=sys.stderr) + return [] + except Exception as e: + print(f'Error getting changed files: {e}', file=sys.stderr) + return [] + + +def match_files(files: list[str], patterns: list[str], exclude: bool) -> list[str]: + """Match files against glob patterns.""" + matches = [] + + for file_path in files: + for pattern in patterns: + if (fnmatch.fnmatch(file_path, pattern) and not exclude) or exclude: + matches.append(file_path) + break + + return matches + + +def set_output(name: str, value: str) -> None: + """Set GitHub Actions output.""" + if 'GITHUB_OUTPUT' in os.environ: + with open(os.environ['GITHUB_OUTPUT'], 'a') as f: + f.write(f'{name}={value}\n') + else: + # Fallback for older runners + print(f'::set-output name={name}::{value}') + + +def main() -> None: + """Main function.""" + parser = argparse.ArgumentParser( + description=( + 'A utility script to retrieve the list of changed files in the ' + 'current PR, intended to be run as part of a GitHub Actions ' + 'pipeline.' + ), + ) + parser.parse_args() + + try: + patterns_input = os.environ.get('INPUT_PATTERNS', '') + token = os.environ.get('INPUT_TOKEN', os.environ.get('GITHUB_TOKEN', '')) + exclude = os.environ.get('INPUT_EXCLUDE') + repo_name = os.environ.get('GITHUB_REPOSITORY', '') + event_type = os.environ.get('GITHUB_EVENT_NAME') + + print(f'Event type: {event_type}') + if event_type in ('schedule',): + print(f'Skipping file check for event_type={event_type}') + set_output('matches', 'true') + set_output('count', '') + set_output('files', '') + sys.exit(0) + + if not patterns_input: + print('Error: No patterns provided', file=sys.stderr) + sys.exit(1) + + if not token: + print('Error: No GitHub token provided', file=sys.stderr) + sys.exit(1) + + if not repo_name: + print('Error: No repository name found', file=sys.stderr) + sys.exit(1) + + if exclude and exclude not in ('true', 'false'): + print('Error: exclude must be one of: true, false', file=sys.stderr) + sys.exit(1) + + patterns = parse_patterns(patterns_input) + print(f'Parsed patterns: {patterns}') + + github_client = github.Github(token) + changed_files = get_changed_files(github_client, repo_name) + matched_files = match_files(changed_files, patterns, exclude == 'true') + + print(f'Has matches? {"true" if matched_files else "false"}') + print(f'Matched files: {matched_files}') + + set_output('matches', 'true' if matched_files else 'false') + set_output('count', str(len(matched_files))) + set_output('files', json.dumps(matched_files)) + sys.exit(0) + except Exception as e: + print(f'Error: {e}', file=sys.stderr) + sys.exit(1) + + +if __name__ == '__main__': + main() From 23eae2cf8c936e4a8c41bb6955c053ca2dc52a63 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 11 Aug 2025 18:20:50 +0100 Subject: [PATCH 2166/2296] Always run functional tests Inspired by [1]. [1] https://brunoscheufler.com/blog/2022-04-24-required-github-actions-jobs-in-a-monorepo Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 56 +++++++++++++++---- .github/workflows/functional-basic.yaml | 45 ++++++++++++--- .../workflows/functional-blockstorage.yaml | 54 ++++++++++++++---- .github/workflows/functional-compute.yaml | 54 ++++++++++++++---- .../workflows/functional-containerinfra.yaml | 54 ++++++++++++++---- .github/workflows/functional-dns.yaml | 54 ++++++++++++++---- .github/workflows/functional-fwaas_v2.yaml | 56 +++++++++++++++---- .github/workflows/functional-identity.yaml | 54 ++++++++++++++---- .github/workflows/functional-image.yaml | 54 ++++++++++++++---- .github/workflows/functional-keymanager.yaml | 54 ++++++++++++++---- .../workflows/functional-loadbalancer.yaml | 54 ++++++++++++++---- .github/workflows/functional-messaging.yaml | 54 ++++++++++++++---- .github/workflows/functional-networking.yaml | 56 +++++++++++++++---- .../workflows/functional-objectstorage.yaml | 54 ++++++++++++++---- .../workflows/functional-orchestration.yaml | 54 ++++++++++++++---- .github/workflows/functional-placement.yaml | 54 ++++++++++++++---- .../functional-sharedfilesystems.yaml | 54 ++++++++++++++---- .github/workflows/functional-workflow.yaml | 54 ++++++++++++++---- 18 files changed, 758 insertions(+), 211 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index de497cfd3c..b4caf40994 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -2,16 +2,6 @@ name: functional-baremetal on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**baremetal**' - - '.github/workflows/functional-baremetal.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,9 +31,34 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **baremetal** + .github/workflows/functional-baremetal.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Work around broken dnsmasq + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: sudo apt-get purge -y dnsmasq-base + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -82,25 +97,42 @@ jobs: 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,${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-baremetal + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} USE_SYSTEM_SCOPE: true + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-baremetal-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 3e87b70194..756c742cc2 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -2,11 +2,6 @@ name: functional-basic on: merge_group: pull_request: - paths-ignore: - - 'docs/**' - - '**.md' - - '**.gitignore' - - '**LICENSE' schedule: - cron: '0 0 */3 * *' jobs: @@ -36,29 +31,65 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + docs/** + **.md + **.gitignore + **LICENSE + exclude: true + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy,${{ matrix.additional_services }}' + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-basic + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-basic-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 4a108b3247..7c4ab14bac 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -2,16 +2,6 @@ name: functional-blockstorage on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**blockstorage**' - - '.github/workflows/functional-blockstorage.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,31 +31,71 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **blockstorage** + .github/workflows/functional-blockstorage.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} conf_overrides: | CINDER_ISCSI_HELPER=lioadm enabled_services: "s-account,s-container,s-object,s-proxy,c-bak,${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-blockstorage + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-blockstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 4a684edd84..8d932e44b5 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -2,16 +2,6 @@ name: functional-compute on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**compute**' - - '.github/workflows/functional-compute.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,31 +31,71 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **compute** + .github/workflows/functional-compute.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} conf_overrides: | CINDER_ISCSI_HELPER=lioadm enabled_services: "${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-compute + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-compute-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index dad5a6b4d7..bca3ee49b0 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -2,16 +2,6 @@ name: functional-containerinfra on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**containerinfra**' - - '.github/workflows/functional-containerinfra.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -65,7 +55,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **containerinfra** + .github/workflows/functional-containerinfra.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -78,24 +91,41 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: "h-eng,h-api,h-api-cfn,h-api-cw,${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-containerinfra + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-containerinfra-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 21879e8321..51adcd0535 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -2,16 +2,6 @@ name: functional-dns on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**dns**' - - '.github/workflows/functional-dns.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -47,7 +37,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **dns** + .github/workflows/functional-dns.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -56,24 +69,41 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: "designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns,${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-dns + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-dns-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index d7856d4571..3fd3e971eb 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -6,16 +6,6 @@ name: functional-fwaas_v2 on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**networking/v2/extensions/fwaas_v2**' - - '.github/workflows/functional-fwaas_v2.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -45,7 +35,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **networking/v2/extensions/fwaas_v2** + .github/workflows/functional-fwaas_v2.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Create additional neutron policies + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | mkdir /tmp/neutron-policies cat << EOF >> /tmp/neutron-policies/port_binding.yaml @@ -53,7 +66,9 @@ jobs: "create_port:binding:profile": "rule:admin_only or rule:service_api" "update_port:binding:profile": "rule:admin_only or rule:service_api" EOF + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -69,24 +84,41 @@ jobs: [oslo_policy] policy_dirs = /tmp/neutron-policies 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,${{ matrix.additional_services }}' + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-networking + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-fwaas_v2-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 58c9b2188f..f1c920981b 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -2,16 +2,6 @@ name: functional-identity on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**identity**' - - '.github/workflows/functional-identity.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,29 +31,69 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **identity** + .github/workflows/functional-identity.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} enabled_services: "${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-identity + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-identity-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index e4d290339f..eb299c93b3 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -2,16 +2,6 @@ name: functional-image on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**image**' - - '.github/workflows/functional-image.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,29 +31,69 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **image** + .github/workflows/functional-image.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} enabled_services: "${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-image + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-image-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index e1bd85c0e9..b5db25d8ca 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -2,16 +2,6 @@ name: functional-keymanager on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**keymanager**' - - '.github/workflows/functional-keymanager.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -53,7 +43,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **keymanager** + .github/workflows/functional-keymanager.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -62,24 +75,41 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: "barbican-svc,barbican-retry,barbican-keystone-listener,${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-keymanager + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-keymanager-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 9aa8ffed3b..02df282845 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -2,16 +2,6 @@ name: functional-loadbalancer on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**loadbalancer**' - - '.github/workflows/functional-loadbalancer.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -47,7 +37,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **loadbalancer** + .github/workflows/functional-loadbalancer.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -57,24 +70,41 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: "octavia,o-api,o-cw,o-hk,o-hm,o-da,neutron-qos,${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-loadbalancer + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-loadbalancer-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 415c85086e..7f8ef7cc8e 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -2,16 +2,6 @@ name: functional-messaging on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**messaging**' - - '.github/workflows/functional-messaging.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,7 +31,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **messaging** + .github/workflows/functional-messaging.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -49,24 +62,41 @@ jobs: enable_plugin zaqar https://github.com/openstack/zaqar ${{ matrix.openstack_version }} ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} enabled_services: "${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-messaging + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-messaging-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 4f57d1da57..4ae22544f0 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -2,16 +2,6 @@ name: functional-networking on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**networking**' - - '.github/workflows/functional-networking.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,7 +31,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **networking** + .github/workflows/functional-networking.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Create additional neutron policies + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | mkdir /tmp/neutron-policies cat << EOF >> /tmp/neutron-policies/port_binding.yaml @@ -49,7 +62,9 @@ jobs: "create_port:binding:profile": "rule:admin_only or rule:service_api" "update_port:binding:profile": "rule:admin_only or rule:service_api" EOF + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -64,24 +79,41 @@ jobs: [oslo_policy] policy_dirs = /tmp/neutron-policies enabled_services: "neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding,${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-networking + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-networking-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index d484ea1b33..38d37a793e 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -2,16 +2,6 @@ name: functional-objectstorage on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**objectstorage**' - - '.github/workflows/functional-objectstorage.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,7 +31,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **objectstorage** + .github/workflows/functional-objectstorage.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -52,24 +65,41 @@ jobs: [filter:versioned_writes] allow_object_versioning = true enabled_services: 's-account,s-container,s-object,s-proxy,${{ matrix.additional_services }}' + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-objectstorage + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-objectstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index ca553ad82e..4322089751 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -2,16 +2,6 @@ name: functional-orchestration on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**orchestration**' - - '.github/workflows/functional-orchestration.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,31 +31,71 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **orchestration** + .github/workflows/functional-orchestration.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin heat https://github.com/openstack/heat ${{ matrix.openstack_version }} enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw,${{ matrix.additional_services }}' + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-orchestration + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-orchestration-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 9345989800..6897d549f1 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -2,16 +2,6 @@ name: functional-placement on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**placement**' - - '.github/workflows/functional-placement.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -41,29 +31,69 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **placement** + .github/workflows/functional-placement.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} enabled_services: "${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-placement + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-placement-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 79bb42844d..6cd3958ce1 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -2,16 +2,6 @@ name: functional-sharedfilesystems on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**sharedfilesystems**' - - '.github/workflows/functional-sharedfilesystems.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -47,7 +37,30 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **sharedfilesystems** + .github/workflows/functional-sharedfilesystems.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} @@ -70,24 +83,41 @@ jobs: ${{ matrix.devstack_conf_overrides }} enabled_services: "${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-sharedfilesystems + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-sharedfilesystems-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 2288e90e70..13538ff410 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -2,16 +2,6 @@ name: functional-workflow on: merge_group: pull_request: - paths: - - 'openstack/auth_env.go' - - 'openstack/client.go' - - 'openstack/endpoint.go' - - 'openstack/endpoint_location.go' - - 'openstack/config/provider_client.go' - - 'openstack/utils/choose_version.go' - - 'openstack/utils/discovery.go' - - '**workflow**' - - '.github/workflows/functional-workflow.yaml' schedule: - cron: '0 0 */3 * *' jobs: @@ -45,31 +35,71 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **workflow** + .github/workflows/functional-workflow.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin mistral https://github.com/openstack/mistral ${{ matrix.mistral_plugin_version }} enabled_services: "mistral,mistral-api,mistral-engine,mistral-executor,mistral-event-engine,${{ matrix.additional_services }}" + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: actions/setup-go@v5 with: go-version-file: 'go.mod' cache: true + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv make acceptance-workflow + echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack OS_BRANCH: ${{ matrix.openstack_version }} + - name: Generate logs on failure run: ./script/collectlogs - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + - name: Upload logs artifacts on failure - if: failure() + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} uses: actions/upload-artifact@v4 with: name: functional-workflow-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi From 9d2ef73566073189b953c952cb859c6bd55c7372 Mon Sep 17 00:00:00 2001 From: Sharpz7 Date: Wed, 3 Sep 2025 03:39:29 +0000 Subject: [PATCH 2167/2296] Initial Commit --- .github/workflows/functional-baremetal.yaml | 4 ++-- .../acceptance/openstack/baremetal/noauth/portgroups_test.go | 3 ++- internal/acceptance/openstack/baremetal/v1/nodes_test.go | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index ae0e40333f..e95234847e 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -25,7 +25,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" @@ -63,7 +63,7 @@ jobs: GLANCE_LIMIT_IMAGE_SIZE_TOTAL=5000 Q_USE_SECGROUP=False API_WORKERS=1 - IRONIC_BAREMETAL_BASIC_OPS=True + IRONIC_BAREMETAL_BASIC_OPS=False IRONIC_BUILD_DEPLOY_RAMDISK=False IRONIC_AUTOMATED_CLEAN_ENABLED=False IRONIC_CALLBACK_TIMEOUT=600 diff --git a/internal/acceptance/openstack/baremetal/noauth/portgroups_test.go b/internal/acceptance/openstack/baremetal/noauth/portgroups_test.go index c3ed33cd67..0ea05ed518 100644 --- a/internal/acceptance/openstack/baremetal/noauth/portgroups_test.go +++ b/internal/acceptance/openstack/baremetal/noauth/portgroups_test.go @@ -15,8 +15,9 @@ import ( func TestPortGroupsCreateDestroy(t *testing.T) { clients.RequireLong(t) + RequireIronicNoAuth(t) - client, err := clients.NewBareMetalV1Client() + client, err := clients.NewBareMetalV1NoAuthClient() th.AssertNoErr(t, err) // NOTE(sharpz7) - increased due to create fake node requiring it. diff --git a/internal/acceptance/openstack/baremetal/v1/nodes_test.go b/internal/acceptance/openstack/baremetal/v1/nodes_test.go index be48ee9197..87030a72e7 100644 --- a/internal/acceptance/openstack/baremetal/v1/nodes_test.go +++ b/internal/acceptance/openstack/baremetal/v1/nodes_test.go @@ -214,7 +214,7 @@ func TestNodesFirmwareInterface(t *testing.T) { } func TestNodesVirtualMedia(t *testing.T) { - clients.SkipReleasesBelow(t, "master") // 2024.1 + clients.SkipReleasesBelow(t, "stable/2024.2") clients.RequireLong(t) client, err := clients.NewBareMetalV1Client() From ec017aefdb8ff99c331cf93e836a0be218d1b124 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 16 Sep 2025 13:47:35 +0100 Subject: [PATCH 2168/2296] Trigger "hold" workflow on merge groups Things are currently timing out due to this being missing. Signed-off-by: Stephen Finucane --- .github/workflows/check-pr-labels.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check-pr-labels.yaml b/.github/workflows/check-pr-labels.yaml index 4fbb7fc0b8..8d3cdde07b 100644 --- a/.github/workflows/check-pr-labels.yaml +++ b/.github/workflows/check-pr-labels.yaml @@ -1,5 +1,6 @@ name: Ready on: + merge_group: pull_request_target: types: - labeled From 6e9876786d0f6f876f0edfd465cd5e8785bf0a01 Mon Sep 17 00:00:00 2001 From: dlawton Date: Wed, 17 Sep 2025 11:55:23 +0100 Subject: [PATCH 2169/2296] Closes #2321 - Fix TestRolesCRUD by including DomainID to CreateOpts/ListOps structs. Signed-off-by: Dan Lawton --- internal/acceptance/openstack/identity/v3/roles_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index ec69a9ba4e..b17ecd9049 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -58,7 +58,8 @@ func TestRolesCRUD(t *testing.T) { th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ - Name: "testrole", + Name: "testrole", + DomainID: "default", Extra: map[string]any{ "description": "test role description", }, @@ -72,7 +73,9 @@ func TestRolesCRUD(t *testing.T) { tools.PrintResource(t, role) tools.PrintResource(t, role.Extra) - listOpts := roles.ListOpts{} + listOpts := roles.ListOpts{ + DomainID: "default", + } allPages, err := roles.List(client, listOpts).AllPages(context.TODO()) th.AssertNoErr(t, err) From 04310b14eaea6d657b3c0f8b189300a454e66f01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:18:07 +0000 Subject: [PATCH 2170/2296] build(deps): bump golang.org/x/crypto from 0.41.0 to 0.42.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.41.0 to 0.42.0. - [Commits](https://github.com/golang/crypto/compare/v0.41.0...v0.42.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.42.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 8 +++----- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 9b62787571..57fd884f66 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,10 @@ module github.com/gophercloud/gophercloud/v2 -go 1.23.0 - -toolchain go1.23.6 +go 1.24.0 require ( - golang.org/x/crypto v0.41.0 + golang.org/x/crypto v0.42.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.35.0 // indirect +require golang.org/x/sys v0.36.0 // indirect diff --git a/go.sum b/go.sum index f1687a61ee..ee0ae6bec7 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= 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 38b64e2568c3acbe24770e62982f760009638c47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:18:27 +0000 Subject: [PATCH 2171/2296] build(deps): bump kiegroup/git-backporting from 4.8.5 to 4.8.6 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.8.5 to 4.8.6. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/7ff4fce545cf2b9170c91c032bf66a9348ba2490...7d895d030f5cf02f4a76c7f0bc79b41d8747b17c) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-version: 4.8.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 00e787285f..86504701ea 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -34,7 +34,7 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') || contains(github.event.label.name, 'semver:patch') - uses: kiegroup/git-backporting@7ff4fce545cf2b9170c91c032bf66a9348ba2490 + uses: kiegroup/git-backporting@7d895d030f5cf02f4a76c7f0bc79b41d8747b17c with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -97,7 +97,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@7ff4fce545cf2b9170c91c032bf66a9348ba2490 + uses: kiegroup/git-backporting@7d895d030f5cf02f4a76c7f0bc79b41d8747b17c with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From 50cc13cc4e16020dfaf14b2474aaeacefffd28c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:18:29 +0000 Subject: [PATCH 2172/2296] build(deps): bump actions/labeler from 5 to 6 Bumps [actions/labeler](https://github.com/actions/labeler) from 5 to 6. - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/labeler dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/label-pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index 15abc3203c..578694dc72 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -84,4 +84,4 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - uses: actions/labeler@v6 From 2ab97519c65cf5612ceddf118258cd45d4de2f1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:18:53 +0000 Subject: [PATCH 2173/2296] build(deps): bump actions/setup-go from 5 to 6 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yaml | 2 +- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- .github/workflows/label-pr.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/unit.yaml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 58952951c6..b24893a9e5 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index f850bd11ab..1fe5fadf50 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -100,7 +100,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 756c742cc2..70acbf3762 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -58,7 +58,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 7c4ab14bac..86f7de3671 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -64,7 +64,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 8d932e44b5..5322a86401 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -64,7 +64,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index bca3ee49b0..6172dccb03 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -94,7 +94,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 51adcd0535..ee57512203 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -72,7 +72,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 3fd3e971eb..5e2cadc271 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -87,7 +87,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index f1c920981b..37622af366 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -62,7 +62,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index eb299c93b3..c05fad1e80 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -62,7 +62,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index b5db25d8ca..44d9607067 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -78,7 +78,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 02df282845..3f73436037 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -73,7 +73,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 7f8ef7cc8e..0222956e89 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -65,7 +65,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 4ae22544f0..28107f4fc5 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -82,7 +82,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 38d37a793e..fb71022fba 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -68,7 +68,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 4322089751..45859d2fac 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -64,7 +64,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 6897d549f1..2219c99a0a 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -62,7 +62,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 6cd3958ce1..cac6da18da 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -86,7 +86,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 13538ff410..4ac0dde74d 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -68,7 +68,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index 15abc3203c..e9955df9b8 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -24,7 +24,7 @@ jobs: env: GIT_SEQUENCE_EDITOR: '/usr/bin/true' - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version: '1' diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index f1696959e9..9c2d4787fa 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v5 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version: '1' - name: Run linters diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index ee7eb7fd58..4cda470c05 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -17,7 +17,7 @@ jobs: - name: Checkout Gophercloud uses: actions/checkout@v5 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: 'go.mod' cache: true From caa35e72707ea098b40ca0e9ecd4ea1e99912754 Mon Sep 17 00:00:00 2001 From: Sharpz7 Date: Wed, 17 Sep 2025 19:17:51 +0000 Subject: [PATCH 2174/2296] Added comment addressing IRONIC_BAREMETAL_BASIC_OPS=false --- .github/workflows/functional-baremetal.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 1fe5fadf50..c4c9929f6d 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -57,6 +57,9 @@ jobs: if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: sudo apt-get purge -y dnsmasq-base + # NOTE(sharpz7) IRONIC_BAREMETAL_BASIC_OPS was originally set to false as it + # was failing (https://review.opendev.org/c/openstack/ironic/+/960299) + # See https://github.com/gophercloud/gophercloud/pull/3500 - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 From 816179ee8a7c0722341c8213ba7e53dd937e97e7 Mon Sep 17 00:00:00 2001 From: Sharpz7 Date: Wed, 17 Sep 2025 19:38:19 +0000 Subject: [PATCH 2175/2296] Changed wording --- .github/workflows/functional-baremetal.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index c4c9929f6d..341060fbb0 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -60,6 +60,7 @@ jobs: # NOTE(sharpz7) IRONIC_BAREMETAL_BASIC_OPS was originally set to false as it # was failing (https://review.opendev.org/c/openstack/ironic/+/960299) # See https://github.com/gophercloud/gophercloud/pull/3500 + # This change however is suitable longer-term as it is not necessary for SDK testing. - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 From 8397c0eaf24b36ee4a0fbb40ec968407e36b94d8 Mon Sep 17 00:00:00 2001 From: Pierre Prinetti Date: Sun, 21 Sep 2025 15:47:09 +0200 Subject: [PATCH 2176/2296] refactor: Trivial fixes Address some gopls suggestions. The behaviour of the code is expected to remain unchanged. --- pagination/testing/doc.go | 2 -- provider_client.go | 13 +++---------- util.go | 3 --- 3 files changed, 3 insertions(+), 15 deletions(-) delete mode 100644 pagination/testing/doc.go diff --git a/pagination/testing/doc.go b/pagination/testing/doc.go deleted file mode 100644 index 0bc1eb3807..0000000000 --- a/pagination/testing/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// pagination -package testing diff --git a/provider_client.go b/provider_client.go index 598596c61e..a75592a2d1 100644 --- a/provider_client.go +++ b/provider_client.go @@ -7,6 +7,7 @@ import ( "errors" "io" "net/http" + "slices" "strings" "sync" ) @@ -437,16 +438,8 @@ func (client *ProviderClient) doRequest(ctx context.Context, method, url string, okc = defaultOkCodes(method) } - // Validate the HTTP response status. - var ok bool - for _, code := range okc { - if resp.StatusCode == code { - ok = true - break - } - } - - if !ok { + // Check the response code against the acceptable codes + if !slices.Contains(okc, resp.StatusCode) { body, _ := io.ReadAll(resp.Body) resp.Body.Close() respErr := ErrUnexpectedResponseCode{ diff --git a/util.go b/util.go index ad8a7dfaaa..d11a723b1b 100644 --- a/util.go +++ b/util.go @@ -37,9 +37,6 @@ func NormalizePathURL(basePath, rawPath string) (string, error) { absPathSys = filepath.Join(basePath, rawPath) u.Path = filepath.ToSlash(absPathSys) - if err != nil { - return "", err - } u.Scheme = "file" return u.String(), nil } From 80936c08a751cea1b551f160680e82e08b6fb4b4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 24 Sep 2025 16:34:28 +0100 Subject: [PATCH 2177/2296] Bump golangci-lint, gotestsum Signed-off-by: Stephen Finucane --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2d9cc5939b..e5b77e0369 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ undefine GOFLAGS -GOLANGCI_LINT_VERSION?=v2.1.6 -GOTESTSUM_VERSION?=v1.12.2 +GOLANGCI_LINT_VERSION?=v2.5.0 +GOTESTSUM_VERSION?=v1.13.0 GO_TEST?=go run gotest.tools/gotestsum@$(GOTESTSUM_VERSION) --format testname -- TIMEOUT := "60m" From b23a891a4d00f5a7a19be0709dc164e41180eac3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 26 Sep 2025 13:10:10 +0100 Subject: [PATCH 2178/2296] compute: Add host aggregate uuid field Signed-off-by: Stephen Finucane --- openstack/compute/v2/aggregates/results.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openstack/compute/v2/aggregates/results.go b/openstack/compute/v2/aggregates/results.go index 0d4794acff..8e67edd87c 100644 --- a/openstack/compute/v2/aggregates/results.go +++ b/openstack/compute/v2/aggregates/results.go @@ -39,6 +39,10 @@ type Aggregate struct { // A boolean indicates whether this aggregate is deleted or not, // if it has not been deleted, false will appear. Deleted bool `json:"deleted"` + + // The UUID of the aggregate. + // The requires microversion 2.41 or later. + UUID string `json:"uuid"` } // UnmarshalJSON to override default From bba1cb59c99a01e8a2ab98a6c7e7aa43fc275d3a Mon Sep 17 00:00:00 2001 From: Bram Kranendonk Date: Sat, 27 Sep 2025 13:19:35 +0200 Subject: [PATCH 2179/2296] Add image uploading status --- openstack/image/v2/images/types.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openstack/image/v2/images/types.go b/openstack/image/v2/images/types.go index 147be19927..eedc13a330 100644 --- a/openstack/image/v2/images/types.go +++ b/openstack/image/v2/images/types.go @@ -13,10 +13,14 @@ const ( // been reserved for an image in the image registry. ImageStatusQueued ImageStatus = "queued" - // ImageStatusSaving denotes that an image’s raw data is currently being + // ImageStatusSaving denotes that an image's raw data is currently being // uploaded to Glance ImageStatusSaving ImageStatus = "saving" + // ImageStatusUploading denotes that an image's raw data is currently being + // uploaded to Glance through the upload process + ImageStatusUploading ImageStatus = "uploading" + // ImageStatusActive denotes an image that is fully available in Glance. ImageStatusActive ImageStatus = "active" From 81eb442beecea50c0a41c3fb21ba5f7ec5712bdf Mon Sep 17 00:00:00 2001 From: Maximilian Meister Date: Wed, 24 Sep 2025 08:47:57 +0200 Subject: [PATCH 2180/2296] Enable load balancer quota deletion --- .../openstack/loadbalancer/v2/quotas_test.go | 10 ++++++++++ openstack/loadbalancer/v2/quotas/doc.go | 10 ++++++++++ openstack/loadbalancer/v2/quotas/requests.go | 6 ++++++ openstack/loadbalancer/v2/quotas/results.go | 6 ++++++ .../v2/quotas/testing/fixtures_test.go | 18 +++++++++++++++++- .../v2/quotas/testing/requests_test.go | 10 ++++++++++ openstack/loadbalancer/v2/quotas/urls.go | 4 ++++ 7 files changed, 63 insertions(+), 1 deletion(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go index cc778e5403..87f9d7621f 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/quotas_test.go @@ -80,3 +80,13 @@ func TestQuotasUpdate(t *testing.T) { tools.PrintResource(t, restoredQuotas) } + +func TestQuotasDelete(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + err = quotas.Delete(context.TODO(), client, os.Getenv("OS_PROJECT_NAME")).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/loadbalancer/v2/quotas/doc.go b/openstack/loadbalancer/v2/quotas/doc.go index c2c63512c3..77c5600226 100644 --- a/openstack/loadbalancer/v2/quotas/doc.go +++ b/openstack/loadbalancer/v2/quotas/doc.go @@ -30,5 +30,15 @@ Example to Update project quotas } fmt.Printf("quotas: %#v\n", quotasInfo) + +Example to Delete project quotas + + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + err := quotas.Delete(context.TODO(), networkClient, projectID).ExtractErr() + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Deleted quotas for project: %s\n", projectID) */ package quotas diff --git a/openstack/loadbalancer/v2/quotas/requests.go b/openstack/loadbalancer/v2/quotas/requests.go index 8b7771eea8..f07f9392e9 100644 --- a/openstack/loadbalancer/v2/quotas/requests.go +++ b/openstack/loadbalancer/v2/quotas/requests.go @@ -63,3 +63,9 @@ func Update(ctx context.Context, c *gophercloud.ServiceClient, projectID string, _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +func Delete(ctx context.Context, c *gophercloud.ServiceClient, projectID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, projectID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/loadbalancer/v2/quotas/results.go b/openstack/loadbalancer/v2/quotas/results.go index e1ef385982..5487d7a84e 100644 --- a/openstack/loadbalancer/v2/quotas/results.go +++ b/openstack/loadbalancer/v2/quotas/results.go @@ -96,3 +96,9 @@ func (r *Quota) UnmarshalJSON(b []byte) error { return nil } + +// 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 +} diff --git a/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go b/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go index e5185b2012..e4609aa868 100644 --- a/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go +++ b/openstack/loadbalancer/v2/quotas/testing/fixtures_test.go @@ -1,6 +1,13 @@ package testing -import "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/quotas" +import ( + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/quotas" + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) const GetResponseRaw_1 = ` { @@ -77,3 +84,12 @@ var UpdateResponse = quotas.Quota{ L7Policy: 50, L7Rule: 100, } + +func MockDeleteResponse(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/v2.0/quotas/0a73845280574ad389c292f6a74afa76", 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/loadbalancer/v2/quotas/testing/requests_test.go b/openstack/loadbalancer/v2/quotas/testing/requests_test.go index ccf8e51f35..ab3ba6a177 100644 --- a/openstack/loadbalancer/v2/quotas/testing/requests_test.go +++ b/openstack/loadbalancer/v2/quotas/testing/requests_test.go @@ -105,3 +105,13 @@ func TestUpdate_2(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &UpdateResponse) } + +func TestDelete(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + MockDeleteResponse(t, fakeServer) + + res := quotas.Delete(context.TODO(), fake.ServiceClient(fakeServer), "0a73845280574ad389c292f6a74afa76") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/loadbalancer/v2/quotas/urls.go b/openstack/loadbalancer/v2/quotas/urls.go index 0365d9c8ad..545e832307 100644 --- a/openstack/loadbalancer/v2/quotas/urls.go +++ b/openstack/loadbalancer/v2/quotas/urls.go @@ -15,3 +15,7 @@ func getURL(c *gophercloud.ServiceClient, projectID string) string { func updateURL(c *gophercloud.ServiceClient, projectID string) string { return resourceURL(c, projectID) } + +func deleteURL(c *gophercloud.ServiceClient, projectID string) string { + return getURL(c, projectID) +} From 1abcebd1252541ce49fe6a5bb2c1a6cebf6f7ed6 Mon Sep 17 00:00:00 2001 From: Maximilian Meister Date: Wed, 24 Sep 2025 09:34:08 +0200 Subject: [PATCH 2181/2296] Enable network quota deletion --- .../networking/v2/extensions/quotas/quotas_test.go | 10 ++++++++++ openstack/networking/v2/extensions/quotas/doc.go | 10 ++++++++++ .../networking/v2/extensions/quotas/requests.go | 6 ++++++ .../networking/v2/extensions/quotas/results.go | 6 ++++++ .../v2/extensions/quotas/testing/fixtures_test.go | 14 ++++++++++++++ .../v2/extensions/quotas/testing/requests_test.go | 10 ++++++++++ openstack/networking/v2/extensions/quotas/urls.go | 4 ++++ 7 files changed, 60 insertions(+) diff --git a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go index 3eaf7d232b..06b2280791 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/quotas/quotas_test.go @@ -63,3 +63,13 @@ func TestQuotasUpdate(t *testing.T) { tools.PrintResource(t, restoredQuotas) } + +func TestQuotasDelete(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + err = quotas.Delete(context.TODO(), client, os.Getenv("OS_PROJECT_NAME")).ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/networking/v2/extensions/quotas/doc.go b/openstack/networking/v2/extensions/quotas/doc.go index fe1cc26311..5e5ec3e58a 100644 --- a/openstack/networking/v2/extensions/quotas/doc.go +++ b/openstack/networking/v2/extensions/quotas/doc.go @@ -43,5 +43,15 @@ Example to Update project quotas } fmt.Printf("quotas: %#v\n", quotasInfo) + +Example to Delete project quotas + + projectID = "23d5d3f79dfa4f73b72b8b0b0063ec55" + err := quotas.Delete(context.TODO(), networkClient, projectID).ExtractErr() + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Deleted quotas for project: %s\n", projectID) */ package quotas diff --git a/openstack/networking/v2/extensions/quotas/requests.go b/openstack/networking/v2/extensions/quotas/requests.go index 859c552cef..6289368841 100644 --- a/openstack/networking/v2/extensions/quotas/requests.go +++ b/openstack/networking/v2/extensions/quotas/requests.go @@ -78,3 +78,9 @@ func Update(ctx context.Context, c *gophercloud.ServiceClient, projectID string, _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +func Delete(ctx context.Context, c *gophercloud.ServiceClient, projectID string) (r DeleteResult) { + resp, err := c.Delete(ctx, deleteURL(c, projectID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/networking/v2/extensions/quotas/results.go b/openstack/networking/v2/extensions/quotas/results.go index 249d115175..5fa10cf21d 100644 --- a/openstack/networking/v2/extensions/quotas/results.go +++ b/openstack/networking/v2/extensions/quotas/results.go @@ -171,3 +171,9 @@ func (q *QuotaDetail) UnmarshalJSON(b []byte) error { return nil } + +// 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 +} diff --git a/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go b/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go index a4a7703c89..1f34dead15 100644 --- a/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/quotas/testing/fixtures_test.go @@ -1,7 +1,12 @@ package testing import ( + "net/http" + "testing" + + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/quotas" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) const GetResponseRaw = ` @@ -140,3 +145,12 @@ var UpdateResponse = quotas.Quota{ SubnetPool: 0, Trunk: 5, } + +func MockDeleteResponse(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/v2.0/quotas/0a73845280574ad389c292f6a74afa76", 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/networking/v2/extensions/quotas/testing/requests_test.go b/openstack/networking/v2/extensions/quotas/testing/requests_test.go index 597edd4853..e4550c11fd 100644 --- a/openstack/networking/v2/extensions/quotas/testing/requests_test.go +++ b/openstack/networking/v2/extensions/quotas/testing/requests_test.go @@ -80,3 +80,13 @@ func TestUpdate(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, q, &UpdateResponse) } + +func TestDelete(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + MockDeleteResponse(t, fakeServer) + + res := quotas.Delete(context.TODO(), fake.ServiceClient(fakeServer), "0a73845280574ad389c292f6a74afa76") + th.AssertNoErr(t, res.Err) +} diff --git a/openstack/networking/v2/extensions/quotas/urls.go b/openstack/networking/v2/extensions/quotas/urls.go index 94cbe23880..726c4da8eb 100644 --- a/openstack/networking/v2/extensions/quotas/urls.go +++ b/openstack/networking/v2/extensions/quotas/urls.go @@ -24,3 +24,7 @@ func getDetailURL(c *gophercloud.ServiceClient, projectID string) string { func updateURL(c *gophercloud.ServiceClient, projectID string) string { return resourceURL(c, projectID) } + +func deleteURL(c *gophercloud.ServiceClient, projectID string) string { + return getURL(c, projectID) +} From a108557f9118bab942a45317e2860570931da621 Mon Sep 17 00:00:00 2001 From: Maximilian Meister Date: Wed, 1 Oct 2025 08:08:57 +0200 Subject: [PATCH 2182/2296] Add acceptance testing hint to contribution docs Also fix broken link --- docs/contributor-tutorial/step-04-acceptance-testing.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/contributor-tutorial/step-04-acceptance-testing.md b/docs/contributor-tutorial/step-04-acceptance-testing.md index fe82717439..a6708ee5fb 100644 --- a/docs/contributor-tutorial/step-04-acceptance-testing.md +++ b/docs/contributor-tutorial/step-04-acceptance-testing.md @@ -17,9 +17,13 @@ And, to be frank, submitting untested code will inevitably cause someone else to have to spend time fixing it. If you don't have an OpenStack environment to test with, we have lots of -documentation [here](/acceptance) to help you build your own small OpenStack +documentation [here](/internal/acceptance) to help you build your own small OpenStack environment for testing. +If you add new API calls/actions e.g. also make sure to include them into the +respective [acceptance tests](/internal/acceptance/openstack) for the code to be +validated against all the supported OpenStack versions. + --- Once you've confirmed you are able to test your code, proceed to From c210c547b819a392d4b8e05e7d9380d45cf8afb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 09:13:26 +0000 Subject: [PATCH 2183/2296] build(deps): bump golang.org/x/crypto from 0.42.0 to 0.43.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.42.0 to 0.43.0. - [Commits](https://github.com/golang/crypto/compare/v0.42.0...v0.43.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.43.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 57fd884f66..033b59f3ec 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.24.0 require ( - golang.org/x/crypto v0.42.0 + golang.org/x/crypto v0.43.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.36.0 // indirect +require golang.org/x/sys v0.37.0 // indirect diff --git a/go.sum b/go.sum index ee0ae6bec7..fc73315e9a 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= 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 ffb0d11eb2c7059b30c29f5a5b02baa5fa506d6f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 24 Oct 2025 13:16:23 +0100 Subject: [PATCH 2184/2296] docs: Document tested releases for acceptance tests Signed-off-by: Stephen Finucane --- docs/contributor-tutorial/step-05-pull-requests.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/contributor-tutorial/step-05-pull-requests.md b/docs/contributor-tutorial/step-05-pull-requests.md index c65eff8789..5f29a938e4 100644 --- a/docs/contributor-tutorial/step-05-pull-requests.md +++ b/docs/contributor-tutorial/step-05-pull-requests.md @@ -99,7 +99,10 @@ generate your own fixtures using the OpenStack environment you're Since unit tests are not run against an actual OpenStack environment, acceptance tests can arguably be more important. The acceptance tests that you include in your Pull Request should confirm that your implemented code works -as intended with an actual OpenStack environment. +as intended with an actual OpenStack environment. We run our functional jobs +against all supported Skip Level Upgrade Release Process (SLURP) releases as +well as `master`. This information is sourced from +[releases.openstack.org](https://releases.openstack.org/). ### Documentation From 42acda1a102aab46e920f4dd8ba3b11a021e284e Mon Sep 17 00:00:00 2001 From: Local rebase Date: Fri, 24 Oct 2025 12:08:24 +0100 Subject: [PATCH 2185/2296] Identity: Add Options field to roles. Signed-off-by: Dan Lawton --- .../openstack/identity/v3/roles_test.go | 6 ++ openstack/identity/v3/roles/requests.go | 14 ++++ openstack/identity/v3/roles/results.go | 3 + .../v3/roles/testing/fixtures_test.go | 82 +++++++++++++++++-- .../v3/roles/testing/requests_test.go | 24 ++++++ 5 files changed, 122 insertions(+), 7 deletions(-) diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index b17ecd9049..96337dc4c1 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -63,6 +63,9 @@ func TestRolesCRUD(t *testing.T) { Extra: map[string]any{ "description": "test role description", }, + Options: map[roles.Option]any{ + "immutable": false, + }, } // Create Role in the default domain @@ -98,6 +101,9 @@ func TestRolesCRUD(t *testing.T) { Extra: map[string]any{ "description": "updated test role description", }, + Options: map[roles.Option]any{ + "immutable": nil, + }, } newRole, err := roles.Update(context.TODO(), client, role.ID, updateOpts).Extract() diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 2e40e93d6d..0552dc2852 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -9,6 +9,14 @@ import ( "github.com/gophercloud/gophercloud/v2/pagination" ) +// Option is a specific option defined at the API to enable features +// on a role. +type Option string + +const ( + Immutable Option = "immutable" +) + // ListOptsBuilder allows extensions to add additional parameters to // the List request type ListOptsBuilder interface { @@ -88,6 +96,9 @@ type CreateOpts struct { // Extra is free-form extra key/value pairs to describe the role. Extra map[string]any `json:"-"` + + // Options are defined options in the API to enable certain features. + Options map[Option]any `json:"options,omitempty"` } // ToRoleCreateMap formats a CreateOpts into a create request. @@ -135,6 +146,9 @@ type UpdateOpts struct { // Extra is free-form extra key/value pairs to describe the role. Extra map[string]any `json:"-"` + + // Options are defined options in the API to enable certain features. + Options map[Option]any `json:"options,omitempty"` } // ToRoleUpdateMap formats a UpdateOpts into an update request. diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index 43abaf6477..b2e348df06 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -23,6 +23,9 @@ type Role struct { // Extra is a collection of miscellaneous key/values. Extra map[string]any `json:"-"` + + // Options are a set of defined options that allow certain features for a role + Options map[Option]any `json:"options"` } func (r *Role) UnmarshalJSON(b []byte) error { diff --git a/openstack/identity/v3/roles/testing/fixtures_test.go b/openstack/identity/v3/roles/testing/fixtures_test.go index 1e8403eb64..3fa6171c1b 100644 --- a/openstack/identity/v3/roles/testing/fixtures_test.go +++ b/openstack/identity/v3/roles/testing/fixtures_test.go @@ -52,9 +52,25 @@ const GetOutput = ` "self": "https://example.com/identity/v3/roles/9fe1d3" }, "name": "support", - "extra": { - "description": "read-only support role" - } + "description": "read-only support role" + } +} +` + +// GetOutputWithOptions provides a Get result of a role with options. +const GetOutputWithOptions = ` +{ + "role": { + "domain_id": "1789d1", + "id": "9fe1d3", + "links": { + "self": "https://example.com/identity/v3/roles/9fe1d3" + }, + "name": "support", + "description": "read-only support role", + "options": { + "immutable": true + } } } ` @@ -65,8 +81,22 @@ const CreateRequest = ` "role": { "domain_id": "1789d1", "name": "support", - "description": "read-only support role" - } + "description": "read-only support role" + } +} +` + +// CreateRequestWithOptions provides the input to a Create request with Options. +const CreateRequestWithOptions = ` +{ + "role": { + "domain_id": "1789d1", + "name": "support", + "description": "read-only support role", + "options": { + "immutable": true + } + } } ` @@ -74,7 +104,10 @@ const CreateRequest = ` const UpdateRequest = ` { "role": { - "description": "admin read-only support role" + "description": "admin read-only support role", + "options": { + "immutable": false + } } } ` @@ -91,7 +124,10 @@ const UpdateOutput = ` "name": "support", "extra": { "description": "admin read-only support role" - } + }, + "options": { + "immutable": false + } } } ` @@ -324,6 +360,22 @@ var SecondRole = roles.Role{ }, } +// SecondRoleWithOptions is the second role in the List request +var SecondRoleWithOptions = roles.Role{ + DomainID: "1789d1", + ID: "9fe1d3", + Links: map[string]any{ + "self": "https://example.com/identity/v3/roles/9fe1d3", + }, + Name: "support", + Extra: map[string]any{ + "description": "read-only support role", + }, + Options: map[roles.Option]any{ + roles.Immutable: true, + }, +} + // SecondRoleUpdated is how SecondRole should look after an Update. var SecondRoleUpdated = roles.Role{ DomainID: "1789d1", @@ -335,6 +387,9 @@ var SecondRoleUpdated = roles.Role{ Extra: map[string]any{ "description": "admin read-only support role", }, + Options: map[roles.Option]any{ + roles.Immutable: false, + }, } // ExpectedRolesSlice is the slice of roles expected to be returned from ListOutput. @@ -381,6 +436,19 @@ func HandleCreateRoleSuccessfully(t *testing.T, fakeServer th.FakeServer) { }) } +// HandleCreateRoleWithOptionsSuccessfully creates an HTTP handler at `/roles` on the +// test handler mux that tests role creation. +func HandleCreateWithOptionsRoleSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/roles", 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, CreateRequestWithOptions) + + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, GetOutputWithOptions) + }) +} + // HandleUpdateRoleSuccessfully creates an HTTP handler at `/roles` on the // test handler mux that tests role update. func HandleUpdateRoleSuccessfully(t *testing.T, fakeServer th.FakeServer) { diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index fb3021064a..88da0a4b44 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -104,6 +104,27 @@ func TestCreateRole(t *testing.T) { th.CheckDeepEquals(t, SecondRole, *actual) } +func TestCreateWithOptionsRole(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleCreateWithOptionsRoleSuccessfully(t, fakeServer) + + createOpts := roles.CreateOpts{ + Name: "support", + DomainID: "1789d1", + Options: map[roles.Option]any{ + roles.Immutable: true, + }, + Extra: map[string]any{ + "description": "read-only support role", + }, + } + + actual, err := roles.Create(context.TODO(), client.ServiceClient(fakeServer), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, SecondRoleWithOptions, *actual) +} + func TestUpdateRole(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() @@ -113,6 +134,9 @@ func TestUpdateRole(t *testing.T) { Extra: map[string]any{ "description": "admin read-only support role", }, + Options: map[roles.Option]any{ + roles.Immutable: false, + }, } actual, err := roles.Update(context.TODO(), client.ServiceClient(fakeServer), "9fe1d3", updateOpts).Extract() From 398a995fc99b0092e565d901d06eee0537149210 Mon Sep 17 00:00:00 2001 From: ycalansy Date: Tue, 28 Oct 2025 17:13:09 +0000 Subject: [PATCH 2186/2296] fix: handle Nova create image response for microversion 2.45 and above Since microversion 2.45, the Location header is removed from the create image response. The image ID is now returned in the response body as JSON. CreateImage now reads the response body and stores it in CreateImageResult. The image ID extraction checks X-OpenStack-Nova-API-Version to determine whether to extract the ID from the Location header (< 2.45) or the response body (>= 2.45). --- openstack/compute/v2/servers/requests.go | 29 +++++++++++++++- openstack/compute/v2/servers/results.go | 34 ++++++++++++++++++- .../v2/servers/testing/fixtures_test.go | 31 +++++++++++++++-- .../v2/servers/testing/requests_test.go | 17 ++++++++-- 4 files changed, 103 insertions(+), 8 deletions(-) diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go index 6754d0a939..8d0685997d 100644 --- a/openstack/compute/v2/servers/requests.go +++ b/openstack/compute/v2/servers/requests.go @@ -1,10 +1,12 @@ package servers import ( + "bytes" "context" "encoding/base64" "encoding/json" "fmt" + "io" "maps" "net" "regexp" @@ -1053,10 +1055,35 @@ func CreateImage(ctx context.Context, client *gophercloud.ServiceClient, id stri r.Err = err return } + resp, err := client.Post(ctx, actionURL(client, id), b, nil, &gophercloud.RequestOpts{ - OkCodes: []int{202}, + OkCodes: []int{202}, + KeepResponseBody: true, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + if r.Err != nil { + return + } + defer resp.Body.Close() + + if v := r.Header.Get("Content-Type"); v != "application/json" { + return + } + + // The response body is expected to be a small JSON object containing only "image_id". + // Read it fully into memory so the response body can be closed immediately. + // If the caller doesn't read from the buffer, it can still be safely garbage collected. + + var buf bytes.Buffer + + _, r.Err = io.Copy(&buf, resp.Body) + if r.Err != nil { + return + } + + r.Body = &buf + return } diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 2685c1c969..f40760dc50 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -132,18 +133,49 @@ func (r CreateImageResult) ExtractImageID() (string, error) { if r.Err != nil { return "", r.Err } - // Get the image id from the header + + microversion := r.Header.Get("X-OpenStack-Nova-API-Version") + + major, minor, err := utils.ParseMicroversion(microversion) + if err != nil { + return "", fmt.Errorf("failed to parse X-OpenStack-Nova-API-Version header: %s", err) + } + + // In microversions prior to 2.45, the image ID was provided in the Location header. + if major < 2 || (major == 2 && minor < 45) { + return r.extractImageIDFromLocationHeader() + } + + // Starting from 2.45, it is included in the response body. + return r.extractImageIDFromResponseBody() +} + +func (r CreateImageResult) extractImageIDFromLocationHeader() (string, error) { u, err := url.ParseRequestURI(r.Header.Get("Location")) if err != nil { return "", err } + imageID := path.Base(u.Path) if imageID == "." || imageID == "/" { return "", fmt.Errorf("failed to parse the ID of newly created image: %s", u) } + return imageID, nil } +func (r CreateImageResult) extractImageIDFromResponseBody() (string, error) { + var response struct { + ImageID string `json:"image_id"` + } + + if err := r.ExtractInto(&response); err != nil { + return "", err + } + + return response.ImageID, nil +} + // Server represents a server/instance in the OpenStack cloud. type Server struct { // ID uniquely identifies this server amongst all other servers, diff --git a/openstack/compute/v2/servers/testing/fixtures_test.go b/openstack/compute/v2/servers/testing/fixtures_test.go index 5c5153887a..206e20a53c 100644 --- a/openstack/compute/v2/servers/testing/fixtures_test.go +++ b/openstack/compute/v2/servers/testing/fixtures_test.go @@ -1303,14 +1303,39 @@ func HandleNetworkAddressListSuccessfully(t *testing.T, fakeServer th.FakeServer }) } -// HandleCreateServerImageSuccessfully sets up the test server to respond to a TestCreateServerImage request. -func HandleCreateServerImageSuccessfully(t *testing.T, fakeServer th.FakeServer) { +// HandleCreateServerImageSuccessfullyBeforeMicroversion_2_45 sets up the test server to respond to a TestCreateServerImageBeforeMicroversion_2_45 request. +func HandleCreateServerImageSuccessfullyBeforeMicroversion_2_45(t *testing.T, fakeServer th.FakeServer) string { + imageID := "xxxx-xxxxx-xxxxx-xxxx" + fakeServer.Mux.HandleFunc("/servers/serverimage/action", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "POST") th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Location", "https://0.0.0.0/images/xxxx-xxxxx-xxxxx-xxxx") + + w.Header().Set("Location", fmt.Sprintf("https://0.0.0.0/images/%s", imageID)) + w.Header().Set("X-OpenStack-Nova-API-Version", "2.44") w.WriteHeader(http.StatusAccepted) }) + + return imageID +} + +// HandleCreateServerImageSuccessfullySinceMicroversion_2_45 sets up the test server to respond to a TestCreateServerImageSinceMicroversion_2_45 request. +func HandleCreateServerImageSuccessfullySinceMicroversion_2_45(t *testing.T, fakeServer th.FakeServer) string { + imageID := "yyyy-yyyyy-yyyyy-yyyyy" + + fakeServer.Mux.HandleFunc("/servers/serverimage/action", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Set("Content-Type", "application/json") + w.Header().Set("X-OpenStack-Nova-API-Version", "2.45") + w.WriteHeader(http.StatusAccepted) + + _, err := w.Write(fmt.Appendf(nil, `{"image_id":"%s"}`, imageID)) + th.AssertNoErr(t, err) + }) + + return imageID } // HandlePasswordGetSuccessfully sets up the test server to respond to a password Get request. diff --git a/openstack/compute/v2/servers/testing/requests_test.go b/openstack/compute/v2/servers/testing/requests_test.go index 96cb77f14e..7127a09a38 100644 --- a/openstack/compute/v2/servers/testing/requests_test.go +++ b/openstack/compute/v2/servers/testing/requests_test.go @@ -1092,13 +1092,24 @@ func TestListAddressesByNetwork(t *testing.T) { th.CheckEquals(t, 1, pages) } -func TestCreateServerImage(t *testing.T) { +func TestCreateServerImageBeforeMicroversion_2_45(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() - HandleCreateServerImageSuccessfully(t, fakeServer) + expected := HandleCreateServerImageSuccessfullyBeforeMicroversion_2_45(t, fakeServer) - _, err := servers.CreateImage(context.TODO(), client.ServiceClient(fakeServer), "serverimage", servers.CreateImageOpts{Name: "test"}).ExtractImageID() + imageID, err := servers.CreateImage(context.TODO(), client.ServiceClient(fakeServer), "serverimage", servers.CreateImageOpts{Name: "test"}).ExtractImageID() th.AssertNoErr(t, err) + th.AssertEquals(t, expected, imageID) +} + +func TestCreateServerImageSinceMicroversion_2_45(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + expected := HandleCreateServerImageSuccessfullySinceMicroversion_2_45(t, fakeServer) + + imageID, err := servers.CreateImage(context.TODO(), client.ServiceClient(fakeServer), "serverimage", servers.CreateImageOpts{Name: "test"}).ExtractImageID() + th.AssertNoErr(t, err) + th.AssertEquals(t, expected, imageID) } func TestMarshalPersonality(t *testing.T) { From 811a469a51a991cbb4f57d96255bafbb434f019d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 19 May 2025 10:37:18 +0100 Subject: [PATCH 2187/2296] Bump Epoxy jobs to Ubuntu 24.04 (Noble) Now that the DevStack fix has landed [1]. [1] https://review.opendev.org/c/openstack/devstack/+/950289 Signed-off-by: Stephen Finucane --- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 70acbf3762..c5e5fdedfd 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 86f7de3671..81562b182c 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 5322a86401..928269cda2 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 6172dccb03..b6cb29fd5e 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -25,7 +25,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" devstack_conf_overrides: | # ensure we're using a working version of setuptools if [ -n "\$TOP_DIR" ]; then diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index ee57512203..3446262d23 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -22,7 +22,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 5e2cadc271..88f7e2a2f2 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -20,7 +20,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 37622af366..c15fcbe41d 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index c05fad1e80..0c28af851c 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 44d9607067..a24aaabf01 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -22,7 +22,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" devstack_conf_overrides: | # ensure we're using a working version of setuptools if [ -n "\$TOP_DIR" ]; then diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 3f73436037..7b0e64937e 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -22,7 +22,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 0222956e89..8275e912ff 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 28107f4fc5..ed0e6ecefe 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index fb71022fba..c0b61d85e6 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 45859d2fac..1c2b60a20d 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 2219c99a0a..aaf8252c84 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -16,7 +16,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index cac6da18da..5c57a53d08 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -22,7 +22,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 4ac0dde74d..aff65e5e6f 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -17,7 +17,7 @@ jobs: additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" - ubuntu_version: "22.04" + ubuntu_version: "24.04" mistral_plugin_version: "stable/2025.1" additional_services: "openstack-cli-server" - name: "dalmatian" From 06ae96c3995f6d4718e0d507055aef43cf118428 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 30 Oct 2025 12:23:39 +0000 Subject: [PATCH 2188/2296] Drop Caracal jobs This is now EOL. Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 4 ---- .github/workflows/functional-basic.yaml | 4 ---- .github/workflows/functional-blockstorage.yaml | 4 ---- .github/workflows/functional-compute.yaml | 4 ---- .github/workflows/functional-containerinfra.yaml | 7 ------- .github/workflows/functional-dns.yaml | 4 ---- .github/workflows/functional-fwaas_v2.yaml | 4 ---- .github/workflows/functional-identity.yaml | 4 ---- .github/workflows/functional-image.yaml | 4 ---- .github/workflows/functional-keymanager.yaml | 4 ---- .github/workflows/functional-loadbalancer.yaml | 4 ---- .github/workflows/functional-messaging.yaml | 4 ---- .github/workflows/functional-networking.yaml | 4 ---- .github/workflows/functional-objectstorage.yaml | 4 ---- .github/workflows/functional-orchestration.yaml | 4 ---- .github/workflows/functional-placement.yaml | 4 ---- .github/workflows/functional-sharedfilesystems.yaml | 4 ---- .github/workflows/functional-workflow.yaml | 5 ----- 18 files changed, 76 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 341060fbb0..84f020d184 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Ironic on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index c5e5fdedfd..7c16e6f97d 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: basic tests on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 81562b182c..ba0571b79f 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Cinder on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 928269cda2..2fca7a2fd0 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Nova on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index b6cb29fd5e..fa28ceceb6 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -43,13 +43,6 @@ jobs: enable_plugin magnum https://github.com/openstack/magnum stable/2024.2 MAGNUMCLIENT_BRANCH=stable/2024.2 additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum stable/2024.1 - MAGNUMCLIENT_BRANCH=stable/2024.1 - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Magnum on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 3446262d23..afbe468765 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -28,10 +28,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Designate on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 88f7e2a2f2..9072610440 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -26,10 +26,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: FWaaS_v2 on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index c15fcbe41d..90423332e9 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Keystone on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 0c28af851c..e6534ce026 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Glance on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index a24aaabf01..40da884938 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -34,10 +34,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Barbican on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 7b0e64937e..e5e3914323 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -28,10 +28,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Octavia on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 8275e912ff..c13dd3a5b2 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Zaqar on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index ed0e6ecefe..afc0f0de8e 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Neutron on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index c0b61d85e6..82406692a0 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Swift on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 1c2b60a20d..9e4242e0bc 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Heat on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index aaf8252c84..e2655d1daf 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -22,10 +22,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Placement on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 5c57a53d08..c87cf8d75a 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -28,10 +28,6 @@ jobs: openstack_version: "stable/2024.2" ubuntu_version: "22.04" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Manila on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index aff65e5e6f..c9f38bc591 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -25,11 +25,6 @@ jobs: ubuntu_version: "22.04" mistral_plugin_version: "stable/2024.2" additional_services: "openstack-cli-server" - - name: "caracal" - openstack_version: "stable/2024.1" - ubuntu_version: "22.04" - mistral_plugin_version: "stable/2024.1" - additional_services: "" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Mistral on Deploy OpenStack ${{ matrix.name }} steps: From 3260d638cc43cfa067de33d2c2790149db2cd538 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 30 Oct 2025 12:24:34 +0000 Subject: [PATCH 2189/2296] Always enable openstack-cli-server All tested versions of OpenStack now support this allowing us to simplify our jobs. Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 5 +---- .github/workflows/functional-basic.yaml | 5 +---- .github/workflows/functional-blockstorage.yaml | 5 +---- .github/workflows/functional-compute.yaml | 5 +---- .github/workflows/functional-containerinfra.yaml | 5 +---- .github/workflows/functional-dns.yaml | 5 +---- .github/workflows/functional-fwaas_v2.yaml | 5 +---- .github/workflows/functional-identity.yaml | 5 +---- .github/workflows/functional-image.yaml | 5 +---- .github/workflows/functional-keymanager.yaml | 5 +---- .github/workflows/functional-loadbalancer.yaml | 5 +---- .github/workflows/functional-messaging.yaml | 5 +---- .github/workflows/functional-networking.yaml | 5 +---- .github/workflows/functional-objectstorage.yaml | 5 +---- .github/workflows/functional-orchestration.yaml | 5 +---- .github/workflows/functional-placement.yaml | 5 +---- .github/workflows/functional-sharedfilesystems.yaml | 5 +---- .github/workflows/functional-workflow.yaml | 5 +---- 18 files changed, 18 insertions(+), 72 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 84f020d184..802e886901 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Ironic on OpenStack ${{ matrix.name }} steps: @@ -96,7 +93,7 @@ jobs: IRONIC_ENABLED_DEPLOY_INTERFACES=direct,fake 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,${{ matrix.additional_services }}" + 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,openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 7c16e6f97d..7cc97ce2ed 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: basic tests on OpenStack ${{ matrix.name }} steps: @@ -50,7 +47,7 @@ jobs: uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} - enabled_services: 's-account,s-container,s-object,s-proxy,${{ matrix.additional_services }}' + enabled_services: 's-account,s-container,s-object,s-proxy,openstack-cli-server' - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index ba0571b79f..4e88bcf39a 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Cinder on OpenStack ${{ matrix.name }} steps: @@ -56,7 +53,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | CINDER_ISCSI_HELPER=lioadm - enabled_services: "s-account,s-container,s-object,s-proxy,c-bak,${{ matrix.additional_services }}" + enabled_services: "s-account,s-container,s-object,s-proxy,c-bak,openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 2fca7a2fd0..c798e9c5b7 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Nova on OpenStack ${{ matrix.name }} steps: @@ -56,7 +53,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | CINDER_ISCSI_HELPER=lioadm - enabled_services: "${{ matrix.additional_services }}" + enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index fa28ceceb6..c672171b46 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -22,7 +22,6 @@ jobs: enable_plugin magnum https://github.com/openstack/magnum master MAGNUMCLIENT_BRANCH=master - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" @@ -35,14 +34,12 @@ jobs: enable_plugin magnum https://github.com/openstack/magnum stable/2025.1 MAGNUMCLIENT_BRANCH=stable/2025.1 - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" devstack_conf_overrides: | enable_plugin magnum https://github.com/openstack/magnum stable/2024.2 MAGNUMCLIENT_BRANCH=stable/2024.2 - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Magnum on OpenStack ${{ matrix.name }} steps: @@ -83,7 +80,7 @@ jobs: KEYSTONE_ADMIN_ENDPOINT=true ${{ matrix.devstack_conf_overrides }} - enabled_services: "h-eng,h-api,h-api-cfn,h-api-cw,${{ matrix.additional_services }}" + enabled_services: "h-eng,h-api,h-api-cfn,h-api-cw,openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index afbe468765..e4aa1e8caa 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -19,15 +19,12 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Designate on OpenStack ${{ matrix.name }} steps: @@ -64,7 +61,7 @@ jobs: enable_plugin designate https://github.com/openstack/designate ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} - enabled_services: "designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns,${{ matrix.additional_services }}" + enabled_services: "designate,designate-central,designate-api,designate-worker,designate-producer,designate-mdns,openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 9072610440..4d722a5d52 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -17,15 +17,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: FWaaS_v2 on OpenStack ${{ matrix.name }} steps: @@ -79,7 +76,7 @@ jobs: [[post-config|\$NEUTRON_CONF]] [oslo_policy] policy_dirs = /tmp/neutron-policies - 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,${{ matrix.additional_services }}' + 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,openstack-cli-server' - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 90423332e9..7ad9b1cc2f 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Keystone on OpenStack ${{ matrix.name }} steps: @@ -54,7 +51,7 @@ jobs: uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} - enabled_services: "${{ matrix.additional_services }}" + enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index e6534ce026..1730a3e896 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Glance on OpenStack ${{ matrix.name }} steps: @@ -54,7 +51,7 @@ jobs: uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} - enabled_services: "${{ matrix.additional_services }}" + enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 40da884938..cbc2ddbc13 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -19,7 +19,6 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" @@ -29,11 +28,9 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Barbican on OpenStack ${{ matrix.name }} steps: @@ -70,7 +67,7 @@ jobs: enable_plugin barbican https://github.com/openstack/barbican ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} - enabled_services: "barbican-svc,barbican-retry,barbican-keystone-listener,${{ matrix.additional_services }}" + enabled_services: "barbican-svc,barbican-retry,barbican-keystone-listener,openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index e5e3914323..448b3b94fd 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -19,15 +19,12 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Octavia on OpenStack ${{ matrix.name }} steps: @@ -65,7 +62,7 @@ jobs: enable_plugin neutron https://github.com/openstack/neutron ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} - enabled_services: "octavia,o-api,o-cw,o-hk,o-hm,o-da,neutron-qos,${{ matrix.additional_services }}" + enabled_services: "octavia,o-api,o-cw,o-hk,o-hm,o-da,neutron-qos,openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index c13dd3a5b2..3dc6ee7e31 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Zaqar on OpenStack ${{ matrix.name }} steps: @@ -57,7 +54,7 @@ jobs: conf_overrides: | enable_plugin zaqar https://github.com/openstack/zaqar ${{ matrix.openstack_version }} ZAQARCLIENT_BRANCH=${{ matrix.openstack_version }} - enabled_services: "${{ matrix.additional_services }}" + enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index afc0f0de8e..7403f829b9 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Neutron on OpenStack ${{ matrix.name }} steps: @@ -74,7 +71,7 @@ jobs: [[post-config|\$NEUTRON_CONF]] [oslo_policy] policy_dirs = /tmp/neutron-policies - enabled_services: "neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding,${{ matrix.additional_services }}" + enabled_services: "neutron-dns,neutron-qos,neutron-segments,neutron-trunk,neutron-uplink-status-propagation,neutron-network-segment-range,neutron-port-forwarding,openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 82406692a0..7cae7e9200 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Swift on OpenStack ${{ matrix.name }} steps: @@ -60,7 +57,7 @@ jobs: [[post-config|\$SWIFT_CONFIG_PROXY_SERVER]] [filter:versioned_writes] allow_object_versioning = true - enabled_services: 's-account,s-container,s-object,s-proxy,${{ matrix.additional_services }}' + enabled_services: 's-account,s-container,s-object,s-proxy,openstack-cli-server' - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 9e4242e0bc..10b811570e 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Heat on OpenStack ${{ matrix.name }} steps: @@ -56,7 +53,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin heat https://github.com/openstack/heat ${{ matrix.openstack_version }} - enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw,${{ matrix.additional_services }}' + enabled_services: 'h-eng,h-api,h-api-cfn,h-api-cw,openstack-cli-server' - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index e2655d1daf..418a1945f0 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -13,15 +13,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Placement on OpenStack ${{ matrix.name }} steps: @@ -54,7 +51,7 @@ jobs: uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 with: branch: ${{ matrix.openstack_version }} - enabled_services: "${{ matrix.additional_services }}" + enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index c87cf8d75a..05ab3337af 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -19,15 +19,12 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Manila on OpenStack ${{ matrix.name }} steps: @@ -78,7 +75,7 @@ jobs: MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE=false ${{ matrix.devstack_conf_overrides }} - enabled_services: "${{ matrix.additional_services }}" + enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index c9f38bc591..5a2735e1f5 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -14,17 +14,14 @@ jobs: openstack_version: "master" ubuntu_version: "24.04" mistral_plugin_version: "master" - additional_services: "openstack-cli-server" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" mistral_plugin_version: "stable/2025.1" - additional_services: "openstack-cli-server" - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" mistral_plugin_version: "stable/2024.2" - additional_services: "openstack-cli-server" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Mistral on Deploy OpenStack ${{ matrix.name }} steps: @@ -59,7 +56,7 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin mistral https://github.com/openstack/mistral ${{ matrix.mistral_plugin_version }} - enabled_services: "mistral,mistral-api,mistral-engine,mistral-executor,mistral-event-engine,${{ matrix.additional_services }}" + enabled_services: "mistral,mistral-api,mistral-engine,mistral-executor,mistral-event-engine,openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} From 86e7a03fabfe7fd6c6c223eb16de1f1da177c1d2 Mon Sep 17 00:00:00 2001 From: Vladimir Ermakov Date: Fri, 31 Oct 2025 10:47:00 +0100 Subject: [PATCH 2190/2296] compute: add cpu info topology cells entry Fix #3545 Signed-off-by: Vladimir Ermakov --- openstack/compute/v2/hypervisors/results.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/compute/v2/hypervisors/results.go b/openstack/compute/v2/hypervisors/results.go index cb442a9b61..a6a5d86028 100644 --- a/openstack/compute/v2/hypervisors/results.go +++ b/openstack/compute/v2/hypervisors/results.go @@ -11,6 +11,7 @@ import ( // Topology represents a CPU Topology. type Topology struct { + Cells int `json:"cells"` Sockets int `json:"sockets"` Cores int `json:"cores"` Threads int `json:"threads"` From 62a7ff0414487fc1248c81a2327ff6ea5d399fcc Mon Sep 17 00:00:00 2001 From: Samuel Kunkel Date: Thu, 16 Oct 2025 19:08:54 +0200 Subject: [PATCH 2191/2296] Add config_drive to server struct Adds config_drive to the regular server struct used for responses. (currently only part of createOpts) which leads to situation that one can create a server with a config_drive. But one can not verify / see that information in any kind of response. This fixes #3534. --- openstack/compute/v2/servers/results.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/openstack/compute/v2/servers/results.go b/openstack/compute/v2/servers/results.go index 2685c1c969..25595a28fe 100644 --- a/openstack/compute/v2/servers/results.go +++ b/openstack/compute/v2/servers/results.go @@ -7,6 +7,7 @@ import ( "fmt" "net/url" "path" + "strconv" "time" "github.com/gophercloud/gophercloud/v2" @@ -283,6 +284,9 @@ type Server struct { // Locked indicates the lock status of the server // This requires microversion 2.9 or later Locked *bool `json:"locked"` + + // ConfigDrive enables metadata injection through a configuration drive. + ConfigDrive bool `json:"-"` } type AttachedVolume struct { @@ -343,6 +347,7 @@ func (r *Server) UnmarshalJSON(b []byte) error { Image any `json:"image"` LaunchedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:launched_at"` TerminatedAt gophercloud.JSONRFC3339MilliNoZ `json:"OS-SRV-USG:terminated_at"` + ConfigDrive any `json:"config_drive"` } err := json.Unmarshal(b, &s) if err != nil { @@ -364,6 +369,24 @@ func (r *Server) UnmarshalJSON(b []byte) error { r.LaunchedAt = time.Time(s.LaunchedAt) r.TerminatedAt = time.Time(s.TerminatedAt) + switch t := s.ConfigDrive.(type) { + case nil: + r.ConfigDrive = false + case bool: + r.ConfigDrive = t + case string: + if t == "" { + r.ConfigDrive = false + } else { + r.ConfigDrive, err = strconv.ParseBool(t) + if err != nil { + return fmt.Errorf("failed to parse ConfigDrive %q: %v", t, err) + } + } + default: + return fmt.Errorf("unknown type for ConfigDrive: %T (value: %v)", t, t) + } + return err } From c16fa097f6717c90a026166b732f1ddca813629f Mon Sep 17 00:00:00 2001 From: dlawton Date: Fri, 10 Oct 2025 15:15:19 +0100 Subject: [PATCH 2192/2296] Identity: Add description field to roles. Signed-off-by: Daniel Lawton --- .../openstack/identity/v3/roles_test.go | 16 +++++++----- openstack/identity/v3/roles/requests.go | 8 +++++- openstack/identity/v3/roles/results.go | 3 +++ .../v3/roles/testing/fixtures_test.go | 25 +++++++++++++------ .../v3/roles/testing/requests_test.go | 16 ++++++------ 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/internal/acceptance/openstack/identity/v3/roles_test.go b/internal/acceptance/openstack/identity/v3/roles_test.go index 96337dc4c1..753736843a 100644 --- a/internal/acceptance/openstack/identity/v3/roles_test.go +++ b/internal/acceptance/openstack/identity/v3/roles_test.go @@ -58,10 +58,11 @@ func TestRolesCRUD(t *testing.T) { th.AssertNoErr(t, err) createOpts := roles.CreateOpts{ - Name: "testrole", - DomainID: "default", + Name: "role-test", + DomainID: "default", + Description: "test description", Extra: map[string]any{ - "description": "test role description", + "email": "testrole@example.com", }, Options: map[roles.Option]any{ "immutable": false, @@ -96,10 +97,11 @@ func TestRolesCRUD(t *testing.T) { } th.AssertEquals(t, found, true) - + description := "updated role test" updateOpts := roles.UpdateOpts{ + Description: &description, Extra: map[string]any{ - "description": "updated test role description", + "email": "updatedtestrole@example.com", }, Options: map[roles.Option]any{ "immutable": nil, @@ -112,7 +114,9 @@ func TestRolesCRUD(t *testing.T) { tools.PrintResource(t, newRole) tools.PrintResource(t, newRole.Extra) - th.AssertEquals(t, newRole.Extra["description"], "updated test role description") + th.AssertEquals(t, newRole.Description, description) + th.AssertEquals(t, newRole.Extra["email"], "updatedtestrole@example.com") + } func TestRolesFilterList(t *testing.T) { diff --git a/openstack/identity/v3/roles/requests.go b/openstack/identity/v3/roles/requests.go index 0552dc2852..260aa153ce 100644 --- a/openstack/identity/v3/roles/requests.go +++ b/openstack/identity/v3/roles/requests.go @@ -94,6 +94,9 @@ type CreateOpts struct { // DomainID is the ID of the domain the role belongs to. DomainID string `json:"domain_id,omitempty"` + // Description is the description of the new role. + Description string `json:"description,omitempty"` + // Extra is free-form extra key/value pairs to describe the role. Extra map[string]any `json:"-"` @@ -141,9 +144,12 @@ type UpdateOptsBuilder interface { // UpdateOpts provides options for updating a role. type UpdateOpts struct { - // Name is the name of the new role. + // Name is an updated name for the role. Name string `json:"name,omitempty"` + // Description is an updated description for the role. + Description *string `json:"description,omitempty"` + // Extra is free-form extra key/value pairs to describe the role. Extra map[string]any `json:"-"` diff --git a/openstack/identity/v3/roles/results.go b/openstack/identity/v3/roles/results.go index b2e348df06..cf9d1cb99e 100644 --- a/openstack/identity/v3/roles/results.go +++ b/openstack/identity/v3/roles/results.go @@ -21,6 +21,9 @@ type Role struct { // Name is the role name Name string `json:"name"` + // Description is the description of the role. + Description string `json:"description"` + // Extra is a collection of miscellaneous key/values. Extra map[string]any `json:"-"` diff --git a/openstack/identity/v3/roles/testing/fixtures_test.go b/openstack/identity/v3/roles/testing/fixtures_test.go index 3fa6171c1b..7cb374d2d7 100644 --- a/openstack/identity/v3/roles/testing/fixtures_test.go +++ b/openstack/identity/v3/roles/testing/fixtures_test.go @@ -34,8 +34,9 @@ const ListOutput = ` "self": "https://example.com/identity/v3/roles/9fe1d3" }, "name": "support", + "description": "read-only support role", "extra": { - "description": "read-only support role" + "test": "this is for the test" } } ] @@ -52,7 +53,10 @@ const GetOutput = ` "self": "https://example.com/identity/v3/roles/9fe1d3" }, "name": "support", - "description": "read-only support role" + "description": "read-only support role", + "extra": { + "test": "this is for the test" + } } } ` @@ -70,6 +74,9 @@ const GetOutputWithOptions = ` "description": "read-only support role", "options": { "immutable": true + }, + "extra": { + "test": "this is for the test" } } } @@ -81,7 +88,8 @@ const CreateRequest = ` "role": { "domain_id": "1789d1", "name": "support", - "description": "read-only support role" + "description": "read-only support role", + "test": "this is for the test" } } ` @@ -93,6 +101,7 @@ const CreateRequestWithOptions = ` "domain_id": "1789d1", "name": "support", "description": "read-only support role", + "test": "this is for the test", "options": { "immutable": true } @@ -354,9 +363,10 @@ var SecondRole = roles.Role{ Links: map[string]any{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, - Name: "support", + Name: "support", + Description: "read-only support role", Extra: map[string]any{ - "description": "read-only support role", + "test": "this is for the test", }, } @@ -367,9 +377,10 @@ var SecondRoleWithOptions = roles.Role{ Links: map[string]any{ "self": "https://example.com/identity/v3/roles/9fe1d3", }, - Name: "support", + Name: "support", + Description: "read-only support role", Extra: map[string]any{ - "description": "read-only support role", + "test": "this is for the test", }, Options: map[roles.Option]any{ roles.Immutable: true, diff --git a/openstack/identity/v3/roles/testing/requests_test.go b/openstack/identity/v3/roles/testing/requests_test.go index 88da0a4b44..7ef5085904 100644 --- a/openstack/identity/v3/roles/testing/requests_test.go +++ b/openstack/identity/v3/roles/testing/requests_test.go @@ -40,7 +40,7 @@ func TestListRolesAllPages(t *testing.T) { actual, err := roles.ExtractRoles(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedRolesSlice, actual) - th.AssertEquals(t, ExpectedRolesSlice[1].Extra["description"], "read-only support role") + th.AssertEquals(t, ExpectedRolesSlice[1].Extra["test"], "this is for the test") } func TestListUsersFiltersCheck(t *testing.T) { @@ -92,10 +92,11 @@ func TestCreateRole(t *testing.T) { HandleCreateRoleSuccessfully(t, fakeServer) createOpts := roles.CreateOpts{ - Name: "support", - DomainID: "1789d1", + Name: "support", + DomainID: "1789d1", + Description: "read-only support role", Extra: map[string]any{ - "description": "read-only support role", + "test": "this is for the test", }, } @@ -110,13 +111,14 @@ func TestCreateWithOptionsRole(t *testing.T) { HandleCreateWithOptionsRoleSuccessfully(t, fakeServer) createOpts := roles.CreateOpts{ - Name: "support", - DomainID: "1789d1", + Name: "support", + DomainID: "1789d1", + Description: "read-only support role", Options: map[roles.Option]any{ roles.Immutable: true, }, Extra: map[string]any{ - "description": "read-only support role", + "test": "this is for the test", }, } From 119c76f867d1c4d8c1bb6a17640c82cb87bb7aa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:45:36 +0000 Subject: [PATCH 2193/2296] build(deps): bump github/codeql-action from 3 to 4 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [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/v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index b24893a9e5..52619c3d30 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -27,12 +27,12 @@ jobs: cache: true - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 From d17c4aa75ad89c5e07493bcb33d4cb61926267bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:54:39 +0000 Subject: [PATCH 2194/2296] build(deps): bump actions/upload-artifact from 4 to 5 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 802e886901..d90653c08c 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -119,7 +119,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-baremetal-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 7cc97ce2ed..31df68c284 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -72,7 +72,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-basic-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 4e88bcf39a..2e37c98529 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -78,7 +78,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-blockstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index c798e9c5b7..455a514a5b 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -78,7 +78,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-compute-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index c672171b46..ebfbbcf57f 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -105,7 +105,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-containerinfra-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index e4aa1e8caa..8c9a837a15 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -86,7 +86,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-dns-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 4d722a5d52..e9c8a4a34b 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -101,7 +101,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-fwaas_v2-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 7ad9b1cc2f..381e1b79da 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -76,7 +76,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-identity-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 1730a3e896..646d9f5f4e 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -76,7 +76,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-image-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index cbc2ddbc13..f573423523 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -92,7 +92,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-keymanager-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 448b3b94fd..2dad9a65bb 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -87,7 +87,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-loadbalancer-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 3dc6ee7e31..80da8e09cc 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -79,7 +79,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-messaging-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 7403f829b9..be1a694d81 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -96,7 +96,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-networking-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 7cae7e9200..118a0b14a9 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -82,7 +82,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-objectstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 10b811570e..4c7f772d1e 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -78,7 +78,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-orchestration-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 418a1945f0..eebbcc5091 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -76,7 +76,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-placement-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 05ab3337af..17f7108f74 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -100,7 +100,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-sharedfilesystems-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 5a2735e1f5..df597a74f7 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -81,7 +81,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: functional-workflow-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* From 69bd65e2885f61bedb69842427eb450424326343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Fri, 14 Nov 2025 08:35:03 +0100 Subject: [PATCH 2195/2296] Fix EC2 authentication to work with new Keystone auth requirement Keystone recently changed to require authentication for the EC2 tokens endpoint [1]. Previously, the EC2 tokens endpoint could be accessed without authentication, but now it requires a valid token. Fixes #3555 [1] https://review.opendev.org/q/Ic84b84247e05f29874e2c5636a033aaedd4de83c --- internal/acceptance/openstack/client_test.go | 3 ++- openstack/identity/v3/ec2tokens/requests.go | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index ccef68855f..3a732d42ba 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -94,7 +94,8 @@ func TestEC2AuthMethod(t *testing.T) { defer credentials.Delete(context.TODO(), client, credential.ID) tools.PrintResource(t, credential) - newClient, err := clients.NewIdentityV3UnauthenticatedClient() + // Create a new provider client for EC2 authentication using the existing token + newClient, err := clients.NewIdentityV3Client() th.AssertNoErr(t, err) ec2AuthOptions := &ec2tokens.AuthOptions{ diff --git a/openstack/identity/v3/ec2tokens/requests.go b/openstack/identity/v3/ec2tokens/requests.go index 5b1f3d6882..1d4cb54928 100644 --- a/openstack/identity/v3/ec2tokens/requests.go +++ b/openstack/identity/v3/ec2tokens/requests.go @@ -300,8 +300,7 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts tokens.AuthO deleteBodyElements(b, "token") resp, err := c.Post(ctx, ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: map[string]string{"X-Auth-Token": ""}, - OkCodes: []int{200}, + OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return @@ -320,8 +319,7 @@ func ValidateS3Token(ctx context.Context, c *gophercloud.ServiceClient, opts tok deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb") resp, err := c.Post(ctx, s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: map[string]string{"X-Auth-Token": ""}, - OkCodes: []int{200}, + OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return From 47c569100bef6786bb235ebc3b489fde440aa75c Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Thu, 13 Nov 2025 22:05:13 -0300 Subject: [PATCH 2196/2296] identity/services: add omitempty to the `type` field --- openstack/identity/v3/services/requests.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index c1e07908ad..8e8011da01 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -109,7 +109,7 @@ type UpdateOptsBuilder interface { // UpdateOpts provides options for updating a service. type UpdateOpts struct { // Type is the type of the service. - Type string `json:"type"` + Type string `json:"type,omitempty"` // Enabled is whether or not the service is enabled. Enabled *bool `json:"enabled,omitempty"` From 817b1b5f21629771cd95506c23791c00522b3d7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 12:54:34 +0000 Subject: [PATCH 2197/2296] build(deps): bump golang.org/x/crypto from 0.43.0 to 0.44.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.43.0 to 0.44.0. - [Commits](https://github.com/golang/crypto/compare/v0.43.0...v0.44.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.44.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 033b59f3ec..e9f5b3d1ec 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.24.0 require ( - golang.org/x/crypto v0.43.0 + golang.org/x/crypto v0.44.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.37.0 // indirect +require golang.org/x/sys v0.38.0 // indirect diff --git a/go.sum b/go.sum index fc73315e9a..f61f4a7916 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 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 41a23bb1deacd7527df3776103bee0bce4eaaf05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 02:59:49 +0000 Subject: [PATCH 2198/2296] build(deps): bump golang.org/x/crypto from 0.44.0 to 0.45.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.44.0 to 0.45.0. - [Commits](https://github.com/golang/crypto/compare/v0.44.0...v0.45.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.45.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e9f5b3d1ec..860326324b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/gophercloud/gophercloud/v2 go 1.24.0 require ( - golang.org/x/crypto v0.44.0 + golang.org/x/crypto v0.45.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index f61f4a7916..8606f6d030 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= -golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= From 4daa3f4f9c69d102e9d376ac2bd6949408ff2d2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 09:04:13 +0000 Subject: [PATCH 2199/2296] build(deps): bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [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/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yaml | 2 +- .github/workflows/ensure-labels.yaml | 2 +- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- .github/workflows/label-pr.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/unit.yaml | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 52619c3d30..90765a6eca 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@v6 diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index 3f8bd42cb2..907a7b8733 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -10,7 +10,7 @@ jobs: ensure: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: micnncim/action-label-syncer@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index d90653c08c..da24d3c8e6 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -23,7 +23,7 @@ jobs: name: Ironic on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 31df68c284..eacf51d7b6 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -23,7 +23,7 @@ jobs: name: basic tests on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 2e37c98529..e1b52375e2 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -23,7 +23,7 @@ jobs: name: Cinder on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 455a514a5b..e5b71f9a31 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -23,7 +23,7 @@ jobs: name: Nova on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index ebfbbcf57f..cda8e1a93b 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -44,7 +44,7 @@ jobs: name: Magnum on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 8c9a837a15..05a9d0e0a9 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -29,7 +29,7 @@ jobs: name: Designate on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index e9c8a4a34b..031a2f0554 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -27,7 +27,7 @@ jobs: name: FWaaS_v2 on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 381e1b79da..a9eb2ebdf2 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -23,7 +23,7 @@ jobs: name: Keystone on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 646d9f5f4e..68a3a5a952 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -23,7 +23,7 @@ jobs: name: Glance on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index f573423523..65a336b8da 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -35,7 +35,7 @@ jobs: name: Barbican on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 2dad9a65bb..32a89fa38f 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -29,7 +29,7 @@ jobs: name: Octavia on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 80da8e09cc..3d3e91e788 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -23,7 +23,7 @@ jobs: name: Zaqar on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index be1a694d81..0330136664 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -23,7 +23,7 @@ jobs: name: Neutron on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 118a0b14a9..c845ea9425 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -23,7 +23,7 @@ jobs: name: Swift on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 4c7f772d1e..310d626805 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -23,7 +23,7 @@ jobs: name: Heat on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index eebbcc5091..6daa9ad097 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -23,7 +23,7 @@ jobs: name: Placement on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 17f7108f74..e0879e34b8 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -29,7 +29,7 @@ jobs: name: Manila on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index df597a74f7..72fe6572ea 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -26,7 +26,7 @@ jobs: name: Mistral on Deploy OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index 8c7e9c55fa..d180b9207f 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -10,7 +10,7 @@ jobs: semver: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 9c2d4787fa..3440a1e0c3 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: go-version: '1' diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index 4cda470c05..01d4ea6697 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -15,7 +15,7 @@ jobs: fail-fast: false steps: - name: Checkout Gophercloud - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@v6 with: From a2037fab10c9d5defc3918f5cc18c755ff82a009 Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Wed, 26 Nov 2025 14:24:44 -0300 Subject: [PATCH 2200/2296] identity/service: add `name` and `description` fields --- openstack/identity/v3/services/requests.go | 12 ++++++ openstack/identity/v3/services/results.go | 19 ++++++++- .../v3/services/testing/fixtures_test.go | 40 +++++++++---------- .../v3/services/testing/requests_test.go | 19 +++++---- 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/openstack/identity/v3/services/requests.go b/openstack/identity/v3/services/requests.go index 8e8011da01..0a25c18179 100644 --- a/openstack/identity/v3/services/requests.go +++ b/openstack/identity/v3/services/requests.go @@ -15,6 +15,12 @@ type CreateOptsBuilder interface { // CreateOpts provides options used to create a service. type CreateOpts struct { + // Name is the name of the service. + Name string `json:"name,omitempty"` + + // Description is the description of the service. + Description string `json:"description,omitempty"` + // Type is the type of the service. Type string `json:"type"` @@ -108,6 +114,12 @@ type UpdateOptsBuilder interface { // UpdateOpts provides options for updating a service. type UpdateOpts struct { + // Name is an updated name for the service. + Name *string `json:"name,omitempty"` + + // Description is an update description for the service. + Description *string `json:"description,omitempty"` + // Type is the type of the service. Type string `json:"type,omitempty"` diff --git a/openstack/identity/v3/services/results.go b/openstack/identity/v3/services/results.go index 365e29b588..576370fc6d 100644 --- a/openstack/identity/v3/services/results.go +++ b/openstack/identity/v3/services/results.go @@ -50,6 +50,12 @@ type Service struct { // ID is the unique ID of the service. ID string `json:"id"` + // Name is the name of the service. + Name string `json:"name"` + + // Description is the description of the service. + Description string `json:"description"` + // Type is the type of the service. Type string `json:"type"` @@ -75,8 +81,6 @@ func (r *Service) UnmarshalJSON(b []byte) error { } *r = Service(s.tmp) - // Collect other fields and bundle them into Extra - // but only if a field titled "extra" wasn't sent. if s.Extra != nil { r.Extra = s.Extra } else { @@ -85,8 +89,19 @@ func (r *Service) UnmarshalJSON(b []byte) error { if err != nil { return err } + if resultMap, ok := result.(map[string]any); ok { r.Extra = gophercloud.RemainingKeys(Service{}, resultMap) + + // the following code is required for backward compatibility with the + // old behavior, when both description and name were in extra + if description, ok := resultMap["description"]; ok { + r.Extra["description"] = description + } + + if name, ok := resultMap["name"]; ok { + r.Extra["name"] = name + } } } diff --git a/openstack/identity/v3/services/testing/fixtures_test.go b/openstack/identity/v3/services/testing/fixtures_test.go index 9bd0076c58..42811a09d6 100644 --- a/openstack/identity/v3/services/testing/fixtures_test.go +++ b/openstack/identity/v3/services/testing/fixtures_test.go @@ -23,11 +23,11 @@ const ListOutput = ` "links": { "self": "https://example.com/identity/v3/services/1234" }, + "name": "service-one", "type": "identity", "enabled": false, "extra": { - "name": "service-one", - "description": "Service One" + "email": "service-one@example.com" } }, { @@ -35,11 +35,11 @@ const ListOutput = ` "links": { "self": "https://example.com/identity/v3/services/9876" }, + "name": "service-two", + "description": "Service Two", "type": "compute", "enabled": false, "extra": { - "name": "service-two", - "description": "Service Two", "email": "service@example.com" } } @@ -55,11 +55,11 @@ const GetOutput = ` "links": { "self": "https://example.com/identity/v3/services/9876" }, + "name": "service-two", + "description": "Service Two", "type": "compute", "enabled": false, "extra": { - "name": "service-two", - "description": "Service Two", "email": "service@example.com" } } @@ -96,11 +96,11 @@ const UpdateOutput = ` "links": { "self": "https://example.com/identity/v3/services/9876" }, + "name": "service-two", + "description": "Service Two Updated", "type": "compute2", "enabled": false, "extra": { - "name": "service-two", - "description": "Service Two Updated", "email": "service@example.com" } } @@ -113,11 +113,11 @@ var FirstService = services.Service{ Links: map[string]any{ "self": "https://example.com/identity/v3/services/1234", }, + Name: "service-one", Type: "identity", Enabled: false, Extra: map[string]any{ - "name": "service-one", - "description": "Service One", + "email": "service-one@example.com", }, } @@ -127,12 +127,12 @@ var SecondService = services.Service{ Links: map[string]any{ "self": "https://example.com/identity/v3/services/9876", }, - Type: "compute", - Enabled: false, + Name: "service-two", + Description: "Service Two", + Type: "compute", + Enabled: false, Extra: map[string]any{ - "name": "service-two", - "description": "Service Two", - "email": "service@example.com", + "email": "service@example.com", }, } @@ -142,12 +142,12 @@ var SecondServiceUpdated = services.Service{ Links: map[string]any{ "self": "https://example.com/identity/v3/services/9876", }, - Type: "compute2", - Enabled: false, + Name: "service-two", + Description: "Service Two Updated", + Type: "compute2", + Enabled: false, Extra: map[string]any{ - "name": "service-two", - "description": "Service Two Updated", - "email": "service@example.com", + "email": "service@example.com", }, } diff --git a/openstack/identity/v3/services/testing/requests_test.go b/openstack/identity/v3/services/testing/requests_test.go index 12c241ea9e..c88cefb322 100644 --- a/openstack/identity/v3/services/testing/requests_test.go +++ b/openstack/identity/v3/services/testing/requests_test.go @@ -17,11 +17,11 @@ func TestCreateSuccessful(t *testing.T) { HandleCreateServiceSuccessfully(t, fakeServer) createOpts := services.CreateOpts{ - Type: "compute", + Name: "service-two", + Description: "Service Two", + Type: "compute", Extra: map[string]any{ - "name": "service-two", - "description": "Service Two", - "email": "service@example.com", + "email": "service@example.com", }, } @@ -60,7 +60,7 @@ func TestListServicesAllPages(t *testing.T) { actual, err := services.ExtractServices(allPages) th.AssertNoErr(t, err) th.CheckDeepEquals(t, ExpectedServicesSlice, actual) - th.AssertEquals(t, ExpectedServicesSlice[0].Extra["name"], "service-one") + th.AssertEquals(t, ExpectedServicesSlice[0].Extra["email"], "service-one@example.com") th.AssertEquals(t, ExpectedServicesSlice[1].Extra["email"], "service@example.com") } @@ -81,16 +81,15 @@ func TestUpdateSuccessful(t *testing.T) { defer fakeServer.Teardown() HandleUpdateServiceSuccessfully(t, fakeServer) + updatedDescription := "Service Two Updated" updateOpts := services.UpdateOpts{ - Type: "compute2", - Extra: map[string]any{ - "description": "Service Two Updated", - }, + Description: &updatedDescription, + Type: "compute2", } actual, err := services.Update(context.TODO(), client.ServiceClient(fakeServer), "9876", updateOpts).Extract() th.AssertNoErr(t, err) th.CheckDeepEquals(t, SecondServiceUpdated, *actual) - th.AssertEquals(t, SecondServiceUpdated.Extra["description"], "Service Two Updated") + th.AssertEquals(t, SecondServiceUpdated.Description, "Service Two Updated") } func TestDeleteSuccessful(t *testing.T) { From f22d562c59aab9ff7585598eeea9bf7211751e38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 09:02:48 +0000 Subject: [PATCH 2201/2296] build(deps): bump golang.org/x/crypto from 0.45.0 to 0.46.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.45.0 to 0.46.0. - [Commits](https://github.com/golang/crypto/compare/v0.45.0...v0.46.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.46.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 860326324b..91c175c962 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.24.0 require ( - golang.org/x/crypto v0.45.0 + golang.org/x/crypto v0.46.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.38.0 // indirect +require golang.org/x/sys v0.39.0 // indirect diff --git a/go.sum b/go.sum index 8606f6d030..03a132e602 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= 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 603dd53722762343f6e628fb0752db29282e762a Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Wed, 10 Dec 2025 14:31:23 -0300 Subject: [PATCH 2202/2296] add: description field to identity.v3.Endpoint Signed-off-by: Winicius Silva --- openstack/identity/v3/endpoints/requests.go | 6 ++++ openstack/identity/v3/endpoints/results.go | 3 ++ .../v3/endpoints/testing/requests_test.go | 32 +++++++++++++------ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 4386bb9bc9..4865d8861f 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -30,6 +30,9 @@ type CreateOpts struct { // ServiceID is the ID of the service the Endpoint refers to. ServiceID string `json:"service_id" required:"true"` + + // Description is the description of the Endpoint. + Description string `json:"description,omitempty"` } // ToEndpointCreateMap builds a request body from the Endpoint Create options. @@ -121,6 +124,9 @@ type UpdateOpts struct { // ServiceID is the ID of the service the Endpoint refers to. ServiceID string `json:"service_id,omitempty"` + + // Description is an updated description of the endpoint. + Description string `json:"description,omitempty"` } // ToEndpointUpdateMap builds an update request body from the Update options. diff --git a/openstack/identity/v3/endpoints/results.go b/openstack/identity/v3/endpoints/results.go index 19d279ebc6..7dbb1c9f7d 100644 --- a/openstack/identity/v3/endpoints/results.go +++ b/openstack/identity/v3/endpoints/results.go @@ -66,6 +66,9 @@ type Endpoint struct { // Enabled is whether or not the endpoint is enabled. Enabled bool `json:"enabled"` + + // Description is the description of the Endpoint. + Description string `json:"description"` } // EndpointPage is a single page of Endpoint results. diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 7c418aed88..78991fe547 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -26,7 +26,8 @@ func TestCreateSuccessful(t *testing.T) { "name": "the-endiest-of-points", "region": "underground", "url": "https://1.2.3.4:9000/", - "service_id": "asdfasdfasdfasdf" + "service_id": "asdfasdfasdfasdf", + "description": "Test description" } }`) @@ -42,7 +43,8 @@ func TestCreateSuccessful(t *testing.T) { "name": "the-endiest-of-points", "region": "underground", "service_id": "asdfasdfasdfasdf", - "url": "https://1.2.3.4:9000/" + "url": "https://1.2.3.4:9000/", + "description": "Test description" } }`) }) @@ -53,6 +55,7 @@ func TestCreateSuccessful(t *testing.T) { Region: "underground", URL: "https://1.2.3.4:9000/", ServiceID: "asdfasdfasdfasdf", + Description: "Test description", }).Extract() th.AssertNoErr(t, err) @@ -64,6 +67,7 @@ func TestCreateSuccessful(t *testing.T) { Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", + Description: "Test description", } th.AssertDeepEquals(t, expected, actual) @@ -90,7 +94,8 @@ func TestListEndpoints(t *testing.T) { "name": "the-endiest-of-points", "region": "underground", "service_id": "asdfasdfasdfasdf", - "url": "https://1.2.3.4:9000/" + "url": "https://1.2.3.4:9000/", + "description": "List endpoint1 test" }, { "id": "13", @@ -102,7 +107,8 @@ func TestListEndpoints(t *testing.T) { "name": "shhhh", "region": "underground", "service_id": "asdfasdfasdfasdf", - "url": "https://1.2.3.4:9001/" + "url": "https://1.2.3.4:9001/", + "description": "List endpoint2 test" } ], "links": { @@ -130,6 +136,7 @@ func TestListEndpoints(t *testing.T) { Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", + Description: "List endpoint1 test", }, { ID: "13", @@ -139,6 +146,7 @@ func TestListEndpoints(t *testing.T) { Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9001/", + Description: "List endpoint2 test", }, } th.AssertDeepEquals(t, expected, actual) @@ -167,7 +175,8 @@ func TestGetEndpoint(t *testing.T) { "name": "the-endiest-of-points", "region": "underground", "service_id": "asdfasdfasdfasdf", - "url": "https://1.2.3.4:9000/" + "url": "https://1.2.3.4:9000/", + "description": "Get endpoint test" } }`) }) @@ -185,6 +194,7 @@ func TestGetEndpoint(t *testing.T) { Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", + Description: "Get endpoint test", } th.AssertDeepEquals(t, expected, actual) } @@ -199,7 +209,8 @@ func TestUpdateEndpoint(t *testing.T) { th.TestJSONRequest(t, r, `{ "endpoint": { "name": "renamed", - "region": "somewhere-else" + "region": "somewhere-else", + "description": "Changed description" } }`) @@ -214,14 +225,16 @@ func TestUpdateEndpoint(t *testing.T) { "name": "renamed", "region": "somewhere-else", "service_id": "asdfasdfasdfasdf", - "url": "https://1.2.3.4:9000/" + "url": "https://1.2.3.4:9000/", + "description": "Changed description" } }`) }) actual, err := endpoints.Update(context.TODO(), client.ServiceClient(fakeServer), "12", endpoints.UpdateOpts{ - Name: "renamed", - Region: "somewhere-else", + Name: "renamed", + Region: "somewhere-else", + Description: "Changed description", }).Extract() if err != nil { t.Fatalf("Unexpected error from Update: %v", err) @@ -235,6 +248,7 @@ func TestUpdateEndpoint(t *testing.T) { Region: "somewhere-else", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", + Description: "Changed description", } th.AssertDeepEquals(t, expected, actual) } From c707541d489ca737b9fe97e8c16263f64fa9eb41 Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Thu, 11 Dec 2025 13:09:53 -0300 Subject: [PATCH 2203/2296] add: crud acceptance test for endpoint Signed-off-by: Winicius Silva --- .../openstack/identity/v3/endpoint_test.go | 36 +++++++++++++++ .../openstack/identity/v3/identity.go | 45 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/internal/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go index 8dd9e5bad1..6f96adb981 100644 --- a/internal/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -100,3 +100,39 @@ func TestEndpointsNavigateCatalog(t *testing.T) { tools.PrintResource(t, allEndpoints[0]) } + +func TestEndpointCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewIdentityV3Client() + th.AssertNoErr(t, err) + + service, err := CreateService(t, client, &services.CreateOpts{ + Type: "endpoint-test", + Name: tools.RandomString("ACPTTEST", 8), + Extra: map[string]any{}, + }) + th.AssertNoErr(t, err) + defer DeleteService(t, client, service.ID) + + endpoint, err := CreateEndpoint(t, client, &endpoints.CreateOpts{ + Availability: gophercloud.Availability("internal"), + ServiceID: service.ID, + URL: "https://example.com", + }) + th.AssertNoErr(t, err) + defer DeleteEndpoint(t, client, endpoint.ID) + + tools.PrintResource(t, endpoint) + tools.PrintResource(t, endpoint.URL) + + newEndpoint, err := endpoints.Update(context.TODO(), client, endpoint.ID, &endpoints.UpdateOpts{ + Name: "new-endpoint", + URL: "https://example-updated.com", + Description: "Updated Endpoint", + }).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, newEndpoint.URL, "https://example-updated.com") + th.AssertEquals(t, newEndpoint.Description, "Updated Endpoint") +} diff --git a/internal/acceptance/openstack/identity/v3/identity.go b/internal/acceptance/openstack/identity/v3/identity.go index ab34bb7530..a71c914430 100644 --- a/internal/acceptance/openstack/identity/v3/identity.go +++ b/internal/acceptance/openstack/identity/v3/identity.go @@ -7,6 +7,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/domains" + "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/endpoints" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/groups" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/projects" "github.com/gophercloud/gophercloud/v2/openstack/identity/v3/regions" @@ -17,6 +18,38 @@ import ( th "github.com/gophercloud/gophercloud/v2/testhelper" ) +// CreateEndpoint will create an endpoint with a random name. It only define +// endpoint name and description, and receive from CreateOpts others parameters, +// such as URL, Availability and ServiceID. An error will be returned if the +// endpoint was unabled to be created. +func CreateEndpoint(t *testing.T, client *gophercloud.ServiceClient, c *endpoints.CreateOpts) (*endpoints.Endpoint, error) { + name := tools.RandomString("ACPTTEST", 8) + description := tools.RandomString("ACPTTEST-DESC", 8) + t.Logf("Attempting to create endpoint: %s", name) + + var createOpts endpoints.CreateOpts + if c != nil { + createOpts = *c + } else { + createOpts = endpoints.CreateOpts{} + } + + createOpts.Name = name + createOpts.Description = description + + endpoint, err := endpoints.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + return endpoint, err + } + + t.Logf("Successfully created endpoint %s with ID %s", name, endpoint.ID) + + th.AssertEquals(t, endpoint.Name, name) + th.AssertEquals(t, endpoint.Description, description) + + return endpoint, nil +} + // CreateProject will create a project with a random name. // It takes an optional createOpts parameter since creating a project // has so many options. An error will be returned if the project was @@ -228,6 +261,18 @@ func CreateService(t *testing.T, client *gophercloud.ServiceClient, c *services. return service, nil } +// DeleteEndpoint will delete the specified Endpoint using its ID. A fatal error +// will occur if the endpoint failed to be deleted. This works best when using +// it as a deferred function. +func DeleteEndpoint(t *testing.T, client *gophercloud.ServiceClient, endpointID string) { + err := endpoints.Delete(context.TODO(), client, endpointID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete endpoint %s: %v", endpointID, err) + } + + t.Logf("Deleted endpoint: %s", endpointID) +} + // DeleteProject will delete a project by ID. A fatal error will occur if // the project ID failed to be deleted. This works best when using it as // a deferred function. From 9b8dcd512507ecc09662672ab0fe4b20aa103f0c Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Thu, 11 Dec 2025 23:32:15 -0300 Subject: [PATCH 2204/2296] add: support for update mac_address on port --- internal/acceptance/openstack/networking/v2/ports_test.go | 3 +++ openstack/networking/v2/ports/requests.go | 1 + openstack/networking/v2/ports/testing/fixtures.go | 5 +++-- openstack/networking/v2/ports/testing/requests_test.go | 4 ++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index 569beeebd4..f6ff48e829 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -45,9 +45,11 @@ func TestPortsCRUD(t *testing.T) { // Update port newPortName := "" newPortDescription := "" + newMACAddress := "aa:bb:cc:dd:ee:ff" updateOpts := ports.UpdateOpts{ Name: &newPortName, Description: &newPortDescription, + MACAddress: &newMACAddress, } newPort, err := ports.Update(context.TODO(), client, port.ID, updateOpts).Extract() th.AssertNoErr(t, err) @@ -56,6 +58,7 @@ func TestPortsCRUD(t *testing.T) { th.AssertEquals(t, newPort.Name, newPortName) th.AssertEquals(t, newPort.Description, newPortDescription) + th.AssertEquals(t, newPort.MACAddress, newMACAddress) allPages, err := ports.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go index bfff2dffb2..a9c5c37535 100644 --- a/openstack/networking/v2/ports/requests.go +++ b/openstack/networking/v2/ports/requests.go @@ -197,6 +197,7 @@ type UpdateOpts struct { AllowedAddressPairs *[]AddressPair `json:"allowed_address_pairs,omitempty"` PropagateUplinkStatus *bool `json:"propagate_uplink_status,omitempty"` ValueSpecs *map[string]string `json:"value_specs,omitempty"` + MACAddress *string `json:"mac_address,omitempty"` // RevisionNumber implements extension:standard-attr-revisions. If != "" it // will set revision_number=%s. If the revision number does not match, the diff --git a/openstack/networking/v2/ports/testing/fixtures.go b/openstack/networking/v2/ports/testing/fixtures.go index b4c03fd618..b11650863f 100644 --- a/openstack/networking/v2/ports/testing/fixtures.go +++ b/openstack/networking/v2/ports/testing/fixtures.go @@ -409,7 +409,8 @@ const UpdateRequest = ` ], "security_groups": [ "f0ac4394-7e4a-4409-9701-ba8be283dbc3" - ] + ], + "mac_address": "fa:16:3e:c9:cb:f4" } } ` @@ -423,7 +424,7 @@ const UpdateResponse = ` "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7", "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa", "device_owner": "", - "mac_address": "fa:16:3e:c9:cb:f0", + "mac_address": "fa:16:3e:c9:cb:f4", "fixed_ips": [ { "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2", diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go index ac1a2cd79a..f69962f74b 100644 --- a/openstack/networking/v2/ports/testing/requests_test.go +++ b/openstack/networking/v2/ports/testing/requests_test.go @@ -538,6 +538,7 @@ func TestUpdate(t *testing.T) { }) name := "new_port_name" + newMACAddress := "fa:16:3e:c9:cb:f4" options := ports.UpdateOpts{ Name: &name, FixedIPs: []ports.IP{ @@ -547,6 +548,7 @@ func TestUpdate(t *testing.T) { AllowedAddressPairs: &[]ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, + MACAddress: &newMACAddress, } s, err := ports.Update(context.TODO(), fake.ServiceClient(fakeServer), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() @@ -727,6 +729,7 @@ func TestUpdateRevision(t *testing.T) { }) name := "new_port_name" + newMACAddress := "fa:16:3e:c9:cb:f4" options := ports.UpdateOpts{ Name: &name, FixedIPs: []ports.IP{ @@ -736,6 +739,7 @@ func TestUpdateRevision(t *testing.T) { AllowedAddressPairs: &[]ports.AddressPair{ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"}, }, + MACAddress: &newMACAddress, } _, err := ports.Update(context.TODO(), fake.ServiceClient(fakeServer), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract() th.AssertNoErr(t, err) From c85676dca90f8cff1cb9c69bf7a3d2fff5daf09f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:04:01 +0000 Subject: [PATCH 2205/2296] build(deps): bump kiegroup/git-backporting from 4.8.6 to 4.8.7 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.8.6 to 4.8.7. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/7d895d030f5cf02f4a76c7f0bc79b41d8747b17c...baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-version: 4.8.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 86504701ea..04f06538ac 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -34,7 +34,7 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') || contains(github.event.label.name, 'semver:patch') - uses: kiegroup/git-backporting@7d895d030f5cf02f4a76c7f0bc79b41d8747b17c + uses: kiegroup/git-backporting@baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -97,7 +97,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@7d895d030f5cf02f4a76c7f0bc79b41d8747b17c + uses: kiegroup/git-backporting@baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From 0119785a87742869e499b4477d7c352ecb9ca288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?p=C3=BDrus?= Date: Thu, 11 Dec 2025 11:34:22 +0100 Subject: [PATCH 2206/2296] keystone: add support for per page limit --- openstack/identity/v3/domains/requests.go | 3 ++ openstack/identity/v3/projects/requests.go | 3 ++ .../v3/projects/testing/fixtures_test.go | 37 ++++++++++++++++++- .../v3/projects/testing/requests_test.go | 13 +++++-- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/openstack/identity/v3/domains/requests.go b/openstack/identity/v3/domains/requests.go index 8ce72a9b33..94bb567f3c 100644 --- a/openstack/identity/v3/domains/requests.go +++ b/openstack/identity/v3/domains/requests.go @@ -20,6 +20,9 @@ type ListOpts struct { // Name filters the response by domain name. Name string `q:"name"` + + // Limit limits the number of projects returned per page. + Limit int `q:"limit"` } // ToDomainListQuery formats a ListOpts into a query string. diff --git a/openstack/identity/v3/projects/requests.go b/openstack/identity/v3/projects/requests.go index 6fb8448a9f..5aecc94d93 100644 --- a/openstack/identity/v3/projects/requests.go +++ b/openstack/identity/v3/projects/requests.go @@ -45,6 +45,9 @@ type ListOpts struct { // NotTagsAny filters on specific project tags. At least one of the tags must be absent for the project. NotTagsAny string `q:"not-tags-any"` + // Limit limits the number of projects returned per page. + Limit int `q:"limit"` + // Filters filters the response by custom filters such as // 'name__contains=foo' Filters map[string]string `q:"-"` diff --git a/openstack/identity/v3/projects/testing/fixtures_test.go b/openstack/identity/v3/projects/testing/fixtures_test.go index 5c97fc7dd3..5782fde87c 100644 --- a/openstack/identity/v3/projects/testing/fixtures_test.go +++ b/openstack/identity/v3/projects/testing/fixtures_test.go @@ -59,7 +59,19 @@ const ListOutput = ` "parent_id": null, "tags": ["Red", "Team"], "test": "old" - }, + } + ], + "links": { + "next": "%sprojects?limit=1&marker=1234", + "self": "%sprojects?limit=1", + "previous": null + } +} +` + +const ListOutputSecondPage = ` +{ + "projects": [ { "is_domain": false, "description": "The team that is blue", @@ -73,8 +85,21 @@ const ListOutput = ` } } ], + "links": { + "next": "%sprojects?limit=1&marker=9876", + "self": "%sprojects?limit=1&marker=1234", + "previous": null + } +} +` + +const ListOutputThirdPage = ` +{ + "projects": [ + ], "links": { "next": null, + "self": "%sprojects?limit=1&marker=9876", "previous": null } } @@ -299,7 +324,15 @@ func HandleListProjectsSuccessfully(t *testing.T, fakeServer th.FakeServer) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - fmt.Fprint(w, ListOutput) + + switch r.URL.Query().Get("marker") { + case "": + fmt.Fprintf(w, ListOutput, fakeServer.Endpoint(), fakeServer.Endpoint()) + case "1234": + fmt.Fprintf(w, ListOutputSecondPage, fakeServer.Endpoint(), fakeServer.Endpoint()) + case "9876": + fmt.Fprintf(w, ListOutputThirdPage, fakeServer.Endpoint()) + } }) } diff --git a/openstack/identity/v3/projects/testing/requests_test.go b/openstack/identity/v3/projects/testing/requests_test.go index 5ecc88e45d..ca5bebe454 100644 --- a/openstack/identity/v3/projects/testing/requests_test.go +++ b/openstack/identity/v3/projects/testing/requests_test.go @@ -35,19 +35,26 @@ func TestListProjects(t *testing.T) { defer fakeServer.Teardown() HandleListProjectsSuccessfully(t, fakeServer) + actualProjects := make([]projects.Project, 0, 2) count := 0 - err := projects.List(client.ServiceClient(fakeServer), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + opts := projects.ListOpts{ + Limit: 1, + } + err := projects.List(client.ServiceClient(fakeServer), opts).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { count++ actual, err := projects.ExtractProjects(page) th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedProjectSlice, actual) + actualProjects = append(actualProjects, actual...) return true, nil }) th.AssertNoErr(t, err) - th.CheckEquals(t, count, 1) + th.CheckEquals(t, count, 2) + + th.CheckEquals(t, len(actualProjects), 2) + th.CheckDeepEquals(t, ExpectedProjectSlice, actualProjects) } func TestListGroupsFiltersCheck(t *testing.T) { From a4ba89918de3ab04dd6ea972a15c44e46601a636 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:04:09 +0000 Subject: [PATCH 2207/2296] build(deps): bump actions/upload-artifact from 5 to 6 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index d90653c08c..e8c2d61fbb 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -119,7 +119,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-baremetal-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 31df68c284..bf41ffc542 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -72,7 +72,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-basic-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 2e37c98529..d03ccfa243 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -78,7 +78,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-blockstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 455a514a5b..c642399dff 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -78,7 +78,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-compute-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index ebfbbcf57f..528e8cb924 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -105,7 +105,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-containerinfra-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 8c9a837a15..9992345007 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -86,7 +86,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-dns-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index e9c8a4a34b..57c02ef9e1 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -101,7 +101,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-fwaas_v2-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 381e1b79da..eeda5e352f 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -76,7 +76,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-identity-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 646d9f5f4e..859314ddd3 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -76,7 +76,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-image-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index f573423523..c9a86b210c 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -92,7 +92,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-keymanager-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 2dad9a65bb..4534d92fe4 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -87,7 +87,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-loadbalancer-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 80da8e09cc..69a5c3e1be 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -79,7 +79,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-messaging-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index be1a694d81..3167d54132 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -96,7 +96,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-networking-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 118a0b14a9..a2b82391d3 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -82,7 +82,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-objectstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 4c7f772d1e..f2b2ce17c6 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -78,7 +78,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-orchestration-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index eebbcc5091..bd82762859 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -76,7 +76,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-placement-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 17f7108f74..a1a492a156 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -100,7 +100,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-sharedfilesystems-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index df597a74f7..6c3f3a9822 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -81,7 +81,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: functional-workflow-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* From dd4071356bfc919b163f3a558701b1f630d2c367 Mon Sep 17 00:00:00 2001 From: eshulman2 Date: Thu, 11 Dec 2025 13:32:23 +0200 Subject: [PATCH 2208/2296] identity/v3/tokens: Add access rules support for application credentials This change adds support for the OpenStack-Identity-Access-Rules header when getting or validating tokens created with application credentials that have access rules configured. Changes: - Add GetOptsBuilder interface and GetOpts struct for Get() function - Add ValidateOptsBuilder interface and ValidateOpts struct for Validate() - Both structs contain AccessRulesVersion (float64) field to specify the header version. Pass nil to skip access rules validation, or provide opts with a version >= 1 to enable it. - Add float64 support to BuildHeaders() in params.go - Add AccessRule and ApplicationCredential types to represent the application credential information in token responses - Add ExtractApplicationCredential() method to extract application credential details including access rules from token responses - Update all usages across tests and client code The decision to use an opts struct pattern instead of passing the parameter directly was made to align with the rest of the gophercloud codebase conventions, although it may feel like overkill for a single optional parameter. This fixes the issue where tokens created with application credentials that have access rules would return 404 Not Found when fetched without the OpenStack-Identity-Access-Rules header. See: https://docs.openstack.org/keystone/latest/user/application_credentials.html Closes: #3573 assisted by: Claude --- internal/acceptance/openstack/client_test.go | 4 +- .../v3/applicationcredentials_test.go | 8 +- .../openstack/identity/v3/credentials_test.go | 8 +- .../openstack/identity/v3/oauth1_test.go | 2 +- .../openstack/identity/v3/token_test.go | 8 +- .../openstack/identity/v3/trusts_test.go | 2 +- openstack/client.go | 2 +- openstack/identity/v3/tokens/doc.go | 48 ++++++++++++ openstack/identity/v3/tokens/requests.go | 78 ++++++++++++++++--- openstack/identity/v3/tokens/results.go | 38 +++++++++ .../identity/v3/tokens/testing/fixtures.go | 70 +++++++++++++++++ .../v3/tokens/testing/requests_test.go | 78 ++++++++++++++++++- .../v3/tokens/testing/results_test.go | 20 +++++ 13 files changed, 334 insertions(+), 32 deletions(-) diff --git a/internal/acceptance/openstack/client_test.go b/internal/acceptance/openstack/client_test.go index 3a732d42ba..acb0399b11 100644 --- a/internal/acceptance/openstack/client_test.go +++ b/internal/acceptance/openstack/client_test.go @@ -71,11 +71,11 @@ func TestEC2AuthMethod(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) diff --git a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go index 7a4ad01e1c..f8bb2ae129 100644 --- a/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go +++ b/internal/acceptance/openstack/identity/v3/applicationcredentials_test.go @@ -50,15 +50,15 @@ func TestApplicationCredentialsCRD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - roles, err := tokens.Get(context.TODO(), client, token.ID).ExtractRoles() + roles, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractRoles() th.AssertNoErr(t, err) tools.PrintResource(t, roles) - project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) @@ -194,7 +194,7 @@ func TestApplicationCredentialsAccessRules(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) diff --git a/internal/acceptance/openstack/identity/v3/credentials_test.go b/internal/acceptance/openstack/identity/v3/credentials_test.go index ce0362abee..9590f41bff 100644 --- a/internal/acceptance/openstack/identity/v3/credentials_test.go +++ b/internal/acceptance/openstack/identity/v3/credentials_test.go @@ -39,11 +39,11 @@ func TestCredentialsCRUD(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) @@ -116,11 +116,11 @@ func TestCredentialsValidateS3(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, token) - user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) diff --git a/internal/acceptance/openstack/identity/v3/oauth1_test.go b/internal/acceptance/openstack/identity/v3/oauth1_test.go index 2e5b9b25fe..7a2ef800ab 100644 --- a/internal/acceptance/openstack/identity/v3/oauth1_test.go +++ b/internal/acceptance/openstack/identity/v3/oauth1_test.go @@ -215,7 +215,7 @@ func oauth1MethodTest(t *testing.T, client *gophercloud.ServiceClient, consumer tokens.Token oauth1.TokenExt } - tokenRes := tokens.Get(context.TODO(), newClient, newClient.TokenID) + tokenRes := tokens.Get(context.TODO(), newClient, newClient.TokenID, nil) err = tokenRes.ExtractInto(&token) th.AssertNoErr(t, err) oauth1Roles, err := tokenRes.ExtractRoles() diff --git a/internal/acceptance/openstack/identity/v3/token_test.go b/internal/acceptance/openstack/identity/v3/token_test.go index 1bc436caa7..290bce9474 100644 --- a/internal/acceptance/openstack/identity/v3/token_test.go +++ b/internal/acceptance/openstack/identity/v3/token_test.go @@ -40,19 +40,19 @@ func TestTokensGet(t *testing.T) { th.AssertNoErr(t, err) tools.PrintResource(t, token) - catalog, err := tokens.Get(context.TODO(), client, token.ID).ExtractServiceCatalog() + catalog, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractServiceCatalog() th.AssertNoErr(t, err) tools.PrintResource(t, catalog) - user, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() + user, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractUser() th.AssertNoErr(t, err) tools.PrintResource(t, user) - roles, err := tokens.Get(context.TODO(), client, token.ID).ExtractRoles() + roles, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractRoles() th.AssertNoErr(t, err) tools.PrintResource(t, roles) - project, err := tokens.Get(context.TODO(), client, token.ID).ExtractProject() + project, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractProject() th.AssertNoErr(t, err) tools.PrintResource(t, project) } diff --git a/internal/acceptance/openstack/identity/v3/trusts_test.go b/internal/acceptance/openstack/identity/v3/trusts_test.go index 2eb659d55b..225e09d4b0 100644 --- a/internal/acceptance/openstack/identity/v3/trusts_test.go +++ b/internal/acceptance/openstack/identity/v3/trusts_test.go @@ -43,7 +43,7 @@ func TestTrustCRUD(t *testing.T) { token, err := tokens.Create(context.TODO(), client, &authOptions).Extract() th.AssertNoErr(t, err) - adminUser, err := tokens.Get(context.TODO(), client, token.ID).ExtractUser() + adminUser, err := tokens.Get(context.TODO(), client, token.ID, nil).ExtractUser() th.AssertNoErr(t, err) // Get the admin and member role IDs. diff --git a/openstack/client.go b/openstack/client.go index 2c81b27f3f..234f23eba2 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -206,7 +206,7 @@ func v3auth(ctx context.Context, client *gophercloud.ProviderClient, endpoint st } v3Client.SetToken(tokenID) - result := tokens3.Get(ctx, v3Client, tokenID) + result := tokens3.Get(ctx, v3Client, tokenID, nil) if result.Err != nil { return result.Err } diff --git a/openstack/identity/v3/tokens/doc.go b/openstack/identity/v3/tokens/doc.go index c711d0c28e..597d781dce 100644 --- a/openstack/identity/v3/tokens/doc.go +++ b/openstack/identity/v3/tokens/doc.go @@ -103,5 +103,53 @@ Example to Create a Token from a Username and Password with Project Name Scope if err != nil { panic(err) } + +Example to Get a Token + + token, err := tokens.Get(context.TODO(), identityClient, "token_id", nil).ExtractToken() + if err != nil { + panic(err) + } + +# Example to Get a Token Created with Application Credentials Access Rules + +When validating or retrieving tokens that were created using application +credentials with access rules, the OpenStack-Identity-Access-Rules header +must be sent. Without this header, Keystone will return a 404 Not Found error. + +See https://docs.openstack.org/keystone/latest/user/application_credentials.html + + getOpts := tokens.GetOpts{ + AccessRulesVersion: "1.0", + } + token, err := tokens.Get(context.TODO(), identityClient, "token_id", getOpts).ExtractToken() + if err != nil { + panic(err) + } + +Example to Validate a Token + + ok, err := tokens.Validate(context.TODO(), identityClient, "token_id", nil) + if err != nil { + panic(err) + } + + if ok { + fmt.Println("Token is valid") + } + +Example to Validate a Token Created with Application Credentials Access Rules + + validateOpts := tokens.ValidateOpts{ + AccessRulesVersion: "1.0", + } + ok, err := tokens.Validate(context.TODO(), identityClient, "token_id", validateOpts) + if err != nil { + panic(err) + } + + if ok { + fmt.Println("Token is valid") + } */ package tokens diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go index fa8b925d04..40b41e44db 100644 --- a/openstack/identity/v3/tokens/requests.go +++ b/openstack/identity/v3/tokens/requests.go @@ -2,10 +2,13 @@ package tokens import ( "context" + "maps" "github.com/gophercloud/gophercloud/v2" ) +const xSubjectTokenHeader = "X-Subject-Token" + // Scope allows a created token to be limited to a specific domain or project. type Scope struct { ProjectID string @@ -118,12 +121,6 @@ func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]any) (map[string]string, return nil, nil } -func subjectTokenHeaders(subjectToken string) map[string]string { - return map[string]string{ - "X-Subject-Token": subjectToken, - } -} - // Create authenticates and either generates a new token, or changes the Scope // of an existing token. func Create(ctx context.Context, c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { @@ -146,20 +143,79 @@ func Create(ctx context.Context, c *gophercloud.ServiceClient, opts AuthOptionsB return } +// GetOptsBuilder allows extensions to add additional parameters to +// the Get request. +type GetOptsBuilder interface { + ToTokenGetParams() (map[string]string, error) +} + +// GetOpts provides options for the Get request. +type GetOpts struct { + // AccessRulesVersion specifies the OpenStack-Identity-Access-Rules header + // version. This is required when getting tokens that were created using + // application credentials with access rules. Versions less than 1 will cause + // Keystone to ignore access rules validation. + AccessRulesVersion string `h:"OpenStack-Identity-Access-Rules"` +} + +// ToTokenGetParams formats GetOpts into request headers. +func (opts GetOpts) ToTokenGetParams() (map[string]string, error) { + return gophercloud.BuildHeaders(opts) +} + // Get validates and retrieves information about another token. -func Get(ctx context.Context, c *gophercloud.ServiceClient, token string) (r GetResult) { +func Get(ctx context.Context, c *gophercloud.ServiceClient, token string, opts GetOptsBuilder) (r GetResult) { + h := map[string]string{xSubjectTokenHeader: token} + if opts != nil { + b, err := opts.ToTokenGetParams() + if err != nil { + r.Err = err + return + } + maps.Copy(h, b) + } + resp, err := c.Get(ctx, tokenURL(c), &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(token), + MoreHeaders: h, OkCodes: []int{200, 203}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } +// ValidateOptsBuilder allows extensions to add additional parameters to +// the Validate request. +type ValidateOptsBuilder interface { + ToTokenValidateParams() (map[string]string, error) +} + +// ValidateOpts provides options for the Validate request. +type ValidateOpts struct { + // AccessRulesVersion specifies the OpenStack-Identity-Access-Rules header + // version. This is required when validating tokens that were created using + // application credentials with access rules. Versions less than 1 will cause + // Keystone to ignore access rules validation. + AccessRulesVersion string `h:"OpenStack-Identity-Access-Rules"` +} + +// ToTokenValidateParams formats ValidateOpts into request headers. +func (opts ValidateOpts) ToTokenValidateParams() (map[string]string, error) { + return gophercloud.BuildHeaders(opts) +} + // Validate determines if a specified token is valid or not. -func Validate(ctx context.Context, c *gophercloud.ServiceClient, token string) (bool, error) { +func Validate(ctx context.Context, c *gophercloud.ServiceClient, token string, opts ValidateOptsBuilder) (bool, error) { + h := map[string]string{xSubjectTokenHeader: token} + if opts != nil { + b, err := opts.ToTokenValidateParams() + if err != nil { + return false, err + } + maps.Copy(h, b) + } + resp, err := c.Head(ctx, tokenURL(c), &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(token), + MoreHeaders: h, OkCodes: []int{200, 204, 404}, }) if err != nil { @@ -172,7 +228,7 @@ func Validate(ctx context.Context, c *gophercloud.ServiceClient, token string) ( // Revoke immediately makes specified token invalid. func Revoke(ctx context.Context, c *gophercloud.ServiceClient, token string) (r RevokeResult) { resp, err := c.Delete(ctx, tokenURL(c), &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(token), + MoreHeaders: map[string]string{xSubjectTokenHeader: token}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return diff --git a/openstack/identity/v3/tokens/results.go b/openstack/identity/v3/tokens/results.go index bd61a9a67d..1ec1ce4001 100644 --- a/openstack/identity/v3/tokens/results.go +++ b/openstack/identity/v3/tokens/results.go @@ -88,6 +88,33 @@ type Trust struct { TrustorUserID TrustUser `json:"trustor_user"` } +// AccessRule represents an access rule for an application credential. +type AccessRule struct { + // The ID of the access rule. + ID string `json:"id"` + // The API path that the application credential is permitted to access. + Path string `json:"path"` + // The request method that the application credential is permitted to use + // for a given API endpoint. + Method string `json:"method"` + // The service type identifier for the service that the application + // credential is permitted to access. + Service string `json:"service"` +} + +// ApplicationCredential represents the application credential information +// included in a token when the token was created using an application credential. +type ApplicationCredential struct { + // The ID of the application credential. + ID string `json:"id"` + // The name of the application credential. + Name string `json:"name"` + // A flag indicating whether the application credential is restricted. + Restricted bool `json:"restricted"` + // A list of access rules for the application credential. + AccessRules []AccessRule `json:"access_rules"` +} + // commonResult is the response from a request. A commonResult has various // methods which can be used to extract different details about the result. type commonResult struct { @@ -181,6 +208,17 @@ func (r commonResult) ExtractTrust() (*Trust, error) { return s.Trust, err } +// ExtractApplicationCredential returns the ApplicationCredential that was used +// to create the token. This is only present when the token was created using +// an application credential. +func (r commonResult) ExtractApplicationCredential() (*ApplicationCredential, error) { + var s struct { + ApplicationCredential *ApplicationCredential `json:"application_credential"` + } + err := r.ExtractInto(&s) + return s.ApplicationCredential, err +} + // CreateResult is the response from a Create request. Use ExtractToken() // to interpret it as a Token, or ExtractServiceCatalog() to interpret it // as a service catalog. diff --git a/openstack/identity/v3/tokens/testing/fixtures.go b/openstack/identity/v3/tokens/testing/fixtures.go index 00797867c9..f0580521cd 100644 --- a/openstack/identity/v3/tokens/testing/fixtures.go +++ b/openstack/identity/v3/tokens/testing/fixtures.go @@ -323,3 +323,73 @@ func getGetDomainResult(t *testing.T) tokens.GetResult { th.AssertNoErr(t, err) return result } + +// ApplicationCredentialTokenOutput is a sample response to a Token call +// using application credentials with access rules. +const ApplicationCredentialTokenOutput = ` +{ + "token":{ + "methods":["application_credential"], + "expires_at":"2017-06-03T02:19:49.000000Z", + "user":{ + "domain":{ + "id":"default", + "name":"Default" + }, + "id":"0fe36e73809d46aeae6705c39077b1b3", + "name":"admin" + }, + "application_credential":{ + "id":"d832f05beee743c696672ef65ee073ff", + "name":"test", + "restricted":true, + "access_rules":[ + { + "id":"4641f614301d48bfae1ad323e3e1c44c", + "service":"identity", + "path":"/v3/auth/tokens", + "method":"GET" + }, + { + "id":"968c404cb9b44672a574e5ee9d3db987", + "service":"identity", + "path":"/v3/**", + "method":"HEAD" + } + ] + }, + "issued_at":"2017-06-03T01:19:49.000000Z" + } +}` + +// ExpectedApplicationCredential contains expected application credential +// extracted from token response. +var ExpectedApplicationCredential = tokens.ApplicationCredential{ + ID: "d832f05beee743c696672ef65ee073ff", + Name: "test", + Restricted: true, + AccessRules: []tokens.AccessRule{ + { + ID: "4641f614301d48bfae1ad323e3e1c44c", + Service: "identity", + Path: "/v3/auth/tokens", + Method: "GET", + }, + { + ID: "968c404cb9b44672a574e5ee9d3db987", + Service: "identity", + Path: "/v3/**", + Method: "HEAD", + }, + }, +} + +func getGetApplicationCredentialResult(t *testing.T) tokens.GetResult { + result := tokens.GetResult{} + result.Header = http.Header{ + "X-Subject-Token": []string{testTokenID}, + } + err := json.Unmarshal([]byte(ApplicationCredentialTokenOutput), &result.Body) + th.AssertNoErr(t, err) + return result +} diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index 8157de3ca7..c8c5d49805 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -675,7 +675,7 @@ func TestGetRequest(t *testing.T) { `) }) - token, err := tokens.Get(context.TODO(), &client, "abcdef12345").Extract() + token, err := tokens.Get(context.TODO(), &client, "abcdef12345", nil).Extract() if err != nil { t.Errorf("Info returned an error: %v", err) } @@ -712,7 +712,7 @@ func TestValidateRequestSuccessful(t *testing.T) { defer fakeServer.Teardown() client := prepareAuthTokenHandler(t, fakeServer, "HEAD", http.StatusNoContent) - ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345") + ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345", nil) if err != nil { t.Errorf("Unexpected error from Validate: %v", err) } @@ -727,7 +727,7 @@ func TestValidateRequestFailure(t *testing.T) { defer fakeServer.Teardown() client := prepareAuthTokenHandler(t, fakeServer, "HEAD", http.StatusNotFound) - ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345") + ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345", nil) if err != nil { t.Errorf("Unexpected error from Validate: %v", err) } @@ -742,7 +742,7 @@ func TestValidateRequestError(t *testing.T) { defer fakeServer.Teardown() client := prepareAuthTokenHandler(t, fakeServer, "HEAD", http.StatusMethodNotAllowed) - _, err := tokens.Validate(context.TODO(), &client, "abcdef12345") + _, err := tokens.Validate(context.TODO(), &client, "abcdef12345", nil) if err == nil { t.Errorf("Missing expected error from Validate") } @@ -768,6 +768,76 @@ func TestRevokeRequestError(t *testing.T) { } } +func TestGetRequestWithAccessRules(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + client := gophercloud.ServiceClient{ + ProviderClient: &gophercloud.ProviderClient{ + TokenID: "12345abcdef", + }, + Endpoint: fakeServer.Endpoint(), + } + + fakeServer.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", "12345abcdef") + th.TestHeader(t, r, "X-Subject-Token", "abcdef12345") + th.TestHeader(t, r, "OpenStack-Identity-Access-Rules", "1") + + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, ` + { "token": { "expires_at": "2014-08-29T13:10:01.000000Z" } } + `) + }) + + getOpts := tokens.GetOpts{ + AccessRulesVersion: "1", + } + token, err := tokens.Get(context.TODO(), &client, "abcdef12345", getOpts).Extract() + if err != nil { + t.Errorf("Get returned an error: %v", err) + } + + expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014") + if token.ExpiresAt != expected { + t.Errorf("Expected expiration time %s, but was %s", expected.Format(time.UnixDate), time.Time(token.ExpiresAt).Format(time.UnixDate)) + } +} + +func TestValidateRequestWithAccessRules(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + client := gophercloud.ServiceClient{ + ProviderClient: &gophercloud.ProviderClient{ + TokenID: "12345abcdef", + }, + Endpoint: fakeServer.Endpoint(), + } + + fakeServer.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "HEAD") + th.TestHeader(t, r, "X-Auth-Token", "12345abcdef") + th.TestHeader(t, r, "X-Subject-Token", "abcdef12345") + th.TestHeader(t, r, "OpenStack-Identity-Access-Rules", "1") + + w.WriteHeader(http.StatusNoContent) + }) + + validateOpts := tokens.ValidateOpts{ + AccessRulesVersion: "1", + } + ok, err := tokens.Validate(context.TODO(), &client, "abcdef12345", validateOpts) + if err != nil { + t.Errorf("Unexpected error from Validate: %v", err) + } + + if !ok { + t.Errorf("Validate returned false for a valid token") + } +} + func TestNoTokenInResponse(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() diff --git a/openstack/identity/v3/tokens/testing/results_test.go b/openstack/identity/v3/tokens/testing/results_test.go index c45581ffde..f5e4029c23 100644 --- a/openstack/identity/v3/tokens/testing/results_test.go +++ b/openstack/identity/v3/tokens/testing/results_test.go @@ -59,3 +59,23 @@ func TestExtractDomain(t *testing.T) { th.CheckDeepEquals(t, &ExpectedDomain, domain) } + +func TestExtractApplicationCredential(t *testing.T) { + result := getGetApplicationCredentialResult(t) + + appCred, err := result.ExtractApplicationCredential() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, &ExpectedApplicationCredential, appCred) +} + +func TestExtractApplicationCredentialNotPresent(t *testing.T) { + result := getGetResult(t) + + appCred, err := result.ExtractApplicationCredential() + th.AssertNoErr(t, err) + + if appCred != nil { + t.Errorf("Expected nil application credential, got %v", appCred) + } +} From eb55a4f54774f0b7e28b6352b7d914d0b3b136a8 Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Mon, 15 Dec 2025 18:55:15 -0300 Subject: [PATCH 2209/2296] identity.v3.Endpoint: add enabled field on creation and on update --- .../openstack/identity/v3/endpoint_test.go | 4 ++++ openstack/identity/v3/endpoints/requests.go | 14 +++++++++++--- .../v3/endpoints/testing/requests_test.go | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/internal/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go index 6f96adb981..7d9da99854 100644 --- a/internal/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -115,10 +115,12 @@ func TestEndpointCRUD(t *testing.T) { th.AssertNoErr(t, err) defer DeleteService(t, client, service.ID) + enabled := false endpoint, err := CreateEndpoint(t, client, &endpoints.CreateOpts{ Availability: gophercloud.Availability("internal"), ServiceID: service.ID, URL: "https://example.com", + Enabled: &enabled, }) th.AssertNoErr(t, err) defer DeleteEndpoint(t, client, endpoint.ID) @@ -126,10 +128,12 @@ func TestEndpointCRUD(t *testing.T) { tools.PrintResource(t, endpoint) tools.PrintResource(t, endpoint.URL) + enabled = true newEndpoint, err := endpoints.Update(context.TODO(), client, endpoint.ID, &endpoints.UpdateOpts{ Name: "new-endpoint", URL: "https://example-updated.com", Description: "Updated Endpoint", + Enabled: &enabled, }).Extract() th.AssertNoErr(t, err) diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 4865d8861f..45893aa3c6 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -18,6 +18,13 @@ type CreateOpts struct { // or public), referenced by the gophercloud.Availability type. Availability gophercloud.Availability `json:"interface" required:"true"` + // Description is the description of the Endpoint. + Description string `json:"description,omitempty"` + + // Enabled indicates whether the Endpoint appears in the service + // or not. + Enabled *bool `json:"enabled,omitempty"` + // Name is the name of the Endpoint. Name string `json:"name" required:"true"` @@ -30,9 +37,6 @@ type CreateOpts struct { // ServiceID is the ID of the service the Endpoint refers to. ServiceID string `json:"service_id" required:"true"` - - // Description is the description of the Endpoint. - Description string `json:"description,omitempty"` } // ToEndpointCreateMap builds a request body from the Endpoint Create options. @@ -115,6 +119,10 @@ type UpdateOpts struct { // Name is the name of the Endpoint. Name string `json:"name,omitempty"` + // Enabled indicates whether the Endpoint appears in the service + // or not. + Enabled *bool `json:"enabled,omitempty"` + // Region is the region the Endpoint is located in. // This field can be omitted or left as a blank string. Region string `json:"region,omitempty"` diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 78991fe547..07798947c6 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -27,7 +27,8 @@ func TestCreateSuccessful(t *testing.T) { "region": "underground", "url": "https://1.2.3.4:9000/", "service_id": "asdfasdfasdfasdf", - "description": "Test description" + "description": "Test description", + "enabled": false } }`) @@ -36,7 +37,7 @@ func TestCreateSuccessful(t *testing.T) { "endpoint": { "id": "12", "interface": "public", - "enabled": true, + "enabled": false, "links": { "self": "https://localhost:5000/v3/endpoints/12" }, @@ -49,6 +50,7 @@ func TestCreateSuccessful(t *testing.T) { }`) }) + enabled := false actual, err := endpoints.Create(context.TODO(), client.ServiceClient(fakeServer), endpoints.CreateOpts{ Availability: gophercloud.AvailabilityPublic, Name: "the-endiest-of-points", @@ -56,14 +58,15 @@ func TestCreateSuccessful(t *testing.T) { URL: "https://1.2.3.4:9000/", ServiceID: "asdfasdfasdfasdf", Description: "Test description", + Enabled: &enabled, }).Extract() th.AssertNoErr(t, err) expected := &endpoints.Endpoint{ ID: "12", Availability: gophercloud.AvailabilityPublic, - Enabled: true, Name: "the-endiest-of-points", + Enabled: false, Region: "underground", ServiceID: "asdfasdfasdfasdf", URL: "https://1.2.3.4:9000/", @@ -210,7 +213,8 @@ func TestUpdateEndpoint(t *testing.T) { "endpoint": { "name": "renamed", "region": "somewhere-else", - "description": "Changed description" + "description": "Changed description", + "enabled": false } }`) @@ -218,7 +222,7 @@ func TestUpdateEndpoint(t *testing.T) { "endpoint": { "id": "12", "interface": "public", - "enabled": true, + "enabled": false, "links": { "self": "https://localhost:5000/v3/endpoints/12" }, @@ -231,10 +235,12 @@ func TestUpdateEndpoint(t *testing.T) { }`) }) + enabled := false actual, err := endpoints.Update(context.TODO(), client.ServiceClient(fakeServer), "12", endpoints.UpdateOpts{ Name: "renamed", Region: "somewhere-else", Description: "Changed description", + Enabled: &enabled, }).Extract() if err != nil { t.Fatalf("Unexpected error from Update: %v", err) @@ -243,7 +249,7 @@ func TestUpdateEndpoint(t *testing.T) { expected := &endpoints.Endpoint{ ID: "12", Availability: gophercloud.AvailabilityPublic, - Enabled: true, + Enabled: false, Name: "renamed", Region: "somewhere-else", ServiceID: "asdfasdfasdfasdf", From 9c2ed1136a45fcbf960aad2aade81a1843a1c085 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Mon, 17 Nov 2025 08:51:08 +0300 Subject: [PATCH 2210/2296] Add new fields for ListOpts volumetypes Added fields: name, description, extra_specs Added tests --- .../blockstorage/v3/volumetypes/requests.go | 8 ++ .../v3/volumetypes/testing/requests_test.go | 92 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index 8849c394fb..ca8c8be754 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -73,9 +73,17 @@ type ListOptsBuilder interface { // ListOpts holds options for listing Volume Types. It is passed to the volumetypes.List // function. type ListOpts struct { + // Name will filter by the specified volume type name. + Name string `q:"name"` + // Description will filter by the specified volume type description. + Description string `q:"description"` // Specifies whether the query should include public or private Volume Types. // By default, it queries both types. IsPublic visibility `q:"is_public"` + // ExtraSpecs will filter results based on specified extra specs. + // The map key is the extra spec name, and the value is the filter value. + // For example: map[string]string{"multiattach": " True", "storage_protocol": "nfs"} + ExtraSpecs map[string]string `q:"extra_specs"` // Comma-separated list of sort keys and optional sort directions in the // form of [:]. Sort string `q:"sort"` diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 2ed84d144e..4e21506a62 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -162,6 +162,98 @@ func TestListIsPublicParam(t *testing.T) { th.AssertNoErr(t, err) } +func TestListNameParam(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + result := make(map[string]string) + HandleListIsPublicParam(t, fakeServer, result) + + // Test with name filter + result["is_public"] = "None" + result["name"] = "test-type" + err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + Name: "test-type", + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + return true, nil + }) + th.AssertNoErr(t, err) + + // Test with name, description, is_public, and extra_specs together + result["is_public"] = "true" + result["name"] = "test-type" + result["description"] = "test" + result["extra_specs"] = "{'storage_protocol':'nfs'}" + err = volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + Name: "test-type", + Description: "test", + IsPublic: volumetypes.VisibilityPublic, + ExtraSpecs: map[string]string{"storage_protocol": "nfs"}, + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + return true, nil + }) + th.AssertNoErr(t, err) +} + +func TestListDescriptionParam(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + result := make(map[string]string) + HandleListIsPublicParam(t, fakeServer, result) + + // Test with description filter + result["is_public"] = "None" + result["description"] = "test" + err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + Description: "test", + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + return true, nil + }) + th.AssertNoErr(t, err) + + // Test with description, is_public, and extra_specs together + result["is_public"] = "true" + result["description"] = "test" + result["extra_specs"] = "{'multiattach':' True'}" + err = volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + Description: "test", + IsPublic: volumetypes.VisibilityPublic, + ExtraSpecs: map[string]string{"multiattach": " True"}, + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + return true, nil + }) + th.AssertNoErr(t, err) +} + +func TestListExtraSpecsParam(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + result := make(map[string]string) + HandleListIsPublicParam(t, fakeServer, result) + + // Test with extra_specs filter + result["is_public"] = "None" + result["extra_specs"] = "{'storage_protocol':'nfs'}" + err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + ExtraSpecs: map[string]string{"storage_protocol": "nfs"}, + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + return true, nil + }) + th.AssertNoErr(t, err) + + // Test with multiple extra_specs + result["extra_specs"] = "{'multiattach':' True', 'replication_enabled':' True', 'RESKEY:availability_zones':'zone'}" + err = volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + ExtraSpecs: map[string]string{ + "multiattach": " True", + "replication_enabled": " True", + "RESKEY:availability_zones": "zone", + }, + }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + return true, nil + }) + th.AssertNoErr(t, err) +} + func TestVolumeTypeExtraSpecsList(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() From bcdc1d97376776acc5378d4d0bcfa5ace0367444 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Thu, 18 Dec 2025 00:31:52 +0300 Subject: [PATCH 2211/2296] Add tests --- .../v3/volumetypes/testing/fixtures_test.go | 120 ++++++++++++++++++ .../v3/volumetypes/testing/requests_test.go | 98 +++++++------- 2 files changed, 169 insertions(+), 49 deletions(-) diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go index a03209f199..af09e18086 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go @@ -207,6 +207,126 @@ func HandleListIsPublicParam(t *testing.T, fakeServer th.FakeServer, values map[ }) } +func HandleListWithNameFilter(t *testing.T, fakeServer th.FakeServer, values map[string]string) { + fakeServer.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestFormValues(t, r, values) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, ` +{ + "volume_types": [ + { + "name": "test-type", + "qos_specs_id": null, + "os-volume-type-access:is_public": true, + "extra_specs": { + "storage_protocol": "nfs" + }, + "is_public": true, + "id": "996af3df-92fd-4814-a0ee-ba5f899aa1ec", + "description": "test" + } + ] +} +`) + }) +} + +func HandleListWithDescriptionFilter(t *testing.T, fakeServer th.FakeServer, values map[string]string) { + fakeServer.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestFormValues(t, r, values) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, ` +{ + "volume_types": [ + { + "name": "test-type", + "qos_specs_id": null, + "os-volume-type-access:is_public": true, + "extra_specs": { + "multiattach": " True" + }, + "is_public": true, + "id": "ab948f0a-13ed-47c8-b9be-cade0beb0706", + "description": "test" + } + ] +} +`) + }) +} + +func HandleListWithExtraSpecsFilter(t *testing.T, fakeServer th.FakeServer, values map[string]string) { + fakeServer.Mux.HandleFunc("/types", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestFormValues(t, r, values) + + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + extraSpecsFilter := r.Form.Get("extra_specs") + + // Determine which volume type to return based on extra_specs filter + switch extraSpecsFilter { + case "{'storage_protocol':'nfs'}": + // Return only nfs-type + fmt.Fprint(w, ` +{ + "volume_types": [ + { + "name": "nfs-type", + "qos_specs_id": null, + "os-volume-type-access:is_public": true, + "extra_specs": { + "storage_protocol": "nfs" + }, + "is_public": true, + "id": "6b0cfee7-48b6-41b7-9d68-0d74cbdc08de", + "description": "NFS storage type" + } + ] +} +`) + case "{'multiattach':' True', 'replication_enabled':' True', 'RESKEY:availability_zones':'zone'}": + // Return only multiattach-type + fmt.Fprint(w, ` +{ + "volume_types": [ + { + "name": "multiattach-type", + "qos_specs_id": null, + "os-volume-type-access:is_public": true, + "extra_specs": { + "multiattach": " True", + "replication_enabled": " True", + "RESKEY:availability_zones": "zone" + }, + "is_public": true, + "id": "e1fc0553-0357-4206-af30-23137ee5f743", + "description": "Multiattach volume type" + } + ] +} +`) + default: + // Default: return empty list + fmt.Fprint(w, `{"volume_types": []}`) + } + }) +} + func HandleExtraSpecsListSuccessfully(t *testing.T, fakeServer th.FakeServer) { fakeServer.Mux.HandleFunc("/types/1/extra_specs", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 4e21506a62..83b9ecd7fb 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -166,92 +166,92 @@ func TestListNameParam(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() result := make(map[string]string) - HandleListIsPublicParam(t, fakeServer, result) + HandleListWithNameFilter(t, fakeServer, result) - // Test with name filter result["is_public"] = "None" result["name"] = "test-type" - err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + allPages, err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ Name: "test-type", - }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - return true, nil - }) + }).AllPages(context.TODO()) th.AssertNoErr(t, err) - - // Test with name, description, is_public, and extra_specs together - result["is_public"] = "true" - result["name"] = "test-type" - result["description"] = "test" - result["extra_specs"] = "{'storage_protocol':'nfs'}" - err = volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ - Name: "test-type", - Description: "test", - IsPublic: volumetypes.VisibilityPublic, - ExtraSpecs: map[string]string{"storage_protocol": "nfs"}, - }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - return true, nil - }) + actual, err := volumetypes.ExtractVolumeTypes(allPages) th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + th.AssertEquals(t, "test-type", actual[0].Name) + th.AssertEquals(t, "996af3df-92fd-4814-a0ee-ba5f899aa1ec", actual[0].ID) + th.AssertEquals(t, "test", actual[0].Description) + th.AssertEquals(t, true, actual[0].IsPublic) + th.AssertEquals(t, true, actual[0].PublicAccess) + th.AssertEquals(t, "nfs", actual[0].ExtraSpecs["storage_protocol"]) } func TestListDescriptionParam(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() result := make(map[string]string) - HandleListIsPublicParam(t, fakeServer, result) + HandleListWithDescriptionFilter(t, fakeServer, result) - // Test with description filter result["is_public"] = "None" result["description"] = "test" - err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + allPages, err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ Description: "test", - }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - return true, nil - }) + }).AllPages(context.TODO()) th.AssertNoErr(t, err) - - // Test with description, is_public, and extra_specs together - result["is_public"] = "true" - result["description"] = "test" - result["extra_specs"] = "{'multiattach':' True'}" - err = volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ - Description: "test", - IsPublic: volumetypes.VisibilityPublic, - ExtraSpecs: map[string]string{"multiattach": " True"}, - }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - return true, nil - }) + actual, err := volumetypes.ExtractVolumeTypes(allPages) th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + th.AssertEquals(t, "test", actual[0].Description) + th.AssertEquals(t, "test-type", actual[0].Name) + th.AssertEquals(t, "ab948f0a-13ed-47c8-b9be-cade0beb0706", actual[0].ID) + th.AssertEquals(t, true, actual[0].IsPublic) + th.AssertEquals(t, true, actual[0].PublicAccess) + th.AssertEquals(t, " True", actual[0].ExtraSpecs["multiattach"]) } func TestListExtraSpecsParam(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() result := make(map[string]string) - HandleListIsPublicParam(t, fakeServer, result) - - // Test with extra_specs filter result["is_public"] = "None" result["extra_specs"] = "{'storage_protocol':'nfs'}" - err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + HandleListWithExtraSpecsFilter(t, fakeServer, result) + + // Test with extra_specs filter + allPages, err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ ExtraSpecs: map[string]string{"storage_protocol": "nfs"}, - }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - return true, nil - }) + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := volumetypes.ExtractVolumeTypes(allPages) th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + th.AssertEquals(t, "nfs-type", actual[0].Name) + th.AssertEquals(t, "6b0cfee7-48b6-41b7-9d68-0d74cbdc08de", actual[0].ID) + th.AssertEquals(t, "NFS storage type", actual[0].Description) + th.AssertEquals(t, true, actual[0].IsPublic) + th.AssertEquals(t, true, actual[0].PublicAccess) + th.AssertEquals(t, "nfs", actual[0].ExtraSpecs["storage_protocol"]) // Test with multiple extra_specs result["extra_specs"] = "{'multiattach':' True', 'replication_enabled':' True', 'RESKEY:availability_zones':'zone'}" - err = volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ + allPages, err = volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ ExtraSpecs: map[string]string{ "multiattach": " True", "replication_enabled": " True", "RESKEY:availability_zones": "zone", }, - }).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { - return true, nil - }) + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err = volumetypes.ExtractVolumeTypes(allPages) th.AssertNoErr(t, err) + th.AssertEquals(t, 1, len(actual)) + th.AssertEquals(t, "multiattach-type", actual[0].Name) + th.AssertEquals(t, "e1fc0553-0357-4206-af30-23137ee5f743", actual[0].ID) + th.AssertEquals(t, "Multiattach volume type", actual[0].Description) + th.AssertEquals(t, true, actual[0].IsPublic) + th.AssertEquals(t, true, actual[0].PublicAccess) + th.AssertEquals(t, " True", actual[0].ExtraSpecs["multiattach"]) + th.AssertEquals(t, " True", actual[0].ExtraSpecs["replication_enabled"]) + th.AssertEquals(t, "zone", actual[0].ExtraSpecs["RESKEY:availability_zones"]) } func TestVolumeTypeExtraSpecsList(t *testing.T) { From 0460120be71c49dcfa1c5910217c5906805cbd50 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Thu, 18 Dec 2025 02:23:03 +0300 Subject: [PATCH 2212/2296] Add Volume Typres acceptance test, fix unit test --- .../openstack/blockstorage/v3/blockstorage.go | 2 +- .../blockstorage/v3/volumetypes_test.go | 100 +++++++++++++++++- .../v3/volumetypes/testing/fixtures_test.go | 33 ++---- .../v3/volumetypes/testing/requests_test.go | 22 ---- 4 files changed, 106 insertions(+), 51 deletions(-) diff --git a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go index 16e0a23dd1..782089963d 100644 --- a/internal/acceptance/openstack/blockstorage/v3/blockstorage.go +++ b/internal/acceptance/openstack/blockstorage/v3/blockstorage.go @@ -158,7 +158,7 @@ func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumet createOpts := volumetypes.CreateOpts{ Name: name, - ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name"}, + ExtraSpecs: map[string]string{"volume_backend_name": "fake_backend_name", "RESKEY:availability_zones": "zone", "multiattach": " True"}, Description: description, } diff --git a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go index f19fd033de..4b2a1bdfaa 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -4,6 +4,7 @@ package v3 import ( "context" + "fmt" "testing" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" @@ -13,7 +14,7 @@ import ( th "github.com/gophercloud/gophercloud/v2/testhelper" ) -func TestVolumeTypes(t *testing.T) { +func TestVolumeTypesCRUD(t *testing.T) { clients.RequireAdmin(t) client, err := clients.NewBlockStorageV3Client() @@ -39,9 +40,9 @@ func TestVolumeTypes(t *testing.T) { th.AssertEquals(t, found, true) + name := vt.Name + "-updated" + description := vt.Description + "-updated" isPublic := false - name := vt.Name + "-UPDATED" - description := "" updateOpts := volumetypes.UpdateOpts{ Name: &name, Description: &description, @@ -55,6 +56,99 @@ func TestVolumeTypes(t *testing.T) { th.AssertEquals(t, name, newVT.Name) th.AssertEquals(t, description, newVT.Description) th.AssertEquals(t, isPublic, newVT.IsPublic) + + for _, tt := range []struct { + name string + opts volumetypes.ListOpts + expectedVolumeTypes int + }{ + { + name: "Volume Type Name", + opts: volumetypes.ListOpts{ + Name: newVT.Name, + }, + expectedVolumeTypes: 1, + }, + { + name: "Description", + opts: volumetypes.ListOpts{ + Description: "create_from_gophercloud-updated", + }, + expectedVolumeTypes: 1, + }, + { + name: "Partitial Extra Specs", + opts: volumetypes.ListOpts{ + ExtraSpecs: map[string]string{ + "volume_backend_name": "fake_backend_name", + "RESKEY:availability_zones": "zone", + }, + }, + expectedVolumeTypes: 1, + }, + { + name: "Full Extra Specs", + opts: volumetypes.ListOpts{ + ExtraSpecs: map[string]string{ + "volume_backend_name": "fake_backend_name", + "RESKEY:availability_zones": "zone", + "multiattach": " True", + }, + }, + expectedVolumeTypes: 1, + }, + { + name: "Extra Specs Not Found", + opts: volumetypes.ListOpts{ + ExtraSpecs: map[string]string{ + "volume_backend_name": "fake_backend_name", + "RESKEY:availability_zones": "zone", + "multiattach": " True", + "another_one_spec": "another_one_value", + }, + }, + expectedVolumeTypes: 0, + }, + } { + t.Run(fmt.Sprintf("List volumetypes by %s", tt.name), func(t *testing.T) { + allPages, err := volumetypes.List(client, tt.opts).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allVolumeTypes, err := volumetypes.ExtractVolumeTypes(allPages) + th.AssertNoErr(t, err) + + logVolumeTypes := func() { + for _, volumetype := range allVolumeTypes { + tools.PrintResource(t, volumetype) + } + } + + if tt.name != "Extra Specs Not Found" { + if len(allVolumeTypes) != tt.expectedVolumeTypes { + if len(allVolumeTypes) == 0 { + t.Fatalf("Volume type not found") + } + } + } else { + if len(allVolumeTypes) != 0 { + logVolumeTypes() + t.Fatalf("Expected %d volume type but got %d", tt.expectedVolumeTypes, len(allVolumeTypes)) + } + } + func() { + for _, volumetype := range allVolumeTypes { + if tt.name != "Extra Specs Not Found" { + if volumetype.ID == newVT.ID { + return + } else { + logVolumeTypes() + t.Fatalf("Returned volume types did not contain expected volume type") + } + } + } + }() + }) + } } func TestVolumeTypesExtraSpecs(t *testing.T) { diff --git a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go index af09e18086..d305f22fbb 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/fixtures_test.go @@ -278,9 +278,13 @@ func HandleListWithExtraSpecsFilter(t *testing.T, fakeServer th.FakeServer, valu extraSpecsFilter := r.Form.Get("extra_specs") - // Determine which volume type to return based on extra_specs filter - switch extraSpecsFilter { - case "{'storage_protocol':'nfs'}": + // Determine which volume type to return based on extra_specs filter. + // Note: We only test with a single extra_spec value because testing multiple + // values is not feasible due to the non-deterministic order of map keys in Go. + // The order of keys in the serialized string can vary between runs, making + // it impossible to reliably match the expected string without adding complex + // normalization code that would clutter the test. + if extraSpecsFilter == "{'storage_protocol':'nfs'}" { // Return only nfs-type fmt.Fprint(w, ` { @@ -299,28 +303,7 @@ func HandleListWithExtraSpecsFilter(t *testing.T, fakeServer th.FakeServer, valu ] } `) - case "{'multiattach':' True', 'replication_enabled':' True', 'RESKEY:availability_zones':'zone'}": - // Return only multiattach-type - fmt.Fprint(w, ` -{ - "volume_types": [ - { - "name": "multiattach-type", - "qos_specs_id": null, - "os-volume-type-access:is_public": true, - "extra_specs": { - "multiattach": " True", - "replication_enabled": " True", - "RESKEY:availability_zones": "zone" - }, - "is_public": true, - "id": "e1fc0553-0357-4206-af30-23137ee5f743", - "description": "Multiattach volume type" - } - ] -} -`) - default: + } else { // Default: return empty list fmt.Fprint(w, `{"volume_types": []}`) } diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index 83b9ecd7fb..da830502ee 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -230,28 +230,6 @@ func TestListExtraSpecsParam(t *testing.T) { th.AssertEquals(t, true, actual[0].IsPublic) th.AssertEquals(t, true, actual[0].PublicAccess) th.AssertEquals(t, "nfs", actual[0].ExtraSpecs["storage_protocol"]) - - // Test with multiple extra_specs - result["extra_specs"] = "{'multiattach':' True', 'replication_enabled':' True', 'RESKEY:availability_zones':'zone'}" - allPages, err = volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ - ExtraSpecs: map[string]string{ - "multiattach": " True", - "replication_enabled": " True", - "RESKEY:availability_zones": "zone", - }, - }).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err = volumetypes.ExtractVolumeTypes(allPages) - th.AssertNoErr(t, err) - th.AssertEquals(t, 1, len(actual)) - th.AssertEquals(t, "multiattach-type", actual[0].Name) - th.AssertEquals(t, "e1fc0553-0357-4206-af30-23137ee5f743", actual[0].ID) - th.AssertEquals(t, "Multiattach volume type", actual[0].Description) - th.AssertEquals(t, true, actual[0].IsPublic) - th.AssertEquals(t, true, actual[0].PublicAccess) - th.AssertEquals(t, " True", actual[0].ExtraSpecs["multiattach"]) - th.AssertEquals(t, " True", actual[0].ExtraSpecs["replication_enabled"]) - th.AssertEquals(t, "zone", actual[0].ExtraSpecs["RESKEY:availability_zones"]) } func TestVolumeTypeExtraSpecsList(t *testing.T) { From c830e599366eb4b274cd3b5783b9683293171b1c Mon Sep 17 00:00:00 2001 From: Chi Wai Chan Date: Thu, 18 Dec 2025 14:43:13 +0800 Subject: [PATCH 2213/2296] fix (image/v2/images): image v2 unmarshal issue --- openstack/image/v2/images/results.go | 10 ++++- .../image/v2/images/testing/fixtures_test.go | 43 ++++++++++++++++++- .../image/v2/images/testing/requests_test.go | 7 +-- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/openstack/image/v2/images/results.go b/openstack/image/v2/images/results.go index f21101cc87..566bbb4e82 100644 --- a/openstack/image/v2/images/results.go +++ b/openstack/image/v2/images/results.go @@ -70,7 +70,7 @@ type Image struct { // Properties is a set of key-value pairs, if any, that are associated with // the image. - Properties map[string]any + Properties map[string]any `json:"-"` // CreatedAt is the date when the image has been created. CreatedAt time.Time `json:"created_at"` @@ -102,6 +102,7 @@ func (r *Image) UnmarshalJSON(b []byte) error { type tmp Image var s struct { tmp + Properties string `json:"properties"` SizeBytes any `json:"size"` OpenStackImageImportMethods string `json:"openstack-image-import-methods"` OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` @@ -123,7 +124,7 @@ func (r *Image) UnmarshalJSON(b []byte) error { return fmt.Errorf("unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t) } - // Bundle all other fields into Properties + // Bundle all other fields into Properties, except for the field named "properties" var result any err = json.Unmarshal(b, &result) if err != nil { @@ -137,6 +138,11 @@ func (r *Image) UnmarshalJSON(b []byte) error { r.Properties = gophercloud.RemainingKeys(Image{}, resultMap) } + // Add the "properties" field to the image since it's not included in above step (i.e. in remaining keys) + if s.Properties != "" { + r.Properties["properties"] = s.Properties + } + if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageImportMethods), splitFunc); len(v) > 0 { r.OpenStackImageImportMethods = v } diff --git a/openstack/image/v2/images/testing/fixtures_test.go b/openstack/image/v2/images/testing/fixtures_test.go index 0ec25214d1..9665e4ed55 100644 --- a/openstack/image/v2/images/testing/fixtures_test.go +++ b/openstack/image/v2/images/testing/fixtures_test.go @@ -19,7 +19,7 @@ type imageEntry struct { // HandleImageListSuccessfully test setup func HandleImageListSuccessfully(t *testing.T, fakeServer th.FakeServer) { - images := make([]imageEntry, 3) + images := make([]imageEntry, 4) images[0] = imageEntry{"cirros-0.3.4-x86_64-uec", `{ @@ -98,6 +98,46 @@ func HandleImageListSuccessfully(t *testing.T, fakeServer th.FakeServer) { "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" }`} + images[3] = imageEntry{"ubuntu-24.04-server-cloudimg-amd64.img", + `{ + "owner_specified.openstack.md5": "", + "owner_specified.openstack.object": "images/johndoe-noble-iso-test", + "owner_specified.openstack.sha256": "", + "properties": "{'hypervisor_type': 'qemu', 'architecture': 'x86_64'}", + "additional_property": "{\"name\": \"noble\", \"size\": 100, \"hidden\": false}", + "name": "johndoe-noble-iso-test", + "disk_format": "iso", + "container_format": "bare", + "visibility": "shared", + "size": 3303444480, + "virtual_size": null, + "status": "active", + "checksum": "19fe5793f7411cfe124d9fa0c0f4d449", + "protected": false, + "min_ram": 0, + "min_disk": 0, + "owner": "dfdfed1a51504178abe8c5eeaeb8343b", + "os_hidden": false, + "os_hash_algo": "sha256", + "os_hash_value": "c3514bf0056180d09376462a7a1b4f213c1d6e8ea67fae5c25099c6fd3d8274b", + "id": "370ba8b7-3e72-4f9d-8623-7d9c95e4c28d", + "created_at": "2025-12-18T02:05:15Z", + "updated_at": "2025-12-18T02:42:05Z", + "locations": [ + { + "url": "rbd://eea9d068-c18c-11ed-8dc0-013aacb71b80/glance/370ba8b7-3e72-4f9d-8623-7d9c95e4c28d/snap", + "metadata": { + "store": "ceph" + } + } + ], + "direct_url": "rbd://eea9d068-c18c-11ed-8dc0-013aacb71b80/glance/370ba8b7-3e72-4f9d-8623-7d9c95e4c28d/snap", + "tags": [], + "self": "/v2/images/370ba8b7-3e72-4f9d-8623-7d9c95e4c28d", + "file": "/v2/images/370ba8b7-3e72-4f9d-8623-7d9c95e4c28d/file", + "schema": "/v2/schemas/image", + "stores": "ceph" + }`} fakeServer.Mux.HandleFunc("/images", func(w http.ResponseWriter, r *http.Request) { th.TestMethod(t, r, "GET") @@ -282,6 +322,7 @@ func HandleImageGetSuccessfully(t *testing.T, fakeServer th.FakeServer) { "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi" + "properties": "{\"test\": true}", }`) }) } diff --git a/openstack/image/v2/images/testing/requests_test.go b/openstack/image/v2/images/testing/requests_test.go index 90c9833be1..d98ac88ae0 100644 --- a/openstack/image/v2/images/testing/requests_test.go +++ b/openstack/image/v2/images/testing/requests_test.go @@ -40,8 +40,8 @@ func TestListImage(t *testing.T) { th.AssertNoErr(t, err) t.Logf("--------\n%d images listed on %d pages.\n", count, pages) - th.AssertEquals(t, 3, pages) - th.AssertEquals(t, 3, count) + th.AssertEquals(t, 4, pages) + th.AssertEquals(t, 4, count) } func TestAllPagesImage(t *testing.T) { @@ -54,7 +54,7 @@ func TestAllPagesImage(t *testing.T) { th.AssertNoErr(t, err) images, err := images.ExtractImages(pages) th.AssertNoErr(t, err) - th.AssertEquals(t, 3, len(images)) + th.AssertEquals(t, 4, len(images)) } func TestCreateImage(t *testing.T) { @@ -239,6 +239,7 @@ func TestGetImage(t *testing.T) { "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", "hw_scsi_model": "virtio-scsi", + "properties": "{\"test\": true}", }, } From 83f935cd792742223852ec52b3002e586689a7f7 Mon Sep 17 00:00:00 2001 From: Chi Wai Chan Date: Fri, 19 Dec 2025 12:46:29 +0800 Subject: [PATCH 2214/2296] test (image/v2): improve CreateEmptyImage --- internal/acceptance/openstack/image/v2/imageservice.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/acceptance/openstack/image/v2/imageservice.go b/internal/acceptance/openstack/image/v2/imageservice.go index caa832ce6b..0454a74b88 100644 --- a/internal/acceptance/openstack/image/v2/imageservice.go +++ b/internal/acceptance/openstack/image/v2/imageservice.go @@ -38,6 +38,7 @@ func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images. Visibility: &visibility, Properties: map[string]string{ "architecture": "x86_64", + "properties": "{'hypervisor_type': 'qemu', 'architecture': 'x86_64'}", }, Tags: []string{"foo", "bar", "baz"}, } @@ -56,6 +57,7 @@ func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images. th.CheckEquals(t, newImage.Name, name) th.CheckEquals(t, newImage.Properties["architecture"], "x86_64") + th.CheckEquals(t, newImage.Properties["properties"], "{'hypervisor_type': 'qemu', 'architecture': 'x86_64'}") return newImage, nil } From 77360388fc7e0736e5c4d56e002e607131cdde57 Mon Sep 17 00:00:00 2001 From: Chi Wai Chan Date: Fri, 19 Dec 2025 18:13:26 +0800 Subject: [PATCH 2215/2296] test: add missing comma --- openstack/image/v2/images/testing/fixtures_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstack/image/v2/images/testing/fixtures_test.go b/openstack/image/v2/images/testing/fixtures_test.go index 9665e4ed55..0f47975677 100644 --- a/openstack/image/v2/images/testing/fixtures_test.go +++ b/openstack/image/v2/images/testing/fixtures_test.go @@ -321,8 +321,8 @@ func HandleImageGetSuccessfully(t *testing.T, fakeServer th.FakeServer) { "virtual_size": null, "hw_disk_bus": "scsi", "hw_disk_bus_model": "virtio-scsi", - "hw_scsi_model": "virtio-scsi" - "properties": "{\"test\": true}", + "hw_scsi_model": "virtio-scsi", + "properties": "{\"test\": true}" }`) }) } From 7e2131ea0837c5d1d0b178f394afd17f7639fe26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Mon, 22 Dec 2025 09:43:02 +0100 Subject: [PATCH 2216/2296] GHA: do not add backport-v2 labels to v2 PRs As seen in [1], we shouldn't add the `backport-v2` label to PRs that are already submitted against `v2` branch. [1] https://github.com/gophercloud/gophercloud/pull/3590#issuecomment-3681014409 --- .github/workflows/label-pr.yaml | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index 8c7e9c55fa..fb3cf97505 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -43,19 +43,44 @@ jobs: - name: Add label semver:patch if: steps.go-apidiff.outputs.semver-type == 'patch' - run: gh pr edit "$NUMBER" --add-label "semver:patch,backport-v2" --remove-label "semver:major,semver:minor" + run: | + LABELS="semver:patch" + REMOVE_LABELS="semver:major,semver:minor" + + # Only add backport-v2 if not already targeting v2 or lower + if [[ "$BASE_REF" != "v2" && "$BASE_REF" != "v1" ]]; then + LABELS="$LABELS,backport-v2" + else + REMOVE_LABELS="$REMOVE_LABELS,backport-v2" + fi + + gh pr edit "$NUMBER" --add-label "$LABELS" --remove-label "$REMOVE_LABELS" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} NUMBER: ${{ github.event.pull_request.number }} + BASE_REF: ${{ github.base_ref }} - name: Add label semver:minor if: steps.go-apidiff.outputs.semver-type == 'minor' - run: gh pr edit "$NUMBER" --add-label "semver:minor,backport-v2" --remove-label "semver:major,semver:patch,backport-v1" + run: | + LABELS="semver:minor" + REMOVE_LABELS="semver:major,semver:patch" + + # Only add backport-v2 if not already targeting v2 or lower + if [[ "$BASE_REF" != "v2" && "$BASE_REF" != "v1" ]]; then + LABELS="$LABELS,backport-v2" + REMOVE_LABELS="$REMOVE_LABELS,backport-v1" + else + REMOVE_LABELS="$REMOVE_LABELS,backport-v2,backport-v1" + fi + + gh pr edit "$NUMBER" --add-label "$LABELS" --remove-label "$REMOVE_LABELS" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} NUMBER: ${{ github.event.pull_request.number }} + BASE_REF: ${{ github.base_ref }} - name: Add label semver:major if: steps.go-apidiff.outputs.semver-type == 'major' From 12d6828ae59a8a535ee813e3e4e448748bb56f40 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Mon, 22 Dec 2025 21:29:38 +0300 Subject: [PATCH 2217/2296] Remove extra_specs, fix tests --- .../blockstorage/v3/volumetypes_test.go | 52 ++++--------------- .../blockstorage/v3/volumetypes/requests.go | 4 -- .../v3/volumetypes/testing/requests_test.go | 24 --------- 3 files changed, 11 insertions(+), 69 deletions(-) diff --git a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go index 4b2a1bdfaa..7fa8457b19 100644 --- a/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go +++ b/internal/acceptance/openstack/blockstorage/v3/volumetypes_test.go @@ -56,6 +56,7 @@ func TestVolumeTypesCRUD(t *testing.T) { th.AssertEquals(t, name, newVT.Name) th.AssertEquals(t, description, newVT.Description) th.AssertEquals(t, isPublic, newVT.IsPublic) + newVisibility := volumetypes.VisibilityPrivate for _, tt := range []struct { name string @@ -77,38 +78,12 @@ func TestVolumeTypesCRUD(t *testing.T) { expectedVolumeTypes: 1, }, { - name: "Partitial Extra Specs", + name: "Is Public", opts: volumetypes.ListOpts{ - ExtraSpecs: map[string]string{ - "volume_backend_name": "fake_backend_name", - "RESKEY:availability_zones": "zone", - }, + IsPublic: newVisibility, }, expectedVolumeTypes: 1, }, - { - name: "Full Extra Specs", - opts: volumetypes.ListOpts{ - ExtraSpecs: map[string]string{ - "volume_backend_name": "fake_backend_name", - "RESKEY:availability_zones": "zone", - "multiattach": " True", - }, - }, - expectedVolumeTypes: 1, - }, - { - name: "Extra Specs Not Found", - opts: volumetypes.ListOpts{ - ExtraSpecs: map[string]string{ - "volume_backend_name": "fake_backend_name", - "RESKEY:availability_zones": "zone", - "multiattach": " True", - "another_one_spec": "another_one_value", - }, - }, - expectedVolumeTypes: 0, - }, } { t.Run(fmt.Sprintf("List volumetypes by %s", tt.name), func(t *testing.T) { allPages, err := volumetypes.List(client, tt.opts).AllPages(context.TODO()) @@ -123,29 +98,24 @@ func TestVolumeTypesCRUD(t *testing.T) { } } - if tt.name != "Extra Specs Not Found" { - if len(allVolumeTypes) != tt.expectedVolumeTypes { - if len(allVolumeTypes) == 0 { - t.Fatalf("Volume type not found") - } + if len(allVolumeTypes) != tt.expectedVolumeTypes { + if len(allVolumeTypes) == 0 { + t.Fatalf("Volume type not found") } } else { - if len(allVolumeTypes) != 0 { + if len(allVolumeTypes) > 1 { logVolumeTypes() t.Fatalf("Expected %d volume type but got %d", tt.expectedVolumeTypes, len(allVolumeTypes)) } } func() { for _, volumetype := range allVolumeTypes { - if tt.name != "Extra Specs Not Found" { - if volumetype.ID == newVT.ID { - return - } else { - logVolumeTypes() - t.Fatalf("Returned volume types did not contain expected volume type") - } + if volumetype.ID == newVT.ID { + return } } + logVolumeTypes() + t.Fatalf("Returned volume types did not contain expected volume type") }() }) } diff --git a/openstack/blockstorage/v3/volumetypes/requests.go b/openstack/blockstorage/v3/volumetypes/requests.go index ca8c8be754..d62db1c8b6 100644 --- a/openstack/blockstorage/v3/volumetypes/requests.go +++ b/openstack/blockstorage/v3/volumetypes/requests.go @@ -80,10 +80,6 @@ type ListOpts struct { // Specifies whether the query should include public or private Volume Types. // By default, it queries both types. IsPublic visibility `q:"is_public"` - // ExtraSpecs will filter results based on specified extra specs. - // The map key is the extra spec name, and the value is the filter value. - // For example: map[string]string{"multiattach": " True", "storage_protocol": "nfs"} - ExtraSpecs map[string]string `q:"extra_specs"` // Comma-separated list of sort keys and optional sort directions in the // form of [:]. Sort string `q:"sort"` diff --git a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go index da830502ee..b64a269c59 100644 --- a/openstack/blockstorage/v3/volumetypes/testing/requests_test.go +++ b/openstack/blockstorage/v3/volumetypes/testing/requests_test.go @@ -208,30 +208,6 @@ func TestListDescriptionParam(t *testing.T) { th.AssertEquals(t, " True", actual[0].ExtraSpecs["multiattach"]) } -func TestListExtraSpecsParam(t *testing.T) { - fakeServer := th.SetupHTTP() - defer fakeServer.Teardown() - result := make(map[string]string) - result["is_public"] = "None" - result["extra_specs"] = "{'storage_protocol':'nfs'}" - HandleListWithExtraSpecsFilter(t, fakeServer, result) - - // Test with extra_specs filter - allPages, err := volumetypes.List(client.ServiceClient(fakeServer), volumetypes.ListOpts{ - ExtraSpecs: map[string]string{"storage_protocol": "nfs"}, - }).AllPages(context.TODO()) - th.AssertNoErr(t, err) - actual, err := volumetypes.ExtractVolumeTypes(allPages) - th.AssertNoErr(t, err) - th.AssertEquals(t, 1, len(actual)) - th.AssertEquals(t, "nfs-type", actual[0].Name) - th.AssertEquals(t, "6b0cfee7-48b6-41b7-9d68-0d74cbdc08de", actual[0].ID) - th.AssertEquals(t, "NFS storage type", actual[0].Description) - th.AssertEquals(t, true, actual[0].IsPublic) - th.AssertEquals(t, true, actual[0].PublicAccess) - th.AssertEquals(t, "nfs", actual[0].ExtraSpecs["storage_protocol"]) -} - func TestVolumeTypeExtraSpecsList(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() From 53e1258a8d7447fbd5ab913ca0a9545af09113b5 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Wed, 24 Dec 2025 01:31:26 +0300 Subject: [PATCH 2218/2296] LoadBalancer V2: Add availability zone profiles Add availability zone requests Add tests Add docs --- .../v2/availabilityzoneprofiles_test.go | 54 ++++++ .../v2/availabilityzoneprofiles/doc.go | 57 +++++++ .../v2/availabilityzoneprofiles/requests.go | 145 ++++++++++++++++ .../v2/availabilityzoneprofiles/results.go | 96 +++++++++++ .../availabilityzoneprofiles/testing/doc.go | 1 + .../testing/fixtures.go | 159 ++++++++++++++++++ .../testing/requests_test.go | 122 ++++++++++++++ .../v2/availabilityzoneprofiles/urls.go | 16 ++ 8 files changed, 650 insertions(+) create mode 100644 internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go create mode 100644 openstack/loadbalancer/v2/availabilityzoneprofiles/doc.go create mode 100644 openstack/loadbalancer/v2/availabilityzoneprofiles/requests.go create mode 100644 openstack/loadbalancer/v2/availabilityzoneprofiles/results.go create mode 100644 openstack/loadbalancer/v2/availabilityzoneprofiles/testing/doc.go create mode 100644 openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go create mode 100644 openstack/loadbalancer/v2/availabilityzoneprofiles/testing/requests_test.go create mode 100644 openstack/loadbalancer/v2/availabilityzoneprofiles/urls.go diff --git a/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go new file mode 100644 index 0000000000..fc991caa2c --- /dev/null +++ b/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go @@ -0,0 +1,54 @@ +//go:build acceptance || networking || loadbalancer || flavorprofiles + +package v2 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/internal/ptr" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/availabilityzoneprofiles" + + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestAvailabilityZoneProfilesList(t *testing.T) { + client, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + allPages, err := availabilityzoneprofiles.List(client, nil).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allAvailabilityZoneProfiles, err := availabilityzoneprofiles.ExtractFlavorProfiles(allPages) + th.AssertNoErr(t, err) + + for _, availabilityzoneprofile := range allAvailabilityZoneProfiles { + tools.PrintResource(t, availabilityzoneprofile) + } +} + +func TestAvailabilityZoneProfilesCRUD(t *testing.T) { + lbClient, err := clients.NewLoadBalancerV2Client() + th.AssertNoErr(t, err) + + availabilityZoneProfile, err := CreateAvailabilityZonerProfile(t, lbClient) + th.AssertNoErr(t, err) + defer DeleteAvailabilityZoneProfile(t, lbClient, availabilityZoneProfile) + + tools.PrintResource(t, availabilityZoneProfile) + + th.AssertEquals(t, "amphora", availabilityZoneProfile.ProviderName) + + availabilityZoneProfileUpdateOpts := availabilityzoneprofiles.UpdateOpts{ + Name: ptr.To(tools.RandomString("TESTACCTUP-", 8)), + } + + availabilityZoneProfileUpdated, err := availabilityzoneprofiles.Update(context.TODO(), lbClient, availabilityZoneProfile.ID, availabilityZoneProfileUpdateOpts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, *availabilityZoneProfileUpdateOpts.Name, availabilityZoneProfileUpdated.Name) + + t.Logf("Successfully updated availabiltyzoneprofile %s", availabilityZoneProfileUpdated.Name) +} diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/doc.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/doc.go new file mode 100644 index 0000000000..a6a47f205f --- /dev/null +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/doc.go @@ -0,0 +1,57 @@ +/* +Package availabilityzoneprofiles provides information and interaction +with AvailabilityZoneProfiles for the OpenStack Load-balancing service. + +Example to List AvailabilityZoneProfiles + + listOpts := availabilityzoneprofiles.ListOpts{} + + allPages, err := availabilityzoneprofiles.List(octaviaClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allAvailabilityZoneProfiles, err := availabilityzoneprofiles.ExtractAvailabilityZoneProfiles(allPages) + if err != nil { + panic(err) + } + + for _, availabilityZoneProfile := range allAvailabilityZoneProfiles { + fmt.Printf("%+v\n", availabilityZoneProfile) + } + +Example to Create a AvailabilityZoneProfile + + createOpts := availabilityzoneprofiles.CreateOpts{ + Name: "availability-zone-profile", + ProviderName: "amphora", + AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + } + + availabilityZoneProfile, err := availabilityzoneprofiles.Create(context.TODO(), octaviaClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update a AvailabilityZoneProfile + + availabilityZoneProfileID := "0c359d38-6164-498f-8409-5b11d05b6226" + + updateOpts := availabilityzoneprofiles.UpdateOpts{ + Name: "availability-zone-profile-updated", + } + + availabilityZoneProfile, err := availabilityzoneprofiles.Update(context.TODO(), octaviaClient, availabilityZoneProfileID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a AvailabilityZoneProfile + + availabilityZoneProfileID := "0c359d38-6164-498f-8409-5b11d05b6226" + err := availabilityzoneprofiles.Delete(context.TODO(), octaviaClient, availabilityZoneProfileID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package availabilityzoneprofiles diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/requests.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/requests.go new file mode 100644 index 0000000000..869bf0d84d --- /dev/null +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/requests.go @@ -0,0 +1,145 @@ +package availabilityzoneprofiles + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToAvailabilityZoneProfileListQuery() (string, error) +} + +// ListOpts allows to manage the output of the request. +type ListOpts struct { + // The name of the availability zone profile to filter by. + Name string `q:"name"` + // The provider name of the availability zone profile to filter by. + ProviderName string `q:"provider_name"` + // The fields that you want the server to return + Fields []string `q:"fields"` +} + +// ToAvailabilityZoneProfileListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToAvailabilityZoneProfileListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List returns a Pager which allows you to iterate over a collection of +// AvailabilityZoneProfiles. 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.ToAvailabilityZoneProfileListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return AvailabilityZoneProfilePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. +type CreateOptsBuilder interface { + ToAvailabilityZoneProfileCreateMap() (map[string]any, error) +} + +// CreateOpts is the common options struct used in this package's create +// operation. +type CreateOpts struct { + // Human-readable name for the avaialability zone profile. + // 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 availability zone metadata. + AvailabilityZoneData string `json:"availability_zone_data" required:"true"` +} + +// ToAvailabilityZoneProfileCreateMap builds a request body from CreateOpts. +func (opts CreateOpts) ToAvailabilityZoneProfileCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "availability_zone_profile") +} + +// Create is and operation which add a new AvailabilityZoneProfile into the database. +// CreateResult will be returned. +func Create(ctx context.Context, c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToAvailabilityZoneProfileCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Post(ctx, rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Get retrieves a particular AvailabilityZoneProfile based on its unique ID. +func Get(ctx context.Context, c *gophercloud.ServiceClient, id string) (r GetResult) { + resp, err := c.Get(ctx, resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional parameters to the +// update request. +type UpdateOptsBuilder interface { + ToAvailabiltyZoneProfileUpdateMap() (map[string]any, error) +} + +// UpdateOpts is the common options struct used in this package's update +// operation. +type UpdateOpts struct { + // Human-readable name for the availability zone profile. + // 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 availability zone metadata. + AvailabiltyZoneData *string `json:"availability_zone_data,omitempty"` +} + +// ToAvailabiltyZoneProfileUpdateMap builds a request body from UpdateOpts. +func (opts UpdateOpts) ToAvailabiltyZoneProfileUpdateMap() (map[string]any, error) { + b, err := gophercloud.BuildRequestBody(opts, "availability_zone_profile") + if err != nil { + return nil, err + } + + return b, nil +} + +// Update is an operation which modifies the attributes of the specified +// AvailabilityZoneProfile. +func Update(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToAvailabiltyZoneProfileUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(ctx, 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 AvailabiltyZoneProfile based on +// its unique ID. +func Delete(ctx context.Context, c *gophercloud.ServiceClient, id string) (r DeleteResult) { + resp, err := c.Delete(ctx, resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/results.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/results.go new file mode 100644 index 0000000000..b002ba9ad0 --- /dev/null +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/results.go @@ -0,0 +1,96 @@ +package availabilityzoneprofiles + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +type AvailabilityZoneProfile struct { + // The unique ID for the AvailabilityZoneProfile + ID string `json:"id"` + + // Human-readable name for the AvailabilityZoneProfile. + // Does not have to be unique. + Name string `json:"name"` + + // Name of the provider + ProviderName string `json:"provider_name"` + + // Availability zone data + AvailabilityZoneData string `json:"availability_zone_data"` +} + +// AvailabilityZoneProfile is the page returned by a pager when traversing +// over a collection of profiles. +type AvailabilityZoneProfilePage struct { + pagination.LinkedPageBase +} + +// NextPageURL is invoked when a paginated collection of 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 AvailabilityZoneProfilePage) NextPageURL(endpointURL string) (string, error) { + var s struct { + Links []gophercloud.Link `json:"availabilityzoneprofiles_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// IsEmpty checks whether a AvailabilityZoneProfilePage struct is empty. +func (r AvailabilityZoneProfilePage) IsEmpty() (bool, error) { + is, err := ExtractAvailabilityZoneProfiles(r) + return len(is) == 0, err +} + +// ExtractAvailabilityZoneProfiles accepts a Page struct, specifically a +// AvailabilityZoneProfilePage struct, and extracts the elements into a slice +// of Flavor structs. In other words, a generic collection is mapped into a +// relevant slice. +func ExtractAvailabilityZoneProfiles(r pagination.Page) ([]AvailabilityZoneProfile, error) { + var s struct { + AvailabilityZoneProfiles []AvailabilityZoneProfile `json:"availability_zone_profiles"` + } + err := (r.(AvailabilityZoneProfilePage)).ExtractInto(&s) + return s.AvailabilityZoneProfiles, err +} + +type commonResult struct { + gophercloud.Result +} + +// Extract is a function that accepts a result and extracts a flavor. +func (r commonResult) Extract() (*AvailabilityZoneProfile, error) { + var s struct { + AvailabilityZoneProfile *AvailabilityZoneProfile `json:"availability_zone_profile"` + } + err := r.ExtractInto(&s) + return s.AvailabilityZoneProfile, 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/availabilityzoneprofiles/testing/doc.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go new file mode 100644 index 0000000000..873bd52244 --- /dev/null +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go @@ -0,0 +1,159 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/availabilityzoneprofiles" + + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const AvailabilityZoneProfilesListBody = ` +{ + "availability_zone_profiles": [ + { + "id": "1d334061-d807-4997-8f34-9fe428ba37df", + "name": "availability-zone-profile-first", + "provider_name": "amphora", + "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + }, + { + "id": "56f45d00-86e4-4bea-8525-19e835776c4e", + "name": "availability-zone-profile-second", + "provider_name": "amphora", + "availability_zone_data": "{\"compute_zone\": \"not_nova\", \"volume_zone\": \"not_nova\"}" + } + ] +} +` + +const SingleAvailabilityZoneProfileBody = ` +{ + "availability_zone_profile": { + "id": "13be083b-f502-426e-8500-07600f98b91b", + "name": "availability-zone-profile", + "provider_name": "amphora", + "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + } +} +` + +const PostUpdateAvailabilityZoneFlavorBody = ` +{ + "availability_zone_profile": { + "id": "13be083b-f502-426e-8500-07600f98b91b", + "name": "availability-zone-profile-updated", + "provider_name": "amphora", + "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + } +} +` + +var ( + AvailabilityZoneProfileSingle = availabilityzoneprofiles.AvailabilityZoneProfile{ + ID: "1d334061-d807-4997-8f34-9fe428ba37df", + Name: "availability-zone-profile-first", + ProviderName: "amphora", + AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + } + + AvailabilityZoneProfileAct = availabilityzoneprofiles.AvailabilityZoneProfile{ + ID: "56f45d00-86e4-4bea-8525-19e835776c4e", + Name: "availability-zone-profile-second", + ProviderName: "amphora", + AvailabilityZoneData: "{\"compute_zone\": \"not_nova\", \"volume_zone\": \"not_nova\"}", + } + + AvailabilityZoneProfileDb = availabilityzoneprofiles.AvailabilityZoneProfile{ + ID: "13be083b-f502-426e-8500-07600f98b91b", + Name: "availability-zone-profile", + ProviderName: "amphora", + AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + } + + AvailabilityZoneProfileUpdated = availabilityzoneprofiles.AvailabilityZoneProfile{ + ID: "13be083b-f502-426e-8500-07600f98b91b", + Name: "availability-zone-profile-updated", + ProviderName: "amphora", + AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + } +) + +func HandleAvailabilityZoneProfileListSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/v2.0/lbaas/availabilityzoneprofiles", 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") + if err := r.ParseForm(); err != nil { + t.Errorf("Failed to parse request form %v", err) + } + marker := r.Form.Get("marker") + switch marker { + case "": + fmt.Fprint(w, AvailabilityZoneProfilesListBody) + case "56f45d00-86e4-4bea-8525-19e835776c4e": + fmt.Fprint(w, `{ "availability_zone_profiles": [] }`) + default: + t.Fatalf("/v2.0/lbaas/availabilityzoneprofiles invoked with unexpected marker=[%s]", marker) + } + }) +} + +func HandleAvailabilityZoneProfileCreationSuccessfully(t *testing.T, fakeServer th.FakeServer, response string) { + fakeServer.Mux.HandleFunc("/v2.0/lbaas/availabilityzoneprofiles", 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, `{ + "availability_zone_profile": { + "name": "availability-zone-profile", + "provider_name": "amphora", + "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + } + }`) + + w.WriteHeader(http.StatusAccepted) + w.Header().Add("Content-Type", "application/json") + fmt.Fprint(w, response) + }) +} + +func HandleAvailabilityZoneProfileGetSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/v2.0/lbaas/availabilityzoneprofiles/13be083b-f502-426e-8500-07600f98b91b", 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.Fprint(w, SingleAvailabilityZoneProfileBody) + }) +} + +func HandleAvailabilityZoneProfileDeletionSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/v2.0/lbaas/availabilityzoneprofiles/13be083b-f502-426e-8500-07600f98b91b", 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 HandleAvailabilityZoneProfileUpdateSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/v2.0/lbaas/availabilityzoneprofiles/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, `{ + "availability_zone_profile": { + "name": "availability-zone-profile-updated", + "provider_name": "amphora", + "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + } + }`) + + fmt.Fprint(w, PostUpdateAvailabilityZoneFlavorBody) + }) +} diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/requests_test.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/requests_test.go new file mode 100644 index 0000000000..afea3921fb --- /dev/null +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/requests_test.go @@ -0,0 +1,122 @@ +package testing + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/ptr" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/availabilityzoneprofiles" + "github.com/gophercloud/gophercloud/v2/pagination" + + fake "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/testhelper" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestListAvailabiltyZoneProfiles(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleAvailabilityZoneProfileListSuccessfully(t, fakeServer) + + pages := 0 + err := availabilityzoneprofiles.List(fake.ServiceClient(fakeServer), availabilityzoneprofiles.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + pages++ + + actual, err := availabilityzoneprofiles.ExtractAvailabilityZoneProfiles(page) + if err != nil { + return false, err + } + + if len(actual) != 2 { + t.Fatalf("Expected 2 avaialbility zone profiles, got %d", len(actual)) + } + th.CheckDeepEquals(t, AvailabilityZoneProfileSingle, actual[0]) + th.CheckDeepEquals(t, AvailabilityZoneProfileAct, actual[1]) + + return true, nil + }) + + th.AssertNoErr(t, err) + + if pages != 1 { + t.Errorf("Expected 1 page, saw %d", pages) + } +} + +func TestListAllAvailabilityZoneProfiles(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleAvailabilityZoneProfileListSuccessfully(t, fakeServer) + + allPages, err := availabilityzoneprofiles.List(fake.ServiceClient(fakeServer), availabilityzoneprofiles.ListOpts{}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := availabilityzoneprofiles.ExtractAvailabilityZoneProfiles(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, AvailabilityZoneProfileSingle, actual[0]) + th.CheckDeepEquals(t, AvailabilityZoneProfileAct, actual[1]) +} + +func TestCreateAvailabilityZoneProfile(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleAvailabilityZoneProfileCreationSuccessfully(t, fakeServer, SingleAvailabilityZoneProfileBody) + + actual, err := availabilityzoneprofiles.Create(context.TODO(), fake.ServiceClient(fakeServer), availabilityzoneprofiles.CreateOpts{ + Name: "availability-zone-profile", + ProviderName: "amphora", + AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + }).Extract() + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, AvailabilityZoneProfileDb, *actual) +} + +func TestRequiredCreateOpts(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + res := availabilityzoneprofiles.Create(context.TODO(), fake.ServiceClient(fakeServer), availabilityzoneprofiles.CreateOpts{}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestGetAvailabilityZoneProfiles(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleAvailabilityZoneProfileGetSuccessfully(t, fakeServer) + + client := fake.ServiceClient(fakeServer) + actual, err := availabilityzoneprofiles.Get(context.TODO(), client, "13be083b-f502-426e-8500-07600f98b91b").Extract() + if err != nil { + t.Fatalf("Unexpected Get error: %v", err) + } + + th.CheckDeepEquals(t, AvailabilityZoneProfileDb, *actual) +} + +func TestDeleteAvailabilityZoneProfile(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleAvailabilityZoneProfileDeletionSuccessfully(t, fakeServer) + + res := availabilityzoneprofiles.Delete(context.TODO(), fake.ServiceClient(fakeServer), "13be083b-f502-426e-8500-07600f98b91b") + th.AssertNoErr(t, res.Err) +} + +func TestUpdateAvailabililtyZoneProfile(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleAvailabilityZoneProfileUpdateSuccessfully(t, fakeServer) + + client := fake.ServiceClient(fakeServer) + actual, err := availabilityzoneprofiles.Update(context.TODO(), client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272", availabilityzoneprofiles.UpdateOpts{ + Name: ptr.To("availability-zone-profile-updated"), + ProviderName: ptr.To("amphora"), + AvailabiltyZoneData: ptr.To(`{"compute_zone": "nova", "volume_zone": "nova"}`), + }).Extract() + if err != nil { + t.Fatalf("Unexpected Update error: %v", err) + } + + th.CheckDeepEquals(t, AvailabilityZoneProfileUpdated, *actual) +} diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/urls.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/urls.go new file mode 100644 index 0000000000..e2321402a3 --- /dev/null +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/urls.go @@ -0,0 +1,16 @@ +package availabilityzoneprofiles + +import "github.com/gophercloud/gophercloud/v2" + +const ( + rootPath = "lbaas" + resourcePath = "availabilityzoneprofiles" +) + +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 fd88cf1a5ea1052aab22247fdc7391125a8b6482 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Wed, 24 Dec 2025 01:34:31 +0300 Subject: [PATCH 2219/2296] Fix syntax --- .../openstack/loadbalancer/v2/availabilityzoneprofiles_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go index fc991caa2c..27483926ed 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go @@ -1,4 +1,4 @@ -//go:build acceptance || networking || loadbalancer || flavorprofiles +//go:build acceptance || networking || loadbalancer || availabilityzoneprofiles package v2 From 7dedf220109e7c1fd53df1dff5015d4faeee7dbe Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Thu, 25 Dec 2025 16:50:36 +0300 Subject: [PATCH 2220/2296] Add missing methods, fix typo --- .../v2/availabilityzoneprofiles_test.go | 4 +-- .../openstack/loadbalancer/v2/loadbalancer.go | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go index 27483926ed..03707128cf 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go @@ -21,7 +21,7 @@ func TestAvailabilityZoneProfilesList(t *testing.T) { allPages, err := availabilityzoneprofiles.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) - allAvailabilityZoneProfiles, err := availabilityzoneprofiles.ExtractFlavorProfiles(allPages) + allAvailabilityZoneProfiles, err := availabilityzoneprofiles.ExtractAvailabilityZones(allPages) th.AssertNoErr(t, err) for _, availabilityzoneprofile := range allAvailabilityZoneProfiles { @@ -33,7 +33,7 @@ func TestAvailabilityZoneProfilesCRUD(t *testing.T) { lbClient, err := clients.NewLoadBalancerV2Client() th.AssertNoErr(t, err) - availabilityZoneProfile, err := CreateAvailabilityZonerProfile(t, lbClient) + availabilityZoneProfile, err := CreateAvailabilityZoneProfile(t, lbClient) th.AssertNoErr(t, err) defer DeleteAvailabilityZoneProfile(t, lbClient, availabilityZoneProfile) diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index cc62ba626e..714f4b8e01 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/internal/ptr" + "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/availabilityzoneprofiles" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavorprofiles" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/flavors" "github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/l7policies" @@ -761,3 +762,37 @@ func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavo t.Logf("Successfully deleted flavor %s", flavor.Name) } + +func CreateAvailabilityZoneProfile(t *testing.T, client *gophercloud.ServiceClient) (*availabilityzoneprofiles.AvailabilityZoneProfile, error) { + availabilityZoneProfileName := tools.RandomString("TESTACCT-", 8) + availabilityZoneProfileDriver := "amphora" + availabilityZoneData := "{\"compute_zone\": \"not_nova\", \"volume_zone\": \"not_nova\"}" + + createOpts := availabilityzoneprofiles.CreateOpts{ + Name: availabilityZoneProfileName, + ProviderName: availabilityZoneProfileDriver, + AvailabilityZoneData: availabilityZoneData, + } + + availabilityZoneProfile, err := availabilityzoneprofiles.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + return availabilityZoneProfile, err + } + + t.Logf("Successfully created availabilityzoneprofile %s", availabilityZoneProfileName) + + th.AssertEquals(t, availabilityZoneProfileName, availabilityZoneProfile.Name) + th.AssertEquals(t, availabilityZoneProfileDriver, availabilityZoneProfile.ProviderName) + th.AssertEquals(t, availabilityZoneData, availabilityZoneProfile.AvailabilityZoneData) + + return availabilityZoneProfile, nil +} + +func DeleteAvailabilityZoneProfile(t *testing.T, client *gophercloud.ServiceClient, availabilityZoneProfile *availabilityzoneprofiles.AvailabilityZoneProfile) { + err := availabilityzoneprofiles.Delete(context.TODO(), client, availabilityZoneProfile.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete availabilityzoneprofile: %v", err) + } + + t.Logf("Successfully deleted availabilityzoneprofile %s", availabilityZoneProfile.Name) +} From c2f47ec499148dd7ab722f9d870cf14050c07bf8 Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Thu, 25 Dec 2025 16:56:45 +0300 Subject: [PATCH 2221/2296] Fix typo --- .../openstack/loadbalancer/v2/availabilityzoneprofiles_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go b/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go index 03707128cf..238647a3b0 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go +++ b/internal/acceptance/openstack/loadbalancer/v2/availabilityzoneprofiles_test.go @@ -21,7 +21,7 @@ func TestAvailabilityZoneProfilesList(t *testing.T) { allPages, err := availabilityzoneprofiles.List(client, nil).AllPages(context.TODO()) th.AssertNoErr(t, err) - allAvailabilityZoneProfiles, err := availabilityzoneprofiles.ExtractAvailabilityZones(allPages) + allAvailabilityZoneProfiles, err := availabilityzoneprofiles.ExtractAvailabilityZoneProfiles(allPages) th.AssertNoErr(t, err) for _, availabilityzoneprofile := range allAvailabilityZoneProfiles { From b6e7b17aa6c75f65ee5d3b1b721eabb752352faa Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Thu, 25 Dec 2025 17:22:44 +0300 Subject: [PATCH 2222/2296] Fix fixtures data --- internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go | 2 +- .../v2/availabilityzoneprofiles/testing/fixtures.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index 714f4b8e01..bdbe9ddf5a 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -766,7 +766,7 @@ func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavo func CreateAvailabilityZoneProfile(t *testing.T, client *gophercloud.ServiceClient) (*availabilityzoneprofiles.AvailabilityZoneProfile, error) { availabilityZoneProfileName := tools.RandomString("TESTACCT-", 8) availabilityZoneProfileDriver := "amphora" - availabilityZoneData := "{\"compute_zone\": \"not_nova\", \"volume_zone\": \"not_nova\"}" + availabilityZoneData := "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" createOpts := availabilityzoneprofiles.CreateOpts{ Name: availabilityZoneProfileName, diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go index 873bd52244..1aebb02284 100644 --- a/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go @@ -24,7 +24,7 @@ const AvailabilityZoneProfilesListBody = ` "id": "56f45d00-86e4-4bea-8525-19e835776c4e", "name": "availability-zone-profile-second", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"not_nova\", \"volume_zone\": \"not_nova\"}" + "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" } ] } @@ -64,7 +64,7 @@ var ( ID: "56f45d00-86e4-4bea-8525-19e835776c4e", Name: "availability-zone-profile-second", ProviderName: "amphora", - AvailabilityZoneData: "{\"compute_zone\": \"not_nova\", \"volume_zone\": \"not_nova\"}", + AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", } AvailabilityZoneProfileDb = availabilityzoneprofiles.AvailabilityZoneProfile{ From d761fb88339840dd9cefcb0a18199d6f7f7331dc Mon Sep 17 00:00:00 2001 From: Konstantin Eremin Date: Thu, 25 Dec 2025 18:18:06 +0300 Subject: [PATCH 2223/2296] Remove availability zone profile volume_zone, only master --- .../openstack/loadbalancer/v2/loadbalancer.go | 2 +- .../v2/availabilityzoneprofiles/doc.go | 2 +- .../testing/fixtures.go | 20 +++++++++---------- .../testing/requests_test.go | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go index bdbe9ddf5a..8490d27f36 100644 --- a/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go +++ b/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go @@ -766,7 +766,7 @@ func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavo func CreateAvailabilityZoneProfile(t *testing.T, client *gophercloud.ServiceClient) (*availabilityzoneprofiles.AvailabilityZoneProfile, error) { availabilityZoneProfileName := tools.RandomString("TESTACCT-", 8) availabilityZoneProfileDriver := "amphora" - availabilityZoneData := "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + availabilityZoneData := "{\"compute_zone\": \"nova\"}" createOpts := availabilityzoneprofiles.CreateOpts{ Name: availabilityZoneProfileName, diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/doc.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/doc.go index a6a47f205f..30a3223da2 100644 --- a/openstack/loadbalancer/v2/availabilityzoneprofiles/doc.go +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/doc.go @@ -25,7 +25,7 @@ Example to Create a AvailabilityZoneProfile createOpts := availabilityzoneprofiles.CreateOpts{ Name: "availability-zone-profile", ProviderName: "amphora", - AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + AvailabilityZoneData: "{\"compute_zone\": \"nova\"}", } availabilityZoneProfile, err := availabilityzoneprofiles.Create(context.TODO(), octaviaClient, createOpts).Extract() diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go index 1aebb02284..b60ce711ac 100644 --- a/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/fixtures.go @@ -18,13 +18,13 @@ const AvailabilityZoneProfilesListBody = ` "id": "1d334061-d807-4997-8f34-9fe428ba37df", "name": "availability-zone-profile-first", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + "availability_zone_data": "{\"compute_zone\": \"nova\"}" }, { "id": "56f45d00-86e4-4bea-8525-19e835776c4e", "name": "availability-zone-profile-second", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + "availability_zone_data": "{\"compute_zone\": \"nova\"}" } ] } @@ -36,7 +36,7 @@ const SingleAvailabilityZoneProfileBody = ` "id": "13be083b-f502-426e-8500-07600f98b91b", "name": "availability-zone-profile", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + "availability_zone_data": "{\"compute_zone\": \"nova\"}" } } ` @@ -47,7 +47,7 @@ const PostUpdateAvailabilityZoneFlavorBody = ` "id": "13be083b-f502-426e-8500-07600f98b91b", "name": "availability-zone-profile-updated", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + "availability_zone_data": "{\"compute_zone\": \"nova\"}" } } ` @@ -57,28 +57,28 @@ var ( ID: "1d334061-d807-4997-8f34-9fe428ba37df", Name: "availability-zone-profile-first", ProviderName: "amphora", - AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + AvailabilityZoneData: "{\"compute_zone\": \"nova\"}", } AvailabilityZoneProfileAct = availabilityzoneprofiles.AvailabilityZoneProfile{ ID: "56f45d00-86e4-4bea-8525-19e835776c4e", Name: "availability-zone-profile-second", ProviderName: "amphora", - AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + AvailabilityZoneData: "{\"compute_zone\": \"nova\"}", } AvailabilityZoneProfileDb = availabilityzoneprofiles.AvailabilityZoneProfile{ ID: "13be083b-f502-426e-8500-07600f98b91b", Name: "availability-zone-profile", ProviderName: "amphora", - AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + AvailabilityZoneData: "{\"compute_zone\": \"nova\"}", } AvailabilityZoneProfileUpdated = availabilityzoneprofiles.AvailabilityZoneProfile{ ID: "13be083b-f502-426e-8500-07600f98b91b", Name: "availability-zone-profile-updated", ProviderName: "amphora", - AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + AvailabilityZoneData: "{\"compute_zone\": \"nova\"}", } ) @@ -111,7 +111,7 @@ func HandleAvailabilityZoneProfileCreationSuccessfully(t *testing.T, fakeServer "availability_zone_profile": { "name": "availability-zone-profile", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + "availability_zone_data": "{\"compute_zone\": \"nova\"}" } }`) @@ -150,7 +150,7 @@ func HandleAvailabilityZoneProfileUpdateSuccessfully(t *testing.T, fakeServer th "availability_zone_profile": { "name": "availability-zone-profile-updated", "provider_name": "amphora", - "availability_zone_data": "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}" + "availability_zone_data": "{\"compute_zone\": \"nova\"}" } }`) diff --git a/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/requests_test.go b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/requests_test.go index afea3921fb..7ce47457b6 100644 --- a/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/requests_test.go +++ b/openstack/loadbalancer/v2/availabilityzoneprofiles/testing/requests_test.go @@ -63,7 +63,7 @@ func TestCreateAvailabilityZoneProfile(t *testing.T) { actual, err := availabilityzoneprofiles.Create(context.TODO(), fake.ServiceClient(fakeServer), availabilityzoneprofiles.CreateOpts{ Name: "availability-zone-profile", ProviderName: "amphora", - AvailabilityZoneData: "{\"compute_zone\": \"nova\", \"volume_zone\": \"nova\"}", + AvailabilityZoneData: "{\"compute_zone\": \"nova\"}", }).Extract() th.AssertNoErr(t, err) @@ -112,7 +112,7 @@ func TestUpdateAvailabililtyZoneProfile(t *testing.T) { actual, err := availabilityzoneprofiles.Update(context.TODO(), client, "dcd65be5-f117-4260-ab3d-b32cc5bd1272", availabilityzoneprofiles.UpdateOpts{ Name: ptr.To("availability-zone-profile-updated"), ProviderName: ptr.To("amphora"), - AvailabiltyZoneData: ptr.To(`{"compute_zone": "nova", "volume_zone": "nova"}`), + AvailabiltyZoneData: ptr.To(`{"compute_zone": "nova"}`), }).Extract() if err != nil { t.Fatalf("Unexpected Update error: %v", err) From d2e0b940a8194903f4bda421348a2740838d50fb Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Fri, 12 Dec 2025 11:18:21 -0300 Subject: [PATCH 2224/2296] identity.v3.Endpoint: make name as optional on creation --- openstack/identity/v3/endpoints/requests.go | 2 +- openstack/identity/v3/endpoints/testing/requests_test.go | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 45893aa3c6..8c08542848 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -26,7 +26,7 @@ type CreateOpts struct { Enabled *bool `json:"enabled,omitempty"` // Name is the name of the Endpoint. - Name string `json:"name" required:"true"` + Name string `json:"name,omitempty"` // Region is the region the Endpoint is located in. // This field can be omitted or left as a blank string. diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 07798947c6..8e50467e25 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -23,7 +23,6 @@ func TestCreateSuccessful(t *testing.T) { th.TestJSONRequest(t, r, `{ "endpoint": { "interface": "public", - "name": "the-endiest-of-points", "region": "underground", "url": "https://1.2.3.4:9000/", "service_id": "asdfasdfasdfasdf", @@ -41,7 +40,6 @@ func TestCreateSuccessful(t *testing.T) { "links": { "self": "https://localhost:5000/v3/endpoints/12" }, - "name": "the-endiest-of-points", "region": "underground", "service_id": "asdfasdfasdfasdf", "url": "https://1.2.3.4:9000/", @@ -53,7 +51,6 @@ func TestCreateSuccessful(t *testing.T) { enabled := false actual, err := endpoints.Create(context.TODO(), client.ServiceClient(fakeServer), endpoints.CreateOpts{ Availability: gophercloud.AvailabilityPublic, - Name: "the-endiest-of-points", Region: "underground", URL: "https://1.2.3.4:9000/", ServiceID: "asdfasdfasdfasdf", @@ -65,7 +62,6 @@ func TestCreateSuccessful(t *testing.T) { expected := &endpoints.Endpoint{ ID: "12", Availability: gophercloud.AvailabilityPublic, - Name: "the-endiest-of-points", Enabled: false, Region: "underground", ServiceID: "asdfasdfasdfasdf", From 57d6a1259926cfdcd374e0a027b02f6a6e7b4a94 Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Wed, 7 Jan 2026 20:47:05 -0300 Subject: [PATCH 2225/2296] endpoint: add missing pointer to description on update --- internal/acceptance/openstack/identity/v3/endpoint_test.go | 5 +++-- openstack/identity/v3/endpoints/requests.go | 2 +- openstack/identity/v3/endpoints/testing/requests_test.go | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/acceptance/openstack/identity/v3/endpoint_test.go b/internal/acceptance/openstack/identity/v3/endpoint_test.go index 7d9da99854..8140a7d4f9 100644 --- a/internal/acceptance/openstack/identity/v3/endpoint_test.go +++ b/internal/acceptance/openstack/identity/v3/endpoint_test.go @@ -129,14 +129,15 @@ func TestEndpointCRUD(t *testing.T) { tools.PrintResource(t, endpoint.URL) enabled = true + description := "" newEndpoint, err := endpoints.Update(context.TODO(), client, endpoint.ID, &endpoints.UpdateOpts{ Name: "new-endpoint", URL: "https://example-updated.com", - Description: "Updated Endpoint", + Description: &description, Enabled: &enabled, }).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, newEndpoint.URL, "https://example-updated.com") - th.AssertEquals(t, newEndpoint.Description, "Updated Endpoint") + th.AssertEquals(t, newEndpoint.Description, description) } diff --git a/openstack/identity/v3/endpoints/requests.go b/openstack/identity/v3/endpoints/requests.go index 8c08542848..a5dca90a9d 100644 --- a/openstack/identity/v3/endpoints/requests.go +++ b/openstack/identity/v3/endpoints/requests.go @@ -134,7 +134,7 @@ type UpdateOpts struct { ServiceID string `json:"service_id,omitempty"` // Description is an updated description of the endpoint. - Description string `json:"description,omitempty"` + Description *string `json:"description,omitempty"` } // ToEndpointUpdateMap builds an update request body from the Update options. diff --git a/openstack/identity/v3/endpoints/testing/requests_test.go b/openstack/identity/v3/endpoints/testing/requests_test.go index 8e50467e25..5ffde6638c 100644 --- a/openstack/identity/v3/endpoints/testing/requests_test.go +++ b/openstack/identity/v3/endpoints/testing/requests_test.go @@ -232,10 +232,11 @@ func TestUpdateEndpoint(t *testing.T) { }) enabled := false + description := "Changed description" actual, err := endpoints.Update(context.TODO(), client.ServiceClient(fakeServer), "12", endpoints.UpdateOpts{ Name: "renamed", Region: "somewhere-else", - Description: "Changed description", + Description: &description, Enabled: &enabled, }).Extract() if err != nil { From 4f71add6750071a34d76f489519b9886a8d1232a Mon Sep 17 00:00:00 2001 From: MahnoorAsghar Date: Thu, 8 Jan 2026 13:58:44 +0100 Subject: [PATCH 2226/2296] Add PCIAddress field to baremetal InterfaceType --- openstack/baremetal/inventory/types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/openstack/baremetal/inventory/types.go b/openstack/baremetal/inventory/types.go index 468f32a74f..98498d530d 100644 --- a/openstack/baremetal/inventory/types.go +++ b/openstack/baremetal/inventory/types.go @@ -24,6 +24,7 @@ type InterfaceType struct { Product string `json:"product"` SpeedMbps int `json:"speed_mbps"` Vendor string `json:"vendor"` + PCIAddress string `json:"pci_address"` } type MemoryType struct { From fd11834b3ee987379546a13f80458ffdb4bb13c2 Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Mon, 12 Jan 2026 17:24:03 +0000 Subject: [PATCH 2227/2296] networking/v2/layer3/routers: Add external gateways management Add support for managing external gateways on routers: * AddExternalGateways * UpdateExternalGateways * RemoveExternalGateways These operations extend router.external_gateway_info to support multiple external gateways on a single router. Ref: https://docs.openstack.org/api-ref/network/v2/index.html#add-external-gateways-to-router --- .../v2/extensions/layer3/routers_test.go | 91 ++++++++ .../v2/extensions/layer3/routers/doc.go | 68 ++++++ .../v2/extensions/layer3/routers/requests.go | 96 +++++++++ .../layer3/routers/testing/requests_test.go | 200 ++++++++++++++++++ .../v2/extensions/layer3/routers/urls.go | 12 ++ 5 files changed, 467 insertions(+) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 5930be2a1e..be275f2ce1 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -4,9 +4,11 @@ package layer3 import ( "context" + "net/http" "strings" "testing" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -281,3 +283,92 @@ func TestLayer3RouterRevision(t *testing.T) { th.AssertEquals(t, router.Name, newName) th.AssertEquals(t, router.Description, newDescription) } + +func TestLayer3RouterExternalGateways(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewNetworkV2Client() + th.AssertNoErr(t, err) + + // Skip if the external-gateway-multihoming extension is not available + networking.RequireNeutronExtension(t, client, "external-gateway-multihoming") + + // Create a router with external gateway + router, err := CreateExternalRouter(t, client) + th.AssertNoErr(t, err) + defer DeleteRouter(t, client, router.ID) + + tools.PrintResource(t, router) + + choices, err := clients.AcceptanceTestChoicesFromEnv() + th.AssertNoErr(t, err) + + // Test AddExternalGateways + t.Logf("Attempting to add external gateways to router %s", router.ID) + + addOpts := routers.AddExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: choices.ExternalNetworkID, + }, + }, + } + + updatedRouter, err := routers.AddExternalGateways(context.TODO(), client, router.ID, addOpts).Extract() + if err != nil { + // If we get a 404, the extension might not be fully functional + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { + t.Skipf("AddExternalGateways not supported: %v", err) + } + th.AssertNoErr(t, err) + } + + t.Logf("Successfully added external gateways to router %s", router.ID) + tools.PrintResource(t, updatedRouter) + + // Test UpdateExternalGateways + t.Logf("Attempting to update external gateways of router %s", router.ID) + + enableSNAT := true + updateOpts := routers.UpdateExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: choices.ExternalNetworkID, + EnableSNAT: &enableSNAT, + }, + }, + } + + updatedRouter, err = routers.UpdateExternalGateways(context.TODO(), client, router.ID, updateOpts).Extract() + if err != nil { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { + t.Skipf("UpdateExternalGateways not supported: %v", err) + } + th.AssertNoErr(t, err) + } + + t.Logf("Successfully updated external gateways of router %s", router.ID) + tools.PrintResource(t, updatedRouter) + + // Test RemoveExternalGateways + t.Logf("Attempting to remove external gateways from router %s", router.ID) + + removeOpts := routers.RemoveExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: choices.ExternalNetworkID, + }, + }, + } + + updatedRouter, err = routers.RemoveExternalGateways(context.TODO(), client, router.ID, removeOpts).Extract() + if err != nil { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { + t.Skipf("RemoveExternalGateways not supported: %v", err) + } + th.AssertNoErr(t, err) + } + + t.Logf("Successfully removed external gateways from router %s", router.ID) + tools.PrintResource(t, updatedRouter) +} diff --git a/openstack/networking/v2/extensions/layer3/routers/doc.go b/openstack/networking/v2/extensions/layer3/routers/doc.go index 42ccbcf7cf..4faa862fc1 100644 --- a/openstack/networking/v2/extensions/layer3/routers/doc.go +++ b/openstack/networking/v2/extensions/layer3/routers/doc.go @@ -135,5 +135,73 @@ Example to List an L3 agents for a Router for _, agent := range allL3Agents { fmt.Printf("%+v\n", agent) } + +Example to Add External Gateways to a Router + +This requires the external-gateway-multihoming extension. + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + addOpts := routers.AddExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + ExternalFixedIPs: []routers.ExternalFixedIP{ + {SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + }, + }, + }, + } + + router, err := routers.AddExternalGateways(context.TODO(), networkClient, routerID, addOpts).Extract() + if err != nil { + panic(err) + } + +Example to Update External Gateways of a Router + +This requires the external-gateway-multihoming extension. + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + enableSNAT := true + updateOpts := routers.UpdateExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + EnableSNAT: &enableSNAT, + ExternalFixedIPs: []routers.ExternalFixedIP{ + {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + }, + }, + }, + } + + router, err := routers.UpdateExternalGateways(context.TODO(), networkClient, routerID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Remove External Gateways from a Router + +This requires the external-gateway-multihoming extension. + + routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + + removeOpts := routers.RemoveExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + ExternalFixedIPs: []routers.ExternalFixedIP{ + {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + }, + }, + }, + } + + router, err := routers.RemoveExternalGateways(context.TODO(), networkClient, routerID, removeOpts).Extract() + if err != nil { + panic(err) + } */ package routers diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go index def4699db3..3e49a30f10 100644 --- a/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -282,3 +282,99 @@ func ListL3Agents(c *gophercloud.ServiceClient, id string) (result pagination.Pa return ListL3AgentsPage{pagination.SinglePageBase(r)} }) } + +// AddExternalGatewaysOptsBuilder allows extensions to add additional parameters +// to the AddExternalGateways request. +type AddExternalGatewaysOptsBuilder interface { + ToRouterAddExternalGatewaysMap() (map[string]any, error) +} + +// AddExternalGatewaysOpts represents the options for adding external gateways +// to a router. +type AddExternalGatewaysOpts struct { + ExternalGateways []GatewayInfo `json:"external_gateways" required:"true"` +} + +// ToRouterAddExternalGatewaysMap builds a request body from AddExternalGatewaysOpts. +func (opts AddExternalGatewaysOpts) ToRouterAddExternalGatewaysMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "router") +} + +// AddExternalGateways adds external gateways to a router. +// This requires the external-gateway-multihoming extension. +func AddExternalGateways(ctx context.Context, c *gophercloud.ServiceClient, id string, opts AddExternalGatewaysOptsBuilder) (r UpdateResult) { + b, err := opts.ToRouterAddExternalGatewaysMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(ctx, addExternalGatewaysURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateExternalGatewaysOptsBuilder allows extensions to add additional +// parameters to the UpdateExternalGateways request. +type UpdateExternalGatewaysOptsBuilder interface { + ToRouterUpdateExternalGatewaysMap() (map[string]any, error) +} + +// UpdateExternalGatewaysOpts represents the options for updating external +// gateways of a router. +type UpdateExternalGatewaysOpts struct { + ExternalGateways []GatewayInfo `json:"external_gateways" required:"true"` +} + +// ToRouterUpdateExternalGatewaysMap builds a request body from UpdateExternalGatewaysOpts. +func (opts UpdateExternalGatewaysOpts) ToRouterUpdateExternalGatewaysMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "router") +} + +// UpdateExternalGateways updates external gateways of a router. +// This requires the external-gateway-multihoming extension. +func UpdateExternalGateways(ctx context.Context, c *gophercloud.ServiceClient, id string, opts UpdateExternalGatewaysOptsBuilder) (r UpdateResult) { + b, err := opts.ToRouterUpdateExternalGatewaysMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(ctx, updateExternalGatewaysURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RemoveExternalGatewaysOptsBuilder allows extensions to add additional +// parameters to the RemoveExternalGateways request. +type RemoveExternalGatewaysOptsBuilder interface { + ToRouterRemoveExternalGatewaysMap() (map[string]any, error) +} + +// RemoveExternalGatewaysOpts represents the options for removing external +// gateways from a router. +type RemoveExternalGatewaysOpts struct { + ExternalGateways []GatewayInfo `json:"external_gateways" required:"true"` +} + +// ToRouterRemoveExternalGatewaysMap builds a request body from RemoveExternalGatewaysOpts. +func (opts RemoveExternalGatewaysOpts) ToRouterRemoveExternalGatewaysMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "router") +} + +// RemoveExternalGateways removes external gateways from a router. +// This requires the external-gateway-multihoming extension. +func RemoveExternalGateways(ctx context.Context, c *gophercloud.ServiceClient, id string, opts RemoveExternalGatewaysOptsBuilder) (r UpdateResult) { + b, err := opts.ToRouterRemoveExternalGatewaysMap() + if err != nil { + r.Err = err + return + } + resp, err := c.Put(ctx, removeExternalGatewaysURL(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/routers/testing/requests_test.go b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go index f6cd2c0bbd..1125aa12c4 100644 --- a/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go +++ b/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go @@ -697,3 +697,203 @@ func TestListL3Agents(t *testing.T) { } th.CheckDeepEquals(t, expected, actual) } + +func TestAddExternalGateways(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/add_external_gateways", 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": { + "external_gateways": [ + { + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "external_fixed_ips": [ + {"subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] + } + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ` +{ + "router": { + "status": "ACTIVE", + "external_gateway_info": { + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "external_fixed_ips": [ + {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] + }, + "name": "router1", + "admin_state_up": true, + "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", + "distributed": false, + "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + } +} + `) + }) + + efi := []routers.ExternalFixedIP{ + {SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + } + opts := routers.AddExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + ExternalFixedIPs: efi, + }, + }, + } + + n, err := routers.AddExternalGateways(context.TODO(), fake.ServiceClient(fakeServer), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Name, "router1") + th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c") + th.AssertEquals(t, n.GatewayInfo.NetworkID, "8ca37218-28ff-41cb-9b10-039601ea7e6b") +} + +func TestUpdateExternalGateways(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/update_external_gateways", 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": { + "external_gateways": [ + { + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "enable_snat": true, + "external_fixed_ips": [ + {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] + } + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ` +{ + "router": { + "status": "ACTIVE", + "external_gateway_info": { + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "enable_snat": true, + "external_fixed_ips": [ + {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] + }, + "name": "router1", + "admin_state_up": true, + "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", + "distributed": false, + "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + } +} + `) + }) + + enableSNAT := true + efi := []routers.ExternalFixedIP{ + {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + } + opts := routers.UpdateExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + EnableSNAT: &enableSNAT, + ExternalFixedIPs: efi, + }, + }, + } + + n, err := routers.UpdateExternalGateways(context.TODO(), fake.ServiceClient(fakeServer), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Name, "router1") + th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c") + th.AssertEquals(t, *n.GatewayInfo.EnableSNAT, true) +} + +func TestRemoveExternalGateways(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/remove_external_gateways", 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": { + "external_gateways": [ + { + "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", + "external_fixed_ips": [ + {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"} + ] + } + ] + } +} + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, ` +{ + "router": { + "status": "ACTIVE", + "external_gateway_info": null, + "name": "router1", + "admin_state_up": true, + "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3", + "distributed": false, + "id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + } +} + `) + }) + + efi := []routers.ExternalFixedIP{ + {IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}, + } + opts := routers.RemoveExternalGatewaysOpts{ + ExternalGateways: []routers.GatewayInfo{ + { + NetworkID: "8ca37218-28ff-41cb-9b10-039601ea7e6b", + ExternalFixedIPs: efi, + }, + }, + } + + n, err := routers.RemoveExternalGateways(context.TODO(), fake.ServiceClient(fakeServer), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, n.Name, "router1") + th.AssertEquals(t, n.ID, "4e8e5957-649f-477b-9e5b-f1f75b21c03c") + th.AssertEquals(t, n.GatewayInfo.NetworkID, "") +} diff --git a/openstack/networking/v2/extensions/layer3/routers/urls.go b/openstack/networking/v2/extensions/layer3/routers/urls.go index 87815aa6ec..b07c2fdc7b 100644 --- a/openstack/networking/v2/extensions/layer3/routers/urls.go +++ b/openstack/networking/v2/extensions/layer3/routers/urls.go @@ -23,3 +23,15 @@ func removeInterfaceURL(c *gophercloud.ServiceClient, id string) string { func listl3AgentsURL(c *gophercloud.ServiceClient, id string) string { return c.ServiceURL(resourcePath, id, "l3-agents") } + +func addExternalGatewaysURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "add_external_gateways") +} + +func updateExternalGatewaysURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "update_external_gateways") +} + +func removeExternalGatewaysURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id, "remove_external_gateways") +} From 94e6a57327ef32dbee0c76e868318ead19272c67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:10:22 +0000 Subject: [PATCH 2228/2296] build(deps): bump golang.org/x/crypto from 0.46.0 to 0.47.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.46.0 to 0.47.0. - [Commits](https://github.com/golang/crypto/compare/v0.46.0...v0.47.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.47.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 91c175c962..c83558414b 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.24.0 require ( - golang.org/x/crypto v0.46.0 + golang.org/x/crypto v0.47.0 gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/sys v0.39.0 // indirect +require golang.org/x/sys v0.40.0 // indirect diff --git a/go.sum b/go.sum index 03a132e602..eacdf99c42 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= -golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= 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 7e8c769e7044aafe3e09c7837c9895bfc98aa4be Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Fri, 16 Jan 2026 00:41:07 +0100 Subject: [PATCH 2229/2296] replace gopkg.in/yaml.v2 with go.yaml.in/yaml/v3 --- go.mod | 2 +- go.sum | 4 ++-- openstack/config/clouds/clouds.go | 2 +- openstack/container/v1/capsules/template.go | 2 +- openstack/orchestration/v1/stacks/environment.go | 2 +- openstack/orchestration/v1/stacks/template.go | 2 +- openstack/orchestration/v1/stacks/utils.go | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index c83558414b..e4dfa74850 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/gophercloud/gophercloud/v2 go 1.24.0 require ( + go.yaml.in/yaml/v3 v3.0.4 golang.org/x/crypto v0.47.0 - gopkg.in/yaml.v2 v2.4.0 ) require golang.org/x/sys v0.40.0 // indirect diff --git a/go.sum b/go.sum index eacdf99c42..aedfcaa0b6 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= @@ -6,5 +8,3 @@ golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= 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= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index eae6f38c8e..94c01645d9 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -28,7 +28,7 @@ import ( "reflect" "github.com/gophercloud/gophercloud/v2" - "gopkg.in/yaml.v2" + "go.yaml.in/yaml/v3" ) // Parse fetches a clouds.yaml file from disk and returns the parsed diff --git a/openstack/container/v1/capsules/template.go b/openstack/container/v1/capsules/template.go index b3b4446131..06df6ae938 100644 --- a/openstack/container/v1/capsules/template.go +++ b/openstack/container/v1/capsules/template.go @@ -3,7 +3,7 @@ package capsules import ( "encoding/json" - yaml "gopkg.in/yaml.v2" + yaml "go.yaml.in/yaml/v3" ) // Template is a structure that represents OpenStack Zun Capsule templates diff --git a/openstack/orchestration/v1/stacks/environment.go b/openstack/orchestration/v1/stacks/environment.go index bb9004a9f8..7efe29e5fd 100644 --- a/openstack/orchestration/v1/stacks/environment.go +++ b/openstack/orchestration/v1/stacks/environment.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - yaml "gopkg.in/yaml.v2" + yaml "go.yaml.in/yaml/v3" ) // Environment is a structure that represents stack environments diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index af49c612a0..021267f56f 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/gophercloud/gophercloud/v2" - yaml "gopkg.in/yaml.v2" + yaml "go.yaml.in/yaml/v3" ) // Template is a structure that represents OpenStack Heat templates diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go index 228335388f..36b2914a4f 100644 --- a/openstack/orchestration/v1/stacks/utils.go +++ b/openstack/orchestration/v1/stacks/utils.go @@ -9,7 +9,7 @@ import ( "reflect" "github.com/gophercloud/gophercloud/v2" - yaml "gopkg.in/yaml.v2" + yaml "go.yaml.in/yaml/v3" ) // Client is an interface that expects a Get method similar to http.Get. This From 7d61cd7d7c6c80939e61a27d3e72ac14c4c264bd Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Fri, 16 Jan 2026 15:34:54 +0100 Subject: [PATCH 2230/2296] orchestration: adjust template parsing logic for yaml/v3 - `map[any]any` is not unmarshaled anymore if all keys are strings, so type casting logic can be removed - version strings like `2014-10-16` are unmarshaled into `any` as `time.Time`, so for compatibility, this needs to be type-casted back into `string` manually --- .../v1/capsules/testing/fixtures_test.go | 12 +- .../orchestration/v1/stacks/environment.go | 121 ++++++++---------- openstack/orchestration/v1/stacks/template.go | 19 +-- openstack/orchestration/v1/stacks/utils.go | 38 +++--- .../orchestration/v1/stacks/utils_test.go | 15 --- 5 files changed, 73 insertions(+), 132 deletions(-) diff --git a/openstack/container/v1/capsules/testing/fixtures_test.go b/openstack/container/v1/capsules/testing/fixtures_test.go index e21b852569..00ef3404f6 100644 --- a/openstack/container/v1/capsules/testing/fixtures_test.go +++ b/openstack/container/v1/capsules/testing/fixtures_test.go @@ -144,9 +144,9 @@ var ValidYAMLTemplateParsed = map[string]any{ "app1": "web1", }, }, - "spec": map[any]any{ + "spec": map[string]any{ "restartPolicy": "Always", - "containers": []map[any]any{ + "containers": []map[string]any{ { "image": "ubuntu", "command": []any{ @@ -155,20 +155,20 @@ var ValidYAMLTemplateParsed = map[string]any{ "imagePullPolicy": "ifnotpresent", "workDir": "/root", "ports": []any{ - map[any]any{ + map[string]any{ "name": "nginx-port", "containerPort": 80, "hostPort": 80, "protocol": "TCP", }, }, - "resources": map[any]any{ - "requests": map[any]any{ + "resources": map[string]any{ + "requests": map[string]any{ "cpu": 1, "memory": 1024, }, }, - "env": map[any]any{ + "env": map[string]any{ "ENV1": "/usr/local/bin", "ENV2": "/usr/bin", }, diff --git a/openstack/orchestration/v1/stacks/environment.go b/openstack/orchestration/v1/stacks/environment.go index 7efe29e5fd..30fdcc00a6 100644 --- a/openstack/orchestration/v1/stacks/environment.go +++ b/openstack/orchestration/v1/stacks/environment.go @@ -46,87 +46,68 @@ func (e *Environment) getRRFileContents(ignoreIf igFunc) error { e.fileMaps = make(map[string]string) } - // get the resource registry - rr := e.Parsed["resource_registry"] + // get the resource registry, only process further if it is a map + rr, ok := e.Parsed["resource_registry"].(map[string]any) + if !ok { + return nil + } // search the resource registry for URLs - switch rr.(type) { - // process further only if the resource registry is a map - case map[string]any, map[any]any: - rrMap, err := toStringKeys(rr) - if err != nil { - return err - } - // the resource registry might contain a base URL for the resource. If - // such a field is present, use it. Otherwise, use the default base URL. - var baseURL string - if val, ok := rrMap["base_url"]; ok { - baseURL = val.(string) - } else { - baseURL = e.baseURL - } + // + // the resource registry might contain a base URL for the resource. If + // such a field is present, use it. Otherwise, use the default base URL. + var baseURL string + if val, ok := rr["base_url"]; ok { + baseURL = val.(string) + } else { + baseURL = e.baseURL + } - // The contents of the resource may be located in a remote file, which - // will be a template. Instantiate a temporary template to manage the - // contents. - tempTemplate := new(Template) - tempTemplate.baseURL = baseURL - tempTemplate.client = e.client + // The contents of the resource may be located in a remote file, which + // will be a template. Instantiate a temporary template to manage the + // contents. + tempTemplate := new(Template) + tempTemplate.baseURL = baseURL + tempTemplate.client = e.client - // Fetch the contents of remote resource URL's - if err = tempTemplate.getFileContents(rr, ignoreIf, false); err != nil { - return err - } - // check the `resources` section (if it exists) for more URL's. Note that - // the previous call to GetFileContents was (deliberately) not recursive - // as we want more control over where to look for URL's - if val, ok := rrMap["resources"]; ok { - switch val.(type) { - // process further only if the contents are a map - case map[string]any, map[any]any: - resourcesMap, err := toStringKeys(val) - if err != nil { - return err + // Fetch the contents of remote resource URL's + if err := tempTemplate.getFileContents(rr, ignoreIf, false); err != nil { + return err + } + // check the `resources` section (if it exists) for more URL's. Note that + // the previous call to GetFileContents was (deliberately) not recursive + // as we want more control over where to look for URL's + if resourcesMap, ok := rr["resources"].(map[string]any); ok { + for _, v := range resourcesMap { + if resourceMap, ok := v.(map[string]any); ok { + var resourceBaseURL string + // if base_url for the resource type is defined, use it + if val, ok := resourceMap["base_url"]; ok { + resourceBaseURL = val.(string) + } else { + resourceBaseURL = baseURL } - for _, v := range resourcesMap { - switch v.(type) { - case map[string]any, map[any]any: - resourceMap, err := toStringKeys(v) - if err != nil { - return err - } - var resourceBaseURL string - // if base_url for the resource type is defined, use it - if val, ok := resourceMap["base_url"]; ok { - resourceBaseURL = val.(string) - } else { - resourceBaseURL = baseURL - } - tempTemplate.baseURL = resourceBaseURL - if err := tempTemplate.getFileContents(v, ignoreIf, false); err != nil { - return err - } - } + tempTemplate.baseURL = resourceBaseURL + if err := tempTemplate.getFileContents(v, ignoreIf, false); err != nil { + return err } } } - // if the resource registry contained any URL's, store them. This can - // then be passed as parameter to api calls to Heat api. - e.Files = tempTemplate.Files + } + // if the resource registry contained any URL's, store them. This can + // then be passed as parameter to api calls to Heat api. + e.Files = tempTemplate.Files - // In case some element was updated, regenerate the string representation - if len(e.Files) > 0 { - var err error - e.Bin, err = yaml.Marshal(&e.Parsed) - if err != nil { - return fmt.Errorf("failed to marshal updated environment: %w", err) - } + // In case some element was updated, regenerate the string representation + if len(e.Files) > 0 { + var err error + e.Bin, err = yaml.Marshal(&e.Parsed) + if err != nil { + return fmt.Errorf("failed to marshal updated environment: %w", err) } - - return nil - default: - return nil } + + return nil } // function to choose keys whose values are other environment files diff --git a/openstack/orchestration/v1/stacks/template.go b/openstack/orchestration/v1/stacks/template.go index 021267f56f..0943aae978 100644 --- a/openstack/orchestration/v1/stacks/template.go +++ b/openstack/orchestration/v1/stacks/template.go @@ -84,12 +84,7 @@ func (t *Template) makeChildTemplate(childURL string, ignoreIf igFunc, recurse b // Applies the transformation for getFileContents() to just one element of a map. // In case the element requires transforming, the function returns its new value. -func (t *Template) mapElemFileContents(k any, v any, ignoreIf igFunc, recurse bool) (any, error) { - key, ok := k.(string) - if !ok { - return nil, fmt.Errorf("can't convert map key to string: %v", k) - } - +func (t *Template) mapElemFileContents(key string, v any, ignoreIf igFunc, recurse bool) (any, error) { value, ok := v.(string) if !ok { // if the value is not a string, recursively parse that value @@ -153,18 +148,6 @@ func (t *Template) getFileContents(te any, ignoreIf igFunc, recurse bool) error updated = true } } - // same if te is a map[non-string] (can't group with above case because we - // can't range over and update 'te' without knowing its key type) - case map[any]any: - for k, v := range teTyped { - newVal, err := t.mapElemFileContents(k, v, ignoreIf, recurse) - if err != nil { - return err - } else if newVal != nil { - teTyped[k] = newVal - updated = true - } - } // if te is a slice, call the function on each element of the slice. case []any: for i := range teTyped { diff --git a/openstack/orchestration/v1/stacks/utils.go b/openstack/orchestration/v1/stacks/utils.go index 36b2914a4f..bb936ae1b3 100644 --- a/openstack/orchestration/v1/stacks/utils.go +++ b/openstack/orchestration/v1/stacks/utils.go @@ -6,7 +6,7 @@ import ( "io" "net/http" "path/filepath" - "reflect" + "time" "github.com/gophercloud/gophercloud/v2" yaml "go.yaml.in/yaml/v3" @@ -113,10 +113,20 @@ func (t *TE) Parse() error { if err := t.Fetch(); err != nil { return err } - if jerr := json.Unmarshal(t.Bin, &t.Parsed); jerr != nil { - if yerr := yaml.Unmarshal(t.Bin, &t.Parsed); yerr != nil { - return ErrInvalidDataFormat{} - } + + // either parse as JSON... + if jerr := json.Unmarshal(t.Bin, &t.Parsed); jerr == nil { + return nil + } + + // ... or as YAML (but in this case, take extra care because yaml.Unmarshal() + // might incorrectly decode the `heat_template_version` attribute as a + // time.Time instead of as a string because it looks like "YYYY-MM-DD") + if yerr := yaml.Unmarshal(t.Bin, &t.Parsed); yerr != nil { + return ErrInvalidDataFormat{} + } + if versionAsTime, ok := t.Parsed["heat_template_version"].(time.Time); ok { + t.Parsed["heat_template_version"] = versionAsTime.Format(time.DateOnly) } return nil } @@ -124,21 +134,3 @@ func (t *TE) Parse() error { // igfunc is a parameter used by GetFileContents and GetRRFileContents to check // for valid URL's. type igFunc func(string, any) bool - -// convert map[any]any to map[string]any -func toStringKeys(m any) (map[string]any, error) { - switch m.(type) { - case map[string]any, map[any]any: - typedMap := make(map[string]any) - if _, ok := m.(map[any]any); ok { - for k, v := range m.(map[any]any) { - typedMap[k.(string)] = v - } - } else { - typedMap = m.(map[string]any) - } - return typedMap, nil - default: - return nil, gophercloud.ErrUnexpectedType{Expected: "map[string]any/map[any]any", Actual: fmt.Sprintf("%v", reflect.TypeOf(m))} - } -} diff --git a/openstack/orchestration/v1/stacks/utils_test.go b/openstack/orchestration/v1/stacks/utils_test.go index bb90993702..1daceefa93 100644 --- a/openstack/orchestration/v1/stacks/utils_test.go +++ b/openstack/orchestration/v1/stacks/utils_test.go @@ -10,21 +10,6 @@ import ( th "github.com/gophercloud/gophercloud/v2/testhelper" ) -func TestToStringKeys(t *testing.T) { - var test1 any = map[any]any{ - "Adam": "Smith", - "Isaac": "Newton", - } - result1, err := toStringKeys(test1) - th.AssertNoErr(t, err) - - expected := map[string]any{ - "Adam": "Smith", - "Isaac": "Newton", - } - th.AssertDeepEquals(t, result1, expected) -} - func TestGetBasePath(t *testing.T) { _, err := getBasePath() th.AssertNoErr(t, err) From ecbd02fc6f5abc0095284e693f3e6c27c2d81541 Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Sun, 18 Jan 2026 01:06:53 +0000 Subject: [PATCH 2231/2296] networking/v2/layer3/routers: Fix acceptance tests and linting --- .../v2/extensions/layer3/routers_test.go | 30 ++++++++++++++++--- .../v2/extensions/layer3/routers/doc.go | 12 ++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index be275f2ce1..9aefcb6630 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -327,14 +327,25 @@ func TestLayer3RouterExternalGateways(t *testing.T) { tools.PrintResource(t, updatedRouter) // Test UpdateExternalGateways + // Note: UpdateExternalGateways requires external_fixed_ips to identify which gateway to update t.Logf("Attempting to update external gateways of router %s", router.ID) - enableSNAT := true + // Get current external_fixed_ips from the router to identify the gateway + currentFixedIPs := make([]routers.ExternalFixedIP, len(updatedRouter.GatewayInfo.ExternalFixedIPs)) + for i, ip := range updatedRouter.GatewayInfo.ExternalFixedIPs { + currentFixedIPs[i] = routers.ExternalFixedIP{ + IPAddress: ip.IPAddress, + SubnetID: ip.SubnetID, + } + } + + enableSNAT := false updateOpts := routers.UpdateExternalGatewaysOpts{ ExternalGateways: []routers.GatewayInfo{ { - NetworkID: choices.ExternalNetworkID, - EnableSNAT: &enableSNAT, + NetworkID: updatedRouter.GatewayInfo.NetworkID, + EnableSNAT: &enableSNAT, + ExternalFixedIPs: currentFixedIPs, }, }, } @@ -351,12 +362,23 @@ func TestLayer3RouterExternalGateways(t *testing.T) { tools.PrintResource(t, updatedRouter) // Test RemoveExternalGateways + // Note: RemoveExternalGateways requires external_fixed_ips to identify which gateway to remove t.Logf("Attempting to remove external gateways from router %s", router.ID) + // Get current external_fixed_ips from the updated router + currentFixedIPs = make([]routers.ExternalFixedIP, len(updatedRouter.GatewayInfo.ExternalFixedIPs)) + for i, ip := range updatedRouter.GatewayInfo.ExternalFixedIPs { + currentFixedIPs[i] = routers.ExternalFixedIP{ + IPAddress: ip.IPAddress, + SubnetID: ip.SubnetID, + } + } + removeOpts := routers.RemoveExternalGatewaysOpts{ ExternalGateways: []routers.GatewayInfo{ { - NetworkID: choices.ExternalNetworkID, + NetworkID: updatedRouter.GatewayInfo.NetworkID, + ExternalFixedIPs: currentFixedIPs, }, }, } diff --git a/openstack/networking/v2/extensions/layer3/routers/doc.go b/openstack/networking/v2/extensions/layer3/routers/doc.go index 4faa862fc1..772c9e3bbf 100644 --- a/openstack/networking/v2/extensions/layer3/routers/doc.go +++ b/openstack/networking/v2/extensions/layer3/routers/doc.go @@ -136,9 +136,7 @@ Example to List an L3 agents for a Router fmt.Printf("%+v\n", agent) } -Example to Add External Gateways to a Router - -This requires the external-gateway-multihoming extension. +Example to Add External Gateways to a Router: This requires the external-gateway-multihoming extension. routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" @@ -158,9 +156,7 @@ This requires the external-gateway-multihoming extension. panic(err) } -Example to Update External Gateways of a Router - -This requires the external-gateway-multihoming extension. +Example to Update External Gateways of a Router: This requires the external-gateway-multihoming extension. routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" @@ -182,9 +178,7 @@ This requires the external-gateway-multihoming extension. panic(err) } -Example to Remove External Gateways from a Router - -This requires the external-gateway-multihoming extension. +Example to Remove External Gateways from a Router: This requires the external-gateway-multihoming extension. routerID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" From 9753503b19ba49bb0cc48e1add1b2a11422ebb1f Mon Sep 17 00:00:00 2001 From: Daniel Lawton Date: Thu, 22 Jan 2026 10:59:27 +0000 Subject: [PATCH 2232/2296] Networking V2: Added support for ML2 extension port_trusted_vif field. This allows users to create and update the 'trusted' status of a port via the ML2 extension. Includes: - Extension package definition - Request/Response structs (CreateOptsExt, UpdateOptsExt) - Unit tests and acceptance tests Signed-off-by: Daniel Lawton --- .github/workflows/functional-networking.yaml | 2 +- .../openstack/networking/v2/ports_test.go | 54 ++++ .../v2/extensions/portstrustedvif/doc.go | 74 ++++++ .../v2/extensions/portstrustedvif/requests.go | 53 ++++ .../v2/extensions/portstrustedvif/results.go | 6 + .../portstrustedvif/testing/fixtures_test.go | 161 ++++++++++++ .../portstrustedvif/testing/requests_test.go | 242 ++++++++++++++++++ 7 files changed, 591 insertions(+), 1 deletion(-) create mode 100644 openstack/networking/v2/extensions/portstrustedvif/doc.go create mode 100644 openstack/networking/v2/extensions/portstrustedvif/requests.go create mode 100644 openstack/networking/v2/extensions/portstrustedvif/results.go create mode 100644 openstack/networking/v2/extensions/portstrustedvif/testing/fixtures_test.go create mode 100644 openstack/networking/v2/extensions/portstrustedvif/testing/requests_test.go diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 8ab9d9798a..e7bf14d7ee 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -65,7 +65,7 @@ jobs: 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 }} enable_plugin networking-bgpvpn https://github.com/openstack/networking-bgpvpn.git ${{ matrix.openstack_version }} - Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords + Q_ML2_PLUGIN_EXT_DRIVERS=qos,port_security,dns_domain_keywords,port_trusted BGP_SCHEDULER_DRIVER=neutron_dynamic_routing.services.bgp.scheduler.bgp_dragent_scheduler.StaticScheduler [[post-config|\$NEUTRON_CONF]] diff --git a/internal/acceptance/openstack/networking/v2/ports_test.go b/internal/acceptance/openstack/networking/v2/ports_test.go index f6ff48e829..1506e4f8d6 100644 --- a/internal/acceptance/openstack/networking/v2/ports_test.go +++ b/internal/acceptance/openstack/networking/v2/ports_test.go @@ -13,6 +13,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/extradhcpopts" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsecurity" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portstrustedvif" "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -467,6 +468,59 @@ func TestPortsWithExtraDHCPOptsCRUD(t *testing.T) { tools.PrintResource(t, newPort) } +func TestPortsTrustedVIFCRUD(t *testing.T) { + client, err := clients.NewNetworkV2Client() + if err != nil { + t.Fatalf("Unable to create a network client: %v", err) + } + + // Create Network + network, err := CreateNetwork(t, client) + if err != nil { + t.Fatalf("Unable to create network: %v", err) + } + defer DeleteNetwork(t, client, network.ID) + + // Create Subnet + subnet, err := CreateSubnet(t, client, network.ID) + if err != nil { + t.Fatalf("Unable to create subnet: %v", err) + } + defer DeleteSubnet(t, client, subnet.ID) + + // Create port + port, err := CreatePort(t, client, network.ID, subnet.ID) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + defer DeletePort(t, client, port.ID) + + var portWithExt struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + + err = ports.Get(context.TODO(), client, port.ID).ExtractInto(&portWithExt) + if err != nil { + t.Fatalf("Unable to create port: %v", err) + } + + tools.PrintResource(t, portWithExt) + + // Update Port Trusted VIF status as true + iTrue := true + portUpdateOpts := ports.UpdateOpts{} + updateOpts := portstrustedvif.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + PortTrustedVIF: &iTrue, + } + + err = ports.Update(context.TODO(), client, port.ID, updateOpts).ExtractInto(&portWithExt) + if err != nil { + t.Fatalf("Unable to update port: %v", err) + } +} + func TestPortsRevision(t *testing.T) { client, err := clients.NewNetworkV2Client() th.AssertNoErr(t, err) diff --git a/openstack/networking/v2/extensions/portstrustedvif/doc.go b/openstack/networking/v2/extensions/portstrustedvif/doc.go new file mode 100644 index 0000000000..9efedf04ad --- /dev/null +++ b/openstack/networking/v2/extensions/portstrustedvif/doc.go @@ -0,0 +1,74 @@ +/* +Package portstrustedvif provides information and interaction with the port +trusted vif extension which adds trusted attributes to the port resource +for the OpenStack Networking service. + +Example to Get a Port with a Port Trusted VIF + + var portWithPortTrustedVIFExtension struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + + portID := "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2" + + err := ports.Get(context.TODO(), networkingClient, portID).ExtractInto(&portWithPortTrustedVIFExtension) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", portWithPortTrustedVIFExtension) + +Example to Create a Port With Port Trusted VIF set as true + + var portWithPortTrustedVIFExtension struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + + iTrue := true + networkID := "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + subnetID := "a87cc70a-3e15-4acf-8205-9b711a3531b7" + + portCreateOpts := ports.CreateOpts{ + NetworkID: networkID, + FixedIPs: []ports.IP{ports.IP{SubnetID: subnetID}}, + } + + createOpts := portstrustedvif.PortCreateOptsExt{ + CreateOptsBuilder: portCreateOpts, + PortTrustedVIF: &iTrue, + } + + err := ports.Create(context.TODO(), networkingClient, createOpts).ExtractInto(&portWithPortTrustedVIFExtension) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", portWithPortTrustedVIFExtension) + +Example to Update the status of the Port Trusted VIF to false on an Existing Port + + var portWithPortTrustedVIDExtension struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + + iFalse := false + portID := "65c0ee9f-d634-4522-8954-51021b570b0d" + + portUpdateOpts := ports.UpdateOpts{} + updateOpts := portstrustedvif.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + PortTrustedVIF: &iFalse, + } + + err := ports.Update(context.TODO(), networkingClient, portID, updateOpts).ExtractInto(&portWithPortTrustedVIFExtension) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", portWithPortTrustedVIFExtension) +*/ + +package portstrustedvif diff --git a/openstack/networking/v2/extensions/portstrustedvif/requests.go b/openstack/networking/v2/extensions/portstrustedvif/requests.go new file mode 100644 index 0000000000..c4166ce1bd --- /dev/null +++ b/openstack/networking/v2/extensions/portstrustedvif/requests.go @@ -0,0 +1,53 @@ +package portstrustedvif + +import ( + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" +) + +// PortCreateOptsExt adds port trusted VIF options to the base ports.CreateOpts. +type PortCreateOptsExt struct { + ports.CreateOptsBuilder + + // PortTrustedVIF toggles the port's trusted VIF status. + PortTrustedVIF *bool `json:"trusted,omitempty"` +} + +// To PortCreateMap casts a CreateOpts struct to a map +func (opts PortCreateOptsExt) ToPortCreateMap() (map[string]any, error) { + base, err := opts.CreateOptsBuilder.ToPortCreateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]any) + + if opts.PortTrustedVIF != nil { + port["trusted"] = *opts.PortTrustedVIF + } + + return base, nil +} + +// PortUpdateOptsExt adds port trusted VIF options to the base ports.UpdateOpts. +type PortUpdateOptsExt struct { + ports.UpdateOptsBuilder + + // PortTrustedVIF updates the port's trusted VIF status. + PortTrustedVIF *bool `json:"trusted,omitempty"` +} + +// ToPortUpdateMap casts a UpdateOpts struct to a map. +func (opts PortUpdateOptsExt) ToPortUpdateMap() (map[string]any, error) { + base, err := opts.UpdateOptsBuilder.ToPortUpdateMap() + if err != nil { + return nil, err + } + + port := base["port"].(map[string]any) + + if opts.PortTrustedVIF != nil { + port["trusted"] = *opts.PortTrustedVIF + } + + return base, nil +} diff --git a/openstack/networking/v2/extensions/portstrustedvif/results.go b/openstack/networking/v2/extensions/portstrustedvif/results.go new file mode 100644 index 0000000000..39cfeacfaa --- /dev/null +++ b/openstack/networking/v2/extensions/portstrustedvif/results.go @@ -0,0 +1,6 @@ +package portstrustedvif + +type PortTrustedVIFExt struct { + // PortTrustedVIF stores information about whether a SR-IOV port should be trusted + PortTrustedVIF *bool `json:"trusted"` +} diff --git a/openstack/networking/v2/extensions/portstrustedvif/testing/fixtures_test.go b/openstack/networking/v2/extensions/portstrustedvif/testing/fixtures_test.go new file mode 100644 index 0000000000..0a9df88bbe --- /dev/null +++ b/openstack/networking/v2/extensions/portstrustedvif/testing/fixtures_test.go @@ -0,0 +1,161 @@ +package testing + +const ListResponse = ` +{ + "ports": [ + { + "status": "ACTIVE", + "binding:host_id": "devstack", + "name": "", + "admin_state_up": true, + "network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3", + "tenant_id": "", + "device_owner": "network:router_gateway", + "mac_address": "fa:16:3e:58:42:ed", + "binding:vnic_type": "normal", + "fixed_ips": [ + { + "subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062", + "ip_address": "172.24.4.2" + } + ], + "id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b", + "security_groups": [], + "dns_name": "test-port", + "dns_assignment": [ + { + "hostname": "test-port", + "ip_address": "172.24.4.2", + "fqdn": "test-port.openstack.local." + } + ], + "device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824", + "port_security_enabled": false, + "trusted": true, + "created_at": "2019-06-30T04:15:37", + "updated_at": "2019-06-30T05:18:49" + } + ] +} +` + +const GetPortTrustedVIFStatusResponse = ` +{ + "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.1" + } + ], + "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", + "trusted": false, + "device_id": "" + } +} +` +const GetPortTrustedVIFUnsetResponse = ` +{ + "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.1" + } + ], + "id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", + "device_id": "" + } +} +` + +const CreatePortTrustedVIFRequest = ` +{ + "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" + } + ], + "trusted": true + } +}` + +const CreatePortTrustedVIFResponse = ` +{ + "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", + "trusted": true, + "device_id": "" + } +}` + +const UpdatePortTrustedVIFRequest = ` +{ + "port": { + "trusted": false + } +}` + +const UpdatePortTrustedVIFResponse = ` +{ + "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.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" + ], + "trusted": false, + "device_id": "" + } +} +` diff --git a/openstack/networking/v2/extensions/portstrustedvif/testing/requests_test.go b/openstack/networking/v2/extensions/portstrustedvif/testing/requests_test.go new file mode 100644 index 0000000000..e768dda87b --- /dev/null +++ b/openstack/networking/v2/extensions/portstrustedvif/testing/requests_test.go @@ -0,0 +1,242 @@ +package testing + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + fake "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/common" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portstrustedvif" + "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestList(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/ports", 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, ListResponse) + }) + + type PortWithExt struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + var actual []PortWithExt + + iTrue := true + expected := []PortWithExt{ + { + Port: ports.Port{ + Status: "ACTIVE", + Name: "", + AdminStateUp: true, + NetworkID: "70c1db1f-b701-45bd-96e0-a313ee3430b3", + TenantID: "", + DeviceOwner: "network:router_gateway", + MACAddress: "fa:16:3e:58:42:ed", + FixedIPs: []ports.IP{ + { + SubnetID: "008ba151-0b8c-4a67-98b5-0d2b87666062", + IPAddress: "172.24.4.2", + }, + }, + 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), + }, + PortTrustedVIFExt: portstrustedvif.PortTrustedVIFExt{ + PortTrustedVIF: &iTrue, + }, + }, + } + + allPages, err := ports.List(fake.ServiceClient(fakeServer), ports.ListOpts{}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + err = ports.ExtractPortsInto(allPages, &actual) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expected, actual) +} + +func TestGet(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", 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, GetPortTrustedVIFStatusResponse) + }) + + var s struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + + err := ports.Get(context.TODO(), fake.ServiceClient(fakeServer), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, "DOWN") + th.AssertEquals(t, s.Name, "private-port") + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, s.DeviceOwner, "") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"}, + }) + th.AssertEquals(t, s.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2") + th.AssertEquals(t, s.DeviceID, "") + + if s.PortTrustedVIF == nil { + t.Fatalf("Expected s.PortTrustedVIF to be not nil") + } + th.AssertEquals(t, *s.PortTrustedVIF, false) +} + +func TestGetUnset(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", 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, GetPortTrustedVIFUnsetResponse) + }) + + var s struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + + err := ports.Get(context.TODO(), fake.ServiceClient(fakeServer), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s) + th.AssertNoErr(t, err) + + th.AssertEquals(t, s.Status, "DOWN") + th.AssertEquals(t, s.Name, "private-port") + th.AssertEquals(t, s.AdminStateUp, true) + th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, s.DeviceOwner, "") + th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"}, + }) + th.AssertEquals(t, s.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2") + th.AssertEquals(t, s.DeviceID, "") + + if s.PortTrustedVIF != nil { + t.Fatalf("Expected s.PortTrustedVIF to be nil") + } +} + +func TestCreateWithPortTrustedVIF(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.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, CreatePortTrustedVIFRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprint(w, CreatePortTrustedVIFResponse) + }) + + var portWithExt struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + + asu := true + portTrustedVIFStatus := 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"}, + }, + } + createOpts := portstrustedvif.PortCreateOptsExt{ + CreateOptsBuilder: options, + PortTrustedVIF: &portTrustedVIFStatus, + } + err := ports.Create(context.TODO(), fake.ServiceClient(fakeServer), createOpts).ExtractInto(&portWithExt) + th.AssertNoErr(t, err) + + th.AssertEquals(t, portWithExt.Status, "DOWN") + th.AssertEquals(t, portWithExt.Name, "private-port") + th.AssertEquals(t, portWithExt.AdminStateUp, true) + th.AssertEquals(t, portWithExt.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7") + th.AssertEquals(t, portWithExt.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa") + th.AssertEquals(t, portWithExt.DeviceOwner, "") + th.AssertEquals(t, portWithExt.MACAddress, "fa:16:3e:c9:cb:f0") + th.AssertDeepEquals(t, portWithExt.FixedIPs, []ports.IP{ + {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"}, + }) + th.AssertEquals(t, portWithExt.ID, "65c0ee9f-d634-4522-8954-51021b570b0d") + th.AssertEquals(t, *portWithExt.PortTrustedVIF, true) +} + +func TestUpdatePortTrustedVIF(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + fakeServer.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, UpdatePortTrustedVIFRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, UpdatePortTrustedVIFResponse) + }) + + var portWithExt struct { + ports.Port + portstrustedvif.PortTrustedVIFExt + } + + portTrustedVIFStatus := false + portUpdateOpts := ports.UpdateOpts{} + updateOpts := portstrustedvif.PortUpdateOptsExt{ + UpdateOptsBuilder: portUpdateOpts, + PortTrustedVIF: &portTrustedVIFStatus, + } + + err := ports.Update(context.TODO(), fake.ServiceClient(fakeServer), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithExt) + th.AssertNoErr(t, err) + + th.AssertEquals(t, portWithExt.Name, "private-port") + th.AssertDeepEquals(t, *portWithExt.PortTrustedVIF, false) +} From cab952c98de413faea477c945f774fca75cf9308 Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Thu, 22 Jan 2026 19:19:54 +0000 Subject: [PATCH 2233/2296] networking/v2/layer3/routers: Remove 404 failuer skipping --- .../v2/extensions/layer3/routers_test.go | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go index 9aefcb6630..e6519472c6 100644 --- a/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go +++ b/internal/acceptance/openstack/networking/v2/extensions/layer3/routers_test.go @@ -4,11 +4,9 @@ package layer3 import ( "context" - "net/http" "strings" "testing" - "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" networking "github.com/gophercloud/gophercloud/v2/internal/acceptance/openstack/networking/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" @@ -315,13 +313,7 @@ func TestLayer3RouterExternalGateways(t *testing.T) { } updatedRouter, err := routers.AddExternalGateways(context.TODO(), client, router.ID, addOpts).Extract() - if err != nil { - // If we get a 404, the extension might not be fully functional - if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { - t.Skipf("AddExternalGateways not supported: %v", err) - } - th.AssertNoErr(t, err) - } + th.AssertNoErr(t, err) t.Logf("Successfully added external gateways to router %s", router.ID) tools.PrintResource(t, updatedRouter) @@ -351,12 +343,7 @@ func TestLayer3RouterExternalGateways(t *testing.T) { } updatedRouter, err = routers.UpdateExternalGateways(context.TODO(), client, router.ID, updateOpts).Extract() - if err != nil { - if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { - t.Skipf("UpdateExternalGateways not supported: %v", err) - } - th.AssertNoErr(t, err) - } + th.AssertNoErr(t, err) t.Logf("Successfully updated external gateways of router %s", router.ID) tools.PrintResource(t, updatedRouter) @@ -384,12 +371,7 @@ func TestLayer3RouterExternalGateways(t *testing.T) { } updatedRouter, err = routers.RemoveExternalGateways(context.TODO(), client, router.ID, removeOpts).Extract() - if err != nil { - if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { - t.Skipf("RemoveExternalGateways not supported: %v", err) - } - th.AssertNoErr(t, err) - } + th.AssertNoErr(t, err) t.Logf("Successfully removed external gateways from router %s", router.ID) tools.PrintResource(t, updatedRouter) From 905410a3fcb6f67fda77212ca393325b3c71757b Mon Sep 17 00:00:00 2001 From: Anthony ROUSSEL Date: Thu, 22 Jan 2026 22:57:57 +0100 Subject: [PATCH 2234/2296] gomod: bump golang to 1.25.6 --- .github/workflows/label-pr.yaml | 3 ++- .github/workflows/lint.yaml | 3 ++- go.mod | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index 254af8c502..423ea1fdc6 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -26,7 +26,8 @@ jobs: - uses: actions/setup-go@v6 with: - go-version: '1' + go-version-file: 'go.mod' + cache: true - name: Checking Go API Compatibility id: go-apidiff diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 3440a1e0c3..f2e4fafa2b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -13,7 +13,8 @@ jobs: uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: - go-version: '1' + go-version-file: 'go.mod' + cache: true - name: Run linters run: | make lint diff --git a/go.mod b/go.mod index e4dfa74850..3362dec47e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gophercloud/gophercloud/v2 -go 1.24.0 +go 1.25.6 require ( go.yaml.in/yaml/v3 v3.0.4 From d2dde1024c194c9f3f78e336dfe09b8e49c8bbc2 Mon Sep 17 00:00:00 2001 From: eshulman2 Date: Mon, 2 Feb 2026 16:03:09 +0200 Subject: [PATCH 2235/2296] Use jimmy amphora in octavia job To avoid kvm error in nested virtualization use older amphora image --- .github/workflows/functional-loadbalancer.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index fc18748982..2afdb83f90 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -14,6 +14,8 @@ jobs: openstack_version: "master" ubuntu_version: "24.04" devstack_conf_overrides: | + LIBVIRT_CPU_MODE=host-passthrough + OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID=jammy # ensure we're using a working version of setuptools if [ -n "\$TOP_DIR" ]; then sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python From 67428efe77972332b507f68cb53ae7b0094d2ec8 Mon Sep 17 00:00:00 2001 From: Jacob Anders Date: Wed, 28 Jan 2026 20:07:17 +1000 Subject: [PATCH 2236/2296] baremetal: Add Health field to Node Add support for the node health field which reports hardware health status from the BMC. Requires Ironic API microversion 1.109. Partially implements metal3-io#2888 Signed-off-by: Jacob Anders Assisted-by: Claude-4.5-opus-high Co-authored-by: Cursor --- openstack/baremetal/v1/nodes/requests.go | 4 ++++ openstack/baremetal/v1/nodes/results.go | 5 +++++ openstack/baremetal/v1/nodes/testing/fixtures_test.go | 3 +++ 3 files changed, 12 insertions(+) diff --git a/openstack/baremetal/v1/nodes/requests.go b/openstack/baremetal/v1/nodes/requests.go index 6268f5037e..89ba6a5b16 100644 --- a/openstack/baremetal/v1/nodes/requests.go +++ b/openstack/baremetal/v1/nodes/requests.go @@ -113,6 +113,10 @@ type ListOpts struct { // Filter the list with the specified fault. Fault string `q:"fault"` + // Filter the list with the specified health status. + // Requires microversion 1.109 or later. + Health string `q:"health"` + // One or more fields to be returned in the response. Fields []string `q:"fields" format:"comma-separated"` diff --git a/openstack/baremetal/v1/nodes/results.go b/openstack/baremetal/v1/nodes/results.go index 06ab429110..e2c5947b7b 100644 --- a/openstack/baremetal/v1/nodes/results.go +++ b/openstack/baremetal/v1/nodes/results.go @@ -139,6 +139,11 @@ type Node struct { // node. There are other possible types, e.g., “clean failure” and “rescue abort failure”. Fault string `json:"fault"` + // Health indicates the hardware health status reported by the BMC. + // Possible values are "OK", "Warning", "Critical", or empty if not available. + // This field is read-only and requires microversion 1.109 or later. + Health string `json:"health"` + // Error from the most recent (last) transaction that started but failed to finish. LastError string `json:"last_error"` diff --git a/openstack/baremetal/v1/nodes/testing/fixtures_test.go b/openstack/baremetal/v1/nodes/testing/fixtures_test.go index 064351ea87..84a6cccb96 100644 --- a/openstack/baremetal/v1/nodes/testing/fixtures_test.go +++ b/openstack/baremetal/v1/nodes/testing/fixtures_test.go @@ -102,6 +102,7 @@ const NodeListDetailBody = ` "extra": {}, "fault": null, "firmware_interface": "no-firmware", + "health": "OK", "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, @@ -417,6 +418,7 @@ const SingleNodeBody = ` "extra": {}, "fault": null, "firmware_interface": "no-firmware", + "health": "OK", "inspect_interface": "no-inspect", "inspection_finished_at": null, "inspection_started_at": null, @@ -939,6 +941,7 @@ var ( Maintenance: false, MaintenanceReason: "", Fault: "", + Health: "OK", LastError: "", Reservation: "", Driver: "ipmi", From cafe1ac98cd78f0e528ac08a567afd16fd1e295f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 09:03:00 +0000 Subject: [PATCH 2237/2296] build(deps): bump golang.org/x/crypto from 0.47.0 to 0.48.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.47.0 to 0.48.0. - [Commits](https://github.com/golang/crypto/compare/v0.47.0...v0.48.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.48.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 3362dec47e..2fce7628d9 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.6 require ( go.yaml.in/yaml/v3 v3.0.4 - golang.org/x/crypto v0.47.0 + golang.org/x/crypto v0.48.0 ) -require golang.org/x/sys v0.40.0 // indirect +require golang.org/x/sys v0.41.0 // indirect diff --git a/go.sum b/go.sum index aedfcaa0b6..457e8ed0f8 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= 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= From f0d41456842f5a440962793826787c575f2bf3e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 12 Feb 2026 15:03:38 +0100 Subject: [PATCH 2238/2296] CI: Fix fwaas jobs They were configured to run the whole networking test suite without devstack being configured for it. Furthermore, it's redundant with the networking job. Restore the job to only run the fwaas_v2 tests as it was previous to commit 90cded10439a88581c9cd5afbbca206bc20c3230. --- .github/workflows/functional-fwaas_v2.yaml | 2 +- Makefile | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 50908995bf..be6211ca78 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -89,7 +89,7 @@ jobs: if: ${{ fromJSON(steps.changed-files.outputs.matches) }} run: | source ${{ github.workspace }}/script/stackenv - make acceptance-networking + make acceptance-fwaas echo "TESTS_RUN=true" >> $GITHUB_ENV env: DEVSTACK_PATH: ${{ github.workspace }}/devstack diff --git a/Makefile b/Makefile index e5b77e0369..79411c0026 100644 --- a/Makefile +++ b/Makefile @@ -78,6 +78,10 @@ acceptance-dns: $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/dns/... .PHONY: acceptance-dns +acceptance-fwaas: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/networking/v2/extensions/fwaas_v2/... +.PHONY: acceptance-fwaas + acceptance-identity: $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/identity/... .PHONY: acceptance-identity From 53881b95501136f55232949581b0a4c2342771e8 Mon Sep 17 00:00:00 2001 From: Omer Date: Mon, 1 Dec 2025 14:10:50 +0200 Subject: [PATCH 2239/2296] Add TSIG key support for OpenStack DNS v2 API Implement TSIG (Transaction SIGnature) key management for the Designate DNS service, enabling authentication of DNS transactions between servers for zone transfers and dynamic updates. Fixes #3622 Signed-off-by: Omer --- internal/acceptance/openstack/dns/v2/dns.go | 46 ++++ .../openstack/dns/v2/tsigkeys_test.go | 56 +++++ openstack/dns/v2/tsigkeys/doc.go | 72 ++++++ openstack/dns/v2/tsigkeys/requests.go | 158 ++++++++++++ openstack/dns/v2/tsigkeys/results.go | 119 ++++++++++ openstack/dns/v2/tsigkeys/testing/doc.go | 2 + .../dns/v2/tsigkeys/testing/fixtures_test.go | 224 ++++++++++++++++++ .../dns/v2/tsigkeys/testing/requests_test.go | 98 ++++++++ openstack/dns/v2/tsigkeys/urls.go | 13 + 9 files changed, 788 insertions(+) create mode 100644 internal/acceptance/openstack/dns/v2/tsigkeys_test.go create mode 100644 openstack/dns/v2/tsigkeys/doc.go create mode 100644 openstack/dns/v2/tsigkeys/requests.go create mode 100644 openstack/dns/v2/tsigkeys/results.go create mode 100644 openstack/dns/v2/tsigkeys/testing/doc.go create mode 100644 openstack/dns/v2/tsigkeys/testing/fixtures_test.go create mode 100644 openstack/dns/v2/tsigkeys/testing/requests_test.go create mode 100644 openstack/dns/v2/tsigkeys/urls.go diff --git a/internal/acceptance/openstack/dns/v2/dns.go b/internal/acceptance/openstack/dns/v2/dns.go index 9adb61882d..596ce75431 100644 --- a/internal/acceptance/openstack/dns/v2/dns.go +++ b/internal/acceptance/openstack/dns/v2/dns.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/recordsets" transferAccepts "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/accept" transferRequests "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/transfer/request" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/tsigkeys" "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/zones" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -308,3 +309,48 @@ func WaitForZoneStatus(client *gophercloud.ServiceClient, zone *zones.Zone, stat return false, nil }) } + +// CreateTSIGKey will create a TSIG key with a random name. An error will +// be returned if the TSIG key was unable to be created. +func CreateTSIGKey(t *testing.T, client *gophercloud.ServiceClient) (*tsigkeys.TSIGKey, error) { + keyName := tools.RandomString("ACPTTEST", 8) + + t.Logf("Attempting to create TSIG key: %s", keyName) + createOpts := tsigkeys.CreateOpts{ + Name: keyName, + Algorithm: "hmac-sha256", + Secret: "example-test-secret-key==", + Scope: "POOL", + // Default pool ID from designate/conf/central.py + ResourceID: "794ccc2c-d751-44fe-b57f-8894c9f5c842", + } + + tsigkey, err := tsigkeys.Create(context.TODO(), client, createOpts).Extract() + if err != nil { + return tsigkey, err + } + + newTSIGKey, err := tsigkeys.Get(context.TODO(), client, tsigkey.ID).Extract() + if err != nil { + return tsigkey, err + } + + t.Logf("Created TSIG key: %s", keyName) + + th.AssertEquals(t, newTSIGKey.Name, keyName) + th.AssertEquals(t, newTSIGKey.Algorithm, "hmac-sha256") + + return newTSIGKey, nil +} + +// DeleteTSIGKey will delete a specified TSIG key. A fatal error will occur if +// the TSIG key failed to be deleted. This works best when used as a deferred +// function. +func DeleteTSIGKey(t *testing.T, client *gophercloud.ServiceClient, tsigkey *tsigkeys.TSIGKey) { + err := tsigkeys.Delete(context.TODO(), client, tsigkey.ID).ExtractErr() + if err != nil { + t.Fatalf("Unable to delete TSIG key %s: %v", tsigkey.ID, err) + } + + t.Logf("Deleted TSIG key: %s", tsigkey.ID) +} diff --git a/internal/acceptance/openstack/dns/v2/tsigkeys_test.go b/internal/acceptance/openstack/dns/v2/tsigkeys_test.go new file mode 100644 index 0000000000..e07b695be2 --- /dev/null +++ b/internal/acceptance/openstack/dns/v2/tsigkeys_test.go @@ -0,0 +1,56 @@ +//go:build acceptance || dns || tsigkeys + +package v2 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/tsigkeys" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestTSIGKeysCRUD(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewDNSV2Client() + th.AssertNoErr(t, err) + + tsigkey, err := CreateTSIGKey(t, client) + th.AssertNoErr(t, err) + defer DeleteTSIGKey(t, client, tsigkey) + + tools.PrintResource(t, &tsigkey) + + allPages, err := tsigkeys.List(client, nil).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allTSIGKeys, err := tsigkeys.ExtractTSIGKeys(allPages) + th.AssertNoErr(t, err) + + var found bool + for _, k := range allTSIGKeys { + tools.PrintResource(t, &k) + + if tsigkey.Name == k.Name { + found = true + } + } + + th.AssertEquals(t, found, true) + + updateOpts := tsigkeys.UpdateOpts{ + Name: tsigkey.Name + "-updated", + Secret: "updated-test-secret-key==", + } + + newTSIGKey, err := tsigkeys.Update(context.TODO(), client, tsigkey.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, &newTSIGKey) + + th.AssertEquals(t, newTSIGKey.Name, tsigkey.Name+"-updated") + th.AssertEquals(t, newTSIGKey.Secret, "updated-test-secret-key==") +} diff --git a/openstack/dns/v2/tsigkeys/doc.go b/openstack/dns/v2/tsigkeys/doc.go new file mode 100644 index 0000000000..7e99c6dbe4 --- /dev/null +++ b/openstack/dns/v2/tsigkeys/doc.go @@ -0,0 +1,72 @@ +/* +Package tsigkeys provides information and interaction with the TSIG key API +resource for the OpenStack DNS service. + +TSIG (Transaction SIGnature) keys are used to authenticate DNS transactions +between servers, such as zone transfers and dynamic updates. + +Example to List TSIG Keys + + listOpts := tsigkeys.ListOpts{ + Scope: "POOL", + } + + allPages, err := tsigkeys.List(dnsClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allTSIGKeys, err := tsigkeys.ExtractTSIGKeys(allPages) + if err != nil { + panic(err) + } + + for _, tsigkey := range allTSIGKeys { + fmt.Printf("%+v\n", tsigkey) + } + +Example to Create a TSIG Key + + createOpts := tsigkeys.CreateOpts{ + Name: "mytsigkey", + Algorithm: "hmac-sha256", + Secret: "example-secret-key-value==", + Scope: "POOL", + ResourceID: "pool-id-here", + } + + tsigkey, err := tsigkeys.Create(context.TODO(), dnsClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Get a TSIG Key + + tsigkeyID := "99d10f68-5623-4491-91a0-6daafa32b60e" + tsigkey, err := tsigkeys.Get(context.TODO(), dnsClient, tsigkeyID).Extract() + if err != nil { + panic(err) + } + +Example to Update a TSIG Key + + tsigkeyID := "99d10f68-5623-4491-91a0-6daafa32b60e" + updateOpts := tsigkeys.UpdateOpts{ + Name: "updatedname", + Secret: "updated-secret-key-value==", + } + + tsigkey, err := tsigkeys.Update(context.TODO(), dnsClient, tsigkeyID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a TSIG Key + + tsigkeyID := "99d10f68-5623-4491-91a0-6daafa32b60e" + err := tsigkeys.Delete(context.TODO(), dnsClient, tsigkeyID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package tsigkeys diff --git a/openstack/dns/v2/tsigkeys/requests.go b/openstack/dns/v2/tsigkeys/requests.go new file mode 100644 index 0000000000..2f8b0a4aa5 --- /dev/null +++ b/openstack/dns/v2/tsigkeys/requests.go @@ -0,0 +1,158 @@ +package tsigkeys + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add parameters to the List request. +type ListOptsBuilder interface { + ToTSIGKeyListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the server attributes you want to see returned. Marker and Limit are used +// for pagination. +// https://docs.openstack.org/api-ref/dns/ +type ListOpts struct { + // Integer value for the limit of values to return. + Limit int `q:"limit"` + + // UUID of the TSIG key at which you want to set a marker. + Marker string `q:"marker"` + + // Name of the TSIG key. + Name string `q:"name"` + + // Algorithm used by the TSIG key. + Algorithm string `q:"algorithm"` + + // Scope of the TSIG key (ZONE or POOL). + Scope string `q:"scope"` +} + +// ToTSIGKeyListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTSIGKeyListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List implements a TSIG key List request. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := baseURL(client) + if opts != nil { + query, err := opts.ToTSIGKeyListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return TSIGKeyPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// Get returns information about a TSIG key, given its ID. +func Get(ctx context.Context, client *gophercloud.ServiceClient, tsigkeyID string) (r GetResult) { + resp, err := client.Get(ctx, tsigkeyURL(client, tsigkeyID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateOptsBuilder allows extensions to add additional attributes to the +// Create request. +type CreateOptsBuilder interface { + ToTSIGKeyCreateMap() (map[string]any, error) +} + +// CreateOpts specifies the attributes used to create a TSIG key. +type CreateOpts struct { + // Name of the TSIG key. + Name string `json:"name" required:"true"` + + // Algorithm is the TSIG algorithm (e.g., hmac-sha256, hmac-sha512). + Algorithm string `json:"algorithm" required:"true"` + + // Secret is the base64-encoded secret key. + Secret string `json:"secret" required:"true"` + + // Scope defines the scope of the TSIG key (ZONE or POOL). + Scope string `json:"scope" required:"true"` + + // ResourceID is the ID of the resource (zone or pool) this key is associated with. + ResourceID string `json:"resource_id" required:"true"` +} + +// ToTSIGKeyCreateMap formats a CreateOpts structure into a request body. +func (opts CreateOpts) ToTSIGKeyCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create implements a TSIG key create request. +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTSIGKeyCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateOptsBuilder allows extensions to add additional attributes to the +// Update request. +type UpdateOptsBuilder interface { + ToTSIGKeyUpdateMap() (map[string]any, error) +} + +// UpdateOpts specifies the attributes to update a TSIG key. +type UpdateOpts struct { + // Name of the TSIG key. + Name string `json:"name,omitempty"` + + // Algorithm is the TSIG algorithm. + Algorithm string `json:"algorithm,omitempty"` + + // Secret is the base64-encoded secret key. + Secret string `json:"secret,omitempty"` + + // Scope defines the scope of the TSIG key. + Scope string `json:"scope,omitempty"` + + // ResourceID is the ID of the resource this key is associated with. + ResourceID string `json:"resource_id,omitempty"` +} + +// ToTSIGKeyUpdateMap formats an UpdateOpts structure into a request body. +func (opts UpdateOpts) ToTSIGKeyUpdateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Update implements a TSIG key update request. +func Update(ctx context.Context, client *gophercloud.ServiceClient, tsigkeyID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToTSIGKeyUpdateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Patch(ctx, tsigkeyURL(client, tsigkeyID), &b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete implements a TSIG key delete request. +func Delete(ctx context.Context, client *gophercloud.ServiceClient, tsigkeyID string) (r DeleteResult) { + resp, err := client.Delete(ctx, tsigkeyURL(client, tsigkeyID), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/dns/v2/tsigkeys/results.go b/openstack/dns/v2/tsigkeys/results.go new file mode 100644 index 0000000000..02382e9b7a --- /dev/null +++ b/openstack/dns/v2/tsigkeys/results.go @@ -0,0 +1,119 @@ +package tsigkeys + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +type commonResult struct { + gophercloud.Result +} + +// Extract interprets a GetResult, CreateResult or UpdateResult as a TSIGKey. +// An error is returned if the original call or the extraction failed. +func (r commonResult) Extract() (*TSIGKey, error) { + var s *TSIGKey + err := r.ExtractInto(&s) + return s, err +} + +// CreateResult is the result of a Create request. Call its Extract method +// to interpret the result as a TSIGKey. +type CreateResult struct { + commonResult +} + +// GetResult is the result of a Get request. Call its Extract method +// to interpret the result as a TSIGKey. +type GetResult struct { + commonResult +} + +// UpdateResult is the result of an Update request. Call its Extract method +// to interpret the result as a TSIGKey. +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 +} + +// TSIGKeyPage is a single page of TSIGKey results. +type TSIGKeyPage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if the page contains no results. +func (r TSIGKeyPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + s, err := ExtractTSIGKeys(r) + return len(s) == 0, err +} + +// ExtractTSIGKeys extracts a slice of TSIGKeys from a List result. +func ExtractTSIGKeys(r pagination.Page) ([]TSIGKey, error) { + var s struct { + TSIGKeys []TSIGKey `json:"tsigkeys"` + } + err := (r.(TSIGKeyPage)).ExtractInto(&s) + return s.TSIGKeys, err +} + +// TSIGKey represents a TSIG key for DNS transaction authentication. +type TSIGKey struct { + // ID uniquely identifies this TSIG key. + ID string `json:"id"` + + // Name is the name of the TSIG key. + Name string `json:"name"` + + // Algorithm is the TSIG algorithm used (e.g., hmac-sha256, hmac-sha512). + Algorithm string `json:"algorithm"` + + // Secret is the base64-encoded secret key. + Secret string `json:"secret"` + + // Scope defines the scope of the TSIG key (ZONE or POOL). + Scope string `json:"scope"` + + // ResourceID is the ID of the resource (zone or pool) this key is associated with. + ResourceID string `json:"resource_id"` + + // CreatedAt is the date when the TSIG key was created. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the date when the TSIG key was last updated. + UpdatedAt time.Time `json:"-"` + + // Links includes HTTP references to the itself. + Links map[string]any `json:"links"` +} + +func (r *TSIGKey) UnmarshalJSON(b []byte) error { + type tmp TSIGKey + 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 = TSIGKey(s.tmp) + + r.CreatedAt = time.Time(s.CreatedAt) + r.UpdatedAt = time.Time(s.UpdatedAt) + + return nil +} diff --git a/openstack/dns/v2/tsigkeys/testing/doc.go b/openstack/dns/v2/tsigkeys/testing/doc.go new file mode 100644 index 0000000000..6f54fc2d97 --- /dev/null +++ b/openstack/dns/v2/tsigkeys/testing/doc.go @@ -0,0 +1,2 @@ +// tsigkeys unit tests +package testing diff --git a/openstack/dns/v2/tsigkeys/testing/fixtures_test.go b/openstack/dns/v2/tsigkeys/testing/fixtures_test.go new file mode 100644 index 0000000000..dc15e74197 --- /dev/null +++ b/openstack/dns/v2/tsigkeys/testing/fixtures_test.go @@ -0,0 +1,224 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/tsigkeys" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +// ListOutput is a sample response to a List call. +const ListOutput = ` +{ + "links": { + "self": "http://example.com:9001/v2/tsigkeys" + }, + "metadata": { + "total_count": 2 + }, + "tsigkeys": [ + { + "id": "8add45a3-0f29-489f-854e-7609baf8d7a1", + "name": "poolsecondarykey", + "algorithm": "hmac-sha256", + "secret": "my-base64-secret-example==", + "scope": "POOL", + "resource_id": "adcc2fb6-7984-4453-a6f9-2cc2a24a38bb", + "created_at": "2025-08-13T15:54:18.000000", + "updated_at": null, + "links": { + "self": "http://127.0.0.1:9001/v2/tsigkeys/8add45a3-0f29-489f-854e-7609baf8d7a1" + } + }, + { + "id": "9bef46b4-1f3a-59a0-965f-8710caf9e8b2", + "name": "zonekey", + "algorithm": "hmac-sha512", + "secret": "another-base64-secret-example==", + "scope": "ZONE", + "resource_id": "c4d5e6f7-8a9b-0c1d-2e3f-4a5b6c7d8e9f", + "created_at": "2025-08-14T10:30:45.000000", + "updated_at": "2025-08-15T14:22:33.000000", + "links": { + "self": "http://127.0.0.1:9001/v2/tsigkeys/9bef46b4-1f3a-59a0-965f-8710caf9e8b2" + } + } + ] +} +` + +// GetOutput is a sample response to a Get call. +const GetOutput = ` +{ + "id": "8add45a3-0f29-489f-854e-7609baf8d7a1", + "name": "poolsecondarykey", + "algorithm": "hmac-sha256", + "secret": "my-base64-secret-example==", + "scope": "POOL", + "resource_id": "adcc2fb6-7984-4453-a6f9-2cc2a24a38bb", + "created_at": "2025-08-13T15:54:18.000000", + "updated_at": null, + "links": { + "self": "http://127.0.0.1:9001/v2/tsigkeys/8add45a3-0f29-489f-854e-7609baf8d7a1" + } +} +` + +// FirstTSIGKeyCreatedAt is the created at time for the first TSIG key +var FirstTSIGKeyCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2025-08-13T15:54:18.000000") + +// FirstTSIGKey is the first result in ListOutput +var FirstTSIGKey = tsigkeys.TSIGKey{ + ID: "8add45a3-0f29-489f-854e-7609baf8d7a1", + Name: "poolsecondarykey", + Algorithm: "hmac-sha256", + Secret: "my-base64-secret-example==", + Scope: "POOL", + ResourceID: "adcc2fb6-7984-4453-a6f9-2cc2a24a38bb", + CreatedAt: FirstTSIGKeyCreatedAt, + Links: map[string]any{ + "self": "http://127.0.0.1:9001/v2/tsigkeys/8add45a3-0f29-489f-854e-7609baf8d7a1", + }, +} + +var SecondTSIGKeyCreatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2025-08-14T10:30:45.000000") +var SecondTSIGKeyUpdatedAt, _ = time.Parse(gophercloud.RFC3339MilliNoZ, "2025-08-15T14:22:33.000000") + +var SecondTSIGKey = tsigkeys.TSIGKey{ + ID: "9bef46b4-1f3a-59a0-965f-8710caf9e8b2", + Name: "zonekey", + Algorithm: "hmac-sha512", + Secret: "another-base64-secret-example==", + Scope: "ZONE", + ResourceID: "c4d5e6f7-8a9b-0c1d-2e3f-4a5b6c7d8e9f", + CreatedAt: SecondTSIGKeyCreatedAt, + UpdatedAt: SecondTSIGKeyUpdatedAt, + Links: map[string]any{ + "self": "http://127.0.0.1:9001/v2/tsigkeys/9bef46b4-1f3a-59a0-965f-8710caf9e8b2", + }, +} + +// ExpectedTSIGKeysSlice is the slice of results that should be parsed +// from ListOutput, in the expected order. +var ExpectedTSIGKeysSlice = []tsigkeys.TSIGKey{FirstTSIGKey, SecondTSIGKey} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/tsigkeys", 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.Fprint(w, ListOutput) + }) +} + +// HandleGetSuccessfully configures the test server to respond to a Get request. +func HandleGetSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/tsigkeys/8add45a3-0f29-489f-854e-7609baf8d7a1", 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.Fprint(w, GetOutput) + }) +} + +// CreateTSIGKeyRequest is a sample request to create a TSIG key. +const CreateTSIGKeyRequest = ` +{ + "name": "poolsecondarykey", + "algorithm": "hmac-sha256", + "secret": "my-base64-secret-example==", + "scope": "POOL", + "resource_id": "adcc2fb6-7984-4453-a6f9-2cc2a24a38bb" +} +` + +// CreateTSIGKeyResponse is a sample response to a create request. +const CreateTSIGKeyResponse = ` +{ + "id": "8add45a3-0f29-489f-854e-7609baf8d7a1", + "name": "poolsecondarykey", + "algorithm": "hmac-sha256", + "secret": "my-base64-secret-example==", + "scope": "POOL", + "resource_id": "adcc2fb6-7984-4453-a6f9-2cc2a24a38bb", + "created_at": "2025-08-13T15:54:18.000000", + "updated_at": null, + "links": { + "self": "http://127.0.0.1:9001/v2/tsigkeys/8add45a3-0f29-489f-854e-7609baf8d7a1" + } +} +` + +// CreatedTSIGKey is the expected created TSIG key +var CreatedTSIGKey = FirstTSIGKey + +// HandleCreateSuccessfully configures the test server to respond to a Create request. +func HandleCreateSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/tsigkeys", 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, CreateTSIGKeyRequest) + + w.WriteHeader(http.StatusCreated) + w.Header().Add("Content-Type", "application/json") + fmt.Fprint(w, CreateTSIGKeyResponse) + }) +} + +// UpdateTSIGKeyRequest is a sample request to update a TSIG key. +const UpdateTSIGKeyRequest = ` +{ + "name": "updatedsecondarykey", + "secret": "new-base64-secret-example==" +} +` + +// UpdateTSIGKeyResponse is a sample response to update a TSIG key. +const UpdateTSIGKeyResponse = ` +{ + "id": "8add45a3-0f29-489f-854e-7609baf8d7a1", + "name": "updatedsecondarykey", + "algorithm": "hmac-sha256", + "secret": "new-base64-secret-example==", + "scope": "POOL", + "resource_id": "adcc2fb6-7984-4453-a6f9-2cc2a24a38bb", + "created_at": "2025-08-13T15:54:18.000000", + "updated_at": "2025-08-16T09:15:22.000000", + "links": { + "self": "http://127.0.0.1:9001/v2/tsigkeys/8add45a3-0f29-489f-854e-7609baf8d7a1" + } +} +` + +// HandleUpdateSuccessfully configures the test server to respond to an Update request. +func HandleUpdateSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/tsigkeys/8add45a3-0f29-489f-854e-7609baf8d7a1", + 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, UpdateTSIGKeyRequest) + + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-Type", "application/json") + fmt.Fprint(w, UpdateTSIGKeyResponse) + }) +} + +// HandleDeleteSuccessfully configures the test server to respond to a Delete request. +func HandleDeleteSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/tsigkeys/8add45a3-0f29-489f-854e-7609baf8d7a1", + 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/dns/v2/tsigkeys/testing/requests_test.go b/openstack/dns/v2/tsigkeys/testing/requests_test.go new file mode 100644 index 0000000000..3bc163d5b4 --- /dev/null +++ b/openstack/dns/v2/tsigkeys/testing/requests_test.go @@ -0,0 +1,98 @@ +package testing + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/dns/v2/tsigkeys" + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestList(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleListSuccessfully(t, fakeServer) + + count := 0 + err := tsigkeys.List(client.ServiceClient(fakeServer), nil).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + count++ + actual, err := tsigkeys.ExtractTSIGKeys(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedTSIGKeysSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, count) +} + +func TestListAllPages(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleListSuccessfully(t, fakeServer) + + allPages, err := tsigkeys.List(client.ServiceClient(fakeServer), nil).AllPages(context.TODO()) + th.AssertNoErr(t, err) + allTSIGKeys, err := tsigkeys.ExtractTSIGKeys(allPages) + th.AssertNoErr(t, err) + th.CheckEquals(t, 2, len(allTSIGKeys)) +} + +func TestGet(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleGetSuccessfully(t, fakeServer) + + actual, err := tsigkeys.Get(context.TODO(), client.ServiceClient(fakeServer), "8add45a3-0f29-489f-854e-7609baf8d7a1").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &FirstTSIGKey, actual) +} + +func TestCreate(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleCreateSuccessfully(t, fakeServer) + + createOpts := tsigkeys.CreateOpts{ + Name: "poolsecondarykey", + Algorithm: "hmac-sha256", + Secret: "my-base64-secret-example==", + Scope: "POOL", + ResourceID: "adcc2fb6-7984-4453-a6f9-2cc2a24a38bb", + } + + actual, err := tsigkeys.Create(context.TODO(), client.ServiceClient(fakeServer), createOpts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &CreatedTSIGKey, actual) +} + +func TestUpdate(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleUpdateSuccessfully(t, fakeServer) + + updateOpts := tsigkeys.UpdateOpts{ + Name: "updatedsecondarykey", + Secret: "new-base64-secret-example==", + } + + UpdatedTSIGKey := CreatedTSIGKey + UpdatedTSIGKey.Name = "updatedsecondarykey" + UpdatedTSIGKey.Secret = "new-base64-secret-example==" + + actual, err := tsigkeys.Update(context.TODO(), client.ServiceClient(fakeServer), UpdatedTSIGKey.ID, updateOpts).Extract() + th.AssertNoErr(t, err) + th.CheckEquals(t, UpdatedTSIGKey.Name, actual.Name) + th.CheckEquals(t, UpdatedTSIGKey.Secret, actual.Secret) +} + +func TestDelete(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleDeleteSuccessfully(t, fakeServer) + + err := tsigkeys.Delete(context.TODO(), client.ServiceClient(fakeServer), "8add45a3-0f29-489f-854e-7609baf8d7a1").ExtractErr() + th.AssertNoErr(t, err) +} diff --git a/openstack/dns/v2/tsigkeys/urls.go b/openstack/dns/v2/tsigkeys/urls.go new file mode 100644 index 0000000000..f7c4b0cfe3 --- /dev/null +++ b/openstack/dns/v2/tsigkeys/urls.go @@ -0,0 +1,13 @@ +package tsigkeys + +import "github.com/gophercloud/gophercloud/v2" + +// baseURL returns the base URL for TSIG keys. +func baseURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("tsigkeys") +} + +// tsigkeyURL returns the URL for a specific TSIG key. +func tsigkeyURL(c *gophercloud.ServiceClient, tsigkeyID string) string { + return c.ServiceURL("tsigkeys", tsigkeyID) +} From 0b45b510ae393b51fa707ef0b188af5e8963a5fa Mon Sep 17 00:00:00 2001 From: Winicius Silva Date: Thu, 19 Feb 2026 17:18:58 +0000 Subject: [PATCH 2240/2296] remove description from ListOpts --- .../layer3/addressscopes/requests.go | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go index 22e4df39de..d75a3f0581 100644 --- a/openstack/networking/v2/extensions/layer3/addressscopes/requests.go +++ b/openstack/networking/v2/extensions/layer3/addressscopes/requests.go @@ -20,17 +20,16 @@ type ListOptsBuilder interface { // SortDir sets the direction, and is either `asc' or `desc'. // Marker and Limit are used for the pagination. type ListOpts struct { - ID string `q:"id"` - Name string `q:"name"` - TenantID string `q:"tenant_id"` - ProjectID string `q:"project_id"` - IPVersion int `q:"ip_version"` - Shared *bool `q:"shared"` - Description string `q:"description"` - 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"` + IPVersion int `q:"ip_version"` + Shared *bool `q:"shared"` + Limit int `q:"limit"` + Marker string `q:"marker"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` } // ToAddressScopeListQuery formats a ListOpts into a query string. From 7f478e145f769faf73576712a1336344e9635d93 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Tue, 3 Mar 2026 21:14:23 +0100 Subject: [PATCH 2241/2296] fix: networkipavailabilities: handle scientific notation in IP counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Go's JSON decoder converts large integers to float64 when decoding into interface{}, which causes values ≥ 1e21 to be re-encoded in scientific notation. big.Int cannot parse that notation, crashing any List() call that includes a network with a large IPv6 prefix such as /56 or shorter. Signed-off-by: Bertrand Lanson --- .../networkipavailabilities/results.go | 62 ++++++++++++++++--- .../testing/fixtures_test.go | 14 +++-- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/openstack/networking/v2/extensions/networkipavailabilities/results.go b/openstack/networking/v2/extensions/networkipavailabilities/results.go index b37617f9d1..e29810972a 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/results.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/results.go @@ -2,6 +2,7 @@ package networkipavailabilities import ( "encoding/json" + "fmt" "math/big" "github.com/gophercloud/gophercloud/v2" @@ -52,12 +53,35 @@ type NetworkIPAvailability struct { UsedIPs string `json:"-"` } +// Go's encoding/json decodes all JSON numbers into float64 when the target is +// interface{}. For large integers (abs >= 1e21), re-encoding that float64 +// produces scientific notation (e.g. "1.1805916207174113e+21"), which +// big.Int.UnmarshalJSON cannot parse. This function handles both plain integer +// and scientific notation forms. +func parseBigIntFromNumber(n json.Number) (*big.Int, error) { + s := string(n) + + // Fast path: plain integer notation + bi := new(big.Int) + if _, ok := bi.SetString(s, 10); ok { + return bi, nil + } + + // Slow path: scientific notation from float64 round-trip + bf := new(big.Float).SetPrec(256) + if _, _, err := bf.Parse(s, 10); err != nil { + return nil, fmt.Errorf("networkipavailabilities: cannot parse %q as an integer: %w", s, err) + } + result, _ := bf.Int(nil) + return result, nil +} + func (r *NetworkIPAvailability) UnmarshalJSON(b []byte) error { type tmp NetworkIPAvailability var s struct { tmp - TotalIPs big.Int `json:"total_ips"` - UsedIPs big.Int `json:"used_ips"` + TotalIPs json.Number `json:"total_ips"` + UsedIPs json.Number `json:"used_ips"` } err := json.Unmarshal(b, &s) @@ -66,10 +90,19 @@ func (r *NetworkIPAvailability) UnmarshalJSON(b []byte) error { } *r = NetworkIPAvailability(s.tmp) - r.TotalIPs = s.TotalIPs.String() - r.UsedIPs = s.UsedIPs.String() + totalIPs, err := parseBigIntFromNumber(s.TotalIPs) + if err != nil { + return err + } + r.TotalIPs = totalIPs.String() - return err + usedIPs, err := parseBigIntFromNumber(s.UsedIPs) + if err != nil { + return err + } + r.UsedIPs = usedIPs.String() + + return nil } // SubnetIPAvailability represents availability details for a single subnet. @@ -97,8 +130,8 @@ func (r *SubnetIPAvailability) UnmarshalJSON(b []byte) error { type tmp SubnetIPAvailability var s struct { tmp - TotalIPs big.Int `json:"total_ips"` - UsedIPs big.Int `json:"used_ips"` + TotalIPs json.Number `json:"total_ips"` + UsedIPs json.Number `json:"used_ips"` } err := json.Unmarshal(b, &s) @@ -107,10 +140,19 @@ func (r *SubnetIPAvailability) UnmarshalJSON(b []byte) error { } *r = SubnetIPAvailability(s.tmp) - r.TotalIPs = s.TotalIPs.String() - r.UsedIPs = s.UsedIPs.String() + totalIPs, err := parseBigIntFromNumber(s.TotalIPs) + if err != nil { + return err + } + r.TotalIPs = totalIPs.String() + + usedIPs, err := parseBigIntFromNumber(s.UsedIPs) + if err != nil { + return err + } + r.UsedIPs = usedIPs.String() - return err + return nil } // NetworkIPAvailabilityPage stores a single page of NetworkIPAvailabilities diff --git a/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go index 58900db23c..475511cce2 100644 --- a/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go +++ b/openstack/networking/v2/extensions/networkipavailabilities/testing/fixtures_test.go @@ -15,11 +15,11 @@ const NetworkIPAvailabilityListResult = ` "project_id": "fb57277ef2f84a0e85b9018ec2dedbf7", "subnet_ip_availability": [ { - "cidr": "fdbc:bf53:567e::/64", + "cidr": "fdbc:bf53:567e::/56", "ip_version": 6, "subnet_id": "497ac4d3-0b92-42cf-82de-71302ab2b656", "subnet_name": "ipv6-private-subnet", - "total_ips": 18446744073709552000, + "total_ips": 4722366482869645213696, "used_ips": 2 }, { @@ -69,10 +69,14 @@ var NetworkIPAvailability1 = networkipavailabilities.NetworkIPAvailability{ { SubnetID: "497ac4d3-0b92-42cf-82de-71302ab2b656", SubnetName: "ipv6-private-subnet", - CIDR: "fdbc:bf53:567e::/64", + CIDR: "fdbc:bf53:567e::/56", IPVersion: int(gophercloud.IPv6), - TotalIPs: "18446744073709552000", - UsedIPs: "2", + // 4722366482869645213696 is 2^72 (a /56 IPv6 subnet). + // After gophercloud's float64 round-trip (numbers >= 1e21 are + // re-encoded in scientific notation), the value loses the last + // few digits of precision but no longer crashes. + TotalIPs: "4722366482869645000000", + UsedIPs: "2", }, { SubnetID: "521f47e7-c4fb-452c-b71a-851da38cc571", From 421f7d4af2830a2f1ed2078fcb0b1fc9cc6f4e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Tue, 10 Mar 2026 09:20:14 +0100 Subject: [PATCH 2242/2296] Do not specify go patch version There's no good reason for this, and makes it harder to consume. --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 2fce7628d9..f1415ecc13 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gophercloud/gophercloud/v2 -go 1.25.6 +go 1.25.0 require ( go.yaml.in/yaml/v3 v3.0.4 From 1cb9d1c03b2c4d64fe508ddcbc010a52804c4388 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 09:02:59 +0000 Subject: [PATCH 2243/2296] build(deps): bump golang.org/x/crypto from 0.48.0 to 0.49.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.48.0 to 0.49.0. - [Commits](https://github.com/golang/crypto/compare/v0.48.0...v0.49.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-version: 0.49.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index f1415ecc13..2c668ddd43 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.0 require ( go.yaml.in/yaml/v3 v3.0.4 - golang.org/x/crypto v0.48.0 + golang.org/x/crypto v0.49.0 ) -require golang.org/x/sys v0.41.0 // indirect +require golang.org/x/sys v0.42.0 // indirect diff --git a/go.sum b/go.sum index 457e8ed0f8..53ebe91e84 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= 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= From 74130d7edf09ac2e7626adf422c84b7384ab1e3c Mon Sep 17 00:00:00 2001 From: jlarriba Date: Tue, 10 Mar 2026 13:07:45 +0100 Subject: [PATCH 2244/2296] Fix clouds.yaml search path to use XDG convention on all platforms os.UserConfigDir() returns ~/Library/Application Support on macOS, which doesn't match the OpenStack standard. Use XDG_CONFIG_HOME with a ~/.config fallback instead. --- openstack/config/clouds/clouds.go | 12 ++- openstack/config/clouds/clouds_test.go | 113 +++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 3 deletions(-) diff --git a/openstack/config/clouds/clouds.go b/openstack/config/clouds/clouds.go index 94c01645d9..a8eb357f7d 100644 --- a/openstack/config/clouds/clouds.go +++ b/openstack/config/clouds/clouds.go @@ -81,9 +81,15 @@ func Parse(opts ...ParseOption) (gophercloud.AuthOptions, gophercloud.EndpointOp 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) + // Use XDG_CONFIG_HOME or fall back to ~/.config, matching the + // OpenStack convention for clouds.yaml location on all platforms. + userConfig := os.Getenv("XDG_CONFIG_HOME") + if userConfig == "" { + homeDir, err := os.UserHomeDir() + if err != nil { + return gophercloud.AuthOptions{}, gophercloud.EndpointOpts{}, nil, fmt.Errorf("failed to get the user home directory: %w", err) + } + userConfig = path.Join(homeDir, ".config") } options.locations = []string{path.Join(cwd, "clouds.yaml"), path.Join(userConfig, "openstack", "clouds.yaml"), path.Join("/etc", "openstack", "clouds.yaml")} } diff --git a/openstack/config/clouds/clouds_test.go b/openstack/config/clouds/clouds_test.go index 384aea92d8..bd705833af 100644 --- a/openstack/config/clouds/clouds_test.go +++ b/openstack/config/clouds/clouds_test.go @@ -173,6 +173,119 @@ func TestParse(t *testing.T) { } }) + t.Run("uses XDG_CONFIG_HOME for clouds.yaml location", func(t *testing.T) { + const cloudsYAML = `clouds: + gophercloud-test: + auth: + auth_url: https://example.com/xdg-config:13000` + + // Create a temp dir to use as XDG_CONFIG_HOME with clouds.yaml inside. + xdgDir, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(xdgDir) + + openstackDir := path.Join(xdgDir, "openstack") + if err := os.MkdirAll(openstackDir, 0755); err != nil { + t.Fatalf("unable to create openstack config directory: %v", err) + } + if err := os.WriteFile(path.Join(openstackDir, "clouds.yaml"), []byte(cloudsYAML), 0644); err != nil { + t.Fatalf("unable to create a mock clouds.yaml file: %v", err) + } + + // Change to an empty temp dir so cwd has no clouds.yaml. + emptyDir, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(emptyDir) + + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("unable to determine the current working directory: %v", err) + } + if err := os.Chdir(emptyDir); err != nil { + t.Fatalf("unable to move to a temporary directory: %v", err) + } + defer func() { + if err := os.Chdir(cwd); err != nil { + panic("unable to reset the current working directory: " + err.Error()) + } + }() + + t.Setenv("XDG_CONFIG_HOME", xdgDir) + t.Setenv("OS_CLIENT_CONFIG_FILE", "") + + ao, _, _, err := clouds.Parse( + clouds.WithCloudName("gophercloud-test"), + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if got := ao.IdentityEndpoint; got != "https://example.com/xdg-config:13000" { + t.Errorf("unexpected identity endpoint: %q", got) + } + }) + + t.Run("falls back to ~/.config when XDG_CONFIG_HOME is not set", func(t *testing.T) { + const cloudsYAML = `clouds: + gophercloud-test: + auth: + auth_url: https://example.com/home-config:13000` + + // Create a temp dir to use as HOME with clouds.yaml in .config/openstack/. + homeDir, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(homeDir) + + openstackDir := path.Join(homeDir, ".config", "openstack") + if err := os.MkdirAll(openstackDir, 0755); err != nil { + t.Fatalf("unable to create openstack config directory: %v", err) + } + if err := os.WriteFile(path.Join(openstackDir, "clouds.yaml"), []byte(cloudsYAML), 0644); err != nil { + t.Fatalf("unable to create a mock clouds.yaml file: %v", err) + } + + // Change to an empty temp dir so cwd has no clouds.yaml. + emptyDir, err := os.MkdirTemp(os.TempDir(), tempDirPrefix) + if err != nil { + t.Fatalf("unable to create a temporary directory: %v", err) + } + defer rmTmpDirOrPanic(emptyDir) + + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("unable to determine the current working directory: %v", err) + } + if err := os.Chdir(emptyDir); err != nil { + t.Fatalf("unable to move to a temporary directory: %v", err) + } + defer func() { + if err := os.Chdir(cwd); err != nil { + panic("unable to reset the current working directory: " + err.Error()) + } + }() + + t.Setenv("XDG_CONFIG_HOME", "") + t.Setenv("HOME", homeDir) + t.Setenv("OS_CLIENT_CONFIG_FILE", "") + + ao, _, _, err := clouds.Parse( + clouds.WithCloudName("gophercloud-test"), + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if got := ao.IdentityEndpoint; got != "https://example.com/home-config:13000" { + t.Errorf("unexpected identity endpoint: %q", got) + } + }) + t.Run("falls back to the next location if clouds.yaml is not found", func(t *testing.T) { const cloudsYAML1 = `clouds: gophercloud-test: From 89a59ae20fd6c2176833792a7e3eaab8880bdce8 Mon Sep 17 00:00:00 2001 From: Juan Larriba Date: Mon, 23 Mar 2026 11:09:25 +0100 Subject: [PATCH 2245/2296] Use jammy amphora in octavia epoxy job --- .github/workflows/functional-loadbalancer.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 2afdb83f90..8740cf21ce 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -24,6 +24,9 @@ jobs: - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" + devstack_conf_overrides: | + LIBVIRT_CPU_MODE=host-passthrough + OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID=jammy - name: "dalmatian" openstack_version: "stable/2024.2" ubuntu_version: "22.04" From 3b0490f4445fbe5f6f06772d83448849dbb4a6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 11:28:49 +0100 Subject: [PATCH 2246/2296] ci: fix unsound condition in label-issue workflow The condition was using separate ${{ }} expressions around an == operator, which expands to a non-empty string that always evaluates to true. Move the comparison inside a single expression so it evaluates correctly as a boolean. Reported by zizmor (unsound-condition). --- .github/workflows/label-issue.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-issue.yaml b/.github/workflows/label-issue.yaml index 45ab4cbe34..cb5e84e897 100644 --- a/.github/workflows/label-issue.yaml +++ b/.github/workflows/label-issue.yaml @@ -7,7 +7,7 @@ on: jobs: clear_needinfo: name: Clear needinfo - if: ${{ github.event.issue.user.login }} == ${{ github.event.comment.user.login }} + if: ${{ github.event.issue.user.login == github.event.comment.user.login }} runs-on: ubuntu-latest permissions: issues: write From 02253dd2cc1dfa646605934f55278bc04a0ab385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 11:30:01 +0100 Subject: [PATCH 2247/2296] ci: add persist-credentials: false to all checkout steps Prevent the GitHub token from being persisted in the git config after checkout. This avoids potential credential leakage through artifacts or subsequent steps that don't need repository access. Reported by zizmor (artipacked). --- .github/workflows/codeql-analysis.yaml | 2 ++ .github/workflows/ensure-labels.yaml | 2 ++ .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-fwaas_v2.yaml | 2 ++ .github/workflows/functional-identity.yaml | 2 ++ .github/workflows/functional-image.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/functional-workflow.yaml | 2 ++ .github/workflows/label-pr.yaml | 1 + .github/workflows/lint.yaml | 2 ++ .github/workflows/unit.yaml | 2 ++ 23 files changed, 45 insertions(+) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 90765a6eca..dabe73a738 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -19,6 +19,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v6 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v6 diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index 907a7b8733..e881954b42 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -11,6 +11,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + with: + persist-credentials: false - uses: micnncim/action-label-syncer@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 69455e91b0..8d92efd576 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 5f81afd1ac..4a4a38a31f 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 67ecfe9e61..3b540e3898 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index b704ebd815..72e71d58c3 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 2128b5db1f..2ef91940f1 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -45,6 +45,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index ad0f78be29..6b5f0b06ca 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -30,6 +30,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index be6211ca78..34cd6a6007 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -28,6 +28,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 2d3e65f6c1..e18ce5dddf 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 6a89bfdf73..4c1fd9f298 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 753fe0b841..d9a6d9c11d 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -36,6 +36,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 8740cf21ce..7620d29ce8 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -35,6 +35,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index e4723ac922..7c1b7e476f 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index e7bf14d7ee..778a1a8dd5 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index e0c8ce599c..f18c4d28e7 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 545088574c..3551bbafad 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index f65534d955..192b994d5f 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index a723b9879b..3636005740 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -30,6 +30,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index ee987992fb..7ab647caeb 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -27,6 +27,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Check changed files uses: ./.github/actions/file-filter diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index 423ea1fdc6..7cbc0e95bd 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -15,6 +15,7 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: Rebase the PR against origin/github.base_ref to ensure actual API compatibility run: | diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index f2e4fafa2b..84f2280f20 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -11,6 +11,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index 01d4ea6697..a5858e7cb8 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -16,6 +16,8 @@ jobs: steps: - name: Checkout Gophercloud uses: actions/checkout@v6 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v6 with: From b3e4310a6e83e46e670e3ad30daab142a1d9a3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 11:31:38 +0100 Subject: [PATCH 2248/2296] ci: add explicit permissions to all workflow jobs Apply the principle of least privilege by adding explicit permissions blocks to every job that was relying on default (overly broad) permissions. Each job now declares only the permissions it needs. Reported by zizmor (excessive-permissions). --- .github/workflows/backport.yaml | 8 ++++++++ .github/workflows/check-pr-labels.yaml | 1 + .github/workflows/ensure-labels.yaml | 3 +++ .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-fwaas_v2.yaml | 2 ++ .github/workflows/functional-identity.yaml | 2 ++ .github/workflows/functional-image.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/functional-workflow.yaml | 2 ++ .github/workflows/label-pr.yaml | 4 ++++ 22 files changed, 52 insertions(+) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 04f06538ac..88704f66df 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -9,6 +9,10 @@ on: jobs: backport_v1: name: "Backport to v1" + permissions: + contents: read + issues: write + pull-requests: write # Only react to merged PRs for security reasons. # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. if: > @@ -70,6 +74,10 @@ jobs: backport_v2: name: "Backport to v2" + permissions: + contents: read + issues: write + pull-requests: write # Only react to merged PRs for security reasons. # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. if: > diff --git a/.github/workflows/check-pr-labels.yaml b/.github/workflows/check-pr-labels.yaml index 8d3cdde07b..2b30487abd 100644 --- a/.github/workflows/check-pr-labels.yaml +++ b/.github/workflows/check-pr-labels.yaml @@ -11,6 +11,7 @@ on: jobs: hold: + permissions: {} if: github.event.pull_request.merged == false runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index e881954b42..76f7e460f8 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -8,6 +8,9 @@ on: - .github/workflows/ensure-labels.yaml jobs: ensure: + permissions: + contents: read + issues: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 8d92efd576..2ab2e72976 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-baremetal: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 4a4a38a31f..b80efe2da2 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-basic: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 3b540e3898..bfde0f6d87 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-blockstorage: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 72e71d58c3..87c967cf5d 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-compute: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 2ef91940f1..743b6cf136 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-containerinfra: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 6b5f0b06ca..795c5506ac 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-dns: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 34cd6a6007..1fcefe8523 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -10,6 +10,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-fwaas_v2: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index e18ce5dddf..ced561b8d9 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-identity: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 4c1fd9f298..9cdcf5609a 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-image: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index d9a6d9c11d..4182c8c5d4 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-keymanager: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 7620d29ce8..24d21112f3 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-loadbalancer: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 7c1b7e476f..230d1d7018 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-messaging: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 778a1a8dd5..120e6a13c2 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-networking: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index f18c4d28e7..a20e34d548 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-objectstorage: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 3551bbafad..e735401d51 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-orchestration: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 192b994d5f..cf1ca7d946 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-placement: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 3636005740..a848d8a6dd 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-sharedfilesystems: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 7ab647caeb..569e2d36bb 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -6,6 +6,8 @@ on: - cron: '0 0 */3 * *' jobs: functional-workflow: + permissions: + contents: read strategy: fail-fast: false matrix: diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index 7cbc0e95bd..e0fdb9ad17 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -8,6 +8,10 @@ on: jobs: semver: + permissions: + contents: read + issues: write + pull-requests: write runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 From 135f30d22527c40c3d32c30e7c731e6c9aa6a65d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 11:32:10 +0100 Subject: [PATCH 2249/2296] ci: fix template injection vulnerabilities in workflow run blocks Replace ${{ env.* }} expressions in shell run blocks with native shell variable references ($VAR), and pass ${{ github.base_ref }} through an env variable instead of expanding it directly in a shell command. This prevents potential code injection through template expansion. Reported by zizmor (template-injection). --- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- .github/workflows/label-pr.yaml | 3 ++- 19 files changed, 20 insertions(+), 19 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 2ab2e72976..d5bc6a0ab3 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -130,7 +130,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index b80efe2da2..df679f399d 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -83,7 +83,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index bfde0f6d87..7727e4c378 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -89,7 +89,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 87c967cf5d..a53169e81c 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -89,7 +89,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 743b6cf136..beaf44a1e2 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -116,7 +116,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 795c5506ac..21f5b732d3 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -97,7 +97,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 1fcefe8523..082225f7f3 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -112,7 +112,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index ced561b8d9..53259796b1 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -87,7 +87,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 9cdcf5609a..95227639dd 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -87,7 +87,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 4182c8c5d4..7f06589fe1 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -103,7 +103,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 24d21112f3..50c7a3211b 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -103,7 +103,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 230d1d7018..669b0d1448 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -90,7 +90,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 120e6a13c2..ecc9961d38 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -107,7 +107,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index a20e34d548..d2327a618c 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -93,7 +93,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index e735401d51..0c6ff30d26 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -89,7 +89,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index cf1ca7d946..0520b3e40e 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -87,7 +87,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index a848d8a6dd..9847f1c2fd 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -111,7 +111,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 569e2d36bb..236c356bf5 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -92,7 +92,7 @@ jobs: - name: Set job status run: | - if [[ "${{ env.TESTS_SKIPPED }}" == "true" || "${{ env.TESTS_RUN }}" == "true" ]]; then + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then echo "Job completed successfully (either ran tests or skipped appropriately)" exit 0 else diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index e0fdb9ad17..a15e54f7f0 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -25,9 +25,10 @@ jobs: run: | git config --global user.email "localrebase@gophercloud.io" git config --global user.name "Local rebase" - git rebase -i origin/${{ github.base_ref }} + git rebase -i origin/$BASE_REF env: GIT_SEQUENCE_EDITOR: '/usr/bin/true' + BASE_REF: ${{ github.base_ref }} - uses: actions/setup-go@v6 with: From 2981aebec8e0750e09ce32fcfbaa22bd5223176e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 11:33:15 +0100 Subject: [PATCH 2250/2296] ci: pin all GitHub Actions to SHA hashes Pin all third-party action references to their full commit SHA to prevent supply-chain attacks through tag manipulation. A version comment is added after each SHA for maintainability. Reported by zizmor (unpinned-uses). --- .github/workflows/backport.yaml | 4 ++-- .github/workflows/codeql-analysis.yaml | 10 +++++----- .github/workflows/ensure-labels.yaml | 4 ++-- .github/workflows/functional-baremetal.yaml | 8 ++++---- .github/workflows/functional-basic.yaml | 8 ++++---- .github/workflows/functional-blockstorage.yaml | 8 ++++---- .github/workflows/functional-compute.yaml | 8 ++++---- .github/workflows/functional-containerinfra.yaml | 8 ++++---- .github/workflows/functional-dns.yaml | 8 ++++---- .github/workflows/functional-fwaas_v2.yaml | 8 ++++---- .github/workflows/functional-identity.yaml | 8 ++++---- .github/workflows/functional-image.yaml | 8 ++++---- .github/workflows/functional-keymanager.yaml | 8 ++++---- .github/workflows/functional-loadbalancer.yaml | 8 ++++---- .github/workflows/functional-messaging.yaml | 8 ++++---- .github/workflows/functional-networking.yaml | 8 ++++---- .github/workflows/functional-objectstorage.yaml | 8 ++++---- .github/workflows/functional-orchestration.yaml | 8 ++++---- .github/workflows/functional-placement.yaml | 8 ++++---- .github/workflows/functional-sharedfilesystems.yaml | 8 ++++---- .github/workflows/functional-workflow.yaml | 8 ++++---- .github/workflows/label-pr.yaml | 8 ++++---- .github/workflows/lint.yaml | 4 ++-- .github/workflows/unit.yaml | 8 ++++---- 24 files changed, 91 insertions(+), 91 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 88704f66df..2b403ddebf 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -38,7 +38,7 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') || contains(github.event.label.name, 'semver:patch') - uses: kiegroup/git-backporting@baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7 + uses: kiegroup/git-backporting@baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7 # v4.8.7 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -105,7 +105,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7 + uses: kiegroup/git-backporting@baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7 # v4.8.7 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index dabe73a738..3040b3cc41 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -18,23 +18,23 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false - name: Setup Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v4 + uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4 diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index 76f7e460f8..bb92db737b 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -13,10 +13,10 @@ jobs: issues: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false - - uses: micnncim/action-label-syncer@v1 + - uses: micnncim/action-label-syncer@3abd5ab72fda571e69fffd97bd4e0033dd5f495c # v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index d5bc6a0ab3..88aa1ff597 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -25,7 +25,7 @@ jobs: name: Ironic on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -60,7 +60,7 @@ jobs: # This change however is suitable longer-term as it is not necessary for SDK testing. - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -101,7 +101,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -123,7 +123,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-baremetal-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index df679f399d..9bd15f3c88 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -25,7 +25,7 @@ jobs: name: basic tests on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -48,14 +48,14 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} enabled_services: 's-account,s-container,s-object,s-proxy,openstack-cli-server' - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -76,7 +76,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-basic-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 7727e4c378..49e5c18b17 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -25,7 +25,7 @@ jobs: name: Cinder on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -52,7 +52,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -82,7 +82,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-blockstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index a53169e81c..ff40c8a3e8 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -25,7 +25,7 @@ jobs: name: Nova on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -52,7 +52,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -82,7 +82,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-compute-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index beaf44a1e2..1c5e4b2317 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -46,7 +46,7 @@ jobs: name: Magnum on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -73,7 +73,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -88,7 +88,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -109,7 +109,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-containerinfra-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 21f5b732d3..57748e80a6 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -31,7 +31,7 @@ jobs: name: Designate on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -58,7 +58,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -69,7 +69,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -90,7 +90,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-dns-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 082225f7f3..c5d3a5157f 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -29,7 +29,7 @@ jobs: name: FWaaS_v2 on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -66,7 +66,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -84,7 +84,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -105,7 +105,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-fwaas_v2-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 53259796b1..af17bc082f 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -25,7 +25,7 @@ jobs: name: Keystone on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -52,14 +52,14 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -80,7 +80,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-identity-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 95227639dd..cedc4625da 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -25,7 +25,7 @@ jobs: name: Glance on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -52,14 +52,14 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -80,7 +80,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-image-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 7f06589fe1..8a87c286bf 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -37,7 +37,7 @@ jobs: name: Barbican on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -64,7 +64,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -75,7 +75,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -96,7 +96,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-keymanager-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 50c7a3211b..ded8fa188b 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -36,7 +36,7 @@ jobs: name: Octavia on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -63,7 +63,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -75,7 +75,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -96,7 +96,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-loadbalancer-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 669b0d1448..783359b4ed 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -25,7 +25,7 @@ jobs: name: Zaqar on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -52,7 +52,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -62,7 +62,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -83,7 +83,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-messaging-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index ecc9961d38..c2332f921b 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -25,7 +25,7 @@ jobs: name: Neutron on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -62,7 +62,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -79,7 +79,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -100,7 +100,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-networking-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index d2327a618c..a49c12f759 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -25,7 +25,7 @@ jobs: name: Swift on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -52,7 +52,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -65,7 +65,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -86,7 +86,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-objectstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 0c6ff30d26..f805fa6ff0 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -25,7 +25,7 @@ jobs: name: Heat on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -52,7 +52,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -82,7 +82,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-orchestration-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 0520b3e40e..b82ee3f74f 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -25,7 +25,7 @@ jobs: name: Placement on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -52,14 +52,14 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} enabled_services: "openstack-cli-server" - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -80,7 +80,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-placement-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 9847f1c2fd..7e6508d6f3 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -31,7 +31,7 @@ jobs: name: Manila on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -58,7 +58,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -83,7 +83,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -104,7 +104,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-sharedfilesystems-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 236c356bf5..8f29c75033 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -28,7 +28,7 @@ jobs: name: Mistral on Deploy OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false @@ -55,7 +55,7 @@ jobs: - name: Deploy devstack if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 with: branch: ${{ matrix.openstack_version }} conf_overrides: | @@ -64,7 +64,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -85,7 +85,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: functional-workflow-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index a15e54f7f0..f5f4256eb6 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -14,7 +14,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} @@ -30,7 +30,7 @@ jobs: GIT_SEQUENCE_EDITOR: '/usr/bin/true' BASE_REF: ${{ github.base_ref }} - - uses: actions/setup-go@v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -40,7 +40,7 @@ jobs: # 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@60c4206be8f84348ebda2a3e0c3ac9cb54b8f685 + uses: joelanford/go-apidiff@60c4206be8f84348ebda2a3e0c3ac9cb54b8f685 # v0.8.3 # 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. @@ -116,4 +116,4 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v6 + - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 84f2280f20..b019a83b55 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false - - uses: actions/setup-go@v6 + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index a5858e7cb8..c938fd0a8c 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -15,11 +15,11 @@ jobs: fail-fast: false steps: - name: Checkout Gophercloud - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false - name: Setup Go - uses: actions/setup-go@v6 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: go-version-file: 'go.mod' cache: true @@ -34,7 +34,7 @@ jobs: make unit make coverage - name: Check coverage - uses: coverallsapp/github-action@v2 + uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2 with: file: cover.out parallel: true @@ -46,6 +46,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Store coverage results - uses: coverallsapp/github-action@v2 + uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2 with: parallel-finished: true From e1da83f5af8c00d5668f3cb84a6946f8a9fde0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 11:33:45 +0100 Subject: [PATCH 2251/2296] ci: suppress secrets-outside-env warnings in backport workflow Add inline zizmor ignore comments for secrets used outside a dedicated GitHub Environment. Fixing this properly requires external repository configuration (creating a GitHub Environment) which cannot be done from code alone. Reported by zizmor (secrets-outside-env). --- .github/workflows/backport.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 2b403ddebf..1c4b6fe420 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -31,8 +31,8 @@ jobs: id: generate_token uses: getsentry/action-github-app-token@a0061014b82a6a5d6aeeb3b824aced47e3c3a7ef with: - app_id: ${{ secrets.BACKPORT_APP_ID }} - private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} + app_id: ${{ secrets.BACKPORT_APP_ID }} # zizmor: ignore[secrets-outside-env] + private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] - name: Backporting if: > @@ -96,8 +96,8 @@ jobs: id: generate_token uses: getsentry/action-github-app-token@a0061014b82a6a5d6aeeb3b824aced47e3c3a7ef with: - app_id: ${{ secrets.BACKPORT_APP_ID }} - private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} + app_id: ${{ secrets.BACKPORT_APP_ID }} # zizmor: ignore[secrets-outside-env] + private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] - name: Backporting if: > From 4a163e8fb494ef301400a68329a2459abfa782a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 14:10:47 +0100 Subject: [PATCH 2252/2296] ci: replace pull_request_target with pull_request in check-pr-labels The hold job has zero permissions, no secrets access, and no code checkout, so the privileged pull_request_target trigger is unnecessary. The merge_group trigger remains as the tamper-proof gate for the merge queue. Reported by zizmor (dangerous-triggers). --- .github/workflows/check-pr-labels.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-pr-labels.yaml b/.github/workflows/check-pr-labels.yaml index 2b30487abd..a81d2b223a 100644 --- a/.github/workflows/check-pr-labels.yaml +++ b/.github/workflows/check-pr-labels.yaml @@ -1,7 +1,7 @@ name: Ready on: merge_group: - pull_request_target: + pull_request: types: - labeled - opened From 01de474dd045d77a885c6f59437796271ac89b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 14:11:00 +0100 Subject: [PATCH 2253/2296] ci: suppress dangerous-triggers warning in backport workflow The pull_request_target trigger is required here to access secrets for the backport bot token. The workflow is safe: it only runs on merged PRs (github.event.pull_request.merged guard), does not checkout or execute any PR code, and uses a dedicated GitHub App token for creating backport PRs. Reported by zizmor (dangerous-triggers). --- .github/workflows/backport.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index 1c4b6fe420..b23910b5d4 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -1,7 +1,9 @@ name: Pull Request backporting on: - pull_request_target: + # Safe: only runs on merged PRs, does not checkout PR code, and + # needs secrets to generate the backport bot token. + pull_request_target: # zizmor: ignore[dangerous-triggers] types: - closed - labeled From 031765f054ea80a5b06f815aff5692dc722e0806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 14:19:41 +0100 Subject: [PATCH 2254/2296] ci: split semver analysis from label-pr to fix dangerous-triggers Extract the semver analysis (checkout, rebase, go-apidiff) into a new semver.yaml workflow triggered by pull_request, which runs in the unprivileged fork context. The privileged labeling logic stays in label-pr.yaml as a new semver-label job triggered by workflow_run on completion of the analysis. PR number and base ref are resolved via the GitHub API from the workflow_run head SHA. The edits job (actions/labeler) remains on pull_request_target with an event_name guard. Both triggers are suppressed with inline comments: neither job checks out or executes PR code. Reported by zizmor (dangerous-triggers). --- .github/workflows/label-pr.yaml | 115 +++++++++++++++++--------------- .github/workflows/semver.yaml | 62 +++++++++++++++++ 2 files changed, 124 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/semver.yaml diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index f5f4256eb6..d30c28ae99 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -1,55 +1,76 @@ name: Label PR on: - pull_request_target: + # Safe: the edits job does not checkout or execute any PR code. + # It only runs the pinned actions/labeler action which reads event + # metadata and the base branch .github/labeler.yml configuration. + pull_request_target: # zizmor: ignore[dangerous-triggers] types: - opened - synchronize - reopened + # Safe: the semver-label job never checks out or executes PR code. + # It only reads a data artifact produced by the unprivileged + # "Semver analysis" workflow and uses the GitHub API to apply labels. + workflow_run: # zizmor: ignore[dangerous-triggers] + workflows: ["Semver analysis"] + types: + - completed + +permissions: {} jobs: - semver: + semver-label: + if: github.event_name == 'workflow_run' + runs-on: ubuntu-latest permissions: - contents: read + actions: read issues: write pull-requests: write - runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - token: ${{ secrets.GITHUB_TOKEN }} - persist-credentials: false + - name: Get PR number and base ref + id: pr + run: | + PR_DATA=$(gh api "repos/$REPO/commits/$HEAD_SHA/pulls" --jq '.[0] | {number, base_ref: .base.ref}') + PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number') + BASE_REF=$(echo "$PR_DATA" | jq -r '.base_ref') + if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" = "null" ]; then + echo "Could not determine PR number" >&2 + exit 1 + fi + echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT" + echo "base-ref=$BASE_REF" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} - - name: Rebase the PR against origin/github.base_ref to ensure actual API compatibility + - name: Report failure + if: github.event.workflow_run.conclusion == 'failure' run: | - git config --global user.email "localrebase@gophercloud.io" - git config --global user.name "Local rebase" - git rebase -i origin/$BASE_REF + gh pr edit "$NUMBER" --remove-label "semver:major,semver:minor,semver:patch,backport-v2,backport-v1" + gh issue comment "$NUMBER" --body "$BODY" env: - GIT_SEQUENCE_EDITOR: '/usr/bin/true' - BASE_REF: ${{ github.base_ref }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + NUMBER: ${{ steps.pr.outputs.number }} + BODY: > + Failed to assess the semver bump. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) for details. - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - name: Download semver results + if: github.event.workflow_run.conclusion == 'success' + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: - go-version-file: 'go.mod' - cache: true + name: semver-results + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} - - 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@60c4206be8f84348ebda2a3e0c3ac9cb54b8f685 # v0.8.3 - - # 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.outputs.semver-type != 'major' - run: exit 1 + - name: Read semver type + if: github.event.workflow_run.conclusion == 'success' + id: semver + run: echo "type=$(cat semver-type)" >> "$GITHUB_OUTPUT" - name: Add label semver:patch - if: steps.go-apidiff.outputs.semver-type == 'patch' + if: github.event.workflow_run.conclusion == 'success' && steps.semver.outputs.type == 'patch' run: | LABELS="semver:patch" REMOVE_LABELS="semver:major,semver:minor" @@ -65,11 +86,11 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - NUMBER: ${{ github.event.pull_request.number }} - BASE_REF: ${{ github.base_ref }} + NUMBER: ${{ steps.pr.outputs.number }} + BASE_REF: ${{ steps.pr.outputs.base-ref }} - name: Add label semver:minor - if: steps.go-apidiff.outputs.semver-type == 'minor' + if: github.event.workflow_run.conclusion == 'success' && steps.semver.outputs.type == 'minor' run: | LABELS="semver:minor" REMOVE_LABELS="semver:major,semver:patch" @@ -86,34 +107,22 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - NUMBER: ${{ github.event.pull_request.number }} - BASE_REF: ${{ github.base_ref }} + NUMBER: ${{ steps.pr.outputs.number }} + BASE_REF: ${{ steps.pr.outputs.base-ref }} - name: Add label semver:major - if: steps.go-apidiff.outputs.semver-type == 'major' + if: github.event.workflow_run.conclusion == 'success' && steps.semver.outputs.type == 'major' run: gh pr edit "$NUMBER" --add-label "semver:major" --remove-label "semver:minor,semver:patch,backport-v2,backport-v1" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - NUMBER: ${{ github.event.pull_request.number }} - - - name: Report failure - if: failure() - run: | - gh pr edit "$NUMBER" --remove-label "semver:major,semver:minor,semver:patch,backport-v2,backport-v1" - gh issue comment "$NUMBER" --body "$BODY" - exit 1 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_REPO: ${{ github.repository }} - NUMBER: ${{ github.event.pull_request.number }} - BODY: > - Failed to assess the semver bump. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. + NUMBER: ${{ steps.pr.outputs.number }} edits: + if: github.event_name == 'pull_request_target' + runs-on: ubuntu-latest permissions: contents: read pull-requests: write - runs-on: ubuntu-latest steps: - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6 diff --git a/.github/workflows/semver.yaml b/.github/workflows/semver.yaml new file mode 100644 index 0000000000..5d54e77299 --- /dev/null +++ b/.github/workflows/semver.yaml @@ -0,0 +1,62 @@ +name: Semver analysis +on: + pull_request: + types: + - opened + - synchronize + - reopened + +permissions: + contents: read + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Rebase the PR against 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/$BASE_REF + env: + GIT_SEQUENCE_EDITOR: '/usr/bin/true' + BASE_REF: ${{ github.base_ref }} + + - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + with: + go-version-file: 'go.mod' + cache: true + + - 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@60c4206be8f84348ebda2a3e0c3ac9cb54b8f685 # v0.8.3 + + # 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.outputs.semver-type != 'major' + run: exit 1 + + - name: Save semver result + if: always() + run: | + mkdir -p semver-results + echo "$SEMVER_TYPE" > semver-results/semver-type + env: + SEMVER_TYPE: ${{ steps.go-apidiff.outputs.semver-type }} + + - name: Upload semver results + if: always() + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: semver-results + path: semver-results/ From 54ebc3629b6c3e0952014f3c3b4c37ee9022aab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 25 Mar 2026 14:40:16 +0100 Subject: [PATCH 2255/2296] ci: add zizmor security scanner for GitHub Actions workflows Run zizmor on pushes to master and pull requests that modify workflow files. Results are uploaded as SARIF to GitHub's security-events API for code scanning integration. --- .github/workflows/zizmor.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/zizmor.yaml diff --git a/.github/workflows/zizmor.yaml b/.github/workflows/zizmor.yaml new file mode 100644 index 0000000000..494a54ed80 --- /dev/null +++ b/.github/workflows/zizmor.yaml @@ -0,0 +1,28 @@ +name: zizmor + +on: + push: + branches: + - master + paths: + - '.github/workflows/**' + pull_request: + paths: + - '.github/workflows/**' + +permissions: {} + +jobs: + zizmor: + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 From 69bf30cc747dc374970feb9b794118b143bf96e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 26 Mar 2026 08:05:21 +0100 Subject: [PATCH 2256/2296] Fix TestCreateTempURL flaky test by removing hardcoded port dependency SetupPersistentPortHTTP used t.Errorf on bind failure, which didn't stop execution and caused a nil pointer panic on server.Start(). Changed to t.Fatalf to fail cleanly. Replaced SetupPersistentPortHTTP with SetupHTTP in objectstorage temp URL tests since the HMAC signature only covers the path, not the host:port. This eliminates the flaky port-already-in-use failures in CI. Fixes #3661. --- .../objectstorage/v1/objects/testing/requests_test.go | 8 +++----- testhelper/http_responses.go | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go index 004b798ba7..b49cf63a7f 100644 --- a/openstack/objectstorage/v1/objects/testing/requests_test.go +++ b/openstack/objectstorage/v1/objects/testing/requests_test.go @@ -93,8 +93,7 @@ func TestContainerNames(t *testing.T) { th.CheckErr(t, res.Err, &tc.expectedError) }) t.Run("createTempURL", func(t *testing.T) { - port := 33201 - fakeServer := th.SetupPersistentPortHTTP(t, port) + fakeServer := th.SetupHTTP() defer fakeServer.Teardown() // Handle fetching of secret key inside of CreateTempURL @@ -503,8 +502,7 @@ func TestObjectCreateParamsWithSeek(t *testing.T) { } func TestCreateTempURL(t *testing.T) { - port := 33200 - fakeServer := th.SetupPersistentPortHTTP(t, port) + fakeServer := th.SetupHTTP() defer fakeServer.Teardown() // Handle fetching of secret key inside of CreateTempURL @@ -522,7 +520,7 @@ func TestCreateTempURL(t *testing.T) { sig := "89be454a9c7e2e9f3f50a8441815e0b5801cba5b" expiry := "1593565980" - expectedURL := fmt.Sprintf("http://127.0.0.1:%v/v1/testContainer/testObject%%2FtestFile.txt?temp_url_sig=%v&temp_url_expires=%v", port, sig, expiry) + expectedURL := fmt.Sprintf("%sv1/testContainer/testObject%%2FtestFile.txt?temp_url_sig=%v&temp_url_expires=%v", fakeServer.Endpoint(), sig, expiry) th.AssertNoErr(t, err) th.AssertEquals(t, expectedURL, tempURL) diff --git a/testhelper/http_responses.go b/testhelper/http_responses.go index f57645e441..d0034a5a7b 100644 --- a/testhelper/http_responses.go +++ b/testhelper/http_responses.go @@ -50,7 +50,7 @@ func SetupPersistentPortHTTP(t *testing.T, port int) FakeServer { server := httptest.NewUnstartedServer(mux) l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) if err != nil { - t.Errorf("Failed to listen to 127.0.0.1:%d: %s", port, err) + t.Fatalf("Failed to listen to 127.0.0.1:%d: %s", port, err) } server.Listener = l server.Start() From 2254df15bd217b958c0cb019d1933b11c2b41c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 26 Mar 2026 08:29:43 +0100 Subject: [PATCH 2257/2296] Replace t.Errorf with th.AssertNoErr to prevent nil pointer panics in tests Several tests used t.Errorf after fallible operations, which logs the error but doesn't stop execution. This caused nil pointer dereferences when subsequent code accessed the result. Replace with th.AssertNoErr which calls t.Fatal internally, stopping the test immediately on error. --- .../sharedfilesystems/v2/replicas_test.go | 8 ++------ .../v2/securityservices_test.go | 13 ++++-------- .../v2/sharenetworks_test.go | 12 +++-------- .../sharedfilesystems/v2/shares_test.go | 20 +++++-------------- .../sharedfilesystems/v2/snapshots_test.go | 12 +++-------- .../v3/tokens/testing/requests_test.go | 16 ++++----------- 6 files changed, 21 insertions(+), 60 deletions(-) diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go index 8432afeb77..e662781360 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/replicas_test.go @@ -181,15 +181,11 @@ func TestReplicaExportLocations(t *testing.T) { } exportLocations, err = replicas.ListExportLocations(context.TODO(), client, activeReplicaID).Extract() - if err != nil { - t.Errorf("Unable to list replica export locations: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, exportLocations) exportLocation, err := replicas.GetExportLocation(context.TODO(), client, activeReplicaID, exportLocations[0].ID).Extract() - if err != nil { - t.Errorf("Unable to get replica export location: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, exportLocation) // unset CreatedAt and UpdatedAt exportLocation.CreatedAt = time.Time{} diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go index a149a83324..abbb0b557c 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/securityservices_test.go @@ -9,6 +9,7 @@ import ( "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/securityservices" + th "github.com/gophercloud/gophercloud/v2/testhelper" ) func TestSecurityServiceCreateDelete(t *testing.T) { @@ -23,9 +24,7 @@ func TestSecurityServiceCreateDelete(t *testing.T) { } newSecurityService, err := securityservices.Get(context.TODO(), client, securityService.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve the security service: %v", err) - } + th.AssertNoErr(t, err) if newSecurityService.Name != securityService.Name { t.Fatalf("Security service name was expeted to be: %s", securityService.Name) @@ -125,14 +124,10 @@ func TestSecurityServiceUpdate(t *testing.T) { } _, err = securityservices.Update(context.TODO(), client, securityService.ID, options).Extract() - if err != nil { - t.Errorf("Unable to update the security service: %v", err) - } + th.AssertNoErr(t, err) newSecurityService, err := securityservices.Get(context.TODO(), client, securityService.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve the security service: %v", err) - } + th.AssertNoErr(t, err) if newSecurityService.Name != name { t.Fatalf("Security service name was expeted to be: %s", name) diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go index c6a9bf1530..c75c07a26b 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/sharenetworks_test.go @@ -25,9 +25,7 @@ func TestShareNetworkCreateDestroy(t *testing.T) { } newShareNetwork, err := sharenetworks.Get(context.TODO(), client, shareNetwork.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve shareNetwork: %v", err) - } + th.AssertNoErr(t, err) if newShareNetwork.Name != shareNetwork.Name { t.Fatalf("Share network name was expeted to be: %s", shareNetwork.Name) @@ -56,9 +54,7 @@ func TestShareNetworkUpdate(t *testing.T) { } expectedShareNetwork, err := sharenetworks.Get(context.TODO(), client, shareNetwork.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve shareNetwork: %v", err) - } + th.AssertNoErr(t, err) name := "NewName" description := "" @@ -76,9 +72,7 @@ func TestShareNetworkUpdate(t *testing.T) { } updatedShareNetwork, err := sharenetworks.Get(context.TODO(), client, shareNetwork.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve shareNetwork: %v", err) - } + th.AssertNoErr(t, err) // Update time has to be set in order to get the assert equal to pass expectedShareNetwork.UpdatedAt = updatedShareNetwork.UpdatedAt diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go index 3d8bb0d32e..7247aa8984 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/shares_test.go @@ -53,15 +53,11 @@ func TestShareExportLocations(t *testing.T) { client.Microversion = "2.9" exportLocations, err := shares.ListExportLocations(context.TODO(), client, share.ID).Extract() - if err != nil { - t.Errorf("Unable to list share export locations: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, exportLocations) exportLocation, err := shares.GetExportLocation(context.TODO(), client, share.ID, exportLocations[0].ID).Extract() - if err != nil { - t.Errorf("Unable to get share export location: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, exportLocation) th.AssertEquals(t, exportLocations[0], *exportLocation) } @@ -80,9 +76,7 @@ func TestShareUpdate(t *testing.T) { defer DeleteShare(t, client, share) expectedShare, err := shares.Get(context.TODO(), client, share.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve share: %v", err) - } + th.AssertNoErr(t, err) name := "NewName" description := "" @@ -98,14 +92,10 @@ func TestShareUpdate(t *testing.T) { expectedShare.IsPublic = iFalse _, err = shares.Update(context.TODO(), client, share.ID, options).Extract() - if err != nil { - t.Errorf("Unable to update share: %v", err) - } + th.AssertNoErr(t, err) updatedShare, err := shares.Get(context.TODO(), client, share.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve share: %v", err) - } + th.AssertNoErr(t, err) // Update time has to be set in order to get the assert equal to pass expectedShare.UpdatedAt = updatedShare.UpdatedAt diff --git a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go index 46b7c6d488..3c997d3c49 100644 --- a/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go +++ b/internal/acceptance/openstack/sharedfilesystems/v2/snapshots_test.go @@ -65,9 +65,7 @@ func TestSnapshotUpdate(t *testing.T) { defer DeleteSnapshot(t, client, snapshot) expectedSnapshot, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve snapshot: %v", err) - } + th.AssertNoErr(t, err) name := "NewName" description := "" @@ -80,14 +78,10 @@ func TestSnapshotUpdate(t *testing.T) { expectedSnapshot.Description = description _, err = snapshots.Update(context.TODO(), client, snapshot.ID, options).Extract() - if err != nil { - t.Errorf("Unable to update snapshot: %v", err) - } + th.AssertNoErr(t, err) updatedSnapshot, err := snapshots.Get(context.TODO(), client, snapshot.ID).Extract() - if err != nil { - t.Errorf("Unable to retrieve snapshot: %v", err) - } + th.AssertNoErr(t, err) tools.PrintResource(t, snapshot) diff --git a/openstack/identity/v3/tokens/testing/requests_test.go b/openstack/identity/v3/tokens/testing/requests_test.go index c8c5d49805..d099ef11ca 100644 --- a/openstack/identity/v3/tokens/testing/requests_test.go +++ b/openstack/identity/v3/tokens/testing/requests_test.go @@ -383,18 +383,14 @@ func TestCreateUserIDPasswordTrustID(t *testing.T) { rsp := tokens.Create(context.TODO(), client.ServiceClient(fakeServer), &ao) token, err := rsp.Extract() - if err != nil { - t.Errorf("Create returned an error: %v", err) - } + th.AssertNoErr(t, err) expectedToken := &tokens.Token{ ExpiresAt: time.Date(2024, 02, 28, 12, 10, 39, 0, time.UTC), } th.AssertDeepEquals(t, expectedToken, token) trust, err := rsp.ExtractTrust() - if err != nil { - t.Errorf("ExtractTrust returned an error: %v", err) - } + th.AssertNoErr(t, err) expectedTrust := &tokens.Trust{ ID: "95946f9eef864fdc993079d8fe3e5747", Impersonation: false, @@ -676,9 +672,7 @@ func TestGetRequest(t *testing.T) { }) token, err := tokens.Get(context.TODO(), &client, "abcdef12345", nil).Extract() - if err != nil { - t.Errorf("Info returned an error: %v", err) - } + th.AssertNoErr(t, err) expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014") if token.ExpiresAt != expected { @@ -795,9 +789,7 @@ func TestGetRequestWithAccessRules(t *testing.T) { AccessRulesVersion: "1", } token, err := tokens.Get(context.TODO(), &client, "abcdef12345", getOpts).Extract() - if err != nil { - t.Errorf("Get returned an error: %v", err) - } + th.AssertNoErr(t, err) expected, _ := time.Parse(time.UnixDate, "Fri Aug 29 13:10:01 UTC 2014") if token.ExpiresAt != expected { From 7850e65b94d1ed4c4a57313bf297d5ac5d5e56ef Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Wed, 25 Mar 2026 13:18:31 +0100 Subject: [PATCH 2258/2296] Implement READ operations on Placement traits Related: #526 API reference: https://docs.openstack.org/api-ref/placement/#traits Code reference: https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/trait.py#L108-L124 https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/trait.py#L152-L178 Signed-off-by: Dominik Danelski --- .../openstack/placement/v1/traits_test.go | 98 ++++++++++++++++ openstack/placement/v1/traits/doc.go | 42 +++++++ openstack/placement/v1/traits/requests.go | 60 ++++++++++ openstack/placement/v1/traits/results.go | 38 ++++++ openstack/placement/v1/traits/testing/doc.go | 2 + .../v1/traits/testing/fixtures_test.go | 108 ++++++++++++++++++ .../v1/traits/testing/requests_test.go | 101 ++++++++++++++++ openstack/placement/v1/traits/urls.go | 15 +++ 8 files changed, 464 insertions(+) create mode 100644 internal/acceptance/openstack/placement/v1/traits_test.go create mode 100644 openstack/placement/v1/traits/doc.go create mode 100644 openstack/placement/v1/traits/requests.go create mode 100644 openstack/placement/v1/traits/results.go create mode 100644 openstack/placement/v1/traits/testing/doc.go create mode 100644 openstack/placement/v1/traits/testing/fixtures_test.go create mode 100644 openstack/placement/v1/traits/testing/requests_test.go create mode 100644 openstack/placement/v1/traits/urls.go diff --git a/internal/acceptance/openstack/placement/v1/traits_test.go b/internal/acceptance/openstack/placement/v1/traits_test.go new file mode 100644 index 0000000000..3099da80df --- /dev/null +++ b/internal/acceptance/openstack/placement/v1/traits_test.go @@ -0,0 +1,98 @@ +//go:build acceptance || placement || traits + +package v1 + +import ( + "context" + "net/http" + "slices" + "strings" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/traits" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestTraitsList(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + clients.SkipRelease(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + allPages, err := traits.List(client, traits.ListOpts{}).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allTraits, err := traits.ExtractTraits(allPages) + th.AssertNoErr(t, err) + + // Ensure COMPUTE_NODE is in the list + // os-traits never removes traits, so this should always pass + th.AssertEquals(t, true, slices.Contains(allTraits, "COMPUTE_NODE")) +} + +func TestTraitGet(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + clients.SkipRelease(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + // Verify that Get confirms the existence of the COMPUTE_NODE trait + // os-traits never removes traits, so this should always pass + err = traits.Get(context.TODO(), client, "COMPUTE_NODE").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestTraitGetNegative(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + clients.SkipRelease(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + // Verify that Get returns an error for a non-existent trait + err = traits.Get(context.TODO(), client, "CUSTOM_NON_EXISTENT_TRAIT").ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestTraitsListFiltering(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipRelease(t, "stable/mitaka") + clients.SkipRelease(t, "stable/newton") + clients.SkipRelease(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + // os-traits never removes traits, so this should always pass + listOpts := traits.ListOpts{ + Name: "startswith:HW_", + } + + allPages, err := traits.List(client, listOpts).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + filteredTraits, err := traits.ExtractTraits(allPages) + th.AssertNoErr(t, err) + + for _, trait := range filteredTraits { + th.AssertEquals(t, true, strings.HasPrefix(trait, "HW_")) + } +} diff --git a/openstack/placement/v1/traits/doc.go b/openstack/placement/v1/traits/doc.go new file mode 100644 index 0000000000..e11afc158e --- /dev/null +++ b/openstack/placement/v1/traits/doc.go @@ -0,0 +1,42 @@ +/* +Package traits lists traits from the OpenStack Placement service. + +Traits API requests are available starting from microversion 1.6. + +Example to list traits + + placementClient.Microversion = "1.6" + + allPages, err := traits.List(placementClient, traits.ListOpts{}).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allTraits, err := traits.ExtractTraits(allPages) + if err != nil { + panic(err) + } + + for _, t := range allTraits { + fmt.Println(t) + } + +Example to check if a trait exists + + placementClient.Microversion = "1.6" + + traitName := "CUSTOM_HW_FPGA_CLASS1" + err := traits.Get(context.TODO(), placementClient, traitName).ExtractErr() + if err != nil { + if gophercloud.ResponseCodeIs(err, http.StatusNotFound) { + // 404 Not Found - The trait does not exist + fmt.Println("Trait does not exist.") + } else { + // Another error occurred + panic(err) + } + } else { + fmt.Println("Trait exists!") + } +*/ +package traits diff --git a/openstack/placement/v1/traits/requests.go b/openstack/placement/v1/traits/requests.go new file mode 100644 index 0000000000..82fd8a0dbb --- /dev/null +++ b/openstack/placement/v1/traits/requests.go @@ -0,0 +1,60 @@ +package traits + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to +// the List request. +type ListOptsBuilder interface { + ToTraitListQuery() (string, error) +} + +// ListOpts allows the filtering of traits. Filtering is achieved by passing in struct +// field values that map to the trait attributes you want to see returned. +type ListOpts struct { + // Name is a string used to filter traits by name. + // It supports startswith operator to filter the traits whose name begins with + // a specific prefix, e.g. name=startswith:CUSTOM + // in operator filters the traits whose name is in the specified list, + // e.g. name=in:HW_CPU_X86_AVX,HW_CPU_X86_SSE,HW_CPU_X86_INVALID_FEATURE + Name string `q:"name"` + + // Associated is a boolean used to filter traits by whether they are associated with + // at least one resource provider. + Associated *bool `q:"associated"` +} + +// ToTraitListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToTraitListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List retrieves a list of traits. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + + if opts != nil { + query, err := opts.ToTraitListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return TraitsPage{pagination.SinglePageBase(r)} + }) +} + +// Get confirms the existence of a trait. +func Get(ctx context.Context, client *gophercloud.ServiceClient, traitName string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, traitName), nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/placement/v1/traits/results.go b/openstack/placement/v1/traits/results.go new file mode 100644 index 0000000000..5e638cf226 --- /dev/null +++ b/openstack/placement/v1/traits/results.go @@ -0,0 +1,38 @@ +package traits + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// GetResult is the response from a Get operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type GetResult struct { + gophercloud.ErrResult +} + +// TraitsPage contains a single page of all traits from a List call. +type TraitsPage struct { + pagination.SinglePageBase +} + +// IsEmpty satisfies the IsEmpty method of the Page interface. It returns true +// if a List contains no results. +func (r TraitsPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + traits, err := ExtractTraits(r) + return len(traits) == 0, err +} + +// ExtractTraits takes a List result and extracts the collection of traits +// returned by the API. +func ExtractTraits(p pagination.Page) ([]string, error) { + var s struct { + Traits []string `json:"traits"` + } + err := (p.(TraitsPage)).ExtractInto(&s) + return s.Traits, err +} diff --git a/openstack/placement/v1/traits/testing/doc.go b/openstack/placement/v1/traits/testing/doc.go new file mode 100644 index 0000000000..3231ffdf0e --- /dev/null +++ b/openstack/placement/v1/traits/testing/doc.go @@ -0,0 +1,2 @@ +// placement traits +package testing diff --git a/openstack/placement/v1/traits/testing/fixtures_test.go b/openstack/placement/v1/traits/testing/fixtures_test.go new file mode 100644 index 0000000000..6c37649d18 --- /dev/null +++ b/openstack/placement/v1/traits/testing/fixtures_test.go @@ -0,0 +1,108 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const PresentTrait = "CUSTOM_HW_FPGA_CLASS1" +const AbsentTrait = "NON_EXISTENT_TRAIT" + +const TraitsListResultAll = ` +{ + "traits": [ + "CUSTOM_HW_FPGA_CLASS1", + "CUSTOM_HW_FPGA_CLASS2", + "HW_CPU_X86_AVX" + ] +}` + +const TraitsListFilteredCustomResult = ` +{ + "traits": [ + "CUSTOM_HW_FPGA_CLASS1", + "CUSTOM_HW_FPGA_CLASS2" + ] +}` + +const TraitsListFilteredAssociatedResult = TraitsListResultAll + +var ExpectedTraitsListResultAll = []string{ + "CUSTOM_HW_FPGA_CLASS1", + "CUSTOM_HW_FPGA_CLASS2", + "HW_CPU_X86_AVX", +} + +var ExpectedTraitsListFilteredNameResult = []string{ + "CUSTOM_HW_FPGA_CLASS1", + "CUSTOM_HW_FPGA_CLASS2", +} + +var ExpectedTraitsListFilteredAssociatedResult = ExpectedTraitsListResultAll + +func HandleListTraitsAll(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits", + 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.Fprint(w, TraitsListResultAll) + }) +} + +func HandleListTraitsFilteredName(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + th.TestFormValues(t, r, map[string]string{"name": "startswith:CUSTOM"}) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, TraitsListFilteredCustomResult) + }) +} + +func HandleListTraitsFilteredAssociated(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + th.TestFormValues(t, r, map[string]string{"associated": "true"}) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, TraitsListFilteredAssociatedResult) + }) +} + +func HandleGetTraitSuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+PresentTrait, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +func HandleGetTraitNotFound(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+AbsentTrait, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNotFound) + }) +} diff --git a/openstack/placement/v1/traits/testing/requests_test.go b/openstack/placement/v1/traits/testing/requests_test.go new file mode 100644 index 0000000000..39642afb91 --- /dev/null +++ b/openstack/placement/v1/traits/testing/requests_test.go @@ -0,0 +1,101 @@ +package testing + +import ( + "context" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/traits" + + "github.com/gophercloud/gophercloud/v2/pagination" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestListTraitsAll(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListTraitsAll(t, fakeServer) + + count := 0 + err := traits.List(client.ServiceClient(fakeServer), traits.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + count++ + + actual, err := traits.ExtractTraits(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedTraitsListResultAll, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + + th.AssertEquals(t, 1, count) +} + +func TestListTraitsFilteredName(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListTraitsFilteredName(t, fakeServer) + + count := 0 + err := traits.List(client.ServiceClient(fakeServer), traits.ListOpts{Name: "startswith:CUSTOM"}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + count++ + + actual, err := traits.ExtractTraits(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedTraitsListFilteredNameResult, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + + th.AssertEquals(t, 1, count) +} + +func TestListTraitsFilteredAssociated(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListTraitsFilteredAssociated(t, fakeServer) + + count := 0 + associated := true + err := traits.List(client.ServiceClient(fakeServer), traits.ListOpts{Associated: &associated}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { + count++ + + actual, err := traits.ExtractTraits(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedTraitsListFilteredAssociatedResult, actual) + + return true, nil + }) + + th.AssertNoErr(t, err) + + th.AssertEquals(t, 1, count) +} + +func TestGetTraitSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleGetTraitSuccess(t, fakeServer) + + err := traits.Get(context.TODO(), client.ServiceClient(fakeServer), PresentTrait).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestGetTraitNotFound(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleGetTraitNotFound(t, fakeServer) + + err := traits.Get(context.TODO(), client.ServiceClient(fakeServer), AbsentTrait).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} diff --git a/openstack/placement/v1/traits/urls.go b/openstack/placement/v1/traits/urls.go new file mode 100644 index 0000000000..a6ea7757a1 --- /dev/null +++ b/openstack/placement/v1/traits/urls.go @@ -0,0 +1,15 @@ +package traits + +import "github.com/gophercloud/gophercloud/v2" + +const ( + apiName = "traits" +) + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiName) +} + +func getURL(client *gophercloud.ServiceClient, traitName string) string { + return client.ServiceURL(apiName, traitName) +} From 3be82725db07df15ec7c435e9fa7cbfe133917b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:03:30 +0000 Subject: [PATCH 2259/2296] build(deps): bump kiegroup/git-backporting from 4.8.7 to 4.9.1 Bumps [kiegroup/git-backporting](https://github.com/kiegroup/git-backporting) from 4.8.7 to 4.9.1. - [Release notes](https://github.com/kiegroup/git-backporting/releases) - [Changelog](https://github.com/kiegroup/git-backporting/blob/main/CHANGELOG.md) - [Commits](https://github.com/kiegroup/git-backporting/compare/baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7...08da0b07ef2330d189f6074ec8db736b3aa9f465) --- updated-dependencies: - dependency-name: kiegroup/git-backporting dependency-version: 4.9.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index b23910b5d4..ba02bce85b 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -40,7 +40,7 @@ jobs: if: > contains(github.event.pull_request.labels.*.name, 'semver:patch') || contains(github.event.label.name, 'semver:patch') - uses: kiegroup/git-backporting@baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7 # v4.8.7 + uses: kiegroup/git-backporting@08da0b07ef2330d189f6074ec8db736b3aa9f465 # v4.9.1 with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} @@ -107,7 +107,7 @@ jobs: || contains(github.event.pull_request.labels.*.name, 'semver:minor') || contains(github.event.label.name, 'semver:patch') || contains(github.event.label.name, 'semver:minor') - uses: kiegroup/git-backporting@baae3fe1e3c71bc6b1a2699b3bc1e153a19d5ac7 # v4.8.7 + uses: kiegroup/git-backporting@08da0b07ef2330d189f6074ec8db736b3aa9f465 # v4.9.1 with: target-branch: v2 pull-request: ${{ github.event.pull_request.url }} From 6f68ef2199ad229d1320e0b9d8035bfc0e092d7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:03:58 +0000 Subject: [PATCH 2260/2296] build(deps): bump actions/upload-artifact from 6.0.0 to 7.0.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/b7c566a772e6b6bfb58ed0dc250532a479d7789f...bbbca2ddaa5d8feaa63e36b76fdaad77386f024f) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- .github/workflows/semver.yaml | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 88aa1ff597..7021620482 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -123,7 +123,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-baremetal-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 9bd15f3c88..8964c0b550 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -76,7 +76,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-basic-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 49e5c18b17..4d981062d1 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -82,7 +82,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-blockstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index ff40c8a3e8..46c7cb41bd 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -82,7 +82,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-compute-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 1c5e4b2317..294624b0f1 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -109,7 +109,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-containerinfra-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 57748e80a6..7abe11855f 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -90,7 +90,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-dns-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index c5d3a5157f..b3f381756e 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -105,7 +105,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-fwaas_v2-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index af17bc082f..7bdbf7ab9c 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -80,7 +80,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-identity-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index cedc4625da..8254a7d0c8 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -80,7 +80,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-image-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 8a87c286bf..fee5e6603b 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -96,7 +96,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-keymanager-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index ded8fa188b..3886f48146 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -96,7 +96,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-loadbalancer-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 783359b4ed..fc7de2d4ad 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -83,7 +83,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-messaging-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index c2332f921b..1ff2ce14fc 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -100,7 +100,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-networking-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index a49c12f759..04f1a6d56a 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -86,7 +86,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-objectstorage-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index f805fa6ff0..0eaf8885a6 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -82,7 +82,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-orchestration-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index b82ee3f74f..99eb3cca0b 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -80,7 +80,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-placement-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 7e6508d6f3..426111ed36 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -104,7 +104,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-sharedfilesystems-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 8f29c75033..dc4030ba77 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -85,7 +85,7 @@ jobs: - name: Upload logs artifacts on failure if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: functional-workflow-${{ matrix.name }}-${{ github.run_id }} path: /tmp/devstack-logs/* diff --git a/.github/workflows/semver.yaml b/.github/workflows/semver.yaml index 5d54e77299..9da00aa88e 100644 --- a/.github/workflows/semver.yaml +++ b/.github/workflows/semver.yaml @@ -56,7 +56,7 @@ jobs: - name: Upload semver results if: always() - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: semver-results path: semver-results/ From 869c802c4dca9c84152cb7d5b8cdc0673cafb3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 26 Mar 2026 09:40:45 +0100 Subject: [PATCH 2261/2296] ci: pass PR number and base ref through artifact instead of API The commits/:sha/pulls API endpoint does not return results for fork PRs because the commit does not exist in the base repository. Instead, include the PR number and base ref in the artifact uploaded by the semver analysis workflow, where they are always available from the pull_request event context. This fixes up commit 031765f054ea80a5b06f815aff5692dc722e0806. --- .github/workflows/label-pr.yaml | 52 ++++++++++++--------------------- .github/workflows/semver.yaml | 4 +++ 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index d30c28ae99..c876bf6625 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -27,22 +27,19 @@ jobs: issues: write pull-requests: write steps: - - name: Get PR number and base ref - id: pr + - name: Download semver results + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: semver-results + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Read semver results + id: semver run: | - PR_DATA=$(gh api "repos/$REPO/commits/$HEAD_SHA/pulls" --jq '.[0] | {number, base_ref: .base.ref}') - PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number') - BASE_REF=$(echo "$PR_DATA" | jq -r '.base_ref') - if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" = "null" ]; then - echo "Could not determine PR number" >&2 - exit 1 - fi - echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT" - echo "base-ref=$BASE_REF" >> "$GITHUB_OUTPUT" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPO: ${{ github.repository }} - HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + echo "type=$(cat semver-type)" >> "$GITHUB_OUTPUT" + echo "pr-number=$(cat pr-number)" >> "$GITHUB_OUTPUT" + echo "base-ref=$(cat base-ref)" >> "$GITHUB_OUTPUT" - name: Report failure if: github.event.workflow_run.conclusion == 'failure' @@ -52,23 +49,10 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - NUMBER: ${{ steps.pr.outputs.number }} + NUMBER: ${{ steps.semver.outputs.pr-number }} BODY: > Failed to assess the semver bump. See [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) for details. - - name: Download semver results - if: github.event.workflow_run.conclusion == 'success' - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: semver-results - run-id: ${{ github.event.workflow_run.id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Read semver type - if: github.event.workflow_run.conclusion == 'success' - id: semver - run: echo "type=$(cat semver-type)" >> "$GITHUB_OUTPUT" - - name: Add label semver:patch if: github.event.workflow_run.conclusion == 'success' && steps.semver.outputs.type == 'patch' run: | @@ -86,8 +70,8 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - NUMBER: ${{ steps.pr.outputs.number }} - BASE_REF: ${{ steps.pr.outputs.base-ref }} + NUMBER: ${{ steps.semver.outputs.pr-number }} + BASE_REF: ${{ steps.semver.outputs.base-ref }} - name: Add label semver:minor if: github.event.workflow_run.conclusion == 'success' && steps.semver.outputs.type == 'minor' @@ -107,8 +91,8 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - NUMBER: ${{ steps.pr.outputs.number }} - BASE_REF: ${{ steps.pr.outputs.base-ref }} + NUMBER: ${{ steps.semver.outputs.pr-number }} + BASE_REF: ${{ steps.semver.outputs.base-ref }} - name: Add label semver:major if: github.event.workflow_run.conclusion == 'success' && steps.semver.outputs.type == 'major' @@ -116,7 +100,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} - NUMBER: ${{ steps.pr.outputs.number }} + NUMBER: ${{ steps.semver.outputs.pr-number }} edits: if: github.event_name == 'pull_request_target' diff --git a/.github/workflows/semver.yaml b/.github/workflows/semver.yaml index 5d54e77299..9ccbca0fde 100644 --- a/.github/workflows/semver.yaml +++ b/.github/workflows/semver.yaml @@ -51,8 +51,12 @@ jobs: run: | mkdir -p semver-results echo "$SEMVER_TYPE" > semver-results/semver-type + echo "$PR_NUMBER" > semver-results/pr-number + echo "$BASE_REF" > semver-results/base-ref env: SEMVER_TYPE: ${{ steps.go-apidiff.outputs.semver-type }} + PR_NUMBER: ${{ github.event.pull_request.number }} + BASE_REF: ${{ github.base_ref }} - name: Upload semver results if: always() From 553a7e978052b5d958f3bc2d2c9069ee88cbfb84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 26 Mar 2026 10:23:36 +0100 Subject: [PATCH 2262/2296] ci: add dependabot cooldown configuration Add a 7-day cooldown to all Dependabot package ecosystem entries. This reduces supply-chain risk by waiting for newly released versions to be vetted before automatically proposing updates, and avoids pulling in compromised versions before they can be taken down. Reported by zizmor (dependabot-cooldown). --- .github/dependabot.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 2b5c704536..b061387fea 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -4,9 +4,13 @@ updates: directory: "/" schedule: interval: daily + cooldown: + default-days: 7 open-pull-requests-limit: 10 - package-ecosystem: "github-actions" directory: "/" - schedule: + schedule: interval: daily + cooldown: + default-days: 7 open-pull-requests-limit: 10 From d14dbe9b97c9722392998237d1cdd29660de6541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Thu, 26 Mar 2026 10:23:49 +0100 Subject: [PATCH 2263/2296] ci: expand zizmor scan scope to cover dependabot config Widen the path filter from .github/workflows/** to .github/** so that changes to .github/dependabot.yaml also trigger the zizmor security scan. --- .github/workflows/zizmor.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/zizmor.yaml b/.github/workflows/zizmor.yaml index 494a54ed80..e9b06d69ac 100644 --- a/.github/workflows/zizmor.yaml +++ b/.github/workflows/zizmor.yaml @@ -5,10 +5,10 @@ on: branches: - master paths: - - '.github/workflows/**' + - '.github/**' pull_request: paths: - - '.github/workflows/**' + - '.github/**' permissions: {} From 9e2872b16d371a62181c5242023728806a4b79c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 10:09:22 +0000 Subject: [PATCH 2264/2296] build(deps): bump actions/download-artifact from 4.3.0 to 8.0.1 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.3.0 to 8.0.1. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/d3f86a106a0bac45b974a628896c90dbdf5c8093...3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 8.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/label-pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index c876bf6625..de5d9a52b0 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -28,7 +28,7 @@ jobs: pull-requests: write steps: - name: Download semver results - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: semver-results run-id: ${{ github.event.workflow_run.id }} From 058f51767ad291edaead030a5711d5a8af04a9b2 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Fri, 27 Mar 2026 13:11:08 +0100 Subject: [PATCH 2265/2296] Use SkipReleasesBelow instead of SkipRelease in traits_test Signed-off-by: Dominik Danelski --- .../openstack/placement/v1/traits_test.go | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/internal/acceptance/openstack/placement/v1/traits_test.go b/internal/acceptance/openstack/placement/v1/traits_test.go index 3099da80df..15775f5281 100644 --- a/internal/acceptance/openstack/placement/v1/traits_test.go +++ b/internal/acceptance/openstack/placement/v1/traits_test.go @@ -17,9 +17,7 @@ import ( func TestTraitsList(t *testing.T) { // The Traits API requires microversion 1.6 or later - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") + clients.SkipReleasesBelow(t, "stable/pike") client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) @@ -39,9 +37,7 @@ func TestTraitsList(t *testing.T) { func TestTraitGet(t *testing.T) { // The Traits API requires microversion 1.6 or later - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") + clients.SkipReleasesBelow(t, "stable/pike") client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) @@ -56,9 +52,7 @@ func TestTraitGet(t *testing.T) { func TestTraitGetNegative(t *testing.T) { // The Traits API requires microversion 1.6 or later - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") + clients.SkipReleasesBelow(t, "stable/pike") client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) @@ -72,9 +66,7 @@ func TestTraitGetNegative(t *testing.T) { func TestTraitsListFiltering(t *testing.T) { // The Traits API requires microversion 1.6 or later - clients.SkipRelease(t, "stable/mitaka") - clients.SkipRelease(t, "stable/newton") - clients.SkipRelease(t, "stable/ocata") + clients.SkipReleasesBelow(t, "stable/pike") client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) From 9400749ff595475f23f017d9f8955750dfee928c Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Thu, 26 Mar 2026 19:39:01 +0100 Subject: [PATCH 2266/2296] Implement CREATE operation on Placement traits Related: #526 API reference: https://docs.openstack.org/api-ref/placement/#traits Code reference: https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/trait.py#L67-L103 Signed-off-by: Dominik Danelski --- .../openstack/placement/v1/traits_test.go | 59 +++++++++++++++++++ openstack/placement/v1/traits/doc.go | 13 ++++ openstack/placement/v1/traits/requests.go | 29 +++++++++ openstack/placement/v1/traits/results.go | 6 ++ .../v1/traits/testing/fixtures_test.go | 32 ++++++++++ .../v1/traits/testing/requests_test.go | 30 ++++++++++ openstack/placement/v1/traits/urls.go | 4 ++ 7 files changed, 173 insertions(+) diff --git a/internal/acceptance/openstack/placement/v1/traits_test.go b/internal/acceptance/openstack/placement/v1/traits_test.go index 15775f5281..180a85282a 100644 --- a/internal/acceptance/openstack/placement/v1/traits_test.go +++ b/internal/acceptance/openstack/placement/v1/traits_test.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/traits" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -88,3 +89,61 @@ func TestTraitsListFiltering(t *testing.T) { th.AssertEquals(t, true, strings.HasPrefix(trait, "HW_")) } } + +func TestTraitsCreateSuccess(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipReleasesBelow(t, "stable/pike") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + traitName := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) + createOpts := traits.CreateOpts{} + + err = traits.Create(context.TODO(), client, traitName, createOpts).ExtractErr() + th.AssertNoErr(t, err) + + // Assert that the trait now exists + err = traits.Get(context.TODO(), client, traitName).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestTraitsCreateDuplicate(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipReleasesBelow(t, "stable/pike") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + traitName := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) + createOpts := traits.CreateOpts{} + + // Create the trait for the first time + err = traits.Create(context.TODO(), client, traitName, createOpts).ExtractErr() + th.AssertNoErr(t, err) + + // Creating the same trait again results in 204 (no error) + err = traits.Create(context.TODO(), client, traitName, createOpts).ExtractErr() + th.AssertNoErr(t, err) +} + +// Test of creating a trait name that cannot be created in an API +func TestTraitsCreateInvalidName(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipReleasesBelow(t, "stable/pike") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + traitName := "HW_WE_CANNOT_CREATE_THIS_TRAIT" + createOpts := traits.CreateOpts{} + + err = traits.Create(context.TODO(), client, traitName, createOpts).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} diff --git a/openstack/placement/v1/traits/doc.go b/openstack/placement/v1/traits/doc.go index e11afc158e..215f8bd9a4 100644 --- a/openstack/placement/v1/traits/doc.go +++ b/openstack/placement/v1/traits/doc.go @@ -38,5 +38,18 @@ Example to check if a trait exists } else { fmt.Println("Trait exists!") } + +Example to create a trait + + placementClient.Microversion = "1.6" + + traitName := "CUSTOM_HW_FPGA_CLASS1" + createOpts := traits.CreateOpts{} + err := traits.Create(context.TODO(), placementClient, traitName, createOpts).ExtractErr() + if err != nil { + panic(err) + } else { + fmt.Println("Trait created successfully!") + } */ package traits diff --git a/openstack/placement/v1/traits/requests.go b/openstack/placement/v1/traits/requests.go index 82fd8a0dbb..67f21a6f02 100644 --- a/openstack/placement/v1/traits/requests.go +++ b/openstack/placement/v1/traits/requests.go @@ -58,3 +58,32 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, traitName strin _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToTraitCreateMap() (map[string]any, error) +} + +// CreateOpts provides options used to create a trait. +type CreateOpts struct { +} + +// ToTraitCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToTraitCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create creates a new trait. +func Create(ctx context.Context, client *gophercloud.ServiceClient, traitName string, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToTraitCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(ctx, createURL(client, traitName), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201, 204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/placement/v1/traits/results.go b/openstack/placement/v1/traits/results.go index 5e638cf226..0718b64601 100644 --- a/openstack/placement/v1/traits/results.go +++ b/openstack/placement/v1/traits/results.go @@ -16,6 +16,12 @@ type TraitsPage struct { pagination.SinglePageBase } +// CreateResult is the response from a Create operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type CreateResult struct { + gophercloud.ErrResult +} + // IsEmpty satisfies the IsEmpty method of the Page interface. It returns true // if a List contains no results. func (r TraitsPage) IsEmpty() (bool, error) { diff --git a/openstack/placement/v1/traits/testing/fixtures_test.go b/openstack/placement/v1/traits/testing/fixtures_test.go index 6c37649d18..9a22bcb7d2 100644 --- a/openstack/placement/v1/traits/testing/fixtures_test.go +++ b/openstack/placement/v1/traits/testing/fixtures_test.go @@ -11,6 +11,7 @@ import ( const PresentTrait = "CUSTOM_HW_FPGA_CLASS1" const AbsentTrait = "NON_EXISTENT_TRAIT" +const CustomTraitToCreate = "CUSTOM_TRAIT_TO_CREATE" const TraitsListResultAll = ` { @@ -106,3 +107,34 @@ func HandleGetTraitNotFound(t *testing.T, fakeServer th.FakeServer) { w.WriteHeader(http.StatusNotFound) }) } + +func HandleCreateTraitSuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+CustomTraitToCreate, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusCreated) + }) +} + +func HandleCreateTraitThatAlreadyExists(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+PresentTrait, + 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) + }) +} + +// Trait names created via the API must be prefixed with CUSTOM_. +func HandleCreateTraitInvalidName(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+AbsentTrait, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusBadRequest) + }) +} diff --git a/openstack/placement/v1/traits/testing/requests_test.go b/openstack/placement/v1/traits/testing/requests_test.go index 39642afb91..f3ba58f127 100644 --- a/openstack/placement/v1/traits/testing/requests_test.go +++ b/openstack/placement/v1/traits/testing/requests_test.go @@ -99,3 +99,33 @@ func TestGetTraitNotFound(t *testing.T) { err := traits.Get(context.TODO(), client.ServiceClient(fakeServer), AbsentTrait).ExtractErr() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } + +func TestCreateTraitSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleCreateTraitSuccess(t, fakeServer) + + err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), CustomTraitToCreate, traits.CreateOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestCreateTraitThatAlreadyExists(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleCreateTraitThatAlreadyExists(t, fakeServer) + + err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), PresentTrait, traits.CreateOpts{}).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestCreateTraitInvalidName(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleCreateTraitInvalidName(t, fakeServer) + + err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), AbsentTrait, traits.CreateOpts{}).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} diff --git a/openstack/placement/v1/traits/urls.go b/openstack/placement/v1/traits/urls.go index a6ea7757a1..2211abb309 100644 --- a/openstack/placement/v1/traits/urls.go +++ b/openstack/placement/v1/traits/urls.go @@ -13,3 +13,7 @@ func listURL(client *gophercloud.ServiceClient) string { func getURL(client *gophercloud.ServiceClient, traitName string) string { return client.ServiceURL(apiName, traitName) } + +func createURL(client *gophercloud.ServiceClient, traitName string) string { + return client.ServiceURL(apiName, traitName) +} From feee1069392a8e508e77665d6d1075cd6cb74a12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:08:53 +0000 Subject: [PATCH 2267/2296] build(deps): bump actions/setup-go from 6.3.0 to 6.4.0 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.3.0 to 6.4.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/4b73464bb391d4059bd26b0524d20df3927bd417...4a3601121dd01d1626a1e23e37211e3254c1c06c) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: 6.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yaml | 2 +- .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-fwaas_v2.yaml | 2 +- .github/workflows/functional-identity.yaml | 2 +- .github/workflows/functional-image.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/functional-workflow.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/semver.yaml | 2 +- .github/workflows/unit.yaml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 3040b3cc41..12d4008b74 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -23,7 +23,7 @@ jobs: persist-credentials: false - name: Setup Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 7021620482..7248cbf947 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -101,7 +101,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 8964c0b550..313d67c2d3 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -55,7 +55,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index 4d981062d1..f067e785ce 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index 46c7cb41bd..b2d2f27701 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 294624b0f1..8edabce192 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -88,7 +88,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 7abe11855f..5ad1d8771f 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -69,7 +69,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index b3f381756e..97cbb3f716 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -84,7 +84,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 7bdbf7ab9c..651e59bc3d 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -59,7 +59,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 8254a7d0c8..e9e688044d 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -59,7 +59,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index fee5e6603b..e312e034a0 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -75,7 +75,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 3886f48146..8a80c2ea5e 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -75,7 +75,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index fc7de2d4ad..3aba20dd6f 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -62,7 +62,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index 1ff2ce14fc..b93e3c1f2c 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -79,7 +79,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 04f1a6d56a..2b3db097d0 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -65,7 +65,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 0eaf8885a6..dbd5d36668 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 99eb3cca0b..641b22e9bc 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -59,7 +59,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 426111ed36..e8fc1a03a6 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -83,7 +83,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index dc4030ba77..bb57db4067 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -64,7 +64,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index b019a83b55..88784d2215 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/semver.yaml b/.github/workflows/semver.yaml index 121a29d2c7..af9ff9643d 100644 --- a/.github/workflows/semver.yaml +++ b/.github/workflows/semver.yaml @@ -28,7 +28,7 @@ jobs: GIT_SEQUENCE_EDITOR: '/usr/bin/true' BASE_REF: ${{ github.base_ref }} - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index c938fd0a8c..fd82541b38 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -19,7 +19,7 @@ jobs: with: persist-credentials: false - name: Setup Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: 'go.mod' cache: true From 3a2ad5ae9e6a1eb4b5282d3424c2b931e0a6d76d Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Mon, 30 Mar 2026 12:58:55 +0200 Subject: [PATCH 2268/2296] Update traits doc to include a wider range of available operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin André --- openstack/placement/v1/traits/doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstack/placement/v1/traits/doc.go b/openstack/placement/v1/traits/doc.go index 215f8bd9a4..f6c88a62fb 100644 --- a/openstack/placement/v1/traits/doc.go +++ b/openstack/placement/v1/traits/doc.go @@ -1,5 +1,5 @@ /* -Package traits lists traits from the OpenStack Placement service. +Package traits manages traits from the OpenStack Placement service. Traits API requests are available starting from microversion 1.6. From 5c4a59ac74025ba8ef8228b75e4b5cfebff64956 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Mon, 30 Mar 2026 17:12:44 +0200 Subject: [PATCH 2269/2296] Simplify trait PUT operation, remove CreateOptsBuilder It's useless, as API doesn't take any arguments at the moment. That's unlikely to change in the near future. Signed-off-by: Dominik Danelski --- .../openstack/placement/v1/traits_test.go | 11 ++++----- openstack/placement/v1/traits/doc.go | 3 +-- openstack/placement/v1/traits/requests.go | 24 ++----------------- .../v1/traits/testing/requests_test.go | 6 ++--- 4 files changed, 10 insertions(+), 34 deletions(-) diff --git a/internal/acceptance/openstack/placement/v1/traits_test.go b/internal/acceptance/openstack/placement/v1/traits_test.go index 180a85282a..8ceeb42aee 100644 --- a/internal/acceptance/openstack/placement/v1/traits_test.go +++ b/internal/acceptance/openstack/placement/v1/traits_test.go @@ -100,9 +100,8 @@ func TestTraitsCreateSuccess(t *testing.T) { client.Microversion = "1.6" traitName := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) - createOpts := traits.CreateOpts{} - err = traits.Create(context.TODO(), client, traitName, createOpts).ExtractErr() + err = traits.Create(context.TODO(), client, traitName).ExtractErr() th.AssertNoErr(t, err) // Assert that the trait now exists @@ -120,14 +119,13 @@ func TestTraitsCreateDuplicate(t *testing.T) { client.Microversion = "1.6" traitName := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) - createOpts := traits.CreateOpts{} // Create the trait for the first time - err = traits.Create(context.TODO(), client, traitName, createOpts).ExtractErr() + err = traits.Create(context.TODO(), client, traitName).ExtractErr() th.AssertNoErr(t, err) // Creating the same trait again results in 204 (no error) - err = traits.Create(context.TODO(), client, traitName, createOpts).ExtractErr() + err = traits.Create(context.TODO(), client, traitName).ExtractErr() th.AssertNoErr(t, err) } @@ -142,8 +140,7 @@ func TestTraitsCreateInvalidName(t *testing.T) { client.Microversion = "1.6" traitName := "HW_WE_CANNOT_CREATE_THIS_TRAIT" - createOpts := traits.CreateOpts{} - err = traits.Create(context.TODO(), client, traitName, createOpts).ExtractErr() + err = traits.Create(context.TODO(), client, traitName).ExtractErr() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } diff --git a/openstack/placement/v1/traits/doc.go b/openstack/placement/v1/traits/doc.go index f6c88a62fb..91c7a8888e 100644 --- a/openstack/placement/v1/traits/doc.go +++ b/openstack/placement/v1/traits/doc.go @@ -44,8 +44,7 @@ Example to create a trait placementClient.Microversion = "1.6" traitName := "CUSTOM_HW_FPGA_CLASS1" - createOpts := traits.CreateOpts{} - err := traits.Create(context.TODO(), placementClient, traitName, createOpts).ExtractErr() + err := traits.Create(context.TODO(), placementClient, traitName).ExtractErr() if err != nil { panic(err) } else { diff --git a/openstack/placement/v1/traits/requests.go b/openstack/placement/v1/traits/requests.go index 67f21a6f02..2a106ff013 100644 --- a/openstack/placement/v1/traits/requests.go +++ b/openstack/placement/v1/traits/requests.go @@ -59,29 +59,9 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, traitName strin return } -// CreateOptsBuilder allows extensions to add additional parameters to -// the Create request. -type CreateOptsBuilder interface { - ToTraitCreateMap() (map[string]any, error) -} - -// CreateOpts provides options used to create a trait. -type CreateOpts struct { -} - -// ToTraitCreateMap formats a CreateOpts into a create request. -func (opts CreateOpts) ToTraitCreateMap() (map[string]any, error) { - return gophercloud.BuildRequestBody(opts, "") -} - // Create creates a new trait. -func Create(ctx context.Context, client *gophercloud.ServiceClient, traitName string, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToTraitCreateMap() - if err != nil { - r.Err = err - return - } - resp, err := client.Put(ctx, createURL(client, traitName), b, nil, &gophercloud.RequestOpts{ +func Create(ctx context.Context, client *gophercloud.ServiceClient, traitName string) (r CreateResult) { + resp, err := client.Put(ctx, createURL(client, traitName), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 204}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/placement/v1/traits/testing/requests_test.go b/openstack/placement/v1/traits/testing/requests_test.go index f3ba58f127..9f5b63663d 100644 --- a/openstack/placement/v1/traits/testing/requests_test.go +++ b/openstack/placement/v1/traits/testing/requests_test.go @@ -106,7 +106,7 @@ func TestCreateTraitSuccess(t *testing.T) { HandleCreateTraitSuccess(t, fakeServer) - err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), CustomTraitToCreate, traits.CreateOpts{}).ExtractErr() + err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), CustomTraitToCreate).ExtractErr() th.AssertNoErr(t, err) } @@ -116,7 +116,7 @@ func TestCreateTraitThatAlreadyExists(t *testing.T) { HandleCreateTraitThatAlreadyExists(t, fakeServer) - err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), PresentTrait, traits.CreateOpts{}).ExtractErr() + err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), PresentTrait).ExtractErr() th.AssertNoErr(t, err) } @@ -126,6 +126,6 @@ func TestCreateTraitInvalidName(t *testing.T) { HandleCreateTraitInvalidName(t, fakeServer) - err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), AbsentTrait, traits.CreateOpts{}).ExtractErr() + err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), AbsentTrait).ExtractErr() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } From 990245747c4425342c574da0bac4e01cb076b0d1 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Thu, 26 Mar 2026 22:28:27 +0100 Subject: [PATCH 2270/2296] Implement DELETE operation on Placement traits Related: #526 API reference: https://docs.openstack.org/api-ref/placement/#traits Code reference: https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/trait.py#L129-L146 Signed-off-by: Dominik Danelski --- .../openstack/placement/v1/traits_test.go | 56 +++++++++++++++++++ openstack/placement/v1/traits/doc.go | 12 ++++ openstack/placement/v1/traits/requests.go | 9 +++ openstack/placement/v1/traits/results.go | 6 ++ .../v1/traits/testing/fixtures_test.go | 42 ++++++++++++++ .../v1/traits/testing/requests_test.go | 40 +++++++++++++ openstack/placement/v1/traits/urls.go | 4 ++ 7 files changed, 169 insertions(+) diff --git a/internal/acceptance/openstack/placement/v1/traits_test.go b/internal/acceptance/openstack/placement/v1/traits_test.go index 8ceeb42aee..0bea5ef944 100644 --- a/internal/acceptance/openstack/placement/v1/traits_test.go +++ b/internal/acceptance/openstack/placement/v1/traits_test.go @@ -144,3 +144,59 @@ func TestTraitsCreateInvalidName(t *testing.T) { err = traits.Create(context.TODO(), client, traitName).ExtractErr() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } + +func TestTraitsDeleteSuccess(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipReleasesBelow(t, "stable/pike") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + traitName := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) + + // Prepare: Create the trait + err = traits.Create(context.TODO(), client, traitName).ExtractErr() + th.AssertNoErr(t, err) + + // Act: Delete the trait + err = traits.Delete(context.TODO(), client, traitName).ExtractErr() + th.AssertNoErr(t, err) + + // Assert: The trait no longer exists + err = traits.Get(context.TODO(), client, traitName).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestTraitsDeleteNotFound(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipReleasesBelow(t, "stable/pike") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + traitName := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) + + err = traits.Delete(context.TODO(), client, traitName).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +// API does allow manipulation solely of custom traits, +// so trying to delete a standard trait should fail. +func TestTraitsDeleteStandardTraitFailure(t *testing.T) { + // The Traits API requires microversion 1.6 or later + clients.SkipReleasesBelow(t, "stable/pike") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.6" + + traitName := "COMPUTE_NODE" + + err = traits.Delete(context.TODO(), client, traitName).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} diff --git a/openstack/placement/v1/traits/doc.go b/openstack/placement/v1/traits/doc.go index 91c7a8888e..d2774d9f29 100644 --- a/openstack/placement/v1/traits/doc.go +++ b/openstack/placement/v1/traits/doc.go @@ -50,5 +50,17 @@ Example to create a trait } else { fmt.Println("Trait created successfully!") } + +Example to delete a trait + + placementClient.Microversion = "1.6" + + traitName := "CUSTOM_HW_FPGA_CLASS1" + err := traits.Delete(context.TODO(), placementClient, traitName).ExtractErr() + if err != nil { + panic(err) + } else { + fmt.Println("Trait deleted successfully!") + } */ package traits diff --git a/openstack/placement/v1/traits/requests.go b/openstack/placement/v1/traits/requests.go index 2a106ff013..99e9b58434 100644 --- a/openstack/placement/v1/traits/requests.go +++ b/openstack/placement/v1/traits/requests.go @@ -67,3 +67,12 @@ func Create(ctx context.Context, client *gophercloud.ServiceClient, traitName st _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Delete deletes the trait specified by name. +func Delete(ctx context.Context, client *gophercloud.ServiceClient, traitName string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, traitName), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/placement/v1/traits/results.go b/openstack/placement/v1/traits/results.go index 0718b64601..085de1daf0 100644 --- a/openstack/placement/v1/traits/results.go +++ b/openstack/placement/v1/traits/results.go @@ -22,6 +22,12 @@ type CreateResult struct { gophercloud.ErrResult } +// 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 satisfies the IsEmpty method of the Page interface. It returns true // if a List contains no results. func (r TraitsPage) IsEmpty() (bool, error) { diff --git a/openstack/placement/v1/traits/testing/fixtures_test.go b/openstack/placement/v1/traits/testing/fixtures_test.go index 9a22bcb7d2..3a8eb9446a 100644 --- a/openstack/placement/v1/traits/testing/fixtures_test.go +++ b/openstack/placement/v1/traits/testing/fixtures_test.go @@ -12,6 +12,8 @@ import ( const PresentTrait = "CUSTOM_HW_FPGA_CLASS1" const AbsentTrait = "NON_EXISTENT_TRAIT" const CustomTraitToCreate = "CUSTOM_TRAIT_TO_CREATE" +const CustomTraitToDelete = CustomTraitToCreate +const StandardHardwareTrait = "HW_CPU_X86_AVX" const TraitsListResultAll = ` { @@ -138,3 +140,43 @@ func HandleCreateTraitInvalidName(t *testing.T, fakeServer th.FakeServer) { w.WriteHeader(http.StatusBadRequest) }) } + +func HandleDeleteTraitSuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+CustomTraitToDelete, + 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 HandleDeleteTraitNotFound(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+AbsentTrait, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNotFound) + }) +} + +func HandleDeleteStandardTraitFailure(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+StandardHardwareTrait, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusBadRequest) + }) +} + +func HandleDeleteTraitInUseFailure(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/traits/"+PresentTrait, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusConflict) + }) +} diff --git a/openstack/placement/v1/traits/testing/requests_test.go b/openstack/placement/v1/traits/testing/requests_test.go index 9f5b63663d..ecd08005d4 100644 --- a/openstack/placement/v1/traits/testing/requests_test.go +++ b/openstack/placement/v1/traits/testing/requests_test.go @@ -129,3 +129,43 @@ func TestCreateTraitInvalidName(t *testing.T) { err := traits.Create(context.TODO(), client.ServiceClient(fakeServer), AbsentTrait).ExtractErr() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } + +func TestDeleteTraitSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleDeleteTraitSuccess(t, fakeServer) + + err := traits.Delete(context.TODO(), client.ServiceClient(fakeServer), CustomTraitToDelete).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDeleteTraitNotFound(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleDeleteTraitNotFound(t, fakeServer) + + err := traits.Delete(context.TODO(), client.ServiceClient(fakeServer), AbsentTrait).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestDeleteStandardTraitFailure(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleDeleteStandardTraitFailure(t, fakeServer) + + err := traits.Delete(context.TODO(), client.ServiceClient(fakeServer), StandardHardwareTrait).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} + +func TestDeleteTraitInUseFailure(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleDeleteTraitInUseFailure(t, fakeServer) + + err := traits.Delete(context.TODO(), client.ServiceClient(fakeServer), PresentTrait).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) +} diff --git a/openstack/placement/v1/traits/urls.go b/openstack/placement/v1/traits/urls.go index 2211abb309..2baba47c49 100644 --- a/openstack/placement/v1/traits/urls.go +++ b/openstack/placement/v1/traits/urls.go @@ -17,3 +17,7 @@ func getURL(client *gophercloud.ServiceClient, traitName string) string { func createURL(client *gophercloud.ServiceClient, traitName string) string { return client.ServiceURL(apiName, traitName) } + +func deleteURL(client *gophercloud.ServiceClient, traitName string) string { + return client.ServiceURL(apiName, traitName) +} From 4f722d2a8a42fc80b5c3c84914259fafea7cd060 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Tue, 31 Mar 2026 15:02:02 +0200 Subject: [PATCH 2271/2296] Update acceptance README to remove mentions of Packstack Fixes #3667 --- internal/acceptance/README.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/internal/acceptance/README.md b/internal/acceptance/README.md index febf394f10..2010e368dc 100644 --- a/internal/acceptance/README.md +++ b/internal/acceptance/README.md @@ -18,16 +18,8 @@ environment. Additionally, you may incur bandwidth and service charges for the resources used, as mentioned in the note above. Therefore, it is usually best to first practice running acceptance tests in -an isolated test environment. Two options to easily create a testing -environment are [DevStack](https://docs.openstack.org/devstack/latest/) -and [PackStack](https://www.rdoproject.org/install/packstack/). - -The following blog posts detail how to create reusable PackStack environments. -These posts were written with Gophercloud in mind: - -* http://terrarum.net/blog/building-openstack-environments.html -* http://terrarum.net/blog/building-openstack-environments-2.html -* http://terrarum.net/blog/building-openstack-environments-3.html +an isolated test environment. The best option to easily create a testing +environment is [DevStack](https://docs.openstack.org/devstack/latest/). ### Step 2. Set environment variables From ff521e38909301348173e3369d9da7bb1faaba58 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Tue, 31 Mar 2026 17:47:24 +0200 Subject: [PATCH 2272/2296] Implement resource_classes GET operations Related #526 API reference: https://docs.openstack.org/api-ref/placement/#resource-classes Code reference: https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/resource_class.py#L34-L39 https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/resource_class.py#L42-L52 Signed-off-by: Dominik Danelski --- .../placement/v1/resourceclasses_test.go | 65 ++++++++++ openstack/placement/v1/resourceclasses/doc.go | 36 ++++++ .../placement/v1/resourceclasses/requests.go | 24 ++++ .../placement/v1/resourceclasses/results.go | 62 +++++++++ .../v1/resourceclasses/testing/doc.go | 2 + .../resourceclasses/testing/fixtures_test.go | 118 ++++++++++++++++++ .../resourceclasses/testing/requests_test.go | 47 +++++++ .../placement/v1/resourceclasses/urls.go | 13 ++ 8 files changed, 367 insertions(+) create mode 100644 internal/acceptance/openstack/placement/v1/resourceclasses_test.go create mode 100644 openstack/placement/v1/resourceclasses/doc.go create mode 100644 openstack/placement/v1/resourceclasses/requests.go create mode 100644 openstack/placement/v1/resourceclasses/results.go create mode 100644 openstack/placement/v1/resourceclasses/testing/doc.go create mode 100644 openstack/placement/v1/resourceclasses/testing/fixtures_test.go create mode 100644 openstack/placement/v1/resourceclasses/testing/requests_test.go create mode 100644 openstack/placement/v1/resourceclasses/urls.go diff --git a/internal/acceptance/openstack/placement/v1/resourceclasses_test.go b/internal/acceptance/openstack/placement/v1/resourceclasses_test.go new file mode 100644 index 0000000000..81673417bf --- /dev/null +++ b/internal/acceptance/openstack/placement/v1/resourceclasses_test.go @@ -0,0 +1,65 @@ +//go:build acceptance || placement || resourceclasses + +package v1 + +import ( + "context" + "net/http" + "slices" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceclasses" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestResourceClassesList(t *testing.T) { + // Resource classes were introduced in 1.2 + clients.SkipReleasesBelow(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.2" + + allPages, err := resourceclasses.List(client).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + allResourceClasses, err := resourceclasses.ExtractResourceClasses(allPages) + th.AssertNoErr(t, err) + + // Ensure VCPU is in the list + th.AssertEquals(t, true, slices.ContainsFunc(allResourceClasses, func(rc resourceclasses.ResourceClass) bool { + return rc.Name == "VCPU" + })) +} + +func TestResourceClassGetSuccess(t *testing.T) { + // Resource classes were introduced in 1.2 + clients.SkipReleasesBelow(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.2" + + // VCPU is a standard resource class + rc, err := resourceclasses.Get(context.TODO(), client, "VCPU").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "VCPU", rc.Name) +} + +func TestResourceClassGetNegative(t *testing.T) { + // Resource classes were introduced in 1.2 + clients.SkipReleasesBelow(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.2" + + _, err = resourceclasses.Get(context.TODO(), client, "NON_EXISTENT_RC").Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} diff --git a/openstack/placement/v1/resourceclasses/doc.go b/openstack/placement/v1/resourceclasses/doc.go new file mode 100644 index 0000000000..27f7c31ac8 --- /dev/null +++ b/openstack/placement/v1/resourceclasses/doc.go @@ -0,0 +1,36 @@ +/* +Package resourceclasses manages resource classes from the OpenStack Placement service. + +Resource Class API requests are available starting from microversion 1.2. + +Example to list resource classes + + placementClient.Microversion = "1.2" + + allPages, err := resourceclasses.List(placementClient).AllPages(context.TODO()) + if err != nil { + panic(err) + } + + allResourceClasses, err := resourceclasses.ExtractResourceClasses(allPages) + if err != nil { + panic(err) + } + + for _, rc := range allResourceClasses { + fmt.Printf("%+v\n", rc) + } + +Example to Get a resource class + + placementClient.Microversion = "1.2" + + resourceClassName := "VCPU" + resourceClass, err := resourceclasses.Get(context.TODO(), placementClient, resourceClassName).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", resourceClass) +*/ +package resourceclasses diff --git a/openstack/placement/v1/resourceclasses/requests.go b/openstack/placement/v1/resourceclasses/requests.go new file mode 100644 index 0000000000..d497224aaf --- /dev/null +++ b/openstack/placement/v1/resourceclasses/requests.go @@ -0,0 +1,24 @@ +package resourceclasses + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// List retrieves a list of resource classes. +func List(client *gophercloud.ServiceClient) pagination.Pager { + url := listURL(client) + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return ResourceClassesPage{pagination.SinglePageBase(r)} + }) +} + +// Get retrieves the resource class with the provided name. +func Get(ctx context.Context, client *gophercloud.ServiceClient, name string) (r GetResult) { + resp, err := client.Get(ctx, getURL(client, name), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/placement/v1/resourceclasses/results.go b/openstack/placement/v1/resourceclasses/results.go new file mode 100644 index 0000000000..85ed0a4e80 --- /dev/null +++ b/openstack/placement/v1/resourceclasses/results.go @@ -0,0 +1,62 @@ +package resourceclasses + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +type Link struct { + Href string `json:"href"` + Rel string `json:"rel"` +} + +type ResourceClass struct { + Name string `json:"name"` + + // Links is a list of links associated with the resource class. + Links []Link `json:"links"` +} + +// resourceClassResult is the response of a base ResourceClass result. +type resourceClassResult struct { + gophercloud.Result +} + +// Extract interprets any resourceClassResult-base result as a ResourceClass. +func (r resourceClassResult) Extract() (*ResourceClass, error) { + var s ResourceClass + err := r.ExtractInto(&s) + return &s, err +} + +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret it as a ResourceClass. +type GetResult struct { + resourceClassResult +} + +// ResourceClassesPage contains a single page of all resource classes from a List call. +type ResourceClassesPage struct { + pagination.SinglePageBase +} + +// IsEmpty satisfies the IsEmpty method of the Page interface. It returns true +// if a List contains no results. +func (r ResourceClassesPage) IsEmpty() (bool, error) { + if r.StatusCode == 204 { + return true, nil + } + + resourceClasses, err := ExtractResourceClasses(r) + return len(resourceClasses) == 0, err +} + +// ExtractResourceClasses takes a List result and extracts the collection of resource classes +// returned by the API. +func ExtractResourceClasses(p pagination.Page) ([]ResourceClass, error) { + var s struct { + ResourceClasses []ResourceClass `json:"resource_classes"` + } + err := (p.(ResourceClassesPage)).ExtractInto(&s) + return s.ResourceClasses, err +} diff --git a/openstack/placement/v1/resourceclasses/testing/doc.go b/openstack/placement/v1/resourceclasses/testing/doc.go new file mode 100644 index 0000000000..2a3dd0b663 --- /dev/null +++ b/openstack/placement/v1/resourceclasses/testing/doc.go @@ -0,0 +1,2 @@ +// Package testing contains resourceclasses unit tests. +package testing diff --git a/openstack/placement/v1/resourceclasses/testing/fixtures_test.go b/openstack/placement/v1/resourceclasses/testing/fixtures_test.go new file mode 100644 index 0000000000..b13e355244 --- /dev/null +++ b/openstack/placement/v1/resourceclasses/testing/fixtures_test.go @@ -0,0 +1,118 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceclasses" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const PresentResourceClass = "CUSTOM_RESOURCE_CLASS" +const AbsentResourceClass = "NON_EXISTENT_RC" + +const ResourceClassGetResult = ` +{ + "links": [ + { + "href": "/placement/resource_classes/CUSTOM_RESOURCE_CLASS", + "rel": "self" + } + ], + "name": "CUSTOM_RESOURCE_CLASS" +} +` + +const ResourceClassesListResult = ` +{ + "resource_classes": [ + { + "name": "VCPU", + "links": [ + { + "href": "/resource_classes/VCPU", + "rel": "self" + } + ] + }, + { + "name": "CUSTOM_RESOURCE_CLASS", + "links": [ + { + "href": "/placement/resource_classes/CUSTOM_RESOURCE_CLASS", + "rel": "self" + } + ] + } + ] +} +` + +var ExpectedResourceClass = resourceclasses.ResourceClass{ + Name: "CUSTOM_RESOURCE_CLASS", + Links: []resourceclasses.Link{ + { + Href: "/placement/resource_classes/CUSTOM_RESOURCE_CLASS", + Rel: "self", + }, + }, +} + +var ExpectedResourceClassesList = []resourceclasses.ResourceClass{ + { + Name: "VCPU", + Links: []resourceclasses.Link{ + { + Href: "/resource_classes/VCPU", + Rel: "self", + }, + }, + }, + { + Name: "CUSTOM_RESOURCE_CLASS", + Links: []resourceclasses.Link{ + { + Href: "/placement/resource_classes/CUSTOM_RESOURCE_CLASS", + Rel: "self", + }, + }, + }, +} + +func HandleListResourceClasses(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes", + 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.Fprint(w, ResourceClassesListResult) + }) +} + +func HandleGetResourceClassSuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes/"+PresentResourceClass, + 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.Fprint(w, ResourceClassGetResult) + }) +} + +func HandleGetResourceClassNotFound(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes/"+AbsentResourceClass, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNotFound) + }) +} diff --git a/openstack/placement/v1/resourceclasses/testing/requests_test.go b/openstack/placement/v1/resourceclasses/testing/requests_test.go new file mode 100644 index 0000000000..4bb1f29f3c --- /dev/null +++ b/openstack/placement/v1/resourceclasses/testing/requests_test.go @@ -0,0 +1,47 @@ +package testing + +import ( + "context" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceclasses" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestListResourceClasses(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListResourceClasses(t, fakeServer) + + allPages, err := resourceclasses.List(client.ServiceClient(fakeServer)).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + actual, err := resourceclasses.ExtractResourceClasses(allPages) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedResourceClassesList, actual) +} + +func TestGetResourceClassSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleGetResourceClassSuccess(t, fakeServer) + + actual, err := resourceclasses.Get(context.TODO(), client.ServiceClient(fakeServer), PresentResourceClass).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, &ExpectedResourceClass, actual) +} + +func TestGetResourceClassNotFound(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleGetResourceClassNotFound(t, fakeServer) + + _, err := resourceclasses.Get(context.TODO(), client.ServiceClient(fakeServer), AbsentResourceClass).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} diff --git a/openstack/placement/v1/resourceclasses/urls.go b/openstack/placement/v1/resourceclasses/urls.go new file mode 100644 index 0000000000..0fec96e30c --- /dev/null +++ b/openstack/placement/v1/resourceclasses/urls.go @@ -0,0 +1,13 @@ +package resourceclasses + +import ( + "github.com/gophercloud/gophercloud/v2" +) + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("resource_classes") +} + +func getURL(client *gophercloud.ServiceClient, name string) string { + return client.ServiceURL("resource_classes", name) +} From a07e407eb50577059d67aaf6f6b0d4d9479c6d6a Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Tue, 31 Mar 2026 21:18:38 +0200 Subject: [PATCH 2273/2296] Implement resource_classes CREATE operations Related #526 API reference: https://docs.openstack.org/api-ref/placement/#resource-classes Code reference: https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/resource_class.py#L58-L85 https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/resource_class.py#L209-L240 Signed-off-by: Dominik Danelski --- .../placement/v1/resourceclasses_test.go | 94 +++++++++++++++++++ openstack/placement/v1/resourceclasses/doc.go | 22 +++++ .../placement/v1/resourceclasses/requests.go | 40 ++++++++ .../placement/v1/resourceclasses/results.go | 12 +++ .../resourceclasses/testing/fixtures_test.go | 53 +++++++++++ .../resourceclasses/testing/requests_test.go | 58 ++++++++++++ .../placement/v1/resourceclasses/urls.go | 8 ++ 7 files changed, 287 insertions(+) diff --git a/internal/acceptance/openstack/placement/v1/resourceclasses_test.go b/internal/acceptance/openstack/placement/v1/resourceclasses_test.go index 81673417bf..d2ea98fb51 100644 --- a/internal/acceptance/openstack/placement/v1/resourceclasses_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceclasses_test.go @@ -6,10 +6,12 @@ import ( "context" "net/http" "slices" + "strings" "testing" "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceclasses" th "github.com/gophercloud/gophercloud/v2/testhelper" ) @@ -63,3 +65,95 @@ func TestResourceClassGetNegative(t *testing.T) { _, err = resourceclasses.Get(context.TODO(), client, "NON_EXISTENT_RC").Extract() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } + +func TestResourceClassCreateByPostSuccess(t *testing.T) { + // Resource classes were introduced in 1.2 + clients.SkipReleasesBelow(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.2" + + name := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) + createOpts := resourceclasses.CreateOpts{ + Name: name, + } + + // Act: Create a resource class using POST + err = resourceclasses.Create(context.TODO(), client, createOpts).ExtractErr() + th.AssertNoErr(t, err) + + // Assert: The resource class exists + rc, err := resourceclasses.Get(context.TODO(), client, name).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, name, rc.Name) +} + +func TestResourceClassCreateByPostDuplicate(t *testing.T) { + // Resource classes were introduced in 1.2 + clients.SkipReleasesBelow(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.2" + + name := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) + createOpts := resourceclasses.CreateOpts{ + Name: name, + } + + // Act: Create a resource class using POST + err = resourceclasses.Create(context.TODO(), client, createOpts).ExtractErr() + th.AssertNoErr(t, err) + + // Act: Try to create the same resource class again + err = resourceclasses.Create(context.TODO(), client, createOpts).ExtractErr() + // Assert: The error is a conflict + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) +} + +func TestResourceClassCreateByUpdateSuccess(t *testing.T) { + // Creating by Update (PUT) requires microversion 1.7 or later + clients.SkipReleasesBelow(t, "stable/pike") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.7" + + name := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) + + // Act: Create a resource class using PUT (Update) + err = resourceclasses.Update(context.TODO(), client, name).ExtractErr() + // No error, with 201 returned + th.AssertNoErr(t, err) + + // Act: Try to create the same resource class again + err = resourceclasses.Update(context.TODO(), client, name).ExtractErr() + // No error, with 204 returned + th.AssertNoErr(t, err) + + // Assert: The resource class exists + rc, err := resourceclasses.Get(context.TODO(), client, name).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, name, rc.Name) +} + +func TestResourceClassCreateByUpdateNonCustomName(t *testing.T) { + // Creating by Update (PUT) requires microversion 1.7 or later + clients.SkipReleasesBelow(t, "stable/pike") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.7" + + name := "CANNOT_CREATE_THIS" + + // Act: Try to create a resource class with a non-custom name using PUT (Update) + err = resourceclasses.Update(context.TODO(), client, name).ExtractErr() + // Assert: We get 400 + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} diff --git a/openstack/placement/v1/resourceclasses/doc.go b/openstack/placement/v1/resourceclasses/doc.go index 27f7c31ac8..53eae204c9 100644 --- a/openstack/placement/v1/resourceclasses/doc.go +++ b/openstack/placement/v1/resourceclasses/doc.go @@ -32,5 +32,27 @@ Example to Get a resource class } fmt.Printf("%+v\n", resourceClass) + +Example to Create a resource class using POST + + placementClient.Microversion = "1.2" + + createOpts := resourceclasses.CreateOpts{ + Name: "CUSTOM_RESOURCE_CLASS", + } + + err := resourceclasses.Create(context.TODO(), placementClient, createOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to ensure the existence of a resource class using PUT (idempotent creation) + + placementClient.Microversion = "1.7" + + err := resourceclasses.Update(context.TODO(), placementClient, "CUSTOM_RESOURCE_CLASS").ExtractErr() + if err != nil { + panic(err) + } */ package resourceclasses diff --git a/openstack/placement/v1/resourceclasses/requests.go b/openstack/placement/v1/resourceclasses/requests.go index d497224aaf..4fccd33c9c 100644 --- a/openstack/placement/v1/resourceclasses/requests.go +++ b/openstack/placement/v1/resourceclasses/requests.go @@ -22,3 +22,43 @@ func Get(ctx context.Context, client *gophercloud.ServiceClient, name string) (r _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// CreateOptsBuilder allows extensions to add additional parameters to +// the Create request. +type CreateOptsBuilder interface { + ToResourceClassCreateMap() (map[string]any, error) +} + +// CreateOpts represents the attributes of a new resource class. +type CreateOpts struct { + Name string `json:"name" required:"true"` +} + +// ToResourceClassCreateMap formats a CreateOpts into a create request. +func (opts CreateOpts) ToResourceClassCreateMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create creates a new resource class. +func Create(ctx context.Context, client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToResourceClassCreateMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(ctx, createURL(client), b, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Update ensures the existence of a custom resource class with the +// provided name (can be safely called multiple times). +func Update(ctx context.Context, client *gophercloud.ServiceClient, name string) (r UpdateResult) { + resp, err := client.Put(ctx, updateURL(client, name), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201, 204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/placement/v1/resourceclasses/results.go b/openstack/placement/v1/resourceclasses/results.go index 85ed0a4e80..cbd5501b9c 100644 --- a/openstack/placement/v1/resourceclasses/results.go +++ b/openstack/placement/v1/resourceclasses/results.go @@ -35,6 +35,18 @@ type GetResult struct { resourceClassResult } +// CreateResult is the response from a Create operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type CreateResult struct { + gophercloud.ErrResult +} + +// UpdateResult is the response from an Update operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type UpdateResult struct { + gophercloud.ErrResult +} + // ResourceClassesPage contains a single page of all resource classes from a List call. type ResourceClassesPage struct { pagination.SinglePageBase diff --git a/openstack/placement/v1/resourceclasses/testing/fixtures_test.go b/openstack/placement/v1/resourceclasses/testing/fixtures_test.go index b13e355244..7b0819cdef 100644 --- a/openstack/placement/v1/resourceclasses/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceclasses/testing/fixtures_test.go @@ -12,6 +12,7 @@ import ( const PresentResourceClass = "CUSTOM_RESOURCE_CLASS" const AbsentResourceClass = "NON_EXISTENT_RC" +const NewResourceClass = "CUSTOM_NEW_RC" const ResourceClassGetResult = ` { @@ -116,3 +117,55 @@ func HandleGetResourceClassNotFound(t *testing.T, fakeServer th.FakeServer) { w.WriteHeader(http.StatusNotFound) }) } + +func HandleCreateResourceClassSuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes", + 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, `{"name": "CUSTOM_NEW_RC"}`) + + w.WriteHeader(http.StatusCreated) + }) +} + +func HandleCreateResourceClassConflict(t *testing.T, fakeServer th.FakeServer) { + // We simulate a conflict by trying to create a resource class that already exists. + fakeServer.Mux.HandleFunc("/resource_classes", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusConflict) + }) +} + +func HandleUpdateResourceClassSuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes/"+NewResourceClass, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusCreated) + }) +} + +func HandleUpdateResourceClassExists(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes/"+PresentResourceClass, + 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) + }) +} + +func HandleUpdateResourceClassNonCustom(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes/VCPU", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusBadRequest) + }) +} diff --git a/openstack/placement/v1/resourceclasses/testing/requests_test.go b/openstack/placement/v1/resourceclasses/testing/requests_test.go index 4bb1f29f3c..e1a821f0de 100644 --- a/openstack/placement/v1/resourceclasses/testing/requests_test.go +++ b/openstack/placement/v1/resourceclasses/testing/requests_test.go @@ -45,3 +45,61 @@ func TestGetResourceClassNotFound(t *testing.T) { _, err := resourceclasses.Get(context.TODO(), client.ServiceClient(fakeServer), AbsentResourceClass).Extract() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } + +func TestCreateResourceClassSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleCreateResourceClassSuccess(t, fakeServer) + + createOpts := resourceclasses.CreateOpts{ + Name: NewResourceClass, + } + + err := resourceclasses.Create(context.TODO(), client.ServiceClient(fakeServer), createOpts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestCreateResourceClassConflict(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleCreateResourceClassConflict(t, fakeServer) + + createOpts := resourceclasses.CreateOpts{ + Name: PresentResourceClass, + } + + err := resourceclasses.Create(context.TODO(), client.ServiceClient(fakeServer), createOpts).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) +} + +func TestUpdateResourceClassCreateSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleUpdateResourceClassSuccess(t, fakeServer) + + err := resourceclasses.Update(context.TODO(), client.ServiceClient(fakeServer), NewResourceClass).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUpdateResourceClassExists(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleUpdateResourceClassExists(t, fakeServer) + + err := resourceclasses.Update(context.TODO(), client.ServiceClient(fakeServer), PresentResourceClass).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestUpdateResourceClassNonCustom(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleUpdateResourceClassNonCustom(t, fakeServer) + + err := resourceclasses.Update(context.TODO(), client.ServiceClient(fakeServer), "VCPU").ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} diff --git a/openstack/placement/v1/resourceclasses/urls.go b/openstack/placement/v1/resourceclasses/urls.go index 0fec96e30c..3dc3f82d19 100644 --- a/openstack/placement/v1/resourceclasses/urls.go +++ b/openstack/placement/v1/resourceclasses/urls.go @@ -8,6 +8,14 @@ func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("resource_classes") } +func createURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL("resource_classes") +} + func getURL(client *gophercloud.ServiceClient, name string) string { return client.ServiceURL("resource_classes", name) } + +func updateURL(client *gophercloud.ServiceClient, name string) string { + return client.ServiceURL("resource_classes", name) +} From 7ed59bb3448c1ab8d84a9e5cb66fda66c2b9b70a Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Wed, 1 Apr 2026 20:12:12 +0200 Subject: [PATCH 2274/2296] Implement UPDATE operations on Placement resource providers inventories Related: #526 API reference: https://docs.openstack.org/api-ref/placement/#update-resource-provider-inventories https://docs.openstack.org/api-ref/placement/#update-resource-provider-inventory Code reference: https://opendev.org/openstack/placement/src/branch/stable/2026.1/placement/handlers/inventory.py#L257-L277 https://opendev.org/openstack/placement/src/branch/stable/2026.1/placement/handlers/inventory.py#L280-L307 Signed-off-by: Dominik Danelski --- .../placement/v1/resourceproviders_test.go | 147 ++++++++++++++++++ .../placement/v1/resourceproviders/doc.go | 51 ++++++ .../v1/resourceproviders/requests.go | 58 +++++++ .../placement/v1/resourceproviders/results.go | 18 +++ .../testing/fixtures_test.go | 132 ++++++++++++++++ .../testing/requests_test.go | 48 ++++++ .../placement/v1/resourceproviders/urls.go | 4 + 7 files changed, 458 insertions(+) diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index bd6c3f1fa4..b23cb318ae 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -4,14 +4,19 @@ package v1 import ( "context" + "net/http" "testing" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" th "github.com/gophercloud/gophercloud/v2/testhelper" ) +const InventoryResourceClass = "VCPU" +const NonExistentRPUUID = "00000000-0000-0000-0000-000000000000" + func TestResourceProviderList(t *testing.T) { clients.RequireAdmin(t) @@ -100,6 +105,148 @@ func TestResourceProviderInventories(t *testing.T) { tools.PrintResource(t, usage) } +func TestResourceProviderUpdateInventory(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + // Arrange: Get the current inventory to retrieve the generation + inventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + // Arrange: The resource class on this provider must exist first + seedOpts := resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventories: map[string]resourceproviders.Inventory{ + InventoryResourceClass: { + AllocationRatio: 1.0, + MaxUnit: 4, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 4, + }, + }, + } + + seededInventories, err := resourceproviders.UpdateInventories(context.TODO(), client, resourceProvider.UUID, seedOpts).Extract() + th.AssertNoErr(t, err) + + expectedInventory := resourceproviders.Inventory{ + AllocationRatio: 1.0, + MaxUnit: 8, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 8, + } + + updateOpts := resourceproviders.UpdateInventoryOpts{ + ResourceProviderGeneration: seededInventories.ResourceProviderGeneration, + Inventory: expectedInventory, + } + + _, err = resourceproviders.UpdateInventory(context.TODO(), client, resourceProvider.UUID, InventoryResourceClass, updateOpts).Extract() + th.AssertNoErr(t, err) + + updatedInventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + actualInventory, ok := updatedInventories.Inventories[InventoryResourceClass] + th.AssertEquals(t, true, ok) + th.AssertDeepEquals(t, expectedInventory, actualInventory) +} + +func TestResourceProviderUpdateInventoryNotFound(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + updateOpts := resourceproviders.UpdateInventoryOpts{ + ResourceProviderGeneration: 0, + Inventory: resourceproviders.Inventory{ + AllocationRatio: 1.0, + MaxUnit: 1, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 1, + }, + } + + _, err = resourceproviders.UpdateInventory(context.TODO(), client, NonExistentRPUUID, InventoryResourceClass, updateOpts).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestResourceProviderUpdateInventories(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + // Arrange: Get the current inventory to retrieve the generation + inventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + expectedInventories := map[string]resourceproviders.Inventory{ + "DISK_GB": { + AllocationRatio: 1.0, + MaxUnit: 100, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 100, + }, + } + + updateOpts := resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventories: expectedInventories, + } + + _, err = resourceproviders.UpdateInventories(context.TODO(), client, resourceProvider.UUID, updateOpts).Extract() + th.AssertNoErr(t, err) + + updatedInventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expectedInventories, updatedInventories.Inventories) +} + +func TestResourceProviderUpdateInventoriesNotFound(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + updateOpts := resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: 0, + Inventories: map[string]resourceproviders.Inventory{ + InventoryResourceClass: { + AllocationRatio: 1.0, + MaxUnit: 1, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 1, + }, + }, + } + + _, err = resourceproviders.UpdateInventories(context.TODO(), client, NonExistentRPUUID, updateOpts).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + func TestResourceProviderTraits(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index d659558f15..729222ab9b 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -75,6 +75,57 @@ Example to get resource providers inventories panic(err) } +Example to update (replace) all resource provider inventories + + inventories, err := resourceproviders.GetInventories(context.TODO(), placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } + + updateInventoriesOpts := resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventories: map[string]resourceproviders.Inventory{ + "VCPU": { + Total: 4, + Reserved: 0, + MinUnit: 1, + MaxUnit: 4, + StepSize: 1, + AllocationRatio: 16.0, + }, + }, + } + + rp, err = resourceproviders.UpdateInventories(context.TODO(), placementClient, resourceProviderID, updateInventoriesOpts).Extract() + if err != nil { + panic(err) + } + +Example to update one existing resource provider inventory + + inventories, err := resourceproviders.GetInventories(context.TODO(), placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } + + // UpdateInventory updates an existing resource class inventory. + updateInventoryOpts := resourceproviders.UpdateInventoryOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventory: resourceproviders.Inventory{ + Total: 4, + Reserved: 0, + MinUnit: 1, + MaxUnit: 4, + StepSize: 1, + AllocationRatio: 16.0, + }, + } + + rpInventory, err := resourceproviders.UpdateInventory(context.TODO(), placementClient, resourceProviderID, "VCPU", updateInventoryOpts).Extract() + if err != nil { + panic(err) + } + Example to get resource providers traits rp, err := resourceproviders.GetTraits(context.TODO(), placementClient, resourceProviderID).Extract() diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index f0dfa9d66f..1ef7690ad1 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -164,6 +164,64 @@ func GetInventories(ctx context.Context, client *gophercloud.ServiceClient, reso return } +// UpdateInventoriesOptsBuilder allows extensions to add additional parameters to the +// UpdateInventories request. +type UpdateInventoriesOptsBuilder interface { + ToResourceProviderUpdateInventoriesMap() (map[string]any, error) +} + +// UpdateInventoriesOpts represents options used to update all inventories of a resource provider. +type UpdateInventoriesOpts = ResourceProviderInventories + +// ToResourceProviderUpdateInventoriesMap constructs a request body from UpdateInventoriesOpts. +func (opts UpdateInventoriesOpts) ToResourceProviderUpdateInventoriesMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// UpdateInventories updates all inventories of a resource provider. +func UpdateInventories(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string, opts UpdateInventoriesOptsBuilder) (r GetInventoriesResult) { + b, err := opts.ToResourceProviderUpdateInventoriesMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(ctx, getResourceProviderInventoriesURL(client, resourceProviderID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateInventoryOptsBuilder allows extensions to add additional parameters to the +// UpdateInventory request. +type UpdateInventoryOptsBuilder interface { + ToResourceProviderUpdateInventoryMap() (map[string]any, error) +} + +// UpdateInventoryOpts represents options used to update one inventory of a resource provider. +type UpdateInventoryOpts = ResourceProviderInventory + +// ToResourceProviderUpdateInventoryMap constructs a request body from UpdateInventoryOpts. +func (opts UpdateInventoryOpts) ToResourceProviderUpdateInventoryMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// UpdateInventory updates one inventory of a resource provider. +func UpdateInventory(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID, resourceClass string, opts UpdateInventoryOptsBuilder) (r UpdateInventoryResult) { + b, err := opts.ToResourceProviderUpdateInventoryMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(ctx, updateResourceProviderInventoryURL(client, resourceProviderID, resourceClass), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + func GetAllocations(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r GetAllocationsResult) { resp, err := client.Get(ctx, getResourceProviderAllocationsURL(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 19b71103e1..4c00bad016 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -56,6 +56,11 @@ type ResourceProviderInventories struct { Inventories map[string]Inventory `json:"inventories"` } +type ResourceProviderInventory struct { + ResourceProviderGeneration int `json:"resource_provider_generation"` + Inventory +} + type ResourceProviderAllocations struct { ResourceProviderGeneration int `json:"resource_provider_generation"` Allocations map[string]Allocation `json:"allocations"` @@ -153,6 +158,19 @@ func (r GetInventoriesResult) Extract() (*ResourceProviderInventories, error) { return &s, err } +// UpdateInventoryResult is the response of an Update inventory operation. Call its Extract method +// to interpret it as a ResourceProviderInventory. +type UpdateInventoryResult struct { + gophercloud.Result +} + +// Extract interprets a UpdateInventoryResult as a ResourceProviderInventory. +func (r UpdateInventoryResult) Extract() (*ResourceProviderInventory, error) { + var s ResourceProviderInventory + err := r.ExtractInto(&s) + return &s, err +} + // GetAllocationsResult is the response of a Get allocations operations. Call its Extract method // to interpret it as a ResourceProviderAllocations. type GetAllocationsResult struct { diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index 6ac4b8ca73..f50a7f4547 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -12,6 +12,8 @@ import ( ) const ResourceProviderTestID = "99c09379-6e52-4ef8-9a95-b9ce6f68452e" +const PresentInventoryResourceClass = "VCPU" +const NonExistentRPID = "00000000-0000-0000-0000-000000000000" const ResourceProvidersBody = ` { @@ -128,6 +130,62 @@ const InventoriesBody = ` } ` +const UpdateInventoriesRequest = ` +{ + "resource_provider_generation": 7, + "inventories": { + "DISK_GB": { + "allocation_ratio": 1.0, + "max_unit": 35, + "min_unit": 1, + "reserved": 0, + "step_size": 1, + "total": 35 + }, + "MEMORY_MB": { + "allocation_ratio": 1.5, + "max_unit": 5825, + "min_unit": 1, + "reserved": 512, + "step_size": 1, + "total": 5825 + }, + "VCPU": { + "allocation_ratio": 16.0, + "max_unit": 4, + "min_unit": 1, + "reserved": 0, + "step_size": 1, + "total": 4 + } + } +} +` + +const InventoryBody = ` +{ + "resource_provider_generation": 7, + "allocation_ratio": 16.0, + "max_unit": 4, + "min_unit": 1, + "reserved": 0, + "step_size": 1, + "total": 4 +} +` + +const UpdateInventoryRequest = ` +{ + "resource_provider_generation": 7, + "allocation_ratio": 16.0, + "max_unit": 4, + "min_unit": 1, + "reserved": 0, + "step_size": 1, + "total": 4 +} +` + const AllocationsBody = ` { "allocations": { @@ -236,6 +294,18 @@ var ExpectedInventories = resourceproviders.ResourceProviderInventories{ }, } +var ExpectedInventory = resourceproviders.ResourceProviderInventory{ + ResourceProviderGeneration: 7, + Inventory: resourceproviders.Inventory{ + AllocationRatio: 16.0, + MaxUnit: 4, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 4, + }, +} + var ExpectedAllocations = resourceproviders.ResourceProviderAllocations{ ResourceProviderGeneration: 12, Allocations: map[string]resourceproviders.Allocation{ @@ -358,6 +428,68 @@ func HandleResourceProviderGetInventories(t *testing.T, fakeServer th.FakeServer }) } +func HandleResourceProviderPutInventories(t *testing.T, fakeServer th.FakeServer) { + inventoriesTestURL := fmt.Sprintf("/resource_providers/%s/inventories", ResourceProviderTestID) + + fakeServer.Mux.HandleFunc(inventoriesTestURL, + 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, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, UpdateInventoriesRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, InventoriesBody) + }) +} + +func HandleResourceProviderPutInventoriesNotFound(t *testing.T, fakeServer th.FakeServer) { + inventoriesNotFoundURL := fmt.Sprintf("/resource_providers/%s/inventories", NonExistentRPID) + + fakeServer.Mux.HandleFunc(inventoriesNotFoundURL, + 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.StatusNotFound) + fmt.Fprint(w, `{"errors":[{"status":404,"title":"Not Found"}]}`) + }) +} + +func HandleResourceProviderPutInventory(t *testing.T, fakeServer th.FakeServer) { + inventoryTestURL := fmt.Sprintf("/resource_providers/%s/inventories/%s", ResourceProviderTestID, PresentInventoryResourceClass) + + fakeServer.Mux.HandleFunc(inventoryTestURL, + 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, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, UpdateInventoryRequest) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, InventoryBody) + }) +} + +func HandleResourceProviderPutInventoryNotFound(t *testing.T, fakeServer th.FakeServer) { + inventoryNotFoundURL := fmt.Sprintf("/resource_providers/%s/inventories/%s", NonExistentRPID, PresentInventoryResourceClass) + + fakeServer.Mux.HandleFunc(inventoryNotFoundURL, + 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.StatusNotFound) + fmt.Fprint(w, `{"errors":[{"status":404,"title":"Not Found"}]}`) + }) +} + func HandleResourceProviderGetAllocations(t *testing.T, fakeServer th.FakeServer) { allocationsTestUrl := fmt.Sprintf("/resource_providers/%s/allocations", ResourceProviderTestID) diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 4633c81136..67546d1930 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -2,8 +2,10 @@ package testing import ( "context" + "net/http" "testing" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" "github.com/gophercloud/gophercloud/v2/pagination" @@ -124,6 +126,52 @@ func TestGetResourceProvidersInventories(t *testing.T) { th.AssertDeepEquals(t, ExpectedInventories, *actual) } +func TestUpdateResourceProvidersInventories(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderPutInventories(t, fakeServer) + + opts := resourceproviders.UpdateInventoriesOpts(ExpectedInventories) + actual, err := resourceproviders.UpdateInventories(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedInventories, *actual) +} + +func TestUpdateResourceProvidersInventoriesNotFound(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderPutInventoriesNotFound(t, fakeServer) + + opts := resourceproviders.UpdateInventoriesOpts(ExpectedInventories) + _, err := resourceproviders.UpdateInventories(context.TODO(), client.ServiceClient(fakeServer), NonExistentRPID, opts).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestUpdateResourceProviderInventory(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderPutInventory(t, fakeServer) + + opts := resourceproviders.UpdateInventoryOpts(ExpectedInventory) + actual, err := resourceproviders.UpdateInventory(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, PresentInventoryResourceClass, opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedInventory, *actual) +} + +func TestUpdateResourceProviderInventoryNotFound(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderPutInventoryNotFound(t, fakeServer) + + opts := resourceproviders.UpdateInventoryOpts(ExpectedInventory) + _, err := resourceproviders.UpdateInventory(context.TODO(), client.ServiceClient(fakeServer), NonExistentRPID, PresentInventoryResourceClass, opts).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + func TestGetResourceProvidersAllocations(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 037149b684..5fe326e4bc 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -30,6 +30,10 @@ func getResourceProviderInventoriesURL(client *gophercloud.ServiceClient, resour return client.ServiceURL(apiName, resourceProviderID, "inventories") } +func updateResourceProviderInventoryURL(client *gophercloud.ServiceClient, resourceProviderID, resourceClass string) string { + return client.ServiceURL(apiName, resourceProviderID, "inventories", resourceClass) +} + func getResourceProviderAllocationsURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "allocations") } From 6b55a24bcb5a51d908f2db3ba5a30269169ead03 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Wed, 1 Apr 2026 18:16:46 +0200 Subject: [PATCH 2275/2296] Implement resource providers inventory GET Related #526 API reference: https://docs.openstack.org/api-ref/placement/#show-resource-provider-inventory Code reference: https://opendev.org/openstack/placement/src/branch/stable/2026.1/placement/handlers/inventory.py#L280-L307 Signed-off-by: Dominik Danelski --- .../placement/v1/resourceproviders_test.go | 67 +++++++++++++++++++ .../placement/v1/resourceproviders/doc.go | 7 ++ .../v1/resourceproviders/requests.go | 6 ++ .../placement/v1/resourceproviders/results.go | 13 ++++ .../testing/fixtures_test.go | 27 ++++++++ .../testing/requests_test.go | 21 ++++++ .../placement/v1/resourceproviders/urls.go | 4 ++ 7 files changed, 145 insertions(+) diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index b23cb318ae..c1aa52ea94 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -15,6 +15,7 @@ import ( ) const InventoryResourceClass = "VCPU" +const MissingInventoryResourceClass = "NO_SUCH_CLASS" const NonExistentRPUUID = "00000000-0000-0000-0000-000000000000" func TestResourceProviderList(t *testing.T) { @@ -105,6 +106,72 @@ func TestResourceProviderInventories(t *testing.T) { tools.PrintResource(t, usage) } +func TestResourceProviderInventory(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + inventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + seededInventories, err := resourceproviders.UpdateInventories(context.TODO(), client, resourceProvider.UUID, resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventories: map[string]resourceproviders.Inventory{ + InventoryResourceClass: { + AllocationRatio: 1.0, + MaxUnit: 4, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 4, + }, + }, + }).Extract() + th.AssertNoErr(t, err) + + inventory, err := resourceproviders.GetInventory(context.TODO(), client, resourceProvider.UUID, InventoryResourceClass).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, seededInventories.ResourceProviderGeneration, inventory.ResourceProviderGeneration) + th.AssertDeepEquals(t, seededInventories.Inventories[InventoryResourceClass], inventory.Inventory) +} + +func TestResourceProviderInventoryNotFound(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + inventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + _, err = resourceproviders.UpdateInventories(context.TODO(), client, resourceProvider.UUID, resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventories: map[string]resourceproviders.Inventory{ + InventoryResourceClass: { + AllocationRatio: 1.0, + MaxUnit: 4, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 4, + }, + }, + }).Extract() + th.AssertNoErr(t, err) + + _, err = resourceproviders.GetInventory(context.TODO(), client, resourceProvider.UUID, MissingInventoryResourceClass).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + func TestResourceProviderUpdateInventory(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 729222ab9b..4a1270794a 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -75,6 +75,13 @@ Example to get resource providers inventories panic(err) } +Example to get one resource provider inventory + + rpInventory, err := resourceproviders.GetInventory(context.TODO(), placementClient, resourceProviderID, "VCPU").Extract() + if err != nil { + panic(err) + } + Example to update (replace) all resource provider inventories inventories, err := resourceproviders.GetInventories(context.TODO(), placementClient, resourceProviderID).Extract() diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 1ef7690ad1..2985d63794 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -164,6 +164,12 @@ func GetInventories(ctx context.Context, client *gophercloud.ServiceClient, reso return } +func GetInventory(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID, resourceClass string) (r GetInventoryResult) { + resp, err := client.Get(ctx, getResourceProviderInventoryURL(client, resourceProviderID, resourceClass), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + // UpdateInventoriesOptsBuilder allows extensions to add additional parameters to the // UpdateInventories request. type UpdateInventoriesOptsBuilder interface { diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index 4c00bad016..7f67c39819 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -158,6 +158,19 @@ func (r GetInventoriesResult) Extract() (*ResourceProviderInventories, error) { return &s, err } +// GetInventoryResult is the response of a Get inventory operation. Call its Extract method +// to interpret it as a ResourceProviderInventory. +type GetInventoryResult struct { + gophercloud.Result +} + +// Extract interprets a GetInventoryResult as a ResourceProviderInventory. +func (r GetInventoryResult) Extract() (*ResourceProviderInventory, error) { + var s ResourceProviderInventory + err := r.ExtractInto(&s) + return &s, err +} + // UpdateInventoryResult is the response of an Update inventory operation. Call its Extract method // to interpret it as a ResourceProviderInventory. type UpdateInventoryResult struct { diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index f50a7f4547..d904e1e98c 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -13,6 +13,7 @@ import ( const ResourceProviderTestID = "99c09379-6e52-4ef8-9a95-b9ce6f68452e" const PresentInventoryResourceClass = "VCPU" +const MissingInventoryResourceClass = "NO_SUCH_CLASS" const NonExistentRPID = "00000000-0000-0000-0000-000000000000" const ResourceProvidersBody = ` @@ -428,6 +429,32 @@ func HandleResourceProviderGetInventories(t *testing.T, fakeServer th.FakeServer }) } +func HandleResourceProviderGetInventory(t *testing.T, fakeServer th.FakeServer) { + inventoryTestURL := fmt.Sprintf("/resource_providers/%s/inventories/%s", ResourceProviderTestID, PresentInventoryResourceClass) + + fakeServer.Mux.HandleFunc(inventoryTestURL, + 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.Fprint(w, InventoryBody) + }) +} + +func HandleResourceProviderGetInventoryNotFound(t *testing.T, fakeServer th.FakeServer) { + inventoryNotFoundURL := fmt.Sprintf("/resource_providers/%s/inventories/%s", ResourceProviderTestID, MissingInventoryResourceClass) + + fakeServer.Mux.HandleFunc(inventoryNotFoundURL, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + w.WriteHeader(http.StatusNotFound) + }) +} + func HandleResourceProviderPutInventories(t *testing.T, fakeServer th.FakeServer) { inventoriesTestURL := fmt.Sprintf("/resource_providers/%s/inventories", ResourceProviderTestID) diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 67546d1930..5b12d37c89 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -126,6 +126,27 @@ func TestGetResourceProvidersInventories(t *testing.T) { th.AssertDeepEquals(t, ExpectedInventories, *actual) } +func TestGetResourceProviderInventory(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderGetInventory(t, fakeServer) + + actual, err := resourceproviders.GetInventory(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, PresentInventoryResourceClass).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedInventory, *actual) +} + +func TestGetResourceProviderInventoryNotFound(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderGetInventoryNotFound(t, fakeServer) + + _, err := resourceproviders.GetInventory(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, MissingInventoryResourceClass).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + func TestUpdateResourceProvidersInventories(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 5fe326e4bc..61d6e1e811 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -30,6 +30,10 @@ func getResourceProviderInventoriesURL(client *gophercloud.ServiceClient, resour return client.ServiceURL(apiName, resourceProviderID, "inventories") } +func getResourceProviderInventoryURL(client *gophercloud.ServiceClient, resourceProviderID, resourceClass string) string { + return client.ServiceURL(apiName, resourceProviderID, "inventories", resourceClass) +} + func updateResourceProviderInventoryURL(client *gophercloud.ServiceClient, resourceProviderID, resourceClass string) string { return client.ServiceURL(apiName, resourceProviderID, "inventories", resourceClass) } From b4771fb6d359c492668161414125e1cd8e407184 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Thu, 2 Apr 2026 11:31:47 +0200 Subject: [PATCH 2276/2296] Implement resource providers inventory DELETE API reference: https://docs.openstack.org/api-ref/placement/#delete-resource-provider-inventories https://docs.openstack.org/api-ref/placement/#delete-resource-provider-inventory Code reference: https://opendev.org/openstack/placement/src/branch/stable/2026.1/placement/handlers/inventory.py#L377-L410 https://opendev.org/openstack/placement/src/branch/stable/2026.1/placement/handlers/inventory.py#L222-L254 Signed-off-by: Dominik Danelski --- .../placement/v1/resourceproviders_test.go | 102 ++++++++++++++++++ .../placement/v1/resourceproviders/doc.go | 16 +++ .../v1/resourceproviders/requests.go | 14 +++ .../testing/fixtures_test.go | 52 +++++++++ .../testing/requests_test.go | 40 +++++++ .../placement/v1/resourceproviders/urls.go | 8 ++ 6 files changed, 232 insertions(+) diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index c1aa52ea94..ecad86b4d9 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -251,6 +251,108 @@ func TestResourceProviderUpdateInventoryNotFound(t *testing.T) { th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } +func TestResourceProviderDeleteInventorySuccess(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + // Arrange: Get the current inventory to retrieve the generation + inventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + _, err = resourceproviders.UpdateInventories(context.TODO(), client, resourceProvider.UUID, resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventories: map[string]resourceproviders.Inventory{ + InventoryResourceClass: { + AllocationRatio: 1.0, + MaxUnit: 4, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 4, + }, + }, + }).Extract() + th.AssertNoErr(t, err) + + err = resourceproviders.DeleteInventory(context.TODO(), client, resourceProvider.UUID, InventoryResourceClass).ExtractErr() + th.AssertNoErr(t, err) + + // Assert: The inventory should no longer be found + updatedInventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + _, found := updatedInventories.Inventories[InventoryResourceClass] + th.AssertEquals(t, false, found) +} + +func TestResourceProviderDeleteInventoryNotFound(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + err = resourceproviders.DeleteInventory(context.TODO(), client, resourceProvider.UUID, MissingInventoryResourceClass).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestResourceProviderDeleteInventoriesSuccess(t *testing.T) { + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + inventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + + _, err = resourceproviders.UpdateInventories(context.TODO(), client, resourceProvider.UUID, resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventories: map[string]resourceproviders.Inventory{ + InventoryResourceClass: { + AllocationRatio: 1.0, + MaxUnit: 4, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 4, + }, + "MEMORY_MB": { + AllocationRatio: 1.0, + MaxUnit: 1024, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 1024, + }, + }, + }).Extract() + th.AssertNoErr(t, err) + + seededInventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, 2, len(seededInventories.Inventories)) + + err = resourceproviders.DeleteInventories(context.TODO(), client, resourceProvider.UUID).ExtractErr() + th.AssertNoErr(t, err) + + updatedInventories, err := resourceproviders.GetInventories(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(updatedInventories.Inventories)) +} + func TestResourceProviderUpdateInventories(t *testing.T) { clients.RequireAdmin(t) diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 4a1270794a..d47be9310c 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -133,6 +133,22 @@ Example to update one existing resource provider inventory panic(err) } +Example to delete one existing resource provider inventory +Since this request does not accept the resource provider generation, it is not safe to use when multiple threads are managing inventories for a single provider. In such situations use UpdateInventories with the empty inventory. + + err = resourceproviders.DeleteInventory(context.TODO(), placementClient, resourceProviderID, "VCPU").ExtractErr() + if err != nil { + panic(err) + } + +Example to delete all resource provider inventories +Since this request does not accept the resource provider generation, it is not safe to use when multiple threads are managing inventories for a single provider. In such situations use UpdateInventories with an empty inventory map. + + err = resourceproviders.DeleteInventories(context.TODO(), placementClient, resourceProviderID).ExtractErr() + if err != nil { + panic(err) + } + Example to get resource providers traits rp, err := resourceproviders.GetTraits(context.TODO(), placementClient, resourceProviderID).Extract() diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 2985d63794..a1eedeb28d 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -199,6 +199,13 @@ func UpdateInventories(ctx context.Context, client *gophercloud.ServiceClient, r return } +// DeleteInventories deletes all inventories from a resource provider. +func DeleteInventories(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteResourceProviderInventoriesURL(client, resourceProviderID), &gophercloud.RequestOpts{OkCodes: []int{204}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + // UpdateInventoryOptsBuilder allows extensions to add additional parameters to the // UpdateInventory request. type UpdateInventoryOptsBuilder interface { @@ -228,6 +235,13 @@ func UpdateInventory(ctx context.Context, client *gophercloud.ServiceClient, res return } +// DeleteInventory deletes one inventory from a resource provider. +func DeleteInventory(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID, resourceClass string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteResourceProviderInventoryURL(client, resourceProviderID, resourceClass), &gophercloud.RequestOpts{OkCodes: []int{204}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + func GetAllocations(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r GetAllocationsResult) { resp, err := client.Get(ctx, getResourceProviderAllocationsURL(client, resourceProviderID), &r.Body, nil) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index d904e1e98c..7a0b176114 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -517,6 +517,58 @@ func HandleResourceProviderPutInventoryNotFound(t *testing.T, fakeServer th.Fake }) } +func HandleResourceProviderDeleteInventorySuccess(t *testing.T, fakeServer th.FakeServer) { + inventoryDeleteURL := fmt.Sprintf("/resource_providers/%s/inventories/%s", ResourceProviderTestID, PresentInventoryResourceClass) + + fakeServer.Mux.HandleFunc(inventoryDeleteURL, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.AssertEquals(t, "", r.URL.RawQuery) + w.WriteHeader(http.StatusNoContent) + }) +} + +func HandleResourceProviderDeleteInventoryInUse(t *testing.T, fakeServer th.FakeServer) { + inventoryDeleteURL := fmt.Sprintf("/resource_providers/%s/inventories/%s", ResourceProviderTestID, PresentInventoryResourceClass) + + fakeServer.Mux.HandleFunc(inventoryDeleteURL, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.AssertEquals(t, "", r.URL.RawQuery) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusConflict) + fmt.Fprint(w, `{"errors":[{"status":409,"title":"Conflict","detail":"Inventory is in use.","code":"placement.inventory.inuse"}]}`) + }) +} + +func HandleResourceProviderDeleteInventoriesSuccess(t *testing.T, fakeServer th.FakeServer) { + inventoriesDeleteURL := fmt.Sprintf("/resource_providers/%s/inventories", ResourceProviderTestID) + + fakeServer.Mux.HandleFunc(inventoriesDeleteURL, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.AssertEquals(t, "", r.URL.RawQuery) + w.WriteHeader(http.StatusNoContent) + }) +} + +func HandleResourceProviderDeleteInventoriesConflict(t *testing.T, fakeServer th.FakeServer) { + inventoriesDeleteURL := fmt.Sprintf("/resource_providers/%s/inventories", ResourceProviderTestID) + + fakeServer.Mux.HandleFunc(inventoriesDeleteURL, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.AssertEquals(t, "", r.URL.RawQuery) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusConflict) + fmt.Fprint(w, `{"errors":[{"status":409,"title":"Conflict","detail":"Inventory is in use.","code":"placement.inventory.inuse"}]}`) + }) +} + func HandleResourceProviderGetAllocations(t *testing.T, fakeServer th.FakeServer) { allocationsTestUrl := fmt.Sprintf("/resource_providers/%s/allocations", ResourceProviderTestID) diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 5b12d37c89..693b0d80ff 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -193,6 +193,46 @@ func TestUpdateResourceProviderInventoryNotFound(t *testing.T) { th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } +func TestDeleteResourceProviderInventorySuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderDeleteInventorySuccess(t, fakeServer) + + err := resourceproviders.DeleteInventory(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, PresentInventoryResourceClass).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDeleteResourceProviderInventoryInUse(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderDeleteInventoryInUse(t, fakeServer) + + err := resourceproviders.DeleteInventory(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, PresentInventoryResourceClass).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) +} + +func TestDeleteResourceProviderInventoriesSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderDeleteInventoriesSuccess(t, fakeServer) + + err := resourceproviders.DeleteInventories(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDeleteResourceProviderInventoriesConflict(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderDeleteInventoriesConflict(t, fakeServer) + + err := resourceproviders.DeleteInventories(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) +} + func TestGetResourceProvidersAllocations(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 61d6e1e811..fbc0ff2955 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -30,6 +30,10 @@ func getResourceProviderInventoriesURL(client *gophercloud.ServiceClient, resour return client.ServiceURL(apiName, resourceProviderID, "inventories") } +func deleteResourceProviderInventoriesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID, "inventories") +} + func getResourceProviderInventoryURL(client *gophercloud.ServiceClient, resourceProviderID, resourceClass string) string { return client.ServiceURL(apiName, resourceProviderID, "inventories", resourceClass) } @@ -38,6 +42,10 @@ func updateResourceProviderInventoryURL(client *gophercloud.ServiceClient, resou return client.ServiceURL(apiName, resourceProviderID, "inventories", resourceClass) } +func deleteResourceProviderInventoryURL(client *gophercloud.ServiceClient, resourceProviderID, resourceClass string) string { + return client.ServiceURL(apiName, resourceProviderID, "inventories", resourceClass) +} + func getResourceProviderAllocationsURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "allocations") } From 0345eacab4c45e614edce5f26d35a8144d01e027 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Tue, 31 Mar 2026 22:10:49 +0200 Subject: [PATCH 2277/2296] Implement resource_classes DELETE operation Related #526 API reference: https://docs.openstack.org/api-ref/placement/#resource-classes Code reference: https://github.com/openstack/placement/blob/stable/2025.1/placement/handlers/resource_class.py#L90-L110 Signed-off-by: Dominik Danelski --- .../placement/v1/resourceclasses_test.go | 62 +++++++++++++++++++ openstack/placement/v1/resourceclasses/doc.go | 9 +++ .../placement/v1/resourceclasses/requests.go | 9 +++ .../placement/v1/resourceclasses/results.go | 6 ++ .../resourceclasses/testing/fixtures_test.go | 30 +++++++++ .../resourceclasses/testing/requests_test.go | 31 ++++++++++ .../placement/v1/resourceclasses/urls.go | 4 ++ 7 files changed, 151 insertions(+) diff --git a/internal/acceptance/openstack/placement/v1/resourceclasses_test.go b/internal/acceptance/openstack/placement/v1/resourceclasses_test.go index d2ea98fb51..13f7663e40 100644 --- a/internal/acceptance/openstack/placement/v1/resourceclasses_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceclasses_test.go @@ -157,3 +157,65 @@ func TestResourceClassCreateByUpdateNonCustomName(t *testing.T) { // Assert: We get 400 th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } + +func TestResourceClassDeleteSuccess(t *testing.T) { + // Resource classes were introduced in 1.2 + clients.SkipReleasesBelow(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.2" + + name := strings.ToUpper(tools.RandomString("CUSTOM_", 8)) + createOpts := resourceclasses.CreateOpts{ + Name: name, + } + + // Arrange: Create a resource class to delete + err = resourceclasses.Create(context.TODO(), client, createOpts).ExtractErr() + th.AssertNoErr(t, err) + + // Arrange: Sanity check, the newly created resource class exists + rc, err := resourceclasses.Get(context.TODO(), client, name).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, name, rc.Name) + + // Act: Delete the resource class + err = resourceclasses.Delete(context.TODO(), client, name).ExtractErr() + th.AssertNoErr(t, err) + + // Assert: The resource class no longer exists + _, err = resourceclasses.Get(context.TODO(), client, name).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestResourceClassDeleteNotFound(t *testing.T) { + // Resource classes were introduced in 1.2 + clients.SkipReleasesBelow(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.2" + + // Act: Try to delete a non-existent resource class + err = resourceclasses.Delete(context.TODO(), client, "CUSTOM_NON_EXISTENT_RC").ExtractErr() + // Assert: We get 404 + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestResourceClassDeleteStandardClass(t *testing.T) { + // Resource classes were introduced in 1.2 + clients.SkipReleasesBelow(t, "stable/ocata") + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.2" + + // Act: Try to delete a standard resource class + err = resourceclasses.Delete(context.TODO(), client, "VCPU").ExtractErr() + // Assert: We get 400 + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} diff --git a/openstack/placement/v1/resourceclasses/doc.go b/openstack/placement/v1/resourceclasses/doc.go index 53eae204c9..b1535af4ae 100644 --- a/openstack/placement/v1/resourceclasses/doc.go +++ b/openstack/placement/v1/resourceclasses/doc.go @@ -54,5 +54,14 @@ Example to ensure the existence of a resource class using PUT (idempotent creati if err != nil { panic(err) } + +Example to delete a resource class + + placementClient.Microversion = "1.2" + + err := resourceclasses.Delete(context.TODO(), placementClient, "CUSTOM_RESOURCE_CLASS").ExtractErr() + if err != nil { + panic(err) + } */ package resourceclasses diff --git a/openstack/placement/v1/resourceclasses/requests.go b/openstack/placement/v1/resourceclasses/requests.go index 4fccd33c9c..8621eddce5 100644 --- a/openstack/placement/v1/resourceclasses/requests.go +++ b/openstack/placement/v1/resourceclasses/requests.go @@ -62,3 +62,12 @@ func Update(ctx context.Context, client *gophercloud.ServiceClient, name string) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } + +// Delete deletes the resource class with the provided name. +func Delete(ctx context.Context, client *gophercloud.ServiceClient, name string) (r DeleteResult) { + resp, err := client.Delete(ctx, deleteURL(client, name), &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/placement/v1/resourceclasses/results.go b/openstack/placement/v1/resourceclasses/results.go index cbd5501b9c..61131c372e 100644 --- a/openstack/placement/v1/resourceclasses/results.go +++ b/openstack/placement/v1/resourceclasses/results.go @@ -47,6 +47,12 @@ type UpdateResult struct { gophercloud.ErrResult } +// DeleteResult is the response from a Delete operation. Call its ExtractErr to +// determine if the request succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + // ResourceClassesPage contains a single page of all resource classes from a List call. type ResourceClassesPage struct { pagination.SinglePageBase diff --git a/openstack/placement/v1/resourceclasses/testing/fixtures_test.go b/openstack/placement/v1/resourceclasses/testing/fixtures_test.go index 7b0819cdef..cfd5de8847 100644 --- a/openstack/placement/v1/resourceclasses/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceclasses/testing/fixtures_test.go @@ -169,3 +169,33 @@ func HandleUpdateResourceClassNonCustom(t *testing.T, fakeServer th.FakeServer) w.WriteHeader(http.StatusBadRequest) }) } + +func HandleDeleteResourceClassSuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes/"+PresentResourceClass, + 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 HandleDeleteResourceClassNotFound(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes/"+AbsentResourceClass, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNotFound) + }) +} + +func HandleDeleteResourceClassStandardClass(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/resource_classes/VCPU", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusBadRequest) + }) +} diff --git a/openstack/placement/v1/resourceclasses/testing/requests_test.go b/openstack/placement/v1/resourceclasses/testing/requests_test.go index e1a821f0de..9ff18e6a71 100644 --- a/openstack/placement/v1/resourceclasses/testing/requests_test.go +++ b/openstack/placement/v1/resourceclasses/testing/requests_test.go @@ -103,3 +103,34 @@ func TestUpdateResourceClassNonCustom(t *testing.T) { err := resourceclasses.Update(context.TODO(), client.ServiceClient(fakeServer), "VCPU").ExtractErr() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) } + +func TestDeleteResourceClassSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleDeleteResourceClassSuccess(t, fakeServer) + + err := resourceclasses.Delete(context.TODO(), client.ServiceClient(fakeServer), PresentResourceClass).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDeleteResourceClassNotFound(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleDeleteResourceClassNotFound(t, fakeServer) + + err := resourceclasses.Delete(context.TODO(), client.ServiceClient(fakeServer), AbsentResourceClass).ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestDeleteResourceClassStandardClass(t *testing.T) { + // Removing standard resource classes is not allowed. + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleDeleteResourceClassStandardClass(t, fakeServer) + + err := resourceclasses.Delete(context.TODO(), client.ServiceClient(fakeServer), "VCPU").ExtractErr() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} diff --git a/openstack/placement/v1/resourceclasses/urls.go b/openstack/placement/v1/resourceclasses/urls.go index 3dc3f82d19..85d9f77041 100644 --- a/openstack/placement/v1/resourceclasses/urls.go +++ b/openstack/placement/v1/resourceclasses/urls.go @@ -19,3 +19,7 @@ func getURL(client *gophercloud.ServiceClient, name string) string { func updateURL(client *gophercloud.ServiceClient, name string) string { return client.ServiceURL("resource_classes", name) } + +func deleteURL(client *gophercloud.ServiceClient, name string) string { + return client.ServiceURL("resource_classes", name) +} From 2d54a48f4af39860da3b1cec236472d2afb553dd Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Thu, 2 Apr 2026 15:11:50 +0200 Subject: [PATCH 2278/2296] Implement GET operation on Placement resource provider aggregates Related: #526 API reference: https://docs.openstack.org/api-ref/placement/#list-resource-provider-aggregates Code reference: https://opendev.org/openstack/placement/src/branch/stable/2026.1/placement/handlers/aggregate.py#L81-L99 Signed-off-by: Dominik Danelski --- .../placement/v1/resourceproviders_test.go | 69 +++++++++++++++++++ .../placement/v1/resourceproviders/doc.go | 9 +++ .../v1/resourceproviders/requests.go | 6 ++ .../placement/v1/resourceproviders/results.go | 19 +++++ .../testing/fixtures_test.go | 47 +++++++++++++ .../testing/requests_test.go | 23 +++++++ .../placement/v1/resourceproviders/urls.go | 4 ++ 7 files changed, 177 insertions(+) diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index bd6c3f1fa4..3514d7f2b3 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -4,8 +4,10 @@ package v1 import ( "context" + "net/http" "testing" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" @@ -135,3 +137,70 @@ func TestResourceProviderAllocations(t *testing.T) { tools.PrintResource(t, usage) } + +func TestResourceProviderAggregatesGetSuccess(t *testing.T) { + // Resource_provider_generation in the aggregates response was introduced in microversion 1.19. + clients.SkipReleasesBelow(t, "stable/ocata") + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + client.Microversion = "1.19" + + aggregates, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, aggregates.ResourceProviderGeneration != nil) +} + +func TestResourceProviderAggregatesGetPreGenerationSuccess(t *testing.T) { + // Resource provider aggregates operations were introduced in microversion 1.1. + clients.SkipReleasesBelow(t, "stable/ocata") + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + client.Microversion = "1.1" + + aggregates, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(aggregates.Aggregates)) + th.AssertDeepEquals(t, (*int)(nil), aggregates.ResourceProviderGeneration) +} + +func TestResourceProviderAggregatesGetNegative(t *testing.T) { + // Resource_provider_generation in the aggregates response was introduced in microversion 1.19. + clients.SkipReleasesBelow(t, "stable/ocata") + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.19" + + _, err = resourceproviders.GetAggregates(context.TODO(), client, "00000000-0000-0000-0000-000000000000").Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} + +func TestResourceProviderAggregatesGetPreGenerationNegative(t *testing.T) { + // Resource provider aggregates operations were introduced in microversion 1.1. + clients.SkipReleasesBelow(t, "stable/ocata") + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + client.Microversion = "1.1" + + _, err = resourceproviders.GetAggregates(context.TODO(), client, "00000000-0000-0000-0000-000000000000").Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index d659558f15..efd98b775d 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -88,5 +88,14 @@ Example to get resource providers allocations if err != nil { panic(err) } + +Example to get resource providers aggregates + + placementClient.Microversion = "1.1" + + rp, err := resourceproviders.GetAggregates(context.TODO(), placementClient, resourceProviderID).Extract() + if err != nil { + panic(err) + } */ package resourceproviders diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index f0dfa9d66f..5a8ece6876 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -176,6 +176,12 @@ func GetTraits(ctx context.Context, client *gophercloud.ServiceClient, resourceP return } +func GetAggregates(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string) (r GetAggregatesResult) { + resp, err := client.Get(ctx, getResourceProviderAggregatesURL(client, resourceProviderID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + // UpdateTraitsOptsBuilder allows extensions to add additional parameters to the // UpdateTraits request. type UpdateTraitsOptsBuilder interface { diff --git a/openstack/placement/v1/resourceproviders/results.go b/openstack/placement/v1/resourceproviders/results.go index 19b71103e1..3f65f97627 100644 --- a/openstack/placement/v1/resourceproviders/results.go +++ b/openstack/placement/v1/resourceproviders/results.go @@ -66,6 +66,12 @@ type ResourceProviderTraits struct { Traits []string `json:"traits"` } +type ResourceProviderAggregates struct { + // ResourceProviderGeneration is available from microversion 1.19 and later. + ResourceProviderGeneration *int `json:"resource_provider_generation"` + Aggregates []string `json:"aggregates"` +} + // resourceProviderResult is the response of a base ResourceProvider result. type resourceProviderResult struct { gophercloud.Result @@ -178,3 +184,16 @@ func (r GetTraitsResult) Extract() (*ResourceProviderTraits, error) { err := r.ExtractInto(&s) return &s, err } + +// GetAggregatesResult is the response of a Get aggregates operations. Call its Extract method +// to interpret it as a ResourceProviderAggregates. +type GetAggregatesResult struct { + gophercloud.Result +} + +// Extract interprets a GetAggregatesResult as a ResourceProviderAggregates. +func (r GetAggregatesResult) Extract() (*ResourceProviderAggregates, error) { + var s ResourceProviderAggregates + err := r.ExtractInto(&s) + return &s, err +} diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index 6ac4b8ca73..d3682a7177 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -164,6 +164,18 @@ const TraitsBody = ` } ` +const AggregatesBody = ` +{ + "resource_provider_generation": 1, + "aggregates": [ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4" + ] +} +` + +const AbsentResourceProviderID = "non-existent-rp" + var ExpectedResourceProvider1 = resourceproviders.ResourceProvider{ Generation: 1, UUID: "99c09379-6e52-4ef8-9a95-b9ce6f68452e", @@ -268,6 +280,14 @@ var ExpectedTraits = resourceproviders.ResourceProviderTraits{ }, } +var ExpectedAggregates = resourceproviders.ResourceProviderAggregates{ + ResourceProviderGeneration: 1, + Aggregates: []string{ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4", + }, +} + func HandleResourceProviderList(t *testing.T, fakeServer th.FakeServer) { fakeServer.Mux.HandleFunc("/resource_providers", func(w http.ResponseWriter, r *http.Request) { @@ -413,3 +433,30 @@ func HandleResourceProviderDeleteTraits(t *testing.T, fakeServer th.FakeServer) w.WriteHeader(http.StatusNoContent) }) } + +func HandleResourceProviderGetAggregatesSuccess(t *testing.T, fakeServer th.FakeServer) { + aggregatesTestURL := fmt.Sprintf("/resource_providers/%s/aggregates", ResourceProviderTestID) + + fakeServer.Mux.HandleFunc(aggregatesTestURL, + 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.Fprint(w, AggregatesBody) + }) +} + +func HandleResourceProviderGetAggregatesNotFound(t *testing.T, fakeServer th.FakeServer) { + aggregatesTestURL := fmt.Sprintf("/resource_providers/%s/aggregates", AbsentResourceProviderID) + + fakeServer.Mux.HandleFunc(aggregatesTestURL, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNotFound) + }) +} diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 4633c81136..4a97672c25 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -2,8 +2,10 @@ package testing import ( "context" + "net/http" "testing" + "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" "github.com/gophercloud/gophercloud/v2/pagination" @@ -167,3 +169,24 @@ func TestDeleteResourceProvidersTraits(t *testing.T) { err := resourceproviders.DeleteTraits(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).ExtractErr() th.AssertNoErr(t, err) } + +func TestGetResourceProviderAggregatesSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderGetAggregatesSuccess(t, fakeServer) + + actual, err := resourceproviders.GetAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAggregates, *actual) +} + +func TestGetResourceProviderAggregatesNotFound(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderGetAggregatesNotFound(t, fakeServer) + + _, err := resourceproviders.GetAggregates(context.TODO(), client.ServiceClient(fakeServer), AbsentResourceProviderID).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) +} diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 037149b684..821fdcf57a 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -37,3 +37,7 @@ func getResourceProviderAllocationsURL(client *gophercloud.ServiceClient, resour func getResourceProviderTraitsURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "traits") } + +func getResourceProviderAggregatesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID, "aggregates") +} From 20b5ab81576899b261dd79c3a0de5e086f67ca83 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Thu, 2 Apr 2026 15:16:25 +0200 Subject: [PATCH 2279/2296] Implement PUT operation on Placement resource provider aggregates Related: #526 API reference: https://docs.openstack.org/api-ref/placement/#update-resource-provider-aggregates Code reference: https://opendev.org/openstack/placement/src/branch/stable/2026.1/placement/handlers/aggregate.py#L102-L132 Signed-off-by: Dominik Danelski --- .../placement/v1/resourceproviders_test.go | 69 ++++++++ .../placement/v1/resourceproviders/doc.go | 17 ++ .../v1/resourceproviders/requests.go | 32 ++++ .../testing/fixtures_test.go | 150 +++++++++++++++++- .../testing/requests_test.go | 54 +++++++ .../placement/v1/resourceproviders/urls.go | 4 + 6 files changed, 324 insertions(+), 2 deletions(-) diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index 3514d7f2b3..1a519c9202 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -5,6 +5,7 @@ package v1 import ( "context" "net/http" + "slices" "testing" "github.com/gophercloud/gophercloud/v2" @@ -204,3 +205,71 @@ func TestResourceProviderAggregatesGetPreGenerationNegative(t *testing.T) { _, err = resourceproviders.GetAggregates(context.TODO(), client, "00000000-0000-0000-0000-000000000000").Extract() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } + +func TestResourceProviderAggregatesUpdateSuccess(t *testing.T) { + // resource_provider_generation is required in the PUT request body from microversion 1.19. + clients.SkipReleasesBelow(t, "stable/ocata") + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + client.Microversion = "1.19" + + before, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, before.ResourceProviderGeneration != nil) + + updateOpts := resourceproviders.UpdateAggregatesOpts{ + ResourceProviderGeneration: before.ResourceProviderGeneration, + Aggregates: []string{ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4", + }, + } + + _, err = resourceproviders.UpdateAggregates(context.TODO(), client, resourceProvider.UUID, updateOpts).Extract() + th.AssertNoErr(t, err) + + after, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, len(updateOpts.Aggregates), len(after.Aggregates)) + + for _, aggregate := range updateOpts.Aggregates { + th.AssertEquals(t, true, slices.Contains(after.Aggregates, aggregate)) + } +} + +func TestResourceProviderAggregatesUpdateNegative(t *testing.T) { + // resource_provider_generation is required in the PUT request body from microversion 1.19. + clients.SkipReleasesBelow(t, "stable/ocata") + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + client.Microversion = "1.19" + + current, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, current.ResourceProviderGeneration != nil) + wrongGeneration := *current.ResourceProviderGeneration + 100 + + updateOpts := resourceproviders.UpdateAggregatesOpts{ + ResourceProviderGeneration: &wrongGeneration, + Aggregates: []string{ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + }, + } + + _, err = resourceproviders.UpdateAggregates(context.TODO(), client, resourceProvider.UUID, updateOpts).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) +} diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index efd98b775d..7164e6cfb7 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -97,5 +97,22 @@ Example to get resource providers aggregates if err != nil { panic(err) } + +Example to update resource providers aggregates + + placementClient.Microversion = "1.1" + + updateOpts := resourceproviders.UpdateAggregatesOpts{ + ResourceProviderGeneration: rp.ResourceProviderGeneration, + Aggregates: []string{ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4", + }, + } + + rp, err = resourceproviders.UpdateAggregates(context.TODO(), placementClient, resourceProviderID, updateOpts).Extract() + if err != nil { + panic(err) + } */ package resourceproviders diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 5a8ece6876..fef0fe1a9b 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -182,6 +182,38 @@ func GetAggregates(ctx context.Context, client *gophercloud.ServiceClient, resou return } +// UpdateAggregatesOptsBuilder allows extensions to add additional parameters to the +// UpdateAggregates request. +type UpdateAggregatesOptsBuilder interface { + ToResourceProviderUpdateAggregatesMap() (map[string]any, error) +} + +// UpdateAggregatesOpts represents options used to update aggregates of a resource provider. +type UpdateAggregatesOpts struct { + // ResourceProviderGeneration is required from microversion 1.19 and later. + ResourceProviderGeneration *int `json:"resource_provider_generation,omitempty"` + Aggregates []string `json:"aggregates"` +} + +// ToResourceProviderUpdateAggregatesMap constructs a request body from UpdateAggregatesOpts. +func (opts UpdateAggregatesOpts) ToResourceProviderUpdateAggregatesMap() (map[string]any, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +func UpdateAggregates(ctx context.Context, client *gophercloud.ServiceClient, resourceProviderID string, opts UpdateAggregatesOptsBuilder) (r GetAggregatesResult) { + b, err := opts.ToResourceProviderUpdateAggregatesMap() + if err != nil { + r.Err = err + return + } + + resp, err := client.Put(ctx, updateResourceProviderAggregatesURL(client, resourceProviderID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + // UpdateTraitsOptsBuilder allows extensions to add additional parameters to the // UpdateTraits request. type UpdateTraitsOptsBuilder interface { diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index d3682a7177..78132e7048 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -174,7 +174,35 @@ const AggregatesBody = ` } ` -const AbsentResourceProviderID = "non-existent-rp" +const AggregatesBodyPreGeneration = ` +{ + "aggregates": [ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4" + ] +} +` + +const AggregatesUpdateBody = ` +{ + "resource_provider_generation": 1, + "aggregates": [ + "89f68995-4fd8-4f8b-a03e-7d5980762ff2", + "16d0e5f2-7f66-4f32-9040-b09de2f40afd" + ] +} +` + +const AggregatesUpdateBodyPreGeneration = ` +{ + "aggregates": [ + "89f68995-4fd8-4f8b-a03e-7d5980762ff2", + "16d0e5f2-7f66-4f32-9040-b09de2f40afd" + ] +} +` + +const AbsentResourceProviderID = "00000000-0000-0000-0000-000000000000" var ExpectedResourceProvider1 = resourceproviders.ResourceProvider{ Generation: 1, @@ -280,14 +308,41 @@ var ExpectedTraits = resourceproviders.ResourceProviderTraits{ }, } +var expectedAggregatesGeneration = 1 + var ExpectedAggregates = resourceproviders.ResourceProviderAggregates{ - ResourceProviderGeneration: 1, + ResourceProviderGeneration: &expectedAggregatesGeneration, + Aggregates: []string{ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4", + }, +} + +var ExpectedAggregatesPreGeneration = resourceproviders.ResourceProviderAggregates{ + ResourceProviderGeneration: nil, Aggregates: []string{ "6d84f6f6-7736-40ff-84d2-7db47f18ea25", "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4", }, } +var expectedUpdatedAggregatesGeneration = 1 + +var ExpectedUpdatedAggregates = resourceproviders.ResourceProviderAggregates{ + ResourceProviderGeneration: &expectedUpdatedAggregatesGeneration, + Aggregates: []string{ + "89f68995-4fd8-4f8b-a03e-7d5980762ff2", + "16d0e5f2-7f66-4f32-9040-b09de2f40afd", + }, +} + +var ExpectedUpdatedAggregatesPreGeneration = resourceproviders.ResourceProviderAggregates{ + Aggregates: []string{ + "89f68995-4fd8-4f8b-a03e-7d5980762ff2", + "16d0e5f2-7f66-4f32-9040-b09de2f40afd", + }, +} + func HandleResourceProviderList(t *testing.T, fakeServer th.FakeServer) { fakeServer.Mux.HandleFunc("/resource_providers", func(w http.ResponseWriter, r *http.Request) { @@ -449,6 +504,21 @@ func HandleResourceProviderGetAggregatesSuccess(t *testing.T, fakeServer th.Fake }) } +func HandleResourceProviderGetAggregatesPreGenerationSuccess(t *testing.T, fakeServer th.FakeServer) { + aggregatesTestURL := fmt.Sprintf("/resource_providers/%s/aggregates", ResourceProviderTestID) + + fakeServer.Mux.HandleFunc(aggregatesTestURL, + 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.Fprint(w, AggregatesBodyPreGeneration) + }) +} + func HandleResourceProviderGetAggregatesNotFound(t *testing.T, fakeServer th.FakeServer) { aggregatesTestURL := fmt.Sprintf("/resource_providers/%s/aggregates", AbsentResourceProviderID) @@ -460,3 +530,79 @@ func HandleResourceProviderGetAggregatesNotFound(t *testing.T, fakeServer th.Fak w.WriteHeader(http.StatusNotFound) }) } + +func HandleResourceProviderUpdateAndGetAggregatesSuccess(t *testing.T, fakeServer th.FakeServer) { + aggregatesTestURL := fmt.Sprintf("/resource_providers/%s/aggregates", ResourceProviderTestID) + + // This handler must cover PUT and GET because the test updates and then fetches on the same path. + body := AggregatesBody + + fakeServer.Mux.HandleFunc(aggregatesTestURL, + func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, body) + case http.MethodPut: + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, AggregatesUpdateBody) + + body = AggregatesUpdateBody + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, body) + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } + }) +} + +func HandleResourceProviderUpdateAndGetAggregatesPreGenerationSuccess(t *testing.T, fakeServer th.FakeServer) { + aggregatesTestURL := fmt.Sprintf("/resource_providers/%s/aggregates", ResourceProviderTestID) + + // Pre-generation variant of the same update-then-fetch flow on a shared endpoint. + body := AggregatesBodyPreGeneration + + fakeServer.Mux.HandleFunc(aggregatesTestURL, + func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, body) + case http.MethodPut: + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, AggregatesUpdateBodyPreGeneration) + + body = AggregatesUpdateBodyPreGeneration + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, body) + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } + }) +} + +func HandleResourceProviderUpdateAggregatesConflict(t *testing.T, fakeServer th.FakeServer) { + aggregatesTestURL := fmt.Sprintf("/resource_providers/%s/aggregates", ResourceProviderTestID) + + fakeServer.Mux.HandleFunc(aggregatesTestURL, + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "PUT") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusConflict) + }) +} diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index 4a97672c25..5cb6101ddb 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -181,6 +181,17 @@ func TestGetResourceProviderAggregatesSuccess(t *testing.T) { th.AssertDeepEquals(t, ExpectedAggregates, *actual) } +func TestGetResourceProviderAggregatesPreGenerationSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderGetAggregatesPreGenerationSuccess(t, fakeServer) + + actual, err := resourceproviders.GetAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAggregatesPreGeneration, *actual) +} + func TestGetResourceProviderAggregatesNotFound(t *testing.T) { fakeServer := th.SetupHTTP() defer fakeServer.Teardown() @@ -190,3 +201,46 @@ func TestGetResourceProviderAggregatesNotFound(t *testing.T) { _, err := resourceproviders.GetAggregates(context.TODO(), client.ServiceClient(fakeServer), AbsentResourceProviderID).Extract() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } + +func TestUpdateResourceProviderAggregatesSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderUpdateAndGetAggregatesSuccess(t, fakeServer) + + updateOpts := resourceproviders.UpdateAggregatesOpts(ExpectedUpdatedAggregates) + _, err := resourceproviders.UpdateAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, updateOpts).Extract() + th.AssertNoErr(t, err) + + actual, err := resourceproviders.GetAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedUpdatedAggregates, *actual) +} + +func TestUpdateResourceProviderAggregatesPreGenerationSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderUpdateAndGetAggregatesPreGenerationSuccess(t, fakeServer) + + updateOpts := resourceproviders.UpdateAggregatesOpts{ + Aggregates: ExpectedUpdatedAggregatesPreGeneration.Aggregates, + } + _, err := resourceproviders.UpdateAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, updateOpts).Extract() + th.AssertNoErr(t, err) + + actual, err := resourceproviders.GetAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedUpdatedAggregatesPreGeneration, *actual) +} + +func TestUpdateResourceProviderAggregatesConflict(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleResourceProviderUpdateAggregatesConflict(t, fakeServer) + + updateOpts := resourceproviders.UpdateAggregatesOpts(ExpectedUpdatedAggregates) + _, err := resourceproviders.UpdateAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, updateOpts).Extract() + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) +} diff --git a/openstack/placement/v1/resourceproviders/urls.go b/openstack/placement/v1/resourceproviders/urls.go index 821fdcf57a..f661b362cf 100644 --- a/openstack/placement/v1/resourceproviders/urls.go +++ b/openstack/placement/v1/resourceproviders/urls.go @@ -41,3 +41,7 @@ func getResourceProviderTraitsURL(client *gophercloud.ServiceClient, resourcePro func getResourceProviderAggregatesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { return client.ServiceURL(apiName, resourceProviderID, "aggregates") } + +func updateResourceProviderAggregatesURL(client *gophercloud.ServiceClient, resourceProviderID string) string { + return client.ServiceURL(apiName, resourceProviderID, "aggregates") +} From ef5741426842072b762dbd731371dd4e3ae89d32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:05:11 +0000 Subject: [PATCH 2280/2296] build(deps): bump github/codeql-action from 4.34.1 to 4.35.1 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.34.1 to 4.35.1. - [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/38697555549f1db7851b81482ff19f1fa5c4fedc...c10b8064de6f491fea524254123dbe5e09572f13) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.35.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 12d4008b74..3d11b8f50a 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -29,12 +29,12 @@ jobs: cache: true - name: Initialize CodeQL - uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4 + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4 + uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4 + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4 From 5894e298ab2419b24693b36140f3b315faf1d20e Mon Sep 17 00:00:00 2001 From: jlarriba Date: Mon, 23 Feb 2026 15:30:41 +0100 Subject: [PATCH 2281/2296] Add metric-storage (Aetos) v1 service support Aetos is a Keystone-authenticated reverse proxy for Prometheus that enforces multi-tenant RBAC on all Prometheus queries. This adds Go bindings for the Prometheus HTTP API exposed under /api/v1/ with OpenStack authentication. Supported operations: Query, Labels, LabelValues, Series, Targets, RuntimeInfo, CleanTombstones, DeleteSeries, Snapshot. --- .github/workflows/functional-metric.yaml | 105 ++++++ Makefile | 6 +- internal/acceptance/clients/clients.go | 21 ++ .../openstack/metric/v1/metrics_test.go | 80 +++++ .../acceptance/openstack/metric/v1/pkg.go | 4 + openstack/client.go | 8 + openstack/metric/v1/metrics/doc.go | 59 ++++ openstack/metric/v1/metrics/requests.go | 263 ++++++++++++++ openstack/metric/v1/metrics/results.go | 298 ++++++++++++++++ openstack/metric/v1/metrics/testing/doc.go | 2 + .../v1/metrics/testing/fixtures_test.go | 327 ++++++++++++++++++ .../v1/metrics/testing/requests_test.go | 109 ++++++ openstack/metric/v1/metrics/urls.go | 39 +++ 13 files changed, 1320 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/functional-metric.yaml create mode 100644 internal/acceptance/openstack/metric/v1/metrics_test.go create mode 100644 internal/acceptance/openstack/metric/v1/pkg.go create mode 100644 openstack/metric/v1/metrics/doc.go create mode 100644 openstack/metric/v1/metrics/requests.go create mode 100644 openstack/metric/v1/metrics/results.go create mode 100644 openstack/metric/v1/metrics/testing/doc.go create mode 100644 openstack/metric/v1/metrics/testing/fixtures_test.go create mode 100644 openstack/metric/v1/metrics/testing/requests_test.go create mode 100644 openstack/metric/v1/metrics/urls.go diff --git a/.github/workflows/functional-metric.yaml b/.github/workflows/functional-metric.yaml new file mode 100644 index 0000000000..8f60039792 --- /dev/null +++ b/.github/workflows/functional-metric.yaml @@ -0,0 +1,105 @@ +name: functional-metric +on: + merge_group: + pull_request: + schedule: + - cron: '0 0 */3 * *' +jobs: + functional-metric: + permissions: + contents: read + strategy: + fail-fast: false + matrix: + include: + - name: "master" + openstack_version: "master" + ubuntu_version: "24.04" + devstack_conf_overrides: | + # ensure we're using a working version of setuptools + if [ -n "\$TOP_DIR" ]; then + sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python + sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra + fi + runs-on: ubuntu-${{ matrix.ubuntu_version }} + name: Aetos on OpenStack ${{ matrix.name }} + steps: + - name: Checkout Gophercloud + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Check changed files + uses: ./.github/actions/file-filter + id: changed-files + with: + patterns: | + openstack/auth_env.go + openstack/client.go + openstack/endpoint.go + openstack/endpoint_location.go + openstack/config/provider_client.go + openstack/utils/choose_version.go + openstack/utils/discovery.go + **metric** + .github/workflows/functional-metric.yaml + + - name: Skip tests for unrelated changed-files + if: ${{ ! fromJSON(steps.changed-files.outputs.matches) }} + run: | + echo "No relevant files changed - skipping tests for ${{ matrix.name }}" + echo "TESTS_SKIPPED=true" >> $GITHUB_ENV + + - name: Deploy devstack + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} + uses: gophercloud/devstack-action@60ca1042045c0c9e3e001c64575d381654ffcba1 # v0.19 + with: + branch: ${{ matrix.openstack_version }} + conf_overrides: | + enable_plugin devstack-plugin-prometheus https://opendev.org/openstack/devstack-plugin-prometheus ${{ matrix.openstack_version }} + enable_plugin ceilometer https://opendev.org/openstack/ceilometer ${{ matrix.openstack_version }} + CEILOMETER_BACKEND=sg-core + PROMETHEUS_CUSTOM_SCRAPE_TARGETS="localhost:3000" + enable_plugin sg-core https://github.com/openstack-k8s-operators/sg-core main + enable_plugin aetos https://opendev.org/openstack/aetos ${{ matrix.openstack_version }} + + ${{ matrix.devstack_conf_overrides }} + enabled_services: "prometheus,node_exporter,openstack_exporter,ceilometer-acompute,ceilometer-acentral,ceilometer-anotification,sg-core,aetos,openstack-cli-server" + + - name: Checkout go + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 + with: + go-version-file: 'go.mod' + cache: true + + - name: Run Gophercloud acceptance tests + if: ${{ fromJSON(steps.changed-files.outputs.matches) }} + run: | + source ${{ github.workspace }}/script/stackenv + make acceptance-metric + echo "TESTS_RUN=true" >> $GITHUB_ENV + env: + DEVSTACK_PATH: ${{ github.workspace }}/devstack + OS_BRANCH: ${{ matrix.openstack_version }} + + - name: Generate logs on failure + run: ./script/collectlogs + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + + - name: Upload logs artifacts on failure + if: ${{ failure() && fromJSON(steps.changed-files.outputs.matches) }} + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: functional-metric-${{ matrix.name }}-${{ github.run_id }} + path: /tmp/devstack-logs/* + + - name: Set job status + run: | + if [[ "$TESTS_SKIPPED" == "true" || "$TESTS_RUN" == "true" ]]; then + echo "Job completed successfully (either ran tests or skipped appropriately)" + exit 0 + else + echo "Job failed - neither tests ran nor were properly skipped" + exit 1 + fi diff --git a/Makefile b/Makefile index 79411c0026..6b9d043cf2 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ coverage: $(GO_TEST) -shuffle on -covermode count -coverprofile cover.out -coverpkg=./... ./... .PHONY: coverage -acceptance: acceptance-basic acceptance-baremetal acceptance-blockstorage acceptance-compute acceptance-container acceptance-containerinfra acceptance-db acceptance-dns acceptance-identity acceptance-image acceptance-keymanager acceptance-loadbalancer acceptance-messaging acceptance-networking acceptance-objectstorage acceptance-orchestration acceptance-placement acceptance-sharedfilesystems acceptance-workflow +acceptance: acceptance-basic acceptance-baremetal acceptance-blockstorage acceptance-compute acceptance-container acceptance-containerinfra acceptance-db acceptance-dns acceptance-identity acceptance-image acceptance-keymanager acceptance-loadbalancer acceptance-messaging acceptance-metric acceptance-networking acceptance-objectstorage acceptance-orchestration acceptance-placement acceptance-sharedfilesystems acceptance-workflow .PHONY: acceptance acceptance-basic: @@ -102,6 +102,10 @@ acceptance-messaging: $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/messaging/... .PHONY: acceptance-messaging +acceptance-metric: + $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/metric/... +.PHONY: acceptance-metric + acceptance-networking: $(GO_TEST) -timeout $(TIMEOUT) -tags "fixtures acceptance" ./internal/acceptance/openstack/networking/... .PHONY: acceptance-networking diff --git a/internal/acceptance/clients/clients.go b/internal/acceptance/clients/clients.go index 8f9b8de35f..a2d0b0a69b 100644 --- a/internal/acceptance/clients/clients.go +++ b/internal/acceptance/clients/clients.go @@ -585,6 +585,27 @@ func NewMessagingV2Client(clientID string) (*gophercloud.ServiceClient, error) { }) } +// NewMetricV1Client returns a *ServiceClient for making calls +// to the OpenStack Metric v1 API. An error will be returned +// if authentication or client creation was not possible. +func NewMetricV1Client() (*gophercloud.ServiceClient, error) { + ao, err := openstack.AuthOptionsFromEnv() + if err != nil { + return nil, err + } + + client, err := openstack.AuthenticatedClient(context.TODO(), ao) + if err != nil { + return nil, err + } + + client = configureDebug(client) + + return openstack.NewMetricV1(context.TODO(), client, gophercloud.EndpointOpts{ + Region: os.Getenv("OS_REGION_NAME"), + }) +} + // NewContainerV1Client returns a *ServiceClient for making calls // to the OpenStack Container V1 API. An error will be returned // if authentication or client creation was not possible. diff --git a/internal/acceptance/openstack/metric/v1/metrics_test.go b/internal/acceptance/openstack/metric/v1/metrics_test.go new file mode 100644 index 0000000000..6fee465d62 --- /dev/null +++ b/internal/acceptance/openstack/metric/v1/metrics_test.go @@ -0,0 +1,80 @@ +//go:build acceptance || metric || metrics + +package v1 + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/metric/v1/metrics" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestMetricQuery(t *testing.T) { + client, err := clients.NewMetricV1Client() + th.AssertNoErr(t, err) + + result, err := metrics.Query(context.TODO(), client, metrics.QueryOpts{ + Query: "up", + }).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, result) + th.AssertEquals(t, result.ResultType, "vector") +} + +func TestMetricLabels(t *testing.T) { + client, err := clients.NewMetricV1Client() + th.AssertNoErr(t, err) + + result, err := metrics.Labels(context.TODO(), client, nil).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, result) + if len(result) == 0 { + t.Fatal("expected at least one label") + } +} + +func TestMetricLabelValues(t *testing.T) { + client, err := clients.NewMetricV1Client() + th.AssertNoErr(t, err) + + result, err := metrics.LabelValues(context.TODO(), client, "__name__", nil).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, result) + if len(result) == 0 { + t.Fatal("expected at least one label value") + } +} + +func TestMetricSeries(t *testing.T) { + client, err := clients.NewMetricV1Client() + th.AssertNoErr(t, err) + + result, err := metrics.Series(context.TODO(), client, metrics.SeriesOpts{ + Match: []string{`{__name__="up"}`}, + }).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, result) + if len(result) == 0 { + t.Fatal("expected at least one series") + } +} + +func TestMetricRuntimeInfo(t *testing.T) { + client, err := clients.NewMetricV1Client() + th.AssertNoErr(t, err) + + result, err := metrics.RuntimeInfo(context.TODO(), client).Extract() + th.AssertNoErr(t, err) + + tools.PrintResource(t, result) + if result.StorageRetention == "" { + t.Fatal("expected non-empty StorageRetention") + } +} diff --git a/internal/acceptance/openstack/metric/v1/pkg.go b/internal/acceptance/openstack/metric/v1/pkg.go new file mode 100644 index 0000000000..05e9b4a1ab --- /dev/null +++ b/internal/acceptance/openstack/metric/v1/pkg.go @@ -0,0 +1,4 @@ +//go:build acceptance || metric + +// Package v1 contains acceptance tests for the OpenStack metric-storage v1 service. +package v1 diff --git a/openstack/client.go b/openstack/client.go index 234f23eba2..4de89ab4ea 100644 --- a/openstack/client.go +++ b/openstack/client.go @@ -464,6 +464,14 @@ func NewLoadBalancerV2(ctx context.Context, client *gophercloud.ProviderClient, return sc, err } +// NewMetricV1 creates a ServiceClient that may be used with the v1 metric-storage +// service (Aetos). +func NewMetricV1(ctx context.Context, client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + sc, err := initClientOpts(ctx, client, eo, "metric-storage", 1) + sc.ResourceBase = sc.Endpoint + "api/v1/" + return sc, err +} + // NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging // service. func NewMessagingV2(ctx context.Context, client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { diff --git a/openstack/metric/v1/metrics/doc.go b/openstack/metric/v1/metrics/doc.go new file mode 100644 index 0000000000..410dc07735 --- /dev/null +++ b/openstack/metric/v1/metrics/doc.go @@ -0,0 +1,59 @@ +/* +Package metrics provides interaction with the Prometheus HTTP API through the +OpenStack Aetos service (metric-storage). + +Aetos acts as a Keystone-authenticated reverse proxy for Prometheus, enforcing +multi-tenant RBAC on all Prometheus queries. It exposes the Prometheus HTTP API +under /api/v1/ with OpenStack authentication. + +Example to Create a Metric Service Client + + metricClient, err := openstack.NewMetricV1(ctx, providerClient, gophercloud.EndpointOpts{ + Region: "RegionOne", + }) + if err != nil { + panic(err) + } + +Example to Query Metrics + + opts := metrics.QueryOpts{ + Query: "up", + } + + result, err := metrics.Query(ctx, metricClient, opts).Extract() + if err != nil { + panic(err) + } + + for _, v := range result.Result { + fmt.Printf("%s: %v\n", v.Metric["__name__"], v.Value) + } + +Example to List Label Names + + labels, err := metrics.Labels(ctx, metricClient, nil).Extract() + if err != nil { + panic(err) + } + + for _, label := range labels { + fmt.Println(label) + } + +Example to Find Series by Label Matchers + + opts := metrics.SeriesOpts{ + Match: []string{"up", `process_start_time_seconds{job="prometheus"}`}, + } + + series, err := metrics.Series(ctx, metricClient, opts).Extract() + if err != nil { + panic(err) + } + + for _, s := range series { + fmt.Printf("%v\n", s) + } +*/ +package metrics diff --git a/openstack/metric/v1/metrics/requests.go b/openstack/metric/v1/metrics/requests.go new file mode 100644 index 0000000000..0fdc9624fb --- /dev/null +++ b/openstack/metric/v1/metrics/requests.go @@ -0,0 +1,263 @@ +package metrics + +import ( + "context" + + "github.com/gophercloud/gophercloud/v2" +) + +// QueryOptsBuilder allows extensions to add parameters to the Query request. +type QueryOptsBuilder interface { + ToMetricQueryQuery() (string, error) +} + +// QueryOpts contains the options for a Prometheus instant query. +type QueryOpts struct { + // Query is the PromQL query string. + Query string `q:"query" required:"true"` + + // Time is the evaluation timestamp (RFC3339 or Unix timestamp). + Time string `q:"time"` + + // Timeout is the evaluation timeout. + Timeout string `q:"timeout"` +} + +// ToMetricQueryQuery formats QueryOpts into a query string. +func (opts QueryOpts) ToMetricQueryQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Query performs a Prometheus instant query. +func Query(ctx context.Context, client *gophercloud.ServiceClient, opts QueryOptsBuilder) (r QueryResult) { + url := queryURL(client) + if opts != nil { + query, err := opts.ToMetricQueryQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Get(ctx, url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// LabelsOptsBuilder allows extensions to add parameters to the Labels request. +type LabelsOptsBuilder interface { + ToMetricLabelsQuery() (string, error) +} + +// LabelsOpts contains the options for listing label names. +type LabelsOpts struct { + // Match is a list of series selectors to filter labels by. + Match []string `q:"match[]"` + + // Start is the start timestamp (RFC3339 or Unix timestamp). + Start string `q:"start"` + + // End is the end timestamp (RFC3339 or Unix timestamp). + End string `q:"end"` +} + +// ToMetricLabelsQuery formats LabelsOpts into a query string. +func (opts LabelsOpts) ToMetricLabelsQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Labels returns a list of label names. +func Labels(ctx context.Context, client *gophercloud.ServiceClient, opts LabelsOptsBuilder) (r LabelsResult) { + url := labelsURL(client) + if opts != nil { + query, err := opts.ToMetricLabelsQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Get(ctx, url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// LabelValuesOptsBuilder allows extensions to add parameters to the LabelValues request. +type LabelValuesOptsBuilder interface { + ToMetricLabelValuesQuery() (string, error) +} + +// LabelValuesOpts contains the options for listing label values. +type LabelValuesOpts struct { + // Match is a list of series selectors to filter label values by. + Match []string `q:"match[]"` + + // Start is the start timestamp (RFC3339 or Unix timestamp). + Start string `q:"start"` + + // End is the end timestamp (RFC3339 or Unix timestamp). + End string `q:"end"` +} + +// ToMetricLabelValuesQuery formats LabelValuesOpts into a query string. +func (opts LabelValuesOpts) ToMetricLabelValuesQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// LabelValues returns a list of label values for a given label name. +func LabelValues(ctx context.Context, client *gophercloud.ServiceClient, name string, opts LabelValuesOptsBuilder) (r LabelValuesResult) { + url := labelValuesURL(client, name) + if opts != nil { + query, err := opts.ToMetricLabelValuesQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Get(ctx, url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// SeriesOptsBuilder allows extensions to add parameters to the Series request. +type SeriesOptsBuilder interface { + ToMetricSeriesQuery() (string, error) +} + +// SeriesOpts contains the options for finding series by label matchers. +type SeriesOpts struct { + // Match is a list of series selectors. At least one must be provided. + Match []string `q:"match[]" required:"true"` + + // Start is the start timestamp (RFC3339 or Unix timestamp). + Start string `q:"start"` + + // End is the end timestamp (RFC3339 or Unix timestamp). + End string `q:"end"` +} + +// ToMetricSeriesQuery formats SeriesOpts into a query string. +func (opts SeriesOpts) ToMetricSeriesQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Series returns the list of time series that match certain label sets. +func Series(ctx context.Context, client *gophercloud.ServiceClient, opts SeriesOptsBuilder) (r SeriesResult) { + url := seriesURL(client) + if opts != nil { + query, err := opts.ToMetricSeriesQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Get(ctx, url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// TargetsOptsBuilder allows extensions to add parameters to the Targets request. +type TargetsOptsBuilder interface { + ToMetricTargetsQuery() (string, error) +} + +// TargetsOpts contains the options for listing targets. +type TargetsOpts struct { + // State filters targets by state ("active", "dropped", "any"). + State string `q:"state"` +} + +// ToMetricTargetsQuery formats TargetsOpts into a query string. +func (opts TargetsOpts) ToMetricTargetsQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// Targets returns an overview of the current state of Prometheus target discovery. +func Targets(ctx context.Context, client *gophercloud.ServiceClient, opts TargetsOptsBuilder) (r TargetsResult) { + url := targetsURL(client) + if opts != nil { + query, err := opts.ToMetricTargetsQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Get(ctx, url, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RuntimeInfo returns runtime information about the Prometheus server. +func RuntimeInfo(ctx context.Context, client *gophercloud.ServiceClient) (r RuntimeInfoResult) { + resp, err := client.Get(ctx, runtimeInfoURL(client), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CleanTombstones removes deleted data from disk and cleans up the existing tombstones. +func CleanTombstones(ctx context.Context, client *gophercloud.ServiceClient) (r CleanTombstonesResult) { + resp, err := client.Post(ctx, cleanTombstonesURL(client), nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// DeleteSeriesOptsBuilder allows extensions to add parameters to the DeleteSeries request. +type DeleteSeriesOptsBuilder interface { + ToMetricDeleteSeriesQuery() (string, error) +} + +// DeleteSeriesOpts contains the options for deleting time series. +type DeleteSeriesOpts struct { + // Match is a list of series selectors. At least one must be provided. + Match []string `q:"match[]" required:"true"` + + // Start is the start timestamp (RFC3339 or Unix timestamp). + Start string `q:"start"` + + // End is the end timestamp (RFC3339 or Unix timestamp). + End string `q:"end"` +} + +// ToMetricDeleteSeriesQuery formats DeleteSeriesOpts into a query string. +func (opts DeleteSeriesOpts) ToMetricDeleteSeriesQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// DeleteSeries deletes data for a selection of series in a time range. +func DeleteSeries(ctx context.Context, client *gophercloud.ServiceClient, opts DeleteSeriesOptsBuilder) (r DeleteSeriesResult) { + url := deleteSeriesURL(client) + if opts != nil { + query, err := opts.ToMetricDeleteSeriesQuery() + if err != nil { + r.Err = err + return + } + url += query + } + resp, err := client.Post(ctx, url, nil, nil, &gophercloud.RequestOpts{ + OkCodes: []int{204}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Snapshot creates a snapshot of all current data into snapshots/- +// under the TSDB's data directory and returns the directory as response. +func Snapshot(ctx context.Context, client *gophercloud.ServiceClient) (r SnapshotResult) { + resp, err := client.Post(ctx, snapshotURL(client), nil, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/openstack/metric/v1/metrics/results.go b/openstack/metric/v1/metrics/results.go new file mode 100644 index 0000000000..c4c6463ecf --- /dev/null +++ b/openstack/metric/v1/metrics/results.go @@ -0,0 +1,298 @@ +package metrics + +import ( + "encoding/json" + "fmt" + + "github.com/gophercloud/gophercloud/v2" +) + +// prometheusResponse is the common envelope for all Prometheus HTTP API responses. +type prometheusResponse struct { + Status string `json:"status"` + Data json.RawMessage `json:"data"` + ErrorType string `json:"errorType"` + Error string `json:"error"` +} + +// checkResponse unmarshals the Prometheus envelope and returns an error if +// the response status is "error". +func checkResponse(body any) (*prometheusResponse, error) { + b, err := json.Marshal(body) + if err != nil { + return nil, err + } + var resp prometheusResponse + if err := json.Unmarshal(b, &resp); err != nil { + return nil, err + } + if resp.Status == "error" { + return &resp, fmt.Errorf("prometheus %s: %s", resp.ErrorType, resp.Error) + } + return &resp, nil +} + +// QueryResult is the result of a Query request. +type QueryResult struct { + gophercloud.Result +} + +// Extract interprets a QueryResult as QueryData. +func (r QueryResult) Extract() (*QueryData, error) { + if r.Err != nil { + return nil, r.Err + } + resp, err := checkResponse(r.Body) + if err != nil { + return nil, err + } + var data QueryData + if err := json.Unmarshal(resp.Data, &data); err != nil { + return nil, err + } + return &data, nil +} + +// LabelsResult is the result of a Labels request. +type LabelsResult struct { + gophercloud.Result +} + +// Extract interprets a LabelsResult as a slice of label name strings. +func (r LabelsResult) Extract() ([]string, error) { + if r.Err != nil { + return nil, r.Err + } + resp, err := checkResponse(r.Body) + if err != nil { + return nil, err + } + var data []string + if err := json.Unmarshal(resp.Data, &data); err != nil { + return nil, err + } + return data, nil +} + +// LabelValuesResult is the result of a LabelValues request. +type LabelValuesResult struct { + gophercloud.Result +} + +// Extract interprets a LabelValuesResult as a slice of label value strings. +func (r LabelValuesResult) Extract() ([]string, error) { + if r.Err != nil { + return nil, r.Err + } + resp, err := checkResponse(r.Body) + if err != nil { + return nil, err + } + var data []string + if err := json.Unmarshal(resp.Data, &data); err != nil { + return nil, err + } + return data, nil +} + +// SeriesResult is the result of a Series request. +type SeriesResult struct { + gophercloud.Result +} + +// Extract interprets a SeriesResult as a slice of label-set maps. +func (r SeriesResult) Extract() ([]map[string]string, error) { + if r.Err != nil { + return nil, r.Err + } + resp, err := checkResponse(r.Body) + if err != nil { + return nil, err + } + var data []map[string]string + if err := json.Unmarshal(resp.Data, &data); err != nil { + return nil, err + } + return data, nil +} + +// TargetsResult is the result of a Targets request. +type TargetsResult struct { + gophercloud.Result +} + +// Extract interprets a TargetsResult as TargetsData. +func (r TargetsResult) Extract() (*TargetsData, error) { + if r.Err != nil { + return nil, r.Err + } + resp, err := checkResponse(r.Body) + if err != nil { + return nil, err + } + var data TargetsData + if err := json.Unmarshal(resp.Data, &data); err != nil { + return nil, err + } + return &data, nil +} + +// RuntimeInfoResult is the result of a RuntimeInfo request. +type RuntimeInfoResult struct { + gophercloud.Result +} + +// Extract interprets a RuntimeInfoResult as RuntimeInfoData. +func (r RuntimeInfoResult) Extract() (*RuntimeInfoData, error) { + if r.Err != nil { + return nil, r.Err + } + resp, err := checkResponse(r.Body) + if err != nil { + return nil, err + } + var data RuntimeInfoData + if err := json.Unmarshal(resp.Data, &data); err != nil { + return nil, err + } + return &data, nil +} + +// CleanTombstonesResult is the result of a CleanTombstones request. +type CleanTombstonesResult struct { + gophercloud.ErrResult +} + +// DeleteSeriesResult is the result of a DeleteSeries request. +type DeleteSeriesResult struct { + gophercloud.ErrResult +} + +// SnapshotResult is the result of a Snapshot request. +type SnapshotResult struct { + gophercloud.Result +} + +// Extract interprets a SnapshotResult as SnapshotData. +func (r SnapshotResult) Extract() (*SnapshotData, error) { + if r.Err != nil { + return nil, r.Err + } + resp, err := checkResponse(r.Body) + if err != nil { + return nil, err + } + var data SnapshotData + if err := json.Unmarshal(resp.Data, &data); err != nil { + return nil, err + } + return &data, nil +} + +// QueryData represents the data field of a Prometheus instant query response. +type QueryData struct { + // ResultType is the type of result (e.g., "vector", "matrix", "scalar", "string"). + ResultType string `json:"resultType"` + + // Result is the list of metric values returned by the query. + Result []MetricValue `json:"result"` +} + +// MetricValue represents a single metric result with its labels and value. +type MetricValue struct { + // Metric contains the label set identifying this metric. + Metric map[string]string `json:"metric"` + + // Value contains a single sample value as [timestamp, "string_value"]. + Value []any `json:"value"` +} + +// TargetsData represents the data field of a Prometheus targets response. +type TargetsData struct { + // ActiveTargets is the list of currently active scrape targets. + ActiveTargets []ActiveTarget `json:"activeTargets"` + + // DroppedTargets is the list of targets that were dropped during relabeling. + DroppedTargets []DroppedTarget `json:"droppedTargets"` +} + +// ActiveTarget represents an active Prometheus scrape target. +type ActiveTarget struct { + // DiscoveredLabels are the unmodified labels from service discovery. + DiscoveredLabels map[string]string `json:"discoveredLabels"` + + // Labels are the labels after relabeling. + Labels map[string]string `json:"labels"` + + // ScrapePool is the name of the scrape configuration. + ScrapePool string `json:"scrapePool"` + + // ScrapeURL is the URL being scraped. + ScrapeURL string `json:"scrapeUrl"` + + // GlobalURL is the URL as available from other Prometheus instances. + GlobalURL string `json:"globalUrl"` + + // LastError is the last error encountered during scraping. + LastError string `json:"lastError"` + + // LastScrape is the time of the last scrape. + LastScrape string `json:"lastScrape"` + + // LastScrapeDuration is the duration of the last scrape in seconds. + LastScrapeDuration float64 `json:"lastScrapeDuration"` + + // Health is the health status of the target ("up", "down", "unknown"). + Health string `json:"health"` + + // ScrapeInterval is the configured scrape interval. + ScrapeInterval string `json:"scrapeInterval"` + + // ScrapeTimeout is the configured scrape timeout. + ScrapeTimeout string `json:"scrapeTimeout"` +} + +// DroppedTarget represents a target that was dropped during relabeling. +type DroppedTarget struct { + // DiscoveredLabels are the unmodified labels from service discovery. + DiscoveredLabels map[string]string `json:"discoveredLabels"` +} + +// RuntimeInfoData represents the data field of a Prometheus runtime info response. +type RuntimeInfoData struct { + // StartTime is when the Prometheus server started. + StartTime string `json:"startTime"` + + // CWD is the current working directory of the Prometheus server. + CWD string `json:"CWD"` + + // ReloadConfigSuccess indicates if the last config reload was successful. + ReloadConfigSuccess bool `json:"reloadConfigSuccess"` + + // LastConfigTime is the time of the last config reload. + LastConfigTime string `json:"lastConfigTime"` + + // CorruptionCount is the number of WAL corruptions encountered. + CorruptionCount int `json:"corruptionCount"` + + // GoroutineCount is the number of goroutines. + GoroutineCount int `json:"goroutineCount"` + + // GOMAXPROCS is the configured GOMAXPROCS value. + GOMAXPROCS int `json:"GOMAXPROCS"` + + // GOGC is the configured GOGC value. + GOGC string `json:"GOGC"` + + // GODEBUG is the configured GODEBUG value. + GODEBUG string `json:"GODEBUG"` + + // StorageRetention is the configured storage retention. + StorageRetention string `json:"storageRetention"` +} + +// SnapshotData represents the data field of a Prometheus snapshot response. +type SnapshotData struct { + // Name is the filename of the snapshot created. + Name string `json:"name"` +} diff --git a/openstack/metric/v1/metrics/testing/doc.go b/openstack/metric/v1/metrics/testing/doc.go new file mode 100644 index 0000000000..c1ca16c942 --- /dev/null +++ b/openstack/metric/v1/metrics/testing/doc.go @@ -0,0 +1,2 @@ +// metrics unit tests +package testing diff --git a/openstack/metric/v1/metrics/testing/fixtures_test.go b/openstack/metric/v1/metrics/testing/fixtures_test.go new file mode 100644 index 0000000000..7da259fc61 --- /dev/null +++ b/openstack/metric/v1/metrics/testing/fixtures_test.go @@ -0,0 +1,327 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/metric/v1/metrics" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +// serviceClient returns a ServiceClient with ResourceBase set to include api/v1/. +func serviceClient(fakeServer th.FakeServer) *gophercloud.ServiceClient { + sc := client.ServiceClient(fakeServer) + sc.ResourceBase = sc.Endpoint + "api/v1/" + return sc +} + +// QueryOutput is a sample response to a Query call. +const QueryOutput = ` +{ + "status": "success", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "__name__": "up", + "instance": "localhost:9090", + "job": "prometheus" + }, + "value": [1435781451.781, "1"] + } + ] + } +} +` + +// ExpectedQueryData is the expected result from QueryOutput. +var ExpectedQueryData = &metrics.QueryData{ + ResultType: "vector", + Result: []metrics.MetricValue{ + { + Metric: map[string]string{ + "__name__": "up", + "instance": "localhost:9090", + "job": "prometheus", + }, + Value: []any{1435781451.781, "1"}, + }, + }, +} + +// LabelsOutput is a sample response to a Labels call. +const LabelsOutput = ` +{ + "status": "success", + "data": ["__name__", "instance", "job"] +} +` + +// ExpectedLabels is the expected result from LabelsOutput. +var ExpectedLabels = []string{"__name__", "instance", "job"} + +// LabelValuesOutput is a sample response to a LabelValues call. +const LabelValuesOutput = ` +{ + "status": "success", + "data": ["node", "prometheus"] +} +` + +// ExpectedLabelValues is the expected result from LabelValuesOutput. +var ExpectedLabelValues = []string{"node", "prometheus"} + +// SeriesOutput is a sample response to a Series call. +const SeriesOutput = ` +{ + "status": "success", + "data": [ + { + "__name__": "up", + "instance": "localhost:9090", + "job": "prometheus" + }, + { + "__name__": "up", + "instance": "localhost:9100", + "job": "node" + } + ] +} +` + +// ExpectedSeries is the expected result from SeriesOutput. +var ExpectedSeries = []map[string]string{ + { + "__name__": "up", + "instance": "localhost:9090", + "job": "prometheus", + }, + { + "__name__": "up", + "instance": "localhost:9100", + "job": "node", + }, +} + +// TargetsOutput is a sample response to a Targets call. +const TargetsOutput = ` +{ + "status": "success", + "data": { + "activeTargets": [ + { + "discoveredLabels": { + "__address__": "localhost:9090", + "__scheme__": "http", + "job": "prometheus" + }, + "labels": { + "instance": "localhost:9090", + "job": "prometheus" + }, + "scrapePool": "prometheus", + "scrapeUrl": "http://localhost:9090/metrics", + "globalUrl": "http://localhost:9090/metrics", + "lastError": "", + "lastScrape": "2025-08-20T10:30:00.000Z", + "lastScrapeDuration": 0.003145, + "health": "up", + "scrapeInterval": "15s", + "scrapeTimeout": "10s" + } + ], + "droppedTargets": [ + { + "discoveredLabels": { + "__address__": "localhost:9091", + "job": "dropped" + } + } + ] + } +} +` + +// ExpectedTargetsData is the expected result from TargetsOutput. +var ExpectedTargetsData = &metrics.TargetsData{ + ActiveTargets: []metrics.ActiveTarget{ + { + DiscoveredLabels: map[string]string{ + "__address__": "localhost:9090", + "__scheme__": "http", + "job": "prometheus", + }, + Labels: map[string]string{ + "instance": "localhost:9090", + "job": "prometheus", + }, + ScrapePool: "prometheus", + ScrapeURL: "http://localhost:9090/metrics", + GlobalURL: "http://localhost:9090/metrics", + LastError: "", + LastScrape: "2025-08-20T10:30:00.000Z", + LastScrapeDuration: 0.003145, + Health: "up", + ScrapeInterval: "15s", + ScrapeTimeout: "10s", + }, + }, + DroppedTargets: []metrics.DroppedTarget{ + { + DiscoveredLabels: map[string]string{ + "__address__": "localhost:9091", + "job": "dropped", + }, + }, + }, +} + +// RuntimeInfoOutput is a sample response to a RuntimeInfo call. +const RuntimeInfoOutput = ` +{ + "status": "success", + "data": { + "startTime": "2025-08-20T10:00:00.000Z", + "CWD": "/prometheus", + "reloadConfigSuccess": true, + "lastConfigTime": "2025-08-20T10:00:00.000Z", + "corruptionCount": 0, + "goroutineCount": 42, + "GOMAXPROCS": 4, + "GOGC": "", + "GODEBUG": "", + "storageRetention": "15d" + } +} +` + +// ExpectedRuntimeInfoData is the expected result from RuntimeInfoOutput. +var ExpectedRuntimeInfoData = &metrics.RuntimeInfoData{ + StartTime: "2025-08-20T10:00:00.000Z", + CWD: "/prometheus", + ReloadConfigSuccess: true, + LastConfigTime: "2025-08-20T10:00:00.000Z", + CorruptionCount: 0, + GoroutineCount: 42, + GOMAXPROCS: 4, + GOGC: "", + GODEBUG: "", + StorageRetention: "15d", +} + +// SnapshotOutput is a sample response to a Snapshot call. +const SnapshotOutput = ` +{ + "status": "success", + "data": { + "name": "20250820T100000Z-abcdef1234567890" + } +} +` + +// ExpectedSnapshotData is the expected result from SnapshotOutput. +var ExpectedSnapshotData = &metrics.SnapshotData{ + Name: "20250820T100000Z-abcdef1234567890", +} + +// HandleQuerySuccessfully configures the test server to respond to a Query request. +func HandleQuerySuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/query", 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.Fprint(w, QueryOutput) + }) +} + +// HandleLabelsSuccessfully configures the test server to respond to a Labels request. +func HandleLabelsSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/labels", 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.Fprint(w, LabelsOutput) + }) +} + +// HandleLabelValuesSuccessfully configures the test server to respond to a LabelValues request. +func HandleLabelValuesSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/label/job/values", 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.Fprint(w, LabelValuesOutput) + }) +} + +// HandleSeriesSuccessfully configures the test server to respond to a Series request. +func HandleSeriesSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/series", 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.Fprint(w, SeriesOutput) + }) +} + +// HandleTargetsSuccessfully configures the test server to respond to a Targets request. +func HandleTargetsSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/targets", 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.Fprint(w, TargetsOutput) + }) +} + +// HandleRuntimeInfoSuccessfully configures the test server to respond to a RuntimeInfo request. +func HandleRuntimeInfoSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/status/runtimeinfo", 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.Fprint(w, RuntimeInfoOutput) + }) +} + +// HandleCleanTombstonesSuccessfully configures the test server to respond to a CleanTombstones request. +func HandleCleanTombstonesSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/admin/tsdb/clean_tombstones", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleDeleteSeriesSuccessfully configures the test server to respond to a DeleteSeries request. +func HandleDeleteSeriesSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/admin/tsdb/delete_series", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.WriteHeader(http.StatusNoContent) + }) +} + +// HandleSnapshotSuccessfully configures the test server to respond to a Snapshot request. +func HandleSnapshotSuccessfully(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/api/v1/admin/tsdb/snapshot", 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") + fmt.Fprint(w, SnapshotOutput) + }) +} diff --git a/openstack/metric/v1/metrics/testing/requests_test.go b/openstack/metric/v1/metrics/testing/requests_test.go new file mode 100644 index 0000000000..3311250643 --- /dev/null +++ b/openstack/metric/v1/metrics/testing/requests_test.go @@ -0,0 +1,109 @@ +package testing + +import ( + "context" + "testing" + + "github.com/gophercloud/gophercloud/v2/openstack/metric/v1/metrics" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +func TestQuery(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleQuerySuccessfully(t, fakeServer) + + opts := metrics.QueryOpts{ + Query: "up", + } + + actual, err := metrics.Query(context.TODO(), serviceClient(fakeServer), opts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedQueryData, actual) +} + +func TestLabels(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleLabelsSuccessfully(t, fakeServer) + + actual, err := metrics.Labels(context.TODO(), serviceClient(fakeServer), nil).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedLabels, actual) +} + +func TestLabelValues(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleLabelValuesSuccessfully(t, fakeServer) + + actual, err := metrics.LabelValues(context.TODO(), serviceClient(fakeServer), "job", nil).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedLabelValues, actual) +} + +func TestSeries(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleSeriesSuccessfully(t, fakeServer) + + opts := metrics.SeriesOpts{ + Match: []string{"up"}, + } + + actual, err := metrics.Series(context.TODO(), serviceClient(fakeServer), opts).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedSeries, actual) +} + +func TestTargets(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleTargetsSuccessfully(t, fakeServer) + + actual, err := metrics.Targets(context.TODO(), serviceClient(fakeServer), nil).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedTargetsData, actual) +} + +func TestRuntimeInfo(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleRuntimeInfoSuccessfully(t, fakeServer) + + actual, err := metrics.RuntimeInfo(context.TODO(), serviceClient(fakeServer)).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedRuntimeInfoData, actual) +} + +func TestCleanTombstones(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleCleanTombstonesSuccessfully(t, fakeServer) + + err := metrics.CleanTombstones(context.TODO(), serviceClient(fakeServer)).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestDeleteSeries(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleDeleteSeriesSuccessfully(t, fakeServer) + + opts := metrics.DeleteSeriesOpts{ + Match: []string{"up"}, + } + + err := metrics.DeleteSeries(context.TODO(), serviceClient(fakeServer), opts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestSnapshot(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + HandleSnapshotSuccessfully(t, fakeServer) + + actual, err := metrics.Snapshot(context.TODO(), serviceClient(fakeServer)).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedSnapshotData, actual) +} diff --git a/openstack/metric/v1/metrics/urls.go b/openstack/metric/v1/metrics/urls.go new file mode 100644 index 0000000000..856f84da5b --- /dev/null +++ b/openstack/metric/v1/metrics/urls.go @@ -0,0 +1,39 @@ +package metrics + +import "github.com/gophercloud/gophercloud/v2" + +func queryURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("query") +} + +func labelsURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("labels") +} + +func labelValuesURL(c *gophercloud.ServiceClient, name string) string { + return c.ServiceURL("label", name, "values") +} + +func seriesURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("series") +} + +func targetsURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("targets") +} + +func runtimeInfoURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("status", "runtimeinfo") +} + +func cleanTombstonesURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("admin", "tsdb", "clean_tombstones") +} + +func deleteSeriesURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("admin", "tsdb", "delete_series") +} + +func snapshotURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("admin", "tsdb", "snapshot") +} From 754152282466de0efdfc7b2a7b7176d5b30e064c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 8 Apr 2026 11:14:20 +0200 Subject: [PATCH 2282/2296] ci: configure dependabot to target both main and v2 branches --- .github/dependabot.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index b061387fea..83e79253ce 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -2,6 +2,7 @@ version: 2 updates: - package-ecosystem: gomod directory: "/" + target-branch: "main" schedule: interval: daily cooldown: @@ -9,6 +10,23 @@ updates: open-pull-requests-limit: 10 - package-ecosystem: "github-actions" directory: "/" + target-branch: "main" + schedule: + interval: daily + cooldown: + default-days: 7 + open-pull-requests-limit: 10 +- package-ecosystem: gomod + directory: "/" + target-branch: "v2" + schedule: + interval: daily + cooldown: + default-days: 7 + open-pull-requests-limit: 10 +- package-ecosystem: "github-actions" + directory: "/" + target-branch: "v2" schedule: interval: daily cooldown: From 5dfe40e243a1dce4cecf4d4cffc1c29c02ab2eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 8 Apr 2026 11:18:03 +0200 Subject: [PATCH 2283/2296] ci: skip backporting PRs with the dependencies label --- .github/workflows/backport.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index ba02bce85b..db5bc27aeb 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -19,6 +19,7 @@ jobs: # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. if: > github.event.pull_request.merged + && !contains(github.event.pull_request.labels.*.name, 'dependencies') && ( github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'backport-v1') @@ -84,6 +85,7 @@ jobs: # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. if: > github.event.pull_request.merged + && !contains(github.event.pull_request.labels.*.name, 'dependencies') && ( github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'backport-v2') From 52045f730156d7f246ebd8429161fa6b4b28fb2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 8 Apr 2026 12:20:25 +0200 Subject: [PATCH 2284/2296] ci: use exact release tags in GitHub Actions version comments Replace floating major version comments (e.g. # v6, # v2, # v1) with exact release tags matching each pinned SHA, and add missing version comments. This makes it easier to track which version is actually in use. --- .github/workflows/backport.yaml | 4 ++-- .github/workflows/codeql-analysis.yaml | 10 +++++----- .github/workflows/ensure-labels.yaml | 4 ++-- .github/workflows/functional-baremetal.yaml | 4 ++-- .github/workflows/functional-basic.yaml | 4 ++-- .github/workflows/functional-blockstorage.yaml | 4 ++-- .github/workflows/functional-compute.yaml | 4 ++-- .github/workflows/functional-containerinfra.yaml | 4 ++-- .github/workflows/functional-dns.yaml | 4 ++-- .github/workflows/functional-fwaas_v2.yaml | 4 ++-- .github/workflows/functional-identity.yaml | 4 ++-- .github/workflows/functional-image.yaml | 4 ++-- .github/workflows/functional-keymanager.yaml | 4 ++-- .github/workflows/functional-loadbalancer.yaml | 4 ++-- .github/workflows/functional-messaging.yaml | 4 ++-- .github/workflows/functional-metric.yaml | 4 ++-- .github/workflows/functional-networking.yaml | 4 ++-- .github/workflows/functional-objectstorage.yaml | 4 ++-- .github/workflows/functional-orchestration.yaml | 4 ++-- .github/workflows/functional-placement.yaml | 4 ++-- .github/workflows/functional-sharedfilesystems.yaml | 4 ++-- .github/workflows/functional-workflow.yaml | 4 ++-- .github/workflows/label-pr.yaml | 2 +- .github/workflows/lint.yaml | 4 ++-- .github/workflows/semver.yaml | 4 ++-- .github/workflows/unit.yaml | 8 ++++---- .github/workflows/zizmor.yaml | 2 +- 27 files changed, 57 insertions(+), 57 deletions(-) diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index db5bc27aeb..7e23e840f4 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -32,7 +32,7 @@ jobs: steps: - name: Generate a token from the gophercloud-backport-bot github-app id: generate_token - uses: getsentry/action-github-app-token@a0061014b82a6a5d6aeeb3b824aced47e3c3a7ef + uses: getsentry/action-github-app-token@5c1e90706fe007857338ac1bfbd7a4177db2f789 # v4.0.0 with: app_id: ${{ secrets.BACKPORT_APP_ID }} # zizmor: ignore[secrets-outside-env] private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] @@ -98,7 +98,7 @@ jobs: steps: - name: Generate a token from the gophercloud-backport-bot github-app id: generate_token - uses: getsentry/action-github-app-token@a0061014b82a6a5d6aeeb3b824aced47e3c3a7ef + uses: getsentry/action-github-app-token@5c1e90706fe007857338ac1bfbd7a4177db2f789 # v4.0.0 with: app_id: ${{ secrets.BACKPORT_APP_ID }} # zizmor: ignore[secrets-outside-env] private_key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }} # zizmor: ignore[secrets-outside-env] diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 3d11b8f50a..a445b45fc9 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -18,23 +18,23 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true - name: Initialize CodeQL - uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4 + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4 + uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4 + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 diff --git a/.github/workflows/ensure-labels.yaml b/.github/workflows/ensure-labels.yaml index bb92db737b..4df98ce2bc 100644 --- a/.github/workflows/ensure-labels.yaml +++ b/.github/workflows/ensure-labels.yaml @@ -13,10 +13,10 @@ jobs: issues: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: micnncim/action-label-syncer@3abd5ab72fda571e69fffd97bd4e0033dd5f495c # v1 + - uses: micnncim/action-label-syncer@3abd5ab72fda571e69fffd97bd4e0033dd5f495c # v1.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 7248cbf947..1aabed3a50 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -25,7 +25,7 @@ jobs: name: Ironic on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -101,7 +101,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index 313d67c2d3..ee9da6e81c 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -25,7 +25,7 @@ jobs: name: basic tests on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -55,7 +55,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index f067e785ce..a931d13f06 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -25,7 +25,7 @@ jobs: name: Cinder on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index b2d2f27701..d280e8f328 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -25,7 +25,7 @@ jobs: name: Nova on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index 8edabce192..db4122cc9b 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -46,7 +46,7 @@ jobs: name: Magnum on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -88,7 +88,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 5ad1d8771f..956b0c16da 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -31,7 +31,7 @@ jobs: name: Designate on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -69,7 +69,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 97cbb3f716..41559bd9eb 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -29,7 +29,7 @@ jobs: name: FWaaS_v2 on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -84,7 +84,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 651e59bc3d..6f9a1d1340 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -25,7 +25,7 @@ jobs: name: Keystone on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -59,7 +59,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index e9e688044d..980fbf7e02 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -25,7 +25,7 @@ jobs: name: Glance on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -59,7 +59,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index e312e034a0..aad940ca9b 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -37,7 +37,7 @@ jobs: name: Barbican on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -75,7 +75,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 8a80c2ea5e..e3c24ee6c9 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -36,7 +36,7 @@ jobs: name: Octavia on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -75,7 +75,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index 3aba20dd6f..c7b27e8058 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -25,7 +25,7 @@ jobs: name: Zaqar on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -62,7 +62,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-metric.yaml b/.github/workflows/functional-metric.yaml index 8f60039792..6178579e6e 100644 --- a/.github/workflows/functional-metric.yaml +++ b/.github/workflows/functional-metric.yaml @@ -25,7 +25,7 @@ jobs: name: Aetos on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -68,7 +68,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index b93e3c1f2c..eab70110b7 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -25,7 +25,7 @@ jobs: name: Neutron on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -79,7 +79,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 2b3db097d0..07c29a7438 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -25,7 +25,7 @@ jobs: name: Swift on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -65,7 +65,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index dbd5d36668..8396b954fb 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -25,7 +25,7 @@ jobs: name: Heat on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -61,7 +61,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 641b22e9bc..99fa2615c7 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -25,7 +25,7 @@ jobs: name: Placement on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -59,7 +59,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index e8fc1a03a6..928f297b24 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -31,7 +31,7 @@ jobs: name: Manila on OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -83,7 +83,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index bb57db4067..77ec801634 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -28,7 +28,7 @@ jobs: name: Mistral on Deploy OpenStack ${{ matrix.name }} steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -64,7 +64,7 @@ jobs: - name: Checkout go if: ${{ fromJSON(steps.changed-files.outputs.matches) }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/label-pr.yaml b/.github/workflows/label-pr.yaml index de5d9a52b0..882a879b13 100644 --- a/.github/workflows/label-pr.yaml +++ b/.github/workflows/label-pr.yaml @@ -109,4 +109,4 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6 + - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 88784d2215..37108b22f2 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/semver.yaml b/.github/workflows/semver.yaml index af9ff9643d..9e7133b525 100644 --- a/.github/workflows/semver.yaml +++ b/.github/workflows/semver.yaml @@ -13,7 +13,7 @@ jobs: analyze: runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} @@ -28,7 +28,7 @@ jobs: GIT_SEQUENCE_EDITOR: '/usr/bin/true' BASE_REF: ${{ github.base_ref }} - - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index fd82541b38..69b5466ee5 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -15,11 +15,11 @@ jobs: fail-fast: false steps: - name: Checkout Gophercloud - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Setup Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: 'go.mod' cache: true @@ -34,7 +34,7 @@ jobs: make unit make coverage - name: Check coverage - uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2 + uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7 with: file: cover.out parallel: true @@ -46,6 +46,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Store coverage results - uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2 + uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7 with: parallel-finished: true diff --git a/.github/workflows/zizmor.yaml b/.github/workflows/zizmor.yaml index e9b06d69ac..0a0fb0f41a 100644 --- a/.github/workflows/zizmor.yaml +++ b/.github/workflows/zizmor.yaml @@ -20,7 +20,7 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false From 817740a329e59d90c08b1abcc857b3e6a4214e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 8 Apr 2026 13:53:23 +0200 Subject: [PATCH 2285/2296] CI: prefer github mirrors whenever possible Our CI runs in github infra, let's use code that's already in github if we can. --- .github/workflows/functional-fwaas_v2.yaml | 2 +- .github/workflows/functional-metric.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index 97cbb3f716..39598b2530 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -70,7 +70,7 @@ 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 https://github.com/openstack/neutron-fwaas ${{ matrix.openstack_version }} Q_AGENT=openvswitch Q_ML2_PLUGIN_MECHANISM_DRIVERS=openvswitch,l2population Q_ML2_PLUGIN_TYPE_DRIVERS=flat,gre,vlan,vxlan diff --git a/.github/workflows/functional-metric.yaml b/.github/workflows/functional-metric.yaml index 8f60039792..71cae7d6bc 100644 --- a/.github/workflows/functional-metric.yaml +++ b/.github/workflows/functional-metric.yaml @@ -56,12 +56,12 @@ jobs: with: branch: ${{ matrix.openstack_version }} conf_overrides: | - enable_plugin devstack-plugin-prometheus https://opendev.org/openstack/devstack-plugin-prometheus ${{ matrix.openstack_version }} - enable_plugin ceilometer https://opendev.org/openstack/ceilometer ${{ matrix.openstack_version }} + enable_plugin devstack-plugin-prometheus https://github.com/openstack/devstack-plugin-prometheus ${{ matrix.openstack_version }} + enable_plugin ceilometer https://github.com/openstack/ceilometer ${{ matrix.openstack_version }} CEILOMETER_BACKEND=sg-core PROMETHEUS_CUSTOM_SCRAPE_TARGETS="localhost:3000" enable_plugin sg-core https://github.com/openstack-k8s-operators/sg-core main - enable_plugin aetos https://opendev.org/openstack/aetos ${{ matrix.openstack_version }} + enable_plugin aetos https://github.com/openstack/aetos ${{ matrix.openstack_version }} ${{ matrix.devstack_conf_overrides }} enabled_services: "prometheus,node_exporter,openstack_exporter,ceilometer-acompute,ceilometer-acentral,ceilometer-anotification,sg-core,aetos,openstack-cli-server" From 15c319c3c7d0671dfa7e4a306c0ebf60625b9926 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Wed, 8 Apr 2026 15:48:12 +0200 Subject: [PATCH 2286/2296] Implement Placement allocationcandidates Related #526 API reference: https://docs.openstack.org/api-ref/placement/#list-allocation-candidates Code reference: https://opendev.org/openstack/placement/src/branch/stable/2026.1/placement/handlers/allocation_candidate.py#L256-L304 Signed-off-by: Dominik Danelski --- .../placement/v1/allocationcandidates_test.go | 208 +++++++ .../placement/v1/allocationcandidates/doc.go | 58 ++ .../v1/allocationcandidates/microversions.go | 52 ++ .../v1/allocationcandidates/requests.go | 140 +++++ .../v1/allocationcandidates/results.go | 85 +++ .../v1/allocationcandidates/testing/doc.go | 1 + .../testing/fixtures_test.go | 578 ++++++++++++++++++ .../testing/requests_test.go | 140 +++++ .../placement/v1/allocationcandidates/urls.go | 9 + 9 files changed, 1271 insertions(+) create mode 100644 internal/acceptance/openstack/placement/v1/allocationcandidates_test.go create mode 100644 openstack/placement/v1/allocationcandidates/doc.go create mode 100644 openstack/placement/v1/allocationcandidates/microversions.go create mode 100644 openstack/placement/v1/allocationcandidates/requests.go create mode 100644 openstack/placement/v1/allocationcandidates/results.go create mode 100644 openstack/placement/v1/allocationcandidates/testing/doc.go create mode 100644 openstack/placement/v1/allocationcandidates/testing/fixtures_test.go create mode 100644 openstack/placement/v1/allocationcandidates/testing/requests_test.go create mode 100644 openstack/placement/v1/allocationcandidates/urls.go diff --git a/internal/acceptance/openstack/placement/v1/allocationcandidates_test.go b/internal/acceptance/openstack/placement/v1/allocationcandidates_test.go new file mode 100644 index 0000000000..2cfc4074ac --- /dev/null +++ b/internal/acceptance/openstack/placement/v1/allocationcandidates_test.go @@ -0,0 +1,208 @@ +//go:build acceptance || placement || allocationcandidates + +package v1 + +import ( + "context" + "slices" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/acceptance/clients" + "github.com/gophercloud/gophercloud/v2/internal/acceptance/tools" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/allocationcandidates" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders" + th "github.com/gophercloud/gophercloud/v2/testhelper" +) + +// createRPWithVCPUInventory creates a resource provider and seeds it with +// VCPU inventory, returning the provider UUID. The caller is responsible for +// deferring deletion of the provider. +func createRPWithVCPUInventory(t *testing.T, microversion string) (string, func()) { + t.Helper() + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + client.Microversion = microversion + + rp, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + + inventories, err := resourceproviders.GetInventories(context.TODO(), client, rp.UUID).Extract() + th.AssertNoErr(t, err) + + inventories, err = resourceproviders.UpdateInventories(context.TODO(), client, rp.UUID, resourceproviders.UpdateInventoriesOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Inventories: map[string]resourceproviders.Inventory{ + "VCPU": { + AllocationRatio: 1.0, + MaxUnit: 8, + MinUnit: 1, + Reserved: 0, + StepSize: 1, + Total: 8, + }, + }, + }).Extract() + th.AssertNoErr(t, err) + + _, err = resourceproviders.UpdateTraits(context.TODO(), client, rp.UUID, resourceproviders.UpdateTraitsOpts{ + ResourceProviderGeneration: inventories.ResourceProviderGeneration, + Traits: []string{"COMPUTE_NODE"}, + }).Extract() + th.AssertNoErr(t, err) + + cleanup := func() { DeleteResourceProvider(t, client, rp.UUID) } + return rp.UUID, cleanup +} + +func TestAllocationCandidatesList(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/train") + clients.RequireAdmin(t) + + microversion := "1.34" + rpUUID, cleanup := createRPWithVCPUInventory(t, microversion) + defer cleanup() + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + client.Microversion = microversion + + page, err := allocationcandidates.List(client, allocationcandidates.ListOpts{ + Resources: "VCPU:1", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + result, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + + th.AssertEquals(t, true, len(result.AllocationRequests) > 0) + + // Assert: The provider's summary contains the exact inventory we seeded: + // VCPU total=8, reserved=0 → capacity=8, used=0. + summary, present := result.ProviderSummaries[rpUUID] + th.AssertEquals(t, true, present) + vcpuSummary, present := summary.Resources["VCPU"] + th.AssertEquals(t, true, present) + th.AssertEquals(t, 8, vcpuSummary.Capacity) + th.AssertEquals(t, 0, vcpuSummary.Used) + th.AssertEquals(t, true, slices.Contains(*summary.Traits, "COMPUTE_NODE")) + // It is a root provider: root UUID equals its own UUID, parent is absent. + th.AssertEquals(t, rpUUID, *summary.RootProviderUUID) + th.AssertEquals(t, (*string)(nil), summary.ParentProviderUUID) + + // Assert: The allocation request contains the exact resource amount + // and the unsuffixed group maps to the newly created RP. + var req allocationcandidates.AllocationRequest + for _, r := range result.AllocationRequests { + if _, present := r.Allocations[rpUUID]; present { + req = r + break + } + } + th.AssertEquals(t, 1, req.Allocations[rpUUID].Resources["VCPU"]) + th.AssertDeepEquals(t, []string{rpUUID}, (*req.Mappings)[""]) + + tools.PrintResource(t, result) +} + +func TestAllocationCandidatesListPre129(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/queens") + clients.RequireAdmin(t) + + microversion := "1.17" + rpUUID, cleanup := createRPWithVCPUInventory(t, microversion) + defer cleanup() + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + client.Microversion = microversion + + page, err := allocationcandidates.List(client, allocationcandidates.ListOpts{ + Resources: "VCPU:1", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + result, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + + th.AssertEquals(t, true, len(result.AllocationRequests) > 0) + + summary, present := result.ProviderSummaries[rpUUID] + th.AssertEquals(t, true, present) + vcpuSummary, present := summary.Resources["VCPU"] + th.AssertEquals(t, true, present) + th.AssertEquals(t, 8, vcpuSummary.Capacity) + th.AssertEquals(t, 0, vcpuSummary.Used) + th.AssertEquals(t, true, slices.Contains(*summary.Traits, "COMPUTE_NODE")) + // Root/parent UUIDs are absent below 1.29. + th.AssertEquals(t, (*string)(nil), summary.RootProviderUUID) + th.AssertEquals(t, (*string)(nil), summary.ParentProviderUUID) + // Mappings are absent below 1.34. + th.AssertEquals(t, (*map[string][]string)(nil), result.AllocationRequests[0].Mappings) +} + +func TestAllocationCandidatesList110(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/pike") + clients.RequireAdmin(t) + + microversion := "1.10" + rpUUID, cleanup := createRPWithVCPUInventory(t, microversion) + defer cleanup() + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + client.Microversion = microversion + + page, err := allocationcandidates.List(client, allocationcandidates.ListOpts{ + Resources: "VCPU:1", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + // Pre 1.12 uses an incompatible response format; use the separate function + // from microversions.go. + result, err := allocationcandidates.ExtractAllocationCandidates110(page) + th.AssertNoErr(t, err) + + th.AssertEquals(t, true, len(result.AllocationRequests) > 0) + th.AssertEquals(t, true, len(result.ProviderSummaries) > 0) + + // Assert: UUID of the created RP present and resource amount correct. + var foundAlloc allocationcandidates.AllocationRequest110Resource + for _, req := range result.AllocationRequests { + for _, alloc := range req.Allocations { + if alloc.ResourceProvider.UUID == rpUUID { + foundAlloc = alloc + } + } + } + th.AssertEquals(t, rpUUID, foundAlloc.ResourceProvider.UUID) + th.AssertEquals(t, 1, foundAlloc.Resources["VCPU"]) + + // Assert: The provider summary contains the expected inventory. + rpSummary, present := result.ProviderSummaries[rpUUID] + th.AssertEquals(t, true, present) + vcpuSummary, present := rpSummary.Resources["VCPU"] + th.AssertEquals(t, true, present) + th.AssertEquals(t, 8, vcpuSummary.Capacity) + th.AssertEquals(t, 0, vcpuSummary.Used) +} + +func TestAllocationCandidatesListEmpty(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/pike") + clients.RequireAdmin(t) + + microversion := "1.10" + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + client.Microversion = microversion + + page, err := allocationcandidates.List(client, allocationcandidates.ListOpts{ + Resources: "MEMORY_MB:999999", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + result, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(result.AllocationRequests)) + th.AssertEquals(t, 0, len(result.ProviderSummaries)) +} diff --git a/openstack/placement/v1/allocationcandidates/doc.go b/openstack/placement/v1/allocationcandidates/doc.go new file mode 100644 index 0000000000..ae9ceb284c --- /dev/null +++ b/openstack/placement/v1/allocationcandidates/doc.go @@ -0,0 +1,58 @@ +/* +Package allocationcandidates queries allocation candidates from the +OpenStack Placement service. + +Allocation candidates API requests are available starting from version 1.10. + +The response format changed in version 1.12: the allocations field in +allocation_requests changed from an array to a dictionary keyed by +resource provider UUID. Use the microversions.go types for 1.10-1.11. + +Example to list allocation candidates + + listOpts := allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + Required: []string{"HW_CPU_X86_SSE"}, + } + + page, err := allocationcandidates.List(placementClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + allocationCandidates, err := allocationcandidates.ExtractAllocationCandidates(page) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", allocationCandidates) + +Example to list allocation candidates with granular resource groups (microversion >= 1.33) + + listOpts := allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + GroupPolicy: "isolate", + ResourceGroups: map[string]allocationcandidates.ResourceGroup{ + "1": { + Resources: "SRIOV_NET_VF:1", + Required: []string{"CUSTOM_PHYSNET1"}, + }, + "_NET": { + Resources: "NET_BW_EGR_KILOBIT_PER_SEC:10", + MemberOf: "in:42896e0d-205d-4fe3-bd1e-100924931787,5e08ea53-c4c6-448e-9334-ac4953de3cfa", + }, + }, + } + + placementClient.Microversion = "1.33" + page, err := allocationcandidates.List(placementClient, listOpts).AllPages(context.TODO()) + if err != nil { + panic(err) + } + allocationCandidates, err := allocationcandidates.ExtractAllocationCandidates(page) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", allocationCandidates) +*/ +package allocationcandidates diff --git a/openstack/placement/v1/allocationcandidates/microversions.go b/openstack/placement/v1/allocationcandidates/microversions.go new file mode 100644 index 0000000000..56b3c3c41b --- /dev/null +++ b/openstack/placement/v1/allocationcandidates/microversions.go @@ -0,0 +1,52 @@ +package allocationcandidates + +import "github.com/gophercloud/gophercloud/v2/pagination" + +// AllocationCandidates110 represents the response from a List allocation +// candidates request for microversions 1.10-1.11. +// In these versions the allocations field is an array rather than a dictionary. +type AllocationCandidates110 struct { + // AllocationRequests is a list of objects that contain information + // for creating a later allocation claim request. + AllocationRequests []AllocationRequest110 `json:"allocation_requests"` + + // ProviderSummaries is a dictionary keyed by resource provider UUID of + // inventory/capacity information for providers in the allocation_requests. + ProviderSummaries map[string]ProviderSummary110 `json:"provider_summaries"` +} + +// AllocationRequest110 represents a single allocation request for +// microversions 1.10-1.11 where allocations is an array. +type AllocationRequest110 struct { + // Allocations is a list of allocation resources per provider. + Allocations []AllocationRequest110Resource `json:"allocations"` +} + +// AllocationRequest110Resource represents a single provider allocation +// within a 1.10-1.11 allocation request. +type AllocationRequest110Resource struct { + // ResourceProvider contains the UUID of the resource provider. + ResourceProvider AllocationRequest110ResourceProvider `json:"resource_provider"` + + // Resources is a dictionary of resource class names to the amount requested. + Resources map[string]int `json:"resources"` +} + +// AllocationRequest110ResourceProvider contains the UUID of a resource provider. +type AllocationRequest110ResourceProvider struct { + UUID string `json:"uuid"` +} + +// ProviderSummary110 represents a provider summary for microversions 1.10-1.11 +// which only includes resources (no traits or parent/root UUIDs). +type ProviderSummary110 struct { + // Resources is a dictionary of resource class names to capacity/usage info. + Resources map[string]ProviderSummaryResource `json:"resources"` +} + +// ExtractAllocationCandidates110 interprets an AllocationCandidatesPage as AllocationCandidates110 (microversions 1.10-1.11). +func ExtractAllocationCandidates110(r pagination.Page) (*AllocationCandidates110, error) { + var s AllocationCandidates110 + err := (r.(AllocationCandidatesPage)).ExtractInto(&s) + return &s, err +} diff --git a/openstack/placement/v1/allocationcandidates/requests.go b/openstack/placement/v1/allocationcandidates/requests.go new file mode 100644 index 0000000000..02530a7cf2 --- /dev/null +++ b/openstack/placement/v1/allocationcandidates/requests.go @@ -0,0 +1,140 @@ +package allocationcandidates + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToAllocationCandidatesListQuery() (string, error) +} + +// ResourceGroup represents a granular resource request group. +// It is not a standalone request type, but becomes part of ListOpts, +// keyed by a user-defined suffix string. +// Available in version >= 1.25. +type ResourceGroup struct { + // Resources is a comma-separated list of resource amounts, e.g. "VCPU:1,MEMORY_MB:1024". + // Becomes the resourcesN query parameter. + Resources string + + // Required is a list of trait expressions for this group. + // Each entry becomes one requiredN query parameter. + // Available in version >= 1.39: can be repeated; supports in: syntax. + Required []string + + // MemberOf is an aggregate UUID or the prefix in: followed by a + // comma-separated list of aggregate UUIDs for this group. + // Becomes the member_ofN query parameter. + // Available in version >= 1.32: forbidden aggregates can be expressed + // with a ! prefix or the !in: prefix. + MemberOf string + + // InTree filters results to include only providers in the same tree as + // the specified provider UUID for this group. + // Becomes the in_treeN query parameter. + // Available in version >= 1.31. + InTree string +} + +// ListOpts allows filtering of allocation candidates. +type ListOpts struct { + // Resources is a comma-separated list of resource amounts that providers + // must collectively have capacity to serve, e.g. "VCPU:4,DISK_GB:64,MEMORY_MB:2048". + Resources string `q:"resources"` + + // Required is a comma-separated list of traits that a provider must have. + // Available in version >= 1.17. + // Available in version >= 1.22: prefix with ! for forbidden traits. + // Available in version >= 1.39: can be repeated and supports in: syntax. + Required []string `q:"required"` + + // MemberOf is a string representing an aggregate UUID, or the prefix in: + // followed by a comma-separated list of aggregate UUIDs. + // Available in version >= 1.21. + // Available in version >= 1.24: can be specified multiple times. + MemberOf []string `q:"member_of"` + + // InTree is a resource provider UUID. When supplied, filters candidates to + // only those providers that are in the same tree. + // Available in version >= 1.31. + InTree string `q:"in_tree"` + + // GroupPolicy indicates how the groups should interact when more than one + // resourcesN parameter is supplied. Valid values are "none" and "isolate". + // Available in version >= 1.25. + GroupPolicy string `q:"group_policy"` + + // Limit is a positive integer used to limit the maximum number of + // allocation candidates returned. + // Available in version >= 1.16. + Limit int `q:"limit"` + + // RootRequired is a comma-separated list of trait requirements that the + // root provider of the (non-sharing) tree must satisfy. + // Available in version >= 1.35. + RootRequired string `q:"root_required"` + + // SameSubtree is a comma-separated list of request group suffix strings. + // At least one of the resource providers satisfying a specified request group + // must be an ancestor of the rest. + // Available in version >= 1.36. + SameSubtree []string `q:"same_subtree"` + + // ResourceGroups allows specifying suffixed granular resource request groups. + // The map key is the non-empty group suffix (e.g. "1", "_NET1", "_STORAGE"). + // Use the top-level Resources/Required/MemberOf/InTree fields for the + // unsuffixed (default) group; do not use an empty string key here. + // In microversions 1.25-1.32 the suffix must be a numeric string. + // Starting from microversion 1.33 it can be 1-64 characters [a-zA-Z0-9_-]. + // Available in version >= 1.25. + ResourceGroups map[string]ResourceGroup `q:"-"` +} + +// ToAllocationCandidatesListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToAllocationCandidatesListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return "", err + } + + params := q.Query() + for suffix, group := range opts.ResourceGroups { + if group.Resources != "" { + params.Add("resources"+suffix, group.Resources) + } + for _, required := range group.Required { + if required != "" { + params.Add("required"+suffix, required) + } + } + if group.MemberOf != "" { + params.Add("member_of"+suffix, group.MemberOf) + } + if group.InTree != "" { + params.Add("in_tree"+suffix, group.InTree) + } + } + q.RawQuery = params.Encode() + + return q.String(), nil +} + +// List makes a request against the API to list allocation candidates. +func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(client) + + if opts != nil { + query, err := opts.ToAllocationCandidatesListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AllocationCandidatesPage{pagination.SinglePageBase(r)} + }) +} diff --git a/openstack/placement/v1/allocationcandidates/results.go b/openstack/placement/v1/allocationcandidates/results.go new file mode 100644 index 0000000000..44212872de --- /dev/null +++ b/openstack/placement/v1/allocationcandidates/results.go @@ -0,0 +1,85 @@ +package allocationcandidates + +import "github.com/gophercloud/gophercloud/v2/pagination" + +// AllocationCandidates represents the response from a List allocation +// candidates request for microversions 1.12 and above. +type AllocationCandidates struct { + // AllocationRequests is a list of objects that contain information + // for creating a later allocation claim request. + AllocationRequests []AllocationRequest `json:"allocation_requests"` + + // ProviderSummaries is a dictionary keyed by resource provider UUID of + // inventory/capacity information for providers in the allocation_requests. + ProviderSummaries map[string]ProviderSummary `json:"provider_summaries"` +} + +// AllocationRequest represents a single allocation request within the +// allocation candidates response. +type AllocationRequest struct { + // Allocations is a dictionary of resource allocations keyed by + // resource provider UUID. + Allocations map[string]AllocationRequestResource `json:"allocations"` + + // Mappings is a dictionary associating request group suffixes with a + // list of UUIDs identifying the resource providers that satisfied each group. + // Available in version >= 1.34. + Mappings *map[string][]string `json:"mappings"` +} + +// AllocationRequestResource represents the resources requested from a +// single resource provider within an allocation request. +type AllocationRequestResource struct { + // Resources is a dictionary of resource class names to the amount requested. + Resources map[string]int `json:"resources"` +} + +// ProviderSummary represents the summary of a resource provider's inventory +// and traits. +type ProviderSummary struct { + // Resources is a dictionary of resource class names to capacity/usage info. + Resources map[string]ProviderSummaryResource `json:"resources"` + + // Traits is a list of traits the resource provider has associated with it. + // Available in version >= 1.17. + Traits *[]string `json:"traits"` + + // ParentProviderUUID is the UUID of the immediate parent of the resource provider. + // Available in version >= 1.29. + ParentProviderUUID *string `json:"parent_provider_uuid"` + + // RootProviderUUID is the UUID of the top-most provider in this provider tree. + // Available in version >= 1.29. + RootProviderUUID *string `json:"root_provider_uuid"` +} + +// ProviderSummaryResource represents the capacity and usage of a single +// resource class for a provider. +type ProviderSummaryResource struct { + // Capacity is the amount of the resource that the provider can accommodate. + Capacity int `json:"capacity"` + + // Used is the amount of the resource that has been already allocated. + Used int `json:"used"` +} + +// AllocationCandidatesPage is the page returned from a List call. +type AllocationCandidatesPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines if an AllocationCandidatesPage contains any results. +func (page AllocationCandidatesPage) IsEmpty() (bool, error) { + candidates, err := ExtractAllocationCandidates(page) + if err != nil { + return false, err + } + return len(candidates.AllocationRequests) == 0, nil +} + +// ExtractAllocationCandidates interprets an AllocationCandidatesPage as AllocationCandidates (microversion 1.12+). +func ExtractAllocationCandidates(r pagination.Page) (*AllocationCandidates, error) { + var s AllocationCandidates + err := (r.(AllocationCandidatesPage)).ExtractInto(&s) + return &s, err +} diff --git a/openstack/placement/v1/allocationcandidates/testing/doc.go b/openstack/placement/v1/allocationcandidates/testing/doc.go new file mode 100644 index 0000000000..7603f836a0 --- /dev/null +++ b/openstack/placement/v1/allocationcandidates/testing/doc.go @@ -0,0 +1 @@ +package testing diff --git a/openstack/placement/v1/allocationcandidates/testing/fixtures_test.go b/openstack/placement/v1/allocationcandidates/testing/fixtures_test.go new file mode 100644 index 0000000000..f69a1ffb62 --- /dev/null +++ b/openstack/placement/v1/allocationcandidates/testing/fixtures_test.go @@ -0,0 +1,578 @@ +package testing + +import ( + "fmt" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2/internal/ptr" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/allocationcandidates" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +const AllocationCandidatesBody = ` +{ + "allocation_requests": [ + { + "allocations": { + "rp-uuid-1": { + "resources": { + "VCPU": 1, + "MEMORY_MB": 1024 + } + }, + "rp-uuid-2": { + "resources": { + "DISK_GB": 100 + } + } + }, + "mappings": { + "": ["rp-uuid-1"], + "_DISK": ["rp-uuid-2"] + } + } + ], + "provider_summaries": { + "rp-uuid-1": { + "resources": { + "VCPU": { + "capacity": 16, + "used": 0 + }, + "MEMORY_MB": { + "capacity": 32768, + "used": 0 + } + }, + "traits": ["HW_CPU_X86_AVX2", "HW_CPU_X86_SSE"], + "parent_provider_uuid": null, + "root_provider_uuid": "rp-uuid-1" + }, + "rp-uuid-2": { + "resources": { + "DISK_GB": { + "capacity": 1900, + "used": 0 + } + }, + "traits": ["MISC_SHARES_VIA_AGGREGATE"], + "parent_provider_uuid": null, + "root_provider_uuid": "rp-uuid-2" + } + } +} +` + +const AllocationCandidatesBodyPre134 = ` +{ + "allocation_requests": [ + { + "allocations": { + "rp-uuid-1": { + "resources": { + "VCPU": 1, + "MEMORY_MB": 1024 + } + }, + "rp-uuid-2": { + "resources": { + "DISK_GB": 100 + } + } + } + } + ], + "provider_summaries": { + "rp-uuid-1": { + "resources": { + "VCPU": { + "capacity": 16, + "used": 0 + }, + "MEMORY_MB": { + "capacity": 32768, + "used": 0 + } + }, + "traits": ["HW_CPU_X86_AVX2", "HW_CPU_X86_SSE"], + "parent_provider_uuid": null, + "root_provider_uuid": "rp-uuid-1" + }, + "rp-uuid-2": { + "resources": { + "DISK_GB": { + "capacity": 1900, + "used": 0 + } + }, + "traits": ["MISC_SHARES_VIA_AGGREGATE"], + "parent_provider_uuid": null, + "root_provider_uuid": "rp-uuid-2" + } + } +} +` + +const AllocationCandidatesBodyPre129 = ` +{ + "allocation_requests": [ + { + "allocations": { + "rp-uuid-1": { + "resources": { + "VCPU": 1, + "MEMORY_MB": 1024 + } + }, + "rp-uuid-2": { + "resources": { + "DISK_GB": 100 + } + } + } + } + ], + "provider_summaries": { + "rp-uuid-1": { + "resources": { + "VCPU": { + "capacity": 16, + "used": 0 + }, + "MEMORY_MB": { + "capacity": 32768, + "used": 0 + } + }, + "traits": ["HW_CPU_X86_AVX2", "HW_CPU_X86_SSE"] + }, + "rp-uuid-2": { + "resources": { + "DISK_GB": { + "capacity": 1900, + "used": 0 + } + }, + "traits": ["MISC_SHARES_VIA_AGGREGATE"] + } + } +} +` + +const AllocationCandidatesBodyPre117 = ` +{ + "allocation_requests": [ + { + "allocations": { + "rp-uuid-1": { + "resources": { + "VCPU": 1, + "MEMORY_MB": 1024 + } + }, + "rp-uuid-2": { + "resources": { + "DISK_GB": 100 + } + } + } + } + ], + "provider_summaries": { + "rp-uuid-1": { + "resources": { + "VCPU": { + "capacity": 16, + "used": 0 + }, + "MEMORY_MB": { + "capacity": 32768, + "used": 0 + } + } + }, + "rp-uuid-2": { + "resources": { + "DISK_GB": { + "capacity": 1900, + "used": 0 + } + } + } + } +} +` + +const AllocationCandidatesBody110 = ` +{ + "allocation_requests": [ + { + "allocations": [ + { + "resource_provider": { + "uuid": "rp-uuid-1" + }, + "resources": { + "VCPU": 1, + "MEMORY_MB": 1024 + } + } + ] + } + ], + "provider_summaries": { + "rp-uuid-1": { + "resources": { + "VCPU": { + "capacity": 16, + "used": 0 + }, + "MEMORY_MB": { + "capacity": 32768, + "used": 0 + } + } + } + } +} +` + +const AllocationCandidatesEmptyBody = ` +{ + "allocation_requests": [], + "provider_summaries": {} +} +` + +var ExpectedAllocationCandidates = allocationcandidates.AllocationCandidates{ + AllocationRequests: []allocationcandidates.AllocationRequest{ + { + Allocations: map[string]allocationcandidates.AllocationRequestResource{ + "rp-uuid-1": { + Resources: map[string]int{ + "VCPU": 1, + "MEMORY_MB": 1024, + }, + }, + "rp-uuid-2": { + Resources: map[string]int{ + "DISK_GB": 100, + }, + }, + }, + Mappings: &map[string][]string{ + "": {"rp-uuid-1"}, + "_DISK": {"rp-uuid-2"}, + }, + }, + }, + ProviderSummaries: map[string]allocationcandidates.ProviderSummary{ + "rp-uuid-1": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "VCPU": { + Capacity: 16, + Used: 0, + }, + "MEMORY_MB": { + Capacity: 32768, + Used: 0, + }, + }, + Traits: ptr.To([]string{"HW_CPU_X86_AVX2", "HW_CPU_X86_SSE"}), + ParentProviderUUID: nil, + RootProviderUUID: ptr.To("rp-uuid-1"), + }, + "rp-uuid-2": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "DISK_GB": { + Capacity: 1900, + Used: 0, + }, + }, + Traits: ptr.To([]string{"MISC_SHARES_VIA_AGGREGATE"}), + ParentProviderUUID: nil, + RootProviderUUID: ptr.To("rp-uuid-2"), + }, + }, +} + +var ExpectedAllocationCandidatesPre134 = allocationcandidates.AllocationCandidates{ + AllocationRequests: []allocationcandidates.AllocationRequest{ + { + Allocations: map[string]allocationcandidates.AllocationRequestResource{ + "rp-uuid-1": { + Resources: map[string]int{ + "VCPU": 1, + "MEMORY_MB": 1024, + }, + }, + "rp-uuid-2": { + Resources: map[string]int{ + "DISK_GB": 100, + }, + }, + }, + }, + }, + ProviderSummaries: map[string]allocationcandidates.ProviderSummary{ + "rp-uuid-1": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "VCPU": { + Capacity: 16, + Used: 0, + }, + "MEMORY_MB": { + Capacity: 32768, + Used: 0, + }, + }, + Traits: ptr.To([]string{"HW_CPU_X86_AVX2", "HW_CPU_X86_SSE"}), + ParentProviderUUID: nil, + RootProviderUUID: ptr.To("rp-uuid-1"), + }, + "rp-uuid-2": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "DISK_GB": { + Capacity: 1900, + Used: 0, + }, + }, + Traits: ptr.To([]string{"MISC_SHARES_VIA_AGGREGATE"}), + ParentProviderUUID: nil, + RootProviderUUID: ptr.To("rp-uuid-2"), + }, + }, +} + +var ExpectedAllocationCandidatesPre129 = allocationcandidates.AllocationCandidates{ + AllocationRequests: []allocationcandidates.AllocationRequest{ + { + Allocations: map[string]allocationcandidates.AllocationRequestResource{ + "rp-uuid-1": { + Resources: map[string]int{ + "VCPU": 1, + "MEMORY_MB": 1024, + }, + }, + "rp-uuid-2": { + Resources: map[string]int{ + "DISK_GB": 100, + }, + }, + }, + }, + }, + ProviderSummaries: map[string]allocationcandidates.ProviderSummary{ + "rp-uuid-1": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "VCPU": { + Capacity: 16, + Used: 0, + }, + "MEMORY_MB": { + Capacity: 32768, + Used: 0, + }, + }, + Traits: ptr.To([]string{"HW_CPU_X86_AVX2", "HW_CPU_X86_SSE"}), + }, + "rp-uuid-2": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "DISK_GB": { + Capacity: 1900, + Used: 0, + }, + }, + Traits: ptr.To([]string{"MISC_SHARES_VIA_AGGREGATE"}), + }, + }, +} + +var ExpectedAllocationCandidatesPre117 = allocationcandidates.AllocationCandidates{ + AllocationRequests: []allocationcandidates.AllocationRequest{ + { + Allocations: map[string]allocationcandidates.AllocationRequestResource{ + "rp-uuid-1": { + Resources: map[string]int{ + "VCPU": 1, + "MEMORY_MB": 1024, + }, + }, + "rp-uuid-2": { + Resources: map[string]int{ + "DISK_GB": 100, + }, + }, + }, + }, + }, + ProviderSummaries: map[string]allocationcandidates.ProviderSummary{ + "rp-uuid-1": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "VCPU": { + Capacity: 16, + Used: 0, + }, + "MEMORY_MB": { + Capacity: 32768, + Used: 0, + }, + }, + }, + "rp-uuid-2": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "DISK_GB": { + Capacity: 1900, + Used: 0, + }, + }, + }, + }, +} + +var ExpectedAllocationCandidates110 = allocationcandidates.AllocationCandidates110{ + AllocationRequests: []allocationcandidates.AllocationRequest110{ + { + Allocations: []allocationcandidates.AllocationRequest110Resource{ + { + ResourceProvider: allocationcandidates.AllocationRequest110ResourceProvider{ + UUID: "rp-uuid-1", + }, + Resources: map[string]int{ + "VCPU": 1, + "MEMORY_MB": 1024, + }, + }, + }, + }, + }, + ProviderSummaries: map[string]allocationcandidates.ProviderSummary110{ + "rp-uuid-1": { + Resources: map[string]allocationcandidates.ProviderSummaryResource{ + "VCPU": { + Capacity: 16, + Used: 0, + }, + "MEMORY_MB": { + Capacity: 32768, + Used: 0, + }, + }, + }, + }, +} + +func HandleListAllocationCandidatesSuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/allocation_candidates", + 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.Fprint(w, AllocationCandidatesBody) + }) +} + +func HandleListAllocationCandidatesPre134Success(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/allocation_candidates", + 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.Fprint(w, AllocationCandidatesBodyPre134) + }) +} + +func HandleListAllocationCandidatesPre129Success(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/allocation_candidates", + 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.Fprint(w, AllocationCandidatesBodyPre129) + }) +} + +func HandleListAllocationCandidatesPre117Success(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/allocation_candidates", + 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.Fprint(w, AllocationCandidatesBodyPre117) + }) +} + +func HandleListAllocationCandidates110Success(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/allocation_candidates", + 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.Fprint(w, AllocationCandidatesBody110) + }) +} + +func HandleListAllocationCandidatesEmptySuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/allocation_candidates", + 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.Fprint(w, AllocationCandidatesEmptyBody) + }) +} + +func HandleListAllocationCandidatesBadRequest(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/allocation_candidates", + 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.StatusBadRequest) + + fmt.Fprint(w, `{"errors": [{"status": 400, "detail": "Invalid resources parameter."}]}`) + }) +} + +func HandleListAllocationCandidatesWithFullQuerySuccess(t *testing.T, fakeServer th.FakeServer) { + fakeServer.Mux.HandleFunc("/allocation_candidates", + func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + q := r.URL.Query() + th.AssertEquals(t, "VCPU:1,MEMORY_MB:1024", q.Get("resources")) + th.AssertEquals(t, "5", q.Get("limit")) + th.AssertEquals(t, "isolate", q.Get("group_policy")) + th.AssertEquals(t, "SRIOV_NET_VF:1", q.Get("resources1")) + th.AssertEquals(t, "CUSTOM_PHYSNET1", q.Get("required1")) + // required is repeated; order matches the ListOpts.Required slice order. + th.AssertDeepEquals(t, []string{"HW_CPU_X86_SSE", "!HW_CPU_X86_AVX2"}, q["required"]) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprint(w, AllocationCandidatesBody) + }) +} diff --git a/openstack/placement/v1/allocationcandidates/testing/requests_test.go b/openstack/placement/v1/allocationcandidates/testing/requests_test.go new file mode 100644 index 0000000000..14b0530512 --- /dev/null +++ b/openstack/placement/v1/allocationcandidates/testing/requests_test.go @@ -0,0 +1,140 @@ +package testing + +import ( + "context" + "net/http" + "testing" + + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/allocationcandidates" + th "github.com/gophercloud/gophercloud/v2/testhelper" + "github.com/gophercloud/gophercloud/v2/testhelper/client" +) + +func TestListAllocationCandidatesSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidatesSuccess(t, fakeServer) + + page, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAllocationCandidates, *actual) +} + +func TestListAllocationCandidatesPre134Success(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidatesPre134Success(t, fakeServer) + + page, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAllocationCandidatesPre134, *actual) +} + +func TestListAllocationCandidatesPre129Success(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidatesPre129Success(t, fakeServer) + + page, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAllocationCandidatesPre129, *actual) +} + +func TestListAllocationCandidatesPre117Success(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidatesPre117Success(t, fakeServer) + + page, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAllocationCandidatesPre117, *actual) +} + +func TestListAllocationCandidates110Success(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidates110Success(t, fakeServer) + + page, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := allocationcandidates.ExtractAllocationCandidates110(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAllocationCandidates110, *actual) +} + +func TestListAllocationCandidatesEmptySuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidatesEmptySuccess(t, fakeServer) + + page, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + th.AssertEquals(t, 0, len(actual.AllocationRequests)) + th.AssertEquals(t, 0, len(actual.ProviderSummaries)) +} + +func TestListAllocationCandidatesBadRequest(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidatesBadRequest(t, fakeServer) + + _, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "INVALID", + }).AllPages(context.TODO()) + th.AssertErr(t, err) + th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusBadRequest)) +} + +func TestListAllocationCandidatesWithFullQuerySuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidatesWithFullQuerySuccess(t, fakeServer) + + page, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + Required: []string{"HW_CPU_X86_SSE", "!HW_CPU_X86_AVX2"}, + Limit: 5, + GroupPolicy: "isolate", + ResourceGroups: map[string]allocationcandidates.ResourceGroup{ + "1": { + Resources: "SRIOV_NET_VF:1", + Required: []string{"CUSTOM_PHYSNET1"}, + }, + }, + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + actual, err := allocationcandidates.ExtractAllocationCandidates(page) + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedAllocationCandidates, *actual) +} diff --git a/openstack/placement/v1/allocationcandidates/urls.go b/openstack/placement/v1/allocationcandidates/urls.go new file mode 100644 index 0000000000..fd3f4a6743 --- /dev/null +++ b/openstack/placement/v1/allocationcandidates/urls.go @@ -0,0 +1,9 @@ +package allocationcandidates + +import "github.com/gophercloud/gophercloud/v2" + +const apiName = "allocation_candidates" + +func listURL(client *gophercloud.ServiceClient) string { + return client.ServiceURL(apiName) +} From 5492c7b025ebb2f11218c39a826b319fd9f766f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 8 Apr 2026 16:58:14 +0200 Subject: [PATCH 2287/2296] CI: drop pyghmi dependency It has been deprecated for a very long time [1]. [1] https://opendev.org/openstack/ironic/src/branch/master/releasenotes/notes/remove-ipminative-driver-3367d25bbcc41fdc.yaml --- .github/workflows/functional-baremetal.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 7248cbf947..8b85e752b1 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -64,10 +64,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 + LIBS_FROM_GIT=virtualbmc FORCE_CONFIG_DRIVE=True Q_AGENT=openvswitch Q_ML2_TENANT_NETWORK_TYPE=vxlan From 0e864ef2debd5a5e220c44b505080b0b982a6ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Andr=C3=A9?= Date: Wed, 8 Apr 2026 17:09:34 +0200 Subject: [PATCH 2288/2296] CI: Get virtualbmc from pypi --- .github/workflows/functional-baremetal.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index 8b85e752b1..08ffa80ae4 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -65,7 +65,6 @@ jobs: branch: ${{ matrix.openstack_version }} conf_overrides: | enable_plugin ironic https://github.com/openstack/ironic ${{ matrix.openstack_version }} - LIBS_FROM_GIT=virtualbmc FORCE_CONFIG_DRIVE=True Q_AGENT=openvswitch Q_ML2_TENANT_NETWORK_TYPE=vxlan From ee950e0d6a379ed7aab8a7da94710192793dbe45 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 8 Apr 2026 17:20:26 +0100 Subject: [PATCH 2289/2296] script: Add script to managed required jobs You cannot do this via the workflow files and doing it via the Web API is tedious and error prone. Add a minimal script to allow us to do this via the REST API. Signed-off-by: Stephen Finucane Assisted-by: Claude Sonnet 4.6 --- script/configure-required-github-jobs | 146 ++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100755 script/configure-required-github-jobs diff --git a/script/configure-required-github-jobs b/script/configure-required-github-jobs new file mode 100755 index 0000000000..172911cd5a --- /dev/null +++ b/script/configure-required-github-jobs @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +WORKFLOWS_DIR="${REPO_ROOT}/.github/workflows" +DRY_RUN=false + +usage() { + cat <&2; usage >&2; exit 1 ;; + esac +done + +command -v yq &>/dev/null || { echo "ERROR: yq not installed (https://github.com/mikefarah/yq)"; exit 1; } +command -v jq &>/dev/null || { echo "ERROR: jq not installed"; exit 1; } +command -v gh &>/dev/null || { echo "ERROR: gh CLI not installed"; exit 1; } +gh auth status &>/dev/null || { echo "ERROR: run 'gh auth login' first"; exit 1; } + +# Check names listed here are excluded from required status checks even if +# their workflow has an `on: merge_group` trigger. Add entries using the exact +# name format: " / " (with resolved matrix values). +SKIP_CHECKS=( + "finish" # coveralls parallel-finish job, not a test gate +) + +current_contexts=() +while IFS= read -r ctx; do + current_contexts+=("${ctx}") +done < <(gh api \ + -H "Accept: application/vnd.github+json" \ + "/repos/gophercloud/gophercloud/branches/main/protection" \ + --jq '.required_status_checks.contexts[]?' 2>/dev/null | sort) + +updated_contexts=() +for f in "${WORKFLOWS_DIR}"/*.yaml; do + # Only consider workflows that run in the merge queue + grep -q "merge_group" "${f}" || continue + + while IFS= read -r job_id; do + # Use the job's display name if set, otherwise fall back to the job ID + job_name="$(yq ".jobs[\"${job_id}\"].name // \"${job_id}\"" "${f}")" + + # Check whether this job has a matrix 'include' with a 'name' field + matrix_names="$(yq ".jobs[\"${job_id}\"].strategy.matrix.include[].name" "${f}" 2>/dev/null || true)" + + if [[ -n "${matrix_names}" ]]; then + # Expand each matrix entry, replacing ${{ matrix.name }} in the job name + while IFS= read -r matrix_val; do + resolved="${job_name//\$\{\{ matrix.name \}\}/${matrix_val}}" + updated_contexts+=("${resolved}") + done <<< "${matrix_names}" + else + updated_contexts+=("${job_name}") + fi + done < <(yq '.jobs | keys | .[]' "${f}") +done + +filtered_contexts=() +for ctx in "${updated_contexts[@]}"; do + skip=false + for skip_ctx in "${SKIP_CHECKS[@]}"; do + [[ "${ctx}" == "${skip_ctx}" ]] && { skip=true; break; } + done + "${skip}" || filtered_contexts+=("${ctx}") +done + +added_contexts=() +removed_contexts=() + +for ctx in "${filtered_contexts[@]}"; do + found=false + for cur in "${current_contexts[@]}"; do + [[ "${ctx}" == "${cur}" ]] && { found=true; break; } + done + "${found}" || added_contexts+=("${ctx}") +done + +for cur in "${current_contexts[@]}"; do + found=false + for ctx in "${filtered_contexts[@]}"; do + [[ "${cur}" == "${ctx}" ]] && { found=true; break; } + done + "${found}" || removed_contexts+=("${cur}") +done + +if [[ ${#added_contexts[@]} -eq 0 && ${#removed_contexts[@]} -eq 0 ]]; then + echo "Required status checks: no changes (${#filtered_contexts[@]} checks already configured)." +else + echo "Required status checks diff:" + for ctx in "${removed_contexts[@]}"; do printf ' - %s\n' "${ctx}"; done + for ctx in "${added_contexts[@]}"; do printf ' + %s\n' "${ctx}"; done +fi + +if "${DRY_RUN}"; then + echo "" + echo "Dry-run mode — not applying." + exit 0 +fi + +echo "" +echo "Applying to gophercloud/gophercloud:main ..." + +contexts_json="$(printf '%s\n' "${filtered_contexts[@]}" | jq -R . | jq -s .)" + +payload="$(jq -n \ + --argjson contexts "${contexts_json}" \ + '{ + required_status_checks: {strict: false, contexts: $contexts}, + enforce_admins: false, + required_pull_request_reviews: { + required_approving_review_count: 1, + dismiss_stale_reviews: true, + require_last_push_approval: true + }, + restrictions: null, + required_linear_history: true, + allow_force_pushes: false, + allow_deletions: false, + required_conversation_resolution: true + }')" + +gh api \ + --method PUT \ + -H "Accept: application/vnd.github+json" \ + "/repos/gophercloud/gophercloud/branches/main/protection" \ + --input - <<< "${payload}" + +echo "Done. Verify at: https://github.com/gophercloud/gophercloud/settings/branches" From f080482f5be59f9a222603aace70330e5ce3eec4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 9 Apr 2026 10:08:18 +0100 Subject: [PATCH 2290/2296] Verify all branch protection rules Signed-off-by: Stephen Finucane --- script/configure-required-github-jobs | 55 ++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/script/configure-required-github-jobs b/script/configure-required-github-jobs index 172911cd5a..bb4412961d 100755 --- a/script/configure-required-github-jobs +++ b/script/configure-required-github-jobs @@ -9,9 +9,12 @@ usage() { cat </dev/null || { echo "ERROR: jq not installed"; exit 1; } command -v gh &>/dev/null || { echo "ERROR: gh CLI not installed"; exit 1; } gh auth status &>/dev/null || { echo "ERROR: run 'gh auth login' first"; exit 1; } +# Our non-required status check settings are defined here. These should likely +# never change. +declare -A EXPECTED_SETTINGS=( + [".enforce_admins.enabled"]="true" + [".required_linear_history.enabled"]="false" + [".allow_force_pushes.enabled"]="false" + [".allow_deletions.enabled"]="false" + [".required_conversation_resolution.enabled"]="true" + [".required_status_checks.strict"]="false" + [".required_pull_request_reviews.required_approving_review_count"]="1" + [".required_pull_request_reviews.dismiss_stale_reviews"]="true" + [".required_pull_request_reviews.require_last_push_approval"]="true" +) + # Check names listed here are excluded from required status checks even if # their workflow has an `on: merge_group` trigger. Add entries using the exact -# name format: " / " (with resolved matrix values). +# name format: "" (with resolved matrix values). SKIP_CHECKS=( "finish" # coveralls parallel-finish job, not a test gate ) +protection="$(gh api \ + -H "Accept: application/vnd.github+json" \ + "/repos/gophercloud/gophercloud/branches/main/protection")" + +settings_ok=true +for path in "${!EXPECTED_SETTINGS[@]}"; do + actual="$(jq -r "${path}" <<< "${protection}")" + expected="${EXPECTED_SETTINGS[${path}]}" + if [[ "${actual}" != "${expected}" ]]; then + echo "ERROR: branch protection drift: ${path} = ${actual} (expected ${expected})" >&2 + settings_ok=false + fi +done +if ! "${settings_ok}"; then + echo "Branch protection settings have drifted." >&2 + exit 1 +fi + +echo "Non-required status check branch protection settings are as expected." +echo + current_contexts=() while IFS= read -r ctx; do current_contexts+=("${ctx}") -done < <(gh api \ - -H "Accept: application/vnd.github+json" \ - "/repos/gophercloud/gophercloud/branches/main/protection" \ - --jq '.required_status_checks.contexts[]?' 2>/dev/null | sort) +done < <(jq -r '.required_status_checks.contexts[]?' <<< "${protection}" | sort) updated_contexts=() for f in "${WORKFLOWS_DIR}"/*.yaml; do @@ -124,14 +159,14 @@ payload="$(jq -n \ --argjson contexts "${contexts_json}" \ '{ required_status_checks: {strict: false, contexts: $contexts}, - enforce_admins: false, + enforce_admins: true, required_pull_request_reviews: { required_approving_review_count: 1, dismiss_stale_reviews: true, require_last_push_approval: true }, restrictions: null, - required_linear_history: true, + required_linear_history: false, allow_force_pushes: false, allow_deletions: false, required_conversation_resolution: true From 6bb7b9b82b8d9f15f77ae6efc8bbb9ee44dd7266 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 8 Apr 2026 12:23:41 +0100 Subject: [PATCH 2291/2296] ci: Drop Dalmatian testing, add Gazpacho Dalmatian goes EOL later this month [1] and Gazpacho is the latest SLURP release. Epoxy is retained as our new oldest supported release. Signed-off-by: Stephen Finucane --- .github/workflows/functional-baremetal.yaml | 6 +++--- .github/workflows/functional-basic.yaml | 6 +++--- .github/workflows/functional-blockstorage.yaml | 6 +++--- .github/workflows/functional-compute.yaml | 6 +++--- .github/workflows/functional-containerinfra.yaml | 16 ++++++++-------- .github/workflows/functional-dns.yaml | 6 +++--- .github/workflows/functional-fwaas_v2.yaml | 6 +++--- .github/workflows/functional-identity.yaml | 6 +++--- .github/workflows/functional-image.yaml | 6 +++--- .github/workflows/functional-keymanager.yaml | 10 +++++----- .github/workflows/functional-loadbalancer.yaml | 9 ++++++--- .github/workflows/functional-messaging.yaml | 6 +++--- .github/workflows/functional-networking.yaml | 6 +++--- .github/workflows/functional-objectstorage.yaml | 6 +++--- .github/workflows/functional-orchestration.yaml | 6 +++--- .github/workflows/functional-placement.yaml | 6 +++--- .../workflows/functional-sharedfilesystems.yaml | 6 +++--- .github/workflows/functional-workflow.yaml | 8 ++++---- 18 files changed, 65 insertions(+), 62 deletions(-) diff --git a/.github/workflows/functional-baremetal.yaml b/.github/workflows/functional-baremetal.yaml index b7db693b50..e7f0417392 100644 --- a/.github/workflows/functional-baremetal.yaml +++ b/.github/workflows/functional-baremetal.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Ironic on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-basic.yaml b/.github/workflows/functional-basic.yaml index ee9da6e81c..ebde821546 100644 --- a/.github/workflows/functional-basic.yaml +++ b/.github/workflows/functional-basic.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: basic tests on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-blockstorage.yaml b/.github/workflows/functional-blockstorage.yaml index a931d13f06..5df7932e68 100644 --- a/.github/workflows/functional-blockstorage.yaml +++ b/.github/workflows/functional-blockstorage.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Cinder on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-compute.yaml b/.github/workflows/functional-compute.yaml index d280e8f328..6f0a96a964 100644 --- a/.github/workflows/functional-compute.yaml +++ b/.github/workflows/functional-compute.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Nova on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index db4122cc9b..a11783dfea 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -24,8 +24,8 @@ jobs: enable_plugin magnum https://github.com/openstack/magnum master MAGNUMCLIENT_BRANCH=master - - name: "epoxy" - openstack_version: "stable/2025.1" + - name: "gazpacho" + openstack_version: "stable/2026.1" ubuntu_version: "24.04" devstack_conf_overrides: | # ensure we're using a working version of setuptools @@ -34,14 +34,14 @@ jobs: sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi + enable_plugin magnum https://github.com/openstack/magnum stable/2026.1 + MAGNUMCLIENT_BRANCH=stable/2026.1 + - name: "epoxy" + openstack_version: "stable/2025.1" + ubuntu_version: "24.04" + devstack_conf_overrides: | enable_plugin magnum https://github.com/openstack/magnum stable/2025.1 MAGNUMCLIENT_BRANCH=stable/2025.1 - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" - devstack_conf_overrides: | - enable_plugin magnum https://github.com/openstack/magnum stable/2024.2 - MAGNUMCLIENT_BRANCH=stable/2024.2 runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Magnum on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 956b0c16da..0d32d8cc67 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -21,12 +21,12 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Designate on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-fwaas_v2.yaml b/.github/workflows/functional-fwaas_v2.yaml index bce62f88f7..8e1d86203f 100644 --- a/.github/workflows/functional-fwaas_v2.yaml +++ b/.github/workflows/functional-fwaas_v2.yaml @@ -19,12 +19,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: FWaaS_v2 on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-identity.yaml b/.github/workflows/functional-identity.yaml index 6f9a1d1340..a986d9df3e 100644 --- a/.github/workflows/functional-identity.yaml +++ b/.github/workflows/functional-identity.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Keystone on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-image.yaml b/.github/workflows/functional-image.yaml index 980fbf7e02..4400ec1925 100644 --- a/.github/workflows/functional-image.yaml +++ b/.github/workflows/functional-image.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Glance on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index aad940ca9b..75f3e0467f 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -21,8 +21,8 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi - - name: "epoxy" - openstack_version: "stable/2025.1" + - name: "gazpacho" + openstack_version: "stable/2026.1" ubuntu_version: "24.04" devstack_conf_overrides: | # ensure we're using a working version of setuptools @@ -30,9 +30,9 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" + - name: "epoxy" + openstack_version: "stable/2025.1" + ubuntu_version: "24.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Barbican on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index e3c24ee6c9..423ae7f841 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -23,15 +23,18 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" + devstack_conf_overrides: | + LIBVIRT_CPU_MODE=host-passthrough + OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID=jammy - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" devstack_conf_overrides: | LIBVIRT_CPU_MODE=host-passthrough OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID=jammy - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Octavia on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-messaging.yaml b/.github/workflows/functional-messaging.yaml index c7b27e8058..143a84c466 100644 --- a/.github/workflows/functional-messaging.yaml +++ b/.github/workflows/functional-messaging.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Zaqar on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-networking.yaml b/.github/workflows/functional-networking.yaml index eab70110b7..b68e0f8788 100644 --- a/.github/workflows/functional-networking.yaml +++ b/.github/workflows/functional-networking.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Neutron on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-objectstorage.yaml b/.github/workflows/functional-objectstorage.yaml index 07c29a7438..14a574cdba 100644 --- a/.github/workflows/functional-objectstorage.yaml +++ b/.github/workflows/functional-objectstorage.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Swift on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-orchestration.yaml b/.github/workflows/functional-orchestration.yaml index 8396b954fb..72e7791674 100644 --- a/.github/workflows/functional-orchestration.yaml +++ b/.github/workflows/functional-orchestration.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Heat on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-placement.yaml b/.github/workflows/functional-placement.yaml index 99fa2615c7..ef8fb9e3da 100644 --- a/.github/workflows/functional-placement.yaml +++ b/.github/workflows/functional-placement.yaml @@ -15,12 +15,12 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Placement on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 928f297b24..289e884d30 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -21,12 +21,12 @@ jobs: sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra fi + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Manila on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-workflow.yaml b/.github/workflows/functional-workflow.yaml index 77ec801634..faa216b566 100644 --- a/.github/workflows/functional-workflow.yaml +++ b/.github/workflows/functional-workflow.yaml @@ -16,14 +16,14 @@ jobs: openstack_version: "master" ubuntu_version: "24.04" mistral_plugin_version: "master" + - name: "gazpacho" + openstack_version: "stable/2026.1" + ubuntu_version: "24.04" + mistral_plugin_version: "stable/2026.1" - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" mistral_plugin_version: "stable/2025.1" - - name: "dalmatian" - openstack_version: "stable/2024.2" - ubuntu_version: "22.04" - mistral_plugin_version: "stable/2024.2" runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Mistral on Deploy OpenStack ${{ matrix.name }} steps: From 8fdecf282260738f25a4be3853e797bbb064efaf Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 8 Apr 2026 12:25:32 +0100 Subject: [PATCH 2292/2296] ci: Remove setuptools overrides Most of these issues have been fixed upstream by now. Signed-off-by: Stephen Finucane --- .github/workflows/functional-containerinfra.yaml | 12 ------------ .github/workflows/functional-dns.yaml | 6 ------ .github/workflows/functional-keymanager.yaml | 12 ------------ .github/workflows/functional-loadbalancer.yaml | 5 ----- .github/workflows/functional-metric.yaml | 6 ------ .github/workflows/functional-sharedfilesystems.yaml | 6 ------ 6 files changed, 47 deletions(-) diff --git a/.github/workflows/functional-containerinfra.yaml b/.github/workflows/functional-containerinfra.yaml index a11783dfea..eb0d408214 100644 --- a/.github/workflows/functional-containerinfra.yaml +++ b/.github/workflows/functional-containerinfra.yaml @@ -16,24 +16,12 @@ jobs: openstack_version: "master" ubuntu_version: "24.04" devstack_conf_overrides: | - # ensure we're using a working version of setuptools - if [ -n "\$TOP_DIR" ]; then - sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python - sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra - fi - enable_plugin magnum https://github.com/openstack/magnum master MAGNUMCLIENT_BRANCH=master - name: "gazpacho" openstack_version: "stable/2026.1" ubuntu_version: "24.04" devstack_conf_overrides: | - # ensure we're using a working version of setuptools - if [ -n "\$TOP_DIR" ]; then - sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python - sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra - fi - enable_plugin magnum https://github.com/openstack/magnum stable/2026.1 MAGNUMCLIENT_BRANCH=stable/2026.1 - name: "epoxy" diff --git a/.github/workflows/functional-dns.yaml b/.github/workflows/functional-dns.yaml index 0d32d8cc67..1ab0e95f4f 100644 --- a/.github/workflows/functional-dns.yaml +++ b/.github/workflows/functional-dns.yaml @@ -15,12 +15,6 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - devstack_conf_overrides: | - # ensure we're using a working version of setuptools - if [ -n "\$TOP_DIR" ]; then - sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python - sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra - fi - name: "gazpacho" openstack_version: "stable/2026.1" ubuntu_version: "24.04" diff --git a/.github/workflows/functional-keymanager.yaml b/.github/workflows/functional-keymanager.yaml index 75f3e0467f..7bb4cdc1d9 100644 --- a/.github/workflows/functional-keymanager.yaml +++ b/.github/workflows/functional-keymanager.yaml @@ -15,21 +15,9 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - devstack_conf_overrides: | - # ensure we're using a working version of setuptools - if [ -n "\$TOP_DIR" ]; then - sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python - sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra - fi - name: "gazpacho" openstack_version: "stable/2026.1" ubuntu_version: "24.04" - devstack_conf_overrides: | - # ensure we're using a working version of setuptools - if [ -n "\$TOP_DIR" ]; then - sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python - sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra - fi - name: "epoxy" openstack_version: "stable/2025.1" ubuntu_version: "24.04" diff --git a/.github/workflows/functional-loadbalancer.yaml b/.github/workflows/functional-loadbalancer.yaml index 423ae7f841..588bc400f8 100644 --- a/.github/workflows/functional-loadbalancer.yaml +++ b/.github/workflows/functional-loadbalancer.yaml @@ -18,11 +18,6 @@ jobs: devstack_conf_overrides: | LIBVIRT_CPU_MODE=host-passthrough OCTAVIA_AMP_DISTRIBUTION_RELEASE_ID=jammy - # ensure we're using a working version of setuptools - if [ -n "\$TOP_DIR" ]; then - sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python - sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra - fi - name: "gazpacho" openstack_version: "stable/2026.1" ubuntu_version: "24.04" diff --git a/.github/workflows/functional-metric.yaml b/.github/workflows/functional-metric.yaml index 8efa8d14e9..b00719e921 100644 --- a/.github/workflows/functional-metric.yaml +++ b/.github/workflows/functional-metric.yaml @@ -15,12 +15,6 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - devstack_conf_overrides: | - # ensure we're using a working version of setuptools - if [ -n "\$TOP_DIR" ]; then - sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python - sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra - fi runs-on: ubuntu-${{ matrix.ubuntu_version }} name: Aetos on OpenStack ${{ matrix.name }} steps: diff --git a/.github/workflows/functional-sharedfilesystems.yaml b/.github/workflows/functional-sharedfilesystems.yaml index 289e884d30..fd1fa1ec22 100644 --- a/.github/workflows/functional-sharedfilesystems.yaml +++ b/.github/workflows/functional-sharedfilesystems.yaml @@ -15,12 +15,6 @@ jobs: - name: "master" openstack_version: "master" ubuntu_version: "24.04" - devstack_conf_overrides: | - # ensure we're using a working version of setuptools - if [ -n "\$TOP_DIR" ]; then - sed -i 's/setuptools\[core\]$/setuptools[core]==79.0.1/g' \$TOP_DIR/lib/infra \$TOP_DIR/inc/python - sed -i 's/pip_install "-U" "pbr"/pip_install "-U" "pbr" "setuptools[core]==79.0.1"/g' \$TOP_DIR/lib/infra - fi - name: "gazpacho" openstack_version: "stable/2026.1" ubuntu_version: "24.04" From de92433b5d19b20cf28fe5cd45480e9abd433764 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Thu, 9 Apr 2026 22:45:33 +0200 Subject: [PATCH 2293/2296] Properly handle pre-1.19 in resourceproviders aggregates We don't provide resource provider's generation in request nor do we get it back in response. The original tests got it slightly wrong; fix it. Automatically correct the request if user wrongly provides generation in opts for pre-1.19. Signed-off-by: Dominik Danelski --- .../placement/v1/resourceproviders_test.go | 69 +++++++++++++++++++ .../placement/v1/resourceproviders/doc.go | 7 +- .../v1/resourceproviders/requests.go | 6 +- .../testing/fixtures_test.go | 12 +++- .../testing/requests_test.go | 40 +++++++++-- .../placement/v1/resourceproviders/util.go | 14 ++++ 6 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 openstack/placement/v1/resourceproviders/util.go diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index c60f0b93a4..277dd5d38b 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -587,3 +587,72 @@ func TestResourceProviderAggregatesUpdateNegative(t *testing.T) { _, err = resourceproviders.UpdateAggregates(context.TODO(), client, resourceProvider.UUID, updateOpts).Extract() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) } + +func TestResourceProviderAggregatesUpdatePreGenerationSuccess(t *testing.T) { + // Before microversion 1.19, the PUT request body is just a list of aggregate UUIDs. + clients.SkipReleasesBelow(t, "stable/ocata") + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + client.Microversion = "1.1" + + updateOpts := resourceproviders.UpdateAggregatesOpts{ + Aggregates: []string{ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4", + }, + } + + _, err = resourceproviders.UpdateAggregates(context.TODO(), client, resourceProvider.UUID, updateOpts).Extract() + th.AssertNoErr(t, err) + + after, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, len(updateOpts.Aggregates), len(after.Aggregates)) + + for _, aggregate := range updateOpts.Aggregates { + th.AssertEquals(t, true, slices.Contains(after.Aggregates, aggregate)) + } +} + +func TestResourceProviderAggregatesUpdatePreGenerationWithGenerationSuccess(t *testing.T) { + // Before microversion 1.19, ResourceProviderGeneration in opts is silently stripped from + // the request body, so the operation must succeed even when the caller supplies it. + clients.SkipReleasesBelow(t, "stable/ocata") + clients.RequireAdmin(t) + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + + resourceProvider, err := CreateResourceProvider(t, client) + th.AssertNoErr(t, err) + defer DeleteResourceProvider(t, client, resourceProvider.UUID) + + client.Microversion = "1.1" + + gen := 1 + updateOpts := resourceproviders.UpdateAggregatesOpts{ + ResourceProviderGeneration: &gen, + Aggregates: []string{ + "6d84f6f6-7736-40ff-84d2-7db47f18ea25", + "f11f14bc-6f17-4f0a-b7c2-44b3e685ccf4", + }, + } + + _, err = resourceproviders.UpdateAggregates(context.TODO(), client, resourceProvider.UUID, updateOpts).Extract() + th.AssertNoErr(t, err) + + after, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() + th.AssertNoErr(t, err) + th.AssertEquals(t, len(updateOpts.Aggregates), len(after.Aggregates)) + + for _, aggregate := range updateOpts.Aggregates { + th.AssertEquals(t, true, slices.Contains(after.Aggregates, aggregate)) + } +} diff --git a/openstack/placement/v1/resourceproviders/doc.go b/openstack/placement/v1/resourceproviders/doc.go index 10e65ea7f5..d0a6caa515 100644 --- a/openstack/placement/v1/resourceproviders/doc.go +++ b/openstack/placement/v1/resourceproviders/doc.go @@ -172,9 +172,12 @@ Example to get resource providers aggregates panic(err) } -Example to update resource providers aggregates +# Example to update resource providers aggregates - placementClient.Microversion = "1.1" +For microversion 1.18 and earlier the ResourceProviderGeneration is optional and would be ignored if provided, +as it was not supported pre-1.19. For greater safety, it is recommended to use the newer microversion. + + placementClient.Microversion = "1.19" updateOpts := resourceproviders.UpdateAggregatesOpts{ ResourceProviderGeneration: rp.ResourceProviderGeneration, diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index 1f7eed4af6..f71bfe8519 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -285,7 +285,11 @@ func UpdateAggregates(ctx context.Context, client *gophercloud.ServiceClient, re return } - resp, err := client.Put(ctx, updateResourceProviderAggregatesURL(client, resourceProviderID), b, &r.Body, &gophercloud.RequestOpts{ + var body any = b + if useGenerations := microversionAtLeast(client, 1, 19); !useGenerations { + body = b["aggregates"] + } + resp, err := client.Put(ctx, updateResourceProviderAggregatesURL(client, resourceProviderID), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) diff --git a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go index 8b25cd8102..45faff6427 100644 --- a/openstack/placement/v1/resourceproviders/testing/fixtures_test.go +++ b/openstack/placement/v1/resourceproviders/testing/fixtures_test.go @@ -252,7 +252,13 @@ const AggregatesUpdateBody = ` } ` -const AggregatesUpdateBodyPreGeneration = ` +// AggregatesUpdateRequestPreGeneration is the raw JSON array sent as the PUT request body +// for microversions < 1.19, where the payload is just a list of aggregate UUIDs. +const AggregatesUpdateRequestPreGeneration = `["89f68995-4fd8-4f8b-a03e-7d5980762ff2","16d0e5f2-7f66-4f32-9040-b09de2f40afd"]` + +// AggregatesUpdateBodyWithoutGeneration is the pre-1.19 server response body for an aggregates update. +// Unlike the request, the response is not flat. +const AggregatesUpdateBodyWithoutGeneration = ` { "aggregates": [ "89f68995-4fd8-4f8b-a03e-7d5980762ff2", @@ -793,9 +799,9 @@ func HandleResourceProviderUpdateAndGetAggregatesPreGenerationSuccess(t *testing th.TestHeader(t, r, "X-Auth-Token", client.TokenID) th.TestHeader(t, r, "Content-Type", "application/json") th.TestHeader(t, r, "Accept", "application/json") - th.TestJSONRequest(t, r, AggregatesUpdateBodyPreGeneration) + th.TestJSONRequest(t, r, AggregatesUpdateRequestPreGeneration) - body = AggregatesUpdateBodyPreGeneration + body = AggregatesUpdateBodyWithoutGeneration w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) diff --git a/openstack/placement/v1/resourceproviders/testing/requests_test.go b/openstack/placement/v1/resourceproviders/testing/requests_test.go index ccdef63c6a..36909fb3df 100644 --- a/openstack/placement/v1/resourceproviders/testing/requests_test.go +++ b/openstack/placement/v1/resourceproviders/testing/requests_test.go @@ -315,11 +315,14 @@ func TestUpdateResourceProviderAggregatesSuccess(t *testing.T) { HandleResourceProviderUpdateAndGetAggregatesSuccess(t, fakeServer) + sc := client.ServiceClient(fakeServer) + sc.Microversion = "1.19" + updateOpts := resourceproviders.UpdateAggregatesOpts(ExpectedUpdatedAggregates) - _, err := resourceproviders.UpdateAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, updateOpts).Extract() + _, err := resourceproviders.UpdateAggregates(context.TODO(), sc, ResourceProviderTestID, updateOpts).Extract() th.AssertNoErr(t, err) - actual, err := resourceproviders.GetAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).Extract() + actual, err := resourceproviders.GetAggregates(context.TODO(), sc, ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdatedAggregates, *actual) } @@ -330,13 +333,42 @@ func TestUpdateResourceProviderAggregatesPreGenerationSuccess(t *testing.T) { HandleResourceProviderUpdateAndGetAggregatesPreGenerationSuccess(t, fakeServer) + sc := client.ServiceClient(fakeServer) + sc.Microversion = "1.10" + updateOpts := resourceproviders.UpdateAggregatesOpts{ Aggregates: ExpectedUpdatedAggregatesPreGeneration.Aggregates, } - _, err := resourceproviders.UpdateAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID, updateOpts).Extract() + _, err := resourceproviders.UpdateAggregates(context.TODO(), sc, ResourceProviderTestID, updateOpts).Extract() th.AssertNoErr(t, err) - actual, err := resourceproviders.GetAggregates(context.TODO(), client.ServiceClient(fakeServer), ResourceProviderTestID).Extract() + actual, err := resourceproviders.GetAggregates(context.TODO(), sc, ResourceProviderTestID).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, ExpectedUpdatedAggregatesPreGeneration, *actual) +} + +// TestUpdateResourceProviderAggregatesPreGenerationWithGenerationInOptsSuccess validates that +// when the microversion is < 1.19 but the caller supplies ResourceProviderGeneration in opts, +// the generation field is stripped from the request body and the operation succeeds. +func TestUpdateResourceProviderAggregatesPreGenerationWithGenerationInOptsSuccess(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + // Reuse the same handler: it validates the request body contains only aggregates (no generation). + HandleResourceProviderUpdateAndGetAggregatesPreGenerationSuccess(t, fakeServer) + + sc := client.ServiceClient(fakeServer) + sc.Microversion = "1.10" + + gen := 1 + updateOpts := resourceproviders.UpdateAggregatesOpts{ + ResourceProviderGeneration: &gen, + Aggregates: ExpectedUpdatedAggregatesPreGeneration.Aggregates, + } + _, err := resourceproviders.UpdateAggregates(context.TODO(), sc, ResourceProviderTestID, updateOpts).Extract() + th.AssertNoErr(t, err) + + actual, err := resourceproviders.GetAggregates(context.TODO(), sc, ResourceProviderTestID).Extract() th.AssertNoErr(t, err) th.AssertDeepEquals(t, ExpectedUpdatedAggregatesPreGeneration, *actual) } diff --git a/openstack/placement/v1/resourceproviders/util.go b/openstack/placement/v1/resourceproviders/util.go new file mode 100644 index 0000000000..ecddaed8ea --- /dev/null +++ b/openstack/placement/v1/resourceproviders/util.go @@ -0,0 +1,14 @@ +package resourceproviders + +import ( + "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" +) + +func microversionAtLeast(client *gophercloud.ServiceClient, major, minor int) bool { + M, m, err := utils.ParseMicroversion(client.Microversion) + if err != nil { + return false + } + return M > major || (M == major && m >= minor) +} From 0f346066c8b772165b73bb7ee4f3862608262402 Mon Sep 17 00:00:00 2001 From: Dominik Danelski Date: Fri, 10 Apr 2026 00:54:39 +0200 Subject: [PATCH 2294/2296] Fix IsEmpty for allocation_candidates in 1.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full deserialisation could crash on older structure defined for 1.10. Co-authored-by: Martin André Signed-off-by: Dominik Danelski --- .../placement/v1/allocationcandidates_test.go | 26 +++++++++++++++++++ .../v1/allocationcandidates/results.go | 14 +++++++--- .../testing/requests_test.go | 20 ++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/internal/acceptance/openstack/placement/v1/allocationcandidates_test.go b/internal/acceptance/openstack/placement/v1/allocationcandidates_test.go index 2cfc4074ac..38cc6dcc06 100644 --- a/internal/acceptance/openstack/placement/v1/allocationcandidates_test.go +++ b/internal/acceptance/openstack/placement/v1/allocationcandidates_test.go @@ -187,6 +187,28 @@ func TestAllocationCandidatesList110(t *testing.T) { th.AssertEquals(t, 0, vcpuSummary.Used) } +func TestAllocationCandidatesIsEmpty110(t *testing.T) { + clients.SkipReleasesBelow(t, "stable/pike") + clients.RequireAdmin(t) + + microversion := "1.10" + _, cleanup := createRPWithVCPUInventory(t, microversion) + defer cleanup() + + client, err := clients.NewPlacementV1Client() + th.AssertNoErr(t, err) + client.Microversion = microversion + + page, err := allocationcandidates.List(client, allocationcandidates.ListOpts{ + Resources: "VCPU:1", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + isEmpty, err := page.IsEmpty() + th.AssertNoErr(t, err) + th.AssertEquals(t, false, isEmpty) +} + func TestAllocationCandidatesListEmpty(t *testing.T) { clients.SkipReleasesBelow(t, "stable/pike") clients.RequireAdmin(t) @@ -205,4 +227,8 @@ func TestAllocationCandidatesListEmpty(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(result.AllocationRequests)) th.AssertEquals(t, 0, len(result.ProviderSummaries)) + + isEmpty, err := page.IsEmpty() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, isEmpty) } diff --git a/openstack/placement/v1/allocationcandidates/results.go b/openstack/placement/v1/allocationcandidates/results.go index 44212872de..8b94a0a7f5 100644 --- a/openstack/placement/v1/allocationcandidates/results.go +++ b/openstack/placement/v1/allocationcandidates/results.go @@ -1,6 +1,10 @@ package allocationcandidates -import "github.com/gophercloud/gophercloud/v2/pagination" +import ( + "encoding/json" + + "github.com/gophercloud/gophercloud/v2/pagination" +) // AllocationCandidates represents the response from a List allocation // candidates request for microversions 1.12 and above. @@ -69,12 +73,16 @@ type AllocationCandidatesPage struct { } // IsEmpty determines if an AllocationCandidatesPage contains any results. +// It avoids full deserialization so that it works across all microversions, func (page AllocationCandidatesPage) IsEmpty() (bool, error) { - candidates, err := ExtractAllocationCandidates(page) + var s struct { + AllocationRequests []json.RawMessage `json:"allocation_requests"` + } + err := page.ExtractInto(&s) if err != nil { return false, err } - return len(candidates.AllocationRequests) == 0, nil + return len(s.AllocationRequests) == 0, nil } // ExtractAllocationCandidates interprets an AllocationCandidatesPage as AllocationCandidates (microversion 1.12+). diff --git a/openstack/placement/v1/allocationcandidates/testing/requests_test.go b/openstack/placement/v1/allocationcandidates/testing/requests_test.go index 14b0530512..98bb29b9c9 100644 --- a/openstack/placement/v1/allocationcandidates/testing/requests_test.go +++ b/openstack/placement/v1/allocationcandidates/testing/requests_test.go @@ -100,6 +100,26 @@ func TestListAllocationCandidatesEmptySuccess(t *testing.T) { th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(actual.AllocationRequests)) th.AssertEquals(t, 0, len(actual.ProviderSummaries)) + + isEmpty, err := page.IsEmpty() + th.AssertNoErr(t, err) + th.AssertEquals(t, true, isEmpty) +} + +func TestIsEmpty110Success(t *testing.T) { + fakeServer := th.SetupHTTP() + defer fakeServer.Teardown() + + HandleListAllocationCandidates110Success(t, fakeServer) + + page, err := allocationcandidates.List(client.ServiceClient(fakeServer), allocationcandidates.ListOpts{ + Resources: "VCPU:1,MEMORY_MB:1024", + }).AllPages(context.TODO()) + th.AssertNoErr(t, err) + + isEmpty, err := page.IsEmpty() + th.AssertNoErr(t, err) + th.AssertEquals(t, false, isEmpty) } func TestListAllocationCandidatesBadRequest(t *testing.T) { From 88702e7f4d002fc44809f45d2cb0d3480616da5b Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 Apr 2026 13:37:01 +0100 Subject: [PATCH 2295/2296] Address review comments from resourceprovider aggregates PR Per comments [1], remove an unnecessary helper and tweak some tests. [1] https://github.com/gophercloud/gophercloud/pull/3709 Signed-off-by: Stephen Finucane --- .../placement/v1/resourceproviders_test.go | 48 ++++--------------- .../v1/resourceproviders/requests.go | 13 ++++- .../placement/v1/resourceproviders/util.go | 14 ------ 3 files changed, 21 insertions(+), 54 deletions(-) delete mode 100644 openstack/placement/v1/resourceproviders/util.go diff --git a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go index 277dd5d38b..3babbf9006 100644 --- a/internal/acceptance/openstack/placement/v1/resourceproviders_test.go +++ b/internal/acceptance/openstack/placement/v1/resourceproviders_test.go @@ -453,47 +453,33 @@ func TestResourceProviderAllocations(t *testing.T) { tools.PrintResource(t, usage) } -func TestResourceProviderAggregatesGetSuccess(t *testing.T) { - // Resource_provider_generation in the aggregates response was introduced in microversion 1.19. +func TestResourceProviderAggregates(t *testing.T) { clients.SkipReleasesBelow(t, "stable/ocata") clients.RequireAdmin(t) client, err := clients.NewPlacementV1Client() th.AssertNoErr(t, err) + // first create new resource provider resourceProvider, err := CreateResourceProvider(t, client) th.AssertNoErr(t, err) defer DeleteResourceProvider(t, client, resourceProvider.UUID) + // now get the aggregates for same client.Microversion = "1.19" - aggregates, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, true, aggregates.ResourceProviderGeneration != nil) -} - -func TestResourceProviderAggregatesGetPreGenerationSuccess(t *testing.T) { - // Resource provider aggregates operations were introduced in microversion 1.1. - clients.SkipReleasesBelow(t, "stable/ocata") - clients.RequireAdmin(t) - - client, err := clients.NewPlacementV1Client() - th.AssertNoErr(t, err) - - resourceProvider, err := CreateResourceProvider(t, client) - th.AssertNoErr(t, err) - defer DeleteResourceProvider(t, client, resourceProvider.UUID) + // ensure that we handle older microversions where generation is missing client.Microversion = "1.1" - - aggregates, err := resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() + aggregates, err = resourceproviders.GetAggregates(context.TODO(), client, resourceProvider.UUID).Extract() th.AssertNoErr(t, err) th.AssertEquals(t, 0, len(aggregates.Aggregates)) th.AssertDeepEquals(t, (*int)(nil), aggregates.ResourceProviderGeneration) } -func TestResourceProviderAggregatesGetNegative(t *testing.T) { - // Resource_provider_generation in the aggregates response was introduced in microversion 1.19. +func TestResourceProviderAggregatesNotFound(t *testing.T) { clients.SkipReleasesBelow(t, "stable/ocata") clients.RequireAdmin(t) @@ -501,27 +487,15 @@ func TestResourceProviderAggregatesGetNegative(t *testing.T) { th.AssertNoErr(t, err) client.Microversion = "1.19" - _, err = resourceproviders.GetAggregates(context.TODO(), client, "00000000-0000-0000-0000-000000000000").Extract() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) -} - -func TestResourceProviderAggregatesGetPreGenerationNegative(t *testing.T) { - // Resource provider aggregates operations were introduced in microversion 1.1. - clients.SkipReleasesBelow(t, "stable/ocata") - clients.RequireAdmin(t) - - client, err := clients.NewPlacementV1Client() - th.AssertNoErr(t, err) client.Microversion = "1.1" - _, err = resourceproviders.GetAggregates(context.TODO(), client, "00000000-0000-0000-0000-000000000000").Extract() th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusNotFound)) } -func TestResourceProviderAggregatesUpdateSuccess(t *testing.T) { - // resource_provider_generation is required in the PUT request body from microversion 1.19. +func TestResourceProviderUpdateAggregates(t *testing.T) { clients.SkipReleasesBelow(t, "stable/ocata") clients.RequireAdmin(t) @@ -558,8 +532,7 @@ func TestResourceProviderAggregatesUpdateSuccess(t *testing.T) { } } -func TestResourceProviderAggregatesUpdateNegative(t *testing.T) { - // resource_provider_generation is required in the PUT request body from microversion 1.19. +func TestResourceProviderUpdateAggregateMismatch(t *testing.T) { clients.SkipReleasesBelow(t, "stable/ocata") clients.RequireAdmin(t) @@ -588,8 +561,7 @@ func TestResourceProviderAggregatesUpdateNegative(t *testing.T) { th.AssertEquals(t, true, gophercloud.ResponseCodeIs(err, http.StatusConflict)) } -func TestResourceProviderAggregatesUpdatePreGenerationSuccess(t *testing.T) { - // Before microversion 1.19, the PUT request body is just a list of aggregate UUIDs. +func TestResourceProviderUpdateAggregatesPreGeneration(t *testing.T) { clients.SkipReleasesBelow(t, "stable/ocata") clients.RequireAdmin(t) @@ -621,7 +593,7 @@ func TestResourceProviderAggregatesUpdatePreGenerationSuccess(t *testing.T) { } } -func TestResourceProviderAggregatesUpdatePreGenerationWithGenerationSuccess(t *testing.T) { +func TestResourceProviderUpdateAggregatesPreGenerationWithGenerationSuccess(t *testing.T) { // Before microversion 1.19, ResourceProviderGeneration in opts is silently stripped from // the request body, so the operation must succeed even when the caller supplies it. clients.SkipReleasesBelow(t, "stable/ocata") diff --git a/openstack/placement/v1/resourceproviders/requests.go b/openstack/placement/v1/resourceproviders/requests.go index f71bfe8519..aecc975060 100644 --- a/openstack/placement/v1/resourceproviders/requests.go +++ b/openstack/placement/v1/resourceproviders/requests.go @@ -4,6 +4,7 @@ import ( "context" "github.com/gophercloud/gophercloud/v2" + "github.com/gophercloud/gophercloud/v2/openstack/utils" "github.com/gophercloud/gophercloud/v2/pagination" ) @@ -286,8 +287,16 @@ func UpdateAggregates(ctx context.Context, client *gophercloud.ServiceClient, re } var body any = b - if useGenerations := microversionAtLeast(client, 1, 19); !useGenerations { - body = b["aggregates"] + if client.Microversion != "" { + _, minor, err := utils.ParseMicroversion(client.Microversion) + if err != nil { + r.Err = err + return + } + // In microversions prior to 1.19, the body was not enveloped. + if minor < 19 { + body = b["aggregates"] + } } resp, err := client.Put(ctx, updateResourceProviderAggregatesURL(client, resourceProviderID), body, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, diff --git a/openstack/placement/v1/resourceproviders/util.go b/openstack/placement/v1/resourceproviders/util.go deleted file mode 100644 index ecddaed8ea..0000000000 --- a/openstack/placement/v1/resourceproviders/util.go +++ /dev/null @@ -1,14 +0,0 @@ -package resourceproviders - -import ( - "github.com/gophercloud/gophercloud/v2" - "github.com/gophercloud/gophercloud/v2/openstack/utils" -) - -func microversionAtLeast(client *gophercloud.ServiceClient, major, minor int) bool { - M, m, err := utils.ParseMicroversion(client.Microversion) - if err != nil { - return false - } - return M > major || (M == major && m >= minor) -} From be391f3ddde959eb418c5cbbaba044a7d165eecf Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 9 Apr 2026 16:30:48 +0100 Subject: [PATCH 2296/2296] ci: Make "Aetos on OpenStack master" non-voting Sync our script with current configuration. Signed-off-by: Stephen Finucane --- script/configure-required-github-jobs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/configure-required-github-jobs b/script/configure-required-github-jobs index bb4412961d..5f431c65fb 100755 --- a/script/configure-required-github-jobs +++ b/script/configure-required-github-jobs @@ -55,6 +55,9 @@ declare -A EXPECTED_SETTINGS=( # their workflow has an `on: merge_group` trigger. Add entries using the exact # name format: "" (with resolved matrix values). SKIP_CHECKS=( + # FIXME(stephenfin): Re-enable this once [1] merges + # [1] https://review.opendev.org/c/openstack/project-config/+/983728 + "Aetos on OpenStack master" "finish" # coveralls parallel-finish job, not a test gate )